From d3ed9e20ca6d13bc384b54c21bcf28150ea922b0 Mon Sep 17 00:00:00 2001 From: St4lker0k765 <126080360+St4lker0k765@users.noreply.github.com> Date: Sat, 7 Dec 2024 21:30:19 +0300 Subject: [PATCH] Add xrGameLA files --- src/CMakeLists.txt | 2 + src/xrGameLA/AI_PhraseDialogManager.cpp | 108 + src/xrGameLA/AI_PhraseDialogManager.h | 39 + src/xrGameLA/AcidFog.cpp | 120 + src/xrGameLA/AcidFog.h | 39 + src/xrGameLA/Actor.cpp | 1935 +++++++ src/xrGameLA/Actor.h | 862 +++ src/xrGameLA/ActorAnimation.cpp | 639 +++ src/xrGameLA/ActorAnimation.h | 47 + src/xrGameLA/ActorCameras.cpp | 455 ++ src/xrGameLA/ActorCondition.cpp | 443 ++ src/xrGameLA/ActorCondition.h | 149 + src/xrGameLA/ActorEatablesEffects.cpp | 221 + src/xrGameLA/ActorEffector.cpp | 547 ++ src/xrGameLA/ActorEffector.h | 174 + src/xrGameLA/ActorEffector_script.cpp | 29 + src/xrGameLA/ActorFollowers.cpp | 92 + src/xrGameLA/ActorFollowers.h | 22 + src/xrGameLA/ActorHudWetness.cpp | 123 + src/xrGameLA/ActorInput.cpp | 693 +++ src/xrGameLA/ActorMountedWeapon.cpp | 46 + src/xrGameLA/ActorState.h | 37 + src/xrGameLA/ActorVehicle.cpp | 172 + src/xrGameLA/Actor_Events.cpp | 268 + src/xrGameLA/Actor_Feel.cpp | 291 + src/xrGameLA/Actor_Flags.h | 25 + src/xrGameLA/Actor_Movement.cpp | 644 +++ src/xrGameLA/Actor_Network.cpp | 1977 +++++++ src/xrGameLA/Actor_Sleep.cpp | 1 + src/xrGameLA/Actor_Weapon.cpp | 280 + src/xrGameLA/AdvancedAfDetector.cpp | 324 ++ src/xrGameLA/AdvancedAfDetector.h | 30 + src/xrGameLA/AmebaZone.cpp | 121 + src/xrGameLA/AmebaZone.h | 21 + src/xrGameLA/Artifact.cpp | 663 +++ src/xrGameLA/Artifact.h | 134 + src/xrGameLA/BastArtifact.cpp | 279 + src/xrGameLA/BastArtifact.h | 80 + src/xrGameLA/BlackDrops.cpp | 22 + src/xrGameLA/BlackDrops.h | 20 + src/xrGameLA/BlackGraviArtifact.cpp | 243 + src/xrGameLA/BlackGraviArtifact.h | 57 + src/xrGameLA/BlockAllocator.h | 118 + src/xrGameLA/Bolt.cpp | 79 + src/xrGameLA/Bolt.h | 27 + src/xrGameLA/BoneProtections.cpp | 101 + src/xrGameLA/BoneProtections.h | 24 + src/xrGameLA/Booster.h | 16 + src/xrGameLA/BottleItem.cpp | 92 + src/xrGameLA/BottleItem.h | 36 + src/xrGameLA/BreakableObject.cpp | 351 ++ src/xrGameLA/BreakableObject.h | 67 + src/xrGameLA/CHelmet.cpp | 36 + src/xrGameLA/CHelmet.h | 19 + src/xrGameLA/CMakeLists.txt | 178 + src/xrGameLA/CalculateTriangle.h | 178 + src/xrGameLA/CameraEffector.cpp | 1 + src/xrGameLA/CameraEffector.h | 41 + src/xrGameLA/CameraFirstEye.cpp | 77 + src/xrGameLA/CameraFirstEye.h | 23 + src/xrGameLA/Car.cpp | 2216 ++++++++ src/xrGameLA/Car.h | 659 +++ src/xrGameLA/CarCameras.cpp | 86 + src/xrGameLA/CarDamageParticles.cpp | 101 + src/xrGameLA/CarDamageParticles.h | 23 + src/xrGameLA/CarDoors.cpp | 787 +++ src/xrGameLA/CarExhaust.cpp | 73 + src/xrGameLA/CarInput.cpp | 273 + src/xrGameLA/CarLights.cpp | 200 + src/xrGameLA/CarLights.h | 76 + src/xrGameLA/CarScript.cpp | 48 + src/xrGameLA/CarSound.cpp | 186 + src/xrGameLA/CarWeapon.cpp | 294 + src/xrGameLA/CarWeapon.h | 69 + src/xrGameLA/CarWheels.cpp | 363 ++ src/xrGameLA/CharacterPhysicsSupport.cpp | 1054 ++++ src/xrGameLA/CharacterPhysicsSupport.h | 175 + src/xrGameLA/ClimableObject.cpp | 379 ++ src/xrGameLA/ClimableObject.h | 69 + src/xrGameLA/ContextMenu.cpp | 45 + src/xrGameLA/ContextMenu.h | 26 + src/xrGameLA/CustomDetector.cpp | 325 ++ src/xrGameLA/CustomDetector.h | 94 + src/xrGameLA/CustomDetector2.cpp | 403 ++ src/xrGameLA/CustomDetector2.h | 82 + src/xrGameLA/CustomMonster.cpp | 1165 ++++ src/xrGameLA/CustomMonster.h | 328 ++ src/xrGameLA/CustomMonster_VCPU.cpp | 47 + src/xrGameLA/CustomMonster_inline.h | 102 + src/xrGameLA/CustomOutfit.cpp | 308 + src/xrGameLA/CustomOutfit.h | 65 + src/xrGameLA/CustomRocket.cpp | 643 +++ src/xrGameLA/CustomRocket.h | 166 + src/xrGameLA/CustomTimer.cpp | 144 + src/xrGameLA/CustomTimer.h | 120 + src/xrGameLA/CustomTimer_script.cpp | 83 + src/xrGameLA/CustomTimersManager.cpp | 261 + src/xrGameLA/CustomTimersManager.h | 66 + src/xrGameLA/CustomTimersManager_script.cpp | 21 + src/xrGameLA/CustomZone.cpp | 1549 +++++ src/xrGameLA/CustomZone.h | 396 ++ src/xrGameLA/CycleConstStorage.h | 33 + src/xrGameLA/DBG_Car.cpp | 212 + src/xrGameLA/DamagableItem.cpp | 63 + src/xrGameLA/DamagableItem.h | 32 + src/xrGameLA/DamageSource.h | 11 + src/xrGameLA/DelayedActionFuse.cpp | 79 + src/xrGameLA/DelayedActionFuse.h | 27 + src/xrGameLA/DestroyablePhysicsObject.cpp | 155 + src/xrGameLA/DestroyablePhysicsObject.h | 33 + src/xrGameLA/DisablingParams.cpp | 44 + src/xrGameLA/DisablingParams.h | 29 + src/xrGameLA/DummyArtifact.cpp | 23 + src/xrGameLA/DummyArtifact.h | 20 + src/xrGameLA/DynamicHeightMap.cpp | 191 + src/xrGameLA/DynamicHeightMap.h | 75 + src/xrGameLA/EffectorBobbing.cpp | 107 + src/xrGameLA/EffectorBobbing.h | 35 + src/xrGameLA/EffectorFall.cpp | 41 + src/xrGameLA/EffectorFall.h | 21 + src/xrGameLA/EffectorShot.cpp | 202 + src/xrGameLA/EffectorShot.h | 65 + src/xrGameLA/EffectorShotX.cpp | 64 + src/xrGameLA/EffectorShotX.h | 18 + src/xrGameLA/EffectorZoomInertion.cpp | 236 + src/xrGameLA/EffectorZoomInertion.h | 68 + src/xrGameLA/ElectricBall.cpp | 29 + src/xrGameLA/ElectricBall.h | 22 + src/xrGameLA/ElevatorState.cpp | 388 ++ src/xrGameLA/ElevatorState.h | 71 + src/xrGameLA/EliteAfDetector.cpp | 366 ++ src/xrGameLA/EliteAfDetector.h | 29 + src/xrGameLA/Entity.cpp | 370 ++ src/xrGameLA/Entity.h | 129 + src/xrGameLA/EntityCondition.cpp | 589 ++ src/xrGameLA/EntityCondition.h | 192 + src/xrGameLA/ExoOutfit.cpp | 17 + src/xrGameLA/ExoOutfit.h | 18 + src/xrGameLA/Explosive.cpp | 791 +++ src/xrGameLA/Explosive.h | 184 + src/xrGameLA/ExplosiveItem.cpp | 100 + src/xrGameLA/ExplosiveItem.h | 45 + src/xrGameLA/ExplosiveRocket.cpp | 164 + src/xrGameLA/ExplosiveRocket.h | 77 + src/xrGameLA/ExplosiveScript.cpp | 15 + src/xrGameLA/ExtendedGeom.h | 276 + src/xrGameLA/F1.cpp | 20 + src/xrGameLA/F1.h | 18 + src/xrGameLA/FadedBall.cpp | 23 + src/xrGameLA/FadedBall.h | 20 + src/xrGameLA/FastDelegate.h | 2114 +++++++ src/xrGameLA/Fireball.cpp | 154 + src/xrGameLA/Fireball.h | 29 + src/xrGameLA/FoodItem.cpp | 10 + src/xrGameLA/FoodItem.h | 11 + src/xrGameLA/FryupZone.cpp | 15 + src/xrGameLA/FryupZone.h | 16 + src/xrGameLA/GalantineArtifact.cpp | 22 + src/xrGameLA/GalantineArtifact.h | 20 + src/xrGameLA/GameObject.cpp | 937 +++ src/xrGameLA/GameObject.h | 293 + src/xrGameLA/GamePersistent.cpp | 699 +++ src/xrGameLA/GamePersistent.h | 95 + src/xrGameLA/GameTask.cpp | 710 +++ src/xrGameLA/GameTask.h | 163 + src/xrGameLA/GameTaskDefs.h | 47 + src/xrGameLA/GameTask_script.cpp | 69 + src/xrGameLA/GametaskManager.cpp | 344 ++ src/xrGameLA/GametaskManager.h | 38 + src/xrGameLA/Geometry.cpp | 664 +++ src/xrGameLA/Geometry.h | 168 + src/xrGameLA/GraviArtifact.cpp | 72 + src/xrGameLA/GraviArtifact.h | 25 + src/xrGameLA/GraviZone.cpp | 324 ++ src/xrGameLA/GraviZone.h | 97 + src/xrGameLA/Grenade.cpp | 371 ++ src/xrGameLA/Grenade.h | 74 + src/xrGameLA/GrenadeLauncher.cpp | 55 + src/xrGameLA/GrenadeLauncher.h | 31 + src/xrGameLA/HUDCrosshair.cpp | 150 + src/xrGameLA/HUDCrosshair.h | 35 + src/xrGameLA/HUDManager.cpp | 373 ++ src/xrGameLA/HUDManager.h | 55 + src/xrGameLA/HUDTarget.cpp | 294 + src/xrGameLA/HUDTarget.h | 39 + src/xrGameLA/HairsZone.cpp | 161 + src/xrGameLA/HairsZone.h | 30 + src/xrGameLA/HairsZone_script.cpp | 14 + src/xrGameLA/HangingLamp.cpp | 412 ++ src/xrGameLA/HangingLamp.h | 79 + src/xrGameLA/Helicopter.cpp | 510 ++ src/xrGameLA/Helicopter2.cpp | 471 ++ src/xrGameLA/HelicopterMovementManager.cpp | 494 ++ src/xrGameLA/HelicopterWeapon.cpp | 330 ++ src/xrGameLA/Hit.cpp | 156 + src/xrGameLA/Hit.h | 47 + src/xrGameLA/HitMarker.cpp | 96 + src/xrGameLA/HitMarker.h | 45 + src/xrGameLA/HudItem.cpp | 407 ++ src/xrGameLA/HudItem.h | 192 + src/xrGameLA/HudItemCallback.cpp | 36 + src/xrGameLA/HudSound.cpp | 210 + src/xrGameLA/HudSound.h | 81 + src/xrGameLA/IColisiondamageInfo.h | 16 + src/xrGameLA/IKFoot.cpp | 442 ++ src/xrGameLA/IKFoot.h | 76 + src/xrGameLA/IKFoot_inl.h | 52 + src/xrGameLA/IKLimbsController.cpp | 356 ++ src/xrGameLA/IKLimbsController.h | 51 + src/xrGameLA/InfoDocument.cpp | 86 + src/xrGameLA/InfoDocument.h | 32 + src/xrGameLA/InfoPortion.cpp | 146 + src/xrGameLA/InfoPortion.h | 85 + src/xrGameLA/InfoPortionDefs.h | 19 + src/xrGameLA/Inventory.cpp | 1674 ++++++ src/xrGameLA/Inventory.h | 227 + src/xrGameLA/InventoryBox.cpp | 94 + src/xrGameLA/InventoryBox.h | 24 + src/xrGameLA/InventoryOwner.cpp | 634 +++ src/xrGameLA/InventoryOwner.h | 227 + src/xrGameLA/ItemHealth.cpp | 10 + src/xrGameLA/ItemHealth.h | 10 + src/xrGameLA/ItemListTypes.h | 53 + src/xrGameLA/Level.cpp | 1210 ++++ src/xrGameLA/Level.h | 442 ++ src/xrGameLA/LevelFogOfWar.cpp | 275 + src/xrGameLA/LevelFogOfWar.h | 56 + src/xrGameLA/LevelGameDef.cpp | 17 + src/xrGameLA/LevelGameDef.h | 196 + src/xrGameLA/Level_Bullet_Manager.cpp | 1099 ++++ src/xrGameLA/Level_Bullet_Manager.h | 253 + src/xrGameLA/Level_SLS_Default.cpp | 19 + src/xrGameLA/Level_SLS_Load.cpp | 6 + src/xrGameLA/Level_SLS_Save.cpp | 29 + .../Level_bullet_manager_firetrace.cpp | 533 ++ src/xrGameLA/Level_input.cpp | 534 ++ src/xrGameLA/Level_load.cpp | 195 + src/xrGameLA/Level_network.cpp | 569 ++ src/xrGameLA/Level_network_Demo.cpp | 428 ++ src/xrGameLA/Level_network_Demo.h | 92 + src/xrGameLA/Level_network_messages.cpp | 387 ++ src/xrGameLA/Level_network_spawn.cpp | 209 + src/xrGameLA/Level_network_start_client.cpp | 179 + src/xrGameLA/Level_start.cpp | 322 ++ src/xrGameLA/MPPlayersBag.cpp | 49 + src/xrGameLA/MPPlayersBag.h | 16 + src/xrGameLA/MainMenu.cpp | 642 +++ src/xrGameLA/MainMenu.h | 160 + src/xrGameLA/MathUtils.cpp | 485 ++ src/xrGameLA/MathUtils.h | 395 ++ src/xrGameLA/MathUtilsOde.h | 109 + src/xrGameLA/MercuryBall.cpp | 54 + src/xrGameLA/MercuryBall.h | 56 + src/xrGameLA/MilitaryOutfit.cpp | 17 + src/xrGameLA/MilitaryOutfit.h | 18 + src/xrGameLA/Mincer.cpp | 178 + src/xrGameLA/Mincer.h | 56 + src/xrGameLA/Missile.cpp | 722 +++ src/xrGameLA/Missile.h | 108 + src/xrGameLA/MosquitoBald.cpp | 116 + src/xrGameLA/MosquitoBald.h | 35 + src/xrGameLA/MosquitoBald_script.cpp | 27 + src/xrGameLA/NET_Queue.h | 125 + src/xrGameLA/Needles.cpp | 22 + src/xrGameLA/Needles.h | 16 + src/xrGameLA/NoGravityZone.cpp | 62 + src/xrGameLA/NoGravityZone.h | 15 + src/xrGameLA/OutfitBase.cpp | 309 + src/xrGameLA/OutfitBase.h | 87 + src/xrGameLA/PDA.cpp | 220 + src/xrGameLA/PDA.h | 84 + src/xrGameLA/PHAICharacter.cpp | 205 + src/xrGameLA/PHAICharacter.h | 28 + src/xrGameLA/PHActivationShape.cpp | 291 + src/xrGameLA/PHActivationShape.h | 45 + src/xrGameLA/PHActorCharacter.cpp | 316 + src/xrGameLA/PHActorCharacter.h | 115 + src/xrGameLA/PHActorCharacterInline.h | 1 + src/xrGameLA/PHBaseBodyEffector.h | 16 + src/xrGameLA/PHCapture.cpp | 330 ++ src/xrGameLA/PHCapture.h | 75 + src/xrGameLA/PHCaptureInit.cpp | 318 ++ src/xrGameLA/PHCharacter.cpp | 162 + src/xrGameLA/PHCharacter.h | 170 + src/xrGameLA/PHCollideValidator.cpp | 121 + src/xrGameLA/PHCollideValidator.h | 109 + src/xrGameLA/PHCollisionDamageReceiver.cpp | 115 + src/xrGameLA/PHCollisionDamageReceiver.h | 32 + src/xrGameLA/PHCommander.cpp | 240 + src/xrGameLA/PHCommander.h | 104 + src/xrGameLA/PHContactBodyEffector.cpp | 62 + src/xrGameLA/PHContactBodyEffector.h | 15 + src/xrGameLA/PHDebug.cpp | 680 +++ src/xrGameLA/PHDebug.h | 155 + src/xrGameLA/PHDefs.h | 84 + src/xrGameLA/PHDestroyable.cpp | 353 ++ src/xrGameLA/PHDestroyable.h | 78 + src/xrGameLA/PHDestroyableNotificate.cpp | 23 + src/xrGameLA/PHDestroyableNotificate.h | 15 + src/xrGameLA/PHDisabling.cpp | 254 + src/xrGameLA/PHDisabling.h | 110 + src/xrGameLA/PHDynamicData.cpp | 203 + src/xrGameLA/PHDynamicData.h | 153 + src/xrGameLA/PHElement.cpp | 1561 +++++ src/xrGameLA/PHElement.h | 260 + src/xrGameLA/PHElementInline.h | 16 + src/xrGameLA/PHElementNetState.cpp | 67 + src/xrGameLA/PHFracture.cpp | 590 ++ src/xrGameLA/PHFracture.h | 64 + src/xrGameLA/PHGeometryOwner.cpp | 406 ++ src/xrGameLA/PHGeometryOwner.h | 76 + src/xrGameLA/PHImpact.h | 13 + src/xrGameLA/PHInterpolation.cpp | 99 + src/xrGameLA/PHInterpolation.h | 28 + src/xrGameLA/PHIsland.cpp | 40 + src/xrGameLA/PHIsland.h | 253 + src/xrGameLA/PHItemList.h | 103 + src/xrGameLA/PHJoint.cpp | 1281 +++++ src/xrGameLA/PHJoint.h | 238 + src/xrGameLA/PHJointDestroyInfo.cpp | 41 + src/xrGameLA/PHJointDestroyInfo.h | 25 + src/xrGameLA/PHMoveStorage.cpp | 42 + src/xrGameLA/PHMoveStorage.h | 55 + src/xrGameLA/PHMovementControl.cpp | 1168 ++++ src/xrGameLA/PHMovementControl.h | 279 + src/xrGameLA/PHMovementDynamicActivate.cpp | 442 ++ src/xrGameLA/PHNetState.cpp | 271 + src/xrGameLA/PHNetState.h | 57 + src/xrGameLA/PHObject.cpp | 257 + src/xrGameLA/PHObject.h | 142 + src/xrGameLA/PHReqComparer.h | 24 + src/xrGameLA/PHScriptCall.cpp | 174 + src/xrGameLA/PHScriptCall.h | 171 + src/xrGameLA/PHShell.cpp | 1707 ++++++ src/xrGameLA/PHShell.h | 267 + src/xrGameLA/PHShellActivate.cpp | 280 + src/xrGameLA/PHShellCreator.cpp | 25 + src/xrGameLA/PHShellCreator.h | 13 + src/xrGameLA/PHShellNetState.cpp | 24 + src/xrGameLA/PHShellSplitter.cpp | 529 ++ src/xrGameLA/PHShellSplitter.h | 65 + src/xrGameLA/PHSimpleCalls.cpp | 91 + src/xrGameLA/PHSimpleCalls.h | 82 + src/xrGameLA/PHSimpleCallsScript.cpp | 42 + src/xrGameLA/PHSimpleCharacter.cpp | 1852 ++++++ src/xrGameLA/PHSimpleCharacter.h | 233 + src/xrGameLA/PHSimpleCharacterInline.h | 146 + src/xrGameLA/PHSkeleton.cpp | 442 ++ src/xrGameLA/PHSkeleton.h | 63 + src/xrGameLA/PHSoundPlayer.cpp | 31 + src/xrGameLA/PHSoundPlayer.h | 17 + src/xrGameLA/PHSplitedShell.cpp | 28 + src/xrGameLA/PHSplitedShell.h | 20 + src/xrGameLA/PHStaticGeomShell.cpp | 88 + src/xrGameLA/PHStaticGeomShell.h | 28 + src/xrGameLA/PHSynchronize.cpp | 4 + src/xrGameLA/PHSynchronize.h | 23 + src/xrGameLA/PHValideValues.h | 134 + src/xrGameLA/PHWorld.cpp | 511 ++ src/xrGameLA/PHWorld.h | 107 + src/xrGameLA/PHWorldScript.cpp | 18 + src/xrGameLA/ParticlesObject.cpp | 291 + src/xrGameLA/ParticlesObject.h | 67 + src/xrGameLA/ParticlesPlayer.cpp | 323 ++ src/xrGameLA/ParticlesPlayer.h | 100 + src/xrGameLA/PdaMsg.h | 45 + src/xrGameLA/PdaScript.cpp | 13 + src/xrGameLA/Phrase.cpp | 33 + src/xrGameLA/Phrase.h | 44 + src/xrGameLA/PhraseDialog.cpp | 333 ++ src/xrGameLA/PhraseDialog.h | 139 + src/xrGameLA/PhraseDialogDefs.h | 17 + src/xrGameLA/PhraseDialogManager.cpp | 116 + src/xrGameLA/PhraseDialogManager.h | 41 + src/xrGameLA/PhraseDialog_script.cpp | 52 + src/xrGameLA/PhraseDialog_script.h | 11 + src/xrGameLA/PhraseScript.cpp | 231 + src/xrGameLA/PhraseScript.h | 81 + src/xrGameLA/PhysicObject.cpp | 319 ++ src/xrGameLA/PhysicObject.h | 54 + src/xrGameLA/PhysicObject_script.cpp | 14 + src/xrGameLA/Physics.cpp | 544 ++ src/xrGameLA/Physics.h | 52 + src/xrGameLA/PhysicsCommon.h | 71 + src/xrGameLA/PhysicsGamePars.cpp | 27 + src/xrGameLA/PhysicsGamePars.h | 23 + src/xrGameLA/PhysicsShell.cpp | 245 + src/xrGameLA/PhysicsShell.h | 355 ++ src/xrGameLA/PhysicsShellAnimator.cpp | 72 + src/xrGameLA/PhysicsShellAnimator.h | 19 + src/xrGameLA/PhysicsShellAnimatorBoneData.h | 10 + src/xrGameLA/PhysicsShellHolder.cpp | 387 ++ src/xrGameLA/PhysicsShellHolder.h | 123 + src/xrGameLA/PhysicsShellScript.cpp | 85 + src/xrGameLA/PhysicsSkeletonObject.cpp | 108 + src/xrGameLA/PhysicsSkeletonObject.h | 38 + src/xrGameLA/PostprocessAnimator.cpp | 450 ++ src/xrGameLA/PostprocessAnimator.h | 212 + src/xrGameLA/PropertiesListHelper.h | 77 + src/xrGameLA/PropertiesListTypes.h | 682 +++ src/xrGameLA/RGD5.cpp | 25 + src/xrGameLA/RGD5.h | 18 + src/xrGameLA/RadioactiveZone.cpp | 129 + src/xrGameLA/RadioactiveZone.h | 22 + src/xrGameLA/RegistryFuncs.cpp | 171 + src/xrGameLA/RegistryFuncs.h | 10 + src/xrGameLA/RocketLauncher.cpp | 133 + src/xrGameLA/RocketLauncher.h | 32 + src/xrGameLA/RustyHairArtifact.cpp | 22 + src/xrGameLA/RustyHairArtifact.h | 20 + src/xrGameLA/ScientificOutfit.cpp | 17 + src/xrGameLA/ScientificOutfit.h | 18 + src/xrGameLA/Scope.cpp | 22 + src/xrGameLA/Scope.h | 21 + src/xrGameLA/ScriptXMLInit.cpp | 277 + src/xrGameLA/ScriptXMLInit.h | 67 + src/xrGameLA/ShapeData.h | 24 + src/xrGameLA/ShellHit.cpp | 65 + src/xrGameLA/ShootingHitEffector.h | 55 + src/xrGameLA/ShootingObject.cpp | 507 ++ src/xrGameLA/ShootingObject.h | 201 + src/xrGameLA/Silencer.cpp | 52 + src/xrGameLA/Silencer.h | 26 + src/xrGameLA/SimpleDetector.cpp | 12 + src/xrGameLA/SimpleDetector.h | 10 + src/xrGameLA/SimpleDetector2.cpp | 418 ++ src/xrGameLA/SimpleDetector2.h | 27 + src/xrGameLA/SleepEffector.cpp | 94 + src/xrGameLA/SleepEffector.h | 45 + src/xrGameLA/SpaceUtils.h | 21 + src/xrGameLA/StalkerOutfit.cpp | 22 + src/xrGameLA/StalkerOutfit.h | 23 + src/xrGameLA/StdAfx.cpp | 2 + src/xrGameLA/StdAfx.h | 37 + src/xrGameLA/Store.cpp | 644 +++ src/xrGameLA/Store.h | 105 + src/xrGameLA/Store_script.cpp | 50 + src/xrGameLA/TeleWhirlwind.cpp | 354 ++ src/xrGameLA/TeleWhirlwind.h | 65 + src/xrGameLA/ThornArtifact.cpp | 22 + src/xrGameLA/ThornArtifact.h | 20 + src/xrGameLA/Torch.cpp | 747 +++ src/xrGameLA/Torch.h | 137 + src/xrGameLA/TorridZone.cpp | 60 + src/xrGameLA/TorridZone.h | 21 + src/xrGameLA/Tracer.cpp | 142 + src/xrGameLA/Tracer.h | 32 + src/xrGameLA/UIActorStateIcons.cpp | 75 + src/xrGameLA/UIActorStateIcons.h | 23 + src/xrGameLA/UICursor.cpp | 127 + src/xrGameLA/UICursor.h | 30 + src/xrGameLA/UIDialogHolder.cpp | 396 ++ src/xrGameLA/UIDialogHolder.h | 62 + src/xrGameLA/UIGameClock.cpp | 9 + src/xrGameLA/UIGameClock.h | 18 + src/xrGameLA/UIGameCustom.cpp | 483 ++ src/xrGameLA/UIGameCustom.h | 162 + src/xrGameLA/UIGameCustom_script.cpp | 39 + src/xrGameLA/UIGameSP.cpp | 334 ++ src/xrGameLA/UIGameSP.h | 73 + src/xrGameLA/UIGameTDM.cpp | 166 + src/xrGameLA/UIGameTDM.h | 48 + src/xrGameLA/UIGame_custom_script.cpp | 29 + src/xrGameLA/UIGame_custom_script.h | 18 + src/xrGameLA/UIStaticItem.cpp | 221 + src/xrGameLA/UIStaticItem.h | 61 + src/xrGameLA/UIZoneMap.cpp | 128 + src/xrGameLA/UIZoneMap.h | 41 + src/xrGameLA/UsableScriptObject.cpp | 50 + src/xrGameLA/UsableScriptObject.h | 25 + src/xrGameLA/WaveForm.h | 64 + src/xrGameLA/Weapon.cpp | 2061 +++++++ src/xrGameLA/Weapon.h | 571 ++ src/xrGameLA/WeaponAK74.cpp | 23 + src/xrGameLA/WeaponAK74.h | 23 + src/xrGameLA/WeaponAmmo.cpp | 249 + src/xrGameLA/WeaponAmmo.h | 82 + src/xrGameLA/WeaponAutomaticShotgun.cpp | 247 + src/xrGameLA/WeaponAutomaticShotgun.h | 45 + src/xrGameLA/WeaponBinoculars.cpp | 122 + src/xrGameLA/WeaponBinoculars.h | 43 + src/xrGameLA/WeaponBinocularsVision.cpp | 292 + src/xrGameLA/WeaponBinocularsVision.h | 46 + src/xrGameLA/WeaponBinoculars_script.cpp | 13 + src/xrGameLA/WeaponCustomPistol.cpp | 18 + src/xrGameLA/WeaponCustomPistol.h | 15 + src/xrGameLA/WeaponDispersion.cpp | 89 + src/xrGameLA/WeaponFN2000.cpp | 25 + src/xrGameLA/WeaponFN2000.h | 28 + src/xrGameLA/WeaponFORT.cpp | 22 + src/xrGameLA/WeaponFORT.h | 26 + src/xrGameLA/WeaponFire.cpp | 151 + src/xrGameLA/WeaponGroza.cpp | 22 + src/xrGameLA/WeaponGroza.h | 18 + src/xrGameLA/WeaponHPSA.cpp | 14 + src/xrGameLA/WeaponHPSA.h | 24 + src/xrGameLA/WeaponHPSA_script.cpp | 14 + src/xrGameLA/WeaponHUD.cpp | 241 + src/xrGameLA/WeaponHUD.h | 148 + src/xrGameLA/WeaponKnife.cpp | 348 ++ src/xrGameLA/WeaponKnife.h | 73 + src/xrGameLA/WeaponKnife_script.cpp | 14 + src/xrGameLA/WeaponLR300.cpp | 20 + src/xrGameLA/WeaponLR300.h | 30 + src/xrGameLA/WeaponMagazined.cpp | 1385 +++++ src/xrGameLA/WeaponMagazined.h | 199 + src/xrGameLA/WeaponMagazinedWGrenade.cpp | 876 +++ src/xrGameLA/WeaponMagazinedWGrenade.h | 95 + src/xrGameLA/WeaponMounted.cpp | 373 ++ src/xrGameLA/WeaponMounted.h | 104 + src/xrGameLA/WeaponPM.cpp | 23 + src/xrGameLA/WeaponPM.h | 24 + src/xrGameLA/WeaponPistol.cpp | 168 + src/xrGameLA/WeaponPistol.h | 37 + src/xrGameLA/WeaponRG6.cpp | 172 + src/xrGameLA/WeaponRG6.h | 29 + src/xrGameLA/WeaponRG6_script.cpp | 14 + src/xrGameLA/WeaponRPG7.cpp | 187 + src/xrGameLA/WeaponRPG7.h | 42 + src/xrGameLA/WeaponRPG7_script.cpp | 14 + src/xrGameLA/WeaponSVD.cpp | 40 + src/xrGameLA/WeaponSVD.h | 21 + src/xrGameLA/WeaponSVU.cpp | 20 + src/xrGameLA/WeaponSVU.h | 18 + src/xrGameLA/WeaponShotgun.cpp | 298 + src/xrGameLA/WeaponShotgun.h | 51 + src/xrGameLA/WeaponStatMgun.cpp | 292 + src/xrGameLA/WeaponStatMgun.h | 104 + src/xrGameLA/WeaponStatMgunFire.cpp | 96 + src/xrGameLA/WeaponStatMgunIR.cpp | 49 + src/xrGameLA/WeaponUSP45.cpp | 20 + src/xrGameLA/WeaponUSP45.h | 18 + src/xrGameLA/WeaponUpgrade.cpp | 259 + src/xrGameLA/WeaponVal.cpp | 20 + src/xrGameLA/WeaponVal.h | 18 + src/xrGameLA/WeaponVintorez.cpp | 20 + src/xrGameLA/WeaponVintorez.h | 18 + src/xrGameLA/WeaponWalther.cpp | 20 + src/xrGameLA/WeaponWalther.h | 18 + src/xrGameLA/WeaponZoomable.cpp | 45 + src/xrGameLA/WeaponZoomable.h | 28 + src/xrGameLA/WeaponZoomable_script.cpp | 13 + src/xrGameLA/Wound.cpp | 99 + src/xrGameLA/Wound.h | 58 + src/xrGameLA/ZoneCampfire.cpp | 178 + src/xrGameLA/ZoneCampfire.h | 34 + src/xrGameLA/ZoneGalantine.cpp | 16 + src/xrGameLA/ZoneGalantine.h | 19 + src/xrGameLA/ZoneMine.cpp | 92 + src/xrGameLA/ZoneMine.h | 35 + src/xrGameLA/ZoneVisual.cpp | 80 + src/xrGameLA/ZoneVisual.h | 24 + src/xrGameLA/ZudaArtifact.cpp | 22 + src/xrGameLA/ZudaArtifact.h | 20 + src/xrGameLA/a_star.h | 135 + src/xrGameLA/a_star_inline.h | 265 + src/xrGameLA/abstract_location_selector.h | 70 + .../abstract_location_selector_inline.h | 151 + src/xrGameLA/abstract_path_manager.h | 87 + src/xrGameLA/abstract_path_manager_inline.h | 186 + src/xrGameLA/action_base.h | 92 + src/xrGameLA/action_base_inline.h | 206 + src/xrGameLA/action_base_script.cpp | 40 + src/xrGameLA/action_management_config.h | 14 + src/xrGameLA/action_planner.h | 118 + src/xrGameLA/action_planner_action.h | 59 + src/xrGameLA/action_planner_action_inline.h | 91 + src/xrGameLA/action_planner_action_script.cpp | 33 + src/xrGameLA/action_planner_action_script.h | 32 + .../action_planner_action_script_inline.h | 58 + src/xrGameLA/action_planner_inline.h | 341 ++ src/xrGameLA/action_planner_script.cpp | 58 + src/xrGameLA/action_planner_script.h | 28 + src/xrGameLA/action_planner_script_inline.h | 29 + src/xrGameLA/action_script_base.h | 31 + src/xrGameLA/action_script_base_inline.h | 49 + src/xrGameLA/actor_anim_defs.h | 97 + src/xrGameLA/actor_communication.cpp | 344 ++ src/xrGameLA/actor_defs.h | 137 + src/xrGameLA/actor_input_handler.cpp | 36 + src/xrGameLA/actor_input_handler.h | 18 + src/xrGameLA/actor_memory.cpp | 50 + src/xrGameLA/actor_memory.h | 34 + src/xrGameLA/actor_mp_client.cpp | 25 + src/xrGameLA/actor_mp_client.h | 29 + src/xrGameLA/actor_mp_client_export.cpp | 118 + src/xrGameLA/actor_mp_client_import.cpp | 134 + src/xrGameLA/actor_mp_server.cpp | 36 + src/xrGameLA/actor_mp_server.h | 31 + src/xrGameLA/actor_mp_server_export.cpp | 43 + src/xrGameLA/actor_mp_server_import.cpp | 44 + src/xrGameLA/actor_mp_state.cpp | 263 + src/xrGameLA/actor_mp_state.h | 51 + src/xrGameLA/actor_mp_state_inline.h | 15 + src/xrGameLA/actor_statistic_defs.h | 33 + src/xrGameLA/actor_statistic_mgr.cpp | 184 + src/xrGameLA/actor_statistic_mgr.h | 19 + src/xrGameLA/agent_corpse_manager.cpp | 120 + src/xrGameLA/agent_corpse_manager.h | 38 + src/xrGameLA/agent_corpse_manager_inline.h | 38 + src/xrGameLA/agent_enemy_manager.cpp | 721 +++ src/xrGameLA/agent_enemy_manager.h | 65 + src/xrGameLA/agent_enemy_manager_inline.h | 28 + src/xrGameLA/agent_explosive_manager.cpp | 137 + src/xrGameLA/agent_explosive_manager.h | 40 + src/xrGameLA/agent_explosive_manager_inline.h | 26 + src/xrGameLA/agent_location_manager.cpp | 208 + src/xrGameLA/agent_location_manager.h | 47 + src/xrGameLA/agent_location_manager_inline.h | 54 + src/xrGameLA/agent_manager.cpp | 132 + src/xrGameLA/agent_manager.h | 78 + src/xrGameLA/agent_manager_actions.cpp | 99 + src/xrGameLA/agent_manager_actions.h | 69 + src/xrGameLA/agent_manager_inline.h | 56 + src/xrGameLA/agent_manager_planner.cpp | 73 + src/xrGameLA/agent_manager_planner.h | 25 + src/xrGameLA/agent_manager_properties.cpp | 66 + src/xrGameLA/agent_manager_properties.h | 59 + .../agent_manager_properties_inline.h | 24 + src/xrGameLA/agent_manager_space.h | 31 + src/xrGameLA/agent_member_manager.cpp | 281 + src/xrGameLA/agent_member_manager.h | 67 + src/xrGameLA/agent_member_manager_inline.h | 98 + src/xrGameLA/agent_memory_manager.cpp | 96 + src/xrGameLA/agent_memory_manager.h | 64 + src/xrGameLA/agent_memory_manager_inline.h | 61 + src/xrGameLA/ai/ai_monsters_anims.h | 58 + src/xrGameLA/ai/ai_monsters_misc.cpp | 204 + src/xrGameLA/ai/ai_monsters_misc.h | 105 + src/xrGameLA/ai/crow/ai_crow.cpp | 441 ++ src/xrGameLA/ai/crow/ai_crow.h | 140 + src/xrGameLA/ai/monsters/ai_monster_bones.cpp | 169 + src/xrGameLA/ai/monsters/ai_monster_bones.h | 55 + src/xrGameLA/ai/monsters/ai_monster_defs.h | 513 ++ .../ai/monsters/ai_monster_effector.cpp | 100 + .../ai/monsters/ai_monster_effector.h | 41 + .../ai/monsters/ai_monster_motion_stats.cpp | 53 + .../ai/monsters/ai_monster_motion_stats.h | 25 + .../ai/monsters/ai_monster_shared_data.h | 41 + src/xrGameLA/ai/monsters/ai_monster_squad.cpp | 218 + src/xrGameLA/ai/monsters/ai_monster_squad.h | 183 + .../ai/monsters/ai_monster_squad_attack.cpp | 136 + .../ai/monsters/ai_monster_squad_manager.cpp | 112 + .../ai/monsters/ai_monster_squad_manager.h | 40 + .../ai_monster_squad_manager_inline.h | 9 + .../ai/monsters/ai_monster_squad_rest.cpp | 144 + src/xrGameLA/ai/monsters/ai_monster_utils.cpp | 42 + src/xrGameLA/ai/monsters/ai_monster_utils.h | 103 + src/xrGameLA/ai/monsters/anim_triple.cpp | 118 + src/xrGameLA/ai/monsters/anim_triple.h | 47 + src/xrGameLA/ai/monsters/anomaly_detector.cpp | 96 + src/xrGameLA/ai/monsters/anomaly_detector.h | 47 + .../ai/monsters/basemonster/base_monster.cpp | 584 ++ .../ai/monsters/basemonster/base_monster.h | 465 ++ .../basemonster/base_monster_anim.cpp | 21 + .../basemonster/base_monster_debug.cpp | 288 + .../basemonster/base_monster_feel.cpp | 336 ++ .../basemonster/base_monster_inline.h | 11 + .../basemonster/base_monster_misc.cpp | 47 + .../monsters/basemonster/base_monster_net.cpp | 108 + .../basemonster/base_monster_path.cpp | 75 + .../basemonster/base_monster_script.cpp | 347 ++ .../basemonster/base_monster_startup.cpp | 393 ++ .../basemonster/base_monster_think.cpp | 112 + .../ai/monsters/bloodsucker/bloodsucker.cpp | 517 ++ .../ai/monsters/bloodsucker/bloodsucker.h | 165 + .../bloodsucker/bloodsucker_alien.cpp | 267 + .../monsters/bloodsucker/bloodsucker_alien.h | 30 + .../bloodsucker/bloodsucker_attack_state.h | 26 + .../bloodsucker_attack_state_hide.h | 29 + .../bloodsucker_attack_state_hide_inline.h | 149 + .../bloodsucker_attack_state_inline.h | 213 + .../bloodsucker/bloodsucker_predator.h | 31 + .../bloodsucker/bloodsucker_predator_inline.h | 220 + .../bloodsucker/bloodsucker_predator_lite.h | 32 + .../bloodsucker_predator_lite_inline.h | 236 + .../bloodsucker/bloodsucker_script.cpp | 14 + .../bloodsucker/bloodsucker_state_manager.cpp | 90 + .../bloodsucker/bloodsucker_state_manager.h | 12 + .../bloodsucker/bloodsucker_vampire.h | 33 + .../bloodsucker_vampire_approach.h | 16 + .../bloodsucker_vampire_approach_inline.h | 39 + .../bloodsucker_vampire_effector.cpp | 144 + .../bloodsucker_vampire_effector.h | 41 + .../bloodsucker/bloodsucker_vampire_execute.h | 40 + .../bloodsucker_vampire_execute_inline.h | 210 + .../bloodsucker/bloodsucker_vampire_hide.h | 17 + .../bloodsucker_vampire_hide_inline.h | 56 + .../bloodsucker/bloodsucker_vampire_inline.h | 169 + src/xrGameLA/ai/monsters/boar/boar.cpp | 184 + src/xrGameLA/ai/monsters/boar/boar.h | 41 + src/xrGameLA/ai/monsters/boar/boar_script.cpp | 14 + .../ai/monsters/boar/boar_state_manager.cpp | 66 + .../ai/monsters/boar/boar_state_manager.h | 14 + src/xrGameLA/ai/monsters/burer/burer.cpp | 393 ++ src/xrGameLA/ai/monsters/burer/burer.h | 159 + .../ai/monsters/burer/burer_fast_gravi.cpp | 47 + .../ai/monsters/burer/burer_fast_gravi.h | 18 + .../ai/monsters/burer/burer_script.cpp | 14 + .../ai/monsters/burer/burer_state_attack.h | 21 + .../monsters/burer/burer_state_attack_gravi.h | 38 + .../burer/burer_state_attack_gravi_inline.h | 160 + .../burer/burer_state_attack_inline.h | 134 + .../monsters/burer/burer_state_attack_melee.h | 16 + .../burer/burer_state_attack_melee_inline.h | 37 + .../burer/burer_state_attack_run_around.h | 22 + .../burer_state_attack_run_around_inline.h | 89 + .../burer/burer_state_attack_shield.h | 33 + .../burer/burer_state_attack_shield_inline.h | 119 + .../monsters/burer/burer_state_attack_tele.h | 58 + .../burer/burer_state_attack_tele_inline.h | 363 ++ .../ai/monsters/burer/burer_state_manager.cpp | 90 + .../ai/monsters/burer/burer_state_manager.h | 16 + src/xrGameLA/ai/monsters/cat/cat.cpp | 158 + src/xrGameLA/ai/monsters/cat/cat.h | 29 + src/xrGameLA/ai/monsters/cat/cat_script.cpp | 14 + .../ai/monsters/cat/cat_state_manager.cpp | 77 + .../ai/monsters/cat/cat_state_manager.h | 17 + src/xrGameLA/ai/monsters/chimera/chimera.cpp | 246 + src/xrGameLA/ai/monsters/chimera/chimera.h | 36 + .../ai/monsters/chimera/chimera_script.cpp | 14 + .../monsters/chimera/chimera_state_hunting.h | 24 + .../chimera/chimera_state_hunting_come_out.h | 18 + .../chimera_state_hunting_come_out_inline.h | 37 + .../chimera/chimera_state_hunting_inline.h | 42 + .../chimera_state_hunting_move_to_cover.h | 18 + ...imera_state_hunting_move_to_cover_inline.h | 36 + .../chimera/chimera_state_manager.cpp | 69 + .../monsters/chimera/chimera_state_manager.h | 15 + .../monsters/chimera/chimera_state_threaten.h | 35 + .../chimera/chimera_state_threaten_inline.h | 113 + .../chimera/chimera_state_threaten_roar.h | 16 + .../chimera_state_threaten_roar_inline.h | 36 + .../chimera/chimera_state_threaten_steal.h | 17 + .../chimera_state_threaten_steal_inline.h | 67 + .../chimera/chimera_state_threaten_walk.h | 17 + .../chimera_state_threaten_walk_inline.h | 65 + .../ai/monsters/control_animation.cpp | 257 + src/xrGameLA/ai/monsters/control_animation.h | 109 + .../ai/monsters/control_animation_base.cpp | 605 ++ .../ai/monsters/control_animation_base.h | 218 + .../monsters/control_animation_base_accel.cpp | 153 + .../monsters/control_animation_base_load.cpp | 143 + .../control_animation_base_update.cpp | 303 + src/xrGameLA/ai/monsters/control_com_defs.h | 74 + src/xrGameLA/ai/monsters/control_combase.h | 150 + .../ai/monsters/control_critical_wound.cpp | 51 + .../ai/monsters/control_critical_wound.h | 17 + .../ai/monsters/control_direction.cpp | 194 + src/xrGameLA/ai/monsters/control_direction.h | 58 + .../ai/monsters/control_direction_base.cpp | 76 + .../ai/monsters/control_direction_base.h | 38 + src/xrGameLA/ai/monsters/control_jump.cpp | 528 ++ src/xrGameLA/ai/monsters/control_jump.h | 127 + src/xrGameLA/ai/monsters/control_manager.cpp | 419 ++ src/xrGameLA/ai/monsters/control_manager.h | 104 + .../ai/monsters/control_manager_custom.cpp | 619 ++ .../ai/monsters/control_manager_custom.h | 99 + .../ai/monsters/control_melee_jump.cpp | 93 + src/xrGameLA/ai/monsters/control_melee_jump.h | 24 + src/xrGameLA/ai/monsters/control_movement.cpp | 44 + src/xrGameLA/ai/monsters/control_movement.h | 24 + .../ai/monsters/control_movement_base.cpp | 118 + .../ai/monsters/control_movement_base.h | 32 + .../ai/monsters/control_path_builder.cpp | 297 + .../ai/monsters/control_path_builder.h | 68 + .../ai/monsters/control_path_builder_base.cpp | 167 + .../ai/monsters/control_path_builder_base.h | 178 + .../control_path_builder_base_inline.h | 32 + .../control_path_builder_base_path.cpp | 199 + .../control_path_builder_base_set.cpp | 69 + .../control_path_builder_base_update.cpp | 111 + .../ai/monsters/control_rotation_jump.cpp | 255 + .../ai/monsters/control_rotation_jump.h | 51 + .../ai/monsters/control_run_attack.cpp | 129 + src/xrGameLA/ai/monsters/control_run_attack.h | 22 + .../ai/monsters/control_sequencer.cpp | 62 + src/xrGameLA/ai/monsters/control_sequencer.h | 24 + src/xrGameLA/ai/monsters/control_threaten.cpp | 86 + src/xrGameLA/ai/monsters/control_threaten.h | 20 + src/xrGameLA/ai/monsters/controlled_actor.cpp | 125 + src/xrGameLA/ai/monsters/controlled_actor.h | 44 + src/xrGameLA/ai/monsters/controlled_entity.h | 76 + .../ai/monsters/controlled_entity_inline.h | 70 + .../ai/monsters/controller/controller.cpp | 795 +++ .../ai/monsters/controller/controller.h | 190 + .../controller/controller_animation.cpp | 364 ++ .../controller/controller_animation.h | 109 + .../controller/controller_direction.cpp | 124 + .../controller/controller_direction.h | 37 + .../controller/controller_psy_aura.cpp | 306 + .../monsters/controller/controller_psy_aura.h | 88 + .../controller/controller_psy_hit.cpp | 330 ++ .../monsters/controller/controller_psy_hit.h | 52 + .../controller_psy_hit_effector.cpp | 44 + .../controller/controller_psy_hit_effector.h | 39 + .../monsters/controller/controller_script.cpp | 14 + .../controller/controller_state_attack.h | 29 + .../controller/controller_state_attack_camp.h | 29 + .../controller_state_attack_camp_inline.h | 107 + .../controller_state_attack_fast_move.h | 18 + ...controller_state_attack_fast_move_inline.h | 43 + .../controller/controller_state_attack_fire.h | 25 + .../controller_state_attack_fire_inline.h | 76 + .../controller/controller_state_attack_hide.h | 40 + .../controller_state_attack_hide_inline.h | 107 + .../controller_state_attack_hide_lite.h | 38 + ...controller_state_attack_hide_lite_inline.h | 91 + .../controller_state_attack_inline.h | 124 + .../controller_state_attack_moveout.h | 41 + .../controller_state_attack_moveout_inline.h | 118 + .../controller/controller_state_control_hit.h | 37 + .../controller_state_control_hit_inline.h | 126 + .../controller/controller_state_manager.cpp | 94 + .../controller/controller_state_manager.h | 16 + .../controller/controller_state_panic.h | 23 + .../ai/monsters/controller/controller_tube.h | 16 + .../controller/controller_tube_inline.h | 47 + src/xrGameLA/ai/monsters/corpse_cover.cpp | 29 + src/xrGameLA/ai/monsters/corpse_cover.h | 31 + src/xrGameLA/ai/monsters/custom_events.h | 22 + src/xrGameLA/ai/monsters/dog/dog.cpp | 166 + src/xrGameLA/ai/monsters/dog/dog.h | 37 + src/xrGameLA/ai/monsters/dog/dog_script.cpp | 14 + .../ai/monsters/dog/dog_state_manager.cpp | 64 + .../ai/monsters/dog/dog_state_manager.h | 13 + src/xrGameLA/ai/monsters/energy_holder.cpp | 75 + src/xrGameLA/ai/monsters/energy_holder.h | 59 + src/xrGameLA/ai/monsters/flesh/flesh.cpp | 157 + src/xrGameLA/ai/monsters/flesh/flesh.h | 33 + .../ai/monsters/flesh/flesh_script.cpp | 14 + .../ai/monsters/flesh/flesh_state_manager.cpp | 69 + .../ai/monsters/flesh/flesh_state_manager.h | 13 + .../ai/monsters/fracture/fracture.cpp | 93 + src/xrGameLA/ai/monsters/fracture/fracture.h | 22 + .../ai/monsters/fracture/fracture_script.cpp | 14 + .../fracture/fracture_state_manager.cpp | 63 + .../fracture/fracture_state_manager.h | 14 + .../ai/monsters/ghostboss/ghostboss.cpp | 464 ++ .../ai/monsters/ghostboss/ghostboss.h | 171 + .../ghostboss/ghostboss_fast_gravi.cpp | 47 + .../monsters/ghostboss/ghostboss_fast_gravi.h | 18 + .../monsters/ghostboss/ghostboss_script.cpp | 14 + .../ghostboss/ghostboss_state_attack.h | 21 + .../ghostboss/ghostboss_state_attack_gravi.h | 38 + .../ghostboss_state_attack_gravi_inline.h | 160 + .../ghostboss/ghostboss_state_attack_inline.h | 135 + .../ghostboss/ghostboss_state_attack_melee.h | 16 + .../ghostboss_state_attack_melee_inline.h | 37 + .../ghostboss_state_attack_run_around.h | 22 + ...ghostboss_state_attack_run_around_inline.h | 89 + .../ghostboss/ghostboss_state_attack_shield.h | 33 + .../ghostboss_state_attack_shield_inline.h | 119 + .../ghostboss/ghostboss_state_attack_tele.h | 58 + .../ghostboss_state_attack_tele_inline.h | 363 ++ .../ghostboss_state_attack_teleport.h | 28 + .../ghostboss_state_attack_teleport_inline.h | 102 + .../ghostboss/ghostboss_state_manager.cpp | 96 + .../ghostboss/ghostboss_state_manager.h | 16 + src/xrGameLA/ai/monsters/invisibility.cpp | 105 + src/xrGameLA/ai/monsters/invisibility.h | 49 + src/xrGameLA/ai/monsters/karlik/karlik.cpp | 95 + src/xrGameLA/ai/monsters/karlik/karlik.h | 28 + .../ai/monsters/karlik/karlik_script.cpp | 15 + .../monsters/karlik/karlik_state_manager.cpp | 67 + .../ai/monsters/karlik/karlik_state_manager.h | 17 + src/xrGameLA/ai/monsters/melee_checker.cpp | 54 + src/xrGameLA/ai/monsters/melee_checker.h | 49 + .../ai/monsters/melee_checker_inline.h | 35 + .../ai/monsters/monster_corpse_manager.cpp | 71 + .../ai/monsters/monster_corpse_manager.h | 32 + .../ai/monsters/monster_corpse_memory.cpp | 117 + .../ai/monsters/monster_corpse_memory.h | 34 + .../ai/monsters/monster_cover_manager.cpp | 234 + .../ai/monsters/monster_cover_manager.h | 23 + .../ai/monsters/monster_enemy_manager.cpp | 251 + .../ai/monsters/monster_enemy_manager.h | 73 + .../ai/monsters/monster_enemy_memory.cpp | 157 + .../ai/monsters/monster_enemy_memory.h | 40 + .../ai/monsters/monster_event_manager.cpp | 58 + .../ai/monsters/monster_event_manager.h | 38 + .../ai/monsters/monster_event_manager_defs.h | 17 + .../ai/monsters/monster_hit_memory.cpp | 178 + src/xrGameLA/ai/monsters/monster_hit_memory.h | 39 + src/xrGameLA/ai/monsters/monster_home.cpp | 138 + src/xrGameLA/ai/monsters/monster_home.h | 28 + src/xrGameLA/ai/monsters/monster_morale.cpp | 48 + src/xrGameLA/ai/monsters/monster_morale.h | 54 + .../ai/monsters/monster_morale_inline.h | 32 + src/xrGameLA/ai/monsters/monster_sound_defs.h | 42 + .../ai/monsters/monster_sound_memory.cpp | 213 + .../ai/monsters/monster_sound_memory.h | 102 + .../ai/monsters/monster_state_manager.h | 25 + .../monsters/monster_state_manager_inline.h | 67 + .../ai/monsters/monster_velocity_space.h | 59 + .../ai/monsters/poltergeist/poltergeist.cpp | 315 + .../ai/monsters/poltergeist/poltergeist.h | 277 + .../poltergeist/poltergeist_ability.cpp | 157 + .../poltergeist/poltergeist_flame_thrower.cpp | 340 ++ .../poltergeist/poltergeist_movement.cpp | 114 + .../poltergeist/poltergeist_movement.h | 21 + .../poltergeist/poltergeist_script.cpp | 14 + .../poltergeist_state_attack_hidden.h | 30 + .../poltergeist_state_attack_hidden_inline.h | 56 + .../poltergeist/poltergeist_state_manager.cpp | 127 + .../poltergeist/poltergeist_state_manager.h | 27 + .../poltergeist/poltergeist_state_rest.h | 56 + .../poltergeist/poltergeist_telekinesis.cpp | 268 + .../ai/monsters/pseudodog/pseudodog.cpp | 183 + .../ai/monsters/pseudodog/pseudodog.h | 52 + .../pseudodog/pseudodog_psi_effector.cpp | 2 + .../pseudodog/pseudodog_psi_effector.h | 1 + .../monsters/pseudodog/pseudodog_script.cpp | 33 + .../pseudodog/pseudodog_state_manager.cpp | 64 + .../pseudodog/pseudodog_state_manager.h | 13 + .../ai/monsters/pseudodog/psy_dog.cpp | 311 + src/xrGameLA/ai/monsters/pseudodog/psy_dog.h | 109 + .../ai/monsters/pseudodog/psy_dog_aura.cpp | 118 + .../ai/monsters/pseudodog/psy_dog_aura.h | 38 + .../pseudodog/psy_dog_state_manager.cpp | 36 + .../pseudodog/psy_dog_state_manager.h | 9 + .../pseudodog/psy_dog_state_psy_attack.h | 18 + .../pseudodog/psy_dog_state_psy_attack_hide.h | 29 + .../psy_dog_state_psy_attack_hide_inline.h | 69 + .../psy_dog_state_psy_attack_inline.h | 24 + .../monsters/pseudogigant/pseudo_gigant.cpp | 323 ++ .../ai/monsters/pseudogigant/pseudo_gigant.h | 68 + .../pseudo_gigant_step_effector.cpp | 53 + .../pseudo_gigant_step_effector.h | 15 + .../pseudogigant/pseudogigant_script.cpp | 14 + .../pseudogigant_state_manager.cpp | 67 + .../pseudogigant/pseudogigant_state_manager.h | 12 + src/xrGameLA/ai/monsters/psy_aura.cpp | 23 + src/xrGameLA/ai/monsters/psy_aura.h | 34 + src/xrGameLA/ai/monsters/rats/ai_rat.cpp | 705 +++ src/xrGameLA/ai/monsters/rats/ai_rat.h | 444 ++ .../ai/monsters/rats/ai_rat_animations.cpp | 101 + .../ai/monsters/rats/ai_rat_behaviour.cpp | 50 + src/xrGameLA/ai/monsters/rats/ai_rat_feel.cpp | 55 + src/xrGameLA/ai/monsters/rats/ai_rat_fire.cpp | 159 + src/xrGameLA/ai/monsters/rats/ai_rat_fsm.cpp | 661 +++ src/xrGameLA/ai/monsters/rats/ai_rat_impl.h | 76 + src/xrGameLA/ai/monsters/rats/ai_rat_inline.h | 55 + src/xrGameLA/ai/monsters/rats/ai_rat_space.h | 30 + .../ai/monsters/rats/ai_rat_templates.cpp | 552 ++ .../ai/monsters/rats/rat_state_activation.cpp | 187 + .../ai/monsters/rats/rat_state_initialize.cpp | 53 + .../ai/monsters/rats/rat_state_switch.cpp | 331 ++ src/xrGameLA/ai/monsters/scanning_ability.h | 61 + .../ai/monsters/scanning_ability_inline.h | 161 + src/xrGameLA/ai/monsters/snork/snork.cpp | 306 + src/xrGameLA/ai/monsters/snork/snork.h | 49 + src/xrGameLA/ai/monsters/snork/snork_jump.cpp | 178 + src/xrGameLA/ai/monsters/snork/snork_jump.h | 31 + .../ai/monsters/snork/snork_script.cpp | 14 + .../ai/monsters/snork/snork_state_manager.cpp | 83 + .../ai/monsters/snork/snork_state_manager.h | 16 + src/xrGameLA/ai/monsters/state.h | 75 + src/xrGameLA/ai/monsters/state_defs.h | 206 + src/xrGameLA/ai/monsters/state_inline.h | 163 + src/xrGameLA/ai/monsters/state_manager.h | 13 + .../ai/monsters/states/monster_state_attack.h | 36 + .../states/monster_state_attack_camp.h | 27 + .../states/monster_state_attack_camp_inline.h | 170 + .../monster_state_attack_camp_stealout.h | 17 + ...onster_state_attack_camp_stealout_inline.h | 57 + .../states/monster_state_attack_inline.h | 290 + .../states/monster_state_attack_melee.h | 19 + .../monster_state_attack_melee_inline.h | 47 + .../states/monster_state_attack_run.h | 24 + .../states/monster_state_attack_run_attack.h | 20 + .../monster_state_attack_run_attack_inline.h | 67 + .../states/monster_state_attack_run_inline.h | 87 + .../states/monster_state_controlled.h | 14 + .../states/monster_state_controlled_attack.h | 21 + .../monster_state_controlled_attack_inline.h | 51 + .../states/monster_state_controlled_follow.h | 16 + .../monster_state_controlled_follow_inline.h | 89 + .../states/monster_state_controlled_inline.h | 43 + .../ai/monsters/states/monster_state_eat.h | 35 + .../monsters/states/monster_state_eat_drag.h | 26 + .../states/monster_state_eat_drag_inline.h | 113 + .../monsters/states/monster_state_eat_eat.h | 23 + .../states/monster_state_eat_eat_inline.h | 81 + .../states/monster_state_eat_inline.h | 258 + .../states/monster_state_find_enemy.h | 20 + .../states/monster_state_find_enemy_angry.h | 17 + .../monster_state_find_enemy_angry_inline.h | 33 + .../states/monster_state_find_enemy_inline.h | 43 + .../states/monster_state_find_enemy_look.h | 34 + .../monster_state_find_enemy_look_inline.h | 114 + .../states/monster_state_find_enemy_run.h | 21 + .../monster_state_find_enemy_run_inline.h | 69 + .../states/monster_state_find_enemy_walk.h | 15 + .../monster_state_find_enemy_walk_inline.h | 15 + .../states/monster_state_hear_danger_sound.h | 20 + .../monster_state_hear_danger_sound_inline.h | 99 + .../states/monster_state_hear_int_sound.h | 23 + .../monster_state_hear_int_sound_inline.h | 81 + .../states/monster_state_help_sound.h | 23 + .../states/monster_state_help_sound_inline.h | 84 + .../ai/monsters/states/monster_state_hitted.h | 18 + .../states/monster_state_hitted_hide.h | 21 + .../states/monster_state_hitted_hide_inline.h | 55 + .../states/monster_state_hitted_inline.h | 48 + .../states/monster_state_hitted_moveout.h | 30 + .../monster_state_hitted_moveout_inline.h | 63 + .../states/monster_state_home_point_attack.h | 26 + .../monster_state_home_point_attack_inline.h | 167 + .../states/monster_state_home_point_danger.h | 29 + .../monster_state_home_point_danger_inline.h | 183 + .../states/monster_state_home_point_rest.h | 20 + .../monster_state_home_point_rest_inline.h | 45 + .../ai/monsters/states/monster_state_panic.h | 20 + .../states/monster_state_panic_inline.h | 86 + .../monsters/states/monster_state_panic_run.h | 17 + .../states/monster_state_panic_run_inline.h | 45 + .../ai/monsters/states/monster_state_rest.h | 25 + .../monsters/states/monster_state_rest_fun.h | 20 + .../states/monster_state_rest_fun_inline.h | 88 + .../monsters/states/monster_state_rest_idle.h | 22 + .../states/monster_state_rest_idle_inline.h | 136 + .../states/monster_state_rest_inline.h | 134 + .../states/monster_state_rest_sleep.h | 19 + .../states/monster_state_rest_sleep_inline.h | 46 + .../states/monster_state_rest_walk_graph.h | 15 + .../monster_state_rest_walk_graph_inline.h | 26 + .../states/monster_state_smart_terrain_task.h | 24 + ...ster_state_smart_terrain_task_graph_walk.h | 18 + ...ate_smart_terrain_task_graph_walk_inline.h | 59 + .../monster_state_smart_terrain_task_inline.h | 170 + .../states/monster_state_squad_rest.h | 21 + .../states/monster_state_squad_rest_follow.h | 23 + .../monster_state_squad_rest_follow_inline.h | 104 + .../states/monster_state_squad_rest_inline.h | 90 + .../ai/monsters/states/monster_state_steal.h | 21 + .../states/monster_state_steal_inline.h | 86 + .../ai/monsters/states/state_custom_action.h | 19 + .../states/state_custom_action_inline.h | 46 + .../states/state_custom_action_look.h | 19 + .../states/state_custom_action_look_inline.h | 47 + src/xrGameLA/ai/monsters/states/state_data.h | 136 + .../monsters/states/state_hide_from_point.h | 22 + .../states/state_hide_from_point_inline.h | 55 + .../ai/monsters/states/state_hit_object.h | 21 + .../monsters/states/state_hit_object_inline.h | 91 + .../ai/monsters/states/state_look_point.h | 21 + .../monsters/states/state_look_point_inline.h | 51 + .../states/state_look_unprotected_area.h | 26 + .../state_look_unprotected_area_inline.h | 67 + .../monsters/states/state_move_around_point.h | 22 + .../states/state_move_around_point_inline.h | 46 + .../ai/monsters/states/state_move_to_point.h | 39 + .../states/state_move_to_point_inline.h | 97 + .../states/state_move_to_restrictor.h | 19 + .../states/state_move_to_restrictor_inline.h | 43 + .../monsters/states/state_test_look_actor.h | 30 + .../states/state_test_look_actor_inline.h | 49 + .../ai/monsters/states/state_test_state.h | 31 + .../monsters/states/state_test_state_inline.h | 137 + .../ai/monsters/swampbeast/swampbeast.cpp | 157 + .../ai/monsters/swampbeast/swampbeast.h | 33 + .../monsters/swampbeast/swampbeast_script.cpp | 15 + .../swampbeast/swampbeast_state_manager.cpp | 69 + .../swampbeast/swampbeast_state_manager.h | 13 + src/xrGameLA/ai/monsters/telekinesis.cpp | 230 + src/xrGameLA/ai/monsters/telekinesis.h | 82 + src/xrGameLA/ai/monsters/telekinesis_inline.h | 143 + .../ai/monsters/telekinetic_object.cpp | 284 + src/xrGameLA/ai/monsters/telekinetic_object.h | 81 + .../ai/monsters/tushkano/tushkano.cpp | 96 + src/xrGameLA/ai/monsters/tushkano/tushkano.h | 25 + .../ai/monsters/tushkano/tushkano_script.cpp | 14 + .../tushkano/tushkano_state_manager.cpp | 72 + .../tushkano/tushkano_state_manager.h | 14 + src/xrGameLA/ai/monsters/zombie/zombie.cpp | 286 + src/xrGameLA/ai/monsters/zombie/zombie.h | 83 + .../ai/monsters/zombie/zombie_choke.h | 29 + .../monsters/zombie/zombie_choke_effector.cpp | 41 + .../monsters/zombie/zombie_choke_effector.h | 19 + .../ai/monsters/zombie/zombie_choke_execute.h | 40 + .../zombie/zombie_choke_execute_inline.h | 200 + .../ai/monsters/zombie/zombie_choke_inline.h | 105 + .../ai/monsters/zombie/zombie_script.cpp | 14 + .../monsters/zombie/zombie_state_attack_run.h | 26 + .../zombie/zombie_state_attack_run_inline.h | 122 + .../monsters/zombie/zombie_state_manager.cpp | 75 + .../ai/monsters/zombie/zombie_state_manager.h | 15 + src/xrGameLA/ai/phantom/phantom.cpp | 397 ++ src/xrGameLA/ai/phantom/phantom.h | 87 + src/xrGameLA/ai/rats/ai_rat.cpp | 705 +++ src/xrGameLA/ai/rats/ai_rat_animations.cpp | 101 + src/xrGameLA/ai/rats/ai_rat_behaviour.cpp | 50 + src/xrGameLA/ai/rats/ai_rat_feel.cpp | 55 + src/xrGameLA/ai/rats/ai_rat_fire.cpp | 159 + src/xrGameLA/ai/rats/ai_rat_fsm.cpp | 661 +++ src/xrGameLA/ai/rats/ai_rat_templates.cpp | 552 ++ src/xrGameLA/ai/rats/rat_state_activation.cpp | 186 + src/xrGameLA/ai/rats/rat_state_initialize.cpp | 53 + src/xrGameLA/ai/rats/rat_state_switch.cpp | 331 ++ src/xrGameLA/ai/stalker/ai_stalker.cpp | 1098 ++++ src/xrGameLA/ai/stalker/ai_stalker.h | 606 ++ src/xrGameLA/ai/stalker/ai_stalker_cover.cpp | 218 + src/xrGameLA/ai/stalker/ai_stalker_debug.cpp | 1086 ++++ src/xrGameLA/ai/stalker/ai_stalker_events.cpp | 158 + src/xrGameLA/ai/stalker/ai_stalker_feel.cpp | 80 + src/xrGameLA/ai/stalker/ai_stalker_fire.cpp | 1252 ++++ src/xrGameLA/ai/stalker/ai_stalker_impl.h | 38 + src/xrGameLA/ai/stalker/ai_stalker_inline.h | 173 + src/xrGameLA/ai/stalker/ai_stalker_misc.cpp | 189 + src/xrGameLA/ai/stalker/ai_stalker_script.cpp | 171 + .../ai/stalker/ai_stalker_script_entity.cpp | 307 + src/xrGameLA/ai/stalker/ai_stalker_space.h | 83 + src/xrGameLA/ai/trader/ai_trader.cpp | 394 ++ src/xrGameLA/ai/trader/ai_trader.h | 145 + src/xrGameLA/ai/trader/ai_trader_script.cpp | 14 + src/xrGameLA/ai/trader/trader_animation.cpp | 131 + src/xrGameLA/ai/trader/trader_animation.h | 49 + src/xrGameLA/ai_debug.h | 57 + src/xrGameLA/ai_monster_space.h | 131 + src/xrGameLA/ai_object_location.h | 36 + src/xrGameLA/ai_object_location_impl.h | 63 + src/xrGameLA/ai_object_location_inline.h | 29 + src/xrGameLA/ai_sounds.cpp | 29 + src/xrGameLA/ai_sounds.h | 85 + src/xrGameLA/ai_space.cpp | 258 + src/xrGameLA/ai_space.h | 87 + src/xrGameLA/ai_space_inline.h | 99 + src/xrGameLA/ai_stalker_alife.cpp | 465 ++ src/xrGameLA/alife_abstract_registry.h | 37 + src/xrGameLA/alife_abstract_registry_inline.h | 76 + src/xrGameLA/alife_anomalous_zone.cpp | 158 + src/xrGameLA/alife_combat_manager.cpp | 474 ++ src/xrGameLA/alife_combat_manager.h | 49 + src/xrGameLA/alife_combat_manager_inline.h | 16 + src/xrGameLA/alife_communication_manager.cpp | 905 +++ src/xrGameLA/alife_communication_manager.h | 79 + .../alife_communication_manager_inline.h | 10 + src/xrGameLA/alife_communication_space.h | 23 + src/xrGameLA/alife_creature_abstract.cpp | 43 + src/xrGameLA/alife_dynamic_object.cpp | 344 ++ src/xrGameLA/alife_graph_registry.cpp | 187 + src/xrGameLA/alife_graph_registry.h | 80 + src/xrGameLA/alife_graph_registry_inline.h | 67 + src/xrGameLA/alife_group_abstract.cpp | 201 + src/xrGameLA/alife_group_registry.cpp | 52 + src/xrGameLA/alife_group_registry.h | 33 + src/xrGameLA/alife_group_registry_inline.h | 14 + src/xrGameLA/alife_human_abstract.cpp | 97 + src/xrGameLA/alife_human_brain.cpp | 109 + src/xrGameLA/alife_human_brain.h | 55 + src/xrGameLA/alife_human_brain_inline.h | 21 + src/xrGameLA/alife_human_brain_save.h | 79 + src/xrGameLA/alife_human_brain_script.cpp | 21 + src/xrGameLA/alife_human_object_handler.cpp | 100 + src/xrGameLA/alife_human_object_handler.h | 55 + .../alife_human_object_handler_inline.h | 21 + .../alife_human_object_handler_save.h | 610 ++ src/xrGameLA/alife_interaction_manager.cpp | 206 + src/xrGameLA/alife_interaction_manager.h | 36 + .../alife_interaction_manager_inline.h | 10 + src/xrGameLA/alife_level_registry.h | 37 + src/xrGameLA/alife_level_registry_inline.h | 69 + src/xrGameLA/alife_monster_abstract.cpp | 228 + src/xrGameLA/alife_monster_base.cpp | 45 + src/xrGameLA/alife_monster_brain.cpp | 194 + src/xrGameLA/alife_monster_brain.h | 75 + src/xrGameLA/alife_monster_brain_inline.h | 31 + src/xrGameLA/alife_monster_brain_script.cpp | 30 + .../alife_monster_detail_path_manager.cpp | 274 + .../alife_monster_detail_path_manager.h | 85 + ...alife_monster_detail_path_manager_inline.h | 38 + ...ife_monster_detail_path_manager_script.cpp | 30 + .../alife_monster_movement_manager.cpp | 76 + src/xrGameLA/alife_monster_movement_manager.h | 58 + .../alife_monster_movement_manager_inline.h | 37 + .../alife_monster_movement_manager_script.cpp | 39 + .../alife_monster_patrol_path_manager.cpp | 212 + .../alife_monster_patrol_path_manager.h | 80 + ...alife_monster_patrol_path_manager_inline.h | 77 + ...ife_monster_patrol_path_manager_script.cpp | 40 + src/xrGameLA/alife_object.cpp | 110 + src/xrGameLA/alife_object_registry.cpp | 143 + src/xrGameLA/alife_object_registry.h | 44 + src/xrGameLA/alife_object_registry_inline.h | 58 + src/xrGameLA/alife_online_offline_group.cpp | 281 + .../alife_online_offline_group_brain.cpp | 11 + .../alife_online_offline_group_brain.h | 21 + .../alife_online_offline_group_brain_inline.h | 14 + src/xrGameLA/alife_registry_container.cpp | 106 + src/xrGameLA/alife_registry_container.h | 31 + .../alife_registry_container_composition.h | 85 + .../alife_registry_container_inline.h | 25 + src/xrGameLA/alife_registry_container_space.h | 15 + src/xrGameLA/alife_registry_wrapper.h | 99 + src/xrGameLA/alife_registry_wrappers.h | 60 + src/xrGameLA/alife_schedule_registry.cpp | 36 + src/xrGameLA/alife_schedule_registry.h | 67 + src/xrGameLA/alife_schedule_registry_inline.h | 45 + src/xrGameLA/alife_simulator.cpp | 106 + src/xrGameLA/alife_simulator.h | 43 + src/xrGameLA/alife_simulator_base.cpp | 361 ++ src/xrGameLA/alife_simulator_base.h | 137 + src/xrGameLA/alife_simulator_base2.cpp | 97 + src/xrGameLA/alife_simulator_base_inline.h | 236 + src/xrGameLA/alife_simulator_header.cpp | 39 + src/xrGameLA/alife_simulator_header.h | 27 + src/xrGameLA/alife_simulator_header_inline.h | 19 + src/xrGameLA/alife_simulator_inline.h | 9 + src/xrGameLA/alife_simulator_script.cpp | 642 +++ src/xrGameLA/alife_smart_terrain_registry.cpp | 37 + src/xrGameLA/alife_smart_terrain_registry.h | 31 + .../alife_smart_terrain_registry_inline.h | 21 + src/xrGameLA/alife_smart_terrain_task.cpp | 67 + src/xrGameLA/alife_smart_terrain_task.h | 53 + .../alife_smart_terrain_task_inline.h | 68 + .../alife_smart_terrain_task_script.cpp | 27 + src/xrGameLA/alife_smart_zone.cpp | 34 + src/xrGameLA/alife_space.cpp | 21 + src/xrGameLA/alife_space.h | 177 + src/xrGameLA/alife_spawn_registry.cpp | 250 + src/xrGameLA/alife_spawn_registry.h | 80 + src/xrGameLA/alife_spawn_registry_header.cpp | 26 + src/xrGameLA/alife_spawn_registry_header.h | 31 + .../alife_spawn_registry_header_inline.h | 34 + src/xrGameLA/alife_spawn_registry_inline.h | 61 + src/xrGameLA/alife_spawn_registry_spawn.cpp | 147 + src/xrGameLA/alife_storage_manager.cpp | 192 + src/xrGameLA/alife_storage_manager.h | 36 + src/xrGameLA/alife_storage_manager_inline.h | 16 + src/xrGameLA/alife_story_registry.cpp | 35 + src/xrGameLA/alife_story_registry.h | 30 + src/xrGameLA/alife_story_registry_inline.h | 46 + src/xrGameLA/alife_surge_manager.cpp | 67 + src/xrGameLA/alife_surge_manager.h | 36 + src/xrGameLA/alife_surge_manager_inline.h | 14 + src/xrGameLA/alife_switch_manager.cpp | 288 + src/xrGameLA/alife_switch_manager.h | 49 + src/xrGameLA/alife_switch_manager_inline.h | 46 + src/xrGameLA/alife_time_manager.cpp | 52 + src/xrGameLA/alife_time_manager.h | 38 + src/xrGameLA/alife_time_manager_inline.h | 37 + src/xrGameLA/alife_trader.cpp | 48 + src/xrGameLA/alife_trader_abstract.cpp | 405 ++ src/xrGameLA/alife_update_manager.cpp | 566 ++ src/xrGameLA/alife_update_manager.h | 70 + src/xrGameLA/alife_update_manager_inline.h | 14 + .../animation_movement_controller.cpp | 67 + src/xrGameLA/animation_movement_controller.h | 21 + src/xrGameLA/animation_utils.cpp | 87 + src/xrGameLA/animation_utils.h | 18 + src/xrGameLA/antirad.cpp | 58 + src/xrGameLA/antirad.h | 26 + src/xrGameLA/artefact_script.cpp | 38 + src/xrGameLA/associative_vector.h | 150 + .../associative_vector_compare_predicate.h | 37 + ...ociative_vector_compare_predicate_inline.h | 61 + src/xrGameLA/associative_vector_inline.h | 383 ++ src/xrGameLA/attachable_item.cpp | 137 + src/xrGameLA/attachable_item.h | 63 + src/xrGameLA/attachable_item_inline.h | 57 + src/xrGameLA/attachment_owner.cpp | 213 + src/xrGameLA/attachment_owner.h | 44 + src/xrGameLA/attachment_owner_inline.h | 18 + src/xrGameLA/autosave_manager.cpp | 85 + src/xrGameLA/autosave_manager.h | 40 + src/xrGameLA/autosave_manager_inline.h | 50 + src/xrGameLA/base_client_classes.h | 80 + src/xrGameLA/base_client_classes_script.cpp | 205 + src/xrGameLA/base_client_classes_wrappers.h | 324 ++ src/xrGameLA/battery.cpp | 83 + src/xrGameLA/battery.h | 26 + src/xrGameLA/battleye.h | 4 + src/xrGameLA/battleye_system.cpp | 363 ++ src/xrGameLA/battleye_system.h | 64 + src/xrGameLA/builder_allocator_constructor.h | 38 + .../builder_allocator_constructor_inline.h | 37 + src/xrGameLA/callback_info.h | 21 + src/xrGameLA/cameralook.cpp | 127 + src/xrGameLA/cameralook.h | 37 + src/xrGameLA/car_memory.cpp | 70 + src/xrGameLA/car_memory.h | 51 + src/xrGameLA/character_community.cpp | 82 + src/xrGameLA/character_community.h | 66 + src/xrGameLA/character_hit_animations.cpp | 190 + src/xrGameLA/character_hit_animations.h | 38 + .../character_hit_animations_params.h | 17 + src/xrGameLA/character_info.cpp | 191 + src/xrGameLA/character_info.h | 137 + src/xrGameLA/character_info_defs.h | 31 + src/xrGameLA/character_rank.cpp | 84 + src/xrGameLA/character_rank.h | 69 + src/xrGameLA/character_reputation.cpp | 74 + src/xrGameLA/character_reputation.h | 61 + src/xrGameLA/character_supplies.cpp | 165 + src/xrGameLA/character_supplies.h | 116 + src/xrGameLA/client_spawn_manager.cpp | 198 + src/xrGameLA/client_spawn_manager.h | 68 + src/xrGameLA/client_spawn_manager_inline.h | 20 + src/xrGameLA/client_spawn_manager_script.cpp | 24 + src/xrGameLA/clsid_game.h | 226 + src/xrGameLA/condition_state.h | 41 + src/xrGameLA/condition_state_inline.h | 207 + src/xrGameLA/console_commands.cpp | 1896 ++++++ src/xrGameLA/console_registrator.h | 10 + src/xrGameLA/console_registrator_script.cpp | 57 + src/xrGameLA/control_action.h | 29 + src/xrGameLA/control_action_inline.h | 51 + src/xrGameLA/controller_state_panic_inline.h | 25 + src/xrGameLA/cover_evaluators.cpp | 289 + src/xrGameLA/cover_evaluators.h | 188 + src/xrGameLA/cover_evaluators_inline.h | 211 + src/xrGameLA/cover_manager.cpp | 99 + src/xrGameLA/cover_manager.h | 57 + src/xrGameLA/cover_manager_inline.h | 124 + src/xrGameLA/cover_point.h | 30 + src/xrGameLA/cover_point_inline.h | 30 + src/xrGameLA/cover_point_script.cpp | 23 + src/xrGameLA/dRayMotions.cpp | 139 + src/xrGameLA/dRayMotions.h | 12 + src/xrGameLA/damage_manager.cpp | 138 + src/xrGameLA/damage_manager.h | 32 + src/xrGameLA/damage_manager_inline.h | 10 + src/xrGameLA/danger_cover_location.cpp | 16 + src/xrGameLA/danger_cover_location.h | 24 + src/xrGameLA/danger_cover_location_inline.h | 19 + src/xrGameLA/danger_explosive.cpp | 22 + src/xrGameLA/danger_explosive.h | 31 + src/xrGameLA/danger_explosive_inline.h | 23 + src/xrGameLA/danger_location.cpp | 15 + src/xrGameLA/danger_location.h | 34 + src/xrGameLA/danger_location_inline.h | 24 + src/xrGameLA/danger_manager.cpp | 336 ++ src/xrGameLA/danger_manager.h | 73 + src/xrGameLA/danger_manager_inline.h | 41 + src/xrGameLA/danger_object.cpp | 14 + src/xrGameLA/danger_object.h | 55 + src/xrGameLA/danger_object_inline.h | 75 + src/xrGameLA/danger_object_location.cpp | 26 + src/xrGameLA/danger_object_location.h | 26 + src/xrGameLA/danger_object_location_inline.h | 19 + src/xrGameLA/data_storage_binary_heap.h | 49 + .../data_storage_binary_heap_inline.h | 97 + src/xrGameLA/data_storage_bucket_list.h | 69 + .../data_storage_bucket_list_inline.h | 234 + src/xrGameLA/data_storage_constructor.h | 67 + .../data_storage_double_linked_list.h | 59 + .../data_storage_double_linked_list_inline.h | 147 + .../data_storage_single_linked_list.h | 59 + .../data_storage_single_linked_list_inline.h | 136 + src/xrGameLA/date_time.cpp | 132 + src/xrGameLA/date_time.h | 13 + src/xrGameLA/dbg_draw_frustum.cpp | 134 + src/xrGameLA/dcylinder/dCylinder.cpp | 1545 +++++ src/xrGameLA/dcylinder/dCylinder.h | 15 + src/xrGameLA/debug_renderer.cpp | 143 + src/xrGameLA/debug_renderer.h | 31 + src/xrGameLA/debug_renderer_inline.h | 32 + src/xrGameLA/detail_path_builder.h | 63 + src/xrGameLA/detail_path_manager.cpp | 176 + src/xrGameLA/detail_path_manager.h | 221 + src/xrGameLA/detail_path_manager_inline.h | 322 ++ src/xrGameLA/detail_path_manager_smooth.cpp | 890 +++ src/xrGameLA/detail_path_manager_space.h | 44 + src/xrGameLA/detectors_script.cpp | 24 + src/xrGameLA/dijkstra.h | 126 + src/xrGameLA/dijkstra_inline.h | 246 + src/xrGameLA/doug_lea_memory_allocator.c | 5064 +++++++++++++++++ src/xrGameLA/doug_lea_memory_allocator.h | 531 ++ src/xrGameLA/dynamic_patrol_path.cpp | 45 + src/xrGameLA/dynamic_patrol_path.h | 44 + src/xrGameLA/dynamic_patrol_path_inline.h | 23 + src/xrGameLA/dynamic_patrol_path_script.cpp | 25 + src/xrGameLA/dynamic_patrol_point.cpp | 46 + src/xrGameLA/dynamic_patrol_point.h | 32 + src/xrGameLA/dynamic_patrol_point_inline.h | 42 + src/xrGameLA/eatable_item.cpp | 356 ++ src/xrGameLA/eatable_item.h | 76 + src/xrGameLA/eatable_item_object.cpp | 211 + src/xrGameLA/eatable_item_object.h | 77 + src/xrGameLA/edge_path.h | 52 + src/xrGameLA/edge_path_inline.h | 66 + src/xrGameLA/ef_base.h | 76 + src/xrGameLA/ef_pattern.cpp | 145 + src/xrGameLA/ef_pattern.h | 60 + src/xrGameLA/ef_primary.cpp | 543 ++ src/xrGameLA/ef_primary.h | 484 ++ src/xrGameLA/ef_storage.cpp | 93 + src/xrGameLA/ef_storage.h | 213 + src/xrGameLA/ef_storage_inline.h | 27 + src/xrGameLA/ef_storage_script.cpp | 128 + src/xrGameLA/encyclopedia_article.cpp | 148 + src/xrGameLA/encyclopedia_article.h | 59 + src/xrGameLA/encyclopedia_article_defs.h | 51 + src/xrGameLA/enemy_manager.cpp | 415 ++ src/xrGameLA/enemy_manager.h | 81 + src/xrGameLA/enemy_manager_inline.h | 34 + src/xrGameLA/entity_alive.cpp | 957 ++++ src/xrGameLA/entity_alive.h | 202 + src/xrGameLA/entity_alive_inline.h | 7 + src/xrGameLA/fast_entity_update.cpp | 68 + src/xrGameLA/fast_entity_update.h | 54 + src/xrGameLA/file_transfer.cpp | 677 +++ src/xrGameLA/file_transfer.h | 117 + src/xrGameLA/filereceiver_node.cpp | 83 + src/xrGameLA/filereceiver_node.h | 43 + src/xrGameLA/filetransfer_common.h | 49 + src/xrGameLA/filetransfer_node.cpp | 382 ++ src/xrGameLA/filetransfer_node.h | 122 + src/xrGameLA/firedeps.h | 16 + src/xrGameLA/fs_registrator.h | 10 + src/xrGameLA/fs_registrator_script.cpp | 244 + src/xrGameLA/game_base.cpp | 284 + src/xrGameLA/game_base.h | 199 + src/xrGameLA/game_base_kill_type.h | 37 + src/xrGameLA/game_base_menu_events.h | 7 + src/xrGameLA/game_base_script.cpp | 75 + src/xrGameLA/game_base_space.h | 42 + src/xrGameLA/game_cl_base.cpp | 403 ++ src/xrGameLA/game_cl_base.h | 132 + src/xrGameLA/game_cl_base_script.cpp | 31 + .../game_cl_base_weapon_usage_statistic.cpp | 628 ++ .../game_cl_base_weapon_usage_statistic.h | 216 + ...me_cl_base_weapon_usage_statistic_save.cpp | 260 + src/xrGameLA/game_cl_mp_script.cpp | 129 + src/xrGameLA/game_cl_mp_script.h | 28 + src/xrGameLA/game_cl_single.cpp | 136 + src/xrGameLA/game_cl_single.h | 49 + src/xrGameLA/game_cl_teamdeathmatch.cpp | 758 +++ .../game_cl_teamdeathmatch_snd_messages.h | 22 + src/xrGameLA/game_graph.h | 94 + src/xrGameLA/game_graph_inline.h | 406 ++ src/xrGameLA/game_graph_script.cpp | 67 + src/xrGameLA/game_graph_space.h | 178 + src/xrGameLA/game_level_cross_table.h | 99 + src/xrGameLA/game_level_cross_table_inline.h | 104 + src/xrGameLA/game_location_selector.h | 63 + src/xrGameLA/game_location_selector_inline.h | 174 + src/xrGameLA/game_news.cpp | 69 + src/xrGameLA/game_news.h | 40 + src/xrGameLA/game_object_space.h | 69 + src/xrGameLA/game_path_manager.h | 51 + src/xrGameLA/game_path_manager_inline.h | 69 + src/xrGameLA/game_sv_base.cpp | 989 ++++ src/xrGameLA/game_sv_base.h | 189 + src/xrGameLA/game_sv_base_console_vars.cpp | 1 + src/xrGameLA/game_sv_base_console_vars.h | 1 + src/xrGameLA/game_sv_base_script.cpp | 202 + src/xrGameLA/game_sv_event_queue.cpp | 117 + src/xrGameLA/game_sv_event_queue.h | 26 + src/xrGameLA/game_sv_single.cpp | 370 ++ src/xrGameLA/game_sv_single.h | 70 + src/xrGameLA/graph_abstract.h | 84 + src/xrGameLA/graph_abstract_inline.h | 312 + src/xrGameLA/graph_edge.h | 34 + src/xrGameLA/graph_edge_inline.h | 60 + src/xrGameLA/graph_engine.h | 157 + src/xrGameLA/graph_engine_inline.h | 220 + src/xrGameLA/graph_engine_space.h | 150 + src/xrGameLA/graph_vertex.h | 53 + src/xrGameLA/graph_vertex_inline.h | 146 + src/xrGameLA/group_hierarchy_holder.cpp | 162 + src/xrGameLA/group_hierarchy_holder.h | 94 + src/xrGameLA/group_hierarchy_holder_inline.h | 74 + src/xrGameLA/helicopter.h | 359 ++ src/xrGameLA/helicopter_script.cpp | 117 + src/xrGameLA/hit_immunity.cpp | 52 + src/xrGameLA/hit_immunity.h | 25 + src/xrGameLA/hit_immunity_space.h | 6 + src/xrGameLA/hit_memory_manager.cpp | 410 ++ src/xrGameLA/hit_memory_manager.h | 88 + src/xrGameLA/hit_memory_manager_inline.h | 53 + src/xrGameLA/holder_custom.cpp | 17 + src/xrGameLA/holder_custom.h | 52 + src/xrGameLA/holder_custom_script.cpp | 17 + src/xrGameLA/hud_item_object.cpp | 124 + src/xrGameLA/hud_item_object.h | 51 + src/xrGameLA/id_generator.h | 117 + src/xrGameLA/ik/Dof7control.cpp | 801 +++ src/xrGameLA/ik/Dof7control.h | 237 + src/xrGameLA/ik/IKLimb.cpp | 1314 +++++ src/xrGameLA/ik/IKLimb.h | 109 + src/xrGameLA/ik/aint.cxx | 762 +++ src/xrGameLA/ik/aint.h | 337 ++ src/xrGameLA/ik/eqn.cxx | 356 ++ src/xrGameLA/ik/eqn.h | 147 + src/xrGameLA/ik/eulersolver.cxx | 421 ++ src/xrGameLA/ik/eulersolver.h | 117 + src/xrGameLA/ik/jtlimits.cxx | 812 +++ src/xrGameLA/ik/jtlimits.h | 373 ++ src/xrGameLA/ik/limb.cxx | 1051 ++++ src/xrGameLA/ik/limb.h | 194 + src/xrGameLA/ik/math3d.cpp | 991 ++++ src/xrGameLA/ik/math3d.h | 252 + src/xrGameLA/ik/mathTrig.cpp | 172 + src/xrGameLA/ik/mathTrig.h | 95 + src/xrGameLA/ik_anim_state.cpp | 117 + src/xrGameLA/ik_anim_state.h | 24 + src/xrGameLA/ik_calculate_data.cpp | 17 + src/xrGameLA/ik_calculate_data.h | 36 + src/xrGameLA/ik_calculate_state.h | 54 + src/xrGameLA/ik_collide_data.h | 47 + src/xrGameLA/ik_dbg_matrix.cpp | 64 + src/xrGameLA/ik_dbg_matrix.h | 53 + src/xrGameLA/ik_foot_collider.cpp | 416 ++ src/xrGameLA/ik_foot_collider.h | 75 + src/xrGameLA/ik_limb_state.cpp | 67 + src/xrGameLA/ik_limb_state.h | 90 + src/xrGameLA/ik_limb_state_predict.h | 9 + src/xrGameLA/ik_object_shift.cpp | 152 + src/xrGameLA/ik_object_shift.h | 26 + src/xrGameLA/ini_id_loader.h | 178 + src/xrGameLA/ini_table_loader.h | 137 + src/xrGameLA/interactive_motion.cpp | 160 + src/xrGameLA/interactive_motion.h | 56 + src/xrGameLA/inventory_item.cpp | 1164 ++++ src/xrGameLA/inventory_item.h | 333 ++ src/xrGameLA/inventory_item_impl.h | 18 + src/xrGameLA/inventory_item_inline.h | 59 + src/xrGameLA/inventory_item_object.cpp | 228 + src/xrGameLA/inventory_item_object.h | 93 + src/xrGameLA/inventory_item_object_inline.h | 10 + src/xrGameLA/inventory_item_object_script.cpp | 14 + src/xrGameLA/inventory_item_upgrade.cpp | 301 + src/xrGameLA/inventory_owner_info.cpp | 177 + src/xrGameLA/inventory_owner_inline.h | 8 + src/xrGameLA/inventory_space.h | 60 + src/xrGameLA/inventory_upgrade.cpp | 246 + src/xrGameLA/inventory_upgrade.h | 169 + src/xrGameLA/inventory_upgrade_base.cpp | 133 + src/xrGameLA/inventory_upgrade_base.h | 89 + src/xrGameLA/inventory_upgrade_base_inline.h | 35 + src/xrGameLA/inventory_upgrade_group.cpp | 150 + src/xrGameLA/inventory_upgrade_group.h | 57 + src/xrGameLA/inventory_upgrade_group_inline.h | 30 + src/xrGameLA/inventory_upgrade_inline.h | 68 + src/xrGameLA/inventory_upgrade_manager.cpp | 485 ++ src/xrGameLA/inventory_upgrade_manager.h | 107 + .../inventory_upgrade_manager_inline.h | 20 + src/xrGameLA/inventory_upgrade_property.cpp | 76 + src/xrGameLA/inventory_upgrade_property.h | 62 + .../inventory_upgrade_property_inline.h | 45 + src/xrGameLA/inventory_upgrade_root.cpp | 179 + src/xrGameLA/inventory_upgrade_root.h | 57 + src/xrGameLA/inventory_upgrade_root_inline.h | 25 + src/xrGameLA/item_manager.cpp | 144 + src/xrGameLA/item_manager.h | 36 + src/xrGameLA/item_manager_inline.h | 10 + src/xrGameLA/key_binding_registrator.h | 12 + .../key_binding_registrator_script.cpp | 212 + src/xrGameLA/level_changer.cpp | 193 + src/xrGameLA/level_changer.h | 41 + src/xrGameLA/level_debug.cpp | 282 + src/xrGameLA/level_debug.h | 230 + src/xrGameLA/level_graph.cpp | 228 + src/xrGameLA/level_graph.h | 260 + src/xrGameLA/level_graph_debug.cpp | 641 +++ src/xrGameLA/level_graph_debug2.cpp | 331 ++ src/xrGameLA/level_graph_inline.h | 668 +++ src/xrGameLA/level_graph_space.h | 52 + src/xrGameLA/level_graph_vertex.cpp | 516 ++ src/xrGameLA/level_graph_vertex_inline.h | 591 ++ src/xrGameLA/level_location_selector.h | 45 + src/xrGameLA/level_location_selector_inline.h | 44 + src/xrGameLA/level_map_locations.cpp | 1 + src/xrGameLA/level_path_builder.h | 62 + src/xrGameLA/level_path_manager.h | 55 + src/xrGameLA/level_path_manager_inline.h | 90 + src/xrGameLA/level_script.cpp | 978 ++++ src/xrGameLA/level_sounds.cpp | 292 + src/xrGameLA/level_sounds.h | 62 + src/xrGameLA/location_manager.cpp | 33 + src/xrGameLA/location_manager.h | 28 + src/xrGameLA/location_manager_inline.h | 21 + src/xrGameLA/magic_box3.cpp | 177 + src/xrGameLA/magic_box3.h | 31 + src/xrGameLA/magic_box3_inline.h | 75 + src/xrGameLA/magic_minimize_1d.cpp | 266 + src/xrGameLA/magic_minimize_1d.h | 33 + src/xrGameLA/magic_minimize_1d_inline.h | 16 + src/xrGameLA/magic_minimize_nd.h | 43 + src/xrGameLA/magic_minimize_nd_inline.h | 173 + .../manager_builder_allocator_constructor.h | 59 + ...ger_builder_allocator_constructor_inline.h | 59 + src/xrGameLA/map_location.cpp | 797 +++ src/xrGameLA/map_location.h | 161 + src/xrGameLA/map_location_defs.h | 25 + src/xrGameLA/map_manager.cpp | 296 + src/xrGameLA/map_manager.h | 30 + src/xrGameLA/map_spot.cpp | 176 + src/xrGameLA/map_spot.h | 45 + src/xrGameLA/material_manager.cpp | 134 + src/xrGameLA/material_manager.h | 41 + src/xrGameLA/material_manager_inline.h | 24 + src/xrGameLA/matrix_utils.h | 101 + src/xrGameLA/medkit.cpp | 59 + src/xrGameLA/medkit.h | 26 + src/xrGameLA/member_corpse.h | 30 + src/xrGameLA/member_corpse_inline.h | 41 + src/xrGameLA/member_enemy.h | 34 + src/xrGameLA/member_enemy_inline.h | 27 + src/xrGameLA/member_order.h | 92 + src/xrGameLA/member_order_inline.h | 97 + src/xrGameLA/memory_manager.cpp | 365 ++ src/xrGameLA/memory_manager.h | 88 + src/xrGameLA/memory_manager_inline.h | 81 + src/xrGameLA/memory_space.h | 210 + src/xrGameLA/memory_space_impl.h | 90 + src/xrGameLA/memory_space_script.cpp | 156 + src/xrGameLA/min_obb.cpp | 202 + src/xrGameLA/mincer_script.cpp | 17 + src/xrGameLA/monster_community.cpp | 84 + src/xrGameLA/monster_community.h | 61 + src/xrGameLA/mounted_turret.cpp | 689 +++ src/xrGameLA/mounted_turret.h | 155 + src/xrGameLA/mounted_turret_script.cpp | 61 + src/xrGameLA/movement_manager.cpp | 374 ++ src/xrGameLA/movement_manager.h | 259 + src/xrGameLA/movement_manager_game.cpp | 227 + src/xrGameLA/movement_manager_impl.h | 12 + src/xrGameLA/movement_manager_inline.h | 152 + src/xrGameLA/movement_manager_level.cpp | 118 + src/xrGameLA/movement_manager_patrol.cpp | 126 + src/xrGameLA/movement_manager_physic.cpp | 236 + src/xrGameLA/movement_manager_space.h | 20 + src/xrGameLA/mslotutils.h | 175 + src/xrGameLA/mt_config.h | 22 + src/xrGameLA/object_actions.cpp | 874 +++ src/xrGameLA/object_actions.h | 349 ++ src/xrGameLA/object_actions_inline.h | 65 + src/xrGameLA/object_broker.h | 19 + src/xrGameLA/object_cloner.h | 188 + src/xrGameLA/object_comparer.h | 209 + src/xrGameLA/object_destroyer.h | 153 + src/xrGameLA/object_factory.cpp | 30 + src/xrGameLA/object_factory.h | 102 + src/xrGameLA/object_factory_impl.h | 66 + src/xrGameLA/object_factory_inline.h | 132 + src/xrGameLA/object_factory_register.cpp | 414 ++ src/xrGameLA/object_factory_script.cpp | 103 + src/xrGameLA/object_factory_space.h | 33 + src/xrGameLA/object_handler.cpp | 304 + src/xrGameLA/object_handler.h | 84 + src/xrGameLA/object_handler_inline.h | 20 + src/xrGameLA/object_handler_planner.cpp | 312 + src/xrGameLA/object_handler_planner.h | 79 + src/xrGameLA/object_handler_planner_impl.h | 40 + src/xrGameLA/object_handler_planner_inline.h | 25 + .../object_handler_planner_missile.cpp | 99 + .../object_handler_planner_weapon.cpp | 372 ++ src/xrGameLA/object_handler_space.h | 102 + src/xrGameLA/object_interfaces.h | 46 + src/xrGameLA/object_item_abstract.h | 32 + src/xrGameLA/object_item_abstract_inline.h | 30 + src/xrGameLA/object_item_client_server.h | 46 + .../object_item_client_server_inline.h | 81 + src/xrGameLA/object_item_script.cpp | 96 + src/xrGameLA/object_item_script.h | 40 + src/xrGameLA/object_item_single.h | 47 + src/xrGameLA/object_item_single_inline.h | 61 + src/xrGameLA/object_loader.h | 290 + src/xrGameLA/object_manager.h | 36 + src/xrGameLA/object_manager_inline.h | 113 + src/xrGameLA/object_property_evaluators.cpp | 220 + src/xrGameLA/object_property_evaluators.h | 196 + .../object_property_evaluators_inline.h | 29 + src/xrGameLA/object_saver.h | 210 + src/xrGameLA/object_type_traits.h | 225 + src/xrGameLA/ode_include.h | 9 + src/xrGameLA/ode_redefine.h | 33 + src/xrGameLA/operator_abstract.h | 63 + src/xrGameLA/operator_abstract_inline.h | 417 ++ src/xrGameLA/operator_condition.h | 37 + src/xrGameLA/operator_condition_inline.h | 72 + src/xrGameLA/particle_params.cpp | 14 + src/xrGameLA/particle_params.h | 30 + src/xrGameLA/particle_params_inline.h | 20 + src/xrGameLA/particle_params_script.cpp | 25 + src/xrGameLA/path_manager.h | 57 + src/xrGameLA/path_manager_game.h | 59 + src/xrGameLA/path_manager_game_inline.h | 77 + src/xrGameLA/path_manager_game_level.h | 75 + src/xrGameLA/path_manager_game_level_inline.h | 85 + src/xrGameLA/path_manager_game_vertex.h | 72 + .../path_manager_game_vertex_inline.h | 80 + src/xrGameLA/path_manager_generic.h | 55 + src/xrGameLA/path_manager_generic_inline.h | 160 + src/xrGameLA/path_manager_level.h | 87 + src/xrGameLA/path_manager_level_flooder.h | 79 + .../path_manager_level_flooder_inline.h | 113 + src/xrGameLA/path_manager_level_inline.h | 141 + .../path_manager_level_nearest_vertex.h | 81 + ...path_manager_level_nearest_vertex_inline.h | 124 + .../path_manager_level_straight_line.h | 72 + .../path_manager_level_straight_line_inline.h | 111 + src/xrGameLA/path_manager_params.h | 40 + src/xrGameLA/path_manager_params_flooder.h | 39 + src/xrGameLA/path_manager_params_game_level.h | 51 + .../path_manager_params_game_vertex.h | 54 + .../path_manager_params_nearest_vertex.h | 43 + .../path_manager_params_straight_line.h | 47 + src/xrGameLA/path_manager_solver.h | 66 + src/xrGameLA/path_manager_solver_inline.h | 115 + src/xrGameLA/patrol_path.cpp | 54 + src/xrGameLA/patrol_path.h | 50 + src/xrGameLA/patrol_path_inline.h | 52 + src/xrGameLA/patrol_path_manager.cpp | 380 ++ src/xrGameLA/patrol_path_manager.h | 86 + src/xrGameLA/patrol_path_manager_inline.h | 123 + src/xrGameLA/patrol_path_manager_space.h | 26 + src/xrGameLA/patrol_path_params.cpp | 97 + src/xrGameLA/patrol_path_params.h | 46 + src/xrGameLA/patrol_path_params_inline.h | 10 + src/xrGameLA/patrol_path_params_script.cpp | 57 + src/xrGameLA/patrol_path_storage.cpp | 134 + src/xrGameLA/patrol_path_storage.h | 45 + src/xrGameLA/patrol_path_storage_inline.h | 28 + src/xrGameLA/patrol_point.cpp | 134 + src/xrGameLA/patrol_point.h | 67 + src/xrGameLA/patrol_point_inline.h | 59 + src/xrGameLA/pch_script.cpp | 2 + src/xrGameLA/pch_script.h | 40 + src/xrGameLA/pda_space.h | 9 + src/xrGameLA/ph_shell_interface.h | 7 + src/xrGameLA/phvalide.h | 58 + src/xrGameLA/physic_item.cpp | 210 + src/xrGameLA/physic_item.h | 41 + src/xrGameLA/physic_item_inline.h | 10 + src/xrGameLA/physics_game.cpp | 154 + src/xrGameLA/player_hud.cpp | 851 +++ src/xrGameLA/player_hud.h | 154 + src/xrGameLA/player_hud_tune.cpp | 386 ++ src/xrGameLA/pose_extrapolation.cpp | 99 + src/xrGameLA/pose_extrapolation.h | 57 + src/xrGameLA/pp_effector_custom.cpp | 85 + src/xrGameLA/pp_effector_custom.h | 100 + src/xrGameLA/pp_effector_distance.cpp | 40 + src/xrGameLA/pp_effector_distance.h | 24 + src/xrGameLA/problem_solver.h | 150 + src/xrGameLA/problem_solver_inline.h | 440 ++ src/xrGameLA/profiler.cpp | 272 + src/xrGameLA/profiler.h | 93 + src/xrGameLA/profiler_inline.h | 51 + src/xrGameLA/property_evaluator.h | 49 + src/xrGameLA/property_evaluator_const.h | 27 + .../property_evaluator_const_inline.h | 33 + src/xrGameLA/property_evaluator_inline.h | 61 + src/xrGameLA/property_evaluator_member.h | 30 + .../property_evaluator_member_inline.h | 39 + src/xrGameLA/property_evaluator_script.cpp | 33 + src/xrGameLA/property_storage.h | 34 + src/xrGameLA/property_storage_inline.h | 30 + src/xrGameLA/property_storage_script.cpp | 24 + src/xrGameLA/purchase_list.cpp | 50 + src/xrGameLA/purchase_list.h | 38 + src/xrGameLA/purchase_list_inline.h | 57 + src/xrGameLA/quadtree.h | 116 + src/xrGameLA/quadtree_inline.h | 299 + src/xrGameLA/random32.cpp | 12 + src/xrGameLA/random32.h | 33 + src/xrGameLA/rat_state_base.cpp | 16 + src/xrGameLA/rat_state_base.h | 31 + src/xrGameLA/rat_state_base_inline.h | 23 + src/xrGameLA/rat_state_manager.cpp | 79 + src/xrGameLA/rat_state_manager.h | 46 + src/xrGameLA/rat_state_manager_inline.h | 18 + src/xrGameLA/rat_states.cpp | 518 ++ src/xrGameLA/rat_states.h | 87 + src/xrGameLA/relation_registry.cpp | 217 + src/xrGameLA/relation_registry.h | 123 + src/xrGameLA/relation_registry_actions.cpp | 292 + src/xrGameLA/relation_registry_defs.h | 39 + src/xrGameLA/relation_registry_fights.cpp | 88 + src/xrGameLA/relation_registry_inline.h | 91 + src/xrGameLA/restricted_object.cpp | 417 ++ src/xrGameLA/restricted_object.h | 78 + src/xrGameLA/restricted_object_inline.h | 39 + src/xrGameLA/restriction_space.h | 35 + src/xrGameLA/safe_map_iterator.h | 53 + src/xrGameLA/safe_map_iterator_inline.h | 155 + src/xrGameLA/saved_game_wrapper.cpp | 105 + src/xrGameLA/saved_game_wrapper.h | 40 + src/xrGameLA/saved_game_wrapper_inline.h | 24 + src/xrGameLA/saved_game_wrapper_script.cpp | 41 + src/xrGameLA/script_abstract_action.cpp | 19 + src/xrGameLA/script_abstract_action.h | 21 + src/xrGameLA/script_abstract_action_inline.h | 14 + src/xrGameLA/script_action_condition.cpp | 14 + src/xrGameLA/script_action_condition.h | 44 + src/xrGameLA/script_action_condition_inline.h | 28 + .../script_action_condition_script.cpp | 34 + .../script_action_planner_action_wrapper.cpp | 61 + .../script_action_planner_action_wrapper.h | 31 + ...ipt_action_planner_action_wrapper_inline.h | 20 + .../script_action_planner_wrapper.cpp | 40 + src/xrGameLA/script_action_planner_wrapper.h | 21 + .../script_action_planner_wrapper_inline.h | 9 + src/xrGameLA/script_action_wrapper.cpp | 68 + src/xrGameLA/script_action_wrapper.h | 28 + src/xrGameLA/script_action_wrapper_inline.h | 15 + src/xrGameLA/script_animation_action.cpp | 14 + src/xrGameLA/script_animation_action.h | 50 + src/xrGameLA/script_animation_action_inline.h | 62 + .../script_animation_action_script.cpp | 51 + src/xrGameLA/script_bind_macroses.h | 98 + src/xrGameLA/script_binder.cpp | 240 + src/xrGameLA/script_binder.h | 38 + src/xrGameLA/script_binder_inline.h | 14 + src/xrGameLA/script_binder_object.cpp | 70 + src/xrGameLA/script_binder_object.h | 41 + src/xrGameLA/script_binder_object_script.cpp | 38 + src/xrGameLA/script_binder_object_wrapper.cpp | 132 + src/xrGameLA/script_binder_object_wrapper.h | 42 + src/xrGameLA/script_callStack.cpp | 72 + src/xrGameLA/script_callStack.h | 25 + src/xrGameLA/script_callback_ex.h | 113 + src/xrGameLA/script_callback_ex_generators.h | 48 + src/xrGameLA/script_callback_ex_inline.h | 78 + src/xrGameLA/script_callback_ex_nonvoid.h | 18 + src/xrGameLA/script_callback_ex_void.h | 18 + src/xrGameLA/script_debugger.cpp | 520 ++ src/xrGameLA/script_debugger.h | 106 + src/xrGameLA/script_debugger_messages.h | 84 + src/xrGameLA/script_debugger_threads.cpp | 70 + src/xrGameLA/script_debugger_threads.h | 19 + src/xrGameLA/script_effector.cpp | 37 + src/xrGameLA/script_effector.h | 32 + src/xrGameLA/script_effector_inline.h | 15 + src/xrGameLA/script_effector_script.cpp | 75 + src/xrGameLA/script_effector_wrapper.cpp | 24 + src/xrGameLA/script_effector_wrapper.h | 21 + src/xrGameLA/script_effector_wrapper_inline.h | 14 + src/xrGameLA/script_engine.cpp | 370 ++ src/xrGameLA/script_engine.h | 102 + src/xrGameLA/script_engine_export.cpp | 105 + src/xrGameLA/script_engine_export.h | 170 + src/xrGameLA/script_engine_help.cpp | 303 + src/xrGameLA/script_engine_inline.h | 66 + src/xrGameLA/script_engine_script.cpp | 239 + src/xrGameLA/script_engine_space.h | 17 + src/xrGameLA/script_entity.cpp | 681 +++ src/xrGameLA/script_entity.h | 124 + src/xrGameLA/script_entity_action.cpp | 14 + src/xrGameLA/script_entity_action.h | 76 + src/xrGameLA/script_entity_action_inline.h | 202 + src/xrGameLA/script_entity_action_script.cpp | 40 + src/xrGameLA/script_entity_inline.h | 15 + src/xrGameLA/script_entity_space.h | 22 + src/xrGameLA/script_export_macroses.h | 324 ++ src/xrGameLA/script_export_space.h | 32 + src/xrGameLA/script_fcolor.h | 16 + src/xrGameLA/script_fcolor_script.cpp | 30 + src/xrGameLA/script_flags.h | 16 + src/xrGameLA/script_flags_script.cpp | 124 + src/xrGameLA/script_fmatrix.h | 16 + src/xrGameLA/script_fmatrix_script.cpp | 109 + src/xrGameLA/script_fvector.h | 16 + src/xrGameLA/script_fvector_script.cpp | 117 + src/xrGameLA/script_game_object.cpp | 826 +++ src/xrGameLA/script_game_object.h | 680 +++ src/xrGameLA/script_game_object2.cpp | 692 +++ src/xrGameLA/script_game_object3.cpp | 1067 ++++ src/xrGameLA/script_game_object4.cpp | 226 + src/xrGameLA/script_game_object_impl.h | 26 + .../script_game_object_inventory_owner.cpp | 1128 ++++ src/xrGameLA/script_game_object_script.cpp | 114 + src/xrGameLA/script_game_object_script2.cpp | 320 ++ src/xrGameLA/script_game_object_script3.cpp | 296 + .../script_game_object_script_trader.cpp | 23 + src/xrGameLA/script_game_object_trader.cpp | 60 + src/xrGameLA/script_game_object_use.cpp | 299 + src/xrGameLA/script_game_object_use2.cpp | 160 + src/xrGameLA/script_hit.cpp | 14 + src/xrGameLA/script_hit.h | 36 + src/xrGameLA/script_hit_inline.h | 29 + src/xrGameLA/script_hit_script.cpp | 43 + src/xrGameLA/script_ini_file.cpp | 110 + src/xrGameLA/script_ini_file.h | 42 + src/xrGameLA/script_ini_file_inline.h | 10 + src/xrGameLA/script_ini_file_script.cpp | 90 + src/xrGameLA/script_lanim.cpp | 50 + src/xrGameLA/script_lanim.h | 10 + src/xrGameLA/script_lua_helper.cpp | 551 ++ src/xrGameLA/script_lua_helper.h | 52 + src/xrGameLA/script_monster_action.cpp | 20 + src/xrGameLA/script_monster_action.h | 35 + src/xrGameLA/script_monster_action_inline.h | 28 + src/xrGameLA/script_monster_action_script.cpp | 33 + src/xrGameLA/script_monster_hit_info.h | 30 + .../script_monster_hit_info_script.cpp | 35 + src/xrGameLA/script_movement_action.cpp | 122 + src/xrGameLA/script_movement_action.h | 118 + src/xrGameLA/script_movement_action_inline.h | 121 + .../script_movement_action_script.cpp | 109 + src/xrGameLA/script_net_packet.h | 17 + src/xrGameLA/script_net_packet_script.cpp | 138 + src/xrGameLA/script_object.cpp | 62 + src/xrGameLA/script_object.h | 29 + src/xrGameLA/script_object_action.cpp | 21 + src/xrGameLA/script_object_action.h | 42 + src/xrGameLA/script_object_action_inline.h | 56 + src/xrGameLA/script_object_action_script.cpp | 55 + src/xrGameLA/script_particle_action.cpp | 25 + src/xrGameLA/script_particle_action.h | 54 + src/xrGameLA/script_particle_action_inline.h | 73 + .../script_particle_action_script.cpp | 33 + src/xrGameLA/script_particles.cpp | 172 + src/xrGameLA/script_particles.h | 63 + src/xrGameLA/script_particles_inline.h | 10 + src/xrGameLA/script_particles_script.cpp | 39 + src/xrGameLA/script_process.cpp | 104 + src/xrGameLA/script_process.h | 66 + src/xrGameLA/script_process_inline.h | 19 + .../script_property_evaluator_wrapper.cpp | 48 + .../script_property_evaluator_wrapper.h | 26 + ...script_property_evaluator_wrapper_inline.h | 14 + src/xrGameLA/script_reader.h | 16 + src/xrGameLA/script_reader_script.cpp | 75 + src/xrGameLA/script_render_device.h | 16 + src/xrGameLA/script_render_device_script.cpp | 71 + src/xrGameLA/script_rtoken_list.h | 32 + src/xrGameLA/script_rtoken_list_inline.h | 43 + src/xrGameLA/script_rtoken_list_script.cpp | 27 + src/xrGameLA/script_sound.cpp | 75 + src/xrGameLA/script_sound.h | 55 + src/xrGameLA/script_sound_action.cpp | 34 + src/xrGameLA/script_sound_action.h | 69 + src/xrGameLA/script_sound_action_inline.h | 143 + src/xrGameLA/script_sound_action_script.cpp | 61 + src/xrGameLA/script_sound_info.h | 36 + src/xrGameLA/script_sound_info_script.cpp | 19 + src/xrGameLA/script_sound_inline.h | 119 + src/xrGameLA/script_sound_script.cpp | 55 + src/xrGameLA/script_sound_type.h | 18 + src/xrGameLA/script_sound_type_script.cpp | 71 + src/xrGameLA/script_space_forward.h | 16 + src/xrGameLA/script_stack_tracker.cpp | 83 + src/xrGameLA/script_stack_tracker.h | 31 + src/xrGameLA/script_stack_tracker_inline.h | 10 + src/xrGameLA/script_storage.cpp | 614 ++ src/xrGameLA/script_storage.h | 79 + src/xrGameLA/script_storage_inline.h | 30 + src/xrGameLA/script_storage_space.h | 22 + src/xrGameLA/script_thread.cpp | 166 + src/xrGameLA/script_thread.h | 44 + src/xrGameLA/script_thread_inline.h | 29 + src/xrGameLA/script_token_list.cpp | 18 + src/xrGameLA/script_token_list.h | 71 + src/xrGameLA/script_token_list_inline.h | 76 + src/xrGameLA/script_token_list_script.cpp | 32 + src/xrGameLA/script_ui_registrator.h | 11 + src/xrGameLA/script_value_container.h | 26 + src/xrGameLA/script_value_container_impl.h | 53 + src/xrGameLA/script_watch_action.cpp | 32 + src/xrGameLA/script_watch_action.h | 64 + src/xrGameLA/script_watch_action_inline.h | 69 + src/xrGameLA/script_watch_action_script.cpp | 48 + src/xrGameLA/script_world_property.h | 24 + src/xrGameLA/script_world_property_inline.h | 14 + src/xrGameLA/script_world_property_script.cpp | 27 + src/xrGameLA/script_world_state.h | 22 + src/xrGameLA/script_world_state_script.cpp | 31 + src/xrGameLA/script_zone.cpp | 138 + src/xrGameLA/script_zone.h | 42 + src/xrGameLA/script_zone_script.cpp | 32 + src/xrGameLA/searchlight.cpp | 249 + src/xrGameLA/searchlight.h | 61 + src/xrGameLA/secure_messaging.cpp | 118 + src/xrGameLA/secure_messaging.h | 42 + src/xrGameLA/seniority_hierarchy_holder.cpp | 25 + src/xrGameLA/seniority_hierarchy_holder.h | 32 + .../seniority_hierarchy_holder_inline.h | 19 + src/xrGameLA/seniority_hierarchy_space.h | 30 + src/xrGameLA/server_entity_wrapper.cpp | 111 + src/xrGameLA/server_entity_wrapper.h | 29 + src/xrGameLA/server_entity_wrapper_inline.h | 20 + src/xrGameLA/setup_manager.h | 61 + src/xrGameLA/setup_manager_inline.h | 155 + src/xrGameLA/shared_data.h | 153 + src/xrGameLA/sight_action.cpp | 276 + src/xrGameLA/sight_action.h | 87 + src/xrGameLA/sight_action_inline.h | 133 + src/xrGameLA/sight_control_action.h | 29 + src/xrGameLA/sight_control_action_inline.h | 47 + src/xrGameLA/sight_manager.cpp | 385 ++ src/xrGameLA/sight_manager.h | 62 + src/xrGameLA/sight_manager_inline.h | 51 + src/xrGameLA/sight_manager_space.h | 27 + src/xrGameLA/smart_cast.cpp | 28 + src/xrGameLA/smart_cast.h | 338 ++ src/xrGameLA/smart_cast_impl0.h | 67 + src/xrGameLA/smart_cast_impl1.h | 514 ++ src/xrGameLA/smart_cast_impl2.h | 29 + src/xrGameLA/smart_cast_stats.cpp | 180 + src/xrGameLA/smart_zone.h | 21 + src/xrGameLA/sound_collection_storage.cpp | 49 + src/xrGameLA/sound_collection_storage.h | 32 + .../sound_collection_storage_inline.h | 18 + src/xrGameLA/sound_memory_manager.cpp | 518 ++ src/xrGameLA/sound_memory_manager.h | 114 + src/xrGameLA/sound_memory_manager_inline.h | 59 + src/xrGameLA/sound_player.cpp | 320 ++ src/xrGameLA/sound_player.h | 162 + src/xrGameLA/sound_player_inline.h | 86 + src/xrGameLA/sound_user_data_visitor.h | 18 + src/xrGameLA/space_restriction.cpp | 366 ++ src/xrGameLA/space_restriction.h | 93 + src/xrGameLA/space_restriction_abstract.h | 40 + .../space_restriction_abstract_inline.h | 81 + src/xrGameLA/space_restriction_base.cpp | 74 + src/xrGameLA/space_restriction_base.h | 40 + src/xrGameLA/space_restriction_base_inline.h | 16 + src/xrGameLA/space_restriction_bridge.cpp | 148 + src/xrGameLA/space_restriction_bridge.h | 46 + .../space_restriction_bridge_inline.h | 113 + .../space_restriction_composition.cpp | 234 + src/xrGameLA/space_restriction_composition.h | 55 + .../space_restriction_composition_inline.h | 35 + src/xrGameLA/space_restriction_holder.cpp | 209 + src/xrGameLA/space_restriction_holder.h | 56 + .../space_restriction_holder_inline.h | 25 + src/xrGameLA/space_restriction_inline.h | 90 + src/xrGameLA/space_restriction_manager.cpp | 303 + src/xrGameLA/space_restriction_manager.h | 81 + .../space_restriction_manager_inline.h | 24 + src/xrGameLA/space_restriction_shape.cpp | 233 + src/xrGameLA/space_restriction_shape.h | 46 + src/xrGameLA/space_restriction_shape_inline.h | 60 + src/xrGameLA/space_restrictor.cpp | 299 + src/xrGameLA/space_restrictor.h | 75 + src/xrGameLA/space_restrictor_inline.h | 29 + src/xrGameLA/space_restrictor_script.cpp | 14 + src/xrGameLA/specific_character.cpp | 313 + src/xrGameLA/specific_character.h | 168 + src/xrGameLA/squad_hierarchy_holder.cpp | 41 + src/xrGameLA/squad_hierarchy_holder.h | 52 + src/xrGameLA/squad_hierarchy_holder_inline.h | 44 + src/xrGameLA/stalker_alife_actions.cpp | 167 + src/xrGameLA/stalker_alife_actions.h | 44 + src/xrGameLA/stalker_alife_planner.cpp | 67 + src/xrGameLA/stalker_alife_planner.h | 25 + src/xrGameLA/stalker_alife_task_actions.cpp | 260 + src/xrGameLA/stalker_alife_task_actions.h | 46 + src/xrGameLA/stalker_animation_callbacks.cpp | 196 + src/xrGameLA/stalker_animation_data.cpp | 17 + src/xrGameLA/stalker_animation_data.h | 28 + .../stalker_animation_data_storage.cpp | 62 + src/xrGameLA/stalker_animation_data_storage.h | 32 + .../stalker_animation_data_storage_inline.h | 18 + src/xrGameLA/stalker_animation_global.cpp | 85 + src/xrGameLA/stalker_animation_head.cpp | 37 + src/xrGameLA/stalker_animation_legs.cpp | 287 + src/xrGameLA/stalker_animation_manager.cpp | 109 + src/xrGameLA/stalker_animation_manager.h | 175 + .../stalker_animation_manager_debug.cpp | 252 + src/xrGameLA/stalker_animation_manager_impl.h | 55 + .../stalker_animation_manager_inline.h | 68 + .../stalker_animation_manager_update.cpp | 226 + src/xrGameLA/stalker_animation_names.cpp | 116 + src/xrGameLA/stalker_animation_names.h | 30 + src/xrGameLA/stalker_animation_pair.cpp | 244 + src/xrGameLA/stalker_animation_pair.h | 98 + src/xrGameLA/stalker_animation_pair_inline.h | 117 + src/xrGameLA/stalker_animation_script.cpp | 66 + src/xrGameLA/stalker_animation_script.h | 24 + .../stalker_animation_script_inline.h | 35 + src/xrGameLA/stalker_animation_state.cpp | 36 + src/xrGameLA/stalker_animation_state.h | 36 + src/xrGameLA/stalker_animation_state_inline.h | 10 + src/xrGameLA/stalker_animation_torso.cpp | 296 + src/xrGameLA/stalker_anomaly_actions.cpp | 164 + src/xrGameLA/stalker_anomaly_actions.h | 46 + src/xrGameLA/stalker_anomaly_planner.cpp | 66 + src/xrGameLA/stalker_anomaly_planner.h | 26 + src/xrGameLA/stalker_base_action.cpp | 40 + src/xrGameLA/stalker_base_action.h | 35 + src/xrGameLA/stalker_combat_action_base.cpp | 323 ++ src/xrGameLA/stalker_combat_action_base.h | 30 + src/xrGameLA/stalker_combat_actions.cpp | 1287 +++++ src/xrGameLA/stalker_combat_actions.h | 282 + src/xrGameLA/stalker_combat_actions_inline.h | 15 + src/xrGameLA/stalker_combat_planner.cpp | 504 ++ src/xrGameLA/stalker_combat_planner.h | 46 + .../stalker_danger_by_sound_actions.cpp | 182 + .../stalker_danger_by_sound_actions.h | 86 + .../stalker_danger_by_sound_planner.cpp | 59 + .../stalker_danger_by_sound_planner.h | 29 + .../stalker_danger_grenade_actions.cpp | 280 + src/xrGameLA/stalker_danger_grenade_actions.h | 89 + .../stalker_danger_grenade_planner.cpp | 96 + src/xrGameLA/stalker_danger_grenade_planner.h | 29 + .../stalker_danger_in_direction_actions.cpp | 378 ++ .../stalker_danger_in_direction_actions.h | 92 + .../stalker_danger_in_direction_planner.cpp | 96 + .../stalker_danger_in_direction_planner.h | 29 + src/xrGameLA/stalker_danger_planner.cpp | 107 + src/xrGameLA/stalker_danger_planner.h | 31 + src/xrGameLA/stalker_danger_planner_inline.h | 10 + .../stalker_danger_property_evaluators.cpp | 258 + .../stalker_danger_property_evaluators.h | 140 + .../stalker_danger_unknown_actions.cpp | 172 + src/xrGameLA/stalker_danger_unknown_actions.h | 59 + .../stalker_danger_unknown_planner.cpp | 84 + src/xrGameLA/stalker_danger_unknown_planner.h | 29 + src/xrGameLA/stalker_death_actions.cpp | 125 + src/xrGameLA/stalker_death_actions.h | 29 + src/xrGameLA/stalker_death_planner.cpp | 58 + src/xrGameLA/stalker_death_planner.h | 25 + src/xrGameLA/stalker_decision_space.h | 167 + src/xrGameLA/stalker_kill_wounded_actions.cpp | 365 ++ src/xrGameLA/stalker_kill_wounded_actions.h | 88 + src/xrGameLA/stalker_kill_wounded_planner.cpp | 129 + src/xrGameLA/stalker_kill_wounded_planner.h | 31 + src/xrGameLA/stalker_movement_manager.cpp | 756 +++ src/xrGameLA/stalker_movement_manager.h | 118 + .../stalker_movement_manager_inline.h | 145 + src/xrGameLA/stalker_movement_manager_space.h | 71 + src/xrGameLA/stalker_movement_params.h | 45 + src/xrGameLA/stalker_movement_params_inline.h | 46 + src/xrGameLA/stalker_movement_restriction.h | 31 + .../stalker_movement_restriction_inline.h | 33 + src/xrGameLA/stalker_planner.cpp | 202 + src/xrGameLA/stalker_planner.h | 52 + src/xrGameLA/stalker_planner_inline.h | 19 + src/xrGameLA/stalker_property_evaluators.cpp | 467 ++ src/xrGameLA/stalker_property_evaluators.h | 297 + .../stalker_property_evaluators_inline.h | 9 + src/xrGameLA/stalker_search_actions.cpp | 252 + src/xrGameLA/stalker_search_actions.h | 73 + src/xrGameLA/stalker_search_planner.cpp | 82 + src/xrGameLA/stalker_search_planner.h | 32 + src/xrGameLA/stalker_sound_data.cpp | 29 + src/xrGameLA/stalker_sound_data.h | 25 + src/xrGameLA/stalker_sound_data_inline.h | 21 + src/xrGameLA/stalker_sound_data_visitor.cpp | 59 + src/xrGameLA/stalker_sound_data_visitor.h | 27 + .../stalker_sound_data_visitor_inline.h | 21 + src/xrGameLA/stalker_velocity_collection.cpp | 40 + src/xrGameLA/stalker_velocity_collection.h | 29 + .../stalker_velocity_collection_inline.h | 68 + src/xrGameLA/stalker_velocity_holder.cpp | 30 + src/xrGameLA/stalker_velocity_holder.h | 32 + src/xrGameLA/stalker_velocity_holder_inline.h | 18 + src/xrGameLA/step_manager.cpp | 247 + src/xrGameLA/step_manager.h | 44 + src/xrGameLA/step_manager_defs.h | 42 + src/xrGameLA/string_table.cpp | 180 + src/xrGameLA/string_table.h | 43 + src/xrGameLA/string_table_defs.h | 7 + src/xrGameLA/team_base_zone.cpp | 182 + src/xrGameLA/team_base_zone.h | 38 + src/xrGameLA/team_hierarchy_holder.cpp | 26 + src/xrGameLA/team_hierarchy_holder.h | 35 + src/xrGameLA/team_hierarchy_holder_inline.h | 27 + src/xrGameLA/torch_script.cpp | 14 + src/xrGameLA/trade.cpp | 183 + src/xrGameLA/trade.h | 68 + src/xrGameLA/trade2.cpp | 271 + src/xrGameLA/trade_action_parameters.h | 35 + src/xrGameLA/trade_action_parameters_inline.h | 67 + src/xrGameLA/trade_bool_parameters.h | 28 + src/xrGameLA/trade_bool_parameters_inline.h | 31 + src/xrGameLA/trade_factor_parameters.h | 29 + src/xrGameLA/trade_factor_parameters_inline.h | 38 + src/xrGameLA/trade_factors.h | 22 + src/xrGameLA/trade_factors_inline.h | 30 + src/xrGameLA/trade_parameters.cpp | 13 + src/xrGameLA/trade_parameters.h | 80 + src/xrGameLA/trade_parameters_inline.h | 183 + src/xrGameLA/traffic_optimization.cpp | 59 + src/xrGameLA/traffic_optimization.h | 42 + src/xrGameLA/trajectories.cpp | 230 + src/xrGameLA/trajectories.h | 29 + .../TriPrimitiveCollideClassDef.h | 51 + src/xrGameLA/tri-colliderknoopc/__aabb_tri.h | 329 ++ .../tri-colliderknoopc/dSortTriPrimitive.cpp | 2 + .../tri-colliderknoopc/dSortTriPrimitive.h | 348 ++ src/xrGameLA/tri-colliderknoopc/dTriBox.cpp | 709 +++ src/xrGameLA/tri-colliderknoopc/dTriBox.h | 385 ++ .../tri-colliderknoopc/dTriCallideK.cpp | 15 + .../tri-colliderknoopc/dTriCollideK.h | 9 + .../tri-colliderknoopc/dTriColliderCommon.h | 20 + .../tri-colliderknoopc/dTriColliderMath.h | 113 + .../tri-colliderknoopc/dTriCylinder.cpp | 807 +++ .../tri-colliderknoopc/dTriCylinder.h | 27 + src/xrGameLA/tri-colliderknoopc/dTriList.cpp | 221 + src/xrGameLA/tri-colliderknoopc/dTriList.h | 55 + .../tri-colliderknoopc/dTriSphere.cpp | 254 + src/xrGameLA/tri-colliderknoopc/dTriSphere.h | 9 + .../tri-colliderknoopc/dcTriListCollider.cpp | 132 + .../tri-colliderknoopc/dcTriListCollider.h | 162 + src/xrGameLA/tri-colliderknoopc/dcTriangle.h | 22 + src/xrGameLA/tri-colliderknoopc/dxTriList.h | 475 ++ src/xrGameLA/ui/ArtefactDetectorUI.cpp | 1 + src/xrGameLA/ui/ArtefactDetectorUI.h | 101 + src/xrGameLA/ui/CExtraContentFilter.cpp | 68 + src/xrGameLA/ui/CExtraContentFilter.h | 21 + src/xrGameLA/ui/KillMessageStruct.h | 27 + src/xrGameLA/ui/MMSound.cpp | 94 + src/xrGameLA/ui/MMSound.h | 30 + src/xrGameLA/ui/Restrictions.cpp | 268 + src/xrGameLA/ui/Restrictions.h | 54 + src/xrGameLA/ui/UI3tButton.cpp | 242 + src/xrGameLA/ui/UI3tButton.h | 63 + src/xrGameLA/ui/UIActorInfo.cpp | 369 ++ src/xrGameLA/ui/UIActorInfo.h | 70 + src/xrGameLA/ui/UIActorProtectionInfo.cpp | 182 + src/xrGameLA/ui/UIActorProtectionInfo.h | 49 + src/xrGameLA/ui/UIAnimatedStatic.cpp | 97 + src/xrGameLA/ui/UIAnimatedStatic.h | 56 + src/xrGameLA/ui/UIArtefactPanel.cpp | 76 + src/xrGameLA/ui/UIArtefactPanel.h | 25 + src/xrGameLA/ui/UIArtifactParams.cpp | 202 + src/xrGameLA/ui/UIArtifactParams.h | 47 + src/xrGameLA/ui/UIBtnHint.cpp | 58 + src/xrGameLA/ui/UIBtnHint.h | 25 + src/xrGameLA/ui/UIButton.cpp | 277 + src/xrGameLA/ui/UIButton.h | 70 + src/xrGameLA/ui/UIButton_script.cpp | 76 + src/xrGameLA/ui/UIBuyWeaponTab.cpp | 75 + src/xrGameLA/ui/UIBuyWeaponTab.h | 28 + src/xrGameLA/ui/UIBuyWndBase.h | 76 + src/xrGameLA/ui/UIBuyWndShared.cpp | 82 + src/xrGameLA/ui/UIBuyWndShared.h | 43 + src/xrGameLA/ui/UICarBodyWnd.cpp | 693 +++ src/xrGameLA/ui/UICarBodyWnd.h | 110 + src/xrGameLA/ui/UICarPanel.cpp | 94 + src/xrGameLA/ui/UICarPanel.h | 26 + src/xrGameLA/ui/UICellCustomItems.cpp | 448 ++ src/xrGameLA/ui/UICellCustomItems.h | 83 + src/xrGameLA/ui/UICellItem.cpp | 300 + src/xrGameLA/ui/UICellItem.h | 109 + src/xrGameLA/ui/UICellItemFactory.cpp | 27 + src/xrGameLA/ui/UICellItemFactory.h | 6 + src/xrGameLA/ui/UICharacterInfo.cpp | 322 ++ src/xrGameLA/ui/UICharacterInfo.h | 67 + src/xrGameLA/ui/UIChatWnd.cpp | 103 + src/xrGameLA/ui/UIChatWnd.h | 41 + src/xrGameLA/ui/UICheckButton.cpp | 114 + src/xrGameLA/ui/UICheckButton.h | 51 + src/xrGameLA/ui/UIColorAnimatorWrapper.cpp | 164 + src/xrGameLA/ui/UIColorAnimatorWrapper.h | 74 + src/xrGameLA/ui/UIComboBox.cpp | 380 ++ src/xrGameLA/ui/UIComboBox.h | 86 + src/xrGameLA/ui/UIComboBox_script.cpp | 41 + src/xrGameLA/ui/UICustomEdit.cpp | 346 ++ src/xrGameLA/ui/UICustomEdit.h | 79 + src/xrGameLA/ui/UICustomSpin.cpp | 193 + src/xrGameLA/ui/UICustomSpin.h | 58 + src/xrGameLA/ui/UIDebugFonts.cpp | 63 + src/xrGameLA/ui/UIDebugFonts.h | 25 + src/xrGameLA/ui/UIDialogWnd.cpp | 62 + src/xrGameLA/ui/UIDialogWnd.h | 37 + src/xrGameLA/ui/UIDiaryWnd.h | 77 + src/xrGameLA/ui/UIDiaryWnd2.cpp | 354 ++ src/xrGameLA/ui/UIDragDropListEx.cpp | 997 ++++ src/xrGameLA/ui/UIDragDropListEx.h | 209 + src/xrGameLA/ui/UIDragDropReferenceList.cpp | 195 + src/xrGameLA/ui/UIDragDropReferenceList.h | 28 + src/xrGameLA/ui/UIEditBox.cpp | 74 + src/xrGameLA/ui/UIEditBox.h | 28 + src/xrGameLA/ui/UIEditBoxEx.cpp | 35 + src/xrGameLA/ui/UIEditBoxEx.h | 20 + src/xrGameLA/ui/UIEditBox_script.cpp | 34 + src/xrGameLA/ui/UIEditKeyBind.cpp | 240 + src/xrGameLA/ui/UIEditKeyBind.h | 47 + src/xrGameLA/ui/UIEncyclopediaArticleWnd.cpp | 68 + src/xrGameLA/ui/UIEncyclopediaArticleWnd.h | 24 + src/xrGameLA/ui/UIEncyclopediaWnd.cpp | 252 + src/xrGameLA/ui/UIEncyclopediaWnd.h | 69 + src/xrGameLA/ui/UIEventsWnd.cpp | 319 ++ src/xrGameLA/ui/UIEventsWnd.h | 64 + src/xrGameLA/ui/UIFrameLineWnd.cpp | 225 + src/xrGameLA/ui/UIFrameLineWnd.h | 47 + src/xrGameLA/ui/UIFrameWindow.cpp | 295 + src/xrGameLA/ui/UIFrameWindow.h | 49 + src/xrGameLA/ui/UIGameLog.cpp | 126 + src/xrGameLA/ui/UIGameLog.h | 38 + src/xrGameLA/ui/UIGameTutorial.cpp | 486 ++ src/xrGameLA/ui/UIGameTutorial.h | 186 + src/xrGameLA/ui/UIGameTutorialSimpleItem.cpp | 354 ++ src/xrGameLA/ui/UIGameTutorialVideoItem.cpp | 226 + src/xrGameLA/ui/UIHelper.cpp | 132 + src/xrGameLA/ui/UIHelper.h | 46 + src/xrGameLA/ui/UIHint.cpp | 153 + src/xrGameLA/ui/UIHint.h | 83 + src/xrGameLA/ui/UIInteractiveBackground.h | 122 + src/xrGameLA/ui/UIInvUpgrade.cpp | 420 ++ src/xrGameLA/ui/UIInvUpgrade.h | 146 + src/xrGameLA/ui/UIInvUpgradeInfo.cpp | 201 + src/xrGameLA/ui/UIInvUpgradeInfo.h | 55 + src/xrGameLA/ui/UIInvUpgradeProperty.cpp | 233 + src/xrGameLA/ui/UIInvUpgradeProperty.h | 78 + src/xrGameLA/ui/UIInventoryUpgradeWnd.cpp | 387 ++ src/xrGameLA/ui/UIInventoryUpgradeWnd.h | 114 + src/xrGameLA/ui/UIInventoryUpgradeWnd_add.cpp | 142 + src/xrGameLA/ui/UIInventoryUtilities.cpp | 667 +++ src/xrGameLA/ui/UIInventoryUtilities.h | 98 + src/xrGameLA/ui/UIInventoryWnd.cpp | 627 ++ src/xrGameLA/ui/UIInventoryWnd.h | 182 + src/xrGameLA/ui/UIInventoryWnd3.cpp | 327 ++ src/xrGameLA/ui/UIItemInfo.cpp | 301 + src/xrGameLA/ui/UIItemInfo.h | 54 + src/xrGameLA/ui/UIKeyBinding.cpp | 167 + src/xrGameLA/ui/UIKeyBinding.h | 25 + src/xrGameLA/ui/UILabel.cpp | 62 + src/xrGameLA/ui/UILabel.h | 36 + src/xrGameLA/ui/UILanimController.cpp | 44 + src/xrGameLA/ui/UILanimController.h | 118 + src/xrGameLA/ui/UILine.cpp | 184 + src/xrGameLA/ui/UILine.h | 59 + src/xrGameLA/ui/UILines.cpp | 579 ++ src/xrGameLA/ui/UILines.h | 88 + src/xrGameLA/ui/UIListBox.cpp | 372 ++ src/xrGameLA/ui/UIListBox.h | 65 + src/xrGameLA/ui/UIListBoxItem.cpp | 185 + src/xrGameLA/ui/UIListBoxItem.h | 56 + src/xrGameLA/ui/UIListBoxItemEx.cpp | 43 + src/xrGameLA/ui/UIListBoxItemEx.h | 29 + src/xrGameLA/ui/UIListBoxItemMsgChain.cpp | 9 + src/xrGameLA/ui/UIListBoxItemMsgChain.h | 16 + src/xrGameLA/ui/UIListBox_script.cpp | 95 + src/xrGameLA/ui/UIListItem.cpp | 83 + src/xrGameLA/ui/UIListItem.h | 52 + src/xrGameLA/ui/UIListItemEx.cpp | 46 + src/xrGameLA/ui/UIListItemEx.h | 28 + src/xrGameLA/ui/UIListWnd.cpp | 658 +++ src/xrGameLA/ui/UIListWnd.h | 166 + src/xrGameLA/ui/UIListWnd_inline.h | 73 + src/xrGameLA/ui/UIListWnd_script.cpp | 67 + src/xrGameLA/ui/UIMMShniaga.cpp | 417 ++ src/xrGameLA/ui/UIMMShniaga.h | 72 + src/xrGameLA/ui/UIMainIngameWnd.cpp | 1049 ++++ src/xrGameLA/ui/UIMainIngameWnd.h | 167 + src/xrGameLA/ui/UIMap.cpp | 547 ++ src/xrGameLA/ui/UIMap.h | 126 + src/xrGameLA/ui/UIMapInfo.cpp | 154 + src/xrGameLA/ui/UIMapInfo.h | 22 + src/xrGameLA/ui/UIMapInfo_script.cpp | 16 + src/xrGameLA/ui/UIMapWnd.cpp | 923 +++ src/xrGameLA/ui/UIMapWnd.h | 122 + src/xrGameLA/ui/UIMapWndActions.cpp | 292 + src/xrGameLA/ui/UIMapWndActions.h | 20 + src/xrGameLA/ui/UIMapWndActionsSpace.h | 22 + src/xrGameLA/ui/UIMessageBox.cpp | 450 ++ src/xrGameLA/ui/UIMessageBox.h | 69 + src/xrGameLA/ui/UIMessageBoxEx.cpp | 114 + src/xrGameLA/ui/UIMessageBoxEx.h | 29 + src/xrGameLA/ui/UIMessageBox_script.cpp | 27 + src/xrGameLA/ui/UIMessages.h | 146 + src/xrGameLA/ui/UIMessagesWindow.cpp | 114 + src/xrGameLA/ui/UIMessagesWindow.h | 50 + src/xrGameLA/ui/UIMoneyIndicator.cpp | 63 + src/xrGameLA/ui/UIMoneyIndicator.h | 32 + src/xrGameLA/ui/UIMotionIcon.cpp | 165 + src/xrGameLA/ui/UIMotionIcon.h | 48 + src/xrGameLA/ui/UIMpItemsStoreWnd.cpp | 169 + src/xrGameLA/ui/UIMpItemsStoreWnd.h | 50 + src/xrGameLA/ui/UIMultiTextStatic.cpp | 197 + src/xrGameLA/ui/UIMultiTextStatic.h | 69 + src/xrGameLA/ui/UINewsItemWnd.cpp | 73 + src/xrGameLA/ui/UINewsItemWnd.h | 20 + src/xrGameLA/ui/UINewsWnd.cpp | 105 + src/xrGameLA/ui/UINewsWnd.h | 29 + src/xrGameLA/ui/UIOptionsItem.cpp | 127 + src/xrGameLA/ui/UIOptionsItem.h | 53 + src/xrGameLA/ui/UIOptionsManager.cpp | 150 + src/xrGameLA/ui/UIOptionsManager.h | 43 + src/xrGameLA/ui/UIOptionsManagerScript.cpp | 88 + src/xrGameLA/ui/UIOptionsManagerScript.h | 24 + src/xrGameLA/ui/UIOutfitParams.cpp | 178 + src/xrGameLA/ui/UIOutfitParams.h | 65 + src/xrGameLA/ui/UIOutfitSlot.cpp | 106 + src/xrGameLA/ui/UIOutfitSlot.h | 22 + src/xrGameLA/ui/UIPdaAux.cpp | 15 + src/xrGameLA/ui/UIPdaAux.h | 50 + src/xrGameLA/ui/UIPdaContactsWnd.cpp | 174 + src/xrGameLA/ui/UIPdaContactsWnd.h | 58 + src/xrGameLA/ui/UIPdaKillMessage.cpp | 102 + src/xrGameLA/ui/UIPdaKillMessage.h | 29 + src/xrGameLA/ui/UIPdaListItem.cpp | 59 + src/xrGameLA/ui/UIPdaListItem.h | 28 + src/xrGameLA/ui/UIPdaMsgListItem.cpp | 65 + src/xrGameLA/ui/UIPdaMsgListItem.h | 24 + src/xrGameLA/ui/UIPdaWnd.cpp | 437 ++ src/xrGameLA/ui/UIPdaWnd.h | 93 + src/xrGameLA/ui/UIPointerGage.cpp | 58 + src/xrGameLA/ui/UIPointerGage.h | 40 + src/xrGameLA/ui/UIProgressBar.cpp | 138 + src/xrGameLA/ui/UIProgressBar.h | 64 + src/xrGameLA/ui/UIProgressBar_script.cpp | 20 + src/xrGameLA/ui/UIProgressShape.cpp | 162 + src/xrGameLA/ui/UIProgressShape.h | 26 + src/xrGameLA/ui/UIPropertiesBox.cpp | 172 + src/xrGameLA/ui/UIPropertiesBox.h | 43 + src/xrGameLA/ui/UIPropertiesBox_script.cpp | 23 + src/xrGameLA/ui/UIRadioButton.cpp | 24 + src/xrGameLA/ui/UIRadioButton.h | 13 + src/xrGameLA/ui/UIRankIndicator.cpp | 43 + src/xrGameLA/ui/UIRankIndicator.h | 18 + src/xrGameLA/ui/UIScriptWnd.cpp | 79 + src/xrGameLA/ui/UIScriptWnd.h | 38 + src/xrGameLA/ui/UIScriptWnd_script.cpp | 61 + src/xrGameLA/ui/UIScrollBar.cpp | 358 ++ src/xrGameLA/ui/UIScrollBar.h | 82 + src/xrGameLA/ui/UIScrollBox.cpp | 58 + src/xrGameLA/ui/UIScrollBox.h | 11 + src/xrGameLA/ui/UIScrollView.cpp | 445 ++ src/xrGameLA/ui/UIScrollView.h | 87 + src/xrGameLA/ui/UISkinSelector.cpp | 346 ++ src/xrGameLA/ui/UISkinSelector.h | 74 + src/xrGameLA/ui/UISpawnWnd.cpp | 189 + src/xrGameLA/ui/UISpawnWnd.h | 56 + src/xrGameLA/ui/UISpinNum.cpp | 187 + src/xrGameLA/ui/UISpinNum.h | 77 + src/xrGameLA/ui/UISpinText.cpp | 139 + src/xrGameLA/ui/UISpinText.h | 43 + src/xrGameLA/ui/UIStalkersRankingWnd.cpp | 290 + src/xrGameLA/ui/UIStalkersRankingWnd.h | 64 + src/xrGameLA/ui/UIStatic.cpp | 396 ++ src/xrGameLA/ui/UIStatic.h | 212 + src/xrGameLA/ui/UIStatic_script.cpp | 89 + src/xrGameLA/ui/UIStatix.cpp | 91 + src/xrGameLA/ui/UIStatix.h | 22 + src/xrGameLA/ui/UISubLine.cpp | 101 + src/xrGameLA/ui/UISubLine.h | 28 + src/xrGameLA/ui/UITabButton.cpp | 57 + src/xrGameLA/ui/UITabButton.h | 25 + src/xrGameLA/ui/UITabButtonMP.cpp | 85 + src/xrGameLA/ui/UITabButtonMP.h | 24 + src/xrGameLA/ui/UITabControl.cpp | 223 + src/xrGameLA/ui/UITabControl.h | 75 + src/xrGameLA/ui/UITabControl_script.cpp | 27 + src/xrGameLA/ui/UITalkDialogWnd.cpp | 329 ++ src/xrGameLA/ui/UITalkDialogWnd.h | 119 + src/xrGameLA/ui/UITalkWnd.cpp | 489 ++ src/xrGameLA/ui/UITalkWnd.h | 89 + src/xrGameLA/ui/UITaskDescrWnd.cpp | 70 + src/xrGameLA/ui/UITaskDescrWnd.h | 26 + src/xrGameLA/ui/UITaskItem.cpp | 547 ++ src/xrGameLA/ui/UITaskItem.h | 142 + src/xrGameLA/ui/UITaskWnd.cpp | 456 ++ src/xrGameLA/ui/UITaskWnd.h | 125 + src/xrGameLA/ui/UITextBanner.cpp | 188 + src/xrGameLA/ui/UITextBanner.h | 103 + src/xrGameLA/ui/UITextureMaster.cpp | 192 + src/xrGameLA/ui/UITextureMaster.h | 56 + src/xrGameLA/ui/UITrackBar.cpp | 336 ++ src/xrGameLA/ui/UITrackBar.h | 64 + src/xrGameLA/ui/UITrackBarVariable.cpp | 291 + src/xrGameLA/ui/UITrackBarVariable.h | 61 + src/xrGameLA/ui/UITrackButton.cpp | 17 + src/xrGameLA/ui/UITrackButton.h | 15 + src/xrGameLA/ui/UITradeWnd.cpp | 780 +++ src/xrGameLA/ui/UITradeWnd.h | 108 + src/xrGameLA/ui/UITreeViewBoxItem.cpp | 579 ++ src/xrGameLA/ui/UITreeViewBoxItem.h | 121 + src/xrGameLA/ui/UITreeViewItem.cpp | 572 ++ src/xrGameLA/ui/UITreeViewItem.h | 128 + src/xrGameLA/ui/UIUpgradeWnd.cpp | 582 ++ src/xrGameLA/ui/UIUpgradeWnd.h | 112 + src/xrGameLA/ui/UIVideoPlayerWnd.cpp | 156 + src/xrGameLA/ui/UIVideoPlayerWnd.h | 48 + src/xrGameLA/ui/UIVoteStatusWnd.cpp | 28 + src/xrGameLA/ui/UIVoteStatusWnd.h | 16 + src/xrGameLA/ui/UIWeatherEditor.cpp | 1185 ++++ src/xrGameLA/ui/UIWeatherEditor.h | 353 ++ src/xrGameLA/ui/UIWindow.cpp | 725 +++ src/xrGameLA/ui/UIWindow.h | 261 + src/xrGameLA/ui/UIWindow_script.cpp | 338 ++ src/xrGameLA/ui/UIWndCallback.cpp | 63 + src/xrGameLA/ui/UIWndCallback.h | 24 + src/xrGameLA/ui/UIWpnParams.cpp | 121 + src/xrGameLA/ui/UIWpnParams.h | 33 + src/xrGameLA/ui/UIXmlInit.cpp | 1637 ++++++ src/xrGameLA/ui/UIXmlInit.h | 111 + src/xrGameLA/ui/UI_IB_FrameLineWnd.cpp | 18 + src/xrGameLA/ui/UI_IB_FrameLineWnd.h | 19 + src/xrGameLA/ui/UI_IB_Static.cpp | 25 + src/xrGameLA/ui/UI_IB_Static.h | 20 + src/xrGameLA/ui/map_hint.cpp | 40 + src/xrGameLA/ui/map_hint.h | 22 + src/xrGameLA/ui/pch_script.h | 1 + src/xrGameLA/ui/stdafx.h | 1 + src/xrGameLA/ui/uiabstract.h | 110 + src/xrGameLA/ui/uiinventorywnd2.cpp | 576 ++ src/xrGameLA/ui/uilinestd.h | 7 + src/xrGameLA/ui/uiscriptwnd_script.h | 40 + src/xrGameLA/ui/uiscriptwnd_script2.cpp | 43 + src/xrGameLA/ui/xrUIXmlParser.cpp | 74 + src/xrGameLA/ui/xrUIXmlParser.h | 13 + src/xrGameLA/ui_base.cpp | 300 + src/xrGameLA/ui_base.h | 102 + src/xrGameLA/ui_defs.h | 44 + src/xrGameLA/ui_export_script.cpp | 78 + src/xrGameLA/vertex_allocator_fixed.h | 33 + src/xrGameLA/vertex_allocator_fixed_inline.h | 56 + src/xrGameLA/vertex_manager_fixed.h | 82 + src/xrGameLA/vertex_manager_fixed_inline.h | 114 + src/xrGameLA/vertex_manager_hash_fixed.h | 100 + .../vertex_manager_hash_fixed_inline.h | 168 + src/xrGameLA/vertex_path.h | 40 + src/xrGameLA/vertex_path_inline.h | 71 + src/xrGameLA/vision_client.cpp | 131 + src/xrGameLA/vision_client.h | 78 + src/xrGameLA/vision_client_inline.h | 18 + src/xrGameLA/visual_memory_manager.cpp | 906 +++ src/xrGameLA/visual_memory_manager.h | 147 + src/xrGameLA/visual_memory_manager_inline.h | 51 + src/xrGameLA/visual_memory_params.cpp | 30 + src/xrGameLA/visual_memory_params.h | 24 + src/xrGameLA/wallmark_manager.cpp | 458 ++ src/xrGameLA/wallmark_manager.h | 18 + src/xrGameLA/weaponBM16.cpp | 163 + src/xrGameLA/weaponBM16.h | 28 + src/xrGameLA/weaponBM16_script.cpp | 14 + src/xrGameLA/weapon_bobbing.cpp | 102 + src/xrGameLA/weapon_bobbing.h | 42 + src/xrGameLA/weapon_collision.cpp | 149 + src/xrGameLA/weapon_collision.h | 32 + src/xrGameLA/weaponshotgun_script.cpp | 17 + src/xrGameLA/wrapper_abstract.h | 69 + src/xrGameLA/wrapper_abstract_inline.h | 154 + src/xrGameLA/xml_str_id_loader.h | 207 + src/xrGameLA/xrClientsPool.cpp | 95 + src/xrGameLA/xrClientsPool.h | 42 + src/xrGameLA/xrEProps.h | 153 + src/xrGameLA/xrGame.cpp | 66 + src/xrGameLA/xrMessages.h | 238 + src/xrGameLA/xrServer.cpp | 1169 ++++ src/xrGameLA/xrServer.h | 315 + src/xrGameLA/xrServer_CL_connect.cpp | 196 + src/xrGameLA/xrServer_CL_disconnect.cpp | 58 + src/xrGameLA/xrServer_Connect.cpp | 150 + src/xrGameLA/xrServer_Disconnect.cpp | 9 + src/xrGameLA/xrServer_Factory.cpp | 15 + src/xrGameLA/xrServer_Object_Base.cpp | 440 ++ src/xrGameLA/xrServer_Object_Base.h | 167 + src/xrGameLA/xrServer_Objects.cpp | 275 + src/xrGameLA/xrServer_Objects.h | 231 + src/xrGameLA/xrServer_Objects_ALife.cpp | 2007 +++++++ src/xrGameLA/xrServer_Objects_ALife.h | 589 ++ src/xrGameLA/xrServer_Objects_ALife_All.h | 15 + src/xrGameLA/xrServer_Objects_ALife_Items.cpp | 1150 ++++ src/xrGameLA/xrServer_Objects_ALife_Items.h | 336 ++ .../xrServer_Objects_ALife_Items_script.cpp | 120 + .../xrServer_Objects_ALife_Items_script2.cpp | 102 + .../xrServer_Objects_ALife_Items_script3.cpp | 31 + .../xrServer_Objects_ALife_Monsters.cpp | 1981 +++++++ .../xrServer_Objects_ALife_Monsters.h | 535 ++ ...xrServer_Objects_ALife_Monsters_script.cpp | 74 + ...rServer_Objects_ALife_Monsters_script2.cpp | 60 + ...rServer_Objects_ALife_Monsters_script4.cpp | 102 + .../xrServer_Objects_ALife_script.cpp | 150 + .../xrServer_Objects_ALife_script2.cpp | 114 + .../xrServer_Objects_ALife_script3.cpp | 63 + src/xrGameLA/xrServer_Objects_Abstract.cpp | 125 + src/xrGameLA/xrServer_Objects_Abstract.h | 126 + src/xrGameLA/xrServer_Objects_script.cpp | 163 + src/xrGameLA/xrServer_Objects_script2.cpp | 40 + src/xrGameLA/xrServer_Space.h | 65 + src/xrGameLA/xrServer_balance.cpp | 24 + src/xrGameLA/xrServer_info.cpp | 194 + src/xrGameLA/xrServer_info.h | 44 + src/xrGameLA/xrServer_perform_GameExport.cpp | 42 + src/xrGameLA/xrServer_perform_RPgen.cpp | 34 + src/xrGameLA/xrServer_perform_migration.cpp | 28 + src/xrGameLA/xrServer_perform_sls_default.cpp | 76 + src/xrGameLA/xrServer_perform_sls_load.cpp | 30 + src/xrGameLA/xrServer_perform_sls_save.cpp | 35 + src/xrGameLA/xrServer_perform_transfer.cpp | 56 + src/xrGameLA/xrServer_process_event.cpp | 290 + .../xrServer_process_event_activate.cpp | 39 + .../xrServer_process_event_destroy.cpp | 103 + .../xrServer_process_event_ownership.cpp | 112 + .../xrServer_process_event_reject.cpp | 67 + src/xrGameLA/xrServer_process_spawn.cpp | 160 + src/xrGameLA/xrServer_process_update.cpp | 87 + src/xrGameLA/xrServer_script_macroses.h | 454 ++ src/xrGameLA/xrServer_sls_clear.cpp | 96 + src/xrGameLA/xrServer_updates_compressor.cpp | 302 + src/xrGameLA/xrServer_updates_compressor.h | 78 + src/xrGameLA/xr_Client_BattlEye.cpp | 157 + src/xrGameLA/xr_Client_BattlEye.h | 54 + src/xrGameLA/xr_Server_BattlEye.cpp | 265 + src/xrGameLA/xr_Server_BattlEye.h | 69 + src/xrGameLA/xr_level_controller.cpp | 631 ++ src/xrGameLA/xr_level_controller.h | 179 + src/xrGameLA/xr_time.cpp | 97 + src/xrGameLA/xr_time.h | 51 + src/xrGameLA/xrgame_dll_detach.cpp | 138 + ...rserver_objects_alife_monsters_script3.cpp | 93 + src/xrGameLA/zone_effector.cpp | 83 + src/xrGameLA/zone_effector.h | 32 + 2594 files changed, 373812 insertions(+) create mode 100644 src/xrGameLA/AI_PhraseDialogManager.cpp create mode 100644 src/xrGameLA/AI_PhraseDialogManager.h create mode 100644 src/xrGameLA/AcidFog.cpp create mode 100644 src/xrGameLA/AcidFog.h create mode 100644 src/xrGameLA/Actor.cpp create mode 100644 src/xrGameLA/Actor.h create mode 100644 src/xrGameLA/ActorAnimation.cpp create mode 100644 src/xrGameLA/ActorAnimation.h create mode 100644 src/xrGameLA/ActorCameras.cpp create mode 100644 src/xrGameLA/ActorCondition.cpp create mode 100644 src/xrGameLA/ActorCondition.h create mode 100644 src/xrGameLA/ActorEatablesEffects.cpp create mode 100644 src/xrGameLA/ActorEffector.cpp create mode 100644 src/xrGameLA/ActorEffector.h create mode 100644 src/xrGameLA/ActorEffector_script.cpp create mode 100644 src/xrGameLA/ActorFollowers.cpp create mode 100644 src/xrGameLA/ActorFollowers.h create mode 100644 src/xrGameLA/ActorHudWetness.cpp create mode 100644 src/xrGameLA/ActorInput.cpp create mode 100644 src/xrGameLA/ActorMountedWeapon.cpp create mode 100644 src/xrGameLA/ActorState.h create mode 100644 src/xrGameLA/ActorVehicle.cpp create mode 100644 src/xrGameLA/Actor_Events.cpp create mode 100644 src/xrGameLA/Actor_Feel.cpp create mode 100644 src/xrGameLA/Actor_Flags.h create mode 100644 src/xrGameLA/Actor_Movement.cpp create mode 100644 src/xrGameLA/Actor_Network.cpp create mode 100644 src/xrGameLA/Actor_Sleep.cpp create mode 100644 src/xrGameLA/Actor_Weapon.cpp create mode 100644 src/xrGameLA/AdvancedAfDetector.cpp create mode 100644 src/xrGameLA/AdvancedAfDetector.h create mode 100644 src/xrGameLA/AmebaZone.cpp create mode 100644 src/xrGameLA/AmebaZone.h create mode 100644 src/xrGameLA/Artifact.cpp create mode 100644 src/xrGameLA/Artifact.h create mode 100644 src/xrGameLA/BastArtifact.cpp create mode 100644 src/xrGameLA/BastArtifact.h create mode 100644 src/xrGameLA/BlackDrops.cpp create mode 100644 src/xrGameLA/BlackDrops.h create mode 100644 src/xrGameLA/BlackGraviArtifact.cpp create mode 100644 src/xrGameLA/BlackGraviArtifact.h create mode 100644 src/xrGameLA/BlockAllocator.h create mode 100644 src/xrGameLA/Bolt.cpp create mode 100644 src/xrGameLA/Bolt.h create mode 100644 src/xrGameLA/BoneProtections.cpp create mode 100644 src/xrGameLA/BoneProtections.h create mode 100644 src/xrGameLA/Booster.h create mode 100644 src/xrGameLA/BottleItem.cpp create mode 100644 src/xrGameLA/BottleItem.h create mode 100644 src/xrGameLA/BreakableObject.cpp create mode 100644 src/xrGameLA/BreakableObject.h create mode 100644 src/xrGameLA/CHelmet.cpp create mode 100644 src/xrGameLA/CHelmet.h create mode 100644 src/xrGameLA/CMakeLists.txt create mode 100644 src/xrGameLA/CalculateTriangle.h create mode 100644 src/xrGameLA/CameraEffector.cpp create mode 100644 src/xrGameLA/CameraEffector.h create mode 100644 src/xrGameLA/CameraFirstEye.cpp create mode 100644 src/xrGameLA/CameraFirstEye.h create mode 100644 src/xrGameLA/Car.cpp create mode 100644 src/xrGameLA/Car.h create mode 100644 src/xrGameLA/CarCameras.cpp create mode 100644 src/xrGameLA/CarDamageParticles.cpp create mode 100644 src/xrGameLA/CarDamageParticles.h create mode 100644 src/xrGameLA/CarDoors.cpp create mode 100644 src/xrGameLA/CarExhaust.cpp create mode 100644 src/xrGameLA/CarInput.cpp create mode 100644 src/xrGameLA/CarLights.cpp create mode 100644 src/xrGameLA/CarLights.h create mode 100644 src/xrGameLA/CarScript.cpp create mode 100644 src/xrGameLA/CarSound.cpp create mode 100644 src/xrGameLA/CarWeapon.cpp create mode 100644 src/xrGameLA/CarWeapon.h create mode 100644 src/xrGameLA/CarWheels.cpp create mode 100644 src/xrGameLA/CharacterPhysicsSupport.cpp create mode 100644 src/xrGameLA/CharacterPhysicsSupport.h create mode 100644 src/xrGameLA/ClimableObject.cpp create mode 100644 src/xrGameLA/ClimableObject.h create mode 100644 src/xrGameLA/ContextMenu.cpp create mode 100644 src/xrGameLA/ContextMenu.h create mode 100644 src/xrGameLA/CustomDetector.cpp create mode 100644 src/xrGameLA/CustomDetector.h create mode 100644 src/xrGameLA/CustomDetector2.cpp create mode 100644 src/xrGameLA/CustomDetector2.h create mode 100644 src/xrGameLA/CustomMonster.cpp create mode 100644 src/xrGameLA/CustomMonster.h create mode 100644 src/xrGameLA/CustomMonster_VCPU.cpp create mode 100644 src/xrGameLA/CustomMonster_inline.h create mode 100644 src/xrGameLA/CustomOutfit.cpp create mode 100644 src/xrGameLA/CustomOutfit.h create mode 100644 src/xrGameLA/CustomRocket.cpp create mode 100644 src/xrGameLA/CustomRocket.h create mode 100644 src/xrGameLA/CustomTimer.cpp create mode 100644 src/xrGameLA/CustomTimer.h create mode 100644 src/xrGameLA/CustomTimer_script.cpp create mode 100644 src/xrGameLA/CustomTimersManager.cpp create mode 100644 src/xrGameLA/CustomTimersManager.h create mode 100644 src/xrGameLA/CustomTimersManager_script.cpp create mode 100644 src/xrGameLA/CustomZone.cpp create mode 100644 src/xrGameLA/CustomZone.h create mode 100644 src/xrGameLA/CycleConstStorage.h create mode 100644 src/xrGameLA/DBG_Car.cpp create mode 100644 src/xrGameLA/DamagableItem.cpp create mode 100644 src/xrGameLA/DamagableItem.h create mode 100644 src/xrGameLA/DamageSource.h create mode 100644 src/xrGameLA/DelayedActionFuse.cpp create mode 100644 src/xrGameLA/DelayedActionFuse.h create mode 100644 src/xrGameLA/DestroyablePhysicsObject.cpp create mode 100644 src/xrGameLA/DestroyablePhysicsObject.h create mode 100644 src/xrGameLA/DisablingParams.cpp create mode 100644 src/xrGameLA/DisablingParams.h create mode 100644 src/xrGameLA/DummyArtifact.cpp create mode 100644 src/xrGameLA/DummyArtifact.h create mode 100644 src/xrGameLA/DynamicHeightMap.cpp create mode 100644 src/xrGameLA/DynamicHeightMap.h create mode 100644 src/xrGameLA/EffectorBobbing.cpp create mode 100644 src/xrGameLA/EffectorBobbing.h create mode 100644 src/xrGameLA/EffectorFall.cpp create mode 100644 src/xrGameLA/EffectorFall.h create mode 100644 src/xrGameLA/EffectorShot.cpp create mode 100644 src/xrGameLA/EffectorShot.h create mode 100644 src/xrGameLA/EffectorShotX.cpp create mode 100644 src/xrGameLA/EffectorShotX.h create mode 100644 src/xrGameLA/EffectorZoomInertion.cpp create mode 100644 src/xrGameLA/EffectorZoomInertion.h create mode 100644 src/xrGameLA/ElectricBall.cpp create mode 100644 src/xrGameLA/ElectricBall.h create mode 100644 src/xrGameLA/ElevatorState.cpp create mode 100644 src/xrGameLA/ElevatorState.h create mode 100644 src/xrGameLA/EliteAfDetector.cpp create mode 100644 src/xrGameLA/EliteAfDetector.h create mode 100644 src/xrGameLA/Entity.cpp create mode 100644 src/xrGameLA/Entity.h create mode 100644 src/xrGameLA/EntityCondition.cpp create mode 100644 src/xrGameLA/EntityCondition.h create mode 100644 src/xrGameLA/ExoOutfit.cpp create mode 100644 src/xrGameLA/ExoOutfit.h create mode 100644 src/xrGameLA/Explosive.cpp create mode 100644 src/xrGameLA/Explosive.h create mode 100644 src/xrGameLA/ExplosiveItem.cpp create mode 100644 src/xrGameLA/ExplosiveItem.h create mode 100644 src/xrGameLA/ExplosiveRocket.cpp create mode 100644 src/xrGameLA/ExplosiveRocket.h create mode 100644 src/xrGameLA/ExplosiveScript.cpp create mode 100644 src/xrGameLA/ExtendedGeom.h create mode 100644 src/xrGameLA/F1.cpp create mode 100644 src/xrGameLA/F1.h create mode 100644 src/xrGameLA/FadedBall.cpp create mode 100644 src/xrGameLA/FadedBall.h create mode 100644 src/xrGameLA/FastDelegate.h create mode 100644 src/xrGameLA/Fireball.cpp create mode 100644 src/xrGameLA/Fireball.h create mode 100644 src/xrGameLA/FoodItem.cpp create mode 100644 src/xrGameLA/FoodItem.h create mode 100644 src/xrGameLA/FryupZone.cpp create mode 100644 src/xrGameLA/FryupZone.h create mode 100644 src/xrGameLA/GalantineArtifact.cpp create mode 100644 src/xrGameLA/GalantineArtifact.h create mode 100644 src/xrGameLA/GameObject.cpp create mode 100644 src/xrGameLA/GameObject.h create mode 100644 src/xrGameLA/GamePersistent.cpp create mode 100644 src/xrGameLA/GamePersistent.h create mode 100644 src/xrGameLA/GameTask.cpp create mode 100644 src/xrGameLA/GameTask.h create mode 100644 src/xrGameLA/GameTaskDefs.h create mode 100644 src/xrGameLA/GameTask_script.cpp create mode 100644 src/xrGameLA/GametaskManager.cpp create mode 100644 src/xrGameLA/GametaskManager.h create mode 100644 src/xrGameLA/Geometry.cpp create mode 100644 src/xrGameLA/Geometry.h create mode 100644 src/xrGameLA/GraviArtifact.cpp create mode 100644 src/xrGameLA/GraviArtifact.h create mode 100644 src/xrGameLA/GraviZone.cpp create mode 100644 src/xrGameLA/GraviZone.h create mode 100644 src/xrGameLA/Grenade.cpp create mode 100644 src/xrGameLA/Grenade.h create mode 100644 src/xrGameLA/GrenadeLauncher.cpp create mode 100644 src/xrGameLA/GrenadeLauncher.h create mode 100644 src/xrGameLA/HUDCrosshair.cpp create mode 100644 src/xrGameLA/HUDCrosshair.h create mode 100644 src/xrGameLA/HUDManager.cpp create mode 100644 src/xrGameLA/HUDManager.h create mode 100644 src/xrGameLA/HUDTarget.cpp create mode 100644 src/xrGameLA/HUDTarget.h create mode 100644 src/xrGameLA/HairsZone.cpp create mode 100644 src/xrGameLA/HairsZone.h create mode 100644 src/xrGameLA/HairsZone_script.cpp create mode 100644 src/xrGameLA/HangingLamp.cpp create mode 100644 src/xrGameLA/HangingLamp.h create mode 100644 src/xrGameLA/Helicopter.cpp create mode 100644 src/xrGameLA/Helicopter2.cpp create mode 100644 src/xrGameLA/HelicopterMovementManager.cpp create mode 100644 src/xrGameLA/HelicopterWeapon.cpp create mode 100644 src/xrGameLA/Hit.cpp create mode 100644 src/xrGameLA/Hit.h create mode 100644 src/xrGameLA/HitMarker.cpp create mode 100644 src/xrGameLA/HitMarker.h create mode 100644 src/xrGameLA/HudItem.cpp create mode 100644 src/xrGameLA/HudItem.h create mode 100644 src/xrGameLA/HudItemCallback.cpp create mode 100644 src/xrGameLA/HudSound.cpp create mode 100644 src/xrGameLA/HudSound.h create mode 100644 src/xrGameLA/IColisiondamageInfo.h create mode 100644 src/xrGameLA/IKFoot.cpp create mode 100644 src/xrGameLA/IKFoot.h create mode 100644 src/xrGameLA/IKFoot_inl.h create mode 100644 src/xrGameLA/IKLimbsController.cpp create mode 100644 src/xrGameLA/IKLimbsController.h create mode 100644 src/xrGameLA/InfoDocument.cpp create mode 100644 src/xrGameLA/InfoDocument.h create mode 100644 src/xrGameLA/InfoPortion.cpp create mode 100644 src/xrGameLA/InfoPortion.h create mode 100644 src/xrGameLA/InfoPortionDefs.h create mode 100644 src/xrGameLA/Inventory.cpp create mode 100644 src/xrGameLA/Inventory.h create mode 100644 src/xrGameLA/InventoryBox.cpp create mode 100644 src/xrGameLA/InventoryBox.h create mode 100644 src/xrGameLA/InventoryOwner.cpp create mode 100644 src/xrGameLA/InventoryOwner.h create mode 100644 src/xrGameLA/ItemHealth.cpp create mode 100644 src/xrGameLA/ItemHealth.h create mode 100644 src/xrGameLA/ItemListTypes.h create mode 100644 src/xrGameLA/Level.cpp create mode 100644 src/xrGameLA/Level.h create mode 100644 src/xrGameLA/LevelFogOfWar.cpp create mode 100644 src/xrGameLA/LevelFogOfWar.h create mode 100644 src/xrGameLA/LevelGameDef.cpp create mode 100644 src/xrGameLA/LevelGameDef.h create mode 100644 src/xrGameLA/Level_Bullet_Manager.cpp create mode 100644 src/xrGameLA/Level_Bullet_Manager.h create mode 100644 src/xrGameLA/Level_SLS_Default.cpp create mode 100644 src/xrGameLA/Level_SLS_Load.cpp create mode 100644 src/xrGameLA/Level_SLS_Save.cpp create mode 100644 src/xrGameLA/Level_bullet_manager_firetrace.cpp create mode 100644 src/xrGameLA/Level_input.cpp create mode 100644 src/xrGameLA/Level_load.cpp create mode 100644 src/xrGameLA/Level_network.cpp create mode 100644 src/xrGameLA/Level_network_Demo.cpp create mode 100644 src/xrGameLA/Level_network_Demo.h create mode 100644 src/xrGameLA/Level_network_messages.cpp create mode 100644 src/xrGameLA/Level_network_spawn.cpp create mode 100644 src/xrGameLA/Level_network_start_client.cpp create mode 100644 src/xrGameLA/Level_start.cpp create mode 100644 src/xrGameLA/MPPlayersBag.cpp create mode 100644 src/xrGameLA/MPPlayersBag.h create mode 100644 src/xrGameLA/MainMenu.cpp create mode 100644 src/xrGameLA/MainMenu.h create mode 100644 src/xrGameLA/MathUtils.cpp create mode 100644 src/xrGameLA/MathUtils.h create mode 100644 src/xrGameLA/MathUtilsOde.h create mode 100644 src/xrGameLA/MercuryBall.cpp create mode 100644 src/xrGameLA/MercuryBall.h create mode 100644 src/xrGameLA/MilitaryOutfit.cpp create mode 100644 src/xrGameLA/MilitaryOutfit.h create mode 100644 src/xrGameLA/Mincer.cpp create mode 100644 src/xrGameLA/Mincer.h create mode 100644 src/xrGameLA/Missile.cpp create mode 100644 src/xrGameLA/Missile.h create mode 100644 src/xrGameLA/MosquitoBald.cpp create mode 100644 src/xrGameLA/MosquitoBald.h create mode 100644 src/xrGameLA/MosquitoBald_script.cpp create mode 100644 src/xrGameLA/NET_Queue.h create mode 100644 src/xrGameLA/Needles.cpp create mode 100644 src/xrGameLA/Needles.h create mode 100644 src/xrGameLA/NoGravityZone.cpp create mode 100644 src/xrGameLA/NoGravityZone.h create mode 100644 src/xrGameLA/OutfitBase.cpp create mode 100644 src/xrGameLA/OutfitBase.h create mode 100644 src/xrGameLA/PDA.cpp create mode 100644 src/xrGameLA/PDA.h create mode 100644 src/xrGameLA/PHAICharacter.cpp create mode 100644 src/xrGameLA/PHAICharacter.h create mode 100644 src/xrGameLA/PHActivationShape.cpp create mode 100644 src/xrGameLA/PHActivationShape.h create mode 100644 src/xrGameLA/PHActorCharacter.cpp create mode 100644 src/xrGameLA/PHActorCharacter.h create mode 100644 src/xrGameLA/PHActorCharacterInline.h create mode 100644 src/xrGameLA/PHBaseBodyEffector.h create mode 100644 src/xrGameLA/PHCapture.cpp create mode 100644 src/xrGameLA/PHCapture.h create mode 100644 src/xrGameLA/PHCaptureInit.cpp create mode 100644 src/xrGameLA/PHCharacter.cpp create mode 100644 src/xrGameLA/PHCharacter.h create mode 100644 src/xrGameLA/PHCollideValidator.cpp create mode 100644 src/xrGameLA/PHCollideValidator.h create mode 100644 src/xrGameLA/PHCollisionDamageReceiver.cpp create mode 100644 src/xrGameLA/PHCollisionDamageReceiver.h create mode 100644 src/xrGameLA/PHCommander.cpp create mode 100644 src/xrGameLA/PHCommander.h create mode 100644 src/xrGameLA/PHContactBodyEffector.cpp create mode 100644 src/xrGameLA/PHContactBodyEffector.h create mode 100644 src/xrGameLA/PHDebug.cpp create mode 100644 src/xrGameLA/PHDebug.h create mode 100644 src/xrGameLA/PHDefs.h create mode 100644 src/xrGameLA/PHDestroyable.cpp create mode 100644 src/xrGameLA/PHDestroyable.h create mode 100644 src/xrGameLA/PHDestroyableNotificate.cpp create mode 100644 src/xrGameLA/PHDestroyableNotificate.h create mode 100644 src/xrGameLA/PHDisabling.cpp create mode 100644 src/xrGameLA/PHDisabling.h create mode 100644 src/xrGameLA/PHDynamicData.cpp create mode 100644 src/xrGameLA/PHDynamicData.h create mode 100644 src/xrGameLA/PHElement.cpp create mode 100644 src/xrGameLA/PHElement.h create mode 100644 src/xrGameLA/PHElementInline.h create mode 100644 src/xrGameLA/PHElementNetState.cpp create mode 100644 src/xrGameLA/PHFracture.cpp create mode 100644 src/xrGameLA/PHFracture.h create mode 100644 src/xrGameLA/PHGeometryOwner.cpp create mode 100644 src/xrGameLA/PHGeometryOwner.h create mode 100644 src/xrGameLA/PHImpact.h create mode 100644 src/xrGameLA/PHInterpolation.cpp create mode 100644 src/xrGameLA/PHInterpolation.h create mode 100644 src/xrGameLA/PHIsland.cpp create mode 100644 src/xrGameLA/PHIsland.h create mode 100644 src/xrGameLA/PHItemList.h create mode 100644 src/xrGameLA/PHJoint.cpp create mode 100644 src/xrGameLA/PHJoint.h create mode 100644 src/xrGameLA/PHJointDestroyInfo.cpp create mode 100644 src/xrGameLA/PHJointDestroyInfo.h create mode 100644 src/xrGameLA/PHMoveStorage.cpp create mode 100644 src/xrGameLA/PHMoveStorage.h create mode 100644 src/xrGameLA/PHMovementControl.cpp create mode 100644 src/xrGameLA/PHMovementControl.h create mode 100644 src/xrGameLA/PHMovementDynamicActivate.cpp create mode 100644 src/xrGameLA/PHNetState.cpp create mode 100644 src/xrGameLA/PHNetState.h create mode 100644 src/xrGameLA/PHObject.cpp create mode 100644 src/xrGameLA/PHObject.h create mode 100644 src/xrGameLA/PHReqComparer.h create mode 100644 src/xrGameLA/PHScriptCall.cpp create mode 100644 src/xrGameLA/PHScriptCall.h create mode 100644 src/xrGameLA/PHShell.cpp create mode 100644 src/xrGameLA/PHShell.h create mode 100644 src/xrGameLA/PHShellActivate.cpp create mode 100644 src/xrGameLA/PHShellCreator.cpp create mode 100644 src/xrGameLA/PHShellCreator.h create mode 100644 src/xrGameLA/PHShellNetState.cpp create mode 100644 src/xrGameLA/PHShellSplitter.cpp create mode 100644 src/xrGameLA/PHShellSplitter.h create mode 100644 src/xrGameLA/PHSimpleCalls.cpp create mode 100644 src/xrGameLA/PHSimpleCalls.h create mode 100644 src/xrGameLA/PHSimpleCallsScript.cpp create mode 100644 src/xrGameLA/PHSimpleCharacter.cpp create mode 100644 src/xrGameLA/PHSimpleCharacter.h create mode 100644 src/xrGameLA/PHSimpleCharacterInline.h create mode 100644 src/xrGameLA/PHSkeleton.cpp create mode 100644 src/xrGameLA/PHSkeleton.h create mode 100644 src/xrGameLA/PHSoundPlayer.cpp create mode 100644 src/xrGameLA/PHSoundPlayer.h create mode 100644 src/xrGameLA/PHSplitedShell.cpp create mode 100644 src/xrGameLA/PHSplitedShell.h create mode 100644 src/xrGameLA/PHStaticGeomShell.cpp create mode 100644 src/xrGameLA/PHStaticGeomShell.h create mode 100644 src/xrGameLA/PHSynchronize.cpp create mode 100644 src/xrGameLA/PHSynchronize.h create mode 100644 src/xrGameLA/PHValideValues.h create mode 100644 src/xrGameLA/PHWorld.cpp create mode 100644 src/xrGameLA/PHWorld.h create mode 100644 src/xrGameLA/PHWorldScript.cpp create mode 100644 src/xrGameLA/ParticlesObject.cpp create mode 100644 src/xrGameLA/ParticlesObject.h create mode 100644 src/xrGameLA/ParticlesPlayer.cpp create mode 100644 src/xrGameLA/ParticlesPlayer.h create mode 100644 src/xrGameLA/PdaMsg.h create mode 100644 src/xrGameLA/PdaScript.cpp create mode 100644 src/xrGameLA/Phrase.cpp create mode 100644 src/xrGameLA/Phrase.h create mode 100644 src/xrGameLA/PhraseDialog.cpp create mode 100644 src/xrGameLA/PhraseDialog.h create mode 100644 src/xrGameLA/PhraseDialogDefs.h create mode 100644 src/xrGameLA/PhraseDialogManager.cpp create mode 100644 src/xrGameLA/PhraseDialogManager.h create mode 100644 src/xrGameLA/PhraseDialog_script.cpp create mode 100644 src/xrGameLA/PhraseDialog_script.h create mode 100644 src/xrGameLA/PhraseScript.cpp create mode 100644 src/xrGameLA/PhraseScript.h create mode 100644 src/xrGameLA/PhysicObject.cpp create mode 100644 src/xrGameLA/PhysicObject.h create mode 100644 src/xrGameLA/PhysicObject_script.cpp create mode 100644 src/xrGameLA/Physics.cpp create mode 100644 src/xrGameLA/Physics.h create mode 100644 src/xrGameLA/PhysicsCommon.h create mode 100644 src/xrGameLA/PhysicsGamePars.cpp create mode 100644 src/xrGameLA/PhysicsGamePars.h create mode 100644 src/xrGameLA/PhysicsShell.cpp create mode 100644 src/xrGameLA/PhysicsShell.h create mode 100644 src/xrGameLA/PhysicsShellAnimator.cpp create mode 100644 src/xrGameLA/PhysicsShellAnimator.h create mode 100644 src/xrGameLA/PhysicsShellAnimatorBoneData.h create mode 100644 src/xrGameLA/PhysicsShellHolder.cpp create mode 100644 src/xrGameLA/PhysicsShellHolder.h create mode 100644 src/xrGameLA/PhysicsShellScript.cpp create mode 100644 src/xrGameLA/PhysicsSkeletonObject.cpp create mode 100644 src/xrGameLA/PhysicsSkeletonObject.h create mode 100644 src/xrGameLA/PostprocessAnimator.cpp create mode 100644 src/xrGameLA/PostprocessAnimator.h create mode 100644 src/xrGameLA/PropertiesListHelper.h create mode 100644 src/xrGameLA/PropertiesListTypes.h create mode 100644 src/xrGameLA/RGD5.cpp create mode 100644 src/xrGameLA/RGD5.h create mode 100644 src/xrGameLA/RadioactiveZone.cpp create mode 100644 src/xrGameLA/RadioactiveZone.h create mode 100644 src/xrGameLA/RegistryFuncs.cpp create mode 100644 src/xrGameLA/RegistryFuncs.h create mode 100644 src/xrGameLA/RocketLauncher.cpp create mode 100644 src/xrGameLA/RocketLauncher.h create mode 100644 src/xrGameLA/RustyHairArtifact.cpp create mode 100644 src/xrGameLA/RustyHairArtifact.h create mode 100644 src/xrGameLA/ScientificOutfit.cpp create mode 100644 src/xrGameLA/ScientificOutfit.h create mode 100644 src/xrGameLA/Scope.cpp create mode 100644 src/xrGameLA/Scope.h create mode 100644 src/xrGameLA/ScriptXMLInit.cpp create mode 100644 src/xrGameLA/ScriptXMLInit.h create mode 100644 src/xrGameLA/ShapeData.h create mode 100644 src/xrGameLA/ShellHit.cpp create mode 100644 src/xrGameLA/ShootingHitEffector.h create mode 100644 src/xrGameLA/ShootingObject.cpp create mode 100644 src/xrGameLA/ShootingObject.h create mode 100644 src/xrGameLA/Silencer.cpp create mode 100644 src/xrGameLA/Silencer.h create mode 100644 src/xrGameLA/SimpleDetector.cpp create mode 100644 src/xrGameLA/SimpleDetector.h create mode 100644 src/xrGameLA/SimpleDetector2.cpp create mode 100644 src/xrGameLA/SimpleDetector2.h create mode 100644 src/xrGameLA/SleepEffector.cpp create mode 100644 src/xrGameLA/SleepEffector.h create mode 100644 src/xrGameLA/SpaceUtils.h create mode 100644 src/xrGameLA/StalkerOutfit.cpp create mode 100644 src/xrGameLA/StalkerOutfit.h create mode 100644 src/xrGameLA/StdAfx.cpp create mode 100644 src/xrGameLA/StdAfx.h create mode 100644 src/xrGameLA/Store.cpp create mode 100644 src/xrGameLA/Store.h create mode 100644 src/xrGameLA/Store_script.cpp create mode 100644 src/xrGameLA/TeleWhirlwind.cpp create mode 100644 src/xrGameLA/TeleWhirlwind.h create mode 100644 src/xrGameLA/ThornArtifact.cpp create mode 100644 src/xrGameLA/ThornArtifact.h create mode 100644 src/xrGameLA/Torch.cpp create mode 100644 src/xrGameLA/Torch.h create mode 100644 src/xrGameLA/TorridZone.cpp create mode 100644 src/xrGameLA/TorridZone.h create mode 100644 src/xrGameLA/Tracer.cpp create mode 100644 src/xrGameLA/Tracer.h create mode 100644 src/xrGameLA/UIActorStateIcons.cpp create mode 100644 src/xrGameLA/UIActorStateIcons.h create mode 100644 src/xrGameLA/UICursor.cpp create mode 100644 src/xrGameLA/UICursor.h create mode 100644 src/xrGameLA/UIDialogHolder.cpp create mode 100644 src/xrGameLA/UIDialogHolder.h create mode 100644 src/xrGameLA/UIGameClock.cpp create mode 100644 src/xrGameLA/UIGameClock.h create mode 100644 src/xrGameLA/UIGameCustom.cpp create mode 100644 src/xrGameLA/UIGameCustom.h create mode 100644 src/xrGameLA/UIGameCustom_script.cpp create mode 100644 src/xrGameLA/UIGameSP.cpp create mode 100644 src/xrGameLA/UIGameSP.h create mode 100644 src/xrGameLA/UIGameTDM.cpp create mode 100644 src/xrGameLA/UIGameTDM.h create mode 100644 src/xrGameLA/UIGame_custom_script.cpp create mode 100644 src/xrGameLA/UIGame_custom_script.h create mode 100644 src/xrGameLA/UIStaticItem.cpp create mode 100644 src/xrGameLA/UIStaticItem.h create mode 100644 src/xrGameLA/UIZoneMap.cpp create mode 100644 src/xrGameLA/UIZoneMap.h create mode 100644 src/xrGameLA/UsableScriptObject.cpp create mode 100644 src/xrGameLA/UsableScriptObject.h create mode 100644 src/xrGameLA/WaveForm.h create mode 100644 src/xrGameLA/Weapon.cpp create mode 100644 src/xrGameLA/Weapon.h create mode 100644 src/xrGameLA/WeaponAK74.cpp create mode 100644 src/xrGameLA/WeaponAK74.h create mode 100644 src/xrGameLA/WeaponAmmo.cpp create mode 100644 src/xrGameLA/WeaponAmmo.h create mode 100644 src/xrGameLA/WeaponAutomaticShotgun.cpp create mode 100644 src/xrGameLA/WeaponAutomaticShotgun.h create mode 100644 src/xrGameLA/WeaponBinoculars.cpp create mode 100644 src/xrGameLA/WeaponBinoculars.h create mode 100644 src/xrGameLA/WeaponBinocularsVision.cpp create mode 100644 src/xrGameLA/WeaponBinocularsVision.h create mode 100644 src/xrGameLA/WeaponBinoculars_script.cpp create mode 100644 src/xrGameLA/WeaponCustomPistol.cpp create mode 100644 src/xrGameLA/WeaponCustomPistol.h create mode 100644 src/xrGameLA/WeaponDispersion.cpp create mode 100644 src/xrGameLA/WeaponFN2000.cpp create mode 100644 src/xrGameLA/WeaponFN2000.h create mode 100644 src/xrGameLA/WeaponFORT.cpp create mode 100644 src/xrGameLA/WeaponFORT.h create mode 100644 src/xrGameLA/WeaponFire.cpp create mode 100644 src/xrGameLA/WeaponGroza.cpp create mode 100644 src/xrGameLA/WeaponGroza.h create mode 100644 src/xrGameLA/WeaponHPSA.cpp create mode 100644 src/xrGameLA/WeaponHPSA.h create mode 100644 src/xrGameLA/WeaponHPSA_script.cpp create mode 100644 src/xrGameLA/WeaponHUD.cpp create mode 100644 src/xrGameLA/WeaponHUD.h create mode 100644 src/xrGameLA/WeaponKnife.cpp create mode 100644 src/xrGameLA/WeaponKnife.h create mode 100644 src/xrGameLA/WeaponKnife_script.cpp create mode 100644 src/xrGameLA/WeaponLR300.cpp create mode 100644 src/xrGameLA/WeaponLR300.h create mode 100644 src/xrGameLA/WeaponMagazined.cpp create mode 100644 src/xrGameLA/WeaponMagazined.h create mode 100644 src/xrGameLA/WeaponMagazinedWGrenade.cpp create mode 100644 src/xrGameLA/WeaponMagazinedWGrenade.h create mode 100644 src/xrGameLA/WeaponMounted.cpp create mode 100644 src/xrGameLA/WeaponMounted.h create mode 100644 src/xrGameLA/WeaponPM.cpp create mode 100644 src/xrGameLA/WeaponPM.h create mode 100644 src/xrGameLA/WeaponPistol.cpp create mode 100644 src/xrGameLA/WeaponPistol.h create mode 100644 src/xrGameLA/WeaponRG6.cpp create mode 100644 src/xrGameLA/WeaponRG6.h create mode 100644 src/xrGameLA/WeaponRG6_script.cpp create mode 100644 src/xrGameLA/WeaponRPG7.cpp create mode 100644 src/xrGameLA/WeaponRPG7.h create mode 100644 src/xrGameLA/WeaponRPG7_script.cpp create mode 100644 src/xrGameLA/WeaponSVD.cpp create mode 100644 src/xrGameLA/WeaponSVD.h create mode 100644 src/xrGameLA/WeaponSVU.cpp create mode 100644 src/xrGameLA/WeaponSVU.h create mode 100644 src/xrGameLA/WeaponShotgun.cpp create mode 100644 src/xrGameLA/WeaponShotgun.h create mode 100644 src/xrGameLA/WeaponStatMgun.cpp create mode 100644 src/xrGameLA/WeaponStatMgun.h create mode 100644 src/xrGameLA/WeaponStatMgunFire.cpp create mode 100644 src/xrGameLA/WeaponStatMgunIR.cpp create mode 100644 src/xrGameLA/WeaponUSP45.cpp create mode 100644 src/xrGameLA/WeaponUSP45.h create mode 100644 src/xrGameLA/WeaponUpgrade.cpp create mode 100644 src/xrGameLA/WeaponVal.cpp create mode 100644 src/xrGameLA/WeaponVal.h create mode 100644 src/xrGameLA/WeaponVintorez.cpp create mode 100644 src/xrGameLA/WeaponVintorez.h create mode 100644 src/xrGameLA/WeaponWalther.cpp create mode 100644 src/xrGameLA/WeaponWalther.h create mode 100644 src/xrGameLA/WeaponZoomable.cpp create mode 100644 src/xrGameLA/WeaponZoomable.h create mode 100644 src/xrGameLA/WeaponZoomable_script.cpp create mode 100644 src/xrGameLA/Wound.cpp create mode 100644 src/xrGameLA/Wound.h create mode 100644 src/xrGameLA/ZoneCampfire.cpp create mode 100644 src/xrGameLA/ZoneCampfire.h create mode 100644 src/xrGameLA/ZoneGalantine.cpp create mode 100644 src/xrGameLA/ZoneGalantine.h create mode 100644 src/xrGameLA/ZoneMine.cpp create mode 100644 src/xrGameLA/ZoneMine.h create mode 100644 src/xrGameLA/ZoneVisual.cpp create mode 100644 src/xrGameLA/ZoneVisual.h create mode 100644 src/xrGameLA/ZudaArtifact.cpp create mode 100644 src/xrGameLA/ZudaArtifact.h create mode 100644 src/xrGameLA/a_star.h create mode 100644 src/xrGameLA/a_star_inline.h create mode 100644 src/xrGameLA/abstract_location_selector.h create mode 100644 src/xrGameLA/abstract_location_selector_inline.h create mode 100644 src/xrGameLA/abstract_path_manager.h create mode 100644 src/xrGameLA/abstract_path_manager_inline.h create mode 100644 src/xrGameLA/action_base.h create mode 100644 src/xrGameLA/action_base_inline.h create mode 100644 src/xrGameLA/action_base_script.cpp create mode 100644 src/xrGameLA/action_management_config.h create mode 100644 src/xrGameLA/action_planner.h create mode 100644 src/xrGameLA/action_planner_action.h create mode 100644 src/xrGameLA/action_planner_action_inline.h create mode 100644 src/xrGameLA/action_planner_action_script.cpp create mode 100644 src/xrGameLA/action_planner_action_script.h create mode 100644 src/xrGameLA/action_planner_action_script_inline.h create mode 100644 src/xrGameLA/action_planner_inline.h create mode 100644 src/xrGameLA/action_planner_script.cpp create mode 100644 src/xrGameLA/action_planner_script.h create mode 100644 src/xrGameLA/action_planner_script_inline.h create mode 100644 src/xrGameLA/action_script_base.h create mode 100644 src/xrGameLA/action_script_base_inline.h create mode 100644 src/xrGameLA/actor_anim_defs.h create mode 100644 src/xrGameLA/actor_communication.cpp create mode 100644 src/xrGameLA/actor_defs.h create mode 100644 src/xrGameLA/actor_input_handler.cpp create mode 100644 src/xrGameLA/actor_input_handler.h create mode 100644 src/xrGameLA/actor_memory.cpp create mode 100644 src/xrGameLA/actor_memory.h create mode 100644 src/xrGameLA/actor_mp_client.cpp create mode 100644 src/xrGameLA/actor_mp_client.h create mode 100644 src/xrGameLA/actor_mp_client_export.cpp create mode 100644 src/xrGameLA/actor_mp_client_import.cpp create mode 100644 src/xrGameLA/actor_mp_server.cpp create mode 100644 src/xrGameLA/actor_mp_server.h create mode 100644 src/xrGameLA/actor_mp_server_export.cpp create mode 100644 src/xrGameLA/actor_mp_server_import.cpp create mode 100644 src/xrGameLA/actor_mp_state.cpp create mode 100644 src/xrGameLA/actor_mp_state.h create mode 100644 src/xrGameLA/actor_mp_state_inline.h create mode 100644 src/xrGameLA/actor_statistic_defs.h create mode 100644 src/xrGameLA/actor_statistic_mgr.cpp create mode 100644 src/xrGameLA/actor_statistic_mgr.h create mode 100644 src/xrGameLA/agent_corpse_manager.cpp create mode 100644 src/xrGameLA/agent_corpse_manager.h create mode 100644 src/xrGameLA/agent_corpse_manager_inline.h create mode 100644 src/xrGameLA/agent_enemy_manager.cpp create mode 100644 src/xrGameLA/agent_enemy_manager.h create mode 100644 src/xrGameLA/agent_enemy_manager_inline.h create mode 100644 src/xrGameLA/agent_explosive_manager.cpp create mode 100644 src/xrGameLA/agent_explosive_manager.h create mode 100644 src/xrGameLA/agent_explosive_manager_inline.h create mode 100644 src/xrGameLA/agent_location_manager.cpp create mode 100644 src/xrGameLA/agent_location_manager.h create mode 100644 src/xrGameLA/agent_location_manager_inline.h create mode 100644 src/xrGameLA/agent_manager.cpp create mode 100644 src/xrGameLA/agent_manager.h create mode 100644 src/xrGameLA/agent_manager_actions.cpp create mode 100644 src/xrGameLA/agent_manager_actions.h create mode 100644 src/xrGameLA/agent_manager_inline.h create mode 100644 src/xrGameLA/agent_manager_planner.cpp create mode 100644 src/xrGameLA/agent_manager_planner.h create mode 100644 src/xrGameLA/agent_manager_properties.cpp create mode 100644 src/xrGameLA/agent_manager_properties.h create mode 100644 src/xrGameLA/agent_manager_properties_inline.h create mode 100644 src/xrGameLA/agent_manager_space.h create mode 100644 src/xrGameLA/agent_member_manager.cpp create mode 100644 src/xrGameLA/agent_member_manager.h create mode 100644 src/xrGameLA/agent_member_manager_inline.h create mode 100644 src/xrGameLA/agent_memory_manager.cpp create mode 100644 src/xrGameLA/agent_memory_manager.h create mode 100644 src/xrGameLA/agent_memory_manager_inline.h create mode 100644 src/xrGameLA/ai/ai_monsters_anims.h create mode 100644 src/xrGameLA/ai/ai_monsters_misc.cpp create mode 100644 src/xrGameLA/ai/ai_monsters_misc.h create mode 100644 src/xrGameLA/ai/crow/ai_crow.cpp create mode 100644 src/xrGameLA/ai/crow/ai_crow.h create mode 100644 src/xrGameLA/ai/monsters/ai_monster_bones.cpp create mode 100644 src/xrGameLA/ai/monsters/ai_monster_bones.h create mode 100644 src/xrGameLA/ai/monsters/ai_monster_defs.h create mode 100644 src/xrGameLA/ai/monsters/ai_monster_effector.cpp create mode 100644 src/xrGameLA/ai/monsters/ai_monster_effector.h create mode 100644 src/xrGameLA/ai/monsters/ai_monster_motion_stats.cpp create mode 100644 src/xrGameLA/ai/monsters/ai_monster_motion_stats.h create mode 100644 src/xrGameLA/ai/monsters/ai_monster_shared_data.h create mode 100644 src/xrGameLA/ai/monsters/ai_monster_squad.cpp create mode 100644 src/xrGameLA/ai/monsters/ai_monster_squad.h create mode 100644 src/xrGameLA/ai/monsters/ai_monster_squad_attack.cpp create mode 100644 src/xrGameLA/ai/monsters/ai_monster_squad_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/ai_monster_squad_manager.h create mode 100644 src/xrGameLA/ai/monsters/ai_monster_squad_manager_inline.h create mode 100644 src/xrGameLA/ai/monsters/ai_monster_squad_rest.cpp create mode 100644 src/xrGameLA/ai/monsters/ai_monster_utils.cpp create mode 100644 src/xrGameLA/ai/monsters/ai_monster_utils.h create mode 100644 src/xrGameLA/ai/monsters/anim_triple.cpp create mode 100644 src/xrGameLA/ai/monsters/anim_triple.h create mode 100644 src/xrGameLA/ai/monsters/anomaly_detector.cpp create mode 100644 src/xrGameLA/ai/monsters/anomaly_detector.h create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster.cpp create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster.h create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster_anim.cpp create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster_debug.cpp create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster_feel.cpp create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster_inline.h create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster_misc.cpp create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster_net.cpp create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster_path.cpp create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster_script.cpp create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster_startup.cpp create mode 100644 src/xrGameLA/ai/monsters/basemonster/base_monster_think.cpp create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker.cpp create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_alien.cpp create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_alien.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_hide.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_hide_inline.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_inline.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_inline.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_lite.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_lite_inline.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_script.cpp create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_approach.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_approach_inline.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_effector.cpp create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_effector.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_execute.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_execute_inline.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_hide.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_hide_inline.h create mode 100644 src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_inline.h create mode 100644 src/xrGameLA/ai/monsters/boar/boar.cpp create mode 100644 src/xrGameLA/ai/monsters/boar/boar.h create mode 100644 src/xrGameLA/ai/monsters/boar/boar_script.cpp create mode 100644 src/xrGameLA/ai/monsters/boar/boar_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/boar/boar_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer.cpp create mode 100644 src/xrGameLA/ai/monsters/burer/burer.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_fast_gravi.cpp create mode 100644 src/xrGameLA/ai/monsters/burer/burer_fast_gravi.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_script.cpp create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_gravi.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_gravi_inline.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_inline.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_melee.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_melee_inline.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_run_around.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_run_around_inline.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_shield.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_shield_inline.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_tele.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_attack_tele_inline.h create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/burer/burer_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/cat/cat.cpp create mode 100644 src/xrGameLA/ai/monsters/cat/cat.h create mode 100644 src/xrGameLA/ai/monsters/cat/cat_script.cpp create mode 100644 src/xrGameLA/ai/monsters/cat/cat_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/cat/cat_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera.cpp create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_script.cpp create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_hunting.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_come_out.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_come_out_inline.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_inline.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_move_to_cover.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_move_to_cover_inline.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_threaten.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_inline.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_roar.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_roar_inline.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_steal.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_steal_inline.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_walk.h create mode 100644 src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_walk_inline.h create mode 100644 src/xrGameLA/ai/monsters/control_animation.cpp create mode 100644 src/xrGameLA/ai/monsters/control_animation.h create mode 100644 src/xrGameLA/ai/monsters/control_animation_base.cpp create mode 100644 src/xrGameLA/ai/monsters/control_animation_base.h create mode 100644 src/xrGameLA/ai/monsters/control_animation_base_accel.cpp create mode 100644 src/xrGameLA/ai/monsters/control_animation_base_load.cpp create mode 100644 src/xrGameLA/ai/monsters/control_animation_base_update.cpp create mode 100644 src/xrGameLA/ai/monsters/control_com_defs.h create mode 100644 src/xrGameLA/ai/monsters/control_combase.h create mode 100644 src/xrGameLA/ai/monsters/control_critical_wound.cpp create mode 100644 src/xrGameLA/ai/monsters/control_critical_wound.h create mode 100644 src/xrGameLA/ai/monsters/control_direction.cpp create mode 100644 src/xrGameLA/ai/monsters/control_direction.h create mode 100644 src/xrGameLA/ai/monsters/control_direction_base.cpp create mode 100644 src/xrGameLA/ai/monsters/control_direction_base.h create mode 100644 src/xrGameLA/ai/monsters/control_jump.cpp create mode 100644 src/xrGameLA/ai/monsters/control_jump.h create mode 100644 src/xrGameLA/ai/monsters/control_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/control_manager.h create mode 100644 src/xrGameLA/ai/monsters/control_manager_custom.cpp create mode 100644 src/xrGameLA/ai/monsters/control_manager_custom.h create mode 100644 src/xrGameLA/ai/monsters/control_melee_jump.cpp create mode 100644 src/xrGameLA/ai/monsters/control_melee_jump.h create mode 100644 src/xrGameLA/ai/monsters/control_movement.cpp create mode 100644 src/xrGameLA/ai/monsters/control_movement.h create mode 100644 src/xrGameLA/ai/monsters/control_movement_base.cpp create mode 100644 src/xrGameLA/ai/monsters/control_movement_base.h create mode 100644 src/xrGameLA/ai/monsters/control_path_builder.cpp create mode 100644 src/xrGameLA/ai/monsters/control_path_builder.h create mode 100644 src/xrGameLA/ai/monsters/control_path_builder_base.cpp create mode 100644 src/xrGameLA/ai/monsters/control_path_builder_base.h create mode 100644 src/xrGameLA/ai/monsters/control_path_builder_base_inline.h create mode 100644 src/xrGameLA/ai/monsters/control_path_builder_base_path.cpp create mode 100644 src/xrGameLA/ai/monsters/control_path_builder_base_set.cpp create mode 100644 src/xrGameLA/ai/monsters/control_path_builder_base_update.cpp create mode 100644 src/xrGameLA/ai/monsters/control_rotation_jump.cpp create mode 100644 src/xrGameLA/ai/monsters/control_rotation_jump.h create mode 100644 src/xrGameLA/ai/monsters/control_run_attack.cpp create mode 100644 src/xrGameLA/ai/monsters/control_run_attack.h create mode 100644 src/xrGameLA/ai/monsters/control_sequencer.cpp create mode 100644 src/xrGameLA/ai/monsters/control_sequencer.h create mode 100644 src/xrGameLA/ai/monsters/control_threaten.cpp create mode 100644 src/xrGameLA/ai/monsters/control_threaten.h create mode 100644 src/xrGameLA/ai/monsters/controlled_actor.cpp create mode 100644 src/xrGameLA/ai/monsters/controlled_actor.h create mode 100644 src/xrGameLA/ai/monsters/controlled_entity.h create mode 100644 src/xrGameLA/ai/monsters/controlled_entity_inline.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller.cpp create mode 100644 src/xrGameLA/ai/monsters/controller/controller.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_animation.cpp create mode 100644 src/xrGameLA/ai/monsters/controller/controller_animation.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_direction.cpp create mode 100644 src/xrGameLA/ai/monsters/controller/controller_direction.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_psy_aura.cpp create mode 100644 src/xrGameLA/ai/monsters/controller/controller_psy_aura.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_psy_hit.cpp create mode 100644 src/xrGameLA/ai/monsters/controller/controller_psy_hit.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_psy_hit_effector.cpp create mode 100644 src/xrGameLA/ai/monsters/controller/controller_psy_hit_effector.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_script.cpp create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_camp.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_camp_inline.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_fast_move.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_fast_move_inline.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_fire.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_fire_inline.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_hide.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_inline.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_lite.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_lite_inline.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_inline.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_moveout.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_attack_moveout_inline.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_control_hit.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_control_hit_inline.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_state_panic.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_tube.h create mode 100644 src/xrGameLA/ai/monsters/controller/controller_tube_inline.h create mode 100644 src/xrGameLA/ai/monsters/corpse_cover.cpp create mode 100644 src/xrGameLA/ai/monsters/corpse_cover.h create mode 100644 src/xrGameLA/ai/monsters/custom_events.h create mode 100644 src/xrGameLA/ai/monsters/dog/dog.cpp create mode 100644 src/xrGameLA/ai/monsters/dog/dog.h create mode 100644 src/xrGameLA/ai/monsters/dog/dog_script.cpp create mode 100644 src/xrGameLA/ai/monsters/dog/dog_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/dog/dog_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/energy_holder.cpp create mode 100644 src/xrGameLA/ai/monsters/energy_holder.h create mode 100644 src/xrGameLA/ai/monsters/flesh/flesh.cpp create mode 100644 src/xrGameLA/ai/monsters/flesh/flesh.h create mode 100644 src/xrGameLA/ai/monsters/flesh/flesh_script.cpp create mode 100644 src/xrGameLA/ai/monsters/flesh/flesh_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/flesh/flesh_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/fracture/fracture.cpp create mode 100644 src/xrGameLA/ai/monsters/fracture/fracture.h create mode 100644 src/xrGameLA/ai/monsters/fracture/fracture_script.cpp create mode 100644 src/xrGameLA/ai/monsters/fracture/fracture_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/fracture/fracture_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss.cpp create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_fast_gravi.cpp create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_fast_gravi.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_script.cpp create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_gravi.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_gravi_inline.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_inline.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_melee.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_melee_inline.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_run_around.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_run_around_inline.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_shield.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_shield_inline.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_tele.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_tele_inline.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_teleport.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_teleport_inline.h create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/invisibility.cpp create mode 100644 src/xrGameLA/ai/monsters/invisibility.h create mode 100644 src/xrGameLA/ai/monsters/karlik/karlik.cpp create mode 100644 src/xrGameLA/ai/monsters/karlik/karlik.h create mode 100644 src/xrGameLA/ai/monsters/karlik/karlik_script.cpp create mode 100644 src/xrGameLA/ai/monsters/karlik/karlik_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/karlik/karlik_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/melee_checker.cpp create mode 100644 src/xrGameLA/ai/monsters/melee_checker.h create mode 100644 src/xrGameLA/ai/monsters/melee_checker_inline.h create mode 100644 src/xrGameLA/ai/monsters/monster_corpse_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/monster_corpse_manager.h create mode 100644 src/xrGameLA/ai/monsters/monster_corpse_memory.cpp create mode 100644 src/xrGameLA/ai/monsters/monster_corpse_memory.h create mode 100644 src/xrGameLA/ai/monsters/monster_cover_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/monster_cover_manager.h create mode 100644 src/xrGameLA/ai/monsters/monster_enemy_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/monster_enemy_manager.h create mode 100644 src/xrGameLA/ai/monsters/monster_enemy_memory.cpp create mode 100644 src/xrGameLA/ai/monsters/monster_enemy_memory.h create mode 100644 src/xrGameLA/ai/monsters/monster_event_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/monster_event_manager.h create mode 100644 src/xrGameLA/ai/monsters/monster_event_manager_defs.h create mode 100644 src/xrGameLA/ai/monsters/monster_hit_memory.cpp create mode 100644 src/xrGameLA/ai/monsters/monster_hit_memory.h create mode 100644 src/xrGameLA/ai/monsters/monster_home.cpp create mode 100644 src/xrGameLA/ai/monsters/monster_home.h create mode 100644 src/xrGameLA/ai/monsters/monster_morale.cpp create mode 100644 src/xrGameLA/ai/monsters/monster_morale.h create mode 100644 src/xrGameLA/ai/monsters/monster_morale_inline.h create mode 100644 src/xrGameLA/ai/monsters/monster_sound_defs.h create mode 100644 src/xrGameLA/ai/monsters/monster_sound_memory.cpp create mode 100644 src/xrGameLA/ai/monsters/monster_sound_memory.h create mode 100644 src/xrGameLA/ai/monsters/monster_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/monster_state_manager_inline.h create mode 100644 src/xrGameLA/ai/monsters/monster_velocity_space.h create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist.cpp create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist.h create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_ability.cpp create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_flame_thrower.cpp create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_movement.cpp create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_movement.h create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_script.cpp create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_attack_hidden.h create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_attack_hidden_inline.h create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_rest.h create mode 100644 src/xrGameLA/ai/monsters/poltergeist/poltergeist_telekinesis.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudodog/pseudodog.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudodog/pseudodog.h create mode 100644 src/xrGameLA/ai/monsters/pseudodog/pseudodog_psi_effector.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudodog/pseudodog_psi_effector.h create mode 100644 src/xrGameLA/ai/monsters/pseudodog/pseudodog_script.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudodog/pseudodog_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudodog/pseudodog_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/pseudodog/psy_dog.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudodog/psy_dog.h create mode 100644 src/xrGameLA/ai/monsters/pseudodog/psy_dog_aura.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudodog/psy_dog_aura.h create mode 100644 src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack.h create mode 100644 src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_hide.h create mode 100644 src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_hide_inline.h create mode 100644 src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_inline.h create mode 100644 src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant.h create mode 100644 src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant_step_effector.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant_step_effector.h create mode 100644 src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_script.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/psy_aura.cpp create mode 100644 src/xrGameLA/ai/monsters/psy_aura.h create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat.cpp create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat.h create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat_animations.cpp create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat_behaviour.cpp create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat_feel.cpp create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat_fire.cpp create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat_fsm.cpp create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat_impl.h create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat_inline.h create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat_space.h create mode 100644 src/xrGameLA/ai/monsters/rats/ai_rat_templates.cpp create mode 100644 src/xrGameLA/ai/monsters/rats/rat_state_activation.cpp create mode 100644 src/xrGameLA/ai/monsters/rats/rat_state_initialize.cpp create mode 100644 src/xrGameLA/ai/monsters/rats/rat_state_switch.cpp create mode 100644 src/xrGameLA/ai/monsters/scanning_ability.h create mode 100644 src/xrGameLA/ai/monsters/scanning_ability_inline.h create mode 100644 src/xrGameLA/ai/monsters/snork/snork.cpp create mode 100644 src/xrGameLA/ai/monsters/snork/snork.h create mode 100644 src/xrGameLA/ai/monsters/snork/snork_jump.cpp create mode 100644 src/xrGameLA/ai/monsters/snork/snork_jump.h create mode 100644 src/xrGameLA/ai/monsters/snork/snork_script.cpp create mode 100644 src/xrGameLA/ai/monsters/snork/snork_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/snork/snork_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/state.h create mode 100644 src/xrGameLA/ai/monsters/state_defs.h create mode 100644 src/xrGameLA/ai/monsters/state_inline.h create mode 100644 src/xrGameLA/ai/monsters/state_manager.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_camp.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_camp_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_camp_stealout.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_camp_stealout_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_melee.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_melee_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_run.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_run_attack.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_run_attack_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_attack_run_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_controlled.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_controlled_attack.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_controlled_attack_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_controlled_follow.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_controlled_follow_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_controlled_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_eat.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_eat_drag.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_eat_drag_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_eat_eat.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_eat_eat_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_eat_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_find_enemy.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_find_enemy_angry.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_find_enemy_angry_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_find_enemy_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_find_enemy_look.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_find_enemy_look_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_find_enemy_run.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_find_enemy_run_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_find_enemy_walk.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_find_enemy_walk_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_hear_danger_sound.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_hear_danger_sound_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_hear_int_sound.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_hear_int_sound_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_help_sound.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_help_sound_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_hitted.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_hitted_hide.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_hitted_hide_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_hitted_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_hitted_moveout.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_hitted_moveout_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_home_point_attack.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_home_point_attack_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_home_point_danger.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_home_point_danger_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_home_point_rest.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_home_point_rest_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_panic.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_panic_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_panic_run.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_panic_run_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_rest.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_rest_fun.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_rest_fun_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_rest_idle.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_rest_idle_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_rest_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_rest_sleep.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_rest_sleep_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_rest_walk_graph.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_rest_walk_graph_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_graph_walk.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_graph_walk_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_squad_rest.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_squad_rest_follow.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_squad_rest_follow_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_squad_rest_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_steal.h create mode 100644 src/xrGameLA/ai/monsters/states/monster_state_steal_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_custom_action.h create mode 100644 src/xrGameLA/ai/monsters/states/state_custom_action_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_custom_action_look.h create mode 100644 src/xrGameLA/ai/monsters/states/state_custom_action_look_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_data.h create mode 100644 src/xrGameLA/ai/monsters/states/state_hide_from_point.h create mode 100644 src/xrGameLA/ai/monsters/states/state_hide_from_point_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_hit_object.h create mode 100644 src/xrGameLA/ai/monsters/states/state_hit_object_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_look_point.h create mode 100644 src/xrGameLA/ai/monsters/states/state_look_point_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_look_unprotected_area.h create mode 100644 src/xrGameLA/ai/monsters/states/state_look_unprotected_area_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_move_around_point.h create mode 100644 src/xrGameLA/ai/monsters/states/state_move_around_point_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_move_to_point.h create mode 100644 src/xrGameLA/ai/monsters/states/state_move_to_point_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_move_to_restrictor.h create mode 100644 src/xrGameLA/ai/monsters/states/state_move_to_restrictor_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_test_look_actor.h create mode 100644 src/xrGameLA/ai/monsters/states/state_test_look_actor_inline.h create mode 100644 src/xrGameLA/ai/monsters/states/state_test_state.h create mode 100644 src/xrGameLA/ai/monsters/states/state_test_state_inline.h create mode 100644 src/xrGameLA/ai/monsters/swampbeast/swampbeast.cpp create mode 100644 src/xrGameLA/ai/monsters/swampbeast/swampbeast.h create mode 100644 src/xrGameLA/ai/monsters/swampbeast/swampbeast_script.cpp create mode 100644 src/xrGameLA/ai/monsters/swampbeast/swampbeast_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/swampbeast/swampbeast_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/telekinesis.cpp create mode 100644 src/xrGameLA/ai/monsters/telekinesis.h create mode 100644 src/xrGameLA/ai/monsters/telekinesis_inline.h create mode 100644 src/xrGameLA/ai/monsters/telekinetic_object.cpp create mode 100644 src/xrGameLA/ai/monsters/telekinetic_object.h create mode 100644 src/xrGameLA/ai/monsters/tushkano/tushkano.cpp create mode 100644 src/xrGameLA/ai/monsters/tushkano/tushkano.h create mode 100644 src/xrGameLA/ai/monsters/tushkano/tushkano_script.cpp create mode 100644 src/xrGameLA/ai/monsters/tushkano/tushkano_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/tushkano/tushkano_state_manager.h create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie.cpp create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie.h create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_choke.h create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_choke_effector.cpp create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_choke_effector.h create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_choke_execute.h create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_choke_execute_inline.h create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_choke_inline.h create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_script.cpp create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_state_attack_run.h create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_state_attack_run_inline.h create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_state_manager.cpp create mode 100644 src/xrGameLA/ai/monsters/zombie/zombie_state_manager.h create mode 100644 src/xrGameLA/ai/phantom/phantom.cpp create mode 100644 src/xrGameLA/ai/phantom/phantom.h create mode 100644 src/xrGameLA/ai/rats/ai_rat.cpp create mode 100644 src/xrGameLA/ai/rats/ai_rat_animations.cpp create mode 100644 src/xrGameLA/ai/rats/ai_rat_behaviour.cpp create mode 100644 src/xrGameLA/ai/rats/ai_rat_feel.cpp create mode 100644 src/xrGameLA/ai/rats/ai_rat_fire.cpp create mode 100644 src/xrGameLA/ai/rats/ai_rat_fsm.cpp create mode 100644 src/xrGameLA/ai/rats/ai_rat_templates.cpp create mode 100644 src/xrGameLA/ai/rats/rat_state_activation.cpp create mode 100644 src/xrGameLA/ai/rats/rat_state_initialize.cpp create mode 100644 src/xrGameLA/ai/rats/rat_state_switch.cpp create mode 100644 src/xrGameLA/ai/stalker/ai_stalker.cpp create mode 100644 src/xrGameLA/ai/stalker/ai_stalker.h create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_cover.cpp create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_debug.cpp create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_events.cpp create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_feel.cpp create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_fire.cpp create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_impl.h create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_inline.h create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_misc.cpp create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_script.cpp create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_script_entity.cpp create mode 100644 src/xrGameLA/ai/stalker/ai_stalker_space.h create mode 100644 src/xrGameLA/ai/trader/ai_trader.cpp create mode 100644 src/xrGameLA/ai/trader/ai_trader.h create mode 100644 src/xrGameLA/ai/trader/ai_trader_script.cpp create mode 100644 src/xrGameLA/ai/trader/trader_animation.cpp create mode 100644 src/xrGameLA/ai/trader/trader_animation.h create mode 100644 src/xrGameLA/ai_debug.h create mode 100644 src/xrGameLA/ai_monster_space.h create mode 100644 src/xrGameLA/ai_object_location.h create mode 100644 src/xrGameLA/ai_object_location_impl.h create mode 100644 src/xrGameLA/ai_object_location_inline.h create mode 100644 src/xrGameLA/ai_sounds.cpp create mode 100644 src/xrGameLA/ai_sounds.h create mode 100644 src/xrGameLA/ai_space.cpp create mode 100644 src/xrGameLA/ai_space.h create mode 100644 src/xrGameLA/ai_space_inline.h create mode 100644 src/xrGameLA/ai_stalker_alife.cpp create mode 100644 src/xrGameLA/alife_abstract_registry.h create mode 100644 src/xrGameLA/alife_abstract_registry_inline.h create mode 100644 src/xrGameLA/alife_anomalous_zone.cpp create mode 100644 src/xrGameLA/alife_combat_manager.cpp create mode 100644 src/xrGameLA/alife_combat_manager.h create mode 100644 src/xrGameLA/alife_combat_manager_inline.h create mode 100644 src/xrGameLA/alife_communication_manager.cpp create mode 100644 src/xrGameLA/alife_communication_manager.h create mode 100644 src/xrGameLA/alife_communication_manager_inline.h create mode 100644 src/xrGameLA/alife_communication_space.h create mode 100644 src/xrGameLA/alife_creature_abstract.cpp create mode 100644 src/xrGameLA/alife_dynamic_object.cpp create mode 100644 src/xrGameLA/alife_graph_registry.cpp create mode 100644 src/xrGameLA/alife_graph_registry.h create mode 100644 src/xrGameLA/alife_graph_registry_inline.h create mode 100644 src/xrGameLA/alife_group_abstract.cpp create mode 100644 src/xrGameLA/alife_group_registry.cpp create mode 100644 src/xrGameLA/alife_group_registry.h create mode 100644 src/xrGameLA/alife_group_registry_inline.h create mode 100644 src/xrGameLA/alife_human_abstract.cpp create mode 100644 src/xrGameLA/alife_human_brain.cpp create mode 100644 src/xrGameLA/alife_human_brain.h create mode 100644 src/xrGameLA/alife_human_brain_inline.h create mode 100644 src/xrGameLA/alife_human_brain_save.h create mode 100644 src/xrGameLA/alife_human_brain_script.cpp create mode 100644 src/xrGameLA/alife_human_object_handler.cpp create mode 100644 src/xrGameLA/alife_human_object_handler.h create mode 100644 src/xrGameLA/alife_human_object_handler_inline.h create mode 100644 src/xrGameLA/alife_human_object_handler_save.h create mode 100644 src/xrGameLA/alife_interaction_manager.cpp create mode 100644 src/xrGameLA/alife_interaction_manager.h create mode 100644 src/xrGameLA/alife_interaction_manager_inline.h create mode 100644 src/xrGameLA/alife_level_registry.h create mode 100644 src/xrGameLA/alife_level_registry_inline.h create mode 100644 src/xrGameLA/alife_monster_abstract.cpp create mode 100644 src/xrGameLA/alife_monster_base.cpp create mode 100644 src/xrGameLA/alife_monster_brain.cpp create mode 100644 src/xrGameLA/alife_monster_brain.h create mode 100644 src/xrGameLA/alife_monster_brain_inline.h create mode 100644 src/xrGameLA/alife_monster_brain_script.cpp create mode 100644 src/xrGameLA/alife_monster_detail_path_manager.cpp create mode 100644 src/xrGameLA/alife_monster_detail_path_manager.h create mode 100644 src/xrGameLA/alife_monster_detail_path_manager_inline.h create mode 100644 src/xrGameLA/alife_monster_detail_path_manager_script.cpp create mode 100644 src/xrGameLA/alife_monster_movement_manager.cpp create mode 100644 src/xrGameLA/alife_monster_movement_manager.h create mode 100644 src/xrGameLA/alife_monster_movement_manager_inline.h create mode 100644 src/xrGameLA/alife_monster_movement_manager_script.cpp create mode 100644 src/xrGameLA/alife_monster_patrol_path_manager.cpp create mode 100644 src/xrGameLA/alife_monster_patrol_path_manager.h create mode 100644 src/xrGameLA/alife_monster_patrol_path_manager_inline.h create mode 100644 src/xrGameLA/alife_monster_patrol_path_manager_script.cpp create mode 100644 src/xrGameLA/alife_object.cpp create mode 100644 src/xrGameLA/alife_object_registry.cpp create mode 100644 src/xrGameLA/alife_object_registry.h create mode 100644 src/xrGameLA/alife_object_registry_inline.h create mode 100644 src/xrGameLA/alife_online_offline_group.cpp create mode 100644 src/xrGameLA/alife_online_offline_group_brain.cpp create mode 100644 src/xrGameLA/alife_online_offline_group_brain.h create mode 100644 src/xrGameLA/alife_online_offline_group_brain_inline.h create mode 100644 src/xrGameLA/alife_registry_container.cpp create mode 100644 src/xrGameLA/alife_registry_container.h create mode 100644 src/xrGameLA/alife_registry_container_composition.h create mode 100644 src/xrGameLA/alife_registry_container_inline.h create mode 100644 src/xrGameLA/alife_registry_container_space.h create mode 100644 src/xrGameLA/alife_registry_wrapper.h create mode 100644 src/xrGameLA/alife_registry_wrappers.h create mode 100644 src/xrGameLA/alife_schedule_registry.cpp create mode 100644 src/xrGameLA/alife_schedule_registry.h create mode 100644 src/xrGameLA/alife_schedule_registry_inline.h create mode 100644 src/xrGameLA/alife_simulator.cpp create mode 100644 src/xrGameLA/alife_simulator.h create mode 100644 src/xrGameLA/alife_simulator_base.cpp create mode 100644 src/xrGameLA/alife_simulator_base.h create mode 100644 src/xrGameLA/alife_simulator_base2.cpp create mode 100644 src/xrGameLA/alife_simulator_base_inline.h create mode 100644 src/xrGameLA/alife_simulator_header.cpp create mode 100644 src/xrGameLA/alife_simulator_header.h create mode 100644 src/xrGameLA/alife_simulator_header_inline.h create mode 100644 src/xrGameLA/alife_simulator_inline.h create mode 100644 src/xrGameLA/alife_simulator_script.cpp create mode 100644 src/xrGameLA/alife_smart_terrain_registry.cpp create mode 100644 src/xrGameLA/alife_smart_terrain_registry.h create mode 100644 src/xrGameLA/alife_smart_terrain_registry_inline.h create mode 100644 src/xrGameLA/alife_smart_terrain_task.cpp create mode 100644 src/xrGameLA/alife_smart_terrain_task.h create mode 100644 src/xrGameLA/alife_smart_terrain_task_inline.h create mode 100644 src/xrGameLA/alife_smart_terrain_task_script.cpp create mode 100644 src/xrGameLA/alife_smart_zone.cpp create mode 100644 src/xrGameLA/alife_space.cpp create mode 100644 src/xrGameLA/alife_space.h create mode 100644 src/xrGameLA/alife_spawn_registry.cpp create mode 100644 src/xrGameLA/alife_spawn_registry.h create mode 100644 src/xrGameLA/alife_spawn_registry_header.cpp create mode 100644 src/xrGameLA/alife_spawn_registry_header.h create mode 100644 src/xrGameLA/alife_spawn_registry_header_inline.h create mode 100644 src/xrGameLA/alife_spawn_registry_inline.h create mode 100644 src/xrGameLA/alife_spawn_registry_spawn.cpp create mode 100644 src/xrGameLA/alife_storage_manager.cpp create mode 100644 src/xrGameLA/alife_storage_manager.h create mode 100644 src/xrGameLA/alife_storage_manager_inline.h create mode 100644 src/xrGameLA/alife_story_registry.cpp create mode 100644 src/xrGameLA/alife_story_registry.h create mode 100644 src/xrGameLA/alife_story_registry_inline.h create mode 100644 src/xrGameLA/alife_surge_manager.cpp create mode 100644 src/xrGameLA/alife_surge_manager.h create mode 100644 src/xrGameLA/alife_surge_manager_inline.h create mode 100644 src/xrGameLA/alife_switch_manager.cpp create mode 100644 src/xrGameLA/alife_switch_manager.h create mode 100644 src/xrGameLA/alife_switch_manager_inline.h create mode 100644 src/xrGameLA/alife_time_manager.cpp create mode 100644 src/xrGameLA/alife_time_manager.h create mode 100644 src/xrGameLA/alife_time_manager_inline.h create mode 100644 src/xrGameLA/alife_trader.cpp create mode 100644 src/xrGameLA/alife_trader_abstract.cpp create mode 100644 src/xrGameLA/alife_update_manager.cpp create mode 100644 src/xrGameLA/alife_update_manager.h create mode 100644 src/xrGameLA/alife_update_manager_inline.h create mode 100644 src/xrGameLA/animation_movement_controller.cpp create mode 100644 src/xrGameLA/animation_movement_controller.h create mode 100644 src/xrGameLA/animation_utils.cpp create mode 100644 src/xrGameLA/animation_utils.h create mode 100644 src/xrGameLA/antirad.cpp create mode 100644 src/xrGameLA/antirad.h create mode 100644 src/xrGameLA/artefact_script.cpp create mode 100644 src/xrGameLA/associative_vector.h create mode 100644 src/xrGameLA/associative_vector_compare_predicate.h create mode 100644 src/xrGameLA/associative_vector_compare_predicate_inline.h create mode 100644 src/xrGameLA/associative_vector_inline.h create mode 100644 src/xrGameLA/attachable_item.cpp create mode 100644 src/xrGameLA/attachable_item.h create mode 100644 src/xrGameLA/attachable_item_inline.h create mode 100644 src/xrGameLA/attachment_owner.cpp create mode 100644 src/xrGameLA/attachment_owner.h create mode 100644 src/xrGameLA/attachment_owner_inline.h create mode 100644 src/xrGameLA/autosave_manager.cpp create mode 100644 src/xrGameLA/autosave_manager.h create mode 100644 src/xrGameLA/autosave_manager_inline.h create mode 100644 src/xrGameLA/base_client_classes.h create mode 100644 src/xrGameLA/base_client_classes_script.cpp create mode 100644 src/xrGameLA/base_client_classes_wrappers.h create mode 100644 src/xrGameLA/battery.cpp create mode 100644 src/xrGameLA/battery.h create mode 100644 src/xrGameLA/battleye.h create mode 100644 src/xrGameLA/battleye_system.cpp create mode 100644 src/xrGameLA/battleye_system.h create mode 100644 src/xrGameLA/builder_allocator_constructor.h create mode 100644 src/xrGameLA/builder_allocator_constructor_inline.h create mode 100644 src/xrGameLA/callback_info.h create mode 100644 src/xrGameLA/cameralook.cpp create mode 100644 src/xrGameLA/cameralook.h create mode 100644 src/xrGameLA/car_memory.cpp create mode 100644 src/xrGameLA/car_memory.h create mode 100644 src/xrGameLA/character_community.cpp create mode 100644 src/xrGameLA/character_community.h create mode 100644 src/xrGameLA/character_hit_animations.cpp create mode 100644 src/xrGameLA/character_hit_animations.h create mode 100644 src/xrGameLA/character_hit_animations_params.h create mode 100644 src/xrGameLA/character_info.cpp create mode 100644 src/xrGameLA/character_info.h create mode 100644 src/xrGameLA/character_info_defs.h create mode 100644 src/xrGameLA/character_rank.cpp create mode 100644 src/xrGameLA/character_rank.h create mode 100644 src/xrGameLA/character_reputation.cpp create mode 100644 src/xrGameLA/character_reputation.h create mode 100644 src/xrGameLA/character_supplies.cpp create mode 100644 src/xrGameLA/character_supplies.h create mode 100644 src/xrGameLA/client_spawn_manager.cpp create mode 100644 src/xrGameLA/client_spawn_manager.h create mode 100644 src/xrGameLA/client_spawn_manager_inline.h create mode 100644 src/xrGameLA/client_spawn_manager_script.cpp create mode 100644 src/xrGameLA/clsid_game.h create mode 100644 src/xrGameLA/condition_state.h create mode 100644 src/xrGameLA/condition_state_inline.h create mode 100644 src/xrGameLA/console_commands.cpp create mode 100644 src/xrGameLA/console_registrator.h create mode 100644 src/xrGameLA/console_registrator_script.cpp create mode 100644 src/xrGameLA/control_action.h create mode 100644 src/xrGameLA/control_action_inline.h create mode 100644 src/xrGameLA/controller_state_panic_inline.h create mode 100644 src/xrGameLA/cover_evaluators.cpp create mode 100644 src/xrGameLA/cover_evaluators.h create mode 100644 src/xrGameLA/cover_evaluators_inline.h create mode 100644 src/xrGameLA/cover_manager.cpp create mode 100644 src/xrGameLA/cover_manager.h create mode 100644 src/xrGameLA/cover_manager_inline.h create mode 100644 src/xrGameLA/cover_point.h create mode 100644 src/xrGameLA/cover_point_inline.h create mode 100644 src/xrGameLA/cover_point_script.cpp create mode 100644 src/xrGameLA/dRayMotions.cpp create mode 100644 src/xrGameLA/dRayMotions.h create mode 100644 src/xrGameLA/damage_manager.cpp create mode 100644 src/xrGameLA/damage_manager.h create mode 100644 src/xrGameLA/damage_manager_inline.h create mode 100644 src/xrGameLA/danger_cover_location.cpp create mode 100644 src/xrGameLA/danger_cover_location.h create mode 100644 src/xrGameLA/danger_cover_location_inline.h create mode 100644 src/xrGameLA/danger_explosive.cpp create mode 100644 src/xrGameLA/danger_explosive.h create mode 100644 src/xrGameLA/danger_explosive_inline.h create mode 100644 src/xrGameLA/danger_location.cpp create mode 100644 src/xrGameLA/danger_location.h create mode 100644 src/xrGameLA/danger_location_inline.h create mode 100644 src/xrGameLA/danger_manager.cpp create mode 100644 src/xrGameLA/danger_manager.h create mode 100644 src/xrGameLA/danger_manager_inline.h create mode 100644 src/xrGameLA/danger_object.cpp create mode 100644 src/xrGameLA/danger_object.h create mode 100644 src/xrGameLA/danger_object_inline.h create mode 100644 src/xrGameLA/danger_object_location.cpp create mode 100644 src/xrGameLA/danger_object_location.h create mode 100644 src/xrGameLA/danger_object_location_inline.h create mode 100644 src/xrGameLA/data_storage_binary_heap.h create mode 100644 src/xrGameLA/data_storage_binary_heap_inline.h create mode 100644 src/xrGameLA/data_storage_bucket_list.h create mode 100644 src/xrGameLA/data_storage_bucket_list_inline.h create mode 100644 src/xrGameLA/data_storage_constructor.h create mode 100644 src/xrGameLA/data_storage_double_linked_list.h create mode 100644 src/xrGameLA/data_storage_double_linked_list_inline.h create mode 100644 src/xrGameLA/data_storage_single_linked_list.h create mode 100644 src/xrGameLA/data_storage_single_linked_list_inline.h create mode 100644 src/xrGameLA/date_time.cpp create mode 100644 src/xrGameLA/date_time.h create mode 100644 src/xrGameLA/dbg_draw_frustum.cpp create mode 100644 src/xrGameLA/dcylinder/dCylinder.cpp create mode 100644 src/xrGameLA/dcylinder/dCylinder.h create mode 100644 src/xrGameLA/debug_renderer.cpp create mode 100644 src/xrGameLA/debug_renderer.h create mode 100644 src/xrGameLA/debug_renderer_inline.h create mode 100644 src/xrGameLA/detail_path_builder.h create mode 100644 src/xrGameLA/detail_path_manager.cpp create mode 100644 src/xrGameLA/detail_path_manager.h create mode 100644 src/xrGameLA/detail_path_manager_inline.h create mode 100644 src/xrGameLA/detail_path_manager_smooth.cpp create mode 100644 src/xrGameLA/detail_path_manager_space.h create mode 100644 src/xrGameLA/detectors_script.cpp create mode 100644 src/xrGameLA/dijkstra.h create mode 100644 src/xrGameLA/dijkstra_inline.h create mode 100644 src/xrGameLA/doug_lea_memory_allocator.c create mode 100644 src/xrGameLA/doug_lea_memory_allocator.h create mode 100644 src/xrGameLA/dynamic_patrol_path.cpp create mode 100644 src/xrGameLA/dynamic_patrol_path.h create mode 100644 src/xrGameLA/dynamic_patrol_path_inline.h create mode 100644 src/xrGameLA/dynamic_patrol_path_script.cpp create mode 100644 src/xrGameLA/dynamic_patrol_point.cpp create mode 100644 src/xrGameLA/dynamic_patrol_point.h create mode 100644 src/xrGameLA/dynamic_patrol_point_inline.h create mode 100644 src/xrGameLA/eatable_item.cpp create mode 100644 src/xrGameLA/eatable_item.h create mode 100644 src/xrGameLA/eatable_item_object.cpp create mode 100644 src/xrGameLA/eatable_item_object.h create mode 100644 src/xrGameLA/edge_path.h create mode 100644 src/xrGameLA/edge_path_inline.h create mode 100644 src/xrGameLA/ef_base.h create mode 100644 src/xrGameLA/ef_pattern.cpp create mode 100644 src/xrGameLA/ef_pattern.h create mode 100644 src/xrGameLA/ef_primary.cpp create mode 100644 src/xrGameLA/ef_primary.h create mode 100644 src/xrGameLA/ef_storage.cpp create mode 100644 src/xrGameLA/ef_storage.h create mode 100644 src/xrGameLA/ef_storage_inline.h create mode 100644 src/xrGameLA/ef_storage_script.cpp create mode 100644 src/xrGameLA/encyclopedia_article.cpp create mode 100644 src/xrGameLA/encyclopedia_article.h create mode 100644 src/xrGameLA/encyclopedia_article_defs.h create mode 100644 src/xrGameLA/enemy_manager.cpp create mode 100644 src/xrGameLA/enemy_manager.h create mode 100644 src/xrGameLA/enemy_manager_inline.h create mode 100644 src/xrGameLA/entity_alive.cpp create mode 100644 src/xrGameLA/entity_alive.h create mode 100644 src/xrGameLA/entity_alive_inline.h create mode 100644 src/xrGameLA/fast_entity_update.cpp create mode 100644 src/xrGameLA/fast_entity_update.h create mode 100644 src/xrGameLA/file_transfer.cpp create mode 100644 src/xrGameLA/file_transfer.h create mode 100644 src/xrGameLA/filereceiver_node.cpp create mode 100644 src/xrGameLA/filereceiver_node.h create mode 100644 src/xrGameLA/filetransfer_common.h create mode 100644 src/xrGameLA/filetransfer_node.cpp create mode 100644 src/xrGameLA/filetransfer_node.h create mode 100644 src/xrGameLA/firedeps.h create mode 100644 src/xrGameLA/fs_registrator.h create mode 100644 src/xrGameLA/fs_registrator_script.cpp create mode 100644 src/xrGameLA/game_base.cpp create mode 100644 src/xrGameLA/game_base.h create mode 100644 src/xrGameLA/game_base_kill_type.h create mode 100644 src/xrGameLA/game_base_menu_events.h create mode 100644 src/xrGameLA/game_base_script.cpp create mode 100644 src/xrGameLA/game_base_space.h create mode 100644 src/xrGameLA/game_cl_base.cpp create mode 100644 src/xrGameLA/game_cl_base.h create mode 100644 src/xrGameLA/game_cl_base_script.cpp create mode 100644 src/xrGameLA/game_cl_base_weapon_usage_statistic.cpp create mode 100644 src/xrGameLA/game_cl_base_weapon_usage_statistic.h create mode 100644 src/xrGameLA/game_cl_base_weapon_usage_statistic_save.cpp create mode 100644 src/xrGameLA/game_cl_mp_script.cpp create mode 100644 src/xrGameLA/game_cl_mp_script.h create mode 100644 src/xrGameLA/game_cl_single.cpp create mode 100644 src/xrGameLA/game_cl_single.h create mode 100644 src/xrGameLA/game_cl_teamdeathmatch.cpp create mode 100644 src/xrGameLA/game_cl_teamdeathmatch_snd_messages.h create mode 100644 src/xrGameLA/game_graph.h create mode 100644 src/xrGameLA/game_graph_inline.h create mode 100644 src/xrGameLA/game_graph_script.cpp create mode 100644 src/xrGameLA/game_graph_space.h create mode 100644 src/xrGameLA/game_level_cross_table.h create mode 100644 src/xrGameLA/game_level_cross_table_inline.h create mode 100644 src/xrGameLA/game_location_selector.h create mode 100644 src/xrGameLA/game_location_selector_inline.h create mode 100644 src/xrGameLA/game_news.cpp create mode 100644 src/xrGameLA/game_news.h create mode 100644 src/xrGameLA/game_object_space.h create mode 100644 src/xrGameLA/game_path_manager.h create mode 100644 src/xrGameLA/game_path_manager_inline.h create mode 100644 src/xrGameLA/game_sv_base.cpp create mode 100644 src/xrGameLA/game_sv_base.h create mode 100644 src/xrGameLA/game_sv_base_console_vars.cpp create mode 100644 src/xrGameLA/game_sv_base_console_vars.h create mode 100644 src/xrGameLA/game_sv_base_script.cpp create mode 100644 src/xrGameLA/game_sv_event_queue.cpp create mode 100644 src/xrGameLA/game_sv_event_queue.h create mode 100644 src/xrGameLA/game_sv_single.cpp create mode 100644 src/xrGameLA/game_sv_single.h create mode 100644 src/xrGameLA/graph_abstract.h create mode 100644 src/xrGameLA/graph_abstract_inline.h create mode 100644 src/xrGameLA/graph_edge.h create mode 100644 src/xrGameLA/graph_edge_inline.h create mode 100644 src/xrGameLA/graph_engine.h create mode 100644 src/xrGameLA/graph_engine_inline.h create mode 100644 src/xrGameLA/graph_engine_space.h create mode 100644 src/xrGameLA/graph_vertex.h create mode 100644 src/xrGameLA/graph_vertex_inline.h create mode 100644 src/xrGameLA/group_hierarchy_holder.cpp create mode 100644 src/xrGameLA/group_hierarchy_holder.h create mode 100644 src/xrGameLA/group_hierarchy_holder_inline.h create mode 100644 src/xrGameLA/helicopter.h create mode 100644 src/xrGameLA/helicopter_script.cpp create mode 100644 src/xrGameLA/hit_immunity.cpp create mode 100644 src/xrGameLA/hit_immunity.h create mode 100644 src/xrGameLA/hit_immunity_space.h create mode 100644 src/xrGameLA/hit_memory_manager.cpp create mode 100644 src/xrGameLA/hit_memory_manager.h create mode 100644 src/xrGameLA/hit_memory_manager_inline.h create mode 100644 src/xrGameLA/holder_custom.cpp create mode 100644 src/xrGameLA/holder_custom.h create mode 100644 src/xrGameLA/holder_custom_script.cpp create mode 100644 src/xrGameLA/hud_item_object.cpp create mode 100644 src/xrGameLA/hud_item_object.h create mode 100644 src/xrGameLA/id_generator.h create mode 100644 src/xrGameLA/ik/Dof7control.cpp create mode 100644 src/xrGameLA/ik/Dof7control.h create mode 100644 src/xrGameLA/ik/IKLimb.cpp create mode 100644 src/xrGameLA/ik/IKLimb.h create mode 100644 src/xrGameLA/ik/aint.cxx create mode 100644 src/xrGameLA/ik/aint.h create mode 100644 src/xrGameLA/ik/eqn.cxx create mode 100644 src/xrGameLA/ik/eqn.h create mode 100644 src/xrGameLA/ik/eulersolver.cxx create mode 100644 src/xrGameLA/ik/eulersolver.h create mode 100644 src/xrGameLA/ik/jtlimits.cxx create mode 100644 src/xrGameLA/ik/jtlimits.h create mode 100644 src/xrGameLA/ik/limb.cxx create mode 100644 src/xrGameLA/ik/limb.h create mode 100644 src/xrGameLA/ik/math3d.cpp create mode 100644 src/xrGameLA/ik/math3d.h create mode 100644 src/xrGameLA/ik/mathTrig.cpp create mode 100644 src/xrGameLA/ik/mathTrig.h create mode 100644 src/xrGameLA/ik_anim_state.cpp create mode 100644 src/xrGameLA/ik_anim_state.h create mode 100644 src/xrGameLA/ik_calculate_data.cpp create mode 100644 src/xrGameLA/ik_calculate_data.h create mode 100644 src/xrGameLA/ik_calculate_state.h create mode 100644 src/xrGameLA/ik_collide_data.h create mode 100644 src/xrGameLA/ik_dbg_matrix.cpp create mode 100644 src/xrGameLA/ik_dbg_matrix.h create mode 100644 src/xrGameLA/ik_foot_collider.cpp create mode 100644 src/xrGameLA/ik_foot_collider.h create mode 100644 src/xrGameLA/ik_limb_state.cpp create mode 100644 src/xrGameLA/ik_limb_state.h create mode 100644 src/xrGameLA/ik_limb_state_predict.h create mode 100644 src/xrGameLA/ik_object_shift.cpp create mode 100644 src/xrGameLA/ik_object_shift.h create mode 100644 src/xrGameLA/ini_id_loader.h create mode 100644 src/xrGameLA/ini_table_loader.h create mode 100644 src/xrGameLA/interactive_motion.cpp create mode 100644 src/xrGameLA/interactive_motion.h create mode 100644 src/xrGameLA/inventory_item.cpp create mode 100644 src/xrGameLA/inventory_item.h create mode 100644 src/xrGameLA/inventory_item_impl.h create mode 100644 src/xrGameLA/inventory_item_inline.h create mode 100644 src/xrGameLA/inventory_item_object.cpp create mode 100644 src/xrGameLA/inventory_item_object.h create mode 100644 src/xrGameLA/inventory_item_object_inline.h create mode 100644 src/xrGameLA/inventory_item_object_script.cpp create mode 100644 src/xrGameLA/inventory_item_upgrade.cpp create mode 100644 src/xrGameLA/inventory_owner_info.cpp create mode 100644 src/xrGameLA/inventory_owner_inline.h create mode 100644 src/xrGameLA/inventory_space.h create mode 100644 src/xrGameLA/inventory_upgrade.cpp create mode 100644 src/xrGameLA/inventory_upgrade.h create mode 100644 src/xrGameLA/inventory_upgrade_base.cpp create mode 100644 src/xrGameLA/inventory_upgrade_base.h create mode 100644 src/xrGameLA/inventory_upgrade_base_inline.h create mode 100644 src/xrGameLA/inventory_upgrade_group.cpp create mode 100644 src/xrGameLA/inventory_upgrade_group.h create mode 100644 src/xrGameLA/inventory_upgrade_group_inline.h create mode 100644 src/xrGameLA/inventory_upgrade_inline.h create mode 100644 src/xrGameLA/inventory_upgrade_manager.cpp create mode 100644 src/xrGameLA/inventory_upgrade_manager.h create mode 100644 src/xrGameLA/inventory_upgrade_manager_inline.h create mode 100644 src/xrGameLA/inventory_upgrade_property.cpp create mode 100644 src/xrGameLA/inventory_upgrade_property.h create mode 100644 src/xrGameLA/inventory_upgrade_property_inline.h create mode 100644 src/xrGameLA/inventory_upgrade_root.cpp create mode 100644 src/xrGameLA/inventory_upgrade_root.h create mode 100644 src/xrGameLA/inventory_upgrade_root_inline.h create mode 100644 src/xrGameLA/item_manager.cpp create mode 100644 src/xrGameLA/item_manager.h create mode 100644 src/xrGameLA/item_manager_inline.h create mode 100644 src/xrGameLA/key_binding_registrator.h create mode 100644 src/xrGameLA/key_binding_registrator_script.cpp create mode 100644 src/xrGameLA/level_changer.cpp create mode 100644 src/xrGameLA/level_changer.h create mode 100644 src/xrGameLA/level_debug.cpp create mode 100644 src/xrGameLA/level_debug.h create mode 100644 src/xrGameLA/level_graph.cpp create mode 100644 src/xrGameLA/level_graph.h create mode 100644 src/xrGameLA/level_graph_debug.cpp create mode 100644 src/xrGameLA/level_graph_debug2.cpp create mode 100644 src/xrGameLA/level_graph_inline.h create mode 100644 src/xrGameLA/level_graph_space.h create mode 100644 src/xrGameLA/level_graph_vertex.cpp create mode 100644 src/xrGameLA/level_graph_vertex_inline.h create mode 100644 src/xrGameLA/level_location_selector.h create mode 100644 src/xrGameLA/level_location_selector_inline.h create mode 100644 src/xrGameLA/level_map_locations.cpp create mode 100644 src/xrGameLA/level_path_builder.h create mode 100644 src/xrGameLA/level_path_manager.h create mode 100644 src/xrGameLA/level_path_manager_inline.h create mode 100644 src/xrGameLA/level_script.cpp create mode 100644 src/xrGameLA/level_sounds.cpp create mode 100644 src/xrGameLA/level_sounds.h create mode 100644 src/xrGameLA/location_manager.cpp create mode 100644 src/xrGameLA/location_manager.h create mode 100644 src/xrGameLA/location_manager_inline.h create mode 100644 src/xrGameLA/magic_box3.cpp create mode 100644 src/xrGameLA/magic_box3.h create mode 100644 src/xrGameLA/magic_box3_inline.h create mode 100644 src/xrGameLA/magic_minimize_1d.cpp create mode 100644 src/xrGameLA/magic_minimize_1d.h create mode 100644 src/xrGameLA/magic_minimize_1d_inline.h create mode 100644 src/xrGameLA/magic_minimize_nd.h create mode 100644 src/xrGameLA/magic_minimize_nd_inline.h create mode 100644 src/xrGameLA/manager_builder_allocator_constructor.h create mode 100644 src/xrGameLA/manager_builder_allocator_constructor_inline.h create mode 100644 src/xrGameLA/map_location.cpp create mode 100644 src/xrGameLA/map_location.h create mode 100644 src/xrGameLA/map_location_defs.h create mode 100644 src/xrGameLA/map_manager.cpp create mode 100644 src/xrGameLA/map_manager.h create mode 100644 src/xrGameLA/map_spot.cpp create mode 100644 src/xrGameLA/map_spot.h create mode 100644 src/xrGameLA/material_manager.cpp create mode 100644 src/xrGameLA/material_manager.h create mode 100644 src/xrGameLA/material_manager_inline.h create mode 100644 src/xrGameLA/matrix_utils.h create mode 100644 src/xrGameLA/medkit.cpp create mode 100644 src/xrGameLA/medkit.h create mode 100644 src/xrGameLA/member_corpse.h create mode 100644 src/xrGameLA/member_corpse_inline.h create mode 100644 src/xrGameLA/member_enemy.h create mode 100644 src/xrGameLA/member_enemy_inline.h create mode 100644 src/xrGameLA/member_order.h create mode 100644 src/xrGameLA/member_order_inline.h create mode 100644 src/xrGameLA/memory_manager.cpp create mode 100644 src/xrGameLA/memory_manager.h create mode 100644 src/xrGameLA/memory_manager_inline.h create mode 100644 src/xrGameLA/memory_space.h create mode 100644 src/xrGameLA/memory_space_impl.h create mode 100644 src/xrGameLA/memory_space_script.cpp create mode 100644 src/xrGameLA/min_obb.cpp create mode 100644 src/xrGameLA/mincer_script.cpp create mode 100644 src/xrGameLA/monster_community.cpp create mode 100644 src/xrGameLA/monster_community.h create mode 100644 src/xrGameLA/mounted_turret.cpp create mode 100644 src/xrGameLA/mounted_turret.h create mode 100644 src/xrGameLA/mounted_turret_script.cpp create mode 100644 src/xrGameLA/movement_manager.cpp create mode 100644 src/xrGameLA/movement_manager.h create mode 100644 src/xrGameLA/movement_manager_game.cpp create mode 100644 src/xrGameLA/movement_manager_impl.h create mode 100644 src/xrGameLA/movement_manager_inline.h create mode 100644 src/xrGameLA/movement_manager_level.cpp create mode 100644 src/xrGameLA/movement_manager_patrol.cpp create mode 100644 src/xrGameLA/movement_manager_physic.cpp create mode 100644 src/xrGameLA/movement_manager_space.h create mode 100644 src/xrGameLA/mslotutils.h create mode 100644 src/xrGameLA/mt_config.h create mode 100644 src/xrGameLA/object_actions.cpp create mode 100644 src/xrGameLA/object_actions.h create mode 100644 src/xrGameLA/object_actions_inline.h create mode 100644 src/xrGameLA/object_broker.h create mode 100644 src/xrGameLA/object_cloner.h create mode 100644 src/xrGameLA/object_comparer.h create mode 100644 src/xrGameLA/object_destroyer.h create mode 100644 src/xrGameLA/object_factory.cpp create mode 100644 src/xrGameLA/object_factory.h create mode 100644 src/xrGameLA/object_factory_impl.h create mode 100644 src/xrGameLA/object_factory_inline.h create mode 100644 src/xrGameLA/object_factory_register.cpp create mode 100644 src/xrGameLA/object_factory_script.cpp create mode 100644 src/xrGameLA/object_factory_space.h create mode 100644 src/xrGameLA/object_handler.cpp create mode 100644 src/xrGameLA/object_handler.h create mode 100644 src/xrGameLA/object_handler_inline.h create mode 100644 src/xrGameLA/object_handler_planner.cpp create mode 100644 src/xrGameLA/object_handler_planner.h create mode 100644 src/xrGameLA/object_handler_planner_impl.h create mode 100644 src/xrGameLA/object_handler_planner_inline.h create mode 100644 src/xrGameLA/object_handler_planner_missile.cpp create mode 100644 src/xrGameLA/object_handler_planner_weapon.cpp create mode 100644 src/xrGameLA/object_handler_space.h create mode 100644 src/xrGameLA/object_interfaces.h create mode 100644 src/xrGameLA/object_item_abstract.h create mode 100644 src/xrGameLA/object_item_abstract_inline.h create mode 100644 src/xrGameLA/object_item_client_server.h create mode 100644 src/xrGameLA/object_item_client_server_inline.h create mode 100644 src/xrGameLA/object_item_script.cpp create mode 100644 src/xrGameLA/object_item_script.h create mode 100644 src/xrGameLA/object_item_single.h create mode 100644 src/xrGameLA/object_item_single_inline.h create mode 100644 src/xrGameLA/object_loader.h create mode 100644 src/xrGameLA/object_manager.h create mode 100644 src/xrGameLA/object_manager_inline.h create mode 100644 src/xrGameLA/object_property_evaluators.cpp create mode 100644 src/xrGameLA/object_property_evaluators.h create mode 100644 src/xrGameLA/object_property_evaluators_inline.h create mode 100644 src/xrGameLA/object_saver.h create mode 100644 src/xrGameLA/object_type_traits.h create mode 100644 src/xrGameLA/ode_include.h create mode 100644 src/xrGameLA/ode_redefine.h create mode 100644 src/xrGameLA/operator_abstract.h create mode 100644 src/xrGameLA/operator_abstract_inline.h create mode 100644 src/xrGameLA/operator_condition.h create mode 100644 src/xrGameLA/operator_condition_inline.h create mode 100644 src/xrGameLA/particle_params.cpp create mode 100644 src/xrGameLA/particle_params.h create mode 100644 src/xrGameLA/particle_params_inline.h create mode 100644 src/xrGameLA/particle_params_script.cpp create mode 100644 src/xrGameLA/path_manager.h create mode 100644 src/xrGameLA/path_manager_game.h create mode 100644 src/xrGameLA/path_manager_game_inline.h create mode 100644 src/xrGameLA/path_manager_game_level.h create mode 100644 src/xrGameLA/path_manager_game_level_inline.h create mode 100644 src/xrGameLA/path_manager_game_vertex.h create mode 100644 src/xrGameLA/path_manager_game_vertex_inline.h create mode 100644 src/xrGameLA/path_manager_generic.h create mode 100644 src/xrGameLA/path_manager_generic_inline.h create mode 100644 src/xrGameLA/path_manager_level.h create mode 100644 src/xrGameLA/path_manager_level_flooder.h create mode 100644 src/xrGameLA/path_manager_level_flooder_inline.h create mode 100644 src/xrGameLA/path_manager_level_inline.h create mode 100644 src/xrGameLA/path_manager_level_nearest_vertex.h create mode 100644 src/xrGameLA/path_manager_level_nearest_vertex_inline.h create mode 100644 src/xrGameLA/path_manager_level_straight_line.h create mode 100644 src/xrGameLA/path_manager_level_straight_line_inline.h create mode 100644 src/xrGameLA/path_manager_params.h create mode 100644 src/xrGameLA/path_manager_params_flooder.h create mode 100644 src/xrGameLA/path_manager_params_game_level.h create mode 100644 src/xrGameLA/path_manager_params_game_vertex.h create mode 100644 src/xrGameLA/path_manager_params_nearest_vertex.h create mode 100644 src/xrGameLA/path_manager_params_straight_line.h create mode 100644 src/xrGameLA/path_manager_solver.h create mode 100644 src/xrGameLA/path_manager_solver_inline.h create mode 100644 src/xrGameLA/patrol_path.cpp create mode 100644 src/xrGameLA/patrol_path.h create mode 100644 src/xrGameLA/patrol_path_inline.h create mode 100644 src/xrGameLA/patrol_path_manager.cpp create mode 100644 src/xrGameLA/patrol_path_manager.h create mode 100644 src/xrGameLA/patrol_path_manager_inline.h create mode 100644 src/xrGameLA/patrol_path_manager_space.h create mode 100644 src/xrGameLA/patrol_path_params.cpp create mode 100644 src/xrGameLA/patrol_path_params.h create mode 100644 src/xrGameLA/patrol_path_params_inline.h create mode 100644 src/xrGameLA/patrol_path_params_script.cpp create mode 100644 src/xrGameLA/patrol_path_storage.cpp create mode 100644 src/xrGameLA/patrol_path_storage.h create mode 100644 src/xrGameLA/patrol_path_storage_inline.h create mode 100644 src/xrGameLA/patrol_point.cpp create mode 100644 src/xrGameLA/patrol_point.h create mode 100644 src/xrGameLA/patrol_point_inline.h create mode 100644 src/xrGameLA/pch_script.cpp create mode 100644 src/xrGameLA/pch_script.h create mode 100644 src/xrGameLA/pda_space.h create mode 100644 src/xrGameLA/ph_shell_interface.h create mode 100644 src/xrGameLA/phvalide.h create mode 100644 src/xrGameLA/physic_item.cpp create mode 100644 src/xrGameLA/physic_item.h create mode 100644 src/xrGameLA/physic_item_inline.h create mode 100644 src/xrGameLA/physics_game.cpp create mode 100644 src/xrGameLA/player_hud.cpp create mode 100644 src/xrGameLA/player_hud.h create mode 100644 src/xrGameLA/player_hud_tune.cpp create mode 100644 src/xrGameLA/pose_extrapolation.cpp create mode 100644 src/xrGameLA/pose_extrapolation.h create mode 100644 src/xrGameLA/pp_effector_custom.cpp create mode 100644 src/xrGameLA/pp_effector_custom.h create mode 100644 src/xrGameLA/pp_effector_distance.cpp create mode 100644 src/xrGameLA/pp_effector_distance.h create mode 100644 src/xrGameLA/problem_solver.h create mode 100644 src/xrGameLA/problem_solver_inline.h create mode 100644 src/xrGameLA/profiler.cpp create mode 100644 src/xrGameLA/profiler.h create mode 100644 src/xrGameLA/profiler_inline.h create mode 100644 src/xrGameLA/property_evaluator.h create mode 100644 src/xrGameLA/property_evaluator_const.h create mode 100644 src/xrGameLA/property_evaluator_const_inline.h create mode 100644 src/xrGameLA/property_evaluator_inline.h create mode 100644 src/xrGameLA/property_evaluator_member.h create mode 100644 src/xrGameLA/property_evaluator_member_inline.h create mode 100644 src/xrGameLA/property_evaluator_script.cpp create mode 100644 src/xrGameLA/property_storage.h create mode 100644 src/xrGameLA/property_storage_inline.h create mode 100644 src/xrGameLA/property_storage_script.cpp create mode 100644 src/xrGameLA/purchase_list.cpp create mode 100644 src/xrGameLA/purchase_list.h create mode 100644 src/xrGameLA/purchase_list_inline.h create mode 100644 src/xrGameLA/quadtree.h create mode 100644 src/xrGameLA/quadtree_inline.h create mode 100644 src/xrGameLA/random32.cpp create mode 100644 src/xrGameLA/random32.h create mode 100644 src/xrGameLA/rat_state_base.cpp create mode 100644 src/xrGameLA/rat_state_base.h create mode 100644 src/xrGameLA/rat_state_base_inline.h create mode 100644 src/xrGameLA/rat_state_manager.cpp create mode 100644 src/xrGameLA/rat_state_manager.h create mode 100644 src/xrGameLA/rat_state_manager_inline.h create mode 100644 src/xrGameLA/rat_states.cpp create mode 100644 src/xrGameLA/rat_states.h create mode 100644 src/xrGameLA/relation_registry.cpp create mode 100644 src/xrGameLA/relation_registry.h create mode 100644 src/xrGameLA/relation_registry_actions.cpp create mode 100644 src/xrGameLA/relation_registry_defs.h create mode 100644 src/xrGameLA/relation_registry_fights.cpp create mode 100644 src/xrGameLA/relation_registry_inline.h create mode 100644 src/xrGameLA/restricted_object.cpp create mode 100644 src/xrGameLA/restricted_object.h create mode 100644 src/xrGameLA/restricted_object_inline.h create mode 100644 src/xrGameLA/restriction_space.h create mode 100644 src/xrGameLA/safe_map_iterator.h create mode 100644 src/xrGameLA/safe_map_iterator_inline.h create mode 100644 src/xrGameLA/saved_game_wrapper.cpp create mode 100644 src/xrGameLA/saved_game_wrapper.h create mode 100644 src/xrGameLA/saved_game_wrapper_inline.h create mode 100644 src/xrGameLA/saved_game_wrapper_script.cpp create mode 100644 src/xrGameLA/script_abstract_action.cpp create mode 100644 src/xrGameLA/script_abstract_action.h create mode 100644 src/xrGameLA/script_abstract_action_inline.h create mode 100644 src/xrGameLA/script_action_condition.cpp create mode 100644 src/xrGameLA/script_action_condition.h create mode 100644 src/xrGameLA/script_action_condition_inline.h create mode 100644 src/xrGameLA/script_action_condition_script.cpp create mode 100644 src/xrGameLA/script_action_planner_action_wrapper.cpp create mode 100644 src/xrGameLA/script_action_planner_action_wrapper.h create mode 100644 src/xrGameLA/script_action_planner_action_wrapper_inline.h create mode 100644 src/xrGameLA/script_action_planner_wrapper.cpp create mode 100644 src/xrGameLA/script_action_planner_wrapper.h create mode 100644 src/xrGameLA/script_action_planner_wrapper_inline.h create mode 100644 src/xrGameLA/script_action_wrapper.cpp create mode 100644 src/xrGameLA/script_action_wrapper.h create mode 100644 src/xrGameLA/script_action_wrapper_inline.h create mode 100644 src/xrGameLA/script_animation_action.cpp create mode 100644 src/xrGameLA/script_animation_action.h create mode 100644 src/xrGameLA/script_animation_action_inline.h create mode 100644 src/xrGameLA/script_animation_action_script.cpp create mode 100644 src/xrGameLA/script_bind_macroses.h create mode 100644 src/xrGameLA/script_binder.cpp create mode 100644 src/xrGameLA/script_binder.h create mode 100644 src/xrGameLA/script_binder_inline.h create mode 100644 src/xrGameLA/script_binder_object.cpp create mode 100644 src/xrGameLA/script_binder_object.h create mode 100644 src/xrGameLA/script_binder_object_script.cpp create mode 100644 src/xrGameLA/script_binder_object_wrapper.cpp create mode 100644 src/xrGameLA/script_binder_object_wrapper.h create mode 100644 src/xrGameLA/script_callStack.cpp create mode 100644 src/xrGameLA/script_callStack.h create mode 100644 src/xrGameLA/script_callback_ex.h create mode 100644 src/xrGameLA/script_callback_ex_generators.h create mode 100644 src/xrGameLA/script_callback_ex_inline.h create mode 100644 src/xrGameLA/script_callback_ex_nonvoid.h create mode 100644 src/xrGameLA/script_callback_ex_void.h create mode 100644 src/xrGameLA/script_debugger.cpp create mode 100644 src/xrGameLA/script_debugger.h create mode 100644 src/xrGameLA/script_debugger_messages.h create mode 100644 src/xrGameLA/script_debugger_threads.cpp create mode 100644 src/xrGameLA/script_debugger_threads.h create mode 100644 src/xrGameLA/script_effector.cpp create mode 100644 src/xrGameLA/script_effector.h create mode 100644 src/xrGameLA/script_effector_inline.h create mode 100644 src/xrGameLA/script_effector_script.cpp create mode 100644 src/xrGameLA/script_effector_wrapper.cpp create mode 100644 src/xrGameLA/script_effector_wrapper.h create mode 100644 src/xrGameLA/script_effector_wrapper_inline.h create mode 100644 src/xrGameLA/script_engine.cpp create mode 100644 src/xrGameLA/script_engine.h create mode 100644 src/xrGameLA/script_engine_export.cpp create mode 100644 src/xrGameLA/script_engine_export.h create mode 100644 src/xrGameLA/script_engine_help.cpp create mode 100644 src/xrGameLA/script_engine_inline.h create mode 100644 src/xrGameLA/script_engine_script.cpp create mode 100644 src/xrGameLA/script_engine_space.h create mode 100644 src/xrGameLA/script_entity.cpp create mode 100644 src/xrGameLA/script_entity.h create mode 100644 src/xrGameLA/script_entity_action.cpp create mode 100644 src/xrGameLA/script_entity_action.h create mode 100644 src/xrGameLA/script_entity_action_inline.h create mode 100644 src/xrGameLA/script_entity_action_script.cpp create mode 100644 src/xrGameLA/script_entity_inline.h create mode 100644 src/xrGameLA/script_entity_space.h create mode 100644 src/xrGameLA/script_export_macroses.h create mode 100644 src/xrGameLA/script_export_space.h create mode 100644 src/xrGameLA/script_fcolor.h create mode 100644 src/xrGameLA/script_fcolor_script.cpp create mode 100644 src/xrGameLA/script_flags.h create mode 100644 src/xrGameLA/script_flags_script.cpp create mode 100644 src/xrGameLA/script_fmatrix.h create mode 100644 src/xrGameLA/script_fmatrix_script.cpp create mode 100644 src/xrGameLA/script_fvector.h create mode 100644 src/xrGameLA/script_fvector_script.cpp create mode 100644 src/xrGameLA/script_game_object.cpp create mode 100644 src/xrGameLA/script_game_object.h create mode 100644 src/xrGameLA/script_game_object2.cpp create mode 100644 src/xrGameLA/script_game_object3.cpp create mode 100644 src/xrGameLA/script_game_object4.cpp create mode 100644 src/xrGameLA/script_game_object_impl.h create mode 100644 src/xrGameLA/script_game_object_inventory_owner.cpp create mode 100644 src/xrGameLA/script_game_object_script.cpp create mode 100644 src/xrGameLA/script_game_object_script2.cpp create mode 100644 src/xrGameLA/script_game_object_script3.cpp create mode 100644 src/xrGameLA/script_game_object_script_trader.cpp create mode 100644 src/xrGameLA/script_game_object_trader.cpp create mode 100644 src/xrGameLA/script_game_object_use.cpp create mode 100644 src/xrGameLA/script_game_object_use2.cpp create mode 100644 src/xrGameLA/script_hit.cpp create mode 100644 src/xrGameLA/script_hit.h create mode 100644 src/xrGameLA/script_hit_inline.h create mode 100644 src/xrGameLA/script_hit_script.cpp create mode 100644 src/xrGameLA/script_ini_file.cpp create mode 100644 src/xrGameLA/script_ini_file.h create mode 100644 src/xrGameLA/script_ini_file_inline.h create mode 100644 src/xrGameLA/script_ini_file_script.cpp create mode 100644 src/xrGameLA/script_lanim.cpp create mode 100644 src/xrGameLA/script_lanim.h create mode 100644 src/xrGameLA/script_lua_helper.cpp create mode 100644 src/xrGameLA/script_lua_helper.h create mode 100644 src/xrGameLA/script_monster_action.cpp create mode 100644 src/xrGameLA/script_monster_action.h create mode 100644 src/xrGameLA/script_monster_action_inline.h create mode 100644 src/xrGameLA/script_monster_action_script.cpp create mode 100644 src/xrGameLA/script_monster_hit_info.h create mode 100644 src/xrGameLA/script_monster_hit_info_script.cpp create mode 100644 src/xrGameLA/script_movement_action.cpp create mode 100644 src/xrGameLA/script_movement_action.h create mode 100644 src/xrGameLA/script_movement_action_inline.h create mode 100644 src/xrGameLA/script_movement_action_script.cpp create mode 100644 src/xrGameLA/script_net_packet.h create mode 100644 src/xrGameLA/script_net_packet_script.cpp create mode 100644 src/xrGameLA/script_object.cpp create mode 100644 src/xrGameLA/script_object.h create mode 100644 src/xrGameLA/script_object_action.cpp create mode 100644 src/xrGameLA/script_object_action.h create mode 100644 src/xrGameLA/script_object_action_inline.h create mode 100644 src/xrGameLA/script_object_action_script.cpp create mode 100644 src/xrGameLA/script_particle_action.cpp create mode 100644 src/xrGameLA/script_particle_action.h create mode 100644 src/xrGameLA/script_particle_action_inline.h create mode 100644 src/xrGameLA/script_particle_action_script.cpp create mode 100644 src/xrGameLA/script_particles.cpp create mode 100644 src/xrGameLA/script_particles.h create mode 100644 src/xrGameLA/script_particles_inline.h create mode 100644 src/xrGameLA/script_particles_script.cpp create mode 100644 src/xrGameLA/script_process.cpp create mode 100644 src/xrGameLA/script_process.h create mode 100644 src/xrGameLA/script_process_inline.h create mode 100644 src/xrGameLA/script_property_evaluator_wrapper.cpp create mode 100644 src/xrGameLA/script_property_evaluator_wrapper.h create mode 100644 src/xrGameLA/script_property_evaluator_wrapper_inline.h create mode 100644 src/xrGameLA/script_reader.h create mode 100644 src/xrGameLA/script_reader_script.cpp create mode 100644 src/xrGameLA/script_render_device.h create mode 100644 src/xrGameLA/script_render_device_script.cpp create mode 100644 src/xrGameLA/script_rtoken_list.h create mode 100644 src/xrGameLA/script_rtoken_list_inline.h create mode 100644 src/xrGameLA/script_rtoken_list_script.cpp create mode 100644 src/xrGameLA/script_sound.cpp create mode 100644 src/xrGameLA/script_sound.h create mode 100644 src/xrGameLA/script_sound_action.cpp create mode 100644 src/xrGameLA/script_sound_action.h create mode 100644 src/xrGameLA/script_sound_action_inline.h create mode 100644 src/xrGameLA/script_sound_action_script.cpp create mode 100644 src/xrGameLA/script_sound_info.h create mode 100644 src/xrGameLA/script_sound_info_script.cpp create mode 100644 src/xrGameLA/script_sound_inline.h create mode 100644 src/xrGameLA/script_sound_script.cpp create mode 100644 src/xrGameLA/script_sound_type.h create mode 100644 src/xrGameLA/script_sound_type_script.cpp create mode 100644 src/xrGameLA/script_space_forward.h create mode 100644 src/xrGameLA/script_stack_tracker.cpp create mode 100644 src/xrGameLA/script_stack_tracker.h create mode 100644 src/xrGameLA/script_stack_tracker_inline.h create mode 100644 src/xrGameLA/script_storage.cpp create mode 100644 src/xrGameLA/script_storage.h create mode 100644 src/xrGameLA/script_storage_inline.h create mode 100644 src/xrGameLA/script_storage_space.h create mode 100644 src/xrGameLA/script_thread.cpp create mode 100644 src/xrGameLA/script_thread.h create mode 100644 src/xrGameLA/script_thread_inline.h create mode 100644 src/xrGameLA/script_token_list.cpp create mode 100644 src/xrGameLA/script_token_list.h create mode 100644 src/xrGameLA/script_token_list_inline.h create mode 100644 src/xrGameLA/script_token_list_script.cpp create mode 100644 src/xrGameLA/script_ui_registrator.h create mode 100644 src/xrGameLA/script_value_container.h create mode 100644 src/xrGameLA/script_value_container_impl.h create mode 100644 src/xrGameLA/script_watch_action.cpp create mode 100644 src/xrGameLA/script_watch_action.h create mode 100644 src/xrGameLA/script_watch_action_inline.h create mode 100644 src/xrGameLA/script_watch_action_script.cpp create mode 100644 src/xrGameLA/script_world_property.h create mode 100644 src/xrGameLA/script_world_property_inline.h create mode 100644 src/xrGameLA/script_world_property_script.cpp create mode 100644 src/xrGameLA/script_world_state.h create mode 100644 src/xrGameLA/script_world_state_script.cpp create mode 100644 src/xrGameLA/script_zone.cpp create mode 100644 src/xrGameLA/script_zone.h create mode 100644 src/xrGameLA/script_zone_script.cpp create mode 100644 src/xrGameLA/searchlight.cpp create mode 100644 src/xrGameLA/searchlight.h create mode 100644 src/xrGameLA/secure_messaging.cpp create mode 100644 src/xrGameLA/secure_messaging.h create mode 100644 src/xrGameLA/seniority_hierarchy_holder.cpp create mode 100644 src/xrGameLA/seniority_hierarchy_holder.h create mode 100644 src/xrGameLA/seniority_hierarchy_holder_inline.h create mode 100644 src/xrGameLA/seniority_hierarchy_space.h create mode 100644 src/xrGameLA/server_entity_wrapper.cpp create mode 100644 src/xrGameLA/server_entity_wrapper.h create mode 100644 src/xrGameLA/server_entity_wrapper_inline.h create mode 100644 src/xrGameLA/setup_manager.h create mode 100644 src/xrGameLA/setup_manager_inline.h create mode 100644 src/xrGameLA/shared_data.h create mode 100644 src/xrGameLA/sight_action.cpp create mode 100644 src/xrGameLA/sight_action.h create mode 100644 src/xrGameLA/sight_action_inline.h create mode 100644 src/xrGameLA/sight_control_action.h create mode 100644 src/xrGameLA/sight_control_action_inline.h create mode 100644 src/xrGameLA/sight_manager.cpp create mode 100644 src/xrGameLA/sight_manager.h create mode 100644 src/xrGameLA/sight_manager_inline.h create mode 100644 src/xrGameLA/sight_manager_space.h create mode 100644 src/xrGameLA/smart_cast.cpp create mode 100644 src/xrGameLA/smart_cast.h create mode 100644 src/xrGameLA/smart_cast_impl0.h create mode 100644 src/xrGameLA/smart_cast_impl1.h create mode 100644 src/xrGameLA/smart_cast_impl2.h create mode 100644 src/xrGameLA/smart_cast_stats.cpp create mode 100644 src/xrGameLA/smart_zone.h create mode 100644 src/xrGameLA/sound_collection_storage.cpp create mode 100644 src/xrGameLA/sound_collection_storage.h create mode 100644 src/xrGameLA/sound_collection_storage_inline.h create mode 100644 src/xrGameLA/sound_memory_manager.cpp create mode 100644 src/xrGameLA/sound_memory_manager.h create mode 100644 src/xrGameLA/sound_memory_manager_inline.h create mode 100644 src/xrGameLA/sound_player.cpp create mode 100644 src/xrGameLA/sound_player.h create mode 100644 src/xrGameLA/sound_player_inline.h create mode 100644 src/xrGameLA/sound_user_data_visitor.h create mode 100644 src/xrGameLA/space_restriction.cpp create mode 100644 src/xrGameLA/space_restriction.h create mode 100644 src/xrGameLA/space_restriction_abstract.h create mode 100644 src/xrGameLA/space_restriction_abstract_inline.h create mode 100644 src/xrGameLA/space_restriction_base.cpp create mode 100644 src/xrGameLA/space_restriction_base.h create mode 100644 src/xrGameLA/space_restriction_base_inline.h create mode 100644 src/xrGameLA/space_restriction_bridge.cpp create mode 100644 src/xrGameLA/space_restriction_bridge.h create mode 100644 src/xrGameLA/space_restriction_bridge_inline.h create mode 100644 src/xrGameLA/space_restriction_composition.cpp create mode 100644 src/xrGameLA/space_restriction_composition.h create mode 100644 src/xrGameLA/space_restriction_composition_inline.h create mode 100644 src/xrGameLA/space_restriction_holder.cpp create mode 100644 src/xrGameLA/space_restriction_holder.h create mode 100644 src/xrGameLA/space_restriction_holder_inline.h create mode 100644 src/xrGameLA/space_restriction_inline.h create mode 100644 src/xrGameLA/space_restriction_manager.cpp create mode 100644 src/xrGameLA/space_restriction_manager.h create mode 100644 src/xrGameLA/space_restriction_manager_inline.h create mode 100644 src/xrGameLA/space_restriction_shape.cpp create mode 100644 src/xrGameLA/space_restriction_shape.h create mode 100644 src/xrGameLA/space_restriction_shape_inline.h create mode 100644 src/xrGameLA/space_restrictor.cpp create mode 100644 src/xrGameLA/space_restrictor.h create mode 100644 src/xrGameLA/space_restrictor_inline.h create mode 100644 src/xrGameLA/space_restrictor_script.cpp create mode 100644 src/xrGameLA/specific_character.cpp create mode 100644 src/xrGameLA/specific_character.h create mode 100644 src/xrGameLA/squad_hierarchy_holder.cpp create mode 100644 src/xrGameLA/squad_hierarchy_holder.h create mode 100644 src/xrGameLA/squad_hierarchy_holder_inline.h create mode 100644 src/xrGameLA/stalker_alife_actions.cpp create mode 100644 src/xrGameLA/stalker_alife_actions.h create mode 100644 src/xrGameLA/stalker_alife_planner.cpp create mode 100644 src/xrGameLA/stalker_alife_planner.h create mode 100644 src/xrGameLA/stalker_alife_task_actions.cpp create mode 100644 src/xrGameLA/stalker_alife_task_actions.h create mode 100644 src/xrGameLA/stalker_animation_callbacks.cpp create mode 100644 src/xrGameLA/stalker_animation_data.cpp create mode 100644 src/xrGameLA/stalker_animation_data.h create mode 100644 src/xrGameLA/stalker_animation_data_storage.cpp create mode 100644 src/xrGameLA/stalker_animation_data_storage.h create mode 100644 src/xrGameLA/stalker_animation_data_storage_inline.h create mode 100644 src/xrGameLA/stalker_animation_global.cpp create mode 100644 src/xrGameLA/stalker_animation_head.cpp create mode 100644 src/xrGameLA/stalker_animation_legs.cpp create mode 100644 src/xrGameLA/stalker_animation_manager.cpp create mode 100644 src/xrGameLA/stalker_animation_manager.h create mode 100644 src/xrGameLA/stalker_animation_manager_debug.cpp create mode 100644 src/xrGameLA/stalker_animation_manager_impl.h create mode 100644 src/xrGameLA/stalker_animation_manager_inline.h create mode 100644 src/xrGameLA/stalker_animation_manager_update.cpp create mode 100644 src/xrGameLA/stalker_animation_names.cpp create mode 100644 src/xrGameLA/stalker_animation_names.h create mode 100644 src/xrGameLA/stalker_animation_pair.cpp create mode 100644 src/xrGameLA/stalker_animation_pair.h create mode 100644 src/xrGameLA/stalker_animation_pair_inline.h create mode 100644 src/xrGameLA/stalker_animation_script.cpp create mode 100644 src/xrGameLA/stalker_animation_script.h create mode 100644 src/xrGameLA/stalker_animation_script_inline.h create mode 100644 src/xrGameLA/stalker_animation_state.cpp create mode 100644 src/xrGameLA/stalker_animation_state.h create mode 100644 src/xrGameLA/stalker_animation_state_inline.h create mode 100644 src/xrGameLA/stalker_animation_torso.cpp create mode 100644 src/xrGameLA/stalker_anomaly_actions.cpp create mode 100644 src/xrGameLA/stalker_anomaly_actions.h create mode 100644 src/xrGameLA/stalker_anomaly_planner.cpp create mode 100644 src/xrGameLA/stalker_anomaly_planner.h create mode 100644 src/xrGameLA/stalker_base_action.cpp create mode 100644 src/xrGameLA/stalker_base_action.h create mode 100644 src/xrGameLA/stalker_combat_action_base.cpp create mode 100644 src/xrGameLA/stalker_combat_action_base.h create mode 100644 src/xrGameLA/stalker_combat_actions.cpp create mode 100644 src/xrGameLA/stalker_combat_actions.h create mode 100644 src/xrGameLA/stalker_combat_actions_inline.h create mode 100644 src/xrGameLA/stalker_combat_planner.cpp create mode 100644 src/xrGameLA/stalker_combat_planner.h create mode 100644 src/xrGameLA/stalker_danger_by_sound_actions.cpp create mode 100644 src/xrGameLA/stalker_danger_by_sound_actions.h create mode 100644 src/xrGameLA/stalker_danger_by_sound_planner.cpp create mode 100644 src/xrGameLA/stalker_danger_by_sound_planner.h create mode 100644 src/xrGameLA/stalker_danger_grenade_actions.cpp create mode 100644 src/xrGameLA/stalker_danger_grenade_actions.h create mode 100644 src/xrGameLA/stalker_danger_grenade_planner.cpp create mode 100644 src/xrGameLA/stalker_danger_grenade_planner.h create mode 100644 src/xrGameLA/stalker_danger_in_direction_actions.cpp create mode 100644 src/xrGameLA/stalker_danger_in_direction_actions.h create mode 100644 src/xrGameLA/stalker_danger_in_direction_planner.cpp create mode 100644 src/xrGameLA/stalker_danger_in_direction_planner.h create mode 100644 src/xrGameLA/stalker_danger_planner.cpp create mode 100644 src/xrGameLA/stalker_danger_planner.h create mode 100644 src/xrGameLA/stalker_danger_planner_inline.h create mode 100644 src/xrGameLA/stalker_danger_property_evaluators.cpp create mode 100644 src/xrGameLA/stalker_danger_property_evaluators.h create mode 100644 src/xrGameLA/stalker_danger_unknown_actions.cpp create mode 100644 src/xrGameLA/stalker_danger_unknown_actions.h create mode 100644 src/xrGameLA/stalker_danger_unknown_planner.cpp create mode 100644 src/xrGameLA/stalker_danger_unknown_planner.h create mode 100644 src/xrGameLA/stalker_death_actions.cpp create mode 100644 src/xrGameLA/stalker_death_actions.h create mode 100644 src/xrGameLA/stalker_death_planner.cpp create mode 100644 src/xrGameLA/stalker_death_planner.h create mode 100644 src/xrGameLA/stalker_decision_space.h create mode 100644 src/xrGameLA/stalker_kill_wounded_actions.cpp create mode 100644 src/xrGameLA/stalker_kill_wounded_actions.h create mode 100644 src/xrGameLA/stalker_kill_wounded_planner.cpp create mode 100644 src/xrGameLA/stalker_kill_wounded_planner.h create mode 100644 src/xrGameLA/stalker_movement_manager.cpp create mode 100644 src/xrGameLA/stalker_movement_manager.h create mode 100644 src/xrGameLA/stalker_movement_manager_inline.h create mode 100644 src/xrGameLA/stalker_movement_manager_space.h create mode 100644 src/xrGameLA/stalker_movement_params.h create mode 100644 src/xrGameLA/stalker_movement_params_inline.h create mode 100644 src/xrGameLA/stalker_movement_restriction.h create mode 100644 src/xrGameLA/stalker_movement_restriction_inline.h create mode 100644 src/xrGameLA/stalker_planner.cpp create mode 100644 src/xrGameLA/stalker_planner.h create mode 100644 src/xrGameLA/stalker_planner_inline.h create mode 100644 src/xrGameLA/stalker_property_evaluators.cpp create mode 100644 src/xrGameLA/stalker_property_evaluators.h create mode 100644 src/xrGameLA/stalker_property_evaluators_inline.h create mode 100644 src/xrGameLA/stalker_search_actions.cpp create mode 100644 src/xrGameLA/stalker_search_actions.h create mode 100644 src/xrGameLA/stalker_search_planner.cpp create mode 100644 src/xrGameLA/stalker_search_planner.h create mode 100644 src/xrGameLA/stalker_sound_data.cpp create mode 100644 src/xrGameLA/stalker_sound_data.h create mode 100644 src/xrGameLA/stalker_sound_data_inline.h create mode 100644 src/xrGameLA/stalker_sound_data_visitor.cpp create mode 100644 src/xrGameLA/stalker_sound_data_visitor.h create mode 100644 src/xrGameLA/stalker_sound_data_visitor_inline.h create mode 100644 src/xrGameLA/stalker_velocity_collection.cpp create mode 100644 src/xrGameLA/stalker_velocity_collection.h create mode 100644 src/xrGameLA/stalker_velocity_collection_inline.h create mode 100644 src/xrGameLA/stalker_velocity_holder.cpp create mode 100644 src/xrGameLA/stalker_velocity_holder.h create mode 100644 src/xrGameLA/stalker_velocity_holder_inline.h create mode 100644 src/xrGameLA/step_manager.cpp create mode 100644 src/xrGameLA/step_manager.h create mode 100644 src/xrGameLA/step_manager_defs.h create mode 100644 src/xrGameLA/string_table.cpp create mode 100644 src/xrGameLA/string_table.h create mode 100644 src/xrGameLA/string_table_defs.h create mode 100644 src/xrGameLA/team_base_zone.cpp create mode 100644 src/xrGameLA/team_base_zone.h create mode 100644 src/xrGameLA/team_hierarchy_holder.cpp create mode 100644 src/xrGameLA/team_hierarchy_holder.h create mode 100644 src/xrGameLA/team_hierarchy_holder_inline.h create mode 100644 src/xrGameLA/torch_script.cpp create mode 100644 src/xrGameLA/trade.cpp create mode 100644 src/xrGameLA/trade.h create mode 100644 src/xrGameLA/trade2.cpp create mode 100644 src/xrGameLA/trade_action_parameters.h create mode 100644 src/xrGameLA/trade_action_parameters_inline.h create mode 100644 src/xrGameLA/trade_bool_parameters.h create mode 100644 src/xrGameLA/trade_bool_parameters_inline.h create mode 100644 src/xrGameLA/trade_factor_parameters.h create mode 100644 src/xrGameLA/trade_factor_parameters_inline.h create mode 100644 src/xrGameLA/trade_factors.h create mode 100644 src/xrGameLA/trade_factors_inline.h create mode 100644 src/xrGameLA/trade_parameters.cpp create mode 100644 src/xrGameLA/trade_parameters.h create mode 100644 src/xrGameLA/trade_parameters_inline.h create mode 100644 src/xrGameLA/traffic_optimization.cpp create mode 100644 src/xrGameLA/traffic_optimization.h create mode 100644 src/xrGameLA/trajectories.cpp create mode 100644 src/xrGameLA/trajectories.h create mode 100644 src/xrGameLA/tri-colliderknoopc/TriPrimitiveCollideClassDef.h create mode 100644 src/xrGameLA/tri-colliderknoopc/__aabb_tri.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dSortTriPrimitive.cpp create mode 100644 src/xrGameLA/tri-colliderknoopc/dSortTriPrimitive.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriBox.cpp create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriBox.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriCallideK.cpp create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriCollideK.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriColliderCommon.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriColliderMath.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriCylinder.cpp create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriCylinder.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriList.cpp create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriList.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriSphere.cpp create mode 100644 src/xrGameLA/tri-colliderknoopc/dTriSphere.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dcTriListCollider.cpp create mode 100644 src/xrGameLA/tri-colliderknoopc/dcTriListCollider.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dcTriangle.h create mode 100644 src/xrGameLA/tri-colliderknoopc/dxTriList.h create mode 100644 src/xrGameLA/ui/ArtefactDetectorUI.cpp create mode 100644 src/xrGameLA/ui/ArtefactDetectorUI.h create mode 100644 src/xrGameLA/ui/CExtraContentFilter.cpp create mode 100644 src/xrGameLA/ui/CExtraContentFilter.h create mode 100644 src/xrGameLA/ui/KillMessageStruct.h create mode 100644 src/xrGameLA/ui/MMSound.cpp create mode 100644 src/xrGameLA/ui/MMSound.h create mode 100644 src/xrGameLA/ui/Restrictions.cpp create mode 100644 src/xrGameLA/ui/Restrictions.h create mode 100644 src/xrGameLA/ui/UI3tButton.cpp create mode 100644 src/xrGameLA/ui/UI3tButton.h create mode 100644 src/xrGameLA/ui/UIActorInfo.cpp create mode 100644 src/xrGameLA/ui/UIActorInfo.h create mode 100644 src/xrGameLA/ui/UIActorProtectionInfo.cpp create mode 100644 src/xrGameLA/ui/UIActorProtectionInfo.h create mode 100644 src/xrGameLA/ui/UIAnimatedStatic.cpp create mode 100644 src/xrGameLA/ui/UIAnimatedStatic.h create mode 100644 src/xrGameLA/ui/UIArtefactPanel.cpp create mode 100644 src/xrGameLA/ui/UIArtefactPanel.h create mode 100644 src/xrGameLA/ui/UIArtifactParams.cpp create mode 100644 src/xrGameLA/ui/UIArtifactParams.h create mode 100644 src/xrGameLA/ui/UIBtnHint.cpp create mode 100644 src/xrGameLA/ui/UIBtnHint.h create mode 100644 src/xrGameLA/ui/UIButton.cpp create mode 100644 src/xrGameLA/ui/UIButton.h create mode 100644 src/xrGameLA/ui/UIButton_script.cpp create mode 100644 src/xrGameLA/ui/UIBuyWeaponTab.cpp create mode 100644 src/xrGameLA/ui/UIBuyWeaponTab.h create mode 100644 src/xrGameLA/ui/UIBuyWndBase.h create mode 100644 src/xrGameLA/ui/UIBuyWndShared.cpp create mode 100644 src/xrGameLA/ui/UIBuyWndShared.h create mode 100644 src/xrGameLA/ui/UICarBodyWnd.cpp create mode 100644 src/xrGameLA/ui/UICarBodyWnd.h create mode 100644 src/xrGameLA/ui/UICarPanel.cpp create mode 100644 src/xrGameLA/ui/UICarPanel.h create mode 100644 src/xrGameLA/ui/UICellCustomItems.cpp create mode 100644 src/xrGameLA/ui/UICellCustomItems.h create mode 100644 src/xrGameLA/ui/UICellItem.cpp create mode 100644 src/xrGameLA/ui/UICellItem.h create mode 100644 src/xrGameLA/ui/UICellItemFactory.cpp create mode 100644 src/xrGameLA/ui/UICellItemFactory.h create mode 100644 src/xrGameLA/ui/UICharacterInfo.cpp create mode 100644 src/xrGameLA/ui/UICharacterInfo.h create mode 100644 src/xrGameLA/ui/UIChatWnd.cpp create mode 100644 src/xrGameLA/ui/UIChatWnd.h create mode 100644 src/xrGameLA/ui/UICheckButton.cpp create mode 100644 src/xrGameLA/ui/UICheckButton.h create mode 100644 src/xrGameLA/ui/UIColorAnimatorWrapper.cpp create mode 100644 src/xrGameLA/ui/UIColorAnimatorWrapper.h create mode 100644 src/xrGameLA/ui/UIComboBox.cpp create mode 100644 src/xrGameLA/ui/UIComboBox.h create mode 100644 src/xrGameLA/ui/UIComboBox_script.cpp create mode 100644 src/xrGameLA/ui/UICustomEdit.cpp create mode 100644 src/xrGameLA/ui/UICustomEdit.h create mode 100644 src/xrGameLA/ui/UICustomSpin.cpp create mode 100644 src/xrGameLA/ui/UICustomSpin.h create mode 100644 src/xrGameLA/ui/UIDebugFonts.cpp create mode 100644 src/xrGameLA/ui/UIDebugFonts.h create mode 100644 src/xrGameLA/ui/UIDialogWnd.cpp create mode 100644 src/xrGameLA/ui/UIDialogWnd.h create mode 100644 src/xrGameLA/ui/UIDiaryWnd.h create mode 100644 src/xrGameLA/ui/UIDiaryWnd2.cpp create mode 100644 src/xrGameLA/ui/UIDragDropListEx.cpp create mode 100644 src/xrGameLA/ui/UIDragDropListEx.h create mode 100644 src/xrGameLA/ui/UIDragDropReferenceList.cpp create mode 100644 src/xrGameLA/ui/UIDragDropReferenceList.h create mode 100644 src/xrGameLA/ui/UIEditBox.cpp create mode 100644 src/xrGameLA/ui/UIEditBox.h create mode 100644 src/xrGameLA/ui/UIEditBoxEx.cpp create mode 100644 src/xrGameLA/ui/UIEditBoxEx.h create mode 100644 src/xrGameLA/ui/UIEditBox_script.cpp create mode 100644 src/xrGameLA/ui/UIEditKeyBind.cpp create mode 100644 src/xrGameLA/ui/UIEditKeyBind.h create mode 100644 src/xrGameLA/ui/UIEncyclopediaArticleWnd.cpp create mode 100644 src/xrGameLA/ui/UIEncyclopediaArticleWnd.h create mode 100644 src/xrGameLA/ui/UIEncyclopediaWnd.cpp create mode 100644 src/xrGameLA/ui/UIEncyclopediaWnd.h create mode 100644 src/xrGameLA/ui/UIEventsWnd.cpp create mode 100644 src/xrGameLA/ui/UIEventsWnd.h create mode 100644 src/xrGameLA/ui/UIFrameLineWnd.cpp create mode 100644 src/xrGameLA/ui/UIFrameLineWnd.h create mode 100644 src/xrGameLA/ui/UIFrameWindow.cpp create mode 100644 src/xrGameLA/ui/UIFrameWindow.h create mode 100644 src/xrGameLA/ui/UIGameLog.cpp create mode 100644 src/xrGameLA/ui/UIGameLog.h create mode 100644 src/xrGameLA/ui/UIGameTutorial.cpp create mode 100644 src/xrGameLA/ui/UIGameTutorial.h create mode 100644 src/xrGameLA/ui/UIGameTutorialSimpleItem.cpp create mode 100644 src/xrGameLA/ui/UIGameTutorialVideoItem.cpp create mode 100644 src/xrGameLA/ui/UIHelper.cpp create mode 100644 src/xrGameLA/ui/UIHelper.h create mode 100644 src/xrGameLA/ui/UIHint.cpp create mode 100644 src/xrGameLA/ui/UIHint.h create mode 100644 src/xrGameLA/ui/UIInteractiveBackground.h create mode 100644 src/xrGameLA/ui/UIInvUpgrade.cpp create mode 100644 src/xrGameLA/ui/UIInvUpgrade.h create mode 100644 src/xrGameLA/ui/UIInvUpgradeInfo.cpp create mode 100644 src/xrGameLA/ui/UIInvUpgradeInfo.h create mode 100644 src/xrGameLA/ui/UIInvUpgradeProperty.cpp create mode 100644 src/xrGameLA/ui/UIInvUpgradeProperty.h create mode 100644 src/xrGameLA/ui/UIInventoryUpgradeWnd.cpp create mode 100644 src/xrGameLA/ui/UIInventoryUpgradeWnd.h create mode 100644 src/xrGameLA/ui/UIInventoryUpgradeWnd_add.cpp create mode 100644 src/xrGameLA/ui/UIInventoryUtilities.cpp create mode 100644 src/xrGameLA/ui/UIInventoryUtilities.h create mode 100644 src/xrGameLA/ui/UIInventoryWnd.cpp create mode 100644 src/xrGameLA/ui/UIInventoryWnd.h create mode 100644 src/xrGameLA/ui/UIInventoryWnd3.cpp create mode 100644 src/xrGameLA/ui/UIItemInfo.cpp create mode 100644 src/xrGameLA/ui/UIItemInfo.h create mode 100644 src/xrGameLA/ui/UIKeyBinding.cpp create mode 100644 src/xrGameLA/ui/UIKeyBinding.h create mode 100644 src/xrGameLA/ui/UILabel.cpp create mode 100644 src/xrGameLA/ui/UILabel.h create mode 100644 src/xrGameLA/ui/UILanimController.cpp create mode 100644 src/xrGameLA/ui/UILanimController.h create mode 100644 src/xrGameLA/ui/UILine.cpp create mode 100644 src/xrGameLA/ui/UILine.h create mode 100644 src/xrGameLA/ui/UILines.cpp create mode 100644 src/xrGameLA/ui/UILines.h create mode 100644 src/xrGameLA/ui/UIListBox.cpp create mode 100644 src/xrGameLA/ui/UIListBox.h create mode 100644 src/xrGameLA/ui/UIListBoxItem.cpp create mode 100644 src/xrGameLA/ui/UIListBoxItem.h create mode 100644 src/xrGameLA/ui/UIListBoxItemEx.cpp create mode 100644 src/xrGameLA/ui/UIListBoxItemEx.h create mode 100644 src/xrGameLA/ui/UIListBoxItemMsgChain.cpp create mode 100644 src/xrGameLA/ui/UIListBoxItemMsgChain.h create mode 100644 src/xrGameLA/ui/UIListBox_script.cpp create mode 100644 src/xrGameLA/ui/UIListItem.cpp create mode 100644 src/xrGameLA/ui/UIListItem.h create mode 100644 src/xrGameLA/ui/UIListItemEx.cpp create mode 100644 src/xrGameLA/ui/UIListItemEx.h create mode 100644 src/xrGameLA/ui/UIListWnd.cpp create mode 100644 src/xrGameLA/ui/UIListWnd.h create mode 100644 src/xrGameLA/ui/UIListWnd_inline.h create mode 100644 src/xrGameLA/ui/UIListWnd_script.cpp create mode 100644 src/xrGameLA/ui/UIMMShniaga.cpp create mode 100644 src/xrGameLA/ui/UIMMShniaga.h create mode 100644 src/xrGameLA/ui/UIMainIngameWnd.cpp create mode 100644 src/xrGameLA/ui/UIMainIngameWnd.h create mode 100644 src/xrGameLA/ui/UIMap.cpp create mode 100644 src/xrGameLA/ui/UIMap.h create mode 100644 src/xrGameLA/ui/UIMapInfo.cpp create mode 100644 src/xrGameLA/ui/UIMapInfo.h create mode 100644 src/xrGameLA/ui/UIMapInfo_script.cpp create mode 100644 src/xrGameLA/ui/UIMapWnd.cpp create mode 100644 src/xrGameLA/ui/UIMapWnd.h create mode 100644 src/xrGameLA/ui/UIMapWndActions.cpp create mode 100644 src/xrGameLA/ui/UIMapWndActions.h create mode 100644 src/xrGameLA/ui/UIMapWndActionsSpace.h create mode 100644 src/xrGameLA/ui/UIMessageBox.cpp create mode 100644 src/xrGameLA/ui/UIMessageBox.h create mode 100644 src/xrGameLA/ui/UIMessageBoxEx.cpp create mode 100644 src/xrGameLA/ui/UIMessageBoxEx.h create mode 100644 src/xrGameLA/ui/UIMessageBox_script.cpp create mode 100644 src/xrGameLA/ui/UIMessages.h create mode 100644 src/xrGameLA/ui/UIMessagesWindow.cpp create mode 100644 src/xrGameLA/ui/UIMessagesWindow.h create mode 100644 src/xrGameLA/ui/UIMoneyIndicator.cpp create mode 100644 src/xrGameLA/ui/UIMoneyIndicator.h create mode 100644 src/xrGameLA/ui/UIMotionIcon.cpp create mode 100644 src/xrGameLA/ui/UIMotionIcon.h create mode 100644 src/xrGameLA/ui/UIMpItemsStoreWnd.cpp create mode 100644 src/xrGameLA/ui/UIMpItemsStoreWnd.h create mode 100644 src/xrGameLA/ui/UIMultiTextStatic.cpp create mode 100644 src/xrGameLA/ui/UIMultiTextStatic.h create mode 100644 src/xrGameLA/ui/UINewsItemWnd.cpp create mode 100644 src/xrGameLA/ui/UINewsItemWnd.h create mode 100644 src/xrGameLA/ui/UINewsWnd.cpp create mode 100644 src/xrGameLA/ui/UINewsWnd.h create mode 100644 src/xrGameLA/ui/UIOptionsItem.cpp create mode 100644 src/xrGameLA/ui/UIOptionsItem.h create mode 100644 src/xrGameLA/ui/UIOptionsManager.cpp create mode 100644 src/xrGameLA/ui/UIOptionsManager.h create mode 100644 src/xrGameLA/ui/UIOptionsManagerScript.cpp create mode 100644 src/xrGameLA/ui/UIOptionsManagerScript.h create mode 100644 src/xrGameLA/ui/UIOutfitParams.cpp create mode 100644 src/xrGameLA/ui/UIOutfitParams.h create mode 100644 src/xrGameLA/ui/UIOutfitSlot.cpp create mode 100644 src/xrGameLA/ui/UIOutfitSlot.h create mode 100644 src/xrGameLA/ui/UIPdaAux.cpp create mode 100644 src/xrGameLA/ui/UIPdaAux.h create mode 100644 src/xrGameLA/ui/UIPdaContactsWnd.cpp create mode 100644 src/xrGameLA/ui/UIPdaContactsWnd.h create mode 100644 src/xrGameLA/ui/UIPdaKillMessage.cpp create mode 100644 src/xrGameLA/ui/UIPdaKillMessage.h create mode 100644 src/xrGameLA/ui/UIPdaListItem.cpp create mode 100644 src/xrGameLA/ui/UIPdaListItem.h create mode 100644 src/xrGameLA/ui/UIPdaMsgListItem.cpp create mode 100644 src/xrGameLA/ui/UIPdaMsgListItem.h create mode 100644 src/xrGameLA/ui/UIPdaWnd.cpp create mode 100644 src/xrGameLA/ui/UIPdaWnd.h create mode 100644 src/xrGameLA/ui/UIPointerGage.cpp create mode 100644 src/xrGameLA/ui/UIPointerGage.h create mode 100644 src/xrGameLA/ui/UIProgressBar.cpp create mode 100644 src/xrGameLA/ui/UIProgressBar.h create mode 100644 src/xrGameLA/ui/UIProgressBar_script.cpp create mode 100644 src/xrGameLA/ui/UIProgressShape.cpp create mode 100644 src/xrGameLA/ui/UIProgressShape.h create mode 100644 src/xrGameLA/ui/UIPropertiesBox.cpp create mode 100644 src/xrGameLA/ui/UIPropertiesBox.h create mode 100644 src/xrGameLA/ui/UIPropertiesBox_script.cpp create mode 100644 src/xrGameLA/ui/UIRadioButton.cpp create mode 100644 src/xrGameLA/ui/UIRadioButton.h create mode 100644 src/xrGameLA/ui/UIRankIndicator.cpp create mode 100644 src/xrGameLA/ui/UIRankIndicator.h create mode 100644 src/xrGameLA/ui/UIScriptWnd.cpp create mode 100644 src/xrGameLA/ui/UIScriptWnd.h create mode 100644 src/xrGameLA/ui/UIScriptWnd_script.cpp create mode 100644 src/xrGameLA/ui/UIScrollBar.cpp create mode 100644 src/xrGameLA/ui/UIScrollBar.h create mode 100644 src/xrGameLA/ui/UIScrollBox.cpp create mode 100644 src/xrGameLA/ui/UIScrollBox.h create mode 100644 src/xrGameLA/ui/UIScrollView.cpp create mode 100644 src/xrGameLA/ui/UIScrollView.h create mode 100644 src/xrGameLA/ui/UISkinSelector.cpp create mode 100644 src/xrGameLA/ui/UISkinSelector.h create mode 100644 src/xrGameLA/ui/UISpawnWnd.cpp create mode 100644 src/xrGameLA/ui/UISpawnWnd.h create mode 100644 src/xrGameLA/ui/UISpinNum.cpp create mode 100644 src/xrGameLA/ui/UISpinNum.h create mode 100644 src/xrGameLA/ui/UISpinText.cpp create mode 100644 src/xrGameLA/ui/UISpinText.h create mode 100644 src/xrGameLA/ui/UIStalkersRankingWnd.cpp create mode 100644 src/xrGameLA/ui/UIStalkersRankingWnd.h create mode 100644 src/xrGameLA/ui/UIStatic.cpp create mode 100644 src/xrGameLA/ui/UIStatic.h create mode 100644 src/xrGameLA/ui/UIStatic_script.cpp create mode 100644 src/xrGameLA/ui/UIStatix.cpp create mode 100644 src/xrGameLA/ui/UIStatix.h create mode 100644 src/xrGameLA/ui/UISubLine.cpp create mode 100644 src/xrGameLA/ui/UISubLine.h create mode 100644 src/xrGameLA/ui/UITabButton.cpp create mode 100644 src/xrGameLA/ui/UITabButton.h create mode 100644 src/xrGameLA/ui/UITabButtonMP.cpp create mode 100644 src/xrGameLA/ui/UITabButtonMP.h create mode 100644 src/xrGameLA/ui/UITabControl.cpp create mode 100644 src/xrGameLA/ui/UITabControl.h create mode 100644 src/xrGameLA/ui/UITabControl_script.cpp create mode 100644 src/xrGameLA/ui/UITalkDialogWnd.cpp create mode 100644 src/xrGameLA/ui/UITalkDialogWnd.h create mode 100644 src/xrGameLA/ui/UITalkWnd.cpp create mode 100644 src/xrGameLA/ui/UITalkWnd.h create mode 100644 src/xrGameLA/ui/UITaskDescrWnd.cpp create mode 100644 src/xrGameLA/ui/UITaskDescrWnd.h create mode 100644 src/xrGameLA/ui/UITaskItem.cpp create mode 100644 src/xrGameLA/ui/UITaskItem.h create mode 100644 src/xrGameLA/ui/UITaskWnd.cpp create mode 100644 src/xrGameLA/ui/UITaskWnd.h create mode 100644 src/xrGameLA/ui/UITextBanner.cpp create mode 100644 src/xrGameLA/ui/UITextBanner.h create mode 100644 src/xrGameLA/ui/UITextureMaster.cpp create mode 100644 src/xrGameLA/ui/UITextureMaster.h create mode 100644 src/xrGameLA/ui/UITrackBar.cpp create mode 100644 src/xrGameLA/ui/UITrackBar.h create mode 100644 src/xrGameLA/ui/UITrackBarVariable.cpp create mode 100644 src/xrGameLA/ui/UITrackBarVariable.h create mode 100644 src/xrGameLA/ui/UITrackButton.cpp create mode 100644 src/xrGameLA/ui/UITrackButton.h create mode 100644 src/xrGameLA/ui/UITradeWnd.cpp create mode 100644 src/xrGameLA/ui/UITradeWnd.h create mode 100644 src/xrGameLA/ui/UITreeViewBoxItem.cpp create mode 100644 src/xrGameLA/ui/UITreeViewBoxItem.h create mode 100644 src/xrGameLA/ui/UITreeViewItem.cpp create mode 100644 src/xrGameLA/ui/UITreeViewItem.h create mode 100644 src/xrGameLA/ui/UIUpgradeWnd.cpp create mode 100644 src/xrGameLA/ui/UIUpgradeWnd.h create mode 100644 src/xrGameLA/ui/UIVideoPlayerWnd.cpp create mode 100644 src/xrGameLA/ui/UIVideoPlayerWnd.h create mode 100644 src/xrGameLA/ui/UIVoteStatusWnd.cpp create mode 100644 src/xrGameLA/ui/UIVoteStatusWnd.h create mode 100644 src/xrGameLA/ui/UIWeatherEditor.cpp create mode 100644 src/xrGameLA/ui/UIWeatherEditor.h create mode 100644 src/xrGameLA/ui/UIWindow.cpp create mode 100644 src/xrGameLA/ui/UIWindow.h create mode 100644 src/xrGameLA/ui/UIWindow_script.cpp create mode 100644 src/xrGameLA/ui/UIWndCallback.cpp create mode 100644 src/xrGameLA/ui/UIWndCallback.h create mode 100644 src/xrGameLA/ui/UIWpnParams.cpp create mode 100644 src/xrGameLA/ui/UIWpnParams.h create mode 100644 src/xrGameLA/ui/UIXmlInit.cpp create mode 100644 src/xrGameLA/ui/UIXmlInit.h create mode 100644 src/xrGameLA/ui/UI_IB_FrameLineWnd.cpp create mode 100644 src/xrGameLA/ui/UI_IB_FrameLineWnd.h create mode 100644 src/xrGameLA/ui/UI_IB_Static.cpp create mode 100644 src/xrGameLA/ui/UI_IB_Static.h create mode 100644 src/xrGameLA/ui/map_hint.cpp create mode 100644 src/xrGameLA/ui/map_hint.h create mode 100644 src/xrGameLA/ui/pch_script.h create mode 100644 src/xrGameLA/ui/stdafx.h create mode 100644 src/xrGameLA/ui/uiabstract.h create mode 100644 src/xrGameLA/ui/uiinventorywnd2.cpp create mode 100644 src/xrGameLA/ui/uilinestd.h create mode 100644 src/xrGameLA/ui/uiscriptwnd_script.h create mode 100644 src/xrGameLA/ui/uiscriptwnd_script2.cpp create mode 100644 src/xrGameLA/ui/xrUIXmlParser.cpp create mode 100644 src/xrGameLA/ui/xrUIXmlParser.h create mode 100644 src/xrGameLA/ui_base.cpp create mode 100644 src/xrGameLA/ui_base.h create mode 100644 src/xrGameLA/ui_defs.h create mode 100644 src/xrGameLA/ui_export_script.cpp create mode 100644 src/xrGameLA/vertex_allocator_fixed.h create mode 100644 src/xrGameLA/vertex_allocator_fixed_inline.h create mode 100644 src/xrGameLA/vertex_manager_fixed.h create mode 100644 src/xrGameLA/vertex_manager_fixed_inline.h create mode 100644 src/xrGameLA/vertex_manager_hash_fixed.h create mode 100644 src/xrGameLA/vertex_manager_hash_fixed_inline.h create mode 100644 src/xrGameLA/vertex_path.h create mode 100644 src/xrGameLA/vertex_path_inline.h create mode 100644 src/xrGameLA/vision_client.cpp create mode 100644 src/xrGameLA/vision_client.h create mode 100644 src/xrGameLA/vision_client_inline.h create mode 100644 src/xrGameLA/visual_memory_manager.cpp create mode 100644 src/xrGameLA/visual_memory_manager.h create mode 100644 src/xrGameLA/visual_memory_manager_inline.h create mode 100644 src/xrGameLA/visual_memory_params.cpp create mode 100644 src/xrGameLA/visual_memory_params.h create mode 100644 src/xrGameLA/wallmark_manager.cpp create mode 100644 src/xrGameLA/wallmark_manager.h create mode 100644 src/xrGameLA/weaponBM16.cpp create mode 100644 src/xrGameLA/weaponBM16.h create mode 100644 src/xrGameLA/weaponBM16_script.cpp create mode 100644 src/xrGameLA/weapon_bobbing.cpp create mode 100644 src/xrGameLA/weapon_bobbing.h create mode 100644 src/xrGameLA/weapon_collision.cpp create mode 100644 src/xrGameLA/weapon_collision.h create mode 100644 src/xrGameLA/weaponshotgun_script.cpp create mode 100644 src/xrGameLA/wrapper_abstract.h create mode 100644 src/xrGameLA/wrapper_abstract_inline.h create mode 100644 src/xrGameLA/xml_str_id_loader.h create mode 100644 src/xrGameLA/xrClientsPool.cpp create mode 100644 src/xrGameLA/xrClientsPool.h create mode 100644 src/xrGameLA/xrEProps.h create mode 100644 src/xrGameLA/xrGame.cpp create mode 100644 src/xrGameLA/xrMessages.h create mode 100644 src/xrGameLA/xrServer.cpp create mode 100644 src/xrGameLA/xrServer.h create mode 100644 src/xrGameLA/xrServer_CL_connect.cpp create mode 100644 src/xrGameLA/xrServer_CL_disconnect.cpp create mode 100644 src/xrGameLA/xrServer_Connect.cpp create mode 100644 src/xrGameLA/xrServer_Disconnect.cpp create mode 100644 src/xrGameLA/xrServer_Factory.cpp create mode 100644 src/xrGameLA/xrServer_Object_Base.cpp create mode 100644 src/xrGameLA/xrServer_Object_Base.h create mode 100644 src/xrGameLA/xrServer_Objects.cpp create mode 100644 src/xrGameLA/xrServer_Objects.h create mode 100644 src/xrGameLA/xrServer_Objects_ALife.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife.h create mode 100644 src/xrGameLA/xrServer_Objects_ALife_All.h create mode 100644 src/xrGameLA/xrServer_Objects_ALife_Items.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife_Items.h create mode 100644 src/xrGameLA/xrServer_Objects_ALife_Items_script.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife_Items_script2.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife_Items_script3.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife_Monsters.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife_Monsters.h create mode 100644 src/xrGameLA/xrServer_Objects_ALife_Monsters_script.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife_Monsters_script2.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife_Monsters_script4.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife_script.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife_script2.cpp create mode 100644 src/xrGameLA/xrServer_Objects_ALife_script3.cpp create mode 100644 src/xrGameLA/xrServer_Objects_Abstract.cpp create mode 100644 src/xrGameLA/xrServer_Objects_Abstract.h create mode 100644 src/xrGameLA/xrServer_Objects_script.cpp create mode 100644 src/xrGameLA/xrServer_Objects_script2.cpp create mode 100644 src/xrGameLA/xrServer_Space.h create mode 100644 src/xrGameLA/xrServer_balance.cpp create mode 100644 src/xrGameLA/xrServer_info.cpp create mode 100644 src/xrGameLA/xrServer_info.h create mode 100644 src/xrGameLA/xrServer_perform_GameExport.cpp create mode 100644 src/xrGameLA/xrServer_perform_RPgen.cpp create mode 100644 src/xrGameLA/xrServer_perform_migration.cpp create mode 100644 src/xrGameLA/xrServer_perform_sls_default.cpp create mode 100644 src/xrGameLA/xrServer_perform_sls_load.cpp create mode 100644 src/xrGameLA/xrServer_perform_sls_save.cpp create mode 100644 src/xrGameLA/xrServer_perform_transfer.cpp create mode 100644 src/xrGameLA/xrServer_process_event.cpp create mode 100644 src/xrGameLA/xrServer_process_event_activate.cpp create mode 100644 src/xrGameLA/xrServer_process_event_destroy.cpp create mode 100644 src/xrGameLA/xrServer_process_event_ownership.cpp create mode 100644 src/xrGameLA/xrServer_process_event_reject.cpp create mode 100644 src/xrGameLA/xrServer_process_spawn.cpp create mode 100644 src/xrGameLA/xrServer_process_update.cpp create mode 100644 src/xrGameLA/xrServer_script_macroses.h create mode 100644 src/xrGameLA/xrServer_sls_clear.cpp create mode 100644 src/xrGameLA/xrServer_updates_compressor.cpp create mode 100644 src/xrGameLA/xrServer_updates_compressor.h create mode 100644 src/xrGameLA/xr_Client_BattlEye.cpp create mode 100644 src/xrGameLA/xr_Client_BattlEye.h create mode 100644 src/xrGameLA/xr_Server_BattlEye.cpp create mode 100644 src/xrGameLA/xr_Server_BattlEye.h create mode 100644 src/xrGameLA/xr_level_controller.cpp create mode 100644 src/xrGameLA/xr_level_controller.h create mode 100644 src/xrGameLA/xr_time.cpp create mode 100644 src/xrGameLA/xr_time.h create mode 100644 src/xrGameLA/xrgame_dll_detach.cpp create mode 100644 src/xrGameLA/xrserver_objects_alife_monsters_script3.cpp create mode 100644 src/xrGameLA/zone_effector.cpp create mode 100644 src/xrGameLA/zone_effector.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f1fc0ac9c..14446162e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,7 @@ if(NOT IXRAY_COMPRESSOR_ONLY) if(IXR_TEST_CI) add_subdirectory("Layers/xrRenderPC_R4") add_subdirectory("xrGame") + add_subdirectory("xrGameLA") endif() endif() @@ -57,6 +58,7 @@ if(NOT IXRAY_COMPRESSOR_ONLY) if(IXR_TEST_CI) set_target_properties(xrGame PROPERTIES ${IXRAY_FOLDER_GAME}) + set_target_properties(xrGameLA PROPERTIES ${IXRAY_FOLDER_GAME}) endif() endif() diff --git a/src/xrGameLA/AI_PhraseDialogManager.cpp b/src/xrGameLA/AI_PhraseDialogManager.cpp new file mode 100644 index 000000000..e3f6deaec --- /dev/null +++ b/src/xrGameLA/AI_PhraseDialogManager.cpp @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////// +// AI_PhraseDialogManager.cpp +// Класс, от которого наследуются NPC персонажи, ведущие диалог +// с актером +// +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "AI_PhraseDialogManager.h" +#include "PhraseDialog.h" +#include "inventoryowner.h" +#include "character_info.h" +#include "gameobject.h" +#include "relation_registry.h" + +CAI_PhraseDialogManager::CAI_PhraseDialogManager (void) +{ + m_sStartDialog = m_sDefaultStartDialog = NULL; +} + +CAI_PhraseDialogManager::~CAI_PhraseDialogManager (void) +{} + +//PhraseDialogManager +void CAI_PhraseDialogManager::ReceivePhrase (DIALOG_SHARED_PTR& phrase_dialog) +{ + AnswerPhrase(phrase_dialog); + CPhraseDialogManager::ReceivePhrase(phrase_dialog); +} +#include "uigamesp.h" +#include "hudmanager.h" +#include "level.h" +#include "ui/UItalkWnd.h" + +void CAI_PhraseDialogManager::AnswerPhrase (DIALOG_SHARED_PTR& phrase_dialog) +{ + CInventoryOwner* pInvOwner = smart_cast(this); + THROW (pInvOwner); + CGameObject* pOthersGO = smart_cast(phrase_dialog->OurPartner(this)); + THROW (pOthersGO); + CInventoryOwner* pOthersIO = smart_cast(pOthersGO); + THROW (pOthersIO); + + if(!phrase_dialog->IsFinished()) + { + CHARACTER_GOODWILL attitude = RELATION_REGISTRY().GetAttitude(pOthersIO, pInvOwner); + + xr_vector phrases; + CHARACTER_GOODWILL phrase_goodwill = NO_GOODWILL; + //если не найдем более подходяещей выводим фразу + //последнюю из списка (самую грубую) + int phrase_num = phrase_dialog->PhraseList().size()-1; + for(u32 i=0; iPhraseList().size(); ++i) + { + phrase_goodwill = phrase_dialog->PhraseList()[phrase_num]->GoodwillLevel(); + if(attitude >= phrase_goodwill) + { + phrase_num = i; + break; + } + } + + for(i=0; iPhraseList().size(); i++) + { + if(phrase_goodwill == phrase_dialog->PhraseList()[phrase_num]->GoodwillLevel()) + phrases.push_back(i); + } + + phrase_num = phrases[Random.randI(0, phrases.size())]; + + shared_str phrase_id = phrase_dialog->PhraseList()[phrase_num]->GetID(); + + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + pGameSP->TalkMenu->AddAnswer (phrase_dialog->GetPhraseText(phrase_id), pInvOwner->Name()); + + CPhraseDialogManager::SayPhrase(phrase_dialog, phrase_id); + } +} + + + +void CAI_PhraseDialogManager::SetStartDialog(shared_str phrase_dialog) +{ + m_sStartDialog = phrase_dialog; +} + +void CAI_PhraseDialogManager::SetDefaultStartDialog(shared_str phrase_dialog) +{ + m_sDefaultStartDialog = phrase_dialog; +} + +void CAI_PhraseDialogManager::RestoreDefaultStartDialog() +{ + m_sStartDialog = m_sDefaultStartDialog; +} + + +void CAI_PhraseDialogManager::UpdateAvailableDialogs (CPhraseDialogManager* partner) +{ + m_AvailableDialogs.clear(); + m_CheckedDialogs.clear(); + + if(*m_sStartDialog) + inherited::AddAvailableDialog(*m_sStartDialog, partner); + inherited::AddAvailableDialog("hello_dialog", partner); + + inherited::UpdateAvailableDialogs(partner); +} \ No newline at end of file diff --git a/src/xrGameLA/AI_PhraseDialogManager.h b/src/xrGameLA/AI_PhraseDialogManager.h new file mode 100644 index 000000000..c73525c15 --- /dev/null +++ b/src/xrGameLA/AI_PhraseDialogManager.h @@ -0,0 +1,39 @@ +/////////////////////////////////////////////////////////////// +// AI_PhraseDialogManager.h +// Класс, от которого наследуются NPC персонажи, ведущие диалог +// с актером +// +/////////////////////////////////////////////////////////////// + +#pragma once + +#include "PhraseDialogManager.h" + + +class CAI_PhraseDialogManager: public CPhraseDialogManager +{ +private: + typedef CPhraseDialogManager inherited; +public: + CAI_PhraseDialogManager (); + virtual ~CAI_PhraseDialogManager (); + + virtual void ReceivePhrase (DIALOG_SHARED_PTR& phrase_dialog); + virtual void UpdateAvailableDialogs (CPhraseDialogManager* partner); + virtual void AnswerPhrase (DIALOG_SHARED_PTR& phrase_dialog); + + + virtual void SetStartDialog (shared_str phrase_dialog); + virtual void SetDefaultStartDialog (shared_str phrase_dialog); + virtual shared_str GetStartDialog () {return m_sStartDialog;} + virtual void RestoreDefaultStartDialog (); +protected: + //диалог, если не NULL, то его персонаж запустит + //при встрече с актером + shared_str m_sStartDialog; + shared_str m_sDefaultStartDialog; + + DEFINE_VECTOR(DIALOG_SHARED_PTR, DIALOG_SHARED_VECTOR, DIALOG_SHARED_IT); + //список диалогов, на которые нужно ответить + DIALOG_SHARED_VECTOR m_PendingDialogs; +}; \ No newline at end of file diff --git a/src/xrGameLA/AcidFog.cpp b/src/xrGameLA/AcidFog.cpp new file mode 100644 index 000000000..1bee51944 --- /dev/null +++ b/src/xrGameLA/AcidFog.cpp @@ -0,0 +1,120 @@ +///////////////////////////////////// +// file name: AcidFog.cpp +// author: lost alpha +// created: 18/08/2011 +// edited: 18/08/2011 +// purpose: rusty fog anomaly +////////////////////////////////////// +#include "stdafx.h" +#include "AcidFog.h" +#include "hudmanager.h" +#include "ParticlesObject.h" +#include "level.h" +#include "actor.h" +#include "inventory.h" +#include "physicsshellholder.h" +#include "../xr_collide_form.h" + +#define DEFAULT_RUST_DAMAGE 0.15f + +CRustyFog::CRustyFog(void) +{ + m_dwDeltaTime = 0; + m_fHitImpulseScale = 1.f; + + m_bLastBlowoutUpdate = false; +} + +CRustyFog::~CRustyFog(void) +{ +} + +void CRustyFog::Load(LPCSTR section) +{ + inherited::Load(section); + m_rust_damage = READ_IF_EXISTS(pSettings, r_float, section, "rusty_damage", DEFAULT_RUST_DAMAGE); +} + + +void CRustyFog::Postprocess(f32 /**val/**/) +{ +} + +bool CRustyFog::BlowoutState() +{ + bool result = inherited::BlowoutState(); + if(!result) + { + m_bLastBlowoutUpdate = false; + UpdateBlowout(); + } + else if(!m_bLastBlowoutUpdate) + { + m_bLastBlowoutUpdate = true; + UpdateBlowout(); + } + + return result; +} + +void CRustyFog::Affect(SZoneObjectInfo* O) +{ + CPhysicsShellHolder *pGameObject = smart_cast(O->object); + if(!pGameObject) return; + + if(O->zone_ignore) return; + + Fvector P; + XFORM().transform_tiny(P,CFORM()->getSphere().P); + +#ifdef DEBUG + char l_pow[255]; + xr_sprintf(l_pow, "zone hit. %.1f", Power(pGameObject->Position().distance_to(P))); + if(bDebug) Msg("%s %s",*pGameObject->cName(), l_pow); +#endif + + Fvector hit_dir; + hit_dir.set(::Random.randF(-.5f,.5f), + ::Random.randF(.0f,1.f), + ::Random.randF(-.5f,.5f)); + hit_dir.normalize(); + + + Fvector position_in_bone_space; + + VERIFY(!pGameObject->getDestroy()); + + float dist = pGameObject->Position().distance_to(P) - pGameObject->Radius(); + float power = Power(dist>0.f?dist:0.f); + float impulse = m_fHitImpulseScale*power*pGameObject->GetMass(); + + //статистика по объекту + O->total_damage += power; + O->hit_num++; + + if(power > 0.01f) + { + m_dwDeltaTime = 0; + position_in_bone_space.set(0.f,0.f,0.f); + + CreateHit(pGameObject->ID(),ID(),hit_dir,power,0,position_in_bone_space,impulse,m_eHitTypeBlowout); + + PlayHitParticles(pGameObject); + } + if (pGameObject->ID() == Actor()->ID()) + RustyItems(); +} + +#define RUSTY_ITEM(item, val) \ + if (item) \ + item->ChangeCondition(item->GetCondition() - val); \ + + +void CRustyFog::RustyItems() +{ + auto& slots = Actor()->inventory().m_slots; + RUSTY_ITEM(slots[PISTOL_SLOT].m_pIItem, m_rust_damage); + RUSTY_ITEM(slots[RIFLE_SLOT].m_pIItem, m_rust_damage); + RUSTY_ITEM(slots[RIFLE_2_SLOT].m_pIItem, m_rust_damage); + RUSTY_ITEM(slots[OUTFIT_SLOT].m_pIItem, m_rust_damage); +} \ No newline at end of file diff --git a/src/xrGameLA/AcidFog.h b/src/xrGameLA/AcidFog.h new file mode 100644 index 000000000..b835505a0 --- /dev/null +++ b/src/xrGameLA/AcidFog.h @@ -0,0 +1,39 @@ +///////////////////////////////////// +// file name: AcidFog.h +// author: lost alpha +// created: 18/08/2011 +// edited: 18/08/2011 +// purpose: rusty fog anomaly +////////////////////////////////////// +#pragma once + +#include "customzone.h" +#include "script_export_space.h" + +class CRustyFog : public CCustomZone +{ +private: + typedef CCustomZone inherited; +public: + CRustyFog(void); + virtual ~CRustyFog(void); + + virtual void Load(LPCSTR section); + virtual void Postprocess(f32 val); + virtual bool EnableEffector() {return true;} + + + virtual void Affect(SZoneObjectInfo* O); + +protected: + virtual bool BlowoutState(); + bool m_bLastBlowoutUpdate; +private: + void RustyItems(); + float m_rust_damage; +public: + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CRustyFog) +#undef script_type_list +#define script_type_list save_type_list(CRustyFog) \ No newline at end of file diff --git a/src/xrGameLA/Actor.cpp b/src/xrGameLA/Actor.cpp new file mode 100644 index 000000000..b76a9f8fa --- /dev/null +++ b/src/xrGameLA/Actor.cpp @@ -0,0 +1,1935 @@ +#include "pch_script.h" +#include "Actor_Flags.h" +#include "hudmanager.h" +#ifdef DEBUG +# include "ode_include.h" +# include "../StatGraph.h" +# include "PHDebug.h" +#endif // DEBUG +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "Car.h" +#include "xrserver_objects_alife_monsters.h" +#include "CameraLook.h" +#include "CameraFirstEye.h" +#include "effectorfall.h" +#include "EffectorBobbing.h" +#include "clsid_game.h" +#include "ActorEffector.h" +#include "EffectorZoomInertion.h" +#include "SleepEffector.h" +#include "character_info.h" +#include "CustomOutfit.h" +#include "actorcondition.h" +#include "UIGameCustom.h" +#include "game_cl_base_weapon_usage_statistic.h" + +// breakpoints +#include "../xr_input.h" + +// +#include "Actor.h" +#include "ActorAnimation.h" +#include "actor_anim_defs.h" +#include "HudItem.h" +#include "ai_sounds.h" +#include "ai_space.h" +#include "trade.h" +#include "inventory.h" +#include "Physics.h" +#include "level.h" +#include "GamePersistent.h" +#include "game_cl_base.h" +#include "game_cl_single.h" +#include "xrmessages.h" +#include "string_table.h" +#include "usablescriptobject.h" +#include "../cl_intersect.h" +#include "ExtendedGeom.h" +#include "alife_registry_wrappers.h" +#include "../../Include/xrRender/KinematicsAnimated.h" +#include "../../Include/xrRender/Kinematics.h" +#include "artifact.h" +#include "CharacterPhysicsSupport.h" +#include "material_manager.h" +#include "IColisiondamageInfo.h" +#include "ui/UIMainIngameWnd.h" +#include "ui/UIArtefactPanel.h" +#include "map_manager.h" +#include "GameTaskManager.h" +#include "actor_memory.h" +#include "Script_Game_Object.h" +#include "Game_Object_Space.h" +#include "script_callback_ex.h" +#include "InventoryBox.h" +#include "mounted_turret.h" +#include "location_manager.h" +#include "player_hud.h" + +const u32 patch_frames = 50; +const float respawn_delay = 1.f; +const float respawn_auto = 7.f; + +static float IReceived = 0; +static float ICoincidenced = 0; + + +//skeleton + + + +static Fbox bbStandBox; +static Fbox bbCrouchBox; +static Fvector vFootCenter; +static Fvector vFootExt; + +Flags32 psActorFlags={0}; + + + +CActor::CActor() : CEntityAlive() +{ + encyclopedia_registry = new CEncyclopediaRegistryWrapper(); + game_news_registry = new CGameNewsRegistryWrapper(); + // Cameras + cameras[eacFirstEye] = new CCameraFirstEye(this, CCameraBase::flPositionRigid); + cameras[eacFirstEye]->Load("actor_firsteye_cam"); + + psActorFlags.set(AF_PSP, TRUE); + + cameras[eacLookAt] = new CCameraLook2(this); + cameras[eacLookAt]->Load("actor_look_cam"); + + cameras[eacFreeLook] = new CCameraLook(this); + cameras[eacFreeLook]->Load("actor_free_cam"); + + cam_active = eacFirstEye; + fPrevCamPos = 0.0f; + vPrevCamDir.set (0.f,0.f,1.f); + fCurAVelocity = 0.0f; + // эффекторы + pCamBobbing = 0; + m_pSleepEffector = NULL; + m_pSleepEffectorPP = NULL; + + + r_torso.yaw = 0; + r_torso.pitch = 0; + r_torso.roll = 0; + r_torso_tgt_roll = 0; + r_model_yaw = 0; + r_model_yaw_delta = 0; + r_model_yaw_dest = 0; + + b_DropActivated = 0; + f_DropPower = 0.f; + + m_fRunFactor = 2.f; + m_fCrouchFactor = 0.2f; + m_fClimbFactor = 1.f; + m_fCamHeightFactor = 0.87f; + + m_fRunFactorAdditional = 0.f; + m_fSprintFactorAdditional = 0.f; + + m_fFallTime = s_fFallTime; + m_bAnimTorsoPlayed = false; + b_saveAllowed = true; + + m_pPhysicsShell = NULL; + + m_holder = NULL; + m_holderID = u16(-1); + + +#ifdef DEBUG + Device.seqRender.Add (this,REG_PRIORITY_LOW); +#endif + + //разрешить использование пояса в inventory + inventory().SetBeltUseful(true); + + m_pPersonWeLookingAt = NULL; + m_pHolderWeLookingAt = NULL; + m_pObjectWeLookingAt = NULL; + m_bPickupMode = false; + + pStatGraph = NULL; + + m_pActorEffector = NULL; + + m_bZoomAimingMode = false; + + m_sDefaultObjAction = NULL; + + m_fSprintFactor = 4.f; + + m_fImmunityCoef = 1.f; + m_fDispersionCoef = 1.f; + m_fPriceFactor = 1.f; + m_fZoomInertCoef = 1.f; + +// TODO: Friendly Indicator + m_icons_state.zero (); + m_pUsableObject = NULL; + + + m_anims = new SActorMotions(); + m_vehicle_anims = new SActorVehicleAnims(); + m_entity_condition = NULL; + m_iLastHitterID = u16(-1); + m_iLastHittingWeaponID = u16(-1); + m_game_task_manager = NULL; + m_statistic_manager = NULL; + //----------------------------------------------------------------------------------- + m_memory = g_dedicated_server ? 0 : new CActorMemory(this); + m_bOutBorder = false; + hit_probability = 1.f; + m_feel_touch_characters = 0; + //----------------------------------------------------------------------------------- + m_dwILastUpdateTime = 0; + + m_location_manager = new CLocationManager(this); + m_current_torch = 0; +// m_torch_battery_duration = 0; + inventory().SetCurrentDetector(NULL); + m_secondRifleSlotAllowed = false; + + trySprintCounter_ = 0; + + maxHudWetness_ = 0.7f; + wetnessAccmBase_ = 0.01f; + wetnessDecreaseF_ = 0.023f; +} + + +CActor::~CActor() +{ + xr_delete (m_location_manager); + + xr_delete (m_memory); + + xr_delete (encyclopedia_registry); + xr_delete (game_news_registry); +#ifdef DEBUG + Device.seqRender.Remove(this); +#endif + //xr_delete(Weapons); + for (int i=0; imovement()->CreateCharacter (); + character_physics_support()->movement()->SetPhysicsRefObject (this); + CEntityAlive::reinit (); + CInventoryOwner::reinit (); + + character_physics_support()->in_Init (); + material().reinit (); + + m_pUsableObject = NULL; + if (!g_dedicated_server) + memory().reinit (); + + set_input_external_handler (0); + m_time_lock_accel = 0; +} + +void CActor::reload (LPCSTR section) +{ + CEntityAlive::reload (section); + CInventoryOwner::reload (section); + material().reload (section); + CStepManager::reload (section); + if (!g_dedicated_server) + memory().reload (section); + m_location_manager->reload (section); +} + +void CActor::Load (LPCSTR section ) +{ + // Msg ("Loading actor: %s",section); + inherited::Load (section); + material().Load (section); + CInventoryOwner::Load (section); + m_location_manager->Load (section); + + if (GameID() == GAME_SINGLE) + OnDifficultyChanged (); + ////////////////////////////////////////////////////////////////////////// + ISpatial* self = smart_cast (this); + if (self) { + self->spatial.type |= STYPE_VISIBLEFORAI; + self->spatial.type &= ~STYPE_REACTTOSOUND; + } + ////////////////////////////////////////////////////////////////////////// + + // m_PhysicMovementControl: General + //m_PhysicMovementControl->SetParent (this); + Fbox bb;Fvector vBOX_center,vBOX_size; + // m_PhysicMovementControl: BOX + vBOX_center= pSettings->r_fvector3 (section,"ph_box2_center" ); + vBOX_size = pSettings->r_fvector3 (section,"ph_box2_size" ); + bb.set (vBOX_center,vBOX_center); bb.grow(vBOX_size); + character_physics_support()->movement()->SetBox (2,bb); + + // m_PhysicMovementControl: BOX + vBOX_center= pSettings->r_fvector3 (section,"ph_box1_center" ); + vBOX_size = pSettings->r_fvector3 (section,"ph_box1_size" ); + bb.set (vBOX_center,vBOX_center); bb.grow(vBOX_size); + character_physics_support()->movement()->SetBox (1,bb); + + // m_PhysicMovementControl: BOX + vBOX_center= pSettings->r_fvector3 (section,"ph_box0_center" ); + vBOX_size = pSettings->r_fvector3 (section,"ph_box0_size" ); + bb.set (vBOX_center,vBOX_center); bb.grow(vBOX_size); + character_physics_support()->movement()->SetBox (0,bb); + + //// m_PhysicMovementControl: Foots + //Fvector vFOOT_center= pSettings->r_fvector3 (section,"ph_foot_center" ); + //Fvector vFOOT_size = pSettings->r_fvector3 (section,"ph_foot_size" ); + //bb.set (vFOOT_center,vFOOT_center); bb.grow(vFOOT_size); + ////m_PhysicMovementControl->SetFoots (vFOOT_center,vFOOT_size); + + // m_PhysicMovementControl: Crash speed and mass + float cs_min = pSettings->r_float (section,"ph_crash_speed_min" ); + float cs_max = pSettings->r_float (section,"ph_crash_speed_max" ); + float mass = pSettings->r_float (section,"ph_mass" ); + character_physics_support()->movement()->SetCrashSpeeds (cs_min,cs_max); + character_physics_support()->movement()->SetMass (mass); + if(pSettings->line_exist(section,"stalker_restrictor_radius")) + character_physics_support()->movement()->SetActorRestrictorRadius(CPHCharacter::rtStalker,pSettings->r_float(section,"stalker_restrictor_radius")); + if(pSettings->line_exist(section,"stalker_small_restrictor_radius")) + character_physics_support()->movement()->SetActorRestrictorRadius(CPHCharacter::rtStalkerSmall,pSettings->r_float(section,"stalker_small_restrictor_radius")); + if(pSettings->line_exist(section,"medium_monster_restrictor_radius")) + character_physics_support()->movement()->SetActorRestrictorRadius(CPHCharacter::rtMonsterMedium,pSettings->r_float(section,"medium_monster_restrictor_radius")); + character_physics_support()->movement()->Load(section); + + + + m_fWalkAccel = pSettings->r_float(section,"walk_accel"); + m_fJumpSpeed = pSettings->r_float(section,"jump_speed"); + m_fRunFactor = pSettings->r_float(section,"run_coef"); + m_fRunBackFactor = pSettings->r_float(section,"run_back_coef"); + m_fWalkBackFactor = pSettings->r_float(section,"walk_back_coef"); + m_fCrouchFactor = pSettings->r_float(section,"crouch_coef"); + m_fClimbFactor = pSettings->r_float(section,"climb_coef"); + m_fSprintFactor = pSettings->r_float(section,"sprint_koef"); + + m_fWalk_StrafeFactor = READ_IF_EXISTS(pSettings, r_float, section, "walk_strafe_coef", 1.0f); + m_fRun_StrafeFactor = READ_IF_EXISTS(pSettings, r_float, section, "run_strafe_coef", 1.0f); + + m_fRunFactorAdditionalLimit = READ_IF_EXISTS(pSettings, r_float, section, "artifact_run_speed_limit", m_fRunFactor); + m_fSprintFactorAdditionalLimit = READ_IF_EXISTS(pSettings, r_float, section, "artifact_sprint_speed_limit", m_fSprintFactor); + m_fJumpFactorAdditionalLimit = READ_IF_EXISTS(pSettings, r_float, section, "artifact_jump_speed_limit", m_fJumpSpeed); + + m_fCamHeightFactor = pSettings->r_float(section,"camera_height_factor"); + character_physics_support()->movement() ->SetJumpUpVelocity(m_fJumpSpeed); + float AirControlParam = pSettings->r_float (section,"air_control_param" ); + character_physics_support()->movement() ->SetAirControlParam(AirControlParam); + + m_fPickupInfoRadius = pSettings->r_float(section,"pickup_info_radius"); + m_fSleepTimeFactor = pSettings->r_float(section,"sleep_time_factor"); + + character_physics_support()->in_Load (section); + + //загрузить параметры эффектора +// LoadShootingEffector ("shooting_effector"); + LoadSleepEffector ("sleep_effector"); + + //загрузить параметры смещения firepoint + m_vMissileOffset = pSettings->r_fvector3(section,"missile_throw_offset"); + + //Weapons = xr_new (this); + +if(!g_dedicated_server) +{ + LPCSTR hit_snd_sect = pSettings->r_string(section,"hit_sounds"); + for(int hit_type=0; hit_type<(int)ALife::eHitTypeMax; ++hit_type) + { + LPCSTR hit_name = ALife::g_cafHitType2String((ALife::EHitType)hit_type); + LPCSTR hit_snds = READ_IF_EXISTS(pSettings, r_string, hit_snd_sect, hit_name, ""); + int cnt = _GetItemCount(hit_snds); + string128 tmp; + VERIFY (cnt!=0); + for(int i=0; icreate (sndDie[0], strconcat(sizeof(buf),buf,*cName(),"\\die0"), st_Effect,SOUND_TYPE_MONSTER_DYING); + ::Sound->create (sndDie[1], strconcat(sizeof(buf),buf,*cName(),"\\die1"), st_Effect,SOUND_TYPE_MONSTER_DYING); + ::Sound->create (sndDie[2], strconcat(sizeof(buf),buf,*cName(),"\\die2"), st_Effect,SOUND_TYPE_MONSTER_DYING); + ::Sound->create (sndDie[3], strconcat(sizeof(buf),buf,*cName(),"\\die3"), st_Effect,SOUND_TYPE_MONSTER_DYING); + + m_HeavyBreathSnd.create (pSettings->r_string(section,"heavy_breath_snd"), st_Effect,SOUND_TYPE_MONSTER_INJURING); + m_BloodSnd.create (pSettings->r_string(section,"heavy_blood_snd"), st_Effect,SOUND_TYPE_MONSTER_INJURING); + } +} + cam_Set (eacFirstEye); + + // sheduler + shedule.t_min = shedule.t_max = 1; + + // настройки дисперсии стрельбы + m_fDispBase = pSettings->r_float (section,"disp_base" ); + m_fDispBase = deg2rad(m_fDispBase); + + m_fDispAim = pSettings->r_float (section,"disp_aim" ); + m_fDispAim = deg2rad(m_fDispAim); + + m_fDispVelFactor = pSettings->r_float (section,"disp_vel_factor" ); + m_fDispAccelFactor = pSettings->r_float (section,"disp_accel_factor" ); + m_fDispCrouchFactor = pSettings->r_float (section,"disp_crouch_factor"); + m_fDispCrouchNoAccelFactor = pSettings->r_float (section,"disp_crouch_no_acc_factor"); + + m_bActorShadows = (psActorFlags.test(AF_ACTOR_BODY)) ? false : true; + + LPCSTR default_outfit = READ_IF_EXISTS(pSettings,r_string,section,"default_outfit",0); + SetDefaultVisualOutfit (default_outfit); + LPCSTR default_outfit_legs = pSettings->r_string(section, "default_outfit_legs"); + if (m_bActorShadows) + SetDefaultVisualOutfit_legs (default_outfit); + else + SetDefaultVisualOutfit_legs (default_outfit_legs); + + m_bCanBeDrawLegs = true; + //if (pSettings->section_exist("lost_alpha_cfg") && pSettings->line_exist("lost_alpha_cfg","actor_legs_visible")) + // m_bCanBeDrawLegs = !!pSettings->r_bool("lost_alpha_cfg","actor_legs_visible"); //#x# SkyLoader: added console command for this + + if (IsGameTypeSingle() && CanBeDrawLegs()) + m_bDrawLegs = true; + else + m_bDrawLegs = false; + + invincibility_fire_shield_1st = READ_IF_EXISTS(pSettings,r_string,section,"Invincibility_Shield_1st",0); + invincibility_fire_shield_3rd = READ_IF_EXISTS(pSettings,r_string,section,"Invincibility_Shield_3rd",0); +//----------------------------------------- + m_AutoPickUp_AABB = READ_IF_EXISTS(pSettings,r_fvector3,section,"AutoPickUp_AABB",Fvector().set(0.02f, 0.02f, 0.02f)); + m_AutoPickUp_AABB_Offset = READ_IF_EXISTS(pSettings,r_fvector3,section,"AutoPickUp_AABB_offs",Fvector().set(0, 0, 0)); + + CStringTable string_table; + m_sCharacterUseAction = "character_use"; + m_sDeadCharacterUseAction = "dead_character_use"; + m_sDeadCharacterUseOrDragAction = "dead_character_use_or_drag"; + m_sCarCharacterUseAction = "car_character_use"; + m_sInventoryItemUseAction = "inventory_item_use"; + m_sInventoryBoxUseAction = "inventory_box_use"; + m_sTurretCharacterUseAction = "turret_character_use"; + //--------------------------------------------------------------------- + m_sHeadShotParticle = READ_IF_EXISTS(pSettings,r_string,section,"HeadShotParticle",0); + m_secondRifleSlotAllowed = READ_IF_EXISTS(pSettings,r_bool,section,"allow_second_rifle_slot",false) != 0; + + + maxHudWetness_ = READ_IF_EXISTS(pSettings, r_float, section, "hud_wetness_max", 0.7f); + wetnessAccmBase_ = READ_IF_EXISTS(pSettings, r_float, section, "hud_wetness_accm_base", 0.023f); + wetnessDecreaseF_ = READ_IF_EXISTS(pSettings, r_float, section, "hud_wetness_decreese_base", 0.01f); +} + +void CActor::PHHit(float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type /* = ALife::eHitTypeWound */) +{ + m_pPhysics_support->in_Hit(P,dir,who,element,p_in_object_space,impulse,hit_type,!g_Alive()); +} + +struct playing_pred +{ + IC bool operator() (ref_sound &s) + { + return (NULL != s._feedback() ); + } +}; + +void CActor::Hit (SHit* pHDS) +{ + pHDS->aim_bullet = false; + + SHit HDS = *pHDS; + if( HDS.hit_type= ALife::eHitTypeMax ) + { + string256 err; + xr_sprintf (err, "Unknown/unregistered hit type [%d]", HDS.hit_type); + R_ASSERT2 (0, err ); + + } +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgCharacterControl)) + { + DBG_OpenCashedDraw(); + Fvector to;to.add(Position(),Fvector().mul(HDS.dir,HDS.phys_impulse())); + DBG_DrawLine(Position(),to,D3DCOLOR_XRGB(124,124,0)); + DBG_ClosedCashedDraw(500); + } +#endif // DEBUG + + bool bPlaySound = true; + if (!g_Alive()) bPlaySound = false; + + if( !g_dedicated_server && + !sndHit[HDS.hit_type].empty() && + (ALife::eHitTypeTelepatic != HDS.hit_type)) + { + ref_sound& S = sndHit[HDS.hit_type][Random.randI(sndHit[HDS.hit_type].size())]; + bool b_snd_hit_playing = sndHit[HDS.hit_type].end() != std::find_if(sndHit[HDS.hit_type].begin(), sndHit[HDS.hit_type].end(), playing_pred()); + + if(ALife::eHitTypeExplosion == HDS.hit_type) + { + if (this == Level().CurrentControlEntity()) + { + S.set_volume(10.0f); + if(!m_sndShockEffector){ + m_sndShockEffector = new SndShockEffector(); + m_sndShockEffector->Start(this, float(S._handle()->length_sec() * 1000), HDS.damage() ); + } + } + else + bPlaySound = false; + } + if (bPlaySound && !b_snd_hit_playing) + { + Fvector point = Position(); + point.y += CameraHeight(); + S.play_at_pos (this, point); + }; + } + + + //slow actor, only when he gets hit + if(HDS.hit_type == ALife::eHitTypeWound || HDS.hit_type == ALife::eHitTypeStrike) + { + hit_slowmo = HDS.damage(); + clamp (hit_slowmo,0.0f,1.f); + } + else + hit_slowmo = 0.f; + //--------------------------------------------------------------- + if (Level().CurrentViewEntity() == this && !g_dedicated_server && HDS.hit_type == ALife::eHitTypeFireWound) + { + CObject* pLastHitter = Level().Objects.net_Find(m_iLastHitterID); + CObject* pLastHittingWeapon = Level().Objects.net_Find(m_iLastHittingWeaponID); + HitSector(pLastHitter, pLastHittingWeapon); + }; + + if ((mstate_real&mcSprint) && Level().CurrentControlEntity() == this && + HDS.hit_type != ALife::eHitTypeTelepatic && + HDS.hit_type != ALife::eHitTypeRadiation + ) + { +// mstate_real &=~mcSprint; + mstate_wishful &=~mcSprint; + }; + if(!g_dedicated_server) + { + HitMark (HDS.damage(), HDS.dir, HDS.who, HDS.bone(), HDS.p_in_bone_space, HDS.impulse, HDS.hit_type); + } + + + float hit_power = HitArtefactsOnBelt(HDS.damage(), HDS.hit_type); + hit_power = HitBoosters(hit_power, HDS.hit_type); + + hit_power *= m_fImmunityCoef; + + if (GodMode())//psActorFlags.test(AF_GODMODE)) + { + HDS.power = 0.0f; +// inherited::Hit(0.f,dir,who,element,position_in_bone_space,impulse, hit_type); + inherited::Hit(&HDS); + return; + } + else + { + //inherited::Hit (hit_power,dir,who,element,position_in_bone_space, impulse, hit_type); + HDS.power = hit_power; + HDS.add_wound = true; + inherited::Hit(&HDS); + }; +} + +void CActor::HitMark (float P, + Fvector dir, + CObject* who, + s16 element, + Fvector position_in_bone_space, + float impulse, + ALife::EHitType hit_type) +{ + // hit marker + if ( (hit_type==ALife::eHitTypeFireWound||hit_type==ALife::eHitTypeWound_2) && g_Alive() && Local() && (Level().CurrentEntity()==this) ) + { + HUD().Hit(0, P, dir); + + CEffectorCam* ce = Cameras().GetCamEffector((ECamEffectorType)effFireHit); + if(!ce) + { + int id = -1; + Fvector cam_pos,cam_dir,cam_norm; + cam_Active()->Get (cam_pos,cam_dir,cam_norm); + cam_dir.normalize_safe (); + dir.normalize_safe (); + + float ang_diff = angle_difference (cam_dir.getH(), dir.getH()); + Fvector cp; + cp.crossproduct (cam_dir,dir); + bool bUp = (cp.y>0.0f); + + Fvector cross; + cross.crossproduct (cam_dir, dir); + VERIFY (ang_diff>=0.0f && ang_diff<=PI); + + float _s1 = PI_DIV_8; + float _s2 = _s1+PI_DIV_4; + float _s3 = _s2+PI_DIV_4; + float _s4 = _s3+PI_DIV_4; + + if(ang_diff<=_s1) + id = 2; + else if(ang_diff>_s1 && ang_diff<=_s2) + id = (bUp)?5:7; + else if(ang_diff>_s2 && ang_diff<=_s3) + id = (bUp)?3:1; + else if(ang_diff>_s3 && ang_diff<=_s4) + id = (bUp)?4:6; + else if(ang_diff>_s4) + id = 0; + else + VERIFY(0); + + string64 sect_name; + xr_sprintf(sect_name,"effector_fire_hit_%d",id); + AddEffector(this, effFireHit, sect_name, P/100.0f); + } + } + + if (who && !fis_zero(P)) + callback(GameObject::eHit)( + lua_game_object(), + P, + dir, + smart_cast(who)->lua_game_object(), + element + ); + +} + +void CActor::HitSignal(float perc, Fvector& vLocalDir, CObject* who, s16 element) +{ + if (g_Alive()) + { + + // stop-motion + if (character_physics_support()->movement()->Environment()==CPHMovementControl::peOnGround || character_physics_support()->movement()->Environment()==CPHMovementControl::peAtWall) + { + Fvector zeroV; + zeroV.set (0,0,0); + character_physics_support()->movement()->SetVelocity(zeroV); + } + + // check damage bone + Fvector D; + XFORM().transform_dir(D,vLocalDir); + + float yaw, pitch; + D.getHP(yaw,pitch); + IKinematics *K = smart_cast(Visual()); + IKinematicsAnimated *KA = smart_cast(Visual()); + VERIFY(K && KA); +#pragma todo("Dima to Dima : forward-back bone impulse direction has been determined incorrectly!") + MotionID motion_ID = m_anims->m_normal.m_damage[iFloor(K->LL_GetBoneInstance(element).get_param(1) + (angle_difference(r_model_yaw + r_model_yaw_delta,yaw) <= PI_DIV_2 ? 0 : 1))]; + float power_factor = perc/100.f; clamp(power_factor,0.f,1.f); + VERIFY(motion_ID.valid()); + KA->PlayFX(motion_ID,power_factor); + } +} +void start_tutorial(LPCSTR name); +void CActor::Die(CObject* who) +{ + inherited::Die (who); + + if (OnServer()) + { + xr_vector::iterator I = inventory().m_slots.begin(); + xr_vector::iterator E = inventory().m_slots.end(); + + + for (u32 slot_idx=0 ; I != E; ++I,++slot_idx) + { + if (slot_idx == inventory().GetActiveSlot()) + { + if((*I).m_pIItem) + { + if (IsGameTypeSingle()) + (*I).m_pIItem->SetDropManual(TRUE); + else + { + if ((*I).m_pIItem->object().CLS_ID!=CLSID_OBJECT_W_KNIFE && slot_idx!=GRENADE_SLOT) + { + (*I).m_pIItem->SetDropManual(TRUE); + } + } + }; + continue; + } + else + { + CCustomOutfit *pOutfit = smart_cast ((*I).m_pIItem); + if (pOutfit) continue; + }; + if((*I).m_pIItem) + inventory().Ruck((*I).m_pIItem); + }; + + + ///!!! чистка пояса + TIItemContainer &l_blist = inventory().m_belt; + while (!l_blist.empty()) + inventory().Ruck(l_blist.front()); + + }; + + if (psActorFlags.test(AF_FST_PSN_DEATH)) + { + cam_Set (eacFirstEye); + m_bActorShadows = true; + } else + cam_Set (eacFreeLook); + + mstate_wishful &= ~mcAnyMove; + mstate_real &= ~mcAnyMove; + + ::Sound->play_at_pos (sndDie[Random.randI(SND_DIE_COUNT)],this,Position()); + + m_HeavyBreathSnd.stop (); + m_BloodSnd.stop (); + + + start_tutorial ("game_over"); + + CurrentGameUI()->HideShownDialogs(); + + xr_delete (m_sndShockEffector); + xr_delete (m_ScriptCameraDirection); +} + +void CActor::SwitchOutBorder(bool new_border_state) +{ + if(new_border_state) + { + callback(GameObject::eExitLevelBorder)(lua_game_object()); + } + else + { +//. Msg("enter level border"); + callback(GameObject::eEnterLevelBorder)(lua_game_object()); + } + m_bOutBorder=new_border_state; +} + +void CActor::g_Physics(Fvector& _accel, float jump, float dt) +{ + // Correct accel + Fvector accel; + accel.set (_accel); + hit_slowmo -= dt; + if (hit_slowmo<0) hit_slowmo = 0.f; + + accel.mul (1.f-hit_slowmo); + + if(g_Alive()) + { + if(mstate_real&mcClimb&&!cameras[eacFirstEye]->bClampYaw)accel.set(0.f,0.f,0.f); + character_physics_support()->movement()->Calculate (accel,cameras[cam_active]->vDirection,0,jump,dt,false); + bool new_border_state=character_physics_support()->movement()->isOutBorder(); + if(m_bOutBorder!=new_border_state && Level().CurrentControlEntity() == this) + { + SwitchOutBorder(new_border_state); + } + character_physics_support()->movement()->GetPosition (Position()); + character_physics_support()->movement()->bSleep =false; + } + + if (Local() && g_Alive()) { + if (character_physics_support()->movement()->gcontact_Was) + Cameras().AddCamEffector (new CEffectorFall(character_physics_support()->movement()->gcontact_Power)); + if (!fis_zero(character_physics_support()->movement()->gcontact_HealthLost)) { + const ICollisionDamageInfo* di=character_physics_support()->movement()->CollisionDamageInfo(); + Fvector hdir;di->HitDir(hdir); + SetHitInfo(this, NULL, 0, Fvector().set(0, 0, 0), hdir); + // Hit (m_PhysicMovementControl->gcontact_HealthLost,hdir,di->DamageInitiator(),m_PhysicMovementControl->ContactBone(),di->HitPos(),0.f,ALife::eHitTypeStrike);//s16(6 + 2*::Random.randI(0,2)) + if (Level().CurrentControlEntity() == this) + { + SHit HDS = SHit(character_physics_support()->movement()->gcontact_HealthLost,hdir,di->DamageInitiator(),character_physics_support()->movement()->ContactBone(),di->HitPos(),0.f,di->HitType()); +// Hit(&HDS); + + NET_Packet l_P; + HDS.GenHeader(GE_HIT, ID()); + HDS.whoID = di->DamageInitiator()->ID(); + HDS.weaponID = di->DamageInitiator()->ID(); + HDS.Write_Packet(l_P); + + u_EventSend (l_P); + } + } + } +} +float g_fov; + +float CActor::currentFOV() +{ + CWeapon* pWeapon = smart_cast(inventory().ActiveItem()); + + if (eacFreeLook != cam_active && pWeapon && + pWeapon->IsZoomed() && (!pWeapon->ZoomTexture() || + (!pWeapon->IsRotatingToZoom() && pWeapon->ZoomTexture() && eacFirstEye == cam_active) || (pWeapon->ZoomTexture() && eacLookAt == cam_active))) + return pWeapon->GetZoomFactor() * (0.75f); + else + return g_fov; +} + +extern u32 crosshairAnimationType; +void CActor::UpdateCL() +{ + if (m_feel_touch_characters > 0) + { + for (xr_vector::iterator it = feel_touch.begin(); it != feel_touch.end(); it++) + { + CPhysicsShellHolder *sh = smart_cast(*it); + if (sh&&sh->character_physics_support()) + { + sh->character_physics_support()->movement()->UpdateObjectBox(character_physics_support()->movement()->PHCharacter()); + } + } + } + if (m_holder) + m_holder->UpdateEx(currentFOV()); + + m_snd_noise -= 0.3f*Device.fTimeDelta; + + VERIFY2(_valid(renderable.xform), *cName()); + inherited::UpdateCL(); + VERIFY2(_valid(renderable.xform), *cName()); + m_pPhysics_support->in_UpdateCL(); + VERIFY2(_valid(renderable.xform), *cName()); + + if (g_Alive()) { + PickupModeUpdate(); + PickupModeUpdate_COD(); + } + + + + m_bZoomAimingMode = false; + CWeapon* pWeapon = smart_cast(inventory().ActiveItem()); + + Device.Statistic->TEST1.Begin (); + cam_Update(float(Device.dwTimeDelta)/1000.0f, currentFOV()); + Device.Statistic->TEST1.End (); + + if(Level().CurrentEntity() && this->ID()==Level().CurrentEntity()->ID() ) + { + psHUD_Flags.set( HUD_CROSSHAIR_RT2, true ); + psHUD_Flags.set( HUD_DRAW_RT, true ); + } + if(pWeapon ) + { + if(pWeapon->IsZoomed()) + { + CEffectorZoomInertion* S = smart_cast (Cameras().GetCamEffector(eCEZoom)); + if (S) + { + S->SetParams(GetWeaponAccuracy() * pWeapon->GetZoomInertion() * m_fZoomInertCoef); + } + + m_bZoomAimingMode = true; + } + + if(Level().CurrentEntity() && this->ID()==Level().CurrentEntity()->ID() ) + { + float fire_disp_full = pWeapon->GetFireDispersion(true) + GetWeaponAccuracy(); + + HUD().SetCrosshairDisp(fire_disp_full, 0.02f); + if (crosshairAnimationType != 0) + HUD().ShowCrosshair(pWeapon->use_crosshair()); + else + HUD().ShowCrosshair(false); + + if (eacLookAt == cam_active) { + psHUD_Flags.set( HUD_CROSSHAIR_RT2, true ); + psHUD_Flags.set( HUD_DRAW_RT, true ); + } else { + psHUD_Flags.set( HUD_CROSSHAIR_RT2, pWeapon->show_crosshair() ); + psHUD_Flags.set( HUD_DRAW_RT, pWeapon->show_indicators() ); + } + } + + } + else if (m_holder && smart_cast(m_holder)) + { + HUD().SetCrosshairDisp(0.f); + HUD().ShowCrosshair(true); + psHUD_Flags.set(HUD_CROSSHAIR_RT2, true); + psHUD_Flags.set(HUD_DRAW_RT, true); + } + else + { + if(Level().CurrentEntity() && this->ID()==Level().CurrentEntity()->ID() ) + { + HUD().SetCrosshairDisp(0.f); + HUD().ShowCrosshair(false); + } + } + + UpdateDefferedMessages(); + + if (g_Alive()) + CStepManager::update(); + + spatial.type |=STYPE_REACTTOSOUND; + + if(m_sndShockEffector) + { + if (this == Level().CurrentViewEntity()) + { + m_sndShockEffector->Update(); + + if(!m_sndShockEffector->InWork() || !g_Alive()) + xr_delete(m_sndShockEffector); + } + else + xr_delete(m_sndShockEffector); + } + if (m_ScriptCameraDirection) + { + if (this == Level().CurrentViewEntity()) + { + m_ScriptCameraDirection->Update(); + + if(!m_ScriptCameraDirection->InWork() || !g_Alive()) + xr_delete(m_ScriptCameraDirection); + } + else + xr_delete(m_ScriptCameraDirection); + } + + CTorch *flashlight = GetCurrentTorch(); + if (flashlight) + flashlight->UpdateBattery(); + + Fmatrix trans; + if(cam_Active() == cam_FirstEye()) + Cameras().hud_camera_Matrix (trans); + else + Cameras().camera_Matrix (trans); + + if(IsFocused()) + g_player_hud->update (trans); +} + +float NET_Jump = 0; + +#include "ai\monsters\ai_monster_utils.h" +void CActor::shedule_Update (u32 DT) +{ + setSVU(OnServer()); + + if(IsFocused()) + { + BOOL bHudView = HUDview(); + if(bHudView) + { + CInventoryItem* pInvItem = inventory().ActiveItem(); + if( pInvItem ) + { + CHudItem* pHudItem = smart_cast(pInvItem); + if(pHudItem) + { + if( pHudItem->IsHidden() ) + { + g_player_hud->detach_item (pHudItem); + } + else + { + g_player_hud->attach_item (pHudItem); + } + } + }else + { + g_player_hud->detach_item_idx ( 0 ); + //Msg("---No active item in inventory(), item 0 detached."); + } + } + else + { + g_player_hud->detach_all_items(); + //Msg("---No hud view found, all items detached."); + } + + } + + //обновление инвентаря + UpdateInventoryOwner (DT); + if (GameID() == GAME_SINGLE) + GameTaskManager().UpdateTasks (); + else Msg("Remove GameID == GAME_SINGLE if i never apear in console"); + + if(m_holder || !getEnabled() || !Ready()) + { + m_sDefaultObjAction = NULL; + inherited::shedule_Update (DT); + return; + } + + // + clamp (DT,0u,100u); + float dt = float(DT)/1000.f; + + // Check controls, create accel, prelimitary setup "mstate_real" + + //----------- for E3 ----------------------------- +// if (Local() && (OnClient() || Level().CurrentEntity()==this)) + if (Level().CurrentControlEntity() == this && (!Level().IsDemoPlay() || Level().IsServerDemo())) + //------------------------------------------------ + { + g_cl_CheckControls (mstate_wishful,NET_SavedAccel,NET_Jump,dt); + { + /* + if (mstate_real & mcJump) + { + NET_Packet P; + u_EventGen(P, GE_ACTOR_JUMPING, ID()); + P.w_sdir(NET_SavedAccel); + P.w_float(NET_Jump); + u_EventSend(P); + } + */ + } + g_cl_Orientate (mstate_real,dt); + g_Orientate (mstate_real,dt); + + g_Physics (NET_SavedAccel,NET_Jump,dt); + + g_cl_ValidateMState (dt,mstate_wishful); + g_SetAnimation (mstate_real); + + // Check for game-contacts + Fvector C; float R; + //m_PhysicMovementControl->GetBoundingSphere (C,R); + + Center(C); + R=Radius(); + feel_touch_update (C,R); + + // Dropping + if (b_DropActivated) { + f_DropPower += dt*0.1f; + clamp (f_DropPower,0.f,1.f); + } else { + f_DropPower = 0.f; + } + if (!Level().IsDemoPlay()) + { + //----------------------------------------------------- + mstate_wishful &=~mcAccel; + mstate_wishful &=~mcLStrafe; + mstate_wishful &=~mcRStrafe; + mstate_wishful &=~mcLLookout; + mstate_wishful &=~mcRLookout; + mstate_wishful &=~mcFwd; + mstate_wishful &=~mcBack; + extern bool g_bAutoClearCrouch; + extern u32 g_bAutoApplySprint; + if (g_bAutoClearCrouch){ + mstate_wishful &= ~mcCrouch; + if (g_bAutoApplySprint > 0){ + g_bAutoApplySprint += 1; + } + } + if (g_bAutoApplySprint == 10)//применит бег на 10й кадр + { + mstate_wishful |= mcSprint; + g_bAutoApplySprint = 0; + } + //----------------------------------------------------- + } + } + else + { + make_Interpolation(); + + if (NET.size()) + { + +// NET_SavedAccel = NET_Last.p_accel; +// mstate_real = mstate_wishful = NET_Last.mstate; + + g_sv_Orientate (mstate_real,dt ); + g_Orientate (mstate_real,dt ); + g_Physics (NET_SavedAccel,NET_Jump,dt ); + if (!m_bInInterpolation) + g_cl_ValidateMState (dt,mstate_wishful); + g_SetAnimation (mstate_real); + + if (NET_Last.mstate & mcCrouch) + { + if (isActorAccelerated(mstate_real, IsZoomAimingMode())) + character_physics_support()->movement()->ActivateBox(1, true); + else + character_physics_support()->movement()->ActivateBox(2, true); + } + else + character_physics_support()->movement()->ActivateBox(0, true); + } + mstate_old = mstate_real; + } + + if (this == Level().CurrentViewEntity()) + { + UpdateMotionIcon (mstate_real); + }; + + NET_Jump = 0; + + + inherited::shedule_Update (DT); + + //эффектор включаемый при ходьбе + if (psActorFlags.test(AF_HEAD_BOBBING)) + { + if (!pCamBobbing) + { + pCamBobbing = new CEffectorBobbing(); + Cameras().AddCamEffector (pCamBobbing); + } + pCamBobbing->SetState (mstate_real, conditions().IsLimping(), IsZoomAimingMode()); + } + else if (pCamBobbing) + { + Cameras().RemoveCamEffector(eCEBobbing); + pCamBobbing = nullptr; + } + + //звук тяжелого дыхания при уталости и хромании + if(this==Level().CurrentControlEntity() && !g_dedicated_server ) + { + if(conditions().IsLimping() && g_Alive()) + { + if(!m_HeavyBreathSnd._feedback()) + { + m_HeavyBreathSnd.play_at_pos(this, Fvector().set(0,ACTOR_HEIGHT,0), sm_Looped | sm_2D); + }else{ + m_HeavyBreathSnd.set_position(Fvector().set(0,ACTOR_HEIGHT,0)); + } + }else if(m_HeavyBreathSnd._feedback()){ + m_HeavyBreathSnd.stop (); + } + + float bs = conditions().BleedingSpeed(); + if(bs>0.6f) + { + Fvector snd_pos; + snd_pos.set(0,ACTOR_HEIGHT,0); + if(!m_BloodSnd._feedback()) + m_BloodSnd.play_at_pos(this, snd_pos, sm_Looped | sm_2D); + else + m_BloodSnd.set_position(snd_pos); + + float v = bs+0.25f; + + m_BloodSnd.set_volume (v); + }else{ + if(m_BloodSnd._feedback()) + m_BloodSnd.stop(); + } + + if(!g_Alive()&&m_BloodSnd._feedback()) + m_BloodSnd.stop(); + } + ComputeHudWetness(); + + if (((BOOL)m_bActorShadows == psActorFlags.test(AF_ACTOR_BODY)) && g_Alive() && !m_holder) + { + if (m_bActorShadows) + SetDefaultVisualOutfit_legs (pSettings->r_string(*cNameSect(),"default_outfit_legs")); + else + SetDefaultVisualOutfit_legs (GetDefaultVisualOutfit()); + + m_bActorShadows = (psActorFlags.test(AF_ACTOR_BODY)) ? false : true; + + if (eacFirstEye == cam_active) //reset visual + { + cam_Set(eacLookAt); + cam_Set(eacFirstEye); + } + } + + //если в режиме HUD, то сама модель актера не рисуется + if(!character_physics_support()->IsRemoved()) + if (m_bDrawLegs && ((!psDeviceFlags.test(rsR2) && !psDeviceFlags.test(rsR3) && !psDeviceFlags.test(rsR4) && !m_bActorShadows) || ((psDeviceFlags.test(rsR2) || psDeviceFlags.test(rsR3) || psDeviceFlags.test(rsR4)) && m_bActorShadows))) + setVisible (TRUE); + else + setVisible (!HUDview ()); + //что актер видит перед собой + collide::rq_result& RQ = HUD().GetCurrentRayQuery(); + + float dist_to_obj = RQ.range; + if (RQ.O && eacFirstEye != cam_active) + dist_to_obj = get_bone_position(this, "bip01_spine").distance_to((smart_cast(RQ.O))->Position()); + + if(!input_external_handler_installed() && RQ.O && dist_to_obj(RQ.O); + + CGameObject *game_object = smart_cast(RQ.O); + m_pUsableObject = smart_cast(game_object); + m_pInvBoxWeLookingAt = smart_cast(game_object); + inventory().m_pTarget = smart_cast(game_object); + m_pPersonWeLookingAt = smart_cast(game_object); + m_pHolderWeLookingAt = smart_cast(game_object); + CEntityAlive* pEntityAlive = smart_cast(game_object); + + if (GameID() == GAME_SINGLE ) + { + if (m_pUsableObject && m_pUsableObject->tip_text()) + { + m_sDefaultObjAction = CStringTable().translate( m_pUsableObject->tip_text() ); + } + else + { + if (m_pPersonWeLookingAt && pEntityAlive && pEntityAlive->g_Alive()) + m_sDefaultObjAction = m_sCharacterUseAction; + + else if (pEntityAlive && !pEntityAlive->g_Alive()) + { + bool b_allow_drag = !!pSettings->line_exist("ph_capture_visuals",pEntityAlive->cNameVisual()); + + if (m_pPersonWeLookingAt && !m_pPersonWeLookingAt->inventory().CanBeDragged()) + b_allow_drag = false; + + if(b_allow_drag) + m_sDefaultObjAction = m_sDeadCharacterUseOrDragAction; + else + m_sDefaultObjAction = m_sDeadCharacterUseAction; + + } + else if (m_pHolderWeLookingAt) + { + if (smart_cast(m_pHolderWeLookingAt)) + m_sDefaultObjAction = m_sCarCharacterUseAction; + else + m_sDefaultObjAction = m_sTurretCharacterUseAction; + } + + else if (inventory().m_pTarget && inventory().m_pTarget->CanTake() ) + m_sDefaultObjAction = m_sInventoryItemUseAction; +//. else if (m_pInvBoxWeLookingAt) +//. m_sDefaultObjAction = m_sInventoryBoxUseAction; + else + m_sDefaultObjAction = NULL; + } + } + } + else + { + inventory().m_pTarget = NULL; + m_pPersonWeLookingAt = NULL; + m_sDefaultObjAction = NULL; + m_pUsableObject = NULL; + m_pObjectWeLookingAt = NULL; + m_pHolderWeLookingAt = NULL; + m_pInvBoxWeLookingAt = NULL; + } + +// UpdateSleep (); + + //для свойст артефактов, находящихся на поясе + UpdateArtefactsOnBeltAndOutfit (); + m_pPhysics_support->in_shedule_Update (DT); + Check_for_AutoPickUp (); +}; +#include "debug_renderer.h" +void CActor::renderable_Render () +{ + inherited::renderable_Render (); + if (!HUDview() || (m_bActorShadows && m_bFirstEye)){ + CInventoryOwner::renderable_Render (); + } +} + +BOOL CActor::renderable_ShadowGenerate () +{ + if(m_holder || (!m_bActorShadows && m_bFirstEye)) + return FALSE; + + return inherited::renderable_ShadowGenerate(); +} + + + +void CActor::g_PerformDrop ( ) +{ + b_DropActivated = FALSE; + + PIItem pItem = inventory().ActiveItem(); + if (0==pItem) return; + + u32 s = inventory().GetActiveSlot(); + if(inventory().m_slots[s].m_bPersistent) return; + + pItem->SetDropManual (TRUE); +} + + +#ifdef DEBUG +extern BOOL g_ShowAnimationInfo ; +#endif // DEBUG +// HUD +void CActor::OnHUDDraw (CCustomHUD* /**hud/**/) +{ + if(IsFocused() && ! ( (mstate_real & mcLookout) && !IsGameTypeSingle() ) ) + g_player_hud->render_hud (); + +#if 0//ndef NDEBUG + if (Level().CurrentControlEntity() == this && g_ShowAnimationInfo) + { + string128 buf; + HUD().Font().pFontStat->SetColor (0xffffffff); + HUD().Font().pFontStat->OutSet (170,530); + HUD().Font().pFontStat->OutNext ("Position: [%3.2f, %3.2f, %3.2f]",VPUSH(Position())); + HUD().Font().pFontStat->OutNext ("Velocity: [%3.2f, %3.2f, %3.2f]",VPUSH(m_PhysicMovementControl->GetVelocity())); + HUD().Font().pFontStat->OutNext ("Vel Magnitude: [%3.2f]",m_PhysicMovementControl->GetVelocityMagnitude()); + HUD().Font().pFontStat->OutNext ("Vel Actual: [%3.2f]",m_PhysicMovementControl->GetVelocityActual()); + switch (m_PhysicMovementControl->Environment()) + { + case CPHMovementControl::peOnGround: strcpy(buf,"ground"); break; + case CPHMovementControl::peInAir: strcpy(buf,"air"); break; + case CPHMovementControl::peAtWall: strcpy(buf,"wall"); break; + } + HUD().Font().pFontStat->OutNext (buf); + + if (IReceived != 0) + { + float Size = 0; + Size = HUD().Font().pFontStat->GetSize(); + HUD().Font().pFontStat->SetSize(Size*2); + HUD().Font().pFontStat->SetColor (0xffff0000); + HUD().Font().pFontStat->OutNext ("Input : [%3.2f]", ICoincidenced/IReceived * 100.0f); + HUD().Font().pFontStat->SetSize(Size); + }; + }; +#endif +} + +void CActor::RenderIndicator (Fvector dpos, float r1, float r2, const ui_shader &IndShader) +{ + if (!g_Alive()) return; + + u32 dwOffset = 0,dwCount = 0; +// FVF::LIT* pv_start = (FVF::LIT*)RCache.Vertex.DEBUG_LOCK(4,hFriendlyIndicator->vb_stride,dwOffset); +// FVF::LIT* pv_start = (FVF::LIT*)RCache.Vertex.Lock(4,hFriendlyIndicator->vb_stride,dwOffset); +// FVF::LIT* pv = pv_start; + UIRender->StartPrimitive(4, IUIRender::ptTriStrip, IUIRender::pttLIT); +//TODO: Friendly Indicator + + // base rect + + CBoneInstance& BI = smart_cast(Visual())->LL_GetBoneInstance(u16(m_head)); + Fmatrix M; + smart_cast(Visual())->CalculateBones (); + M.mul (XFORM(),BI.mTransform); + + Fvector pos = M.c; pos.add(dpos); + const Fvector& T = Device.vCameraTop; + const Fvector& R = Device.vCameraRight; + Fvector Vr, Vt; + Vr.x = R.x*r1; + Vr.y = R.y*r1; + Vr.z = R.z*r1; + Vt.x = T.x*r2; + Vt.y = T.y*r2; + Vt.z = T.z*r2; + + Fvector a,b,c,d; + a.sub (Vt,Vr); + b.add (Vt,Vr); + c.invert (a); + d.invert (b); + + UIRender->PushPoint(d.x+pos.x,d.y+pos.y,d.z+pos.z, 0xffffffff, 0.f,1.f); + UIRender->PushPoint(a.x+pos.x,a.y+pos.y,a.z+pos.z, 0xffffffff, 0.f,0.f); + UIRender->PushPoint(c.x+pos.x,c.y+pos.y,c.z+pos.z, 0xffffffff, 1.f,1.f); + UIRender->PushPoint(b.x+pos.x,b.y+pos.y,b.z+pos.z, 0xffffffff, 1.f,0.f); + /* + pv->set (d.x+pos.x,d.y+pos.y,d.z+pos.z, 0xffffffff, 0.f,1.f); pv++; + pv->set (a.x+pos.x,a.y+pos.y,a.z+pos.z, 0xffffffff, 0.f,0.f); pv++; + pv->set (c.x+pos.x,c.y+pos.y,c.z+pos.z, 0xffffffff, 1.f,1.f); pv++; + pv->set (b.x+pos.x,b.y+pos.y,b.z+pos.z, 0xffffffff, 1.f,0.f); pv++; + */ + // render +//TODO: Friendly Indicator + UIRender->CacheSetXformWorld(Fidentity); + UIRender->SetShader(*IndShader); + UIRender->FlushPrimitive(); + + //dwCount = u32(pv-pv_start); + //RCache.Vertex.Unlock (dwCount,hFriendlyIndicator->vb_stride); + + //RCache.set_xform_world (Fidentity); + //RCache.set_Shader (IndShader); + //RCache.set_Geometry (hFriendlyIndicator); + //RCache.Render (D3DPT_TRIANGLESTRIP,dwOffset,0, dwCount, 0, 2); +}; + +static float mid_size = 0.097f; +static float fontsize = 15.0f; +static float upsize = 0.33f; +void CActor::RenderText (LPCSTR Text, Fvector dpos, float* pdup, u32 color) +{ + if (!g_Alive()) return; + + CBoneInstance& BI = smart_cast(Visual())->LL_GetBoneInstance(u16(m_head)); + Fmatrix M; + smart_cast(Visual())->CalculateBones (); + M.mul (XFORM(),BI.mTransform); + //------------------------------------------------ + Fvector v0, v1; + v0.set(M.c); v1.set(M.c); + Fvector T = Device.vCameraTop; + v1.add(T); + + Fvector v0r, v1r; + Device.mFullTransform.transform(v0r,v0); + Device.mFullTransform.transform(v1r,v1); + float size = v1r.distance_to(v0r); + CGameFont* pFont = UI().Font().pFontArial14; + if (!pFont) return; +// float OldFontSize = pFont->GetHeight (); + float delta_up = 0.0f; + if (size < mid_size) delta_up = upsize; + else delta_up = upsize*(mid_size/size); + dpos.y += delta_up; + if (size > mid_size) size = mid_size; +// float NewFontSize = size/mid_size * fontsize; + //------------------------------------------------ + M.c.y += dpos.y; + + Fvector4 v_res; + Device.mFullTransform.transform(v_res,M.c); + + if (v_res.z < 0 || v_res.w < 0) return; + if (v_res.x < -1.f || v_res.x > 1.f || v_res.y<-1.f || v_res.y>1.f) return; + + float x = (1.f + v_res.x)/2.f * (Device.dwWidth); + float y = (1.f - v_res.y)/2.f * (Device.dwHeight); + + pFont->SetAligment (CGameFont::alCenter); + pFont->SetColor (color); +// pFont->SetHeight (NewFontSize); + pFont->Out (x,y,Text); + //------------------------------------------------- +// pFont->SetHeight(OldFontSize); + *pdup = delta_up; +}; + +void CActor::SetPhPosition(const Fmatrix &transform) +{ + if(!m_pPhysicsShell){ + character_physics_support()->movement()->SetPosition(transform.c); + } + //else m_phSkeleton->S +} + +void CActor::ForceTransform(const Fmatrix& m) +{ + if(!g_Alive()) return; + XFORM().set (m); + if(character_physics_support()->movement()->CharacterExist()) character_physics_support()->movement()->EnableCharacter (); + character_physics_support()->set_movement_position( m.c ); + character_physics_support()->movement()->SetVelocity (0,0,0); +} + +ENGINE_API extern float psHUD_FOV; +float CActor::Radius()const +{ + float R = inherited::Radius(); + CWeapon* W = smart_cast(inventory().ActiveItem()); + if (W) R += W->Radius(); + // if (HUDview()) R *= 1.f/psHUD_FOV; + return R; +} + +bool CActor::use_bolts () const +{ + if (GameID() != GAME_SINGLE) return false; + return CInventoryOwner::use_bolts(); +}; + +int g_iCorpseRemove = 1; + +bool CActor::NeedToDestroyObject() const +{ + if(g_Alive()) return false; + if(g_iCorpseRemove == -1) return false; + if(g_iCorpseRemove == 0 && m_bAllowDeathRemove) return true; + return (TimePassedAfterDeath()>m_dwBodyRemoveTime && m_bAllowDeathRemove); +} + +ALife::_TIME_ID CActor::TimePassedAfterDeath() const +{ + if(!g_Alive()) + return Level().timeServer() - GetLevelDeathTime(); + else + return 0; +} + + +void CActor::OnItemTake (CInventoryItem *inventory_item, bool duringSpawn) +{ + CInventoryOwner::OnItemTake(inventory_item, duringSpawn); + if (OnClient()) return; +} + +void CActor::OnItemDrop (CInventoryItem *inventory_item) +{ + + //Begin: tatarinrafa: added change of actor hud and visual if there is no outfit in slot and droped item.type is outfit + PIItem outfit = this->inventory().ItemFromSlot(OUTFIT_SLOT); + CCustomOutfit* pOutfit = smart_cast (inventory_item); + + if (!outfit && pOutfit) + { + if (this->IsFirstEye()) + { + shared_str DefVisual = this->GetDefaultVisualOutfit_legs(); + if (DefVisual.size()) + { + this->ChangeVisual(DefVisual); + } + } else { + shared_str DefVisual = this->GetDefaultVisualOutfit(); + if (DefVisual.size()) + { + this->ChangeVisual(DefVisual); + } + } + + if (this == Level().CurrentViewEntity()) + g_player_hud->load_default(); + } + //End: tatarinrafa: added change of actor hud and visual if there is no outfit in slot and droped item.type is outfit + + CWeapon* weapon = smart_cast(inventory_item); + if (weapon && weapon->m_eItemPlace == eItemPlaceSlot) + { + weapon->OnZoomOut(); + if (weapon->GetRememberActorNVisnStatus()) + weapon->EnableActorNVisnAfterZoom(); + } + + CInventoryOwner::OnItemDrop(inventory_item); + CArtefact* artefact = smart_cast(inventory_item); + if(artefact && artefact->m_eItemPlace == eItemPlaceBelt) + MoveArtefactBelt(artefact, false); +} + + +void CActor::OnItemDropUpdate () +{ + CInventoryOwner::OnItemDropUpdate (); + + TIItemContainer::iterator I = inventory().m_all.begin(); + TIItemContainer::iterator E = inventory().m_all.end(); + + for ( ; I != E; ++I) + if( !(*I)->IsInvalid() && !attached(*I)) + attach(*I); +} + + +void CActor::OnItemRuck (CInventoryItem *inventory_item, EItemPlace previous_place) +{ + CInventoryOwner::OnItemRuck(inventory_item, previous_place); + + CArtefact* artefact = smart_cast(inventory_item); + if(artefact && previous_place == eItemPlaceBelt) + MoveArtefactBelt(artefact, false); +} +void CActor::OnItemBelt (CInventoryItem *inventory_item, EItemPlace previous_place) +{ + CInventoryOwner::OnItemBelt(inventory_item, previous_place); + + CArtefact* artefact = smart_cast(inventory_item); + if(artefact) + MoveArtefactBelt(artefact, true); +} + + +void CActor::MoveArtefactBelt(const CArtefact* artefact, bool on_belt) +{ + VERIFY(artefact); + + //повесить артефакт на пояс + if(on_belt) + { + VERIFY(m_ArtefactsOnBelt.end() == std::find(m_ArtefactsOnBelt.begin(), m_ArtefactsOnBelt.end(), artefact)); + m_ArtefactsOnBelt.push_back(artefact); + } + else + { + xr_vector::iterator it = std::remove(m_ArtefactsOnBelt.begin(), m_ArtefactsOnBelt.end(), artefact); + VERIFY(it != m_ArtefactsOnBelt.end()); + m_ArtefactsOnBelt.erase(it); + } + if (Level().CurrentViewEntity() && Level().CurrentViewEntity() == this) + CurrentGameUI()->UIMainIngameWnd->m_artefactPanel->InitIcons(m_ArtefactsOnBelt); +} + +#define ARTEFACTS_UPDATE_TIME 0.100f + +void CActor::UpdateArtefactsOnBeltAndOutfit() +{ + static float update_time = 0; + + float f_update_time = 0; + + if(update_time(*it); + if(artefact) + { + conditions().ChangeBleeding (artefact->m_fBleedingRestoreSpeed*f_update_time); + conditions().ChangeHealth (artefact->m_fHealthRestoreSpeed*f_update_time); + conditions().ChangePower (artefact->m_fPowerRestoreSpeed*f_update_time); + conditions().ChangeSatiety (artefact->m_fSatietyRestoreSpeed*f_update_time); + conditions().ChangeRadiation (artefact->m_fRadiationRestoreSpeed*f_update_time); + conditions().ChangePsyHealth (artefact->m_fPsyhealthRestoreSpeed*f_update_time); + + //сложим бонусы скорости от артифактов на поясе + run_koef_additional += artefact->m_additional_run_coef; + sprint_koef_additional += artefact->m_additional_sprint_koef; + jump_koef_additional += artefact->m_additional_jump_speed; + } + } + + //проверим не превысили ли лимит указаный в актор лтх. только для артов. для костюмов не проверяем + + if (run_koef_additional > m_fRunFactorAdditionalLimit) + run_koef_additional = m_fRunFactorAdditionalLimit; + if (sprint_koef_additional >m_fSprintFactorAdditionalLimit) + sprint_koef_additional = m_fSprintFactorAdditionalLimit; + if (jump_koef_additional >m_fJumpFactorAdditionalLimit) + jump_koef_additional = m_fJumpFactorAdditionalLimit; + + CCustomOutfit* outfit = GetOutfit(); + if (outfit) + { + conditions().ChangeBleeding (outfit->GetBleedingRestoreSpeed() * f_update_time); + conditions().ChangeHealth (outfit->GetHealthRestoreSpeed() * f_update_time); + conditions().ChangePower (outfit->GetPowerRestoreSpeed() * f_update_time); + conditions().ChangeSatiety (outfit->GetSatietyRestoreSpeed() * f_update_time); + conditions().ChangeRadiation(outfit->GetRadiationRestoreSpeed() * f_update_time); + + //добавим бонусы от костюма + run_koef_additional += outfit->m_additional_run_coef; + sprint_koef_additional += outfit->m_additional_sprint_koef; + jump_koef_additional += outfit->m_additional_jump_speed; + } + + m_fSprintFactorAdditional = sprint_koef_additional; + m_fRunFactorAdditional = run_koef_additional; + //для прыжка немного подругому + character_physics_support()->movement()->SetJumpUpVelocity(m_fJumpSpeed + jump_koef_additional); +} + +float CActor::HitArtefactsOnBelt (float hit_power, ALife::EHitType hit_type) +{ + float res_hit_power_k = 1.0f; + float _af_count = 0.0f; + for(TIItemContainer::iterator it = inventory().m_belt.begin(); + inventory().m_belt.end() != it; ++it) + { + CArtefact* artefact = smart_cast(*it); + if(artefact){ + res_hit_power_k += artefact->m_ArtefactHitImmunities.AffectHit(1.0f, hit_type); + _af_count += 1.0f; + } + } + res_hit_power_k -= _af_count; + + return res_hit_power_k * hit_power; +} + + +float CActor::HitBoosters(float hit_power, ALife::EHitType hit_type) +{ + float res_hit_power_k = 1.0f; + float _af_count = 0.0f; + + for (u16 i = 0; i < conditions().Eat_Effects.size(); i++){ + if (conditions().Eat_Effects[i].BoosterParam.EffectIsBooster && (conditions().Eat_Effects[i].DurationExpiration >= Device.fTimeGlobal) && (conditions().Eat_Effects[i].UseTimeExpiration < Device.fTimeGlobal)){ + res_hit_power_k += conditions().Eat_Effects[i].BoosterParam.BoosterHitImmunities.AffectHit(1.0f, hit_type); + _af_count += 1.0f; + } + } + res_hit_power_k -= _af_count; + + return res_hit_power_k * hit_power; +} + + +void CActor::SetZoomRndSeed (s32 Seed) +{ + if (0 != Seed) m_ZoomRndSeed = Seed; + else m_ZoomRndSeed = s32(Level().timeServer_Async()); +}; + +void CActor::SetShotRndSeed (s32 Seed) +{ + if (0 != Seed) m_ShotRndSeed = Seed; + else m_ShotRndSeed = s32(Level().timeServer_Async()); +}; + +Fvector CActor::GetMissileOffset () const +{ + return m_vMissileOffset; +} + +void CActor::SetMissileOffset (const Fvector &vNewOffset) +{ + m_vMissileOffset.set(vNewOffset); +} + +void CActor::spawn_supplies () +{ + inherited::spawn_supplies (); + CInventoryOwner::spawn_supplies (); +} + + +void CActor::AnimTorsoPlayCallBack(CBlend* B) +{ + CActor* actor = (CActor*)B->CallbackParam; + actor->m_bAnimTorsoPlayed = FALSE; +} + +void CActor::SetActorVisibility(u16 who, float value) +{ + CUIMotionIcon &motion_icon = CurrentGameUI()->UIMainIngameWnd->MotionIcon(); + motion_icon.SetActorVisibility (who, value); +} + +void CActor::UpdateMotionIcon(u32 mstate_rl) +{ + CUIMotionIcon &motion_icon=CurrentGameUI()->UIMainIngameWnd->MotionIcon(); + if(mstate_rl&mcClimb) + { + motion_icon.ShowState(CUIMotionIcon::stClimb); + } + else + { + if(mstate_rl&mcCrouch) + { + if (!isActorAccelerated(mstate_rl, IsZoomAimingMode())) + motion_icon.ShowState(CUIMotionIcon::stCreep); + else + motion_icon.ShowState(CUIMotionIcon::stCrouch); + } + else + if(mstate_rl&mcSprint) + motion_icon.ShowState(CUIMotionIcon::stSprint); + else + if(mstate_rl&mcAnyMove && isActorAccelerated(mstate_rl, IsZoomAimingMode())) + motion_icon.ShowState(CUIMotionIcon::stRun); + else + motion_icon.ShowState(CUIMotionIcon::stNormal); + } + +/* + stNormal, -- + stCrouch, -- + stCreep, -- + stClimb, -- + stRun, -- + stSprint, -- +*/ +} + + +CPHDestroyable* CActor::ph_destroyable () +{ + return smart_cast(character_physics_support()); +} + +CEntityConditionSimple *CActor::create_entity_condition (CEntityConditionSimple* ec) +{ + if(!ec) + m_entity_condition = new CActorCondition(this); + else + m_entity_condition = smart_cast(ec); + + return (inherited::create_entity_condition(m_entity_condition)); +} + +DLL_Pure *CActor::_construct () +{ + m_pPhysics_support = new CCharacterPhysicsSupport(CCharacterPhysicsSupport::etActor,this); + CEntityAlive::_construct (); + CInventoryOwner::_construct (); + CStepManager::_construct (); + + return (this); +} + +bool CActor::use_center_to_aim () const +{ + return (!(mstate_real&mcCrouch)); +} + + + +bool CActor::can_attach(const CInventoryItem *inventory_item) const +{ + const CAttachableItem *item = smart_cast(inventory_item); + if (!item || (item && !item->can_be_attached()))/*(!item->enabled() || !item->can_be_attached()))*/ + return (false); + + //можно ли присоединять объекты такого типа + if( m_attach_item_sections.end() == std::find(m_attach_item_sections.begin(),m_attach_item_sections.end(),inventory_item->object().cNameSect()) ) + return false; + + //если уже есть присоединненый объет такого типа + if(attached(inventory_item->object().cNameSect())) + return false; + + return true; +} + +void CActor::OnDifficultyChanged () +{ + // immunities + VERIFY(g_SingleGameDifficulty>=egdNovice && g_SingleGameDifficulty<=egdMaster); + LPCSTR diff_name = get_token_name(difficulty_type_token, g_SingleGameDifficulty); + string128 tmp; + strconcat (sizeof(tmp),tmp,"actor_immunities_",diff_name); + conditions().LoadImmunities (tmp,pSettings); + // hit probability + strconcat (sizeof(tmp),tmp,"hit_probability_",diff_name); + hit_probability = pSettings->r_float(*cNameSect(),tmp); +} + +CVisualMemoryManager *CActor::visual_memory () const +{ + return (&memory().visual()); +} + +float CActor::GetMass () +{ + return g_Alive()?character_physics_support()->movement()->GetMass():m_pPhysicsShell?m_pPhysicsShell->getMass():0; +} + +bool CActor::is_on_ground() +{ + return (character_physics_support()->movement()->Environment() != CPHMovementControl::peInAir); +} + +CCustomOutfit* CActor::GetOutfit() const +{ + PIItem _of = inventory().m_slots[OUTFIT_SLOT].m_pIItem; + return _of?smart_cast(_of):NULL; +} + +// lost alpha start + +void CActor::RechargeTorchBattery(void) +{ + m_current_torch->Recharge(); +} + +CTorch *CActor::GetCurrentTorch(void) +{ + if (inventory().ItemFromSlot(TORCH_SLOT)) + { + CTorch *torch = smart_cast(inventory().ItemFromSlot(TORCH_SLOT)); + if (torch) + m_current_torch = torch; + else + m_current_torch = 0; + } else + m_current_torch = 0; + + return m_current_torch; +} + +bool CActor::UsingTurret() +{ + return m_holder && smart_cast(m_holder); +} + +u16 CActor::GetTurretTemp() +{ + CMountedTurret *turret = smart_cast(m_holder); + R_ASSERT(turret); + return turret->GetTemperature(); +} + +void CActor::SetDirectionSlowly(Fvector pos, float time) +{ + if(!m_ScriptCameraDirection) + m_ScriptCameraDirection = new CScriptCameraDirection(); + + m_ScriptCameraDirection->Start(this, pos, time); +} + +void CActor::SetIconState(EActorState state, bool show) +{ + m_icons_state.set (1 << state, show); +} + +float CActor::SetWalkAccel(float new_value) +{ + float old_value = m_fWalkAccel; + m_fWalkAccel = new_value; + return old_value; +} + +bool CActor::CanPutInSlot(PIItem item, u32 slot) +{ + if (slot == RIFLE_2_SLOT && !m_secondRifleSlotAllowed) + { + return false; + } + return CInventoryOwner::CanPutInSlot(item, slot); +} diff --git a/src/xrGameLA/Actor.h b/src/xrGameLA/Actor.h new file mode 100644 index 000000000..a87f89c16 --- /dev/null +++ b/src/xrGameLA/Actor.h @@ -0,0 +1,862 @@ +#pragma once + +#include "../feel_touch.h" +#include "../feel_sound.h" +#include "../iinputreceiver.h" +#include "../../Include/xrRender/KinematicsAnimated.h" +#include "actor_flags.h" +#include "actor_defs.h" +#include "entity_alive.h" +#include "PHMovementControl.h" +#include "PhysicsShell.h" +#include "InventoryOwner.h" +#include "../StatGraph.h" +#include "PhraseDialogManager.h" +#include "torch.h" +#include "step_manager.h" +#include "ActorState.h" +#include "ui_defs.h" + +using namespace ACTOR_DEFS; + +class CInfoPortion; +struct GAME_NEWS_DATA; +class CActorCondition; +class CCustomOutfit; +class CKnownContactsRegistryWrapper; +class CEncyclopediaRegistryWrapper; +class CGameTaskRegistryWrapper; +class CGameNewsRegistryWrapper; +class CCharacterPhysicsSupport; +class CActorCameraManager; +// refs +class ENGINE_API CCameraBase; +class ENGINE_API CBoneInstance; +class ENGINE_API CBlend; +class CWeaponList; +class CEffectorBobbing; +class CHolderCustom; +class CUsableScriptObject; + +//struct SShootingEffector; +struct SSleepEffector; +class CSleepEffectorPP; +class CInventoryBox; +//class CActorEffector; + +class CHudItem; +class CArtefact; + +class CCar; + +struct SActorMotions; +struct SActorVehicleAnims; +class CActorCondition; +class SndShockEffector; +class CScriptCameraDirection; +class CActorFollowerMngr; +class CGameTaskManager; + +class CCameraShotEffector; +class CActorInputHandler; + +class CActorMemory; +class CActorStatisticMgr; + +class CLocationManager; +class CTorch; + +class CActor: + public CEntityAlive, + public IInputReceiver, + public Feel::Touch, + public CInventoryOwner, + public CPhraseDialogManager, + public CStepManager, + public Feel::Sound +#ifdef DEBUG + ,public pureRender +#endif +{ + friend class CActorCondition; +private: + typedef CEntityAlive inherited; + ////////////////////////////////////////////////////////////////////////// + // General fucntions + ////////////////////////////////////////////////////////////////////////// +public: + CActor (); + virtual ~CActor (); + +public: + virtual BOOL AlwaysTheCrow () { return TRUE; } + + virtual CAttachmentOwner* cast_attachment_owner () {return this;} + virtual CInventoryOwner* cast_inventory_owner () {return this;} + virtual CActor* cast_actor () {return this;} + virtual CGameObject* cast_game_object () {return this;} + virtual IInputReceiver* cast_input_receiver () {return this;} + virtual CCharacterPhysicsSupport* character_physics_support () {return m_pPhysics_support;} + virtual CCharacterPhysicsSupport* character_physics_support () const {return m_pPhysics_support;} + virtual CPHDestroyable* ph_destroyable () ; + CHolderCustom* Holder () {return m_holder;} +public: + + virtual void Load ( LPCSTR section ); + + virtual void shedule_Update ( u32 T ); + virtual void UpdateCL ( ); + + virtual void OnEvent ( NET_Packet& P, u16 type ); + + // Render + virtual void renderable_Render (); + virtual BOOL renderable_ShadowGenerate (); + virtual void feel_sound_new (CObject* who, int type, CSound_UserDataPtr user_data, const Fvector& Position, float power); + virtual Feel::Sound* dcast_FeelSound () { return this; } + float m_snd_noise; +#ifdef DEBUG + virtual void OnRender (); +#endif + + + ///////////////////////////////////////////////////////////////// + // Inventory Owner + +public: + //information receive & dialogs + virtual bool OnReceiveInfo (shared_str info_id) const; + virtual void OnDisableInfo (shared_str info_id) const; +// virtual void ReceivePdaMessage (u16 who, EPdaMsg msg, shared_str info_id); + + virtual void NewPdaContact (CInventoryOwner*); + virtual void LostPdaContact (CInventoryOwner*); + +protected: +// virtual void AddMapLocationsFromInfo (const CInfoPortion* info_portion) const; + virtual void AddEncyclopediaArticle (const CInfoPortion* info_portion) const; + virtual void AddGameTask (const CInfoPortion* info_portion) const; +protected: +struct SDefNewsMsg{ + GAME_NEWS_DATA* news_data; + u32 time; + bool operator < (const SDefNewsMsg& other) const {return time>other.time;} + }; + xr_vector m_defferedMessages; + void UpdateDefferedMessages(); +public: + void AddGameNews_deffered (GAME_NEWS_DATA& news_data, u32 delay); + virtual void AddGameNews (GAME_NEWS_DATA& news_data); +protected: + CGameTaskManager* m_game_task_manager; + CActorStatisticMgr* m_statistic_manager; +public: + virtual void StartTalk (CInventoryOwner* talk_partner); + virtual void RunTalkDialog (CInventoryOwner* talk_partner); + CGameTaskManager& GameTaskManager() const {return *m_game_task_manager;} + CActorStatisticMgr& StatisticMgr() {return *m_statistic_manager;} + CEncyclopediaRegistryWrapper *encyclopedia_registry; + CGameNewsRegistryWrapper *game_news_registry; + CCharacterPhysicsSupport *m_pPhysics_support; + + virtual LPCSTR Name () const {return CInventoryOwner::Name();} + +public: + //PhraseDialogManager + virtual void ReceivePhrase (DIALOG_SHARED_PTR& phrase_dialog); + virtual void UpdateAvailableDialogs (CPhraseDialogManager* partner); + virtual void TryToTalk (); + bool OnDialogSoundHandlerStart (CInventoryOwner *inv_owner, LPCSTR phrase); + bool OnDialogSoundHandlerStop (CInventoryOwner *inv_owner); + + + virtual void reinit (); + virtual void reload (LPCSTR section); + virtual bool use_bolts () const; + + void OnItemTake (CInventoryItem *inventory_item, bool duringSpawn) override; + + virtual void OnItemRuck (CInventoryItem *inventory_item, EItemPlace previous_place); + virtual void OnItemBelt (CInventoryItem *inventory_item, EItemPlace previous_place); + + virtual void OnItemDrop (CInventoryItem *inventory_item); + virtual void OnItemDropUpdate (); + + virtual void OnPlayHeadShotParticle (NET_Packet P); + + + virtual void Die (CObject* who); + virtual void Hit (SHit* pHDS); + virtual void PHHit (float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type /* = ALife::eHitTypeWound */); + virtual void HitSignal (float P, Fvector &vLocalDir, CObject* who, s16 element); + void HitSector (CObject* who, CObject* weapon); + void HitMark (float P, Fvector dir, CObject* who, s16 element, Fvector position_in_bone_space, float impulse, ALife::EHitType hit_type); + + virtual float GetMass () ; + virtual float Radius () const; + virtual void g_PerformDrop (); + + + virtual bool NeedToDestroyObject() const; + virtual ALife::_TIME_ID TimePassedAfterDeath() const; + + +public: + //сон +// void UpdateSleep (); + + //свойства артефактов + virtual void UpdateArtefactsOnBeltAndOutfit (); + virtual void MoveArtefactBelt (const CArtefact* artefact, bool on_belt); + virtual float HitArtefactsOnBelt (float hit_power, ALife::EHitType hit_type); + //tatarinrafa:for boosters + virtual float HitBoosters (float hit_power, ALife::EHitType hit_type); + const xr_vector& ArtefactsOnBelt() {return m_ArtefactsOnBelt;} +protected: + //звук тяжелого дыхания + ref_sound m_HeavyBreathSnd; + ref_sound m_BloodSnd; + + xr_vector m_ArtefactsOnBelt; + +protected: + //Sleep params + //время когда актера надо разбудить + ALife::_TIME_ID m_dwWakeUpTime; + float m_fOldTimeFactor; + float m_fOldOnlineRadius; + float m_fSleepTimeFactor; + + ///////////////////////////////////////////////////////////////// + // misc properties +protected: + // Death + float hit_slowmo; + float hit_probability; + + // media + SndShockEffector* m_sndShockEffector; + CScriptCameraDirection* m_ScriptCameraDirection; + xr_vector sndHit[ALife::eHitTypeMax]; + ref_sound sndDie[SND_DIE_COUNT]; + + + float m_fLandingTime; + float m_fJumpTime; + float m_fFallTime; + float m_fCamHeightFactor; + float m_fImmunityCoef; + float m_fDispersionCoef; + + // Dropping + BOOL b_DropActivated; + float f_DropPower; + + //random seed для Zoom mode + s32 m_ZoomRndSeed; + //random seed для Weapon Effector Shot + s32 m_ShotRndSeed; + + bool m_bOutBorder; + //сохраняет счетчик объектов в feel_touch, для которых необходимо обновлять размер колижена с актером + u32 m_feel_touch_characters; + //разрешения на удаление трупа актера + //после того как контролирующий его игрок зареспавнился заново. + //устанавливается в game +private: + void SwitchOutBorder(bool new_border_state); +public: + bool m_bAllowDeathRemove; +// u32 m_u32RespawnTime; + + //////////////////////////////////////////////////////// + void SetZoomRndSeed (s32 Seed = 0); + s32 GetZoomRndSeed () { return m_ZoomRndSeed; }; + //////////////////////////////////////////////////////// + void SetShotRndSeed (s32 Seed = 0); + s32 GetShotRndSeed () { return m_ShotRndSeed; }; + +public: + void detach_Vehicle (); + void steer_Vehicle (float angle); + void attach_Vehicle (CHolderCustom* vehicle); + + virtual bool can_attach (const CInventoryItem *inventory_item) const; + void cam_Set (EActorCameras style); + bool use_Vehicle (CHolderCustom* object); + bool use_MountedWeapon (CHolderCustom* object); +protected: + CHolderCustom* m_holder; + u16 m_holderID; + bool use_Holder (CHolderCustom* holder); + void ActorUse (); + + + ///////////////////////////////////////////////////////// + // actor model & animations + ///////////////////////////////////////////////////////// +protected: + BOOL m_bAnimTorsoPlayed; + static void AnimTorsoPlayCallBack(CBlend* B); + + // Rotation + SRotation r_torso; + float r_torso_tgt_roll; + //положение торса без воздействия эффекта отдачи оружия + SRotation unaffected_r_torso; + + //ориентация модели + float r_model_yaw_dest; + float r_model_yaw; // orientation of model + float r_model_yaw_delta; // effect on multiple "strafe"+"something" + + +public: + SActorMotions* m_anims; + SActorVehicleAnims* m_vehicle_anims; + + CBlend* m_current_legs_blend; + CBlend* m_current_torso_blend; + CBlend* m_current_jump_blend; + MotionID m_current_legs; + MotionID m_current_torso; + MotionID m_current_head; + + bool b_saveAllowed; + + // callback на анимации модели актера + void SetCallbacks (); + void ResetCallbacks (); + static void __stdcall Spin0Callback (CBoneInstance*); + static void __stdcall Spin1Callback (CBoneInstance*); + static void __stdcall ShoulderCallback (CBoneInstance*); + static void __stdcall HeadCallback (CBoneInstance*); + static void __stdcall VehicleHeadCallback (CBoneInstance*); + + virtual const SRotation Orientation () const { return r_torso; }; + SRotation &Orientation () { return r_torso; }; + + void g_SetAnimation (u32 mstate_rl); + void g_SetSprintAnimation(u32 mstate_rl,MotionID &head,MotionID &torso,MotionID &legs); + ////////////////////////////////////////////////////////////////////////// + // HUD + ////////////////////////////////////////////////////////////////////////// +public: + virtual void OnHUDDraw (CCustomHUD* hud); + BOOL HUDview ( )const ; + + //visiblity + virtual float ffGetFov () const { return 90.f; } + virtual float ffGetRange () const { return 500.f; } + + + ////////////////////////////////////////////////////////////////////////// + // Cameras and effectors + ////////////////////////////////////////////////////////////////////////// +public: + CActorCameraManager& Cameras () {VERIFY(m_pActorEffector); return *m_pActorEffector;} + IC CCameraBase* cam_Active () {return cameras[cam_active];} + IC CCameraBase* cam_FirstEye () {return cameras[eacFirstEye];} + +protected: + void cam_Update (float dt, float fFOV); + void camUpdateLadder (float dt); + void cam_SetLadder (); + void cam_UnsetLadder (); + float currentFOV (); + + // Cameras + CCameraBase* cameras[eacMaxCam]; + EActorCameras cam_active; + float fPrevCamPos; + Fvector vPrevCamDir; + float fCurAVelocity; + CEffectorBobbing* pCamBobbing; +//try shooting effector +// void LoadShootingEffector (LPCSTR section); +// SShootingEffector* m_pShootingEffector; + + void LoadSleepEffector (LPCSTR section); + SSleepEffector* m_pSleepEffector; + CSleepEffectorPP* m_pSleepEffectorPP; + + //менеджер эффекторов, есть у каждого актрера + CActorCameraManager* m_pActorEffector; + static float f_Ladder_cam_limit; + + float maxHudWetness_; + float wetnessAccmBase_; + float wetnessDecreaseF_; + + //////////////////////////////////////////// + // для взаимодействия с другими персонажами + // или предметами + /////////////////////////////////////////// +public: + virtual void feel_touch_new (CObject* O); + virtual void feel_touch_delete (CObject* O); + virtual BOOL feel_touch_contact (CObject* O); + virtual BOOL feel_touch_on_contact (CObject* O); + + CGameObject* ObjectWeLookingAt () {return m_pObjectWeLookingAt;} + CInventoryOwner* PersonWeLookingAt () {return m_pPersonWeLookingAt;} + LPCSTR GetDefaultActionForObject () {return *m_sDefaultObjAction;} +//. void AddFollower (u16 id); +//. void RemoveFollower (u16 id); +//. void SendCmdToFollowers (int cmd); +protected: +//. void DestroyFollowerInternal();//hack +//. CActorFollowerMngr& Followers (); +//. CActorFollowerMngr* m_followers; + CUsableScriptObject* m_pUsableObject; + // Person we're looking at + CInventoryOwner* m_pPersonWeLookingAt; + CHolderCustom* m_pHolderWeLookingAt; + CGameObject* m_pObjectWeLookingAt; + CInventoryBox* m_pInvBoxWeLookingAt; + + // Tip for action for object we're looking at + shared_str m_sDefaultObjAction; + shared_str m_sCharacterUseAction; + shared_str m_sDeadCharacterUseAction; + shared_str m_sDeadCharacterUseOrDragAction; + shared_str m_sCarCharacterUseAction; + shared_str m_sInventoryItemUseAction; + shared_str m_sInventoryBoxUseAction; + shared_str m_sTurretCharacterUseAction; + + //режим подбирания предметов + bool m_bPickupMode; + //расстояние подсветки предметов + float m_fPickupInfoRadius; + + void PickupModeUpdate (); + void PickupInfoDraw (CObject* object); + void PickupModeUpdate_COD (); + +public: + void PickupModeOn (); + void PickupModeOff (); + + + + ////////////////////////////////////////////////////////////////////////// + // Motions (передвижения актрера) + ////////////////////////////////////////////////////////////////////////// +public: + void g_cl_CheckControls (u32 mstate_wf, Fvector &vControlAccel, float &Jump, float dt); + void g_cl_ValidateMState (float dt, u32 mstate_wf); + void g_cl_Orientate (u32 mstate_rl, float dt); + void g_sv_Orientate (u32 mstate_rl, float dt); + void g_Orientate (u32 mstate_rl, float dt); + bool g_LadderOrient () ; + void UpdateMotionIcon (u32 mstate_rl); + + bool CanAccelerate (); + bool CanJump (); + bool CanMove (); + float CameraHeight (); + bool CanSprint (); + bool CanRun (); + void StopAnyMove (); + void BreakSprint (); + + bool AnyAction () {return (mstate_real & mcAnyAction) != 0;}; + bool AnyMove () {return (mstate_real & mcAnyMove) != 0;}; + + bool is_jump (); + // Max Weight when you can walk + float MaxWalkWeight () const; + // Max Weight bonus from stuff like Outfit, Boosters, Artifacts, etc. + float GetAdditionalWeight () const override; + // Max Weight bonus from Outfit + float GetOutfitWeightBonus () const override; + u32 MovingState () const {return mstate_real;} + + // For activating sprint when reloading + u8 trySprintCounter_; +protected: + u32 mstate_wishful; + u32 mstate_old; + u32 mstate_real; + + BOOL m_bJumpKeyPressed; + + float m_fWalkAccel; + float m_fJumpSpeed; + float m_fRunFactor; + float m_fRunBackFactor; + float m_fWalkBackFactor; + float m_fCrouchFactor; + float m_fClimbFactor; + float m_fSprintFactor; + + float m_fWalk_StrafeFactor; + float m_fRun_StrafeFactor; + float m_fPriceFactor; + float m_fZoomInertCoef; + + //tatarinrafa: for additional jump speed sprint speed walk speed needs + float m_fRunFactorAdditional; + float m_fSprintFactorAdditional; + + //Для ограничение настакивания кучи артов дающих эти бонусы + float m_fRunFactorAdditionalLimit; + float m_fSprintFactorAdditionalLimit; + float m_fJumpFactorAdditionalLimit; + ////////////////////////////////////////////////////////////////////////// + // User input/output + ////////////////////////////////////////////////////////////////////////// +public: + virtual void IR_OnMouseMove (int x, int y); + virtual void IR_OnKeyboardPress (int dik); + virtual void IR_OnKeyboardRelease (int dik); + virtual void IR_OnKeyboardHold (int dik); + virtual void IR_OnMouseWheel (int direction); + virtual float GetLookFactor (); + + ////////////////////////////////////////////////////////////////////////// + // Weapon fire control (оружие актрера) + ////////////////////////////////////////////////////////////////////////// +public: + virtual void g_WeaponBones (int &L, int &R1, int &R2); + virtual void g_fireParams (const CHudItem* pHudItem, Fvector& P, Fvector& D); + virtual BOOL g_State (SEntityState& state) const; + virtual float GetWeaponAccuracy () const; + bool IsZoomAimingMode () const {return m_bZoomAimingMode;} + +protected: + //если актер целится в прицел + bool m_bZoomAimingMode; + + //настройки аккуратности стрельбы + //базовая дисперсия (когда игрок стоит на месте) + float m_fDispBase; + float m_fDispAim; + //коэффициенты на сколько процентов увеличится базовая дисперсия + //учитывает скорость актера + float m_fDispVelFactor; + //если актер бежит + float m_fDispAccelFactor; + //если актер сидит + float m_fDispCrouchFactor; + //crouch+no acceleration + float m_fDispCrouchNoAccelFactor; + //смещение firepoint относительно default firepoint для бросания болтов и гранат + Fvector m_vMissileOffset; +public: + // Получение, и запись смещения для гранат + Fvector GetMissileOffset () const; + void SetMissileOffset (const Fvector &vNewOffset); + +protected: + //косточки используемые при стрельбе + int m_r_hand; + int m_l_finger1; + int m_r_finger2; + int m_head; + + int m_l_clavicle; + int m_r_clavicle; + int m_spine2; + int m_spine1; + int m_spine; + int m_neck; + + + + ////////////////////////////////////////////////////////////////////////// + // Network + ////////////////////////////////////////////////////////////////////////// + void ConvState (u32 mstate_rl, string128 *buf); +public: + virtual BOOL net_Spawn ( CSE_Abstract* DC); + virtual void net_Export ( NET_Packet& P); // export to server + virtual void net_Import ( NET_Packet& P); // import from server + virtual void net_Destroy (); + virtual BOOL net_Relevant ();// { return getSVU() | getLocal(); }; // relevant for export to server + virtual void net_Relcase ( CObject* O ); // + virtual void xr_stdcall on_requested_spawn (CObject *object); + //object serialization + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + virtual void net_Save (NET_Packet& P) ; + virtual BOOL net_SaveRelevant () ; +protected: + xr_deque NET; + Fvector NET_SavedAccel; + net_update NET_Last; + BOOL NET_WasInterpolating; // previous update was by interpolation or by extrapolation + u32 NET_Time; // server time of last update + + //--------------------------------------------- + void net_Import_Base ( NET_Packet& P); + void net_Import_Physic ( NET_Packet& P); + void net_Import_Base_proceed ( ); + void net_Import_Physic_proceed ( ); + //--------------------------------------------- + + + +//////////////////////////////////////////////////////////////////////////// +virtual bool can_validate_position_on_spawn (){return false;} + /////////////////////////////////////////////////////// + // апдайт с данными физики + xr_deque NET_A; + + //--------------------------------------------- +// bool m_bHasUpdate; + /// spline coeff ///////////////////// + float SCoeff[3][4]; //коэффициэнты для сплайна Бизье + float HCoeff[3][4]; //коэффициэнты для сплайна Эрмита + Fvector IPosS, IPosH, IPosL; //положение актера после интерполяции Бизье, Эрмита, линейной + +#ifdef DEBUG + DEF_DEQUE (VIS_POSITION, Fvector); + + VIS_POSITION LastPosS; + VIS_POSITION LastPosH; + VIS_POSITION LastPosL; +#endif + + + SPHNetState LastState; + SPHNetState RecalculatedState; + SPHNetState PredictedState; + + InterpData IStart; + InterpData IRec; + InterpData IEnd; + + bool m_bInInterpolation; + bool m_bInterpolate; + u32 m_dwIStartTime; + u32 m_dwIEndTime; + u32 m_dwILastUpdateTime; + + //--------------------------------------------- + DEF_DEQUE (PH_STATES, SPHNetState); + PH_STATES m_States; + u16 m_u16NumBones; + void net_ExportDeadBody (NET_Packet &P); + //--------------------------------------------- + void CalculateInterpolationParams(); + //--------------------------------------------- + virtual void make_Interpolation (); +#ifdef DEBUG + //--------------------------------------------- + virtual void OnRender_Network(); + //--------------------------------------------- +#endif + + // TODO: Friendly Indicator + //ref_geom hFriendlyIndicator; + + ////////////////////////////////////////////////////////////////////////// + // Actor physics + ////////////////////////////////////////////////////////////////////////// +public: + void g_Physics (Fvector& accel, float jump, float dt); + virtual void ForceTransform (const Fmatrix &m); + void SetPhPosition (const Fmatrix& pos); + virtual void PH_B_CrPr (); // actions & operations before physic correction-prediction steps + virtual void PH_I_CrPr (); // actions & operations after correction before prediction steps + virtual void PH_A_CrPr (); // actions & operations after phisic correction-prediction steps +// virtual void UpdatePosStack ( u32 Time0, u32 Time1 ); + virtual void MoveActor (Fvector NewPos, Fvector NewDir); + + virtual void SpawnAmmoForWeapon (CInventoryItem *pIItem); + virtual void RemoveAmmoForWeapon (CInventoryItem *pIItem); + virtual void spawn_supplies (); + virtual bool human_being () const + { + return (true); + } + + virtual shared_str GetDefaultVisualOutfit () const {return m_DefaultVisualOutfit;}; + virtual void SetDefaultVisualOutfit (shared_str DefaultOutfit) {m_DefaultVisualOutfit = DefaultOutfit;}; + + //Functions for actor legs and actor shadows #+# SkyLoader + virtual shared_str GetDefaultVisualOutfit_legs () const {return m_DefaultVisualOutfit_legs;}; + virtual void SetDefaultVisualOutfit_legs (shared_str DefaultOutfit) {m_DefaultVisualOutfit_legs = DefaultOutfit;}; + virtual void SetDrawLegs (bool DrawLegs) {m_bDrawLegs = DrawLegs;}; + virtual bool DrawLegs () const {return m_bDrawLegs;} + virtual void SetActorShadows (bool ActorShadows) {m_bActorShadows = ActorShadows;}; + virtual bool IsActorShadowsOn () const {return m_bActorShadows;} + virtual bool IsFirstEye () const {return (m_bFirstEye);} + virtual bool IsLookAt () const {return (eacLookAt==cam_active);} + virtual bool CanBeDrawLegs () const {return (m_bCanBeDrawLegs);} + // + + virtual u16 HolderID () const {return m_holderID;} + + virtual void UpdateAnimation () { g_SetAnimation(mstate_real); }; + + virtual void ChangeVisual ( shared_str NewVisual ); + virtual void OnChangeVisual (); + + virtual void RenderIndicator (Fvector dpos, float r1, float r2, const ui_shader& IndShader); + virtual void RenderText (LPCSTR Text, Fvector dpos, float* pdup, u32 color); + + // Calc hud wetness for raindrops shader + void ComputeHudWetness(); + + ////////////////////////////////////////////////////////////////////////// + // Controlled Routines + ////////////////////////////////////////////////////////////////////////// + + void set_input_external_handler (CActorInputHandler *handler); + bool input_external_handler_installed () const {return (m_input_external_handler != 0);} + + IC void lock_accel_for (u32 time){m_time_lock_accel = Device.dwTimeGlobal + time;} + +private: + CActorInputHandler *m_input_external_handler; + u32 m_time_lock_accel; + + ///////////////////////////////////////// + // DEBUG INFO +protected: + CStatGraph *pStatGraph; + + shared_str m_DefaultVisualOutfit; + shared_str m_DefaultVisualOutfit_legs; + bool m_bDrawLegs; + bool m_bFirstEye; + bool m_bCanBeDrawLegs; + bool m_bActorShadows; + + LPCSTR invincibility_fire_shield_3rd; + LPCSTR invincibility_fire_shield_1st; + shared_str m_sHeadShotParticle; + u32 last_hit_frame; +#ifdef DEBUG + friend class CLevelGraph; +#endif + Fvector m_AutoPickUp_AABB; + Fvector m_AutoPickUp_AABB_Offset; + + void Check_for_AutoPickUp (); + void SelectBestWeapon (CObject* O); +public: + void SetWeaponHideState (u32 State, bool bSet); + virtual CCustomOutfit* GetOutfit() const; +private: + CActorCondition *m_entity_condition; + +protected: + virtual CEntityConditionSimple *create_entity_condition (CEntityConditionSimple* ec); + +public: + IC CActorCondition &conditions () const; + virtual DLL_Pure *_construct (); + virtual bool natural_weapon () const {return false;} + virtual bool natural_detector () const {return false;} + virtual bool use_center_to_aim () const; + +protected: + u16 m_iLastHitterID; + u16 m_iLastHittingWeaponID; + s16 m_s16LastHittedElement; + Fvector m_vLastHitDir; + Fvector m_vLastHitPos; + float m_fLastHealth; + bool m_bWasHitted; + bool m_bWasBackStabbed; + + bool m_secondRifleSlotAllowed; + + virtual bool Check_for_BackStab_Bone (u16 element); +public: + virtual void SetHitInfo (CObject* who, CObject* weapon, s16 element, Fvector Pos, Fvector Dir); + + virtual void OnHitHealthLoss (float NewHealth); + virtual void OnCriticalHitHealthLoss (); + virtual void OnCriticalWoundHealthLoss (); + virtual void OnCriticalRadiationHealthLoss (); + + virtual bool InventoryAllowSprint (); + virtual void OnNextWeaponSlot (); + virtual void OnPrevWeaponSlot (); + bool CanPutInSlot (PIItem item, u32 slot) override; + +public: + + virtual void on_weapon_shot_start (CWeapon *weapon); + virtual void on_weapon_shot_stop (CWeapon *weapon); + virtual void on_weapon_hide (CWeapon *weapon); + Fvector weapon_recoil_delta_angle (); + Fvector weapon_recoil_last_delta (); +protected: + virtual void update_camera (CCameraShotEffector* effector); + //step manager + virtual bool is_on_ground (); + +private: + CActorMemory *m_memory; + +public: + void SetActorVisibility (u16 who, float value); + IC CActorMemory &memory () const {VERIFY(m_memory); return(*m_memory); }; + + void OnDifficultyChanged (); + + IC float HitProbability () {return hit_probability;} + virtual CVisualMemoryManager*visual_memory () const; + + virtual BOOL BonePassBullet (int boneID); + virtual void On_B_NotCurrentEntity (); + +private: + collide::rq_results RQR; + BOOL CanPickItem (const CFrustum& frustum, const Fvector& from, CObject* item); + xr_vector ISpatialResult; + +private: + CLocationManager *m_location_manager; + +public: + IC const CLocationManager &locations () const + { + VERIFY (m_location_manager); + return (*m_location_manager); + } +private: + ALife::_OBJECT_ID m_holder_id; + +public: + virtual bool register_schedule () const {return false;} +// lost alpha start + + void SwitchNightVision(); + CCar* GetAttachedCar (); + void RechargeTorchBattery (); + CTorch *GetCurrentTorch (); + bool IsLimping (); + bool IsReloadingWeapon (); + bool UsingTurret (); + u16 GetTurretTemp (); + void SetDirectionSlowly (Fvector pos, float time); + void SetIconState (EActorState state, bool show); + bool GetIconState (EActorState state) { return !!m_icons_state.is(1 << state); } + Flags32& GetIconsState () { return m_icons_state; } + + float SetWalkAccel (float new_value); + void SetImmunityCoeff (float value) { m_fImmunityCoef = value;} + void SetCamDispersionCoeff (float value) { m_fDispersionCoef = value; } + void SetZoomInertCoeff (float value) { m_fZoomInertCoef = value; } + void SetSprintFactor (float value) { m_fSprintFactor = value; } + void SetPriceFactor (float value) { m_fPriceFactor = value; } + float GetPriceFactor () { return m_fPriceFactor; } +private: + CTorch* m_current_torch; + Flags32 m_icons_state; +}; + +extern bool isActorAccelerated (u32 mstate, bool ZoomMode); + +IC CActorCondition &CActor::conditions () const{ VERIFY(m_entity_condition); return(*m_entity_condition);} + +extern CActor* g_actor; +CActor* Actor (); +extern const float s_fFallTime; diff --git a/src/xrGameLA/ActorAnimation.cpp b/src/xrGameLA/ActorAnimation.cpp new file mode 100644 index 000000000..b9f2b80ae --- /dev/null +++ b/src/xrGameLA/ActorAnimation.cpp @@ -0,0 +1,639 @@ +#include "stdafx.h" +#include "Actor.h" +#include "ActorAnimation.h" +#include "actor_anim_defs.h" + +#include "hudmanager.h" +#include "weapon.h" +#include "inventory.h" +#include "missile.h" +#include "level.h" +#ifdef DEBUG +#include "PHDebug.h" +#endif +#include "hit.h" +#include "PHDestroyable.h" +#include "Car.h" +#include "../Include/xrRender/Kinematics.h" +#include "clsid_game.h" +#include "ai_object_location.h" +#include "game_cl_base.h" +#include "../motion.h" +#include "artifact.h" +#include "IKLimbsController.h" +#include "player_hud.h" + +static const float y_spin0_factor = 0.0f; +static const float y_spin1_factor = 0.4f; +static const float y_shoulder_factor = 0.4f; +static const float y_head_factor = 0.2f; +static const float p_spin0_factor = 0.0f; +static const float p_spin1_factor = 0.2f; +static const float p_shoulder_factor = 0.7f; +static const float p_head_factor = 0.1f; +static const float r_spin0_factor = 0.3f; +static const float r_spin1_factor = 0.3f; +static const float r_shoulder_factor = 0.2f; +static const float r_head_factor = 0.2f; + + +void CActor::Spin0Callback(CBoneInstance* B) +{ + CActor* A = static_cast(B->callback_param()); VERIFY (A); + + Fmatrix spin; + float bone_yaw = angle_normalize_signed(A->r_torso.yaw - A->r_model_yaw - A->r_model_yaw_delta)*y_spin0_factor; + float bone_pitch = angle_normalize_signed(A->r_torso.pitch)*p_spin0_factor; + float bone_roll = angle_normalize_signed(A->r_torso.roll)*r_spin0_factor; + Fvector c = B->mTransform.c; + spin.setXYZ (-bone_pitch,bone_yaw,bone_roll); + B->mTransform.mulA_43(spin); + B->mTransform.c = c; +} +void CActor::Spin1Callback(CBoneInstance* B) +{ + CActor* A = static_cast(B->callback_param()); VERIFY (A); + + Fmatrix spin; + float bone_yaw = angle_normalize_signed(A->r_torso.yaw - A->r_model_yaw - A->r_model_yaw_delta)*y_spin1_factor; + float bone_pitch = angle_normalize_signed(A->r_torso.pitch)*p_spin1_factor; + float bone_roll = angle_normalize_signed(A->r_torso.roll)*r_spin1_factor; + Fvector c = B->mTransform.c; + spin.setXYZ (-bone_pitch,bone_yaw,bone_roll); + B->mTransform.mulA_43(spin); + B->mTransform.c = c; +} +void CActor::ShoulderCallback(CBoneInstance* B) +{ + CActor* A = static_cast(B->callback_param()); VERIFY (A); + Fmatrix spin; + float bone_yaw = angle_normalize_signed(A->r_torso.yaw - A->r_model_yaw - A->r_model_yaw_delta)*y_shoulder_factor; + float bone_pitch = angle_normalize_signed(A->r_torso.pitch)*p_shoulder_factor; + float bone_roll = angle_normalize_signed(A->r_torso.roll)*r_shoulder_factor; + Fvector c = B->mTransform.c; + spin.setXYZ (-bone_pitch,bone_yaw,bone_roll); + B->mTransform.mulA_43(spin); + B->mTransform.c = c; +} +void CActor::HeadCallback(CBoneInstance* B) +{ + CActor* A = static_cast(B->callback_param()); VERIFY (A); + Fmatrix spin; + float bone_yaw = angle_normalize_signed(A->r_torso.yaw - A->r_model_yaw - A->r_model_yaw_delta)*y_head_factor; + float bone_pitch = angle_normalize_signed(A->r_torso.pitch)*p_head_factor; + float bone_roll = angle_normalize_signed(A->r_torso.roll)*r_head_factor; + Fvector c = B->mTransform.c; + spin.setXYZ (-bone_pitch,bone_yaw,bone_roll); + B->mTransform.mulA_43(spin); + B->mTransform.c = c; +} + +void CActor::VehicleHeadCallback(CBoneInstance* B) +{ + CActor* A = static_cast(B->callback_param()); VERIFY (A); + Fmatrix spin; + float bone_yaw = angle_normalize_signed(A->r_torso.yaw)*0.75f; + float bone_pitch = angle_normalize_signed(A->r_torso.pitch)*0.75f; + float bone_roll = angle_normalize_signed(A->r_torso.roll)*r_head_factor; + Fvector c = B->mTransform.c; + spin.setHPB (bone_yaw,bone_pitch,-bone_roll); + B->mTransform.mulA_43(spin); + B->mTransform.c = c; +} + +void STorsoWpn::Create(IKinematicsAnimated* K, LPCSTR base0, LPCSTR base1) +{ + char buf[128]; + if (!xr_strcmp(base1, "_0")) + { + //для безоружного гг отдельные анимации + if (xr_strcmp(base0, "norm")) + moving[eIdle] = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_aim_1")); + else + moving[eIdle] = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_idle_0")); + moving[eWalk] = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_aim_2")); + moving[eRun] = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_aim_3")); + moving[eSprint] = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_escape_0")); + }else{ + moving[eIdle] = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_aim_1")); + moving[eWalk] = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_aim_2")); + moving[eRun] = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_aim_3")); + moving[eSprint] = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_escape_0")); + } + zoom = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_aim_0")); + holster = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_holster_0")); + draw = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_draw_0")); + reload = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_reload_0")); + reload_1 = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_reload_1")); + reload_2 = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_reload_2")); + drop = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_drop_0")); + attack = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_attack_1")); + attack_zoom = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_attack_0")); + fire_idle = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_attack_1")); + fire_end = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_torso",base1,"_attack_2")); + all_attack_0 = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_all",base1,"_attack_0")); + all_attack_1 = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_all",base1,"_attack_1")); + all_attack_2 = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base0,"_all",base1,"_attack_2")); +} +void SAnimState::Create(IKinematicsAnimated* K, LPCSTR base0, LPCSTR base1) +{ + char buf[128]; + legs_fwd = K->ID_Cycle(strconcat(sizeof(buf),buf,base0,base1,"_fwd_0")); + legs_back = K->ID_Cycle(strconcat(sizeof(buf),buf,base0,base1,"_back_0")); + legs_ls = K->ID_Cycle(strconcat(sizeof(buf),buf,base0,base1,"_ls_0")); + legs_rs = K->ID_Cycle(strconcat(sizeof(buf),buf,base0,base1,"_rs_0")); +} + + +void SActorState::CreateClimb(IKinematicsAnimated* K) +{ + string128 buf,buf1; + string16 base; + + //climb anims + xr_strcpy(base,"cl"); + legs_idle = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_idle_1")); + m_torso_idle = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_torso_0_aim_0")); + m_walk.Create (K,base,"_run"); + m_run.Create (K,base,"_run"); + + //norm anims + xr_strcpy(base,"norm"); + legs_turn = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_turn")); + death = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_death_0")); + m_torso[0].Create(K,base,"_1"); + m_torso[1].Create(K,base,"_2"); + m_torso[2].Create(K,base,"_3"); + m_torso[3].Create(K,base,"_4"); + m_torso[4].Create(K,base,"_5"); + m_torso[5].Create(K,base,"_6"); + m_torso[6].Create(K,base,"_7"); + m_torso[7].Create(K,base,"_8"); + m_torso[8].Create(K,base,"_9"); + m_torso[9].Create(K,base,"_10"); + m_torso[10].Create(K,base,"_11"); + m_torso[11].Create(K,base,"_12"); + m_torso[12].Create(K,base,"_13"); + m_torso[13].Create(K,base,"_0"); + + + m_head_idle.invalidate();///K->ID_Cycle("head_idle_0"); + jump_begin = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_jump_begin")); + jump_idle = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_jump_idle")); + landing[0] = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_jump_end")); + landing[1] = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_jump_end_1")); + + for (int k=0; k<12; ++k) + m_damage[k] = K->ID_FX(strconcat(sizeof(buf),buf,base,"_damage_",itoa(k,buf1,10))); +} + + +void SActorState::Create(IKinematicsAnimated* K, LPCSTR base) +{ + string128 buf,buf1; + legs_turn = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_turn")); + legs_idle = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_idle_0")); + death = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_death_0")); + + m_walk.Create (K,base,"_walk"); + m_run.Create (K,base,"_run"); + + m_torso[0].Create(K,base,"_1"); + m_torso[1].Create(K,base,"_2"); + m_torso[2].Create(K,base,"_3"); + m_torso[3].Create(K,base,"_4"); + m_torso[4].Create(K,base,"_5"); + m_torso[5].Create(K,base,"_6"); + m_torso[6].Create(K,base,"_7"); + m_torso[7].Create(K,base,"_8"); + m_torso[8].Create(K,base,"_9"); + m_torso[9].Create(K,base,"_10"); + m_torso[10].Create(K,base,"_11"); + m_torso[11].Create(K,base,"_12"); + m_torso[12].Create(K,base,"_13"); + m_torso[13].Create(K,base,"_0"); + + m_torso_idle = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_torso_0_aim_0")); + m_head_idle = K->ID_Cycle("head_idle_0"); + jump_begin = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_jump_begin")); + jump_idle = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_jump_idle")); + landing[0] = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_jump_end")); + landing[1] = K->ID_Cycle(strconcat(sizeof(buf),buf,base,"_jump_end_1")); + + for (int k=0; k<12; ++k) + m_damage[k] = K->ID_FX(strconcat(sizeof(buf),buf,base,"_damage_",itoa(k,buf1,10))); +} + +void SActorSprintState::Create(IKinematicsAnimated* K) +{ + //leg anims + legs_fwd=K->ID_Cycle("norm_escape_00"); + legs_ls=K->ID_Cycle("norm_escape_ls_00"); + legs_rs=K->ID_Cycle("norm_escape_rs_00"); +} + +void SActorMotions::Create(IKinematicsAnimated* V) +{ + m_dead_stop = V->ID_Cycle("norm_dead_stop_0"); + + m_normal.Create (V,"norm"); + m_crouch.Create (V,"cr"); + //m_climb.Create (V,"cr"); + m_climb.CreateClimb(V); + m_sprint.Create(V); +} + +SActorVehicleAnims::SActorVehicleAnims() +{ + +} +void SActorVehicleAnims::Create(IKinematicsAnimated* V) +{ + for(u16 i=0;TYPES_NUMBER>i;++i) m_vehicles_type_collections[i].Create(V,i); +} + +SVehicleAnimCollection::SVehicleAnimCollection() +{ + for(u16 i=0;MAX_IDLES>i;++i) idles[i].invalidate(); + idles_num = 0; + steer_left.invalidate(); + steer_right.invalidate(); +} + +void SVehicleAnimCollection::Create(IKinematicsAnimated* V,u16 num) +{ + string128 buf,buff1,buff2; + strconcat(sizeof(buff1),buff1,itoa(num,buf,10),"_"); + + steer_left = V->ID_Cycle_Safe(strconcat(sizeof(buf),buf,"steering_idle_",buff1,"ls")); + if (!steer_left) steer_left = V->ID_Cycle("steering_idle_0_ls"); + + steer_right = V->ID_Cycle_Safe(strconcat(sizeof(buf),buf,"steering_idle_",buff1,"rs")); + if (!steer_right) steer_right = V->ID_Cycle("steering_idle_0_rs"); + + for(int i=0;MAX_IDLES>i;++i){ + idles[i]=V->ID_Cycle_Safe(strconcat(sizeof(buf),buf,"steering_idle_",buff1,itoa(i,buff2,10))); + if (idles[i]) + idles_num++; + else + break; + } +} + +void CActor::steer_Vehicle(float angle) +{ + if(!m_holder) return; + CCar* car = smart_cast(m_holder); + u16 anim_type = car->DriverAnimationType(); + SVehicleAnimCollection& anims=m_vehicle_anims->m_vehicles_type_collections[anim_type]; + if(angle==0.f) smart_cast (Visual())->PlayCycle(anims.idles[0]); + else if(angle>0.f) smart_cast (Visual())->PlayCycle(anims.steer_right); + else smart_cast (Visual())->PlayCycle(anims.steer_left); +} + +void legs_play_callback (CBlend *blend) +{ + CActor *object = (CActor*)blend->CallbackParam; + VERIFY (object); + object->m_current_legs.invalidate(); +} + +void CActor::g_SetSprintAnimation( u32 mstate_rl,MotionID &head,MotionID &torso,MotionID &legs) +{ + SActorSprintState& sprint = m_anims->m_sprint; + + if (mstate_rl & mcFwd) legs = sprint.legs_fwd; + else if (mstate_rl & mcLStrafe) legs = sprint.legs_ls; + else if (mstate_rl & mcRStrafe) legs = sprint.legs_rs; +} + +CMotion* FindMotionKeys(MotionID motion_ID,IRenderVisual* V) +{ + IKinematicsAnimated* VA = smart_cast(V); + return (VA && motion_ID.valid())?VA->LL_GetRootMotion(motion_ID):0; +} + +#ifdef DEBUG +BOOL g_ShowAnimationInfo = TRUE; +#endif // DEBUG +char* mov_state[] ={ + "idle", + "walk", + "run", + "sprint", +}; +void CActor::g_SetAnimation( u32 mstate_rl ) +{ + + + if (!g_Alive()) { + if (m_current_legs||m_current_torso){ + SActorState* ST = 0; + if (mstate_rl&mcCrouch) ST = &m_anims->m_crouch; + else ST = &m_anims->m_normal; + mstate_real = 0; + m_current_legs.invalidate (); + m_current_torso.invalidate (); + + smart_cast(Visual())->PlayCycle(m_anims->m_dead_stop); + } + + return; + } + STorsoWpn::eMovingState moving_idx = STorsoWpn::eIdle; + SActorState* ST = 0; + SAnimState* AS = 0; + + if (mstate_rl&mcCrouch) ST = &m_anims->m_crouch; + else if (mstate_rl&mcClimb) ST = &m_anims->m_climb; + else ST = &m_anims->m_normal; + + bool bAccelerated = isActorAccelerated(mstate_rl, IsZoomAimingMode()); + if ( bAccelerated ){ + AS = &ST->m_run; + }else{ + AS = &ST->m_walk; + } + if(mstate_rl&mcAnyMove){ + if( bAccelerated ) + moving_idx = STorsoWpn::eRun; + else + moving_idx = STorsoWpn::eWalk; + } + // анимации + MotionID M_legs; + MotionID M_torso; + MotionID M_head; + + //если мы просто стоим на месте + bool is_standing = false; + + // Legs + if (mstate_rl&mcLanding) M_legs = ST->landing[0]; + else if (mstate_rl&mcLanding2) M_legs = ST->landing[1]; + else if ((mstate_rl&mcTurn)&& + !(mstate_rl&mcClimb)) M_legs = ST->legs_turn; + else if (mstate_rl&mcFall) M_legs = ST->jump_idle; + else if (mstate_rl&mcJump) M_legs = ST->jump_begin; + else if (mstate_rl&mcFwd) M_legs = AS->legs_fwd; + else if (mstate_rl&mcBack) M_legs = AS->legs_back; + else if (mstate_rl&mcLStrafe) M_legs = AS->legs_ls; + else if (mstate_rl&mcRStrafe) M_legs = AS->legs_rs; + else is_standing = true; + + if(mstate_rl&mcSprint){ + g_SetSprintAnimation (mstate_rl,M_head,M_torso,M_legs); + moving_idx = STorsoWpn::eSprint; + } + //--------------------------------------------------------------- + if (this == Level().CurrentViewEntity()) + { + if ((mstate_rl&mcSprint) != (mstate_old&mcSprint)) + { + g_player_hud->OnMovementChanged(mcSprint); + }else + if ((mstate_rl&mcAnyMove) != (mstate_old&mcAnyMove)) + { + g_player_hud->OnMovementChanged(mcAnyMove); + } + }; + //----------------------------------------------------------------------- + // Torso + if(mstate_rl&mcClimb) + { + if (mstate_rl&mcFwd) M_torso = AS->legs_fwd; + else if (mstate_rl&mcBack) M_torso = AS->legs_back; + else if (mstate_rl&mcLStrafe) M_torso = AS->legs_ls; + else if (mstate_rl&mcRStrafe) M_torso = AS->legs_rs; + } + + if(!M_torso) + { + CInventoryItem* _i = inventory().ActiveItem(); + CHudItem *H = smart_cast(_i); + CWeapon *W = smart_cast(_i); + CMissile *M = smart_cast(_i); + CArtefact *A = smart_cast(_i); + + if (_i) { + if (H) { + VERIFY(H->animation_slot() <= _total_anim_slots_); + STorsoWpn* TW = &ST->m_torso[H->animation_slot() - 1]; + if (!b_DropActivated&&!fis_zero(f_DropPower)){ + M_torso = TW->drop; + if (!M_torso) + { + Msg("! drop animation for %s", *(H->object().cName())); + M_torso = ST->m_torso_idle; + }; + m_bAnimTorsoPlayed = TRUE; + }else{ + if (!m_bAnimTorsoPlayed) { + if (W) { + bool K =inventory().GetActiveSlot() == KNIFE_SLOT; + bool R3 = W->IsTriStateReload(); + + if(K) + { + switch (W->GetState()){ + case CWeapon::eIdle: M_torso = TW->moving[moving_idx]; break; + + case CWeapon::eFire: + if(is_standing && IsActorShadowsOn()) + M_torso = M_legs = M_head = TW->all_attack_0; + else + M_torso = TW->attack_zoom; + break; + + case CWeapon::eFire2: + if(is_standing && IsActorShadowsOn()) + M_torso = M_legs = M_head = TW->all_attack_1; + else + M_torso = TW->fire_idle; + break; + + case CWeapon::eReload: M_torso = TW->reload; break; + case CWeapon::eShowing: M_torso = TW->draw; break; + case CWeapon::eHiding: M_torso = TW->holster; break; + default : M_torso = TW->moving[moving_idx]; break; + } + } + else + { + switch (W->GetState()){ + case CWeapon::eIdle: M_torso = W->IsZoomed()?TW->zoom:TW->moving[moving_idx]; break; + case CWeapon::eFire: M_torso = W->IsZoomed()?TW->attack_zoom:TW->attack; break; + case CWeapon::eFire2: M_torso = W->IsZoomed()?TW->attack_zoom:TW->attack; break; + case CWeapon::eReload: + if(!R3) + M_torso = TW->reload; + else{ + CWeapon::EWeaponSubStates sub_st = W->GetReloadState(); + switch (sub_st){ + case CWeapon::eSubstateReloadBegin: M_torso = TW->reload; break; + case CWeapon::eSubstateReloadInProcess: M_torso = TW->reload_1; break; + case CWeapon::eSubstateReloadEnd: M_torso = TW->reload_2; break; + default: M_torso = TW->reload; break; + } + }break; + + case CWeapon::eShowing: M_torso = TW->draw; break; + case CWeapon::eHiding: M_torso = TW->holster; break; + default : M_torso = TW->moving[moving_idx]; break; + } + } + } + else if (M) { + if(is_standing && IsActorShadowsOn()) + { + switch (M->GetState()){ + case CMissile::eShowing : M_torso = TW->draw; break; + case CMissile::eHiding : M_torso = TW->holster; break; + case CMissile::eIdle : M_torso = TW->moving[moving_idx]; break; + case CMissile::eThrowStart : M_torso = M_legs = M_head = TW->all_attack_0; break; + case CMissile::eReady : M_torso = M_legs = M_head = TW->all_attack_1; break; + case CMissile::eThrow : M_torso = M_legs = M_head = TW->all_attack_2; break; + case CMissile::eThrowEnd : M_torso = M_legs = M_head = TW->all_attack_2; break; + default : M_torso = TW->moving[moving_idx]; break; + } + } + else + { + switch (M->GetState()){ + case CMissile::eShowing : M_torso = TW->draw; break; + case CMissile::eHiding : M_torso = TW->holster; break; + case CMissile::eIdle : M_torso = TW->moving[moving_idx]; break; + case CMissile::eThrowStart : M_torso = TW->attack_zoom; break; + case CMissile::eReady : M_torso = TW->fire_idle; break; + case CMissile::eThrow : M_torso = TW->fire_end; break; + case CMissile::eThrowEnd : M_torso = TW->fire_end; break; + default : M_torso = TW->moving[moving_idx]; break; + } + } + } + else if (A){ + switch(A->GetState()){ + case CArtefact::eIdle : M_torso = TW->moving[moving_idx]; break; + case CArtefact::eShowing : M_torso = TW->draw; break; + case CArtefact::eHiding : M_torso = TW->holster; break; + case CArtefact::eActivating : M_torso = TW->zoom; break; + default : M_torso = TW->moving[moving_idx]; + } + + } + } + } + } + } else { + STorsoWpn* TW = &ST->m_torso[13]; + if (!m_bAnimTorsoPlayed) + M_torso = TW->moving[moving_idx]; + } + } + + if (!M_legs) + { + if((mstate_rl&mcCrouch)&&!isActorAccelerated(mstate_rl, IsZoomAimingMode()))//!(mstate_rl&mcAccel)) + { + M_legs=smart_cast(Visual())->ID_Cycle("cr_idle_1"); + } + else + M_legs = ST->legs_idle; + } + if (!M_head) M_head = ST->m_head_idle; + if (!M_torso){ + if (m_bAnimTorsoPlayed) M_torso = m_current_torso; + else M_torso = ST->m_torso_idle; + } + + // есть анимация для всего - запустим / иначе запустим анимацию по частям + if (m_current_torso!=M_torso){ + if (m_bAnimTorsoPlayed) m_current_torso_blend = smart_cast (Visual())->PlayCycle(M_torso,TRUE,AnimTorsoPlayCallBack,this); + else /**/m_current_torso_blend = /**/smart_cast (Visual())->PlayCycle(M_torso); + + m_current_torso=M_torso; + } + if(m_current_head!=M_head) + { + if(M_head)smart_cast(Visual())->PlayCycle(M_head); + m_current_head=M_head; + } + if (m_current_legs!=M_legs){ + float pos = 0.f; + VERIFY (!m_current_legs_blend || !fis_zero(m_current_legs_blend->timeTotal)); + if ((mstate_real&mcAnyMove)&&(mstate_old&mcAnyMove)&&m_current_legs_blend) + pos = fmod(m_current_legs_blend->timeCurrent,m_current_legs_blend->timeTotal)/m_current_legs_blend->timeTotal; + m_current_legs_blend = smart_cast(Visual())->PlayCycle(M_legs,TRUE,legs_play_callback,this); + if ((!(mstate_old&mcAnyMove))&&(mstate_real&mcAnyMove)) + pos = 0.5f*Random.randI(2); + if (m_current_legs_blend) + m_current_legs_blend->timeCurrent = m_current_legs_blend->timeTotal*pos; + m_current_legs = M_legs; + + CStepManager::on_animation_start(M_legs, m_current_legs_blend); + } + + + +#ifdef _DEBUG + if(bDebug){ + UI().Font().pFontStat->OutSetI (0,0); + UI().Font().pFontStat->OutNext("[%s]",mov_state[moving_idx]); + } +#endif + +#ifdef _DEBUG + if ((Level().CurrentControlEntity() == this) && g_ShowAnimationInfo) { + string128 buf; + strcpy(buf,""); + if (isActorAccelerated(mstate_rl, IsZoomAimingMode())) strcat(buf,"Accel "); + if (mstate_rl&mcCrouch) strcat(buf,"Crouch "); + if (mstate_rl&mcFwd) strcat(buf,"Fwd "); + if (mstate_rl&mcBack) strcat(buf,"Back "); + if (mstate_rl&mcLStrafe) strcat(buf,"LStrafe "); + if (mstate_rl&mcRStrafe) strcat(buf,"RStrafe "); + if (mstate_rl&mcJump) strcat(buf,"Jump "); + if (mstate_rl&mcFall) strcat(buf,"Fall "); + if (mstate_rl&mcTurn) strcat(buf,"Turn "); + if (mstate_rl&mcLanding) strcat(buf,"Landing "); + if (mstate_rl&mcLLookout) strcat(buf,"LLookout "); + if (mstate_rl&mcRLookout) strcat(buf,"RLookout "); + if (m_bJumpKeyPressed) strcat(buf,"+Jumping "); + UI().Font().pFontStat->OutNext ("MSTATE: [%s]",buf); +/* + switch (m_PhysicMovementControl->Environment()) + { + case CPHMovementControl::peOnGround: strcpy(buf,"ground"); break; + case CPHMovementControl::peInAir: strcpy(buf,"air"); break; + case CPHMovementControl::peAtWall: strcpy(buf,"wall"); break; + } + UI().Font().pFontStat->OutNext (buf); + UI().Font().pFontStat->OutNext ("Accel [%3.2f, %3.2f, %3.2f]",VPUSH(NET_SavedAccel)); + UI().Font().pFontStat->OutNext ("V [%3.2f, %3.2f, %3.2f]",VPUSH(m_PhysicMovementControl->GetVelocity())); + UI().Font().pFontStat->OutNext ("vertex ID %d",ai_location().level_vertex_id()); + + Game().m_WeaponUsageStatistic->Draw(); + */ + }; +#endif + + if (!m_current_torso_blend) + return; + + IKinematicsAnimated *skeleton_animated = smart_cast(Visual()); + + CMotionDef *motion0 = skeleton_animated->LL_GetMotionDef(m_current_torso); + VERIFY (motion0); + if (!(motion0->flags & esmSyncPart)) + return; + + if (!m_current_legs_blend) + return; + + CMotionDef *motion1 = skeleton_animated->LL_GetMotionDef(m_current_legs); + VERIFY (motion1); + if (!(motion1->flags & esmSyncPart)) + return; + + m_current_torso_blend->timeCurrent = m_current_legs_blend->timeCurrent/m_current_legs_blend->timeTotal*m_current_torso_blend->timeTotal; +} diff --git a/src/xrGameLA/ActorAnimation.h b/src/xrGameLA/ActorAnimation.h new file mode 100644 index 000000000..4c15005e2 --- /dev/null +++ b/src/xrGameLA/ActorAnimation.h @@ -0,0 +1,47 @@ +#ifndef _Actor_Animation_H +#define _Actor_Animation_H +#pragma once + +// animation state constants +//------------------------------------------------------------------------------- +#define _Fwd (mcFwd) +#define _Back (mcBack) +#define _LStr (mcLStrafe) +#define _RStr (mcRStrafe) +#define _FwdLStr (mcFwd|mcLStrafe) +#define _FwdRStr (mcFwd|mcRStrafe) +#define _BackLStr (mcBack|mcLStrafe) +#define _BackRStr (mcBack|mcRStrafe) + +#define _AFwd (mcAccel|mcFwd) +#define _ABack (mcAccel|mcBack) +#define _ALStr (mcAccel|mcLStrafe) +#define _ARStr (mcAccel|mcRStrafe) +#define _AFwdLStr (mcAccel|mcFwd|mcLStrafe) +#define _AFwdRStr (mcAccel|mcFwd|mcRStrafe) +#define _ABackLStr (mcAccel|mcBack|mcLStrafe) +#define _ABackRStr (mcAccel|mcBack|mcRStrafe) +// +#define _Crch (mcCrouch) +#define _ACrch (mcCrouch|mcAccel) +#define _CrchFwd (mcCrouch|mcFwd) +#define _CrchBack (mcCrouch|mcBack) +#define _CrchLStr (mcCrouch|mcLStrafe) +#define _CrchRStr (mcCrouch|mcRStrafe) +#define _CrchFwdLStr (mcCrouch|mcFwd|mcLStrafe) +#define _CrchFwdRStr (mcCrouch|mcFwd|mcRStrafe) +#define _CrchBackLStr (mcCrouch|mcBack|mcLStrafe) +#define _CrchBackRStr (mcCrouch|mcBack|mcRStrafe) +#define _ACrchFwd (mcCrouch|mcAccel|mcFwd) +#define _ACrchBack (mcCrouch|mcAccel|mcBack) +#define _ACrchLStr (mcCrouch|mcAccel|mcLStrafe) +#define _ACrchRStr (mcCrouch|mcAccel|mcRStrafe) +#define _ACrchFwdLStr (mcCrouch|mcAccel|mcFwd|mcLStrafe) +#define _ACrchFwdRStr (mcCrouch|mcAccel|mcFwd|mcRStrafe) +#define _ACrchBackLStr (mcCrouch|mcAccel|mcBack|mcLStrafe) +#define _ACrchBackRStr (mcCrouch|mcAccel|mcBack|mcRStrafe) + +#define _Jump (mcJump) +//------------------------------------------------------------------------------- + +#endif //_Actor_Animation_H diff --git a/src/xrGameLA/ActorCameras.cpp b/src/xrGameLA/ActorCameras.cpp new file mode 100644 index 000000000..12c12ab22 --- /dev/null +++ b/src/xrGameLA/ActorCameras.cpp @@ -0,0 +1,455 @@ +#include "stdafx.h" +#include "Actor.h" +#include "../CameraBase.h" +#ifdef DEBUG +#include "PHDebug.h" +#endif +#include "hit.h" +#include "PHDestroyable.h" +#include "Car.h" + +#include "Weapon.h" +#include "Inventory.h" + +#include "SleepEffector.h" +//#include "ShootingHitEffector.h" +#include "ActorEffector.h" +#include "level.h" +#include "../cl_intersect.h" +#include "../gamemtllib.h" +#include "elevatorstate.h" +#include "CharacterPhysicsSupport.h" +#include "EffectorShot.h" +#include "phcollidevalidator.h" +#include "PHShell.h" +#include "CustomOutfit.h" + +void CActor::cam_Set(EActorCameras style) +{ + if (style != cam_active) + { + if (eacFirstEye == cam_active) + { + m_bFirstEye = false; + CCustomOutfit *pOutfit = smart_cast(inventory().ItemFromSlot(OUTFIT_SLOT)); + if (pOutfit) + pOutfit->OnMoveToSlot(); + else + ChangeVisual(m_DefaultVisualOutfit); + } + else if (eacFirstEye == style) + { + m_bFirstEye = true; + CCustomOutfit *pOutfit = smart_cast(inventory().ItemFromSlot(OUTFIT_SLOT)); + if (pOutfit) + pOutfit->OnMoveToSlot(); + else + ChangeVisual(m_DefaultVisualOutfit_legs); + } + } + CCameraBase* old_cam = cam_Active(); + cam_active = style; + old_cam->OnDeactivate(); + cam_Active()->OnActivate(old_cam); +} +float CActor::f_Ladder_cam_limit = 1.f; +void CActor::cam_SetLadder() +{ + if (CanBeDrawLegs() && !m_bActorShadows) + { + setVisible(FALSE); + m_bDrawLegs = false; + } + + CCameraBase* C = cameras[eacFirstEye]; + g_LadderOrient(); + float yaw = (-XFORM().k.getH()); + float &cam_yaw = C->yaw; + float delta_yaw = angle_difference_signed(yaw, cam_yaw); + + if (-f_Ladder_cam_limitdelta_yaw) + { + yaw = cam_yaw + delta_yaw; + float lo = (yaw - f_Ladder_cam_limit); + float hi = (yaw + f_Ladder_cam_limit); + C->lim_yaw[0] = lo; + C->lim_yaw[1] = hi; + C->bClampYaw = true; + } +} + +void CActor::camUpdateLadder(float dt) +{ + if (!character_physics_support()->movement()->ElevatorState()) + return; + if (cameras[eacFirstEye]->bClampYaw) return; + float yaw = (-XFORM().k.getH()); + + float & cam_yaw = cameras[eacFirstEye]->yaw; + float delta = angle_difference_signed(yaw, cam_yaw); + + if (-0.05fdelta) + { + yaw = cam_yaw + delta; + float lo = (yaw - f_Ladder_cam_limit); + float hi = (yaw + f_Ladder_cam_limit); + cameras[eacFirstEye]->lim_yaw[0] = lo; + cameras[eacFirstEye]->lim_yaw[1] = hi; + cameras[eacFirstEye]->bClampYaw = true; + } + else{ + cam_yaw += delta * _min(dt*10.f, 1.f); + } + + CElevatorState* es = character_physics_support()->movement()->ElevatorState(); + if (es && es->State() == CElevatorState::clbClimbingDown) + { + float &cam_pitch = cameras[eacFirstEye]->pitch; + const float ldown_pitch = cameras[eacFirstEye]->lim_pitch.y; + float delta = angle_difference_signed(ldown_pitch, cam_pitch); + if (delta>0.f) + cam_pitch += delta* _min(dt*10.f, 1.f); + } +} + +void CActor::cam_UnsetLadder() +{ + if (CanBeDrawLegs() && !m_bActorShadows) + { + setVisible(TRUE); + m_bDrawLegs = true; + } + + CCameraBase* C = cameras[eacFirstEye]; + C->lim_yaw[0] = 0; + C->lim_yaw[1] = 0; + C->bClampYaw = false; +} +float CActor::CameraHeight() +{ + Fvector R; + character_physics_support()->movement()->Box().getsize(R); + return m_fCamHeightFactor*R.y; +} + +IC float viewport_near(float& w, float& h) +{ + w = 2.f*VIEWPORT_NEAR*tan(deg2rad(Device.fFOV) / 2.f); + h = w*Device.fASPECT; + float c = _sqrt(w*w + h*h); + return _max(_max(VIEWPORT_NEAR, _max(w, h)), c); +} + +ICF void calc_point(Fvector& pt, float radius, float depth, float alpha) +{ + pt.x = radius*_sin(alpha); + pt.y = radius + radius*_cos(alpha); + pt.z = depth; +} + +ICF BOOL test_point(xrXRC& xrc, const Fmatrix& xform, const Fmatrix33& mat, const Fvector& ext, float radius, float angle) +{ + Fvector pt; + calc_point(pt, radius, VIEWPORT_NEAR / 2, angle); + xform.transform_tiny(pt); + + CDB::RESULT* it = xrc.r_begin(); + CDB::RESULT* end = xrc.r_end(); + for (; it != end; it++) { + CDB::RESULT& O = *it; + if (GMLib.GetMaterialByIdx(O.material)->Flags.is(SGameMtl::flPassable)) continue; + if (CDB::TestBBoxTri(mat, pt, ext, O.verts, FALSE)) + return TRUE; + } + return FALSE; +} + +#include "physics.h" +#include "PHActivationShape.h" +#include "debug_renderer.h" +void CActor::cam_Update(float dt, float fFOV) +{ + if (m_holder) return; + + if (mstate_real & mcClimb&&cam_active != eacFreeLook) + camUpdateLadder(dt); + + Fvector point = { 0, CameraHeight(), 0 }, dangle = { 0, 0, 0 }; + + + Fmatrix xform, xformR; + xform.setXYZ(0, r_torso.yaw, 0); + xform.translate_over(XFORM().c); + + // lookout + if (this == Level().CurrentControlEntity()) + { + if (!fis_zero(r_torso_tgt_roll)){ + Fvector src_pt, tgt_pt; + float radius = point.y*0.5f; + float alpha = r_torso_tgt_roll / 2.f; + float dZ = ((PI_DIV_2 - ((PI + alpha) / 2))); + calc_point(tgt_pt, radius, 0, alpha); + src_pt.set(0, tgt_pt.y, 0); + // init valid angle + float valid_angle = alpha; + // xform with roll + xformR.setXYZ(-r_torso.pitch, r_torso.yaw, -dZ); + Fmatrix33 mat; + mat.i = xformR.i; + mat.j = xformR.j; + mat.k = xformR.k; + // get viewport params + float w, h; + float c = viewport_near(w, h); w /= 2.f; h /= 2.f; + // find tris + Fbox box; + box.invalidate(); + box.modify(src_pt); + box.modify(tgt_pt); + box.grow(c); + + // query + Fvector bc, bd; + Fbox xf; + xf.xform(box, xform); + xf.get_CD(bc, bd); + + xrXRC xrc; + xrc.box_options(0); + xrc.box_query(Level().ObjectSpace.GetStaticModel(), bc, bd); + u32 tri_count = xrc.r_count(); + if (tri_count) { + float da = 0.f; + BOOL bIntersect = FALSE; + Fvector ext = { w, h, VIEWPORT_NEAR / 2 }; + if (test_point(xrc, xform, mat, ext, radius, alpha)){ + da = PI / 1000.f; + if (!fis_zero(r_torso.roll)) + da *= r_torso.roll / _abs(r_torso.roll); + for (float angle = 0.f; _abs(angle)<_abs(alpha); angle += da) + if (test_point(xrc, xform, mat, ext, radius, angle)) { bIntersect = TRUE; break; } + valid_angle = bIntersect ? angle : alpha; + } + } + r_torso.roll = valid_angle*2.f; + r_torso_tgt_roll = r_torso.roll; + } + else + { + r_torso_tgt_roll = 0.f; + r_torso.roll = 0.f; + } + } + if (!fis_zero(r_torso.roll)) + { + float radius = point.y*0.5f; + float valid_angle = r_torso.roll / 2.f; + calc_point(point, radius, 0, valid_angle); + dangle.z = (PI_DIV_2 - ((PI + valid_angle) / 2)); + } + + float flCurrentPlayerY = xform.c.y; + + // Smooth out stair step ups + if ((character_physics_support()->movement()->Environment()==peOnGround) && (flCurrentPlayerY-fPrevCamPos>0)){ + fPrevCamPos += dt*1.5f; + if (fPrevCamPos > flCurrentPlayerY) + fPrevCamPos = flCurrentPlayerY; + if (flCurrentPlayerY-fPrevCamPos>0.2f) + fPrevCamPos = flCurrentPlayerY-0.2f; + point.y += fPrevCamPos-flCurrentPlayerY; + }else{ + fPrevCamPos = flCurrentPlayerY; + } + float _viewport_near = VIEWPORT_NEAR; + // calc point + xform.transform_tiny(point); + + CCameraBase* C = cam_Active(); + + if (eacFirstEye == cam_active) + { + // CCameraBase* C = cameras[eacFirstEye]; + + xrXRC xrc; + xrc.box_options(0); + xrc.box_query(Level().ObjectSpace.GetStaticModel(), point, Fvector().set(VIEWPORT_NEAR, VIEWPORT_NEAR, VIEWPORT_NEAR)); + u32 tri_count = xrc.r_count(); + if (tri_count) + { + _viewport_near = 0.01f; + } + else + { + xr_vector ISpatialResult; + g_SpatialSpacePhysic->q_box(ISpatialResult, 0, STYPE_PHYSIC, point, Fvector().set(VIEWPORT_NEAR, VIEWPORT_NEAR, VIEWPORT_NEAR)); + for (u32 o_it = 0; o_it(ISpatialResult[o_it]); + if (pCPHS) + { + _viewport_near = 0.01f; + break; + } + } + } + } + /* + { + CCameraBase* C = cameras[eacFirstEye]; + float oobox_size = 2*VIEWPORT_NEAR; + + + Fmatrix _rot; + _rot.k = C->vDirection; + _rot.c = C->vPosition; + _rot.i.crossproduct (C->vNormal, _rot.k); + _rot.j.crossproduct (_rot.k, _rot.i); + + + Fvector vbox; + vbox.set (oobox_size, oobox_size, oobox_size); + + Level().debug_renderer().draw_aabb (C->vPosition, 0.05f, 0.051f, 0.05f, D3DCOLOR_XRGB(0,255,0)); + Level().debug_renderer().draw_obb (_rot, Fvector().div(vbox,2.0f), D3DCOLOR_XRGB(255,0,0)); + + dMatrix3 d_rot; + PHDynamicData::FMXtoDMX (_rot, d_rot); + + CPHActivationShape activation_shape; + activation_shape.Create (point, vbox, this); + dBodySetRotation (activation_shape.ODEBody(), d_rot); + + CPHCollideValidator::SetDynamicNotCollide(activation_shape); + activation_shape.Activate (vbox,1,1.f,0.0F); + + point.set (activation_shape.Position()); + + activation_shape.Destroy (); + } +*/ + C->Update (point,dangle); + C->f_fov = fFOV; + if(eacFirstEye != cam_active) + { + cameras[eacLookAt]->Update(point, dangle); + cameras[eacLookAt]->f_fov = fFOV; + } + + if (psActorFlags.test(AF_PSP) && eacFreeLook != cam_active) + { + Cameras().UpdateFromCamera(C); + } + else + { + Cameras().UpdateFromCamera(cameras[eacFirstEye]); + } + + fCurAVelocity = vPrevCamDir.sub(cameras[eacFirstEye]->vDirection).magnitude() / Device.fTimeDelta; + vPrevCamDir = cameras[eacFirstEye]->vDirection; + + if (Level().CurrentEntity() == this) + { + Level().Cameras().UpdateFromCamera(C); + if((eacFirstEye == cam_active || eacLookAt == cam_active) && !Level().Cameras().GetCamEffector(cefDemo)){ + Cameras().ApplyDevice (_viewport_near); + } + } +} + +// shot effector stuff +void CActor::update_camera(CCameraShotEffector* effector) +{ + if (!effector) return; + // if (Level().CurrentViewEntity() != this) return; + + CCameraBase* pACam = NULL; + if (eacLookAt == cam_active) + pACam = cam_Active(); + else + pACam = cam_FirstEye(); + + if (!pACam) return; + + if (pACam->bClampPitch) + { + while (pACam->pitch < pACam->lim_pitch[0]) + pACam->pitch += PI_MUL_2; + while (pACam->pitch > pACam->lim_pitch[1]) + pACam->pitch -= PI_MUL_2; + }; + + effector->ApplyLastAngles(&(pACam->pitch), &(pACam->yaw)); + + if (pACam->bClampYaw) clamp(pACam->yaw, pACam->lim_yaw[0], pACam->lim_yaw[1]); + if (pACam->bClampPitch) clamp(pACam->pitch, pACam->lim_pitch[0], pACam->lim_pitch[1]); +} + + +#ifdef DEBUG +void dbg_draw_frustum(float FOV, float _FAR, float A, Fvector &P, Fvector &D, Fvector &U); +extern Flags32 dbg_net_Draw_Flags; + +void CActor::OnRender() +{ + if (!bDebug) return; + + if ((dbg_net_Draw_Flags.is_any((1 << 5)))) + character_physics_support()->movement()->dbg_Draw(); + + OnRender_Network(); + + inherited::OnRender(); +} +#endif +//try shooting effector +/* +void CActor::LoadShootingEffector (LPCSTR section) +{ + +if(!m_pShootingEffector) +m_pShootingEffector = xr_new(); + + +m_pShootingEffector->ppi.duality.h = pSettings->r_float(section,"duality_h"); +m_pShootingEffector->ppi.duality.v = pSettings->r_float(section,"duality_v"); +m_pShootingEffector->ppi.gray = pSettings->r_float(section,"gray"); +m_pShootingEffector->ppi.blur = pSettings->r_float(section,"blur"); +m_pShootingEffector->ppi.noise.intensity = pSettings->r_float(section,"noise_intensity"); +m_pShootingEffector->ppi.noise.grain = pSettings->r_float(section,"noise_grain"); +m_pShootingEffector->ppi.noise.fps = pSettings->r_float(section,"noise_fps"); +VERIFY(!fis_zero(m_pShootingEffector->ppi.noise.fps)); + +sscanf(pSettings->r_string(section,"color_base"), "%f,%f,%f", &m_pShootingEffector->ppi.color_base.r, &m_pShootingEffector->ppi.color_base.g, &m_pShootingEffector->ppi.color_base.b); +sscanf(pSettings->r_string(section,"color_gray"), "%f,%f,%f", &m_pShootingEffector->ppi.color_gray.r, &m_pShootingEffector->ppi.color_gray.g, &m_pShootingEffector->ppi.color_gray.b); +sscanf(pSettings->r_string(section,"color_add"), "%f,%f,%f", &m_pShootingEffector->ppi.color_add.r, &m_pShootingEffector->ppi.color_add.g, &m_pShootingEffector->ppi.color_add.b); + +m_pShootingEffector->time = pSettings->r_float(section,"time"); +m_pShootingEffector->time_attack = pSettings->r_float(section,"time_attack"); +m_pShootingEffector->time_release = pSettings->r_float(section,"time_release"); + +} +*/ +void CActor::LoadSleepEffector(LPCSTR section) +{ + if (!m_pSleepEffector) + m_pSleepEffector = new SSleepEffector(); + + m_pSleepEffector->ppi.duality.h = pSettings->r_float(section, "duality_h"); + m_pSleepEffector->ppi.duality.v = pSettings->r_float(section, "duality_v"); + m_pSleepEffector->ppi.gray = pSettings->r_float(section, "gray"); + m_pSleepEffector->ppi.blur = pSettings->r_float(section, "blur"); + m_pSleepEffector->ppi.noise.intensity = pSettings->r_float(section, "noise_intensity"); + m_pSleepEffector->ppi.noise.grain = pSettings->r_float(section, "noise_grain"); + m_pSleepEffector->ppi.noise.fps = pSettings->r_float(section, "noise_fps"); + VERIFY(!fis_zero(m_pSleepEffector->ppi.noise.fps)); + + sscanf(pSettings->r_string(section, "color_base"), "%f,%f,%f", &m_pSleepEffector->ppi.color_base.r, &m_pSleepEffector->ppi.color_base.g, &m_pSleepEffector->ppi.color_base.b); + sscanf(pSettings->r_string(section, "color_gray"), "%f,%f,%f", &m_pSleepEffector->ppi.color_gray.r, &m_pSleepEffector->ppi.color_gray.g, &m_pSleepEffector->ppi.color_gray.b); + sscanf(pSettings->r_string(section, "color_add"), "%f,%f,%f", &m_pSleepEffector->ppi.color_add.r, &m_pSleepEffector->ppi.color_add.g, &m_pSleepEffector->ppi.color_add.b); + + m_pSleepEffector->time = pSettings->r_float(section, "time"); + m_pSleepEffector->time_attack = pSettings->r_float(section, "time_attack"); + m_pSleepEffector->time_release = pSettings->r_float(section, "time_release"); +} diff --git a/src/xrGameLA/ActorCondition.cpp b/src/xrGameLA/ActorCondition.cpp new file mode 100644 index 000000000..30280dcf4 --- /dev/null +++ b/src/xrGameLA/ActorCondition.cpp @@ -0,0 +1,443 @@ +#include "pch_script.h" +#include "actorcondition.h" +#include "actor.h" +#include "actorEffector.h" +#include "inventory.h" +#include "level.h" +#include "sleepeffector.h" +#include "game_base_space.h" +#include "autosave_manager.h" +#include "xrserver.h" +#include "ai_space.h" +#include "script_callback_ex.h" +#include "script_game_object.h" +#include "game_object_space.h" +#include "script_callback_ex.h" +#include "object_broker.h" +#include "weapon.h" + +#define MAX_SATIETY 1.0f +#define START_SATIETY 0.5f +void ClearActorEffects() +{ + Actor()->conditions().Eat_Effects.clear(); +} + +BOOL GodMode () +{ + if (GameID() == GAME_SINGLE) + return psActorFlags.test(AF_GODMODE); + return FALSE; +} + +bool CActor::IsLimping() +{ + return m_entity_condition->IsLimping(); +} + +CActorCondition::CActorCondition(CActor *object) : + inherited (object) +{ + m_fJumpPower = 0.f; + m_fStandPower = 0.f; + m_fWalkPower = 0.f; + m_fJumpWeightPower = 0.f; + m_fWalkWeightPower = 0.f; + m_fOverweightWalkK = 0.f; + m_fOverweightJumpK = 0.f; + m_fAccelK = 0.f; + m_fSprintK = 0.f; + m_fAlcohol = 0.f; + m_fSatiety = 1.0f; + m_fThirsty = 1.0f; + + VERIFY (object); + m_object = object; + m_condition_flags.zero (); + + fHandsHideTime = -1; + m_fBoostersAddWeight = 0; +} + +CActorCondition::~CActorCondition(void) +{ +} + +void CActorCondition::LoadCondition(LPCSTR entity_section) +{ + inherited::LoadCondition(entity_section); + + LPCSTR section = READ_IF_EXISTS(pSettings,r_string,entity_section,"condition_sect",entity_section); + + m_fJumpPower = pSettings->r_float(section,"jump_power"); + m_fStandPower = pSettings->r_float(section,"stand_power"); + m_fWalkPower = pSettings->r_float(section,"walk_power"); + m_fJumpWeightPower = pSettings->r_float(section,"jump_weight_power"); + m_fWalkWeightPower = pSettings->r_float(section,"walk_weight_power"); + m_fOverweightWalkK = pSettings->r_float(section,"overweight_walk_k"); + m_fOverweightJumpK = pSettings->r_float(section,"overweight_jump_k"); + m_fAccelK = pSettings->r_float(section,"accel_k"); + m_fSprintK = pSettings->r_float(section,"sprint_k"); + + //порог силы и здоровья меньше которого актер начинает хромать + m_fLimpingHealthBegin = pSettings->r_float(section, "limping_health_begin"); + m_fLimpingHealthEnd = pSettings->r_float(section, "limping_health_end"); + R_ASSERT (m_fLimpingHealthBegin<=m_fLimpingHealthEnd); + + m_fLimpingPowerBegin = pSettings->r_float(section, "limping_power_begin"); + m_fLimpingPowerEnd = pSettings->r_float(section, "limping_power_end"); + R_ASSERT (m_fLimpingPowerBegin<=m_fLimpingPowerEnd); + + m_fCantWalkPowerBegin = pSettings->r_float(section, "cant_walk_power_begin"); + m_fCantWalkPowerEnd = pSettings->r_float(section, "cant_walk_power_end"); + R_ASSERT (m_fCantWalkPowerBegin<=m_fCantWalkPowerEnd); + + m_fCantSprintPowerBegin = pSettings->r_float(section, "cant_sprint_power_begin"); + m_fCantSprintPowerEnd = pSettings->r_float(section, "cant_sprint_power_end"); + R_ASSERT (m_fCantSprintPowerBegin<=m_fCantSprintPowerEnd); + + m_fPowerLeakSpeed = pSettings->r_float(section,"max_power_leak_speed"); + + m_fV_Alcohol = pSettings->r_float(section,"alcohol_v"); + + m_fV_Satiety = pSettings->r_float(section,"satiety_v"); + m_fV_SatietyPower = pSettings->r_float(section,"satiety_power_v"); + m_fV_SatietyHealth = pSettings->r_float(section,"satiety_health_v"); + + m_fV_Thirsty = pSettings->r_float(section, "thirsty_v"); + m_fV_ThirstyPower = pSettings->r_float(section, "thirsty_power_v"); + m_fV_ThirstyHealth = pSettings->r_float(section, "thirsty_health_v"); + + m_MaxWalkWeight = pSettings->r_float(section,"max_walk_weight"); +} + + +//вычисление параметров с ходом времени +#include "HUDManager.h" + +void CActorCondition::UpdateCondition() +{ + + if (!object().g_Alive()) return; + if (!object().Local() && m_object != Level().CurrentViewEntity()) return; + + if (fHandsHideTime != -1 && fHandsHideTime < Device.fTimeGlobal){ + //Msg("Return Hands"); + Actor()->SetWeaponHideState(INV_STATE_BLOCK_ALL, false); + fHandsHideTime = -1; + } + + UpdateEatablesEffects(); + + if (GodMode()) return; + + float base_weight = object().MaxCarryWeight(); + float cur_weight = object().inventory().TotalWeight(); + + if ((object().mstate_real&mcAnyMove)) { + ConditionWalk(cur_weight / base_weight, isActorAccelerated(object().mstate_real,object().IsZoomAimingMode()), (object().mstate_real&mcSprint) != 0); + } + else { + ConditionStand(cur_weight / base_weight); + }; + + if( IsGameTypeSingle() ){ + + float k_max_power = 1.0f + _min(cur_weight, base_weight) / base_weight + _max(0.0f, (cur_weight - base_weight)/10.0f); + + SetMaxPower (GetMaxPower() - m_fPowerLeakSpeed*m_fDeltaTime*k_max_power); + } + + + m_fAlcohol += m_fV_Alcohol*m_fDeltaTime; + clamp (m_fAlcohol, 0.0f, 1.0f); + + if ( IsGameTypeSingle() ) + { + CEffectorCam* ce = Actor()->Cameras().GetCamEffector((ECamEffectorType)effAlcohol); + if ((m_fAlcohol>0.0001f) ){ + if(!ce){ + AddEffector(m_object,effAlcohol, "effector_alcohol", GET_KOEFF_FUNC(this, &CActorCondition::GetAlcohol)); + } + }else{ + if(ce) + RemoveEffector(m_object,effAlcohol); + } + + + CEffectorPP* ppe = object().Cameras().GetPPEffector((EEffectorPPType)effPsyHealth); + + string64 pp_sect_name; + shared_str ln = Level().name(); + strconcat (sizeof(pp_sect_name),pp_sect_name, "effector_psy_health", "_", *ln); + if(!pSettings->section_exist(pp_sect_name)) + xr_strcpy (pp_sect_name, "effector_psy_health"); + + if ( !fsimilar(GetPsyHealth(), 1.0f, 0.05f) ) + { + if(!ppe) + { + AddEffector(m_object,effPsyHealth, pp_sect_name, GET_KOEFF_FUNC(this, &CActorCondition::GetPsy)); + } + }else + { + if(ppe) + RemoveEffector(m_object,effPsyHealth); + } + if(fis_zero(GetPsyHealth())) + health() =0.0f; + }; + + UpdateSatiety (); + UpdateThirsty (); + + inherited::UpdateCondition (); + + if( IsGameTypeSingle() ) + UpdateTutorialThresholds(); +} + +void CActorCondition::UpdateSatiety() +{ + if (!IsGameTypeSingle()) return; + + if (m_fSatiety > 0.f) + { + m_fSatiety -= m_fV_Satiety*m_fDeltaTime; + if (m_fSatiety <= 0.f && !GodMode() && object().g_Alive()) //skyloader: kill actor + object().KillEntity(object().ID()); + else + clamp(m_fSatiety, 0.0f, 1.0f); + } + + //сытость увеличивает здоровье только если нет открытых ран + if (!m_bIsBleeding) + { + m_fDeltaHealth += CanBeHarmed() ? (m_fV_SatietyHealth*(m_fSatiety>0.0f ? 1.f : -1.f)*m_fDeltaTime) : 0.f; + } + + //коэффициенты уменьшения восстановления силы от сытости + m_fDeltaPower += m_fV_SatietyPower*(m_fSatiety>0.0f ? 1.f : -1.f)*m_fDeltaTime; +} + +void CActorCondition::UpdateThirsty() +{ + if (!IsGameTypeSingle()) return; + + if (m_fThirsty > 0.f) + { + m_fThirsty -= m_fV_Thirsty*m_fDeltaTime; + if (m_fThirsty <= 0.f && !GodMode() && object().g_Alive()) //skyloader: kill actor + object().KillEntity(object().ID()); + else + clamp (m_fThirsty, 0.0f, 1.0f); + } + + //уталенная жажда увеличивает здоровье только если нет открытых ран + if(!m_bIsBleeding) + { + m_fDeltaHealth += CanBeHarmed() ? (m_fV_ThirstyHealth*(m_fThirsty>0.0f ? 1.f : -1.f)*m_fDeltaTime) : 0.f; + } + + //коэффициенты уменьшения восстановления силы от жажды + m_fDeltaPower += (m_fV_ThirstyPower*(m_fThirsty>0.0f ? 1.f : -1.f))*m_fDeltaTime; +} + + +CWound* CActorCondition::ConditionHit(SHit* pHDS) +{ + if (GodMode()) return NULL; + return inherited::ConditionHit(pHDS); +} + +//weight - "удельный" вес от 0..1 +void CActorCondition::ConditionJump(float weight) +{ + float power = m_fJumpPower; + power += m_fJumpWeightPower*weight*(weight>1.f?m_fOverweightJumpK:1.f); + m_fPower -= HitPowerEffect(power); +} +void CActorCondition::ConditionWalk(float weight, bool accel, bool sprint) +{ + float power = m_fWalkPower; + power += m_fWalkWeightPower*weight*(weight>1.f?m_fOverweightWalkK:1.f); + power *= m_fDeltaTime*(accel?(sprint?m_fSprintK:m_fAccelK):1.f); + m_fPower -= HitPowerEffect(power); +} + +void CActorCondition::ConditionStand(float weight) +{ + float power = m_fStandPower; + power *= m_fDeltaTime; + m_fPower -= power; +} + +bool CActorCondition::IsCantWalk() const +{ + if(m_fPower< m_fCantWalkPowerBegin) + m_bCantWalk = true; + else if(m_fPower > m_fCantWalkPowerEnd) + m_bCantWalk = false; + return m_bCantWalk; +} + +bool CActorCondition::IsCantWalkWeight() +{ + if(IsGameTypeSingle() && !GodMode()) + { + if( object().inventory().TotalWeight() > object().MaxWalkWeight()) + { + m_condition_flags.set (eCantWalkWeight, TRUE); + return true; + } + } + m_condition_flags.set (eCantWalkWeight, FALSE); + return false; +} + +bool CActorCondition::IsCantSprint() const +{ + if(m_fPower< m_fCantSprintPowerBegin) + m_bCantSprint = true; + else if(m_fPower > m_fCantSprintPowerEnd) + m_bCantSprint = false; + return m_bCantSprint; +} + +bool CActorCondition::IsLimping() const +{ + if(m_fPower< m_fLimpingPowerBegin || GetHealth() < m_fLimpingHealthBegin) + m_bLimping = true; + else if(m_fPower > m_fLimpingPowerEnd && GetHealth() > m_fLimpingHealthEnd) + m_bLimping = false; + return m_bLimping; +} +extern bool g_bShowHudInfo; + +void CActorCondition::save(NET_Packet &output_packet) +{ + inherited::save(output_packet); + save_data(m_fAlcohol, output_packet); + save_data(m_condition_flags, output_packet); + save_data(m_fSatiety, output_packet); + save_data(m_fThirsty, output_packet); + save_data(m_MaxWalkWeight, output_packet); + + save_effects(output_packet); +} + +void CActorCondition::load(IReader &input_packet) +{ + inherited::load(input_packet); + load_data(m_fAlcohol, input_packet); + load_data(m_condition_flags, input_packet); + load_data(m_fSatiety, input_packet); + load_data(m_fThirsty, input_packet); + load_data(m_MaxWalkWeight, input_packet); + + load_effects(input_packet); +} + +void CActorCondition::reinit () +{ + inherited::reinit (); + m_bLimping = false; + m_fSatiety = 1.f; + m_fThirsty = 1.f; +} + +void CActorCondition::ChangeAlcohol (const float value) +{ + m_fAlcohol += value; +} + +void CActorCondition::ChangeSatiety(const float value) +{ + m_fSatiety += value; + clamp (m_fSatiety, 0.0f, 1.0f); +} + +void CActorCondition::ChangeThirsty(const float value) +{ + m_fThirsty += value; + clamp(m_fThirsty, 0.0f, 1.0f); +} + +void CActorCondition::UpdateTutorialThresholds() +{ + string256 cb_name; + static float _cPowerThr = pSettings->r_float("tutorial_conditions_thresholds","power"); + static float _cPowerMaxThr = pSettings->r_float("tutorial_conditions_thresholds","max_power"); + static float _cBleeding = pSettings->r_float("tutorial_conditions_thresholds","bleeding"); + static float _cSatiety = pSettings->r_float("tutorial_conditions_thresholds","satiety"); + static float _cThirsty = pSettings->r_float("tutorial_conditions_thresholds","thirsty"); + static float _cRadiation = pSettings->r_float("tutorial_conditions_thresholds","radiation"); + static float _cWpnCondition = pSettings->r_float("tutorial_conditions_thresholds","weapon_jammed"); + static float _cPsyHealthThr = pSettings->r_float("tutorial_conditions_thresholds","psy_health"); + + + + bool b = true; + if(b && !m_condition_flags.test(eCriticalPowerReached) && GetPower()<_cPowerThr){ + m_condition_flags.set (eCriticalPowerReached, TRUE); + b=false; + xr_strcpy(cb_name,"_G.on_actor_critical_power"); + } + + if(b && !m_condition_flags.test(eCriticalMaxPowerReached) && GetMaxPower()<_cPowerMaxThr){ + m_condition_flags.set (eCriticalMaxPowerReached, TRUE); + b=false; + xr_strcpy(cb_name,"_G.on_actor_critical_max_power"); + } + + if(b && !m_condition_flags.test(eCriticalBleedingSpeed) && BleedingSpeed()>_cBleeding){ + m_condition_flags.set (eCriticalBleedingSpeed, TRUE); + b=false; + xr_strcpy(cb_name,"_G.on_actor_bleeding"); + } + + if(b && !m_condition_flags.test(eCriticalSatietyReached) && GetSatiety()<_cSatiety){ + m_condition_flags.set (eCriticalSatietyReached, TRUE); + b=false; + xr_strcpy(cb_name,"_G.on_actor_satiety"); + } + + if (b && !m_condition_flags.test(eCriticalThirstyReached) && GetSatiety()<_cThirsty){ + m_condition_flags.set(eCriticalThirstyReached, TRUE); + b = false; + xr_strcpy(cb_name, "_G.on_actor_thirsty"); + } + + if(b && !m_condition_flags.test(eCriticalRadiationReached) && GetRadiation()>_cRadiation){ + m_condition_flags.set (eCriticalRadiationReached, TRUE); + b=false; + xr_strcpy(cb_name,"_G.on_actor_radiation"); + } + + if(b && !m_condition_flags.test(ePhyHealthMinReached) && GetPsyHealth()>_cPsyHealthThr){ +//. m_condition_flags.set (ePhyHealthMinReached, TRUE); + b=false; + xr_strcpy(cb_name,"_G.on_actor_psy"); + } + + if(b && !m_condition_flags.test(eCantWalkWeight)){ +//. m_condition_flags.set (eCantWalkWeight, TRUE); + b=false; + xr_strcpy(cb_name,"_G.on_actor_cant_walk_weight"); + } + + if(b && !m_condition_flags.test(eWeaponJammedReached)&&m_object->inventory().GetActiveSlot()!=NO_ACTIVE_SLOT){ + PIItem item = m_object->inventory().ItemFromSlot(m_object->inventory().GetActiveSlot()); + CWeapon* pWeapon = smart_cast(item); + if(pWeapon&&pWeapon->GetCondition()<_cWpnCondition){ + m_condition_flags.set (eWeaponJammedReached, TRUE);b=false; + xr_strcpy(cb_name,"_G.on_actor_weapon_jammed"); + } + } + + if(!b){ + luabind::functor fl; + R_ASSERT (ai().script_engine().functor(cb_name,fl)); + fl (); + } +} diff --git a/src/xrGameLA/ActorCondition.h b/src/xrGameLA/ActorCondition.h new file mode 100644 index 000000000..6c22b5481 --- /dev/null +++ b/src/xrGameLA/ActorCondition.h @@ -0,0 +1,149 @@ +// ActorCondition.h: класс состояния игрока +// +#pragma once + +#include "EntityCondition.h" +#include "actor_defs.h" +#include "hit_immunity.h" +#include "booster.h" + +template +class CScriptCallbackEx; +class CActor; + +struct Eat_Effect //Эффект от съедаемых предметов +{ + float DurationExpiration; // Должно хранить движковое время + неободмый отступ из конфига предмета + float Rate; + float UseTimeExpiration; // Время блокировки рук: Должно хранить движковое время + неободмый отступ из конфига предмета + u8 AffectedStat; + u8 BlockingGroup; + BoosterParams BoosterParam; + + Eat_Effect() + { + DurationExpiration = 0.f; + Rate = 0.f; + UseTimeExpiration = 0.f; + AffectedStat = 0; + BlockingGroup = 0; + } +}; + + +class CActorCondition: public CEntityCondition +{ +private: + typedef CEntityCondition inherited; + enum { eCriticalPowerReached =(1<<0), + eCriticalMaxPowerReached =(1<<1), + eCriticalBleedingSpeed =(1<<2), + eCriticalSatietyReached =(1<<3), + eCriticalThirstyReached =(1<<4), + eCriticalRadiationReached =(1<<5), + eWeaponJammedReached =(1<<6), + ePhyHealthMinReached =(1<<7), + eCantWalkWeight =(1<<8), + }; + Flags16 m_condition_flags; +private: + CActor* m_object; + void UpdateTutorialThresholds (); + void UpdateSatiety (); + void UpdateThirsty (); + + void UpdateEatablesEffects(); + + u16 effects_size; //for save/load process optimization + + void save_effects(NET_Packet &output_packet); + void load_effects(IReader &input_packet); + +public: + CActorCondition (CActor *object); + virtual ~CActorCondition (void); + + virtual void LoadCondition (LPCSTR section); + virtual void reinit (); + + virtual CWound* ConditionHit (SHit* pHDS); + virtual void UpdateCondition (); + + virtual void ChangeAlcohol (const float value); + virtual void ChangeSatiety (const float value); + virtual void ChangeThirsty (const float value); + virtual void ChangeWalkWeight (const float value) { m_MaxWalkWeight = value;} + + // хромание при потере сил и здоровья + virtual bool IsLimping () const; + virtual bool IsCantWalk () const; + virtual bool IsCantWalkWeight (); + virtual bool IsCantSprint () const; + + void ConditionJump (float weight); + void ConditionWalk (float weight, bool accel, bool sprint); + void ConditionStand (float weight); + + float xr_stdcall GetAlcohol () {return m_fAlcohol;} + float xr_stdcall GetPsy () {return 1.0f-GetPsyHealth();} + float GetSatiety () {return m_fSatiety;} + float GetThirsty () { return m_fThirsty; } + float MaxWalkWeight () {return m_MaxWalkWeight;} + + float m_fBoostersAddWeight; + + xr_vector Eat_Effects; //список эффектов от съеденных предметов + float fHandsHideTime; +public: + IC CActor &object () const + { + VERIFY (m_object); + return (*m_object); + } + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + +protected: + float m_fAlcohol; + float m_fV_Alcohol; +//-- + float m_fThirsty; + float m_fV_Thirsty; + float m_fV_ThirstyPower; + float m_fV_ThirstyHealth; +//-- + float m_fSatiety; + float m_fV_Satiety; + float m_fV_SatietyPower; + float m_fV_SatietyHealth; +//-- + float m_fPowerLeakSpeed; + + float m_fJumpPower; + float m_fStandPower; + float m_fWalkPower; + float m_fJumpWeightPower; + float m_fWalkWeightPower; + float m_fOverweightWalkK; + float m_fOverweightJumpK; + float m_fAccelK; + float m_fSprintK; + + float m_MaxWalkWeight; + + mutable bool m_bLimping; + mutable bool m_bCantWalk; + mutable bool m_bCantSprint; + + //порог силы и здоровья меньше которого актер начинает хромать + float m_fLimpingPowerBegin; + float m_fLimpingPowerEnd; + float m_fCantWalkPowerBegin; + float m_fCantWalkPowerEnd; + + float m_fCantSprintPowerBegin; + float m_fCantSprintPowerEnd; + + float m_fLimpingHealthBegin; + float m_fLimpingHealthEnd; +}; diff --git a/src/xrGameLA/ActorEatablesEffects.cpp b/src/xrGameLA/ActorEatablesEffects.cpp new file mode 100644 index 000000000..73f142c3c --- /dev/null +++ b/src/xrGameLA/ActorEatablesEffects.cpp @@ -0,0 +1,221 @@ +#include "stdafx.h" +#include "actorcondition.h" +#include "object_broker.h" + +bool debug_effects = false; +extern BOOL GodMode(); + +void CActorCondition::UpdateEatablesEffects() +{ + for (u16 i = 0; i < Eat_Effects.size(); i++){ + if (debug_effects) Msg("Eat_Effect %i EngineTime() %f DurationExpiration %f, AffectedStat = %u, UseTimeExpiration = %f", i, Device.fTimeGlobal, Eat_Effects[i].DurationExpiration, Eat_Effects[i].AffectedStat, Eat_Effects[i].UseTimeExpiration); + + if (Eat_Effects[i].DurationExpiration >= Device.fTimeGlobal) + { + if (Eat_Effects[i].UseTimeExpiration < Device.fTimeGlobal) + { + if (GodMode()) + { + if (debug_effects) Msg("Eatable Effects are skipped: Developer?!"); + continue; + } + + if (Eat_Effects[i].AffectedStat == 1) + { + if (debug_effects) Msg("Health %f", Eat_Effects[i].Rate * Device.fTimeDelta); + ChangeHealth(Eat_Effects[i].Rate * Device.fTimeDelta); + } + else if (Eat_Effects[i].AffectedStat == 2) + { + if (debug_effects) Msg("Bleed %f", Eat_Effects[i].Rate * Device.fTimeDelta); + ChangeBleeding(Eat_Effects[i].Rate * Device.fTimeDelta); + } + else if (Eat_Effects[i].AffectedStat == 3) + { + if (debug_effects) Msg("Rad %f", Eat_Effects[i].Rate * Device.fTimeDelta); + ChangeRadiation(Eat_Effects[i].Rate * Device.fTimeDelta); + } + else if (Eat_Effects[i].AffectedStat == 4) + { + if (debug_effects) Msg("Psy %f", Eat_Effects[i].Rate * Device.fTimeDelta); + ChangePsyHealth(Eat_Effects[i].Rate * Device.fTimeDelta); + } + else if (Eat_Effects[i].AffectedStat == 5) + { + if (debug_effects) Msg("Food %f", Eat_Effects[i].Rate * Device.fTimeDelta); + ChangeSatiety(Eat_Effects[i].Rate * Device.fTimeDelta); + } + else if (Eat_Effects[i].AffectedStat == 6) + { + if (debug_effects) Msg("Thirst %f", Eat_Effects[i].Rate * Device.fTimeDelta); + ChangeThirsty(Eat_Effects[i].Rate * Device.fTimeDelta); + } + else if (Eat_Effects[i].AffectedStat == 7) + { + if (debug_effects) Msg("Energy %f", Eat_Effects[i].Rate * Device.fTimeDelta); + ChangePower(Eat_Effects[i].Rate * Device.fTimeDelta); + } + else if (Eat_Effects[i].AffectedStat == 8) + { + if (debug_effects) Msg("Boris %f", Eat_Effects[i].Rate * Device.fTimeDelta); + ChangeAlcohol(Eat_Effects[i].Rate * Device.fTimeDelta); + } + } + } + else + { + if (debug_effects) Msg("Effect Expired, remove it"); + if (Eat_Effects[i].BoosterParam.EffectIsBooster) + { + m_fBoostersAddWeight -= Eat_Effects[i].BoosterParam.AddWeight; + } + Eat_Effects.erase(Eat_Effects.begin() + i); + } + } + + //if (debug_effects) Msg("Eat_Effects size2 == %i", Eat_Effects.size()); +} + + +void CActorCondition::save_effects(NET_Packet &output_packet) +{ +#ifdef DEBUG + Msg("# Saving Actor Eatable Items Effects"); +#endif + + save_data(m_fBoostersAddWeight, output_packet); + + effects_size = (u16)Eat_Effects.size(); + save_data(effects_size, output_packet); + + if (effects_size > 0) // save data only if array is not empty + { + xr_vector Saved_Durs; + xr_vector Saved_UseDurs; + xr_vector Saved_Rates; + xr_vector Saved_AffStats; + xr_vector Saved_Groups; + xr_vector Saved_IsBosters; + xr_vector Saved_AddWeights; + xr_vector Saved_ImmunSections; + + for (u32 i = 0; i < Eat_Effects.size(); i++) + { + Saved_Durs.push_back (Eat_Effects[i].DurationExpiration - Device.fTimeGlobal); + Saved_UseDurs.push_back (Eat_Effects[i].UseTimeExpiration - Device.fTimeGlobal); + Saved_Rates.push_back (Eat_Effects[i].Rate); + Saved_AffStats.push_back (Eat_Effects[i].AffectedStat); + Saved_Groups.push_back (Eat_Effects[i].BlockingGroup); + Saved_IsBosters.push_back (Eat_Effects[i].BoosterParam.EffectIsBooster); + Saved_AddWeights.push_back (Eat_Effects[i].BoosterParam.AddWeight); + Saved_ImmunSections.push_back (Eat_Effects[i].BoosterParam.HitImmunitySect); + + if (debug_effects) + { + Msg("Save Eat_Effect: [%i]", i); + Msg("EngineTime() %f, DurationExpiration %f, UseTimeExpiration = %f, Rate = %f, AffectedStat = %u, BlockingGroup = %u, EffectIsBooster = %u, AddWeight = %f", + Device.fTimeGlobal, Saved_Durs[i], Saved_UseDurs[i], Saved_Rates[i], Saved_AffStats[i], Saved_Groups[i], Saved_IsBosters[i], Saved_AddWeights[i]); + Msg("HitImmunitySect = %s", Saved_ImmunSections[i]); + + } + } + + save_data(Saved_Durs, output_packet); + save_data(Saved_UseDurs, output_packet); + save_data(Saved_Rates, output_packet); + save_data(Saved_AffStats, output_packet); + save_data(Saved_Groups, output_packet); + save_data(Saved_IsBosters, output_packet); + save_data(Saved_AddWeights, output_packet); + save_data(Saved_ImmunSections, output_packet); + } + +} + + +void CActorCondition::load_effects(IReader &input_packet) +{ +#ifdef DEBUG + Msg("# Loading Actor Eatable Items Effects"); +#endif + + load_data(m_fBoostersAddWeight, input_packet); + + Eat_Effects.clear(); + load_data(effects_size, input_packet); + if (effects_size > 0) // load data only if array is not empty + { + xr_vector Saved_Durs; + xr_vector Saved_UseDurs; + xr_vector Saved_Rates; + xr_vector Saved_AffStats; + xr_vector Saved_Groups; + xr_vector Saved_IsBosters; + xr_vector Saved_AddWeights; + xr_vector Saved_ImmunSections; + + load_data(Saved_Durs, input_packet); + load_data(Saved_UseDurs, input_packet); + load_data(Saved_Rates, input_packet); + load_data(Saved_AffStats, input_packet); + load_data(Saved_Groups, input_packet); + load_data(Saved_IsBosters, input_packet); + load_data(Saved_AddWeights, input_packet); + load_data(Saved_ImmunSections, input_packet); + + for (u32 i = 0; i < Saved_Durs.size(); i++) + { + if (debug_effects) Msg("Loading Eat_Effect [%u]", i); + + Eat_Effect loaded_effect; + loaded_effect.DurationExpiration = Saved_Durs[i] + Device.fTimeGlobal; + loaded_effect.UseTimeExpiration = Saved_UseDurs[i] + Device.fTimeGlobal; + loaded_effect.Rate = Saved_Rates[i]; + + loaded_effect.AffectedStat = Saved_AffStats[i]; + loaded_effect.BlockingGroup = Saved_Groups[i]; + + BoosterParams loaded_booster; + loaded_booster.EffectIsBooster = Saved_IsBosters[i]; + if (loaded_booster.EffectIsBooster) + { + loaded_booster.AddWeight = Saved_AddWeights[i]; + + LPCSTR immun_section = Saved_ImmunSections[i].c_str(); + + if (!immun_section || immun_section && !pSettings->section_exist(immun_section)) // to protect save file from immunities sections editions/changes + { + Msg("!Can't find immunity section [%s] for booster effect, it was renamed or removed from configs. Trying to use booster_protection_base section", immun_section ? immun_section : "null"); + + if (!pSettings->section_exist("booster_protection_base")) // for complete savefile protection + { + Msg("!Can't find neither [%s], nor booster_protection_base; booster is disabled. Return booster_protection_base or rename backup section in engine", immun_section ? immun_section : "null"); + loaded_booster.EffectIsBooster = false; + } + else + immun_section = "booster_protection_base"; + } + + if (loaded_booster.EffectIsBooster) // check again as it could become false if no section found + { + CHitImmunity immunity; + immunity.LoadImmunities(immun_section, pSettings); + loaded_booster.BoosterHitImmunities = immunity; + + loaded_booster.HitImmunitySect = immun_section; + } + } + + loaded_effect.BoosterParam = loaded_booster; + + Eat_Effects.push_back(loaded_effect); + + if (debug_effects) + { + Msg("Loaded Eat_Effect: [%i], EngineTime() %f, DurationExpiration %f, UseTimeExpiration = %f, Rate = %f, AffectedStat = %u, BlockingGroup = %u, EffectIsBooster = %d, AddWeight = %f", + i, Device.fTimeGlobal, loaded_effect.DurationExpiration, loaded_effect.UseTimeExpiration, loaded_effect.Rate, loaded_effect.AffectedStat, loaded_effect.BlockingGroup, loaded_effect.BoosterParam.EffectIsBooster, loaded_effect.BoosterParam.AddWeight); + Msg("HitImmunitySect = %s", loaded_booster.HitImmunitySect.c_str()); + } + } + } +} diff --git a/src/xrGameLA/ActorEffector.cpp b/src/xrGameLA/ActorEffector.cpp new file mode 100644 index 000000000..245bf65a4 --- /dev/null +++ b/src/xrGameLA/ActorEffector.cpp @@ -0,0 +1,547 @@ +#include "stdafx.h" + +#include "ActorEffector.h" +#include "PostprocessAnimator.h" +#include "../effectorPP.h" +#include "../ObjectAnimator.h" +#include "object_broker.h" +#include "actor.h" +#include "../CameraBase.h" +#include "xr_level_controller.h" + +void AddEffector (CActor* A, int type, const shared_str& sect_name) +{ + if(pSettings->line_exist(sect_name,"pp_eff_name")){ + CPostprocessAnimator* pp_anm = new CPostprocessAnimator(); + bool bCyclic = !!pSettings->r_bool(sect_name,"pp_eff_cyclic"); + pp_anm->SetType ((EEffectorPPType)type); + pp_anm->SetCyclic (bCyclic); + + LPCSTR fn = pSettings->r_string (sect_name,"pp_eff_name"); + pp_anm->Load (fn); + A->Cameras().AddPPEffector (pp_anm); + } + if(pSettings->line_exist(sect_name,"cam_eff_name")){ + bool bCyclic = !!pSettings->r_bool(sect_name,"cam_eff_cyclic"); + CAnimatorCamEffector* cam_anm = new CAnimatorCamEffector(); + cam_anm->SetType ((ECamEffectorType)type); + cam_anm->SetCyclic (bCyclic); + + if(pSettings->line_exist(sect_name, "cam_eff_hud_affect")) + { + bool b_hud_affect = !!pSettings->r_bool(sect_name, "cam_eff_hud_affect"); + cam_anm->SetHudAffect (b_hud_affect); + } + + LPCSTR fn = pSettings->r_string (sect_name,"cam_eff_name"); + cam_anm->Start (fn); + A->Cameras().AddCamEffector (cam_anm); + } +} + +void AddEffector (CActor* A, int type, const shared_str& sect_name, CEffectorController* ec) +{ + if(pSettings->line_exist(sect_name,"pp_eff_name")){ + bool bCyclic = !!pSettings->r_bool(sect_name,"pp_eff_cyclic"); + CPostprocessAnimatorControlled* pp_anm = new CPostprocessAnimatorControlled(ec); + pp_anm->SetType ((EEffectorPPType)type); + pp_anm->SetCyclic (bCyclic); + LPCSTR fn = pSettings->r_string (sect_name,"pp_eff_name"); + pp_anm->Load (fn); + A->Cameras().AddPPEffector (pp_anm); + } + if(pSettings->line_exist(sect_name,"cam_eff_name")){ + bool bCyclic = !!pSettings->r_bool(sect_name,"cam_eff_cyclic"); + CCameraEffectorControlled* cam_anm = new CCameraEffectorControlled(ec); + cam_anm->SetType ((ECamEffectorType)type); + cam_anm->SetCyclic (bCyclic); + + if(pSettings->line_exist(sect_name, "cam_eff_hud_affect")) + { + bool b_hud_affect = !!pSettings->r_bool(sect_name, "cam_eff_hud_affect"); + cam_anm->SetHudAffect (b_hud_affect); + } + + LPCSTR fn = pSettings->r_string (sect_name,"cam_eff_name"); + cam_anm->Start (fn); + A->Cameras().AddCamEffector (cam_anm); + } +} + +void AddEffector (CActor* A, int type, const shared_str& sect_name, GET_KOEFF_FUNC k_func) +{ + if(pSettings->line_exist(sect_name,"pp_eff_name")){ + bool bCyclic = !!pSettings->r_bool(sect_name,"pp_eff_cyclic"); + CPostprocessAnimatorLerp* pp_anm = new CPostprocessAnimatorLerp(); + pp_anm->SetType ((EEffectorPPType)type); + pp_anm->SetCyclic (bCyclic); + LPCSTR fn = pSettings->r_string (sect_name,"pp_eff_name"); + pp_anm->SetFactorFunc (k_func); + pp_anm->Load (fn); + A->Cameras().AddPPEffector (pp_anm); + } + if(pSettings->line_exist(sect_name,"cam_eff_name")){ + bool bCyclic = !!pSettings->r_bool(sect_name,"cam_eff_cyclic"); + CAnimatorCamLerpEffector* cam_anm = new CAnimatorCamLerpEffector(); + cam_anm->SetFactorFunc (k_func); + cam_anm->SetType ((ECamEffectorType)type); + cam_anm->SetCyclic (bCyclic); + + if(pSettings->line_exist(sect_name, "cam_eff_hud_affect")) + { + bool b_hud_affect = !!pSettings->r_bool(sect_name, "cam_eff_hud_affect"); + cam_anm->SetHudAffect (b_hud_affect); + } + + LPCSTR fn = pSettings->r_string (sect_name,"cam_eff_name"); + cam_anm->Start (fn); + A->Cameras().AddCamEffector (cam_anm); + } +}; + +void AddEffector(CActor* A, int type, const shared_str& sect_name, float factor) +{ + clamp(factor, 0.001f, 1.5f); + if(pSettings->line_exist(sect_name,"pp_eff_name")){ + bool bCyclic = !!pSettings->r_bool(sect_name,"pp_eff_cyclic"); + CPostprocessAnimatorLerpConst* pp_anm= new CPostprocessAnimatorLerpConst(); + pp_anm->SetType ((EEffectorPPType)type); + pp_anm->SetCyclic (bCyclic); + pp_anm->SetPower (factor); + LPCSTR fn = pSettings->r_string (sect_name,"pp_eff_name"); + pp_anm->Load (fn); + A->Cameras().AddPPEffector (pp_anm); + } + if(pSettings->line_exist(sect_name,"cam_eff_name")){ + bool bCyclic = !!pSettings->r_bool(sect_name,"cam_eff_cyclic"); + CAnimatorCamLerpEffectorConst* cam_anm = new CAnimatorCamLerpEffectorConst(); + cam_anm->SetFactor (factor); + cam_anm->SetType ((ECamEffectorType)type); + cam_anm->SetCyclic (bCyclic); + + if(pSettings->line_exist(sect_name, "cam_eff_hud_affect")) + { + bool b_hud_affect = !!pSettings->r_bool(sect_name, "cam_eff_hud_affect"); + cam_anm->SetHudAffect (b_hud_affect); + } + + LPCSTR fn = pSettings->r_string (sect_name,"cam_eff_name"); + cam_anm->Start (fn); + A->Cameras().AddCamEffector (cam_anm); + } +}; + + +void RemoveEffector (CActor* A, int type) +{ + A->Cameras().RemoveCamEffector ((ECamEffectorType)type); + A->Cameras().RemovePPEffector ((EEffectorPPType)type); +} + + +CEffectorController::~CEffectorController() +{ + R_ASSERT(!m_ce&&!m_pe); +} + +CAnimatorCamEffector::CAnimatorCamEffector() +{ + m_bCyclic = true; + m_objectAnimator = new CObjectAnimator(); + m_bAbsolutePositioning = false; +} + +CAnimatorCamEffector::~CAnimatorCamEffector() +{ + delete_data (m_objectAnimator); +} + +void CAnimatorCamEffector::Start(LPCSTR fn) +{ + m_objectAnimator->Load (fn); + m_objectAnimator->Play (Cyclic()); + fLifeTime = m_objectAnimator->GetLength(); +} + +BOOL CAnimatorCamEffector::Valid() +{ + if(Cyclic()) return TRUE; + return inherited::Valid(); +} + +BOOL CAnimatorCamEffector::ProcessCam(SCamEffectorInfo& info) +{ + if(!inherited::ProcessCam(info)) + return FALSE; + + const Fmatrix& m = m_objectAnimator->XFORM(); + m_objectAnimator->Update (Device.fTimeDelta); + + if(!m_bAbsolutePositioning){ + Fmatrix Mdef; + Mdef.identity (); + Mdef.j = info.n; + Mdef.k = info.d; + Mdef.i.crossproduct (info.n,info.d); + Mdef.c = info.p; + + Fmatrix mr; + mr.mul (Mdef,m); + info.d = mr.k; + info.n = mr.j; + info.p = mr.c; + }else{ + info.d = m.k; + info.n = m.j; + info.p = m.c; + }; + + return TRUE; +} + +BOOL CAnimatorCamLerpEffector::ProcessCam(SCamEffectorInfo& info) +{ + if(!inherited::inherited::ProcessCam(info)) return FALSE; + + const Fmatrix& m = m_objectAnimator->XFORM(); + m_objectAnimator->Update (Device.fTimeDelta); + + Fmatrix Mdef; + Mdef.identity (); + Mdef.j = info.n; + Mdef.k = info.d; + Mdef.i.crossproduct (info.n,info.d); + Mdef.c = info.p; + + Fmatrix mr; + mr.mul (Mdef,m); + + + Fquaternion q_src, q_dst, q_res; + q_src.set (Mdef); + q_dst.set (mr); + + float t = m_func(); + clamp (t,0.0f,1.0f); + + VERIFY (t>=0.f && t<=1.f); + q_res.slerp (q_src, q_dst, t); + + Fmatrix res; + res.rotation (q_res); + res.c.lerp (info.p, mr.c, t); + + info.d = res.k; + info.n = res.j; + info.p = res.c; + + return TRUE; +} + + +CAnimatorCamLerpEffectorConst::CAnimatorCamLerpEffectorConst() +:m_factor(0.0f) +{ + SetFactorFunc (GET_KOEFF_FUNC(this, &CAnimatorCamLerpEffectorConst::GetFactor)); +} + + +CCameraEffectorControlled::CCameraEffectorControlled(CEffectorController* c) +:m_controller(c) +{ + m_controller->SetCam(this); + SetFactorFunc (GET_KOEFF_FUNC(m_controller, &CEffectorController::GetFactor)); +} + +CCameraEffectorControlled::~CCameraEffectorControlled() +{ + m_controller->SetCam(NULL); +} + +BOOL CCameraEffectorControlled::Valid() +{ + return m_controller->Valid(); +} + +#define SND_MIN_VOLUME_FACTOR (0.1f) + +SndShockEffector::SndShockEffector () +{ + m_snd_length = 0.0f; + m_cur_length = 0.0f; + m_stored_volume = -1.0f; + m_actor = NULL; +} + +SndShockEffector::~SndShockEffector () +{ + psSoundVFactor = m_stored_volume; + if(m_actor&&(m_ce||m_pe)) + RemoveEffector (m_actor, effHit); + + R_ASSERT (!m_ce&&!m_pe); +} + +BOOL SndShockEffector::Valid() +{ + return (m_cur_length<=m_snd_length); +} + +BOOL SndShockEffector::InWork() +{ + return inherited::Valid(); +} + +float SndShockEffector::GetFactor() +{ + float f = (m_end_time-Device.fTimeGlobal)/m_life_time; + + float ff = f*m_life_time/8.0f; + return clampr(ff, 0.0f, 1.0f); +} + +void SndShockEffector::Start(CActor* A, float snd_length, float power) +{ + clamp (power, 0.1f, 1.5f); + m_actor = A; + m_snd_length = snd_length; + + if( m_stored_volume<0.0f ) + m_stored_volume = psSoundVFactor; + + + m_cur_length = 0; + psSoundVFactor = m_stored_volume*SND_MIN_VOLUME_FACTOR; + + static float xxx = 6.0f/1.50f; //6sec on max power(1.5) + + m_life_time = power*xxx; + m_end_time = Device.fTimeGlobal + m_life_time; + + AddEffector (A, effHit,"snd_shock_effector", this); +} + +void SndShockEffector::Update() +{ + m_cur_length += Device.dwTimeDelta; + float x = float(m_cur_length)/m_snd_length; + float y = 2.f*x-1; + if (y>0.f){ + psSoundVFactor = y*(m_stored_volume-m_stored_volume*SND_MIN_VOLUME_FACTOR)+m_stored_volume*SND_MIN_VOLUME_FACTOR; + } +} + + +////////////////////////////////////////////////////////////////////////// + +#define DELTA_ANGLE_X 0.5f * PI / 180 +#define DELTA_ANGLE_Y 0.5f * PI / 180 +#define DELTA_ANGLE_Z 0.5f * PI / 180 +#define ANGLE_SPEED 1.5f + +CControllerPsyHitCamEffector::CControllerPsyHitCamEffector(ECamEffectorType type, const Fvector &src_pos, const Fvector &target_pos, float time) + :inherited(eCEControllerPsyHit, flt_max) +{ + m_time_total = time; + m_time_current = 0; + m_dangle_target.set (angle_normalize(Random.randFs(DELTA_ANGLE_X)),angle_normalize(Random.randFs(DELTA_ANGLE_Y)),angle_normalize(Random.randFs(DELTA_ANGLE_Z))); + m_dangle_current.set (0.f, 0.f, 0.f); + m_position_source = src_pos; + m_direction.sub (target_pos,src_pos); + m_distance = m_direction.magnitude(); + m_direction.normalize (); +} + +const float _base_fov = 170.f; +const float _max_fov_add = 160.f; + + +BOOL CControllerPsyHitCamEffector::ProcessCam(SCamEffectorInfo& info) +{ + Fmatrix Mdef; + Mdef.identity (); + Mdef.j.set (info.n); + Mdef.k.set (m_direction); + Mdef.i.crossproduct (info.n,m_direction); + Mdef.c.set (info.p); + + ////////////////////////////////////////////////////////////////////////// + + if (angle_lerp(m_dangle_current.x, m_dangle_target.x, ANGLE_SPEED, Device.fTimeDelta)) { + m_dangle_target.x = angle_normalize(Random.randFs(DELTA_ANGLE_X)); + } + + if (angle_lerp(m_dangle_current.y, m_dangle_target.y, ANGLE_SPEED, Device.fTimeDelta)) { + m_dangle_target.y = angle_normalize(Random.randFs(DELTA_ANGLE_Y)); + } + + if (angle_lerp(m_dangle_current.z, m_dangle_target.z, ANGLE_SPEED, Device.fTimeDelta)) { + m_dangle_target.z = angle_normalize(Random.randFs(DELTA_ANGLE_Z)); + } + + ////////////////////////////////////////////////////////////////////////// + + if (m_time_current > m_time_total) m_time_current = m_time_total; + + float perc_past = m_time_current / m_time_total; + float cur_dist = m_distance * perc_past; + + Mdef.c.mad (m_position_source, m_direction, cur_dist); + info.fFov = _base_fov - _max_fov_add*perc_past; + + m_time_current += Device.fTimeDelta; + + ////////////////////////////////////////////////////////////////////////// + + // Установить углы смещения + Fmatrix R; + if (m_time_current > m_time_total) + R.identity (); + else + R.setHPB (m_dangle_current.x,m_dangle_current.y,m_dangle_current.z); + + Fmatrix mR; + mR.mul (Mdef,R); + + info.d.set (mR.k); + info.n.set (mR.j); + info.p.set (mR.c); + + return TRUE; +} + +bool similar_cam_info(const SCamEffectorInfo& c1, const SCamEffectorInfo& c2) +{ + return( c1.p.similar(c2.p, EPS_L) && + c1.d.similar(c2.d, EPS_L) && + c1.n.similar(c2.n, EPS_L) && + c1.r.similar(c2.r, EPS_L) ); + +} +void CActorCameraManager::UpdateCamEffectors() +{ + m_cam_info_hud = m_cam_info; + + inherited::UpdateCamEffectors(); + + m_cam_info_hud.d.normalize (); + m_cam_info_hud.n.normalize (); + m_cam_info_hud.r.crossproduct (m_cam_info_hud.n, m_cam_info_hud.d); + m_cam_info_hud.n.crossproduct (m_cam_info_hud.d, m_cam_info_hud.r); +} + +void cam_effector_sub(const SCamEffectorInfo& c1, const SCamEffectorInfo& c2, SCamEffectorInfo& dest) +{ + dest.p.sub (c1.p, c2.p); + dest.d.sub (c1.d, c2.d); + dest.n.sub (c1.n, c2.n); + dest.r.sub (c1.r, c2.r); +} + +void cam_effector_add(const SCamEffectorInfo& diff, SCamEffectorInfo& dest) +{ + dest.p.add (diff.p); + dest.d.add (diff.d); + dest.n.add (diff.n); + dest.r.add (diff.r); +} + +bool CActorCameraManager::ProcessCameraEffector(CEffectorCam* eff) +{ + SCamEffectorInfo prev = m_cam_info; + + bool res = inherited::ProcessCameraEffector (eff); + if(res) + { + if(eff->GetHudAffect()) + { + SCamEffectorInfo affected = m_cam_info; + SCamEffectorInfo diff; + + cam_effector_sub (affected, prev, diff); + + cam_effector_add (diff, m_cam_info_hud); // m_cam_info_hud += difference + } + + m_cam_info_hud.fFov = m_cam_info.fFov; + m_cam_info_hud.fFar = m_cam_info.fFar; + m_cam_info_hud.fAspect = m_cam_info.fAspect; + } + return res; +} + +////////////////////////////////////////////////////////////////////////// +// Slowly set direction of the camera +////////////////////////////////////////////////////////////////////////// + +#define EPS_ANGLE 1 * PI / 180 + +CScriptCameraDirection::CScriptCameraDirection() +{ + m_target_point.set(0.f,0.f,0.f); + m_speed = 0.0f; + m_need_turn = false; + m_actor = NULL; + m_turned_yaw = false; + m_turned_pitch = false; +} + +CScriptCameraDirection::~CScriptCameraDirection() +{ +} + +void CScriptCameraDirection::Start(CActor* A, const Fvector &tgt, float time) +{ + m_target_point = tgt; + m_speed = time; + m_need_turn = true; + m_actor = A; + m_turned_yaw = false; + m_turned_pitch = false; +} + +void CScriptCameraDirection::Update() +{ + // get yaw and pitch to target + float cam_target_yaw, cam_target_pitch; + + Fvector P,D,N; + m_actor->cam_Active()->Get (P,D,N); + Fvector().sub(m_target_point, P).getHP (cam_target_yaw, cam_target_pitch); + + // get yaw and pitch of current cam direction + float cam_current_yaw, cam_current_pitch; + D.getHP (cam_current_yaw, cam_current_pitch); + + // YAW + if (fsimilar(cam_current_yaw, cam_target_yaw, EPS_ANGLE)) { + m_turned_yaw = true; + } else { + if (angle_normalize_signed(cam_target_yaw - cam_current_yaw) > 0) + m_actor->cam_Active()->Move (kLEFT, m_speed * Device.fTimeDelta); + else + m_actor->cam_Active()->Move (kRIGHT, m_speed * Device.fTimeDelta); + } + + // PITCH + if (fsimilar(cam_current_pitch, cam_target_pitch, EPS_ANGLE)) { + m_turned_pitch = true; + } else { + if (angle_normalize_signed(cam_target_pitch - cam_current_pitch) > 0) + m_actor->cam_Active()->Move (kDOWN, m_speed * Device.fTimeDelta); + else + m_actor->cam_Active()->Move (kUP, m_speed * Device.fTimeDelta); + } + + if (m_turned_yaw&&m_turned_pitch) m_need_turn = false; +} + +bool CScriptCameraDirection::InWork() +{ + return (m_need_turn); +} + +float CScriptCameraDirection::GetFactor() +{ + return 0.0f; +} \ No newline at end of file diff --git a/src/xrGameLA/ActorEffector.h b/src/xrGameLA/ActorEffector.h new file mode 100644 index 000000000..ef2d97d40 --- /dev/null +++ b/src/xrGameLA/ActorEffector.h @@ -0,0 +1,174 @@ +#pragma once + +#include "CameraEffector.h" + +class CObjectAnimator; +class CEffectorController; +class CActor; + +class CActorCameraManager :public CCameraManager +{ + typedef CCameraManager inherited; + + SCamEffectorInfo m_cam_info_hud; + +protected: + virtual void UpdateCamEffectors (); + virtual bool ProcessCameraEffector (CEffectorCam* eff); + +public: + CActorCameraManager():inherited(false){} + virtual ~CActorCameraManager() {} + + IC void hud_camera_Matrix (Fmatrix& M){M.set(m_cam_info_hud.r, m_cam_info_hud.n, m_cam_info_hud.d, m_cam_info_hud.p);} +}; + +typedef fastdelegate::FastDelegate0 GET_KOEFF_FUNC; + +void AddEffector (CActor* A, int type, const shared_str& sect_name); +void AddEffector (CActor* A, int type, const shared_str& sect_name, float factor); +void AddEffector (CActor* A, int type, const shared_str& sect_name, GET_KOEFF_FUNC); +void AddEffector (CActor* A, int type, const shared_str& sect_name, CEffectorController*); +void RemoveEffector (CActor* A, int type); + +class CEffectorController +{ +protected: + CEffectorCam* m_ce; + CEffectorPP* m_pe; +public: + CEffectorController():m_ce(NULL),m_pe(NULL) {} + virtual ~CEffectorController(); + + void SetPP (CEffectorPP* p) {m_pe=p;} + void SetCam (CEffectorCam* p) {m_ce=p;} + virtual BOOL Valid () {return m_ce||m_pe;}; + virtual float xr_stdcall GetFactor () =0; +}; + +class CAnimatorCamEffector :public CEffectorCam +{ + typedef CEffectorCam inherited; + bool m_bCyclic; +protected: + virtual bool Cyclic () const {return m_bCyclic;} + CObjectAnimator* m_objectAnimator; +public: + bool m_bAbsolutePositioning; + + CAnimatorCamEffector (); + virtual ~CAnimatorCamEffector (); + void Start (LPCSTR fn); + virtual BOOL ProcessCam (SCamEffectorInfo& info); + void SetCyclic (bool b) {m_bCyclic=b;} + virtual BOOL Valid (); + float GetAnimatorLength () {return fLifeTime;}; + + virtual bool AbsolutePositioning () {return m_bAbsolutePositioning;} +}; + +class CAnimatorCamEffectorScriptCB :public CAnimatorCamEffector +{ + typedef CAnimatorCamEffector inherited; + + shared_str cb_name; +public: + CAnimatorCamEffectorScriptCB (LPCSTR _cb){cb_name =_cb;}; + virtual BOOL Valid (); + virtual BOOL AllowProcessingIfInvalid() {return m_bAbsolutePositioning;} + virtual void ProcessIfInvalid (SCamEffectorInfo& info); +}; + +class CAnimatorCamLerpEffector :public CAnimatorCamEffector +{ +protected: + typedef CAnimatorCamEffector inherited; + GET_KOEFF_FUNC m_func; +public: + void SetFactorFunc (GET_KOEFF_FUNC f) {m_func=f;} + virtual BOOL ProcessCam (SCamEffectorInfo& info); +}; + +class CAnimatorCamLerpEffectorConst :public CAnimatorCamLerpEffector +{ +protected: + float m_factor; +public: + CAnimatorCamLerpEffectorConst (); + void SetFactor (float v) {m_factor=v; clamp(m_factor,0.0f,1.0f);} + float xr_stdcall GetFactor () {return m_factor;} +}; + +class CCameraEffectorControlled :public CAnimatorCamLerpEffector +{ + CEffectorController* m_controller; +public: + CCameraEffectorControlled (CEffectorController* c); + virtual ~CCameraEffectorControlled (); + virtual BOOL Valid (); +}; + +class SndShockEffector:public CEffectorController +{ + typedef CEffectorController inherited; +public: + float m_snd_length; //ms + float m_cur_length; //ms + float m_stored_volume; + float m_end_time; + float m_life_time; + CActor* m_actor; +public: + SndShockEffector (); + virtual ~SndShockEffector (); + void Start (CActor* A, float snd_length, float power); + void Update (); + + virtual BOOL Valid (); + BOOL InWork (); + virtual float xr_stdcall GetFactor (); +}; + + +////////////////////////////////////////////////////////////////////////// +class CControllerPsyHitCamEffector :public CEffectorCam { + typedef CEffectorCam inherited; + + float m_time_total; + float m_time_current; + Fvector m_dangle_target; + Fvector m_dangle_current; + Fvector m_position_source; + Fvector m_direction; + float m_distance; + +public: + CControllerPsyHitCamEffector (ECamEffectorType type, const Fvector &src_pos, const Fvector &target_pos, float time); + virtual BOOL ProcessCam (SCamEffectorInfo& info); +}; +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +// Script Camera Direction +////////////////////////////////////////////////////////////////////////// +class CScriptCameraDirection:public CEffectorController +{ + typedef CEffectorController inherited; +public: + Fvector m_target_point; + + bool m_turned_yaw; + bool m_turned_pitch; + float m_speed; + CActor* m_actor; + bool m_need_turn; + +public: + CScriptCameraDirection(); + virtual ~CScriptCameraDirection(); + void Start(CActor* A, const Fvector &tgt, float time); + void Update(); + bool InWork(); + virtual float xr_stdcall GetFactor(); +}; + diff --git a/src/xrGameLA/ActorEffector_script.cpp b/src/xrGameLA/ActorEffector_script.cpp new file mode 100644 index 000000000..65be25ea1 --- /dev/null +++ b/src/xrGameLA/ActorEffector_script.cpp @@ -0,0 +1,29 @@ +#include "pch_script.h" +#include "ai_space.h" +#include "script_engine.h" +#include "ActorEffector.h" +#include "../ObjectAnimator.h" + +void CAnimatorCamEffectorScriptCB::ProcessIfInvalid(SCamEffectorInfo& info) +{ + if(m_bAbsolutePositioning) + { + const Fmatrix& m = m_objectAnimator->XFORM(); + info.d = m.k; + info.n = m.j; + info.p = m.c; + } +} + +BOOL CAnimatorCamEffectorScriptCB::Valid() +{ + BOOL res = inherited::Valid(); + if(!res && cb_name.size() ) + { + luabind::functor fl; + R_ASSERT (ai().script_engine().functor(*cb_name,fl)); + fl (); + cb_name = ""; + } + return res; +} diff --git a/src/xrGameLA/ActorFollowers.cpp b/src/xrGameLA/ActorFollowers.cpp new file mode 100644 index 000000000..4fe20bf53 --- /dev/null +++ b/src/xrGameLA/ActorFollowers.cpp @@ -0,0 +1,92 @@ +#include "stdafx.h" +/* +#include "ActorFollowers.h" +#include "ui/xrxmlparser.h" +#include "ui/UIFollowerPanel.h" +#include "Level.h" +#include "HUDManager.h" +#include "UIGameCustom.h" +#include "actor.h" + +CActorFollowerMngr::CActorFollowerMngr() +{ + CUIXml uiXml; + uiXml.Init(CONFIG_PATH, UI_PATH, "follower_panel.xml"); + + m_uiPanel = xr_new (); + m_uiPanel->Init (&uiXml,"followers_panel",0); + CurrentGameUI()->AddDialogToRender(m_uiPanel); + m_uiPanel->Show (false); +} + +CActorFollowerMngr::~CActorFollowerMngr() +{ + CurrentGameUI()->RemoveDialogToRender(m_uiPanel); + xr_delete(m_uiPanel); +} + +void CActorFollowerMngr::AddFollower(u16 id) +{ +#ifdef DEBUG + FOLLOWER_IT it = std::find(m_followers.begin(),m_followers.end(),id); + if(it!=m_followers.end()){ + Msg("Attempt to add follower [%d] twice !!!",id); + return; + } +#endif + + m_followers.push_back(id); + m_uiPanel->AddFollower(id); + +} + +void CActorFollowerMngr::RemoveFollower(u16 id) +{ + FOLLOWER_IT it = std::find(m_followers.begin(),m_followers.end(),id); + if(it==m_followers.end()){ + Msg("Attempt to remove not registered follower [%d] !!!",id); + return; + } + std::remove(m_followers.begin(),m_followers.end(),id); + m_uiPanel->RemoveFollower(id); +} + +void CActorFollowerMngr::SendCommand(int cmd) +{ + FOLLOWER_IT it = m_followers.begin(); + FOLLOWER_IT it_e = m_followers.end(); + CInventoryOwner* IO = NULL; + for(;it!=it_e;++it){ + IO = smart_cast(Level().Objects.net_Find(*it)); + IO->OnFollowerCmd (cmd); + } + +} + +CActorFollowerMngr& CActor::Followers() +{ + if(!m_followers) + m_followers = xr_new(); + + return *m_followers; +} + +void CActor::AddFollower(u16 id) +{ + Followers().AddFollower (id); +} + +void CActor::RemoveFollower(u16 id) +{ + Followers().RemoveFollower (id); +} + +void CActor::DestroyFollowerInternal() +{ + xr_delete(m_followers); +} + +void CActor::SendCmdToFollowers(int cmd) +{ + Followers().SendCommand(cmd); +}*/ \ No newline at end of file diff --git a/src/xrGameLA/ActorFollowers.h b/src/xrGameLA/ActorFollowers.h new file mode 100644 index 000000000..173a1c8b8 --- /dev/null +++ b/src/xrGameLA/ActorFollowers.h @@ -0,0 +1,22 @@ +#pragma once +/* +class CUIFollowerPanel; +class CInventoryOwner; + +class CActorFollowerMngr +{ + typedef u16 FOLLOWER_T; + typedef xr_vector FOLLOWER_V; + typedef FOLLOWER_V::iterator FOLLOWER_IT; + + FOLLOWER_V m_followers; + CUIFollowerPanel* m_uiPanel; +public: + CActorFollowerMngr (); + ~CActorFollowerMngr (); + void AddFollower (u16 id); + void RemoveFollower (u16 id); + void SendCommand (int cmd); +}; + +*/ diff --git a/src/xrGameLA/ActorHudWetness.cpp b/src/xrGameLA/ActorHudWetness.cpp new file mode 100644 index 000000000..e36533978 --- /dev/null +++ b/src/xrGameLA/ActorHudWetness.cpp @@ -0,0 +1,123 @@ +#include "stdafx.h" +#include "Actor.h" +#include "../CameraBase.h" +#include "Inventory.h" +#include "CustomOutfit.h" +#include "gamepersistent.h" +#include "level.h" +#include "UIGameCustom.h" + +#define REGULAR_RAIN_DENS 1.7f; +#define REGULAR_HEMI_AMOUNT 0.05f; +#define REGULAR_LOOK_DIR_Y 1.1f; + +u8 mainLayer_ = 1; + +void CActor::ComputeHudWetness() +{ + COutfitBase* outfit_with_visor = nullptr; + + // outfit with helmet and regular helmets are exlcluding each other, so no need to do extra checks for priority + if (inventory().ItemFromSlot(OUTFIT_SLOT)) + { + COutfitBase* outf = smart_cast(inventory().ItemFromSlot(OUTFIT_SLOT)); + + if (outf && outf->hasVisorEffects_) + outfit_with_visor = outf; + } + + if (inventory().ItemFromSlot(HELMET_SLOT)) + { + COutfitBase* helm = smart_cast(inventory().ItemFromSlot(HELMET_SLOT)); + + if (helm && helm->hasVisorEffects_) + outfit_with_visor = helm; + } + + if (outfit_with_visor) + { + if (CurrentGameUI()->GameIndicatorsShown()) // if hud is not blocked by some event + g_pGamePersistent->Environment().SetCastHudGlassEffects(true); // start casting visor and sun flares effects anyway + else + g_pGamePersistent->Environment().SetCastHudGlassEffects(false); + + // Calc if actor hud has rain drops on it + + CCameraBase* C = cameras[eacFirstEye]; + + float actor_hemi_factor = ROS()->get_luminocity_hemi(); + + // check if actro is under open skyes and there is rain + bool actor_under_rain = g_pGamePersistent->Environment().CurrentEnv->rain_density > 0.01 && actor_hemi_factor > 0.005f; + + if (actor_under_rain) // Increase + { + float rain_denc_amount_f = g_pGamePersistent->Environment().CurrentEnv->rain_density / REGULAR_RAIN_DENS; + float hemi_amount_f = actor_hemi_factor / REGULAR_HEMI_AMOUNT; + float actor_looks_up_f = (1.f + C->Direction().y) / REGULAR_LOOK_DIR_Y; + + actor_looks_up_f = actor_looks_up_f * actor_looks_up_f * actor_looks_up_f; + + clamp(actor_looks_up_f, 0.01f, 5.f); + + float add = wetnessAccmBase_ * hemi_amount_f * rain_denc_amount_f * actor_looks_up_f * Device.fTimeDelta; + + if (mainLayer_ == 1) + { + // increase layer 1 + if (outfit_with_visor->visorWetness_1_ <= maxHudWetness_) + { + outfit_with_visor->visorWetness_1_ += add; + g_pGamePersistent->Environment().SetActorHudWetness1(outfit_with_visor->visorWetness_1_); + } + + // decrease layer 2 + if (outfit_with_visor->visorWetness_2_ > 0.7f * maxHudWetness_) + { + outfit_with_visor->visorWetness_2_ -= add; + g_pGamePersistent->Environment().SetActorHudWetness2(outfit_with_visor->visorWetness_2_); + } + + // perform swithcing of layer for icreasing if full + if (outfit_with_visor->visorWetness_1_ >= maxHudWetness_) // if the first rain drops layer is filled - start filling second and cleaning first + mainLayer_ = 2; + + } + else if (mainLayer_ == 2) + { + // increase layer 2 + if (outfit_with_visor->visorWetness_2_ <= maxHudWetness_) + { + outfit_with_visor->visorWetness_2_ += add; + g_pGamePersistent->Environment().SetActorHudWetness2(outfit_with_visor->visorWetness_2_); + } + + // decrease layer 1 + if (outfit_with_visor->visorWetness_1_ > 0.7f * maxHudWetness_) + { + outfit_with_visor->visorWetness_1_ -= add; + g_pGamePersistent->Environment().SetActorHudWetness1(outfit_with_visor->visorWetness_1_); + } + + // perform swithcing of layer for icreasing if full + if (outfit_with_visor->visorWetness_2_ >= maxHudWetness_) // if the first rain drops layer is filled - start filling second and cleaning first + mainLayer_ = 1; + + } + } + else if (outfit_with_visor->visorWetness_1_ > 0 || outfit_with_visor->visorWetness_2_ > 0) // Decrease, if not under rain + { + if (outfit_with_visor->visorWetness_1_ > 0) + outfit_with_visor->visorWetness_1_ -= wetnessDecreaseF_ * Device.fTimeDelta; + else if (outfit_with_visor->visorWetness_2_ > 0) + outfit_with_visor->visorWetness_2_ -= wetnessDecreaseF_ * Device.fTimeDelta; + + g_pGamePersistent->Environment().SetActorHudWetness1(outfit_with_visor->visorWetness_1_); + g_pGamePersistent->Environment().SetActorHudWetness2(outfit_with_visor->visorWetness_2_); + } + } + else + { + g_pGamePersistent->Environment().SetCastHudGlassEffects(false); + } +} diff --git a/src/xrGameLA/ActorInput.cpp b/src/xrGameLA/ActorInput.cpp new file mode 100644 index 000000000..4348fbe44 --- /dev/null +++ b/src/xrGameLA/ActorInput.cpp @@ -0,0 +1,693 @@ +#include "stdafx.h" +#include +#include "pch_script.h" +#include "Actor.h" +#include "Torch.h" +#include "customoutfit.h" +#include "trade.h" +#include "../CameraBase.h" +#ifdef DEBUG +#include "PHDebug.h" +#endif +#include "hit.h" +#include "PHDestroyable.h" +#include "Car.h" +#include "HudManager.h" +#include "UIGameSP.h" +#include "inventory.h" +#include "level.h" +#include "game_cl_base.h" +#include "xr_level_controller.h" +#include "UsableScriptObject.h" +#include "clsid_game.h" +#include "actorcondition.h" +#include "actor_input_handler.h" +#include "string_table.h" +#include "UI/UIStatic.h" +#include "CharacterPhysicsSupport.h" +#include "InventoryBox.h" +#include "WeaponMagazined.h" +#include "game_object_space.h" +#include "script_callback_ex.h" +#include "script_game_object.h" +#include "../xr_input.h" +#include "player_hud.h" +#include "ui/UIInventoryWnd.h" //for detector needs + +bool g_bAutoClearCrouch = true; +u32 g_bAutoApplySprint = 0; +extern int hud_adj_mode; + +void CActor::IR_OnKeyboardPress(int cmd) +{ + if (CAttachableItem::m_dbgItem || hud_adj_mode && pInput->iGetAsyncKeyState(DIK_LSHIFT)) return; + + if (Remote()) return; + +// if (conditions().IsSleeping()) return; + + callback(GameObject::eOnButtonPress)(lua_game_object(), cmd); + + if (IsTalking()) return; + if (m_input_external_handler && !m_input_external_handler->authorized(cmd)) return; + + switch (cmd) + { + case kWPN_FIRE: + { + BreakSprint(); + }break; + default: + { + }break; + } + + if (!g_Alive()) return; + + if(m_holder && kUSE != cmd) + { + m_holder->OnKeyboardPress (cmd); + if(m_holder->allowWeapon() && inventory().Action(cmd, CMD_START)) return; + return; + } else { + if (cmd==kWPN_ZOOM&&!psHoldZoom.test(1)) + { + if(inventory().ActiveItem()) + { + CWeaponMagazined* pWM = smart_cast(inventory().ActiveItem()); + if (pWM) + { + if (IsZoomAimingMode()) + { + if(inventory().Action(cmd, CMD_STOP)) return; + }else{ + if(inventory().Action(cmd, CMD_START)) return; + } + }else{ + if(inventory().Action(cmd, CMD_START)) return; + } + }else{ + if(inventory().Action(cmd, CMD_START)) return; + } + } else { + if(inventory().Action(cmd, CMD_START)) return; + } + } + + switch(cmd){ + case kJUMP: + { + mstate_wishful |= mcJump; + { +// NET_Packet P; +// u_EventGen(P, GE_ACTOR_JUMPING, ID()); +// u_EventSend(P); + } + }break; + case kCROUCH_TOGGLE: + { + g_bAutoClearCrouch = !g_bAutoClearCrouch; + if (!g_bAutoClearCrouch) + mstate_wishful |= mcCrouch; + + }break; + case kSPRINT_TOGGLE: + { + CWeapon* W = smart_cast(inventory().ActiveItem()); + + if (IsReloadingWeapon()) + { + if (trySprintCounter_ == 0) // don't interrupt reloading on first key press and skip sprint request + { + trySprintCounter_++; + + return; + } + else if (trySprintCounter_ >= 1) // break reloading, if player insist(presses two or more times) and do sprint + { + W->StopAllSounds(); + W->SwitchState(CHUDState::EHudStates::eIdle); + } + } + + trySprintCounter_ = 0; + + if (mstate_wishful & mcSprint) + { + mstate_wishful &= ~mcSprint; + } + else + { + g_bAutoClearCrouch = true; + g_bAutoApplySprint = 1; + mstate_wishful |= mcSprint; + } + }break; + case kCAM_1:{ cam_Set(eacFirstEye); CCustomDetectorR * detectortoshow = inventory().CurrentDetector(); if (detectortoshow)g_player_hud->attach_item(detectortoshow); }break; //Для востановления детектора в руке + case kCAM_2: cam_Set(eacLookAt); break; + case kCAM_3: cam_Set(eacFreeLook); break; + case kNIGHT_VISION: SwitchNightVision(); break; + /*{ + CCustomOutfit* outfit = GetOutfit(); + if (outfit) + outfit->SwitchNightVision(); + } break;*/ + + case kTORCH:{ + if (!m_current_torch) + { + if (inventory().ItemFromSlot(TORCH_SLOT)) + { + CTorch *torch = smart_cast(inventory().ItemFromSlot(TORCH_SLOT)); + if (torch) + { + m_current_torch = torch; + m_current_torch->Switch(); + } + } + } else { + if (inventory().ItemFromSlot(TORCH_SLOT)) + { + CTorch *torch = smart_cast(inventory().ItemFromSlot(TORCH_SLOT)); + if (torch) + { + m_current_torch = torch; + m_current_torch->Switch(); + } else + m_current_torch = 0; + + } else + m_current_torch = 0; + } + } break; + + case kWPN_1: + case kWPN_2: + case kWPN_3: + case kWPN_3b: + case kWPN_4: + case kWPN_5: + case kWPN_6: + case kWPN_RELOAD: + break; + case kUSE: + ActorUse(); + break; + case kDROP: + b_DropActivated = TRUE; + f_DropPower = 0; + break; + case kNEXT_SLOT: + { + OnNextWeaponSlot(); + }break; + case kPREV_SLOT: + { + OnPrevWeaponSlot(); + }break; + + case kUSE_BANDAGE: + case kUSE_MEDKIT: + { + if (IsGameTypeSingle()) + { + PIItem itm = inventory().item((cmd == kUSE_BANDAGE) ? CLSID_IITEM_BANDAGE : CLSID_IITEM_MEDKIT); + if (itm) + { + bool used = inventory().Eat(itm); + if (used) + { + SDrawStaticStruct* HudMessage = CurrentGameUI()->AddCustomStatic("inv_hud_message", true); + HudMessage->m_endTime = Device.fTimeGlobal + 3.0f;// 3sec + string1024 str; + xr_sprintf(str, "%s : %s", *CStringTable().translate("st_item_used"), itm->Name()); + HudMessage->wnd()->TextItemControl()->SetText(str); + } + } + } + }break; + } +} + +void CActor::SwitchNightVision() +{ + CWeapon *wpn1 = nullptr, *wpn2 = nullptr, *wpn3 = nullptr; + if (inventory().ItemFromSlot(PISTOL_SLOT)) + wpn1 = smart_cast(inventory().ItemFromSlot(PISTOL_SLOT)); + + if (inventory().ItemFromSlot(RIFLE_SLOT)) + wpn2 = smart_cast(inventory().ItemFromSlot(RIFLE_SLOT)); + + if (inventory().ItemFromSlot(RIFLE_2_SLOT)) + wpn3 = smart_cast(inventory().ItemFromSlot(RIFLE_2_SLOT)); + + xr_vector const& all = CAttachmentOwner::attached_objects(); + xr_vector::const_iterator it = all.begin(); + xr_vector::const_iterator it_e = all.end(); + for (; it != it_e; ++it) + { + CTorch* torch = smart_cast(*it); + if (torch) + { + if (wpn1 && wpn1->IsZoomed()) + return; + + if (wpn2 && wpn2->IsZoomed()) + return; + + if (wpn3 && wpn3->IsZoomed()) + return; + + torch->SwitchNightVision(); + return; + } + } +} + +void CActor::IR_OnMouseWheel(int direction) +{ + if(hud_adj_mode) + { + g_player_hud->tune (Ivector().set(0,0,direction)); + return; + } + + if (CAttachableItem::m_dbgItem) + { + return; + } + + if(inventory().Action( (direction>0)? kWPN_ZOOM_DEC:kWPN_ZOOM_INC , CMD_START)) return; + + + if (direction>0) + { + if (eacLookAt==cam_active) + for (int i=0; i<10; ++i) + cam_Active()->Move(kCAM_ZOOM_IN); + else + OnNextWeaponSlot(); + } else { + if (eacLookAt==cam_active) + for (int i=0; i<10; ++i) + cam_Active()->Move(kCAM_ZOOM_OUT); + else + OnPrevWeaponSlot(); + } +} +void CActor::IR_OnKeyboardRelease(int cmd) +{ + if (CAttachableItem::m_dbgItem || hud_adj_mode && pInput->iGetAsyncKeyState(DIK_LSHIFT)) return; + + if (Remote()) return; + +// if (conditions().IsSleeping()) return; + + callback(GameObject::eOnButtonRelease)(lua_game_object(), cmd); + + if (m_input_external_handler && !m_input_external_handler->authorized(cmd)) return; + + if (g_Alive()) + { + if (cmd == kUSE) + PickupModeOff(); + + if(m_holder) + { + m_holder->OnKeyboardRelease(cmd); + + if(m_holder->allowWeapon() && inventory().Action(cmd, CMD_STOP)) return; + return; + }else{ + if (cmd==kWPN_ZOOM&&!psHoldZoom.test(1)) + { + if(inventory().ActiveItem()) + { + CWeaponMagazined* pWM = smart_cast(inventory().ActiveItem()); + if (!pWM || (pWM&&!pWM->IsZoomEnabled())) + { + if(inventory().Action(cmd, CMD_STOP)) return; + } + } else { + if(inventory().Action(cmd, CMD_STOP)) return; + } + } else { + if(inventory().Action(cmd, CMD_STOP)) return; + } + } + + + + switch(cmd) + { + case kJUMP: mstate_wishful &=~mcJump; break; + case kDROP: if(GAME_PHASE_INPROGRESS == Game().Phase()) g_PerformDrop(); break; + case kCROUCH: g_bAutoClearCrouch = true; + } + } +} + +void CActor::IR_OnKeyboardHold(int cmd) +{ + if (CAttachableItem::m_dbgItem || hud_adj_mode && pInput->iGetAsyncKeyState(DIK_LSHIFT)) return; + + if (Remote() || !g_Alive()) return; +// if (conditions().IsSleeping()) return; + + callback(GameObject::eOnButtonHold)(lua_game_object(), cmd); + + if (m_input_external_handler && !m_input_external_handler->authorized(cmd)) return; + if (IsTalking()) return; + + if(m_holder) + { + m_holder->OnKeyboardHold(cmd); + return; + } + + float LookFactor = GetLookFactor(); + switch(cmd) + { + case kUP: + case kDOWN: + cam_Active()->Move( (cmd==kUP) ? kDOWN : kUP, 0, LookFactor); break; + case kCAM_ZOOM_IN: + case kCAM_ZOOM_OUT: + cam_Active()->Move(cmd); break; + case kLEFT: + case kRIGHT: + if (eacFreeLook!=cam_active) cam_Active()->Move(cmd, 0, LookFactor); break; + + case kACCEL: mstate_wishful |= mcAccel; break; + case kL_STRAFE: mstate_wishful |= mcLStrafe; break; + case kR_STRAFE: mstate_wishful |= mcRStrafe; break; + case kL_LOOKOUT:mstate_wishful |= mcLLookout; break; + case kR_LOOKOUT:mstate_wishful |= mcRLookout; break; + case kFWD: mstate_wishful |= mcFwd; break; + case kBACK: mstate_wishful |= mcBack; break; + case kCROUCH: mstate_wishful |= mcCrouch; break; + + + } +} + +void CActor::IR_OnMouseMove(int dx, int dy) +{ + if(hud_adj_mode) + { + g_player_hud->tune (Ivector().set(dx,dy,0)); + return; + } + + if (CAttachableItem::m_dbgItem) + { + return; + } + + PIItem iitem = inventory().ActiveItem(); + if(iitem && iitem->cast_hud_item()) + iitem->cast_hud_item()->ResetSubStateTime(); + + if (Remote()) return; +// if (conditions().IsSleeping()) return; + + if(m_holder) + { + m_holder->OnMouseMove(dx,dy); + return; + } + + float LookFactor = GetLookFactor(); + + CCameraBase* C = cameras [cam_active]; + float scale = (C->f_fov/g_fov)*psMouseSens * psMouseSensScale/50.f / LookFactor; + if (dx){ + float d = float(dx)*scale; + cam_Active()->Move((d<0)?kLEFT:kRIGHT, _abs(d)); + } + if (dy){ + float d = ((psMouseInvert.test(1))?-1:1)*float(dy)*scale*3.f/4.f; + cam_Active()->Move((d>0)?kUP:kDOWN, _abs(d)); + } +} +#include "HudItem.h" +bool CActor::use_Holder (CHolderCustom* holder) +{ + + if(m_holder){ + bool b = false; + CGameObject* holderGO = smart_cast(m_holder); + + if(smart_cast(holderGO)) + b = use_Vehicle(0); + else + if (holderGO->CLS_ID==CLSID_OBJECT_W_MOUNTED || + holderGO->CLS_ID==CLSID_OBJECT_W_STATMGUN || + holderGO->CLS_ID==CLSID_OBJECT_W_TURRET) + b = use_MountedWeapon(0); + + /*if(inventory().ActiveItem()){ //SkyLoader: why we added it? It works incorrent on long distance if use car + CHudItem* hi = smart_cast(inventory().ActiveItem()); + if(hi) hi->OnAnimationEnd(hi->GetState()); + }*/ + + return b; + }else{ + bool b = false; + CGameObject* holderGO = smart_cast(holder); + if(smart_cast(holder)) + b = use_Vehicle(holder); + + if (holderGO->CLS_ID==CLSID_OBJECT_W_MOUNTED || + holderGO->CLS_ID==CLSID_OBJECT_W_STATMGUN || + holderGO->CLS_ID==CLSID_OBJECT_W_TURRET) + b = use_MountedWeapon(holder); + + if(b){//used succesfully + // switch off torch... + CAttachableItem *I = CAttachmentOwner::attachedItem(CLSID_DEVICE_TORCH); + if (I){ + CTorch* torch = smart_cast(I); + if (torch) torch->Switch(false); + } + } + + /*if(inventory().ActiveItem()){ //SkyLoader: why we added it? It works incorrent on long distance if use car + CHudItem* hi = smart_cast(inventory().ActiveItem()); + if(hi) hi->OnAnimationEnd(hi->GetState()); + }*/ + + return b; + } +} + +void CActor::ActorUse() +{ + //mstate_real = 0; + PickupModeOn(); + + + if (m_holder) + { + CGameObject* GO = smart_cast(m_holder); + NET_Packet P; + CGameObject::u_EventGen (P, GEG_PLAYER_DETACH_HOLDER, ID()); + P.w_u32 (GO->ID()); + CGameObject::u_EventSend (P); + return; + } + + if(character_physics_support()->movement()->PHCapture()) + character_physics_support()->movement()->PHReleaseObject(); + + + + if(m_pUsableObject)m_pUsableObject->use(this); + + if(m_pInvBoxWeLookingAt && m_pInvBoxWeLookingAt->nonscript_usable()) + { + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if (pGameSP) { + if (m_pInvBoxWeLookingAt->IsSafe){ + luabind::functor lua_func; + R_ASSERT2(ai().script_engine().functor("ui_lock.start_lock_ui", lua_func), "Can't find ui_lock.start_lock_ui"); + lua_func(m_pInvBoxWeLookingAt->SafeCode, m_pInvBoxWeLookingAt->lua_game_object()); + pGameSP->StoredInvBox = m_pInvBoxWeLookingAt; + } + else + { + pGameSP->StartStashUI(this, m_pInvBoxWeLookingAt); + } + } + return; + } + + if(!m_pUsableObject||m_pUsableObject->nonscript_usable()) + { + if(m_pPersonWeLookingAt) + { + CEntityAlive* pEntityAliveWeLookingAt = + smart_cast(m_pPersonWeLookingAt); + + if (pEntityAliveWeLookingAt && GameID()==GAME_SINGLE) + { + if(pEntityAliveWeLookingAt->g_Alive()) + { + TryToTalk(); + } + //обыск трупа + else if(!Level().IR_GetKeyState(DIK_LSHIFT)) + { + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if (pGameSP)pGameSP->StartStashUI(this, m_pPersonWeLookingAt); + } + } + } + + collide::rq_result& RQ = HUD().GetCurrentRayQuery(); + CPhysicsShellHolder* object = smart_cast(RQ.O); + u16 element = BI_NONE; + if(object) + element = (u16)RQ.element; + + if(object && Level().IR_GetKeyState(DIK_LSHIFT)) + { + bool b_allow = !!pSettings->line_exist("ph_capture_visuals",object->cNameVisual()); + + if (m_pPersonWeLookingAt && !m_pPersonWeLookingAt->inventory().CanBeDragged()) + b_allow = false; + + if(b_allow && !character_physics_support()->movement()->PHCapture()) + { + character_physics_support()->movement()->PHCaptureObject(object,element); + + } + } + else + { + if (object && smart_cast(object)) + { + NET_Packet P; + CGameObject::u_EventGen (P, GEG_PLAYER_ATTACH_HOLDER, ID()); + P.w_u32 (object->ID()); + CGameObject::u_EventSend (P); + return; + } + + } + } + + +} +BOOL CActor::HUDview()const +{ + return IsFocused() && (cam_active == eacFirstEye || (cam_active == eacLookAt && m_holder && m_holder->HUDView())); +} + +//void CActor::IR_OnMousePress(int btn) +static u32 SlotsToCheck [] = { + KNIFE_SLOT , // 0 + PISTOL_SLOT , // 1 + RIFLE_SLOT , // 2 + RIFLE_2_SLOT , // 14 + GRENADE_SLOT , // 3 + APPARATUS_SLOT , // 4 + BOLT_SLOT , // 5 - Nova: For mouse scrolling to bolt. + ARTEFACT_SLOT , // 10 +}; + +void CActor::OnNextWeaponSlot() +{ + u32 ActiveSlot = inventory().GetActiveSlot(); + if (ActiveSlot == NO_ACTIVE_SLOT) + ActiveSlot = inventory().GetPrevActiveSlot(); + + if (ActiveSlot == NO_ACTIVE_SLOT) + ActiveSlot = KNIFE_SLOT; + + u32 NumSlotsToCheck = sizeof(SlotsToCheck)/sizeof(u32); + for (u32 CurSlot=0; CurSlot= NumSlotsToCheck) return; + for (u32 i=CurSlot+1; i= NumSlotsToCheck) return; + for (s32 i=s32(CurSlot-1); i>=0; i--) + { + if (inventory().ItemFromSlot(SlotsToCheck[i])) + { + if (SlotsToCheck[i] == ARTEFACT_SLOT) + { + IR_OnKeyboardPress(kARTEFACT); + } + else + IR_OnKeyboardPress(kWPN_1 + i); + return; + } + } +} + +float CActor::GetLookFactor() +{ + if (m_input_external_handler) + return m_input_external_handler->mouse_scale_factor(); + + + float factor = 1.f; + + PIItem pItem = inventory().ActiveItem(); + + if (pItem) + factor *= pItem->GetControlInertionFactor(); + + VERIFY(!fis_zero(factor)); + + return factor; +} + +void CActor::set_input_external_handler(CActorInputHandler *handler) +{ + // clear state + if (handler) + mstate_wishful = 0; + + // release fire button + if (handler) + IR_OnKeyboardRelease (kWPN_FIRE); + + // set handler + m_input_external_handler = handler; +} + + + diff --git a/src/xrGameLA/ActorMountedWeapon.cpp b/src/xrGameLA/ActorMountedWeapon.cpp new file mode 100644 index 000000000..ce0caa461 --- /dev/null +++ b/src/xrGameLA/ActorMountedWeapon.cpp @@ -0,0 +1,46 @@ +#include "stdafx.h" +#pragma hdrstop + +#include "actor.h" +#include "WeaponMounted.h" +#include "mounted_turret.h" +#include "../CameraBase.h" +#include "ActorEffector.h" +#include "CharacterPhysicsSupport.h" +bool CActor::use_MountedWeapon(CHolderCustom* object) +{ + cam_Set(eacFirstEye); + +// CHolderCustom* wpn =smart_cast(object); + CHolderCustom* wpn =object; + if(m_holder){ + if(!wpn||(m_holder==wpn)){ + m_holder->detach_Actor(); + character_physics_support()->movement()->CreateCharacter(); + character_physics_support()->movement()->SetPosition(m_holder->ExitPosition()); + character_physics_support()->movement()->SetVelocity(m_holder->ExitVelocity()); + SetWeaponHideState(INV_STATE_BLOCK_ALL, false); + m_holder=NULL; + } + return true; + }else{ + if(wpn){ + Fvector center; Center(center); + if(wpn->Use(Device.vCameraPosition, Device.vCameraDirection,center)){ + if(wpn->attach_Actor(this)){ + // destroy actor character + character_physics_support()->movement()->DestroyCharacter(); + SetWeaponHideState(INV_STATE_BLOCK_ALL, true); + PickupModeOff(); + m_holder=wpn; + if (pCamBobbing){ + Cameras().RemoveCamEffector(eCEBobbing); + pCamBobbing = NULL; + } + return true; + } + } + } + } + return false; +} \ No newline at end of file diff --git a/src/xrGameLA/ActorState.h b/src/xrGameLA/ActorState.h new file mode 100644 index 000000000..cecde5bc6 --- /dev/null +++ b/src/xrGameLA/ActorState.h @@ -0,0 +1,37 @@ +#pragma once +#ifndef __ACTOR_STATE_H__ +#define __ACTOR_STATE_H__ + +enum EActorState +{ + eJammedInactive = 0, + eJammedRed, + eJammedYellow, + eJammedGreen, + eRadiationInactive, + eRadiationRed, + eRadiationYellow, + eRadiationGreen, + eBleedingInactive, + eBleedingRed, + eBleedingYellow, + eBleedingGreen, + eHungerInactive, + eHungerRed, + eHungerYellow, + eHungerGreen, + eThirstInactive, + eThirstRed, + eThirstYellow, + eThirstGreen, + ePsyHealthInactive, + ePsyHealthRed, + ePsyHealthYellow, + ePsyHealthGreen, + eSleepInactive, + eSleepRed, + eSleepYellow, + eSleepGreen, +}; + +#endif \ No newline at end of file diff --git a/src/xrGameLA/ActorVehicle.cpp b/src/xrGameLA/ActorVehicle.cpp new file mode 100644 index 000000000..4a8f39312 --- /dev/null +++ b/src/xrGameLA/ActorVehicle.cpp @@ -0,0 +1,172 @@ +#include "stdafx.h" +#pragma hdrstop + +#include "actor.h" +#include "../CameraBase.h" +#include "ActorEffector.h" +#include "holder_custom.h" +#ifdef DEBUG +#include "PHDebug.h" +#endif +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "Car.h" +#include "../../Include/xrRender/KinematicsAnimated.h" +#include "../../Include/xrRender/Kinematics.h" +#include "PHShellSplitter.h" +#include "actor_anim_defs.h" +#include "game_object_space.h" +#include "characterphysicssupport.h" +#include "inventory.h" + +void CActor::attach_Vehicle(CHolderCustom* vehicle) +{ + if(!vehicle) return; + + if(m_holder) return; + PickupModeOff (); + + // *** movement state - reload + mstate_wishful = 0; + mstate_real = 0; + mstate_old = 0; + m_bJumpKeyPressed = FALSE; + + m_holder=vehicle; + + cam_Set (eacLookAt); + + IKinematics* V = smart_cast(Visual()); R_ASSERT(V); + IKinematicsAnimated* VA = smart_cast(Visual()); R_ASSERT(VA); + + if(!m_holder->attach_Actor(this)){ + m_holder=NULL; + return; + } + // temp play animation + CCar* car = smart_cast(m_holder); + u16 anim_type = car->DriverAnimationType(); + SVehicleAnimCollection& anims = m_vehicle_anims->m_vehicles_type_collections[anim_type]; + VA->PlayCycle (anims.idles[0],FALSE); + + ResetCallbacks (); + u16 head_bone = V->LL_BoneID("bip01_head"); + V->LL_GetBoneInstance (u16(head_bone)).set_callback (bctPhysics, VehicleHeadCallback,this); + + character_physics_support ()->movement()->DestroyCharacter(); + mstate_wishful = 0; + m_holderID=car->ID (); + + SetWeaponHideState (INV_STATE_BLOCK_ALL, true); + + CTorch *flashlight = GetCurrentTorch(); + if (flashlight) + flashlight->Switch(FALSE); + + + CStepManager::on_animation_start(MotionID(), 0); +} + +void CActor::detach_Vehicle() +{ + if(!m_holder) return; + CCar* car=smart_cast(m_holder); + if(!car)return; + + IKinematics* pKinematics = smart_cast(Visual()); R_ASSERT(pKinematics); + u16 head_bone = pKinematics->LL_BoneID("bip01_head"); + //pKinematics->LL_HideBoneVisible(head_bone,TRUE); + + CPHShellSplitterHolder*sh= car->PPhysicsShell()->SplitterHolder(); + if(sh)sh->Deactivate(); + if(!character_physics_support()->movement()->ActivateBoxDynamic(0)) + { + if(sh)sh->Activate(); + return; + } + if(sh)sh->Activate(); + m_holder->detach_Actor(); + + character_physics_support()->movement()->SetPosition(m_holder->ExitPosition()); + character_physics_support()->movement()->SetVelocity(m_holder->ExitVelocity()); + + r_model_yaw=-m_holder->Camera()->yaw; + r_torso.yaw=r_model_yaw; + r_model_yaw_dest=r_model_yaw; + m_holder=NULL; + SetCallbacks (); + IKinematicsAnimated* V= smart_cast(Visual()); R_ASSERT(V); + V->PlayCycle (m_anims->m_normal.legs_idle); + V->PlayCycle (m_anims->m_normal.m_torso_idle); + m_holderID=u16(-1); + + SetWeaponHideState(INV_STATE_BLOCK_ALL, false); + + cam_Set(eacFirstEye); +} + +bool CActor::use_Vehicle(CHolderCustom* object) +{ + +// CHolderCustom* vehicle=smart_cast(object); + CHolderCustom* vehicle=object; + Fvector center; + Center(center); + if(m_holder){ + if(!vehicle&& m_holder->Use(Device.vCameraPosition, Device.vCameraDirection,center)) detach_Vehicle(); + else{ + if(m_holder==vehicle) + if(m_holder->Use(Device.vCameraPosition, Device.vCameraDirection,center))detach_Vehicle(); + } + return true; + }else{ + if(vehicle) + { + if( vehicle->Use(Device.vCameraPosition, Device.vCameraDirection,center)) + { + if (pCamBobbing) + { + Cameras().RemoveCamEffector(eCEBobbing); + pCamBobbing = NULL; + } + + attach_Vehicle(vehicle); + } + return true; + } + return false; + } +} + +void CActor::on_requested_spawn(CObject *object) +{ + CCar * car= smart_cast(object); + if (!car) return; + + CPHShellSplitterHolder*sh= car->PPhysicsShell()->SplitterHolder(); + if(sh)sh->Deactivate(); + if(!character_physics_support()->movement()->ActivateBoxDynamic(0)) + { + if(sh)sh->Activate(); + return; + } + if(sh)sh->Activate(); + + character_physics_support()->movement()->SetPosition(car->ExitPosition()); + character_physics_support()->movement()->SetVelocity(car->ExitVelocity()); + + car->DoEnter(); + attach_Vehicle(car); + + //SkyLoader: straightening of actor torso: + Fvector xyz; + car->XFORM().getXYZi(xyz); + r_torso.yaw = xyz.y; +} + + +CCar* CActor::GetAttachedCar() +{ + return m_holder ? smart_cast(m_holder) : NULL; +} \ No newline at end of file diff --git a/src/xrGameLA/Actor_Events.cpp b/src/xrGameLA/Actor_Events.cpp new file mode 100644 index 000000000..86e5312c1 --- /dev/null +++ b/src/xrGameLA/Actor_Events.cpp @@ -0,0 +1,268 @@ +#include "stdafx.h" +#include "actor.h" +#include "customdetector.h" +#include "uigamesp.h" +#include "hudmanager.h" +#include "weapon.h" +#include "artifact.h" +#include "scope.h" +#include "silencer.h" +#include "grenadelauncher.h" +#include "inventory.h" +#include "level.h" +#include "xr_level_controller.h" +#include "FoodItem.h" +#include "ActorCondition.h" +#include "Grenade.h" + +#include "CameraLook.h" +#include "CameraFirstEye.h" +#include "holder_custom.h" +#include "ui/uiinventoryWnd.h" +#include "game_base_space.h" +#include "UIGameCustom.h" +#include "ui/UIInventoryWnd.h" + +#ifdef DEBUG +#include "PHDebug.h" +#endif +IC BOOL BE (BOOL A, BOOL B) +{ + bool a = !!A; + bool b = !!B; + return a==b; +} + +void CActor::OnEvent (NET_Packet& P, u16 type) +{ + inherited::OnEvent (P,type); + CInventoryOwner::OnEvent (P,type); + + u16 id; + switch (type) + { + case GE_TRADE_BUY: + case GE_OWNERSHIP_TAKE: + { + P.r_u16 (id); + bool duringSpawn = !P.r_eof() && P.r_u8(); + CObject* O = Level().Objects.net_Find (id); + if (!O) + { + Msg("! Error: No object to take/buy [%d]", id); + break; + } + + CFoodItem* pFood = smart_cast(O); + if(pFood) + pFood->SetCurrPlace(eItemPlaceRuck); + + CGameObject* _GO = smart_cast(O); + + if( inventory().CanTakeItem(smart_cast(_GO)) ) + { + O->H_SetParent(smart_cast(this)); + + inventory().Take(_GO, false, true, duringSpawn); + + CUIGameSP* pGameSP = NULL; + if(CurrentGameUI()) + { + pGameSP = smart_cast(CurrentGameUI()); + if (Level().CurrentViewEntity() == this) + CurrentGameUI()->ReInitShownUI(); + }; + + //добавить отсоединенный аддон в инвентарь + if(pGameSP) + { + if(pGameSP->TopInputReceiver() == pGameSP->m_InventoryMenu) + { + pGameSP->m_InventoryMenu->AddItemToBag(smart_cast(O)); + } + } + + SelectBestWeapon(O); + } + else + { + NET_Packet P; + u_EventGen(P,GE_OWNERSHIP_REJECT,ID()); + P.w_u16(u16(O->ID())); + u_EventSend(P); + } + } + break; + case GE_TRADE_SELL: + case GE_OWNERSHIP_REJECT: + { + P.r_u16 (id); + CObject* O = Level().Objects.net_Find (id); + if (!O) + { + Msg("! Error: No object to reject/sell [%d]", id); + break; + } + bool just_before_destroy = !P.r_eof() && P.r_u8(); + O->SetTmpPreDestroy (just_before_destroy); + if (inventory().DropItem(smart_cast(O)) && !O->getDestroy()) + { + O->H_SetParent(0,just_before_destroy); +//. feel_touch_deny(O,2000); + Level().m_feel_deny.feel_touch_deny(O, 1000); + + } + + SelectBestWeapon(O); + + if (Level().CurrentViewEntity() == this && CurrentGameUI()) + CurrentGameUI()->ReInitShownUI(); + } + break; + case GE_INV_ACTION: + { + s32 cmd; + P.r_s32 (cmd); + u32 flags; + P.r_u32 (flags); + s32 ZoomRndSeed = P.r_s32(); + s32 ShotRndSeed = P.r_s32(); + + if (flags & CMD_START) + { + if (cmd == kWPN_ZOOM) + SetZoomRndSeed(ZoomRndSeed); + if (cmd == kWPN_FIRE) + SetShotRndSeed(ShotRndSeed); + IR_OnKeyboardPress(cmd); + } + else + IR_OnKeyboardRelease(cmd); + } + break; + case GEG_PLAYER_ITEM2SLOT: + case GEG_PLAYER_ITEM2BELT: + case GEG_PLAYER_ITEM2RUCK: + case GEG_PLAYER_ITEM_EAT: + case GEG_PLAYER_ACTIVATEARTEFACT: + { + P.r_u16 (id); + CObject* O = Level().Objects.net_Find (id); + if(!O) break; + if (O->getDestroy()) + { +#ifdef DEBUG + Msg("! something to destroyed object - %s[%d]0x%X", *O->cName(), id, smart_cast(O)); +#endif + break; + } + switch (type) + { + case GEG_PLAYER_ITEM2SLOT: + u16 slot_id; + P.r_u16(slot_id); + inventory().Slot((TSlotId)slot_id, smart_cast(O)); + break; + case GEG_PLAYER_ITEM2BELT: + inventory().Belt(smart_cast(O)); + break; + case GEG_PLAYER_ITEM2RUCK: + inventory().Ruck(smart_cast(O)); + break; + case GEG_PLAYER_ITEM_EAT: + inventory().Eat(smart_cast(O)); + break; + case GEG_PLAYER_ACTIVATEARTEFACT: + { + CArtefact* pArtefact = smart_cast(O); + pArtefact->ActivateArtefact (); + }break; + } + }break; + case GEG_PLAYER_ACTIVATE_SLOT: + { + u16 slot_id; + P.r_u16 (slot_id); + + inventory().Activate ((TSlotId)slot_id); + + }break; + + case GEG_PLAYER_WEAPON_HIDE_STATE: + { + u32 State = P.r_u32(); + BOOL Set = !!P.r_u8(); + inventory().SetSlotsBlocked ((u16)State, !!Set); + }break; + case GE_MOVE_ACTOR: + { + Fvector NewPos, NewRot; + P.r_vec3(NewPos); + P.r_vec3(NewRot); + + MoveActor(NewPos, NewRot); + }break; + case GE_ACTOR_MAX_POWER: + { + conditions().MaxPower(); + conditions().ClearWounds(); + ClearBloodWounds(); + }break; + case GEG_PLAYER_ATTACH_HOLDER: + { + u32 id = P.r_u32(); + CObject* O = Level().Objects.net_Find (id); + if (!O){ + Msg("! Error: No object to attach holder [%d]", id); + break; + } + VERIFY(m_holder==NULL); + CHolderCustom* holder = smart_cast(O); + if(!holder->Engaged()) use_Holder (holder); + + }break; + case GEG_PLAYER_DETACH_HOLDER: + { + if (!m_holder) break; + u32 id = P.r_u32(); + CGameObject* GO = smart_cast(m_holder); + VERIFY (id==GO->ID()); + use_Holder (NULL); + }break; + case GEG_PLAYER_PLAY_HEADSHOT_PARTICLE: + { + OnPlayHeadShotParticle(P); + }break; + case GE_ACTOR_JUMPING: + { + /* + Fvector dir; + P.r_dir(dir); + float jump = P.r_float(); + NET_SavedAccel = dir; + extern float NET_Jump; + NET_Jump = jump; + m_bInInterpolation = false; + mstate_real |= mcJump; + */ + }break; + } +} + +void CActor::MoveActor (Fvector NewPos, Fvector NewDir) +{ + Fmatrix M = XFORM(); + M.translate(NewPos); + r_model_yaw = NewDir.y; + r_torso.yaw = NewDir.y; + r_torso.pitch = -NewDir.x; + unaffected_r_torso.yaw = r_torso.yaw; + unaffected_r_torso.pitch= r_torso.pitch; + unaffected_r_torso.roll = 0;//r_torso.roll; + + r_torso_tgt_roll = 0; + cam_Active()->Set (-unaffected_r_torso.yaw,unaffected_r_torso.pitch,unaffected_r_torso.roll); + ForceTransform(M); + + m_bInInterpolation = false; +} \ No newline at end of file diff --git a/src/xrGameLA/Actor_Feel.cpp b/src/xrGameLA/Actor_Feel.cpp new file mode 100644 index 000000000..982db8ee9 --- /dev/null +++ b/src/xrGameLA/Actor_Feel.cpp @@ -0,0 +1,291 @@ +#include "stdafx.h" +#include "actor.h" +#include "weapon.h" +#include "mercuryball.h" +#include "inventory.h" +#include "hudmanager.h" +#include "character_info.h" +#include "xr_level_controller.h" +#include "UsableScriptObject.h" +#include "customzone.h" +#include "../GameMtlLib.h" +#include "ui/UIMainIngameWnd.h" +#include "UIGameCustom.h" +#include "Grenade.h" +#include "clsid_game.h" +#include "game_cl_base.h" +#include "Level.h" + +//B77B40 +#define PICKUP_INFO_COLOR_FAREST 0xFF686054//895555//DDDDDD +#define PICKUP_INFO_COLOR_MIDLE 0xFF776E61 +#define PICKUP_INFO_COLOR_NEAREST 0xFF897F70 +//AAAAAA + +void CActor::feel_touch_new (CObject* O) +{ +} + +void CActor::feel_touch_delete (CObject* O) +{ + CPhysicsShellHolder* sh=smart_cast(O); + if(sh&&sh->character_physics_support()) m_feel_touch_characters--; +} + +BOOL CActor::feel_touch_contact (CObject *O) +{ + CInventoryItem *item = smart_cast(O); + CInventoryOwner *inventory_owner = smart_cast(O); + + if (item && item->Useful() && !item->object().H_Parent()) + return TRUE; + + if(inventory_owner && inventory_owner != smart_cast(this)) + { + CPhysicsShellHolder* sh=smart_cast(O); + if(sh&&sh->character_physics_support()) m_feel_touch_characters++; + return TRUE; + } + + return (FALSE); +} + +BOOL CActor::feel_touch_on_contact (CObject *O) +{ + CCustomZone *custom_zone = smart_cast(O); + if (!custom_zone) + return (TRUE); + + Fsphere sphere; + sphere.P = Position(); + sphere.R = EPS_L; + if (custom_zone->inside(sphere)) + return (TRUE); + + return (FALSE); +} + +void CActor::PickupModeOn() +{ + m_bPickupMode = true; +} + +void CActor::PickupModeOff() +{ + m_bPickupMode = false; +} + +ICF static BOOL info_trace_callback(collide::rq_result& result, LPVOID params) +{ + BOOL& bOverlaped = *(BOOL*)params; + if(result.O){ + if (Level().CurrentEntity()!=result.O){ + bOverlaped = TRUE; //tatarinrafa: раскоментил, чтобы нельзя было подбирать и видеть предметы через двери и тд. Надо тестить.| Uncomented so that actor cant pick up through dynamic objects. Needs tests + return TRUE;//FALSE; + }else{ + + return TRUE; + } + } + else{ + //получить треугольник и узнать его материал + CDB::TRI* T = Level().ObjectSpace.GetStaticTris() + result.element; + if (GMLib.GetMaterialByIdx(T->material)->Flags.is(SGameMtl::flPassable) && !GMLib.GetMaterialByIdx(T->material)->Flags.is(SGameMtl::flDynamic)){ + return TRUE; + } + } + bOverlaped = TRUE; + return FALSE; +} + +BOOL CActor::CanPickItem(const CFrustum& frustum, const Fvector& from, CObject* item) +{ + BOOL bOverlaped = FALSE; + Fvector dir,to; + item->Center (to); + float range = dir.sub(to,from).magnitude(); + //CInventoryItem* invitem = smart_cast(item); + //if (invitem->object().CLS_ID == CLSID_ARTEFACT){ + //Msg("%f %s %s %s %s", range, invitem->object().cName(), invitem->object().cNameSect_str(), invitem->object().cNameSect().c_str(), invitem->object().cNameSect()); + //} + if (range>0.25f){ + if (frustum.testSphere_dirty(to,item->Radius())){ + dir.div (range); + collide::ray_defs RD(from, dir, range, CDB::OPT_CULL, collide::rqtBoth); + VERIFY (!fis_zero(RD.dir.square_magnitude())); + RQR.r_clear (); + Level().ObjectSpace.RayQuery(RQR,RD, info_trace_callback, &bOverlaped, NULL, item); + } + } + return !bOverlaped; +} + +#include "ai\monsters\ai_monster_utils.h" +void CActor::PickupModeUpdate() +{ + if(!m_bPickupMode) return; + if (GameID() != GAME_SINGLE) return; + + //подбирание объекта + if(inventory().m_pTarget && inventory().m_pTarget->Useful() && + m_pUsableObject && m_pUsableObject->nonscript_usable() && + !Level().m_feel_deny.is_object_denied(smart_cast(inventory().m_pTarget)) ) + { + NET_Packet P; + u_EventGen(P,GE_OWNERSHIP_TAKE, ID()); + P.w_u16(inventory().m_pTarget->object().ID()); + u_EventSend(P); + } + + if (eacFirstEye != cam_active) + feel_touch_update (Position(), m_fPickupInfoRadius); + else { + feel_touch_update (get_bone_position(this, "bip01_spine"), m_fPickupInfoRadius); + } + + CFrustum frustum; + frustum.CreateFromMatrix(Device.mFullTransform,FRUSTUM_P_LRTB|FRUSTUM_P_FAR); + //. slow (ray-query test) + for(xr_vector::iterator it = feel_touch.begin(); it != feel_touch.end(); it++) + if (CanPickItem(frustum,Device.vCameraPosition,*it)) PickupInfoDraw(*it); +} + +#include "../CameraBase.h" +BOOL g_b_COD_PickUpMode = TRUE; +void CActor::PickupModeUpdate_COD () +{ + if (Level().CurrentViewEntity() != this || !g_b_COD_PickUpMode) return; + + if (!g_Alive() || eacFreeLook == cam_active) + { + CurrentGameUI()->UIMainIngameWnd->SetPickUpItem(NULL); + return; + }; + + CFrustum frustum; + frustum.CreateFromMatrix(Device.mFullTransform,FRUSTUM_P_LRTB|FRUSTUM_P_FAR); + + //--------------------------------------------------------------------------- + ISpatialResult.clear_not_free (); + g_SpatialSpace->q_frustum(ISpatialResult, 0, STYPE_COLLIDEABLE, frustum); + //--------------------------------------------------------------------------- + + float maxlen = 1000.0f; + CInventoryItem* pNearestItem = NULL; + for (u32 o_it=0; o_it (spatial->dcast_CObject ()); + if (0 == pIItem) continue; + if (pIItem->object().H_Parent() != NULL) continue; + if (!pIItem->CanTake()) continue; + if (pIItem->object().CLS_ID == CLSID_OBJECT_G_RPG7 || pIItem->object().CLS_ID == CLSID_OBJECT_G_FAKE) + continue; + + CGrenade* pGrenade = smart_cast (spatial->dcast_CObject ()); + if (pGrenade && !pGrenade->Useful()) continue; + + CMissile* pMissile = smart_cast (spatial->dcast_CObject ()); + if (pMissile && !pMissile->Useful()) continue; + + Fvector A, B, tmp; + pIItem->object().Center (A); + if (A.distance_to_sqr(Position())>4) continue; + + tmp.sub(A, cam_Active()->vPosition); + B.mad(cam_Active()->vPosition, cam_Active()->vDirection, tmp.dotproduct(cam_Active()->vDirection)); + float len = B.distance_to_sqr(A); + if (len > 1) continue; + + if (maxlen>len && !pIItem->object().getDestroy()) + { + maxlen = len; + pNearestItem = pIItem; + }; + } + + if(pNearestItem) + { + CFrustum frustum; + frustum.CreateFromMatrix (Device.mFullTransform,FRUSTUM_P_LRTB|FRUSTUM_P_FAR); + if (!CanPickItem(frustum,Device.vCameraPosition,&pNearestItem->object())) + pNearestItem = NULL; + } + + if (pNearestItem && pNearestItem->cast_game_object()) + { + if (Level().m_feel_deny.is_object_denied(pNearestItem->cast_game_object())) + pNearestItem = NULL; + } + + if (CurrentGameUI() && CurrentGameUI()->UIMainIngameWnd) + CurrentGameUI()->UIMainIngameWnd->SetPickUpItem(pNearestItem); + + if (pNearestItem && m_bPickupMode) + { + //подбирание объекта + Game().SendPickUpEvent(ID(), pNearestItem->object().ID()); + + PickupModeOff(); + } +}; + +void CActor::PickupInfoDraw(CObject* object) +{ + //tatarinrafa: Немного изменил то, как рабоатет подсвечивание/Added some changes to how highlighting works + CInventoryItem* item = smart_cast(object); + if(!item || !item->IsPickUpVisible()) return; + + LPCSTR draw_str = item->NameShort(); + Fmatrix res; + res.mul (Device.mFullTransform,object->XFORM()); + Fvector4 v_res; + Fvector shift; + + Fvector dir, to; + object->Center(to); + float range = dir.sub(to, Device.vCameraPosition).magnitude(); + + shift.set(0,0,0); + + res.transform(v_res,shift); + + if (v_res.z < 0 || v_res.w < 0) return; + if (v_res.x < -1.f || v_res.x > 1.f || v_res.y<-1.f || v_res.y>1.f) return; + + float x = (1.f + v_res.x)/2.f * (Device.dwWidth); + float y = (1.f - v_res.y)/2.f * (Device.dwHeight); + + float convertedX = (x / Device.dwWidth); + float convertedY = (y / Device.dwHeight); + convertedX = 1280 * convertedX; + convertedY = 720 * convertedY; + //Msg("x = %f, y = %f, convertedX = %f, convertedY = %f", x, y, convertedX, convertedY); + + if (draw_str && convertedX > 380 && convertedX < 900 && convertedY > 200 && convertedY <660) + { + if (range < 2.f){ + UI().Font().pFontGraffiti22Russian->SetAligment(CGameFont::alCenter); + UI().Font().pFontGraffiti22Russian->SetColor(PICKUP_INFO_COLOR_NEAREST); + UI().Font().pFontGraffiti22Russian->Out(x, y, draw_str); + } + else if(range < 4.f){ + UI().Font().pFontLetterica18Russian->SetAligment(CGameFont::alCenter); + UI().Font().pFontLetterica18Russian->SetColor(PICKUP_INFO_COLOR_MIDLE); + UI().Font().pFontLetterica18Russian->Out(x, y, draw_str); + }else + { + UI().Font().pFontLetterica16Russian->SetAligment(CGameFont::alCenter); + UI().Font().pFontLetterica16Russian->SetColor(PICKUP_INFO_COLOR_FAREST); + UI().Font().pFontLetterica16Russian->Out(x, y, draw_str); + } + + + } +} + +void CActor::feel_sound_new(CObject* who, int type, CSound_UserDataPtr user_data, const Fvector& Position, float power) +{ + if(who == this) + m_snd_noise = _max(m_snd_noise,power); +} diff --git a/src/xrGameLA/Actor_Flags.h b/src/xrGameLA/Actor_Flags.h new file mode 100644 index 000000000..02203bb66 --- /dev/null +++ b/src/xrGameLA/Actor_Flags.h @@ -0,0 +1,25 @@ +#pragma once + +enum +{ + AF_GODMODE = (1<<0), + AF_INVISIBLE = (1<<1), + AF_ALWAYSRUN = (1<<2), + AF_UNLIMITEDAMMO = (1<<3), + AF_RUN_BACKWARD = (1<<4), + AF_AUTOPICKUP = (1<<5), + AF_PSP = (1<<6), + AF_USE_CROSSHAIR = (1<<7), + AF_WPN_BOBBING = (1<<8), + AF_COLLISION = (1<<9), + AF_STRAFE_INERT = (1<<10), + AF_FST_PSN_DEATH = (1<<11), + AF_ACTOR_BODY = (1<<12), + AF_HEAD_BOBBING = (1<<13), + AF_CORRECT_FIREPOS = (1<<14), +}; + +extern Flags32 psActorFlags; + +extern BOOL GodMode (); + diff --git a/src/xrGameLA/Actor_Movement.cpp b/src/xrGameLA/Actor_Movement.cpp new file mode 100644 index 000000000..822a7a0ed --- /dev/null +++ b/src/xrGameLA/Actor_Movement.cpp @@ -0,0 +1,644 @@ +// Actor_Movement.cpp: передвижения актера +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "actor.h" +#include "inventory.h" +#include "weapon.h" +#include "../CameraBase.h" +#include "xrMessages.h" + +#include "level.h" +#include "string_table.h" +#include "actorcondition.h" +#include "game_cl_base.h" +#include "WeaponMagazined.h" +#include "CharacterPhysicsSupport.h" +#ifdef DEBUG +#include "phdebug.h" +#endif +#include "UIGameCustom.h" + +static const float s_fLandingTime1 = 0.1f;// через сколько снять флаг Landing1 (т.е. включить следующую анимацию) +static const float s_fLandingTime2 = 0.3f;// через сколько снять флаг Landing2 (т.е. включить следующую анимацию) +static const float s_fJumpTime = 0.3f; +static const float s_fJumpGroundTime = 0.1f; // для снятия флажка Jump если на земле + const float s_fFallTime = 0.2f; + +IC static void generate_orthonormal_basis1(const Fvector& dir,Fvector& updir, Fvector& right) +{ + + right.crossproduct(dir,updir); //. <-> + right.normalize(); + updir.crossproduct(right,dir); +} + + +void CActor::g_cl_ValidateMState(float dt, u32 mstate_wf) +{ + // Lookout + if (mstate_wf&mcLookout) mstate_real |= mstate_wf&mcLookout; + else mstate_real &= ~mcLookout; + + if (mstate_real&(mcJump|mcFall|mcLanding|mcLanding2)) + mstate_real &= ~mcLookout; + + // закончить приземление + if (mstate_real&(mcLanding|mcLanding2)){ + m_fLandingTime -= dt; + if (m_fLandingTime<=0.f){ + mstate_real &=~ (mcLanding|mcLanding2); + mstate_real &=~ (mcFall|mcJump); + } + } + // закончить падение + if (character_physics_support()->movement()->gcontact_Was){ + if (mstate_real&mcFall){ + if (character_physics_support()->movement()->GetContactSpeed()>4.f){ + if (fis_zero(character_physics_support()->movement()->gcontact_HealthLost)){ + m_fLandingTime = s_fLandingTime1; + mstate_real |= mcLanding; + }else{ + m_fLandingTime = s_fLandingTime2; + mstate_real |= mcLanding2; + } + } + } + m_bJumpKeyPressed = TRUE; + m_fJumpTime = s_fJumpTime; + mstate_real &=~ (mcFall|mcJump); + } + if ((mstate_wf&mcJump)==0) + m_bJumpKeyPressed = FALSE; + + // Зажало-ли меня/уперся - не двигаюсь + if (((character_physics_support()->movement()->GetVelocityActual()<0.2f)&&(!(mstate_real&(mcFall|mcJump)))) || character_physics_support()->movement()->bSleep) + { + mstate_real &=~ mcAnyMove; + } + if (character_physics_support()->movement()->Environment()==CPHMovementControl::peOnGround || character_physics_support()->movement()->Environment()==CPHMovementControl::peAtWall) + { + // если на земле гарантированно снимать флажок Jump + if (((s_fJumpTime-m_fJumpTime)>s_fJumpGroundTime)&&(mstate_real&mcJump)) + { + mstate_real &=~ mcJump; + m_fJumpTime = s_fJumpTime; + } + } + if(character_physics_support()->movement()->Environment()==CPHMovementControl::peAtWall) + { + if(!(mstate_real & mcClimb)) + { + mstate_real |=mcClimb; + mstate_real &=~mcSprint; + cam_SetLadder(); + } + } + else + { + if (mstate_real & mcClimb) + { + cam_UnsetLadder(); + } + mstate_real &=~mcClimb; + }; + + if (mstate_wf != mstate_real){ + if ((mstate_real&mcCrouch)&&((0==(mstate_wf&mcCrouch)) || mstate_real&mcClimb)){ + if (character_physics_support()->movement()->ActivateBoxDynamic(0)){ + mstate_real &= ~mcCrouch; + } + } + } + + if(!CanAccelerate()&&isActorAccelerated(mstate_real, IsZoomAimingMode())) + { + mstate_real ^=mcAccel; + }; + + if (this == Level().CurrentControlEntity()) + { + bool bOnClimbNow = !!(mstate_real&mcClimb); + bool bOnClimbOld = !!(mstate_old&mcClimb); + + if (bOnClimbNow != bOnClimbOld ) + { + SetWeaponHideState (INV_STATE_LADDER, bOnClimbNow ); + }; + }; +}; + +void CActor::g_cl_CheckControls(u32 mstate_wf, Fvector &vControlAccel, float &Jump, float dt) +{ + mstate_old = mstate_real; + vControlAccel.set (0,0,0); + + if (!(mstate_real&mcFall) && (character_physics_support()->movement()->Environment()==CPHMovementControl::peInAir)) + { + m_fFallTime -= dt; + if (m_fFallTime<=0.f){ + m_fFallTime = s_fFallTime; + mstate_real |= mcFall; + mstate_real &=~ mcJump; + } + } + + if(!CanMove()) + { + if(mstate_wf&mcAnyMove) + { + StopAnyMove(); + mstate_wf &= ~mcAnyMove; + mstate_wf &= ~mcJump; + } + //character_physics_support()->movement()->EnableCharacter(); + //return; + } + + // update player accel + if (mstate_wf&mcFwd) vControlAccel.z += 1; + if (mstate_wf&mcBack) vControlAccel.z += -1; + if (mstate_wf&mcLStrafe) vControlAccel.x += -1; + if (mstate_wf&mcRStrafe) vControlAccel.x += 1; + + if (character_physics_support()->movement()->Environment()==CPHMovementControl::peOnGround || character_physics_support()->movement()->Environment()==CPHMovementControl::peAtWall ) + { + // crouch + if ((0==(mstate_real&mcCrouch))&&(mstate_wf&mcCrouch)) + { + if(mstate_real&mcClimb) + { + mstate_wf&=~mcCrouch; + } + else + { + character_physics_support()->movement()->EnableCharacter(); + bool Crouched = false; + if (isActorAccelerated(mstate_wf, IsZoomAimingMode())) + Crouched = character_physics_support()->movement()->ActivateBoxDynamic(1); + else + Crouched = character_physics_support()->movement()->ActivateBoxDynamic(2); + if (Crouched) mstate_real |= mcCrouch; + } + } + // jump + m_fJumpTime -= dt; + + if ( CanJump() && (mstate_wf&mcJump) ) + { + mstate_real |= mcJump; + m_bJumpKeyPressed = TRUE; + Jump = m_fJumpSpeed; + m_fJumpTime = s_fJumpTime; + + + //уменьшить силу игрока из-за выполненого прыжка + if (!GodMode()) + conditions().ConditionJump(inventory().TotalWeight() / MaxCarryWeight()); + } + + /* + if(m_bJumpKeyPressed) + Jump = m_fJumpSpeed; + */ + + + // mask input into "real" state + u32 move = mcAnyMove|mcAccel; + + if (((mstate_real&mcCrouch))) + { + if (!isActorAccelerated(mstate_real, IsZoomAimingMode()) && isActorAccelerated(mstate_wf, IsZoomAimingMode())) + { + character_physics_support()->movement()->EnableCharacter(); + if(!character_physics_support()->movement()->ActivateBoxDynamic(1))move &=~mcAccel; + } + + if (isActorAccelerated(mstate_real, IsZoomAimingMode()) && !isActorAccelerated(mstate_wf, IsZoomAimingMode())) + { + character_physics_support()->movement()->EnableCharacter(); + if(character_physics_support()->movement()->ActivateBoxDynamic(2))mstate_real &=~mcAccel; + } + } + + if ((mstate_wf&mcSprint) && !CanSprint()) + { + mstate_wf &= ~mcSprint; + } + + mstate_real &= (~move); + mstate_real |= (mstate_wf & move); + + if(mstate_wf&mcSprint) + mstate_real|=mcSprint; + else + mstate_real&=~mcSprint; + if(!(mstate_real&(mcFwd|mcLStrafe|mcRStrafe))||(mstate_real&mcFwd&&mstate_real&mcBack)||(mstate_real&mcLStrafe&&mstate_real&mcRStrafe)||mstate_real&(mcCrouch|mcClimb)|| !isActorAccelerated(mstate_wf, IsZoomAimingMode())) + { + mstate_real&=~mcSprint; + mstate_wishful&=~mcSprint; + } + + // check player move state + if (mstate_real&mcAnyMove) + { + BOOL bAccelerated = isActorAccelerated(mstate_real, IsZoomAimingMode())&&CanAccelerate(); + + + + // correct "mstate_real" if opposite keys pressed + if (_abs(vControlAccel.z)EPS) { + scale = m_fWalkAccel/scale; + if (bAccelerated) + { + if (mstate_real&mcBack) + scale *= m_fRunBackFactor; + else + scale *= m_fRunFactor + m_fRunFactorAdditional;//tatarinrafa: added additional run speed for arts and outfit + } + else + { + if (mstate_real&mcBack) + scale *= m_fWalkBackFactor; + } + + + if (mstate_real&mcCrouch) scale *= m_fCrouchFactor; + if (mstate_real&mcClimb) scale *= m_fClimbFactor; + if (mstate_real&mcSprint) scale *= m_fSprintFactor + m_fSprintFactorAdditional;//tatarinrafa: added additional sprint speed for arts and outfit + + if (mstate_real&(mcLStrafe|mcRStrafe) && !(mstate_real&mcCrouch)) + { + if (bAccelerated) + scale *= m_fRun_StrafeFactor; + else + scale *= m_fWalk_StrafeFactor; + } + + vControlAccel.mul (scale); + }else{ + // mstate_real &= ~mcAnyMove; + } + } + }else{ + // mstate_real &=~ mcAnyMove; + } + + //------------------------------------------------------------------------------- + + + //transform local dir to world dir + Fmatrix mOrient; + mOrient.rotateY (-r_model_yaw); + mOrient.transform_dir(vControlAccel); + //XFORM().transform_dir(vControlAccel); + + /* + if(mstate_real&mcClimb&&mstate_real&mcAnyMove&& + inventory().ActiveItem()&&inventory().ActiveItem()->HandDependence()==hd2Hand) + { + //inventory().ActiveItem()->Deactivate(); + inventory().Activate(NO_ACTIVE_SLOT); + } +*/ +} + +#define ACTOR_ANIM_SECT "actor_animation" + +void CActor::g_Orientate (u32 mstate_rl, float dt) +{ + static float fwd_l_strafe_yaw = deg2rad(pSettings->r_float(ACTOR_ANIM_SECT, "fwd_l_strafe_yaw")); + static float back_l_strafe_yaw = deg2rad(pSettings->r_float(ACTOR_ANIM_SECT, "back_l_strafe_yaw")); + static float fwd_r_strafe_yaw = deg2rad(pSettings->r_float(ACTOR_ANIM_SECT, "fwd_r_strafe_yaw")); + static float back_r_strafe_yaw = deg2rad(pSettings->r_float(ACTOR_ANIM_SECT, "back_r_strafe_yaw")); + static float l_strafe_yaw = deg2rad(pSettings->r_float(ACTOR_ANIM_SECT, "l_strafe_yaw")); + static float r_strafe_yaw = deg2rad(pSettings->r_float(ACTOR_ANIM_SECT, "r_strafe_yaw")); + + if(!g_Alive())return; + // visual effect of "fwd+strafe" like motion + float calc_yaw = 0; + if(mstate_real&mcClimb) + { + if(g_LadderOrient()) return; + } + switch(mstate_rl&mcAnyMove) + { + case mcFwd+mcLStrafe: + calc_yaw = +fwd_l_strafe_yaw;//+PI_DIV_4; + break; + case mcBack+mcRStrafe: + calc_yaw = +back_r_strafe_yaw;//+PI_DIV_4; + break; + case mcFwd+mcRStrafe: + calc_yaw = -fwd_r_strafe_yaw;//-PI_DIV_4; + break; + case mcBack+mcLStrafe: + calc_yaw = -back_l_strafe_yaw;//-PI_DIV_4; + break; + case mcLStrafe: + calc_yaw = +l_strafe_yaw;//+PI_DIV_3-EPS_L; + break; + case mcRStrafe: + calc_yaw = -r_strafe_yaw;//-PI_DIV_4+EPS_L; + break; + } + + // lerp angle for "effect" and capture torso data from camera + angle_lerp (r_model_yaw_delta,calc_yaw,PI_MUL_4,dt); + + // build matrix + Fmatrix mXFORM; + mXFORM.rotateY (-(r_model_yaw + r_model_yaw_delta)); + mXFORM.c.set (Position()); + XFORM().set (mXFORM); + + //------------------------------------------------- + + float tgt_roll = 0.f; + if (mstate_rl&mcLookout) + { + tgt_roll = (mstate_rl&mcLLookout)?-ACTOR_LOOKOUT_ANGLE:ACTOR_LOOKOUT_ANGLE; + + if( (mstate_rl&mcLLookout) && (mstate_rl&mcRLookout) ) + tgt_roll = 0.0f; + } + if (!fsimilar(tgt_roll,r_torso_tgt_roll,EPS)){ + angle_lerp (r_torso_tgt_roll,tgt_roll,PI_MUL_2,dt); + r_torso_tgt_roll= angle_normalize_signed(r_torso_tgt_roll); + } +} +bool CActor::g_LadderOrient() +{ + Fvector leader_norm; + character_physics_support()->movement()->GroundNormal(leader_norm); + if(_abs(leader_norm.y)>M_SQRT1_2) return false; + //leader_norm.y=0.f; + float mag=leader_norm.magnitude(); + if(magGetWorldYaw(); + r_torso.pitch = cam_Active()->GetWorldPitch(); + } + else if (eacFreeLook==cam_active || m_bActorShadows) + { + r_torso.yaw = cam_FirstEye()->GetWorldYaw(); + r_torso.pitch = cam_FirstEye()->GetWorldPitch(); + } + else + { + r_torso.yaw = cam_FirstEye()->GetWorldYaw(); + r_torso.pitch = 0; + } + + unaffected_r_torso.yaw = r_torso.yaw; + unaffected_r_torso.pitch = r_torso.pitch; + unaffected_r_torso.roll = r_torso.roll; + + CWeaponMagazined *pWM = smart_cast(inventory().GetActiveSlot() != NO_ACTIVE_SLOT ? + inventory().ItemFromSlot(inventory().GetActiveSlot())/*inventory().m_slots[inventory().GetActiveSlot()].m_pIItem*/ : NULL); + if (pWM && pWM->GetCurrentFireMode() == 1 && eacFirstEye != cam_active) + { + Fvector dangle = weapon_recoil_last_delta(); + r_torso.yaw = unaffected_r_torso.yaw + dangle.y; + r_torso.pitch = unaffected_r_torso.pitch + dangle.x; + } + + // если есть движение - выровнять модель по камере + if (mstate_rl&mcAnyMove) { + r_model_yaw = angle_normalize(r_torso.yaw); + mstate_real &=~mcTurn; + } else { + if (eacFirstEye!=cam_active) + { + // if camera rotated more than 45 degrees - align model with it + float ty = angle_normalize(r_torso.yaw); + if (_abs(r_model_yaw-ty)>PI_DIV_4) { + r_model_yaw_dest = ty; + // + mstate_real |= mcTurn; + } + if (_abs(r_model_yaw-r_model_yaw_dest)PI_DIV_6) { + if ((r_model_yaw-ty) > 0) + r_model_yaw = angle_normalize(r_torso.yaw + PI_DIV_6); + else + r_model_yaw = angle_normalize(r_torso.yaw - PI_DIV_6); + r_model_yaw_dest = ty; + // + mstate_real |= mcTurn; + } + if (_abs(r_model_yaw-r_model_yaw_dest)(inventory().GetActiveSlot() != NO_ACTIVE_SLOT ? + inventory().ItemFromSlot(inventory().GetActiveSlot())/*inventory().m_slots[inventory().GetActiveSlot()].m_pIItem*/ : NULL); + if (pWM && pWM->GetCurrentFireMode() == 1/* && eacFirstEye != cam_active*/) + { + Fvector dangle = weapon_recoil_last_delta(); + r_torso.yaw += dangle.y; + r_torso.pitch += dangle.x; + r_torso.roll += dangle.z; + } +} + +bool isActorAccelerated (u32 mstate, bool ZoomMode) +{ + bool res = false; + if (mstate&mcAccel) + res = psActorFlags.test(AF_ALWAYSRUN)?false:true; + else + res = psActorFlags.test(AF_ALWAYSRUN)?true :false; + if (mstate&(mcCrouch|mcClimb|mcJump|mcLanding|mcLanding2)) + return res; + if (mstate & mcLookout || ZoomMode) + return false; + return res; +} + +bool CActor::CanAccelerate () +{ + bool can_accel = !conditions().IsLimping() && + !character_physics_support()->movement()->PHCapture() && +// && !m_bZoomAimingMode +// && !(mstate_real&mcLookout) + (m_time_lock_accel < Device.dwTimeGlobal) + ; + + return can_accel; +} + +bool CActor::CanRun() +{ + bool can_run = !m_bZoomAimingMode && !(mstate_real&mcLookout); + return can_run; +} + +bool CActor::CanSprint () +{ + bool can_Sprint = CanAccelerate() && !conditions().IsCantSprint() && + Game().PlayerCanSprint(this) + && CanRun() + && !(mstate_real&mcLStrafe || mstate_real&mcRStrafe) + && InventoryAllowSprint() + ; + + return can_Sprint; +} + +bool CActor::CanJump () +{ + bool can_Jump = /*!IsLimping() &&*/ + !character_physics_support()->movement()->PHCapture() &&((mstate_real&mcJump)==0) && (m_fJumpTime<=0.f) + && !m_bJumpKeyPressed && !conditions().IsCantWalk() && !conditions().IsCantWalkWeight() &&!m_bZoomAimingMode;// && ((mstate_real&mcCrouch)==0); + + return can_Jump; +} + +bool CActor::CanMove () +{ + if( conditions().IsCantWalk() ) + { + if(mstate_wishful&mcAnyMove) + { + CurrentGameUI()->AddCustomStatic("cant_walk", true); + } + return false; + }else + if( conditions().IsCantWalkWeight() ) + { + if(mstate_wishful&mcAnyMove) + { + CurrentGameUI()->AddCustomStatic("cant_walk_weight", true); + } + return false; + + } + + if(IsTalking()) + return false; + else + return true; +} +#include "player_hud.h" +void CActor::StopAnyMove() +{ + mstate_wishful &= ~mcAnyMove; + mstate_real &= ~mcAnyMove; + + if (this == Level().CurrentViewEntity()) + { + g_player_hud->OnMovementChanged((EMoveCommand)0); + } +} + +void CActor::BreakSprint() +{ + mstate_wishful &= ~mcSprint; +} + +bool CActor::is_jump() +{ + return ((mstate_real & (mcJump|mcFall|mcLanding|mcLanding2)) != 0); +} + +float CActor::MaxWalkWeight() const +{ + float max_w = CActor::conditions().MaxWalkWeight(); + max_w += GetAdditionalWeight(); + return max_w; +} + +#include "artifact.h" +#include "CustomOutfit.h" + +float CActor::GetOutfitWeightBonus() const +{ + CCustomOutfit* outfit = GetOutfit(); + if (outfit) + { + return outfit->m_additional_weight; + } + return 0.f; +} + +float CActor::GetAdditionalWeight() const +{ + float res = CInventoryOwner::GetAdditionalWeight(); + res += conditions().m_fBoostersAddWeight; + + return res; +} diff --git a/src/xrGameLA/Actor_Network.cpp b/src/xrGameLA/Actor_Network.cpp new file mode 100644 index 000000000..1870d1b15 --- /dev/null +++ b/src/xrGameLA/Actor_Network.cpp @@ -0,0 +1,1977 @@ +#include "pch_script.h" +#include "actor.h" +#include "Actor_Flags.h" +#include "inventory.h" +#include "xrserver_objects_alife_monsters.h" +#include "xrServer.h" + +#include "CameraLook.h" +#include "CameraFirstEye.h" + +#include "ActorEffector.h" + +#include "PHWorld.h" +#include "level.h" +#include "xr_level_controller.h" +#include "game_cl_base.h" +#include "infoportion.h" +#include "alife_registry_wrappers.h" +#include "../Include/xrRender/Kinematics.h" +#include "client_spawn_manager.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "CharacterPhysicsSupport.h" +#include "Grenade.h" +#include "WeaponMagazined.h" +#include "CustomOutfit.h" +#include "UIGameCustom.h" + +#include "actor_anim_defs.h" + +#include "map_manager.h" +#include "HUDManager.h" +#include "ui/UIArtefactPanel.h" +#include "ui/UIMainIngameWnd.h" +#include "gamepersistent.h" +#include "game_object_space.h" +#include "GameTaskManager.h" +#include "game_base_kill_type.h" +#include "holder_custom.h" +#include "actor_memory.h" +#include "actor_statistic_mgr.h" +#include "characterphysicssupport.h" +#include "game_cl_base_weapon_usage_statistic.h" +#include "clsid_game.h" +#include "../xr_collide_form.h" +#include "artifact.h" + +#ifdef DEBUG +# include "debug_renderer.h" +#endif + +int g_cl_InterpolationType = 0; +u32 g_cl_InterpolationMaxPoints = 0; +CActor* g_actor = NULL; + +CActor* Actor() +{ + VERIFY (g_actor); + if (GameID() != GAME_SINGLE) + VERIFY (g_actor == Level().CurrentControlEntity()); + return (g_actor); +}; + +//-------------------------------------------------------------------- +void CActor::ConvState(u32 mstate_rl, string128 *buf) +{ + xr_strcpy(*buf,""); + if (isActorAccelerated(mstate_rl, IsZoomAimingMode())) xr_strcat(*buf,"Accel "); + if (mstate_rl&mcCrouch) xr_strcat(*buf,"Crouch "); + if (mstate_rl&mcFwd) xr_strcat(*buf,"Fwd "); + if (mstate_rl&mcBack) xr_strcat(*buf,"Back "); + if (mstate_rl&mcLStrafe) xr_strcat(*buf,"LStrafe "); + if (mstate_rl&mcRStrafe) xr_strcat(*buf,"RStrafe "); + if (mstate_rl&mcJump) xr_strcat(*buf,"Jump "); + if (mstate_rl&mcFall) xr_strcat(*buf,"Fall "); + if (mstate_rl&mcTurn) xr_strcat(*buf,"Turn "); + if (mstate_rl&mcLanding) xr_strcat(*buf,"Landing "); + if (mstate_rl&mcLLookout) xr_strcat(*buf,"LLookout "); + if (mstate_rl&mcRLookout) xr_strcat(*buf,"RLookout "); + if (m_bJumpKeyPressed) xr_strcat(*buf,"+Jumping "); +}; +//-------------------------------------------------------------------- +void CActor::net_Export (NET_Packet& P) // export to server +{ + //CSE_ALifeCreatureAbstract + u8 flags = 0; + P.w_float (GetfHealth()); + P.w_u32 (Level().timeServer()); + P.w_u8 (flags); + Fvector p = Position(); + P.w_vec3 (p);//Position()); + + P.w_float /*w_angle8*/ (angle_normalize(r_model_yaw)); //Device.vCameraDirection.getH());// + P.w_float /*w_angle8*/ (angle_normalize(unaffected_r_torso.yaw));//(r_torso.yaw); + P.w_float /*w_angle8*/ (angle_normalize(unaffected_r_torso.pitch));//(r_torso.pitch); + P.w_float /*w_angle8*/ (angle_normalize(unaffected_r_torso.roll));//(r_torso.roll); + P.w_u8 (u8(g_Team())); + P.w_u8 (u8(g_Squad())); + P.w_u8 (u8(g_Group())); + + //CSE_ALifeCreatureActor + u16 ms = (u16)(mstate_real & 0x0000ffff); + P.w_u16 (u16(ms)); + P.w_sdir (NET_SavedAccel); + Fvector v = character_physics_support()->movement()->GetVelocity(); + P.w_sdir (v); + P.w_float (g_Radiation()); + + P.w_u8 (u8(inventory().GetActiveSlot())); + + ///////////////////////////////////////////////// + u16 NumItems = PHGetSyncItemsNumber(); + + if (H_Parent() || (GameID() == GAME_SINGLE) || ((NumItems > 1) && OnClient())) + NumItems = 0; + + if (!g_Alive()) NumItems = 0; + + P.w_u16 (NumItems); + if (!NumItems) return; + + if (g_Alive()) + { + SPHNetState State; + + CPHSynchronize* pSyncObj = NULL; + pSyncObj = PHGetSyncItem(0); + pSyncObj->get_State(State); + + P.w_u8 ( State.enabled ); + + P.w_vec3 ( State.angular_vel); + P.w_vec3 ( State.linear_vel); + + P.w_vec3 ( State.force); + P.w_vec3 ( State.torque); + + P.w_vec3 ( State.position); + + P.w_float ( State.quaternion.x ); + P.w_float ( State.quaternion.y ); + P.w_float ( State.quaternion.z ); + P.w_float ( State.quaternion.w ); + } + else + { + net_ExportDeadBody(P); + }; +}; + +static void w_vec_q8(NET_Packet& P,const Fvector& vec,const Fvector& min,const Fvector& max) +{ + P.w_float_q8(vec.x,min.x,max.x); + P.w_float_q8(vec.y,min.y,max.y); + P.w_float_q8(vec.z,min.z,max.z); +} +static void r_vec_q8(NET_Packet& P,Fvector& vec,const Fvector& min,const Fvector& max) +{ + P.r_float_q8(vec.x,min.x,max.x); + P.r_float_q8(vec.y,min.y,max.y); + P.r_float_q8(vec.z,min.z,max.z); + + clamp(vec.x,min.x,max.x); + clamp(vec.y,min.y,max.y); + clamp(vec.z,min.z,max.z); +} +static void w_qt_q8(NET_Packet& P,const Fquaternion& q) +{ + //Fvector Q; + //Q.set(q.x,q.y,q.z); + //if(q.w<0.f) Q.invert(); + //P.w_float_q8(Q.x,-1.f,1.f); + //P.w_float_q8(Q.y,-1.f,1.f); + //P.w_float_q8(Q.z,-1.f,1.f); + /////////////////////////////////////////////////// + P.w_float_q8(q.x,-1.f,1.f); + P.w_float_q8(q.y,-1.f,1.f); + P.w_float_q8(q.z,-1.f,1.f); + P.w_float_q8(q.w,-1.f,1.f); + + /////////////////////////////////////////// + + + //P.w_float_q8(q.x,-1.f,1.f); + //P.w_float_q8(q.y,-1.f,1.f); + //P.w_float_q8(q.z,-1.f,1.f); + //P.w(sign()) +} +static void r_qt_q8(NET_Packet& P,Fquaternion& q) +{ + //// x^2 + y^2 + z^2 + w^2 = 1 + //P.r_float_q8(q.x,-1.f,1.f); + //P.r_float_q8(q.y,-1.f,1.f); + //P.r_float_q8(q.z,-1.f,1.f); + //float w2=1.f-q.x*q.x-q.y*q.y-q.z*q.z; + //w2=w2<0.f ? 0.f : w2; + //q.w=_sqrt(w2); + ///////////////////////////////////////////////////// + /////////////////////////////////////////////////// + P.r_float_q8(q.x,-1.f,1.f); + P.r_float_q8(q.y,-1.f,1.f); + P.r_float_q8(q.z,-1.f,1.f); + P.r_float_q8(q.w,-1.f,1.f); + + clamp(q.x,-1.f,1.f); + clamp(q.y,-1.f,1.f); + clamp(q.z,-1.f,1.f); + clamp(q.w,-1.f,1.f); +} + +#define F_MAX 3.402823466e+38F + +static void UpdateLimits (Fvector &p, Fvector& min, Fvector& max) +{ + if(p.xmax.x)max.x=p.x; + if(p.y>max.y)max.y=p.y; + if(p.z>max.z)max.z=p.z; + + for (int k=0; k<3; k++) + { + if (p[k]max[k]) + { + R_ASSERT2(0, "Fuck"); + UpdateLimits(p, min, max); + } + } +}; + +void CActor::net_ExportDeadBody (NET_Packet &P) +{ + ///////////////////////////// + Fvector min,max; + + min.set(F_MAX,F_MAX,F_MAX); + max.set(-F_MAX,-F_MAX,-F_MAX); + ///////////////////////////////////// + u16 bones_number = PHGetSyncItemsNumber(); + for(u16 i=0;iget_State(state); + + Fvector& p=state.position; + UpdateLimits (p, min, max); + + Fvector px =state.linear_vel; + px.div(10.0f); + px.add(state.position); + UpdateLimits (px, min, max); + }; + + P.w_u8(10); + P.w_vec3(min); + P.w_vec3(max); + + for(u16 i=0;iget_State(state); +// state.net_Save(P,min,max); + w_vec_q8(P,state.position,min,max); + w_qt_q8(P,state.quaternion); + + //--------------------------------- + Fvector px =state.linear_vel; + px.div(10.0f); + px.add(state.position); + w_vec_q8(P,px,min,max); + }; +}; + +void CActor::net_Import (NET_Packet& P) // import from server +{ + //----------------------------------------------- + net_Import_Base(P); + //----------------------------------------------- + + m_u16NumBones = P.r_u16(); + if (m_u16NumBones == 0) return; + //----------------------------------------------- + net_Import_Physic(P); + //----------------------------------------------- +}; + +void CActor::net_Import_Base ( NET_Packet& P) +{ + net_update N; + + u8 flags; + u16 tmp; + + //CSE_ALifeCreatureAbstract + float health; + P.r_float (health); + //----------- for E3 ----------------------------- + if (OnClient())SetfHealth(health); + //------------------------------------------------ + P.r_u32 (N.dwTimeStamp ); + //--------------------------------------------- + + //--------------------------------------------- + + P.r_u8 (flags ); + P.r_vec3 (N.p_pos ); + P.r_float /*r_angle8*/ (N.o_model ); + P.r_float /*r_angle8*/ (N.o_torso.yaw ); + P.r_float /*r_angle8*/ (N.o_torso.pitch); + P.r_float /*r_angle8*/ (N.o_torso.roll ); if (N.o_torso.roll > PI) N.o_torso.roll -= PI_MUL_2; + id_Team = P.r_u8(); + id_Squad = P.r_u8(); + id_Group = P.r_u8(); + + + //----------- for E3 ----------------------------- +// if (OnClient()) + //------------------------------------------------ + { +// if (OnServer() || Remote()) + if (Level().IsDemoPlay()) + { + unaffected_r_torso.yaw = N.o_torso.yaw; + unaffected_r_torso.pitch = N.o_torso.pitch; + unaffected_r_torso.roll = N.o_torso.roll; + + cam_Active()->yaw = -N.o_torso.yaw; + cam_Active()->pitch = N.o_torso.pitch; + }; + }; + + //CSE_ALifeCreatureTrader +// P.r_float (fDummy); +// m_dwMoney = P.r_u32(); + + //CSE_ALifeCreatureActor + P.r_u16 (tmp ); N.mstate = u32(tmp); + P.r_sdir (N.p_accel ); + P.r_sdir (N.p_velocity ); + float fRRadiation; + P.r_float (fRRadiation); + //----------- for E3 ----------------------------- + if (OnClient()) + { +// fArmor = fRArmor; + SetfRadiation(fRRadiation); + }; + //------------------------------------------------ + + u8 ActiveSlot; + P.r_u8 (ActiveSlot); + //----------- for E3 ----------------------------- + if (OnClient()) + //------------------------------------------------ + { + if (ActiveSlot == 0xff) inventory().SetActiveSlot(NO_ACTIVE_SLOT); + else + { + if (inventory().GetActiveSlot() != u32(ActiveSlot)) + inventory().Activate(u32(ActiveSlot)); + }; + } + + //----------- for E3 ----------------------------- + if (Local() && OnClient()) return; + //------------------------------------------------- + if (!NET.empty() && N.dwTimeStamp < NET.back().dwTimeStamp) return; + + if (!NET.empty() && N.dwTimeStamp == NET.back().dwTimeStamp) + { + NET.back() = N; + } + else + { + NET.push_back (N); + if (NET.size()>5) NET.pop_front(); + } + //----------------------------------------------- + net_Import_Base_proceed (); + //----------------------------------------------- +}; + +void CActor::net_Import_Base_proceed ( ) +{ + if (g_Alive()) + { + setVisible ((BOOL)!HUDview ()); + setEnabled (TRUE); + }; + //--------------------------------------------- + + if (Remote()) return; + + net_update N = NET.back(); +}; + +void CActor::net_Import_Physic ( NET_Packet& P) +{ + m_States.clear(); + if (m_u16NumBones != 1) + { + Fvector min, max; + + P.r_u8(); + P.r_vec3(min); + P.r_vec3(max); + + for (u16 i=0; iget_State(state); +// stateL.net_Load(P, min, max); + r_vec_q8(P, stateL.position, min, max); + r_qt_q8(P, stateL.quaternion); + //--------------------------------------- + r_vec_q8(P, stateL.linear_vel, min, max); + stateL.linear_vel.sub(stateL.position); + stateL.linear_vel.mul(10.0f); + //--------------------------------------- + state.position = stateL.position; + state.previous_position = stateL.position; + state.quaternion = stateL.quaternion; + state.previous_quaternion = stateL.quaternion; + state.linear_vel = stateL.linear_vel; + //--------------------------------------- + m_States.push_back(state); + }; + } + else + { + net_update_A N_A; + + P.r_u8 ( *((u8*)&(N_A.State.enabled)) ); + + P.r_vec3 ( N_A.State.angular_vel); + P.r_vec3 ( N_A.State.linear_vel); + + P.r_vec3 ( N_A.State.force); + P.r_vec3 ( N_A.State.torque); + + P.r_vec3 ( N_A.State.position); + + P.r_float ( N_A.State.quaternion.x ); + P.r_float ( N_A.State.quaternion.y ); + P.r_float ( N_A.State.quaternion.z ); + P.r_float ( N_A.State.quaternion.w ); + + if (!NET.empty()) + N_A.dwTimeStamp = NET.back().dwTimeStamp; + else + N_A.dwTimeStamp = Level().timeServer(); + + N_A.State.previous_position = N_A.State.position; + N_A.State.previous_quaternion = N_A.State.quaternion; + //----------- for E3 ----------------------------- + if (Local() && OnClient() || !g_Alive()) return; +// if (g_Alive() && (Remote() || OnServer())) + { + //----------------------------------------------- + if (!NET_A.empty() && N_A.dwTimeStamp < NET_A.back().dwTimeStamp) return; + if (!NET_A.empty() && N_A.dwTimeStamp == NET_A.back().dwTimeStamp) + { + NET_A.back() = N_A; + } + else + { + NET_A.push_back (N_A); + if (NET_A.size()>5) NET_A.pop_front(); + }; + + if (!NET_A.empty()) m_bInterpolate = true; + }; + } + //----------------------------------------------- + net_Import_Physic_proceed(); + //----------------------------------------------- +}; + +void CActor::net_Import_Physic_proceed ( ) +{ + Level().AddObject_To_Objects4CrPr(this); + CrPr_SetActivated(false); + CrPr_SetActivationStep(0); +}; + +BOOL CActor::net_Spawn (CSE_Abstract* DC) +{ + m_holder_id = ALife::_OBJECT_ID(-1); + m_feel_touch_characters = 0; + m_snd_noise = 0.0f; + m_sndShockEffector = NULL; + m_ScriptCameraDirection = NULL; +/* m_followers = NULL;*/ + if (m_pPhysicsShell) + { + m_pPhysicsShell->Deactivate(); + xr_delete(m_pPhysicsShell); + }; + //force actor to be local on server client + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeCreatureActor *E = smart_cast(e); + if (OnServer()) + { + E->s_flags.set(M_SPAWN_OBJECT_LOCAL, TRUE); + } + + if( TRUE == E->s_flags.test(M_SPAWN_OBJECT_LOCAL) && TRUE == E->s_flags.is(M_SPAWN_OBJECT_ASPLAYER)) + g_actor = this; + + VERIFY(m_pActorEffector == NULL); + m_pActorEffector = new CActorCameraManager(); + + // motions + m_bAnimTorsoPlayed = false; + m_current_legs_blend = 0; + m_current_jump_blend = 0; + m_current_legs.invalidate (); + m_current_torso.invalidate (); + m_current_head.invalidate (); + //------------------------------------- + // инициализация реестров, используемых актером + encyclopedia_registry->registry().init(ID()); + game_news_registry->registry().init(ID()); + + + if (!CInventoryOwner::net_Spawn(DC)) return FALSE; + if (!inherited::net_Spawn(DC)) return FALSE; + + CSE_ALifeTraderAbstract *pTA = smart_cast(e); + set_money (pTA->m_dwMoney, false); + + //убрать все артефакты с пояса + m_ArtefactsOnBelt.clear(); +//. if( TRUE == E->s_flags.test(M_SPAWN_OBJECT_LOCAL) && TRUE == E->s_flags.is(M_SPAWN_OBJECT_ASPLAYER)) +//. CurrentGameUI()->UIMainIngameWnd->m_artefactPanel->InitIcons(m_ArtefactsOnBelt); + + + ROS()->force_mode (IRender_ObjectSpecific::TRACE_ALL); + + m_pPhysics_support->in_NetSpawn (e); + character_physics_support()->movement()->ActivateBox (0); + + if(m_bOutBorder)character_physics_support()->movement()->setOutBorder(); + r_torso_tgt_roll = 0; + + r_model_yaw = E->o_torso.yaw; + r_torso.yaw = E->o_torso.yaw; + r_torso.pitch = E->o_torso.pitch; + r_torso.roll = 0.0f;//E->o_Angle.z; + + unaffected_r_torso.yaw = r_torso.yaw; + unaffected_r_torso.pitch= r_torso.pitch; + unaffected_r_torso.roll = r_torso.roll; + + cam_Set (eacFirstEye); + + cam_Active()->Set (-E->o_torso.yaw,E->o_torso.pitch,0);//E->o_Angle.z); + + // *** movement state - respawn + mstate_wishful = 0; + mstate_real = 0; + mstate_old = 0; + m_bJumpKeyPressed = FALSE; + + NET_SavedAccel.set (0,0,0); + NET_WasInterpolating = TRUE; + + setEnabled (E->s_flags.is(M_SPAWN_OBJECT_LOCAL)); + + Engine.Sheduler.Register (this,TRUE); + + if (!IsGameTypeSingle()) + { + setEnabled(TRUE); + } + + hit_slowmo = 0.f; + + OnChangeVisual(); + //---------------------------------- + m_bAllowDeathRemove = false; + +// m_bHasUpdate = false; + m_bInInterpolation = false; + m_bInterpolate = false; + +// if (GameID() != GAME_SINGLE) + { + processing_activate(); + } + +#ifdef DEBUG + LastPosS.clear(); + LastPosH.clear(); + LastPosL.clear(); +#endif +//* + +// if (OnServer())// && E->s_flags.is(M_SPAWN_OBJECT_LOCAL)) +/* + if (OnClient()) + { + if (!pStatGraph) + { + static g_Y = 0; + pStatGraph = xr_new(); + pStatGraph->SetRect(0, g_Y, Device.dwWidth, 100, 0xff000000, 0xff000000); + g_Y += 110; + if (g_Y > 700) g_Y = 100; + pStatGraph->SetGrid(0, 0.0f, 10, 1.0f, 0xff808080, 0xffffffff); + pStatGraph->SetMinMax(0, 10, 300); + pStatGraph->SetStyle(CStatGraph::stBar); + pStatGraph->AppendSubGraph(CStatGraph::stCurve); + pStatGraph->AppendSubGraph(CStatGraph::stCurve); + } + } +*/ + SetDefaultVisualOutfit(cNameVisual()); + ChangeVisual(m_DefaultVisualOutfit_legs); + m_bFirstEye = true; + + smart_cast(Visual())->CalculateBones(); + + //-------------------------------------------------------------- + inventory().SetPrevActiveSlot(NO_ACTIVE_SLOT); + + + //------------------------------------- + m_States.empty(); + //------------------------------------- + if (!g_Alive()) + { + mstate_wishful &= ~mcAnyMove; + mstate_real &= ~mcAnyMove; + IKinematicsAnimated* K= smart_cast(Visual()); + K->PlayCycle("death_init"); + + + //остановить звук тяжелого дыхания + m_HeavyBreathSnd.stop(); + } + + typedef CClientSpawnManager::CALLBACK_TYPE CALLBACK_TYPE; + CALLBACK_TYPE callback; + callback.bind (this,&CActor::on_requested_spawn); + m_holder_id = E->m_holderID; + if (E->m_holderID != ALife::_OBJECT_ID(-1)) + if(!g_dedicated_server) + Level().client_spawn_manager().add(E->m_holderID,ID(),callback); + //F + //------------------------------------------------------------- + m_iLastHitterID = u16(-1); + m_iLastHittingWeaponID = u16(-1); + m_s16LastHittedElement = -1; + m_bWasHitted = false; + m_dwILastUpdateTime = 0; + + if (IsGameTypeSingle()){ + Level().MapManager().AddMapLocation("actor_location",ID()); + Level().MapManager().AddMapLocation("actor_location_p",ID()); + + m_game_task_manager = new CGameTaskManager(); + GameTaskManager().initialize(ID()); + + m_statistic_manager = new CActorStatisticMgr(); + } + + + spatial.type |=STYPE_REACTTOSOUND; + psHUD_Flags.set(HUD_WEAPON_RT,TRUE); + + if (Level().IsDemoPlay() && OnClient()) + { + setLocal(FALSE); + }; + return TRUE; +} + +void CActor::net_Destroy () +{ + inherited::net_Destroy (); + + if (m_holder_id != ALife::_OBJECT_ID(-1)) + if(!g_dedicated_server) + Level().client_spawn_manager().remove (m_holder_id,ID()); + + delete_data (m_game_task_manager); + delete_data (m_statistic_manager); + + if(!g_dedicated_server) + Level().MapManager ().RemoveMapLocationByObjectID(ID()); + + CInventoryOwner::net_Destroy(); + cam_UnsetLadder(); + character_physics_support()->movement()->DestroyCharacter(); + if(m_pPhysicsShell) { + m_pPhysicsShell->Deactivate(); + xr_delete(m_pPhysicsShell); + }; + m_pPhysics_support->in_NetDestroy (); + + xr_delete (m_sndShockEffector); + xr_delete (m_ScriptCameraDirection); + xr_delete (pStatGraph); + xr_delete (m_pActorEffector); + pCamBobbing = NULL; + +#ifdef DEBUG + LastPosS.clear(); + LastPosH.clear(); + LastPosL.clear(); +#endif + + processing_deactivate(); + m_holder=NULL; + m_holderID=u16(-1); + + m_ArtefactsOnBelt.clear(); + if (Level().CurrentViewEntity() == this) + CurrentGameUI()->UIMainIngameWnd->m_artefactPanel->InitIcons(m_ArtefactsOnBelt); + + SetDefaultVisualOutfit(NULL); + SetDefaultVisualOutfit_legs(NULL); + + + if(g_actor == this) g_actor= NULL; + + Engine.Sheduler.Unregister (this); +} + +void CActor::net_Relcase (CObject* O) +{ + + VERIFY(O); + CGameObject* GO = smart_cast(O); + if(GO&&m_pObjectWeLookingAt==GO){ + m_pObjectWeLookingAt=NULL; + } + CHolderCustom* HC=smart_cast(GO); + if(HC&&HC==m_pHolderWeLookingAt){ + m_pHolderWeLookingAt=NULL; + } + if(HC&&HC==m_holder) + { + m_holder->detach_Actor(); + m_holder=NULL; + } + inherited::net_Relcase (O); + + if (!g_dedicated_server) + memory().remove_links(O); + m_pPhysics_support->in_NetRelcase(O); +} + +BOOL CActor::net_Relevant () // relevant for export to server +{ + if (OnServer()) + { + return getSVU() | getLocal(); + } + else + { + return Local() & g_Alive(); + }; +}; + +void CActor::SetCallbacks() +{ + IKinematics* V = smart_cast(Visual()); + VERIFY (V); + u16 spine0_bone = V->LL_BoneID("bip01_spine"); + u16 spine1_bone = V->LL_BoneID("bip01_spine1"); + u16 shoulder_bone = V->LL_BoneID("bip01_spine2"); + u16 head_bone = V->LL_BoneID("bip01_head"); + V->LL_GetBoneInstance(u16(spine0_bone)).set_callback (bctCustom,Spin0Callback,this); + V->LL_GetBoneInstance(u16(spine1_bone)).set_callback (bctCustom,Spin1Callback,this); + V->LL_GetBoneInstance(u16(shoulder_bone)).set_callback (bctCustom,ShoulderCallback,this); + V->LL_GetBoneInstance(u16(head_bone)).set_callback (bctCustom,HeadCallback,this); +} +void CActor::ResetCallbacks() +{ + IKinematics* V = smart_cast(Visual()); + VERIFY (V); + u16 spine0_bone = V->LL_BoneID("bip01_spine"); + u16 spine1_bone = V->LL_BoneID("bip01_spine1"); + u16 shoulder_bone = V->LL_BoneID("bip01_spine2"); + u16 head_bone = V->LL_BoneID("bip01_head"); + V->LL_GetBoneInstance(u16(spine0_bone)).reset_callback (); + V->LL_GetBoneInstance(u16(spine1_bone)).reset_callback (); + V->LL_GetBoneInstance(u16(shoulder_bone)).reset_callback(); + V->LL_GetBoneInstance(u16(head_bone)).reset_callback (); +} + +void CActor::OnChangeVisual() +{ +/// inherited::OnChangeVisual(); + { + CPhysicsShell* tmp_shell=PPhysicsShell(); + PPhysicsShell()=NULL; + inherited::OnChangeVisual(); + PPhysicsShell()=tmp_shell; + tmp_shell=NULL; + } + + IKinematicsAnimated* V = smart_cast(Visual()); + if (V){ + SetCallbacks (); + m_anims->Create (V); + m_vehicle_anims->Create (V); + CDamageManager::reload(*cNameSect(),"damage",pSettings); + //------------------------------------------------------------------------------- + m_head = smart_cast(Visual())->LL_BoneID("bip01_head"); + m_r_hand = smart_cast(Visual())->LL_BoneID(pSettings->r_string(*cNameSect(),"weapon_bone0")); + m_l_finger1 = smart_cast(Visual())->LL_BoneID(pSettings->r_string(*cNameSect(),"weapon_bone1")); + m_r_finger2 = smart_cast(Visual())->LL_BoneID(pSettings->r_string(*cNameSect(),"weapon_bone2")); + //------------------------------------------------------------------------------- + m_neck = smart_cast(Visual())->LL_BoneID("bip01_neck"); + m_l_clavicle = smart_cast(Visual())->LL_BoneID("bip01_l_clavicle"); + m_r_clavicle = smart_cast(Visual())->LL_BoneID("bip01_r_clavicle"); + m_spine2 = smart_cast(Visual())->LL_BoneID("bip01_spine2"); + m_spine1 = smart_cast(Visual())->LL_BoneID("bip01_spine1"); + m_spine = smart_cast(Visual())->LL_BoneID("bip01_spine"); + //------------------------------------------------------------------------------- + reattach_items(); + //------------------------------------------------------------------------------- + m_pPhysics_support->in_ChangeVisual(); + //------------------------------------------------------------------------------- + SetCallbacks (); + //------------------------------------------------------------------------------- + m_current_head.invalidate (); + m_current_legs.invalidate (); + m_current_torso.invalidate (); + m_current_legs_blend = NULL; + m_current_torso_blend = NULL; + m_current_jump_blend = NULL; + } +}; + +void CActor::ChangeVisual ( shared_str NewVisual ) +{ + if (!NewVisual.size()) return; + if (cNameVisual().size() ) + { + if (cNameVisual() == NewVisual) return; + } + + cNameVisual_set(NewVisual); + + g_SetAnimation (mstate_real); + Visual()->dcast_PKinematics()->CalculateBones_Invalidate(); + Visual()->dcast_PKinematics()->CalculateBones(TRUE); + CStepManager::reload(*cNameSect()); +}; + +void ACTOR_DEFS::net_update::lerp(ACTOR_DEFS::net_update& A, ACTOR_DEFS::net_update& B, float f) +{ +// float invf = 1.f-f; +// // +// o_model = angle_lerp (A.o_model,B.o_model, f); +// o_torso.yaw = angle_lerp (A.o_torso.yaw,B.o_torso.yaw,f); +// o_torso.pitch = angle_lerp (A.o_torso.pitch,B.o_torso.pitch,f); +// o_torso.roll = angle_lerp (A.o_torso.roll,B.o_torso.roll,f); +// p_pos.lerp (A.p_pos,B.p_pos,f); +// p_accel = (f<0.5f)?A.p_accel:B.p_accel; +// p_velocity.lerp (A.p_velocity,B.p_velocity,f); +// mstate = (f<0.5f)?A.mstate:B.mstate; +// weapon = (f<0.5f)?A.weapon:B.weapon; +// fHealth = invf*A.fHealth+f*B.fHealth; +// fArmor = invf*A.fArmor+f*B.fArmor; +// weapon = (f<0.5f)?A.weapon:B.weapon; +} + +InterpData IStartT; +InterpData IRecT; +InterpData IEndT; + +void CActor::PH_B_CrPr () // actions & operations before physic correction-prediction steps +{ + //just set last update data for now +// if (!m_bHasUpdate) return; + if (CrPr_IsActivated()) return; + if (CrPr_GetActivationStep() > ph_world->m_steps_num) return; + + if (g_Alive()) + { + CrPr_SetActivated(true); + { + /////////////////////////////////////////////// + InterpData* pIStart = &IStart; + pIStart->Pos = Position(); + pIStart->Vel = character_physics_support()->movement()->GetVelocity(); + pIStart->o_model = angle_normalize(r_model_yaw); + pIStart->o_torso.yaw = angle_normalize(unaffected_r_torso.yaw); + pIStart->o_torso.pitch = angle_normalize(unaffected_r_torso.pitch); + pIStart->o_torso.roll = angle_normalize(unaffected_r_torso.roll); + if (pIStart->o_torso.roll > PI) + pIStart->o_torso.roll -= PI_MUL_2; + } + /////////////////////////////////////////////// + CPHSynchronize* pSyncObj = NULL; + pSyncObj = PHGetSyncItem(0); + if (!pSyncObj) return; + pSyncObj->get_State(LastState); + /////////////////////////////////////////////// + + //----------- for E3 ----------------------------- + if (Local() && OnClient()) + //------------------------------------------------ + { + PHUnFreeze(); + + pSyncObj->set_State(NET_A.back().State); + } + else + { + net_update_A N_A = NET_A.back(); + net_update N = NET.back(); + + NET_Last = N; + /////////////////////////////////////////////// + cam_Active()->Set (-unaffected_r_torso.yaw,unaffected_r_torso.pitch, 0);//, unaffected_r_torso.roll); // set's camera orientation + if (!N_A.State.enabled) + { + pSyncObj->set_State(N_A.State); + } + else + { + PHUnFreeze(); + + pSyncObj->set_State(N_A.State); + + g_Physics(N.p_accel, 0.0f, 0.0f); + Position().set(IStart.Pos); + }; + }; + } + else + { + if (PHGetSyncItemsNumber() != m_u16NumBones || m_States.empty()) return; + CrPr_SetActivated(true); + + PHUnFreeze(); + + for (u16 i=0; iget_State(state); + stateL = m_States[i]; + //--------------------------------------- + state.position = stateL.position; + state.previous_position = stateL.previous_position; + state.quaternion = stateL.quaternion; + state.previous_quaternion = stateL.previous_quaternion; + state.linear_vel = stateL.linear_vel; + state.enabled = true; + //--------------------------------------- + PHGetSyncItem(i)->set_State(state); + }; + }; +}; + + +void CActor::PH_I_CrPr () // actions & operations between two phisic prediction steps +{ + //store recalculated data, then we able to restore it after small future prediction +// if (!m_bHasUpdate) return; + if (!CrPr_IsActivated()) return; + if (g_Alive()) + { + //////////////////////////////////// + CPHSynchronize* pSyncObj = NULL; + pSyncObj = PHGetSyncItem(0); + if (!pSyncObj) return; + //////////////////////////////////// + pSyncObj->get_State(RecalculatedState); + //////////////////////////////////// + }; +}; + +void CActor::PH_A_CrPr () +{ + //restore recalculated data and get data for interpolation +// if (!m_bHasUpdate) return; +// m_bHasUpdate = false; + if (!CrPr_IsActivated()) return; + if (!g_Alive()) return; + //////////////////////////////////// + CPHSynchronize* pSyncObj = NULL; + pSyncObj = PHGetSyncItem(0); + if (!pSyncObj) return; + //////////////////////////////////// + pSyncObj->get_State(PredictedState); + //////////////////////////////////// + pSyncObj->set_State(RecalculatedState); + //////////////////////////////////// + if (!m_bInterpolate) return; + //////////////////////////////////// + mstate_wishful = mstate_real = NET_Last.mstate; + CalculateInterpolationParams(); +}; +extern float g_cl_lvInterp; + +void CActor::CalculateInterpolationParams() +{ + // Fmatrix xformX0, xformX1; + CPHSynchronize* pSyncObj = NULL; + pSyncObj = PHGetSyncItem(0); + /////////////////////////////////////////////// + InterpData* pIStart = &IStart; + InterpData* pIRec = &IRec; + InterpData* pIEnd = &IEnd; + + /////////////////////////////////////////////// + /* + pIStart->Pos = Position(); + pIStart->Vel = m_PhysicMovementControl->GetVelocity(); + pIStart->o_model = r_model_yaw; + pIStart->o_torso.yaw = unaffected_r_torso.yaw; + pIStart->o_torso.pitch = unaffected_r_torso.pitch; + pIStart->o_torso.roll = unaffected_r_torso.roll; + */ + ///////////////////////////////////////////////////////////////////// + pIRec->Pos = RecalculatedState.position; + pIRec->Vel = RecalculatedState.linear_vel; + pIRec->o_model = NET_Last.o_model; + pIRec->o_torso = NET_Last.o_torso; + ///////////////////////////////////////////////////////////////////// + pIEnd->Pos = PredictedState.position; + pIEnd->Vel = PredictedState.linear_vel; + pIEnd->o_model = pIRec->o_model ; + pIEnd->o_torso.yaw = pIRec->o_torso.yaw ; + pIEnd->o_torso.pitch = pIRec->o_torso.pitch ; + pIEnd->o_torso.roll = pIRec->o_torso.roll ; + ///////////////////////////////////////////////////////////////////// +// Msg("from %f, to %f", IStart.o_torso.yaw/PI*180.0f, IEnd.o_torso.yaw/PI*180.0f); + ///////////////////////////////////////////////////////////////////// + Fvector SP0, SP1, SP2, SP3; + Fvector HP0, HP1, HP2, HP3; + + SP0 = pIStart->Pos; + HP0 = pIStart->Pos; + + if (m_bInInterpolation) + { + u32 CurTime = Level().timeServer(); + float factor = float(CurTime - m_dwIStartTime)/(m_dwIEndTime - m_dwIStartTime); + if (factor > 1.0f) factor = 1.0f; + + float c = factor; + for (u32 k=0; k<3; k++) + { + SP0[k] = c*(c*(c*SCoeff[k][0]+SCoeff[k][1])+SCoeff[k][2])+SCoeff[k][3]; + SP1[k] = (c*c*SCoeff[k][0]*3+c*SCoeff[k][1]*2+SCoeff[k][2])/3; // сокрость из формулы в 3 раза превышает скорость при расчете коэффициентов !!!! + + HP0[k] = c*(c*(c*HCoeff[k][0]+HCoeff[k][1])+HCoeff[k][2])+HCoeff[k][3]; + HP1[k] = (c*c*HCoeff[k][0]*3+c*HCoeff[k][1]*2+HCoeff[k][2]); // сокрость из формулы в 3 раза превышает скорость при расчете коэффициентов !!!! + }; + + SP1.add(SP0); + } + else + { + if (LastState.linear_vel.x == 0 && LastState.linear_vel.y == 0 && LastState.linear_vel.z == 0) + { + HP1.sub(RecalculatedState.position, RecalculatedState.previous_position); + } + else + { + HP1.sub(LastState.position, LastState.previous_position); + }; + HP1.mul(1.0f/fixed_step); + SP1.add(HP1, SP0); + } + HP2.sub(PredictedState.position, PredictedState.previous_position); + HP2.mul(1.0f/fixed_step); + SP2.sub(PredictedState.position, HP2); + + SP3.set(PredictedState.position); + HP3.set(PredictedState.position); + /* + { + Fvector d0, d1; + d0.sub(SP1, SP0); + d1.sub(SP3, SP0); + float res = d0.dotproduct(d1); + if (res < 0) + { + Msg ("! %f", res); + } + else + Msg ("%f", res); + } + */ + ///////////////////////////////////////////////////////////////////////////// + Fvector TotalPath; + TotalPath.sub(SP3, SP0); + float TotalLen = TotalPath.magnitude(); + + SPHNetState State0 = (NET_A.back()).State; + SPHNetState State1 = PredictedState; + + float lV0 = State0.linear_vel.magnitude(); + float lV1 = State1.linear_vel.magnitude(); + + u32 ConstTime = u32((fixed_step - ph_world->m_frame_time)*1000)+ Level().GetInterpolationSteps()*u32(fixed_step*1000); + + m_dwIStartTime = m_dwILastUpdateTime; + +// if (( lV0 + lV1) > 0.000001 && g_cl_lvInterp == 0) + { +// u32 CulcTime = iCeil(TotalLen*2000/( lV0 + lV1)); +// m_dwIEndTime = m_dwIStartTime + min(CulcTime, ConstTime); + } +// else + m_dwIEndTime = m_dwIStartTime + ConstTime; + ///////////////////////////////////////////////////////////////////////////// + Fvector V0, V1; + // V0.sub(SP1, SP0); + // V1.sub(SP3, SP2); + V0.set(HP1); + V1.set(HP2); + lV0 = V0.magnitude(); + lV1 = V1.magnitude(); + + if (TotalLen != 0) + { + if (V0.x != 0 || V0.y != 0 || V0.z != 0) + { + if (lV0 > TotalLen/3) + { + HP1.normalize(); + // V0.normalize(); + // V0.mul(TotalLen/3); + HP1.normalize(); + HP1.mul(TotalLen/3); + SP1.add(HP1, SP0); + } + } + + if (V1.x != 0 || V1.y != 0 || V1.z != 0) + { + if (lV1 > TotalLen/3) + { + // V1.normalize(); + // V1.mul(TotalLen/3); + HP2.normalize(); + HP2.mul(TotalLen/3); + SP2.sub(SP3, HP2); + }; + } + }; + ///////////////////////////////////////////////////////////////////////////// + for( u32 i =0; i<3; i++) + { + SCoeff[i][0] = SP3[i] - 3*SP2[i] + 3*SP1[i] - SP0[i]; + SCoeff[i][1] = 3*SP2[i] - 6*SP1[i] + 3*SP0[i]; + SCoeff[i][2] = 3*SP1[i] - 3*SP0[i]; + SCoeff[i][3] = SP0[i]; + + HCoeff[i][0] = 2*HP0[i] - 2*HP3[i] + HP1[i] + HP2[i]; + HCoeff[i][1] = -3*HP0[i] + 3*HP3[i] - 2*HP1[i] - HP2[i]; + HCoeff[i][2] = HP1[i]; + HCoeff[i][3] = HP0[i]; + }; + ///////////////////////////////////////////////////////////////////////////// + m_bInInterpolation = true; + + if (m_pPhysicsShell) m_pPhysicsShell->NetInterpolationModeON(); +} + +int actInterpType = 0; +void CActor::make_Interpolation () +{ + m_dwILastUpdateTime = Level().timeServer(); + + if(g_Alive() && m_bInInterpolation) + { + u32 CurTime = m_dwILastUpdateTime; + + if (CurTime >= m_dwIEndTime) + { + m_bInInterpolation = false; + mstate_real = mstate_wishful = NET_Last.mstate; + NET_SavedAccel = NET_Last.p_accel; + + CPHSynchronize* pSyncObj = NULL; + pSyncObj = PHGetSyncItem(0); + if (!pSyncObj) return; + pSyncObj->set_State(PredictedState);//, PredictedState.enabled); + VERIFY2 (_valid(renderable.xform),*cName()); + } + else + { + float factor = 0.0f; + + if (m_dwIEndTime != m_dwIStartTime) + factor = float(CurTime - m_dwIStartTime)/(m_dwIEndTime - m_dwIStartTime); + + Fvector NewPos; + NewPos.lerp(IStart.Pos, IEnd.Pos, factor); + + VERIFY2 (_valid(renderable.xform),*cName()); + +// r_model_yaw = angle_lerp (IStart.o_model,IEnd.o_model, factor); + unaffected_r_torso.yaw = angle_lerp (IStart.o_torso.yaw,IEnd.o_torso.yaw,factor); + unaffected_r_torso.pitch = angle_lerp (IStart.o_torso.pitch,IEnd.o_torso.pitch,factor); + unaffected_r_torso.roll = angle_lerp (IStart.o_torso.roll,IEnd.o_torso.roll,factor); + + for (u32 k=0; k<3; k++) + { + IPosL[k] = NewPos[k]; + IPosS[k] = factor*(factor*(factor*SCoeff[k][0]+SCoeff[k][1])+SCoeff[k][2])+SCoeff[k][3]; + IPosH[k] = factor*(factor*(factor*HCoeff[k][0]+HCoeff[k][1])+HCoeff[k][2])+HCoeff[k][3]; + }; + + Fvector SpeedVector, ResPosition; + switch (g_cl_InterpolationType) + { + case 0: + { + ResPosition.set(IPosL); + SpeedVector.sub(IEnd.Pos, IStart.Pos); + SpeedVector.div(float(m_dwIEndTime - m_dwIStartTime)/1000.0f); + }break; + case 1: + { + for (int k=0; k<3; k++) + SpeedVector[k] = (factor*factor*SCoeff[k][0]*3+factor*SCoeff[k][1]*2+SCoeff[k][2])/3; // сокрость из формулы в 3 раза превышает скорость при расчете коэффициентов !!!! + + ResPosition.set(IPosS); + }break; + case 2: + { + for (int k=0; k<3; k++) + SpeedVector[k] = (factor*factor*HCoeff[k][0]*3+factor*HCoeff[k][1]*2+HCoeff[k][2]); + + ResPosition.set(IPosH); + }break; + default: + { + R_ASSERT2(0, "Unknown interpolation curve type!"); + } + } + character_physics_support()->movement()->SetPosition (ResPosition); + character_physics_support()->movement()->SetVelocity (SpeedVector); + cam_Active()->Set (-unaffected_r_torso.yaw,unaffected_r_torso.pitch, 0);//, unaffected_r_torso.roll); + }; + } + else + { + m_bInInterpolation = false; + }; + +#ifdef DEBUG + if (getVisible() && g_Alive() && mstate_real) + { + LastPosS.push_back(IPosS); while (LastPosS.size()>g_cl_InterpolationMaxPoints) LastPosS.pop_front(); + LastPosH.push_back(IPosH); while (LastPosH.size()>g_cl_InterpolationMaxPoints) LastPosH.pop_front(); + LastPosL.push_back(IPosL); while (LastPosL.size()>g_cl_InterpolationMaxPoints) LastPosL.pop_front(); + }; +#endif +}; +/* +void CActor::UpdatePosStack ( u32 Time0, u32 Time1 ) +{ + //******** Storing Last Position in stack ******** + CPHSynchronize* pSyncObj = NULL; + pSyncObj = PHGetSyncItem(0); + if (!pSyncObj) return; + + SPHNetState State; + pSyncObj->get_State(State); + + if (!SMemoryPosStack.empty() && SMemoryPosStack.back().u64WorldStep >= ph_world->m_steps_num) + { + xr_deque::iterator B = SMemoryPosStack.begin(); + xr_deque::iterator E = SMemoryPosStack.end(); + xr_deque::iterator I = std::lower_bound(B,E,u64(ph_world->m_steps_num-1)); + if (I != E) + { + I->SState = State; + I->u64WorldStep = ph_world->m_steps_num; + }; + } + else + { + SMemoryPosStack.push_back(SMemoryPos(Time0, Time1, ph_world->m_steps_num, State)); + if (SMemoryPosStack.front().dwTime0 < (Level().timeServer() - 2000)) SMemoryPosStack.pop_front(); + }; +}; + +ACTOR_DEFS::SMemoryPos* CActor::FindMemoryPos (u32 Time) +{ + if (SMemoryPosStack.empty()) return NULL; + + if (Time > SMemoryPosStack.back().dwTime1) return NULL; + + xr_deque::iterator B = SMemoryPosStack.begin(); + xr_deque::iterator E = SMemoryPosStack.end(); + xr_deque::iterator I = std::lower_bound(B,E,Time); + + if (I==E) return NULL; + + return &(*I); +}; +*/ + +void CActor::save(NET_Packet &output_packet) +{ + inherited::save(output_packet); + CInventoryOwner::save(output_packet); + output_packet.w_u8(u8(m_bOutBorder)); + output_packet.w_float(float(inventory().GetMaxWeight())); // skyloader: for skills +} + +void CActor::load(IReader &input_packet) +{ + inherited::load(input_packet); + CInventoryOwner::load(input_packet); + m_bOutBorder=!!(input_packet.r_u8()); + float fRMaxWeight = input_packet.r_float(); + inventory().SetMaxWeight (fRMaxWeight); +} + +#ifdef DEBUG + +extern Flags32 dbg_net_Draw_Flags; +void dbg_draw_piramid (Fvector pos, Fvector dir, float size, float xdir, u32 color) +{ + + Fvector p0, p1, p2, p3, p4; + p0.set(size, size, 0.0f); + p1.set(-size, size, 0.0f); + p2.set(-size, -size, 0.0f); + p3.set(size, -size, 0.0f); + p4.set(0, 0, size*4); + + bool Double = false; + Fmatrix t; t.identity(); + if (_valid(dir) && dir.square_magnitude()>0.01f) + { + t.k.normalize (dir); + Fvector::generate_orthonormal_basis(t.k, t.j, t.i); + } + else + { + t.rotateY(xdir); + Double = true; + } + t.c.set(pos); + +// Level().debug_renderer().draw_line(t, p0, p1, color); +// Level().debug_renderer().draw_line(t, p1, p2, color); +// Level().debug_renderer().draw_line(t, p2, p3, color); +// Level().debug_renderer().draw_line(t, p3, p0, color); + +// Level().debug_renderer().draw_line(t, p0, p4, color); +// Level().debug_renderer().draw_line(t, p1, p4, color); +// Level().debug_renderer().draw_line(t, p2, p4, color); +// Level().debug_renderer().draw_line(t, p3, p4, color); + + if (!Double) + { + DRender->dbg_DrawTRI(t, p0, p1, p4, color); + DRender->dbg_DrawTRI(t, p1, p2, p4, color); + DRender->dbg_DrawTRI(t, p2, p3, p4, color); + DRender->dbg_DrawTRI(t, p3, p0, p4, color); + } + else + { +// Fmatrix scale; +// scale.scale(0.8f, 0.8f, 0.8f); +// t.mulA_44(scale); +// t.c.set(pos); + + Level().debug_renderer().draw_line(t, p0, p1, color); + Level().debug_renderer().draw_line(t, p1, p2, color); + Level().debug_renderer().draw_line(t, p2, p3, color); + Level().debug_renderer().draw_line(t, p3, p0, color); + + Level().debug_renderer().draw_line(t, p0, p4, color); + Level().debug_renderer().draw_line(t, p1, p4, color); + Level().debug_renderer().draw_line(t, p2, p4, color); + Level().debug_renderer().draw_line(t, p3, p4, color); + }; +}; + +void CActor::OnRender_Network() +{ + DRender->OnFrameEnd(); + + //----------------------------------------------------------------------------------------------------- + float size = 0.2f; + +// dbg_draw_piramid(Position(), m_PhysicMovementControl->GetVelocity(), size/2, -r_model_yaw, color_rgba(255, 255, 255, 255)); + //----------------------------------------------------------------------------------------------------- + if (g_Alive()) + { + if (dbg_net_Draw_Flags.test(1<<8)) + { + Fvector bc; bc.add(Position(), m_AutoPickUp_AABB_Offset); + Fvector bd = m_AutoPickUp_AABB; + + Level().debug_renderer().draw_aabb (bc, bd.x, bd.y, bd.z, color_rgba(0, 255, 0, 255)); + }; + + IKinematics* V = smart_cast(Visual()); + if (dbg_net_Draw_Flags.test(1<<0) && V) + { + if (this != Level().CurrentViewEntity() || cam_active != eacFirstEye) + { + /* + u16 BoneCount = V->LL_BoneCount(); + for (u16 i=0; iLL_GetBox(i); + Fmatrix BoneMatrix; BoneOBB.xform_get(BoneMatrix); + Fmatrix BoneMatrixRes; BoneMatrixRes.mul(V->LL_GetTransform(i), BoneMatrix); + BoneMatrix.mul(XFORM(), BoneMatrixRes); + Level().debug_renderer().draw_obb(BoneMatrix, BoneOBB.m_halfsize, color_rgba(0, 255, 0, 255)); + }; + */ + CCF_Skeleton* Skeleton = smart_cast(collidable.model); + if (Skeleton){ + Skeleton->_dbg_refresh(); + + const CCF_Skeleton::ElementVec& Elements = Skeleton->_GetElements(); + for (CCF_Skeleton::ElementVec::const_iterator I=Elements.begin(); I!=Elements.end(); I++){ + if (!I->valid()) continue; + switch (I->type){ + case SBoneShape::stBox:{ + Fmatrix M; + M.invert (I->b_IM); + Fvector h_size = I->b_hsize; + Level().debug_renderer().draw_obb (M, h_size, color_rgba(0, 255, 0, 255)); + }break; + case SBoneShape::stCylinder:{ + Fmatrix M; + M.c.set (I->c_cylinder.m_center); + M.k.set (I->c_cylinder.m_direction); + Fvector h_size; + h_size.set (I->c_cylinder.m_radius,I->c_cylinder.m_radius,I->c_cylinder.m_height*0.5f); + Fvector::generate_orthonormal_basis(M.k,M.j,M.i); + Level().debug_renderer().draw_obb (M, h_size, color_rgba(0, 127, 255, 255)); + }break; + case SBoneShape::stSphere:{ + Fmatrix l_ball; + l_ball.scale (I->s_sphere.R, I->s_sphere.R, I->s_sphere.R); + l_ball.translate_add(I->s_sphere.P); + Level().debug_renderer().draw_ellipse(l_ball, color_rgba(0, 255, 0, 255)); + }break; + }; + }; + } + }; + }; + + if (!(dbg_net_Draw_Flags.is_any((1<<1)))) return; + + dbg_draw_piramid(Position(), character_physics_support()->movement()->GetVelocity(), size, -r_model_yaw, color_rgba(128, 255, 128, 255)); + dbg_draw_piramid(IStart.Pos, IStart.Vel, size, -IStart.o_model, color_rgba(255, 0, 0, 255)); +// Fvector tmp, tmp1; tmp1.set(0, .1f, 0); +// dbg_draw_piramid(tmp.add(IStartT.Pos, tmp1), IStartT.Vel, size, -IStartT.o_model, color_rgba(155, 0, 0, 155)); + dbg_draw_piramid(IRec.Pos, IRec.Vel, size, -IRec.o_model, color_rgba(0, 0, 255, 255)); +// dbg_draw_piramid(tmp.add(IRecT.Pos, tmp1), IRecT.Vel, size, -IRecT.o_model, color_rgba(0, 0, 155, 155)); + dbg_draw_piramid(IEnd.Pos, IEnd.Vel, size, -IEnd.o_model, color_rgba(0, 255, 0, 255)); +// dbg_draw_piramid(tmp.add(IEndT.Pos, tmp1), IEndT.Vel, size, -IEndT.o_model, color_rgba(0, 155, 0, 155)); + dbg_draw_piramid(NET_Last.p_pos, NET_Last.p_velocity, size*3/4, -NET_Last.o_model, color_rgba(255, 255, 255, 255)); + + Fmatrix MS, MH, ML, *pM = NULL; + ML.translate(0, 0.2f, 0); + MS.translate(0, 0.2f, 0); + MH.translate(0, 0.2f, 0); + + Fvector point0S, point1S, point0H, point1H, point0L, point1L, *ppoint0 = NULL, *ppoint1 = NULL; + Fvector tS, tH; + u32 cColor = 0, sColor = 0; + VIS_POSITION* pLastPos = NULL; + + switch (g_cl_InterpolationType) + { + case 0: ppoint0 = &point0L; ppoint1 = &point1L; cColor = color_rgba(0, 255, 0, 255); sColor = color_rgba(128, 255, 128, 255); pM = &ML; pLastPos = &LastPosL; break; + case 1: ppoint0 = &point0S; ppoint1 = &point1S; cColor = color_rgba(0, 0, 255, 255); sColor = color_rgba(128, 128, 255, 255); pM = &MS; pLastPos = &LastPosS; break; + case 2: ppoint0 = &point0H; ppoint1 = &point1H; cColor = color_rgba(255, 0, 0, 255); sColor = color_rgba(255, 128, 128, 255); pM = &MH; pLastPos = &LastPosH; break; + } + + //drawing path trajectory + float c = 0; + for (int i=0; i<11; i++) + { + c = float(i) * 0.1f; + for (u32 k=0; k<3; k++) + { + point1S[k] = c*(c*(c*SCoeff[k][0]+SCoeff[k][1])+SCoeff[k][2])+SCoeff[k][3]; + point1H[k] = c*(c*(c*HCoeff[k][0]+HCoeff[k][1])+HCoeff[k][2])+HCoeff[k][3]; + point1L[k] = IStart.Pos[k] + c*(IEnd.Pos[k]-IStart.Pos[k]); + }; + if (i!=0) + { + Level().debug_renderer().draw_line(*pM, *ppoint0, *ppoint1, cColor); + }; + point0S.set(point1S); + point0H.set(point1H); + point0L.set(point1L); + }; + + //drawing speed vectors + for (i=0; i<2; i++) + { + c = float(i); + for (u32 k=0; k<3; k++) + { + point1S[k] = c*(c*(c*SCoeff[k][0]+SCoeff[k][1])+SCoeff[k][2])+SCoeff[k][3]; + point1H[k] = c*(c*(c*HCoeff[k][0]+HCoeff[k][1])+HCoeff[k][2])+HCoeff[k][3]; + + tS[k] = (c*c*SCoeff[k][0]*3+c*SCoeff[k][1]*2+SCoeff[k][2])/3; // сокрость из формулы в 3 раза превышает скорость при расчете коэффициентов !!!! + tH[k] = (c*c*HCoeff[k][0]*3+c*HCoeff[k][1]*2+HCoeff[k][2]); + }; + + point0S.add(tS, point1S); + point0H.add(tH, point1H); + + if (g_cl_InterpolationType > 0) + { + Level().debug_renderer().draw_line(*pM, *ppoint0, *ppoint1, sColor); + } + } + + //draw interpolation history curve + if (!pLastPos->empty()) + { + Fvector Pos1, Pos2; + VIS_POSITION_it It = pLastPos->begin(); + Pos1 = *It; + for (; It != pLastPos->end(); It++) + { + Pos2 = *It; + + Level().debug_renderer().draw_line (*pM, Pos1, Pos2, cColor); + Level().debug_renderer().draw_aabb (Pos2, size/5, size/5, size/5, sColor); + Pos1 = *It; + }; + }; + + Fvector PH, PS; + PH.set(IPosH); PH.y += 1; + PS.set(IPosS); PS.y += 1; +// Level().debug_renderer().draw_aabb (PS, size, size, size, color_rgba(128, 128, 255, 255)); +// Level().debug_renderer().draw_aabb (PH, size, size, size, color_rgba(255, 128, 128, 255)); + ///////////////////////////////////////////////////////////////////////////////// + } + else + { + if (!(dbg_net_Draw_Flags.is_any((1<<1)))) return; + + IKinematics* V = smart_cast(Visual()); + if (dbg_net_Draw_Flags.test(1<<0) && V) + { + u16 BoneCount = V->LL_BoneCount(); + for (u16 i=0; iLL_GetBox(i); + Fmatrix BoneMatrix; BoneOBB.xform_get(BoneMatrix); + Fmatrix BoneMatrixRes; BoneMatrixRes.mul(V->LL_GetTransform(i), BoneMatrix); + BoneMatrix.mul(XFORM(), BoneMatrixRes); + Level().debug_renderer().draw_obb(BoneMatrix, BoneOBB.m_halfsize, color_rgba(0, 255, 0, 255)); + }; + }; + + if (!m_States.empty()) + { + u32 NumBones = m_States.size(); + for (u32 i=0; iget_State(state); + + Color = color_rgba(0, 255, 0, 255); + M = Fidentity; + M.rotation(state.quaternion); + M.translate_add(state.position); + Level().debug_renderer().draw_obb (M, half_dim, Color); + }; + } + else + { + if (!g_Alive() && PHGetSyncItemsNumber() > 2) + { + u16 NumBones = PHGetSyncItemsNumber(); + for (u16 i=0; iget_State(state); + + Fmatrix M; + M = Fidentity; + M.rotation(state.quaternion); + M.translate_add(state.position); + + Fvector half_dim; + half_dim.x = 0.2f; + half_dim.y = 0.1f; + half_dim.z = 0.1f; + + u32 Color = color_rgba(0, 255, 0, 255); + Level().debug_renderer().draw_obb (M, half_dim, Color); + }; + //----------------------------------------------------------------- + Fvector min,max; + + min.set(F_MAX,F_MAX,F_MAX); + max.set(-F_MAX,-F_MAX,-F_MAX); + ///////////////////////////////////// + for(u16 i=0;iget_State(state); + + Fvector& p=state.position; + UpdateLimits (p, min, max); + + Fvector px =state.linear_vel; + px.div(10.0f); + px.add(state.position); + UpdateLimits (px, min, max); + }; + + NET_Packet PX; + for(u16 i=0;iget_State(state); + + PX.B.count = 0; + w_vec_q8(PX,state.position,min,max); + w_qt_q8(PX,state.quaternion); +// w_vec_q8(PX,state.linear_vel,min,max); + + PX.r_pos = 0; + r_vec_q8(PX,state.position,min,max); + r_qt_q8(PX,state.quaternion); +// r_vec_q8(PX,state.linear_vel,min,max); + //=============================================== + Fmatrix M; + M = Fidentity; + M.rotation(state.quaternion); + M.translate_add(state.position); + + Fvector half_dim; + half_dim.x = 0.2f; + half_dim.y = 0.1f; + half_dim.z = 0.1f; + + u32 Color = color_rgba(255, 0, 0, 255); + Level().debug_renderer().draw_obb (M, half_dim, Color); + }; + Fvector LC, LS; + LC.add(min, max); LC.div(2.0f); + LS.sub(max, min); LS.div(2.0f); + + Level().debug_renderer().draw_aabb (LC, LS.x, LS.y, LS.z, color_rgba(255, 128, 128, 255)); + //----------------------------------------------------------------- + }; + } + } +}; + +#endif + +void CActor::net_Save(NET_Packet& P) +{ +#ifdef DEBUG + u32 pos; + Msg ("Actor net_Save"); + + pos = P.w_tell(); + inherited::net_Save (P); + Msg ("inherited::net_Save() : %d",P.w_tell() - pos); + + pos = P.w_tell(); + m_pPhysics_support->in_NetSave(P); + P.w_u16(m_holderID); + Msg ("m_pPhysics_support->in_NetSave() : %d",P.w_tell() - pos); +#else + inherited::net_Save (P); + m_pPhysics_support->in_NetSave(P); + P.w_u16(m_holderID); +#endif +} + +BOOL CActor::net_SaveRelevant() +{ + return TRUE; +} + +void CActor::Check_for_AutoPickUp() +{ + if (!psActorFlags.test(AF_AUTOPICKUP)) return; + if (GameID() == GAME_SINGLE) return; + if (Level().CurrentControlEntity() != this) return; + if (!g_Alive()) return; + + Fvector bc; bc.add(Position(), m_AutoPickUp_AABB_Offset); + Fbox APU_Box; + APU_Box.set(Fvector().sub(bc, m_AutoPickUp_AABB), Fvector().add(bc, m_AutoPickUp_AABB)); + + xr_vector ISpatialResult; + g_SpatialSpace->q_box (ISpatialResult,0,STYPE_COLLIDEABLE,bc,m_AutoPickUp_AABB); + + // Determine visibility for dynamic part of scene + for (u32 o_it=0; o_it (spatial->dcast_CObject ()); + if (0 == pIItem) continue; + if (!pIItem->CanTake()) continue; + if (Level().m_feel_deny.is_object_denied(pIItem->cast_game_object()) ) continue; + + CGrenade* pGrenade = smart_cast (pIItem); + if (pGrenade) continue; + + if (APU_Box.Pick(pIItem->object().Position(), pIItem->object().Position())) + { + NET_Packet P; + u_EventGen(P,GE_OWNERSHIP_TAKE, ID()); + P.w_u16(pIItem->object().ID()); + u_EventSend(P); + } + } +} + +void CActor::SetHitInfo (CObject* who, CObject* weapon, s16 element, Fvector Pos, Fvector Dir) +{ + m_iLastHitterID = (who!= NULL) ? who->ID() : u16(-1); + m_iLastHittingWeaponID = (weapon != NULL) ? weapon->ID() : u16(-1); + m_s16LastHittedElement = element; + m_fLastHealth = GetfHealth(); + m_bWasHitted = true; + m_vLastHitDir = Dir; + m_vLastHitPos = Pos; +}; + +void CActor::OnHitHealthLoss (float NewHealth) +{ + if (!m_bWasHitted) return; + if (GameID() == GAME_SINGLE || !OnServer()) return; + float fNewHealth = NewHealth; + m_bWasHitted = false; + + if (m_iLastHitterID != u16(-1)) + { + NET_Packet P; + u_EventGen (P,GE_GAME_EVENT,ID()); + P.w_u16(GAME_EVENT_PLAYER_HITTED); + P.w_u16(u16(ID()&0xffff)); + P.w_u16 (u16(m_iLastHitterID&0xffff)); + P.w_float(m_fLastHealth - fNewHealth); + u_EventSend(P); + } +}; + + +void CActor::OnCriticalHitHealthLoss () +{ + if (GameID() == GAME_SINGLE || !OnServer()) return; + + CObject* pLastHitter = Level().Objects.net_Find(m_iLastHitterID); + CObject* pLastHittingWeapon = Level().Objects.net_Find(m_iLastHittingWeaponID); + +#ifdef DEBUG + + + Msg("%s killed by hit from %s %s", + *cName(), + (pLastHitter ? *(pLastHitter->cName()) : ""), + ((pLastHittingWeapon && pLastHittingWeapon != pLastHitter) ? *(pLastHittingWeapon->cName()) : "")); +#endif + //------------------------------------------------------------------- + if (m_iLastHitterID != u16(-1)) + { + NET_Packet P; + u_EventGen (P,GE_GAME_EVENT,ID()); + P.w_u16(GAME_EVENT_PLAYER_HITTED); + P.w_u16(u16(ID()&0xffff)); + P.w_u16 (u16(m_iLastHitterID&0xffff)); + P.w_float(m_fLastHealth); + u_EventSend(P); + } + //------------------------------------------------------------------- + SPECIAL_KILL_TYPE SpecialHit = SKT_NONE; + if(pLastHittingWeapon) + { + if(pLastHittingWeapon->CLS_ID==CLSID_OBJECT_W_KNIFE) + SpecialHit = SKT_KNIFEKILL; + } + if (m_s16LastHittedElement > 0) + { + if (m_s16LastHittedElement == m_head) + { + CWeaponMagazined* pWeaponMagazined = smart_cast(pLastHittingWeapon); + if (pWeaponMagazined) + { + SpecialHit = SKT_HEADSHOT; + //------------------------------- + NET_Packet P; + u_EventGen(P, GEG_PLAYER_PLAY_HEADSHOT_PARTICLE, ID()); + P.w_s16(m_s16LastHittedElement); + P.w_dir(m_vLastHitDir); + P.w_vec3(m_vLastHitPos); + u_EventSend(P); + //------------------------------- + } + } + else + { + IKinematics* pKinematics = smart_cast(Visual()); + VERIFY (pKinematics); + u16 ParentBone = u16(m_s16LastHittedElement); + while (ParentBone) + { + ParentBone = pKinematics->LL_GetData(ParentBone).GetParentID(); + if (ParentBone && ParentBone == m_head) + { + SpecialHit = SKT_HEADSHOT; + break; + }; + } + }; + }; + //------------------------------- + if (m_bWasBackStabbed) SpecialHit = SKT_BACKSTAB; + //------------------------------- + NET_Packet P; + u_EventGen (P,GE_GAME_EVENT,ID()); + P.w_u16(GAME_EVENT_PLAYER_KILLED); + P.w_u16(u16(ID()&0xffff)); + P.w_u8 (KT_HIT); + P.w_u16 ((m_iLastHitterID) ? u16(m_iLastHitterID&0xffff) : 0); + P.w_u16 ((m_iLastHittingWeaponID && m_iLastHitterID != m_iLastHittingWeaponID) ? u16(m_iLastHittingWeaponID&0xffff) : 0); + P.w_u8 (u8(SpecialHit)); + u_EventSend(P); + //------------------------------------------- + if (GameID() != GAME_SINGLE) + Game().m_WeaponUsageStatistic->OnBullet_Check_Result(true); +}; + +void CActor::OnPlayHeadShotParticle(NET_Packet P) +{ + Fvector HitDir, HitPos; + s16 element = P.r_s16(); + P.r_dir(HitDir); HitDir.invert(); + P.r_vec3(HitPos); + //----------------------------------- + if (!m_sHeadShotParticle.size()) return; + Fmatrix pos; + CParticlesPlayer::MakeXFORM(this,element,HitDir,HitPos,pos); + // установить particles + CParticlesObject* ps = NULL; + + ps = CParticlesObject::Create(m_sHeadShotParticle.c_str(),TRUE); + + ps->UpdateParent(pos,Fvector().set(0.f,0.f,0.f)); + GamePersistent().ps_needtoplay.push_back(ps); +}; + +void CActor::OnCriticalWoundHealthLoss () +{ + if (GameID() == GAME_SINGLE || !OnServer()) return; +#ifdef DEBUG +/// Msg("%s is bleed out, thanks to %s", *cName(), (m_pLastHitter ? *(m_pLastHitter->cName()) : "")); +#endif + //------------------------------- + NET_Packet P; + u_EventGen (P,GE_GAME_EVENT,ID()); + P.w_u16(GAME_EVENT_PLAYER_KILLED); + P.w_u16(u16(ID()&0xffff)); + P.w_u8 (KT_BLEEDING); + P.w_u16 ((m_iLastHitterID) ? u16(m_iLastHitterID&0xffff) : 0); + P.w_u16 ((m_iLastHittingWeaponID && m_iLastHitterID != m_iLastHittingWeaponID) ? u16(m_iLastHittingWeaponID&0xffff) : 0); + P.w_u8 (SKT_NONE); + u_EventSend(P); +}; + +void CActor::OnCriticalRadiationHealthLoss () +{ + if (GameID() == GAME_SINGLE || !OnServer()) return; + //------------------------------- +// Msg("%s killed by radiation", *cName()); + NET_Packet P; + u_EventGen (P,GE_GAME_EVENT,ID()); + P.w_u16(GAME_EVENT_PLAYER_KILLED); + P.w_u16(u16(ID()&0xffff)); + P.w_u8 (KT_RADIATION); + P.w_u16 (0); + P.w_u16 (0); + P.w_u8 (SKT_NONE); + u_EventSend(P); +}; + +bool CActor::Check_for_BackStab_Bone (u16 element) +{ + if (element == m_head) return true; + else + if (element == m_neck) return true; + else + if (element == m_spine2) return true; + else + if (element == m_l_clavicle) return true; + else + if (element == m_r_clavicle) return true; + else + if (element == m_spine1) return true; + else + if (element == m_spine) return true; + return false; +} + +bool CActor::InventoryAllowSprint () +{ + PIItem pActiveItem = inventory().ActiveItem(); + if (pActiveItem && !pActiveItem->IsSprintAllowed()) + { + return false; + }; + PIItem pOutfitItem = inventory().ItemFromSlot(OUTFIT_SLOT); + if (pOutfitItem && !pOutfitItem->IsSprintAllowed()) + { + return false; + } + //tatarinrafa added sprint_allowed to artefacts + for (int it = 0; it < inventory().m_belt.size(); ++it) + { + CArtefact* artefact = smart_cast(inventory().m_belt[it]); + if (artefact && artefact->IsSprintAllowed() == false) + { + return false; + } + } + return true; +}; + +BOOL CActor::BonePassBullet (int boneID) +{ + if (GameID() == GAME_SINGLE) return inherited::BonePassBullet(boneID); + + CCustomOutfit* pOutfit = (CCustomOutfit*)inventory().m_slots[OUTFIT_SLOT].m_pIItem; + if(!pOutfit) + { + IKinematics* V = smart_cast(Visual()); VERIFY(V); + CBoneInstance &bone_instance = V->LL_GetBoneInstance(u16(boneID)); + return (bone_instance.get_param(3)> 0.5f); + } + return pOutfit->BonePassBullet(boneID); +} + +void CActor::On_B_NotCurrentEntity () +{ + inventory().Items_SetCurrentEntityHud(false); +}; \ No newline at end of file diff --git a/src/xrGameLA/Actor_Sleep.cpp b/src/xrGameLA/Actor_Sleep.cpp new file mode 100644 index 000000000..a27b824da --- /dev/null +++ b/src/xrGameLA/Actor_Sleep.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/src/xrGameLA/Actor_Weapon.cpp b/src/xrGameLA/Actor_Weapon.cpp new file mode 100644 index 000000000..2a97eeebc --- /dev/null +++ b/src/xrGameLA/Actor_Weapon.cpp @@ -0,0 +1,280 @@ +// Actor_Weapon.cpp: для работы с оружием +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "actor.h" +#include "actoreffector.h" +#include "Missile.h" +#include "inventory.h" +#include "weapon.h" +#include "map_manager.h" +#include "level.h" +#include "CharacterPhysicsSupport.h" +#include "EffectorShot.h" +#include "WeaponMagazined.h" +#include "Grenade.h" +#include "game_base_space.h" +#include "Artifact.h" +#include "weaponknife.h" + +static const float VEL_MAX = 10.f; +static const float VEL_A_MAX = 10.f; + +#define GetWeaponParam(pWeapon, func_name, def_value) ((pWeapon) ? (pWeapon->func_name) : def_value) + +//возвращает текуший разброс стрельбы (в радианах)с учетом движения +float CActor::GetWeaponAccuracy() const +{ + CWeapon* W = smart_cast(inventory().ActiveItem()); + + + if(m_bZoomAimingMode&&W&&!GetWeaponParam(W, IsRotatingToZoom(), false)) + return m_fDispAim; + + float dispersion = m_fDispBase*GetWeaponParam(W, Get_PDM_Base(), 1.0f); + + CEntity::SEntityState state; + if (g_State(state)) + { + // angular factor + dispersion *= (1.f + (state.fAVelocity/VEL_A_MAX)*m_fDispVelFactor*GetWeaponParam(W, Get_PDM_Vel_F(), 1.0f)); +// Msg("--- base=[%f] angular disp=[%f]",m_fDispBase, dispersion); + // linear movement factor + bool bAccelerated = isActorAccelerated(mstate_real, IsZoomAimingMode()); + if( bAccelerated ) + dispersion *= (1.f + (state.fVelocity/VEL_MAX)*m_fDispVelFactor*GetWeaponParam(W, Get_PDM_Vel_F(), 1.0f)*(1.f + m_fDispAccelFactor*GetWeaponParam(W, Get_PDM_Accel_F(), 1.0f))); + else + dispersion *= (1.f + (state.fVelocity/VEL_MAX)*m_fDispVelFactor*GetWeaponParam(W, Get_PDM_Vel_F(), 1.0f)); + + if (state.bCrouch){ + dispersion *= (1.f + m_fDispCrouchFactor*GetWeaponParam(W, Get_PDM_Crouch(), 1.0f)); + + if(!bAccelerated ) + dispersion *= (1.f + m_fDispCrouchNoAccelFactor*GetWeaponParam(W, Get_PDM_Crouch_NA(), 1.0f)); + } + } + + return dispersion; +} + + +void CActor::g_fireParams (const CHudItem* pHudItem, Fvector &fire_pos, Fvector &fire_dir) +{ + CWeapon* weapon = smart_cast(inventory().ActiveItem()); + CWeaponKnife* knife = smart_cast(weapon); // don't correct knife fire pos + + if ((weapon && eacLookAt == cam_active) || (weapon && !knife && eacFirstEye == cam_active && psActorFlags.test(AF_CORRECT_FIREPOS))) + fire_pos = weapon->get_LastFP(); + else + fire_pos = Cameras().Position(); + + fire_dir = Cameras().Direction(); + + const CMissile *pMissile = smart_cast (pHudItem); + if (pMissile) + { + Fvector offset; + XFORM().transform_dir(offset, m_vMissileOffset); + fire_pos.add(offset); + } +} + +void CActor::g_WeaponBones (int &L, int &R1, int &R2) +{ + R1 = m_r_hand; + R2 = m_r_finger2; + L = m_l_finger1; +} + +BOOL CActor::g_State (SEntityState& state) const +{ + state.bJump = !!(mstate_real&mcJump); + state.bCrouch = !!(mstate_real&mcCrouch); + state.bFall = !!(mstate_real&mcFall); + state.bSprint = !!(mstate_real&mcSprint); + state.fVelocity = character_physics_support()->movement()->GetVelocityActual(); + state.fAVelocity = fCurAVelocity; + return TRUE; +} + +void CActor::SetWeaponHideState (u32 State, bool bSet) +{ + if (g_Alive() && this == Level().CurrentControlEntity()) + { + NET_Packet P; + u_EventGen (P, GEG_PLAYER_WEAPON_HIDE_STATE, ID()); + P.w_u32 (State); + P.w_u8 (u8(bSet)); + u_EventSend (P); + }; +} +static u16 BestWeaponSlots [] = { + RIFLE_SLOT , // 2 + PISTOL_SLOT , // 1 + GRENADE_SLOT , // 3 + KNIFE_SLOT , // 0 +}; +void CActor::SelectBestWeapon (CObject* O) +{ + return; +} + +#define ENEMY_HIT_SPOT "mp_hit_sector_location" +BOOL g_bShowHitSectors = TRUE; + +void CActor::HitSector(CObject* who, CObject* weapon) +{ + if (!g_bShowHitSectors) return; + if (!g_Alive()) return; + + bool bShowHitSector = true; + + CEntityAlive* pEntityAlive = smart_cast(who); + + if (!pEntityAlive || this == who) bShowHitSector = false; + + if (weapon) + { + CWeapon* pWeapon = smart_cast (weapon); + if (pWeapon) + { + if (pWeapon->IsSilencerAttached()) + { + bShowHitSector = false; + if (pWeapon->IsGrenadeLauncherAttached()) + { + } + } + } + } + + if (!bShowHitSector) return; + Level().MapManager().AddMapLocation(ENEMY_HIT_SPOT, who->ID()); +} + +void CActor::on_weapon_shot_start (CWeapon *weapon) +{ + CWeaponMagazined* pWM = smart_cast (weapon); + + CWeapon::CameraRecoil& cam_recoil = IsZoomAimingMode() + ? weapon->zoom_cam_recoil + : weapon->cam_recoil; + + CCameraShotEffector *effector = smart_cast (Cameras().GetCamEffector(eCEShot)); + if (!effector) { + effector = + (CCameraShotEffector*)Cameras().AddCamEffector( + new CCameraShotEffector(cam_recoil.camMaxAngleVert, + cam_recoil.camRelaxSpeed, + cam_recoil.camMaxAngleHorz, + cam_recoil.camStepAngleHorz, + cam_recoil.camDispersionFrac)); + } + R_ASSERT (effector); + + if (pWM) + { + if (effector->IsSingleShot()) + update_camera(effector); + + if (pWM->GetCurrentFireMode() == 1) + { + effector->SetSingleShoot(TRUE); + } + else + { + effector->SetSingleShoot(FALSE); + } + }; + + effector->SetRndSeed (GetShotRndSeed()); + effector->SetActor (this); + effector->Shot((cam_recoil.camDispersion + cam_recoil.camDispersionInc*float(weapon->ShotsFired()))*m_fDispersionCoef); + + if (pWM) + { + if (pWM->GetCurrentFireMode() != 1) + { + effector->SetActive(FALSE); + update_camera(effector); + } + } +} + +void CActor::on_weapon_shot_stop (CWeapon *weapon) +{ + //--------------------------------------------- + CCameraShotEffector *effector = smart_cast(Cameras().GetCamEffector(eCEShot)); + if (effector && effector->IsActive()) + { + if (effector->IsSingleShot()) + update_camera(effector); + } + //--------------------------------------------- + Cameras().RemoveCamEffector(eCEShot); +} + +void CActor::on_weapon_hide (CWeapon *weapon) +{ + CCameraShotEffector *effector = smart_cast(Cameras().GetCamEffector(eCEShot)); + if (effector && !effector->IsActive()) + effector->Clear (); +} + +Fvector CActor::weapon_recoil_delta_angle () +{ + CCameraShotEffector *effector = smart_cast(Cameras().GetCamEffector(eCEShot)); + Fvector result = {0.f,0.f,0.f}; + + if (effector) + effector->GetDeltaAngle (result); + + return (result); +} + +Fvector CActor::weapon_recoil_last_delta() +{ + CCameraShotEffector *effector = smart_cast(Cameras().GetCamEffector(eCEShot)); + Fvector result = {0.f,0.f,0.f}; + + if (effector) + effector->GetLastDelta (result); + + return (result); +} +////////////////////////////////////////////////////////////////////////// + +void CActor::SpawnAmmoForWeapon (CInventoryItem *pIItem) +{ + if (OnClient()) return; + if (!pIItem) return; + + CWeaponMagazined* pWM = smart_cast (pIItem); + if (!pWM || !pWM->AutoSpawnAmmo()) return; + + pWM->SpawnAmmo(0xffffffff, NULL, ID()); +}; + +void CActor::RemoveAmmoForWeapon (CInventoryItem *pIItem) +{ + if (OnClient()) return; + if (!pIItem) return; + + CWeaponMagazined* pWM = smart_cast (pIItem); + if (!pWM || !pWM->AutoSpawnAmmo()) return; + + CWeaponAmmo* pAmmo = smart_cast(inventory().GetAny(*(pWM->m_ammoTypes[0]) )); + if (!pAmmo) return; + pAmmo->DestroyObject(); +}; + +bool CActor::IsReloadingWeapon() +{ + if (!inventory().ActiveItem()) + return false; + + CWeapon* wpn = inventory().ActiveItem()->cast_weapon(); + + return wpn && wpn->GetState() == CWeapon::eReload; +} diff --git a/src/xrGameLA/AdvancedAfDetector.cpp b/src/xrGameLA/AdvancedAfDetector.cpp new file mode 100644 index 000000000..9e8d99b8a --- /dev/null +++ b/src/xrGameLA/AdvancedAfDetector.cpp @@ -0,0 +1,324 @@ +#include "stdafx.h" +#include "advancedafdetector.h" +#include "ui/ArtefactDetectorUI.h" +//#include "../Include/xrRender/Kinematics.h" +//#include "../LightAnimLibrary.h" +#include "player_hud.h" +//#include "clsid_game.h" +#include "artifact.h" +//#include "ai\monsters\ai_monster_utils.h" + +CAdvancedDetector::CAdvancedDetector() +{ + +} + +CAdvancedDetector::~CAdvancedDetector() +{} + +void CAdvancedDetector::CreateUI() +{ + R_ASSERT(NULL == m_ui); + m_ui = new CUIArtefactDetectorAdv(); + ui().construct(this); + + if (for_test){ + Fvector P; + P.set(this->Position()); + feel_touch_update(P, fortestrangetocheck); + + + for (xr_vector::iterator it = feel_touch.begin(); it != feel_touch.end(); it++) + { + CObject* item = *it; + if (item){ + Fvector dir, to; + + CInventoryItem* invitem = smart_cast(item); + item->Center(to); + float range = dir.sub(to, this->Position()).magnitude(); + Msg("Artefact %s, distance is %f", invitem->object().cNameSect().c_str(), range); + } + } + } +} + +//------------------------------------Feel Touch--------------------------------------- +void CAdvancedDetector::feel_touch_new(CObject* O) +{ +} + +void CAdvancedDetector::feel_touch_delete(CObject* O) +{ + +} + +BOOL CAdvancedDetector::feel_touch_contact(CObject *O) +{ + + bool is_visibleforDetector = false; + CArtefact* artefact = smart_cast(O); + + if (artefact) + { + for (u16 id = 0; id < af_types.size(); ++id) + { + + if ((xr_strcmp(O->cNameSect(), af_types[id]) == 0) || (xr_strcmp(af_types[id], "all") == 0)) + { + return true; + } + } + return false; + } + else{ return false; } + + +} + +//---------------------------------------------------------------------------------------- + +void CAdvancedDetector::UpdateAf() +{ + ui().SetValue(0.0f, Fvector().set(0, 0, 0)); + + //------------Находим ближайший арт из списка feeltouch + CArtefact* pCurrentAf; + LPCSTR closest_art = "null"; + feel_touch_update_delay = feel_touch_update_delay + 1; + if (feel_touch_update_delay >= 5)//Как то снизить нагрузку на кадр поможет + { + feel_touch_update_delay = 0; + Fvector P; + P.set(this->Position()); + feel_touch_update(P, foverallrangetocheck); + } + + float disttoclosestart = 0.0; + + for (xr_vector::iterator it = feel_touch.begin(); it != feel_touch.end(); it++) + { + float disttoart = DetectorFeel(*it); + if (disttoart != -10.0) + { + //Если переменная досих пор не заданаа(первый проход цикла), то даем ей значение + if (disttoclosestart <= 0.0) + { + disttoclosestart = disttoart; + closest_art = closestart; + pCurrentAf = smart_cast(*it); + } + //нашли более близкий арт... + if (disttoclosestart > disttoart) + { + disttoclosestart = disttoart; + closest_art = closestart; + pCurrentAf = smart_cast(*it); + } + } + } + + if (!reaction_sound_off) + { + //определить текущую частоту срабатывания сигнала + if (disttoclosestart == 0.0) + { + return; + } + else + { + cur_periodperiod = disttoclosestart / (fdetect_radius * pCurrentAf->detect_radius_koef); + } + + //Чтобы не перегружать звук. движок + if (cur_periodperiod < 0.11) + { + cur_periodperiod = 0.11; + } + + if (snd_timetime > cur_periodperiod && detect_sndsnd_line || pCurrentAf->custom_detect_sound_string) + { + //Добавил врзможность задать разные звуки для различных артов + freqq = 1.8 - cur_periodperiod; + + if (freqq < 0.8) + freqq = 0.8; + + if (pCurrentAf->custom_detect_sound_string) + { + pCurrentAf->custom_detect_sound.play_at_pos(this, this->Position()); + pCurrentAf->custom_detect_sound.set_frequency(freqq); + } + else if (detect_sndsnd_line) + { + detect_snd.play_at_pos(this, this->Position()); + detect_snd.set_frequency(freqq); + } + + snd_timetime = 0; + } + else + snd_timetime += Device.fTimeDelta; + } + + if (!pCurrentAf) return; //на всякий случай + + //Находим направление и передаем его в УИ экрана + Fvector dir_to_artefact; + dir_to_artefact.sub(pCurrentAf->Position(), Device.vCameraPosition); + dir_to_artefact.normalize(); + float _ang_af = dir_to_artefact.getH(); + float _ang_cam = Device.vCameraDirection.getH(); + + float _diff = angle_difference_signed(_ang_af, _ang_cam); + + ui().SetValue(_diff, dir_to_artefact); +} + +//Просто для удобства вынес в отдельную функцию, а то и так месево там +float CAdvancedDetector::DetectorFeel(CObject* item) +{ + Fvector dir, to; + + item->Center(to); + float range = dir.sub(to, Position()).magnitude(); + CInventoryItem* invitem = smart_cast(item); + CArtefact* artefact = smart_cast(item); + + float gogogo = fdetect_radius * artefact->detect_radius_koef; + if (rangeobject().cNameSect_str(), invitem->object().cNameSect().c_str()); + closestart = invitem->object().cNameSect_str(); + return range; + } + return -10.0; +} + +//---------------UI(Экранчик направления)------------------ +CUIArtefactDetectorAdv& CAdvancedDetector::ui() +{ + return *((CUIArtefactDetectorAdv*)m_ui); +} + +void CUIArtefactDetectorAdv::construct(CAdvancedDetector* p) +{ + m_parent = p; + m_target_dir.set(0, 0, 0); + m_curr_ang_speed = 0.0f; + m_cur_y_rot = 0.0f; + m_bid = u16(-1); +} + +CUIArtefactDetectorAdv::~CUIArtefactDetectorAdv() +{ +} + +void CUIArtefactDetectorAdv::SetValue(const float val1, const Fvector& val2) +{ + m_target_dir = val2; +} + +void CUIArtefactDetectorAdv::update() +{ + if (NULL == m_parent->HudItemData() || m_bid == u16(-1)) return; + inherited::update(); + attachable_hud_item* itm = m_parent->HudItemData(); + R_ASSERT(itm); + + BOOL b_visible = !fis_zero(m_target_dir.magnitude()); + if (b_visible != itm->m_model->LL_GetBoneVisible(m_bid)) + itm->m_model->LL_SetBoneVisible(m_bid, b_visible, TRUE); + + if (!b_visible) + return; + + + Fvector dest; + Fmatrix Mi; + Mi.invert(itm->m_item_transform); + Mi.transform_dir(dest, m_target_dir); + + float dest_y_rot = -dest.getH(); + + + /* + m_cur_y_rot = angle_normalize_signed(m_cur_y_rot); + float diff = angle_difference_signed(m_cur_y_rot, dest_y_rot); + float a = (diff>0.0f)?-1.0f:1.0f; + + a *= 2.0f; + + m_curr_ang_speed = m_curr_ang_speed + a*Device.fTimeDelta; + clamp (m_curr_ang_speed,-2.0f,2.0f); + float _add = m_curr_ang_speed*Device.fTimeDelta; + + m_cur_y_rot += _add; + */ + m_cur_y_rot = angle_inertion_var(m_cur_y_rot, + dest_y_rot, + PI_DIV_4, + PI_MUL_4, + PI_MUL_2, + Device.fTimeDelta); + +} + +float CUIArtefactDetectorAdv::CurrentYRotation() const +{ + float one = PI_MUL_2 / 24.0f; + float ret = fmod(m_cur_y_rot, one); + return (m_cur_y_rot - ret); +} + +void CAdvancedDetector::on_a_hud_attach() +{ + inherited::on_a_hud_attach(); + ui().SetBoneCallbacks(); +} + +/* +void CAdvancedDetector::on_b_hud_detach() +{ + Msg("On a hud attach"); + inherited::on_b_hud_detach(); + //ui().ResetBoneCallbacks(); +} +*/ + +void CUIArtefactDetectorAdv::BoneCallback(CBoneInstance *B) +{ + CUIArtefactDetectorAdv *P = static_cast(B->callback_param()); + Fmatrix rY; + rY.rotateY(P->CurrentYRotation()); + B->mTransform.mulB_43(rY); +} + +void CUIArtefactDetectorAdv::SetBoneCallbacks() +{ + attachable_hud_item* itm = m_parent->HudItemData(); + R_ASSERT(itm); + m_bid = itm->m_model->LL_BoneID("screen_bone"); + + CBoneInstance& bi = itm->m_model->LL_GetBoneInstance(m_bid); + bi.set_callback(bctCustom, BoneCallback, this); + + float p, b; + bi.mTransform.getHPB(m_cur_y_rot, p, b); +} + +/* +void CUIArtefactDetectorAdv::ResetBoneCallbacks() +{ + Msg("On a hud attach1"); + attachable_hud_item* itm = m_parent->HudItemData(); + R_ASSERT(itm); + Msg("On a hud attach2"); + u16 bid = itm->m_model->LL_BoneID("screen_bone"); + + Msg("On a hud attach3"); + CBoneInstance& bi = itm->m_model->LL_GetBoneInstance(bid); + Msg("On a hud attach4"); + bi.reset_callback(); +} +*/ diff --git a/src/xrGameLA/AdvancedAfDetector.h b/src/xrGameLA/AdvancedAfDetector.h new file mode 100644 index 000000000..2b3aaf0aa --- /dev/null +++ b/src/xrGameLA/AdvancedAfDetector.h @@ -0,0 +1,30 @@ +#pragma once +#include "CustomDetector2.h" +#include "../feel_touch.h" + +class CUIArtefactDetectorAdv; + +class CAdvancedDetector :public CCustomDetectorR, + public Feel::Touch +{ + typedef CCustomDetectorR inherited; + +public: + CAdvancedDetector(); + virtual ~CAdvancedDetector(); + + virtual void feel_touch_new(CObject* O); + virtual void feel_touch_delete(CObject* O); + virtual BOOL feel_touch_contact(CObject* O); + + virtual void on_a_hud_attach(); + //virtual void on_b_hud_detach(); + + float DetectorFeel(CObject* object); + +protected: + virtual void UpdateAf(); + virtual void CreateUI(); + CUIArtefactDetectorAdv& ui(); + +}; diff --git a/src/xrGameLA/AmebaZone.cpp b/src/xrGameLA/AmebaZone.cpp new file mode 100644 index 000000000..4bffec7eb --- /dev/null +++ b/src/xrGameLA/AmebaZone.cpp @@ -0,0 +1,121 @@ +#include "stdafx.h" +#include "CustomZone.h" +#include "../../Include/xrRender/KinematicsAnimated.h" +#include "ZoneVisual.h" +#include "PHObject.h" +#include "PHMovementControl.h" +#include "AmebaZone.h" +#include "hudmanager.h" +#include "level.h" +#include "entity_alive.h" +#include "CharacterPhysicsSupport.h" +#include "../xr_collide_form.h" + +CAmebaZone::CAmebaZone() +{ + m_fVelocityLimit=1.f; +} + +CAmebaZone::~CAmebaZone() +{ + +} +void CAmebaZone::Load(LPCSTR section) +{ + inherited::Load(section); + m_fVelocityLimit= pSettings->r_float(section, "max_velocity_in_zone"); +} +bool CAmebaZone::BlowoutState() +{ + bool result = inherited::BlowoutState(); + if(!result) UpdateBlowout(); + + for(OBJECT_INFO_VEC_IT it = m_ObjectInfoMap.begin(); m_ObjectInfoMap.end() != it; ++it) + Affect(&(*it)); + + return result; +} + +void CAmebaZone::Affect(SZoneObjectInfo* O) +{ + CPhysicsShellHolder *pGameObject = smart_cast(O->object); + if(!pGameObject) return; + + if(O->zone_ignore) return; + +#ifdef DEBUG + char l_pow[255]; + xr_sprintf(l_pow, "zone hit. %.1f", Power(distance_to_center(O->object))); + if(bDebug) Msg("%s %s",*pGameObject->cName(), l_pow); +#endif + Fvector hit_dir; + hit_dir.set(::Random.randF(-.5f,.5f), + ::Random.randF(.0f,1.f), + ::Random.randF(-.5f,.5f)); + hit_dir.normalize(); + + + Fvector position_in_bone_space; + + float power = Power(distance_to_center(O->object)); + float impulse = m_fHitImpulseScale*power*pGameObject->GetMass(); + + //статистика по объекту + O->total_damage += power; + O->hit_num++; + + if(power > 0.01f) + { + m_dwDeltaTime = 0; + position_in_bone_space.set(0.f,0.f,0.f); + + CreateHit(pGameObject->ID(),ID(),hit_dir,power,0,position_in_bone_space,impulse,m_eHitTypeBlowout); + + PlayHitParticles(pGameObject); + } +} + +void CAmebaZone::PhTune(dReal step) +{ + OBJECT_INFO_VEC_IT it; + for(it = m_ObjectInfoMap.begin(); m_ObjectInfoMap.end() != it; ++it) + { + CEntityAlive *EA=smart_cast((*it).object); + if(EA) + { + CPHMovementControl* mc=EA->character_physics_support()->movement(); + if(mc) + { + //Fvector vel; + //mc->GetCharacterVelocity(vel); + //vel.invert(); + //vel.mul(mc->GetMass()); + if(distance_to_center(EA)SetVelocityLimit(m_fVelocityLimit); + } + } + + } +} + +void CAmebaZone::SwitchZoneState(EZoneState new_state) +{ + if(new_state==eZoneStateBlowout&&m_eZoneState!=eZoneStateBlowout) + { + CPHUpdateObject::Activate(); + } + + if(new_state!=eZoneStateBlowout&&m_eZoneState==eZoneStateBlowout) + { + CPHUpdateObject::Deactivate(); + } + inherited::SwitchZoneState(new_state); +} + +float CAmebaZone::distance_to_center(CObject* O) +{ + Fvector P; + XFORM().transform_tiny(P,CFORM()->getSphere().P); + Fvector OP;OP.set(O->Position()); + return _sqrt((P.x-OP.x)*(P.x-OP.x)+(P.x-OP.x)*(P.x-OP.x)); +} diff --git a/src/xrGameLA/AmebaZone.h b/src/xrGameLA/AmebaZone.h new file mode 100644 index 000000000..e48e1ed22 --- /dev/null +++ b/src/xrGameLA/AmebaZone.h @@ -0,0 +1,21 @@ +#pragma once + +class CAmebaZone : + public CVisualZone, + public CPHUpdateObject +{ +typedef CVisualZone inherited; +float m_fVelocityLimit; +public: + CAmebaZone () ; + ~CAmebaZone () ; + virtual void Affect (SZoneObjectInfo* O) ; + +protected: + virtual void PhTune (dReal step) ; + virtual void PhDataUpdate (dReal step) {;} + virtual bool BlowoutState () ; + virtual void SwitchZoneState (EZoneState new_state) ; + virtual void Load (LPCSTR section) ; + virtual float distance_to_center (CObject* O) ; +}; diff --git a/src/xrGameLA/Artifact.cpp b/src/xrGameLA/Artifact.cpp new file mode 100644 index 000000000..4f033d8e1 --- /dev/null +++ b/src/xrGameLA/Artifact.cpp @@ -0,0 +1,663 @@ +#include "stdafx.h" +#include "artifact.h" +#include "PhysicsShell.h" +#include "PhysicsShellHolder.h" +#include "game_cl_base.h" +#include "../Include/xrRender/Kinematics.h" +#include "../Include/xrRender/KinematicsAnimated.h" +#include "inventory.h" +#include "level.h" +#include "ai_object_location.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "phworld.h" +#include "restriction_space.h" +#include "../IGame_Persistent.h" + +#define FASTMODE_DISTANCE (50.f) //distance to camera from sphere, when zone switches to fast update sequence + +#define CHOOSE_MAX(x,inst_x,y,inst_y,z,inst_z)\ + if(x>y)\ + if(x>z){inst_x;}\ + else{inst_z;}\ + else\ + if(y>z){inst_y;}\ + else{inst_z;} + +struct SArtefactActivation{ + enum EActivationStates {eNone=0, eStarting, eFlying, eBeforeSpawn, eSpawnZone, eMax}; + struct SStateDef{ + float m_time; + shared_str m_snd; + Fcolor m_light_color; + float m_light_range; + shared_str m_particle; + shared_str m_animation; + + SStateDef ():m_time(0.0f){}; + void Load (LPCSTR section, LPCSTR name); + }; + + SArtefactActivation (CArtefact* af, u32 owner_id); + ~SArtefactActivation (); + CArtefact* m_af; + svector m_activation_states; + EActivationStates m_cur_activation_state; + float m_cur_state_time; + + ref_light m_light; + ref_sound m_snd; + + u32 m_owner_id; + + void UpdateActivation (); + void Load (); + void Start (); + void ChangeEffects (); + void UpdateEffects (); + void SpawnAnomaly (); + void PhDataUpdate (dReal step); +}; + + +CArtefact::CArtefact(void) +{ + shedule.t_min = 20; + shedule.t_max = 50; + m_sParticlesName = NULL; + m_pTrailLight = NULL; + m_activationObj = NULL; +} + + +CArtefact::~CArtefact(void) +{ + if (custom_detect_sound_string)custom_detect_sound.destroy(); +} + +void CArtefact::Load(LPCSTR section) +{ + inherited::Load (section); + + + if (pSettings->line_exist(section, "particles")) + m_sParticlesName = pSettings->r_string(section, "particles"); + + m_bLightsEnabled = !!pSettings->r_bool(section, "lights_enabled"); + detect_radius_koef = READ_IF_EXISTS(pSettings,r_float,section,"detect_radius_koef",1.0f); + + custom_detect_sound_string = READ_IF_EXISTS(pSettings, r_string, section, "custom_detect_sound", NULL); + if (custom_detect_sound_string) + { + custom_detect_sound.create(pSettings->r_string(section, "custom_detect_sound"), st_Effect, SOUND_TYPE_ITEM); + } + + if(m_bLightsEnabled){ + sscanf(pSettings->r_string(section,"trail_light_color"), "%f,%f,%f", + &m_TrailLightColor.r, &m_TrailLightColor.g, &m_TrailLightColor.b); + m_fTrailLightRange = pSettings->r_float(section,"trail_light_range"); + } + + + { + m_fHealthRestoreSpeed = pSettings->r_float (section,"health_restore_speed" ); + m_fRadiationRestoreSpeed = pSettings->r_float (section,"radiation_restore_speed" ); + m_fSatietyRestoreSpeed = pSettings->r_float (section,"satiety_restore_speed" ); + m_fPowerRestoreSpeed = pSettings->r_float (section,"power_restore_speed" ); + m_fBleedingRestoreSpeed = pSettings->r_float (section,"bleeding_restore_speed" ); + m_fPsyhealthRestoreSpeed = READ_IF_EXISTS (pSettings, r_float, section, "psy_health_restore_speed", 0.0f); + if(pSettings->section_exist(/**cNameSect(), */pSettings->r_string(section,"hit_absorbation_sect"))) + m_ArtefactHitImmunities.LoadImmunities(pSettings->r_string(section,"hit_absorbation_sect"),pSettings); + } + m_bCanSpawnZone = !!pSettings->line_exist("artefact_spawn_zones", section); + + //tatarinrafa added additional_inventory_weight to artefacts + m_additional_weight = READ_IF_EXISTS(pSettings, r_float, section, "additional_inventory_weight", 0.0f); + + //tatarinrafa: added additional jump speed sprint speed walk speed + m_additional_jump_speed = READ_IF_EXISTS(pSettings, r_float, section, "additional_jump_speed", 0.0f); + m_additional_run_coef = READ_IF_EXISTS(pSettings, r_float, section, "additional_run_coef", 0.0f); + m_additional_sprint_koef = READ_IF_EXISTS(pSettings, r_float, section, "additional_sprint_koef", 0.0f); +} + +BOOL CArtefact::net_Spawn(CSE_Abstract* DC) +{ + BOOL result = inherited::net_Spawn(DC); + if (*m_sParticlesName) + {Fvector dir; + dir.set(0,1,0); + CParticlesPlayer::StartParticles(m_sParticlesName,dir,ID(),-1, false); + } + + VERIFY(m_pTrailLight == NULL); + m_pTrailLight = ::Render->light_create(); + m_pTrailLight->set_shadow(true); + + StartLights(); + ///////////////////////////////////////// + m_CarringBoneID = u16(-1); + ///////////////////////////////////////// + IKinematicsAnimated *K = smart_cast(Visual()); + if(K) + K->PlayCycle("idle"); + + o_fastmode = FALSE ; // start initially with fast-mode enabled + o_render_frame = 0 ; + SetState (eHidden); + + return result; +} + +void CArtefact::net_Destroy() +{ +/* + if (*m_sParticlesName) + CParticlesPlayer::StopParticles(m_sParticlesName, BI_NONE, true); +*/ + inherited::net_Destroy (); + + StopLights (); + m_pTrailLight.destroy (); + CPHUpdateObject::Deactivate (); + xr_delete (m_activationObj); +} + +void CArtefact::OnH_A_Chield() +{ + inherited::OnH_A_Chield (); + + StopLights(); + if (GameID() == GAME_SINGLE) + { + if (*m_sParticlesName) + { + CParticlesPlayer::StopParticles(m_sParticlesName, BI_NONE, true); + } + } + else + { + IKinematics* K = smart_cast(H_Parent()->Visual()); + if (K) + m_CarringBoneID = K->LL_BoneID("bip01_head"); + else + m_CarringBoneID = u16(-1); + } +} + +void CArtefact::OnH_B_Independent(bool just_before_destroy) +{ + VERIFY(!ph_world->Processing()); + inherited::OnH_B_Independent(just_before_destroy); + + StartLights(); + if (*m_sParticlesName) + { + Fvector dir; + dir.set(0,1,0); + CParticlesPlayer::StartParticles(m_sParticlesName,dir,ID(),-1, false); + } +} + +// called only in "fast-mode" +void CArtefact::UpdateCL () +{ + inherited::UpdateCL (); + + if (o_fastmode || m_activationObj) + UpdateWorkload (Device.dwTimeDelta); +} + +void CArtefact::UpdateWorkload (u32 dt) +{ + VERIFY(!ph_world->Processing()); + // particles - velocity + Fvector vel = {0, 0, 0}; + if (H_Parent()) + { + CPhysicsShellHolder* pPhysicsShellHolder = smart_cast(H_Parent()); + if(pPhysicsShellHolder) pPhysicsShellHolder->PHGetLinearVell(vel); + } + CParticlesPlayer::SetParentVel (vel); + + // + UpdateLights (); + if(m_activationObj) { + CPHUpdateObject::Activate (); + m_activationObj->UpdateActivation (); + return ; + } + + // custom-logic + UpdateCLChild (); +} + +void CArtefact::shedule_Update (u32 dt) +{ + inherited::shedule_Update (dt); + + ////////////////////////////////////////////////////////////////////////// + // check "fast-mode" border + if (H_Parent()) o_switch_2_slow (); + else { + Fvector center; Center(center); + BOOL rendering = (Device.dwFrame==o_render_frame); + float cam_distance = Device.vCameraPosition.distance_to(center)-Radius(); + if (rendering || (cam_distance < FASTMODE_DISTANCE)) o_switch_2_fast (); + else o_switch_2_slow (); + } + if (!o_fastmode) UpdateWorkload (dt); +} + + +void CArtefact::create_physic_shell () +{ + ///create_box2sphere_physic_shell (); + m_pPhysicsShell=P_build_Shell(this,false); + m_pPhysicsShell->Deactivate(); +} + +void CArtefact::StartLights() +{ + VERIFY(!ph_world->Processing()); + if(!m_bLightsEnabled) return; + + //включить световую подсветку от двигателя + m_pTrailLight->set_color(m_TrailLightColor.r, + m_TrailLightColor.g, + m_TrailLightColor.b); + + m_pTrailLight->set_range(m_fTrailLightRange); + m_pTrailLight->set_position(Position()); + m_pTrailLight->set_active(true); +} + +void CArtefact::StopLights() +{ + VERIFY(!ph_world->Processing()); + if(!m_bLightsEnabled) return; + m_pTrailLight->set_active(false); +} + +void CArtefact::UpdateLights() +{ + VERIFY(!ph_world->Processing()); + if(!m_bLightsEnabled || !m_pTrailLight->get_active()) return; + m_pTrailLight->set_position(Position()); +} + +void CArtefact::ActivateArtefact () +{ + VERIFY(m_bCanSpawnZone); + VERIFY( H_Parent() ); + m_activationObj = new SArtefactActivation(this,H_Parent()->ID()); + m_activationObj->Start(); + +} + +void CArtefact::PhDataUpdate (dReal step) +{ + if(m_activationObj) + m_activationObj->PhDataUpdate (step); +} + +bool CArtefact::CanTake() const +{ + if(!inherited::CanTake())return false; + return (m_activationObj==NULL); +} + +void CArtefact::Hide() +{ + SwitchState(eHiding); +} + +void CArtefact::Show() +{ + SwitchState(eShowing); +} +#include "inventoryOwner.h" +#include "Entity_alive.h" +void CArtefact::UpdateXForm() +{ + if (Device.dwFrame!=dwXF_Frame) + { + dwXF_Frame = Device.dwFrame; + + if (0==H_Parent()) return; + + // Get access to entity and its visual + CEntityAlive* E = smart_cast(H_Parent()); + + if(!E) return ; + + const CInventoryOwner *parent = smart_cast(E); + if (parent && parent->use_simplified_visual()) + return; + + VERIFY (E); + IKinematics* V = smart_cast (E->Visual()); + VERIFY (V); + + // Get matrices + int boneL = -1, boneR = -1, boneR2 = -1; + E->g_WeaponBones (boneL,boneR,boneR2); + if (boneR == -1 || boneR2 == -1) return; + + boneL = boneR2; + + V->CalculateBones (); + Fmatrix& mL = V->LL_GetTransform(u16(boneL)); + Fmatrix& mR = V->LL_GetTransform(u16(boneR)); + + // Calculate + Fmatrix mRes; + Fvector R,D,N; + D.sub (mL.c,mR.c); D.normalize_safe(); + R.crossproduct (mR.j,D); R.normalize_safe(); + N.crossproduct (D,R); N.normalize_safe(); + mRes.set (R,N,D,mR.c); + mRes.mulA_43 (E->XFORM()); +// UpdatePosition (mRes); + XFORM().mul (mRes,offset()); + } +} +#include "xr_level_controller.h" +bool CArtefact::Action(u16 cmd, u32 flags) +{ + switch (cmd) + { + case kWPN_FIRE: + { + if (flags&CMD_START && m_bCanSpawnZone){ + SwitchState(eActivating); + return true; + } + if (flags&CMD_STOP && m_bCanSpawnZone && GetState()==eActivating) + { + SwitchState(eIdle); + return true; + } + }break; + default: + break; + } + return inherited::Action(cmd,flags); +} + +void CArtefact::onMovementChanged (ACTOR_DEFS::EMoveCommand cmd) +{ + if( (cmd == ACTOR_DEFS::mcSprint)&&(GetState()==eIdle) ) + PlayAnimIdle (); +} + +void CArtefact::OnStateSwitch (u32 S) +{ + inherited::OnStateSwitch (S); + switch(S){ + case eShowing: + { + PlayHUDMotion("anim_show", FALSE, this, S); + }break; + case eHiding: + { + PlayHUDMotion("anim_hide", FALSE, this, S); + }break; + case eActivating: + { + PlayHUDMotion("anim_activate", FALSE, this, S); + }break; + case eIdle: + { + PlayAnimIdle(); + }break; + }; +} + +void CArtefact::PlayAnimIdle() +{ + PlayHUDMotion("anim_idle", FALSE, NULL, eIdle); +} + +void CArtefact::OnAnimationEnd (u32 state) +{ + inherited::OnAnimationEnd(state); + switch (state) + { + case eHiding: + { + SwitchState(eHidden); +//. if(m_pCurrentInventory->GetNextActiveSlot()!=NO_ACTIVE_SLOT) +//. m_pCurrentInventory->Activate(m_pCurrentInventory->GetPrevActiveSlot()); + }break; + case eShowing: + { + SwitchState(eIdle); + }break; + case eActivating: + { + if(Local()){ + SwitchState (eHiding); + NET_Packet P; + u_EventGen (P, GEG_PLAYER_ACTIVATEARTEFACT, H_Parent()->ID()); + P.w_u16 (ID()); + u_EventSend (P); + } + }break; + }; +} + + + +u16 CArtefact::bone_count_to_synchronize () const +{ + return CInventoryItem::object().PHGetSyncItemsNumber(); +} + + + +//---SArtefactActivation---- +SArtefactActivation::SArtefactActivation(CArtefact* af,u32 owner_id) +{ + m_af = af; + Load (); + m_light = ::Render->light_create(); + m_light->set_shadow(true); + m_owner_id = owner_id; +} +SArtefactActivation::~SArtefactActivation() +{ + m_light.destroy(); + +} + +void SArtefactActivation::Load() +{ + for(int i=0; i<(int)eMax; ++i) + m_activation_states.push_back(SStateDef()); + + LPCSTR activation_seq = pSettings->r_string(*m_af->cNameSect(),"artefact_activation_seq"); + + + m_activation_states[(int)eStarting].Load(activation_seq, "starting"); + m_activation_states[(int)eFlying].Load(activation_seq, "flying"); + m_activation_states[(int)eBeforeSpawn].Load(activation_seq, "idle_before_spawning"); + m_activation_states[(int)eSpawnZone].Load(activation_seq, "spawning"); + +} + +void SArtefactActivation::Start() +{ + VERIFY(!ph_world->Processing()); + m_af->StopLights (); + m_cur_activation_state = eStarting; + m_cur_state_time = 0.0f; + + m_af->processing_activate(); + + NET_Packet P; + CGameObject::u_EventGen (P,GE_OWNERSHIP_REJECT, m_af->H_Parent()->ID()); + P.w_u16 (m_af->ID()); + if (OnServer()) + CGameObject::u_EventSend (P); + m_light->set_active (true); + ChangeEffects (); +} + +void SArtefactActivation::UpdateActivation() +{ + VERIFY(!ph_world->Processing()); + m_cur_state_time += Device.fTimeDelta; + if(m_cur_state_time >= m_activation_states[int(m_cur_activation_state)].m_time){ + m_cur_activation_state = (EActivationStates)(int)(m_cur_activation_state+1); + + if(m_cur_activation_state == eMax){ + m_cur_activation_state = eNone; + + m_af->processing_deactivate (); + m_af->CPHUpdateObject::Deactivate (); + m_af->DestroyObject(); + } + + m_cur_state_time = 0.0f; + ChangeEffects (); + + + if(m_cur_activation_state==eSpawnZone && OnServer()) + SpawnAnomaly (); + + } + UpdateEffects (); + +} + +void SArtefactActivation::PhDataUpdate(dReal step) +{ + if (m_cur_activation_state==eFlying) { + Fvector dir = {0, -1.f, 0}; + if(Level().ObjectSpace.RayTest(m_af->Position(), dir, 1.0f, collide::rqtBoth,NULL,m_af) ){ + dir.y = ph_world->Gravity()*1.1f; + m_af->m_pPhysicsShell->applyGravityAccel(dir); + } + } + +} +void SArtefactActivation::ChangeEffects() +{ + VERIFY(!ph_world->Processing()); + SStateDef& state_def = m_activation_states[(int)m_cur_activation_state]; + + if(m_snd._feedback()) + m_snd.stop(); + + if(state_def.m_snd.size()) + { + m_snd.create (state_def.m_snd.c_str(),st_Effect,sg_SourceType); + m_snd.play_at_pos (m_af, m_af->Position()); + }; + + m_light->set_range ( state_def.m_light_range); + m_light->set_color ( state_def.m_light_color.r, + state_def.m_light_color.g, + state_def.m_light_color.b); + + if(state_def.m_particle.size()) + { + Fvector dir; + dir.set(0,1,0); + + m_af->CParticlesPlayer::StartParticles( state_def.m_particle, + dir, + m_af->ID(), + iFloor(state_def.m_time*1000) ); + }; + if(state_def.m_animation.size()) + { + IKinematicsAnimated *K=smart_cast(m_af->Visual()); + if(K)K->PlayCycle(state_def.m_animation.c_str()); + } + +} + +void SArtefactActivation::UpdateEffects() +{ + VERIFY(!ph_world->Processing()); + if(m_snd._feedback()) + m_snd.set_position( m_af->Position() ); + + m_light->set_position(m_af->Position()); +} + +void SArtefactActivation::SpawnAnomaly() +{ + VERIFY(!ph_world->Processing()); + string128 tmp; + LPCSTR str = pSettings->r_string("artefact_spawn_zones",*m_af->cNameSect()); + VERIFY3(3==_GetItemCount(str),"Bad record format in artefact_spawn_zones",str); + float zone_radius = (float)atof(_GetItem(str,1,tmp)); + float zone_power = (float)atof(_GetItem(str,2,tmp)); + LPCSTR zone_sect = _GetItem(str,0,tmp); //must be last call of _GetItem... (LPCSTR !!!) + + Fvector pos; + m_af->Center(pos); + CSE_Abstract *object = Level().spawn_item( zone_sect, + pos, + (g_dedicated_server)?u32(-1):m_af->ai_location().level_vertex_id(), + 0xffff, + true + ); + CSE_ALifeAnomalousZone* AlifeZone = smart_cast(object); + VERIFY(AlifeZone); + CShapeData::shape_def _shape; + _shape.data.sphere.P.set (0.0f,0.0f,0.0f); + _shape.data.sphere.R = zone_radius; + _shape.type = CShapeData::cfSphere; + AlifeZone->assign_shapes (&_shape,1); + AlifeZone->m_maxPower = zone_power; + AlifeZone->m_owner_id = m_owner_id; + AlifeZone->m_space_restrictor_type = RestrictionSpace::eRestrictorTypeNone; + + NET_Packet P; + object->Spawn_Write (P,TRUE); + Level().Send (P,net_flags(TRUE)); + F_entity_Destroy (object); +//. #ifdef DEBUG + Msg("artefact [%s] spawned a zone [%s] at [%f]", *m_af->cName(), zone_sect, Device.fTimeGlobal); +//. #endif +} + +shared_str clear_brackets(LPCSTR src) +{ + if (0==src) return shared_str(0); + + if( NULL == strchr(src,'"') ) return shared_str(src); + + string512 _original; + xr_strcpy (_original,src); + u32 _len = xr_strlen(_original); + if (0==_len) return shared_str(""); + if ('"'==_original[_len-1]) _original[_len-1]=0; // skip end + if ('"'==_original[0]) return shared_str(&_original[0] + 1); // skip begin + return shared_str(_original); + +} +void SArtefactActivation::SStateDef::Load(LPCSTR section, LPCSTR name) +{ + LPCSTR str = pSettings->r_string(section,name); + VERIFY(_GetItemCount(str)==8); + + + string128 tmp; + + m_time = (float)atof( _GetItem(str,0,tmp) ); + + m_snd = clear_brackets( _GetItem(str,1,tmp) ) ; + + m_light_color.r = (float)atof( _GetItem(str,2,tmp) ); + m_light_color.g = (float)atof( _GetItem(str,3,tmp) ); + m_light_color.b = (float)atof( _GetItem(str,4,tmp) ); + + m_light_range = (float)atof( _GetItem(str,5,tmp) ); + + m_particle = clear_brackets( _GetItem(str,6,tmp) ); + m_animation = clear_brackets( _GetItem(str,7,tmp) ); + +} \ No newline at end of file diff --git a/src/xrGameLA/Artifact.h b/src/xrGameLA/Artifact.h new file mode 100644 index 000000000..308d22f6f --- /dev/null +++ b/src/xrGameLA/Artifact.h @@ -0,0 +1,134 @@ +#pragma once + +#include "hud_item_object.h" +#include "hit_immunity.h" +#include "PHObject.h" +#include "script_export_space.h" + +struct SArtefactActivation; + +class CArtefact : public CHudItemObject, + public CPHUpdateObject { +private: + typedef CHudItemObject inherited; +public: + CArtefact (); + virtual ~CArtefact (); + + virtual void Load (LPCSTR section); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + + virtual void OnH_A_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + + virtual void UpdateCL (); + virtual void shedule_Update (u32 dt); + void UpdateWorkload (u32 dt); + + + virtual bool CanTake () const; + + //virtual void renderable_Render (); + virtual BOOL renderable_ShadowGenerate () { return FALSE; } + virtual BOOL renderable_ShadowReceive () { return TRUE; } + virtual void create_physic_shell(); + + //for smart_cast + virtual CArtefact* cast_artefact () {return this;} + +protected: + virtual void UpdateCLChild () {}; + + u16 m_CarringBoneID; + shared_str m_sParticlesName; +protected: + SArtefactActivation* m_activationObj; + ////////////////////////////////////////////////////////////////////////// + // Lights + ////////////////////////////////////////////////////////////////////////// + //флаг, что подсветка может быть включена + bool m_bLightsEnabled; + + //подсветка во время полета и работы двигателя + ref_light m_pTrailLight; + Fcolor m_TrailLightColor; + float m_fTrailLightRange; +protected: + virtual void UpdateLights(); + +public: + virtual void StartLights(); + virtual void StopLights(); + void ActivateArtefact (); + bool CanBeActivated () {return m_bCanSpawnZone;};// does artefact can spawn anomaly zone + + virtual void PhDataUpdate (dReal step); + virtual void PhTune (dReal step) {}; + + bool m_bCanSpawnZone; + //tatarinrafa: коефициент радиуса обнаружения для детектора. радиус обнаружения = fdetect_radius * detect_radius_koef + float detect_radius_koef; + + LPCSTR custom_detect_sound_string; + ref_sound custom_detect_sound; + + float m_fHealthRestoreSpeed; + float m_fRadiationRestoreSpeed; + float m_fSatietyRestoreSpeed; + float m_fPowerRestoreSpeed; + float m_fBleedingRestoreSpeed; + float m_fPsyhealthRestoreSpeed; + CHitImmunity m_ArtefactHitImmunities; + + //tatarinrafa added additional_inventory_weight to artefacts + float m_additional_weight; + + //tatarinrafa: added additional jump speed sprint speed walk speed + float m_additional_jump_speed; + float m_additional_run_coef; + float m_additional_sprint_koef; + +protected: +public: + enum EAFHudStates { + eIdle = 0, + eShowing, + eHiding, + eHidden, + eActivating, + }; + virtual void PlayAnimIdle (); +public: + virtual void Hide (); + virtual void Show (); + virtual void UpdateXForm (); + virtual bool Action (u16 cmd, u32 flags); + virtual void onMovementChanged (ACTOR_DEFS::EMoveCommand cmd); + virtual void OnStateSwitch (u32 S); + virtual void OnAnimationEnd (u32 state); + virtual bool IsHidden () const {return GetState()==eHidden;} + virtual u16 bone_count_to_synchronize () const; + + // optimization FAST/SLOW mode +public: + u32 o_render_frame ; + BOOL o_fastmode ; + IC void o_switch_2_fast () { + if (o_fastmode) return ; + o_fastmode = TRUE ; + //processing_activate (); + } + IC void o_switch_2_slow () { + if (!o_fastmode) return ; + o_fastmode = FALSE ; + //processing_deactivate (); + } + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CArtefact) +#undef script_type_list +#define script_type_list save_type_list(CArtefact) + diff --git a/src/xrGameLA/BastArtifact.cpp b/src/xrGameLA/BastArtifact.cpp new file mode 100644 index 000000000..e3994249d --- /dev/null +++ b/src/xrGameLA/BastArtifact.cpp @@ -0,0 +1,279 @@ +/////////////////////////////////////////////////////////////// +// BastArtifact.cpp +// BastArtefact - артефакт мочалка +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "BastArtifact.h" +#include "PhysicsShell.h" +#include "extendedgeom.h" +#include "ParticlesObject.h" + + +CBastArtefact::CBastArtefact(void) +{ + m_fImpulseThreshold = 10.f; + + m_fRadius = 10.f; + m_fStrikeImpulse = 15.f; + + m_bStrike = false; + m_AttakingEntity = NULL; + + m_fEnergy = 0.f; + m_fEnergyMax = m_fStrikeImpulse * 100.f; + m_fEnergyDecreasePerTime = 1.1f; + +} + +CBastArtefact::~CBastArtefact(void) +{ +} + +//вызывается при столкновении мочалки с чем-то +void CBastArtefact::ObjectContactCallback(bool& /**do_colide/**/,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/) +{ + dxGeomUserData *l_pUD1 = NULL; + dxGeomUserData *l_pUD2 = NULL; + l_pUD1 = retrieveGeomUserData(c.geom.g1); + l_pUD2 = retrieveGeomUserData(c.geom.g2); + + if(!l_pUD1 || !l_pUD2) return; + + //определить кто есть кто, из двух столкнувшихся предметов + CBastArtefact *pBastArtefact = l_pUD1 ? smart_cast(l_pUD1->ph_ref_object) : NULL; + if(!pBastArtefact) pBastArtefact = l_pUD2 ? smart_cast(l_pUD2->ph_ref_object) : NULL; + if(!pBastArtefact) return; + if(!pBastArtefact->IsAttacking()) return; + + CEntityAlive *pEntityAlive = NULL; + pEntityAlive = l_pUD1 ? smart_cast(l_pUD1->ph_ref_object) : NULL; + if(!pEntityAlive) pEntityAlive = l_pUD2 ? smart_cast(l_pUD2->ph_ref_object) : NULL; + + pBastArtefact->BastCollision(pEntityAlive); +} + +void CBastArtefact::BastCollision(CEntityAlive* pEntityAlive) +{ + //попали во что-то живое + if(pEntityAlive && pEntityAlive->g_Alive()) + { + m_AttakingEntity = NULL; + m_pHitedEntity = pEntityAlive; + + + if(m_AliveList.size()>1) + { + m_bStrike = true; + } + else + { + m_bStrike = false; + } + + m_bStrike = true; + Fvector vel; + vel.set(0,0,0); + // this->m_pPhysicsShell->set_LinearVel(vel); + // this->m_pPhysicsShell->set_AngularVel(vel); + + } +} + +BOOL CBastArtefact::net_Spawn(CSE_Abstract* DC) +{ + BOOL result = inherited::net_Spawn(DC); + if(!result) return FALSE; + + m_bStrike = false; + m_AttakingEntity = NULL; + m_pHitedEntity = NULL; + m_AliveList.clear(); + + return TRUE; +} + +void CBastArtefact::net_Destroy () +{ + inherited::net_Destroy(); + + m_bStrike = false; + m_AttakingEntity = NULL; + m_pHitedEntity = NULL; + m_AliveList.clear(); +} + +void CBastArtefact::Load(LPCSTR section) +{ + inherited::Load(section); + + m_fImpulseThreshold = pSettings->r_float(section,"impulse_threshold"); + m_fRadius = pSettings->r_float(section,"radius"); + m_fStrikeImpulse = pSettings->r_float(section,"strike_impulse"); + + m_fEnergyMax = pSettings->r_float(section,"energy_max"); + m_fEnergyDecreasePerTime = pSettings->r_float(section,"energy_decrease_speed"); + + m_sParticleName = pSettings->r_string(section,"particle"); + +} + +void CBastArtefact::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + + Fvector P; + P.set(Position()); + feel_touch_update(P,m_fRadius); +} + + +void CBastArtefact::UpdateCLChild() +{ + //Log ("--- A - CBastArtefact",*cName()); + //Log ("--- A - CBastArtefact",renderable.xform); + + //современем энергия по немногу тоже уменьшается + if(m_fEnergy>0) m_fEnergy -= m_fEnergyDecreasePerTime*Device.fTimeDelta; + + if (getVisible() && m_pPhysicsShell) { + if(m_bStrike) { + //выбрать жертву, если она еще не выбрана + if(!m_AliveList.empty() && m_AttakingEntity == NULL) { + CEntityAlive* pEntityToHit = NULL; + if(m_AliveList.size()>1) + { + do + { + int rnd = ::Random.randI(m_AliveList.size()); + pEntityToHit = m_AliveList[rnd]; + } while (pEntityToHit == m_pHitedEntity); + } + else + { + pEntityToHit = m_AliveList.front(); + } + + m_AttakingEntity = pEntityToHit; + } + } + + if(m_AttakingEntity) + { + if(m_AttakingEntity->g_Alive() && m_fEnergy>m_fStrikeImpulse) + { + m_fEnergy -= m_fStrikeImpulse; + + //бросить артефакт на выбранную цель + Fvector dir; + m_AttakingEntity->Center(dir); + dir.sub(this->Position()); + dir.y += ::Random.randF(-0.05f, 0.5f); + + m_pPhysicsShell->applyImpulse(dir, + m_fStrikeImpulse * Device.fTimeDelta * + m_pPhysicsShell->getMass()); + } + else + { + m_AttakingEntity = NULL; + m_bStrike = false; + } + } + + + + if(m_fEnergy>0 && ::Random.randF(0.f, 1.0f)<(m_fEnergy/(m_fStrikeImpulse*100.f))) + { + CParticlesObject* pStaticPG; + pStaticPG = CParticlesObject::Create(*m_sParticleName,TRUE); + Fmatrix pos; + pos.set(XFORM()); + Fvector vel; + //vel.sub(Position(),ps_Element(0).vPosition); + //vel.div((Level().timeServer()-ps_Element(0).dwTime)/1000.f); + vel.set(0,0,0); + pStaticPG->UpdateParent(pos, vel); + pStaticPG->Play(false); + } + + } + else if(H_Parent()) XFORM().set(H_Parent()->XFORM()); +} + + +//void CBastArtefact::Hit(float P, Fvector &dir, +// CObject* who, s16 element, +// Fvector position_in_object_space, +// float impulse, +// ALife::EHitType hit_type) +void CBastArtefact::Hit (SHit* pHDS) +{ + SHit HDS = *pHDS; + if(HDS.impulse>m_fImpulseThreshold && !m_AliveList.empty()) + { + m_bStrike = true; + m_AttakingEntity = m_pHitedEntity = NULL; + + m_fEnergy += m_fStrikeImpulse*HDS.impulse; + + if(m_fEnergy>m_fEnergyMax) m_fEnergy = m_fEnergyMax; + + //чтоб выстрел не повлиял на траекторию полета артефакта + HDS.impulse = 0; + } + +// inherited::Hit(P, dir, who, element, position_in_object_space, impulse, hit_type); + inherited::Hit(&HDS); +} + + +//объект можно поднять только в спокойном состоянии +bool CBastArtefact::Useful() const +{ + if(m_fEnergy>0) + return false; + else + return true; + +} + +void CBastArtefact::feel_touch_new(CObject* O) +{ + CEntityAlive* pEntityAlive = smart_cast(O); + + if(pEntityAlive && pEntityAlive->g_Alive()) + { + m_AliveList.push_back(pEntityAlive); + } +} + +void CBastArtefact::feel_touch_delete(CObject* O) +{ + CEntityAlive* pEntityAlive = smart_cast(O); + + if(pEntityAlive) + { + m_AliveList.erase(std::find(m_AliveList.begin(), + m_AliveList.end(), + pEntityAlive)); + } +} + +BOOL CBastArtefact::feel_touch_contact(CObject* O) +{ + CEntityAlive* pEntityAlive = smart_cast(O); + + if(pEntityAlive && pEntityAlive->g_Alive()) + return TRUE; + else + return FALSE; +} + +void CBastArtefact::setup_physic_shell () +{ + inherited::setup_physic_shell(); + m_pPhysicsShell->set_PhysicsRefObject(this); + m_pPhysicsShell->set_ObjectContactCallback(ObjectContactCallback); + m_pPhysicsShell->set_ContactCallback(NULL); +} \ No newline at end of file diff --git a/src/xrGameLA/BastArtifact.h b/src/xrGameLA/BastArtifact.h new file mode 100644 index 000000000..b08170692 --- /dev/null +++ b/src/xrGameLA/BastArtifact.h @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////// +// BastArtifact.h +// BastArtefact - артефакт мочалка +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" +#include "../feel_touch.h" + +#include "entity_alive.h" + +struct SGameMtl; +struct dContact; + +DEFINE_VECTOR (CEntityAlive*, ALIVE_LIST, ALIVE_LIST_it); + + +class CBastArtefact : public CArtefact, + public Feel::Touch +{ +private: + typedef CArtefact inherited; +public: + CBastArtefact(void); + virtual ~CBastArtefact(void); + + virtual void Load (LPCSTR section); + virtual void shedule_Update (u32 dt); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + + virtual void Hit (SHit* pHDS); + + virtual bool Useful() const; + + + virtual void feel_touch_new (CObject* O); + virtual void feel_touch_delete (CObject* O); + virtual BOOL feel_touch_contact (CObject* O); + + bool IsAttacking() {return NULL!=m_AttakingEntity;} + +protected: + virtual void UpdateCLChild (); + + static void ObjectContactCallback(bool& do_colide,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/); + //столкновение мочалки с сущностью + void BastCollision(CEntityAlive* pEntityAlive); + + + //параметры артефакта + + //пороговое значение импульса после получения + //которого артефакт активизируется + float m_fImpulseThreshold; + + float m_fEnergy; + float m_fEnergyMax; + float m_fEnergyDecreasePerTime; + shared_str m_sParticleName; + + + float m_fRadius; + float m_fStrikeImpulse; + + //флаг, того что артефакт получил хит + //и теперь может совершить бросок + bool m_bStrike; + + //список живых существ в зоне досягаемости артефакта + ALIVE_LIST m_AliveList; + //то, что мы ударили + CEntityAlive* m_pHitedEntity; + //то что атакуем + CEntityAlive* m_AttakingEntity; + +public: + virtual void setup_physic_shell (); +}; \ No newline at end of file diff --git a/src/xrGameLA/BlackDrops.cpp b/src/xrGameLA/BlackDrops.cpp new file mode 100644 index 000000000..f4fbd7a4b --- /dev/null +++ b/src/xrGameLA/BlackDrops.cpp @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////// +// BlackDrops.cpp +// BlackDrops - черные капли +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "BlackDrops.h" +#include "PhysicsShell.h" + + +CBlackDrops::CBlackDrops(void) +{ +} + +CBlackDrops::~CBlackDrops(void) +{ +} + +void CBlackDrops::Load(LPCSTR section) +{ + inherited::Load(section); +} diff --git a/src/xrGameLA/BlackDrops.h b/src/xrGameLA/BlackDrops.h new file mode 100644 index 000000000..825e8b12b --- /dev/null +++ b/src/xrGameLA/BlackDrops.h @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////// +// BlackDrops.h +// BlackDrops - черные капли +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CBlackDrops : public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CBlackDrops(void); + virtual ~CBlackDrops(void); + + virtual void Load (LPCSTR section); + +protected: +}; \ No newline at end of file diff --git a/src/xrGameLA/BlackGraviArtifact.cpp b/src/xrGameLA/BlackGraviArtifact.cpp new file mode 100644 index 000000000..0cf533374 --- /dev/null +++ b/src/xrGameLA/BlackGraviArtifact.cpp @@ -0,0 +1,243 @@ +/////////////////////////////////////////////////////////////// +// BlackGraviArtifact.cpp +// BlackGraviArtefact - гравитационный артефакт, +// такой же как и обычный, но при получении хита +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "BlackGraviArtifact.h" +#include "PhysicsShell.h" +#include "entity_alive.h" +#include "ParticlesObject.h" +#include "phmovementcontrol.h" +#include "xrmessages.h" +#include "physicsshellholder.h" +#include "explosive.h" +#include "../../xrNetServer/net_utils.h" +#include "PHWorld.h" +#include "CharacterPhysicsSupport.h" +extern CPHWorld* ph_world; +CBlackGraviArtefact::CBlackGraviArtefact(void) +{ + m_fImpulseThreshold = 10.f; + m_fRadius = 10.f; + m_fStrikeImpulse = 50.f; + + m_bStrike = false; +} + +CBlackGraviArtefact::~CBlackGraviArtefact(void) +{ + m_GameObjectList.clear(); +} + + + +void CBlackGraviArtefact::Load(LPCSTR section) +{ + inherited::Load(section); + + m_fImpulseThreshold = pSettings->r_float(section,"impulse_threshold"); + m_fRadius = pSettings->r_float(section,"radius"); + m_fStrikeImpulse = pSettings->r_float(section,"strike_impulse"); + m_sParticleName = pSettings->r_string(section,"particle"); +} + +BOOL CBlackGraviArtefact::net_Spawn(CSE_Abstract* DC) +{ + if(!inherited::net_Spawn(DC)) return FALSE; + + + + CParticlesObject* pStaticPG; + pStaticPG = CParticlesObject::Create("anomaly\\galantine",FALSE); + Fmatrix pos; + //pos.rotateY(1.57); + //pos.mulA(pos); + pos.scale(0.7f,0.7f,0.7f); + pos.translate_over(XFORM().c); + + Fvector vel; + vel.set(0,0,0); + pStaticPG->UpdateParent(pos, vel); + pStaticPG->Play(false); + + + + return TRUE; +} +struct SRP +{ + const CPhysicsShellHolder* obj; + SRP(const CPhysicsShellHolder* O) + { + obj=O; + } + bool operator () (CPhysicsShellHolder* O) const + { + return obj==O; + } +}; +void CBlackGraviArtefact::net_Relcase(CObject* O) +{ + inherited::net_Relcase(O); + //for vector + GAME_OBJECT_LIST_it I=std::remove_if(m_GameObjectList.begin(),m_GameObjectList.end(),SRP(smart_cast(O))); + m_GameObjectList.erase(I,m_GameObjectList.end()); + //for list + //m_GameObjectList.remove_if(SRP(smart_cast(O))); +} +void CBlackGraviArtefact::UpdateCLChild() +{ + VERIFY(!ph_world->Processing()); + inherited::UpdateCLChild (); + + if (getVisible() && m_pPhysicsShell) { + if (m_bStrike) { + Fvector P; + P.set(Position()); + feel_touch_update(P,m_fRadius); + + GraviStrike(); + + CParticlesObject* pStaticPG; + pStaticPG = CParticlesObject::Create(*m_sParticleName,TRUE); + Fmatrix pos; + pos.set(XFORM()); + Fvector vel; + //vel.sub(Position(),ps_Element(0).vPosition); + //vel.div((Level().timeServer()-ps_Element(0).dwTime)/1000.f); + vel.set(0,0,0); + pStaticPG->UpdateParent(pos, vel); + pStaticPG->Play(false); + + m_bStrike = false; + } + }else if(H_Parent()) XFORM().set(H_Parent()->XFORM()); +} + +//void CBlackGraviArtefact::Hit(float P, Fvector &dir, +// CObject* who, s16 element, +// Fvector position_in_object_space, +// float impulse, +// ALife::EHitType hit_type) +void CBlackGraviArtefact::Hit (SHit* pHDS) +{ + SHit HDS = *pHDS; + if(HDS.impulse>m_fImpulseThreshold) + { + m_bStrike = true; + //чтоб выстрел не повлиял на траекторию полета артефакта + HDS.impulse = 0; + } + +// inherited::Hit(P, dir, who, element, position_in_object_space, impulse, hit_type); + inherited::Hit(&HDS); +} + +void CBlackGraviArtefact::feel_touch_new(CObject* O) +{ + CPhysicsShellHolder* pGameObject = smart_cast(O); + CArtefact* pArtefact = smart_cast(O); + + if(pGameObject && !pArtefact) + { + m_GameObjectList.push_back(pGameObject); + } +} + +void CBlackGraviArtefact::feel_touch_delete(CObject* O) +{ + CGameObject* pGameObject = static_cast(O); + CArtefact* pArtefact = smart_cast(O); + + if(pGameObject && !pArtefact) + { + m_GameObjectList.erase(std::find(m_GameObjectList.begin(), + m_GameObjectList.end(), + pGameObject)); + } +} + +BOOL CBlackGraviArtefact::feel_touch_contact(CObject* O) +{ + CGameObject* pGameObject = static_cast(O); + + if(pGameObject) + return TRUE; + else + return FALSE; +} + +void CBlackGraviArtefact::GraviStrike() +{ + xr_list elements_list; + xr_list bone_position_list; + + Fvector object_pos ; + Fvector strike_dir ; + + rq_storage.r_clear (); + + for(GAME_OBJECT_LIST_it it = m_GameObjectList.begin(); + m_GameObjectList.end() != it; + ++it) + { + CPhysicsShellHolder* pGameObject = *it; + + if(pGameObject->Visual()) + pGameObject->Center(object_pos); + else + object_pos.set(pGameObject->Position()); + + strike_dir.sub(object_pos, Position()); + float distance = strike_dir.magnitude(); + + float impulse = 100.f*m_fStrikeImpulse * (1.f - (distance/m_fRadius)* + (distance/m_fRadius)); + + if(impulse > .001f) + { +//? BOOL enabled = getEnabled(); +//? setEnabled (FALSE); + impulse *= CExplosive::ExplosionEffect (rq_storage,NULL,pGameObject, Position(),m_fRadius); +//? setEnabled (enabled); + } + + float hit_power ; + CEntityAlive* pEntityAlive = smart_cast(pGameObject); + if(pGameObject->m_pPhysicsShell) hit_power = 0; + else if(pEntityAlive && pEntityAlive->g_Alive() && + pEntityAlive->character_physics_support()->movement()->CharacterExist()) + hit_power = 0; + else + hit_power = impulse; + + + if(impulse > .001f) + { + while(!elements_list.empty()) + { + s16 element = elements_list.front(); + Fvector bone_pos = bone_position_list.front(); + + NET_Packet P; + SHit HS; + HS.GenHeader(GE_HIT, pGameObject->ID()); // u_EventGen (P,GE_HIT, pGameObject->ID()); + HS.whoID =ID(); // P.w_u16 (ID()); + HS.weaponID = ID(); // P.w_u16 (ID()); + HS.dir = strike_dir; // P.w_dir (strike_dir); + HS.power = hit_power; // P.w_float (hit_power); + HS.boneID = element; // P.w_s16 (element); + HS.p_in_bone_space = bone_pos; // P.w_vec3 (bone_pos); + HS.impulse = impulse; // P.w_float (impulse); + HS.hit_type = (ALife::eHitTypeWound); // P.w_u16 (u16(ALife::eHitTypeWound)); + HS.Write_Packet(P); + + u_EventSend (P); + elements_list.pop_front(); + bone_position_list.pop_front(); + } + } + } +} \ No newline at end of file diff --git a/src/xrGameLA/BlackGraviArtifact.h b/src/xrGameLA/BlackGraviArtifact.h new file mode 100644 index 000000000..322eab7fc --- /dev/null +++ b/src/xrGameLA/BlackGraviArtifact.h @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////// +// BlackGraviArtifact.h +// BlackGraviArtefact - гравитационный артефакт, +// такой же как и обычный, но при получении хита +/////////////////////////////////////////////////////////////// + +#pragma once +#include "GraviArtifact.h" +#include "../feel_touch.h" +#include "PhysicsShellHolder.h" +DEFINE_VECTOR (CPhysicsShellHolder*, GAME_OBJECT_LIST, GAME_OBJECT_LIST_it); + +class CBlackGraviArtefact: public CGraviArtefact, + public Feel::Touch +{ +private: + collide::rq_results rq_storage; + +private: + typedef CGraviArtefact inherited; +public: + CBlackGraviArtefact(void); + virtual ~CBlackGraviArtefact(void); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + + virtual void Load (LPCSTR section); + + virtual void Hit (SHit* pHDS); + + virtual void feel_touch_new (CObject* O); + virtual void feel_touch_delete (CObject* O); + virtual BOOL feel_touch_contact (CObject* O); + +protected: + virtual void net_Relcase (CObject* O ); + virtual void UpdateCLChild (); + + //гравитационный удар по всем объектам в зоне досягаемости + void GraviStrike(); + + GAME_OBJECT_LIST m_GameObjectList; + + + //которого артефакт активизируется + float m_fImpulseThreshold; + //радиус действия артефакта + float m_fRadius; + //импульс передаваемый окружающим предметам + float m_fStrikeImpulse; + + //флаг, того что артефакт получил хит + //и теперь может совершить бросок + bool m_bStrike; + + shared_str m_sParticleName; +}; diff --git a/src/xrGameLA/BlockAllocator.h b/src/xrGameLA/BlockAllocator.h new file mode 100644 index 000000000..9fbce2979 --- /dev/null +++ b/src/xrGameLA/BlockAllocator.h @@ -0,0 +1,118 @@ +#ifndef BLOCK_ALLOCATOR_H +#define BLOCK_ALLOCATOR_H +template +class CBlockAllocator +{ + u32 block_count; + u32 block_position; + T* current_block; + xr_vector blocks; +public: + IC T* add() + { + if(block_position==block_size)next_block(); + ++block_position; + return ¤t_block[block_position-1]; + } + IC void empty() + { + block_count=0; + if(blocks.size()) + { + block_position=0; + current_block=blocks[0]; + } + else + { + block_position=block_size; + } + } + CBlockAllocator() + { + init(); + } + ~CBlockAllocator() + { + clear(); + } + IC void init () + { + block_position=block_size; + block_count=0; + current_block=NULL; + } + IC void clear() + { + xr_vector::iterator i=blocks.begin(),e=blocks.end(); + for(;i!=e;++i) xr_free(*i); + blocks.clear(); + init(); + } +private: +///////////////////////////////////////////////////////////////// + IC void add_block() + { + blocks.push_back(xr_alloc(block_size)); + }; + IC void next_block() + { + + if(block_count==blocks.size()) add_block(); + current_block=blocks[block_count]; + ++block_count; + block_position=0; + } +//////////////////////////////////////////////////////////////// +public: + template + IC void for_each(const _Predicate &pred) + { + if(! current_block) return; + xr_vector::iterator i = blocks.begin(); + xr_vector::iterator e = blocks.begin()+block_count; + u32 j; + for ( ; i != e; ++i) + { + for(j=0;j ().construct(pointer(position)); + } + + IC void construct_back() + { + xr_allocator_t ().construct(back_pointer()); + } +}; + + +#endif \ No newline at end of file diff --git a/src/xrGameLA/Bolt.cpp b/src/xrGameLA/Bolt.cpp new file mode 100644 index 000000000..3dfd901e8 --- /dev/null +++ b/src/xrGameLA/Bolt.cpp @@ -0,0 +1,79 @@ +#include "stdafx.h" +#include "bolt.h" +#include "ParticlesObject.h" +#include "PhysicsShell.h" +#include "xr_level_controller.h" + +CBolt::CBolt(void) +{ + m_weight = .1f; + m_baseSlot = BOLT_SLOT; + m_flags.set (Fruck, FALSE); + m_thrower_id =u16(-1); +} + +CBolt::~CBolt(void) +{ +} + +void CBolt::OnH_A_Chield() +{ + inherited::OnH_A_Chield(); + CObject* o= H_Parent()->H_Parent(); + if(o)SetInitiator(o->ID()); + +} + +void CBolt::Throw() +{ + CMissile *l_pBolt = smart_cast(m_fake_missile); + if(!l_pBolt) return; + l_pBolt->set_destroy_time (u32(m_dwDestroyTimeMax/phTimefactor)); + inherited::Throw (); + spawn_fake_missile (); +} + +bool CBolt::Useful() const +{ + return false; +} + +bool CBolt::Action(u16 cmd, u32 flags) +{ + if(inherited::Action(cmd, flags)) return true; +/* + switch(cmd) + { + case kDROP: + { + if(flags&CMD_START) + { + m_throw = false; + if(State() == MS_IDLE) State(MS_THREATEN); + } + else if(State() == MS_READY || State() == MS_THREATEN) + { + m_throw = true; + if(State() == MS_READY) State(MS_THROW); + } + } + return true; + } +*/ + return false; +} + +void CBolt::activate_physic_shell () +{ + inherited::activate_physic_shell (); + m_pPhysicsShell->SetAirResistance (.0001f); +} + +void CBolt::SetInitiator (u16 id) +{ + m_thrower_id=id; +} +u16 CBolt::Initiator () +{ + return m_thrower_id; +} \ No newline at end of file diff --git a/src/xrGameLA/Bolt.h b/src/xrGameLA/Bolt.h new file mode 100644 index 000000000..57fb38406 --- /dev/null +++ b/src/xrGameLA/Bolt.h @@ -0,0 +1,27 @@ +#pragma once +#include "missile.h" +#include "DamageSource.h" +class CBolt : + public CMissile, + public IDamageSource +{ + typedef CMissile inherited; + u16 m_thrower_id; +public: + CBolt (); + virtual ~CBolt (); + + virtual void OnH_A_Chield(); + + virtual void SetInitiator(u16 id); + virtual u16 Initiator(); + + virtual void Throw(); + virtual bool Action(u16 cmd, u32 flags); + virtual bool Useful() const; + + virtual void activate_physic_shell (); + + virtual BOOL UsedAI_Locations() {return FALSE;} + virtual IDamageSource* cast_IDamageSource () {return this;} +}; diff --git a/src/xrGameLA/BoneProtections.cpp b/src/xrGameLA/BoneProtections.cpp new file mode 100644 index 000000000..4612d2d83 --- /dev/null +++ b/src/xrGameLA/BoneProtections.cpp @@ -0,0 +1,101 @@ +#include "stdafx.h" +#include "BoneProtections.h" +#include "../Include/xrRender/Kinematics.h" +#include "../bone.h" +#include "Level.h" + +float SBoneProtections::getBoneProtection(s16 bone_id) +{ + storage_it it = m_bones_koeff.find(bone_id); + if( it != m_bones_koeff.end() ) + return it->second.koeff; + else + return m_default.koeff; +} + +float SBoneProtections::getBoneArmor(s16 bone_id) +{ + storage_it it = m_bones_koeff.find(bone_id); + if( it != m_bones_koeff.end() ) + return it->second.armor; + else + return m_default.armor; +} + +BOOL SBoneProtections::getBonePassBullet(s16 bone_id) +{ + storage_it it = m_bones_koeff.find(bone_id); + if( it != m_bones_koeff.end() ) + return it->second.BonePassBullet; + else + return m_default.BonePassBullet; +} + +void SBoneProtections::reload(const shared_str& bone_sect, IKinematics* kinematics) +{ + VERIFY (kinematics); + m_bones_koeff.clear (); + + m_fHitFracNpc = READ_IF_EXISTS(pSettings, r_float, bone_sect, "hit_fraction", 0.1f); + + m_default.koeff = 1.0f; + m_default.armor = 0.0f; + m_default.BonePassBullet = FALSE; + + CInifile::Sect &protections = pSettings->r_section(bone_sect); + for (CInifile::SectCIt i=protections.Data.begin(); protections.Data.end() != i; ++i) + { + string256 buffer; + + BoneProtection BP; + + BP.koeff = (float)atof( _GetItem( i->second.c_str(), 0, buffer) ); + BP.armor = (float)atof( _GetItem( i->second.c_str(), 1, buffer) ); + BP.BonePassBullet = (BOOL) (atoi( _GetItem(i->second.c_str(), 2, buffer) )>0.5f); + + if (!xr_strcmp(i->first.c_str(), "default")) + { + m_default = BP; + } + else + { + if (!xr_strcmp(i->first.c_str(), "hit_fraction")) continue; + + s16 bone_id = kinematics->LL_BoneID(i->first); + R_ASSERT2 (BI_NONE != bone_id, i->first.c_str()); + m_bones_koeff.insert (mk_pair(bone_id,BP)); + } + } +} + +void SBoneProtections::add(const shared_str& bone_sect, IKinematics* kinematics) +{ + if(!IsGameTypeSingle()) + return; + + VERIFY(kinematics); + m_fHitFracNpc += READ_IF_EXISTS(pSettings, r_float, bone_sect.c_str(), "hit_fraction", 0.0f); + + CInifile::Sect &protections = pSettings->r_section(bone_sect); + for(CInifile::SectCIt i=protections.Data.begin(); protections.Data.end()!=i; ++i) + { + if(!xr_strcmp(i->first.c_str(), "hit_fraction")) + continue; + + string256 buffer; + if(!xr_strcmp(i->first.c_str(), "default")) + { + BoneProtection& BP = m_default; + BP.koeff += (float)atof( _GetItem( i->second.c_str(), 0, buffer) ); + BP.armor += (float)atof( _GetItem( i->second.c_str(), 1, buffer) ); + } + else + { + s16 bone_id = kinematics->LL_BoneID(i->first); + R_ASSERT2 (BI_NONE != bone_id, i->first.c_str()); + BoneProtection& BP = m_bones_koeff[bone_id]; + BP.koeff += (float)atof( _GetItem( i->second.c_str(), 0, buffer) ); + BP.armor += (float)atof( _GetItem( i->second.c_str(), 1, buffer) ); + } + } +} diff --git a/src/xrGameLA/BoneProtections.h b/src/xrGameLA/BoneProtections.h new file mode 100644 index 000000000..1d26d601b --- /dev/null +++ b/src/xrGameLA/BoneProtections.h @@ -0,0 +1,24 @@ +#pragma once + +class IKinematics; + +struct SBoneProtections{ + struct BoneProtection { + float koeff; + float armor; + BOOL BonePassBullet; + }; + float m_fHitFracNpc; + float m_fHitFracActor; + typedef xr_map storage_type; + typedef storage_type::iterator storage_it; + SBoneProtections () {m_default.koeff = 1.0f; m_default.armor = 0; m_fHitFracActor = 0.1f; } + BoneProtection m_default; + storage_type m_bones_koeff; + void reload (const shared_str& outfit_section, IKinematics* kinematics); + void add (const shared_str& outfit_section, IKinematics* kinematics); + float getBoneProtection (s16 bone_id); + float getBoneArmor (s16 bone_id); + BOOL getBonePassBullet (s16 bone_id); +}; + diff --git a/src/xrGameLA/Booster.h b/src/xrGameLA/Booster.h new file mode 100644 index 000000000..4143990ff --- /dev/null +++ b/src/xrGameLA/Booster.h @@ -0,0 +1,16 @@ +#pragma once + +struct BoosterParams +{ + u8 EffectIsBooster; // if its 0, then HitImmunitySect is uninitialized and HitImmunitySect = "null" + float AddWeight; //Additional actor weight + CHitImmunity BoosterHitImmunities; + shared_str HitImmunitySect; // for save/load process, so that we dont have to save the whole hit immunity class object into savefile + + BoosterParams() + { + EffectIsBooster = false; + AddWeight = 0.f; + HitImmunitySect = "nullnull"; + }; +}; \ No newline at end of file diff --git a/src/xrGameLA/BottleItem.cpp b/src/xrGameLA/BottleItem.cpp new file mode 100644 index 000000000..61299da04 --- /dev/null +++ b/src/xrGameLA/BottleItem.cpp @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////// +// BottleItem.cpp +// BottleItem - бутылка с напитком, которую можно разбить +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "BottleItem.h" +#include "xrmessages.h" +#include "../../xrNetServer/net_utils.h" +#include "entity_alive.h" +#include "EntityCondition.h" + +#define BREAK_POWER 5.f + +CBottleItem::CBottleItem(void) +{ +} + +CBottleItem::~CBottleItem(void) +{ + sndBreaking.destroy(); +} + + +void CBottleItem::Load(LPCSTR section) +{ + inherited::Load(section); + + if(pSettings->line_exist(section, "break_particles")) + m_sBreakParticles = pSettings->r_string(section, "break_particles"); + + if(pSettings->line_exist(section, "break_sound")) + sndBreaking.create(pSettings->r_string(section, "break_sound"),st_Effect,sg_SourceType); +} + +void CBottleItem::OnEvent(NET_Packet& P, u16 type) +{ + inherited::OnEvent(P,type); + + switch (type) + { + case GE_GRENADE_EXPLODE : + BreakToPieces(); + break; + } +} + +void CBottleItem::BreakToPieces() +{ + //играем звук + sndBreaking.play_at_pos(0, Position(), false); + + //отыграть партиклы разбивания + if(*m_sBreakParticles) + { + //показываем эффекты + CParticlesObject* pStaticPG; + pStaticPG = CParticlesObject::Create(*m_sBreakParticles,TRUE); + pStaticPG->play_at_pos(Position()); + } + + //ликвидировать сам объект + if (Local()) + { + DestroyObject (); + } +} + +void CBottleItem::Hit (SHit* pHDS) +{ + inherited::Hit(pHDS); + + if(pHDS->damage()>BREAK_POWER) + { + //Generate Expode event + if (Local()) + { + NET_Packet P; + u_EventGen (P,GE_GRENADE_EXPLODE,ID()); + u_EventSend (P); + }; + } +} + +bool CBottleItem::UseBy (CEntityAlive* entity_alive) +{ + bool used = inherited::UseBy (entity_alive); + + if (!used) return false; + + return true; +} \ No newline at end of file diff --git a/src/xrGameLA/BottleItem.h b/src/xrGameLA/BottleItem.h new file mode 100644 index 000000000..fd2a23868 --- /dev/null +++ b/src/xrGameLA/BottleItem.h @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////// +// BottleItem.h +// BottleItem - бутылка с напитком, которую можно разбить +/////////////////////////////////////////////////////////////// + + +#pragma once + +#include "fooditem.h" + + +class CBottleItem: public CFoodItem +{ +private: + typedef CFoodItem inherited; +public: + CBottleItem(void); + virtual ~CBottleItem(void); + + + virtual void Load (LPCSTR section); + + + void OnEvent (NET_Packet& P, u16 type); + + + virtual void Hit (SHit* pHDS); + + + void BreakToPieces (); + virtual bool UseBy (CEntityAlive* entity_alive); +protected: + //партиклы разбивания бутылки + shared_str m_sBreakParticles; + ref_sound sndBreaking; +}; \ No newline at end of file diff --git a/src/xrGameLA/BreakableObject.cpp b/src/xrGameLA/BreakableObject.cpp new file mode 100644 index 000000000..c5d4ca751 --- /dev/null +++ b/src/xrGameLA/BreakableObject.cpp @@ -0,0 +1,351 @@ +#include "stdafx.h" +#pragma hdrstop + +#include "BreakableObject.h" +#include "xrserver_objects_alife.h" +#include "PHStaticGeomShell.h" +#include "PhysicsShell.h" +#include "Physics.h" +#include "../xr_collide_form.h" +#include "../../xrNetServer/net_utils.h" +#include "clsid_game.h" +#include "../../Include/xrRender/Kinematics.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +u32 CBreakableObject :: m_remove_time =0 ; +float CBreakableObject :: m_damage_threshold =5.f ; +float CBreakableObject :: m_health_threshhold =0.f ; +float CBreakableObject :: m_immunity_factor =0.1f ; +CBreakableObject::CBreakableObject () +{ + Init(); +} + +CBreakableObject::~CBreakableObject () +{ +} + +void CBreakableObject::Load (LPCSTR section) +{ + inherited::Load (section); + m_remove_time=pSettings ->r_u32(section,"remove_time")*1000; + m_health_threshhold=pSettings ->r_float(section,"hit_break_threthhold"); + m_damage_threshold=pSettings ->r_float(section,"collision_break_threthhold"); + m_immunity_factor =pSettings ->r_float(section,"immunity_factor"); + this->shedule.t_min = 1000; + this->shedule.t_max = 1000; +} + +BOOL CBreakableObject::net_Spawn(CSE_Abstract* DC) +{ + + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeObjectBreakable *obj = smart_cast(e); + R_ASSERT (obj); + inherited::net_Spawn (DC); + VERIFY(!collidable.model); + collidable.model = new CCF_Skeleton(this); + // set bone id + R_ASSERT (Visual()&&smart_cast(Visual())); +// IKinematics* K = smart_cast(Visual()); + fHealth = obj->m_health; + processing_deactivate (); + setVisible (TRUE); + setEnabled (TRUE); + CreateUnbroken (); + //CreateBroken (); + bRemoved =false; + //Break (); +// shedule_unregister (); + return (TRUE); +} + +void CBreakableObject::shedule_Update (u32 dt) +{ + inherited::shedule_Update (dt); + if(m_pPhysicsShell&&!bRemoved&&Device.dwTimeGlobal-m_break_time>m_remove_time) SendDestroy(); +} +void CBreakableObject::UpdateCL() +{ + inherited::UpdateCL(); +// Fmatrix d; + if(m_pPhysicsShell&&m_pPhysicsShell->isFullActive())m_pPhysicsShell->InterpolateGlobalTransform(&XFORM()); +} +void CBreakableObject::enable_notificate() +{ + if(b_resived_damage)ProcessDamage(); +} + +//void CBreakableObject::Hit(float P,Fvector &dir, CObject* who,s16 element, +// Fvector p_in_object_space, float impulse, ALife::EHitType hit_type) +void CBreakableObject::Hit (SHit* pHDS) +{ + CheckHitBreak(pHDS->damage(),pHDS->hit_type); + if(m_pPhysicsShell) + { + if(pHDS->hit_type==ALife::eHitTypeExplosion) + { + ApplyExplosion(pHDS->dir,pHDS->impulse); + } + else + { + //. hack: slipch ??? + if ((pHDS->impulse>EPS)&&(BI_NONE != pHDS->bone())) + m_pPhysicsShell->applyImpulseTrace(pHDS->p_in_bone_space,pHDS->dir,pHDS->impulse,pHDS->bone()); + } + } +} + +void CBreakableObject::net_Export(NET_Packet& P) +{ + VERIFY (Local()); +} + +void CBreakableObject::net_Import(NET_Packet& P) +{ + VERIFY (Remote()); +} + +BOOL CBreakableObject::UsedAI_Locations() +{ + return (FALSE); +} + + + +void CBreakableObject::CreateUnbroken() +{ + m_pUnbrokenObject=P_BuildStaticGeomShell(smart_cast(this),ObjectContactCallback); +} +void CBreakableObject::DestroyUnbroken() +{ + if(!m_pUnbrokenObject) return; + m_pUnbrokenObject->Deactivate(); + xr_delete(m_pUnbrokenObject); +} + +//void CBreakableObject::CreateBroken() +//{ + //CPhysicsShell* shell=P_create_splited_Shell(); + //shell->preBuild_FromKinematics(smart_cast(Visual())); + //shell->mXFORM.set(XFORM()); + //shell->set_PhysicsRefObject(this); + ////m_Shell->Build(); + //shell->setDensity(1000.f); + //dMass m; + //dMassSetBox(&m,m_Shell->getMass()/100.f,1.f,1.f,1.f); + //shell->addEquelInertiaToEls(m); + //shell->SmoothElementsInertia(0.3f); + ////shell->SetAirResistance(0.002f*skel_airr_lin_factor, + //// 0.3f*skel_airr_ang_factor); + //ELEMENT_STORAGE& elements = pshell->Elements(); + //ELEMENT_I i=elements.begin(),e=elements.end(); + //for(;e!=i;i++) + //{ + // m_Shells.push_back(P_create_splited_Shell()); + // m_Shells.back()->mXFORM.set(XFORM()); + // m_Shells.back()->add_Element (*i); + // m_Shells.back()->Build(); + //} + +//} +void CBreakableObject::CreateBroken() +{ + processing_activate(); + m_Shell=P_create_splited_Shell(); + m_Shell->preBuild_FromKinematics(smart_cast(Visual())); + m_Shell->mXFORM.set(XFORM()); + //m_Shell->SetAirResistance(0.002f*skel_airr_lin_factor, + // 0.3f*skel_airr_ang_factor); + m_Shell->set_PhysicsRefObject(this); + m_Shell->Build(); + m_Shell->setMass(m_Shell->getMass()*0.1f*100.f); + dMass m; + dMassSetBox(&m,m_Shell->getMass()/100.f,1.f,1.f,1.f); + m_Shell->addEquelInertiaToEls(m); + m_Shell->SmoothElementsInertia(0.3f); + Fobb b; + Visual()->getVisData().box.getradius(b.m_halfsize); + m_Shell->SetMaxAABBRadius(_max(_max(b.m_halfsize.x,b.m_halfsize.y),b.m_halfsize.z)*2.f);//+2.f + +} + +void CBreakableObject::ActivateBroken() +{ + m_pPhysicsShell=m_Shell; + IKinematics* K=smart_cast(Visual()); + m_pPhysicsShell->set_Kinematics(K); + m_pPhysicsShell->RunSimulation(); + m_pPhysicsShell->SetCallbacks(m_pPhysicsShell->GetBonesCallback()); + K->CalculateBones_Invalidate(); + K->CalculateBones(); + m_pPhysicsShell->GetGlobalTransformDynamic(&XFORM()); +} + +void CBreakableObject::net_Destroy() +{ + DestroyUnbroken(); + if(m_Shell) + { + m_Shell->Deactivate (); + xr_delete (m_Shell); + SheduleUnregister (); + } + + m_pPhysicsShell=NULL; + inherited::net_Destroy(); + xr_delete(collidable.model); + Init(); + //Visual()->vis.box.set(m_saved_box); + Render->model_Delete(renderable.visual,TRUE); + cNameVisual_set(""); + +} +void CBreakableObject::Split() +{ + //for (u16 k=0; kLL_BoneCount(); k++){ + + // Fmatrix& M = K->LL_GetTransform(k); + // Fmatrix R; R.setHPB(-0.1,-0.1,-0.1); + // M.mulB (R); + // Fmatrix S; S.scale(0.98f,0.98f,0.98f); + // M.mulB (S); + //} +} + +void CBreakableObject::Break() +{ + if(m_pPhysicsShell)return; + DestroyUnbroken(); + CreateBroken(); + ActivateBroken(); + u16 el_num=m_pPhysicsShell->get_ElementsNumber(); + for(u16 i=0;iget_ElementByStoreOrder(i)->applyImpulseTrace(pos,dir,Random.randF(0.5f,3.f),0); + } + m_break_time=Device.dwTimeGlobal; + SheduleRegister (); +} + +void CBreakableObject::SendDestroy() +{ + if (Local()) DestroyObject (); +// NET_Packet P; +// u_EventGen (P,GE_DESTROY,ID()); +// Msg ("ge_destroy: [%d] - %s",ID(),*cName()); +// if (Local()) u_EventSend (P); + bRemoved=true; +} + +void CBreakableObject::ObjectContactCallback(bool&/**do_colide/**/,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/) +{ + dxGeomUserData* usr_data_1= retrieveGeomUserData(c.geom.g1); + dxGeomUserData* usr_data_2=retrieveGeomUserData(c.geom.g2); + CBreakableObject* this_object; + dBodyID body; + float norm_sign; + if( + usr_data_1&& + usr_data_1->ph_ref_object&& + usr_data_1->ph_ref_object->CLS_ID == CLSID_OBJECT_BREAKABLE + ) { + body=dGeomGetBody(c.geom.g2); + if(!body) return; + this_object=static_cast(usr_data_1->ph_ref_object); + norm_sign=-1.f; + } + else if( + usr_data_2&& + usr_data_2->ph_ref_object&& + usr_data_2->ph_ref_object->CLS_ID == CLSID_OBJECT_BREAKABLE + ){ + body=dGeomGetBody(c.geom.g1); + if(!body) return; + this_object=static_cast(usr_data_2->ph_ref_object); + norm_sign=1.f; + } + else return; + + if(!this_object->m_pUnbrokenObject) return; + float c_damage=E_NlS(body,c.geom.normal,norm_sign); + if(this_object->m_damage_thresholdm_max_frame_damageb_resived_damage=true; + this_object->m_max_frame_damage=c_damage; + this_object->m_contact_damage_pos.set(c.geom.pos[0],c.geom.pos[1],c.geom.pos[2]); + this_object->m_contact_damage_dir.set(-c.geom.normal[0]*norm_sign,-c.geom.normal[1]*norm_sign,-c.geom.normal[2]*norm_sign); + } +} + +void CBreakableObject::ProcessDamage() +{ + NET_Packet P; + SHit HS; + HS.GenHeader (GE_HIT, ID()); + HS.whoID = (ID()); + HS.weaponID = (ID()); + HS.dir = (m_contact_damage_dir); + HS.power = (m_max_frame_damage); + HS.boneID = (PKinematics(Visual())->LL_GetBoneRoot()); + HS.p_in_bone_space = (m_contact_damage_pos); + HS.impulse = (0.f); + HS.hit_type = (ALife::eHitTypeStrike); + HS.Write_Packet (P); + + u_EventSend (P); + + m_max_frame_damage = 0.f; + b_resived_damage =false; +} +void CBreakableObject::CheckHitBreak(float power,ALife::EHitType hit_type) +{ + if( hit_type!=ALife::eHitTypeStrike) + { + float res_power=power*m_immunity_factor; + if(power>m_health_threshhold) fHealth-=res_power; + } + if(fHealth<=0.f) + { + Break();return; + } + + if(hit_type==ALife::eHitTypeStrike)Break(); +} + +void CBreakableObject::ApplyExplosion(const Fvector &dir,float impulse) +{ + if(!m_pPhysicsShell) return; + Fvector pos;pos.set(0.f,0.f,0.f); + u16 el_num=m_pPhysicsShell->get_ElementsNumber(); + for(u16 i=0;iget_ElementByStoreOrder(i); + element->get_MaxAreaDir(max_area_dir); + float sign=max_area_dir.dotproduct(dir)>0.f ? 1.f : -1.f; + max_area_dir.mul(sign); + element->applyImpulseTrace(pos,max_area_dir,impulse/el_num,0); + } +} + +void CBreakableObject::Init() +{ + fHealth = 1.f; + m_pUnbrokenObject = NULL; + m_Shell = NULL; + bRemoved = false; + m_max_frame_damage = 0.f; + b_resived_damage = false; + //m_damage_threshold =5.f; + //m_health_threshhold =0.f +} + diff --git a/src/xrGameLA/BreakableObject.h b/src/xrGameLA/BreakableObject.h new file mode 100644 index 000000000..700c0d631 --- /dev/null +++ b/src/xrGameLA/BreakableObject.h @@ -0,0 +1,67 @@ +// DummyObject.h: interface for the CHangingLamp class. +// +////////////////////////////////////////////////////////////////////// + +#ifndef BreakableObjectH +#define BreakableObjectH + +#pragma once + +#include "physicsshellholder.h" + +class CPHStaticGeomShell; +struct dContact; +struct SGameMtl; + +class CBreakableObject: public CPhysicsShellHolder { + typedef CPhysicsShellHolder inherited; +private: + bool b_resived_damage; + float m_max_frame_damage; +static float m_damage_threshold; +static float m_health_threshhold; +static float m_immunity_factor; + Fvector m_contact_damage_pos; + Fvector m_contact_damage_dir; + + float fHealth; + CPHStaticGeomShell *m_pUnbrokenObject; + CPhysicsShell *m_Shell; +static u32 m_remove_time; + u32 m_break_time; + bool bRemoved; +// Fbox m_saved_box; +public: + CBreakableObject (); + virtual ~CBreakableObject (); + + virtual void Load ( LPCSTR section); + virtual BOOL net_Spawn ( CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void shedule_Update ( u32 dt); // Called by sheduler + virtual void UpdateCL (); + virtual BOOL renderable_ShadowGenerate ( ) { return FALSE; } + virtual BOOL renderable_ShadowReceive ( ) { return TRUE; } + + virtual void Hit (SHit* pHDS); + + virtual void net_Export (NET_Packet& P); + virtual void net_Import (NET_Packet& P); + virtual BOOL UsedAI_Locations (); +private: + void Init (); + void CreateUnbroken (); + void CreateBroken (); + void DestroyUnbroken (); + void ActivateBroken (); + void Split (); + void Break (); + void ApplyExplosion (const Fvector &dir,float impulse); + void CheckHitBreak (float power,ALife::EHitType hit_type); + void ProcessDamage (); + void SendDestroy (); + void __stdcall enable_notificate (); + static void ObjectContactCallback(bool& /**do_colide/**/,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/); +}; + +#endif //BreakableObjectH diff --git a/src/xrGameLA/CHelmet.cpp b/src/xrGameLA/CHelmet.cpp new file mode 100644 index 000000000..5fbf5286c --- /dev/null +++ b/src/xrGameLA/CHelmet.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" + +#include "customoutfit.h" +#include "PhysicsShell.h" +#include "inventory_space.h" +#include "Inventory.h" +#include "Actor.h" +#include "game_cl_base.h" +#include "Level.h" +#include "BoneProtections.h" +#include "../Include/xrRender/Kinematics.h" +#include "player_hud.h" +#include "CHelmet.h" + +CHelmet::CHelmet() +{ + m_baseSlot = HELMET_SLOT; +} + +void CHelmet::Load(LPCSTR section) +{ + inherited::Load(section); + + m_fShowNearestEnemiesDistance = READ_IF_EXISTS(pSettings, r_float, section, "nearest_enemies_show_dist", 0.0f); +} + +//------------>------Upgrades------<------------- + +bool CHelmet::install_upgrade_impl(LPCSTR section, bool test) +{ + bool result = inherited::install_upgrade_impl(section, test); + + result |= process_if_exists(section, "nearest_enemies_show_dist", &CInifile::r_float, m_fShowNearestEnemiesDistance, test); + + return result; +} \ No newline at end of file diff --git a/src/xrGameLA/CHelmet.h b/src/xrGameLA/CHelmet.h new file mode 100644 index 000000000..4ae635d3d --- /dev/null +++ b/src/xrGameLA/CHelmet.h @@ -0,0 +1,19 @@ +#pragma once + +#include "OutfitBase.h" + +struct SBoneProtections; + +class CHelmet: public COutfitBase { +private: + typedef COutfitBase inherited; +public: + CHelmet (void); + + virtual void Load (LPCSTR section); + + float m_fShowNearestEnemiesDistance; + +protected: + virtual bool install_upgrade_impl ( LPCSTR section, bool test ); +}; \ No newline at end of file diff --git a/src/xrGameLA/CMakeLists.txt b/src/xrGameLA/CMakeLists.txt new file mode 100644 index 000000000..e78c07a22 --- /dev/null +++ b/src/xrGameLA/CMakeLists.txt @@ -0,0 +1,178 @@ +project(xrGameLA) +set(XGM_SRC_FILES) + +# Files import +file(GLOB_RECURSE XGM_SOURCE_ALL_FILES CONFIGURE_DEPENDS + "*.cpp" + "*.cxx" + "*.c" + "*.h" + "../xrGameSpy/gamespy/md5c.c" +) +file(GLOB_RECURSE XGM_SOURCE_AI_FILES + "ai/*.*" + "ai_*.*" +) +file(GLOB_RECURSE XGM_SOURCE_SC_FILES + "smart_cover*.*" +) +file(GLOB_RECURSE XGM_SOURCE_SR_FILES + "space_restriction*.*" +) +file(GLOB_RECURSE XGM_SOURCE_UI_FILES + "ui/*.*" + "ui*.*" + "UI*.*" +) +file(GLOB_RECURSE XGM_SOURCE_IKAN_FILES + "ik/*.*" + "ik_*.*" + "IK*.*" +) +file(GLOB_RECURSE XGM_SOURCE_ACTOR_FILES + "Actor*.*" +) +file(GLOB_RECURSE XGM_SOURCE_LEVEL_FILES + "Level*.*" + "level*.*" +) +file(GLOB_RECURSE XGM_SOURCE_INV_FILES + "inventory*.*" + "Inventory*.*" +) +file(GLOB_RECURSE XGM_SOURCE_CAR_FILES + "Car*.*" + "car*.*" +) +file(GLOB XGM_SOURCE_ALIFE_FILES + "alife_*.*" +) +file(GLOB_RECURSE XGM_SOURCE_SCRIPT_FILES + "script_*.*" +) +file(GLOB_RECURSE XGM_SOURCE_SERV_FILES + "xrServer*.*" + "game_*.*" +) + +file(GLOB_RECURSE XGM_SOURCE_AI_BASE_FILES "ai/monsters/base_monster/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_GS_FILES "ai/monsters/group_states/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_STATE_FILES "ai/monsters/states/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_BS_FILES "ai/monsters/bloodsucker/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_DOG_FILES "ai/monsters/dog/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_BOAR_FILES "ai/monsters/boar/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_BURER_FILES "ai/monsters/burer/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_CAT_FILES "ai/monsters/cat/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_CHIM_FILES "ai/monsters/chimera/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_CONT_FILES "ai/monsters/controller/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_FLSH_FILES "ai/monsters/flesh/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_FACT_FILES "ai/monsters/fracture/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_POLT_FILES "ai/monsters/poltergeist/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_PDOG_FILES "ai/monsters/pseudodog/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_PGIG_FILES "ai/monsters/pseudogigant/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_RATS_FILES "ai/monsters/rats/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_SNORK_FILES "ai/monsters/snork/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_TUSH_FILES "ai/monsters/tushkano/*.*") +file(GLOB_RECURSE XGM_SOURCE_AI_ZOMB_FILES "ai/monsters/zombie/*.*") + +file(GLOB_RECURSE XGM_SOURCE_LEGACY_FILES "Legacy/*.*") + +# Source groups +source_group("core" FILES ${XGM_SOURCE_ALL_FILES}) +source_group("ui" FILES ${XGM_SOURCE_UI_FILES}) +#source_group("ai" FILES ${XGM_SOURCE_AI_FILES}) +source_group("ai/car" FILES ${XGM_SOURCE_CAR_FILES}) +source_group("ai/smart_cover" FILES ${XGM_SOURCE_SC_FILES}) +source_group("ai/space_restictor" FILES ${XGM_SOURCE_SR_FILES}) +source_group("ai/monsters/base" FILES ${XGM_SOURCE_AI_BASE_FILES}) +source_group("ai/monsters/base/group_states" FILES ${XGM_SOURCE_AI_GS_FILES}) +source_group("ai/monsters/base/states" FILES ${XGM_SOURCE_AI_STATE_FILES}) +source_group("ai/monsters/boar" FILES ${XGM_SOURCE_AI_BOAR_FILES}) +source_group("ai/monsters/bloodsucker" FILES ${XGM_SOURCE_AI_BS_FILES}) +source_group("ai/monsters/dog" FILES ${XGM_SOURCE_AI_DOG_FILES}) +source_group("ai/monsters/burer" FILES ${XGM_SOURCE_AI_BURER_FILES}) +source_group("ai/monsters/cat" FILES ${XGM_SOURCE_AI_CAT_FILES}) +source_group("ai/monsters/chimera" FILES ${XGM_SOURCE_AI_CHIM_FILES}) +source_group("ai/monsters/controller" FILES ${XGM_SOURCE_AI_CONT_FILES}) +source_group("ai/monsters/flesh" FILES ${XGM_SOURCE_AI_FLSH_FILES}) +source_group("ai/monsters/fracture" FILES ${XGM_SOURCE_AI_FACT_FILES}) +source_group("ai/monsters/poltergeist" FILES ${XGM_SOURCE_AI_POLT_FILES}) +source_group("ai/monsters/pseudodog" FILES ${XGM_SOURCE_AI_PDOG_FILES}) +source_group("ai/monsters/pseudogigant" FILES ${XGM_SOURCE_AI_PGIG_FILES}) +source_group("ai/monsters/rats" FILES ${XGM_SOURCE_AI_RATS_FILES}) +source_group("ai/monsters/snork" FILES ${XGM_SOURCE_AI_SNORK_FILES}) +source_group("ai/monsters/tushkano" FILES ${XGM_SOURCE_AI_TUSH_FILES}) +source_group("ai/monsters/zombie" FILES ${XGM_SOURCE_AI_ZOMB_FILES}) +source_group("alife" FILES ${XGM_SOURCE_ALIFE_FILES}) +source_group("script" FILES ${XGM_SOURCE_SCRIPT_FILES}) +source_group("server" FILES ${XGM_SOURCE_SERV_FILES}) +source_group("actor" FILES ${XGM_SOURCE_ACTOR_FILES}) +source_group("level" FILES ${XGM_SOURCE_LEVEL_FILES}) +source_group("ikan" FILES ${XGM_SOURCE_IKAN_FILES}) +source_group("inventory" FILES ${XGM_SOURCE_INV_FILES}) +source_group("Legacy" FILES ${XGM_SOURCE_LEGACY_FILES}) + +# Apply list +list(APPEND XGM_SRC_FILES ${XGM_SOURCE_ALL_FILES}) + +# Remove unused files +list(FILTER XGM_SRC_FILES EXCLUDE REGEX "DynamicHeightMap.*") +list(FILTER XGM_SRC_FILES EXCLUDE REGEX "LevelFogOfWar.*") +list(FILTER XGM_SRC_FILES EXCLUDE REGEX "ai/monsters/rats/ai_rat_fsm.*") +list(FILTER XGM_SRC_FILES EXCLUDE REGEX "ui/IUWpnParams.*") +list(FILTER XGM_SRC_FILES EXCLUDE REGEX "ui/UIRankFaction.*") +list(FILTER XGM_SRC_FILES EXCLUDE REGEX "ui/UIRankFraction.*") + +# xrGameLA project +add_library(xrGameLA SHARED ${XGM_SRC_FILES}) + +if (IXRAY_UNITYBUILD) + set_target_properties(xrGameLA PROPERTIES UNITY_BUILD ON UNITY_BUILD_BATCH_SIZE 32) + target_compile_options(xrGameLA PRIVATE /bigobj) + set_source_files_properties(script_engine_export.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) +endif() + +# Include directories +target_include_directories(xrGameLA PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") +target_include_directories(xrGameLA PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/ui/") +target_include_directories(xrGameLA PUBLIC "${IXRAY_SDK_INC}") + +# Project defines +target_compile_definitions(xrGameLA PRIVATE _USRDLL) +target_compile_definitions(xrGameLA PRIVATE XRGAME_EXPORTS) +target_compile_definitions(xrGameLA PRIVATE dSINGLE) + +if (IXRAY_MP) + target_compile_definitions(xrGameLA PRIVATE XR_MP_BUILD) +endif() + +add_compile_options(/fp:fast) +add_compile_options(/FS) +target_compile_definitions(xrGameLA PRIVATE "$<$:DEBUG>") + +# Linker list +target_link_libraries(xrGameLA PUBLIC xrAbstractions) +target_link_libraries(xrGameLA PUBLIC xrParticles) +target_link_libraries(xrGameLA PUBLIC luabind) +target_link_libraries(xrGameLA PUBLIC crypto) + +target_link_libraries(xrGameLA PRIVATE "shlwapi.lib") + +if(IXRAY_MP) + target_link_libraries(xrGameLA PUBLIC CxImage) +endif() + +# Move precompiled header +set_target_properties(xrGameLA PROPERTIES DISABLE_PRECOMPILE_HEADERS ON) + +if (NOT IXRAY_UNITYBUILD) + set_target_properties(xrGameLA PROPERTIES COMPILE_FLAGS "/Yustdafx.h") + set_source_files_properties(stdafx.cpp PROPERTIES COMPILE_FLAGS "/Ycstdafx.h") + target_precompile_headers(xrGameLA PUBLIC "stdafx.h") + + file(GLOB_RECURSE GAME_SOURCE_ALL_C_FILES "*.c" "../xrGameSpy/gamespy/md5c.c") + set_source_files_properties(${GAME_SOURCE_ALL_C_FILES} PROPERTIES SKIP_PRECOMPILE_HEADERS ON) +endif() + +file(GLOB_RECURSE GAME_SOURCE_PCH_FILES "stdafx.*") +source_group("pch" FILES ${GAME_SOURCE_PCH_FILES}) diff --git a/src/xrGameLA/CalculateTriangle.h b/src/xrGameLA/CalculateTriangle.h new file mode 100644 index 000000000..3f9590d8d --- /dev/null +++ b/src/xrGameLA/CalculateTriangle.h @@ -0,0 +1,178 @@ +#include "ExtendedGeom.h" +#include "MathUtils.h" +#include "Level.h" +#include "Geometry.h" +#include "tri-colliderknoopc/dtricollidermath.h" +ICF void GetNormal(CDB::TRI*XTri,Fvector &n) +{ + const Fvector* V_array=Level().ObjectSpace.GetStaticVerts(); + Fvector sd1;sd1.sub(V_array[XTri->verts[1]],V_array[XTri->verts[0]]); + Fvector sd2;sd2.sub(V_array[XTri->verts[2]],V_array[XTri->verts[1]]); + n.crossproduct(sd1,sd2); +} +ICF void InitTriangle(CDB::TRI* XTri,Triangle& triangle) +{ + const Fvector* V_array=Level().ObjectSpace.GetStaticVerts(); + const float* VRT[3]={(dReal*)&V_array[XTri->verts[0]],(dReal*)&V_array[XTri->verts[1]],(dReal*)&V_array[XTri->verts[2]]}; + dVectorSub(triangle.side0,VRT[1],VRT[0]) ; + dVectorSub(triangle.side1,VRT[2],VRT[1]) ; + triangle.T=XTri ; + dCROSS(triangle.norm,=,triangle.side0,triangle.side1) ; + cast_fv(triangle.norm).normalize() ; + triangle.pos=dDOT(VRT[0],triangle.norm) ; +} +ICF void CalculateTriangle(CDB::TRI* XTri,const float* pos,Triangle& triangle) +{ + InitTriangle(XTri,triangle); + triangle.dist=dDOT(pos,triangle.norm)-triangle.pos; +} +ICF void CalculateTriangle(CDB::TRI* XTri,dGeomID g,Triangle& triangle) +{ + dVector3 v ; + dMatrix3 m ; + const float *p =NULL ; + const float *r =NULL ; + VERIFY (g) ; + CODEGeom::get_final_tx (g,p,r,v,m) ; + VERIFY (p) ; + CalculateTriangle (XTri,p,triangle) ; + +} + +inline bool TriContainPoint(const dReal* v0,const dReal* v1,const dReal* v2, + const dReal* triSideAx0,const dReal* triSideAx1,const dReal* triSideAx2, + const dReal* triAx, const dReal* pos,u16 &c){ + c=0; + dVector3 cross0, cross1, cross2; + dCROSS(cross0,=,triAx,triSideAx0); + if(dDOT(cross0,pos)T; + const float* VRT[3]={(dReal*)&V_array[XTri->verts[0]],(dReal*)&V_array[XTri->verts[1]],(dReal*)&V_array[XTri->verts[2]]}; + return TriContainPoint(VRT[0],VRT[1],VRT[2],T->norm,T->side0,T->side1,pos,c); +} + +enum ETriDist +{ + tdBehind, + tdPlane, + tdSide, + tdVert +}; + +IC float DistToFragmenton(const dReal *point,const dReal* pt1, const dReal* pt2,dReal *p,dReal* to_point,u16 &c) +{ + dVector3 V={pt2[0]-pt1[0],pt2[1]-pt1[1],pt2[2]-pt1[2]}; + dVector3 L={pt1[0]-point[0],pt1[1]-point[1],pt1[2]-point[2]}; + dReal sq_mag_V=dDOT(V,V); + dReal dot_L_V=dDOT(L,V); + dReal t=-dot_L_V/sq_mag_V;//t + if(t<0.f) + { + c=1; + dVectorSet(p,pt1); + dVectorSet(to_point,L); + return dSqrt(dDOT(L,L)); + } + else if (t>1.f) + { + c=2; + dVectorSet(p,pt2); + dVectorSub(L,pt2,point); + dVectorSet(to_point,L); + return dSqrt(dDOT(L,L)); + } + c=0; + dVector3 Pc={pt1[0]+t*V[0],pt1[1]+t*V[1],pt1[2]+t*V[2]}; + dVectorSet(p,Pc); + dVector3 Dc={point[0]-Pc[0],point[1]-Pc[1],point[2]-Pc[2]}; + dVectorSet(to_point,Dc); + return dSqrt(dDOT(Dc,Dc)); +} +ICF float DistToTri(Triangle* T,const float *pos,float *dir,float* p,ETriDist &c,const Fvector *V_array) +{ + if(!TriPlaneContainPoint(T)) + { + c=tdBehind; return -1.f; + } + u16 code; + if(TriContainPoint(T,pos,code)) + { + c=tdPlane; + + cast_fv(p).mad(cast_fv(pos), cast_fv(T->norm), -T->dist); + cast_fv(dir).invert(cast_fv(T->norm)); + return T->dist; + } + CDB::TRI *XTri=T->T; + const float* VRT[3]={(dReal*)&V_array[XTri->verts[0]],(dReal*)&V_array[XTri->verts[1]],(dReal*)&V_array[XTri->verts[2]]}; + u16 cd=u16(-1); + float tdist=0.f; + + switch (code) + { + case 1: tdist=DistToFragmenton(pos,VRT[0],VRT[1],p,dir,cd);break; + case 2: tdist=DistToFragmenton(pos,VRT[1],VRT[2],p,dir,cd);break; + case 3: tdist=DistToFragmenton(pos,VRT[2],VRT[0],p,dir,cd);break; + default: NODEFAULT; + } + switch (cd) + { + case 0: + if(tdist>EPS_S) cast_fv(dir).mul(1.f/tdist); + c=tdSide; + return tdist; + case 1: + dVectorSet(p,VRT[code-1]); + break; + case 2: + dVectorSet(p,VRT[code%3]); + break; + default: NODEFAULT; + } + dVectorSub(dir,p,pos); + float sqd=dDOT(dir,dir); + if(sqd>EPS_S) + { + tdist=dSqrt(sqd); + cast_fv(dir).mul(1.f/tdist); + } + else tdist =0.f; + return tdist; + //u16 c2; + //float tdist2=DistToFragmenton(pos,VRT[0],VRT[1],p,dir,c); + //u16 c3; + //float tdist3=DistToFragmenton(pos,VRT[0],VRT[1],p,dir,c); + //u16 cc;float tdist; + //MIN_OF(tdist1,cc=c1;tdist=tdist1,tdist2,cc=c2;tdist=tdist2,tdist3,cc=c3;tdist=tdist3); + + //return _min(_min(DistToFragmenton(pos))) +} \ No newline at end of file diff --git a/src/xrGameLA/CameraEffector.cpp b/src/xrGameLA/CameraEffector.cpp new file mode 100644 index 000000000..a27b824da --- /dev/null +++ b/src/xrGameLA/CameraEffector.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/src/xrGameLA/CameraEffector.h b/src/xrGameLA/CameraEffector.h new file mode 100644 index 000000000..8e66dccb4 --- /dev/null +++ b/src/xrGameLA/CameraEffector.h @@ -0,0 +1,41 @@ +#pragma once + +#include "../cameramanager.h" +#include "../effector.h" +#include "../effectorPP.h" + +#define eStartEffectorID 50 + +#define effHit (eStartEffectorID+1) +#define effAlcohol (eStartEffectorID+2) +#define effFireHit (eStartEffectorID+3) +#define effExplodeHit (eStartEffectorID+4) +#define effNightvision (eStartEffectorID+5) +#define effPsyHealth (eStartEffectorID+6) +#define effControllerAura (eStartEffectorID+7) +#define effControllerAura2 (eStartEffectorID+8) +#define effBigMonsterHit (eStartEffectorID+9) +#define effActorDeath (eStartEffectorID+10) + +#define effPoltergeistTeleDetectStartEffect 2048 +// warning: ~50 constants after effPoltergeistTeleDetectStartEffect are reserved for poltergeists + +#define effCustomEffectorStartID 10000 +// warning: constants after effCustomEffectorStartID are reserved + +#define eCEFall ((ECamEffectorType)(cefNext+1)) +#define eCENoise ((ECamEffectorType)(cefNext+2)) +#define eCEShot ((ECamEffectorType)(cefNext+3)) +#define eCEZoom ((ECamEffectorType)(cefNext+4)) +#define eCERecoil ((ECamEffectorType)(cefNext+5)) +#define eCEBobbing ((ECamEffectorType)(cefNext+6)) +#define eCEHit ((ECamEffectorType)(cefNext+7)) +#define eCEUser ((ECamEffectorType)(cefNext+11)) +#define eCEControllerPsyHit ((ECamEffectorType)(cefNext+12)) +#define eCEVampire ((ECamEffectorType)(cefNext+13)) +#define eCEPseudoGigantStep ((ECamEffectorType)(cefNext+14)) +#define eCEMonsterHit ((ECamEffectorType)(cefNext+15)) +#define eCEDOF ((ECamEffectorType)(cefNext+16)) +#define eCEWeaponAction ((ECamEffectorType)(cefNext+17)) +#define eCEActorMoving ((ECamEffectorType)(cefNext+18)) +#define eCESetActorDirection ((ECamEffectorType)(cefNext+19)) diff --git a/src/xrGameLA/CameraFirstEye.cpp b/src/xrGameLA/CameraFirstEye.cpp new file mode 100644 index 000000000..ef802d7de --- /dev/null +++ b/src/xrGameLA/CameraFirstEye.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" +#pragma hdrstop + +#include "CameraFirstEye.h" +#include "xr_level_controller.h" +#include "../xr_object.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CCameraFirstEye::CCameraFirstEye(CObject* p, u32 flags ) : CCameraBase(p, flags) +{ +} + +CCameraFirstEye::~CCameraFirstEye() +{ +} + +void CCameraFirstEye::Load(LPCSTR section) +{ + inherited::Load (section); + style = csFirstEye; +} + +void CCameraFirstEye::Update(Fvector& point, Fvector& noise_dangle) +{ + Fmatrix mR, R; + Fmatrix rX, rY, rZ; + rX.rotateX (noise_dangle.x); + rY.rotateY (-noise_dangle.y); + rZ.rotateZ (noise_dangle.z); + R.mul_43 (rY,rX); + R.mulB_43 (rZ); + + mR.identity (); + Fquaternion Q; + Q.rotationYawPitchRoll(roll,yaw,pitch); + mR.rotation (Q); + mR.transpose (); + mR.mulB_43 (R); + + vDirection.set (mR.k); + vNormal.set (mR.j); + + if (m_Flags.is(flRelativeLink)) { + parent->XFORM().transform_dir (vDirection); + parent->XFORM().transform_dir (vNormal); + } + + vPosition.set (point); +} + +void CCameraFirstEye::Move( int cmd, float val, float factor ) +{ + if (bClampPitch) + { + while (pitch < lim_pitch[0]) + pitch += PI_MUL_2; + while (pitch > lim_pitch[1]) + pitch -= PI_MUL_2; + }; + switch (cmd){ + case kDOWN: pitch -= val?val:(rot_speed.y*Device.fTimeDelta/factor); break; + case kUP: pitch += val?val:(rot_speed.y*Device.fTimeDelta/factor); break; + case kLEFT: yaw -= val?val:(rot_speed.x*Device.fTimeDelta/factor); break; + case kRIGHT: yaw += val?val:(rot_speed.x*Device.fTimeDelta/factor); break; + } + if (bClampYaw) clamp(yaw,lim_yaw[0],lim_yaw[1]); + if (bClampPitch) clamp(pitch,lim_pitch[0],lim_pitch[1]); +} + +void CCameraFirstEye::OnActivate( CCameraBase* old_cam ) +{ + if (old_cam&&(m_Flags.is(flRelativeLink)==old_cam->m_Flags.is(flRelativeLink))) + yaw = (old_cam)->yaw; +} diff --git a/src/xrGameLA/CameraFirstEye.h b/src/xrGameLA/CameraFirstEye.h new file mode 100644 index 000000000..8152b15b9 --- /dev/null +++ b/src/xrGameLA/CameraFirstEye.h @@ -0,0 +1,23 @@ +#ifndef __CAMERA_FE_H__ +#define __CAMERA_FE_H__ + +#include "../CameraBase.h" + +class CCameraFirstEye : public CCameraBase +{ + typedef CCameraBase inherited; +public: + CCameraFirstEye ( CObject* p, u32 flags=0); + virtual ~CCameraFirstEye( ); + + virtual void Load (LPCSTR section); + virtual void Move ( int cmd, float val=0, float factor=1.0f ); + + virtual void OnActivate ( CCameraBase* old_cam ); + virtual void Update ( Fvector& point, Fvector& noise_angle ); + + virtual float GetWorldYaw ( ) { return -yaw; }; + virtual float GetWorldPitch ( ) { return pitch; }; +}; + +#endif // __CAMERALOOK_H__ diff --git a/src/xrGameLA/Car.cpp b/src/xrGameLA/Car.cpp new file mode 100644 index 000000000..611fc5e48 --- /dev/null +++ b/src/xrGameLA/Car.cpp @@ -0,0 +1,2216 @@ +#include "stdafx.h" +#include "pch_script.h" +#include "ParticlesObject.h" +#include "Physics.h" + +#ifdef DEBUG +# include "../StatGraph.h" +# include "PHDebug.h" +#endif // DEBUG + +#include "ai_space.h" +#include "alife_simulator.h" +#include "script_engine.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "car.h" +#include "hudmanager.h" +#include "cameralook.h" +#include "camerafirsteye.h" +#include "Actor.h" +#include "ActorEffector.h" +#include "math.h" +#include "script_entity_action.h" +#include "inventory.h" +#include "xrserver_objects_alife_items.h" +#include "../Include/xrRender/Kinematics.h" +#include "level.h" +#include "ui/UIMainIngameWnd.h" +#include "CarWeapon.h" +#include "game_object_space.h" +#include "../GameMtlLib.h" +#include "PHActivationShape.h" +#include "CharacterPhysicsSupport.h" +#include "car_memory.h" +#include "HudManager.h" +#include "UIGameSP.h" + +BONE_P_MAP CCar::bone_map=BONE_P_MAP(); + +extern CPHWorld* ph_world; + +CCar::CCar() +{ + m_memory = NULL; + m_driver_anim_type = 0; + m_bone_steer = BI_NONE; + m_bone_trunk = BI_NONE; + active_camera = 0; + camera[ectFirst]= new CCameraFirstEye(this, CCameraBase::flRelativeLink|CCameraBase::flPositionRigid); + camera[ectFirst]->tag = ectFirst; + camera[ectFirst]->Load("car_firsteye_cam"); + + camera[ectChase]= new CCameraLook(this, CCameraBase::flRelativeLink); + camera[ectChase]->tag = ectChase; + camera[ectChase]->Load("car_look_cam"); + + camera[ectFree] = new CCameraLook(this); + camera[ectFree]->tag = ectFree; + camera[ectFree]->Load("car_free_cam"); + OnCameraChange(ectFirst); + + m_repairing =false; + + /////////////////////////////// + ////////////////////////////// + ///////////////////////////// + b_wheels_limited=false; + b_engine_on=false; + e_state_steer=idle; + e_state_drive=neutral; + m_current_gear_ratio=dInfinity; + rsp=false;lsp=false;fwp=false;bkp=false;brp=false; + /////////////////////////////// + ////////////////////////////// + ///////////////////////////// + m_exhaust_particles ="vehiclefx\\exhaust_1"; + m_car_sound = new SCarSound(this); + + //у машины слотов в инвентаре нет + inventory().SetSlotsUseful(false); + m_doors_torque_factor = 2.f; + m_power_increment_factor=0.5f; + m_rpm_increment_factor=0.5f; + m_power_decrement_factor=0.5f; + m_rpm_decrement_factor=0.5f; + b_breaks=false; + m_break_start=0.f; + m_break_time=1.; + m_breaks_to_back_rate=1.f; + + b_exploded=false; + m_car_weapon=NULL; + m_power_neutral_factor=0.25f; + m_steer_angle=0.f; + m_current_rpm = 0.f; + m_current_engine_power = 0.f; + +#ifdef DEBUG + InitDebug(); +#endif +} + +CCar::~CCar(void) +{ + xr_delete (camera[0]); + xr_delete (camera[1]); + xr_delete (camera[2]); + xr_delete (m_car_sound); + ClearExhausts (); + xr_delete (m_car_weapon); + xr_delete (m_memory); + // xr_delete (l_tpEntityAction); +} + +void CCar::reinit () +{ + CEntity::reinit (); + CScriptEntity::reinit (); + CInventoryOwner::reinit (); + if(m_memory) + m_memory->reinit (); +} + +void CCar::reload (LPCSTR section) +{ + CEntity::reload (section); + CInventoryOwner::reload (section); + if(m_memory) + m_memory->reload (section); + // fuel load + m_fuel_tank = READ_IF_EXISTS(pSettings, r_float, section, "fuel_tank", 10.0); + m_fuel_consumption = READ_IF_EXISTS(pSettings, r_float, section, "fuel_consumption", 0.0005); + inventory().SetMaxWeight(READ_IF_EXISTS(pSettings, r_float, section, "trunk_capacity", 1000.f)); +} + +void CCar::cb_Steer (CBoneInstance* B) +{ + VERIFY2(fsimilar(DET(B->mTransform),1.f,DET_CHECK_EPS),"Bones receive returns 0 matrix"); + CCar* C = static_cast(B->callback_param()); + Fmatrix m; + + + m.rotateZ(C->m_steer_angle); + + B->mTransform.mulB_43 (m); +#ifdef DEBUG + if( !fsimilar(DET(B->mTransform),1.f,DET_CHECK_EPS) ){ + + Log("RotatingZ angle=",C->m_steer_angle); + VERIFY2(0,"Bones callback returns BAD!!! matrix"); + } +#endif +} + +// Core events +void CCar::Load ( LPCSTR section ) +{ + inherited::Load (section); + + //CPHSkeleton::Load(section); + ISpatial* self = smart_cast (this); + if (self) self->spatial.type |= STYPE_VISIBLEFORAI; +} + +BOOL CCar::net_Spawn (CSE_Abstract* DC) +{ +#ifdef DEBUG + InitDebug(); +#endif + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeCar *co=smart_cast(e); + BOOL R = inherited::net_Spawn(DC); + + PKinematics(Visual())->CalculateBones_Invalidate(); + PKinematics(Visual())->CalculateBones(TRUE); + + CPHSkeleton::Spawn(e); + setEnabled (TRUE); + setVisible (TRUE); + PKinematics(Visual())->CalculateBones_Invalidate(); + PKinematics(Visual())->CalculateBones(TRUE); + m_fSaveMaxRPM = m_max_rpm; + SetfHealth (co->health); + m_fuel = co->fuel; + if(!g_Alive()) b_exploded=true; + else b_exploded=false; + + CDamagableItem::RestoreEffect(); + + + CInifile* pUserData = PKinematics(Visual())->LL_UserData(); + if(pUserData->section_exist("destroyed")) + CPHDestroyable::Load(pUserData,"destroyed"); + if(pUserData->section_exist("mounted_weapon_definition")) + m_car_weapon = new CCarWeapon(this); + + if (pUserData->line_exist("car_definition","trunk_bone")) + m_bone_trunk = PKinematics(Visual())->LL_BoneID(pUserData->r_string("car_definition","trunk_bone")); + + if(pUserData->section_exist("visual_memory_definition")) + { + m_memory = new car_memory(this); + m_memory->reload (pUserData->r_string("visual_memory_definition", "section")); + } + + CInventoryOwner::net_Spawn(DC); + + return (CScriptEntity::net_Spawn(DC) && R); + +} + +void CCar::ActorObstacleCallback (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + if(!do_colide) + { + if(material_1&&material_1->Flags.test(SGameMtl::flActorObstacle))do_colide=true; + if(material_2&&material_2->Flags.test(SGameMtl::flActorObstacle))do_colide=true; + } +} + +void CCar::SpawnInitPhysics (CSE_Abstract *D) +{ + CSE_PHSkeleton *so = smart_cast(D); + R_ASSERT (so); + ParseDefinitions ();//parse ini filling in m_driving_wheels,m_steering_wheels,m_breaking_wheels + CreateSkeleton (D);//creates m_pPhysicsShell & fill in bone_map + IKinematics *K =smart_cast(Visual()); + K->CalculateBones_Invalidate();//this need to call callbacks + K->CalculateBones (TRUE); + Init ();//inits m_driving_wheels,m_steering_wheels,m_breaking_wheels values using recieved in ParceDefinitions & from bone_map + //PPhysicsShell()->add_ObjectContactCallback(ActorObstacleCallback); + SetDefaultNetState (so); + CPHUpdateObject::Activate (); +} + +void CCar::net_Destroy() +{ +#ifdef DEBUG + DBgClearPlots(); +#endif + IKinematics* pKinematics=smart_cast(Visual()); + if(m_bone_steer!=BI_NONE) + { + + pKinematics->LL_GetBoneInstance(m_bone_steer).reset_callback(); + + } + CScriptEntity::net_Destroy(); + inherited::net_Destroy(); + CExplosive::net_Destroy(); + CInventoryOwner::net_Destroy(); + if(m_pPhysicsShell) + { + m_pPhysicsShell->Deactivate(); + m_pPhysicsShell->ZeroCallbacks(); + xr_delete(m_pPhysicsShell); + } + CHolderCustom::detach_Actor(); + ClearExhausts(); + m_wheels_map.clear(); + m_steering_wheels.clear(); + m_driving_wheels.clear(); + m_exhausts.clear(); + m_breaking_wheels.clear(); + m_doors.clear(); + m_gear_ratious.clear(); + m_car_sound->Destroy(); + CPHUpdateObject::Deactivate(); + CPHSkeleton::RespawnInit(); + m_damage_particles.Clear(); + CPHDestroyable::RespawnInit(); + CPHCollisionDamageReceiver::Clear(); + b_breaks=false; +} + +void CCar::net_Save(NET_Packet& P) +{ + inherited::net_Save(P); + SaveNetState(P); + P.w_float(m_fuel); +} + + + +BOOL CCar::net_SaveRelevant() +{ + return TRUE; + //return !m_explosion_flags.test(CExplosive::flExploding)&&!CExplosive::IsExploded()&&!CPHDestroyable::Destroyed()&&!b_exploded; +} + +void CCar::SaveNetState(NET_Packet& P) +{ + + CPHSkeleton::SaveNetState (P); + P.w_vec3(Position()); + Fvector Angle; + XFORM().getXYZ(Angle); + P.w_vec3(Angle); + { + xr_map::iterator i,e; + i=m_doors.begin(); + e=m_doors.end(); + P.w_u16(u16(m_doors.size())); + for(;i!=e;++i) + i->second.SaveNetState(P); + } + + { + xr_map::iterator i,e; + i=m_wheels_map.begin(); + e=m_wheels_map.end(); + P.w_u16(u16(m_wheels_map.size())); + for(;i!=e;++i) + i->second.SaveNetState(P); + } + P.w_float(GetfHealth()); +} + + + +void CCar::RestoreNetState(CSE_PHSkeleton* po) +{ + if(!po->_flags.test(CSE_PHSkeleton::flSavedData))return; + CPHSkeleton::RestoreNetState(po); + + CSE_ALifeCar* co=smart_cast(po); + + { + xr_map::iterator i,e; + xr_vector::iterator ii=co->door_states.begin(); + i=m_doors.begin(); + e=m_doors.end(); + for(;i!=e;++i,++ii) + { + i->second.RestoreNetState(*ii); + } + } + { + xr_map::iterator i,e; + xr_vector::iterator ii=co->wheel_states.begin(); + i=m_wheels_map.begin(); + e=m_wheels_map.end(); + for(;i!=e;++i,++ii) + { + i->second.RestoreNetState(*ii); + } + } + //as later may kill diable/enable state save it; + bool enable = PPhysicsShell()->isEnabled(); +///////////////////////////////////////////////////////////////////////// + Fmatrix restored_form; + PPhysicsShell()->GetGlobalTransformDynamic(&restored_form); +///////////////////////////////////////////////////////////////////// + Fmatrix inv ,replace,sof; + sof.setXYZ(co->o_Angle.x,co->o_Angle.y,co->o_Angle.z); + sof.c.set(co->o_Position); + inv.set(restored_form); + inv.invert(); + replace.mul(sof,inv); +//////////////////////////////////////////////////////////////////// + { + + PKinematics(Visual())->CalculateBones_Invalidate(); + PKinematics(Visual())->CalculateBones(TRUE); + PPhysicsShell()->DisableCollision(); + CPHActivationShape activation_shape;//Fvector start_box;m_PhysicMovementControl.Box().getsize(start_box); + + Fvector center;Center(center); + Fvector obj_size;BoundingBox().getsize(obj_size); + get_box(PPhysicsShell(),restored_form,obj_size,center); + replace.transform(center); + activation_shape.Create(center,obj_size,this); + activation_shape.set_rotation(sof); + activation_shape.Activate(obj_size,1,1.f,M_PI/8.f); + Fvector dd; + dd.sub(activation_shape.Position(),center); + activation_shape.Destroy(); + sof.c.add(dd); + PPhysicsShell()->EnableCollision(); + } +//////////////////////////////////////////////////////////////////// + replace.mul(sof,inv); + PPhysicsShell()->TransformPosition(replace); + if(enable)PPhysicsShell()->Enable(); + else PPhysicsShell()->Disable(); + PPhysicsShell()->GetGlobalTransformDynamic(&XFORM()); +} +void CCar::SetDefaultNetState(CSE_PHSkeleton* po) +{ + if(po->_flags.test(CSE_PHSkeleton::flSavedData))return; + xr_map::iterator i,e; + i=m_doors.begin(); + e=m_doors.end(); + for(;i!=e;++i) + { + i->second.SetDefaultNetState(); + } +} +void CCar::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + if(CPHDestroyable::Destroyed())CPHDestroyable::SheduleUpdate(dt); + else CPHSkeleton::Update(dt); + + if(CDelayedActionFuse::isActive()&&CDelayedActionFuse::Update(GetfHealth())) + { + //CarExplode(); + } + if(b_exploded&&!m_explosion_flags.test(flExploding)&&!getEnabled())//!m_bExploding + setEnabled(TRUE); +#ifdef DEBUG + DbgSheduleUpdate(); +#endif +} + +void CCar::UpdateEx (float fov) +{ + #ifdef DEBUG + DbgUbdateCl(); + #endif + + // Log("UpdateCL",Device.dwFrame); + //XFORM().set(m_pPhysicsShell->mXFORM); + VisualUpdate(fov); + if(OwnerActor() && OwnerActor()->IsMyCamera()) + { + cam_Update(Device.fTimeDelta, fov); + OwnerActor()->Cameras().UpdateFromCamera(Camera()); + OwnerActor()->Cameras().ApplyDevice(VIEWPORT_NEAR); + } + + +} + +BOOL CCar::AlwaysTheCrow() +{ + return (m_car_weapon && m_car_weapon->IsActive() ); +} + +void CCar::UpdateCL ( ) +{ + inherited::UpdateCL(); + CExplosive::UpdateCL(); + if(m_car_weapon) + { + m_car_weapon->UpdateCL(); + if(m_memory) + m_memory->set_camera(m_car_weapon->ViewCameraPos(), m_car_weapon->ViewCameraDir(), m_car_weapon->ViewCameraNorm()); + + if (OwnerActor() && HasWeapon() && m_car_weapon->IsActive()) + { + collide::rq_result& rq = HUD().GetCurrentRayQuery(); + CCameraBase* C = active_camera; + m_car_weapon->SetParam(CCarWeapon::eWpnDesiredPos, C->vPosition.add(C->vDirection.mul(rq.range))); + } + } + ASCUpdate (); + if(Owner()) return; +// UpdateEx (g_fov); + VisualUpdate(90); + if (GetScriptControl()) + ProcessScripts(); + +} + + void CCar::VisualUpdate(float fov) +{ + m_pPhysicsShell->InterpolateGlobalTransform(&XFORM()); + + Fvector lin_vel; + m_pPhysicsShell->get_LinearVel(lin_vel); + // Sound + Fvector C,V; + Center (C); + V.set (lin_vel); + + m_car_sound->Update(); + if(Owner()) + { + + if(m_pPhysicsShell->isEnabled()) + { + Owner()->XFORM().mul_43 (XFORM(),m_sits_transforms); + } + + if(CurrentGameUI())// + { + CurrentGameUI()->UIMainIngameWnd->CarPanel().Show(true); + CurrentGameUI()->UIMainIngameWnd->CarPanel().SetCarHealth(GetfHealth()/* /100.f*/); + CurrentGameUI()->UIMainIngameWnd->CarPanel().SetSpeed(lin_vel.magnitude()/1000.f*3600.f/100.f); + CurrentGameUI()->UIMainIngameWnd->CarPanel().SetRPM(m_current_rpm/m_max_rpm/2.f); + CurrentGameUI()->UIMainIngameWnd->CarPanel().SetFuel(100.0f * (m_fuel / m_fuel_tank)); + } + } + + UpdateExhausts (); + m_lights.Update (); +} + +void CCar::renderable_Render ( ) +{ + inherited::renderable_Render (); + if(m_car_weapon) + m_car_weapon->Render_internal(); +} + +void CCar::net_Export (NET_Packet& P) +{ + inherited::net_Export(P); +// P.w_u32 (Level().timeServer()); +// P.w_u16 (0); +} + +void CCar::net_Import (NET_Packet& P) +{ + inherited::net_Import(P); +// u32 TimeStamp = 0; +// P.w_u32 (TimeStamp); +// u16 NumItems = 0; +// P.w_u32 (NumItems); +} + +void CCar::OnHUDDraw (CCustomHUD* /**hud/**/) +{ +#ifdef DEBUG + Fvector velocity; + m_pPhysicsShell->get_LinearVel(velocity); + UI().Font().pFontStat->SetColor (0xffffffff); + UI().Font().pFontStat->OutSet (120,530); + UI().Font().pFontStat->OutNext ("Position: [%3.2f, %3.2f, %3.2f]",VPUSH(Position())); + UI().Font().pFontStat->OutNext ("Velocity: [%3.2f]",velocity.magnitude()); + + +#endif +} + +//void CCar::Hit(float P,Fvector &dir,CObject * who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type) +void CCar::Hit (SHit* pHDS) +{ + + SHit HDS = *pHDS; + //if(CDelayedActionFuse::isActive()||Initiator()==u16(-1)&&HDS.hit_type==ALife::eHitTypeStrike) + //{ + // HDS.power=0.f; + //} + + //if(HDS.who->ID()!=ID()) + //{ + // CExplosive::SetInitiator(HDS.who->ID()); + //} + WheelHit(HDS.damage(),HDS.bone(),HDS.hit_type); + DoorHit(HDS.damage(),HDS.bone(),HDS.hit_type); + float hitScale=1.f,woundScale=1.f; + if(HDS.hit_type!=ALife::eHitTypeStrike) CDamageManager::HitScale(HDS.bone(), hitScale, woundScale); + HDS.power *= GetHitImmunity(HDS.hit_type) * hitScale; + + inherited::Hit(&HDS); + if(!CDelayedActionFuse::isActive()) + { + CDelayedActionFuse::CheckCondition(GetfHealth()); + } + CDamagableItem::HitEffect(); + if(Owner()&&Owner()->ID()==Level().CurrentEntity()->ID()) + CurrentGameUI()->UIMainIngameWnd->CarPanel().SetCarHealth(GetfHealth()/* /100.f */); +} + +void CCar::ChangeCondition (float fDeltaCondition) +{ + + CEntity::CalcCondition(-fDeltaCondition); + CDamagableItem::HitEffect(); + if (Local() && !g_Alive() && !AlreadyDie()) + KillEntity (Initiator()); + if(Owner()&&Owner()->ID()==Level().CurrentEntity()->ID()) + CurrentGameUI()->UIMainIngameWnd->CarPanel().SetCarHealth(GetfHealth()/* /100.f */); +} + +void CCar::PHHit(float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type) +{ + if(!m_pPhysicsShell) return; + if(m_bone_steer==element) return; + if(CPHUpdateObject::IsActive()) + { + Fvector vimpulse;vimpulse.set(dir); + vimpulse.mul(impulse); + vimpulse.y *=GravityFactorImpulse(); + float mag=vimpulse.magnitude(); + if(!fis_zero(mag)) + { + vimpulse.mul(1.f/mag); + m_pPhysicsShell->applyHit(p_in_object_space,vimpulse,mag,element,hit_type); + } + + } else + { + m_pPhysicsShell->applyHit(p_in_object_space,dir,impulse,element,hit_type); + } +} + + +void CCar::ApplyDamage(u16 level) +{ + if (level == m_level_applied) return; + + if (level > m_level_applied) + { + switch(level) + { + case 1: m_damage_particles.Play1(this);break; + case 2: + { + if(!CDelayedActionFuse::isActive()) + { + CDelayedActionFuse::CheckCondition(GetfHealth()); + } + m_damage_particles.Play2(this); + } + break; + case 3: m_fuel=0.f; + + } + } + else + { + switch(level) + { + case 0: + m_damage_particles.Stop1(this); + break; + case 1: + m_damage_particles.Stop2(this); + CDelayedActionFuse::Reset(); + break; + } + } + + CDamagableItem::ApplyDamage(level); +} + +float CCar::SetfHealth(float value) +{ + float result = CEntity::SetfHealth(value); + CDamagableItem::HitEffect(); + return result; +} + +void CCar::detach_Actor() +{ + if(!Owner()) return; + Owner()->setVisible(1); + if (OwnerActor()) OwnerActor()->SetActorShadows(!psActorFlags.test(AF_ACTOR_BODY)); + CHolderCustom::detach_Actor(); + PPhysicsShell()->remove_ObjectContactCallback(ActorObstacleCallback); + NeutralDrive(); + Unclutch(); + ResetKeys(); + m_current_rpm=m_min_rpm; + CurrentGameUI()->UIMainIngameWnd->CarPanel().Show(false); + HandBreak(); + if (HasWeapon()) m_car_weapon->Action(CCarWeapon::eWpnFire, 0); + processing_deactivate(); +#ifdef DEBUG + DBgClearPlots(); +#endif +} + +bool CCar::attach_Actor(CGameObject* actor) +{ + if(CPHDestroyable::Destroyed()) return false; + CHolderCustom::attach_Actor(actor); + + IKinematics* K = smart_cast(Visual()); + CInifile* ini = K->LL_UserData(); + int id; + Fmatrix driver_xform; + if(ini->line_exist("car_definition","driver_position") && ini->line_exist("car_definition","driver_direction")) + { + driver_xform.c.set(ini->r_fvector3("car_definition","driver_position")); + driver_xform.k.set(ini->r_fvector3("car_definition","driver_direction")); + } else if(ini->line_exist("car_definition","driver_place")) { + + id=K->LL_BoneID(ini->r_string("car_definition","driver_place")); + CBoneInstance& instance=K->LL_GetBoneInstance (u16(id)); + driver_xform.set(instance.mTransform); + } else { + id=K->LL_GetBoneRoot(); + CBoneInstance& instance=K->LL_GetBoneInstance (u16(id)); + driver_xform.set(instance.mTransform); + } + + Owner()->setVisible(0); + if (OwnerActor()) OwnerActor()->SetActorShadows(true); + m_sits_transforms.set(driver_xform); + actor->XFORM().mul_43 (XFORM(),m_sits_transforms); + + OnCameraChange(ectFirst); + PPhysicsShell()->Enable(); + PPhysicsShell()->add_ObjectContactCallback(ActorObstacleCallback); +// VisualUpdate(); + processing_activate(); + ReleaseHandBreak(); + ReleaseBreaks(); + + return true; +} + +/* +void CCar::TeleportCarAndDriver(Fvector3 pos) +{ + + StopEngine(); + HandBreak(); + + renderable.xform.c = pos; + + m_pPhysicsShell->Activate(false); + m_pPhysicsShell->mXFORM.set(XFORM()); + m_pPhysicsShell->Activate(true); + + IKinematics* K = smart_cast(Visual()); + CInifile* ini = K->LL_UserData(); + int id; + Fmatrix driver_xform; + if (ini->line_exist("car_definition", "driver_position") && ini->line_exist("car_definition", "driver_direction")) + { + driver_xform.c.set(ini->r_fvector3("car_definition", "driver_position")); + driver_xform.k.set(ini->r_fvector3("car_definition", "driver_direction")); + } + else if (ini->line_exist("car_definition", "driver_place")) { + + id = K->LL_BoneID(ini->r_string("car_definition", "driver_place")); + CBoneInstance& instance = K->LL_GetBoneInstance(u16(id)); + driver_xform.set(instance.mTransform); + } + else { + id = K->LL_GetBoneRoot(); + CBoneInstance& instance = K->LL_GetBoneInstance(u16(id)); + driver_xform.set(instance.mTransform); + } + + Owner()->setVisible(0); + if (OwnerActor()) OwnerActor()->SetActorShadows(true); + m_sits_transforms.set(driver_xform); + Actor()->XFORM().mul_43(XFORM(), m_sits_transforms); + +} +*/ +bool CCar::is_Door(u16 id,xr_map::iterator& i) +{ + i = m_doors.find(id); + if (i == m_doors.end()) + { + return false; + } + else + { + if(i->second.joint)//temp for fake doors + return true; + else + return false; + } +} +bool CCar::is_Door(u16 id) +{ + xr_map::iterator i; + i = m_doors.find(id); + if (i == m_doors.end()) + { + return false; + } + return true; +} + +bool CCar::Enter(const Fvector& pos,const Fvector& dir,const Fvector& foot_pos) +{ + xr_map::iterator i,e; + + i=m_doors.begin();e=m_doors.end(); + Fvector enter_pos; + enter_pos.add(pos,foot_pos); + enter_pos.mul(0.5f); + for(;i!=e;++i) + { + if(i->second.CanEnter(pos,dir,enter_pos)) return true; + } + return false; +} + +bool CCar::Exit(const Fvector& pos,const Fvector& dir) +{ + xr_map::iterator i,e; + + i=m_doors.begin();e=m_doors.end(); + for(;i!=e;++i) + { + if(i->second.CanExit(pos,dir)) + { + i->second.GetExitPosition(m_exit_position); + return true; + } + } + return false; + +} + +void CCar::DoEnter() +{ + Fvector center; + Center(center); + + Fvector enter_pos; + enter_pos.add(Device.vCameraPosition,center); + enter_pos.mul(0.5f); +} + +void CCar::DoExit() +{ + CActor* A=OwnerActor(); + if(A) + { + if(!m_doors.empty())m_doors.begin()->second.GetExitPosition(m_exit_position); + else m_exit_position.set(Position()); + A->detach_Vehicle(); + if(A->g_Alive()<=0.f)A->character_physics_support()->movement()->DestroyCharacter(); + + if (CurrentGameUI()->UIMainIngameWnd->CarPanel().IsShown()) + CurrentGameUI()->UIMainIngameWnd->CarPanel().Show(false); + } +} + +void CCar::ParseDefinitions() +{ + + + bone_map.clear(); + + IKinematics* pKinematics=smart_cast(Visual()); + bone_map.insert(mk_pair(pKinematics->LL_GetBoneRoot(),physicsBone())); + CInifile* ini = pKinematics->LL_UserData(); + R_ASSERT2(ini,"Car has no description !!! See ActorEditor Object - UserData"); + CExplosive::Load(ini,"explosion"); + + m_camera_position_firsteye = ini->r_fvector3("car_definition","camera_pos_firsteye"); + m_camera_position_lookat = ini->r_fvector3("car_definition","camera_pos_lookat"); + m_camera_position_free = ini->r_fvector3("car_definition","camera_pos_free"); + + ///////////////////////////car definition/////////////////////////////////////////////////// + fill_wheel_vector (ini->r_string ("car_definition","driving_wheels"),m_driving_wheels); + fill_wheel_vector (ini->r_string ("car_definition","steering_wheels"),m_steering_wheels); + fill_wheel_vector (ini->r_string ("car_definition","breaking_wheels"),m_breaking_wheels); + fill_exhaust_vector (ini->r_string ("car_definition","exhausts"),m_exhausts); + fill_doors_map (ini->r_string ("car_definition","doors"),m_doors); + + if (ini->line_exist("car_definition","trunk_bone")) + { + u16 bone_id = pKinematics->LL_BoneID(ini->r_string("car_definition","trunk_bone")); + SDoor door(this); + door.bone_id= bone_id; + m_doors.insert (mk_pair(bone_id,door)); + BONE_P_PAIR_IT J = bone_map.find(bone_id); + if (J == bone_map.end()) + { + bone_map.insert(mk_pair(bone_id,physicsBone())); + } + } + + ///////////////////////////car properties/////////////////////////////// + + + m_max_power = ini->r_float("car_definition","engine_power"); + m_max_power *= (0.8f*1000.f); + + m_max_rpm = ini->r_float("car_definition","max_engine_rpm"); + m_max_rpm *= (1.f/60.f*2.f*M_PI); + + + m_min_rpm = ini->r_float("car_definition","idling_engine_rpm"); + m_min_rpm *= (1.f/60.f*2.f*M_PI); + + m_power_rpm = ini->r_float("car_definition","max_power_rpm"); + m_power_rpm *= (1.f/60.f*2.f*M_PI);// + + m_torque_rpm = ini->r_float("car_definition","max_torque_rpm"); + m_torque_rpm *= (1.f/60.f*2.f*M_PI);// + + m_power_increment_factor = READ_IF_EXISTS(ini,r_float,"car_definition","power_increment_factor",m_power_increment_factor); + m_rpm_increment_factor = READ_IF_EXISTS(ini,r_float,"car_definition","rpm_increment_factor",m_rpm_increment_factor); + m_power_decrement_factor = READ_IF_EXISTS(ini,r_float,"car_definition","power_decrement_factor",m_power_increment_factor); + m_rpm_decrement_factor = READ_IF_EXISTS(ini,r_float,"car_definition","rpm_decrement_factor",m_rpm_increment_factor); + m_power_neutral_factor = READ_IF_EXISTS(ini,r_float,"car_definition","power_neutral_factor",m_power_neutral_factor); + R_ASSERT2(m_power_neutral_factor>0.1f&&m_power_neutral_factor<1.f,"power_neutral_factor must be 0 - 1 !!"); + if(ini->line_exist("car_definition","exhaust_particles")) + { + m_exhaust_particles =ini->r_string("car_definition","exhaust_particles"); + } + + b_auto_switch_transmission= !!ini->r_bool("car_definition","auto_transmission"); + + InitParabola (); + + m_axle_friction = ini->r_float("car_definition","axle_friction"); + m_steering_speed = ini->r_float("car_definition","steering_speed"); + + if(ini->line_exist("car_definition","break_time")) + { + m_break_time=ini->r_float("car_definition","break_time"); + } + /////////////////////////transmission//////////////////////////////////////////////////////////////////////// + float main_gear_ratio=ini->r_float("car_definition","main_gear_ratio"); + + R_ASSERT2(ini->section_exist("transmission_gear_ratio"),"no section transmission_gear_ratio"); + m_gear_ratious.push_back(ini->r_fvector3("transmission_gear_ratio","R")); + m_gear_ratious[0][0]=-m_gear_ratious[0][0]*main_gear_ratio; + string32 rat_num; + for(int i=1;true;++i) + { + xr_sprintf(rat_num,"N%d",i); + if(!ini->line_exist("transmission_gear_ratio",rat_num)) break; + Fvector gear_rat=ini->r_fvector3("transmission_gear_ratio",rat_num); + gear_rat[0]*=main_gear_ratio; + gear_rat[1]*=(1.f/60.f*2.f*M_PI); + gear_rat[2]*=(1.f/60.f*2.f*M_PI); + m_gear_ratious.push_back(gear_rat); +//#ifdef DEBUG +// Msg("! gear ratio [%s][%s] [%3.3f,%3.3f,%3.3f]", *(cName()), rat_num, VPUSH(gear_rat)); +//#endif + } + ///////////////////////////////sound/////////////////////////////////////////////////////// + m_car_sound->Init(); + ///////////////////////////////fuel/////////////////////////////////////////////////// +/* + m_fuel_tank=ini->r_float("car_definition","fuel_tank"); + m_fuel=m_fuel_tank; + m_fuel_consumption=ini->r_float("car_definition","fuel_consumption"); + m_fuel_consumption/=100000.f; +*/ + if(ini->line_exist("car_definition","exhaust_particles")) + m_exhaust_particles = ini->r_string("car_definition","exhaust_particles"); + ///////////////////////////////lights/////////////////////////////////////////////////// + m_lights.Init(this); + m_lights.ParseDefinitions(); + + + + if(ini->section_exist("animations")) + { + m_driver_anim_type=ini->r_u16("animations","driver_animation_type"); + } + + + if(ini->section_exist("doors")) + { + m_doors_torque_factor=ini->r_u16("doors","open_torque_factor"); + } + + m_damage_particles.Init(this); +} + +void CCar::CreateSkeleton(CSE_Abstract *po) +{ + + if (!Visual()) return; + IRenderVisual *pVis = Visual(); + IKinematics* pK = smart_cast(pVis); + IKinematicsAnimated* pKA = smart_cast(pVis); + if(pKA) + { + pKA->PlayCycle ("idle"); + pK->CalculateBones (TRUE); + } + +#pragma todo(" replace below by P_build_Shell or call inherited") + m_pPhysicsShell = P_create_Shell(); + m_pPhysicsShell->build_FromKinematics(pK,&bone_map); + m_pPhysicsShell->set_PhysicsRefObject(this); + m_pPhysicsShell->mXFORM.set(XFORM()); + m_pPhysicsShell->Activate(true); + m_pPhysicsShell->SetAirResistance(0.f,0.f); + m_pPhysicsShell->SetPrefereExactIntegration(); + + m_pPhysicsShell->Enable(); + + ApplySpawnIniToPhysicShell(&po->spawn_ini(),m_pPhysicsShell,false); + ApplySpawnIniToPhysicShell(pK->LL_UserData(),m_pPhysicsShell,false); +} + +void CCar::Init() +{ + + CPHCollisionDamageReceiver::Init(); + + //get reference wheel radius + IKinematics* pKinematics=smart_cast(Visual()); + CInifile* ini = pKinematics->LL_UserData(); + R_ASSERT2(ini,"Car has no description !!! See ActorEditor Object - UserData"); + ///SWheel& ref_wheel=m_wheels_map.find(pKinematics->LL_BoneID(ini->r_string("car_definition","reference_wheel")))->second; + + SetCollisionHitThreshold(READ_IF_EXISTS(ini, r_float, "car_definition", "collision_hit_threshold", 5.f)); + + if(ini->section_exist("air_resistance")) + { + PPhysicsShell()->SetAirResistance(default_k_l*ini->r_float("air_resistance","linear_factor"),default_k_w*ini->r_float("air_resistance","angular_factor")); + } + if(ini->line_exist("car_definition","steer")) + { + + + m_bone_steer=pKinematics->LL_BoneID(ini->r_string("car_definition","steer")); + VERIFY2(fsimilar(DET(pKinematics->LL_GetTransform(m_bone_steer)),1.f,EPS_L),"BBADD MTX"); + pKinematics->LL_GetBoneInstance(m_bone_steer).set_callback(bctPhysics,cb_Steer,this); + } + m_steer_angle=0.f; + //ref_wheel.Init(); + m_ref_radius=ini->r_float("car_definition","reference_radius");//ref_wheel.radius; + b_exploded =false; + b_engine_on =false; + b_clutch =false; + b_starting =false; + b_stalling =false; + b_transmission_switching =false; + m_root_transform.set(bone_map.find(pKinematics->LL_GetBoneRoot())->second.element->mXFORM); + m_current_transmission_num=0; + m_pPhysicsShell->set_DynamicScales(1.f,1.f); + CDamagableItem::Init(GetfHealth(),3); + float l_time_to_explosion=READ_IF_EXISTS(ini,r_float,"car_definition","time_to_explosion",120.f); + CDelayedActionFuse::Initialize(l_time_to_explosion,CDamagableItem::DamageLevelToHealth(2)); + { + xr_map::iterator i,e; + i=m_wheels_map.begin(); + e=m_wheels_map.end(); + for(;i!=e;++i) + { + i->second.Init(); + i->second.CDamagableHealthItem::Init(100.f,2); + } + } + + { + xr_vector::iterator i,e; + i=m_driving_wheels.begin(); + e=m_driving_wheels.end(); + for(;i!=e;++i) + i->Init(); + } + + { + xr_vector::iterator i,e; + i=m_breaking_wheels.begin(); + e=m_breaking_wheels.end(); + for(;i!=e;++i) + i->Init(); + } + + { + xr_vector::iterator i,e; + i=m_steering_wheels.begin(); + e=m_steering_wheels.end(); + for(;i!=e;++i) + i->Init(); + } + + { + xr_vector::iterator i,e; + i=m_exhausts.begin(); + e=m_exhausts.end(); + for(;i!=e;++i) + i->Init(); + } + + { + xr_map::iterator i,e; + i=m_doors.begin(); + e=m_doors.end(); + for(;i!=e;++i) + { + i->second.Init(); + i->second.CDamagableHealthItem::Init(100,1); + } + + } + + if(ini->section_exist("damage_items")) + { + CInifile::Sect& data = ini->r_section("damage_items"); + for (CInifile::SectCIt I=data.Data.begin(); I!=data.Data.end(); I++){ + const CInifile::Item& item = *I; + u16 index = pKinematics->LL_BoneID(*item.first); + R_ASSERT3(index != BI_NONE, "Wrong bone name", *item.first); + xr_map ::iterator i=m_wheels_map.find(index); + + if(i!=m_wheels_map.end()) + i->second.CDamagableHealthItem::Init(float(atof(*item.second)),2); + else + { + xr_map ::iterator i=m_doors.find(index); + R_ASSERT3(i!=m_doors.end(),"only wheel and doors bones allowed for damage defs",*item.first); + i->second.CDamagableHealthItem::Init(float(atof(*item.second)),1); + } + + } + } + + + if(ini->section_exist("immunities")) + { + LoadImmunities("immunities",ini); + } + + CDamageManager::reload("car_definition","damage",ini); + + HandBreak(); + Transmission(1); + +} + +void CCar::Revert() +{ + m_pPhysicsShell->applyForce(0,1.5f*EffectiveGravity()*m_pPhysicsShell->getMass(),0); +} + +void CCar::NeutralDrive() +{ + + xr_vector::iterator i,e; + i=m_driving_wheels.begin(); + e=m_driving_wheels.end(); + for(;i!=e;++i) + i->Neutral(); + e_state_drive=neutral; +} +void CCar::ReleaseHandBreak() +{ + xr_vector::iterator i,e; + i=m_breaking_wheels.begin(); + e=m_breaking_wheels.end(); + for(;i!=e;++i) + i->Neutral(); + if(e_state_drive==drive) + Drive(); +} +void CCar::Drive() +{ + + if(!b_clutch||!b_engine_on) return; + m_pPhysicsShell->Enable(); + m_current_rpm=EngineDriveSpeed(); + m_current_engine_power=EnginePower(); + xr_vector::iterator i,e; + i=m_driving_wheels.begin(); + e=m_driving_wheels.end(); + for(;i!=e;++i) + i->Drive(); + e_state_drive=drive; + +} + +void CCar::StartEngine() +{ + + if(m_fuelStart(); + b_engine_on=true; + m_current_rpm=0.f; + m_current_engine_power = 0.f; + b_starting=true; +} +void CCar::StopEngine() +{ + if(!b_engine_on) return; + //m_car_sound->Stop(); + //StopExhausts(); + AscCall(ascSndStall); + AscCall(ascExhoustStop); + NeutralDrive();//set zero speed + b_engine_on=false; + UpdatePower();//set engine friction; + m_current_rpm=0.f; +} + +void CCar::Stall() +{ + //m_car_sound->Stall(); + //StopExhausts(); + AscCall(ascSndStall); + AscCall(ascExhoustStop); + NeutralDrive();//set zero speed + b_engine_on=false; + UpdatePower();//set engine friction; + m_current_rpm=0.f; + +} +void CCar::ReleasePedals() +{ + Clutch(); + NeutralDrive();//set zero speed + UpdatePower();//set engine friction; +} + +void CCar::SwitchEngine() +{ + if(b_engine_on) StopEngine(); + else StartEngine(); +} +void CCar::Clutch() +{ + b_clutch=true; +} + +void CCar::Unclutch() +{ + b_clutch=false; +} + +void CCar::Starter() +{ + b_starting=true; + m_dwStartTime=Device.dwTimeGlobal; +} +void CCar::UpdatePower() +{ + m_current_rpm=EngineDriveSpeed(); + m_current_engine_power=EnginePower(); + if(b_auto_switch_transmission&&!b_transmission_switching&&b_engine_on) + { + VERIFY2(CurrentTransmission()m_gear_ratious[CurrentTransmission()][2]) TransmissionUp(); + } + + xr_vector::iterator i,e; + i=m_driving_wheels.begin(); + e=m_driving_wheels.end(); + for(;i!=e;++i) + i->UpdatePower(); +} + +void CCar::SteerRight() +{ + b_wheels_limited=true; //no need to limit wheels when stiring + m_pPhysicsShell->Enable(); + xr_vector::iterator i,e; + i=m_steering_wheels.begin(); + e=m_steering_wheels.end(); + for(;i!=e;++i) + i->SteerRight(); + e_state_steer=right; + +} +void CCar::SteerLeft() +{ + b_wheels_limited=true; //no need to limit wheels when stiring + m_pPhysicsShell->Enable(); + xr_vector::iterator i,e; + i=m_steering_wheels.begin(); + e=m_steering_wheels.end(); + for(;i!=e;++i) + i->SteerLeft(); + e_state_steer=left; +} + +void CCar::SteerIdle() +{ + b_wheels_limited=false; + m_pPhysicsShell->Enable(); + xr_vector::iterator i,e; + i=m_steering_wheels.begin(); + e=m_steering_wheels.end(); + for(;i!=e;++i) + i->SteerIdle(); + e_state_steer=idle; +} + +void CCar::LimitWheels() +{ + if(b_wheels_limited) return; + b_wheels_limited=true; + xr_vector::iterator i,e; + i=m_steering_wheels.begin(); + e=m_steering_wheels.end(); + for(;i!=e;++i) + i->Limit(); +} +void CCar::HandBreak() +{ + xr_vector::iterator i,e; + i=m_breaking_wheels.begin(); + e=m_breaking_wheels.end(); + for(;i!=e;++i) + i->HandBreak(); +} + +void CCar::StartBreaking() +{ + if(!b_breaks) + { + b_breaks=true; + m_break_start=Device.fTimeGlobal; + } +} +void CCar::StopBreaking() +{ + xr_vector::iterator i,e; + i=m_breaking_wheels.begin(); + e=m_breaking_wheels.end(); + for(;i!=e;++i) + i->Neutral(); + if(e_state_drive==drive) + Drive(); + b_breaks=false; +} +void CCar::PressRight() +{ + if(lsp) + { + if(!fwp)SteerIdle(); + } + else + SteerRight(); + rsp=true; +} +void CCar::PressLeft() +{ + if(rsp) + { + if(!fwp)SteerIdle(); + } + else + SteerLeft(); + lsp=true; +} +void CCar::PressForward() +{ + if(bkp) + { + Unclutch(); + NeutralDrive(); + } + else + { + DriveForward(); + } + fwp=true; +} +void CCar::PressBack() +{ + if(fwp) + { + Unclutch(); + NeutralDrive(); + } + else + { + //DriveBack(); + Unclutch(); + NeutralDrive(); + StartBreaking(); + } + bkp=true; +} +void CCar::PressBreaks() +{ + HandBreak(); + brp=true; +} + +void CCar::DriveBack() +{ + Clutch(); + Transmission(0); + if(1==CurrentTransmission()||0==CurrentTransmission())Starter(); + Drive(); +} +void CCar::DriveForward() +{ + Clutch(); + if(0==CurrentTransmission()) Transmission(1); + if(1==CurrentTransmission()||0==CurrentTransmission())Starter(); + Drive(); +} +void CCar::ReleaseRight() +{ + if(lsp) + SteerLeft(); + else + SteerIdle(); + rsp=false; +} +void CCar::ReleaseLeft() +{ + if(rsp) + SteerRight(); + else + SteerIdle(); + lsp=false; +} +void CCar::ReleaseForward() +{ + if(bkp) + { + Clutch(); + Transmission(0); + if(1==CurrentTransmission()||0==CurrentTransmission())Starter(); + Drive(); + } + else + { + Unclutch(); + NeutralDrive(); + } + + fwp=false; +} +void CCar::ReleaseBack() +{ + if(b_breaks) + { + StopBreaking(); + } + if(fwp) + { + Clutch(); + if(0==CurrentTransmission()) Transmission(1); + if(1==CurrentTransmission()||0==CurrentTransmission()) Starter(); + Drive(); + } + else + { + Unclutch(); + NeutralDrive(); + } + bkp=false; +} + + +void CCar::ReleaseBreaks() +{ + ReleaseHandBreak(); + brp=false; +} + +void CCar::Transmission(size_t num) +{ + + if(numTransmissionSwitch() ; + AscCall(ascSndTransmission) ; + m_current_transmission_num =num ; + m_current_gear_ratio =m_gear_ratious[num][0] ; + b_transmission_switching =true ; + Drive () ; + } + } +#ifdef DEBUG + //Log("Transmission switch %d",(u32)num); +#endif +} +void CCar::CircleSwitchTransmission() +{ + if(0==CurrentTransmission())return; + size_t transmission=1+CurrentTransmission(); + transmission=transmission%m_gear_ratious.size(); + 0==transmission ? transmission++ : transmission; + Transmission(transmission); + +} + +void CCar::TransmissionUp() +{ + if(0==CurrentTransmission())return; + size_t transmission=1+CurrentTransmission(); + size_t max_transmition_num=m_gear_ratious.size()-1; + transmission>max_transmition_num ? transmission=max_transmition_num :transmission; + Transmission(transmission); + +} + +void CCar::TransmissionDown() +{ + if(0==CurrentTransmission())return; + size_t transmission=CurrentTransmission()-1; + transmission<1 ? transmission=1 : transmission; + Transmission(transmission); + +} + + + +void CCar::PhTune(float step) +{ + + + + for(u16 i=PPhysicsShell()->get_ElementsNumber();i!=0;i--) + { + CPhysicsElement* e=PPhysicsShell()->get_ElementByStoreOrder(i-1); + if(e->isActive()&&e->isEnabled())dBodyAddForce(e->get_body(),0,e->getMass()*AntiGravityAccel(),0); + } +} +float CCar::EffectiveGravity() +{ + float g= ph_world->Gravity(); + if(CPHUpdateObject::IsActive())g*=0.5f; + return g; +} +float CCar::AntiGravityAccel() +{ + return ph_world->Gravity()-EffectiveGravity(); +} +float CCar::GravityFactorImpulse() +{ + return _sqrt(EffectiveGravity()/ph_world->Gravity()); +} +void CCar::UpdateBack() +{ + if(b_breaks) + { + float k=1.f; + float time=(Device.fTimeGlobal-m_break_start); + if(time::iterator i,e; + i=m_breaking_wheels.begin(); + e=m_breaking_wheels.end(); + for(;i!=e;++i) + i->Break(k); + Fvector v; + m_pPhysicsShell->get_LinearVel(v); + //if(DriveWheelsMeanAngleRate()::iterator i,e; + i=m_exhausts.begin(); + e=m_exhausts.end(); + for(;i!=e;++i) + i->Play(); + +} + +void CCar::StopExhausts() +{ + + xr_vector::iterator i,e; + i=m_exhausts.begin(); + e=m_exhausts.end(); + for(;i!=e;++i) + i->Stop(); +} + +void CCar::UpdateExhausts() +{ + if(!b_engine_on) return; + xr_vector::iterator i,e; + i=m_exhausts.begin(); + e=m_exhausts.end(); + for(;i!=e;++i) + i->Update(); +} + +void CCar::ClearExhausts() +{ + xr_vector::iterator i,e; + i=m_exhausts.begin(); + e=m_exhausts.end(); + for(;i!=e;++i) + i->Clear(); +} + +bool CCar::Use(const Fvector& pos,const Fvector& dir,const Fvector& foot_pos) +{ + xr_map::iterator i; + + if(!Owner()) + { + if(Enter(pos,dir,foot_pos)) return true; + } + + RQR.r_clear (); + collide::ray_defs Q(pos, dir, 3.f, CDB::OPT_CULL,collide::rqtObject); // CDB::OPT_ONLYFIRST CDB::OPT_ONLYNEAREST + VERIFY(!fis_zero(Q.dir.square_magnitude())); + if (g_pGameLevel->ObjectSpace.RayQuery(RQR,collidable.model,Q)) + { + IKinematics* K = smart_cast(Visual()); + + collide::rq_results& R = RQR; + int y=R.r_count(); + for (int k=0; kelement) + { + luabind::functor lua_function; + string256 fn; + xr_strcpy (fn, pSettings->r_string("lost_alpha_cfg", "on_use_trunk")); + R_ASSERT2 (ai().script_engine().functor(fn,lua_function),make_string("Can't find function %s",fn)); + lua_function (ID()); + return false; + } + + if(is_Door((u16)I->element,i)) + { + bool front=i->second.IsFront(pos,dir); + if((Owner()&&!front)||(!Owner()&&front))i->second.Use(); + if(i->second.state==SDoor::broken) break; + return false; + } + } + } + + if(Owner())return Exit(pos,dir); + + return false; + +} +bool CCar::DoorUse(u16 id) +{ + + xr_map::iterator i; + if(is_Door(id,i)) + { + i->second.Use(); + return true; + } + else + { + return false; + } + +} + +bool CCar::DoorSwitch(u16 id) +{ + xr_map::iterator i; + if(is_Door(id,i)) + { + i->second.Switch(); + return true; + } + else + { + return false; + } +} +bool CCar::DoorClose(u16 id) +{ + + xr_map::iterator i; + if(is_Door(id,i)) + { + i->second.Close(); + return true; + } + else + { + return false; + } +} + +bool CCar::DoorOpen(u16 id) +{ + + xr_map::iterator i; + if(is_Door(id,i)) + { + i->second.Open(); + return true; + } + else + { + return false; + } +} +void CCar::InitParabola() +{ + //float t1=(m_power_rpm-m_torque_rpm); + //float t2=m_max_power/m_power_rpm; + //m_c = t2* (3.f*m_power_rpm - 4.f*m_torque_rpm)/t1/2.f; + //t2/=m_power_rpm; + //m_a = -t2/t1/2.f; + //m_b = t2*m_torque_rpm/t1; + + + //m_c = m_max_power* (3.f*m_power_rpm - 4.f*m_torque_rpm)/(m_power_rpm-m_torque_rpm)/2.f/m_power_rpm; + //m_a = -m_max_power/(m_power_rpm-m_torque_rpm)/m_power_rpm/m_power_rpm/2.f; + //m_b = m_max_power*m_torque_rpm/(m_power_rpm-m_torque_rpm)/m_power_rpm/m_power_rpm; + + + + + m_a=expf((m_power_rpm - m_torque_rpm)/(2.f*m_power_rpm))*m_max_power/m_power_rpm; + m_b=m_torque_rpm; + m_c=_sqrt(2.f*m_power_rpm*(m_power_rpm - m_torque_rpm)); + + +} +float CCar::Parabola(float rpm) +{ + //float rpm_2=rpm*rpm; + //float value=(m_a*rpm_2*rpm_2*rpm_2+m_b*rpm_2+m_c)*rpm_2; + float ex=(rpm-m_b)/m_c; + float value=m_a*expf(-ex*ex)*rpm; + if(value<0.f) return 0.f; + if(e_state_drive==neutral) value*=m_power_neutral_factor; + return value; +} + +float CCar::EnginePower() +{ + + float value;value = Parabola(m_current_rpm); + if(b_starting) + { + if(m_current_rpm1000) b_starting=false; + } + if(value>m_current_engine_power) + return value * m_power_increment_factor+m_current_engine_power*(1.f-m_power_increment_factor); + else + return value * m_power_decrement_factor+m_current_engine_power*(1.f-m_power_decrement_factor); +} +float CCar::DriveWheelsMeanAngleRate() +{ + xr_vector::iterator i,e; + i=m_driving_wheels.begin(); + e=m_driving_wheels.end(); + float drive_speed=0.f; + for(;i!=e;++i) + { + drive_speed+=i->ASpeed(); + //if(wheel_speedm_power_rpm) + { + b_transmission_switching=false; + } + }else + { + calc_rpm=EngineRpmFromWheels(); + + if(!b_clutch&&calc_rpmm_current_rpm) + return (1.f-m_rpm_increment_factor)*m_current_rpm+m_rpm_increment_factor*calc_rpm; + else + return (1.f-m_rpm_decrement_factor)*m_current_rpm+m_rpm_decrement_factor*calc_rpm; + + //if(drive_speedm_min_rpm) + m_fuel-=time_delta*(m_current_rpm-m_min_rpm)*m_fuel_consumption; + else + m_fuel-=time_delta*m_min_rpm*m_fuel_consumption; + if(m_fuel(O))) + { + O->H_SetParent(this); + inventory().Take(smart_cast(O), false, false, duringSpawn); + } + else + { + if (!O || !O->H_Parent() || (this != O->H_Parent())) return; + NET_Packet P; + u_EventGen(P,GE_OWNERSHIP_REJECT,ID()); + P.w_u16(u16(O->ID())); + u_EventSend(P); + } + }break; + case GE_OWNERSHIP_REJECT: + { + P.r_u16 (id); + CObject* O = Level().Objects.net_Find (id); + + bool just_before_destroy = !P.r_eof() && P.r_u8(); + O->SetTmpPreDestroy (just_before_destroy); + if (inventory().DropItem(smart_cast(O)) && !O->getDestroy()) + { + O->H_SetParent(nullptr, just_before_destroy); + } + }break; + } + +} + +void CCar::ResetScriptData(void *P) +{ + CScriptEntity::ResetScriptData(P); +} + +void CCar::PhDataUpdate(float step) +{ + if(m_repairing)Revert(); + LimitWheels(); + UpdateFuel(step); + + + //if(fwp) + { + UpdatePower(); + if(b_engine_on&&!b_starting && m_current_rpmupdate) + { + m_doors_update.erase(m_doors_update.begin()+k); + --k; + } + else + { + D->Update(); + } + } + + m_steer_angle=m_steering_wheels.begin()->GetSteerAngle()*0.1f+m_steer_angle*0.9f; + VERIFY(_valid(m_steer_angle)); +} + +BOOL CCar::UsedAI_Locations() +{ + return (FALSE); +} + +u16 CCar::DriverAnimationType() +{ + return m_driver_anim_type; +} + +void CCar::OnAfterExplosion() +{ + +} + +void CCar::OnBeforeExplosion() +{ + setEnabled(FALSE); +} + +void CCar::CarExplode() +{ + + if(b_exploded) return; + CPHSkeleton::SetNotNeedSave(); + if(m_car_weapon)m_car_weapon->Action(CCarWeapon::eWpnActivate,0); + m_lights.TurnOffHeadLights(); + b_exploded=true; + CExplosive::GenExplodeEvent(Position(),Fvector().set(0.f,1.f,0.f)); + + CActor* A=OwnerActor(); + if(A) + { + if(!m_doors.empty())m_doors.begin()->second.GetExitPosition(m_exit_position); + else m_exit_position.set(Position()); + A->detach_Vehicle(); + if(A->g_Alive()<=0.f)A->character_physics_support()->movement()->DestroyCharacter(); + + if (CurrentGameUI()->UIMainIngameWnd->CarPanel().IsShown()) + CurrentGameUI()->UIMainIngameWnd->CarPanel().Show(false); + } + + if(CPHDestroyable::CanDestroy()) + CPHDestroyable::Destroy(ID(),"physic_destroyable_object"); + + m_damage_particles.Stop1(this); + m_damage_particles.Stop2(this); + CDelayedActionFuse::Reset(); +} + +//void CCar::object_contactCallbackFun(bool& do_colide,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/) +//{ +// +// dxGeomUserData *l_pUD1 = NULL; +// dxGeomUserData *l_pUD2 = NULL; +// l_pUD1 = retrieveGeomUserData(c.geom.g1); +// l_pUD2 = retrieveGeomUserData(c.geom.g2); +// +// if(! l_pUD1) return; +// if(!l_pUD2) return; +// +// CEntityAlive* capturer=smart_cast(l_pUD1->ph_ref_object); +// if(capturer) +// { +// CPHCapture* capture=capturer->m_PhysicMovementControl->PHCapture(); +// if(capture) +// { +// if(capture->m_taget_element->PhysicsRefObject()==l_pUD2->ph_ref_object) +// { +// do_colide = false; +// capture->m_taget_element->Enable(); +// if(capture->e_state==CPHCapture::cstReleased) capture->ReleaseInCallBack(); +// } +// +// } +// +// +// } +// +// capturer=smart_cast(l_pUD2->ph_ref_object); +// if(capturer) +// { +// CPHCapture* capture=capturer->m_PhysicMovementControl->PHCapture(); +// if(capture) +// { +// if(capture->m_taget_element->PhysicsRefObject()==l_pUD1->ph_ref_object) +// { +// do_colide = false; +// capture->m_taget_element->Enable(); +// if(capture->e_state==CPHCapture::cstReleased) capture->ReleaseInCallBack(); +// } +// +// } +// +// } +//} + +template IC void CCar::fill_wheel_vector(LPCSTR S,xr_vector& type_wheels) +{ + IKinematics* pKinematics =smart_cast(Visual()); + string64 S1; + int count = _GetItemCount(S); + for (int i=0 ;iLL_BoneID(S1); + + type_wheels.push_back (T()); + T& twheel = type_wheels.back(); + + + BONE_P_PAIR_IT J = bone_map.find(bone_id); + if (J == bone_map.end()) + { + bone_map.insert(mk_pair(bone_id,physicsBone())); + + + SWheel& wheel = (m_wheels_map.insert(mk_pair(bone_id,SWheel(this)))).first->second; + wheel.bone_id = bone_id; + twheel.pwheel = &wheel; + wheel .Load(S1); + twheel .Load(S1); + } + else + { + twheel.pwheel = &(m_wheels_map.find(bone_id))->second; + twheel .Load(S1); + } + } +} + +IC void CCar::fill_exhaust_vector(LPCSTR S,xr_vector& exhausts) +{ + IKinematics* pKinematics =smart_cast(Visual()); + string64 S1; + int count = _GetItemCount(S); + for (int i=0 ;iLL_BoneID(S1); + + exhausts.push_back (SExhaust(this)); + SExhaust& exhaust = exhausts.back(); + exhaust.bone_id = bone_id; + + BONE_P_PAIR_IT J = bone_map.find(bone_id); + if (J == bone_map.end()) + { + bone_map.insert(mk_pair(bone_id,physicsBone())); + } + + } +} + +IC void CCar::fill_doors_map(LPCSTR S,xr_map& doors) +{ + IKinematics* pKinematics =smart_cast(Visual()); + string64 S1; + int count = _GetItemCount(S); + for (int i=0 ;iLL_BoneID(S1); + SDoor door(this); + door.bone_id= bone_id; + doors.insert (mk_pair(bone_id,door)); + BONE_P_PAIR_IT J = bone_map.find(bone_id); + if (J == bone_map.end()) + { + bone_map.insert(mk_pair(bone_id,physicsBone())); + } + + } +} + +DLL_Pure *CCar::_construct () +{ + inherited::_construct (); + CScriptEntity::_construct (); + CInventoryOwner::_construct (); + return (this); +} + +u16 CCar::Initiator() +{ + if(g_Alive() && Owner()) + { + return Owner()->ID(); + } + //else if(CExplosive::Initiator()!=u16(-1))return CExplosive::Initiator(); + else return ID() ; +} + +float CCar::RefWheelMaxSpeed() +{ + return m_max_rpm/m_current_gear_ratio; +} + +float CCar:: EngineCurTorque() +{ + return m_current_engine_power/m_current_rpm; +} +float CCar:: RefWheelCurTorque() +{ + if(b_transmission_switching) return 0.f; + return EngineCurTorque()*((m_current_gear_ratio<0.f) ? -m_current_gear_ratio : m_current_gear_ratio); +} +void CCar::GetRayExplosionSourcePos(Fvector &pos) +{ + random_point_in_object_box(pos,this); +} +void CCar::net_Relcase(CObject* O) +{ + CExplosive::net_Relcase(O); + inherited::net_Relcase(O); + if(m_memory) + m_memory->remove_links(O); +} + +void CCar::ASCUpdate() +{ + for(u16 i=0;iTransmissionSwitch();break; + case ascSndStall:m_car_sound->Stop();break; + case ascExhoustStop:StopExhausts();break; + default: NODEFAULT; + } +} + +void CCar::AscCall(EAsyncCalls c) +{ + async_calls.set(u16(c),TRUE); +} + +bool CCar::CanRemoveObject() +{ + return CExplosive::IsExploded()&&!CExplosive::IsSoundPlaying(); +} + +void CCar::SetExplodeTime (u32 et) +{ + CDelayedActionFuse::Initialize(float(et)/1000.f,CDamagableItem::DamageLevelToHealth(2)); +} +u32 CCar::ExplodeTime () +{ + if(CDelayedActionFuse::isInitialized()) + return u32(CDelayedActionFuse::Time())*1000; + else return 0; +} + +void CCar:: Die (CObject* who) +{ + inherited::Die(who); + CarExplode(); +} + +Fvector CCar:: ExitVelocity () +{ + CPhysicsShell *P=PPhysicsShell(); + if(!P||!P->isActive())return Fvector().set(0,0,0); + CPhysicsElement *E=P->get_ElementByStoreOrder(0); + Fvector v=ExitPosition(); + dBodyGetPointVel(E->get_body(),v.x,v.y,v.z,cast_fp(v)); + return v; +} + +void CCar::ShowTrunk() +{ + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if (pGameSP)pGameSP->StartStashUI(Actor(), this); +} + +void CCar::OpenTrunkBone() +{ + bool door = DoorOpen(m_bone_trunk); +} + +void CCar::CloseTrunkBone() +{ + bool door = DoorClose(m_bone_trunk); +} + +void CCar::KillEngine() +{ + if (!b_starting) StopEngine(); +} diff --git a/src/xrGameLA/Car.h b/src/xrGameLA/Car.h new file mode 100644 index 000000000..026c2cad3 --- /dev/null +++ b/src/xrGameLA/Car.h @@ -0,0 +1,659 @@ +#pragma once +#include "entity.h" +#include "PHDynamicData.h" +#include "PhysicsShell.h" +#include "script_entity.h" +#include "CarLights.h" +#include "phobject.h" +#include "holder_custom.h" +#include "PHSkeleton.h" +#include "DamagableItem.h" +#include "phcollisiondamagereceiver.h" +#include "CarDamageParticles.h" +#include "xrserver_objects_alife.h" +#include "CarDamageParticles.h" +#include "hit_immunity.h" +#include "Explosive.h" +#include "PHDestroyable.h" +#include "DelayedActionFuse.h" +#include "InventoryOwner.h" +#include "inventory_space.h" + + +// refs +class ENGINE_API CBoneInstance; +class CActor; +class CInventory; +class CSE_PHSkeleton; +class CCarWeapon; +struct dxGeomUserData; +struct dSurfaceParameters; +// defs + +#ifdef DEBUG + #include "../StatGraph.h" + #include "PHDebug.h" +#endif + +class CScriptEntityAction; +class car_memory; + +class CCar : + public CEntity, + public CScriptEntity, + public CPHUpdateObject, + public CHolderCustom, + public CPHSkeleton, + public CDamagableItem, + public CPHDestroyable, + public CPHCollisionDamageReceiver, + public CHitImmunity, + public CExplosive, + public CInventoryOwner, + public CDelayedActionFuse +{ +private: + collide::rq_results RQR; + +#ifdef DEBUG + CFunctionGraph m_dbg_power_rpm ; + CFunctionGraph m_dbg_torque_rpm ; + CStatGraph *m_dbg_dynamic_plot ; + bool b_plots ; + float _stdcall TorqueRpmFun (float rpm) {return Parabola(rpm)/rpm;} + void InitDebug () ; + void DbgSheduleUpdate () ; + void DbgUbdateCl () ; + void DbgCreatePlots () ; + void DBgClearPlots () ; +#endif +//////////////////////////////////////////////////////////////////// + Flags16 async_calls ; +static const u16 cAsCallsnum =3; + enum EAsyncCalls + { + ascSndTransmission = 1<<0, + ascSndStall = 1<<1, + ascExhoustStop = 1<<2, + ascLast = 1<(this);} + virtual CPHCollisionDamageReceiver *PHCollisionDamageReceiver () {return static_cast(this);} + +//////////////////////////////////////////////////////////////////////// + CCarDamageParticles m_damage_particles; +/////////////////////////////////////////////////////////////////////// +protected: + enum ECarCamType{ + ectFirst = 0, + ectChase, + ectFree + }; +public: + + + bool rsp,lsp,fwp,bkp,brp; + Fmatrix m_root_transform; + Fvector m_exit_position; + + enum eStateDrive + { + drive, + neutral + }; + + eStateDrive e_state_drive; + + enum eStateSteer + { + right, + idle, + left + }; + + eStateSteer e_state_steer; + + bool b_wheels_limited; + bool b_engine_on; + bool b_clutch; + bool b_starting; + bool b_stalling; + bool b_breaks; + bool b_transmission_switching; + + u32 m_dwStartTime; + float m_fuel; + float m_fuel_tank; + float m_fuel_consumption; + u16 m_driver_anim_type; + + float m_break_start; + float m_break_time; + float m_breaks_to_back_rate; + float m_power_neutral_factor;//multiplier for power when accelerator is not pressed (0-1,0.25) + bool b_exploded; + + struct SWheel: + public CDamagableHealthItem + { + typedef CDamagableHealthItem inherited; + u16 bone_id ; + bool inited ; + float radius ; + CPhysicsJoint *joint ; + CCar *car ; + struct SWheelCollisionParams + { + float spring_factor ; + float damping_factor ; + float mu_factor ; + SWheelCollisionParams (); + } collision_params ; + + IC static void applywheelCollisionParams (const dxGeomUserData *ud,bool& do_colide,dContact& c,SGameMtl* material_1,SGameMtl* material_2); + static void WheellCollisionCallback (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) ; + + void Init ();//asumptions: bone_map is 1. ini parsed 2. filled in 3. bone_id is set + void Load (LPCSTR section); + void RestoreNetState (const CSE_ALifeCar::SWheelState& a_state) ; + void SaveNetState (NET_Packet& P) ; + void ApplyDriveAxisVel (float vel) ; + void ApplyDriveAxisTorque (float torque) ; + void ApplyDriveAxisVelTorque (float vel,float torque) ; + void ApplySteerAxisVel (float vel) ; + void ApplySteerAxisTorque (float torque) ; + void ApplySteerAxisVelTorque (float vel,float torque) ; + void SetSteerLoLimit (float lo) ; + void SetSteerHiLimit (float hi) ; + void SetSteerLimits (float hi,float lo) ; + +virtual void ApplyDamage (u16 level); + SWheel(CCar* acar) + { + bone_id=BI_NONE; + car=acar; + joint=NULL; + inited=false; + } + }; + struct SWheelDrive + { + SWheel* pwheel; + float pos_fvd; + float gear_factor; + void Init () ; + void Drive () ; + void Neutral () ; + void UpdatePower () ; + float ASpeed () ; + void Load (LPCSTR /*section*/){} ; + }; + struct SWheelSteer + { + SWheel* pwheel; + float pos_right; + float lo_limit; + float hi_limit; + float steering_velocity; + float steering_torque; + bool limited; //zero limited for idle steering drive + float GetSteerAngle() + { + return -pos_right*dJointGetHinge2Angle1 (pwheel->joint->GetDJoint()); + } + void Init () ; + void SteerRight () ; + void SteerLeft () ; + void SteerIdle () ; + void Limit () ; + void Load (LPCSTR /*section*/){} ; + }; + struct SWheelBreak + { + SWheel *pwheel ; + float break_torque ; + float hand_break_torque ; + void Init () ; + void Break (float k) ; + void HandBreak () ; + void Neutral () ; + void Load (LPCSTR section) ; + }; + + struct SExhaust + { + u16 bone_id; + Fmatrix transform; + //Fvector velocity; + CParticlesObject* p_pgobject; + CPhysicsElement* pelement; + CCar* pcar; + void Init(); + void Play(); + void Stop(); + void Update(); + void Clear (); + SExhaust(CCar* acar) + { + bone_id=BI_NONE; + pcar=acar; + p_pgobject=NULL; + pelement=NULL; + } + ~SExhaust(); + }; + + struct SDoor; + struct SDoor : + public CDamagableHealthItem + { + typedef CDamagableHealthItem inherited; + u16 bone_id; + CCar* pcar; + bool update; + CPhysicsJoint* joint; + float torque; + float a_vel; + float pos_open; + float opened_angle; + float closed_angle; + u32 open_time; + struct SDoorway + { + Fvector2 door_plane_ext; + _vector2 door_plane_axes; + SDoor *door; + SDoorway (); + void SPass (); + void Init (SDoor *adoor); + void Trace (const Fvector &point,const Fvector &dir); + }; + Fvector2 door_plane_ext; + _vector2 door_plane_axes; + Fvector door_dir_in_door; + Fmatrix closed_door_form_in_object; + void Use(); + void Switch(); + void Init(); + void Open(); + void Close(); + void Break(); +virtual void ApplyDamage(u16 level); + void Update(); + float GetAngle(); + bool CanEnter(const Fvector& pos,const Fvector& dir,const Fvector& foot_pos); + bool IsInArea(const Fvector& pos,const Fvector& dir); + bool IsFront (const Fvector& pos,const Fvector& dir); + bool CanExit(const Fvector& pos,const Fvector& dir); + bool TestPass(const Fvector& pos,const Fvector& dir); + //bool TestPass1(const Fvector& pos,const Fvector& dir); + void GetExitPosition(Fvector& pos); + void ApplyOpenTorque(); + void ApplyTorque(float atorque,float aa_vel); + void ApplyCloseTorque(); + void NeutralTorque(float atorque); + void ClosingToClosed(); + void ClosedToOpening(); + void PlaceInUpdate(); + void RemoveFromUpdate(); + void SaveNetState(NET_Packet& P); + void RestoreNetState(const CSE_ALifeCar::SDoorState& a_state); + void SetDefaultNetState(); + enum eState + { + opening, + closing, + opened, + closed, + broken + }; + eState state; + SDoor(CCar* acar) + { + bone_id=BI_NONE; + pcar=acar; + joint=NULL; + state=closed; + torque=500.f; + a_vel=M_PI; + } + }; + + struct SCarSound + { + ref_sound snd_engine ; + ref_sound snd_engine_start ; + ref_sound snd_engine_stop ; + ref_sound snd_transmission ; + + enum ESoundState + { + sndOff, + sndStalling, + sndStoping, + sndStarting, + sndDrive + } eCarSound; + void Update () ; + void UpdateStarting () ; + void UpdateStoping () ; + void UpdateStalling () ; + void UpdateDrive () ; + void SwitchState (ESoundState new_state) ; + void SetSoundPosition (ref_sound &snd) ; + void SwitchOff () ; + void SwitchOn () ; + void Init () ; + void Destroy () ; + void Start () ; + void Stop () ; + void Stall () ; + void Drive () ; + void TransmissionSwitch () ; + + SCarSound (CCar* car) ; + ~SCarSound () ; + Fvector relative_pos ; + float volume ; + u32 engine_start_delay ;//snd_engine starts after engine_start_delay ms by snd_engine_start + u32 time_state_start ; + CCar* pcar ; + } *m_car_sound; + +private: + typedef CEntity inherited; +private: + CCarWeapon* m_car_weapon; + float m_steer_angle; + bool m_repairing; + u16 m_bone_steer; + u16 m_bone_trunk; + CCameraBase* camera[3]; + CCameraBase* active_camera; + + Fvector m_camera_position_firsteye; + Fvector m_camera_position_lookat; + Fvector m_camera_position_free; + + //////////////////////////////////////////////////// + friend struct SWheel; + friend struct SDoor; + + xr_map m_wheels_map; + xr_vector m_driving_wheels; + xr_vector m_steering_wheels; + xr_vector m_breaking_wheels; + xr_vector m_exhausts; + shared_str m_exhaust_particles; + xr_map m_doors; + xr_vector m_doors_update; + xr_vector m_gear_ratious; + Fmatrix m_sits_transforms; // driver_place + float m_current_gear_ratio; + + ///////////////////////////////////////////////////////////// + bool b_auto_switch_transmission; + + ///////////////////////////////////////////////////////////// + float m_doors_torque_factor; + ///////////////////////////////////////////////////////////// + + float m_max_power ;//best rpm + float m_power_increment_factor ; + float m_power_decrement_factor ; + float m_rpm_increment_factor ; + float m_rpm_decrement_factor ; + /////////////////////porabola + float m_a,m_b,m_c; + + float m_current_engine_power; + float m_current_rpm; + + float m_axle_friction; + + float m_fSaveMaxRPM; + float m_max_rpm; + float m_min_rpm; + float m_power_rpm;//max power + float m_torque_rpm;//max torque + + + float m_steering_speed; + float m_ref_radius; + size_t m_current_transmission_num; + /////////////////////////////////////////////////// + CCarLights m_lights; + //////////////////////////////////////////////////// + ///////////////////////////////////////////////// + void InitParabola(); + float _stdcall Parabola(float rpm); + //float GetSteerAngle(); + void LimitWheels () ; + void Drive () ; + void Starter () ; + void StartEngine () ; + void StopEngine () ; + void Stall () ; + void Clutch () ; + void Unclutch () ; + void SwitchEngine () ; + void NeutralDrive () ; + void UpdatePower () ; + void ReleasePedals () ; + void ResetKeys () ; + void ShowTrunk () ; + + //////////////////////////////////////////////////////////////////////////// + float RefWheelMaxSpeed () ; + float EngineCurTorque () ; + float RefWheelCurTorque () ; + float EnginePower () ; + float EngineDriveSpeed () ; + float DriveWheelsMeanAngleRate () ; +IC float EngineRpmFromWheels (){return dFabs(DriveWheelsMeanAngleRate()*m_current_gear_ratio);} + ///////////////////////////////////////////////////////////////////////// + void SteerRight (); + void SteerLeft (); + void SteerIdle (); + void Transmission (size_t num); + void CircleSwitchTransmission (); + void TransmissionUp (); + void TransmissionDown (); +IC size_t CurrentTransmission (){return m_current_transmission_num;} + void PressRight (); + void PressLeft (); + void PressForward (); + void PressBack (); + void PressBreaks (); + + void ReleaseRight (); + void ReleaseLeft (); + void ReleaseForward (); + void ReleaseBack (); + void ReleaseBreaks (); + void Revert (); + float EffectiveGravity (); + float AntiGravityAccel (); + float GravityFactorImpulse (); + void StartBreaking (); + void StopBreaking (); + void UpdateBack (); + + void HandBreak (); + void ReleaseHandBreak (); + void DriveForward (); + void DriveBack (); + void ParseDefinitions (); + void CreateSkeleton (CSE_Abstract *po);//creates m_pPhysicsShell + void Init (); + + void PlayExhausts (); + void StopExhausts (); + void UpdateExhausts (); + void ClearExhausts (); + void UpdateFuel (float time_delta); + float AddFuel (float ammount); //ammount - fuel to load, ret - fuel loaded + void Refuel (); + float GetFuel () { return m_fuel; } + void CarExplode (); + //////////////////////////////////////////// //////// + + void OnCameraChange (int type); + + + + + + bool HUDview ( ) { return IsFocused(); } + + static void __stdcall cb_Steer (CBoneInstance* B); + virtual void Hit (SHit* pHDS); + virtual void Die (CObject* who); + virtual void PHHit (float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type/* =ALife::eHitTypeWound */); + bool WheelHit (float P,s16 element,ALife::EHitType hit_type); + bool DoorHit (float P,s16 element,ALife::EHitType hit_type); +public: + virtual bool allowWeapon () const {return true;}; + virtual bool HUDView () const; + virtual Fvector ExitPosition (){return m_exit_position;} + virtual Fvector ExitVelocity (); + void GetVelocity (Fvector& vel) {m_pPhysicsShell->get_LinearVel(vel);} + void cam_Update (float dt, float fov); + void detach_Actor (); + bool attach_Actor (CGameObject* actor); + bool is_Door (u16 id,xr_map::iterator& i); + bool is_Door (u16 id); + bool DoorOpen (u16 id); + bool DoorClose (u16 id); + bool DoorUse (u16 id); + bool DoorSwitch (u16 id); + bool Enter (const Fvector& pos,const Fvector& dir,const Fvector& foot_pos); + bool Exit (const Fvector& pos,const Fvector& dir); + bool Use (const Fvector& pos,const Fvector& dir,const Fvector& foot_pos); + void DoExit (); + void DoEnter (); + void OpenTrunkBone (); + void CloseTrunkBone (); + // stop engine due to external forces + void KillEngine (); + u16 GetTrunkBone () const {return m_bone_trunk;}; + u16 DriverAnimationType (); + + // Core events + virtual DLL_Pure *_construct (); + virtual void Load ( LPCSTR section ); + virtual BOOL net_Spawn ( CSE_Abstract* DC ); + virtual void net_Destroy (); + virtual void UpdateCL ( ); + virtual void UpdateEx (float fov); //called by owner + + virtual void shedule_Update (u32 dt); + virtual void renderable_Render ( ); + virtual bool bfAssignMovement (CScriptEntityAction *tpEntityAction); + virtual bool bfAssignObject (CScriptEntityAction *tpEntityAction); + + // Network + virtual void net_Export (NET_Packet& P); // export to server + virtual void net_Import (NET_Packet& P); // import from server + virtual BOOL net_Relevant () { return getLocal(); }; // relevant for export to server + virtual BOOL UsedAI_Locations (); + virtual void net_Relcase (CObject* O ); + // Input + virtual void OnMouseMove (int x, int y); + virtual void OnKeyboardPress (int dik); + virtual void OnKeyboardRelease (int dik); + virtual void OnKeyboardHold (int dik); + virtual void vfProcessInputKey (int iCommand, bool bPressed); + virtual void OnEvent ( NET_Packet& P, u16 type); + virtual void OnAfterExplosion (); + virtual void OnBeforeExplosion (); + virtual void GetRayExplosionSourcePos (Fvector &pos); + virtual void ActivateExplosionBox (const Fvector &size,Fvector &in_out_pos){}; + virtual void ResetScriptData (void *P=0); + + virtual void Action (int id, u32 flags); + virtual void SetParam (int id, Fvector2 val); + virtual void SetParam (int id, Fvector val); + bool HasWeapon (); + bool WpnCanHit (); + float FireDirDiff (); + bool isObjectVisible (CScriptGameObject* O); + Fvector CurrentVel (); + virtual float GetfHealth () const {return CEntity::GetfHealth();}; + virtual float SetfHealth (float value); + + // Hits + virtual void HitSignal (float /**HitAmount/**/, Fvector& /**local_dir/**/, CObject* /**who/**/, s16 /**element/**/) {}; + virtual void HitImpulse (float /**amount/**/, Fvector& /**vWorldDir/**/, Fvector& /**vLocalDir/**/) {}; + virtual void g_fireParams (const CHudItem* /**pHudItem/**/, Fvector& /**P/**/, Fvector& /**D/**/) {}; + virtual u16 Initiator (); + // HUD + virtual void OnHUDDraw (CCustomHUD* hud); + + CCameraBase* Camera () {return active_camera;} + void SetExplodeTime (u32 et) ; + u32 ExplodeTime () ; + + CInventory* GetInventory () override { return &inventory(); } + void VisualUpdate (float fov=90.0f); +protected: + virtual void SpawnInitPhysics (CSE_Abstract *D) ; + virtual void net_Save (NET_Packet& P) ; + virtual BOOL net_SaveRelevant () ; + void SaveNetState (NET_Packet& P) ; + virtual void RestoreNetState (CSE_PHSkeleton* po) ; + void SetDefaultNetState (CSE_PHSkeleton* po) ; + + virtual bool IsHudModeNow (){return false;}; + +public: + CCar(void); + virtual ~CCar(void); + virtual BOOL AlwaysTheCrow (); + +private: + template IC void fill_wheel_vector(LPCSTR S,xr_vector& type_wheels); + IC void fill_exhaust_vector(LPCSTR S,xr_vector& exhausts); + IC void fill_doors_map(LPCSTR S,xr_map& doors); + +public: + virtual CEntity* cast_entity () {return this;} + + virtual void reinit (); + virtual void reload (LPCSTR section); + virtual CGameObject *cast_game_object () {return this;} + virtual CExplosive *cast_explosive () {return this;} + virtual CPhysicsShellHolder *cast_physics_shell_holder () {return this;} + virtual CParticlesPlayer *cast_particles_player () {return this;} + virtual CScriptEntity *cast_script_entity () {return this;} + virtual IDamageSource *cast_IDamageSource () {return this;} + virtual CHolderCustom *cast_holder_custom () {return this;} + virtual CInventoryOwner *cast_inventory_owner () {return this;} + +private: + car_memory *m_memory; + +public: + DECLARE_SCRIPT_REGISTER_FUNCTION +public: + void RemoveDamangeParticles() { m_damage_particles.Stop1(this); m_damage_particles.Stop2(this); } +}; +add_to_type_list(CCar) +#undef script_type_list +#define script_type_list save_type_list(CCar) diff --git a/src/xrGameLA/CarCameras.cpp b/src/xrGameLA/CarCameras.cpp new file mode 100644 index 000000000..c1e439231 --- /dev/null +++ b/src/xrGameLA/CarCameras.cpp @@ -0,0 +1,86 @@ +#include "stdafx.h" +#pragma hdrstop +#ifdef DEBUG +#include "ode_include.h" +#include "../StatGraph.h" +#include "PHDebug.h" +#include "phworld.h" +#endif +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "../Include/xrRender/Kinematics.h" +#include "car.h" +#include "actor.h" +#include "cameralook.h" +#include "camerafirsteye.h" +#include "level.h" +#include "../cameramanager.h" + +bool CCar::HUDView() const +{ + return active_camera->tag==ectFirst; +} + +void CCar::cam_Update (float dt, float fov) +{ + VERIFY(!ph_world->Processing()); + Fvector P,Da; + Da.set (0,0,0); + //bool owner = !!Owner(); + + + switch(active_camera->tag) { + case ectFirst: + XFORM().transform_tiny (P,m_camera_position_firsteye); + + // rotate head + if(OwnerActor()) OwnerActor()->Orientation().yaw = -active_camera->yaw; + if(OwnerActor()) OwnerActor()->Orientation().pitch = -active_camera->pitch; + break; + case ectChase: + XFORM().transform_tiny (P,m_camera_position_lookat); + + break; + case ectFree: + XFORM().transform_tiny (P,m_camera_position_free); + + break; + } + active_camera->f_fov = fov; + active_camera->Update (P,Da); + Level().Cameras().UpdateFromCamera(active_camera); +} + + +void CCar::OnCameraChange(int type) +{ + if (Owner()) + { + if (type == ectFirst) + Owner()->setVisible(FALSE); + else if (active_camera->tag == ectFirst) + Owner()->setVisible(TRUE); + + IKinematics* pKinematics = smart_cast(Owner()->Visual()); + u16 head_bone = pKinematics->LL_BoneID("bip01_head"); + + /* + if (type==ectFirst) + pKinematics->LL_HideBoneVisible(head_bone,FALSE); + else if (active_camera->tag==ectFirst) + pKinematics->LL_HideBoneVisible(head_bone,TRUE); + */ + } + + if (!active_camera||active_camera->tag!=type){ + active_camera = camera[type]; + if (ectFree==type){ + Fvector xyz; + XFORM().getXYZi(xyz); + active_camera->yaw = xyz.y; + } + } + +} + diff --git a/src/xrGameLA/CarDamageParticles.cpp b/src/xrGameLA/CarDamageParticles.cpp new file mode 100644 index 000000000..4edde1193 --- /dev/null +++ b/src/xrGameLA/CarDamageParticles.cpp @@ -0,0 +1,101 @@ +#include "stdafx.h" +#include "cardamageparticles.h" +#ifdef DEBUG +#include "ode_include.h" +#include "../StatGraph.h" +#include "PHDebug.h" +#endif +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "Car.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHWorld.h" +extern CPHWorld* ph_world; +void read_bones(IKinematics *K, LPCSTR S , xr_vector& bones) +{ + string64 S1; + int count = _GetItemCount(S); + for (int i=0 ;iLL_BoneID(S1); + R_ASSERT3(bone_id!=BI_NONE,"wrong bone",S1); + xr_vector::iterator iter=std::find(bones.begin(),bones.end(),bone_id); + R_ASSERT3(iter==bones.end(),"double bone",S1); + bones.push_back (bone_id); + } +} +void CCarDamageParticles::Init(CCar *car) +{ + IKinematics *K=smart_cast(car->Visual()); + CInifile *ini=K->LL_UserData(); + if(ini->section_exist("damage_particles")) + { + m_car_damage_particles1=ini->r_string("damage_particles","car_damage_particles1"); + m_car_damage_particles2=ini->r_string("damage_particles","car_damage_particles2"); + m_wheels_damage_particles1=ini->r_string("damage_particles","wheels_damage_particles1"); + m_wheels_damage_particles2=ini->r_string("damage_particles","wheels_damage_particles2"); + + read_bones(K,ini->r_string("damage_particles","particle_bones1"),bones1); + read_bones(K,ini->r_string("damage_particles","particle_bones2"),bones2); + } +} + +void CCarDamageParticles::Play1(CCar* car) +{ + if(*m_car_damage_particles1) + { + BIDS_I i=bones1.begin(),e=bones1.end(); + for(;e!=i;++i) car->StartParticles(m_car_damage_particles1,*i,Fvector().set(0,1,0),car->ID()); + } +} + +void CCarDamageParticles::Play2(CCar* car) +{ + VERIFY(!ph_world->Processing()); + if(*m_car_damage_particles2) + { + BIDS_I i=bones2.begin(),e=bones2.end(); + for(;e!=i;++i) car->StartParticles(m_car_damage_particles2,*i,Fvector().set(0,1,0),car->ID()); + } +} + +void CCarDamageParticles::Stop1(CCar* car) +{ + if(*m_car_damage_particles1) + { + BIDS_I i=bones1.begin(),e=bones1.end(); + for(;e!=i;++i) car->StopParticles(car->ID(), *i, false); + } +} + +void CCarDamageParticles::Stop2(CCar* car) +{ + VERIFY(!ph_world->Processing()); + if(*m_car_damage_particles2) + { + BIDS_I i=bones2.begin(),e=bones2.end(); + for(;e!=i;++i) car->StopParticles(car->ID(), *i, false); + } +} + +void CCarDamageParticles::PlayWheel1(CCar*car,u16 bone_id) +{ + VERIFY(!ph_world->Processing()); + if(*m_wheels_damage_particles1)car->StartParticles(m_wheels_damage_particles1,bone_id,Fvector().set(0,1,0),car->ID()); +} + +void CCarDamageParticles::PlayWheel2(CCar*car,u16 bone_id) +{ + VERIFY(!ph_world->Processing()); + if(*m_wheels_damage_particles2)car->StartParticles(m_wheels_damage_particles2,bone_id,Fvector().set(0,1,0),car->ID()); +} + + +void CCarDamageParticles::Clear() +{ + bones1.clear(); + bones2.clear(); +} \ No newline at end of file diff --git a/src/xrGameLA/CarDamageParticles.h b/src/xrGameLA/CarDamageParticles.h new file mode 100644 index 000000000..42eba940e --- /dev/null +++ b/src/xrGameLA/CarDamageParticles.h @@ -0,0 +1,23 @@ +#pragma once +class CCar; +DEFINE_VECTOR(u16,BIDS,BIDS_I); +struct CCarDamageParticles +{ + BIDS bones1; + BIDS bones2; + shared_str m_wheels_damage_particles1; + shared_str m_wheels_damage_particles2; + shared_str m_car_damage_particles1; + shared_str m_car_damage_particles2; + +public: + +void Init (CCar* car); +void Clear (); +void Play1 (CCar* car); +void Play2 (CCar* car); +void Stop1 (CCar* car); +void Stop2 (CCar* car); +void PlayWheel1 (CCar*car,u16 bone_id); +void PlayWheel2 (CCar*car,u16 bone_id); +}; \ No newline at end of file diff --git a/src/xrGameLA/CarDoors.cpp b/src/xrGameLA/CarDoors.cpp new file mode 100644 index 000000000..08390e097 --- /dev/null +++ b/src/xrGameLA/CarDoors.cpp @@ -0,0 +1,787 @@ +#include "stdafx.h" +#ifdef DEBUG +#include "ode_include.h" +#include "../StatGraph.h" +#include "PHDebug.h" +#endif +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "car.h" +#include "../../xrNetServer/net_utils.h" +#include "../Include/xrRender/Kinematics.h" +#include "MathUtils.h" +#include "game_object_space.h" +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool CCar::DoorHit(float P,s16 element,ALife::EHitType hit_type) +{ + if(hit_type==ALife::eHitTypeStrike && P > 20.f) + { + xr_map::iterator i=m_doors.begin(),e=m_doors.end(); + //for(;e!=i;++i)i->second.Open(); + } + xr_map ::iterator i=m_doors.find(element); + if(i!=m_doors.end()) + { + i->second.Hit(P); + return true; + } + else return false; +} +void CCar::SDoor::Init() +{ + update=false; + joint=bone_map.find(bone_id)->second.joint; + if(!joint) return; + R_ASSERT2(dJointGetType(joint->GetDJoint())==dJointTypeHinge,"Wrong door joint!!! Only simple joint valid for a door and only one axis can be active, check other axes are zerro limited !!!"); + joint->SetBackRef(&joint); + Fvector door_position,door_axis; + dJointGetHingeAnchor (joint->GetDJoint(),(float*) &door_position); + dJointGetHingeAxis (joint->GetDJoint(), (float*) &door_axis); + door_position.sub(pcar->XFORM().c); + + Fmatrix door_transform; + joint->PSecond_element()->InterpolateGlobalTransform(&door_transform); + closed_door_form_in_object.set(joint->PSecond_element()->mXFORM); +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + Fvector jaxis,janchor; + float lo_ext,hi_ext,ext; + joint->GetAxisDirDynamic(0,jaxis); + joint->GetAnchorDynamic(janchor); + joint->PSecond_element()->get_Extensions(jaxis,janchor.dotproduct(jaxis),lo_ext,hi_ext); + door_plane_ext.x=hi_ext-lo_ext; + Fvector jaxis_in_door; + Fmatrix inv_door_transform; + inv_door_transform.set(door_transform); + inv_door_transform.invert(); + inv_door_transform.transform_dir(jaxis_in_door,jaxis); + + float door_dir_sign; + if(jaxis_in_door.x>jaxis_in_door.y) + { + if(jaxis_in_door.x>jaxis_in_door.z) + { + joint->PSecond_element()->get_Extensions(door_transform.j,janchor.dotproduct(door_transform.j),lo_ext,hi_ext); + door_plane_ext.y=hi_ext-lo_ext; + door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door_plane_axes.x=0; + door_plane_axes.y=1; + joint->PSecond_element()->get_Extensions(door_transform.k,janchor.dotproduct(door_transform.k),lo_ext,hi_ext); + ext=hi_ext-lo_ext; + if(ext>door_plane_ext.y) + { + door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door_plane_ext.y=ext; + door_plane_axes.y=2; + } + } + else + { + joint->PSecond_element()->get_Extensions(door_transform.j,janchor.dotproduct(door_transform.j),lo_ext,hi_ext); + door_plane_ext.y=hi_ext-lo_ext; + door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door_plane_axes.x=2; + door_plane_axes.y=1; + joint->PSecond_element()->get_Extensions(door_transform.i,janchor.dotproduct(door_transform.i),lo_ext,hi_ext); + ext=hi_ext-lo_ext; + if(ext>door_plane_ext.y) + { + door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door_plane_ext.y=ext; + door_plane_axes.y=0; + } + } + } + else + { + if(jaxis_in_door.y>jaxis_in_door.z) + { + joint->PSecond_element()->get_Extensions(door_transform.i,janchor.dotproduct(door_transform.i),lo_ext,hi_ext); + door_plane_ext.y=hi_ext-lo_ext; + door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door_plane_axes.x=1; + door_plane_axes.y=0; + joint->PSecond_element()->get_Extensions(door_transform.k,janchor.dotproduct(door_transform.k),lo_ext,hi_ext); + ext=hi_ext-lo_ext; + if(ext>door_plane_ext.y) + { + door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door_plane_ext.y=ext; + door_plane_axes.y=2; + } + } + else + { + joint->PSecond_element()->get_Extensions(door_transform.j,janchor.dotproduct(door_transform.j),lo_ext,hi_ext); + door_plane_ext.y=hi_ext-lo_ext; + door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door_plane_axes.x=2; + door_plane_axes.y=1; + joint->PSecond_element()->get_Extensions(door_transform.i,janchor.dotproduct(door_transform.i),lo_ext,hi_ext); + ext=hi_ext-lo_ext; + if(ext>door_plane_ext.y) + { + door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door_plane_ext.y=ext; + door_plane_axes.y=0; + } + } + } + + switch(door_plane_axes.y) + { + case 0: + door_dir_in_door.set(door_dir_sign,0.f,0.f); + break; + case 1: + door_dir_in_door.set(0.f,door_dir_sign,0.f); + break; + case 2: + door_dir_in_door.set(0.f,0.f,door_dir_sign); + break; + default: NODEFAULT; + } +/////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + ///////////////////////////define positive open/////////////////////////////////// + Fvector door_dir,door_test; + door_transform.transform_dir(door_dir,door_dir_in_door); + //cr_dr_pos.crossproduct(door_dir,door_position); + door_test.crossproduct(door_dir,door_axis); + door_test.normalize(); + joint->PFirst_element()->get_Extensions(door_test,door_transform.c.dotproduct(door_test),lo_ext,hi_ext); + if(hi_ext>-lo_ext) pos_open=-1.f; + else pos_open=1.f; + //pos_open=-cr_dr_pos.dotproduct(door_axis); + //pos_open=door_position.dotproduct(pcar->m_root_transform.i)*door_axis.dotproduct(pcar->m_root_transform.j); + if(pos_open>0.f) + { + pos_open=1.f; + joint->GetLimits(closed_angle,opened_angle,0); + // closed_angle+=2.f*M_PI/180.f; + //opened_angle-=4.f*M_PI/180.f; + opened_angle-=opened_angle/4.f; + } + else + { + pos_open=-1.f; + joint->GetLimits(opened_angle,closed_angle,0); + opened_angle+=2.f*M_PI/180.f; + closed_angle-=2.f*M_PI/180.f; + } + Fvector shoulder; + + shoulder.sub(door_transform.c,joint->PSecond_element()->mass_Center()); + torque=shoulder.magnitude()*joint->PSecond_element()->getMass()*pcar->m_doors_torque_factor*10.f; + state=opened; +// Close(); +} +void CCar::SDoor::Open() +{ + + if(!joint) + { + state=opened; + + return; + } + + switch(state) + { + case closed: + ClosedToOpening(); + PlaceInUpdate(); + case closing: + state=opening; + + ApplyOpenTorque(); + case opened: + case opening: break; + case broken : break; + default: NODEFAULT; + } + +} + +void CCar::SDoor::Close() +{ + if(!joint) + { + state=closed; + return; + } + + switch(state) + { + case opened: + PlaceInUpdate(); + case opening: + state=closing; + ApplyCloseTorque(); + case closed: + case closing: + break; + default: NODEFAULT; + } + +} + +void CCar::SDoor::PlaceInUpdate() +{ + if(update) return; + pcar->m_doors_update.push_back(this); + //list_iterator=(--pcar->m_doors_update.end()); + update=true; +} + +void CCar::SDoor::RemoveFromUpdate() +{ + update=false; +} +void CCar::SDoor::Update() +{ + switch(state) + { + case closing: + { + if(pos_open*closed_angle>pos_open*GetAngle()) ClosingToClosed(); + + break; + } + case opening: + { + if(pos_open*opened_angle1000) + { + ApplyTorque(torque/5.f,a_vel); + RemoveFromUpdate(); + } + } + } +} + +void CCar::SDoor::Use() +{ + switch(state) { +case opened: +case opening: + Close(); + break; +case closed: +case closing: + Open(); + break; +default: return; + } +} + +void CCar::SDoor::Switch() +{ + switch(state) { +case opened: + Close(); + break; +case closed: + Open(); + break; +default: return; + } +} + +void CCar::SDoor::ApplyTorque(float atorque,float aa_vel) +{ + if(!joint||!joint->bActive)return; + joint->PSecond_element()->Enable(); + dJointSetHingeParam(joint->GetDJoint(),dParamFMax,atorque); + dJointSetHingeParam(joint->GetDJoint(),dParamVel,aa_vel*pos_open); +} +void CCar::SDoor::ApplyOpenTorque() +{ + if(!joint->bActive)return; + joint->PSecond_element()->Enable(); + dJointSetHingeParam(joint->GetDJoint(),dParamFMax,torque); + dJointSetHingeParam(joint->GetDJoint(),dParamVel,a_vel*pos_open); +} + +void CCar::SDoor::ApplyCloseTorque() +{ + if(!joint->bActive)return; + joint->PSecond_element()->Enable(); + dJointSetHingeParam(joint->GetDJoint(),dParamFMax,torque); + dJointSetHingeParam(joint->GetDJoint(),dParamVel,-a_vel*pos_open); +} + +void CCar::SDoor::NeutralTorque(float atorque) +{ + if(!joint->bActive)return; + //joint->PSecond_element()->Enable(); + dJointSetHingeParam(joint->GetDJoint(),dParamFMax,atorque); + dJointSetHingeParam(joint->GetDJoint(),dParamVel,0); +} + + + +void CCar::SDoor::ClosedToOpening() +{ + if(!joint)return; + if(joint->bActive)return; + Fmatrix door_form,root_form; + IKinematics* pKinematics=smart_cast(pcar->Visual()); +// CBoneData& bone_data= pKinematics->LL_GetData(u16(bone_id)); + CBoneInstance& bone_instance=pKinematics->LL_GetBoneInstance(u16(bone_id)); + bone_instance.set_callback(bctPhysics,pcar->PPhysicsShell()->GetBonesCallback(),joint->PSecond_element()); + + door_form.set(bone_instance.mTransform); + //door_form.mulB(pcar->XFORM()); + joint->PSecond_element()->mXFORM.set(door_form); + pcar->m_pPhysicsShell->GetGlobalTransformDynamic(&root_form); + joint->PSecond_element()->Activate(root_form,false); + pcar->m_pPhysicsShell->Enable(); + joint->Activate(); + pKinematics->CalculateBones(); +} + +void CCar::SDoor::ClosingToClosed() +{ + state =closed; + if(!joint) return; + smart_cast(pcar->Visual())->CalculateBones(); + +// Fmatrix door_form; + IKinematics* pKinematics=smart_cast(pcar->Visual()); +// CBoneData& bone_data= pKinematics->LL_GetData(u16(bone_id)); + CBoneInstance& bone_instance=pKinematics->LL_GetBoneInstance(u16(bone_id)); + bone_instance.set_callback(bctPhysics,0,joint->PFirst_element(),FALSE); + //bone_instance.set_callback( bone_instance.callback_type(),bone_instance.callback(),bone_instance.callback_param(),FALSE); + //bone_instance.Callback_overwrite=FALSE; + joint->PSecond_element()->Deactivate(); + joint->Deactivate(); + + RemoveFromUpdate(); + + //door_form.set(bone_data.bind_transform); + //bone_instance.mTransform.set(door_form); +} + + + +float CCar::SDoor::GetAngle() +{ + if(!joint||!joint->bActive) return 0.f; + return dJointGetHingeAngle(joint->GetDJoint()); +} + + +static xr_vector bones_bind_forms; +bool CCar::SDoor::IsFront(const Fvector& pos,const Fvector& dir) +{ + IKinematics* K=PKinematics(pcar->Visual()); + //CBoneInstance bi=K->LL_GetBoneInstance(bone_id); + //CBoneData& bd=K->LL_GetData(bone_id); + K->LL_GetBindTransform(bones_bind_forms); + // Fobb bb=bd.obb; + Fvector tdir;tdir.set(pcar->XFORM().i);if(tdir.dotproduct(dir)<0.f)tdir.invert(); + Fmatrix pf; + pf.mul(pcar->XFORM(),bones_bind_forms[bone_id]); + Fvector dif,dif1; + dif.sub(pf.c,pos); + pcar->Center(dif1); + Fvector c_to_d;c_to_d.sub(pf.c,dif1); + + dif1.sub(pos); + //dif.normalize_safe(); + return (dif1.dotproduct(tdir)>dif.dotproduct(tdir) && abs(c_to_d.dotproduct(tdir)) < dif1.dotproduct(tdir) ); +} +bool CCar::SDoor::IsInArea(const Fvector& pos,const Fvector& dir) +{ + if(!joint) + { + if(!IsFront(pos,dir))return false; + + IKinematics* K=PKinematics(pcar->Visual()); + //CBoneInstance bi=K->LL_GetBoneInstance(bone_id); + //CBoneData& bd=K->LL_GetData(bone_id); + K->LL_GetBindTransform(bones_bind_forms); + // Fobb bb=bd.obb; + Fvector tdir;tdir.set(pcar->XFORM().i);if(tdir.dotproduct(dir)<0.f)tdir.invert(); + Fmatrix pf; + pf.mul(pcar->XFORM(),bones_bind_forms[bone_id]); + Fvector dif,dif1; + dif.sub(pf.c,pos); + pcar->Center(dif1); + Fvector c_to_d;c_to_d.sub(pf.c,dif1); + dif1.sub(pos); + return 2.f*abs(c_to_d.dotproduct(pcar->XFORM().i))>abs(dif1.dotproduct(pcar->XFORM().i)); + } + Fmatrix closed_door_form,door_form; + Fvector closed_door_dir,door_dir,anchor_to_pos,door_axis; + joint->GetAxisDirDynamic(0,door_axis); + joint->PSecond_element()->InterpolateGlobalTransform(&door_form); + + closed_door_form.mul(pcar->XFORM(),closed_door_form_in_object); + closed_door_form.transform_dir(closed_door_dir,door_dir_in_door); + //closed_door_form_in_object.transform_dir(closed_door_dir,door_dir_in_door); + //pcar->XFORM().transform_dir(closed_door_dir); + + + door_form.transform_dir(door_dir,door_dir_in_door); + door_dir.normalize(); + closed_door_dir.normalize(); + float cprg=door_dir.dotproduct(door_form.c); + float loe,hie; + joint->PSecond_element()->get_Extensions(door_dir,cprg,loe,hie); + float signum=(hie>-loe) ? 1.f : -1.f; + + Fvector closed_door_norm,door_norm; + closed_door_norm.crossproduct(door_axis,closed_door_dir); + door_norm.crossproduct(door_axis,door_dir); + anchor_to_pos.sub(pos,closed_door_form.c); + float a,b,c; + a=anchor_to_pos.dotproduct(closed_door_dir)*signum; + b=anchor_to_pos.dotproduct(door_dir)*signum; + c= anchor_to_pos.dotproduct(closed_door_norm)*anchor_to_pos.dotproduct(door_norm); + if( + a < (signum>0.f ? hie : -loe) && a > 0.f && + b < (signum>0.f ? hie : -loe) && b > 0.f && + anchor_to_pos.dotproduct(closed_door_norm)*anchor_to_pos.dotproduct(door_norm)<0.f + )return true; + else return false; +} + +bool CCar::SDoor::CanExit(const Fvector& pos,const Fvector& dir) +{ + if(state==closed&&joint||(pcar->GetTrunkBone()==bone_id))return false; + return TestPass(pos,dir); +} + +void CCar::SDoor::GetExitPosition(Fvector& pos) +{ +// if(!joint) + { + IKinematics* K=PKinematics(pcar->Visual()); + //CBoneInstance bi=K->LL_GetBoneInstance(bone_id); + CBoneData& bd=K->LL_GetData(bone_id); + K->LL_GetBindTransform(bones_bind_forms); + Fobb bb;//=bd.obb; + + Fmatrix pf; + pf.mul(pcar->XFORM(),bones_bind_forms[bone_id]); + bb.transform(bd.obb,pf); + bb.xform_get(pf); + pos.set(pf.c); + Fvector add,add1; + MAX_OF(abs(pf.i.y),add.set(pf.i);add.mul(bb.m_halfsize.x*fsignum(pf.i.y)),abs(pf.j.y),add.set(pf.j);add.mul(bb.m_halfsize.y*fsignum(pf.j.y)),abs(pf.k.y),add.set(pf.k);add.mul(bb.m_halfsize.z*fsignum(pf.k.y))); + pos.sub(add); + + MIN_OF(bb.m_halfsize.x,add1.set(pf.i);add1.mul(bb.m_halfsize.x), + bb.m_halfsize.y,add1.set(pf.j);add1.mul(bb.m_halfsize.y), + bb.m_halfsize.z,add1.set(pf.k);add1.mul(bb.m_halfsize.z)) + Fvector dir_from_car;dir_from_car.sub(pf.c,pcar->Position()); + dir_from_car.y=0.f; + if(add1.dotproduct(dir_from_car)<0.f)add1.invert(); + add1.mul(3.f); + pos.add(add1); + return; + } + //float lo_ext,hi_ext; + //Fvector door_axis,door_pos,door_dir,closed_door_dir,add; + //joint->GetAxisDirDynamic(0,door_axis); + //joint->GetAnchorDynamic(door_pos); + + //Fmatrix door_form,root_form; + //root_form.mul(pcar->m_root_transform,pcar->XFORM()); + //joint->PSecond_element()->InterpolateGlobalTransform(&door_form); + //door_form.transform_dir(door_dir,door_dir_in_door); + + // + //closed_door_form_in_object.transform_dir(closed_door_dir,door_dir_in_door); + //pcar->XFORM().transform_dir(closed_door_dir); + + + //pos.set(door_pos); + //door_axis.normalize(); + //float center_prg=door_axis.dotproduct(door_pos); + //joint->PSecond_element()->get_Extensions(door_axis,center_prg,lo_ext,hi_ext); + //add.set(door_axis); + //if(door_axis.dotproduct(root_form.j)>0.f) add.mul(lo_ext); + //else add.mul(hi_ext); + //pos.add(add); + + //door_dir.normalize(); + //center_prg=door_pos.dotproduct(door_dir); + //joint->PSecond_element()->get_Extensions(door_dir,center_prg,lo_ext,hi_ext); + //closed_door_dir.normalize(); + //add.add(closed_door_dir,door_dir); + //add.normalize(); + //if(hi_ext>-lo_ext)add.mul(hi_ext); + //else add.mul(lo_ext); + //pos.add(add); +} + + + +bool CCar::SDoor::TestPass(const Fvector& pos,const Fvector& dir) +{ + if(!joint) + { + IKinematics* K=PKinematics(pcar->Visual()); + //CBoneInstance bi=K->LL_GetBoneInstance(bone_id); + //CBoneData& bd=K->LL_GetData(bone_id); + K->LL_GetBindTransform(bones_bind_forms); + // Fobb bb=bd.obb; + Fmatrix pf; + pf.mul(pcar->XFORM(),bones_bind_forms[bone_id]); + Fvector dif; + dif.sub(pf.c,pos); + //dif.normalize_safe(); + return (dif.dotproduct(dir)>0.f); + } + float lo_ext,hi_ext; + Fvector door_axis,door_pos,door_dir,closed_door_dir; + + joint->GetAxisDirDynamic(0,door_axis); + joint->GetAnchorDynamic(door_pos); + + Fmatrix door_form,root_form; + root_form.mul(pcar->m_root_transform,pcar->XFORM()); + joint->PSecond_element()->InterpolateGlobalTransform(&door_form); + door_form.transform_dir(door_dir,door_dir_in_door); +// closed_door_form.mul(closed_door_form_in_object,pcar->XFORM()); + closed_door_form_in_object.transform_dir(closed_door_dir,door_dir_in_door); + pcar->XFORM().transform_dir(closed_door_dir); + door_axis.normalize(); + + door_dir.normalize(); + closed_door_dir.normalize(); + + Fvector closed_door_norm; + + closed_door_norm.crossproduct(door_axis,closed_door_dir); + + Fvector point_on_door,add,sub; + add.set(dir); + sub.sub(pos,door_pos); + add.mul(-sub.dotproduct(closed_door_norm)/(dir.dotproduct(closed_door_norm))); + + if(add.dotproduct(dir)<0.f) return false; + + point_on_door.add(pos,add); + + float center_prg=door_pos.dotproduct(door_dir); + joint->PSecond_element()->get_Extensions(door_dir,center_prg,lo_ext,hi_ext); + + float point_prg=point_on_door.dotproduct(closed_door_dir); + center_prg=door_pos.dotproduct(closed_door_dir); + if(!(center_prg+hi_ext>point_prg)||!(center_prg+lo_extPSecond_element()->get_Extensions(door_axis,center_prg,lo_ext,hi_ext); + + point_prg=point_on_door.dotproduct(door_axis); + if(!(center_prg+hi_ext>point_prg)||!(center_prg+lo_extGetTrunkBone()!=bone_id); +} + +void CCar::SDoor::SaveNetState(NET_Packet& P) +{ + CSE_ALifeCar::SDoorState ds; + ds.health=Health(); + ds.open_state=u8(state); + ds.write(P); +} + +void CCar::SDoor::RestoreNetState(const CSE_ALifeCar::SDoorState& a_state) +{ + eState lstate=eState(a_state.open_state); + if(lstate==closed) ClosingToClosed(); + state=lstate; + SetHealth(a_state.health); + RestoreEffect(); +} + +void CCar::SDoor::SetDefaultNetState() +{ + ClosingToClosed(); +} + +void CCar::SDoor::Break() +{ + switch(state) { + case closed: + ClosedToOpening(); + break; + case opened: + case closing: + RemoveFromUpdate(); + case opening: + ApplyTorque(torque/10.f,0.f); + } + if(joint) + { + dVector3 v;float sf,df; + dJointID dj=joint->GetDJoint(); + dJointGetHingeAxis(dj,v); + v[0]+=0.1f;v[1]+=0.1f;v[2]+=0.1f; + dNormalize3(v); + dJointSetHingeAxis(dj,v[0],v[1],v[2]); + joint->GetJointSDfactors(sf,df); + sf/=30.f;df*=8.f; + joint->SetJointSDfactors(sf,df); + joint->GetAxisSDfactors(sf,df,0); + sf/=20.f;df*=8.f; + joint->SetAxisSDfactors(sf,df,0); + float lo,hi; + joint->GetLimits(lo,hi,0); + if(pos_open>0.f) + joint->SetLimits(lo+M_PI/4.f,hi,0); + else + joint->SetLimits(lo,hi-M_PI/4.f,0); + } + //ApplyOpenTorque(); + state=broken; +} + +void CCar::SDoor::ApplyDamage(u16 level) +{ + inherited::ApplyDamage(level); + switch(level) + { + case 1: Break(); + } +} +CCar::SDoor::SDoorway::SDoorway() +{ + door=NULL; + door_plane_ext.set(0.f,0.f); + door_plane_axes.set(0,0); +} +void CCar::SDoor::SDoorway::Init(SDoor* adoor) +{ +door=adoor; +Fmatrix door_transform; +door->joint->PSecond_element()->InterpolateGlobalTransform(&door_transform); +door->closed_door_form_in_object.set(door->joint->PSecond_element()->mXFORM); +Fvector jaxis,janchor; +door->joint->GetAxisDirDynamic(0,jaxis); +door->joint->GetAnchorDynamic(janchor); +Fmatrix inv_door_transform; +inv_door_transform.set(door_transform); +inv_door_transform.invert(); +Fvector door_axis_in_door; +inv_door_transform.transform_dir(door_axis_in_door,jaxis); +float lo_ext,hi_ext,ext; + +if(_abs(door_axis_in_door.x)>_abs(door_axis_in_door.y)) +{ + if(_abs(door_axis_in_door.x)>_abs(door_axis_in_door.z)) + { + //door axis aligned along x + door_plane_axes.y=0; //door axis is x (door_plane_axes.y stores door axis direction (i,j,k)=(0,1,2) + door->joint->PSecond_element()->get_Extensions(door_transform.i,janchor.dotproduct(door_transform.i),lo_ext,hi_ext); + door->door_plane_ext.y=hi_ext-lo_ext; //door extension along door axis + + door->joint->PSecond_element()->get_Extensions(door_transform.j,janchor.dotproduct(door_transform.j),lo_ext,hi_ext); + door_plane_ext.x=hi_ext-lo_ext;//door extensions + door_plane_axes.x=1; //door_plane_axes.x stores door direction it may be j or k in this point + + door->joint->PSecond_element()->get_Extensions(door_transform.k,janchor.dotproduct(door_transform.k),lo_ext,hi_ext); + ext=hi_ext-lo_ext; + if(_abs(ext)>_abs(door_axis_in_door.x)) + { + door->door_plane_ext.x=ext; + door->door_plane_axes.x=2; + } + + } + else + { + door->joint->PSecond_element()->get_Extensions(door_transform.j,janchor.dotproduct(door_transform.j),lo_ext,hi_ext); + //door->door_plane_ext.y=hi_ext-lo_ext; + //door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + //door->door_plane_axes.x=2; + //door->door_plane_axes.y=1; + + door->joint->PSecond_element()->get_Extensions(door_transform.i,janchor.dotproduct(door_transform.i),lo_ext,hi_ext); + ext=hi_ext-lo_ext; + if(_abs(ext)>door->door_plane_ext.y) + { + //door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + //door->door_plane_ext.y=ext; + //door->door_plane_axes.y=0; + + } + } +} +else +{ + if(door_axis_in_door.y>door_axis_in_door.z) + { + door->joint->PSecond_element()->get_Extensions(door_transform.i,janchor.dotproduct(door_transform.i),lo_ext,hi_ext); + door->door_plane_ext.y=hi_ext-lo_ext; + //door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door->door_plane_axes.x=1; + door->door_plane_axes.y=0; + door->joint->PSecond_element()->get_Extensions(door_transform.k,janchor.dotproduct(door_transform.k),lo_ext,hi_ext); + ext=hi_ext-lo_ext; + if(ext>door->door_plane_ext.y) + { + //door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door->door_plane_ext.y=ext; + door->door_plane_axes.y=2; + } + } + else + { + door->joint->PSecond_element()->get_Extensions(door_transform.j,janchor.dotproduct(door_transform.j),lo_ext,hi_ext); + door->door_plane_ext.y=hi_ext-lo_ext; + //door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door->door_plane_axes.x=2; + door->door_plane_axes.y=1; + door->joint->PSecond_element()->get_Extensions(door_transform.i,janchor.dotproduct(door_transform.i),lo_ext,hi_ext); + ext=hi_ext-lo_ext; + if(ext>door->door_plane_ext.y) + { + //door_dir_sign=hi_ext>-lo_ext ? 1.f : -1.f; + door->door_plane_ext.y=ext; + door->door_plane_axes.y=0; + } + } +} +/* +switch(door->door_plane_axes.y) +{ +case 0: + door_dir.set(door_dir_sign,0.f,0.f); + break; +case 1: + door_dir.set(0.f,door_dir_sign,0.f); + break; +case 2: + door_dir.set(0.f,0.f,door_dir_sign); + break; +default: NODEFAULT; +} +*/ +} +void CCar::SDoor::SDoorway::Trace(const Fvector &point,const Fvector &dir) +{ + +} diff --git a/src/xrGameLA/CarExhaust.cpp b/src/xrGameLA/CarExhaust.cpp new file mode 100644 index 000000000..2c257d206 --- /dev/null +++ b/src/xrGameLA/CarExhaust.cpp @@ -0,0 +1,73 @@ +#include "stdafx.h" +#ifdef DEBUG +#include "ode_include.h" +#include "../StatGraph.h" +#include "PHDebug.h" +#endif +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "car.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHWorld.h" +extern CPHWorld* ph_world; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +CCar::SExhaust::~SExhaust() +{ + + CParticlesObject::Destroy(p_pgobject); +} + +void CCar::SExhaust::Init() +{ + VERIFY(!ph_world->Processing()); + pelement=(bone_map.find(bone_id))->second.element; + IKinematics* K=smart_cast(pcar->Visual()); + CBoneData& bone_data=K->LL_GetData(u16(bone_id)); + transform.set(bone_data.bind_transform); + ///transform.mulA(pcar->XFORM()); + //Fmatrix element_transform; + //pelement->InterpolateGlobalTransform(&element_transform); + //element_transform.invert(); + //transform.mulA(element_transform); + p_pgobject=CParticlesObject::Create(*pcar->m_exhaust_particles,FALSE); + Fvector zero_vector; + zero_vector.set(0.f,0.f,0.f); + p_pgobject->UpdateParent(pcar->XFORM(), zero_vector ); + +} + +void CCar::SExhaust::Update() +{ + VERIFY(!ph_world->Processing()); + Fmatrix global_transform; + pelement->InterpolateGlobalTransform(&global_transform); + global_transform.mulB_43(transform); + dVector3 res; + Fvector res_vel; + dBodyGetPointVel(pelement->get_body(),global_transform.c.x,global_transform.c.y,global_transform.c.z,res); + CopyMemory (&res_vel,res,sizeof(Fvector)); + //velocity.mul(0.95f); + //res_vel.mul(0.05f); + //velocity.add(res_vel); + p_pgobject->UpdateParent(global_transform,res_vel); +} + +void CCar::SExhaust::Clear() +{ + CParticlesObject::Destroy(p_pgobject); +} + +void CCar::SExhaust::Play() +{ + VERIFY(!ph_world->Processing()); + p_pgobject->Play(false); + Update(); +} + +void CCar::SExhaust::Stop() +{ + VERIFY(!ph_world->Processing()); + p_pgobject->Stop(); +} \ No newline at end of file diff --git a/src/xrGameLA/CarInput.cpp b/src/xrGameLA/CarInput.cpp new file mode 100644 index 000000000..6a3e9ac3c --- /dev/null +++ b/src/xrGameLA/CarInput.cpp @@ -0,0 +1,273 @@ +#include "stdafx.h" +#pragma hdrstop +#ifdef DEBUG +#include "ode_include.h" +#include "../StatGraph.h" +#include "PHDebug.h" +#endif +#include "alife_space.h" +#include "hit.h" +#include "phdestroyable.h" +#include "car.h" +#include "actor.h" +#include "cameralook.h" +#include "camerafirsteye.h" +#include "script_entity_action.h" +#include "xr_level_controller.h" +#include "../../Include/xrRender/Kinematics.h" +#include "level.h" +#include "CarWeapon.h" + +void CCar::OnMouseMove(int dx, int dy) +{ + if (Remote()) return; + + CCameraBase* C = active_camera; + float scale = (C->f_fov/g_fov)*psMouseSens * psMouseSensScale/50.f; + if (dx){ + float d = float(dx)*scale; + C->Move ((d<0)?kLEFT:kRIGHT, _abs(d)); + } + if (dy){ + float d = ((psMouseInvert.test(1))?-1:1)*float(dy)*scale*3.f/4.f; + C->Move ((d>0)?kUP:kDOWN, _abs(d)); + } +} + +bool CCar::bfAssignMovement(CScriptEntityAction *tpEntityAction) +{ + if (tpEntityAction->m_tMovementAction.m_bCompleted) + return(false); + + u32 l_tInput = tpEntityAction->m_tMovementAction.m_tInputKeys; + + vfProcessInputKey(kFWD , !!(l_tInput & CScriptMovementAction::eInputKeyForward )); + vfProcessInputKey(kBACK , !!(l_tInput & CScriptMovementAction::eInputKeyBack )); + vfProcessInputKey(kL_STRAFE , !!(l_tInput & CScriptMovementAction::eInputKeyLeft )); + vfProcessInputKey(kR_STRAFE , !!(l_tInput & CScriptMovementAction::eInputKeyRight )); + vfProcessInputKey(kACCEL , !!(l_tInput & CScriptMovementAction::eInputKeyShiftUp )); + vfProcessInputKey(kCROUCH , !!(l_tInput & CScriptMovementAction::eInputKeyShiftDown )); + vfProcessInputKey(kJUMP , !!(l_tInput & CScriptMovementAction::eInputKeyBreaks )); + if (!!(l_tInput & CScriptMovementAction::eInputKeyEngineOn)) StartEngine(); + if (!!(l_tInput & CScriptMovementAction::eInputKeyEngineOff)) StopEngine(); + + //if (_abs(tpEntityAction->m_tMovementAction.m_fSpeed) > EPS_L) + //m_current_rpm = _abs(tpEntityAction->m_tMovementAction.m_fSpeed*m_current_gear_ratio); + + return (true); +} + +bool CCar::bfAssignObject(CScriptEntityAction *tpEntityAction) +{ + CScriptObjectAction &l_tObjectAction = tpEntityAction->m_tObjectAction; + if (l_tObjectAction.m_bCompleted || !xr_strlen(l_tObjectAction.m_caBoneName)) + return((l_tObjectAction.m_bCompleted = true) == false); + + s16 l_sBoneID = smart_cast(Visual())->LL_BoneID(l_tObjectAction.m_caBoneName); + if (is_Door(l_sBoneID)) { + switch(l_tObjectAction.m_tGoalType) { + case MonsterSpace::eObjectActionActivate : { + if (!DoorOpen(l_sBoneID)) + return((l_tObjectAction.m_bCompleted = true) == false); + break; + } + case MonsterSpace::eObjectActionDeactivate : { + if (!DoorClose(l_sBoneID)) + return((l_tObjectAction.m_bCompleted = true) == false); + break; + } + case MonsterSpace::eObjectActionUse : { + if (!DoorSwitch(l_sBoneID)) + return((l_tObjectAction.m_bCompleted = true) == false); + break; + } + default : + return ((l_tObjectAction.m_bCompleted = true) == false); + } + return (false); + } + SCarLight* light=NULL; + if (m_lights.findLight(l_sBoneID,light)) { + switch(l_tObjectAction.m_tGoalType) { + case MonsterSpace::eObjectActionActivate : { + light->TurnOn(); + return ((l_tObjectAction.m_bCompleted = true) == false); + } + case MonsterSpace::eObjectActionDeactivate : { + light->TurnOff(); + return ((l_tObjectAction.m_bCompleted = true) == false); + } + case MonsterSpace::eObjectActionUse : { + light->Switch(); + return ((l_tObjectAction.m_bCompleted = true) == false); + } + default : + return ((l_tObjectAction.m_bCompleted = true) == false); + } + + } + + return (false); +} + +void CCar::vfProcessInputKey (int iCommand, bool bPressed) +{ + if (bPressed) + OnKeyboardPress (iCommand); + else + OnKeyboardRelease (iCommand); +} + +void CCar::OnKeyboardPress(int cmd) +{ + if (Remote()) return; + + switch (cmd) + { + case kCAM_1: OnCameraChange(ectFirst); break; + case kCAM_2: OnCameraChange(ectChase); break; + case kCAM_3: OnCameraChange(ectFree); break; + case kACCEL: TransmissionUp(); break; + case kCROUCH: TransmissionDown(); break; +// case kFWD: DoorOpen(1); break; + case kFWD: PressForward(); break; + case kBACK: PressBack(); break; + case kR_STRAFE: PressRight(); if (OwnerActor()) OwnerActor()->steer_Vehicle(1); break; + case kL_STRAFE: PressLeft(); if (OwnerActor()) OwnerActor()->steer_Vehicle(-1);break; + case kJUMP: PressBreaks(); break; + case kENGINE: + SwitchEngine(); + if (HasWeapon()) + m_car_weapon->Action(CCarWeapon::eWpnActivate, b_engine_on); + break; + case kTORCH: m_lights.SwitchHeadLights();break; + case kNIGHT_VISION: Actor()->SwitchNightVision();break;//tatarinrafa: remove if cause "disbalance" + case kUSE: break; + case kWPN_FIRE: if (HasWeapon()) m_car_weapon->Action(CCarWeapon::eWpnFire, 1); break; + }; + +} + +void CCar::OnKeyboardRelease(int cmd) +{ + if (Remote()) return; + switch (cmd) + { + case kACCEL:break; + case kFWD: ReleaseForward(); break; + case kBACK: ReleaseBack(); break; + case kL_STRAFE: ReleaseLeft(); if (OwnerActor()) OwnerActor()->steer_Vehicle(0); break; + case kR_STRAFE: ReleaseRight(); if (OwnerActor()) OwnerActor()->steer_Vehicle(0); break; + case kJUMP: ReleaseBreaks(); break; + case kWPN_FIRE: if (HasWeapon()) m_car_weapon->Action(CCarWeapon::eWpnFire, 0); break; + }; +} + +void CCar::OnKeyboardHold(int cmd) +{ + if (Remote()) return; + + switch(cmd) + { + case kCAM_ZOOM_IN: + case kCAM_ZOOM_OUT: + case kUP: + case kDOWN: + case kLEFT: + case kRIGHT: active_camera->Move(cmd); break; +/* + case kFWD: + if (ectFree==active_camera->tag) active_camera->Move(kUP); + else m_vCamDeltaHP.y += active_camera->rot_speed.y*Device.fTimeDelta; + break; + case kBACK: + if (ectFree==active_camera->tag) active_camera->Move(kDOWN); + else m_vCamDeltaHP.y -= active_camera->rot_speed.y*Device.fTimeDelta; + break; + case kL_STRAFE: + if (ectFree==active_camera->tag) active_camera->Move(kLEFT); + else m_vCamDeltaHP.x -= active_camera->rot_speed.x*Device.fTimeDelta; + break; + case kR_STRAFE: + if (ectFree==active_camera->tag) active_camera->Move(kRIGHT); + else m_vCamDeltaHP.x += active_camera->rot_speed.x*Device.fTimeDelta; + break; +*/ + } +// clamp(m_vCamDeltaHP.x, -PI_DIV_2, PI_DIV_2); +// clamp(m_vCamDeltaHP.y, active_camera->lim_pitch.x, active_camera->lim_pitch.y); +} +void CCar::Action(int id, u32 flags) +{ + if(m_car_weapon)m_car_weapon->Action(id,flags); +} +void CCar::SetParam(int id, Fvector2 val) +{ + if(m_car_weapon)m_car_weapon->SetParam(id,val); +} +void CCar::SetParam (int id, Fvector val) +{ + if(m_car_weapon)m_car_weapon->SetParam(id,val); +} +bool CCar::WpnCanHit() +{ + if(m_car_weapon) return m_car_weapon->AllowFire(); + return false; +} + +float CCar::FireDirDiff() +{ + if(m_car_weapon) return m_car_weapon->FireDirDiff(); + return 0.0f; +} +#include "script_game_object.h" +#include "car_memory.h" +#include "visual_memory_manager.h" + +bool CCar::isObjectVisible (CScriptGameObject* O_) +{ + if(m_memory) + { + return m_memory->visual().visible_now(&O_->object()); + }else + { + + if(!O_) + { + Msg("Attempt to call CCar::isObjectVisible method wihth passed NULL parameter"); + return false; + } + CObject* O = &O_->object(); + Fvector dir_to_object; + Fvector to_point; + O->Center(to_point); + + Fvector from_point; + Center (from_point); + + if(HasWeapon()) + { + from_point.y = XFORM().c.y + m_car_weapon->_height(); + } + + dir_to_object.sub(to_point,from_point).normalize_safe(); + float ray_length = from_point.distance_to(to_point); + + + BOOL res = Level().ObjectSpace.RayTest(from_point, dir_to_object, ray_length, collide::rqtStatic, NULL, NULL); + return (0==res); + } +} + +bool CCar::HasWeapon() +{ + return (m_car_weapon != NULL); +} + +Fvector CCar::CurrentVel() +{ + Fvector lin_vel; + m_pPhysicsShell->get_LinearVel(lin_vel); + + return lin_vel; +} diff --git a/src/xrGameLA/CarLights.cpp b/src/xrGameLA/CarLights.cpp new file mode 100644 index 000000000..543e0e582 --- /dev/null +++ b/src/xrGameLA/CarLights.cpp @@ -0,0 +1,200 @@ +#include "stdafx.h" +#include "CarLights.h" +#ifdef DEBUG +#include "ode_include.h" +#include "../StatGraph.h" +#include "PHDebug.h" +#endif +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "Car.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHWorld.h" +extern CPHWorld* ph_world; + +SCarLight::SCarLight() +{ + light_render =NULL; + glow_render =NULL; + bone_id =BI_NONE; + m_holder =NULL; +} + +SCarLight::~SCarLight() +{ + + light_render.destroy () ; + glow_render.destroy () ; + bone_id = BI_NONE ; +} + +void SCarLight::Init(CCarLights* holder) +{ + m_holder=holder; +} + +void SCarLight::ParseDefinitions(LPCSTR section) +{ + + light_render = ::Render->light_create(); + light_render->set_type (IRender_Light::SPOT); + light_render->set_shadow(true); + glow_render = ::Render->glow_create(); + // lanim = 0; + // time2hide = 0; + + // set bone id + IKinematics* pKinematics=smart_cast(m_holder->PCar()->Visual()); + CInifile* ini = pKinematics->LL_UserData(); + + Fcolor clr; + clr.set (ini->r_fcolor(section,"color")); + //clr.mul_rgb (torch->spot_brightness); + //fBrightness = torch->spot_brightness; + light_render->set_range (ini->r_float(section,"range")); + light_render->set_color (clr); + light_render->set_cone (deg2rad(ini->r_float(section,"cone_angle"))); + light_render->set_texture(ini->r_string(section,"spot_texture")); + + glow_render->set_texture(ini->r_string(section,"glow_texture")); + glow_render->set_color (clr); + glow_render->set_radius (ini->r_float(section,"glow_radius")); + + bone_id = pKinematics->LL_BoneID(ini->r_string(section,"bone")); + glow_render ->set_active(false); + light_render->set_active(false); + pKinematics->LL_SetBoneVisible(bone_id,FALSE,TRUE); + + //lanim = LALib.FindItem(ini->r_string(section,"animator")); + +} + +void SCarLight::Switch() +{ + VERIFY(!ph_world->Processing()); + if(isOn())TurnOff(); + else TurnOn(); +} +void SCarLight::TurnOn() +{ + VERIFY(!ph_world->Processing()); + if(isOn()) return; + IKinematics* K=smart_cast(m_holder->PCar()->Visual()); + K->LL_SetBoneVisible(bone_id,TRUE,TRUE); + K->CalculateBones_Invalidate (); + K->CalculateBones(TRUE); + glow_render ->set_active(true); + light_render->set_active(true); + Update(); + +} +void SCarLight::TurnOff() +{ + VERIFY(!ph_world->Processing()); + if(!isOn()) return; + glow_render ->set_active(false); + light_render->set_active(false); + smart_cast(m_holder->PCar()->Visual())->LL_SetBoneVisible(bone_id,FALSE,TRUE); +} + +bool SCarLight::isOn() +{ + VERIFY(!ph_world->Processing()); + VERIFY(light_render->get_active()==glow_render->get_active()); + return light_render->get_active(); +} + +void SCarLight::Update() +{ + VERIFY(!ph_world->Processing()); + if(!isOn()) return; + CCar* pcar=m_holder->PCar(); + CBoneInstance& BI = smart_cast(pcar->Visual())->LL_GetBoneInstance(bone_id); + Fmatrix M; + M.mul(pcar->XFORM(),BI.mTransform); + light_render->set_rotation (M.k,M.i); + glow_render->set_direction(M.k); + glow_render->set_position (M.c); + light_render->set_position (M.c); + +} + + +CCarLights::CCarLights() +{ + m_pcar=NULL; +} + +void CCarLights::Init(CCar* pcar) +{ + m_pcar=pcar; + m_lights.clear(); +} + +void CCarLights::ParseDefinitions() +{ + CInifile* ini= smart_cast(m_pcar->Visual())->LL_UserData(); + if(!ini->section_exist("lights")) return; + LPCSTR S= ini->r_string("lights","headlights"); + string64 S1; + int count = _GetItemCount(S); + for (int i=0 ;iInit(this); + m_lights.back()->ParseDefinitions(S1); + } + +} + +void CCarLights::Update() +{ + VERIFY(!ph_world->Processing()); + LIGHTS_I i =m_lights.begin(),e=m_lights.end(); + for(;i!=e;++i) (*i)->Update(); +} + +void CCarLights::SwitchHeadLights() +{ + + VERIFY(!ph_world->Processing()); + LIGHTS_I i =m_lights.begin(),e=m_lights.end(); + for(;i!=e;++i) (*i)->Switch(); +} + +void CCarLights::TurnOnHeadLights() +{ + + VERIFY(!ph_world->Processing()); + LIGHTS_I i =m_lights.begin(),e=m_lights.end(); + for(;i!=e;++i) (*i)->TurnOn(); +} +void CCarLights::TurnOffHeadLights() +{ + VERIFY(!ph_world->Processing()); + LIGHTS_I i =m_lights.begin(),e=m_lights.end(); + for(;i!=e;++i) (*i)->TurnOff(); +} + +bool CCarLights::IsLight(u16 bone_id) +{ + SCarLight* light=NULL; + return findLight(bone_id,light); +} +bool CCarLights::findLight(u16 bone_id,SCarLight* &light) +{ + LIGHTS_I i,e=m_lights.end(); + SCarLight find_light; + find_light.bone_id=bone_id; + i=std::find_if(m_lights.begin(),e,SFindLightPredicate(&find_light)); + light=*i; + return i!=e; +} +CCarLights::~CCarLights() +{ + LIGHTS_I i =m_lights.begin(),e=m_lights.end(); + for(;i!=e;++i) xr_delete(*i); + m_lights.clear(); +} \ No newline at end of file diff --git a/src/xrGameLA/CarLights.h b/src/xrGameLA/CarLights.h new file mode 100644 index 000000000..7c81e293c --- /dev/null +++ b/src/xrGameLA/CarLights.h @@ -0,0 +1,76 @@ +#ifndef CAR_LIGHTS_H +#define CAR_LIGHTS_H +#pragma once + +#include "../Render.h" + +class CCarLights; +class CCar; + +struct SCarLight +{ + ref_light light_render; + ref_glow glow_render; + u16 bone_id; + CCarLights* m_holder; + SCarLight (); + ~SCarLight (); + void Switch (); + void TurnOn (); + void TurnOff (); + bool isOn (); + void Init (CCarLights* holder); + void Update (); + void ParseDefinitions(LPCSTR section); + + +}; + +DEFINE_VECTOR(SCarLight*,LIGHTS_STORAGE,LIGHTS_I) +class CCarLights +{ +public: + void ParseDefinitions () ; + void Init (CCar* pcar) ; + void Update () ; + CCar* PCar () {return m_pcar;} + void SwitchHeadLights () ; + void TurnOnHeadLights () ; + void TurnOffHeadLights () ; + bool IsLight (u16 bone_id) ; + bool findLight (u16 bone_id,SCarLight* &light); + CCarLights () ; + ~CCarLights () ; +protected: + struct SFindLightPredicate { + const SCarLight *m_light; + + SFindLightPredicate (const SCarLight *light) : + m_light(light) + { + } + + bool operator() (const SCarLight *light) const + { + return light->bone_id==m_light->bone_id; + } + }; + LIGHTS_STORAGE m_lights ; + CCar* m_pcar ; +/* + Ivector2 m_head_near_lights ; + Ivector2 m_head_far_lights ; + Ivector2 m_left_turns ; + Ivector2 m_stops ; + Ivector2 m_gabarites ; + Ivector2 m_door_gabarites ; +*/ +private: +}; + + + + + + +#endif \ No newline at end of file diff --git a/src/xrGameLA/CarScript.cpp b/src/xrGameLA/CarScript.cpp new file mode 100644 index 000000000..ddb80ddab --- /dev/null +++ b/src/xrGameLA/CarScript.cpp @@ -0,0 +1,48 @@ +#include "pch_script.h" +#include "alife_space.h" +#include "Car.h" +#include "CarWeapon.h" +#include "Actor.h" +#include "script_game_object.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CCar::script_register(lua_State *L) +{ + module(L) + [ + class_ >("CCar") + .enum_("wpn_action") + [ + value("eWpnDesiredDir", int(CCarWeapon::eWpnDesiredDir)), + value("eWpnDesiredPos", int(CCarWeapon::eWpnDesiredPos)), + value("eWpnActivate", int(CCarWeapon::eWpnActivate)), + value("eWpnFire", int(CCarWeapon::eWpnFire)), + value("eWpnAutoFire", int(CCarWeapon::eWpnAutoFire)), + value("eWpnToDefaultDir", int(CCarWeapon::eWpnToDefaultDir)) + ] + .def("Action", &CCar::Action) +// .def("SetParam", (void (CCar::*)(int,Fvector2)) &CCar::SetParam) + .def("SetParam", (void (CCar::*)(int,Fvector)) &CCar::SetParam) + .def("CanHit", &CCar::WpnCanHit) + .def("FireDirDiff", &CCar::FireDirDiff) + .def("IsObjectVisible", &CCar::isObjectVisible) + .def("HasWeapon", &CCar::HasWeapon) + .def("CurrentVel", &CCar::CurrentVel) + .def("GetfHealth", &CCar::GetfHealth) + .def("SetfHealth", &CCar::SetfHealth) + .def("SetExplodeTime", &CCar::SetExplodeTime) + .def("ExplodeTime", &CCar::ExplodeTime) + .def("CarExplode", &CCar::CarExplode) + .def("RemoveDamageParticles", &CCar::RemoveDamangeParticles) + .def("ShowTrunk", &CCar::ShowTrunk) + .def("OpenTrunkBone", &CCar::OpenTrunkBone) + .def("CloseTrunkBone", &CCar::CloseTrunkBone) + .def("GetFuel", &CCar::GetFuel) + .def("Refuel", &CCar::Refuel) + .def("AddFuel", &CCar::AddFuel) + + .def(constructor<>()) + ]; +} \ No newline at end of file diff --git a/src/xrGameLA/CarSound.cpp b/src/xrGameLA/CarSound.cpp new file mode 100644 index 000000000..cddbe8a59 --- /dev/null +++ b/src/xrGameLA/CarSound.cpp @@ -0,0 +1,186 @@ +#include "stdafx.h" +#ifdef DEBUG +#include "ode_include.h" +#include "../StatGraph.h" +#include "PHDebug.h" +#endif +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "car.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHWorld.h" +extern CPHWorld* ph_world; +CCar::SCarSound::SCarSound(CCar* car) +{ + volume =1.f; + pcar=car; + relative_pos.set(0.f,0.5f,-1.f); +} + +CCar::SCarSound::~SCarSound() +{ + +} +void CCar::SCarSound::Init() +{ + CInifile* ini=smart_cast(pcar->Visual())->LL_UserData(); + if (ini->section_exist("car_sound") && ini->line_exist("car_sound","snd_volume")) + { + volume = ini->r_float("car_sound","snd_volume"); + + snd_engine.create (ini->r_string("car_sound","snd_name"),st_Effect,sg_SourceType);// + snd_engine_start.create (READ_IF_EXISTS(ini,r_string,"car_sound","engine_start","car\\test_car_start"),st_Effect,sg_SourceType); + snd_engine_stop.create (READ_IF_EXISTS(ini,r_string,"car_sound","engine_stop","car\\test_car_stop"),st_Effect,sg_SourceType); + float fengine_start_delay=READ_IF_EXISTS(ini,r_float,"car_sound","engine_sound_start_dellay",0.25f); + engine_start_delay=iFloor((snd_engine_start._handle() ? snd_engine_start._handle()->length_sec() * 1000: 1.f)*fengine_start_delay); + if(ini->line_exist("car_sound","relative_pos")) + { + relative_pos.set(ini->r_fvector3("car_sound","relative_pos")); + } + if(ini->line_exist("car_sound","transmission_switch")) + { + snd_transmission.create(ini->r_string("car_sound","transmission_switch"),st_Effect,sg_SourceType); + } + + + } else { + Msg ("! Car doesn't contain sound params"); + } + eCarSound=sndOff; +} +void CCar::SCarSound::SetSoundPosition(ref_sound &snd) +{ + VERIFY(!ph_world->Processing()); + if (snd._feedback()) + { + Fvector pos; + pcar->XFORM().transform_tiny(pos,relative_pos); + snd.set_position (pos); + } +} +void CCar::SCarSound::UpdateStarting() +{ + VERIFY(!ph_world->Processing()); + SetSoundPosition(snd_engine_start); + + if(snd_engine._feedback()) + { + UpdateDrive(); + } else + { + + if(time_state_start+engine_start_delayProcessing()); + SetSoundPosition(snd_engine_stop); + if(!snd_engine_stop._feedback())SwitchOff(); +} +void CCar::SCarSound::UpdateStalling() +{ + SetSoundPosition(snd_engine_stop); + if(!snd_engine_stop._feedback())SwitchOff(); +} +void CCar::SCarSound::UpdateDrive() +{ +VERIFY(!ph_world->Processing()); +float scale = 0.5f+0.5f*pcar->m_current_rpm/pcar->m_torque_rpm; clamp(scale,0.5f,1.25f); + snd_engine.set_frequency (scale); + SetSoundPosition(snd_engine); +} +void CCar::SCarSound::SwitchState(ESoundState new_state) +{ + eCarSound=new_state; + time_state_start=Device.dwTimeGlobal; +} +void CCar::SCarSound::Update() +{ + VERIFY(!ph_world->Processing()); + if(eCarSound==sndOff) return; + + switch (eCarSound) + { + case sndStarting :UpdateStarting () ; break; + case sndDrive :UpdateDrive () ; break; + case sndStalling :UpdateStalling () ; break; + case sndStoping :UpdateStalling () ; break; + } + + +} + +void CCar::SCarSound::SwitchOn() +{ + pcar->processing_activate(); +} +void CCar::SCarSound::Destroy() +{ + SwitchOff(); + snd_engine.destroy (); + snd_transmission.destroy(); + snd_engine_stop.destroy(); + snd_engine_start.destroy(); +} + +void CCar::SCarSound::SwitchOff() +{ + eCarSound=sndOff; + pcar->processing_deactivate(); +} + +void CCar::SCarSound::Start() +{ + VERIFY(!ph_world->Processing()); + if(eCarSound==sndOff) SwitchOn(); + SwitchState(sndStarting); + snd_engine_start.play(pcar); + SetSoundPosition(snd_engine_start); +} + +void CCar::SCarSound::Stall() +{ + VERIFY(!ph_world->Processing()); + if(eCarSound==sndOff)return; + SwitchState(sndStalling); + snd_engine.stop_deffered(); + snd_engine_stop.play(pcar); + SetSoundPosition(snd_engine_stop); +} + +void CCar::SCarSound::Stop() +{ + VERIFY(!ph_world->Processing()); + if(eCarSound==sndOff)return; + SwitchState(sndStoping); + snd_engine.stop_deffered(); + snd_engine_stop.play(pcar); + SetSoundPosition(snd_engine_stop); +} + +void CCar::SCarSound::Drive() +{ + VERIFY(!ph_world->Processing()); + if(eCarSound==sndOff) SwitchOn(); + SwitchState(sndDrive); + if(!snd_engine._feedback())snd_engine.play(pcar,sm_Looped); + SetSoundPosition(snd_engine); +} +void CCar::SCarSound::TransmissionSwitch() +{ + VERIFY(!ph_world->Processing()); + if(snd_transmission._handle()&&eCarSound!=sndOff) + { + snd_transmission.play(pcar); + SetSoundPosition(snd_transmission); + } +} + diff --git a/src/xrGameLA/CarWeapon.cpp b/src/xrGameLA/CarWeapon.cpp new file mode 100644 index 000000000..67bb011b9 --- /dev/null +++ b/src/xrGameLA/CarWeapon.cpp @@ -0,0 +1,294 @@ +#include "stdafx.h" +#include "CarWeapon.h" +#include "PhysicsShell.h" +#include "PhysicsShellHolder.h" +//#include "../Include/xrRender/Kinematics.h" +#include "../Include/xrRender/Kinematics.h" +#include "object_broker.h" +#include "ai_sounds.h" +#include "weaponAmmo.h" +#include "xr_level_controller.h" +#include "game_object_space.h" + +void CCarWeapon::BoneCallbackX (CBoneInstance *B) +{ + CCarWeapon *P = static_cast(B->callback_param()); + Fmatrix rX; rX.rotateX (P->m_cur_x_rot); + B->mTransform.mulB_43 (rX); +} + +void CCarWeapon::BoneCallbackY (CBoneInstance *B) +{ + CCarWeapon *P = static_cast(B->callback_param()); + Fmatrix rY; rY.rotateY (P->m_cur_y_rot); + B->mTransform.mulB_43 (rY); +} + +CCarWeapon::CCarWeapon(CPhysicsShellHolder* obj) +{ + m_bActive = false; + m_bAutoFire = false; + m_object = obj; + m_Ammo = new CCartridge(); + + IKinematics* K = smart_cast(m_object->Visual()); + CInifile* pUserData = K->LL_UserData(); + + m_rotate_x_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","rotate_x_bone")); + m_rotate_y_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","rotate_y_bone")); + m_fire_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","fire_bone")); + m_min_gun_speed = pUserData->r_float("mounted_weapon_definition","min_gun_speed"); + m_max_gun_speed = pUserData->r_float("mounted_weapon_definition","max_gun_speed"); + CBoneData& bdX = K->LL_GetData(m_rotate_x_bone); //VERIFY(bdX.IK_data.type==jtJoint); + m_lim_x_rot.set (bdX.IK_data.limits[0].limit.x,bdX.IK_data.limits[0].limit.y); + CBoneData& bdY = K->LL_GetData(m_rotate_y_bone); //VERIFY(bdY.IK_data.type==jtJoint); + m_lim_y_rot.set (bdY.IK_data.limits[1].limit.x,bdY.IK_data.limits[1].limit.y); + + + xr_vector matrices; + K->LL_GetBindTransform (matrices); + m_i_bind_x_xform.invert (matrices[m_rotate_x_bone]); + m_i_bind_y_xform.invert (matrices[m_rotate_y_bone]); + m_bind_x_rot = matrices[m_rotate_x_bone].k.getP(); + m_bind_y_rot = matrices[m_rotate_y_bone].k.getH(); + m_bind_x.set (matrices[m_rotate_x_bone].c); + m_bind_y.set (matrices[m_rotate_y_bone].c); + + m_cur_x_rot = m_bind_x_rot; + m_cur_y_rot = m_bind_y_rot; + m_destEnemyDir.setHP (m_bind_y_rot,m_bind_x_rot); + m_object->XFORM().transform_dir (m_destEnemyDir); + + + inheritedShooting::Light_Create (); + Load (pUserData->r_string("mounted_weapon_definition","wpn_section")); + SetBoneCallbacks (); + m_object->processing_activate (); + + m_weapon_h = matrices[m_rotate_y_bone].c.y; + m_fire_norm.set (0,1,0); + m_fire_dir.set (0,0,1); + m_fire_pos.set (0,0,0); +} + +CCarWeapon::~CCarWeapon() +{ + delete_data(m_Ammo); +//. m_object->processing_deactivate (); +} + +void CCarWeapon::Load(LPCSTR section) +{ + inheritedShooting::Load(section); + HUD_SOUND_ITEM::LoadSound(section,"snd_shoot", m_sndShot, SOUND_TYPE_WEAPON_SHOOTING); + m_Ammo->Load(pSettings->r_string(section, "ammo_class"), 0); +} + +void CCarWeapon::UpdateCL() +{ + if(!m_bActive) return; + UpdateBarrelDir (); + IKinematics* K = smart_cast(m_object->Visual()); + K->CalculateBones_Invalidate(); + K->CalculateBones (TRUE); + UpdateFire (); +} + +void CCarWeapon::UpdateFire() +{ + fTime -= Device.fTimeDelta; + + inheritedShooting::UpdateFlameParticles(); + inheritedShooting::UpdateLight(); + + if(m_bAutoFire){ + if(m_allow_fire){ + FireStart(); + }else + FireEnd(); + }; + + if(!IsWorking()){ + if(fTime<0) fTime = 0.f; + return; + } + + if(fTime<=0){ + OnShot(); + fTime += fTimeToFire; + } +} + +void CCarWeapon::Render_internal() +{ + RenderLight (); +} + +void CCarWeapon::SetBoneCallbacks() +{ +// m_object->PPhysicsShell()->EnabledCallbacks(FALSE); + + CBoneInstance& biX = smart_cast(m_object->Visual())->LL_GetBoneInstance(m_rotate_x_bone); + biX.set_callback (bctCustom,BoneCallbackX,this); + CBoneInstance& biY = smart_cast(m_object->Visual())->LL_GetBoneInstance(m_rotate_y_bone); + biY.set_callback (bctCustom,BoneCallbackY,this); +} + +void CCarWeapon::ResetBoneCallbacks() +{ + CBoneInstance& biX = smart_cast(m_object->Visual())->LL_GetBoneInstance(m_rotate_x_bone); + biX.reset_callback (); + CBoneInstance& biY = smart_cast(m_object->Visual())->LL_GetBoneInstance(m_rotate_y_bone); + biY.reset_callback (); + +// m_object->PPhysicsShell()->EnabledCallbacks(TRUE); +} + +void CCarWeapon::UpdateBarrelDir() +{ + IKinematics* K = smart_cast(m_object->Visual()); + m_fire_bone_xform = K->LL_GetTransform(m_fire_bone); + + m_fire_bone_xform.mulA_43(m_object->XFORM()); + m_fire_pos.set(0,0,0); + m_fire_bone_xform.transform_tiny(m_fire_pos); + m_fire_dir.set(0,0,1); + m_fire_bone_xform.transform_dir(m_fire_dir); + m_fire_norm.set(0,1,0); + m_fire_bone_xform.transform_dir(m_fire_norm); + + + m_allow_fire = true; + Fmatrix XFi; + XFi.invert (m_object->XFORM()); + Fvector dep; + XFi.transform_dir (dep,m_destEnemyDir); + {// x angle + m_i_bind_x_xform.transform_dir(dep); dep.normalize(); + m_tgt_x_rot = angle_normalize_signed(m_bind_x_rot-dep.getP()); + clamp (m_tgt_x_rot,-m_lim_x_rot.y,-m_lim_x_rot.x); + } + {// y angle + m_i_bind_y_xform.transform_dir(dep); dep.normalize(); + m_tgt_y_rot = angle_normalize_signed(m_bind_y_rot-dep.getH()); + clamp (m_tgt_y_rot,-m_lim_y_rot.y,-m_lim_y_rot.x); + } + + m_cur_x_rot = angle_inertion_var(m_cur_x_rot,m_tgt_x_rot,m_min_gun_speed,m_max_gun_speed,PI,Device.fTimeDelta); + m_cur_y_rot = angle_inertion_var(m_cur_y_rot,m_tgt_y_rot,m_min_gun_speed,m_max_gun_speed,PI,Device.fTimeDelta); + static float dir_eps = deg2rad(5.0f); + if( !fsimilar(m_cur_x_rot,m_tgt_x_rot,dir_eps)|| !fsimilar(m_cur_y_rot,m_tgt_y_rot,dir_eps)) + m_allow_fire=FALSE; + +#if (0) + if(Device.dwFrame%200==0){ + Msg("m_cur_x_rot=[%f]",m_cur_x_rot); + Msg("m_cur_y_rot=[%f]",m_cur_y_rot); + } +#endif +} +bool CCarWeapon::AllowFire() +{ + return m_allow_fire; +} + +float CCarWeapon::FireDirDiff() +{ + Fvector d1,d2; + d1.set(m_cur_x_rot,m_cur_y_rot,0).normalize_safe(); + d2.set(m_tgt_x_rot,m_tgt_y_rot,0).normalize_safe(); + return rad2deg( acos(d1.dotproduct(d2)) ); +} + +const Fvector& CCarWeapon::get_CurrentFirePoint() +{ + return m_fire_pos; +} + +const Fmatrix& CCarWeapon::get_ParticlesXFORM () +{ + return m_fire_bone_xform; +} + +void CCarWeapon::FireStart() +{ + inheritedShooting::FireStart(); +} + +void CCarWeapon::FireEnd() +{ + inheritedShooting::FireEnd(); + StopFlameParticles (); +} + +void CCarWeapon::OnShot() +{ + FireBullet ( m_fire_pos, m_fire_dir, fireDispersionBase, *m_Ammo, + m_object->ID(), m_object->ID(), SendHitAllowed(m_object)); + + StartShotParticles (); + + if(m_bLightShotEnabled) + Light_Start (); + + StartFlameParticles (); + StartSmokeParticles (m_fire_pos, zero_vel); +// OnShellDrop (m_fire_pos, zero_vel); + + HUD_SOUND_ITEM::PlaySound (m_sndShot, m_fire_pos, m_object, false); +} + +void CCarWeapon::Action (int id, u32 flags) +{ + switch (id){ + case eWpnFire:{ + if(flags==1) FireStart (); + else FireEnd (); + }break; + case eWpnActivate:{ + if(flags==1) m_bActive = true; + else {m_bActive = false; FireEnd();} + }break; + + case eWpnAutoFire:{ + if(flags==1) m_bAutoFire = true; + else m_bAutoFire = false; + }break; + case eWpnToDefaultDir:{ + SetParam(eWpnDesiredDir,Fvector2().set(m_bind_y_rot,m_bind_x_rot)); + }break; + + } +} + +void CCarWeapon::SetParam (int id, Fvector2 val) +{ + switch (id){ + case eWpnDesiredDir: + m_destEnemyDir.setHP (val.x,val.y); + break; + } +} + +void CCarWeapon::SetParam (int id, Fvector val) +{ + switch (id){ + case eWpnDesiredPos: + m_destEnemyDir.sub(val,m_fire_pos).normalize_safe(); + break; + } +} +const Fvector& CCarWeapon::ViewCameraPos() +{ + return m_fire_pos; +} + +const Fvector& CCarWeapon::ViewCameraDir() +{ + return m_fire_dir; +} + +const Fvector& CCarWeapon::ViewCameraNorm() +{ + return m_fire_norm; +} diff --git a/src/xrGameLA/CarWeapon.h b/src/xrGameLA/CarWeapon.h new file mode 100644 index 000000000..a9e7d54e1 --- /dev/null +++ b/src/xrGameLA/CarWeapon.h @@ -0,0 +1,69 @@ +#pragma once +#include "ShootingObject.h" +#include "HudSound.h" + +class CPhysicsShellHolder; + +class CCarWeapon :public CShootingObject +{ +protected: + typedef CShootingObject inheritedShooting; + + void SetBoneCallbacks (); + void ResetBoneCallbacks (); + virtual void FireStart (); + virtual void FireEnd (); + virtual void UpdateFire (); + virtual void OnShot (); + void UpdateBarrelDir (); + virtual const Fvector& get_CurrentFirePoint(); + virtual const Fmatrix& get_ParticlesXFORM (); + + CPhysicsShellHolder* m_object; + bool m_bActive; + bool m_bAutoFire; + float m_weapon_h; + virtual bool IsHudModeNow (){return false;}; + +public: + enum{ + eWpnDesiredDir =1, + eWpnDesiredPos, + eWpnActivate, + eWpnFire, + eWpnAutoFire, + eWpnToDefaultDir, + }; + CCarWeapon (CPhysicsShellHolder* obj); + virtual ~CCarWeapon (); + static void __stdcall BoneCallbackX (CBoneInstance *B); + static void __stdcall BoneCallbackY (CBoneInstance *B); + void Load (LPCSTR section); + void UpdateCL (); + void Action (int id, u32 flags); + void SetParam (int id, Fvector2 val); + void SetParam (int id, Fvector val); + bool AllowFire (); + float FireDirDiff (); + IC bool IsActive () {return m_bActive;} + float _height () const {return m_weapon_h;}; + const Fvector& ViewCameraPos (); + const Fvector& ViewCameraDir (); + const Fvector& ViewCameraNorm (); + + void Render_internal (); +private: + u16 m_rotate_x_bone, m_rotate_y_bone, m_fire_bone, m_camera_bone; + float m_tgt_x_rot, m_tgt_y_rot, m_cur_x_rot, m_cur_y_rot, m_bind_x_rot, m_bind_y_rot; + Fvector m_bind_x, m_bind_y; + Fvector m_fire_dir,m_fire_pos, m_fire_norm; + + Fmatrix m_i_bind_x_xform, m_i_bind_y_xform, m_fire_bone_xform; + Fvector2 m_lim_x_rot, m_lim_y_rot; //in bone space + float m_min_gun_speed, m_max_gun_speed; + CCartridge* m_Ammo; + float m_barrel_speed; + Fvector m_destEnemyDir; + bool m_allow_fire; + HUD_SOUND_ITEM m_sndShot; +}; \ No newline at end of file diff --git a/src/xrGameLA/CarWheels.cpp b/src/xrGameLA/CarWheels.cpp new file mode 100644 index 000000000..0b1c65093 --- /dev/null +++ b/src/xrGameLA/CarWheels.cpp @@ -0,0 +1,363 @@ +#include "stdafx.h" +#ifdef DEBUG +#include "ode_include.h" +#include "../StatGraph.h" +#include "PHDebug.h" +#endif +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "car.h" +#include "../Include/xrRender/Kinematics.h" +#include "ExtendedGeom.h" +#include "mathutilsode.h" + +CCar::SWheel::SWheelCollisionParams::SWheelCollisionParams() +{ + spring_factor =1 ; + damping_factor =1 ; + mu_factor =1 ; +} +IC void CCar::SWheel::applywheelCollisionParams(const dxGeomUserData *ud,bool& do_colide,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + if(ud&&ud->object_callbacks&&ud->object_callbacks->HasCallback(WheellCollisionCallback)) + { + SWheelCollisionParams &cp = *((SWheelCollisionParams*)(ud->callback_data)) ; + dSurfaceParameters &sp = c.surface ; + sp.mu *= cp.mu_factor ; + MulSprDmp(sp.soft_cfm,sp.soft_cfm,cp.spring_factor,cp.damping_factor) ; + } +} + +void CCar::SWheel::WheellCollisionCallback(bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + + dxGeomUserData *ud1 = retrieveGeomUserData(c.geom.g1) ; + dxGeomUserData *ud2 = retrieveGeomUserData(c.geom.g2) ; + applywheelCollisionParams (ud1,do_colide,c,material_1,material_2) ; + applywheelCollisionParams (ud2,do_colide,c,material_1,material_2) ; +} +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool CCar::WheelHit(float P,s16 element,ALife::EHitType hit_type) +{ + xr_map ::iterator i=m_wheels_map.find(element); + if(i!=m_wheels_map.end()) + { + i->second.Hit(P); + return true; + } + else return false; +} +void CCar::SWheel::Init() +{ + if(inited) return; + BONE_P_PAIR_CIT bone=bone_map.find(bone_id); + R_ASSERT2(bone->second.element,"No Element was created for wheel. Check collision is set"); + bone->second.element->set_DynamicLimits(default_l_limit,default_w_limit*100.f); + CPhysicsElement *e=bone->second.element ; + CPhysicsJoint *j=bone->second.joint ; + radius=e->getRadius(); + R_ASSERT2(j,"No wheel joint was set for a wheel"); + joint=j; + joint->SetBackRef(&joint); + R_ASSERT2(dJointGetType(joint->GetDJoint())==dJointTypeHinge2,"No wheel join was set for a wheel, only wheel-joint valid!!!"); + ApplyDriveAxisVelTorque(0.f,0.f); + e->add_ObjectContactCallback(WheellCollisionCallback); + e->set_CallbackData((void*)&collision_params); + e->SetAirResistance(0,0); + inited=true; +} +void CCar::SWheel::Load(LPCSTR section) +{ + IKinematics *K =PKinematics(car->Visual()) ; + CInifile *ini =K->LL_UserData() ; + VERIFY (ini) ; + if(ini->section_exist(section)) + { + collision_params.damping_factor =READ_IF_EXISTS(ini,r_float,section,"damping_factor",collision_params.damping_factor); + collision_params.spring_factor =READ_IF_EXISTS(ini,r_float,section,"spring_factor",collision_params.spring_factor); + collision_params.mu_factor =READ_IF_EXISTS(ini,r_float,section,"friction_factor",collision_params.mu_factor); + } + else if(ini->section_exist("wheels_params")) + { + collision_params.damping_factor =ini->r_float("wheels_params","damping_factor") ; + collision_params.spring_factor =ini->r_float("wheels_params","spring_factor") ; + collision_params.mu_factor =ini->r_float("wheels_params","friction_factor") ; + } + +} +void CCar::SWheel::ApplyDriveAxisTorque(float torque) +{ + if(!joint) return; + dJointSetHinge2Param(joint->GetDJoint(), dParamFMax2,torque);//car->m_axle_friction +} +void CCar::SWheel::ApplyDriveAxisVel(float vel) +{ + if(!joint) return; + dJointSetHinge2Param(joint->GetDJoint(), dParamVel2, vel); +} + + +void CCar::SWheel::ApplyDriveAxisVelTorque(float vel,float torque) +{ + ApplyDriveAxisVel(vel); + ApplyDriveAxisTorque(torque); + +} +void CCar::SWheel::ApplySteerAxisVel(float vel) +{ + if(!joint) return; + dJointSetHinge2Param(joint->GetDJoint(), dParamVel, vel); +} + +void CCar::SWheel::ApplySteerAxisTorque(float torque) +{ + if(!joint) return; + dJointSetHinge2Param(joint->GetDJoint(), dParamFMax, torque); +} + +void CCar::SWheel::ApplySteerAxisVelTorque(float vel,float torque) +{ + ApplySteerAxisVel(vel); + ApplySteerAxisTorque(torque); +} + +void CCar::SWheel::SetSteerHiLimit(float hi) +{ + if(!joint) return; + dJointSetHinge2Param(joint->GetDJoint(), dParamHiStop, hi); +} +void CCar::SWheel::SetSteerLoLimit(float lo) +{ + if(!joint) return; + dJointSetHinge2Param(joint->GetDJoint(), dParamLoStop, lo); +} +void CCar::SWheel::SetSteerLimits(float hi,float lo) +{ + SetSteerHiLimit(hi); + SetSteerLoLimit(lo); +} + +void CCar::SWheel::ApplyDamage(u16 level) +{ + inherited::ApplyDamage(level); + if(!joint) return; + if(level == 0 )return; + float sf,df; + dJointID dj=joint->GetDJoint(); + switch(level) { + + case 1: + joint->GetJointSDfactors(sf,df); + sf/=20.f;df*=4.f; + joint->SetJointSDfactors(sf,df); + car->m_damage_particles.PlayWheel1(car,bone_id); + break; + case 2: + + dVector3 v; + + dJointGetHinge2Axis2(dj,v); + v[0]+=0.1f;v[1]+=0.1f;v[2]+=0.1f; + accurate_normalize(v); + dJointSetHinge2Axis2(dj,v[0],v[1],v[2]); + joint->GetJointSDfactors(sf,df); + sf/=30.f;df*=8.f; + joint->SetJointSDfactors(sf,df); + car->m_damage_particles.PlayWheel2(car,bone_id); + break; + default: NODEFAULT; + } + +} + +void CCar::SWheel::SaveNetState(NET_Packet& P) +{ + CSE_ALifeCar::SWheelState ws; + ws.health=Health(); + ws.write(P); +} + +void CCar::SWheel::RestoreNetState(const CSE_ALifeCar::SWheelState& a_state) +{ + SetHealth(a_state.health); + RestoreEffect(); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void CCar::SWheelDrive::Init() +{ + pwheel->Init(); + gear_factor=pwheel->radius/pwheel->car->m_ref_radius; + CBoneData& bone_data= smart_cast(pwheel->car->Visual())->LL_GetData(u16(pwheel->bone_id)); + switch(bone_data.IK_data.type) + { + case jtWheel: + pos_fvd=bone_map.find(pwheel->bone_id)->second.element->mXFORM.k.x; + break; + + default: NODEFAULT; + } + + pos_fvd=pos_fvd>0.f ? -1.f : 1.f; + +} +void CCar::SWheelDrive::Drive() +{ + float cur_speed=pwheel->car->RefWheelMaxSpeed()/gear_factor; + pwheel->ApplyDriveAxisVel(pos_fvd*cur_speed); +} +void CCar::SWheelDrive::UpdatePower() +{ + pwheel->ApplyDriveAxisTorque(pwheel->car->RefWheelCurTorque()/gear_factor); +} +void CCar::SWheelDrive::Neutral() +{ + pwheel->ApplyDriveAxisVelTorque(0.f,pwheel->car->m_axle_friction); +} + +float CCar::SWheelDrive::ASpeed() +{ + CPhysicsJoint* J=pwheel->joint; + if(!J) return 0.f; + return (dJointGetHinge2Angle2Rate(J->GetDJoint()))*pos_fvd;//dFabs +} +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void CCar::SWheelSteer::Init() +{ + IKinematics* pKinematics=smart_cast(pwheel->car->Visual()); + pwheel->Init(); + (bone_map.find(pwheel->bone_id))->second.joint->GetLimits(lo_limit,hi_limit,0); + CBoneData& bone_data= pKinematics->LL_GetData(u16(pwheel->bone_id)); + switch(bone_data.IK_data.type) + { + case jtWheel: + pos_right=bone_map.find(pwheel->bone_id)->second.element->mXFORM.i.y;//.dotproduct(pwheel->car->m_root_transform.j); + break; + + default: NODEFAULT; + } + + pos_right=pos_right>0.f ? -1.f : 1.f; + float steering_torque=pKinematics->LL_UserData()->r_float("car_definition","steering_torque"); + pwheel->ApplySteerAxisTorque(steering_torque); + dJointSetHinge2Param(pwheel->joint->GetDJoint(), dParamFudgeFactor, 0.005f/steering_torque); + pwheel->ApplySteerAxisVel(0.f); + limited=false; +} + +void CCar::SWheelSteer::SteerRight() +{ + limited=true; //no need to limit wheels when steering + if(pos_right>0) + { + pwheel->SetSteerHiLimit(hi_limit); + pwheel->ApplySteerAxisVel(pwheel->car->m_steering_speed); + } + else + { + pwheel->SetSteerLoLimit(lo_limit); + pwheel->ApplySteerAxisVel( -pwheel->car->m_steering_speed); + } +} +void CCar::SWheelSteer::SteerLeft() +{ + + limited=true; //no need to limit wheels when steering + if(pos_right<0) + { + pwheel->SetSteerHiLimit(hi_limit); + pwheel->ApplySteerAxisVel(pwheel->car->m_steering_speed); + } + else + { + pwheel->SetSteerLoLimit(lo_limit); + pwheel->ApplySteerAxisVel( -pwheel->car->m_steering_speed); + } +} +void CCar::SWheelSteer::SteerIdle() +{ + limited=false; + if(pwheel->car->e_state_steer==right) + { + if(pos_right<0) + { + pwheel->SetSteerHiLimit(0.f); + pwheel->ApplySteerAxisVel( pwheel->car->m_steering_speed); + } + else + { + pwheel->SetSteerLoLimit(0.f); + pwheel->ApplySteerAxisVel(-pwheel->car->m_steering_speed); + } + } + else + { + if(pos_right>0) + { + pwheel->SetSteerHiLimit(0.f); + pwheel->ApplySteerAxisVel( pwheel->car->m_steering_speed); + } + else + { + pwheel->SetSteerLoLimit(0.f); + pwheel->ApplySteerAxisVel(-pwheel->car->m_steering_speed); + } + } + +} + +void CCar::SWheelSteer::Limit() +{ + CPhysicsJoint* J=pwheel->joint; + if(!J) return; + dJointID joint=J->GetDJoint(); + if(!limited) + { + dReal angle = dJointGetHinge2Angle1(joint); + if(dFabs(angle)SetSteerLimits(0.f,0.f); + pwheel->ApplySteerAxisVel(0.f); + limited=true; + } + } + pwheel->car->b_wheels_limited=pwheel->car->b_wheels_limited&&limited; +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void CCar::SWheelBreak::Init() +{ + pwheel->Init(); + float k=pwheel->radius/pwheel->car->m_ref_radius; + + break_torque *=k; + hand_break_torque *=k; +} +void CCar::SWheelBreak::Load(LPCSTR section) +{ + IKinematics *K =PKinematics(pwheel->car->Visual()) ; + CInifile *ini =K->LL_UserData() ; + VERIFY (ini) ; + break_torque = ini->r_float("car_definition","break_torque") ; + hand_break_torque = READ_IF_EXISTS(ini,r_float,"car_definition","hand_break_torque",break_torque) ; + if(ini->section_exist(section)) + { + break_torque =READ_IF_EXISTS(ini,r_float,section,"break_torque",break_torque); + hand_break_torque =READ_IF_EXISTS(ini,r_float,section,"hand_break_torque",hand_break_torque); + } +} +void CCar::SWheelBreak::Break(float k) +{ + pwheel->ApplyDriveAxisVelTorque(0.f,100000.f*break_torque*k); +} + +void CCar::SWheelBreak::HandBreak() +{ + pwheel->ApplyDriveAxisVelTorque(0.f,100000.f*hand_break_torque); +} + +void CCar::SWheelBreak::Neutral() +{ + pwheel->ApplyDriveAxisVelTorque(0.f,pwheel->car->m_axle_friction); +} + diff --git a/src/xrGameLA/CharacterPhysicsSupport.cpp b/src/xrGameLA/CharacterPhysicsSupport.cpp new file mode 100644 index 000000000..e425d2924 --- /dev/null +++ b/src/xrGameLA/CharacterPhysicsSupport.cpp @@ -0,0 +1,1054 @@ +#include "stdafx.h" +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "CharacterPhysicsSupport.h" +#include "PHMovementControl.h" +#include "CustomMonster.h" + +#include "../Include/xrRender/KinematicsAnimated.h" + +#include "PhysicsShell.h" +//#include "../xrRender/skeletonanimated.h" +#include "Actor.h" +#include "CustomZone.h" +#include "Extendedgeom.h" +#include "Physics.h" +#include "level.h" +#include "PHActivationShape.h" +#include "IKLimbsController.h" +#include "PHCapture.h" +#include "PHCollideValidator.h" +#include "ai/stalker/ai_stalker.h" +#include "interactive_motion.h" +#include "animation_movement_controller.h" +//const float default_hinge_friction = 5.f;//gray_wolf comment +#ifdef DEBUG +# include "PHDebug.h" +#endif // DEBUG + +#include "../device.h" + +#define USE_SMART_HITS +#define USE_IK + +void NodynamicsCollide(bool& do_colide,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/) +{ + dBodyID body1=dGeomGetBody(c.geom.g1); + dBodyID body2=dGeomGetBody(c.geom.g2); + if(!body1||!body2||(dGeomUserDataHasCallback(c.geom.g1,NodynamicsCollide)&&dGeomUserDataHasCallback(c.geom.g2,NodynamicsCollide)))return; + do_colide=false; +} + +void OnCharacterContactInDeath(bool& do_colide,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/) +{ + dSurfaceParameters &surface=c.surface; + CCharacterPhysicsSupport* l_character_physic_support=0; + if (bo1) + { + l_character_physic_support=(CCharacterPhysicsSupport*)retrieveGeomUserData(c.geom.g1)->callback_data; + } + else + { + l_character_physic_support=(CCharacterPhysicsSupport*)retrieveGeomUserData(c.geom.g2)->callback_data; + } + + surface.mu=l_character_physic_support->m_curr_skin_friction_in_death; +} + +IC bool is_imotion(interactive_motion *im) +{ + return im && im->is_enabled(); +} + +CCharacterPhysicsSupport::~CCharacterPhysicsSupport() +{ + if(m_flags.test(fl_skeleton_in_shell)) + { + if(m_physics_skeleton)m_physics_skeleton->Deactivate(); + xr_delete(m_physics_skeleton);//!b_skeleton_in_shell + } + xr_delete(m_PhysicMovementControl); + VERIFY(!m_interactive_motion); +} + +CCharacterPhysicsSupport::CCharacterPhysicsSupport(EType atype,CEntityAlive* aentity) +: m_pPhysicsShell(aentity->PPhysicsShell()), + m_EntityAlife(*aentity), + mXFORM(aentity->XFORM()), + m_ph_sound_player(aentity), + m_interactive_motion(0) +{ + m_PhysicMovementControl= new CPHMovementControl(aentity); + m_flags.assign(0); + m_eType=atype; + m_eState=esAlive; + //b_death_anim_on = false; + m_flags.set(fl_death_anim_on,FALSE); + m_pPhysicsShell = NULL; + //m_saved_impulse = 0.f; + m_physics_skeleton = NULL; + //b_skeleton_in_shell = false; + m_flags.set(fl_skeleton_in_shell,FALSE); + m_shot_up_factor =0.f; + m_after_death_velocity_factor =1.f; + m_ik_controller = NULL; + m_BonceDamageFactor =1.f; + m_collision_hit_callback = NULL; + m_Pred_Time = 0.0; + m_was_wounded = false; + m_death_impulse_vec.set(0.f, 0.f, 0.f); + m_death_impulse_cooldown_speed = 1.f; + switch(atype) + { + case etActor: + m_PhysicMovementControl->AllocateCharacterObject(CPHMovementControl::actor); + m_PhysicMovementControl->SetRestrictionType(CPHCharacter::rtActor); + break; + case etStalker: + m_PhysicMovementControl->AllocateCharacterObject(CPHMovementControl::ai); + m_PhysicMovementControl->SetRestrictionType(CPHCharacter::rtStalker); + m_PhysicMovementControl->SetActorMovable(false); + break; + case etBitting: + m_PhysicMovementControl->AllocateCharacterObject(CPHMovementControl::ai); + } +}; + +void CCharacterPhysicsSupport::SetRemoved() +{ + m_eState=esRemoved; + if(m_flags.test(fl_skeleton_in_shell))//b_skeleton_in_shell + { + if(m_pPhysicsShell->isEnabled()) + { + m_EntityAlife.processing_deactivate(); + } + if(m_pPhysicsShell)m_pPhysicsShell->Deactivate(); + xr_delete(m_pPhysicsShell); + } + else + { + if(m_physics_skeleton)m_physics_skeleton->Deactivate(); + xr_delete(m_physics_skeleton); + m_EntityAlife.processing_deactivate(); + } + +} + +void CCharacterPhysicsSupport::in_Load(LPCSTR section) +{ + + skel_airr_ang_factor = pSettings->r_float(section,"ph_skeleton_airr_ang_factor"); + skel_airr_lin_factor = pSettings->r_float(section,"ph_skeleton_airr_lin_factor"); + hinge_force_factor1 = pSettings->r_float(section,"ph_skeleton_hinger_factor1"); + skel_ddelay = pSettings->r_float(section,"ph_skeleton_ddelay"); + skel_remain_time = skel_ddelay; + skel_fatal_impulse_factor = pSettings->r_float(section,"ph_skel_fatal_impulse_factor"); + + //lost alpha start + //collision of dead bodies after death + skel_collision_enable = false; + if (pSettings->line_exist(section,"ph_skel_collision_enable")) + skel_collision_enable = !!pSettings->r_bool(section,"ph_skel_collision_enable"); + // death impulse accumulation + m_death_impulse_cooldown_speed = READ_IF_EXISTS(pSettings, r_float, section, "ph_death_impulse_cooldown_speed", 1000.f); + //lost alpha end + + //gray_wolf>Читаем из ltx параметры для поддержки изменяющегося трения у персонажей во время смерти + skeleton_skin_ddelay = pSettings->r_float(section,"ph_skeleton_skin_ddelay"); + skeleton_skin_remain_time = skeleton_skin_ddelay; + skeleton_skin_friction_start = pSettings->r_float(section,"ph_skeleton_skin_friction_start"); + skeleton_skin_friction_end = pSettings->r_float(section,"ph_skeleton_skin_friction_end"); + character_have_wounded_state = pSettings->r_bool(section,"ph_character_have_wounded_state"); + skeleton_skin_ddelay_after_wound= pSettings->r_float(section,"ph_skeleton_skin_ddelay_after_wound"); + skeleton_skin_remain_time_after_wound= skeleton_skin_ddelay_after_wound; + pelvis_factor_low_pose_detect= pSettings->r_float(section,"ph_pelvis_factor_low_pose_detect"); + + //gray_wolf< + if(pSettings->line_exist(section,"ph_skel_shot_up_factor")) m_shot_up_factor=pSettings->r_float(section,"ph_skel_shot_up_factor"); + if(pSettings->line_exist(section,"ph_after_death_velocity_factor")) m_after_death_velocity_factor=pSettings->r_float(section,"ph_after_death_velocity_factor"); + m_flags.set(fl_specific_bonce_demager,TRUE); + if(pSettings->line_exist(section,"bonce_damage_factor")) + { + + m_BonceDamageFactor=pSettings->r_float(section,"bonce_damage_factor_for_objects"); + }else + { + m_BonceDamageFactor=pSettings->r_float("collision_damage","bonce_damage_factor_for_objects"); + } + CPHDestroyable::Load(section); +} + +void CCharacterPhysicsSupport::in_NetSpawn(CSE_Abstract* e) +{ + + if(m_EntityAlife.use_simplified_visual ()) + { + m_flags.set( fl_death_anim_on, TRUE ); + IKinematics* ka = smart_cast( m_EntityAlife.Visual( ) ); + VERIFY( ka ); + ka->CalculateBones_Invalidate( ); + ka->CalculateBones( TRUE ); + CollisionCorrectObjPos( m_EntityAlife.Position( ) ); + m_pPhysicsShell = P_build_Shell( &m_EntityAlife, false ); + ka->CalculateBones_Invalidate( ); + ka->CalculateBones( TRUE ); + return; + } + + CPHDestroyable::Init();//this zerows colbacks !!; + IRenderVisual *pVisual = m_EntityAlife.Visual(); + IKinematicsAnimated*ka= smart_cast( pVisual ); + IKinematics*pK= smart_cast( pVisual ); + if(!m_EntityAlife.g_Alive()) + { + + if(m_eType==etStalker) + ka->PlayCycle("waunded_1_idle_0"); + else + ka->PlayCycle("death_init"); + + }else if( !m_EntityAlife.animation_movement_controlled( ) ) + { + + ka->PlayCycle( "death_init" );///непонятно зачем это вообще надо запускать + ///этот хак нужен, потому что некоторым монстрам + ///анимация после спона, может быть вообще не назначена + } + pK->CalculateBones_Invalidate( ); + pK->CalculateBones( TRUE ); + + CPHSkeleton::Spawn(e); + movement()->EnableCharacter(); + movement()->SetPosition(m_EntityAlife.Position()); + movement()->SetVelocity (0,0,0); + if(m_eType!=etActor) + { + m_flags.set(fl_specific_bonce_demager,TRUE); + m_BonceDamageFactor=1.f; + } + if(Type() == etStalker) + { + m_hit_animations.SetupHitMotions( *smart_cast( m_EntityAlife.Visual( ) ) ); + } + anim_mov_state.init(); + + anim_mov_state.active = m_EntityAlife.animation_movement_controlled(); +} + + +void CCharacterPhysicsSupport::CreateCharacter( ) +{ + //if( m_eType == etBitting ) + //return; + if( m_PhysicMovementControl->CharacterExist( ) )return; + CollisionCorrectObjPos( m_EntityAlife.Position( ), true ); + m_PhysicMovementControl->CreateCharacter( ); + m_PhysicMovementControl->SetPhysicsRefObject( &m_EntityAlife ); + m_PhysicMovementControl->SetPosition ( m_EntityAlife.Position( ) ); +} +void CCharacterPhysicsSupport::SpawnInitPhysics(CSE_Abstract* e) +{ + //if(!m_physics_skeleton)CreateSkeleton(m_physics_skeleton); + + if(m_EntityAlife.g_Alive()) + { +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&stricmp(PH_DBG_ObjectTrack(),*m_EntityAlife.cName())==0) + { + Msg("CCharacterPhysicsSupport::SpawnInitPhysics obj %s before collision correction %f,%f,%f",PH_DBG_ObjectTrack(),m_EntityAlife.Position().x,m_EntityAlife.Position().y,m_EntityAlife.Position().z); + } +#endif +#ifdef USE_IK + // if( etStalker == m_eType || etActor == m_eType || (m_EntityAlife.Visual()->dcast_PKinematics()->LL_UserData() && m_EntityAlife.Visual()->dcast_PKinematics()->LL_UserData()->section_exist("ik")) ) + if( etStalker == m_eType || etActor == m_eType ) + CreateIKController( ); +#endif + if( !m_EntityAlife.animation_movement_controlled( ) ) + CreateCharacter( ); +#ifdef DEBUG + if( ph_dbg_draw_mask1.test( ph_m1_DbgTrackObject ) && stricmp( PH_DBG_ObjectTrack( ), *m_EntityAlife.cName()) == 0 ) + { + Msg( "CCharacterPhysicsSupport::SpawnInitPhysics obj %s after collision correction %f,%f,%f", PH_DBG_ObjectTrack(),m_EntityAlife.Position( ).x, m_EntityAlife.Position().y, m_EntityAlife.Position().z ); + } +#endif + //m_PhysicMovementControl.SetMaterial( ) + } + else + { + ActivateShell( NULL ); + } +} +void CCharacterPhysicsSupport::in_NetDestroy( ) +{ + m_PhysicMovementControl->DestroyCharacter( ); + + if( m_physics_skeleton ) + { + m_physics_skeleton->Deactivate( ); + xr_delete( m_physics_skeleton ); + } + if(m_pPhysicsShell) + { + m_pPhysicsShell->Deactivate(); + xr_delete(m_pPhysicsShell); + } + + m_flags.set( fl_skeleton_in_shell, FALSE ); + CPHSkeleton::RespawnInit( ); + CPHDestroyable::RespawnInit( ); + m_eState = esAlive; + xr_delete( m_interactive_motion ); + DestroyIKController( ); +} + +void CCharacterPhysicsSupport::in_NetSave( NET_Packet& P ) +{ + + CPHSkeleton::SaveNetState( P ); +} + +void CCharacterPhysicsSupport::in_Init( ) +{ + + //b_death_anim_on = false; + //m_pPhysicsShell = NULL; + //m_saved_impulse = 0.f; +} + +void CCharacterPhysicsSupport::in_shedule_Update( u32 DT ) +{ + //CPHSkeleton::Update(DT); + if( !m_EntityAlife.use_simplified_visual ( ) ) + CPHDestroyable::SheduleUpdate( DT ); + else if( m_pPhysicsShell&&m_pPhysicsShell->isFullActive( ) && !m_pPhysicsShell->isEnabled( ) ) + { + m_EntityAlife.deactivate_physics_shell( ); + } + movement( )->in_shedule_Update( DT ); + +#if 0 + if( anim_mov_state.active ) + { + DBG_OpenCashedDraw( ); + DBG_DrawMatrix( mXFORM, 0.5f ); + DBG_ClosedCashedDraw( 5000 ); + } +#endif + +} + +#ifdef DEBUG + string64 sdbg_stalker_death_anim = "none"; + LPSTR dbg_stalker_death_anim = sdbg_stalker_death_anim; + BOOL b_death_anim_velocity = TRUE; +#endif +const float cmp_angle = M_PI/10.f; +const float cmp_ldisp = 0.1f; +IC bool cmp(const Fmatrix &f0, const Fmatrix &f1 ) +{ + Fmatrix if0;if0.invert(f0); + Fmatrix cm;cm.mul_43(if0,f1); + + Fvector ax;float ang; + Fquaternion q; + q.set(cm); + q.get_axis_angle(ax,ang); + + return ang < cmp_angle && cm.c.square_magnitude() < cmp_ldisp* cmp_ldisp ; +} + +bool is_similar(const Fmatrix &m0,const Fmatrix &m1,float param) +{ + Fmatrix tmp1;tmp1.invert(m0); + Fmatrix tmp2;tmp2.mul(tmp1,m1); + Fvector ax;float ang; + Fquaternion q; + q.set(tmp2); + q.get_axis_angle(ax,ang); + return _abs(ang)setup(dbg_stalker_death_anim, m_pPhysicsShell); + } + } +#endif + + if(is_imotion(m_interactive_motion)) + m_interactive_motion->play(m_pPhysicsShell); + + if (!m_was_wounded) + { + impulse*=(hit_type==ALife::eHitTypeExplosion ? 1.f : skel_fatal_impulse_factor); + } + //if(!is_imotion(m_interactive_motion)) + // m_flags.set(fl_block_hit, TRUE); +} + +void CCharacterPhysicsSupport::in_Hit(float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse,ALife::EHitType hit_type ,bool is_killing) +{ + if(m_EntityAlife.use_simplified_visual ()) return; + /*if(m_flags.test(fl_block_hit)) + { + VERIFY(!m_EntityAlife.g_Alive()); + if(Device.dwTimeGlobal-m_EntityAlife.GetLevelDeathTime()>=2000) + m_flags.set(fl_block_hit,FALSE); + else return; + }*/ + + is_killing=is_killing||(m_eState==esAlive&&!m_EntityAlife.g_Alive()); + if(m_EntityAlife.g_Alive()&&is_killing&&hit_type==ALife::eHitTypeExplosion&&P>70.f) + CPHDestroyable::Destroy(); + + if((!m_EntityAlife.g_Alive()||is_killing)&&!fis_zero(m_shot_up_factor)&&hit_type!=ALife::eHitTypeExplosion) + { + dir.y+=m_shot_up_factor; + dir.normalize(); + } + + bool isKillHit = false; + if(!m_pPhysicsShell&&is_killing) + { + KillHit(who, hit_type, impulse); + isKillHit = true; + } + + if (!m_pPhysicsShell || !m_pPhysicsShell->isActive() || isKillHit) + { + // accumulate impulse + Fvector3 newImpulse = Fvector3(dir).normalize().mul(impulse); + m_death_impulse_vec.add(newImpulse); + //Msg("Accumulate impulse %.2f (%.2f, %.2f, %.2f), result %.f (%.2f, %.2f, %.2f)", + // impulse, dir.x, dir.y, dir.z, m_death_impulse_vec.magnitude(), m_death_impulse_vec.x, m_death_impulse_vec.y, m_death_impulse_vec.z); + } + if(!(m_pPhysicsShell&&m_pPhysicsShell->isActive())) + { + if(!is_killing&&m_EntityAlife.g_Alive()) { + m_PhysicMovementControl->ApplyHit(dir,impulse,hit_type); + } + +#ifdef USE_SMART_HITS + if(Type()==etStalker) + { + m_hit_animations.PlayHitMotion(dir,p_in_object_space,element,m_EntityAlife); + } +#endif // USE_SMART_HITS + + }else { + float impulseMagnitude = isKillHit + ? m_death_impulse_vec.magnitude() + : impulse; + + Fvector3 impulseDir = isKillHit && !fis_zero(impulseMagnitude) + ? Fvector3(m_death_impulse_vec).normalize() + : dir; + + m_pPhysicsShell->applyHit(p_in_object_space,impulseDir,impulseMagnitude,element,hit_type); + //Msg("Apply hit to shell (%.2f, %.2f, %.2f) impulse %.2f, kill hit: %d", impulseDir.x, impulseDir.y, impulseDir.z, impulseMagnitude, isKillHit); + } +} + + +IC void CCharacterPhysicsSupport:: UpdateDeathAnims () +{ + VERIFY(m_pPhysicsShell->isFullActive()); + + if(!m_flags.test(fl_death_anim_on) && !is_imotion(m_interactive_motion))//!m_flags.test(fl_use_death_motion)//!b_death_anim_on&&m_pPhysicsShell->isFullActive() + { + smart_cast(m_EntityAlife.Visual())->PlayCycle("death_init"); + m_flags.set(fl_death_anim_on,TRUE); + } +} + + +void CCharacterPhysicsSupport::in_UpdateCL( ) +{ + if( m_eState==esRemoved ) + { + return; + } + CalculateTimeDelta( ); + if( m_pPhysicsShell ) + { + VERIFY( m_pPhysicsShell->isFullActive( ) ); + m_pPhysicsShell->SetRagDoll( );//Теперь шела относиться к классу объектов cbClassRagDoll + + if( !is_imotion(m_interactive_motion ) )//!m_flags.test(fl_use_death_motion) + m_pPhysicsShell->InterpolateGlobalTransform( &mXFORM ); + else + m_interactive_motion->update( m_pPhysicsShell ); + + UpdateDeathAnims(); + UpdateFrictionAndJointResistanse( ); + } else if ( !m_EntityAlife.g_Alive( ) && !m_EntityAlife.use_simplified_visual( ) ) + { + ActivateShell( NULL ); + m_PhysicMovementControl->DestroyCharacter( ); + } else if( ik_controller( ) ) + ik_controller( )->Update(); + + if (!fis_zero(m_death_impulse_vec.magnitude())) { + float reducedMagnitude = m_death_impulse_vec.magnitude() - m_death_impulse_cooldown_speed * m_time_delta; + m_death_impulse_vec.set_length(fmax(reducedMagnitude, 0.f)); + } + +#ifdef DEBUG + if(Type()==etStalker && ph_dbg_draw_mask1.test(phDbgHitAnims)) + { + Fmatrix m; + m_hit_animations.GetBaseMatrix(m,m_EntityAlife); + DBG_DrawMatrix(m,1.5f); +/* + IKinematicsAnimated *K = smart_cast(m_EntityAlife.Visual()); + u16 hb = K->LL_BoneID("bip01_head"); + u16 pb = K->LL_GetBoneRoot(); + u16 nb = K->LL_BoneID("bip01_neck"); + u16 eb = K->LL_BoneID("eye_right"); + Fmatrix &mh = K->LL_GetTransform(hb); + Fmatrix &mp = K->LL_GetTransform(pb); + Fmatrix &me = K->LL_GetTransform(eb); + Fmatrix &mn = K->LL_GetTransform(nb); + float d = DET(mh); + if(Fvector().sub(mh.c,mp.c).magnitude() < 0.3f||d<0.7 )//|| Fvector().sub(me.c,mn.c) < 0.5 + { + + K->CalculateBones_Invalidate(); + K->CalculateBones(); + ; + } +*/ + } +#endif +} + +void CCharacterPhysicsSupport::CreateSkeleton(CPhysicsShell* &pShell) +{ + + R_ASSERT2(!pShell,"pShell already initialized!!"); + if (!m_EntityAlife.Visual()) + return; +#ifdef DEBUG + CTimer t;t.Start(); +#endif + pShell = P_create_Shell(); + pShell->preBuild_FromKinematics(smart_cast(m_EntityAlife.Visual())); + pShell->mXFORM.set(mXFORM); + pShell->SetAirResistance(skel_airr_lin_factor,skel_airr_ang_factor); + pShell->SmoothElementsInertia(0.3f); + pShell->set_PhysicsRefObject(&m_EntityAlife); + SAllDDOParams disable_params; + disable_params.Load(smart_cast(m_EntityAlife.Visual())->LL_UserData()); + pShell->set_DisableParams(disable_params); + + pShell->Build(); +#ifdef DEBUG + Msg("shell for %s[%d] created in %f ms",*m_EntityAlife.cName(),m_EntityAlife.ID(),t.GetElapsed_sec()*1000.f); +#endif +} +void CCharacterPhysicsSupport::CreateSkeleton() +{ + if(m_pPhysicsShell) return; + Fvector velocity; + m_PhysicMovementControl->GetCharacterVelocity(velocity); + m_PhysicMovementControl->GetDeathPosition (m_EntityAlife.Position()); + m_PhysicMovementControl->DestroyCharacter(); + if (!m_EntityAlife.Visual()) + return; + m_pPhysicsShell = P_create_Shell(); + m_pPhysicsShell->build_FromKinematics(smart_cast(m_EntityAlife.Visual())); + m_pPhysicsShell->mXFORM.set(mXFORM); + m_pPhysicsShell->SetAirResistance(skel_airr_lin_factor, + skel_airr_ang_factor); + m_pPhysicsShell->set_PhysicsRefObject(&m_EntityAlife); + SAllDDOParams disable_params; + disable_params.Load(smart_cast(m_EntityAlife.Visual())->LL_UserData()); + m_pPhysicsShell->set_DisableParams(disable_params); + m_pPhysicsShell->set_JointResistance(0.f); + m_pPhysicsShell->Activate(true); + velocity.mul(1.25f*m_after_death_velocity_factor); + m_pPhysicsShell->set_LinearVel(velocity); + smart_cast(m_EntityAlife.Visual())->CalculateBones(); + //b_death_anim_on=false; + m_flags.set(fl_death_anim_on,FALSE); + m_eState=esDead; +} +bool CCharacterPhysicsSupport::DoCharacterShellCollide() +{ + if(m_eType==etStalker) + { + CAI_Stalker* OBJ=smart_cast(&m_EntityAlife); + VERIFY (OBJ); + return !OBJ->wounded(); + } + return true; +} +void CCharacterPhysicsSupport::CollisionCorrectObjPos(const Fvector& start_from,bool character_create/*=false*/) +{ + Fvector shift;shift.sub(start_from,m_EntityAlife.Position()); + + Fbox box; + if(character_create)box.set(movement()->Box()); + else box.set(m_EntityAlife.BoundingBox()); + Fvector vbox;Fvector activation_pos; + box.get_CD(activation_pos,vbox);shift.add(activation_pos);vbox.mul(2.f); + activation_pos.add(shift,m_EntityAlife.Position()); + CPHActivationShape activation_shape; + activation_shape.Create(activation_pos,vbox,&m_EntityAlife); + if(!DoCharacterShellCollide()&&!character_create) + { + CPHCollideValidator::SetCharacterClassNotCollide(activation_shape); + } + activation_shape.Activate(vbox,1,1.f,M_PI/8.f); + m_EntityAlife.Position().sub(activation_shape.Position(),shift); + activation_shape.Destroy(); +} + +void CCharacterPhysicsSupport::set_movement_position( const Fvector &pos ) +{ + VERIFY( movement() ); + + CollisionCorrectObjPos( pos, true ); + + movement()->SetPosition( m_EntityAlife.Position() ); +} + +extern Flags32 psActorFlags; +void CCharacterPhysicsSupport::ActivateShell ( CObject* who ) +{ + DestroyIKController( ); + IKinematics* K=smart_cast( m_EntityAlife.Visual( ) ); + + //animation movement controller issues + bool anim_mov_ctrl =m_EntityAlife.animation_movement_controlled( ); + CBoneInstance &BR = K->LL_GetBoneInstance( K->LL_GetBoneRoot( ) ); + Fmatrix start_xform; start_xform.identity( ); + CBlend *anim_mov_blend = 0; + //float blend_time = 0; + if( anim_mov_ctrl ) + { + m_EntityAlife.animation_movement( )->ObjStartXform( start_xform ); + anim_mov_blend = m_EntityAlife.animation_movement( )->ControlBlend( ); + /* + VERIFY( anim_mov_blend->blend != CBlend::eFREE_SLOT ); + anim_mov_blend->timeCurrent -= 2 * Device.fTimeDelta * anim_mov_blend->speed; + blend_time = anim_mov_blend->timeCurrent; + anim_mov_blend->playing = true; + + K->CalculateBones_Invalidate( ); + K->CalculateBones (); + anim_mov_blend->playing = false; + */ + m_EntityAlife.destroy_anim_mov_ctrl( ); + BR.set_callback_overwrite(TRUE); + } + // + + if( !m_physics_skeleton ) + CreateSkeleton( m_physics_skeleton ); + + if( m_eType == etActor ) + { + CActor* A=smart_cast( &m_EntityAlife ); + R_ASSERT2( A, "not an actor has actor type" ); + if( A->Holder( ) ) return; + if( m_eState==esRemoved )return; + } + +//////////////////////this needs to evaluate object box////////////////////////////////////////////////////// + for( u16 I = K->LL_BoneCount( )-1; I!=u16(-1); --I ) + K->LL_GetBoneInstance( I ).reset_callback( ); + + if( anim_mov_ctrl ) //we do not whant to move by long animation in root + BR.set_callback_overwrite(TRUE); + + K->CalculateBones_Invalidate(); + K->CalculateBones (); +//////////////////////////////////////////////////////////////////////////// + if( m_pPhysicsShell ) return; + Fvector velocity; + m_PhysicMovementControl->GetCharacterVelocity ( velocity ); + velocity.mul( 1.3f ); + Fvector dp, start;start.set( m_EntityAlife.Position( ) ); + if( !m_PhysicMovementControl->CharacterExist( ) ) + dp.set( m_EntityAlife.Position( ) ); + else m_PhysicMovementControl->GetDeathPosition( dp ); + m_PhysicMovementControl->DestroyCharacter( ); + + CollisionCorrectObjPos( dp ); + + //shell create + R_ASSERT2(m_physics_skeleton,"No skeleton created!!"); + m_pPhysicsShell=m_physics_skeleton; + m_physics_skeleton=NULL; + m_pPhysicsShell->set_Kinematics(K); + m_pPhysicsShell->RunSimulation(); + m_pPhysicsShell->mXFORM.set(mXFORM); + m_pPhysicsShell->SetCallbacks(m_pPhysicsShell->GetBonesCallback()); + // + + if(anim_mov_ctrl) //we do not whant to move by long animation in root + BR.set_callback_overwrite(TRUE); + + //set shell params + if(!smart_cast(who)) + { + velocity.mul(1.25f*m_after_death_velocity_factor); + } + if(!DoCharacterShellCollide()) + { + m_pPhysicsShell->DisableCharacterCollision(); + } + m_pPhysicsShell->set_LinearVel(velocity); + K->CalculateBones_Invalidate(); + K->CalculateBones (); + m_flags.set(fl_death_anim_on,FALSE); + m_eState=esDead; + m_flags.set(fl_skeleton_in_shell,TRUE); + + if(IsGameTypeSingle()) + { + if (!psActorFlags.test(AF_COLLISION) || !skel_collision_enable) + { + m_pPhysicsShell->SetPrefereExactIntegration ();//use exact integration for ragdolls in single + m_pPhysicsShell->SetRemoveCharacterCollLADisable(); + } + } + else + { + m_pPhysicsShell->SetIgnoreDynamic(); + } + m_pPhysicsShell->SetIgnoreSmall(); + //end seting params + + + + //fly back after correction + FlyTo(Fvector().sub(start,m_EntityAlife.Position())); + // + + //actualize + m_pPhysicsShell->GetGlobalTransformDynamic(&mXFORM); + // + + m_pPhysicsShell->add_ObjectContactCallback(OnCharacterContactInDeath); + m_pPhysicsShell->set_CallbackData((void*)this); +// + + if(anim_mov_ctrl && anim_mov_blend && anim_mov_blend->blend_state() != CBlend::eFREE_SLOT && anim_mov_blend->timeCurrent + Device.fTimeDelta*anim_mov_blend->speed < anim_mov_blend->timeTotal-SAMPLE_SPF-EPS)//. + { + const Fmatrix sv_xform = mXFORM; + mXFORM.set( start_xform ); + //anim_mov_blend->blendPower = 1; + anim_mov_blend->timeCurrent += Device.fTimeDelta * anim_mov_blend->speed; + m_pPhysicsShell->AnimToVelocityState( Device.fTimeDelta, 2 * default_l_limit, 10.f * default_w_limit ); + mXFORM.set( sv_xform ); + } + +} +void CCharacterPhysicsSupport::in_ChangeVisual() +{ + if(m_ik_controller) + { + DestroyIKController(); + CreateIKController(); + } + + IKinematicsAnimated* KA = smart_cast( m_EntityAlife.Visual( ) ); + if( KA ) + { + if(Type()==etStalker) + m_hit_animations.SetupHitMotions( *KA ); + } + + if(!m_physics_skeleton&&!m_pPhysicsShell) return; + + if(m_pPhysicsShell) + { + VERIFY(m_eType!=etStalker); + if(m_physics_skeleton) + { + m_EntityAlife.processing_deactivate() ; + m_physics_skeleton->Deactivate() ; + xr_delete(m_physics_skeleton) ; + } + CreateSkeleton(m_physics_skeleton); + if(m_pPhysicsShell)m_pPhysicsShell->Deactivate(); + xr_delete(m_pPhysicsShell); + ActivateShell(NULL); + } +} + +bool CCharacterPhysicsSupport::CanRemoveObject() +{ + if(m_eType==etActor) + { + return false; + } + else + { + return !m_EntityAlife.IsPlaying(); + } +} + +void CCharacterPhysicsSupport::PHGetLinearVell(Fvector &velocity) +{ + if(m_pPhysicsShell&&m_pPhysicsShell->isActive()) + { + m_pPhysicsShell->get_LinearVel(velocity); + } + else + movement()->GetCharacterVelocity(velocity); + +} + +extern BOOL useInverseKinematics_; + +void CCharacterPhysicsSupport::CreateIKController() +{ + if (!useInverseKinematics_) + return; + + VERIFY(!m_ik_controller); + m_ik_controller= new CIKLimbsController(); + m_ik_controller->Create(&m_EntityAlife); + +} +void CCharacterPhysicsSupport::DestroyIKController() +{ + if(!m_ik_controller)return; + m_ik_controller->Destroy(&m_EntityAlife); + xr_delete(m_ik_controller); +} + +void CCharacterPhysicsSupport::in_NetRelcase(CObject* O) +{ + CPHCapture* c=m_PhysicMovementControl->PHCapture(); + if(c) + { + c->net_Relcase(O); + } +} + +bool CCharacterPhysicsSupport::set_collision_hit_callback(SCollisionHitCallback* cc) +{ + if(!cc) + { + m_collision_hit_callback=NULL; + return true; + } + if(m_pPhysicsShell) + { + VERIFY2(cc->m_collision_hit_callback!=0,"No callback function"); + m_collision_hit_callback=cc; + return true; + }else return false; +} +SCollisionHitCallback * CCharacterPhysicsSupport::get_collision_hit_callback() +{ + return m_collision_hit_callback; +} + +void StaticEnvironmentCB (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + dJointID contact_joint = dJointCreateContact(0, ContactGroup, &c); + + if(bo1) + { + ((CPHIsland*)(retrieveGeomUserData(c.geom.g1)->callback_data))->DActiveIsland()->ConnectJoint(contact_joint); + dJointAttach (contact_joint, dGeomGetBody(c.geom.g1), 0); + } + else + { + ((CPHIsland*)(retrieveGeomUserData(c.geom.g2)->callback_data))->DActiveIsland()->ConnectJoint(contact_joint); + dJointAttach (contact_joint, 0, dGeomGetBody(c.geom.g2)); + } + do_colide=false; +} + +void CCharacterPhysicsSupport::FlyTo(const Fvector &disp) +{ + VERIFY(m_pPhysicsShell); + float ammount=disp.magnitude(); + if(fis_zero(ammount,EPS_L)) return; + ph_world->Freeze(); + bool g=m_pPhysicsShell->get_ApplyByGravity(); + m_pPhysicsShell->set_ApplyByGravity(false); + m_pPhysicsShell->add_ObjectContactCallback(StaticEnvironmentCB); + void* cd=m_pPhysicsShell->get_CallbackData(); + m_pPhysicsShell->set_CallbackData(m_pPhysicsShell->PIsland()); + m_pPhysicsShell->UnFreeze(); + Fvector vel;vel.set(disp); + const u16 steps_num=10; + const float fsteps_num=steps_num; + vel.mul(1.f/fsteps_num/fixed_step); + + + for(u16 i=0;steps_num>i;++i) + { + m_pPhysicsShell->set_LinearVel(vel); + ph_world->Step(); + } + //u16 step_num=disp.magnitude()/fixed_step; + m_pPhysicsShell->set_ApplyByGravity(g); + m_pPhysicsShell->set_CallbackData(cd); + m_pPhysicsShell->remove_ObjectContactCallback(StaticEnvironmentCB); + ph_world->UnFreeze(); +} + +void CCharacterPhysicsSupport::TestForWounded() +{ + m_was_wounded=false; + if (!character_have_wounded_state) + { + return; + } + + IKinematics* CKA=smart_cast(m_EntityAlife.Visual()); + CKA->CalculateBones(); + CBoneInstance CBI=CKA->LL_GetBoneInstance(0); + Fmatrix position_matrix; + position_matrix.mul(mXFORM,CBI.mTransform); + + xrXRC xrc; + xrc.ray_options (0); + xrc.ray_query(Level().ObjectSpace.GetStaticModel(),position_matrix.c,Fvector().set(0.0f,-1.0f,0.0f),pelvis_factor_low_pose_detect); + + if (xrc.r_count()) + { + m_was_wounded=true; + } +}; + +void CCharacterPhysicsSupport::UpdateFrictionAndJointResistanse() +{ + //Преобразование skel_ddelay из кадров в секунды и линейное нарастание сопротивления в джоинтах со временем от момента смерти + + if(skel_remain_time!=0) + { + skel_remain_time-=m_time_delta; + }; + if (skel_remain_time<0) + { + skel_remain_time=0; + }; + + float curr_joint_resistance=hinge_force_factor1- + (skel_remain_time*hinge_force_factor1)/skel_ddelay; + m_pPhysicsShell->set_JointResistance(curr_joint_resistance); + + + + + if(skeleton_skin_remain_time!=0) + { + skeleton_skin_remain_time-=m_time_delta; + } + if (skeleton_skin_remain_time<0) + { + skeleton_skin_remain_time=0; + } + + if(skeleton_skin_remain_time_after_wound!=0) + { + skeleton_skin_remain_time_after_wound-=m_time_delta; + }; + if (skeleton_skin_remain_time_after_wound<0) + { + skeleton_skin_remain_time_after_wound=0; + }; + + float ddelay,remain; + if (m_was_wounded) + { + ddelay=skeleton_skin_ddelay_after_wound; + remain=skeleton_skin_remain_time_after_wound; + } + else + { + ddelay=skeleton_skin_ddelay; + remain=skeleton_skin_remain_time; + } + + m_curr_skin_friction_in_death=skeleton_skin_friction_end+ + (remain/ddelay)*(skeleton_skin_friction_start-skeleton_skin_friction_end); + + +}; + +void CCharacterPhysicsSupport::CalculateTimeDelta() +{ + if (m_Pred_Time==0.0) + { + m_time_delta=0; + } + else + { + m_time_delta=Device.fTimeGlobal-m_Pred_Time; + } + m_Pred_Time=Device.fTimeGlobal; +}; + +void CCharacterPhysicsSupport::on_create_anim_mov_ctrl () +{ + VERIFY( !anim_mov_state.active ); + anim_mov_state.character_exist = m_PhysicMovementControl->CharacterExist(); + if(anim_mov_state.character_exist) + m_PhysicMovementControl->DestroyCharacter(); + anim_mov_state.active = true; +} + +void CCharacterPhysicsSupport::on_destroy_anim_mov_ctrl () +{ + VERIFY( anim_mov_state.active ); + if( anim_mov_state.character_exist ) + CreateCharacter(); + anim_mov_state.active = false; +} + +void CCharacterPhysicsSupport::ForceTransform( const Fmatrix &m ) +{ + if( !m_EntityAlife.g_Alive() ) + return; + VERIFY(_valid(m)); + m_EntityAlife.XFORM().set( m ); + if( movement()->CharacterExist() ) + movement()->EnableCharacter(); + set_movement_position( m.c ); + movement()->SetVelocity( 0, 0, 0 ); + +} \ No newline at end of file diff --git a/src/xrGameLA/CharacterPhysicsSupport.h b/src/xrGameLA/CharacterPhysicsSupport.h new file mode 100644 index 000000000..cda150f01 --- /dev/null +++ b/src/xrGameLA/CharacterPhysicsSupport.h @@ -0,0 +1,175 @@ + +#ifndef CHARACTER_PHYSICS_SUPPORT +#define CHARACTER_PHYSICS_SUPPORT + +#include "alife_space.h" +#include "PHSkeleton.h" +#include "Entity_Alive.h" +#include "PHSoundPlayer.h" +#include "Phdestroyable.h" +#include "character_hit_animations.h" + + +class CPhysicsShell; +class CPHMovementControl; +class CIKLimbsController; +class interactive_motion; + + + +class CCharacterPhysicsSupport : +public CPHSkeleton, +public CPHDestroyable +{ +public: +enum EType +{ + etActor, + etStalker, + etBitting +}; + +enum EState +{ + esDead, + esAlive, + esRemoved +}; + +private: + EType m_eType; + EState m_eState; + Flags8 m_flags; + enum Fags + { + fl_death_anim_on =1<<0, + fl_skeleton_in_shell =1<<1, + fl_specific_bonce_demager =1<<2, + fl_block_hit =1<<3, + }; + + struct animation_movement_state{ + bool active; + bool character_exist; + void init( ){ active = false ; character_exist = false ; } + animation_movement_state( ) { init( ); } + } anim_mov_state; + + CEntityAlive &m_EntityAlife ; + Fmatrix &mXFORM ; + CPhysicsShell *&m_pPhysicsShell ; + CPhysicsShell *m_physics_skeleton ; + CPHMovementControl *m_PhysicMovementControl ; + CPHSoundPlayer m_ph_sound_player ; + CIKLimbsController *m_ik_controller ; + SCollisionHitCallback *m_collision_hit_callback; + character_hit_animation_controller m_hit_animations; + + interactive_motion *m_interactive_motion; +//skeleton modell(!share?) + float skel_airr_lin_factor ; + float skel_airr_ang_factor ; + float hinge_force_factor1 ; + float skel_fatal_impulse_factor ; + float skel_ddelay ; + float skel_remain_time ; +///////////////////////////////////////////////// + //bool b_death_anim_on ; + //bool b_skeleton_in_shell ; +/////////////////////////////////////////////////////////////////////////// + float m_shot_up_factor ; + float m_after_death_velocity_factor ; + float m_BonceDamageFactor ; + //gray_wolf>Переменные для поддержки изменяющегося трения у персонажей во время смерти + float skeleton_skin_ddelay; + float skeleton_skin_remain_time; + float skeleton_skin_friction_start; + float skeleton_skin_friction_end; + float skeleton_skin_ddelay_after_wound; + float skeleton_skin_remain_time_after_wound; + bool m_was_wounded; + bool skel_collision_enable; + float m_Pred_Time;//Для вычисления дельта времени между пересчётами сопротивления в джоинтах и коэффициента NPC + float m_time_delta; + float pelvis_factor_low_pose_detect; + BOOL character_have_wounded_state; + float m_death_impulse_cooldown_speed; + Fvector3 m_death_impulse_vec; + //gray_wolf< +public: + //gray_wolf> + float m_curr_skin_friction_in_death; + //gray_wolf< +EType Type() + { + return m_eType; + } +EState STate() + { + return m_eState; + } +void SetState(EState astate) + { + m_eState=astate; + } +IC bool isDead() + { + return m_eState==esDead; + } +IC bool isAlive() + { + return !m_pPhysicsShell; + } +protected: +virtual void SpawnInitPhysics (CSE_Abstract *D) ; +virtual CPhysicsShellHolder* PPhysicsShellHolder () {return m_EntityAlife.PhysicsShellHolder();} + +virtual bool CanRemoveObject (); +public: +IC CPHMovementControl *movement () {return m_PhysicMovementControl;} +IC CPHSoundPlayer *ph_sound_player () {return &m_ph_sound_player;} +IC CIKLimbsController *ik_controller () {return m_ik_controller;} + void SetRemoved (); + bool IsRemoved (){return m_eState==esRemoved;} + bool IsSpecificDamager () {return !!m_flags.test(fl_specific_bonce_demager) ;} + float BonceDamageFactor (){return m_BonceDamageFactor;} + void set_movement_position ( const Fvector &pos ); + void ForceTransform ( const Fmatrix &m); +//////////////////base hierarchi methods/////////////////////////////////////////////////// + void CreateCharacter (); + void in_UpdateCL() ; + void in_shedule_Update ( u32 DT ) ; + void in_NetSpawn (CSE_Abstract* e) ; + void in_NetDestroy () ; + void in_NetRelcase (CObject* O) ; + void in_Init () ; + void in_Load (LPCSTR section) ; + void in_Hit (float P,Fvector &dir, CObject *who, s16 element,Fvector p_in_object_space, float impulse,ALife::EHitType hit_type ,bool is_killing=false); + void in_NetSave (NET_Packet& P) ; + void in_ChangeVisual (); + void on_create_anim_mov_ctrl (); + void on_destroy_anim_mov_ctrl (); + void PHGetLinearVell (Fvector& velocity); + SCollisionHitCallback* get_collision_hit_callback (); + bool set_collision_hit_callback (SCollisionHitCallback* cc); +///////////////////////////////////////////////////////////////// + CCharacterPhysicsSupport& operator = (CCharacterPhysicsSupport& /**asup/**/){R_ASSERT2(false,"Can not assign it");} + CCharacterPhysicsSupport (EType atype,CEntityAlive* aentity) ; +virtual ~CCharacterPhysicsSupport () ; +private: + void CreateSkeleton (CPhysicsShell* &pShell) ; + void CreateSkeleton (); + void ActivateShell (CObject* who) ; + void KillHit (CObject* who, ALife::EHitType hit_type, float &impulse) ; +static void DeathAnimCallback (CBlend *B) ; + void CreateIKController () ; + void DestroyIKController () ; + void CollisionCorrectObjPos (const Fvector& start_from,bool character_create=false); + void FlyTo (const Fvector &disp); + void TestForWounded (); +IC void UpdateFrictionAndJointResistanse(); +IC void UpdateDeathAnims (); +IC void CalculateTimeDelta (); +IC bool DoCharacterShellCollide (); +}; +#endif //CHARACTER_PHYSICS_SUPPORT \ No newline at end of file diff --git a/src/xrGameLA/ClimableObject.cpp b/src/xrGameLA/ClimableObject.cpp new file mode 100644 index 000000000..29128ae92 --- /dev/null +++ b/src/xrGameLA/ClimableObject.cpp @@ -0,0 +1,379 @@ +#include "stdafx.h" +#include "climableobject.h " +#include "PHStaticGeomShell.h" +#include "xrServer_Objects_ALife.h" +#include "PHCharacter.h" +#include "MathUtils.h" + +#ifdef DEBUG +# include "debug_renderer.h" +# include "level.h" +# include "PHDebug.h" +#endif + +#ifdef DEBUG +# include "debug_renderer.h" +#endif + +static const float down_leader_extension_tolerance=0.2f; +static const float up_leader_extension_tolerance=0.0f; + +IC void OrientToNorm(const Fvector& normal,Fmatrix& form,Fobb& box) +{ + + Fvector * ax_pointer= (Fvector*)&form; + float * s_pointer = (float*) &(box.m_halfsize); + float max_dot=abs(ax_pointer[0].dotproduct(normal)); + float min_size=box.m_halfsize.x; + int max_ax_i=0,min_size_i=0; + for(int i=1;3>i;++i) + { + float dot_pr=abs(ax_pointer[i].dotproduct(normal)); + if(max_dots_pointer[i]) + { + min_size_i=i;min_size=s_pointer[i]; + } + } + VERIFY(min_size_i==max_ax_i); + if(ax_pointer[max_ax_i].dotproduct(normal)<0.f) + { + ax_pointer[max_ax_i].invert(); + ax_pointer[(max_ax_i+1)%3].invert(); + } +} + +class CPHLeaderGeomShell: public CPHStaticGeomShell +{ +CClimableObject *m_pClimable; +public: + CPHLeaderGeomShell (CClimableObject* climable); +void near_callback (CPHObject* obj); +}; + +CPHLeaderGeomShell::CPHLeaderGeomShell(CClimableObject* climable) +{ + m_pClimable=climable; +} +void CPHLeaderGeomShell::near_callback (CPHObject* obj) +{ + if(obj && obj->CastType()==CPHObject::tpCharacter) + { + CPHCharacter* ch=static_cast(obj); + ch->SetElevator(m_pClimable); + } +} + + + CClimableObject::CClimableObject () +{ + m_pStaticShell=NULL; +} + CClimableObject::~CClimableObject () +{ + +} +void CClimableObject:: Load ( LPCSTR section) +{ + inherited::Load(section); +} +BOOL CClimableObject:: net_Spawn ( CSE_Abstract* DC) +{ + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeObjectClimable *CLB=smart_cast(e); + const Fmatrix& b=CLB->shapes[0].data.box; + m_box.m_halfsize.set(b._11,b._22,b._33); + m_radius=_max(_max(m_box.m_halfsize.x,m_box.m_halfsize.y),m_box.m_halfsize.z); + + //m_box.m_halfsize.set(1.f,1.f,1.f); + BOOL ret = inherited::net_Spawn(DC); + const float f_min_width=0.2f; + Fvector shift;shift.set(0.f,0.f,0.f); + SORT(b._11,m_axis.set(XFORM().i);m_axis.mul(m_box.m_halfsize.x),m_side.set(XFORM().i);m_side.mul(m_box.m_halfsize.x),m_norm.set(XFORM().i);if(m_box.m_halfsize.x(m_pStaticShell),smart_cast(this),0,m_box); + m_pStaticShell->SetMaterial("materials\\fake_ladders"); + + if(m_axis.y<0.f) + { + m_axis.invert(); + m_side.invert(); + + } +// shedule_unregister(); + processing_deactivate(); + m_pStaticShell->set_ObjectContactCallback(ObjectContactCallback); + return ret; +} +void CClimableObject:: net_Destroy () +{ + inherited::net_Destroy(); + m_pStaticShell->Deactivate(); + xr_delete(m_pStaticShell); +} +void CClimableObject:: shedule_Update ( u32 dt) // Called by shedule +{ + inherited::shedule_Update(dt); +} +void CClimableObject:: UpdateCL ( ) // Called each frame, so no need for d +{ + inherited::UpdateCL(); +} + +void CClimableObject::Center (Fvector &C) const +{ + C.set(XFORM().c); +} +float CClimableObject::Radius () const +{ + return m_radius; +} + +float CClimableObject:: DDLowerP (CPHCharacter *actor,Fvector &out_dir)const +{ + VERIFY(actor); + Fvector pos; + LowerPoint(out_dir); + actor->GetFootCenter(pos); + out_dir.sub(pos); + return to_mag_and_dir(out_dir); +} +float CClimableObject:: DDUpperP (CPHCharacter *actor,Fvector &out_dir)const +{ + VERIFY(actor); + Fvector pos; + UpperPoint(out_dir); + actor->GetFootCenter(pos); + out_dir.sub(pos); + return to_mag_and_dir(out_dir); +} + + +void CClimableObject:: DefineClimbState (CPHCharacter *actor)const +{ + + +} +float CClimableObject::DDAxis(Fvector& dir)const +{ + dir.set(m_axis); + return to_mag_and_dir(dir); +} + +float CClimableObject::DDSide(Fvector& dir)const +{ + dir.set(m_side); + return to_mag_and_dir(dir); +} +float CClimableObject::DDNorm(Fvector &dir)const +{ + dir.set(m_norm); + return to_mag_and_dir(dir); +} +float CClimableObject:: DDToAxis (CPHCharacter *actor,Fvector &out_dir)const +{ + VERIFY(actor); + DToAxis(actor,out_dir); + return to_mag_and_dir(out_dir); +} + +void CClimableObject:: POnAxis (CPHCharacter *actor,Fvector &P)const +{ + VERIFY(actor); + actor->GetFootCenter(P); + prg_pos_on_axis(Position(),m_axis,P); +} +void CClimableObject:: LowerPoint (Fvector &P)const +{ + P.sub(XFORM().c,m_axis); + P.add(m_norm); +} + +void CClimableObject:: UpperPoint (Fvector &P)const +{ + P.add(XFORM().c,m_axis); + P.add(m_norm); +} + +void CClimableObject::DToAxis(CPHCharacter *actor,Fvector &dir)const +{ + VERIFY(actor); + POnAxis(actor,dir); + Fvector pos;actor->GetFootCenter(pos); + dir.sub(pos); +} +void CClimableObject::DSideToAxis (CPHCharacter *actor,Fvector &dir)const +{ + VERIFY(actor); + DToAxis(actor,dir); + Fvector side;side.set(m_side); + to_mag_and_dir(side); + side.mul(side.dotproduct(dir)); + dir.set(side); +} + +float CClimableObject::DDSideToAxis(CPHCharacter *actor,Fvector &dir)const +{ + VERIFY(actor); + DToAxis(actor,dir); + Fvector side;side.set(m_side);to_mag_and_dir(side); + float dot=side.dotproduct(dir); + if(dot>0.f) + { + dir.set(side); + return dot; + } + else + { + dir.set(side); + dir.invert(); + return -dot; + } + +} + +void CClimableObject::DToPlain(CPHCharacter *actor,Fvector &dist)const +{ + VERIFY(actor); + DToAxis(actor,dist); + Fvector norm;norm.set(m_norm); + to_mag_and_dir(norm); + float dot=norm.dotproduct(dist); + norm.mul(dot); + dist.set(norm); +} + +float CClimableObject::DDToPlain(CPHCharacter *actor,Fvector &dir)const +{ + VERIFY(actor); + DToPlain(actor,dir); + return to_mag_and_dir(dir); +} + +bool CClimableObject::InTouch(CPHCharacter *actor)const +{ + VERIFY(actor); + Fvector dir; + const float normal_tolerance=0.05f; + float foot_radius=actor->FootRadius(); + return (DDToPlain(actor,dir)GetFootCenter(v1); + UpperPoint(v2); + v2.sub(v1); + v1.set(m_axis);to_mag_and_dir(v1); + return v1.dotproduct(v2); +} + +float CClimableObject::AxDistToLowerP(CPHCharacter *actor)const +{ + VERIFY(actor); + Fvector v1,v2; + actor->GetFootCenter(v1); + LowerPoint(v2); + v2.sub(v1); + v1.set(m_axis);to_mag_and_dir(v1); + return -v1.dotproduct(v2); +} +bool CClimableObject::InRange(CPHCharacter *actor)const +{ + VERIFY(actor); + return AxDistToLowerP(actor)>-down_leader_extension_tolerance && AxDistToUpperP(actor)+actor->FootRadius()>-up_leader_extension_tolerance; +} + + +bool CClimableObject::BeforeLadder(CPHCharacter *actor,float tolerance/*=0.f*/)const +{ + VERIFY(actor); + Fvector d; + DToAxis(actor,d); + Fvector n;n.set(Norm()); + float width=to_mag_and_dir(n); + return d.dotproduct(n)<-(width+actor->FootRadius()/2.f+tolerance); +} + +BOOL CClimableObject::UsedAI_Locations() +{ + return FALSE; +} + + +void CClimableObject::ObjectContactCallback(bool& do_colide,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/) +{ + dxGeomUserData* usr_data_1= retrieveGeomUserData(c.geom.g1); + dxGeomUserData* usr_data_2=retrieveGeomUserData(c.geom.g2); + dxGeomUserData* usr_data_ch=NULL; + dxGeomUserData* usr_data_lad=NULL; + CClimableObject* this_object=NULL; + CPHCharacter* ch=NULL; + float norm_sign=0.f; + if(bo1) { + usr_data_ch=usr_data_2; + usr_data_lad=usr_data_1; + norm_sign=-1.f; + } + else { + norm_sign=1.f; + usr_data_ch=usr_data_1; + usr_data_lad=usr_data_2; + } + + if(usr_data_ch&&usr_data_ch->ph_object&&usr_data_ch->ph_object->CastType()==CPHObject::tpCharacter) + ch=static_cast(usr_data_ch->ph_object); + else + { + do_colide=false; + return; + } + VERIFY(ch); + VERIFY(usr_data_lad); + this_object=static_cast(usr_data_lad->ph_ref_object); + VERIFY(this_object); + if(!this_object->BeforeLadder(ch,-0.1f)) do_colide=false; + +} +#ifdef DEBUG +extern Flags32 dbg_net_Draw_Flags; +void CClimableObject ::OnRender() +{ + if (!dbg_net_Draw_Flags.test(1<<10)&&!ph_dbg_draw_mask.test(phDbgLadder)) return; + + Fmatrix form;m_box.xform_get(form); + //form.mulA(XFORM()); + Level().debug_renderer().draw_obb(XFORM(),m_box.m_halfsize,D3DCOLOR_XRGB(0,0,255)); + Fvector p1,p2,d; + d.set(m_axis); + p1.add(XFORM().c,d); + p2.sub(XFORM().c,d); + Level().debug_renderer().draw_line(Fidentity,p1,p2,D3DCOLOR_XRGB(255,0,0)); + + d.set(m_side); + p1.add(XFORM().c,d); + p2.sub(XFORM().c,d); + Level().debug_renderer().draw_line(Fidentity,p1,p2,D3DCOLOR_XRGB(255,0,0)); + + d.set(m_norm); + d.mul(10.f); + p1.add(XFORM().c,d); + p2.set(XFORM().c); + Level().debug_renderer().draw_line(Fidentity,p1,p2,D3DCOLOR_XRGB(0,255,0)); +} +#endif \ No newline at end of file diff --git a/src/xrGameLA/ClimableObject.h b/src/xrGameLA/ClimableObject.h new file mode 100644 index 000000000..fb5021622 --- /dev/null +++ b/src/xrGameLA/ClimableObject.h @@ -0,0 +1,69 @@ +#pragma once +#include "physicsshellholder.h" +class CPHLeaderGeomShell; +class CPHCharacter; +struct dContact; +struct SGameMtl; +class CClimableObject: public CPhysicsShellHolder +#ifdef DEBUG +,public pureRender +#endif +{ + typedef CPhysicsShellHolder inherited; + CPHLeaderGeomShell* m_pStaticShell; + Fobb m_box; + Fvector m_axis; + Fvector m_side; + Fvector m_norm; + float m_radius; +public: + CClimableObject (); + ~CClimableObject (); + virtual void Load ( LPCSTR section); + virtual BOOL net_Spawn ( CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void shedule_Update ( u32 dt); // Called by sheduler + virtual void UpdateCL ( ); // Called each frame, so no need for dt + virtual void Center (Fvector &C) const; + virtual float Radius () const; +#ifdef DEBUG + virtual void OnRender (); +#endif +protected: + virtual BOOL UsedAI_Locations (); +public: + const Fvector& Axis ()const {return m_axis;} + float DDAxis (Fvector& dir)const; + + const Fvector& Side ()const {return m_side;} + float DDSide (Fvector& dir)const; + + const Fvector& Norm ()const{return m_norm;} + float DDNorm (Fvector &dir)const; + bool BeforeLadder (CPHCharacter *actor,float tolerance=0.f)const; + float DDLowerP (CPHCharacter *actor,Fvector &out_dir)const;//returns distance and dir to lover point + float DDUpperP (CPHCharacter *actor,Fvector &out_dir)const;//returns distance and dir to upper point + + void DToAxis (CPHCharacter *actor,Fvector &dir)const; + float DDToAxis (CPHCharacter *actor,Fvector &out_dir)const;//returns distance and dir to ladder axis + void POnAxis (CPHCharacter *actor,Fvector &P)const; + + float AxDistToUpperP (CPHCharacter *actor)const; + float AxDistToLowerP (CPHCharacter *actor)const; + + void DSideToAxis (CPHCharacter *actor,Fvector &dir)const; + float DDSideToAxis (CPHCharacter *actor,Fvector &dir)const; + + void DToPlain (CPHCharacter *actor,Fvector &dist)const; + float DDToPlain (CPHCharacter *actor,Fvector &dir)const; + bool InRange (CPHCharacter *actor)const; + bool InTouch (CPHCharacter *actor)const; + + void LowerPoint (Fvector &P)const; + void UpperPoint (Fvector &P)const; + void DefineClimbState (CPHCharacter *actor)const; + static void ObjectContactCallback(bool& /**do_colide/**/,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/); + +public: + virtual bool register_schedule () const {return false;} +}; diff --git a/src/xrGameLA/ContextMenu.cpp b/src/xrGameLA/ContextMenu.cpp new file mode 100644 index 000000000..57804741c --- /dev/null +++ b/src/xrGameLA/ContextMenu.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "contextmenu.h" +#include "../gamefont.h" + +const float fade_speed = 8.0f; + +CContextMenu::~CContextMenu(){ + for (xr_vector::iterator I=Items.begin(); Items.end()!=I; ++I){ + Engine.Event.Destroy(I->Event); + xr_free(I->Name); + xr_free(I->Param); + } + xr_free(Name); +} +void CContextMenu::Load(CInifile* INI, LPCSTR SECT){ + CInifile::Sect& S = INI->r_section(SECT); + for (CInifile::SectCIt I=S.Data.begin(); S.Data.end()!=I; ++I){ + char Event[128],Param[128]; + Event[0]=0; Param[0]=0; + sscanf (*I->second,"%[^,],%s",Event,Param); + MenuItem Item; + Item.Name = xr_strdup(*I->first); + Item.Event = Engine.Event.Create(Event); + Item.Param = xr_strdup(Param); + Items.push_back(Item); + } +} +void CContextMenu::Render(CGameFont* F, u32 cT, u32 cI, float s) +{ + F->SetHeightI (0.05f); + F->SetColor (cT); + F->OutNext ("%s",Name); + F->SetColor (cI); + F->SetHeightI (0.03f); + + for (u32 i=0; iOutNext("%d. %s", i, (char*)Items[i].Name); +} +void CContextMenu::Select(int I) +{ + if (I>=0 && I<(int)(Items.size())){ + MenuItem& M = Items[I]; + Engine.Event.Signal(M.Event, u64(M.Param)); + } +} diff --git a/src/xrGameLA/ContextMenu.h b/src/xrGameLA/ContextMenu.h new file mode 100644 index 000000000..51853cdc7 --- /dev/null +++ b/src/xrGameLA/ContextMenu.h @@ -0,0 +1,26 @@ +#ifndef __XR_CONTEXTMENU_H__ +#define __XR_CONTEXTMENU_H__ +#pragma once + +// refs +class CInifile; +class CFontBase; + +class CContextMenu +{ +public: + struct MenuItem + { + char* Name; + EVENT Event; + char* Param; + }; + char* Name; + xr_vector Items; +public: + ~CContextMenu (); + void Load (CInifile* INI, LPCSTR S); + void Render (CGameFont* F, u32 cT, u32 cI, float s); + void Select (int num); +}; +#endif // __XR_CONTEXTMENU_H__ diff --git a/src/xrGameLA/CustomDetector.cpp b/src/xrGameLA/CustomDetector.cpp new file mode 100644 index 000000000..1492447e9 --- /dev/null +++ b/src/xrGameLA/CustomDetector.cpp @@ -0,0 +1,325 @@ +#include "stdafx.h" +#include "customdetector.h" +#include "customzone.h" +#include "hudmanager.h" +#include "artifact.h" +#include "inventory.h" +#include "level.h" +#include "map_manager.h" +#include "ActorEffector.h" +#include "actor.h" +#include "ai_sounds.h" +#include "clsid_game.h" +#include "game_base_space.h" + +ZONE_INFO::ZONE_INFO () +{ + pParticle=NULL; +} + +ZONE_INFO::~ZONE_INFO () +{ + if(pParticle) + CParticlesObject::Destroy(pParticle); +} + +CCustomDetector::CCustomDetector(void) +{ + m_bWorking = false; +} + +CCustomDetector::~CCustomDetector(void) +{ + ZONE_TYPE_MAP_IT it; + for(it = m_ZoneTypeMap.begin(); m_ZoneTypeMap.end() != it; ++it) + HUD_SOUND_ITEM::DestroySound(it->second.detect_snds); +// it->second.detect_snd.destroy(); + + m_ZoneInfoMap.clear(); +} + +BOOL CCustomDetector::net_Spawn(CSE_Abstract* DC) +{ + m_pCurrentActor = NULL; + m_pCurrentInvOwner = NULL; + + return (inherited::net_Spawn(DC)); +} + +void CCustomDetector::Load(LPCSTR section) +{ + inherited::Load (section); + + m_fRadius = pSettings->r_float(section,"radius"); + + if( pSettings->line_exist(section,"night_vision_particle") ) + m_nightvision_particle = pSettings->r_string(section,"night_vision_particle"); + + u32 i = 1; + string256 temp; + + //загрузить звуки для обозначения различных типов зон + do + { + xr_sprintf (temp, "zone_class_%d", i); + if(pSettings->line_exist(section,temp)) + { + LPCSTR z_Class = pSettings->r_string(section,temp); + CLASS_ID zone_cls = TEXT2CLSID(pSettings->r_string(z_Class,"class")); + + m_ZoneTypeMap.insert (std::make_pair(zone_cls,ZONE_TYPE())); + ZONE_TYPE& zone_type = m_ZoneTypeMap[zone_cls]; + xr_sprintf (temp, "zone_min_freq_%d", i); + zone_type.min_freq = pSettings->r_float(section,temp); + xr_sprintf (temp, "zone_max_freq_%d", i); + zone_type.max_freq = pSettings->r_float(section,temp); + R_ASSERT (zone_type.min_freqline_exist(section,temp) ) + zone_type.zone_map_location = pSettings->r_string(section,temp); + + ++i; + } + else break; + } while(true); + + m_ef_detector_type = pSettings->r_u32(section,"ef_detector_type"); +} + + +void CCustomDetector::shedule_Update(u32 dt) +{ + inherited::shedule_Update (dt); + + if( !IsWorking() ) return; + if( !H_Parent() ) return; + + Position().set(H_Parent()->Position()); + + if (H_Parent() && H_Parent() == Level().CurrentViewEntity()) + { + Fvector P; + P.set (H_Parent()->Position()); + feel_touch_update (P,m_fRadius); + UpdateNightVisionMode(); + } +} + +void CCustomDetector::StopAllSounds() +{ + ZONE_TYPE_MAP_IT it; + for(it = m_ZoneTypeMap.begin(); m_ZoneTypeMap.end() != it; ++it) + { + ZONE_TYPE& zone_type = (*it).second; + HUD_SOUND_ITEM::StopSound(zone_type.detect_snds); +// zone_type.detect_snd.stop(); + } +} + +void CCustomDetector::UpdateCL() +{ + inherited::UpdateCL(); + + if( !IsWorking() ) return; + if( !H_Parent() ) return; + + if(!m_pCurrentActor) return; + + ZONE_INFO_MAP_IT it; + for(it = m_ZoneInfoMap.begin(); m_ZoneInfoMap.end() != it; ++it) + { + CCustomZone *pZone = it->first; + ZONE_INFO& zone_info = it->second; + + + //такой тип зон не обнаруживается + if(m_ZoneTypeMap.find(pZone->CLS_ID) == m_ZoneTypeMap.end() || + !pZone->VisibleByDetector()) + continue; + + ZONE_TYPE& zone_type = m_ZoneTypeMap[pZone->CLS_ID]; + + float dist_to_zone = H_Parent()->Position().distance_to(pZone->Position()) - 0.8f*pZone->Radius(); + if(dist_to_zone<0) dist_to_zone = 0; + + float fRelPow = 1.f - dist_to_zone / m_fRadius; + clamp(fRelPow, 0.f, 1.f); + + //определить текущую частоту срабатывания сигнала + zone_info.cur_freq = zone_type.min_freq + + (zone_type.max_freq - zone_type.min_freq) * fRelPow* fRelPow* fRelPow* fRelPow; + + float current_snd_time = 1000.f*1.f/zone_info.cur_freq; + + if((float)zone_info.snd_time > current_snd_time) + { + zone_info.snd_time = 0; + HUD_SOUND_ITEM::PlaySound (zone_type.detect_snds, Fvector().set(0,0,0), this, true, false); + + } + else + zone_info.snd_time += Device.dwTimeDelta; + } +} + +void CCustomDetector::feel_touch_new(CObject* O) +{ + CCustomZone *pZone = smart_cast(O); + if(pZone && pZone->IsEnabled()) + { + m_ZoneInfoMap[pZone].snd_time = 0; + + AddRemoveMapSpot(pZone,true); + } +} + +void CCustomDetector::feel_touch_delete(CObject* O) +{ + CCustomZone *pZone = smart_cast(O); + if(pZone) + { + m_ZoneInfoMap.erase(pZone); + AddRemoveMapSpot(pZone,false); + } +} + +BOOL CCustomDetector::feel_touch_contact(CObject* O) +{ + return (NULL != smart_cast(O)); +} + +void CCustomDetector::OnH_A_Chield() +{ + m_pCurrentActor = smart_cast(H_Parent()); + m_pCurrentInvOwner = smart_cast(H_Parent()); + inherited::OnH_A_Chield (); +} + +void CCustomDetector::OnH_B_Independent(bool just_before_destroy) +{ + inherited::OnH_B_Independent(just_before_destroy); + + m_pCurrentActor = NULL; + m_pCurrentInvOwner = NULL; + + StopAllSounds (); + + m_ZoneInfoMap.clear (); + Feel::Touch::feel_touch.clear(); +} + + +u32 CCustomDetector::ef_detector_type () const +{ + return (m_ef_detector_type); +} + +void CCustomDetector::OnMoveToRuck() +{ + inherited::OnMoveToRuck(); + TurnOff(); +} + +void CCustomDetector::OnMoveToSlot() +{ + inherited::OnMoveToSlot (); + TurnOn (); +} + +void CCustomDetector::OnMoveToBelt () +{ + inherited::OnMoveToBelt (); + TurnOn (); +} + +void CCustomDetector::TurnOn() +{ + m_bWorking = true; + UpdateMapLocations (); + UpdateNightVisionMode (); +} + +void CCustomDetector::TurnOff() +{ + m_bWorking = false; + UpdateMapLocations (); + UpdateNightVisionMode (); +} + +void CCustomDetector::AddRemoveMapSpot(CCustomZone* pZone, bool bAdd) +{ + if(m_ZoneTypeMap.find(pZone->CLS_ID) == m_ZoneTypeMap.end() )return; + + if ( bAdd && !pZone->VisibleByDetector() ) return; + + + ZONE_TYPE& zone_type = m_ZoneTypeMap[pZone->CLS_ID]; + if( xr_strlen(zone_type.zone_map_location) ){ + if( bAdd ) + Level().MapManager().AddMapLocation(*zone_type.zone_map_location,pZone->ID()); + else + Level().MapManager().RemoveMapLocation(*zone_type.zone_map_location,pZone->ID()); + } +} + +void CCustomDetector::UpdateMapLocations() // called on turn on/off only +{ + ZONE_INFO_MAP_IT it; + for(it = m_ZoneInfoMap.begin(); it != m_ZoneInfoMap.end(); ++it) + AddRemoveMapSpot(it->first,IsWorking()); +} + +void CCustomDetector::UpdateNightVisionMode() +{ +// CObject* tmp = Level().CurrentViewEntity(); + bool bNightVision = false; + if (GameID() == GAME_SINGLE) + { + bNightVision = Actor()->Cameras().GetPPEffector(EEffectorPPType(effNightvision))!=NULL; + } + else + { + if (Level().CurrentViewEntity() && + Level().CurrentViewEntity()->CLS_ID == CLSID_OBJECT_ACTOR) + { + CActor* pActor = smart_cast(Level().CurrentViewEntity()); + if (pActor) + bNightVision = pActor->Cameras().GetPPEffector(EEffectorPPType(effNightvision))!=NULL; + } + } + + bool bOn = bNightVision && + m_pCurrentActor && + m_pCurrentActor==Level().CurrentViewEntity()&& + IsWorking() && + m_nightvision_particle.size(); + + ZONE_INFO_MAP_IT it; + for(it = m_ZoneInfoMap.begin(); m_ZoneInfoMap.end() != it; ++it) + { + CCustomZone *pZone = it->first; + ZONE_INFO& zone_info = it->second; + + if(bOn){ + Fvector zero_vector; + zero_vector.set(0.f,0.f,0.f); + + if(!zone_info.pParticle) + zone_info.pParticle = CParticlesObject::Create(*m_nightvision_particle,FALSE); + + zone_info.pParticle->UpdateParent(pZone->XFORM(),zero_vector); + if(!zone_info.pParticle->IsPlaying()) + zone_info.pParticle->Play(false); + }else{ + if(zone_info.pParticle){ + zone_info.pParticle->Stop (); + CParticlesObject::Destroy(zone_info.pParticle); + } + } + } +} diff --git a/src/xrGameLA/CustomDetector.h b/src/xrGameLA/CustomDetector.h new file mode 100644 index 000000000..866104309 --- /dev/null +++ b/src/xrGameLA/CustomDetector.h @@ -0,0 +1,94 @@ +#pragma once +#include "inventory_item_object.h" +#include "../feel_touch.h" +#include "hudsound.h" + +class CCustomZone; +//описание типа зоны +struct ZONE_TYPE +{ + //интервал частот отыгрывания звука + float min_freq; + float max_freq; + //звук реакции детектора на конкретную зону +// ref_sound detect_snd; + HUD_SOUND_ITEM detect_snds; + + shared_str zone_map_location; +}; + +//описание зоны, обнаруженной детектором +struct ZONE_INFO +{ + u32 snd_time; + //текущая частота работы датчика + float cur_freq; + //particle for night-vision mode + CParticlesObject* pParticle; + + ZONE_INFO (); + ~ZONE_INFO (); +}; + +class CInventoryOwner; + +class CCustomDetector : + public CInventoryItemObject, + public Feel::Touch +{ + typedef CInventoryItemObject inherited; +public: + CCustomDetector(void); + virtual ~CCustomDetector(void); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void Load (LPCSTR section); + + virtual void OnH_A_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + + virtual void shedule_Update (u32 dt); + virtual void UpdateCL (); + + virtual void feel_touch_new (CObject* O); + virtual void feel_touch_delete (CObject* O); + virtual BOOL feel_touch_contact (CObject* O); + + void TurnOn (); + void TurnOff (); + bool IsWorking () {return m_bWorking;} + + virtual void OnMoveToSlot (); + virtual void OnMoveToRuck (); + virtual void OnMoveToBelt (); + +protected: + void StopAllSounds (); + void UpdateMapLocations (); + void AddRemoveMapSpot (CCustomZone* pZone, bool bAdd); + void UpdateNightVisionMode (); + + bool m_bWorking; + + float m_fRadius; + + //если хозяин текущий актер + CActor* m_pCurrentActor; + CInventoryOwner* m_pCurrentInvOwner; + + //информация об онаруживаемых зонах + DEFINE_MAP(CLASS_ID, ZONE_TYPE, ZONE_TYPE_MAP, ZONE_TYPE_MAP_IT); + ZONE_TYPE_MAP m_ZoneTypeMap; + + //список обнаруженных зон и информация о них + DEFINE_MAP(CCustomZone*, ZONE_INFO, ZONE_INFO_MAP, ZONE_INFO_MAP_IT); + ZONE_INFO_MAP m_ZoneInfoMap; + + shared_str m_nightvision_particle; + +protected: + u32 m_ef_detector_type; + +public: + virtual u32 ef_detector_type () const; +}; diff --git a/src/xrGameLA/CustomDetector2.cpp b/src/xrGameLA/CustomDetector2.cpp new file mode 100644 index 000000000..6b83ed2ad --- /dev/null +++ b/src/xrGameLA/CustomDetector2.cpp @@ -0,0 +1,403 @@ +#include "stdafx.h" +#include "customdetector2.h" +#include "ui/ArtefactDetectorUI.h" + +#include "inventory.h" + +#include "actor.h" + +#include "player_hud.h" +#include "weapon.h" + +CCustomDetectorR::CCustomDetectorR() +{ + m_ui = NULL; + m_bFastAnimMode = false; + m_bNeedActivation = false; + + freqq = 0.f; + cur_periodperiod = 0.f; + snd_timetime = 0.f; +} + +void CCustomDetectorR::ToggleDetector(bool bFastMode) +{ + if (GetState() == eHidden) + { + ShowDetector(bFastMode); + } + else + { + HideDetector(bFastMode); + } +} + +void CCustomDetectorR::OnStateSwitch(u32 S) +{ + inherited::OnStateSwitch(S); + + switch(S) + { + case eShowing: + { + TurnDetectorInternal(true); + m_pCurrentInventory->SetCurrentDetector(this); + g_player_hud->attach_item (this); + m_sounds.PlaySound ("sndShow", Fvector().set(0,0,0), this, true, false); + PlayHUDMotion ("anim_show", FALSE, this, GetState()); + SetPending (TRUE); + }break; + case eHiding: + { + m_sounds.PlaySound ("sndHide", Fvector().set(0,0,0), this, true, false); + PlayHUDMotion (m_bFastAnimMode?"anim_hide_fast":"anim_hide", TRUE, this, GetState()); + SetPending (TRUE); + }break; + case eHidden: + { + m_pCurrentInventory->SetCurrentDetector(NULL); + g_player_hud->detach_item (this); + StopCurrentAnimWithoutCallback (); + TurnDetectorInternal (false); + }break; + case eIdle: + { + PlayAnimIdle (); + SetPending (FALSE); + }break; + } +} + +void CCustomDetectorR::OnAnimationEnd(u32 state) +{ + inherited::OnAnimationEnd (state); + switch(state) + { + case eShowing: + { + SwitchState (eIdle); + } break; + case eHiding: + { + SwitchState (eHidden); + } break; + } +} + +void CCustomDetectorR::UpdateXForm() +{ + CInventoryItem::UpdateXForm(); +} + +void CCustomDetectorR::OnActiveItem() +{ + ShowDetector(false); + inherited::OnActiveItem(); +} + +void CCustomDetectorR::OnHiddenItem() +{ + HideDetector(false); + inherited::OnHiddenItem(); +} + +CCustomDetectorR::~CCustomDetectorR() +{ + //m_artefacts.destroy (); + TurnDetectorInternal (false); + xr_delete (m_ui); + if (detect_sndsnd_line)detect_snd.destroy(); +} + +BOOL CCustomDetectorR::net_Spawn(CSE_Abstract* DC) +{ + TurnDetectorInternal(false); + return (inherited::net_Spawn(DC)); +} + +void CCustomDetectorR::Load(LPCSTR section) +{ + + inherited::Load(section); + + fdetect_radius = pSettings->r_float(section, "detect_radius");//Считываем базовый радиус действия + foverallrangetocheck = READ_IF_EXISTS(pSettings, r_float, section, "overall_range_to_check", 50.0f); + fortestrangetocheck = READ_IF_EXISTS(pSettings, r_float, section, "test_range_to_check", 250.0f); + for_test = !!READ_IF_EXISTS(pSettings, r_bool, section, "for_test", FALSE); + reaction_sound_off = !!READ_IF_EXISTS(pSettings, r_bool, section, "reaction_sound_off", FALSE); + + // РџРѕРґРіСЂСѓР·РёРј СЃРїРёСЃРѕРє всех артов, СЃРїРёСЃРѕРє прописан РІ линии af_to_detect_list. РџРѕРєР° будет так, так как РїРѕ РґСЂСѓРіРѕРјСѓ сделать РЅРµ хватает опыта + xr_vector af_total_list; + + LPCSTR S = READ_IF_EXISTS(pSettings, r_string, section, "af_to_detect_list", "null"); + + if (S && S[0]) + { + string128 artefact; + int count = _GetItemCount(S); + for (int it = 0; it < count; ++it) + { + _GetItem(S, it, artefact); + af_total_list.push_back(artefact); + } + } + + + // Отсеим только те арты, которые построчно прописаны РІ конфиге детектора + af_types.clear(); + + for (int it = 0; it < af_total_list.size(); ++it) + { + u32 af_line_true_false; + af_line_true_false = READ_IF_EXISTS(pSettings, r_u32, section, af_total_list[it].c_str(), 0); + if (af_line_true_false == 1 || (xr_strcmp(af_total_list[it].c_str(), "all") == 0)){ + af_types.push_back(af_total_list[it]); + } + } + + //Для проверки + //for (int it = 0; it < af_types.size(); ++it) + //{ + //Msg("af_types%i == %s, it was marked true", it, af_types[it].c_str()); + //} + + + m_sounds.LoadSound(section, "snd_draw", "sndShow"); + m_sounds.LoadSound(section, "snd_holster", "sndHide"); + + //Этот Р·РІСѓРє берется РїРѕ дефолту если РЅРµ задана строка "sounds" + detect_sndsnd_line = READ_IF_EXISTS(pSettings, r_string, section, "af_sound_if_no_artefact_sound", NULL); + if (detect_sndsnd_line){ + detect_snd.create(pSettings->r_string(section, "af_sound_if_no_artefact_sound"), st_Effect, SOUND_TYPE_ITEM); + } + + //Добавил врзможность задать разные Р·РІСѓРєРё для различных артов + af_sounds_section = READ_IF_EXISTS(pSettings, r_string, section, "sounds", "null"); + + detector_section = section; + +} + + +void CCustomDetectorR::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + + if( !IsWorking() ) return; + + Position().set(H_Parent()->Position()); + + Fvector P; + P.set (H_Parent()->Position()); +} + + +bool CCustomDetectorR::IsWorking() +{ + return m_bWorking && H_Parent() && H_Parent()==Level().CurrentViewEntity(); +} + +void CCustomDetectorR::UpdateCL() +{ + inherited::UpdateCL(); + + if(H_Parent()!=Level().CurrentEntity() ) return; + + UpdateVisibility (); + if( !IsWorking() ) return; + UpdateWork (); +} + +void CCustomDetectorR::OnH_A_Chield() +{ + inherited::OnH_A_Chield (); +} + +void CCustomDetectorR::OnH_B_Independent(bool just_before_destroy) +{ + inherited::OnH_B_Independent(just_before_destroy); +} + +void CCustomDetectorR::TurnDetectorInternal(bool b) +{ + m_bWorking = b; + if(b && m_ui==NULL) + { + CreateUI (); + }else + { + xr_delete (m_ui); + } +} +///------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + +void CCustomDetectorR::HideDetector(bool bFastMode, bool needToReactivate) +{ + // Msg("Hide Detector, fast:%d, needActivate: %d", bFastMode, needToReactivate); + m_bFastAnimMode = bFastMode; + m_bNeedActivation = needToReactivate; + + if (IsHiding()) return; + + SendDeactivateItem(); +} + +void CCustomDetectorR::ShowDetector(bool bFastMode) +{ + // Msg("Show Detector, fast:%d", bFastMode); + m_bFastAnimMode = bFastMode; + m_bNeedActivation = false; + + if (!IsHidden()) return; + + PIItem iitem = m_pCurrentInventory->ActiveItem(); + CHudItem* itm = (iitem) ? iitem->cast_hud_item() : NULL; + + if (iitem == nullptr) + { + // no weapon, take out detector and bolt together + SwitchToBolt(); + } + else if (iitem->BaseSlot() != DETECTOR_SLOT && !iitem->IsSingleHand()) + { + // Two-handed weapon, switch to bolt and activate detector later + SwitchToBolt(); + m_bNeedActivation = true; + return; + } + else if (itm->IsHiding() || !IsItemStateCompatible(itm)) + { + // Weapon needs both hands right now, can't show + return; + } + + SwitchState(eShowing); +} + +void CCustomDetectorR::SwitchToBolt() +{ + auto bolt = m_pCurrentInventory->ItemFromSlot(BOLT_SLOT); + // Msg("No bolt selected or weapon is not single hand, %s.", bolt != nullptr ? "Activate bolt" : "Hide weapon"); + m_pCurrentInventory->Activate(bolt != nullptr ? BOLT_SLOT : NO_ACTIVE_SLOT); +} + +bool CCustomDetectorR::IsItemStateCompatible(CHudItem* itm) +{ + if (!itm) return true; + + auto W = smart_cast(itm); + if (!W) return true; + return W->GetState() != CWeapon::eReload + && W->GetState() != CWeapon::eSwitch + && !W->IsZoomed(); +} + +void CCustomDetectorR::HideDetectorInstantly(bool needToReactivate) +{ + // Msg("Hide Detector Instantly, needActivate:%d", needToReactivate); + SwitchState(eHidden); + m_bNeedActivation = needToReactivate; +} + +void CCustomDetectorR::UpdateWork() +{ + UpdateAf (); + m_ui->update (); +} + +// TODO: don't run this check repeatedly, use event-driven approach instead (check detector at all relevant events) +void CCustomDetectorR::UpdateVisibility()//РџРѕРґРіРѕРЅРєР° нахождения детектора РІ левой СЂСѓРєРµ РїРѕРґ различные события С…СѓРґР° +{ + attachable_hud_item* i0 = g_player_hud->attached_item(0); + if (i0 != nullptr + && HudItemData() + && m_pCurrentInventory->CurrentDetector() != NULL + && !IsItemStateCompatible(i0->m_parent_hud_item)) + { + HideDetectorInstantly(true); + } + else if (m_bNeedActivation + && m_pCurrentInventory->m_slots[DETECTOR_SLOT].CanBeActivated()) + { + auto hudItem = i0 != nullptr ? i0->m_parent_hud_item : nullptr; + if (hudItem == nullptr || (!hudItem->IsHidden() && !hudItem->IsHiding() && IsItemStateCompatible(hudItem))) + { + ShowDetector(false); + } + } +} + + +bool CCustomDetectorR::install_upgrade_impl(LPCSTR section, bool test) +{ + bool result = inherited::install_upgrade_impl(section, test); + + //Msg("Detecting radius before %f", fdetect_radius); + result |= process_if_exists(section, "detect_radius", &CInifile::r_float, fdetect_radius, test); + //Msg("Detecting radius after %f", fdetect_radius); + result |= process_if_exists(section, "overall_range_to_check", &CInifile::r_float, foverallrangetocheck, test); + + result |= process_if_exists(section, "test_range_to_check", &CInifile::r_float, fortestrangetocheck, test); + result |= process_if_exists_set(section, "for_test", &CInifile::r_bool, for_test, test); + + result |= process_if_exists_set(section, "reaction_sound_off", &CInifile::r_bool, reaction_sound_off, test); + result |= process_if_exists_set(section, "sounds", &CInifile::r_string, af_sounds_section, test); + + LPCSTR str; + bool result2 = process_if_exists_set(section, "af_sound_if_no_artefact_sound", &CInifile::r_string, str, test); + if (result2 && !test) + { + detect_snd.create(pSettings->r_string(section, "af_sound_if_no_artefact_sound"), st_Effect, SOUND_TYPE_ITEM); + } + + result |= result2; + + //Для проверки + //for (int it = 0; it < af_types.size(); ++it) + //{ + // Msg("Afs After Upgrade af_types%i == %s, it is marked true", it, af_types[it].c_str()); + //} + + // РџРѕРґРіСЂСѓР·РёРј СЃРїРёСЃРѕРє всех артов, СЃРїРёСЃРѕРє прописан РІ линии af_to_detect_list. РџРѕРєР° будет так, так как РїРѕ РґСЂСѓРіРѕРјСѓ сделать РЅРµ хватает опыта + xr_vector af_total_list; + + LPCSTR S = READ_IF_EXISTS(pSettings, r_string, detector_section, "af_to_detect_list", "null"); + + if (S && S[0]) + { + string128 artefact; + int count = _GetItemCount(S); + for (int it = 0; it < count; ++it) + { + _GetItem(S, it, artefact); + af_total_list.push_back(artefact); + } + } + + // Отсеим только те арты, которые построчно прописаны РІ конфиге апгрейда Рё добавим РёС… РІ СЃРїРёСЃРѕРє видимых для детектора af_types + + for (int it = 0; it < af_total_list.size(); ++it) + { + u32 af_line_true_false = 0; + //Для проверки + + //Msg("Afs processing af_total_list %i == %s", it, af_total_list[it].c_str()); + + result2 = process_if_exists_set(section, af_total_list[it].c_str(), &CInifile::r_u32, af_line_true_false, test); + + if (af_line_true_false == 1){ + af_types.push_back(af_total_list[it]); + //Msg("found true %s", af_total_list[it].c_str()); + } + } + + ///Для проверки + //for (int it = 0; it < af_types.size(); ++it) + //{ + // Msg("Afs After Upgrade af_types%i == %s, it was marked true", it, af_types[it].c_str()); + //} + + result |= result2; + + return result; +} diff --git a/src/xrGameLA/CustomDetector2.h b/src/xrGameLA/CustomDetector2.h new file mode 100644 index 000000000..bbf9419bc --- /dev/null +++ b/src/xrGameLA/CustomDetector2.h @@ -0,0 +1,82 @@ +#pragma once + +#include "hud_item_object.h" +#include "ai_sounds.h" +//#include "script_export_space.h" + +class CUIArtefactDetectorBase; + +class CCustomDetectorR : public CHudItemObject +{ + typedef CHudItemObject inherited; +protected: + CUIArtefactDetectorBase* m_ui; + bool m_bFastAnimMode; + bool m_bNeedActivation; +public: + CCustomDetectorR(); + virtual ~CCustomDetectorR(); + + virtual BOOL net_Spawn(CSE_Abstract* DC); + virtual void Load(LPCSTR section); + + virtual void OnH_A_Chield(); + virtual void OnH_B_Independent(bool just_before_destroy); + + virtual void shedule_Update(u32 dt); + virtual void UpdateCL(); + + + bool IsWorking(); + + virtual void OnActiveItem(); + virtual void OnHiddenItem(); + virtual void OnStateSwitch(u32 S); + virtual void OnAnimationEnd(u32 state); + virtual void UpdateXForm(); + void OnMoveToSlot() override {}; + void OnMoveToRuck() override {}; + + // TODO: do we really need this "fast mode"? + void ToggleDetector(bool bFastMode); + void HideDetector(bool bFastMode, bool needToReactivate = false); + void HideDetectorInstantly(bool needToReactivate = false); + void ShowDetector(bool bFastMode); + + float fdetect_radius; + float foverallrangetocheck; + float fortestrangetocheck; + BOOL for_test; + BOOL reaction_sound_off; + xr_vector af_types; + LPCSTR af_sounds_section; + LPCSTR closestart; + LPCSTR detector_section; +protected: + void TurnDetectorInternal(bool b); + + void UpdateVisibility(); + void SwitchToBolt(); + bool IsItemStateCompatible(CHudItem*); + + virtual void UpdateWork(); + virtual void UpdateAf() {}; + virtual void CreateUI() {}; + + bool m_bWorking; +public: + float freqq; + float snd_timetime; + float cur_periodperiod; + u8 feel_touch_update_delay; + LPCSTR detect_sndsnd_line; + + ref_sound detect_snd; + + virtual bool install_upgrade_impl(LPCSTR section, bool test); + + //DECLARE_SCRIPT_REGISTER_FUNCTION +}; +//add_to_type_list(CCustomDetectorR) +//#undef script_type_list +//#define script_type_list save_type_list(CCustomDetectorR) \ No newline at end of file diff --git a/src/xrGameLA/CustomMonster.cpp b/src/xrGameLA/CustomMonster.cpp new file mode 100644 index 000000000..85bd27f26 --- /dev/null +++ b/src/xrGameLA/CustomMonster.cpp @@ -0,0 +1,1165 @@ +// CustomMonster.cpp: implementation of the CCustomMonster class. +// +////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_debug.h" +#include "CustomMonster.h" +#include "hudmanager.h" +#include "ai_space.h" +#include "ai/monsters/BaseMonster/base_monster.h" +#include "xrserver_objects_alife_monsters.h" +#include "xrserver.h" +#include "seniority_hierarchy_holder.h" +#include "team_hierarchy_holder.h" +#include "squad_hierarchy_holder.h" +#include "group_hierarchy_holder.h" +#include "customzone.h" +#include "../Include/xrRender/Kinematics.h" +#include "detail_path_manager.h" +#include "memory_manager.h" +#include "visual_memory_manager.h" +#include "sound_memory_manager.h" +#include "enemy_manager.h" +#include "item_manager.h" +#include "danger_manager.h" +#include "ai_object_location.h" +#include "level_graph.h" +#include "game_graph.h" +#include "movement_manager.h" +#include "entitycondition.h" +#include "sound_player.h" +#include "level.h" +#include "level_debug.h" +#include "material_manager.h" +#include "sound_user_data_visitor.h" +#include "mt_config.h" +#include "PHMovementControl.h" +#include "profiler.h" +#include "date_time.h" +#include "characterphysicssupport.h" +#include "ai/monsters/snork/snork.h" +#include "ai/monsters/burer/burer.h" +#include "GamePersistent.h" +#include "actor.h" +#include "alife_simulator.h" +#include "alife_object_registry.h" +#include "client_spawn_manager.h" + +#include "../IGame_Level.h" +#include "../../xrCore/_vector3d_ext.h" +#ifdef DEBUG +# include "debug_renderer.h" +#endif + +extern int g_AI_inactive_time; + +#ifndef MASTER_GOLD + Flags32 psAI_Flags = {0}; +#endif // MASTER_GOLD + +void CCustomMonster::SAnimState::Create(IKinematicsAnimated* K, LPCSTR base) +{ + char buf[128]; + fwd = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base,"_fwd")); + back = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base,"_back")); + ls = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base,"_ls")); + rs = K->ID_Cycle_Safe(strconcat(sizeof(buf),buf,base,"_rs")); +} + +//void __stdcall CCustomMonster::TorsoSpinCallback(CBoneInstance* B) +//{ +// CCustomMonster* M = static_cast (B->Callback_Param); +// +// Fmatrix spin; +// spin.setXYZ (0, M->NET_Last.o_torso.pitch, 0); +// B->mTransform.mulB_43 (spin); +//} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CCustomMonster::CCustomMonster() : + // this is non-polymorphic call of the virtual function cast_entity_alive + // just to remove warning C4355 if we use this instead + Feel::Vision ( cast_game_object() ) +{ + m_sound_user_data_visitor = 0; + m_memory_manager = 0; + m_movement_manager = 0; + m_sound_player = 0; + m_already_dead = false; + m_invulnerable = false; +} + +CCustomMonster::~CCustomMonster () +{ + xr_delete (m_sound_user_data_visitor); + xr_delete (m_memory_manager); + xr_delete (m_movement_manager); + xr_delete (m_sound_player); + + // Lain: added (asking GameLevel to forget about self) + if ( g_pGameLevel ) + { + g_pGameLevel->SoundEvent_OnDestDestroy(this); + } + +#ifdef DEBUG + Msg ("dumping client spawn manager stuff for object with id %d",ID()); + if(!g_dedicated_server) + Level().client_spawn_manager().dump (ID()); +#endif // DEBUG + if(!g_dedicated_server) + Level().client_spawn_manager().clear(ID()); +} + +void CCustomMonster::Load (LPCSTR section) +{ + inherited::Load (section); + material().Load (section); + memory().Load (section); + movement().Load (section); + ////////////////////////////////////////////////////////////////////////// + + /////////// + // m_PhysicMovementControl: General + + character_physics_support()->movement()->Load (section); + //Fbox bb; + + //// m_PhysicMovementControl: BOX + //Fvector vBOX0_center= pSettings->r_fvector3 (section,"ph_box0_center" ); + //Fvector vBOX0_size = pSettings->r_fvector3 (section,"ph_box0_size" ); + //bb.set (vBOX0_center,vBOX0_center); bb.grow(vBOX0_size); + //m_PhysicMovementControl->SetBox (0,bb); + + //// m_PhysicMovementControl: BOX + //Fvector vBOX1_center= pSettings->r_fvector3 (section,"ph_box1_center" ); + //Fvector vBOX1_size = pSettings->r_fvector3 (section,"ph_box1_size" ); + //bb.set (vBOX1_center,vBOX1_center); bb.grow(vBOX1_size); + //m_PhysicMovementControl->SetBox (1,bb); + + //// m_PhysicMovementControl: Foots + //Fvector vFOOT_center= pSettings->r_fvector3 (section,"ph_foot_center" ); + //Fvector vFOOT_size = pSettings->r_fvector3 (section,"ph_foot_size" ); + //bb.set (vFOOT_center,vFOOT_center); bb.grow(vFOOT_size); + //m_PhysicMovementControl->SetFoots (vFOOT_center,vFOOT_size); + + //// m_PhysicMovementControl: Crash speed and mass + //float cs_min = pSettings->r_float (section,"ph_crash_speed_min" ); + //float cs_max = pSettings->r_float (section,"ph_crash_speed_max" ); + //float mass = pSettings->r_float (section,"ph_mass" ); + //m_PhysicMovementControl->SetCrashSpeeds (cs_min,cs_max); + //m_PhysicMovementControl->SetMass (mass); + + + // m_PhysicMovementControl: Frictions + /* + float af, gf, wf; + af = pSettings->r_float (section,"ph_friction_air" ); + gf = pSettings->r_float (section,"ph_friction_ground"); + wf = pSettings->r_float (section,"ph_friction_wall" ); + m_PhysicMovementControl->SetFriction (af,wf,gf); + + // BOX activate + m_PhysicMovementControl->ActivateBox (0); + */ + //////// + + Position().y += EPS_L; + + // m_current = 0; + + eye_fov = pSettings->r_float(section,"eye_fov"); + eye_range = pSettings->r_float(section,"eye_range"); + + // Health & Armor +// fArmor = 0; + + // Msg ("! cmonster size: %d",sizeof(*this)); +} + +void CCustomMonster::reinit () +{ + CScriptEntity::reinit (); + CEntityAlive::reinit (); + material().reinit (); + movement().reinit (); + sound().reinit (); + + m_client_update_delta = 0; + m_last_client_update_time = Device.dwTimeGlobal; + + eye_pp_stage = 0; + m_dwLastUpdateTime = 0xffffffff; + m_tEyeShift.set (0,0,0); + m_fEyeShiftYaw = 0.f; + NET_WasExtrapolating = FALSE; + + ////////////////////////////////////////////////////////////////////////// + // Critical Wounds + ////////////////////////////////////////////////////////////////////////// + + m_critical_wound_type = u32(-1); + m_last_hit_time = 0; + m_critical_wound_accumulator = 0.f; + m_critical_wound_threshold = pSettings->r_float(cNameSect(),"critical_wound_threshold"); + m_critical_wound_decrease_quant = pSettings->r_float(cNameSect(),"critical_wound_decrease_quant"); + + if (m_critical_wound_threshold >= 0) + load_critical_wound_bones (); + ////////////////////////////////////////////////////////////////////////// + m_update_rotation_on_frame = true; + m_movement_enabled_before_animation_controller = true; +} + +void CCustomMonster::reload (LPCSTR section) +{ + sound().reload (section); + CEntityAlive::reload (section); + material().reload (section); + movement().reload (section); + load_killer_clsids (section); + + m_far_plane_factor = READ_IF_EXISTS(pSettings,r_float,section,"far_plane_factor",1.f); + m_fog_density_factor = READ_IF_EXISTS(pSettings,r_float,section,"fog_density_factor",.05f); + + m_panic_threshold = pSettings->r_float(section,"panic_threshold"); +} + +void CCustomMonster::mk_orientation(Fvector &dir, Fmatrix& mR) +{ + // orient only in XZ plane + dir.y = 0; + float len = dir.magnitude(); + if (len>EPS_S) + { + // normalize + dir.x /= len; + dir.z /= len; + Fvector up; up.set(0,1,0); + mR.rotation (dir,up); + } +} + +void CCustomMonster::net_Export(NET_Packet& P) // export to server +{ + R_ASSERT (Local()); + + // export last known packet + R_ASSERT (!NET.empty()); + net_update& N = NET.back(); + P.w_float (GetfHealth()); + P.w_u32 (N.dwTimeStamp); + P.w_u8 (0); + P.w_vec3 (N.p_pos); + P.w_float /*w_angle8*/ (N.o_model); + P.w_float /*w_angle8*/ (N.o_torso.yaw); + P.w_float /*w_angle8*/ (N.o_torso.pitch); + P.w_float /*w_angle8*/ (N.o_torso.roll); + P.w_u8 (u8(g_Team())); + P.w_u8 (u8(g_Squad())); + P.w_u8 (u8(g_Group())); +} + +void CCustomMonster::net_Import(NET_Packet& P) +{ + R_ASSERT (Remote()); + net_update N; + + u8 flags; + + float health; + P.r_float (health); + SetfHealth (health); + + P.r_u32 (N.dwTimeStamp); + P.r_u8 (flags); + P.r_vec3 (N.p_pos); + P.r_float /*r_angle8*/ (N.o_model); + P.r_float /*r_angle8*/ (N.o_torso.yaw); + P.r_float /*r_angle8*/ (N.o_torso.pitch); + P.r_float /*r_angle8*/ (N.o_torso.roll ); + + id_Team = P.r_u8(); + id_Squad = P.r_u8(); + id_Group = P.r_u8(); + + if (NET.empty() || (NET.back().dwTimeStamp2) && (NET[1].dwTimeStamp(this,&CCustomMonster::Exec_Visibility)); +#else // DEBUG + { + if (!psAI_Flags.test(aiStalker) || !!smart_cast(Level().CurrentEntity())) + Device.seqParallel.push_back(fastdelegate::FastDelegate0<>(this,&CCustomMonster::Exec_Visibility)); + else + Exec_Visibility (); + } +#endif // DEBUG + else + Exec_Visibility (); + memory().update (dt); + } + inherited::shedule_Update (DT); + + // Queue setup + if (dt > 3) return; + + m_dwCurrentTime = Device.dwTimeGlobal; + + VERIFY (_valid(Position())); + if (Remote()) { + } else { + // here is monster AI call + m_fTimeUpdateDelta = dt; + Device.Statistic->AI_Think.Begin (); + Device.Statistic->TEST1.Begin(); + if (GetScriptControl()) + ProcessScripts(); + else { + if (Device.dwFrame > spawn_time() + g_AI_inactive_time) + Think (); + } + m_dwLastUpdateTime = Device.dwTimeGlobal; + Device.Statistic->TEST1.End(); + Device.Statistic->AI_Think.End (); + + // Look and action streams + float temp = conditions().health(); + if (temp > 0) { + Exec_Action (dt); + VERIFY (_valid(Position())); + //Exec_Visibility (); + VERIFY (_valid(Position())); + ////////////////////////////////////// + //Fvector C; float R; + ////////////////////////////////////// + // С Олеся - ПИВО!!!! (Диме :-)))) + // m_PhysicMovementControl->GetBoundingSphere (C,R); + ////////////////////////////////////// + //Center(C); + //R = Radius(); + ////////////////////////////////////// + /// #pragma todo("Oles to all AI guys: perf/logical problem: Only few objects needs 'feel_touch' why to call update for everybody?") + /// feel_touch_update (C,R); + + net_update uNext; + uNext.dwTimeStamp = Level().timeServer(); + uNext.o_model = movement().m_body.current.yaw; + uNext.o_torso = movement().m_body.current; + uNext.p_pos = Position(); + uNext.fHealth = GetfHealth(); + NET.push_back (uNext); + } + else + { + net_update uNext; + uNext.dwTimeStamp = Level().timeServer(); + uNext.o_model = movement().m_body.current.yaw; + uNext.o_torso = movement().m_body.current; + uNext.p_pos = Position(); + uNext.fHealth = GetfHealth(); + NET.push_back (uNext); + } + } +} + +void CCustomMonster::net_update::lerp(CCustomMonster::net_update& A, CCustomMonster::net_update& B, float f) +{ + // + o_model = angle_lerp (A.o_model,B.o_model, f); + o_torso.yaw = angle_lerp (A.o_torso.yaw,B.o_torso.yaw, f); + o_torso.pitch = angle_lerp (A.o_torso.pitch,B.o_torso.pitch, f); + p_pos.lerp (A.p_pos,B.p_pos,f); + fHealth = A.fHealth*(1.f - f) + B.fHealth*f; +} + +void CCustomMonster::update_sound_player() +{ + sound().update (client_update_fdelta()); +} + +void CCustomMonster::UpdateCL () +{ + START_PROFILE("CustomMonster/client_update") + m_client_update_delta = (u32)std::min(Device.dwTimeGlobal - m_last_client_update_time, u32(100) ); + m_last_client_update_time = Device.dwTimeGlobal; + + START_PROFILE("CustomMonster/client_update/inherited") + inherited::UpdateCL (); + STOP_PROFILE + + CScriptEntity::process_sound_callbacks(); + + /* //. hack just to skip 'CalculateBones' + if (sound().need_bone_data()) { + // we do this because we know here would be virtual function call + CKinematics *kinematics = smart_cast(Visual()); + VERIFY (kinematics); + kinematics->CalculateBones (); + } + */ + + if (g_mt_config.test(mtSoundPlayer)) + Device.seqParallel.push_back (fastdelegate::FastDelegate0<>(this,&CCustomMonster::update_sound_player)); + else { + START_PROFILE("CustomMonster/client_update/sound_player") + update_sound_player (); + STOP_PROFILE + } + + START_PROFILE("CustomMonster/client_update/network extrapolation") + if (NET.empty()) { + update_animation_movement_controller (); + return; + } + + m_dwCurrentTime = Device.dwTimeGlobal; + + // distinguish interpolation/extrapolation + u32 dwTime = Level().timeServer()-NET_Latency; + net_update& N = NET.back(); + if ((dwTime > N.dwTimeStamp) || (NET.size() < 2)) { + // BAD. extrapolation + NET_Last = N; + } + else { + // OK. interpolation + NET_WasExtrapolating = FALSE; + // Search 2 keyframes for interpolation + int select = -1; + for (u32 id=0; id=0) + { + // Interpolate state + net_update& A = NET[select+0]; + net_update& B = NET[select+1]; + u32 d1 = dwTime-A.dwTimeStamp; + u32 d2 = B.dwTimeStamp - A.dwTimeStamp; +// VERIFY (d2); + float factor = d2 ? (float(d1)/float(d2)) : 1.f; + Fvector l_tOldPosition = Position(); + NET_Last.lerp (A,B,factor); + if (Local()) { + NET_Last.p_pos = l_tOldPosition; + } + else { + if (!bfScriptAnimation()) + SelectAnimation (XFORM().k,movement().detail().direction(),movement().speed()); + } + + // Signal, that last time we used interpolation + NET_WasInterpolating = TRUE; + NET_Time = dwTime; + } + } + STOP_PROFILE + + if (Local() && g_Alive()) { +#pragma todo("Dima to All : this is FAKE, network is not supported here!") + + UpdatePositionAnimation(); + } + + // Use interpolated/last state + if (g_Alive()) { + if (!animation_movement_controlled() && m_update_rotation_on_frame) + XFORM().rotateY (NET_Last.o_model); + + XFORM().translate_over (NET_Last.p_pos); + + if (!animation_movement_controlled() && m_update_rotation_on_frame) { + Fmatrix M; + M.setHPB (0.0f, -NET_Last.o_torso.pitch, 0.0f); + XFORM().mulB_43 (M); + } + } + +#ifdef DEBUG + if (IsMyCamera()) + UpdateCamera (); +#endif // DEBUG + + update_animation_movement_controller (); + + STOP_PROFILE +} + +void CCustomMonster::UpdatePositionAnimation() +{ + START_PROFILE("CustomMonster/client_update/movement") + movement().on_frame (character_physics_support()->movement(),NET_Last.p_pos); + STOP_PROFILE + + START_PROFILE("CustomMonster/client_update/animation") + if (!bfScriptAnimation()) + SelectAnimation (XFORM().k,movement().detail().direction(),movement().speed()); + STOP_PROFILE +} + +BOOL CCustomMonster::feel_visible_isRelevant (CObject* O) +{ + CEntityAlive* E = smart_cast (O); + if (0==E) return FALSE; + if (E->g_Team() == g_Team()) return FALSE; + return TRUE; +} + +void CCustomMonster::eye_pp_s0 ( ) +{ + // Eye matrix + IKinematics* V = smart_cast(Visual()); + V->CalculateBones (); + Fmatrix& mEye = V->LL_GetTransform(u16(eye_bone)); + Fmatrix X; X.mul_43 (XFORM(),mEye); + VERIFY (_valid(mEye)); + + const MonsterSpace::SBoneRotation &rotation = head_orientation(); + + eye_matrix.setHPB (-rotation.current.yaw + m_fEyeShiftYaw,-rotation.current.pitch,0); + eye_matrix.c.add (X.c,m_tEyeShift); +} + +void CCustomMonster::update_range_fov (float &new_range, float &new_fov, float start_range, float start_fov) +{ + const float standard_far_plane = eye_range; + + float current_fog_density = GamePersistent().Environment().CurrentEnv->fog_density ; + // 0=no_fog, 1=full_fog, >1 = super-fog + float current_far_plane = GamePersistent().Environment().CurrentEnv->far_plane ; + // 300=standart, 50=super-fog + + new_fov = start_fov; + new_range = + start_range + * + ( + _min(m_far_plane_factor*current_far_plane,standard_far_plane) + / + standard_far_plane + ) + * + ( + 1.f + / + ( + 1.f + m_fog_density_factor*current_fog_density + ) + ) + ; +} + +void CCustomMonster::eye_pp_s1 () +{ + float new_range = eye_range, new_fov = eye_fov; + if (g_Alive()) { +#ifndef USE_STALKER_VISION_FOR_MONSTERS + update_range_fov (new_range, new_fov, human_being() ? memory().visual().current_state().m_max_view_distance*eye_range : eye_range, eye_fov); +#else + update_range_fov (new_range, new_fov, memory().visual().current_state().m_max_view_distance*eye_range, eye_fov); +#endif + } + // Standart visibility + Device.Statistic->AI_Vis_Query.Begin (); + Fmatrix mProject,mFull,mView; + mView.build_camera_dir (eye_matrix.c,eye_matrix.k,eye_matrix.j); + VERIFY (_valid(eye_matrix)); + mProject.build_projection (deg2rad(new_fov),1,0.1f,new_range); + mFull.mul (mProject,mView); + feel_vision_query (mFull,eye_matrix.c); + Device.Statistic->AI_Vis_Query.End (); +} + +void CCustomMonster::eye_pp_s2 ( ) +{ + // Tracing + Device.Statistic->AI_Vis_RayTests.Begin (); + u32 dwTime = Level().timeServer(); + u32 dwDT = dwTime-eye_pp_timestamp; + eye_pp_timestamp = dwTime; + feel_vision_update (this,eye_matrix.c,float(dwDT)/1000.f,memory().visual().transparency_threshold()); + Device.Statistic->AI_Vis_RayTests.End (); +} + +void CCustomMonster::Exec_Visibility ( ) +{ + //if (0==Sector()) return; + if (!g_Alive()) return; + + Device.Statistic->AI_Vis.Begin (); + switch (eye_pp_stage%2) + { + case 0: + eye_pp_s0(); + eye_pp_s1(); break; + case 1: eye_pp_s2(); break; + } + ++eye_pp_stage ; + Device.Statistic->AI_Vis.End (); +} + +void CCustomMonster::UpdateCamera() +{ + float new_range = eye_range, new_fov = eye_fov; + if (g_Alive()) + update_range_fov (new_range, new_fov, memory().visual().current_state().m_max_view_distance*eye_range, eye_fov); + g_pGameLevel->Cameras().Update(eye_matrix.c,eye_matrix.k,eye_matrix.j,new_fov,.75f,new_range,0); +} + +void CCustomMonster::HitSignal(float /**perc/**/, Fvector& /**vLocalDir/**/, CObject* /**who/**/) +{ +} + +void CCustomMonster::Die (CObject* who) +{ + inherited::Die (who); + //Level().RemoveMapLocationByID(this->ID()); + Actor()->SetActorVisibility (ID(),0.f); +} + +BOOL CCustomMonster::net_Spawn (CSE_Abstract* DC) +{ + memory().reload (*cNameSect()); + memory().reinit (); + + if (!movement().net_Spawn(DC) || !inherited::net_Spawn(DC) || !CScriptEntity::net_Spawn(DC)) + return (FALSE); + + ISpatial *self = smart_cast (this); + if (self) { + self->spatial.type |= STYPE_VISIBLEFORAI; + // enable react to sound only if alive + if (g_Alive()) + self->spatial.type |= STYPE_REACTTOSOUND; + } + + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeMonsterAbstract *E = smart_cast(e); + + eye_matrix.identity (); + movement().m_body.current.yaw = movement().m_body.target.yaw = -E->o_torso.yaw; + movement().m_body.current.pitch = movement().m_body.target.pitch = 0; + SetfHealth (E->fHealth); + if (!g_Alive()) { + set_death_time (); +// Msg ("%6d : Object [%d][%s][%s] is spawned DEAD",Device.dwTimeGlobal,ID(),*cName(),*cNameSect()); + } + + if (ai().get_level_graph() && UsedAI_Locations() && (e->ID_Parent == 0xffff)) { + if (ai().game_graph().valid_vertex_id(E->m_tGraphID)) + ai_location().game_vertex (E->m_tGraphID); + + if ( + ai().game_graph().valid_vertex_id(E->m_tNextGraphID) + && + (ai().game_graph().vertex(E->m_tNextGraphID)->level_id() == ai().level_graph().level_id()) + && + movement().restrictions().accessible( + ai().game_graph().vertex( + E->m_tNextGraphID + )->level_vertex_id() + ) + ) + movement().set_game_dest_vertex (E->m_tNextGraphID); + + if (movement().restrictions().accessible(ai_location().level_vertex_id())) + movement().set_level_dest_vertex (ai_location().level_vertex_id()); + else { + Fvector dest_position; + u32 level_vertex_id; + level_vertex_id = movement().restrictions().accessible_nearest(ai().level_graph().vertex_position(ai_location().level_vertex_id()),dest_position); + movement().set_level_dest_vertex (level_vertex_id); + movement().detail().set_dest_position (dest_position); + } + } + + // Eyes + eye_bone = smart_cast(Visual())->LL_BoneID(pSettings->r_string(cNameSect(),"bone_head")); + + // weapons + if (Local()) { + net_update N; + N.dwTimeStamp = Level().timeServer()-NET_Latency; + N.o_model = -E->o_torso.yaw; + N.o_torso.yaw = -E->o_torso.yaw; + N.o_torso.pitch = 0; + N.p_pos.set (Position()); + NET.push_back (N); + + N.dwTimeStamp += NET_Latency; + NET.push_back (N); + + setVisible (TRUE); + setEnabled (TRUE); + } + + // Sheduler + shedule.t_min = 100; + shedule.t_max = 250; // This equaltiy is broken by Dima :-( // 30 * NET_Latency / 4; + + return TRUE; +} + +#ifdef DEBUG +void CCustomMonster::OnHUDDraw(CCustomHUD *hud) +{ +} +#endif + +void CCustomMonster::Exec_Action(float /**dt/**/) +{ +} + +//void CCustomMonster::Hit(float P, Fvector &dir,CObject* who, s16 element,Fvector position_in_object_space, float impulse, ALife::EHitType hit_type) +void CCustomMonster::Hit (SHit* pHDS) +{ + if (!invulnerable()) + inherited::Hit (pHDS); +} + +void CCustomMonster::OnEvent(NET_Packet& P, u16 type) +{ + inherited::OnEvent (P,type); +} + +void CCustomMonster::net_Destroy() +{ + inherited::net_Destroy (); + CScriptEntity::net_Destroy (); + sound().unload (); + movement().net_Destroy (); + + Device.remove_from_seq_parallel ( + fastdelegate::FastDelegate0<>( + this, + &CCustomMonster::update_sound_player + ) + ); + Device.remove_from_seq_parallel ( + fastdelegate::FastDelegate0<>( + this, + &CCustomMonster::Exec_Visibility + ) + ); + +#ifdef DEBUG + DBG().on_destroy_object(this); +#endif +} + +BOOL CCustomMonster::UsedAI_Locations() +{ + return (TRUE); +} + +void CCustomMonster::PitchCorrection() +{ + CLevelGraph::SContour contour; + ai().level_graph().contour(contour, ai_location().level_vertex_id()); + + Fplane P; + P.build(contour.v1,contour.v2,contour.v3); + + Fvector position_on_plane; + P.project(position_on_plane,Position()); + + // находим проекцию точки, лежащей на векторе текущего направления + Fvector dir_point, proj_point; + dir_point.mad(position_on_plane, Direction(), 1.f); + P.project(proj_point,dir_point); + + // получаем искомый вектор направления + Fvector target_dir; + target_dir.sub(proj_point,position_on_plane); + + float yaw,pitch; + target_dir.getHP(yaw,pitch); + + movement().m_body.target.pitch = -pitch; + +} + +BOOL CCustomMonster::feel_touch_on_contact (CObject *O) +{ + CCustomZone *custom_zone = smart_cast(O); + if (!custom_zone) + return (TRUE); + + Fsphere sphere; + sphere.P = Position(); + sphere.R = EPS_L; + if (custom_zone->inside(sphere)) + return (TRUE); + + return (FALSE); +} + +BOOL CCustomMonster::feel_touch_contact (CObject *O) +{ + CCustomZone *custom_zone = smart_cast(O); + if (!custom_zone) + return (TRUE); + + Fsphere sphere; + sphere.P = Position(); + sphere.R = 0.f; + if (custom_zone->inside(sphere)) + return (TRUE); + + return (FALSE); +} + +void CCustomMonster::set_ready_to_save () +{ + inherited::set_ready_to_save (); + memory().enemy().set_ready_to_save (); +} + +void CCustomMonster::load_killer_clsids(LPCSTR section) +{ + m_killer_clsids.clear (); + LPCSTR killers = pSettings->r_string(section,"killer_clsids"); + string16 temp; + for (u32 i=0, n=_GetItemCount(killers); iCLS_ID) != m_killer_clsids.end())); +} + +float CCustomMonster::feel_vision_mtl_transp(CObject* O, u32 element) +{ + return (memory().visual().feel_vision_mtl_transp(O,element)); +} + +void CCustomMonster::feel_sound_new (CObject* who, int type, CSound_UserDataPtr user_data, const Fvector &position, float power) +{ + // Lain: added + if (!g_Alive()) + { + return; + } + if (getDestroy()) + { + return; + } + memory().sound().feel_sound_new(who,type,user_data,position,power); +} + +bool CCustomMonster::useful (const CItemManager *manager, const CGameObject *object) const +{ + return (memory().item().useful(object)); +} + +float CCustomMonster::evaluate (const CItemManager *manager, const CGameObject *object) const +{ + return (memory().item().evaluate(object)); +} + +bool CCustomMonster::useful (const CEnemyManager *manager, const CEntityAlive *object) const +{ + return (memory().enemy().useful(object)); +} + +float CCustomMonster::evaluate (const CEnemyManager *manager, const CEntityAlive *object) const +{ + return (memory().enemy().evaluate(object)); +} + +bool CCustomMonster::useful (const CDangerManager *manager, const CDangerObject &object) const +{ + return (memory().danger().useful(object)); +} + +float CCustomMonster::evaluate (const CDangerManager *manager, const CDangerObject &object) const +{ + return (memory().danger().evaluate(object)); +} + +CMovementManager *CCustomMonster::create_movement_manager () +{ + return (new CMovementManager(this)); +} + +CSound_UserDataVisitor *CCustomMonster::create_sound_visitor () +{ + return (m_sound_user_data_visitor = new CSound_UserDataVisitor()); +} + +CMemoryManager *CCustomMonster::create_memory_manager () +{ + return (new CMemoryManager(this,create_sound_visitor())); +} + +const SRotation CCustomMonster::Orientation () const +{ + return (movement().m_body.current); +}; + +const MonsterSpace::SBoneRotation &CCustomMonster::head_orientation () const +{ + return (movement().m_body); +} + +DLL_Pure *CCustomMonster::_construct() +{ + m_memory_manager = create_memory_manager(); + m_movement_manager = create_movement_manager(); + m_sound_player = new CSoundPlayer(this); + + inherited::_construct (); + CScriptEntity::_construct (); + + return (this); +} + +void CCustomMonster::net_Relcase (CObject *object) +{ + inherited::net_Relcase (object); + memory().remove_links (object); +} + +void CCustomMonster::set_fov (float new_fov) +{ + VERIFY (new_fov > 0.f); + eye_fov = new_fov; +} + +void CCustomMonster::set_range (float new_range) +{ + VERIFY (new_range > 1.f); + eye_range = new_range; +} + +void CCustomMonster::on_restrictions_change () +{ + memory().on_restrictions_change (); + movement().on_restrictions_change (); +} + +LPCSTR CCustomMonster::visual_name (CSE_Abstract *server_entity) +{ + return (inherited::visual_name(server_entity)); +} + +void CCustomMonster::on_enemy_change(const CEntityAlive *enemy) +{ +} + +CVisualMemoryManager *CCustomMonster::visual_memory () const +{ + return (&memory().visual()); +} + +void CCustomMonster::save (NET_Packet &packet) +{ + inherited::save (packet); + if (g_Alive()) + memory().save (packet); +} + +void CCustomMonster::load (IReader &packet) +{ + inherited::load (packet); + if (g_Alive()) + memory().load (packet); +} + + +bool CCustomMonster::update_critical_wounded (const u16 &bone_id, const float &power) +{ + // object should not be critical wounded + VERIFY (m_critical_wound_type == u32(-1)); + // check 'multiple updates during last hit' situation + VERIFY (Device.dwTimeGlobal >= m_last_hit_time); + + if (m_critical_wound_threshold < 0) return (false); + + + float time_delta = m_last_hit_time ? float(Device.dwTimeGlobal - m_last_hit_time)/1000.f : 0.f; + m_critical_wound_accumulator += power - m_critical_wound_decrease_quant*time_delta; + clamp (m_critical_wound_accumulator,0.f,m_critical_wound_threshold); + +#if 0//def _DEBUG + Msg ( + "%6d [%s] update_critical_wounded: %f[%f] (%f,%f) [%f]", + Device.dwTimeGlobal, + *cName(), + m_critical_wound_accumulator, + power, + m_critical_wound_threshold, + m_critical_wound_decrease_quant, + time_delta + ); +#endif // DEBUG + + m_last_hit_time = Device.dwTimeGlobal; + if (m_critical_wound_accumulator < m_critical_wound_threshold) + return (false); + + m_last_hit_time = 0; + m_critical_wound_accumulator = 0.f; + + if (critical_wound_external_conditions_suitable()) { + BODY_PART::const_iterator I = m_bones_body_parts.find(bone_id); + if (I == m_bones_body_parts.end()) return (false); + + m_critical_wound_type = (*I).second; + + critical_wounded_state_start (); + + return (true); + } + + return (false); +} + +#ifdef DEBUG + +extern void dbg_draw_frustum (float FOV, float _FAR, float A, Fvector &P, Fvector &D, Fvector &U); +void draw_visiblity_rays (CCustomMonster *self, const CObject *object, collide::rq_results& rq_storage); + +void CCustomMonster::OnRender() +{ + DRender->OnFrameEnd(); + //RCache.OnFrameEnd (); + + for (int i=0; i<1; ++i) { + const xr_vector &keys = !i ? movement().detail().m_key_points : movement().detail().m_key_points; + const xr_vector &path = !i ? movement().detail().path() : movement().detail().path(); + u32 color0 = !i ? D3DCOLOR_XRGB(0,255,0) : D3DCOLOR_XRGB(0,0,255); + u32 color1 = !i ? D3DCOLOR_XRGB(255,0,0) : D3DCOLOR_XRGB(255,255,0); + u32 color2 = !i ? D3DCOLOR_XRGB(0,0,255) : D3DCOLOR_XRGB(0,255,255); + u32 color3 = !i ? D3DCOLOR_XRGB(255,255,255) : D3DCOLOR_XRGB(255,0,255); + float radius0 = !i ? .1f : .15f; + float radius1 = !i ? .2f : .3f; + { + for (u32 I=1; Iposition(); + P1.y += 1.f; + Level().debug_renderer().draw_aabb (P1,1.f,1.f,1.f,D3DCOLOR_XRGB(0,0,0)); + } + } + + if (psAI_Flags.test(aiFrustum)) { + float new_range = eye_range, new_fov = eye_fov; + + if (g_Alive()) + update_range_fov (new_range, new_fov, memory().visual().current_state().m_max_view_distance*eye_range, eye_fov); + + dbg_draw_frustum (new_fov,new_range,1,eye_matrix.c,eye_matrix.k,eye_matrix.j); + } + + if (psAI_Flags.test(aiMotion)) + character_physics_support()->movement()->dbg_Draw(); + + if (bDebug) + smart_cast(Visual())->DebugRender(XFORM()); +// if (m_jump_picks.size() < 1) +// return; +} +#endif // DEBUG + +void CCustomMonster::create_anim_mov_ctrl (CBlend *b) +{ + inherited::create_anim_mov_ctrl (b); + + m_movement_enabled_before_animation_controller = movement().enabled(); + movement().enable_movement (false); +} + +void CCustomMonster::destroy_anim_mov_ctrl () +{ + inherited::destroy_anim_mov_ctrl(); + + movement().enable_movement (m_movement_enabled_before_animation_controller); + + float roll; + XFORM().getHPB (movement().m_body.current.yaw, movement().m_body.current.pitch, roll); + + movement().m_body.current.yaw *= -1.f; + movement().m_body.current.pitch *= -1.f; + + movement().m_body.target.yaw = movement().m_body.current.yaw; + movement().m_body.target.pitch = movement().m_body.current.pitch; + + NET_Last.o_model = movement().m_body.current.yaw; + NET_Last.o_torso.pitch = movement().m_body.current.pitch; +} + +void CCustomMonster::ForceTransform(const Fmatrix& m) +{ + character_physics_support()->ForceTransform( m ); +} diff --git a/src/xrGameLA/CustomMonster.h b/src/xrGameLA/CustomMonster.h new file mode 100644 index 000000000..c77e61d4e --- /dev/null +++ b/src/xrGameLA/CustomMonster.h @@ -0,0 +1,328 @@ +// CustomMonster.h: interface for the CCustomMonster class. +// +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "entity_alive.h" +#include "script_entity.h" +#include "../feel_vision.h" +#include "../feel_sound.h" +#include "../feel_touch.h" +#include "../../Include/xrRender/Kinematics.h" +#include "associative_vector.h" +#include "trajectories.h" + +namespace MonsterSpace { + struct SBoneRotation; +}; + +class CMotionDef; +class IKinematicsAnimated; +class CMemoryManager; +class CItemManager; +class CEnemyManager; +class CDangerManager; +class CMovementManager; +class CSoundPlayer; +class CAI_Stalker; +class CDangerObject; + +class CCustomMonster : + public CEntityAlive, + public CScriptEntity, + public Feel::Vision, + public Feel::Sound, + public Feel::Touch +{ +protected: + typedef CEntityAlive inherited; + +private: + CMemoryManager *m_memory_manager; + CMovementManager *m_movement_manager; + CSoundPlayer *m_sound_player; + +private: + u32 m_client_update_delta; + u32 m_last_client_update_time; + +protected: + + struct SAnimState + { + MotionID fwd; + MotionID back; + MotionID ls; + MotionID rs; + + void Create(IKinematicsAnimated* K, LPCSTR base); + }; + +private: + xr_vector m_killer_clsids; + +public: + // Eyes + Fmatrix eye_matrix; + int eye_bone; + float eye_fov; + float eye_range; + + float m_fCurSpeed; + + u32 eye_pp_stage; + u32 eye_pp_timestamp; + Fvector m_tEyeShift; + float m_fEyeShiftYaw; + BOOL NET_WasExtrapolating; + + Fvector tWatchDirection; + + virtual void Think() = 0; + + float m_fTimeUpdateDelta; + u32 m_dwLastUpdateTime; + u32 m_current_update; +// Fmatrix m_tServerTransform; + + u32 m_dwCurrentTime; // time updated in UpdateCL + + struct net_update { + u32 dwTimeStamp; // server(game) timestamp + float o_model; // model yaw + SRotation o_torso; // torso in world coords + Fvector p_pos; // in world coords + float fHealth; + + // non-exported (temporal) + + net_update() { + dwTimeStamp = 0; + o_model = 0; + o_torso.yaw = 0; + o_torso.pitch = 0; + p_pos.set (0,0,0); + fHealth = 0.f; + } + void lerp (net_update& A,net_update& B, float f); + }; + xr_deque NET; + net_update NET_Last; + BOOL NET_WasInterpolating; // previous update was by interpolation or by extrapolation + u32 NET_Time; // server time of last update +//------------------------------ + + virtual BOOL feel_touch_on_contact (CObject *); + virtual BOOL feel_touch_contact (CObject *); + // utils + void mk_orientation ( Fvector& dir, Fmatrix& mR ); + void mk_rotation ( Fvector& dir, SRotation &R); + + // stream executors + virtual void Exec_Action ( float dt ); + virtual void Exec_Look ( float dt ); + void __stdcall Exec_Visibility ( ); + virtual void eye_pp_s0 ( ); + void eye_pp_s1 ( ); + void eye_pp_s2 ( ); + + virtual void UpdateCamera ( ); + +public: + CCustomMonster ( ); + virtual ~CCustomMonster ( ); + +public: + virtual CEntityAlive* cast_entity_alive () {return this;} + virtual CEntity* cast_entity () {return this;} + +public: + + virtual DLL_Pure *_construct (); + virtual BOOL net_Spawn ( CSE_Abstract* DC); + virtual void Die ( CObject* who); + + virtual void HitSignal ( float P, Fvector& vLocalDir, CObject* who); + virtual void g_WeaponBones (int &/**L/**/, int &/**R1/**/, int &/**R2/**/) {}; + virtual void shedule_Update ( u32 DT ); + virtual void UpdateCL ( ); + + // Network + virtual void net_Export (NET_Packet& P); // export to server + virtual void net_Import (NET_Packet& P); // import from server + virtual void net_Relcase (CObject* O); + + virtual void SelectAnimation ( const Fvector& _view, const Fvector& _move, float speed ) = 0; + + virtual bool is_base_monster_with_enemy () { return false; } + + // debug +#ifdef DEBUG + virtual void OnRender ( ); + virtual void OnHUDDraw (CCustomHUD* hud); +#endif + + virtual bool bfExecMovement (){return(false);}; + + + IC bool angle_lerp_bounds (float &a, float b, float c, float d); + IC void vfNormalizeSafe (Fvector& Vector); + +public: + virtual float ffGetFov () const {return eye_fov;} + virtual float ffGetRange () const {return eye_range;} + void set_fov (float new_fov); + void set_range (float new_range); +// virtual void feel_touch_new (CObject *O); + virtual BOOL feel_visible_isRelevant (CObject *O); + virtual Feel::Sound* dcast_FeelSound () { return this; } + virtual void Hit (SHit* pHDS); + + virtual void OnEvent ( NET_Packet& P, u16 type ); + virtual void net_Destroy (); + virtual BOOL UsedAI_Locations (); + /////////////////////////////////////////////////////////////////////// + virtual u16 PHGetSyncItemsNumber () {return inherited ::PHGetSyncItemsNumber();} + virtual CPHSynchronize* PHGetSyncItem (u16 item) {return inherited ::PHGetSyncItem(item);} + virtual void PHUnFreeze () {return inherited ::PHUnFreeze();} + virtual void PHFreeze () {return inherited ::PHFreeze();} + /////////////////////////////////////////////////////////////////////// +public: + virtual void Load (LPCSTR section); + virtual void reinit (); + virtual void reload (LPCSTR section); + virtual const SRotation Orientation () const; + virtual float get_custom_pitch_speed (float def_speed) {return def_speed;} + virtual bool human_being () const + { + return (false); + } + virtual void PitchCorrection (); + + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + virtual BOOL net_SaveRelevant () {return inherited::net_SaveRelevant();} + + virtual const MonsterSpace::SBoneRotation &head_orientation () const; + + virtual void UpdatePositionAnimation (); + virtual void set_ready_to_save (); + virtual CPhysicsShellHolder*cast_physics_shell_holder () {return this;} + virtual CParticlesPlayer* cast_particles_player () {return this;} + virtual CCustomMonster* cast_custom_monster () {return this;} + virtual CScriptEntity* cast_script_entity () {return this;} + + void load_killer_clsids (LPCSTR section); + bool is_special_killer (CObject *obj); + + IC CMemoryManager &memory () const; + virtual float feel_vision_mtl_transp (CObject* O, u32 element); + virtual void feel_sound_new (CObject* who, int type, CSound_UserDataPtr user_data, const Fvector &Position, float power); + + virtual bool useful (const CItemManager *manager, const CGameObject *object) const; + virtual float evaluate (const CItemManager *manager, const CGameObject *object) const; + virtual bool useful (const CEnemyManager *manager, const CEntityAlive *object) const; + virtual float evaluate (const CEnemyManager *manager, const CEntityAlive *object) const; + virtual bool useful (const CDangerManager *manager, const CDangerObject &object) const; + virtual float evaluate (const CDangerManager *manager, const CDangerObject &object) const; + +protected: + float m_panic_threshold; + +public: + IC float panic_threshold () const; + + +private: + CSound_UserDataVisitor *m_sound_user_data_visitor; + +protected: + virtual CSound_UserDataVisitor *create_sound_visitor (); + virtual CMemoryManager *create_memory_manager (); + virtual CMovementManager *create_movement_manager(); + +public: + IC CMovementManager &movement () const; + IC CSoundPlayer &sound () const; + IC CSound_UserDataVisitor *sound_user_data_visitor() const; + +protected: + float m_far_plane_factor; + float m_fog_density_factor; + +public: + virtual void update_range_fov (float &new_range, float &new_fov, float start_range, float start_fov); + +public: + void __stdcall update_sound_player (); + virtual void on_restrictions_change (); + + virtual bool should_wait_to_use_corspe_visual () { return true; } + virtual LPCSTR visual_name (CSE_Abstract *server_entity); + +private: + bool m_already_dead; + +public: + IC const bool &already_dead () const {return (m_already_dead);}; + virtual bool use_simplified_visual () const {return false;} //(already_dead());}; + virtual void on_enemy_change (const CEntityAlive *enemy); + virtual CVisualMemoryManager *visual_memory () const; + +public: + IC float client_update_fdelta () const; + IC const u32 &client_update_delta () const; + IC const u32 &last_client_update_time() const; + + ////////////////////////////////////////////////////////////////////////// + // Critical Wounds + ////////////////////////////////////////////////////////////////////////// +public: + typedef u32 CriticalWoundType; +private: + typedef associative_vector BODY_PART; + +protected: + u32 m_last_hit_time; + float m_critical_wound_threshold; + float m_critical_wound_decrease_quant; + float m_critical_wound_accumulator; + CriticalWoundType m_critical_wound_type; + BODY_PART m_bones_body_parts; + +protected: + virtual void load_critical_wound_bones () {} + + virtual bool critical_wound_external_conditions_suitable () {return true;} + virtual void critical_wounded_state_start () {} + + bool update_critical_wounded (const u16 &bone_id, const float &power); + +public: + IC void critical_wounded_state_stop (); +public: + IC bool critically_wounded (); + IC const u32 &critical_wound_type () const; + + ////////////////////////////////////////////////////////////////////////// +private: + bool m_invulnerable; + +public: + IC void invulnerable (const bool &invulnerable); + IC bool invulnerable () const; + +protected: + bool m_update_rotation_on_frame; + +private: + bool m_movement_enabled_before_animation_controller; + +public: + virtual void create_anim_mov_ctrl (CBlend *b); + virtual void destroy_anim_mov_ctrl (); + virtual void ForceTransform ( Fmatrix const& m ); +}; + +#include "custommonster_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/CustomMonster_VCPU.cpp b/src/xrGameLA/CustomMonster_VCPU.cpp new file mode 100644 index 000000000..9a33916aa --- /dev/null +++ b/src/xrGameLA/CustomMonster_VCPU.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include "custommonster.h" +#include "movement_manager.h" + +IC void conv_angle(float& c) +{ + if (c<0) c+=PI_MUL_2; + else if (c>PI_MUL_2) c-=PI_MUL_2; +} + +void CCustomMonster::mk_rotation (Fvector &dir, SRotation &R) +{ + // parse yaw + Fvector DYaw; + DYaw.set(dir.x,0.f,dir.z); + DYaw.normalize_safe(); + clamp(DYaw.x,-0.9999999f,0.9999999f); + clamp(DYaw.y,-0.9999999f,0.9999999f); + clamp(DYaw.z,-0.9999999f,0.9999999f); + if (DYaw.x>=0) + R.yaw = acosf(DYaw.z); + else + R.yaw = 2*PI-acosf(DYaw.z); + + // parse pitch + dir.normalize_safe (); + R.pitch = -asinf(dir.y); +} + +void CCustomMonster::Exec_Look ( float dt ) +{ + if (animation_movement_controlled()) + return; + + movement().m_body.current.yaw = angle_normalize_signed (movement().m_body.current.yaw); + movement().m_body.current.pitch = angle_normalize_signed (movement().m_body.current.pitch); + movement().m_body.target.yaw = angle_normalize_signed (movement().m_body.target.yaw); + movement().m_body.target.pitch = angle_normalize_signed (movement().m_body.target.pitch); + + float pitch_speed = get_custom_pitch_speed(movement().m_body.speed); + angle_lerp_bounds (movement().m_body.current.yaw,movement().m_body.target.yaw,movement().m_body.speed,dt); + angle_lerp_bounds (movement().m_body.current.pitch,movement().m_body.target.pitch,pitch_speed,dt); + + Fvector P = Position(); + XFORM().setHPB (-NET_Last.o_model,-NET_Last.o_torso.pitch,0); + Position() = P; +} \ No newline at end of file diff --git a/src/xrGameLA/CustomMonster_inline.h b/src/xrGameLA/CustomMonster_inline.h new file mode 100644 index 000000000..82cf7d284 --- /dev/null +++ b/src/xrGameLA/CustomMonster_inline.h @@ -0,0 +1,102 @@ +#pragma once + +IC bool CCustomMonster::angle_lerp_bounds(float &a, float b, float c, float d) +{ + if (c*d >= angle_difference(a,b)) { + a = b; + return(true); + } + + angle_lerp(a,b,c,d); + + return(false); +}; + +IC void CCustomMonster::vfNormalizeSafe(Fvector& Vector) +{ + float fMagnitude = Vector.magnitude(); + if (fMagnitude > EPS_L) { + Vector.x /= fMagnitude; + Vector.y /= fMagnitude; + Vector.z /= fMagnitude; + } + else { + Vector.x = 1.f; + Vector.y = 0.f; + Vector.z = 0.f; + } +} + +ICF bool left_angle(float y1, float y2) +{ + return (_sin(y1)*_cos(y2) - _sin(y2)*_cos(y1) <= 0.f); +} + +IC CMemoryManager &CCustomMonster::memory () const +{ + VERIFY (m_memory_manager); + return (*m_memory_manager); +} + +IC CMovementManager &CCustomMonster::movement () const +{ + VERIFY (m_movement_manager); + return (*m_movement_manager); +} + +IC CSoundPlayer &CCustomMonster::sound () const +{ + VERIFY (m_sound_player); + return (*m_sound_player); +} + +IC CSound_UserDataVisitor *CCustomMonster::sound_user_data_visitor () const +{ + VERIFY (m_sound_user_data_visitor); + return (m_sound_user_data_visitor); +} + +IC float CCustomMonster::panic_threshold () const +{ + return (m_panic_threshold); +} + +IC float CCustomMonster::client_update_fdelta () const +{ + return ((float)m_client_update_delta/1000.f); +} + +IC const u32 &CCustomMonster::client_update_delta () const +{ + return (m_client_update_delta); +} + +IC const u32 &CCustomMonster::last_client_update_time () const +{ + return (m_last_client_update_time); +} + +IC const u32 &CCustomMonster::critical_wound_type () const +{ + return (m_critical_wound_type); +} + +IC bool CCustomMonster::critically_wounded () +{ + return (m_critical_wound_type != u32(-1)); +} + +IC void CCustomMonster::critical_wounded_state_stop () +{ + m_critical_wound_type = u32(-1); +} + +IC void CCustomMonster::invulnerable (const bool &invulnerable) +{ + m_invulnerable = invulnerable; +} + +IC bool CCustomMonster::invulnerable () const +{ + return (m_invulnerable); +} diff --git a/src/xrGameLA/CustomOutfit.cpp b/src/xrGameLA/CustomOutfit.cpp new file mode 100644 index 000000000..057d93545 --- /dev/null +++ b/src/xrGameLA/CustomOutfit.cpp @@ -0,0 +1,308 @@ +#include "stdafx.h" + +#include "customoutfit.h" +#include "PhysicsShell.h" +#include "inventory_space.h" +#include "Inventory.h" +#include "Actor.h" +#include "game_cl_base.h" +#include "Level.h" +#include "BoneProtections.h" +#include "../../Include/xrRender/Kinematics.h" +#include "../../Include/xrRender/RenderVisual.h" +#include "ai_sounds.h" +#include "actorEffector.h" +#include "player_hud.h" + +CCustomOutfit::CCustomOutfit() +{ + m_baseSlot = OUTFIT_SLOT; +} + +CCustomOutfit::~CCustomOutfit() +{ + /*HUD_SOUND_ITEM::DestroySound (m_NightVisionOnSnd); + HUD_SOUND_ITEM::DestroySound (m_NightVisionOffSnd); + HUD_SOUND_ITEM::DestroySound (m_NightVisionIdleSnd); + HUD_SOUND_ITEM::DestroySound (m_NightVisionBrokenSnd);*/ +} + +void CCustomOutfit::net_Export(NET_Packet& P) +{ + inherited::net_Export(P); + //P.w_u8(m_bNightVisionOn ? 1 : 0); +} + +void CCustomOutfit::net_Import(NET_Packet& P) +{ + inherited::net_Import(P); + /*bool new_bNightVisionOn = !!P.r_u8(); + + if (new_bNightVisionOn != m_bNightVisionOn) + SwitchNightVision(new_bNightVisionOn);*/ +} + +void CCustomOutfit::Load(LPCSTR section) +{ + inherited::Load(section); + + if (pSettings->line_exist(section, "actor_visual")) + m_ActorVisual = pSettings->r_string(section, "actor_visual"); + else + m_ActorVisual = NULL; + + if (pSettings->line_exist(section, "actor_visual_legs")) + m_ActorVisual_legs = pSettings->r_string(section, "actor_visual_legs"); + else + m_ActorVisual_legs = NULL; + + m_ef_equipment_type = pSettings->r_u32(section,"ef_equipment_type"); + + m_additional_weight = pSettings->r_float(section,"additional_inventory_weight"); + m_additional_weight2 = pSettings->r_float(section,"additional_inventory_weight2"); + + //tatarinrafa: added additional jump speed sprint speed walk speed + m_additional_jump_speed = READ_IF_EXISTS(pSettings, r_float, section, "additional_jump_speed", 0.0f); + m_additional_run_coef = READ_IF_EXISTS(pSettings, r_float, section, "additional_run_coef", 0.0f); + m_additional_sprint_koef = READ_IF_EXISTS(pSettings, r_float, section, "additional_sprint_koef", 0.0f); + + if (pSettings->line_exist(section, "nightvision_sect")) + m_NightVisionSect = pSettings->r_string(section, "nightvision_sect"); + else + m_NightVisionSect = NULL; + + block_pnv_slot = READ_IF_EXISTS(pSettings, r_u32, section, "block_pnv_slot", 0); + block_helmet_slot = READ_IF_EXISTS(pSettings, r_u32, section, "block_helmet_slot", 0); +/* + m_bNightVisionEnabled = !!pSettings->r_bool(section,"night_vision"); + if(m_bNightVisionEnabled) + { + HUD_SOUND_ITEM::LoadSound(section,"snd_night_vision_on" , m_NightVisionOnSnd , SOUND_TYPE_ITEM_USING); + HUD_SOUND_ITEM::LoadSound(section,"snd_night_vision_off" , m_NightVisionOffSnd , SOUND_TYPE_ITEM_USING); + HUD_SOUND_ITEM::LoadSound(section,"snd_night_vision_idle", m_NightVisionIdleSnd , SOUND_TYPE_ITEM_USING); + HUD_SOUND_ITEM::LoadSound(section,"snd_night_vision_broken", m_NightVisionBrokenSnd, SOUND_TYPE_ITEM_USING); + } +*/ + m_full_icon_name = pSettings->r_string(section,"full_icon_name"); +} + +//void CCustomOutfit::SwitchNightVision() +//{ +// if (OnClient()) return; +// SwitchNightVision(!m_bNightVisionOn); +//} + +//void CCustomOutfit::SwitchNightVision(bool vision_on) +//{ +// if(!m_bNightVisionEnabled) return; +// +// m_bNightVisionOn = vision_on; +// +// CActor *pA = smart_cast(H_Parent()); +// +// if(!pA) return; +// bool bPlaySoundFirstPerson = (pA == Level().CurrentViewEntity()); +// +// LPCSTR disabled_names = pSettings->r_string(cNameSect(),"disabled_maps"); +// LPCSTR curr_map = *Level().name(); +// u32 cnt = _GetItemCount(disabled_names); +// bool b_allow = true; +// string512 tmp; +// for(u32 i=0; iPosition(), pA, bPlaySoundFirstPerson); +// return; +// } +// +// if(m_bNightVisionOn) +// { +// CEffectorPP* pp = pA->Cameras().GetPPEffector((EEffectorPPType)effNightvision); +// if(!pp) +// { +// if (m_NightVisionSect.size()) +// { +// AddEffector(pA,effNightvision, m_NightVisionSect); +// HUD_SOUND_ITEM::PlaySound(m_NightVisionOnSnd, pA->Position(), pA, bPlaySoundFirstPerson); +// HUD_SOUND_ITEM::PlaySound(m_NightVisionIdleSnd, pA->Position(), pA, bPlaySoundFirstPerson, true); +// } +// } +// } else { +// CEffectorPP* pp = pA->Cameras().GetPPEffector((EEffectorPPType)effNightvision); +// if(pp) +// { +// pp->Stop (1.0f); +// HUD_SOUND_ITEM::PlaySound(m_NightVisionOffSnd, pA->Position(), pA, bPlaySoundFirstPerson); +// HUD_SOUND_ITEM::StopSound(m_NightVisionIdleSnd); +// } +// } +//} + +void CCustomOutfit::net_Destroy() +{ + //SwitchNightVision (false); + + inherited::net_Destroy (); +} + +BOOL CCustomOutfit::net_Spawn(CSE_Abstract* DC) +{ + //SwitchNightVision (false); + return inherited::net_Spawn(DC); +} + +void CCustomOutfit::OnH_B_Independent (bool just_before_destroy) +{ + inherited::OnH_B_Independent (just_before_destroy); + + /*SwitchNightVision (false); + + HUD_SOUND_ITEM::StopSound (m_NightVisionOnSnd); + HUD_SOUND_ITEM::StopSound (m_NightVisionOffSnd); + HUD_SOUND_ITEM::StopSound (m_NightVisionIdleSnd);*/ +} + +void CCustomOutfit::OnMoveToSlot () +{ + inherited::OnMoveToSlot(); + + if (m_pCurrentInventory) + { + CActor* pActor = smart_cast (m_pCurrentInventory->GetOwner()); + if (pActor) + { + //SwitchNightVision(false); + + if (pActor->IsFirstEye() && IsGameTypeSingle() && !pActor->IsActorShadowsOn()) + { + if (m_ActorVisual_legs.size()) + { + shared_str NewVisual = m_ActorVisual_legs; + pActor->ChangeVisual(NewVisual); + + if (pActor == Level().CurrentViewEntity()) + g_player_hud->load(pSettings->r_string(cNameSect(),"player_hud_section")); + + } else { + shared_str NewVisual = pActor->GetDefaultVisualOutfit_legs(); + pActor->ChangeVisual(NewVisual); + + if (pActor == Level().CurrentViewEntity()) + g_player_hud->load_default(); + } + } else { + if (m_ActorVisual.size()) + { + shared_str NewVisual = NULL; + char* TeamSection = Game().getTeamSection(pActor->g_Team()); + if (TeamSection) + { + if (pSettings->line_exist(TeamSection, *cNameSect())) + { + NewVisual = pSettings->r_string(TeamSection, *cNameSect()); + string256 SkinName; + xr_strcpy(SkinName, pSettings->r_string("mp_skins_path", "skin_path")); + xr_strcat(SkinName, *NewVisual); + xr_strcat(SkinName, ".ogf"); + NewVisual._set(SkinName); + } + } + + if (!NewVisual.size()) + NewVisual = m_ActorVisual; + + pActor->ChangeVisual(NewVisual); + + if (pActor == Level().CurrentViewEntity()) + g_player_hud->load(pSettings->r_string(cNameSect(),"player_hud_section")); + + } else { + shared_str NewVisual = pActor->GetDefaultVisualOutfit(); + pActor->ChangeVisual(NewVisual); + + if (pActor == Level().CurrentViewEntity()) + g_player_hud->load_default(); + } + } + } + } +}; + +void CCustomOutfit::OnMoveToRuck () +{ + inherited::OnMoveToRuck(); + + if (m_pCurrentInventory) + { + CActor* pActor = smart_cast (m_pCurrentInventory->GetOwner()); + + if (pActor) + { + CCustomOutfit* outfit = pActor->GetOutfit(); + if (!outfit) + { + //pActor->SwitchNightVision(); + + if (pActor->IsFirstEye() && IsGameTypeSingle()) + { + shared_str DefVisual = pActor->GetDefaultVisualOutfit_legs(); + if (DefVisual.size()) + { + pActor->ChangeVisual(DefVisual); + } + } else { + shared_str DefVisual = pActor->GetDefaultVisualOutfit(); + if (DefVisual.size()) + { + pActor->ChangeVisual(DefVisual); + } + } + + if (pActor == Level().CurrentViewEntity()) + g_player_hud->load_default(); + + } + } + } +}; + +u32 CCustomOutfit::ef_equipment_type () const +{ + return (m_ef_equipment_type); +} + +bool CCustomOutfit::install_upgrade_impl( LPCSTR section, bool test ) +{ + bool result = inherited::install_upgrade_impl( section, test ); + + LPCSTR str; + bool result2 = process_if_exists_set( section, "nightvision_sect", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + m_NightVisionSect._set( str ); + } + result |= result2; + + result |= process_if_exists( section, "additional_inventory_weight", &CInifile::r_float, m_additional_weight, test ); + result |= process_if_exists( section, "additional_inventory_weight2", &CInifile::r_float, m_additional_weight2, test ); + + result |= process_if_exists(section, "block_pnv_slot", &CInifile::r_u32, block_pnv_slot, test); + result |= process_if_exists(section, "block_helmet_slot", &CInifile::r_u32, block_helmet_slot, test); + + result |= process_if_exists(section, "additional_jump_speed", &CInifile::r_float, m_additional_jump_speed, test); + result |= process_if_exists(section, "additional_run_coef", &CInifile::r_float, m_additional_run_coef, test); + result |= process_if_exists(section, "additional_sprint_koef", &CInifile::r_float, m_additional_sprint_koef, test); + //result |= process_if_exists( section, "artefact_count", &CInifile::r_u32, m_artefact_count, test ); + //clamp( m_artefact_count, (u32)0, (u32)5 ); + + return result; +} diff --git a/src/xrGameLA/CustomOutfit.h b/src/xrGameLA/CustomOutfit.h new file mode 100644 index 000000000..f7bd4aa00 --- /dev/null +++ b/src/xrGameLA/CustomOutfit.h @@ -0,0 +1,65 @@ +#pragma once + +#include "OutfitBase.h" +#include "hudsound.h" + +struct SBoneProtections; + +class CCustomOutfit: public COutfitBase { +private: + typedef COutfitBase inherited; +public: + CCustomOutfit (void); + virtual ~CCustomOutfit (void); + + virtual void Load (LPCSTR section); + + virtual void OnMoveToSlot (); + virtual void OnMoveToRuck (); + + /* + void SwitchNightVision (); + void SwitchNightVision (bool light_on); + IC bool GetNightVisionStatus () const { return m_bNightVisionOn; } + */ +protected: + shared_str m_ActorVisual; + shared_str m_ActorVisual_legs; + shared_str m_full_icon_name; + +protected: + u32 m_ef_equipment_type; + + bool m_bNightVisionEnabled; + bool m_bNightVisionOn; + /* + HUD_SOUND_ITEM m_NightVisionOnSnd; + HUD_SOUND_ITEM m_NightVisionOffSnd; + HUD_SOUND_ITEM m_NightVisionIdleSnd; + HUD_SOUND_ITEM m_NightVisionBrokenSnd; + */ +public: + //tatarinrafa: added additional jump speed sprint speed walk speed + float m_additional_jump_speed; + float m_additional_run_coef; + float m_additional_sprint_koef; + + //-- + u32 block_pnv_slot; + u32 block_helmet_slot; + float m_additional_weight; + float m_additional_weight2; + shared_str m_NightVisionSect; + virtual u32 ef_equipment_type () const; + const shared_str& GetFullIconName () const {return m_full_icon_name;}; + + virtual void net_Export (NET_Packet& P); + virtual void net_Import (NET_Packet& P); + + virtual void net_Destroy (); + virtual BOOL net_Spawn (CSE_Abstract* DC); + + virtual void OnH_B_Independent (bool just_before_destroy); +protected: + virtual bool install_upgrade_impl( LPCSTR section, bool test ); +}; diff --git a/src/xrGameLA/CustomRocket.cpp b/src/xrGameLA/CustomRocket.cpp new file mode 100644 index 000000000..c9832a86d --- /dev/null +++ b/src/xrGameLA/CustomRocket.cpp @@ -0,0 +1,643 @@ +////////////////////////////////////////////////////////////////////// +// CustomRocket.cpp: ракета, которой стреляет RocketLauncher +// (умеет лететь, светиться и отыгрывать партиклы) +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "customrocket.h" +#include "ParticlesObject.h" +#include "PhysicsShell.h" +#include "extendedgeom.h" +#include "level.h" +#include "xrMessages.h" +#include "../gamemtllib.h" +#include "tri-colliderknoopc/dTriList.h" +#include "../../Include/xrRender/RenderVisual.h" +#include "CalculateTriangle.h" +#include "actor.h" +#ifdef DEBUG +#include "PHDebug.h" +#include "game_base_space.h" +#endif + +#define CHOOSE_MAX(x,inst_x,y,inst_y,z,inst_z)\ + if(x>y)\ + if(x>z){inst_x;}\ + else{inst_z;}\ + else\ + if(y>z){inst_y;}\ + else{inst_z;} +CCustomRocket::CCustomRocket() +{ + m_eState = eInactive; + m_bEnginePresent = false; + m_bStopLightsWithEngine = true; + m_bLightsEnabled = false; + + + m_vPrevVel.set (0,0,0); + + m_pTrailLight = NULL; + m_LaunchXForm.identity (); + m_vLaunchVelocity.set (0,0,0); + m_vLaunchAngularVelocity.set(0,0,0); + m_bLaunched = false; +} + +CCustomRocket::~CCustomRocket () +{ + m_pTrailLight.destroy (); +} + + +void CCustomRocket::reinit () +{ + inherited::reinit (); + + m_pTrailLight.destroy (); + m_pTrailLight = ::Render->light_create(); + m_pTrailLight->set_shadow (true); + + m_pEngineParticles = NULL; + m_pFlyParticles = NULL; + + m_pOwner = NULL; + + m_vPrevVel.set(0,0,0); +} + + +BOOL CCustomRocket::net_Spawn(CSE_Abstract* DC) +{ + m_eState = eInactive; + BOOL result = inherited::net_Spawn(DC); + m_LaunchXForm.set(XFORM()); + return result; +} + +void CCustomRocket::net_Destroy() +{ +// Msg("---------net_Destroy [%d] frame[%d]",ID(), Device.dwFrame); + CPHUpdateObject::Deactivate(); + inherited::net_Destroy(); + + + StopEngine(); + StopFlying(); +} + + +void CCustomRocket::SetLaunchParams (const Fmatrix& xform, + const Fvector& vel, + const Fvector& angular_vel) +{ + VERIFY2 (_valid(xform),"SetLaunchParams. Invalid xform argument!"); + m_LaunchXForm = xform; + m_vLaunchVelocity = vel; +// if(m_pOwner->ID()==Actor()->ID()) +// { +// Msg("set p start v: %f,%f,%f \n",m_vLaunchVelocity.x,m_vLaunchVelocity.y,m_vLaunchVelocity.z); +// } + m_vLaunchAngularVelocity = angular_vel; + m_time_to_explode = Device.fTimeGlobal + pSettings->r_float(cNameSect(), "force_explode_time")/1000.0f; +#ifdef DEBUG + gbg_rocket_speed1=0; + gbg_rocket_speed2=0; +#endif +} + + +void CCustomRocket::activate_physic_shell () +{ + R_ASSERT(H_Parent()); + R_ASSERT(!m_pPhysicsShell); + create_physic_shell(); + + R_ASSERT(m_pPhysicsShell); + if( m_pPhysicsShell->isActive()) + return; + VERIFY2(_valid(m_LaunchXForm),"CCustomRocket::activate_physic_shell. Invalid m_LaunchXForm!"); + +// if(m_pOwner->ID()==Actor()->ID()) +// { +// Msg("start v: %f,%f,%f \n",m_vLaunchVelocity.x,m_vLaunchVelocity.y,m_vLaunchVelocity.z); +// } + m_pPhysicsShell->Activate(m_LaunchXForm, m_vLaunchVelocity, m_vLaunchAngularVelocity); + m_pPhysicsShell->Update (); + + XFORM().set(m_pPhysicsShell->mXFORM); + Position().set(m_pPhysicsShell->mXFORM.c); + m_pPhysicsShell->set_PhysicsRefObject(this); + m_pPhysicsShell->set_ObjectContactCallback(ObjectContactCallback); + m_pPhysicsShell->set_ContactCallback(NULL); + m_pPhysicsShell->SetAirResistance(0.f,0.f); + m_pPhysicsShell->set_DynamicScales(1.f,1.f); + m_pPhysicsShell->SetAllGeomTraced(); +} + +void CCustomRocket::create_physic_shell () +{ + R_ASSERT(!m_pPhysicsShell); + Fobb obb; + Visual()->getVisData().box.get_CD (obb.m_translate,obb.m_halfsize); + obb.m_rotate.identity (); + + // Physics (Elements) + CPhysicsElement *E = P_create_Element (); + R_ASSERT (E); + + Fvector ax; + float radius; + CHOOSE_MAX( + obb.m_halfsize.x,ax.set(obb.m_rotate.i) ; ax.mul(obb.m_halfsize.x); radius=_min(obb.m_halfsize.y,obb.m_halfsize.z) ;obb.m_halfsize.y/=2.f;obb.m_halfsize.z/=2.f, + obb.m_halfsize.y,ax.set(obb.m_rotate.j) ; ax.mul(obb.m_halfsize.y); radius=_min(obb.m_halfsize.x,obb.m_halfsize.z) ;obb.m_halfsize.x/=2.f;obb.m_halfsize.z/=2.f, + obb.m_halfsize.z,ax.set(obb.m_rotate.k) ; ax.mul(obb.m_halfsize.z); radius=_min(obb.m_halfsize.y,obb.m_halfsize.x) ;obb.m_halfsize.y/=2.f;obb.m_halfsize.x/=2.f + ) + //radius*=1.4142f; + Fsphere sphere1,sphere2; + sphere1.P.add (obb.m_translate,ax); + sphere1.R =radius*1.4142f; + + sphere2.P.sub (obb.m_translate,ax); + sphere2.R =radius/2.f; + + E->add_Box (obb); + E->add_Sphere (sphere1); + E->add_Sphere (sphere2); + + // Physics (Shell) + m_pPhysicsShell = P_create_Shell (); + R_ASSERT (m_pPhysicsShell); + m_pPhysicsShell->add_Element (E); + m_pPhysicsShell->setMass (7.f); + m_pPhysicsShell->SetAirResistance(); +} + +////////////////////////////////////////////////////////////////////////// +// Rocket specific functions +////////////////////////////////////////////////////////////////////////// + + +void CCustomRocket::ObjectContactCallback(bool& do_colide,bool bo1,dContact& c ,SGameMtl * material_1,SGameMtl * material_2) +{ + do_colide = false; + + + dxGeomUserData *l_pUD1 = NULL; + dxGeomUserData *l_pUD2 = NULL; + l_pUD1 = retrieveGeomUserData(c.geom.g1); + l_pUD2 = retrieveGeomUserData(c.geom.g2); + + SGameMtl* material=0; + CCustomRocket *l_this = l_pUD1 ? smart_cast(l_pUD1->ph_ref_object) : NULL; + Fvector vUp; + if(!l_this){ + l_this = l_pUD2 ? smart_cast(l_pUD2->ph_ref_object) : NULL; + vUp.invert(*(Fvector*)&c.geom.normal); + + //if(dGeomGetClass(c.geom.g1)==dTriListClass) + // material=GMLib.GetMaterialByIdx((u16)c.surface.mode); + //else + // material=GMLib.GetMaterialByIdx(l_pUD2->material); + material=material_1; + + }else{ + vUp.set(*(Fvector*)&c.geom.normal); + + //if(dGeomGetClass(c.geom.g2)==dTriListClass) + // material=GMLib.GetMaterialByIdx((u16)c.surface.mode); + //else + // material=GMLib.GetMaterialByIdx(l_pUD1->material); + material=material_2; + + } + VERIFY(material); + if(material->Flags.is(SGameMtl::flPassable)) return; + + if(!l_this||l_this->m_contact.contact) return; + + CGameObject *l_pOwner = l_pUD1 ? smart_cast(l_pUD1->ph_ref_object) : NULL; + if(!l_pOwner || l_pOwner == (CGameObject*)l_this) l_pOwner = l_pUD2 ? smart_cast(l_pUD2->ph_ref_object) : NULL; + if(!l_pOwner || l_pOwner != l_this->m_pOwner) + { + if(l_this->m_pOwner) + { + Fvector l_pos; l_pos.set(l_this->Position()); +#ifdef DEBUG + bool corrected_pos=false; +#endif + if(!l_pUD1||!l_pUD2) + { + dGeomID g =NULL ; + dxGeomUserData *&l_pUD = l_pUD1?l_pUD1:l_pUD2; + if(l_pUD1) g=c.geom.g1;else g=c.geom.g2; + + if(l_pUD->pushing_neg) + { + Fvector velocity; + l_this->PHGetLinearVell(velocity); + if (velocity.square_magnitude() > EPS) + { //. desync? + velocity.normalize(); + Triangle neg_tri; + CalculateTriangle(l_pUD->neg_tri,g,neg_tri); + float cosinus=velocity.dotproduct(*((Fvector*)neg_tri.norm)); + VERIFY(_valid(neg_tri.dist)); + float dist=neg_tri.dist/cosinus; + velocity.mul(dist*1.1f); + l_pos.sub(velocity); +#ifdef DEBUG + corrected_pos=true; +//. DBG_OpenCashedDraw(); +//. const Fvector* V_array = Level().ObjectSpace.GetStaticVerts(); +//. DBG_DrawTri(neg_tri.T, V_array, D3DCOLOR_XRGB(255,255,0)); +//. DBG_ClosedCashedDraw(50000); +#endif + } + } + } +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawExplosionPos)) + DBG_DrawPoint(l_pos,0.05f,D3DCOLOR_XRGB(255,255,(!corrected_pos)*255)); +#endif + + l_this->Contact(l_pos, vUp); + l_this->m_pPhysicsShell->DisableCollision(); + l_this->m_pPhysicsShell->set_LinearVel(Fvector().set(0,0,0)); + l_this->m_pPhysicsShell->set_AngularVel(Fvector().set(0,0,0)); + l_this->m_pPhysicsShell->setForce(Fvector().set(0,0,0)); + l_this->m_pPhysicsShell->setTorque(Fvector().set(0,0,0)); + l_this->m_pPhysicsShell->set_ApplyByGravity(false); + l_this->setEnabled(FALSE); + + } + } else {} +} + +void CCustomRocket::Load(LPCSTR section) +{ + inherited::Load (section); + + reload(section); +} + +void CCustomRocket::reload (LPCSTR section) +{ + inherited::reload (section); + m_eState = eInactive; + + m_bEnginePresent = !!pSettings->r_bool(section, "engine_present"); + if(m_bEnginePresent) + { + m_dwEngineWorkTime = pSettings->r_u32(section, "engine_work_time"); + m_fEngineImpulse = pSettings->r_float(section, "engine_impulse"); + m_fEngineImpulseUp = pSettings->r_float(section, "engine_impulse_up"); + } + + + m_bLightsEnabled = !!pSettings->r_bool(section, "lights_enabled"); + if(m_bLightsEnabled) + { + sscanf(pSettings->r_string(section,"trail_light_color"), "%f,%f,%f", + &m_TrailLightColor.r, &m_TrailLightColor.g, &m_TrailLightColor.b); + m_fTrailLightRange = pSettings->r_float(section,"trail_light_range"); + } + + if(pSettings->line_exist(section,"engine_particles")) + m_sEngineParticles = pSettings->r_string(section,"engine_particles"); + if(pSettings->line_exist(section,"fly_particles")) + m_sFlyParticles = pSettings->r_string(section,"fly_particles"); + + if(pSettings->line_exist(section,"snd_fly_sound")){ + m_flyingSound.create(pSettings->r_string(section,"snd_fly_sound"),st_Effect,sg_SourceType); + } + + +} + +void CCustomRocket::Contact(const Fvector &pos, const Fvector &normal) +{ +m_contact.contact=true; +m_contact.pos.set(pos); +m_contact.up.set(normal); +} +void CCustomRocket::PlayContact() +{ + + if(!m_contact.contact)return; + if(eCollide == m_eState) return; + + StopEngine(); + StopFlying(); + + + m_eState = eCollide; + + //дективировать физическую оболочку,чтоб ракета не летела дальше + if(m_pPhysicsShell) + { + m_pPhysicsShell->set_LinearVel(zero_vel); + m_pPhysicsShell->set_AngularVel(zero_vel); + m_pPhysicsShell->set_ObjectContactCallback(NULL); + m_pPhysicsShell->Disable(); + } +// if (OnClient()) return; + + Position().set(m_contact.pos); + m_contact.contact=false; +} + + +void CCustomRocket::OnH_B_Chield () +{ + VERIFY(m_eState == eInactive); + inherited::OnH_B_Chield (); +// Msg("! CCustomRocket::OnH_B_Chield called, id[%d] frame[%d]",ID(),Device.dwFrame); +} +void CCustomRocket::OnH_A_Chield () +{ + VERIFY(m_eState == eInactive); + inherited::OnH_A_Chield (); +// Msg("! CCustomRocket::OnH_A_Chield called, id[%d] frame[%d]",ID(),Device.dwFrame); +} + + +void CCustomRocket::OnH_B_Independent(bool just_before_destroy) +{ + inherited::OnH_B_Independent(just_before_destroy); + //------------------------------------------- + m_pOwner = H_Parent() ? smart_cast(H_Parent()->H_Root()) : NULL; + //------------------------------------------- +} + + +void CCustomRocket::OnH_A_Independent() +{ + inherited::OnH_A_Independent(); + + if(!g_pGameLevel->bReady || !m_bLaunched) return; + setVisible (true); + StartFlying (); + StartEngine (); +// Msg("! CCustomRocket::OnH_A_Independent called, id[%d] frame[%d]",ID(),Device.dwFrame); + +} + + +void CCustomRocket::UpdateCL() +{ + inherited::UpdateCL(); + + PlayContact(); + switch (m_eState) + { + case eInactive: + break; + //состояния eEngine и eFlying отличаются, тем + //что вызывается UpdateEngine у eEngine, остальные + //функции общие + case eEngine: + UpdateEngine(); + case eFlying: + UpdateLights(); + UpdateParticles(); + break; + } + if(m_eState==eEngine || m_eState==eFlying ) + { + if(m_time_to_explodeapplyImpulse(l_dir,(1.f+k_back)*force); + m_pPhysicsShell->get_LinearVel(l_dir); + l_dir.normalize_safe(); + l_dir.invert(); + m_pPhysicsShell->applyImpulseTrace(l_pos, l_dir, force); + l_dir.set(0, 1.f, 0); + force = m_fEngineImpulseUp*fixed_step;// * Device.fTimeDelta; + m_pPhysicsShell->applyImpulse(l_dir, force); + + + //m_pPhysicsShell->set_AngularVel() +} + + +void CCustomRocket::UpdateEngine () +{ + // VERIFY( getVisible() ); + // VERIFY( m_pPhysicsShell); + if( !m_pPhysicsShell ) + Msg("! CCustomRocket::UpdateEngine called, but m_pPhysicsShell is NULL"); + + if( !getVisible() ){ + Msg("! CCustomRocket::UpdateEngine called, but false==getVisible() id[%d] frame[%d]",ID(),Device.dwFrame); + } + + if (m_dwEngineTime <= 0) + { + + StopEngine(); + return; + } + + m_dwEngineTime -= Device.dwTimeDelta; +} + + + +////////////////////////////////////////////////////////////////////////// +// Lights +////////////////////////////////////////////////////////////////////////// +void CCustomRocket::StartLights() +{ + if(!m_bLightsEnabled) return; + + //включить световую подсветку от двигателя + m_pTrailLight->set_color(m_TrailLightColor.r, + m_TrailLightColor.g, + m_TrailLightColor.b); + + m_pTrailLight->set_range(m_fTrailLightRange); + m_pTrailLight->set_position(Position()); + m_pTrailLight->set_active(true); +} + +void CCustomRocket::StopLights() +{ + if(!m_bLightsEnabled) return; + m_pTrailLight->set_active(false); +} + +void CCustomRocket::UpdateLights() +{ + if(!m_bLightsEnabled || !m_pTrailLight->get_active()) return; + m_pTrailLight->set_position(Position()); +} + + +void CCustomRocket::PhDataUpdate (float step) +{ + +} +void CCustomRocket::PhTune (float step) +{ + UpdateEnginePh (); + +} + + + +////////////////////////////////////////////////////////////////////////// +// Particles +////////////////////////////////////////////////////////////////////////// + +void CCustomRocket::UpdateParticles() +{ + if(m_flyingSound._handle() && m_flyingSound._feedback()) + m_flyingSound.set_position( XFORM().c ); + + if(!m_pEngineParticles && !m_pFlyParticles) return; + + Fvector vel; + PHGetLinearVell(vel); + + vel.add(m_vPrevVel,vel); + vel.mul(0.5f); + m_vPrevVel.set(vel); + + Fmatrix particles_xform; + particles_xform.identity(); + particles_xform.k.set(XFORM().k); + particles_xform.k.mul(-1.f); + Fvector dir = particles_xform.k; + Fvector::generate_orthonormal_basis(particles_xform.k, + particles_xform.j, + particles_xform.i); + particles_xform.c.set (XFORM().c); + dir.normalize_safe (); //1m offset fake -( + particles_xform.c.add (dir); + + if(m_pEngineParticles) m_pEngineParticles->UpdateParent(particles_xform, vel); + if(m_pFlyParticles) m_pFlyParticles->UpdateParent(particles_xform, vel); +} + +void CCustomRocket::StartEngineParticles() +{ + VERIFY(m_pEngineParticles == NULL); + if(!m_sEngineParticles) return; + m_pEngineParticles = CParticlesObject::Create(*m_sEngineParticles,FALSE); + + UpdateParticles(); + m_pEngineParticles->Play(false); + + VERIFY(m_pEngineParticles); +} +void CCustomRocket::StopEngineParticles() +{ + if(m_pEngineParticles == NULL) return; + m_pEngineParticles->Stop(); + m_pEngineParticles->SetAutoRemove(true); + m_pEngineParticles = NULL; +} +void CCustomRocket::StartFlyParticles() +{ + if(m_flyingSound._handle()) + m_flyingSound.play_at_pos(0, XFORM().c, sm_Looped ); + + VERIFY(m_pFlyParticles == NULL); + + if(!m_sFlyParticles) return; + m_pFlyParticles = CParticlesObject::Create(*m_sFlyParticles,FALSE); + + UpdateParticles(); + m_pFlyParticles->Play(false); + + VERIFY(m_pFlyParticles); + VERIFY3(m_pFlyParticles->IsLooped(), "must be a looped particle system for rocket fly: %s", *m_sFlyParticles); +} +void CCustomRocket::StopFlyParticles() +{ + if(m_flyingSound._handle()) + m_flyingSound.stop(); + + if(m_pFlyParticles == NULL) return; + m_pFlyParticles->Stop(); + m_pFlyParticles->SetAutoRemove(true); + m_pFlyParticles = NULL; +} + +void CCustomRocket::StartFlying () +{ + StartFlyParticles(); + StartLights(); +} +void CCustomRocket::StopFlying () +{ + StopFlyParticles(); + StopLights(); +} + +void CCustomRocket::OnEvent(NET_Packet& P, u16 type) +{ + switch (type) + { + case GE_GRENADE_EXPLODE: + { + if (m_eState != eCollide && OnClient()) + { + CCustomRocket::Contact(Position(), Direction()); + }; + }break; + } + inherited::OnEvent(P,type); +}; \ No newline at end of file diff --git a/src/xrGameLA/CustomRocket.h b/src/xrGameLA/CustomRocket.h new file mode 100644 index 000000000..7238794b0 --- /dev/null +++ b/src/xrGameLA/CustomRocket.h @@ -0,0 +1,166 @@ +////////////////////////////////////////////////////////////////////// +// CustomRocket.h: ракета, которой стреляет RocketLauncher +// (умеет лететь, светиться и отыгрывать партиклы) +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "physic_item.h" +#include "PHObject.h" + +class CRocketLauncher; + +struct SRoketContact +{ + bool contact; + Fvector pos; + Fvector up; + SRoketContact() + { + contact = false; + } +}; + +class CCustomRocket : public CPhysicItem, + public CPHUpdateObject +{ +private: + typedef CPhysicItem inherited; + friend CRocketLauncher; +public: + ////////////////////////////////////////////////////////////////////////// + // Generic + ////////////////////////////////////////////////////////////////////////// + + CCustomRocket(void); + virtual ~CCustomRocket(void); + + virtual void Load(LPCSTR section); + virtual BOOL net_Spawn(CSE_Abstract* DC); + virtual void net_Destroy(); + virtual BOOL AlwaysTheCrow () { return TRUE; } + + virtual void reinit (); + virtual void reload (LPCSTR section); + + virtual void OnH_A_Independent (); + virtual void OnH_B_Independent (bool just_before_destroy); + virtual void OnH_B_Chield (); + virtual void OnH_A_Chield (); + virtual void UpdateCL(); + + virtual BOOL UsedAI_Locations () {return (FALSE);} + virtual bool Useful () const {return (m_eState == eInactive); } + + virtual void renderable_Render() {inherited::renderable_Render();} + + //создание физической оболочки + virtual void activate_physic_shell (); + virtual void create_physic_shell (); + + virtual void PhDataUpdate (float step); + virtual void PhTune (float step); + + ////////////////////////////////////////////////////////////////////////// + // Rocket Properties + ////////////////////////////////////////////////////////////////////////// +public: +#ifdef DEBUG + CGameObject* owner (){return m_pOwner;} +#endif + virtual void StartEngine (); + virtual void StopEngine (); + virtual void UpdateEngine (); + virtual void UpdateEnginePh (); + + virtual void StartFlying (); + virtual void StopFlying (); + + virtual void SetLaunchParams (const Fmatrix& xform, + const Fvector& vel, + const Fvector& angular_vel); + + virtual void OnEvent (NET_Packet& P, u16 type); + bool m_bLaunched; +protected: + //указатель на владельца RocketLauncher - который стреляет ракету + CGameObject* m_pOwner; + + SRoketContact m_contact; + //параметры которые задаются RocketLauncher-ом перед пуском + Fmatrix m_LaunchXForm; + Fvector m_vLaunchVelocity; + Fvector m_vLaunchAngularVelocity; + + enum ERocketState{ + eInactive, //ракета неактивна и находиться в инвентаре + eEngine, //включен двигатель + eFlying, //просто летим + eCollide //произошло столкновение с препятствием + }; + + //текущее состояние ракеты + ERocketState m_eState; + + //двигатель присутствует + bool m_bEnginePresent; + //время работы двигателя с момента старта + int m_dwEngineWorkTime; + //сила работы двигателя (размер импульса в секунду) + float m_fEngineImpulse; + float m_fEngineImpulseUp; + //текущее время работы двигателя + int m_dwEngineTime; + + //обработка столкновения + virtual void Contact(const Fvector &pos, const Fvector &normal); + void PlayContact(); + static void ObjectContactCallback(bool& do_colide,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/); + + + ////////////////////////////////////////////////////////////////////////// + // Lights + ////////////////////////////////////////////////////////////////////////// +protected: + //флаг, что подсветка может быть включена + bool m_bLightsEnabled; + //флаг, что подсветка будет остановлена + //вместе с двигателем + bool m_bStopLightsWithEngine; + //подсветка во время полета и работы двигателя + ref_light m_pTrailLight; + Fcolor m_TrailLightColor; + float m_fTrailLightRange; + ref_sound m_flyingSound; + +protected: + virtual void StartLights(); + virtual void StopLights(); + virtual void UpdateLights(); + + + ////////////////////////////////////////////////////////////////////////// + // Particles + ////////////////////////////////////////////////////////////////////////// +protected: + //имя партиклов двигателя + shared_str m_sEngineParticles; + CParticlesObject* m_pEngineParticles; + //имя партиклов полета + shared_str m_sFlyParticles; + CParticlesObject* m_pFlyParticles; + + Fvector m_vPrevVel; + float m_time_to_explode; +#ifdef DEBUG + float gbg_rocket_speed1; + float gbg_rocket_speed2; +#endif +protected: + virtual void StartEngineParticles(); + virtual void StopEngineParticles(); + virtual void StartFlyParticles(); + virtual void StopFlyParticles(); + + virtual void UpdateParticles(); +}; \ No newline at end of file diff --git a/src/xrGameLA/CustomTimer.cpp b/src/xrGameLA/CustomTimer.cpp new file mode 100644 index 000000000..44fb5723f --- /dev/null +++ b/src/xrGameLA/CustomTimer.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" +#include "CustomTimer.h" +#include "alife_space.h" +#include "level.h" + + +u64 generate_add_time (u32 days, u32 hours, u32 minutes, u32 seconds) +{ + u64 result = 0; + + result += u64(days); + result = result*u64(24) + u64(hours); + result = result*u64(60) + u64(minutes); + result = result*u64(60) + u64(seconds); + result *=u64(1000); + + return (result); +} + +CTimerCustom::CTimerCustom(CTimersManager *parent) + { + m_time = 0; + m_game_time = 0; + m_day = 0; + m_hour = 0; + m_min = 0; + m_sec = 0; + m_ms = 0; + m_flags.zero(); + m_parent = parent; + m_name = "@"; + m_action = ""; +} + +CTimerCustom::~CTimerCustom() +{ +} + +bool CTimerCustom::Prepare() +{ + if (isGameTimer() && !isHUD()) + { + PrepareGameTime(); + return true; + } + else + { + PrepareTime(); + return false; + } +} + +xrTime CTimerCustom::TimeObject() +{ + return xrTime(m_time); +} + +bool CTimerCustom::valide() +{ + if (isGameTimer() && !isHUD()) + return (m_game_time!=0); + else + return (m_time!=0 || m_day!=0 || m_hour!=0 || m_min!=0 || m_sec!=0 || m_ms!=0); +} + +xrTime CTimerCustom::TimeElapsed() +{ + xrTime timeElapsed(m_time); + xrTime time = get_time_struct(); + if (time < timeElapsed) + timeElapsed.sub(time); + else + timeElapsed.set(0,0,0,0,0,0,0); + return timeElapsed; +} + +void CTimerCustom::SetTimerType(bool value) +{ + m_flags.set(lmGameTimer, value); +} + +bool CTimerCustom::CheckTime(ALife::_TIME_ID time) +{ + if (!m_time) return false; + + if (time > m_time) return true; + return false; +} + +bool CTimerCustom::CheckGameTime() +{ + return (Device.dwTimeGlobal > m_game_time); +} + +void CTimerCustom::PrepareTime() +{ + ALife::_TIME_ID time_now = ai().get_alife() ? ai().alife().time().game_time() : Level().GetGameTime(); + + ALife::_TIME_ID time_plus = generate_add_time(m_day,m_hour,m_min,m_sec) + m_ms; + + time_now+=time_plus; + + m_time = time_now; +} + +void CTimerCustom::PrepareGameTime() +{ + m_game_time += Device.dwTimeGlobal; +} + + + +#include "CustomTimersManager.h" +void CTimerCustom::SetHUD(bool b) +{ + m_flags.set(lmHUD,b); + if (m_parent) m_parent->OnHud(this,b); +} + +void CTimerCustom::save (NET_Packet &stream) +{ + u32 m_save_game_time = m_game_time; + if (m_save_game_time > 0) + m_save_game_time -= Device.dwTimeGlobal; + + save_data(m_name, stream); + save_data(m_action, stream); + save_data(m_time, stream); + save_data(m_save_game_time, stream); + save_data(m_flags, stream); +} + +void CTimerCustom::load (IReader &stream) +{ + load_data(m_name, stream); + load_data(m_action, stream); + load_data(m_time, stream); + load_data(m_game_time, stream); + load_data(m_flags, stream); + if (isHUD()) m_parent->OnHud(this,true); +} + + + diff --git a/src/xrGameLA/CustomTimer.h b/src/xrGameLA/CustomTimer.h new file mode 100644 index 000000000..3bb0ba9c7 --- /dev/null +++ b/src/xrGameLA/CustomTimer.h @@ -0,0 +1,120 @@ +#pragma once +#ifdef USE_TIMERS_MANAGER +#include "pch_script.h" +#include "script_export_space.h" +#include "xr_time.h" +#include "date_time.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_time_manager.h" +#include "level.h" +#include "script_engine.h" +#include "object_broker.h" +#include "script_space_forward.h" + + +class CTimersManager; + + +class CTimerCustom : public IPureSerializeObject +{ + + public: + + CTimerCustom (CTimersManager *parent = 0); + virtual ~CTimerCustom (); + + + virtual void load (IReader& stream); + virtual void save (NET_Packet& stream); + + //Name + virtual void SetName (LPCSTR _name ) {m_name = _name;} + virtual LPCSTR Name () {return *m_name;} + + //Time + virtual void SetTimerType (bool value); + xrTime TimeObject (); + ALife::_TIME_ID Time () {return m_time;} + u32 GameTime () {return m_game_time;} + xrTime TimeElapsed (); + bool isGameTimer () const {return (m_flags.test(lmGameTimer)==1)?true:false;} + + + //Hud + virtual void SetHUD (bool b); + bool isHUD () {return (m_flags.test(lmHUD)==1)?true:false;} + + virtual void execute (LPCSTR action) { } + + virtual void SetAction (LPCSTR func) { m_action = func; } + LPCSTR Action () { return *m_action; } + + //valide + bool valide (); + bool CheckTime (ALife::_TIME_ID time); + bool CheckGameTime (); + + void SetParent (CTimersManager *parent) { m_parent = parent;} + + bool Prepare (); + private: + void PrepareTime (); + void PrepareGameTime (); + public: + + + u32 m_day,m_hour,m_min,m_sec,m_ms; + + protected: + + shared_str m_name; + shared_str m_action; + + + u32 m_game_time; + + + ALife::_TIME_ID m_time; + + + enum lm_flags + { + lmHUD = (1<<0), + lmRestart = (1<<1), + lmGameTimer = (1<<2), + }; + + flags8 m_flags; + CTimersManager* m_parent; + bool m_bGameTimer; + + public: + DECLARE_SCRIPT_REGISTER_FUNCTION; +}; + +class CTimerCustomWrapper : public CTimerCustom, public luabind::wrap_base +{ + public: + CTimerCustomWrapper (CTimersManager *parent = 0); + virtual ~CTimerCustomWrapper(); + virtual void load (IReader& stream); + virtual void save (NET_Packet& stream); + static void load_static (CTimerCustom* self, IReader& stream); + static void save_static (CTimerCustom* self, NET_Packet& stream); + + + + virtual void execute (LPCSTR action); + static void execute_static (CTimerCustom* self, LPCSTR action); + + + +}; + + +add_to_type_list(CTimerCustom) +#undef script_type_list +#define script_type_list save_type_list(CTimerCustom) + +#endif \ No newline at end of file diff --git a/src/xrGameLA/CustomTimer_script.cpp b/src/xrGameLA/CustomTimer_script.cpp new file mode 100644 index 000000000..41109feac --- /dev/null +++ b/src/xrGameLA/CustomTimer_script.cpp @@ -0,0 +1,83 @@ +#include "stdafx.h" +#include "CustomTimer.h" +#include "pch_script.h" +#include "actor.h" + +using namespace luabind; + +CTimerCustomWrapper::CTimerCustomWrapper(CTimersManager *parent) : + CTimerCustom(parent) +{ +} + +CTimerCustomWrapper::~CTimerCustomWrapper() +{ +} + +void CTimerCustomWrapper::load (IReader& stream) +{ + luabind::call_member (this, "load", stream); +} +void CTimerCustomWrapper::save (NET_Packet& stream) +{ + luabind::call_member (this, "save", stream); +} + +void CTimerCustomWrapper::load_static (CTimerCustom* self, IReader& stream) +{ + self->CTimerCustom::load (stream); +} + +void CTimerCustomWrapper::save_static (CTimerCustom* self, NET_Packet& stream) +{ + self->CTimerCustom::save (stream); +} + +void CTimerCustomWrapper::execute (LPCSTR action) +{ + luabind::call_member (this, "execute", action); +} + +void CTimerCustomWrapper::execute_static (CTimerCustom* self, LPCSTR action) +{ + self->CTimerCustom::execute (action); +} + + + +#pragma optimize("s",on) +void CTimerCustom::script_register(lua_State *L) +{ + module(L) + [ + class_("Timer") + .def(constructor<>()) + .def("SetName", &CTimerCustom::SetName) + .def("Name", &CTimerCustom::Name) + .def("SetTimerType", &CTimerCustom::SetTimerType) + .def("Time", &CTimerCustom::Time) + .def("isGameTimer", &CTimerCustom::isGameTimer) + .def("TimeElapsed", &CTimerCustom::TimeElapsed) + .def("SetAction", &CTimerCustom::SetAction) + + .def_readwrite("day", &CTimerCustom::m_day) + .def_readwrite("hour", &CTimerCustom::m_hour) + .def_readwrite("min", &CTimerCustom::m_min) + .def_readwrite("sec", &CTimerCustom::m_sec) + .def_readwrite("ms", &CTimerCustom::m_ms) + .def_readwrite("game_time", &CTimerCustom::m_game_time) + + .def("SetHUD", &CTimerCustom::SetHUD) + .def("isHUD", &CTimerCustom::isHUD) + .def("Valide", &CTimerCustom::valide) + + .def("save", &CTimerCustom::save, &CTimerCustomWrapper::save_static) + .def("load", &CTimerCustom::load, &CTimerCustomWrapper::load_static) + + .def("execute", &CTimerCustom::execute, &CTimerCustomWrapper::execute_static) + + + ]; +} + + diff --git a/src/xrGameLA/CustomTimersManager.cpp b/src/xrGameLA/CustomTimersManager.cpp new file mode 100644 index 000000000..03b6aac87 --- /dev/null +++ b/src/xrGameLA/CustomTimersManager.cpp @@ -0,0 +1,261 @@ +#include "stdafx.h" +#include "CustomTimersManager.h" +#include "ui\uistatic.h" +#include "profiler.h" + + +CTimersManager::CTimersManager() +{ + b_HUDTimerActive = false; + hud_timer = NULL; + b_GameLoaded = false; +} + +template +void delete_container(xr_vector& container) +{ + xr_vector::iterator I = container.begin(); + xr_vector::iterator E = container.end(); + for ( ; I != E; ++I) + { + try + { + xr_delete (*I); + } + catch(...) + { + *I = 0; + } + } + container.clear(); +} + +CTimersManager::~CTimersManager() +{ + delete_container(game_timers); + delete_container(timers); +} + +void CTimersManager::OnHud(CTimerCustom *t,bool b) +{ + if (b) { + if (!t) return; + if (IsAnyHUDTimerActive()) { + FATAL("Trying to add more than one HUD timer"); + } else { + b_HUDTimerActive = true; + ui_hud_timer = CurrentGameUI()->AddCustomStatic("hud_timer", true)->wnd(); + hud_timer = t; + } + } else { + if (IsAnyHUDTimerActive()) { + CurrentGameUI()->RemoveCustomStatic("hud_timer"); + ui_hud_timer = NULL; + b_HUDTimerActive = false; + hud_timer = NULL; + } + } +} + +bool CTimersManager::AddTimer (CTimerCustom *timer) +{ + + if (xr_strcmp(timer->Name(), "@") != 0 && TimerExist(timer->Name())) + { + Msg("! adding a timer with same name: %s", timer->Name()); + return false; + } + + bool b = timer->Prepare(); + timer->SetParent(this); + + if (timer->isHUD()) + OnHud(timers.back(), true); + + if (b) + { + game_timers.push_back(timer); + std::push_heap(game_timers.begin(), game_timers.end(), m_game_timer_pred); + } + else + { + timers.push_back(timer); + std::push_heap(timers.begin(), timers.end(), m_timer_pred); + } + + return true; +} + +void CTimersManager::RemoveTimer (LPCSTR name) +{ + TIMERS_IT it0 = std::find_if(timers.begin(), timers.end(), STimerPred(name)); + TIMERS_IT it1 = std::find_if(game_timers.begin(), game_timers.end(), STimerPred(name)); + + TIMERS_IT *it = (it0 != timers.end()) ? &it0 : ((it1 != game_timers.end()) ? &it1 : NULL); + + R_ASSERT3(it != NULL, "Can't find timer with name ", name); + + if ((**it)->isHUD()) + OnHud(NULL, false); + if (it0 != timers.end()) + timers.erase(it0); + if (it1 != game_timers.end()) + game_timers.erase(it1); + xr_delete(**it); + std::make_heap(timers.begin(), timers.end(), m_timer_pred); + std::make_heap(game_timers.begin(), game_timers.end(), m_game_timer_pred); +} + +CTimerCustom* CTimersManager::SearchTimer(LPCSTR name) +{ + TIMERS_IT it0 = std::find_if(timers.begin(), timers.end(), STimerPred(name)); + TIMERS_IT it1 = std::find_if(game_timers.begin(), game_timers.end(), STimerPred(name)); + + TIMERS_IT *it = (it0 != timers.end()) ? &it0 : ((it1 != game_timers.end()) ? &it1 : NULL); + + if (it) + return **it; + return NULL; +} + +CTimerCustom* CTimersManager::GetTimerByName(LPCSTR name) +{ + CTimerCustom* timer = SearchTimer(name); + R_ASSERT3(timer,"Can't find timer with name ",name); + return timer; +} + + +bool CTimersManager::TimerExist(LPCSTR name) +{ + return SearchTimer(name)!=NULL; +} + +void CTimersManager::save(IWriter &memory_stream) +{ + Msg ("* Saving timers..."); + + memory_stream.open_chunk (TIMERS_CHUNK_DATA); + memory_stream.w_u16(timers.size()); + NET_Packet packet; + packet.write_start(); + for (TIMERS_IT it = timers.begin(), it_end = timers.end(); it!=it_end; ++it) + (*it)->save(packet); + if (packet.B.count) + memory_stream.w(packet.B.data, packet.B.count); + + memory_stream.w_u16(game_timers.size()); + packet.write_start(); + for (TIMERS_IT it = game_timers.begin(), it_end = game_timers.end(); it!=it_end; ++it) + (*it)->save(packet); + if (packet.B.count) + memory_stream.w(packet.B.data, packet.B.count); + memory_stream.close_chunk (); + + + + Msg ("* %d timers successfully saved",game_timers.size() + timers.size()); +} + +void CTimersManager::load(IReader &file_stream) +{ + Msg ("* Loading timers..."); + + R_ASSERT2 (file_stream.find_chunk(TIMERS_CHUNK_DATA),"Can't find chunk TIMERS_CHUNK_DATA!"); + u16 size0 = file_stream.r_u16(); + for (int idx=0; idx(this); + CTimerCustom* timer = new CTimerCustom(this); + timer->load(file_stream); + to_register.push_back(timer); + } + + u16 size1 = file_stream.r_u16(); + for (int idx=0; idx(this); + CTimerCustom* timer = new CTimerCustom(this); + timer->load(file_stream); + to_register.push_back(timer); + } + Msg ("* %d timers successfully loaded", size0 + size1); +} + + +void CTimersManager::Update () +{ + ALife::_TIME_ID time_now = u64(0); + + if (!b_GameLoaded) + return; + START_PROFILE("ALife/Timers") + u64 start = CPU::QPC(); + time_now = ai().get_alife() ? ai().alife().time().game_time() : Level().GetGameTime(); + + if (!game_timers.empty()) + { + CTimerCustom *timer = game_timers.front(); + if (timer && timer->CheckGameTime()) + { + // + std::pop_heap(game_timers.begin(), game_timers.end(), m_game_timer_pred); + game_timers.pop_back(); + timer->execute(timer->Action()); + //xr_delete(timer); + } + } + + if (!timers.empty()) + { + CTimerCustom *timer = timers.front(); + if (timer && timer->CheckTime(time_now)) + { + //timer->execute(timer->Action()); + if (timer->isHUD()) + OnHud(NULL,false); + std::pop_heap(timers.begin(), timers.end(), m_timer_pred); + timers.pop_back(); + timer->execute(timer->Action()); + //xr_delete(timer); + } + } + + + if (b_GameLoaded) + { + while (!to_register.empty()) + { + CTimerCustom *tmp = to_register.back(); + to_register.pop_back(); + if (tmp->Prepare()) + { + game_timers.push_back(tmp); + std::push_heap(game_timers.begin(), game_timers.end(), m_game_timer_pred); + } + else + { + timers.push_back(tmp); + std::push_heap(timers.begin(), timers.end(), m_timer_pred); + } + } + } + + + if (hud_timer) + { + string64 str; + + ALife::_TIME_ID time_elapsed = hud_timer->Time() - time_now; + + u32 _years,_months,_days,_hours=0,_minutes=0,_seconds=0,_mseconds; + split_time(time_elapsed,_years,_months,_days,_hours,_minutes,_seconds,_mseconds); + + if (hud_timer->isGameTimer()) + sprintf(str,"%02d:%02d",_hours,_minutes); + else + sprintf(str,"%02d:%02d",_minutes,_seconds); + + ui_hud_timer->SetText(str); + } + STOP_PROFILE +} + diff --git a/src/xrGameLA/CustomTimersManager.h b/src/xrGameLA/CustomTimersManager.h new file mode 100644 index 000000000..fefa77c7d --- /dev/null +++ b/src/xrGameLA/CustomTimersManager.h @@ -0,0 +1,66 @@ +#pragma once +#ifdef USE_TIMERS_MANAGER +#include "script_export_space.h" +#include "CustomTimer.h" +#include "UIGameSP.h" + +class CTimersManager : public IPureSerializeObject +{ + private: + DEFINE_VECTOR(CTimerCustom*, TIMERS_STORAGE, TIMERS_IT); + public: + CTimersManager (); + virtual ~CTimersManager (); + + void Update (); + + virtual void save (IWriter &memory_stream); + virtual void load (IReader &file_stream); + + bool AddTimer (CTimerCustom *timer); + void RemoveTimer (LPCSTR name); + CTimerCustom* GetTimerByName (LPCSTR name); + bool TimerExist (LPCSTR name); + + void OnHud (CTimerCustom *t,bool b); + bool IsAnyHUDTimerActive () const { return b_HUDTimerActive; } + + void GameLoaded (bool val) { b_GameLoaded = val; } + bool IsGameLoaded () const { return b_GameLoaded; } + + private: + CTimerCustom* SearchTimer (LPCSTR name); + + public: + DECLARE_SCRIPT_REGISTER_FUNCTION; + protected: + struct STimerPred + { + private: + shared_str m_name; + public: + STimerPred(shared_str name) : m_name(name) {} + bool operator() (CTimerCustom *t) { return xr_strcmp(t->Name(), m_name) == 0; } + }; + struct SGameTimerHeapPred + { + public: + bool operator() (CTimerCustom *t0, CTimerCustom *t1) { return t0->GameTime() > t1->GameTime(); } + } m_game_timer_pred; + struct STimerHeapPred + { + public: + bool operator() (CTimerCustom *t0, CTimerCustom *t1) { return t0->Time() > t1->Time(); } + } m_timer_pred; + protected: + TIMERS_STORAGE game_timers, timers, to_register; + CTimerCustom *hud_timer; + CUIStatic *ui_hud_timer; + bool b_HUDTimerActive; + bool b_GameLoaded; +}; +add_to_type_list(CTimersManager) +#undef script_type_list +#define script_type_list save_type_list(CTimersManager) + +#endif \ No newline at end of file diff --git a/src/xrGameLA/CustomTimersManager_script.cpp b/src/xrGameLA/CustomTimersManager_script.cpp new file mode 100644 index 000000000..0d3e95174 --- /dev/null +++ b/src/xrGameLA/CustomTimersManager_script.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "CustomTimersManager.h" +#include "pch_script.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CTimersManager::script_register(lua_State *L) +{ + module(L) + [ + + class_("TimerManager") + .def(constructor<>()) + .def("AddTimer", &CTimersManager::AddTimer) + .def("RemoveTimer", &CTimersManager::RemoveTimer) + .def("GetTimerByName", &CTimersManager::GetTimerByName) + .def("IsAnyHUDTimerActive", &CTimersManager::IsAnyHUDTimerActive) + .def("TimerExist", &CTimersManager::TimerExist) + ]; +} \ No newline at end of file diff --git a/src/xrGameLA/CustomZone.cpp b/src/xrGameLA/CustomZone.cpp new file mode 100644 index 000000000..40de433df --- /dev/null +++ b/src/xrGameLA/CustomZone.cpp @@ -0,0 +1,1549 @@ +#include "stdafx.h" +#include "../xr_ioconsole.h" +#include "customzone.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "actor.h" +#include "hudmanager.h" +#include "ParticlesObject.h" +#include "xrserver_objects_alife_monsters.h" +#include "../LightAnimLibrary.h" +#include "level.h" +#include "game_cl_base.h" +#include "../igame_persistent.h" +#include "artifact.h" +#include "ai_object_location.h" +#include "../../Include/xrRender/Kinematics.h" +#include "zone_effector.h" +#include "breakableobject.h" +#include "../xr_collide_form.h" +#include "Car.h" + +////////////////////////////////////////////////////////////////////////// +#define PREFETCHED_ARTEFACTS_NUM 1 //количество предварительно проспавненых артефактов +#define WIND_RADIUS (4*Radius()) //расстояние до актера, когда появляется ветер +#define FASTMODE_DISTANCE (50.f) //distance to camera from sphere, when zone switches to fast update sequence + +CCustomZone::CCustomZone(void) +{ + m_zone_flags.zero (); + + m_fMaxPower = 100.f; + m_fAttenuation = 1.f; + m_dwPeriod = 1100; + m_fEffectiveRadius = 0.75f; + m_bZoneActive = false; + m_eHitTypeBlowout = ALife::eHitTypeWound; + m_pLocalActor = NULL; + m_pIdleParticles = NULL; + m_pLight = NULL; + m_pIdleLight = NULL; + m_pIdleLAnim = NULL; + + + m_StateTime.resize(eZoneStateMax); + for(int i=0; ir_s32(section, "disable_time"); + m_iDisableHitTimeSmall = pSettings->r_s32(section, "disable_time_small"); + m_iDisableIdleTime = pSettings->r_s32(section, "disable_idle_time"); + m_fHitImpulseScale = pSettings->r_float(section, "hit_impulse_scale"); + m_fEffectiveRadius = pSettings->r_float(section, "effective_radius"); + + m_fMaxPower = READ_IF_EXISTS(pSettings, r_float, section, "hit_power", 1.f); + + m_eHitTypeBlowout = ALife::g_tfString2HitType(pSettings->r_string(section, "hit_type")); + + m_zone_flags.set(eIgnoreNonAlive, pSettings->r_bool(section, "ignore_nonalive")); + m_zone_flags.set(eIgnoreSmall, pSettings->r_bool(section, "ignore_small")); + m_zone_flags.set(eIgnoreArtefact, pSettings->r_bool(section, "ignore_artefacts")); + m_zone_flags.set(eVisibleByDetector,pSettings->r_bool(section, "visible_by_detector")); + + + + + //загрузить времена для зоны + m_StateTime[eZoneStateIdle] = -1; + m_StateTime[eZoneStateAwaking] = pSettings->r_s32(section, "awaking_time"); + m_StateTime[eZoneStateBlowout] = pSettings->r_s32(section, "blowout_time"); + m_StateTime[eZoneStateAccumulate] = pSettings->r_s32(section, "accamulate_time"); + +////////////////////////////////////////////////////////////////////////// + ISpatial* self = smart_cast (this); + if (self) self->spatial.type |= (STYPE_COLLIDEABLE|STYPE_SHAPE); +////////////////////////////////////////////////////////////////////////// + + LPCSTR sound_str = NULL; + + if(pSettings->line_exist(section,"idle_sound")) + { + sound_str = pSettings->r_string(section,"idle_sound"); + m_idle_sound.create(sound_str, st_Effect,sg_SourceType); + } + + if(pSettings->line_exist(section,"accum_sound")) + { + sound_str = pSettings->r_string(section,"accum_sound"); + m_accum_sound.create(sound_str, st_Effect,sg_SourceType); + } + if(pSettings->line_exist(section,"awake_sound")) + { + sound_str = pSettings->r_string(section,"awake_sound"); + m_awaking_sound.create(sound_str, st_Effect,sg_SourceType); + } + + if(pSettings->line_exist(section,"blowout_sound")) + { + sound_str = pSettings->r_string(section,"blowout_sound"); + m_blowout_sound.create(sound_str, st_Effect,sg_SourceType); + } + + + if(pSettings->line_exist(section,"hit_sound")) + { + sound_str = pSettings->r_string(section,"hit_sound"); + m_hit_sound.create(sound_str, st_Effect,sg_SourceType); + } + + if(pSettings->line_exist(section,"entrance_sound")) + { + sound_str = pSettings->r_string(section,"entrance_sound"); + m_entrance_sound.create(sound_str, st_Effect,sg_SourceType); + } + + + if(pSettings->line_exist(section,"idle_particles")) + m_sIdleParticles = pSettings->r_string(section,"idle_particles"); + if(pSettings->line_exist(section,"blowout_particles")) + m_sBlowoutParticles = pSettings->r_string(section,"blowout_particles"); + + if(pSettings->line_exist(section,"accum_particles")) + m_sAccumParticles = pSettings->r_string(section,"accum_particles"); + + if(pSettings->line_exist(section,"awake_particles")) + m_sAwakingParticles = pSettings->r_string(section,"awake_particles"); + + + if(pSettings->line_exist(section,"entrance_small_particles")) + m_sEntranceParticlesSmall = pSettings->r_string(section,"entrance_small_particles"); + if(pSettings->line_exist(section,"entrance_big_particles")) + m_sEntranceParticlesBig = pSettings->r_string(section,"entrance_big_particles"); + + if(pSettings->line_exist(section,"hit_small_particles")) + m_sHitParticlesSmall = pSettings->r_string(section,"hit_small_particles"); + if(pSettings->line_exist(section,"hit_big_particles")) + m_sHitParticlesBig = pSettings->r_string(section,"hit_big_particles"); + + if(pSettings->line_exist(section,"idle_small_particles")) + m_sIdleObjectParticlesBig = pSettings->r_string(section,"idle_big_particles"); + if(pSettings->line_exist(section,"idle_big_particles")) + m_sIdleObjectParticlesSmall = pSettings->r_string(section,"idle_small_particles"); + if(pSettings->line_exist(section,"idle_particles_dont_stop")) + m_bIdleObjectParticlesDontStop=pSettings->r_bool(section,"idle_particles_dont_stop"); + + if(pSettings->line_exist(section,"postprocess")) + { + m_effector = new CZoneEffector(); + m_effector->Load (pSettings->r_string(section,"postprocess")); + }; + + + + if(pSettings->line_exist(section,"blowout_particles_time")) + { + m_dwBlowoutParticlesTime = pSettings->r_u32(section,"blowout_particles_time"); + if (s32(m_dwBlowoutParticlesTime)>m_StateTime[eZoneStateBlowout]) { + m_dwBlowoutParticlesTime=m_StateTime[eZoneStateBlowout]; + Msg("! ERROR: invalid 'blowout_particles_time' in '%s'",section); + } + } + else + m_dwBlowoutParticlesTime = 0; + + if(pSettings->line_exist(section,"blowout_light_time")) + { + m_dwBlowoutLightTime = pSettings->r_u32(section,"blowout_light_time"); + if (s32(m_dwBlowoutLightTime)>m_StateTime[eZoneStateBlowout]) { + m_dwBlowoutLightTime=m_StateTime[eZoneStateBlowout]; + Msg("! ERROR: invalid 'blowout_light_time' in '%s'",section); + } + } + else + m_dwBlowoutLightTime = 0; + + if(pSettings->line_exist(section,"blowout_sound_time")) + { + m_dwBlowoutSoundTime = pSettings->r_u32(section,"blowout_sound_time"); + if (s32(m_dwBlowoutSoundTime)>m_StateTime[eZoneStateBlowout]) { + m_dwBlowoutSoundTime=m_StateTime[eZoneStateBlowout]; + Msg("! ERROR: invalid 'blowout_sound_time' in '%s'",section); + } + } + else + m_dwBlowoutSoundTime = 0; + + if(pSettings->line_exist(section,"blowout_explosion_time")) { + m_dwBlowoutExplosionTime = pSettings->r_u32(section,"blowout_explosion_time"); + if (s32(m_dwBlowoutExplosionTime)>m_StateTime[eZoneStateBlowout]) { + m_dwBlowoutExplosionTime=m_StateTime[eZoneStateBlowout]; + Msg("! ERROR: invalid 'blowout_explosion_time' in '%s'",section); + } + } + else + m_dwBlowoutExplosionTime = 0; + + m_zone_flags.set(eBlowoutWind, pSettings->r_bool(section,"blowout_wind")); + if( m_zone_flags.test(eBlowoutWind) ){ + m_dwBlowoutWindTimeStart = pSettings->r_u32(section,"blowout_wind_time_start"); + m_dwBlowoutWindTimePeak = pSettings->r_u32(section,"blowout_wind_time_peak"); + m_dwBlowoutWindTimeEnd = pSettings->r_u32(section,"blowout_wind_time_end"); + R_ASSERT(m_dwBlowoutWindTimeStart < m_dwBlowoutWindTimePeak); + R_ASSERT(m_dwBlowoutWindTimePeak < m_dwBlowoutWindTimeEnd); + + if((s32)m_dwBlowoutWindTimeEnd < m_StateTime[eZoneStateBlowout]){ + m_dwBlowoutWindTimeEnd =u32( m_StateTime[eZoneStateBlowout]-1); + Msg("! ERROR: invalid 'blowout_wind_time_end' in '%s'",section); + } + + + m_fBlowoutWindPowerMax = pSettings->r_float(section,"blowout_wind_power"); + } + + //загрузить параметры световой вспышки от взрыва + m_zone_flags.set(eBlowoutLight, pSettings->r_bool (section, "blowout_light")); + + if(m_zone_flags.test(eBlowoutLight) ){ + sscanf(pSettings->r_string(section,"light_color"), "%f,%f,%f", &m_LightColor.r, &m_LightColor.g, &m_LightColor.b); + m_fLightRange = pSettings->r_float(section,"light_range"); + m_fLightTime = pSettings->r_float(section,"light_time"); + m_fLightTimeLeft = 0; + + m_fLightHeight = pSettings->r_float(section,"light_height"); + } + + //загрузить параметры idle подсветки + m_zone_flags.set(eIdleLight, pSettings->r_bool (section, "idle_light")); + if( m_zone_flags.test(eIdleLight) ) + { + m_fIdleLightRange = pSettings->r_float(section,"idle_light_range"); + m_fIdleLightRangeDelta = pSettings->r_float(section,"idle_light_range_delta"); + LPCSTR light_anim = pSettings->r_string(section,"idle_light_anim"); + m_pIdleLAnim = LALib.FindItem(light_anim); + m_fIdleLightHeight = pSettings->r_float(section,"idle_light_height"); + } + + + //загрузить параметры для разбрасывания артефактов + m_zone_flags.set(eSpawnBlowoutArtefacts, pSettings->r_bool(section,"spawn_blowout_artefacts")); + if(m_zone_flags.test(eSpawnBlowoutArtefacts) ) + { + //Msg("spawn_blowout_artefacts, %s", section); + + m_fArtefactSpawnProbability = READ_IF_EXISTS(pSettings, r_float, section, "artefact_spawn_probability", 0.0); + m_iArtSpawnCicles = READ_IF_EXISTS(pSettings, r_u8, section, "artefact_spawn_cicles",2); + //m_fArtefactSpawnProbability = 1.0; + //m_fArtefactSpawnProbability = pSettings->r_float (section,"artefact_spawn_probability"); + if(pSettings->line_exist(section,"artefact_spawn_particles")) + m_sArtefactSpawnParticles = pSettings->r_string(section,"artefact_spawn_particles"); + else + m_sArtefactSpawnParticles = NULL; + + if(pSettings->line_exist(section,"artefact_born_sound")) + { + sound_str = pSettings->r_string(section,"artefact_born_sound"); + m_ArtefactBornSound.create(sound_str, st_Effect,sg_SourceType); + } + + m_fThrowOutPower = READ_IF_EXISTS(pSettings, r_float, section, "throw_out_power", 0.0); + //m_fThrowOutPower = pSettings->r_float (section, "throw_out_power"); + m_fArtefactSpawnHeight = READ_IF_EXISTS(pSettings, r_float, section, "artefact_spawn_height", 0.0); + //m_fArtefactSpawnHeight = pSettings->r_float (section, "artefact_spawn_height"); + + // LPCSTR l_caParameters = pSettings->r_string(section, "artefacts"); + LPCSTR l_caParameters = READ_IF_EXISTS(pSettings, r_string, section, "artefacts", ""); + u16 m_wItemCount = (u16)_GetItemCount(l_caParameters); + R_ASSERT2 (!(m_wItemCount & 1),"Invalid number of parameters in string 'artefacts' in the 'system.ltx'!"); + m_wItemCount >>= 1; + + m_ArtefactSpawn.clear(); + string512 l_caBuffer; + + float total_probability = 0.f; + + m_ArtefactSpawn.resize(m_wItemCount); + for (u16 i=0; ir_u32(section,"ef_anomaly_type"); + m_ef_weapon_type = pSettings->r_u32(section,"ef_weapon_type"); +} + +BOOL CCustomZone::net_Spawn(CSE_Abstract* DC) +{ + if (!inherited::net_Spawn(DC)) + return (FALSE); + + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeCustomZone *Z = smart_cast(e); + VERIFY (Z); + + m_fAttenuation = pSettings->r_float(cNameSect(),"attenuation"); + m_dwPeriod = pSettings->r_u32(cNameSect(),"period"); + m_owner_id = Z->m_owner_id; + if(m_owner_id != u32(-1)) + m_ttl = Device.dwTimeGlobal + 40000;// 40 sec + else + m_ttl = u32(-1); + + if (GameID() != GAME_SINGLE) + m_zone_flags.set(eSpawnBlowoutArtefacts, FALSE); + + m_TimeToDisable = Z->m_disabled_time*1000; + m_TimeToEnable = Z->m_enabled_time*1000; + m_TimeShift = Z->m_start_time_shift*1000; + m_StartTime = Device.dwTimeGlobal; + m_zone_flags.set (eUseOnOffTime, (m_TimeToDisable!=0)&&(m_TimeToEnable!=0) ); + + //добавить источники света + if ( m_zone_flags.test(eIdleLight) ) + { + m_pIdleLight = ::Render->light_create(); + m_pIdleLight->set_shadow(true); + } + else + m_pIdleLight = NULL; + + if ( m_zone_flags.test(eBlowoutLight) ) + { + m_pLight = ::Render->light_create(); + m_pLight->set_shadow(true); + }else + m_pLight = NULL; + + setEnabled (TRUE); + + PlayIdleParticles (); + + m_eZoneState = eZoneStateIdle; + m_iPreviousStateTime = m_iStateTime = 0; + + if(m_effector) m_effector->SetRadius (CFORM()->getSphere().R); + + m_dwLastTimeMoved = Device.dwTimeGlobal; + m_vPrevPos.set (Position()); + + + m_fDistanceToCurEntity = flt_max; + m_bBlowoutWindActive = false; + + o_fastmode = TRUE; // start initially with fast-mode enabled + if(spawn_ini() && spawn_ini()->line_exist("fast_mode","always_fast")) + { + m_b_always_fastmode = spawn_ini()->r_bool("fast_mode","always_fast"); + } + return (TRUE); +} + +void CCustomZone::net_Destroy() +{ + StopIdleParticles (); + + inherited::net_Destroy (); + + StopWind (); + + m_pLight.destroy (); + m_pIdleLight.destroy (); + + CParticlesObject::Destroy(m_pIdleParticles); + + if(m_effector) m_effector->Stop (); + //--------------------------------------------- + OBJECT_INFO_VEC_IT i=m_ObjectInfoMap.begin(),e=m_ObjectInfoMap.end(); + for(;e!=i;i++)exit_Zone(*i); + m_ObjectInfoMap.clear(); +} + +void CCustomZone::net_Import(NET_Packet& P) +{ + inherited::net_Import(P); +// P.r_u32 (m_owner_id); +} + +void CCustomZone::net_Export(NET_Packet& P) +{ + inherited::net_Export(P); +// P.w_u32 (m_owner_id); +} + +bool CCustomZone::IdleState() +{ + UpdateOnOffState (); + + return false; +} + +bool CCustomZone::AwakingState() +{ + if(m_iStateTime>=m_StateTime[eZoneStateAwaking]) + { + SwitchZoneState(eZoneStateBlowout); + return true; + } + return false; +} + +bool CCustomZone::BlowoutState() +{ + if(m_iStateTime>=m_StateTime[eZoneStateBlowout]) + { + SwitchZoneState(eZoneStateAccumulate); + return true; + } + return false; +} +bool CCustomZone::AccumulateState() +{ + if(m_iStateTime>=m_StateTime[eZoneStateAccumulate]) + { + if(m_bZoneActive) + SwitchZoneState(eZoneStateBlowout); + else + SwitchZoneState(eZoneStateIdle); + + return true; + } + return false; +} + +void CCustomZone::UpdateWorkload (u32 dt) +{ + m_iPreviousStateTime = m_iStateTime; + m_iStateTime += (int)dt; + + if (!IsEnabled()) { + if (m_effector && EnableEffector()) + m_effector->Stop(); + return; + }; + + UpdateIdleLight (); + + switch(m_eZoneState) + { + case eZoneStateIdle: + IdleState(); + break; + case eZoneStateAwaking: + AwakingState(); + break; + case eZoneStateBlowout: + BlowoutState(); + break; + case eZoneStateAccumulate: + AccumulateState(); + break; + case eZoneStateDisabled: + break; + default: NODEFAULT; + } + + //вычислить время срабатывания зоны + if(m_bZoneActive) m_dwDeltaTime += dt; + else m_dwDeltaTime = 0; + + if(m_dwDeltaTime > m_dwPeriod) { + m_dwDeltaTime = m_dwPeriod; + } + + if (Level().CurrentEntity()) { + m_fDistanceToCurEntity = Level().CurrentEntity()->Position().distance_to(Position()); + + if (m_effector && EnableEffector()) + m_effector->Update(m_fDistanceToCurEntity); + } + + if(m_pLight && m_pLight->get_active()) + UpdateBlowoutLight (); +} + +// called only in "fast-mode" +void CCustomZone::UpdateCL () +{ + inherited::UpdateCL (); + if (o_fastmode) UpdateWorkload (Device.dwTimeDelta); +} + +// called as usual +void CCustomZone::shedule_Update(u32 dt) +{ + m_bZoneActive = false; + + if (IsEnabled()) + { + const Fsphere& s = CFORM()->getSphere(); + Fvector P; + XFORM().transform_tiny (P,s.P); + + // update + //Msg("%s sphere range = %f", Name(), s.R); + feel_touch_update (P,s.R); + + //пройтись по всем объектам в зоне + //и проверить их состояние + for(OBJECT_INFO_VEC_IT it = m_ObjectInfoMap.begin(); + m_ObjectInfoMap.end() != it; ++it) + { + CGameObject* pObject = (*it).object; +// CObject* pObject = (*it).object; + if (!pObject) continue; + SZoneObjectInfo& info = (*it); + + info.time_in_zone += dt; + + if((!info.small_object && m_iDisableHitTime != -1 && (int)info.time_in_zone > m_iDisableHitTime) || + (info.small_object && m_iDisableHitTimeSmall != -1 && (int)info.time_in_zone > m_iDisableHitTimeSmall)) + { + if (ShouldIgnoreObject(pObject)) + { + info.zone_ignore = true; + } + } + if(m_iDisableIdleTime != -1 && (int)info.time_in_zone > m_iDisableIdleTime) + { + if (ShouldIgnoreObject(pObject)) + { + StopObjectIdleParticles( pObject ); + } +// StopObjectIdleParticles(smart_cast(pObject)); + } + + //если есть хотя бы один не дисабленый объект, то + //зона считается активной + if(info.zone_ignore == false) + m_bZoneActive = true; + } + + if(eZoneStateIdle == m_eZoneState) + CheckForAwaking(); + + inherited::shedule_Update(dt); + + // check "fast-mode" border + float cam_distance = Device.vCameraPosition.distance_to(P)-s.R; + if (cam_distance > FASTMODE_DISTANCE && !m_b_always_fastmode) + o_switch_2_slow (); + else + o_switch_2_fast (); + + if (!o_fastmode) UpdateWorkload (dt); + + }; + + UpdateOnOffState (); + + if( !IsGameTypeSingle() && Local() ) + { + if(Device.dwTimeGlobal > m_ttl) + DestroyObject (); + } +} + +void CCustomZone::CheckForAwaking() +{ + if(m_bZoneActive && eZoneStateIdle == m_eZoneState) + SwitchZoneState(eZoneStateAwaking); +} + +void CCustomZone::feel_touch_new (CObject* O) +{ + if(smart_cast(O) && O == Level().CurrentEntity()) + m_pLocalActor = smart_cast(O); + + CGameObject* pGameObject = smart_cast(O); + CEntityAlive* pEntityAlive = smart_cast(pGameObject); + CArtefact* pArtefact = smart_cast(pGameObject); + + + SZoneObjectInfo object_info ; + object_info.object = pGameObject; + + if(pEntityAlive && pEntityAlive->g_Alive()) + object_info.nonalive_object = false; + else + object_info.nonalive_object = true; + + if(pGameObject->Radius()cName(),"leaving a zone."); +#endif + + if(smart_cast(O)) m_pLocalActor = NULL; + CGameObject* pGameObject =smart_cast(O); + if(!pGameObject->getDestroy()) + { + StopObjectIdleParticles(pGameObject); + } + + OBJECT_INFO_VEC_IT it = std::find(m_ObjectInfoMap.begin(),m_ObjectInfoMap.end(),pGameObject); + if(it!=m_ObjectInfoMap.end()) + { + exit_Zone(*it); + m_ObjectInfoMap.erase(it); + } +} + +BOOL CCustomZone::feel_touch_contact(CObject* O) +{ + if (smart_cast(O)) return FALSE; + if (smart_cast(O)) return FALSE; + if (0==smart_cast(O->Visual())) return FALSE; + + if (O->ID() == ID()) + return (FALSE); + + CGameObject *object = smart_cast(O); + if (!object || !object->IsVisibleForZones()) + return (FALSE); + + + if (!((CCF_Shape*)CFORM())->Contact(O)) + return (FALSE); + + return (object->feel_touch_on_contact(this)); +} + + +float CCustomZone::RelativePower(float dist) +{ + float radius = effective_radius(); + float power = radius < dist ? 0 : (1.f - m_fAttenuation*(dist/radius)*(dist/radius)); + return power < 0 ? 0 : power; +} +float CCustomZone::effective_radius() +{ + return Radius()*m_fEffectiveRadius; +} + +float CCustomZone::distance_to_center(CObject* O) +{ + Fvector P; + XFORM().transform_tiny(P,CFORM()->getSphere().P); + return P.distance_to(O->Position()); +} +float CCustomZone::Power(float dist) +{ + return m_fMaxPower * RelativePower(dist); +} + +void CCustomZone::PlayIdleParticles() +{ + m_idle_sound.play_at_pos(0, Position(), true); + + if(*m_sIdleParticles) + { + if (!m_pIdleParticles) + { + m_pIdleParticles = CParticlesObject::Create(*m_sIdleParticles,FALSE); + m_pIdleParticles->UpdateParent(XFORM(),zero_vel); + } + m_pIdleParticles->UpdateParent(XFORM(),zero_vel); + m_pIdleParticles->Play(false); + } + + StartIdleLight (); +} + +void CCustomZone::StopIdleParticles() +{ + m_idle_sound.stop(); + + if(m_pIdleParticles) + { + m_pIdleParticles->Stop(FALSE); + CParticlesObject::Destroy(m_pIdleParticles); + } + + StopIdleLight(); +} + + +void CCustomZone::StartIdleLight () +{ + if(m_pIdleLight) + { + m_pIdleLight->set_range(m_fIdleLightRange); + Fvector pos = Position(); + pos.y += m_fIdleLightHeight; + m_pIdleLight->set_position(pos); + m_pIdleLight->set_active(true); + } +} +void CCustomZone::StopIdleLight () +{ + if(m_pIdleLight) + m_pIdleLight->set_active(false); +} +void CCustomZone::UpdateIdleLight () +{ + if(!m_pIdleLight || !m_pIdleLight->get_active()) + return; + + + VERIFY(m_pIdleLAnim); + + int frame = 0; + u32 clr = m_pIdleLAnim->CalculateBGR(Device.fTimeGlobal,frame); // возвращает в формате BGR + Fcolor fclr; + fclr.set ((float)color_get_B(clr)/255.f,(float)color_get_G(clr)/255.f,(float)color_get_R(clr)/255.f,1.f); + + float range = m_fIdleLightRange + m_fIdleLightRangeDelta*::Random.randF(-1.f,1.f); + m_pIdleLight->set_range (range); + m_pIdleLight->set_color (fclr); + + Fvector pos = Position(); + pos.y += m_fIdleLightHeight; + m_pIdleLight->set_position(pos); +} + + +void CCustomZone::PlayBlowoutParticles() +{ + if(!m_sBlowoutParticles) return; + + CParticlesObject* pParticles; + pParticles = CParticlesObject::Create(*m_sBlowoutParticles,TRUE); + pParticles->UpdateParent(XFORM(),zero_vel); + pParticles->Play(false); +} + +void CCustomZone::PlayHitParticles(CGameObject* pObject) +{ + m_hit_sound.play_at_pos(0, pObject->Position()); + + shared_str particle_str = NULL; + + if(pObject->Radius()(pObject); + if (PP){ + u16 play_bone = PP->GetRandomBone(); + if (play_bone!=BI_NONE) + PP->StartParticles (particle_str,play_bone,Fvector().set(0,1,0), ID()); + } + } +} + +void CCustomZone::PlayEntranceParticles(CGameObject* pObject) +{ + if (!IsEnabled()) return; + + m_entrance_sound.play_at_pos(0, pObject->Position()); + + shared_str particle_str = NULL; + + if(pObject->Radius()(pObject); + if(shell_holder) + shell_holder->PHGetLinearVell(vel); + else + vel.set(0,0,0); + + //выбрать случайную косточку на объекте + CParticlesPlayer* PP = smart_cast(pObject); + if (PP){ + u16 play_bone = PP->GetRandomBone(); + if (play_bone!=BI_NONE){ + CParticlesObject* pParticles = CParticlesObject::Create(*particle_str,TRUE); + Fmatrix xform; + + Fvector dir; + if(fis_zero(vel.magnitude())) + dir.set(0,1,0); + else + { + dir.set(vel); + dir.normalize(); + } + + PP->MakeXFORM (pObject,play_bone,dir,Fvector().set(0,0,0),xform); + pParticles->UpdateParent(xform, vel); + { + pParticles->Play (false); + //. <--> + //. PP->StartParticles (particle_str, play_bone, dir, ID()); + } + } + } +} + + +void CCustomZone::PlayBulletParticles(Fvector& pos) +{ + m_entrance_sound.play_at_pos(0, pos); + + if(!m_sEntranceParticlesSmall) return; + + CParticlesObject* pParticles; + pParticles = CParticlesObject::Create(*m_sEntranceParticlesSmall,TRUE); + + Fmatrix M; + M = XFORM(); + M.c.set(pos); + + pParticles->UpdateParent(M,zero_vel); + pParticles->Play(false); +} + +void CCustomZone::PlayObjectIdleParticles(CGameObject* pObject) +{ + CParticlesPlayer* PP = smart_cast(pObject); + if(!PP) return; + + shared_str particle_str = NULL; + + //разные партиклы для объектов разного размера + if(pObject->Radius()StopParticles (particle_str, BI_NONE, true); + + PP->StartParticles (particle_str, Fvector().set(0,1,0), ID()); + if (!IsEnabled()) + PP->StopParticles (particle_str, BI_NONE, true); +} + +void CCustomZone::StopObjectIdleParticles(CGameObject* pObject) +{ + //. new + if (!pObject || (m_bIdleObjectParticlesDontStop&&!pObject->cast_actor())) + return; + + CParticlesPlayer* PP = smart_cast(pObject); + if(!PP) return; + + + OBJECT_INFO_VEC_IT it = std::find(m_ObjectInfoMap.begin(),m_ObjectInfoMap.end(),pObject); + if(m_ObjectInfoMap.end() == it) return; + + + shared_str particle_str = NULL; + //разные партиклы для объектов разного размера + if(pObject->Radius()StopParticles (particle_str, BI_NONE, true); +} + +void CCustomZone::Hit (SHit* pHDS) +{ + Fmatrix M; + M.identity(); + M.translate_over (pHDS->p_in_bone_space); + M.mulA_43 (XFORM()); + PlayBulletParticles (M.c); +} + +void CCustomZone::StartBlowoutLight () +{ + if(!m_pLight || m_fLightTime<=0.f) return; + + m_fLightTimeLeft = m_fLightTime; + + m_pLight->set_color(m_LightColor.r, m_LightColor.g, m_LightColor.b); + m_pLight->set_range(m_fLightRange); + + Fvector pos = Position(); + pos.y += m_fLightHeight; + m_pLight->set_position(pos); + m_pLight->set_active(true); + +} + +void CCustomZone::StopBlowoutLight () +{ + m_fLightTimeLeft = 0.f; + m_pLight->set_active(false); +} + +void CCustomZone::UpdateBlowoutLight () +{ + if(m_fLightTimeLeft>0) + { + m_fLightTimeLeft -= Device.fTimeDelta; + clamp(m_fLightTimeLeft,0.0f,m_fLightTime); + + float scale = m_fLightTimeLeft/m_fLightTime; + scale = powf(scale+EPS_L, 0.15f); + float r = m_fLightRange*scale; + VERIFY(_valid(r)); + m_pLight->set_color(m_LightColor.r*scale, + m_LightColor.g*scale, + m_LightColor.b*scale); + m_pLight->set_range(r); + + Fvector pos = Position(); + pos.y += m_fLightHeight; + m_pLight->set_position(pos); + } + else + StopBlowoutLight (); +} + +void CCustomZone::AffectObjects() +{ + if(m_dwAffectFrameNum == Device.dwFrame) return; + m_dwAffectFrameNum = Device.dwFrame; + + if(Device.dwPrecacheFrame) return; + + + OBJECT_INFO_VEC_IT it; + for(it = m_ObjectInfoMap.begin(); m_ObjectInfoMap.end() != it; ++it) + { + if (!(*it).object->getDestroy()){ + Affect(&(*it)); + + + CEntityAlive* EA = smart_cast((*it).object); + if (EA) + { + //Msg("%s affects %s", Name(), (*it).object->Name()); + + if (EA->g_Alive()){ + // Занесем монстра в список, если его там еще нет + bool found = false; + for (int i = 0; i < affectedgameobjects.size(); ++i) + { + if (EA == affectedgameobjects[i]) { + //Msg("%s: %s is in list alredy", Name(), (*it).object->Name()); + found = true; + } + } + if (!found){ + //Msg("%s puts %s in list", Name(), (*it).object->Name()); + affectedgameobjects.push_back(EA); + } + } + else if (!EA->g_Alive()){ + // Отчистим список если монстр в списоке и он мертв, резрешим спавн арта + bool found = false; + for (int i = 0; i < affectedgameobjects.size(); ++i) + { + if (EA == affectedgameobjects[i]) { + //Msg("%s -> %s is in list so lets clear list and let af spawn", Name(), (*it).object->Name()); + found = true; + } + } + if (found){ + //Msg("%s -> %s let af to spawn", Name(), (*it).object->Name()); + SpawnArtefactWhenBlewOut = true; + affectedgameobjects.clear(); + } + } + } + } + } + + m_dwDeltaTime = 0; +} + +void CCustomZone::UpdateBlowout() +{ + if(m_dwBlowoutParticlesTime>=(u32)m_iPreviousStateTime && + m_dwBlowoutParticlesTime<(u32)m_iStateTime) + PlayBlowoutParticles(); + + if(m_dwBlowoutLightTime>=(u32)m_iPreviousStateTime && + m_dwBlowoutLightTime<(u32)m_iStateTime) + StartBlowoutLight (); + + if(m_dwBlowoutSoundTime>=(u32)m_iPreviousStateTime && + m_dwBlowoutSoundTime<(u32)m_iStateTime) + m_blowout_sound.play_at_pos (0, Position()); + + if(m_zone_flags.test(eBlowoutWind) && m_dwBlowoutWindTimeStart>=(u32)m_iPreviousStateTime && + m_dwBlowoutWindTimeStart<(u32)m_iStateTime) + StartWind(); + + UpdateWind(); + + + if(m_dwBlowoutExplosionTime>=(u32)m_iPreviousStateTime && + m_dwBlowoutExplosionTime<(u32)m_iStateTime) + { + AffectObjects(); + BornArtefact(); + } +} + +void CCustomZone::OnMove() +{ + if(m_dwLastTimeMoved == 0) + { + m_dwLastTimeMoved = Device.dwTimeGlobal; + m_vPrevPos.set(Position()); + } + else + { + float time_delta = float(Device.dwTimeGlobal - m_dwLastTimeMoved)/1000.f; + m_dwLastTimeMoved = Device.dwTimeGlobal; + + Fvector vel; + + if(fis_zero(time_delta)) + vel = zero_vel; + else + { + vel.sub(Position(), m_vPrevPos); + vel.div(time_delta); + } + + if (m_pIdleParticles) + m_pIdleParticles->UpdateParent(XFORM(), vel); + + if(m_pLight && m_pLight->get_active()) + m_pLight->set_position(Position()); + + if(m_pIdleLight && m_pIdleLight->get_active()) + m_pIdleLight->set_position(Position()); + } +} + +void CCustomZone::OnEvent (NET_Packet& P, u16 type) +{ + switch (type) + { + case GE_ZONE_STATE_CHANGE: + { + u8 S; + P.r_u8 (S); + OnStateSwitch (EZoneState(S)); + break; + } + case GE_OWNERSHIP_TAKE : + { + u16 id; + P.r_u16(id); + OnOwnershipTake(id); + break; + } + case GE_OWNERSHIP_REJECT : + { + u16 id; + P.r_u16 (id); + CArtefact *artefact = smart_cast(Level().Objects.net_Find(id)); + if(artefact) + { + bool just_before_destroy = !P.r_eof() && P.r_u8(); + artefact->H_SetParent(NULL,just_before_destroy); + if (!just_before_destroy) + ThrowOutArtefact(artefact); + } + break; + } + } + inherited::OnEvent(P, type); +}; +void CCustomZone::OnOwnershipTake(u16 id) +{ + CGameObject* GO = smart_cast(Level().Objects.net_Find(id)); VERIFY(GO); + if(!smart_cast(GO)) + { + Msg("zone_name[%s] object_name[%s]",cName().c_str(), GO->cName().c_str() ); + } + CArtefact *artefact = smart_cast(Level().Objects.net_Find(id)); VERIFY(artefact); + artefact->H_SetParent(this); + + artefact->setVisible(FALSE); + artefact->setEnabled(FALSE); + + if (Local()) { + NET_Packet P; + u_EventGen(P, GE_OWNERSHIP_REJECT, ID()); + P.w_u16(id); + u_EventSend(P); + } + + //m_SpawnedArtefacts.push_back(artefact); +} + +void CCustomZone::OnStateSwitch (EZoneState new_state) +{ + if (eZoneStateDisabled == new_state) + Disable(); + else + Enable(); + + if(m_eZoneState==eZoneStateIdle) + StopIdleParticles(); + + if(new_state==eZoneStateIdle) + PlayIdleParticles(); + + if(new_state==eZoneStateAccumulate) + PlayAccumParticles(); + + if(new_state==eZoneStateAwaking) + PlayAwakingParticles(); + + m_eZoneState = new_state; + m_iPreviousStateTime = m_iStateTime = 0; +}; + +void CCustomZone::SwitchZoneState(EZoneState new_state) +{ + if (OnServer()) + { + // !!! Just single entry for given state !!! + NET_Packet P; + u_EventGen (P,GE_ZONE_STATE_CHANGE,ID()); + P.w_u8 (u8(new_state)); + u_EventSend (P); + }; + + m_iPreviousStateTime = m_iStateTime = 0; +} + +bool CCustomZone::Enable() +{ + if (IsEnabled()) return false; + + for(OBJECT_INFO_VEC_IT it = m_ObjectInfoMap.begin(); + m_ObjectInfoMap.end() != it; ++it) + { + CGameObject* pObject = (*it).object; + if (!pObject) continue; + PlayEntranceParticles(pObject); + PlayObjectIdleParticles(pObject); + } + return true; +}; + +bool CCustomZone::Disable() +{ + if (!IsEnabled()) return false; + + for(OBJECT_INFO_VEC_IT it = m_ObjectInfoMap.begin(); + m_ObjectInfoMap.end() != it; ++it) + { + CGameObject* pObject = (*it).object; + if (!pObject) continue; + StopObjectIdleParticles(pObject); + } + return false; +}; + +void CCustomZone::ZoneEnable() +{ + SwitchZoneState(eZoneStateIdle); +}; + +void CCustomZone::ZoneDisable() +{ + SwitchZoneState(eZoneStateDisabled); +} + +bool CCustomZone::ShouldIgnoreObject(CGameObject* pObject) +{ + auto pEntityAlive = smart_cast(pObject); + return !pEntityAlive || !pEntityAlive->g_Alive(); +} + + +void CCustomZone::SpawnArtefact() +{ + //вычислить согласно распределению вероятностей + //какой артефакт из списка ставить + float rnd = ::Random.randF(.0f,1.f-EPS_L); + float prob_threshold = 0.f; + + std::size_t i=0; + for(; iXFORM().c.set(pos); + + if(*m_sArtefactSpawnParticles) + { + CParticlesObject* pParticles; + pParticles = CParticlesObject::Create(*m_sArtefactSpawnParticles,TRUE); + pParticles->UpdateParent(pArtefact->XFORM(),zero_vel); + pParticles->Play(false); + } + + m_ArtefactBornSound.play_at_pos(0, pos); + + NET_Packet PP; + CGameObject::u_EventGen(PP, GE_CHANGE_POS, pArtefact->ID()); + PP.w_vec3(pos); + CGameObject::u_EventSend(PP); + + Fvector dir; + dir.random_dir(); + dir.normalize(); + pArtefact->m_pPhysicsShell->applyImpulse (dir, m_fThrowOutPower); +} + +void CCustomZone::StartWind() +{ + if(m_fDistanceToCurEntity>WIND_RADIUS) return; + + m_bBlowoutWindActive = true; + m_fStoreWindPower = g_pGamePersistent->Environment().wind_strength_factor; + clamp(g_pGamePersistent->Environment().wind_strength_factor, 0.f, 1.f); +} + +void CCustomZone::StopWind() +{ + if(!m_bBlowoutWindActive) return; + m_bBlowoutWindActive = false; + g_pGamePersistent->Environment().wind_strength_factor = m_fStoreWindPower; +} + +void CCustomZone::UpdateWind() +{ + if(!m_bBlowoutWindActive) return; + + if(m_fDistanceToCurEntity>WIND_RADIUS || m_dwBlowoutWindTimeEnd<(u32)m_iStateTime) + { + StopWind(); + return; + } + + if(m_dwBlowoutWindTimePeak > (u32)m_iStateTime) + { + g_pGamePersistent->Environment().wind_strength_factor = m_fBlowoutWindPowerMax + ( m_fStoreWindPower - m_fBlowoutWindPowerMax)* + float(m_dwBlowoutWindTimePeak - (u32)m_iStateTime)/ + float(m_dwBlowoutWindTimePeak - m_dwBlowoutWindTimeStart); + clamp(g_pGamePersistent->Environment().wind_strength_factor, 0.f, 1.f); + } + else + { + g_pGamePersistent->Environment().wind_strength_factor = m_fBlowoutWindPowerMax + (m_fStoreWindPower - m_fBlowoutWindPowerMax)* + float((u32)m_iStateTime - m_dwBlowoutWindTimePeak)/ + float(m_dwBlowoutWindTimeEnd - m_dwBlowoutWindTimePeak); + clamp(g_pGamePersistent->Environment().wind_strength_factor, 0.f, 1.f); + } +} + +u32 CCustomZone::ef_anomaly_type () const +{ + return (m_ef_anomaly_type); +} + +u32 CCustomZone::ef_weapon_type () const +{ + VERIFY (m_ef_weapon_type != u32(-1)); + return (m_ef_weapon_type); +} + +void CCustomZone::CreateHit ( u16 id_to, + u16 id_from, + const Fvector& hit_dir, + float hit_power, + s16 bone_id, + const Fvector& pos_in_bone, + float hit_impulse, + ALife::EHitType hit_type) +{ + if (OnServer()) + { + if(m_owner_id != u32(-1) ) + id_from = (u16)m_owner_id; + + NET_Packet l_P; + Fvector hdir = hit_dir; + SHit Hit = SHit(hit_power, hdir, this, bone_id, pos_in_bone, hit_impulse, hit_type); + Hit.GenHeader(GE_HIT, id_to); + Hit.whoID = id_from; + Hit.weaponID = this->ID(); + Hit.Write_Packet(l_P); + + u_EventSend (l_P); + }; +} + +void CCustomZone::net_Relcase(CObject* O) +{ + CGameObject* GO = smart_cast(O); + OBJECT_INFO_VEC_IT it = std::find(m_ObjectInfoMap.begin(),m_ObjectInfoMap.end(), GO); + if(it!=m_ObjectInfoMap.end()){ + exit_Zone(*it); + m_ObjectInfoMap.erase(it); + } + if(GO->ID()==m_owner_id) m_owner_id = u32(-1); + + if(m_effector && m_effector->m_pActor && m_effector->m_pActor->ID() == GO->ID()) + m_effector->Stop(); + + inherited::net_Relcase(O); +} + +void CCustomZone::enter_Zone(SZoneObjectInfo& io) +{ + +} + +void CCustomZone::exit_Zone (SZoneObjectInfo& io) +{ + StopObjectIdleParticles(io.object); +} + +void CCustomZone::PlayAccumParticles() +{ + if(m_sAccumParticles.size()){ + CParticlesObject* pParticles; + pParticles = CParticlesObject::Create(*m_sAccumParticles,TRUE); + pParticles->UpdateParent(XFORM(),zero_vel); + pParticles->Play(false); + } + + if(m_accum_sound._handle()) + m_accum_sound.play_at_pos (0, Position()); +} + +void CCustomZone::PlayAwakingParticles() +{ + if(m_sAwakingParticles.size()){ + CParticlesObject* pParticles; + pParticles = CParticlesObject::Create(*m_sAwakingParticles,TRUE); + pParticles->UpdateParent(XFORM(),zero_vel); + pParticles->Play(false); + } + + if(m_awaking_sound._handle()) + m_awaking_sound.play_at_pos (0, Position()); +} + +void CCustomZone::UpdateOnOffState () +{ + if(!m_zone_flags.test(eUseOnOffTime)) return; + + bool dest_state; + u32 t = (Device.dwTimeGlobal-m_StartTime+m_TimeShift) % (m_TimeToEnable+m_TimeToDisable); + if (t < m_TimeToEnable) + dest_state=true; + else + if (t >=(m_TimeToEnable+m_TimeToDisable) ) + dest_state=true; + else{ + dest_state=false; + VERIFY(t<(m_TimeToEnable+m_TimeToDisable)); + } + + if( (eZoneStateDisabled==m_eZoneState) && dest_state){ + GoEnabledState (); + }else + if( (eZoneStateIdle==m_eZoneState) && !dest_state){ + GoDisabledState (); + } +} + +void CCustomZone::GoDisabledState() +{ + //switch to disable + NET_Packet P; + u_EventGen (P,GE_ZONE_STATE_CHANGE,ID()); + P.w_u8 (u8(eZoneStateDisabled)); + u_EventSend (P); + + OBJECT_INFO_VEC_IT it = m_ObjectInfoMap.begin(); + OBJECT_INFO_VEC_IT it_e = m_ObjectInfoMap.end(); + + for(;it!=it_e;++it) + exit_Zone(*it); + + m_ObjectInfoMap.clear (); + feel_touch.clear (); +} + +void CCustomZone::GoEnabledState() +{ + //switch to idle + NET_Packet P; + u_EventGen (P,GE_ZONE_STATE_CHANGE,ID()); + P.w_u8 (u8(eZoneStateIdle)); + u_EventSend (P); +} + +BOOL CCustomZone::feel_touch_on_contact (CObject *O) +{ + if ((spatial.type | STYPE_VISIBLEFORAI) != spatial.type) + return (FALSE); + + return (inherited::feel_touch_on_contact(O)); +} + +BOOL CCustomZone::AlwaysTheCrow() +{ + if(m_b_always_fastmode && IsEnabled() ) + return TRUE; + else + return inherited::AlwaysTheCrow(); +} +void CCustomZone::save (NET_Packet &output_packet) +{ + inherited::save (output_packet); + output_packet.w_u8 (static_cast(m_eZoneState)); +} + +void CCustomZone::load (IReader &input_packet) +{ + inherited::load (input_packet); + + CCustomZone::EZoneState temp = static_cast(input_packet.r_u8()); + + if (temp == eZoneStateDisabled) + m_eZoneState = eZoneStateDisabled; + else + m_eZoneState = eZoneStateIdle; +} \ No newline at end of file diff --git a/src/xrGameLA/CustomZone.h b/src/xrGameLA/CustomZone.h new file mode 100644 index 000000000..812c1c766 --- /dev/null +++ b/src/xrGameLA/CustomZone.h @@ -0,0 +1,396 @@ +#pragma once + +#include "space_restrictor.h" +#include "../feel_touch.h" + +class CActor; +class CLAItem; +class CArtefact; +class CParticlesObject; +class CZoneEffector; + +#define SMALL_OBJECT_RADIUS 0.6f + +//информация о объекте, находящемся в зоне +struct SZoneObjectInfo +{ + SZoneObjectInfo():object(NULL),zone_ignore(false),time_in_zone(0),hit_num(0),total_damage(0),small_object(false),nonalive_object(false) {} + CGameObject* object; + bool small_object; + bool nonalive_object; + //игнорирование объекта в зоне + bool zone_ignore; + //присоединенные партиклы + xr_vector particles_vector; + //время прибывания в зоне + u32 time_in_zone; + //количество раз, сколько зона воздействовала на объект + u32 hit_num; + //количество повреждений нанесенных зоной + float total_damage; + + bool operator == (const CGameObject* O) const {return object==O;} +}; + + +class CCustomZone : + public CSpaceRestrictor, + public Feel::Touch +{ +private: + typedef CSpaceRestrictor inherited; + +public: + CZoneEffector* m_effector; + +public: + + CCustomZone (); + virtual ~CCustomZone (); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Import (NET_Packet& P); + virtual void net_Export (NET_Packet& P); + virtual void Load (LPCSTR section); + virtual void net_Destroy (); + + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + + virtual void UpdateCL (); + virtual void UpdateWorkload (u32 dt ); // related to fast-mode optimizations + virtual void shedule_Update (u32 dt ); + virtual void enter_Zone (SZoneObjectInfo& io); + virtual void exit_Zone (SZoneObjectInfo& io); + virtual void feel_touch_new (CObject* O ); + virtual void feel_touch_delete (CObject* O ); + virtual BOOL feel_touch_contact (CObject* O ); + virtual BOOL feel_touch_on_contact (CObject* O ); + virtual float effective_radius (); + virtual float distance_to_center (CObject* O ); + virtual void Postprocess (float val) {} + virtual void net_Relcase (CObject* O ); + virtual void OnEvent (NET_Packet& P, u16 type); + void OnOwnershipTake (u16 id); + + float GetMaxPower () {return m_fMaxPower;} + void SetMaxPower (float p) {m_fMaxPower = p;} + + //вычисление силы хита в зависимости от расстояния до центра зоны + //относительный размер силы (от 0 до 1) + virtual float RelativePower (float dist); + //абсолютный размер + float Power (float dist); + + virtual CCustomZone *cast_custom_zone () {return this;} + + //различные состояния в которых может находиться зона + typedef enum { + eZoneStateIdle = 0, //состояние зоны, когда внутри нее нет активных объектов + eZoneStateAwaking, //пробуждение зоны (объект попал в зону) + eZoneStateBlowout, //выброс + eZoneStateAccumulate, //накапливание энергии, после выброса + eZoneStateDisabled, + eZoneStateMax + } EZoneState; + +protected: + enum EZoneFlags{ + eIgnoreNonAlive =(1<<0), + eIgnoreSmall =(1<<1), + eIgnoreArtefact =(1<<2), + eVisibleByDetector =(1<<3), + eBlowoutWind =(1<<4), + eBlowoutLight =(1<<5), + eIdleLight =(1<<6), + eSpawnBlowoutArtefacts =(1<<7), + eUseOnOffTime =(1<<8), + }; + u32 m_owner_id; //if created from artefact + u32 m_ttl; + Flags32 m_zone_flags; + //список объетков, находящихся в зоне + CActor* m_pLocalActor; + + //максимальная сила заряда зоны + float m_fMaxPower; + + //линейный коэффициент затухания в зависимости от расстояния + float m_fAttenuation; + //процент удара зоны, который пойдет на физический импульс + float m_fHitImpulseScale; + //размер радиуса в процентах от оригинального, + //где действует зона + float m_fEffectiveRadius; + + //тип наносимого хита + ALife::EHitType m_eHitTypeBlowout; + + + + EZoneState m_eZoneState; + + + //текущее время пребывания зоны в определенном состоянии + int m_iStateTime; + int m_iPreviousStateTime; + + u32 m_TimeToDisable; + u32 m_TimeToEnable; + u32 m_TimeShift; + u32 m_StartTime; + + //массив с временами, сколько каждое состояние должно + //длиться (если 0, то мгновенно -1 - бесконечность, + //-2 - вообще не должно вызываться) + typedef svector StateTimeSVec; + StateTimeSVec m_StateTime; + + virtual void SwitchZoneState (EZoneState new_state); + virtual void OnStateSwitch (EZoneState new_state); + virtual void CheckForAwaking (); + //обработка зоны в различных состояниях + virtual bool IdleState (); + virtual bool AwakingState (); + virtual bool BlowoutState (); + virtual bool AccumulateState (); + + virtual bool Enable (); + virtual bool Disable (); + void UpdateOnOffState (); + virtual void GoEnabledState (); + virtual void GoDisabledState (); +public: + bool IsEnabled () {return m_eZoneState != eZoneStateDisabled; }; + void ZoneEnable (); + void ZoneDisable (); + EZoneState ZoneState () {return m_eZoneState;} + virtual bool ShouldIgnoreObject (CGameObject*); + +protected: + + + //воздействие зоной на объект + virtual void Affect (SZoneObjectInfo* O) {} + + //воздействовать на все объекты в зоне + virtual void AffectObjects (); + + u32 m_dwAffectFrameNum; + + u32 m_dwDeltaTime; + u32 m_dwPeriod; +// bool m_bZoneReady; + //если в зоне есть не disabled объекты + bool m_bZoneActive; + + + //параметры для выброса, с какой задержкой + //включать эффекты и логику + u32 m_dwBlowoutParticlesTime; + u32 m_dwBlowoutLightTime; + u32 m_dwBlowoutSoundTime; + u32 m_dwBlowoutExplosionTime; + virtual void UpdateBlowout(); + + //ветер + bool m_bBlowoutWindActive; + u32 m_dwBlowoutWindTimeStart; + u32 m_dwBlowoutWindTimePeak; + u32 m_dwBlowoutWindTimeEnd; + //сила ветра (увеличение текущего) (0,1) когда в аномалию попадает актер + float m_fBlowoutWindPowerMax; + float m_fStoreWindPower; + + void StartWind (); + void StopWind (); + void UpdateWind (); + + + //время, через которое, зона перестает реагировать + //на объект мертвый объект (-1 если не указано) + int m_iDisableHitTime; + //тоже самое но для маленьких объектов + int m_iDisableHitTimeSmall; + int m_iDisableIdleTime; + + //////////////////////////////// + // имена партиклов зоны + + //обычное состояние зоны + shared_str m_sIdleParticles; + //выброс зоны + shared_str m_sBlowoutParticles; + shared_str m_sAccumParticles; + shared_str m_sAwakingParticles; + + + //появление большого и мальнекого объекта в зоне + shared_str m_sEntranceParticlesSmall; + shared_str m_sEntranceParticlesBig; + //поражение большого и мальнекого объекта в зоне + shared_str m_sHitParticlesSmall; + shared_str m_sHitParticlesBig; + //нахождение большого и мальнекого объекта в зоне + shared_str m_sIdleObjectParticlesSmall; + shared_str m_sIdleObjectParticlesBig; + BOOL m_bIdleObjectParticlesDontStop; + + ref_sound m_idle_sound; + ref_sound m_awaking_sound; + ref_sound m_accum_sound; + ref_sound m_blowout_sound; + ref_sound m_hit_sound; + ref_sound m_entrance_sound; + + //объект партиклов обычного состояния зоны + CParticlesObject* m_pIdleParticles; + + ////////////////////////////// + //подсветка аномалии + + //подсветка idle состояния + ref_light m_pIdleLight; + Fcolor m_IdleLightColor; + float m_fIdleLightRange; + float m_fIdleLightHeight; + float m_fIdleLightRangeDelta; + CLAItem* m_pIdleLAnim; + + void StartIdleLight (); + void StopIdleLight (); + void UpdateIdleLight (); + + + //подсветка выброса + ref_light m_pLight; + float m_fLightRange; + Fcolor m_LightColor; + float m_fLightTime; + float m_fLightTimeLeft; + float m_fLightHeight; + + + + void StartBlowoutLight (); + void StopBlowoutLight (); + void UpdateBlowoutLight (); + + //список партиклов для объетов внутри зоны +// DEFINE_MAP (CObject*, SZoneObjectInfo, OBJECT_INFO_MAP, OBJECT_INFO_MAP_IT); + DEFINE_VECTOR(SZoneObjectInfo,OBJECT_INFO_VEC,OBJECT_INFO_VEC_IT); + OBJECT_INFO_VEC m_ObjectInfoMap; + + void CreateHit ( u16 id_to, + u16 id_from, + const Fvector& hit_dir, + float hit_power, + s16 bone_id, + const Fvector& pos_in_bone, + float hit_impulse, + ALife::EHitType hit_type); + + + virtual void Hit (SHit* pHDS); + + + //для визуализации зоны + virtual void PlayIdleParticles (); + virtual void StopIdleParticles (); + void PlayAccumParticles (); + void PlayAwakingParticles (); + void PlayBlowoutParticles (); + void PlayEntranceParticles (CGameObject* pObject); + void PlayBulletParticles (Fvector& pos ); + + void PlayHitParticles (CGameObject* pObject); + + void PlayObjectIdleParticles (CGameObject* pObject); + void StopObjectIdleParticles (CGameObject* pObject); + + virtual bool EnableEffector () {return false;} + + virtual bool IsVisibleForZones () { return false;} + + //обновление, если зона передвигается + virtual void OnMove (); + Fvector m_vPrevPos; + u32 m_dwLastTimeMoved; + + //видимость зоны детектором +public: + bool VisibleByDetector () {return !!m_zone_flags.test(eVisibleByDetector);} + + ////////////////////////////////////////////////////////////////////////// + // список артефактов +protected: + virtual void SpawnArtefact (); + + //рождение артефакта в зоне, во время ее срабатывания + //и присоединение его к зоне + void BornArtefact (); + //выброс артефактов из зоны + void ThrowOutArtefact (CArtefact* pArtefact); + + void PrefetchArtefacts (); + virtual BOOL AlwaysTheCrow (); + +protected: + DEFINE_VECTOR(CArtefact*, ARTEFACT_VECTOR, ARTEFACT_VECTOR_IT); + ARTEFACT_VECTOR m_SpawnedArtefacts; + + //есть ли вообще функция выбрасывания артефактов во время срабатывания +// bool m_bSpawnBlowoutArtefacts; + //вероятность того, что артефакт засповниться при единичном + //срабатывании аномалии + float m_fArtefactSpawnProbability; + //tatarinrafa:Lets add an oportunity of spawning several arts + u8 m_iArtSpawnCicles; + //величина импульса выкидывания артефакта из зоны + float m_fThrowOutPower; + //высота над центром зоны, где будет появляться артефакт + float m_fArtefactSpawnHeight; + + //имя партиклов, которые проигрываются во время и на месте рождения артефакта + shared_str m_sArtefactSpawnParticles; + //звук рождения артефакта + ref_sound m_ArtefactBornSound; + + bool SpawnArtefactWhenBlewOut; + xr_vector affectedgameobjects; + + struct ARTEFACT_SPAWN + { + shared_str section; + float probability; + }; + + DEFINE_VECTOR(ARTEFACT_SPAWN, ARTEFACT_SPAWN_VECTOR, ARTEFACT_SPAWN_IT); + ARTEFACT_SPAWN_VECTOR m_ArtefactSpawn; + + //расстояние от зоны до текущего актера + float m_fDistanceToCurEntity; + +protected: + u32 m_ef_anomaly_type; + u32 m_ef_weapon_type; + BOOL m_b_always_fastmode; +public: + virtual u32 ef_anomaly_type () const; + virtual u32 ef_weapon_type () const; + virtual bool register_schedule () const {return true;} + + // optimization FAST/SLOW mode +public: + BOOL o_fastmode; + IC void o_switch_2_fast () { + if (o_fastmode) return ; + o_fastmode = TRUE ; + processing_activate (); + } + IC void o_switch_2_slow () { + if (!o_fastmode) return ; + o_fastmode = FALSE ; + processing_deactivate (); + } +}; diff --git a/src/xrGameLA/CycleConstStorage.h b/src/xrGameLA/CycleConstStorage.h new file mode 100644 index 000000000..1e0f6d1ad --- /dev/null +++ b/src/xrGameLA/CycleConstStorage.h @@ -0,0 +1,33 @@ +#ifndef CYCLE_CONST_STORAGE_H +#define CYCLE_CONST_STORAGE_H + +template +class CCycleConstStorage +{ + T array[size]; + int first; + IC int position( int i ) const { return (first+i)%size; } +public: + IC CCycleConstStorage() + { + first=0; + } + IC void fill_in(const T& val) + { + std::fill(array,array+size,val); + } + IC void push_back(T& val) + { + array[first]=val; + first =position( 1 ); + } + IC T& operator [] (int i) + { + return array[ position( i ) ]; + } + IC const T& operator [] (int i) const + { + return array[ position( i ) ]; + } +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/DBG_Car.cpp b/src/xrGameLA/DBG_Car.cpp new file mode 100644 index 000000000..7a4c7e16f --- /dev/null +++ b/src/xrGameLA/DBG_Car.cpp @@ -0,0 +1,212 @@ +#include "stdafx.h" +#ifdef DEBUG + +#include "ode_include.h" +#include "../StatGraph.h" +#include "PHDebug.h" +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "Car.h" +#include "hudmanager.h" +#include "Level.h" +#include "ui_base.h" + +void CCar::InitDebug() +{ + m_dbg_power_rpm .Clear() ; + m_dbg_torque_rpm .Clear() ; + m_dbg_dynamic_plot =0 ; + b_plots =false ; +} +void CCar::DbgSheduleUpdate() +{ + if(ph_dbg_draw_mask.test(phDbgDrawCarPlots)&&m_pPhysicsShell&&OwnerActor()&&static_cast(Owner())==Level().CurrentViewEntity()) + { + DbgCreatePlots(); + } + else + { + DBgClearPlots(); + } +} + +static float torq_pow_max_ratio=1.f; +static float rpm_pow_max_ratio=1.f; + +void CCar::DbgCreatePlots() +{ + if(b_plots)return; + eStateDrive state=e_state_drive; + e_state_drive=drive; +////////////////////////////// + int y_pos=0,y_w=100; + m_dbg_power_rpm.Init(CFunctionGraph::type_function(this,&CCar::Parabola),m_min_rpm,m_max_rpm,0,y_pos,500,y_w,1000,D3DCOLOR_XRGB(0,0,255)); + m_dbg_power_rpm.AddMarker(CStatGraph::stVert, 0, D3DCOLOR_XRGB(255, 0, 0)); + m_dbg_power_rpm.AddMarker(CStatGraph::stHor, 0, D3DCOLOR_XRGB(0, 0, 255)); + m_dbg_power_rpm.AddMarker(CStatGraph::stVert,0,D3DCOLOR_XRGB(0, 0, 0)); + + m_dbg_power_rpm.AddMarker(CStatGraph::stVert,0,D3DCOLOR_XRGB(127, 0, 0)); + m_dbg_power_rpm.AddMarker(CStatGraph::stVert,0,D3DCOLOR_XRGB(0, 0, 127)); + + y_pos+=y_w+10; + + m_dbg_torque_rpm.Init(CFunctionGraph::type_function(this,&CCar::TorqueRpmFun),m_min_rpm,m_max_rpm,0,y_pos,500,y_w,1000); + m_dbg_torque_rpm.AddMarker(CStatGraph::stVert, 0, D3DCOLOR_XRGB(255, 0, 0)); + m_dbg_torque_rpm.AddMarker(CStatGraph::stHor, 0, D3DCOLOR_XRGB(0, 255,0)); + m_dbg_torque_rpm.AddMarker(CStatGraph::stVert,0,D3DCOLOR_XRGB(0, 0, 0)); + + m_dbg_torque_rpm.AddMarker(CStatGraph::stVert,0,D3DCOLOR_XRGB(127, 0, 0)); + m_dbg_torque_rpm.AddMarker(CStatGraph::stVert,0,D3DCOLOR_XRGB(0, 0, 127)); + + y_pos+=y_w+10; + + if(b_auto_switch_transmission&&ph_dbg_draw_mask.test(phDbgDrawCarAllTrnsm)) + { + xr_vector::iterator i=m_gear_ratious.begin()+1,e=m_gear_ratious.end(); + for(;iSetRect(0,y_pos,500,y_w,D3DCOLOR_XRGB(255,255,255),D3DCOLOR_XRGB(255,255,255)); + m_dbg_dynamic_plot ->SetMinMax(Parabola(m_min_rpm),m_max_power,1000); + m_dbg_dynamic_plot ->AppendSubGraph(CStatGraph::stCurve); + torq_pow_max_ratio =Parabola(m_torque_rpm)/m_torque_rpm /m_max_power; + + m_dbg_dynamic_plot ->AppendSubGraph(CStatGraph::stCurve); + rpm_pow_max_ratio =m_max_rpm /m_max_power; + //-------------------------------------- + m_dbg_dynamic_plot ->AddMarker(CStatGraph::stHor, 0, D3DCOLOR_XRGB(255, 0, 0)); + xr_vector::iterator i=m_gear_ratious.begin()+1,e=m_gear_ratious.end(); + for(;iAddMarker(CStatGraph::stHor, (*i)[1]/rpm_pow_max_ratio,D3DCOLOR_XRGB(127, 0, 0)); + m_dbg_dynamic_plot ->AddMarker(CStatGraph::stHor, (*i)[2]/rpm_pow_max_ratio,D3DCOLOR_XRGB(0, 0, 127)); + } +////////////////////////////// + e_state_drive=state; + b_plots=true; +} +void CCar::DBgClearPlots() +{ + if(!b_plots)return; +//////////////////////////////// + m_dbg_power_rpm.Clear(); + m_dbg_torque_rpm.Clear(); + xr_delete(m_dbg_dynamic_plot); +//////////////////////////////// + b_plots=false; +} + +void CCar::DbgUbdateCl() +{ + + if(m_pPhysicsShell&&OwnerActor()&&static_cast(Owner())==Level().CurrentViewEntity()) + { + if(ph_dbg_draw_mask.test(phDbgDrawCarDynamics)) + { + Fvector v; + m_pPhysicsShell->get_LinearVel(v); + string32 s; + xr_sprintf (s,"speed, %f km/hour",v.magnitude()/1000.f*3600.f) ; + UI().Font().pFontStat->SetColor (color_rgba(0xff,0xff,0xff,0xff)) ; + UI().Font().pFontStat->OutSet (120,530) ; + UI().Font().pFontStat->OutNext (s) ; + UI().Font().pFontStat->SetColor (D3DCOLOR_XRGB(255,!b_transmission_switching*255,!b_transmission_switching*255)); + UI().Font().pFontStat->OutNext ("Transmission num: [%d]",m_current_transmission_num) ; + UI().Font().pFontStat->SetColor (color_rgba(0xff,0xff,0xff,0xff)) ; + UI().Font().pFontStat->OutNext ("gear ratio: [%3.2f]",m_current_gear_ratio) ; + UI().Font().pFontStat->OutNext ("Power: [%3.2f]",m_current_engine_power/(0.8f*1000.f)) ; + UI().Font().pFontStat->OutNext ("rpm: [%3.2f][%3.2f]",m_current_rpm/(1.f/60.f*2.f*M_PI),m_current_rpm) ; + UI().Font().pFontStat->OutNext ("wheel torque: [%3.2f]",RefWheelCurTorque()) ; + UI().Font().pFontStat->OutNext ("engine torque: [%3.2f]",EngineCurTorque()) ; + UI().Font().pFontStat->OutNext ("fuel: [%3.2f]",m_fuel) ; + if(b_clutch) + { + UI().Font().pFontStat->SetColor (D3DCOLOR_XRGB(0,255,0)) ; + UI().Font().pFontStat->OutNext ("CLUTCH") ; + UI().Font().pFontStat->SetColor (color_rgba(0xff,0xff,0xff,0xff)) ; + } + if(b_engine_on) + { + UI().Font().pFontStat->SetColor (D3DCOLOR_XRGB(0,255,0)) ; + UI().Font().pFontStat->OutNext ("ENGINE ON") ; + UI().Font().pFontStat->SetColor (color_rgba(0xff,0xff,0xff,0xff)) ; + } + if(b_stalling) + { + UI().Font().pFontStat->SetColor (D3DCOLOR_XRGB(255,0,0)) ; + UI().Font().pFontStat->OutNext ("STALLING") ; + UI().Font().pFontStat->SetColor (color_rgba(0xff,0xff,0xff,0xff)) ; + } + if(b_starting) + { + UI().Font().pFontStat->SetColor (D3DCOLOR_XRGB(255,0,0)) ; + UI().Font().pFontStat->OutNext ("STARTER") ; + UI().Font().pFontStat->SetColor (color_rgba(0xff,0xff,0xff,0xff)) ; + } + if(b_breaks) + { + UI().Font().pFontStat->SetColor (D3DCOLOR_XRGB(255,0,0)) ; + UI().Font().pFontStat->OutNext ("BREAKS") ; + UI().Font().pFontStat->SetColor (color_rgba(0xff,0xff,0xff,0xff)) ; + } + //UI().Font().pFontStat->OutNext("Vel Magnitude: [%3.2f]",m_PhysicMovementControl->GetVelocityMagnitude()); + //UI().Font().pFontStat->OutNext("Vel Actual: [%3.2f]",m_PhysicMovementControl->GetVelocityActual()); + } + + if(ph_dbg_draw_mask.test(phDbgDrawCarPlots)&&b_plots) + { + float cur_torque=EngineCurTorque(); + m_dbg_dynamic_plot->AppendItem(m_current_engine_power,D3DCOLOR_XRGB(0,0,255)); + m_dbg_dynamic_plot->AppendItem(cur_torque/torq_pow_max_ratio,D3DCOLOR_XRGB(0,255,0),1); + m_dbg_dynamic_plot->AppendItem(m_current_rpm/rpm_pow_max_ratio,D3DCOLOR_XRGB(255,0,0),2); + + m_dbg_dynamic_plot->UpdateMarkerPos(0,m_current_rpm/rpm_pow_max_ratio); + + float engine_wheels_rpm=EngineRpmFromWheels() ; + m_dbg_power_rpm.UpdateMarker(0,m_current_rpm) ; + m_dbg_power_rpm.UpdateMarker(1,m_current_engine_power) ; + m_dbg_power_rpm.UpdateMarker(2,engine_wheels_rpm) ; + m_dbg_power_rpm.UpdateMarker(3,m_gear_ratious[m_current_transmission_num][2]); + m_dbg_power_rpm.UpdateMarker(4,m_gear_ratious[m_current_transmission_num][1]); + + m_dbg_torque_rpm.UpdateMarker(0,m_current_rpm) ; + m_dbg_torque_rpm.UpdateMarker(1,cur_torque) ; + m_dbg_torque_rpm.UpdateMarker(2,engine_wheels_rpm) ; + m_dbg_torque_rpm.UpdateMarker(3,m_gear_ratious[m_current_transmission_num][2]); + m_dbg_torque_rpm.UpdateMarker(4,m_gear_ratious[m_current_transmission_num][1]); + + } + } +} + + + + + + + + + + + + + + + + + + + + + +#endif \ No newline at end of file diff --git a/src/xrGameLA/DamagableItem.cpp b/src/xrGameLA/DamagableItem.cpp new file mode 100644 index 000000000..4d1ee337a --- /dev/null +++ b/src/xrGameLA/DamagableItem.cpp @@ -0,0 +1,63 @@ +#include "stdafx.h" +#include "DamagableItem.h" + +CDamagableItem::CDamagableItem() +{ + m_max_health=0.f; + m_levels_num=u16(-1); + m_level_applied=u16(-1); +} + + + +u16 CDamagableItem::DamageLevel() +{ +float health=Health();if(health<0.f)health=0.f; +u16 dl=u16((1.f-Health()/m_max_health)*m_levels_num); + +if(dl m_level_applied) + for(u16 i=m_level_applied+1;i<=new_lewel;i++) ApplyDamage(i); + else + for(int i=(int)m_level_applied-1;i>=new_lewel;i--) ApplyDamage(i); +} +void CDamagableItem::ApplyDamage(u16 level) +{ + m_level_applied=level; +} + +void CDamagableHealthItem::Init(float max_health,u16 level_num) +{ + inherited::Init(max_health,level_num); + m_health=max_health; +} + +void CDamagableHealthItem::Hit(float P) +{ + if(m_level_applied==m_levels_num) return; + m_health-=P; + if(m_health<0.f)m_health=0.f; + HitEffect(); +} + +void CDamagableItem::RestoreEffect() +{ +u16 dl=DamageLevel(); +for(u16 i=1;i<=dl;i++)ApplyDamage(i); +} \ No newline at end of file diff --git a/src/xrGameLA/DamagableItem.h b/src/xrGameLA/DamagableItem.h new file mode 100644 index 000000000..d54316921 --- /dev/null +++ b/src/xrGameLA/DamagableItem.h @@ -0,0 +1,32 @@ +class CDamagableItem +{ + +protected: + u16 m_levels_num ; + float m_max_health ; + u16 m_level_applied ; +public: + CDamagableItem () ; + virtual void Init (float max_health,u16 level_num) ; + void HitEffect () ; + void RestoreEffect () ; + float DamageLevelToHealth (u16 dl) ; +protected: + u16 DamageLevel () ; + virtual float Health () =0; + virtual void ApplyDamage (u16 level) ; +}; + +class CDamagableHealthItem : + public CDamagableItem +{ + typedef CDamagableItem inherited ; + float m_health ; +public: +virtual void Init (float max_health,u16 level_num) ; + void Hit (float P) ; + void SetHealth (float health) {m_health=health;} +protected: + virtual float Health () {return m_health;} + +}; diff --git a/src/xrGameLA/DamageSource.h b/src/xrGameLA/DamageSource.h new file mode 100644 index 000000000..20751472f --- /dev/null +++ b/src/xrGameLA/DamageSource.h @@ -0,0 +1,11 @@ +#pragma once + + +class IDamageSource +{ +public: + virtual ~IDamageSource () {} ; + virtual void SetInitiator (u16 id) =0 ; + virtual u16 Initiator () =0 ; + virtual IDamageSource *cast_IDamageSource () {return this ;} +}; \ No newline at end of file diff --git a/src/xrGameLA/DelayedActionFuse.cpp b/src/xrGameLA/DelayedActionFuse.cpp new file mode 100644 index 000000000..0ffd9d608 --- /dev/null +++ b/src/xrGameLA/DelayedActionFuse.cpp @@ -0,0 +1,79 @@ +#include "stdafx.h" +#include "DelayedActionFuse.h" + +CDelayedActionFuse::CDelayedActionFuse() +{ + m_dafflags.assign (0) ; + m_fTime =0.f ; + m_fSpeedChangeCondition =0.f ; +} + +void CDelayedActionFuse::SetTimer(float current_condition) +{ + VERIFY(isInitialized()&&!isActive()); + m_dafflags.set(flActive,TRUE); + ChangeCondition(m_fSpeedChangeCondition-current_condition); + VERIFY(!fis_zero(m_fTime)||m_dafflags.test(flNoConditionChange)); + if(!m_dafflags.test(flNoConditionChange))m_fSpeedChangeCondition/=m_fTime; + //Msg("to_expl moment %f",m_fTime); + m_fTime+=Device.fTimeGlobal;//+current_condition/m_fSpeedChangeCondition; + //Msg("expl moment %f",m_fTime); + StartTimerEffects(); + +} +float CDelayedActionFuse::Time() +{ + VERIFY(isInitialized()); + if(!isActive()) return m_fTime; + else return m_fTime-Device.fTimeGlobal; +} +void CDelayedActionFuse::Initialize(float time,float critical_condition) +{ + if(isActive()) return; + + VERIFY(time>=0.f&&critical_condition>=0.f); + if(!fis_zero(time)) + { + m_fSpeedChangeCondition=critical_condition;//time; + m_fTime=time; + }else + { + m_fSpeedChangeCondition=0.f; + m_fTime=0.f; + } + if(fis_zero(m_fSpeedChangeCondition))m_dafflags.set(flNoConditionChange,TRUE); + m_dafflags.set(flInitialized,TRUE); +} +bool CDelayedActionFuse::Update(float current_condition) +{ + VERIFY(isActive()); + + bool ret=false; + float l_time_to_explosion=m_fTime-Device.fTimeGlobal; + + if(!m_dafflags.test(flNoConditionChange)) + { + float delta_condition=m_fSpeedChangeCondition*l_time_to_explosion-current_condition; + //float t=current_condition/m_fSpeedChangeCondition; + //if(t0.f) delta_condition=0.f;//. + ChangeCondition(delta_condition); + ret = current_condition+delta_condition<=0.f; + } else + { + ret = l_time_to_explosion<=0.f; + } + + if(ret) + { + m_dafflags.set(flActive,FALSE); + m_dafflags.set(flInitialized,FALSE); + } + return ret; +} + +void CDelayedActionFuse::Reset() +{ + m_dafflags.set(flActive,FALSE); +} diff --git a/src/xrGameLA/DelayedActionFuse.h b/src/xrGameLA/DelayedActionFuse.h new file mode 100644 index 000000000..9b5aabfb6 --- /dev/null +++ b/src/xrGameLA/DelayedActionFuse.h @@ -0,0 +1,27 @@ +#pragma once + +class CDelayedActionFuse +{ + enum{ + flActive = 1<<0, + flInitialized = 1<<1, + flNoConditionChange = 1<<2 + }; + Flags8 m_dafflags ; + float m_fTime ; + float m_fSpeedChangeCondition ; + +public: + void SetTimer (float current_condition) ; + void Initialize (float time,float critical_condition) ; +ICF bool CheckCondition (float current_condition) {if(isInitialized()&&!isActive()&&m_fSpeedChangeCondition>=current_condition){SetTimer(current_condition);return true;} else return false ;} +ICF bool isActive () {return !!m_dafflags.test(flActive);} +ICF bool isInitialized () {return !!m_dafflags.test(flInitialized);} + bool Update (float current_condition) ; + float Time () ; +protected: + CDelayedActionFuse () ; +virtual void ChangeCondition (float fDeltaCondition) =0; +virtual void StartTimerEffects () =0; + void Reset(); +}; \ No newline at end of file diff --git a/src/xrGameLA/DestroyablePhysicsObject.cpp b/src/xrGameLA/DestroyablePhysicsObject.cpp new file mode 100644 index 000000000..2aefe8f2b --- /dev/null +++ b/src/xrGameLA/DestroyablePhysicsObject.cpp @@ -0,0 +1,155 @@ +#include "pch_script.h" +#include "PHCollisionDamageReceiver.h" +#include "PhysicObject.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "hit_immunity.h" +#include "damage_manager.h" +#include "DestroyablePhysicsObject.h" +#include "../../Include/xrRender/Kinematics.h" +#include "xrServer_Objects_ALife.h" +#include "game_object_space.h" +#include "script_callback_ex.h" +#include "script_game_object.h" +#include "PhysicsShell.h" +#ifdef DEBUG +#include "PHWorld.h" +extern CPHWorld *ph_world; +#endif +CDestroyablePhysicsObject ::CDestroyablePhysicsObject() +{ + m_fHealth=1.f; +} + +CDestroyablePhysicsObject::~CDestroyablePhysicsObject() +{ + +} +void CDestroyablePhysicsObject::OnChangeVisual() +{ + if (m_pPhysicsShell){ + m_pPhysicsShell->Deactivate(); + xr_delete (m_pPhysicsShell); + VERIFY (0==Visual()); + } + inherited::OnChangeVisual(); +} +CPhysicsShellHolder* CDestroyablePhysicsObject :: PPhysicsShellHolder () +{ + return cast_physics_shell_holder(); +} + +void CDestroyablePhysicsObject::net_Destroy() +{ + inherited::net_Destroy(); + CPHDestroyable::RespawnInit(); + CPHCollisionDamageReceiver::Clear(); +} + +BOOL CDestroyablePhysicsObject::net_Spawn(CSE_Abstract* DC) +{ + BOOL res=inherited::net_Spawn(DC); + IKinematics *K=smart_cast(Visual()); + CInifile* ini=K->LL_UserData(); + //R_ASSERT2(ini->section_exist("destroyed"),"destroyable_object must have -destroyed- section in model user data"); + CPHDestroyable::Init(); + if(ini&&ini->section_exist("destroyed")) + CPHDestroyable::Load(ini,"destroyed"); + + CDamageManager::reload("damage_section",ini); + if(ini){ + if(ini->section_exist("immunities")) CHitImmunity::LoadImmunities("immunities",ini); + CPHCollisionDamageReceiver::Init(); + if(ini->section_exist("sound")) m_destroy_sound.create(ini->r_string("sound","break_sound"),st_Effect,sg_SourceType); + if(ini->section_exist("particles")) m_destroy_particles=ini->r_string("particles","destroy_particles"); + } + CParticlesPlayer::LoadParticles(K); + RunStartupAnim(DC); + return res; +} + +//void CDestroyablePhysicsObject::Hit (float P,Fvector &dir,CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type) +void CDestroyablePhysicsObject::Hit (SHit* pHDS) +{ + SHit HDS = *pHDS; + callback(GameObject::eHit)( + lua_game_object(), + HDS.power, + HDS.dir, + smart_cast(HDS.who)->lua_game_object(), + HDS.bone() + ); + HDS.power=CHitImmunity::AffectHit(HDS.power,HDS.hit_type); + float hit_scale=1.f,wound_scale=1.f; + CDamageManager::HitScale(HDS.bone(),hit_scale,wound_scale); + HDS.power*=hit_scale; +// inherited::Hit(P,dir,who,element,p_in_object_space,impulse,hit_type); + inherited::Hit(&HDS); + m_fHealth-=HDS.power; + if(m_fHealth<=0.f) + { +// CPHDestroyable::SetFatalHit(SHit(P,dir,who,element,p_in_object_space,impulse,hit_type)); + CPHDestroyable::SetFatalHit(HDS); + if(CPHDestroyable::CanDestroy())Destroy(); + } +} +void CDestroyablePhysicsObject::Destroy() +{ + VERIFY(!ph_world->Processing()); + const CGameObject *who_object = smart_cast(FatalHit().initiator()); + callback(GameObject::eDeath)(lua_game_object(), who_object ? who_object->lua_game_object() : 0); + CPHDestroyable::Destroy(ID(),"physic_destroyable_object"); + if(m_destroy_sound._handle()) + { + m_destroy_sound.play_at_pos(this,Position()); + } + if(*m_destroy_particles) + { + //Fvector dir;dir.set(0,1,0); + Fmatrix m;m.identity(); + ///////////////////////////////////////////////// + m.j.set(0,1.f,0); + /////////////////////////////////////////////// + + Fvector hdir;hdir.set(CPHDestroyable::FatalHit().direction()); + + if(fsimilar(_abs(m.j.dotproduct(hdir)),1.f,EPS_L)) + { + do { + hdir.random_dir(); + } while(fsimilar(_abs(m.j.dotproduct(hdir)),1.f,EPS_L)); + } + m.i.crossproduct(m.j,hdir);m.i.normalize(); + m.k.crossproduct(m.i,m.j); + StartParticles(m_destroy_particles,m,ID()); + } + SheduleRegister(); +} +void CDestroyablePhysicsObject::InitServerObject(CSE_Abstract* D) +{ + CSE_PHSkeleton *ps = smart_cast(D); + R_ASSERT (ps); + if(ps->_flags.test(CSE_PHSkeleton::flSpawnCopy)) + inherited::InitServerObject(D); + else + CPHDestroyable::InitServerObject(D); + + CSE_ALifeObjectPhysic *PO = smart_cast(D); + if(PO)PO->type=epotSkeleton; +} +void CDestroyablePhysicsObject::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + CPHDestroyable::SheduleUpdate(dt); +} + +bool CDestroyablePhysicsObject::CanRemoveObject() +{ + return !CParticlesPlayer::IsPlaying()&& !m_destroy_sound._feedback();//&& sound! +} +DLL_Pure *CDestroyablePhysicsObject::_construct() +{ + + CDamageManager::_construct(); + return inherited::_construct(); +} \ No newline at end of file diff --git a/src/xrGameLA/DestroyablePhysicsObject.h b/src/xrGameLA/DestroyablePhysicsObject.h new file mode 100644 index 000000000..fc3f8d6f1 --- /dev/null +++ b/src/xrGameLA/DestroyablePhysicsObject.h @@ -0,0 +1,33 @@ +#pragma once + +class CDestroyablePhysicsObject : +public CPhysicObject, +public CPHDestroyable, +public CPHCollisionDamageReceiver, +public CHitImmunity, +public CDamageManager +{ +typedef CPhysicObject inherited; + float m_fHealth; + ref_sound m_destroy_sound; + shared_str m_destroy_particles; +public: + CDestroyablePhysicsObject () ; + virtual ~CDestroyablePhysicsObject () ; + virtual CPhysicsShellHolder* PPhysicsShellHolder () ; + virtual BOOL net_Spawn (CSE_Abstract* DC) ; + virtual void net_Destroy () ; + virtual void Hit (SHit* pHDS); + virtual void InitServerObject (CSE_Abstract* D) ; + virtual CPHCollisionDamageReceiver *PHCollisionDamageReceiver () {return static_cast(this);} + virtual DLL_Pure *_construct () ; + virtual CPhysicsShellHolder* cast_physics_shell_holder () {return this;} + virtual CParticlesPlayer* cast_particles_player () {return this;} + virtual CPHDestroyable* ph_destroyable () {return this;} + virtual void shedule_Update (u32 dt) ; + virtual bool CanRemoveObject () ; + virtual void OnChangeVisual (); +protected: + void Destroy () ; +private: +}; \ No newline at end of file diff --git a/src/xrGameLA/DisablingParams.cpp b/src/xrGameLA/DisablingParams.cpp new file mode 100644 index 000000000..c37b2524f --- /dev/null +++ b/src/xrGameLA/DisablingParams.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" +#include "DisablingParams.h" + + + +SAllDDWParams worldDisablingParams = + { + //object + { + {0.001f , 0.1f } , //translational vel , accel + {0.005f , 0.05f } , //rotational vel , accel + 64 //level2 frames 2^ + } , + 1.5f //reanable factor + }; + + + +void SOneDDOParams::Mul(float v) +{ + velocity *= v ; + acceleration *= v ; +} + +void SAllDDOParams::Reset() +{ + *this=worldDisablingParams.objects_params; +} + +void SAllDDOParams::Load(CInifile* ini) +{ + Reset() ; + if(!ini) return ; + if(!ini->section_exist("disable")) return ; + if(ini->line_exist("disable","linear_factor")) translational .Mul(ini->r_float("disable","linear_factor")) ; + if(ini->line_exist("disable","angular_factor")) rotational .Mul(ini->r_float("disable","angular_factor")) ; + if(ini->line_exist("disable","change_count")) + { + int ch_cnt=ini->r_s8("disable","change_count"); + if(ch_cnt<0)L2frames=L2frames >> u16(-ch_cnt); + else L2frames=L2frames << u16(ch_cnt); + VERIFY(ch_cnt<4 && L2frames!=0 ); + } +} \ No newline at end of file diff --git a/src/xrGameLA/DisablingParams.h b/src/xrGameLA/DisablingParams.h new file mode 100644 index 000000000..22ea1b57e --- /dev/null +++ b/src/xrGameLA/DisablingParams.h @@ -0,0 +1,29 @@ +#ifndef DISABLING_PARAMS_H +#define DISABLING_PARAMS_H +struct SOneDDOParams +{ + void Mul (float v) ; + float velocity ; + float acceleration ; +}; + +struct SAllDDOParams +{ + void Reset () ; + void Load (CInifile* ini) ; + SOneDDOParams translational ; + SOneDDOParams rotational ; + u16 L2frames ; +}; + +struct SAllDDWParams +{ + SAllDDOParams objects_params ; + float reanable_factor ; +}; + + + +extern SAllDDWParams worldDisablingParams ; + +#endif \ No newline at end of file diff --git a/src/xrGameLA/DummyArtifact.cpp b/src/xrGameLA/DummyArtifact.cpp new file mode 100644 index 000000000..9a74e7de2 --- /dev/null +++ b/src/xrGameLA/DummyArtifact.cpp @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////// +// DummyArtifact.cpp +// DummyArtefact - артефакт пустышка +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "DummyArtifact.h" +#include "PhysicsShell.h" + + +CDummyArtefact::CDummyArtefact(void) +{ +} + +CDummyArtefact::~CDummyArtefact(void) +{ +} + +void CDummyArtefact::Load(LPCSTR section) +{ + inherited::Load(section); +} + diff --git a/src/xrGameLA/DummyArtifact.h b/src/xrGameLA/DummyArtifact.h new file mode 100644 index 000000000..d9e578356 --- /dev/null +++ b/src/xrGameLA/DummyArtifact.h @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////// +// DummyArtifact.h +// DummyArtefact - артефакт пустышка +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CDummyArtefact : public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CDummyArtefact(void); + virtual ~CDummyArtefact(void); + + virtual void Load (LPCSTR section); + +protected: +}; \ No newline at end of file diff --git a/src/xrGameLA/DynamicHeightMap.cpp b/src/xrGameLA/DynamicHeightMap.cpp new file mode 100644 index 000000000..6509aee62 --- /dev/null +++ b/src/xrGameLA/DynamicHeightMap.cpp @@ -0,0 +1,191 @@ +// DynamicHeightMap.cpp: implementation of the CDynamicHeightMap class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "DynamicHeightMap.h" +#include "../cl_intersect.h" + +const int tasksPerFrame = 1; +const float limit_up = 100.f; +const float limit_down = 20.f; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CHM_Static::CHM_Static() +{ + // Initialize slots + Slot* slt = pool; + for (u32 i=0; ic_x) { + // scroll matrix to left + ++c_x; + for (int z=0; zbReady) { S->bReady = FALSE; task.push_back(S); } + for (int x=1; xset (c_x-dhm_line+dhm_matrix-1, c_z-dhm_line+z); + } + } else { + // scroll matrix to right + --c_x; + for (int z=0; zbReady) { S->bReady = FALSE; task.push_back(S); } + for (int x=dhm_matrix-1; x>0; --x) data[z][x] = data[z][x-1]; + data[z][0] = S; + S->set (c_x-dhm_line+0,c_z-dhm_line+z); + } + } + } + if (v_z!=c_z) { + if (v_z>c_z) { + // scroll matrix down a bit + ++c_z; + for (int x=0; xbReady) { S->bReady = FALSE; task.push_back(S); } + for (int z=dhm_matrix-1; z>0; --z) data[z][x] = data[z-1][x]; + data[0][x] = S; + S->set (c_x-dhm_line+x,c_z-dhm_line+0); + } + } else { + // scroll matrix up + --c_z; + for (int x=0; xbReady) { S->bReady = FALSE; task.push_back(S); } + for (int z=0; zset (c_x-dhm_line+x,c_z-dhm_line+dhm_matrix-1); + } + } + } + + // ***** perform TASKs + for (int taskid=0; (taskidbReady = TRUE; + + // Build BBox + Fbox bb; + bb.min.set (S->x*dhm_size, view.y-limit_down, S->z*dhm_size); + bb.max.set (bb.min.x+dhm_size, view.y+limit_up, bb.min.z+dhm_size); + bb.grow (EPS_L); + + // Select polygons + XRC.BBoxMode (0); // BBOX_TRITEST + XRC.BBoxCollide (precalc_identity,g_pGameLevel->ObjectSpace.GetStaticModel(),precalc_identity,bb); + u32 triCount = XRC.GetBBoxContactCount(); + if (0==triCount) { + S->clear (); + continue; + } + + // Cull polys + RAPID::tri* tris = g_pGameLevel->ObjectSpace.GetStaticTris(); + Fvector vecUP; vecUP.set(0,1,0); + for (u32 tid=0; tid=0) { + float y_test = pos.y - r_range; + if (y_test>ry) ry = y_test; + } + } + } + S->data[z][x] = ry; + } + } + } +} + +float CHM_Static::Query (float x, float z) +{ + // base slot + int v_x = iFloor(x/dhm_size); + int v_z = iFloor(z/dhm_size); + int dx = v_x - c_x; + int dz = v_z - c_z; + int gx = dx - dhm_line; clamp(gx,0,dhm_matrix-1); + int gz = dz - dhm_line; clamp(gz,0,dhm_matrix-1); + Slot* S = data[gz][gx]; + + // precision + float ostX = x-v_x*dhm_size; + float ostZ = z-v_z*dhm_size; + int px = iFloor(dhm_precision*ostX/dhm_size); clamp(px,0,dhm_precision-1); + int pz = iFloor(dhm_precision*ostZ/dhm_size); clamp(pz,0,dhm_precision-1); + return S->data [pz][px]; +} + +// +void CHM_Dynamic::Update () +{ +} +float CHM_Dynamic::Query (float x, float z) +{ + return flt_min; +} + +// +float CHeightMap::Query (float x, float z) +{ + if (dwFrame!=Device.dwFrame) + { + dwFrame = Device.dwFrame; + hm_static.Update (); + hm_dynamic.Update (); + } + float q1 = hm_static.Query(x,z); + float q2 = hm_dynamic.Query(x,z); + return _max(q1,q2); +} diff --git a/src/xrGameLA/DynamicHeightMap.h b/src/xrGameLA/DynamicHeightMap.h new file mode 100644 index 000000000..6ed264a86 --- /dev/null +++ b/src/xrGameLA/DynamicHeightMap.h @@ -0,0 +1,75 @@ +// DynamicHeightMap.h: interface for the CDynamicHeightMap class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_DYNAMICHEIGHTMAP_H__5A5BD0B8_1FC7_4067_A5A4_D40422E8B6D1__INCLUDED_) +#define AFX_DYNAMICHEIGHTMAP_H__5A5BD0B8_1FC7_4067_A5A4_D40422E8B6D1__INCLUDED_ +#pragma once + +const int dhm_line = 4; +const int dhm_matrix = (dhm_line+1+dhm_line); // 9x9 array +const float dhm_size = 4.f; // 4m per slot +const int dhm_precision = 16; // 32x32 subdivs per slot +const int dhm_total = dhm_matrix*dhm_matrix; + +// +class CHM_Static +{ + struct Slot + { + float data [dhm_precision][dhm_precision]; + + BOOL bReady; + int x,z; + + IC void set (int _x, int _z) { x=_x; z=_z; } + IC void clear () + { + for (u32 i=0; i task; + xr_vector polys; +public: + void Update (); + float Query (float x, float z); // 2D query + + CHM_Static (); +}; + +// +class CHM_Dynamic +{ +public: + void Update (); + float Query (float x, float z); // 2D query +}; + +// +class CHeightMap +{ + CHM_Static hm_static; + CHM_Dynamic hm_dynamic; + u32 dwFrame; +public: + float Query (float x, float z); // 2D query + Fvector Query (Fvector& pos, Fvector& dir); // 3D ray-query +}; + +#endif // !defined(AFX_DYNAMICHEIGHTMAP_H__5A5BD0B8_1FC7_4067_A5A4_D40422E8B6D1__INCLUDED_) diff --git a/src/xrGameLA/EffectorBobbing.cpp b/src/xrGameLA/EffectorBobbing.cpp new file mode 100644 index 000000000..dc80a70bd --- /dev/null +++ b/src/xrGameLA/EffectorBobbing.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" +#include "EffectorBobbing.h" + + +#include "actor.h" +#include "actor_defs.h" + + +#define BOBBING_SECT "bobbing_effector" + +#define CROUCH_FACTOR 0.75f +#define SPEED_REMINDER 5.f + + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CEffectorBobbing::CEffectorBobbing() : CEffectorCam(eCEBobbing,10000.f) +{ + fTime = 0; + fReminderFactor = 0; + is_limping = false; + + m_fAmplitudeRun = pSettings->r_float(BOBBING_SECT, "run_amplitude"); + m_fAmplitudeWalk = pSettings->r_float(BOBBING_SECT, "walk_amplitude"); + m_fAmplitudeLimp = pSettings->r_float(BOBBING_SECT, "limp_amplitude"); + + m_fSpeedRun = pSettings->r_float(BOBBING_SECT, "run_speed"); + m_fSpeedWalk = pSettings->r_float(BOBBING_SECT, "walk_speed"); + m_fSpeedLimp = pSettings->r_float(BOBBING_SECT, "limp_speed"); +} + +CEffectorBobbing::~CEffectorBobbing () +{ +} + +void CEffectorBobbing::SetState(u32 mstate, bool limping, bool ZoomMode){ + dwMState = mstate; + is_limping = limping; + m_bZoomMode = ZoomMode; +} + + +BOOL CEffectorBobbing::ProcessCam(SCamEffectorInfo& info) +{ + fTime += Device.fTimeDelta; + if (dwMState&ACTOR_DEFS::mcAnyMove){ + if (fReminderFactor<1.f) fReminderFactor += SPEED_REMINDER*Device.fTimeDelta; + else fReminderFactor = 1.f; + }else{ + if (fReminderFactor>0.f) fReminderFactor -= SPEED_REMINDER*Device.fTimeDelta; + else fReminderFactor = 0.f; + } + if (!fsimilar(fReminderFactor,0)){ + Fmatrix M; + M.identity (); + M.j.set (info.n); + M.k.set (info.d); + M.i.crossproduct(info.n, info.d); + M.c.set (info.p); + + // apply footstep bobbing effect + Fvector dangle; + float k = ((dwMState& ACTOR_DEFS::mcCrouch)?CROUCH_FACTOR:1.f); + + float A, ST; + + if(isActorAccelerated(dwMState, m_bZoomMode)) + { + A = m_fAmplitudeRun*k; + ST = m_fSpeedRun*fTime*k; + } + else if(is_limping) + { + A = m_fAmplitudeLimp*k; + ST = m_fSpeedLimp*fTime*k; + } + else + { + A = m_fAmplitudeWalk*k; + ST = m_fSpeedWalk*fTime*k; + } + + float _sinA = _abs(_sin(ST)*A)*fReminderFactor; + float _cosA = _cos(ST)*A*fReminderFactor; + + info.p.y += _sinA; + dangle.x = _cosA; + dangle.z = _cosA; + dangle.y = _sinA; + + Fmatrix R; + R.setHPB (dangle.x,dangle.y,dangle.z); + + Fmatrix mR; + mR.mul (M,R); + + info.d.set (mR.k); + info.n.set (mR.j); + } +// else{ +// fTime = 0; +// } + return TRUE; +} diff --git a/src/xrGameLA/EffectorBobbing.h b/src/xrGameLA/EffectorBobbing.h new file mode 100644 index 000000000..bc806c820 --- /dev/null +++ b/src/xrGameLA/EffectorBobbing.h @@ -0,0 +1,35 @@ +#ifndef _EFFECTOR_BOBBING_H +#define _EFFECTOR_BOBBING_H +#pragma once + +#include "CameraEffector.h" +#include "../cameramanager.h" + +class CEffectorBobbing : public CEffectorCam +{ + float fTime; + Fvector vAngleAmplitude; + float fYAmplitude; + float fSpeed; + + u32 dwMState; + float fReminderFactor; + bool is_limping; + bool m_bZoomMode; + + float m_fAmplitudeRun; + float m_fAmplitudeWalk; + float m_fAmplitudeLimp; + + float m_fSpeedRun; + float m_fSpeedWalk; + float m_fSpeedLimp; + +public: + CEffectorBobbing (); + virtual ~CEffectorBobbing (); + virtual BOOL ProcessCam (SCamEffectorInfo& info); + void SetState (u32 st, bool limping, bool ZoomMode); +}; + +#endif //_EFFECTOR_BOBBING_H diff --git a/src/xrGameLA/EffectorFall.cpp b/src/xrGameLA/EffectorFall.cpp new file mode 100644 index 000000000..de57bbd8a --- /dev/null +++ b/src/xrGameLA/EffectorFall.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "EffectorFall.h" +#include "CameraEffector.h" +#include "GamePersistent.h" + +#define FALL_SPEED 3.5f +#define FALL_MAXDIST 0.15f + +CEffectorFall::CEffectorFall(float power,float life_time) : CEffectorCam(eCEFall, life_time) +{ + fPower = (power>1)?1:((power<0)?0:power*power); + fPhase = 0; +} + + +BOOL CEffectorFall::ProcessCam(SCamEffectorInfo& info) +{ + fPhase+=FALL_SPEED*Device.fTimeDelta; + if (fPhase<1) + info.p.y-=FALL_MAXDIST*fPower*_sin(M_PI*fPhase+M_PI); + else + fLifeTime=-1; + return TRUE; +} + +CEffectorDOF::CEffectorDOF(const Fvector4& dof) +:CEffectorCam(eCEDOF, 100000) +{ + GamePersistent().SetEffectorDOF (Fvector().set(dof.x,dof.y,dof.z)); + m_fPhase = Device.fTimeGlobal + dof.w; +} + +BOOL CEffectorDOF::ProcessCam(SCamEffectorInfo& info) +{ + if (m_fPhase=0.f) + fAngleHorz -= relax_speed*Device.fTimeDelta; + else + fAngleHorz += relax_speed*Device.fTimeDelta; + + if (bSSActive) + { + if (fLastDeltaHorz >= 0.f) + fLastDeltaHorz -= relax_speed_l*Device.fTimeDelta; + else + fLastDeltaHorz += relax_speed_l*Device.fTimeDelta; + } +// VERIFY(_valid(fLastDeltaHorz)); + //------------------------------------------------------- + if (fAngleVert>=0.f){ + fAngleVert -= fRelaxSpeed*Device.fTimeDelta; + if (fAngleVert<0.f) bActive = FALSE; + }else{ + fAngleVert += fRelaxSpeed*Device.fTimeDelta; + if (fAngleVert>0.f) bActive = FALSE; + } + + if (bSSActive) + { + if (fLastDeltaVert>=0.f){ + fLastDeltaVert -= fRelaxSpeed*Device.fTimeDelta; + if (fLastDeltaVert<0.f) bSSActive = FALSE; + }else{ + fLastDeltaVert += fRelaxSpeed*Device.fTimeDelta; + if (fLastDeltaVert>0.f) bSSActive = FALSE; + } + }; + + //------------------------------------------------------- + if (!bActive){ + fAngleVert = 0.f; + fAngleHorz = 0.f; + m_LastSeed = 0; + bSSActive = FALSE; + } + //------------------------------------------------------- + if (!bSSActive) + { + fLastDeltaVert = 0.f; + fLastDeltaHorz = 0.f; + } +// VERIFY(_valid(fAngleVert)); +// VERIFY(_valid(fAngleHorz)); +// VERIFY(_valid(fLastDeltaHorz)); +// VERIFY(_valid(fLastDeltaVert)); + } +} + +void CWeaponShotEffector::Clear () +{ + bActive = false; + fAngleVert = 0.f; + fAngleHorz = 0.f; + m_LastSeed = 0; +}; + +void CWeaponShotEffector::GetDeltaAngle (Fvector &delta_angle) +{ + delta_angle.x = -fAngleVert; + delta_angle.y = -fAngleHorz; + delta_angle.z = 0.0f; +} + +void CWeaponShotEffector::GetLastDelta (Fvector& delta_angle) +{ + delta_angle.x = -fLastDeltaVert; + delta_angle.y = -fLastDeltaHorz; + delta_angle.z = 0.0f; +}; + +void CWeaponShotEffector::SetRndSeed (s32 Seed) +{ + if (m_LastSeed == 0){ + m_LastSeed = Seed; + m_Random.seed (Seed); + }; +}; + +void CWeaponShotEffector::ApplyLastAngles (float *pitch, float *yaw) +{ + *pitch -= fLastDeltaVert; + *yaw -= fLastDeltaHorz; +} +void CWeaponShotEffector::ApplyDeltaAngles (float *pitch, float *yaw) +{ + *pitch -= fAngleVert; + *yaw -= fAngleHorz; + +}; +//----------------------------------------------------------------------------- +// Camera shot effector +//----------------------------------------------------------------------------- +CCameraShotEffector::CCameraShotEffector(float max_angle, float relax_speed, float max_angle_horz, float step_angle_horz, float angle_frac) : CEffectorCam(eCEShot,100000.f) +{ + CWeaponShotEffector::Initialize(max_angle, relax_speed, max_angle_horz, step_angle_horz, angle_frac); + m_pActor = NULL; +} + +CCameraShotEffector::~CCameraShotEffector() +{ +} + +BOOL CCameraShotEffector::ProcessCam(SCamEffectorInfo& info) +{ + if (bActive){ + float h,p; + info.d.getHP (h,p); + if (bSingleShoot) + { + if (bSSActive) + info.d.setHP (h+fLastDeltaHorz,p+fLastDeltaVert); + } + else + info.d.setHP (h+fAngleHorz,p+fAngleVert); + + Update (); + } + return TRUE; +} + diff --git a/src/xrGameLA/EffectorShot.h b/src/xrGameLA/EffectorShot.h new file mode 100644 index 000000000..5d6534eec --- /dev/null +++ b/src/xrGameLA/EffectorShot.h @@ -0,0 +1,65 @@ +// EffectorShot.h: interface for the CCameraShotEffector class. +// +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "CameraEffector.h" +#include "../cameramanager.h" +#include "Actor.h" + +class CWeaponShotEffector{ +protected: + float fAngleVert; + float fAngleVertMax; + float fAngleVertFrac; + float fAngleHorz; + float fAngleHorzMax; + float fAngleHorzStep; + float fRelaxSpeed; + + float fLastDeltaVert; + float fLastDeltaHorz; +protected: + BOOL bActive; + BOOL bSingleShoot; + BOOL bSSActive; +private: + CRandom m_Random; + s32 m_LastSeed; +public: + CWeaponShotEffector (); + virtual ~CWeaponShotEffector(){}; + + void Initialize (float max_angle, float relax_speed, float max_angle_horz, float step_angle_horz, float angle_frac); + IC BOOL IsActive (){return bActive;} + virtual void SetActive (BOOL Active) {bActive = Active;}; + IC BOOL IsSingleShot (){return bSingleShoot;} + virtual void SetSingleShoot (BOOL Single) {bSingleShoot = Single;}; + void Update (); + + void SetRndSeed (s32 Seed); + + virtual void Shot (float angle); + virtual void GetDeltaAngle (Fvector& delta_angle); + virtual void GetLastDelta (Fvector& delta_angle); + virtual void Clear (); + + virtual void ApplyLastAngles (float *pitch, float *yaw); + virtual void ApplyDeltaAngles (float *pitch, float *yaw); +}; + +class CCameraShotEffector : public CWeaponShotEffector, public CEffectorCam +{ +protected: + CActor* m_pActor; +public: + CCameraShotEffector (float max_angle, float relax_speed, float max_angle_horz, float step_angle_horz, float angle_frac); + virtual ~CCameraShotEffector(); + + virtual BOOL ProcessCam (SCamEffectorInfo& info); + + virtual void SetActor (CActor* pActor) {m_pActor = pActor;}; + + virtual CCameraShotEffector* cast_effector_shot () {return this;} +}; \ No newline at end of file diff --git a/src/xrGameLA/EffectorShotX.cpp b/src/xrGameLA/EffectorShotX.cpp new file mode 100644 index 000000000..673333302 --- /dev/null +++ b/src/xrGameLA/EffectorShotX.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "EffectorShotX.h" +#include "..\CameraBase.h" + +CCameraShotEffectorX::CCameraShotEffectorX (float max_angle, float relax_speed, + float max_angle_horz, float step_angle_horz, float angle_frac) : CCameraShotEffector(max_angle, relax_speed, max_angle_horz, step_angle_horz, angle_frac) +{ +} + +CCameraShotEffectorX::~CCameraShotEffectorX () +{ +} + +BOOL CCameraShotEffectorX::ProcessCam(SCamEffectorInfo& info) +{ + return TRUE; +}; + +void CCameraShotEffectorX::GetDeltaAngle (Fvector& delta_angle) +{ + delta_angle.x = 0.0f; + delta_angle.y = 0.0f; + delta_angle.z = 0.0f; +} + +void CCameraShotEffectorX::Shot (float angle) +{ + float fAC_Old = fAngleVert; + float fAH_Old = fAngleHorz; + inherited::Shot(angle); + float dAC = fAngleVert - fAC_Old; + float dAH = fAngleHorz - fAH_Old; + + UpdateActorCamera(-dAC, -dAH); +} + +void CCameraShotEffectorX::Clear () +{ + fAngleVert = -EPS_S; + fAngleHorz = 0.0f; +}; + +void CCameraShotEffectorX::UpdateActorCamera (float dPitch, float dYaw) +{ + if (!m_pActor) return; + + CCameraBase* pACam = m_pActor->cam_FirstEye(); + if (!pACam) return; + + if (pACam->bClampPitch) + { + while (pACam->pitch < pACam->lim_pitch[0]) + pACam->pitch += PI_MUL_2; + while (pACam->pitch > pACam->lim_pitch[1]) + pACam->pitch -= PI_MUL_2; + }; + + pACam->pitch += dPitch; + pACam->yaw += dYaw; + + if (pACam->bClampYaw) clamp(pACam->yaw,pACam->lim_yaw[0],pACam->lim_yaw[1]); + if (pACam->bClampPitch) clamp(pACam->pitch,pACam->lim_pitch[0],pACam->lim_pitch[1]); + +} \ No newline at end of file diff --git a/src/xrGameLA/EffectorShotX.h b/src/xrGameLA/EffectorShotX.h new file mode 100644 index 000000000..b0cf24c75 --- /dev/null +++ b/src/xrGameLA/EffectorShotX.h @@ -0,0 +1,18 @@ +#pragma once + +#include "EffectorShot.h" + +class CCameraShotEffectorX : public CCameraShotEffector +{ + typedef CCameraShotEffector inherited; +public: + CCameraShotEffectorX(float max_angle, float relax_time, float max_angle_horz, float step_angle_horz, float angle_frac = 0.7f); + virtual ~CCameraShotEffectorX(); + + virtual BOOL ProcessCam (SCamEffectorInfo& info); + virtual void GetDeltaAngle (Fvector& delta_angle); + virtual void Shot (float angle); + virtual void Clear (); +protected: + virtual void UpdateActorCamera (float dPitch, float dYaw); +}; \ No newline at end of file diff --git a/src/xrGameLA/EffectorZoomInertion.cpp b/src/xrGameLA/EffectorZoomInertion.cpp new file mode 100644 index 000000000..40fead7c5 --- /dev/null +++ b/src/xrGameLA/EffectorZoomInertion.cpp @@ -0,0 +1,236 @@ +// EffectorZoomInertion.cpp: инерция(покачивания) оружия в режиме +// приближения +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "EffectorZoomInertion.h" + + +#define EFFECTOR_ZOOM_SECTION "zoom_inertion_effector" +#define CAMERA_MOVE_DELTA_TIME (0.150f) // delay to react when camera stopped (to compensate for inconsistent camera angular distance between frames on high FPS) + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CEffectorZoomInertion::CEffectorZoomInertion () : CEffectorCam(eCEZoom,100000.f) +{ + Load(); + SetRndSeed (Device.dwTimeContinual); + m_dwTimePassed = (u32)-1; + m_fTimeCameraMove = 0.f; + m_bCameraMoving = false; + m_enabled = false; +} + +CEffectorZoomInertion::~CEffectorZoomInertion () +{ + +} + +template +IC T LoadParamInternal(LPCSTR section, LPCSTR prefix, LPCSTR param, T (CInifile::*method)(LPCSTR, LPCSTR)const, T defVal) +{ + static string256 full_name; + strconcat(sizeof(full_name), full_name, prefix, param); + if (pSettings->line_exist(section, full_name)) + { + return (pSettings->*method)(section, full_name); + } + else if (pSettings->line_exist(EFFECTOR_ZOOM_SECTION, param)) + { + return (pSettings->*method)(EFFECTOR_ZOOM_SECTION, param); + } + return defVal; +} + +void CEffectorZoomInertion::LoadParams(LPCSTR section, LPCSTR prefix) +{ + m_fCameraSpeedThreshold = LoadParam(section, prefix, "camera_speed_threshold", 1.f); + m_fDispEpsilon = LoadParam(section, prefix, "disp_epsilon", 0.1f); + m_fDispMin = LoadParam(section, prefix, "disp_min", 0.0f); + m_fZoomAimingDispK = LoadParam(section, prefix, "zoom_aim_disp_k", 1.f); + m_fDispHorzCoef = LoadParam(section, prefix, "disp_horz_koef", 1.f); + m_dwDeltaTime = LoadParam (section, prefix, "delta_time", 1000); +} + +template <> +float CEffectorZoomInertion::LoadParam(LPCSTR section, LPCSTR prefix, LPCSTR param, float defVal) +{ + return LoadParamInternal(section, prefix, param, &CInifile::r_float, defVal); +} + +template <> +u32 CEffectorZoomInertion::LoadParam(LPCSTR section, LPCSTR prefix, LPCSTR param, u32 defVal) +{ + return LoadParamInternal(section, prefix, param, &CInifile::r_u32, defVal); +} + +void CEffectorZoomInertion::Load() +{ + LoadParams(EFFECTOR_ZOOM_SECTION, ""); + + m_fDispRadius = m_fOldDispRadius = m_fDispMin; + + m_vCurrentPoint.set(0.f,0.f,0.f); + m_vTargetPoint.set(0.f,0.f,0.f); + m_vLastPoint.set(0.f,0.f,0.f); + m_vOldCameraDir.set(0.f,0.f,0.f); +} + +void CEffectorZoomInertion::Init(CWeaponMagazined* pWeapon) +{ + if (!pWeapon) return; + + LoadParams(*pWeapon->cNameSect(), "ezi_"); +} + +void CEffectorZoomInertion::Enable(bool flag, float rotateTime) +{ + if (flag && !m_enabled) + { + m_enabled = true; + if (!IsFirstUpdate()) + { + CalcNextPoint(); + } + } + else if (!flag && m_enabled) + { + m_dwTimePassed = 0; + m_vLastPoint = m_vCurrentPoint; + m_vTargetPoint.set(0.f, 0.f, 0.f); + m_dwCenterDeltaTime = !fis_zero(rotateTime) ? (u32)(rotateTime * 1000) : m_dwDeltaTime; + m_enabled = false; + } +} + +void CEffectorZoomInertion::SetParams (float disp) +{ + m_fDispRadius = disp * m_fZoomAimingDispK; + if (m_fDispRadius < m_fDispMin) + m_fDispRadius = m_fDispMin; + + if (IsFirstUpdate()) + { + m_fOldDispRadius = m_fDispRadius; + } + else if (!fsimilar(m_fOldDispRadius, m_fDispRadius, m_fDispEpsilon) && m_enabled && !IsCameraMoving()) + { + // Msg("EZI: Disp changed. Old: %.2f, new: %.2f, eps: %.5f", m_fOldDispRadius, m_fDispRadius, m_fDispEpsilon); + CalcNextPoint(); + m_fOldDispRadius = m_fDispRadius; + } +} + + +void CEffectorZoomInertion::CalcNextPoint () +{ + m_dwTimePassed = 0; + m_vLastPoint = m_vCurrentPoint; + + float vertMax = m_fDispRadius/2.f; + float horzMax = vertMax * m_fDispHorzCoef; + m_vTargetPoint.x = m_Random.randF(-horzMax, horzMax); + m_vTargetPoint.y = m_Random.randF(-vertMax, vertMax); + + //Msg("EZI: Next point (%.4f, %.4f)", m_vTargetPoint.x, m_vTargetPoint.y); +} + +void CEffectorZoomInertion::ApplyPoint(Fvector& dest, const Fvector& pt) +{ + float h, p; + dest.getHP(h, p); + dest.setHP(h + pt.x, p + pt.y); +} + +static float SmoothStep(float from, float to, float t) +{ + clamp(t, 0.f, 1.f); + t = -2.f * t * t * t + 3.f * t * t; + return to * t + from * (1.f - t); +} + +//определяем двигал ли прицелом актер +bool CEffectorZoomInertion::UpdateCameraMoved(const Fvector& camDir, bool& justStopped) +{ + justStopped = false; + + // check camera speed only once in a while + if (m_fTimeCameraMove < CAMERA_MOVE_DELTA_TIME) + { + m_fTimeCameraMove += Device.fTimeDelta; + } + else + { + Fvector lastDir = m_vOldCameraDir, curDir = camDir; + float dist = camDir.distance_to(m_vOldCameraDir); + float cameraSpeed = camDir.distance_to(m_vOldCameraDir) / m_fTimeCameraMove; + + if (cameraSpeed > m_fCameraSpeedThreshold) + { + // camera moved + if (!m_bCameraMoving) + { + // Msg("EZI: Camera started moving with speed %.3f (max %.3f), dist: %.4f, time: %.4f, dir: (%.4f,%.4f,%.4f)->(%.4f,%.4f,%.4f)", + // cameraSpeed, m_fCameraSpeedThreshold, dist, m_fTimeCameraMove, lastDir.x, lastDir.y, lastDir.z, curDir.x, curDir.y, curDir.z); + + m_bCameraMoving = true; + } + } + else if (m_bCameraMoving) + { + //Msg("EZI: Camera stopped moving: speed %.3f, dist: %.4f, fDelta: %.4f, dir: (%.4f,%.4f,%.4f)->(%.4f,%.4f,%.4f)", + // cameraSpeed, dist, m_fTimeCameraMove, lastDir.x, lastDir.y, lastDir.z, curDir.x, curDir.y, curDir.z); + + m_bCameraMoving = false; + justStopped = true; + } + + m_fTimeCameraMove = 0.f; + m_vOldCameraDir = camDir; + } + + return m_bCameraMoving; +} + +BOOL CEffectorZoomInertion::ProcessCam(SCamEffectorInfo& info) +{ + if (!m_enabled) + { + // transition to center + if (m_dwTimePassed < m_dwCenterDeltaTime) + { + m_vCurrentPoint.lerp(m_vLastPoint, m_vTargetPoint, SmoothStep(0.f, 1.f, float(m_dwTimePassed)/m_dwCenterDeltaTime)); + ApplyPoint(info.d, m_vCurrentPoint); + m_dwTimePassed += Device.dwTimeDelta; + } + return TRUE; + } + + // check if camera is moving + bool justStopped; + if (UpdateCameraMoved(info.d, justStopped)) + { + ApplyPoint(info.d, m_vCurrentPoint); + return TRUE; + } + else if (justStopped) + { + // force next point after camera stopped moving + m_dwTimePassed = m_dwDeltaTime; + } + + if (m_dwTimePassed >= m_dwDeltaTime) + { + CalcNextPoint(); + } + + m_vCurrentPoint.lerp(m_vLastPoint, m_vTargetPoint, SmoothStep(0.f, 1.f, float(m_dwTimePassed)/m_dwDeltaTime)); + + ApplyPoint(info.d, m_vCurrentPoint); + + m_dwTimePassed += Device.dwTimeDelta; + + return TRUE; +} diff --git a/src/xrGameLA/EffectorZoomInertion.h b/src/xrGameLA/EffectorZoomInertion.h new file mode 100644 index 000000000..70b80c6db --- /dev/null +++ b/src/xrGameLA/EffectorZoomInertion.h @@ -0,0 +1,68 @@ +// EffectorZoomInertion.h: инерция(покачивания) оружия в режиме +// приближения +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "CameraEffector.h" +#include "../cameramanager.h" +#include "WeaponMagazined.h" + +class CEffectorZoomInertion : public CEffectorCam +{ + bool m_enabled; + float m_fDispRadius; + float m_fOldDispRadius; + + Fvector m_vCurrentPoint; + Fvector m_vLastPoint; + Fvector m_vTargetPoint; + + Fvector m_vOldCameraDir; + + u32 m_dwTimePassed; + float m_fTimeCameraMove; + bool m_bCameraMoving; + + //параметры настройки эффектора + float m_fCameraSpeedThreshold; + float m_fDispEpsilon; + float m_fDispMin; + float m_fDispHorzCoef; + float m_fZoomAimingDispK; + //время через которое эффектор меняет направление движения + u32 m_dwDeltaTime; + u32 m_dwCenterDeltaTime; + + CRandom m_Random; + + void CalcNextPoint (); + void LoadParams (LPCSTR Section, LPCSTR Prefix); + IC bool IsFirstUpdate () { return m_dwTimePassed == (u32)-1; } + IC bool IsCameraMoving () { return m_bCameraMoving; } + + template + T LoadParam (LPCSTR section, LPCSTR prefix, LPCSTR param, T devFal) = delete; + + template <> + float LoadParam (LPCSTR section, LPCSTR prefix, LPCSTR param, float devFal); + template <> + u32 LoadParam (LPCSTR section, LPCSTR prefix, LPCSTR param, u32 devFal); + void ApplyPoint (Fvector& dest, const Fvector& point); + bool UpdateCameraMoved (const Fvector& currentDir, bool& justStopped); + +public: + CEffectorZoomInertion (); + virtual ~CEffectorZoomInertion (); + + void Load (); + void SetParams (float disp); + + virtual BOOL ProcessCam (SCamEffectorInfo& info); + virtual void SetRndSeed (s32 Seed) { m_Random.seed(Seed); }; + virtual void Init (CWeaponMagazined* pWeapon); + virtual void Enable (bool flag, float rotateTime = 0.f); + virtual bool Enabled () { return m_enabled; } + + virtual CEffectorZoomInertion* cast_effector_zoom_inertion () {return this;} +}; \ No newline at end of file diff --git a/src/xrGameLA/ElectricBall.cpp b/src/xrGameLA/ElectricBall.cpp new file mode 100644 index 000000000..0152efd8a --- /dev/null +++ b/src/xrGameLA/ElectricBall.cpp @@ -0,0 +1,29 @@ +/////////////////////////////////////////////////////////////// +// ElectricBall.cpp +// ElectricBall - артефакт электрический шар +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ElectricBall.h" +#include "PhysicsShell.h" + + +CElectricBall::CElectricBall(void) +{ +} + +CElectricBall::~CElectricBall(void) +{ +} + +void CElectricBall::Load(LPCSTR section) +{ + inherited::Load(section); +} + +void CElectricBall::UpdateCLChild () +{ + inherited::UpdateCLChild(); + + if(H_Parent()) XFORM().set(H_Parent()->XFORM()); +}; \ No newline at end of file diff --git a/src/xrGameLA/ElectricBall.h b/src/xrGameLA/ElectricBall.h new file mode 100644 index 000000000..b4cc403d1 --- /dev/null +++ b/src/xrGameLA/ElectricBall.h @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////// +// ElectricBall.h +// ElectricBall - артефакт электрический шар +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CElectricBall : public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CElectricBall(void); + virtual ~CElectricBall(void); + + virtual void Load (LPCSTR section); + +protected: + virtual void UpdateCLChild (); + +}; \ No newline at end of file diff --git a/src/xrGameLA/ElevatorState.cpp b/src/xrGameLA/ElevatorState.cpp new file mode 100644 index 000000000..92fd50437 --- /dev/null +++ b/src/xrGameLA/ElevatorState.cpp @@ -0,0 +1,388 @@ +#include "stdafx.h" +#include "ElevatorState.h" +#include "ClimableObject.h" +#include "PHCharacter.h" +#include "MathUtils.h" +#include "PHWorld.h" +#ifdef DEBUG +#include "../Statgraph.h" +#include "PHDebug.h" +#endif +static const float getting_on_dist =0.3f; +static const float getting_out_dist =0.4f; +static const float start_climbing_dist =0.f; +static const float stop_climbing_dist =0.1f; +static const float out_dist =1.5f; + +static const float look_angle_cosine =0.9238795f;//22.5 +static const float lookup_angle_sine =0.34202014f;//20 +extern class CPHWorld *ph_world; +CElevatorState::CElevatorState() +{ + m_state=clbNoLadder; + m_ladder=NULL; + m_character=NULL; +} + +float CElevatorState::ClimbDirection() +{ + VERIFY(m_ladder&&m_character); + Fvector d; + m_ladder->DToPlain(m_character,d); + float dir=m_character->ControlAccel().dotproduct(d); + if(dir>EPS_L)dir*=(m_character->CamDir().y+lookup_angle_sine); + return dir; +} + +void CElevatorState::PhTune(float step) +{ + VERIFY(m_character&&m_character->b_exist&&m_character->is_active()); + if(!m_ladder) return; + switch(m_state) + { + case clbNone :UpdateStNone() ; break; + case clbNearUp :UpdateStNearUp() ; break; + case clbNearDown :UpdateStNearDown() ; break; + case clbClimbingUp :UpdateStClimbingUp() ; break; + case clbClimbingDown :UpdateStClimbingDown() ; break; + case clbDepart :UpdateDepart() ; break; + case clbNoLadder :m_ladder = NULL ; break; + } + +} + +void CElevatorState::PhDataUpdate(float step) +{ + +} + +void CElevatorState::InitContact(dContact* c,bool &do_collide,u16 ,u16 ) +{ + +} + +void CElevatorState::SetElevator(CClimableObject* climable) +{ + Fvector d; + float dist=climable->DDToAxis(m_character,d); + if(m_ladder==climable||dist>out_dist) return; + if(m_ladder && m_ladder->DDToAxis(m_character,d)get_body(),0); + + if((new_state!=clbClimbingUp&&new_state!=clbClimbingDown) && + (m_state==clbClimbingUp||m_state==clbClimbingDown) + )dBodySetGravityMode(m_character->get_body(),1); + + //if(new_state==clbDepart) InitDepart(); + NewState(); + m_state=new_state; +} +void CElevatorState::UpdateStNone() +{ + VERIFY(m_ladder&&m_character); + Fvector d;m_ladder->DToPlain(m_character,d); + if(m_ladder->BeforeLadder(m_character)&&m_ladder->InTouch(m_character)&&dXZDotNormalized(d,m_character->CamDir())>look_angle_cosine) + { + + if(ClimbDirection()>0.f) + { + SwitchState(clbClimbingUp); + } + else + { + SwitchState(clbClimbingDown); + } + } + else + { + Fvector temp; + float d_to_lower=m_ladder->DDLowerP(m_character,temp),d_to_upper=m_ladder->DDUpperP(m_character,temp); + if(d_to_lowerFootRadius() > d_to_lower) + SwitchState(clbNearDown); + } + else + { + if(getting_on_dist+m_character->FootRadius() > d_to_upper) + SwitchState(clbNearUp); + } + } +} + +void CElevatorState::UpdateStNearUp() +{ + VERIFY(m_ladder&&m_character); + Fvector d; + + if( m_ladder->InTouch(m_character) && + m_character->CamDir().y<-M_PI/20.f && + //d.dotproduct(m_character->ControlAccel())<0.f&& + //ClimbDirection()<0.f&& + m_ladder->DDToPlain(m_character,d)>m_character->FootRadius()/3.f&& + m_ladder->BeforeLadder(m_character,0.1f) + ) + SwitchState(clbClimbingDown); + float dist=m_ladder->DDUpperP(m_character,d); + if(dist-m_character->FootRadius()>out_dist)SwitchState((clbNoLadder)); +} + +void CElevatorState::UpdateStNearDown() +{ + VERIFY(m_ladder&&m_character); + Fvector d; + float dist=m_ladder->DDLowerP(m_character,d); + if( m_ladder->InTouch(m_character)&& + dXZDotNormalized(d,m_character->CamDir())>look_angle_cosine&& + d.dotproduct(m_character->ControlAccel())>0.f&& + ClimbDirection()>0.f&& + m_ladder->BeforeLadder(m_character) + )SwitchState(clbClimbingUp); + if(dist-m_character->FootRadius()>out_dist)SwitchState((clbNoLadder)); +} + + +void CElevatorState::UpdateStClimbingDown() +{ + VERIFY(m_ladder&&m_character); + Fvector d; + + if(ClimbDirection()>0.f&&m_ladder->BeforeLadder(m_character)) + SwitchState(clbClimbingUp); + float to_ax=m_ladder->DDToAxis(m_character,d); + Fvector ca;ca.set(m_character->ControlAccel()); + float control_a=to_mag_and_dir(ca); + if(!fis_zero(to_ax)&&!fis_zero(control_a)&&abs(-ca.dotproduct(Fvector(m_ladder->Norm()).normalize()))AxDistToLowerP(m_character)-m_character->FootRadius()AxDistToUpperP(m_character)<-m_character->FootRadius())SwitchState(clbNoLadder); + + Fvector vel; + m_character->GetVelocity(vel); + if(vel.y>EPS_S) + { + m_character->ApplyForce(0.f,-m_character->Mass()*ph_world->Gravity(),0.f); + } + //if(to_ax-m_character->FootRadius()>out_dist) + // SwitchState((clbNone)); + //if(fis_zero(control_a)) + // m_character->ApplyForce(d,m_character->Mass()); +} + +void CElevatorState::UpdateStClimbingUp() +{ + VERIFY(m_ladder&&m_character); + Fvector d; + + if(ClimbDirection()<0.f&&m_ladder->BeforeLadder(m_character)) + SwitchState(clbClimbingDown); + float to_ax=m_ladder->DDToAxis(m_character,d); + Fvector ca;ca.set(m_character->ControlAccel()); + float control_a=to_mag_and_dir(ca); + if(!fis_zero(to_ax)&&!fis_zero(control_a)&&abs(-ca.dotproduct(Fvector(m_ladder->Norm()).normalize()))AxDistToUpperP(m_character)+m_character->FootRadius()FootRadius()>out_dist) + // SwitchState((clbNone)); + //if(fis_zero(control_a)) + // m_character->ApplyForce(d,m_character->Mass()); +} +void CElevatorState::UpdateClimbingCommon(const Fvector &d_to_ax,float to_ax,const Fvector& control_accel,float ca) +{ + VERIFY(m_ladder&&m_character); + if(to_ax-m_character->FootRadius()>out_dist) + SwitchState((clbNoLadder)); + if(fis_zero(ca)&&d_to_ax.dotproduct(m_ladder->Norm())<0.f) + { +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgLadder)) + { +//. Msg("force applied"); + } +#endif + m_character->ApplyForce(d_to_ax,m_character->Mass()*ph_world->Gravity());// + + } +} +bool CElevatorState::GetControlDir(Fvector& dir) +{ + bool ret=true; + VERIFY(m_ladder&&m_character); + Fvector d; + float dist; + switch(m_state) + { + case clbDepart : + case clbNoLadder : + case clbNone : break; + case clbNearUp : dist= m_ladder->DDUpperP(m_character,d); + if( dXZDotNormalized(d,m_character->CamDir())>look_angle_cosine&& + !fis_zero(dist,EPS_L)&&m_character->ControlAccel().dotproduct(d)>0.f) dir.set(d); + break; + case clbNearDown : + dist=m_ladder->DDLowerP(m_character,d); + if(dXZDotNormalized(d,m_character->CamDir())>look_angle_cosine&& + !fis_zero(dist,EPS_L)&&m_character->ControlAccel().dotproduct(d)>0.f) dir.set(d); + break; + case clbClimbingUp : m_ladder->DDAxis(dir); + m_ladder->DDToAxis(m_character,d); + dir.add(d);dir.normalize(); + break; + case clbClimbingDown : m_ladder->DDToAxis(m_character,d); + if(m_ladder->BeforeLadder(m_character)||d.dotproduct(dir)>0.f) + { + m_ladder->DDAxis(dir); + dir.invert(); + dir.add(d);dir.normalize(); + } + else + { +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgLadder)) + { + Msg("no c dir"); + } +#endif + ret=false; + } + break; + } + return ret; +} +static const float depart_dist=2.f; +static const u32 depart_time=3000; +void CElevatorState::UpdateDepart() +{ + VERIFY(m_ladder&&m_character); + Fvector temp; + float d_to_lower=m_ladder->DDLowerP(m_character,temp),d_to_upper=m_ladder->DDUpperP(m_character,temp); + if(d_to_lowerFootRadius() > d_to_lower) + SwitchState(clbNearDown); + } + else + { + if(getting_on_dist+m_character->FootRadius() > d_to_upper) + SwitchState(clbNearUp); + } + + //Fvector p;m_character->GetFootCenter(p); + //p.sub(m_start_position); + //if( p.magnitude()>depart_dist || + // Device.dwTimeGlobal-m_start_time>depart_time) + SwitchState(clbNoLadder); + +} + +void CElevatorState::NewState() +{ + VERIFY(m_character); + m_start_time=Device.dwTimeGlobal; + m_character->GetFootCenter(m_start_position); +} + +void CElevatorState::Depart() +{ + VERIFY(m_character); + if(m_ladder && ClimbingState())SwitchState(clbDepart); +} +void CElevatorState::GetLeaderNormal(Fvector& dir) +{ + if(!m_ladder) return; + VERIFY(m_ladder&&m_character); + m_ladder->DDNorm(dir); + //Fvector d; + //m_ladder->DToAxis(m_character,d); + //if(dir.dotproduct(d)>0.f) dir.invert(); +} + +void CElevatorState::GetJumpDir(const Fvector& accel,Fvector& dir) +{ + VERIFY(m_ladder&&m_character); + Fvector norm,side; + m_ladder->DDNorm(norm); + m_ladder->DDSide(side); + Fvector ac;ac.set(accel).normalize_safe(); + float side_component=ac.dotproduct(side); + dir.set(norm); + if(_abs(side_component)>M_SQRT1_2) + { + if(side_component<0.f)side.invert(); + dir.add(side); + dir.normalize_safe(); + } +} + +void CElevatorState::Deactivate() +{ + SwitchState(clbNoLadder); + m_state=clbNoLadder; + m_ladder=NULL; + m_character=NULL; +} + + + +CElevatorState::SEnertionState CElevatorState:: m_etable[CElevatorState::clbNoState][CElevatorState::clbNoState]= +{ +// clbNone clbNearUp clbNearDown clbClimbingUp clbClimbingDown clbDepart clbNoLadder +/*clbNone */ {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}, //clbNone +/*clbNearUp */ {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}, //clbNearUp +/*clbNearDown */ {{0,0}, {0.0f,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}, //clbNearDown +/*clbClimbingUp */ {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}, //clbClimbingUp +/*clbClimbingDown */ {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}, //clbClimbingDown +/*clbDepart */ {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {depart_dist,depart_time}}, //clbDepart +/*clbNoLadder */ {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}} //clbNoLadder +}; + +bool CElevatorState::StateSwitchInertion(Estate new_state) +{ + Fvector p;m_character->GetFootCenter(p); + p.sub(m_start_position); + if(m_etable[m_state][new_state].distPosition()); + feel_touch_update(P, fortestrangetocheck); + + + for (xr_vector::iterator it = feel_touch.begin(); it != feel_touch.end(); it++) + { + CObject* item = *it; + if (item){ + Fvector dir, to; + + CInventoryItem* invitem = smart_cast(item); + item->Center(to); + float range = dir.sub(to, this->Position()).magnitude(); + Msg("Artefact %s, distance is %f", invitem->object().cNameSect().c_str(), range); + } + } + } +} + +//---------------------------------------------------------------------- +void CEliteDetector::feel_touch_new(CObject* O) +{ +} + +void CEliteDetector::feel_touch_delete(CObject* O) +{ +} + +BOOL CEliteDetector::feel_touch_contact(CObject *O) +{ + + + CArtefact* artefact = smart_cast(O); + + if (artefact) + { + for (u16 id = 0; id < af_types.size(); ++id) + { + if ((xr_strcmp(O->cNameSect(), af_types[id]) == 0) || (xr_strcmp(af_types[id], "all") == 0)) + { + return true; + } + } + return false; + } + else{ return false; } + +} + +//---------------------------------------------------------------------- + +void CEliteDetector::UpdateAf() +{ + ui().Clear(); + CArtefact* pCurrentAf; + LPCSTR closest_art = "null"; + feel_touch_update_delay = feel_touch_update_delay + 1; + if (feel_touch_update_delay >= 5) //Как то снизить нагрузку на кадр поможет + { + feel_touch_update_delay = 0; + Fvector P; + P.set(this->Position()); + feel_touch_update(P, foverallrangetocheck); + } + + float disttoclosestart = 0.0; + + CArtefact* arttemp; + for (xr_vector::iterator it = feel_touch.begin(); it != feel_touch.end(); it++) + { + + float disttoart = DetectorFeel(*it); + if (disttoart != -10.0) + { + //-----------Все арты, в рудиусе действия, из списка фил тач - идут на экран + arttemp = smart_cast(*it); + ui().RegisterItemToDraw(arttemp->Position(), "af_sign"); + + //Если переменная досих пор не заданаа(первый проход цикла), то даем ей значение + if (disttoclosestart <= 0.0) + { + disttoclosestart = disttoart; + closest_art = closestart; + pCurrentAf = arttemp; + } + //нашли более близкий арт... + if (disttoclosestart > disttoart) + { + disttoclosestart = disttoart; + closest_art = closestart; + pCurrentAf = arttemp; + } + } + } + + if (!reaction_sound_off) + { + //определить текущую частоту срабатывания сигнала + if (disttoclosestart == 0.0) + { + return; + } + else + { + cur_periodperiod = disttoclosestart / (fdetect_radius * pCurrentAf->detect_radius_koef); + } + + //Чтобы не перегружать звук. движок + if (cur_periodperiod < 0.11) + { + cur_periodperiod = 0.11; + } + + if (snd_timetime > cur_periodperiod && detect_sndsnd_line || pCurrentAf->custom_detect_sound_string) + { + //Добавил врзможность задать разные звуки для различных артов + freqq = 1.8 - cur_periodperiod; + + if (freqq < 0.8) + freqq = 0.8; + + if (pCurrentAf->custom_detect_sound_string) + { + pCurrentAf->custom_detect_sound.play_at_pos(this, this->Position()); + pCurrentAf->custom_detect_sound.set_frequency(freqq); + } + else if (detect_sndsnd_line) + { + detect_snd.play_at_pos(this, this->Position()); + detect_snd.set_frequency(freqq); + } + + snd_timetime = 0; + } + else + snd_timetime += Device.fTimeDelta; + } +} + +//Просто для удобства вынес в отдельную функцию, а то и так месево там + +float CEliteDetector::DetectorFeel(CObject* item) +{ + Fvector dir, to; + + item->Center(to); + float range = dir.sub(to, Position()).magnitude(); + CInventoryItem* invitem = smart_cast(item); + CArtefact* artefact = smart_cast(item); + + float gogogo = fdetect_radius * artefact->detect_radius_koef; + if (rangeobject().cNameSect_str(), invitem->object().cNameSect().c_str()); + closestart = invitem->object().cNameSect_str(); + return range; + } + return -10.0; +} + +//---------------UI(Экранчик артов)------------------ + +bool CEliteDetector::render_item_3d_ui_query() +{ + return IsWorking(); +} + +void CEliteDetector::render_item_3d_ui() +{ + R_ASSERT(HudItemData()); + inherited::render_item_3d_ui(); + ui().Draw(); + // Restore cull mode + UIRender->CacheSetCullMode(IUIRender::cmCCW); +} + +void fix_ws_wnd_size(CUIWindow* w, float kx) +{ + Fvector2 p = w->GetWndSize(); + p.x /= kx; + w->SetWndSize(p); + + p = w->GetWndPos(); + p.x /= kx; + w->SetWndPos(p); + + CUIWindow::WINDOW_LIST::iterator it = w->GetChildWndList().begin(); + CUIWindow::WINDOW_LIST::iterator it_e = w->GetChildWndList().end(); + + for (; it != it_e; ++it) + { + CUIWindow* w2 = *it; + fix_ws_wnd_size(w2, kx); + } +} + +CUIArtefactDetectorElite& CEliteDetector::ui() +{ + return *((CUIArtefactDetectorElite*)m_ui); +} + + +void CUIArtefactDetectorElite::construct(CEliteDetector* p) +{ + m_parent = p; + CUIXml uiXml; + uiXml.Load(CONFIG_PATH, UI_PATH, "ui_detector_artefact.xml"); + + CUIXmlInit xml_init; + string512 buff; + xr_strcpy(buff, p->ui_xml_tag()); + + xml_init.InitWindow(uiXml, buff, 0, this); + + m_wrk_area = new CUIWindow(); + + xr_sprintf(buff, "%s:wrk_area", p->ui_xml_tag()); + + xml_init.InitWindow(uiXml, buff, 0, m_wrk_area); + m_wrk_area->SetAutoDelete(true); + AttachChild(m_wrk_area); + xr_sprintf(buff, "%s", p->ui_xml_tag()); + int num = uiXml.GetNodesNum(buff, 0, "palette"); + XML_NODE* pStoredRoot = uiXml.GetLocalRoot(); + uiXml.SetLocalRoot(uiXml.NavigateToNode(buff, 0)); + for (int idx = 0; idxSetAutoDelete(true); + m_wrk_area->AttachChild(S); + S->SetCustomDraw(true); + } + uiXml.SetLocalRoot(pStoredRoot); + + Fvector _map_attach_p = pSettings->r_fvector3(m_parent->cNameSect(), "ui_p"); + Fvector _map_attach_r = pSettings->r_fvector3(m_parent->cNameSect(), "ui_r"); + + _map_attach_r.mul(PI / 180.f); + m_map_attach_offset.setHPB(_map_attach_r.x, _map_attach_r.y, _map_attach_r.z); + m_map_attach_offset.translate_over(_map_attach_p); +} + +void CUIArtefactDetectorElite::update() +{ + inherited::update(); + CUIWindow::Update(); +} + +void CUIArtefactDetectorElite::Draw() +{ + + Fmatrix LM; + GetUILocatorMatrix(LM); + + IUIRender::ePointType bk = UI().m_currentPointType; + + UI().m_currentPointType = IUIRender::pttLIT; + + UIRender->CacheSetXformWorld(LM); + UIRender->CacheSetCullMode(IUIRender::cmNONE); + + CUIWindow::Draw(); + + //. Frect r = m_wrk_area->GetWndRect(); + Fvector2 wrk_sz = m_wrk_area->GetWndSize(); + Fvector2 rp; + m_wrk_area->GetAbsolutePos(rp); + + Fmatrix M, Mc; + float h, p; + Device.vCameraDirection.getHP(h, p); + Mc.setHPB(h, 0, 0); + Mc.c.set(Device.vCameraPosition); + M.invert(Mc); + + UI().ScreenFrustumLIT().CreateFromRect(Frect().set(rp.x, + rp.y, + wrk_sz.x, + wrk_sz.y)); + + xr_vector::const_iterator it = m_items_to_draw.begin(); + xr_vector::const_iterator it_e = m_items_to_draw.end(); + for (; it != it_e; ++it) + { + Fvector p = (*it).pos; + Fvector pt3d; + M.transform_tiny(pt3d, p); + float kz = wrk_sz.y / m_parent->fdetect_radius; + pt3d.x *= kz; + pt3d.z *= kz; + + pt3d.x += wrk_sz.x / 2.0f; + pt3d.z -= wrk_sz.y; + + Fvector2 pos; + pos.set(pt3d.x, -pt3d.z); + pos.sub(rp); + if (1 /* r.in(pos)*/) + { + (*it).pStatic->SetWndPos(pos); + (*it).pStatic->Draw(); + } + } + + UI().m_currentPointType = bk; +} + +void CUIArtefactDetectorElite::GetUILocatorMatrix(Fmatrix& _m) +{ + Fmatrix trans = m_parent->HudItemData()->m_item_transform; + u16 bid = m_parent->HudItemData()->m_model->LL_BoneID("cover"); + Fmatrix cover_bone = m_parent->HudItemData()->m_model->LL_GetTransform(bid); + _m.mul(trans, cover_bone); + _m.mulB_43(m_map_attach_offset); +} + + +void CUIArtefactDetectorElite::Clear() +{ + m_items_to_draw.clear(); +} + +void CUIArtefactDetectorElite::RegisterItemToDraw(const Fvector& p, const shared_str& palette_idx) +{ + xr_map::iterator it = m_palette.find(palette_idx); + if (it == m_palette.end()) + { + Msg("! RegisterItemToDraw. static not found for [%s]", palette_idx.c_str()); + return; + } + CUIStatic* S = m_palette[palette_idx]; + SDrawOneItem itm(S, p); + m_items_to_draw.push_back(itm); +} \ No newline at end of file diff --git a/src/xrGameLA/EliteAfDetector.h b/src/xrGameLA/EliteAfDetector.h new file mode 100644 index 000000000..b09829396 --- /dev/null +++ b/src/xrGameLA/EliteAfDetector.h @@ -0,0 +1,29 @@ +#pragma once +#include "customdetector2.h" +#include "../feel_touch.h" + +class CUIArtefactDetectorElite; + +class CEliteDetector : public CCustomDetectorR, + public Feel::Touch +{ + typedef CCustomDetectorR inherited; +public: + CEliteDetector(); + virtual ~CEliteDetector(); + virtual void render_item_3d_ui(); + virtual bool render_item_3d_ui_query(); + virtual LPCSTR ui_xml_tag() const { return "elite"; } + + + virtual void feel_touch_new(CObject* O); + virtual void feel_touch_delete(CObject* O); + virtual BOOL feel_touch_contact(CObject* O); + //virtual BOOL feel_touch_on_contact (CObject* O); + + float DetectorFeel(CObject* object); +protected: + virtual void UpdateAf(); + virtual void CreateUI(); + CUIArtefactDetectorElite& ui(); +}; diff --git a/src/xrGameLA/Entity.cpp b/src/xrGameLA/Entity.cpp new file mode 100644 index 000000000..32a1af6e2 --- /dev/null +++ b/src/xrGameLA/Entity.cpp @@ -0,0 +1,370 @@ +// Entity.cpp: implementation of the CEntity class. + +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "hudmanager.h" +#include "Entity.h" +#include "actor.h" +#include "xrserver_objects_alife_monsters.h" +#include "entity.h" +#include "level.h" +#include "seniority_hierarchy_holder.h" +#include "team_hierarchy_holder.h" +#include "squad_hierarchy_holder.h" +#include "group_hierarchy_holder.h" +#include "clsid_game.h" +#include "../Include/xrRender/Kinematics.h" +#include "monster_community.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_time_manager.h" + +#define BODY_REMOVE_TIME 600000 + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CEntity::CEntity() +{ + m_registered_member = false; +} + +CEntity::~CEntity() +{ + xr_delete (m_entity_condition); +} + + +CEntityConditionSimple *CEntity::create_entity_condition(CEntityConditionSimple* ec) +{ + if(!ec) + m_entity_condition = new CEntityConditionSimple(); + else + m_entity_condition = smart_cast(ec); + + return m_entity_condition; +} + +void CEntity::OnEvent (NET_Packet& P, u16 type) +{ + inherited::OnEvent (P,type); + + switch (type) + { + + case GE_DIE: + { + u16 id; + u32 cl; + P.r_u16 (id); + P.r_u32 (cl); + CObject *who = Level().Objects.net_Find (id); + if (who && !IsGameTypeSingle()) + { + if (this!=who) /*if(bDebug) */ Msg( "%s killed by %s ...", cName().c_str(), who->cName().c_str() ); + else /*if(bDebug) */ Msg( "%s dies himself ...", cName().c_str() ); + } + Die (who); + } + break; + } +} + +void CEntity::Die(CObject* who) +{ + if (!AlreadyDie()) set_death_time(); + set_ready_to_save (); + SetfHealth (-1.f); + + if(IsGameTypeSingle()) + { + VERIFY (m_registered_member); + } + m_registered_member = false; + if (IsGameTypeSingle()) + Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()).unregister_member(this); +} + +//обновление состояния +float CEntity::CalcCondition(float hit) +{ + + // If Local() - perform some logic + if (Local() && g_Alive()) { + SetfHealth (GetfHealth()-hit); + SetfHealth ((GetfHealth()<-1000)?-1000:GetfHealth()); + } + return hit; +} + + + + +//void CEntity::Hit (float perc, Fvector &dir, CObject* who, s16 element,Fvector position_in_object_space, float impulse, ALife::EHitType hit_type) +void CEntity::Hit (SHit* pHDS) +{ + +// if (bDebug) Log("Process HIT: ", *cName()); + + // *** process hit calculations + // Calc impulse + Fvector vLocalDir; + float m = pHDS->dir.magnitude(); + VERIFY (m>EPS); + + // convert impulse into local coordinate system + Fmatrix mInvXForm; + mInvXForm.invert (XFORM()); + mInvXForm.transform_dir (vLocalDir,pHDS->dir); + vLocalDir.invert (); + + // hit impulse + if(pHDS->impulse) HitImpulse (pHDS->impulse,pHDS->dir,vLocalDir); // @@@: WT + + // Calc amount (correct only on local player) + float lost_health = CalcCondition(pHDS->damage()); + + // Signal hit + if(BI_NONE!=pHDS->bone()) HitSignal(lost_health,vLocalDir,pHDS->who,pHDS->boneID); + + // If Local() - perform some logic + if (Local() && !g_Alive() && !AlreadyDie() && (m_killer_id == ALife::_OBJECT_ID(-1))) { + KillEntity (pHDS->whoID); + } + //must be last!!! @slipch + inherited::Hit(pHDS); +} + +void CEntity::Load (LPCSTR section) +{ + inherited::Load (section); + + setVisible (FALSE); + + // Team params + id_Team = READ_IF_EXISTS(pSettings,r_s32,section,"team",-1); + id_Squad = READ_IF_EXISTS(pSettings,r_s32,section,"squad",-1); + id_Group = READ_IF_EXISTS(pSettings,r_s32,section,"group",-1); + +#pragma todo("Jim to Dima: no specific figures or comments needed") + m_fMorale = 66.f; + + //время убирания тела с уровня + m_dwBodyRemoveTime = READ_IF_EXISTS(pSettings,r_u32,section,"body_remove_time",BODY_REMOVE_TIME); + ////////////////////////////////////// +} + +BOOL CEntity::net_Spawn (CSE_Abstract* DC) +{ + m_level_death_time = 0; + m_game_death_time = 0; + m_killer_id = ALife::_OBJECT_ID(-1); + + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeCreatureAbstract *E = smart_cast(e); + + // Initialize variables + if (E) { + SetfHealth (E->fHealth); + + R_ASSERT2(!((E->get_killer_id() != ALife::_OBJECT_ID(-1)) && g_Alive()), make_string("server entity [%s][%d] has an killer [%d] and not dead", + E->name_replace(), E->ID, E->get_killer_id()).c_str()); + m_killer_id = E->m_killer_id; + if (m_killer_id == ID()) + m_killer_id = ALife::_OBJECT_ID(-1); + } + else + SetfHealth (1.0f); + + // load damage params + if (!E) { + // Car or trader only!!!! + CSE_ALifeCar *C = smart_cast(e); + CSE_ALifeTrader *T = smart_cast(e); + CSE_ALifeHelicopter *H = smart_cast(e); + CSE_ALifeMountedTurret *M = smart_cast(e); + + R_ASSERT2 (C||T||H||M,"Invalid entity (no inheritance from CSE_CreatureAbstract, CSE_ALifeItemCar and CSE_ALifeTrader and CSE_ALifeHelicopter)!"); + id_Team = id_Squad = id_Group = 0; + } + else { + id_Team = E->g_team(); + id_Squad = E->g_squad(); + id_Group = E->g_group(); + + CSE_ALifeMonsterBase *monster = smart_cast(E); + if (monster) { + MONSTER_COMMUNITY monster_community; + monster_community.set (pSettings->r_string(*cNameSect(), "species")); + + if(monster_community.team() != 255) + id_Team = monster_community.team(); + } + } + + if (g_Alive() && IsGameTypeSingle()) { + m_registered_member = true; + Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()).register_member(this); + ++Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()).m_dwAliveCount; + } + + if(!g_Alive()) + { + m_level_death_time = Device.dwTimeGlobal; + m_game_death_time = E->m_game_death_time;; + } + + if (!inherited::net_Spawn(DC)) + return (FALSE); + +// SetfHealth (E->fHealth); + IKinematics* pKinematics=smart_cast(Visual()); + CInifile* ini = NULL; + + if(pKinematics) ini = pKinematics->LL_UserData(); + if (ini) { + if (ini->section_exist("damage_section") && !use_simplified_visual()) + CDamageManager::reload(pSettings->r_string("damage_section","damage"),ini); + + CParticlesPlayer::LoadParticles(pKinematics); + } + return TRUE; +} + +void CEntity::net_Destroy () +{ + if (m_registered_member) { + m_registered_member = false; + if (IsGameTypeSingle()) + Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()).unregister_member(this); + } + + inherited::net_Destroy (); + + set_ready_to_save (); +} + +void CEntity::KillEntity(u16 whoID) +{ + if (whoID != ID()) { +#ifdef DEBUG + if (m_killer_id != ALife::_OBJECT_ID(-1)) { + Msg ("! Entity [%s][%s] already has killer with id %d, but new killer id arrived - %d",*cNameSect(),*cName(),m_killer_id,whoID); + + CObject *old_killer = Level().Objects.net_Find(m_killer_id); + Msg ("! Old killer is %s",old_killer ? *old_killer->cName() : "unknown"); + + CObject *new_killer = Level().Objects.net_Find(whoID); + Msg ("! New killer is %s",new_killer ? *new_killer->cName() : "unknown"); + + VERIFY (m_killer_id == ALife::_OBJECT_ID(-1)); + } +#endif + } + else { + if (m_killer_id != ALife::_OBJECT_ID(-1)) + return; + } + + m_killer_id = whoID; + + set_death_time (); + + if (!getDestroy()){ + NET_Packet P; + u_EventGen (P,GE_DIE,ID()); + P.w_u16 (u16(whoID)); + P.w_u32 (0); + if (OnServer()) + u_EventSend (P, net_flags(TRUE, TRUE, FALSE, TRUE)); + } +}; + +//void CEntity::KillEntity(CObject* who) +//{ +// VERIFY (who); +// if (who) KillEntity(who->ID()); +//} + +void CEntity::reinit () +{ + inherited::reinit (); +} + + +void CEntity::reload (LPCSTR section) +{ + inherited::reload (section); + if (!use_simplified_visual()) + CDamageManager::reload (section,"damage",pSettings); +} + +void CEntity::set_death_time () +{ + m_level_death_time = Device.dwTimeGlobal; + m_game_death_time = ai().get_alife() ? ai().alife().time_manager().game_time() : Level().GetGameTime(); +} + +bool CEntity::IsFocused ()const { return (smart_cast(g_pGameLevel->CurrentEntity())==this); } +bool CEntity::IsMyCamera ()const { return (smart_cast(g_pGameLevel->CurrentViewEntity())==this); } + +void CEntity::set_ready_to_save () +{ +} + +DLL_Pure *CEntity::_construct () +{ + inherited::_construct (); + CDamageManager::_construct (); + m_entity_condition = create_entity_condition(NULL); + return (this); +} + +const u32 FORGET_KILLER_TIME = 180000; + +void CEntity::shedule_Update (u32 dt) +{ + inherited::shedule_Update (dt); + if (!getDestroy() && !g_Alive() && (m_killer_id != u16(-1))) { + if (Device.dwTimeGlobal > m_level_death_time + FORGET_KILLER_TIME) { + m_killer_id = u16(-1); + NET_Packet P; + u_EventGen (P,GE_ASSIGN_KILLER,ID()); + P.w_u16 (u16(-1)); + if (IsGameTypeSingle()) u_EventSend (P); + } + } +} + +void CEntity::on_before_change_team () +{ +} + +void CEntity::on_after_change_team () +{ +} + +void CEntity::ChangeTeam(int team, int squad, int group) +{ + if ((team == g_Team()) && (squad == g_Squad()) && (group == g_Group())) return; + + VERIFY2 (g_Alive(), "Try to change team of a dead object"); + + if(IsGameTypeSingle()) + { + VERIFY (m_registered_member); + } + // remove from current team + on_before_change_team (); + Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()).unregister_member (this); + + id_Team = team; + id_Squad = squad; + id_Group = group; + + // add to new team + Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()).register_member (this); + on_after_change_team (); +} diff --git a/src/xrGameLA/Entity.h b/src/xrGameLA/Entity.h new file mode 100644 index 000000000..bd6c98c39 --- /dev/null +++ b/src/xrGameLA/Entity.h @@ -0,0 +1,129 @@ +#pragma once + + +#include "physicsshellholder.h" +#include "damage_manager.h" +#include "entitycondition.h" +// refs +class ENGINE_API CCameraBase; +class ENGINE_API C3DSound; +class ENGINE_API CMotionDef; +class ENGINE_API IKinematics; +class ENGINE_API CBoneInstance; +class CWeaponList; +class CPHMovementControl; +class CHudItem; + +class CEntity : + public CPhysicsShellHolder, + public CDamageManager +{ + friend class CEntityCondition; +private: + typedef CPhysicsShellHolder inherited; + CEntityConditionSimple* m_entity_condition; + +protected: + //время через которое мертвое тело убирется с уровня + ALife::_TIME_ID m_dwBodyRemoveTime; +protected: + virtual CEntityConditionSimple *create_entity_condition (CEntityConditionSimple* ec); + +public: + /*virtual*/IC float GetfHealth () const { return m_entity_condition->GetHealth(); } + /*virtual*/IC float SetfHealth (float value) {m_entity_condition->health()=value; return value;} + float m_fMorale; + // Team params + int id_Team; + int id_Squad; + int id_Group; + + virtual void ChangeTeam (int team, int squad, int group); + + struct SEntityState + { + u32 bJump :1; + u32 bCrouch :1; + u32 bFall :1; + u32 bSprint :1; + float fVelocity; + float fAVelocity; + }; + + float m_fFood; + + // General + CEntity (); + virtual ~CEntity (); + virtual DLL_Pure *_construct (); + virtual CEntity* cast_entity () {return this;} +public: + + // Core events + virtual void Load (LPCSTR section); + virtual void reinit (); + virtual void reload (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + + virtual void shedule_Update (u32 dt); + + bool IsFocused ()const; + bool IsMyCamera ()const; + +// virtual float g_Health ()const { return GetfHealth();} +/* virtual*/ IC float GetMaxHealth ()const { return m_entity_condition->max_health(); } +/* virtual*/ IC void SetMaxHealth (float v) { m_entity_condition->max_health()=v;} + + /*virtual*/ IC BOOL g_Alive ()const { return GetfHealth()>0; } + virtual BOOL g_State (SEntityState&) const {return FALSE;} + + bool AlreadyDie () {return 0!=GetLevelDeathTime()?true:false;} + ALife::_TIME_ID GetGameDeathTime ()const {return m_game_death_time;} + u32 GetLevelDeathTime ()const {return m_level_death_time;} + + virtual float CalcCondition (float hit); + + // if false - hits go through and dont hit + virtual bool in_solid_state () { return true; } + + int g_Team ()const { return id_Team; } + int g_Squad ()const { return id_Squad; } + int g_Group ()const { return id_Group; } + + // Health calculations + virtual void Hit (SHit* pHDS); + virtual void HitSignal (float P, Fvector &local_dir, CObject* who, s16 element) = 0; + virtual void HitImpulse (float P, Fvector &vWorldDir, Fvector& vLocalDir) = 0; + + virtual void Die (CObject* who); +// void KillEntity (CObject* who); + void KillEntity (u16 whoID); + + // Events + virtual void OnEvent ( NET_Packet& P, u16 type ); + + virtual BOOL IsVisibleForHUD () {return g_Alive(); } + virtual void g_fireParams (const CHudItem*, Fvector &, Fvector &){}; + virtual bool g_stateFire () {return true;} + //time of entity death + u32 m_level_death_time; + ALife::_TIME_ID m_game_death_time; + + void set_death_time (); + virtual void set_ready_to_save (); + +private: + ALife::_OBJECT_ID m_killer_id; + +public: + IC u16 killer_id () const {return m_killer_id;}; + virtual bool use_simplified_visual () const {return false;}; + +public: + virtual void on_before_change_team (); + virtual void on_after_change_team (); + +private: + bool m_registered_member; +}; diff --git a/src/xrGameLA/EntityCondition.cpp b/src/xrGameLA/EntityCondition.cpp new file mode 100644 index 000000000..b4026b226 --- /dev/null +++ b/src/xrGameLA/EntityCondition.cpp @@ -0,0 +1,589 @@ +#include "stdafx.h" +#include "entitycondition.h" +#include "inventoryowner.h" +#include "customoutfit.h" +#include "chelmet.h" +#include "inventory.h" +#include "wound.h" +#include "level.h" +#include "game_cl_base.h" +#include "entity_alive.h" +#include "../Include/xrRender/KinematicsAnimated.h" +#include "../Include/xrRender/Kinematics.h" +#include "object_broker.h" + +#define MAX_HEALTH 1.0f +#define MIN_HEALTH -0.01f +#define CHEM_BURN_HEALTH_HIT_KOEF 0.5f + +#define MAX_POWER 1.0f +#define MAX_RADIATION 1.0f +#define MAX_PSY_HEALTH 1.0f + +CEntityConditionSimple::CEntityConditionSimple() +{ + max_health() = MAX_HEALTH; + SetHealth ( MAX_HEALTH ); +} + +CEntityConditionSimple::~CEntityConditionSimple() +{} + +CEntityCondition::CEntityCondition(CEntityAlive *object) +:CEntityConditionSimple() +{ + VERIFY (object); + + m_object = object; + + m_use_limping_state = false; + m_iLastTimeCalled = 0; + m_bTimeValid = false; + + m_fPowerMax = MAX_POWER; + m_fRadiationMax = MAX_RADIATION; + m_fPsyHealthMax = MAX_PSY_HEALTH; + m_fEntityMorale = m_fEntityMoraleMax = 1.f; + + + m_fPower = MAX_POWER; + m_fRadiation = 0; + m_fPsyHealth = MAX_PSY_HEALTH; + + m_fMinWoundSize = 0.00001f; + + + m_fHealthHitPart = 1.0f; + m_fPowerHitPart = 0.5f; + + m_fDeltaHealth = 0; + m_fDeltaPower = 0; + m_fDeltaRadiation = 0; + m_fDeltaPsyHealth = 0; + + + m_fHealthLost = 0.f; + m_pWho = NULL; + m_iWhoID = 0; + + m_WoundVector.clear (); + + + m_fHitBoneScale = 1.f; + m_fWoundBoneScale = 1.f; + + m_bIsBleeding = false; + m_bCanBeHarmed = true; +} + +CEntityCondition::~CEntityCondition(void) +{ + ClearWounds (); +} + +void CEntityCondition::ClearWounds() +{ +/* + for(WOUND_VECTOR_IT it = m_WoundVector.begin(); m_WoundVector.end() != it; ++it) + xr_delete(*it); +*/ + while (m_WoundVector.size()) + { + CWound *tmp = m_WoundVector[0]; + m_WoundVector.erase(m_WoundVector.begin()); + xr_delete(tmp); + } + m_WoundVector.clear(); + + m_bIsBleeding = false; +} + +void CEntityCondition::LoadCondition(LPCSTR entity_section) +{ + LPCSTR section = READ_IF_EXISTS(pSettings,r_string,entity_section,"condition_sect",entity_section); + + m_change_v.load (section,""); + + m_fMinWoundSize = pSettings->r_float(section,"min_wound_size"); + m_fHealthHitPart = pSettings->r_float(section,"health_hit_part"); + m_fPowerHitPart = pSettings->r_float(section,"power_hit_part"); + + m_use_limping_state = !!(READ_IF_EXISTS(pSettings,r_bool,section,"use_limping_state",FALSE)); + m_limping_threshold = READ_IF_EXISTS(pSettings,r_float,section,"limping_threshold",.5f); +} + +void CEntityCondition::reinit () +{ + m_iLastTimeCalled = 0; + m_bTimeValid = false; + + max_health() = MAX_HEALTH; + m_fPowerMax = MAX_POWER; + m_fRadiationMax = MAX_RADIATION; + m_fPsyHealthMax = MAX_PSY_HEALTH; + + m_fEntityMorale = m_fEntityMoraleMax = 1.f; + + SetHealth ( MAX_HEALTH ); + m_fPower = MAX_POWER; + m_fRadiation = 0; + m_fPsyHealth = MAX_PSY_HEALTH; + + m_fDeltaHealth = 0; + m_fDeltaPower = 0; + m_fDeltaRadiation = 0; + m_fDeltaCircumspection = 0; + m_fDeltaEntityMorale = 0; + m_fDeltaPsyHealth = 0; + + + m_fHealthLost = 0.f; + m_pWho = NULL; + m_iWhoID = NULL; + + ClearWounds (); + +} + +void CEntityCondition::ChangeHealth(const float value) +{ + VERIFY(_valid(value)); + m_fDeltaHealth += (CanBeHarmed() || (value > 0)) ? value : 0; +} + +void CEntityCondition::ChangePower(const float value) +{ + m_fDeltaPower += value; +} + +void CEntityCondition::ChangeRadiation(const float value) +{ + m_fDeltaRadiation += value; +} + +void CEntityCondition::ChangePsyHealth(const float value) +{ + m_fDeltaPsyHealth += value; +} + +void CEntityCondition::ChangeCircumspection(const float value) +{ + m_fDeltaCircumspection += value; +} +void CEntityCondition::ChangeEntityMorale(const float value) +{ + m_fDeltaEntityMorale += value; +} + + +void CEntityCondition::ChangeBleeding(const float percent) +{ + //затянуть раны + for(WOUND_VECTOR_IT it = m_WoundVector.begin(); m_WoundVector.end() != it; ++it) + { + (*it)->Incarnation (percent, m_fMinWoundSize); + if(0 == (*it)->TotalSize ()) + (*it)->SetDestroy (true); + } +} +bool RemoveWoundPred(CWound* pWound) +{ + if(pWound->GetDestroy()) + { + xr_delete (pWound); + return true; + } + return false; +} + +void CEntityCondition::UpdateWounds () +{ + //убрать все зашившие раны из списка + m_WoundVector.erase( + std::remove_if( + m_WoundVector.begin(), + m_WoundVector.end(), + &RemoveWoundPred + ), + m_WoundVector.end() + ); +} + +void CEntityCondition::UpdateConditionTime() +{ + u64 _cur_time = (GameID() == GAME_SINGLE) ? Level().GetGameTime() : Level().timeServer(); + + if(m_bTimeValid) + { + if (_cur_time > m_iLastTimeCalled){ + float x = float(_cur_time-m_iLastTimeCalled)/1000.0f; + SetConditionDeltaTime (x); + + }else + SetConditionDeltaTime(0.0f); + } + else + { + SetConditionDeltaTime (0.0f); + m_bTimeValid = true; + + m_fDeltaHealth = 0; + m_fDeltaPower = 0; + m_fDeltaRadiation = 0; + m_fDeltaCircumspection = 0; + m_fDeltaEntityMorale = 0; + } + + m_iLastTimeCalled = _cur_time; +} + +//вычисление параметров с ходом игрового времени +void CEntityCondition::UpdateCondition() +{ + if(GetHealth()<=0) return; + //----------------------------------------- + bool CriticalHealth = false; + + if (m_fDeltaHealth+GetHealth() <= 0) + { + CriticalHealth = true; + m_object->OnCriticalHitHealthLoss(); + } + else + { + if (m_fDeltaHealth<0) m_object->OnHitHealthLoss(GetHealth()+m_fDeltaHealth); + } + //----------------------------------------- + UpdateHealth (); + //----------------------------------------- + if (!CriticalHealth && m_fDeltaHealth+GetHealth() <= 0) + { + CriticalHealth = true; + m_object->OnCriticalWoundHealthLoss(); + }; + //----------------------------------------- + UpdatePower (); + UpdateRadiation (); + //----------------------------------------- + if (!CriticalHealth && m_fDeltaHealth+GetHealth() <= 0) + { + CriticalHealth = true; + m_object->OnCriticalRadiationHealthLoss(); + }; + //----------------------------------------- + UpdatePsyHealth (); + + UpdateEntityMorale (); + + health() += m_fDeltaHealth; + m_fPower += m_fDeltaPower; + m_fPsyHealth += m_fDeltaPsyHealth; + m_fEntityMorale += m_fDeltaEntityMorale; + m_fRadiation += m_fDeltaRadiation; + + m_fDeltaHealth = 0; + m_fDeltaPower = 0; + m_fDeltaRadiation = 0; + m_fDeltaPsyHealth = 0; + m_fDeltaCircumspection = 0; + m_fDeltaEntityMorale = 0; + + clamp (health(), MIN_HEALTH, max_health()); + clamp (m_fPower, 0.0f, m_fPowerMax); + clamp (m_fRadiation, 0.0f, m_fRadiationMax); + clamp (m_fEntityMorale, 0.0f, m_fEntityMoraleMax); + clamp (m_fPsyHealth, 0.0f, m_fPsyHealthMax); +} + +float CEntityCondition::HitOutfitEffect(float hit_power, ALife::EHitType hit_type, s16 element, float ap, bool& add_wound) +{ + CInventoryOwner* pInvOwner = smart_cast(m_object); + if (!pInvOwner) + return hit_power; + + CCustomOutfit* pOutfit = (CCustomOutfit*)pInvOwner->inventory().m_slots[OUTFIT_SLOT].m_pIItem; + CHelmet* pHelmet = (CHelmet*)pInvOwner->inventory().m_slots[HELMET_SLOT].m_pIItem; + if (!pOutfit && !pHelmet) + return hit_power; + + //Msg("HitOutfitEffect before: hit_power = %f | element(bone_id) = %i", hit_power, element); + float new_hit_power = hit_power; + if (pOutfit) + new_hit_power = pOutfit->HitThroughArmor(hit_power, element, ap, add_wound, hit_type); + // Msg("HitOutfitEffect after outfit: hit_power = %f", new_hit_power); + if (pHelmet) + new_hit_power = pHelmet->HitThroughArmor(new_hit_power, element, ap, add_wound, hit_type); + // Msg("HitOutfitEffect after helmet: hit_power = %f", new_hit_power); + //if (bDebug) + //Msg("HitOutfitEffect total: hit_power = %.3f | hit_type = %s ap = %.3f", new_hit_power, ALife::g_cafHitType2String(hit_type), ap); + + return new_hit_power; +} + +float CEntityCondition::HitPowerEffect(float power_loss) +{ + CInventoryOwner* pInvOwner = smart_cast(m_object); + if(!pInvOwner) return power_loss; + + CCustomOutfit* pOutfit = (CCustomOutfit*)pInvOwner->inventory().m_slots[OUTFIT_SLOT].m_pIItem; + if(!pOutfit) return power_loss; + + float new_power_loss = power_loss*pOutfit->GetPowerLoss(); + + return new_power_loss; +} + +CWound* CEntityCondition::AddWound(float hit_power, ALife::EHitType hit_type, u16 element) +{ + //максимальное число косточек 64 + VERIFY(element < 64 || BI_NONE == element); + + //запомнить кость по которой ударили и силу удара + WOUND_VECTOR_IT it = m_WoundVector.begin(); + for(;it != m_WoundVector.end(); it++) + { + if((*it)->GetBoneNum() == element) + break; + } + + CWound* pWound = NULL; + + //новая рана + if (it == m_WoundVector.end()) + { + pWound = new CWound(element); + pWound->AddHit(hit_power*::Random.randF(0.5f,1.5f), hit_type); + m_WoundVector.push_back(pWound); + } + //старая + else + { + pWound = *it; + pWound->AddHit(hit_power*::Random.randF(0.5f,1.5f), hit_type); + } + + VERIFY(pWound); + return pWound; +} + +//tatarinrafa:Подстроил под ЗП +CWound* CEntityCondition::ConditionHit(SHit* pHDS) +{ + //кто нанес последний хит + m_pWho = pHDS->who; + m_iWhoID = (NULL != pHDS->who) ? pHDS->who->ID() : 0; + + float hit_power_org = pHDS->damage(); + float hit_power = hit_power_org; + bool bAddWound = pHDS->add_wound; + + hit_power = HitOutfitEffect(hit_power_org, pHDS->hit_type, pHDS->boneID, pHDS->ap, bAddWound); + //hit_power = HitOutfitEffect(hit_power, pHDS->hit_type, pHDS->boneID, pHDS->ap); + + //bool bAddWound = true; + + switch (pHDS->hit_type) + { + case ALife::eHitTypeTelepatic: + // ------------------------------------------------- + hit_power *= GetHitImmunity(pHDS->hit_type); + hit_power *= GetHitImmunity(pHDS->hit_type); + ChangePsyHealth(-hit_power); + bAddWound = false; + break; + case ALife::eHitTypeBurn: + hit_power *= GetHitImmunity(pHDS->hit_type); + m_fHealthLost = hit_power*m_fHealthHitPart*m_fHitBoneScale; + m_fDeltaHealth -= CanBeHarmed() ? m_fHealthLost : 0; + m_fDeltaPower -= hit_power*m_fPowerHitPart; + bAddWound = false; + break; + case ALife::eHitTypeChemicalBurn: + hit_power *= GetHitImmunity(pHDS->hit_type); + m_fHealthLost = hit_power*m_fHealthHitPart*CHEM_BURN_HEALTH_HIT_KOEF; + m_fDeltaHealth -= CanBeHarmed() ? m_fHealthLost : 0; + m_fDeltaPower -= hit_power*m_fPowerHitPart; + break; + case ALife::eHitTypeShock: + hit_power *= GetHitImmunity(pHDS->hit_type); + m_fHealthLost = hit_power*m_fHealthHitPart; + m_fDeltaHealth -= CanBeHarmed() ? m_fHealthLost : 0; + m_fDeltaPower -= hit_power*m_fPowerHitPart; + bAddWound = false; + break; + // case ALife::eHitTypeRadiation: + // m_fDeltaRadiation += hit_power; + // return NULL; + // break; + case ALife::eHitTypeRadiation: + if (hit_power < 0.f) + hit_power = 0.f; + hit_power *= GetHitImmunity(pHDS->hit_type); + m_fDeltaRadiation += hit_power; + bAddWound = false; + return NULL; + break; + case ALife::eHitTypeExplosion: + case ALife::eHitTypeStrike: + case ALife::eHitTypePhysicStrike: + hit_power *= GetHitImmunity(pHDS->hit_type); + m_fHealthLost = hit_power*m_fHealthHitPart; + m_fDeltaHealth -= CanBeHarmed() ? m_fHealthLost : 0; + m_fDeltaPower -= hit_power*m_fPowerHitPart; + break; + case ALife::eHitTypeFireWound: + case ALife::eHitTypeWound: + hit_power *= GetHitImmunity(pHDS->hit_type); + m_fHealthLost = hit_power*m_fHealthHitPart*m_fHitBoneScale; + m_fDeltaHealth -= CanBeHarmed() ? m_fHealthLost : 0; + m_fDeltaPower -= hit_power*m_fPowerHitPart; + break; + default: + { + R_ASSERT2(0, "unknown hit type"); + }break; + } + + if (bDebug) Msg("%s hitted in %s with %f[%f]", m_object->Name(), smart_cast(m_object->Visual())->LL_BoneName_dbg(pHDS->boneID), m_fHealthLost*100.0f, hit_power_org); + //раны добавляются только живому + if (bAddWound && GetHealth()>0) + return AddWound(hit_power*m_fWoundBoneScale, pHDS->hit_type, pHDS->boneID); + else + return NULL; +} + + +float CEntityCondition::BleedingSpeed() +{ + float bleeding_speed =0; + + for(WOUND_VECTOR_IT it = m_WoundVector.begin(); m_WoundVector.end() != it; ++it) + bleeding_speed += (*it)->TotalSize(); + + + return (m_WoundVector.empty() ? 0.f : bleeding_speed / m_WoundVector.size()); +} + + +void CEntityCondition::UpdateHealth() +{ + float bleeding_speed = BleedingSpeed() * m_fDeltaTime * m_change_v.m_fV_Bleeding; + m_bIsBleeding = fis_zero(bleeding_speed)?false:true; + m_fDeltaHealth -= CanBeHarmed() ? bleeding_speed : 0; + m_fDeltaHealth += m_fDeltaTime * m_change_v.m_fV_HealthRestore; + + VERIFY (_valid(m_fDeltaHealth)); + ChangeBleeding (m_change_v.m_fV_WoundIncarnation * m_fDeltaTime); +} + +void CEntityCondition::UpdatePower() +{ +} + +void CEntityCondition::UpdatePsyHealth(float k) +{ + if(m_fPsyHealth>0) + { + m_fDeltaPsyHealth += m_change_v.m_fV_PsyHealth*k*m_fDeltaTime; + } +} + +void CEntityCondition::UpdateRadiation(float k) +{ + if(m_fRadiation>0) + { + m_fDeltaRadiation -= m_change_v.m_fV_Radiation* + k* + m_fDeltaTime; + + m_fDeltaHealth -= CanBeHarmed() ? m_change_v.m_fV_RadiationHealth*m_fRadiation*m_fDeltaTime : 0.0f; + } +} + +void CEntityCondition::UpdateEntityMorale() +{ + if(m_fEntityMorale0.f)?1:0; + + output_packet.w_u8 (is_alive); + if(is_alive) + { + save_data (m_fPower,output_packet); + save_data (m_fRadiation,output_packet); + save_data (m_fEntityMorale,output_packet); + save_data (m_fPsyHealth,output_packet); + + output_packet.w_u8 ((u8)m_WoundVector.size()); + for(WOUND_VECTOR_IT it = m_WoundVector.begin(); m_WoundVector.end() != it; it++) + (*it)->save(output_packet); + } +} + +void CEntityCondition::load (IReader &input_packet) +{ + m_bTimeValid = false; + + u8 is_alive = input_packet.r_u8 (); + if(is_alive) + { + load_data (m_fPower,input_packet); + load_data (m_fRadiation,input_packet); + load_data (m_fEntityMorale,input_packet); + load_data (m_fPsyHealth,input_packet); + + ClearWounds(); + m_WoundVector.resize(input_packet.r_u8()); + if(!m_WoundVector.empty()) + for(u32 i=0; iload(input_packet); + m_WoundVector[i] = pWound; + } + } +} + +void CEntityCondition::SConditionChangeV::load(LPCSTR sect, LPCSTR prefix) +{ + string256 str; + m_fV_Circumspection = 0.01f; + + strconcat (sizeof(str),str,"radiation_v",prefix); + m_fV_Radiation = pSettings->r_float(sect,str); + strconcat (sizeof(str),str,"radiation_health_v",prefix); + m_fV_RadiationHealth = pSettings->r_float(sect,str); + strconcat (sizeof(str),str,"morale_v",prefix); + m_fV_EntityMorale = pSettings->r_float(sect,str); + strconcat (sizeof(str),str,"psy_health_v",prefix); + m_fV_PsyHealth = pSettings->r_float(sect,str); + strconcat (sizeof(str),str,"bleeding_v",prefix); + m_fV_Bleeding = pSettings->r_float(sect,str); + strconcat (sizeof(str),str,"wound_incarnation_v",prefix); + m_fV_WoundIncarnation = pSettings->r_float(sect,str); + strconcat (sizeof(str),str,"health_restore_v",prefix); + m_fV_HealthRestore = READ_IF_EXISTS(pSettings,r_float,sect, str,0.0f); +} + +void CEntityCondition::remove_links (const CObject *object) +{ + if (m_pWho != object) + return; + + m_pWho = m_object; + m_iWhoID = m_object->ID(); +} diff --git a/src/xrGameLA/EntityCondition.h b/src/xrGameLA/EntityCondition.h new file mode 100644 index 000000000..06f620b1d --- /dev/null +++ b/src/xrGameLA/EntityCondition.h @@ -0,0 +1,192 @@ +#pragma once + +class CWound; +class NET_Packet; +class CEntityAlive; +class CLevel; + +#include "hit_immunity.h" +#include "Hit.h" +#include "Level.h" + +class CEntityConditionSimple +{ + float m_fHealth; + float m_fHealthMax; +public: + CEntityConditionSimple (); + virtual ~CEntityConditionSimple (); + + IC float GetHealth () const {return m_fHealth;} + IC void SetHealth ( const float value ) { m_fHealth = value; } + IC float GetMaxHealth () const {return m_fHealthMax;} + IC float& health () {return m_fHealth;} + IC float& max_health () {return m_fHealthMax;} +}; + +class CEntityCondition: public CEntityConditionSimple, public CHitImmunity +{ +private: + bool m_use_limping_state; + CEntityAlive *m_object; + +public: + CEntityCondition (CEntityAlive *object); + virtual ~CEntityCondition (); + + virtual void LoadCondition (LPCSTR section); + virtual void remove_links (const CObject *object); + + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + + virtual float GetSatiety () const {return 1.f;} + virtual float GetThirsty () const {return 1.f;} + + IC float GetPower () const {return m_fPower;} + IC float GetRadiation () const {return m_fRadiation;} + IC float GetPsyHealth () const {return m_fPsyHealth;} + + IC float GetEntityMorale () const {return m_fEntityMorale;} + + IC float GetHealthLost () const {return m_fHealthLost;} + + virtual bool IsLimping () const; + + virtual void ChangeSatiety (const float value) {}; + virtual void ChangeThirsty (const float value) {}; + virtual void ChangeHealth (const float value); + virtual void ChangePower (const float value); + virtual void ChangeRadiation (const float value); + virtual void ChangePsyHealth (const float value); + virtual void ChangeAlcohol (const float value){}; + + IC void MaxPower () {m_fPower = m_fPowerMax;}; + IC void SetMaxPower (const float val) {m_fPowerMax = val; clamp(m_fPowerMax,0.1f,1.0f);}; + IC float GetMaxPower () const {return m_fPowerMax;}; + + void ChangeBleeding (const float percent); + + void ChangeCircumspection (const float value); + void ChangeEntityMorale (const float value); + + virtual CWound* ConditionHit (SHit* pHDS); + //обновления состояния с течением времени + virtual void UpdateCondition (); + void UpdateWounds (); + void UpdateConditionTime (); + IC void SetConditionDeltaTime (float DeltaTime) { m_fDeltaTime = DeltaTime; }; + + + //скорость потери крови из всех открытых ран + float BleedingSpeed (); + + CObject* GetWhoHitLastTime () {return m_pWho;} + u16 GetWhoHitLastTimeID () {return m_iWhoID;} + + CWound* AddWound (float hit_power, ALife::EHitType hit_type, u16 element); + + IC void SetCanBeHarmedState (bool CanBeHarmed) {m_bCanBeHarmed = CanBeHarmed;} + IC bool CanBeHarmed () const {return OnServer() && m_bCanBeHarmed;}; + + void ClearWounds(); +protected: + void UpdateHealth (); + void UpdatePower (); + void UpdateSatiety (float k=1.0f); + void UpdateThirsty (float k=1.0f); + void UpdateRadiation (float k=1.0f); + void UpdatePsyHealth (float k=1.0f); + + void UpdateEntityMorale (); + + + //изменение силы хита в зависимости от надетого костюма + //(только для InventoryOwner) + float HitOutfitEffect(float hit_power, ALife::EHitType hit_type, s16 element, float AP, bool& add_wound); + //изменение потери сил в зависимости от надетого костюма + float HitPowerEffect (float power_loss); + + //для подсчета состояния открытых ран, + //запоминается кость куда был нанесен хит + //и скорость потери крови из раны + DEFINE_VECTOR(CWound*, WOUND_VECTOR, WOUND_VECTOR_IT); + WOUND_VECTOR m_WoundVector; + //очистка массива ран + + + //все величины от 0 до 1 + float m_fPower; //сила + float m_fRadiation; //доза радиактивного облучения + float m_fPsyHealth; //здоровье + float m_fEntityMorale; //мораль + + //максимальные величины + float m_fPowerMax; + float m_fRadiationMax; + float m_fPsyHealthMax; + + float m_fEntityMoraleMax; + + //величины изменения параметров на каждом обновлении + float m_fDeltaHealth; + float m_fDeltaPower; + float m_fDeltaRadiation; + float m_fDeltaPsyHealth; + + float m_fDeltaCircumspection; + float m_fDeltaEntityMorale; + + struct SConditionChangeV + { + float m_fV_Radiation; + float m_fV_PsyHealth; + float m_fV_Circumspection; + float m_fV_EntityMorale; + float m_fV_RadiationHealth; + float m_fV_Bleeding; + float m_fV_WoundIncarnation; + float m_fV_HealthRestore; + void load(LPCSTR sect, LPCSTR prefix); + }; + + SConditionChangeV m_change_v; + + float m_fMinWoundSize; + bool m_bIsBleeding; + + //части хита, затрачиваемые на уменьшение здоровья и силы + float m_fHealthHitPart; + float m_fPowerHitPart; + + + + //потеря здоровья от последнего хита + float m_fHealthLost; + + + //для отслеживания времени + u64 m_iLastTimeCalled; + float m_fDeltaTime; + //кто нанес последний хит + CObject* m_pWho; + u16 m_iWhoID; + + //для передачи параметров из DamageManager + float m_fHitBoneScale; + float m_fWoundBoneScale; + + float m_limping_threshold; + + bool m_bTimeValid; + bool m_bCanBeHarmed; + +public: + virtual void reinit (); + + IC const float fdelta_time () const {return (m_fDeltaTime); } + IC const WOUND_VECTOR& wounds () const {return (m_WoundVector); } + IC float& radiation () {return (m_fRadiation); } + IC float& hit_bone_scale () {return (m_fHitBoneScale); } + IC float& wound_bone_scale () {return (m_fWoundBoneScale); } +}; diff --git a/src/xrGameLA/ExoOutfit.cpp b/src/xrGameLA/ExoOutfit.cpp new file mode 100644 index 000000000..b7ed85ccb --- /dev/null +++ b/src/xrGameLA/ExoOutfit.cpp @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////// +// ExoOutfit.h +// ExoOutfit - защитный костюм с усилением +/////////////////////////////////////////////////////////////// + +#pragma once + +#include "stdafx.h" +#include "exooutfit.h" + +CExoOutfit::CExoOutfit() +{ +} + +CExoOutfit::~CExoOutfit() +{ +} \ No newline at end of file diff --git a/src/xrGameLA/ExoOutfit.h b/src/xrGameLA/ExoOutfit.h new file mode 100644 index 000000000..c8849fee6 --- /dev/null +++ b/src/xrGameLA/ExoOutfit.h @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////// +// ExoOutfit.h +// ExoOutfit - защитный костюм с усилением +/////////////////////////////////////////////////////////////// + + +#pragma once + +#include "customoutfit.h" + +class CExoOutfit: public CCustomOutfit +{ +private: + typedef CCustomOutfit inherited; +public: + CExoOutfit(void); + virtual ~CExoOutfit(void); +}; diff --git a/src/xrGameLA/Explosive.cpp b/src/xrGameLA/Explosive.cpp new file mode 100644 index 000000000..5df465377 --- /dev/null +++ b/src/xrGameLA/Explosive.cpp @@ -0,0 +1,791 @@ +// Explosive.cpp: интерфейс для взврывающихся объектов +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "explosive.h" + +#include "PhysicsShell.h" +#include "entity.h" +//#include "PSObject.h" +#include "ParticlesObject.h" + +//для вызова статических функций поражения осколками +#include "Weapon.h" + +#include "actor.h" +#include "actoreffector.h" +#include "level.h" +#include "level_bullet_manager.h" +#include "xrmessages.h" +#include "../gamemtllib.h" +#include "clsid_game.h" +#ifdef DEBUG +#include "../StatGraph.h" +#include "PHDebug.h" +#endif +#include "Physics.h" +#include "MathUtils.h" +#include "phvalidevalues.h" +#include "PHActivationShape.h" +#include "game_base_space.h" +#include "profiler.h" +#include "../Include/xrRender/Kinematics.h" + +#define EFFECTOR_RADIUS 30.f +const u16 TEST_RAYS_PER_OBJECT=5; +const u16 BLASTED_OBJ_PROCESSED_PER_FRAME=3; +const float exp_dist_extinction_factor=3.f;//(>1.f, 1.f -means no dist change of exp effect) on the dist of m_fBlastRadius exp. wave effect in exp_dist_extinction_factor times less than maximum + +CExplosive::CExplosive(void) +{ + m_fBlastHit = 50.0f; + m_fBlastRadius = 10.0f; + m_iFragsNum = 20; + m_fFragsRadius = 30.0f; + m_fFragHit = 50.0f; + m_fUpThrowFactor = 0.f; + + + m_eSoundExplode = ESoundTypes(SOUND_TYPE_WEAPON_SHOOTING); + + + m_eHitTypeBlast = ALife::eHitTypeExplosion; + m_eHitTypeFrag = ALife::eHitTypeFireWound; + + + m_iCurrentParentID = 0xffff; + +// m_bReadyToExplode = false; +// m_bExploding = false; +// m_bExplodeEventSent = false; + m_explosion_flags.assign(0); + m_vExplodeSize.set (0.001f,0.001f,0.001f); + + m_bHideInExplosion = TRUE; + m_fExplodeHideDurationMax = 0; + m_bDynamicParticles = FALSE; + m_pExpParticle = NULL; +} + +void CExplosive::LightCreate() +{ + m_pLight = ::Render->light_create(); + m_pLight->set_shadow (true); +} + +void CExplosive::LightDestroy() +{ + m_pLight.destroy (); +} + +CExplosive::~CExplosive(void) +{ + sndExplode.destroy (); +} + +void CExplosive::Load(LPCSTR section) +{ + Load (pSettings,section); +} + +void CExplosive::Load(CInifile const *ini,LPCSTR section) +{ + m_fBlastHit = ini->r_float(section,"blast"); + m_fBlastRadius = ini->r_float(section,"blast_r"); + m_fBlastHitImpulse = ini->r_float(section,"blast_impulse"); + + m_iFragsNum = ini->r_s32(section,"frags"); + m_fFragsRadius = ini->r_float(section,"frags_r"); + m_fFragHit = ini->r_float(section,"frag_hit"); + m_fFragHitImpulse = ini->r_float(section,"frag_hit_impulse"); + + m_eHitTypeBlast = ALife::g_tfString2HitType(ini->r_string(section, "hit_type_blast")); + m_eHitTypeFrag = ALife::g_tfString2HitType(ini->r_string(section, "hit_type_frag")); + + m_fUpThrowFactor = ini->r_float(section,"up_throw_factor"); + + + fWallmarkSize = ini->r_float(section,"wm_size"); + R_ASSERT (fWallmarkSize>0); + + m_sExplodeParticles = ini->r_string(section,"explode_particles"); + + sscanf (ini->r_string(section,"light_color"), "%f,%f,%f", &m_LightColor.r, &m_LightColor.g, &m_LightColor.b); + m_fLightRange = ini->r_float(section,"light_range"); + m_fLightTime = ini->r_float(section,"light_time"); + + //трассы для разлета осколков + m_fFragmentSpeed = ini->r_float (section,"fragment_speed" ); + + LPCSTR snd_name = ini->r_string(section,"snd_explode"); + sndExplode.create (snd_name, st_Effect,m_eSoundExplode); + + m_fExplodeDurationMax = ini->r_float(section, "explode_duration"); + + effector.effect_sect_name= ini->r_string("explode_effector","effect_sect_name"); +// if( ini->line_exist(section,"wallmark_section") ) +// { + m_wallmark_manager.m_owner = cast_game_object(); +// m_wallmark_manager.Load(pSettings,ini->r_string(section,"wallmark_section")); +// } + + m_bHideInExplosion = TRUE; + if (ini->line_exist(section, "hide_in_explosion")) + { + m_bHideInExplosion = ini->r_bool(section, "hide_in_explosion"); + m_fExplodeHideDurationMax = 0; + if (ini->line_exist(section, "explode_hide_duration")) + { + m_fExplodeHideDurationMax = ini->r_float(section, "explode_hide_duration"); + } + } + + m_bDynamicParticles = FALSE; + if (ini->line_exist(section, "dynamic_explosion_particles")) + m_bDynamicParticles = ini->r_bool(section, "dynamic_explosion_particles"); +} + +void CExplosive::net_Destroy () +{ + m_blasted_objects.clear (); + StopLight (); + m_explosion_flags.assign (0); +} + + +struct SExpQParams +{ +#ifdef DEBUG + + SExpQParams(const Fvector& ec,const Fvector& d) + { + shoot_factor= 1.f ; + source_p .set(ec) ; + l_dir .set(d) ; + } + Fvector source_p ; + Fvector l_dir ; +#else + SExpQParams() + { + shoot_factor= 1.f ; + } +#endif + + float shoot_factor ; +}; +//проверка на попадание "осколком" по объекту +ICF static BOOL grenade_hit_callback(collide::rq_result& result, LPVOID params) +{ + SExpQParams& ep = *(SExpQParams*)params; + u16 mtl_idx = GAMEMTL_NONE_IDX; + if(result.O){ + IKinematics* V = 0; + if (0!=(V=smart_cast(result.O->Visual()))){ + CBoneData& B= V->LL_GetData((u16)result.element); + mtl_idx = B.game_mtl_idx; + } + }else{ + //получить треугольник и узнать его материал + CDB::TRI* T = Level().ObjectSpace.GetStaticTris()+result.element; + mtl_idx = T->material; + } + SGameMtl* mtl = GMLib.GetMaterialByIdx(mtl_idx); + float shoot_factor = 1.f - mtl->fShootFactor; + ep.shoot_factor *=shoot_factor; +// ep.shoot_factor *=mtl->fShootFactor; +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawExplosions)) + { + Fvector p;p.set(ep.l_dir);p.mul(result.range);p.add(ep.source_p); + u8 c =u8(shoot_factor*255.f); + DBG_DrawPoint(p,0.1f,D3DCOLOR_XRGB(255-c,0,c)); + } +#endif + return (ep.shoot_factor>0.01f); +} + + + +float CExplosive::ExplosionEffect(collide::rq_results& storage, CExplosive*exp_obj,CPhysicsShellHolder*blasted_obj, const Fvector &expl_centre, const float expl_radius) +{ + + const Fmatrix &obj_xform=blasted_obj->XFORM(); + Fmatrix inv_obj_form;inv_obj_form.invert(obj_xform); + Fvector local_exp_center;inv_obj_form.transform_tiny(local_exp_center,expl_centre); + + const Fbox &l_b1 = blasted_obj->BoundingBox(); + if(l_b1.contains(local_exp_center)) + return 1.f; + Fvector l_c, l_d;l_b1.get_CD(l_c,l_d); + float effective_volume=l_d.x*l_d.y*l_d.z; + float max_s=effective_volume/(_min(_min(l_d.x,l_d.y),l_d.z)); + if(blasted_obj->PPhysicsShell()&&blasted_obj->PPhysicsShell()->isActive()) + { + float ph_volume=blasted_obj->PPhysicsShell()->getVolume(); + if(ph_volumeEPS_L) + { + VERIFY(!fis_zero(dir.square_magnitude())); + collide::ray_defs RD (source_p,dir,range,CDB::OPT_CULL,collide::rqtBoth); + VERIFY (!fis_zero(RD.dir.square_magnitude())); +#ifdef DEBUG + SExpQParams ep (source_p,dir); +#else + SExpQParams ep; +#endif + g_pGameLevel->ObjectSpace.RayQuery(storage,RD,grenade_hit_callback,&ep,NULL,blasted_obj); + shoot_factor=ep.shoot_factor; + } + else return dist_factor; + return shoot_factor*dist_factor; +} +void CExplosive::Explode() +{ + VERIFY(0xffff != Initiator()); + VERIFY(m_explosion_flags.test(flReadyToExplode));//m_bReadyToExplode + VERIFY(!ph_world->Processing()); + //m_bExploding = true; + m_explosion_flags.set(flExploding,TRUE); + cast_game_object()->processing_activate(); + + Fvector& pos = m_vExplodePos; + Fvector& dir = m_vExplodeDir; +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawExplosions)) + { + DBG_OpenCashedDraw(); + DBG_DrawPoint(pos,0.3f,D3DCOLOR_XRGB(255,0,0)); + } +#endif +// Msg("---------CExplosive Explode [%d] frame[%d]",cast_game_object()->ID(), Device.dwFrame); + OnBeforeExplosion(); + //играем звук взрыва + Sound->play_at_pos(sndExplode, 0, pos, false); + + //показываем эффекты + + m_wallmark_manager.PlaceWallmarks (pos); + + Fvector vel; + smart_cast(cast_game_object())->PHGetLinearVell(vel); + + Fmatrix explode_matrix; + explode_matrix.identity(); + explode_matrix.j.set(dir); + Fvector::generate_orthonormal_basis(explode_matrix.j, explode_matrix.i, explode_matrix.k); + explode_matrix.c.set(pos); + + CParticlesObject* pStaticPG; + pStaticPG = CParticlesObject::Create(*m_sExplodeParticles,!m_bDynamicParticles); + if (m_bDynamicParticles) m_pExpParticle = pStaticPG; + pStaticPG->UpdateParent(explode_matrix,vel); + pStaticPG->Play(false); + + //включаем подсветку от взрыва + StartLight(); + + //trace frags + Fvector frag_dir; + + ////////////////////////////// + //осколки + ////////////////////////////// + //------------------------------------- + bool SendHits = false; + if (OnServer()) SendHits = true; + else SendHits = false; + + + for(int i = 0; i < m_iFragsNum; ++i){ + frag_dir.random_dir (); + frag_dir.normalize (); + + CCartridge cartridge; + cartridge.param_s.kDist = 1.f; + cartridge.param_s.kHit = 1.f; +//. cartridge.param_s.kCritical = 1.f; + cartridge.param_s.kImpulse = 1.f; + cartridge.param_s.kAP = 1.f; + cartridge.param_s.fWallmarkSize = fWallmarkSize; + cartridge.bullet_material_idx = GMLib.GetMaterialIdx(WEAPON_MATERIAL_NAME); + cartridge.m_flags.set (CCartridge::cfTracer,FALSE); + + Level().BulletManager().AddBullet( pos, frag_dir, m_fFragmentSpeed, + m_fFragHit, m_fFragHitImpulse, Initiator(), + cast_game_object()->ID(), m_eHitTypeFrag, m_fFragsRadius, + cartridge, 1.f, SendHits ); + } + + if (cast_game_object()->Remote()) return; + + ///////////////////////////////// + //взрывная волна + //////////////////////////////// + //--------------------------------------------------------------------- + xr_vector ISpatialResult; + g_SpatialSpace->q_sphere(ISpatialResult,0,STYPE_COLLIDEABLE,pos,m_fBlastRadius); + + m_blasted_objects.clear (); + for (u32 o_it=0; o_itdcast_CObject()); + + CPhysicsShellHolder *pGameObject = smart_cast(spatial->dcast_CObject()); + if(pGameObject && cast_game_object()->ID() != pGameObject->ID()) + m_blasted_objects.push_back(pGameObject); + } + + GetExplosionBox(m_vExplodeSize); +START_PROFILE("explosive/activate explosion box") + ActivateExplosionBox(m_vExplodeSize,m_vExplodePos); +STOP_PROFILE + //--------------------------------------------------------------------- +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawExplosions)) + { + DBG_ClosedCashedDraw(100000); + + } +#endif + ////////////////////////////////////////////////////////////////////////// + // Explode Effector ////////////// + CGameObject* GO = smart_cast(Level().CurrentEntity()); + CActor* pActor = smart_cast(GO); + if(pActor) + { + float dist_to_actor = pActor->Position().distance_to(pos); + float max_dist = EFFECTOR_RADIUS; + if (dist_to_actor < max_dist) + AddEffector (pActor, effExplodeHit, effector.effect_sect_name, (max_dist - dist_to_actor) / max_dist ); + } +} + +void CExplosive::PositionUpdate() +{ + Fvector vel; + Fvector& pos=m_vExplodePos; + Fvector& dir=m_vExplodeDir; + GetExplVelocity(vel); + GetExplPosition(pos); + GetExplDirection(dir); + Fmatrix explode_matrix; + explode_matrix.identity(); + explode_matrix.j.set(dir); + Fvector::generate_orthonormal_basis(explode_matrix.j, explode_matrix.i, explode_matrix.k); + explode_matrix.c.set(pos); + +} +void CExplosive::GetExplPosition(Fvector &p) +{ + p.set(m_vExplodePos); +} + +void CExplosive::GetExplDirection(Fvector &d) +{ + d.set(m_vExplodeDir); +} +void CExplosive::GetExplVelocity(Fvector &v) +{ + smart_cast(cast_game_object())->PHGetLinearVell(v); +} + +void CExplosive::UpdateCL() +{ + //VERIFY(!this->getDestroy()); + VERIFY(!ph_world->Processing()); + if(!m_explosion_flags.test(flExploding)) return;// !m_bExploding + if(m_explosion_flags.test(flExploded)) + { + CGameObject* go=cast_game_object(); + go->processing_deactivate(); + m_explosion_flags.set(flExploding,FALSE);//m_bExploding = false; + OnAfterExplosion(); + return; + } + //время вышло, убираем объект взрывчатки + if(m_fExplodeDuration < 0.f&&m_blasted_objects.empty()) + { + m_explosion_flags.set(flExploded,TRUE); + + + StopLight(); + + +// Msg("---------CExplosive OnAfterExplosion [%d] frame[%d]",cast_game_object()->ID(), Device.dwFrame); + + } + else + { + m_fExplodeDuration -= Device.fTimeDelta; + if (!m_bHideInExplosion && !m_bAlreadyHidden) + { + if (m_fExplodeHideDurationMax <= (m_fExplodeDurationMax - m_fExplodeDuration)) + { + HideExplosive(); + } + } + UpdateExplosionPos(); + UpdateExplosionParticles(); + ExplodeWaveProcess(); + //обновить подсветку взрыва + if(m_pLight && m_pLight->get_active() && m_fLightTime>0) + { + if(m_fExplodeDuration > (m_fExplodeDurationMax - m_fLightTime)) + { + float scale = (m_fExplodeDuration - (m_fExplodeDurationMax - m_fLightTime))/m_fLightTime; + m_pLight->set_color(m_LightColor.r*scale, m_LightColor.g*scale, m_LightColor.b*scale); + m_pLight->set_range(m_fLightRange*scale); + } + else + StopLight(); + } + } +} + +void CExplosive::OnAfterExplosion() +{ + if(m_pExpParticle){ + m_pExpParticle->Stop(); + CParticlesObject::Destroy(m_pExpParticle); + m_pExpParticle = NULL; + } + //ликвидировать сам объект + if (cast_game_object()->Local()) cast_game_object()->DestroyObject(); +} + +void CExplosive::OnBeforeExplosion() +{ + m_bAlreadyHidden = false; + if (m_bHideInExplosion) + { + HideExplosive(); + // Msg("---------CExplosive OnBeforeExplosion setVisible(false) [%d] frame[%d]",cast_game_object()->ID(), Device.dwFrame); + } +} +void CExplosive::HideExplosive() +{ + CGameObject *GO=cast_game_object(); + GO->setVisible(FALSE); + GO->setEnabled(FALSE); + CPhysicsShell* phshell=(smart_cast(GO))->PPhysicsShell(); + if(phshell) + { + phshell->Disable(); + phshell->DisableCollision(); + } + m_bAlreadyHidden = true; +}; + +void CExplosive::OnEvent(NET_Packet& P, u16 type) +{ + switch (type) { + case GE_GRENADE_EXPLODE : { + Fvector pos, normal; + u16 parent_id; + P.r_u16(parent_id); + P.r_vec3(pos); + P.r_vec3(normal); + + SetInitiator(parent_id); + ExplodeParams(pos,normal); + Explode(); + m_fExplodeDuration = m_fExplodeDurationMax; + break; + } + } +} + +void CExplosive::ExplodeParams(const Fvector& pos, + const Fvector& dir) +{ + //m_bReadyToExplode = true; + m_explosion_flags.set (flReadyToExplode,TRUE); + m_vExplodePos = pos; + m_vExplodePos.y += 0.1f;// fake + m_vExplodeDir = dir; +} + +void CExplosive::GenExplodeEvent (const Fvector& pos, const Fvector& normal) +{ + if (OnClient() || cast_game_object()->Remote()) return; + +// if( m_bExplodeEventSent ) +// return; + VERIFY(!m_explosion_flags.test(flExplodEventSent));//!m_bExplodeEventSent + VERIFY(0xffff != Initiator()); + + NET_Packet P; + cast_game_object()->u_EventGen (P,GE_GRENADE_EXPLODE,cast_game_object()->ID()); + P.w_u16 (Initiator()); + P.w_vec3 (pos); + P.w_vec3 (normal); + cast_game_object()->u_EventSend (P); + + //m_bExplodeEventSent = true; + m_explosion_flags.set(flExplodEventSent,TRUE); +} + +void CExplosive::FindNormal(Fvector& normal) +{ + collide::rq_result RQ; + + Fvector pos, dir; + dir.set(0,-1.f,0); + cast_game_object()->Center(pos); + + BOOL result = Level().ObjectSpace.RayPick(pos, dir, cast_game_object()->Radius(), + collide::rqtBoth, RQ, NULL); + if(!result || RQ.O){ + normal.set(0,1,0); + //если лежим на статике + //найти треугольник и вычислить нормаль по нему + }else + { + Fvector* pVerts = Level().ObjectSpace.GetStaticVerts(); + CDB::TRI* pTri = Level().ObjectSpace.GetStaticTris() + RQ.element; + normal.mknormal (pVerts[pTri->verts[0]],pVerts[pTri->verts[1]],pVerts[pTri->verts[2]]); + } +} + +void CExplosive::StartLight () +{ + + VERIFY(!ph_world->Processing()); + if(m_fLightTime>0) + { + +// VERIFY (!m_pLight); + LightCreate (); + + m_pLight->set_color (m_LightColor.r, m_LightColor.g, m_LightColor.b); + m_pLight->set_range (m_fLightRange); + m_pLight->set_position (m_vExplodePos); + m_pLight->set_active (true); + } +} +void CExplosive::StopLight () +{ + if (m_pLight){ + VERIFY (!ph_world->Processing()); + m_pLight->set_active (false); + LightDestroy (); + } +} + +void CExplosive::GetRaySourcePos(CExplosive*exp_obj,const Fvector &expl_center,Fvector &p) +{ + if(exp_obj) + { + exp_obj->GetRayExplosionSourcePos(p); + } +} +void CExplosive::GetRayExplosionSourcePos(Fvector &pos) +{ + pos.set (m_vExplodeSize);pos.mul(0.5f); + pos.random_point (pos); + pos.add (m_vExplodePos); +} +void CExplosive::ExplodeWaveProcessObject(collide::rq_results& storage, CPhysicsShellHolder*l_pGO) +{ + Fvector l_goPos; + if(l_pGO->Visual()) l_pGO->Center (l_goPos); + else return; //мне непонятно зачем наносить хит от взрыва по объектам не имеющим вижуал - поэтому игнорируем + +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawExplosions)) + { + DBG_OpenCashedDraw(); + + } +#endif + + float l_effect=ExplosionEffect(storage,this,l_pGO,m_vExplodePos,m_fBlastRadius); + float l_impuls = m_fBlastHitImpulse * l_effect; + float l_hit = m_fBlastHit * l_effect; + + if(l_impuls > .001f||l_hit> 0.001) + { + + Fvector l_dir;l_dir.sub(l_goPos,m_vExplodePos); + + float rmag=_sqrt(m_fUpThrowFactor*m_fUpThrowFactor+1.f+2.f*m_fUpThrowFactor*l_dir.y); + l_dir.y += m_fUpThrowFactor; + //rmag -модуль l_dir после l_dir.y += m_fUpThrowFactor, модуль=_sqrt(l_dir^2+y^2+2.*(l_dir,y)),y=(0,m_fUpThrowFactor,0) (до этого модуль l_dir =1) + l_dir.mul(1.f/rmag);//перенормировка + NET_Packet P; + SHit HS; + HS.GenHeader(GE_HIT, l_pGO->ID()); // cast_game_object()->u_EventGen (P,GE_HIT,l_pGO->ID()); + HS.whoID =Initiator(); // P.w_u16 (Initiator()); + HS.weaponID = cast_game_object()->ID(); // P.w_u16 (cast_game_object()->ID()); + HS.dir = l_dir; // P.w_dir (l_dir); + HS.power = l_hit; // P.w_float (l_hit); + HS.p_in_bone_space = l_goPos; // P.w_vec3 (l_goPos); + HS.impulse = l_impuls; // P.w_float (l_impuls); + HS.hit_type = (m_eHitTypeBlast); // P.w_u16 (u16(m_eHitTypeBlast)); + HS.boneID = 0; // P.w_s16 (0); + HS.Write_Packet(P); + cast_game_object()->u_EventSend (P); + } +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawExplosions)) + { + DBG_ClosedCashedDraw(100000); + + } +#endif +} +struct SRemovePred +{ + bool operator () (CGameObject* O) + { + return !!O->getDestroy(); + } +}; +void CExplosive::ExplodeWaveProcess() +{ + + BLASTED_OBJECTS_I I=std::remove_if(m_blasted_objects.begin(),m_blasted_objects.end(),SRemovePred()); + m_blasted_objects.erase (I,m_blasted_objects.end()); + rq_storage.r_clear (); + u16 i = BLASTED_OBJ_PROCESSED_PER_FRAME ; + while (m_blasted_objects.size()&&0!=i) { + ExplodeWaveProcessObject (rq_storage,m_blasted_objects.back()); + m_blasted_objects.pop_back (); + --i; + } +} + +void CExplosive::GetExplosionBox(Fvector &size) +{ + size.set(m_vExplodeSize); +} +void CExplosive::SetExplosionSize(const Fvector &new_size) +{ + m_vExplodeSize.set(new_size); + +} +void CExplosive::ActivateExplosionBox(const Fvector &size,Fvector &in_out_pos) +{ + CPhysicsShellHolder *self_obj=smart_cast(cast_game_object()); + CPhysicsShell* self_shell=self_obj->PPhysicsShell(); + if(self_shell&&self_shell->isActive())self_shell->DisableCollision(); + CPHActivationShape activation_shape;//Fvector start_box;m_PhysicMovementControl.Box().getsize(start_box); + activation_shape.Create(in_out_pos,size,self_obj); + dBodySetGravityMode(activation_shape.ODEBody(),0); + activation_shape.Activate(size,1,1.f,M_PI/8.f); + in_out_pos.set(activation_shape.Position()); + activation_shape.Size(m_vExplodeSize); + activation_shape.Destroy(); + if(self_shell&&self_shell->isActive())self_shell->EnableCollision(); +} +void CExplosive::net_Relcase(CObject* O) +{ + if (GameID() == GAME_SINGLE) + { + if(O->ID()==m_iCurrentParentID) + m_iCurrentParentID=u16(-1); + } + + BLASTED_OBJECTS_I I=std::find(m_blasted_objects.begin(),m_blasted_objects.end(),smart_cast(O)); + if(m_blasted_objects.end()!=I) + { + m_blasted_objects.erase(I); + } +} + +u16 CExplosive::Initiator() +{ + u16 initiator=CurrentParentID(); + if(initiator==u16(-1))initiator=cast_game_object()->ID(); + return initiator; +} + +void CExplosive::UpdateExplosionParticles () +{ + if (!m_bDynamicParticles || m_pExpParticle == NULL || !m_pExpParticle->IsPlaying()) return; + CGameObject *GO=cast_game_object(); + if (!GO) return; + + Fmatrix ParticleMatrix = m_pExpParticle->XFORM(); + Fvector Vel; + Vel.sub(GO->Position(), ParticleMatrix.c); + ParticleMatrix.c.set(GO->Position()); + m_pExpParticle->UpdateParent(ParticleMatrix, Vel); +} + +bool CExplosive::Useful() const +{ + return m_explosion_flags.flags == 0; +} \ No newline at end of file diff --git a/src/xrGameLA/Explosive.h b/src/xrGameLA/Explosive.h new file mode 100644 index 000000000..eef2978c9 --- /dev/null +++ b/src/xrGameLA/Explosive.h @@ -0,0 +1,184 @@ +// Explosive.h: интерфейс для взврывающихся объектов +// +////////////////////////////////////////////////////////////////////// + +#pragma once + +#define SND_RIC_COUNT 5 + +#include "../Render.h" +#include "../feel_touch.h" +#include "inventory_item.h" +#include "ai_sounds.h" +#include "script_export_space.h" +#include "DamageSource.h" +#include "wallmark_manager.h" +#include "ParticlesObject.h" +class IRender_Light; +DEFINE_VECTOR(CPhysicsShellHolder*,BLASTED_OBJECTS_V,BLASTED_OBJECTS_I); +class CExplosive : + public IDamageSource +{ +private: + collide::rq_results rq_storage; + +public: + CExplosive(void); + virtual ~CExplosive(void); + + virtual void Load(LPCSTR section); + virtual void Load(CInifile const * ini,LPCSTR section); + + virtual void net_Destroy (); + virtual void net_Relcase (CObject* O); + virtual void UpdateCL(); + +private: + virtual void Explode(); +public: + virtual void ExplodeParams (const Fvector& pos, const Fvector& dir); + + static float ExplosionEffect (collide::rq_results& storage,CExplosive*exp_obj,CPhysicsShellHolder*blasted_obj, const Fvector &expl_centre, const float expl_radius); + + + virtual void OnEvent (NET_Packet& P, u16 type) ;//{inherited::OnEvent( P, type);} + virtual void OnAfterExplosion(); + virtual void OnBeforeExplosion(); + virtual void SetCurrentParentID (u16 parent_id) {m_iCurrentParentID = parent_id; } + IC u16 CurrentParentID () const {return m_iCurrentParentID;} + + virtual void SetInitiator(u16 id){SetCurrentParentID(id);} + virtual u16 Initiator(); + + virtual void UpdateExplosionPos(){} + virtual void GetExplVelocity(Fvector &v); + virtual void GetExplPosition(Fvector &p) ; + virtual void GetExplDirection(Fvector &d); + virtual void GenExplodeEvent (const Fvector& pos, const Fvector& normal); + virtual void FindNormal(Fvector& normal); + virtual CGameObject *cast_game_object()=0; + virtual CExplosive* cast_explosive(){return this;} + virtual IDamageSource* cast_IDamageSource() {return this;} + virtual void GetRayExplosionSourcePos(Fvector &pos); + virtual void GetExplosionBox (Fvector &size); + virtual void ActivateExplosionBox (const Fvector &size,Fvector &in_out_pos); + void SetExplosionSize (const Fvector &new_size); + virtual bool Useful () const; +protected: + bool IsSoundPlaying (){return !!sndExplode._feedback();} + bool IsExploded (){return !!m_explosion_flags.test(flExploded);} +public: + bool IsExploding (){return !!m_explosion_flags.test(flExploding);} +private: + void PositionUpdate (); +static void GetRaySourcePos (CExplosive *exp_obj,const Fvector &expl_centre,Fvector &p); + + void ExplodeWaveProcessObject(collide::rq_results& storage,CPhysicsShellHolder*sh); + void ExplodeWaveProcess (); +static float TestPassEffect (const Fvector &source_p, const Fvector &dir,float range,float ef_radius,collide::rq_results& storage, CObject* blasted_obj); + void LightCreate (); + void LightDestroy (); +protected: + + CWalmarkManager m_wallmark_manager; + //ID персонажа который иницировал действие + u16 m_iCurrentParentID; + + //bool m_bReadyToExplode; + Fvector m_vExplodePos; + Fvector m_vExplodeSize; + Fvector m_vExplodeDir; + + //параметры взрыва + float m_fBlastHit; + float m_fBlastHitImpulse; + float m_fBlastRadius; + + //параметры и количество осколков + float m_fFragsRadius; + float m_fFragHit; + float m_fFragHitImpulse; + int m_iFragsNum; + + //типы наносимых хитов + ALife::EHitType m_eHitTypeBlast; + ALife::EHitType m_eHitTypeFrag; + + //фактор подпроса предмета вверх взрывной волной + float m_fUpThrowFactor; + + //список пораженных объектов + BLASTED_OBJECTS_V m_blasted_objects; + + //текущая продолжительность взрыва + float m_fExplodeDuration; + //общее время взрыва + float m_fExplodeDurationMax; + //Время, через которое надо сделать взрывчатку невиимой, если она не становится невидимой во время взрыва + float m_fExplodeHideDurationMax; + //флаг состояния взрыва + enum{ + flExploding =1<<0 , + flExplodEventSent =1<<1 , + flReadyToExplode =1<<2 , + flExploded =1<<3 + }; + Flags8 m_explosion_flags; + /////////////////////////////////////////////// + //Должен ли объект быть скрыт после взрыва: true - для всех кроме дымовой гранаты + BOOL m_bHideInExplosion; + bool m_bAlreadyHidden; + virtual void HideExplosive (); + //bool m_bExploding; + //bool m_bExplodeEventSent; + + ////////////////////////////////////////////// + //для разлета осколков + float m_fFragmentSpeed; + + //звуки + ref_sound sndExplode; + ESoundTypes m_eSoundExplode; + + //размер отметки на стенах + float fWallmarkSize; + + //эффекты и подсветка + shared_str m_sExplodeParticles; + + //подсветка взрыва + ref_light m_pLight; + Fcolor m_LightColor; + float m_fLightRange; + float m_fLightTime; + + virtual void StartLight (); + virtual void StopLight (); + + BOOL m_bDynamicParticles; + CParticlesObject* m_pExpParticle; + virtual void UpdateExplosionParticles (); + + // эффектор + struct { +/* float time; + float amplitude; + float period_number; + shared_str file_name;*/ + shared_str effect_sect_name; + } effector; + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CExplosive) +#undef script_type_list +#define script_type_list save_type_list(CExplosive) + +IC void random_point_in_object_box(Fvector &out_pos,CObject* obj) +{ + const Fbox &l_b1 = obj->BoundingBox(); + Fvector l_c, l_d;l_b1.get_CD(l_c,l_d); + out_pos.random_point(l_d); + obj->XFORM().transform_tiny(out_pos); + out_pos.add(l_c); +} \ No newline at end of file diff --git a/src/xrGameLA/ExplosiveItem.cpp b/src/xrGameLA/ExplosiveItem.cpp new file mode 100644 index 000000000..bd365d5e4 --- /dev/null +++ b/src/xrGameLA/ExplosiveItem.cpp @@ -0,0 +1,100 @@ +////////////////////////////////////////////////////////////////////// +// ExplosiveItem.cpp: класс для вещи которая взрывается под +// действием различных хитов (канистры, +// балоны с газом и т.д.) +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "ExplosiveItem.h" + + +CExplosiveItem::CExplosiveItem(void) +{ +} +CExplosiveItem::~CExplosiveItem(void) +{ +} + +void CExplosiveItem::Load(LPCSTR section) +{ + inherited::Load (section); + CExplosive::Load (section); + m_flags.set (FUsingCondition, TRUE); + CDelayedActionFuse::Initialize (pSettings->r_float(section,"time_to_explode"),pSettings->r_float(section,"condition_to_explode")); + VERIFY(pSettings->line_exist (section,"set_timer_particles")); +} + +void CExplosiveItem::net_Destroy() +{ + inherited::net_Destroy(); + CExplosive::net_Destroy(); +} + +//void CExplosiveItem::Hit(float P, Fvector &dir, CObject* who, s16 element, +// Fvector position_in_object_space, float impulse, +// ALife::EHitType hit_type) +void CExplosiveItem::Hit (SHit* pHDS) +{ +// inherited::Hit(P,dir,who,element,position_in_object_space,impulse,hit_type); + if(CDelayedActionFuse::isActive())pHDS->power=0.f; + inherited::Hit(pHDS); + if(!CDelayedActionFuse::isActive()&&CDelayedActionFuse::CheckCondition(GetCondition())&&CExplosive::Initiator()==u16(-1)) + { + //запомнить того, кто взорвал вещь + SetInitiator( pHDS->who->ID()); + + } +} +void CExplosiveItem::StartTimerEffects () +{ + CParticlesPlayer::StartParticles(pSettings->r_string(*cNameSect(),"set_timer_particles"),Fvector().set(0,1,0),ID()); + +} +void CExplosiveItem::OnEvent (NET_Packet& P, u16 type) +{ + CExplosive::OnEvent (P, type); + inherited::OnEvent (P, type); + +} +void CExplosiveItem::UpdateCL() +{ + CExplosive::UpdateCL(); + inherited::UpdateCL(); +} +void CExplosiveItem::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + if(CDelayedActionFuse::isActive()&&CDelayedActionFuse::Update(GetCondition())) + { + Fvector normal; + FindNormal(normal); + CExplosive::GenExplodeEvent(Position(), normal); + CParticlesPlayer::StopParticles(ID(), BI_NONE, true); + } +} + +bool CExplosiveItem::shedule_Needed() +{ +//. return true; + + return ( inherited::shedule_Needed() || CDelayedActionFuse::isActive() ); +} + +void CExplosiveItem::renderable_Render() +{ + inherited::renderable_Render(); +} +void CExplosiveItem::net_Relcase(CObject* O ) +{ + CExplosive::net_Relcase(O); + inherited::net_Relcase(O); +} +void CExplosiveItem::ActivateExplosionBox (const Fvector &size,Fvector &in_out_pos) +{ + //PKinematics(Visual())->CalculateBones(); +} +void CExplosiveItem::GetRayExplosionSourcePos(Fvector &pos) +{ + random_point_in_object_box(pos,this); +} \ No newline at end of file diff --git a/src/xrGameLA/ExplosiveItem.h b/src/xrGameLA/ExplosiveItem.h new file mode 100644 index 000000000..3daae3863 --- /dev/null +++ b/src/xrGameLA/ExplosiveItem.h @@ -0,0 +1,45 @@ +////////////////////////////////////////////////////////////////////// +// ExplosiveItem.h: класс для вещи которая взрывается под +// действием различных хитов (канистры, +// балоны с газом и т.д.) +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "Explosive.h" +#include "inventory_item_object.h" +#include "DelayedActionFuse.h" +class CExplosiveItem: + public CInventoryItemObject , + public CDelayedActionFuse , + public CExplosive +{ +private: + typedef CInventoryItemObject inherited; + +public: + CExplosiveItem(void); + virtual ~CExplosiveItem(void); + + virtual void Load (LPCSTR section) ; + virtual BOOL net_Spawn (CSE_Abstract* DC) {return CInventoryItemObject::net_Spawn(DC);} + virtual void net_Destroy () ; + virtual void net_Export (NET_Packet& P) {CInventoryItemObject::net_Export(P);} + virtual void net_Import (NET_Packet& P) {CInventoryItemObject::net_Import(P);} + virtual void net_Relcase (CObject* O ) ; + virtual CGameObject *cast_game_object () {return this;} + virtual CExplosive* cast_explosive () {return this;} + virtual IDamageSource* cast_IDamageSource () {return CExplosive::cast_IDamageSource();} + virtual void GetRayExplosionSourcePos(Fvector &pos) ; + virtual void ActivateExplosionBox (const Fvector &size,Fvector &in_out_pos) ; + virtual void OnEvent (NET_Packet& P, u16 type) ; + virtual void Hit (SHit* pHDS) ; + virtual void shedule_Update (u32 dt) ; + virtual bool shedule_Needed (); + + virtual void UpdateCL () ; + virtual void renderable_Render () ; + virtual void ChangeCondition (float fDeltaCondition) {CInventoryItem::ChangeCondition(fDeltaCondition);}; + virtual void StartTimerEffects () ; + +}; \ No newline at end of file diff --git a/src/xrGameLA/ExplosiveRocket.cpp b/src/xrGameLA/ExplosiveRocket.cpp new file mode 100644 index 000000000..4901e8f91 --- /dev/null +++ b/src/xrGameLA/ExplosiveRocket.cpp @@ -0,0 +1,164 @@ +////////////////////////////////////////////////////////////////////// +// ExplosiveRocket.cpp: ракета, которой стреляет RocketLauncher +// взрывается при столкновении +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "../Include/xrRender/Kinematics.h" +#include "ExplosiveRocket.h" + + +CExplosiveRocket::CExplosiveRocket() +{ +} + +CExplosiveRocket::~CExplosiveRocket() +{ +} + +DLL_Pure *CExplosiveRocket::_construct () +{ + CCustomRocket::_construct (); + CInventoryItem::_construct (); + return (this); +} + +void CExplosiveRocket::Load(LPCSTR section) +{ + inherited::Load(section); + CInventoryItem::Load(section); + CExplosive::Load(section); +} + +BOOL CExplosiveRocket::net_Spawn(CSE_Abstract* DC) +{ + BOOL result = inherited::net_Spawn(DC); + result=result&&CInventoryItem::net_Spawn(DC); + Fvector box;BoundingBox().getsize(box); + float max_size=_max(_max(box.x,box.y),box.z); + box.set(max_size,max_size,max_size); + box.mul(3.f); + CExplosive::SetExplosionSize(box); + return result; +} + +void CExplosiveRocket::Contact(const Fvector &pos, const Fvector &normal) +{ + if(eCollide == m_eState) return; + + if(m_bLaunched) + CExplosive::GenExplodeEvent(pos,normal); + + inherited::Contact(pos, normal); +} + +void CExplosiveRocket::net_Destroy() +{ + CInventoryItem::net_Destroy(); + CExplosive::net_Destroy(); + inherited::net_Destroy(); +} + +void CExplosiveRocket::OnH_A_Independent() +{ + inherited::OnH_A_Independent(); +} + +void CExplosiveRocket::OnH_B_Independent(bool just_before_destroy) +{ + CInventoryItem::OnH_B_Independent(just_before_destroy); + inherited::OnH_B_Independent(just_before_destroy); +} + +void CExplosiveRocket::UpdateCL() +{ + if(eCollide == m_eState) + { + CExplosive::UpdateCL(); + inherited::UpdateCL(); + } + else + inherited::UpdateCL(); +} + + +void CExplosiveRocket::OnEvent (NET_Packet& P, u16 type) +{ + CExplosive::OnEvent(P, type); + inherited::OnEvent(P,type); +} + + +void CExplosiveRocket::make_Interpolation () +{ + inherited::make_Interpolation(); +} + +void CExplosiveRocket::PH_B_CrPr () +{ + inherited::PH_B_CrPr (); +} + +void CExplosiveRocket::PH_I_CrPr () +{ + inherited::PH_I_CrPr (); +} + +void CExplosiveRocket::PH_A_CrPr () +{ + inherited::PH_A_CrPr (); +} + +#ifdef DEBUG +void CExplosiveRocket::PH_Ch_CrPr () +{ + inherited::PH_Ch_CrPr (); +} + +void CExplosiveRocket::OnRender () +{ + inherited::OnRender (); +} +#endif + +void CExplosiveRocket::reinit () +{ + inherited::reinit (); + CInventoryItem::reinit (); +} + +void CExplosiveRocket::reload (LPCSTR section) +{ + inherited::reload (section); + CInventoryItem::reload (section); +} + +void CExplosiveRocket::activate_physic_shell () +{ + inherited::activate_physic_shell(); +} + +void CExplosiveRocket::on_activate_physic_shell () +{ + CCustomRocket::activate_physic_shell(); +} + +void CExplosiveRocket::setup_physic_shell () +{ + inherited::setup_physic_shell(); +} + +void CExplosiveRocket::create_physic_shell () +{ + inherited::create_physic_shell(); +} + +bool CExplosiveRocket::Useful () const +{ + return (inherited::Useful()); +} +void CExplosiveRocket::net_Relcase(CObject* O ) +{ + CExplosive::net_Relcase(O); + inherited::net_Relcase(O); +} \ No newline at end of file diff --git a/src/xrGameLA/ExplosiveRocket.h b/src/xrGameLA/ExplosiveRocket.h new file mode 100644 index 000000000..ce2f9b437 --- /dev/null +++ b/src/xrGameLA/ExplosiveRocket.h @@ -0,0 +1,77 @@ +////////////////////////////////////////////////////////////////////// +// ExplosiveRocket.h: ракета, которой стреляет RocketLauncher +// взрывается при столкновении +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "CustomRocket.h" +#include "Explosive.h" +#include "inventory_item.h" + +class CExplosiveRocket : + public CCustomRocket, + public CInventoryItem, + public CExplosive +{ +private: + typedef CCustomRocket inherited; + friend CRocketLauncher; +public: + CExplosiveRocket(void); + virtual ~CExplosiveRocket(void); + virtual DLL_Pure *_construct (); +public: + virtual CExplosive *cast_explosive () {return this;} + virtual CInventoryItem *cast_inventory_item () {return this;} + virtual CAttachableItem *cast_attachable_item () {return this;} + virtual CWeapon *cast_weapon () {return NULL;} + virtual CGameObject *cast_game_object () {return this;} + virtual IDamageSource* cast_IDamageSource() {return CExplosive::cast_IDamageSource();} + virtual void on_activate_physic_shell(); +public: + + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Relcase (CObject* O ); + virtual void OnH_A_Independent (); + virtual void OnH_B_Independent (bool just_before_destroy); + virtual void UpdateCL (); + + virtual void Contact(const Fvector &pos, const Fvector &normal); + + virtual void OnEvent (NET_Packet& P, u16 type) ; + + virtual void Hit (SHit* pHDS) + { inherited::Hit(pHDS); }; + +public: + virtual BOOL UsedAI_Locations () {return inherited::UsedAI_Locations();} + virtual void net_Import (NET_Packet& P) {inherited::net_Import(P);} + virtual void net_Export (NET_Packet& P) {inherited::net_Export(P);} + + virtual void save (NET_Packet &output_packet) {inherited::save(output_packet);} + virtual void load (IReader &input_packet) {inherited::load(input_packet);} + virtual BOOL net_SaveRelevant () {return inherited::net_SaveRelevant();} + + virtual void OnH_A_Chield () {inherited::OnH_A_Chield();} + virtual void OnH_B_Chield () {inherited::OnH_B_Chield();} + virtual void renderable_Render () {inherited::renderable_Render();} + virtual void make_Interpolation (); + virtual void PH_B_CrPr (); // actions & operations before physic correction-prediction steps + virtual void PH_I_CrPr (); // actions & operations after correction before prediction steps +#ifdef DEBUG + virtual void PH_Ch_CrPr (); // + virtual void OnRender (); +#endif + virtual void PH_A_CrPr (); // actions & operations after phisic correction-prediction steps + virtual void reinit (); + virtual void reload (LPCSTR section); + virtual void activate_physic_shell (); + virtual void setup_physic_shell (); + virtual void create_physic_shell (); + +public: + virtual bool Useful () const; +}; \ No newline at end of file diff --git a/src/xrGameLA/ExplosiveScript.cpp b/src/xrGameLA/ExplosiveScript.cpp new file mode 100644 index 000000000..dffd26c31 --- /dev/null +++ b/src/xrGameLA/ExplosiveScript.cpp @@ -0,0 +1,15 @@ +#include "pch_script.h" +#include "Explosive.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CExplosive::script_register(lua_State *L) +{ + module(L) + [ + class_("explosive") + .def("explode", (&CExplosive::Explode)) + ]; +} + diff --git a/src/xrGameLA/ExtendedGeom.h b/src/xrGameLA/ExtendedGeom.h new file mode 100644 index 000000000..520c66fae --- /dev/null +++ b/src/xrGameLA/ExtendedGeom.h @@ -0,0 +1,276 @@ +#ifndef EXTENDED_GEOM +#define EXTENDED_GEOM +#include "PHObject.h" +#include "ode_include.h" +#include "physicscommon.h" +#include "MathUtils.h" + + + +#ifdef DEBUG +extern u32 dbg_total_saved_tries ; +#endif +class CPhysicsShellHolder; + + + + + +class CObjectContactCallback +{ + + CObjectContactCallback *next ; + ObjectContactCallbackFun *callback ; + public: + CObjectContactCallback (ObjectContactCallbackFun *c) + :callback(c) + { + next=NULL; VERIFY(c); + } + ~CObjectContactCallback() + { + xr_delete(next); + } + void Add(ObjectContactCallbackFun *c) + { + VERIFY(c); + VERIFY(callback!=c); + + if(next) + { + next->Add(c); + } + else + { + next= new CObjectContactCallback(c); + } + } + bool HasCallback(ObjectContactCallbackFun *c) + { + for(CObjectContactCallback*i=this;i;i=i->next) + { + VERIFY(i->callback); + if(c==i->callback) return true; + } + return false; + } + +static void RemoveCallback(CObjectContactCallback* &callbacks,ObjectContactCallbackFun *c) + { + if(!callbacks) return; + VERIFY(c); + VERIFY(callbacks->callback); + + if(c==callbacks->callback) + { + CObjectContactCallback *del=callbacks; + callbacks=callbacks->next; + del->next=NULL; + xr_delete(del); + VERIFY(!callbacks||!callbacks->HasCallback(c)); + } else{ + for(CObjectContactCallback *i=callbacks->next,*p=callbacks;i;) + { + + VERIFY(p->callback); + VERIFY(i->callback); + VERIFY(i); + VERIFY(p); + if(c==i->callback) + { + CObjectContactCallback *del=i; + p->next=i->next;del->next=NULL;xr_delete(del); + VERIFY(!callbacks->HasCallback(c)); + break; + } + i=i->next; + p=p->next; + VERIFY(p->next==i); + } + } + } + + void Call(bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) + { + for(CObjectContactCallback*i=this;i;i=i->next) + { + VERIFY(i->callback); + i->callback(do_colide,bo1,c,material_1,material_2); + } + } +}; + +class CGameObject; +struct dxGeomUserData +{ + dVector3 last_pos ; + bool pushing_neg,pushing_b_neg,b_static_colide ; + CDB::TRI *neg_tri,*b_neg_tri ; + CPHObject *ph_object ; + CPhysicsShellHolder *ph_ref_object ; + u16 material ; + u16 tri_material ; + ContactCallbackFun *callback ; + void *callback_data ; +// ObjectContactCallbackFun *object_callback ; + CObjectContactCallback *object_callbacks ; + u16 element_position ; + u16 bone_id ; + xr_vector cashed_tries ; + Fvector last_aabb_size ; + Fvector last_aabb_pos ; + +// struct ContactsParameters +// { +// dReal damping; +// dReal spring; +// dReal bonce; +// dReal bonce_vel; +// dReal mu; +// unsigned int maxc; +// }; +}; + +IC dxGeomUserData* dGeomGetUserData(dxGeom* geom) +{ + return (dxGeomUserData*) dGeomGetData(geom); +} + +IC dGeomID retrieveGeom(dGeomID geom) +{ + if(dGeomGetClass(geom)==dGeomTransformClass) + return dGeomTransformGetGeom(geom); + else + return geom; +} + +IC dxGeomUserData* retrieveGeomUserData(dGeomID geom) +{ + return dGeomGetUserData(retrieveGeom(geom)); + //if(dGeomGetClass(geom)==dGeomTransformClass) + // return dGeomGetUserData(dGeomTransformGetGeom(geom)); + //else + // return dGeomGetUserData(geom); +} + +IC CPhysicsShellHolder* retrieveRefObject(dGeomID geom) +{ + dxGeomUserData* ud=dGeomGetUserData(retrieveGeom(geom)); + if(ud)return ud->ph_ref_object; + else return NULL; +} +IC void dGeomCreateUserData(dxGeom* geom) +{ + if(!geom) return; + dGeomSetData(geom, new dxGeomUserData()); + (dGeomGetUserData(geom))->pushing_neg=false; + (dGeomGetUserData(geom))->pushing_b_neg=false; + (dGeomGetUserData(geom))->b_static_colide=true; + (dGeomGetUserData(geom))->last_pos[0]=-dInfinity; + (dGeomGetUserData(geom))->last_pos[1]=-dInfinity; + (dGeomGetUserData(geom))->last_pos[2]=-dInfinity; + (dGeomGetUserData(geom))->ph_object=NULL; + (dGeomGetUserData(geom))->material=0; + (dGeomGetUserData(geom))->tri_material=0; + (dGeomGetUserData(geom))->callback=NULL; + (dGeomGetUserData(geom))->object_callbacks=NULL; + (dGeomGetUserData(geom))->ph_ref_object=NULL; + (dGeomGetUserData(geom))->element_position=u16(-1); + (dGeomGetUserData(geom))->bone_id=u16(-1); + (dGeomGetUserData(geom))->callback_data=NULL; + //((dxGeomUserData*)dGeomGetData(geom))->ContactsParameters::mu=1.f; + //((dxGeomUserData*)dGeomGetData(geom))->ContactsParameters::damping=1.f; + //((dxGeomUserData*)dGeomGetData(geom))->ContactsParameters::spring=1.f; + //((dxGeomUserData*)dGeomGetData(geom))->ContactsParameters::bonce=0.f; + //((dxGeomUserData*)dGeomGetData(geom))->ContactsParameters::bonce_vel=0.f; +} + + + +IC void dGeomDestroyUserData(dxGeom* geom) +{ + if(!geom) return ; + dxGeomUserData* P = dGeomGetUserData(geom) ; + if(P) + { +#ifdef DEBUG + dbg_total_saved_tries-=P->cashed_tries.size() ; +#endif + P->cashed_tries .clear() ; + xr_delete (P->object_callbacks) ; + } + xr_delete (P) ; + dGeomSetData (geom,0) ; +} + +IC void dGeomUserDataSetCallbackData(dxGeom* geom,void *cd) +{ + (dGeomGetUserData(geom))->callback_data=cd; +} +IC void dGeomUserDataSetPhObject(dxGeom* geom,CPHObject* phObject) +{ + (dGeomGetUserData(geom))->ph_object=phObject; +} + +IC void dGeomUserDataSetPhysicsRefObject(dxGeom* geom,CPhysicsShellHolder* phRefObject) +{ + (dGeomGetUserData(geom))->ph_ref_object=phRefObject; +} + +IC void dGeomUserDataSetContactCallback(dxGeom* geom,ContactCallbackFun* callback) +{ + (dGeomGetUserData(geom))->callback=callback; +} + +IC void dGeomUserDataSetObjectContactCallback(dxGeom* geom,ObjectContactCallbackFun *obj_callback) +{ + xr_delete((dGeomGetUserData(geom))->object_callbacks); + if(obj_callback)(dGeomGetUserData(geom))->object_callbacks= new CObjectContactCallback(obj_callback); +} + +IC void dGeomUserDataAddObjectContactCallback(dxGeom* geom,ObjectContactCallbackFun *obj_callback) +{ + if((dGeomGetUserData(geom))->object_callbacks) + { + (dGeomGetUserData(geom))->object_callbacks->Add(obj_callback); + } + else dGeomUserDataSetObjectContactCallback(geom,obj_callback); +} + +IC void dGeomUserDataRemoveObjectContactCallback(dxGeom* geom,ObjectContactCallbackFun *obj_callback) +{ + CObjectContactCallback::RemoveCallback((dGeomGetUserData(geom))->object_callbacks,(obj_callback)); +} + +IC bool dGeomUserDataHasCallback(dxGeom* geom,ObjectContactCallbackFun *obj_callback) +{ + geom=retrieveGeom(geom); + if(geom&&dGeomGetUserData(geom)&&(dGeomGetUserData(geom))->object_callbacks) + return (dGeomGetUserData(geom))->object_callbacks->HasCallback(obj_callback); + else return false; +} +IC void dGeomUserDataSetElementPosition(dxGeom* geom,u16 e_pos) +{ + (dGeomGetUserData(geom))->element_position=e_pos; +} +IC void dGeomUserDataSetBoneId(dxGeom* geom,u16 bone_id) +{ + (dGeomGetUserData(geom))->bone_id=bone_id; +} +IC void dGeomUserDataResetLastPos(dxGeom* geom) +{ + (dGeomGetUserData(geom))->last_pos[0]=-dInfinity; + (dGeomGetUserData(geom))->last_pos[1]=-dInfinity; + (dGeomGetUserData(geom))->last_pos[2]=-dInfinity; +} +IC void dGeomUserDataClearCashedTries(dxGeom* geom) +{ + dxGeomUserData* P = dGeomGetUserData(geom); + +#ifdef DEBUG + dbg_total_saved_tries-=P->cashed_tries.size(); +#endif + P->cashed_tries.clear(); + P->last_aabb_size.set(0.f,0.f,0.f); +} +#endif \ No newline at end of file diff --git a/src/xrGameLA/F1.cpp b/src/xrGameLA/F1.cpp new file mode 100644 index 000000000..34632e8be --- /dev/null +++ b/src/xrGameLA/F1.cpp @@ -0,0 +1,20 @@ +#include "pch_script.h" +#include "f1.h" + +CF1::CF1(void) { +} + +CF1::~CF1(void) { +} + +using namespace luabind; + +#pragma optimize("s",on) +void CF1::script_register (lua_State *L) +{ + module(L) + [ + class_("CF1") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/F1.h b/src/xrGameLA/F1.h new file mode 100644 index 000000000..88b943728 --- /dev/null +++ b/src/xrGameLA/F1.h @@ -0,0 +1,18 @@ +#pragma once + +#include "grenade.h" +#include "script_export_space.h" + +class CF1 : + public CGrenade +{ + typedef CGrenade inherited; +public: + CF1(void); + virtual ~CF1(void); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CF1) +#undef script_type_list +#define script_type_list save_type_list(CF1) diff --git a/src/xrGameLA/FadedBall.cpp b/src/xrGameLA/FadedBall.cpp new file mode 100644 index 000000000..a122b6add --- /dev/null +++ b/src/xrGameLA/FadedBall.cpp @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////// +// FadedBall.cpp +// FadedBall - артефакт блеклый шар +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "FadedBall.h" +#include "PhysicsShell.h" + + +CFadedBall::CFadedBall(void) +{ +} + +CFadedBall::~CFadedBall(void) +{ +} + +void CFadedBall::Load(LPCSTR section) +{ + inherited::Load(section); +} + diff --git a/src/xrGameLA/FadedBall.h b/src/xrGameLA/FadedBall.h new file mode 100644 index 000000000..162275cdc --- /dev/null +++ b/src/xrGameLA/FadedBall.h @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////// +// FadedBall.h +// FadedBall - артефакт блеклый шар +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CFadedBall : public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CFadedBall(void); + virtual ~CFadedBall(void); + + virtual void Load (LPCSTR section); + +protected: +}; \ No newline at end of file diff --git a/src/xrGameLA/FastDelegate.h b/src/xrGameLA/FastDelegate.h new file mode 100644 index 000000000..87dee0acb --- /dev/null +++ b/src/xrGameLA/FastDelegate.h @@ -0,0 +1,2114 @@ +// FastDelegate.h +// Efficient delegates in C++ that generate only two lines of asm code! +// Documentation is found at http://www.codeproject.com/cpp/FastDelegate.asp +// +// - Don Clugston, Mar 2004. +// Major contributions were made by Jody Hagins. +// History: +// 24-Apr-04 1.0 * Submitted to CodeProject. +// 28-Apr-04 1.1 * Prevent most unsafe uses of evil static function hack. +// * Improved syntax for horrible_cast (thanks Paul Bludov). +// * Tested on Metrowerks MWCC and Intel ICL (IA32) +// * Compiled, but not run, on Comeau C++ and Intel Itanium ICL. +// 27-Jun-04 1.2 * Now works on Borland C++ Builder 5.5 +// * Now works on /clr "managed C++" code on VC7, VC7.1 +// * Comeau C++ now compiles without warnings. +// * Prevent the virtual inheritance case from being used on +// VC6 and earlier, which generate incorrect code. +// * Improved warning and error messages. Non-standard hacks +// now have compile-time checks to make them safer. +// * implicit_cast used instead of static_cast in many cases. +// * If calling a const member function, a const class pointer can be used. +// * MakeDelegate() global helper function added to simplify pass-by-value. +// * Added fastdelegate.clear() +// 16-Jul-04 1.2.1* Workaround for gcc bug (const member function pointers in templates) +// 30-Oct-04 1.3 * Support for (non-void) return values. +// * No more workarounds in client code! +// MSVC and Intel now use a clever hack invented by John Dlugosz: +// - The FASTDELEGATEDECLARE workaround is no longer necessary. +// - No more warning messages for VC6 +// * Less use of macros. Error messages should be more comprehensible. +// * Added include guards +// * Added FastDelegate::empty() to test if invocation is safe (Thanks Neville Franks). +// * Now tested on VS 2005 Express Beta, PGI C++ +// 24-Dec-04 1.4 * Added DelegateMemento, to allow collections of disparate delegates. +// * <,>,<=,>= comparison operators to allow storage in ordered containers. +// * Substantial reduction of code size, especially the 'Closure' class. +// * Standardised all the compiler-specific workarounds. +// * MFP conversion now works for CodePlay (but not yet supported in the full code). +// * Now compiles without warnings on _any_ supported compiler, including BCC 5.5.1 +// * New syntax: FastDelegate< int (char *, double) >. +// 14-Feb-05 1.4.1* Now treats =0 as equivalent to .clear(), ==0 as equivalent to .empty(). (Thanks elfric). +// * Now tested on Intel ICL for AMD64, VS2005 Beta for AMD64 and Itanium. +// 30-Mar-05 1.5 * Safebool idiom: "if (dg)" is now equivalent to "if (!dg.empty())" +// * Fully supported by CodePlay VectorC +// * Bugfix for Metrowerks: empty() was buggy because a valid MFP can be 0 on MWCC! +// * More optimal assignment,== and != operators for static function pointers. + +#ifndef FASTDELEGATE_H +#define FASTDELEGATE_H +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include // to allow <,> comparisons + +////////////////////////////////////////////////// +//#define xr_stdcall +#define xr_stdcall __stdcall +//#define xr_stdcall __cdecl +/////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Configuration options +// +//////////////////////////////////////////////////////////////////////////////// + +// Uncomment the following #define for optimally-sized delegates. +// In this case, the generated asm code is almost identical to the code you'd get +// if the compiler had native support for delegates. +// It will not work on systems where sizeof(dataptr) < sizeof(codeptr). +// Thus, it will not work for DOS compilers using the medium model. +// It will also probably fail on some DSP systems. +#define FASTDELEGATE_USESTATICFUNCTIONHACK + +// Uncomment the next line to allow function declarator syntax. +// It is automatically enabled for those compilers where it is known to work. +//#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +//////////////////////////////////////////////////////////////////////////////// +// Compiler identification for workarounds +// +//////////////////////////////////////////////////////////////////////////////// + +// Compiler identification. It's not easy to identify Visual C++ because +// many vendors fraudulently define Microsoft's identifiers. +#if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__VECTOR_C) && !defined(__ICL) && !defined(__BORLANDC__) +#define FASTDLGT_ISMSVC + +#if (_MSC_VER <1300) // Many workarounds are required for VC6. +#define FASTDLGT_VC6 +#pragma warning(disable:4786) // disable this ridiculous warning +#endif + +#endif + +// Does the compiler uses Microsoft's member function pointer structure? +// If so, it needs special treatment. +// Metrowerks CodeWarrior, Intel, and CodePlay fraudulently define Microsoft's +// identifier, _MSC_VER. We need to filter Metrowerks out. +#if defined(_MSC_VER) && !defined(__MWERKS__) +#define FASTDLGT_MICROSOFT_MFP + +#if !defined(__VECTOR_C) +// CodePlay doesn't have the __single/multi/virtual_inheritance keywords +#define FASTDLGT_HASINHERITANCE_KEYWORDS +#endif +#endif + +// Does it allow function declarator syntax? The following compilers are known to work: +#if defined(FASTDLGT_ISMSVC) && (_MSC_VER >=1310) // VC 7.1 +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX +#endif + +// Gcc(2.95+), and versions of Digital Mars, Intel and Comeau in common use. +#if defined (__DMC__) || defined(__GNUC__) || defined(__ICL) || defined(__COMO__) +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX +#endif + +// It works on Metrowerks MWCC 3.2.2. From boost.Config it should work on earlier ones too. +#if defined (__MWERKS__) +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX +#endif + +#ifdef __GNUC__ // Workaround GCC bug #8271 + // At present, GCC doesn't recognize constness of MFPs in templates +#define FASTDELEGATE_GCC_BUG_8271 +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// General tricks used in this code +// +// (a) Error messages are generated by typdefing an array of negative size to +// generate compile-time errors. +// (b) Warning messages on MSVC are generated by declaring unused variables, and +// enabling the "variable XXX is never used" warning. +// (c) Unions are used in a few compiler-specific cases to perform illegal casts. +// (d) For Microsoft and Intel, when adjusting the 'this' pointer, it's cast to +// (char *) first to ensure that the correct number of *bytes* are added. +// +//////////////////////////////////////////////////////////////////////////////// +// Helper templates +// +//////////////////////////////////////////////////////////////////////////////// + + +namespace fastdelegate { +namespace detail { // we'll hide the implementation details in a nested namespace. + +// implicit_cast< > +// I believe this was originally going to be in the C++ standard but +// was left out by accident. It's even milder than static_cast. +// I use it instead of static_cast<> to emphasize that I'm not doing +// anything nasty. +// Usage is identical to static_cast<> +template +inline OutputClass implicit_cast(InputClass input){ + return input; +} + +// horrible_cast< > +// This is truly evil. It completely subverts C++'s type system, allowing you +// to cast from any class to any other class. Technically, using a union +// to perform the cast is undefined behaviour (even in C). But we can see if +// it is OK by checking that the union is the same size as each of its members. +// horrible_cast<> should only be used for compiler-specific workarounds. +// Usage is identical to reinterpret_cast<>. + +// This union is declared outside the horrible_cast because BCC 5.5.1 +// can't inline a function with a nested class, and gives a warning. +template +union horrible_union{ + OutputClass out; + InputClass in; +}; + +template +inline OutputClass horrible_cast(const InputClass input){ + horrible_union u; + // Cause a compile-time error if in, out and u are not the same size. + // If the compile fails here, it means the compiler has peculiar + // unions which would prevent the cast from working. + typedef int ERROR_CantUseHorrible_cast[sizeof(InputClass)==sizeof(u) + && sizeof(InputClass)==sizeof(OutputClass) ? 1 : -1]; + u.in = input; + return u.out; +} + +//////////////////////////////////////////////////////////////////////////////// +// Workarounds +// +//////////////////////////////////////////////////////////////////////////////// + +// Backwards compatibility: This macro used to be necessary in the virtual inheritance +// case for Intel and Microsoft. Now it just forward-declares the class. +#define FASTDELEGATEDECLARE(CLASSNAME) class CLASSNAME; + +// Prevent use of the static function hack with the DOS medium model. +#ifdef __MEDIUM__ +#undef FASTDELEGATE_USESTATICFUNCTIONHACK +#endif + +// DefaultVoid - a workaround for 'void' templates in VC6. +// +// (1) VC6 and earlier do not allow 'void' as a default template argument. +// (2) They also doesn't allow you to return 'void' from a function. +// +// Workaround for (1): Declare a dummy type 'DefaultVoid' which we use +// when we'd like to use 'void'. We convert it into 'void' and back +// using the templates DefaultVoidToVoid<> and VoidToDefaultVoid<>. +// Workaround for (2): On VC6, the code for calling a void function is +// identical to the code for calling a non-void function in which the +// return value is never used, provided the return value is returned +// in the EAX register, rather than on the stack. +// This is true for most fundamental types such as int, enum, void *. +// Const void * is the safest option since it doesn't participate +// in any automatic conversions. But on a 16-bit compiler it might +// cause extra code to be generated, so we disable it for all compilers +// except for VC6 (and VC5). +#ifdef FASTDLGT_VC6 +// VC6 workaround +typedef const void * DefaultVoid; +#else +// On any other compiler, just use a normal void. +typedef void DefaultVoid; +#endif + +// Translate from 'DefaultVoid' to 'void'. +// Everything else is unchanged +template +struct DefaultVoidToVoid { typedef T type; }; + +template <> +struct DefaultVoidToVoid { typedef void type; }; + +// Translate from 'void' into 'DefaultVoid' +// Everything else is unchanged +template +struct VoidToDefaultVoid { typedef T type; }; + +template <> +struct VoidToDefaultVoid { typedef DefaultVoid type; }; + + + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 1: +// +// Conversion of member function pointer to a standard form +// +//////////////////////////////////////////////////////////////////////////////// + +// GenericClass is a fake class, ONLY used to provide a type. +// It is vitally important that it is never defined, so that the compiler doesn't +// think it can optimize the invocation. For example, Borland generates simpler +// code if it knows the class only uses single inheritance. + +// Compilers using Microsoft's structure need to be treated as a special case. +#ifdef FASTDLGT_MICROSOFT_MFP + +#ifdef FASTDLGT_HASINHERITANCE_KEYWORDS + // For Microsoft and Intel, we want to ensure that it's the most efficient type of MFP + // (4 bytes), even when the /vmg option is used. Declaring an empty class + // would give 16 byte pointers in this case.... + class __single_inheritance GenericClass; +#endif + // ...but for Codeplay, an empty class *always* gives 4 byte pointers. + // If compiled with the /clr option ("managed C++"), the JIT compiler thinks + // it needs to load GenericClass before it can call any of its functions, + // (compiles OK but crashes at runtime!), so we need to declare an + // empty class to make it happy. + // Codeplay and VC4 can't cope with the unknown_inheritance case either. + class GenericClass {}; +#else + class GenericClass; +#endif + +// The size of a single inheritance member function pointer. +const int SINGLE_MEMFUNCPTR_SIZE = sizeof(void (xr_stdcall GenericClass::*)()); + +// SimplifyMemFunc< >::Convert() +// +// A template function that converts an arbitrary member function pointer into the +// simplest possible form of member function pointer, using a supplied 'this' pointer. +// According to the standard, this can be done legally with reinterpret_cast<>. +// For (non-standard) compilers which use member function pointers which vary in size +// depending on the class, we need to use knowledge of the internal structure of a +// member function pointer, as used by the compiler. Template specialization is used +// to distinguish between the sizes. Because some compilers don't support partial +// template specialisation, I use full specialisation of a wrapper struct. + +// general case -- don't know how to convert it. Force a compile failure +template +struct SimplifyMemFunc { + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // Unsupported member function type -- force a compile failure. + // (it's illegal to have a array with negative size). + typedef char ERROR_Unsupported_member_function_pointer_on_this_compiler[N-100]; + return 0; + } +}; + +// For compilers where all member func ptrs are the same size, everything goes here. +// For non-standard compilers, only single_inheritance classes go here. +template <> +struct SimplifyMemFunc { + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { +#if defined __DMC__ + // Digital Mars doesn't allow you to cast between abitrary PMF's, + // even though the standard says you can. The 32-bit compiler lets you + // static_cast through an int, but the DOS compiler doesn't. + bound_func = horrible_cast(function_to_bind); +#else + bound_func = reinterpret_cast(function_to_bind); +#endif + return reinterpret_cast(pthis); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 1b: +// +// Workarounds for Microsoft and Intel +// +//////////////////////////////////////////////////////////////////////////////// + + +// Compilers with member function pointers which violate the standard (MSVC, Intel, Codeplay), +// need to be treated as a special case. +#ifdef FASTDLGT_MICROSOFT_MFP + +// We use unions to perform horrible_casts. I would like to use #pragma pack(push, 1) +// at the start of each function for extra safety, but VC6 seems to ICE +// intermittently if you do this inside a template. + +// __multiple_inheritance classes go here +// Nasty hack for Microsoft and Intel (IA32 and Itanium) +template<> +struct SimplifyMemFunc< SINGLE_MEMFUNCPTR_SIZE + sizeof(int) > { + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // We need to use a horrible_cast to do this conversion. + // In MSVC, a multiple inheritance member pointer is internally defined as: + union { + XFuncType func; + struct { + GenericMemFuncType funcaddress; // points to the actual member function + int delta; // #BYTES to be added to the 'this' pointer + }s; + } u; + // Check that the horrible_cast will work + typedef int ERROR_CantUsehorrible_cast[sizeof(function_to_bind)==sizeof(u.s)? 1 : -1]; + u.func = function_to_bind; + bound_func = u.s.funcaddress; + return reinterpret_cast(reinterpret_cast(pthis) + u.s.delta); + } +}; + +// virtual inheritance is a real nuisance. It's inefficient and complicated. +// On MSVC and Intel, there isn't enough information in the pointer itself to +// enable conversion to a closure pointer. Earlier versions of this code didn't +// work for all cases, and generated a compile-time error instead. +// But a very clever hack invented by John M. Dlugosz solves this problem. +// My code is somewhat different to his: I have no asm code, and I make no +// assumptions about the calling convention that is used. + +// In VC++ and ICL, a virtual_inheritance member pointer +// is internally defined as: +struct MicrosoftVirtualMFP { + void (GenericClass::*codeptr)(); // points to the actual member function + int delta; // #bytes to be added to the 'this' pointer + int vtable_index; // or 0 if no virtual inheritance +}; +// The CRUCIAL feature of Microsoft/Intel MFPs which we exploit is that the +// m_codeptr member is *always* called, regardless of the values of the other +// members. (This is *not* true for other compilers, eg GCC, which obtain the +// function address from the vtable if a virtual function is being called). +// Dlugosz's trick is to make the codeptr point to a probe function which +// returns the 'this' pointer that was used. + +// Define a generic class that uses virtual inheritance. +// It has a trival member function that returns the value of the 'this' pointer. +struct GenericVirtualClass : virtual public GenericClass +{ + typedef GenericVirtualClass * (GenericVirtualClass::*ProbePtrType)(); + GenericVirtualClass * GetThis() { return this; } +}; + +// __virtual_inheritance classes go here +template <> +struct SimplifyMemFunc +{ + + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + union { + XFuncType func; + GenericClass* (X::*ProbeFunc)(); + MicrosoftVirtualMFP s; + } u; + u.func = function_to_bind; + bound_func = reinterpret_cast(u.s.codeptr); + union { + GenericVirtualClass::ProbePtrType virtfunc; + MicrosoftVirtualMFP s; + } u2; + // Check that the horrible_cast<>s will work + typedef int ERROR_CantUsehorrible_cast[sizeof(function_to_bind)==sizeof(u.s) + && sizeof(function_to_bind)==sizeof(u.ProbeFunc) + && sizeof(u2.virtfunc)==sizeof(u2.s) ? 1 : -1]; + // Unfortunately, taking the address of a MF prevents it from being inlined, so + // this next line can't be completely optimised away by the compiler. + u2.virtfunc = &GenericVirtualClass::GetThis; + u.s.codeptr = u2.s.codeptr; + return (pthis->*u.ProbeFunc)(); + } +}; + +#if (_MSC_VER <1300) + +// Nasty hack for Microsoft Visual C++ 6.0 +// unknown_inheritance classes go here +// There is a compiler bug in MSVC6 which generates incorrect code in this case!! +template <> +struct SimplifyMemFunc +{ + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // There is an apalling but obscure compiler bug in MSVC6 and earlier: + // vtable_index and 'vtordisp' are always set to 0 in the + // unknown_inheritance case! + // This means that an incorrect function could be called!!! + // Compiling with the /vmg option leads to potentially incorrect code. + // This is probably the reason that the IDE has a user interface for specifying + // the /vmg option, but it is disabled - you can only specify /vmg on + // the command line. In VC1.5 and earlier, the compiler would ICE if it ever + // encountered this situation. + // It is OK to use the /vmg option if /vmm or /vms is specified. + + // Fortunately, the wrong function is only called in very obscure cases. + // It only occurs when a derived class overrides a virtual function declared + // in a virtual base class, and the member function + // points to the *Derived* version of that function. The problem can be + // completely averted in 100% of cases by using the *Base class* for the + // member fpointer. Ie, if you use the base class as an interface, you'll + // stay out of trouble. + // Occasionally, you might want to point directly to a derived class function + // that isn't an override of a base class. In this case, both vtable_index + // and 'vtordisp' are zero, but a virtual_inheritance pointer will be generated. + // We can generate correct code in this case. To prevent an incorrect call from + // ever being made, on MSVC6 we generate a warning, and call a function to + // make the program crash instantly. + typedef char ERROR_VC6CompilerBug[-100]; + return 0; + } +}; + + +#else + +// Nasty hack for Microsoft and Intel (IA32 and Itanium) +// unknown_inheritance classes go here +// This is probably the ugliest bit of code I've ever written. Look at the casts! +// There is a compiler bug in MSVC6 which prevents it from using this code. +template <> +struct SimplifyMemFunc +{ + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // The member function pointer is 16 bytes long. We can't use a normal cast, but + // we can use a union to do the conversion. + union { + XFuncType func; + // In VC++ and ICL, an unknown_inheritance member pointer + // is internally defined as: + struct { + GenericMemFuncType m_funcaddress; // points to the actual member function + int delta; // #bytes to be added to the 'this' pointer + int vtordisp; // #bytes to add to 'this' to find the vtable + int vtable_index; // or 0 if no virtual inheritance + } s; + } u; + // Check that the horrible_cast will work + typedef int ERROR_CantUsehorrible_cast[sizeof(XFuncType)==sizeof(u.s)? 1 : -1]; + u.func = function_to_bind; + bound_func = u.s.funcaddress; + int virtual_delta = 0; + if (u.s.vtable_index) { // Virtual inheritance is used + // First, get to the vtable. + // It is 'vtordisp' bytes from the start of the class. + const int * vtable = *reinterpret_cast( + reinterpret_cast(pthis) + u.s.vtordisp ); + + // 'vtable_index' tells us where in the table we should be looking. + virtual_delta = u.s.vtordisp + *reinterpret_cast( + reinterpret_cast(vtable) + u.s.vtable_index); + } + // The int at 'virtual_delta' gives us the amount to add to 'this'. + // Finally we can add the three components together. Phew! + return reinterpret_cast( + reinterpret_cast(pthis) + u.s.delta + virtual_delta); + }; +}; +#endif // MSVC 7 and greater + +#endif // MS/Intel hacks + +} // namespace detail + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 2: +// +// Define the delegate storage, and cope with static functions +// +//////////////////////////////////////////////////////////////////////////////// + +// DelegateMemento -- an opaque structure which can hold an arbitary delegate. +// It knows nothing about the calling convention or number of arguments used by +// the function pointed to. +// It supplies comparison operators so that it can be stored in STL collections. +// It cannot be set to anything other than null, nor invoked directly: +// it must be converted to a specific delegate. + +// Implementation: +// There are two possible implementations: the Safe method and the Evil method. +// DelegateMemento - Safe version +// +// This implementation is standard-compliant, but a bit tricky. +// A static function pointer is stored inside the class. +// Here are the valid values: +// +-- Static pointer --+--pThis --+-- pMemFunc-+-- Meaning------+ +// | 0 | 0 | 0 | Empty | +// | !=0 |(dontcare)| Invoker | Static function| +// | 0 | !=0 | !=0* | Method call | +// +--------------------+----------+------------+----------------+ +// * For Metrowerks, this can be 0. (first virtual function in a +// single_inheritance class). +// When stored stored inside a specific delegate, the 'dontcare' entries are replaced +// with a reference to the delegate itself. This complicates the = and == operators +// for the delegate class. + +// DelegateMemento - Evil version +// +// For compilers where data pointers are at least as big as code pointers, it is +// possible to store the function pointer in the this pointer, using another +// horrible_cast. In this case the DelegateMemento implementation is simple: +// +--pThis --+-- pMemFunc-+-- Meaning---------------------+ +// | 0 | 0 | Empty | +// | !=0 | !=0* | Static function or method call| +// +----------+------------+-------------------------------+ +// * For Metrowerks, this can be 0. (first virtual function in a +// single_inheritance class). +// Note that the Sun C++ and MSVC documentation explicitly state that they +// support static_cast between void * and function pointers. + +class DelegateMemento { +protected: + // the data is protected, not private, because many + // compilers have problems with template friends. + typedef void (detail::GenericClass::*GenericMemFuncType)(); // arbitrary MFP. + detail::GenericClass *m_pthis; + GenericMemFuncType m_pFunction; + +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + typedef void (xr_stdcall *GenericFuncPtr)(); // arbitrary code pointer + GenericFuncPtr m_pStaticFunction; +#endif + +public: +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + DelegateMemento() : m_pthis(0), m_pFunction(0), m_pStaticFunction(0) {}; + void clear() { + m_pthis=0; m_pFunction=0; m_pStaticFunction=0; + } +#else + DelegateMemento() : m_pthis(0), m_pFunction(0) {}; + void clear() { m_pthis=0; m_pFunction=0; } +#endif +public: +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + inline bool IsEqual (const DelegateMemento &x) const{ + // We have to cope with the static function pointers as a special case + if (m_pFunction!=x.m_pFunction) return false; + // the static function ptrs must either both be equal, or both be 0. + if (m_pStaticFunction!=x.m_pStaticFunction) return false; + if (m_pStaticFunction!=0) return m_pthis==x.m_pthis; + else return true; + } +#else // Evil Method + inline bool IsEqual (const DelegateMemento &x) const{ + return m_pthis==x.m_pthis && m_pFunction==x.m_pFunction; + } +#endif + // Provide a strict weak ordering for DelegateMementos. + inline bool IsLess(const DelegateMemento &right) const { + // deal with static function pointers first +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + if (m_pStaticFunction !=0 || right.m_pStaticFunction!=0) + return m_pStaticFunction < right.m_pStaticFunction; +#endif + if (m_pthis !=right.m_pthis) return m_pthis < right.m_pthis; + // There are no ordering operators for member function pointers, + // but we can fake one by comparing each byte. The resulting ordering is + // arbitrary (and compiler-dependent), but it permits storage in ordered STL containers. + return memcmp(&m_pFunction, &right.m_pFunction, sizeof(m_pFunction)) < 0; + + } + // BUGFIX (Mar 2005): + // We can't just compare m_pFunction because on Metrowerks, + // m_pFunction can be zero even if the delegate is not empty! + inline bool operator ! () const // Is it bound to anything? + { return m_pthis==0 && m_pFunction==0; } + inline bool empty() const // Is it bound to anything? + { return m_pthis==0 && m_pFunction==0; } +public: + DelegateMemento & operator = (const DelegateMemento &right) { + SetMementoFrom(right); + return *this; + } + inline bool operator <(const DelegateMemento &right) { + return IsLess(right); + } + inline bool operator >(const DelegateMemento &right) { + return right.IsLess(*this); + } + DelegateMemento (const DelegateMemento &right) : + m_pFunction(right.m_pFunction), m_pthis(right.m_pthis) +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + , m_pStaticFunction (right.m_pStaticFunction) +#endif + {} +protected: + void SetMementoFrom(const DelegateMemento &right) { + m_pFunction = right.m_pFunction; + m_pthis = right.m_pthis; +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = right.m_pStaticFunction; +#endif + } +}; + + +// ClosurePtr<> +// +// A private wrapper class that adds function signatures to DelegateMemento. +// It's the class that does most of the actual work. +// The signatures are specified by: +// GenericMemFunc: must be a type of GenericClass member function pointer. +// StaticFuncPtr: must be a type of function pointer with the same signature +// as GenericMemFunc. +// UnvoidStaticFuncPtr: is the same as StaticFuncPtr, except on VC6 +// where it never returns void (returns DefaultVoid instead). + +// An outer class, FastDelegateN<>, handles the invoking and creates the +// necessary typedefs. +// This class does everything else. + +namespace detail { + +template < class GenericMemFunc, class StaticFuncPtr, class UnvoidStaticFuncPtr> +class ClosurePtr : public DelegateMemento { +public: + // These functions are for setting the delegate to a member function. + + // Here's the clever bit: we convert an arbitrary member function into a + // standard form. XMemFunc should be a member function of class X, but I can't + // enforce that here. It needs to be enforced by the wrapper class. + template < class X, class XMemFunc > + inline void bindmemfunc(X *pthis, XMemFunc function_to_bind ) { + m_pthis = SimplifyMemFunc< sizeof(function_to_bind) > + ::Convert(pthis, function_to_bind, m_pFunction); +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = 0; +#endif + } + // For const member functions, we only need a const class pointer. + // Since we know that the member function is const, it's safe to + // remove the const qualifier from the 'this' pointer with a const_cast. + // VC6 has problems if we just overload 'bindmemfunc', so we give it a different name. + template < class X, class XMemFunc> + inline void bindconstmemfunc(const X *pthis, XMemFunc function_to_bind) { + m_pthis= SimplifyMemFunc< sizeof(function_to_bind) > + ::Convert(const_cast(pthis), function_to_bind, m_pFunction); +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = 0; +#endif + } +#ifdef FASTDELEGATE_GCC_BUG_8271 // At present, GCC doesn't recognize constness of MFPs in templates + template < class X, class XMemFunc> + inline void bindmemfunc(const X *pthis, XMemFunc function_to_bind) { + bindconstmemfunc(pthis, function_to_bind); +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = 0; +#endif + } +#endif + // These functions are required for invoking the stored function + inline GenericClass *GetClosureThis() const { return m_pthis; } + inline GenericMemFunc GetClosureMemPtr() const { return reinterpret_cast(m_pFunction); } + +// There are a few ways of dealing with static function pointers. +// There's a standard-compliant, but tricky method. +// There's also a straightforward hack, that won't work on DOS compilers using the +// medium memory model. It's so evil that I can't recommend it, but I've +// implemented it anyway because it produces very nice asm code. + +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + +// ClosurePtr<> - Safe version +// +// This implementation is standard-compliant, but a bit tricky. +// I store the function pointer inside the class, and the delegate then +// points to itself. Whenever the delegate is copied, these self-references +// must be transformed, and this complicates the = and == operators. +public: + // The next two functions are for operator ==, =, and the copy constructor. + // We may need to convert the m_pthis pointers, so that + // they remain as self-references. + template< class DerivedClass > + inline void CopyFrom (DerivedClass *pParent, const DelegateMemento &x) { + SetMementoFrom(x); + if (m_pStaticFunction!=0) { + // transform self references... + m_pthis=reinterpret_cast(pParent); + } + } + // For static functions, the 'static_function_invoker' class in the parent + // will be called. The parent then needs to call GetStaticFunction() to find out + // the actual function to invoke. + template < class DerivedClass, class ParentInvokerSig > + inline void bindstaticfunc(DerivedClass *pParent, ParentInvokerSig static_function_invoker, + StaticFuncPtr function_to_bind ) { + if (function_to_bind==0) { // cope with assignment to 0 + m_pFunction=0; + } else { + bindmemfunc(pParent, static_function_invoker); + } + m_pStaticFunction=reinterpret_cast(function_to_bind); + } + inline UnvoidStaticFuncPtr GetStaticFunction() const { + return reinterpret_cast(m_pStaticFunction); + } +#else + +// ClosurePtr<> - Evil version +// +// For compilers where data pointers are at least as big as code pointers, it is +// possible to store the function pointer in the this pointer, using another +// horrible_cast. Invocation isn't any faster, but it saves 4 bytes, and +// speeds up comparison and assignment. If C++ provided direct language support +// for delegates, they would produce asm code that was almost identical to this. +// Note that the Sun C++ and MSVC documentation explicitly state that they +// support static_cast between void * and function pointers. + + template< class DerivedClass > + inline void CopyFrom (DerivedClass *pParent, const DelegateMemento &right) { + SetMementoFrom(right); + } + // For static functions, the 'static_function_invoker' class in the parent + // will be called. The parent then needs to call GetStaticFunction() to find out + // the actual function to invoke. + // ******** EVIL, EVIL CODE! ******* + template < class DerivedClass, class ParentInvokerSig> + inline void bindstaticfunc(DerivedClass *pParent, ParentInvokerSig static_function_invoker, + StaticFuncPtr function_to_bind) { + if (function_to_bind==0) { // cope with assignment to 0 + m_pFunction=0; + } else { + // We'll be ignoring the 'this' pointer, but we need to make sure we pass + // a valid value to bindmemfunc(). + bindmemfunc(pParent, static_function_invoker); + } + + // WARNING! Evil hack. We store the function in the 'this' pointer! + // Ensure that there's a compilation failure if function pointers + // and data pointers have different sizes. + // If you get this error, you need to #undef FASTDELEGATE_USESTATICFUNCTIONHACK. + typedef int ERROR_CantUseEvilMethod[sizeof(GenericClass *)==sizeof(function_to_bind) ? 1 : -1]; + m_pthis = horrible_cast(function_to_bind); + // MSVC, SunC++ and DMC accept the following (non-standard) code: +// m_pthis = static_cast(static_cast(function_to_bind)); + // BCC32, Comeau and DMC accept this method. MSVC7.1 needs __int64 instead of long +// m_pthis = reinterpret_cast(reinterpret_cast(function_to_bind)); + } + // ******** EVIL, EVIL CODE! ******* + // This function will be called with an invalid 'this' pointer!! + // We're just returning the 'this' pointer, converted into + // a function pointer! + inline UnvoidStaticFuncPtr GetStaticFunction() const { + // Ensure that there's a compilation failure if function pointers + // and data pointers have different sizes. + // If you get this error, you need to #undef FASTDELEGATE_USESTATICFUNCTIONHACK. + typedef int ERROR_CantUseEvilMethod[sizeof(UnvoidStaticFuncPtr)==sizeof(this) ? 1 : -1]; + return horrible_cast(this); + } +#endif // !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + + // Does the closure contain this static function? + inline bool IsEqualToStaticFuncPtr(StaticFuncPtr funcptr){ + if (funcptr==0) return empty(); + // For the Evil method, if it doesn't actually contain a static function, this will return an arbitrary + // value that is not equal to any valid function pointer. + else return funcptr==reinterpret_cast(GetStaticFunction()); + } +}; + + +} // namespace detail + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 3: +// +// Wrapper classes to ensure type safety +// +//////////////////////////////////////////////////////////////////////////////// + + +// Once we have the member function conversion templates, it's easy to make the +// wrapper classes. So that they will work with as many compilers as possible, +// the classes are of the form +// FastDelegate3 +// They can cope with any combination of parameters. The max number of parameters +// allowed is 8, but it is trivial to increase this limit. +// Note that we need to treat const member functions seperately. +// All this class does is to enforce type safety, and invoke the delegate with +// the correct list of parameters. + +// Because of the weird rule about the class of derived member function pointers, +// you sometimes need to apply a downcast to the 'this' pointer. +// This is the reason for the use of "implicit_cast(pthis)" in the code below. +// If CDerivedClass is derived from CBaseClass, but doesn't override SimpleVirtualFunction, +// without this trick you'd need to write: +// MyDelegate(static_cast(&d), &CDerivedClass::SimpleVirtualFunction); +// but with the trick you can write +// MyDelegate(&d, &CDerivedClass::SimpleVirtualFunction); + +// RetType is the type the compiler uses in compiling the template. For VC6, +// it cannot be void. DesiredRetType is the real type which is returned from +// all of the functions. It can be void. + +// Implicit conversion to "bool" is achieved using the safe_bool idiom, +// using member data pointers (MDP). This allows "if (dg)..." syntax +// Because some compilers (eg codeplay) don't have a unique value for a zero +// MDP, an extra padding member is added to the SafeBool struct. +// Some compilers (eg VC6) won't implicitly convert from 0 to an MDP, so +// in that case the static function constructor is not made explicit; this +// allows "if (dg==0) ..." to compile. + +//N=0 +template +class FastDelegate0 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(); + typedef RetType (*UnvoidStaticFunctionPtr)(); + typedef RetType (xr_stdcall detail::GenericClass::*GenericMemFn)(); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate0 type; + + // Construction and comparison functions + FastDelegate0() { clear(); } + FastDelegate0(const FastDelegate0 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate0 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate0 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate0 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate0 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate0 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate0(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)() ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)()) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate0(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)() const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)() const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate0(DesiredRetType (*function_to_bind)() ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)() ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)()) { + m_Closure.bindstaticfunc(this, &FastDelegate0::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() () const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction() const { + return (*(m_Closure.GetStaticFunction()))(); } +}; + +//N=1 +template +class FastDelegate1 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1); + typedef RetType (xr_stdcall detail::GenericClass::*GenericMemFn)(Param1 p1); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate1 type; + + // Construction and comparison functions + FastDelegate1() { clear(); } + FastDelegate1(const FastDelegate1 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate1 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate1 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate1 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate1 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate1 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate1(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate1(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate1(DesiredRetType (*function_to_bind)(Param1 p1) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1)) { + m_Closure.bindstaticfunc(this, &FastDelegate1::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1) const { + return (*(m_Closure.GetStaticFunction()))(p1); } +}; + +//N=2 +template +class FastDelegate2 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2); + typedef RetType (xr_stdcall detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate2 type; + + // Construction and comparison functions + FastDelegate2() { clear(); } + FastDelegate2(const FastDelegate2 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate2 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate2 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate2 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate2 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate2 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate2(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate2(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate2(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2)) { + m_Closure.bindstaticfunc(this, &FastDelegate2::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2); } +}; + +//N=3 +template +class FastDelegate3 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3); + typedef RetType (xr_stdcall detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate3 type; + + // Construction and comparison functions + FastDelegate3() { clear(); } + FastDelegate3(const FastDelegate3 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate3 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate3 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate3 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate3 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate3 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate3(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate3(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate3(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3)) { + m_Closure.bindstaticfunc(this, &FastDelegate3::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3); } +}; + +//N=4 +template +class FastDelegate4 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4); + typedef RetType (xr_stdcall detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate4 type; + + // Construction and comparison functions + FastDelegate4() { clear(); } + FastDelegate4(const FastDelegate4 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate4 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate4 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate4 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate4 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate4 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate4(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate4(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate4(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4)) { + m_Closure.bindstaticfunc(this, &FastDelegate4::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4); } +}; + +//N=5 +template +class FastDelegate5 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5); + typedef RetType (xr_stdcall detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate5 type; + + // Construction and comparison functions + FastDelegate5() { clear(); } + FastDelegate5(const FastDelegate5 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate5 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate5 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate5 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate5 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate5 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate5(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate5(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate5(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5)) { + m_Closure.bindstaticfunc(this, &FastDelegate5::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5); } +}; + +//N=6 +template +class FastDelegate6 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6); + typedef RetType (xr_stdcall detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate6 type; + + // Construction and comparison functions + FastDelegate6() { clear(); } + FastDelegate6(const FastDelegate6 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate6 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate6 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate6 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate6 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate6 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate6(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate6(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate6(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6)) { + m_Closure.bindstaticfunc(this, &FastDelegate6::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5, p6); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5, p6); } +}; + +//N=7 +template +class FastDelegate7 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7); + typedef RetType (xr_stdcall detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate7 type; + + // Construction and comparison functions + FastDelegate7() { clear(); } + FastDelegate7(const FastDelegate7 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate7 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate7 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate7 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate7 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate7 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate7(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate7(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate7(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7)) { + m_Closure.bindstaticfunc(this, &FastDelegate7::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5, p6, p7); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5, p6, p7); } +}; + +//N=8 +template +class FastDelegate8 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8); + typedef RetType (xr_stdcall detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate8 type; + + // Construction and comparison functions + FastDelegate8() { clear(); } + FastDelegate8(const FastDelegate8 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate8 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate8 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate8 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate8 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate8 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate8(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate8(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (xr_stdcall X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate8(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8)) { + m_Closure.bindstaticfunc(this, &FastDelegate8::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5, p6, p7, p8); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5, p6, p7, p8); } +}; + + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 4: +// +// FastDelegate<> class (Original author: Jody Hagins) +// Allows boost::function style syntax like: +// FastDelegate< double (int, long) > +// instead of: +// FastDelegate2< int, long, double > +// +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +// Declare FastDelegate as a class template. It will be specialized +// later for all number of arguments. +template +class FastDelegate; + +//N=0 +// Specialization to allow use of +// FastDelegate< R ( ) > +// instead of +// FastDelegate0 < R > +template +class FastDelegate< R ( ) > + // Inherit from FastDelegate0 so that it can be treated just like a FastDelegate0 + : public FastDelegate0 < R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate0 < R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (xr_stdcall X::* function_to_bind)( )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (xr_stdcall X::* function_to_bind)( ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=1 +// Specialization to allow use of +// FastDelegate< R ( Param1 ) > +// instead of +// FastDelegate1 < Param1, R > +template +class FastDelegate< R ( Param1 ) > + // Inherit from FastDelegate1 so that it can be treated just like a FastDelegate1 + : public FastDelegate1 < Param1, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate1 < Param1, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=2 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2 ) > +// instead of +// FastDelegate2 < Param1, Param2, R > +template +class FastDelegate< R ( Param1, Param2 ) > + // Inherit from FastDelegate2 so that it can be treated just like a FastDelegate2 + : public FastDelegate2 < Param1, Param2, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate2 < Param1, Param2, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=3 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3 ) > +// instead of +// FastDelegate3 < Param1, Param2, Param3, R > +template +class FastDelegate< R ( Param1, Param2, Param3 ) > + // Inherit from FastDelegate3 so that it can be treated just like a FastDelegate3 + : public FastDelegate3 < Param1, Param2, Param3, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate3 < Param1, Param2, Param3, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=4 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4 ) > +// instead of +// FastDelegate4 < Param1, Param2, Param3, Param4, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4 ) > + // Inherit from FastDelegate4 so that it can be treated just like a FastDelegate4 + : public FastDelegate4 < Param1, Param2, Param3, Param4, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate4 < Param1, Param2, Param3, Param4, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=5 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5 ) > +// instead of +// FastDelegate5 < Param1, Param2, Param3, Param4, Param5, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5 ) > + // Inherit from FastDelegate5 so that it can be treated just like a FastDelegate5 + : public FastDelegate5 < Param1, Param2, Param3, Param4, Param5, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate5 < Param1, Param2, Param3, Param4, Param5, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=6 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6 ) > +// instead of +// FastDelegate6 < Param1, Param2, Param3, Param4, Param5, Param6, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6 ) > + // Inherit from FastDelegate6 so that it can be treated just like a FastDelegate6 + : public FastDelegate6 < Param1, Param2, Param3, Param4, Param5, Param6, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate6 < Param1, Param2, Param3, Param4, Param5, Param6, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=7 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7 ) > +// instead of +// FastDelegate7 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7 ) > + // Inherit from FastDelegate7 so that it can be treated just like a FastDelegate7 + : public FastDelegate7 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate7 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=8 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8 ) > +// instead of +// FastDelegate8 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8 ) > + // Inherit from FastDelegate8 so that it can be treated just like a FastDelegate8 + : public FastDelegate8 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate8 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (xr_stdcall X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + + +#endif //FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 5: +// +// MakeDelegate() helper function +// +// MakeDelegate(&x, &X::func) returns a fastdelegate of the type +// necessary for calling x.func() with the correct number of arguments. +// This makes it possible to eliminate many typedefs from user code. +// +//////////////////////////////////////////////////////////////////////////////// + +// Also declare overloads of a MakeDelegate() global function to +// reduce the need for typedefs. +// We need seperate overloads for const and non-const member functions. +// Also, because of the weird rule about the class of derived member function pointers, +// implicit downcasts may need to be applied later to the 'this' pointer. +// That's why two classes (X and Y) appear in the definitions. Y must be implicitly +// castable to X. + +// Workaround for VC6. VC6 needs void return types converted into DefaultVoid. +// GCC 3.2 and later won't compile this unless it's preceded by 'typename', +// but VC6 doesn't allow 'typename' in this context. +// So, I have to use a macro. + +#ifdef FASTDLGT_VC6 +#define FASTDLGT_RETTYPE detail::VoidToDefaultVoid::type +#else +#define FASTDLGT_RETTYPE RetType +#endif + +//N=0 +template +FastDelegate0 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)()) { + return FastDelegate0(x, func); +} + +template +FastDelegate0 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)() const) { + return FastDelegate0(x, func); +} + +//N=1 +template +FastDelegate1 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1)) { + return FastDelegate1(x, func); +} + +template +FastDelegate1 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1) const) { + return FastDelegate1(x, func); +} + +//N=2 +template +FastDelegate2 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1, Param2 p2)) { + return FastDelegate2(x, func); +} + +template +FastDelegate2 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1, Param2 p2) const) { + return FastDelegate2(x, func); +} + +//N=3 +template +FastDelegate3 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1, Param2 p2, Param3 p3)) { + return FastDelegate3(x, func); +} + +template +FastDelegate3 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1, Param2 p2, Param3 p3) const) { + return FastDelegate3(x, func); +} + +//N=4 +template +FastDelegate4 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4)) { + return FastDelegate4(x, func); +} + +template +FastDelegate4 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const) { + return FastDelegate4(x, func); +} + +//N=5 +template +FastDelegate5 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5)) { + return FastDelegate5(x, func); +} + +template +FastDelegate5 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const) { + return FastDelegate5(x, func); +} + +//N=6 +template +FastDelegate6 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6)) { + return FastDelegate6(x, func); +} + +template +FastDelegate6 MakeDelegate(Y* x, RetType (xr_stdcall X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const) { + return FastDelegate6(x, func); +} + +//N=7 +template +FastDelegate7 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7)) { + return FastDelegate7(x, func); +} + +template +FastDelegate7 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const) { + return FastDelegate7(x, func); +} + +//N=8 +template +FastDelegate8 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8)) { + return FastDelegate8(x, func); +} + +template +FastDelegate8 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const) { + return FastDelegate8(x, func); +} + + +// clean up after ourselves... +#undef FASTDLGT_RETTYPE + +} // namespace fastdelegate + +#endif // !defined(FASTDELEGATE_H) + diff --git a/src/xrGameLA/Fireball.cpp b/src/xrGameLA/Fireball.cpp new file mode 100644 index 000000000..890263a4c --- /dev/null +++ b/src/xrGameLA/Fireball.cpp @@ -0,0 +1,154 @@ +#include "stdafx.h" +#include "Fireball.h" +#include "xr_level_controller.h" + +CFireball::CFireball() : CWeaponKnife() +{ +} + +CFireball::~CFireball(void) +{ +} + +void CFireball::Load(LPCSTR section) +{ + //inherited::Load(section); + CWeapon::Load(section); + + fWallmarkSize = pSettings->r_float(section,"wm_size"); + + m_sounds.LoadSound(section,"snd_shoot", "sndShot" , false, SOUND_TYPE_WEAPON_SHOOTING); + + knife_material_idx = GMLib.GetMaterialIdx(pSettings->r_string(*hud_sect,"fireball_material")); +} + +bool CFireball::Action(u16 cmd, u32 flags) +{ + switch(cmd){ + case kWPN_FIRE: { + if (flags&CMD_START){ + SwitchState(ePreFire); + return true; + } else if (flags&CMD_STOP && GetState()==ePreFire) { + SwitchState(eIdle); + return true; + } + } return true; + case kWPN_ZOOM: return true; + + } + return inherited::Action(cmd, flags); +} + +void CFireball::OnStateSwitch (u32 S) +{ + if (S==eFire) { + int t=10; + } + inherited::OnStateSwitch(S); + switch (S) { + case eFire: { + StartFlameParticles(); + fTime += fTimeToFire; + }return; + case eFire2: { + //Msg("eFire2 CFireball called!!! WTF???"); + }return; + case ePreFire: { + switch2_PreFire (); + }break; + } +} + + + +void CFireball::switch2_PreFire () +{ + if(IsPending()) return; + + PlayHUDMotion("anim_activate", FALSE, this, ePreFire); + SetPending (TRUE); +} + +void CFireball::OnAnimationEnd (u32 state) +{ + switch (state) + { + case eFire: { + Fvector p1, d; + p1.set(get_LastFP()); + d.set(get_LastFD()); + + KnifeStrike(p1,d); + + SwitchState(eIdle); + }return; + case ePreFire: SetPending (FALSE); SwitchState(eFire); return; + + } + inherited::OnAnimationEnd(state); +} + + +void CFireball::LoadFireParams(LPCSTR section, LPCSTR prefix) +{ + CWeapon::LoadFireParams(section, prefix); + + fvHitPower_1 = fvHitPower; + fHitImpulse_1 = fHitImpulse; + m_eHitType_1 = ALife::g_tfString2HitType(pSettings->r_string(section, "hit_type")); +} + + + +void CFireball::UpdateCL () +{ + inherited::UpdateCL (); + float dt = Device.fTimeDelta; + + + + //когда происходит апдейт состояния оружия + //ничего другого не делать + if(GetNextState() == GetState()) + { + switch (GetState()) + { + case eShowing: + case eHiding: + case eIdle: + fTime -= dt; + if (fTime<0) + fTime = 0; + break; + case eFire: + //if(iAmmoElapsed>0) + // state_Fire (dt); + + if(fTime<=0) StopShooting(); + else fTime -= dt; + + break; + case eHidden: break; + } + } +} + + + + +#include "pch_script.h" + +using namespace luabind; + +void CFireball::script_register (lua_State *L) +{ + module(L) + [ + class_("CFireball") + .def(constructor<>()) + ]; +} + + + diff --git a/src/xrGameLA/Fireball.h b/src/xrGameLA/Fireball.h new file mode 100644 index 000000000..1bf2cc6c3 --- /dev/null +++ b/src/xrGameLA/Fireball.h @@ -0,0 +1,29 @@ +#pragma once +#include "bolt.h" +#include "WeaponKnife.h" +#include "script_export_space.h" +#include "../gamemtllib.h" +#include "level.h" + +class CFireball : + public CWeaponKnife +{ + typedef CWeaponKnife inherited; +public: + CFireball(); + virtual ~CFireball(void); + + virtual void Load (LPCSTR section); + virtual bool Action (u16 cmd, u32 flags); + virtual void OnStateSwitch (u32 S); + virtual void OnAnimationEnd (u32 state); + virtual void switch2_PreFire (); + virtual void LoadFireParams (LPCSTR section, LPCSTR prefix); + virtual void UpdateCL (); +public: + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CFireball) +#undef script_type_list +#define script_type_list save_type_list(CFireball) diff --git a/src/xrGameLA/FoodItem.cpp b/src/xrGameLA/FoodItem.cpp new file mode 100644 index 000000000..04c1f44d1 --- /dev/null +++ b/src/xrGameLA/FoodItem.cpp @@ -0,0 +1,10 @@ +#include "stdafx.h" + +#include "FoodItem.h" +CFoodItem::CFoodItem() +{ +} + +CFoodItem::~CFoodItem() +{ +} diff --git a/src/xrGameLA/FoodItem.h b/src/xrGameLA/FoodItem.h new file mode 100644 index 000000000..2b6c17cf4 --- /dev/null +++ b/src/xrGameLA/FoodItem.h @@ -0,0 +1,11 @@ +#pragma once + +#include "eatable_item_object.h" + +class CFoodItem: public CEatableItemObject +{ +public: + CFoodItem (); + virtual ~CFoodItem (); + +}; diff --git a/src/xrGameLA/FryupZone.cpp b/src/xrGameLA/FryupZone.cpp new file mode 100644 index 000000000..22bc2ebf6 --- /dev/null +++ b/src/xrGameLA/FryupZone.cpp @@ -0,0 +1,15 @@ +#include "stdafx.h" +#include "FryupZone.h" + +CFryupZone::CFryupZone() +{ +} +CFryupZone::~CFryupZone() +{ +} + +#ifdef DEBUG +void CFryupZone::OnRender() +{ +} +#endif diff --git a/src/xrGameLA/FryupZone.h b/src/xrGameLA/FryupZone.h new file mode 100644 index 000000000..2070a849f --- /dev/null +++ b/src/xrGameLA/FryupZone.h @@ -0,0 +1,16 @@ +#pragma once + +#include "script_object.h" + +class CFryupZone : public CScriptObject { + typedef CScriptObject inherited; + +public: + CFryupZone (); + virtual ~CFryupZone (); + +#ifdef DEBUG + virtual void OnRender ( ); +#endif + +}; \ No newline at end of file diff --git a/src/xrGameLA/GalantineArtifact.cpp b/src/xrGameLA/GalantineArtifact.cpp new file mode 100644 index 000000000..0c1063de4 --- /dev/null +++ b/src/xrGameLA/GalantineArtifact.cpp @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////// +// GalantineArtifact.cpp +// GalantineArtefact - артефакт ведбмин студень +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "GalantineArtifact.h" + + +CGalantineArtefact::CGalantineArtefact(void) +{ +} + +CGalantineArtefact::~CGalantineArtefact(void) +{ +} + +void CGalantineArtefact::Load(LPCSTR section) +{ + inherited::Load(section); +} + diff --git a/src/xrGameLA/GalantineArtifact.h b/src/xrGameLA/GalantineArtifact.h new file mode 100644 index 000000000..d8fcc4696 --- /dev/null +++ b/src/xrGameLA/GalantineArtifact.h @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////// +// GalantineArtifact.h +// GalantineArtefact - артефакт ведбмин студень +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CGalantineArtefact : public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CGalantineArtefact(void); + virtual ~CGalantineArtefact(void); + + virtual void Load (LPCSTR section); + +protected: +}; \ No newline at end of file diff --git a/src/xrGameLA/GameObject.cpp b/src/xrGameLA/GameObject.cpp new file mode 100644 index 000000000..971d905ba --- /dev/null +++ b/src/xrGameLA/GameObject.cpp @@ -0,0 +1,937 @@ +#include "pch_script.h" +#include "GameObject.h" +//#include "../Include/xrRender/RenderVisual.h" +#include "../Include/xrRender/RenderVisual.h" +#include "PhysicsShell.h" +#include "ai_space.h" +#include "CustomMonster.h" +#include "physicobject.h" +#include "HangingLamp.h" +#include "PhysicsShell.h" +#include "game_sv_single.h" +#include "level_graph.h" +#include "ph_shell_interface.h" +#include "script_game_object.h" +#include "xrserver_objects_alife.h" +#include "xrServer_Objects_ALife_Items.h" +#include "game_cl_base.h" +#include "object_factory.h" +#include "../Include/xrRender/Kinematics.h" +#include "ai_object_location_impl.h" +#include "game_graph.h" +#include "ai_debug.h" +#include "../xr_3da/igame_level.h" +#include "level.h" +#include "../../xrNetServer/net_utils.h" +#include "script_callback_ex.h" +#include "MathUtils.h" +#include "game_cl_base_weapon_usage_statistic.h" +//#include "reward_event_generator.h" +#include "game_level_cross_table.h" +//#include "ai_obstacle.h" +#include "magic_box3.h" +#include "animation_movement_controller.h" +#include "../xr_3da/xr_collide_form.h" + +#ifdef DEBUG +# include "debug_renderer.h" +# include "PHDebug.h" +#endif + +ENGINE_API bool g_dedicated_server; + +CGameObject::CGameObject () +{ + init (); + //----------------------------------------- + m_bCrPr_Activated = false; + m_dwCrPr_ActivationStep = 0; + m_spawn_time = 0; + m_ai_location = !g_dedicated_server ? new CAI_ObjectLocation() : 0; + m_server_flags.one (); + + m_callbacks = new CALLBACK_MAP(); + m_anim_mov_ctrl = 0; +} + +CGameObject::~CGameObject () +{ + VERIFY ( !animation_movement( ) ); + VERIFY (!m_ini_file); + VERIFY (!m_lua_game_object); + VERIFY (!m_spawned); + xr_delete (m_ai_location); + xr_delete (m_callbacks); +} + +void CGameObject::init () +{ + m_lua_game_object = 0; + m_script_clsid = -1; + m_ini_file = 0; + m_spawned = false; +} + +void CGameObject::Load(LPCSTR section) +{ + inherited::Load (section); + ISpatial* self = smart_cast (this); + if (self) { + // #pragma todo("to Dima: All objects are visible for AI ???") + // self->spatial.type |= STYPE_VISIBLEFORAI; + self->spatial.type &= ~STYPE_REACTTOSOUND; + } +} + +void CGameObject::reinit () +{ + m_visual_callback.clear (); + if (!g_dedicated_server) + ai_location().reinit (); + + // clear callbacks + for (CALLBACK_MAP_IT it = m_callbacks->begin(); it != m_callbacks->end(); ++it) it->second.clear(); + m_callbacks->erase(m_callbacks->begin(), m_callbacks->end()); +} + +void CGameObject::reload (LPCSTR section) +{ + m_script_clsid = object_factory().script_clsid(CLS_ID); +} + +void CGameObject::net_Destroy () +{ +#ifdef DEBUG + if (psAI_Flags.test(aiDestroy)) + Msg ("Destroying client object [%d][%s][%x]",ID(),*cName(),this); +#endif + + VERIFY (m_spawned); + if(animation_movement_controlled()) + destroy_anim_mov_ctrl (); + + xr_delete (m_ini_file); + + m_script_clsid = -1; + if (Visual() && smart_cast(Visual())) + smart_cast(Visual())->Callback (0,0); + + inherited::net_Destroy (); + setReady (FALSE); + g_pGameLevel->Objects.net_Unregister (this); + + if (this == Level().CurrentEntity()) + { + Level().SetEntity (0); + Level().SetControlEntity (0); + } + + Level().RemoveObject_From_4CrPr(this); + +//. Parent = 0; + + CScriptBinder::net_Destroy (); + + xr_delete (m_lua_game_object); + m_spawned = false; + + +} + +void CGameObject::OnEvent (NET_Packet& P, u16 type) +{ + switch (type) + { + case GE_HIT: + case GE_HIT_STATISTIC: + { +/* + u16 id,weapon_id; + Fvector dir; + float power, impulse; + s16 element; + Fvector position_in_bone_space; + u16 hit_type; + float ap = 0.0f; + + P.r_u16 (id); + P.r_u16 (weapon_id); + P.r_dir (dir); + P.r_float (power); + P.r_s16 (element); + P.r_vec3 (position_in_bone_space); + P.r_float (impulse); + P.r_u16 (hit_type); //hit type + if ((ALife::EHitType)hit_type == ALife::eHitTypeFireWound) + { + P.r_float (ap); + } + + CObject* Hitter = Level().Objects.net_Find(id); + CObject* Weapon = Level().Objects.net_Find(weapon_id); + + SHit HDS = SHit(power, dir, Hitter, element, position_in_bone_space, impulse, (ALife::EHitType)hit_type, ap); +*/ + SHit HDS; + HDS.PACKET_TYPE = type; + HDS.Read_Packet_Cont(P); +// Msg("Hit received: %d[%d,%d]", HDS.whoID, HDS.weaponID, HDS.BulletID); + CObject* Hitter = Level().Objects.net_Find(HDS.whoID); + CObject* Weapon = Level().Objects.net_Find(HDS.weaponID); + HDS.who = Hitter; + //------------------------------------------------------- + switch (HDS.PACKET_TYPE) + { + case GE_HIT_STATISTIC: + { + if (GameID() != GAME_SINGLE) + Game().m_WeaponUsageStatistic->OnBullet_Check_Request(&HDS); + }break; + default: + { + }break; + } + SetHitInfo(Hitter, Weapon, HDS.bone(), HDS.p_in_bone_space, HDS.dir); + Hit (&HDS); + //--------------------------------------------------------------------------- + if (GameID() != GAME_SINGLE) + Game().m_WeaponUsageStatistic->OnBullet_Check_Result(false); + //--------------------------------------------------------------------------- + } + break; + case GE_DESTROY: + { + if(H_Parent()) + { + Msg("GE_DESTROY arrived, but H_Parent() exist. object[%d][%s] parent[%d][%s] [%d]", + ID(), cName().c_str(), + H_Parent()->ID(), H_Parent()->cName().c_str(), + Device.dwFrame); + } + setDestroy (TRUE); + } + break; + } +} + +void VisualCallback(IKinematics *tpKinematics); + +BOOL CGameObject::net_Spawn (CSE_Abstract* DC) +{ + VERIFY (!m_spawned); + m_spawned = true; + m_spawn_time = Device.dwFrame; + CSE_Abstract *E = (CSE_Abstract*)DC; + VERIFY (E); + + const CSE_Visual *visual = smart_cast(E); + if (visual) { + cNameVisual_set (visual_name(E)); + if (visual->flags.test(CSE_Visual::flObstacle)) { + ISpatial *self = smart_cast(this); + self->spatial.type |= STYPE_OBSTACLE; + } + } + + // Naming + cName_set (E->s_name); + cNameSect_set (E->s_name); + if (E->name_replace()[0]) + cName_set (E->name_replace()); + + setID (E->ID); +// if (GameID() != GAME_SINGLE) +// Msg ("CGameObject::net_Spawn -- object %s[%x] setID [%d]", *(E->s_name), this, E->ID); +// R_ASSERT(Level().Objects.net_Find(E->ID) == NULL); + + // XForm + XFORM().setXYZ (E->o_Angle); + Position().set (E->o_Position); +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&stricmp(PH_DBG_ObjectTrack(),*cName())==0) + { + Msg("CGameObject::net_Spawn obj %s Position set from CSE_Abstract %f,%f,%f",PH_DBG_ObjectTrack(),Position().x,Position().y,Position().z); + } +#endif + VERIFY (_valid(renderable.xform)); + VERIFY (!fis_zero(DET(renderable.xform))); + CSE_ALifeObject *O = smart_cast(E); + if (O && xr_strlen(O->m_ini_string)) { +#pragma warning(push) +#pragma warning(disable:4238) + m_ini_file = new CInifile( + &IReader ( + (void*)(*(O->m_ini_string)), + O->m_ini_string.size() + ), + FS.get_path("$game_config$")->m_Path + ); +#pragma warning(pop) + } + + m_story_id = ALife::_STORY_ID(-1); + if (O) + m_story_id = O->m_story_id; + + // Net params + setLocal (E->s_flags.is(M_SPAWN_OBJECT_LOCAL)); + if (Level().IsDemoPlay() && OnClient()) + { + setLocal(FALSE); + }; + + setReady (TRUE); + g_pGameLevel->Objects.net_Register (this); + + m_server_flags.one (); + if (O) { + m_server_flags = O->m_flags; + if (O->m_flags.is(CSE_ALifeObject::flVisibleForAI)) + spatial.type |= STYPE_VISIBLEFORAI; + else + spatial.type = (spatial.type | STYPE_VISIBLEFORAI) ^ STYPE_VISIBLEFORAI; + } + + reload (*cNameSect()); + if(!g_dedicated_server) + CScriptBinder::reload (*cNameSect()); + + reinit (); + if(!g_dedicated_server) + CScriptBinder::reinit (); +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&stricmp(PH_DBG_ObjectTrack(),*cName())==0) + { + Msg("CGameObject::net_Spawn obj %s After Script Binder reinit %f,%f,%f",PH_DBG_ObjectTrack(),Position().x,Position().y,Position().z); + } +#endif + //load custom user data from server + if(!E->client_data.empty()) + { +// Msg ("client data is present for object [%d][%s], load is processed",ID(),*cName()); + IReader ireader = IReader(&*E->client_data.begin(), E->client_data.size()); + net_Load (ireader); + } + else + { +// Msg ("no client data for object [%d][%s], load is skipped",ID(),*cName()); + } + + // if we have a parent + if (0xffff != E->ID_Parent) { + + if (!Parent) { + // // we need this to prevent illegal ref_dec/ref_add + // this is obsolete, since ref_dec/ref_add are removed + // but I propose do not touch this, or touch and then + // test the whole spawn sequence + Parent = this; + inherited::net_Spawn(DC); + Parent = 0; + } + else + inherited::net_Spawn(DC); + } + else { + if (ai().get_level_graph()) { + CSE_ALifeObject *l_tpALifeObject = smart_cast(E); + CSE_Temporary *l_tpTemporary = smart_cast (E); + if (l_tpALifeObject && ai().level_graph().valid_vertex_id(l_tpALifeObject->m_tNodeID)) + ai_location().level_vertex (l_tpALifeObject->m_tNodeID); + else + if (l_tpTemporary && ai().level_graph().valid_vertex_id(l_tpTemporary->m_tNodeID)) + ai_location().level_vertex (l_tpTemporary->m_tNodeID); + + if (l_tpALifeObject && ai().game_graph().valid_vertex_id(l_tpALifeObject->m_tGraphID)) + ai_location().game_vertex (l_tpALifeObject->m_tGraphID); + + validate_ai_locations (false); + + // validating position + if ( + UsedAI_Locations() && + ai().level_graph().inside( + ai_location().level_vertex_id(), + Position() + ) && + can_validate_position_on_spawn() + ) + Position().y = EPS_L + ai().level_graph().vertex_plane_y(*ai_location().level_vertex(),Position().x,Position().z); + + } + inherited::net_Spawn (DC); + } + + m_bObjectRemoved = false; + + spawn_supplies (); +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&stricmp(PH_DBG_ObjectTrack(),*cName())==0) + { + Msg("CGameObject::net_Spawn obj %s Before CScriptBinder::net_Spawn %f,%f,%f",PH_DBG_ObjectTrack(),Position().x,Position().y,Position().z); + } + BOOL ret =CScriptBinder::net_Spawn(DC); +#else + return (CScriptBinder::net_Spawn(DC)); +#endif + +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&stricmp(PH_DBG_ObjectTrack(),*cName())==0) + { + Msg("CGameObject::net_Spawn obj %s Before CScriptBinder::net_Spawn %f,%f,%f",PH_DBG_ObjectTrack(),Position().x,Position().y,Position().z); + } +return ret; +#endif +} + +void CGameObject::net_Save (NET_Packet &net_packet) +{ + u32 position; + net_packet.w_chunk_open16 (position); + save (net_packet); + + // Script Binder Save --------------------------------------- +#ifdef DEBUG + if (psAI_Flags.test(aiSerialize)) { + Msg(">> **** Save script object [%s] *****", *cName()); + Msg(">> Before save :: packet position = [%u]", net_packet.w_tell()); + } + +#endif + + CScriptBinder::save (net_packet); + +#ifdef DEBUG + + if (psAI_Flags.test(aiSerialize)) { + Msg(">> After save :: packet position = [%u]", net_packet.w_tell()); + } +#endif + + // ---------------------------------------------------------- + + net_packet.w_chunk_close16 (position); +} + +void CGameObject::net_Load (IReader &ireader) +{ + load (ireader); + + // Script Binder Load --------------------------------------- +#ifdef DEBUG + if (psAI_Flags.test(aiSerialize)) { + Msg(">> **** Load script object [%s] *****", *cName()); + Msg(">> Before load :: reader position = [%i]", ireader.tell()); + } + +#endif + + CScriptBinder::load (ireader); + + +#ifdef DEBUG + + if (psAI_Flags.test(aiSerialize)) { + Msg(">> After load :: reader position = [%i]", ireader.tell()); + } +#endif + // ---------------------------------------------------------- +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&stricmp(PH_DBG_ObjectTrack(),*cName())==0) + { + Msg("CGameObject::net_Load obj %s (loaded) %f,%f,%f",PH_DBG_ObjectTrack(),Position().x,Position().y,Position().z); + } + +#endif + +} + +void CGameObject::save (NET_Packet &output_packet) +{ +} + +void CGameObject::load (IReader &input_packet) +{ +} + +void CGameObject::spawn_supplies() +{ + if (!spawn_ini() || ai().get_alife()) + return; + + if (!spawn_ini()->section_exist("spawn")) + return; + + LPCSTR N,V; + float p; + bool bScope = false; + bool bSilencer = false; + bool bLauncher = false; + + for (u32 k = 0, j; spawn_ini()->r_line("spawn",k,&N,&V); k++) { + VERIFY (xr_strlen(N)); + j = 1; + p = 1.f; + + float f_cond = 1.0f; + if (V && xr_strlen(V)) { + int n = _GetItemCount(V); + string16 temp; + if (n > 0) + j = atoi(_GetItem(V,0,temp)); //count + + if(NULL!=strstr(V,"prob=")) + p =(float)atof(strstr(V,"prob=")+5); + if (fis_zero(p))p = 1.f; + if (!j) j = 1; + if(NULL!=strstr(V,"cond=")) + f_cond = (float)atof(strstr(V,"cond=")+5); + bScope = (NULL!=strstr(V,"scope")); + bSilencer = (NULL!=strstr(V,"silencer")); + bLauncher = (NULL!=strstr(V,"launcher")); + + } + for (u32 i=0; i(A); + if(pSE_InventoryItem) + pSE_InventoryItem->m_fCondition = f_cond; + + CSE_ALifeItemWeapon* W = smart_cast(A); + if (W) { + if (W->m_scope_status == CSE_ALifeItemWeapon::eAddonAttachable) + W->m_addon_flags.set(CSE_ALifeItemWeapon::eWeaponAddonScope, bScope); + if (W->m_silencer_status == CSE_ALifeItemWeapon::eAddonAttachable) + W->m_addon_flags.set(CSE_ALifeItemWeapon::eWeaponAddonSilencer, bSilencer); + if (W->m_grenade_launcher_status == CSE_ALifeItemWeapon::eAddonAttachable) + W->m_addon_flags.set(CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher, bLauncher); + } + + NET_Packet P; + A->Spawn_Write (P,TRUE); + Level().Send (P,net_flags(TRUE)); + F_entity_Destroy (A); + } + } +} + +void CGameObject::setup_parent_ai_locations(bool assign_position) +{ +// CGameObject *l_tpGameObject = static_cast(H_Root()); + VERIFY (H_Parent()); + CGameObject *l_tpGameObject = static_cast(H_Parent()); + VERIFY (l_tpGameObject); + + // get parent's position + if (assign_position && use_parent_ai_locations()) + Position().set (l_tpGameObject->Position()); + + // setup its ai locations + if (!UsedAI_Locations()) + return; + + if (!ai().get_level_graph()) + return; + + if (l_tpGameObject->UsedAI_Locations() && ai().level_graph().valid_vertex_id(l_tpGameObject->ai_location().level_vertex_id())) + ai_location().level_vertex (l_tpGameObject->ai_location().level_vertex_id()); + else + validate_ai_locations (false); +// VERIFY2 (l_tpGameObject->UsedAI_Locations(),*l_tpGameObject->cNameSect()); +// VERIFY2 (ai().level_graph().valid_vertex_id(l_tpGameObject->ai_location().level_vertex_id()),*cNameSect()); +// ai_location().level_vertex (l_tpGameObject->ai_location().level_vertex_id()); + + if (ai().game_graph().valid_vertex_id(l_tpGameObject->ai_location().game_vertex_id())) + ai_location().game_vertex (l_tpGameObject->ai_location().game_vertex_id()); + else + ai_location().game_vertex (ai().cross_table().vertex(ai_location().level_vertex_id()).game_vertex_id()); +// VERIFY2 (ai().game_graph().valid_vertex_id(l_tpGameObject->ai_location().game_vertex_id()),*cNameSect()); +// ai_location().game_vertex (l_tpGameObject->ai_location().game_vertex_id()); +} + +void CGameObject::validate_ai_locations (bool decrement_reference) +{ + if (!ai().get_level_graph()) + return; + + if (!UsedAI_Locations()) { +// if (ai().get_game_graph() && ai().get_cross_table()) +// set_game_vertex (ai().cross_table().vertex(level_vertex_id()).game_vertex_id()); + return; + } + +// CTimer timer; +// timer.Start (); + Fvector center; + Center (center); + center.x = Position().x; + center.z = Position().z; + u32 l_dwNewLevelVertexID = ai().level_graph().vertex(ai_location().level_vertex_id(),center); + +#ifdef _DEBUG +// Msg ("%6d Searching for node for object %s (%.5f seconds)",Device.dwTimeGlobal,*cName(),timer.GetElapsed_sec()); +#endif + VERIFY (ai().level_graph().valid_vertex_id(l_dwNewLevelVertexID)); + +#if 0 + if (decrement_reference && (ai_location().level_vertex_id() != l_dwNewLevelVertexID)) { + Fvector new_position = ai().level_graph().vertex_position(l_dwNewLevelVertexID); + if (Position().y - new_position.y >= 1.5f) { + u32 new_vertex_id = ai().level_graph().vertex(ai_location().level_vertex_id(),center); + new_vertex_id = new_vertex_id; + } + } +#endif + + if (decrement_reference && (ai_location().level_vertex_id() == l_dwNewLevelVertexID)) + return; + + ai_location().level_vertex (l_dwNewLevelVertexID); + + if (ai().get_game_graph() && ai().get_cross_table()) { + ai_location().game_vertex (ai().cross_table().vertex(ai_location().level_vertex_id()).game_vertex_id()); + VERIFY (ai().game_graph().valid_vertex_id(ai_location().game_vertex_id())); + } +} + +void CGameObject::spatial_move () +{ + if (H_Parent()) + setup_parent_ai_locations (); + else + if (Visual()) + validate_ai_locations (); + + inherited::spatial_move (); +} + +#ifdef DEBUG +void CGameObject::dbg_DrawSkeleton () +{ + CCF_Skeleton* Skeleton = smart_cast(collidable.model); + if (!Skeleton) return; + Skeleton->_dbg_refresh(); + + const CCF_Skeleton::ElementVec& Elements = Skeleton->_GetElements(); + for (CCF_Skeleton::ElementVec::const_iterator I=Elements.begin(); I!=Elements.end(); I++){ + if (!I->valid()) continue; + switch (I->type){ + case SBoneShape::stBox:{ + Fmatrix M; + M.invert (I->b_IM); + Fvector h_size = I->b_hsize; + Level().debug_renderer().draw_obb (M, h_size, color_rgba(0, 255, 0, 255)); + }break; + case SBoneShape::stCylinder:{ + Fmatrix M; + M.c.set (I->c_cylinder.m_center); + M.k.set (I->c_cylinder.m_direction); + Fvector h_size; + h_size.set (I->c_cylinder.m_radius,I->c_cylinder.m_radius,I->c_cylinder.m_height*0.5f); + Fvector::generate_orthonormal_basis(M.k,M.j,M.i); + Level().debug_renderer().draw_obb (M, h_size, color_rgba(0, 127, 255, 255)); + }break; + case SBoneShape::stSphere:{ + Fmatrix l_ball; + l_ball.scale (I->s_sphere.R, I->s_sphere.R, I->s_sphere.R); + l_ball.translate_add(I->s_sphere.P); + Level().debug_renderer().draw_ellipse(l_ball, color_rgba(0, 255, 0, 255)); + }break; + }; + }; +} +#endif + +void CGameObject::renderable_Render () +{ + inherited::renderable_Render(); + ::Render->set_Transform (&XFORM()); + ::Render->add_Visual (Visual()); + Visual()->getVisData().hom_frame = Device.dwFrame; +} + +/* +float CGameObject::renderable_Ambient () +{ + return (ai().get_level_graph() && ai().level_graph().valid_vertex_id(level_vertex_id()) ? float(level_vertex()->light()/15.f) : 1.f); +} +*/ + +CObject::SavedPosition CGameObject::ps_Element(u32 ID) const +{ + VERIFY(ID(H_Parent()); + VERIFY (parent); + if (ai().get_level_graph() && ai().level_graph().valid_vertex_id(parent->ai_location().level_vertex_id())) + validate_ai_locations (false); +} + + +#ifdef DEBUG + +void CGameObject::OnRender() +{ + if (bDebug && Visual()) + { + Fvector bc,bd; + Visual()->getVisData().box.get_CD (bc,bd); + Fmatrix M = XFORM(); M.c.add (bc); + Level().debug_renderer().draw_obb (M,bd,color_rgba(0,0,255,255)); + } +} +#endif + +BOOL CGameObject::UsedAI_Locations() +{ + return (m_server_flags.test(CSE_ALifeObject::flUsedAI_Locations)); +} + +BOOL CGameObject::TestServerFlag(u32 Flag) const +{ + return (m_server_flags.test(Flag)); +} + +void CGameObject::add_visual_callback (visual_callback *callback) +{ + VERIFY (smart_cast(Visual())); + CALLBACK_VECTOR_IT I = std::find(visual_callbacks().begin(),visual_callbacks().end(),callback); + VERIFY (I == visual_callbacks().end()); + + if (m_visual_callback.empty()) SetKinematicsCallback(true); +// smart_cast(Visual())->Callback(VisualCallback,this); + m_visual_callback.push_back (callback); +} + +void CGameObject::remove_visual_callback (visual_callback *callback) +{ + CALLBACK_VECTOR_IT I = std::find(m_visual_callback.begin(),m_visual_callback.end(),callback); + VERIFY (I != m_visual_callback.end()); + m_visual_callback.erase (I); + if (m_visual_callback.empty()) SetKinematicsCallback(false); +// smart_cast(Visual())->Callback(0,0); +} + +void CGameObject::SetKinematicsCallback (bool set) +{ + if(!Visual()) return; + if (set) + smart_cast(Visual())->Callback(VisualCallback,this); + else + smart_cast(Visual())->Callback(0,0); +}; + +void VisualCallback (IKinematics *tpKinematics) +{ + CGameObject *game_object = static_cast(static_cast(tpKinematics->GetUpdateCallbackParam())); + VERIFY (game_object); + + CGameObject::CALLBACK_VECTOR_IT I = game_object->visual_callbacks().begin(); + CGameObject::CALLBACK_VECTOR_IT E = game_object->visual_callbacks().end(); + for ( ; I != E; ++I) + (*I) (tpKinematics); +} + +CScriptGameObject *CGameObject::lua_game_object () const +{ + if (!m_spawned) + Msg ("! You are trying to use a destroyed object (or binder object) [%d][%s][%x]",ID(),*cName(),this); + + THROW (m_spawned); + if (!m_lua_game_object) + m_lua_game_object = new CScriptGameObject(const_cast(this)); + return (m_lua_game_object); +} + +bool CGameObject::NeedToDestroyObject() const +{ + return false; +} + +void CGameObject::DestroyObject() +{ + + if(m_bObjectRemoved) return; + m_bObjectRemoved = true; + if (getDestroy()) return; + + if (Local()) + { + NET_Packet P; + u_EventGen (P,GE_DESTROY,ID()); + u_EventSend (P); + } +} + +void CGameObject::shedule_Update (u32 dt) +{ + //уничтожить + if(!IsGameTypeSingle() && OnServer() && NeedToDestroyObject()) + { +#ifdef DEBUG + Msg("--NeedToDestroyObject for [%d][%d]", ID(), Device.dwFrame); +#endif + DestroyObject (); + + } + + // Msg ("-SUB-:[%x][%s] CGameObject::shedule_Update",smart_cast(this),*cName()); + inherited::shedule_Update (dt); + + if(!g_dedicated_server) + CScriptBinder::shedule_Update(dt); +} + +BOOL CGameObject::net_SaveRelevant () +{ + return (CScriptBinder::net_SaveRelevant()); +} + +//игровое имя объекта +LPCSTR CGameObject::Name () const +{ + return (*cName()); +} + +u32 CGameObject::ef_creature_type () const +{ + string16 temp; CLSID2TEXT(CLS_ID,temp); + R_ASSERT3 (false,"Invalid creature type request, virtual function is not properly overridden!",temp); + return (u32(-1)); +} + +u32 CGameObject::ef_equipment_type () const +{ + string16 temp; CLSID2TEXT(CLS_ID,temp); + R_ASSERT3 (false,"Invalid equipment type request, virtual function is not properly overridden!",temp); + return (u32(-1)); +// return (6); +} + +u32 CGameObject::ef_main_weapon_type () const +{ + string16 temp; CLSID2TEXT(CLS_ID,temp); + R_ASSERT3 (false,"Invalid main weapon type request, virtual function is not properly overridden!",temp); + return (u32(-1)); +// return (5); +} + +u32 CGameObject::ef_anomaly_type () const +{ + string16 temp; CLSID2TEXT(CLS_ID,temp); + R_ASSERT3 (false,"Invalid anomaly type request, virtual function is not properly overridden!",temp); + return (u32(-1)); +} + +u32 CGameObject::ef_weapon_type () const +{ + string16 temp; CLSID2TEXT(CLS_ID,temp); + R_ASSERT3 (false,"Invalid weapon type request, virtual function is not properly overridden!",temp); + return (u32(-1)); +// return (u32(0)); +} + +u32 CGameObject::ef_detector_type () const +{ + string16 temp; CLSID2TEXT(CLS_ID,temp); + R_ASSERT3 (false,"Invalid detector type request, virtual function is not properly overridden!",temp); + return (u32(-1)); +} + +void CGameObject::net_Relcase (CObject* O) +{ + inherited::net_Relcase (O); + if(!g_dedicated_server) + CScriptBinder::net_Relcase (O); +} + +CGameObject::CScriptCallbackExVoid &CGameObject::callback(GameObject::ECallbackType type) const +{ + return ((*m_callbacks)[type]); +} + +LPCSTR CGameObject::visual_name (CSE_Abstract *server_entity) +{ + const CSE_Visual *visual = smart_cast(server_entity); + VERIFY (visual); + return (visual->get_visual()); +} + +void CGameObject::update_animation_movement_controller () +{ + if (!animation_movement_controlled()) + return; + + m_anim_mov_ctrl->OnFrame (); + + if (m_anim_mov_ctrl->isActive()) + return; + + destroy_anim_mov_ctrl (); +} + +void CGameObject:: UpdateCL ( ) +{ + inherited::UpdateCL (); +} + +void CGameObject::OnChangeVisual ( ) +{ + inherited::OnChangeVisual( ); + if( animation_movement_controlled( ) ) + { + destroy_anim_mov_ctrl( ); + } +} +bool CGameObject::shedule_Needed( ) +{ + return ( !getDestroy( ) ); +// return (processing_enabled() || CScriptBinder::object()); +}; + +void CGameObject:: create_anim_mov_ctrl( CBlend* b ) +{ + //if(m_anim_mov_ctrl) + //destroy_anim_mov_ctrl( ); + VERIFY( !animation_movement() ); + VERIFY(Visual()); + IKinematics *K = Visual( )->dcast_PKinematics( ); + VERIFY( K ); + m_anim_mov_ctrl = new animation_movement_controller( &XFORM(), K, b ); +} +void CGameObject:: destroy_anim_mov_ctrl() +{ + xr_delete( m_anim_mov_ctrl ); +} \ No newline at end of file diff --git a/src/xrGameLA/GameObject.h b/src/xrGameLA/GameObject.h new file mode 100644 index 000000000..5ac8fce2d --- /dev/null +++ b/src/xrGameLA/GameObject.h @@ -0,0 +1,293 @@ +// GameObject.h: interface for the CGameObject class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_GAMEOBJECT_H__3DA72D03_C759_4688_AEBB_89FA812AA873__INCLUDED_) +#define AFX_GAMEOBJECT_H__3DA72D03_C759_4688_AEBB_89FA812AA873__INCLUDED_ +#pragma once + +#include "../xr_object.h" +#include "xrServer_Space.h" +#include "alife_space.h" +#include "UsableScriptObject.h" +#include "script_binder.h" +#include "Hit.h" +#include "game_object_space.h" + +class CPhysicsShell; +class CSE_Abstract; +class CPHSynchronize; +class CScriptGameObject; +class CInventoryItem; +class CEntity; +class CEntityAlive; +class CInventoryOwner; +class CActor; +class CPhysicsShellHolder; +class CParticlesPlayer; +class CCustomZone; +class IInputReceiver; +class CArtefact; +class CCustomMonster; +class CAI_Stalker; +class CScriptEntity; +class CAI_ObjectLocation; +class CWeapon; +class CMountedTurret; +class CExplosive; +class CHolderCustom; +class CAttachmentOwner; +class CBaseMonster; +class CSpaceRestrictor; +class CAttachableItem; +class animation_movement_controller; +class CBlend; +class IKinematics; +namespace GameObject { + enum ECallbackType; +}; + +template +class CScriptCallbackEx; + +class CGameObject : + public CObject, + public CUsableScriptObject, + public CScriptBinder +{ + typedef CObject inherited; + bool m_spawned; + Flags32 m_server_flags; + CAI_ObjectLocation *m_ai_location; + ALife::_STORY_ID m_story_id; + animation_movement_controller *m_anim_mov_ctrl; +protected: + //время удаления объекта + bool m_bObjectRemoved; +public: + CGameObject(); + virtual ~CGameObject(); +public: + //functions used for avoiding most of the smart_cast + virtual CAttachmentOwner* cast_attachment_owner () {return NULL;} + virtual CInventoryOwner* cast_inventory_owner () {return NULL;} + virtual CInventoryItem* cast_inventory_item () {return NULL;} + virtual CEntity* cast_entity () {return NULL;} + virtual CEntityAlive* cast_entity_alive () {return NULL;} + virtual CActor* cast_actor () {return NULL;} + virtual CGameObject* cast_game_object () {return this;} + virtual CCustomZone* cast_custom_zone () {return NULL;} + virtual CPhysicsShellHolder* cast_physics_shell_holder () {return NULL;} + virtual IInputReceiver* cast_input_receiver () {return NULL;} + virtual CParticlesPlayer* cast_particles_player () {return NULL;} + virtual CArtefact* cast_artefact () {return NULL;} + virtual CCustomMonster* cast_custom_monster () {return NULL;} + virtual CAI_Stalker* cast_stalker () {return NULL;} + virtual CScriptEntity* cast_script_entity () {return NULL;} + virtual CWeapon* cast_weapon () {return NULL;} + virtual CExplosive* cast_explosive () {return NULL;} + virtual CSpaceRestrictor* cast_restrictor () {return NULL;} + virtual CAttachableItem* cast_attachable_item () {return NULL;} + virtual CHolderCustom* cast_holder_custom () {return NULL;} + virtual CBaseMonster* cast_base_monster () {return NULL;} + virtual CMountedTurret* cast_mounted_turret () {return NULL;} + +public: + virtual BOOL feel_touch_on_contact (CObject *) {return TRUE;} + virtual bool use (CGameObject* who_use) {return CUsableScriptObject::use(who_use);}; + +public: + CInifile *m_ini_file; + + // Utilities + static void u_EventGen (NET_Packet& P, u32 type, u32 dest ); + static void u_EventSend (NET_Packet& P, u32 dwFlags = DPNSEND_GUARANTEED ); + + // Methods + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Relcase ( CObject* O ); + virtual void UpdateCL ( ); + virtual void OnChangeVisual ( ); + //object serialization + virtual void net_Save (NET_Packet &net_packet); + virtual void net_Load (IReader &ireader); + virtual BOOL net_SaveRelevant (); + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + + virtual BOOL net_Relevant () { return getLocal(); } // send messages only if active and local + virtual void spatial_move (); + virtual BOOL Ready () { return getReady(); } // update only if active and fully initialized by/for network +// virtual float renderable_Ambient (); + + virtual void shedule_Update (u32 dt); + virtual bool shedule_Needed (); + + virtual void renderable_Render (); + virtual void OnEvent (NET_Packet& P, u16 type); + virtual void Hit (SHit* pHDS) {}; + virtual void SetHitInfo (CObject* who, CObject* weapon, s16 element, Fvector Pos, Fvector Dir) {}; + virtual BOOL BonePassBullet (int boneID) { return FALSE; } + + + //игровое имя объекта + virtual LPCSTR Name () const; + + //virtual void OnH_A_Independent (); + virtual void OnH_B_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + + virtual bool IsVisibleForZones () { return true; } +/////////////////////////////////////////////////////////////////////// + virtual bool NeedToDestroyObject () const; + virtual void DestroyObject (); +/////////////////////////////////////////////////////////////////////// + + // Position stack + virtual SavedPosition ps_Element (u32 ID) const; + + void setup_parent_ai_locations(bool assign_position = true); + void validate_ai_locations(bool decrement_reference = true); + + //animation_movement_controller + virtual void create_anim_mov_ctrl ( CBlend* b ); + virtual void destroy_anim_mov_ctrl ( ); + void update_animation_movement_controller(); + IC bool animation_movement_controlled ( ) const { return !!animation_movement(); } +const animation_movement_controller*animation_movement ( ) const { return m_anim_mov_ctrl; } + // Game-specific events + + virtual BOOL UsedAI_Locations (); + BOOL TestServerFlag (u32 Flag) const; + virtual bool can_validate_position_on_spawn (){return true;} +#ifdef DEBUG + virtual void OnRender (); +#endif + + void init (); + virtual void reinit (); + virtual void reload (LPCSTR section); + ///////////////////// network ///////////////////////////////////////// +private: + bool m_bCrPr_Activated; + u32 m_dwCrPr_ActivationStep; + +public: + virtual void make_Interpolation () {}; // interpolation from last visible to corrected position/rotation + virtual void PH_B_CrPr () {}; // actions & operations before physic correction-prediction steps + virtual void PH_I_CrPr () {}; // actions & operations after correction before prediction steps +#ifdef DEBUG + virtual void PH_Ch_CrPr () {}; // + virtual void dbg_DrawSkeleton (); +#endif + virtual void PH_A_CrPr () {}; // actions & operations after phisic correction-prediction steps + virtual void CrPr_SetActivationStep (u32 Step) {m_dwCrPr_ActivationStep = Step; }; + virtual u32 CrPr_GetActivationStep () { return m_dwCrPr_ActivationStep; }; + virtual void CrPr_SetActivated (bool Activate) { m_bCrPr_Activated = Activate; }; + virtual bool CrPr_IsActivated () { return m_bCrPr_Activated; }; + /////////////////////////////////////////////////////////////////////// + virtual const SRotation Orientation () const + { + SRotation rotation; + float h,p,b; + XFORM().getHPB (h,p,b); + rotation.yaw = h; + rotation.pitch = p; + return (rotation); + }; + + virtual bool use_parent_ai_locations () const + { + return (true); + } + +public: + typedef void __stdcall visual_callback(IKinematics *); + typedef svector CALLBACK_VECTOR; + typedef CALLBACK_VECTOR::iterator CALLBACK_VECTOR_IT; + + CALLBACK_VECTOR m_visual_callback; + +public: + void add_visual_callback (visual_callback *callback); + void remove_visual_callback (visual_callback *callback); + void SetKinematicsCallback (bool set); + + IC CALLBACK_VECTOR &visual_callbacks () + { + return (m_visual_callback); + } + + +private: + mutable CScriptGameObject *m_lua_game_object; + int m_script_clsid; +public: + CScriptGameObject *lua_game_object() const; + int clsid () const + { + THROW (m_script_clsid >= 0); + return (m_script_clsid); + } +public: + IC CInifile *spawn_ini () + { + return (m_ini_file); + } +protected: + virtual void spawn_supplies (); + +public: + IC CAI_ObjectLocation &ai_location () const + { + VERIFY (m_ai_location); + return (*m_ai_location); + } + +private: + u32 m_spawn_time; + +public: + IC u32 spawn_time () const + { + VERIFY (m_spawned); + return (m_spawn_time); + } + + IC const ALife::_STORY_ID &story_id () const + { + return (m_story_id); + } + +public: + virtual u32 ef_creature_type () const; + virtual u32 ef_equipment_type () const; + virtual u32 ef_main_weapon_type () const; + virtual u32 ef_anomaly_type () const; + virtual u32 ef_weapon_type () const; + virtual u32 ef_detector_type () const; + virtual bool natural_weapon () const {return true;} + virtual bool natural_detector () const {return true;} + virtual bool use_center_to_aim () const {return false;} + // [12.11.07] Alexander Maniluk: added this method for moving object + virtual void MoveTo(Fvector const & position) {}; + +public: + + typedef CScriptCallbackEx CScriptCallbackExVoid; + +private: + + DEFINE_MAP (GameObject::ECallbackType, CScriptCallbackExVoid, CALLBACK_MAP, CALLBACK_MAP_IT); + CALLBACK_MAP *m_callbacks; + +public: + CScriptCallbackExVoid &callback (GameObject::ECallbackType type) const; + virtual LPCSTR visual_name (CSE_Abstract *server_entity); + + virtual void On_B_NotCurrentEntity () {}; +}; + +#endif // !defined(AFX_GAMEOBJECT_H__3DA72D03_C759_4688_AEBB_89FA812AA873__INCLUDED_) diff --git a/src/xrGameLA/GamePersistent.cpp b/src/xrGameLA/GamePersistent.cpp new file mode 100644 index 000000000..83b9069c6 --- /dev/null +++ b/src/xrGameLA/GamePersistent.cpp @@ -0,0 +1,699 @@ +#include "pch_script.h" +#include "gamepersistent.h" +#include "../fmesh.h" +#include "../xr_ioconsole.h" +#include "../gamemtllib.h" +#include "../../Include/xrRender/Kinematics.h" +#include "profiler.h" +#include "MainMenu.h" +#include "UICursor.h" +#include "game_base_space.h" +#include "level.h" +#include "ParticlesObject.h" +#include "actor.h" +#include "game_base_space.h" +#include "player_hud.h" +#include "stalker_animation_data_storage.h" +#include "stalker_velocity_holder.h" +#include "alife_simulator.h" +#include "CustomTimersManager.h" +#include "HUDManager.h" +#include "ui/UIPdaWnd.h" + +#include "ActorEffector.h" +#include "actor.h" +#include "script_engine.h" +#include "ui/UITextureMaster.h" + +#ifndef MASTER_GOLD +# include "custommonster.h" +#endif // MASTER_GOLD + +#ifndef _EDITOR +# include "ai_debug.h" +#endif // _EDITOR + +#include "../x_ray.h" + +#ifdef DEBUG_MEMORY_MANAGER + static void * ode_alloc (size_t size) { return Memory.mem_alloc(size,"ODE"); } + static void * ode_realloc (void *ptr, size_t oldsize, size_t newsize) { return Memory.mem_realloc(ptr,newsize,"ODE"); } + static void ode_free (void *ptr, size_t size) { return xr_free(ptr); } +#else // DEBUG_MEMORY_MANAGER + static void * ode_alloc (size_t size) { return xr_malloc(size); } + static void * ode_realloc (void *ptr, size_t oldsize, size_t newsize) { return xr_realloc(ptr,newsize); } + static void ode_free (void *ptr, size_t size) { return xr_free(ptr); } +#endif // DEBUG_MEMORY_MANAGER + +static float diff_far = 70.0f; +static float diff_near = -70.0f; + +bool m_bCamReady = false; + +CGamePersistent::CGamePersistent(void) +{ + m_bPickableDOF = false; + m_game_params.m_e_game_type = GAME_ANY; + ambient_sound_next_time = 0; + ambient_effect_next_time = 0; + ambient_effect_stop_time = 0; + ambient_particles = 0; + + m_pUI_core = NULL; + m_pMainMenu = NULL; + m_intro = NULL; + m_intro_event.bind (this,&CGamePersistent::start_logo_intro); +#ifdef DEBUG + m_frame_counter = 0; + m_last_stats_frame = u32(-2); +#endif + // + dSetAllocHandler (ode_alloc ); + dSetReallocHandler (ode_realloc ); + dSetFreeHandler (ode_free ); + + // + BOOL bDemoMode = (0!=strstr(Core.Params,"-demomode ")); + if (bDemoMode) + { + string256 fname; + LPCSTR name = strstr(Core.Params,"-demomode ") + 10; + sscanf (name,"%s",fname); + R_ASSERT2 (fname[0],"Missing filename for 'demomode'"); + Msg ("- playing in demo mode '%s'",fname); + pDemoFile = FS.r_open (fname); + Device.seqFrame.Add (this); + eDemoStart = Engine.Event.Handler_Attach("GAME:demo",this); + uTime2Change = 0; + } else { + pDemoFile = NULL; + eDemoStart = NULL; + } + + eQuickLoad = Engine.Event.Handler_Attach("Game:QuickLoad",this); + + Fvector3* DofValue = Console->GetFVectorPtr("r2_dof"); + SetBaseDof (*DofValue); +} + +CGamePersistent::~CGamePersistent(void) +{ + FS.r_close (pDemoFile); + Device.seqFrame.Remove (this); + Engine.Event.Handler_Detach (eDemoStart,this); + Engine.Event.Handler_Detach (eQuickLoad,this); +} + +void CGamePersistent::RegisterModel(IRenderVisual* V) +{ + // Check types + switch (V->getType()){ + case MT_SKELETON_ANIM: + case MT_SKELETON_RIGID:{ + u16 def_idx = GMLib.GetMaterialIdx("default_object"); + R_ASSERT2 (GMLib.GetMaterialByIdx(def_idx)->Flags.is(SGameMtl::flDynamic),"'default_object' - must be dynamic"); + IKinematics* K = smart_cast(V); VERIFY(K); + int cnt = K->LL_BoneCount(); + for (u16 k=0; kLL_GetData(k); + if (*(bd.game_mtl_name)){ + bd.game_mtl_idx = GMLib.GetMaterialIdx(*bd.game_mtl_name); + R_ASSERT2(GMLib.GetMaterialByIdx(bd.game_mtl_idx)->Flags.is(SGameMtl::flDynamic),"Required dynamic game material"); + }else{ + bd.game_mtl_idx = def_idx; + } + } + }break; + } +} + +extern void clean_game_globals (); +extern void init_game_globals (); + +void CGamePersistent::OnAppStart() +{ + // load game materials + GMLib.Load (); + init_game_globals (); + __super::OnAppStart (); + m_pUI_core = new ui_core(); + m_pMainMenu = new CMainMenu(); +} + + +void CGamePersistent::OnAppEnd () +{ + if(m_pMainMenu->IsActive()) + m_pMainMenu->Activate(false); + + xr_delete (m_pMainMenu); + xr_delete (m_pUI_core); + + __super::OnAppEnd (); + + clean_game_globals (); + + GMLib.Unload (); + +} + +void CGamePersistent::Start (LPCSTR op) +{ + __super::Start (op); +} + +void CGamePersistent::Disconnect() +{ + // destroy ambient particles + CParticlesObject::Destroy(ambient_particles); + + __super::Disconnect (); + // stop all played emitters + ::Sound->stop_emitters (); + m_game_params.m_e_game_type = GAME_ANY; +} + +#include "xr_level_controller.h" + +void CGamePersistent::OnGameStart() +{ + __super::OnGameStart (); + + UpdateGameType (); + + diff_far = pSettings->r_float("zone_pick_dof", "far"); + diff_near = pSettings->r_float("zone_pick_dof", "near"); + +} + +void CGamePersistent::UpdateGameType () +{ + __super::UpdateGameType (); + // [7/11/2005] + if (!xr_strcmp(m_game_params.m_game_type, "single")) m_game_params.m_e_game_type = GAME_SINGLE; + else m_game_params.m_e_game_type = GAME_ANY; + // [7/11/2005] + + g_current_keygroup = _sp; +} + +void CGamePersistent::OnGameEnd () +{ + __super::OnGameEnd (); + + xr_delete (g_stalker_animation_data_storage); + xr_delete (g_stalker_velocity_holder); +} + +void CGamePersistent::WeathersUpdate() +{ + if (g_pGameLevel && !g_dedicated_server) + { + CActor* actor = smart_cast(Level().CurrentViewEntity()); + BOOL bIndoor = TRUE; + if (actor) bIndoor = actor->renderable_ROS()->get_luminocity_hemi()<0.05f; + + int data_set = (Random.randF()<(1.f-Environment().CurrentEnv->weight))?0:1; + + CEnvDescriptor* const _env = Environment().Current[data_set]; + VERIFY (_env); + + CEnvAmbient* env_amb = _env->env_ambient; + + // start sound + if (env_amb) + { + if (Device.dwTimeGlobal > ambient_sound_next_time) + { + ref_sound* snd = env_amb->get_rnd_sound(); + ambient_sound_next_time = Device.dwTimeGlobal + env_amb->get_rnd_sound_time(); + if (snd) + { + Fvector pos; + float angle = ::Random.randF(PI_MUL_2); + pos.x = _cos(angle); + pos.y = 0; + pos.z = _sin(angle); + pos.normalize ().mul(env_amb->get_rnd_sound_dist()).add(Device.vCameraPosition); + pos.y += 10.f; + snd->play_at_pos (0,pos); + } + } + + // start effect + if ((FALSE==bIndoor) && (0==ambient_particles) && Device.dwTimeGlobal>ambient_effect_next_time){ + CEnvAmbient::SEffect* eff = env_amb->get_rnd_effect(); + if (eff){ + Environment().wind_gust_factor = eff->wind_gust_factor; + ambient_effect_next_time = Device.dwTimeGlobal + env_amb->get_rnd_effect_time(); + ambient_effect_stop_time = Device.dwTimeGlobal + eff->life_time; + ambient_particles = CParticlesObject::Create(eff->particles.c_str(),FALSE,false); + Fvector pos; pos.add (Device.vCameraPosition,eff->offset); + ambient_particles->play_at_pos (pos); + if (eff->sound._handle()) eff->sound.play_at_pos(0,pos); + } + } + } + // stop if time exceed or indoor + if (bIndoor || Device.dwTimeGlobal>=ambient_effect_stop_time){ + if (ambient_particles) ambient_particles->Stop(); + Environment().wind_gust_factor = 0.f; + } + // if particles not playing - destroy + if (ambient_particles&&!ambient_particles->IsPlaying()) + CParticlesObject::Destroy(ambient_particles); + } +} + +bool allow_intro () +{ + return 0 == strstr(Core.Params,"-nointro"); +} + +#include "UI/UIGameTutorial.h" + +extern int ps_intro; + +void CGamePersistent::start_logo_intro () +{ + if (!ps_intro){ + m_intro_event = 0; + if (!xr_strlen(m_game_params.m_new_or_load)) + { + Console->Show (); + Console->Execute ("main_menu on"); + } + return; + } + + if (Device.dwPrecacheFrame==0) + { + m_intro_event.bind (this,&CGamePersistent::update_logo_intro); + if (!g_dedicated_server && 0==xr_strlen(m_game_params.m_game_or_spawn) && NULL==g_pGameLevel) + { + if (NULL!=m_intro) return; + m_intro = new CUISequencer(); + m_intro->Start ("intro_logo"); + Console->Hide (); + } + } +} +void CGamePersistent::update_logo_intro () +{ + if(m_intro && (false==m_intro->IsActive())){ + m_intro_event = 0; + xr_delete (m_intro); + Console->Execute ("main_menu on"); + } + else if (!m_intro) + { + m_intro_event = 0; + } +} + +void CGamePersistent::game_loaded() +{ + if(Device.dwPrecacheFrame<=2) + { + if( g_pGameLevel && + g_pGameLevel->bReady && + load_screen_renderer.b_need_user_input && + m_game_params.m_e_game_type == GAME_SINGLE) + { + pApp->ClearTitle(); + + if (NULL!=m_intro) return; + + m_intro = new CUISequencer(); + m_intro->Start ("game_loaded"); + m_intro->m_on_destroy_event.bind(this, &CGamePersistent::update_game_loaded); + } + m_intro_event = 0; + } +} + +void CGamePersistent::update_game_loaded() +{ + xr_delete (m_intro); + start_game_intro (); +#ifdef USE_TIMERS_MANAGER + ai().alife().timers().GameLoaded(true); +#endif +} +#include "ai_space.h" +#include "alife_spawn_registry.h" +void CGamePersistent::start_game_intro () +{ + if (g_pGameLevel && g_pGameLevel->bReady && Device.dwPrecacheFrame<=2){ + m_intro_event.bind (this,&CGamePersistent::update_game_intro); + + LPCSTR spawn_name = ai().alife().spawns().GetSpawnName(); + bool load_spawn = (0==stricmp(m_game_params.m_new_or_load,"load") && 0==xr_strcmp(m_game_params.m_game_or_spawn, spawn_name)); //skyloader: flag if load save and (save == spawn_name), for example, all.sav + if (0==stricmp(m_game_params.m_new_or_load,"new") || load_spawn) + { + if (NULL!=m_intro) return; + m_intro = new CUISequencer(); + m_intro->Start ("intro_game"); + + if (ps_intro && !load_spawn) + Msg("intro_start intro_game"); + else + m_intro->Stop(); //<= skyloader: call functions from sequencer (call the first scene in sid bunker) + } + + } +} +#include "script_engine.h" +void synchronization_callback() +{ + string256 fn; + luabind::functor callback; + xr_strcpy (fn, pSettings->r_string("lost_alpha_cfg", "on_synchronization_done")); + R_ASSERT (ai().script_engine().functor(fn, callback)); + callback (); +} + +void CGamePersistent::update_game_intro () +{ + if(m_intro && (false==m_intro->IsActive())){ + xr_delete (m_intro); + m_intro_event = 0; + synchronization_callback(); + } + else if(!m_intro) + { + m_intro_event = 0; + synchronization_callback(); + } +} +#include "holder_custom.h" +extern CUISequencer * g_tutorial; +extern CUISequencer * g_tutorial2; + +void CGamePersistent::OnFrame () +{ + if(Device.dwPrecacheFrame==5 && m_intro_event.empty()) + { + m_intro_event.bind (this,&CGamePersistent::game_loaded); + } + + if(g_tutorial2) + { + g_tutorial2->Destroy (); + xr_delete (g_tutorial2); + } + + if(g_tutorial && !g_tutorial->IsActive()) + { + xr_delete(g_tutorial); + } + if(0==Device.dwFrame%200) + CUITextureMaster::FreeCachedShaders(); + +#ifdef DEBUG + ++m_frame_counter; +#endif + if (!g_dedicated_server && !m_intro_event.empty()) + m_intro_event(); + if(!g_dedicated_server && Device.dwPrecacheFrame==0 && !m_intro && m_intro_event.empty()) + load_screen_renderer.stop(); + + if( !m_pMainMenu->IsActive() ) + m_pMainMenu->DestroyInternal(false); + + if(!g_pGameLevel) return; + if(!g_pGameLevel->bReady) return; + + if(Device.Paused()){ +#ifndef MASTER_GOLD + if (Level().CurrentViewEntity()) { + if (!g_actor || (g_actor->ID() != Level().CurrentViewEntity()->ID())) { + CCustomMonster *custom_monster = smart_cast(Level().CurrentViewEntity()); + if (custom_monster) // can be spectator in multiplayer + custom_monster->UpdateCamera(); + } + else + { + CCameraBase* C = NULL; + if (g_actor) + { + if(!Actor()->Holder()) + C = Actor()->cam_Active(); + else + C = Actor()->Holder()->Camera(); + + Actor()->Cameras().UpdateFromCamera(C); + Actor()->Cameras().ApplyDevice(VIEWPORT_NEAR); + } + } + } +#else // MASTER_GOLD + if (g_actor) + { + CCameraBase* C = NULL; + if(!Actor()->Holder()) + C = Actor()->cam_Active(); + else + C = Actor()->Holder()->Camera(); + + Actor()->Cameras().UpdateFromCamera (C); + Actor()->Cameras().ApplyDevice (VIEWPORT_NEAR); + } +#endif // MASTER_GOLD + } + __super::OnFrame (); + + if(!Device.Paused()) + Engine.Sheduler.Update (); + + // update weathers ambient + if(!Device.Paused()) + WeathersUpdate (); + + if (m_bCamReady == false) { + m_bCamReady = true; + } + + if (0!=pDemoFile) + { + if (Device.dwTimeGlobal>uTime2Change){ + // Change level + play demo + if (pDemoFile->elapsed()<3) pDemoFile->seek(0); // cycle + + // Read params + string512 params; + pDemoFile->r_string (params,sizeof(params)); + string256 o_server, o_client, o_demo; u32 o_time; + sscanf (params,"%[^,],%[^,],%[^,],%d",o_server,o_client,o_demo,&o_time); + + // Start _new level + demo + Engine.Event.Defer ("KERNEL:disconnect"); + Engine.Event.Defer ("KERNEL:start",size_t(xr_strdup(_Trim(o_server))),size_t(xr_strdup(_Trim(o_client)))); + Engine.Event.Defer ("GAME:demo", size_t(xr_strdup(_Trim(o_demo))), u64(o_time)); + uTime2Change = 0xffffffff; // Block changer until Event received + } + } + +#ifdef DEBUG + if ((m_last_stats_frame + 1) < m_frame_counter) + profiler().clear (); +#endif + + UpdateDof(); +} + +#include "game_sv_single.h" +#include "xrServer.h" +#include "UIGameCustom.h" +#include "ui/UIMainIngameWnd.h" +#include "ui/UIPdaWnd.h" + +void CGamePersistent::OnEvent(EVENT E, u64 P1, u64 P2) +{ + if(E==eQuickLoad) + { + loading_save_timer.Start(); + pApp->LoadBegin(); + if (Device.Paused()) + Device.Pause (FALSE, TRUE, TRUE, "eQuickLoad"); + + CUIGameCustom* ui_game_custom = NULL; + if ((ui_game_custom = CurrentGameUI()) != NULL) + { + ui_game_custom->HideShownDialogs(); + ui_game_custom->UIMainIngameWnd->reset_ui(); + + //CurrentGameUI()->PdaMenu().Reset(); + // resetting pda is not enough... + + xr_delete(ui_game_custom->m_PdaMenu); + ui_game_custom->m_PdaMenu = new CUIPdaWnd(); + } + + if(g_tutorial) + g_tutorial->Stop(); + + if(g_tutorial2) + g_tutorial2->Stop(); + + + LPSTR saved_name = (LPSTR)(P1); + + Level().remove_objects (); + game_sv_Single *game = smart_cast(Level().Server->game); + R_ASSERT (game); + game->restart_simulator (saved_name); + xr_free (saved_name); + pApp->LoadEnd(); + return; + }else + if(E==eDemoStart) + { + string256 cmd; + LPCSTR demo = LPCSTR(P1); + xr_sprintf (cmd,"demo_play %s",demo); + Console->Execute (cmd); + xr_free (demo); + uTime2Change = Device.TimerAsync() + u32(P2)*1000; + } +} + +void CGamePersistent::Statistics (CGameFont* F) +{ +#ifdef DEBUG +# ifndef _EDITOR + m_last_stats_frame = m_frame_counter; + profiler().show_stats (F,!!psAI_Flags.test(aiStats)); +# endif +#endif +} + +float CGamePersistent::MtlTransparent(u32 mtl_idx) +{ + return GMLib.GetMaterialByIdx((u16)mtl_idx)->fVisTransparencyFactor; +} +static BOOL bRestorePause = FALSE; +static BOOL bEntryFlag = TRUE; + +void CGamePersistent::OnAppActivate () +{ + bool bIsMP = (g_pGameLevel && Level().game && GameID() != GAME_SINGLE); + bIsMP &= !Device.Paused(); + + if( !bIsMP ) + { + Device.Pause (FALSE, !bRestorePause, TRUE, "CGP::OnAppActivate"); + }else + { + Device.Pause (FALSE, TRUE, TRUE, "CGP::OnAppActivate MP"); + } + + bEntryFlag = TRUE; +} + +void CGamePersistent::OnAppDeactivate () +{ + if(!bEntryFlag) return; + + bool bIsMP = (g_pGameLevel && Level().game && GameID() != GAME_SINGLE); + + bRestorePause = FALSE; + + if ( !bIsMP ) + { + bRestorePause = Device.Paused(); + Device.Pause (TRUE, TRUE, TRUE, "CGP::OnAppDeactivate"); + }else + { + Device.Pause (TRUE, FALSE, TRUE, "CGP::OnAppDeactivate MP"); + } + bEntryFlag = FALSE; +} + +bool CGamePersistent::OnRenderPPUI_query() +{ + return MainMenu()->OnRenderPPUI_query(); + // enable PP or not +} + +void CGamePersistent::OnRenderPPUI_main() +{ + // always + MainMenu()->OnRenderPPUI_main(); +} + +void CGamePersistent::OnRenderPPUI_PP() +{ + MainMenu()->OnRenderPPUI_PP(); +} +#include "string_table.h" + +void CGamePersistent::LoadTitle(LPCSTR str) +{ + string512 buff; + xr_sprintf (buff, "%s...", CStringTable().translate(str).c_str()); + pApp->LoadTitleInt (buff); +} + +bool CGamePersistent::CanBePaused() +{ + return IsGameTypeSingle (); +} + +void CGamePersistent::SetPickableEffectorDOF(bool bSet) +{ + m_bPickableDOF = bSet; + if(!bSet) + RestoreEffectorDOF(); +} + +void CGamePersistent::GetCurrentDof(Fvector3& dof) +{ + dof = m_dof[1]; +} + +void CGamePersistent::SetBaseDof(const Fvector3& dof) +{ + if (!m_bPickableDOF) + m_dof[0] = m_dof[1] = m_dof[2] = m_dof[3] = dof; + else + m_dof[3] = dof; +} + +void CGamePersistent::SetEffectorDOF(const Fvector& needed_dof) +{ + if(m_bPickableDOF) return; + m_dof[0] = needed_dof; + m_dof[2] = m_dof[1]; //current +} + +void CGamePersistent::RestoreEffectorDOF() +{ + SetEffectorDOF (m_dof[3]); +} + +#include "UIGameSP.h" + +void CGamePersistent::UpdateDof() +{ + if(m_bPickableDOF) + { + Fvector pick_dof; + pick_dof.y = HUD().GetCurrentRayQuery().range; + pick_dof.x = pick_dof.y+diff_near; + pick_dof.z = pick_dof.y+diff_far; + m_dof[0] = pick_dof; + m_dof[2] = m_dof[1]; //current + } + if(m_dof[1].similar(m_dof[0])) + return; + + float td = Device.fTimeDelta; + Fvector diff; + diff.sub (m_dof[0], m_dof[2]); + diff.mul (td/0.2f); //0.2 sec + m_dof[1].add (diff); + (m_dof[0].x m_intro_event; + + void xr_stdcall start_logo_intro (); + void xr_stdcall update_logo_intro (); + void xr_stdcall game_loaded (); + void xr_stdcall update_game_loaded (); + void xr_stdcall start_game_intro (); + void xr_stdcall update_game_intro (); + +#ifdef DEBUG + u32 m_frame_counter; + u32 m_last_stats_frame; +#endif + + void WeathersUpdate (); + void UpdateDof (); + +public: + ui_core* m_pUI_core; + IReader* pDemoFile; + u32 uTime2Change; + EVENT eDemoStart; + + CGamePersistent (); + virtual ~CGamePersistent (); + + virtual void Start (LPCSTR op); + virtual void Disconnect (); + + virtual void OnAppActivate (); + virtual void OnAppDeactivate (); + + virtual void OnAppStart (); + virtual void OnAppEnd (); + virtual void OnGameStart (); + virtual void OnGameEnd (); + virtual void _BCL OnFrame (); + virtual void OnEvent (EVENT E, u64 P1, u64 P2); + + virtual void UpdateGameType (); + + virtual void RegisterModel (IRenderVisual* V); + virtual float MtlTransparent (u32 mtl_idx); + virtual void Statistics (CGameFont* F); + + virtual bool OnRenderPPUI_query (); + virtual void OnRenderPPUI_main (); + virtual void OnRenderPPUI_PP (); + virtual void LoadTitle (LPCSTR str); + + virtual bool CanBePaused (); + + void SetPickableEffectorDOF (bool bSet); + void SetEffectorDOF (const Fvector& needed_dof); + void RestoreEffectorDOF (); + + virtual void GetCurrentDof (Fvector3& dof); + virtual void SetBaseDof (const Fvector3& dof); +}; + +IC CGamePersistent& GamePersistent() { return *((CGamePersistent*) g_pGamePersistent); } + +extern bool m_bCamReady; + +#endif //GamePersistentH + diff --git a/src/xrGameLA/GameTask.cpp b/src/xrGameLA/GameTask.cpp new file mode 100644 index 000000000..f5b7c174e --- /dev/null +++ b/src/xrGameLA/GameTask.cpp @@ -0,0 +1,710 @@ +#include "pch_script.h" +#include "GameTask.h" +#include "ui/xrUIXmlParser.h" +#include "encyclopedia_article.h" +#include "map_location.h" +#include "map_manager.h" + +#include "level.h" +#include "actor.h" +#include "script_engine.h" +#include "script_callback_ex.h" +#include "script_game_object.h" +#include "ai_space.h" +#include "alife_object_registry.h" +#include "alife_simulator.h" +#include "alife_story_registry.h" +#include "game_object_space.h" +#include "object_broker.h" +#include "ui/uitexturemaster.h" + +ALife::_STORY_ID story_id (LPCSTR story_id); +u16 storyId2GameId (ALife::_STORY_ID); + + + +using namespace luabind; + +ALife::_STORY_ID story_id (LPCSTR story_id) +{ + int res= + ( + object_cast( + luabind::object( + luabind::get_globals( + ai().script_engine().lua() + ) + ["story_ids"] + ) + [story_id] + ) + ); + return ALife::_STORY_ID(res); +} + +u16 storyId2GameId (ALife::_STORY_ID id) +{ + if(ai().get_alife()){ + CSE_ALifeDynamicObject* so = ai().alife().story_objects().object(id, true); + return (so)?so->ID:u16(-1); + }else{ + u32 cnt = Level().Objects.o_count(); + for(u32 it=0;it(O); + if(GO->story_id()==id) + return GO->ID(); + } + return u16(-1); + } +} + +CUIXml* g_gameTaskXml=NULL; + +CGameTask::CGameTask(const TASK_ID& id) +{ + m_ReceiveTime = 0; + m_FinishTime = 0; + m_Title = NULL; + m_priority = u32(-1); + Load (id); +} + +CGameTask::CGameTask() +{ + m_ReceiveTime = 0; + m_FinishTime = 0; + m_Title = NULL; + m_ID = NULL; +} + +void CGameTask::Load(const TASK_ID& id) +{ + m_ID = id; + + if(!g_gameTaskXml){ + g_gameTaskXml = new CUIXml(); + g_gameTaskXml->Load (CONFIG_PATH, "gameplay\\tasks", "game_tasks.xml"); + } + XML_NODE* task_node = g_gameTaskXml->NavigateToNodeWithAttribute("game_task","id",*id); + + THROW3 (task_node, "game task id=", *id); + g_gameTaskXml->SetLocalRoot (task_node); + m_Title = g_gameTaskXml->Read(g_gameTaskXml->GetLocalRoot(), "title", 0, NULL); + m_priority = g_gameTaskXml->ReadAttribInt(g_gameTaskXml->GetLocalRoot(), "prio", -1); +#ifdef DEBUG + if(m_priority == u32(-1)) + { + Msg("Game Task [%s] has no priority", *id); + } +#endif // DEBUG + int tag_num = g_gameTaskXml->GetNodesNum(g_gameTaskXml->GetLocalRoot(),"objective"); + m_Objectives.clear (); + for (int i = 0; i < tag_num; i++) + { + XML_NODE* l_root = NULL; + l_root = g_gameTaskXml->NavigateToNode("objective", i); + g_gameTaskXml->SetLocalRoot(l_root); + + m_Objectives.push_back(SGameTaskObjective(this, i)); + SGameTaskObjective& objective = m_Objectives.back(); + + //. + LPCSTR tag_text = g_gameTaskXml->Read(l_root, "text", 0, NULL); + objective.description = tag_text; + //. + tag_text = g_gameTaskXml->Read(l_root, "article", 0, NULL); + if (tag_text) + objective.article_id = tag_text; + + //. + tag_text = g_gameTaskXml->ReadAttrib(l_root, "key", NULL); + if (tag_text) + objective.article_key = tag_text; + + //. + if (i == 0) + { + objective.icon_texture_node_name = g_gameTaskXml->Read(g_gameTaskXml->GetLocalRoot(), "icon", 0, NULL); + + if (objective.icon_texture_node_name.size() && + 0 != stricmp(*objective.icon_texture_node_name, "ui\\ui_icons_task")) + { + objective.icon_rect = CUITextureMaster::GetTextureRect(*objective.icon_texture_node_name); + objective.icon_rect.rb.sub(objective.icon_rect.rb, objective.icon_rect.lt); + objective.icon_texture_file_name = CUITextureMaster::GetTextureFileName(*objective.icon_texture_node_name); + } + else + if (objective.icon_texture_node_name.size()) + { + objective.icon_rect.x1 = g_gameTaskXml->ReadAttribFlt(l_root, "icon", 0, "x"); + objective.icon_rect.y1 = g_gameTaskXml->ReadAttribFlt(l_root, "icon", 0, "y"); + objective.icon_rect.x2 = g_gameTaskXml->ReadAttribFlt(l_root, "icon", 0, "width"); + objective.icon_rect.y2 = g_gameTaskXml->ReadAttribFlt(l_root, "icon", 0, "height"); + } + } + //. + objective.map_location = g_gameTaskXml->Read(l_root, "map_location_type", 0, NULL); + + LPCSTR object_story_id = g_gameTaskXml->Read(l_root, "object_story_id", 0, NULL); + + //* + LPCSTR ddd; + ddd = g_gameTaskXml->Read(l_root, "map_location_hidden", 0, NULL); + if (ddd) + objective.def_location_enabled = false; + + objective.visible_subtask = true; + LPCSTR showhide = g_gameTaskXml->Read(l_root, "default_show", 0, NULL); + + if (showhide && !xr_strcmp(showhide, "0")) + { + objective.visible_subtask = false; + } + + bool b1,b2; + b1 = (0==objective.map_location.size()); + b2 = (NULL==object_story_id); + VERIFY3 (b1==b2,"check [map_location_type] and [object_story_id] fields in objective definition for: ",*objective.description); + +//. + objective.object_id = u16(-1); + +//. + objective.map_hint = g_gameTaskXml->ReadAttrib(l_root, "map_location_type", 0, "hint", NULL); + + if(object_story_id){ + ALife::_STORY_ID _sid = story_id(object_story_id); + objective.object_id = storyId2GameId(_sid); + } + + +//------infoportion_complete + int info_num = g_gameTaskXml->GetNodesNum(l_root,"infoportion_complete"); + objective.m_completeInfos.resize(info_num); + int j; + for(j=0; jRead(l_root, "infoportion_complete", j, NULL); + +//------infoportion_fail + info_num = g_gameTaskXml->GetNodesNum(l_root,"infoportion_fail"); + objective.m_failInfos.resize (info_num); + + for(j=0; jRead(l_root, "infoportion_fail", j, NULL); + +//------infoportion_skiped + info_num = g_gameTaskXml->GetNodesNum(l_root,"infoportion_skipped"); + objective.m_skipedInfos.resize (info_num); + + for(j=0; jRead(l_root, "infoportion_skipped", j, NULL); + +//------infoportion_set_complete + info_num = g_gameTaskXml->GetNodesNum(l_root,"infoportion_set_complete"); + objective.m_infos_on_complete.resize(info_num); + for(j=0; jRead(l_root, "infoportion_set_complete", j, NULL); + +//------infoportion_set_fail + info_num = g_gameTaskXml->GetNodesNum(l_root,"infoportion_set_fail"); + objective.m_infos_on_fail.resize(info_num); + for(j=0; jRead(l_root, "infoportion_set_fail", j, NULL); + +//------infoportion_set_skiped + info_num = g_gameTaskXml->GetNodesNum(l_root, "infoportion_set_skipped"); + objective.m_infos_on_skiped.resize(info_num); + for (j = 0; jRead(l_root, "infoportion_set_skipped", j, NULL); + + +//------function_complete + LPCSTR str; + bool functor_exists; + info_num = g_gameTaskXml->GetNodesNum(l_root,"function_complete"); + objective.m_complete_lua_functions.resize(info_num); + for(j=0; jRead(l_root, "function_complete", j, NULL); + functor_exists = ai().script_engine().functor(str ,objective.m_complete_lua_functions[j]); + THROW3 (functor_exists, "Cannot find script function described in task objective ", str); + } + + +//------function_fail + info_num = g_gameTaskXml->GetNodesNum(l_root,"function_fail"); + objective.m_fail_lua_functions.resize(info_num); + for(j=0; jRead(l_root, "function_fail", j, NULL); + functor_exists = ai().script_engine().functor(str ,objective.m_fail_lua_functions[j]); + THROW3 (functor_exists, "Cannot find script function described in task objective ", str); + } + +//------function_skiped + info_num = g_gameTaskXml->GetNodesNum(l_root,"function_skipped"); + objective.m_skiped_lua_functions.resize(info_num); + for(j=0; jRead(l_root, "function_skipped", j, NULL); + functor_exists = ai().script_engine().functor(str ,objective.m_skiped_lua_functions[j]); + THROW3 (functor_exists, "Cannot find script function described in task objective ", str); + } + +//------function_on_complete + info_num = g_gameTaskXml->GetNodesNum(l_root,"function_call_complete"); + objective.m_lua_functions_on_complete.resize(info_num); + for(j=0; jRead(l_root, "function_call_complete", j, NULL); + functor_exists = ai().script_engine().functor(str ,objective.m_lua_functions_on_complete[j]); + THROW3 (functor_exists, "Cannot find script function described in task objective ", str); + } + + +//------function_on_fail + info_num = g_gameTaskXml->GetNodesNum(l_root,"function_call_fail"); + objective.m_lua_functions_on_fail.resize(info_num); + for(j=0; jRead(l_root, "function_call_fail", j, NULL); + functor_exists = ai().script_engine().functor(str ,objective.m_lua_functions_on_fail[j]); + THROW3 (functor_exists, "Cannot find script function described in task objective ", str); + } + +//------function_on_fail + info_num = g_gameTaskXml->GetNodesNum(l_root,"function_call_skipped"); + objective.m_lua_functions_on_skiped.resize(info_num); + for(j=0; jRead(l_root, "function_call_skipped", j, NULL); + functor_exists = ai().script_engine().functor(str ,objective.m_lua_functions_on_skiped[j]); + THROW3 (functor_exists, "Cannot find script function described in task objective ", str); + } + + g_gameTaskXml->SetLocalRoot (task_node); + } + g_gameTaskXml->SetLocalRoot (g_gameTaskXml->GetRoot()); +} + +bool CGameTask::HasLinkedMapLocations () +{ + for(u32 i=0; im_ReceiveTime != parent->m_TimeToComplete) ) + { + if(Level().GetGameTime() > parent->m_TimeToComplete) + { + return eTaskStateFail; + } + } +//check fail infos + if( CheckInfo(m_failInfos) ) + return eTaskStateFail; + +//check fail functor + if( CheckFunctions(m_fail_lua_functions) ) + return eTaskStateFail; + +//check complete infos + if( CheckInfo(m_completeInfos) ) + return eTaskStateCompleted; + +//check complete functor + if( CheckFunctions(m_complete_lua_functions) ) + return eTaskStateCompleted; + +//check skiped infos + if (CheckInfo(m_skipedInfos)) + return eTaskStateSkiped; + +//check skiped functor + if (CheckFunctions(m_skiped_lua_functions)) + return eTaskStateSkiped; + + return TaskState(); +} + +void SGameTaskObjective::SendInfo (xr_vector& v) +{ + xr_vector::iterator it = v.begin(); + for(;it!=v.end();++it) + Actor()->TransferInfo ((*it),true); + +} + +bool SGameTaskObjective::CheckInfo (xr_vector& v) +{ + bool res = false; + xr_vector::iterator it = v.begin(); + for(;it!=v.end();++it){ + res = Actor()->HasInfo (*it); + if(!res) break; + } + return res; +} + +bool SGameTaskObjective::CheckFunctions (xr_vector >& v) +{ + bool res = false; + xr_vector >::iterator it = v.begin(); + for(;it!=v.end();++it){ + if( (*it).is_valid() ) res = (*it)(*(parent->m_ID), idx); + if(!res) break; + } + return res; + +} + +void SGameTaskObjective::CallAllFuncs (xr_vector >& v) +{ + xr_vector >::iterator it = v.begin(); + for(;it!=v.end();++it){ + if( (*it).is_valid() ) (*it)(*(parent->m_ID), idx, parent); + } +} + +void SGameTaskObjective::ShowSubtask(bool show) +{ + //Msg("ShowSubtask %s %s", description.c_str(), description); + visible_subtask = show; +} + +void SGameTaskObjective::SetDescription_script(LPCSTR _descr) +{ + description = _descr; +} + +void SGameTaskObjective::SetArticleID_script(LPCSTR _id) +{ + article_id = _id; +} + +void SGameTaskObjective::SetMapHint_script(LPCSTR _str) +{ + map_hint = _str; +} + +void SGameTaskObjective::SetMapLocation_script(LPCSTR _str) +{ + map_location = _str; +} + +void SGameTaskObjective::SetObjectID_script(u16 id) +{ + object_id = id; +} + +void SGameTaskObjective::SetIconName_script(LPCSTR _str) +{ + icon_texture_node_name = _str; + + icon_rect = CUITextureMaster::GetTextureRect(icon_texture_node_name.c_str()); + icon_rect.rb.sub (icon_rect.rb, icon_rect.lt); + icon_texture_file_name = CUITextureMaster::GetTextureFileName(icon_texture_node_name.c_str()); +} + +void SGameTaskObjective::SetArticleKey_script(LPCSTR _str) +{ + article_key = _str; +} + +void SGameTaskObjective::AddCompleteInfo_script(LPCSTR _str) +{ + m_completeInfos.push_back(_str); +} + +void SGameTaskObjective::AddFailInfo_script(LPCSTR _str) +{ + m_failInfos.push_back(_str); +} + +void SGameTaskObjective::AddSkipedInfo_script(LPCSTR _str) +{ + m_skipedInfos.push_back(_str); +} + +void SGameTaskObjective::AddOnCompleteInfo_script(LPCSTR _str) +{ + m_infos_on_complete.push_back(_str); +} + +void SGameTaskObjective::AddOnFailInfo_script(LPCSTR _str) +{ + m_infos_on_fail.push_back(_str); +} + +void SGameTaskObjective::AddOnSkipedInfo_script(LPCSTR _str) +{ + m_infos_on_skiped.push_back(_str); +} + +void SGameTaskObjective::AddCompleteFunc_script(LPCSTR _str) +{ + m_pScriptHelper.m_s_complete_lua_functions.push_back(_str); +} +void SGameTaskObjective::AddFailFunc_script(LPCSTR _str) +{ + m_pScriptHelper.m_s_fail_lua_functions.push_back(_str); +} +void SGameTaskObjective::AddSkipedFunc_script(LPCSTR _str) +{ + m_pScriptHelper.m_s_skiped_lua_functions.push_back(_str); +} +void SGameTaskObjective::AddOnCompleteFunc_script(LPCSTR _str) +{ + m_pScriptHelper.m_s_lua_functions_on_complete.push_back(_str); +} +void SGameTaskObjective::AddOnFailFunc_script(LPCSTR _str) +{ + m_pScriptHelper.m_s_lua_functions_on_fail.push_back(_str); +} +void SGameTaskObjective::AddOnSkipedFunc_script(LPCSTR _str) +{ + m_pScriptHelper.m_s_lua_functions_on_skiped.push_back(_str); +} + + + +void CGameTask::Load_script(LPCSTR _id) +{ + Load(_id); +} + +void CGameTask::SetTitle_script(LPCSTR _title) +{ + m_Title = _title; +} + +void CGameTask::SetPriority_script(int _prio) +{ + m_priority = _prio; +} + +void CGameTask::AddObjective_script(SGameTaskObjective* O) +{ + O->m_pScriptHelper.init_functors(O->m_pScriptHelper.m_s_complete_lua_functions, O->m_complete_lua_functions); + O->m_pScriptHelper.init_functors(O->m_pScriptHelper.m_s_fail_lua_functions, O->m_fail_lua_functions); + O->m_pScriptHelper.init_functors(O->m_pScriptHelper.m_s_skiped_lua_functions, O->m_skiped_lua_functions); + O->m_pScriptHelper.init_functors(O->m_pScriptHelper.m_s_lua_functions_on_complete, O->m_lua_functions_on_complete); + O->m_pScriptHelper.init_functors(O->m_pScriptHelper.m_s_lua_functions_on_fail, O->m_lua_functions_on_fail); + O->m_pScriptHelper.init_functors(O->m_pScriptHelper.m_s_lua_functions_on_skiped, O->m_lua_functions_on_skiped); + + m_Objectives.push_back(*O); +} + +void SGameTaskObjective::ChangeStateCallback() +{ + Actor()->callback(GameObject::eTaskStateChange)(parent, this, task_state); +} + + +void SGameTaskObjective::save(IWriter &stream) +{ + save_data(idx, stream); + save_data(task_state, stream); + + save_data(description, stream); + save_data(map_location, stream); + save_data(object_id, stream); + save_data(task_state, stream); + save_data(def_location_enabled, stream); + save_data(map_hint, stream); + save_data(icon_texture_node_name, stream); + save_data(icon_rect, stream); + save_data(article_id, stream); + + save_data(m_completeInfos, stream); + save_data(m_failInfos, stream); + save_data(m_skipedInfos, stream); + save_data(m_infos_on_complete, stream); + save_data(m_infos_on_fail, stream); + save_data(m_infos_on_skiped, stream); + save_data(visible_subtask, stream); + + bool b_script = m_pScriptHelper.not_empty(); + save_data(b_script, stream); + if(b_script) + save_data(m_pScriptHelper, stream); + +} + +void SGameTaskObjective::load(IReader &stream) +{ + load_data(idx, stream); + load_data(task_state, stream); + + load_data(description, stream); + load_data(map_location, stream); + load_data(object_id, stream); + load_data(task_state, stream); + load_data(def_location_enabled, stream); + load_data(map_hint, stream); + load_data(icon_texture_node_name, stream); + load_data(icon_rect, stream); + load_data(article_id, stream); + + load_data(m_completeInfos, stream); + load_data(m_failInfos, stream); + load_data(m_skipedInfos, stream); + load_data(m_infos_on_complete, stream); + load_data(m_infos_on_fail, stream); + load_data(m_infos_on_skiped, stream); + load_data(visible_subtask, stream); + + icon_texture_file_name = CUITextureMaster::GetTextureFileName(icon_texture_node_name.c_str()); + + bool b_script; + load_data(b_script, stream); + if(b_script){ + load_data(m_pScriptHelper, stream); + + m_pScriptHelper.init_functors (m_pScriptHelper.m_s_complete_lua_functions, m_complete_lua_functions); + m_pScriptHelper.init_functors (m_pScriptHelper.m_s_fail_lua_functions, m_fail_lua_functions); + m_pScriptHelper.init_functors (m_pScriptHelper.m_s_skiped_lua_functions, m_skiped_lua_functions); + m_pScriptHelper.init_functors (m_pScriptHelper.m_s_lua_functions_on_complete, m_lua_functions_on_complete); + m_pScriptHelper.init_functors (m_pScriptHelper.m_s_lua_functions_on_fail, m_lua_functions_on_fail); + m_pScriptHelper.init_functors (m_pScriptHelper.m_s_lua_functions_on_skiped, m_lua_functions_on_skiped); + } +} + +void SScriptObjectiveHelper::init_functors(xr_vector& v_src, xr_vector >& v_dest) +{ + xr_vector::iterator it = v_src.begin(); + xr_vector::iterator it_e = v_src.end(); + v_dest.resize(v_src.size()); + + for(u32 idx=0 ;it!=it_e;++it,++idx) + { + bool functor_exists = ai().script_engine().functor(*(*it) ,v_dest[idx]); + if(!functor_exists) Log("Cannot find script function described in task objective ", *(*it)); + } +} + +void SScriptObjectiveHelper::load(IReader &stream) +{ + load_data(m_s_complete_lua_functions, stream); + load_data(m_s_fail_lua_functions, stream); + load_data(m_s_skiped_lua_functions, stream); + load_data(m_s_lua_functions_on_complete, stream); + load_data(m_s_lua_functions_on_fail, stream); + load_data(m_s_lua_functions_on_skiped, stream); +} + +void SScriptObjectiveHelper::save(IWriter &stream) +{ + save_data(m_s_complete_lua_functions, stream); + save_data(m_s_fail_lua_functions, stream); + save_data(m_s_skiped_lua_functions, stream); + save_data(m_s_lua_functions_on_complete, stream); + save_data(m_s_lua_functions_on_fail, stream); + save_data(m_s_lua_functions_on_skiped, stream); +} + +void SGameTaskKey::save(IWriter &stream) +{ + save_data(task_id, stream); + save_data(game_task->m_ReceiveTime, stream); + save_data(game_task->m_FinishTime, stream); + save_data(game_task->m_TimeToComplete, stream); + save_data(game_task->m_Title, stream); + + u32 cnt = game_task->m_Objectives.size(); + save_data(cnt, stream); + + OBJECTIVE_VECTOR_IT it = game_task->m_Objectives.begin(); + OBJECTIVE_VECTOR_IT it_e = game_task->m_Objectives.end(); + for(;it!=it_e;++it) + save_data(*it, stream); + +} + +void SGameTaskKey::load(IReader &stream) +{ + load_data(task_id, stream); + game_task = new CGameTask(task_id); + load_data(game_task->m_ReceiveTime, stream); + load_data(game_task->m_FinishTime, stream); + load_data(game_task->m_TimeToComplete, stream); + + load_data(game_task->m_Title, stream); + + u32 cnt; + load_data(cnt, stream); + + if(cnt>game_task->m_Objectives.size()) + game_task->m_Objectives.resize(cnt); + + for(u32 i=0; im_Objectives[i], stream); + game_task->m_Objectives[i].parent = game_task; + } +} + +void SGameTaskKey::destroy() +{ + delete_data(game_task); +} \ No newline at end of file diff --git a/src/xrGameLA/GameTask.h b/src/xrGameLA/GameTask.h new file mode 100644 index 000000000..f3ce24820 --- /dev/null +++ b/src/xrGameLA/GameTask.h @@ -0,0 +1,163 @@ +#pragma once + +#include "encyclopedia_article_defs.h" +#include "GameTaskDefs.h" +#include "script_export_space.h" +#include "pch_script.h" + +class CGameTaskManager; +class CMapLocation; +class CGameTask; + +typedef xr_vector > task_state_functors; + +class SScriptObjectiveHelper: public IPureSerializeObject +{ +public: + xr_vector m_s_complete_lua_functions; + xr_vector m_s_fail_lua_functions; + xr_vector m_s_skiped_lua_functions; + + xr_vector m_s_lua_functions_on_complete; + xr_vector m_s_lua_functions_on_fail; + xr_vector m_s_lua_functions_on_skiped; +public: + bool not_empty () {return m_s_complete_lua_functions.size() || + m_s_fail_lua_functions.size() || + m_s_skiped_lua_functions.size() || + m_s_lua_functions_on_complete.size()|| + m_s_lua_functions_on_fail.size() || + m_s_lua_functions_on_skiped.size(); + } + + virtual void save (IWriter &stream); + virtual void load (IReader &stream); + + void init_functors (xr_vector& v_src, task_state_functors& v_dest); +}; + +class SGameTaskObjective : public IPureSerializeObject +{ + friend struct SGameTaskKey; + friend class CGameTaskManager; +private: + ETaskState task_state; + CGameTask* parent; + int idx; + void SendInfo (xr_vector&); + void CallAllFuncs (xr_vector >& v); + bool CheckInfo (xr_vector&); + bool CheckFunctions (xr_vector >& v); + void SetTaskState (ETaskState new_state); +public: + SScriptObjectiveHelper m_pScriptHelper; + virtual void save (IWriter &stream); + virtual void load (IReader &stream); + + SGameTaskObjective (CGameTask* parent, int idx); + SGameTaskObjective (); + shared_str description; + shared_str article_id; + shared_str map_hint; + shared_str map_location; + u16 object_id; + shared_str article_key; + CMapLocation* LinkedMapLocation (); + ETaskState TaskState () {return task_state;}; + ETaskState UpdateState (); + + shared_str icon_texture_file_name; + shared_str icon_texture_node_name; // for storing name of texture node from texture xml + Frect icon_rect; + bool def_location_enabled; + bool visible_subtask; +//complete/fail stuff + xr_vector m_completeInfos; + xr_vector m_failInfos; + xr_vector m_skipedInfos; + xr_vector m_infos_on_complete; + xr_vector m_infos_on_fail; + xr_vector m_infos_on_skiped; + + task_state_functors m_fail_lua_functions; + task_state_functors m_complete_lua_functions; + task_state_functors m_skiped_lua_functions; + + task_state_functors m_lua_functions_on_complete; + task_state_functors m_lua_functions_on_fail; + task_state_functors m_lua_functions_on_skiped; + +// for scripting access + void SetDescription_script (LPCSTR _descr); + void SetArticleID_script (LPCSTR _id); + int GetIDX_script () {return idx;}; + void SetMapHint_script (LPCSTR _str); + void SetMapLocation_script (LPCSTR _str); + + void SetObjectID_script (u16 id); + LPCSTR GetIconName_script () { return icon_texture_node_name.size() ? icon_texture_node_name.c_str() : "null icon"; }; + + void SetArticleKey_script (LPCSTR _str); + + void SetIconName_script (LPCSTR _str); + + void AddCompleteInfo_script (LPCSTR _str); + void AddFailInfo_script (LPCSTR _str); + void AddSkipedInfo_script (LPCSTR _str); + void AddOnCompleteInfo_script(LPCSTR _str); + void AddOnFailInfo_script (LPCSTR _str); + void AddOnSkipedInfo_script (LPCSTR _str); + + void AddCompleteFunc_script (LPCSTR _str); + void AddFailFunc_script (LPCSTR _str); + void AddSkipedFunc_script (LPCSTR _str); + void AddOnCompleteFunc_script(LPCSTR _str); + void AddOnFailFunc_script (LPCSTR _str); + void AddOnSkipedFunc_script (LPCSTR _str); + LPCSTR GetDescription_script () {return *description;}; + void ChangeStateCallback (); + void ShowSubtask (bool show); +}; + +DEFINE_VECTOR(SGameTaskObjective, OBJECTIVE_VECTOR, OBJECTIVE_VECTOR_IT); + +class CGameTask +{ +private: + CGameTask (const CGameTask&){}; //disable copy ctor +protected: + void Load (const TASK_ID& id); +public: + CGameTask (const TASK_ID& id); + CGameTask (); + + bool HasLinkedMapLocations (); + bool HasInProgressObjective (); + + SGameTaskObjective& Objective (int objectice_id) {return m_Objectives[objectice_id];}; + + TASK_ID m_ID; + shared_str m_Title; + OBJECTIVE_VECTOR m_Objectives; + ALife::_TIME_ID m_ReceiveTime; + ALife::_TIME_ID m_FinishTime; + ALife::_TIME_ID m_TimeToComplete; + u32 m_priority; + +// for scripting access + void Load_script (LPCSTR _id); + void SetTitle_script (LPCSTR _title); + LPCSTR GetTitle_script () {return *m_Title;}; + void SetPriority_script (int _prio); + int GetPriority_script () {return m_priority;}; + void AddObjective_script (SGameTaskObjective* O); + SGameTaskObjective* GetObjective_script (int objective_id) {return &(Objective(objective_id));} + LPCSTR GetID_script () {return *m_ID;} + void SetID_script (LPCSTR _id) {m_ID = _id;} + int GetObjectiveSize_script () {return m_Objectives.size();} + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CGameTask) +#undef script_type_list +#define script_type_list save_type_list(CGameTask) diff --git a/src/xrGameLA/GameTaskDefs.h b/src/xrGameLA/GameTaskDefs.h new file mode 100644 index 000000000..1c4064dc1 --- /dev/null +++ b/src/xrGameLA/GameTaskDefs.h @@ -0,0 +1,47 @@ +#pragma once + +enum ETaskState { + eTaskStateFail = 0, + eTaskStateInProgress, + eTaskStateCompleted, + eTaskStateSkiped, +//. eTaskUserDefined, + eTaskStateDummy = u32(-1) +}; + + +typedef shared_str TASK_ID; +DEFINE_VECTOR (TASK_ID, TASK_ID_VECTOR, TASK_ID_IT); +extern shared_str g_active_task_id; +extern u16 g_active_task_objective_id; + +#include "alife_abstract_registry.h" + +class CGameTask; + +struct SGameTaskKey : public IPureSerializeObject,public IPureDestroyableObject { + TASK_ID task_id; + CGameTask* game_task; + SGameTaskKey (TASK_ID t_id):task_id(t_id),game_task(NULL){}; + SGameTaskKey ():task_id(NULL),game_task(NULL){}; + + + virtual void save (IWriter &stream); + virtual void load (IReader &stream); + virtual void destroy (); +}; + +DEFINE_VECTOR (SGameTaskKey, GameTasks, GameTasks_it); + +struct CGameTaskRegistry : public CALifeAbstractRegistry { + virtual void save(IWriter &stream){ + CALifeAbstractRegistry::save(stream); + save_data (g_active_task_id, stream); + save_data (g_active_task_objective_id, stream); + }; + virtual void load(IReader &stream){ + CALifeAbstractRegistry::load(stream); + load_data (g_active_task_id, stream); + load_data (g_active_task_objective_id, stream); + }; +}; diff --git a/src/xrGameLA/GameTask_script.cpp b/src/xrGameLA/GameTask_script.cpp new file mode 100644 index 000000000..f54617b5a --- /dev/null +++ b/src/xrGameLA/GameTask_script.cpp @@ -0,0 +1,69 @@ +#include "pch_script.h" +#include "GameTask.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CGameTask::script_register(lua_State *L) +{ + module(L) + [ + class_ >("task") + .enum_("task_state") + [ + value("fail", int(eTaskStateFail)), + value("in_progress", int(eTaskStateInProgress)), + value("completed", int(eTaskStateCompleted)), + value("skipped", int(eTaskStateSkiped)), + value("task_dummy", int(eTaskStateDummy)) + ], + + class_("SGameTaskObjective") + .def( constructor() ) + .def("set_description", &SGameTaskObjective::SetDescription_script ) + .def("get_description", &SGameTaskObjective::GetDescription_script ) + .def("set_article_id", &SGameTaskObjective::SetArticleID_script ) + .def("set_map_hint", &SGameTaskObjective::SetMapHint_script ) + .def("set_map_location", &SGameTaskObjective::SetMapLocation_script ) + .def("set_object_id", &SGameTaskObjective::SetObjectID_script ) + .def("set_article_key", &SGameTaskObjective::SetArticleKey_script ) + + .def("set_icon_name", &SGameTaskObjective::SetIconName_script) + .def("get_icon_name", &SGameTaskObjective::GetIconName_script) + + .def_readwrite("def_ml_enabled", &SGameTaskObjective::def_location_enabled ) + + .def("add_complete_info", &SGameTaskObjective::AddCompleteInfo_script ) + .def("add_fail_info", &SGameTaskObjective::AddFailInfo_script ) + .def("add_skipped_info", &SGameTaskObjective::AddSkipedInfo_script ) + .def("add_on_complete_info", &SGameTaskObjective::AddOnCompleteInfo_script ) + .def("add_on_fail_info", &SGameTaskObjective::AddOnFailInfo_script ) + .def("add_on_skipped_info", &SGameTaskObjective::AddOnSkipedInfo_script ) + + .def("add_complete_func", &SGameTaskObjective::AddCompleteFunc_script ) + .def("add_fail_func", &SGameTaskObjective::AddFailFunc_script ) + .def("add_skipped_func", &SGameTaskObjective::AddSkipedFunc_script ) + .def("add_on_complete_func", &SGameTaskObjective::AddOnCompleteFunc_script ) + .def("add_on_fail_func", &SGameTaskObjective::AddOnFailFunc_script ) + .def("add_on_skipped_func", &SGameTaskObjective::AddOnSkipedFunc_script ) + + .def("get_state", &SGameTaskObjective::TaskState ) + .def("get_idx", &SGameTaskObjective::GetIDX_script ) + .def("get_state", &SGameTaskObjective::TaskState ) + .def("show_subtask", &SGameTaskObjective::ShowSubtask ), + + class_("CGameTask") + .def( constructor<>() ) + .def("load", &CGameTask::Load_script ) + .def("set_title", &CGameTask::SetTitle_script ) + .def("get_title", &CGameTask::GetTitle_script ) + .def("set_priority", &CGameTask::SetPriority_script ) + .def("get_priority", &CGameTask::GetPriority_script ) + .def("add_objective", &CGameTask::AddObjective_script, adopt(_2)) + .def("get_objective", &CGameTask::GetObjective_script ) + .def("get_id", &CGameTask::GetID_script ) + .def("set_id", &CGameTask::SetID_script ) + .def("get_objectives_cnt", &CGameTask::GetObjectiveSize_script ) + + ]; +} \ No newline at end of file diff --git a/src/xrGameLA/GametaskManager.cpp b/src/xrGameLA/GametaskManager.cpp new file mode 100644 index 000000000..ec1a7a409 --- /dev/null +++ b/src/xrGameLA/GametaskManager.cpp @@ -0,0 +1,344 @@ +#include "pch_script.h" +#include "GameTaskManager.h" +#include "alife_registry_wrappers.h" +#include "ui/xrUIXmlParser.h" +#include "GameTask.h" +#include "Level.h" +#include "map_manager.h" +#include "map_location.h" +#include "HUDManager.h" +#include "actor.h" +#include "UIGameSP.h" +#include "ui/UIPDAWnd.h" +#include "encyclopedia_article.h" +#include "ui/UIEventsWnd.h" + +#pragma warning(push) +#pragma warning(disable:4995) +#include +#pragma warning(pop) + +shared_str g_active_task_id = ""; +u16 g_active_task_objective_id = u16(-1); + +struct FindTaskByID{ + TASK_ID id; + FindTaskByID(const TASK_ID& s):id(s){} + bool operator () (const SGameTaskKey& key){ + return (id==key.task_id); + } +}; + +bool task_prio_pred(const SGameTaskKey& k1, const SGameTaskKey& k2) +{ + return k1.game_task->m_priority < k2.game_task->m_priority; +} + +CGameTaskManager::CGameTaskManager() +{ + m_gametasks = new CGameTaskWrapper(); + m_flags.zero (); + m_flags.set (eChanged, TRUE); + if(g_active_task_id.size()) SetActiveTask(g_active_task_id, g_active_task_objective_id); +} + +CGameTaskManager::~CGameTaskManager() +{ + delete_data (m_gametasks); +} + +void CGameTaskManager::initialize(u16 id) +{ + m_gametasks->registry().init(id);// actor's id +} + +GameTasks& CGameTaskManager::GameTasks () +{ + return m_gametasks->registry().objects(); +} + +CGameTask* CGameTaskManager::HasGameTask(const TASK_ID& id) +{ + FindTaskByID key(id); + GameTasks_it it = std::find_if(GameTasks().begin(),GameTasks().end(),key); + if( it!=GameTasks().end() ) + return (*it).game_task; + + return 0; +} + +CGameTask* CGameTaskManager::HasGameTask(const CMapLocation* ml, bool only_inprocess) +{ + GameTasks_it it = GameTasks().begin(); + GameTasks_it it_e = GameTasks().end(); + + for(; it!=it_e; ++it) + { + CGameTask* gt = (*it).game_task; + + for(u16 i=0; im_Objectives.size() ;++i) + { + SGameTaskObjective& obj = gt->Objective(i); + if(obj.LinkedMapLocation()==ml) + { + if(only_inprocess && obj.TaskState()!=eTaskStateInProgress) + continue; + + return gt; + } + } + } + return NULL; +} + +CGameTask* CGameTaskManager::GiveGameTaskToActor(const TASK_ID& id, u32 timeToComplete, bool bCheckExisting) +{ + if(bCheckExisting && HasGameTask(id)) return NULL; + CGameTask* t = new CGameTask(id); + + return GiveGameTaskToActor (t, timeToComplete, bCheckExisting); +} + +CGameTask* CGameTaskManager::GiveGameTaskToActor(CGameTask* t, u32 timeToComplete, bool bCheckExisting) +{ + if(bCheckExisting && HasGameTask(t->m_ID)) return NULL; + m_flags.set (eChanged, TRUE); + + GameTasks().push_back (SGameTaskKey(t->m_ID) ); + GameTasks().back().game_task = t; + t->m_ReceiveTime = Level().GetGameTime(); + t->m_TimeToComplete = t->m_ReceiveTime + timeToComplete; + + + std::sort (GameTasks().begin(), GameTasks().end(), task_prio_pred); + + ARTICLE_VECTOR& article_vector = Actor()->encyclopedia_registry->registry().objects(); + + + SGameTaskObjective *obj = NULL; + for (u32 i = 0; i < t->m_Objectives.size(); ++i){ + obj = &t->m_Objectives[i]; + if(obj->article_id.size()){ + FindArticleByIDPred pred(obj->article_id); + if( std::find_if(article_vector.begin(), article_vector.end(), pred) == article_vector.end() ){ + CEncyclopediaArticle article; + article.Load(obj->article_id); + article_vector.push_back(ARTICLE_DATA(obj->article_id, Level().GetGameTime(), article.data()->articleType)); + } + } + + if(obj->object_id!=u16(-1) && obj->map_location.size() && obj->def_location_enabled){ + CMapLocation* ml = Level().MapManager().AddMapLocation(obj->map_location, obj->object_id); + if(obj->map_hint.size()) ml->SetHint(obj->map_hint); + ml->DisablePointer (); + ml->SetSerializable (true); + } + } + CGameTask* _at = ActiveTask(); + if ( (NULL==_at) || (_at->m_priority > t->m_priority) ) + { + SetActiveTask(t->m_ID, 1); + } + + + //установить флажок необходимости прочтения тасков в PDA + if(CurrentGameUI()){ + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if(pGameSP) + pGameSP->m_PdaMenu->PdaContentsChanged (pda_section::quests); + } + if(true /*t->m_ID!="user_task"*/) + t->Objective(0).ChangeStateCallback(); + + return t; +} + +void CGameTaskManager::SetTaskState(CGameTask* t, u16 objective_num, ETaskState state) +{ + m_flags.set (eChanged, TRUE); + bool isRoot = (objective_num==0); + SGameTaskObjective* o = &t->Objective(objective_num); + + CMapLocation* ml = o->LinkedMapLocation(); + bool bActive = ActiveObjective()==o; + + if (((state == eTaskStateFail) || (state == eTaskStateCompleted) || (state == eTaskStateSkiped)) && ml){ + Level().MapManager().RemoveMapLocation(o->map_location, o->object_id); + o->map_location = NULL; + o->object_id = u16(-1); + } + + o->SetTaskState (state); + + //highlight next objective if needed + if( (isRoot || !t->HasInProgressObjective()) && (ActiveTask()==t) ) + { + SetActiveTask ("", 1 ); + }else + if(!isRoot && bActive && objective_num != (t->m_Objectives.size()-1) ){//not last + SetActiveTask (t->m_ID, objective_num+1 ); + } + + + if(isRoot){//setState for task and all sub-tasks + + for(u16 i=0; im_Objectives.size();++i) + if( t->Objective(i).TaskState()==eTaskStateInProgress ) + SetTaskState(t,i,state); + } + + if (0 == objective_num && eTaskStateCompleted == state || eTaskStateFail == state || eTaskStateSkiped == state) + t->m_FinishTime = Level().GetGameTime(); + + + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if(pGameSP) { + pGameSP->m_PdaMenu->PdaContentsChanged (pda_section::quests); + } + +} + +void CGameTaskManager::SetTaskState(const TASK_ID& id, u16 objective_num, ETaskState state) +{ + CGameTask* t = HasGameTask(id); + if (NULL==t) {Msg("actor does not has task [%s]", *id); return;} + SetTaskState (t,objective_num, state); +} + +void CGameTaskManager::UpdateTasks () +{ + u32 task_count = GameTasks().size(); + if(0==task_count) return; + + SGameTaskKey *tasks = (SGameTaskKey*)_alloca(task_count*sizeof(SGameTaskKey)); + SGameTaskKey *I = tasks; + SGameTaskKey *E = tasks + task_count; + GameTasks_it i = GameTasks().begin(); + + for ( ; I != E; ++I, ++i) + new (I) SGameTaskKey(*i); + + for (I = tasks; I != E; ++I) + { + CGameTask *t = (*I).game_task; + for(u16 i=0; im_Objectives.size() ;++i) + { + SGameTaskObjective& obj = t->Objective(i); + if(obj.TaskState()!=eTaskStateInProgress && i==0) break; + if(obj.TaskState()!=eTaskStateInProgress) continue; + + ETaskState state = obj.UpdateState(); + + if ((state == eTaskStateFail || state == eTaskStateCompleted || state == eTaskStateSkiped)) + SetTaskState(t, i, state); + } + } + + for ( ; I != E; ++I, ++i) + I->~SGameTaskKey (); + + SGameTaskObjective* obj = ActiveObjective(); + if(obj) + { + Level().MapManager().DisableAllPointers(); + CMapLocation* ml = obj->LinkedMapLocation(); + if(ml && !ml->PointerEnabled()) + ml->EnablePointer(); + } + + if( m_flags.test(eChanged) ) + UpdateActiveTask (); +} + + +void CGameTaskManager::UpdateActiveTask () +{ + GameTasks_it it = GameTasks().begin(); + GameTasks_it it_e = GameTasks().end(); + bool bHasSpotPointer = false; + + for( ;it!=it_e; ++it ) + { + CGameTask* t = (*it).game_task; + + if( t->Objective(0).TaskState() != eTaskStateInProgress ) + continue; + + for(u32 i=0; im_Objectives.size() ;++i) + { + SGameTaskObjective& obj = t->Objective(i); + + //1-st enable hidden locations + if( (!obj.def_location_enabled) && + (obj.TaskState()==eTaskStateInProgress) && + (t->Objective(i-1).TaskState()==eTaskStateCompleted) ) + { + if(obj.object_id!=u16(-1) && *obj.map_location) + { + CMapLocation* ml = obj.LinkedMapLocation(); + if(!ml) ml = Level().MapManager().AddMapLocation(obj.map_location, obj.object_id); + if(obj.map_hint.size()) ml->SetHint(obj.map_hint); + ml->DisablePointer (); + ml->SetSerializable (true); + } + } + bHasSpotPointer = bHasSpotPointer || (ActiveObjective()==&t->Objective(i)); + } + } + // highlight new spot pointer + if( !bHasSpotPointer ) + { + bool bDone =false; + GameTasks::iterator it = GameTasks().begin(); + GameTasks::iterator it_e = GameTasks().end(); + + for( ;(it!=it_e)&&(!bDone); ++it ) + { + CGameTask* t = (*it).game_task; + if(t->Objective(0).TaskState()!=eTaskStateInProgress) continue; + + for(u16 i=0; (im_Objectives.size())&&(!bDone) ;++i) + { + if( (i==0) || (t->Objective(i).TaskState() != eTaskStateInProgress) ) continue; + + SetActiveTask (t->m_ID, i); + bDone = true; + } + } + } + + m_flags.set (eChanged, FALSE); +} + +CGameTask* CGameTaskManager::ActiveTask() +{ + const TASK_ID& t_id = g_active_task_id; + if(!t_id.size()) return NULL; + return HasGameTask( t_id ); +} + +void CGameTaskManager::SetActiveTask(const TASK_ID& id, u16 idx) +{ + if(idx==0) + Msg("! g_active_task_objective_idx==0"); + + g_active_task_id = id; + g_active_task_objective_id = idx; + + Level().MapManager().DisableAllPointers(); + SGameTaskObjective* o = ActiveObjective(); + + if(o) + { + CMapLocation* ml = o->LinkedMapLocation(); + if(ml) + ml->EnablePointer(); + } +} + +SGameTaskObjective* CGameTaskManager::ActiveObjective() +{ + CGameTask* t = ActiveTask(); + + return (t)?&t->Objective(g_active_task_objective_id):NULL; +} diff --git a/src/xrGameLA/GametaskManager.h b/src/xrGameLA/GametaskManager.h new file mode 100644 index 000000000..b8b51b917 --- /dev/null +++ b/src/xrGameLA/GametaskManager.h @@ -0,0 +1,38 @@ +#pragma once + +#include "GameTaskDefs.h" +#include "object_interfaces.h" + +class CGameTaskWrapper; +class CGameTask; +class CMapLocation; +class SGameTaskObjective; + +class CGameTaskManager +{ + CGameTaskWrapper* m_gametasks; + enum {eChanged = (1<<0),}; + Flags8 m_flags; +protected: + void UpdateActiveTask (); +public: + + CGameTaskManager (); + ~CGameTaskManager (); + + void initialize (u16 id); + GameTasks& GameTasks (); + CGameTask* HasGameTask (const TASK_ID& id); + CGameTask* HasGameTask (const CMapLocation* ml, bool only_inprocess); + CGameTask* GiveGameTaskToActor (const TASK_ID& id, u32 timeToComplete, bool bCheckExisting=true); + CGameTask* GiveGameTaskToActor (CGameTask* t, u32 timeToComplete, bool bCheckExisting=true); + void SetTaskState (const TASK_ID& id, u16 objective_num, ETaskState state); + void SetTaskState (CGameTask* t, u16 objective_num, ETaskState state); + + void UpdateTasks (); +//. void RemoveUserTask (CMapLocation* ml); + + CGameTask* ActiveTask (); + SGameTaskObjective* ActiveObjective (); + void SetActiveTask (const TASK_ID& id, u16 idx); +}; diff --git a/src/xrGameLA/Geometry.cpp b/src/xrGameLA/Geometry.cpp new file mode 100644 index 000000000..45d2a58f6 --- /dev/null +++ b/src/xrGameLA/Geometry.cpp @@ -0,0 +1,664 @@ +#include "stdafx.h" +#include "Geometry.h" +#include "PHDynamicData.h" +#include "ExtendedGeom.h" +#include "dcylinder//dCylinder.h" + +#include "../bone.h" + +//global + + +static void computeFinalTx(dGeomID geom_transform,dReal* final_pos,dReal* final_R) +{ + R_ASSERT2(dGeomGetClass(geom_transform)==dGeomTransformClass,"is not a geom transform"); + dGeomID obj=dGeomTransformGetGeom(geom_transform); + const dReal *R =dGeomGetRotation(geom_transform); + const dReal *pos=dGeomGetPosition(geom_transform); + dMULTIPLY0_331 (final_pos,R,dGeomGetPosition(obj)); + final_pos[0] += pos[0]; + final_pos[1] += pos[1]; + final_pos[2] += pos[2]; + dMULTIPLY0_333 (final_R,R,dGeomGetRotation(obj)); +} + +void GetBoxExtensions(dGeomID box,const dReal* axis, + const dReal *pos, const dReal *rot, + float center_prg,dReal* lo_ext,dReal* hi_ext) +{ + R_ASSERT2(dGeomGetClass(box)==dBoxClass,"is not a box"); + dVector3 length; + dGeomBoxGetLengths(box,length); + dReal dif=dDOT(pos,axis)-center_prg; + dReal ful_ext=dFabs(dDOT14(axis,rot+0))*length[0] + +dFabs(dDOT14(axis,rot+1))*length[1] + +dFabs(dDOT14(axis,rot+2))*length[2]; + ful_ext/=2.f; + *lo_ext=-ful_ext+dif; + *hi_ext=ful_ext+dif; +} + +void GetCylinderExtensions(dGeomID cyl,const dReal* axis, + const dReal *pos, const dReal *rot, + float center_prg,dReal* lo_ext,dReal* hi_ext) +{ + R_ASSERT2(dGeomGetClass(cyl)==dCylinderClassUser,"is not a cylinder"); + dReal radius,length; + dGeomCylinderGetParams(cyl,&radius,&length); + dReal dif=dDOT(pos,axis)-center_prg; + dReal _cos=dFabs(dDOT14(axis,rot+1)); + dReal cos1=dDOT14(axis,rot+0); + dReal cos3=dDOT14(axis,rot+2); + dReal _sin=_sqrt(cos1*cos1+cos3*cos3); + length/=2.f; + dReal ful_ext=_cos*length+_sin*radius; + *lo_ext=-ful_ext+dif; + *hi_ext=ful_ext+dif; +} + +void GetSphereExtensions(dGeomID sphere,const dReal* axis, + const dReal *pos, + float center_prg,dReal* lo_ext,dReal* hi_ext) +{ + R_ASSERT2(dGeomGetClass(sphere)==dSphereClass,"is not a sphere"); + dReal radius=dGeomSphereGetRadius(sphere); + dReal dif=dDOT(pos,axis)-center_prg; + *lo_ext=-radius+dif; + *hi_ext=radius+dif; +} + +void TransformedGeometryExtensionLocalParams(dGeomID geom_transform,const dReal* axis,float center_prg,dReal* local_axis,dReal& local_center_prg) +{ + R_ASSERT2(dGeomGetClass(geom_transform)==dGeomTransformClass,"is not a geom transform"); + const dReal* rot=dGeomGetRotation(geom_transform); + const dReal* pos=dGeomGetPosition(geom_transform); + dVector3 local_pos; + + dMULTIPLY1_331(local_axis,rot,axis); + dMULTIPLY1_331(local_pos,rot,pos); + local_center_prg=center_prg-dDOT(local_pos,local_axis); +} + + + +CODEGeom::CODEGeom() +{ + m_geom_transform=NULL; + m_bone_id=u16(-1); +} + +CODEGeom::~CODEGeom() +{ + if(m_geom_transform) destroy(); +} + +void CODEGeom::get_mass(dMass& m,const Fvector& ref_point, float density) +{ + get_mass(m); + dMassAdjust(&m,density*volume()); + Fvector l; + l.sub(local_center(),ref_point); + dMassTranslate(&m,l.x,l.y,l.z); +} + +void CODEGeom::get_mass(dMass& m,const Fvector& ref_point) +{ + get_mass(m); + Fvector l; + l.sub(local_center(),ref_point); + dMassTranslate(&m,l.x,l.y,l.z); +} + +void CODEGeom::add_self_mass(dMass& m,const Fvector& ref_point, float density) +{ + dMass self_mass; + get_mass(self_mass,ref_point,density); + dMassAdd(&m,&self_mass); +} + +void CODEGeom::add_self_mass(dMass& m,const Fvector& ref_point) +{ + dMass self_mass; + get_mass(self_mass,ref_point); + dMassAdd(&m,&self_mass); +} + +void CODEGeom::get_local_center_bt(Fvector& center) +{ + if(! m_geom_transform) return; + if(!geom()) //geom is not transformed + { + center.set(0.f,0.f,0.f); + } + center.set(*((const Fvector*)dGeomGetPosition(geom()))); +} +void CODEGeom::get_local_form_bt(Fmatrix& form) +{ + PHDynamicData::DMXPStoFMX(dGeomGetRotation(geom()),dGeomGetPosition(geom()),form); +} +void CODEGeom::get_global_center_bt(Fvector& center) +{ + center.set(*((const Fvector*)dGeomGetPosition(m_geom_transform))); + dVector3 add; + dMULTIPLY0_331 (add,dGeomGetRotation(m_geom_transform),dGeomGetPosition(geom())); + center.x += add[0]; + center.y += add[1]; + center.z += add[2]; +} +void CODEGeom::get_global_form_bt(Fmatrix& form) +{ + dMULTIPLY0_331 ((dReal*)(&form.c),dGeomGetRotation(m_geom_transform),dGeomGetPosition(geom())); + form.c.add(*((const Fvector*)dGeomGetPosition(m_geom_transform))); + dMULTIPLY3_333 ((dReal*)(&form),dGeomGetRotation(m_geom_transform),dGeomGetRotation(geom())); + //PHDynamicData::DMXtoFMX((dReal*)(&form),form); +} + +void CODEGeom::set_static_ref_form(const Fmatrix& form) +{ + dGeomSetPosition(geometry_transform(),form.c.x,form.c.y,form.c.z); + Fmatrix33 m33; + m33.set(form); + dMatrix3 R; + PHDynamicData::FMX33toDMX(m33,R); + dGeomSetRotation(geometry_transform(),R); +} + +void CODEGeom::set_position(const Fvector& /*ref_point*/) +{ + dGeomUserDataResetLastPos(geom()); +} + +void CODEGeom::set_body(dBodyID body) +{ + if(m_geom_transform) dGeomSetBody(m_geom_transform,body); +} + +void CODEGeom::add_to_space(dSpaceID space) +{ + if(m_geom_transform) dSpaceAdd(space,m_geom_transform); +} +void CODEGeom::remove_from_space(dSpaceID space) +{ + if(m_geom_transform) dSpaceRemove(space,m_geom_transform); +} +void CODEGeom::clear_cashed_tries() +{ + if(!m_geom_transform)return; + dGeomID g=geom(); + if(g) + { + VERIFY(dGeomGetUserData(g)); + dGeomUserDataClearCashedTries(g); + } + else + { + VERIFY(dGeomGetUserData(m_geom_transform)); + dGeomUserDataClearCashedTries(m_geom_transform); + } +} +void CODEGeom::set_material(u16 ul_material) +{ + if(!m_geom_transform) return; + if(geom()) + { + VERIFY(dGeomGetUserData(geom())); + dGeomGetUserData(geom())->material=ul_material; + } + else + { + VERIFY(dGeomGetUserData(m_geom_transform)); + dGeomGetUserData(m_geom_transform)->material=ul_material; + } +} + +void CODEGeom::set_contact_cb(ContactCallbackFun* ccb) +{ + if(!m_geom_transform) return; + if(geom()) + { + VERIFY(dGeomGetUserData(geom())); + dGeomUserDataSetContactCallback(geom(),ccb); + } + else + { + VERIFY(dGeomGetUserData(m_geom_transform)); + dGeomUserDataSetContactCallback(m_geom_transform,ccb); + } +} + +void CODEGeom::set_obj_contact_cb(ObjectContactCallbackFun* occb) +{ + if(!m_geom_transform) return; + if(geom()) + { + VERIFY(dGeomGetUserData(geom())); + dGeomUserDataSetObjectContactCallback(geom(),occb); + } + else + { + VERIFY(dGeomGetUserData(m_geom_transform)); + dGeomUserDataSetObjectContactCallback(m_geom_transform,occb); + } +} +void CODEGeom::add_obj_contact_cb(ObjectContactCallbackFun* occb) +{ + if(!m_geom_transform) return; + if(geom()) + { + VERIFY(dGeomGetUserData(geom())); + dGeomUserDataAddObjectContactCallback(geom(),occb); + } + else + { + VERIFY(dGeomGetUserData(m_geom_transform)); + dGeomUserDataAddObjectContactCallback(m_geom_transform,occb); + } +} +void CODEGeom::remove_obj_contact_cb(ObjectContactCallbackFun* occb) +{ + if(!m_geom_transform) return; + if(geom()) + { + VERIFY(dGeomGetUserData(geom())); + dGeomUserDataRemoveObjectContactCallback(geom(),occb); + } + else + { + VERIFY(dGeomGetUserData(m_geom_transform)); + dGeomUserDataRemoveObjectContactCallback(m_geom_transform,occb); + } +} +void CODEGeom::set_callback_data(void *cd) +{ + if(!m_geom_transform) return; + if(geom()) + { + VERIFY(dGeomGetUserData(geom())); + dGeomUserDataSetCallbackData(geom(),cd); + } + else + { + VERIFY(dGeomGetUserData(m_geom_transform)); + dGeomUserDataSetCallbackData(m_geom_transform,cd); + } +} +void* CODEGeom::get_callback_data() +{ + if(!m_geom_transform) return NULL; + if(geom()) + { + VERIFY(dGeomGetUserData(geom())); + return dGeomGetUserData(geom())->callback_data; + } + else + { + VERIFY(dGeomGetUserData(m_geom_transform)); + return dGeomGetUserData(m_geom_transform)->callback_data; + } +} +void CODEGeom::set_ref_object(CPhysicsShellHolder* ro) +{ + if(!m_geom_transform) return; + if(geom()) + { + VERIFY(dGeomGetUserData(geom())); + dGeomUserDataSetPhysicsRefObject(geom(),ro); + } + else + { + VERIFY(dGeomGetUserData(m_geom_transform)); + dGeomUserDataSetPhysicsRefObject(m_geom_transform,ro); + } +} + +void CODEGeom::set_ph_object(CPHObject* o) +{ + if(!m_geom_transform) return; + if(geom()) + { + VERIFY(dGeomGetUserData(geom())); + dGeomGetUserData(geom())->ph_object=o; + } + else + { + VERIFY(dGeomGetUserData(m_geom_transform)); + dGeomGetUserData(m_geom_transform)->ph_object=o; + } +} +void CODEGeom::move_local_basis(const Fmatrix& inv_new_mul_old) +{ + Fmatrix new_form; + get_local_form (new_form); + new_form.mulA_43 (inv_new_mul_old); + set_local_form (new_form); +} +void CODEGeom::build(const Fvector& ref_point) +{ + init(); + set_position(ref_point); +} +void CODEGeom::init() +{ + dGeomID geom=create(); + m_geom_transform=dCreateGeomTransform(0); + dGeomTransformSetCleanup(m_geom_transform,0); + dGeomSetData(m_geom_transform,0); + dGeomTransformSetGeom(m_geom_transform,geom); + dGeomTransformSetInfo(m_geom_transform,1); + dGeomCreateUserData(geom); + dGeomUserDataSetBoneId(geom,m_bone_id); +} +void CODEGeom::destroy() +{ + if(!m_geom_transform) return; + if(geom()) + { + dGeomDestroyUserData(geom()); + dGeomDestroy(geom()); + dGeomTransformSetGeom(m_geom_transform,0); + } + dGeomDestroyUserData(m_geom_transform); + dGeomDestroy(m_geom_transform); + m_geom_transform=NULL; +} + +CBoxGeom::CBoxGeom(const Fobb& box) +{ + m_box=box; +} + +void CBoxGeom::get_mass(dMass& m) +{ + Fvector& hside=m_box.m_halfsize; + dMassSetBox(&m,1.f,hside.x*2.f,hside.y*2.f,hside.z*2.f); + dMatrix3 DMatx; + PHDynamicData::FMX33toDMX(m_box.m_rotate,DMatx); + dMassRotate(&m,DMatx); +} + +float CBoxGeom::volume() +{ + return m_box.m_halfsize.x*m_box.m_halfsize.y*m_box.m_halfsize.z*8.f; +} + +float CBoxGeom::radius() +{ + return m_box.m_halfsize.x; +} +void CODEGeom::get_final_tx_bt(const dReal* &p, const dReal* &R,dReal *bufV, dReal *bufM) +{ + VERIFY(m_geom_transform); + //dGeomID g = geometry_bt() ; + get_final_tx (m_geom_transform,p,R,bufV,bufM) ; + +} +void CODEGeom::get_final_tx(dGeomID g,const dReal* &p,const dReal* &R,dReal * bufV, dReal* bufM) +{ + if(is_transform(g)) + { + computeFinalTx(g,bufV,bufM); + R=bufM;p=bufV; + }else + { + R=dGeomGetRotation(g); + p=dGeomGetPosition(g); + } +} +void CBoxGeom::get_extensions_bt(const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) +{ + + VERIFY (m_geom_transform) ; + const dReal *rot =NULL ; + const dReal *pos =NULL ; + dVector3 p ; + dMatrix3 r ; + dGeomID g =geometry_bt() ; + get_final_tx_bt(pos,rot,p,r) ; + GetBoxExtensions(g,cast_fp(axis),pos,rot,center_prg,&lo_ext,&hi_ext); +} + +void CBoxGeom::get_max_area_dir_bt(Fvector& dir) +{ + dVector3 length,ddir; + dGeomBoxGetLengths (geometry(),length); + dReal S1=length[0]*length[1],S2=length[0]*length[2],S3=length[1]*length[2]; + const dReal* R= dGeomGetRotation(geometry()); + if(S1>S2) + { + if(S1>S3) + { + ddir[0]=R[2];ddir[1]=R[6];ddir[2]=R[10];//S1 + } + else + { + ddir[0]=R[0];ddir[1]=R[4];ddir[2]=R[8];//S3 + } + } + else + { + if(S2>S3) + { + ddir[0]=R[1];ddir[1]=R[5];ddir[2]=R[9];//S2 + } + else + { + ddir[0]=R[0];ddir[1]=R[4];ddir[2]=R[8];//S3 + } + } + + if(geom()) + { + const dReal* TR=dGeomGetRotation(geometry_transform()); + dir.x=dDOT(ddir,TR); + dir.y=dDOT(ddir,TR+4); + dir.z=dDOT(ddir,TR+8); + } + else + { + dir.x=ddir[0]; + dir.y=ddir[1]; + dir.z=ddir[2]; + } +} + +const Fvector& CBoxGeom::local_center() +{ + return m_box.m_translate; +} + +void CBoxGeom::get_local_form(Fmatrix& form) +{ + form._14=0; + form._24=0; + form._34=0; + form._44=1; + form.i.set(m_box.m_rotate.i); + form.j.set(m_box.m_rotate.j); + form.k.set(m_box.m_rotate.k); + form.c.set(m_box.m_translate); +} +void CBoxGeom::set_local_form(const Fmatrix& form) +{ + m_box.m_rotate.i.set(form.i); + m_box.m_rotate.j.set(form.j); + m_box.m_rotate.k.set(form.k); + m_box.m_translate.set(form.c); +} +dGeomID CBoxGeom::create() +{ + +return dCreateBox(0, + m_box.m_halfsize.x*2.f, + m_box.m_halfsize.y*2.f, + m_box.m_halfsize.z*2.f + ); +} + +void CBoxGeom::set_position(const Fvector& ref_point) +{ + + inherited::set_position(ref_point); + + dVector3 local_position={m_box.m_translate.x-ref_point.x, + m_box.m_translate.y-ref_point.y, + m_box.m_translate.z-ref_point.z + }; + dGeomSetPosition(geom(), + local_position[0], + local_position[1], + local_position[2] + ); + dMatrix3 R; + PHDynamicData::FMX33toDMX(m_box.m_rotate,R); + dGeomSetRotation(geom(),R); +} + +CSphereGeom::CSphereGeom(const Fsphere& sphere) +{ + m_sphere=sphere; +} +void CSphereGeom::get_mass(dMass& m) +{ + dMassSetSphere(&m,1.f,m_sphere.R); +} + +float CSphereGeom::volume() +{ + return 4.f*M_PI*m_sphere.R*m_sphere.R*m_sphere.R/3.f; +} + +float CSphereGeom::radius() +{ + return m_sphere.R; +} + +void CSphereGeom::get_extensions_bt(const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) +{ + VERIFY (m_geom_transform) ; + const dReal *rot =NULL ; + const dReal *pos =NULL ; + dVector3 p ; + dMatrix3 r ; + dGeomID g =geometry_bt() ; + get_final_tx_bt(pos,rot,p,r) ; + GetSphereExtensions(g,cast_fp(axis),pos,center_prg,&lo_ext,&hi_ext); +} +const Fvector& CSphereGeom::local_center() +{ + return m_sphere.P; +} + +void CSphereGeom::get_local_form(Fmatrix& form) +{ + form.identity(); + form.c.set(m_sphere.P); +} +void CSphereGeom::set_local_form(const Fmatrix& form) +{ + m_sphere.P.set(form.c); +} +dGeomID CSphereGeom::create() +{ + return dCreateSphere(0,m_sphere.R); +} + +void CSphereGeom::set_position(const Fvector& ref_point) +{ + + inherited::set_position(ref_point); + dVector3 local_position={ + m_sphere.P.x-ref_point.x, + m_sphere.P.y-ref_point.y, + m_sphere.P.z-ref_point.z + }; + + dGeomSetPosition(geom(),local_position[0],local_position[1],local_position[2]); +} + +CCylinderGeom::CCylinderGeom(const Fcylinder& cyl) +{ + m_cylinder=cyl; +} +void CCylinderGeom::get_mass(dMass& m) +{ + dMassSetCylinder(&m,1.f,2,m_cylinder.m_radius,m_cylinder.m_height); + dMatrix3 DMatx; + Fmatrix33 m33; + m33.j.set(m_cylinder.m_direction); + Fvector::generate_orthonormal_basis(m33.j,m33.k,m33.i); + PHDynamicData::FMX33toDMX(m33,DMatx); + dMassRotate(&m,DMatx); +} + +float CCylinderGeom::volume() +{ + return M_PI*m_cylinder.m_radius*m_cylinder.m_radius*m_cylinder.m_height; +} + +float CCylinderGeom::radius() +{ + return m_cylinder.m_radius; +} + +void CCylinderGeom::get_extensions_bt(const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) +{ + VERIFY (m_geom_transform) ; + const dReal *rot =NULL ; + const dReal *pos =NULL ; + dVector3 p ; + dMatrix3 r ; + dGeomID g =geometry_bt() ; + get_final_tx_bt(pos,rot,p,r) ; + GetCylinderExtensions(g,cast_fp(axis),pos,rot,center_prg,&lo_ext,&hi_ext); +} +const Fvector& CCylinderGeom::local_center() +{ + return m_cylinder.m_center; +} + +void CCylinderGeom::get_local_form(Fmatrix& form) +{ + form._14=0; + form._24=0; + form._34=0; + form._44=1; + form.j.set(m_cylinder.m_direction); + Fvector::generate_orthonormal_basis(form.j,form.k,form.i); + form.c.set(m_cylinder.m_center); +} +void CCylinderGeom::set_local_form(const Fmatrix& form) +{ + m_cylinder.m_center.set(form.c); + m_cylinder.m_direction.set(form.j); +} +dGeomID CCylinderGeom::create() +{ +return dCreateCylinder( + 0, + m_cylinder.m_radius, + m_cylinder.m_height + ); +} +void CCylinderGeom::set_position(const Fvector& ref_point) +{ + + inherited::set_position(ref_point); + dVector3 local_position={ + m_cylinder.m_center.x-ref_point.x, + m_cylinder.m_center.y-ref_point.y, + m_cylinder.m_center.z-ref_point.z + }; + + dGeomSetPosition( + geom(), + local_position[0], + local_position[1], + local_position[2] + ); + dMatrix3 R; + Fmatrix33 m33; + m33.j.set(m_cylinder.m_direction); + Fvector::generate_orthonormal_basis(m33.j,m33.k,m33.i); + PHDynamicData::FMX33toDMX(m33,R); + dGeomSetRotation(geom(),R); +} \ No newline at end of file diff --git a/src/xrGameLA/Geometry.h b/src/xrGameLA/Geometry.h new file mode 100644 index 000000000..5b4577de6 --- /dev/null +++ b/src/xrGameLA/Geometry.h @@ -0,0 +1,168 @@ +#ifndef GEOMETRY_H +#define GEOMETRY_H +#include "PhysicsCommon.h" +#include "ExtendedGeom.h" + +//this is equivalent dMULTIPLYOP0_333 whith consequent transposion of A +#define dMULTIPLYOP3_333(A,op,B,C) \ + (A)[0] op dDOT14((B), (C)); \ + (A)[1] op dDOT14((B+4),(C)); \ + (A)[2] op dDOT14((B+8),(C)); \ + (A)[4] op dDOT14((B), (C+1)); \ + (A)[5] op dDOT14((B+4),(C+1)); \ + (A)[6] op dDOT14((B+8),(C+1)); \ + (A)[8] op dDOT14((B), (C+2)); \ + (A)[9] op dDOT14((B+4),(C+2)); \ + (A)[10] op dDOT14((B+8),(C+2)); + +inline void dMULTIPLY3_333(dReal *A, const dReal *B, const dReal *C) +{ dMULTIPLYOP3_333(A,=,B,C) } + + +class CGameObject; +class CPHObject; +class CODEGeom +{ +protected: + dGeomID m_geom_transform; + u16 m_bone_id; +protected: + + +public: + //get + virtual float volume () =0; + virtual void get_mass (dMass& m) =0; //unit dencity mass; + void get_mass (dMass& m,const Fvector& ref_point, float density) ; + void get_mass (dMass& m,const Fvector& ref_point) ; + void add_self_mass (dMass& m,const Fvector& ref_point) ; + void add_self_mass (dMass& m,const Fvector& ref_point, float density) ; + void get_local_center_bt (Fvector& center) ; //for built + void get_global_center_bt(Fvector& center) ; //for built + void get_local_form_bt (Fmatrix& form) ; //for built + void get_global_form_bt (Fmatrix& form) ; //for built + + void set_static_ref_form (const Fmatrix& form) ; //for built + virtual void get_max_area_dir_bt (Fvector& dir) =0; + virtual float radius () =0; + virtual void get_extensions_bt (const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) =0; + void clear_cashed_tries () ; + IC dGeomID geom() + { + return dGeomTransformGetGeom(m_geom_transform); + } + IC dGeomID geometry_transform () + { + return m_geom_transform; + } + IC dGeomID geometry() + { + return m_geom_transform ? (geom() ? geom() : m_geom_transform) : NULL; + } + IC dGeomID geometry_bt() + { + if(is_transformed_bt()) return geom() ; + else return geometry_transform(); + + } + ICF static bool is_transform(dGeomID g) + { + return dGeomGetClass(g)==dGeomTransformClass; + } + IC bool is_transformed_bt() + { + return is_transform(m_geom_transform); + } + IC u16& element_position() + { + return dGeomGetUserData(geometry())->element_position; + } +virtual const Fvector& local_center () =0; +virtual void get_local_form (Fmatrix& form) =0; +virtual void set_local_form (const Fmatrix& form) =0; + //set + //element part + void set_body (dBodyID body) ; + void set_bone_id (u16 id) {m_bone_id=id;} + u16 bone_id () {return m_bone_id;} + void add_to_space (dSpaceID space) ; + void remove_from_space (dSpaceID space) ; + void set_material (u16 ul_material) ; + void set_contact_cb (ContactCallbackFun* ccb) ; + void set_obj_contact_cb (ObjectContactCallbackFun* occb) ; + void add_obj_contact_cb (ObjectContactCallbackFun* occb) ; + void remove_obj_contact_cb(ObjectContactCallbackFun* occb) ; + void set_callback_data (void *cd) ; + void *get_callback_data () ; + void set_ref_object (CPhysicsShellHolder* ro) ; + void set_ph_object (CPHObject* o) ; + + //build/destroy +protected: + void init () ; + void get_final_tx_bt (const dReal* &p,const dReal* &R,dReal * bufV, dReal* bufM) ; + virtual dGeomID create () =0; +public: + static void get_final_tx (dGeomID g,const dReal* &p,const dReal* &R,dReal * bufV, dReal* bufM); + void build (const Fvector& ref_point) ; + virtual void set_position (const Fvector& ref_point) ;//for build geom + void move_local_basis (const Fmatrix& inv_new_mul_old) ; + void destroy () ; + CODEGeom () ; + virtual ~ CODEGeom () ; +}; + +class CBoxGeom : public CODEGeom +{ + typedef CODEGeom inherited ; + Fobb m_box; +public: + CBoxGeom (const Fobb& box) ; +// virtual ~CBoxGeom (const Fobb& box) ; + virtual float volume () ; + virtual float radius () ; + virtual void get_extensions_bt (const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) ; + virtual void get_max_area_dir_bt (Fvector& dir) ; + virtual void get_mass (dMass& m) ;//unit dencity mass; +virtual const Fvector& local_center () ; + virtual void get_local_form (Fmatrix& form) ; +virtual void set_local_form (const Fmatrix& form) ; + virtual dGeomID create () ; + virtual void set_position (const Fvector& ref_point) ; +}; + +class CSphereGeom : public CODEGeom +{ + typedef CODEGeom inherited ; + Fsphere m_sphere; +public: + CSphereGeom (const Fsphere& sphere) ; + virtual float volume () ; + virtual float radius () ; + virtual void get_extensions_bt (const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) ; + virtual void get_max_area_dir_bt (Fvector& dir) {}; + virtual void get_mass (dMass& m) ;//unit dencity mass; +virtual const Fvector& local_center () ; + virtual void get_local_form (Fmatrix& form) ; + virtual void set_local_form (const Fmatrix& form) ; + virtual dGeomID create () ; + virtual void set_position (const Fvector& ref_point) ; +}; +class CCylinderGeom : public CODEGeom +{ + typedef CODEGeom inherited ; + Fcylinder m_cylinder; +public: + CCylinderGeom (const Fcylinder& cyl) ; + virtual float volume () ; + virtual float radius () ; + virtual void get_extensions_bt (const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) ; + virtual void get_max_area_dir_bt (Fvector& dir) {}; +virtual const Fvector& local_center () ; + virtual void get_mass (dMass& m) ;//unit dencity mass; + virtual void get_local_form (Fmatrix& form) ; + virtual void set_local_form (const Fmatrix& form) ; + virtual dGeomID create () ; + virtual void set_position (const Fvector& ref_point) ; +}; +#endif //GEOMETRY_H \ No newline at end of file diff --git a/src/xrGameLA/GraviArtifact.cpp b/src/xrGameLA/GraviArtifact.cpp new file mode 100644 index 000000000..c6bbd752b --- /dev/null +++ b/src/xrGameLA/GraviArtifact.cpp @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////// +// GraviArtifact.cpp +// GraviArtefact - гравитационный артефакт, прыгает на месте +// и неустойчиво парит над землей +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "GraviArtifact.h" +#include "PhysicsShell.h" +#include "level.h" +#include "xrmessages.h" +#include "game_cl_base.h" +#include "../../Include/xrRender/Kinematics.h" +#include "phworld.h" +extern CPHWorld* ph_world; +#define CHOOSE_MAX(x,inst_x,y,inst_y,z,inst_z)\ + if(x>y)\ + if(x>z){inst_x;}\ + else{inst_z;}\ + else\ + if(y>z){inst_y;}\ + else{inst_z;} + + +CGraviArtefact::CGraviArtefact(void) +{ + shedule.t_min = 20; + shedule.t_max = 50; + + m_fJumpHeight = 0; + m_fEnergy = 1.f; +} + +CGraviArtefact::~CGraviArtefact(void) +{ +} + +void CGraviArtefact::Load(LPCSTR section) +{ + inherited::Load(section); + + if(pSettings->line_exist(section, "jump_height")) m_fJumpHeight = pSettings->r_float(section,"jump_height"); +// m_fEnergy = pSettings->r_float(section,"energy"); +} + + + +void CGraviArtefact::UpdateCLChild() +{ + + VERIFY(!ph_world->Processing()); + if (getVisible() && m_pPhysicsShell) { + if (m_fJumpHeight) { + Fvector dir; + dir.set(0, -1.f, 0); + collide::rq_result RQ; + + //проверить высоту артифакта + if(Level().ObjectSpace.RayPick(Position(), dir, m_fJumpHeight, collide::rqtBoth, RQ, this)) + { + dir.y = 1.f; + m_pPhysicsShell->applyImpulse(dir, + 30.f * Device.fTimeDelta * + m_pPhysicsShell->getMass()); + } + } + } else + if(H_Parent()) + { + XFORM().set(H_Parent()->XFORM()); + }; +} \ No newline at end of file diff --git a/src/xrGameLA/GraviArtifact.h b/src/xrGameLA/GraviArtifact.h new file mode 100644 index 000000000..fe1a9a0ec --- /dev/null +++ b/src/xrGameLA/GraviArtifact.h @@ -0,0 +1,25 @@ +/////////////////////////////////////////////////////////////// +// GraviArtifact.h +// GraviArtefact - гравитационный артефакт, прыгает на месте +// и парит над землей +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CGraviArtefact : public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CGraviArtefact(void); + virtual ~CGraviArtefact(void); + + virtual void Load (LPCSTR section); + +protected: + virtual void UpdateCLChild (); + //параметры артефакта + float m_fJumpHeight; + float m_fEnergy; +}; diff --git a/src/xrGameLA/GraviZone.cpp b/src/xrGameLA/GraviZone.cpp new file mode 100644 index 000000000..6ce9cc243 --- /dev/null +++ b/src/xrGameLA/GraviZone.cpp @@ -0,0 +1,324 @@ +////////////////////////////////////////////////////////////////////////// +// GraviZone.cpp: гравитационная аномалия +////////////////////////////////////////////////////////////////////////// +// состоит как бы из 2х зон +// одна затягивает объект, другая взрывает и +// все неживые объекты (предметы и трупы) +// поднимает в воздух и качает там какое-то +// время +////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "gravizone.h" + +#include "PhysicsShell.h" +#include "entity_alive.h" +#include "phmovementcontrol.h" +#include "xrmessages.h" +#include "PhysicsShellHolder.h" +#include "Level.h" +#include "CharacterPhysicsSupport.h" +#include "Car.h" + +#define ALIVE_PULL_FORCE_MULT (60.f) + +CBaseGraviZone ::CBaseGraviZone (void) +{ + m_dwTeleTime = 0; +} + +CBaseGraviZone ::~CBaseGraviZone (void) +{ +} + +void CBaseGraviZone ::Load(LPCSTR section) +{ + inherited::Load(section); + + + m_fThrowInImpulse = pSettings->r_float(section, "throw_in_impulse");//800.f; + m_fThrowInImpulseAlive = pSettings->r_float(section, "throw_in_impulse_alive");//800.f; + m_fThrowInImpulseCar = READ_IF_EXISTS(pSettings, r_float, section, "throw_in_impulse_car", m_fThrowInImpulseAlive); + m_fThrowInAtten = pSettings->r_float(section, "throw_in_atten"); + m_fThrowInHitKoefAlive = READ_IF_EXISTS(pSettings, r_float, section, "throw_in_hit_koef_alive", 1.0f); + m_fThrowInHitKoefCar = READ_IF_EXISTS(pSettings, r_float, section, "throw_in_hit_koef_car", 1.0f); + m_fBlowoutRadiusPercent = pSettings->r_float(section, "blowout_radius_percent");//0.3f; + + m_fTeleHeight = pSettings->r_float(section, "tele_height");//1.5f; + m_dwTimeToTele = pSettings->r_u32(section, "time_to_tele");//7000; + m_dwTelePause = pSettings->r_u32(section, "tele_pause");//1000 + + if(pSettings->line_exist(section, "tele_particles_big")) + m_sTeleParticlesBig = pSettings->r_string(section, "tele_particles_big"); + else + m_sTeleParticlesBig = NULL; + + if(pSettings->line_exist(section, "tele_particles_small")) + m_sTeleParticlesSmall = pSettings->r_string(section, "tele_particles_small"); + else + m_sTeleParticlesSmall = NULL; +} + +BOOL CBaseGraviZone ::net_Spawn(CSE_Abstract* DC) +{ + return inherited::net_Spawn(DC); +} + +void CBaseGraviZone ::net_Destroy() +{ + Telekinesis().deactivate(); + inherited::net_Destroy(); +} + +void CBaseGraviZone ::shedule_Update (u32 dt) +{ + inherited::shedule_Update(dt); + Telekinesis().schedule_update(); +} + + +bool CBaseGraviZone ::BlowoutState() +{ + bool result = inherited::BlowoutState(); + + UpdateBlowout(); + AffectObjects(); + + return result; +} + + +bool CBaseGraviZone ::IdleState() +{ + bool result = inherited::IdleState(); + + m_dwTeleTime += Device.dwTimeDelta; + + if(!result) + { + if(m_dwTeleTime> m_dwTimeToTele) + { + for(OBJECT_INFO_VEC_IT it = m_ObjectInfoMap.begin(); m_ObjectInfoMap.end() != it; ++it) + { + CPhysicsShellHolder * GO = smart_cast( (*it).object ); + + if(GO && GO->PPhysicsShell() && Telekinesis().is_active_object(GO)) + { + Telekinesis().deactivate(GO); + StopTeleParticles(GO); + } + } + } + if(m_dwTeleTime> m_dwTimeToTele + m_dwTelePause) + { + m_dwTeleTime = 0; + + for(OBJECT_INFO_VEC_IT it = m_ObjectInfoMap.begin(); m_ObjectInfoMap.end() != it; ++it) + { + CPhysicsShellHolder * GO = smart_cast( (*it).object ); + + if(GO && GO->PPhysicsShell() && !Telekinesis().is_active_object(GO)) + { + Telekinesis().activate(GO, 0.1f, m_fTeleHeight, m_dwTimeToTele); + PlayTeleParticles(GO); + } + } + } + } + else + Telekinesis().deactivate(); + + return result; +} + +bool CBaseGraviZone::CheckAffectField(CPhysicsShellHolder* GO,float dist_to_radius) +{ + return dist_to_radius>BlowoutRadiusPercent(GO); +} + +void CBaseGraviZone ::Affect(SZoneObjectInfo* O) +{ + CPhysicsShellHolder* GO = smart_cast(O->object); + if(!GO) return; + + + ////////////////////////////////////////////////////////////////////////// + // затягиваем объет по направлению к центру зоны + + Fvector throw_in_dir; + Fvector zone_center; + ThrowInCenter (zone_center); + Fvector go_center; + GO->Center (go_center); + throw_in_dir.sub (zone_center, go_center); + + float dist = throw_in_dir.magnitude(); + float dist_to_radius = dist/Radius(); + + if(!fis_zero(dist)) + { + throw_in_dir.mul(1.f/dist); + } + else throw_in_dir.set(0.f,1.f,0.f); + + bool CanApplyPhisImpulse = GO->Local() == TRUE; + + if( CheckAffectField(GO,dist_to_radius)&& CanApplyPhisImpulse) + { + AffectPull(GO,throw_in_dir,dist); + } + else + { + ////////////////////////////////////////////////////////////////////////// + // выброс аномалии + + //если время выброса еще не пришло + if(m_dwBlowoutExplosionTime<(u32)m_iPreviousStateTime || + m_dwBlowoutExplosionTime>=(u32)m_iStateTime) + { + + AffectPull(GO,throw_in_dir,BlowoutRadiusPercent(GO)*Radius()); + return; + } + AffectThrow(O,GO,throw_in_dir,dist); + + } +} + +void CBaseGraviZone :: ThrowInCenter(Fvector& C) +{ + Center(C); +} +void CBaseGraviZone :: AffectPull(CPhysicsShellHolder* GO,const Fvector& throw_in_dir,float dist) +{ + CEntityAlive* EA = smart_cast(GO); + if(EA && EA->g_Alive()) + { + AffectPullAlife(EA,throw_in_dir,dist); + } + else if(GO && GO->PPhysicsShell()) + { + AffectPullDead(GO,throw_in_dir,dist); + } +} + +void CBaseGraviZone :: AffectPullAlife(CEntityAlive* EA,const Fvector& throw_in_dir,float dist) +{ + float rel_power = RelativePower(dist); + float throw_power = m_fThrowInImpulseAlive * GetPullDeltaPower() * ALIVE_PULL_FORCE_MULT * rel_power * rel_power * rel_power * rel_power * rel_power; + + Fvector vel; + vel.set(throw_in_dir); + vel.mul(throw_power); + + EA->character_physics_support()->movement()->AddControlVel(vel); +} + +void CBaseGraviZone :: AffectPullDead(CPhysicsShellHolder* GO,const Fvector& throw_in_dir,float dist) +{ + GO->PPhysicsShell()->applyImpulse(throw_in_dir,dist * m_fThrowInImpulse*GO->GetMass()/100.f); +} + +void CBaseGraviZone :: AffectThrow(SZoneObjectInfo* O, CPhysicsShellHolder* GO,const Fvector& throw_in_dir,float dist) +{ + + Fvector position_in_bone_space; + + float power = Power(dist);//Power(GO->Position().distance_to(zone_center)); + float impulse = m_fHitImpulseScale*power*GO->GetMass(); + + //статистика по объекту + O->total_damage += power; + O->hit_num++; + + if(power > 0.01f) + { + m_dwDeltaTime = 0; + position_in_bone_space.set(0.f,0.f,0.f); + CreateHit(GO->ID(),ID(),throw_in_dir,power,0,position_in_bone_space,impulse,m_eHitTypeBlowout); + PlayHitParticles(GO); + } +} + + +void CBaseGraviZone ::PlayTeleParticles(CGameObject* pObject) +{ + CParticlesPlayer* PP = smart_cast(pObject); + if(!PP) return; + + shared_str particle_str = NULL; + + //разные партиклы для объектов разного размера + if(pObject->Radius()StartParticles(particle_str, Fvector().set(0,1,0), ID()); +} +void CBaseGraviZone ::StopTeleParticles(CGameObject* pObject) +{ + CParticlesPlayer* PP = smart_cast(pObject); + if(!PP) return; + shared_str particle_str = NULL; + + //разные партиклы для объектов разного размера + if(pObject->Radius()StopParticles (particle_str, BI_NONE, true); +} + +float CBaseGraviZone ::RelativePower(float dist) +{ + float radius = Radius(); +// if(dist>radius*m_fBlowoutRadiusPercent) return 0.f; + + radius = Radius()*m_fThrowInAtten; + float power = radius < dist ? 0 : (1.f - m_fAttenuation*(dist/radius)*(dist/radius)); + return power < 0 ? 0 : power; +} + +void CBaseGraviZone::net_Relcase(CObject* O) +{ + inherited::net_Relcase(O); + + Telekinesis().remove_links(O); +} + +void CBaseGraviZone::exit_Zone(SZoneObjectInfo& io) +{ + CPhysicsShellHolder * GO = smart_cast(io.object ); + + if(GO && GO->PPhysicsShell() && Telekinesis().is_active_object(GO)) + { + Telekinesis().deactivate(GO); + StopTeleParticles(GO); + } + + inherited::exit_Zone(io); +} + +bool CBaseGraviZone::ShouldIgnoreObject(CGameObject* pObject) +{ + auto pCar = smart_cast(pObject); + if (pCar) return false; + + return inherited::ShouldIgnoreObject(pObject); +} diff --git a/src/xrGameLA/GraviZone.h b/src/xrGameLA/GraviZone.h new file mode 100644 index 000000000..431981537 --- /dev/null +++ b/src/xrGameLA/GraviZone.h @@ -0,0 +1,97 @@ +////////////////////////////////////////////////////////////////////////// +// GraviZone.h: гравитационная аномалия +////////////////////////////////////////////////////////////////////////// +// состоит как бы из 2х зон +// одна затягивает объект, другая взрывает и +// все неживые объекты (предметы и трупы) +// поднимает в воздух и качает там какое-то +// время +////////////////////////////////////////////////////////////////////////// + +#pragma once +#include "customzone.h" +#include "ai/monsters/telekinesis.h" + + + +class CBaseGraviZone : public CCustomZone +{ +private: + typedef CCustomZone inherited; + +public: + CBaseGraviZone(void); + virtual ~CBaseGraviZone(void); + + virtual void Load (LPCSTR section); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Relcase (CObject* O); + + + //воздействие зоной на объект + virtual void Affect(SZoneObjectInfo* O); + virtual void AffectPull(CPhysicsShellHolder* GO,const Fvector& throw_in_dir,float dist); + virtual void AffectPullAlife(CEntityAlive* EA,const Fvector& throw_in_dir,float dist); + virtual void AffectPullDead(CPhysicsShellHolder* GO,const Fvector& throw_in_dir,float dist); + virtual void AffectThrow(SZoneObjectInfo* O, CPhysicsShellHolder* GO,const Fvector& throw_in_dir,float dist); + virtual void ThrowInCenter(Fvector& C); + virtual bool CheckAffectField(CPhysicsShellHolder* GO,float dist_to_radius); + virtual void shedule_Update (u32 dt); + virtual bool BlowoutState(); + virtual bool IdleState(); + + virtual float RelativePower(float dist); + virtual float BlowoutRadiusPercent(CPhysicsShellHolder* /*GO*/){return m_fBlowoutRadiusPercent;} + +protected: + virtual void exit_Zone (SZoneObjectInfo& io); + // pull power relative to current delta time, for FPS-agnostic damage/impulse + virtual float GetPullDeltaPower() { return (m_iStateTime - m_iPreviousStateTime) / 1000.f; } + + bool ShouldIgnoreObject(CGameObject*) override; + +protected: + virtual CTelekinesis& Telekinesis() =0; +protected: + //сила импульса втягивания в зону (для веса 100 кг) + float m_fThrowInImpulse; + //сила импульса втягивания в зону для живых существ + float m_fThrowInImpulseAlive; + //сила импульса втягивания в зону для машин + float m_fThrowInImpulseCar; + //коэфф. затягивания (чем меньше, тем плавнее затягивает) + float m_fThrowInAtten; + //коэфф. хита при затягивании живых существ (домножается на основной хит выбрасывания) + float m_fThrowInHitKoefAlive; + //коэфф. хита при затягивании машин + float m_fThrowInHitKoefCar; + //радиус действия выброса (в процентах от всего) + float m_fBlowoutRadiusPercent; + + + //параметры телекинеза + float m_fTeleHeight; + u32 m_dwTimeToTele; + u32 m_dwTelePause; + u32 m_dwTeleTime; + + //имя партиклов телекинеза + void PlayTeleParticles(CGameObject* pObject); + void StopTeleParticles(CGameObject* pObject); + + shared_str m_sTeleParticlesBig; + shared_str m_sTeleParticlesSmall; +}; + +class CGraviZone : public CBaseGraviZone +{ + typedef CBaseGraviZone inherited; + CTelekinesis m_telekinesis; +protected: + virtual CTelekinesis& Telekinesis() {return m_telekinesis;} +public: + CGraviZone (void) {} + virtual ~CGraviZone (void) {} +}; \ No newline at end of file diff --git a/src/xrGameLA/Grenade.cpp b/src/xrGameLA/Grenade.cpp new file mode 100644 index 000000000..9372623a4 --- /dev/null +++ b/src/xrGameLA/Grenade.cpp @@ -0,0 +1,371 @@ +#include "stdafx.h" +#include "grenade.h" +#include "PhysicsShell.h" +#include "player_hud.h" +#include "entity.h" +#include "ParticlesObject.h" +#include "actor.h" +#include "inventory.h" +#include "level.h" +#include "xrmessages.h" +#include "xr_level_controller.h" +#include "game_cl_base.h" +#include "xrserver_objects_alife.h" + +#define GRENADE_REMOVE_TIME 30000 +const float default_grenade_detonation_threshold_hit=100; +CGrenade::CGrenade(void) +{ + + m_eSoundCheckout = ESoundTypes(SOUND_TYPE_WEAPON_RECHARGING); +} + +CGrenade::~CGrenade(void) +{ +} + +void CGrenade::Load(LPCSTR section) +{ + inherited::Load(section); + CExplosive::Load(section); + + m_sounds.LoadSound(section,"snd_checkout", "sndCheckout", false, m_eSoundCheckout); + + ////////////////////////////////////// + //время убирания оружия с уровня + if(pSettings->line_exist(section,"grenade_remove_time")) + m_dwGrenadeRemoveTime = pSettings->r_u32(section,"grenade_remove_time"); + else + m_dwGrenadeRemoveTime = GRENADE_REMOVE_TIME; + m_grenade_detonation_threshold_hit=READ_IF_EXISTS(pSettings,r_float,section,"detonation_threshold_hit",default_grenade_detonation_threshold_hit); +} + +void CGrenade::Hit (SHit* pHDS) +{ + if( ALife::eHitTypeExplosion==pHDS->hit_type && m_grenade_detonation_threshold_hitdamage()&&CExplosive::Initiator()==u16(-1)) + { + CExplosive::SetCurrentParentID(pHDS->who->ID()); + Destroy(); + } + inherited::Hit(pHDS); +} + +BOOL CGrenade::net_Spawn(CSE_Abstract* DC) +{ + m_dwGrenadeIndependencyTime = 0; + BOOL ret= inherited::net_Spawn (DC); + Fvector box;BoundingBox().getsize (box); + float max_size = _max(_max(box.x,box.y),box.z); + box.set (max_size,max_size,max_size); + box.mul (3.f); + CExplosive::SetExplosionSize (box); + m_thrown = false; + return ret; +} + +void CGrenade::net_Destroy() +{ + inherited::net_Destroy (); + CExplosive::net_Destroy (); +} + +void CGrenade::OnH_B_Independent(bool just_before_destroy) +{ + inherited::OnH_B_Independent(just_before_destroy); +} + +void CGrenade::OnH_A_Independent() +{ + m_dwGrenadeIndependencyTime = Level().timeServer(); + inherited::OnH_A_Independent (); +} + +void CGrenade::OnH_A_Chield() +{ + m_dwGrenadeIndependencyTime = 0; + m_dwDestroyTime = 0xffffffff; + inherited::OnH_A_Chield (); +} + +void CGrenade::State(u32 state) +{ + switch (state) + { + case eThrowStart: + { + Fvector C; + Center (C); + PlaySound ("sndCheckout", C); + }break; + case eThrowEnd: + { + if(m_thrown) + { + if (m_pPhysicsShell) m_pPhysicsShell->Deactivate(); + xr_delete (m_pPhysicsShell); + m_dwDestroyTime = 0xffffffff; + PutNextToSlot (); + if (Local()) + { + #ifdef DEBUG + Msg("Destroying local grenade[%d][%d]",ID(),Device.dwFrame); + #endif + DestroyObject (); + } + + }; + }break; + }; + inherited::State(state); +} + +bool CGrenade::DropGrenade() +{ + EMissileStates grenade_state = static_cast(GetState()); + if (((grenade_state == eThrowStart) || + (grenade_state == eReady) || + (grenade_state == eThrow)) && + (!m_thrown) + ) + { + Throw(); + return true; + } + return false; +} + +void CGrenade::DiscardState() +{ + if(IsGameTypeSingle() && (GetState()==eReady || GetState()==eThrow) ) + OnStateSwitch(eIdle); +} + +void CGrenade::SendHiddenItem () +{ + if (GetState()==eThrow) + { + Throw (); + } + CActor* pActor = smart_cast( m_pCurrentInventory->GetOwner()); + if (pActor && (GetState()==eReady || GetState()==eThrow)) + { + return; + } + + inherited::SendHiddenItem(); +} + + +void CGrenade::Throw() +{ + if (!m_fake_missile || m_thrown) + return; + + CGrenade *pGrenade = smart_cast(m_fake_missile); + VERIFY (pGrenade); + + if (pGrenade) { + pGrenade->set_destroy_time(m_dwDestroyTimeMax); + //установить ID того кто кинул гранату + pGrenade->SetInitiator( H_Parent()->ID() ); + } + inherited::Throw (); + m_fake_missile->processing_activate();//@sliph + m_thrown = true; +} + +void CGrenade::Destroy() +{ + //Generate Expode event + Fvector normal; + FindNormal (normal); + CExplosive::GenExplodeEvent (Position(), normal); +} + + + +bool CGrenade::Useful() const +{ + + bool res = (/* !m_throw && */ m_dwDestroyTime == 0xffffffff && CExplosive::Useful() && TestServerFlag(CSE_ALifeObject::flCanSave)); + + return res; +} + +void CGrenade::OnEvent(NET_Packet& P, u16 type) +{ + inherited::OnEvent (P,type); + CExplosive::OnEvent (P,type); +} + +void CGrenade::PutNextToSlot() +{ + if (OnClient()) return; + VERIFY (!getDestroy()); + + //выкинуть гранату из инвентаря + NET_Packet P; + if (m_pCurrentInventory) + { + m_pCurrentInventory->Ruck (this); + + this->u_EventGen (P, GEG_PLAYER_ITEM2RUCK, this->H_Parent()->ID()); + P.w_u16 (this->ID()); + this->u_EventSend (P); + } + else + Msg ("! PutNextToSlot : m_pInventory = NULL [%d][%d]", ID(), Device.dwFrame); + + if (smart_cast(H_Parent()) && m_pCurrentInventory) + { + CGrenade *pNext = smart_cast( m_pCurrentInventory->Same(this,true) ); + if(!pNext) + pNext = smart_cast( m_pCurrentInventory->SameSlot(GRENADE_SLOT, this, true) ); + + VERIFY (pNext != this); + + // Msg("Selected next grenade %s", *pNext->cName()); + + if (pNext && m_pCurrentInventory->Slot(pNext->BaseSlot(), pNext)) + { + pNext->u_EventGen (P, GEG_PLAYER_ITEM2SLOT, pNext->H_Parent()->ID()); + P.w_u16 (pNext->ID()); + P.w_u16 (pNext->BaseSlot()); + pNext->u_EventSend (P); + m_pCurrentInventory->SetActiveSlot(pNext->BaseSlot()); + }else + { + CActor* pActor = smart_cast( m_pCurrentInventory->GetOwner()); + + if(pActor) + pActor->OnPrevWeaponSlot(); + } + + m_thrown = false; + } +} + +void CGrenade::OnAnimationEnd(u32 state) +{ + switch(state){ + case eThrowEnd: SwitchState(eHidden); break; + default : inherited::OnAnimationEnd(state); + } +} + + +void CGrenade::UpdateCL() +{ + inherited::UpdateCL (); + CExplosive::UpdateCL (); + + if(!IsGameTypeSingle()) make_Interpolation(); +} + + +bool CGrenade::Action(u16 cmd, u32 flags) +{ + if(inherited::Action(cmd, flags)) return true; + + switch(cmd) + { + //переключение типа гранаты + case kWPN_NEXT: + { + if(flags&CMD_START) + { + if(m_pCurrentInventory) + { + TIItemContainer::iterator it = m_pCurrentInventory->m_ruck.begin(); + TIItemContainer::iterator it_e = m_pCurrentInventory->m_ruck.end(); + for(;it!=it_e;++it) + { + CGrenade *pGrenade = smart_cast(*it); + if(pGrenade && xr_strcmp(pGrenade->cNameSect(), cNameSect())) + { + m_pCurrentInventory->Ruck(this); + m_pCurrentInventory->SetActiveSlot(NO_ACTIVE_SLOT); + m_pCurrentInventory->Slot(pGrenade->BaseSlot(), pGrenade); + return true; + } + } + return true; + } + } + return true; + }; + } + return false; +} + + +bool CGrenade::NeedToDestroyObject() const +{ + if ( IsGameTypeSingle() ) return false; + if ( Remote() ) return false; + if ( TimePassedAfterIndependant() > m_dwGrenadeRemoveTime) + return true; + + return false; +} + +ALife::_TIME_ID CGrenade::TimePassedAfterIndependant() const +{ + if(!H_Parent() && m_dwGrenadeIndependencyTime != 0) + return Level().timeServer() - m_dwGrenadeIndependencyTime; + else + return 0; +} + +BOOL CGrenade::UsedAI_Locations () +{ +#pragma todo("Dima to Yura : It crashes, because on net_Spawn object doesn't use AI locations, but on net_Destroy it does use them") + return TRUE;//m_dwDestroyTime == 0xffffffff; +} + +void CGrenade::net_Relcase(CObject* O ) +{ + CExplosive::net_Relcase(O); + inherited::net_Relcase(O); +} + +void CGrenade::Deactivate() +{ + //Drop grenade if primed + StopCurrentAnimWithoutCallback(); + if ( !GetTmpPreDestroy() && Local() && ( GetState()==eThrowStart || GetState()==eReady || GetState()==eThrow ) ) + { + if (m_fake_missile) + { + CGrenade* pGrenade = smart_cast(m_fake_missile); + if (pGrenade) + { + if (m_pCurrentInventory->GetOwner()) + { + CActor* pActor = smart_cast(m_pCurrentInventory->GetOwner()); + if (pActor) + { + if (!pActor->g_Alive()) + { + m_constpower = false; + m_fThrowForce = 0; + } + } + } + Throw (); + }; + }; + }; + + inherited::Deactivate(); +} + +void CGrenade::GetBriefInfo(xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count) +{ + str_name = NameShort(); + u32 ThisGrenadeCount = m_pCurrentInventory->dwfGetSameItemCount(*cNameSect(), true); + string16 stmp; + xr_sprintf (stmp, "%d", ThisGrenadeCount); + str_count = stmp; + icon_sect_name = *cNameSect(); +} diff --git a/src/xrGameLA/Grenade.h b/src/xrGameLA/Grenade.h new file mode 100644 index 000000000..fc06dcd1f --- /dev/null +++ b/src/xrGameLA/Grenade.h @@ -0,0 +1,74 @@ +#pragma once +#include "missile.h" +#include "explosive.h" +#include "../feel_touch.h" + +#define SND_RIC_COUNT 5 + +class CGrenade : + public CMissile, + public CExplosive +{ + typedef CMissile inherited; +public: + CGrenade (); + virtual ~CGrenade (); + + + virtual void Load (LPCSTR section); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Relcase (CObject* O ); + + virtual void OnH_B_Independent (bool just_before_destroy); + virtual void OnH_A_Independent (); + virtual void OnH_A_Chield (); + virtual void DiscardState (); + + virtual void OnEvent (NET_Packet& P, u16 type); + virtual bool DropGrenade (); //in this case if grenade state is eReady, it should Throw + + virtual void OnAnimationEnd (u32 state); + virtual void UpdateCL (); + + virtual void Throw(); + virtual void Destroy(); + + + virtual bool Action (u16 cmd, u32 flags); + virtual bool Useful () const; + virtual void State (u32 state); + + virtual void OnH_B_Chield () {inherited::OnH_B_Chield();} + + virtual void Hit (SHit* pHDS); + + virtual bool NeedToDestroyObject () const; + virtual ALife::_TIME_ID TimePassedAfterIndependant () const; + + void PutNextToSlot (); + + virtual void Deactivate (); + virtual void GetBriefInfo (xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count); + + virtual void SendHiddenItem (); //same as OnHiddenItem but for client... (sends message to a server)... +protected: + ALife::_TIME_ID m_dwGrenadeRemoveTime; + ALife::_TIME_ID m_dwGrenadeIndependencyTime; +protected: + ESoundTypes m_eSoundCheckout; +private: + float m_grenade_detonation_threshold_hit; + bool m_thrown; +protected: + virtual void UpdateXForm () { CMissile::UpdateXForm(); }; +public: + + virtual BOOL UsedAI_Locations (); + virtual CExplosive *cast_explosive () {return this;} + virtual CMissile *cast_missile () {return this;} + virtual CHudItem *cast_hud_item () {return this;} + virtual CGameObject *cast_game_object () {return this;} + virtual IDamageSource *cast_IDamageSource () {return CExplosive::cast_IDamageSource();} +}; diff --git a/src/xrGameLA/GrenadeLauncher.cpp b/src/xrGameLA/GrenadeLauncher.cpp new file mode 100644 index 000000000..fc194a7f7 --- /dev/null +++ b/src/xrGameLA/GrenadeLauncher.cpp @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////// +// GrenadeLauncher.cpp +// GrenadeLauncher - апгрейд оружия поствольный гранатомет +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "grenadelauncher.h" +//#include "PhysicsShell.h" + +CGrenadeLauncher::CGrenadeLauncher() +{ + m_fGrenadeVel = 0.f; +} + +CGrenadeLauncher::~CGrenadeLauncher() +{ +} + +BOOL CGrenadeLauncher::net_Spawn(CSE_Abstract* DC) +{ + return (inherited::net_Spawn(DC)); +} + +void CGrenadeLauncher::Load(LPCSTR section) +{ + m_fGrenadeVel = pSettings->r_float(section, "grenade_vel"); + inherited::Load(section); +} + +void CGrenadeLauncher::net_Destroy() +{ + inherited::net_Destroy(); +} + +void CGrenadeLauncher::UpdateCL() +{ + inherited::UpdateCL(); +} + + +void CGrenadeLauncher::OnH_A_Chield() +{ + inherited::OnH_A_Chield (); +} + +void CGrenadeLauncher::OnH_B_Independent(bool just_before_destroy) +{ + inherited::OnH_B_Independent(just_before_destroy); +} + +void CGrenadeLauncher::renderable_Render() +{ + inherited::renderable_Render(); +} \ No newline at end of file diff --git a/src/xrGameLA/GrenadeLauncher.h b/src/xrGameLA/GrenadeLauncher.h new file mode 100644 index 000000000..a6a0bfba3 --- /dev/null +++ b/src/xrGameLA/GrenadeLauncher.h @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////// +// GrenadeLauncher.h +// GrenadeLauncher - апгрейд оружия поствольный гранатомет +/////////////////////////////////////////////////////////////// + +#pragma once +#include "inventory_item_object.h" + +class CGrenadeLauncher : public CInventoryItemObject { +private: + typedef CInventoryItemObject inherited; +public: + CGrenadeLauncher (void); + virtual ~CGrenadeLauncher(void); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void Load (LPCSTR section); + virtual void net_Destroy (); + + virtual void OnH_A_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + + virtual void UpdateCL (); + virtual void renderable_Render (); + + float GetGrenadeVel() {return m_fGrenadeVel;} + +protected: + //стартовая скорость вылета подствольной гранаты + float m_fGrenadeVel; +}; \ No newline at end of file diff --git a/src/xrGameLA/HUDCrosshair.cpp b/src/xrGameLA/HUDCrosshair.cpp new file mode 100644 index 000000000..9beded827 --- /dev/null +++ b/src/xrGameLA/HUDCrosshair.cpp @@ -0,0 +1,150 @@ +// HUDCrosshair.cpp: крестик прицела, отображающий текущую дисперсию +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "HUDCrosshair.h" +#include "ui_base.h" + +CHUDCrosshair::CHUDCrosshair () +{ +//. hGeomLine.create (FVF::F_TL0uv,RCache.Vertex.Buffer(),0); +//. hShader.create ("editor\\wire"); + hShader->create ("hud\\crosshair"); + + //вычислить и запомнить центр экрана +// center.set(int(Device.dwWidth)/2,int(Device.dwHeight)/2); + radius = 0; +} + + +CHUDCrosshair::~CHUDCrosshair () +{ +} + +void CHUDCrosshair::Load () +{ + //все размеры в процентах от длины экрана + //длина крестика + cross_length_perc = pSettings->r_float (HUD_CURSOR_SECTION, "cross_length"); +// cross_length = iFloor(0.5f + cross_length_perc*float(Device.dwWidth)); + + min_radius_perc = pSettings->r_float (HUD_CURSOR_SECTION, "min_radius"); + //min_radius = iFloor(0.5f + min_radius_perc*float(Device.dwWidth)); + + max_radius_perc = pSettings->r_float (HUD_CURSOR_SECTION, "max_radius"); + //max_radius = iFloor(0.5f + max_radius_perc*float(Device.dwWidth)); + + cross_color = pSettings->r_fcolor (HUD_CURSOR_SECTION, "cross_color").get(); + + + radius_speed_perc = pSettings->r_float (HUD_CURSOR_SECTION, "radius_lerp_speed"); +} + +//выставляет radius от min_radius до max_radius +void CHUDCrosshair::SetDispersion (float disp) +{ + Fvector4 r; + Fvector R = { VIEWPORT_NEAR*_sin(disp), 0.f, VIEWPORT_NEAR }; + Device.mProject.transform (r,R); + + Fvector2 scr_size; + scr_size.set (float(::Render->getTarget()->get_width()), float(::Render->getTarget()->get_height())); + float radius_pixels = _abs(r.x)*scr_size.x/2.0f; + // clamp(radius_pixels, min_radius, max_radius); + target_radius = radius_pixels; +} + +extern ENGINE_API BOOL g_bRendering; + +extern u32 crosshairAnimationType; +void CHUDCrosshair::OnRender () +{ + VERIFY (g_bRendering); + Fvector2 center; + Fvector2 scr_size; + scr_size.set (float(::Render->getTarget()->get_width()), float(::Render->getTarget()->get_height())); + center.set (scr_size.x/2.0f, scr_size.y/2.0f); + + UIRender->StartPrimitive (10, IUIRender::ptLineList, UI().m_currentPointType); + + float cross_length = cross_length_perc*scr_size.x; + float min_radius = min_radius_perc*scr_size.x; + float max_radius = max_radius_perc*scr_size.x; + + clamp (target_radius , min_radius, max_radius); + + float x_min = min_radius + radius; + float x_max = x_min + cross_length; + + float y_min = x_min; + float y_max = x_max; + + float smeshenie = 20.5 * (scr_size.x / 1280); + + if (crosshairAnimationType == 2) + { + // Правая + UIRender->PushPoint(center.x + 1 + x_min, center.y - y_min, 0, cross_color, 0, 0); + UIRender->PushPoint(center.x + smeshenie + x_min, center.y - y_max * 0.75, 0, cross_color, 0, 0); + // Левая + UIRender->PushPoint(center.x + 1 - x_min, center.y - y_min, 0, cross_color, 0, 0); + UIRender->PushPoint(center.x - smeshenie - x_min, center.y - y_max * 0.75, 0, cross_color, 0, 0); + // Нижняя + UIRender->PushPoint(center.x + 1, center.y + y_min, 0, cross_color, 0, 0); + UIRender->PushPoint(center.x + 1, center.y + y_max, 0, cross_color, 0, 0); + } + else if (crosshairAnimationType == 3) + { + // Правая + UIRender->PushPoint(center.x + 1 + x_min, center.y + y_min, 0, cross_color, 0,0); + UIRender->PushPoint(center.x + smeshenie + x_min, center.y + y_max * 0.75, 0, cross_color, 0,0); + // Левая + UIRender->PushPoint(center.x + 1 - x_min, center.y + y_min, 0, cross_color, 0,0); + UIRender->PushPoint(center.x - smeshenie - x_min, center.y + y_max * 0.75, 0, cross_color, 0,0); + // Верхняя + UIRender->PushPoint(center.x+1, center.y - y_min, 0, cross_color, 0,0); + UIRender->PushPoint(center.x+1, center.y - y_max, 0, cross_color, 0,0); + } + else if (crosshairAnimationType == 4) + { + // 0 Нижняя + UIRender->PushPoint(center.x + 1, center.y + y_min, 0, cross_color, 0, 0); + UIRender->PushPoint(center.x + 1, center.y + y_max, 0, cross_color, 0, 0); + // 1 Верхняя + UIRender->PushPoint(center.x + 1, center.y - y_min, 0, cross_color, 0, 0); + UIRender->PushPoint(center.x + 1, center.y - y_max, 0, cross_color, 0, 0); + // 2 Правая + UIRender->PushPoint(center.x + x_min + 1, center.y, 0, cross_color, 0, 0); + UIRender->PushPoint(center.x + x_max + 1, center.y, 0, cross_color, 0, 0); + // 3 Левая + UIRender->PushPoint(center.x - x_min, center.y, 0, cross_color, 0, 0); + UIRender->PushPoint(center.x - x_max, center.y, 0, cross_color, 0, 0); + } + + // point + UIRender->PushPoint(center.x, center.y, 0, cross_color, 0,0); + UIRender->PushPoint(center.x+1, center.y, 0, cross_color, 0,0); + + + // render + UIRender->SetShader(*hShader); + UIRender->FlushPrimitive (); + + + if(!fsimilar(target_radius,radius)) + { + + //radius = target_radius; + float sp = radius_speed_perc * scr_size.x ; + float radius_change = sp*Device.fTimeDelta; + clamp (radius_change, 0.0f, sp*0.033f); // clamp to 30 fps + clamp (radius_change, 0.0f, _abs(target_radius-radius)); +// + if(target_radius < radius) + radius -= radius_change; + else + radius += radius_change; + }; +} \ No newline at end of file diff --git a/src/xrGameLA/HUDCrosshair.h b/src/xrGameLA/HUDCrosshair.h new file mode 100644 index 000000000..0607d3139 --- /dev/null +++ b/src/xrGameLA/HUDCrosshair.h @@ -0,0 +1,35 @@ +// HUDCrosshair.h: крестик прицела, отображающий текущую дисперсию +// +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "ui_defs.h" + +#define HUD_CURSOR_SECTION "hud_cursor" + +class CHUDCrosshair +{ +private: + float cross_length_perc; + float min_radius_perc; + float max_radius_perc; + + //текущий радиус прицела + float radius; + float target_radius; + float radius_speed_perc; + + //ref_geom hGeomLine; + ui_shader hShader; +public: + u32 cross_color; + + CHUDCrosshair (); + ~CHUDCrosshair (); + + void OnRender (); + void SetDispersion (float disp); + + void Load (); +}; \ No newline at end of file diff --git a/src/xrGameLA/HUDManager.cpp b/src/xrGameLA/HUDManager.cpp new file mode 100644 index 000000000..918047afa --- /dev/null +++ b/src/xrGameLA/HUDManager.cpp @@ -0,0 +1,373 @@ +#include "stdafx.h" +#include "HUDManager.h" +#include "hudtarget.h" + +#include "actor.h" +#include "../igame_level.h" +#include "clsid_game.h" +#include "GamePersistent.h" +#include "UIGameCustom.h" +#include "UICursor.h" +#include "MainMenu.h" +#include "game_cl_base.h" +#include "Car.h" + +u32 ui_hud_type; +extern CUIGameCustom* CurrentGameUI() {return HUD().GetGameUI();} + +CFontManager::CFontManager() +{ + Device.seqDeviceReset.Add(this,REG_PRIORITY_HIGH); + + m_all_fonts.push_back(&pFontMedium );// used cpp + m_all_fonts.push_back(&pFontDI );// used cpp + m_all_fonts.push_back(&pFontArial14 );// used xml + m_all_fonts.push_back(&pFontGraffiti19Russian ); + m_all_fonts.push_back(&pFontGraffiti22Russian ); + m_all_fonts.push_back(&pFontLetterica16Russian ); + m_all_fonts.push_back(&pFontLetterica18Russian ); + m_all_fonts.push_back(&pFontGraffiti32Russian ); + m_all_fonts.push_back(&pFontGraffiti50Russian ); + m_all_fonts.push_back(&pFontLetterica25 ); + m_all_fonts.push_back(&pFontStat ); + + FONTS_VEC_IT it = m_all_fonts.begin(); + FONTS_VEC_IT it_e = m_all_fonts.end(); + for(;it!=it_e;++it) + (**it) = NULL; + + InitializeFonts(); + +} + +void CFontManager::InitializeFonts() +{ + + InitializeFont(pFontMedium ,"hud_font_medium" ); + InitializeFont(pFontDI ,"hud_font_di", CGameFont::fsGradient|CGameFont::fsDeviceIndependent); + InitializeFont(pFontArial14 ,"ui_font_arial_14" ); + InitializeFont(pFontGraffiti19Russian ,"ui_font_graffiti19_russian" ); + InitializeFont(pFontGraffiti22Russian ,"ui_font_graffiti22_russian" ); + InitializeFont(pFontLetterica16Russian ,"ui_font_letterica16_russian" ); + InitializeFont(pFontLetterica18Russian ,"ui_font_letterica18_russian" ); + InitializeFont(pFontGraffiti32Russian ,"ui_font_graff_32" ); + InitializeFont(pFontGraffiti50Russian ,"ui_font_graff_50" ); + InitializeFont(pFontLetterica25 ,"ui_font_letter_25" ); + InitializeFont(pFontStat ,"stat_font", CGameFont::fsDeviceIndependent); + pFontStat->SetInterval (0.75f, 1.0f); +} + +LPCSTR CFontManager::GetFontTexName (LPCSTR section) +{ + static char* tex_names[]={"texture800","texture","texture1600"}; + int def_idx = 1;//default 1024x768 + int idx = def_idx; +#if 0 + u32 w = Device.dwWidth; + + if(w<=800) idx = 0; + else if(w<=1280)idx = 1; + else idx = 2; +#else + u32 h = Device.dwHeight; + + if(h<=600) idx = 0; + else if(h<1024) idx = 1; + else idx = 2; +#endif + + while(idx>=0){ + if( pSettings->line_exist(section,tex_names[idx]) ) + return pSettings->r_string(section,tex_names[idx]); + --idx; + } + return pSettings->r_string(section,tex_names[def_idx]); +} + +void CFontManager::InitializeFont(CGameFont*& F, LPCSTR section, u32 flags) +{ + LPCSTR font_tex_name = GetFontTexName(section); + R_ASSERT(font_tex_name); + + if(!F) + F = new CGameFont ("font", font_tex_name, flags); + else + F->Initialize("font",font_tex_name); + +#ifdef DEBUG + F->m_font_name = section; +#endif + if (pSettings->line_exist(section,"size")){ + float sz = pSettings->r_float(section,"size"); + if (flags&CGameFont::fsDeviceIndependent) F->SetHeightI(sz); + else F->SetHeight(sz); + } + if (pSettings->line_exist(section,"interval")) + F->SetInterval(pSettings->r_fvector2(section,"interval")); + +} + +CFontManager::~CFontManager() +{ + Device.seqDeviceReset.Remove(this); + FONTS_VEC_IT it = m_all_fonts.begin(); + FONTS_VEC_IT it_e = m_all_fonts.end(); + for(;it!=it_e;++it) + xr_delete(**it); +} + +void CFontManager::Render() +{ + FONTS_VEC_IT it = m_all_fonts.begin(); + FONTS_VEC_IT it_e = m_all_fonts.end(); + for(;it!=it_e;++it) + (**it)->OnRender (); +} +void CFontManager::OnDeviceReset() +{ + InitializeFonts(); +} + +//-------------------------------------------------------------------- +CHUDManager::CHUDManager() : pUIGame(NULL), m_pHUDTarget(new CHUDTarget()) +{ +} +//-------------------------------------------------------------------- +CHUDManager::~CHUDManager() +{ + OnDisconnected(); + + if(pUIGame) + pUIGame->UnLoad (); + + xr_delete (pUIGame); + xr_delete (m_pHUDTarget); +} + +//-------------------------------------------------------------------- +void CHUDManager::OnFrame() +{ + if(!b_online) + return; + + if (pUIGame) + pUIGame->OnFrame(); + + m_pHUDTarget->CursorOnFrame(); +} +//-------------------------------------------------------------------- + +ENGINE_API extern float psHUD_FOV; + +void CHUDManager::Render_First() +{ + //if (!psHUD_Flags.is(HUD_WEAPON|HUD_WEAPON_RT))return; //skyloader: commented because it has problems with 'actor shadow'/'actor body' + if (0==pUIGame) return; + CObject* O = g_pGameLevel->CurrentViewEntity(); + if (0==O) return; + CActor* A = smart_cast (O); + if (!A) return; + + if (A->HUDview() && !A->IsActorShadowsOn() && !A->DrawLegs()) + return; + + if (A->UsingTurret()) + return; + + if ((!A->HUDview() && A->IsActorShadowsOn()) || !A->IsActorShadowsOn()) + { + ::Render->set_Object (O->H_Root()); + O->renderable_Render (); + return; + } + + // only shadow + ::Render->set_Invisible (TRUE); + ::Render->set_Object (O->H_Root()); + O->renderable_Render (); + ::Render->set_Invisible (FALSE); +} + +bool need_render_hud() +{ + CObject* O = g_pGameLevel ? g_pGameLevel->CurrentViewEntity() : NULL; + if (0==O) + return false; + + CActor* A = smart_cast (O); + if (A && (!A->HUDview() || !A->g_Alive()) ) + return false; + + if( smart_cast(O) /*|| smart_cast(O)*/ ) + return false; + + return true; +} + +void CHUDManager::Render_Last() +{ + if (!psHUD_Flags.is(HUD_WEAPON|HUD_WEAPON_RT|HUD_WEAPON_RT2|HUD_DRAW_RT2))return; + if (0==pUIGame) return; + CObject* O = g_pGameLevel->CurrentViewEntity(); + if (0==O) return; + CActor* A = smart_cast (O); + if (A && !A->HUDview()) return; + if(O->CLS_ID == CLSID_CAR) + return; + + // hud itself + ::Render->set_HUD (TRUE); + ::Render->set_Object (O->H_Root()); + O->OnHUDDraw (this); + ::Render->set_HUD (FALSE); +} + +#include "player_hud.h" +bool CHUDManager::RenderActiveItemUIQuery() +{ + if (!psHUD_Flags.is(HUD_DRAW_RT2)) + return false; + + if (!psHUD_Flags.is(HUD_WEAPON|HUD_WEAPON_RT|HUD_WEAPON_RT2))return false; + + if(!need_render_hud()) return false; + + return (g_player_hud && g_player_hud->render_item_ui_query() ); +} + +void CHUDManager::RenderActiveItemUI() +{ + if (!psHUD_Flags.is(HUD_DRAW_RT2)) + return; + + g_player_hud->render_item_ui (); +} + +extern void draw_wnds_rects(); +extern ENGINE_API BOOL bShowPauseString; +//отрисовка элементов интерфейса +#include "string_table.h" +void CHUDManager::RenderUI() +{ + if (!psHUD_Flags.is(HUD_DRAW_RT2)) + return; + + if(!b_online) return; + + BOOL bAlready = FALSE; + if (true /*|| psHUD_Flags.is(HUD_DRAW | HUD_DRAW_RT)*/) + { + HitMarker.Render (); + if(pUIGame) + pUIGame->Render (); + + UI().RenderFont (); + } + + if (psHUD_Flags.is(HUD_CROSSHAIR|HUD_CROSSHAIR_RT|HUD_CROSSHAIR_RT2) && !bAlready) + m_pHUDTarget->Render(); + + draw_wnds_rects (); + + if( Device.Paused() && bShowPauseString){ + CGameFont* pFont = UI().Font().pFontGraffiti50Russian; + pFont->SetColor (0x80FF0000 ); + LPCSTR _str = CStringTable().translate("st_game_paused").c_str(); + + Fvector2 _pos; + _pos.set (UI_BASE_WIDTH/2.0f, UI_BASE_HEIGHT/2.0f); + UI().ClientToScreenScaled(_pos); + pFont->SetAligment (CGameFont::alCenter); + pFont->Out (_pos.x, _pos.y, _str); + pFont->OnRender (); + } + +} + +void CHUDManager::OnEvent(EVENT E, u64 P1, u64 P2) +{ +} + +collide::rq_result& CHUDManager::GetCurrentRayQuery () +{ + return m_pHUDTarget->GetRQ(); +} + +void CHUDManager::SetCrosshairDisp (float dispf, float disps) +{ + m_pHUDTarget->GetHUDCrosshair().SetDispersion(psHUD_Flags.test(HUD_CROSSHAIR_DYNAMIC) ? dispf : disps); +} + +void CHUDManager::ShowCrosshair (bool show) +{ + m_pHUDTarget->ShowCrosshair (show); +} + + +void CHUDManager::Hit(int idx, float power, const Fvector& dir) +{ + HitMarker.Hit(idx, dir); +} + +void CHUDManager::SetHitmarkType (LPCSTR tex_name) +{ + HitMarker.InitShader (tex_name); +} + +#include "ui\UIMainInGameWnd.h" +extern CUIXml* pWpnScopeXml; + +void CHUDManager::Load() +{ + if (!pUIGame) + { + pUIGame = Game().createGameUI(); + } else + { + pUIGame->SetClGame (&Game()); + } +} + +void CHUDManager::OnScreenResolutionChanged() +{ + if (pUIGame) + { + pUIGame->HideShownDialogs (); + xr_delete (pWpnScopeXml); + pUIGame->UnLoad (); + pUIGame->Load (); + + pUIGame->OnConnected(); + } +} + +void CHUDManager::OnDisconnected() +{ + + b_online = false; + if(pUIGame) + Device.seqFrame.Remove (pUIGame); +} + +void CHUDManager::OnConnected() +{ + if(b_online) return; + b_online = true; + if(pUIGame) + Device.seqFrame.Add (pUIGame,REG_PRIORITY_LOW-1000); +} + +void CHUDManager::net_Relcase (CObject *object) +{ + VERIFY (m_pHUDTarget); + m_pHUDTarget->net_Relcase (object); +} + +CDialogHolder* CurrentDialogHolder() +{ + if(MainMenu()->IsActive()) + return MainMenu(); + else + return HUD().GetGameUI(); +} + diff --git a/src/xrGameLA/HUDManager.h b/src/xrGameLA/HUDManager.h new file mode 100644 index 000000000..9a19581e6 --- /dev/null +++ b/src/xrGameLA/HUDManager.h @@ -0,0 +1,55 @@ +#pragma once + +#include "../CustomHUD.h" +#include "HitMarker.h" + +extern u32 ui_hud_type; + +class CHUDTarget; +class CUIGameCustom; + +class CHUDManager : + public CCustomHUD +{ + friend class CUI; +private: + CUIGameCustom* pUIGame; + CHitMarker HitMarker; + CHUDTarget* m_pHUDTarget; + bool b_online; +public: + CHUDManager (); + virtual ~CHUDManager (); + virtual void OnEvent (EVENT E, u64 P1, u64 P2); + + virtual void Render_First (); + virtual void Render_Last (); + virtual void OnFrame (); + + virtual void RenderUI (); + + CUIGameCustom* GetGameUI (){return pUIGame;} + + void Hit (int idx, float power, const Fvector& dir); + //CFontManager& Font () {return *(UI().Font());} + //текущий предмет на который смотрит HUD + collide::rq_result& GetCurrentRayQuery (); + + + //устанвка внешнего вида прицела в зависимости от текущей дисперсии + void SetCrosshairDisp (float dispf, float disps = 0.f); + void ShowCrosshair (bool show); + + void SetHitmarkType (LPCSTR tex_name); + virtual void OnScreenResolutionChanged(); + virtual void Load (); + virtual void OnDisconnected (); + virtual void OnConnected (); + + virtual void RenderActiveItemUI (); + virtual bool RenderActiveItemUIQuery(); + + virtual void net_Relcase (CObject *object); +}; + +IC CHUDManager& HUD() { return *((CHUDManager*)g_hud);} \ No newline at end of file diff --git a/src/xrGameLA/HUDTarget.cpp b/src/xrGameLA/HUDTarget.cpp new file mode 100644 index 000000000..0856c25d6 --- /dev/null +++ b/src/xrGameLA/HUDTarget.cpp @@ -0,0 +1,294 @@ +// exxZERO Time Stamp AddIn. Document modified at : Thursday, March 07, 2002 14:13:00 , by user : Oles , from computer : OLES +// HUDCursor.cpp: implementation of the CHUDTarget class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "hudtarget.h" +#include "hudmanager.h" +#include "../GameMtlLib.h" + +#include "../Environment.h" +#include "../CustomHUD.h" +#include "Entity.h" +#include "level.h" +#include "game_cl_base.h" +#include "../igame_persistent.h" +#include "ai/stalker/ai_stalker.h" + + +#include "InventoryOwner.h" +#include "relation_registry.h" +#include "character_info.h" + +#include "string_table.h" +#include "entity_alive.h" + +#include "inventory_item.h" +#include "inventory.h" +#include "ui_base.h" + +u32 C_ON_ENEMY D3DCOLOR_XRGB(0xff,0,0); +u32 C_ON_NEUTRAL D3DCOLOR_XRGB(0xff,0xff,0x80); +u32 C_ON_FRIEND D3DCOLOR_XRGB(0,0xff,0); + + +#define C_DEFAULT D3DCOLOR_XRGB(0xff,0xff,0xff) +#define C_SIZE 0.025f +#define NEAR_LIM 0.5f + +#define SHOW_INFO_SPEED 0.5f +#define HIDE_INFO_SPEED 10.f + + +IC float recon_mindist () { + return 2.f; +} +IC float recon_maxdist () { + return 50.f; +} +IC float recon_minspeed () { + return 0.5f; +} +IC float recon_maxspeed () { + return 10.f; +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CHUDTarget::CHUDTarget () +{ + fuzzyShowInfo = 0.f; + RQ.range = 0.f; + hShader->create ("hud\\cursor","ui\\cursor"); + + RQ.set (NULL, 0.f, -1); + + Load (); + m_bShowCrosshair = false; +} + +CHUDTarget::~CHUDTarget () +{ +} + +void CHUDTarget::net_Relcase(CObject* O) +{ + if(RQ.O == O) + RQ.O = NULL; + + RQR.r_clear (); +} + +void CHUDTarget::Load () +{ + HUDCrosshair.Load(); +} + +ICF static BOOL pick_trace_callback(collide::rq_result& result, LPVOID params) +{ + collide::rq_result* RQ = (collide::rq_result*)params; + if(result.O){ + *RQ = result; + return FALSE; + }else{ + //получить треугольник и узнать его материал + CDB::TRI* T = Level().ObjectSpace.GetStaticTris()+result.element; + if (GMLib.GetMaterialByIdx(T->material)->Flags.is(SGameMtl::flPassable)) + return TRUE; + } + *RQ = result; + return FALSE; +} + +void CHUDTarget::CursorOnFrame () +{ + Fvector p1,dir; + + p1 = Device.vCameraPosition; + dir = Device.vCameraDirection; + + // Render cursor + if(Level().CurrentEntity()){ + RQ.O = 0; + RQ.range = g_pGamePersistent->Environment().CurrentEnv->far_plane*0.99f; + RQ.element = -1; + + collide::ray_defs RD(p1, dir, RQ.range, CDB::OPT_CULL, collide::rqtBoth); + RQR.r_clear (); + VERIFY (!fis_zero(RD.dir.square_magnitude())); + if(Level().ObjectSpace.RayQuery(RQR,RD, pick_trace_callback, &RQ, NULL, Level().CurrentEntity())) + clamp (RQ.range,NEAR_LIM,RQ.range); + } + +} + +void CHUDTarget::ShowCrosshair(bool b) +{ + m_bShowCrosshair = b; +} + +extern ENGINE_API BOOL g_bRendering; +void CHUDTarget::Render() +{ + VERIFY (g_bRendering); + + CObject* O = Level().CurrentEntity(); + if (0==O) return; + CEntity* E = smart_cast(O); + if (0==E) return; + + Fvector p1 = Device.vCameraPosition; + Fvector dir = Device.vCameraDirection; + + // Render cursor + u32 C = C_DEFAULT; + + Fvector p2; + p2.mad (p1,dir,RQ.range); + Fvector4 pt; + Device.mFullTransform.transform(pt, p2); + pt.y = -pt.y; + float di_size = C_SIZE/powf(pt.w,.2f); + + CGameFont* F = UI().Font().pFontGraffiti19Russian; + F->SetAligment (CGameFont::alCenter); + F->OutSetI (0.f,0.05f); + + if (psHUD_Flags.test(HUD_CROSSHAIR_DIST)){ + F->SetColor (C); + if (RQ.range >= 250.0f) + F->OutNext (""); + else + F->OutNext ("%4.1f",RQ.range); + } + + if (psHUD_Flags.test(HUD_INFO)){ + if (RQ.O){ + CEntityAlive* E = smart_cast (RQ.O); + CEntityAlive* pCurEnt = smart_cast (Level().CurrentEntity()); + PIItem l_pI = smart_cast (RQ.O); + + if (IsGameTypeSingle()) + { + CInventoryOwner* our_inv_owner = smart_cast(pCurEnt); + + if (E && E->g_Alive() && !E->cast_base_monster()) + { + CInventoryOwner* others_inv_owner = smart_cast(E); + CAI_Stalker* pStalker = smart_cast(E); + + if ((pStalker && !pStalker->IsGhost()) || !pStalker) + { + if(our_inv_owner && others_inv_owner){ + + switch(RELATION_REGISTRY().GetRelationType(others_inv_owner, our_inv_owner)) + { + case ALife::eRelationTypeEnemy: + C = C_ON_ENEMY; break; + case ALife::eRelationTypeNeutral: + C = C_ON_NEUTRAL; break; + case ALife::eRelationTypeFriend: + C = C_ON_FRIEND; break; + } + + if (fuzzyShowInfo>0.5f) + { + CStringTable strtbl ; + F->SetColor (subst_alpha(C,u8(iFloor(255.f*(fuzzyShowInfo-0.5f)*2.f)))); + F->OutNext ("%s", *strtbl.translate(others_inv_owner->Name()) ); + F->OutNext ("%s", *strtbl.translate(others_inv_owner->CharacterInfo().Community().id()) ); + } + } + + fuzzyShowInfo += SHOW_INFO_SPEED*Device.fTimeDelta; + } + } + else + if (l_pI && our_inv_owner && RQ.range < 2.0f*our_inv_owner->inventory().GetTakeDist()) + { + if (fuzzyShowInfo>0.5f){ + F->SetColor (subst_alpha(C,u8(iFloor(255.f*(fuzzyShowInfo-0.5f)*2.f)))); + F->OutNext ("%s",l_pI->Name/*Complex*/()); + } + fuzzyShowInfo += SHOW_INFO_SPEED*Device.fTimeDelta; + } + } + else + { + if (E && (E->GetfHealth()>0)) + { + if (pCurEnt && GameID() == GAME_SINGLE){ + + if (E->g_Team() != pCurEnt->g_Team()) C = C_ON_ENEMY; + else C = C_ON_FRIEND; + + if (RQ.range >= recon_mindist() && RQ.range <= recon_maxdist()){ + float ddist = (RQ.range - recon_mindist())/(recon_maxdist() - recon_mindist()); + float dspeed = recon_minspeed() + (recon_maxspeed() - recon_minspeed())*ddist; + fuzzyShowInfo += Device.fTimeDelta/dspeed; + }else{ + if (RQ.range < recon_mindist()) fuzzyShowInfo += recon_minspeed()*Device.fTimeDelta; + else fuzzyShowInfo = 0; + }; + + if (fuzzyShowInfo>0.5f){ + clamp(fuzzyShowInfo,0.f,1.f); + int alpha_C = iFloor(255.f*(fuzzyShowInfo-0.5f)*2.f); + u8 alpha_b = u8(alpha_C & 0x00ff); + F->SetColor (subst_alpha(C,alpha_b)); + F->OutNext ("%s",*RQ.O->cName()); + } + } + }; + }; + + }else{ + fuzzyShowInfo -= HIDE_INFO_SPEED*Device.fTimeDelta; + + } + clamp(fuzzyShowInfo,0.f,1.f); + } + + //отрендерить кружочек или крестик + if(!m_bShowCrosshair){ + // actual rendering + UIRender->StartPrimitive (6, IUIRender::ptTriList, UI().m_currentPointType); + + Fvector2 scr_size; +//. scr_size.set (float(::Render->getTarget()->get_width()), float(::Render->getTarget()->get_height())); + scr_size.set (float(Device.dwWidth) ,float(Device.dwHeight)); + float size_x = scr_size.x * di_size; + float size_y = scr_size.y * di_size; + + size_y = size_x; + + float w_2 = scr_size.x/2.0f; + float h_2 = scr_size.y/2.0f; + + // Convert to screen coords + float cx = (pt.x+1)*w_2; + float cy = (pt.y+1)*h_2; + + // TODO: return code back to indexed rendering since we use quads + // Tri 1 + UIRender->PushPoint(cx - size_x, cy + size_y, 0, C, 0, 1); + UIRender->PushPoint(cx - size_x, cy - size_y, 0, C, 0, 0); + UIRender->PushPoint(cx + size_x, cy + size_y, 0, C, 1, 1); + // Tri 2 + UIRender->PushPoint(cx + size_x, cy + size_y, 0, C, 1, 1); + UIRender->PushPoint(cx - size_x, cy - size_y, 0, C, 0, 0); + UIRender->PushPoint(cx + size_x, cy - size_y, 0, C, 1, 0); + + // unlock VB and Render it as triangle LIST + UIRender->SetShader(*hShader); + UIRender->FlushPrimitive(); + }else{ + //отрендерить прицел + HUDCrosshair.cross_color = C; + HUDCrosshair.OnRender (); + } +} + diff --git a/src/xrGameLA/HUDTarget.h b/src/xrGameLA/HUDTarget.h new file mode 100644 index 000000000..622876dd0 --- /dev/null +++ b/src/xrGameLA/HUDTarget.h @@ -0,0 +1,39 @@ +#pragma once + +#include "HUDCrosshair.h" +#include "../xrCDB/xr_collide_defs.h" + +class CHUDManager; + +class CHUDTarget { +private: + friend class CHUDManager; + +private: + typedef collide::rq_result rq_result; + typedef collide::rq_results rq_results; + +private: + ui_shader hShader; + //ref_geom hGeom; + float fuzzyShowInfo; + rq_result RQ; + rq_results RQR; + +private: + bool m_bShowCrosshair; + CHUDCrosshair HUDCrosshair; + +private: + void net_Relcase (CObject* O); + +public: + CHUDTarget (); + ~CHUDTarget (); + void CursorOnFrame (); + void Render (); + void Load (); + collide::rq_result& GetRQ () {return RQ;}; + CHUDCrosshair& GetHUDCrosshair () {return HUDCrosshair;} + void ShowCrosshair (bool b); +}; diff --git a/src/xrGameLA/HairsZone.cpp b/src/xrGameLA/HairsZone.cpp new file mode 100644 index 000000000..63541b378 --- /dev/null +++ b/src/xrGameLA/HairsZone.cpp @@ -0,0 +1,161 @@ +#include "pch_script.h" +#include "HairsZone.h" +#include "hudmanager.h" +#include "level.h" +#include "PhysicsShellHolder.h" +#include "entity_alive.h" +#include "PHMovementControl.h" +#include "CharacterPhysicsSupport.h" +#include "../xr_collide_form.h" + +bool CHairsZone::BlowoutState() +{ + bool result = inherited::BlowoutState(); + + if(!result) + UpdateBlowout(); + + return result; +} + +void CHairsZone::CheckForAwaking() +{ + for(OBJECT_INFO_VEC_IT it = m_ObjectInfoMap.begin(); m_ObjectInfoMap.end() != it; ++it) + { + CObject* pObject = (*it).object; + + if (!pObject) + continue; + + SwitchZoneState(eZoneStateAwaking); + } +} + +void CHairsZone::Load(LPCSTR section) +{ + inherited::Load(section); + + m_min_speed_to_react = pSettings->r_float(section, "min_speed_to_react"); +} + +void CHairsZone::UpdateBlowout() +{ + if (m_dwBlowoutExplosionTime >= (u32)m_iPreviousStateTime && + m_dwBlowoutExplosionTime<(u32)m_iStateTime) + { + AffectObjects(); + BornArtefact(); + } +} + +float CHairsZone::CalcHitPower(float velocity, CPhysicsShellHolder* object) +{ + const Fbox& self_bb = CFORM()->getBBox(); + + const Fbox& hitted_bb = object->character_physics_support()->movement()->Box(); + + Fvector self_r; + self_bb.getradius(self_r); + Fvector hitted_r; + hitted_bb.getradius(hitted_r); + + float max_y = Position().y; + float min_y = Position().y - (self_r.y * 2); + + float hitted_top_point = object->Position().y + hitted_r.y * 2; + + if (Position().y < min_y) // objects is not hitting the anomaly + return 0.f; + + float distnce_from_top = max_y - hitted_top_point; + float distance_k = distnce_from_top / (max_y - min_y); + float velocity_k = velocity / m_min_speed_to_react; + + float hit_power = m_fMaxPower * distance_k * velocity_k; + + //Msg("max %f min %f hitted_top_point %f dist %f D K %f V k %f power %f", max_y, min_y, hitted_top_point, distnce_from_top, distance_k, velocity_k, hit_power); + + return hit_power; +} + +void CHairsZone::Affect(SZoneObjectInfo* O) +{ + if (O->zone_ignore) + return; + + CPhysicsShellHolder* phys_shell_holder = smart_cast(O->object); + + if (!phys_shell_holder) + return; + + if (!phys_shell_holder->character_physics_support()) + { + PlayHitParticles(phys_shell_holder); + + return; + } + + float sp = phys_shell_holder->character_physics_support()->movement()->GetVelocityActual(); + if (sp < m_min_speed_to_react) + return; + + Fvector P; + XFORM().transform_tiny(P,CFORM()->getSphere().P); + + if (phys_shell_holder->Position().y > P.y) // don't interfear with object if its walking on top of it + return; + + float power = CalcHitPower(sp, phys_shell_holder); + float impulse = m_fHitImpulseScale * power * phys_shell_holder->GetMass(); + + if(power > 0.01f) + { + Fvector hit_dir; + hit_dir.set(::Random.randF(-.5f, .5f), + ::Random.randF(.0f, 1.f), + ::Random.randF(-.5f, .5f)); + hit_dir.normalize(); + + m_dwDeltaTime = 0; + + //статистика по объекту + O->total_damage += power; + O->hit_num++; + + Fvector position_in_bone_space; + + position_in_bone_space.set(0.f, 0.f, 0.f); + + CreateHit(phys_shell_holder->ID(), ID(), hit_dir, power, 0, position_in_bone_space, impulse, m_eHitTypeBlowout); + + PlayHit(phys_shell_holder); + } +} + +void CHairsZone::PlayHit(CGameObject* hitted_object) +{ + if (m_dwBlowoutParticlesTime >= (u32)m_iPreviousStateTime && + m_dwBlowoutParticlesTime<(u32)m_iStateTime) + PlayBlowoutParticles(); + + if (m_dwBlowoutLightTime >= (u32)m_iPreviousStateTime && + m_dwBlowoutLightTime<(u32)m_iStateTime) + StartBlowoutLight(); + + if (m_dwBlowoutSoundTime >= (u32)m_iPreviousStateTime && + m_dwBlowoutSoundTime<(u32)m_iStateTime) + m_blowout_sound.play_at_pos(0, Position()); + + if (m_zone_flags.test(eBlowoutWind) && m_dwBlowoutWindTimeStart >= (u32)m_iPreviousStateTime && + m_dwBlowoutWindTimeStart<(u32)m_iStateTime) + StartWind(); + + UpdateWind(); + + PlayHitParticles(hitted_object); +} + +bool CHairsZone::ShouldIgnoreObject(CGameObject* pObject) +{ + return inherited::ShouldIgnoreObject(pObject); +} diff --git a/src/xrGameLA/HairsZone.h b/src/xrGameLA/HairsZone.h new file mode 100644 index 000000000..9ea583b14 --- /dev/null +++ b/src/xrGameLA/HairsZone.h @@ -0,0 +1,30 @@ +#pragma once + +#include "CustomZone.h" +#include "../../Include/xrRender/KinematicsAnimated.h" +#include "ZoneVisual.h" + +#include "script_export_space.h" + +class CHairsZone : public CVisualZone { +typedef CVisualZone inherited; +public: + virtual void Affect (SZoneObjectInfo* O) ; + virtual void Load (LPCSTR section); + +protected: + float m_min_speed_to_react; + virtual bool BlowoutState (); + virtual void CheckForAwaking (); + void UpdateBlowout() override; + void PlayHit(CGameObject* hitted_object); + + float CalcHitPower(float velocity, CPhysicsShellHolder* object); + + bool ShouldIgnoreObject(CGameObject*) override; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CHairsZone) +#undef script_type_list +#define script_type_list save_type_list(CHairsZone) \ No newline at end of file diff --git a/src/xrGameLA/HairsZone_script.cpp b/src/xrGameLA/HairsZone_script.cpp new file mode 100644 index 000000000..cbab9e858 --- /dev/null +++ b/src/xrGameLA/HairsZone_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "HairsZone.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CHairsZone::script_register (lua_State *L) +{ + module(L) + [ + class_("CHairsZone") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/HangingLamp.cpp b/src/xrGameLA/HangingLamp.cpp new file mode 100644 index 000000000..48cf30701 --- /dev/null +++ b/src/xrGameLA/HangingLamp.cpp @@ -0,0 +1,412 @@ +#include "pch_script.h" +#include "HangingLamp.h" +#include "../LightAnimLibrary.h" +#include "../xr_collide_form.h" +#include "PhysicsShell.h" +#include "Physics.h" +#include "xrserver_objects_alife.h" +#include "PHElement.h" +#include "../Include/xrRender/Kinematics.h" +#include "../Include/xrRender/KinematicsAnimated.h" +#include "game_object_space.h" +#include "script_callback_ex.h" +#include "script_game_object.h" +#include "../../xrNETServer/NET_utils.h" +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CHangingLamp::CHangingLamp () +{ + Init(); +} + +CHangingLamp::~CHangingLamp () +{ +} + +void CHangingLamp::Init() +{ + fHealth = 100.f; + light_bone = BI_NONE; + ambient_bone = BI_NONE; + lanim = 0; + ambient_power = 0.f; + light_render = 0; + light_ambient = 0; + glow_render = 0; + m_bState = 1; +} + +void CHangingLamp::RespawnInit() +{ + Init(); + if(Visual()){ + IKinematics* K = smart_cast(Visual()); + K->LL_SetBonesVisible(u64(-1)); + K->CalculateBones_Invalidate(); + K->CalculateBones (TRUE); + } +} + +void CHangingLamp::Center (Fvector& C) const +{ + if (renderable.visual){ + renderable.xform.transform_tiny(C,renderable.visual->getVisData().sphere.P); + }else{ + C.set (XFORM().c); + } +} + +float CHangingLamp::Radius () const +{ + return (renderable.visual)?renderable.visual->getVisData().sphere.R:EPS; +} + +void CHangingLamp::Load (LPCSTR section) +{ + inherited::Load (section); +} + +void CHangingLamp::net_Destroy() +{ + light_render.destroy (); + light_ambient.destroy (); + glow_render.destroy (); + RespawnInit (); + if(Visual())CPHSkeleton::RespawnInit(); + inherited::net_Destroy (); +} + +BOOL CHangingLamp::net_Spawn(CSE_Abstract* DC) +{ + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeObjectHangingLamp *lamp = smart_cast(e); + R_ASSERT (lamp); + inherited::net_Spawn (DC); + Fcolor clr; + + // set bone id +// CInifile* pUserData = K->LL_UserData(); +// R_ASSERT3 (pUserData,"Empty HangingLamp user data!",lamp->get_visual()); + xr_delete(collidable.model); + if (Visual()){ + IKinematics* K = smart_cast(Visual()); + R_ASSERT (Visual()&&smart_cast(Visual())); + light_bone = K->LL_BoneID (*lamp->light_main_bone); VERIFY(light_bone!=BI_NONE); + ambient_bone = K->LL_BoneID (*lamp->light_ambient_bone); +#ifdef DEBUG + if (!(ambient_bone!=BI_NONE)) { + Msg("ERROR: verify(ambient_bone!=BI_NONE name='%s')",lamp->name());//VERIFY3(ambient_bone!=BI_NONE,"Object name :",lamp->name()); + } +#endif + collidable.model = new CCF_Skeleton(this); + } + fBrightness = lamp->brightness; + clr.set (lamp->color); clr.a = 1.f; + clr.mul_rgb (fBrightness); + + light_render = ::Render->light_create(); + light_render->set_shadow(!!lamp->flags.is(CSE_ALifeObjectHangingLamp::flCastShadow)); + light_render->set_type (lamp->flags.is(CSE_ALifeObjectHangingLamp::flTypeSpot)?IRender_Light::SPOT:IRender_Light::POINT); + light_render->set_range (lamp->range); + light_render->set_color (clr); + light_render->set_cone (lamp->spot_cone_angle); + light_render->set_texture(*lamp->light_texture); + + if (lamp->glow_texture.size()) { + glow_render = ::Render->glow_create(); + glow_render->set_texture(*lamp->glow_texture); + glow_render->set_color (clr); + glow_render->set_radius (lamp->glow_radius); + } + + if (lamp->flags.is(CSE_ALifeObjectHangingLamp::flPointAmbient)){ + ambient_power = lamp->m_ambient_power; + light_ambient = ::Render->light_create(); + light_ambient->set_type (IRender_Light::POINT); + light_ambient->set_shadow(false); + clr.mul_rgb (ambient_power); + light_ambient->set_range(lamp->m_ambient_radius); + light_ambient->set_color(clr); + light_ambient->set_texture(*lamp->m_ambient_texture); + } + + fHealth = lamp->m_health; + + lanim = LALib.FindItem(*lamp->color_animator); + + CPHSkeleton::Spawn(e); + if (smart_cast(Visual())) smart_cast (Visual())->PlayCycle("idle"); + if (smart_cast(Visual())){ + smart_cast (Visual())->CalculateBones_Invalidate (); + smart_cast (Visual())->CalculateBones(TRUE); + //.intepolate_pos + } + if (lamp->flags.is(CSE_ALifeObjectHangingLamp::flPhysic)&&!Visual()) + Msg("! WARNING: lamp, obj name [%s],flag physics set, but has no visual",*cName()); +//. if (lamp->flags.is(CSE_ALifeObjectHangingLamp::flPhysic)&&Visual()&&!guid_physic_bone) fHealth=0.f; + if (Alive() && m_bState) + TurnOn (); + else{ + processing_activate (); // temporal enable + TurnOff (); // -> and here is disable :) + } + + setVisible ((BOOL)!!Visual()); + setEnabled ((BOOL)!!collidable.model); + + return (TRUE); +} + + +void CHangingLamp::SpawnInitPhysics (CSE_Abstract *D) +{ + CSE_ALifeObjectHangingLamp *lamp = smart_cast(D); + if (lamp->flags.is(CSE_ALifeObjectHangingLamp::flPhysic)) CreateBody(lamp); + if (smart_cast(Visual())){ + smart_cast (Visual())->CalculateBones_Invalidate (); + smart_cast (Visual())->CalculateBones(TRUE); + //.intepolate_pos + } +} + +void CHangingLamp::CopySpawnInit () +{ + CPHSkeleton::CopySpawnInit(); + IKinematics* K=smart_cast(Visual()); + if(!K->LL_GetBoneVisible(light_bone)) + TurnOff(); +} +void CHangingLamp::net_Save (NET_Packet& P) +{ + inherited::net_Save(P); + CPHSkeleton::SaveNetState(P); +} + +BOOL CHangingLamp::net_SaveRelevant () +{ + return (inherited::net_SaveRelevant() || BOOL(PPhysicsShell()!=NULL)); +} + +void CHangingLamp:: save (NET_Packet &output_packet) +{ + inherited::save(output_packet); + output_packet.w_u8((u8)m_bState); + +} +void CHangingLamp::load (IReader &input_packet) +{ + inherited::load(input_packet); + m_bState = (u8)input_packet.r_u8(); +} +void CHangingLamp::shedule_Update (u32 dt) +{ + CPHSkeleton::Update(dt); + + + inherited::shedule_Update (dt); +} + +void CHangingLamp::UpdateCL () +{ + inherited::UpdateCL (); + + if(m_pPhysicsShell) + m_pPhysicsShell->InterpolateGlobalTransform(&XFORM()); + + if (Alive() && light_render->get_active()){ + if(Visual()) PKinematics(Visual())->CalculateBones(); + + // update T&R from light (main) bone + Fmatrix xf; + if (light_bone!=BI_NONE){ + Fmatrix& M = smart_cast(Visual())->LL_GetTransform(light_bone); + xf.mul (XFORM(),M); + VERIFY(!fis_zero(DET(xf))); + }else{ + xf.set (XFORM()); + } + light_render->set_rotation (xf.k,xf.i); + light_render->set_position (xf.c); + if (glow_render)glow_render->set_position (xf.c); + + // update T&R from ambient bone + if (light_ambient){ + if (ambient_bone!=light_bone){ + if (ambient_bone!=BI_NONE){ + Fmatrix& M = smart_cast(Visual())->LL_GetTransform(ambient_bone); + xf.mul (XFORM(),M); + VERIFY(!fis_zero(DET(xf))); + }else{ + xf.set (XFORM()); + } + } + light_ambient->set_rotation (xf.k,xf.i); + light_ambient->set_position (xf.c); + } + + if (lanim){ + int frame; + u32 clr = lanim->CalculateBGR(Device.fTimeGlobal,frame); // возвращает в формате BGR + Fcolor fclr; + fclr.set ((float)color_get_B(clr),(float)color_get_G(clr),(float)color_get_R(clr),1.f); + fclr.mul_rgb (fBrightness/255.f); + light_render->set_color (fclr); + if (glow_render) glow_render->set_color (fclr); + if (light_ambient) { + fclr.mul_rgb (ambient_power); + light_ambient->set_color(fclr); + } + } + } +} + +void CHangingLamp::TurnOn () +{ + if (!Alive()) + return; + + Fvector p = XFORM().c; + light_render->set_position (p); + light_render->set_active (true); + if (glow_render) + { + glow_render->set_position (p); + glow_render->set_active (true); + } + if (light_ambient) + { + light_ambient->set_position (p); + light_ambient->set_active (true); + } + if (Visual()) + { + IKinematics* K = smart_cast(Visual()); + K->LL_SetBoneVisible (light_bone, TRUE, TRUE); + K->CalculateBones_Invalidate(); + K->CalculateBones (TRUE); + K->LL_SetBoneVisible (light_bone, TRUE, TRUE); //hack + } + processing_activate (); + m_bState = 1; +} + +void CHangingLamp::TurnOff () +{ + if (!m_bState) + return; + + light_render->set_active (false); + if (glow_render) glow_render->set_active (false); + if (light_ambient) light_ambient->set_active (false); + if (Visual()) + { + IKinematics *K = smart_cast(Visual()); + VERIFY( K ); + K->LL_SetBoneVisible(light_bone, FALSE, TRUE); + VERIFY2( K->LL_GetBonesVisible() != 0, make_string("can not Turn Off lamp: %s, visual %s - because all bones become invisible", cNameVisual().c_str(), cName().c_str() )); + } + processing_deactivate (); + m_bState = 0; + +} + +//void CHangingLamp::Hit(float P,Fvector &dir, CObject* who,s16 element, +// Fvector p_in_object_space, float impulse, ALife::EHitType hit_type) +void CHangingLamp::Hit (SHit* pHDS) +{ + SHit HDS = *pHDS; + callback(GameObject::eHit)( + lua_game_object(), + HDS.power, + HDS.dir, + smart_cast(HDS.who)->lua_game_object(), + HDS.bone() + ); + BOOL bWasAlive = Alive (); + + if(m_pPhysicsShell) m_pPhysicsShell->applyHit(pHDS->p_in_bone_space,pHDS->dir,pHDS->impulse,pHDS->boneID,pHDS->hit_type); + + if (pHDS->boneID==light_bone)fHealth = 0.f; + else fHealth -= pHDS->damage()*100.f; + + if (bWasAlive && (!Alive())) TurnOff (); +} + +static BONE_P_MAP bone_map=BONE_P_MAP(); +void CHangingLamp::CreateBody(CSE_ALifeObjectHangingLamp *lamp) +{ + if (!Visual()) return; + if (m_pPhysicsShell) return; + + IKinematics* pKinematics= smart_cast (Visual()); + + m_pPhysicsShell = P_create_Shell(); + + bone_map .clear(); + LPCSTR fixed_bones=*lamp->fixed_bones; + if(fixed_bones){ + int count = _GetItemCount(fixed_bones); + for (int i=0 ;iLL_BoneID(fixed_bone) ; + R_ASSERT2(BI_NONE!=fixed_bone_id,"wrong fixed bone") ; + bone_map.insert(mk_pair(fixed_bone_id,physicsBone())) ; + } + }else{ + bone_map.insert(mk_pair(pKinematics->LL_GetBoneRoot(),physicsBone())) ; + } + + + + m_pPhysicsShell->build_FromKinematics(pKinematics,&bone_map); + m_pPhysicsShell->set_PhysicsRefObject(this); + m_pPhysicsShell->mXFORM.set(XFORM()); + m_pPhysicsShell->Activate(true);//, + //m_pPhysicsShell->SmoothElementsInertia(0.3f); + m_pPhysicsShell->SetAirResistance();//0.0014f,1.5f + +///////////////////////////////////////////////////////////////////////////// + BONE_P_PAIR_IT i=bone_map.begin(),e=bone_map.end(); + for(;i!=e;i++){ + CPhysicsElement* fixed_element=i->second.element; + ///R_ASSERT2(fixed_element,"fixed bone has no physics"); + if(fixed_element)fixed_element->Fix(); + } + + m_pPhysicsShell->mXFORM.set(XFORM()); + m_pPhysicsShell->SetAirResistance(0.001f, 0.02f); + SAllDDOParams disable_params; + disable_params.Load(smart_cast(Visual())->LL_UserData()); + m_pPhysicsShell->set_DisableParams(disable_params); + ApplySpawnIniToPhysicShell(&lamp->spawn_ini(),m_pPhysicsShell,fixed_bones[0]!='\0'); +} + +void CHangingLamp::net_Export(NET_Packet& P) +{ + VERIFY (Local()); +} + +void CHangingLamp::net_Import(NET_Packet& P) +{ + VERIFY (Remote()); +} + +BOOL CHangingLamp::UsedAI_Locations() +{ + return (FALSE); +} + +#pragma optimize("s",on) +void CHangingLamp::script_register(lua_State *L) +{ + luabind::module(L) + [ + luabind::class_("hanging_lamp") + .def(luabind::constructor<>()) + .def("turn_on", &CHangingLamp::TurnOn) + .def("turn_off", &CHangingLamp::TurnOff) + ]; +} diff --git a/src/xrGameLA/HangingLamp.h b/src/xrGameLA/HangingLamp.h new file mode 100644 index 000000000..0ee67b67a --- /dev/null +++ b/src/xrGameLA/HangingLamp.h @@ -0,0 +1,79 @@ +// DummyObject.h: interface for the CHangingLamp class. +// +////////////////////////////////////////////////////////////////////// + +#ifndef HangingLampH +#define HangingLampH +#pragma once + +#include "gameobject.h" +#include "physicsshellholder.h" +#include "PHSkeleton.h" +#include "script_export_space.h" +// refs +class CLAItem; +class CPhysicsElement; +class CSE_ALifeObjectHangingLamp; +class CPHElement; +class CHangingLamp: +public CPhysicsShellHolder, +public CPHSkeleton +{//need m_pPhysicShell + typedef CPhysicsShellHolder inherited; +private: + u16 light_bone; + u16 ambient_bone; + + ref_light light_render; + ref_light light_ambient; + CLAItem* lanim; + float ambient_power; + BOOL m_bState; + + ref_glow glow_render; + + float fHealth; + float fBrightness; + void CreateBody (CSE_ALifeObjectHangingLamp *lamp); + void Init(); + void RespawnInit (); + bool Alive (){return fHealth>0.f;} + + +public: + CHangingLamp (); + virtual ~CHangingLamp (); + void TurnOn (); + void TurnOff (); + virtual void Load ( LPCSTR section); + virtual BOOL net_Spawn ( CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void shedule_Update ( u32 dt); // Called by sheduler + virtual void UpdateCL ( ); // Called each frame, so no need for dt + + + virtual void SpawnInitPhysics (CSE_Abstract *D) ; + virtual CPhysicsShellHolder* PPhysicsShellHolder () {return PhysicsShellHolder();} ; + virtual void CopySpawnInit () ; + virtual void net_Save (NET_Packet& P) ; + virtual BOOL net_SaveRelevant (); + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + + virtual BOOL renderable_ShadowGenerate ( ) { return TRUE; } + virtual BOOL renderable_ShadowReceive ( ) { return TRUE; } + + virtual void Hit (SHit* pHDS); + virtual void net_Export (NET_Packet& P); + virtual void net_Import (NET_Packet& P); + virtual BOOL UsedAI_Locations(); + + virtual void Center (Fvector& C) const; + virtual float Radius () const; + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CHangingLamp) +#undef script_type_list +#define script_type_list save_type_list(CHangingLamp) + +#endif //HangingLampH diff --git a/src/xrGameLA/Helicopter.cpp b/src/xrGameLA/Helicopter.cpp new file mode 100644 index 000000000..ef767cc14 --- /dev/null +++ b/src/xrGameLA/Helicopter.cpp @@ -0,0 +1,510 @@ +#include "pch_script.h" +#include "helicopter.h" +#include "xrserver_objects_alife.h" +#include "PhysicsShell.h" +#include "level.h" +#include "ai_sounds.h" +#include "../Include/xrRender/Kinematics.h" +#include "../Include/xrRender/KinematicsAnimated.h" +#include "clsid_game.h" + +#include "script_callback_ex.h" +#include "game_object_space.h" +#include "script_game_object.h" +#include "../LightAnimLibrary.h" +#include "ui_base.h" +#include "physicscommon.h" +//50fps fixed +float STEP=0.02f; + +CHelicopter::CHelicopter() +{ + m_pParticle = NULL; + m_light_render = NULL; + m_lanim = NULL; + + ISpatial* self = smart_cast (this); + if (self) self->spatial.type |= STYPE_VISIBLEFORAI; + + m_movement.parent = this; + m_body.parent = this; +} + +CHelicopter::~CHelicopter() +{ + HUD_SOUND_ITEM::DestroySound (m_sndShot); + HUD_SOUND_ITEM::DestroySound (m_sndShotRocket); +} + +void CHelicopter::setState(CHelicopter::EHeliState s) +{ + m_curState = s; +} + + + +void CHelicopter::init() +{ + m_cur_rot.set (0.0f,0.0f); + m_tgt_rot.set (0.0f,0.0f); + m_bind_rot.set (0.0f,0.0f); + + m_allow_fire = FALSE; + m_use_rocket_on_attack = TRUE; + m_use_mgun_on_attack = TRUE; + m_syncronize_rocket = TRUE; + m_min_rocket_dist = 20.0f; + m_max_rocket_dist = 200.0f; + m_time_between_rocket_attack = 0; + m_last_rocket_attack = Device.dwTimeGlobal; + + SetfHealth (1.0f); +} + +DLL_Pure* CHelicopter::_construct() +{ + CEntity::_construct (); + init (); + return this; +} + +void CHelicopter::reinit(){ + inherited::reinit (); + m_movement.reinit (); + m_body.reinit (); + m_enemy.reinit (); +}; + + +void CHelicopter::Load(LPCSTR section) +{ + inherited::Load (section); + m_movement.Load (section); + m_body.Load (section); + m_enemy.Load (section); + + + m_death_ang_vel = pSettings->r_fvector3(section,"death_angular_vel"); + m_death_lin_vel_k = pSettings->r_float(section,"death_lin_vel_koeff"); + + CHitImmunity::LoadImmunities (pSettings->r_string(section,"immunities_sect"),pSettings); + + //weapons + CShootingObject::Load (section); + HUD_SOUND_ITEM::LoadSound (section,"snd_shoot", m_sndShot, SOUND_TYPE_WEAPON_SHOOTING); + HUD_SOUND_ITEM::LoadSound (section,"snd_shoot_rocket", m_sndShotRocket, SOUND_TYPE_WEAPON_SHOOTING); + CRocketLauncher::Load (section); + + UseFireTrail (m_enemy.bUseFireTrail);//temp force reloar disp params + + m_sAmmoType = pSettings->r_string(section, "ammo_class"); + m_CurrentAmmo.Load (*m_sAmmoType, 0); + + m_sRocketSection = pSettings->r_string (section,"rocket_class"); + + + m_use_rocket_on_attack = !!pSettings->r_bool(section,"use_rocket"); + m_use_mgun_on_attack = !!pSettings->r_bool(section,"use_mgun"); + m_min_rocket_dist = pSettings->r_float(section,"min_rocket_attack_dist"); + m_max_rocket_dist = pSettings->r_float(section,"max_rocket_attack_dist"); + m_min_mgun_dist = pSettings->r_float(section,"min_mgun_attack_dist"); + m_max_mgun_dist = pSettings->r_float(section,"max_mgun_attack_dist"); + m_time_between_rocket_attack = pSettings->r_u32(section,"time_between_rocket_attack"); + m_syncronize_rocket = !!pSettings->r_bool(section,"syncronize_rocket"); + m_barrel_dir_tolerance = pSettings->r_float(section,"barrel_dir_tolerance"); + +//lighting & sounds + m_smoke_particle = pSettings->r_string(section,"smoke_particle"); + + m_light_range = pSettings->r_float(section,"light_range"); + m_light_brightness = pSettings->r_float(section,"light_brightness"); + + m_light_color = pSettings->r_fcolor(section,"light_color"); + m_light_color.a = 1.f; + m_light_color.mul_rgb (m_light_brightness); + LPCSTR lanim = pSettings->r_string (section,"light_color_animmator"); + m_lanim = LALib.FindItem(lanim); + + +} + +void CHelicopter::reload(LPCSTR section) +{ + inherited::reload (section); +} + +void CollisionCallbackAlife(bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ do_colide=false; } + +void ContactCallbackAlife(CDB::TRI* T,dContactGeom* c) +{ +} +BOOL CHelicopter::net_Spawn(CSE_Abstract* DC) +{ + + SetfHealth(100.0f); + setState(CHelicopter::eAlive); + m_flame_started =false; + m_light_started =false; + m_exploded =false; + m_ready_explode =false; + m_dead =false; + + if (!inherited::net_Spawn(DC)) + return (FALSE); + + CPHSkeleton::Spawn((CSE_Abstract*)(DC)); + for(u32 i=0; i<4; ++i) + CRocketLauncher::SpawnRocket(*m_sRocketSection, smart_cast(this)); + + // assigning m_animator here + CSE_Abstract *abstract =(CSE_Abstract*)(DC); + CSE_ALifeHelicopter *heli = smart_cast(abstract); + VERIFY (heli); + + R_ASSERT (Visual()&&smart_cast(Visual())); + IKinematics* K = smart_cast(Visual()); + CInifile* pUserData = K->LL_UserData(); + + m_rotate_x_bone = K->LL_BoneID (pUserData->r_string("helicopter_definition","wpn_rotate_x_bone")); + m_rotate_y_bone = K->LL_BoneID (pUserData->r_string("helicopter_definition","wpn_rotate_y_bone")); + m_fire_bone = K->LL_BoneID (pUserData->r_string("helicopter_definition","wpn_fire_bone")); + m_death_bones_to_hide = pUserData->r_string("on_death_mode","scale_bone"); + m_left_rocket_bone = K->LL_BoneID (pUserData->r_string("helicopter_definition","left_rocket_bone")); + m_right_rocket_bone = K->LL_BoneID (pUserData->r_string("helicopter_definition","right_rocket_bone")); + + m_smoke_bone = K->LL_BoneID (pUserData->r_string("helicopter_definition","smoke_bone")); + m_light_bone = K->LL_BoneID (pUserData->r_string("helicopter_definition","light_bone")); + + CExplosive::Load (pUserData,"explosion"); + CExplosive::SetInitiator(ID()); + + LPCSTR s = pUserData->r_string("helicopter_definition","hit_section"); + + if( pUserData->section_exist(s) ){ + int lc = pUserData->line_count(s); + LPCSTR name; + LPCSTR value; + s16 boneID; + for (int i=0 ;ir_line( s, i, &name, &value); + boneID =K->LL_BoneID(name); + m_hitBones.insert( std::make_pair(boneID, (float)atof(value)) ); + } + } + + CBoneInstance& biX = smart_cast(Visual())->LL_GetBoneInstance(m_rotate_x_bone); + biX.set_callback (bctCustom,BoneMGunCallbackX,this); + CBoneInstance& biY = smart_cast(Visual())->LL_GetBoneInstance(m_rotate_y_bone); + biY.set_callback (bctCustom,BoneMGunCallbackY,this); + CBoneData& bdX = K->LL_GetData(m_rotate_x_bone); VERIFY(bdX.IK_data.type==jtJoint); + m_lim_x_rot.set (bdX.IK_data.limits[0].limit.x,bdX.IK_data.limits[0].limit.y); + CBoneData& bdY = K->LL_GetData(m_rotate_y_bone); VERIFY(bdY.IK_data.type==jtJoint); + m_lim_y_rot.set (bdY.IK_data.limits[1].limit.x,bdY.IK_data.limits[1].limit.y); + + xr_vector matrices; + K->LL_GetBindTransform (matrices); + m_i_bind_x_xform.invert (matrices[m_rotate_x_bone]); + m_i_bind_y_xform.invert (matrices[m_rotate_y_bone]); + m_bind_rot.x = matrices[m_rotate_x_bone].k.getP(); + m_bind_rot.y = matrices[m_rotate_y_bone].k.getH(); + m_bind_x.set (matrices[m_rotate_x_bone].c); + m_bind_y.set (matrices[m_rotate_y_bone].c); + + IKinematicsAnimated *A = smart_cast(Visual()); + if (A) { + A->PlayCycle (*heli->startup_animation); + K->CalculateBones (TRUE); + } + + m_engineSound.create (*heli->engine_sound,st_Effect,sg_SourceType); + m_engineSound.play_at_pos (0,XFORM().c,sm_Looped); + + CShootingObject::Light_Create (); + + + setVisible (TRUE); + setEnabled (TRUE); + + + + m_stepRemains = 0.0f; + +//lighting + m_light_render = ::Render->light_create(); + m_light_render->set_shadow (false); + m_light_render->set_type (IRender_Light::POINT); + m_light_render->set_range (m_light_range); + m_light_render->set_color (m_light_color); + + if(g_Alive())processing_activate (); + TurnEngineSound(false); + if(pUserData->section_exist("destroyed")) + CPHDestroyable::Load(pUserData,"destroyed"); +#ifdef DEBUG + Device.seqRender.Add(this,REG_PRIORITY_LOW-1); +#endif + + return TRUE; +} + +void CHelicopter::net_Destroy() +{ + inherited::net_Destroy (); + CExplosive::net_Destroy (); + CShootingObject::Light_Destroy (); + CShootingObject::StopFlameParticles (); + CPHSkeleton::RespawnInit (); + CPHDestroyable::RespawnInit (); + m_engineSound.stop (); + m_brokenSound.stop (); + CParticlesObject::Destroy (m_pParticle); + m_light_render.destroy (); + m_movement.net_Destroy (); +#ifdef DEBUG + Device.seqRender.Remove(this); +#endif + +} + +void CHelicopter::SpawnInitPhysics (CSE_Abstract *D) +{ + + PPhysicsShell()=P_build_Shell (this,false); + if(g_Alive()) + { + PPhysicsShell()->EnabledCallbacks (FALSE); + PPhysicsShell()->set_ObjectContactCallback (CollisionCallbackAlife); + PPhysicsShell()->set_ContactCallback (ContactCallbackAlife); + PPhysicsShell()->Disable (); + } +} + +void CHelicopter::net_Save (NET_Packet& P) +{ + inherited::net_Save (P); + CPHSkeleton::SaveNetState (P); +} + +float GetCurrAcc(float V0, float V1, float dist, float a0, float a1); + +void CHelicopter::MoveStep() +{ + Fvector dir, pathDir; + float desired_H = m_movement.currPathH; + float desired_P; + if(m_movement.type != eMovNone){ + + float dist = m_movement.currP.distance_to(m_movement.desiredPoint); + + dir.sub(m_movement.desiredPoint,m_movement.currP); + dir.normalize_safe(); + pathDir = dir; + dir.getHP(desired_H, desired_P); + float speed_ = _min(m_movement.GetSpeedInDestPoint(), GetMaxVelocity() ); + + static float ang = pSettings->r_float (cNameSect(),"magic_angle"); + if(m_movement.curLinearSpeed>GetMaxVelocity() || angle_difference(m_movement.currPathH,desired_H)>ang) + m_movement.curLinearAcc = -m_movement.LinearAcc_bk; + else + m_movement.curLinearAcc = GetCurrAcc( m_movement.curLinearSpeed, + speed_, + dist*0.95f, + m_movement.LinearAcc_fw, + -m_movement.LinearAcc_bk); + + + angle_lerp (m_movement.currPathH, desired_H, m_movement.GetAngSpeedHeading(m_movement.curLinearSpeed), STEP); + angle_lerp (m_movement.currPathP, desired_P, m_movement.GetAngSpeedPitch(m_movement.curLinearSpeed), STEP); + + dir.setHP(m_movement.currPathH, m_movement.currPathP); + + float vp = m_movement.curLinearSpeed*STEP+(m_movement.curLinearAcc*STEP*STEP)/2.0f; + m_movement.currP.mad (dir, vp); + m_movement.curLinearSpeed += m_movement.curLinearAcc*STEP; + static bool aaa = false; + if(aaa) + Log("1-m_movement.curLinearSpeed=",m_movement.curLinearSpeed); + clamp(m_movement.curLinearSpeed,0.0f,1000.0f); + if(aaa) + Log("2-m_movement.curLinearSpeed=",m_movement.curLinearSpeed); + }else{ //go stopping + if( !fis_zero(m_movement.curLinearSpeed) ){ + m_movement.curLinearAcc = -m_movement.LinearAcc_bk; + + float vp = m_movement.curLinearSpeed*STEP+(m_movement.curLinearAcc*STEP*STEP)/2.0f; + dir.setHP(m_movement.currPathH, m_movement.currPathP); + dir.normalize_safe(); + m_movement.currP.mad (dir, vp); + m_movement.curLinearSpeed += m_movement.curLinearAcc*STEP; + clamp(m_movement.curLinearSpeed,0.0f,1000.0f); +// clamp(m_movement.curLinearSpeed,0.0f,m_movement.maxLinearSpeed); + + }else{ + m_movement.curLinearAcc = 0.0f; + m_movement.curLinearSpeed = 0.0f; + } + + }; + + if( m_body.b_looking_at_point){ + Fvector desired_dir; + desired_dir.sub(m_body.looking_point, m_movement.currP ).normalize_safe(); + + float center_desired_H,tmp_P; + desired_dir.getHP(center_desired_H, tmp_P); + angle_lerp (m_body.currBodyHPB.x, center_desired_H, m_movement.GetAngSpeedHeading(m_movement.curLinearSpeed), STEP); + }else{ + angle_lerp (m_body.currBodyHPB.x, m_movement.currPathH, m_movement.GetAngSpeedHeading(m_movement.curLinearSpeed), STEP); + } + + + float needBodyP = -m_body.model_pitch_k*m_movement.curLinearSpeed; + if(m_movement.curLinearAcc < 0) needBodyP*=-1; + angle_lerp (m_body.currBodyHPB.y, needBodyP, m_body.model_angSpeedPitch, STEP); + + + float sign; + Fvector cp; + cp.crossproduct (pathDir,dir); + (cp.y>0.0)?sign=1.0f:sign=-1.0f; + float ang_diff = angle_difference (m_movement.currPathH, desired_H); + + float needBodyB = -ang_diff*sign*m_body.model_bank_k*m_movement.curLinearSpeed; + angle_lerp (m_body.currBodyHPB.z, needBodyB, m_body.model_angSpeedBank, STEP); + + + XFORM().setHPB(m_body.currBodyHPB.x,m_body.currBodyHPB.y,m_body.currBodyHPB.z); + + XFORM().translate_over(m_movement.currP); +} + +void CHelicopter::UpdateCL() +{ + inherited::UpdateCL (); + CExplosive::UpdateCL(); + if(PPhysicsShell() && (state() == CHelicopter::eDead) ){ + + PPhysicsShell()->InterpolateGlobalTransform(&XFORM()); + + IKinematics* K = smart_cast(Visual()); + K->CalculateBones (); + //smoke + UpdateHeliParticles(); + + if(m_brokenSound._feedback()) + m_brokenSound.set_position(XFORM().c); + + + return; + } + else + PPhysicsShell()->SetTransform(XFORM()); + + m_movement.Update(); + + m_stepRemains+=Device.fTimeDelta; + while(m_stepRemains>STEP){ + MoveStep(); + m_stepRemains-=STEP; + } + +#ifdef DEBUG + if(bDebug){ + CGameFont* F = UI().Font().pFontDI; + F->SetAligment (CGameFont::alCenter); +// F->SetSizeI (0.02f); + F->OutSetI (0.f,-0.8f); + F->SetColor (0xffffffff); + F->OutNext ("Heli: speed=%4.4f acc=%4.4f dist=%4.4f",m_movement.curLinearSpeed, m_movement.curLinearAcc, m_movement.GetDistanceToDestPosition()); + } +#endif + + if(m_engineSound._feedback()) + m_engineSound.set_position(XFORM().c); + + + + m_enemy.Update(); + //weapon + UpdateWeapons(); + UpdateHeliParticles(); + + IKinematics* K = smart_cast(Visual()); + K->CalculateBones (); +} + +void CHelicopter::shedule_Update(u32 time_delta) +{ + if (!getEnabled()) return; + + inherited::shedule_Update (time_delta); + if(CPHDestroyable::Destroyed())CPHDestroyable::SheduleUpdate(time_delta); + else CPHSkeleton::Update(time_delta); + + if(state() != CHelicopter::eDead){ + for(u32 i=getRocketCount(); i<4; ++i) + CRocketLauncher::SpawnRocket(*m_sRocketSection, this); + } + if(m_ready_explode)ExplodeHelicopter(); +} + + + +void CHelicopter::goPatrolByPatrolPath (LPCSTR path_name, int start_idx) +{ + m_movement.goPatrolByPatrolPath (path_name, start_idx); +} + + +void CHelicopter::goByRoundPath(Fvector center, float radius, bool clockwise) +{ + m_movement.goByRoundPath(center, radius, clockwise); +} + +void CHelicopter::LookAtPoint(Fvector point, bool do_it) +{ + m_body.LookAtPoint(point,do_it); +} + +void CHelicopter::save(NET_Packet &output_packet) +{ + m_movement.save (output_packet); + m_body.save (output_packet); + m_enemy.save (output_packet); + output_packet.w_vec3(XFORM().c); + output_packet.w_float(m_barrel_dir_tolerance); + save_data (m_use_rocket_on_attack, output_packet); + save_data (m_use_mgun_on_attack, output_packet); + save_data (m_min_rocket_dist, output_packet); + save_data (m_max_rocket_dist, output_packet); + save_data (m_min_mgun_dist, output_packet); + save_data (m_max_mgun_dist, output_packet); + save_data (m_time_between_rocket_attack, output_packet); + save_data (m_syncronize_rocket, output_packet); +} + +void CHelicopter::load(IReader &input_packet) +{ + m_movement.load (input_packet); + m_body.load (input_packet); + m_enemy.load (input_packet); + input_packet.r_fvector3 (XFORM().c); + m_barrel_dir_tolerance = input_packet.r_float(); + UseFireTrail (m_enemy.bUseFireTrail);//force reloar disp params + + + load_data (m_use_rocket_on_attack, input_packet); + load_data (m_use_mgun_on_attack, input_packet); + load_data (m_min_rocket_dist, input_packet); + load_data (m_max_rocket_dist, input_packet); + load_data (m_min_mgun_dist, input_packet); + load_data (m_max_mgun_dist, input_packet); + load_data (m_time_between_rocket_attack, input_packet); + load_data (m_syncronize_rocket, input_packet); +} +void CHelicopter::net_Relcase(CObject* O ) +{ + CExplosive::net_Relcase(O); + inherited::net_Relcase(O); +} \ No newline at end of file diff --git a/src/xrGameLA/Helicopter2.cpp b/src/xrGameLA/Helicopter2.cpp new file mode 100644 index 000000000..f2dd8c647 --- /dev/null +++ b/src/xrGameLA/Helicopter2.cpp @@ -0,0 +1,471 @@ +#include "pch_script.h" +#include "helicopter.h" +#include "level.h" +#include "script_game_object.h" +#include "game_object_space.h" +#include "../Include/xrRender/Kinematics.h" +#include "../LightAnimLibrary.h" +#include "PhysicsShell.h" +#include "clsid_game.h" +#include "script_callback_ex.h" +#include "ai/stalker/ai_stalker.h" +#include "CustomZone.h" +#include "actor.h" +#include "MathUtils.h" + +bool CHelicopter::isObjectVisible (CObject* O) +{ + Fvector dir_to_object; + Fvector to_point; + O->Center (to_point); + Fvector from_point = XFORM().c; + dir_to_object.sub (to_point,from_point).normalize_safe(); + float ray_length = from_point.distance_to(to_point); + + BOOL res = Level().ObjectSpace.RayTest(from_point, dir_to_object, ray_length, collide::rqtStatic, NULL, NULL); + + return !res; +} + +bool CHelicopter::isVisible (CScriptGameObject* O) +{ + return isObjectVisible (&O->object()); +} + +void CHelicopter::TurnLighting(bool bOn) +{ + m_light_render->set_active (bOn); + m_light_started = bOn; + +} +void CHelicopter::TurnEngineSound(bool bOn) +{ + if(bOn) + m_engineSound.set_volume(1.0f); + else + m_engineSound.set_volume(0.0f); + +} + +void CHelicopter::StartFlame () +{ + if(m_pParticle)return; + m_pParticle = CParticlesObject::Create(*m_smoke_particle,FALSE); + + Fvector zero_vector; + zero_vector.set(0.f,0.f,0.f); + m_pParticle->UpdateParent(m_particleXFORM, zero_vector ); + m_pParticle->Play(false); + m_flame_started = true; +} + +void CHelicopter::UpdateHeliParticles () +{ + IKinematics* K = smart_cast(Visual()); + m_particleXFORM = K->LL_GetTransform(m_smoke_bone); + m_particleXFORM.mulA_43(XFORM()); + + if (m_pParticle){ + + Fvector vel; + + Fvector last_pos = PositionStack.back().vPosition; + vel.sub(Position(), last_pos); + vel.mul(5.0f); + + m_pParticle->UpdateParent(m_particleXFORM, vel ); + } +//lighting + if(m_light_render->get_active()){ + Fmatrix xf; + Fmatrix& M = K->LL_GetTransform(u16(m_light_bone)); + xf.mul (XFORM(),M); + VERIFY(!fis_zero(DET(xf))); + + m_light_render->set_rotation (xf.k,xf.i); + m_light_render->set_position (xf.c); + + if (m_lanim) + { + int frame; + u32 clr = m_lanim->CalculateBGR(Device.fTimeGlobal,frame); // тючтЁр•рхЄ т ЇюЁьрЄх BGR + Fcolor fclr; + fclr.set ((float)color_get_B(clr),(float)color_get_G(clr),(float)color_get_R(clr),1.f); + fclr.mul_rgb (m_light_brightness/255.f); + m_light_render->set_color (fclr); + } + + } +} +void CHelicopter::ExplodeHelicopter () +{ + m_ready_explode=false; + m_exploded = true; + if(m_pParticle){ + m_pParticle->Stop(); + CParticlesObject::Destroy(m_pParticle); + } + if(CPHDestroyable::CanDestroy()) + CPHDestroyable::Destroy(ID(),"physic_destroyable_object"); + + CExplosive::SetInitiator(ID()); + CExplosive::GenExplodeEvent(Position(),Fvector().set(0.f,1.f,0.f)); + m_brokenSound.stop (); + +} + +void CHelicopter::SetDestPosition (Fvector* pos) +{ + m_movement.SetDestPosition(pos); + if(bDebug) + Msg("---SetDestPosition %f %f %f", pos->x, pos->y, pos->z); +} + +float CHelicopter::GetDistanceToDestPosition() +{ + return m_movement.GetDistanceToDestPosition(); +} + +void CHelicopter::UnSetEnemy() +{ + m_enemy.type = eEnemyNone; +} + +void CHelicopter::SetEnemy(CScriptGameObject* e) +{ + m_enemy.type = eEnemyEntity; + m_enemy.destEnemyID = e->ID(); +} + +void CHelicopter::SetEnemy(Fvector* pos) +{ + m_enemy.type = eEnemyPoint; + m_enemy.destEnemyPos = *pos; +} + +float CHelicopter::GetCurrVelocity() +{ + return m_movement.curLinearSpeed; +} + +void CHelicopter::SetMaxVelocity(float v) +{ + m_movement.maxLinearSpeed = v; +} +float CHelicopter::GetMaxVelocity() +{ + return m_movement.maxLinearSpeed; +} +//////////////////////Start By JoHnY/////////////////////// +void CHelicopter::SetLinearAcc(float LAcc_fw, float LAcc_bw) +{ + m_movement.LinearAcc_fw = LAcc_fw; //ускорение разгона + m_movement.LinearAcc_bk = LAcc_bw; //ускорение торможения + +} +//////////////////////End By JoHnY///////////////////////// +void CHelicopter::SetSpeedInDestPoint(float sp) +{ + m_movement.SetSpeedInDestPoint(sp); + if(bDebug) + Msg("---SetSpeedInDestPoint %f", sp); +} + +float CHelicopter::GetSpeedInDestPoint(float sp) +{ + return m_movement.GetSpeedInDestPoint(); +} +void CHelicopter::SetOnPointRangeDist(float d) +{ + m_movement.onPointRangeDist = d; + if(bDebug) + Msg("---SetOnPointRangeDist %f", d); +} + +float CHelicopter::GetOnPointRangeDist() +{ + return m_movement.onPointRangeDist; +} + +float CHelicopter::GetRealAltitude() +{ + collide::rq_result cR; + Fvector down_dir; + + down_dir.set(0.0f, -1.0f, 0.0f); + + + Level().ObjectSpace.RayPick(XFORM().c, down_dir, 1000.0f, collide::rqtStatic, cR, NULL); + + return cR.range; +} + +void CHelicopter::Hit (SHit* pHDS) +{ +// inherited::Hit(pHDS); + + + if(GetfHealth()<0.005f) + return; + + if(state() == CHelicopter::eDead ) return; + + if(pHDS->who==this) + return; + + bonesIt It = m_hitBones.find(pHDS->bone()); + if(It != m_hitBones.end() && pHDS->hit_type==ALife::eHitTypeFireWound) { + float curHealth = GetfHealth(); + curHealth -= pHDS->damage()*It->second*1000.0f; + SetfHealth(curHealth); +#ifdef DEBUG + if (bDebug) Log("----Helicopter::PilotHit(). health=",curHealth); +#endif + }else { + float hit_power = pHDS->damage(); + hit_power *= GetHitImmunity(pHDS->hit_type); + + SetfHealth(GetfHealth()-hit_power); +#ifdef DEBUG + if (bDebug) + Log("----Helicopter::Hit(). health=",GetfHealth()); +#endif + }; + if (pHDS->who&& + ( smart_cast(pHDS->who) || + smart_cast(pHDS->who) || + smart_cast(pHDS->who) ) + ){ + callback(GameObject::eHelicopterOnHit)(pHDS->damage(),pHDS->impulse,pHDS->hit_type,pHDS->who->ID()); + } + + CPHDestroyable::SetFatalHit(*pHDS); + +} + +void CHelicopter::PHHit(float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type) +{ + if(!g_Alive())inherited::PHHit(P,dir,who,element,p_in_object_space,impulse,hit_type); +} + + +#include "group_hierarchy_holder.h" +#include "seniority_hierarchy_holder.h" +#include "team_hierarchy_holder.h" +#include "squad_hierarchy_holder.h" + +#include "extendedgeom.h" +void CollisionCallbackDead(bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + do_colide=true; + + CHelicopter *l_this = bo1 ? smart_cast(retrieveGeomUserData(c.geom.g1)->ph_ref_object) : smart_cast(retrieveGeomUserData(c.geom.g2)->ph_ref_object); + + if(l_this&& !l_this->m_exploded) + l_this->m_ready_explode=true; + +} + +void CHelicopter::DieHelicopter() +{ + if ( state() == CHelicopter::eDead ) + return; + CEntity::Die(NULL); + + m_engineSound.stop (); + + m_brokenSound.create (pSettings->r_string(*cNameSect(), "broken_snd"),st_Effect,sg_SourceType); + m_brokenSound.play_at_pos (0,XFORM().c,sm_Looped); + + + IKinematics* K = smart_cast(Visual()); + if(true /*!PPhysicsShell()*/){ + string256 I; + LPCSTR bone; + + u16 bone_id; + for (u32 i=0, n=_GetItemCount(*m_death_bones_to_hide); iLL_BoneID (bone); + K->LL_SetBoneVisible(bone_id,FALSE,TRUE); + } + + ///PPhysicsShell()=P_build_Shell (this,false); + PPhysicsShell()->EnabledCallbacks(TRUE); + PPhysicsShell()->set_ObjectContactCallback(CollisionCallbackDead); + PPhysicsShell()->set_ContactCallback(ContactShotMark); + } + Fvector lin_vel; + + Fvector prev_pos = PositionStack.front().vPosition; + lin_vel.sub (XFORM().c,prev_pos); + + if(Device.dwTimeGlobal != PositionStack.front().dwTime) + lin_vel.div((Device.dwTimeGlobal-PositionStack.front().dwTime)/1000.0f); + + lin_vel.mul (m_death_lin_vel_k); + PPhysicsShell()->set_LinearVel (lin_vel); + PPhysicsShell()->set_AngularVel (m_death_ang_vel); + PPhysicsShell()->Enable (); + K->CalculateBones_Invalidate (); + K->CalculateBones (TRUE); + setState (CHelicopter::eDead); + m_engineSound.stop (); + processing_deactivate (); + m_dead = true; +} + +void SHeliEnemy::Load(LPCSTR section) +{ + fire_trail_length_des = pSettings->r_float(section, "fire_trail_length"); + bUseFireTrail = !!pSettings->r_bool(section, "use_fire_trail"); +} + +void SHeliEnemy::reinit() +{ + type = eEnemyNone; + destEnemyPos.set (0.0f,0.0f,0.0f); + destEnemyID =u16(-1); + fStartFireTime =-1.0f; +} + +void SHeliEnemy::Update() +{ + switch(type){ + case eEnemyNone: + case eEnemyPoint: + break; + case eEnemyEntity:{ + CObject* O = Level().Objects.net_Find(destEnemyID); + if(O) O->Center( destEnemyPos ); + else type = eEnemyNone; + }break; + default: + NODEFAULT; + }; +} + +void SHeliEnemy::save(NET_Packet &output_packet) +{ + output_packet.w_s16 ((s16)type); + output_packet.w_vec3 (destEnemyPos); + output_packet.w_u16 (destEnemyID); + + output_packet.w_float (fire_trail_length_des); + output_packet.w_u8 (bUseFireTrail ? 1 : 0); +} + +void SHeliEnemy::load(IReader &input_packet) +{ + type = (EHeliHuntState)input_packet.r_s16(); + input_packet.r_fvector3 (destEnemyPos); + destEnemyID = input_packet.r_u16(); + + fire_trail_length_des = input_packet.r_float(); + bUseFireTrail = !!input_packet.r_u8(); +} + +void CHelicopter::SetFireTrailLength(float val) +{ + m_enemy.fire_trail_length_des = val; +} +bool CHelicopter::UseFireTrail() +{ + return m_enemy.bUseFireTrail; +} + +void CHelicopter::UseFireTrail(bool val) +{ + m_enemy.bUseFireTrail = val; + if(val){ + fireDispersionBase = pSettings->r_float (*cNameSect(),"fire_dispersion_null"); + fireDispersionBase = deg2rad (fireDispersionBase); + }else{ + fireDispersionBase = pSettings->r_float (*cNameSect(),"fire_dispersion_base"); + fireDispersionBase = deg2rad (fireDispersionBase); + } +} + + +void SHeliBodyState::Load(LPCSTR section) +{ + model_angSpeedBank = pSettings->r_float(section,"model_angular_sp_bank"); + model_angSpeedPitch = pSettings->r_float(section,"model_angular_sp_pitch"); + model_pitch_k = pSettings->r_float(section,"model_pitch_koef"); + model_bank_k = pSettings->r_float(section,"model_bank_koef"); +} + +void SHeliBodyState::reinit() +{ + type = eBodyByPath; + b_looking_at_point = false; + looking_point.set(0.0f,0.0f,0.0f); + parent->XFORM().getHPB(currBodyHPB.x, currBodyHPB.y, currBodyHPB.z); + +} + +void SHeliBodyState::LookAtPoint (Fvector point, bool do_it) +{ + b_looking_at_point = do_it; + looking_point = point; + type = (do_it)?eBodyToPoint:eBodyByPath; +} + +void SHeliBodyState::save(NET_Packet &output_packet) +{ + output_packet.w_s16((s16)type); + output_packet.w_u8(b_looking_at_point ? 1 : 0); + output_packet.w_float(currBodyHPB.x); + output_packet.w_float(currBodyHPB.y); + output_packet.w_float(currBodyHPB.z); +} + +void SHeliBodyState::load(IReader &input_packet) +{ + type = (EHeliBodyState)input_packet.r_s16(); + b_looking_at_point = !!input_packet.r_u8(); + currBodyHPB.x = input_packet.r_float(); + currBodyHPB.y = input_packet.r_float(); + currBodyHPB.z = input_packet.r_float(); +} + + + +float t_xx (float V0, float V1, float a0, float a1, float d, float fSign) +{ + return (V1+_sqrt(V1*V1 - (a1/(a1-a0))*(V1*V1-V0*V0-2*a0*d) )*fSign )/a1; +} + +float t_1(float t10, float t11) +{ + if(t10<0) + return t11; + else + if(t11<0) + return t10; + else + return _min(t10,t11); + +} + +float t_0(float V0, float V1, float a0, float a1, float t1) +{ + return (V1-V0-a1*t1)/a0; +} + +float getA(float t0, float a1, float a0) +{ + float eps = 0.001f; + return (t0(currPatrolPath); + xr_delete( tmp ); + } +} + +void SHeliMovementState::Load(LPCSTR section) +{ + float angularSpeedPitch = pSettings->r_float(section,"path_angular_sp_pitch"); + AngSP = angularSpeedPitch; + float angularSpeedHeading = pSettings->r_float(section,"path_angular_sp_heading"); + AngSH = angularSpeedHeading; + LinearAcc_fw = pSettings->r_float(section,"path_linear_acc_fw"); + LinearAcc_bk = pSettings->r_float(section,"path_linear_acc_bk"); + if(pSettings->line_exist(section,"flag_by_new_acc")) + isAdnAcc = pSettings->r_float(section,"flag_by_new_acc"); + else + isAdnAcc = 0; + onPointRangeDist = pSettings->r_float(section,"on_point_range_dist"); + maxLinearSpeed = pSettings->r_float(section,"velocity"); + min_altitude = pSettings->r_float(section,"min_altitude"); + + + float y0 = pSettings->r_float(section,"path_angular_sp_pitch_0"); + PitchSpB = y0; + PitchSpK = (angularSpeedPitch - PitchSpB)/maxLinearSpeed; + + y0 = pSettings->r_float(section,"path_angular_sp_heading_0"); + HeadingSpB = y0; + HeadingSpK = (angularSpeedHeading - HeadingSpB)/maxLinearSpeed; + safe_altitude_add = pSettings->r_float(section,"safe_altitude"); +} + +float SHeliMovementState::GetAngSpeedPitch(float speed) +{ + return PitchSpK*speed+PitchSpB; + //PitchSpK*speed+PitchSpB; + //(AngSP - PitchSpB)*speed*AngSP + PitchSpB; + //PitchSpB/(1 + speed * AngSP); //+-0.10 +} + +float SHeliMovementState::GetAngSpeedHeading(float speed) +{ + if (isAdnAcc==0) return HeadingSpK*speed+HeadingSpB; + + else return AngSH/(2*HeadingSpB-AngSH + speed * (HeadingSpB-AngSH) / 2); + //HeadingSpK*speed+HeadingSpB; old veersion +} + +void SHeliMovementState::Update() +{ + switch(type){ + case eMovNone: + break; + case eMovToPoint: + UpdateMovToPoint(); + break; + case eMovPatrolPath: + case eMovRoundPath: + UpdatePatrolPath(); + break; + case eMovLanding: + case eMovTakeOff: + break; + default: + NODEFAULT; + }; +} + +void SHeliMovementState::reinit() +{ + type = eMovNone; + currPatrolPath = NULL; + currPatrolVertex = NULL; + patrol_begin_idx = 0; + patrol_path_name = ""; + need_to_del_path = false; + curLinearSpeed = 0.0f; + curLinearAcc = 0.0f; + round_center.set (0.0f,0.0f,0.0f); + round_radius = 0.0f; + round_reverse = false; + desiredPoint = parent->XFORM().c; + currP = desiredPoint; + float bbb; + parent->XFORM().getHPB (currPathH, currPathP, bbb); + + speedInDestPoint = 0.0f; + +} +float SHeliMovementState::GetDistanceToDestPosition() +{ + return desiredPoint.distance_to(currP); +} + +void SHeliMovementState::UpdatePatrolPath() +{ + if( AlreadyOnPoint() ){ + + float dist = GetDistanceToDestPosition(); + parent->callback(GameObject::eHelicopterOnPoint)(dist,currP, currPatrolVertex ? currPatrolVertex->vertex_id() : -1); + CPatrolPath::const_iterator b,e; + currPatrolPath->begin(currPatrolVertex,b,e); + if(b!=e){ + + if(need_to_del_path && currPatrolVertex->data().flags())//fake flags that signals entrypoint for round path + SetPointFlags(currPatrolVertex->vertex_id(), 0); + + currPatrolVertex = currPatrolPath->vertex((*b).vertex_id()); + + Fvector p = currPatrolVertex->data().position(); + desiredPoint = p; + }else{ + type = eMovNone; +// curLinearSpeed = 0.0f; +// curLinearAcc = 0.0f; + } + } +} + +void SHeliMovementState::UpdateMovToPoint() +{ + if( AlreadyOnPoint() ){ + float dist = GetDistanceToDestPosition(); + parent->callback(GameObject::eHelicopterOnPoint)(dist,currP, -1); + type = eMovNone; + } +} +extern float STEP; +bool SHeliMovementState::AlreadyOnPoint() +{ + float dist = GetDistanceToDestPosition(); + bool res = false; + if(dist<=0.1f) res = true; + + if( dist < onPointRangeDist ){ + Fvector P1 = currP; + Fvector dir; + dir.setHP(currPathH,0.0f); + P1.mad(dir, curLinearSpeed*STEP); + float new_dist = desiredPoint.distance_to(P1); + res = new_dist>dist; + } +// if(res) +// Msg("--------OnPoint id=[%d] dist=[%f]", currPatrolVertex->vertex_id(), dist); + + return res; +} + +void SHeliMovementState::getPathAltitude (Fvector& point, float base_altitude) +{ + Fbox boundingVolume = Level().ObjectSpace.GetBoundingVolume(); + Fvector boundSize; + boundingVolume.getsize(boundSize); + + collide::rq_result cR; + Fvector down_dir; + down_dir.set(0.0f, -1.0f, 0.0f); + + point.y = boundingVolume.max.y+EPS_L; + VERIFY( _valid(point) ); + + Level().ObjectSpace.RayPick(point, down_dir, boundSize.y+1.0f, collide::rqtStatic, cR, NULL); + + point.y = point.y-cR.range; + + if( point.y+base_altitude < boundingVolume.max.y ) + point.y += base_altitude; + else + point.y = boundingVolume.max.y-EPS_L; + + VERIFY( _valid(point) ); + + float minY = boundingVolume.min.y;//+(m_boundingVolume.max.y-m_boundingVolume.min.y)*m_heli->m_data.m_alt_korridor; + float maxY = boundingVolume.max.y+base_altitude; + clamp (point.y,minY,maxY); + VERIFY( _valid(point) ); + +} +void SHeliMovementState::SetDestPosition(Fvector* pos) +{ + desiredPoint = *pos; + type = eMovToPoint; + + if(need_to_del_path&&currPatrolPath){ + CPatrolPath* tmp = const_cast(currPatrolPath); + xr_delete( tmp ); + need_to_del_path = false; + } +} + + +void SHeliMovementState::goPatrolByPatrolPath (LPCSTR path_name, int start_idx) +{ + if(need_to_del_path&&currPatrolPath){ + CPatrolPath* tmp = const_cast(currPatrolPath); + xr_delete( tmp ); + } + + patrol_begin_idx = start_idx; + patrol_path_name = path_name; + + currPatrolPath = ai().patrol_paths().path(patrol_path_name); + need_to_del_path = false; + currPatrolVertex = currPatrolPath->vertex(patrol_begin_idx); + + desiredPoint = currPatrolVertex->data().position(); + + type = eMovPatrolPath; +} + +void SHeliMovementState::save(NET_Packet &output_packet) +{ + output_packet.w_s16 ((s16)type); + output_packet.w_u32 (patrol_begin_idx); + output_packet.w_stringZ (patrol_path_name); + + output_packet.w_float (maxLinearSpeed); + output_packet.w_float (LinearAcc_fw); + output_packet.w_float (LinearAcc_bk); + + output_packet.w_float (speedInDestPoint); + + output_packet.w_vec3 (desiredPoint); + + output_packet.w_float (curLinearSpeed); + output_packet.w_float (curLinearAcc); + + output_packet.w_vec3 (currP); + + + output_packet.w_float (currPathH); + output_packet.w_float (currPathP); + + output_packet.w_vec3 (round_center); + + output_packet.w_float (round_radius); + + output_packet.w_u8 (round_reverse ? 1 : 0); + + output_packet.w_float (onPointRangeDist); + + if(type==eMovPatrolPath){ + output_packet.w_s32( currPatrolVertex->vertex_id() ); + } +} + +void SHeliMovementState::load(IReader &input_packet) +{ + type = (EHeilMovementState)input_packet.r_s16(); + patrol_begin_idx = input_packet.r_u32(); + input_packet.r_stringZ (patrol_path_name); + + maxLinearSpeed = input_packet.r_float(); + LinearAcc_fw = input_packet.r_float(); + LinearAcc_bk = input_packet.r_float(); + + speedInDestPoint = input_packet.r_float(); + + input_packet.r_fvector3 (desiredPoint); + + curLinearSpeed = input_packet.r_float(); + curLinearAcc = input_packet.r_float(); + + input_packet.r_fvector3 (currP); + + + currPathH = input_packet.r_float(); + currPathP = input_packet.r_float(); + + input_packet.r_fvector3 (round_center); + + round_radius = input_packet.r_float(); + + round_reverse = !!input_packet.r_u8(); + + onPointRangeDist = input_packet.r_float(); + + if(type==eMovPatrolPath){ + currPatrolPath = ai().patrol_paths().path(patrol_path_name); + int idx = input_packet.r_s32(); + currPatrolVertex = currPatrolPath->vertex(idx); + } + if(type==eMovRoundPath){ + goByRoundPath(round_center, round_radius, !round_reverse); + } + +} + +float SHeliMovementState::GetSafeAltitude() +{ + Fbox boundingVolume = Level().ObjectSpace.GetBoundingVolume(); + return boundingVolume.max.y+safe_altitude_add; +} + +void SHeliMovementState::CreateRoundPoints(Fvector center, float radius, float start_h, float end_h, xr_vector& round_points) +{ + float height = center.y; + + float round_len = 2*PI*radius; + static float dist = 30.0f;//dist between points + float td = 2*PI*dist/round_len; + float dir_h = 0.0f; + + dir_h = start_h; + while( dir_h+td < end_h ){ + Fvector dir, new_pt; + dir.setHP(dir_h,0.0f); + new_pt.mad(center,dir,radius); + new_pt.y = height; + round_points.push_back( STmpPt(new_pt,dir_h) ); + dir_h += td; + } + +} + +void SHeliMovementState::goByRoundPath(Fvector center_, float radius_, bool clockwise_) +{ + if(type == eMovRoundPath) + clockwise_ = !clockwise_; + + float r_verify = maxLinearSpeed*GetAngSpeedHeading(maxLinearSpeed); + if(r_verify>radius_){ + Msg("! Helicopter: cannot build round path R=%f. Min R=%f",radius_,r_verify); + return; + } + + round_center = center_; + round_radius = radius_; + round_reverse = !clockwise_; + + if(need_to_del_path&&currPatrolPath){ + CPatrolPath* tmp = const_cast(currPatrolPath); + xr_delete( tmp ); + } + need_to_del_path = true; + u32 pt_idx = 0; + CPatrolPath* pp = new CPatrolPath("heli_round_path"); + + float start_h = 0.0f; + float end_h = PI_MUL_2-EPS; + + xr_vector round_points; + xr_vector::iterator it,it_e; + CreateRoundPoints(center_, radius_, start_h, end_h, round_points); + if(clockwise_) + std::reverse(round_points.begin()+1, round_points.end()); + + + it = round_points.begin(); + it_e = round_points.end(); + for(;it!=it_e;++it,++pt_idx){ + string128 pt_name; + xr_sprintf(pt_name,"heli_round_path_pt_%d",pt_idx); + CPatrolPoint pt = CPatrolPoint((CLevelGraph*)0,(CGameLevelCrossTable*)0,(CGameGraph*)0,pp,(*it).point,u32(-1),0,pt_name); + pp->add_vertex(pt,pt_idx); + if (pt_idx) + pp->add_edge(pt_idx-1,pt_idx,1.f); + } + pp->add_edge(pt_idx-1, 0, 1.f); + + + currPatrolPath = pp; + +//find nearest point to start from... + u32 start_vertex_id = 0; + float min_dist = flt_max; + float stop_t = curLinearSpeed/LinearAcc_bk; + float stop_path = curLinearSpeed*stop_t-LinearAcc_bk*stop_t*stop_t/2.0f; + + CPatrolPath::const_vertex_iterator b = currPatrolPath->vertices().begin(); + CPatrolPath::const_vertex_iterator e = currPatrolPath->vertices().end(); + for ( ; b != e; ++b) { + float d = (*b).second->data().position().distance_to(currP); + if ( (d>stop_path) && (dvertex(start_vertex_id); + desiredPoint = currPatrolVertex->data().position(); + + type = eMovRoundPath; +} + +void SHeliMovementState::SetPointFlags(u32 idx, u32 new_flags) +{ + CPatrolPath *p = const_cast(currPatrolPath); + CPatrolPoint* pt_curr = &p->vertex(idx)->data(); + CPatrolPoint* pt_new = new CPatrolPoint( + (CLevelGraph*)0, + (CGameLevelCrossTable*)0, + (CGameGraph*)0, + currPatrolPath, + pt_curr->position(), + u32(-1), + new_flags, + pt_curr->name() + ); + + p->vertex(idx)->data (*pt_new); +// xr_delete(pt_curr); + +} + +float SHeliMovementState::GetSpeedInDestPoint () +{ + if(need_to_del_path && currPatrolVertex && currPatrolVertex->data().flags()) return 0.0f; + else + return speedInDestPoint; +} +void SHeliMovementState::SetSpeedInDestPoint (float val) +{ + speedInDestPoint = val; +} + +Fvector CHelicopter::GetCurrVelocityVec() +{ + Fvector dir; + dir.setHP (m_movement.currPathH,m_movement.currPathP); +// dir.sub (m_movement.desiredPoint,m_movement.currP); + dir.normalize_safe (); + return dir; +} + +#ifdef DEBUG +void CHelicopter::OnRender() +{ +/* + if(!bDebug) return; + + if(!m_movement.currPatrolPath) return; + + CPatrolPath::const_vertex_iterator b = m_movement.currPatrolPath->vertices().begin(); + CPatrolPath::const_vertex_iterator e = m_movement.currPatrolPath->vertices().end(); + for ( ; b != e; ++b) { + Fvector p = (*b).second->data().position(); + Level().debug_renderer().draw_aabb (p,0.1f,0.1f,0.1f,D3DCOLOR_XRGB(0,255,0)); + } +*/ +/* + Fvector pos = Level().CurrentEntity()->Position(); + static float radius = 50.0f;//meters + float round_len = 2*PI*radius; + static float dist = 10.0f;//dist between points + float td = 2*PI*dist/round_len; + float dir_h = 0.0f; + xr_vector round_points; + + while(dir_h+td<2*PI){ + Fvector dir, new_pt; + dir.setHP(dir_h,0.0f); + new_pt.mad(pos,dir,radius); + new_pt.y += 1.0f; + round_points.push_back(new_pt); + dir_h += td; + } + + xr_vector::iterator it = round_points.begin(); + xr_vector::iterator it_e = round_points.end(); + for(;it!=it_e;++it){ + Level().debug_renderer().draw_aabb ((*it),0.1f,0.1f,0.1f,D3DCOLOR_XRGB(0,255,0)); + } +*/ +/* Level().debug_renderer().draw_line(Fidentity,m_heli->m_right_rocket_bone_xform.c, m_heli->m_data.m_destEnemyPos,D3DCOLOR_XRGB(0,255,0)); + + Level().debug_renderer().draw_line(Fidentity,m_heli->XFORM().c,m_heli->m_data.m_destEnemyPos,D3DCOLOR_XRGB(255,0,0)); + return; +*/ + + +} +#endif + diff --git a/src/xrGameLA/HelicopterWeapon.cpp b/src/xrGameLA/HelicopterWeapon.cpp new file mode 100644 index 000000000..51aaa38c3 --- /dev/null +++ b/src/xrGameLA/HelicopterWeapon.cpp @@ -0,0 +1,330 @@ +#include "stdafx.h" +#include "helicopter.h" +#include "ExplosiveRocket.h" +#include "xrMessages.h" +#include "../../xrNetServer/net_utils.h" +#include "../Include/xrRender/Kinematics.h" +#include "Level.h" + +void +CHelicopter::BoneMGunCallbackX(CBoneInstance *B) +{ + CHelicopter * P = static_cast(B->callback_param()); + Fmatrix rX; rX.rotateX (P->m_cur_rot.x); + B->mTransform.mulB_43 (rX); +} + +void +CHelicopter::BoneMGunCallbackY(CBoneInstance *B) +{ + CHelicopter * P = static_cast(B->callback_param()); + Fmatrix rY; rY.rotateY (P->m_cur_rot.y); + B->mTransform.mulB_43 (rY); +} + + +void CHelicopter::OnEvent( NET_Packet& P, u16 type) +{ + inherited::OnEvent(P,type); + CExplosive::OnEvent(P,type); + u16 id; + switch (type) { + case GE_OWNERSHIP_TAKE : + { + P.r_u16(id); + CRocketLauncher::AttachRocket(id, this); + } break; + case GE_OWNERSHIP_REJECT : + case GE_LAUNCH_ROCKET : + { + bool bLaunch = (type==GE_LAUNCH_ROCKET); + P.r_u16(id); + CRocketLauncher::DetachRocket(id, bLaunch); + } break; + } +} + +void CHelicopter::MGunUpdateFire() +{ + + fTime -= Device.fTimeDelta; + if (delta_t < 0){ + delta_t = Device.fTimeGlobal; + flag_by_fire = 0; + } + float time_f = Device.fTimeGlobal - delta_t; + + float fire_time; + if(pSettings->line_exist(*cNameSect(),"fire_time")) + fire_time = pSettings->r_float(*cNameSect(),"fire_time"); + else + fire_time = -1; + + float no_fire_time; + if(pSettings->line_exist(*cNameSect(),"no_fire_time")) + no_fire_time = pSettings->r_float(*cNameSect(),"no_fire_time"); + else + no_fire_time = -1; + + CShootingObject::UpdateFlameParticles(); + CShootingObject::UpdateLight(); + + if(!IsWorking()) { + if(fTime<0) fTime = 0.f; + return; + } + if(no_fire_time > 0 && fire_time > 0) { + if (flag_by_fire==1 && time_f > fire_time){ + delta_t = Device.fTimeGlobal; + time_f = Device.fTimeGlobal - delta_t; + flag_by_fire = 0; + } + if (time_f > no_fire_time && flag_by_fire ==0){ + delta_t = Device.fTimeGlobal; + time_f = Device.fTimeGlobal - delta_t; + flag_by_fire = 1; + } + if(flag_by_fire ==0 && time_f < no_fire_time) return; + } + + if(fTime<=0) { + OnShot(); + fTime += fTimeToFire; + } + +} +void CHelicopter::OnShot () +{ + Fvector fire_pos,fire_dir; + fire_pos = get_CurrentFirePoint(); + fire_dir = m_fire_dir; + + float fire_trail_speed = 15.0f; + clamp (fire_trail_speed,GetCurrVelocity(),300.0f); + if(m_enemy.bUseFireTrail){ + Fvector enemy_pos = m_enemy.destEnemyPos; + + float dt = Device.fTimeGlobal - m_enemy.fStartFireTime; VERIFY(dt>=0); + float dist = m_enemy.fire_trail_length_curr - dt*fire_trail_speed; + if(dist<0) + { + MGunFireEnd (); + return ; + } + + Fvector fp = fire_pos; + fp.y = enemy_pos.y; + Fvector fd; + fd.sub(enemy_pos,fp).normalize_safe(); + if(dist > (m_enemy.fire_trail_length_curr/2.0f) ){ + fd.mul(-1.0f); + dist = dist - (m_enemy.fire_trail_length_curr/2.0f); + }else{ + dist = (m_enemy.fire_trail_length_curr/2.0f) - dist; + } + + + static float fire_trace_width = pSettings->r_float(*cNameSect(),"fire_trace_width"); + enemy_pos.mad(fd,dist); + Fvector disp_dir; + disp_dir.random_point(fire_trace_width); + + enemy_pos.add(disp_dir); + fire_dir.sub(enemy_pos,fire_pos).normalize_safe(); + }; + + FireBullet(fire_pos, fire_dir, fireDispersionBase, m_CurrentAmmo, ID(), ID(), OnServer()); + + StartShotParticles (); + if(m_bLightShotEnabled) + Light_Start (); + + + StartFlameParticles (); + StartSmokeParticles (fire_pos, zero_vel); + OnShellDrop (fire_pos, zero_vel); + + HUD_SOUND_ITEM::PlaySound (m_sndShot, fire_pos, this, false); + +} + +void CHelicopter::MGunFireStart() +{ + if(!m_use_mgun_on_attack) + return; + + if(FALSE==IsWorking() && m_enemy.bUseFireTrail){ + //start calc fire trail + m_enemy.fStartFireTime = Device.fTimeGlobal; + Fvector fp = get_CurrentFirePoint(); + Fvector ep = m_enemy.destEnemyPos; + + //calc min firetrail length + float h = fp.y-ep.y; + if(h>0.0f){ + float dl =h*tan(m_lim_x_rot.y); + float ds = fp.distance_to_xz(ep); + if(ds>dl){ + float half_trail = ds-dl; + m_enemy.fire_trail_length_curr = half_trail*2.0f; + clamp(m_enemy.fire_trail_length_curr,0.0f,m_enemy.fire_trail_length_des); +// Msg("Start fire. Desired length=%f, cur_length=%f",m_enemy.fire_trail_length_des,m_enemy.fire_trail_length_curr); + }else + m_enemy.fire_trail_length_curr = m_enemy.fire_trail_length_des; + }else + m_enemy.fire_trail_length_curr = m_enemy.fire_trail_length_des; + } + + CShootingObject::FireStart (); +} + +void CHelicopter::MGunFireEnd() +{ + CShootingObject::FireEnd (); + StopFlameParticles (); + m_enemy.fStartFireTime = -1.0f; +} + +bool between(const float& src, const float& min, const float& max){return( (src>min)&&(src m_time_between_rocket_attack) ) { + if(m_syncronize_rocket) { + startRocket(1); + startRocket(2); + }else{ + if(m_last_launched_rocket==1) + startRocket(2); + else + startRocket(1); + } + + m_last_rocket_attack = Device.dwTimeGlobal; + } + + }else{ + MGunFireEnd(); + } + + }else + MGunFireEnd(); + + MGunUpdateFire(); +} + +void CHelicopter::UpdateMGunDir() +{ + IKinematics* K = smart_cast(Visual()); + m_fire_bone_xform = K->LL_GetTransform(m_fire_bone); + + m_fire_bone_xform.mulA_43 (XFORM()); + m_fire_pos.set (0,0,0); + m_fire_bone_xform.transform_tiny(m_fire_pos); + m_fire_dir.set (0,0,1); + m_fire_bone_xform.transform_dir(m_fire_dir); + + m_fire_dir.sub (m_enemy.destEnemyPos,m_fire_pos).normalize_safe(); + + m_left_rocket_bone_xform = K->LL_GetTransform(m_left_rocket_bone); + m_left_rocket_bone_xform.mulA_43 (XFORM()); + m_left_rocket_bone_xform.c.y += 1.0f; + //.fake + m_right_rocket_bone_xform = K->LL_GetTransform(m_right_rocket_bone); + m_right_rocket_bone_xform.mulA_43 (XFORM()); + m_right_rocket_bone_xform.c.y += 1.0f; + //.fake + + m_allow_fire = TRUE; + Fmatrix XFi; + XFi.invert (XFORM()); + Fvector dep; + XFi.transform_tiny (dep,m_enemy.destEnemyPos); + {// x angle + Fvector A_; A_.sub(dep,m_bind_x); m_i_bind_x_xform.transform_dir(A_); A_.normalize(); + m_tgt_rot.x = angle_normalize_signed(m_bind_rot.x-A_.getP()); + float sv_x = m_tgt_rot.x; + clamp (m_tgt_rot.x,-m_lim_x_rot.y,-m_lim_x_rot.x); + if (!fsimilar(sv_x,m_tgt_rot.x,EPS_L)) m_allow_fire=FALSE; + } + {// y angle + Fvector A_; A_.sub(dep,m_bind_y); m_i_bind_y_xform.transform_dir(A_); A_.normalize(); + m_tgt_rot.y = angle_normalize_signed(m_bind_rot.y-A_.getH()); + float sv_y = m_tgt_rot.y; + clamp (m_tgt_rot.y,-m_lim_y_rot.y,-m_lim_y_rot.x); + if (!fsimilar(sv_y,m_tgt_rot.y,EPS_L)) m_allow_fire=FALSE; + } + + if ((angle_difference(m_cur_rot.x,m_tgt_rot.x)>deg2rad(m_barrel_dir_tolerance))|| + (angle_difference(m_cur_rot.y,m_tgt_rot.y)>deg2rad(m_barrel_dir_tolerance))) + m_allow_fire=FALSE; + +} + +void CHelicopter::startRocket(u16 idx) +{ + if((getRocketCount()>=1)&&m_use_rocket_on_attack) { + CExplosiveRocket* pGrenade = smart_cast(getCurrentRocket()); + VERIFY(pGrenade); + pGrenade->SetInitiator(this->ID()); + + Fmatrix rocketXFORM; + (idx==1)?rocketXFORM=m_left_rocket_bone_xform:rocketXFORM=m_right_rocket_bone_xform; + + Fvector vel; + Fvector dir; + dir.sub(m_enemy.destEnemyPos, rocketXFORM.c ).normalize_safe(); + vel.mul(dir,CRocketLauncher::m_fLaunchSpeed); + + Fmatrix xform; + xform.identity(); + xform.k.set(dir); + Fvector::generate_orthonormal_basis(xform.k,xform.j,xform.i); + xform.c = rocketXFORM.c; + VERIFY2(_valid(xform),"CHelicopter::startRocket. Invalid xform"); + LaunchRocket(xform, vel, zero_vel); + + NET_Packet P; + u_EventGen(P,GE_LAUNCH_ROCKET,ID()); + P.w_u16(u16( getCurrentRocket()->ID())); + u_EventSend(P); + + dropCurrentRocket(); + + m_last_launched_rocket = idx; + HUD_SOUND_ITEM::PlaySound(m_sndShotRocket, xform.c, this, false); + + } +} + +const Fmatrix& CHelicopter::get_ParticlesXFORM() + +{ + return m_fire_bone_xform; +} + +const Fvector& CHelicopter::get_CurrentFirePoint() +{ + return m_fire_pos; +} diff --git a/src/xrGameLA/Hit.cpp b/src/xrGameLA/Hit.cpp new file mode 100644 index 000000000..d28592926 --- /dev/null +++ b/src/xrGameLA/Hit.cpp @@ -0,0 +1,156 @@ +#include "stdafx.h" +#include "alife_space.h" +#include "hit.h" +#include "ode_include.h" +#include "..\bone.h" +#include "../../xrNetServer/net_utils.h" +#include "xrMessages.h" +#include "Level.h" + +SHit::SHit(float aPower,Fvector &adir,CObject *awho, u16 aelement, Fvector ap_in_bone_space, float aimpulse, ALife::EHitType ahit_type, float aAP, bool AimBullet) +{ + power =aPower; + dir .set(adir); + who =awho; + if (awho) + whoID = awho->ID(); + else + whoID = 0; + boneID =aelement; + p_in_bone_space .set(ap_in_bone_space); + impulse =aimpulse; + hit_type =ahit_type; + ap = aAP; + PACKET_TYPE = 0; + BulletID = 0; + SenderID = 0; + aim_bullet = AimBullet; + add_wound = false; +} + +SHit::SHit() +{ + invalidate(); +} +void SHit::invalidate() +{ + Time = 0; + PACKET_TYPE = 0; + DestID = 0; + + power =-dInfinity; + dir .set(-dInfinity,-dInfinity,-dInfinity); + who =NULL; + whoID = 0; + weaponID = 0; + + boneID =BI_NONE; + p_in_bone_space .set(-dInfinity,-dInfinity,-dInfinity); + + impulse =-dInfinity; + hit_type =ALife::eHitTypeMax; + + ap = 0.0f; + BulletID = 0; + SenderID = 0; + aim_bullet = false; + add_wound = false; +} + +bool SHit::is_valide() const +{ + return hit_type!=ALife::eHitTypeMax; +} + +void SHit::GenHeader (u16 PacketType, u16 ID) +{ + DestID = ID; + PACKET_TYPE = PacketType; + Time = Level().timeServer(); +}; + +void SHit::Read_Packet (NET_Packet Packet) +{ + u16 type_dummy; + Packet.r_begin (type_dummy); + Packet.r_u32 (Time); + Packet.r_u16 (PACKET_TYPE); + Packet.r_u16 (DestID); + Read_Packet_Cont (Packet); +}; + +void SHit::Read_Packet_Cont (NET_Packet Packet) +{ + + Packet.r_u16 (whoID); + Packet.r_u16 (weaponID); + Packet.r_dir (dir); + Packet.r_float (power); + Packet.r_u16 (boneID); + Packet.r_vec3 (p_in_bone_space); + Packet.r_float (impulse); + if (IsGameTypeSingle()) + aim_bullet = Packet.r_u16()!=0; + else + aim_bullet = false; + hit_type = (ALife::EHitType)Packet.r_u16(); //hit type + + if (hit_type == ALife::eHitTypeFireWound) + { + Packet.r_float (ap); + } + if (PACKET_TYPE == GE_HIT_STATISTIC) + { + Packet.r_u32(BulletID); + Packet.r_u32(SenderID); + } +} + +void SHit::Write_Packet_Cont (NET_Packet &Packet) +{ + Packet.w_u16 (whoID); + Packet.w_u16 (weaponID); + Packet.w_dir (dir); + Packet.w_float (power); + Packet.w_u16 (boneID); + Packet.w_vec3 (p_in_bone_space); + Packet.w_float (impulse); + if (IsGameTypeSingle()) + Packet.w_u16 (aim_bullet!=0); + Packet.w_u16 (u16(hit_type&0xffff)); + if (hit_type == ALife::eHitTypeFireWound) + { + Packet.w_float (ap); + } + if (PACKET_TYPE == GE_HIT_STATISTIC) + { + Packet.w_u32(BulletID); + Packet.w_u32(SenderID); + } +} +void SHit::Write_Packet (NET_Packet &Packet) +{ + Packet.w_begin (M_EVENT); + Packet.w_u32 (Time); + Packet.w_u16 (u16(PACKET_TYPE&0xffff)); + Packet.w_u16 (u16(DestID&0xffff)); + + Write_Packet_Cont (Packet); +}; + +#ifdef DEBUG +void SHit::_dump() +{ + Msg("SHit::_dump()---begin"); + Log("power=",power); + Log("impulse=",impulse); + Log("dir=",dir); + Log("whoID=",whoID); + Log("weaponID=",weaponID); + Log("element=",boneID); + Log("p_in_bone_space=",p_in_bone_space); + Log("hit_type=",(int)hit_type); + Log("ap=",ap); + Msg("SHit::_dump()---end"); +} +#endif diff --git a/src/xrGameLA/Hit.h b/src/xrGameLA/Hit.h new file mode 100644 index 000000000..b497aa126 --- /dev/null +++ b/src/xrGameLA/Hit.h @@ -0,0 +1,47 @@ +#pragma once + +struct SHit +{ + SHit (float Power, Fvector &dir, CObject *who, u16 element, Fvector p_in_object_space, float impulse, ALife::EHitType hit_type, float ap = 0.0f, bool AimBullet=false); + SHit (); + bool is_valide () const ; + void invalidate () ; +IC float damage () const {VERIFY(is_valide());return power;} +IC const Fvector &direction () const {VERIFY(is_valide());return dir;} +IC const CObject *initiator () const {VERIFY(is_valide());return who;} +IC u16 bone () const {VERIFY(is_valide());return boneID;} +IC const Fvector &bone_space_position () const {VERIFY(is_valide());return p_in_bone_space;} +IC float phys_impulse () const {VERIFY(is_valide());return impulse;} +IC ALife::EHitType type () const {VERIFY(is_valide());return hit_type;} + void Read_Packet (NET_Packet P); + void Read_Packet_Cont (NET_Packet P); + void Write_Packet (NET_Packet &P); + void Write_Packet_Cont (NET_Packet &P); + + void GenHeader (u16 PacketType, u16 ID); +//private: + //GE_HIT + u32 Time; + u16 PACKET_TYPE; + u16 DestID; + + float power; + Fvector dir; + CObject *who; + u16 whoID; + u16 weaponID; + u16 boneID; + Fvector p_in_bone_space; + float impulse; + ALife::EHitType hit_type; + bool add_wound; + float ap; + bool aim_bullet; + //--------------------------------------------------- + //GE_HIT_STATISTIC + u32 BulletID; + u32 SenderID; +#ifdef DEBUG + void _dump (); +#endif +}; \ No newline at end of file diff --git a/src/xrGameLA/HitMarker.cpp b/src/xrGameLA/HitMarker.cpp new file mode 100644 index 000000000..21cd52cb8 --- /dev/null +++ b/src/xrGameLA/HitMarker.cpp @@ -0,0 +1,96 @@ +// exxZERO Time Stamp AddIn. Document modified at : Thursday, March 07, 2002 14:12:50 , by user : Oles , from computer : OLES +#include "stdafx.h" +#include "HitMarker.h" +#include "../render.h" +#include "../LightAnimLibrary.h" +#include "UIStaticItem.h" + +static Fvector2 as_PC[5]; +static Fvector2 as_TC[5]; +const static u32 as_id[4*3] = {0,1,4, 1,2,4, 2,3,4, 3,0,4}; + + +//-------------------------------------------------------------------- +CHitMarker::CHitMarker() +{ + InitShader (pSettings->r_string("hud_hitmark","hit_mark_texture")); +} + +void CHitMarker::InitShader (LPCSTR tex_name) +{ + hShader2->create ("hud\\default", tex_name); +} + +//-------------------------------------------------------------------- +CHitMarker::~CHitMarker() +{ + while( m_HitMarks.size() ){ + xr_delete ( m_HitMarks.front() ); + m_HitMarks.pop_front (); + } +} +//-------------------------------------------------------------------- +const static float fShowTime = 0.5f; +void CHitMarker::Render() +{ + float h1,p1; + Device.vCameraDirection.getHP (h1,p1); + + while( m_HitMarks.size() && !m_HitMarks.front()->IsActive() ){ + xr_delete ( m_HitMarks.front() ); + m_HitMarks.pop_front (); + } + + HITMARKS::iterator it = m_HitMarks.begin(); + HITMARKS::iterator it_e = m_HitMarks.end(); + for(;it!=it_e;++it) + (*it)->Draw(-h1); + +} +//-------------------------------------------------------------------- + +void CHitMarker::Hit(int id, const Fvector& dir){ + + Fvector hit_dir = dir; + hit_dir.mul(-1.0f); + m_HitMarks.push_back (new SHitMark(hShader2,hit_dir)); +} +//-------------------------------------------------------------------- + + + +SHitMark::SHitMark (const ui_shader& sh, const Fvector& dir) +{ + m_StartTime = Device.fTimeGlobal; + m_lanim = LALib.FindItem("hud_hit_mark"); + m_HitDirection = dir.getH(); + m_UIStaticItem = new CUIStaticItem(); + m_UIStaticItem->SetShader (sh); + m_UIStaticItem->SetPos (256.0f, 128.0f); + m_UIStaticItem->SetSize (Fvector2().set( 512.0f, 512.0f) ); +} + +void SHitMark::UpdateAnim () +{ + int frame; + u32 clr = m_lanim->CalculateRGB(Device.fTimeGlobal-m_StartTime,frame); + m_UIStaticItem->SetTextureColor (subst_alpha(m_UIStaticItem->GetTextureColor(), color_get_A(clr))); +} + +SHitMark::~SHitMark () +{ + xr_delete(m_UIStaticItem); +} + +bool SHitMark::IsActive() +{ + return ((Device.fTimeGlobal-m_StartTime) < m_lanim->Length_sec()); +} + +void SHitMark::Draw(float cam_dir) +{ + UpdateAnim (); + + float res_h = cam_dir + m_HitDirection; + m_UIStaticItem->Render (res_h); +} diff --git a/src/xrGameLA/HitMarker.h b/src/xrGameLA/HitMarker.h new file mode 100644 index 000000000..985f5d525 --- /dev/null +++ b/src/xrGameLA/HitMarker.h @@ -0,0 +1,45 @@ +#ifndef __XR_HITMARKER_H__ +#define __XR_HITMARKER_H__ +#pragma once + +#include "ui_defs.h" + +class CUIStaticItem; +class CLAItem; + +struct SHitMark{ + CUIStaticItem* m_UIStaticItem; + float m_StartTime; + float m_HitDirection; + CLAItem* m_lanim; + + SHitMark (const ui_shader& sh, const Fvector& dir); + ~SHitMark (); + bool IsActive (); + void UpdateAnim (); + void Draw (float dir); +}; + + +class CHitMarker +{ +public: +/* + float fHitMarks[4]; + ref_shader hShader; + ref_geom hGeom; +*/ + typedef xr_deque HITMARKS; + FactoryPtr hShader2; + HITMARKS m_HitMarks; + +public: + CHitMarker (); + ~CHitMarker (); + + void Render (); + void Hit (int id, const Fvector& dir); + void InitShader (LPCSTR tex_name); +}; + +#endif // __XR_HITMARKER_H__ diff --git a/src/xrGameLA/HudItem.cpp b/src/xrGameLA/HudItem.cpp new file mode 100644 index 000000000..f3d7c8ffc --- /dev/null +++ b/src/xrGameLA/HudItem.cpp @@ -0,0 +1,407 @@ +#include "stdafx.h" +#include "HudItem.h" +#include "physic_item.h" +#include "actor.h" +#include "actoreffector.h" +#include "Missile.h" +#include "xrmessages.h" +#include "level.h" +#include "inventory.h" +#include "../CameraBase.h" +#include "player_hud.h" +#include "../SkeletonMotions.h" + +CHudItem::CHudItem() +{ + RenderHud (TRUE); + EnableHudInertion (TRUE); + AllowHudInertion (TRUE); + m_bStopAtEndAnimIsRunning = false; + m_current_motion_def = NULL; + m_started_rnd_anim_idx = u8(-1); +} + +DLL_Pure *CHudItem::_construct () +{ + m_object = smart_cast(this); + VERIFY (m_object); + + m_item = smart_cast(this); + VERIFY (m_item); + + return (m_object); +} + +CHudItem::~CHudItem() +{ +} + +void CHudItem::Load(LPCSTR section) +{ + hud_sect = pSettings->r_string (section,"hud"); + m_animation_slot = pSettings->r_u32 (section,"animation_slot"); + + m_sounds.LoadSound(section, "snd_bore", "sndBore", true); + + EnableHudCollide(!!READ_IF_EXISTS(pSettings,r_bool,hud_sect,"apply_collide",TRUE)); + EnableHudStrafeInertion(!!READ_IF_EXISTS(pSettings,r_bool,hud_sect,"apply_strafe_inertion",FALSE)); + EnableHudBobbing(!!READ_IF_EXISTS(pSettings,r_bool,hud_sect,"apply_bobbing",FALSE)); +} + +void CHudItem::PlaySound(LPCSTR alias, const Fvector& position) +{ + m_sounds.PlaySound (alias, position, object().H_Root(), !!GetHUDmode()); +} + +void CHudItem::renderable_Render() +{ + UpdateXForm (); + BOOL _hud_render = ::Render->get_HUD() && GetHUDmode(); + + if(_hud_render && !IsHidden()) + { + } + else + { + if (!object().H_Parent() || (!_hud_render && !IsHidden())) + { + on_renderable_Render (); + debug_draw_firedeps (); + }else + if (object().H_Parent()) + { + CInventoryOwner *owner = smart_cast(object().H_Parent()); + VERIFY (owner); + CInventoryItem *self = smart_cast(this); + + if (owner->attached(self) || (owner->NeedToRenderSecordary() && (item().BaseSlot() == RIFLE_SLOT || item().BaseSlot() == RIFLE_2_SLOT))) + on_renderable_Render(); + } + } +} + +void CHudItem::SwitchState(u32 S) +{ + if (OnClient()) + return; + + SetNextState( S ); + + if (object().Local() && !object().getDestroy()) + { + // !!! Just single entry for given state !!! + NET_Packet P; + object().u_EventGen (P,GE_WPN_STATE_CHANGE,object().ID()); + P.w_u8 (u8(S)); + object().u_EventSend (P); + } +} + +void CHudItem::OnEvent(NET_Packet& P, u16 type) +{ + switch (type) + { + case GE_WPN_STATE_CHANGE: + { + u8 S; + P.r_u8 (S); + OnStateSwitch (u32(S)); + } + break; + } +} + +void CHudItem::OnStateSwitch(u32 S) +{ + SetState (S); + + if(object().Remote()) + SetNextState (S); + + switch (S) + { + case eBore: + SetPending (FALSE); + + PlayAnimBore (); + if(HudItemData()) + { + Fvector P = HudItemData()->m_item_transform.c; + m_sounds.PlaySound("sndBore", P, object().H_Root(), !!GetHUDmode(), false, m_started_rnd_anim_idx); + } + + break; + } +} + +void CHudItem::PlayAnimBore() +{ + PlayHUDMotion ("anim_bore", TRUE, this, GetState()); +} + +bool CHudItem::Activate() +{ + OnActiveItem (); + return true; +} + +void CHudItem::Deactivate() +{ + OnHiddenItem (); +} +void CHudItem::OnMoveToRuck() +{ + SwitchState(eHidden); +} + +void CHudItem::SendDeactivateItem () +{ + SendHiddenItem (); +} +void CHudItem::SendHiddenItem() +{ + if (!object().getDestroy()) + { + NET_Packet P; + object().u_EventGen (P,GE_WPN_STATE_CHANGE,object().ID()); + P.w_u8 (u8(eHiding)); + object().u_EventSend (P, net_flags(TRUE, TRUE, FALSE, TRUE)); + } +} + + +void CHudItem::UpdateHudAdditonal (Fmatrix& hud_trans) +{ +} + +void CHudItem::UpdateCL() +{ + if(m_current_motion_def) + { + if(m_bStopAtEndAnimIsRunning) + { + const xr_vector& marks = m_current_motion_def->marks; + if(!marks.empty()) + { + float motion_prev_time = ((float)m_dwMotionCurrTm - (float)m_dwMotionStartTm)/1000.0f; + float motion_curr_time = ((float)Device.dwTimeGlobal - (float)m_dwMotionStartTm)/1000.0f; + + xr_vector::const_iterator it = marks.begin(); + xr_vector::const_iterator it_e = marks.end(); + for(;it!=it_e;++it) + { + const motion_marks& M = (*it); + if(M.is_empty()) + continue; + + const motion_marks::interval* Iprev = M.pick_mark(motion_prev_time); + const motion_marks::interval* Icurr = M.pick_mark(motion_curr_time); + if(Iprev==NULL && Icurr!=NULL /* || M.is_mark_between(motion_prev_time, motion_curr_time)*/) + { + OnMotionMark (m_startedMotionState, M); + } + } + + } + + m_dwMotionCurrTm = Device.dwTimeGlobal; + if(m_dwMotionCurrTm > m_dwMotionEndTm) + { + m_current_motion_def = NULL; + m_dwMotionStartTm = 0; + m_dwMotionEndTm = 0; + m_dwMotionCurrTm = 0; + m_bStopAtEndAnimIsRunning = false; + OnAnimationEnd (m_startedMotionState); + } + } + } +} + +void CHudItem::OnH_A_Chield () +{} + +void CHudItem::OnH_B_Chield () +{ + StopCurrentAnimWithoutCallback(); +} + +void CHudItem::OnH_B_Independent (bool just_before_destroy) +{ + m_sounds.StopAllSounds (); + UpdateXForm (); + + // next code was commented + /* + if(HudItemData() && !just_before_destroy) + { + object().XFORM().set( HudItemData()->m_item_transform ); + } + + if (HudItemData()) + { + g_player_hud->detach_item(this); + Msg("---Detaching hud item [%s][%d]", this->HudSection().c_str(), this->object().ID()); + }*/ + //SetHudItemData (NULL); +} + +void CHudItem::OnH_A_Independent () +{ + if(HudItemData()) + g_player_hud->detach_item(this); + StopCurrentAnimWithoutCallback(); +} + +void CHudItem::on_b_hud_detach() +{ + m_sounds.StopAllSounds (); +} + +void CHudItem::on_a_hud_attach() +{ + if(m_current_motion_def) + { + PlayHUDMotion_noCB(m_current_motion, FALSE); +#ifdef DEBUG +// Msg("continue playing [%s][%d]",m_current_motion.c_str(), Device.dwFrame); +#endif // #ifdef DEBUG + }else + { +#ifdef DEBUG +// Msg("no active motion"); +#endif // #ifdef DEBUG + } +} + +u32 CHudItem::PlayHUDMotion(const shared_str& M, BOOL bMixIn, CHudItem* W, u32 state) +{ + u32 anim_time = PlayHUDMotion_noCB(M, bMixIn); + if (anim_time>0) + { + m_bStopAtEndAnimIsRunning = true; + m_dwMotionStartTm = Device.dwTimeGlobal; + m_dwMotionCurrTm = m_dwMotionStartTm; + m_dwMotionEndTm = m_dwMotionStartTm + anim_time; + m_startedMotionState = state; + }else + m_bStopAtEndAnimIsRunning = false; + + return anim_time; +} + + +u32 CHudItem::PlayHUDMotion_noCB(const shared_str& motion_name, BOOL bMixIn) +{ + m_current_motion = motion_name; + + if(bDebug && item().m_pCurrentInventory) + { + Msg("-[%s] as[%d] [%d]anim_play [%s][%d]", + HudItemData()?"HUD":"Simulating", + item().m_pCurrentInventory->GetActiveSlot(), + item().object_id(), + motion_name.c_str(), + Device.dwFrame); + } + if( HudItemData() ) + { + return HudItemData()->anim_play (motion_name, bMixIn, m_current_motion_def, m_started_rnd_anim_idx); + }else + { + m_started_rnd_anim_idx = 0; + return g_player_hud->motion_length (motion_name, HudSection(), m_current_motion_def ); + } +} + +void CHudItem::StopCurrentAnimWithoutCallback() +{ + m_dwMotionStartTm = 0; + m_dwMotionEndTm = 0; + m_dwMotionCurrTm = 0; + m_bStopAtEndAnimIsRunning = false; + m_current_motion_def = NULL; +} + +BOOL CHudItem::GetHUDmode() +{ + if(object().H_Parent()) + { + CActor* A = smart_cast(object().H_Parent()); + return ( A && A->HUDview() && HudItemData() && HudItemData() ); + }else + return FALSE; +} + +void CHudItem::PlayAnimIdle() +{ + if (TryPlayAnimIdle()) return; + + PlayHUDMotion("anim_idle", TRUE, NULL, GetState()); +} + +bool CHudItem::TryPlayAnimIdle() +{ + if(MovingAnimAllowedNow()) + { + CActor* pActor = smart_cast(object().H_Parent()); + if(pActor) + { + CEntity::SEntityState st; + pActor->g_State(st); + if(st.bSprint) + { + PlayAnimIdleSprint(); + return true; + }else + if(!st.bCrouch && pActor->AnyMove()) + { + PlayAnimIdleMoving(); + return true; + } + } + } + return false; +} + +void CHudItem::PlayAnimIdleMoving() +{ + if (!HudBobbingEnabled()) + PlayHUDMotion("anim_idle_moving", TRUE, NULL, GetState()); + else + PlayHUDMotion("anim_idle", TRUE, NULL, GetState()); +} + +void CHudItem::PlayAnimIdleSprint() +{ + PlayHUDMotion("anim_idle_sprint", TRUE, NULL,GetState()); +} + +void CHudItem::OnMovementChanged(ACTOR_DEFS::EMoveCommand cmd) +{ + if(GetState()==eIdle && !m_bStopAtEndAnimIsRunning) + { + if( (cmd == ACTOR_DEFS::mcSprint) || (cmd == ACTOR_DEFS::mcAnyMove) ) + { + PlayAnimIdle (); + ResetSubStateTime (); + } + } +} + +attachable_hud_item* CHudItem::HudItemData() +{ + attachable_hud_item* hi = NULL; + if(!g_player_hud) + return hi; + + hi = g_player_hud->attached_item(0); + if (hi && hi->m_parent_hud_item == this) + return hi; + + hi = g_player_hud->attached_item(1); + if (hi && hi->m_parent_hud_item == this) + return hi; + + return NULL; +} diff --git a/src/xrGameLA/HudItem.h b/src/xrGameLA/HudItem.h new file mode 100644 index 000000000..0712d2471 --- /dev/null +++ b/src/xrGameLA/HudItem.h @@ -0,0 +1,192 @@ +#pragma once + +class CSE_Abstract; +class CPhysicItem; +class NET_Packet; +class CInventoryItem; +class CMotionDef; + +#include "actor_defs.h" +#include "inventory_space.h" +#include "hudsound.h" + +struct attachable_hud_item; +class motion_marks; + +class CHUDState +{ +public: +enum EHudStates { + eIdle = 0, + eShowing, + eHiding, + eHidden, + eBore, + eLastBaseState = eBore, +}; + +private: + u32 m_hud_item_state; + u32 m_nextState; + u32 m_dw_curr_state_time; +protected: + u32 m_dw_curr_substate_time; +public: + CHUDState () {SetState(eHidden);} + IC u32 GetNextState () const {return m_nextState;} + IC u32 GetState () const {return m_hud_item_state;} + + IC void SetState (u32 v) {m_hud_item_state = v; m_dw_curr_state_time=Device.dwTimeGlobal;ResetSubStateTime();} + IC void SetNextState (u32 v) {m_nextState = v;} + IC u32 CurrStateTime () const {return Device.dwTimeGlobal-m_dw_curr_state_time;} + IC void ResetSubStateTime () {m_dw_curr_substate_time=Device.dwTimeGlobal;} + virtual void SwitchState (u32 S) = 0; + virtual void OnStateSwitch (u32 S) = 0; +}; + +class CHudItem :public CHUDState +{ +protected: + CHudItem (); + virtual ~CHudItem (); + virtual DLL_Pure* _construct (); + + Flags16 m_huditem_flags; + enum{ + fl_pending = (1<<0), + fl_renderhud = (1<<1), + fl_inertion_enable = (1<<2), + fl_inertion_allow = (1<<3), + fl_aiming = (1<<4), + fl_collide_hud = (1<<5), + fl_strafe_hud = (1<<6), + fl_bobbing_hud = (1<<7), + }; + + struct{ + const CMotionDef* m_current_motion_def; + shared_str m_current_motion; + u32 m_dwMotionCurrTm; + u32 m_dwMotionStartTm; + u32 m_dwMotionEndTm; + u32 m_startedMotionState; + u8 m_started_rnd_anim_idx; + bool m_bStopAtEndAnimIsRunning; + }; +public: + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC) {return TRUE;}; + virtual void net_Destroy () {}; + virtual void OnEvent (NET_Packet& P, u16 type); + + virtual void OnH_A_Chield (); + virtual void OnH_B_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + virtual void OnH_A_Independent (); + + virtual void PlaySound (LPCSTR alias, const Fvector& position); + void StopAllSounds() { m_sounds.StopAllSounds(); }; + + virtual bool Action (u16 cmd, u32 flags) {return false;} + void OnMovementChanged (ACTOR_DEFS::EMoveCommand cmd) ; + + virtual u8 GetCurrentHudOffsetIdx () {return 0;} + + BOOL GetHUDmode (); + IC BOOL IsPending () const { return !!m_huditem_flags.test(fl_pending);} + + virtual bool Activate (); + virtual void Deactivate (); + virtual void SendDeactivateItem (); + virtual void OnActiveItem () {}; + virtual void OnHiddenItem () {}; + virtual void SendHiddenItem (); //same as OnHiddenItem but for client... (sends message to a server)... + virtual void OnMoveToRuck (); + + bool IsHidden () const { return GetState() == eHidden;} // Does weapon is in hidden state + bool IsHiding () const { return GetState() == eHiding;} + bool IsShowing () const { return GetState() == eShowing;} + + virtual void SwitchState (u32 S); + virtual void OnStateSwitch (u32 S); + + virtual void OnAnimationEnd (u32 state); + virtual void OnAnimationStart (u32 state, u32 anim_time); + virtual void OnMotionMark (u32 state, const motion_marks&){}; + + virtual void PlayAnimIdle (); + virtual void PlayAnimBore (); + bool TryPlayAnimIdle (); + virtual bool MovingAnimAllowedNow () {return true;} + + virtual void PlayAnimIdleMoving (); + virtual void PlayAnimIdleSprint (); + + virtual void UpdateCL (); + virtual void renderable_Render (); + + + virtual void UpdateHudAdditonal (Fmatrix&); + + + virtual void UpdateXForm () = 0; + + u32 PlayHUDMotion (const shared_str& M, BOOL bMixIn, CHudItem* W, u32 state); + u32 PlayHUDMotion_noCB (const shared_str& M, BOOL bMixIn); + void StopCurrentAnimWithoutCallback(); + + IC void RenderHud (BOOL B) { m_huditem_flags.set(fl_renderhud, B);} + IC BOOL RenderHud () { return m_huditem_flags.test(fl_renderhud);} + attachable_hud_item* HudItemData (); + virtual void on_a_hud_attach (); + virtual void on_b_hud_detach (); + IC BOOL HudInertionEnabled () const { return m_huditem_flags.test(fl_inertion_enable);} + IC BOOL HudInertionAllowed () const { return m_huditem_flags.test(fl_inertion_allow);} + + IC BOOL HudCollideEnabled () const { return m_huditem_flags.test(fl_collide_hud);} + IC BOOL HudStrafeInertionEnabled () const { return m_huditem_flags.test(fl_strafe_hud);} + IC BOOL HudBobbingEnabled () const { return m_huditem_flags.test(fl_bobbing_hud);} + + + virtual void render_hud_mode () {}; + virtual bool need_renderable () {return true;}; + virtual void render_item_3d_ui () {} + virtual bool render_item_3d_ui_query () {return false;} + + virtual bool CheckCompatibility (CHudItem*) {return true;} +protected: + + IC void SetPending (BOOL H) { m_huditem_flags.set(fl_pending, H);} + shared_str hud_sect; + + //кадры момента пересчета XFORM и FirePos + u32 dwFP_Frame; + u32 dwXF_Frame; + + IC void EnableHudInertion (BOOL B) { m_huditem_flags.set(fl_inertion_enable, B);} + IC void AllowHudInertion (BOOL B) { m_huditem_flags.set(fl_inertion_allow, B);} + + IC void EnableHudCollide (BOOL B) { m_huditem_flags.set(fl_collide_hud, B);} + IC void EnableHudStrafeInertion (BOOL B) { m_huditem_flags.set(fl_strafe_hud, B);} + IC void EnableHudBobbing (BOOL B) { m_huditem_flags.set(fl_bobbing_hud, B);} + + u32 m_animation_slot; + + HUD_SOUND_COLLECTION m_sounds; + +private: + CPhysicItem *m_object; + CInventoryItem *m_item; + +public: + const shared_str& HudSection () const { return hud_sect;} + IC CPhysicItem& object () const { VERIFY(m_object); return(*m_object);} + IC CInventoryItem& item () const { VERIFY(m_item); return(*m_item);} + IC u32 animation_slot () { return m_animation_slot;} + + virtual void on_renderable_Render () = 0; + virtual void debug_draw_firedeps () {}; + + virtual CHudItem* cast_hud_item () { return this; } +}; + diff --git a/src/xrGameLA/HudItemCallback.cpp b/src/xrGameLA/HudItemCallback.cpp new file mode 100644 index 000000000..09606bdd2 --- /dev/null +++ b/src/xrGameLA/HudItemCallback.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "HudItem.h" +#include "script_game_object.h" +#include "pch_script.h" +#include "script_callback_ex.h" +#include "actor.h" + +void CHudItem::OnAnimationEnd(u32 state) +{ + switch(state) + { + case eBore: + { + SwitchState (eIdle); + } break; + } + + CActor* actor = 0; +// Msg("%d CHudItem::OnAnimationEnd %d %s", Device.dwTimeGlobal, state, object().cName().c_str()); + if ((actor = smart_cast(m_object->H_Parent())) != NULL) + { + actor->callback(GameObject::eHudAnimCompleted)(m_object->lua_game_object(), state); + // Msg("OnAnimationEnd callback called"); + } +} + +void CHudItem::OnAnimationStart(u32 state, u32 anim_time) +{ + CActor* actor = 0; + //Msg("%d CHudItem::OnAnimationStart %d %s %d", Device.dwTimeGlobal, state, object().cName().c_str(), anim_time); + if ((actor = smart_cast(m_object->H_Parent())) != NULL) + { + actor->callback(GameObject::eHudAnimStarted)(m_object->lua_game_object(), state, anim_time); + // Msg("OnAnimationStart callback called"); + } +} diff --git a/src/xrGameLA/HudSound.cpp b/src/xrGameLA/HudSound.cpp new file mode 100644 index 000000000..517132972 --- /dev/null +++ b/src/xrGameLA/HudSound.cpp @@ -0,0 +1,210 @@ +#include "stdafx.h" + +#include "HudSound.h" + +float psHUDSoundVolume = 1.0f; +float psHUDStepSoundVolume = 1.0f; +void InitHudSoundSettings() +{ + psHUDSoundVolume = pSettings->r_float("hud_sound", "hud_sound_vol_k"); + psHUDStepSoundVolume = pSettings->r_float("hud_sound", "hud_step_sound_vol_k"); +} + +void HUD_SOUND_ITEM::LoadSound( LPCSTR section, LPCSTR line, + HUD_SOUND_ITEM& hud_snd, int type) +{ + hud_snd.m_activeSnd = NULL; + hud_snd.sounds.clear (); + + string256 sound_line; + xr_strcpy (sound_line,line); + int k=0; + while( pSettings->line_exist(section, sound_line) ){ + hud_snd.sounds.push_back( SSnd() ); + SSnd& s = hud_snd.sounds.back(); + + LoadSound (section, sound_line, s.snd, type, &s.volume, &s.delay); + xr_sprintf (sound_line,"%s%d",line,++k); + }//while +} + +void HUD_SOUND_ITEM::LoadSound(LPCSTR section, + LPCSTR line, + ref_sound& snd, + int type, + float* volume, + float* delay) +{ + LPCSTR str = pSettings->r_string(section, line); + string256 buf_str; + + int count = _GetItemCount (str); + R_ASSERT(count); + + _GetItem(str, 0, buf_str); + snd.create(buf_str, st_Effect,type); + + + if(volume != NULL) + { + *volume = 1.f; + if(count>1) + { + _GetItem (str, 1, buf_str); + if(xr_strlen(buf_str)>0) + *volume = (float)atof(buf_str); + } + } + + if(delay != NULL) + { + *delay = 0; + if(count>2) + { + _GetItem (str, 2, buf_str); + if(xr_strlen(buf_str)>0) + *delay = (float)atof(buf_str); + } + } +} + +void HUD_SOUND_ITEM::DestroySound(HUD_SOUND_ITEM& hud_snd) +{ + xr_vector::iterator it = hud_snd.sounds.begin(); + for(;it!=hud_snd.sounds.end();++it) + (*it).snd.destroy(); + hud_snd.sounds.clear (); + + hud_snd.m_activeSnd = NULL; +} + +void HUD_SOUND_ITEM::PlaySound( HUD_SOUND_ITEM& hud_snd, + const Fvector& position, + const CObject* parent, + bool b_hud_mode, + bool looped, + u8 index) +{ + if (hud_snd.sounds.empty()) return; + + hud_snd.m_activeSnd = NULL; + StopSound (hud_snd); + + u32 flags = b_hud_mode?sm_2D:0; + if(looped) + flags |= sm_Looped; + + if(index==u8(-1)) + index = (u8)Random.randI(hud_snd.sounds.size()); + + hud_snd.m_activeSnd = &hud_snd.sounds[ index ]; + + + hud_snd.m_activeSnd->snd.play_at_pos( const_cast(parent), + flags&sm_2D?Fvector().set(0,0,0):position, + flags, + hud_snd.m_activeSnd->delay); + + hud_snd.m_activeSnd->snd.set_volume (hud_snd.m_activeSnd->volume * b_hud_mode?psHUDSoundVolume:1.0f); +} + +void HUD_SOUND_ITEM::StopSound(HUD_SOUND_ITEM& hud_snd) +{ + xr_vector::iterator it = hud_snd.sounds.begin(); + for(;it!=hud_snd.sounds.end();++it) + (*it).snd.stop (); + hud_snd.m_activeSnd = NULL; +} + +//---------------------------------------------------------- +HUD_SOUND_COLLECTION::~HUD_SOUND_COLLECTION() +{ + xr_vector::iterator it = m_sound_items.begin(); + xr_vector::iterator it_e = m_sound_items.end(); + + for(;it!=it_e;++it) + { + HUD_SOUND_ITEM::StopSound (*it); + HUD_SOUND_ITEM::DestroySound (*it); + } + + m_sound_items.clear(); +} + +HUD_SOUND_ITEM* HUD_SOUND_COLLECTION::FindSoundItem(LPCSTR alias, bool b_assert) +{ + xr_vector::iterator it = std::find(m_sound_items.begin(),m_sound_items.end(),alias); + + if(it!=m_sound_items.end()) + return &*it; + else{ + R_ASSERT3(!b_assert,"sound item not found in collection", alias); + return NULL; + } +} + +void HUD_SOUND_COLLECTION::PlaySound( LPCSTR alias, + const Fvector& position, + const CObject* parent, + bool hud_mode, + bool looped, + u8 index) +{ + xr_vector::iterator it = m_sound_items.begin(); + xr_vector::iterator it_e = m_sound_items.end(); + for(;it!=it_e;++it) + { + if(it->m_b_exclusive) + HUD_SOUND_ITEM::StopSound (*it); + } + + + HUD_SOUND_ITEM* snd_item = FindSoundItem(alias, true); + HUD_SOUND_ITEM::PlaySound (*snd_item, position, parent, hud_mode, looped, index); +} + +void HUD_SOUND_COLLECTION::StopSound(LPCSTR alias) +{ + HUD_SOUND_ITEM* snd_item = FindSoundItem(alias, true); + HUD_SOUND_ITEM::StopSound (*snd_item); +} + +void HUD_SOUND_COLLECTION::SetPosition(LPCSTR alias, const Fvector& pos) +{ + HUD_SOUND_ITEM* snd_item = FindSoundItem(alias, true); + if(snd_item->playing()) + snd_item->set_position (pos); +} + +void HUD_SOUND_COLLECTION::StopAllSounds() +{ + xr_vector::iterator it = m_sound_items.begin(); + xr_vector::iterator it_e = m_sound_items.end(); + + for(;it!=it_e;++it) + { + HUD_SOUND_ITEM::StopSound (*it); + } +} + +void HUD_SOUND_COLLECTION::LoadSound( LPCSTR section, + LPCSTR line, + LPCSTR alias, + bool exclusive, + int type) +{ + HUD_SOUND_ITEM* snd_item = FindSoundItem(alias, false); + if (snd_item != nullptr) + { + HUD_SOUND_ITEM::StopSound (*snd_item); + HUD_SOUND_ITEM::DestroySound (*snd_item); + } + else + { + m_sound_items.resize (m_sound_items.size() + 1); + snd_item = &m_sound_items.back(); + } + HUD_SOUND_ITEM::LoadSound (section, line, *snd_item, type); + snd_item->m_alias = alias; + snd_item->m_b_exclusive = exclusive; +} diff --git a/src/xrGameLA/HudSound.h b/src/xrGameLA/HudSound.h new file mode 100644 index 000000000..449cb79cc --- /dev/null +++ b/src/xrGameLA/HudSound.h @@ -0,0 +1,81 @@ +#pragma once + + +struct HUD_SOUND_ITEM +{ + HUD_SOUND_ITEM():m_activeSnd(NULL),m_b_exclusive(false) {} + + static void LoadSound ( LPCSTR section, LPCSTR line, + ref_sound& hud_snd, + int type = sg_SourceType, + float* volume = NULL, + float* delay = NULL); + + static void LoadSound ( LPCSTR section, + LPCSTR line, + HUD_SOUND_ITEM& hud_snd, + int type = sg_SourceType); + + static void DestroySound ( HUD_SOUND_ITEM& hud_snd); + + static void PlaySound ( HUD_SOUND_ITEM& snd, + const Fvector& position, + const CObject* parent, + bool hud_mode, + bool looped = false, + u8 index=u8(-1)); + + static void StopSound ( HUD_SOUND_ITEM& snd); + + ICF BOOL playing () + { + if (m_activeSnd) return m_activeSnd->snd._feedback()?TRUE:FALSE; + else return FALSE; + } + + ICF void set_position ( const Fvector& pos) + { + if(m_activeSnd) { + if (m_activeSnd->snd._feedback()&&!m_activeSnd->snd._feedback()->is_2D()) + m_activeSnd->snd.set_position (pos); + else m_activeSnd = NULL; + } + } + + struct SSnd { + ref_sound snd; + float delay; //задержка перед проигрыванием + float volume; //громкость + }; + shared_str m_alias; + SSnd* m_activeSnd; + bool m_b_exclusive; + xr_vector sounds; + + bool operator == (LPCSTR alias) const{return 0==stricmp(m_alias.c_str(),alias);} +}; + +class HUD_SOUND_COLLECTION +{ + xr_vector m_sound_items; + HUD_SOUND_ITEM* FindSoundItem ( LPCSTR alias, bool b_assert); +public: + ~HUD_SOUND_COLLECTION(); + void PlaySound ( LPCSTR alias, + const Fvector& position, + const CObject* parent, + bool hud_mode, + bool looped = false, + u8 index=u8(-1)); + + void StopSound ( LPCSTR alias); + + void LoadSound ( LPCSTR section, + LPCSTR line, + LPCSTR alias, + bool exclusive = false, + int type = sg_SourceType); + + void SetPosition ( LPCSTR alias, const Fvector& pos); + void StopAllSounds (); +}; diff --git a/src/xrGameLA/IColisiondamageInfo.h b/src/xrGameLA/IColisiondamageInfo.h new file mode 100644 index 000000000..1e5b4639f --- /dev/null +++ b/src/xrGameLA/IColisiondamageInfo.h @@ -0,0 +1,16 @@ +#ifndef I_COLLISION_DAMAGE_INFO_H +#define I_COLLISION_DAMAGE_INFO_H +struct SCollisionHitCallback; +class ICollisionDamageInfo +{ +public: + virtual float ContactVelocity () const =0; + virtual void HitDir (Fvector &dir) const =0; + virtual const Fvector& HitPos () const =0; + virtual u16 DamageInitiatorID () const =0; + virtual CObject *DamageInitiator () const =0; + virtual ALife::EHitType HitType () const =0; + virtual SCollisionHitCallback *HitCallback () const =0; + virtual void Reinit () =0; +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/IKFoot.cpp b/src/xrGameLA/IKFoot.cpp new file mode 100644 index 000000000..ed4bf9afe --- /dev/null +++ b/src/xrGameLA/IKFoot.cpp @@ -0,0 +1,442 @@ +#include "stdafx.h" + +#include "ikfoot.h" + +#include "ik_collide_data.h" +#include "GameObject.h" + + +#include "ode_include.h" +#include "MathUtils.h" +#include "../Include/xrRender/Kinematics.h" +#include "../bone.h" +#include "../ennumerateVertices.h" +#include + + +#ifdef DEBUG +#include "PHDebug.h" +#endif + + +CIKFoot::CIKFoot( ): + m_bind_b2_to_b3 ( Fidentity ), + m_ref_bone ( u16( -1 ) ), + m_foot_bone_id ( BI_NONE ), + m_toe_bone_id ( BI_NONE ), + m_K ( 0 ) +{ + +} + + +void CIKFoot::Create ( IKinematics *K, LPCSTR section, u16 bones[4] ) +{ + VERIFY(K); + m_K = K; + + ///defaults + m_ref_bone = 2; + if( m_ref_bone == 2 ) + { + m_foot_normal.v .set( 1, 0, 0 );//2 + m_foot_normal.bone = 2; + m_foot_direction.v .set( 0, 0, 1 );//2 + m_foot_direction.bone = 2; + } else + { + m_foot_normal.v .set( 0, 0, -1 );//3 + m_foot_normal.bone = 3; + m_foot_direction.v .set( 1, 0, 0 );//3 + m_foot_direction.bone = 3; + } + + // m_foot_normal.v .set( 1, 0, 0 );//2 + // m_foot_normal.bone = 2; + + //load settings + if( section ) + { + if( !!K->LL_UserData()->r_bool( section, "align_toe" )) + m_ref_bone = 3; + m_foot_normal.bone = m_ref_bone; + m_foot_direction.bone = m_ref_bone; + + m_foot_normal.v = Kinematics()->LL_UserData()->r_fvector3( section, "foot_normal" ); + m_foot_direction.v = Kinematics()->LL_UserData()->r_fvector3( section, "foot_direction" ); + } + set_toe( bones ); +} + +struct envc : +private boost::noncopyable, +public SEnumVerticesCallback +{ + Fvector &pos; + Fvector start_pos; + const Fmatrix &i_bind_transform; + const Fvector &ax; + envc( const Fmatrix &_i_bind_transform, const Fvector &_ax, Fvector &_pos ): + SEnumVerticesCallback(), i_bind_transform( _i_bind_transform ), ax( _ax ), pos( _pos ) { start_pos.set( 0, 0, 0 ); } + void operator () (const Fvector& p) + { + Fvector lpos; + i_bind_transform.transform_tiny(lpos, p ); + //Fvector diff;diff.sub( lpos, pos ); + if( Fvector().sub( lpos, start_pos ).dotproduct( ax ) > Fvector().sub( pos, start_pos ).dotproduct( ax ) ) + pos.set( lpos ); + } +}; +void CIKFoot::set_toe( u16 bones[4] ) +{ + + VERIFY( Kinematics() ); + + m_foot_bone_id = bones[2]; + m_toe_bone_id = bones[3]; + + xr_vector binds; + Kinematics()->LL_GetBindTransform( binds ); + + const Fmatrix bind_ref = binds[ bones[m_ref_bone] ]; + const Fmatrix ibind_ref = Fmatrix().invert( bind_ref ); + + const Fmatrix bind2 = binds[ bones[2] ] ; + const Fmatrix ibind2 = Fmatrix().invert( bind2 ); + + //const Fmatrix ref_to_b2 = Fmatrix().mul_43( ibind2, bind_ref ); + const Fmatrix b2to_ref = Fmatrix().mul_43( ibind_ref, bind2 ); + + const Fmatrix bind3 = binds[ bones[3] ] ; + const Fmatrix ibind3 = Fmatrix().invert( bind3 ); + + m_bind_b2_to_b3.mul_43( ibind2, bind3 ); + /////////////////////////////////////////////////////// + Fvector ax ,foot_normal, foot_dir; + get_local_vector( 2, foot_normal, m_foot_normal ); + get_local_vector( 2, foot_dir, m_foot_direction ); + + //ref_to_b2.transform_tiny( foot_normal, m_foot_normal.v ); + //ref_to_b2.transform_tiny( foot_dir, m_foot_direction.v ); + + ax.add( foot_normal, foot_dir ); + ax.normalize(); + /////////////////////////////////////////////////////// + Fvector pos; pos.set( 0, 0, 0 ); + Fmatrix ibind = ibind3; + envc pred( ibind, ax, pos ); + ///////////////////////////////////////////////////////// + Kinematics()->EnumBoneVertices( pred, bones[3] ); + bind3.transform_tiny( pos ); + ibind2.transform_tiny( pos ); + m_toe_position.v.set( pos ); + ///////////////////////////////////////////////////////// + ibind.set( ibind2 ); + ax.set( foot_normal ); + Kinematics()->EnumBoneVertices( pred, bones[2] ); + m_toe_position.v.x = _max( pos.x, m_toe_position.v.x ); + ///////////////////////////////////////////////////////// + ax.sub( foot_normal, foot_dir ); + ax.normalize(); + pred.start_pos.set(0,0,0);pos.set( 0, 0, 0 ); + Kinematics()->EnumBoneVertices( pred, bones[2] ); + m_heel_position.v = pred.pos ; + m_heel_position.v.add( Fvector().mul( foot_dir, + Fvector().sub( m_toe_position.v, pos ).dotproduct( foot_dir ) * 0.2f ) + ); + m_heel_position.bone = 2; + /////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////// + bind2.transform_tiny( m_toe_position.v ); + ibind_ref.transform_tiny( m_toe_position.v ); + m_toe_position.bone = ref_bone(); + ///////////////////////////////////////////////////////// + + get_local_vector( foot_normal, m_foot_normal ); + m_foot_width = ( Fvector().sub( m_toe_position.v, b2to_ref.c ) ).dotproduct( foot_normal ); + + +} + +Fmatrix& CIKFoot::foot_to_ref_bone_transform ( Fmatrix& m ) const +{ + if(m_ref_bone == 2) + { + m.set( Fidentity ); + return m; + } + m.mul_43( Fmatrix().invert( Kinematics()->LL_GetTransform( m_foot_bone_id ) ), Kinematics()->LL_GetTransform( m_toe_bone_id ) ); + return m; +} + +Fmatrix& CIKFoot::ref_bone_to_foot_transform ( Fmatrix& m ) const +{ + return m.invert(Fmatrix().set( foot_to_ref_bone_transform ( m ) )); +} + +Fmatrix& CIKFoot::foot_to_ref_bone ( Fmatrix &ref_bone, const Fmatrix &foot ) const +{ + if( m_ref_bone == 2 ) + { + ref_bone = foot; + return ref_bone; + } + Fmatrix m; + foot_to_ref_bone_transform ( m ); + ref_bone.mul_43( foot, m ); + return ref_bone; +} + +Fmatrix& CIKFoot::foot_to_ref_bone ( Fmatrix &m ) const +{ + return foot_to_ref_bone( m, Fmatrix( ).set( m ) ); +} + +Fmatrix& CIKFoot::ref_bone_to_foot ( Fmatrix &foot, const Fmatrix &ref_bone ) const +{ + if(m_ref_bone == 2) + { + foot = ref_bone; + return foot; + } + Fmatrix m; + ref_bone_to_foot_transform( m ); +/* + Fmatrix b2 = Kinematics()->LL_GetTransform( m_bones[2] ); + Fmatrix b3 = Kinematics()->LL_GetTransform( m_bones[3] ); + //m.mul_43( Fmatrix().invert( Kinematics()->LL_GetTransform(m_bones[2] ) ),Kinematics()->LL_GetTransform( m_bones[3] ) ); + + Fmatrix ib3; ib3.invert( b3 ); + Fmatrix ib2; ib2.invert( b2 ); + m.mul_43( ib3, b2 ); + m.mul_43( ib2, b3 ); + m.invert(); +*/ + foot.mul_43( ref_bone, m ); + return foot; +} + +Fmatrix& CIKFoot::ref_bone_to_foot ( Fmatrix &m ) const +{ + return ref_bone_to_foot( m, Fmatrix().set( m ) ); +} +int ik_allign_free_foot = 0; +ik_goal_matrix::e_collide_state CIKFoot::CollideFoot( float angle, float &out_angle, const Fvector &global_toe, const Fvector &foot_normal, const Fvector &global_bone_pos, const Fplane &p, const Fvector &ax )const +{ + + float dfoot_tri =-p.d - p.n.dotproduct( global_bone_pos ); // dist from foot bone pos to tri plain + Fvector axp; axp.sub( global_toe, global_bone_pos ); + float dfoot_toe =p.n.dotproduct( axp ); + out_angle = angle; + if( dfoot_tri < m_foot_width * _abs( foot_normal.dotproduct( p.n ) ) ) + return ik_goal_matrix::cl_aligned; + axp.sub( Fvector( ).mul( ax, axp.dotproduct( ax ) ) ); //vector from nc_toe to ax + float dtoe_ax = axp.magnitude(); + + out_angle = 0.f; + + if( dtoe_ax dtoe_ax - EPS_S ) + return ik_goal_matrix::cl_free; + if( dfoot_toe < dfoot_tri ) + return ik_goal_matrix::cl_free; + + float ang_nc = acosf( dfoot_toe/dtoe_ax ); + float ang_c = acosf( dfoot_tri/dtoe_ax ); + out_angle = -( ang_c - ang_nc ); + return ik_goal_matrix::cl_rotational; +} + + + +static const float min_dot = 0.9f;// M_SQRT1_2;//M_SQRT1_2; + +bool CIKFoot::make_shift( Fmatrix &xm,const Fvector &cl_point, bool collide, const Fplane &p, const Fvector &pick_dir )const +{ + Fvector shift = pick_dir; + + //Fvector toe; ToePosition( toe ); xm.transform_tiny( toe ); + Fvector point; + xm.transform_tiny( point, cl_point ); + float dot = p.n.dotproduct( shift ); + if( _abs( dot ) < min_dot ) + { + shift.add( Fvector( ).mul( p.n, min_dot - _abs( dot ) ) ); + dot = p.n.dotproduct( shift ); + } + VERIFY( !fis_zero( dot ) ); + float shift_m = ( -p.d - p.n.dotproduct( point ) )/dot; + if(collide && shift_m > 0.f ) + return false; + clamp( shift_m, -collide_dist, collide_dist ); + shift.mul( shift_m ); + xm.c.add( shift ); +#if 0 + if(shift_m > 0.f) + { + DBG_OpenCashedDraw(); + DBG_DrawLine( toe, Fvector().add( toe, shift ), D3DCOLOR_XRGB( 255, 255, 255 ) ); + DBG_ClosedCashedDraw( 1000 ); + } +#endif + return true; +} + +bool CIKFoot::GetFootStepMatrix( ik_goal_matrix &m, const SCalculateData& cd, const SIKCollideData &cld, bool collide, bool rotate )const +{ + return GetFootStepMatrix( m, cd.state.anim_pos, cld, collide, rotate ); +} + +ik_goal_matrix::e_collide_state CIKFoot::rotate( Fmatrix &xm, const Fplane& p, const Fvector &foot_normal, const Fvector &global_point, bool collide) const +{ + Fvector ax; ax.crossproduct( p.n, foot_normal ); + float s=ax.magnitude( ); + clamp( s, 0.f, 1.f ); + float angle = asinf( -s ); + VERIFY( _valid( angle ) ); + clamp( angle, -M_PI/6, M_PI/6 ); + ik_goal_matrix::e_collide_state cl_state = ik_goal_matrix::cl_undefined; + if( !fis_zero( s ) ) + { + cl_state = ik_goal_matrix::cl_aligned; + ax.mul( 1.f/s ); + ref_bone_to_foot( xm ); + if( collide ) + cl_state = CollideFoot( angle, angle, global_point, foot_normal, xm.c, p, ax); + //if( cld.m_pick_dir ) + Fvector c = xm.c; + xm.mulA_43( Fmatrix( ).rotation( ax, angle ) ); + xm.c = c; + foot_to_ref_bone( xm ); + } + return cl_state; +} + +bool CIKFoot::GetFootStepMatrix( ik_goal_matrix &m, const Fmatrix &g_anim, const SIKCollideData &cld, bool collide, bool rotation, bool b_make_shift/*=true*/ )const +{ + const Fmatrix global_anim = g_anim; + Fvector local_point; ToePosition( local_point ); //toe position in bone[2] space + Fvector global_point; global_anim.transform_tiny( global_point, local_point ); //non collided toe in global space + Fvector foot_normal; FootNormal( foot_normal ); + global_anim.transform_dir( foot_normal ); +#ifdef DEBUG + //if( ph_dbg_draw_mask.test( phDbgDrawIKGoal ) ) + //{ + // DBG_DrawLine( global_point, Fvector().add( global_point, foot_normal ), D3DCOLOR_XRGB( 0, 255, 255) ); + //} +#endif + if( cld.m_collide_point == ik_foot_geom::heel || cld.m_collide_point == ik_foot_geom::side ) + { + Fmatrix foot;ref_bone_to_foot( foot, g_anim ); + Fvector heel; + HeelPosition( heel ); + foot.transform_tiny(global_point, heel ); +#ifdef DEBUG + if( ph_dbg_draw_mask.test( phDbgDrawIKGoal ) ) + DBG_DrawPoint( global_point, 0.01, D3DCOLOR_XRGB( 0, 255, 255)); +#endif + Fmatrix foot_to_ref; + ref_bone_to_foot_transform(foot_to_ref).transform_tiny(local_point, heel ); + } + + float dtoe_tri =-cld.m_plane.d - cld.m_plane.n.dotproduct( global_point ); + if( !cld.collided || _abs( dtoe_tri ) > collide_dist ) + { + m.set( global_anim, ik_goal_matrix::cl_free ); + return false; + } + + Fplane p = cld.m_plane; + Fmatrix xm; xm.set( global_anim ); + ik_goal_matrix::e_collide_state cl_state = ik_goal_matrix::cl_undefined; + if( rotation )//!collide || ik_allign_free_foot + cl_state = rotate( xm, p, foot_normal, global_point, collide ); + + if( b_make_shift && make_shift( xm, local_point, collide, p, cld.m_pick_dir ) ) + switch( cl_state ) + { + case ik_goal_matrix::cl_aligned : break; + case ik_goal_matrix::cl_undefined : + case ik_goal_matrix::cl_free : + cl_state = ik_goal_matrix::cl_translational; break; + case ik_goal_matrix::cl_rotational: + cl_state = ik_goal_matrix::cl_mixed; break; + default: NODEFAULT; + + } + else if( cl_state == ik_goal_matrix::cl_undefined ) + cl_state = ik_goal_matrix::cl_free; + + + VERIFY( _valid( xm ) ); + m.set( xm, cl_state ); +#ifdef DEBUG + if(ph_dbg_draw_mask.test( phDbgDrawIKGoal )) + { + DBG_DrawPoint( global_point, 0.03f, D3DCOLOR_RGBA( 255, 0, 0, 255 ) ); + } + if(!fsimilar( _abs( DET( g_anim ) - 1.f ), _abs( DET( m.get() ) - 1.f ), 0.001f ) ) + Msg("scale g_anim: %f scale m: %f ", DET( g_anim ) , DET( m.get() ) ); +#endif + + return true; +} + + +u16 CIKFoot::get_ref_bone ( const Fmatrix & foot_transform, const Fmatrix &toe_transform ) const +{ + Fvector n0,n1; + get_local_vector( 2, n0, m_foot_normal ); + get_local_vector( 3, n1, m_foot_normal ); + + foot_transform.transform_tiny( n0 ); + toe_transform.transform_tiny( n1 ); + + n0.normalize();n1.normalize(); + if( n0.dotproduct( n1 ) < 0.99f ) + return 3 ; + else + return 2 ; +} +void CIKFoot::set_ref_bone ( ) +{ + set_ref_bone( + get_ref_bone( Kinematics()->LL_GetTransform( m_foot_bone_id ) , + Kinematics()->LL_GetTransform( m_toe_bone_id ) + ) + ); +} +void CIKFoot::SetFootGeom ( ik_foot_geom &fg, const Fmatrix &ref_bone, const Fmatrix& object_matrix ) const +{ + Fmatrix gl_bone; gl_bone.mul_43( object_matrix, ref_bone ); + + Fvector pos_toe; ToePosition( pos_toe ); + gl_bone.transform_tiny( pos_toe ); + + Fvector heel; Fvector pos_hill; + Fmatrix foot =( Fmatrix( ).mul_43( object_matrix, ref_bone_to_foot( foot, ref_bone ) ) ); + foot.transform_tiny( pos_hill, HeelPosition( heel ) ); + const Fvector v_m = Fvector().add(pos_toe, pos_hill ).mul(0.5f) ; + + Fvector normal, direction; + get_local_vector( normal, m_foot_normal ); + get_local_vector( direction, m_foot_direction ); + + Fvector v_side = Fvector().crossproduct( normal, direction ); + gl_bone.transform_dir ( v_side ); + float vsm = v_side.magnitude(); + VERIFY( vsm > EPS_L ); + v_side.mul( Fvector().sub(pos_toe, pos_hill ).magnitude()/vsm ); + + fg.set( pos_toe, pos_hill, Fvector().add( v_m, v_side ) ); + +} +void CIKFoot::Collide( SIKCollideData &cld, ik_foot_collider &collider, const Fmatrix &ref_bone, const Fmatrix& object_matrix, CGameObject *O, bool foot_step ) const +{ + VERIFY( O->Visual( )->dcast_PKinematics() == Kinematics() ); + + ik_foot_geom fg; + SetFootGeom( fg ,ref_bone, object_matrix ); + collider.collide( cld, fg, O, foot_step ); +} diff --git a/src/xrGameLA/IKFoot.h b/src/xrGameLA/IKFoot.h new file mode 100644 index 000000000..95af21e1d --- /dev/null +++ b/src/xrGameLA/IKFoot.h @@ -0,0 +1,76 @@ +#pragma once + +#include "ik_calculate_data.h" +#include "ik_foot_collider.h" + +struct local_vector +{ + Fvector v; + u16 bone; + local_vector(): bone( u16(-1) ), v( Fvector().set( 0, 0, 0 ) ){ } +}; + +class IKinematics; +struct SCalculateData; +struct SIKCollideData; +class CGameObject; + + +class CIKFoot +{ +public: + CIKFoot ( ); + void Create ( IKinematics *K, LPCSTR section, u16 bones[4] ); +IC void set_ref_bone ( u16 ref_bone ) { m_ref_bone = ref_bone; } + void set_ref_bone ( ); + u16 get_ref_bone ( const Fmatrix & foot_transform, const Fmatrix &toe_transform ) const; +public: +IC Fvector &ToePosition ( Fvector &toe_position ) const; +IC Fvector &HeelPosition ( Fvector &heel_position ) const; +IC u16 ref_bone ( ) const { return m_ref_bone; } + Fmatrix &ref_bone_to_foot ( Fmatrix &foot, const Fmatrix &ref_bone ) const; + Fmatrix &ref_bone_to_foot ( Fmatrix &ref_bone ) const; +private: +public: +IC Fvector &FootNormal ( Fvector &foot_normal ) const; +private: +IC Fvector &get_local_vector ( Fvector &v, const local_vector &lv )const; +IC Fvector& get_local_vector ( u16 bone, Fvector &v, const local_vector &lv )const; + Fmatrix &ref_bone_to_foot_transform ( Fmatrix& m ) const; + Fmatrix &foot_to_ref_bone_transform ( Fmatrix& m ) const; + Fmatrix &foot_to_ref_bone ( Fmatrix &ref_bone, const Fmatrix &foot ) const; + Fmatrix &foot_to_ref_bone ( Fmatrix &foot ) const; +public: + bool GetFootStepMatrix ( ik_goal_matrix &m, const SCalculateData& cd, const SIKCollideData &cld, bool collide, bool rotate )const; + bool GetFootStepMatrix ( ik_goal_matrix &m, const Fmatrix &gl_nim, const SIKCollideData &cld, bool collide, bool rotate, bool make_shift = true )const; + void SetFootGeom ( ik_foot_geom &fg, const Fmatrix &ref_bone, const Fmatrix& object_matrix ) const; + void Collide ( SIKCollideData &cld, ik_foot_collider &collider, const Fmatrix &ref_bone, const Fmatrix& object_matrix, CGameObject *O, bool foot_step ) const; +private: + +ik_goal_matrix::e_collide_state CollideFoot ( float angle, float &out_angle, const Fvector &global_toe, const Fvector &foot_normal, const Fvector &global_bone_pos, const Fplane &p, const Fvector &ax )const; +ik_goal_matrix::e_collide_state rotate ( Fmatrix &xm, const Fplane& p, const Fvector &normal, const Fvector &global_point, bool collide )const; + +IC bool make_shift ( Fmatrix &xm, const Fvector &cl_point, bool collide, const Fplane &p, const Fvector &pick_dir )const; + +private: + void set_toe ( u16 bones[4] ); +public: +IC IKinematics * Kinematics ( )const { return m_K; } + +private: + IKinematics *m_K; + + local_vector m_toe_position; + local_vector m_heel_position; + + local_vector m_foot_normal; + local_vector m_foot_direction; + + Fmatrix m_bind_b2_to_b3; + float m_foot_width; + u16 m_ref_bone; + u16 m_foot_bone_id; + u16 m_toe_bone_id; +}; + +#include "IKFoot_inl.h" \ No newline at end of file diff --git a/src/xrGameLA/IKFoot_inl.h b/src/xrGameLA/IKFoot_inl.h new file mode 100644 index 000000000..0b79cd17f --- /dev/null +++ b/src/xrGameLA/IKFoot_inl.h @@ -0,0 +1,52 @@ + +IC Fvector& CIKFoot::ToePosition( Fvector &toe_position ) const +{ + + //toe_position.set( m_toe_position ); + //return toe_position; + return get_local_vector( toe_position, m_toe_position ); +} +IC Fvector& CIKFoot::HeelPosition( Fvector &heel_position ) const +{ + heel_position.set( m_heel_position.v ); + return heel_position; + //return get_local_vector( heel_position, m_heel_position ); +} +IC Fvector& CIKFoot::FootNormal( Fvector &foot_normal ) const +{ + return get_local_vector(foot_normal, m_foot_normal ); +} +IC Fvector& CIKFoot::get_local_vector( Fvector &v, const local_vector &lv )const +{ + return get_local_vector( ref_bone(), v, lv ); +} +IC Fvector& CIKFoot::get_local_vector( u16 bone, Fvector &v, const local_vector &lv )const +{ + + if( bone == lv.bone ) + { + v.set(lv.v); + } + else if ( bone == 2 && lv.bone == 3 ) + { + m_bind_b2_to_b3.transform_tiny( v, lv.v ); + } + else if ( bone == 3 && lv.bone == 2 ) + { + Fmatrix().invert( m_bind_b2_to_b3 ).transform_tiny( v, lv.v ); + } + else + VERIFY( 0 ); + + return v; + + //switch( 1 + ref_bone() - lv.bone ) + //{ + // case 0: + // case 1: + //} + +} + + + diff --git a/src/xrGameLA/IKLimbsController.cpp b/src/xrGameLA/IKLimbsController.cpp new file mode 100644 index 000000000..64a4e7c10 --- /dev/null +++ b/src/xrGameLA/IKLimbsController.cpp @@ -0,0 +1,356 @@ +#include "stdafx.h" +#include "IKLimbsController.h" +#include "IK/IKLimb.h" +#include "physicsshellholder.h" +#include "ik_anim_state.h" +#include "mathutils.h" +#include "../../Include/xrRender/RenderVisual.h" +#include "../ennumerateVertices.h" +#include "ode_include.h" +#include "../../Include/xrRender/KinematicsAnimated.h" +#include "characterphysicssupport.h" +#include "../motion.h" +#ifdef DEBUG +#include "PHDebug.h" +#endif +CIKLimbsController::CIKLimbsController(): m_object(0), m_legs_blend(0) +{ + +} + +void CIKLimbsController::Create( CGameObject* O ) +{ + VERIFY( O ); + m_legs_blend = 0; + + IKinematics* K=smart_cast(O->Visual()); + m_object = O; + + VERIFY( K ); + u16 sz = 2; + if( K->LL_UserData() && K->LL_UserData()->section_exist( "ik" ) ) + sz = K->LL_UserData()->r_u16( "ik", "num_limbs"); + VERIFY( sz <= max_size ); + + _bone_chains.reserve( sz ); + for( u16 i = 0; sz > i; ++i ) + LimbSetup(); + + O->add_visual_callback(IKVisualCallback); + _pose_extrapolation.init( O->XFORM() ); +} + + +void CIKLimbsController::LimbSetup( ) +{ + _bone_chains.push_back( CIKLimb( ) ); + + IKinematicsAnimated *skeleton_animated = m_object->Visual( )->dcast_PKinematicsAnimated( ); + + _bone_chains.back( ).Create( ( u16 ) _bone_chains.size( )-1, skeleton_animated, true ); +} + +void CIKLimbsController::LimbCalculate( SCalculateData &cd ) +{ + cd.do_collide = m_legs_blend && !cd.m_limb->KinematicsAnimated()->LL_GetMotionDef( m_legs_blend->motionID )->marks.empty() ;//m_legs_blend->; + cd.m_limb->ApplyState( cd ); +} + +void CIKLimbsController::LimbUpdate( CIKLimb &L ) +{ + IKinematicsAnimated *skeleton_animated = m_object->Visual( )->dcast_PKinematicsAnimated( ); + VERIFY( skeleton_animated ); + L.Update( m_object, m_legs_blend, _pose_extrapolation ); +} + +IC void update_blend (CBlend* &b) +{ + if(b && CBlend::eFREE_SLOT == b->blend) + b = 0; +} + +IC float lerp(float t, float a, float b) +{ + return ( a + t * (b - a) ); +} + +void y_shift_bones( IKinematics* K, float shift ) +{ + u16 bc = K->LL_BoneCount(); + for(u16 i = 0; bc > i ; ++i ) + K->LL_GetTransform( i ).c.y +=shift ; +} +float CIKLimbsController::LegLengthShiftLimit ( float current_shift, const SCalculateData cd[max_size] ) +{ + float shift_down = -dInfinity; + const u16 sz =(u16)_bone_chains.size(); + for(u16 j = 0; sz > j; ++j ) + if( cd[j].state.foot_step ) + { + float s_down = cd[j].m_limb->ObjShiftDown( current_shift, cd[j] ); + if( shift_down < s_down ) + shift_down = s_down; + } + return shift_down; +} +static const float static_shift_object_speed = .2f; +float CIKLimbsController::StaticObjectShift ( const SCalculateData cd[max_size] ) +{ + const float current_shift = _object_shift.shift(); + + u16 cnt = 0; float shift_up =0; + const u16 sz =(u16)_bone_chains.size(); + for(u16 j = 0; sz > j; ++j ) + if( cd[j].state.foot_step ) + { + float s_up = cd[j].cl_shift.y + current_shift; + if( 0.f < s_up ) + { + shift_up += s_up; + ++cnt; + } + } + if( 0 < cnt ) + shift_up /= cnt; + float shift_down = LegLengthShiftLimit( current_shift, cd ); + float shift = 0; + if( shift_down > 0.f ) + shift = -shift_down; + else if( -shift_down < shift_up ) + shift = -shift_down; + else + shift = shift_up; + VERIFY( _valid( shift ) ); + _object_shift.set_taget( shift , _abs( current_shift - shift ) / static_shift_object_speed ); + return shift; +} + +bool CIKLimbsController::PredictObjectShift ( const SCalculateData cd[max_size] ) +{ + + float predict_time = FLT_MAX; + float predict_shift = 0.f; + bool ret = false; + const u16 sz =(u16)_bone_chains.size(); + for(u16 j = 0; sz > j; ++j ) + if( !cd[j].state.foot_step ) + { + float time = cd[j].m_limb->time_to_footstep(); + if( time < predict_time ) + { + float lshift = cd[j].m_limb->footstep_shift(); + if( lshift < 0.f ) + { + predict_time = time; + predict_shift = lshift; + ret = true; + } + } + } + if( ret ) + { + if( predict_time < EPS_S ) + predict_time = Device.fTimeDelta; + _object_shift.set_taget( predict_shift, predict_time ); + /* + float leg_length_limit = LegLengthShiftLimit( cd ); + if( leg_length_limit> 0.f ) + _object_shift.set_up_shift_limit( -leg_length_limit ); + else + _object_shift.set_up_shift_limit( FLT_MAX ); + */ + } + + return ret; +} + + +void CIKLimbsController::ObjectShift ( float static_shift, const SCalculateData cd[max_size]) +{ + + u16 cnt_in_step = 0; + const u16 sz =(u16)_bone_chains.size(); + for(u16 j = 0; sz > j; ++j ) + if( cd[j].m_limb->foot_step() ) + ++cnt_in_step; + + CPhysicsShellHolder *sh = smart_cast(m_object); + VERIFY( sh ); + //CCharacterPhysicsSupport *ch = sh->character_physics_support(); + _object_shift.freeze( !!Device.Paused() );//ch->interactive_motion() || + + if( cnt_in_step != sz && PredictObjectShift( cd ) )//cnt_in_step > 0 && + return; + StaticObjectShift( cd ); +} + +void CIKLimbsController::ShiftObject( const SCalculateData cd[max_size] ) +{ + IKinematics *skeleton_animated = m_object->Visual()->dcast_PKinematics( ); + VERIFY( skeleton_animated ); + // u16 root = skeleton_animated->LL_GetBoneRoot( ) ; + + //CBoneData &BD=skeleton_animated->LL_GetData(root); + + const float y_shift = _object_shift.shift(); + const u16 bones_count = skeleton_animated->LL_BoneCount(); + for( u16 i = 0; i < bones_count; ++i ) + skeleton_animated->LL_GetTransform( i ).c.y += y_shift; + + for( u16 i = 0; i < bones_count; ++i ) + { + CBoneInstance &bi = skeleton_animated->LL_GetBoneInstance( i ); + if(bi.callback()) + bi.callback()( &bi ); + skeleton_animated->LL_GetTransform_R( i ).c.y += y_shift; + } + // skeleton_animated->LL_GetTransform(root).c.y += _object_shift.shift(); + // skeleton_animated->Bone_Calculate(&BD, &Fidentity ); + +} + +int ik_shift_object = 1; +void CIKLimbsController::Calculate( ) +{ + update_blend( m_legs_blend ); + + Fmatrix &obj = m_object->XFORM( ); +#if 0 //DEBUG + if( ph_dbg_draw_mask1.test( phDbgDrawIKSHiftObject ) ) + _object_shift.dbg_draw( obj, _pose_extrapolation, Fvector().set( 0,2.5f,0)); +#endif + + SCalculateData cd[max_size]; + + + + xr_vector::iterator i, b = _bone_chains.begin(), e = _bone_chains.end(); + for( i = b ; e != i; ++i ) + { + cd[i-b] = SCalculateData ( *i, obj ); + LimbCalculate( cd[i-b] ); + } + + IKinematics *K = m_object->Visual()->dcast_PKinematics( ); + u16 root = K->LL_GetBoneRoot( ) ; + CBoneInstance &root_bi = K->LL_GetBoneInstance(root); + + BOOL sv_root_cb_ovwr = root_bi.callback_overwrite(); + BoneCallback sv_root_cb = root_bi.callback(); + + root_bi.set_callback( root_bi.callback_type(), 0, root_bi.callback_param(), TRUE ); + + + if( ik_shift_object )//&& ! m_object->animation_movement_controlled( ) + { + + ShiftObject( cd ); + } + + const u16 sz =(u16)_bone_chains.size(); + for(u16 j = 0; sz > j; ++j ) + cd[j].m_limb->SetGoal( cd[j] ); + + for(u16 j = 0; sz > j; ++j ) + { + + cd[j].m_limb->SolveBones( cd[j] ); + +#if 0 // DEBUG + if( ph_dbg_draw_mask1.test( phDbgDrawIKPredict ) ) + { + //IKinematics *K = m_object->Visual()->dcast_PKinematics( ); + u16 ref_bone_id = cd[j].m_limb->dbg_ref_bone_id(); + Fmatrix m =Fmatrix().mul( obj, K->LL_GetTransform( ref_bone_id ) ); + Fvector toe; + cd[j].m_limb->dbg_ik_foot().ToePosition( toe ); + m.transform_tiny( toe ); + DBG_DrawLine( toe, Fvector().add( toe, Fvector().set( 0, -_object_shift.shift(), 0 ) ), D3DCOLOR_XRGB( 255, 0, 0 ) ); + } +#endif + } + ObjectShift( 0, cd ); + + + + root_bi.set_callback( root_bi.callback_type(), sv_root_cb, root_bi.callback_param(), sv_root_cb_ovwr ); + +} + +void CIKLimbsController::Destroy(CGameObject* O) +{ +#ifdef _DEBUG + CPhysicsShellHolder* Sh = smart_cast(O); + VERIFY(Sh); + CIKLimbsController* ik = Sh->character_ik_controller(); + VERIFY( ik ); + VERIFY( ik == this ); +#endif + + O->remove_visual_callback(IKVisualCallback); + xr_vector::iterator i = _bone_chains.begin(), e = _bone_chains.end(); + for(;e!=i;++i) + i->Destroy(); + _bone_chains.clear(); +} + +void _stdcall CIKLimbsController:: IKVisualCallback( IKinematics* K ) +{ + //if (Device.Paused()) + // return; + +#ifdef DEBUG + if( ph_dbg_draw_mask1.test( phDbgIKOff ) ) + return; +#endif + + CGameObject* O=( ( CGameObject* )K->GetUpdateCallbackParam()); + CPhysicsShellHolder* Sh = smart_cast( O ); + VERIFY( Sh ); + CIKLimbsController* ik = Sh->character_ik_controller( ); + VERIFY( ik ); + ik->Calculate( ); +} + +void CIKLimbsController::PlayLegs( CBlend *b ) +{ + m_legs_blend = b; +#ifdef DEBUG + IKinematicsAnimated *skeleton_animated = m_object->Visual( )->dcast_PKinematicsAnimated( ); + VERIFY( skeleton_animated ); + anim_name = skeleton_animated->LL_MotionDefName_dbg( b->motionID ).first; + anim_set_name = skeleton_animated->LL_MotionDefName_dbg( b->motionID ).second; +#endif +} +void CIKLimbsController:: Update ( ) +{ +#ifdef DEBUG + if( ph_dbg_draw_mask1.test( phDbgIKOff ) ) + return; +#endif + IKinematicsAnimated *skeleton_animated = m_object->Visual()->dcast_PKinematicsAnimated( ); + VERIFY( skeleton_animated ); + + skeleton_animated->UpdateTracks(); + update_blend( m_legs_blend ); + + _pose_extrapolation.update( m_object->XFORM() ); + xr_vector::iterator i = _bone_chains.begin(), e = _bone_chains.end(); + for( ; e != i; ++i ) + LimbUpdate( *i ); + + /* + Fmatrix predict; + _pose_extrapolation.extrapolate( predict, Device.fTimeGlobal ); + + + + + DBG_DrawMatrix( m_object->XFORM(), 1 ); + DBG_DrawMatrix( predict, 1 ); + + _pose_extrapolation.extrapolate( predict, Device.fTimeGlobal + 1 ); + DBG_DrawMatrix( predict, 1 ); + */ +} + diff --git a/src/xrGameLA/IKLimbsController.h b/src/xrGameLA/IKLimbsController.h new file mode 100644 index 000000000..7c48dd422 --- /dev/null +++ b/src/xrGameLA/IKLimbsController.h @@ -0,0 +1,51 @@ +#pragma once + + +#include "IK/IKLimb.h" +#include "pose_extrapolation.h" +#include "ik_object_shift.h" +class IKinematicsAnimated; +class CGameObject ; +class CBlend ; +struct SIKCrlCalcData; + + + +class CIKLimbsController { +private: + static const u16 max_size = 4; + +public: + CIKLimbsController ( ); + void Create ( CGameObject *O ); + void Destroy ( CGameObject *O ); +public: + void PlayLegs ( CBlend *b ); + void Update ( ); +private: + void Calculate ( ); + void LimbCalculate ( SCalculateData &cd ); + void ShiftObject ( const SCalculateData cd[max_size] ); + float StaticObjectShift ( const SCalculateData cd[max_size] ); + float LegLengthShiftLimit ( float current_shift, const SCalculateData cd[max_size] ); + bool PredictObjectShift ( const SCalculateData cd[max_size] ); + void ObjectShift ( float static_shift, const SCalculateData cd[max_size] ); + void LimbUpdate ( CIKLimb &L ); + void LimbSetup ( ); + +private: + static void __stdcall IKVisualCallback ( IKinematics* K ); + +private: + CBlend *m_legs_blend; + CGameObject *m_object; + xr_vector _bone_chains; + object_shift _object_shift; + extrapolation::points _pose_extrapolation; + +#ifdef DEBUG + LPCSTR anim_name; + LPCSTR anim_set_name; +#endif + +}; diff --git a/src/xrGameLA/InfoDocument.cpp b/src/xrGameLA/InfoDocument.cpp new file mode 100644 index 000000000..61ee99591 --- /dev/null +++ b/src/xrGameLA/InfoDocument.cpp @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////// +// InfoDocument.cpp +// InfoDocument - документ, содержащий сюжетную информацию +/////////////////////////////////////////////////////////////// +#include "stdafx.h" +#include "InfoDocument.h" +#include "PhysicsShell.h" +#include "PDA.h" +#include "inventoryowner.h" +#include "xrserver_objects_alife_items.h" +#include "../../xrNetServer/net_utils.h" + +CInfoDocument::CInfoDocument(void) +{ + m_Info = NULL; +} + +CInfoDocument::~CInfoDocument(void) +{ +} + + +BOOL CInfoDocument::net_Spawn(CSE_Abstract* DC) +{ + BOOL res = inherited::net_Spawn(DC); + + CSE_Abstract *l_tpAbstract = static_cast(DC); + CSE_ALifeItemDocument *l_tpALifeItemDocument = smart_cast(l_tpAbstract); + R_ASSERT (l_tpALifeItemDocument); + + m_Info = l_tpALifeItemDocument->m_wDoc; + + return (res); +} + +void CInfoDocument::Load(LPCSTR section) +{ + inherited::Load(section); +} + +void CInfoDocument::net_Destroy() +{ + inherited::net_Destroy(); +} + +void CInfoDocument::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); +} + +void CInfoDocument::UpdateCL() +{ + inherited::UpdateCL(); +} + + +void CInfoDocument::OnH_A_Chield() +{ + inherited::OnH_A_Chield (); + + //передать информацию содержащуюся в документе + //объекту, который поднял документ + CInventoryOwner* pInvOwner = smart_cast(H_Parent()); + if(!pInvOwner) return; + + //создать и отправить пакет о получении новой информации + if(m_Info.size()) + { + NET_Packet P; + u_EventGen (P,GE_INFO_TRANSFER, H_Parent()->ID()); + P.w_u16 (ID()); //отправитель + P.w_stringZ (m_Info); //сообщение + P.w_u8 (1); //добавление сообщения + u_EventSend (P); + } +} + +void CInfoDocument::OnH_B_Independent(bool just_before_destroy) +{ + inherited::OnH_B_Independent(just_before_destroy); +} + +void CInfoDocument::renderable_Render() +{ + inherited::renderable_Render(); +} \ No newline at end of file diff --git a/src/xrGameLA/InfoDocument.h b/src/xrGameLA/InfoDocument.h new file mode 100644 index 000000000..05ac6d7e1 --- /dev/null +++ b/src/xrGameLA/InfoDocument.h @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////// +// InfoDocument.h +// InfoDocument - документ, содержащий сюжетную информацию +/////////////////////////////////////////////////////////////// + + +#pragma once + +#include "inventory_item_object.h" +#include "InfoPortionDefs.h" + +class CInfoDocument: public CInventoryItemObject { +private: + typedef CInventoryItemObject inherited; +public: + CInfoDocument(void); + virtual ~CInfoDocument(void); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void Load (LPCSTR section); + virtual void net_Destroy (); + virtual void shedule_Update (u32 dt); + virtual void UpdateCL (); + virtual void renderable_Render (); + + virtual void OnH_A_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + +protected: + //индекс информации, содержащейся в документе + shared_str m_Info; +}; diff --git a/src/xrGameLA/InfoPortion.cpp b/src/xrGameLA/InfoPortion.cpp new file mode 100644 index 000000000..111f5bde5 --- /dev/null +++ b/src/xrGameLA/InfoPortion.cpp @@ -0,0 +1,146 @@ +#include "pch_script.h" +#include "InfoPortion.h" +#include "gameobject.h" +#include "encyclopedia_article.h" +#include "gametask.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_story_registry.h" +#include "xrServer_Objects_ALife.h" +#include "script_engine.h" +#include "ui\uixmlinit.h" +#include "object_broker.h" + +void INFO_DATA::load (IReader& stream) +{ + load_data(info_id, stream); + load_data(receive_time, stream); +} + +void INFO_DATA::save (IWriter& stream) +{ + save_data(info_id, stream); + save_data(receive_time, stream); +} + + +SInfoPortionData::SInfoPortionData () +{ +} +SInfoPortionData::~SInfoPortionData () +{ +} + +CInfoPortion::CInfoPortion() +{ +} + +CInfoPortion::~CInfoPortion () +{ +} + +void CInfoPortion::Load (shared_str info_id) +{ + m_InfoId = info_id; + inherited_shared::load_shared(m_InfoId, NULL); +} + + +void CInfoPortion::load_shared (LPCSTR) +{ + const ITEM_DATA& item_data = *id_to_index::GetById(m_InfoId); + + CUIXml* pXML = item_data._xml; + pXML->SetLocalRoot (pXML->GetRoot()); + + //loading from XML + XML_NODE* pNode = pXML->NavigateToNode(id_to_index::tag_name, item_data.pos_in_file); + THROW3 (pNode, "info_portion id=", *item_data.id); + + //список названий диалогов + int dialogs_num = pXML->GetNodesNum(pNode, "dialog"); + info_data()->m_DialogNames.clear(); + for(int i=0; iRead(pNode, "dialog", i,""); + info_data()->m_DialogNames.push_back(dialog_name); + } + + + //список названий порций информации, которые деактивируются, + //после получения этой порции + int disable_num = pXML->GetNodesNum(pNode, "disable"); + info_data()->m_DisableInfo.clear(); + for(i=0; iRead(pNode, "disable", i,""); + info_data()->m_DisableInfo.push_back(info_id); + } + + //имена скриптовых функций + info_data()->m_PhraseScript.Load(pXML, pNode); + + + //индексы статей + info_data()->m_Articles.clear(); + int articles_num = pXML->GetNodesNum(pNode, "article"); + for(i=0; iRead(pNode, "article", i, NULL); + THROW(article_str_id); + info_data()->m_Articles.push_back(article_str_id); + } + + info_data()->m_ArticlesDisable.clear(); + articles_num = pXML->GetNodesNum(pNode, "article_disable"); + for(i=0; iRead(pNode, "article_disable", i, NULL); + THROW(article_str_id); + info_data()->m_ArticlesDisable.push_back(article_str_id); + } + + info_data()->m_GameTasks.clear(); + int task_num = pXML->GetNodesNum(pNode, "task"); + for(i=0; iRead(pNode, "task", i, NULL); + THROW(task_str_id); + info_data()->m_GameTasks.push_back(task_str_id); + } +} + +void CInfoPortion::InitXmlIdToIndex() +{ + if(!id_to_index::tag_name) + id_to_index::tag_name = "info_portion"; + if(!id_to_index::file_str) + id_to_index::file_str = pSettings->r_string("info_portions", "files"); +} + +void _destroy_item_data_vector_cont(T_VECTOR* vec) +{ + T_VECTOR::iterator it = vec->begin(); + T_VECTOR::iterator it_e = vec->end(); + + xr_vector _tmp; + for(;it!=it_e;++it) + { + xr_vector::iterator it_f = std::find(_tmp.begin(), _tmp.end(), (*it)._xml); + if(it_f==_tmp.end()) +//. { + _tmp.push_back ((*it)._xml); +//. Msg("%s is unique",(*it)._xml->m_xml_file_name); +//. }else +//. Msg("%s already in list",(*it)._xml->m_xml_file_name); + + } +//. Log("_tmp.size()",_tmp.size()); + delete_data (_tmp); +} + +bool CInfoPortion::ValidInfoPortion(LPCSTR info_id) +{ + return id_to_index::GetById(info_id, true) != NULL; +} + diff --git a/src/xrGameLA/InfoPortion.h b/src/xrGameLA/InfoPortion.h new file mode 100644 index 000000000..b10e86f7d --- /dev/null +++ b/src/xrGameLA/InfoPortion.h @@ -0,0 +1,85 @@ +#pragma once + +#include "shared_data.h" +#include "PhraseScript.h" + +#include "xml_str_id_loader.h" +#include "encyclopedia_article_defs.h" + +#include "GameTaskDefs.h" +#include "PhraseDialogDefs.h" + + +struct SInfoPortionData : CSharedResource +{ + SInfoPortionData (); + virtual ~SInfoPortionData (); + + //массив с именами диалогов, которые могут быть инициированы + //из этого InfoPortion + DIALOG_ID_VECTOR m_DialogNames; + + //список статей в энциклопедии, которые становятся известными + ARTICLE_ID_VECTOR m_Articles; + //список статей в энциклопедии, которые становятся неизвестными (на тот случай если + //нужно заменить одну статью другой) + ARTICLE_ID_VECTOR m_ArticlesDisable; + + //присоединенные задания + TASK_ID_VECTOR m_GameTasks; + + //скриптовые действия, которые активируется после того как + //информацию получает персонаж + CPhraseScript m_PhraseScript; + + //массив с индексами тех порций информации, которые + //исчезнут, после получения этой info_portion + DEFINE_VECTOR (shared_str, INFO_ID_VECTOR, INFO_ID_VECTOR_IT); + INFO_ID_VECTOR m_DisableInfo; +}; + + +class CInfoPortion; + +//квант - порция информации +class CInfoPortion : public CSharedClass, + public CXML_IdToIndex +{ +private: + typedef CSharedClass inherited_shared; + typedef CXML_IdToIndex id_to_index; + + friend id_to_index; +public: + CInfoPortion (void); + virtual ~CInfoPortion (void); + + //инициализация info данными + //если info с таким id раньше не использовался + //он будет загружен из файла + virtual void Load (shared_str info_str_id); +// virtual void Load (INFO_INDEX info_index); + +// const LOCATIONS_VECTOR& MapLocations() const {return info_data()->m_MapLocations;} + const ARTICLE_ID_VECTOR& Articles () const {return info_data()->m_Articles;} + const ARTICLE_ID_VECTOR& ArticlesDisable () const {return info_data()->m_ArticlesDisable;} + const TASK_ID_VECTOR& GameTasks () const {return info_data()->m_GameTasks;} + const DIALOG_ID_VECTOR& DialogNames () const {return info_data()->m_DialogNames;} + const SInfoPortionData::INFO_ID_VECTOR& DisableInfos() const {return info_data()->m_DisableInfo;} + + void RunScriptActions (const CGameObject* pOwner) {info_data()->m_PhraseScript.Action(pOwner, NULL, NULL);} + + //текстовое представление информации + shared_str GetText () const ; + +protected: + shared_str m_InfoId; + + void load_shared (LPCSTR); + SInfoPortionData* info_data () { VERIFY(inherited_shared::get_sd()); return inherited_shared::get_sd();} + const SInfoPortionData* info_data () const { VERIFY(inherited_shared::get_sd()); return inherited_shared::get_sd();} + + static void InitXmlIdToIndex(); +public: + static bool ValidInfoPortion (LPCSTR info_id); +}; \ No newline at end of file diff --git a/src/xrGameLA/InfoPortionDefs.h b/src/xrGameLA/InfoPortionDefs.h new file mode 100644 index 000000000..f7f90ed33 --- /dev/null +++ b/src/xrGameLA/InfoPortionDefs.h @@ -0,0 +1,19 @@ +#pragma once + +#include "alife_space.h" +#include "object_interfaces.h" + +struct INFO_DATA : public IPureSerializeObject +{ + INFO_DATA ():info_id(NULL),receive_time(0) {}; + INFO_DATA (shared_str id, ALife::_TIME_ID time):info_id(id),receive_time(time){}; + + virtual void load (IReader& stream); + virtual void save (IWriter&); + + shared_str info_id; + //время получения нужно порции информации + ALife::_TIME_ID receive_time; +}; + +DEFINE_VECTOR (INFO_DATA, KNOWN_INFO_VECTOR, KNOWN_INFO_VECTOR_IT); \ No newline at end of file diff --git a/src/xrGameLA/Inventory.cpp b/src/xrGameLA/Inventory.cpp new file mode 100644 index 000000000..46d4f5bf9 --- /dev/null +++ b/src/xrGameLA/Inventory.cpp @@ -0,0 +1,1674 @@ +#include "pch_script.h" +#include "inventory.h" +#include "actor.h" +#include "trade.h" +#include "weapon.h" + +#include "ui/UIInventoryUtilities.h" + +#include "eatable_item.h" +#include "script_engine.h" +#include "xrmessages.h" +//#include "game_cl_base.h" +#include "xr_level_controller.h" +#include "level.h" +#include "ai_space.h" +#include "entitycondition.h" +#include "game_base_space.h" +#include "clsid_game.h" +#include "ai/stalker/ai_stalker.h" +#include "weaponmagazined.h" +#include "game_object_space.h" +#include "script_callback_ex.h" +#include "script_game_object.h" +#include "player_hud.h" +#include "CustomOutfit.h" + +using namespace InventoryUtilities; + +// what to block +u32 INV_STATE_BLOCK_ALL = 0xffffffff; +u32 INV_STATE_CAR = INV_STATE_BLOCK_ALL ^ (1 << PISTOL_SLOT); +u32 INV_STATE_LADDER = (1 << RIFLE_SLOT) | (1 << RIFLE_2_SLOT) | (1 << DETECTOR_SLOT); +u32 INV_STATE_INV_WND = INV_STATE_BLOCK_ALL; +u32 INV_STATE_BUY_MENU = INV_STATE_BLOCK_ALL; + +CInventorySlot::CInventorySlot() +{ + m_pIItem = NULL; + m_bVisible = true; + m_bPersistent = false; + m_blockCounter = 0; +} + +CInventorySlot::~CInventorySlot() +{ +} + +bool CInventorySlot::CanBeActivated() const +{ + return (m_bVisible && !IsBlocked()); +}; + +bool CInventorySlot::IsBlocked() const +{ + return (m_blockCounter>0); +} + + +CInventory::CInventory() +{ + + m_fTakeDist = pSettings->r_float ("inventory","take_dist"); + m_fMaxWeight = pSettings->r_float ("inventory","max_weight"); + m_iMaxBelt = pSettings->r_s32 ("inventory","max_belt"); + + m_slots.resize (LAST_SLOT + 1); // first is [1] + + SetCurrentDetector (NULL); + m_iActiveSlot = NO_ACTIVE_SLOT; + m_iNextActiveSlot = NO_ACTIVE_SLOT; + m_iPrevActiveSlot = NO_ACTIVE_SLOT; + m_iLoadActiveSlot = NO_ACTIVE_SLOT; + m_ActivationSlotReason = eGeneral; + m_pTarget = NULL; + m_bHandsOnly = false; + + string256 temp; + for (TSlotId i = FirstSlot(); i <= LastSlot(); ++i) + { + xr_sprintf(temp, "slot_persistent_%d", i); + if(pSettings->line_exist("inventory",temp)) + m_slots[i].m_bPersistent = !!pSettings->r_bool("inventory",temp); + } + + m_slots[PDA_SLOT].m_bVisible = false; + m_slots[OUTFIT_SLOT].m_bVisible = false; + m_slots[TORCH_SLOT].m_bVisible = false; + + m_bSlotsUseful = true; + m_bBeltUseful = false; + + m_fTotalWeight = -1.f; + m_dwModifyFrame = 0; + m_drop_last_frame = false; + m_iLoadActiveSlotFrame = u32(-1); +} + + +CInventory::~CInventory() +{ +} + +void CInventory::Clear() +{ + m_all.clear (); + m_ruck.clear (); + m_belt.clear (); + + for(TSlotId i=FirstSlot(); i<=LastSlot(); i++) + { + m_slots[i].m_pIItem = NULL; + } + + + m_pOwner = NULL; + + CalcTotalWeight (); + InvalidateState (); +} + +/* +void CInventory::repackAmmo(PIItem pIItem) +{ + CWeaponAmmo* ammo = smart_cast(pIItem); + + if (!ammo) { + Msg("!! Can't convert to weapon ammo class obj with section '%s'!!!",pIItem->object().cNameSect_str()); + return; + } + + + if (!m_ruck.size()) return; + TIItemContainer::const_iterator it_b = m_ruck.begin(); + TIItemContainer::const_iterator it = m_ruck.end(); + + for(--it; it>=it_b; --it) { + CInventoryItem* invAmmoObj = (*it); + if (invAmmoObj->m_eItemPlace!=eItemPlaceBelt && invAmmoObj->object().cNameSect()==pIItem->object().cNameSect()) { + CWeaponAmmo* invAmmo = smart_cast(invAmmoObj); + R_ASSERT(invAmmo); + + u16 freeSpace = invAmmo->m_boxSize - invAmmo->m_boxCurr; + if (!freeSpace) break; //Shouldnt this be a continue? + + if (freeSpace>=ammo->m_boxCurr) { + invAmmo->m_boxCurr+=ammo->m_boxCurr; + + pIItem->SetDeleteManual(TRUE); + } else { + invAmmo->m_boxCurr=invAmmo->m_boxSize; + ammo->m_boxCurr-=freeSpace; + } + break; + } + } + return; +} +*/ + +void CInventory::Take(CGameObject *pObj, bool bNotActivate, bool strict_placement, bool duringSpawn) +{ + CInventoryItem *pIItem = smart_cast(pObj); + VERIFY (pIItem); + + if(pIItem->m_pCurrentInventory) + { + Msg("! ERROR CInventory::Take but object has m_pCurrentInventory"); + Msg("! Inventory Owner is [%d]", GetOwner()->object_id()); + Msg("! Object Inventory Owner is [%d]", pIItem->m_pCurrentInventory->GetOwner()->object_id()); + + CObject* p = pObj->H_Parent(); + if(p) + Msg("! object parent is [%s] [%d]", p->cName().c_str(), p->ID()); + } + + + R_ASSERT (CanTakeItem(pIItem)); + + pIItem->m_pCurrentInventory = this; + pIItem->SetDropManual (FALSE); + + if (Level().CurrentEntity()) + { + u16 actor_id = Level().CurrentEntity()->ID(); + + if (GetOwner()->object_id()==actor_id && this->m_pOwner->object_id()==actor_id) //actors inventory + { + + CWeaponMagazined* pWeapon = smart_cast(pIItem); + if (pWeapon && pWeapon->strapped_mode()) + { + pWeapon->strapped_mode(false); + Ruck(pWeapon); + } + + } + } + + m_all.push_back (pIItem); + + if(!strict_placement) + pIItem->SetCurrPlace (eItemPlaceUndefined); + + bool result = false; + switch(pIItem->CurrPlace()) + { + case eItemPlaceBelt: + result = Belt(pIItem); +#ifdef DEBUG + if(!result) + Msg("cant put in belt item %s", *pIItem->object().cName()); +#endif + + break; + case eItemPlaceRuck: + result = Ruck(pIItem); +#ifdef DEBUG + if(!result) + Msg("cant put in ruck item %s", *pIItem->object().cName()); +#endif + + break; + case eItemPlaceSlot: + result = Slot(pIItem->CurrSlot(), pIItem, bNotActivate); +#ifdef DEBUG + if(!result) + Msg("cant slot in ruck item %s", *pIItem->object().cName()); +#endif + + break; + default: + + if( !pIItem->RuckDefault() ) + { + if (CanPutInSlot(pIItem, pIItem->BaseSlot())) + { + result = Slot(pIItem->BaseSlot(), pIItem, bNotActivate); VERIFY(result); + } + else if (pIItem->BaseSlot() == RIFLE_SLOT && CanPutInSlot(pIItem, RIFLE_2_SLOT)) + { + result = Slot(RIFLE_2_SLOT, pIItem, bNotActivate); VERIFY(result); + } + else if (CanPutInBelt(pIItem)) + { + result = Belt(pIItem); VERIFY(result); + } + else + { + result = Ruck(pIItem); VERIFY(result); + } + }else + { + result = Ruck(pIItem); VERIFY(result); + } + } + + m_pOwner->OnItemTake (pIItem, duringSpawn); + + CWeaponMagazined* pWeapon = smart_cast(pIItem); + if (pWeapon) + pWeapon->InitAddons(); //skyloader: need to do it as in CoP when UI will be ported | надо взять реализацию зум текстур из чн\зп, когда будет пересен уи из зп + + + CalcTotalWeight (); + InvalidateState (); + + pIItem->object().processing_deactivate(); + VERIFY (pIItem->CurrPlace() != eItemPlaceUndefined); +} + +bool CInventory::DropItem(CGameObject *pObj) +{ + CInventoryItem *pIItem = smart_cast(pObj); + VERIFY (pIItem); + if( !pIItem ) return false; + + if(pIItem->m_pCurrentInventory!=this) + { + Msg("ahtung !!! [%d]", Device.dwFrame); + Msg("CInventory::DropItem pIItem->m_pCurrentInventory!=this"); + Msg("this = [%d]", GetOwner()->object_id()); + Msg("pIItem->m_pCurrentInventory = [%d]", pIItem->m_pCurrentInventory->GetOwner()->object_id()); + } + + R_ASSERT (pIItem->m_pCurrentInventory); + R_ASSERT (pIItem->m_pCurrentInventory==this); + VERIFY (pIItem->CurrPlace() != eItemPlaceUndefined); + + pIItem->object().processing_activate(); + + switch(pIItem->CurrPlace()) + { + case eItemPlaceBelt:{ + R_ASSERT(InBelt(pIItem)); + m_belt.erase(std::find(m_belt.begin(), m_belt.end(), pIItem)); + pIItem->object().processing_deactivate(); + }break; + case eItemPlaceRuck:{ + R_ASSERT(InRuck(pIItem)); + m_ruck.erase(std::find(m_ruck.begin(), m_ruck.end(), pIItem)); + }break; + case eItemPlaceSlot:{ + VERIFY (InSlot(pIItem)); + u32 currSlot = pIItem->CurrSlot(); + if (currSlot == NO_ACTIVE_SLOT) + return false; + if (m_iActiveSlot == currSlot) + Activate (NO_ACTIVE_SLOT); + + m_slots[currSlot].m_pIItem = NULL; + pIItem->object().processing_deactivate(); + }break; + default: + NODEFAULT; + }; + + TIItemContainer::iterator it = std::find(m_all.begin(), m_all.end(), pIItem); + if ( it != m_all.end()) + m_all.erase (it); + else + Msg ("! CInventory::Drop item not found in inventory!!!"); + + pIItem->m_pCurrentInventory = NULL; + + m_pOwner->OnItemDrop (smart_cast(pObj)); + + pIItem->SetCurrPlace(eItemPlaceUndefined); + + CalcTotalWeight (); + InvalidateState (); + m_drop_last_frame = true; + return true; +} + +//положить вещь в слот +bool CInventory::Slot(TSlotId slot_id, PIItem pIItem, bool bNotActivate) +{ + VERIFY(pIItem); + //Msg("%s Inventory::Slot id: %d, %s[%d], notActivate: %d", m_pOwner->Name(), slot_id, *pIItem->object().cName(), pIItem->object().ID(), bNotActivate); + if(ItemFromSlot(slot_id) == pIItem) + return false; + + //tatarinrafa: block putting pnv or helmet in slot if outfit does not allow that Здесь это нужно чтобы пнв и шлем не воложились автоматически в слот при их получении + if (pIItem->BaseSlot() == HELMET_SLOT || pIItem->BaseSlot() == PNV_SLOT){ + PIItem itemformoutfitslot = ItemFromSlot(OUTFIT_SLOT); + if (itemformoutfitslot) + { + CCustomOutfit* outfit = smart_cast(itemformoutfitslot);//на всякий случай проверим если это броня в слоте брони, а то.. + if (outfit) + { + if (pIItem->BaseSlot() == HELMET_SLOT && outfit->block_helmet_slot == 1){ + Ruck(pIItem); + return false; + } + + if (pIItem->BaseSlot() == PNV_SLOT && outfit->block_pnv_slot == 1){ + Ruck(pIItem); + return false; + } + } + } + } + + if (!CanPutInSlot(pIItem, slot_id)) + { +#if 0//def _DEBUG + Msg("there is item %s[%d,%x] in slot %d[%d,%x]", + *m_slots[pIItem->BaseSlot()].m_pIItem->object().cName(), + m_slots[pIItem->BaseSlot()].m_pIItem->object().ID(), + m_slots[pIItem->BaseSlot()].m_pIItem, + pIItem->BaseSlot(), + pIItem->object().ID(), + pIItem); +#endif + if (m_slots[slot_id].m_pIItem == pIItem && !bNotActivate){ + Activate(slot_id); + } + + return false; + } + + // If item was in another slot already + auto oldSlot = pIItem->CurrSlot(); + if (oldSlot != NO_ACTIVE_SLOT && oldSlot != slot_id) + { + if(GetActiveSlot() == oldSlot) + Activate(NO_ACTIVE_SLOT); + + m_slots[oldSlot].m_pIItem = NULL; + } + + m_slots[slot_id].m_pIItem = pIItem; + + //удалить из рюкзака или пояса + TIItemContainer::iterator it = std::find(m_ruck.begin(), m_ruck.end(), pIItem); + if(m_ruck.end() != it) m_ruck.erase(it); + it = std::find(m_belt.begin(), m_belt.end(), pIItem); + if(m_belt.end() != it) m_belt.erase(it); + + + if (((m_iActiveSlot == slot_id) || (m_iActiveSlot == NO_ACTIVE_SLOT) && m_iNextActiveSlot == NO_ACTIVE_SLOT) && (!bNotActivate)) + Activate (slot_id); + + m_pOwner->OnItemSlot(pIItem, pIItem->CurrPlace()); + + pIItem->SetCurrPlace(eItemPlaceSlot); + pIItem->SetCurrSlot(slot_id); + pIItem->OnMoveToSlot(); + +#pragma todo("Change to CoP slot ids for scripts (0 no item, 1 knife, etc.).") + if(IsGameTypeSingle() && Actor()->m_inventory == this) + Actor()->callback(GameObject::eOnMoveToSlot)((smart_cast(pIItem))->lua_game_object(), slot_id - 1); + + pIItem->object().processing_activate(); + + +//tatarinrafa: Ruck pnv and helmet if outfit is not compatable with them + CCustomOutfit* outfit = smart_cast(pIItem); + if (outfit){ + if (outfit->block_pnv_slot == 1){ + CInventoryItem* pnv = ItemFromSlot(PNV_SLOT); + if (pnv){ + Ruck(pnv); + } + } + + if (outfit->block_helmet_slot == 1){ + CInventoryItem* helmet = ItemFromSlot(HELMET_SLOT); + if (helmet){ + Ruck(helmet); + } + } + } + + + return true; +} + +void CInventory::RepackBelt( PIItem pIItem ) +{ + + CWeaponAmmo* ammo = smart_cast(pIItem); + R_ASSERT(ammo); + + if ( !m_belt.size() || pIItem->CurrPlace() == eItemPlaceBelt ) return; //Belt is empty, nothing to repack. + TIItemContainer::const_iterator it = m_belt.begin(); + TIItemContainer::const_iterator it_end = m_belt.end(); + + for(it; it != it_end; it++) + { + CInventoryItem* invAmmoObj = (*it); + if( invAmmoObj->CurrPlace() == eItemPlaceBelt && invAmmoObj->object().cNameSect()== pIItem->object().cNameSect() ) //Is on belt, is the same type; + { + CWeaponAmmo* beltAmmo = smart_cast(invAmmoObj); //Cast to ammo obj + R_ASSERT(beltAmmo);//Check the cast + + if( ammo == beltAmmo || beltAmmo->m_boxCurr == beltAmmo->m_boxSize ) + continue; //Just skip it. + + u16 empty_space = beltAmmo->m_boxSize - beltAmmo->m_boxCurr; + if( empty_space > 0 ) //Is this box not full? + { + if( empty_space > ammo->m_boxCurr ) + { + beltAmmo->m_boxCurr += ammo->m_boxCurr; + ammo->m_boxCurr = 0; + } + else + { + beltAmmo->m_boxCurr += empty_space; + ammo->m_boxCurr -= empty_space; + } + } + if( ammo->m_boxCurr == 0 ) //Its and empty Box, Discard it. + { + pIItem->SetDropManual( true ); + return; + } + } + } +} + +bool CInventory::Belt(PIItem pIItem) +{ + if(!CanPutInBelt(pIItem, false)) return false; + + //Nova: Here is my belt repacking code. + auto ammo = smart_cast(pIItem); + if (ammo) + { + int boxCurrBefore = ammo->m_boxCurr; + RepackBelt(pIItem); + if (!CanPutInBelt(pIItem, true) || ammo->m_boxCurr == 0) + { + // if we can't actually move the whole ammo pack to slot, then return + // true if some ammo was actually moved (even if ammo object didn't), false if nothing changed + return boxCurrBefore != ammo->m_boxCurr; + } + } + + //вещь была в слоте + auto currSlot = pIItem->CurrSlot(); + if (currSlot != NO_ACTIVE_SLOT) + { + if (m_iActiveSlot == currSlot) Activate(NO_ACTIVE_SLOT); + m_slots[currSlot].m_pIItem = NULL; + } + + m_belt.insert(m_belt.end(), pIItem); + // phobos2077: sort items in belt after adding new item + std::sort(m_belt.begin(), m_belt.end(), GreaterRoomInRuck); + + if (currSlot == NO_ACTIVE_SLOT) + { + TIItemContainer::iterator it = std::find(m_ruck.begin(), m_ruck.end(), pIItem); + if(m_ruck.end() != it) m_ruck.erase(it); + } + + CalcTotalWeight(); + InvalidateState (); + + EItemPlace p = pIItem->CurrPlace(); + pIItem->SetCurrPlace(eItemPlaceBelt); + m_pOwner->OnItemBelt(pIItem, p); + pIItem->OnMoveToBelt(); + + + if(IsGameTypeSingle() && Actor()->m_inventory == this) + Actor()->callback(GameObject::eOnMoveToBelt)((smart_cast(pIItem))->lua_game_object()); + + if (currSlot != NO_ACTIVE_SLOT) + pIItem->object().processing_deactivate(); + + pIItem->object().processing_activate(); + + return true; +} + +void CInventory::RepackRuck( PIItem pIItem ) +{ + + CWeaponAmmo* ammo = smart_cast(pIItem); + R_ASSERT(ammo); + + if ( !m_ruck.size() || pIItem->CurrPlace() == eItemPlaceRuck ) return; + TIItemContainer::const_iterator it = m_ruck.begin(); + TIItemContainer::const_iterator it_end = m_ruck.end(); + + for(it; it != it_end; it++) + { + CInventoryItem* invAmmoObj = (*it); + if( invAmmoObj->CurrPlace() == eItemPlaceRuck && invAmmoObj->object().cNameSect()== pIItem->object().cNameSect() ) //Is in ruck, is the same type; + { + CWeaponAmmo* ruckAmmo = smart_cast(invAmmoObj); //Cast to ammo obj + R_ASSERT(ruckAmmo);//Check the cast + + if( ammo == ruckAmmo || ruckAmmo->m_boxCurr == ruckAmmo->m_boxSize ) + continue; //Just skip it. + + u16 empty_space = ruckAmmo->m_boxSize - ruckAmmo->m_boxCurr; + if( empty_space > ammo->m_boxCurr ) + { + ruckAmmo->m_boxCurr += ammo->m_boxCurr; + ammo->m_boxCurr = 0; + } + else + { + ruckAmmo->m_boxCurr += empty_space; + ammo->m_boxCurr -= empty_space; + } + + if( ammo->m_boxCurr == 0 ) //Its and empty Box, Discard it. + { + pIItem->SetDropManual( true ); + return; + } + } + } +} + +bool CInventory::Ruck(PIItem pIItem) +{ + if(!CanPutInRuck(pIItem)) return true; + + //Nova: Here is my belt repacking code. + if ( pIItem->object().CLS_ID==CLSID_OBJECT_AMMO ) + { + RepackRuck(pIItem); + } + + auto currSlot = pIItem->CurrSlot(); + // item was in the slot + if (currSlot != NO_ACTIVE_SLOT) + { + if (m_iActiveSlot == currSlot) + { + Activate(NO_ACTIVE_SLOT); + } + else if (m_currentDetectorInHand == pIItem) + { + m_currentDetectorInHand->HideDetector(false); + } + m_slots[currSlot].m_pIItem = NULL; + } + else + { + //вещь была на поясе или вообще только поднята с земли + TIItemContainer::iterator it = std::find(m_belt.begin(), m_belt.end(), pIItem); + if(m_belt.end() != it) m_belt.erase(it); + } + //Msg("%s Inventory::Ruck() %s from slot: %d", m_pOwner->Name(), pIItem->object().cNameSect_str(), currSlot); + + m_ruck.insert (m_ruck.end(), pIItem); + + CalcTotalWeight (); + InvalidateState (); + + m_pOwner->OnItemRuck (pIItem, pIItem->CurrPlace()); + pIItem->SetCurrPlace (eItemPlaceRuck); + pIItem->OnMoveToRuck (); + + if(IsGameTypeSingle() && Actor()->m_inventory == this) + Actor()->callback(GameObject::eOnMoveToRuck)((smart_cast(pIItem))->lua_game_object()); + + if (currSlot != NO_ACTIVE_SLOT) + pIItem->object().processing_deactivate(); + + return true; +} + +void CInventory::Activate_deffered (TSlotId slot, u32 _frame) +{ + m_iLoadActiveSlot = slot; + m_iLoadActiveSlotFrame = _frame; +} + +void CInventory::ActivateNextItemInActiveSlot() +{ + if(m_iActiveSlot==NO_ACTIVE_SLOT) return; + + PIItem current_item = m_slots[m_iActiveSlot].m_pIItem; + PIItem new_item = NULL; + + bool b = (current_item==NULL); + + TIItemContainer::const_iterator it = m_all.begin(); + TIItemContainer::const_iterator it_e = m_all.end(); + + for(; it!=it_e; ++it) + { + PIItem _pIItem = *it; + if(_pIItem==current_item) + { + b = true; + continue; + } + if(_pIItem->BaseSlot()==m_iActiveSlot) + new_item = _pIItem; + + if(b && new_item) + break; + } + + if(new_item==NULL) + return; //only 1 item for this slot + + bool res = Ruck (current_item); + R_ASSERT (res); + NET_Packet P; + current_item->object().u_EventGen (P, GEG_PLAYER_ITEM2RUCK, current_item->object().H_Parent()->ID()); + P.w_u16 (current_item->object().ID()); + current_item->object().u_EventSend (P); + + res = Slot (m_iActiveSlot, new_item); + R_ASSERT (res); + new_item->object().u_EventGen (P, GEG_PLAYER_ITEM2SLOT, new_item->object().H_Parent()->ID()); + P.w_u16 (new_item->object().ID()); + P.w_u16 ((u16)m_iActiveSlot); + new_item->object().u_EventSend (P); + + //activate + new_item->object().u_EventGen (P, GEG_PLAYER_ACTIVATE_SLOT, new_item->object().H_Parent()->ID()); + P.w_u16 ((u16)m_iActiveSlot); + new_item->object().u_EventSend (P); +} + +#pragma todo("Cannot use this function for left hand slots (detector), need to properly implement 2 active slots") +bool CInventory::Activate(TSlotId slot, EActivationReason reason, bool bForce) +{ + //Msg("CInventory::Activate, slot = %d, force: %d, reason: %d, activeSlot: %d, nextSlot: %d", slot, bForce, reason, m_iActiveSlot, m_iNextActiveSlot); + if (slot == DETECTOR_SLOT || slot == ANOM_DET_SLOT || slot == HELMET_SLOT || slot == PNV_SLOT) + { + //Msg("Activate: Wrong slot"); + return false; + } + + bool res = TryActivate(slot, reason, bForce); + if (res) + m_ActivationSlotReason = reason; + + return res; +} + +bool CInventory::TryActivate(TSlotId slot, EActivationReason reason, bool bForce) +{ + R_ASSERT2(slot == NO_ACTIVE_SLOT || slot < m_slots.size(), "wrong slot number"); + + CInventoryItem* tmp_item = nullptr; + CHudItem* tmp_hud_item = nullptr; + if (slot != NO_ACTIVE_SLOT) + { + tmp_item = ItemFromSlot(slot); + if (tmp_item) + tmp_hud_item = tmp_item->cast_hud_item(); + } + + // Если в руке находится детектор и орцжие не совместимо с ним, то отложить доставание оружия, спрятать детектор, а потом достать оружие + if (slot != NO_ACTIVE_SLOT && slot <= LAST_SLOT) + { + if (m_currentDetectorInHand && tmp_item && !tmp_item->IsSingleHand()) + { + m_currentDetectorInHand->HideDetector(false); + } + } + + //Msg("TryActivate, slot: %d, force: %d, currentSlot: %d", slot, bForce, m_iActiveSlot); + + if (m_ActivationSlotReason == eKeyAction && reason == eImportUpdate) + { + //Msg("Activate: Wrong reason"); + return false; + } + + if(Device.dwFrame == m_iLoadActiveSlotFrame) + { + if ((m_iLoadActiveSlot == slot) && tmp_item) + { + m_iLoadActiveSlotFrame = u32(-1); + } + else + { + //Msg("Activate: loadACtiveslotframe, res = false"); + return false; + } + } + + if ((slot!=NO_ACTIVE_SLOT && m_slots[slot].IsBlocked()) && !bForce) + { + //Msg("Activate: Slot is blocked"); + return false; + } + + if (slot != NO_ACTIVE_SLOT && !m_slots[slot].m_bVisible) + { + //Msg("Activate: Slot not visible"); + return false; + } + + /* + if (GetActiveSlot() == slot || (GetNextActiveSlot()==slot && tmp_hud_item && tmp_hud_item->IsHiding() && !bForce)) + { + Msg("Activate: the same slot so set next."); + m_iNextActiveSlot = slot; + return true; + }*/ + + /* + if (m_iActiveSlot == slot || + (m_iNextActiveSlot == slot && + m_iActiveSlot != NO_ACTIVE_SLOT && + m_slots[m_iActiveSlot].m_pIItem && + (m_slots[m_iActiveSlot].m_pIItem->cast_hud_item() && m_slots[m_iActiveSlot].m_pIItem->cast_hud_item()->IsHiding()) + ) + ) + { + Msg("Activate: Item is hiding or same slot"); + return false; + } + */ + + //активный слот не выбран + if(m_iActiveSlot == NO_ACTIVE_SLOT) + { + if(tmp_item) + { + m_iNextActiveSlot = slot; + m_ActivationSlotReason = reason; + //Msg("Activate: No active slot but have item"); + return true; + } + else + { + PIItem active_item = ActiveItem(); + if (slot==GRENADE_SLOT)//fake for grenade + { + PIItem gr = SameSlot(GRENADE_SLOT, NULL, true); + if (gr) + { + Slot(slot, gr); + //Msg("Activate: Grenade to slot"); + } + } + //Msg("Activate: No item"); + } + } + //активный слот задействован + else if(slot == NO_ACTIVE_SLOT || tmp_item) + { + PIItem active_item = ActiveItem(); + + if (active_item && !bForce) + { + CHudItem* tempItem = active_item->cast_hud_item(); + R_ASSERT2(tempItem, active_item->object().cNameSect().c_str()); + + if (GetActiveSlot() == slot) + { + //Msg("Activate: Activate item in same slot"); + tempItem->Activate(); + } + else if (!tempItem->IsHiding()) + { + //Msg("Activate: SendDeactivateItem"); + tempItem->SendDeactivateItem(); + } + } else //in case where weapon is going to destroy + { + if (tmp_hud_item) + { + //Msg("call HudItem activate, slot: %d", slot); + tmp_hud_item->Activate(); + } + + m_iActiveSlot = slot; + } + + m_iNextActiveSlot = slot; + m_ActivationSlotReason = reason; + + //Msg("Activate: Deactivated slot"); + return true; + } + + return false; +} + +PIItem CInventory::ItemFromSlot(TSlotId slot) const +{ + VERIFY(NO_ACTIVE_SLOT != slot); + return m_slots[slot].m_pIItem; +} + +void CInventory::SendActionEvent(u16 cmd, u32 flags) +{ + CActor *pActor = smart_cast(m_pOwner); + if (!pActor) return; + + NET_Packet P; + pActor->u_EventGen (P,GE_INV_ACTION, pActor->ID()); + P.w_s32 (cmd); + P.w_u32 (flags); + P.w_s32 (pActor->GetZoomRndSeed()); + P.w_s32 (pActor->GetShotRndSeed()); + pActor->u_EventSend (P, net_flags(TRUE, TRUE, FALSE, TRUE)); +}; + +bool CInventory::Action(u16 cmd, u32 flags) +{ + CActor *pActor = smart_cast(m_pOwner); + + if (pActor) + { + switch(cmd) + { + case kWPN_FIRE: + { + pActor->SetShotRndSeed(); + }break; + case kWPN_ZOOM : + { + pActor->SetZoomRndSeed(); + }break; + }; + }; + + if (g_pGameLevel && OnClient() && pActor) { + switch(cmd) + { + case kUSE: + { + }break; + + case kDROP: + + { + SendActionEvent(cmd, flags); + return true; + }break; + + case kWPN_NEXT: + case kWPN_RELOAD: + case kWPN_FIRE: + case kWPN_FUNC: + case kWPN_FIREMODE_NEXT: + case kWPN_FIREMODE_PREV: + case kWPN_ZOOM : + case kTORCH: + case kNIGHT_VISION: + + { + SendActionEvent(cmd, flags); + }break; + } + } + + + if (m_iActiveSlot < m_slots.size() && + m_slots[m_iActiveSlot].m_pIItem && + m_slots[m_iActiveSlot].m_pIItem->Action(cmd, flags)) + return true; + bool b_send_event = false; + switch(cmd) + { + case kWPN_1: + case kWPN_2: + case kWPN_3: + case kWPN_3b: + case kWPN_4: + case kWPN_5: + case kWPN_6: + { + if (cmd == kWPN_6 && !IsGameTypeSingle()) return false; + if (flags&CMD_START && !m_bHandsOnly) + { + auto desiredSlot = GetSlotByKey(cmd); + if ((int)m_iActiveSlot == desiredSlot && m_slots[m_iActiveSlot].m_pIItem) + { + if (IsGameTypeSingle()){ + b_send_event = Activate(NO_ACTIVE_SLOT); + } + else + { + ActivateNextItemInActiveSlot(); + } + } else { + if ((int)m_iActiveSlot == desiredSlot && !IsGameTypeSingle()) + { + break; + } + else + { + b_send_event = Activate(desiredSlot, eKeyAction); + } + } + } + }break; + case kARTEFACT: + { + if(flags&CMD_START) + { + if((int)m_iActiveSlot == ARTEFACT_SLOT && + m_slots[m_iActiveSlot].m_pIItem && IsGameTypeSingle()) + { + b_send_event = Activate(NO_ACTIVE_SLOT); + + }else { + b_send_event = Activate(ARTEFACT_SLOT); + + } + } + }break; + case kDETECTOR: + if(flags&CMD_START) + { + auto& slot = m_slots[DETECTOR_SLOT]; + if (slot.m_pIItem) + { + CCustomDetectorR* det = smart_cast(slot.m_pIItem); + if (slot.CanBeActivated()) + { + det->ToggleDetector(false); + } + else + { + det->HideDetector(true); + } + } + } + + break; + } + + if(b_send_event && g_pGameLevel && OnClient() && pActor) + SendActionEvent(cmd, flags); + + return false; +} + +TSlotId CInventory::GetSlotByKey(u16 cmd) +{ + switch (cmd) + { + case kWPN_1: return KNIFE_SLOT; + case kWPN_2: return PISTOL_SLOT; + case kWPN_3: return RIFLE_SLOT; + case kWPN_3b:return RIFLE_2_SLOT; + case kWPN_4: return GRENADE_SLOT; + case kWPN_5: return APPARATUS_SLOT; + case kWPN_6: return BOLT_SLOT; + case kARTEFACT: return ARTEFACT_SLOT; + case kDETECTOR: return DETECTOR_SLOT; + default: return NO_ACTIVE_SLOT; + } +} + + +void CInventory::Update() +{ + if (Level().CurrentViewEntity()->CLS_ID == CLSID_OBJECT_ACTOR) + { +// bool bActiveSlotVisible; +// //tatarinrafa: Lox fix for potentialy null variable being checked for its properties. +// if (m_iActiveSlot == NO_ACTIVE_SLOT || // maybe no slot at all +// !m_slots[m_iActiveSlot].m_pIItem || // maybe no item in it +// (m_slots[m_iActiveSlot].m_pIItem && (!m_slots[m_iActiveSlot].m_pIItem->cast_hud_item()/* item is in slot, but maybe it has no hud-item */ || m_slots[m_iActiveSlot].m_pIItem->cast_hud_item() && (m_slots[m_iActiveSlot].m_pIItem->cast_hud_item()->IsHidden()/* item is in slot, it has hud item, may be it is hidden*/)))) +// { +// bActiveSlotVisible = false; +// } +// else +// { +// bActiveSlotVisible = true; +// } +// } + + // ^above is to much for being checked each frame, lets just find the only way bActiveSlotVisible becomes true + + bool bActiveSlotVisible = false; + + if (m_iActiveSlot != NO_ACTIVE_SLOT && m_slots[m_iActiveSlot].m_pIItem && m_slots[m_iActiveSlot].m_pIItem->cast_hud_item() && !m_slots[m_iActiveSlot].m_pIItem->cast_hud_item()->IsHidden()) + { + bActiveSlotVisible = true; + } + bool bDetectorHiding = m_currentDetectorInHand && m_currentDetectorInHand->IsHiding(); + if (m_iNextActiveSlot != m_iActiveSlot && !bActiveSlotVisible && !bDetectorHiding) + { + if (m_iNextActiveSlot != NO_ACTIVE_SLOT && + m_slots[m_iNextActiveSlot].m_pIItem && + m_slots[m_iNextActiveSlot].m_pIItem->cast_hud_item()){ + m_slots[m_iNextActiveSlot].m_pIItem->cast_hud_item()->Activate(); + } + m_iActiveSlot = m_iNextActiveSlot; + } + //if (m_iNextActiveSlot != NO_ACTIVE_SLOT) && ActiveItem() && ActiveItem()->cast_hud_item()->IsHidden()) + // ActiveItem()->cast_hud_item()->Activate(); + } + + UpdateDropTasks (); +} + +void CInventory::UpdateDropTasks() +{ + for(TSlotId i=FirstSlot(); i<=LastSlot(); ++i) + { + PIItem itm = ItemFromSlot(i); + if(itm) + UpdateDropItem (itm); + } + + for(i = 0; i < 2; ++i) + { + TIItemContainer &list = i?m_ruck:m_belt; + TIItemContainer::iterator it = list.begin(); + TIItemContainer::iterator it_e = list.end(); + + for( ;it!=it_e; ++it) + { + UpdateDropItem (*it); + } + } + + if (m_drop_last_frame) + { + m_drop_last_frame = false; + m_pOwner->OnItemDropUpdate (); + } +} + +void CInventory::UpdateDropItem(PIItem pIItem) +{ + if(pIItem && pIItem->GetDropManual() ) + { + pIItem->SetDropManual(FALSE); + if ( OnServer() ) + { + NET_Packet P; + pIItem->object().u_EventGen (P, GE_OWNERSHIP_REJECT, pIItem->object().H_Parent()->ID()); + P.w_u16 (u16(pIItem->object().ID())); + pIItem->object().u_EventSend(P); + } + }// dropManual + if ( pIItem->GetDeleteManual() ) { + pIItem->SetDeleteManual(FALSE); + if ( OnServer() ) + { + NET_Packet P; + pIItem->object().u_EventGen (P, GE_DESTROY, u16(pIItem->object().ID())); + pIItem->object().u_EventSend(P); + } + } +} + +//ищем на поясе гранату такоже типа +PIItem CInventory::Same(const PIItem pIItem, bool bSearchRuck) const +{ + const TIItemContainer &list = bSearchRuck ? m_ruck : m_belt; + + for(TIItemContainer::const_iterator it = list.begin(); list.end() != it; ++it) + { + const PIItem l_pIItem = *it; + + if((l_pIItem != pIItem) && + !xr_strcmp(l_pIItem->object().cNameSect(), + pIItem->object().cNameSect())) + return l_pIItem; + } + return NULL; +} + +//ищем на поясе вещь для слота + +PIItem CInventory::SameSlot(const TSlotId slot, PIItem pIItem, bool bSearchRuck) const +{ + if(slot == NO_ACTIVE_SLOT) return NULL; + + const TIItemContainer &list = bSearchRuck ? m_ruck : m_belt; + + for(TIItemContainer::const_iterator it = list.begin(); list.end() != it; ++it) + { + PIItem _pIItem = *it; + if(_pIItem != pIItem && _pIItem->BaseSlot() == slot) return _pIItem; + } + + return NULL; +} + +static bool GetItemPredicate(PIItem item, const char* name) +{ + return item != nullptr + && !xr_strcmp(item->object().cNameSect(), name) + && item->Useful(); +} + +//найти в инвенторе вещь с указанным именем +PIItem CInventory::Get(const char *name, bool bSearchRuck) const +{ + const TIItemContainer &list = bSearchRuck ? m_ruck : m_belt; + for (auto it = list.cbegin(); list.cend() != it; ++it) + { + if (GetItemPredicate(*it, name)) return *it; + } + return NULL; +} + +PIItem CInventory::Get(CLASS_ID cls_id, bool bSearchRuck) const +{ + const TIItemContainer &list = bSearchRuck ? m_ruck : m_belt; + + for(TIItemContainer::const_iterator it = list.begin(); list.end() != it; ++it) + { + PIItem pIItem = *it; + if(pIItem && pIItem->object().CLS_ID == cls_id && + pIItem->Useful()) + return pIItem; + } + return NULL; +} + +PIItem CInventory::Get(const u16 id, bool bSearchRuck) const +{ + const TIItemContainer &list = bSearchRuck ? m_ruck : m_belt; + + for(TIItemContainer::const_iterator it = list.begin(); list.end() != it; ++it) + { + PIItem pIItem = *it; + if(pIItem && pIItem->object().ID() == id) + return pIItem; + } + return NULL; +} + +PIItem CInventory::GetAmmo(const char *name, bool bSearchRuck) const +{ + const TIItemContainer &list = bSearchRuck ? m_ruck : m_belt; + + for (auto it = list.crbegin(); list.crend() != it; ++it) + { + PIItem pIItem = *it; + if (GetItemPredicate(pIItem, name)) + { + //auto ammo = smart_cast(pIItem); + // if (ammo && ammo->m_boxCurr < ammo->m_boxSize) + return pIItem; + } + } + return NULL; +} + +//search both (ruck and belt) +PIItem CInventory::GetAny(const char *name) const +{ + PIItem itm = GetAmmo(name, false); + //для ГГ ищем только на поясе + if (!itm && (this != &g_actor->inventory())) + { + itm = GetAmmo(name, true); + } + return itm; +} + +PIItem CInventory::item(CLASS_ID cls_id) const +{ + const TIItemContainer &list = m_all; + + for(TIItemContainer::const_iterator it = list.begin(); list.end() != it; ++it) + { + PIItem pIItem = *it; + if(pIItem && pIItem->object().CLS_ID == cls_id && + pIItem->Useful()) + return pIItem; + } + return NULL; +} + +float CInventory::TotalWeight() const +{ + VERIFY(m_fTotalWeight>=0.f); + return m_fTotalWeight; +} + + +float CInventory::CalcTotalWeight() +{ + float weight = 0; + for(TIItemContainer::const_iterator it = m_all.begin(); m_all.end() != it; ++it) + weight += (*it)->Weight(); + + m_fTotalWeight = weight; + return m_fTotalWeight; +} + + +u32 CInventory::dwfGetSameItemCount(LPCSTR caSection, bool SearchAll) +{ + u32 l_dwCount = 0; + TIItemContainer &l_list = SearchAll ? m_all : m_ruck; + for(TIItemContainer::iterator l_it = l_list.begin(); l_list.end() != l_it; ++l_it) + { + PIItem l_pIItem = *l_it; + if (l_pIItem && !xr_strcmp(l_pIItem->object().cNameSect(), caSection)) + ++l_dwCount; + } + + return (l_dwCount); +} +u32 CInventory::dwfGetGrenadeCount(LPCSTR caSection, bool SearchAll) +{ + u32 l_dwCount = 0; + TIItemContainer &l_list = SearchAll ? m_all : m_ruck; + for(TIItemContainer::iterator l_it = l_list.begin(); l_list.end() != l_it; ++l_it) + { + PIItem l_pIItem = *l_it; + if (l_pIItem && l_pIItem->object().CLS_ID == CLSID_GRENADE_F1 || l_pIItem->object().CLS_ID == CLSID_GRENADE_RGD5) + ++l_dwCount; + } + + return (l_dwCount); +} + +bool CInventory::bfCheckForObject(ALife::_OBJECT_ID tObjectID) +{ + TIItemContainer &l_list = m_all; + for(TIItemContainer::iterator l_it = l_list.begin(); l_list.end() != l_it; ++l_it) + { + PIItem l_pIItem = *l_it; + if (l_pIItem && l_pIItem->object().ID() == tObjectID) + return(true); + } + return (false); +} + +CInventoryItem *CInventory::get_object_by_id(ALife::_OBJECT_ID tObjectID) +{ + TIItemContainer &l_list = m_all; + for(TIItemContainer::iterator l_it = l_list.begin(); l_list.end() != l_it; ++l_it) + { + PIItem l_pIItem = *l_it; + if (l_pIItem && l_pIItem->object().ID() == tObjectID) + return (l_pIItem); + } + return (0); +} + +//скушать предмет +bool CInventory::Eat(PIItem pIItem) +{ + R_ASSERT(pIItem->m_pCurrentInventory==this); + //устанаовить съедобна ли вещь + CEatableItem* pItemToEat = smart_cast(pIItem); + R_ASSERT (pItemToEat); + + CEntityAlive *entity_alive = smart_cast(m_pOwner); + R_ASSERT (entity_alive); + + bool used = pItemToEat->UseBy (entity_alive); + + if (!used) return false; + + if(IsGameTypeSingle() && Actor()->m_inventory == this) + Actor()->callback(GameObject::eUseObject)((smart_cast(pIItem))->lua_game_object()); + + if(pItemToEat->Empty() && entity_alive->Local()) + { + NET_Packet P; + CGameObject::u_EventGen (P,GE_OWNERSHIP_REJECT,entity_alive->ID()); + P.w_u16 (pIItem->object().ID()); + P.w_u8 (1); // send just_before_destroy flag, so physical shell does not activates and disrupts nearby objects + CGameObject::u_EventSend (P); + + CGameObject::u_EventGen (P,GE_DESTROY,pIItem->object().ID()); + CGameObject::u_EventSend (P); + } + return true; +} + +bool CInventory::InSlot(PIItem pIItem) const +{ + if(pIItem->CurrPlace() != eItemPlaceSlot) return false; + + VERIFY(m_slots[pIItem->CurrSlot()].m_pIItem == pIItem); + + return true; +} +bool CInventory::InBelt(PIItem pIItem) const +{ + if(Get(pIItem->object().ID(), false)) return true; + return false; +} +bool CInventory::InRuck(PIItem pIItem) const +{ + if( Get(pIItem->object().ID(), true ) ) return true; + return false; +} + +bool CInventory::CanPutInSlot(PIItem pIItem, TSlotId slot_id) const +{ + if(!m_bSlotsUseful) return false; + + if( !GetOwner()->CanPutInSlot(pIItem, slot_id) ) return false; + + if(slot_id!=NO_ACTIVE_SLOT && + NULL==ItemFromSlot(slot_id) ) + return true; + + return false; +} +//проверяет можем ли поместить вещь на пояс, +//при этом реально ничего не меняется +bool CInventory::CanPutInBelt(PIItem pIItem, bool forceRoomCheck) +{ + if(!pIItem) return false; + if(InBelt(pIItem)) return false; + if(!m_bBeltUseful) return false; + if(!pIItem->Belt()) return false; + bool needRoom = (forceRoomCheck || smart_cast(pIItem) == nullptr); + if(needRoom && m_belt.size() == BeltWidth()) return false; + + return !needRoom || FreeRoom_inBelt(m_belt, pIItem, BeltWidth(), 1); +} +//проверяет можем ли поместить вещь в рюкзак, +//при этом реально ничего не меняется +bool CInventory::CanPutInRuck(PIItem pIItem) const +{ + if(InRuck(pIItem)) return false; + return true; +} + +u32 CInventory::dwfGetObjectCount() +{ + return (m_all.size()); +} + +CInventoryItem *CInventory::tpfGetObjectByIndex(int iIndex) +{ + if ((iIndex >= 0) && (iIndex < (int)m_all.size())) { + TIItemContainer &l_list = m_all; + int i = 0; + for(TIItemContainer::iterator l_it = l_list.begin(); l_list.end() != l_it; ++l_it, ++i) + if (i == iIndex) + return (*l_it); + } + else { + ai().script_engine().script_log (ScriptStorage::eLuaMessageTypeError,"invalid inventory index!"); + return (0); + } + R_ASSERT (false); + return (0); +} + +CInventoryItem *CInventory::tpfGetBeltObjectById(int item_id) +{ + TIItemContainer &list = m_belt; + TIItemContainer::iterator it = std::find_if(list.begin(), list.end(), SBeltItemPred(item_id)); + return (it != list.end()) ? *it : NULL; +} + +CInventoryItem *CInventory::tpfGetBeltObjectByIndex(int iIndex) +{ + if ((iIndex >= 0) && (iIndex < (int)m_belt.size())) { + TIItemContainer &l_list = m_belt; + int i = 0; + for(TIItemContainer::iterator l_it = l_list.begin(); l_list.end() != l_it; ++l_it, ++i) + if (i == iIndex) + return (*l_it); + } + else { + ai().script_engine().script_log (ScriptStorage::eLuaMessageTypeError,"invalid belt index!"); + return (0); + } + R_ASSERT (false); + return (0); +} + +CInventoryItem *CInventory::GetItemFromInventory(LPCSTR caItemName) +{ + TIItemContainer &l_list = m_all; + + u32 crc = crc32(caItemName, xr_strlen(caItemName)); + + for(TIItemContainer::iterator l_it = l_list.begin(); l_list.end() != l_it; ++l_it) + if ((*l_it)->object().cNameSect()._get()->dwCRC == crc){ + VERIFY( 0 == xr_strcmp( (*l_it)->object().cNameSect().c_str(), caItemName) ); + return (*l_it); + } + return (0); +} + + +bool CInventory::CanTakeItem(CInventoryItem *inventory_item) const +{ + if (inventory_item->object().getDestroy()) return false; + + if(!inventory_item->CanTake()) return false; + + for(TIItemContainer::const_iterator it = m_all.begin(); it != m_all.end(); it++) + if((*it)->object().ID() == inventory_item->object().ID()) break; + VERIFY3(it == m_all.end(), "item already exists in inventory",*inventory_item->object().cName()); + + CActor* pActor = smart_cast(m_pOwner); + //актер всегда может взять вещь + if(!pActor && (TotalWeight() + inventory_item->Weight() > m_pOwner->MaxCarryWeight())) + return false; + + return true; +} + + +u32 CInventory::BeltWidth() const +{ + return m_iMaxBelt; +} + +void CInventory::AddAvailableItems(TIItemContainer& items_container, bool for_trade) const +{ + for(TIItemContainer::const_iterator it = m_ruck.begin(); m_ruck.end() != it; ++it) + { + PIItem pIItem = *it; + if(!for_trade || pIItem->CanTrade()) + items_container.push_back(pIItem); + } + + if(m_bBeltUseful) + { + for(TIItemContainer::const_iterator it = m_belt.begin(); m_belt.end() != it; ++it) + { + PIItem pIItem = *it; + if(!for_trade || pIItem->CanTrade()) + items_container.push_back(pIItem); + } + } + + CAI_Stalker* pOwner = smart_cast(m_pOwner); + if (pOwner && !pOwner->g_Alive()) + { + TISlotArr::const_iterator slot_it = m_slots.begin(); + TISlotArr::const_iterator slot_it_e = m_slots.end(); + for(;slot_it!=slot_it_e;++slot_it) + { + const CInventorySlot& S = *slot_it; + if(S.m_pIItem && S.m_pIItem->BaseSlot()!=BOLT_SLOT) + items_container.push_back(S.m_pIItem); + } + + } else if (m_bSlotsUseful) { + TISlotArr::const_iterator slot_it = m_slots.begin(); + TISlotArr::const_iterator slot_it_e = m_slots.end(); + for(;slot_it!=slot_it_e;++slot_it) + { + const CInventorySlot& S = *slot_it; + if (S.m_pIItem && (!for_trade || S.m_pIItem->CanTrade())) + { + if(!S.m_bPersistent || S.m_pIItem->BaseSlot()==GRENADE_SLOT ) + { + if (pOwner) { + u32 slot = S.m_pIItem->BaseSlot(); + if (slot != PISTOL_SLOT && slot != RIFLE_SLOT && slot != RIFLE_2_SLOT) + items_container.push_back(S.m_pIItem); + } else { + items_container.push_back(S.m_pIItem); + } + } + } + } + } +} + + +void CInventory::AddAvailableItems(TIItemContainer& items_container, SInventorySelectorPredicate& pred) const +{ + for (TIItemContainer::const_iterator it = m_ruck.begin(); m_ruck.end() != it; ++it) + { + PIItem pIItem = *it; + if (pred(pIItem)) + items_container.push_back(pIItem); + } + + if (m_bBeltUseful) + { + for (TIItemContainer::const_iterator it = m_belt.begin(); m_belt.end() != it; ++it) + { + PIItem pIItem = *it; + if (pred(pIItem)) + items_container.push_back(pIItem); + } + } + + CAI_Stalker* pOwner = smart_cast(m_pOwner); + if (pOwner && !pOwner->g_Alive()) + { + TISlotArr::const_iterator slot_it = m_slots.begin(); + TISlotArr::const_iterator slot_it_e = m_slots.end(); + for (; slot_it != slot_it_e; ++slot_it) + { + const CInventorySlot& S = *slot_it; + if (S.m_pIItem && S.m_pIItem->BaseSlot() != BOLT_SLOT) + items_container.push_back(S.m_pIItem); + } + + } + else if (m_bSlotsUseful) { + TISlotArr::const_iterator slot_it = m_slots.begin(); + TISlotArr::const_iterator slot_it_e = m_slots.end(); + for (; slot_it != slot_it_e; ++slot_it) + { + const CInventorySlot& S = *slot_it; + if (S.m_pIItem && pred(S.m_pIItem)) + { + if (!S.m_bPersistent || S.m_pIItem->BaseSlot() == GRENADE_SLOT) + { + if (pOwner) { + u32 slot = S.m_pIItem->BaseSlot(); + if (slot != PISTOL_SLOT && slot != RIFLE_SLOT && slot != RIFLE_2_SLOT) + items_container.push_back(S.m_pIItem); + } + else { + items_container.push_back(S.m_pIItem); + } + } + } + } + } +} + +bool CInventory::isBeautifulForActiveSlot (CInventoryItem *pIItem) +{ + if (!IsGameTypeSingle()) return (true); + TISlotArr::iterator it = m_slots.begin(); + for( ; it!=m_slots.end(); ++it) { + if ((*it).m_pIItem && (*it).m_pIItem->IsNecessaryItem(pIItem)) + return (true); + } + return (false); +} + +void CInventory::Items_SetCurrentEntityHud(bool current_entity) +{ + TIItemContainer::iterator it; + for(it = m_all.begin(); m_all.end() != it; ++it) + { + CWeapon* pWeapon = smart_cast(*it); + if (pWeapon) + { + pWeapon->InitAddons(); + pWeapon->UpdateAddonsVisibility(); + } + } +}; +//call this only via Actor()->SetWeaponHideState() +void CInventory::SetSlotsBlocked(u16 mask, bool bBlock) +{ + bool bChanged = false; + for(int i = FirstSlot(); i <= LastSlot(); ++i) + { + if(mask & (1< 5) m_slots[i].m_blockCounter = 1; + VERIFY2(m_slots[i].m_blockCounter< 5,"block slots overflow"); + }else{ + --m_slots[i].m_blockCounter; + if (m_slots[i].m_blockCounter < -5) m_slots[i].m_blockCounter = -1; + VERIFY2(m_slots[i].m_blockCounter>-5,"block slots underflow"); + } + if(bCanBeActivated != m_slots[i].CanBeActivated()) + bChanged = true; + } + } + if (bChanged) + { + /*Msg("Slots blocked changed. Knife: %d, Pistol: %d, Rifle: %d, Grenade: %d, Detector: %d", + m_slots[KNIFE_SLOT].m_blockCounter, + m_slots[PISTOL_SLOT].m_blockCounter, + m_slots[RIFLE_SLOT].m_blockCounter, + m_slots[GRENADE_SLOT].m_blockCounter, + m_slots[DETECTOR_SLOT].m_blockCounter);*/ + + auto ActiveSlot = GetActiveSlot(); + auto PrevActiveSlot = GetPrevActiveSlot(); + + if(ActiveSlot==NO_ACTIVE_SLOT) + {//try to restore hidden weapon + if(PrevActiveSlot!=NO_ACTIVE_SLOT && m_slots[PrevActiveSlot].CanBeActivated()) + if(Activate(PrevActiveSlot)) + SetPrevActiveSlot(NO_ACTIVE_SLOT); + }else + {//try to hide active weapon + if(!m_slots[ActiveSlot].CanBeActivated()) + { + if(Activate(NO_ACTIVE_SLOT)) + SetPrevActiveSlot(ActiveSlot); + } + } + if (m_currentDetectorInHand != nullptr && !m_slots[DETECTOR_SLOT].CanBeActivated()) + { + m_currentDetectorInHand->HideDetector(false, true); + } + } +} + +bool CInventory::AreSlotsBlocked() +{ + for (int i = FirstSlot(); i <= LastSlot(); ++i) + { + if (!m_slots[i].IsBlocked()) + return false; + } + return true; +} + +bool CInventory::SBeltItemPred::operator ()(PIItem &item) +{ + return item->object().ID() == m_id; +} diff --git a/src/xrGameLA/Inventory.h b/src/xrGameLA/Inventory.h new file mode 100644 index 000000000..ebb073727 --- /dev/null +++ b/src/xrGameLA/Inventory.h @@ -0,0 +1,227 @@ +#pragma once +#include "inventory_item.h" +#include "customdetector2.h" + +class CInventory; +class CInventoryItem; +class CHudItem; +class CInventoryOwner; + +class CInventorySlot +{ +public: + CInventorySlot (); + virtual ~CInventorySlot (); + + bool CanBeActivated () const; + bool IsBlocked () const; + + PIItem m_pIItem; + bool m_bPersistent; + bool m_bVisible; + s8 m_blockCounter; +}; +enum EActivationReason{ + eGeneral, + eKeyAction, + eImportUpdate, +}; + +typedef xr_vector TISlotArr; + + + +class CInventory +{ +//gr1ph: +private: + struct SBeltItemPred + { + private: + ALife::_OBJECT_ID m_id; + public: + SBeltItemPred(ALife::_OBJECT_ID id) : m_id(id) { } + IC bool operator() (PIItem &item); + }; + +public: + CInventory (); + virtual ~CInventory (); + + float TotalWeight () const; + float CalcTotalWeight (); + + void Take (CGameObject *pObj, bool bNotActivate, bool strict_placement, bool duringSpawn); + //void repackAmmo (PIItem pObj); //Im refactoring this, perviously it was called by take. ive moved repack calls to belt and ruck + + bool DropItem (CGameObject *pObj); + void Clear (); + + IC TSlotId FirstSlot () const {return KNIFE_SLOT;} + IC TSlotId LastSlot () const {return LAST_SLOT;} // not "end" + IC bool SlotIsPersistent (TSlotId slot_id) const {return m_slots[slot_id].m_bPersistent;} + bool Slot (TSlotId slot, PIItem pIItem, bool bNotActivate = false); + + void RepackBelt (PIItem pIItem); + bool Belt (PIItem pIItem); + void RepackRuck (PIItem pIItem); + bool Ruck (PIItem pIItem); + + bool InSlot (PIItem pIItem) const; + bool InBelt (PIItem pIItem) const; + bool InRuck (PIItem pIItem) const; + + bool CanPutInSlot (PIItem pIItem, TSlotId slot) const; + bool CanPutInBelt (PIItem pIItem, bool forceRoomCheck = true); + bool CanPutInRuck (PIItem pIItem) const; + + bool CanTakeItem (CInventoryItem *inventory_item) const; + + + + bool Activate (TSlotId slot, EActivationReason reason = eGeneral, bool bForce = false); + bool TryActivate (TSlotId slot, EActivationReason reason, bool bForce); + void Activate_deffered (TSlotId slot, u32 _frame); + PIItem ActiveItem ()const {return m_iActiveSlot==NO_ACTIVE_SLOT ? NULL :m_slots[m_iActiveSlot].m_pIItem;} + PIItem ItemFromSlot (TSlotId slot) const; + void ActivateNextItemInActiveSlot(); + bool Action (u16 cmd, u32 flags); + void Update (); + // Ищет на поясе аналогичный IItem + PIItem Same (const PIItem pIItem, bool bSearchRuck) const; + // Ищет на поясе IItem для указанного слота + PIItem SameSlot (const TSlotId slot, PIItem pIItem, bool bSearchRuck) const; + // Ищет на поясе или в рюкзаке IItem с указанным именем (cName()) + PIItem Get (const char *name, bool bSearchRuck) const; + // Ищет на поясе или в рюкзаке IItem с указанным именем (id) + PIItem Get (const u16 id, bool bSearchRuck) const; + // Ищет на поясе или в рюкзаке IItem с указанным CLS_ID + PIItem Get (CLASS_ID cls_id, bool bSearchRuck) const; + PIItem GetAny (const char *name) const;//search both (ruck and belt) + // Ищет на поясе или в рюкзаке пачку патронов с указанным именем (cName()) + PIItem GetAmmo (const char *name, bool bSearchRuck) const; + PIItem item (CLASS_ID cls_id) const; + + // get all the items with the same section name + virtual u32 dwfGetSameItemCount (LPCSTR caSection, bool SearchAll = false); + virtual u32 dwfGetGrenadeCount (LPCSTR caSection, bool SearchAll); + // get all the items with the same object id + virtual bool bfCheckForObject (ALife::_OBJECT_ID tObjectID); + PIItem get_object_by_id (ALife::_OBJECT_ID tObjectID); + + u32 dwfGetObjectCount (); + PIItem tpfGetObjectByIndex (int iIndex); + + CInventoryItem * tpfGetBeltObjectByIndex(int iIndex); + CInventoryItem * tpfGetBeltObjectById(int item_id); + + PIItem GetItemFromInventory(LPCSTR caItemName); + + bool Eat (PIItem pIItem); + + TSlotId GetActiveSlot () const {return m_iActiveSlot;} + + void SetPrevActiveSlot (TSlotId ActiveSlot) {m_iPrevActiveSlot = ActiveSlot;} + TSlotId GetPrevActiveSlot () const {return m_iPrevActiveSlot;} + TSlotId GetNextActiveSlot () const {return m_iNextActiveSlot;} + + void SetActiveSlot (TSlotId ActiveSlot) {m_iActiveSlot = m_iNextActiveSlot = ActiveSlot; } + + bool IsSlotsUseful () const {return m_bSlotsUseful;} + void SetSlotsUseful (bool slots_useful) {m_bSlotsUseful = slots_useful;} + bool IsBeltUseful () const {return m_bBeltUseful;} + void SetBeltUseful (bool belt_useful) {m_bBeltUseful = belt_useful;} + bool IsHandsOnly () const {return m_bHandsOnly;} + void SetHandsOnly (bool hands_only) {m_bHandsOnly = hands_only;} + + void SetSlotsBlocked (u16 mask, bool bBlock); + bool AreSlotsBlocked (); + + void SetCurrentDetector (CCustomDetectorR * detector) { m_currentDetectorInHand = detector;}; // Для детекторов. Использовал для возвращения детектора в руки гг после смены камеры + CCustomDetectorR * CurrentDetector () const { return m_currentDetectorInHand; }; + // void SetNeedToActivateWeapon(u32 slot) { m_needToActivateWeapon = slot; } //для правильной смены оружия + // u32 NeedToActivateWeapon()const { return m_needToActivateWeapon; }; + + + TIItemContainer m_all; + TIItemContainer m_ruck, m_belt; + TISlotArr m_slots; + + //возвращает все кроме PDA в слоте и болта + void AddAvailableItems (TIItemContainer& items_container, bool for_trade) const; + + struct SInventorySelectorPredicate + { + virtual bool operator() (PIItem item) = 0; + }; + + void AddAvailableItems(TIItemContainer& items_container, SInventorySelectorPredicate& pred) const; + + float GetTakeDist () const {return m_fTakeDist;} + void SetTakeDist (float dist) {m_fTakeDist = dist;} + + float GetMaxWeight () const {return m_fMaxWeight;} + void SetMaxWeight (float weight) {m_fMaxWeight = weight;} + bool CanBeDragged () {return (m_fTotalWeight<30.f);} // skyloader: dont use < m_fMaxWeight because maximum value is 1000.f for stalkers + + u32 BeltWidth () const; + + inline CInventoryOwner*GetOwner () const { return m_pOwner; } + + + // Объект на который наведен прицел + PIItem m_pTarget; + + friend class CInventoryOwner; + + + u32 ModifyFrame () const { return m_dwModifyFrame; } + void InvalidateState () { m_dwModifyFrame = Device.dwFrame; } + void Items_SetCurrentEntityHud (bool current_entity); + bool isBeautifulForActiveSlot (CInventoryItem *pIItem); +protected: + void UpdateDropTasks (); + void UpdateDropItem (PIItem pIItem); + TSlotId GetSlotByKey(u16 cmd); + + // Активный слот и слот который станет активным после смены + //значения совпадают в обычном состоянии (нет смены слотов) + TSlotId m_iActiveSlot; + TSlotId m_iNextActiveSlot; + TSlotId m_iPrevActiveSlot; + TSlotId m_iLoadActiveSlot; + u32 m_iLoadActiveSlotFrame; + EActivationReason m_ActivationSlotReason; + + // Для детекторов. Использовал для возвращения детектора в руки гг после смены камеры + CCustomDetectorR * m_currentDetectorInHand; + + u32 m_needToActivateWeapon; //для правильной смены оружия + + CInventoryOwner* m_pOwner; + + //флаг, показывающий наличие пояса в инвенторе + bool m_bBeltUseful; + //флаг, допускающий использование слотов + bool m_bSlotsUseful; + //if need to block all slots and inventory + bool m_bHandsOnly; + + // максимальный вес инвентаря + float m_fMaxWeight; + // текущий вес в инвентаре + float m_fTotalWeight; + + // Максимальное кол-во объектов + //на поясе + u32 m_iMaxBelt; + // Максимальное расстояние на котором можно подобрать объект + float m_fTakeDist; + + //кадр на котором произошло последнее изменение в инвенторе + u32 m_dwModifyFrame; + + bool m_drop_last_frame; + + void SendActionEvent (u16 cmd, u32 flags); +}; \ No newline at end of file diff --git a/src/xrGameLA/InventoryBox.cpp b/src/xrGameLA/InventoryBox.cpp new file mode 100644 index 000000000..1b193b00d --- /dev/null +++ b/src/xrGameLA/InventoryBox.cpp @@ -0,0 +1,94 @@ +#include "pch_script.h" +#include "InventoryBox.h" +#include "level.h" +#include "actor.h" +#include "game_object_space.h" + +#include "script_callback_ex.h" +#include "script_game_object.h" +#include "inventory_item.h" +#include "xrServer_Objects_ALife.h" + +CInventoryBox::CInventoryBox() +{ + m_in_use = false; + IsSafe = false; +} + +CInventoryBox::~CInventoryBox() +{ +} + +void CInventoryBox::OnEvent(NET_Packet& P, u16 type) +{ + inherited::OnEvent (P, type); + + switch (type) + { + //case GE_TRADE_BUY: + case GE_OWNERSHIP_TAKE: + { + u16 id; + P.r_u16(id); + CObject* itm = Level().Objects.net_Find(id); VERIFY(itm); + m_items.push_back (id); + itm->H_SetParent (this); + itm->setVisible (FALSE); + itm->setEnabled (FALSE); + }break; + + //case GE_TRADE_SELL: + case GE_OWNERSHIP_REJECT: + { + u16 id; + P.r_u16(id); + CObject* itm = Level().Objects.net_Find(id); VERIFY(itm); + xr_vector::iterator it; + it = std::find(m_items.begin(),m_items.end(),id); VERIFY(it!=m_items.end()); + m_items.erase (it); + itm->H_SetParent (NULL,!P.r_eof() && P.r_u8()); + + if( m_in_use ) + { + CGameObject* GO = smart_cast(itm); + Actor()->callback(GameObject::eInvBoxItemTake)( this->lua_game_object(), GO->lua_game_object() ); + } + }break; + }; +} + +void CInventoryBox::UpdateCL() +{ + inherited::UpdateCL (); +} + +void CInventoryBox::net_Destroy() +{ + inherited::net_Destroy (); +} +BOOL CInventoryBox::net_Spawn(CSE_Abstract* DC) +{ + inherited::net_Spawn (DC); + setVisible (TRUE); + setEnabled (TRUE); + set_tip_text ("inventory_box_use"); + + return TRUE; +} + +void CInventoryBox::net_Relcase(CObject* O) +{ + inherited::net_Relcase(O); +} +#include "inventory_item.h" +void CInventoryBox::AddAvailableItems(TIItemContainer& items_container) const +{ + xr_vector::const_iterator it = m_items.begin(); + xr_vector::const_iterator it_e = m_items.end(); + + for(;it!=it_e;++it) + { + PIItem itm = smart_cast(Level().Objects.net_Find(*it));VERIFY(itm); + items_container.push_back (itm); + } +} diff --git a/src/xrGameLA/InventoryBox.h b/src/xrGameLA/InventoryBox.h new file mode 100644 index 000000000..a2762e246 --- /dev/null +++ b/src/xrGameLA/InventoryBox.h @@ -0,0 +1,24 @@ +#pragma once +#include "inventory_space.h" +#include "GameObject.h" + +class CInventoryBox :public CGameObject +{ + typedef CGameObject inherited; + xr_vector m_items; +public: + bool m_in_use; + CInventoryBox (); + virtual ~CInventoryBox (); + virtual void OnEvent (NET_Packet& P, u16 type); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Relcase (CObject* O); + void AddAvailableItems (TIItemContainer& items_container) const; + IC bool IsEmpty () const {return m_items.empty();} + virtual void UpdateCL (); + bool IsSafe; + LPCSTR SafeCode; + LPCSTR UnlockInfo; + +}; \ No newline at end of file diff --git a/src/xrGameLA/InventoryOwner.cpp b/src/xrGameLA/InventoryOwner.cpp new file mode 100644 index 000000000..7380e0b57 --- /dev/null +++ b/src/xrGameLA/InventoryOwner.cpp @@ -0,0 +1,634 @@ +#include "pch_script.h" +#include "InventoryOwner.h" +#include "entity_alive.h" +#include "pda.h" +#include "actor.h" +#include "actorcondition.h" +#include "trade.h" +#include "inventory.h" +#include "xrserver_objects_alife_items.h" +#include "character_info.h" +#include "script_game_object.h" +#include "script_engine.h" +#include "AI_PhraseDialogManager.h" +#include "level.h" +#include "game_base_space.h" +#include "PhraseDialog.h" +#include "xrserver.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "alife_registry_wrappers.h" +#include "relation_registry.h" +#include "ai_object_location.h" +#include "script_callback_ex.h" +#include "game_object_space.h" +#include "AI/Monsters/BaseMonster/base_monster.h" +#include "trade_parameters.h" +#include "purchase_list.h" +#include "clsid_game.h" +#include "artifact.h" + +#include "alife_object_registry.h" + +#include "CustomOutfit.h" + +CInventoryOwner::CInventoryOwner () +{ + m_pTrade = NULL; + m_trade_parameters = 0; + + m_inventory = new CInventory(); + m_pCharacterInfo = new CCharacterInfo(); + + EnableTalk(); + EnableTrade(); + + bDisableBreakDialog = false; + m_known_info_registry = new CInfoPortionWrapper(); + m_tmp_active_slot_num = NO_ACTIVE_SLOT; + m_need_osoznanie_mode = FALSE; + + special_map_spot = nullptr; +} + +DLL_Pure *CInventoryOwner::_construct () +{ + m_trade_parameters = 0; + m_purchase_list = 0; + + return (smart_cast(this)); +} + +CInventoryOwner::~CInventoryOwner () +{ + xr_delete (m_inventory); + xr_delete (m_pTrade); + xr_delete (m_pCharacterInfo); + xr_delete (m_known_info_registry); + xr_delete (m_trade_parameters); + xr_delete (m_purchase_list); +} + +void CInventoryOwner::Load (LPCSTR section) +{ + if(pSettings->line_exist(section, "inv_max_weight")) + m_inventory->SetMaxWeight( pSettings->r_float(section,"inv_max_weight") ); + + if(pSettings->line_exist(section, "need_osoznanie_mode")) + { + m_need_osoznanie_mode=pSettings->r_bool(section,"need_osoznanie_mode"); + } + else + { + m_need_osoznanie_mode=FALSE; + } + + m_bDrawSecWpn = true; + if (pSettings->line_exist(section,"draw_secondary_weapon")) + m_bDrawSecWpn = !!pSettings->r_bool(section,"draw_secondary_weapon"); +} + +void CInventoryOwner::reload (LPCSTR section) +{ + inventory().Clear (); + inventory().m_pOwner = this; + inventory().SetSlotsUseful (true); + + m_money = 0; + m_bTalking = false; + m_pTalkPartner = NULL; + + CAttachmentOwner::reload (section); +} + +void CInventoryOwner::reinit () +{ + CAttachmentOwner::reinit (); + m_item_to_spawn = shared_str(); + m_ammo_in_box_to_spawn = 0; +} + +#include "map_manager.h" + +//call this after CGameObject::net_Spawn +BOOL CInventoryOwner::net_Spawn (CSE_Abstract* DC) +{ + if (!m_pTrade) + m_pTrade = new CTrade(this); + + if (m_trade_parameters) + xr_delete (m_trade_parameters); + + m_trade_parameters = new CTradeParameters(trade_section()); + + //получить указатель на объект, InventoryOwner + //m_inventory->setSlotsBlocked(false); + CGameObject *pThis = smart_cast(this); + if(!pThis) return FALSE; + CSE_Abstract* E = (CSE_Abstract*)(DC); + + if ( IsGameTypeSingle() ) + { + CSE_ALifeTraderAbstract* pTrader = NULL; + if(E) pTrader = smart_cast(E); + if(!pTrader) return FALSE; + + R_ASSERT( pTrader->character_profile().size() ); + + //синхронизируем параметры персонажа с серверным объектом + CharacterInfo().InitCInfo(pTrader); + auto customMonster = smart_cast(this); + if (customMonster) + { + customMonster->invulnerable(SpecificCharacter().Invulnerable()); + } + + //------------------------------------- + m_known_info_registry->registry().init(E->ID); + //------------------------------------- + + + CAI_PhraseDialogManager* dialog_manager = smart_cast(this); + if( dialog_manager && !dialog_manager->GetStartDialog().size() ) + { + dialog_manager->SetStartDialog(CharacterInfo().StartDialog()); + dialog_manager->SetDefaultStartDialog(CharacterInfo().StartDialog()); + } + m_game_name = pTrader->m_character_name; + + //Special Map Spot assigned in character desc + LPCSTR str = SpecificCharacter().MapSpotString(); + if (xr_strcmp(str, "null")) + { + special_map_spot = Level().MapManager().AddMapLocation(str, E->ID); + } + } + else + { + CharacterInfo().m_SpecificCharacter.LoadSCharacter ("mp_actor"); + CharacterInfo().InitSpecificCharacter ("mp_actor"); + CharacterInfo().m_SpecificCharacter.data()->m_sGameName = (E->name_replace()[0]) ? E->name_replace() : *pThis->cName(); + m_game_name = (E->name_replace()[0]) ? E->name_replace() : *pThis->cName(); + } + + + if(!pThis->Local()) return TRUE; + + + return TRUE; +} + +void CInventoryOwner::net_Destroy() +{ + CAttachmentOwner::net_Destroy(); + + inventory().Clear(); + inventory().SetActiveSlot(NO_ACTIVE_SLOT); +} + + +void CInventoryOwner::save (NET_Packet &output_packet) +{ + u32 active_slot = inventory().GetActiveSlot(); + if(active_slot == NO_ACTIVE_SLOT /*|| active_slot == BOLT_SLOT*/) + output_packet.w_u8((u8)(-1)); + else + output_packet.w_u8((u8)active_slot); + + CharacterInfo().save(output_packet); + save_data (m_game_name, output_packet); + save_data (m_money, output_packet); +} +void CInventoryOwner::load (IReader &input_packet) +{ + u8 active_slot = input_packet.r_u8(); + if(active_slot == u8(-1)) + inventory().SetActiveSlot(NO_ACTIVE_SLOT); + else + inventory().Activate_deffered(active_slot, Device.dwFrame); + + m_tmp_active_slot_num = active_slot; + + CharacterInfo().load(input_packet); + load_data (m_game_name, input_packet); + load_data (m_money, input_packet); +} + + +void CInventoryOwner::UpdateInventoryOwner(u32 deltaT) +{ + inventory().Update(); + if(m_pTrade) m_pTrade->UpdateTrade(); + + if(IsTalking()) + { + //если наш собеседник перестал говорить с нами, + //то и нам нечего ждать. + if(!m_pTalkPartner->IsTalking()) + { + StopTalk(); + } + + //если мы умерли, то тоже не говорить + CEntityAlive* pOurEntityAlive = smart_cast(this); + R_ASSERT(pOurEntityAlive); + if(!pOurEntityAlive->g_Alive()) StopTalk(); + } +} + + +//достать PDA из специального слота инвентаря +CPda* CInventoryOwner::GetPDA() const +{ + return (CPda*)(m_inventory->m_slots[PDA_SLOT].m_pIItem); +} + +CTrade* CInventoryOwner::GetTrade() +{ + R_ASSERT2(m_pTrade, "trade for object does not init yet"); + return m_pTrade; +} + + +//состояние диалога + +//нам предлагают поговорить, +//проверяем наше отношение +//и если не враг начинаем разговор +bool CInventoryOwner::OfferTalk(CInventoryOwner* talk_partner) +{ + if(!IsTalkEnabled()) return false; + + //проверить отношение к собеседнику + CEntityAlive* pOurEntityAlive = smart_cast(this); + R_ASSERT(pOurEntityAlive); + + CEntityAlive* pPartnerEntityAlive = smart_cast(talk_partner); + R_ASSERT(pPartnerEntityAlive); + +// ALife::ERelationType relation = RELATION_REGISTRY().GetRelationType(this, talk_partner); +// if(relation == ALife::eRelationTypeEnemy) return false; + + if(!pOurEntityAlive->g_Alive() || !pPartnerEntityAlive->g_Alive()) return false; + + StartTalk(talk_partner); + + return true; +} + + +void CInventoryOwner::StartTalk(CInventoryOwner* talk_partner, bool start_trade) +{ + m_bTalking = true; + m_pTalkPartner = talk_partner; + + //тут же включаем торговлю + if(start_trade) + GetTrade()->StartTrade(talk_partner); +} +#include "UIGameSP.h" +#include "HUDmanager.h" +#include "ui\UITalkWnd.h" + +void CInventoryOwner::StopTalk() +{ + m_pTalkPartner = NULL; + m_bTalking = false; + + GetTrade()->StopTrade (); + + CUIGameSP* ui_sp = smart_cast(CurrentGameUI()); + if(ui_sp && ui_sp->TalkMenu->IsShown()) + ui_sp->TalkMenu->Stop(); +} + +bool CInventoryOwner::IsTalking() +{ + return m_bTalking; +} + +void CInventoryOwner::renderable_Render () +{ + auto activeItem = inventory().ActiveItem(); + if (activeItem) + activeItem->renderable_Render(); + + if (m_bDrawSecWpn) + { + auto secWeapon = inventory().ItemFromSlot(RIFLE_SLOT); + if (secWeapon && secWeapon != activeItem) + secWeapon->renderable_Render(); + + secWeapon = inventory().ItemFromSlot(RIFLE_2_SLOT); + if (secWeapon && secWeapon != activeItem) + secWeapon->renderable_Render(); + } + + CAttachmentOwner::renderable_Render(); +} + +void CInventoryOwner::OnItemTake (CInventoryItem *inventory_item, bool duringSpawn) +{ + CGameObject *object = smart_cast(this); + VERIFY (object); + object->callback(GameObject::eOnItemTake)(inventory_item->object().lua_game_object(), duringSpawn); + + attach (inventory_item); + + if(m_tmp_active_slot_num!=NO_ACTIVE_SLOT && inventory_item->BaseSlot()==m_tmp_active_slot_num) + { + inventory().Activate(m_tmp_active_slot_num); + m_tmp_active_slot_num = NO_ACTIVE_SLOT; + } +} + +//возвращает текуший разброс стрельбы с учетом движения (в радианах) +float CInventoryOwner::GetWeaponAccuracy () const +{ + return 0.f; +} + +float CInventoryOwner::GetOutfitWeightBonus () const +{ + const CCustomOutfit* outfit = GetOutfit(); + if (outfit) + return outfit->m_additional_weight2; + + return 0.f; +} + +//максимальный переносимы вес +float CInventoryOwner::MaxCarryWeight () const +{ + return inventory().GetMaxWeight() + GetAdditionalWeight(); +} + +float CInventoryOwner::GetAdditionalWeight () const +{ + float ret = GetOutfitWeightBonus(); + + //tatarinrafa added additional_inventory_weight to artefacts + for (int it = 0; it < inventory().m_belt.size(); ++it) + { + CArtefact* artefact = smart_cast(inventory().m_belt[it]); + if (artefact) + { + ret += artefact->m_additional_weight; + } + } + + return ret; +} + +void CInventoryOwner::spawn_supplies () +{ + CGameObject *game_object = smart_cast(this); + VERIFY (game_object); + if (smart_cast(this)) return; + + + if (use_bolts()) + Level().spawn_item ("bolt",game_object->Position(),game_object->ai_location().level_vertex_id(),game_object->ID()); + + if (!ai().get_alife() && GameID()==GAME_SINGLE) { + CSE_Abstract *abstract = Level().spawn_item("device_pda",game_object->Position(),game_object->ai_location().level_vertex_id(),game_object->ID(),true); + CSE_ALifeItemPDA *pda = smart_cast(abstract); + R_ASSERT (pda); + pda->m_original_owner = (u16)game_object->ID(); + NET_Packet P; + abstract->Spawn_Write (P,TRUE); + Level().Send (P,net_flags(TRUE)); + F_entity_Destroy (abstract); + } +} + +//игровое имя +LPCSTR CInventoryOwner::Name () const +{ +// return CharacterInfo().Name(); + return m_game_name.c_str(); +} + + + +void CInventoryOwner::NewPdaContact (CInventoryOwner* pInvOwner) +{ +} +void CInventoryOwner::LostPdaContact (CInventoryOwner* pInvOwner) +{ +} + +////////////////////////////////////////////////////////////////////////// +//для работы с relation system +u16 CInventoryOwner::object_id () const +{ + return smart_cast(this)->ID(); +} + + +////////////////////////////////////////////////////////////////////////// +//установка группировки на клиентском и серверном объкте + +void CInventoryOwner::SetCommunity (CHARACTER_COMMUNITY_INDEX new_community) +{ + CEntityAlive* EA = smart_cast(this); VERIFY(EA); + + CSE_Abstract* e_entity = ai().alife().objects().object(EA->ID(), false); + if(!e_entity) return; + + CSE_ALifeTraderAbstract* trader = smart_cast(e_entity); + if(!trader) return; + + CharacterInfo().m_CurrentCommunity.set(new_community); +// EA->id_Team = CharacterInfo().m_CurrentCommunity.team(); + EA->ChangeTeam(CharacterInfo().m_CurrentCommunity.team(), EA->g_Squad(), EA->g_Group()); + trader->m_community_index = new_community; +} + +void CInventoryOwner::SetRank (CHARACTER_RANK_VALUE rank) +{ + CEntityAlive* EA = smart_cast(this); VERIFY(EA); + CSE_Abstract* e_entity = ai().alife().objects().object(EA->ID(), false); + if(!e_entity) return; + CSE_ALifeTraderAbstract* trader = smart_cast(e_entity); + if(!trader) return; + + CharacterInfo().m_CurrentRank.set(rank); + trader->m_rank = rank; +} + +void CInventoryOwner::ChangeRank (CHARACTER_RANK_VALUE delta) +{ + SetRank(Rank()+delta); +} + +void CInventoryOwner::SetReputation (CHARACTER_REPUTATION_VALUE reputation) +{ + CEntityAlive* EA = smart_cast(this); VERIFY(EA); + CSE_Abstract* e_entity = ai().alife().objects().object(EA->ID(), false); + if(!e_entity) return; + + CSE_ALifeTraderAbstract* trader = smart_cast(e_entity); + if(!trader) return; + + CharacterInfo().m_CurrentReputation.set(reputation); + trader->m_reputation = reputation; +} + +void CInventoryOwner::ChangeReputation (CHARACTER_REPUTATION_VALUE delta) +{ + SetReputation(Reputation() + delta); +} + + +void CInventoryOwner::OnItemDrop (CInventoryItem *inventory_item) +{ + CGameObject *object = smart_cast(this); + VERIFY (object); + object->callback(GameObject::eOnItemDrop)(inventory_item->object().lua_game_object(), inventory_item->object().GetTmpPreDestroy() != 0); + + detach (inventory_item); +} + +void CInventoryOwner::OnItemDropUpdate () +{ +} + +void CInventoryOwner::OnItemBelt (CInventoryItem *inventory_item, EItemPlace previous_place) +{ + attach (inventory_item); +} +void CInventoryOwner::OnItemRuck (CInventoryItem *inventory_item, EItemPlace previous_place) +{ + detach (inventory_item); +} +void CInventoryOwner::OnItemSlot (CInventoryItem *inventory_item, EItemPlace previous_place) +{ + attach (inventory_item); +} + +CInventoryItem* CInventoryOwner::GetCurrentOutfit() const +{ + return inventory().m_slots[OUTFIT_SLOT].m_pIItem; +} + +void CInventoryOwner::on_weapon_shot_start (CWeapon *weapon) +{ +} + +void CInventoryOwner::on_weapon_shot_stop (CWeapon *weapon) +{ +} + +void CInventoryOwner::on_weapon_hide (CWeapon *weapon) +{ +} + +LPCSTR CInventoryOwner::trade_section () const +{ + const CGameObject *game_object = smart_cast(this); + VERIFY (game_object); + return (READ_IF_EXISTS(pSettings,r_string,game_object->cNameSect(),"trade_section","trade")); +} + +float CInventoryOwner::deficit_factor (const shared_str §ion) const +{ + if (!m_purchase_list) + return (1.f); + + return (m_purchase_list->deficit(section)); +} + +void CInventoryOwner::sell_useless_items () +{ + CGameObject *object = smart_cast(this); + + TIItemContainer::iterator I = inventory().m_all.begin(); + TIItemContainer::iterator E = inventory().m_all.end(); + for ( ; I != E; ++I) { + if ((*I)->object().CLS_ID == CLSID_IITEM_BOLT) + continue; + + if ((*I)->object().CLS_ID == CLSID_DEVICE_PDA) { + CPda *pda = smart_cast(*I); + VERIFY (pda); + if (pda->GetOriginalOwnerID() == object->ID()) + continue; + } + + (*I)->object().DestroyObject(); + } +} + +bool CInventoryOwner::AllowItemToTrade (CInventoryItem const * item, EItemPlace place) const +{ + return ( + trade_parameters().enabled( + CTradeParameters::action_sell(0), + item->object().cNameSect() + ) + ); +} + +void CInventoryOwner::set_money (u32 amount, bool bSendEvent) +{ + if(InfinitiveMoney()) + m_money = _max(m_money, amount); + else + m_money = amount; + + if(bSendEvent) + { + CGameObject *object = smart_cast(this); + NET_Packet packet; + object->u_EventGen (packet,GE_MONEY,object->ID()); + packet.w_u32 (m_money); + object->u_EventSend (packet); + } +} + +bool CInventoryOwner::use_default_throw_force () +{ + return (true); +} + +float CInventoryOwner::missile_throw_force () +{ + NODEFAULT; +#ifdef DEBUG + return (0.f); +#endif +} + +bool CInventoryOwner::use_throw_randomness () +{ + return (true); +} + + +bool CInventoryOwner::IsUpgradeEnabled(const CInventoryOwner* oth) const +{ +#pragma todo("this code supports the infoportion dependancy, but the variable CanUpgrade() returns only true or false. We can change it in specific_character->load_shared") + shared_str str = SpecificCharacter().CanUpgrade(); + if (str == "1") + { + return true; + } + else if (str == "0") + { + return false; + } + else + { + return oth->HasInfo(str); + } + +} + +CPurchaseList* CInventoryOwner::get_create_purchase_list() +{ + if (!m_purchase_list) + { + m_purchase_list = new CPurchaseList(); + } + return m_purchase_list; +} diff --git a/src/xrGameLA/InventoryOwner.h b/src/xrGameLA/InventoryOwner.h new file mode 100644 index 000000000..48054e608 --- /dev/null +++ b/src/xrGameLA/InventoryOwner.h @@ -0,0 +1,227 @@ +///////////////////////////////////////////////////// +// Для персонажей, имеющих инвентарь +// InventoryOwner.h +////////////////////////////////////////////////////// + +#pragma once +#include "InfoPortionDefs.h" +#include "pda_space.h" +#include "attachment_owner.h" +#include "script_space_forward.h" +#include "character_info.h" +#include "inventory_space.h" +#include "map_location.h" + +class CSE_Abstract; +class CInventory; +class CInventoryItem; +class CTrade; +class CPda; +class CGameObject; +class CEntityAlive; +class CCustomZone; +class CInfoPortionWrapper; +class NET_Packet; +class CCharacterInfo; +class CSpecificCharacter; +class CTradeParameters; +class CPurchaseList; +class CWeapon; +class CCustomOutfit; + +class CInventoryOwner : public CAttachmentOwner { +public: + CInventoryOwner (); + virtual ~CInventoryOwner (); + +public: + virtual CInventoryOwner* cast_inventory_owner () {return this;} +public: + + virtual DLL_Pure *_construct (); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + void Init (); + virtual void Load (LPCSTR section); + virtual void reinit (); + virtual void reload (LPCSTR section); + virtual void OnEvent (NET_Packet& P, u16 type); + + //serialization + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + + + //обновление + virtual void UpdateInventoryOwner (u32 deltaT); + virtual bool CanPutInSlot (PIItem item, u32 slot){return true;}; + + + CPda* GetPDA () const; + + + // инвентарь + CInventory *m_inventory; + + //////////////////////////////////// + //торговля и общение с персонажем + + virtual bool AllowItemToTrade (CInventoryItem const * item, EItemPlace place) const; + virtual void OnFollowerCmd (int cmd) {};//redefine for CAI_Stalkker + bool bDisableBreakDialog; + //инициализация объекта торговли + CTrade* GetTrade(); + + //для включения разговора + virtual bool OfferTalk (CInventoryOwner* talk_partner); + virtual void StartTalk (CInventoryOwner* talk_partner, bool start_trade = true); + virtual void StopTalk (); + virtual bool IsTalking (); + + virtual void EnableTalk () {m_bAllowTalk = true;} + virtual void DisableTalk () {m_bAllowTalk = false;} + virtual bool IsTalkEnabled () { return m_bAllowTalk;} + + void EnableTrade () {m_bAllowTrade = true;} + void DisableTrade () {m_bAllowTrade = false;} + bool IsTradeEnabled () { return m_bAllowTrade;} + + bool IsUpgradeEnabled(const CInventoryOwner* oth) const; + + + CInventoryOwner* GetTalkPartner() {return m_pTalkPartner;} + virtual void NewPdaContact (CInventoryOwner*); + virtual void LostPdaContact (CInventoryOwner*); + + //игровое имя + virtual LPCSTR Name () const; + u32 get_money () const {return m_money;} + void set_money (u32 amount, bool bSendEvent); + + CMapLocation* special_map_spot; +protected: + u32 m_money; + // торговля + CTrade* m_pTrade; + bool m_bTalking; + CInventoryOwner* m_pTalkPartner; + + bool m_bAllowTalk; + bool m_bAllowTrade; + //bool m_bAllowUpgrade; + + u32 m_tmp_active_slot_num; + ////////////////////////////////////////////////////////////////////////// + // сюжетная информация +public: + //персонаж получил новую порцию информации + virtual bool OnReceiveInfo (shared_str info_id) const; + //убрать информацию + virtual void OnDisableInfo (shared_str info_id) const; + //передать/удалить информацию через сервер + virtual void TransferInfo (shared_str info_id, bool add_info) const; + //есть ли информация у персонажа + virtual bool HasInfo (shared_str info_id) const; + virtual bool GetInfo (shared_str info_id, INFO_DATA&) const; + + void CInventoryOwner::DumpInfo() const; + + CInfoPortionWrapper *m_known_info_registry; + + ////////////////////////////////////////////////////////////////////////// + // инвентарь +public: + const CInventory &inventory() const {VERIFY (m_inventory); return(*m_inventory);} + CInventory &inventory() {VERIFY (m_inventory); return(*m_inventory);} + + //возвращает текуший разброс стрельбы (в радианах) с учетом движения + virtual float GetWeaponAccuracy () const; + //максимальный переносимы вес + virtual float MaxCarryWeight () const; + virtual float GetOutfitWeightBonus () const; + virtual float GetAdditionalWeight () const; + + virtual CCustomOutfit* GetOutfit() const {return NULL;}; + + ////////////////////////////////////////////////////////////////////////// + //игровые характеристики персонажа +public: + CCharacterInfo& CharacterInfo () const {VERIFY(m_pCharacterInfo); return *m_pCharacterInfo;} + IC const CSpecificCharacter& SpecificCharacter () const {return CharacterInfo().m_SpecificCharacter;}; + bool InfinitiveMoney () {return CharacterInfo().m_SpecificCharacter.MoneyDef().inf_money;} + + //установка группировки на клиентском и серверном объкте + virtual void SetCommunity (CHARACTER_COMMUNITY_INDEX); + virtual void SetRank (CHARACTER_RANK_VALUE); + virtual void ChangeRank (CHARACTER_RANK_VALUE); + virtual void SetReputation (CHARACTER_REPUTATION_VALUE); + virtual void ChangeReputation(CHARACTER_REPUTATION_VALUE); + + //для работы с relation system + u16 object_id () const; + CHARACTER_COMMUNITY_INDEX Community () const {return CharacterInfo().Community().index();}; + CHARACTER_RANK_VALUE Rank () const {return CharacterInfo().Rank().value();}; + CHARACTER_REPUTATION_VALUE Reputation () const {return CharacterInfo().Reputation().value();}; + +protected: + CCharacterInfo* m_pCharacterInfo; + xr_string m_game_name; + +public: + virtual void renderable_Render (); + virtual void OnItemTake (CInventoryItem *inventory_item, bool duringSpawn); + + virtual void OnItemBelt (CInventoryItem *inventory_item, EItemPlace previous_place); + virtual void OnItemRuck (CInventoryItem *inventory_item, EItemPlace previous_place); + virtual void OnItemSlot (CInventoryItem *inventory_item, EItemPlace previous_place); + + virtual void OnItemDrop (CInventoryItem *inventory_item); + + virtual void OnItemDropUpdate (); + virtual bool use_bolts () const {return(true);} + virtual void spawn_supplies (); + + CInventoryItem* CInventoryOwner::GetCurrentOutfit() const; + + ////////////////////////////////////////////////////////////////////////// + // связь со скриптами + ////////////////////////////////////////////////////////////////////////// +protected: + shared_str m_item_to_spawn; + u32 m_ammo_in_box_to_spawn; + +public: + IC const shared_str &item_to_spawn () const {return m_item_to_spawn;} + IC const u32 &ammo_in_box_to_spawn () const {return m_ammo_in_box_to_spawn;} + +public: + virtual void on_weapon_shot_start (CWeapon *weapon); + virtual void on_weapon_shot_stop (CWeapon *weapon); + virtual void on_weapon_hide (CWeapon *weapon); + +public: + virtual bool use_simplified_visual () const {return (false);}; + +private: + CTradeParameters *m_trade_parameters; + CPurchaseList *m_purchase_list; + BOOL m_need_osoznanie_mode; + bool m_bDrawSecWpn; + +public: + IC CTradeParameters &trade_parameters () const; + CPurchaseList *get_create_purchase_list(); + virtual LPCSTR trade_section () const; + float deficit_factor (const shared_str §ion) const; + void sell_useless_items (); + virtual void on_before_sell (CInventoryItem *item) {} + virtual void on_before_buy (CInventoryItem *item) {} + virtual bool can_use_dynamic_lights () {return true;} + virtual bool use_default_throw_force (); + virtual float missile_throw_force (); + virtual bool use_throw_randomness (); + virtual bool NeedOsoznanieMode () {return !!m_need_osoznanie_mode;} + virtual bool NeedToRenderSecordary () {return !!m_bDrawSecWpn;} +}; + +#include "inventory_owner_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ItemHealth.cpp b/src/xrGameLA/ItemHealth.cpp new file mode 100644 index 000000000..441460582 --- /dev/null +++ b/src/xrGameLA/ItemHealth.cpp @@ -0,0 +1,10 @@ +#include "stdafx.h" +#include "itemhealth.h" + +CItemHealth::CItemHealth(void) +{ +} + +CItemHealth::~CItemHealth(void) +{ +} diff --git a/src/xrGameLA/ItemHealth.h b/src/xrGameLA/ItemHealth.h new file mode 100644 index 000000000..929516047 --- /dev/null +++ b/src/xrGameLA/ItemHealth.h @@ -0,0 +1,10 @@ +#pragma once + +#include "gameobject.h" + +class CItemHealth : public CGameObject +{ +public: + CItemHealth(void); + virtual ~CItemHealth(void); +}; diff --git a/src/xrGameLA/ItemListTypes.h b/src/xrGameLA/ItemListTypes.h new file mode 100644 index 000000000..41e6e6467 --- /dev/null +++ b/src/xrGameLA/ItemListTypes.h @@ -0,0 +1,53 @@ +//--------------------------------------------------------------------------- +#ifndef ItemListTypesH +#define ItemListTypesH + +//--------------------------------------------------------------------------- + +class ListItem{ + friend class CListHelper; + friend class TItemList; + shared_str key; + int type; + void* item; +public: + typedef fastdelegate::FastDelegate1 TOnListItemFocused; + typedef fastdelegate::FastDelegate1 TOnClick; + TOnClick OnClickEvent; + TOnListItemFocused OnItemFocused; + TOnDrawThumbnail OnDrawThumbnail; +public: + int tag; + LPVOID m_Object; + int icon_index; + u32 prop_color; +public: + enum{ + flShowCB = (1<<0), + flCBChecked = (1<<1), + flDrawThumbnail = (1<<2), + flDrawCanvas = (1<<3), + flSorted = (1<<4), + flHidden = (1<<5), + }; + Flags32 m_Flags; +public: + ListItem (int _type):type(_type),prop_color(0),item(0),key(0),tag(0),icon_index(-1),OnDrawThumbnail(0),OnItemFocused(0),m_Object(0){m_Flags.zero();} + virtual ~ListItem (){}; + void SetName (LPCSTR _key){key=_key;} + + IC void Visible (BOOL val){m_Flags.set(flHidden, !val);} + IC BOOL Visible () const{ return !m_Flags.test(flHidden);} + IC int Type (){return type;} + IC void* Item (){return item;} + IC LPCSTR Key (){return *key;} + IC void SetIcon (int index){icon_index=index;} +}; + +DEFINE_VECTOR (ListItem*,ListItemsVec,ListItemsIt); +//--------------------------------------------------------------------------- +#endif + + + + diff --git a/src/xrGameLA/Level.cpp b/src/xrGameLA/Level.cpp new file mode 100644 index 000000000..7b0e6df46 --- /dev/null +++ b/src/xrGameLA/Level.cpp @@ -0,0 +1,1210 @@ +#include "pch_script.h" +#include "../fdemorecord.h" +#include "../fdemoplay.h" +#include "../environment.h" +#include "../igame_persistent.h" +#include "ParticlesObject.h" +#include "Level.h" +#include "xrServer.h" +#include "net_queue.h" +#include "game_cl_base.h" +#include "entity_alive.h" +#include "hudmanager.h" +#include "ai_space.h" +#include "ai_debug.h" +#include "PHdynamicdata.h" +#include "Physics.h" +#include "ShootingObject.h" +#include "player_hud.h" +#include "Level_Bullet_Manager.h" +#include "script_process.h" +#include "script_engine.h" +#include "script_engine_space.h" +#include "team_base_zone.h" +#include "infoportion.h" +#include "patrol_path_storage.h" +#include "date_time.h" +#include "space_restriction_manager.h" +#include "seniority_hierarchy_holder.h" +#include "space_restrictor.h" +#include "client_spawn_manager.h" +#include "autosave_manager.h" +#include "ClimableObject.h" +#include "level_graph.h" +#include "mt_config.h" +#include "phcommander.h" +#include "fast_entity_update.h" +#include "map_manager.h" +#include "../CameraManager.h" +#include "level_sounds.h" +#include "car.h" +#include "trade_parameters.h" +#include "game_cl_base_weapon_usage_statistic.h" +#include "clsid_game.h" +#include "MainMenu.h" +#include "..\XR_IOConsole.h" +#include "actor.h" +#include "alife_simulator.h" +#include "CustomTimersManager.h" + +#ifdef DEBUG +# include "level_debug.h" +# include "ai/stalker/ai_stalker.h" +# include "physicobject.h" +#endif + +#include "debug_renderer.h" +#include "ai/stalker/ai_stalker.h" +#include + +ENGINE_API bool g_dedicated_server; + +extern BOOL g_bDebugDumpPhysicsStep; + +int psLUA_GCSTEP = 10; +CPHWorld *ph_world = 0; +float g_cl_lvInterp = 0; +u32 lvInterpSteps = 0; +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CLevel::CLevel():IPureClient (Device.GetTimerGlobal()) +#ifdef PROFILE_CRITICAL_SECTIONS + ,DemoCS(MUTEX_PROFILE_ID(DemoCS)) +#endif // PROFILE_CRITICAL_SECTIONS +{ + g_bDebugEvents = strstr(Core.Params,"-debug_ge")?TRUE:FALSE; + + Server = NULL; + + game = NULL; + game_events = new NET_Queue_Event(); + + game_configured = FALSE; + m_bGameConfigStarted = FALSE; + + eChangeRP = Engine.Event.Handler_Attach ("LEVEL:ChangeRP",this); + eDemoPlay = Engine.Event.Handler_Attach ("LEVEL:PlayDEMO",this); + eChangeTrack = Engine.Event.Handler_Attach ("LEVEL:PlayMusic",this); + eEnvironment = Engine.Event.Handler_Attach ("LEVEL:Environment",this); + + eEntitySpawn = Engine.Event.Handler_Attach ("LEVEL:spawn",this); + + m_pBulletManager = new CBulletManager(); + + if(!g_dedicated_server) + m_map_manager = new CMapManager(); + else + m_map_manager = NULL; + +// m_pFogOfWarMngr = xr_new(); +//---------------------------------------------------- + m_bNeed_CrPr = false; + m_bIn_CrPr = false; + m_dwNumSteps = 0; + m_dwDeltaUpdate = u32(fixed_step*1000); + m_dwLastNetUpdateTime = 0; + + physics_step_time_callback = (PhysicsStepTimeCallback*) &PhisStepsCallback; + m_seniority_hierarchy_holder= new CSeniorityHierarchyHolder(); + + if(!g_dedicated_server) + { + m_level_sound_manager = new CLevelSoundManager(); + m_space_restriction_manager = new CSpaceRestrictionManager(); + m_client_spawn_manager = new CClientSpawnManager(); + m_autosave_manager = new CAutosaveManager(); + #ifdef DRENDER + m_debug_renderer = new CDebugRenderer(); + #endif + #ifdef DEBUG + m_level_debug = new CLevelDebug(); + #endif + + }else + { + m_level_sound_manager = NULL; + m_client_spawn_manager = NULL; + m_autosave_manager = NULL; + m_space_restriction_manager = NULL; + #ifdef DRENDER + m_debug_renderer = NULL; + #endif + #ifdef DEBUG + m_level_debug = NULL; + #endif + + } + + + + m_ph_commander = new CPHCommander(); + m_ph_commander_scripts = new CPHCommander(); + + m_fast_updater = new CFastEntityUpdater(); + +#ifdef DEBUG + m_bSynchronization = false; +#endif + //--------------------------------------------------------- + pStatGraphR = NULL; + pStatGraphS = NULL; + //--------------------------------------------------------- + pObjects4CrPr.clear(); + pActors4CrPr.clear(); + //--------------------------------------------------------- + g_player_hud = new player_hud(); + g_player_hud->load_default(); + //--------------------------------------------------------- + pCurrentControlEntity = NULL; + + //--------------------------------------------------------- + m_dwCL_PingLastSendTime = 0; + m_dwCL_PingDeltaSend = 1000; + m_dwRealPing = 0; + + //--------------------------------------------------------- + m_sDemoName[0] = 0; + m_bDemoSaveMode = FALSE; + m_dwStoredDemoDataSize = 0; + m_pStoredDemoData = NULL; + m_pOldCrashHandler = NULL; + m_we_used_old_crach_handler = false; + + Msg("%s", Core.Params); +} + +extern CAI_Space *g_ai_space; + +CLevel::~CLevel() +{ + xr_delete (g_player_hud); + + Msg ("- Destroying level"); + + Engine.Event.Handler_Detach (eEntitySpawn, this); + + Engine.Event.Handler_Detach (eEnvironment, this); + Engine.Event.Handler_Detach (eChangeTrack, this); + Engine.Event.Handler_Detach (eDemoPlay, this); + Engine.Event.Handler_Detach (eChangeRP, this); + + if (ph_world) + { + ph_world->Destroy (); + xr_delete (ph_world); + } + + // destroy PSs + for (POIt p_it=m_StaticParticles.begin(); m_StaticParticles.end()!=p_it; ++p_it) + CParticlesObject::Destroy(*p_it); + m_StaticParticles.clear (); + + // Unload sounds + // unload prefetched sounds + sound_registry.clear (); + + // unload static sounds + for (u32 i=0; idestroy(); + xr_delete (static_Sounds[i]); + } + static_Sounds.clear (); + + xr_delete (m_level_sound_manager); + + xr_delete (m_space_restriction_manager); + + xr_delete (m_seniority_hierarchy_holder); + + xr_delete (m_client_spawn_manager); + + xr_delete (m_autosave_manager); + +#ifdef DRENDER + xr_delete (m_debug_renderer); +#endif + + if (!g_dedicated_server) + ai().script_engine().remove_script_process(ScriptEngine::eScriptProcessorLevel); + + xr_delete (game); + xr_delete (game_events); + + + //by Dandy + //destroy fog of war +// xr_delete (m_pFogOfWar); + //destroy bullet manager + xr_delete (m_pBulletManager); + //----------------------------------------------------------- + xr_delete (pStatGraphR); + xr_delete (pStatGraphS); + + //----------------------------------------------------------- + xr_delete (m_ph_commander); + xr_delete (m_fast_updater); + xr_delete (m_ph_commander_scripts); + //----------------------------------------------------------- + pObjects4CrPr.clear(); + pActors4CrPr.clear(); + + ai().unload (); + //----------------------------------------------------------- +#ifdef DEBUG + xr_delete (m_level_debug); +#endif + //----------------------------------------------------------- + xr_delete (m_map_manager); +// xr_delete (m_pFogOfWarMngr); + //----------------------------------------------------------- + Demo_Clear (); + m_aDemoData.clear (); + + // here we clean default trade params + // because they should be new for each saved/loaded game + // and I didn't find better place to put this code in + CTradeParameters::clean (); + + if (m_we_used_old_crach_handler) + Debug.set_crashhandler (m_pOldCrashHandler); + +} + +shared_str CLevel::name () const +{ + return (m_name); +} + +void CLevel::GetLevelInfo( CServerInfo* si ) +{ + Server->GetServerInfo( si ); +} + + +void CLevel::PrefetchSound (LPCSTR name) +{ + // preprocess sound name + string_path tmp; + xr_strcpy (tmp,name); + xr_strlwr (tmp); + if (strext(tmp)) *strext(tmp)=0; + shared_str snd_name = tmp; + // find in registry + SoundRegistryMapIt it = sound_registry.find(snd_name); + // if find failed - preload sound + if (it==sound_registry.end()) + sound_registry[snd_name].create(snd_name.c_str(),st_Effect,sg_SourceType); +} + +// Game interface //////////////////////////////////////////////////// +int CLevel::get_RPID(LPCSTR /**name/**/) +{ + /* + // Gain access to string + LPCSTR params = pLevel->r_string("respawn_point",name); + if (0==params) return -1; + + // Read data + Fvector4 pos; + int team; + sscanf (params,"%f,%f,%f,%d,%f",&pos.x,&pos.y,&pos.z,&team,&pos.w); pos.y += 0.1f; + + // Search respawn point + svector &rp = Level().get_team(team).RespawnPoints; + for (int i=0; i<(int)(rp.size()); ++i) + if (pos.similar(rp[i],EPS_L)) return i; + */ + return -1; +} + +BOOL g_bDebugEvents = FALSE ; + + +void CLevel::cl_Process_Event (u16 dest, u16 type, NET_Packet& P) +{ + // Msg ("--- event[%d] for [%d]",type,dest); + CObject* O = Objects.net_Find (dest); + if (0==O) { +#ifdef DEBUG + Msg("* WARNING: c_EVENT[%d] to object: can't find object with id [%d]",type,dest); +#endif // DEBUG + return; + } + CGameObject* GO = smart_cast(O); + if (!GO) { + Msg("! ERROR: c_EVENT[%d] to object: is not gameobject",dest); + return; + } + if (type != GE_DESTROY_REJECT) + { + if (type == GE_DESTROY) + Game().OnDestroy(GO); + GO->OnEvent (P,type); + } + else { // handle GE_DESTROY_REJECT here + u32 pos = P.r_tell(); + u16 id = P.r_u16(); + P.r_seek (pos); + + bool ok = true; + + CObject *D = Objects.net_Find (id); + if (0==D) { + Msg ("! ERROR: c_EVENT[%d] : unknown dest",id); + ok = false; + } + + CGameObject *GD = smart_cast(D); + if (!GD) { + Msg ("! ERROR: c_EVENT[%d] : non-game-object",id); + ok = false; + } + + GO->OnEvent (P,GE_OWNERSHIP_REJECT); + if (ok) + { + Game().OnDestroy(GD); + GD->OnEvent (P,GE_DESTROY); + }; + } +}; + +void CLevel::ProcessGameEvents () +{ + // Game events + { + NET_Packet P; + u32 svT = timeServer()-NET_Latency; + + /* + if (!game_events->queue.empty()) + Msg("- d[%d],ts[%d] -- E[svT=%d],[evT=%d]",Device.dwTimeGlobal,timeServer(),svT,game_events->queue.begin()->timestamp); + */ + + while (game_events->available(svT)) + { + u16 ID,dest,type; + game_events->get (ID,dest,type,P); + + switch (ID) + { + case M_SPAWN: + { + u16 dummy16; + P.r_begin(dummy16); + cl_Process_Spawn(P); + }break; + case M_EVENT: + { + cl_Process_Event(dest, type, P); + }break; + default: + { + VERIFY(0); + }break; + } + } + } + if (OnServer() && GameID()!= GAME_SINGLE) + Game().m_WeaponUsageStatistic->Send_Check_Respond(); +} + +#ifdef DEBUG_MEMORY_MANAGER + extern Flags32 psAI_Flags; + extern float debug_on_frame_gather_stats_frequency; + +struct debug_memory_guard { + inline debug_memory_guard () + { + mem_alloc_gather_stats (!!psAI_Flags.test(aiDebugOnFrameAllocs)); + mem_alloc_gather_stats_frequency (debug_on_frame_gather_stats_frequency); + } + + inline ~debug_memory_guard () + { +// mem_alloc_gather_stats (false); + } +}; +#endif // DEBUG_MEMORY_MANAGER + +void CLevel::OnFrame () +{ +#ifdef DEBUG_MEMORY_MANAGER + debug_memory_guard __guard__; +#endif // DEBUG_MEMORY_MANAGER + + m_feel_deny.update (); + + if (GameID()!=GAME_SINGLE) psDeviceFlags.set(rsDisableObjectsAsCrows,true); + else psDeviceFlags.set(rsDisableObjectsAsCrows,false); + + // commit events from bullet manager from prev-frame + Device.Statistic->TEST0.Begin (); + BulletManager().CommitEvents (); + Device.Statistic->TEST0.End (); + + // Client receive + if (net_isDisconnected()) + { + if (OnClient() && GameID() != GAME_SINGLE) + ClearAllObjects(); + + Engine.Event.Defer ("kernel:disconnect"); + return; + } else { + + Device.Statistic->netClient1.Begin(); + + ClientReceive (); + + Device.Statistic->netClient1.End (); + } + + ProcessGameEvents (); + + + if (m_bNeed_CrPr) make_NetCorrectionPrediction(); + + if(!g_dedicated_server ) + { + if (g_mt_config.test(mtMap)) + Device.seqParallel.push_back (fastdelegate::FastDelegate0<>(m_map_manager,&CMapManager::Update)); + else + MapManager().Update (); + } + // Inherited update + inherited::OnFrame (); + + // Draw client/server stats + if ( !g_dedicated_server && psDeviceFlags.test(rsStatistic)) + { + CGameFont* F = UI().Font().pFontDI; + if (!psNET_direct_connect) + { + if ( IsServer() ) + { + const IServerStatistic* S = Server->GetStatistic(); + F->SetHeightI (0.015f); + F->OutSetI (0.0f,0.5f); + F->SetColor (D3DCOLOR_XRGB(0,255,0)); + F->OutNext ("IN: %4d/%4d (%2.1f%%)", S->bytes_in_real, S->bytes_in, 100.f*float(S->bytes_in_real)/float(S->bytes_in)); + F->OutNext ("OUT: %4d/%4d (%2.1f%%)", S->bytes_out_real, S->bytes_out, 100.f*float(S->bytes_out_real)/float(S->bytes_out)); + F->OutNext ("client_2_sever ping: %d", net_Statistic.getPing()); + F->OutNext ("SPS/Sended : %4d/%4d", S->dwBytesPerSec, S->dwBytesSended); + F->OutNext ("sv_urate/cl_urate : %4d/%4d", psNET_ServerUpdate, psNET_ClientUpdate); + + F->SetColor (D3DCOLOR_XRGB(255,255,255)); + for (u32 I=0; IGetClientsCount(); ++I) + { + IClient* C = Server->client_Get(I); + Server->UpdateClientStatistic(C); + F->OutNext("P(%d), BPS(%2.1fK), MRR(%2d), MSR(%2d), Retried(%2d), Blocked(%2d)", + //Server->game->get_option_s(*C->Name,"name",*C->Name), + // C->Name, + C->stats.getPing(), + float(C->stats.getBPS()),// /1024, + C->stats.getMPS_Receive (), + C->stats.getMPS_Send (), + C->stats.getRetriedCount(), + C->stats.dwTimesBlocked + ); + } + } + if (IsClient()) + { + IPureClient::UpdateStatistic(); + + F->SetHeightI(0.015f); + F->OutSetI (0.0f,0.5f); + F->SetColor (D3DCOLOR_XRGB(0,255,0)); + F->OutNext ("client_2_sever ping: %d", net_Statistic.getPing()); + F->OutNext ("sv_urate/cl_urate : %4d/%4d", psNET_ServerUpdate, psNET_ClientUpdate); + + F->SetColor (D3DCOLOR_XRGB(255,255,255)); + F->OutNext("P(%d), MRR(%2d), MSR(%2d), Retried(%2d), Blocked(%2d), Sended(%2d), SPS(%2d)", // BPS(%2.1fK), + //Server->game->get_option_s(C->Name,"name",C->Name), + // C->Name, + net_Statistic.getPing(), + float(net_Statistic.getBPS()),// /1024, + net_Statistic.getMPS_Receive (), + net_Statistic.getMPS_Send (), + net_Statistic.getRetriedCount(), + net_Statistic.dwTimesBlocked, + net_Statistic.dwBytesSended +// net_Statistic.dwBytesPerSec + ); + } + } + } + +// g_pGamePersistent->Environment().SetGameTime (GetGameDayTimeSec(),GetGameTimeFactor()); + g_pGamePersistent->Environment().SetGameTime (GetEnvironmentGameDayTimeSec(),GetGameTimeFactor()); + + //Device.Statistic->cripting.Begin (); + if (!g_dedicated_server) + { + //TODO: why CLevel::OnFrame is calling before CLevel::Load_GameSpecific_After? + //is it logic error? + CScriptProcess * levelScript = ai().script_engine().script_process(ScriptEngine::eScriptProcessorLevel); + if (levelScript != NULL) + levelScript->update(); + } + //Device.Statistic->Scripting.End (); + m_ph_commander->update (); + m_ph_commander_scripts->update (); + m_fast_updater->Update (); +// autosave_manager().update (); + + //просчитать полет пуль + Device.Statistic->TEST0.Begin (); + BulletManager().CommitRenderSet (); + Device.Statistic->TEST0.End (); +#ifdef USE_TIMERS_MANAGER + if (GameID()==GAME_SINGLE && ai().alife().initialized()) + ai().alife().timers().Update(); +#endif + // update static sounds + if(!g_dedicated_server) + { + if (g_mt_config.test(mtLevelSounds)) + Device.seqParallel.push_back (fastdelegate::FastDelegate0<>(m_level_sound_manager,&CLevelSoundManager::Update)); + else + m_level_sound_manager->Update (); + } + // deffer LUA-GC-STEP + if (!g_dedicated_server) + { + if (g_mt_config.test(mtLUA_GC)) Device.seqParallel.push_back (fastdelegate::FastDelegate0<>(this,&CLevel::script_gc)); + else script_gc () ; + } + //----------------------------------------------------- + if (pStatGraphR) + { + static float fRPC_Mult = 10.0f; + static float fRPS_Mult = 1.0f; + + pStatGraphR->AppendItem(float(m_dwRPC)*fRPC_Mult, 0xffff0000, 1); + pStatGraphR->AppendItem(float(m_dwRPS)*fRPS_Mult, 0xff00ff00, 0); + }; +} + +void CLevel::script_gc () +{ + lua_gc (ai().script_engine().lua(), LUA_GCSTEP, psLUA_GCSTEP); +} + +#ifdef DEBUG_PRECISE_PATH +void test_precise_path (); +#endif + +#ifdef DEBUG +extern Flags32 dbg_net_Draw_Flags; +#endif + +extern void draw_wnds_rects(); + +void CLevel::OnRender() +{ + inherited::OnRender (); + + if (!game) + return; + + Game().OnRender(); + //отрисовать трассы пуль + //Device.Statistic->TEST1.Begin(); + BulletManager().Render(); + //Device.Statistic->TEST1.End(); + //отрисовать интерфейc пользователя + HUD().RenderUI(); + + draw_wnds_rects(); + + + + +#ifdef DEBUG + ph_world->OnRender (); +#endif + +#ifdef DEBUG + if (ai().get_level_graph()) + ai().level_graph().render(); + +#ifdef DEBUG_PRECISE_PATH + test_precise_path (); +#endif + + CAI_Stalker *stalker = smart_cast(Level().CurrentEntity()); + if (stalker) + stalker->OnRender (); + + if (bDebug) { + for (u32 I=0; I < Level().Objects.o_count(); I++) { + CObject* _O = Level().Objects.o_get_by_iterator(I); + + CPhysicObject *physic_object = smart_cast(_O); + if (physic_object) + physic_object->OnRender(); + + CSpaceRestrictor *space_restrictor = smart_cast (_O); + if (space_restrictor) + space_restrictor->OnRender(); + CClimableObject *climable = smart_cast (_O); + if(climable) + climable->OnRender(); + CTeamBaseZone *team_base_zone = smart_cast(_O); + if (team_base_zone) + team_base_zone->OnRender(); + + if (GameID() != GAME_SINGLE) + { + CInventoryItem* pIItem = smart_cast(_O); + if (pIItem) pIItem->OnRender(); + } + + + if (dbg_net_Draw_Flags.test(1<<11)) //draw skeleton + { + CGameObject* pGO = smart_cast (_O); + if (pGO && pGO != Level().CurrentViewEntity() && !pGO->H_Parent()) + { + if (pGO->Position().distance_to_sqr(Device.vCameraPosition) < 400.0f) + { + pGO->dbg_DrawSkeleton(); + } + } + }; + } + // [7/5/2005] + if (Server && Server->game) Server->game->OnRender(); + // [7/5/2005] + ObjectSpace.dbgRender (); + + //--------------------------------------------------------------------- + UI().Font().pFontStat->OutSet (170,630); + UI().Font().pFontStat->SetHeight (16.0f); + UI().Font().pFontStat->SetColor (0xffff0000); + + if(Server)UI().Font().pFontStat->OutNext ("Client Objects: [%d]",Server->GetEntitiesNum()); + UI().Font().pFontStat->OutNext ("Server Objects: [%d]",Objects.o_count()); + UI().Font().pFontStat->OutNext ("Interpolation Steps: [%d]", Level().GetInterpolationSteps()); + UI().Font().pFontStat->SetHeight (8.0f); + //--------------------------------------------------------------------- + } +#endif + +#ifdef DEBUG + if (bDebug) { + DBG().draw_object_info (); + DBG().draw_text (); + DBG().draw_level_info (); + } +#endif + +#ifdef DRENDER + debug_renderer().render (); +#endif + +#ifdef LOG_PLANNER + if (psAI_Flags.is(aiVision)) { + for (u32 I=0; I < Level().Objects.o_count(); I++) { + CObject *object = Objects.o_get_by_iterator(I); + CAI_Stalker *stalker = smart_cast(object); + if (!stalker) + continue; + stalker->dbg_draw_vision (); + } + } + + + if (psAI_Flags.test(aiDrawVisibilityRays)) { + for (u32 I=0; I < Level().Objects.o_count(); I++) { + CObject *object = Objects.o_get_by_iterator(I); + CAI_Stalker *stalker = smart_cast(object); + if (!stalker) + continue; + + stalker->dbg_draw_visibility_rays (); + } + } +#endif +} + +void CLevel::OnEvent(EVENT E, u64 P1, u64 /**P2/**/) +{ + if (E==eEntitySpawn) { + char Name[128]; Name[0]=0; + sscanf (LPCSTR(P1),"%s", Name); + Level().g_cl_Spawn (Name,0xff, M_SPAWN_OBJECT_LOCAL, Fvector().set(0,0,0)); + } else if (E==eChangeRP && P1) { + } else if (E==eDemoPlay && P1) { + char* name = (char*)P1; + string_path RealName; + xr_strcpy (RealName,name); + xr_strcat (RealName,".xrdemo"); + Cameras().AddCamEffector(new CDemoPlay(RealName,1.3f,0)); + } else if (E==eChangeTrack && P1) { + // int id = atoi((char*)P1); + // Environment->Music_Play(id); + } else if (E==eEnvironment) { + // int id=0; float s=1; + // sscanf((char*)P1,"%d,%f",&id,&s); + // Environment->set_EnvMode(id,s); + } else return; +} + +void CLevel::AddObject_To_Objects4CrPr (CGameObject* pObj) +{ + if (!pObj) return; + for (OBJECTS_LIST_it OIt = pObjects4CrPr.begin(); OIt != pObjects4CrPr.end(); OIt++) + { + if (*OIt == pObj) return; + } + pObjects4CrPr.push_back(pObj); + +} +void CLevel::AddActor_To_Actors4CrPr (CGameObject* pActor) +{ + if (!pActor) return; + if (pActor->CLS_ID != CLSID_OBJECT_ACTOR) return; + for (OBJECTS_LIST_it AIt = pActors4CrPr.begin(); AIt != pActors4CrPr.end(); AIt++) + { + if (*AIt == pActor) return; + } + pActors4CrPr.push_back(pActor); +} + +void CLevel::RemoveObject_From_4CrPr (CGameObject* pObj) +{ + if (!pObj) return; + + OBJECTS_LIST_it OIt = std::find(pObjects4CrPr.begin(), pObjects4CrPr.end(), pObj); + if (OIt != pObjects4CrPr.end()) + { + pObjects4CrPr.erase(OIt); + } + + OBJECTS_LIST_it AIt = std::find(pActors4CrPr.begin(), pActors4CrPr.end(), pObj); + if (AIt != pActors4CrPr.end()) + { + pActors4CrPr.erase(AIt); + } +} + +void CLevel::make_NetCorrectionPrediction () +{ + m_bNeed_CrPr = false; + m_bIn_CrPr = true; + u64 NumPhSteps = ph_world->m_steps_num; + ph_world->m_steps_num -= m_dwNumSteps; + if(g_bDebugDumpPhysicsStep&&m_dwNumSteps>10) + { + Msg("!!!TOO MANY PHYSICS STEPS FOR CORRECTION PREDICTION = %d !!!",m_dwNumSteps); + m_dwNumSteps = 10; + }; +////////////////////////////////////////////////////////////////////////////////// + ph_world->Freeze(); + + //setting UpdateData and determining number of PH steps from last received update + for (OBJECTS_LIST_it OIt = pObjects4CrPr.begin(); OIt != pObjects4CrPr.end(); OIt++) + { + CGameObject* pObj = *OIt; + if (!pObj) continue; + pObj->PH_B_CrPr(); + }; +////////////////////////////////////////////////////////////////////////////////// + //first prediction from "delivered" to "real current" position + //making enought PH steps to calculate current objects position based on their updated state + + for (u32 i =0; iStep(); + + for (OBJECTS_LIST_it AIt = pActors4CrPr.begin(); AIt != pActors4CrPr.end(); AIt++) + { + CGameObject* pActor = *AIt; + if (!pActor || pActor->CrPr_IsActivated()) continue; + pActor->PH_B_CrPr(); + }; + }; +////////////////////////////////////////////////////////////////////////////////// + for (OBJECTS_LIST_it OIt = pObjects4CrPr.begin(); OIt != pObjects4CrPr.end(); OIt++) + { + CGameObject* pObj = *OIt; + if (!pObj) continue; + pObj->PH_I_CrPr(); + }; +////////////////////////////////////////////////////////////////////////////////// + if (!InterpolationDisabled()) + { + for (u32 i =0; iStep(); +#ifdef DEBUG +/* + for (OBJECTS_LIST_it OIt = pObjects4CrPr.begin(); OIt != pObjects4CrPr.end(); OIt++) + { + CGameObject* pObj = *OIt; + if (!pObj) continue; + pObj->PH_Ch_CrPr(); + }; +*/ +#endif + } + ////////////////////////////////////////////////////////////////////////////////// + for (OBJECTS_LIST_it OIt = pObjects4CrPr.begin(); OIt != pObjects4CrPr.end(); OIt++) + { + CGameObject* pObj = *OIt; + if (!pObj) continue; + pObj->PH_A_CrPr(); + }; + }; + ph_world->UnFreeze(); + + ph_world->m_steps_num = NumPhSteps; + m_dwNumSteps = 0; + m_bIn_CrPr = false; + + pObjects4CrPr.clear(); + pActors4CrPr.clear(); +}; + +u32 CLevel::GetInterpolationSteps () +{ + return lvInterpSteps; +}; + +void CLevel::UpdateDeltaUpd ( u32 LastTime ) +{ + u32 CurrentDelta = LastTime - m_dwLastNetUpdateTime; + if (CurrentDelta < m_dwDeltaUpdate) + CurrentDelta = iFloor(float(m_dwDeltaUpdate * 10 + CurrentDelta) / 11); + + m_dwLastNetUpdateTime = LastTime; + m_dwDeltaUpdate = CurrentDelta; + + if (0 == g_cl_lvInterp) ReculcInterpolationSteps(); + else + if (g_cl_lvInterp>0) + { + lvInterpSteps = iCeil(g_cl_lvInterp / fixed_step); + } +}; + +void CLevel::ReculcInterpolationSteps () +{ + lvInterpSteps = iFloor(float(m_dwDeltaUpdate) / (fixed_step*1000)); + if (lvInterpSteps > 60) lvInterpSteps = 60; + if (lvInterpSteps < 3) lvInterpSteps = 3; +}; + +bool CLevel::InterpolationDisabled () +{ + return g_cl_lvInterp < 0; +}; + +void CLevel::PhisStepsCallback ( u32 Time0, u32 Time1 ) +{ + if (!Level().game) return; + if (GameID() == GAME_SINGLE) return; + +//#pragma todo("Oles to all: highly inefficient and slow!!!") +//fixed (Andy) + /* + for (xr_vector::iterator O=Level().Objects.objects.begin(); O!=Level().Objects.objects.end(); ++O) + { + if( (*O)->CLS_ID == CLSID_OBJECT_ACTOR){ + CActor* pActor = smart_cast(*O); + if (!pActor || pActor->Remote()) continue; + pActor->UpdatePosStack(Time0, Time1); + } + }; + */ +}; + +void CLevel::SetNumCrSteps ( u32 NumSteps ) +{ + m_bNeed_CrPr = true; + if (m_dwNumSteps > NumSteps) return; + m_dwNumSteps = NumSteps; + if (m_dwNumSteps > 1000000) + { + VERIFY(0); + } +}; + + +ALife::_TIME_ID CLevel::GetGameTime() +{ + return (game->GetGameTime()); +// return (Server->game->GetGameTime()); +} + +ALife::_TIME_ID CLevel::GetEnvironmentGameTime() +{ + return (game->GetEnvironmentGameTime()); +// return (Server->game->GetGameTime()); +} + +// gr1ph start + +#include "game_sv_single.h" +#include "game_cl_single.h" + +void CLevel::SetGameTime(u32 new_hours, u32 new_mins) +{ + float time_factor = Level().GetGameTimeFactor(); + u32 year = 1, month = 0, day = 0, hours = 0, mins = 0, secs = 0, milisecs = 0; + split_time(Level().GetGameTime(), year, month, day, hours, mins, secs, milisecs); + u64 new_time = generate_time(year,month,day,new_hours,new_mins,secs,milisecs); + game_sv_Single* server_game = smart_cast(Level().Server->game); + game_cl_Single* client_game = smart_cast(Level().game); + server_game->SetGameTimeFactor(new_time, time_factor); + server_game->SetEnvironmentGameTimeFactor(new_time, time_factor); + client_game->SetEnvironmentGameTimeFactor(new_time, time_factor); + client_game->SetGameTimeFactor(new_time, time_factor); +} + +u8 CLevel::GetDayTime() +{ + u32 dummy32; + u32 hours; + GetGameDateTime(dummy32, dummy32, dummy32, hours, dummy32, dummy32, dummy32); + VERIFY (hours<256); + return u8(hours); +} + +float CLevel::GetGameDayTimeSec() +{ + return (float(s64(GetGameTime() % (24*60*60*1000)))/1000.f); +} + +u32 CLevel::GetGameDayTimeMS() +{ + return (u32(s64(GetGameTime() % (24*60*60*1000)))); +} + +float CLevel::GetEnvironmentGameDayTimeSec() +{ + return (float(s64(GetEnvironmentGameTime() % (24*60*60*1000)))/1000.f); +} + +void CLevel::GetGameDateTime (u32& year, u32& month, u32& day, u32& hours, u32& mins, u32& secs, u32& milisecs) +{ + split_time(GetGameTime(), year, month, day, hours, mins, secs, milisecs); +} + +void CLevel::GetGameTimeHour(u32& hours) +{ + u32 dummy1; + split_time(GetGameTime(), dummy1, dummy1, dummy1, hours, dummy1, dummy1, dummy1); +} + +void CLevel::GetGameTimeMinute(u32& minute) +{ + u32 dummy1; + split_time(GetGameTime(), dummy1, dummy1, dummy1, dummy1, minute, dummy1, dummy1); +} + +float CLevel::GetGameTimeFactor() +{ + return (game->GetGameTimeFactor()); +// return (Server->game->GetGameTimeFactor()); +} + +void CLevel::SetGameTimeFactor(const float fTimeFactor) +{ + game->SetGameTimeFactor(fTimeFactor); +// Server->game->SetGameTimeFactor(fTimeFactor); +} + +void CLevel::SetGameTimeFactor(ALife::_TIME_ID GameTime, const float fTimeFactor) +{ + game->SetGameTimeFactor(GameTime, fTimeFactor); +// Server->game->SetGameTimeFactor(fTimeFactor); +} +void CLevel::SetEnvironmentGameTimeFactor(u64 const& GameTime, float const& fTimeFactor) +{ + if (!game) + return; + + game->SetEnvironmentGameTimeFactor(GameTime, fTimeFactor); +// Server->game->SetGameTimeFactor(fTimeFactor); +}/* +void CLevel::SetGameTime(ALife::_TIME_ID GameTime) +{ + game->SetGameTime(GameTime); +// Server->game->SetGameTime(GameTime); +} +*/ +bool CLevel::IsServer () +{ +// return (!!Server); + if (IsDemoPlay()) + { + return IsServerDemo(); + }; + if (!Server) return false; + return (Server->GetClientsCount() != 0); + +} + +bool CLevel::IsClient () +{ +// return (!Server); + if (IsDemoPlay()) + { + return IsClientDemo(); + }; + if (!Server) return true; + return (Server->GetClientsCount() == 0); +} + +void CLevel::OnSessionTerminate (LPCSTR reason) +{ + MainMenu()->OnSessionTerminate(reason); +} + +u32 GameID() +{ + return GAME_SINGLE; +} + +#include "../IGame_Persistent.h" + +bool IsGameTypeSingle() +{ + return g_pGamePersistent->GameType()==GAME_SINGLE || g_pGamePersistent->GameType()==GAME_ANY; +} + +#ifdef BATTLEYE + +bool CLevel::TestLoadBEClient() +{ + return battleye_system.TestLoadClient(); +} + +#endif // BATTLEYE + +GlobalFeelTouch::GlobalFeelTouch() +{ +} + +GlobalFeelTouch::~GlobalFeelTouch() +{ +} + +struct delete_predicate_by_time : public std::binary_function +{ + bool operator () (Feel::Touch::DenyTouch const & left, DWORD const expire_time) const + { + if (left.Expire <= expire_time) + return true; + return false; + }; +}; +struct objects_ptrs_equal : public std::binary_function +{ + bool operator() (Feel::Touch::DenyTouch const & left, CObject const * const right) const + { + if (left.O == right) + return true; + return false; + } +}; + +void GlobalFeelTouch::update() +{ + //we ignore P and R arguments, we need just delete evaled denied objects... + xr_vector::iterator new_end = + std::remove_if(feel_touch_disable.begin(), feel_touch_disable.end(), + std::bind2nd(delete_predicate_by_time(), Device.dwTimeGlobal)); + feel_touch_disable.erase(new_end, feel_touch_disable.end()); +} + +bool GlobalFeelTouch::is_object_denied(CObject const * O) +{ + /*Fvector temp_vector; + feel_touch_update(temp_vector, 0.f);*/ + if (std::find_if(feel_touch_disable.begin(), feel_touch_disable.end(), + std::bind2nd(objects_ptrs_equal(), O)) == feel_touch_disable.end()) + { + return false; + } + return true; +} + +#include "../../xrNetServer/NET_AuthCheck.h" + +struct path_excluder_predicate +{ + explicit path_excluder_predicate(xr_auth_strings_t const * ignore) : + m_ignore(ignore) + { + } + bool xr_stdcall is_allow_include(LPCSTR path) + { + if (!m_ignore) + return true; + + return allow_to_include_path(*m_ignore, path); + } + xr_auth_strings_t const * m_ignore; +}; + +void CLevel::ReloadEnvironment() +{ + g_pGamePersistent->DestroyEnvironment(); + Msg("---Environment destroyed"); + Msg("---Start to destroy configs"); + CInifile** s = (CInifile**)(&pSettings); + xr_delete(*s); + xr_delete(pGameIni); + Msg("---Start to rescan configs"); + FS.get_path("$game_config$")->m_Flags.set(FS_Path::flNeedRescan, TRUE); + FS.get_path("$game_scripts$")->m_Flags.set(FS_Path::flNeedRescan, TRUE); + FS.rescan_pathes(); + + Msg("---Start to create configs"); + string_path fname; + FS.update_path(fname, "$game_config$", "system.ltx"); + Msg("---Updated path to system.ltx is %s", fname); + + pSettings = new CInifile(fname, TRUE); + CHECK_OR_EXIT(0 != pSettings->section_count(), make_string("Cannot find file %s.\nReinstalling application may fix this problem.", fname)); + + xr_auth_strings_t tmp_ignore_pathes; + xr_auth_strings_t tmp_check_pathes; + fill_auth_check_params(tmp_ignore_pathes, tmp_check_pathes); + + path_excluder_predicate tmp_excluder(&tmp_ignore_pathes); + CInifile::allow_include_func_t tmp_functor; + tmp_functor.bind(&tmp_excluder, &path_excluder_predicate::is_allow_include); + pSettingsAuth = new CInifile( + fname, + TRUE, + TRUE, + FALSE, + 0, + tmp_functor + ); + + FS.update_path(fname, "$game_config$", "game.ltx"); + pGameIni = new CInifile(fname, TRUE); + CHECK_OR_EXIT(0 != pGameIni->section_count(), make_string("Cannot find file %s.\nReinstalling application may fix this problem.", fname)); + + Msg("---Create environment"); + g_pGamePersistent->CreateEnvironment(); + + Msg("---Call level_weathers.restart_weather_manager"); + luabind::functor lua_function; + string256 fn; + xr_strcpy(fn, "level_weathers.restart_weather_manager"); + R_ASSERT2(ai().script_engine().functor(fn, lua_function), make_string("Can't find function %s", fn)); + lua_function(); + Msg("---Done"); +} + diff --git a/src/xrGameLA/Level.h b/src/xrGameLA/Level.h new file mode 100644 index 000000000..cee897be8 --- /dev/null +++ b/src/xrGameLA/Level.h @@ -0,0 +1,442 @@ +// Level.h: interface for the CLevel class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_LEVEL_H__38F63863_DB0C_494B_AFAB_C495876EC671__INCLUDED_) +#define AFX_LEVEL_H__38F63863_DB0C_494B_AFAB_C495876EC671__INCLUDED_ +#pragma once + +#include "../igame_level.h" +#include "../../xrNetServer/net_client.h" +#include "script_export_space.h" +#include "../StatGraph.h" +#include "xrMessages.h" +#include "alife_space.h" +#include "xrDebug.h" +#include "xrServer.h" +#include "battleye_system.h" + +class CHUDManager; +class CParticlesObject; +class xrServer; +class game_cl_GameState; +class NET_Queue_Event; +class CSE_Abstract; +class CSpaceRestrictionManager; +class CSeniorityHierarchyHolder; +class CClientSpawnManager; +class CGameObject; +class CAutosaveManager; +class CPHCommander; +class CLevelDebug; +class CLevelSoundManager; +class CFastEntityUpdater; +#ifdef DRENDER +class CDebugRenderer; +#endif + +extern float g_fov; + +const int maxRP = 64; +const int maxTeams = 32; + +//class CFogOfWar; +//class CFogOfWarMngr; +class CBulletManager; +class CMapManager; + +#include "../feel_touch.h" + +class GlobalFeelTouch : public Feel::Touch +{ +public: + GlobalFeelTouch(); + virtual ~GlobalFeelTouch(); + + void update (); + bool is_object_denied (CObject const * O); +}; + + +class CLevel : public IGame_Level, public IPureClient +{ + #include "Level_network_Demo.h" + void ClearAllObjects (); +private: +#ifdef DEBUG + bool m_bSynchronization; +#endif +protected: + typedef IGame_Level inherited; + + CLevelSoundManager *m_level_sound_manager; + + // movement restriction manager + CSpaceRestrictionManager *m_space_restriction_manager; + // seniority hierarchy holder + CSeniorityHierarchyHolder *m_seniority_hierarchy_holder; + // client spawn_manager + CClientSpawnManager *m_client_spawn_manager; + // autosave manager + CAutosaveManager *m_autosave_manager; +#ifdef DRENDER + // debug renderer + CDebugRenderer *m_debug_renderer; +#endif + + CPHCommander *m_ph_commander; + CPHCommander *m_ph_commander_scripts; + + CFastEntityUpdater *m_fast_updater; + + // level name + shared_str m_name; + // Local events + EVENT eChangeRP; + EVENT eDemoPlay; + EVENT eChangeTrack; + EVENT eEnvironment; + EVENT eEntitySpawn; + //--------------------------------------------- + CStatGraph *pStatGraphS; + u32 m_dwSPC; //SendedPacketsCount + u32 m_dwSPS; //SendedPacketsSize + CStatGraph *pStatGraphR; + u32 m_dwRPC; //ReceivedPacketsCount + u32 m_dwRPS; //ReceivedPacketsSize + //--------------------------------------------- + +public: +#ifdef DEBUG + // level debugger + CLevelDebug *m_level_debug; +#endif + +public: + ////////////// network //////////////////////// + u32 GetInterpolationSteps (); + void SetInterpolationSteps (u32 InterpSteps); + bool InterpolationDisabled (); + void ReculcInterpolationSteps(); + u32 GetNumCrSteps () const {return m_dwNumSteps; }; + void SetNumCrSteps ( u32 NumSteps ); + static void PhisStepsCallback ( u32 Time0, u32 Time1 ); + bool In_NetCorrectionPrediction () {return m_bIn_CrPr;}; + + virtual void OnMessage (void* data, u32 size); + virtual void OnInvalidHost (); + virtual void OnInvalidPassword (); + virtual void OnSessionFull (); + virtual void OnConnectRejected (); +private: + BOOL m_bNeed_CrPr; + u32 m_dwNumSteps; + bool m_bIn_CrPr; + + DEF_VECTOR (OBJECTS_LIST, CGameObject*); + + OBJECTS_LIST pObjects4CrPr; + OBJECTS_LIST pActors4CrPr; + + CObject* pCurrentControlEntity; + xrServer::EConnect m_connect_server_err; +public: + void AddObject_To_Objects4CrPr (CGameObject* pObj); + void AddActor_To_Actors4CrPr (CGameObject* pActor); + + void RemoveObject_From_4CrPr (CGameObject* pObj); + + CObject* CurrentControlEntity ( void ) const { return pCurrentControlEntity; } + void SetControlEntity ( CObject* O ) { pCurrentControlEntity=O; } +private: + + void make_NetCorrectionPrediction (); + + u32 m_dwDeltaUpdate ; + u32 m_dwLastNetUpdateTime; + void UpdateDeltaUpd ( u32 LastTime ); + void BlockCheatLoad () ; + + BOOL Connect2Server (LPCSTR options); +private: + bool m_bConnectResultReceived; + bool m_bConnectResult; + xr_string m_sConnectResult; +public: + void OnGameSpyChallenge (NET_Packet* P); + void OnBuildVersionChallenge (); + void OnConnectResult (NET_Packet* P); +public: + ////////////////////////////////////////////// + // static particles + DEFINE_VECTOR (CParticlesObject*,POVec,POIt); + POVec m_StaticParticles; + + game_cl_GameState *game; + BOOL m_bGameConfigStarted; + BOOL game_configured; + NET_Queue_Event *game_events; + xr_deque game_spawn_queue; + xrServer* Server; + GlobalFeelTouch m_feel_deny; + + +#ifdef BATTLEYE + BattlEyeSystem battleye_system; + virtual bool TestLoadBEClient(); +#endif // BATTLEYE + +private: + // preload sounds registry + DEFINE_MAP (shared_str,ref_sound,SoundRegistryMap,SoundRegistryMapIt); + SoundRegistryMap sound_registry; + +public: + void PrefetchSound (LPCSTR name); + + void SetGameTime (u32 new_hours, u32 new_mins); + +protected: + BOOL net_start_result_total; + BOOL connected_to_server; + + bool xr_stdcall net_start1 (); + bool xr_stdcall net_start2 (); + bool xr_stdcall net_start3 (); + bool xr_stdcall net_start4 (); + bool xr_stdcall net_start5 (); + bool xr_stdcall net_start6 (); + + bool xr_stdcall net_start_client1 (); + bool xr_stdcall net_start_client2 (); + bool xr_stdcall net_start_client3 (); + bool xr_stdcall net_start_client4 (); + bool xr_stdcall net_start_client5 (); + bool xr_stdcall net_start_client6 (); + + bool xr_stdcall net_start_finalizer (); + + void net_OnChangeSelfName (NET_Packet* P); + +public: + // sounds + xr_vector static_Sounds; + + // startup options + shared_str m_caServerOptions; + shared_str m_caClientOptions; + + // Starting/Loading + virtual BOOL net_Start ( LPCSTR op_server, LPCSTR op_client); + virtual void net_Load ( LPCSTR name ); + virtual void net_Save ( LPCSTR name ); + virtual void net_Stop ( ); + virtual BOOL net_Start_client ( LPCSTR name ); + virtual void net_Update ( ); + + + virtual BOOL Load_GameSpecific_Before( ); + virtual BOOL Load_GameSpecific_After ( ); + virtual void Load_GameSpecific_CFORM ( CDB::TRI* T, u32 count ); + + // Events + virtual void OnEvent ( EVENT E, u64 P1, u64 P2 ); + virtual void _BCL OnFrame ( void ); + virtual void OnRender ( ); + + //virtual shared_str OpenDemoFile (LPCSTR demo_file_name); + //virtual void net_StartPlayDemo (); + + void cl_Process_Event (u16 dest, u16 type, NET_Packet& P); + void cl_Process_Spawn (NET_Packet& P); + void ProcessGameEvents ( ); + void ProcessGameSpawns ( ); + + // Input + virtual void IR_OnKeyboardPress ( int btn ); + virtual void IR_OnKeyboardRelease ( int btn ); + virtual void IR_OnKeyboardHold ( int btn ); + virtual void IR_OnMousePress ( int btn ); + virtual void IR_OnMouseRelease ( int btn ); + virtual void IR_OnMouseHold ( int btn ); + virtual void IR_OnMouseMove ( int, int); + virtual void IR_OnMouseStop ( int, int); + virtual void IR_OnMouseWheel ( int direction); + virtual void IR_OnActivate (void); + + int get_RPID (LPCSTR name); + + + // Game + void InitializeClientGame (NET_Packet& P); + void ClientReceive (); + void ClientSend (); + void ClientSave (); + u32 Objects_net_Save (NET_Packet* _Packet, u32 start, u32 count); + virtual void Send (NET_Packet& P, u32 dwFlags=DPNSEND_GUARANTEED, u32 dwTimeout=0); + + void g_cl_Spawn (LPCSTR name, u8 rp, u16 flags, Fvector pos); // only ask server + void g_sv_Spawn (CSE_Abstract* E); // server reply/command spawning + + // Save/Load/State + void SLS_Load (LPCSTR name); // Game Load + void SLS_Default (); // Default/Editor Load + + IC CSpaceRestrictionManager &space_restriction_manager (); + IC CSeniorityHierarchyHolder &seniority_holder (); + IC CClientSpawnManager &client_spawn_manager (); + IC CAutosaveManager &autosave_manager (); +#ifdef DRENDER + IC CDebugRenderer &debug_renderer (); +#endif + void __stdcall script_gc (); // GC-cycle + + IC CPHCommander &ph_commander (); + IC CPHCommander &ph_commander_scripts (); + IC CFastEntityUpdater &fast_entity_updater (); + IC CLevelSoundManager &level_sound_manager (); + // C/D + CLevel(); + virtual ~CLevel(); + + //названияе текущего уровня + virtual shared_str name () const; + virtual void GetLevelInfo ( CServerInfo* si ); + + //Функция перезагрузки кофигоф и погоды + void ReloadEnvironment(); + + //gets the time from the game simulation + + //возвращает время в милисекундах относительно начала игры + ALife::_TIME_ID GetGameTime (); + //возвращает время для энвайронмента в милисекундах относительно начала игры + ALife::_TIME_ID GetEnvironmentGameTime (); + //игровое время в отформатированном виде + void GetGameDateTime (u32& year, u32& month, u32& day, u32& hours, u32& mins, u32& secs, u32& milisecs); + void GetGameTimeHour (u32& hours); + void GetGameTimeMinute (u32& minute); + + float GetGameTimeFactor (); + void SetGameTimeFactor (const float fTimeFactor); + void SetGameTimeFactor (ALife::_TIME_ID GameTime, const float fTimeFactor); + virtual void SetEnvironmentGameTimeFactor (u64 const& GameTime, float const& fTimeFactor); +// void SetEnvironmentGameTimeFactor (ALife::_TIME_ID GameTime, const float fTimeFactor); +// void SetGameTime (ALife::_TIME_ID GameTime); + + // gets current daytime [0..23] + u8 GetDayTime (); + u32 GetGameDayTimeMS (); + float GetGameDayTimeSec (); + float GetEnvironmentGameDayTimeSec(); + +protected: +// CFogOfWarMngr *m_pFogOfWarMngr; +protected: + CMapManager * m_map_manager; +public: + CMapManager& MapManager () {return *m_map_manager;} +// CFogOfWarMngr& FogOfWarMngr () {return *m_pFogOfWarMngr;} + + //работа с пулями +protected: + CBulletManager* m_pBulletManager; +public: + IC CBulletManager& BulletManager() {return *m_pBulletManager;} + + //by Mad Max + bool IsServer (); + bool IsClient (); + CSE_Abstract *spawn_item (LPCSTR section, const Fvector &position, u32 level_vertex_id, u16 parent_id, bool return_item = false); + +protected: + u32 m_dwCL_PingDeltaSend; + u32 m_dwCL_PingLastSendTime; + u32 m_dwRealPing; +public: + virtual u32 GetRealPing () { return m_dwRealPing; }; + +public: + void remove_objects (); + virtual void OnSessionTerminate (LPCSTR reason); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CLevel) +#undef script_type_list +#define script_type_list save_type_list(CLevel) + +IC CLevel& Level() { return *((CLevel*) g_pGameLevel); } +IC game_cl_GameState& Game() { return *Level().game; } + u32 GameID(); + +#ifdef DEBUG +IC CLevelDebug& DBG() {return *((CLevelDebug*)Level().m_level_debug);} +#endif + + +IC CSpaceRestrictionManager &CLevel::space_restriction_manager() +{ + VERIFY (m_space_restriction_manager); + return (*m_space_restriction_manager); +} + +IC CSeniorityHierarchyHolder &CLevel::seniority_holder() +{ + VERIFY (m_seniority_hierarchy_holder); + return (*m_seniority_hierarchy_holder); +} + +IC CClientSpawnManager &CLevel::client_spawn_manager() +{ + VERIFY (m_client_spawn_manager); + return (*m_client_spawn_manager); +} + +IC CAutosaveManager &CLevel::autosave_manager() +{ + VERIFY (m_autosave_manager); + return (*m_autosave_manager); +} +#ifdef DRENDER +IC CDebugRenderer &CLevel::debug_renderer() +{ + VERIFY (m_debug_renderer); + return (*m_debug_renderer); +} +#endif //DRENDER +IC CPHCommander & CLevel::ph_commander() +{ + VERIFY(m_ph_commander); + return *m_ph_commander; +} +IC CPHCommander & CLevel::ph_commander_scripts() +{ + VERIFY(m_ph_commander_scripts); + return *m_ph_commander_scripts; +} + +IC CLevelSoundManager &CLevel::level_sound_manager() +{ + VERIFY(m_level_sound_manager); + return *m_level_sound_manager; +} + +IC CFastEntityUpdater &CLevel::fast_entity_updater() +{ + VERIFY(m_fast_updater); + return *m_fast_updater; +} + +//by Mad Max +IC bool OnServer() { return Level().IsServer();} +IC bool OnClient() { return Level().IsClient();} + + bool IsGameTypeSingle(); + +class CPHWorld; +extern CPHWorld* ph_world; +extern BOOL g_bDebugEvents; + + +#endif // !defined(AFX_LEVEL_H__38F63863_DB0C_494B_AFAB_C495876EC671__INCLUDED_) diff --git a/src/xrGameLA/LevelFogOfWar.cpp b/src/xrGameLA/LevelFogOfWar.cpp new file mode 100644 index 000000000..753cb9e1e --- /dev/null +++ b/src/xrGameLA/LevelFogOfWar.cpp @@ -0,0 +1,275 @@ +// LevelFogOfWar.cpp: для карты уровня в одиночном режиме игры +// +////////////////////////////////////////////////////////////////////// +#include "stdafx.h" +/** +#include "stdafx.h" +#include "LevelFogOfWar.h" +#include "level.h" +#include "alife_registry_wrappers.h" +#include "ui/UIMap.h" +#include "game_base_space.h" + +#define FOG_CELL_SZ (50.0f) + +struct FindFogByLevelName{ + shared_str level_name; + FindFogByLevelName(const shared_str& name){level_name = name;} + bool operator () (const CLevelFogOfWar& fog){ + return (level_name==fog.m_level_name); + } +}; + +CFogOfWarMngr::CFogOfWarMngr() +{ + + m_fogOfWarRegistry = new CFogOfWarWrapper(); + m_fogOfWarRegistry->registry().init(0); + +} + +CFogOfWarMngr::~CFogOfWarMngr() +{ + xr_delete(m_fogOfWarRegistry); +} +CLevelFogOfWar* CFogOfWarMngr::GetFogOfWar (const shared_str& level_name) +{ + + if(GameID()!=GAME_SINGLE) return NULL; + FOG_STORAGE_IT it = std::find_if(GetFogStorage().begin(),GetFogStorage().end(),FindFogByLevelName(level_name)); + if(it!=GetFogStorage().end())return &(*it); + else { //create new or load... + GetFogStorage().resize(GetFogStorage().size()+1); + CLevelFogOfWar& F = GetFogStorage().back(); + F.Init(level_name); + return &F; + }; + +} + +FOG_STORAGE_T& CFogOfWarMngr::GetFogStorage() +{ + + VERIFY(m_fogOfWarRegistry); + return m_fogOfWarRegistry->registry().objects(); + +}; + +CLevelFogOfWar::CLevelFogOfWar():m_rowNum(0), m_colNum(0) +{ + m_levelRect.set (0.0f,0.0f,0.0f,0.0f); + hShader.create ("hud\\default","ui\\ui_fog_of_war"); + hGeom.create (FVF::F_TL, RCache.Vertex.Buffer(), 0); +}; + + +void CLevelFogOfWar::Init (const shared_str& level) +{ + + m_level_name = level; + + + CInifile& gameLtx = *pGameIni; + + Fvector4 tmp; + if( gameLtx.line_exist(m_level_name,"bound_rect") ) + tmp = gameLtx.r_fvector4(m_level_name,"bound_rect"); + else + tmp.set (-10000.0f,-10000.0f,10000.0f,10000.0f); //. hack + + m_levelRect.set (tmp.x, tmp.y, tmp.z, tmp.w); + + + m_rowNum = iFloor((m_levelRect.height()+ FOG_CELL_SZ*0.5f)/FOG_CELL_SZ); + m_colNum = iFloor((m_levelRect.width()+ FOG_CELL_SZ*0.5f)/FOG_CELL_SZ); + + m_cells.resize(m_rowNum*m_colNum, false); + + hShader.create ("hud\\fog_of_war","ui\\ui_fog_of_war"); + hGeom.create (FVF::F_TL, RCache.Vertex.Buffer(), 0); + +} + +#define FOG_OPEN_RADIUS (FOG_CELL_SZ/4) + +void CLevelFogOfWar::Open (Fvector2 pos) +{ + if(!m_rowNum ||!m_rowNum) return; //invalid map + if(!(pos.x>=m_levelRect.lt.x && pos.y>=m_levelRect.lt.y && pos.x<=m_levelRect.rb.x && pos.y<=m_levelRect.rb.y)) return; //invalid position + VERIFY2((pos.x>=m_levelRect.lt.x && pos.y>=m_levelRect.lt.y && pos.x<=m_levelRect.rb.x && pos.y<=m_levelRect.rb.y),"invalid position for opening FogOfWar map cell" ); + + int col = iFloor( (pos.x - m_levelRect.lt.x)/FOG_CELL_SZ); + int row = iFloor( (m_levelRect.height() - (pos.y - m_levelRect.lt.y))/FOG_CELL_SZ); + + int cell_sz = iCeil (FOG_OPEN_RADIUS/FOG_CELL_SZ); + + Frect tgt,cell; + tgt.lt.x = pos.x-FOG_OPEN_RADIUS; + tgt.lt.y = pos.y-FOG_OPEN_RADIUS; + tgt.rb.x = pos.x+FOG_OPEN_RADIUS; + tgt.rb.y = pos.y+FOG_OPEN_RADIUS; + + for (int rr=row-cell_sz; rr<=row+cell_sz; ++rr){ + if (rr<0) continue; + for (int cc=col-cell_sz; cc<=col+cell_sz; ++cc){ + if (cc<0)continue; + cell.lt.x = m_levelRect.lt.x+cc*FOG_CELL_SZ; + cell.rb.y = m_levelRect.lt.y+m_levelRect.height()-rr*FOG_CELL_SZ; + cell.rb.x = m_levelRect.lt.x+cc*FOG_CELL_SZ+FOG_CELL_SZ; + cell.lt.y = m_levelRect.lt.y+m_levelRect.height()-rr*FOG_CELL_SZ-FOG_CELL_SZ; + if (tgt.intersected(cell)) Open(rr,cc,true); + } + } +} + +void CLevelFogOfWar::Open (u32 row, u32 col, bool b) +{ + if(row>=m_rowNum || col>=m_colNum) return; + + m_cells.at(row*m_colNum+col)=b; +} + +Ivector2 CLevelFogOfWar::ConvertRealToLocal(const Fvector2& src) +{ + Ivector2 res; + res.x = iFloor( (src.x - m_levelRect.lt.x)/FOG_CELL_SZ); + res.y = iFloor( (src.y - m_levelRect.lt.y)/FOG_CELL_SZ); + + return res; +} + +Irect CLevelFogOfWar::ConvertRealToLocal (const Frect& src) +{ + return Irect().set(ConvertRealToLocal(src.lt), ConvertRealToLocal(src.rb)); +} + +Fvector2 CLevelFogOfWar::ConvertLocalToReal(const Ivector2& src) +{ + return Fvector2().set( m_levelRect.lt.x + src.x*FOG_CELL_SZ, + m_levelRect.lt.y + src.y*FOG_CELL_SZ) ; +} + +enum { + tfLT = 0, + tfRT, + tfLB, + tfRB +}; + +void CLevelFogOfWar::GetTexUVLT(Fvector2& uv, u32 col, u32 row) +{ + + if (row>=m_rowNum || col>=m_colNum) uv.set(0.5f,0.0f); + else{ + bool cell_mask = m_cells[row*m_colNum+col]; + if (cell_mask==true) uv.set(0.0f,0.0f); + else uv.set(0.5f,0.0f); + } + +} + +void CLevelFogOfWar::Draw () +{ + + CUICustomMap* m = ((CUICustomMap*)(GetParent())); + + Frect tgt; + Frect clip_rect = m->GetClipperRect(); + Frect _r; m->GetAbsoluteRect(_r); + BOOL intersected = clip_rect.intersection(clip_rect,_r); + VERIFY (intersected); + Fvector2 map_abs_pos; m->GetAbsolutePos(map_abs_pos); + + Frect vis_rect; + vis_rect.set( clip_rect.lt.x-map_abs_pos.x, + clip_rect.lt.y-map_abs_pos.y, + clip_rect.rb.x-map_abs_pos.x, + clip_rect.rb.y-map_abs_pos.y);// vis_rect now in pixels + + tgt.set(float(vis_rect.x1), float(vis_rect.y1), float(vis_rect.x2), float(vis_rect.y2) ); + tgt.div(m->GetCurrentZoom(), m->GetCurrentZoom()); + tgt.add(m_levelRect.lt.x, m_levelRect.lt.y); + tgt.rb.add(Fvector2().set(FOG_CELL_SZ-EPS_L,FOG_CELL_SZ-EPS_L)); + Irect cells = ConvertRealToLocal(tgt); + + Fvector2 realCellsPosLT = ConvertLocalToReal(cells.lt); + + realCellsPosLT.sub (m_levelRect.lt).mul(m->GetCurrentZoom()); + + Ivector2 drawLT; + drawLT.set ((realCellsPosLT.x + map_abs_pos.x)*UI()->GetScaleX(), + (realCellsPosLT.y + map_abs_pos.y)*UI()->GetScaleY()); + + + const Fvector2 pts[6] = {{0.0f,0.0f},{1.0f,0.0f},{1.0f,1.0f}, + {0.0f,0.0f},{1.0f,1.0f},{0.0f,1.0f}}; + + const Fvector2 uvs[6] = {{0.0f,0.0f},{0.5f,0.0f},{0.5f,1.0f}, + {0.0f,0.0f},{0.5f,1.0f},{0.0f,1.0f}}; + + // calculate cell size in screen pixels + float fw = FOG_CELL_SZ*m->GetCurrentZoom()*UI()->GetScaleX(); + float fh = FOG_CELL_SZ*m->GetCurrentZoom()*UI()->GetScaleY(); + + // fill cell buffer + u32 vOffset = 0; + FVF::TL* start_pv = (FVF::TL*)RCache.Vertex.Lock (cells.width()*cells.height()*6,hGeom.stride(),vOffset); + FVF::TL* pv = start_pv; + for (int x=0; xset (iFloor(drawLT.x + p.x*(fw) +fw*x)-0.5f, + iFloor(drawLT.y + p.y*(fh) +fh*y)-0.5f, + 0xFFFFFFFF,tp.x+uv.x,tp.y+uv.y); + } + } + } + std::ptrdiff_t p_cnt = (pv-start_pv)/3; + RCache.Vertex.Unlock (u32(pv-start_pv),hGeom.stride()); + + // set scissor + UI()->PushScissor (clip_rect); + if (p_cnt!=0){ + // draw + RCache.set_Shader (hShader); + RCache.set_Geometry (hGeom); + RCache.Render (D3DPT_TRIANGLELIST,vOffset,u32(p_cnt)); + } + UI()->PopScissor (); + +} + +void CLevelFogOfWar::save (IWriter &stream) +{ + + save_data (m_level_name,stream); + save_data (m_levelRect,stream); + save_data (m_rowNum,stream); + save_data (m_colNum,stream); + save_data (m_cells,stream); + +} + +void CLevelFogOfWar::load (IReader &stream) +{ + + load_data (m_level_name,stream); + Init (m_level_name); + + Frect levelRect; + load_data (levelRect,stream); + VERIFY (m_levelRect.cmp(levelRect)); + + u32 rowNum, colNum; + load_data (rowNum,stream); + load_data (colNum,stream); + VERIFY (rowNum == m_rowNum); + VERIFY (colNum == m_colNum); + load_data (m_cells,stream); + +} +**/ \ No newline at end of file diff --git a/src/xrGameLA/LevelFogOfWar.h b/src/xrGameLA/LevelFogOfWar.h new file mode 100644 index 000000000..6ef658c0d --- /dev/null +++ b/src/xrGameLA/LevelFogOfWar.h @@ -0,0 +1,56 @@ +// LevelFogOfWar.h: для карты уровня в одиночном режиме игры +// +////////////////////////////////////////////////////////////////////// + +#pragma once +/* +#include "UI/UIWindow.h" +#include "alife_abstract_registry.h" + +class CUICustomMap; +class CFogOfWarWrapper; + +class CLevelFogOfWar: public IPureSerializeObject, public CUIWindow +{ +public: + ref_shader hShader; + ref_geom hGeom; + + shared_str m_level_name; + Frect m_levelRect; + u32 m_rowNum, m_colNum; + xr_vector m_cells; + +public: + CLevelFogOfWar (); + + virtual void Draw (); + + void Open (Fvector2 pos); + void Open (u32 row, u32 col, bool mask); + void Init (const shared_str& level); + void GetTexUVLT (Fvector2& frame, u32 col, u32 row); + Ivector2 ConvertRealToLocal (const Fvector2& src); + Irect ConvertRealToLocal (const Frect& src); + Fvector2 ConvertLocalToReal (const Ivector2& src); + + virtual void save (IWriter &stream); + virtual void load (IReader &stream); +}; + + +DEFINE_VECTOR(CLevelFogOfWar,FOG_STORAGE_T,FOG_STORAGE_IT); + +typedef CALifeAbstractRegistry CFogOfWarRegistry; + + +class CFogOfWarMngr +{ + CFogOfWarWrapper* m_fogOfWarRegistry; + FOG_STORAGE_T& GetFogStorage(); +public: + CFogOfWarMngr (); + virtual ~CFogOfWarMngr (); + CLevelFogOfWar* GetFogOfWar (const shared_str& level_name); +}; +*/ \ No newline at end of file diff --git a/src/xrGameLA/LevelGameDef.cpp b/src/xrGameLA/LevelGameDef.cpp new file mode 100644 index 000000000..db1f90eaa --- /dev/null +++ b/src/xrGameLA/LevelGameDef.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#pragma hdrstop + +#include "LevelGameDef.h" + +xr_token rpoint_type[]={ + { "Actor Spawn", rptActorSpawn }, + { "Artefact Spawn", rptArtefactSpawn }, + { "TeamBase Particle", rptTeamBaseParticle }, + { 0, 0 } +}; + +xr_token rpoint_game_type[]={ + { "Any game", rpgtGameAny }, + { 0, 0 } +}; + \ No newline at end of file diff --git a/src/xrGameLA/LevelGameDef.h b/src/xrGameLA/LevelGameDef.h new file mode 100644 index 000000000..37f5dd505 --- /dev/null +++ b/src/xrGameLA/LevelGameDef.h @@ -0,0 +1,196 @@ +//--------------------------------------------------------------------------- +#ifndef LevelGameDefH +#define LevelGameDefH + +#define RPOINT_CHOOSE_NAME "$rpoint" +#define ENVMOD_CHOOSE_NAME "$env_mod" +#define NPCPOINT_CHOOSE_NAME "$npcpoint" + +enum EPointType{ + ptRPoint=0, + ptEnvMod, + ptSpawnPoint, + ptMaxType, + pt_force_dword=u32(-1) +}; + +enum EWayType{ + wtPatrolPath=0, + wtMaxType, + wt_force_dword=u32(-1) +}; + +enum ERPpointType{ // [0..255] + rptActorSpawn = 0, + rptArtefactSpawn , + rptTeamBaseParticle, +}; + +enum EEnvModUsedParams{ eViewDist =(1<<0), + eFogColor =(1<<1), + eFogDensity =(1<<2), + eAmbientColor =(1<<3), + eSkyColor =(1<<4), + eHemiColor =(1<<5) +}; + +enum ERPGameType{ // [0..255] + rpgtGameAny = 0, +}; +extern ECORE_API xr_token rpoint_type[]; +extern ECORE_API xr_token rpoint_game_type[]; + +// BASE offset +#define WAY_BASE 0x1000 +#define POINT_BASE 0x2000 + +// POINT chunks +#define RPOINT_CHUNK POINT_BASE+ptRPoint + +// WAY chunks +#define WAY_PATROLPATH_CHUNK WAY_BASE+wtPatrolPath +//---------------------------------------------------- + +#define WAYOBJECT_VERSION 0x0013 +//---------------------------------------------------- +#define WAYOBJECT_CHUNK_VERSION 0x0001 +#define WAYOBJECT_CHUNK_POINTS 0x0002 +#define WAYOBJECT_CHUNK_LINKS 0x0003 +#define WAYOBJECT_CHUNK_TYPE 0x0004 +#define WAYOBJECT_CHUNK_NAME 0x0005 + +#define NPC_POINT_VERSION 0x0001 +//---------------------------------------------------- +#define NPC_POINT_CHUNK_VERSION 0x0001 +#define NPC_POINT_CHUNK_DATA 0x0002 +//---------------------------------------------------- +/* +- chunk RPOINT_CHUNK + - chunk #0 + vector3 (PPosition); + vector3 (PRotation); + u8 (team_id); + u8 (type) + u16 (reserved) + ... + - chunk #n + +- chunk WAY_PATH_CHUNK + - chunk #0 + chunk WAYOBJECT_CHUNK_VERSION + word (version) + chunk WAYOBJECT_CHUNK_NAME + stringZ (Name) + chunk WAY_CHUNK_TYPE + dword EWayType (type) + chunk WAY_CHUNK_POINTS + word (count) + for (i=0; i 0.f); + + start_position = position; + start_velocity.mul (direction, speed); + born_time = Device.dwTimeGlobal; + life_time = 0.f; + + VERIFY (direction.magnitude() > 0.f); + dir.normalize (direction); + + hit_param.power = power * cartridge.param_s.kHit; + hit_param.impulse = impulse * cartridge.param_s.kImpulse; + + max_dist = maximum_distance * cartridge.param_s.kDist; + fly_dist = 0; + tracer_start_position = bullet_pos; + + parent_id = sender_id; + flags.allow_sendhit = SendHit; + weapon_id = sendersweapon_id; + hit_type = e_hit_type; + + armor_piercing = cartridge.param_s.kAP; + air_resistance = cartridge.param_s.kAirRes*air_resistance_factor; + wallmark_size = cartridge.param_s.fWallmarkSize; + m_u8ColorID = cartridge.param_s.u8ColorID; + + bullet_material_idx = cartridge.bullet_material_idx; + VERIFY ( u16(-1) != bullet_material_idx ); + + flags.allow_tracer = !!cartridge.m_flags.test(CCartridge::cfTracer); + flags.allow_ricochet = !!cartridge.m_flags.test(CCartridge::cfRicochet); + flags.explosive = !!cartridge.m_flags.test(CCartridge::cfExplosive); + flags.magnetic_beam = !!cartridge.m_flags.test(CCartridge::cfMagneticBeam); +// flags.skipped_frame = 0; + + init_frame_num = Device.dwFrame; + + targetID = 0; + density_mode = 0; +} + +////////////////////////////////////////////////////////// +// + +CBulletManager::CBulletManager() +#if 0//def PROFILE_CRITICAL_SECTIONS + : m_Lock(MUTEX_PROFILE_ID(CBulletManager)) +# ifdef DEBUG + ,m_thread_id(GetCurrentThreadId()) +# endif // #ifdef DEBUG +#else // #ifdef PROFILE_CRITICAL_SECTIONS +# ifdef DEBUG + : m_thread_id(GetCurrentThreadId()) +# endif // #ifdef DEBUG +#endif // #ifdef PROFILE_CRITICAL_SECTIONS +{ + m_Bullets.clear (); + m_Bullets.reserve (100); +} + +CBulletManager::~CBulletManager() +{ + m_Bullets.clear (); + m_WhineSounds.clear (); + m_Events.clear (); +} + +void CBulletManager::Load () +{ + char const * bullet_manager_sect = "bullet_manager"; + + m_fTracerWidth = pSettings->r_float(bullet_manager_sect, "tracer_width"); + m_fTracerLengthMax = pSettings->r_float(bullet_manager_sect, "tracer_length_max"); + m_fTracerLengthMin = pSettings->r_float(bullet_manager_sect, "tracer_length_min"); + + m_fGravityConst = pSettings->r_float(bullet_manager_sect, "gravity_const"); + m_fAirResistanceK = pSettings->r_float(bullet_manager_sect, "air_resistance_k"); + + m_fMinBulletSpeed = pSettings->r_float(bullet_manager_sect, "min_bullet_speed"); + m_fCollisionEnergyMin = pSettings->r_float(bullet_manager_sect, "collision_energy_min"); + m_fCollisionEnergyMax = pSettings->r_float(bullet_manager_sect, "collision_energy_max"); + + m_fHPMaxDist = pSettings->r_float(bullet_manager_sect, "hit_probability_max_dist"); + + if (pSettings->line_exist(bullet_manager_sect, "bullet_velocity_time_factor")) + { + g_bullet_time_factor = pSettings->r_float(bullet_manager_sect, "bullet_velocity_time_factor"); + } + + + LPCSTR whine_sounds = pSettings->r_string(bullet_manager_sect, "whine_sounds"); + int cnt = _GetItemCount(whine_sounds); + xr_string tmp; + for (int k=0; kr_string(bullet_manager_sect, "explode_particles"); + cnt = _GetItemCount(explode_particles); + for (int k=0; kUpdateParent (xf,zero_vel); + GamePersistent().ps_needtoplay.push_back(ps); +} + +void CBulletManager::PlayWhineSound(SBullet* bullet, CObject* object, const Fvector& pos) +{ + if (m_WhineSounds.empty()) return; + if (bullet->m_whine_snd._feedback() != NULL) return; + if(bullet->hit_type!=ALife::eHitTypeFireWound ) return; + + bullet->m_whine_snd = m_WhineSounds[Random.randI(0, m_WhineSounds.size())]; + bullet->m_whine_snd.play_at_pos (object,pos); +} + +void CBulletManager::Clear () +{ + m_Bullets.clear (); + m_Events.clear (); +} + +void CBulletManager::AddBullet(const Fvector& position, + const Fvector& direction, + float starting_speed, + float power, +//. float power_critical, + float impulse, + u16 sender_id, + u16 sendersweapon_id, + ALife::EHitType e_hit_type, + float maximum_distance, + const CCartridge& cartridge, + float const air_resistance_factor, + bool SendHit, + bool AimBullet) +{ + VERIFY ( m_thread_id == GetCurrentThreadId() ); + + VERIFY (u16(-1)!=cartridge.bullet_material_idx); +// u32 CurID = Level().CurrentControlEntity()->ID(); +// u32 OwnerID = sender_id; + m_Bullets.push_back (SBullet()); + SBullet& bullet = m_Bullets.back(); + bullet.Init (position, direction, starting_speed, power, /*power_critical,*/ impulse, sender_id, sendersweapon_id, e_hit_type, maximum_distance, cartridge, air_resistance_factor, SendHit); +// bullet.frame_num = Device.dwFrame; + bullet.flags.aim_bullet = AimBullet; + if (!IsGameTypeSingle()) + { + if (SendHit) + Game().m_WeaponUsageStatistic->OnBullet_Fire(&bullet, cartridge); + // game_cl_mp* tmp_cl_game = smart_cast(&Game()); + // if (tmp_cl_game->get_reward_generator()) + // tmp_cl_game->get_reward_generator()->OnBullet_Fire(sender_id, sendersweapon_id, position, direction); + } + +} + +void CBulletManager::UpdateWorkload() +{ +// VERIFY ( m_thread_id == GetCurrentThreadId() ); + + rq_storage.r_clear (); + + u32 const time_delta = Device.dwTimeDelta; + if (!time_delta) + return; + + collide::rq_result dummy; + + // this is because of ugly nature of removing bullets + // when index in vector passed through the tgt_material field + // and we can remove them only in case when we iterate bullets + // in the reversed order + BulletVec::reverse_iterator i = m_Bullets.rbegin(); + BulletVec::reverse_iterator e = m_Bullets.rend(); + for (u16 j=u16(e - i); i != e; ++i, --j) { + if ( process_bullet( rq_storage, *i, u32(time_delta*g_bullet_time_factor)) ) + continue; + + VERIFY (j > 0); + RegisterEvent (EVENT_REMOVE, FALSE, &*i, Fvector().set(0, 0, 0), dummy, j - 1); + } +} + +static Fvector parabolic_velocity ( + Fvector const& start_velocity, + Fvector const& gravity, + float const air_resistance, + float const time + ) +{ + return ( + Fvector(start_velocity).mul( + _max( 0.f, 1.f - air_resistance*time) + ).mad( + gravity, + time + ) + ); +} + +static Fvector trajectory_velocity ( + Fvector const& start_velocity, + Fvector const& gravity, + float const air_resistance, + float const time + ) +{ + float const parabolic_time = _max( 0.f, 2.f/air_resistance - air_resistance_epsilon); + float const fall_down_time = time - parabolic_time; +// float const fake_velocity = start_velocity*2.f; + if ( fall_down_time < 0.f ) { + Fvector const xz_velocity = Fvector().set( start_velocity.x, 0.f, start_velocity.z); + // this could be since we could fire in different directions + // for example, vertically into the ground + if ( !fis_zero(xz_velocity.square_magnitude()) ) { + return ( + parabolic_velocity( + start_velocity, + gravity, + air_resistance, + time + ) + ); + } + + // this fake since our formula doesn't take into account + // directions correctly + return ( + Fvector(start_velocity).mad( + gravity, + time + ) + ); + } + + Fvector parabolic_velocity = + ::parabolic_velocity( + start_velocity, + gravity, + air_resistance, + parabolic_time + ); + + VERIFY (!fis_zero(air_resistance_epsilon) || fis_zero(_sqr(parabolic_velocity.x) + _sqr(parabolic_velocity.z), EPS_L) ); + return ( + parabolic_velocity.mad( + gravity, + fall_down_time + ) + ); +} + +static Fvector parabolic_position ( + Fvector const& start_position, + Fvector const& start_velocity, + Fvector const& gravity, + float const air_resistance, + float const time + ) +{ + float const sqr_t_div_2 = _sqr(time)*.5f; + return ( + Fvector().mad( + start_position, + start_velocity, + time + ).mad( + Fvector(start_velocity).mul(-air_resistance), + sqr_t_div_2 + ).mad( + gravity, + sqr_t_div_2 + ) + ); +} + +//BOOL g_use_new_ballistics = 0; + +float bullet_time_factor = 1.f; + + +static Fvector trajectory_position ( + Fvector const& start_position, + Fvector const& base_start_velocity, + Fvector const& base_gravity, + float base_air_resistance, + float const base_time + ) +{ + Fvector const & gravity = base_gravity;//g_use_new_ballistics ? Fvector(base_gravity).mul(_sqr(factor)) : base_gravity; + float const & air_resistance = base_air_resistance;//g_use_new_ballistics ? base_air_resistance*factor : base_air_resistance; + Fvector const & start_velocity = base_start_velocity;//g_use_new_ballistics ? Fvector(base_start_velocity).mul( factor ) : base_start_velocity; + float const time = base_time; + + float const parabolic_time = _max( 0.f, 1.f/air_resistance - air_resistance_epsilon); + float const fall_down_time = time - parabolic_time; + if ( fall_down_time < 0.f ) { + Fvector const xz_velocity = Fvector().set( start_velocity.x, 0.f, start_velocity.z); + if ( !fis_zero(xz_velocity.square_magnitude()) ) + return ( + parabolic_position( + start_position, + start_velocity, + gravity, + air_resistance, + time + ) + ); + + return ( + Fvector(start_position).mad ( + start_velocity, + time + ).mad( + gravity, + _sqr(time)*.5f + ) + ); + } + + Fvector const parabolic_position = + ::parabolic_position( + start_position, + start_velocity, + gravity, + air_resistance, + parabolic_time + ); + Fvector const parabolic_velocity = + ::parabolic_velocity( + start_velocity, + gravity, + air_resistance, + parabolic_time + ); + return ( + Fvector(parabolic_position).mad ( + parabolic_velocity, + fall_down_time + ).mad( + gravity, + _sqr(fall_down_time)*.5f + ) + ); +} + +inline static float trajectory_max_error_time ( + float const t0, + float const t1 + ) +{ + return ( (t1 + t0)*.5f ); + // this is correct even in our case + // y(t) = V0y*t - V0y*ar*t^2/2 - g*t^2/2 + // x(t) = V0x*t - V0x*ar*t^2/2 +} + +static float trajectory_pick_error ( + float const low, + float const high, + Fvector const& position, + Fvector const& velocity, + Fvector const& gravity, + float const air_resistance + ) +{ + float max_error_time = trajectory_max_error_time(low, high); + + Fvector const start = trajectory_position(position, velocity, gravity, air_resistance, low); + Fvector const target = trajectory_position(position, velocity, gravity, air_resistance, high); + Fvector const max_error = trajectory_position(position, velocity, gravity, air_resistance, max_error_time); + + Fvector start_to_max_error = Fvector().sub(max_error,start); + float magnitude = start_to_max_error.magnitude(); + start_to_max_error.mul (1.f/magnitude); + Fvector start_to_target = Fvector().sub(target,start).normalize(); + float cosine_alpha = _max(-1.f, _min(start_to_max_error.dotproduct(start_to_target), 1.f)); + float sine_alpha = _sqrt(1.f - _sqr(cosine_alpha)); + return (magnitude*sine_alpha); +} + +static float trajectory_select_pick_gravity ( + SBullet& bullet, + float start_low, + float const high, + Fvector const& gravity, + float const air_resistance + ) +{ + float const max_test_distance = bullet.max_dist - bullet.fly_dist; + float const time_delta = high - start_low; + float const time_to_fly = Fvector(bullet.start_velocity).mul(time_delta).mad(gravity, _sqr(time_delta)*.5f).magnitude(); + if (time_to_fly <= max_test_distance) + return (high); + + float const fall_down_velocity_magnitude = bullet.speed; + float const positive_gravity = -gravity.y; + float time = ( + _sqrt( _sqr(fall_down_velocity_magnitude) + 2.f*max_test_distance*positive_gravity ) - + fall_down_velocity_magnitude + )/positive_gravity; + VERIFY (time >= 0.f); + + VERIFY (high >= start_low); + float result = start_low + time; + clamp (result, start_low, high); + VERIFY2 ( result <= high, make_string("result[%f], high[%f], start_low[%f], air_resistance[%f]", result, high, start_low, air_resistance) ); + return ( result ); +} + +static float trajectory_select_pick_parabolic ( + SBullet& bullet, + float const start_low, + float high, + Fvector const& gravity, + float const air_resistance + ) +{ + float const max_test_distance = bullet.max_dist - bullet.fly_dist; + Fvector const start = trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, start_low); + float const start_high = high; + float low = start_low; + float check_time = high; + while ( !fsimilar(low, high) ) { + Fvector const intermediate = trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, start_low + (check_time - start_low)*.5f); + Fvector const target = trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, check_time); + float const distance = start.distance_to(intermediate) + intermediate.distance_to(target); + if (distance < max_test_distance) + low = check_time; + else + high = check_time; + + check_time = (low + high)*.5f; + } + + VERIFY ( low <= start_high ); + return (low); +} + +static bool trajectory_select_pick_ranges( + float& result, + SBullet& bullet, + float const low, + float const high, + Fvector const& gravity, + float const air_resistance + ) +{ + float const max_test_distance = bullet.max_dist - bullet.fly_dist; + VERIFY (max_test_distance > 0.f); + + if ( air_resistance*(low + air_resistance_epsilon) >= 1.f ) { + result = trajectory_select_pick_gravity(bullet, low, high, gravity, air_resistance); + return (true); + } + + if ( air_resistance*(high + air_resistance_epsilon) < 1.f ) { + result = trajectory_select_pick_parabolic(bullet, low, high, gravity, air_resistance); + return (false); + } + + float const fall_down_time = _max( 0.f, 1.f/air_resistance - air_resistance_epsilon); + if ( !fsimilar(fall_down_time, low) ) { + result = trajectory_select_pick_parabolic(bullet, low, fall_down_time, gravity, air_resistance); + return (false); + } + + result = trajectory_select_pick_gravity(bullet, fall_down_time, high, gravity, air_resistance); + return (false); +} + +static float trajectory_select_pick_time ( + SBullet& bullet, + float const start_low, + float high, + Fvector const& gravity, + float const air_resistance + ) +{ + VERIFY2 ( start_low < high, make_string("start_low[%f] high[%f]", start_low, high) ); + float const start_high = high; + if (trajectory_select_pick_ranges(high, bullet, start_low, high, gravity, air_resistance)) { + if (high <= start_high) + return (high); + + return (start_high); + } + + float low = start_low; + float check_time = high; + float const epsilon = .1f; + while ( !fsimilar(low, high) ) { + float distance = trajectory_pick_error(start_low, check_time, bullet.start_position, bullet.start_velocity, gravity, air_resistance); + + if (distance < epsilon) + low = check_time; + else + high = check_time; + + check_time = (low + high)*.5f; + } + + VERIFY2 (low <= start_high, make_string("low[%f], high[%f]", low, start_high)); + return (low); +} + +void CBulletManager::add_bullet_point ( + Fvector const& start_position, + Fvector& previous_position, + Fvector const& start_velocity, + Fvector const& gravity, + float const air_resistance, + float const current_time + ) +{ +#ifdef DEBUG + Fvector const temp = trajectory_position(start_position, start_velocity, gravity, air_resistance, current_time); + m_bullet_points.push_back (previous_position); + m_bullet_points.push_back (temp); + previous_position = temp; +#endif // #ifdef DEBUG +} + +static void update_bullet_parabolic ( + SBullet& bullet, + bullet_test_callback_data& data, + Fvector const& gravity, + float const air_resistance + ) +{ + Fvector xz_projection = Fvector(data.collide_position).sub(bullet.start_position); + xz_projection.y = 0; + float const xz_range = xz_projection.magnitude(); + Fvector const xz_velocity = Fvector().set(bullet.start_velocity.x, 0.f, bullet.start_velocity.z); + + VERIFY (air_resistance >= 0.f); + if ( air_resistance > 0.f ) { + float value = 2*air_resistance*xz_range/xz_velocity.magnitude(); + clamp (value, 0.f, 1.f); + VERIFY (value <= 1.f); + VERIFY (value >= 0.f); + data.collide_time = (1.f - _sqrt(1.f - value))/air_resistance; + } + else + data.collide_time = xz_range/xz_velocity.magnitude(); + + VERIFY (data.collide_time >= 0.f); + +// VERIFY (data.collide_time <= data.high_time); +// VERIFY (data.collide_time >= bullet.life_time); +// VERIFY (data.collide_time <= bullet.life_time + Device.fTimeGlobal); + clamp (data.collide_time, bullet.life_time, data.high_time); + + data.collide_position = trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, data.collide_time); + Fvector const new_velocity = trajectory_velocity(bullet.start_velocity, gravity, air_resistance, data.collide_time); + bullet.speed = new_velocity.magnitude(); + bullet.dir = Fvector(new_velocity).normalize_safe(); +} + +static void update_bullet_gravitation ( + SBullet& bullet, + bullet_test_callback_data& data, + Fvector const& gravity, + float const air_resistance, + float const fall_down_time + ) +{ + Fvector const fall_down_position= trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, fall_down_time); + Fvector const fall_down_velocity= trajectory_velocity(bullet.start_velocity, gravity, air_resistance, fall_down_time); + VERIFY ( !fis_zero(air_resistance_epsilon) || fis_zero( _sqr(fall_down_velocity.x) + _sqr(fall_down_velocity.z), EPS_L) ); + float const fall_down_velocity_magnitude = fall_down_velocity.magnitude(); + + Fvector xz_projection = Fvector(data.collide_position).sub(fall_down_position); + xz_projection.y = 0; + float const xz_range = xz_projection.magnitude(); + Fvector const xz_velocity = Fvector().set(fall_down_velocity.x, 0.f, fall_down_velocity.z); + + if ( !fis_zero(xz_velocity.magnitude()) ) { + data.collide_time = fall_down_time + xz_range/xz_velocity.magnitude(); + VERIFY (data.collide_time >= 0.f); + +// VERIFY (data.collide_time <= data.high_time); +// VERIFY (data.collide_time >= bullet.life_time); +// VERIFY (data.collide_time <= bullet.life_time + Device.fTimeGlobal); + clamp (data.collide_time, bullet.life_time, data.high_time); + } + else { + float const positive_gravity = -gravity.y; + float const distance = fall_down_position.distance_to(data.collide_position); + data.collide_time = fall_down_time + + ( + _sqrt( _sqr(fall_down_velocity_magnitude) + 2.f*distance*positive_gravity ) - + fall_down_velocity_magnitude + )/positive_gravity; + VERIFY (data.collide_time >= 0.f); + +// VERIFY (data.collide_time <= data.high_time); +// VERIFY (data.collide_time >= bullet.life_time); +// VERIFY (data.collide_time <= bullet.life_time + Device.fTimeGlobal); + clamp (data.collide_time, bullet.life_time, data.high_time); + } + + Fvector const new_velocity = trajectory_velocity(bullet.start_velocity, gravity, air_resistance, data.collide_time); + bullet.speed = new_velocity.magnitude(); + bullet.dir = Fvector(new_velocity).normalize_safe(); +} + +static void update_bullet ( + SBullet& bullet, + bullet_test_callback_data& data, + Fvector const& gravity, + float const air_resistance + ) +{ + if ( air_resistance*(bullet.life_time + air_resistance_epsilon) >= 1.f ) { + update_bullet_gravitation (bullet, data, gravity, air_resistance, _max( 0.f, 1.f/air_resistance - air_resistance_epsilon)); + return; + } + + Fvector const xz_velocity = Fvector().set( bullet.start_velocity.x, 0.f, bullet.start_velocity.z ); + if ( fis_zero(xz_velocity.square_magnitude()) ) { + update_bullet_gravitation (bullet, data, gravity, air_resistance, 0.f); + return; + } + + update_bullet_parabolic (bullet, data, gravity, air_resistance); +} + +BOOL CBulletManager::firetrace_callback (collide::rq_result& result, LPVOID params) +{ + bullet_test_callback_data& data = *(bullet_test_callback_data*)params; + SBullet& bullet = *data.pBullet; + + Fvector& collide_position = data.collide_position; + collide_position = Fvector().mad(bullet.bullet_pos, bullet.dir, result.range); + + float const air_resistance = Level().BulletManager().m_fAirResistanceK * bullet.air_resistance;//tatarinrafa:Vlad asked to change + + CBulletManager& bullet_manager = Level().BulletManager(); + Fvector const gravity = { 0.f, -bullet_manager.m_fGravityConst, 0.f }; + update_bullet ( bullet, data, gravity, air_resistance); + if ( fis_zero(bullet.speed) ) + return (FALSE); + + if ( fis_zero(data.collide_time) ) + return (TRUE); + + //статический объект + if (!result.O) { + CDB::TRI const& triangle = *(Level().ObjectSpace.GetStaticTris() + result.element); + bullet_manager.RegisterEvent(EVENT_HIT, FALSE, &bullet, collide_position, result, triangle.material); + return (FALSE); + } + + //динамический объект + VERIFY ( !(result.O->ID() == bullet.parent_id && bullet.fly_dist < parent_ignore_distance) ); + IKinematics* const kinematics = smart_cast(result.O->Visual()); + if (!kinematics) + return (FALSE); + + CBoneData const& bone_data = kinematics->LL_GetData( (u16)result.element ); + bullet_manager.RegisterEvent ( EVENT_HIT, TRUE, &bullet, collide_position, result, bone_data.game_mtl_idx ); + return (FALSE); +} + +bool CBulletManager::trajectory_check_error ( + Fvector& previous_position, + collide::rq_results& storage, + SBullet& bullet, + float& low, + float& high, + Fvector const& gravity, + float const air_resistance + ) +{ + Fvector const& position = bullet.start_position; + Fvector const& velocity = bullet.start_velocity; + Fvector const start = trajectory_position(position, velocity, gravity, air_resistance, low); + Fvector const target = trajectory_position(position, velocity, gravity, air_resistance, high); + Fvector start_to_target = Fvector().sub(target,start); + float const distance = start_to_target.magnitude(); + if ( fis_zero(distance) ) + return (true); + + start_to_target.mul ( 1.f/distance ); + + bullet_test_callback_data data; + data.pBullet = • +#if 1//def DEBUG + data.high_time = high; +#endif // #ifdef DEBUG + bullet.flags.ricochet_was = 0; + bullet.dir = start_to_target; + + collide::ray_defs RD (start, start_to_target, distance, CDB::OPT_FULL_TEST, collide::rqtBoth); + BOOL const result = Level().ObjectSpace.RayQuery(storage, RD, CBulletManager::firetrace_callback, &data, CBulletManager::test_callback, NULL); + if ( !result || (data.collide_time == 0.f) ) { + add_bullet_point (bullet.start_position, previous_position, bullet.start_velocity, gravity, air_resistance, high); + return (true); + } + + add_bullet_point (bullet.start_position, previous_position, bullet.start_velocity, gravity, air_resistance, data.collide_time); + + low = 0.f; + + VERIFY (high >= data.collide_time); + high -= data.collide_time; + + ++bullet.change_rajectory_count; + bullet.start_position = data.collide_position; + bullet.tracer_start_position = bullet.bullet_pos; + bullet.bullet_pos = data.collide_position; + bullet.start_velocity = Fvector().mul(bullet.dir, bullet.speed); + bullet.born_time += iFloor(data.collide_time*1000.f); + bullet.life_time = 0.f; + return (false); +} + +static bool try_update_bullet (SBullet& bullet, Fvector const& gravity, float const air_resistance, float const time) +{ + Fvector const new_position = trajectory_position(bullet.start_position, bullet.start_velocity, gravity, air_resistance, time); + bullet.fly_dist += bullet.bullet_pos.distance_to(new_position); + + if (bullet.fly_dist >= bullet.max_dist) + return (false); + + Fbox const level_box = Level().ObjectSpace.GetBoundingVolume(); + if ( + (bullet.bullet_pos.x < level_box.x1) || + (bullet.bullet_pos.x > level_box.x2) || + (bullet.bullet_pos.y < level_box.y1) || +// (bullet.bullet_pos.y > level_box.y2) || + (bullet.bullet_pos.z < level_box.z1) || + (bullet.bullet_pos.z > level_box.z2) + ) + return (false); + + Fvector const new_velocity = trajectory_velocity(bullet.start_velocity, gravity, air_resistance, bullet.life_time); + bullet.speed = new_velocity.magnitude(); + if ( fis_zero(bullet.speed) ) + return (false); + + bullet.bullet_pos = new_position; + bullet.dir = Fvector(new_velocity).normalize_safe(); + bullet.life_time = time; + return (true); +} + + +bool CBulletManager::process_bullet (collide::rq_results & storage, SBullet& bullet, u32 const delta_time) +{ + float const time_delta = float(delta_time)/1000.f; + Fvector const gravity = Fvector().set( 0.f, -m_fGravityConst, 0.f); + + float const air_resistance = m_fAirResistanceK * bullet.air_resistance; //tatarinrafa:Vlad asked to change + bullet.tracer_start_position= bullet.bullet_pos; + +#if 0//def DEBUG + extern BOOL g_bDrawBulletHit; + if (g_bDrawBulletHit) + { + Msg ( + "free fly velocity: %f", + trajectory_velocity( + bullet.start_velocity, + gravity, + air_resistance, + fis_zero(air_resistance) ? + 0.f : + (1.f/air_resistance - air_resistance_epsilon) + ).magnitude() + ); + } +#endif + + Fvector const&start_position= bullet.bullet_pos; + Fvector previous_position = start_position; + float low = bullet.life_time; + float high = bullet.life_time + time_delta; +// Msg ("process_bullet0: low[%f], high[%f]", low, high); + + bullet.change_rajectory_count = 0; + + for (;;) { + for (;;) { + if ( bullet.speed < 1.f ) + return (false); + + if ( bullet.change_rajectory_count >= 32 ) + return (false); + + float time = trajectory_select_pick_time(bullet, low, high, gravity, air_resistance); + if (time == low) + return (false); + + float safe_time = time; + VERIFY2 ( safe_time <= high, make_string("safe_time[%f], high[%f]", safe_time, high) ); + if ( !trajectory_check_error(previous_position, storage, bullet, low, time, gravity, air_resistance) ) { + VERIFY2 ( safe_time >= time, make_string("safe_time[%f], time[%f]", safe_time, time) ); + VERIFY2 ( safe_time <= high, make_string("safe_time[%f], high[%f]", safe_time, high) ); +// clamp (safe_time, time, high); + high = high - safe_time + time; + VERIFY2 ( low <= high, make_string("start_low[%f] high[%f]", low, high) ); + if ( fsimilar(low, high) ) + return ( !fis_zero(bullet.speed) ); + + break; + } + + if ( !try_update_bullet(bullet, gravity, air_resistance, time) ) + return (false); + + if ( fsimilar(time, high) ) + return ( true ); + + VERIFY2 ( low < high, make_string("start_low[%f] high[%f]", low, high) ); + low = time; + VERIFY2 ( low < high, make_string("start_low[%f] high[%f]", low, high) ); + } + + if ( fis_zero(bullet.speed) ) + return (false); + } +} + +#ifdef DEBUG + BOOL g_bDrawBulletHit = FALSE; +#endif + +float SqrDistancePointToSegment(const Fvector& pt, const Fvector& orig, const Fvector& dir) +{ + Fvector diff; diff.sub(pt,orig); + float fT = diff.dotproduct(dir); + + if ( fT <= 0.0f ){ + fT = 0.0f; + }else{ + float fSqrLen= dir.square_magnitude(); + if ( fT >= fSqrLen ){ + fT = 1.0f; + diff.sub(dir); + }else{ + fT /= fSqrLen; + diff.sub(Fvector().mul(dir,fT)); + } + } + + return diff.square_magnitude(); +} + +void CBulletManager::Render () +{ +#ifdef DEBUG + if (g_bDrawBulletHit && !m_bullet_points.empty()) { + VERIFY (!(m_bullet_points.size() % 2)); + CDebugRenderer& renderer = Level().debug_renderer(); + Fmatrix sphere = Fmatrix().scale(.05f, .05f, .05f); + BulletPoints::const_iterator i = m_bullet_points.begin(); + BulletPoints::const_iterator e = m_bullet_points.end(); + for ( ; i != e; i+=2) { + sphere.c = *i; + renderer.draw_ellipse (sphere, D3DCOLOR_XRGB(255, 0, 0)); + + renderer.draw_line (Fidentity, *i, *(i + 1), D3DCOLOR_XRGB(0, 255, 0)); + + sphere.c = *(i + 1); + renderer.draw_ellipse (sphere, D3DCOLOR_XRGB(255, 0, 0)); + } + + if (m_bullet_points.size() > 32768) + m_bullet_points.clear_not_free (); + } + else + m_bullet_points.clear_not_free (); + + //0-рикошет + //1-застрявание пули в материале + //2-пробивание материала + if (g_bDrawBulletHit) { + extern FvectorVec g_hit[]; + FvectorIt it; + u32 C[3] = {0xffff0000,0xff00ff00,0xff0000ff}; + //RCache.set_xform_world(Fidentity); + DRender->CacheSetXformWorld(Fidentity); + for(int i=0; i<3; ++i) + for(it=g_hit[i].begin();it!=g_hit[i].end();++it){ + Level().debug_renderer().draw_aabb(*it,0.01f,0.01f,0.01f,C[i]); + } + } +#endif + + if(m_BulletsRendered.empty()) return; + + //u32 vOffset = 0 ; + u32 bullet_num = m_BulletsRendered.size(); + + UIRender->StartPrimitive((u32)bullet_num*12, IUIRender::ptTriList, IUIRender::pttLIT); + + for(BulletVecIt it = m_BulletsRendered.begin(); it!=m_BulletsRendered.end(); it++){ + SBullet* bullet = &(*it); + if(!bullet->flags.allow_tracer) + continue; + + if (!bullet->CanBeRenderedNow()) + continue; + + Fvector const tracer = Fvector().sub(bullet->bullet_pos, bullet->tracer_start_position); + float length = tracer.magnitude(); + Fvector const tracer_direction = length >= EPS_L ? Fvector(tracer).mul(1.f/length) : Fvector().set(0.f, 0.f, 1.f); + + if (length < m_fTracerLengthMin) + continue; + + if (length > m_fTracerLengthMax) + length = m_fTracerLengthMax; + + float width = m_fTracerWidth; + float dist2segSqr = SqrDistancePointToSegment(Device.vCameraPosition, bullet->bullet_pos, tracer); + //--------------------------------------------- + float MaxDistSqr = 1.0f; + float MinDistSqr = 0.09f; + if (dist2segSqr < MaxDistSqr) + { + if (dist2segSqr < MinDistSqr) dist2segSqr = MinDistSqr; + + width *= _sqrt(dist2segSqr/MaxDistSqr); + } + if (Device.vCameraPosition.distance_to_sqr(bullet->bullet_pos)<(length*length)) + { + length = Device.vCameraPosition.distance_to(bullet->bullet_pos) - 0.3f; + } + + Fvector center; + center.mad (bullet->bullet_pos, tracer_direction, -length*.5f); + bool bActor = false; + if(Level().CurrentViewEntity()) + { + bActor = ( bullet->parent_id == Level().CurrentViewEntity()->ID() ); + } + tracers.Render (bullet->bullet_pos, center, tracer_direction, length, width, bullet->m_u8ColorID, bullet->speed, bActor); + } + + UIRender->CacheSetCullMode (IUIRender::cmNONE); + UIRender->CacheSetXformWorld (Fidentity); + UIRender->SetShader (*tracers.sh_Tracer); + UIRender->FlushPrimitive (); + UIRender->CacheSetCullMode (IUIRender::cmCCW); +} + +void CBulletManager::CommitRenderSet () // @ the end of frame +{ + m_BulletsRendered = m_Bullets ; + if (g_mt_config.test(mtBullets)) { + Device.seqParallel.push_back (fastdelegate::FastDelegate0<>(this,&CBulletManager::UpdateWorkload)); + } else { + UpdateWorkload (); + } +} +void CBulletManager::CommitEvents () // @ the start of frame +{ + if (m_Events.size() > 1000) + Msg ("! too many bullets during single frame: %d", m_Events.size()); + + for (u32 _it=0; _it 1000) { + static bool breakpoint = true; + if (breakpoint) + __asm int 3; + } +#endif // #ifdef DEBUG + + m_Events.push_back (_event()) ; + _event& E = m_Events.back() ; + E.Type = Type ; + E.bullet = *bullet ; + + switch(Type) + { + case EVENT_HIT: + { + E.dynamic = _dynamic ; + E.point = end_point ; + E.R = R ; + E.tgt_material = tgt_material ; + + ObjectHit( &E.hit_result, bullet, end_point, R, tgt_material, E.normal ); + + if (_dynamic) + { + // Workaround for actor, because it's real ID is always 0 + CActor* actor = smart_cast(R.O); + u16 targetId = actor + ? (u16)(-1) + : R.O->ID(); + + E.Repeated = (targetId == E.bullet.targetID); + // Msg("Dynamic object hit event: %s, id: %d, bullet target: %d, repeated: %d", R.O->cName().c_str(), targetId, E.bullet.targetID, E.Repeated); + bullet->targetID = targetId; + }; + }break; + case EVENT_REMOVE: + { + E.tgt_material = tgt_material ; + }break; + } +} diff --git a/src/xrGameLA/Level_Bullet_Manager.h b/src/xrGameLA/Level_Bullet_Manager.h new file mode 100644 index 000000000..003c43d04 --- /dev/null +++ b/src/xrGameLA/Level_Bullet_Manager.h @@ -0,0 +1,253 @@ +// Level_Bullet_Manager.h: для обеспечения полета пули по траектории +// все пули и осколки передаются сюда +////////////////////////////////////////////////////////////////////// + +#pragma once + + +#include "weaponammo.h" +#include "tracer.h" + +//коэфициенты и параметры патрона +struct SBullet_Hit +{ + float power; // power * cartridge + float impulse; // impulse * cartridge +}; + +//структура, описывающая пулю и ее свойства в полете +struct SBullet +{ + u32 init_frame_num ; //номер кадра на котором была запущена пуля + union { + struct { + u16 ricochet_was : 1 ; //пуля срикошетила + u16 explosive : 1 ; //special explosive mode for particles + u16 allow_tracer : 1 ; + u16 allow_ricochet : 1 ; //разрешить рикошет + u16 allow_sendhit : 1 ; //statistics +//. u16 skipped_frame : 1 ; //пропуск первой отрисовки + u16 aim_bullet : 1 ; //прицеленная пуля( вылетевшая первой после длительного молчания оружия (1-3 сек.)) + u16 magnetic_beam : 1 ; //магнитный луч (нет отклонения после пробивания, не падает скорость после пробивания) + }; + u16 _storage ; + } flags ; + u16 bullet_material_idx ; + + Fvector bullet_pos ; //текущая позиция + Fvector dir ; + float speed ; //текущая скорость + + u16 parent_id ; //ID персонажа который иницировал действие + u16 weapon_id ; //ID оружия из которого была выпущены пуля + + float fly_dist ; //дистанция которую пуля пролетела + Fvector tracer_start_position; + + Fvector start_position ; + Fvector start_velocity ; + u32 born_time ; + float life_time ; + u32 change_rajectory_count; + + //коэфициенты и параметры патрона + SBullet_Hit hit_param; + //------------------------------------------------------------------- + float air_resistance ; + //------------------------------------------------------------------- + float max_speed ; // maxspeed*cartridge + float max_dist ; // maxdist*cartridge + float armor_piercing ; // ap + float wallmark_size ; + //------------------------------------------------------------------- + u8 m_u8ColorID ; + + //тип наносимого хита + ALife::EHitType hit_type ; + //--------------------------------- + u32 m_dwID ; + ref_sound m_whine_snd ; + ref_sound m_mtl_snd ; + //--------------------------------- + u16 targetID ; + //--------------------------------- + bool density_mode ; + float density ; + Fvector begin_density ; + bool operator == (u32 ID){return ID == m_dwID;} +public: + SBullet (); + ~SBullet (); + + bool CanBeRenderedNow () const { return (Device.dwFrame > init_frame_num);} + + void Init (const Fvector& position, + const Fvector& direction, + float start_speed, + float power, +//. float power_critical, + float impulse, + u16 sender_id, + u16 sendersweapon_id, + ALife::EHitType e_hit_type, + float maximum_distance, + const CCartridge& cartridge, + float const air_resistance_factor, + bool SendHit); +}; + +class CLevel; + +class CBulletManager +{ +private: + static float const parent_ignore_distance; + +private: + collide::rq_results rq_storage; + xr_vector rq_spatial; + collide::rq_results m_rq_results; + +private: + DEFINE_VECTOR (ref_sound,SoundVec,SoundVecIt); + DEFINE_VECTOR (SBullet,BulletVec,BulletVecIt); + typedef std::pair _hit ; + friend CLevel; + + enum EventType { + EVENT_HIT = u8(0), + EVENT_REMOVE, + + EVENT_DUMMY = u8(-1), + }; + struct _event { + EventType Type; + BOOL dynamic ; + BOOL Repeated ; // последовательное повторное попадание в динамический объект + SBullet_Hit hit_result ; + SBullet bullet ; + Fvector normal ; + Fvector point ; + collide::rq_result R ; + u16 tgt_material; + }; + static void CalculateNewVelocity(Fvector & dest_new_vel, Fvector const & old_velocity, float ar, float life_time); +protected: + SoundVec m_WhineSounds ; + RStringVec m_ExplodeParticles ; + + //список пуль находящихся в данный момент на уровне +//. xrCriticalSection m_Lock ; + + BulletVec m_Bullets ; // working set, locked + BulletVec m_BulletsRendered ; // copy for rendering + xr_vector<_event> m_Events ; + +#ifdef DEBUG + u32 m_thread_id; + + typedef xr_vector BulletPoints; + BulletPoints m_bullet_points; +#endif // #ifdef DEBUG + + //отрисовка трассеров от пуль + CTracer tracers; + + //минимальная скорость, на которой пуля еще считается + static float m_fMinBulletSpeed; + + float m_fHPMaxDist; + + //константа G + float m_fGravityConst; + //сопротивление воздуха, процент, который отнимается от скорости + //полета пули + float m_fAirResistanceK; + //cколько процентов энергии потеряет пуля при столкновении с материалом (при падении под прямым углом) + float m_fCollisionEnergyMin; + //сколькол процентов энергии устанется у пули при любом столкновении + float m_fCollisionEnergyMax; + + //параметры отрисовки трассеров + float m_fTracerWidth; + float m_fTracerLengthMax; + float m_fTracerLengthMin; +protected: + void PlayWhineSound (SBullet* bullet, CObject* object, const Fvector& pos); + void PlayExplodePS (const Fmatrix& xf); + //функция обработки хитов объектов + static BOOL test_callback (const collide::ray_defs& rd, CObject* object, LPVOID params); + static BOOL firetrace_callback (collide::rq_result& result, LPVOID params); + + // Deffer event + void RegisterEvent (EventType Type, BOOL _dynamic, SBullet* bullet, const Fvector& end_point, collide::rq_result& R, u16 target_material); + + //попадание по динамическому объекту + void DynamicObjectHit (_event& E); + + //попадание по статическому объекту + void StaticObjectHit (_event& E); + + //попадание по любому объекту, на выходе - импульс и сила переданные пулей объекту + bool ObjectHit (SBullet_Hit* hit_res, SBullet* bullet, const Fvector& end_point, + collide::rq_result& R, u16 target_material, Fvector& hit_normal); + //отметка на пораженном объекте + void FireShotmark (SBullet* bullet, const Fvector& vDir, + const Fvector &vEnd, collide::rq_result& R, u16 target_material, + const Fvector& vNormal, bool ShowMark = true); + //просчет полета пули за некоторый промежуток времени + //принимается что на этом участке пуля движется прямолинейно + //и равномерно, а после просчета также изменяется текущая + //скорость и положение с учетом гравитации и ветра + //возвращаем true если пуля продолжает полет + bool trajectory_check_error ( + Fvector& previous_position, + collide::rq_results& rq_storage, + SBullet& bullet, + float& low, + float& high, + Fvector const& gravity, + float const air_resistance + ); + void add_bullet_point ( + Fvector const& start_position, + Fvector& previous_position, + Fvector const& start_velocity, + Fvector const& gravity, + float const ait_resistance, + float const current_time + ); + bool process_bullet ( + collide::rq_results& rq_storage, + SBullet& bullet, + u32 delta_time + ); + void __stdcall UpdateWorkload (); +public: + CBulletManager (); + virtual ~CBulletManager (); + + void Load (); + void Clear (); + void AddBullet (const Fvector& position, const Fvector& direction, float starting_speed, + float power, /*float power_critical,*/ float impulse, + u16 sender_id, u16 sendersweapon_id, + ALife::EHitType e_hit_type, float maximum_distance, + const CCartridge& cartridge, + float const air_resistance_factor, + bool SendHit,bool AimBullet=false); + + void CommitEvents (); // @ the start of frame + void CommitRenderSet (); // @ the end of frame + void Render (); +}; + +struct bullet_test_callback_data +{ + Fvector collide_position; + SBullet* pBullet; + float collide_time; +#if 1//def DEBUG + float high_time; +#endif // #ifdef DEBUG +}; diff --git a/src/xrGameLA/Level_SLS_Default.cpp b/src/xrGameLA/Level_SLS_Default.cpp new file mode 100644 index 000000000..1cef8f273 --- /dev/null +++ b/src/xrGameLA/Level_SLS_Default.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "level.h" +#include "xrserver.h" + +void CLevel::SLS_Default () // Default/Editor Load +{ + // Signal main actor spawn +/* + LPCSTR s_cmd = Engine.Params; + string64 s_name = "actor"; + if (strstr(s_cmd,"-actor ")) { + sscanf(strstr(s_cmd,"-actor ")+xr_strlen("-actor "),"%s",s_name); + ph_world = xr_new (); + ph_world->Create (); + } + g_cl_Spawn (s_name, -1, 0, 0, 0); +*/ + if (Server) Server->SLS_Default (); +} diff --git a/src/xrGameLA/Level_SLS_Load.cpp b/src/xrGameLA/Level_SLS_Load.cpp new file mode 100644 index 000000000..0e1d783b6 --- /dev/null +++ b/src/xrGameLA/Level_SLS_Load.cpp @@ -0,0 +1,6 @@ +#include "stdafx.h" +#include "level.h" + +void CLevel::net_Load (LPCSTR name) // Game Load +{ +} diff --git a/src/xrGameLA/Level_SLS_Save.cpp b/src/xrGameLA/Level_SLS_Save.cpp new file mode 100644 index 000000000..914eb8d70 --- /dev/null +++ b/src/xrGameLA/Level_SLS_Save.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" +#include "HUDmanager.h" +#include "../xrLevel.h" +#include "Level.h" +#include "xrserver.h" + +void CLevel::net_Save (LPCSTR name) // Game Save +{ + if (0==Server) { + Msg("KERNEL::Can't save game on pure client"); + return; + } + + // 1. Create stream + CMemoryWriter fs; + + // 2. Description + fs.open_chunk (fsSLS_Description); + fs.w_stringZ (net_SessionName()); + fs.close_chunk (); + + // 3. Server state + fs.open_chunk (fsSLS_ServerState); + Server->SLS_Save (fs); + fs.close_chunk (); + + // Save it to file + fs.save_to (name); +} diff --git a/src/xrGameLA/Level_bullet_manager_firetrace.cpp b/src/xrGameLA/Level_bullet_manager_firetrace.cpp new file mode 100644 index 000000000..4630855e1 --- /dev/null +++ b/src/xrGameLA/Level_bullet_manager_firetrace.cpp @@ -0,0 +1,533 @@ +// Level_Bullet_Manager.cpp: для обеспечения полета пули по траектории +// все пули и осколки передаются сюда +// (для просчета столкновений и их визуализации) +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "Level_Bullet_Manager.h" +#include "entity.h" +#include "../gamemtllib.h" +#include "level.h" +#include "gamepersistent.h" +#include "game_cl_base.h" +#include "xrmessages.h" +#include "clsid_game.h" +#include "../../Include/xrRender/Kinematics.h" +#include "Actor.h" +#include "AI/Stalker/ai_stalker.h" +#include "character_info.h" +#include "game_cl_base_weapon_usage_statistic.h" +#include "../../xrCDB/xr_collide_defs.h" +#include "../xr_3da/xr_collide_form.h" +#include "weapon.h" +#include "ik/math3d.h" +#include "actor.h" +#include "ai/monsters/basemonster/base_monster.h" +#include "ai/stalker/ai_stalker.h" +#include "BoneProtections.h" + +//константы shoot_factor, определяющие +//поведение пули при столкновении с объектом +#define RICOCHET_THRESHOLD 0.1 +#define STUCK_THRESHOLD 0.4 + +//расстояния не пролетев которого пуля не трогает того кто ее пустил +#define PARENT_IGNORE_DIST 3.f +extern float gCheckHitK; + +//test callback функция +// object - object for testing +//return TRUE-тестировать объект / FALSE-пропустить объект +BOOL CBulletManager::test_callback(const collide::ray_defs& rd, CObject* object, LPVOID params) +{ + bullet_test_callback_data* pData = (bullet_test_callback_data*)params; + SBullet* bullet = pData->pBullet; + + if( (object->ID() == bullet->parent_id) && + (bullet->fly_distflags.ricochet_was)) return FALSE; + + BOOL bRes = TRUE; + if (object){ + CEntity* entity = smart_cast(object); + if (entity&&entity->g_Alive()&&(entity->ID()!=bullet->parent_id)){ + ICollisionForm* cform = entity->collidable.model; + if ((NULL!=cform) && (cftObject==cform->Type())){ + CActor* actor = smart_cast(entity); + CAI_Stalker* stalker= smart_cast(entity); + // в кого попали? + if (actor && IsGameTypeSingle()/**/||stalker/**/){ + // попали в актера или сталкера + Fsphere S = cform->getSphere(); + entity->XFORM().transform_tiny (S.P) ; + float dist = rd.range; + // проверим попали ли мы в описывающую сферу + if (Fsphere::rpNone!=S.intersect_full(bullet->bullet_pos, bullet->dir, dist)) + { + // да попали, найдем кто стрелял + bool play_whine = true; + CObject* initiator = Level().Objects.net_Find (bullet->parent_id); + if (actor){ + // попали в актера + float hpf = 1.f; + float ahp = actor->HitProbability(); +#if 1 +# if 0 + CObject *weapon_object = Level().Objects.net_Find (bullet->weapon_id); + if (weapon_object) { + CWeapon *weapon = smart_cast(weapon_object); + if (weapon) { + float fly_dist = bullet->fly_dist+dist; + float dist_factor = _min(1.f,fly_dist/Level().BulletManager().m_fHPMaxDist); + ahp = dist_factor*weapon->hit_probability() + (1.f-dist_factor)*1.f; + } + } +# else + float game_difficulty_hit_probability = actor->HitProbability(); + CAI_Stalker *stalker = smart_cast(initiator); + if (stalker) + hpf = stalker->SpecificCharacter().hit_probability_factor(); + + float dist_factor = 1.f; + CObject *weapon_object = Level().Objects.net_Find (bullet->weapon_id); + if (weapon_object) { + CWeapon *weapon = smart_cast(weapon_object); + if (weapon) { + game_difficulty_hit_probability = weapon->hit_probability(); + float fly_dist = bullet->fly_dist+dist; + dist_factor = _min(1.f,fly_dist/Level().BulletManager().m_fHPMaxDist); + } + } + + ahp = dist_factor*game_difficulty_hit_probability + (1.f-dist_factor)*1.f; +# endif +#else + CAI_Stalker* i_stalker = smart_cast(initiator); + // если стрелял сталкер, учитываем - hit_probability_factor сталкерa иначе - 1.0 + if (i_stalker) { + hpf = i_stalker->SpecificCharacter().hit_probability_factor(); + float fly_dist = bullet->fly_dist+dist; + float dist_factor = _min(1.f,fly_dist/Level().BulletManager().m_fHPMaxDist); + ahp = dist_factor*actor->HitProbability() + (1.f-dist_factor)*1.f; + } +#endif + if (Random.randF(0.f,1.f)>(ahp*hpf)){ + bRes = FALSE; // don't hit actor + play_whine = true; // play whine sound + }else{ + // real test actor CFORM + Level().BulletManager().m_rq_results.r_clear(); + + if (cform->_RayQuery(rd,Level().BulletManager().m_rq_results)){ + bRes = TRUE; // hit actor + play_whine = false; // don't play whine sound + }else{ + bRes = FALSE; // don't hit actor + play_whine = true; // play whine sound + } + } + } + // play whine sound + if (play_whine){ + Fvector pt; + pt.mad (bullet->bullet_pos, bullet->dir, dist); + Level().BulletManager().PlayWhineSound (bullet,initiator,pt); + } + }else{ + // don't test this object again (return FALSE) + bRes = FALSE; + } + + } + } + } + } + + return bRes; +} + +void CBulletManager::FireShotmark (SBullet* bullet, const Fvector& vDir, const Fvector &vEnd, collide::rq_result& R, u16 target_material, const Fvector& vNormal, bool ShowMark) +{ + SGameMtlPair* mtl_pair = GMLib.GetMaterialPair(bullet->bullet_material_idx, target_material); + Fvector particle_dir; + + if (R.O) + { + particle_dir = vDir; + particle_dir.invert (); + + //на текущем актере отметок не ставим + if(Level().CurrentEntity() && Level().CurrentEntity()->ID() == R.O->ID()) return; + + if (mtl_pair && !mtl_pair->m_pCollideMarks->empty() && ShowMark) + { + //добавить отметку на материале + Fvector p; + p.mad(bullet->bullet_pos, bullet->dir, R.range - 0.01f); + ::Render->add_SkeletonWallmark (&R.O->renderable.xform, + PKinematics(R.O->Visual()), &*mtl_pair->m_pCollideMarks, + p, bullet->dir, bullet->wallmark_size); + } + } + else + { + //вычислить нормаль к пораженной поверхности + particle_dir = vNormal; + Fvector* pVerts = Level().ObjectSpace.GetStaticVerts(); + CDB::TRI* pTri = Level().ObjectSpace.GetStaticTris()+R.element; + + if (mtl_pair && !mtl_pair->m_pCollideMarks->empty() && ShowMark) + { + //добавить отметку на материале + ::Render->add_StaticWallmark (&*mtl_pair->m_pCollideMarks, vEnd, bullet->wallmark_size, pTri, pVerts); + // fprintf (log11,"Section works! add_StaticWallmark \n"); + } + // fclose(log11); + } + + ref_sound* pSound = (!mtl_pair || mtl_pair->CollideSounds.empty())? + NULL:&mtl_pair->CollideSounds[::Random.randI(0,mtl_pair->CollideSounds.size())]; + + //проиграть звук + if(pSound && ShowMark) + { + CObject* O = Level().Objects.net_Find(bullet->parent_id ); + bullet->m_mtl_snd = *pSound; + bullet->m_mtl_snd.play_at_pos(O, vEnd, 0); + } + + LPCSTR ps_name = ( !mtl_pair || mtl_pair->CollideParticles.empty() ) ? NULL : + *mtl_pair->CollideParticles[ ::Random.randI(0,mtl_pair->CollideParticles.size()) ]; + + SGameMtl* tgt_mtl = GMLib.GetMaterialByIdx(target_material); + BOOL bStatic = !tgt_mtl->Flags.test(SGameMtl::flDynamic); + + if( (ps_name && ShowMark) || (bullet->flags.explosive && bStatic) ) + { + VERIFY2 ( + (particle_dir.x*particle_dir.x+particle_dir.y*particle_dir.y+particle_dir.z*particle_dir.z) > flt_zero, + make_string("[%f][%f][%f]", VPUSH(particle_dir)) + ); + Fmatrix pos; + pos.k.normalize(particle_dir); + Fvector::generate_orthonormal_basis(pos.k, pos.j, pos.i); + pos.c.set(vEnd); + if(ps_name && ShowMark) + { + //отыграть партиклы попадания в материал + CParticlesObject* ps = CParticlesObject::Create(ps_name,TRUE); + + ps->UpdateParent( pos, zero_vel ); + GamePersistent().ps_needtoplay.push_back( ps ); + } + + if( bullet->flags.explosive && bStatic ) + { + PlayExplodePS( pos ); + } + } +} + +void CBulletManager::StaticObjectHit (CBulletManager::_event& E) +{ + FireShotmark(&E.bullet, E.bullet.dir, E.point, E.R, E.tgt_material, E.normal); +} + +void CBulletManager::DynamicObjectHit (CBulletManager::_event& E) +{ + //только для динамических объектов + VERIFY(E.R.O); + + if ( CEntity* entity = smart_cast(E.R.O) ) + { + if ( !entity->in_solid_state() ) + { + return; + } + } + + bool NeedShootmark = true; + + if (E.R.O->CLS_ID == CLSID_OBJECT_ACTOR) + { + game_PlayerState* ps = Game().GetPlayerByGameID(E.R.O->ID()); + if (ps && ps->testFlag(GAME_PLAYER_FLAG_INVINCIBLE)) + { + NeedShootmark = false; + }; + } +// else if ( CBaseMonster * monster = smart_cast(E.R.O) ) +// { +// NeedShootmark = monster->need_shotmark(); +// } + + //визуальное обозначение попадание на объекте +// Fvector hit_normal; + FireShotmark (&E.bullet, E.bullet.dir, E.point, E.R, E.tgt_material, E.normal, NeedShootmark); + + Fvector original_dir = E.bullet.dir; + //ObjectHit(&E.bullet, E.end_point, E.R, E.tgt_material, hit_normal); + + SBullet_Hit hit_param = E.hit_result; + + // object-space + //вычислить координаты попадания + Fvector p_in_object_space,position_in_bone_space; + Fmatrix m_inv; + m_inv.invert (E.R.O->XFORM()); + m_inv.transform_tiny(p_in_object_space, E.point); + + // bone-space + IKinematics* V = smart_cast(E.R.O->Visual()); + + if(V) + { + VERIFY3(V->LL_GetBoneVisible(u16(E.R.element)),*E.R.O->cNameVisual(),V->LL_BoneName_dbg(u16(E.R.element))); + Fmatrix& m_bone = (V->LL_GetBoneInstance(u16(E.R.element))).mTransform; + Fmatrix m_inv_bone; + m_inv_bone.invert(m_bone); + m_inv_bone.transform_tiny(position_in_bone_space, p_in_object_space); + } + else + { + position_in_bone_space.set(p_in_object_space); + } + + //отправить хит пораженному объекту + if (E.bullet.flags.allow_sendhit && !E.Repeated) + { + //------------------------------------------------- + bool AddStatistic = false; + if (GameID() != GAME_SINGLE && E.bullet.flags.allow_sendhit && E.R.O->CLS_ID == CLSID_OBJECT_ACTOR + && Game().m_WeaponUsageStatistic->CollectData()) + { + CActor* pActor = smart_cast(E.R.O); + if (pActor)// && pActor->g_Alive()) + { + Game().m_WeaponUsageStatistic->OnBullet_Hit(&E.bullet, E.R.O->ID(), (s16)E.R.element, E.point); + AddStatistic = true; + }; + }; + + SHit Hit = SHit( hit_param.power, + original_dir, + NULL, + u16(E.R.element), + position_in_bone_space, + hit_param.impulse, + E.bullet.hit_type, + E.bullet.armor_piercing, + E.bullet.flags.aim_bullet); + + Hit.GenHeader(u16((AddStatistic)? GE_HIT_STATISTIC : GE_HIT)&0xffff, E.R.O->ID()); + Hit.whoID = E.bullet.parent_id; + Hit.weaponID = E.bullet.weapon_id; + Hit.BulletID = E.bullet.m_dwID; + + NET_Packet np; + Hit.Write_Packet (np); + +// Msg("Hit sended: %d[%d,%d]", Hit.whoID, Hit.weaponID, Hit.BulletID); + CGameObject::u_EventSend(np); + } +} + +#ifdef DEBUG +FvectorVec g_hit[3]; +#endif + +extern void random_dir (Fvector& tgt_dir, const Fvector& src_dir, float dispersion); + +bool CBulletManager::ObjectHit( SBullet_Hit* hit_res, SBullet* bullet, const Fvector& end_point, + collide::rq_result& R, u16 target_material, Fvector& hit_normal ) +{ + //----------- normal - start + if ( R.O ) + { + //вернуть нормаль по которой играть партиклы + CCF_Skeleton* skeleton = smart_cast(R.O->CFORM()); + if ( skeleton ) + { + Fvector e_center; + hit_normal.set (0,0,0); + if ( skeleton->_ElementCenter( (u16)R.element,e_center ) ) + hit_normal.sub (end_point, e_center); + float len = hit_normal.square_magnitude(); + if ( !fis_zero(len) ) hit_normal.div (_sqrt(len)); + else hit_normal.invert (bullet->dir); + } + } + else + { + //вычислить нормаль к поверхности + Fvector* pVerts = Level().ObjectSpace.GetStaticVerts(); + CDB::TRI* pTri = Level().ObjectSpace.GetStaticTris()+R.element; + hit_normal.mknormal (pVerts[pTri->verts[0]],pVerts[pTri->verts[1]],pVerts[pTri->verts[2]]); + if ( bullet->density_mode ) + { + Fvector new_pos; + new_pos.mad(bullet->bullet_pos, bullet->dir, R.range); + float l = bullet->begin_density.distance_to(new_pos); + float shootFactor = l * bullet->density; + bullet->speed -= shootFactor; + if ( bullet->speed < 0 ) bullet->speed = 0; + } + if ( DOT( hit_normal, bullet->dir ) < 0 ) + { + if ( bullet->density_mode ) + { +// Log("WARNING: Material in material found while bullet tracing. Incorrect behaviour of shooting is possible."); + } + bullet->density_mode = true; + SGameMtl* mtl = GMLib.GetMaterialByIdx(target_material); + bullet->density = mtl->fDensityFactor; + bullet->begin_density.mad( bullet->bullet_pos, bullet->dir,R.range ); + } + else + { + bullet->density_mode=false; + } + } + //----------- normal - end + float old_speed = bullet->speed; + + //коэффициент уменьшение силы с падением скорости + float speed_factor = bullet->speed / bullet->max_speed; + //получить силу хита выстрела с учетом патрона + *hit_res = bullet->hit_param; //default param + + hit_res->power = bullet->hit_param.power*speed_factor; + + //(Если = 0, то пуля либо рикошетит(если контакт идёт по касательной), либо застряёт в текущем + //объекте, если больше 0, то пуля прошивает объект) + + SGameMtl* mtl = GMLib.GetMaterialByIdx( target_material ); + float mtl_ap = mtl->fShootFactor; + float shoot_factor = 0.0f; //default >> пуля НЕ пробила материал! + float ap = bullet->armor_piercing; + +#if 0 //skyloader: nobody tested, so i disabled it + //skyloader: the flight of bullets is now dependent on the protection of stalkers. Now, the bullet will not penetrate through the strong armor, and the bullet will ricochet. + if ( CAI_Stalker* stalker = smart_cast(R.O)) + { + float BoneArmor = stalker->GetBoneHitProtections()->getBoneArmor(R.element); + if(!fis_zero(BoneArmor, EPS)) + { + if(ap > BoneArmor) + { + //пуля пробила броню + shoot_factor = (ap - BoneArmor) / ap; + } + } else if ( ap > EPS && ap >= mtl_ap ) + { + //пуля пробила материал + shoot_factor = (( ap - mtl_ap ) / ap); + } + }// end skyloader + else +#endif + if ( ap > EPS && ap >= mtl_ap ) + { + //пуля пробила материал + shoot_factor = (( ap - mtl_ap ) / ap); + } + + hit_res->impulse = 0.0f; + float speed_scale = 0.0f; + +#ifdef DEBUG + //Fvector dbg_bullet_pos; + //dbg_bullet_pos.mad(bullet->bullet_pos,bullet->dir,R.range); + int bullet_state = 0; +#endif + + if ( fsimilar( mtl_ap, 0.0f ) )//Если материал полностью простреливаемый (кусты) + { +#ifdef DEBUG + bullet_state = 2; +#endif + return true; + } + +/* + if (bullet->flags.magnetic_beam && (shoot_factor > EPS)) + { +#ifdef DEBUG + bullet_state = 2; +#endif + //air resistance of magnetic_beam bullet is armor resistance too + bullet->armor_piercing -= mtl_ap * bullet->air_resistance; + return true; + } +*/ + //рикошет + Fvector new_dir; + new_dir.reflect ( bullet->dir,hit_normal ); + Fvector tgt_dir; + random_dir ( tgt_dir, new_dir, deg2rad( 10.0f ) ); + float ricoshet_factor = bullet->dir.dotproduct( tgt_dir ); + + float f = Random.randF( 0.5f, 0.8f ); //(0.5f,1.f); + if ( (f < ricoshet_factor) && !mtl->Flags.test(SGameMtl::flNoRicoshet) && bullet->flags.allow_ricochet ) + { + // уменьшение скорости полета в зависимости от угла падения пули (чем прямее угол, тем больше потеря) + bullet->flags.allow_ricochet = 0; + float scale = 1.0f - _abs(bullet->dir.dotproduct(hit_normal)) * m_fCollisionEnergyMin; + clamp(scale, 0.0f, m_fCollisionEnergyMax); + speed_scale = scale; + + // вычисление рикошета, делается немного фейком, т.к. пуля остается в точке столкновения + // и сразу выходит из RayQuery() + bullet->dir.set (tgt_dir); + bullet->bullet_pos = end_point; + bullet->flags.ricochet_was = 1; + + // Msg("Ricochet, scale = %.2f, ricoshet_factor = %.2f", scale, ricoshet_factor); + +#ifdef DEBUG + bullet_state = 0; +#endif + } + else if ( shoot_factor < EPS ) + { + //застрявание пули в материале + speed_scale = 0.0f; +#ifdef DEBUG + bullet_state = 1; +#endif + } + else + { + //пробивание материала + speed_scale = shoot_factor;//mtl->fShootFactor; + + bullet->bullet_pos.mad(bullet->bullet_pos,bullet->dir,EPS);//fake + //ввести коэффициент случайности при простреливании + Fvector rand_normal; + rand_normal.random_dir(bullet->dir, deg2rad(2.0f), Random); + bullet->dir.set(rand_normal); +#ifdef DEBUG + bullet_state = 2; +#endif + } + + //уменьшить скорость в зависимости от простреливаемости + bullet->speed *= speed_scale; + //сколько энергии в процентах потеряла пуля при столкновении + float energy_lost = 1.0f - (bullet->speed / old_speed) * m_fCollisionEnergyMax; + //импульс переданный объекту равен прямопропорционален потерянной энергии + hit_res->impulse = bullet->hit_param.impulse * speed_factor * energy_lost; + // Msg("ObjectHit material %s, mtl_ap = %.2f, shoot_factor = %.2f, flags = %X, fNoRicochet = %d, final impulse = %.2f, speed_factor = %.2f, energy_lost = %.2f", + // mtl->m_Name.c_str(), mtl_ap, shoot_factor, mtl->Flags.flags, mtl->Flags.test(SGameMtl::flNoRicoshet), hit_res->impulse, speed_factor, energy_lost); + +#ifdef DEBUG + extern BOOL g_bDrawBulletHit; + if(g_bDrawBulletHit) + { +// g_hit[bullet_state].push_back(dbg_bullet_pos); + g_hit[bullet_state].push_back(end_point); + } +#endif + + return true; +} diff --git a/src/xrGameLA/Level_input.cpp b/src/xrGameLA/Level_input.cpp new file mode 100644 index 000000000..a76a1b456 --- /dev/null +++ b/src/xrGameLA/Level_input.cpp @@ -0,0 +1,534 @@ +#include "stdafx.h" +#include +#include "HUDmanager.h" +#include "../xr_ioconsole.h" +#include "entity_alive.h" +#include "game_sv_single.h" +#include "alife_simulator.h" +#include "alife_simulator_header.h" +#include "level_graph.h" +#include "../fdemorecord.h" +#include "level.h" +#include "xr_level_controller.h" +#include "game_cl_base.h" +#include "stalker_movement_manager.h" +#include "Inventory.h" +#include "player_hud.h" +#include "xrServer.h" +#include "autosave_manager.h" +#include "actor.h" +#include "huditem.h" +#include "UIGameCustom.h" +#include "ui/UIDialogWnd.h" +#include "pch_script.h" +#include "ui/UIGameTutorial.h" +#include "clsid_game.h" +#include "../xr_input.h" +#include "saved_game_wrapper.h" +#include "ai_space.h" +#include "script_engine.h" +#include "UI/UIinventoryutilities.h" + +#ifdef DEBUG +# include "ai/monsters/BaseMonster/base_monster.h" +#endif +#include "../../Include/xrRender/DebugRender.h" + +#ifdef LOG_PLANNER + extern void try_change_current_entity(); + extern void restore_actor(); +#endif + +bool g_bDisableAllInput = false; +bool g_bDisableKeyboardInput = false; +extern float g_fTimeFactor; + +extern int quick_save_counter; +extern int max_quick_saves; + +#define CURRENT_ENTITY() (game?((GameID() == GAME_SINGLE) ? CurrentEntity() : CurrentControlEntity()):NULL) + +void CLevel::IR_OnMouseWheel( int direction ) +{ + if( g_bDisableAllInput ) return; + + if (CurrentGameUI()->IR_UIOnMouseWheel(direction)) return; + + if( Device.Paused() ) return; + if (CURRENT_ENTITY()) { + IInputReceiver* IR = smart_cast (smart_cast(CURRENT_ENTITY())); + if (IR) IR->IR_OnMouseWheel(direction); + } + +} + +static int mouse_button_2_key [] = {MOUSE_1,MOUSE_2,MOUSE_3}; + +void CLevel::IR_OnMousePress(int btn) +{ IR_OnKeyboardPress(mouse_button_2_key[btn]);} +void CLevel::IR_OnMouseRelease(int btn) +{ IR_OnKeyboardRelease(mouse_button_2_key[btn]);} +void CLevel::IR_OnMouseHold(int btn) +{ IR_OnKeyboardHold(mouse_button_2_key[btn]);} + +void CLevel::IR_OnMouseMove( int dx, int dy ) +{ + if(g_bDisableAllInput) return; + if (CurrentGameUI()->IR_UIOnMouseMove(dx,dy)) return; + if (Device.Paused()) return; + if (CURRENT_ENTITY()) { + IInputReceiver* IR = smart_cast (smart_cast(CURRENT_ENTITY())); + if (IR) IR->IR_OnMouseMove (dx,dy); + } +} + +class vtune_ { + BOOL enabled_ ; +public: + vtune_ () { + enabled_ = FALSE; + } + void enable () { if (!enabled_) { + Engine.External.tune_resume (); enabled_=TRUE; + Msg ("vtune : enabled"); + }} + void disable () { if (enabled_) { + Engine.External.tune_pause (); enabled_=FALSE; + Msg ("vtune : disabled"); + }} +} vtune ; + + + +extern CUISequencer* g_tutorial; + +#include "../igame_persistent.h" + + +void CLevel::IR_OnKeyboardPress (int key) +{ + bool b_ui_exist = (!!CurrentGameUI()); + +//. if (DIK_F10 == key) vtune.enable(); +//. if (DIK_F11 == key) vtune.disable(); +// Msg("CLevel::IR_OnKeyboardPress(%d)", key); + + + EGameActions _curr = get_binded_action(key); + switch ( _curr ) + { + + case kSCREENSHOT: + string256 scr_additional_name; + + xr_sprintf(scr_additional_name, sizeof(scr_additional_name), "w_%s_to_%s_gt_%s", g_pGamePersistent->Environment().Current[0]->m_identifier.c_str(), + g_pGamePersistent->Environment().Current[1]->m_identifier.c_str(), InventoryUtilities::GetGameTimeAsString(InventoryUtilities::etpTimeToMinutes, '-').c_str()); + + Msg("^ Additional screenshot descr.: %s", scr_additional_name); + + Render->Screenshot(IRender_interface::SM_NORMAL, scr_additional_name); + return; + break; + + case kCONSOLE: + Console->Show (); + return; + break; + + case kQUIT: + { + if(b_ui_exist && CurrentGameUI()->TopInputReceiver() ) + { + if(CurrentGameUI()->IR_UIOnKeyboardPress(key)) return;//special case for mp and main_menu + CurrentGameUI()->TopInputReceiver()->HideDialog(); + }else + { + Console->Execute("main_menu"); + }return; + }break; + case kPAUSE: + Device.Pause(!Device.Paused(), TRUE, TRUE, "li_pause_key"); + return; + + break; +/* + case DIK_DIVIDE: + if( OnServer() ){ + if (GameID() == GAME_SINGLE) + Server->game->SetGameTimeFactor(g_fTimeFactor); + else + { + Server->game->SetEnvironmentGameTimeFactor(g_fTimeFactor); + Server->game->SetGameTimeFactor(g_fTimeFactor); + }; + } + + break; + +case DIK_MULTIPLY: + if( OnServer() ){ + float NewTimeFactor = 1000.f; + if (GameID() == GAME_SINGLE) + Server->game->SetGameTimeFactor(NewTimeFactor); + else + { + Server->game->SetEnvironmentGameTimeFactor(NewTimeFactor); + }; + } + + break; +*/ + + }; + + if( g_bDisableAllInput || g_bDisableKeyboardInput) return; + if ( !bReady || !b_ui_exist ) return; + + if ( b_ui_exist && CurrentGameUI()->IR_UIOnKeyboardPress(key)) return; + + if( Device.Paused() ) return; + + if ( game && game->OnKeyboardPress(get_binded_action(key)) ) return; + + + if(_curr == kQUICK_SAVE && IsGameTypeSingle()) + { + luabind::functor lua_bool; + bool script_handeled = false; + + R_ASSERT2(ai().script_engine().functor("la_input.quick_save", lua_bool), "la_input.quick_save"); + + if (lua_bool.is_valid()){ + script_handeled = lua_bool(); + } + + if (script_handeled) { Msg("# Quick Save handeled by script"); return; } // Return if script handeled quick save + + + if (Actor() && Actor()->b_saveAllowed && Actor()->g_Alive()) + { + quick_save_counter++; + if (quick_save_counter > max_quick_saves) //bimd the index to max_quick_saves + { + quick_save_counter = 0; + } + + string_path saved_game, command; + + xr_sprintf(saved_game, "%s_quicksave_%i", Core.UserName, quick_save_counter); //make a savefile name using user name and save index + + xr_sprintf(command, "save %s", saved_game); //make a command for console + Msg("%s", command); //temporary + + Console->Execute(command); + } + else + { + // Calls refuse message on hud from script + luabind::functor lua_func; + R_ASSERT2(ai().script_engine().functor("la_input.quicksave_refuse", lua_func), "Can't find la_input.quicksave_refuse"); + lua_func(); + } + return; + } + + + if(_curr == kQUICK_LOAD && IsGameTypeSingle()) + { +#ifdef DEBUG + FS.get_path ("$game_config$")->m_Flags.set(FS_Path::flNeedRescan, TRUE); + FS.get_path ("$game_scripts$")->m_Flags.set(FS_Path::flNeedRescan, TRUE); + FS.rescan_pathes (); +#endif // DEBUG + + luabind::functor lua_bool; + bool script_handeled = false; + + R_ASSERT2(ai().script_engine().functor("la_input.quick_load", lua_bool), "la_input.quick_load"); + + if (lua_bool.is_valid()){ + script_handeled = lua_bool(); + } + + if (script_handeled) { Msg("# Quick Load handeled by script"); return; } // Return if script handeled quick load + + + string_path saved_game, command; + + xr_sprintf(saved_game, "%s_quicksave_%i", Core.UserName, quick_save_counter); //make a savefile name using user name and save index + + if (!CSavedGameWrapper::valid_saved_game(saved_game)){ + Msg("!Invalid save %s", saved_game); + return; + } + + if (g_tutorial && g_tutorial->IsActive()) { + g_tutorial->Stop(); + } + + xr_sprintf(command, "load %s", saved_game); //make a command for console + Msg("%s", command); //temporary + + Console->Execute(command); + return; + } + + +#ifndef MASTER_GOLD + switch (key) { + case DIK_NUMPAD5: + { + if (GameID() != GAME_SINGLE) + { + Msg("For this game type Demo Record is disabled."); +/// return; + }; + Console->Hide (); + Console->Execute("demo_record 1"); + } + break; +#endif // MASTER_GOLD + +#ifdef DEBUG + case DIK_RETURN: + bDebug = !bDebug; + return; + + case DIK_BACK: + if (GameID() == GAME_SINGLE) + DRender->NextSceneMode(); + return; + + case DIK_F4: { + if (pInput->iGetAsyncKeyState(DIK_LALT)) + break; + + if (pInput->iGetAsyncKeyState(DIK_RALT)) + break; + + bool bOk = false; + u32 i=0, j, n=Objects.o_count(); + if (pCurrentEntity) + for ( ; i(Objects.o_get_by_iterator(i)); + if (tpEntityAlive && tpEntityAlive->g_Alive()) { + bOk = true; + break; + } + } + if (!bOk) + for (i = 0; i (Objects.o_get_by_iterator(i)); + if (tpEntityAlive && tpEntityAlive->g_Alive()) { + bOk = true; + break; + } + } + if (bOk) { + CObject *tpObject = CurrentEntity(); + CObject *__I = Objects.o_get_by_iterator(i); + CObject **I = &__I; + + SetEntity(*I); + if (tpObject != *I) + { + CActor* pActor = smart_cast (tpObject); + if (pActor) + pActor->inventory().Items_SetCurrentEntityHud(false); + } + if (tpObject) + { + Engine.Sheduler.Unregister (tpObject); + Engine.Sheduler.Register (tpObject, TRUE); + }; + Engine.Sheduler.Unregister (*I); + Engine.Sheduler.Register (*I, TRUE); + + CActor* pActor = smart_cast (*I); + if (pActor) + { + pActor->inventory().Items_SetCurrentEntityHud(true); + + CHudItem* pHudItem = smart_cast(pActor->inventory().ActiveItem()); + if (pHudItem) + { + pHudItem->OnStateSwitch(pHudItem->GetState()); + } + } + } + } + return; + } +#ifdef LOG_PLANNER + case MOUSE_1: { + if (GameID() != GAME_SINGLE) + break; + if (pInput->iGetAsyncKeyState(DIK_LALT)) { + if (CurrentEntity()->CLS_ID == CLSID_OBJECT_ACTOR) + try_change_current_entity (); + else + restore_actor (); + return; + } + break; + } +#endif + case DIK_DIVIDE: + if( OnServer() ){ +// float NewTimeFactor = pSettings->r_float("alife","time_factor"); + + if (GameID() == GAME_SINGLE) + Server->game->SetGameTimeFactor(g_fTimeFactor); + else + { + Server->game->SetEnvironmentGameTimeFactor(g_fTimeFactor); + Server->game->SetGameTimeFactor(g_fTimeFactor); + }; + } + break; + case DIK_MULTIPLY: + if( OnServer() ){ + float NewTimeFactor = 1000.f; + if (GameID() == GAME_SINGLE) + Server->game->SetGameTimeFactor(NewTimeFactor); + else + { + Server->game->SetEnvironmentGameTimeFactor(NewTimeFactor); +// Server->game->SetGameTimeFactor(NewTimeFactor); + }; + } + break; + /**/ +#endif +#ifdef DEBUG +//#ifdef NDEBUG + case DIK_F9:{ +// if (!ai().get_alife()) +// break; +// const_cast(ai().alife().header()).set_state(ALife::eZoneStateSurge); + if (GameID() != GAME_SINGLE) + { + extern INT g_sv_SendUpdate; + g_sv_SendUpdate = 1; + }; + break; + } + return; +// case DIK_F10:{ +// ai().level_graph().set_dest_point(); +// ai().level_graph().build_detail_path(); +// if (!Objects.FindObjectByName("m_stalker_e0000") || !Objects.FindObjectByName("localhost/dima")) +// return; +// if (!m_bSynchronization) { +// m_bSynchronization = true; +// ai().level_graph().set_start_point(); +// m_bSynchronization = false; +// } +// luabind::functor functor; +// ai().script_engine().functor("alife_test.set_switch_online",functor); +// functor(0,false); +// } +// return; +// case DIK_F11: +// ai().level_graph().build_detail_path(); +// if (!Objects.FindObjectByName("m_stalker_e0000") || !Objects.FindObjectByName("localhost/dima")) +// return; +// if (!m_bSynchronization) { +// m_bSynchronization = true; +// ai().level_graph().set_dest_point(); +// ai().level_graph().select_cover_point(); +// m_bSynchronization = false; +// } +// return; +#endif // DEBUG +#ifndef MASTER_GOLD + } +#endif // MASTER_GOLD + + if (bindConsoleCmds.execute(key)) + return; + + if (CURRENT_ENTITY()) + { + IInputReceiver* IR = smart_cast (smart_cast(CURRENT_ENTITY())); + if (IR) IR->IR_OnKeyboardPress(get_binded_action(key)); + } + + + #ifdef _DEBUG + CObject *obj = Level().Objects.FindObjectByName("monster"); + if (obj) { + CBaseMonster *monster = smart_cast(obj); + if (monster) + monster->debug_on_key(key); + } + #endif +} + +void CLevel::IR_OnKeyboardRelease(int key) +{ + if (!bReady || g_bDisableAllInput ) return; + if ( CurrentGameUI() && CurrentGameUI()->IR_UIOnKeyboardRelease(key)) return; + if (game && game->OnKeyboardRelease(get_binded_action(key)) ) return; + if (Device.Paused() ) return; + + if (CURRENT_ENTITY()) + { + IInputReceiver* IR = smart_cast (smart_cast(CURRENT_ENTITY())); + if (IR) IR->IR_OnKeyboardRelease (get_binded_action(key)); + } +} + +void CLevel::IR_OnKeyboardHold(int key) +{ + if (g_bDisableAllInput || g_bDisableKeyboardInput) return; + if (CurrentGameUI() && CurrentGameUI()->IR_UIOnKeyboardHold(key)) return; + if (Device.Paused() && !Level().IsDemoPlay() ) return; + if (CURRENT_ENTITY()) + { + IInputReceiver* IR = smart_cast (smart_cast(CURRENT_ENTITY())); + if (IR) IR->IR_OnKeyboardHold (get_binded_action(key)); + } +} + +void CLevel::IR_OnMouseStop( int /**axis/**/, int /**value/**/) +{ +} + +void CLevel::IR_OnActivate() +{ + if(!pInput) return; + int i; + for (i = 0; i < CInput::COUNT_KB_BUTTONS; i++ ) + { + if(IR_GetKeyState(i)) + { + + EGameActions action = get_binded_action(i); + switch (action){ + case kFWD : + case kBACK : + case kL_STRAFE : + case kR_STRAFE : + case kLEFT : + case kRIGHT : + case kUP : + case kDOWN : + case kCROUCH : + case kACCEL : + case kL_LOOKOUT : + case kR_LOOKOUT : + case kWPN_FIRE : + { + IR_OnKeyboardPress (i); + }break; + }; + }; + } +} \ No newline at end of file diff --git a/src/xrGameLA/Level_load.cpp b/src/xrGameLA/Level_load.cpp new file mode 100644 index 000000000..e2b61299b --- /dev/null +++ b/src/xrGameLA/Level_load.cpp @@ -0,0 +1,195 @@ +#include "stdafx.h" +#include "HUDmanager.h" +#include "LevelGameDef.h" +#include "ai_space.h" +#include "ParticlesObject.h" +#include "script_process.h" +#include "script_engine.h" +#include "script_engine_space.h" +#include "level.h" +#include "game_cl_base.h" +#include "../x_ray.h" +#include "../gamemtllib.h" +#include "PhysicsCommon.h" +#include "level_sounds.h" +#include "GamePersistent.h" + +ENGINE_API bool g_dedicated_server; + +BOOL CLevel::Load_GameSpecific_Before() +{ + // AI space + g_pGamePersistent->LoadTitle ("st_loading_ai_objects"); + string_path fn_game; + + if (GamePersistent().GameType() == GAME_SINGLE && !ai().get_alife() && FS.exist(fn_game,"$level$","level.ai")) + ai().load (net_SessionName()); + + if (!g_dedicated_server && !ai().get_alife() && ai().get_game_graph() && FS.exist(fn_game, "$level$", "level.game")) { + IReader *stream = FS.r_open (fn_game); + ai().patrol_path_storage_raw (*stream); + FS.r_close (stream); + } + + return (TRUE); +} + +BOOL CLevel::Load_GameSpecific_After() +{ + R_ASSERT(m_StaticParticles.empty()); + // loading static particles + string_path fn_game; + if (FS.exist(fn_game, "$level$", "level.ps_static")) { + IReader *F = FS.r_open (fn_game); + CParticlesObject* pStaticParticles; + u32 chunk = 0; + string256 ref_name; + Fmatrix transform; + Fvector zero_vel={0.f,0.f,0.f}; + for (IReader *OBJ = F->open_chunk_iterator(chunk); OBJ; OBJ = F->open_chunk_iterator(chunk,OBJ)) { + OBJ->r_stringZ (ref_name,sizeof(ref_name)); + OBJ->r (&transform,sizeof(Fmatrix));transform.c.y+=0.01f; + pStaticParticles = CParticlesObject::Create(ref_name,FALSE,false); + pStaticParticles->UpdateParent (transform,zero_vel); + pStaticParticles->PlayStatic (false); + m_StaticParticles.push_back (pStaticParticles); + } + FS.r_close (F); + } + + if (!g_dedicated_server) + { + // loading static sounds + VERIFY (m_level_sound_manager); + m_level_sound_manager->Load (); + + // loading sound environment + if ( FS.exist(fn_game, "$level$", "level.snd_env")) { + IReader *F = FS.r_open (fn_game); + ::Sound->set_geometry_env(F); + FS.r_close (F); + } + // loading SOM + if (FS.exist(fn_game, "$level$", "level.som")) { + IReader *F = FS.r_open (fn_game); + ::Sound->set_geometry_som(F); + FS.r_close (F); + } + + // loading random (around player) sounds + if (pSettings->section_exist("sounds_random")){ + CInifile::Sect& S = pSettings->r_section("sounds_random"); + Sounds_Random.reserve (S.Data.size()); + for (CInifile::SectCIt I=S.Data.begin(); S.Data.end()!=I; ++I) + { + Sounds_Random.push_back (ref_sound()); + Sound->create (Sounds_Random.back(),*I->first,st_Effect,sg_SourceType); + } + Sounds_Random_dwNextTime= Device.TimerAsync () + 50000; + Sounds_Random_Enabled = FALSE; + } + } + + if (!g_dedicated_server) { + // loading scripts + ai().script_engine().remove_script_process(ScriptEngine::eScriptProcessorLevel); + + if (pLevel->section_exist("level_scripts") && pLevel->line_exist("level_scripts","script")) + ai().script_engine().add_script_process(ScriptEngine::eScriptProcessorLevel,new CScriptProcess("level",pLevel->r_string("level_scripts","script"))); + else + ai().script_engine().add_script_process(ScriptEngine::eScriptProcessorLevel,new CScriptProcess("level","")); + } + + BlockCheatLoad(); + return TRUE; +} + +struct translation_pair { + u32 m_id; + u16 m_index; + + IC translation_pair (u32 id, u16 index) + { + m_id = id; + m_index = index; + } + + IC bool operator== (const u16 &id) const + { + return (m_id == id); + } + + IC bool operator< (const translation_pair &pair) const + { + return (m_id < pair.m_id); + } + + IC bool operator< (const u16 &id) const + { + return (m_id < id); + } +}; + +void CLevel::Load_GameSpecific_CFORM ( CDB::TRI* tris, u32 count ) +{ + typedef xr_vector ID_INDEX_PAIRS; + ID_INDEX_PAIRS translator; + translator.reserve (GMLib.CountMaterial()); + u16 default_id = (u16)GMLib.GetMaterialIdx("default"); + translator.push_back (translation_pair(u32(-1),default_id)); + + u16 index = 0, static_mtl_count = 1; + int max_ID = 0; + int max_static_ID = 0; + for (GameMtlIt I=GMLib.FirstMaterial(); GMLib.LastMaterial()!=I; ++I, ++index) { + if (!(*I)->Flags.test(SGameMtl::flDynamic)) { + ++static_mtl_count; + translator.push_back (translation_pair((*I)->GetID(),index)); + if ((*I)->GetID()>max_static_ID) max_static_ID = (*I)->GetID(); + } + if ((*I)->GetID()>max_ID) max_ID = (*I)->GetID(); + } + // Msg("* Material remapping ID: [Max:%d, StaticMax:%d]",max_ID,max_static_ID); + VERIFY(max_static_ID<0xFFFF); + + if (static_mtl_count < 128) { + CDB::TRI *I = tris; + CDB::TRI *E = tris + count; + for ( ; I != E; ++I) { + ID_INDEX_PAIRS::iterator i = std::find(translator.begin(),translator.end(),(u16)(*I).material); + if (i != translator.end()) { + (*I).material = (*i).m_index; + SGameMtl* mtl = GMLib.GetMaterialByIdx ((*i).m_index); + (*I).suppress_shadows = mtl->Flags.is(SGameMtl::flSuppressShadows); + (*I).suppress_wm = mtl->Flags.is(SGameMtl::flSuppressWallmarks); + continue; + } + + Debug.fatal (DEBUG_INFO,"Game material '%d' not found",(*I).material); + } + return; + } + + std::sort (translator.begin(),translator.end()); + { + CDB::TRI *I = tris; + CDB::TRI *E = tris + count; + for ( ; I != E; ++I) { + ID_INDEX_PAIRS::iterator i = std::lower_bound(translator.begin(),translator.end(),(u16)(*I).material); + if ((i != translator.end()) && ((*i).m_id == (*I).material)) { + (*I).material = (*i).m_index; + SGameMtl* mtl = GMLib.GetMaterialByIdx ((*i).m_index); + (*I).suppress_shadows = mtl->Flags.is(SGameMtl::flSuppressShadows); + (*I).suppress_wm = mtl->Flags.is(SGameMtl::flSuppressWallmarks); + continue; + } + + Debug.fatal (DEBUG_INFO,"Game material '%d' not found",(*I).material); + } + } +} + +void CLevel::BlockCheatLoad() +{ + if( game && (GameID() != GAME_SINGLE) ) phTimefactor=1.f; +} diff --git a/src/xrGameLA/Level_network.cpp b/src/xrGameLA/Level_network.cpp new file mode 100644 index 000000000..b80765180 --- /dev/null +++ b/src/xrGameLA/Level_network.cpp @@ -0,0 +1,569 @@ +#include "pch_script.h" +#include "Level.h" +#include "Level_Bullet_Manager.h" +#include "xrserver.h" +#include "xrmessages.h" +#include "game_cl_base.h" +#include "PHCommander.h" +#include "net_queue.h" +#include "MainMenu.h" +#include "space_restriction_manager.h" +#include "ai_space.h" +#include "script_engine.h" +#include "stalker_animation_data_storage.h" +#include "client_spawn_manager.h" +#include "seniority_hierarchy_holder.h" +#include "UIGameCustom.h" +#include "UI/UIGameTutorial.h" +#include "ui/UIPdaWnd.h" + +ENGINE_API bool g_dedicated_server; + +const int max_objects_size = 2*1024; +const int max_objects_size_in_save = 8*1024; + +extern bool g_b_ClearGameCaptions; + +void CLevel::remove_objects () +{ + if (!IsGameTypeSingle()) Msg("CLevel::remove_objects - Start"); + BOOL b_stored = psDeviceFlags.test(rsDisableObjectsAsCrows); + + int loop = 5; + while(loop) + { + if (OnServer()) + { + R_ASSERT (Server); + Server->SLS_Clear (); + } + + if (OnClient()) + ClearAllObjects (); + + for (int i=0; i<20; ++i) + { + snd_Events.clear (); + psNET_Flags.set (NETFLAG_MINIMIZEUPDATES,FALSE); + // ugly hack for checks that update is twice on frame + // we need it since we do updates for checking network messages + ++(Device.dwFrame); + psDeviceFlags.set (rsDisableObjectsAsCrows,TRUE); + ClientReceive (); + ProcessGameEvents (); + Objects.Update (false); + #ifdef DEBUG + Msg ("Update objects list..."); + #endif // #ifdef DEBUG + Objects.dump_all_objects(); + } + + if(Objects.o_count()==0) + break; + else + { + --loop; + Msg ("Objects removal next loop. Active objects count=%d", Objects.o_count()); + } + + } + + BulletManager().Clear (); + ph_commander().clear (); + ph_commander_scripts().clear(); + + if(!g_dedicated_server) + space_restriction_manager().clear (); + + psDeviceFlags.set (rsDisableObjectsAsCrows, b_stored); + g_b_ClearGameCaptions = true; + + if (!g_dedicated_server) + ai().script_engine().collect_all_garbage (); + + stalker_animation_data_storage().clear (); + + VERIFY (Render); + Render->models_Clear (FALSE); + +#ifdef DEBUG + if(!g_dedicated_server) + if (!client_spawn_manager().registry().empty()) + client_spawn_manager().dump (); +#endif // DEBUG + if(!g_dedicated_server) + { + VERIFY (client_spawn_manager().registry().empty()); + client_spawn_manager().clear (); + } + + Render->clear_static_wallmarks(); + + g_pGamePersistent->destroy_particles (false); + +//. xr_delete (m_seniority_hierarchy_holder); +//. m_seniority_hierarchy_holder = xr_new(); + if (!IsGameTypeSingle()) Msg("CLevel::remove_objects - End"); +} + +#ifdef DEBUG + extern void show_animation_stats (); +#endif // DEBUG + +extern CUISequencer * g_tutorial; +extern CUISequencer * g_tutorial2; + +void CLevel::net_Stop () +{ + Msg ("- Disconnect"); + + if(CurrentGameUI()) + { + CurrentGameUI()->HideShownDialogs(); + CurrentGameUI()->PdaMenu().Reset(); + } + + if(g_tutorial && !g_tutorial->Persistent()) + g_tutorial->Stop(); + + if(g_tutorial2 && !g_tutorial->Persistent()) + g_tutorial2->Stop(); + + + bReady = false; + m_bGameConfigStarted = FALSE; + game_configured = FALSE; + + remove_objects (); + + IGame_Level::net_Stop (); + IPureClient::Disconnect (); + + if (Server) + { + Server->Disconnect (); + xr_delete (Server); + } + + if (!g_dedicated_server) + ai().script_engine().collect_all_garbage (); + +#ifdef DEBUG + show_animation_stats (); +#endif // DEBUG +} + + +void CLevel::ClientSend() +{ + if (GameID() != GAME_SINGLE && OnClient()) + { + if ( !net_HasBandwidth() ) return; + }; + +#ifdef BATTLEYE + battleye_system.UpdateClient(); +#endif // BATTLEYE + + NET_Packet P; + u32 start = 0; + //----------- for E3 ----------------------------- +// if () + { +// if (!(Game().local_player) || Game().local_player->testFlag(GAME_PLAYER_FLAG_VERY_VERY_DEAD)) return; + if (CurrentControlEntity()) + { + CObject* pObj = CurrentControlEntity(); + if (!pObj->getDestroy() && pObj->net_Relevant()) + { + P.w_begin (M_CL_UPDATE); + + + P.w_u16 (u16(pObj->ID()) ); + P.w_u32 (0); //reserved place for client's ping + + pObj->net_Export (P); + + if (P.B.count>9) + { + if (OnServer()) + { + if (net_IsSyncronised() && IsDemoSave()) + { + DemoCS.Enter(); + Demo_StoreData(P.B.data, P.B.count, DATA_CLIENT_PACKET); + DemoCS.Leave(); + } + } + else + Send (P, net_flags(FALSE)); + } + } + } + }; + if (OnClient()) + { + Flush_Send_Buffer(); + return; + } + //------------------------------------------------- + while (1) + { + P.w_begin (M_UPDATE); + start = Objects.net_Export (&P, start, max_objects_size); + + if (P.B.count>2) + { + Device.Statistic->TEST3.Begin(); + Send (P, net_flags(FALSE)); + Device.Statistic->TEST3.End(); + }else + break; + } + +} + +u32 CLevel::Objects_net_Save (NET_Packet* _Packet, u32 start, u32 max_object_size) +{ + NET_Packet& Packet = *_Packet; + u32 position; + for (; start(_P); +// Msg ("save:iterating:%d:%s",P->ID(),*P->cName()); + if (P && !P->getDestroy() && P->net_SaveRelevant()) { + Packet.w_u16 (u16(P->ID()) ); + Packet.w_chunk_open16 (position); +// Msg ("save:saving:%d:%s",P->ID(),*P->cName()); + P->net_Save (Packet); +#ifdef DEBUG + u32 size = u32 (Packet.w_tell()-position)-sizeof(u16); +// Msg ("save:saved:%d bytes:%d:%s",size,P->ID(),*P->cName()); + if (size>=65536) { + Debug.fatal (DEBUG_INFO,"Object [%s][%d] exceed network-data limit\n size=%d, Pend=%d, Pstart=%d", + *P->cName(), P->ID(), size, Packet.w_tell(), position); + } +#endif + Packet.w_chunk_close16 (position); +// if (0==(--count)) +// break; + if (max_object_size >= (NET_PacketSizeLimit - Packet.w_tell())) + break; + } + } + return ++start; +} + +void CLevel::ClientSave () +{ + NET_Packet P; + u32 start = 0; + int packet_count = 0; + int total_stored = 0; + int object_count = 0; + for (;;) { + P.w_begin (M_SAVE_PACKET); + + start = Objects_net_Save(&P, start, max_objects_size_in_save); + + if (P.B.count>2) + Send (P, net_flags(FALSE)); + else + break; + packet_count++; + total_stored += P.w_tell(); + object_count += start; + //Msg("~ NetPacket #%d stores %d objects in %db", packet_count, start, P.w_tell()); + } + Msg("~ Used %d NetPacket(s) total objects %d total stored %db", packet_count, object_count, total_stored); +} + +extern float phTimefactor; +extern BOOL g_SV_Disable_Auth_Check; + +void CLevel::Send (NET_Packet& P, u32 dwFlags, u32 dwTimeout) +{ + if (IsDemoPlay() && m_bDemoStarted) return; + // optimize the case when server located in our memory + if(psNET_direct_connect){ + ClientID _clid; + _clid.set (1); + Server->OnMessage (P, _clid ); + }else + if (Server && game_configured && OnServer() ) + { + Server->OnMessage (P,Game().local_svdpnid ); + }else + IPureClient::Send (P,dwFlags,dwTimeout ); + + if (g_pGameLevel && Level().game && GameID() != GAME_SINGLE && !g_SV_Disable_Auth_Check) { + // anti-cheat + phTimefactor = 1.f ; + psDeviceFlags.set (rsConstantFPS,FALSE) ; + } +} + +void CLevel::net_Update () +{ + if(game_configured){ + // If we have enought bandwidth - replicate client data on to server + Device.Statistic->netClient2.Begin (); + ClientSend (); + Device.Statistic->netClient2.End (); + } + // If server - perform server-update + if (Server && OnServer()) { + Device.Statistic->netServer.Begin(); + Server->Update (); + Device.Statistic->netServer.End (); + } +} + +struct _NetworkProcessor : public pureFrame +{ + virtual void _BCL OnFrame ( ) + { + if (g_pGameLevel && !Device.Paused() ) g_pGameLevel->net_Update(); + } +} NET_processor; + +pureFrame* g_pNetProcessor = &NET_processor; + +const int ConnectionTimeOut = 60000; //1 min + +BOOL CLevel::Connect2Server (LPCSTR options) +{ + NET_Packet P; + m_bConnectResultReceived = false ; + m_bConnectResult = true ; + if (!Connect(options)) return FALSE; + //--------------------------------------------------------------------------- + if(psNET_direct_connect) m_bConnectResultReceived = true; + u32 EndTime = GetTickCount() + ConnectionTimeOut; + while (!m_bConnectResultReceived) { + ClientReceive (); + Sleep (5); + if(Server) + Server->Update() ; + //----------------------------------------- + u32 CurTime = GetTickCount(); + if (CurTime > EndTime) + { + NET_Packet P; + P.B.count = 0; + P.r_pos = 0; + + P.w_u8(0); + P.w_u8(0); + P.w_stringZ("Data verification failed. Cheater? [1]"); + + OnConnectResult(&P); + } + if (net_isFails_Connect()) + { + OnConnectRejected (); + Disconnect () ; + return FALSE; + } + //----------------------------------------- + } + Msg ("%c client : connection %s - <%s>", m_bConnectResult ?'*':'!', m_bConnectResult ? "accepted" : "rejected", m_sConnectResult.c_str()); + if (!m_bConnectResult) + { + OnConnectRejected (); + Disconnect () ; + return FALSE ; + }; + + + if(psNET_direct_connect) + net_Syncronised = TRUE; + else + net_Syncronize (); + + while (!net_IsSyncronised()) { + }; + + //--------------------------------------------------------------------------- + P.w_begin (M_CLIENT_REQUEST_CONNECTION_DATA); + Send (P, net_flags(TRUE, TRUE, TRUE, TRUE)); + //--------------------------------------------------------------------------- + return TRUE; +}; + +void CLevel::OnBuildVersionChallenge () +{ + NET_Packet P; + P.w_begin (M_CL_AUTH); + u64 auth = FS.auth_get(); + P.w_u64 (auth); + Send (P, net_flags(TRUE, TRUE, TRUE, TRUE)); +}; + +void CLevel::OnConnectResult (NET_Packet* P) +{ + // multiple results can be sent during connection they should be "AND-ed" + m_bConnectResultReceived = true; + u8 result = P->r_u8(); + u8 res1 = P->r_u8(); + string128 ResultStr ; + P->r_stringZ(ResultStr) ; + if (!result) + { + m_bConnectResult = false ; + switch (res1) + { + case 0: //Standart error + { + if (!xr_strcmp(ResultStr, "Data verification failed. Cheater? [2]")) + MainMenu()->SetErrorDialog(CMainMenu::ErrDifferentVersion); + }break; + case 1: //GameSpy CDKey + { + if (!xr_strcmp(ResultStr, "Invalid CD Key")) + MainMenu()->SetErrorDialog(CMainMenu::ErrCDKeyInvalid);//, ResultStr); + if (!xr_strcmp(ResultStr, "CD Key in use")) + MainMenu()->SetErrorDialog(CMainMenu::ErrCDKeyInUse);//, ResultStr); + if (!xr_strcmp(ResultStr, "Your CD Key is disabled. Contact customer service.")) + MainMenu()->SetErrorDialog(CMainMenu::ErrCDKeyDisabled);//, ResultStr); + }break; + case 2: //login+password + { + MainMenu()->SetErrorDialog(CMainMenu::ErrInvalidPassword); + }break; + } + }; + m_sConnectResult = ResultStr; + + if (IsDemoSave()) + { +// P->r_stringZ(m_sDemoHeader.LevelName); +// P->r_stringZ(m_sDemoHeader.GameType); + m_sDemoHeader.bServerClient = P->r_u8(); + P->r_stringZ(m_sDemoHeader.ServerOptions); + //----------------------------------------- + FILE* fTDemo = fopen(m_sDemoName, "ab"); + if (fTDemo) + { + fwrite(&m_sDemoHeader.bServerClient, 32, 1, fTDemo); + + DWORD OptLen = m_sDemoHeader.ServerOptions.size(); + fwrite(&OptLen, 4, 1, fTDemo); + fwrite(*m_sDemoHeader.ServerOptions, OptLen, 1, fTDemo); + fclose(fTDemo); + }; + //----------------------------------------- + }; +}; + +void CLevel::ClearAllObjects () +{ + u32 CLObjNum = Level().Objects.o_count(); + + bool ParentFound = true; + + while (ParentFound) + { + ParentFound = false; + for (u32 i=0; iH_Parent()) continue; + //----------------------------------------------------------- + NET_Packet GEN; + GEN.w_begin (M_EVENT); + //--------------------------------------------- + GEN.w_u32 (Level().timeServer()); + GEN.w_u16 (GE_OWNERSHIP_REJECT); + GEN.w_u16 (pObj->H_Parent()->ID()); + GEN.w_u16 (u16(pObj->ID())); + game_events->insert (GEN); + if (g_bDebugEvents) ProcessGameEvents(); + //------------------------------------------------------------- + ParentFound = true; + //------------------------------------------------------------- +#ifdef DEBUG + Msg ("Rejection of %s[%d] from %s[%d]", *(pObj->cNameSect()), pObj->ID(), *(pObj->H_Parent()->cNameSect()), pObj->H_Parent()->ID()); +#endif + }; + ProcessGameEvents(); + }; + + CLObjNum = Level().Objects.o_count(); + + for (u32 i=0; iH_Parent()==NULL); + //----------------------------------------------------------- + NET_Packet GEN; + GEN.w_begin (M_EVENT); + //--------------------------------------------- + GEN.w_u32 (Level().timeServer()); + GEN.w_u16 (GE_DESTROY); + GEN.w_u16 (u16(pObj->ID())); + game_events->insert (GEN); + if (g_bDebugEvents) ProcessGameEvents(); + //------------------------------------------------------------- + ParentFound = true; + //------------------------------------------------------------- +#ifdef DEBUG + Msg ("Destruction of %s[%d]", *(pObj->cNameSect()), pObj->ID()); +#endif + }; + ProcessGameEvents(); +}; + +void CLevel::OnInvalidHost () +{ + IPureClient::OnInvalidHost(); + if (MainMenu()->GetErrorDialogType() == CMainMenu::ErrNoError) + MainMenu()->SetErrorDialog(CMainMenu::ErrInvalidHost); +}; + +void CLevel::OnInvalidPassword () +{ + IPureClient::OnInvalidPassword(); + MainMenu()->SetErrorDialog(CMainMenu::ErrInvalidPassword); +}; + +void CLevel::OnSessionFull () +{ + IPureClient::OnSessionFull(); + if (MainMenu()->GetErrorDialogType() == CMainMenu::ErrNoError) + MainMenu()->SetErrorDialog(CMainMenu::ErrSessionFull); +} + +void CLevel::OnConnectRejected () +{ + IPureClient::OnConnectRejected(); + +// if (MainMenu()->GetErrorDialogType() != CMainMenu::ErrNoError) +// MainMenu()->SetErrorDialog(CMainMenu::ErrServerReject); +}; + +void CLevel::net_OnChangeSelfName (NET_Packet* P) +{ + if (!P) return; + string64 NewName ; + P->r_stringZ(NewName) ; + if (!strstr(*m_caClientOptions, "/name=")) + { + string1024 tmpstr; + xr_strcpy(tmpstr, *m_caClientOptions); + xr_strcat(tmpstr, "/name="); + xr_strcat(tmpstr, NewName); + m_caClientOptions = tmpstr; + } + else + { + string1024 tmpstr; + xr_strcpy(tmpstr, *m_caClientOptions); + *(strstr(tmpstr, "name=")+5) = 0; + xr_strcat(tmpstr, NewName); + const char* ptmp = strstr(strstr(*m_caClientOptions, "name="), "/"); + if (ptmp) + xr_strcat(tmpstr, ptmp); + m_caClientOptions = tmpstr; + } +} \ No newline at end of file diff --git a/src/xrGameLA/Level_network_Demo.cpp b/src/xrGameLA/Level_network_Demo.cpp new file mode 100644 index 000000000..c3e588b8b --- /dev/null +++ b/src/xrGameLA/Level_network_Demo.cpp @@ -0,0 +1,428 @@ +#include "stdafx.h" +#include "Level.h" +#include "HUDManager.h" +#include "xrServer.h" + +#define DEMO_DATA_SIZE 65536 +u32 g_dwDemoDeltaFrame = 1; + +void CLevel::Demo_StoreServerData (void* data, u32 size) +{ + return; +/* warning 4702 - unreachable code + DemoCS.Enter(); + Demo_StoreData(data, size, DATA_SERVER_PACKET); + DemoCS.Leave(); +*/ +} + +void CLevel::Demo_StoreData (void* data, u32 size, DEMO_CHUNK DataType) +{ + if (!IsDemoSave()) return; + +// DemoCS.Enter(); + + u32 CurTime = timeServer_Async(); + u32 TotalSize = 4 + 4 + 4;// + switch(DataType) + { + case DATA_FRAME: + { + TotalSize += size; + }break; + case DATA_SERVER_PACKET: + case DATA_CLIENT_PACKET: + { + TotalSize += size + 4; + }break; + } + + R_ASSERT2(size <= DEMO_DATA_SIZE, "Data is too BIG!"); + if ((TotalSize + m_dwStoredDemoDataSize) >= DEMO_DATA_SIZE) + { + Demo_DumpData(); + }; + + DEMO_CHUNK Chunk = DataType; + CopyMemory(m_pStoredDemoData + m_dwStoredDemoDataSize, &Chunk, 4); m_dwStoredDemoDataSize += 4; + CopyMemory(m_pStoredDemoData + m_dwStoredDemoDataSize, &m_dwCurDemoFrame, 4); m_dwStoredDemoDataSize += 4; + CopyMemory(m_pStoredDemoData + m_dwStoredDemoDataSize, &CurTime, 4); m_dwStoredDemoDataSize += 4; + switch (DataType) + { + case DATA_FRAME: + { + CopyMemory(m_pStoredDemoData + m_dwStoredDemoDataSize, data, size); m_dwStoredDemoDataSize += size; + }break; + case DATA_SERVER_PACKET: + case DATA_CLIENT_PACKET: + { + CopyMemory(m_pStoredDemoData + m_dwStoredDemoDataSize, &size, 4); m_dwStoredDemoDataSize += 4; + CopyMemory(m_pStoredDemoData + m_dwStoredDemoDataSize, data, size); m_dwStoredDemoDataSize += size; + }break; + } + + +// DemoCS.Leave(); + + /* + FILE* fTDemo = fopen(m_sDemoName, "ab"); + if (!fTDemo) return; + + static u32 Count = 0; + static u32 TotalSize = 0; + u32 CurTime = timeServer_Async(); + fwrite(&(CurTime), sizeof(CurTime), 1, fTDemo); TotalSize += sizeof(CurTime); + fwrite(&(size), sizeof(size), 1, fTDemo); TotalSize += sizeof(size); + if (size) fwrite((data), 1, size, fTDemo); TotalSize += size; + fclose(fTDemo); + Count++; + */ +} + +void CLevel::Demo_DumpData() +{ + if (!m_sDemoName[0]) return; + FILE* fTDemo = fopen(m_sDemoName, "ab"); + if (fTDemo) + { + fwrite(m_pStoredDemoData, m_dwStoredDemoDataSize, 1, fTDemo); + fclose(fTDemo); + } + + m_dwStoredDemoDataSize = 0; +} + +void CLevel_DemoCrash_Handler () +{ + if (!g_pGameLevel) return; + Level().WriteStoredDemo(); + Level().CallOldCrashHandler(); +} + +//#define STORE_TDEMO + +void CLevel::Demo_PrepareToStore () +{ + m_bDemoSaveMode = !!strstr(Core.Params,"-techdemo"); + + if (!m_bDemoSaveMode) return; + + VERIFY (!m_we_used_old_crach_handler); + m_we_used_old_crach_handler = true; + m_pOldCrashHandler = Debug.get_crashhandler(); + Debug.set_crashhandler (CLevel_DemoCrash_Handler); + //--------------------------------------------------------------- + string1024 CName = ""; + u32 CNameSize = 1024; + GetComputerName(CName, (DWORD*)&CNameSize); + SYSTEMTIME Time; + GetLocalTime(&Time); + xr_sprintf(m_sDemoName, "xray_%s_%02d-%02d-%02d_%02d-%02d-%02d.tdemo", CName, Time.wMonth, Time.wDay, Time.wYear, Time.wHour, Time.wMinute, Time.wSecond); + Msg("Tech Demo would be stored in - %s", m_sDemoName); + + FS.update_path (m_sDemoName,"$logs$",m_sDemoName); + //--------------------------------------------------------------- + m_dwStoredDemoDataSize = 0; + m_pStoredDemoData = xr_alloc(DEMO_DATA_SIZE/sizeof(u8)); + //--------------------------------------------------------------- + m_dwCurDemoFrame = 0; +// ZeroMemory(&m_sDemoHeader, sizeof(m_sDemoHeader)); + m_sDemoHeader.Head[0] = 0; + m_sDemoHeader.ServerOptions = ""; +}; + +void CLevel::CallOldCrashHandler () +{ + if (!m_pOldCrashHandler) return; + m_pOldCrashHandler(); +}; + +void CLevel::WriteStoredDemo () +{ + if (!DemoCS.TryEnter()) return; + + Demo_DumpData(); + DemoCS.Leave(); +}; + +BOOL g_bLeaveTDemo = FALSE; + +void CLevel::Demo_Clear () +{ + WriteStoredDemo(); + m_bDemoSaveMode = FALSE; + xr_free(m_pStoredDemoData); + m_dwStoredDemoDataSize = 0; + + if (!g_bLeaveTDemo) + { + DeleteFile(m_sDemoName); + }; +}; + +void CLevel::Demo_Load (LPCSTR DemoName) +{ + string_path DemoFileName; + FS.update_path (DemoFileName,"$logs$",DemoName); + //----------------------------------------------------- + HANDLE hDemoFile = CreateFile(DemoFileName, FILE_ALL_ACCESS, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hDemoFile == NULL) return; + + u32 FileSize = GetFileSize(hDemoFile, NULL); + u8* pDemoData = xr_alloc(FileSize/sizeof(u8)); + ReadFile(hDemoFile, pDemoData, FileSize, (LPDWORD)&FileSize, NULL); + CloseHandle(hDemoFile); + u8* pTDemoData = pDemoData; + //----------------------------------------------------- + +// FILE* fTDemo = fopen(DemoFileName, "rb"); +// if (fTDemo) + { +// NET_Packet NewPacket; + DemoDataStruct NewData; + Msg("\n------- Loading Demo... ---------\n"); + +// while (!feof(fTDemo)) + while (u32(pTDemoData - pDemoData) < FileSize) + { + CopyMemory(&(NewData.m_dwDataType), pTDemoData, sizeof(NewData.m_dwDataType)); pTDemoData += sizeof(NewData.m_dwDataType); + CopyMemory(&(NewData.m_dwFrame), pTDemoData, sizeof(NewData.m_dwFrame)); pTDemoData += sizeof(NewData.m_dwFrame); + CopyMemory(&(NewData.m_dwTimeReceive), pTDemoData, sizeof(NewData.m_dwTimeReceive)); pTDemoData += sizeof(NewData.m_dwTimeReceive); + +// fread(&(NewData.m_dwDataType), sizeof(NewData.m_dwDataType), 1, fTDemo); +// fread(&(NewData.m_dwFrame), sizeof(NewData.m_dwFrame), 1, fTDemo); +// fread(&(NewData.m_dwTimeReceive), sizeof(NewData.m_dwTimeReceive), 1, fTDemo); + switch (NewData.m_dwDataType) + { + case DATA_FRAME: + { + CopyMemory(&(NewData.FrameTime), pTDemoData, sizeof(NewData.FrameTime)); pTDemoData += sizeof(NewData.FrameTime); +// fread(&(NewData.FrameTime), sizeof(NewData.FrameTime), 1, fTDemo); + m_dwLastDemoFrame = NewData.m_dwFrame; + }break; + case DATA_CLIENT_PACKET: + case DATA_SERVER_PACKET: + { + CopyMemory(&(NewData.Packet.B.count), pTDemoData, sizeof(NewData.Packet.B.count)); pTDemoData += sizeof(NewData.Packet.B.count); + CopyMemory((NewData.Packet.B.data), pTDemoData, NewData.Packet.B.count); pTDemoData += NewData.Packet.B.count; + +// fread(&(NewData.Packet.B.count), sizeof(NewData.Packet.B.count), 1, fTDemo); +// fread((NewData.Packet.B.data), 1, NewData.Packet.B.count, fTDemo); + }break; + }; + + m_aDemoData.push_back(NewData); + } +// fclose(fTDemo); + if (!m_aDemoData.empty()) + { + m_bDemoPlayMode = TRUE; + m_bDemoSaveMode = FALSE; + }; + m_dwCurDemoFrame = 0; + } + xr_free(pDemoData); + //----------------------------------------------- + g_dwDemoDeltaFrame = 1; +} + +static long lFileSize = 0; +void CLevel::Demo_Load_toFrame (LPCSTR FileName, DWORD toFrame, long &ofs) +{ + if (ofs == 1) g_dwDemoDeltaFrame = 1; + + m_sDemoFileName = FileName; + string_path DemoFileName; + FS.update_path (DemoFileName,"$logs$",FileName); + //----------------------------------------------------- + FILE* fTDemo = fopen(DemoFileName, "rb"); + if (!fTDemo) return; + if (ofs == 0) + { + //-------------- get file size ------------------------ + fseek(fTDemo, 0, SEEK_END); + lFileSize = ftell(fTDemo); + fseek(fTDemo, 0, SEEK_SET); + + //-------------- load file header --------------------- + fread(&m_sDemoHeader.bServerClient, 32, 1, fTDemo); + DWORD OptSize = 0; + fread(&OptSize, 4, 1, fTDemo); + string4096 ServerOptions = ""; + fread(&ServerOptions, OptSize, 1, fTDemo); + m_sDemoHeader.ServerOptions = ServerOptions; + ofs = ftell(fTDemo); + //------------- replace server and client options ----- + m_caServerOptions = m_sDemoHeader.ServerOptions; + m_caClientOptions = "localhost"; + //----------------------------------------------------- + } + if (fseek(fTDemo, ofs, SEEK_SET)) + { + R_ASSERT(0); + }; + DemoDataStruct NewData; + while (!feof(fTDemo) && (ofs < lFileSize)) + { + fread(&(NewData.m_dwDataType), sizeof(NewData.m_dwDataType), 1, fTDemo); + fread(&(NewData.m_dwFrame), sizeof(NewData.m_dwFrame), 1, fTDemo); + fread(&(NewData.m_dwTimeReceive), sizeof(NewData.m_dwTimeReceive), 1, fTDemo); + switch (NewData.m_dwDataType) + { + case DATA_FRAME: + { + fread(&(NewData.FrameTime), sizeof(NewData.FrameTime), 1, fTDemo); + m_dwLastDemoFrame = NewData.m_dwFrame; + }break; + case DATA_CLIENT_PACKET: + case DATA_SERVER_PACKET: + { + fread(&(NewData.Packet.B.count), sizeof(NewData.Packet.B.count), 1, fTDemo); + fread((NewData.Packet.B.data), 1, NewData.Packet.B.count, fTDemo); + }break; + }; + + ofs = ftell(fTDemo); + m_aDemoData.push_back(NewData); + if (NewData.m_dwFrame > toFrame) break; + }; + + fclose(fTDemo); + + if (!m_aDemoData.empty()) + { + m_bDemoPlayMode = TRUE; + m_bDemoSaveMode = FALSE; + }; +}; + +static DWORD dFrame = 1; +void CLevel::Demo_Update () +{ + if (!IsDemoPlay() || m_aDemoData.empty() || !m_bDemoStarted) return; + if (float(m_lDemoOfs)/lFileSize>0.95f) + { + g_dwDemoDeltaFrame = 1; + dFrame = 1; + } + static u32 Pos = 0; + + if (m_bDemoPlayByFrame) + Demo_Load_toFrame(m_sDemoFileName.c_str(), m_dwCurDemoFrame+dFrame, m_lDemoOfs); + + if (Pos >= m_aDemoData.size()) return; + + if (!m_bDemoPlayByFrame) + { + for (Pos; Pos < m_aDemoData.size(); Pos++) + { + u32 CurTime = timeServer_Async(); + DemoDataStruct* P = &(m_aDemoData[Pos]); + { + switch (P->m_dwDataType) + { + case DATA_SERVER_PACKET: + break; + case DATA_CLIENT_PACKET: + { + if (P->m_dwTimeReceive <= CurTime) + { + Msg("tReceive [%d] - CurTime [%d]",P->m_dwTimeReceive, CurTime); + IPureClient::OnMessage(P->Packet.B.data, P->Packet.B.count); + } + else + { + break; + }; + }break; + case DATA_FRAME: + if (P->m_dwTimeReceive <= CurTime) + { + Msg("tsReceive [%d] - CurTime [%d]",P->m_dwTimeReceive, CurTime); + Server->OnMessage(P->Packet, ClientID()); + } + else + { + break; + }; + break; + } + } + }; + } + else + { + while (!m_aDemoData.empty()) + { + DemoDataStruct* P; + P = &(m_aDemoData.front()); + if (P->m_dwFrame > m_dwCurDemoFrame) + { + break; + }; + switch (P->m_dwDataType) + { + case DATA_FRAME: + { + Device.dwTimeDelta = P->FrameTime.dwTimeDelta; + Device.dwTimeGlobal = P->FrameTime.dwTimeGlobal; + // CurFrameTime.dwTimeServer = Level().timeServer(); + // CurFrameTime.dwTimeServer_Delta = Level().timeServer_Delta(); + Device.fTimeDelta = P->FrameTime.fTimeDelta; + Device.fTimeGlobal = P->FrameTime.fTimeGlobal; + + }break; + case DATA_CLIENT_PACKET: + { + u16 m_type; + P->Packet.r_begin (m_type); + IPureClient::OnMessage(P->Packet.B.data, P->Packet.B.count); + }break; + case DATA_SERVER_PACKET: + { + u16 m_type; + P->Packet.r_begin (m_type); + Server->OnMessage(P->Packet, ClientID()); + }break; + } + m_aDemoData.pop_front(); + }; + }; + +// m_dwCurDemoFrame++; + + if (Pos >= m_aDemoData.size() || m_lDemoOfs>lFileSize) + { + Msg("! ------------- Demo Ended ------------"); + }; +}; + +void CLevel::Demo_StartFrame () +{ + if (!IsDemoSave() || !net_IsSyncronised()) return; + + DemoCS.Enter(); + + DemoFrameTime CurFrameTime; + CurFrameTime.dwTimeDelta = Device.dwTimeDelta; + CurFrameTime.dwTimeGlobal = Device.dwTimeGlobal; + CurFrameTime.dwTimeServer = Level().timeServer(); + CurFrameTime.dwTimeServer_Delta = Level().timeServer_Delta(); + CurFrameTime.fTimeDelta = Device.fTimeDelta; + CurFrameTime.fTimeGlobal= Device.fTimeGlobal; + + Demo_StoreData(&CurFrameTime, sizeof(CurFrameTime), DATA_FRAME); + + DemoCS.Leave(); +}; + +void CLevel::Demo_EndFrame () +{ + if (IsDemoPlay() && m_bDemoPlayByFrame) + m_dwCurDemoFrame+=dFrame; + else + m_dwCurDemoFrame++; + + dFrame = g_dwDemoDeltaFrame; +}; + diff --git a/src/xrGameLA/Level_network_Demo.h b/src/xrGameLA/Level_network_Demo.h new file mode 100644 index 000000000..a77cab4dc --- /dev/null +++ b/src/xrGameLA/Level_network_Demo.h @@ -0,0 +1,92 @@ +private: + string_path m_sDemoName; + + //------------- Demo Play --------------------------------------- + BOOL m_bDemoPlayMode; + BOOL m_bDemoPlayByFrame; + + xr_string m_sDemoFileName; + long m_lDemoOfs; + + enum DEMO_CHUNK + { + DATA_FRAME = u32(0), + DATA_CLIENT_PACKET, + DATA_SERVER_PACKET, + + DATA_DUMMY = u32(-1), + }; + + struct DemoFrameTime { + float fTimeDelta; + float fTimeGlobal; + u32 dwTimeDelta; + u32 dwTimeGlobal; + u32 dwTimeServer; + u32 dwTimeServer_Delta; + }; + + struct DemoHeaderStruct { + u8 bServerClient; + char Head[31]; + shared_str ServerOptions; +// string64 LevelName; +// string64 GameType; + }; + + DemoHeaderStruct m_sDemoHeader; + + struct DemoDataStruct { + u32 m_dwDataType; + u32 m_dwFrame; + u32 m_dwTimeReceive; + union { + NET_Packet Packet; + DemoFrameTime FrameTime; + }; + }; + + DEF_DEQUE(DemoDeque, DemoDataStruct); + DemoDeque m_aDemoData; + void Demo_Load (LPCSTR DemoName); + void Demo_Load_toFrame (LPCSTR FileName, DWORD toFrame, long &ofs); + BOOL m_bDemoStarted; + u32 m_dwLastDemoFrame; + void Demo_Update (); + + + //------------- Demo Store ----------------------------------------- + BOOL m_bDemoSaveMode; + + xrCriticalSection DemoCS; + u32 m_dwStoredDemoDataSize; + u8* m_pStoredDemoData; + + void Demo_PrepareToStore (); + void Demo_StoreData (void* data, u32 size, DEMO_CHUNK DataType); + void Demo_DumpData (); + void Demo_Clear (); + + + + crashhandler* m_pOldCrashHandler; + bool m_we_used_old_crach_handler; + + u32 m_dwCurDemoFrame; + void Demo_StartFrame (); + void Demo_EndFrame (); + +public: + void Demo_StoreServerData (void* data, u32 size); + + void CallOldCrashHandler (); + void WriteStoredDemo (); + + BOOL IsDemoPlay () {return (!m_bDemoSaveMode && m_bDemoPlayMode);}; + BOOL IsDemoSave () {return ( m_bDemoSaveMode && !m_bDemoPlayMode);}; + + bool IsServerDemo () {return (m_sDemoHeader.bServerClient != 0);}; + bool IsClientDemo () {return (m_sDemoHeader.bServerClient == 0);}; + + virtual NET_Packet* net_msg_Retreive (); +private: \ No newline at end of file diff --git a/src/xrGameLA/Level_network_messages.cpp b/src/xrGameLA/Level_network_messages.cpp new file mode 100644 index 000000000..4c4f890df --- /dev/null +++ b/src/xrGameLA/Level_network_messages.cpp @@ -0,0 +1,387 @@ +#include "stdafx.h" +#include "entity.h" +#include "xrserver_objects.h" +#include "level.h" +#include "xrmessages.h" +#include "game_cl_base.h" +#include "net_queue.h" +#include "Physics.h" +#include "xrServer.h" +#include "Actor.h" +#include "game_cl_base_weapon_usage_statistic.h" +#include "ai_space.h" +#include "saved_game_wrapper.h" +#include "level_graph.h" +#include "clsid_game.h" + +void CLevel::ClientReceive() +{ + + Demo_StartFrame(); + + Demo_Update(); + + m_dwRPC = 0; + m_dwRPS = 0; + + for (NET_Packet* P = net_msg_Retreive(); P; P=net_msg_Retreive()) + { + //----------------------------------------------------- + m_dwRPC++; + m_dwRPS += P->B.count; + //----------------------------------------------------- + u16 m_type; + u16 ID; + P->r_begin (m_type); + switch (m_type) + { + case M_MAP_SYNC: + { + shared_str map_name; + P->r_stringZ(map_name); + + shared_str _name = net_Hosts.size() ? net_Hosts.front().dpSessionName:""; + + if(_name.size() && _name!=map_name && OnClient()) + { + Msg("!!! map sync failed. current is[%s] server is[%s]",m_name.c_str(), map_name.c_str()); + Engine.Event.Defer ("KERNEL:disconnect"); + Engine.Event.Defer ("KERNEL:start",m_caServerOptions.size() ? size_t( xr_strdup(*m_caServerOptions)) : 0, m_caClientOptions.size() ? size_t(xr_strdup(*m_caClientOptions)) : 0); + } + }break; + case M_SPAWN: + { + if (!m_bGameConfigStarted || !bReady) + { + Msg ("Unconventional M_SPAWN received : cgf[%s] | bReady[%s]", + (m_bGameConfigStarted) ? "true" : "false", + (bReady) ? "true" : "false"); + break; + } + /*/ + cl_Process_Spawn(*P); + /*/ + game_events->insert (*P); + if (g_bDebugEvents) ProcessGameEvents(); + //*/ + } + break; + case M_EVENT: + game_events->insert (*P); + if (g_bDebugEvents) ProcessGameEvents(); + break; + case M_EVENT_PACK: + NET_Packet tmpP; + while (!P->r_eof()) + { + tmpP.B.count = P->r_u8(); + P->r(&tmpP.B.data, tmpP.B.count); + tmpP.timeReceive = P->timeReceive; + + game_events->insert (tmpP); + if (g_bDebugEvents) ProcessGameEvents(); + }; + break; + case M_UPDATE: + { + game->net_import_update (*P); + //------------------------------------------- + if (OnServer()) break; + //------------------------------------------- + }; // ни в коем случае нельзя здесь ставить break, т.к. в случае если все объекты не влазят в пакет M_UPDATE, + // они досылаются через M_UPDATE_OBJECTS + case M_UPDATE_OBJECTS: + { + Objects.net_Import (P); + + if (OnClient()) UpdateDeltaUpd(timeServer()); + IClientStatistic pStat = Level().GetStatistic(); + u32 dTime = 0; + + if ((Level().timeServer() + pStat.getPing()) < P->timeReceive) + { + dTime = pStat.getPing(); + } + else + dTime = Level().timeServer() - P->timeReceive + pStat.getPing(); + + u32 NumSteps = ph_world->CalcNumSteps(dTime); + SetNumCrSteps(NumSteps); + }break; +// case M_UPDATE_OBJECTS: +// { +// Objects.net_Import (P); +// }break; + //----------- for E3 ----------------------------- + case M_CL_UPDATE: + { + if (OnClient()) break; + P->r_u16 (ID); + u32 Ping = P->r_u32(); + CGameObject* O = smart_cast(Objects.net_Find (ID)); + if (0 == O) break; + O->net_Import(*P); + //--------------------------------------------------- + UpdateDeltaUpd(timeServer()); + if (pObjects4CrPr.empty() && pActors4CrPr.empty()) + break; + if (!smart_cast(O)) + break; + + u32 dTime = 0; + if ((Level().timeServer() + Ping) < P->timeReceive) + { +#ifdef DEBUG +// Msg("! TimeServer[%d] < TimeReceive[%d]", Level().timeServer(), P->timeReceive); +#endif + dTime = Ping; + } + else + dTime = Level().timeServer() - P->timeReceive + Ping; + u32 NumSteps = ph_world->CalcNumSteps(dTime); + SetNumCrSteps(NumSteps); + + O->CrPr_SetActivationStep(u32(ph_world->m_steps_num) - NumSteps); + AddActor_To_Actors4CrPr(O); + + }break; + case M_MOVE_PLAYERS: + { + u8 Count = P->r_u8(); + for (u8 i=0; ir_u16(); + Fvector NewPos, NewDir; + P->r_vec3(NewPos); + P->r_vec3(NewDir); + + CActor* OActor = smart_cast(Objects.net_Find (ID)); + if (0 == OActor) break; + OActor->MoveActor(NewPos, NewDir); + }; + + NET_Packet PRespond; + PRespond.w_begin(M_MOVE_PLAYERS_RESPOND); + Send(PRespond, net_flags(TRUE, TRUE)); + }break; + //------------------------------------------------ + case M_CL_INPUT: + { + P->r_u16 (ID); + CObject* O = Objects.net_Find (ID); + if (0 == O) break; + O->net_ImportInput(*P); + }break; + //--------------------------------------------------- + case M_SV_CONFIG_NEW_CLIENT: + InitializeClientGame(*P); + break; + case M_SV_CONFIG_GAME: + game->net_import_state (*P); + break; + case M_SV_CONFIG_FINISHED: + game_configured = TRUE; + Msg("- Game configuring : Finished "); + break; + case M_MIGRATE_DEACTIVATE: // TO: Changing server, just deactivate + { + P->r_u16 (ID); + CObject* O = Objects.net_Find (ID); + if (0 == O) break; + O->net_MigrateInactive (*P); + if (bDebug) Log("! MIGRATE_DEACTIVATE",*O->cName()); + } + break; + case M_MIGRATE_ACTIVATE: // TO: Changing server, full state + { + P->r_u16 (ID); + CObject* O = Objects.net_Find (ID); + if (0 == O) break; + O->net_MigrateActive (*P); + if (bDebug) Log("! MIGRATE_ACTIVATE",*O->cName()); + } + break; + case M_CHAT: + { + char buffer[256]; + P->r_stringZ(buffer); + Msg ("- %s",buffer); + } + break; + case M_GAMEMESSAGE: + { + if (!game) break; + Game().OnGameMessage(*P); + }break; + case M_RELOAD_GAME: + case M_LOAD_GAME: + case M_CHANGE_LEVEL: + { + if(m_type==M_LOAD_GAME) + { + string256 saved_name; + P->r_stringZ (saved_name); + if(xr_strlen(saved_name) && ai().get_alife()) + { + CSavedGameWrapper wrapper(saved_name); + if (wrapper.level_id() == ai().level_graph().level_id()) + { + Engine.Event.Defer ("Game:QuickLoad", size_t(xr_strdup(saved_name)), 0); + + break; + } + } + } + Engine.Event.Defer ("KERNEL:disconnect"); + Engine.Event.Defer ("KERNEL:start",size_t(xr_strdup(*m_caServerOptions)),size_t(xr_strdup(*m_caClientOptions))); + }break; + case M_SAVE_GAME: + { + ClientSave (); + }break; + case M_GAMESPY_CDKEY_VALIDATION_CHALLENGE: + { + + }break; + case M_AUTH_CHALLENGE: + { + OnBuildVersionChallenge(); + }break; + case M_CLIENT_CONNECT_RESULT: + { + OnConnectResult(P); + }break; + case M_CHAT_MESSAGE: + { + if (!game) break; + Game().OnChatMessage(P); + }break; + case M_CLIENT_WARN: + { + if (!game) break; + Game().OnWarnMessage(P); + }break; + case M_REMOTE_CONTROL_AUTH: + case M_REMOTE_CONTROL_CMD: + { + Game().OnRadminMessage(m_type, P); + }break; + case M_CHANGE_LEVEL_GAME: + { + Msg("- M_CHANGE_LEVEL_GAME Received"); + + if (OnClient()) + { + Engine.Event.Defer ("KERNEL:disconnect"); + Engine.Event.Defer ("KERNEL:start",m_caServerOptions.size() ? size_t( xr_strdup(*m_caServerOptions)) : 0,m_caClientOptions.size() ? size_t(xr_strdup(*m_caClientOptions)) : 0); + } + else + { + const char* m_SO = m_caServerOptions.c_str(); +// const char* m_CO = m_caClientOptions.c_str(); + + m_SO = strchr(m_SO, '/'); if (m_SO) m_SO++; + m_SO = strchr(m_SO, '/'); + + string128 LevelName = ""; + string128 GameType = ""; + + P->r_stringZ(LevelName); + P->r_stringZ(GameType); + + string4096 NewServerOptions = ""; + xr_sprintf(NewServerOptions, "%s/%s", LevelName, GameType); + + if (m_SO) xr_strcat(NewServerOptions, m_SO); + m_caServerOptions = NewServerOptions; + + Engine.Event.Defer ("KERNEL:disconnect"); + Engine.Event.Defer ("KERNEL:start",size_t(xr_strdup(*m_caServerOptions)),size_t(xr_strdup(*m_caClientOptions))); + }; + }break; + case M_CHANGE_SELF_NAME: + { + net_OnChangeSelfName(P); + }break; + case M_BULLET_CHECK_RESPOND: + { + if (!game) break; + if (GameID() != GAME_SINGLE) + Game().m_WeaponUsageStatistic->On_Check_Respond(P); + }break; + case M_STATISTIC_UPDATE: + { + if (!game) break; + if (GameID() != GAME_SINGLE) + Game().m_WeaponUsageStatistic->OnUpdateRequest(P); + }break; + case M_STATISTIC_UPDATE_RESPOND: + { + if (!game) break; + if (GameID() != GAME_SINGLE) + Game().m_WeaponUsageStatistic->OnUpdateRespond(P); + }break; + case M_BATTLEYE: + { +#ifdef BATTLEYE + battleye_system.ReadPacketClient( P ); +#endif // BATTLEYE + }break; + } + + net_msg_Release(); + } + +// if (!g_bDebugEvents) ProcessGameSpawns(); +} + +void CLevel::OnMessage (void* data, u32 size) +{ + DemoCS.Enter(); + + if (IsDemoPlay() ) + { + if (m_bDemoStarted) + { + DemoCS.Leave(); + return; + } + + if (!m_aDemoData.empty() && net_IsSyncronised()) + { +// NET_Packet *P = &(m_aDemoData.front()); + DemoDataStruct *P = &(m_aDemoData.front()); + u32 CurTime = timeServer_Async(); + timeServer_UserDelta(P->m_dwTimeReceive - CurTime); + m_bDemoStarted = TRUE; + Msg("! ------------- Demo Started ------------"); + m_dwCurDemoFrame = P->m_dwFrame; + DemoCS.Leave(); + return; + } + }; + + if (IsDemoSave() && net_IsSyncronised()) + { + Demo_StoreData(data, size, DATA_CLIENT_PACKET); + } + + IPureClient::OnMessage(data, size); + + DemoCS.Leave(); +}; + +NET_Packet* CLevel::net_msg_Retreive () +{ + NET_Packet* P = NULL; + + DemoCS.Enter(); + + P = IPureClient::net_msg_Retreive(); + if (!P) Demo_EndFrame(); + + DemoCS.Leave(); + + return P; +} + diff --git a/src/xrGameLA/Level_network_spawn.cpp b/src/xrGameLA/Level_network_spawn.cpp new file mode 100644 index 000000000..d84a636b8 --- /dev/null +++ b/src/xrGameLA/Level_network_spawn.cpp @@ -0,0 +1,209 @@ +#include "pch_script.h" +#include "xrServer_Objects_ALife_All.h" +#include "level.h" +#include "game_cl_base.h" +#include "net_queue.h" +#include "ai_space.h" +#include "game_level_cross_table.h" +#include "level_graph.h" +#include "client_spawn_manager.h" +#include "../xr_object.h" +#include "../IGame_Persistent.h" + +void CLevel::cl_Process_Spawn(NET_Packet& P) +{ + // Begin analysis + shared_str s_name; + P.r_stringZ (s_name); + + // Create DC (xrSE) + CSE_Abstract* E = F_entity_Create (*s_name); + R_ASSERT2(E, *s_name); + + E->Spawn_Read (P); + if (E->s_flags.is(M_SPAWN_UPDATE)) + E->UPDATE_Read (P); +//------------------------------------------------- +// Msg ("M_SPAWN - %s[%d][%x] - %d", *s_name, E->ID, E,E->ID_Parent); +//------------------------------------------------- + //force object to be local for server client + if (OnServer()) { + E->s_flags.set(M_SPAWN_OBJECT_LOCAL, TRUE); + }; + + /* + game_spawn_queue.push_back(E); + if (g_bDebugEvents) ProcessGameSpawns(); + /*/ + g_sv_Spawn (E); + + F_entity_Destroy (E); + //*/ +}; + +void CLevel::g_cl_Spawn (LPCSTR name, u8 rp, u16 flags, Fvector pos) +{ + // Create + CSE_Abstract* E = F_entity_Create(name); + VERIFY (E); + + // Fill + E->s_name = name; + E->set_name_replace (""); + E->s_gameid = u8(GameID()); + E->s_RP = rp; + E->ID = 0xffff; + E->ID_Parent = 0xffff; + E->ID_Phantom = 0xffff; + E->s_flags.assign (flags); + E->RespawnTime = 0; + E->o_Position = pos; + + // Send + NET_Packet P; + E->Spawn_Write (P,TRUE); + Send (P,net_flags(TRUE)); + + // Destroy + F_entity_Destroy (E); +} + +#ifdef DEBUG + extern Flags32 psAI_Flags; + extern float debug_on_frame_gather_stats_frequency; +# include "ai_debug.h" +#endif // DEBUG + +void CLevel::g_sv_Spawn (CSE_Abstract* E) +{ +#ifdef DEBUG_MEMORY_MANAGER + u32 E_mem = 0; + if (g_bMEMO) { + lua_gc (ai().script_engine().lua(),LUA_GCCOLLECT,0); + lua_gc (ai().script_engine().lua(),LUA_GCCOLLECT,0); + E_mem = Memory.mem_usage(); + Memory.stat_calls = 0; + } +#endif // DEBUG_MEMORY_MANAGER + //----------------------------------------------------------------- +// CTimer T(false); + +#ifdef DEBUG +// Msg ("* CLIENT: Spawn: %s, ID=%d", *E->s_name, E->ID); +#endif + + // Optimization for single-player only - minimize traffic between client and server + if (GameID() == GAME_SINGLE) psNET_Flags.set (NETFLAG_MINIMIZEUPDATES,TRUE); + else psNET_Flags.set (NETFLAG_MINIMIZEUPDATES,FALSE); + + // Client spawn +// T.Start (); + CObject* O = Objects.Create (*E->s_name); + // Msg ("--spawn--CREATE: %f ms",1000.f*T.GetAsync()); + +// T.Start (); +#ifdef DEBUG_MEMORY_MANAGER + mem_alloc_gather_stats (false); +#endif // DEBUG_MEMORY_MANAGER + if (0==O || (!O->net_Spawn (E))) + { + O->net_Destroy ( ); + if(!g_dedicated_server) + client_spawn_manager().clear(O->ID()); + Objects.Destroy (O); + Msg ("! Failed to spawn entity '%s'",*E->s_name); +#ifdef DEBUG_MEMORY_MANAGER + mem_alloc_gather_stats (!!psAI_Flags.test(aiDebugOnFrameAllocs)); +#endif // DEBUG_MEMORY_MANAGER + } else { +#ifdef DEBUG_MEMORY_MANAGER + mem_alloc_gather_stats (!!psAI_Flags.test(aiDebugOnFrameAllocs)); +#endif // DEBUG_MEMORY_MANAGER + if(!g_dedicated_server) + client_spawn_manager().callback(O); + //Msg ("--spawn--SPAWN: %f ms",1000.f*T.GetAsync()); + if ((E->s_flags.is(M_SPAWN_OBJECT_LOCAL)) && (E->s_flags.is(M_SPAWN_OBJECT_ASPLAYER))) { + if (CurrentEntity() != NULL) + { + CGameObject* pGO = smart_cast(CurrentEntity()); + if (pGO) pGO->On_B_NotCurrentEntity(); + } + SetEntity (O); + SetControlEntity (O); +// if (net_Syncronised)net_Syncronize (); // start sync-thread again + } + + if (0xffff != E->ID_Parent) + { + NET_Packet GEN; + GEN.write_start(); + GEN.read_start(); + GEN.w_u16 (u16(O->ID())); + GEN.w_u8 (1); + cl_Process_Event(E->ID_Parent, GE_OWNERSHIP_TAKE, GEN); + } + } + //--------------------------------------------------------- + Game().OnSpawn(O); + //--------------------------------------------------------- +#ifdef DEBUG_MEMORY_MANAGER + if (g_bMEMO) { + lua_gc (ai().script_engine().lua(),LUA_GCCOLLECT,0); + lua_gc (ai().script_engine().lua(),LUA_GCCOLLECT,0); + Msg ("* %20s : %d bytes, %d ops", *E->s_name,Memory.mem_usage()-E_mem, Memory.stat_calls ); + } +#endif // DEBUG_MEMORY_MANAGER +} + +CSE_Abstract *CLevel::spawn_item (LPCSTR section, const Fvector &position, u32 level_vertex_id, u16 parent_id, bool return_item) +{ + CSE_Abstract *abstract = F_entity_Create(section); + R_ASSERT3 (abstract,"Cannot find item with section",section); + CSE_ALifeDynamicObject *dynamic_object = smart_cast(abstract); + if (dynamic_object && ai().get_level_graph()) { + dynamic_object->m_tNodeID = level_vertex_id; + if (ai().level_graph().valid_vertex_id(level_vertex_id) && ai().get_game_graph() && ai().get_cross_table()) + dynamic_object->m_tGraphID = ai().cross_table().vertex(level_vertex_id).game_vertex_id(); + } + + //оружие спавним с полным магазинои + CSE_ALifeItemWeapon* weapon = smart_cast(abstract); + if(weapon) + weapon->a_elapsed = weapon->get_ammo_magsize(); + + // Fill + abstract->s_name = section; + abstract->set_name_replace (section); + abstract->s_gameid = u8(GameID()); + abstract->o_Position = position; + abstract->s_RP = 0xff; + abstract->ID = 0xffff; + abstract->ID_Parent = parent_id; + abstract->ID_Phantom = 0xffff; + abstract->s_flags.assign(M_SPAWN_OBJECT_LOCAL); + abstract->RespawnTime = 0; + + if (!return_item) { + NET_Packet P; + abstract->Spawn_Write (P,TRUE); + Send (P,net_flags(TRUE)); + F_entity_Destroy (abstract); + return (0); + } + else + return (abstract); +} + +void CLevel::ProcessGameSpawns () +{ + while (!game_spawn_queue.empty()) + { + CSE_Abstract* E = game_spawn_queue.front(); + + g_sv_Spawn (E); + + F_entity_Destroy (E); + + game_spawn_queue.pop_front (); + } +} diff --git a/src/xrGameLA/Level_network_start_client.cpp b/src/xrGameLA/Level_network_start_client.cpp new file mode 100644 index 000000000..6fd4bac69 --- /dev/null +++ b/src/xrGameLA/Level_network_start_client.cpp @@ -0,0 +1,179 @@ +#include "stdafx.h" +#include "HUDmanager.h" +#include "PHdynamicdata.h" +#include "Physics.h" +#include "level.h" +#include "../x_ray.h" +#include "../igame_persistent.h" +#include "PhysicsGamePars.h" +#include "ai_space.h" + +extern ENGINE_API BOOL mt_texture_loading; + +extern pureFrame* g_pNetProcessor; + +BOOL CLevel::net_Start_client ( LPCSTR options ) +{ + return FALSE; +} +#include "string_table.h" +bool CLevel::net_start_client1 () +{ + pApp->LoadBegin (); + // name_of_server + string64 name_of_server = ""; +// strcpy (name_of_server,*m_caClientOptions); + if (strchr(*m_caClientOptions, '/')) + strncpy(name_of_server,*m_caClientOptions, strchr(*m_caClientOptions, '/')-*m_caClientOptions); + + if (strchr(name_of_server,'/')) *strchr(name_of_server,'/') = 0; + + // Startup client + string256 temp; + xr_sprintf (temp,"%s %s", + CStringTable().translate("st_client_connecting_to").c_str(), name_of_server); + + g_pGamePersistent->LoadTitle (temp); + return true; +} + +#include "xrServer.h" + +bool CLevel::net_start_client2 () +{ + if(psNET_direct_connect) + { + Server->create_direct_client(); + } + + connected_to_server = Connect2Server(*m_caClientOptions); + + return true; +} + +bool CLevel::net_start_client3 () +{ + if(connected_to_server){ + LPCSTR level_name = NULL; + LPCSTR level_ver = NULL; + if(psNET_direct_connect) + { + shared_str const & server_options = Server->GetConnectOptions(); + level_name = ai().get_alife() ? *name() : Server->level_name( Server->GetConnectOptions() ).c_str(); + level_ver = Server->level_version (server_options).c_str(); //1.0 + }else + level_name = ai().get_alife() ? *name() : net_SessionName (); + level_ver = 0; + + // Determine internal level-ID + int level_id = pApp->Level_ID(level_name); + if (level_id<0) { + Disconnect (); + pApp->LoadEnd (); + connected_to_server = FALSE; + m_name = level_name; + m_connect_server_err = xrServer::ErrNoLevel; + return false; + } + pApp->Level_Set (level_id); + m_name = level_name; + // Load level + R_ASSERT2 (Load(level_id),"Loading failed."); + + } + return true; +} + +bool CLevel::net_start_client4 () +{ + if(connected_to_server){ + // Begin spawn + g_pGamePersistent->LoadTitle ("st_client_spawning"); + + // Send physics to single or multithreaded mode + LoadPhysicsGameParams (); + ph_world = new CPHWorld(); + ph_world->Create (); + + // Send network to single or multithreaded mode + // *note: release version always has "mt_*" enabled + Device.seqFrameMT.Remove (g_pNetProcessor); + Device.seqFrame.Remove (g_pNetProcessor); + if (psDeviceFlags.test(mtNetwork)) Device.seqFrameMT.Add (g_pNetProcessor,REG_PRIORITY_HIGH + 2); + else Device.seqFrame.Add (g_pNetProcessor,REG_PRIORITY_LOW - 2); + + if(!psNET_direct_connect) + { + // Waiting for connection/configuration completition + CTimer timer_sync ; timer_sync.Start (); + while (!net_isCompleted_Connect()) Sleep (5); + Msg ("* connection sync: %d ms", timer_sync.GetElapsed_ms()); + while (!net_isCompleted_Sync()) { ClientReceive(); Sleep(5); } + } + + while(!game_configured) + { + ClientReceive(); + if(Server) + Server->Update() ; + Sleep(5); + } +/* + if(psNET_direct_connect) + { + ClientReceive(); + if(Server) + Server->Update() ; + Sleep(5); + }else + + while(!game_configured) + { + ClientReceive(); + if(Server) + Server->Update() ; + Sleep(5); + } +*/ + } + return true; +} + +bool CLevel::net_start_client5 () +{ + if(connected_to_server){ + // HUD + + // Textures + if (!g_dedicated_server && !g_uCommonFlags.test(CF_SkipTextureLoading)) + { + g_pGamePersistent->LoadTitle ("st_loading_textures"); + Device.m_pRender->DeferredLoad (FALSE); + Device.m_pRender->ResourcesDeferredUpload(mt_texture_loading); + LL_CheckTextures (); + } + } + return true; +} + +bool CLevel::net_start_client6 () +{ + if(connected_to_server){ + // Sync + g_pGamePersistent->LoadTitle ("st_loading_hud"); + if (!g_dedicated_server) + { + g_hud->Load (); + g_hud->OnConnected (); + } + + g_pGamePersistent->LoadTitle ("st_client_synchronising"); + Device.PreCache (30, true, true); + net_start_result_total = TRUE; + }else{ + net_start_result_total = FALSE; + } + + pApp->LoadEnd (); + return true; +} \ No newline at end of file diff --git a/src/xrGameLA/Level_start.cpp b/src/xrGameLA/Level_start.cpp new file mode 100644 index 000000000..0678ea345 --- /dev/null +++ b/src/xrGameLA/Level_start.cpp @@ -0,0 +1,322 @@ +#include "stdafx.h" +#include "level.h" +#include "Level_Bullet_Manager.h" +#include "xrserver.h" +#include "game_cl_base.h" +#include "xrmessages.h" +#include "../x_ray.h" +#include "../device.h" +#include "../IGame_Persistent.h" +#include "../xr_ioconsole.h" +#include "MainMenu.h" +#include "UIGameCustom.h" + +/* +shared_str CLevel::OpenDemoFile(LPCSTR demo_file_name) +{ + PrepareToPlayDemo(demo_file_name); + return m_demo_server_options; +} +void CLevel::net_StartPlayDemo() +{ + net_Start(m_demo_server_options.c_str(), "localhost"); +} +*/ + +BOOL CLevel::net_Start ( LPCSTR op_server, LPCSTR op_client ) +{ + net_start_result_total = TRUE; + + pApp->LoadBegin (); + + //make Client Name if options doesn't have it + LPCSTR NameStart = strstr(op_client,"/name="); + if (!NameStart) + { + string512 tmp; + xr_strcpy(tmp, op_client); + xr_strcat(tmp, "/name="); + xr_strcat(tmp, xr_strlen(Core.UserName) ? Core.UserName : Core.CompName); + m_caClientOptions = tmp; + } else { + string1024 ret=""; + LPCSTR begin = NameStart + xr_strlen("/name="); + sscanf (begin, "%[^/]",ret); + if (!xr_strlen(ret)) + { + string1024 tmpstr; + xr_strcpy(tmpstr, op_client); + *(strstr(tmpstr, "name=")+5) = 0; + xr_strcat(tmpstr, xr_strlen(Core.UserName) ? Core.UserName : Core.CompName); + const char* ptmp = strstr(strstr(op_client, "name="), "/"); + if (ptmp) + xr_strcat(tmpstr, ptmp); + m_caClientOptions = tmpstr; + } + else + { + m_caClientOptions = op_client; + }; + }; + m_caServerOptions = op_server; + //--------------------------------------------------------------------- + m_bDemoPlayMode = FALSE; + m_aDemoData.clear(); + m_bDemoStarted = FALSE; + if (strstr(Core.Params,"-tdemo ") || strstr(Core.Params,"-tdemof ")) + { + string1024 f_name; + if (strstr(Core.Params,"-tdemo ")) + { + sscanf (strstr(Core.Params,"-tdemo ")+7,"%[^ ] ",f_name); + m_bDemoPlayByFrame = FALSE; + + Demo_Load (f_name); + } + else + { + sscanf (strstr(Core.Params,"-tdemof ")+8,"%[^ ] ",f_name); + m_bDemoPlayByFrame = TRUE; + + m_lDemoOfs = 0; + Demo_Load_toFrame(f_name, 100, m_lDemoOfs); + }; + } + else + { + if (m_caServerOptions.size() == 0 || !strstr(*m_caServerOptions, "single")) + { + Demo_PrepareToStore(); + } + } + //--------------------------------------------------------------------------- + + if (!loading_save_timer_started) loading_save_timer.Start();//if game also did prefetching, than dont start timer again, since it was started in prefetching stage + loading_save_timer_started = false; + + g_loading_events.push_back (LOADING_EVENT(this,&CLevel::net_start1)); + g_loading_events.push_back (LOADING_EVENT(this,&CLevel::net_start2)); + g_loading_events.push_back (LOADING_EVENT(this,&CLevel::net_start3)); + g_loading_events.push_back (LOADING_EVENT(this,&CLevel::net_start4)); + g_loading_events.push_back (LOADING_EVENT(this,&CLevel::net_start5)); + g_loading_events.push_back (LOADING_EVENT(this,&CLevel::net_start6)); + + return net_start_result_total; + +} + +bool CLevel::net_start1 () +{ + // Start client and server if need it + if (m_caServerOptions.size()) + { + g_pGamePersistent->LoadTitle ("st_server_starting"); + + typedef IGame_Persistent::params params; + params &p = g_pGamePersistent->m_game_params; + // Connect + if (!xr_strcmp(p.m_game_type,"single")) + Server = new xrServer(); + +// if (!strstr(*m_caServerOptions,"/alife")) + if (xr_strcmp(p.m_alife,"alife")) + { + string64 l_name = ""; + string64 l_ver = ""; + const char* SOpts = *m_caServerOptions; + strncpy(l_name, *m_caServerOptions, strchr(SOpts, '/') - SOpts); + // Activate level + if (strchr(l_name,'/')) + *strchr(l_name,'/') = 0; + + m_name = l_name; + + int id = pApp->Level_ID(l_name); + + if (id<0) { + pApp->LoadEnd (); + Log ("Can't find level: ",l_name); + net_start_result_total = FALSE; + return true; + } + pApp->Level_Set (id); + } + } + return true; +} + +bool CLevel::net_start2 () +{ + if (net_start_result_total && m_caServerOptions.size()) + { + if ((m_connect_server_err=Server->Connect(m_caServerOptions))!=xrServer::ErrNoError) + { + net_start_result_total = false; + Msg ("! Failed to start server."); +// Console->Execute("main_menu on"); + return true; + } + Server->SLS_Default (); + m_name = Server->level_name(m_caServerOptions); + } + return true; +} + +bool CLevel::net_start3 () +{ + if(!net_start_result_total) return true; + //add server port if don't have one in options + if (!strstr(m_caClientOptions.c_str(), "port=") && Server) + { + string64 PortStr; + xr_sprintf(PortStr, "/port=%d", Server->GetPort()); + + string4096 tmp; + xr_strcpy(tmp, m_caClientOptions.c_str()); + xr_strcat(tmp, PortStr); + + m_caClientOptions = tmp; + } + //add password string to client, if don't have one + if(m_caServerOptions.size()){ + if (strstr(m_caServerOptions.c_str(), "psw=") && !strstr(m_caClientOptions.c_str(), "psw=")) + { + string64 PasswordStr = ""; + const char* PSW = strstr(m_caServerOptions.c_str(), "psw=") + 4; + if (strchr(PSW, '/')) + strncpy(PasswordStr, PSW, strchr(PSW, '/') - PSW); + else + xr_strcpy(PasswordStr, PSW); + + string4096 tmp; + xr_sprintf(tmp, "%s/psw=%s", m_caClientOptions.c_str(), PasswordStr); + m_caClientOptions = tmp; + }; + }; + //setting players GameSpy CDKey if it comes from command line + if (strstr(m_caClientOptions.c_str(), "/cdkey=")) + { + string64 CDKey; + const char* start = strstr(m_caClientOptions.c_str(),"/cdkey=") +xr_strlen("/cdkey="); + sscanf (start, "%[^/]",CDKey); + string128 cmd; + xr_sprintf(cmd, "cdkey %s", _strupr(CDKey)); + Console->Execute (cmd); + } + return true; +} + +bool CLevel::net_start4 () +{ + if(!net_start_result_total) return true; + + g_loading_events.pop_front(); + + g_loading_events.push_front (LOADING_EVENT(this,&CLevel::net_start_client6)); + g_loading_events.push_front (LOADING_EVENT(this,&CLevel::net_start_client5)); + g_loading_events.push_front (LOADING_EVENT(this,&CLevel::net_start_client4)); + g_loading_events.push_front (LOADING_EVENT(this,&CLevel::net_start_client3)); + g_loading_events.push_front (LOADING_EVENT(this,&CLevel::net_start_client2)); + g_loading_events.push_front (LOADING_EVENT(this,&CLevel::net_start_client1)); + + return false; +} + +bool CLevel::net_start5 () +{ + if (net_start_result_total) + { + NET_Packet NP; + NP.w_begin (M_CLIENTREADY); + Send (NP,net_flags(TRUE,TRUE)); + + if (OnClient() && Server) + { + Server->SLS_Clear(); + }; + }; + return true; +} +#include "hudmanager.h" +BOOL g_start_total_res = TRUE; +xrServer::EConnect g_connect_server_err = xrServer::ErrConnect; + +struct LevelLoadFinalizer +{ +bool xr_stdcall net_start_finalizer() +{ + if(g_pGameLevel && !g_start_total_res) + { + shared_str ln = Level().name(); + Msg ("! Failed to start client. Check the connection or level existance."); + DEL_INSTANCE (g_pGameLevel); + Console->Execute("main_menu on"); + + if (g_connect_server_err==xrServer::ErrBELoad) + { + MainMenu()->OnLoadError("BattlEye/BEServer.dll"); + }else + if(g_connect_server_err==xrServer::ErrConnect && !psNET_direct_connect && !g_dedicated_server) + { + MainMenu()->SwitchToMultiplayerMenu(); + }else + if(g_connect_server_err==xrServer::ErrNoLevel) + { + MainMenu()->SwitchToMultiplayerMenu(); + MainMenu()->OnLoadError(ln.c_str()); + } + } + return true; +} +}; +LevelLoadFinalizer LF; + +bool CLevel::net_start6() +{ + g_start_total_res = net_start_result_total; + g_connect_server_err = m_connect_server_err; + g_loading_events.pop_front (); + g_loading_events.push_front (LOADING_EVENT(&LF, &LevelLoadFinalizer::net_start_finalizer)); + + //init bullet manager + BulletManager().Clear (); + BulletManager().Load (); + + pApp->LoadEnd (); + + if(net_start_result_total) + { + if (strstr(Core.Params,"-$")) + { + string256 buf,cmd,param; + sscanf (strstr(Core.Params,"-$")+2,"%[^ ] %[^ ] ",cmd,param); + strconcat (sizeof(buf),buf,cmd," ",param); + Console->Execute (buf); + } + + if (!g_dedicated_server) + { + if (CurrentGameUI()) + CurrentGameUI()->OnConnected(); + } + } + + return false; +} + +void CLevel::InitializeClientGame (NET_Packet& P) +{ + string256 game_type_name; + P.r_stringZ(game_type_name); + if(game && !xr_strcmp(game_type_name, game->type_name()) ) + return; + + xr_delete(game); + Msg("- Game configuring : Started "); + CLASS_ID clsid = game_GameState::getCLASS_ID(game_type_name,false); + game = smart_cast ( NEW_INSTANCE ( clsid ) ); + game->set_type_name(game_type_name); + game->Init(); + m_bGameConfigStarted = TRUE; +} + diff --git a/src/xrGameLA/MPPlayersBag.cpp b/src/xrGameLA/MPPlayersBag.cpp new file mode 100644 index 000000000..97061fd77 --- /dev/null +++ b/src/xrGameLA/MPPlayersBag.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" + +#include "MPPlayersBag.h" +#include "Level.h" +//#include "xrMessages.h" +#include "game_base_space.h" + +#define BAG_REMOVE_TIME 60000 + +CMPPlayersBag::CMPPlayersBag() +{ +}; + +CMPPlayersBag::~CMPPlayersBag() +{ +}; + +void CMPPlayersBag::OnEvent(NET_Packet& P, u16 type) +{ + CInventoryItemObject::OnEvent (P,type); + u16 id; + switch (type) { + case GE_OWNERSHIP_TAKE : + { + P.r_u16(id); + CObject* O = Level().Objects.net_Find(id); + CInventoryItem* pIItem = smart_cast(O); + R_ASSERT (pIItem->m_pCurrentInventory==NULL); + O->H_SetParent(this); + O->Position().set(Position()); + }break; + case GE_OWNERSHIP_REJECT : + { + P.r_u16 (id); + CObject* O = Level().Objects.net_Find(id); + O->H_SetParent(0,!P.r_eof() && P.r_u8()); + }break; + + } +} + +extern INT g_iWeaponRemove; +bool CMPPlayersBag::NeedToDestroyObject() const +{ + if (H_Parent()) return false; + if (g_iWeaponRemove == -1) return false; + if (g_iWeaponRemove == 0) return true; + return (TimePassedAfterIndependant() > BAG_REMOVE_TIME); +} \ No newline at end of file diff --git a/src/xrGameLA/MPPlayersBag.h b/src/xrGameLA/MPPlayersBag.h new file mode 100644 index 000000000..289507557 --- /dev/null +++ b/src/xrGameLA/MPPlayersBag.h @@ -0,0 +1,16 @@ +#pragma once + +#include "inventory_item_object.h" + +class CMPPlayersBag : + public CInventoryItemObject +{ +public: + CMPPlayersBag(void); + virtual ~CMPPlayersBag(void); + virtual bool NeedToDestroyObject () const; + + virtual void OnEvent(NET_Packet& P, u16 type); +protected: +private: +}; \ No newline at end of file diff --git a/src/xrGameLA/MainMenu.cpp b/src/xrGameLA/MainMenu.cpp new file mode 100644 index 000000000..73c16f98d --- /dev/null +++ b/src/xrGameLA/MainMenu.cpp @@ -0,0 +1,642 @@ +#include "stdafx.h" +#include "MainMenu.h" +#include "UI/UIDialogWnd.h" +#include "ui/UIMessageBoxEx.h" +#include "../xr_IOConsole.h" +#include "../IGame_Level.h" +#include "../CameraManager.h" +#include "xr_Level_controller.h" +#include "ui\UITextureMaster.h" +#include "ui\UIXmlInit.h" +#include +#include "ui\UIBtnHint.h" +#include "UICursor.h" +#include "string_table.h" + +#include "object_broker.h" + +//#define DEMO_BUILD + +string128 ErrMsgBoxTemplate [] = { + "message_box_invalid_pass", + "message_box_invalid_host", + "message_box_session_full", + "message_box_server_reject", + "message_box_cdkey_in_use", + "message_box_cdkey_disabled", + "message_box_cdkey_invalid", + "message_box_different_version", + "message_box_gs_service_not_available", + "message_box_sb_master_server_connect_failed", + "msg_box_no_new_patch", + "msg_box_new_patch", + "msg_box_patch_download_error", + "msg_box_patch_download_success", + "msg_box_connect_to_master_server", + "msg_box_kicked_by_server", + "msg_box_error_loading" +}; + +extern bool b_shniaganeed_pp; + +CMainMenu* MainMenu() {return (CMainMenu*)g_pGamePersistent->m_pMainMenu; }; +//---------------------------------------------------------------------------------- +#define INIT_MSGBOX(_box, _template) { _box = new CUIMessageBoxEx(); _box->InitMessageBox(_template);} +//---------------------------------------------------------------------------------- + +CMainMenu::CMainMenu () +{ + m_Flags.zero (); + m_startDialog = NULL; + m_screenshotFrame = u32(-1); + g_pGamePersistent->m_pMainMenu = this; + if (Device.b_is_Ready) OnDeviceCreate(); + ReadTextureInfo (); + CUIXmlInit::InitColorDefs (); + g_btnHint = NULL; + g_statHint = NULL; + m_deactivated_frame = 0; + + m_sPatchURL = ""; + + m_sPDProgress.IsInProgress = false; + + //--------------------------------------------------------------- + m_NeedErrDialog = ErrNoError; + m_start_time = 0; + + if(!g_dedicated_server) + { + g_btnHint = new CUIButtonHint(); + g_statHint = new CUIButtonHint(); + + for (u32 i=0; iAddCallbackStr("button_yes", MESSAGE_BOX_YES_CLICKED, CUIWndCallback::void_function(this, &CMainMenu::OnRunDownloadedPatch)); + m_pMB_ErrDlgs[PatchDownloadSuccess]->AddCallbackStr("button_yes", MESSAGE_BOX_OK_CLICKED, CUIWndCallback::void_function(this, &CMainMenu::OnConnectToMasterServerOkClicked)); + } + + Device.seqFrame.Add (this,REG_PRIORITY_LOW-1000); + + if (g_uCommonFlags.test(CF_Prefetch_UI)){ + Msg("*Start prefetching UI textures"); + Device.m_pRender->RenderPrefetchUITextures(); + } +} + +CMainMenu::~CMainMenu () +{ + if (g_uCommonFlags.test(CF_Prefetch_UI)){ + ReportTxrsForPrefetching(); + } + Device.seqFrame.Remove (this); + xr_delete (g_btnHint); + xr_delete (g_statHint); + xr_delete (m_startDialog); + g_pGamePersistent->m_pMainMenu = NULL; + delete_data (m_pMB_ErrDlgs); +} + +void CMainMenu::ReadTextureInfo() +{ + if (pSettings->section_exist("texture_desc")) + { + xr_string itemsList; + string256 single_item; + + itemsList = pSettings->r_string("texture_desc", "files"); + int itemsCount = _GetItemCount(itemsList.c_str()); + + for (int i = 0; i < itemsCount; i++) + { + _GetItem(itemsList.c_str(), i, single_item); + xr_strcat(single_item,".xml"); + CUITextureMaster::ParseShTexInfo(single_item); + } + } +} + +extern ENGINE_API BOOL bShowPauseString; +extern bool IsGameTypeSingle(); + +void CMainMenu::Activate (bool bActivate) +{ + if ( !!m_Flags.test(flActive) == bActivate) return; + if ( m_Flags.test(flGameSaveScreenshot) ) return; + if ( (m_screenshotFrame == Device.dwFrame) || + (m_screenshotFrame == Device.dwFrame-1) || + (m_screenshotFrame == Device.dwFrame+1)) return; + + bool b_is_single = IsGameTypeSingle(); + + if(g_dedicated_server && bActivate) return; + + if(bActivate) + { + b_shniaganeed_pp = true; + Device.Pause (TRUE, FALSE, TRUE, "mm_activate1"); + m_Flags.set (flActive|flNeedChangeCapture,TRUE); + + m_Flags.set (flRestoreCursor,GetUICursor().IsVisible()); + + if(!ReloadUI()) return; + + m_Flags.set (flRestoreConsole,Console->bVisible); + + if(b_is_single) m_Flags.set (flRestorePause,Device.Paused()); + + Console->Hide (); + + + if(b_is_single) + { + m_Flags.set (flRestorePauseStr, bShowPauseString); + bShowPauseString = FALSE; + if(!m_Flags.test(flRestorePause)) + Device.Pause (TRUE, TRUE, FALSE, "mm_activate2"); + } + + if(g_pGameLevel) + { + if(b_is_single){ + Device.seqFrame.Remove (g_pGameLevel); + } + Device.seqRender.Remove (g_pGameLevel); + CCameraManager::ResetPP (); + }; + Device.seqRender.Add (this, 4); // 1-console 2-cursor 3-tutorial + + Console->Execute ("stat_memory"); + }else{ + m_deactivated_frame = Device.dwFrame; + m_Flags.set (flActive, FALSE); + m_Flags.set (flNeedChangeCapture, TRUE); + + Device.seqRender.Remove (this); + + bool b = !!Console->bVisible; + if(b){ + Console->Hide (); + } + + IR_Release (); + if(b){ + Console->Show (); + } + + if(m_startDialog->IsShown()) + m_startDialog->HideDialog (); + + CleanInternals (); + if(g_pGameLevel) + { + if(b_is_single){ + Device.seqFrame.Add (g_pGameLevel); + + } + Device.seqRender.Add (g_pGameLevel); + }; + if(m_Flags.test(flRestoreConsole)) + Console->Show (); + + if(b_is_single) + { + if(!m_Flags.test(flRestorePause)) + Device.Pause (FALSE, TRUE, FALSE, "mm_deactivate1"); + + bShowPauseString = m_Flags.test(flRestorePauseStr); + } + + if(m_Flags.test(flRestoreCursor)) + GetUICursor().Show (); + + Device.Pause (FALSE, TRUE, TRUE, "mm_deactivate2"); + + if(m_Flags.test(flNeedVidRestart)) + { + m_Flags.set (flNeedVidRestart, FALSE); + Console->Execute ("vid_restart"); + } + } +} + +bool CMainMenu::ReloadUI() +{ + if(m_startDialog) + { + if(m_startDialog->IsShown()) + m_startDialog->HideDialog (); + CleanInternals (); + } + DLL_Pure* dlg = NEW_INSTANCE(TEXT2CLSID("MAIN_MNU")); + if(!dlg) + { + m_Flags.set (flActive|flNeedChangeCapture,FALSE); + return false; + } + xr_delete (m_startDialog); + m_startDialog = smart_cast(dlg); + VERIFY (m_startDialog); + m_startDialog->m_bWorkInPause= true; + m_startDialog->ShowDialog (true); + + m_activatedScreenRatio = (float)Device.dwWidth/(float)Device.dwHeight > (UI_BASE_WIDTH/UI_BASE_HEIGHT+0.01f); + return true; +} + +bool CMainMenu::IsActive() +{ + return !!m_Flags.test(flActive); +} + +bool CMainMenu::CanSkipSceneRendering() +{ + return IsActive() && !m_Flags.test(flGameSaveScreenshot); +} + +//IInputReceiver +static int mouse_button_2_key [] = {MOUSE_1,MOUSE_2,MOUSE_3}; +void CMainMenu::IR_OnMousePress (int btn) +{ + if(!IsActive()) return; + + IR_OnKeyboardPress(mouse_button_2_key[btn]); +}; + +void CMainMenu::IR_OnMouseRelease(int btn) +{ + if(!IsActive()) return; + + IR_OnKeyboardRelease(mouse_button_2_key[btn]); +}; + +void CMainMenu::IR_OnMouseHold(int btn) +{ + if(!IsActive()) return; + + IR_OnKeyboardHold(mouse_button_2_key[btn]); + +}; + +void CMainMenu::IR_OnMouseMove(int x, int y) +{ + if(!IsActive()) return; + CDialogHolder::IR_UIOnMouseMove(x, y); +}; + +void CMainMenu::IR_OnMouseStop(int x, int y) +{ +}; + +void CMainMenu::IR_OnKeyboardPress(int dik) +{ + if(!IsActive()) return; + + if( is_binded(kCONSOLE, dik) ) + { + Console->Show(); + return; + } + if (DIK_F12 == dik){ + Render->Screenshot(); + return; + } + + CDialogHolder::IR_UIOnKeyboardPress(dik); +}; + +void CMainMenu::IR_OnKeyboardRelease (int dik) +{ + if(!IsActive()) return; + + CDialogHolder::IR_UIOnKeyboardRelease(dik); +}; + +void CMainMenu::IR_OnKeyboardHold(int dik) +{ + if(!IsActive()) return; + + CDialogHolder::IR_UIOnKeyboardHold(dik); +}; + +void CMainMenu::IR_OnMouseWheel(int direction) +{ + if(!IsActive()) return; + + CDialogHolder::IR_UIOnMouseWheel(direction); +} + + +bool CMainMenu::OnRenderPPUI_query() +{ + return IsActive() && !m_Flags.test(flGameSaveScreenshot) && b_shniaganeed_pp; +} + + +extern void draw_wnds_rects(); +void CMainMenu::OnRender () +{ + if(m_Flags.test(flGameSaveScreenshot)) + return; + + if(g_pGameLevel) + Render->Calculate (); + + Render->Render (); + if(!OnRenderPPUI_query()) + { + DoRenderDialogs(); + UI().RenderFont(); + draw_wnds_rects(); + } +} + +void CMainMenu::OnRenderPPUI_main () +{ + if(!IsActive()) return; + + if(m_Flags.test(flGameSaveScreenshot)) + return; + + UI().pp_start(); + + if(OnRenderPPUI_query()) + { + DoRenderDialogs(); + UI().RenderFont(); + } + + UI().pp_stop(); +} + +void CMainMenu::OnRenderPPUI_PP () +{ + if ( !IsActive() ) return; + + if(m_Flags.test(flGameSaveScreenshot)) return; + + UI().pp_start(); + + xr_vector::iterator it = m_pp_draw_wnds.begin(); + for(; it!=m_pp_draw_wnds.end();++it) + { + (*it)->Draw(); + } + UI().pp_stop(); +} +/* +void CMainMenu::StartStopMenu(CUIDialogWnd* pDialog, bool bDoHideIndicators) +{ + pDialog->m_bWorkInPause = true; + CDialogHolder::StartStopMenu(pDialog, bDoHideIndicators); +}*/ + +//pureFrame +void CMainMenu::OnFrame() +{ + if (m_Flags.test(flNeedChangeCapture)) + { + m_Flags.set (flNeedChangeCapture,FALSE); + if (m_Flags.test(flActive)) IR_Capture(); + else IR_Release(); + } + CDialogHolder::OnFrame (); + + + //screenshot stuff + if(m_Flags.test(flGameSaveScreenshot) && Device.dwFrame > m_screenshotFrame ) + { + m_Flags.set (flGameSaveScreenshot,FALSE); + ::Render->Screenshot (IRender_interface::SM_FOR_GAMESAVE, m_screenshot_name); + + if(g_pGameLevel && m_Flags.test(flActive)) + { + Device.seqFrame.Remove (g_pGameLevel); + Device.seqRender.Remove (g_pGameLevel); + }; + + if(m_Flags.test(flRestoreConsole)) + Console->Show (); + } + + if(IsActive()) + { + CheckForErrorDlg(); + bool b_is_16_9 = (float)Device.dwWidth/(float)Device.dwHeight > (UI_BASE_WIDTH/UI_BASE_HEIGHT+0.01f); + if(b_is_16_9 !=m_activatedScreenRatio) + { + ReloadUI(); + m_startDialog->SendMessage(m_startDialog, MAIN_MENU_RELOADED, NULL); + } + } +} + +void CMainMenu::OnDeviceCreate() +{ +} + + +void CMainMenu::Screenshot(IRender_interface::ScreenshotMode mode, LPCSTR name) +{ + if(mode != IRender_interface::SM_FOR_GAMESAVE) + { + ::Render->Screenshot (mode,name); + }else{ + m_Flags.set (flGameSaveScreenshot, TRUE); + xr_strcpy(m_screenshot_name,name); + if(g_pGameLevel && m_Flags.test(flActive)){ + Device.seqFrame.Add (g_pGameLevel); + Device.seqRender.Add (g_pGameLevel); + }; + m_screenshotFrame = Device.dwFrame+1; + m_Flags.set (flRestoreConsole, Console->bVisible); + Console->Hide (); + } +} + +void CMainMenu::RegisterPPDraw(CUIWindow* w) +{ + UnregisterPPDraw (w); + m_pp_draw_wnds.push_back (w); +} + +void CMainMenu::UnregisterPPDraw (CUIWindow* w) +{ + m_pp_draw_wnds.erase( + std::remove( + m_pp_draw_wnds.begin(), + m_pp_draw_wnds.end(), + w + ), + m_pp_draw_wnds.end() + ); +} + +void CMainMenu::SetErrorDialog (EErrorDlg ErrDlg) +{ + m_NeedErrDialog = ErrDlg; +}; + +void CMainMenu::CheckForErrorDlg() +{ + if (m_NeedErrDialog == ErrNoError) return; + m_pMB_ErrDlgs[m_NeedErrDialog]->ShowDialog(false); + m_NeedErrDialog = ErrNoError; +}; + +void CMainMenu::SwitchToMultiplayerMenu() +{ + +}; + +void CMainMenu::DestroyInternal(bool bForce) +{ + if(m_startDialog && ((m_deactivated_frame < Device.dwFrame+4)||bForce) ) + xr_delete (m_startDialog); +} + +void CMainMenu::OnNewPatchFound(LPCSTR VersionName, LPCSTR URL) +{ + +}; + +void CMainMenu::OnNoNewPatchFound () +{ + +} + +void CMainMenu::OnDownloadPatch(CUIWindow*, void*) +{ + + +} + +void CMainMenu::OnDownloadPatchError() +{ + +}; + +void CMainMenu::OnDownloadPatchSuccess () +{ + +} + +void CMainMenu::OnSessionTerminate(LPCSTR reason) +{ + if ( m_NeedErrDialog == SessionTerminate && (Device.dwTimeGlobal - m_start_time) < 8000 ) + return; + + m_start_time = Device.dwTimeGlobal; + CStringTable st; + LPCSTR str = st.translate("ui_st_kicked_by_server").c_str(); + LPSTR text; + + if ( reason && xr_strlen(reason) && reason[0] == '@' ) + { + STRCONCAT( text, reason + 1 ); + } + else + { + STRCONCAT( text, str, " ", reason ); + } + + m_pMB_ErrDlgs[SessionTerminate]->SetText(st.translate(text).c_str()); + SetErrorDialog(CMainMenu::SessionTerminate); +} + +void CMainMenu::OnLoadError(LPCSTR module) +{ + LPCSTR str=CStringTable().translate("ui_st_error_loading").c_str(); + string1024 Text; + strconcat(sizeof(Text),Text,str," "); + xr_strcat(Text,sizeof(Text),module); + m_pMB_ErrDlgs[LoadingError]->SetText(Text); + SetErrorDialog(CMainMenu::LoadingError); +} + +void CMainMenu::OnDownloadPatchProgress (u64 bytesReceived, u64 totalSize) +{ + m_sPDProgress.Progress = (float(bytesReceived)/float(totalSize))*100.0f; +}; + +extern ENGINE_API string512 g_sLaunchOnExit_app; +extern ENGINE_API string512 g_sLaunchOnExit_params; +void CMainMenu::OnRunDownloadedPatch (CUIWindow*, void*) +{ + xr_strcpy (g_sLaunchOnExit_app,*m_sPatchFileName); + xr_strcpy (g_sLaunchOnExit_params,""); + Console->Execute ("quit"); +} + +void CMainMenu::CancelDownload() +{ + +} + +void CMainMenu::SetNeedVidRestart() +{ + m_Flags.set(flNeedVidRestart,TRUE); +} + +void CMainMenu::OnDeviceReset() +{ + if(IsActive() && g_pGameLevel) + SetNeedVidRestart(); +} + +extern void GetCDKey(char* CDKeyStr); +//extern int VerifyClientCheck(const char *key, unsigned short cskey); + +bool CMainMenu::IsCDKeyIsValid() +{ + + return true; +} + +bool CMainMenu::ValidateCDKey () +{ + if (IsCDKeyIsValid()) return true; + SetErrorDialog(CMainMenu::ErrCDKeyInvalid); + return false; +} + +void CMainMenu::Show_CTMS_Dialog () +{ + if (!m_pMB_ErrDlgs[ConnectToMasterServer]) return; + if (m_pMB_ErrDlgs[ConnectToMasterServer]->IsShown()) return; + m_pMB_ErrDlgs[ConnectToMasterServer]->ShowDialog(false); +} + +void CMainMenu::Hide_CTMS_Dialog() +{ + if (!m_pMB_ErrDlgs[ConnectToMasterServer]) return; + if (!m_pMB_ErrDlgs[ConnectToMasterServer]->IsShown()) return; + m_pMB_ErrDlgs[ConnectToMasterServer]->HideDialog(); +} + +void CMainMenu::OnConnectToMasterServerOkClicked(CUIWindow*, void*) +{ + Hide_CTMS_Dialog(); +} + +LPCSTR CMainMenu::GetGSVer() +{ + // Empty string, because we have no GameSpy. + return ""; +} + +void CMainMenu::ReportTxrsForPrefetching() +{ + if (SuggestedForPrefetching.size() > 0){ + Msg("---These UI textures are suggested to be prefetched since they caused stutterings when some UI window was loading"); + Msg("---Add this list to prefetch_ui_textures.ltx (wisely)"); + for (u32 i = 0; i < SuggestedForPrefetching.size(); i++){ + Msg("%s", SuggestedForPrefetching[i].c_str()); + } + } +} diff --git a/src/xrGameLA/MainMenu.h b/src/xrGameLA/MainMenu.h new file mode 100644 index 000000000..3c48f27ce --- /dev/null +++ b/src/xrGameLA/MainMenu.h @@ -0,0 +1,160 @@ +#pragma once + +class CUIWindow; +class CUIDialogWnd; +class CUICursor; +class CUIMessageBoxEx; +#include "../IInputReceiver.h" +#include "../IGame_Persistent.h" +#include "UIDialogHolder.h" +#include "ui/UIWndCallback.h" +#include "ui_base.h" + +struct Patch_Dawnload_Progress{ + bool IsInProgress; + float Progress; + shared_str Status; + shared_str FileName; + + bool GetInProgress (){return IsInProgress;}; + float GetProgress (){return Progress;}; + LPCSTR GetStatus (){return Status.c_str();}; + LPCSTR GetFlieName (){return FileName.c_str();}; +}; + +class CMainMenu : + public IMainMenu, + public IInputReceiver, + public pureRender, + public CDialogHolder, + public CUIWndCallback, + public CDeviceResetNotifier + +{ + CUIDialogWnd* m_startDialog; + + + enum{ + flRestoreConsole = (1<<0), + flRestorePause = (1<<1), + flRestorePauseStr = (1<<2), + flActive = (1<<3), + flNeedChangeCapture = (1<<4), + flRestoreCursor = (1<<5), + flGameSaveScreenshot= (1<<6), + flNeedVidRestart = (1<<7), + }; + Flags16 m_Flags; + string_path m_screenshot_name; + u32 m_screenshotFrame; + void ReadTextureInfo (); + + + xr_vector m_pp_draw_wnds; + +public: + enum EErrorDlg + { + ErrInvalidPassword, + ErrInvalidHost, + ErrSessionFull, + ErrServerReject, + ErrCDKeyInUse, + ErrCDKeyDisabled, + ErrCDKeyInvalid, + ErrDifferentVersion, + ErrGSServiceFailed, + ErrMasterServerConnectFailed, + NoNewPatch, + NewPatchFound, + PatchDownloadError, + PatchDownloadSuccess, + ConnectToMasterServer, + SessionTerminate, + LoadingError, + ErrMax, + ErrNoError = ErrMax, + }; + + Patch_Dawnload_Progress m_sPDProgress; + Patch_Dawnload_Progress* GetPatchProgress () {return &m_sPDProgress;} + void CancelDownload (); +protected: + EErrorDlg m_NeedErrDialog; + u32 m_start_time; + + shared_str m_sPatchURL; + shared_str m_sPatchFileName; + + xr_vector m_pMB_ErrDlgs; + bool ReloadUI (); +public: + u32 m_deactivated_frame; + bool m_activatedScreenRatio; + virtual void DestroyInternal (bool bForce); + CMainMenu (); + virtual ~CMainMenu (); + + virtual void Activate (bool bActive); + virtual bool IsActive (); + virtual bool CanSkipSceneRendering (); + + virtual bool IgnorePause () {return true;} + virtual bool NeedCenterCursor () const {return true;} + + virtual void IR_OnMousePress (int btn); + virtual void IR_OnMouseRelease (int btn); + virtual void IR_OnMouseHold (int btn); + virtual void IR_OnMouseMove (int x, int y); + virtual void IR_OnMouseStop (int x, int y); + + virtual void IR_OnKeyboardPress (int dik); + virtual void IR_OnKeyboardRelease (int dik); + virtual void IR_OnKeyboardHold (int dik); + + virtual void IR_OnMouseWheel (int direction) ; + + bool OnRenderPPUI_query (); + void OnRenderPPUI_main (); + void OnRenderPPUI_PP (); + + virtual void OnRender (); + virtual void _BCL OnFrame (void); + + virtual bool UseIndicators () {return false;} + + void OnDeviceCreate (); + + void Screenshot (IRender_interface::ScreenshotMode mode=IRender_interface::SM_NORMAL, LPCSTR name = 0); + void RegisterPPDraw (CUIWindow* w); + void UnregisterPPDraw (CUIWindow* w); + + void SetErrorDialog (EErrorDlg ErrDlg); + EErrorDlg GetErrorDialogType () const { return m_NeedErrDialog; } ; + void CheckForErrorDlg (); + void SwitchToMultiplayerMenu (); + void OnNewPatchFound (LPCSTR VersionName, LPCSTR URL); + void OnNoNewPatchFound (); + void xr_stdcall OnDownloadPatch (CUIWindow*, void*); + void xr_stdcall OnConnectToMasterServerOkClicked(CUIWindow*, void*); + void OnSessionTerminate (LPCSTR reason); + void OnLoadError (LPCSTR module); + void OnDownloadPatchError (); + void OnDownloadPatchSuccess (); + void OnDownloadPatchProgress (u64 bytesReceived, u64 totalSize); + void xr_stdcall OnRunDownloadedPatch (CUIWindow*, void*); + void Show_CTMS_Dialog (); + void Hide_CTMS_Dialog (); + +xr_vector SuggestedForPrefetching; + + void SetNeedVidRestart (); + virtual void OnDeviceReset (); + LPCSTR GetGSVer (); + bool ValidateCDKey (); + bool IsCDKeyIsValid(); + //Dump UI Texture list that are suggested to be prefetched + void ReportTxrsForPrefetching(); +}; + +extern CMainMenu* MainMenu(); \ No newline at end of file diff --git a/src/xrGameLA/MathUtils.cpp b/src/xrGameLA/MathUtils.cpp new file mode 100644 index 000000000..8c86a4f3f --- /dev/null +++ b/src/xrGameLA/MathUtils.cpp @@ -0,0 +1,485 @@ +#include "stdafx.h" +#include "mathutils.h" +#include "..\..\xrODE\include\ode\common.h" +/* +#include "MathUtils.h" +enum EBoxSideNearestPointCode +{ + box_inside , + side_invisible , + on_side , + on_edge , + on_vertex +}; +EBoxSideNearestPointCode GetNearestPointOnOBBSide(const Fmatrix &xform,const Fvector ¢er,const Fvector &sides,u16 side,const Fvector &p,Fvector &point) +{ + //to plane dist + const Fvector &norm=xform[side]; + u16 side1=(side+1)%3,side2=(side+2)%3; + float h=sides[side],h1=sides[side1],h2=sides[side2]; + //Fvector vdiffc;vdiffc.sub(center,p); + float c_prg=norm.dotproduct(center); + float p_prg=norm.dotproduct(p); + float diffc=c_prg-p_prg;norm.dotproduct(vdiffc); + float diffs; + if(diffc<0.f) + { + diffs=diffc+h; + point.set(norm); + point.mul(diffs); + point.add(p); + Fvector d;d.sub(center,point); + bool inside1 =_abs(d[side1])0.f) + { + if(inside1&&inside2) return box_inside; + else return side_invisible; + } + else + { + if(inside1&&inside2) return on_side; + else if(inside1) + { + float dd=h2-_abs(d[side2]); + Fvector s;s.set(xform[side2]);s. + } + } + } + float diffs=diffc<0.f ? diffc+h : diffc-h; +} +*/ +IC bool RAYvsCYLINDER(const Fcylinder& c_cylinder, const Fvector &S, const Fvector &D, float &R, BOOL bCull) +{ + + const float &r=c_cylinder.m_radius; + float h=c_cylinder.m_height/2.f; + const Fvector& p=S; + const Fvector& dir=D; + + const Fvector &c=c_cylinder.m_center; + const Fvector &ax=c_cylinder.m_direction; + //c.set(-IM.c.dotproduct(IM.i),-IM.c.dotproduct(IM.j),-IM.c.dotproduct(IM.k)); + //Fvector ax;ax.set(IM.i.z,IM.j.z,IM.k.z);//?? + +////////////////////////////////////////////////////////////// + Fvector v; v .sub (c,p) ; + float cs =dir .dotproduct (ax) ; + float Lc =v .dotproduct (ax) ; + float Lr =v .dotproduct (dir) ; + //////////////////////////////////////////////// + float sq_cos=cs*cs; + float sq_sin=1-sq_cos; + float v_smag=v.square_magnitude(); + const float sq_r=r*r; + + if(sq_sinsq_r) return false; + float r_dist=_sqrt(sq_r-sq_dist)+h; + tr1=Lr-r_dist; + + if(tr1>R) return false;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + tr2=Lr+r_dist; + if(tr2<0.f) return false;// + if(tr2h+r)return false; + float sq_dist=v_smag-Lr*Lr-Lc*Lc; + if(sq_dist>sq_r) return false; + float lc_h=abs_c_dist-h; + if(lc_h>0.f) + { + float sq_sphere_dist=lc_h*lc_h+sq_dist*sq_dist; + if(sq_sphere_dist>sq_r)return false; + float diff=_sqrt(sq_r-sq_sphere_dist); + tr1=Lr-diff; + if(tr1>R) return false;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + tr2=Lr+diff; + if(tr2<0.f) return false;// + if(tr2R) return false;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + tr2=Lr+diff; + if(tr2<0.f) return false;// + if(tr2 radius + //v^2+tc^2+tr^2-2*(cos*tc*tr-Lc*tc+Lr*tr) + + float sq_nearest_dist=v_smag+tr*tr+tc*tc-2*(cs*tc*tr-Lc*tc+Lr*tr); + + if(sq_nearest_dist>sq_r) return false; + //float max_c_diff=//; + + float sq_horde=(sq_r-sq_nearest_dist) ; + + //float horde=_sqrt(sq_horde) ; + float sq_c_diff=sq_horde*sq_cos*r_sq_sin ; + float c_diff=_sqrt(sq_c_diff) ;//ccc + float cp1=tc-c_diff ; + float cp2=tc+c_diff ; + + + //cp1h) + { + //sphere + float tc_h=tc-h;//!! hi (=)/; + float sq_sphere_dist=sq_sin*tc_h*tc_h; + if(sq_sphere_dist>sq_horde)return false; + float tr_c=tr-tc_h*cs;// + float diff=_sqrt(sq_horde-sq_sphere_dist); + tr1=tr_c-diff; + if(tr1>R) return false;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + tr2=tr_c+diff; + if(tr2<0.f) return false;// + if(tr2sq_horde)return false; + float tr_c=tr-tc_h*cs;//!! + float diff=_sqrt(sq_horde-sq_sphere_dist); + tr1=tr_c-diff; + if(tr1>R) return false;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + tr2=tr_c+diff; + if(tr2<0.f) return false;// + if(tr20.f) + { + if(cp1 >-h) + { + if(cp2R) return false;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + tr2=tr+diff; + if(tr2<0.f) return false;// + if(tr2R) return false;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + float tc_h=tc-h ; + float sq_sphere_dist=sq_sin*tc_h*tc_h; + //if(sq_sphere_dist>sq_horde)return false ; + float tr_c=tr-tc_h*cs ; + float diff=_sqrt(sq_horde-sq_sphere_dist) ; + tr2=tr_c+diff ; + if(tr2<0.f) return false ;// + if(tr2sq_horde)return false; + float diff=_sqrt(sq_horde-sq_sphere_dist) ; + float tr_c=tr-tc_h*cs ; + tr1=tr_c-diff ; + if(tr1>R) return false ;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + float diff=c_diff/cs ; + tr2=tr+diff ; + if(tr2<0.f) return false ;// + if(tr2R) return false ;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + float tc_h=tc-h ; + float tr_c=tr-tc_h*cs ; + float diff=_sqrt(sq_horde-sq_sin*tc_h*tc_h) ; + tr2=tr_c+diff ; + if(tr2-h) + { + if(cp2R) return false;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + tr2=tr+diff; + if(tr2<0.f) return false;// + if(tr2-h&&cp2>h + + + float tc_h=tc-h; //hi sphere/cyl + float tr_c=tr-tc_h*cs; + float diff=_sqrt(sq_horde-sq_sin*tc_h*tc_h); + tr1=tr_c-diff; + if(tr1>R) return false ;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + diff=-c_diff/cs; + tr2=tr+diff; + if(tr2<0.f) return false;// + if(tr2R) return false ;// + if(tr1<0.f) + { + if(bCull)return false; + else{ + //mixed//lo + float tc_h=tc+h; + float tr_c=tr-tc_h*cs; + diff=_sqrt(sq_horde-sq_sin*tc_h*tc_h); + tr2=tr_c+diff; + if(tr2<0.f) return false;// + if(tr2=h + { + //-(--)- //sphere hi&&lo + + float tc_h=tc-h ; + float tr_c=tr-tc_h*cs ; + float diff=_sqrt(sq_horde-sq_sin*tc_h*tc_h) ; + tr1=tr_c-diff ; + if(tr1>R) return false ;// + ///////////////////////////////////////////// + if(tr1<0.f) + { + if(bCull)return false; + else{ + tc_h=tc+h ; + tr_c=tr-tc_h*cs ; + diff=_sqrt(sq_horde-sq_sin*tc_h*tc_h) ; + tr2=tr_c+diff ; + if(tr2<0.f) return false ;// + if(tr2EPS) k*=acos(cosinus)/_sqrt(sinus_2); + w.mul(k); +} + +IC float to_mag_and_dir(const Fvector &in_v,Fvector &out_v) +{ + float mag=in_v.magnitude(); + if(!fis_zero(mag)) + out_v.mul(in_v,1.f/mag); + else + out_v.set(0.f,0.f,0.f); + return mag; +} + +IC float to_mag_and_dir(Fvector &in_out_v) +{ + return to_mag_and_dir(in_out_v,in_out_v); +} + +IC void to_vector(Fvector &v,float mag,const Fvector dir) +{ + v.mul(dir,mag); +} + +IC void prg_pos_on_axis(const Fvector &in_ax_p,const Fvector &in_ax_d,Fvector &in_out_pos) +{ + in_out_pos.sub(in_ax_p); + float ax_mag=in_ax_d.magnitude(); + float prg=in_out_pos.dotproduct(in_ax_d)/ax_mag; + in_out_pos.set(in_ax_d); + in_out_pos.mul(prg/ax_mag); + in_out_pos.add(in_ax_p); +} +IC float prg_pos_on_plane(const Fvector &in_norm,float d,const Fvector &in_pos,Fvector &out_pos) +{ + float prg=d-in_pos.dotproduct(in_norm); + Fvector diff;diff.set(in_norm);diff.mul(prg); + out_pos.add(in_pos,diff); + return prg; +} + +IC void prg_on_normal( const Fvector &in_norm, const Fvector &in_dir, Fvector &out_dir ) +{ + float prg=-in_dir.dotproduct(in_norm); + Fvector diff;diff.set(in_norm);diff.mul(prg); + out_dir.add(in_dir,diff); + return; +} + +IC void restrict_vector_in_dir(Fvector& V,const Fvector& dir) +{ + Fvector sub;sub.set(dir); + float dotpr =dir.dotproduct(V); + if(dotpr>0.f) + { + sub.mul(dotpr); + V.sub(sub); + } +} +IC bool check_obb_sise(const Fobb& obb) +{ + return (!fis_zero(obb.m_halfsize.x,EPS_L)|| + !fis_zero(obb.m_halfsize.y,EPS_L)|| + !fis_zero(obb.m_halfsize.z,EPS_L) + ); + +} + +IC float fsignum(float val) +{ + return val<0.f ? -1.f : 1.f ; +} + +IC void save_max(float& max,float val) +{ + if(val>max)max=val; +} +IC void save_min(float& min,float val) +{ + if(vallimit)val=limit; +} + +IC void limit_below(float& val,float limit) +{ + if(valy){\ + if (x>z) {on_x;}\ + else {on_z;}\ + }\ + else\ +{\ + if (y>z) {on_y;}\ + else {on_z;}\ +} + +#define MIN_OF(x,on_x,y,on_y,z,on_z)\ + if(xz){ on_x1;on_z2;}else{on_z1;on_x2;}}\ + else {on_x1;on_y2;}\ +} + +#define SORT(x,on_x1,on_x2,on_x3,y,on_y1,on_y2,on_y3,z,on_z1,on_z2,on_z3)\ + if(xz){ on_x1;on_z2;on_y3;}else{on_z1;on_x2;on_y3;}}\ + else {on_x1;on_y2;on_z3;}\ +} +////////////////////////////////////////////////////////////////////////////////////// + +struct SInertVal +{ + float val; +const float inertion; + SInertVal(float inert): inertion (inert){R_ASSERT(inert>0.f&&inert<1.f);} +IC void new_val (float new_val) + { + val=inertion*val+(1-inertion)*new_val; + } +private: + SInertVal& operator = (SInertVal& v) + { + R_ASSERT(false); + } +}; + +IC float DET(const Fmatrix &a){ + return + (( a._11 * ( a._22 * a._33 - a._23 * a._32 ) - + a._12 * ( a._21 * a._33 - a._23 * a._31 ) + + a._13 * ( a._21 * a._32 - a._22 * a._31 ) )); +} + +IC bool valid_pos(const Fvector &P,const Fbox &B){ + Fbox BB=B;BB.grow(100000); + return !!BB.contains(P) ; +} + + + +#ifdef DEBUG +const float DET_CHECK_EPS =0.15f ;//scale -35% !? ;) +const float DET_CHECK_FATAL_EPS =0.8f ;//scale -35% !? ;) +#define VERIFY_RMATRIX(M) {\ + float d=DET(M);\ + if( !fsimilar(d,1.f,DET_CHECK_EPS) ){\ + \ + Msg("! matrix: %s ",get_string(M).c_str()); \ + Msg("! determinant: %f ",d); \ + Msg("! Is not valid rotational matrix");\ + VERIFY(fsimilar(d,1.f,DET_CHECK_FATAL_EPS));\ + }}; +#else +#define VERIFY_RMATRIX(M) +#endif +#endif \ No newline at end of file diff --git a/src/xrGameLA/MathUtilsOde.h b/src/xrGameLA/MathUtilsOde.h new file mode 100644 index 000000000..33df0d9f4 --- /dev/null +++ b/src/xrGameLA/MathUtilsOde.h @@ -0,0 +1,109 @@ +#pragma once + +#pragma warning(disable:4995) +#pragma warning(disable:4267) +#include "../../xrODE/include/ode/common.h" +#include "../../xrODE/include/ode/odemath.h" +#include "../../xrODE/include/ode/objects.h" +#include "../../xrODE/include/ode/rotation.h" +#include "../../xrODE/include/ode/compatibility.h" +#include "../../xrODE/include/ode/collision.h" +#include "../../xrODE/include/ode/matrix.h" + +#include "mathutils.h" +#include "ode_redefine.h" + +static const dReal accurate_normalize_epsilon = 1.192092896e-05F; + +ICF void accurate_normalize(float* a) +{ + dReal sqr_magnitude = a[0]*a[0] + a[1]*a[1] + a[2]*a[2]; + + if (sqr_magnitude > accurate_normalize_epsilon) + { + dReal l = dRecipSqrt(sqr_magnitude); + a[0] *= l; + a[1] *= l; + a[2] *= l; + return; + } + + dReal a0,a1,a2,aa0,aa1,aa2,l; + VERIFY (a); + a0 = a[0]; + a1 = a[1]; + a2 = a[2]; + aa0 = dFabs(a0); + aa1 = dFabs(a1); + aa2 = dFabs(a2); + if (aa1 > aa0) { + if (aa2 > aa1) { + goto aa2_largest; + } + else { // aa1 is largest + a0 /= aa1; + a2 /= aa1; + l = dRecipSqrt (a0*a0 + a2*a2 + 1); + a[0] = a0*l; + a[1] = (float)_copysign(l,a1); + a[2] = a2*l; + } + } + else { + if (aa2 > aa0) { +aa2_largest: // aa2 is largest + a0 /= aa2; + a1 /= aa2; + l = dRecipSqrt (a0*a0 + a1*a1 + 1); + a[0] = a0*l; + a[1] = a1*l; + a[2] = (float)_copysign(l,a2); + } + else { // aa0 is largest + if (aa0 <= 0) { + // dDEBUGMSG ("vector has zero size"); ... this messace is annoying + a[0] = 1; // if all a's are zero, this is where we'll end up. + a[1] = 0; // return a default unit length vector. + a[2] = 0; + return; + } + a1 /= aa0; + a2 /= aa0; + l = dRecipSqrt (a1*a1 + a2*a2 + 1); + a[0] = (float)_copysign(l,a0); + a[1] = a1*l; + a[2] = a2*l; + } + } +} + +IC bool dVectorLimit(const float* v,float l,float* lv) +{ + float mag = _sqrt(dDOT(v,v)); + if(mag>l) + { + float f=mag/l; + lv[0]=v[0]/f;lv[1]=v[1]/f;lv[2]=v[2]/f; + return true; + } + else + { + dVectorSet(lv,v); + return false; + } +} + +IC void dVectorInterpolate(float* res,const float* from,const float* to,float k) //changes to +{ + dVector3 tov; + dVectorSet(res,from); + dVectorSet(tov,to); + dVectorInterpolate(res,tov,k); +} + +float E_NL( dBodyID b1, dBodyID b2, const dReal* norm ); + +float E_NlS( dBodyID body, const dReal* norm, float norm_sign );//if body c.geom.g1 norm_sign + else - + +#pragma warning(default:4995) +#pragma warning(default:4267) \ No newline at end of file diff --git a/src/xrGameLA/MercuryBall.cpp b/src/xrGameLA/MercuryBall.cpp new file mode 100644 index 000000000..b5f2fc4c2 --- /dev/null +++ b/src/xrGameLA/MercuryBall.cpp @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////// +// MercuryBall.cpp +// MercuryBall - переливающийся и колыхающийся шар +// перекатывается с места на место +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "MercuryBall.h" +#include "PhysicsShell.h" + + +CMercuryBall::CMercuryBall(void) +{ + m_timeLastUpdate = 0; + m_timeToUpdate = 1000; + + m_fImpulseMin = 45.f; + m_fImpulseMax = 90.f; +} + +CMercuryBall::~CMercuryBall(void) +{ +} + +void CMercuryBall::Load(LPCSTR section) +{ + inherited::Load(section); + + m_timeToUpdate = pSettings->r_u32(section,"time_to_update"); + m_fImpulseMin = pSettings->r_float(section,"impulse_min"); + m_fImpulseMax = pSettings->r_float(section,"impulse_max"); +} + + + +void CMercuryBall::UpdateCLChild() +{ + if (getVisible() && m_pPhysicsShell) { + if(Device.TimerAsync() - m_timeLastUpdate> m_timeToUpdate) + { + m_timeLastUpdate = Device.TimerAsync(); + + if(::Random.randF(0.f, 1.0f)>0.6f) + { + Fvector dir; + dir.set(::Random.randF(-0.5f, 0.5f), 0.0f, ::Random.randF(-0.5f, 0.5f)); + m_pPhysicsShell->applyImpulse(dir, ::Random.randF(m_fImpulseMin, m_fImpulseMax) * + Device.fTimeDelta * + m_pPhysicsShell->getMass()); + } + } + } + else if(H_Parent()) XFORM().set(H_Parent()->XFORM()); +} \ No newline at end of file diff --git a/src/xrGameLA/MercuryBall.h b/src/xrGameLA/MercuryBall.h new file mode 100644 index 000000000..2bc89c002 --- /dev/null +++ b/src/xrGameLA/MercuryBall.h @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////// +// MercuryBall.h +// MercuryBall - переливающийся и колыхающийся шар +// перекатывается с места на место +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CMercuryBall : public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CMercuryBall(void); + virtual ~CMercuryBall(void); + + virtual void Load (LPCSTR section); +protected: + virtual void UpdateCLChild (); + + //время последнего обновления поведения шара + ALife::_TIME_ID m_timeLastUpdate; + //время между апдейтами + ALife::_TIME_ID m_timeToUpdate; + + //диапазон импульсов катания шара + float m_fImpulseMin; + float m_fImpulseMax; +}; + +/* + +#pragma once +#include "gameobject.h" +#include "PhysicsShell.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Ртутный шар +// Появляется после выброса, держится недолго, после чего испаряется. +// Цены: от 50 до 200 рублей, в зависимости от размера +// Специфика: опасное аномальное образование, хранить только в защищенном контейнере, +// например в капсуле R1. +class CMercuryBall : public CGameObject { +typedef CGameObject inherited; +public: + CMercuryBall(void); + virtual ~CMercuryBall(void); + + virtual void OnH_A_Chield(); + virtual void OnH_B_Independent(bool just_before_destroy); + + + virtual BOOL net_Spawn (CSE_Abstract* DC); +}; +*/ \ No newline at end of file diff --git a/src/xrGameLA/MilitaryOutfit.cpp b/src/xrGameLA/MilitaryOutfit.cpp new file mode 100644 index 000000000..3ef98f524 --- /dev/null +++ b/src/xrGameLA/MilitaryOutfit.cpp @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////// +// MilitaryOutfit.h +// MilitaryOutfit - защитный костюм военного +/////////////////////////////////////////////////////////////// + +#pragma once + +#include "stdafx.h" +#include "MilitaryOutfit.h" + +CMilitaryOutfit::CMilitaryOutfit() +{ +} + +CMilitaryOutfit::~CMilitaryOutfit() +{ +} \ No newline at end of file diff --git a/src/xrGameLA/MilitaryOutfit.h b/src/xrGameLA/MilitaryOutfit.h new file mode 100644 index 000000000..ec558874d --- /dev/null +++ b/src/xrGameLA/MilitaryOutfit.h @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////// +// MilitaryOutfit.h +// MilitaryOutfit - защитный костюм военного +/////////////////////////////////////////////////////////////// + + +#pragma once + +#include "customoutfit.h" + +class CMilitaryOutfit: public CCustomOutfit +{ +private: + typedef CCustomOutfit inherited; +public: + CMilitaryOutfit(void); + virtual ~CMilitaryOutfit(void); +}; diff --git a/src/xrGameLA/Mincer.cpp b/src/xrGameLA/Mincer.cpp new file mode 100644 index 000000000..ccb6babc4 --- /dev/null +++ b/src/xrGameLA/Mincer.cpp @@ -0,0 +1,178 @@ +#include "stdafx.h" +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "mincer.h" +#include "hudmanager.h" +#include "xrmessages.h" +#include "level.h" +#include "CustomZone.h" +#include "clsid_game.h" +#include "entity_alive.h" +#include "PHDestroyableNotificate.h" +#include "clsid_game.h" +#include "car.h" + +CMincer::CMincer(void) +{ + m_fActorBlowoutRadiusPercent=0.5f; +} + +CMincer::~CMincer(void) +{ +} +void CMincer::OnStateSwitch(EZoneState new_state) +{ + if(m_eZoneState!=eZoneStateBlowout && new_state==eZoneStateBlowout) + { + OBJECT_INFO_VEC_IT it; + for(it = m_ObjectInfoMap.begin(); m_ObjectInfoMap.end() != it; ++it) + { + CPhysicsShellHolder * GO = smart_cast((*it).object); + bool isCar = smart_cast(it->object) != nullptr; + if (GO) Telekinesis().activate(GO, isCar ? m_fThrowInImpulseCar : m_fThrowInImpulse, m_fTeleHeight, 100000); + } + } + + if(m_eZoneState==eZoneStateBlowout && new_state!=eZoneStateBlowout) + { + Telekinesis().clear_deactivate(); + } + inherited::OnStateSwitch(new_state); +} + +void CMincer::Load (LPCSTR section) +{ + inherited::Load(section); + + m_telekinetics.set_destroing_particles(shared_str(pSettings->r_string(section,"tearing_particles"))); + m_telekinetics.set_throw_power(pSettings->r_float(section,"throw_out_impulse")); + m_torn_particles=pSettings->r_string(section,"torn_particles"); + m_tearing_sound.create(pSettings->r_string(section,"body_tearing_sound"),st_Effect,sg_SourceType); + m_fActorBlowoutRadiusPercent=pSettings->r_float(section,"actor_blowout_radius_percent"); + + //pSettings->r_fvector3(section,whirlwind_center); +} + +BOOL CMincer::net_Spawn(CSE_Abstract* DC) +{ + BOOL result=inherited::net_Spawn(DC); + Fvector C; + Center(C); + C.y+=m_fTeleHeight; + m_telekinetics.SetCenter(C); + m_telekinetics.SetOwnerObject(smart_cast(this)); + return result; +} +void CMincer::net_Destroy() +{ + inherited::net_Destroy(); + m_telekinetics.clear_impacts(); +} +void CMincer::feel_touch_new (CObject* O) +{ + + inherited::feel_touch_new(O); + if( m_eZoneState==eZoneStateBlowout && (m_dwBlowoutExplosionTime>(u32)m_iStateTime)) + { + CPhysicsShellHolder * GO = smart_cast(O); + bool isCar = smart_cast(O) != nullptr; + Telekinesis().activate(GO, isCar ? m_fThrowInImpulseCar : m_fThrowInImpulse, m_fTeleHeight, 100000); + } +} +BOOL CMincer::feel_touch_contact (CObject* O) +{ + return inherited::feel_touch_contact(O)&&smart_cast(O); +} +void CMincer:: AffectThrow (SZoneObjectInfo* O, CPhysicsShellHolder* GO,const Fvector& throw_in_dir,float dist) +{ + inherited::AffectThrow(O,GO,throw_in_dir,dist); +} + +bool CMincer::BlowoutState () +{ + bool ret=inherited::BlowoutState (); + + //xr_set::iterator it=m_inZone.begin(),e=m_inZone.end(); + //for(;e!=it;++it) + //{ + // CEntityAlive * EA = smart_cast(*it); + // if(!EA)continue; + // CPhysicsShellHolder * GO = smart_cast(*it); + // Telekinesis().activate(GO,m_fThrowInImpulse, m_fTeleHeight, 100000); + + //} + + if(m_dwBlowoutExplosionTime<(u32)m_iPreviousStateTime || + m_dwBlowoutExplosionTime>=(u32)m_iStateTime) return ret; + Telekinesis().deactivate(); + return ret; +} +void CMincer ::ThrowInCenter(Fvector& C) +{ + C.set(m_telekinetics.Center()); + C.y=Position().y; +} + + +void CMincer ::Center (Fvector& C) const +{ + C.set(Position()); +} + +void CMincer::NotificateDestroy (CPHDestroyableNotificate *dn) +{ + Fvector dir; + float impulse; + //if(!m_telekinetics.has_impacts()) return; + + //CObject* obj=Level().Objects.net_Find(id); + CPhysicsShellHolder* obj=dn->PPhysicsShellHolder(); + m_telekinetics.draw_out_impact(dir,impulse); + CParticlesPlayer* PP = smart_cast(obj); + if(PP && *m_torn_particles) + { + PP->StartParticles(m_torn_particles,Fvector().set(0,1,0),ID()); + } + m_tearing_sound.play_at_pos(0,m_telekinetics.Center()); + + Fvector position_in_bone_space, throw_in_dir; + position_in_bone_space.set (0.0f, 0.0f, 0.0f); + throw_in_dir.set (1.0f, 0.0f, 1.0f); + CreateHit(obj->ID(),ID(),throw_in_dir,0.0f,0,position_in_bone_space,impulse,ALife::eHitTypeExplosion); +} + +void CMincer::AffectPullAlife(CEntityAlive* EA,const Fvector& throw_in_dir,float dist) +{ + float power = Power(dist) * GetPullDeltaPower() * m_fThrowInHitKoefAlive; + //Fvector dir; + //dir.random_dir(throw_in_dir,2.f*M_PI); + if (EA->CLS_ID!=CLSID_OBJECT_ACTOR && power > EPS) + { + // Msg("Hit %s by pull: %.2f, dist: %.2f, delta: %.2f", EA->cName().c_str(), power, dist, GetPullDeltaPower()); + Fvector pos_in_bone_space; + pos_in_bone_space.set(0,0,0); + CreateHit(EA->ID(),ID(),throw_in_dir,power,0,pos_in_bone_space,0.0f,m_eHitTypeBlowout); + } + inherited::AffectPullAlife(EA,throw_in_dir,dist); + +} + +void CMincer::AffectPullDead(CPhysicsShellHolder* GO,const Fvector& throw_in_dir,float dist) +{ + float power = Power(dist) * GetPullDeltaPower() * m_fThrowInHitKoefCar; + if (smart_cast(GO) && power > EPS) + { + CreateHit(GO->ID(), ID(), throw_in_dir, power, 0, Fvector().set(0,0,0), 0.0f, m_eHitTypeBlowout); + } +} + +float CMincer::BlowoutRadiusPercent (CPhysicsShellHolder* GO) +{ + return (GO->CLS_ID!=CLSID_OBJECT_ACTOR? m_fBlowoutRadiusPercent:m_fActorBlowoutRadiusPercent); +} + +void CMincer::exit_Zone(SZoneObjectInfo& io) +{ + inherited::exit_Zone(io); +} diff --git a/src/xrGameLA/Mincer.h b/src/xrGameLA/Mincer.h new file mode 100644 index 000000000..a988abac9 --- /dev/null +++ b/src/xrGameLA/Mincer.h @@ -0,0 +1,56 @@ +///////////////////////////////////////////////////// +// Аномальная зона: "мясорубка" +// При попадании живого объекта в зону происходит +// электрический разряд +// Зона восстанавливает заряд через определенное время +// (через m_dwPeriod заряжается с 0 до m_fMaxPower) +// +///////////////////////////////////////////////////// +#pragma once + +#include "gravizone.h" +#include "telewhirlwind.h" +#include "PhysicsShellHolder.h" +#include "script_export_space.h" +#include "PHDestroyable.h" + +class CMincer : + public CBaseGraviZone, + public CPHDestroyableNotificator +{ +protected: + typedef CBaseGraviZone inherited; + CTeleWhirlwind m_telekinetics; + shared_str m_torn_particles; + ref_sound m_tearing_sound; + float m_fActorBlowoutRadiusPercent; + +public: + virtual CTelekinesis &Telekinesis () {return m_telekinetics;} + +public: + CMincer (); + virtual ~CMincer (); +// virtual void SwitchZoneState (EZoneState new_state); + virtual void OnStateSwitch (EZoneState new_state); + virtual BOOL feel_touch_contact (CObject* O); + virtual void feel_touch_new (CObject* O); + virtual void Load (LPCSTR section); + virtual bool BlowoutState (); + virtual void AffectPullDead (CPhysicsShellHolder* GO,const Fvector& throw_in_dir,float dist); + virtual void AffectPullAlife (CEntityAlive* EA,const Fvector& throw_in_dir,float dist); + virtual void AffectThrow (SZoneObjectInfo* O, CPhysicsShellHolder* GO,const Fvector& throw_in_dir,float dist); + virtual void ThrowInCenter (Fvector& C); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void Center (Fvector& C) const; + virtual void NotificateDestroy (CPHDestroyableNotificate *dn); + virtual float BlowoutRadiusPercent (CPhysicsShellHolder* GO); +protected: + virtual void exit_Zone (SZoneObjectInfo& io); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CMincer) +#undef script_type_list +#define script_type_list save_type_list(CMincer) \ No newline at end of file diff --git a/src/xrGameLA/Missile.cpp b/src/xrGameLA/Missile.cpp new file mode 100644 index 000000000..98e334be8 --- /dev/null +++ b/src/xrGameLA/Missile.cpp @@ -0,0 +1,722 @@ +#include "stdafx.h" +#include "missile.h" +#include "player_hud.h" +#include "PhysicsShell.h" +#include "actor.h" +#include "../CameraBase.h" +#include "xrserver_objects_alife.h" +#include "ActorEffector.h" +#include "level.h" +#include "xr_level_controller.h" +#include "../../Include/xrRender/Kinematics.h" +#include "ai_object_location.h" +#include "ExtendedGeom.h" +#include "MathUtils.h" +#include "characterphysicssupport.h" +#include "inventory.h" +#include "Weapon.h" +#include "../IGame_Persistent.h" +#ifdef DEBUG +# include "phdebug.h" +#endif + + +#define PLAYING_ANIM_TIME 15000 + + +#include "ui/UIProgressShape.h" +#include "ui/UIXmlInit.h" + +CUIProgressShape* g_MissileForceShape = NULL; + +void create_force_progress() +{ + VERIFY (!g_MissileForceShape); + CUIXml uiXml; + uiXml.Load (CONFIG_PATH, UI_PATH, "grenade.xml"); + + CUIXmlInit xml_init; + g_MissileForceShape = new CUIProgressShape(); + xml_init.InitProgressShape (uiXml, "progress", 0, g_MissileForceShape); +} + +CMissile::CMissile(void) +{ + m_dwStateTime = 0; +} + +CMissile::~CMissile(void) +{ +} + +void CMissile::reinit () +{ + inherited::reinit (); + m_throw = false; + m_constpower = false; + m_fThrowForce = 0; + m_dwDestroyTime = 0xffffffff; + SetPending (FALSE); + m_fake_missile = NULL; + SetState ( eHidden ); +} + +void CMissile::Load(LPCSTR section) +{ + inherited::Load (section); + + m_fMinForce = pSettings->r_float(section,"force_min"); + m_fConstForce = pSettings->r_float(section,"force_const"); + m_fMaxForce = pSettings->r_float(section,"force_max"); + m_fForceGrowSpeed = pSettings->r_float(section,"force_grow_speed"); + + m_dwDestroyTimeMax = pSettings->r_u32(section,"destroy_time"); + + m_vThrowPoint = pSettings->r_fvector3(section,"throw_point"); + m_vThrowDir = pSettings->r_fvector3(section,"throw_dir"); + m_vHudThrowPoint = pSettings->r_fvector3(*hud_sect,"throw_point"); + m_vHudThrowDir = pSettings->r_fvector3(*hud_sect,"throw_dir"); + + m_ef_weapon_type = READ_IF_EXISTS(pSettings,r_u32,section,"ef_weapon_type",u32(-1)); +} + +BOOL CMissile::net_Spawn(CSE_Abstract* DC) +{ + BOOL l_res = inherited::net_Spawn(DC); + + dwXF_Frame = 0xffffffff; + + m_throw_direction.set(0.0f, 1.0f, 0.0f); + m_throw_matrix.identity(); + + return l_res; +} + +void CMissile::net_Destroy() +{ + inherited::net_Destroy(); + m_fake_missile = 0; + m_dwStateTime = 0; +} + +void CMissile::OnActiveItem () +{ + if (m_bDraw_off == true) { + SwitchState(eShowing); + } + + inherited::OnActiveItem (); + SetState (eIdle); + SetNextState (eIdle); +} + +void CMissile::OnHiddenItem() +{ + if (IsGameTypeSingle()) { + if (m_bHolster_off == true) { + SwitchState(eHiding); + } + } else { + SwitchState(eHidden); + } + + inherited::OnHiddenItem (); + SetState (eHidden); + SetNextState (eHidden); +} + + +void CMissile::spawn_fake_missile() +{ + if (OnClient()) return; + + if (!getDestroy()) + { + CSE_Abstract *object = Level().spawn_item( + *cNameSect(), + Position(), + (g_dedicated_server)?u32(-1):ai_location().level_vertex_id(), + ID(), + true + ); + + CSE_ALifeObject *alife_object = smart_cast(object); + VERIFY (alife_object); + alife_object->m_flags.set (CSE_ALifeObject::flCanSave,FALSE); + + NET_Packet P; + object->Spawn_Write (P,TRUE); + Level().Send (P,net_flags(TRUE)); + F_entity_Destroy (object); + } +} + +void CMissile::OnH_A_Chield() +{ + inherited::OnH_A_Chield(); + +// if(!m_fake_missile && !smart_cast(H_Parent())) +// spawn_fake_missile (); +} + + +void CMissile::OnH_B_Independent(bool just_before_destroy) +{ + inherited::OnH_B_Independent(just_before_destroy); + if (!just_before_destroy) + { + VERIFY (PPhysicsShell()); + PPhysicsShell()->SetAirResistance (0.f, 0.f); + PPhysicsShell()->set_DynamicScales (1.f, 1.f); + + if(GetState() == eThrow) + { + Msg("Throw on reject"); + Throw (); + } + } + + if(!m_dwDestroyTime && Local()) + { + DestroyObject (); + return; + } +} + +void CMissile::UpdateCL() +{ + m_dwStateTime += Device.dwTimeDelta; + + inherited::UpdateCL(); + CActor* pActor = smart_cast(H_Parent()); + if (pActor && GetState() == eIdle && this==pActor->inventory().ActiveItem() && m_dwStateTime > PLAYING_ANIM_TIME ) + { + CEntity::SEntityState st; + pActor->g_State(st); + + if(!st.bSprint) + { + SwitchState (eBore); + ResetSubStateTime (); + } else + m_dwStateTime = 0; + } + + if(GetState() == eReady) + { + if(m_throw) + SwitchState(eThrow); + else { + if (pActor) + { + m_fThrowForce += (m_fForceGrowSpeed * Device.dwTimeDelta) * .001f; + clamp(m_fThrowForce, m_fMinForce, m_fMaxForce); + } + } + } + +} +void CMissile::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + if(!H_Parent() && getVisible() && m_pPhysicsShell) + { + if(m_dwDestroyTime <= Level().timeServer()) + { + m_dwDestroyTime = 0xffffffff; + VERIFY (!m_pCurrentInventory); + Destroy (); + return; + } + } +} + +void CMissile::State(u32 state) +{ + switch(GetState()) + { + case eShowing: + { + SetPending (TRUE); + PlayHUDMotion("anim_show", FALSE, this, GetState()); + } break; + case eIdle: + { + SetPending (FALSE); + PlayAnimIdle (); + } break; + case eHiding: + { + if(H_Parent()) + { + SetPending (TRUE); + PlayHUDMotion ("anim_hide", TRUE, this, GetState()); + } + } break; + case eHidden: + { + StopCurrentAnimWithoutCallback(); + + if (H_Parent()) + { + setVisible(FALSE); + setEnabled(FALSE); + }; + SetPending (FALSE); + } break; + case eThrowStart: + { + SetPending (TRUE); + m_fThrowForce = m_fMinForce; + PlayHUDMotion ("anim_throw_begin", TRUE, this, GetState()); + } break; + case eReady: + { + PlayHUDMotion ("anim_throw_idle", TRUE, this, GetState()); + } break; + case eThrow: + { + SetPending (TRUE); + m_throw = false; + PlayHUDMotion ("anim_throw", TRUE, this, GetState()); + } break; + case eThrowEnd: + { + SwitchState (eShowing); + } break; + /*case MS_PLAYING: + { + if (Random.randI(30)==1) + PlayHUDMotion ("anim_playing_fail", TRUE, this, GetState()); + else + PlayHUDMotion ("anim_playing", TRUE, this, GetState()); + } break; + */ + } +} + +void CMissile::OnStateSwitch (u32 S) +{ + m_dwStateTime = 0; + inherited::OnStateSwitch (S); + State (S); +} + + +void CMissile::OnAnimationEnd(u32 state) +{ + switch(state) + { + case eHiding: + { + setVisible(FALSE); + SwitchState(eHidden); + } break; + case eShowing: + { + setVisible(TRUE); + SwitchState(eIdle); + } break; + case eThrowStart: + { + if(!m_fake_missile && !smart_cast(H_Parent())) + spawn_fake_missile (); + + if(m_throw) + SwitchState(eThrow); + else + SwitchState(eReady); + } break; + case eThrow: + { + SwitchState (eThrowEnd); + } break; + case eThrowEnd: + { + SwitchState (eShowing); + } break; + default: + inherited::OnAnimationEnd(state); + } +} + + +void CMissile::UpdatePosition(const Fmatrix& trans) +{ + XFORM().mul (trans,offset()); +} + +void CMissile::UpdateXForm () +{ + if (Device.dwFrame!=dwXF_Frame) + { + dwXF_Frame = Device.dwFrame; + + if (0==H_Parent()) return; + + // Get access to entity and its visual + CEntityAlive* E = smart_cast(H_Parent()); + + if(!E) return ; + + const CInventoryOwner *parent = smart_cast(E); + if (parent && parent->use_simplified_visual()) + return; + + if (parent->attached(this)) + return; + + VERIFY (E); + IKinematics* V = smart_cast (E->Visual()); + VERIFY (V); + + // Get matrices + int boneL = -1, boneR = -1, boneR2 = -1; + E->g_WeaponBones (boneL,boneR,boneR2); + if (boneR == -1) return; + + boneL = boneR2; + + + V->CalculateBones (); + Fmatrix& mL = V->LL_GetTransform(u16(boneL)); + Fmatrix& mR = V->LL_GetTransform(u16(boneR)); + + // Calculate + Fmatrix mRes; + Fvector R,D,N; + D.sub (mL.c,mR.c); D.normalize_safe(); + R.crossproduct (mR.j,D); R.normalize_safe(); + N.crossproduct (D,R); N.normalize_safe(); + mRes.set (R,N,D,mR.c); + mRes.mulA_43 (E->XFORM()); + UpdatePosition (mRes); + } +} + +void CMissile::setup_throw_params() +{ + CEntity *entity = smart_cast(H_Parent()); + VERIFY (entity); + CInventoryOwner *inventory_owner = smart_cast(H_Parent()); + VERIFY (inventory_owner); + Fmatrix trans; + trans.identity (); + Fvector FirePos, FireDir; + if (this == inventory_owner->inventory().ActiveItem()) + { + CInventoryOwner* io = smart_cast(H_Parent()); + if(NULL == io->inventory().ActiveItem()) + { + Log("current_state", GetState() ); + Log("next_state", GetNextState()); + Log("state_time", m_dwStateTime); + Log("item_sect", cNameSect().c_str()); + Log("H_Parent", H_Parent()->cNameSect().c_str()); + } + + entity->g_fireParams(this, FirePos, FireDir); + }else{ + FirePos = XFORM().c; + FireDir = XFORM().k; + } + trans.k.set (FireDir); + Fvector::generate_orthonormal_basis(trans.k, trans.j,trans.i); + trans.c.set (FirePos); + m_throw_matrix.set (trans); + m_throw_direction.set (trans.k); +} + +void CMissile::OnMotionMark(u32 state, const motion_marks& M) +{ + inherited::OnMotionMark(state, M); + if(state==eThrow && !m_throw) + { + if (H_Parent()) + Throw (); + } +} + +void CMissile::Throw() +{ + /*Msg("Throw is called for %d", ID());*/ + VERIFY (smart_cast(H_Parent())); + setup_throw_params (); + + m_fake_missile->m_throw_direction = m_throw_direction; + m_fake_missile->m_throw_matrix = m_throw_matrix; + + CInventoryOwner *inventory_owner = smart_cast(H_Parent()); + VERIFY (inventory_owner); + if (inventory_owner->use_default_throw_force()) + m_fake_missile->m_fThrowForce = m_constpower ? m_fConstForce : m_fThrowForce; + else + m_fake_missile->m_fThrowForce = inventory_owner->missile_throw_force(); + + m_fThrowForce = m_fMinForce; + + if (Local() && H_Parent()) + { + NET_Packet P; + u_EventGen (P,GE_OWNERSHIP_REJECT,ID()); + P.w_u16 (u16(m_fake_missile->ID())); + u_EventSend (P); + } +} + +void CMissile::OnEvent(NET_Packet& P, u16 type) +{ + inherited::OnEvent (P,type); + u16 id; + switch (type) { + case GE_OWNERSHIP_TAKE : { + P.r_u16(id); + CMissile *missile = smart_cast(Level().Objects.net_Find(id)); + m_fake_missile = missile; + missile->H_SetParent(this); + missile->Position().set(Position()); + break; + } + case GE_OWNERSHIP_REJECT : { + P.r_u16 (id); + bool IsFakeMissile = false; + if (m_fake_missile && (id == m_fake_missile->ID())) + { + m_fake_missile = NULL; + IsFakeMissile = true; + } + + CMissile *missile = smart_cast(Level().Objects.net_Find(id)); + if (!missile) + { + break; + } + missile->H_SetParent(0,!P.r_eof() && P.r_u8()); + if (IsFakeMissile && OnClient()) + missile->set_destroy_time(m_dwDestroyTimeMax); + break; + } + } +} + +void CMissile::Destroy() +{ + if (Local()) DestroyObject(); +} + +bool CMissile::Action(u16 cmd, u32 flags) +{ + if(inherited::Action(cmd, flags)) return true; + /*Msg("CMissile::Action %d %d %u", ID(), cmd, flags);*/ + switch(cmd) + { + case kWPN_FIRE: + { + m_constpower = true; + if(flags&CMD_START) + { + if (GetState() == eIdle || GetState() == eBore) + { + m_throw = true; + SwitchState(eThrowStart); + } + } + return true; + }break; + + case kWPN_ZOOM: + { + m_constpower = false; + if(flags&CMD_START) + { + m_throw = false; + if (GetState() == eIdle || GetState() == eBore) + SwitchState(eThrowStart); + else + if(GetState()==eReady) + { + m_throw = true; + } + + } + else + if(GetState()==eReady || GetState()==eThrowStart || GetState()==eIdle) + { + m_throw = true; + if(GetState()==eReady) + SwitchState(eThrow); + } + return true; + }break; + } + return false; +} + +void CMissile::UpdateFireDependencies_internal () +{ + if (0==H_Parent()) return; + + if (Device.dwFrame!=dwFP_Frame){ + dwFP_Frame = Device.dwFrame; + + UpdateXForm (); + + if (GetHUDmode() && !IsHidden()){ +/* + // 1st person view - skeletoned + IKinematics* V = smart_cast(GetHUD()->Visual()); + VERIFY (V); + V->CalculateBones (); + + // fire point&direction + Fmatrix& parent = GetHUD()->Transform (); + m_throw_direction.set (parent.k); +*/ + }else{ + // 3rd person + Fmatrix& parent = H_Parent()->XFORM(); + + m_throw_direction.set (m_vThrowDir); + parent.transform_dir (m_throw_direction); + } + } +} + +void CMissile::activate_physic_shell() +{ + if (!smart_cast(H_Parent())) { + inherited::activate_physic_shell(); + if(m_pPhysicsShell&&m_pPhysicsShell->isActive()&&!IsGameTypeSingle()) + { + m_pPhysicsShell->add_ObjectContactCallback (ExitContactCallback); + m_pPhysicsShell->set_CallbackData (smart_cast(H_Root())); + } + return; + } + + Fvector l_vel; + l_vel.set (m_throw_direction); + l_vel.normalize_safe(); + l_vel.mul (m_fThrowForce); + + Fvector a_vel; + CInventoryOwner *inventory_owner = smart_cast(H_Root()); + if (inventory_owner && inventory_owner->use_throw_randomness()) { + float fi,teta,r; + fi = ::Random.randF(0.f,2.f*M_PI); + teta = ::Random.randF(0.f,M_PI); + r = ::Random.randF(2.f*M_PI,3.f*M_PI); + float rxy = r*_sin(teta); + a_vel.set (rxy*_cos(fi),rxy*_sin(fi),r*_cos(teta)); + } + else + a_vel.set (0.f,0.f,0.f); + + XFORM().set (m_throw_matrix); + + CEntityAlive *entity_alive = smart_cast(H_Root()); + if (entity_alive && entity_alive->character_physics_support()){ + Fvector parent_vel; + entity_alive->character_physics_support()->movement()->GetCharacterVelocity(parent_vel); + l_vel.add (parent_vel); + } + + VERIFY (!m_pPhysicsShell); + create_physic_shell (); + m_pPhysicsShell->Activate (m_throw_matrix, l_vel, a_vel); +// m_pPhysicsShell->AddTracedGeom (); + m_pPhysicsShell->SetAllGeomTraced (); + m_pPhysicsShell->add_ObjectContactCallback (ExitContactCallback); + m_pPhysicsShell->set_CallbackData (smart_cast(entity_alive)); +// m_pPhysicsShell->remove_ObjectContactCallback (ExitContactCallback); + m_pPhysicsShell->SetAirResistance (0.f,0.f); + m_pPhysicsShell->set_DynamicScales (1.f,1.f); + + IKinematics *kinematics = smart_cast(Visual()); + VERIFY (kinematics); + kinematics->CalculateBones_Invalidate(); + kinematics->CalculateBones (); +} +void CMissile::net_Relcase(CObject* O) +{ + inherited::net_Relcase(O); + if(PPhysicsShell()&&PPhysicsShell()->isActive()) + { + if(O==smart_cast((CPhysicsShellHolder*)PPhysicsShell()->get_CallbackData())) + { + PPhysicsShell()->remove_ObjectContactCallback(ExitContactCallback); + PPhysicsShell()->set_CallbackData(NULL); + } + } + +} +void CMissile::create_physic_shell () +{ + //create_box2sphere_physic_shell(); + CInventoryItemObject::CreatePhysicsShell(); +} + +void CMissile::setup_physic_shell () +{ + VERIFY(!m_pPhysicsShell); + create_physic_shell(); + m_pPhysicsShell->Activate (XFORM(),0,XFORM());//,true + IKinematics *kinematics = smart_cast(Visual()); + VERIFY (kinematics); + kinematics->CalculateBones_Invalidate(); + kinematics->CalculateBones (); +} + +u32 CMissile::ef_weapon_type () const +{ + VERIFY (m_ef_weapon_type != u32(-1)); + return (m_ef_weapon_type); +} + + +bool CMissile::render_item_ui_query() +{ + bool b_is_active_item = m_pCurrentInventory->ActiveItem()==this; + return b_is_active_item && (GetState()==eReady) && !m_throw && smart_cast(H_Parent()); +} + +void CMissile::render_item_ui() +{ + CActor *actor = smart_cast(H_Parent()); + R_ASSERT(actor); + + if(!g_MissileForceShape) + create_force_progress(); + float k = (m_fThrowForce-m_fMinForce)/(m_fMaxForce-m_fMinForce); + g_MissileForceShape->SetPos (k); + g_MissileForceShape->Draw (); +} + +void CMissile::ExitContactCallback(bool& do_colide,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/) +{ + dxGeomUserData *gd1=NULL, *gd2=NULL; + if(bo1) + { + gd1 =retrieveGeomUserData(c.geom.g1); + gd2 =retrieveGeomUserData(c.geom.g2); + } + else + { + gd2 =retrieveGeomUserData(c.geom.g1); + gd1 =retrieveGeomUserData(c.geom.g2); + } + if(gd1&&gd2&&(CPhysicsShellHolder*)gd1->callback_data==gd2->ph_ref_object) + do_colide=false; +} + +void CMissile::GetBriefInfo(xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count) +{ + str_name = NameShort(); + str_count = ""; + icon_sect_name = ""; +} + +u16 CMissile::bone_count_to_synchronize () const +{ + return CInventoryItem::object().PHGetSyncItemsNumber(); +} \ No newline at end of file diff --git a/src/xrGameLA/Missile.h b/src/xrGameLA/Missile.h new file mode 100644 index 000000000..5cd4e03e2 --- /dev/null +++ b/src/xrGameLA/Missile.h @@ -0,0 +1,108 @@ +#pragma once +#include "hud_item_object.h" +#include "HudSound.h" + +struct dContact; +struct SGameMtl; +class CMissile : public CHudItemObject +{ + typedef CHudItemObject inherited; +public: + enum EMissileStates{ + eThrowStart = eLastBaseState+1, + eReady, + eThrow, + eThrowEnd, + }; + CMissile (); + virtual ~CMissile (); + + virtual BOOL AlwaysTheCrow () { return TRUE; } + virtual void render_item_ui (); + virtual bool render_item_ui_query (); + + virtual void reinit (); + virtual CMissile* cast_missile () {return this;} + + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + + virtual void UpdateCL (); + virtual void shedule_Update (u32 dt); + + virtual void OnH_A_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + + virtual void OnEvent (NET_Packet& P, u16 type); + + virtual void OnAnimationEnd (u32 state); + virtual void OnMotionMark (u32 state, const motion_marks&); + + + virtual void Throw(); + virtual void Destroy(); + + virtual bool Action (u16 cmd, u32 flags); + virtual void State (u32 state); + virtual void OnStateSwitch (u32 S); + virtual void GetBriefInfo (xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count); + +protected: + virtual void UpdateFireDependencies_internal (); + virtual void UpdateXForm (); + void UpdatePosition (const Fmatrix& trans); + void spawn_fake_missile (); + + //инициализация если вещь в активном слоте или спрятана на OnH_B_Chield + virtual void OnActiveItem (); + virtual void OnHiddenItem (); + + //для сети + virtual void net_Relcase (CObject* O ); +protected: + //время нахождения в текущем состоянии + u32 m_dwStateTime; + bool m_throw; + + //время уничтожения + u32 m_dwDestroyTime; + u32 m_dwDestroyTimeMax; + + Fvector m_throw_direction; + Fmatrix m_throw_matrix; + + CMissile *m_fake_missile; + + //параметры броска + + float m_fMinForce, m_fConstForce, m_fMaxForce, m_fForceGrowSpeed; +//private: + bool m_constpower; + float m_fThrowForce; +protected: + //относительная точка и направление вылета гранаты + Fvector m_vThrowPoint; + Fvector m_vThrowDir; + //для HUD + Fvector m_vHudThrowPoint; + Fvector m_vHudThrowDir; +protected: + void setup_throw_params (); +public: + Fvector const& throw_point_offset () const {return m_vThrowPoint;} + virtual void activate_physic_shell (); + virtual void setup_physic_shell (); + virtual void create_physic_shell (); + IC void set_destroy_time (u32 delta_destroy_time) {m_dwDestroyTime = delta_destroy_time + Device.dwTimeGlobal;} + +protected: + u32 m_ef_weapon_type; + +public: + virtual u32 ef_weapon_type () const; + IC u32 destroy_time () const { return m_dwDestroyTime; } + IC int time_from_begin_throw () const { return (Device.dwTimeGlobal + m_dwDestroyTimeMax - m_dwDestroyTime); } + static void ExitContactCallback (bool& do_colide,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/); + virtual u16 bone_count_to_synchronize () const; +}; \ No newline at end of file diff --git a/src/xrGameLA/MosquitoBald.cpp b/src/xrGameLA/MosquitoBald.cpp new file mode 100644 index 000000000..f4c07cb24 --- /dev/null +++ b/src/xrGameLA/MosquitoBald.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "mosquitobald.h" +#include "hudmanager.h" +#include "ParticlesObject.h" +#include "level.h" +#include "physicsshellholder.h" +#include "../xr_collide_form.h" +#include "Actor.h" +#include "Car.h" + +CMosquitoBald::CMosquitoBald(void) +{ + m_dwDeltaTime = 0; + m_fHitImpulseScale = 1.f; + + m_bLastBlowoutUpdate = false; +} + +CMosquitoBald::~CMosquitoBald(void) +{ +} + +void CMosquitoBald::Load(LPCSTR section) +{ + inherited::Load(section); + + m_killCarEngine = !!READ_IF_EXISTS(pSettings, r_bool, section, "car_kill_engine", false); + m_hitKoefCar = READ_IF_EXISTS(pSettings, r_float, section, "hit_koef_car", 1.f); +} + + +void CMosquitoBald::Postprocess(f32 /**val/**/) +{ +} + +bool CMosquitoBald::BlowoutState() +{ + bool result = inherited::BlowoutState(); + if(!result) + { + m_bLastBlowoutUpdate = false; + UpdateBlowout(); + } + else if(!m_bLastBlowoutUpdate) + { + m_bLastBlowoutUpdate = true; + UpdateBlowout(); + } + + return result; +} + +bool CMosquitoBald::ShouldIgnoreObject(CGameObject* pObject) +{ + auto pCar = smart_cast(pObject); + if (pCar) return false; + + return inherited::ShouldIgnoreObject(pObject); +} + +void CMosquitoBald::Affect(SZoneObjectInfo* O) +{ + CPhysicsShellHolder *pGameObject = smart_cast(O->object); + if(!pGameObject) return; + + if(O->zone_ignore) return; + + Fvector P; + XFORM().transform_tiny(P,CFORM()->getSphere().P); + +#ifdef DEBUG + char l_pow[255]; + xr_sprintf(l_pow, "zone hit. %.1f", Power(pGameObject->Position().distance_to(P))); + if(bDebug) Msg("%s %s",*pGameObject->cName(), l_pow); +#endif + + Fvector hit_dir; + hit_dir.set(::Random.randF(-.5f,.5f), + ::Random.randF(.0f,1.f), + ::Random.randF(-.5f,.5f)); + hit_dir.normalize(); + + + Fvector position_in_bone_space; + + VERIFY(!pGameObject->getDestroy()); + + float dist = pGameObject->Position().distance_to(P) - pGameObject->Radius(); + float power = Power(dist>0.f?dist:0.f); + float impulse = m_fHitImpulseScale*power*pGameObject->GetMass(); + + + if (power > EPS_L) + { + m_dwDeltaTime = 0; + position_in_bone_space.set(0.f,0.f,0.f); + + auto car = smart_cast(pGameObject); + if (car != nullptr) + { + if (m_killCarEngine) + { + car->KillEngine(); + } + power *= m_hitKoefCar; + } + CreateHit(pGameObject->ID(),ID(),hit_dir,power,0,position_in_bone_space,impulse,m_eHitTypeBlowout); + + PlayHitParticles(pGameObject); + + O->total_damage += power; + } + + //статистика по объекту + O->hit_num++; +} diff --git a/src/xrGameLA/MosquitoBald.h b/src/xrGameLA/MosquitoBald.h new file mode 100644 index 000000000..7e2f3407d --- /dev/null +++ b/src/xrGameLA/MosquitoBald.h @@ -0,0 +1,35 @@ +#pragma once + +#include "customzone.h" +#include "script_export_space.h" + +class CMosquitoBald : public CCustomZone +{ +private: + typedef CCustomZone inherited; +public: + CMosquitoBald(void); + virtual ~CMosquitoBald(void); + + virtual void Load(LPCSTR section); + virtual void Postprocess(f32 val); + virtual bool EnableEffector() {return true;} + + + virtual void Affect(SZoneObjectInfo* O); + +protected: + virtual bool BlowoutState(); + bool ShouldIgnoreObject(CGameObject*) override; + + //для того чтобы blowout обновился один раз + //после того как зона перключилась в другое состояние + bool m_bLastBlowoutUpdate; + bool m_killCarEngine; + float m_hitKoefCar; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CMosquitoBald) +#undef script_type_list +#define script_type_list save_type_list(CMosquitoBald) \ No newline at end of file diff --git a/src/xrGameLA/MosquitoBald_script.cpp b/src/xrGameLA/MosquitoBald_script.cpp new file mode 100644 index 000000000..1462737a8 --- /dev/null +++ b/src/xrGameLA/MosquitoBald_script.cpp @@ -0,0 +1,27 @@ +#include "pch_script.h" +#include "MosquitoBald.h" +#include "ZoneMine.h" +#include "ZoneCampfire.h" +#include "NoGravityZone.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CMosquitoBald::script_register (lua_State *L) +{ + module(L) + [ + class_("CMosquitoBald") + .def(constructor<>()), + class_("CZoneMine") + .def(constructor<>()), + class_("CNoGravityZone") + .def(constructor<>()), + class_("CZoneCampfire") + .def(constructor<>()) + .def("turn_on", &CZoneCampfire::turn_on_script) + .def("turn_off", &CZoneCampfire::turn_off_script) + .def("is_on", &CZoneCampfire::is_on) + + ]; +} diff --git a/src/xrGameLA/NET_Queue.h b/src/xrGameLA/NET_Queue.h new file mode 100644 index 000000000..2cde65481 --- /dev/null +++ b/src/xrGameLA/NET_Queue.h @@ -0,0 +1,125 @@ +#pragma once + +#include "xrMessages.h" + + +class NET_Event +{ +public: + u16 ID; + u32 timestamp; + u16 type; + u16 destination; + xr_vector data; +public: + void import (NET_Packet& P) + { + data.clear (); + P.r_begin (ID ); //VERIFY(M_EVENT==ID); + switch (ID) + { + case M_SPAWN: + { + P.read_start(); +// timestamp = P-> + }break; + case M_EVENT: + { + P.r_u32 (timestamp ); + P.r_u16 (type ); + P.r_u16 (destination); + }break; + default: + { + VERIFY(0); + }break; + } + + u32 size = P.r_elapsed(); + if (size) + { + data.resize (size); + P.r (&*data.begin(),size); + } + } + void export (NET_Packet& P) + { + u16 ID = M_EVENT; + P.w_begin (ID ); + P.w_u32 (timestamp ); + P.w_u16 (type ); + P.w_u16 (destination); + if (data.size()) P.w(&*data.begin(),(u32)data.size()); + } + void implication (NET_Packet& P) const + { + CopyMemory (P.B.data,&*data.begin(),(u32)data.size()); + P.B.count = (u32)data.size(); + P.r_pos = 0; + } +}; + +IC bool operator < (const NET_Event& A, const NET_Event& B) { return A.timestamp queue; + xr_deque queue; +public: + IC void insert (NET_Packet& P) + { + NET_Event E; + E.import (P); +// queue.insert (E); + queue.push_back (E); + /* + //------------------------------------------- +#ifdef DEBUG + shared_str EventName; + string16 tmp; + + switch (E.type) + { + case 1: EventName = "GE_OWNERSHIP_TAKE [1]"; break; + case 2: EventName = "GE_OWNERSHIP_REJECT [2]"; break; + case 5: EventName = "GE_DIE [5]"; break; + case 7: EventName = "GE_DESTROY [7]"; break; + default: EventName = itoa(E.type, tmp, 10); break; + } + + Msg("Event %s to %d - at %d", *EventName, E.destination, E.timestamp); +#endif + //------------------------------------------- + //*/ + } + IC BOOL available (u32 T) + { +// if (queue.empty()/* || (Ttimestamp)*/) return FALSE; +// else return TRUE; + if (queue.empty()) return FALSE; + /** + else + { + if (!g_bCheckTime) return TRUE; +#ifdef _DEBUG + if (Ttimestamp) return FALSE; +#endif + return TRUE; + } + /**/ + return TRUE; + } + IC void get (u16& ID, u16& dest, u16& type, NET_Packet& P) + { + const NET_Event& E = *queue.begin(); + ID = E.ID; + dest = E.destination; + type = E.type; + E.implication (P); +// queue.erase (queue.begin()); + queue.pop_front(); + } +}; diff --git a/src/xrGameLA/Needles.cpp b/src/xrGameLA/Needles.cpp new file mode 100644 index 000000000..f1c64546e --- /dev/null +++ b/src/xrGameLA/Needles.cpp @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////// +// Needles.cpp +// Needles - артефакт иголки +/////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////// +// BlackDrops.cpp +// BlackDrops - черные капли +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "Needles.h" +#include "PhysicsShell.h" + + +CNeedles::CNeedles(void) +{ +} + +CNeedles::~CNeedles(void) +{ +} diff --git a/src/xrGameLA/Needles.h b/src/xrGameLA/Needles.h new file mode 100644 index 000000000..2ea95fcbe --- /dev/null +++ b/src/xrGameLA/Needles.h @@ -0,0 +1,16 @@ +/////////////////////////////////////////////////////////////// +// Needles.h +// Needles - артефакт иголки +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CNeedles: public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CNeedles(void); + virtual ~CNeedles(void); +}; \ No newline at end of file diff --git a/src/xrGameLA/NoGravityZone.cpp b/src/xrGameLA/NoGravityZone.cpp new file mode 100644 index 000000000..fcd2b2667 --- /dev/null +++ b/src/xrGameLA/NoGravityZone.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "nogravityzone.h" +#include "physicsshell.h" +#include "entity_alive.h" +#include "PHMovementControl.h" +#include "PhWorld.h" +#include "CharacterPhysicsSupport.h" +extern CPHWorld *ph_world; +void CNoGravityZone::enter_Zone(SZoneObjectInfo& io) +{ + inherited::enter_Zone(io); + switchGravity(io,false); + +} +void CNoGravityZone::exit_Zone(SZoneObjectInfo& io) +{ + switchGravity(io,true); + inherited::exit_Zone(io); + +} +void CNoGravityZone::UpdateWorkload(u32 dt) +{ + OBJECT_INFO_VEC_IT i=m_ObjectInfoMap.begin(),e=m_ObjectInfoMap.end(); + for(;e!=i;i++)switchGravity(*i,false); +} +void CNoGravityZone::switchGravity(SZoneObjectInfo& io, bool val) +{ + if(io.object->getDestroy()) return; + CPhysicsShellHolder* sh= smart_cast(io.object); + if(!sh)return; + CPhysicsShell* shell=sh->PPhysicsShell(); + if(shell&&shell->isActive()) + { + shell->set_ApplyByGravity(val); + if(!val&&shell->get_ApplyByGravity()) + { + CPhysicsElement* e=shell->get_ElementByStoreOrder(u16(Random.randI(0,shell->get_ElementsNumber()))); + if(e->isActive()){ + e->applyImpulseTrace(Fvector().random_point(e->getRadius()),Fvector().random_dir(),shell->getMass()*ph_world->Gravity()*fixed_step,e->m_SelfID); + + } + + } + //shell->SetAirResistance(0.f,0.f); + //shell->set_DynamicScales(1.f); + return; + } + if(!io.nonalive_object) + { + CEntityAlive* ea=smart_cast(io.object); + CPHMovementControl*mc=ea->character_physics_support()->movement(); + mc->SetApplyGravity(BOOL(val)); + mc->SetForcedPhysicsControl(!val); + if(!val&&mc->Environment()==CPHMovementControl::peOnGround) + { + Fvector gn; + mc->GroundNormal(gn); + mc->ApplyImpulse(gn,mc->GetMass()*ph_world->Gravity()*fixed_step); + + } + } +} \ No newline at end of file diff --git a/src/xrGameLA/NoGravityZone.h b/src/xrGameLA/NoGravityZone.h new file mode 100644 index 000000000..5630971ba --- /dev/null +++ b/src/xrGameLA/NoGravityZone.h @@ -0,0 +1,15 @@ +#pragma once +#include "CustomZone.h" + +class CNoGravityZone : + public CCustomZone +{ +typedef CCustomZone inherited; +public: +protected: + virtual void enter_Zone (SZoneObjectInfo& io) ; + virtual void exit_Zone (SZoneObjectInfo& io) ; +private: + void switchGravity (SZoneObjectInfo& io,bool val) ; + virtual void UpdateWorkload (u32 dt ) ; +}; \ No newline at end of file diff --git a/src/xrGameLA/OutfitBase.cpp b/src/xrGameLA/OutfitBase.cpp new file mode 100644 index 000000000..8b8018f79 --- /dev/null +++ b/src/xrGameLA/OutfitBase.cpp @@ -0,0 +1,309 @@ +#include "stdafx.h" + +#include "customoutfit.h" +#include "PhysicsShell.h" +#include "inventory_space.h" +#include "Inventory.h" +#include "Actor.h" +#include "game_cl_base.h" +#include "Level.h" +#include "BoneProtections.h" +#include "../../Include/xrRender/Kinematics.h" +#include "../../Include/xrRender/RenderVisual.h" +#include "ai_sounds.h" +#include "actorEffector.h" +#include "player_hud.h" +#include "OutfitBase.h" +#include "gamepersistent.h" + +#define MIN_RDROOPS_SPREAD_VALUE 0.05f +#define MAX_RDROOPS_SPREAD_VALUE 0.1f + + +COutfitBase::COutfitBase() +{ + m_flags.set(FUsingCondition, TRUE); + + m_HitTypeProtection.resize(ALife::eHitTypeMax); + for(int i=0; ir_float(section,"burn_protection"); + m_HitTypeProtection[ALife::eHitTypeStrike] = pSettings->r_float(section,"strike_protection"); + m_HitTypeProtection[ALife::eHitTypeShock] = pSettings->r_float(section,"shock_protection"); + m_HitTypeProtection[ALife::eHitTypeWound] = pSettings->r_float(section,"wound_protection"); + m_HitTypeProtection[ALife::eHitTypeRadiation] = pSettings->r_float(section,"radiation_protection"); + m_HitTypeProtection[ALife::eHitTypeTelepatic] = pSettings->r_float(section,"telepatic_protection"); + m_HitTypeProtection[ALife::eHitTypeChemicalBurn]= pSettings->r_float(section,"chemical_burn_protection"); + m_HitTypeProtection[ALife::eHitTypeExplosion] = pSettings->r_float(section,"explosion_protection"); + m_HitTypeProtection[ALife::eHitTypeFireWound] = pSettings->r_float(section,"fire_wound_protection"); + m_HitTypeProtection[ALife::eHitTypePhysicStrike] = READ_IF_EXISTS(pSettings, r_float, section, "physic_strike_protection", 0.0f); + + m_boneProtection->m_fHitFracActor = READ_IF_EXISTS(pSettings, r_float, section, "hit_fraction_actor", 0.0f); // pSettings->r_float(section, "hit_fraction_actor"); + + m_fHealthRestoreSpeed = READ_IF_EXISTS(pSettings, r_float, section, "health_restore_speed", 0.0f); + m_fRadiationRestoreSpeed = READ_IF_EXISTS(pSettings, r_float, section, "radiation_restore_speed", 0.0f); + m_fSatietyRestoreSpeed = READ_IF_EXISTS(pSettings, r_float, section, "satiety_restore_speed", 0.0f); + m_fPowerRestoreSpeed = READ_IF_EXISTS(pSettings, r_float, section, "power_restore_speed", 0.0f); + m_fBleedingRestoreSpeed = READ_IF_EXISTS(pSettings, r_float, section, "bleeding_restore_speed", 0.0f); + m_fPowerLoss = READ_IF_EXISTS(pSettings, r_float, section, "power_loss", 1.0f); + clamp(m_fPowerLoss, 0.0f, 1.0f); + + m_BonesProtectionSect = READ_IF_EXISTS(pSettings, r_string, section, "bones_koeff_protection", "" ); + + m_armorTestBoneBody = READ_IF_EXISTS(pSettings, r_string, section, "armor_test_bone_body", ""); + m_armorTestBoneHead = READ_IF_EXISTS(pSettings, r_string, section, "armor_test_bone_head", ""); + + hasVisorEffects_ = !!READ_IF_EXISTS(pSettings, r_bool, section, "casts_visor_effects", FALSE); +} + +BOOL COutfitBase::net_Spawn(CSE_Abstract* DC) +{ + ReloadBonesProtection(); + return inherited::net_Spawn(DC); +} + +void COutfitBase::Hit(float hit_power, ALife::EHitType hit_type) +{ + hit_power *= GetHitImmunity(hit_type); + ChangeCondition(-hit_power); +} + +float COutfitBase::GetDefHitTypeProtection(ALife::EHitType hit_type) +{ + return m_HitTypeProtection[hit_type]*GetCondition(); +} + +float COutfitBase::GetHitTypeProtection(ALife::EHitType hit_type, s16 element) +{ + float fBase = m_HitTypeProtection[hit_type]*GetCondition(); + float bone = m_boneProtection->getBoneProtection(element); + return fBase*bone; +} + +//tatarinrafa: Берем ситсему из ЗП + +float COutfitBase::GetBoneArmor(s16 element) +{ + return m_boneProtection->getBoneArmor(element); +} + +float COutfitBase::HitThroughArmor(float hit_power, s16 element, float ap, bool& add_wound, ALife::EHitType hit_type) +{ + float NewHitPower = hit_power; + if (hit_type == ALife::eHitTypeFireWound) + { + float ba = GetBoneArmor(element); + if (ba<0.0f) + return NewHitPower; + + float BoneArmor = ba*GetCondition(); + if (/*!fis_zero(ba, EPS) && */(ap > BoneArmor)) + { + //пуля пробила бронь + float d_hit_power = (ap - BoneArmor) / ap; + if (d_hit_power < m_boneProtection->m_fHitFracActor) + { + d_hit_power = m_boneProtection->m_fHitFracActor; + } + NewHitPower *= d_hit_power; + + VERIFY(NewHitPower >= 0.0f); + } + else + { + //пуля НЕ пробила бронь + NewHitPower *= m_boneProtection->m_fHitFracActor; + add_wound = false; //раны нет + } + } + else if (hit_type == ALife::eHitTypeWound) + { + float protect = GetDefHitTypeProtection(hit_type); + float minimumHit = fmax(hit_power * m_boneProtection->m_fHitFracActor, 0.f); + NewHitPower -= protect; + // Костюм не прокушен + if (NewHitPower < minimumHit) + { + NewHitPower = minimumHit; + add_wound = false; //раны нет + } + } + else + { + float one = 0.1f; + if (hit_type == ALife::eHitTypeStrike || + hit_type == ALife::eHitTypeWound_2 || + hit_type == ALife::eHitTypeExplosion) + { + one = 1.0f; + } + float protect = GetDefHitTypeProtection(hit_type); + NewHitPower -= protect * one; + if (NewHitPower < 0.f) + NewHitPower = 0.f; + } + //увеличить изношенность костюма + Hit(hit_power, hit_type); + //Msg("Hit Through Outfit: new hit power = %f; old hit power = %f; hit_type = %u, ammopiercing = %f; element = %i, bonearmor = %f", NewHitPower, hit_power, hit_type, element, GetBoneArmor(element)); + return NewHitPower; +} + +BOOL COutfitBase::BonePassBullet (int boneID) +{ + return m_boneProtection->getBonePassBullet(s16(boneID)); +}; + +float COutfitBase::GetPowerLoss() +{ + if (m_fPowerLoss<1 && GetCondition() <= 0) + { + return 1.0f; + }; + return m_fPowerLoss; +} + +bool COutfitBase::install_upgrade_impl( LPCSTR section, bool test ) +{ + bool result = inherited::install_upgrade_impl( section, test ); + + result |= process_if_exists( section, "burn_protection", &CInifile::r_float, m_HitTypeProtection[ALife::eHitTypeBurn] , test ); + result |= process_if_exists( section, "shock_protection", &CInifile::r_float, m_HitTypeProtection[ALife::eHitTypeShock] , test ); + result |= process_if_exists( section, "strike_protection", &CInifile::r_float, m_HitTypeProtection[ALife::eHitTypeStrike] , test ); + result |= process_if_exists( section, "wound_protection", &CInifile::r_float, m_HitTypeProtection[ALife::eHitTypeWound] , test ); + result |= process_if_exists( section, "radiation_protection", &CInifile::r_float, m_HitTypeProtection[ALife::eHitTypeRadiation] , test ); + result |= process_if_exists( section, "telepatic_protection", &CInifile::r_float, m_HitTypeProtection[ALife::eHitTypeTelepatic] , test ); + result |= process_if_exists( section, "chemical_burn_protection", &CInifile::r_float, m_HitTypeProtection[ALife::eHitTypeChemicalBurn], test ); + result |= process_if_exists( section, "explosion_protection", &CInifile::r_float, m_HitTypeProtection[ALife::eHitTypeExplosion] , test ); + result |= process_if_exists( section, "fire_wound_protection", &CInifile::r_float, m_HitTypeProtection[ALife::eHitTypeFireWound] , test ); +// result |= process_if_exists( section, "physic_strike_protection", &CInifile::r_float, m_HitTypeProtection[ALife::eHitTypePhysicStrike], test ); + + LPCSTR str; + bool result2; + + result2 = process_if_exists_set( section, "bones_koeff_protection", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + m_BonesProtectionSect = str; + ReloadBonesProtection (); + } + result2 = process_if_exists_set( section, "bones_koeff_protection_add", &CInifile::r_string, str, test ); + if ( result2 && !test ) + AddBonesProtection (str); + + result |= result2; + result |= process_if_exists( section, "hit_fraction_actor", &CInifile::r_float, m_boneProtection->m_fHitFracActor, test ); + + result |= process_if_exists( section, "health_restore_speed", &CInifile::r_float, m_fHealthRestoreSpeed, test ); + result |= process_if_exists( section, "radiation_restore_speed", &CInifile::r_float, m_fRadiationRestoreSpeed, test ); + result |= process_if_exists( section, "satiety_restore_speed", &CInifile::r_float, m_fSatietyRestoreSpeed, test ); + result |= process_if_exists( section, "power_restore_speed", &CInifile::r_float, m_fPowerRestoreSpeed, test ); + result |= process_if_exists( section, "bleeding_restore_speed", &CInifile::r_float, m_fBleedingRestoreSpeed, test ); + result |= process_if_exists( section, "power_loss", &CInifile::r_float, m_fPowerLoss, test ); + clamp( m_fPowerLoss, 0.0f, 1.0f ); + + return result; +} + +void COutfitBase::AddBonesProtection(LPCSTR bones_section) +{ + CObject* parent = smart_cast(Level().CurrentViewEntity()); + + if (parent && parent->Visual() && m_BonesProtectionSect.size()) + m_boneProtection->add(bones_section, smart_cast(parent->Visual())); +} + +void COutfitBase::ReloadBonesProtection() +{ + CObject* parent = smart_cast(Level().CurrentViewEntity()); + + if (parent && parent->Visual() && m_BonesProtectionSect.size()) + m_boneProtection->reload(m_BonesProtectionSect, smart_cast(parent->Visual())); +} + +float COutfitBase::GetArmorByBoneName(const shared_str& boneName) +{ + auto parent = smart_cast(Level().CurrentViewEntity()); + if (parent && parent->Visual() && m_boneProtection && boneName.size() > 0) + { + auto kinematics = smart_cast(parent->Visual()); + VERIFY(kinematics); + return m_boneProtection->getBoneArmor(kinematics->LL_BoneID(boneName)); + } + return 0.0f; +} + +float COutfitBase::GetArmorBody() +{ + return GetArmorByBoneName(m_armorTestBoneBody); +} + +float COutfitBase::GetArmorHead() +{ + return GetArmorByBoneName(m_armorTestBoneHead); +} + +void COutfitBase::OnMoveToRuck() +{ + if (m_pCurrentInventory) + { + visorWetness_1_ = 0.f; // как буд-то гг протер перед убиранием + visorWetness_2_ = 0.f; // как буд-то гг протер перед убиранием + + g_pGamePersistent->Environment().SetActorHudWetness1(visorWetness_1_); + g_pGamePersistent->Environment().SetActorHudWetness2(visorWetness_2_); + } + +}; + +void COutfitBase::OnMoveToSlot() +{ + if (m_pCurrentInventory) + { + visorWetnessSpreadValue_1_ = Random.randF(MIN_RDROOPS_SPREAD_VALUE, MAX_RDROOPS_SPREAD_VALUE); + visorWetnessSpreadValue_2_ = Random.randF(MIN_RDROOPS_SPREAD_VALUE, MAX_RDROOPS_SPREAD_VALUE); + + g_pGamePersistent->Environment().SetActorHudWetnessRand1(visorWetnessSpreadValue_1_); + g_pGamePersistent->Environment().SetActorHudWetnessRand2(visorWetnessSpreadValue_2_); + } +}; + + +void COutfitBase::save(NET_Packet &packet) +{ + inherited::save(packet); + + packet.w_float (visorWetness_1_); + packet.w_float (visorWetness_2_); +} + +void COutfitBase::load(IReader &packet) +{ + inherited::load(packet); + + visorWetness_1_ = packet.r_float(); + visorWetness_2_ = packet.r_float(); + + g_pGamePersistent->Environment().SetActorHudWetness1(visorWetness_1_); + g_pGamePersistent->Environment().SetActorHudWetness2(visorWetness_2_); +} \ No newline at end of file diff --git a/src/xrGameLA/OutfitBase.h b/src/xrGameLA/OutfitBase.h new file mode 100644 index 000000000..eb76a517c --- /dev/null +++ b/src/xrGameLA/OutfitBase.h @@ -0,0 +1,87 @@ +#pragma once + +#include "inventory_item_object.h" +#include "hudsound.h" + +struct SBoneProtections; + +class COutfitBase: public CInventoryItemObject { +private: + typedef CInventoryItemObject inherited; +public: + COutfitBase (void); + virtual ~COutfitBase (void); + + virtual void Load (LPCSTR section); + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + + //уменьшенная версия хита, для вызова, когда костюм надет на персонажа + virtual void Hit (float P, ALife::EHitType hit_type); + + //коэффициенты на которые домножается хит + //при соответствующем типе воздействия + //если на персонаже надет костюм + float GetHitTypeProtection(ALife::EHitType hit_type, s16 element); + float GetDefHitTypeProtection(ALife::EHitType hit_type); + + ////tatarinrafa: Замена на ЗП систему хита + float GetBoneArmor(s16 element); + float HitThroughArmor(float hit_power, s16 element, float ap, bool& add_wound, ALife::EHitType hit_type); + + float GetArmorBody (); + float GetArmorHead (); + + //коэффициент на который домножается потеря силы + //если на персонаже надет костюм + float GetPowerLoss (); + + float GetHealthRestoreSpeed() { return m_fHealthRestoreSpeed; } + float GetRadiationRestoreSpeed() { return m_fRadiationRestoreSpeed; } + float GetSatietyRestoreSpeed() { return m_fSatietyRestoreSpeed; } + float GetPowerRestoreSpeed() { return m_fPowerRestoreSpeed; } + float GetBleedingRestoreSpeed() { return m_fBleedingRestoreSpeed; } + + virtual void OnMoveToRuck (); + virtual void OnMoveToSlot (); + +protected: + HitImmunity::HitTypeSVec m_HitTypeProtection; + float m_fPowerLoss; + float m_fHealthRestoreSpeed; + float m_fRadiationRestoreSpeed; + float m_fSatietyRestoreSpeed; + float m_fPowerRestoreSpeed; + float m_fBleedingRestoreSpeed; + + SBoneProtections* m_boneProtection; + + // Имя костей для тела и головы, по которым смотреть уровень брони для вывода в UI + shared_str m_armorTestBoneBody; + shared_str m_armorTestBoneHead; + + float GetArmorByBoneName (const shared_str& boneName); + +public: + shared_str m_BonesProtectionSect; + + virtual BOOL BonePassBullet (int boneID); + + void ReloadBonesProtection (); + void AddBonesProtection (LPCSTR bones_section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + + // Фактор накопленого количества капель на стекле + float visorWetness_1_; + float visorWetness_2_; + + // Для придаче разного положения капель так как шейдр не может иметь рандом сам по себе + float visorWetnessSpreadValue_1_; + float visorWetnessSpreadValue_2_; + + // Вызывать ли эффекты визора, капли на стекле, блики солнца + bool hasVisorEffects_; + +protected: + virtual bool install_upgrade_impl( LPCSTR section, bool test ); +}; diff --git a/src/xrGameLA/PDA.cpp b/src/xrGameLA/PDA.cpp new file mode 100644 index 000000000..34625b0bb --- /dev/null +++ b/src/xrGameLA/PDA.cpp @@ -0,0 +1,220 @@ +#include "stdafx.h" +#include "pda.h" +#include "hudmanager.h" +#include "PhysicsShell.h" +#include "Entity.h" +#include "actor.h" + +#include "xrserver.h" +#include "xrServer_Objects_ALife_Items.h" +#include "level.h" + +#include "specific_character.h" +#include "alife_registry_wrappers.h" + + +CPda::CPda(void) +{ + m_baseSlot = PDA_SLOT; + m_flags.set (Fruck, TRUE); + + m_idOriginalOwner = u16(-1); + m_SpecificChracterOwner = NULL; + + + TurnOff (); +} + +CPda::~CPda() +{} + +BOOL CPda::net_Spawn(CSE_Abstract* DC) +{ + inherited::net_Spawn (DC); + CSE_Abstract *abstract = (CSE_Abstract*)(DC); + CSE_ALifeItemPDA *pda = smart_cast(abstract); + R_ASSERT (pda); + m_idOriginalOwner = pda->m_original_owner; + m_SpecificChracterOwner = pda->m_specific_character; + + return (TRUE); +} + +void CPda::net_Destroy() +{ + inherited::net_Destroy (); + TurnOff (); + feel_touch.clear (); + UpdateActiveContacts (); +} + +void CPda::Load(LPCSTR section) +{ + inherited::Load(section); + + m_fRadius = pSettings->r_float(section,"radius"); +} + +void CPda::shedule_Update(u32 dt) +{ + inherited::shedule_Update (dt); + + if(!H_Parent()) return; + Position().set (H_Parent()->Position()); + + if( IsOn() && Level().CurrentEntity() && Level().CurrentEntity()->ID()==H_Parent()->ID() ) + { + CEntityAlive* EA = smart_cast(H_Parent()); + if(!EA || !EA->g_Alive()) + { + TurnOff(); + return; + } + + feel_touch_update(Position(),m_fRadius); + UpdateActiveContacts (); + } +} + +void CPda::UpdateActiveContacts () +{ + m_active_contacts.clear_not_free(); + xr_vector::iterator it= feel_touch.begin(); + for(;it!=feel_touch.end();++it){ + CEntityAlive* pEA = smart_cast(*it); + if(!!pEA->g_Alive() && !pEA->cast_base_monster()) + { + m_active_contacts.push_back(*it); + } + } +} + +void CPda::feel_touch_new(CObject* O) +{ + if ( CInventoryOwner* pNewContactInvOwner = smart_cast(O) ) + { + CInventoryOwner* pOwner = smart_cast( H_Parent() ); + + R_ASSERT2(pOwner, "Actor lost his pda. He should always have it!"); + + pOwner->NewPdaContact (pNewContactInvOwner); + } +} + +void CPda::feel_touch_delete(CObject* O) +{ + if(!H_Parent()) return; + if ( CInventoryOwner* pLostContactInvOwner = smart_cast(O) ) + { + CInventoryOwner* pOwner = smart_cast( H_Parent() ); + + R_ASSERT2(pOwner, "Actor lost his pda. He should always have it!"); + + pOwner->LostPdaContact (pLostContactInvOwner); + } +} + +BOOL CPda::feel_touch_contact(CObject* O) +{ + CInventoryOwner* pInvOwner = smart_cast(O); + if(pInvOwner) + { + if( this!=pInvOwner->GetPDA() ) + { + CEntityAlive* pEntityAlive = smart_cast(O); + if( pEntityAlive && !pEntityAlive->cast_base_monster() ) + return TRUE; + } else + return FALSE; + } + + return FALSE; +} + +void CPda::OnH_A_Chield() +{ + VERIFY(IsOff()); + + //включить PDA только если оно находится у первого владельца + if(H_Parent()->ID() == m_idOriginalOwner){ + TurnOn (); + if(m_sFullName.empty()){ + m_sFullName.assign(inherited::Name()); + m_sFullName += " "; + m_sFullName += (smart_cast(H_Parent()))->Name(); + } + }; + inherited::OnH_A_Chield (); +} + +void CPda::OnH_B_Independent(bool just_before_destroy) +{ + inherited::OnH_B_Independent(just_before_destroy); + + //выключить + TurnOff(); +} + + +CInventoryOwner* CPda::GetOriginalOwner() +{ + CObject* pObject = Level().Objects.net_Find(GetOriginalOwnerID()); + CInventoryOwner* pInvOwner = smart_cast(pObject); + + return pInvOwner; +} + + + +void CPda::ActivePDAContacts(xr_vector& res) +{ + res.clear_not_free (); + xr_vector::iterator it = m_active_contacts.begin(); + xr_vector::iterator it_e = m_active_contacts.end(); + + for(;it!=it_e;++it) + { + CPda* p = GetPdaFromOwner(*it); + if(p) + res.push_back(p); + } +} + +void CPda::save(NET_Packet &output_packet) +{ + inherited::save (output_packet); + save_data (m_sFullName, output_packet); +} + +void CPda::load(IReader &input_packet) +{ + inherited::load (input_packet); + load_data (m_sFullName, input_packet); +} + +CObject* CPda::GetOwnerObject() +{ + return Level().Objects.net_Find(GetOriginalOwnerID()); +} +LPCSTR CPda::Name () +{ + if( !m_SpecificChracterOwner.size() ) + return inherited::Name(); + + if(m_sFullName.empty()) + { + m_sFullName.assign(inherited::Name()); + + CSpecificCharacter spec_char; + spec_char.LoadSCharacter(m_SpecificChracterOwner); + m_sFullName += " "; + m_sFullName += xr_string(spec_char.Name()); + } + + return m_sFullName.c_str(); +} + +CPda* CPda::GetPdaFromOwner(CObject* owner) +{ + return smart_cast(owner)->GetPDA (); +} diff --git a/src/xrGameLA/PDA.h b/src/xrGameLA/PDA.h new file mode 100644 index 000000000..aa78c8301 --- /dev/null +++ b/src/xrGameLA/PDA.h @@ -0,0 +1,84 @@ +#pragma once + +#include "../feel_touch.h" +#include "inventory_item_object.h" + +#include "InfoPortionDefs.h" +#include "character_info_defs.h" +#include "script_export_space.h" + +#include "PdaMsg.h" + + +class CInventoryOwner; +class CPda; + +DEF_VECTOR (PDA_LIST, CPda*); + +class CPda : + public CInventoryItemObject, + public Feel::Touch +{ + typedef CInventoryItemObject inherited; +public: + CPda (); + virtual ~CPda (); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void Load (LPCSTR section); + virtual void net_Destroy (); + + virtual void OnH_A_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + + virtual void shedule_Update (u32 dt); + + virtual void feel_touch_new (CObject* O); + virtual void feel_touch_delete (CObject* O); + virtual BOOL feel_touch_contact (CObject* O); + + + virtual u16 GetOriginalOwnerID () {return m_idOriginalOwner;} + virtual CInventoryOwner* GetOriginalOwner (); + virtual CObject* GetOwnerObject (); + + + void TurnOn () {m_bTurnedOff = false;} + void TurnOff () {m_bTurnedOff = true;} + + bool IsActive () {return IsOn();} + bool IsOn () {return !m_bTurnedOff;} + bool IsOff () {return m_bTurnedOff;} + + + void ActivePDAContacts (xr_vector& res); + CPda* GetPdaFromOwner (CObject* owner); + u32 ActiveContactsNum () {return m_active_contacts.size();} + + + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + + virtual LPCSTR Name (); + + +protected: + void UpdateActiveContacts (); + + + xr_vector m_active_contacts; + float m_fRadius; + + u16 m_idOriginalOwner; + shared_str m_SpecificChracterOwner; + xr_string m_sFullName; + + bool m_bTurnedOff; + +public: + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CPda) +#undef script_type_list +#define script_type_list save_type_list(CPda) diff --git a/src/xrGameLA/PHAICharacter.cpp b/src/xrGameLA/PHAICharacter.cpp new file mode 100644 index 000000000..b595e2160 --- /dev/null +++ b/src/xrGameLA/PHAICharacter.cpp @@ -0,0 +1,205 @@ +#include "stdafx.h" + + +#include "PHDynamicData.h" +#include "Physics.h" +#include "ExtendedGeom.h" +#include "../cl_intersect.h" +#include "tri-colliderKNoOPC\__aabb_tri.h" + +#include "phaicharacter.h" + +#ifdef DEBUG +# include "../StatGraph.h" +# include "PHDebug.h" +# include "level.h" +# include "debug_renderer.h" +#endif + +CPHAICharacter::CPHAICharacter() +{ + m_forced_physics_control=false; +} +void CPHAICharacter::Create(dVector3 sizes) +{ + inherited::Create(sizes); + m_forced_physics_control=false;//. +} +bool CPHAICharacter::TryPosition(Fvector pos,bool exact_state){ + if(!b_exist) return false; + if(m_forced_physics_control||JumpState()) return false;//b_was_on_object||b_on_object|| + if( DoCollideObj ()) return false; + Fvector current_pos; + GetPosition(current_pos); + Fvector cur_vel;GetVelocity(cur_vel); + + Fvector displace;displace.sub(pos,current_pos); + float disp_mag=displace.magnitude(); + + if( fis_zero( disp_mag ) || fis_zero( Device.fTimeDelta ) ) + return true ; + const u32 max_steps = 15 ; + const float fmax_steps = float ( max_steps ) ; + float fsteps_num = 1.f ; + u32 steps_num = 1 ; + float disp_pstep = FootRadius ( ); + float rest = 0.f; + + float parts = disp_mag / disp_pstep ; + fsteps_num = floor ( parts ); + steps_num = iFloor ( parts ); + if( steps_num > max_steps ) + { + steps_num = max_steps ; + fsteps_num = fmax_steps ; + disp_pstep = disp_mag / fsteps_num ; + } + rest = disp_mag - fsteps_num * disp_pstep ; + + Fvector vel;vel.mul(displace,disp_pstep/fixed_step/disp_mag); + bool ret=true; + int save_gm=dBodyGetGravityMode(m_body); + dBodySetGravityMode(m_body,0); + for(u32 i=0;steps_num>i;++i) + { + SetVelocity(vel); + Enable(); + if(!step_single(fixed_step)) + { + SetVelocity(cur_vel); + ret= false; + break; + } + } + + vel.mul(displace,rest/fixed_step/disp_mag); + SetVelocity(vel); + Enable(); + ret = step_single(fixed_step); + + + dBodySetGravityMode(m_body,save_gm); + SetVelocity(cur_vel); + Fvector pos_new;GetPosition(pos_new); + +#if 0 + Fvector dif;dif .sub( pos, pos_new ); + float dif_m = dif.magnitude(); + if(ret&&dif_m>EPS_L) + { + Msg("dif vec %f,%f,%f \n",dif.x,dif.y,dif.z); + Msg("dif mag %f \n",dif_m); + } +#endif + + SetPosition(pos_new); + m_last_move.sub(pos_new,current_pos).mul(1.f/Device.fTimeDelta); + m_body_interpolation.UpdatePositions(); + m_body_interpolation.UpdatePositions(); + if(ret) + Disable(); + m_collision_damage_info.m_contact_velocity=0.f; + return ret; +} + +void CPHAICharacter:: SetPosition (Fvector pos) +{ + m_vDesiredPosition.set(pos); + inherited::SetPosition(pos); + +} + +void CPHAICharacter::BringToDesired(float time,float velocity,float /**force/**/) +{ + Fvector pos,move; + GetPosition(pos); + + move.sub(m_vDesiredPosition,pos); + move.y=0.f; + float dist=move.magnitude(); + + float vel; + if(dist>EPS_L*100.f) + { + vel=dist/time; + move.mul(1.f/dist); + } + else if(dist>EPS_L*10.f) + { + vel=dist*dist*dist; + move.mul(1.f/dist); + } + else + { + vel=0.f; + move.set(0,0,0); + } + + if(vel>velocity)//&&velocity>EPS_L + vel=velocity; + + if(velocitysurface.mu = 0.00f; + dxGeomUserData* D1=retrieveGeomUserData(c->geom.g1); + dxGeomUserData* D2=retrieveGeomUserData(c->geom.g2); + if(D1&&D2&&D1->ph_object&&D2->ph_object&&D1->ph_object->CastType()==tpCharacter&&D2->ph_object->CastType()==tpCharacter) + { + b_on_object=true; + b_valide_wall_contact=false; + } +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgNeverUseAiPhMove))do_collide=false; +#endif +} +#ifdef DEBUG +void CPHAICharacter::OnRender() +{ + inherited::OnRender(); + + if(!b_exist) return; + + Fvector pos; + GetDesiredPosition(pos); + pos.y+=m_radius; + + + Fvector scale; + scale.set(0.35f,0.35f,0.35f); + Fmatrix M; + M.identity(); + M.scale(scale); + M.c.set(pos); + + + Level().debug_renderer().draw_ellipse(M, 0xffffffff); +} +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHAICharacter.h b/src/xrGameLA/PHAICharacter.h new file mode 100644 index 000000000..7ccc0ca84 --- /dev/null +++ b/src/xrGameLA/PHAICharacter.h @@ -0,0 +1,28 @@ +#pragma once +#include "PHSimpleCharacter.h" +class CPHAICharacter : public CPHSimpleCharacter +{ + typedef CPHSimpleCharacter inherited; + + Fvector m_vDesiredPosition; + bool m_forced_physics_control; +public: + CPHAICharacter (); + virtual CPHAICharacter *CastAICharacter () {return this;} + virtual void SetPosition (Fvector pos); + virtual void SetDesiredPosition (const Fvector& pos) {m_vDesiredPosition.set(pos) ;} + virtual void GetDesiredPosition (Fvector& dpos) {dpos.set(m_vDesiredPosition) ;} + virtual void ValidateWalkOn () ; + virtual void BringToDesired (float time,float velocity,float force=1.f) ; + virtual bool TryPosition (Fvector pos,bool exact_state) ; + virtual void Jump (const Fvector& jump_velocity) ; + virtual void SetMaximumVelocity (dReal vel) {m_max_velocity=vel ;} + virtual void InitContact (dContact* c,bool &do_collide,u16 material_idx_1,u16 material_idx_2) ; + virtual void SetForcedPhysicsControl (bool v){m_forced_physics_control=v;} + virtual bool ForcedPhysicsControl (){return m_forced_physics_control;} + virtual void Create (dVector3 sizes) ; +#ifdef DEBUG + virtual void OnRender () ; +#endif + +}; \ No newline at end of file diff --git a/src/xrGameLA/PHActivationShape.cpp b/src/xrGameLA/PHActivationShape.cpp new file mode 100644 index 000000000..e9d95dd40 --- /dev/null +++ b/src/xrGameLA/PHActivationShape.cpp @@ -0,0 +1,291 @@ +#include "stdafx.h" +#include "PHObject.h" +#include "Physics.h" +#include "MathUtils.h" +#include "phvalidevalues.h" +#include "PHActivationShape.h" +#include "Extendedgeom.h" +#include "SpaceUtils.h" +#include "MathUtilsOde.h" +#include "MathUtils.h" +#include "../GameMtlLib.h" +#include "Level.h" +#include "PHWorld.h" +#include "../../xrODE/ode/src/util.h" + +#ifdef DEBUG +# include "PHDebug.h" +#endif // DEBUG + +#include "PHDynamicData.h" +#include "PHSynchronize.h" +#include "phnetstate.h" +static float max_depth =0.f; +static float friction_factor =0.f; +static float cfm =1.e-10f; +static float erp =1.f; +#ifdef DEBUG +#define CHECK_POS(pos,msg,br) if (!valid_pos(pos,phBoundaries)){Msg("pos:%f,%f,%f",pos.x,pos.y,pos.z);Msg(msg);VERIFY(!br);} + +#else +#define CHECK_POS(pos,msg,br) +#endif +void ActivateTestDepthCallback (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + + if(do_colide&&!material_1->Flags.test(SGameMtl::flPassable) &&!material_2->Flags.test(SGameMtl::flPassable)) + { + float& depth=c.geom.depth; + float test_depth=depth; + save_max(max_depth,test_depth); + c.surface.mu*=friction_factor; + + c.surface.soft_cfm=cfm; + c.surface.soft_erp=erp; + + } + +} +void StaticEnvironment (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + dJointID contact_joint = dJointCreateContact(0, ContactGroup, &c); + + if(bo1) + { + ((CPHActivationShape*)(retrieveGeomUserData(c.geom.g1)->callback_data))->DActiveIsland()->ConnectJoint(contact_joint); + dJointAttach (contact_joint, dGeomGetBody(c.geom.g1), 0); + } + else + { + ((CPHActivationShape*)(retrieveGeomUserData(c.geom.g2)->callback_data))->DActiveIsland()->ConnectJoint(contact_joint); + dJointAttach (contact_joint, 0, dGeomGetBody(c.geom.g2)); + } + do_colide=false; +} +void GetMaxDepthCallback (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + + if(do_colide&&!material_1->Flags.test(SGameMtl::flPassable) &&!material_2->Flags.test(SGameMtl::flPassable)) + { + float& depth=c.geom.depth; + float test_depth=depth; + //save_max(max_depth,test_depth); + max_depth+=test_depth; + } + //do_colide=false; +} + +void RestoreVelocityState(V_PH_WORLD_STATE& state) +{ + V_PH_WORLD_STATE::iterator i=state.begin(),e=state.end(); + for(;e!=i;++i) + { + CPHSynchronize& sync=*i->first; + SPHNetState& old_s =i->second; + SPHNetState new_s;sync.get_State(new_s); + new_s.angular_vel.set(old_s.angular_vel); + new_s.linear_vel.set(old_s.linear_vel); + new_s.enabled=old_s.enabled; + sync.set_State(new_s); + } +} + +CPHActivationShape::CPHActivationShape() +{ + m_geom=NULL; + m_body=NULL; + m_flags.zero(); + m_flags.set(flFixedRotation,TRUE); +} +CPHActivationShape::~CPHActivationShape() +{ + VERIFY(!m_body&&!m_geom); +} +void CPHActivationShape::Create(const Fvector start_pos,const Fvector start_size,CPhysicsShellHolder* ref_obj,EType _type/*=etBox*/,u16 flags) +{ + VERIFY(ref_obj); + m_body = dBodyCreate (0) ; + dMass m; + dMassSetSphere(&m,1.f,100000.f); + dMassAdjust(&m,1.f); + dBodySetMass(m_body,&m); + switch(_type) + { + case etBox: + m_geom = dCreateBox (0,start_size.x,start_size.y,start_size.z) ; + break; + + case etSphere: + m_geom = dCreateSphere (0,start_size.x); + break; + }; + + dGeomCreateUserData (m_geom) ; + dGeomUserDataSetObjectContactCallback(m_geom,ActivateTestDepthCallback) ; + dGeomUserDataSetPhysicsRefObject(m_geom,ref_obj) ; + dGeomSetBody (m_geom,m_body) ; + dBodySetPosition (m_body,start_pos.x,start_pos.y,start_pos.z) ; + Island() .AddBody (m_body) ; + dBodyEnable (m_body) ; + m_safe_state .create(m_body) ; + spatial_register () ; + m_flags.set(flags,TRUE); +} +void CPHActivationShape:: Destroy () +{ + VERIFY(m_geom&&m_body) ; + spatial_unregister () ; + CPHObject::deactivate () ; + dGeomDestroyUserData (m_geom) ; + dGeomDestroy (m_geom) ; + m_geom =NULL ; + dBodyDestroy (m_body) ; + m_body =NULL ; +} +bool CPHActivationShape:: Activate (const Fvector need_size,u16 steps,float max_displacement,float max_rotation,bool un_freeze_later/* =false*/) +{ + +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawDeathActivationBox)) + { + DBG_OpenCashedDraw(); + Fmatrix M; + PHDynamicData::DMXPStoFMX(dBodyGetRotation(m_body),dBodyGetPosition(m_body),M); + Fvector v;dGeomBoxGetLengths(m_geom,cast_fp(v));v.mul(0.5f); + DBG_DrawOBB(M,v,D3DCOLOR_XRGB(0,255,0)); + } +#endif + VERIFY(m_geom&&m_body); + CPHObject::activate(); + ph_world->Freeze(); + UnFreeze(); + max_depth=0.f; + + dGeomUserDataSetObjectContactCallback(m_geom,GetMaxDepthCallback) ; + //ph_world->Step(); + ph_world->StepTouch(); + u16 num_it =15; + float fnum_it=float(num_it); + float fnum_steps=float(steps); + float fnum_steps_r=1.f/fnum_steps; + float resolve_depth=0.01f; + float max_vel=max_depth/fnum_it*fnum_steps_r/fixed_step; + float limit_l_vel=_max(_max(need_size.x,need_size.y),need_size.z)/fnum_it*fnum_steps_r/fixed_step; + if(limit_l_vel>default_l_limit)limit_l_vel=default_l_limit; + if(max_vel>limit_l_vel) + max_vel=limit_l_vel; + + float max_a_vel=max_rotation/fnum_it*fnum_steps_r/fixed_step; + if(max_a_vel>default_w_limit) + max_a_vel=default_w_limit; + //ph_world->CutVelocity(0.f,0.f); + dGeomUserDataSetCallbackData(m_geom,this); + dGeomUserDataSetObjectContactCallback(m_geom,ActivateTestDepthCallback) ; + if(m_flags.test(flStaticEnvironment))dGeomUserDataAddObjectContactCallback(m_geom,StaticEnvironment); + max_depth=0.f; + + Fvector from_size; + Fvector step_size,size; + dGeomBoxGetLengths(m_geom,cast_fp(from_size)); + step_size.sub(need_size,from_size); + step_size.mul(fnum_steps_r); + size.set(from_size); + bool ret=false; + V_PH_WORLD_STATE temp_state; + ph_world->GetState(temp_state); + for(int m=0;steps>m;++m) + { + //float param =fnum_steps_r*(1+m); + //InterpolateBox(id,param); + size.add(step_size); + dGeomBoxSetLengths(m_geom,size.x,size.y,size.z); + u16 attempts=10; + do{ + + ret=false; + for(int i=0;num_it>i;++i) + { + max_depth=0.f; + ph_world->Step(); + CHECK_POS(Position(),"pos after ph_world->Step()",false); + ph_world->CutVelocity(max_vel,max_a_vel); + CHECK_POS(Position(),"pos after CutVelocity",true); + //if(m==0&&i==0)ph_world->GetState(temp_state); + if(max_depth < resolve_depth) + { + ret=true; + break; + } + } + attempts--; + }while(!ret&&attempts>0); +#ifdef DEBUG + Msg("correction attempts %d",10-attempts); +#endif + + } + RestoreVelocityState(temp_state); + CHECK_POS(Position(),"pos after RestoreVelocityState(temp_state);",true); + if(!un_freeze_later)ph_world->UnFreeze(); +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawDeathActivationBox)) + { + DBG_OpenCashedDraw(); + Fmatrix M; + PHDynamicData::DMXPStoFMX(dBodyGetRotation(m_body),dBodyGetPosition(m_body),M); + Fvector v;v.set(need_size);v.mul(0.5f); + DBG_DrawOBB(M,v,D3DCOLOR_XRGB(0,255,255)); + DBG_ClosedCashedDraw(30000); + } +#endif + return ret; +} +const Fvector& CPHActivationShape:: Position () +{ + return cast_fv(dBodyGetPosition(m_body)); +} +void CPHActivationShape:: Size (Fvector &size) +{ + dGeomBoxGetLengths(m_geom,cast_fp(size)); +} + +void CPHActivationShape:: PhDataUpdate (dReal step) +{ + m_safe_state.new_state(m_body); +} +void CPHActivationShape:: PhTune (dReal step) +{ + +} +dGeomID CPHActivationShape:: dSpacedGeom () +{ + return m_geom; +} +void CPHActivationShape:: get_spatial_params () +{ + spatialParsFromDGeom(m_geom,spatial.sphere.P,AABB,spatial.sphere.R); +} + +void CPHActivationShape:: InitContact (dContact* c,bool& do_collide,u16 ,u16 ) +{ + +} + +void CPHActivationShape::CutVelocity(float l_limit,float /*a_limit*/) +{ + dVector3 limitedl,diffl; + if(dVectorLimit(dBodyGetLinearVel(m_body),l_limit,limitedl)) + { + dVectorSub(diffl,limitedl,dBodyGetLinearVel(m_body)); + dBodySetLinearVel(m_body,diffl[0],diffl[1],diffl[2]); + dBodySetAngularVel(m_body,0.f,0.f,0.f); + dxStepBody(m_body,fixed_step); + dBodySetLinearVel(m_body,limitedl[0],limitedl[1],limitedl[2]); + } +} + +void CPHActivationShape:: set_rotation (const Fmatrix &sof) +{ + dMatrix3 rot;PHDynamicData::FMXtoDMX(sof,rot); + dBodySetRotation(ODEBody(),rot); +} \ No newline at end of file diff --git a/src/xrGameLA/PHActivationShape.h b/src/xrGameLA/PHActivationShape.h new file mode 100644 index 000000000..5b2728338 --- /dev/null +++ b/src/xrGameLA/PHActivationShape.h @@ -0,0 +1,45 @@ +#ifndef PH_ACTIVATION_SHAPE +#define PH_ACTIVATION_SHAPE +#endif +#include "MathUtils.h" +#include "phvalidevalues.h" +class CPHActivationShape : public CPHObject +{ +dBodyID m_body ; +dGeomID m_geom ; +Flags16 m_flags ; +CSafeFixedRotationState m_safe_state ; +public: +enum EType +{ + etBox, + etCylinder, + etSphere +}; + +enum { + flFixedRotation =1<<0, + flFixedPosition =1<<1, + flStaticEnvironment =1<<2, + flGravity =1<<3 + }; + CPHActivationShape () ; + ~CPHActivationShape () ; + void Create (const Fvector start_pos,const Fvector start_size,CPhysicsShellHolder* ref_obj,EType type=etBox,u16 flags=0) ; + void Destroy () ; + bool Activate (const Fvector need_size,u16 steps,float max_displacement,float max_rotation,bool un_freeze_later =false) ; +const Fvector &Position () ; + void Size (Fvector &size) ; + dBodyID ODEBody () {return m_body ;} + void set_rotation (const Fmatrix &rot) ; +private: +virtual void PhDataUpdate (dReal step) ; +virtual void PhTune (dReal step) ; +virtual void CutVelocity (float l_limit,float a_limit) ; +virtual void InitContact (dContact* c,bool& do_collide,u16 ,u16 ) ; +virtual dGeomID dSpacedGeom () ; +virtual void get_spatial_params () ; +virtual u16 get_elements_number () {return 0;} +virtual CPHSynchronize *get_element_sync (u16 element) {return NULL;} + +}; \ No newline at end of file diff --git a/src/xrGameLA/PHActorCharacter.cpp b/src/xrGameLA/PHActorCharacter.cpp new file mode 100644 index 000000000..115524cab --- /dev/null +++ b/src/xrGameLA/PHActorCharacter.cpp @@ -0,0 +1,316 @@ +#include "stdafx.h" +#include "phactorcharacter.h" +#include "Extendedgeom.h" +#include "PhysicsCommon.h" +#include "GameObject.h" +#include "PhysicsShellHolder.h" +#include "ai/stalker/ai_stalker.h" +#include "Actor.h" +#include "../GameMtlLib.h" +#include "level.h" + +//const float JUMP_HIGHT=0.5; +const float JUMP_UP_VELOCITY=6.0f;//5.6f; +const float JUMP_INCREASE_VELOCITY_RATE=1.2f; + +CPHActorCharacter::CPHActorCharacter() +{ + SetRestrictionType(CPHCharacter::rtActor); + + //std::fill(m_restrictors_index,m_restrictors_index+CPHCharacter::rtNone,end(m_restrictors)); + //m_restrictors_index[CPHCharacter::rtStalker] =begin(m_restrictors)+0; + //m_restrictors_index[CPHCharacter::rtMonsterMedium] =begin(m_restrictors)+1; + + { + m_restrictors.resize(3); + m_restrictors[0]=(new stalker_restrictor()); + m_restrictors[1]= new stalker_small_restrictor(); + m_restrictors[2]=(new medium_monster_restrictor()); + } +} + +CPHActorCharacter::~CPHActorCharacter(void) +{ + ClearRestrictors(); +} + +void CPHActorCharacter::Create(dVector3 sizes) +{ + if(b_exist) return; + inherited::Create(sizes); + if(!IsGameTypeSingle()) + { + ClearRestrictors(); + } + RESTRICTOR_I i=begin(m_restrictors),e=end(m_restrictors); + for(;e!=i;++i) + { + (*i)->Create(this,sizes); + } + + if(m_phys_ref_object) + { + SetPhysicsRefObject(m_phys_ref_object); + } +} +void SPHCharacterRestrictor::Create(CPHCharacter* ch,dVector3 sizes) +{ + VERIFY(ch); + if(m_character)return; + m_character=ch; + m_restrictor=dCreateCylinder(0,m_restrictor_radius,sizes[1]); + dGeomSetPosition(m_restrictor,0.f,sizes[1]/2.f,0.f); + m_restrictor_transform=dCreateGeomTransform(0); + dGeomTransformSetCleanup(m_restrictor_transform,0); + dGeomTransformSetInfo(m_restrictor_transform,1); + dGeomTransformSetGeom(m_restrictor_transform,m_restrictor); + dGeomCreateUserData(m_restrictor); + dGeomGetUserData(m_restrictor)->b_static_colide=false; + + dGeomSetBody(m_restrictor_transform,m_character->get_body()); + dSpaceAdd(m_character->dSpace(),m_restrictor_transform); + dGeomUserDataSetPhObject(m_restrictor,(CPHObject*)m_character); + switch(m_type) { + case CPHCharacter::rtStalker:static_cast(this)->Create(ch,sizes); + break; + case CPHCharacter::rtStalkerSmall:static_cast(this)->Create(ch,sizes); + break; + case CPHCharacter::rtMonsterMedium:static_cast(this)->Create(ch,sizes); + break; + default:NODEFAULT; + } + +} + +RESTRICTOR_I CPHActorCharacter::Restrictor(CPHCharacter::ERestrictionType rtype) +{ + R_ASSERT2(rtype0)(*Restrictor(rtype))->SetRadius(r); +} + +void SPHCharacterRestrictor::SetRadius(float r) +{ + m_restrictor_radius=r; + if(m_character) + { + float h; + dGeomCylinderGetParams(m_restrictor,&r,&h); + dGeomCylinderSetParams(m_restrictor,m_restrictor_radius,h); + } +} +void CPHActorCharacter::Destroy() +{ + if(!b_exist) return; + RESTRICTOR_I i=begin(m_restrictors),e=end(m_restrictors); + for(;e!=i;++i) + { + (*i)->Destroy(); + } + inherited::Destroy(); +} +void CPHActorCharacter::ClearRestrictors() +{ + RESTRICTOR_I i=begin(m_restrictors),e=end(m_restrictors); + for(;e!=i;++i) + { + (*i)->Destroy(); + xr_delete(*i); + } + m_restrictors.clear(); +} +void SPHCharacterRestrictor::Destroy() +{ + if(m_restrictor) { + dGeomDestroyUserData(m_restrictor); + dGeomDestroy(m_restrictor); + m_restrictor=NULL; + } + + if(m_restrictor_transform){ + dGeomDestroyUserData(m_restrictor_transform); + m_restrictor_transform=NULL; + } + m_character=NULL; +} +void CPHActorCharacter::SetPhysicsRefObject(CPhysicsShellHolder* ref_object) +{ + inherited::SetPhysicsRefObject(ref_object); + RESTRICTOR_I i=begin(m_restrictors),e=end(m_restrictors); + for(;e!=i;++i) + { + (*i)->SetPhysicsRefObject(ref_object); + } +} +void SPHCharacterRestrictor::SetPhysicsRefObject(CPhysicsShellHolder* ref_object) +{ + if(m_character) + dGeomUserDataSetPhysicsRefObject(m_restrictor,ref_object); +} +void CPHActorCharacter::SetMaterial (u16 material) +{ + inherited::SetMaterial(material); + if(!b_exist) return; + RESTRICTOR_I i=begin(m_restrictors),e=end(m_restrictors); + for(;e!=i;++i) + { + (*i)->SetMaterial(material); + } +} +void SPHCharacterRestrictor::SetMaterial(u16 material) +{ + dGeomGetUserData(m_restrictor)->material=material; +} +void CPHActorCharacter::SetAcceleration(Fvector accel) +{ + Fvector cur_a,input_a;float cur_mug,input_mug; + cur_a.set(m_acceleration);cur_mug=m_acceleration.magnitude(); + if(!fis_zero(cur_mug))cur_a.mul(1.f/cur_mug); + input_a.set(accel);input_mug=accel.magnitude(); + if(!fis_zero(input_mug))input_a.mul(1.f/input_mug); + if(!cur_a.similar(input_a,0.05f)||!fis_zero(input_mug-cur_mug,0.5f)) + inherited::SetAcceleration(accel); +} + +void CPHActorCharacter::Jump(const Fvector& accel) +{ + if(!b_exist) return; + if(!b_lose_control && (m_ground_contact_normal[1]>0.5f||m_elevator_state.ClimbingState())) + { + b_jump=true; + const dReal* vel=dBodyGetLinearVel(m_body); + dReal amag =m_acceleration.magnitude(); + if(amag<1.f)amag=1.f; + if(m_elevator_state.ClimbingState()) + { + m_elevator_state.GetJumpDir(m_acceleration,m_jump_accel); + m_jump_accel.mul(JUMP_UP_VELOCITY/2.f); + //if(accel.square_magnitude()>EPS_L)m_jump_accel.mul(4.f); + } + else{ + m_jump_accel.set(vel[0]*JUMP_INCREASE_VELOCITY_RATE+m_acceleration.x/amag*0.2f,jump_up_velocity,vel[2]*JUMP_INCREASE_VELOCITY_RATE +m_acceleration.z/amag*0.2f); + } + Enable(); + } +} +void CPHActorCharacter::SetObjectContactCallback(ObjectContactCallbackFun* callback) +{ + inherited::SetObjectContactCallback(callback); + +} + +void CPHActorCharacter::Disable() +{ + inherited::Disable(); +} + + +struct SFindPredicate +{ + SFindPredicate(const dContact* ac,bool *b) + { + c=ac; + b1=b; + } + bool *b1 ; + const dContact *c ; + bool operator () (SPHCharacterRestrictor* o) + { + *b1=c->geom.g1==o->m_restrictor_transform; + return *b1||c->geom.g2==o->m_restrictor_transform; + } +}; +void CPHActorCharacter::InitContact(dContact* c,bool &do_collide,u16 material_idx_1,u16 material_idx_2 ) +{ + + bool b1; + SFindPredicate fp(c,&b1); + RESTRICTOR_I r=std::find_if(begin(m_restrictors),end(m_restrictors),fp); + bool b_restrictor=(r!=end(m_restrictors)); + SGameMtl* material_1=GMLib.GetMaterialByIdx(material_idx_1); + SGameMtl* material_2=GMLib.GetMaterialByIdx(material_idx_2); + if((material_1&&material_1->Flags.test(SGameMtl::flActorObstacle))||(material_2&&material_2->Flags.test(SGameMtl::flActorObstacle))) + do_collide=true; + if(IsGameTypeSingle()) + { + + if(b_restrictor) + { + b_side_contact=true; + //MulSprDmp(c->surface.soft_cfm,c->surface.soft_erp,def_spring_rate,def_dumping_rate); + c->surface.mu =0.00f; + } + else + inherited::InitContact(c,do_collide,material_idx_1,material_idx_2); + if(b_restrictor&& + do_collide&& + !(b1 ? static_cast(retrieveGeomUserData(c->geom.g2)->ph_object)->ActorMovable():static_cast(retrieveGeomUserData(c->geom.g1)->ph_object)->ActorMovable()) + ) + { + dJointID contact_joint = dJointCreateContactSpecial(0, ContactGroup, c); + Enable(); + CPHObject::Island().DActiveIsland()->ConnectJoint(contact_joint); + if(b1) + dJointAttach (contact_joint, dGeomGetBody(c->geom.g1), 0); + else + dJointAttach (contact_joint, 0, dGeomGetBody(c->geom.g2)); + do_collide=false; + m_friction_factor*=0.1f; + + } + } + else + { + + dxGeomUserData* D1=retrieveGeomUserData(c->geom.g1); + dxGeomUserData* D2=retrieveGeomUserData(c->geom.g2); + if(D1&&D2) + { + CActor* A1=smart_cast(D1->ph_ref_object); + CActor* A2=smart_cast(D2->ph_ref_object); + if(A1&&A2) + { + do_collide=do_collide&&!b_restrictor&&(A1->PPhysicsShell()==0)==(A2->PPhysicsShell()==0); + c->surface.mu=1.f; + } + } + if(do_collide)inherited::InitContact(c,do_collide,material_idx_1,material_idx_2); + } + +} + +void CPHActorCharacter::ChooseRestrictionType (CPHCharacter::ERestrictionType my_type,float my_depth,CPHCharacter *ch) +{ +if (my_type!=rtStalker||(ch->RestrictionType()!=rtStalker&&ch->RestrictionType()!=rtStalkerSmall))return; +float checkR=m_restrictors[rtStalkerSmall]->m_restrictor_radius*1.5f;//+m_restrictors[rtStalker]->m_restrictor_radius)/2.f; + +switch(ch->RestrictionType()) +{ +case rtStalkerSmall: + if(ch->ObjectRadius()>checkR) + { + if(my_depth>0.05f)ch->SetNewRestrictionType(rtStalker); + else ch->SetRestrictionType(rtStalker); +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgActorRestriction)) + Msg("restriction ready to change small -> large"); +#endif + } + break; +case rtStalker: + if(ch->ObjectRadius() small"); +#endif + ch->SetRestrictionType(rtStalkerSmall); + } + break; +default:NODEFAULT; +} + +} \ No newline at end of file diff --git a/src/xrGameLA/PHActorCharacter.h b/src/xrGameLA/PHActorCharacter.h new file mode 100644 index 000000000..a10b6782d --- /dev/null +++ b/src/xrGameLA/PHActorCharacter.h @@ -0,0 +1,115 @@ +#pragma once +#include "phsimplecharacter.h" +#include "PHActorCharacterInline.h" +class CPhysicShellHolder; +struct SPHCharacterRestrictor +{ + SPHCharacterRestrictor (CPHCharacter::ERestrictionType Ttype) + { + m_type=Ttype; + m_character=NULL; + m_restrictor=NULL; + m_restrictor_transform=NULL; + m_restrictor_radius=0.1f; + } + ~SPHCharacterRestrictor () + { + Destroy(); + }; + CPHCharacter* m_character; + CPHCharacter::ERestrictionType m_type; + + dGeomID m_restrictor; + dGeomID m_restrictor_transform; + float m_restrictor_radius; + void SetObjectContactCallback (ObjectContactCallbackFun* callback); + void SetMaterial (u16 material); + void Create (CPHCharacter* ch,dVector3 sizes); + void Destroy (void); + void SetPhysicsRefObject (CPhysicsShellHolder* ref_object); + void SetRadius (float r); +}; + +template +struct TPHCharacterRestrictor : public SPHCharacterRestrictor +{ + TPHCharacterRestrictor():SPHCharacterRestrictor(Ttype){} + void Create(CPHCharacter* ch,dVector3 sizes) + { + dGeomUserDataSetObjectContactCallback(m_restrictor,RestrictorCallBack); + } +static void RestrictorCallBack (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) + { + do_colide=false; + dBodyID b1 = dGeomGetBody(c.geom.g1); + dBodyID b2 = dGeomGetBody(c.geom.g2); + if(!(b1&&b2)) return; + dxGeomUserData *ud1 = retrieveGeomUserData(c.geom.g1); + dxGeomUserData *ud2 = retrieveGeomUserData(c.geom.g2); + if(!(ud1&&ud2))return; + + CPHObject *o1 = NULL;if(ud1)o1=ud1->ph_object; + CPHObject *o2 = NULL;if(ud2)o2=ud2->ph_object; + if(!(o1&&o2)) return; + if(o1->CastType()!=CPHObject::tpCharacter||o2->CastType()!=CPHObject::tpCharacter) return; + + CPHCharacter* ch1 = static_cast(o1); + CPHCharacter* ch2 = static_cast(o2); + + if(bo1) + { + ch1 -> ChooseRestrictionType(Ttype,c.geom.depth,ch2); + do_colide = ch2->TouchRestrictor(Ttype); + } + else + { + ch2 -> ChooseRestrictionType(Ttype,c.geom.depth,ch1); + do_colide = ch1->TouchRestrictor(Ttype); + } + } + }; +DEFINE_VECTOR(SPHCharacterRestrictor*,RESRICTORS_V,RESTRICTOR_I); +//typedef SPHCharacterRestrictor* RESRICTORS_V[2]; +//typedef SPHCharacterRestrictor** RESTRICTOR_I; +IC RESTRICTOR_I begin(RESRICTORS_V& v) +{ + //return v; + return v.begin(); +} + +IC RESTRICTOR_I end(RESRICTORS_V& v) +{ + //return v+sizeof(RESRICTORS_V)/sizeof(SPHCharacterRestrictor*); + return v.end(); +} + +class CPHActorCharacter : + public CPHSimpleCharacter +{ + typedef CPHSimpleCharacter inherited; + + RESRICTORS_V m_restrictors; + float m_speed_goal; +public: + typedef TPHCharacterRestrictor stalker_restrictor; + typedef TPHCharacterRestrictor stalker_small_restrictor; + typedef TPHCharacterRestrictor medium_monster_restrictor; +public: + virtual CPHActorCharacter *CastActorCharacter (){return this;} + virtual void SetObjectContactCallback (ObjectContactCallbackFun* callback); + virtual void SetMaterial (u16 material); + virtual void Create (dVector3 sizes); + virtual void Destroy (void); + virtual void SetPhysicsRefObject (CPhysicsShellHolder* ref_object); + virtual void SetAcceleration (Fvector accel); + virtual void Disable (); + virtual void Jump (const Fvector& jump_velocity); + virtual void InitContact (dContact* c,bool &do_collide,u16 material_idx_1 ,u16 material_idx_2); + void SetRestrictorRadius (CPHCharacter::ERestrictionType rtype,float r); +virtual void ChooseRestrictionType (ERestrictionType my_type,float my_depth,CPHCharacter *ch); + CPHActorCharacter (); + virtual ~CPHActorCharacter (void); +private: + void ClearRestrictors (); + RESTRICTOR_I Restrictor (CPHCharacter::ERestrictionType rtype); +}; diff --git a/src/xrGameLA/PHActorCharacterInline.h b/src/xrGameLA/PHActorCharacterInline.h new file mode 100644 index 000000000..d3f5a12fa --- /dev/null +++ b/src/xrGameLA/PHActorCharacterInline.h @@ -0,0 +1 @@ + diff --git a/src/xrGameLA/PHBaseBodyEffector.h b/src/xrGameLA/PHBaseBodyEffector.h new file mode 100644 index 000000000..58f50131c --- /dev/null +++ b/src/xrGameLA/PHBaseBodyEffector.h @@ -0,0 +1,16 @@ +#ifndef PH_BASE_BODY_EFFECTOR_H +#define PH_BASE_BODY_EFFECTOR_H + +#include "ode_include.h" + +class CPHBaseBodyEffector +{ +protected: + dBodyID m_body; +public: + void Init(dBodyID body) + { + m_body=body; + } +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHCapture.cpp b/src/xrGameLA/PHCapture.cpp new file mode 100644 index 000000000..eeb9b1f4c --- /dev/null +++ b/src/xrGameLA/PHCapture.cpp @@ -0,0 +1,330 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +#include "StdAfx.h" +#include "phcharacter.h" +#include "Physics.h" +#include "ExtendedGeom.h" +#include "PHCapture.h" +#include "entity_alive.h" +#include "phmovementcontrol.h" +#include "../Include/xrRender/Kinematics.h" +#include "../bone.h" +#include "../device.h" +#include "mathutilsode.h" +#include "phelement.h" +#include "characterphysicssupport.h" +/////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////// + + +void CPHCapture::CreateBody() +{ + m_body= dBodyCreate(0); + m_island.AddBody(m_body); + dMass m; + dMassSetSphere(&m,1.f,1000000.f); + dMassAdjust(&m,100000.f); + dBodySetMass(m_body,&m); + dBodySetGravityMode(m_body,0); +} + +CPHCapture::~CPHCapture() +{ + + Deactivate(); +} +void CPHCapture::PhDataUpdate(dReal /**step/**/) +{ + if(b_failed) return; + switch(e_state) + { + case cstPulling: PullingUpdate(); + break; + case cstCaptured: CapturedUpdate(); + break; + case cstReleased: ReleasedUpdate(); + break; + default: NODEFAULT; + } +} + +void CPHCapture::PhTune(dReal /**step/**/) +{ + if(b_failed) return; + + //if(!m_taget_object->PPhysicsShell()) { + // b_failed=true; + // return; //. hack + //} + bool act_capturer=m_character->CPHObject::is_active(); + bool act_taget=m_taget_object->PPhysicsShell()->isEnabled(); + b_disabled=!act_capturer&&!act_taget; + if(act_capturer) + { + m_taget_element->Enable(); + } + if(act_taget) + { + m_character->Enable(); + } + switch(e_state) + { + case cstPulling: ; + break; + case cstCaptured: + { + if(b_disabled) dBodyDisable(m_body); + else + { + m_character->Island().Merge(&m_island); + m_taget_element->PhysicsShell()->PIsland()->Merge(&m_island); + } + } + break; + case cstReleased: ; + break; + default: NODEFAULT; + } + +} + +void CPHCapture::PullingUpdate() +{ + if(!m_taget_element->isActive()||Device.dwTimeGlobal-m_time_start>m_capture_time) + { + Release(); + return; + } + + Fvector dir; + Fvector capture_bone_position; + CObject* object=smart_cast(m_character->PhysicsRefObject()); + capture_bone_position.set(m_capture_bone->mTransform.c); + object->XFORM().transform_tiny(capture_bone_position); + m_taget_element->GetGlobalPositionDynamic(&dir); + dir.sub(capture_bone_position,dir); + float dist=dir.magnitude(); + if(dist>m_pull_distance) + { + Release(); + return; + } + dir.mul(1.f/dist); + if(distget_body()); + dJointAttach(m_ajoint,m_body,m_taget_element->get_body()); + dJointSetFeedback (m_joint, &m_joint_feedback); + dJointSetFeedback (m_ajoint, &m_joint_feedback); + dJointSetBallAnchor(m_joint,capture_bone_position.x,capture_bone_position.y,capture_bone_position.z); + + + dJointSetAMotorAxis (m_ajoint, 0, 1, dir.x, dir.y, dir.z); + + if(dir.x>EPS) + { + if(dir.y>EPS) + { + float mag=dir.x*dir.x+dir.y*dir.y; + dJointSetAMotorAxis (m_ajoint, 2, 2, -dir.y/mag, dir.x/mag, 0.f); + } + else if(dir.z>EPS) + { + float mag=dir.x*dir.x+dir.z*dir.z; + dJointSetAMotorAxis (m_ajoint, 2, 2, -dir.z/mag,0.f,dir.x/mag); + } + else + { + dJointSetAMotorAxis (m_ajoint, 2, 2, 1.f,0.f,0.f); + } + } + else + { + if(dir.y>EPS) + { + + if(dir.z>EPS) + { + float mag=dir.y*dir.y+dir.z*dir.z; + dJointSetAMotorAxis (m_ajoint, 2, 2,0.f,-dir.z/mag,dir.y/mag); + } + else + { + dJointSetAMotorAxis (m_ajoint, 2, 2, 0.f,1.f,0.f); + } + } + else + { + dJointSetAMotorAxis (m_ajoint, 2, 2, 0.f,0.f,1.f); + } + } + //float hi=-M_PI/2.f,lo=-hi; + //dJointSetAMotorParam(m_ajoint,dParamLoStop ,lo); + //dJointSetAMotorParam(m_ajoint,dParamHiStop ,hi); + //dJointSetAMotorParam(m_ajoint,dParamLoStop2 ,lo); + //dJointSetAMotorParam(m_ajoint,dParamHiStop2 ,hi); + //dJointSetAMotorParam(m_ajoint,dParamLoStop3 ,lo); + //dJointSetAMotorParam(m_ajoint,dParamHiStop3 ,hi); + + + dJointSetAMotorParam(m_ajoint,dParamFMax ,m_capture_force*0.2f); + dJointSetAMotorParam(m_ajoint,dParamVel ,0.f); + + dJointSetAMotorParam(m_ajoint,dParamFMax2 ,m_capture_force*0.2f); + dJointSetAMotorParam(m_ajoint,dParamVel2 ,0.f); + + dJointSetAMotorParam(m_ajoint,dParamFMax3 ,m_capture_force*0.2f); + dJointSetAMotorParam(m_ajoint,dParamVel3 ,0.f); + + +/////////////////////////////////// + float sf=0.1f,df=10.f; + + float erp=ERP(world_spring*sf,world_damping*df); + float cfm=CFM(world_spring*sf,world_damping*df); + dJointSetAMotorParam(m_ajoint,dParamStopERP ,erp); + dJointSetAMotorParam(m_ajoint,dParamStopCFM ,cfm); + + dJointSetAMotorParam(m_ajoint,dParamStopERP2 ,erp); + dJointSetAMotorParam(m_ajoint,dParamStopCFM2 ,cfm); + + dJointSetAMotorParam(m_ajoint,dParamStopERP3 ,erp); + dJointSetAMotorParam(m_ajoint,dParamStopCFM3 ,cfm); + ///////////////////////////////////////////////////////////////////// + ///dJointSetAMotorParam(m_joint1,dParamFudgeFactor ,0.1f); + //dJointSetAMotorParam(m_joint1,dParamFudgeFactor2 ,0.1f); + //dJointSetAMotorParam(m_joint1,dParamFudgeFactor3 ,0.1f); + ///////////////////////////////////////////////////////////////////////////// + sf=0.1f,df=10.f; + erp=ERP(world_spring*sf,world_damping*df); + cfm=CFM(world_spring*sf,world_damping*df); + dJointSetAMotorParam(m_ajoint,dParamCFM ,cfm); + dJointSetAMotorParam(m_ajoint,dParamCFM2 ,cfm); + dJointSetAMotorParam(m_ajoint,dParamCFM3 ,cfm); + + +/////////////////////////// + + //dJointSetAMotorParam(m_ajoint,dParamLoStop ,0.f); + //dJointSetAMotorParam(m_ajoint,dParamHiStop ,0.f); + + m_taget_element->set_DynamicLimits(); + //m_taget_object->PPhysicsShell()->set_JointResistance() + e_state=cstCaptured; + return; + } + m_taget_element->applyForce(dir,m_pull_force); +} + +void CPHCapture::CapturedUpdate() +{ + m_island.Unmerge(); + if(m_character->CPHObject::is_active()) + { + m_taget_element->Enable(); + } + + if(!m_taget_element->isActive()||dDOT(m_joint_feedback.f2,m_joint_feedback.f2)>m_capture_force*m_capture_force) + { + Release(); + return; + } + + float mag=dSqrt(dDOT(m_joint_feedback.f1,m_joint_feedback.f1)); + + //m_back_force=m_back_force*0.999f+ ((magm_capture_force/2.2f) + { + float f=mag/(m_capture_force/15.f); + m_character->ApplyForce(m_joint_feedback.f1[0]/f,m_joint_feedback.f1[1]/f,m_joint_feedback.f1[2]/f); + } + + Fvector capture_bone_position; + CObject* object=smart_cast(m_character->PhysicsRefObject()); + capture_bone_position.set(m_capture_bone->mTransform.c); + object->XFORM().transform_tiny(capture_bone_position); + dBodySetPosition(m_body,capture_bone_position.x,capture_bone_position.y,capture_bone_position.z); +} + +void CPHCapture::ReleasedUpdate() +{ + if(b_disabled) return; + if(!b_collide) + { + b_failed=true; + m_taget_element->Enable(); + } + b_collide=false; +} + +void CPHCapture::ReleaseInCallBack() +{ + // if(!b_failed) return; + b_collide=true; +} + + +void CPHCapture::object_contactCallbackFun(bool& do_colide,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/) +{ + + dxGeomUserData *l_pUD1 = NULL; + dxGeomUserData *l_pUD2 = NULL; + l_pUD1 = retrieveGeomUserData(c.geom.g1); + l_pUD2 = retrieveGeomUserData(c.geom.g2); + + if(! l_pUD1) return; + if(!l_pUD2) return; + + CEntityAlive* capturer=smart_cast(l_pUD1->ph_ref_object); + if(capturer) + { + CPHCapture* capture=capturer->character_physics_support()->movement()->PHCapture(); + if(capture) + { + if(capture->m_taget_element->PhysicsRefObject()==l_pUD2->ph_ref_object) + { + do_colide = false; + capture->m_taget_element->Enable(); + if(capture->e_state==CPHCapture::cstReleased) capture->ReleaseInCallBack(); + } + + } + + + } + + capturer=smart_cast(l_pUD2->ph_ref_object); + if(capturer) + { + CPHCapture* capture=capturer->character_physics_support()->movement()->PHCapture(); + if(capture) + { + if(capture->m_taget_element->PhysicsRefObject()==l_pUD1->ph_ref_object) + { + do_colide = false; + capture->m_taget_element->Enable(); + if(capture->e_state==CPHCapture::cstReleased) capture->ReleaseInCallBack(); + } + + } + + } +} +void CPHCapture::net_Relcase(CObject* O) +{ + if(static_cast(m_taget_object)==O) + { + Deactivate(); + } +} \ No newline at end of file diff --git a/src/xrGameLA/PHCapture.h b/src/xrGameLA/PHCapture.h new file mode 100644 index 000000000..fdaf1b108 --- /dev/null +++ b/src/xrGameLA/PHCapture.h @@ -0,0 +1,75 @@ +#pragma once +#ifndef PH_CAPTURE_H +#define PH_CAPTURE_H + +#include "phobject.h" +#include "gameobject.h" +#include "physicsshellholder.h" + +class CPhysicShellHolder; +class CPHCharacter; + +class CPHCapture : public CPHUpdateObject +{ +public: + CPHCapture (CPHCharacter *a_character,CPhysicsShellHolder *a_taget_object); + CPHCapture (CPHCharacter *a_character,CPhysicsShellHolder *a_taget_object,u16 a_taget_elemrnt); +virtual ~CPHCapture (); + + +bool Failed (){return b_failed;}; +void Release (); +void net_Relcase (CObject* O); +protected: +CPHCharacter *m_character; +CPhysicsElement* m_taget_element; +CPhysicsShellHolder* m_taget_object; +dJointID m_joint; +dJointID m_ajoint; +dJointFeedback m_joint_feedback; +Fvector m_capture_pos; +float m_back_force; +float m_pull_force; +float m_capture_force; +float m_capture_distance; +float m_pull_distance; +u32 m_capture_time; +u32 m_time_start; +CBoneInstance *m_capture_bone; +dBodyID m_body; +CPHIsland m_island; +bool b_failed; +bool b_collide; +bool b_disabled; +bool b_character_feedback; + +private: + enum + { + cstPulling, + cstCaptured, + cstReleased + } e_state; + + void PullingUpdate(); + void CapturedUpdate(); + void ReleasedUpdate(); + void ReleaseInCallBack(); + void Init(CInifile* ini); + + void Deactivate(); + void CreateBody(); + bool Invalid(){return + !m_taget_object->PPhysicsShell()|| + !m_taget_object->PPhysicsShell()->isActive()|| + !m_character->b_exist; + }; + +static void object_contactCallbackFun(bool& do_colide,bool bo1,dContact& c,SGameMtl* /*material_1*/,SGameMtl* /*material_2*/); + +///////////CPHObject///////////////////////////// + virtual void PhDataUpdate(dReal step); + virtual void PhTune(dReal step); + +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHCaptureInit.cpp b/src/xrGameLA/PHCaptureInit.cpp new file mode 100644 index 000000000..a2465bc40 --- /dev/null +++ b/src/xrGameLA/PHCaptureInit.cpp @@ -0,0 +1,318 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +#include "StdAfx.h" +#include "phcharacter.h" +#include "Physics.h" +#include "ExtendedGeom.h" +#include "PHCapture.h" +#include "Entity.h" +#include "inventory_item.h" +#include "../Include/xrRender/Kinematics.h" +#include "Actor.h" +#include "Inventory.h" +#include "../bone.h" +#include "../device.h" +extern class CPHWorld *ph_world; +/////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////// +CPHCapture::CPHCapture (CPHCharacter *a_character, CPhysicsShellHolder *a_taget_object) +{ + CPHUpdateObject::Activate(); + + m_joint =NULL; + m_ajoint =NULL; + m_body =NULL; + m_taget_object =NULL; + m_character =NULL; + b_failed =false; + b_disabled =false; + b_character_feedback =false; + e_state =cstPulling; + + if(!a_taget_object || + !a_taget_object->m_pPhysicsShell || + !a_taget_object->m_pPhysicsShell->isActive()|| + smart_cast(a_taget_object) + ) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + if(!a_character||!a_character->b_exist) + { + m_taget_object=NULL; + b_failed=true; + return; + } + m_taget_object =a_taget_object; + m_character =a_character; + + CObject* capturer_object=smart_cast(m_character->PhysicsRefObject()); + + if(!capturer_object) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + IKinematics* p_kinematics=smart_cast(capturer_object->Visual()); + + if(!p_kinematics) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + CInifile* ini=p_kinematics->LL_UserData(); + + if(!ini) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + if(!ini->section_exist("capture")) + { + m_taget_object=NULL; + b_failed=true; + return; + } + u16 capture_bone_id=p_kinematics->LL_BoneID(ini->r_string("capture","bone")); + R_ASSERT2(capture_bone_id!=BI_NONE,"wrong capture bone"); + m_capture_bone=&p_kinematics->LL_GetBoneInstance(capture_bone_id); + + + + m_taget_element =m_taget_object->m_pPhysicsShell->NearestToPoint(m_capture_bone->mTransform.c); + + Init(ini); + +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +CPHCapture::CPHCapture(CPHCharacter *a_character,CPhysicsShellHolder *a_taget_object,u16 a_taget_element) +{ + + CPHUpdateObject::Activate(); + m_joint =NULL; + m_ajoint =NULL; + m_body =NULL; + b_failed =false; + b_disabled =false; + e_state =cstPulling; + b_character_feedback =false; + m_taget_object =NULL; + m_character =NULL; + if(!a_taget_object || + !a_taget_object->m_pPhysicsShell || + !a_taget_object->m_pPhysicsShell->isActive() || + smart_cast(a_taget_object) + ) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + if(!a_character||!a_character->b_exist) + { + m_taget_object=NULL; + b_failed=true; + return; + } + m_taget_object =a_taget_object; + m_character =a_character; + + CObject* capturer_object=smart_cast(m_character->PhysicsRefObject()); + + if(!capturer_object) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + IKinematics* p_kinematics=smart_cast(capturer_object->Visual()); + + if(!p_kinematics) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + CInifile* ini=p_kinematics->LL_UserData(); + + if(!ini) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + if(a_taget_element==BI_NONE) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + if(!ini->section_exist("capture")) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + u16 capture_bone_id=p_kinematics->LL_BoneID(ini->r_string("capture","bone")); + R_ASSERT2(capture_bone_id!=BI_NONE,"wrong capture bone"); + m_capture_bone=&p_kinematics->LL_GetBoneInstance(capture_bone_id); + + + + IRenderVisual* V=m_taget_object->Visual(); + + if(!V) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + IKinematics* K= smart_cast(V); + + if(!K) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + CBoneInstance& tag_bone=K->LL_GetBoneInstance(a_taget_element); + + if(!tag_bone.callback_param()) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + m_taget_element =(CPhysicsElement*)tag_bone.callback_param(); + + if(!m_taget_element) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + Init(ini); + +} + + + +void CPHCapture::Init(CInifile* ini) +{ + Fvector dir; + Fvector capture_bone_position; + capture_bone_position.set(m_capture_bone->mTransform.c); + b_character_feedback=true; + (m_character->PhysicsRefObject())->XFORM().transform_tiny(capture_bone_position); + + + m_taget_element->GetGlobalPositionDynamic(&dir); + dir.sub(capture_bone_position,dir); + + + m_pull_distance=ini->r_float("capture","pull_distance"); + if(dir.magnitude()>m_pull_distance) + { + m_taget_object=NULL; + b_failed=true; + return; + } + + float pool_force_factor=4.f; + + m_capture_distance =ini->r_float("capture","distance"); //distance + m_capture_force =ini->r_float("capture","capture_force"); //capture force + m_capture_time =ini->r_u32("capture","time_limit")*1000; //time; + m_time_start =Device.dwTimeGlobal; + float max_pull_force =ini->r_float("capture","pull_force"); //pull force + m_pull_force =pool_force_factor*ph_world->Gravity()*m_taget_element->PhysicsShell()->getMass(); + if(m_pull_force>max_pull_force) m_pull_force=max_pull_force; + + + + float pulling_vel_scale =ini->r_float("capture","velocity_scale"); // + m_taget_element->set_DynamicLimits(default_l_limit*pulling_vel_scale,default_w_limit*pulling_vel_scale); + //m_taget_element->PhysicsShell()->set_ObjectContactCallback(object_contactCallbackFun); + m_character->SetObjectContactCallback(object_contactCallbackFun); + m_island.Init(); + CActor* A=smart_cast(m_character->PhysicsRefObject()); + if(A) + { + A->SetWeaponHideState(INV_STATE_BLOCK_ALL,true); + } +} + +void CPHCapture::Release() +{ + if(b_failed) return; + if( e_state==cstReleased) return; + if(m_joint) + { + m_island.RemoveJoint(m_joint); + + dJointDestroy(m_joint); + + } + m_joint=NULL; + if(m_ajoint) + { + m_island.RemoveJoint(m_ajoint); + dJointDestroy(m_ajoint); + } + m_ajoint=NULL; + if(m_body) + { + m_island.RemoveBody(m_body); + dBodyDestroy(m_body); + } + m_body=NULL; + + if(e_state==cstPulling&&m_taget_element&&!m_taget_object->getDestroy()&&m_taget_object->PPhysicsShell()&&m_taget_object->PPhysicsShell()->isActive()) + { + m_taget_element->set_DynamicLimits(); + } + + + e_state=cstReleased; + b_collide=true; + CActor* A=smart_cast(m_character->PhysicsRefObject()); + if(A) + { + A->SetWeaponHideState(INV_STATE_BLOCK_ALL,false); +//. A->inventory().setSlotsBlocked(false); + } +} + +void CPHCapture::Deactivate() +{ + Release(); + //if(m_taget_object&&m_taget_element&&!m_taget_object->getDestroy()&&m_taget_object->m_pPhysicsShell&&m_taget_object->m_pPhysicsShell->isActive()) + //{ + // m_taget_element->set_ObjectContactCallback(0); + + //} + if(m_character)m_character->SetObjectContactCallback(0); + CPHUpdateObject::Deactivate(); + m_character =NULL; + m_taget_object =NULL; + m_taget_element =NULL; +} \ No newline at end of file diff --git a/src/xrGameLA/PHCharacter.cpp b/src/xrGameLA/PHCharacter.cpp new file mode 100644 index 000000000..94e435fe3 --- /dev/null +++ b/src/xrGameLA/PHCharacter.cpp @@ -0,0 +1,162 @@ +#include "stdafx.h" + +#include "phcharacter.h" +#include "PHDynamicData.h" +#include "Physics.h" +#include "ExtendedGeom.h" +#include "../cl_intersect.h" +#include "tri-colliderKNoOPC\__aabb_tri.h" +#include "../../xrODE/ode/src/util.h" +#include "mathutilsode.h" + +CPHCharacter::CPHCharacter(void): + CPHDisablingTranslational() +{ + +m_params.acceleration =0.001f ; +m_params.velocity =0.0001f ; +m_body =NULL ; +m_safe_velocity[0] =0.f ; +m_safe_velocity[1] =0.f ; +m_safe_velocity[2] =0.f ; +m_mean_y =0.f ; +m_new_restriction_type=m_restriction_type =rtNone ; +b_actor_movable =true ; +p_lastMaterialIDX =&lastMaterialIDX ; +lastMaterialIDX =u16(-1) ; +m_creation_step =u64(-1) ; +b_in_touch_resrtrictor =false ; +m_current_object_radius =-1.f ; +} + +CPHCharacter::~CPHCharacter(void) +{ + +} + +void CPHCharacter::FreezeContent() +{ + + dBodyDisable(m_body); + CPHObject::FreezeContent(); +} +void CPHCharacter::UnFreezeContent() +{ + + dBodyEnable(m_body); + CPHObject::UnFreezeContent(); +} +void CPHCharacter::getForce(Fvector& force) +{ + if(!b_exist)return; + force.set(*(Fvector*)dBodyGetForce(m_body)); +} +void CPHCharacter::setForce(const Fvector &force) +{ + if(!b_exist)return; + dBodySetForce(m_body,force.x,force.y,force.z); +} + + +void CPHCharacter::get_State(SPHNetState& state) +{ + GetPosition(state.position); + m_body_interpolation.GetPosition(state.previous_position,0); + GetVelocity(state.linear_vel); + getForce(state.force); + + state.angular_vel.set(0.f,0.f,0.f); + state.quaternion.identity(); + state.previous_quaternion.identity(); + state.torque.set(0.f,0.f,0.f); +// state.accel = GetAcceleration(); +// state.max_velocity = GetMaximumVelocity(); + + if(!b_exist) + { + state.enabled=false; + return; + } + state.enabled=CPHObject::is_active();//!!dBodyIsEnabled(m_body); +} +void CPHCharacter::set_State(const SPHNetState& state) +{ + m_body_interpolation.SetPosition(state.previous_position,0); + m_body_interpolation.SetPosition(state.position,1); + SetPosition(state.position); + SetVelocity(state.linear_vel); + setForce(state.force); + +// SetAcceleration(state.accel); +// SetMaximumVelocity(state.max_velocity); + + if(!b_exist) return; + if(state.enabled) + { + Enable(); + }; + if(!state.enabled ) + { + Disable(); + }; + VERIFY2(dBodyStateValide(m_body),"WRONG BODYSTATE WAS SET"); +} + +void CPHCharacter::Disable() +{ + + CPHObject::deactivate(); + dBodyDisable(m_body); + m_body_interpolation.ResetPositions(); +} + +void CPHCharacter::Enable() +{ + if(!b_exist) return; + CPHObject::activate(); + dBodyEnable(m_body); + +} + + + + + + + +void CarHitCallback(bool& /**do_colide/**/,dContact& /**c/**/) +{ + +} + +void CPHCharacter::GetSavedVelocity(Fvector& vvel) +{ + + if(IsEnabled())vvel.set(m_safe_velocity); + else GetVelocity(vvel); +} + +void CPHCharacter::CutVelocity(float l_limit,float /*a_limit*/) +{ + dVector3 limitedl,diffl; + if(dVectorLimit(dBodyGetLinearVel(m_body),l_limit,limitedl)) + { + dVectorSub(diffl,limitedl,dBodyGetLinearVel(m_body)); + dBodySetLinearVel(m_body,diffl[0],diffl[1],diffl[2]); + dBodySetAngularVel(m_body,0.f,0.f,0.f); + dxStepBody(m_body,fixed_step); + dBodySetLinearVel(m_body,limitedl[0],limitedl[1],limitedl[2]); + } +} + +void CPHCharacter:: fix_body_rotation () +{ + dBodyID b= get_body();//GetBody(); + if(b) + { + dMatrix3 R; + dRSetIdentity (R); + dBodySetAngularVel(b,0.f,0.f,0.f); + dBodySetRotation(b,R); + } +} diff --git a/src/xrGameLA/PHCharacter.h b/src/xrGameLA/PHCharacter.h new file mode 100644 index 000000000..429a411f5 --- /dev/null +++ b/src/xrGameLA/PHCharacter.h @@ -0,0 +1,170 @@ +#pragma once +#include "PHObject.h" +#include "PHInterpolation.h" +#include "PHSynchronize.h" +#include "PHDisabling.h" +class CPhysicsShellHolder; +class CClimableObject; +class CGameObject; +class ICollisionDamageInfo; +class CElevatorState; +class CPHActorCharacter; +class CPHAICharacter; + static enum EEnvironment + { + peOnGround, + peAtWall, + peInAir + }; + + +class CPHCharacter : + public CPHObject, + public CPHSynchronize, + public CPHDisablingTranslational +#ifdef DEBUG + ,public pureRender +#endif +{ +public: + +u64 m_creation_step ; +bool b_exist ; +protected: + +////////////////////////// dynamic + +CPHInterpolation m_body_interpolation; +dBodyID m_body; +CPhysicsShellHolder* m_phys_ref_object; + + +dReal m_mass; +bool was_enabled_before_freeze; + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////// +u16* p_lastMaterialIDX; +u16 lastMaterialIDX; +/////////////////////////////////////////////////////////////////////////// +dVector3 m_safe_velocity; +dVector3 m_safe_position; +dReal m_mean_y; +public: +enum ERestrictionType +{ + + rtStalker =0 , + rtStalkerSmall , + rtMonsterMedium , + rtNone , + rtActor +}; + +private: +protected: +ERestrictionType m_new_restriction_type; +ERestrictionType m_restriction_type; +bool b_actor_movable; + +bool b_in_touch_resrtrictor ; +float m_current_object_radius ; +public: + virtual ECastType CastType (){return CPHObject::tpCharacter;} + virtual CPHActorCharacter *CastActorCharacter (){return NULL;} + virtual CPHAICharacter *CastAICharacter (){return NULL;} + ERestrictionType RestrictionType (){return m_restriction_type;} + void SetNewRestrictionType (ERestrictionType rt){m_new_restriction_type=rt;} + void SetRestrictionType (ERestrictionType rt){m_new_restriction_type=m_restriction_type=rt;} + void SetObjectRadius (float R){m_current_object_radius=R;} + float ObjectRadius (){return m_current_object_radius;} + virtual void ChooseRestrictionType (ERestrictionType my_type,float my_depth,CPHCharacter *ch) {} + virtual bool UpdateRestrictionType (CPHCharacter* ach) =0; + virtual void FreezeContent () ; + virtual void UnFreezeContent () ; + virtual dBodyID get_body () {return m_body;} + virtual void fix_body_rotation () ; + virtual dSpaceID dSpace () =0; + virtual void Disable () ; + virtual void ReEnable () {;} + virtual void Enable () ; //!! + virtual void SwitchOFFInitContact () =0; + virtual void SwitchInInitContact () =0; + bool IsEnabled () { if(!b_exist)return false; return !!dBodyIsEnabled(m_body);} + bool ActorMovable () {return b_actor_movable;} + void SetActorMovable (bool v) {b_actor_movable=v;} +virtual const ICollisionDamageInfo *CollisionDamageInfo()const =0; +virtual void Reinit () =0; +void SetPLastMaterialIDX (u16* p) {p_lastMaterialIDX=p;} +const u16 &LastMaterialIDX ()const {return *p_lastMaterialIDX;} +virtual bool TouchRestrictor (ERestrictionType rttype) =0; +virtual void SetElevator (CClimableObject* climable) {}; +virtual void SetMaterial (u16 material) =0 ; +virtual void SetMaximumVelocity (dReal /**vel/**/) {} //!! +virtual dReal GetMaximumVelocity () {return 0;} +virtual void SetJupmUpVelocity (dReal /**velocity/**/) {} //!! +virtual void IPosition (Fvector& /**pos/**/) {} +virtual u16 ContactBone () {return 0;} +virtual void DeathPosition (Fvector& /**deathPos/**/) {} +virtual void ApplyImpulse (const Fvector& /**dir/**/,const dReal /**P/**/) {} +virtual void ApplyForce (const Fvector& force) =0 ; +virtual void ApplyForce (const Fvector& dir,float force) =0 ; +virtual void ApplyForce (float x,float y, float z) =0 ; +virtual void AddControlVel (const Fvector& vel) =0 ; +virtual void Jump (const Fvector& jump_velocity) =0 ; +virtual bool JumpState () =0 ; +virtual EEnvironment CheckInvironment () =0 ; +virtual bool ContactWas () =0 ; +virtual void GroundNormal (Fvector &norm) =0 ; +virtual void Create (dVector3 /**sizes/**/) =0 ; +virtual void Destroy (void) =0 ; +virtual void SetBox (const dVector3 &sizes) =0 ; +virtual void SetAcceleration (Fvector accel) =0 ; +virtual void SetForcedPhysicsControl (bool v){} +virtual bool ForcedPhysicsControl () {return false;} +virtual void SetCamDir (const Fvector& cam_dir) =0 ; +virtual const Fvector& CamDir ()const =0 ; +virtual Fvector GetAcceleration () =0 ; +virtual void SetPosition (Fvector pos) =0 ; +virtual void SetApplyGravity (BOOL flag) { dBodySetGravityMode(m_body,flag); } +virtual void SetObjectContactCallback (ObjectContactCallbackFun* callback) =0 ; +virtual void SetWheelContactCallback (ObjectContactCallbackFun* callback) =0 ; +virtual ObjectContactCallbackFun* ObjectContactCallBack () {return NULL;} +virtual void GetVelocity (Fvector& vvel) =0 ; +virtual void GetSavedVelocity (Fvector& vvel) ; +virtual void GetSmothedVelocity (Fvector& vvel) =0 ; +virtual void SetVelocity (Fvector vel) =0 ; +virtual void SetAirControlFactor (float factor) =0 ; +virtual void GetPosition (Fvector& vpos) =0 ; +virtual void GetFootCenter (Fvector& vpos) {vpos.set(*(Fvector*)dBodyGetPosition(m_body));} +virtual void SetMas (dReal mass) =0 ; +virtual void SetCollisionDamageFactor (float f) =0 ; +virtual float Mass () =0 ; +virtual void SetPhysicsRefObject (CPhysicsShellHolder* ref_object) =0 ; +virtual CPhysicsShellHolder* PhysicsRefObject () {return m_phys_ref_object;} + +//AICharacter +virtual void GetDesiredPosition (Fvector& /**dpos/**/) {} +virtual void SetDesiredPosition (const Fvector& /**pos/**/) {} +virtual void BringToDesired (float /**time/**/,float /**velocity/**/,float force=1.f) {} +virtual bool TryPosition (Fvector /**pos/**/,bool) {return false;} +virtual bool TouchBorder () {return false;} +virtual void getForce (Fvector& force) ; +virtual void setForce (const Fvector& force) ; +virtual float FootRadius () =0 ; +virtual void get_State ( SPHNetState& state) ; +virtual void set_State (const SPHNetState& state) ; +virtual void cv2obj_Xfrom (const Fquaternion& q,const Fvector& pos, Fmatrix& xform) {;} +virtual void cv2bone_Xfrom (const Fquaternion& q,const Fvector& pos, Fmatrix& xform) {;} +virtual const Fvector& ControlAccel ()const =0; +virtual float &FrictionFactor () =0; +virtual void CutVelocity (float l_limit,float a_limit) ; +virtual u16 get_elements_number () {return 1;}; +virtual CPHSynchronize *get_element_sync (u16 element) {VERIFY(element==0);return static_cast(this);}; +virtual CElevatorState *ElevatorState () =0; + CPHCharacter (void) ; +virtual ~CPHCharacter (void) ; +}; + + diff --git a/src/xrGameLA/PHCollideValidator.cpp b/src/xrGameLA/PHCollideValidator.cpp new file mode 100644 index 000000000..115466f58 --- /dev/null +++ b/src/xrGameLA/PHCollideValidator.cpp @@ -0,0 +1,121 @@ +#include "stdafx.h" +#include "PHObject.h" +#include "phcollidevalidator.h" + +CGID CPHCollideValidator::freeGroupID=0; +_flags CPHCollideValidator::ClassFlags={CLClassBits(0)}; +_flags CPHCollideValidator::ClassNCFlags={CLClassBits(0)}; +_flags CPHCollideValidator::NonTypeFlags={CLClassBits(0)}; +void CPHCollideValidator::Init() +{ + freeGroupID=0; + NonTypeFlags.set(cbNCGroupObject,TRUE); +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + ClassFlags.set(cbClassDynamic|cbClassCharacter|cbClassRagDoll|cbClassAnimated,TRUE); + ClassNCFlags.set(cbNCClassCharacter|cbNCClassDynamic|cbNCClassRagDoll|cbNCClassAnimated,TRUE); +#else + ClassFlags.set(cbClassDynamic|cbClassCharacter|cbClassRagDoll,TRUE); + ClassNCFlags.set(cbNCClassCharacter|cbNCClassDynamic|cbNCClassRagDoll,TRUE); +#endif +} +CGID CPHCollideValidator::RegisterGroup() +{ + ++freeGroupID; + return freeGroupID-1; +} + +void CPHCollideValidator::InitObject(CPHObject& obj) +{ + obj.collide_class_bits().assign(0); + obj.collide_class_bits().set(cbClassDynamic,TRUE); + obj.collide_bits()=0; +} +void CPHCollideValidator::RegisterObjToGroup(CGID group,CPHObject& obj) +{ + R_ASSERT(group ClassFlags; +static _flags ClassNCFlags; +static _flags NonTypeFlags; +}; + + +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHCollisionDamageReceiver.cpp b/src/xrGameLA/PHCollisionDamageReceiver.cpp new file mode 100644 index 000000000..8b153a754 --- /dev/null +++ b/src/xrGameLA/PHCollisionDamageReceiver.cpp @@ -0,0 +1,115 @@ +#include "stdafx.h" +#include "phcollisiondamagereceiver.h" +#include "PhysicsShellHolder.h" +#include "xr_ini.h" +#include "../Include/xrRender/Kinematics.h" +#include "geometry.h" +#include "PhysicsShell.h" +#include "../GameMtlLib.h" +#include "Physics.h" +#include "../../xrNetServer/net_utils.h" +#include "xrMessages.h" +#include "CharacterPhysicsSupport.h" + +void CPHCollisionDamageReceiver::BoneInsert(u16 id,float k) +{ + R_ASSERT2(FindBone(id)==m_controled_bones.end(),"duplicate bone!"); + m_controled_bones.push_back(SControledBone(id,k)); +} +void CPHCollisionDamageReceiver::Init() +{ + CPhysicsShellHolder *sh =PPhysicsShellHolder (); + IKinematics *K =smart_cast(sh->Visual()); + CInifile *ini=K->LL_UserData(); + if(ini->section_exist("collision_damage")) + { + + CInifile::Sect& data = ini->r_section("collision_damage"); + for (CInifile::SectCIt I=data.Data.begin(); I!=data.Data.end(); I++){ + const CInifile::Item& item = *I; + u16 index = K->LL_BoneID(*item.first); + R_ASSERT3(index != BI_NONE, "Wrong bone name", *item.first); + BoneInsert(index,float(atof(*item.second))); + CODEGeom* og= sh->PPhysicsShell()->get_GeomByID(index); + //R_ASSERT3(og, "collision damage bone has no physics collision", *item.first); + if(og)og->add_obj_contact_cb(CollisionCallback); + } + } + + m_hit_threshold = 5.f; +} + + + + +void CPHCollisionDamageReceiver::CollisionCallback(bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + if(material_1->Flags.test(SGameMtl::flPassable)||material_2->Flags.test(SGameMtl::flPassable))return; + dBodyID b1 = dGeomGetBody(c.geom.g1) ; + dBodyID b2 = dGeomGetBody(c.geom.g2) ; + dxGeomUserData *ud_self = bo1 ? retrieveGeomUserData(c.geom.g1):retrieveGeomUserData(c.geom.g2); + dxGeomUserData *ud_damager = bo1 ? retrieveGeomUserData(c.geom.g2):retrieveGeomUserData(c.geom.g1); + + SGameMtl *material_self = bo1 ? material_1:material_2; + SGameMtl *material_damager = bo1 ? material_2:material_1; + VERIFY (ud_self); + CPhysicsShellHolder *o_self = ud_self->ph_ref_object; + CPhysicsShellHolder *o_damager = NULL;if(ud_damager)o_damager=ud_damager->ph_ref_object; + u16 source_id = o_damager ? o_damager->ID():u16(-1); + CPHCollisionDamageReceiver *dr =o_self->PHCollisionDamageReceiver(); + VERIFY2(dr,"wrong callback"); + + float damager_material_factor=material_damager->fBounceDamageFactor; + + if(ud_damager&&ud_damager->ph_object&&ud_damager->ph_object->CastType()==CPHObject::tpCharacter) + { + CCharacterPhysicsSupport* phs=o_damager->character_physics_support(); + if(phs->IsSpecificDamager())damager_material_factor=phs->BonceDamageFactor(); + } + + float dfs=(material_self->fBounceDamageFactor+damager_material_factor); + if(fis_zero(dfs)) return; + Fvector dir;dir.set(*(Fvector*)c.geom.normal); + Fvector pos; + pos.sub(*(Fvector*)c.geom.pos,*(Fvector*)dGeomGetPosition(bo1 ? c.geom.g1:c.geom.g2));//it is not true pos in bone space + dr->Hit(source_id,ud_self->bone_id,E_NL(b1,b2,c.geom.normal)*damager_material_factor/dfs,dir,pos); + +} + +void CPHCollisionDamageReceiver::Hit(u16 source_id,u16 bone_id,float power,const Fvector& dir,Fvector &pos ) +{ + + DAMAGE_BONES_I i=FindBone(bone_id); + if(i==m_controled_bones.end())return; + power*=i->second; + if (power < m_hit_threshold) return; + + NET_Packet P; + CPhysicsShellHolder *ph=PPhysicsShellHolder(); + SHit HS; + + HS.GenHeader(GE_HIT,ph->ID()); // ph->u_EventGen(P,GE_HIT,ph->ID()); + HS.whoID = ph->ID(); // P.w_u16 (ph->ID()); + HS.weaponID = source_id; // P.w_u16 (source_id); + HS.dir = dir; // P.w_dir (dir); + HS.power = power; // P.w_float (power); + HS.boneID = s16(bone_id); // P.w_s16 (s16(bone_id)); + HS.p_in_bone_space = pos; // P.w_vec3 (pos); + HS.impulse = 0.f; // P.w_float (0.f); + HS.hit_type = (ALife::eHitTypeStrike); // P.w_u16 (ALife::eHitTypeStrike); + HS.Write_Packet(P); + + ph->u_EventSend(P); +} + +void CPHCollisionDamageReceiver::Clear() +{ + //CPhysicsShellHolder *sh =PPhysicsShellHolder (); + //xr_map::iterator i=m_controled_bones.begin(),e=m_controled_bones.end(); + //for(;e!=i;++i) + //{ + // CODEGeom* og= sh->PPhysicsShell()->get_GeomByID(i->first); + // if(og)og->set_obj_contact_cb(NULL); + //} + m_controled_bones.clear(); +} \ No newline at end of file diff --git a/src/xrGameLA/PHCollisionDamageReceiver.h b/src/xrGameLA/PHCollisionDamageReceiver.h new file mode 100644 index 000000000..38e131083 --- /dev/null +++ b/src/xrGameLA/PHCollisionDamageReceiver.h @@ -0,0 +1,32 @@ +#pragma once + +class CPhysicsShellHolder; +struct dContact; +struct SGameMtl; + +class CPHCollisionDamageReceiver +{ +typedef std::pair SControledBone; +DEFINE_VECTOR(SControledBone,DAMAGE_CONTROLED_BONES_V,DAMAGE_BONES_I); +struct SFind{u16 id;SFind(u16 _id){id=_id;};bool operator () (const SControledBone& cb){return cb.first==id;}}; +DAMAGE_CONTROLED_BONES_V m_controled_bones; +float m_hit_threshold = 5.f; + +public: + void SetCollisionHitThreshold (float v) { m_hit_threshold = v; } + float CollisionHitThreshold () { return m_hit_threshold; } + +protected: + virtual CPhysicsShellHolder* PPhysicsShellHolder () =0; + void Init () ; + void Hit (u16 source_id,u16 bone_id,float power,const Fvector &dir,Fvector &pos) ; + void Clear () ; +private: + void BoneInsert (u16 id,float k) ; + + IC DAMAGE_BONES_I FindBone (u16 id) + { + return std::find_if(m_controled_bones.begin(),m_controled_bones.end(),SFind(id)); + } + static void CollisionCallback (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) ; +}; diff --git a/src/xrGameLA/PHCommander.cpp b/src/xrGameLA/PHCommander.cpp new file mode 100644 index 000000000..4968897e7 --- /dev/null +++ b/src/xrGameLA/PHCommander.cpp @@ -0,0 +1,240 @@ +#include "stdafx.h" +#include "PHCommander.h" + + +CPHCall::CPHCall(CPHCondition* condition,CPHAction* action) +{ + m_action=action; + m_condition=condition; +} + +CPHCall::~CPHCall() +{ + xr_delete(m_action); + xr_delete(m_condition); +} +bool CPHCall::obsolete() +{ + return m_action->obsolete()||m_condition->obsolete(); +} + +void CPHCall::check() +{ + if(m_condition->is_true())m_action->run(); +} + +bool CPHCall::equal(CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action) +{ + return m_action->compare(cmp_action)&&m_condition->compare(cmp_condition); +} +bool CPHCall::is_any(CPHReqComparerV* v) +{ + return m_action->compare(v)||m_condition->compare(v); +} + +void delete_call(CPHCall* &call) +{ + try{ + xr_delete(call); + } + catch(...) + { + call=NULL; + } +} +///////////////////////////////////////////////////////////////////////////////// +CPHCommander::~CPHCommander() +{ + clear(); +} +void CPHCommander::clear () +{ + while (m_calls.size()) { + remove_call(m_calls.end()-1); + } + while (m_calls_as_add_buffer.size()) { + remove_call(m_calls_as_add_buffer.end()-1); + } + while (m_calls_as_remove_buffer.size()) { + remove_call(m_calls_as_remove_buffer.end()-1); + } +} + +void CPHCommander::update () +{ + for(u32 i=0; icheck(); + } + catch(...) + { + remove_call(m_calls.begin()+i); + i--; + continue; + } + + if(m_calls[i]->obsolete()) + { + remove_call(m_calls.begin()+i); + i--; + continue; + } + } +} + +void CPHCommander::add_call(CPHCondition* condition,CPHAction* action,PHCALL_STORAGE& cs) +{ + cs.push_back(new CPHCall(condition,action)); +} +void CPHCommander::add_call(CPHCondition* condition,CPHAction* action) +{ + add_call(condition,action,m_calls); + +} +void CPHCommander::remove_call(PHCALL_I i,PHCALL_STORAGE& cs) +{ + delete_call(*i); + cs.erase(i); +} + +void CPHCommander::remove_call(PHCALL_I i) +{ + remove_call(i,m_calls); +} + + + +struct SFEqualPred +{ + CPHReqComparerV* cmp_condition,*cmp_action; + SFEqualPred(CPHReqComparerV* cmp_c,CPHReqComparerV* cmp_a) + { + cmp_condition=cmp_c;cmp_action=cmp_a; + } + bool operator()(CPHCall* call) + { + return call->equal(cmp_condition,cmp_action); + } +}; +struct SFRemovePred2 +{ + CPHReqComparerV* cmp_condition,*cmp_action; + SFRemovePred2(CPHReqComparerV* cmp_c,CPHReqComparerV* cmp_a) + { + cmp_condition=cmp_c;cmp_action=cmp_a; + } + bool operator()(CPHCall* call) + { + if(call->equal(cmp_condition,cmp_action)) + { + delete_call(call); + return true; + } + return false; + + } +}; + +PHCALL_I CPHCommander::find_call(CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action,PHCALL_STORAGE& cs) +{ + return std::find_if( cs.begin(),cs.end(),SFEqualPred(cmp_condition,cmp_action)); +} +PHCALL_I CPHCommander::find_call(CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action) +{ + return find_call(cmp_condition,cmp_action,m_calls); +} +void CPHCommander::remove_call(CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action,PHCALL_STORAGE& cs) +{ + cs.erase ( + std::remove_if( + cs.begin(), + cs.end(), + SFRemovePred2( + cmp_condition, + cmp_action + ) + ), + cs.end() + ); +} + +void CPHCommander::remove_call(CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action) +{ + remove_call(cmp_condition,cmp_action,m_calls); +} + +void CPHCommander::add_call_unique(CPHCondition* condition,CPHReqComparerV* cmp_condition,CPHAction* action,CPHReqComparerV* cmp_action,PHCALL_STORAGE& cs) +{ + if(cs.end()==find_call(cmp_condition,cmp_action,cs)) + { + add_call(condition,action,cs); + } +} +void CPHCommander::add_call_unique(CPHCondition* condition,CPHReqComparerV* cmp_condition,CPHAction* action,CPHReqComparerV* cmp_action) +{ + add_call_unique(condition,cmp_condition,action,cmp_action,m_calls); +} +struct SRemoveRped +{ + CPHReqComparerV* cmp_object; + SRemoveRped(CPHReqComparerV* cmp_o) + { + cmp_object=cmp_o; + } + bool operator() (CPHCall* call) + { + if(call->is_any(cmp_object)) + { + delete_call(call); + return true; + } + else + return false; + } +}; +void CPHCommander::remove_calls(CPHReqComparerV* cmp_object,PHCALL_STORAGE& cs) +{ + cs.erase ( + std::remove_if( + cs.begin(), + cs.end(), + SRemoveRped(cmp_object) + ), + cs.end() + ); +} +void CPHCommander::remove_calls(CPHReqComparerV* cmp_object) +{ + remove_calls(cmp_object,m_calls); +} + +void CPHCommander:: add_call_unique_as (CPHCondition* condition,CPHReqComparerV* cmp_condition,CPHAction* action,CPHReqComparerV* cmp_action) +{ + add_call_unique(condition,cmp_condition,action,cmp_action,m_calls_as_add_buffer); +} +void CPHCommander:: add_call_as (CPHCondition* condition,CPHAction* action) +{ + add_call(condition,action,m_calls_as_add_buffer); +} + + +PHCALL_I CPHCommander:: find_call_as (CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action) +{ + return find_call(cmp_condition,cmp_action,m_calls); +} +void CPHCommander:: remove_call_as (CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action) +{ + remove_call(cmp_condition,cmp_action,m_calls_as_add_buffer); + +} +void CPHCommander:: remove_calls_as (CPHReqComparerV* cmp_object) +{ + +} + +void CPHCommander:: update_as () +{ + +} \ No newline at end of file diff --git a/src/xrGameLA/PHCommander.h b/src/xrGameLA/PHCommander.h new file mode 100644 index 000000000..19effc2e0 --- /dev/null +++ b/src/xrGameLA/PHCommander.h @@ -0,0 +1,104 @@ +#ifndef PH_COMMANDER_H +#define PH_COMMANDER_H +class CPHReqBase; +class CPHReqComparerV; + +class CPHReqBase +{ +public: + virtual ~CPHReqBase () {} + virtual bool obsolete () const =0 ; + virtual bool compare (const CPHReqComparerV* v) const {return false;} ; +}; + + + + +class CPHCondition : + public CPHReqBase +{ +public: + virtual bool is_true () =0 ; +}; + +class CPHAction: + public CPHReqBase +{ +public: + virtual void run () =0 ; +}; + +class CPHOnesCondition: + public CPHCondition +{ + bool b_called; +public: + CPHOnesCondition (){b_called=false;} + virtual bool is_true (){b_called =true;return true;} + virtual bool obsolete ()const{return b_called;} +}; + +class CPHDummiAction: + public CPHAction +{ +public: + virtual void run (){;} + virtual bool obsolete ()const {return false;} +}; + +class CPHCall +{ + CPHAction* m_action ; + CPHCondition* m_condition ; +public: + CPHCall (CPHCondition* condition,CPHAction* action) ; + ~CPHCall () ; + void check () ; + bool obsolete () ; + bool equal (CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action); + bool is_any (CPHReqComparerV* v) ; +}; + +DEFINE_VECTOR(CPHCall*,PHCALL_STORAGE,PHCALL_I); +class CPHCommander +{ + + PHCALL_STORAGE m_calls; + PHCALL_STORAGE m_calls_as_add_buffer; + PHCALL_STORAGE m_calls_as_remove_buffer; +public: + ~CPHCommander () ; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void add_call_unique (CPHCondition* condition,CPHReqComparerV* cmp_condition,CPHAction* action,CPHReqComparerV* cmp_action); + void add_call (CPHCondition* condition,CPHAction* action) ; + + void remove_call (PHCALL_I i) ; + PHCALL_I find_call (CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action) ; + void remove_call (CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action) ; + void remove_calls (CPHReqComparerV* cmp_object) ; + + void update () ; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void add_call_unique_as (CPHCondition* condition,CPHReqComparerV* cmp_condition,CPHAction* action,CPHReqComparerV* cmp_action); + void add_call_as (CPHCondition* condition,CPHAction* action) ; + + void remove_call_as (PHCALL_I i) ; + PHCALL_I find_call_as (CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action) ; + void remove_call_as (CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action) ; + void remove_calls_as (CPHReqComparerV* cmp_object) ; + + void update_as () ; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void clear () ; +private: + +IC void add_call_unique (CPHCondition* condition,CPHReqComparerV* cmp_condition,CPHAction* action,CPHReqComparerV* cmp_action,PHCALL_STORAGE& cs); +IC void add_call (CPHCondition* condition,CPHAction* action,PHCALL_STORAGE& cs) ; + +IC void remove_call (PHCALL_I i,PHCALL_STORAGE& cs) ; +IC PHCALL_I find_call (CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action,PHCALL_STORAGE& cs) ; +IC void remove_call (CPHReqComparerV* cmp_condition,CPHReqComparerV* cmp_action,PHCALL_STORAGE& cs) ; +IC void remove_calls (CPHReqComparerV* cmp_object,PHCALL_STORAGE& cs) ; + +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHContactBodyEffector.cpp b/src/xrGameLA/PHContactBodyEffector.cpp new file mode 100644 index 000000000..62a9f05d5 --- /dev/null +++ b/src/xrGameLA/PHContactBodyEffector.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "PHContactBodyEffector.h" +#include "ExtendedGeom.h" +#include "tri-colliderknoopc/dTriList.h" +#include "PhysicsCommon.h" +#include "../gamemtllib.h" +#include "MathUtilsOde.h" +void CPHContactBodyEffector::Init(dBodyID body,const dContact& contact,SGameMtl* material) +{ + CPHBaseBodyEffector::Init(body); + m_contact=contact; + m_recip_flotation=1.f-material->fFlotationFactor; + m_material=material; +} +void CPHContactBodyEffector::Merge(const dContact& contact, SGameMtl* material) +{ + m_recip_flotation=_max(1.f-material->fFlotationFactor,m_recip_flotation); + //m_contact.geom.normal[0]+=contact.geom.normal[0]; + //m_contact.geom.normal[1]+=contact.geom.normal[1]; + //m_contact.geom.normal[2]+=contact.geom.normal[2]; +} + +void CPHContactBodyEffector::Apply() +{ + const dReal* linear_velocity =dBodyGetLinearVel(m_body); + dReal linear_velocity_smag =dDOT(linear_velocity,linear_velocity); + dReal linear_velocity_mag =_sqrt(linear_velocity_smag); + dReal effect =10000.f*m_recip_flotation*m_recip_flotation; + dMass mass; + dBodyGetMass(m_body,&mass); + dReal l_air=linear_velocity_mag*effect;//force/velocity !!! + if(l_air>mass.mass/fixed_step) l_air=mass.mass/fixed_step;//validate + + if(!fis_zero(l_air)) + { + dVector3 force={ + -linear_velocity[0]*l_air, + -linear_velocity[1]*l_air, + -linear_velocity[2]*l_air, + 0.f + }; + + if(!m_material->Flags.is(SGameMtl::flPassable)) + { + dVector3& norm=m_contact.geom.normal; + accurate_normalize(norm); + dMass m; + dBodyGetMass(m_body,&m); + dReal prg=1.f*dDOT(force,norm);//+dDOT(linear_velocity,norm)*m.mass/fixed_step + force[0]-=prg*norm[0]; + force[1]-=prg*norm[1]; + force[2]-=prg*norm[2]; + } + dBodyAddForce( + m_body, + force[0], + force[1], + force[2] + ); + } + dBodySetData(m_body,NULL); +} \ No newline at end of file diff --git a/src/xrGameLA/PHContactBodyEffector.h b/src/xrGameLA/PHContactBodyEffector.h new file mode 100644 index 000000000..a8fd6d6b2 --- /dev/null +++ b/src/xrGameLA/PHContactBodyEffector.h @@ -0,0 +1,15 @@ +#ifndef PH_CONTACT_BODY_EFFECTOR_H +#define PH_CONTACT_BODY_EFFECTOR_H +#include "PHBaseBodyEffector.h" +struct SGameMtl; +class CPHContactBodyEffector : public CPHBaseBodyEffector +{ +dContact m_contact; +float m_recip_flotation; +SGameMtl* m_material; +public: +void Init(dBodyID body,const dContact& contact,SGameMtl* material); +void Merge(const dContact& contact, SGameMtl* material); +void Apply(); +}; +#endif //PH_CONTACT_BODY_EFFECTOR_H \ No newline at end of file diff --git a/src/xrGameLA/PHDebug.cpp b/src/xrGameLA/PHDebug.cpp new file mode 100644 index 000000000..7c40288a2 --- /dev/null +++ b/src/xrGameLA/PHDebug.cpp @@ -0,0 +1,680 @@ +#include "stdafx.h" +#ifdef DEBUG +#include "physics.h" +#include "MathUtils.h" +#include "../StatGraph.h" +#include "PHDebug.h" +#include "PHObject.h" +#include "ExtendedGeom.h" +#include "Level.h" +#include "Hudmanager.h" +#include "ui_base.h" + +#include "debug_renderer.h" + +Flags32 ph_dbg_draw_mask ; +Flags32 ph_dbg_draw_mask1 ; +bool draw_frame=0; + +LPCSTR dbg_trace_object =NULL; +string64 s_dbg_tsrace_obj ; +u32 dbg_bodies_num =0; +u32 dbg_joints_num =0; +u32 dbg_islands_num =0; +u32 dbg_contacts_num =0; +u32 dbg_tries_num =0; +u32 dbg_saved_tries_for_active_objects =0; +u32 dbg_total_saved_tries =0; +u32 dbg_reused_queries_per_step =0; +u32 dbg_new_queries_per_step =0; +float dbg_vel_collid_damage_to_display =7.f; +#ifdef DRAW_CONTACTS +CONTACT_VECTOR Contacts0; +CONTACT_VECTOR Contacts1; +#endif + +PHOBJ_DBG_V dbg_draw_objects0; +PHOBJ_DBG_V dbg_draw_objects1; + +PHABS_DBG_V dbg_draw_abstruct0; +PHABS_DBG_V dbg_draw_abstruct1; + +PHABS_DBG_V dbg_draw_cashed; +PHABS_DBG_V dbg_draw_simple; + +enum EDBGPHDrawMode +{ + dmSecondaryThread, + dmCashed, + dmSimple +} dbg_ph_draw_mode=dmSecondaryThread; +u32 cash_draw_remove_time=u32(-1); + + +struct SPHDBGDrawTri :public SPHDBGDrawAbsract +{ + Fvector v[3]; + u32 c; + bool solid; + SPHDBGDrawTri(CDB::RESULT* T,u32 ac) + { + v[0].set(T->verts[0]); + v[1].set(T->verts[1]); + v[2].set(T->verts[2]); + c=ac; + solid = false; + } + SPHDBGDrawTri(CDB::TRI* T,const Fvector* V_array,u32 ac) + { + + v[0].set(V_array[T->verts[0]]); + v[1].set(V_array[T->verts[1]]); + v[2].set(V_array[T->verts[2]]); + c=ac; + solid = false; + } + SPHDBGDrawTri(const Fvector &v0, const Fvector &v1, const Fvector &v2, u32 ac, bool solid_) + { + v[0].set(v0);v[1].set(v1);v[2].set(v2); + c = ac; + solid = solid_; + } + virtual void render() + { + if(solid) + { + DRender->dbg_DrawTRI (Fidentity, v[0], v[1], v[2], c ); + DRender->dbg_DrawTRI (Fidentity, v[2], v[1], v[0], c ); + } else { + Level().debug_renderer().draw_line(Fidentity,v[0],v[1],c); + Level().debug_renderer().draw_line(Fidentity,v[1],v[2],c); + Level().debug_renderer().draw_line(Fidentity,v[2],v[0],c); + } + } +}; + +static void clear_vector(PHABS_DBG_V& v) +{ + PHABS_DBG_I i,e;i=v.begin();e=v.end(); + for(;e!=i;++i) + { + xr_delete(*i); + } + v.clear(); +} + +void DBG_DrawTri(CDB::RESULT* T,u32 c) +{ + DBG_DrawPHAbstruct(new SPHDBGDrawTri(T,c)); +} +void DBG_DrawTri(CDB::TRI* T,const Fvector* V_verts,u32 c) +{ + DBG_DrawPHAbstruct(new SPHDBGDrawTri(T,V_verts,c)); +} + + +struct SPHDBGDrawLine : public SPHDBGDrawAbsract +{ + Fvector p[2];u32 c; + SPHDBGDrawLine(const Fvector& p0,const Fvector& p1,u32 ca) + { + p[0].set(p0);p[1].set(p1);c=ca; + } + virtual void render() + { + Level().debug_renderer().draw_line(Fidentity,p[0],p[1],c); + } +}; + +void DBG_DrawLine ( const Fvector& p0, const Fvector& p1, u32 c ) +{ + DBG_DrawPHAbstruct( new SPHDBGDrawLine( p0, p1, c ) ); +} +void DBG_DrawMatrix( const Fmatrix &m, float size, u8 a/* = 255*/ ) +{ + Fvector to;to.add( m.c,Fvector( ).mul( m.i, size ) ); + DBG_DrawPHAbstruct( new SPHDBGDrawLine( m.c, to, D3DCOLOR_XRGB(a, 0, 0 ) ) ); + to.add(m.c,Fvector( ).mul( m.j, size ) ); + DBG_DrawPHAbstruct( new SPHDBGDrawLine( m.c, to, D3DCOLOR_XRGB(0, a, 0 ) ) ); + to.add(m.c,Fvector( ).mul( m.k, size ) ); + DBG_DrawPHAbstruct( new SPHDBGDrawLine( m.c, to, D3DCOLOR_XRGB(0, 0, a ) ) ); +} + +template +IC void rotate(Fmatrix &m, float ang); + +template<> +IC void rotate<0>(Fmatrix &m, float ang) +{ + m.rotateX( ang ); +} +template<> +IC void rotate<1>(Fmatrix &m, float ang) +{ + m.rotateY( ang ); +} + +template<> +IC void rotate<2>(Fmatrix &m, float ang) +{ + m.rotateZ( ang ); +} + +template +void DBG_DrawRotation( float ang0, float ang1, const Fmatrix& m, const Fvector &l, float size, u32 ac, bool solid, u32 tessel) +{ + Fvector from; from.set( m.c ); + Fvector ln; ln.set( l ); ln.mul( size ); + + + const float ftess = (float)tessel; + Fmatrix mm; rotate( mm, ang0 ); + mm.mulA_43( m ); + Fmatrix r; + rotate( r, ( ang1 - ang0 ) / ftess ); + for( u32 i = 0; tessel > i; ++i ) + { + Fvector tmp; + mm.transform_dir( tmp, ln ); + Fvector to0; to0.add( from, tmp ); + mm.mulB_43( r ); + mm.transform_dir( tmp, ln ); + Fvector to1; to1.add( from, tmp ); + DBG_DrawPHAbstruct( new SPHDBGDrawTri( from, to0, to1, ac, solid ) ); + } +} + +void DBG_DrawRotationX( const Fmatrix &m, float ang0, float ang1, float size, u32 ac, bool solid, u32 tessel ) +{ + DBG_DrawRotation<0>( ang0 , ang1, m, Fvector().set(0,0,1) ,size, ac, solid, tessel ); +} + +void DBG_DrawRotationY( const Fmatrix &m, float ang0, float ang1, float size, u32 ac, bool solid, u32 tessel ) +{ + DBG_DrawRotation<1>( ang0 , ang1, m, Fvector().set(1,0,0),size, ac, solid, tessel ); +} + +void DBG_DrawRotationZ( const Fmatrix &m, float ang0, float ang1, float size, u32 ac, bool solid, u32 tessel ) +{ + DBG_DrawRotation<2>( ang0 , ang1, m, Fvector().set(0,1,0), size, ac, solid, tessel ); +} + +struct SPHDBGDrawAABB :public SPHDBGDrawAbsract +{ + Fvector p[2];u32 c; + SPHDBGDrawAABB(const Fvector& center,const Fvector& AABB,u32 ac) + { + p[0].set(center);p[1].set(AABB); + c=ac; + } + virtual void render() + { + Level().debug_renderer().draw_aabb (p[0],p[1].x,p[1].y,p[1].z,c); + } +}; + +void DBG_DrawAABB(const Fvector& center,const Fvector& AABB,u32 c) +{ + DBG_DrawPHAbstruct(new SPHDBGDrawAABB(center,AABB,c)); +} + +struct SPHDBGDrawOBB: public SPHDBGDrawAbsract +{ + Fmatrix m;Fvector h;u32 c; + SPHDBGDrawOBB(const Fmatrix am,const Fvector ah, u32 ac) + { + m.set(am);h.set(ah);c=ac; + } + virtual void render() + { + Level().debug_renderer().draw_obb(m,h,c); + } +}; +void DBG_DrawOBB(const Fmatrix& m,const Fvector h,u32 c) +{ + DBG_DrawPHAbstruct(new SPHDBGDrawOBB(m,h,c)); +}; +struct SPHDBGDrawPoint :public SPHDBGDrawAbsract +{ + Fvector p;float size;u32 c; + SPHDBGDrawPoint(const Fvector ap,float s,u32 ac) + { + p.set(ap),size=s;c=ac; + } + virtual void render() + { + //Level().debug_renderer().draw_aabb(p,size,size,size,c); + Fmatrix m;m.identity();m.scale(size,size,size);m.c.set(p); + Level().debug_renderer().draw_ellipse(m,c); + } +}; +void DBG_DrawPoint(const Fvector& p,float size,u32 c) +{ + DBG_DrawPHAbstruct(new SPHDBGDrawPoint(p,size,c)); +} + +struct SPHDBGOutText : public SPHDBGDrawAbsract +{ +string64 s; +bool rendered; + SPHDBGOutText(LPCSTR t) + { + xr_strcpy(s,t); + rendered=false; + } + virtual void render() + { + //if(rendered) return; + UI().Font().pFontStat->OutNext(s); + rendered=true; + } +}; +void _cdecl DBG_OutText(LPCSTR s,...) +{ + string64 t; + va_list marker; + va_start (marker,s); + vsprintf(t,s,marker); + va_end (marker); + DBG_DrawPHAbstruct(new SPHDBGOutText(t)); +} + +void DBG_OpenCashedDraw() +{ + dbg_ph_draw_mode=dmCashed; +} +void DBG_ClosedCashedDraw(u32 remove_time) +{ + dbg_ph_draw_mode =dmSecondaryThread ; + cash_draw_remove_time =remove_time+Device.dwTimeGlobal; +} + +IC void push( PHABS_DBG_V &v, SPHDBGDrawAbsract* a ) +{ + if( v.size() < 500 ) + v.push_back(a); +} +void DBG_DrawPHAbstruct(SPHDBGDrawAbsract* a) +{ + if(dbg_ph_draw_mode!=dmCashed) + { + if(ph_world->Processing()) dbg_ph_draw_mode=dmSecondaryThread; + else dbg_ph_draw_mode=dmSimple; + } + switch (dbg_ph_draw_mode) + { + case dmSecondaryThread: + if(draw_frame) + { + push( dbg_draw_abstruct0, a ); + }else + { + push( dbg_draw_abstruct1, a ); + }; break; + case dmCashed: push( dbg_draw_cashed, a );break; + case dmSimple: push( dbg_draw_simple, a );break; + } + +} + +void DBG_PHAbstruactStartFrame(bool dr_frame) +{ + PHABS_DBG_I i,e; + if(dr_frame) + { + i=dbg_draw_abstruct0.begin(); + e=dbg_draw_abstruct0.end(); + }else + { + i=dbg_draw_abstruct1.begin(); + e=dbg_draw_abstruct1.end(); + } + for(;e!=i;++i) + { + xr_delete(*i); + } + if(dr_frame) + { + dbg_draw_abstruct0.clear(); + } + else + { + dbg_draw_abstruct1.clear(); + } +} +void capped_cylinder_ray_collision_test(); +void DBG_PHAbstructRender() +{ + PHABS_DBG_I i,e; + if(!draw_frame) + { + i=dbg_draw_abstruct0.begin(); + e=dbg_draw_abstruct0.end(); + }else + { + i=dbg_draw_abstruct1.begin(); + e=dbg_draw_abstruct1.end(); + } + + for(;e!=i;++i) + { + (*i)->render(); + } + if(dbg_ph_draw_mode!=dmCashed) + { + PHABS_DBG_I i,e; + i=dbg_draw_cashed.begin();e=dbg_draw_cashed.end(); + for(;e!=i;++i) + { + (*i)->render(); + } + if(cash_draw_remove_timerender(); + } + clear_vector(dbg_draw_simple); + } + //capped_cylinder_ray_collision_test(); +} + +void DBG_PHAbstructClear() +{ + DBG_PHAbstruactStartFrame(true); + DBG_PHAbstruactStartFrame(false); + clear_vector(dbg_draw_cashed); + clear_vector(dbg_draw_simple); +} + +void DBG_DrawPHObject(CPHObject* obj) +{ + if(ph_dbg_draw_mask.test(phDbgDrawEnabledAABBS)) + { + SPHObjDBGDraw obj_draw; + obj_draw.AABB.set(obj->AABB); + obj_draw.AABB_center.set(obj->spatial.sphere.P); + if(draw_frame) + { + dbg_draw_objects0.push_back(obj_draw); + }else + { + dbg_draw_objects1.push_back(obj_draw); + } + } +} +void DBG_DrawContact(dContact& c) +{ +#ifdef DRAW_CONTACTS + + SPHContactDBGDraw dbc; + if(dGeomGetBody(c.geom.g1)) + { + dbc.geomClass =dGeomGetClass(retrieveGeom(c.geom.g1)); + } + else + { + dbc.geomClass=dGeomGetClass(retrieveGeom(c.geom.g2)); + } + dbc.norm.set(cast_fv(c.geom.normal)); + dbc.pos.set(cast_fv(c.geom.pos)); + dbc.depth=c.geom.depth; + if(ph_dbg_draw_mask.test(phDbgDrawContacts)) + { + if(draw_frame)Contacts0.push_back(dbc); + else Contacts1.push_back(dbc); + } +#endif +} +void DBG_DrawFrameStart() +{ + + if(draw_frame) + { +#ifdef DRAW_CONTACTS + Contacts0.clear(); +#endif + dbg_draw_objects0.clear(); + dbg_draw_abstruct0.clear(); + } + else + { +#ifdef DRAW_CONTACTS + Contacts1.clear(); +#endif + dbg_draw_objects1.clear(); + dbg_draw_abstruct1.clear(); + } + DBG_PHAbstruactStartFrame(draw_frame); + + dbg_tries_num =0; + dbg_saved_tries_for_active_objects =0; +} + + +void PH_DBG_Clear() +{ + DBG_PHAbstructClear(); + dbg_draw_objects0.clear(); + dbg_draw_objects1.clear(); +#ifdef DRAW_CONTACTS + Contacts0.clear(); + Contacts1.clear(); +#endif +} + +void PH_DBG_Render() +{ + if(ph_dbg_draw_mask.test(phDbgDrawZDisable)) + DRender->ZEnable(false); + + UI().Font().pFontStat->OutSet (550,250); + + if(ph_dbg_draw_mask.test(phDbgDrawEnabledAABBS)) + { + PHOBJ_DBG_I i,e; + if(!draw_frame) + { + i=dbg_draw_objects0.begin(); + e=dbg_draw_objects0.end(); + }else + { + i=dbg_draw_objects1.begin(); + e=dbg_draw_objects1.end(); + } + for(;e!=i;++i) + { + SPHObjDBGDraw& ds=*i; + Level().debug_renderer().draw_aabb(ds.AABB_center,ds.AABB.x,ds.AABB.y,ds.AABB.z,D3DCOLOR_XRGB(255,0,0)); + } + } + + DBG_PHAbstructRender(); + +#ifdef DRAW_CONTACTS + + if(ph_dbg_draw_mask.test(phDbgDrawContacts)) + { + + CONTACT_I i,e; + if(!draw_frame) + { + i=Contacts0.begin(); + e=Contacts0.end(); + } + else + { + i=Contacts1.begin(); + e=Contacts1.end(); + } + + for(;i!=e;i++) + { + SPHContactDBGDraw &c=*i; + bool is_cyl=c.geomClass==dCylinderClassUser; + Level().debug_renderer().draw_aabb (c.pos,.01f,.01f,.01f,D3DCOLOR_XRGB(255*is_cyl,0,255*!is_cyl)); + Fvector dir; + dir.set(c.norm); + dir.mul(c.depth*100.f); + dir.add(c.pos); + Level().debug_renderer().draw_line(Fidentity,c.pos,dir,D3DCOLOR_XRGB(255*is_cyl,0,255*!is_cyl)); + } + } +// UI().Font().pFontStat->OutNext("---------------------"); +#endif + + if(ph_dbg_draw_mask.test(phDbgDrawZDisable)) + DRender->ZEnable(true); +} + +void DBG_DrawStatBeforeFrameStep() +{ + if(ph_dbg_draw_mask.test(phDbgDrawObjectStatistics)) + { + static float obj_count=0.f; + static float update_obj_count=0.f; + obj_count=obj_count*0.9f + float(ph_world->ObjectsNumber())*0.1f; + update_obj_count=update_obj_count*0.9f + float(ph_world->UpdateObjectsNumber())*0.1f; + DBG_OutText("Active Phys Objects %3.0f",obj_count); + DBG_OutText("Active Phys Update Objects %3.0f",update_obj_count); + } +} + +void DBG_DrawStatAfterFrameStep() +{ + if(ph_dbg_draw_mask.test(phDbgDrawObjectStatistics)) + { + DBG_OutText("------------------------------"); + static float fdbg_bodies_num=0.f; + static float fdbg_joints_num=0.f; + static float fdbg_islands_num=0.f; + static float fdbg_contacts_num=0.f; + static float fdbg_tries_num=0.f; + fdbg_islands_num=0.9f*fdbg_islands_num+0.1f*float(dbg_islands_num); + fdbg_bodies_num=0.9f*fdbg_bodies_num+0.1f*float(dbg_bodies_num); + fdbg_joints_num=0.9f*fdbg_joints_num+0.1f*float(dbg_joints_num); + fdbg_contacts_num=0.9f*fdbg_contacts_num+0.1f*float(dbg_contacts_num); + fdbg_tries_num=0.9f*fdbg_tries_num+0.1f*float(dbg_tries_num); + DBG_OutText("Ph Number of active islands %3.0f",fdbg_islands_num); + DBG_OutText("Ph Number of active bodies %3.0f",fdbg_bodies_num); + DBG_OutText("Ph Number of active joints %4.0f",fdbg_joints_num); + DBG_OutText("Ph Number of contacts %4.0f",fdbg_contacts_num); + DBG_OutText("Ph Number of tries %5.0f",fdbg_tries_num); + DBG_OutText("------------------------------"); + } + if(ph_dbg_draw_mask.test(phDbgDrawCashedTriesStat)) + { + DBG_OutText("------------------------------"); + static float fdbg_saved_tries_for_active_objects =0; + static float fdbg_total_saved_tries =0; + + fdbg_saved_tries_for_active_objects=0.9f*fdbg_saved_tries_for_active_objects+0.1f*float(dbg_saved_tries_for_active_objects); + fdbg_total_saved_tries =0.9f*fdbg_total_saved_tries+0.1f*float(dbg_total_saved_tries); + DBG_OutText("Ph Number of cashed tries in active objects %5.0f",fdbg_saved_tries_for_active_objects); + DBG_OutText("Ph Total number cashed %5.0f",fdbg_total_saved_tries); + + static SInertVal fdbg_reused_queries_per_step(0.9f); + static SInertVal fdbg_new_queries_per_step(0.9f); + fdbg_reused_queries_per_step.new_val(float(dbg_reused_queries_per_step)); + fdbg_new_queries_per_step.new_val(float(dbg_new_queries_per_step)); + DBG_OutText("Ph tri_queries_per_step %5.2f",fdbg_new_queries_per_step.val); + DBG_OutText("Ph reused_tri_queries_per_step %5.2f",fdbg_reused_queries_per_step.val); + DBG_OutText("------------------------------"); + + } + draw_frame=!draw_frame; + + +} + +CFunctionGraph::CFunctionGraph() +{ + m_stat_graph=NULL; + m_function.clear(); +} +CFunctionGraph::~CFunctionGraph() +{ + xr_delete(m_stat_graph); + m_function.clear(); +} +void CFunctionGraph::Init(type_function fun,float x0,float x1,int l, int t, int w, int h,int points_num/*=500*/,u32 color/*=*/,u32 bk_color) +{ + x_min=x0;x_max=x1; + m_stat_graph=new CStatGraph(); + m_function=fun; + R_ASSERT(!m_function.empty()&&m_stat_graph); + R_ASSERT(x1>x0); + s=(x_max-x_min)/points_num; + R_ASSERT(s>0.f); + m_stat_graph->SetRect(l,t,w,h,bk_color,bk_color); + float min=dInfinity;float max=-dInfinity; + for(float x=x_min;x-dInfinity && min<=max); + m_stat_graph->SetMinMax(min,max,points_num); + + for(float x=x_min;xAppendItem(val,color); + + } + //m_stat_graph->AddMarker(CStatGraph::stVert, 0, D3DCOLOR_XRGB(255, 0, 0)); + //m_stat_graph->AddMarker(CStatGraph::stHor, 0, D3DCOLOR_XRGB(255, 0, 0)); +} + +void CFunctionGraph::AddMarker(CStatGraph::EStyle Style, float pos, u32 Color) +{ + VERIFY(IsActive()); + ScaleMarkerPos(Style,pos); + m_stat_graph->AddMarker(Style,pos,Color); +} +void CFunctionGraph::UpdateMarker (u32 ID, float M) +{ + VERIFY(IsActive()); + ScaleMarkerPos(ID,M); + m_stat_graph->UpdateMarkerPos(ID, M); +} +void CFunctionGraph::ScaleMarkerPos(u32 ID,float &p) +{ + VERIFY(IsActive()); + ScaleMarkerPos(m_stat_graph->Marker(ID).m_eStyle,p); +} +void CFunctionGraph::ScaleMarkerPos(CStatGraph::EStyle Style, float &p) +{ + VERIFY(IsActive()); + if(Style==CStatGraph::stVert) p=ScaleX(p); +} +void CFunctionGraph::Clear() +{ + xr_delete(m_stat_graph); + m_function.clear(); +} + +bool CFunctionGraph::IsActive() +{ + VERIFY((m_stat_graph==0)==m_function.empty()); + return !!m_stat_graph; +} + +LPCSTR PH_DBG_ObjectTrack() +{ + return dbg_trace_object; +} +void PH_DBG_SetTrackObject(LPCSTR obj) +{ + xr_strcpy( s_dbg_tsrace_obj,obj); + dbg_trace_object=s_dbg_tsrace_obj; +} +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHDebug.h b/src/xrGameLA/PHDebug.h new file mode 100644 index 000000000..684db945f --- /dev/null +++ b/src/xrGameLA/PHDebug.h @@ -0,0 +1,155 @@ +#ifndef PH_DEBUG_H +#define PH_DEBUG_H +#ifdef DEBUG +struct dContact; +//#include "FastDelegate.h" +#include "../StatGraph.h" +#define DRAW_CONTACTS + + + +extern Flags32 ph_dbg_draw_mask ; +extern Flags32 ph_dbg_draw_mask1 ; +extern bool draw_frame ; +extern u32 dbg_tries_num ; +extern u32 dbg_saved_tries_for_active_objects ; +extern u32 dbg_total_saved_tries ; +extern u32 dbg_reused_queries_per_step ; +extern u32 dbg_new_queries_per_step ; +extern u32 dbg_bodies_num ; +extern u32 dbg_joints_num ; +extern u32 dbg_islands_num ; +extern u32 dbg_contacts_num ; +extern float dbg_vel_collid_damage_to_display ; +extern LPCSTR dbg_trace_object ; +#ifdef DRAW_CONTACTS + +struct SPHContactDBGDraw +{ + int geomClass; + Fvector norm; + Fvector pos; + float depth; +}; +DEFINE_VECTOR(SPHContactDBGDraw,CONTACT_VECTOR,CONTACT_I); +extern CONTACT_VECTOR Contacts0; +extern CONTACT_VECTOR Contacts1; +#endif +///ph_dbg_draw_mask +enum +{ + phDbgDrawContacts = 1<<0, + phDbgDrawEnabledAABBS = 1<<1, + phDBgDrawIntersectedTries = 1<<2, + phDbgDrawSavedTries = 1<<3, + phDbgDrawTriTrace = 1<<4, + phDBgDrawNegativeTries = 1<<5, + phDBgDrawPositiveTries = 1<<6, + phDbgDrawTriTestAABB = 1<<7, + phDBgDrawTriesChangesSign = 1<<8, + phDbgDrawTriPoint = 1<<9, + phDbgDrawExplosionPos = 1<<10, + phDbgDrawObjectStatistics = 1<<11, + phDbgDrawMassCenters = 1<<12, + phDbgDrawDeathActivationBox = 1<<14, + phHitApplicationPoints = 1<<15, + phDbgDrawCashedTriesStat = 1<<16, + phDbgDrawCarDynamics = 1<<17, + phDbgDrawCarPlots = 1<<18, + phDbgLadder = 1<<19, + phDbgDrawExplosions = 1<<20, + phDbgDrawCarAllTrnsm = 1<<21, + phDbgDrawZDisable = 1<<22, + phDbgAlwaysUseAiPhMove = 1<<23, + phDbgNeverUseAiPhMove = 1<<24, + phDbgDispObjCollisionDammage= 1<<25, + phDbgIK = 1<<26, + phDbgDrawIKGoal = 1<<27, + phDbgIKLimits = 1<<28, + phDbgCharacterControl = 1<<29, + phDbgDrawRayMotions = 1<<30, + phDbgTrackObject = 1<<31 + +}; +///ph_dbg_draw_mask1 ne pereputat by blin! +enum +{ + ph_m1_DbgTrackObject = 1<<0, + ph_m1_DbgActorRestriction = 1<<1, + phDbgIKOff = 1<<2, + phDbgHitAnims = 1<<3, + phDbgDrawIKLimits = 1<<4 +}; +struct SPHObjDBGDraw +{ + Fvector AABB; + Fvector AABB_center; +}; + +DEFINE_VECTOR( SPHObjDBGDraw, PHOBJ_DBG_V, PHOBJ_DBG_I ); +extern PHOBJ_DBG_V dbg_draw_objects0; +extern PHOBJ_DBG_V dbg_draw_objects1; +class CPHObject; + + +struct SPHDBGDrawAbsract +{ + virtual void render ( ) =0; + virtual ~SPHDBGDrawAbsract ( ) { }; +}; +DEFINE_VECTOR( SPHDBGDrawAbsract*, PHABS_DBG_V, PHABS_DBG_I ) ; +extern PHABS_DBG_V dbg_draw_abstruct0; +extern PHABS_DBG_V dbg_draw_abstruct1; +void DBG_DrawStatBeforeFrameStep( ); +void DBG_DrawStatAfterFrameStep( ); +void DBG_OpenCashedDraw( ); +void DBG_ClosedCashedDraw( u32 remove_time ); +void DBG_DrawPHAbstruct( SPHDBGDrawAbsract* a ); +void DBG_DrawPHObject( CPHObject *obj ); +void DBG_DrawContact ( dContact &c ); +void DBG_DrawTri( CDB::RESULT *T, u32 c ); +void DBG_DrawTri(CDB::TRI *T, const Fvector *V_verts, u32 c ); +void DBG_DrawLine( const Fvector &p0, const Fvector &p1, u32 c ); +void DBG_DrawAABB( const Fvector ¢er, const Fvector& AABB, u32 c ); +void DBG_DrawOBB( const Fmatrix &m, const Fvector h, u32 c ); +void DBG_DrawPoint( const Fvector& p, float size, u32 c ); +void DBG_DrawMatrix( const Fmatrix &m, float size, u8 a=255 ); +void DBG_DrawRotationX( const Fmatrix &m, float ang0, float ang1, float size, u32 ac, bool solid = false, u32 tessel = 7 ); +void DBG_DrawRotationY( const Fmatrix &m, float ang0, float ang1, float size, u32 ac, bool solid = false, u32 tessel = 7 ); +void DBG_DrawRotationZ( const Fmatrix &m, float ang0, float ang1, float size, u32 ac, bool solid = false, u32 tessel = 7 ); +void _cdecl DBG_OutText( LPCSTR s,... ); +void DBG_DrawFrameStart( ); +void PH_DBG_Render( ); +void PH_DBG_Clear( ); +LPCSTR PH_DBG_ObjectTrack( ); +void PH_DBG_SetTrackObject( LPCSTR obj ); + + + +struct CFunctionGraph +{ +public: + typedef fastdelegate::FastDelegate1 type_function; +private: + CStatGraph *m_stat_graph ; + type_function m_function ; + float x_min,x_max,s; + //float y_min,y_max; + //Fvector2 left_bottom; + //Fvector2 range; +public: + + CFunctionGraph ( ) ; + ~CFunctionGraph ( ) ; + void Init ( type_function fun, float x0, float x1, int l, int t, int w, int h, int points_num=500, u32 color=D3DCOLOR_XRGB( 0, 255, 0 ), u32 bk_color=D3DCOLOR_XRGB( 255, 255, 255 ) ) ; + void Clear ( ) ; + bool IsActive ( ) ; + void AddMarker ( CStatGraph::EStyle Style, float pos, u32 Color ) ; + void UpdateMarker ( u32 ID, float M1 ) ; +IC float ScaleX ( float x ) { VERIFY( IsActive( ) ); return( x-x_min )/s; } + void ScaleMarkerPos ( u32 ID, float &p ) ; + void ScaleMarkerPos ( CStatGraph::EStyle Style, float &p ) ; +IC float ResolutionX ( ){ VERIFY( IsActive( ) ); return s; } +}; +#endif +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHDefs.h b/src/xrGameLA/PHDefs.h new file mode 100644 index 000000000..977053436 --- /dev/null +++ b/src/xrGameLA/PHDefs.h @@ -0,0 +1,84 @@ +#ifndef PHDEFS_H +#define PHDEFS_H +class CPHElement; +class CPHJoint; +class CPhysicsShell; +//class CPHFracture; +//class CShellSplitInfo; + +#include "ode_include.h" + +IC void sub_diapasones(u16 &from1,u16 &to1,const u16 &from0,const u16 &to0) +{ + if(from0==to0 ||from1==to1|| to1<=from0||to1==u16(-1)) return; + R_ASSERT(from0>=from1&&to0<=to1); + u16 dip=to0-from0; + to1=to1-dip; +} + +class CShellSplitInfo +{ +friend class CPHFracturesHolder; +friend class CPHShellSplitterHolder; +friend class CPHElement; +IC bool HaveElements () {return m_end_el_num!=m_start_el_num;} +IC bool HaveJoints () {return m_start_jt_num!=m_end_jt_num;} +public: +IC void sub_diapasone(const CShellSplitInfo& sub) +{ + sub_diapasones(m_start_el_num,m_end_el_num,sub.m_start_el_num,sub.m_end_el_num); + sub_diapasones(m_start_jt_num,m_end_jt_num,sub.m_start_jt_num,sub.m_end_jt_num); +} +protected: + u16 m_start_el_num; + u16 m_end_el_num; + u16 m_start_jt_num; + u16 m_end_jt_num; + u16 m_start_geom_num; + u16 m_end_geom_num; + u16 m_bone_id; +}; + + +class CPHFracture : public CShellSplitInfo +{ +friend class CPHFracturesHolder; +friend class CPHElement; +friend class CPHShell; +bool m_breaked; +dMass m_firstM; +dMass m_secondM; +//when breaked m_pos_in_element-additional force m_break_force-additional torque -x additional torque-y add_torque_z - additional torque z +float m_break_force; +float m_break_torque; +Fvector m_pos_in_element; +float m_add_torque_z; + CPHFracture(); +public: +bool Update(CPHElement* element); +IC bool Breaked(){return m_breaked;} +void SetMassParts(const dMass& first,const dMass& second); +void MassSetZerro(); +void MassAddToFirst(const dMass& m); +void MassAddToSecond(const dMass& m); +void MassSubFromFirst(const dMass& m); +void MassSubFromSecond(const dMass& m); +void MassSetFirst(const dMass& m); +void MassSetSecond(const dMass& m); +const dMass& MassFirst(){return m_firstM;} +const dMass& MassSecond(){return m_secondM;} +void MassUnsplitFromFirstToSecond(const dMass& m); +}; + +typedef std::pair shell_root; +typedef std::pair element_fracture; +DEFINE_VECTOR(CPHElement*,ELEMENT_STORAGE,ELEMENT_I) +DEFINE_VECTOR(CPHJoint*,JOINT_STORAGE,JOINT_I) +DEFINE_VECTOR(shell_root,PHSHELL_PAIR_VECTOR,SHELL_PAIR_I) +typedef xr_vector::reverse_iterator SHELL_PAIR_RI; +DEFINE_VECTOR(element_fracture,ELEMENT_PAIR_VECTOR,ELEMENT_PAIR_I) +typedef xr_vector::reverse_iterator ELEMENT_RI; +typedef xr_vector::reverse_iterator ELEMENT_PAIR_RI; +DEFINE_VECTOR(CPHFracture,FRACTURE_STORAGE,FRACTURE_I) +typedef xr_vector::reverse_iterator FRACTURE_RI; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHDestroyable.cpp b/src/xrGameLA/PHDestroyable.cpp new file mode 100644 index 000000000..7a5cbcef3 --- /dev/null +++ b/src/xrGameLA/PHDestroyable.cpp @@ -0,0 +1,353 @@ +#include "stdafx.h" +#include "alife_space.h" +#include "hit.h" +#include "phdestroyable.h" +#include "physicsshellholder.h" +#include "xrMessages.h" +#include "object_factory.h" +#include "xrServer_Objects_ALife.h" +#include "Level.h" +#include "PhysicsShell.h" +#include "Actor.h" +#include "CharacterPhysicsSupport.h" +#include "ai_object_location.h" +#include "ai_space.h" +#include "game_graph.h" +#include "PHCollideValidator.h" +#include "PHShell.h" +#include "MathUtils.h" +#ifdef DEBUG +# include "PHWorld.h" +#endif +#include "../Include/xrRender/Kinematics.h" +/* +[impulse_transition_to_parts] +random_min =1 ; х массу объекта = величина случайно направленного импульса +; с случайн о выбранной точкой приложения в пределах нового обекта +random_hit_imp =0.1 ; х величена хит - импульса =............ + +;ref_bone ; кость из по которой определяется скорость для частей у который связь не задана по умолчанию рут +imp_transition_factor =0.1 ; фактор с которым прикладывается хит по исходному объекту ко всем частям +lv_transition_factor =1 ; коэффициент передачи линейной скорости +av_transition_factor =1 ; коэффициент передачи угловой скорости + + +[impulse_transition_from_source_bone] +source_bone =0 ; ref_bone +imp_transition_factor =1 ; коэффициент передачи импульса +lv_transition_factor =1 ; коэффициент передачи линейной скорости +av_transition_factor =1 ; коэффициент передачи угловой скорости + +*/ +CPHDestroyable::CPHDestroyable() +{ + m_flags.flags=0; + m_flags.set(fl_released,TRUE); + m_depended_objects=0; +} +/////////spawn object representing destroyed item////////////////////////////////////////////////////////////////////////////////// +void CPHDestroyable::GenSpawnReplace(u16 ref_id,LPCSTR section,shared_str visual_name) +{ + + CSE_Abstract *D = F_entity_Create(section);//*cNameSect() + VERIFY (D); + CSE_Visual *V =smart_cast(D); + V->set_visual (*visual_name); + CSE_PHSkeleton *l_tpPHSkeleton = smart_cast(D); + VERIFY (l_tpPHSkeleton); + l_tpPHSkeleton->source_id = ref_id; + //init + + // Send + D->s_name = section;//*cNameSect() + D->ID_Parent = u16(-1); + InitServerObject (D); + if (OnServer()) + { + NET_Packet P; + D->Spawn_Write (P,TRUE); + Level().Send (P,net_flags(TRUE)); + // Destroy + F_entity_Destroy (D); + m_depended_objects++; + }; +}; + +void CPHDestroyable::InitServerObject(CSE_Abstract* D) +{ + CPhysicsShellHolder *obj =PPhysicsShellHolder() ; + CSE_ALifeDynamicObjectVisual *l_tpALifeDynamicObject = smart_cast(D); + VERIFY (l_tpALifeDynamicObject); + + + l_tpALifeDynamicObject->m_tGraphID =obj->ai_location().game_vertex_id(); + l_tpALifeDynamicObject->m_tNodeID = obj->ai_location().level_vertex_id(); + + + // l_tpALifePhysicObject->startup_animation=m_startup_anim; + + D->set_name_replace (""); + D->s_gameid = u8(GameID()); + D->s_RP = 0xff; + D->ID = 0xffff; + + D->ID_Phantom = 0xffff; + D->o_Position = obj->Position(); + if (ai().get_alife()) + l_tpALifeDynamicObject->m_tGraphID = ai().game_graph().current_level_vertex(); + else + l_tpALifeDynamicObject->m_tGraphID = 0xffff; + obj->XFORM().getXYZ (D->o_Angle); + D->s_flags.assign (M_SPAWN_OBJECT_LOCAL); + D->RespawnTime = 0; +} + +void CPHDestroyable::PhysicallyRemoveSelf() +{ + CPhysicsShellHolder *obj =PPhysicsShellHolder() ; + + CActor *A =smart_cast(obj) ; + if(A) + { + A->character_physics_support()->SetRemoved(); + } + else + { + + //obj->PPhysicsShell()->PureStep(); + obj->PPhysicsShell()->Disable(); + obj->PPhysicsShell()->DisableCollision(); + + } + + obj->setVisible(FALSE); + obj->setEnabled(FALSE); +} + +void CPHDestroyable::PhysicallyRemovePart(CPHDestroyableNotificate *dn) +{ + CPhysicsShellHolder *sh = dn ->PPhysicsShellHolder () ; + CPhysicsShell *s = sh ->PPhysicsShell () ; + sh ->setVisible (FALSE) ; + sh ->setEnabled (FALSE) ; + s ->Disable () ; + s ->DisableCollision () ; +} + +void CPHDestroyable::Destroy(u16 source_id/*=u16(-1)*/,LPCSTR section/*="ph_skeleton_object"*/) +{ + + if(!CanDestroy())return ; + m_notificate_objects.clear(); + CPhysicsShellHolder *obj =PPhysicsShellHolder() ; + CPHSkeleton *phs= obj->PHSkeleton(); + if(phs)phs->SetNotNeedSave(); + if(obj->PPhysicsShell()) obj->PPhysicsShell()->Enable() ; + obj->processing_activate(); + if(source_id==obj->ID()) + { + m_flags.set(fl_released,FALSE); + } + xr_vector::iterator i=m_destroyed_obj_visual_names.begin(),e=m_destroyed_obj_visual_names.end(); + + if (IsGameTypeSingle()) + { + for(;e!=i;i++) + GenSpawnReplace(source_id,section,*i); + }; +/////////////////////////////////////////////////////////////////////////// + m_flags.set(fl_destroyed,TRUE); + return; +} + +void CPHDestroyable::Load(CInifile* ini,LPCSTR section) +{ + m_flags.set(fl_destroyable,FALSE); + if(ini->line_exist(section,"destroyed_vis_name")){ + m_flags.set(fl_destroyable,TRUE); + m_destroyed_obj_visual_names.push_back(ini->r_string(section,"destroyed_vis_name")); + }else{ + CInifile::Sect& data = ini->r_section(section); + if(data.Data.size()>0) m_flags.set(fl_destroyable,TRUE); + for (CInifile::SectCIt I=data.Data.begin(); I!=data.Data.end(); I++) + if(I->first.size()) m_destroyed_obj_visual_names.push_back(I->first); + } +} +void CPHDestroyable::Load(LPCSTR section) +{ + m_flags.set(fl_destroyable,FALSE); + + if(pSettings->line_exist(section,"destroyed_vis_name")){ + m_flags.set (fl_destroyable,TRUE); + m_destroyed_obj_visual_names.push_back (pSettings->r_string(section,"destroyed_vis_name")); + } +} + +void CPHDestroyable::Init() +{ + m_depended_objects=0; +} + +void CPHDestroyable::RespawnInit() +{ + m_flags.set(fl_destroyed,FALSE); + m_flags.set(fl_released,TRUE); + m_destroyed_obj_visual_names.clear(); + m_notificate_objects.clear(); + m_depended_objects=0; +} +void CPHDestroyable::SheduleUpdate(u32 dt) +{ + if(!m_flags.test(fl_destroyed)||!m_flags.test(fl_released)) return; + CPhysicsShellHolder *obj=PPhysicsShellHolder(); + + if( CanRemoveObject() ) + { + if (obj->Local()) obj->DestroyObject(); + } + +} + +void CPHDestroyable::NotificatePart(CPHDestroyableNotificate *dn) +{ + CPhysicsShell *own_shell=PPhysicsShellHolder()->PPhysicsShell() ; + CPhysicsShell *new_shell=dn->PPhysicsShellHolder()->PPhysicsShell() ; + IKinematics *own_K =smart_cast(PPhysicsShellHolder()->Visual()); + IKinematics *new_K =smart_cast(dn->PPhysicsShellHolder()->Visual()) ; + VERIFY (own_K&&new_K&&own_shell&&new_shell) ; + CInifile *own_ini =own_K->LL_UserData() ; + CInifile *new_ini =new_K->LL_UserData() ; + ////////////////////////////////////////////////////////////////////////////////// + Fmatrix own_transform; + own_shell ->GetGlobalTransformDynamic (&own_transform) ; + new_shell ->SetGlTransformDynamic (own_transform) ; + //////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////// + float random_min =1.f ; + float random_hit_imp =1.f ; + //////////////////////////////////////////////////////////////////////////////////// + u16 ref_bone =own_K->LL_GetBoneRoot(); + + float imp_transition_factor =1.f ; + float lv_transition_factor =1.f ; + float av_transition_factor =1.f ; + //////////////////////////////////////////////////////////////////////////////////// + if(own_ini&&own_ini->section_exist("impulse_transition_to_parts")) + { + random_min =own_ini->r_float("impulse_transition_to_parts","random_min"); + random_hit_imp =own_ini->r_float("impulse_transition_to_parts","random_hit_imp"); + //////////////////////////////////////////////////////// + if(own_ini->line_exist("impulse_transition_to_parts","ref_bone")) + ref_bone =own_K->LL_BoneID(own_ini->r_string("impulse_transition_to_parts","ref_bone")); + imp_transition_factor =own_ini->r_float("impulse_transition_to_parts","imp_transition_factor"); + lv_transition_factor =own_ini->r_float("impulse_transition_to_parts","lv_transition_factor"); + av_transition_factor =own_ini->r_float("impulse_transition_to_parts","av_transition_factor"); + + if(own_ini->section_exist("collide_parts")) + { + if(own_ini->line_exist("collide_parts","small_object")) + { + new_shell->SetSmall(); + } + if(own_ini->line_exist("collide_parts","ignore_small_objects")) + { + new_shell->SetIgnoreSmall(); + } + } + } + + if(new_ini&&new_ini->section_exist("impulse_transition_from_source_bone")) + { + //random_min =new_ini->r_float("impulse_transition_from_source_bone","random_min"); + //random_hit_imp =new_ini->r_float("impulse_transition_from_source_bone","random_hit_imp"); + //////////////////////////////////////////////////////// + if(new_ini->line_exist("impulse_transition_from_source_bone","ref_bone")) + ref_bone =own_K->LL_BoneID(new_ini->r_string("impulse_transition_from_source_bone","ref_bone")); + imp_transition_factor =new_ini->r_float("impulse_transition_from_source_bone","imp_transition_factor"); + lv_transition_factor =new_ini->r_float("impulse_transition_from_source_bone","lv_transition_factor"); + av_transition_factor =new_ini->r_float("impulse_transition_from_source_bone","av_transition_factor"); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + + dBodyID own_body=own_shell->get_Element(ref_bone)->get_body() ; + + u16 new_el_number = new_shell->get_ElementsNumber() ; + + for(u16 i=0;iget_ElementByStoreOrder(i); + float random_hit=random_min*e->getMass(); + if(m_fatal_hit.is_valide() && m_fatal_hit.bone()!=BI_NONE ) + { + Fvector pos; + Fmatrix m;m.set(own_K->LL_GetTransform(m_fatal_hit.bone())); + m.mulA_43 (PPhysicsShellHolder()->XFORM()); + m.transform_tiny(pos,m_fatal_hit.bone_space_position()); + e->applyImpulseVsGF(pos,m_fatal_hit.direction(),m_fatal_hit.phys_impulse()*imp_transition_factor); + random_hit+=random_hit_imp*m_fatal_hit.phys_impulse(); + } + Fvector rnd_dir;rnd_dir.random_dir(); + e->applyImpulse(rnd_dir,random_hit); + Fvector mc; mc.set(e->mass_Center()); + dVector3 res_lvell; + dBodyGetPointVel(own_body,mc.x,mc.y,mc.z,res_lvell); + cast_fv(res_lvell).mul(lv_transition_factor); + e->set_LinearVel(cast_fv(res_lvell)); + + Fvector res_avell;res_avell.set(cast_fv(dBodyGetAngularVel(own_body))); + res_avell.mul(av_transition_factor); + e->set_AngularVel(res_avell); + } + + + + + + new_shell->Enable(); + new_shell->EnableCollision(); + dn->PPhysicsShellHolder()->setVisible(TRUE); + dn->PPhysicsShellHolder()->setEnabled(TRUE); + + if(own_shell->IsGroupObject()) + new_shell->RegisterToCLGroup(own_shell->GetCLGroup());//CollideBits + CPHSkeleton* ps=dn->PPhysicsShellHolder()->PHSkeleton(); + if(ps) + { + + if(own_ini&&own_ini->section_exist("autoremove_parts")) + { + ps->SetAutoRemove(1000*(READ_IF_EXISTS(own_ini,r_u32,"autoremove_parts","time",ps->DefaultExitenceTime()))); + } + + if(new_ini&&new_ini->section_exist("autoremove")) + { + ps->SetAutoRemove(1000*(READ_IF_EXISTS(new_ini,r_u32,"autoremove","time",ps->DefaultExitenceTime()))); + } + } + +} + +void CPHDestroyable::NotificateDestroy(CPHDestroyableNotificate *dn) +{ + VERIFY(m_depended_objects); + VERIFY(!ph_world->Processing()); + m_depended_objects--; + PhysicallyRemovePart(dn); + m_notificate_objects.push_back(dn); + if(!m_depended_objects) + { + xr_vector::iterator i=m_notificate_objects.begin(),e=m_notificate_objects.end(); + for(;i m_destroyed_obj_visual_names ; +private: + xr_vector m_notificate_objects ; + u16 m_depended_objects ; + Flags8 m_flags ; + SHit m_fatal_hit ; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + float m_random_min ; + float m_random_hit_imp ; + u16 ref_bone ; + + float m_imp_transition_factor ; + float m_lv_transition_factor ; + float m_av_transition_factor ; +*/ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* + source_bone =-1 ;-1- ref_bone + imp_transition_factor =1 ; коэффициент передачи импульса + lv_transition_factor =1 ; коэффициент передачи линейной скорости + av_transition_factor =1 ; коэффициент передачи угловой скорости +*/ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + enum + { + fl_destroyable = 1<<0, + fl_destroyed = 1<<1, + fl_released = 1<<2, + }; + virtual CPhysicsShellHolder* PPhysicsShellHolder () =0; +public: + CPHDestroyable () ; + void Init () ; + void RespawnInit () ; + void SetFatalHit (const SHit& hit) ; + void Destroy (u16 ref_id=u16(-1),LPCSTR section="ph_skeleton_object") ; + SHit &FatalHit (){return m_fatal_hit;} + void Load (LPCSTR section) ; + void Load (CInifile* ini,LPCSTR section) ; +virtual void NotificateDestroy (CPHDestroyableNotificate *dn) ; + void PhysicallyRemoveSelf () ; +IC bool Destroyable () {return !!m_flags.test(fl_destroyable);} +IC bool Destroyed () {return !!m_flags.test(fl_destroyed);} +IC bool CanDestroy () {return m_flags.test(fl_destroyable)&&!m_flags.test(fl_destroyed) ;} +virtual bool CanRemoveObject () {return true;} +virtual void SheduleUpdate (u32 dt) ; +virtual void GenSpawnReplace (u16 source_id,LPCSTR section,shared_str visual_name) ; +virtual void InitServerObject (CSE_Abstract* D) ; +private: + void NotificatePart (CPHDestroyableNotificate *dn) ; + void PhysicallyRemovePart (CPHDestroyableNotificate *dn) ; ; +}; + + +#endif diff --git a/src/xrGameLA/PHDestroyableNotificate.cpp b/src/xrGameLA/PHDestroyableNotificate.cpp new file mode 100644 index 000000000..4dca81a7a --- /dev/null +++ b/src/xrGameLA/PHDestroyableNotificate.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "PHDestroyableNotificate.h" +#include "alife_space.h" +#include "hit.h" +#include "PHDestroyable.h" +#include "Level.h" +#include "xrServer_Object_Base.h" +#include "../xr_object.h" +#include "PhysicsShellHolder.h" +#include "xrServer_Objects.h" +void CPHDestroyableNotificate::spawn_notificate(CSE_Abstract* so) +{ + CPHDestroyableNotificator* D =NULL; + CSE_PHSkeleton * po =smart_cast(so); + u16 id =u16(-1); + if(po) + id =po->get_source_id(); + if(id!=u16(-1)) + D= smart_cast(Level().Objects.net_Find(id)); + if(D) + D->NotificateDestroy(this); + po->source_id=BI_NONE; +} diff --git a/src/xrGameLA/PHDestroyableNotificate.h b/src/xrGameLA/PHDestroyableNotificate.h new file mode 100644 index 000000000..9069c4a1b --- /dev/null +++ b/src/xrGameLA/PHDestroyableNotificate.h @@ -0,0 +1,15 @@ +#pragma once + +class CSE_Abstract; +class CPhysicsShellHolder; + +class CPHDestroyableNotificate +{ +public: + virtual CPHDestroyableNotificate * cast_phdestroyable_notificate () {return this;} + virtual CPhysicsShellHolder* PPhysicsShellHolder () =0; + virtual void spawn_init () {} + void spawn_notificate (CSE_Abstract*) ; +protected: +private: +}; \ No newline at end of file diff --git a/src/xrGameLA/PHDisabling.cpp b/src/xrGameLA/PHDisabling.cpp new file mode 100644 index 000000000..c14a9992b --- /dev/null +++ b/src/xrGameLA/PHDisabling.cpp @@ -0,0 +1,254 @@ +#include "stdafx.h" +#include "PHDisabling.h" +#include "PhysicsCommon.h" +#include "Physics.h" +extern CPHWorld* ph_world; +SDisableVector::SDisableVector() +{ + Init(); +} + + + +void SDisableVector::Reset() +{ + sum.set(0.f,0.f,0.f); +} + +void SDisableVector::Init() +{ + previous.set(0.f,0.f,0.f); + Reset(); +} + +float SDisableVector::Update(const Fvector& new_vector) +{ + Fvector dif; + dif.sub(new_vector,previous); + previous.set(new_vector); + sum.add(dif); + return dif.magnitude(); +} + +float SDisableVector::UpdatePrevious(const Fvector& new_vector) +{ + Fvector dif; + dif.sub(new_vector,previous); + previous.set(new_vector); + return dif.magnitude(); +} + +float SDisableVector::SumMagnitude() +{ + return sum.magnitude(); +} + +SDisableUpdateState::SDisableUpdateState() +{ + Reset(); +} + + + +void SDisableUpdateState::Reset() +{ + disable = false ; + enable = false ; +} + +SDisableUpdateState& SDisableUpdateState::operator &= (SDisableUpdateState& lstate) +{ + disable = disable && lstate.disable; + enable = enable || lstate.enable; + return *this; +} + +CBaseDisableData::CBaseDisableData() +{ + + m_frames =worldDisablingParams.objects_params.L2frames ; + Reinit(); +} + +void CBaseDisableData::Reinit() +{ + m_count =m_frames ; + if(ph_world) + m_count=m_count+ph_world->disable_count ; + m_stateL1 .Reset() ; + m_stateL2 .Reset() ; +} +void CBaseDisableData::Disabling() +{ + + dBodyID body = get_body(); + m_count--; + + UpdateL1(); + + CheckState(m_stateL1); + + if(m_count==0)//ph_world->disable_count==dis_frames//m_count==m_frames + { + UpdateL2(); + CheckState(m_stateL2); + m_count=m_frames; + } + const dReal* force=dBodyGetForce(body); + const dReal* torqu=dBodyGetTorque(body); + if(dDOT(force,force)>0.f || dDOT(torqu,torqu)>0.f) m_disabled=false; + if(dBodyIsEnabled(body)) + { + ReEnable(); + if(!m_disabled&& (ph_world->disable_count!=m_count%worldDisablingParams.objects_params.L2frames)) + { + m_count=m_frames+ph_world->disable_count; + } + } + if(m_disabled) + Disable();//dBodyDisable(body); + +} + +void CPHDisablingBase::Reinit() +{ + m_mean_velocity.Init() ; + m_mean_acceleration.Init() ; + CBaseDisableData::Reinit() ; + bool disable=!dBodyIsEnabled(get_body()); + m_stateL1.disable=disable; + m_stateL1.enable=!disable; + m_stateL2.disable=disable; + m_stateL2.enable=!disable; +} +void CPHDisablingBase::UpdateValues(const Fvector &new_pos,const Fvector &new_vel) +{ + + if(m_count m_params.velocity*worldDisablingParams.reanable_factor || + accel > m_params.acceleration*worldDisablingParams.reanable_factor) + state.enable=true ; + } +protected: + + SDisableVector m_mean_velocity ; + SDisableVector m_mean_acceleration ; + SOneDDOParams m_params ; +}; + +class CPHDisablingRotational : + public CPHDisablingBase +{ +public: + CPHDisablingRotational () ; + void Reinit () ; + virtual void UpdateL1 () ; + virtual void set_DisableParams (const SAllDDOParams& params) ; +}; + +class CPHDisablingTranslational : + public CPHDisablingBase +{ +public: + CPHDisablingTranslational () ; + void Reinit () ; + virtual void UpdateL1 () ; + virtual void set_DisableParams (const SAllDDOParams& params) ; +}; + +class CPHDisablingFull : + public CPHDisablingTranslational, + public CPHDisablingRotational +{ +public: + void Reinit () ; + virtual void UpdateL1 () ; + virtual void UpdateL2 () ; + virtual void set_DisableParams (const SAllDDOParams& params) ; +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHDynamicData.cpp b/src/xrGameLA/PHDynamicData.cpp new file mode 100644 index 000000000..4e58f214d --- /dev/null +++ b/src/xrGameLA/PHDynamicData.cpp @@ -0,0 +1,203 @@ +// PHDynamicData.cpp: implementation of the PHDynamicData class. +// +////////////////////////////////////////////////////////////////////// +#include "stdafx.h" +#include "PHDynamicData.h" + +#if 0 +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +PHDynamicData::PHDynamicData() +{ + numOfChilds=0; + //Childs=NULL; + p_parent_body_interpolation=NULL; +} + +PHDynamicData::~PHDynamicData() +{ + if(numOfChilds){ + //for(unsigned int i=0;i0) + //Childs[childNum].Childs=new PHDynamicData[numOfchilds]; + Childs[childNum].Childs.resize(numOfchilds); + else + //Childs[childNum].Childs=NULL; + Childs[childNum].numOfChilds=0; + + Childs[childNum].geom=NULL; + Childs[childNum].transform=NULL; + return true; + } + else return false; +} + +void PHDynamicData::CalculateR_N_PosOfChilds(dBodyID parent) +{ + Fmatrix parent_transform;//,mYM; + //mYM.rotateY (deg2rad(-90.f)); + DMXPStoFMX(dBodyGetRotation(parent),dBodyGetPosition(parent),parent_transform); + DMXPStoFMX(dBodyGetRotation(body),dBodyGetPosition(body),BoneTransform); + parent_transform.mulB_43 (ZeroTransform); + //parent_transform.mulA(mYM); + parent_transform.invert (); + + //BoneTransform.mulA(mYM); + BoneTransform.mulA_43 (parent_transform); + + for(unsigned int i=0;iInterpolateRotation(parent_transform); + p_parent_body_interpolation->InterpolatePosition(parent_transform.c); + + body_interpolation.InterpolateRotation(transform); + + body_interpolation.InterpolatePosition(transform.c); + parent_transform.mulB_43 (ZeroTransform); + + parent_transform.invert(); + + + //BoneTransform.mulA(parent_transform); + transform.mulA_43 (parent_transform); +} + +PHDynamicData * PHDynamicData::GetChild(unsigned int ChildNum) +{ + if(ChildNum 1000 +#pragma once +#endif // _MSC_VER > 1000 +class PHDynamicData; + +class PHDynamicData +{ +#if 0 +public: +dVector3 pos; +dMatrix3 R; +Fmatrix BoneTransform; +private: +dBodyID body; +CPHInterpolation* p_parent_body_interpolation; +CPHInterpolation body_interpolation; +dGeomID geom; +dGeomID transform; +//PHDynamicData* Childs; +//xr_vector Childs; +unsigned int numOfChilds; +Fmatrix ZeroTransform; +public: + inline void UpdateInterpolation(){ + body_interpolation.UpdatePositions(); + body_interpolation.UpdateRotations(); + } + void UpdateInterpolationRecursive() ; + void InterpolateTransform(Fmatrix& transform); + void InterpolateTransformVsParent(Fmatrix& transform); + PHDynamicData& operator [] (unsigned int i) {return Childs[i];}; + void Destroy(); + void Create(unsigned int numOfchilds,dBodyID Body); + void CalculateData(void); + PHDynamicData * GetChild(unsigned int ChildNum); + bool SetChild(unsigned int ChildNum,unsigned int numOfchilds,dBodyID body); + void SetAsZero(); + void SetAsZeroRecursive(); + void SetZeroTransform(Fmatrix& aTransform); + PHDynamicData(unsigned int numOfchilds,dBodyID body); + PHDynamicData(); + virtual ~PHDynamicData(); + void GetWorldMX(Fmatrix& aTransform){ + dMatrix3 R; + dQtoR(dBodyGetQuaternion(body),R); + DMXPStoFMX(R,dBodyGetPosition(body),aTransform); + } + void GetTGeomWorldMX(Fmatrix& aTransform){ + if(!transform) return; + Fmatrix NormTransform,Transform; + dVector3 P0={0,0,0,-1}; + Fvector Translate,Translate1; + //compute_final_tx(geom); + //dQtoR(dBodyGetQuaternion(body),R); + DMXPStoFMX(dBodyGetRotation(body),P0,NormTransform); + DMXPStoFMX(dGeomGetRotation(dGeomTransformGetGeom(transform)),P0,Transform); + + + dVectorSet((dReal*)&Translate,dGeomGetPosition(dGeomTransformGetGeom(transform))); + dVectorSet((dReal*)&Translate1,dBodyGetPosition(body)); + + aTransform.identity (); + aTransform.translate_over (Translate); + aTransform.mulA_43 (NormTransform); + aTransform.translate_over (Translate1); + aTransform.mulA_43 (Transform); + + // Translate.add(Translate1); + //transform.translate_over(Translate1); + + //transform.translate_add + //normalTransform=oMatrix4x4(dGeomGetRotation(dGeomTransformGetGeom(geom)))*normalTransform; + //oMatrix4x4 meshTransform(normalTransform); + + //meshTransform.PreTranslate(oVector3(dGeomGetPosition(dGeomTransformGetGeom(geom)))); + //meshTransform.PostTranslate(oVector3(dBodyGetPosition(body))); + } +#endif // #if 0 +public: + static inline void DMXPStoFMX(const dReal* R,const dReal* pos,Fmatrix& aTransform){ + + CopyMemory(&aTransform,R,sizeof(dMatrix3)); + aTransform.transpose(); + CopyMemory(&aTransform.c,pos,sizeof(Fvector)); + aTransform._14=0.f; + aTransform._24=0.f; + aTransform._34=0.f; + aTransform._44=1.f; + }; + static inline void DMXtoFMX(const dReal* R,Fmatrix& aTransform){ + aTransform._11=R[0]; + aTransform._12=R[4]; + aTransform._13=R[8]; + aTransform._14=0.f; + + aTransform._21=R[1]; + aTransform._22=R[5]; + aTransform._23=R[9]; + aTransform._24=0.f; + + aTransform._31=R[2]; + aTransform._32=R[6]; + aTransform._33=R[10]; + aTransform._34=0.f; + aTransform._44=1.f; + }; + static inline void FMX33toDMX(const Fmatrix33& aTransform,dReal* R){ + R[0]=aTransform._11; + R[4]=aTransform._12; + R[8]=aTransform._13; + + R[1]=aTransform._21; + R[5]=aTransform._22; + R[9]=aTransform._23; + + R[2]=aTransform._31; + R[6]=aTransform._32; + R[10]=aTransform._33; + }; + static inline void FMXtoDMX(const Fmatrix& aTransform,dReal* R){ + R[0]=aTransform._11; + R[4]=aTransform._12; + R[8]=aTransform._13; + + R[1]=aTransform._21; + R[5]=aTransform._22; + R[9]=aTransform._23; + + R[2]=aTransform._31; + R[6]=aTransform._32; + R[10]=aTransform._33; + }; +#if 0 +private: + void CalculateR_N_PosOfChilds(dBodyID parent); +public: + bool SetGeom(dGeomID ageom); + bool SetTransform(dGeomID ageom); +#endif // #if 0 +}; + +#endif // !defined(AFX_PHDynamicData_H__ACC01646_B581_4639_B78C_30311432021B__INCLUDED_) diff --git a/src/xrGameLA/PHElement.cpp b/src/xrGameLA/PHElement.cpp new file mode 100644 index 000000000..0911f52e7 --- /dev/null +++ b/src/xrGameLA/PHElement.cpp @@ -0,0 +1,1561 @@ +#include "StdAfx.h" +#include "PHDynamicData.h" +#include "Physics.h" +#include "tri-colliderknoopc/dTriList.h" +#include "PHFracture.h" +#include "PHContactBodyEffector.h" +#include "MathUtilsOde.h" +#include "MathUtils.h" +#include "PhysicsShellHolder.h" +#include "game_object_space.h" +//#include "../skeletoncustom.h" +#include "../Include/xrRender/Kinematics.h" +#include "../Include/xrRender/KinematicsAnimated.h" +#include <../../xrODE/ode/src/util.h> +#ifdef DEBUG +#include "PHDebug.h" +#endif + +/////////////////////////////////////////////////////////////// +#pragma warning(disable:4995) +#pragma warning(disable:4267) + +#include "../../xrODE/ode/src/collision_kernel.h" + + +#pragma warning(default:4267) +#pragma warning(default:4995) +/////////////////////////////////////////////////////////////////// + +#include "ExtendedGeom.h" + +#include "PHShell.h" +#include "PHElement.h" +#include "PHElementInline.h" +extern CPHWorld* ph_world; + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +//////Implementation for CPhysicsElement +CPHElement::CPHElement() //aux +{ + m_w_limit = default_w_limit; + m_l_limit = default_l_limit; + m_l_scale=default_l_scale; + m_w_scale=default_w_scale; + + //push_untill=0; + + //temp_for_push_out=NULL; + + m_body=NULL; + //bActive=false; + //bActivating=false; + m_flags.set(flActive,FALSE); + m_flags.set(flActivating,FALSE); + m_parent_element=NULL; + m_shell=NULL; + + + k_w=default_k_w; + k_l=default_k_l;//1.8f; + m_fratures_holder=NULL; + //b_enabled_onstep=false; + //m_flags.set(flEnabledOnStep,FALSE); + m_flags.assign(0); + mXFORM.identity(); + m_mass.setZero(); + m_mass_center.set(0,0,0); + m_volume=0.f; +} + +void CPHElement::add_Box (const Fobb& V) +{ + CPHGeometryOwner::add_Box(V); +} + + +void CPHElement::add_Sphere (const Fsphere& V) +{ + CPHGeometryOwner::add_Sphere(V); +} + +void CPHElement::add_Cylinder (const Fcylinder& V) +{ + CPHGeometryOwner::add_Cylinder(V); +} + +void CPHElement:: build (){ + + m_body=dBodyCreate(0);//phWorld + //m_saved_contacts=dJointGroupCreate (0); + //b_contacts_saved=false; + dBodyDisable(m_body); + //dBodySetFiniteRotationMode(m_body,1); + //dBodySetFiniteRotationAxis(m_body,0,0,0); + VERIFY2(dMass_valide(&m_mass),"Element has bad mass"); + if(m_geoms.empty()) + { + Fix(); + } + else + { + VERIFY2(m_mass.mass>0.f,"Element has bad mass"); + dBodySetMass(m_body,&m_mass); + } + + VERIFY_BOUNDARIES2(m_mass_center,phBoundaries,PhysicsRefObject(),"m_mass_center") + + dBodySetPosition(m_body,m_mass_center.x,m_mass_center.y,m_mass_center.z); + + CPHDisablingTranslational::Reinit(); + /////////////////////////////////////////////////////////////////////////////////////// + CPHGeometryOwner::build(); + set_body(m_body); +} + +void CPHElement::RunSimulation() +{ + //if(push_untill) + //push_untill+=Device.dwTimeGlobal; + + if(m_group) + dSpaceAdd(m_shell->dSpace(),(dGeomID)m_group); + else + if(!m_geoms.empty())(*m_geoms.begin())->add_to_space(m_shell->dSpace()); + if(!m_body->world) + { + //dWorldAddBody(phWorld, m_body); + m_shell->Island().AddBody(m_body); + } + dBodyEnable(m_body); +} + +void CPHElement::destroy () +{ + //dJointGroupDestroy(m_saved_contacts); + CPHGeometryOwner::destroy(); + if(m_body)//&&m_body->world + { + if(m_body->world)m_shell->Island().RemoveBody(m_body); + dBodyDestroy(m_body); + m_body=NULL; + } + + if(m_group){ + dGeomDestroy(m_group); + m_group=NULL; + } +} + +void CPHElement::calculate_it_data(const Fvector& mc,float mas) +{ + float density=mas/m_volume; + calculate_it_data_use_density(mc,density); +} + +static float static_dencity; +void CPHElement::calc_it_fract_data_use_density(const Fvector& mc,float density) +{ + m_mass_center.set(mc); + dMassSetZero(&m_mass); + static_dencity=density; + recursive_mass_summ(0,m_fratures_holder->m_fractures.begin()); +} + +dMass CPHElement::recursive_mass_summ(u16 start_geom,FRACTURE_I cur_fracture) +{ + dMass end_mass; + dMassSetZero(&end_mass); + GEOM_I i_geom=m_geoms.begin()+start_geom, e=m_geoms.begin()+cur_fracture->m_start_geom_num; + for(;i_geom!=e;++i_geom)(*i_geom)->add_self_mass(end_mass,m_mass_center,static_dencity); + dMassAdd(&m_mass,&end_mass); + start_geom=cur_fracture->m_start_geom_num; + ++cur_fracture; + if(m_fratures_holder->m_fractures.end() != cur_fracture) + cur_fracture->SetMassParts(m_mass,recursive_mass_summ(start_geom,cur_fracture)); + return end_mass; +} +void CPHElement:: setDensity (float M) +{ + calculate_it_data_use_density(get_mc_data(),M); +} + +void CPHElement:: setMass (float M) +{ + + calculate_it_data(get_mc_data(),M); +} + +void CPHElement:: setDensityMC (float M,const Fvector& mass_center) +{ + m_mass_center.set(mass_center); + calc_volume_data(); + calculate_it_data_use_density(mass_center,M); +} + +void CPHElement:: setMassMC (float M,const Fvector& mass_center) +{ + m_mass_center.set(mass_center); + calc_volume_data(); + calculate_it_data(mass_center,M); +} + + + +void CPHElement::Start() +{ + build(); + RunSimulation(); +} + +void CPHElement::Deactivate() +{ + //VERIFY(isActive()); + if (!isActive()) return; + + destroy(); + m_flags.set(flActive,FALSE); + m_flags.set(flActivating,FALSE); + //bActive=false; + //bActivating=false; + IKinematics* K=m_shell->PKinematics(); + if(K) + { + K->LL_GetBoneInstance(m_SelfID).reset_callback(); + } +} + +void CPHElement::SetTransform(const Fmatrix &m0){ + VERIFY2(_valid(m0),"invalid_form_in_set_transform"); + Fvector mc; + CPHGeometryOwner::get_mc_vs_transform(mc,m0); + VERIFY_BOUNDARIES2(mc,phBoundaries,PhysicsRefObject(),"mass center in set transform"); + dBodySetPosition(m_body,mc.x,mc.y,mc.z); + Fmatrix33 m33; + m33.set(m0); + dMatrix3 R; + PHDynamicData::FMX33toDMX(m33,R); + dBodySetRotation(m_body,R); + CPHDisablingFull::Reinit(); + + VERIFY2(dBodyGetPosition(m_body),"not valide safe position"); + VERIFY2(dBodyGetLinearVel(m_body),"not valide safe velocity"); + m_flags.set(flUpdate,TRUE); + m_shell->spatial_move(); +} + +void CPHElement::getQuaternion(Fquaternion& quaternion) +{ + if(!isActive()) return; + const float* q=dBodyGetQuaternion(m_body); + quaternion.set(-q[0],q[1],q[2],q[3]); + VERIFY(_valid(quaternion)); +} +void CPHElement::setQuaternion(const Fquaternion& quaternion) +{ + VERIFY(_valid(quaternion)); + if(!isActive()) return; + dQuaternion q={-quaternion.w,quaternion.x,quaternion.y,quaternion.z}; + dBodySetQuaternion(m_body,q); + CPHDisablingRotational::Reinit(); + m_flags.set(flUpdate,TRUE); + m_shell->spatial_move(); +} +void CPHElement::GetGlobalPositionDynamic(Fvector* v) +{ + if(!isActive()) return; + v->set((*(Fvector*)dBodyGetPosition(m_body))); + VERIFY(_valid(*v)); +} + +void CPHElement::SetGlobalPositionDynamic(const Fvector& position) +{ + if(!isActive()) return; + VERIFY(_valid(position)); + VERIFY_BOUNDARIES2(position,phBoundaries,PhysicsRefObject(),"SetGlobalPosition argument "); + dBodySetPosition(m_body,position.x,position.y,position.z); + CPHDisablingTranslational::Reinit(); + m_flags.set(flUpdate,TRUE); + m_shell->spatial_move(); +} + +void CPHElement::TransformPosition(const Fmatrix &form) +{ + if(!isActive())return; + VERIFY(_valid(form)); + R_ASSERT2(m_body,"body is not created"); + Fmatrix bm; + PHDynamicData::DMXPStoFMX(dBodyGetRotation(m_body),dBodyGetPosition(m_body),bm); + Fmatrix new_bm; + new_bm.mul(form,bm); + dMatrix3 dBM; + PHDynamicData::FMXtoDMX(new_bm,dBM); + dBodySetRotation(m_body,dBM); + VERIFY_BOUNDARIES2(new_bm.c,phBoundaries,PhysicsRefObject(),"TransformPosition dest pos") + dBodySetPosition(m_body,new_bm.c.x,new_bm.c.y,new_bm.c.z); + CPHDisablingFull::Reinit(); + m_body_interpolation.ResetPositions(); + m_body_interpolation.ResetRotations(); + m_flags.set(flUpdate,TRUE); + m_shell->spatial_move(); +} +CPHElement::~CPHElement () +{ + VERIFY(!isActive()); + DeleteFracturesHolder(); +} + +void CPHElement::Activate(const Fmatrix &transform,const Fvector& lin_vel,const Fvector& ang_vel,bool disable){ + VERIFY(!isActive()); + mXFORM.set(transform); + Start(); + SetTransform(transform); + + dBodySetLinearVel(m_body,lin_vel.x,lin_vel.y,lin_vel.z); + + dBodySetAngularVel(m_body,ang_vel.x,ang_vel.y,ang_vel.z); + VERIFY(dBodyStateValide(m_body)); +// dVectorSet(m_safe_position,dBodyGetPosition(m_body)); +// dQuaternionSet(m_safe_quaternion,dBodyGetQuaternion(m_body)); +// dVectorSet(m_safe_velocity,dBodyGetLinearVel(m_body)); + + m_body_interpolation.SetBody(m_body); + + if(disable) dBodyDisable(m_body); + m_flags.set(flActive,TRUE); + m_flags.set(flActivating,TRUE); + IKinematics* K=m_shell->PKinematics(); + if(K) + { + K->LL_GetBoneInstance(m_SelfID).set_callback(bctPhysics,m_shell->GetBonesCallback(),static_cast(this)); + } +} +void CPHElement::Activate(const Fmatrix &m0,float dt01,const Fmatrix &m2,bool disable){ + + Fvector lvel,avel; + lvel.set(m2.c.x-m0.c.x,m2.c.y-m0.c.y,m2.c.z-m0.c.z); + avel.set(0.f,0.f,0.f); + Activate(m0,lvel,avel,disable); + +} + + +void CPHElement::Activate(bool disable){ + + Fvector lvel,avel; + lvel.set(0.f,0.f,0.f); + avel.set(0.f,0.f,0.f); + Activate(mXFORM,lvel,avel,disable); + +} + +void CPHElement::Activate(const Fmatrix& start_from,bool disable){ + VERIFY(_valid(start_from)); + Fmatrix globe; + globe.mul_43(start_from,mXFORM); + + Fvector lvel,avel; + lvel.set(0.f,0.f,0.f); + avel.set(0.f,0.f,0.f); + Activate(globe,lvel,avel,disable); + +} + +void CPHElement::Update(){ + if(!isActive()) return; + if(m_flags.test(flActivating)) m_flags.set(flActivating,FALSE); + if( !dBodyIsEnabled(m_body)&&!m_flags.test(flUpdate)/*!bUpdate*/) return; + + InterpolateGlobalTransform(&mXFORM); + VERIFY2(_valid(mXFORM),"invalid position in update"); +} + +void CPHElement::PhTune(dReal step) +{ + if(!isActive()) return; + CPHContactBodyEffector* contact_effector= + (CPHContactBodyEffector*) dBodyGetData(m_body); + if(contact_effector)contact_effector->Apply(); + VERIFY_BOUNDARIES2(cast_fv(dBodyGetPosition(m_body)),phBoundaries,PhysicsRefObject(),"PhTune body position"); +} +void CPHElement::PhDataUpdate(dReal step){ + + if(! isActive())return; + + ///////////////skip for disabled elements//////////////////////////////////////////////////////////// + //b_enabled_onstep=!!dBodyIsEnabled(m_body); + //VERIFY_BOUNDARIES2(cast_fv(dBodyGetPosition(m_body)),phBoundaries,PhysicsRefObject(),"PhDataUpdate begin, body position"); +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawMassCenters)) + { + DBG_DrawPoint(cast_fv(dBodyGetPosition(m_body)),0.03f,D3DCOLOR_XRGB(255,0,0)); + } +#endif + + m_flags.set(flEnabledOnStep,!!dBodyIsEnabled(m_body)); + if(!m_flags.test(flEnabledOnStep)/*!b_enabled_onstep*/) return; + + + + //////////////////////////////////base pointers///////////////////////////////////////////////// + const dReal* linear_velocity = dBodyGetLinearVel(m_body) ; + const dReal* angular_velocity = dBodyGetAngularVel(m_body) ; + + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////scale velocity/////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + VERIFY(dV_valid(linear_velocity)); +#ifdef DEBUG + if(!dV_valid(angular_velocity)) + { + Msg("angular vel %f,%f,%f",angular_velocity[0],angular_velocity[1],angular_velocity[2]); + Msg("linear vel %f,%f,%f",linear_velocity[0],linear_velocity[1],linear_velocity[2]); + Msg("position %f,%f,%f",dBodyGetPosition(m_body)[0],dBodyGetPosition(m_body)[1],dBodyGetPosition(m_body)[2]); + Msg("quaternion %f,%f,%f,%f",dBodyGetQuaternion(m_body)[0],dBodyGetQuaternion(m_body)[1],dBodyGetQuaternion(m_body)[2],dBodyGetQuaternion(m_body)[3]); + Msg("matrix"); + Msg("x %f,%f,%f",dBodyGetRotation(m_body)[0],dBodyGetRotation(m_body)[4],dBodyGetRotation(m_body)[8]); + Msg("y %f,%f,%f",dBodyGetRotation(m_body)[1],dBodyGetRotation(m_body)[5],dBodyGetRotation(m_body)[9]); + Msg("z %f,%f,%f",dBodyGetRotation(m_body)[2],dBodyGetRotation(m_body)[6],dBodyGetRotation(m_body)[10]); + CPhysicsShellHolder* ph=PhysicsRefObject(); + Msg("name visual %s",*ph->cNameVisual()); + Msg("name obj %s",ph->Name()); + Msg("name section %s",*ph->cNameSect()); + VERIFY2(0,"bad angular velocity"); + } +#endif + VERIFY(!fis_zero(m_l_scale)); + VERIFY(!fis_zero(m_w_scale)); + dBodySetLinearVel( + m_body, + linear_velocity[0] /m_l_scale , + linear_velocity[1] /m_l_scale , + linear_velocity[2] /m_l_scale + ); + dBodySetAngularVel( + m_body, + angular_velocity[0] /m_w_scale , + angular_velocity[1] /m_w_scale , + angular_velocity[2] /m_w_scale + ); + + + ///////////////////scale changes values directly so get base values after it///////////////////////// + /////////////////////////////base values//////////////////////////////////////////////////////////// + dReal linear_velocity_smag = dDOT(linear_velocity,linear_velocity); + dReal linear_velocity_mag = _sqrt(linear_velocity_smag); + + dReal angular_velocity_smag = dDOT(angular_velocity,angular_velocity); + dReal angular_velocity_mag = _sqrt(angular_velocity_smag); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////limit velocity & secure ///////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + ////////////////limit linear vel//////////////////////////////////////////////////////////////////////////////////////// + + VERIFY(dV_valid(linear_velocity)); + if(linear_velocity_mag>m_l_limit) + { + CutVelocity(m_l_limit,m_w_limit); + VERIFY_BOUNDARIES2(cast_fv(dBodyGetPosition(m_body)),phBoundaries,PhysicsRefObject(),"PhDataUpdate end, body position"); + linear_velocity_smag = dDOT(linear_velocity,linear_velocity); + linear_velocity_mag = _sqrt(linear_velocity_smag); + angular_velocity_smag = dDOT(angular_velocity,angular_velocity); + angular_velocity_mag = _sqrt(angular_velocity_smag); + } + ////////////////secure position/////////////////////////////////////////////////////////////////////////////////// + const dReal* position=dBodyGetPosition(m_body); + VERIFY(dV_valid(position)); + /////////////////limit & secure angular vel/////////////////////////////////////////////////////////////////////////////// + VERIFY(dV_valid(angular_velocity)); + + if(angular_velocity_mag>m_w_limit) + { + CutVelocity(m_l_limit,m_w_limit); + angular_velocity_smag = dDOT(angular_velocity,angular_velocity); + angular_velocity_mag = _sqrt(angular_velocity_smag); + linear_velocity_smag = dDOT(linear_velocity,linear_velocity); + linear_velocity_mag = _sqrt(linear_velocity_smag); + } + + ////////////////secure rotation//////////////////////////////////////////////////////////////////////////////////////// + { + + VERIFY(dQ_valid(dBodyGetQuaternion(m_body))); + + + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////disable/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + if(dBodyIsEnabled(m_body)) Disabling(); + if(!dBodyIsEnabled(m_body)) return; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////air resistance///////////////////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + if(!fis_zero(k_w)) + dBodyAddTorque( + m_body, + -angular_velocity[0]*k_w, + -angular_velocity[1]*k_w, + -angular_velocity[2]*k_w + ); + + dMass mass; + dBodyGetMass(m_body,&mass); + dReal l_air=linear_velocity_mag*k_l;//force/velocity !!! + if(l_air>mass.mass/fixed_step) l_air=mass.mass/fixed_step;//validate + + if(!fis_zero(l_air)) + dBodyAddForce( + m_body, + -linear_velocity[0]*l_air, + -linear_velocity[1]*l_air, + -linear_velocity[2]*l_air + ); + VERIFY(dBodyStateValide(m_body)); + VERIFY2(dV_valid(dBodyGetPosition(m_body)),"invalid body position"); + VERIFY2(dV_valid(dBodyGetQuaternion(m_body)),"invalid body rotation"); +/* + if(!valid_pos(cast_fv(dBodyGetPosition(m_body)),phBoundaries)) //hack + { //hack + Fvector pos; //hack + m_body_interpolation.GetPosition(pos,0); //hack + dBodySetPosition(m_body,pos.x,pos.y,pos.z); //hack + } //hack +*/ + VERIFY_BOUNDARIES2(cast_fv(dBodyGetPosition(m_body)),phBoundaries,PhysicsRefObject(),"PhDataUpdate end, body position"); + UpdateInterpolation (); +} + +void CPHElement::Enable() { + if(!isActive()) return; + m_shell->EnableObject(0); + if(dBodyIsEnabled(m_body)) return; + dBodyEnable(m_body); +} + +void CPHElement::Disable() { + +// return; + if(!isActive()||!dBodyIsEnabled(m_body)) return; + FillInterpolation(); + + dBodyDisable(m_body); +} + +void CPHElement::ReEnable(){ + + //dJointGroupEmpty(m_saved_contacts); + //b_contacts_saved=false; + +} + +void CPHElement::Freeze() +{ + if(!m_body) return; + + m_flags.set(flWasEnabledBeforeFreeze,!!dBodyIsEnabled(m_body)); + dBodyDisable(m_body); +} +void CPHElement::UnFreeze() +{ + if(!m_body) return; + if(m_flags.test(flWasEnabledBeforeFreeze)/*was_enabled_before_freeze*/)dBodyEnable(m_body); +} +void CPHElement::applyImpulseVsMC(const Fvector& pos,const Fvector& dir, float val) +{ + if(!isActive()||m_flags.test(flFixed)) return; + if( !dBodyIsEnabled(m_body)) dBodyEnable(m_body); + ///////////////////////////////////////////////////////////////////////// + Fvector impulse; + val/=fixed_step; + impulse.set(dir); + impulse.mul(val); + dBodyAddForceAtRelPos(m_body, impulse.x,impulse.y,impulse.z,pos.x, pos.y,pos.z); + BodyCutForce(m_body,m_l_limit,m_w_limit); + //////////////////////////////////////////////////////////////////////// +} +void CPHElement::applyImpulseVsGF(const Fvector& pos,const Fvector& dir, float val) +{ + VERIFY(_valid(pos)&&_valid(dir)&&_valid(val)); + if(!isActive()||m_flags.test(flFixed)) return; + if( !dBodyIsEnabled(m_body)) dBodyEnable(m_body); + ///////////////////////////////////////////////////////////////////////// + Fvector impulse; + val/=fixed_step; + impulse.set(dir); + impulse.mul(val); + dBodyAddForceAtPos(m_body, impulse.x,impulse.y,impulse.z,pos.x, pos.y,pos.z); + BodyCutForce(m_body,m_l_limit,m_w_limit); + VERIFY(dBodyStateValide(m_body)); + //////////////////////////////////////////////////////////////////////// +} +void CPHElement:: applyImpulseTrace (const Fvector& pos, const Fvector& dir, float val,u16 id) +{ + + VERIFY(_valid(pos)&&_valid(dir)&&_valid(val)); + if(!isActive()||m_flags.test(flFixed)) return; + Fvector body_pos; + if(id!=BI_NONE) + { + if(id==m_SelfID) + { + body_pos.sub(pos,m_mass_center); + } + else + { + IKinematics* K=m_shell->PKinematics(); + if(K) + { + Fmatrix m;m.set(K->LL_GetTransform(m_SelfID)); + m.invert();m.mulB_43(K->LL_GetTransform(id)); + m.transform(body_pos,pos); + body_pos.sub(m_mass_center); + } + else + { + body_pos.set(0.f,0.f,0.f); + } + } + } + else + { + body_pos.set(0.f,0.f,0.f); + } +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phHitApplicationPoints)) + { + DBG_OpenCashedDraw(); + Fvector dbg_position;dbg_position.set(body_pos); + dMULTIPLY0_331 (cast_fp(dbg_position),dBodyGetRotation(m_body),cast_fp(body_pos)); + dbg_position.add(cast_fv(dBodyGetPosition(m_body))); + DBG_DrawPoint(dbg_position,0.01f,D3DCOLOR_XRGB(255,255,255)); + DBG_DrawLine(cast_fv(dBodyGetPosition(m_body)),dbg_position,D3DCOLOR_XRGB(255,255,255)); + DBG_DrawLine(dbg_position,Fvector().add(dbg_position,Fvector().mul(dir,0.4f)),D3DCOLOR_XRGB(255,0,255)); + DBG_ClosedCashedDraw(10000); + } +#endif + applyImpulseVsMC(body_pos,dir,val); + if(m_fratures_holder) + { + ///impulse.add(*((Fvector*)dBodyGetPosition(m_body))); + Fvector impulse;impulse.set(dir);impulse.mul(val/fixed_step); + m_fratures_holder->AddImpact(impulse,body_pos,m_shell->BoneIdToRootGeom(id)); + } +} +void CPHElement::applyImpact(const SPHImpact& I) +{ + Fvector pos; + pos.add(I.point,m_mass_center); + Fvector dir; + dir.set(I.force); + float val=I.force.magnitude(); + + if(!fis_zero(val)&& GeomByBoneID(I.geom)) + { + + dir.mul(1.f/val); + applyImpulseTrace(pos,dir,val,I.geom); + } + +} +void CPHElement::InterpolateGlobalTransform(Fmatrix* m){ + if(!m_flags.test(flUpdate)) + { + GetGlobalTransformDynamic(m); + VERIFY(_valid(*m)); + return; + } + m_body_interpolation.InterpolateRotation(*m); + m_body_interpolation.InterpolatePosition(m->c); + MulB43InverceLocalForm(*m); + m_flags.set(flUpdate,FALSE); + VERIFY(_valid(*m)); +} +void CPHElement::GetGlobalTransformDynamic(Fmatrix* m) +{ +#pragma todo ("lost alpha not normal") + if (m_body == 0) return; + + PHDynamicData::DMXPStoFMX(dBodyGetRotation(m_body),dBodyGetPosition(m_body),*m); + MulB43InverceLocalForm(*m); + VERIFY(_valid(*m)); +} + +void CPHElement::InterpolateGlobalPosition(Fvector* v){ + m_body_interpolation.InterpolatePosition(*v); + VERIFY(_valid(*v)); +} + + + + +void CPHElement::build(bool disable){ + + if(isActive()) return; + //bActive=true; + //bActivating=true; + m_flags.set(flActive,TRUE); + m_flags.set(flActivating,TRUE); + build(); + // if(place_current_forms) + { + + SetTransform(mXFORM); + } + + m_body_interpolation.SetBody(m_body); + //previous_f[0]=dInfinity; + if(disable) dBodyDisable(m_body); + + +} + +void CPHElement::RunSimulation(const Fmatrix& start_from) +{ + + RunSimulation(); + // if(place_current_forms) + { + Fmatrix globe; + globe.mul(start_from,mXFORM); + SetTransform(globe); + } + //dVectorSet(m_safe_position,dBodyGetPosition(m_body)); + //dQuaternionSet(m_safe_quaternion,dBodyGetQuaternion(m_body)); + //dVectorSet(m_safe_velocity,dBodyGetLinearVel(m_body)); + +} + + + +void CPHElement::StataticRootBonesCallBack(CBoneInstance* B) +{ + Fmatrix parent; + VERIFY2( isActive(),"the element is not active"); + VERIFY(_valid(m_shell->mXFORM)); + //VERIFY2(fsimilar(DET(B->mTransform),1.f,DET_CHECK_EPS),"Bones callback resive 0 matrix"); + VERIFY_RMATRIX(B->mTransform); + VERIFY(valid_pos(B->mTransform.c,phBoundaries)); + if(m_flags.test(flActivating)) + { + //if(!dBodyIsEnabled(m_body)) + // dBodyEnable(m_body); + VERIFY(!ph_world->Processing()); + VERIFY(_valid(B->mTransform)); + VERIFY(!m_shell->dSpace()->lock_count); + mXFORM.set(B->mTransform); + //m_start_time=Device.fTimeGlobal; + Fmatrix global_transform; + //if(m_parent_element) + global_transform.mul_43(m_shell->mXFORM,mXFORM); + SetTransform(global_transform); + + FillInterpolation(); + //bActivating=false; + m_flags.set(flActivating,FALSE); + if(!m_parent_element) + { + m_shell->m_object_in_root.set(mXFORM); + m_shell->m_object_in_root.invert(); + m_shell->SetNotActivating(); + } + B->set_callback_overwrite(TRUE); + //VERIFY2(fsimilar(DET(B->mTransform),1.f,DET_CHECK_EPS),"Bones callback returns 0 matrix"); + VERIFY_RMATRIX(B->mTransform); + VERIFY(valid_pos(B->mTransform.c,phBoundaries)); + //return; + } + + + + //VERIFY2(fsimilar(DET(B->mTransform),1.f,DET_CHECK_EPS),"Bones callback returns 0 matrix"); + VERIFY_RMATRIX(B->mTransform); + VERIFY(valid_pos(B->mTransform.c,phBoundaries)); + //if( !m_shell->is_active() && !m_flags.test(flUpdate)/*!bUpdate*/ ) return; + + { + //InterpolateGlobalTransform(&mXFORM); + parent.invert (m_shell->mXFORM); + B->mTransform.mul_43 (parent,mXFORM); + } + //VERIFY2(fsimilar(DET(B->mTransform),1.f,DET_CHECK_EPS),"Bones callback returns 0 matrix"); + VERIFY_RMATRIX(B->mTransform); + VERIFY(valid_pos(B->mTransform.c,phBoundaries)); + VERIFY2(_valid(B->mTransform),"Bones callback returns bad matrix"); + //else + //{ + + // InterpolateGlobalTransform(&m_shell->mXFORM); + // mXFORM.identity(); + // B->mTransform.set(mXFORM); + //parent.set(B->mTransform); + //parent.invert(); + //m_shell->mXFORM.mulB(parent); + + //} + + +} + +void CPHElement::BoneGlPos(Fmatrix &m,CBoneInstance* B) +{ + VERIFY(m_shell); + m.mul_43(m_shell->mXFORM, B->mTransform); +} + +void CPHElement::GetAnimBonePos(Fmatrix &bp) +{ + VERIFY(m_shell->PKinematics()); + IKinematics *k = m_shell->PKinematics(); + VERIFY(k); + CBoneInstance *BI = &k->LL_GetBoneInstance(m_SelfID); + if(!BI->callback())//. + { + bp.set(BI->mTransform); + return; + } + + k->Bone_GetAnimPos( bp, m_SelfID, u8(-1), true ); + +} + +IC bool put_in_range( Fvector &v, float range ) +{ + VERIFY( range > EPS_S ); + float sq_mag=v.square_magnitude( ); + if( sq_mag > range*range ) + { + float mag=_sqrt( sq_mag ); + v.mul( range/mag ); + return true; + } + return false; +} + +bool CPHElement::AnimToVel(float dt, float l_limit,float a_limit ) +{ + VERIFY(m_shell); + VERIFY(m_shell->PKinematics()); + //CBoneInstance *BI = &m_shell->PKinematics()->LL_GetBoneInstance(m_SelfID); +// +// Fmatrix bp;BoneGlPos(bp,BI); +// + CPhysicsShellHolder *ph = PhysicsRefObject(); + VERIFY(ph); + Fmatrix bpl;GetAnimBonePos(bpl); + Fmatrix bp;bp.mul_43(ph->XFORM(),bpl); + //BoneGlPos(bp,BI); + + Fmatrix cp; + GetGlobalTransformDynamic(&cp); + + //Fquaternion q0; q0.set(cp); + + cp.invert(); + Fmatrix diff;diff.mul_43(cp,bp); + if(dtProcessing(),*PhysicsRefObject()->cNameSect()); + VERIFY(_valid(B->mTransform)); + VERIFY(!m_shell->dSpace()->lock_count); + + mXFORM.set(B->mTransform); + + Fmatrix global_transform; + BoneGlPos(global_transform,B); + SetTransform(global_transform); + FillInterpolation(); +} + +void CPHElement::SetBoneCallbackOverwrite (bool v) +{ + VERIFY(m_shell); + VERIFY(m_shell->PKinematics()); + m_shell->PKinematics()->LL_GetBoneInstance(m_SelfID).set_callback_overwrite(v); +} +void CPHElement::BonesCallBack(CBoneInstance* B) +{ + Fmatrix parent; + VERIFY (isActive()); + VERIFY(_valid(m_shell->mXFORM)); + //VERIFY2(fsimilar(DET(B->mTransform),1.f,DET_CHECK_EPS),"Bones callback receive 0 matrix"); + VERIFY_RMATRIX(B->mTransform); + VERIFY_BOUNDARIES2(B->mTransform.c,phBoundaries,PhysicsRefObject(),"BonesCallBack incoming bone position"); + if(m_flags.test(flActivating)) + { + ToBonePos(B); + m_flags.set(flActivating,FALSE); + if(!m_parent_element) + { + m_shell->m_object_in_root.set(mXFORM); + m_shell->m_object_in_root.invert(); + m_shell->SetNotActivating(); + } + B->set_callback_overwrite(TRUE); + //VERIFY2(fsimilar(DET(B->mTransform),1.f,DET_CHECK_EPS),"Bones callback returns 0 matrix"); + VERIFY_RMATRIX(B->mTransform); + VERIFY(valid_pos(B->mTransform.c,phBoundaries)); + return; + } + + VERIFY_RMATRIX(B->mTransform); + VERIFY(valid_pos(B->mTransform.c,phBoundaries)); + + + { + + parent.invert (m_shell->mXFORM); + B->mTransform.mul_43(parent,mXFORM); + } + //VERIFY2(fsimilar(DET(B->mTransform),1.f,DET_CHECK_EPS),"Bones callback returns 0 matrix"); + VERIFY_RMATRIX(B->mTransform); + VERIFY(valid_pos(B->mTransform.c,phBoundaries)); + VERIFY2(_valid(B->mTransform),"Bones callback returns bad matrix"); + //else + //{ + + // InterpolateGlobalTransform(&m_shell->mXFORM); + // mXFORM.identity(); + // B->mTransform.set(mXFORM); + //parent.set(B->mTransform); + //parent.invert(); + //m_shell->mXFORM.mulB(parent); + + //} + + +} + +void CPHElement::set_PhysicsRefObject(CPhysicsShellHolder* ref_object) +{ + CPHGeometryOwner::set_PhysicsRefObject(ref_object); +} + + +void CPHElement::set_ObjectContactCallback(ObjectContactCallbackFun* callback) +{ + CPHGeometryOwner::set_ObjectContactCallback(callback); +} +void CPHElement::add_ObjectContactCallback(ObjectContactCallbackFun* callback) +{ + CPHGeometryOwner::add_ObjectContactCallback(callback); +} +void CPHElement::remove_ObjectContactCallback(ObjectContactCallbackFun* callback) +{ + CPHGeometryOwner::remove_ObjectContactCallback(callback); +} +ObjectContactCallbackFun* CPHElement::get_ObjectContactCallback() +{ + return CPHGeometryOwner::get_ObjectContactCallback(); +} +void CPHElement::set_CallbackData(void * cd) +{ + CPHGeometryOwner::set_CallbackData(cd); +} +void* CPHElement:: get_CallbackData () +{ + return CPHGeometryOwner::get_CallbackData(); +} +void CPHElement::set_ContactCallback(ContactCallbackFun* callback) +{ + //push_untill=0; + CPHGeometryOwner::set_ContactCallback(callback); +} + +void CPHElement::SetMaterial(u16 m) +{ + CPHGeometryOwner::SetMaterial(m); +} + +dMass* CPHElement::getMassTensor() //aux +{ + return &m_mass; +} + +void CPHElement::setInertia(const dMass& M) +{ + m_mass=M; + if(!isActive()||m_flags.test(flFixed))return; + dBodySetMass(m_body,&M); +} + +void CPHElement::addInertia(const dMass& M) +{ + dMassAdd(&m_mass,&M); + if(!isActive())return; + dBodySetMass(m_body,&m_mass); +} +void CPHElement::get_LinearVel(Fvector& velocity) +{ + if(!isActive()||!dBodyIsEnabled(m_body)) + { + velocity.set(0,0,0); + return; + } + dVectorSet((dReal*)&velocity,dBodyGetLinearVel(m_body)); + +} +void CPHElement::get_AngularVel (Fvector& velocity) +{ + if(!isActive()||!dBodyIsEnabled(m_body)) + { + velocity.set(0,0,0); + return; + } + dVectorSet((dReal*)&velocity,dBodyGetAngularVel(m_body)); +} + + +void CPHElement::set_LinearVel (const Fvector& velocity) +{ + + if(!isActive()||m_flags.test(flFixed)) return; + VERIFY2(_valid(velocity),"not valid arqument velocity"); + Fvector vel = velocity; +#ifdef DEBUG + if( velocity.magnitude() > m_l_limit ) + Msg(" CPHElement::set_LinearVel set velocity magnitude is too large %f",velocity.magnitude()); +#endif + put_in_range(vel,m_l_limit); + dBodySetLinearVel(m_body,vel.x,vel.y,vel.z); + //dVectorSet(m_safe_velocity,dBodyGetLinearVel(m_body)); +} + + +void CPHElement::set_AngularVel (const Fvector& velocity) +{ + VERIFY(_valid(velocity)); + if(!isActive()||m_flags.test(flFixed)) return; + + Fvector vel = velocity; +#ifdef DEBUG + if( velocity.magnitude() > m_w_limit ) + Msg("CPHElement::set_AngularVel set velocity magnitude is too large %f",velocity.magnitude()); +#endif + put_in_range(vel,m_w_limit); + dBodySetAngularVel(m_body,vel.x,vel.y,vel.z); +} + +void CPHElement::getForce(Fvector& force) +{ + if(!isActive()) return; + force.set(*(Fvector*)dBodyGetForce(m_body)); + VERIFY(dBodyStateValide(m_body)); +} +void CPHElement::getTorque(Fvector& torque) +{ + if(!isActive()) return; + torque.set(*(Fvector*)dBodyGetTorque(m_body)); + VERIFY(dBodyStateValide(m_body)); +} +void CPHElement::setForce(const Fvector& force) +{ + if(!isActive()||m_flags.test(flFixed)) return; + if( !dBodyIsEnabled(m_body)) dBodyEnable(m_body); + m_shell->EnableObject(0); + dBodySetForce(m_body,force.x,force.y,force.z); + BodyCutForce(m_body,m_l_limit,m_w_limit); + VERIFY(dBodyStateValide(m_body)); +} +void CPHElement::setTorque(const Fvector& torque) +{ + if(!isActive()||m_flags.test(flFixed)) return; + if( !dBodyIsEnabled(m_body)) dBodyEnable(m_body); + m_shell->EnableObject(0); + dBodySetTorque(m_body,torque.x,torque.y,torque.z); + BodyCutForce(m_body,m_l_limit,m_w_limit); + VERIFY(dBodyStateValide(m_body)); +} + +void CPHElement::applyForce(const Fvector& dir, float val) //aux +{ + applyForce (dir.x*val,dir.y*val,dir.z*val); +} +void CPHElement::applyForce(float x,float y,float z) //called anywhere ph state influent +{ + VERIFY(_valid(x)&&_valid(y)&&_valid(z)); + if(!isActive())return;//hack?? + if(m_flags.test(flFixed)) return; + if( !dBodyIsEnabled(m_body)) dBodyEnable(m_body); + m_shell->EnableObject(0); + dBodyAddForce(m_body,x,y,z); + BodyCutForce(m_body,m_l_limit,m_w_limit); + VERIFY(dBodyStateValide(m_body)); +} + +void CPHElement::applyImpulse(const Fvector& dir, float val)//aux +{ + + applyForce(dir.x*val/fixed_step,dir.y*val/fixed_step,dir.z*val/fixed_step); +} + + + +void CPHElement::add_Shape(const SBoneShape& shape,const Fmatrix& offset) +{ + CPHGeometryOwner::add_Shape(shape,offset); +} + +void CPHElement::add_Shape(const SBoneShape& shape) +{ + CPHGeometryOwner::add_Shape(shape); +} + +#pragma todo("remake it using Geometry functions") + +void CPHElement::add_Mass(const SBoneShape& shape,const Fmatrix& offset,const Fvector& mass_center,float mass,CPHFracture* fracture) +{ + + dMass m; + dMatrix3 DMatx; + switch(shape.type) + { + case SBoneShape::stBox : + { + dMassSetBox(&m,1.f,shape.box.m_halfsize.x*2.f,shape.box.m_halfsize.y*2.f,shape.box.m_halfsize.z*2.f); + dMassAdjust(&m,mass); + Fmatrix box_transform; + shape.box.xform_get(box_transform); + PHDynamicData::FMX33toDMX(shape.box.m_rotate,DMatx); + dMassRotate(&m,DMatx); + dMassTranslate(&m,shape.box.m_translate.x-mass_center.x,shape.box.m_translate.y-mass_center.y,shape.box.m_translate.z-mass_center.z); + break; + } + case SBoneShape::stSphere : + { + shape.sphere; + dMassSetSphere(&m,1.f,shape.sphere.R); + dMassAdjust(&m,mass); + dMassTranslate(&m,shape.sphere.P.x-mass_center.x,shape.sphere.P.y-mass_center.y,shape.sphere.P.z-mass_center.z); + break; + } + + + case SBoneShape::stCylinder : + { + const Fvector& pos=shape.cylinder.m_center; + Fvector l; + l.sub(pos,mass_center); + dMassSetCylinder(&m,1.f,2,shape.cylinder.m_radius,shape.cylinder.m_height); + dMassAdjust(&m,mass); + dMatrix3 DMatx; + Fmatrix33 m33; + m33.j.set(shape.cylinder.m_direction); + Fvector::generate_orthonormal_basis(m33.j,m33.k,m33.i); + PHDynamicData::FMX33toDMX(m33,DMatx); + dMassRotate(&m,DMatx); + dMassTranslate(&m,l.x,l.y,l.z); + break; + } + + case SBoneShape::stNone : + break; + default: NODEFAULT; + } + PHDynamicData::FMXtoDMX(offset,DMatx); + dMassRotate(&m,DMatx); + + Fvector mc; + offset.transform_tiny(mc,mass_center); + //calculate _new mass_center + //new_mc=(m_mass_center*m_mass.mass+mc*mass)/(mass+m_mass.mass) + Fvector tmp1; + tmp1.set(m_mass_center); + tmp1.mul(m_mass.mass); + Fvector tmp2; + tmp2.set(mc); + tmp2.mul(mass); + Fvector new_mc; + new_mc.add(tmp1,tmp2); + new_mc.mul(1.f/(mass+m_mass.mass)); + mc.sub(new_mc); + dMassTranslate(&m,mc.x,mc.y,mc.z); + m_mass_center.sub(new_mc); + dMassTranslate(&m_mass,m_mass_center.x,m_mass_center.y,m_mass_center.z); + if(m_fratures_holder) + { + m_fratures_holder->DistributeAdditionalMass(u16(m_geoms.size()-1),m); + } + if(fracture) + { + fracture->MassAddToSecond(m); + } + R_ASSERT2(dMass_valide(&m),"bad bone mass params"); + dMassAdd(&m_mass,&m); + R_ASSERT2(dMass_valide(&m),"bad result mass params"); + m_mass_center.set(new_mc); +} + +void CPHElement::set_BoxMass(const Fobb& box, float mass) +{ + dMassSetZero(&m_mass); + m_mass_center.set(box.m_translate); + const Fvector& hside=box.m_halfsize; + dMassSetBox(&m_mass,1,hside.x*2.f,hside.y*2.f,hside.z*2.f); + dMassAdjust(&m_mass,mass); + dMatrix3 DMatx; + PHDynamicData::FMX33toDMX(box.m_rotate,DMatx); + dMassRotate(&m_mass,DMatx); + +} + +void CPHElement::calculate_it_data_use_density(const Fvector& mc,float density) +{ + dMassSetZero(&m_mass); + GEOM_I i_geom=m_geoms.begin(),e=m_geoms.end(); + for(;i_geom!=e;++i_geom)(*i_geom)->add_self_mass(m_mass,mc,density); + VERIFY2(dMass_valide(&m_mass),"non valide mass obtained!"); +} + +float CPHElement::getRadius() +{ + return CPHGeometryOwner::getRadius(); +} + +void CPHElement::set_DynamicLimits(float l_limit,float w_limit) +{ + m_l_limit=l_limit; + m_w_limit=w_limit; +} + +void CPHElement::set_DynamicScales(float l_scale/* =default_l_scale */,float w_scale/* =default_w_scale */) +{ + m_l_scale=l_scale; + m_w_scale=w_scale; +} + +void CPHElement::set_DisableParams (const SAllDDOParams& params) +{ + CPHDisablingFull::set_DisableParams(params); +} + + + + +void CPHElement::get_Extensions(const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) +{ + //lost alpha starts + if (m_body) + CPHGeometryOwner::get_Extensions(axis,center_prg,lo_ext,hi_ext); + else + { + lo_ext=dInfinity;hi_ext=-dInfinity; + } +} + +const Fvector& CPHElement::mass_Center() +{ + VERIFY(dBodyStateValide(m_body)); + return *((const Fvector*)dBodyGetPosition(m_body)); +} + +CPhysicsShell* CPHElement::PhysicsShell() +{ + return smart_cast(m_shell); +} + +CPHShell* CPHElement::PHShell() +{ + return (m_shell); +} +void CPHElement::SetShell(CPHShell* p) +{ + if(!m_body||!m_shell) + { + m_shell=p; + return; + } + if(m_shell!=p) + { + m_shell->Island().RemoveBody(m_body); + p->Island().AddBody(m_body); + m_shell=p; + } + +} +void CPHElement::PassEndGeoms(u16 from,u16 to,CPHElement* dest) +{ + GEOM_I i_from=m_geoms.begin()+from,e=m_geoms.begin()+to; + u16 shift=to-from; + GEOM_I i=i_from; + for(;i!=e;++i) + { + (*i)->remove_from_space(m_group); + //(*i)->add_to_space(dest->m_group); + //(*i)->set_body(dest->m_body); + (*i)->set_body(0); + u16& element_pos=(*i)->element_position(); + element_pos=element_pos-shift; + } + GEOM_I last=m_geoms.end(); + for(;i!=last;++i) + { + u16& element_pos=(*i)->element_position(); + element_pos=element_pos-shift; + } + + dest->m_geoms.insert(dest->m_geoms.end(),i_from,e); + dest->b_builded=true; + m_geoms.erase(i_from,e); +} +void CPHElement::SplitProcess(ELEMENT_PAIR_VECTOR &new_elements) +{ + m_fratures_holder->SplitProcess(this,new_elements); + if(!m_fratures_holder->m_fractures.size()) xr_delete(m_fratures_holder); +} +void CPHElement::DeleteFracturesHolder() +{ + xr_delete(m_fratures_holder); +} + +void CPHElement::CreateSimulBase() +{ + m_body=dBodyCreate(0); + m_shell->Island().AddBody(m_body); + //m_saved_contacts=dJointGroupCreate (0); + //b_contacts_saved=false; + dBodyDisable(m_body); + CPHGeometryOwner::CreateSimulBase(); +} +void CPHElement::ReAdjustMassPositions(const Fmatrix &shift_pivot,float density) +{ + + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) + { + (*i)->move_local_basis(shift_pivot); + } + if(m_shell->PKinematics()) + { + float mass; + get_mc_kinematics(m_shell->PKinematics(),m_mass_center,mass); + calculate_it_data(m_mass_center,mass); + } + else + { + + setDensity(density); + } + + dBodySetMass(m_body,&m_mass); + //m_inverse_local_transform.identity(); + //m_inverse_local_transform.c.set(m_mass_center); + //m_inverse_local_transform.invert(); + //dBodySetPosition(m_body,m_mass_center.x,m_mass_center.y,m_mass_center.z); +} +void CPHElement::ResetMass(float density) +{ + Fvector tmp,shift_mc; + + tmp.set(m_mass_center); + + + setDensity(density); + dBodySetMass(m_body,&m_mass); + + shift_mc.sub(m_mass_center,tmp); + tmp.set(*(Fvector *)dBodyGetPosition(m_body)); + tmp.add(shift_mc); + + + //bActivating = true; + m_flags.set(flActivating,TRUE); + + CPHGeometryOwner::setPosition(m_mass_center); +} +void CPHElement::ReInitDynamics(const Fmatrix &shift_pivot,float density) +{ + VERIFY(_valid(shift_pivot)&&_valid(density)); + ReAdjustMassPositions(shift_pivot,density); + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) + { + (*i)->set_position(m_mass_center); + (*i)->set_body(m_body); + //if(object_contact_callback)geom.set_obj_contact_cb(object_contact_callback); + //if(m_phys_ref_object) geom.set_ref_object(m_phys_ref_object); + if(m_group) + { + (*i)->add_to_space((dSpaceID)m_group); + } + } +} + +void CPHElement::PresetActive() +{ + if(isActive()) return; + + CBoneInstance& B=m_shell->PKinematics()->LL_GetBoneInstance(m_SelfID); + mXFORM.set(B.mTransform); + //m_start_time=Device.fTimeGlobal; + Fmatrix global_transform; + global_transform.mul_43(m_shell->mXFORM, mXFORM); + SetTransform(global_transform); + + if(!m_parent_element) + { + m_shell->m_object_in_root.set(mXFORM); + m_shell->m_object_in_root.invert(); + + } + //dVectorSet(m_safe_position,dBodyGetPosition(m_body)); + //dQuaternionSet(m_safe_quaternion,dBodyGetQuaternion(m_body)); + //dVectorSet(m_safe_velocity,dBodyGetLinearVel(m_body)); + + ////////////////////////////////////////////////////////////// + //initializing values for disabling////////////////////////// + ////////////////////////////////////////////////////////////// + VERIFY(dBodyStateValide(m_body)); + m_body_interpolation.SetBody(m_body); + FillInterpolation(); + //bActive=true; + m_flags.set(flActive,TRUE); + RunSimulation(); + VERIFY(dBodyStateValide(m_body)); +} + + +bool CPHElement::isBreakable() +{ + return !!m_fratures_holder; +} +u16 CPHElement::setGeomFracturable(CPHFracture& fracture) +{ + if(!m_fratures_holder) m_fratures_holder=new CPHFracturesHolder(); + return m_fratures_holder->AddFracture(fracture); +} + +CPHFracture& CPHElement::Fracture(u16 num) +{ + R_ASSERT2(m_fratures_holder,"no fractures!"); + return m_fratures_holder->Fracture(num); +} +u16 CPHElement::numberOfGeoms() +{ + return CPHGeometryOwner::numberOfGeoms(); +} + + +void CPHElement::cv2bone_Xfrom(const Fquaternion& q,const Fvector& pos, Fmatrix& xform) +{ + VERIFY2(_valid(q)&&_valid(pos),"cv2bone_Xfrom receive wrong data"); + xform.rotation(q); + xform.c.set(pos); + //xform.mulB(m_inverse_local_transform); + MulB43InverceLocalForm(xform); + VERIFY2(_valid(xform),"cv2bone_Xfrom returns wrong data"); +} +void CPHElement::cv2obj_Xfrom(const Fquaternion& q,const Fvector& pos, Fmatrix& xform) +{ + + cv2bone_Xfrom(q,pos,xform); + xform.mulB_43(m_shell->m_object_in_root); + VERIFY2(_valid(xform),"cv2obj_Xfrom returns wrong data"); +} + +void CPHElement::set_ApplyByGravity(bool flag) +{ + if(!isActive()||m_flags.test(flFixed)) return; + dBodySetGravityMode(m_body,flag); +} +bool CPHElement::get_ApplyByGravity() +{ + return (!!dBodyGetGravityMode(m_body)); +} + +void CPHElement::Fix() +{ + m_flags.set(flFixed,TRUE); + FixBody(m_body); +} +void CPHElement::ReleaseFixed() +{ + if(!isFixed()) return; + m_flags.set(flFixed,FALSE); + if(!isActive())return; + dBodySetMass(m_body,&m_mass); +} +void CPHElement::applyGravityAccel (const Fvector& accel) +{ + VERIFY(_valid(accel)); + if(m_flags.test(flFixed)) return; + if( !dBodyIsEnabled(m_body)) dBodyEnable(m_body); + m_shell->EnableObject(0); + Fvector val; + val.set(accel); + val.mul(m_mass.mass); + //ApplyGravityAccel(m_body,(const dReal*)(&accel)); + applyForce(val.x,val.y,val.z); +} + + +void CPHElement::CutVelocity(float l_limit,float a_limit) +{ + + if(!isActive())return; + VERIFY(_valid(l_limit)&&_valid(a_limit)); + dVector3 limitedl,limiteda,diffl,diffa; + bool blimitl=dVectorLimit(dBodyGetLinearVel(m_body),l_limit,limitedl); + bool blimita=dVectorLimit(dBodyGetAngularVel(m_body),a_limit,limiteda); + + if(blimitl||blimita) + { + dVectorSub(diffl,limitedl,dBodyGetLinearVel(m_body)); + dVectorSub(diffa,limiteda,dBodyGetAngularVel(m_body)); + dBodySetLinearVel(m_body,diffl[0],diffl[1],diffl[2]); + dBodySetAngularVel(m_body,diffa[0],diffa[1],diffa[2]); + dxStepBody(m_body,fixed_step); + dBodySetLinearVel(m_body,limitedl[0],limitedl[1],limitedl[2]); + dBodySetAngularVel(m_body,limiteda[0],limiteda[1],limiteda[2]); + } +} +void CPHElement::ClearDestroyInfo() +{ + xr_delete(m_fratures_holder); +} +//bool CPHElement::CheckBreakConsistent() +//{ +// if(!m_fratures_holder) return true; +// m_fratures_holder->m_fractures +// m_fratures_holder->Fracture() +//}) return true; +// m_fratures_holder->m_fractures +// m_fratures_holder->Fracture() +//} \ No newline at end of file diff --git a/src/xrGameLA/PHElement.h b/src/xrGameLA/PHElement.h new file mode 100644 index 000000000..3c4b16076 --- /dev/null +++ b/src/xrGameLA/PHElement.h @@ -0,0 +1,260 @@ +///////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +///////////////////////////Implemetation//for//CPhysicsElement////////////////// +//////////////////////////////////////////////////////////////////////////////// +#include "Geometry.h" +#include "phdefs.h" +#include "PhysicsCommon.h" +#include "PHSynchronize.h" +#include "PHDisabling.h" +#include "PHGeometryOwner.h" +#include "PHInterpolation.h" +#ifndef PH_ELEMENT +#define PH_ELEMENT +class CPHElement; +class CPHShell; +class CPHFracture; +struct SPHImpact; +class CPHFracturesHolder; +class CPHElement : + public CPhysicsElement , + public CPHSynchronize, + public CPHDisablingFull, + public CPHGeometryOwner +{ + friend class CPHFracturesHolder; + + //float m_start_time; //uu ->to shell ?? //aux + dMass m_mass; //e ?? //bl + dBodyID m_body; //e //st + dReal m_l_scale; // ->to shell ?? //bl + dReal m_w_scale; // ->to shell ?? //bl + CPHElement *m_parent_element; //bool ! //bl + CPHShell *m_shell; //e //bl + CPHInterpolation m_body_interpolation; //e //bl + CPHFracturesHolder *m_fratures_holder; //e //bl + + dReal m_w_limit ; //->to shell ?? //bl + dReal m_l_limit ; //->to shell ?? //bl +// dVector3 m_safe_position; //e //st +// dQuaternion m_safe_quaternion; +// dVector3 m_safe_velocity; //e //st +// Fmatrix m_inverse_local_transform; //e //bt + dReal k_w; //->to shell ?? //st + dReal k_l; //->to shell ?? //st + //ObjectContactCallbackFun* temp_for_push_out; //->to shell ?? //aux + //u32 push_untill; //->to shell ?? //st + Flags8 m_flags; // + enum + { + flActive = 1<<0, + flActivating = 1<<1, + flUpdate = 1<<2, + flWasEnabledBeforeFreeze= 1<<3, + flEnabledOnStep = 1<<4, + flFixed = 1<<5, + flAnimated = 1<<6 + }; +// bool was_enabled_before_freeze; +// bool bUpdate; //->to shell ?? //st +// bool b_enabled_onstep; +private: +////////////////////////////////////////////Interpolation///////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void FillInterpolation () //interpolation called anywhere visual influent + { + m_body_interpolation.ResetPositions(); + m_body_interpolation.ResetRotations(); + //bUpdate=true; + m_flags.set(flUpdate,TRUE); + } +IC void UpdateInterpolation () //interpolation called from ph update visual influent + { + ///VERIFY(dBodyStateValide(m_body)); + m_body_interpolation.UpdatePositions(); + m_body_interpolation.UpdateRotations(); + //bUpdate=true; + m_flags.set(flUpdate,TRUE); + } +public: +////////////////////////////////////////////////Geometry///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void add_Sphere (const Fsphere& V); //aux + virtual void add_Box (const Fobb& V); //aux + virtual void add_Cylinder (const Fcylinder& V); //aux + virtual void add_Shape (const SBoneShape& shape); //aux + virtual void add_Shape (const SBoneShape& shape,const Fmatrix& offset); //aux + virtual CODEGeom* last_geom (){return CPHGeometryOwner::last_geom();} //aux + virtual bool has_geoms (){return CPHGeometryOwner::has_geoms();} + virtual void set_ContactCallback (ContactCallbackFun* callback); //aux (may not be) + virtual void set_ObjectContactCallback (ObjectContactCallbackFun* callback); //called anywhere ph state influent + virtual void add_ObjectContactCallback (ObjectContactCallbackFun* callback); //called anywhere ph state influent + virtual void remove_ObjectContactCallback (ObjectContactCallbackFun* callback); + virtual void set_CallbackData (void * cd); + virtual void *get_CallbackData (); + virtual ObjectContactCallbackFun *get_ObjectContactCallback (); + virtual void set_PhysicsRefObject (CPhysicsShellHolder* ref_object); //aux + virtual CPhysicsShellHolder* PhysicsRefObject (){return m_phys_ref_object;} //aux + virtual void SetMaterial (u16 m); //aux + virtual void SetMaterial (LPCSTR m){CPHGeometryOwner::SetMaterial(m);} //aux + virtual u16 numberOfGeoms (); //aux + virtual const Fvector& local_mass_Center () {return CPHGeometryOwner::local_mass_Center();} //aux + virtual float getVolume () {return CPHGeometryOwner::get_volume();} //aux + virtual void get_Extensions (const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext); //aux + virtual void get_MaxAreaDir (Fvector& dir){CPHGeometryOwner::get_MaxAreaDir(dir);} + virtual float getRadius (); +////////////////////////////////////////////////////Mass///////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +private: + void calculate_it_data (const Fvector& mc,float mass); //aux + void calculate_it_data_use_density (const Fvector& mc,float density); //aux + void calc_it_fract_data_use_density (const Fvector& mc,float density);//sets element mass and fractures parts mass //aux + dMass recursive_mass_summ (u16 start_geom,FRACTURE_I cur_fracture); //aux +public: // + virtual const Fvector& mass_Center () ; //aux + virtual void setDensity (float M); //aux + virtual float getDensity (){return m_mass.mass/m_volume;} //aux + virtual void setMassMC (float M,const Fvector& mass_center); //aux + virtual void setDensityMC (float M,const Fvector& mass_center); //aux + virtual void setInertia (const dMass& M); //aux + virtual void addInertia (const dMass& M); + virtual void add_Mass (const SBoneShape& shape,const Fmatrix& offset,const Fvector& mass_center,float mass,CPHFracture* fracture=NULL);//aux + virtual void set_BoxMass (const Fobb& box, float mass); //aux + virtual void setMass (float M); //aux + virtual float getMass (){return m_mass.mass;} //aux + virtual dMass* getMassTensor (); //aux + void ReAdjustMassPositions (const Fmatrix &shift_pivot,float density); //aux + void ResetMass (float density); //aux + void CutVelocity (float l_limit,float a_limit); +///////////////////////////////////////////////////PushOut/////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +private: // + +public: // +//////////////////////////////////////////////Disable///////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void Disable () ; // + virtual void ReEnable () ; // + void Enable () ; //aux + virtual bool isEnabled () const {return isActive()&&dBodyIsEnabled(m_body);} + virtual bool isFullActive () const {return isActive()&&!m_flags.test(flActivating);} + virtual bool isActive () const {return !!m_flags.test(flActive);} + virtual void Freeze () ; // + virtual void UnFreeze () ; // + virtual bool EnabledStateOnStep () {return dBodyIsEnabled(m_body)||m_flags.test(flEnabledOnStep);} // +////////////////////////////////////////////////Updates/////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool AnimToVel ( float dt, float l_limit,float a_limit ); + void BoneGlPos (Fmatrix &m,CBoneInstance* B); + void ToBonePos (CBoneInstance* B); + + void SetBoneCallbackOverwrite (bool v); + void __stdcall BonesCallBack (CBoneInstance* B); //called from updateCL visual influent + void StataticRootBonesCallBack (CBoneInstance* B); + void PhDataUpdate (dReal step); //ph update + void PhTune (dReal step); //ph update + virtual void Update (); //called update CL visual influence +//////////////////////////////////////////////////Dynamics//////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetAirResistance (dReal linear=default_k_l, dReal angular=default_k_w) //aux (may not be) + { // + k_w= angular; // + k_l=linear; // + } // + virtual void GetAirResistance (float &linear, float &angular) // + { // + linear= k_l; // + angular=k_w; // + } + virtual void applyImpact (const SPHImpact& impact); // + virtual void applyImpulseTrace (const Fvector& pos, const Fvector& dir, float val,const u16 id) ; //called anywhere ph state influent + virtual void set_DisableParams (const SAllDDOParams& params) ; // + virtual void set_DynamicLimits (float l_limit=default_l_limit,float w_limit=default_w_limit); //aux (may not be) + virtual void set_DynamicScales (float l_scale=default_l_scale,float w_scale=default_w_scale); //aux (may not be) + virtual void Fix (); + virtual void ReleaseFixed (); + virtual bool isFixed (){return !!(m_flags.test(flFixed));} + virtual void applyForce (const Fvector& dir, float val); //aux + virtual void applyForce (float x,float y,float z); //called anywhere ph state influent + virtual void applyImpulse (const Fvector& dir, float val);//aux + virtual void applyImpulseVsMC (const Fvector& pos,const Fvector& dir, float val); // + virtual void applyImpulseVsGF (const Fvector& pos,const Fvector& dir, float val); // + virtual void __stdcall applyGravityAccel (const Fvector& accel); + virtual void getForce (Fvector& force); + virtual void getTorque (Fvector& torque); + virtual void get_LinearVel (Fvector& velocity); //aux + virtual void get_AngularVel (Fvector& velocity); //aux + virtual void set_LinearVel (const Fvector& velocity); //called anywhere ph state influent + virtual void set_AngularVel (const Fvector& velocity); //called anywhere ph state influent + virtual void setForce (const Fvector& force); // + virtual void setTorque (const Fvector& torque); // + virtual void set_ApplyByGravity (bool flag) ; // + virtual bool get_ApplyByGravity () ; // +///////////////////////////////////////////////////Net//////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void get_State ( SPHNetState& state); // + virtual void set_State (const SPHNetState& state); // + virtual void net_Import (NET_Packet& P) ; + virtual void net_Export (NET_Packet& P) ; +///////////////////////////////////////////////////Position/////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetTransform (const Fmatrix& m0); // + virtual void TransformPosition (const Fmatrix &form); + virtual void getQuaternion (Fquaternion& quaternion); // + virtual void setQuaternion (const Fquaternion& quaternion); // + virtual void SetGlobalPositionDynamic (const Fvector& position); // + virtual void GetGlobalPositionDynamic (Fvector* v); // + virtual void cv2obj_Xfrom (const Fquaternion& q,const Fvector& pos, Fmatrix& xform); // + virtual void cv2bone_Xfrom (const Fquaternion& q,const Fvector& pos, Fmatrix& xform); // + virtual void __stdcall InterpolateGlobalTransform (Fmatrix* m); //called UpdateCL vis influent + virtual void InterpolateGlobalPosition (Fvector* v); //aux + virtual void GetGlobalTransformDynamic (Fmatrix* m); //aux +IC void InverceLocalForm (Fmatrix&) ; +IC void MulB43InverceLocalForm (Fmatrix&) ; + +////////////////////////////////////////////////////Structure///////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual CPhysicsShell* PhysicsShell (); //aux + CPHShell* PHShell (); + virtual void set_ParentElement (CPhysicsElement* p){m_parent_element=(CPHElement*)p;} //aux + void SetShell (CPHShell* p); //aux + virtual dBodyID get_body () {return m_body;}; //aux + virtual const dBodyID get_bodyConst ()const {return m_body;}; //aux +//////////////////////////////////////////////////////Breakable////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + IC CPHFracturesHolder* FracturesHolder (){return m_fratures_holder;} //aux + IC const CPHFracturesHolder* constFracturesHolder ()const{return m_fratures_holder;} //aux + void DeleteFracturesHolder (); // + virtual bool isBreakable (); //aux + virtual u16 setGeomFracturable (CPHFracture& fracture); //aux + virtual CPHFracture& Fracture (u16 num); //aux + void SplitProcess (ELEMENT_PAIR_VECTOR &new_elements); //aux + void PassEndGeoms (u16 from,u16 to,CPHElement* dest); //aux +////////////////////////////////////////////////////Build/Activate//////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void Activate (const Fmatrix& m0, float dt01, const Fmatrix& m2,bool disable=false); //some isues not to be aux + virtual void Activate (const Fmatrix &transform,const Fvector& lin_vel,const Fvector& ang_vel,bool disable=false);//some isues not to be aux + virtual void Activate (bool disable=false); //some isues not to be aux + virtual void Activate (const Fmatrix& start_from, bool disable=false); //some isues not to be aux + virtual void Deactivate (); //aux //aux + void CreateSimulBase ();//create body & cpace //aux + void ReInitDynamics (const Fmatrix &shift_pivot,float density); //set body & geom positions + void PresetActive (); // + void build (); //aux + void build (bool disable); //aux + void destroy (); //called anywhere ph state influent + void Start (); //aux + void RunSimulation (); //called anywhere ph state influent + void RunSimulation (const Fmatrix& start_from); // + void ClearDestroyInfo (); + void GetAnimBonePos (Fmatrix &bp); + // bool CheckBreakConsistent () + CPHElement (); //aux + virtual ~CPHElement (); //aux +}; + +IC CPHElement* cast_PHElement(CPhysicsElement* e){return static_cast(static_cast(e));} +IC CPHElement* cast_PHElement(void* e){return static_cast(static_cast(e));} +IC CPhysicsElement* cast_PhysicsElement(CPHElement* e){return static_cast(static_cast(e));} +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHElementInline.h b/src/xrGameLA/PHElementInline.h new file mode 100644 index 000000000..95d52695e --- /dev/null +++ b/src/xrGameLA/PHElementInline.h @@ -0,0 +1,16 @@ + + +IC void CPHElement::InverceLocalForm(Fmatrix& m) +{ + m.identity(); + m.c.set(m_mass_center); + m.invert(); +} + +IC void CPHElement::MulB43InverceLocalForm(Fmatrix& m) +{ + Fvector ic;ic.set(m_mass_center); + ic.invert(); + m.transform_dir(ic); + m.c.add(ic); +} \ No newline at end of file diff --git a/src/xrGameLA/PHElementNetState.cpp b/src/xrGameLA/PHElementNetState.cpp new file mode 100644 index 000000000..c90541617 --- /dev/null +++ b/src/xrGameLA/PHElementNetState.cpp @@ -0,0 +1,67 @@ +#include "StdAfx.h" +#include "physicsshell.h" +#include "phinterpolation.h" +#include "phelement.h" +#include "phobject.h" +#include "phworld.h" +#include "phshell.h" + +void CPHElement::get_State(SPHNetState& state) +{ + GetGlobalPositionDynamic(&state.position); + getQuaternion(state.quaternion); + m_body_interpolation.GetPosition(state.previous_position,0); + m_body_interpolation.GetRotation(state.previous_quaternion,0); + get_LinearVel(state.linear_vel); + get_AngularVel(state.angular_vel); + getForce(state.force); + getTorque(state.torque); + if(!isActive()) + { + state.enabled=false; + return; + } + state.enabled=!!dBodyIsEnabled(m_body); +} +void CPHElement::set_State(const SPHNetState& state) +{ + //bUpdate=true; + m_flags.set(flUpdate,TRUE); + SetGlobalPositionDynamic(state.position); + setQuaternion(state.quaternion); + m_body_interpolation.SetPosition(state.previous_position,0); + m_body_interpolation.SetRotation(state.previous_quaternion,0); + m_body_interpolation.SetPosition(state.position,1); + m_body_interpolation.SetRotation(state.quaternion,1); + set_LinearVel(state.linear_vel); + set_AngularVel(state.angular_vel); + setForce(state.force); + setTorque(state.torque); + if(!isActive()) return; + if(state.enabled&& !dBodyIsEnabled(m_body)) + { + dBodyEnable(m_body); + m_shell->EnableObject(0); + } + if(!state.enabled && dBodyIsEnabled(m_body)) + { + m_shell->DisableObject(); + Disable(); + } + CPHDisablingFull::Reinit(); + m_flags.set(flUpdate,TRUE); +} + +void CPHElement ::net_Export(NET_Packet& P) +{ + SPHNetState state; + get_State(state); + state.net_Export(P); +} + +void CPHElement::net_Import(NET_Packet& P) +{ + SPHNetState state; + state.net_Import(P); + set_State(state); +} \ No newline at end of file diff --git a/src/xrGameLA/PHFracture.cpp b/src/xrGameLA/PHFracture.cpp new file mode 100644 index 000000000..7ba72aac7 --- /dev/null +++ b/src/xrGameLA/PHFracture.cpp @@ -0,0 +1,590 @@ +#include "stdafx.h" +#include "PHFracture.h" +#include "Physics.h" +#include "PHElement.h" +#include "PHShell.h" +#include "../Include/xrRender/Kinematics.h" + +extern class CPHWorld *ph_world; + +static const float torque_factor=10000000.f; +CPHFracturesHolder::CPHFracturesHolder() +{ + m_has_breaks=false; +} +CPHFracturesHolder::~CPHFracturesHolder() +{ + m_has_breaks=false; + m_fractures.clear(); + m_impacts.clear(); + m_feedbacks.clear(); +} +void CPHFracturesHolder::ApplyImpactsToElement(CPHElement* E) +{ + PH_IMPACT_I i=m_impacts.begin(),e=m_impacts.end(); + BOOL ac_state=E->isActive(); + //E->bActive=true; + E->m_flags.set(CPHElement::flActive,TRUE); + for(;e!=i;++i) + { + E->applyImpact(*i); + } + //E->bActive=ac_state; + E->m_flags.set(CPHElement::flActive,ac_state); +} +element_fracture CPHFracturesHolder::SplitFromEnd(CPHElement* element,u16 fracture) +{ + FRACTURE_I fract_i =m_fractures.begin()+fracture; + u16 geom_num =fract_i->m_start_geom_num; + u16 end_geom_num =fract_i->m_end_geom_num; + SubFractureMass (fracture); + + CPHElement* new_element =cast_PHElement(P_create_Element()); + new_element->m_SelfID=fract_i->m_bone_id; + new_element->mXFORM.set(element->mXFORM); + element->PassEndGeoms(geom_num,end_geom_num,new_element); + ///////////////////////////////////////////// + IKinematics* pKinematics= element->m_shell->PKinematics(); + + Fmatrix shift_pivot; + shift_pivot.set (pKinematics->LL_GetTransform(new_element->m_SelfID)); + shift_pivot.invert (); + shift_pivot.mulB_43 (pKinematics->LL_GetTransform(element->m_SelfID)); + ///////////////////////////////////////////// + float density=element->getDensity(); + new_element->SetShell(element->PHShell()); + Fmatrix current_transtform; + element->GetGlobalTransformDynamic(¤t_transtform); + InitNewElement(new_element,shift_pivot,density); + Fmatrix shell_form; + element->PHShell()->GetGlobalTransformDynamic(&shell_form); + current_transtform.mulA_43 (shell_form); + new_element->SetTransform (current_transtform); + + + //dBodyID new_element_body=new_element->get_body(); + //dBodyAddForce(new_element_body,fract_i->m_pos_in_element[0], + // fract_i->m_pos_in_element[1], + // fract_i->m_pos_in_element[2]); + ApplyImpactsToElement(new_element); + + //dBodyAddTorque(new_element->get_body(),fract_i->m_break_force, + // fract_i->m_break_torque, + // fract_i->m_add_torque_z); + //BodyCutForce(new_element_body,default_l_limit,default_w_limit); + element_fracture ret =mk_pair(new_element,(CShellSplitInfo)(*fract_i)); + + if(m_fractures.size()-fracture>0) + { + if(new_element->m_fratures_holder==NULL)//create fractures holder if it was not created before + { + new_element->m_fratures_holder=new CPHFracturesHolder(); + } + PassEndFractures(fracture,new_element); + } + + return ret; +} + +void CPHFracturesHolder::PassEndFractures(u16 from,CPHElement* dest) +{ + FRACTURE_I i=m_fractures.begin(),i_from=m_fractures.begin()+from,e=m_fractures.end(); + u16 end_geom=i_from->m_end_geom_num; + u16 begin_geom_num=i_from->m_start_geom_num; + u16 leaved_geoms=begin_geom_num; + u16 passed_geoms=end_geom-begin_geom_num; + if(i_from==e) return; + + for(;i!=i_from;++i)//correct end geoms for fractures leaved in source + { + u16& cur_end_geom=i->m_end_geom_num; + if(cur_end_geom>begin_geom_num) cur_end_geom=cur_end_geom-passed_geoms; + } + + i++; // omit used fracture; + //these to be passed + for(;i!=e;i++)//itterate antil a fracture where geom num > end geom num + { + u16 &cur_end_geom =i->m_end_geom_num; + u16 &cur_geom =i->m_start_geom_num; + if(cur_geom>=end_geom) break; + cur_end_geom=cur_end_geom-leaved_geoms; + cur_geom=cur_geom-leaved_geoms; + } + FRACTURE_I i_to=i; + for(;i!=e;++i)//correct data in the rest leaved fractures + { + u16 &cur_end_geom =i->m_end_geom_num; + u16 &cur_geom =i->m_start_geom_num; + cur_end_geom =cur_end_geom-passed_geoms; + cur_geom =cur_geom-passed_geoms; + } + + if(i_from + 1 != i_to)//insure it!! + { + + CPHFracturesHolder* &dest_fract_holder=dest->m_fratures_holder; + if(!dest_fract_holder) dest_fract_holder=new CPHFracturesHolder(); + //pass fractures not including end fracture + dest_fract_holder->m_fractures.insert(dest_fract_holder->m_fractures.end(),i_from+1,i_to); + + //u16 deb=u16(i_to-i_from-1); + //deb++;deb--; + } + m_fractures.erase(i_from,i_to);//erase along whith used fracture +} +void CPHFracturesHolder::SplitProcess(CPHElement* element,ELEMENT_PAIR_VECTOR &new_elements) +{ + //FRACTURE_RI i=m_fractures.rbegin(),e=m_fractures.rend();//reversed + u16 i=u16(m_fractures.size()-1); + + for(;i!=u16(-1);i--) + { + if(m_fractures[i].Breaked()) + { + //float density = element->getDensity(); + new_elements.push_back(SplitFromEnd(element,i)); + //element->ResetMass(density); + } + } + + +} + +void CPHFracturesHolder::InitNewElement(CPHElement* element,const Fmatrix &shift_pivot,float density) +{ +element->CreateSimulBase(); +element->ReInitDynamics(shift_pivot,density); +VERIFY(dBodyStateValide(element->get_body())); +} + +void CPHFracturesHolder::PhTune(dBodyID body) +{ + //iterate through all body's joints and set joints feedbacks where is not already set + //contact feedbacks stored in global storage - ContactFeedBacks wich cleared on each step + //breacable joints already has their feedbacks, + //feedbacks for rest noncontact joints stored in m_feedbacks in runtime in this function and + //and killed by destructor + + //int dBodyGetNumJoints (dBodyID b); + //dJointID dBodyGetJoint (dBodyID, int index); + //dJointGetType + //dJointTypeContact + + int num=dBodyGetNumJoints(body); + for(int i=0;iJointDestroyInfo())) dJointSetFeedback(joint,ContactFeedBacks.add()); + //if(!dJointGetFeedback(joint)) + //{ + // m_feedbacks.push_back(dJointFeedback()); + // dJointSetFeedback(joint,&m_feedbacks.back()); + //} + } + } + +} +bool CPHFracturesHolder::PhDataUpdate(CPHElement* element) +{ + FRACTURE_I i=m_fractures.begin(),e=m_fractures.end(); + for(;i!=e;++i) + { + m_has_breaks=i->Update(element)||m_has_breaks; + } + if(!m_has_breaks)m_impacts.clear(); + return m_has_breaks; + +} + +void CPHFracturesHolder::AddImpact(const Fvector& force,const Fvector& point,u16 id) +{ + m_impacts.push_back(SPHImpact(force,point,id)); +} +u16 CPHFracturesHolder::AddFracture(const CPHFracture& fracture) +{ + m_fractures.push_back(fracture); + return u16(m_fractures.size()-1); +} +CPHFracture& CPHFracturesHolder::Fracture(u16 num) +{ + R_ASSERT2(numm_start_geom_num,"fracture does not initialized!"); + + if(f_i->m_end_geom_num==u16(-1))f_i->MassAddToSecond(m) ; + else f_i->MassAddToFirst(m) ; + + + + //f_i->MassAddToFirst(m); + } +} +void CPHFracturesHolder::SubFractureMass(u16 fracture_num) +{ + FRACTURE_I f_i=m_fractures.begin(),f_e=m_fractures.end(); + FRACTURE_I fracture=f_i+fracture_num; + u16 start_geom=fracture->m_start_geom_num; + u16 end_geom =fracture->m_end_geom_num; + dMass& second_mass=fracture->m_secondM; + dMass& first_mass=fracture->m_firstM; + for(;f_i!=f_e;++f_i) + { + if(f_i==fracture) continue; + R_ASSERT2(start_geom!=f_i->m_start_geom_num,"Double fracture!!!"); + + + if(start_geom>f_i->m_start_geom_num) + { + + if(end_geom<=f_i->m_end_geom_num) f_i->MassSubFromSecond(second_mass);//tag fracture is in current + else + { + R_ASSERT2(start_geom>=f_i->m_end_geom_num,"Odd fracture!!!"); + f_i->MassSubFromFirst(second_mass);//tag fracture is ouside current + } + } + else + { + + if(end_geom>=f_i->m_end_geom_num) f_i->MassSubFromFirst(first_mass);//current fracture is in tag + else + { + R_ASSERT2(end_geom<=f_i->m_start_geom_num,"Odd fracture!!!"); + f_i->MassSubFromFirst(second_mass);//tag fracture is ouside current + } + } + } +} + +CPHFracture::CPHFracture() +{ +//m_bone_id=bone_id; +//m_position.set(position); +//m_direction.set(direction); +//m_break_force=break_force; +//m_break_torque=break_torque; +m_start_geom_num=u16(-1); +m_end_geom_num =u16(-1); +m_breaked=false; +} + +//#define DBG_BREAK +bool CPHFracture::Update(CPHElement* element) +{ + + ////itterate through impacts & calculate + dBodyID body=element->get_body(); + //const Fvector& v_bodyvel=*((Fvector*)dBodyGetLinearVel(body)); + CPHFracturesHolder* holder=element->FracturesHolder(); + PH_IMPACT_STORAGE& impacts=holder->Impacts(); + + Fvector second_part_force,first_part_force,second_part_torque,first_part_torque; + second_part_force.set(0.f,0.f,0.f); + first_part_force.set(0.f,0.f,0.f); + second_part_torque.set(0.f,0.f,0.f); + first_part_torque.set(0.f,0.f,0.f); + + //const Fvector& body_local_pos=element->local_mass_Center(); + const Fvector& body_global_pos=*(const Fvector*)dBodyGetPosition(body); + Fvector body_to_first, body_to_second; + body_to_first.set(*((const Fvector*)m_firstM.c));//,body_local_pos + body_to_second.set(*((const Fvector*)m_secondM.c));//,body_local_pos + //float body_to_first_smag=body_to_first.square_magnitude(); + //float body_to_second_smag=body_to_second.square_magnitude(); + int num=dBodyGetNumJoints(body); + for(int i=0;inode[1].body==body); + Fvector joint_position; + if(dJointGetType(joint)==dJointTypeContact) + { + dxJointContact* c_joint=(dxJointContact*)joint; + dGeomID first_geom=c_joint->contact.geom.g1; + dGeomID second_geom=c_joint->contact.geom.g2; + joint_position.set(*(Fvector*)c_joint->contact.geom.pos); + if(dGeomGetClass(first_geom)==dGeomTransformClass) + { + first_geom=dGeomTransformGetGeom(first_geom); + } + if(dGeomGetClass(second_geom)==dGeomTransformClass) + { + second_geom=dGeomTransformGetGeom(second_geom); + } + dxGeomUserData* UserData; + UserData=dGeomGetUserData(first_geom); + if(UserData) + { + u16 el_position=UserData->element_position; + //define if the contact applied to second part; + if(el_positionnumberOfGeoms()&& + el_position>=m_start_geom_num&& + el_positionGeom(el_position)->geometry() + ) applied_to_second=true; + } + UserData=dGeomGetUserData(second_geom); + if(UserData) + { + u16 el_position=UserData->element_position; + if(el_positionnumberOfGeoms()&& + el_position>=m_start_geom_num&& + el_positionGeom(el_position)->geometry() + ) applied_to_second=true; + } + + } + else + { + CPHJoint* J = (CPHJoint*) dJointGetData(joint); + if(!J)continue;//hack.. + J->PSecondElement()->InterpolateGlobalPosition(&joint_position); + CODEGeom* root_geom=J->RootGeom(); + if(root_geom) + { + u16 el_position=root_geom->element_position(); + if(element==J->PFirst_element()&& + el_positionnumberOfGeoms()&& + el_position>=m_start_geom_num&& + el_positionf2); + second_part_force.add(joint_force); + Fvector torque; + torque.crossproduct(shoulder,joint_force); + second_part_torque.add(torque); + + } + else + { + + Fvector joint_force; + joint_force.set(*(const Fvector*)feedback->f1); + second_part_force.add(joint_force); + + Fvector torque; + torque.crossproduct(shoulder,joint_force); + second_part_torque.add(torque); + } + } + else + { + Fvector shoulder; + shoulder.sub(body_to_joint,body_to_first); + if(b_body_second) + { + + Fvector joint_force; + joint_force.set(*(const Fvector*)feedback->f2); + first_part_force.add(joint_force); + Fvector torque; + torque.crossproduct(shoulder,joint_force); + first_part_torque.add(torque); + } + else + { + Fvector joint_force; + joint_force.set(*(const Fvector*)feedback->f1); + first_part_force.add(joint_force); + Fvector torque; + torque.crossproduct(shoulder,joint_force); + first_part_torque.add(torque); + } + } + + } + + PH_IMPACT_I i_i=impacts.begin(),i_e=impacts.end(); + for(;i_i!=i_e;i_i++) + { + u16 geom = i_i->geom; + + if((geom>=m_start_geom_num&&geomforce); + force.mul(phRigidBreakWeaponFactor); + Fvector second_to_point; + second_to_point.sub(body_to_second,i_i->point); + //force.mul(30.f); + second_part_force.add(force); + Fvector torque; + torque.crossproduct(second_to_point,force); + second_part_torque.add(torque); + } + else + { + Fvector force; + force.set(i_i->force); + Fvector first_to_point; + first_to_point.sub(body_to_first,i_i->point); + //force.mul(4.f); + first_part_force.add(force); + Fvector torque; + torque.crossproduct(first_to_point,force); + second_part_torque.add(torque); + } + } + Fvector gravity_force; + gravity_force.set(0.f,-ph_world->Gravity()*m_firstM.mass,0.f); + first_part_force.add(gravity_force); + second_part_force.add(gravity_force); + dMatrix3 glI1,glI2,glInvI,tmp; + + // compute inertia tensors in global frame + dMULTIPLY2_333 (tmp,body->invI,body->R); + dMULTIPLY0_333 (glInvI,body->R,tmp); + + dMULTIPLY2_333 (tmp,m_firstM.I,body->R); + dMULTIPLY0_333 (glI1,body->R,tmp); + + dMULTIPLY2_333 (tmp,m_secondM.I,body->R); + dMULTIPLY0_333 (glI2,body->R,tmp); + //both parts have eqiual start angular vel same as have body so we ignore it + + //compute breaking torque + ///break_torque=glI2*glInvI*first_part_torque-glI1*glInvI*second_part_torque+crossproduct(second_in_bone,second_part_force)-crossproduct(first_in_bone,first_part_force) + Fvector break_torque,vtemp; + + dMULTIPLY0_331 ((float*)&break_torque,glInvI,(float*)&first_part_torque); + dMULTIPLY0_331 ((float*)&break_torque,glI2,(float*)&break_torque); + + dMULTIPLY0_331 ((float*)&vtemp,glInvI,(float*)&second_part_torque); + dMULTIPLY0_331 ((float*)&vtemp,glI1,(float*)&vtemp); + break_torque.sub(vtemp); + + //Fvector first_in_bone,second_in_bone; + //first_in_bone.sub(*((const Fvector*)m_firstM.c),m_pos_in_element); + //second_in_bone.sub(*((const Fvector*)m_secondM.c),m_pos_in_element); + + //vtemp.crossproduct(second_in_bone,second_part_force); + //break_torque.add(vtemp); + //vtemp.crossproduct(first_in_bone,first_part_force); + //break_torque.sub(vtemp); +#ifdef DBG_BREAK + float btm_dbg=break_torque.magnitude()*phBreakCommonFactor/torque_factor; +#endif + if(break_torque.magnitude()*phBreakCommonFactor>m_break_torque*torque_factor) + { + //m_break_torque.set(second_part_torque); + m_pos_in_element.set(second_part_force); + m_break_force=second_part_torque.x; + m_break_torque=second_part_torque.y; + m_add_torque_z=second_part_torque.z; + m_breaked=true; +#ifndef DBG_BREAK + return m_breaked; +#endif + } + + Fvector break_force;//=1/(m1+m2)*(F1*m2-F2*m1)+r2xT2/(r2^2)-r1xT1/(r1^2) + break_force.set(first_part_force); + break_force.mul(m_secondM.mass); + vtemp.set(second_part_force); + vtemp.mul(m_firstM.mass); + break_force.sub(vtemp); + break_force.mul(1.f/element->getMass());//element->getMass()//body->mass.mass + + //vtemp.crossproduct(second_in_bone,second_part_torque); + //break_force.add(vtemp); + //vtemp.crossproduct(first_in_bone,first_part_torque); + //break_force.sub(vtemp); + + float bfm=break_force.magnitude()*phBreakCommonFactor; + + if(m_break_force shell_root; +typedef std::pair element_fracture; +DEFINE_VECTOR(CPHElement*,ELEMENT_STORAGE,ELEMENT_I) +DEFINE_VECTOR(CPHJoint*,JOINT_STORAGE,JOINT_I) +DEFINE_VECTOR(shell_root,PHSHELL_PAIR_VECTOR,SHELL_PAIR_I) +typedef xr_vector::reverse_iterator SHELL_PAIR_RI; +DEFINE_VECTOR(element_fracture,ELEMENT_PAIR_VECTOR,ELEMENT_PAIR_I) +typedef xr_vector::reverse_iterator ELEMENT_RI; +typedef xr_vector::reverse_iterator ELEMENT_PAIR_RI; +DEFINE_VECTOR(CPHFracture,FRACTURE_STORAGE,FRACTURE_I) +typedef xr_vector::reverse_iterator FRACTURE_RI; + +#endif PH_FRACTURE_H \ No newline at end of file diff --git a/src/xrGameLA/PHGeometryOwner.cpp b/src/xrGameLA/PHGeometryOwner.cpp new file mode 100644 index 000000000..5d718a3a8 --- /dev/null +++ b/src/xrGameLA/PHGeometryOwner.cpp @@ -0,0 +1,406 @@ +#include "stdafx.h" +#include "PHGeometryOwner.h" +#include "phworld.h" +#include "../Include/xrRender/Kinematics.h" +#include "../bone.h" + +CPHGeometryOwner::CPHGeometryOwner() +{ + b_builded=false; + m_mass_center.set(0,0,0); + contact_callback=ContactShotMark; + object_contact_callback=NULL; + ul_material=GMLib.GetMaterialIdx("objects\\small_box"); + m_group=NULL; + m_phys_ref_object=NULL; +} + +CPHGeometryOwner::~CPHGeometryOwner() +{ + GEOM_I i_geom=m_geoms.begin(),e=m_geoms.end(); + for(;i_geom!=e;++i_geom)xr_delete(*i_geom); + m_geoms.clear(); +} +void CPHGeometryOwner:: build_Geom (CODEGeom& geom) +{ + + geom.build(m_mass_center); + //geom.set_body(m_body); + geom.set_material(ul_material); + if(contact_callback)geom.set_contact_cb(contact_callback); + if(object_contact_callback)geom.set_obj_contact_cb(object_contact_callback); + if(m_phys_ref_object) geom.set_ref_object(m_phys_ref_object); + if(m_group) + { + geom.add_to_space((dSpaceID)m_group); + } +} + +void CPHGeometryOwner::build_Geom(u16 i) +{ + CODEGeom& geom=*m_geoms[i]; + build_Geom(geom); + geom.element_position()=i; +} + +void CPHGeometryOwner::build() +{ + if(b_builded) return; + if(m_geoms.size()>1) + { + m_group=dSimpleSpaceCreate(0); + dSpaceSetCleanup(m_group,0); + } + u16 geoms_size=u16(m_geoms.size()); + for(u16 i=0;idestroy(); + } + b_builded=false; +} +void CPHGeometryOwner::set_body(dBodyID body) +{ + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) (*i)->set_body(body); +} + +Fvector CPHGeometryOwner:: get_mc_data (){ + Fvector s; + float pv; + m_mass_center.set(0,0,0); + m_volume=0.f; + GEOM_I i_geom=m_geoms.begin(),e=m_geoms.end(); + for(;i_geom!=e;++i_geom) + { + pv=(*i_geom)->volume(); + s.mul((*i_geom)->local_center(),pv); + m_volume+=pv; + m_mass_center.add(s); + } + m_mass_center.mul(1.f/m_volume); + return m_mass_center; +} + +Fvector CPHGeometryOwner:: get_mc_geoms (){ + ////////////////////to be implemented + Fvector mc; + mc.set(0.f,0.f,0.f); + return mc; +} +void CPHGeometryOwner::get_mc_kinematics(IKinematics* K,Fvector& mc,float& mass) +{ + + mc.set(0.f,0.f,0.f); + mass=0.f; + m_volume=0.f; + GEOM_I i_geom=m_geoms.begin(),e=m_geoms.end(); + for(;i_geom!=e;++i_geom) + { + const IBoneData& data=K->GetBoneData((*i_geom)->bone_id()); + Fvector add; + mass+=data.get_mass(); + m_volume+=(*i_geom)->volume(); + add.set(data.get_center_of_mass()); + add.mul(data.get_mass()); + mc.add(add); + } + mc.mul(1.f/mass); +} +void CPHGeometryOwner:: calc_volume_data () +{ + m_volume=0.f; + GEOM_I i_geom=m_geoms.begin(),e=m_geoms.end(); + for(;i_geom!=e;++i_geom) + { + m_volume+=(*i_geom)->volume(); + } +} + +void CPHGeometryOwner::SetMaterial(u16 m) +{ + ul_material=m; + if(!b_builded) return; + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) (*i)->set_material(m); +} + +void CPHGeometryOwner::SetPhObjectInGeomData(CPHObject* O) +{ + if(!b_builded) return; + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) (*i)->set_ph_object(O); +} + +dGeomID CPHGeometryOwner::dSpacedGeometry() +{ + if(!b_builded) return 0; + if(m_group) return (dGeomID)m_group; + else return (*m_geoms.begin())->geometry_transform(); +} + +void CPHGeometryOwner:: add_Box (const Fobb& V) +{ + Fobb box; + box=V; + if(box.m_halfsize.x<0.005f) box.m_halfsize.x=0.005f; + if(box.m_halfsize.y<0.005f) box.m_halfsize.y=0.005f; + if(box.m_halfsize.z<0.005f) box.m_halfsize.z=0.005f; + m_geoms.push_back(smart_cast(new CBoxGeom(box))); + +} + +void CPHGeometryOwner:: add_Sphere (const Fsphere& V) +{ + m_geoms.push_back(smart_cast(new CSphereGeom(V))); +} + +void CPHGeometryOwner::add_Cylinder (const Fcylinder& V) +{ + m_geoms.push_back(smart_cast(new CCylinderGeom(V))); +} + + +void CPHGeometryOwner::add_Shape(const SBoneShape& shape,const Fmatrix& offset) +{ + switch(shape.type) { + case SBoneShape::stBox : + { + Fobb box=shape.box; + Fmatrix m; + m.set(offset); + //Fmatrix position; + //position.set(box.m_rotate); + //position.c.set(box.m_translate); + //position.mulA(offset); + //box.m_rotate.set(position); + //box.m_translate.set(position.c); + box.transform(box,m); + add_Box(box); + break; + } + case SBoneShape::stSphere : + { + Fsphere sphere=shape.sphere; + offset.transform_tiny(sphere.P); + add_Sphere(sphere); + break; + } + + + case SBoneShape::stCylinder : + { + Fcylinder C=shape.cylinder; + offset.transform_tiny(C.m_center); + offset.transform_dir(C.m_direction); + add_Cylinder(C); + break; + } + + + case SBoneShape::stNone : + break; + default: NODEFAULT; + } +} + +void CPHGeometryOwner::add_Shape(const SBoneShape& shape) +{ + switch(shape.type) { + case SBoneShape::stBox : + { + add_Box(shape.box); + break; + } + case SBoneShape::stSphere : + { + add_Sphere(shape.sphere); + break; + } + + + case SBoneShape::stCylinder : + { + add_Cylinder(shape.cylinder); + break; + } + + case SBoneShape::stNone : + break; + default: NODEFAULT; + } +} + + +void CPHGeometryOwner::set_ContactCallback(ContactCallbackFun* callback) +{ + contact_callback=callback; + if(!b_builded)return; + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) (*i)->set_contact_cb(callback); +} + + +void CPHGeometryOwner::set_ObjectContactCallback(ObjectContactCallbackFun* callback) +{ + object_contact_callback= callback; + if(!b_builded)return; + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) (*i)->set_obj_contact_cb(callback); +} + +void CPHGeometryOwner::add_ObjectContactCallback(ObjectContactCallbackFun* callback) +{ + + if(!object_contact_callback) + { + object_contact_callback= callback; + } + if(!b_builded)return; + { + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) (*i)->add_obj_contact_cb(callback); + } +} + +void CPHGeometryOwner::remove_ObjectContactCallback(ObjectContactCallbackFun* callback) +{ + + if(object_contact_callback==callback) + { + object_contact_callback= NULL; + } + if(!b_builded)return; + { + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) (*i)->remove_obj_contact_cb(callback); + } +} + +ObjectContactCallbackFun* CPHGeometryOwner::get_ObjectContactCallback() +{ + return object_contact_callback; +} +void CPHGeometryOwner::set_CallbackData(void * cd) +{ + VERIFY(b_builded); + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) (*i)->set_callback_data(cd); +} +void* CPHGeometryOwner::get_CallbackData() +{ + VERIFY(b_builded); + return (*m_geoms.begin())->get_callback_data (); +} +void CPHGeometryOwner::set_PhysicsRefObject(CPhysicsShellHolder* ref_object) +{ + m_phys_ref_object=ref_object; + if(!b_builded) return; + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) (*i)->set_ref_object(ref_object); +} + +u16 CPHGeometryOwner::numberOfGeoms() +{ + return (u16)m_geoms.size(); +} + +void CPHGeometryOwner::get_Extensions(const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) +{ + lo_ext=dInfinity;hi_ext=-dInfinity; + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) + { + float temp_lo_ext,temp_hi_ext; + //GetTransformedGeometryExtensions((*i)->geometry_transform(),(float*)&axis,center_prg,&temp_lo_ext,&temp_hi_ext); + (*i)->get_extensions_bt(axis,center_prg,temp_lo_ext,temp_hi_ext); + if(lo_ext>temp_lo_ext)lo_ext=temp_lo_ext; + if(hi_extget_max_area_dir_bt(dir); +} +float CPHGeometryOwner::getRadius() +{ + if(!m_geoms.empty()) return m_geoms.back()->radius(); + else return 0.f; +} + +void CPHGeometryOwner::get_mc_vs_transform(Fvector& mc,const Fmatrix& m) +{ + mc.set(m_mass_center); + m.transform_tiny(mc); + VERIFY2(_valid(mc),"invalid mc in_set_transform"); +} + +void CPHGeometryOwner::setStaticForm(const Fmatrix& form) +{ + if(!b_builded) return; + Fmatrix f; + f.set(form); + get_mc_vs_transform(f.c,form); + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) (*i)->set_static_ref_form(f); +} + +void CPHGeometryOwner::setPosition(const Fvector& pos) +{ + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) + { + (*i)->set_position(pos); + } +} +void CPHGeometryOwner::CreateSimulBase() +{ + if(m_geoms.size()>1) + { + m_group=dSimpleSpaceCreate(0); + dSpaceSetCleanup(m_group,0); + } +} +struct SFindPred +{ + u16 m_val; + SFindPred(u16 val) + { + m_val=val; + } +bool operator () (CODEGeom* g) + { + return g->bone_id()==m_val; + } +}; +CODEGeom* CPHGeometryOwner::GeomByBoneID(u16 bone_id) +{ + + GEOM_I g=std::find_if(m_geoms.begin(),m_geoms.end(),SFindPred(bone_id)); + if(g!=m_geoms.end()) + { + return *g; + } + else + { + return NULL; + } +} + +void CPHGeometryOwner::clear_cashed_tries() +{ + GEOM_I i=m_geoms.begin(),e=m_geoms.end(); + for(;i!=e;++i) + { + (*i)->clear_cashed_tries(); + } +} \ No newline at end of file diff --git a/src/xrGameLA/PHGeometryOwner.h b/src/xrGameLA/PHGeometryOwner.h new file mode 100644 index 000000000..e9bc04731 --- /dev/null +++ b/src/xrGameLA/PHGeometryOwner.h @@ -0,0 +1,76 @@ +#ifndef PH_GEOMETRY_OWNER_H +#define PH_GEOMETRY_OWNER_H +#include "Geometry.h" +#include "../gamemtllib.h" + +DEFINE_VECTOR(CODEGeom*,GEOM_STORAGE,GEOM_I) + +struct SBoneShape; +class IKinematics; + +class CPHGeometryOwner +{ +protected: + GEOM_STORAGE m_geoms; //e + //bl + bool b_builded; + dSpaceID m_group; //e //bl + Fvector m_mass_center; //e ?? //bl + CPhysicsShellHolder* m_phys_ref_object; //->to shell ?? //bl + float m_volume; //e ?? //bl + u16 ul_material; //e ?? //bl + ContactCallbackFun* contact_callback; //->to shell ?? //bt + ObjectContactCallbackFun* object_contact_callback;//->to shell ?? //st +public: + /// + void add_Sphere (const Fsphere& V); //aux + void add_Box (const Fobb& V); //aux + void add_Cylinder (const Fcylinder& V); //aux + void add_Shape (const SBoneShape& shape); //aux + void add_Shape (const SBoneShape& shape,const Fmatrix& offset); //aux + CODEGeom* last_geom (){if(m_geoms.empty())return NULL; return m_geoms.back();} //aux + bool has_geoms (){return !m_geoms.empty();} + void set_ContactCallback (ContactCallbackFun* callback); //aux (may not be) + void set_ObjectContactCallback (ObjectContactCallbackFun* callback); //called anywhere ph state influent + void add_ObjectContactCallback (ObjectContactCallbackFun* callback); //called anywhere ph state influent + void remove_ObjectContactCallback (ObjectContactCallbackFun* callback); //called anywhere ph state influent + void set_CallbackData (void * cd); + void *get_CallbackData (); + ObjectContactCallbackFun *get_ObjectContactCallback (); + void set_PhysicsRefObject (CPhysicsShellHolder* ref_object); //aux + CPhysicsShellHolder* PhysicsRefObject (){return m_phys_ref_object;} //aux + void SetPhObjectInGeomData (CPHObject* O); + + void SetMaterial (u16 m) ; + void SetMaterial (LPCSTR m){SetMaterial(GMLib.GetMaterialIdx(m));} //aux + IC CODEGeom* Geom (u16 num) {R_ASSERT2 (numm_frame_time/fixed_step); + //else + //pos.lerp(*bkp_pos,bk_pos,ph_world->FrameTime(b_frame_mark)/fixed_step); +} + + + +void CPHInterpolation::InterpolateRotation(Fmatrix& rot){ + Fquaternion q; + float t = ph_world->m_frame_time/fixed_step; + VERIFY (t>=0.f && t<=1.f); + //if(!b_udating_rotations) + q.slerp(qRotations[0],qRotations[1],t); +// else + //q.slerp(*bkp_quat,bk_quat,t); + rot.rotation(q); +} + +void CPHInterpolation::ResetPositions() +{ + VERIFY2(dBodyStateValide(m_body),"Invalid body state"); + qPositions.fill_in(*((Fvector*) dBodyGetPosition(m_body))); +} + +void CPHInterpolation::ResetRotations() +{ + VERIFY2(dBodyStateValide(m_body),"Invalid body state"); + const dReal* dQ=dBodyGetQuaternion(m_body); + Fquaternion fQ; + fQ.set(-dQ[0],dQ[1],dQ[2],dQ[3]); + qRotations.fill_in(fQ); +} + +void CPHInterpolation::GetRotation(Fquaternion& q, u16 num) +{ + if(!m_body) return; + q.set(qRotations[num]); +} + +void CPHInterpolation::GetPosition(Fvector& p,u16 num) +{ + if(!m_body) return; + p.set(qPositions[num]); +} +void CPHInterpolation::SetPosition(const Fvector& p, u16 num) +{ + if(!m_body) return; + qPositions[num].set(p); +} + +void CPHInterpolation::SetRotation(const Fquaternion& q, u16 num) +{ + if(!m_body) return; + qRotations[num]=q; +} \ No newline at end of file diff --git a/src/xrGameLA/PHInterpolation.h b/src/xrGameLA/PHInterpolation.h new file mode 100644 index 000000000..990878234 --- /dev/null +++ b/src/xrGameLA/PHInterpolation.h @@ -0,0 +1,28 @@ +#include "CycleConstStorage.h" +#ifndef PHINTERPOLATON_H +#define PHINTERPOLATON_H + +#include "ode_include.h" + +class CPHInterpolation { + +public: +CPHInterpolation(); +void SetBody(dBodyID body); +static const u16 PH_INTERPOLATION_POINTS=2; +void InterpolatePosition (Fvector& pos); +void InterpolateRotation (Fmatrix& rot); +void UpdatePositions (); +void UpdateRotations (); +void ResetPositions (); +void ResetRotations (); +void GetRotation (Fquaternion& q, u16 num); +void GetPosition (Fvector& p, u16 num); +void SetRotation (const Fquaternion& q, u16 num); +void SetPosition (const Fvector& p, u16 num); +private: + dBodyID m_body; + CCycleConstStorage qPositions; + CCycleConstStorage qRotations; +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHIsland.cpp b/src/xrGameLA/PHIsland.cpp new file mode 100644 index 000000000..c86e0939d --- /dev/null +++ b/src/xrGameLA/PHIsland.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "PHIsland.h" +#include "physics.h" +void CPHIsland:: Step(dReal step) +{ + if(!m_flags.is_active()) return; + //dWorldStepFast1 (DWorld(), fixed_step, phIterations/*+Random.randI(0,phIterationCycle)*/); + if(m_flags.is_exact_integration_prefeared() && nj < max_joint_allowed_for_exeact_integration) + dWorldStep(DWorld(),fixed_step); + else + dWorldQuickStep (DWorld(), fixed_step); + //dWorldStep(DWorld(),fixed_step); +} + +void CPHIsland::Enable() +{ + if(!m_flags.is_active()) return; + + for (dxBody *body = DWorld()->firstbody; body; body = (dxBody *) body->next)body->flags &= ~dxBodyDisabled; + +} +void CPHIsland::Repair() +{ + if(!m_flags.is_active()) return; + dBodyID body; + for (body = firstbody; body; body = (dxBody *) body->next) + { + if(!dV_valid(dBodyGetAngularVel(body))) + dBodySetAngularVel(body,0.f,0.f,0.f); + if(!dV_valid(dBodyGetLinearVel(body))) + dBodySetLinearVel(body,0.f,0.f,0.f); + if(!dV_valid(dBodyGetPosition(body))) + dBodySetPosition(body,0.f,0.f,0.f); + if(!dQ_valid(dBodyGetQuaternion(body))) + { + dQuaternion q={1.f,0.f,0.f,0.f};//dQSetIdentity(q); + dBodySetQuaternion(body,q); + } + } +} \ No newline at end of file diff --git a/src/xrGameLA/PHIsland.h b/src/xrGameLA/PHIsland.h new file mode 100644 index 000000000..ab217ef31 --- /dev/null +++ b/src/xrGameLA/PHIsland.h @@ -0,0 +1,253 @@ +#ifndef PH_ISLAND_H +#define PH_ISLAND_H + +#pragma warning(disable:4995) +#pragma warning(disable:4267) +#include "../../xrODE/ode/src/objects.h" +#include "../../xrODE/ode/src/joint.h" +#pragma warning(default:4995) +#pragma warning(default:4267) +#include "PhysicsCommon.h" + + +class CPHIslandFlags +{ + static const int base = 8 ; + static const int shift_to_variable = base/2 ; + static const int mask_static = 0xf ; + Flags8 flags ; + + enum + { + stActive = 1<<0, + flPrefereExactIntegration = 1<<1 + }; +public: + + CPHIslandFlags () {init();} + + IC void init () {flags.zero();flags.set(stActive,TRUE);unmerge();} + IC BOOL is_active () {return flags.test(stActive<m_nb; +} +IC bool IsJointGroun() +{ + return nj>m_nj; +} +IC bool CheckSize() +{ + return njnj; +} + +IC int MaxBodies(CPHIsland* island) +{ + return BODIES_LIMIT-nb-island->nb; +} + +IC bool CanMerge(CPHIsland* island,int& MAX_JOINTS) +{ + MAX_JOINTS=MaxJoints(island); + return MAX_JOINTS>0 && ((nb+island->nb)m_flags.is_active()) m_self_active=m_self_active->m_self_active; +} +IC void Merge(CPHIsland* island) +{ + //VERIFY2(b_active&&island->b_active,"no active island"); + CPHIsland* first_island=DActiveIsland(); + CPHIsland* second_island=island->DActiveIsland(); + if(first_island==second_island)return; + + *(second_island->m_joints_tail)=first_island->firstjoint; + first_island->firstjoint=second_island->firstjoint; + if(0==first_island->nj&&0!=second_island->nj) + { + first_island->m_joints_tail=second_island->m_joints_tail; + } + + *(second_island->m_bodies_tail)=first_island->firstbody; + first_island->firstbody=second_island->firstbody; + + first_island->nj+=second_island->nj; + first_island->nb+=second_island->nb; + VERIFY(!(*(first_island->m_bodies_tail))); + VERIFY(!(*(first_island->m_joints_tail))); + VERIFY(!((!(first_island->nj))&&(first_island->firstjoint))); + second_island->m_self_active=first_island; + //second_island->b_active=false; + m_flags.merge(second_island->m_flags); +} +IC void Unmerge() +{ + firstjoint=m_first_joint; + firstbody=m_first_body; + if(!m_nj) + { + m_joints_tail=&firstjoint; + *m_joints_tail=0; + } + else + { + firstjoint->tome=(dObject**)&firstjoint; + } + *m_joints_tail=0; + *m_bodies_tail=0; + //b_active=true; + m_flags.unmerge(); + m_self_active=this; + nj=m_nj; + nb=m_nb; + +} +IC void Init() +{ + //b_active=true; + m_flags.init(); + m_nj=nj=0; + m_nb=nb=0; + m_first_joint=firstjoint=0; + m_first_body=firstbody=0; + m_joints_tail=&firstjoint; + m_bodies_tail=&firstbody; + m_self_active=this; +} +IC void AddBody(dxBody* body) +{ + VERIFY2(m_nj==nj&&m_nb==nb&& m_flags.is_active(),"can not remove/add during processing phase"); + dWorldAddBody(DWorld(),body); + m_first_body=body; + if(m_nb==0) + { + m_bodies_tail=(dxBody**)&body->next; + } + m_nb++; +} +IC void RemoveBody(dxBody* body) +{ + VERIFY2(m_nj==nj&&m_nb==nb && m_flags.is_active() ,"can not remove/add during processing phase"); + if(m_first_body==body)m_first_body=(dxBody*)body->next; + if(m_bodies_tail==(dxBody**)(&(body->next))) + { + m_bodies_tail=(dxBody**)body->tome; + } + dWorldRemoveBody((dxWorld*)this,body); + m_nb--; +} +IC void AddJoint(dxJoint* joint) +{ + VERIFY2(m_nj==nj&&m_nb==nb&&m_flags.is_active(),"can not remove/add during processing phase"); + dWorldAddJoint(DWorld(),joint); + m_first_joint=joint; + if(!m_nj) + { + VERIFY(joint->next==0); + m_joints_tail=(dxJoint**)(&(joint->next)); + + } + m_nj++; +} + +IC void ConnectJoint(dxJoint* joint) +{ + if(!nj) + { + m_joints_tail=(dxJoint**)(&(joint->next)); + VERIFY(!firstjoint); + } + dWorldAddJoint(DWorld(),joint); + VERIFY(!(*(m_joints_tail))); +} + +IC void DisconnectJoint(dxJoint* joint) +{ + dWorldRemoveJoint(DWorld(),joint); +} + +IC void ConnectBody(dxBody* body) +{ + dWorldAddBody(DWorld(),body); +} +IC void DisconnectBody(dxBody* body) +{ + dWorldRemoveBody(DWorld(),body); +} +IC void RemoveJoint(dxJoint* joint) +{ + VERIFY2(m_nj==nj&&m_nb==nb&&m_flags.is_active(),"can not remove/add during processing phase"); + if(m_first_joint==joint) + m_first_joint=(dxJoint*)joint->next; + if(m_joints_tail==(dxJoint**)(&(joint->next))) + { + m_joints_tail=(dxJoint**)joint->tome; + } + dWorldRemoveJoint(DWorld(),joint); + VERIFY(!*(m_joints_tail)); + m_nj--; +} +void SetPrefereExactIntegration(){m_flags.set_prefere_exact_integration();} +void Step(dReal step); +void Enable(); +void Repair(); +protected: +private: +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHItemList.h b/src/xrGameLA/PHItemList.h new file mode 100644 index 000000000..c598ccca3 --- /dev/null +++ b/src/xrGameLA/PHItemList.h @@ -0,0 +1,103 @@ +#ifndef PH_ITEM_LIST_H +#define PH_ITEM_LIST_H +/* +#define DECLARE_PHLIST_ITEM(class_name) public:\ + class CPHListItem\ + {\ + friend class CPHItemList;\ + friend class CPHItemList::iterator;\ + class_name* next;\ + class_name** tome;\ + };\ + private: +*/ +#define DECLARE_PHLIST_ITEM(class_name) friend class CPHItemList;\ + friend class CPHItemList::iterator;\ + class_name* next;\ + class_name** tome; +#define DECLARE_PHSTACK_ITEM(class_name) DECLARE_PHLIST_ITEM(class_name)\ + friend class CPHItemStack;\ + u16 stack_pos; + +//#define TPI(item) ((T::CPHListItem*)item) + +template + class CPHItemList + { + T *first_next ; + T **last_tome ; + protected: + u16 size ; + + public: + + class iterator; + typedef class iterator + { + + T *my_ptr; + public: + iterator(){my_ptr=0;} + iterator(T* i){ my_ptr=i; } + iterator operator ++ () {return my_ptr=((my_ptr)->next);} + T* operator * () {return my_ptr;} + bool operator != (iterator right){return my_ptr!=right.my_ptr;} + }; + CPHItemList () { empty();} + u16 count () {return size;} + void push_back (T* item) + { + *(last_tome)=item; + item->tome= last_tome; + last_tome=&((item)->next); + item->next=0; + size++; + } + void move_items (CPHItemList& sourse_list) + { + if(!sourse_list.first_next) return; + *(last_tome)=sourse_list.first_next; + sourse_list.first_next->tome= last_tome; + last_tome=sourse_list.last_tome; + size=size+sourse_list.size; + sourse_list.empty(); + } + void erase (iterator i) + { + T* item=*i; + T* next=item->next; + *(item->tome)=next; + if(next)next->tome=item->tome; + else last_tome=item->tome; + size--; + } + void empty () + { + last_tome=&first_next; + first_next=0; + size=0; + } + iterator begin() + { + return iterator(first_next); + } + iterator end() + { + return iterator(0); + } + }; + +template + class CPHItemStack : + public CPHItemList + { + public: + void push_back (T* item) + { + item->stack_pos=size; + CPHItemList::push_back(item); + } + }; +#define DEFINE_PHITEM_LIST(T,N,I) typedef CPHItemList N; typedef CPHItemList::iterator I; +#define DEFINE_PHITEM_STACK(T,N,I) typedef CPHItemStack N; typedef CPHItemStack::iterator I; +#endif diff --git a/src/xrGameLA/PHJoint.cpp b/src/xrGameLA/PHJoint.cpp new file mode 100644 index 000000000..b58f8b888 --- /dev/null +++ b/src/xrGameLA/PHJoint.cpp @@ -0,0 +1,1281 @@ +#include "StdAfx.h" +#include "PHDynamicData.h" +#include "Physics.h" +#include "tri-colliderknoopc/dTriList.h" +#include "PHJointDestroyInfo.h" +/////////////////////////////////////////////////////////////// +///#pragma warning(disable:4995) +////#include "../ode/src/collision_kernel.h" +//#include <../ode/src/joint.h> +//#include <../ode/src/objects.h> + +//#pragma warning(default:4995) +/////////////////////////////////////////////////////////////////// + +#include "ExtendedGeom.h" + +#include "PHElement.h" +#include "PHJoint.h" +#include "PHShell.h" + +const float hinge2_spring=20000.f; +const float hinge2_damping=1000.f; + +IC dBodyID body_for_joint(CPhysicsElement* e) +{ + return e->isFixed() ? 0 : e->get_body();//return e->get_body();// +} +IC void SwapLimits(float &lo,float &hi) +{ + float t=-lo; + lo=-hi; + hi=t; +} +CPHJoint::~CPHJoint(){ + xr_delete(m_destroy_info); + VERIFY(!bActive); + axes.clear(); + if(m_back_ref)*m_back_ref=NULL; +}; + +void CPHJoint::SetBackRef(CPhysicsJoint** j) +{ + R_ASSERT2(*j==static_cast(this),"wronng reference"); + m_back_ref=j; +} +void CPHJoint::CreateBall() +{ + + m_joint=dJointCreateBall(0,0); + Fvector pos; + Fmatrix first_matrix,second_matrix; + CPHElement* first=(pFirst_element); + CPHElement* second=(pSecond_element); + + VERIFY(first&&second); + first->GetGlobalTransformDynamic(&first_matrix); + second->GetGlobalTransformDynamic(&second_matrix); +pos.set(0,0,0); + switch(vs_anchor){ +case vs_first :first_matrix.transform_tiny(pos,anchor); break; +case vs_second:second_matrix.transform_tiny(pos,anchor); break; +case vs_global:pShell->mXFORM.transform_tiny(pos,anchor);break; +default:NODEFAULT; + } + + + + dJointAttach(m_joint,body_for_joint(first),body_for_joint(second)); + dJointSetBallAnchor(m_joint,pos.x,pos.y,pos.z); + +} + + + +void CPHJoint::CreateHinge() +{ + + m_joint=dJointCreateHinge(0,0); + + Fvector pos; + Fmatrix first_matrix,second_matrix; + Fvector axis; + + + CPHElement* first=(pFirst_element); + CPHElement* second=(pSecond_element); + VERIFY(first&&second); + first->GetGlobalTransformDynamic(&first_matrix); + second->GetGlobalTransformDynamic(&second_matrix); + +pos.set(0,0,0); +switch(vs_anchor) +{ +case vs_first :first_matrix.transform_tiny(pos,anchor); break; +case vs_second:second_matrix.transform_tiny(pos,anchor); break; +case vs_global:pShell->mXFORM.transform_tiny(pos,anchor);break; +default:NODEFAULT; +} + + + axis.set(0,0,0); + + first_matrix.invert(); + + Fmatrix rotate; + rotate.mul(first_matrix,second_matrix); + + float hi,lo; + CalcAxis(0,axis,lo,hi,first_matrix,second_matrix,rotate); + dBodyID b1=body_for_joint(first);if(!b1)axis.invert();//SwapLimits(lo,hi); + dJointAttach(m_joint,b1,body_for_joint(second)); + + dJointSetHingeAnchor(m_joint,pos.x,pos.y,pos.z); + dJointSetHingeAxis(m_joint,axis.x,axis.y,axis.z); + + dJointSetHingeParam(m_joint,dParamLoStop ,lo); + dJointSetHingeParam(m_joint,dParamHiStop ,hi); + if(axes[0].force>0.f){ + dJointSetHingeParam(m_joint,dParamFMax ,axes[0].force); + dJointSetHingeParam(m_joint,dParamVel ,axes[0].velocity); + } + dJointSetHingeParam(m_joint,dParamStopERP ,axes[0].erp); + dJointSetHingeParam(m_joint,dParamStopCFM ,axes[0].cfm); + + dJointSetHingeParam(m_joint,dParamCFM ,m_cfm); +} + + +void CPHJoint::CreateHinge2() +{ + + m_joint=dJointCreateHinge2(0,0); + + Fvector pos; + Fmatrix first_matrix,second_matrix; + Fvector axis; + CPHElement* first=(pFirst_element); + CPHElement* second=(pSecond_element); + VERIFY(first&&second); + first->GetGlobalTransformDynamic(&first_matrix); + second->GetGlobalTransformDynamic(&second_matrix); + pos.set(0,0,0); + switch(vs_anchor) + { + case vs_first :first_matrix.transform_tiny(pos,anchor); break; + case vs_second:second_matrix.transform_tiny(pos,anchor); break; + case vs_global:pShell->mXFORM.transform_tiny(pos,anchor);break; + default:NODEFAULT; + } + ////////////////////////////////////// + + dBodyID b1=body_for_joint(first); + dJointAttach(m_joint,b1,body_for_joint(second)); + dJointSetHinge2Anchor(m_joint,pos.x,pos.y,pos.z); + + ///////////////////////////////////////////// + + Fmatrix first_matrix_inv; + first_matrix_inv.set(first_matrix); + first_matrix_inv.invert(); + Fmatrix rotate; + + rotate.mul(first_matrix_inv,second_matrix); + ///////////////////////////////////////////// + + float lo; + float hi; + ////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////// + axis.set(0,0,0); + CalcAxis(0,axis,lo,hi,first_matrix,second_matrix,rotate); + if(!b1)axis.invert();//SwapLimits(lo,hi); + dJointSetHinge2Axis1 (m_joint, axis.x, axis.y, axis.z); + + + dJointSetHinge2Param(m_joint,dParamLoStop ,lo); + dJointSetHinge2Param(m_joint,dParamHiStop ,hi); + + if(!(axes[0].force<0.f)){ + dJointSetHinge2Param(m_joint,dParamFMax,axes[0].force); + dJointSetHinge2Param(m_joint,dParamVel ,axes[0].velocity); + } + + CalcAxis(1,axis,lo,hi,first_matrix,second_matrix,rotate); + + dJointSetHinge2Axis2 (m_joint, axis.x, axis.y, axis.z); + + dJointSetHinge2Param(m_joint,dParamLoStop2 ,lo); + dJointSetHinge2Param(m_joint,dParamHiStop2 ,hi); + if(!(axes[1].force<0.f)){ + dJointSetHinge2Param(m_joint,dParamFMax2 ,axes[1].force); + dJointSetHinge2Param(m_joint,dParamVel2 ,axes[1].velocity); + } + ////////////////////////////////////////////////////////////////// + + dJointSetHinge2Param(m_joint, dParamSuspensionERP, m_erp); + dJointSetHinge2Param(m_joint, dParamSuspensionCFM, m_cfm); + //dJointSetHinge2Param(m_joint, dParamStopERP, 1.f); + //dJointSetHinge2Param(m_joint, dParamStopCFM,0.f); + dJointSetHinge2Param(m_joint, dParamStopERP,axes[0].erp); + dJointSetHinge2Param(m_joint, dParamStopCFM,axes[0].cfm); +} +void CPHJoint::CreateSlider() +{ + Fvector pos; + Fmatrix first_matrix,second_matrix; + Fvector axis; + CPHElement* first=(pFirst_element); + CPHElement* second=(pSecond_element); + + VERIFY(first); + first->GetGlobalTransformDynamic(&first_matrix); + dBodyID body1=body_for_joint(first); + VERIFY(second); + second->GetGlobalTransformDynamic(&second_matrix); + dBodyID body2=body_for_joint(second); + + + pos.set(0,0,0); + switch(vs_anchor){ + case vs_first :first_matrix.transform_tiny(pos,anchor); break; + case vs_second:second_matrix.transform_tiny(pos,anchor); break; + case vs_global:pShell->mXFORM.transform_tiny(pos,anchor);break; + default:NODEFAULT; + } + ////////////////////////////////////// + + + m_joint=dJointCreateSlider(0,0); + dJointAttach(m_joint,body1,body2); + if (body1) + { + axes[0].vs=vs_first; + axes[1].vs=vs_first; + } + else + if (body2) + { + axes[0].vs=vs_second; + axes[1].vs=vs_second; + } + + + + m_joint1=dJointCreateAMotor(0,0); + dJointSetAMotorMode (m_joint1, dAMotorEuler); + dJointSetAMotorNumAxes (m_joint1, 1); + + dJointAttach(m_joint1,body1,body2); + + ///////////////////////////////////////////// + + Fmatrix first_matrix_inv; + first_matrix_inv.set(first_matrix); + first_matrix_inv.invert(); + Fmatrix rotate; + rotate.mul(first_matrix_inv,second_matrix); + ///////////////////////////////////////////// + + float lo; + float hi; + ////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////// + axis.set(0,0,0); + //axis 0 + CalcAxis(0,axis,lo,hi,first_matrix,second_matrix,rotate); + //if(body1)axis.invert();//SwapLimits(lo,hi);!!! + + dJointSetSliderAxis(m_joint, axis.x, axis.y, axis.z); + + dJointSetSliderParam(m_joint,dParamLoStop ,lo); + dJointSetSliderParam(m_joint,dParamHiStop ,hi); + + if(!(axes[0].force<0.f)){ + dJointSetSliderParam(m_joint,dParamFMax ,axes[0].force); + dJointSetSliderParam(m_joint,dParamVel ,axes[0].velocity); + } + dJointSetSliderParam(m_joint,dParamStopERP ,axes[0].erp); + dJointSetSliderParam(m_joint,dParamStopCFM ,axes[0].cfm); + + //axis 1 + + CalcAxis(1,axis,lo,hi,first_matrix,second_matrix,rotate); + if(!body1)axis.invert();//SwapLimits(lo,hi); + int rel = body1 ? 1 : 2; + dJointSetAMotorAxis (m_joint1, 0,rel, axis.x, axis.y, axis.z); + dJointSetAMotorParam(m_joint1,dParamLoStop ,lo); + dJointSetAMotorParam(m_joint1,dParamHiStop ,hi); + if(!(axes[1].force<0.f)){ + dJointSetAMotorParam(m_joint1,dParamFMax ,axes[1].force); + dJointSetAMotorParam(m_joint1,dParamVel ,axes[1].velocity); + } + + + + dJointSetAMotorParam(m_joint1,dParamStopERP ,axes[1].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM ,axes[1].cfm); + + + ///////////////////////////////////////////////////////////////////// + ///dJointSetAMotorParam(m_joint1,dParamFudgeFactor ,0.1f); + //dJointSetAMotorParam(m_joint1,dParamFudgeFactor2 ,0.1f); + //dJointSetAMotorParam(m_joint1,dParamFudgeFactor3 ,0.1f); + ///////////////////////////////////////////////////////////////////////////// + dJointSetAMotorParam(m_joint1,dParamCFM ,m_cfm); + dJointSetSliderParam(m_joint,dParamCFM,m_cfm); +} + +#ifdef ODE_SLOW_SOLVER +#define FIX_BY_ONE_HINGE +#endif +void CPHJoint::CreateFullControl() +{ + + + Fvector pos; + Fmatrix first_matrix,second_matrix; + Fvector axis; + CPHElement* first=(pFirst_element); + CPHElement* second=(pSecond_element); + VERIFY(first); + first->GetGlobalTransformDynamic(&first_matrix); + dBodyID body1=body_for_joint(first); + VERIFY(second); + second->GetGlobalTransformDynamic(&second_matrix); + dBodyID body2=body_for_joint(second); + + + + +pos.set(0,0,0); +switch(vs_anchor){ +case vs_first :first_matrix.transform_tiny(pos,anchor); break; +case vs_second:second_matrix.transform_tiny(pos,anchor); break; +case vs_global:pShell->mXFORM.transform_tiny(pos,anchor);break; +default:NODEFAULT; + } + ////////////////////////////////////// + + + + m_joint=dJointCreateBall(0,0); + dJointAttach(m_joint,body1,body2); + dJointSetBallAnchor(m_joint,pos.x,pos.y,pos.z); + + + + m_joint1=dJointCreateAMotor(0,0); + dJointSetAMotorMode (m_joint1, dAMotorEuler); + dJointSetAMotorNumAxes (m_joint1, 3); + + dJointAttach(m_joint1,body1,body2); + + ///////////////////////////////////////////// + + Fmatrix first_matrix_inv; + first_matrix_inv.set(first_matrix); + first_matrix_inv.invert(); + Fmatrix rotate; + rotate.mul(first_matrix_inv,second_matrix); + ///////////////////////////////////////////// + + float lo; + float hi; + ////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////// + axis.set(0,0,0); + //axis 0 + CalcAxis(0,axis,lo,hi,first_matrix,second_matrix,rotate); + if(!body1)axis.invert();//SwapLimits(lo,hi); + dJointSetAMotorAxis (m_joint1, 0, 1, axis.x, axis.y, axis.z); + dJointSetAMotorParam(m_joint1,dParamLoStop ,lo); + dJointSetAMotorParam(m_joint1,dParamHiStop ,hi); + + if(!(axes[0].force<0.f)){ + dJointSetAMotorParam(m_joint1,dParamFMax ,axes[0].force); + dJointSetAMotorParam(m_joint1,dParamVel ,axes[0].velocity); + } + + //axis 1 + CalcAxis(1,axis,lo,hi,first_matrix,second_matrix,rotate); + if(!body1)axis.invert();//SwapLimits(lo,hi); + dJointSetAMotorParam(m_joint1,dParamLoStop2 ,lo); + dJointSetAMotorParam(m_joint1,dParamHiStop2 ,hi); + if(!(axes[1].force<0.f)){ + dJointSetAMotorParam(m_joint1,dParamFMax2 ,axes[1].force); + dJointSetAMotorParam(m_joint1,dParamVel2 ,axes[1].velocity); + } + + //axis 2 + CalcAxis(2,axis,lo,hi,first_matrix,second_matrix,rotate); + if(!body1)axis.invert();//SwapLimits(lo,hi); + dJointSetAMotorAxis (m_joint1, 2, 2, axis.x, axis.y, axis.z); + dJointSetAMotorParam(m_joint1,dParamLoStop3 ,lo); + dJointSetAMotorParam(m_joint1,dParamHiStop3 ,hi); + if(!(axes[2].force<0.f)){ + dJointSetAMotorParam(m_joint1,dParamFMax3 ,axes[2].force); + dJointSetAMotorParam(m_joint1,dParamVel3 ,axes[2].velocity); + } + + dJointSetAMotorParam(m_joint1,dParamStopERP ,axes[0].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM ,axes[0].cfm); + + dJointSetAMotorParam(m_joint1,dParamStopERP2 ,axes[1].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM2 ,axes[1].cfm); + + dJointSetAMotorParam(m_joint1,dParamStopERP3 ,axes[2].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM3 ,axes[2].cfm); +///////////////////////////////////////////////////////////////////// + ///dJointSetAMotorParam(m_joint1,dParamFudgeFactor ,0.1f); + //dJointSetAMotorParam(m_joint1,dParamFudgeFactor2 ,0.1f); + //dJointSetAMotorParam(m_joint1,dParamFudgeFactor3 ,0.1f); +///////////////////////////////////////////////////////////////////////////// + dJointSetAMotorParam(m_joint1,dParamCFM ,m_cfm); + dJointSetAMotorParam(m_joint1,dParamCFM2 ,m_cfm); + dJointSetAMotorParam(m_joint1,dParamCFM3 ,m_cfm); +} + + +void CPHJoint::SetAnchor(const float x,const float y,const float z) +{ + vs_anchor=vs_global; + anchor.set(x,y,z); +} + +void CPHJoint::SetAnchorVsFirstElement(const float x,const float y,const float z) +{ + vs_anchor=vs_first; + anchor.set(x,y,z); +} + +void CPHJoint::SetAnchorVsSecondElement(const float x,const float y,const float z) +{ + vs_anchor=vs_second; + anchor.set(x,y,z); +} + +void CPHJoint::SetAxisDir(const float x,const float y,const float z,const int axis_num) +{ + int ax=axis_num; + LimitAxisNum(ax); + if(-1==ax) return; + axes[ax].vs=vs_global; + axes[ax].direction.set(x,y,z); +} + +void CPHJoint::SetAxisDirVsFirstElement(const float x,const float y,const float z,const int axis_num) +{ + int ax=axis_num; + LimitAxisNum(ax); + if(-1==ax) return; + axes[ax].vs=vs_first; + axes[ax].direction.set(x,y,z); +} + +void CPHJoint::SetAxisDirVsSecondElement(const float x,const float y,const float z,const int axis_num) +{ + int ax=axis_num; + LimitAxisNum(ax); + if(-1==ax) return; + axes[ax].vs=vs_second; + axes[ax].direction.set(x,y,z); + +} + +void CPHJoint::SetLimits(const float low, const float high, const int axis_num) +{ + if(!(pFirst_element&&pSecond_element))return; + + int ax=axis_num; + LimitAxisNum(ax); + if(-1==ax)return; + + Fvector axis; + switch(axes[ax].vs){ + case vs_first :pFirst_element->mXFORM.transform_dir(axis,axes[ax].direction); break; + case vs_second:pSecond_element->mXFORM.transform_dir(axis,axes[ax].direction); break; + case vs_global: + default: axis.set(axes[ax].direction); + } + + axes[ax].low=low; + axes[ax].high=high; + Fmatrix m1,m2; + m1.set(pFirst_element->mXFORM); + m1.invert(); + m2.mul(m1,pSecond_element->mXFORM); + //m2.mul(pSecond_element->mXFORM,m1); + + + + float zer; + //axis_angleB(m2,axis,zer); + axis_angleA(m2,axes[ax].direction,zer); + + axes[ax].zero=zer; + //m2.invert(); + //axes[ax].zero_transform.set(m2); + if(bActive)SetLimitsActive(axis_num); +} + + +CPHJoint::CPHJoint(CPhysicsJoint::enumType type ,CPhysicsElement* first,CPhysicsElement* second) +{ + + pShell=NULL; + m_bone_id=u16(-1); + m_back_ref =NULL; + m_destroy_info=NULL; + pFirstGeom =NULL; + pFirst_element=cast_PHElement(first); + pSecond_element=cast_PHElement(second); + m_joint=NULL; + m_joint1=NULL; + eType=type; + bActive=false; + +#ifndef ODE_SLOW_SOLVER + m_erp=world_erp; + m_cfm=world_cfm; +#else + m_erp=world_erp; + m_cfm=world_cfm; +#endif + + SPHAxis axis,axis2,axis3; + axis2.set_direction(1,0,0); + axis3.direction.crossproduct(axis.direction,axis3.direction); + vs_anchor=vs_first; + + switch(eType){ + case ball: ; break; + case hinge: axes.push_back(axis); + break; + case hinge2: + axes.push_back(axis); + axes.push_back(axis2); + break; + case full_control: + axes.push_back(axis); + axes.push_back(axis2); + axes.push_back(axis3); + case slider: + axes.push_back(axis); + axes.push_back(axis); + } + +} + +void CPHJoint::SetLimitsVsFirstElement(const float low, const float high,const int axis_num) +{ +} + +void CPHJoint::SetLimitsVsSecondElement(const float low, const float high,const int axis_num) +{ +} + +void CPHJoint::Create() +{ + if(bActive) return; + switch(eType){ + case ball: CreateBall(); break; + case hinge: CreateHinge(); break; + case hinge2: CreateHinge2(); break; + case full_control: CreateFullControl(); break; + case slider: CreateSlider(); break; + } + if(m_destroy_info) + { + dJointSetFeedback(m_joint,m_destroy_info->JointFeedback()); + if(m_joint1)dJointSetFeedback(m_joint1,m_destroy_info->JointFeedback()); + } + dJointSetData(m_joint,(void*)this); + if(m_joint1)dJointSetData(m_joint1,(void*)this); + bActive=true; +} +void CPHJoint::RunSimulation() +{ + pShell->Island().AddJoint(m_joint); + ///dWorldAddJoint(phWorld,m_joint); + if(m_joint1) + { + //dWorldAddJoint(phWorld,m_joint1); + pShell->Island().AddJoint(m_joint1); + } +} +void CPHJoint::Activate() +{ + Create(); + RunSimulation(); +} +void CPHJoint::Deactivate() +{ + if(!bActive) return; + switch(eType){ + case ball: ; + case hinge: ; + case hinge2: ; + if(m_joint->world)pShell->Island().RemoveJoint(m_joint); + dJointDestroy(m_joint); + break; + case full_control: + case slider: + if(m_joint->world)pShell->Island().RemoveJoint(m_joint); + if(m_joint1->world)pShell->Island().RemoveJoint(m_joint1); + dJointDestroy(m_joint); + dJointDestroy(m_joint1); + m_joint1=NULL; + break; + } + m_joint=NULL; + bActive=false; +} +void CPHJoint::ReattachFirstElement(CPHElement* new_element) +{ + Deactivate(); + pFirst_element=(new_element); + Activate(); + //dJointAttach(m_joint,pFirst_element->get_body(),pSecond_element->get_body()); + //if(m_joint1)dJointAttach(m_joint1,pFirst_element->get_body(),pSecond_element->get_body()); +} +void CPHJoint::SetForceAndVelocity (const float force,const float velocity,const int axis_num) +{ + if(pShell&&pShell->isActive())pShell->Enable(); + SetForce(force,axis_num); + SetVelocity(velocity,axis_num); +} + +void CPHJoint::GetMaxForceAndVelocity(float &force,float &velocity,int axis_num) +{ + force=axes[axis_num].force; + velocity=axes[axis_num].velocity; +} + + +void CPHJoint::SetForce (const float force,const int axis_num){ + int ax; + ax=axis_num; + LimitAxisNum(ax); + + if(ax==-1) + switch(eType){ + case ball: return; + case hinge: + axes[0].force=force; + break; + case hinge2: ; + case slider: ; + axes[0].force=force; + axes[1].force=force; + break; + case full_control: + axes[0].force=force; + axes[1].force=force; + axes[2].force=force; + break; + } + + else{ + axes[ax].force=force; + } + + if(bActive) + { + SetForceActive(ax); + } +} + + +void CPHJoint::SetForceActive (const int axis_num) +{ + switch(eType){ + + case hinge2:switch(axis_num) + { + case -1: + dJointSetHinge2Param(m_joint,dParamFMax ,axes[0].force); + dJointSetHinge2Param(m_joint,dParamFMax2 ,axes[1].force); + case 0: dJointSetHinge2Param(m_joint,dParamFMax ,axes[0].force);break; + case 1: dJointSetHinge2Param(m_joint,dParamFMax2 ,axes[1].force);break; + } + break; + case slider:switch(axis_num) + { + case -1: + dJointSetSliderParam(m_joint,dParamFMax ,axes[0].force); + dJointSetAMotorParam(m_joint1,dParamFMax ,axes[1].force); + case 0: dJointSetSliderParam(m_joint,dParamFMax ,axes[0].force);break; + case 1: dJointSetAMotorParam(m_joint1,dParamFMax ,axes[1].force);break; + } + break; + case ball: break; + case hinge: dJointSetHingeParam(m_joint,dParamFMax ,axes[0].force); + break; + + + + case full_control: + switch(axis_num){ + case -1: + dJointSetAMotorParam(m_joint1,dParamFMax ,axes[0].force); + dJointSetAMotorParam(m_joint1,dParamFMax2 ,axes[1].force); + dJointSetAMotorParam(m_joint1,dParamFMax3 ,axes[2].force); + case 0:dJointSetAMotorParam(m_joint1,dParamFMax ,axes[0].force);break; + case 1:dJointSetAMotorParam(m_joint1,dParamFMax2 ,axes[1].force);break; + case 2:dJointSetAMotorParam(m_joint1,dParamFMax3 ,axes[2].force);break; + } + break; + } +} + +void CPHJoint::SetVelocity (const float velocity,const int axis_num){ + int ax; + ax=axis_num; + LimitAxisNum(ax); + + + if(ax==-1) + switch(eType){ + case ball: return; + case hinge: + axes[0].velocity=velocity; + break; + case hinge2: ; + case slider: ; + axes[0].velocity=velocity; + axes[1].velocity=velocity; + break; + case full_control: + axes[0].velocity=velocity; + axes[1].velocity=velocity; + axes[2].velocity=velocity; + break; + } + + else{ + axes[ax].velocity=velocity; + } + + if(bActive) + { + SetVelocityActive(ax); + } +} + +void CPHJoint::SetVelocityActive(const int axis_num) +{ + switch(eType){ + + case hinge2:switch(axis_num) + { + case -1: + dJointSetHinge2Param(m_joint,dParamVel ,axes[0].velocity); + dJointSetHinge2Param(m_joint,dParamVel2 ,axes[1].velocity); + case 0: dJointSetHinge2Param(m_joint,dParamVel ,axes[0].velocity);break; + case 1: dJointSetHinge2Param(m_joint,dParamVel2 ,axes[1].velocity);break; + } + break; + case slider:switch(axis_num) + { + case -1: + dJointSetSliderParam(m_joint,dParamVel ,axes[0].velocity); + dJointSetAMotorParam(m_joint1,dParamVel ,axes[1].velocity); + case 0: dJointSetSliderParam(m_joint,dParamVel ,axes[0].velocity);break; + case 1: dJointSetAMotorParam(m_joint1,dParamVel ,axes[1].velocity);break; + } + break; + case ball: break; + case hinge: dJointSetHingeParam(m_joint,dParamVel ,axes[0].velocity); + break; + + + + case full_control: + switch(axis_num){ + case -1: + dJointSetAMotorParam(m_joint1,dParamVel ,axes[0].velocity); + dJointSetAMotorParam(m_joint1,dParamVel2 ,axes[1].velocity); + dJointSetAMotorParam(m_joint1,dParamVel3 ,axes[2].velocity); + case 0:dJointSetAMotorParam(m_joint1,dParamVel ,axes[0].velocity);break; + case 1:dJointSetAMotorParam(m_joint1,dParamVel2 ,axes[1].velocity);break; + case 2:dJointSetAMotorParam(m_joint1,dParamVel3 ,axes[2].velocity);break; + } + break; + } +} + +void CPHJoint::SetLimitsActive(int axis_num) +{ + switch(eType){ + + case hinge2: + switch(axis_num) + { + case -1: + case 0: + case 1: + dJointSetHinge2Param(m_joint,dParamLoStop ,axes[0].low); + dJointSetHinge2Param(m_joint,dParamHiStop,axes[0].high);break; + } + break; + case slider:switch(axis_num) + { + case -1: + dJointSetSliderParam(m_joint,dParamLoStop ,axes[0].low); + dJointSetSliderParam(m_joint,dParamHiStop ,axes[0].high); + dJointSetAMotorParam(m_joint1,dParamLoStop ,axes[1].low); + dJointSetAMotorParam(m_joint1,dParamHiStop ,axes[1].high); + case 0: dJointSetSliderParam(m_joint,dParamLoStop ,axes[0].low); + dJointSetSliderParam(m_joint,dParamHiStop ,axes[0].high);break; + case 1: dJointSetAMotorParam(m_joint1,dParamLoStop ,axes[1].low); + dJointSetAMotorParam(m_joint1,dParamHiStop ,axes[1].high);break; + } + break; + case ball: break; + case hinge: dJointSetHingeParam(m_joint,dParamLoStop ,axes[0].low); + dJointSetHingeParam(m_joint,dParamHiStop ,axes[0].high); + break; + + + + case full_control: + switch(axis_num){ + case -1: + dJointSetAMotorParam(m_joint1,dParamLoStop ,axes[0].low); + dJointSetAMotorParam(m_joint1,dParamLoStop ,axes[0].low); + dJointSetAMotorParam(m_joint1,dParamLoStop2 ,axes[1].low); + dJointSetAMotorParam(m_joint1,dParamHiStop2 ,axes[1].high); + dJointSetAMotorParam(m_joint1,dParamHiStop3 ,axes[2].high); + dJointSetAMotorParam(m_joint1,dParamHiStop3 ,axes[2].high); + case 0:dJointSetAMotorParam(m_joint1,dParamLoStop ,axes[0].low); + dJointSetAMotorParam(m_joint1,dParamHiStop ,axes[0].high); + break; + case 1:dJointSetAMotorParam(m_joint1,dParamLoStop2 ,axes[1].low); + dJointSetAMotorParam(m_joint1,dParamHiStop2 ,axes[1].high); + break; + case 2:dJointSetAMotorParam(m_joint1,dParamLoStop3 ,axes[2].low); + dJointSetAMotorParam(m_joint1,dParamHiStop3 ,axes[2].high); + break; + } + break; + } +} + +float CPHJoint::GetAxisAngle(int axis_num) +{ + float ret=dInfinity; + switch(eType){ + case hinge2: ret= dJointGetHinge2Angle1(m_joint);break; + case ball: ret= dInfinity;break; + case hinge: ret= dJointGetHingeAngle(m_joint);break; + case full_control: ret= dJointGetAMotorAngle(m_joint1,axis_num);break; + case slider: + switch (axis_num){ + case 0: ret= dJointGetSliderPosition(m_joint);break; + case 1: ret= dJointGetAMotorAngle(m_joint1,0);break; + };break; + } + return ret;// body_for_joint(pFirst_element) ? ret : - +} +void CPHJoint::LimitAxisNum(int &axis_num) +{ + if(axis_num<-1) + { + axis_num=-1; + return; + } + + switch(eType){ + case ball: axis_num=-1; + break; + case hinge: axis_num=0; + break; + case slider: + case hinge2: axis_num= axis_num>1 ? 1 : axis_num; + break; + + case full_control: axis_num= axis_num>2 ? 2 : axis_num; + break; + + } + +} + +void CPHJoint::SetAxis(const SPHAxis& axis,const int axis_num) +{ +int ax=axis_num; +LimitAxisNum(ax); +if(ax==-1) + switch(eType){ + case ball: + break; + case hinge: + axes[0]=axis; + break; + case hinge2: ; + case slider: ; + axes[0]=axis; + axes[1]=axis; + break; + case full_control: + axes[0]=axis; + axes[1]=axis; + axes[2]=axis; + break; + } +else + axes[ax]=axis; +} + +void CPHJoint::SetAxisSDfactors(float spring_factor,float damping_factor,int axis_num) + +{ + int ax=axis_num; + LimitAxisNum(ax); + if(ax==-1) + { + switch(eType){ + case ball: + break; + case hinge: + axes[0].set_sd_factors(spring_factor,damping_factor,eType); + break; + case hinge2: ; + case slider: ; + axes[0].set_sd_factors(spring_factor,damping_factor,eType); + axes[1].set_sd_factors(spring_factor,damping_factor,eType); + break; + + case full_control: + axes[0].set_sd_factors(spring_factor,damping_factor,eType); + axes[1].set_sd_factors(spring_factor,damping_factor,eType); + axes[2].set_sd_factors(spring_factor,damping_factor,eType); + break; + } + + if(bActive)SetLimitsSDfactorsActive(); + } + else + { + axes[ax].set_sd_factors(spring_factor,damping_factor,eType); + if(bActive)SetAxisSDfactorsActive(ax); + } +} + + +void CPHJoint::SetJointSDfactors(float spring_factor,float damping_factor) +{ + switch(eType){ + case hinge2: + m_cfm=CFM(hinge2_spring*spring_factor,hinge2_damping*damping_factor); + m_erp=ERP(hinge2_spring*spring_factor,hinge2_damping*damping_factor); + break; + case ball: ; + case hinge: ; + case full_control: ; + case slider: ; + m_erp=ERP(world_spring*spring_factor,world_damping*damping_factor); + m_cfm=CFM(world_spring*spring_factor,world_damping*damping_factor); + break; + } + if(bActive) SetJointSDfactorsActive(); +} + +void CPHJoint::SetJointSDfactorsActive() +{ + switch(eType){ + case hinge2: dJointSetHinge2Param(m_joint, dParamSuspensionERP, m_erp); + dJointSetHinge2Param(m_joint, dParamSuspensionCFM, m_cfm); break ; + case ball: break ; + case hinge: dJointSetHingeParam(m_joint,dParamCFM ,m_cfm); break ; + case full_control: dJointSetAMotorParam(m_joint1,dParamCFM ,m_cfm); + dJointSetAMotorParam(m_joint1,dParamCFM2 ,m_cfm); + dJointSetAMotorParam(m_joint1,dParamCFM3 ,m_cfm); break ; + case slider: dJointSetSliderParam(m_joint,dParamCFM ,m_cfm); break ; + } +} +void CPHJoint::SetLimitsSDfactorsActive() +{ + switch(eType){ + case hinge2: dJointSetHinge2Param(m_joint, dParamStopERP,axes[0].erp); + dJointSetHinge2Param(m_joint, dParamStopCFM,axes[0].cfm); break ; + case ball: break ; + case hinge: dJointSetHingeParam(m_joint,dParamStopERP ,axes[0].erp); + dJointSetHingeParam(m_joint,dParamStopCFM ,axes[0].cfm); break ; + case full_control: dJointSetAMotorParam(m_joint1,dParamStopERP ,axes[0].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM ,axes[0].cfm); + dJointSetAMotorParam(m_joint1,dParamStopERP2 ,axes[1].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM2 ,axes[1].cfm); + dJointSetAMotorParam(m_joint1,dParamStopERP3 ,axes[2].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM3 ,axes[2].cfm); break ; + case slider: dJointSetSliderParam(m_joint,dParamStopERP,axes[0].erp); + dJointSetSliderParam(m_joint,dParamStopCFM,axes[0].cfm); + dJointSetAMotorParam(m_joint1,dParamStopERP ,axes[1].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM ,axes[1].cfm); break ; + + } +} +void CPHJoint::SetAxisSDfactorsActive(int axis_num) +{ + LimitAxisNum(axis_num); + + switch(eType){ + case hinge2: dJointSetHinge2Param(m_joint, dParamStopERP,axes[0].erp); + dJointSetHinge2Param(m_joint, dParamStopCFM,axes[0].cfm); + break ; + case ball: break ; + case hinge: dJointSetHingeParam(m_joint,dParamStopERP ,axes[0].erp); + dJointSetHingeParam(m_joint,dParamStopCFM ,axes[0].cfm); + break ; + case full_control: switch(axis_num) + { + case 0: + dJointSetAMotorParam(m_joint1,dParamStopERP ,axes[axis_num].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM ,axes[0].cfm); + break; + case 1: + dJointSetAMotorParam(m_joint1,dParamStopERP2 ,axes[1].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM2 ,axes[1].cfm); + break; + case 2: + dJointSetAMotorParam(m_joint1,dParamStopERP3 ,axes[2].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM3 ,axes[2].cfm); + break; + } + break; + case slider: switch(axis_num) + { + case 0: + dJointSetSliderParam(m_joint,dParamStopERP,axes[0].erp); + dJointSetSliderParam(m_joint,dParamStopCFM,axes[0].cfm); + case 1: + dJointSetAMotorParam(m_joint1,dParamStopERP ,axes[1].erp); + dJointSetAMotorParam(m_joint1,dParamStopCFM ,axes[1].cfm); + } + break ; + + + } +} +void CPHJoint::GetJointSDfactors(float& spring_factor,float& damping_factor) +{ +spring_factor =SPRING(m_cfm,m_erp); +damping_factor=DAMPING(m_cfm,m_erp); +if(eType==hinge2) + { + spring_factor/=hinge2_spring; + damping_factor/=hinge2_damping; + } +else + { + spring_factor/=world_spring; + damping_factor/=world_damping; + } +} +void CPHJoint::GetAxisSDfactors(float& spring_factor,float& damping_factor,int axis_num) +{ + LimitAxisNum(axis_num); + spring_factor=SPRING(axes[axis_num].cfm,axes[axis_num].erp)/world_spring; + damping_factor=DAMPING(axes[axis_num].cfm,axes[axis_num].erp)/world_damping; +} +u16 CPHJoint::GetAxesNumber() +{ + return u16(axes.size()); +} +void CPHJoint::CalcAxis(int ax_num,Fvector& axis, float& lo,float& hi,const Fmatrix& first_matrix,const Fmatrix& second_matrix,const Fmatrix& rotate) +{ + switch(axes[ax_num].vs) + { + case vs_first :first_matrix.transform_dir(axis,axes[ax_num].direction) ; break; + case vs_second:second_matrix.transform_dir(axis,axes[ax_num].direction) ; break; + case vs_global:pShell->mXFORM.transform_dir(axis,axes[ax_num].direction);break; + default: NODEFAULT; + } + lo=axes[ax_num].low; + hi=axes[ax_num].high; + if(lo<-M_PI){ + hi-=(lo+M_PI); + lo=-M_PI; + } + if(lo>0.f) { + hi-=lo; + lo=0.f; + } + if(hi>M_PI) { + lo-=(hi-M_PI); + hi=M_PI; + } + if(hi<0.f) { + lo-=hi; + hi=0.f; + } +} + +void CPHJoint::CalcAxis(int ax_num,Fvector& axis,float& lo,float& hi,const Fmatrix& first_matrix,const Fmatrix& second_matrix) +{ + switch(axes[ax_num].vs) + { + + case vs_first :first_matrix.transform_dir(axis,axes[ax_num].direction); break; + case vs_second:second_matrix.transform_dir(axis,axes[ax_num].direction); break; + case vs_global:pShell->mXFORM.transform_dir(axis,axes[ax_num].direction);break; + default: NODEFAULT; + } + + + Fmatrix inv_first_matrix; + inv_first_matrix.set(first_matrix); + inv_first_matrix.invert(); + + Fmatrix rotate; + rotate.mul(inv_first_matrix,second_matrix); + + float shift_angle; + axis_angleA(rotate,axes[ax_num].direction,shift_angle); + + shift_angle-=axes[ax_num].zero; + + if(shift_angle>M_PI) shift_angle-=2.f*M_PI; + if(shift_angle<-M_PI) shift_angle+=2.f*M_PI; + + + lo=axes[ax_num].low;//+shift_angle; + hi=axes[ax_num].high;//+shift_angle; + if(lo<-M_PI){ + hi-=(lo+M_PI); + lo=-M_PI; + } + if(lo>0.f) { + hi-=lo; + lo=0.f; + } + if(hi>M_PI) { + lo-=(hi-M_PI); + hi=M_PI; + } + if(hi<0.f) { + lo-=hi; + hi=0.f; + } + + +} + +void CPHJoint::GetLimits (float& lo_limit,float& hi_limit,int axis_num) +{ + LimitAxisNum(axis_num); + if( body_for_joint(pFirst_element)) + { + + lo_limit=axes[axis_num].low; + hi_limit=axes[axis_num].high; + }else + { + lo_limit=-axes[axis_num].high; + hi_limit=-axes[axis_num].low; + } +} + + +void CPHJoint::GetAxisDir(int num, Fvector& axis, eVs& vs) +{ +LimitAxisNum(num); +vs=axes[num].vs; +axis.set(axes[num].direction); +} + +void CPHJoint::GetAxisDirDynamic(int num,Fvector& axis) +{ + LimitAxisNum(num); + dVector3 result; + switch(eType) + { + + case ball: ; + return; + case hinge: dJointGetHingeAxis (m_joint,result); + break; + case hinge2: if(num) dJointGetHinge2Axis2 (m_joint,result); + else dJointGetHinge2Axis1 (m_joint,result); + break; + case full_control: dJointGetAMotorAxis (m_joint1,num,result); + break; + case slider: dJointGetSliderAxis(m_joint,result); + + } + axis.set(result[0],result[1],result[2]); +} + +void CPHJoint::GetAnchorDynamic(Fvector& anchor) +{ + + dVector3 result; + switch(eType) + { + case hinge: dJointGetHingeAnchor (m_joint,result); + break; + case hinge2: dJointGetHingeAnchor (m_joint,result); + break; + case ball: ; + case full_control: dJointGetBallAnchor (m_joint,result); + break; + case slider: R_ASSERT2(false,"position of slider joint is undefinite"); + } + anchor.set(result[0],result[1],result[2]); +} + +CPHJoint::SPHAxis::SPHAxis(){ + high=dInfinity; + low=-dInfinity; + zero=0.f; + //erp=ERP(world_spring/5.f,world_damping*5.f); + //cfm=CFM(world_spring/5.f,world_damping*5.f); +#ifndef ODE_SLOW_SOLVER + erp=world_erp; + cfm=world_cfm; +#else + erp=0.3f; + cfm=0.000001f; +#endif + direction.set(0,0,1); + vs=vs_first; + force=0.f; + velocity=0.f; +} + +void CPHJoint::SPHAxis::set_sd_factors(float sf,float df,enumType jt) +{ + switch(jt) + { + case hinge2: +#ifndef ODE_SLOW_SOLVER + cfm=0.f; + erp=1.f; + break; +#endif + case ball: ; + case hinge: ; + case full_control: ; + case slider: ; + erp=ERP(world_spring*sf,world_damping*df); + cfm=CFM(world_spring*sf,world_damping*df); + break; + } +} +CPhysicsElement* CPHJoint::PFirst_element() +{ + return cast_PhysicsElement(pFirst_element); +} +CPhysicsElement* CPHJoint::PSecond_element() +{ + return cast_PhysicsElement(pSecond_element); +} +void CPHJoint::SetBreakable(float force,float torque) +{ + if(!m_destroy_info) m_destroy_info=new CPHJointDestroyInfo(force,torque); +} + +void CPHJoint::SetShell(CPHShell* p) +{ + if(!m_joint||!pShell) + { + pShell=p; + return; + } + if(pShell!=p) + { + pShell->Island().RemoveJoint(m_joint); + p->Island().AddJoint(m_joint); + if(m_joint1) + { + pShell->Island().RemoveJoint(m_joint1); + p->Island().AddJoint(m_joint1); + } + pShell=p; + } +} +void CPHJoint::ClearDestroyInfo() +{ + xr_delete(m_destroy_info); +} \ No newline at end of file diff --git a/src/xrGameLA/PHJoint.h b/src/xrGameLA/PHJoint.h new file mode 100644 index 000000000..c0be6a705 --- /dev/null +++ b/src/xrGameLA/PHJoint.h @@ -0,0 +1,238 @@ +///////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +#ifndef PH_JOINT +#define PH_JOINT +#include "PhysicsShell.h" +class CPHJointDestroyInfo; +class CPHJoint: public CPhysicsJoint{ +/////////////////////////////////////////////////////// + u16 m_bone_id ; + CPHElement *pFirst_element ; + CPHElement *pSecond_element ; + CODEGeom *pFirstGeom ; + ///////////////////////////////////////////////////////// + CPHShell *pShell ; + dJointID m_joint ; + dJointID m_joint1 ; + CPhysicsJoint **m_back_ref ; + CPHJointDestroyInfo *m_destroy_info ; + float m_erp ; //joint erp + float m_cfm ; //joint cfm +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + struct SPHAxis { + float high ; //high limit + float low ; //law limit + float zero ; //zero angle position + float erp ; //limit erp + float cfm ; //limit cfm + eVs vs ; //coordinate system + float force ; //max force + float velocity ; //velocity to achieve + Fvector direction ; //axis direction + IC void set_limits (float h, float l) {high=h; low=l ;} + IC void set_direction (const Fvector& v) {direction.set(v) ;} + IC void set_direction (const float x,const float y,const float z) {direction.set(x,y,z) ;} + IC void set_param (const float e,const float c) {erp=e;cfm=c ;} + void set_sd_factors (float sf,float df,enumType jt) ; + SPHAxis (); + }; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + xr_vector axes ; + Fvector anchor ; + eVs vs_anchor ; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void CreateBall () ; + void CreateHinge () ; + void CreateHinge2 () ; + void CreateFullControl () ; + void CreateSlider () ; + void LimitAxisNum (int &axis_num) ; + void SetForceActive (const int axis_num) ; + void SetVelocityActive (const int axis_num) ; + void SetLimitsActive (int axis_num) ; + void CalcAxis (int ax_num,Fvector& axis,float& lo,float& hi,const Fmatrix& first_matrix,const Fmatrix& second_matrix) ; + void CalcAxis (int ax_num,Fvector& axis,float& lo,float& hi,const Fmatrix& first_matrix,const Fmatrix& second_matrix,const Fmatrix& rotate) ; + virtual u16 GetAxesNumber () ; + virtual void SetAxisSDfactors (float spring_factor,float damping_factor,int axis_num) ; + virtual void SetJointSDfactors (float spring_factor,float damping_factor) ; + virtual void SetJointSDfactorsActive () ; + virtual void SetLimitsSDfactorsActive () ; + virtual void SetAxisSDfactorsActive (int axis_num) ; + virtual void SetAxis (const SPHAxis& axis,const int axis_num) ; + virtual void SetAnchor (const Fvector& position) {SetAnchor(position.x,position.y,position.z);} + virtual void SetAnchorVsFirstElement (const Fvector& position) {SetAnchorVsFirstElement(position.x,position.y,position.z) ;} + virtual void SetAnchorVsSecondElement (const Fvector& position) {SetAnchorVsSecondElement(position.x,position.y,position.z) ;} +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetAxisDir (const Fvector& orientation,const int axis_num) {SetAxisDir(orientation.x,orientation.y,orientation.z,axis_num) ;} + virtual void SetAxisDirVsFirstElement (const Fvector& orientation,const int axis_num) {SetAxisDirVsFirstElement(orientation.x,orientation.y,orientation.z,axis_num) ;} + virtual void SetAxisDirVsSecondElement (const Fvector& orientation,const int axis_num) {SetAxisDirVsSecondElement(orientation.x,orientation.y,orientation.z,axis_num) ;} +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetLimits (const float low,const float high,const int axis_num) ; + virtual void SetLimitsVsFirstElement (const float low,const float high,const int axis_num) ; + virtual void SetLimitsVsSecondElement (const float low,const float high,const int axis_num) ; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetAnchor (const float x,const float y,const float z) ; + virtual void SetAnchorVsFirstElement (const float x,const float y,const float z) ; + virtual void SetAnchorVsSecondElement (const float x,const float y,const float z) ; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetAxisDir (const float x,const float y,const float z,const int axis_num) ; + virtual void SetAxisDirVsFirstElement (const float x,const float y,const float z,const int axis_num) ; + virtual void SetAxisDirVsSecondElement (const float x,const float y,const float z,const int axis_num) ; +public: + virtual CPhysicsElement *PFirst_element () ; + virtual CPhysicsElement *PSecond_element () ; + virtual u16 BoneID () {return m_bone_id ;} + virtual void SetBoneID (u16 bone_id) {m_bone_id=bone_id ;} +IC CPHElement *PFirstElement () {return pFirst_element ;} +IC CPHElement *PSecondElement () {return pSecond_element ;} + virtual void Activate () ; + virtual void Create () ; + virtual void RunSimulation () ; + virtual void SetBackRef (CPhysicsJoint **j) ; + virtual void SetForceAndVelocity (const float force,const float velocity=0.f,const int axis_num=-1) ; + virtual void SetForce (const float force,const int axis_num=-1) ; + virtual void SetVelocity (const float velocity=0.f,const int axis_num=-1) ; + virtual void SetBreakable (float force, float torque) ; + virtual bool isBreakable () {return !!m_destroy_info;} + virtual dJointID GetDJoint () {return m_joint ;} + virtual dJointID GetDJoint1 () {return m_joint1 ;} + virtual void GetLimits (float& lo_limit,float& hi_limit,int axis_num) ; + virtual void GetAxisDir (int num,Fvector& axis,eVs& vs) ; + virtual void GetAxisDirDynamic (int num,Fvector& axis) ; + virtual void GetAnchorDynamic (Fvector& anchor) ; + virtual void GetAxisSDfactors (float& spring_factor,float& damping_factor,int axis_num) ; + virtual void GetJointSDfactors (float& spring_factor,float& damping_factor) ; + virtual void GetMaxForceAndVelocity (float &force,float &velocity,int axis_num) ; + virtual float GetAxisAngle (int axis_num) ; + virtual void Deactivate () ; + void ReattachFirstElement (CPHElement* new_element) ; + CODEGeom *&RootGeom () {return pFirstGeom ;} + virtual CPHJointDestroyInfo *JointDestroyInfo () {return m_destroy_info ;} + CPHJoint (CPhysicsJoint::enumType type ,CPhysicsElement* first,CPhysicsElement* second) ; + virtual ~CPHJoint () ; + void SetShell (CPHShell* p) ; + + void ClearDestroyInfo (); + +}; + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +IC void own_axis(const Fmatrix& m,Fvector& axis){ + if(m._11==1.f) {axis.set(1.f,0.f,0.f); return;} + float k=m._13*m._21-m._11*m._23+m._23; + + if(k==0.f){ + if(m._13==0.f) {axis.set(0.f,0.f,1.f);return;} + float k1=m._13/(1.f-m._11); + axis.z=_sqrt(1.f/(1.f+k1*k1)); + axis.x=axis.z*k1; + axis.y=0.f; + return; + } + + float k_zy=-(m._12*m._21-m._11*m._22+m._11+m._22-1.f)/k; + float k_xy=(m._12+m._13*k_zy)/(1.f-m._11); + axis.y=_sqrt(1.f/(k_zy*k_zy+k_xy*k_xy+1.f)); + axis.x=axis.y*k_xy; + axis.z=axis.y*k_zy; + return; +} + + + +IC void own_axis_angle(const Fmatrix& m,Fvector& axis,float& angle){ + own_axis(m,axis); + Fvector ort1,ort2; + if(!(axis.z==0.f&&axis.y==0.f)){ + ort1.set(0.f,-axis.z,axis.y); + ort2.crossproduct(axis,ort1); + } + else{ + ort1.set(0.f,1.f,0.f); + ort2.crossproduct(axis,ort1); + } + ort1.normalize(); + ort2.normalize(); + + Fvector ort1_t; + m.transform_dir(ort1_t,ort1); + + float cosinus=ort1.dotproduct(ort1_t); + float sinus=ort2.dotproduct(ort1_t); + angle=acosf(cosinus); + if(sinus<0.f) angle= -angle; + +} + +IC void axis_angleB(const Fmatrix& m, const Fvector& axis,float& angle){ + + Fvector ort1,ort2; + if(!(fis_zero(axis.z)&&fis_zero(axis.y))){ + ort1.set(0.f,-axis.z,axis.y); + ort2.crossproduct(axis,ort1); + } + else{ + ort1.set(0.f,1.f,0.f); + ort2.crossproduct(axis,ort1); + } + ort1.normalize(); + ort2.normalize(); + Fvector ort1_t; + m.transform_dir(ort1_t,ort1); + Fvector ort_r; + float pr1,pr2; + pr1=ort1.dotproduct(ort1_t); + pr2=ort2.dotproduct(ort1_t); + if(pr1==0.f&&pr2==0.f){angle=0.f;return;} + ort_r.set(pr1*ort1.x+pr2*ort2.x, + pr1*ort1.y+pr2*ort2.y, + pr1*ort1.z+pr2*ort2.z); + + ort_r.normalize(); + float cosinus=ort1.dotproduct(ort_r); + float sinus=ort2.dotproduct(ort_r); + angle=acosf(cosinus); + if(sinus<0.f) angle= -angle; +} + + + +IC void axis_angleA(const Fmatrix& m, const Fvector& axis,float& angle){ + + Fvector ort1,ort2,axis_t; + m.transform_dir(axis_t,axis); + if(!(fis_zero(axis_t.z)&&fis_zero(axis_t.y))){ + ort1.set(0.f,-axis_t.z,axis_t.y); + ort2.crossproduct(axis_t,ort1); + } + else{ + ort1.set(0.f,1.f,0.f); + ort2.crossproduct(axis_t,ort1); + } + ort1.normalize(); + ort2.normalize(); + Fvector ort1_t; + m.transform_dir(ort1_t,ort1); + Fvector ort_r; + float pr1,pr2; + pr1=ort1.dotproduct(ort1_t); + pr2=ort2.dotproduct(ort1_t); + if(pr1==0.f&&pr2==0.f){angle=0.f;return;} + ort_r.set(pr1*ort1.x+pr2*ort2.x, + pr1*ort1.y+pr2*ort2.y, + pr1*ort1.z+pr2*ort2.z); + + ort_r.normalize(); + float cosinus=ort1.dotproduct(ort_r); + float sinus=ort2.dotproduct(ort_r); + angle=acosf(cosinus); + if(sinus<0.f) angle= -angle; + //if(angle>M_PI) angle=angle-2.f*M_PI; + //if(angle<-M_PI) angle=angle+2.f*M_PI; +} +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHJointDestroyInfo.cpp b/src/xrGameLA/PHJointDestroyInfo.cpp new file mode 100644 index 000000000..20b0fc515 --- /dev/null +++ b/src/xrGameLA/PHJointDestroyInfo.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "PHJointDestroyInfo.h" +#include "PhysicsCommon.h" +#include "MathUtils.h" +CPHJointDestroyInfo::CPHJointDestroyInfo(float break_force,float break_torque) +{ + //m_bone_id=bone_id; + m_sq_break_force=break_force*break_force; + m_sq_break_torque=break_torque*break_torque; + dVectorSetZero(m_joint_feedback.f1); + dVectorSetZero(m_joint_feedback.f2); + dVectorSetZero(m_joint_feedback.t1); + dVectorSetZero(m_joint_feedback.t2); + m_breaked=false; +} + +bool CPHJointDestroyInfo::Update() +{ + dReal sq_breack_force=m_sq_break_force/phBreakCommonFactor; + if(dDOT(m_joint_feedback.f1,m_joint_feedback.f1)>sq_breack_force) + { + m_breaked=true; + return true; + } + if(dDOT(m_joint_feedback.f2,m_joint_feedback.f2)>sq_breack_force) + { + m_breaked=true; + return true; + } + if(dDOT(m_joint_feedback.t1,m_joint_feedback.t1)>sq_breack_force) + { + m_breaked=true; + return true; + } + if(dDOT(m_joint_feedback.t2,m_joint_feedback.t2)>sq_breack_force) + { + m_breaked=true; + return true; + } + return false; +} \ No newline at end of file diff --git a/src/xrGameLA/PHJointDestroyInfo.h b/src/xrGameLA/PHJointDestroyInfo.h new file mode 100644 index 000000000..9ef64a560 --- /dev/null +++ b/src/xrGameLA/PHJointDestroyInfo.h @@ -0,0 +1,25 @@ +#ifndef PHJOINT_DESTROY_INFO_H +#define PHJOINT_DESTROY_INFO_H + +#include "ode_include.h" + +class CPHJointDestroyInfo +{ +friend class CPHShellSplitterHolder; +friend class CPHShell; +dJointFeedback m_joint_feedback; +float m_sq_break_force; +float m_sq_break_torque; +u16 m_end_element; +u16 m_end_joint; +bool m_breaked; +public: + CPHJointDestroyInfo (float break_force,float break_torque); +IC dJointFeedback* JointFeedback (){return &m_joint_feedback;} + +IC bool Breaked (){return m_breaked;}; + bool Update (); +}; + + +#endif PHJOINT_DESTROY_INFO_H \ No newline at end of file diff --git a/src/xrGameLA/PHMoveStorage.cpp b/src/xrGameLA/PHMoveStorage.cpp new file mode 100644 index 000000000..d36e2ad33 --- /dev/null +++ b/src/xrGameLA/PHMoveStorage.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "phmovestorage.h" +#pragma warning(disable:4995) +#pragma warning(disable:4267) +#include "../../xrODE/ode/src/collision_kernel.h" +#pragma warning(default:4995) +#pragma warning(default:4267) +struct dxGeomTransform : public dxGeom { + dxGeom *obj; // object that is being transformed + int cleanup; // 1 to destroy obj when destroyed + int infomode; // 1 to put Tx geom in dContactGeom g1 + + // cached final object transform (body tx + relative tx). this is set by + // computeAABB(), and it is valid while the AABB is valid. + dVector3 final_pos; + dMatrix3 final_R; + dxGeomTransform::dxGeomTransform (dSpaceID space) : dxGeom (space,1) + { + type = dGeomTransformClass; + obj = 0; + cleanup = 0; + infomode = 0; + dSetZero (final_pos,4); + dRSetIdentity (final_R); + } +}; +void CPHPositionsPairs::Positions(const Fvector* &p0,const Fvector* &p1) +{ + CODEGeom *g=*geom; + if(g->is_transformed_bt()) + { + g->geometry_transform()->recomputeAABB(); + p0=(const Fvector*)dGeomGetUserData(g->geom())->last_pos; + p1=(const Fvector*)((dxGeomTransform*)g->geometry_transform())->final_pos; + } + else + { + p1=(const Fvector*)dGeomGetPosition(g->geometry_transform()); + p0=(const Fvector*)dGeomGetUserData(g->geometry_transform())->last_pos; + } + +} \ No newline at end of file diff --git a/src/xrGameLA/PHMoveStorage.h b/src/xrGameLA/PHMoveStorage.h new file mode 100644 index 000000000..99aa15923 --- /dev/null +++ b/src/xrGameLA/PHMoveStorage.h @@ -0,0 +1,55 @@ +#ifndef PHMOVESTORAGE_H +#define PHMOVESTORAGE_H +#include "phgeometryowner.h" +//DEFINE_VECTOR(dReal *&,POSITIONS_STORAGE,POSITIONS_I); + +class CPHPositionsPairs +{ + GEOM_I geom; +public: + CPHPositionsPairs(GEOM_I i) + { + geom=i; + } + void Positions(const Fvector *&p0,const Fvector *&p1); + IC CPHPositionsPairs& operator ++ () + { + ++geom; + return *this; + } + IC dGeomID dGeom() + { + return (*geom)->geometry_transform(); + } + IC CPHPositionsPairs& operator ++ (int) + { + geom++; + return *this; + } + IC CPHPositionsPairs& operator = (const CPHPositionsPairs& right) + { + geom=right.geom; + } + IC bool operator == (const CPHPositionsPairs& right ) const + { + return geom==right.geom; + } + IC bool operator != (const CPHPositionsPairs& right ) const + { + return geom!=right.geom; + } +}; + +class CPHMoveStorage +{ + GEOM_STORAGE m_trace_geometries; +public: + typedef CPHPositionsPairs iterator; + IC iterator begin () {return CPHPositionsPairs(m_trace_geometries.begin());} + IC iterator end () {return CPHPositionsPairs(m_trace_geometries.end());} + IC bool empty ()const {return m_trace_geometries.empty();} + void add (CODEGeom* g) {m_trace_geometries.push_back(g);} + void clear () {m_trace_geometries.clear();} +}; + +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHMovementControl.cpp b/src/xrGameLA/PHMovementControl.cpp new file mode 100644 index 000000000..11102baab --- /dev/null +++ b/src/xrGameLA/PHMovementControl.cpp @@ -0,0 +1,1168 @@ +#include "stdafx.h" +#include "../cl_intersect.h" +#include "alife_space.h" +#include "phmovementcontrol.h" +#include "entity.h" +#include "PHDynamicData.h" +#include "Physics.h" +#include "PHAICharacter.h" +#include "PHActorCharacter.h" +#include "PHCapture.h" +#include "ai_space.h" +#include "detail_path_manager.h" +#include "../GameMtlLib.h" +#include "Level.h" +#include "ElevatorState.h" +#include "CalculateTriangle.h" +#include "../../Include/xrRender/Kinematics.h" +#define GROUND_FRICTION 10.0f +#define AIR_FRICTION 0.01f +#define WALL_FRICTION 3.0f +//#define AIR_RESIST 0.001f + +#define def_X_SIZE_2 0.35f +#define def_Y_SIZE_2 0.8f +#define def_Z_SIZE_2 0.35f + +const u64 after_creation_collision_hit_block_steps_number=100; + +CPHMovementControl::CPHMovementControl(CObject* parent) +{ + pObject=parent; + +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&(!!pObject->cName())&&stricmp(PH_DBG_ObjectTrack(),*pObject->cName())==0) + { + Msg("CPHMovementControl::CPHMovementControl %s (constructor) %f,%f,%pObjectf",PH_DBG_ObjectTrack(),pObject->Position().x,pObject->Position().y,pObject->Position().z); + } + +#endif + + m_material =0; + m_capture =NULL; + b_exect_position =true; + m_start_index =0; + eOldEnvironment = peInAir; + eEnvironment = peInAir; + aabb.set (-def_X_SIZE_2,0,-def_Z_SIZE_2, def_X_SIZE_2, def_Y_SIZE_2*2, def_Z_SIZE_2); + ///vFootCenter.set (0,0,0); + //vFootExt.set (0,0,0); + fMass = 100; + fMinCrashSpeed = 12.0f; + fMaxCrashSpeed = 25.0f; + vVelocity.set (0,0,0); + vPosition.set (0,0,0); + vExternalImpulse.set(0,0,0); + bExernalImpulse =false; + fLastMotionMag = 1.f; + SetPathDir (Fvector().set(0,0,1)); + //fAirFriction = AIR_FRICTION; + //fWallFriction = WALL_FRICTION; + //fGroundFriction = GROUND_FRICTION; + //fFriction = fAirFriction; + bIsAffectedByGravity= TRUE; + fActualVelocity = 0; + m_fGroundDelayFactor= 1.f; + gcontact_HealthLost = 0; + + fContactSpeed = 0.f; + fAirControlParam = 0.f; + m_character = NULL; + m_dwCurBox = 0xffffffff; + fCollisionDamageFactor=1.f; + in_dead_area_count =0; +} + +CPHMovementControl::~CPHMovementControl(void) +{ + if(m_character) + m_character->Destroy(); + DeleteCharacterObject(); + xr_delete(m_capture); +} + +//static Fvector old_pos={0,0,0}; +//static bool bFirst=true; +void CPHMovementControl::AddControlVel (const Fvector& vel) +{ + vExternalImpulse.add(vel); + bExernalImpulse=true; +} +void CPHMovementControl::ApplyImpulse(const Fvector& dir,const dReal P) +{ + if(fis_zero(P))return; + Fvector force; + force.set(dir); + force.mul(P/fixed_step); + + AddControlVel(force); + m_character->ApplyImpulse(dir,P); +} +void CPHMovementControl::SetVelocityLimit(float val) +{ + if(m_character)m_character->SetMaximumVelocity(val); +} +float CPHMovementControl::VelocityLimit() +{ + if(!m_character || !m_character->b_exist) return 0.f; + return m_character->GetMaximumVelocity(); +} + +void CPHMovementControl::in_shedule_Update(u32 DT) +{ + if(m_capture) + { + if(m_capture->Failed()) xr_delete(m_capture); + } +} + +void CPHMovementControl::Calculate(Fvector& vAccel,const Fvector& camDir,float /**ang_speed/**/,float jump,float /**dt/**/,bool /**bLight/**/) +{ + Fvector previous_position;previous_position.set(vPosition); + m_character->IPosition(vPosition); + if(bExernalImpulse) + { + + vAccel.add(vExternalImpulse); + m_character->ApplyForce(vExternalImpulse); + vExternalImpulse.set(0.f,0.f,0.f); + + bExernalImpulse=false; + } + //vAccel.y=jump; + float mAccel=vAccel.magnitude(); + m_character->SetCamDir(camDir); + m_character->SetMaximumVelocity(mAccel/10.f); + //if(!fis_zero(mAccel))vAccel.mul(1.f/mAccel); + m_character->SetAcceleration(vAccel); + if(!fis_zero(jump)) m_character->Jump(vAccel); + + m_character->GetSavedVelocity(vVelocity); + fActualVelocity=vVelocity.magnitude(); + //Msg("saved avel %f", fActualVelocity); + gcontact_Was=m_character->ContactWas(); + fContactSpeed=0.f; + const ICollisionDamageInfo* di=m_character->CollisionDamageInfo(); + { + fContactSpeed=di->ContactVelocity(); + + gcontact_Power = fContactSpeed/fMaxCrashSpeed; + + gcontact_HealthLost = 0; + if (fContactSpeed>fMinCrashSpeed) + { + gcontact_HealthLost = + ((fContactSpeed-fMinCrashSpeed))/(fMaxCrashSpeed-fMinCrashSpeed); + } + + } + if(m_character->LastMaterialIDX()!=u16(-1)) + { + const SGameMtl *last_material=GMLib.GetMaterialByIdx(m_character->LastMaterialIDX()); + if(last_material->Flags.test(SGameMtl::flInjurious)) + { + gcontact_HealthLost+=Device.fTimeDelta*last_material->fInjuriousSpeed; + } + } + + //CPhysicsShellHolder * O=di->DamageObject(); + //SCollisionHitCallback* cc= O ? O->get_collision_hit_callback() : NULL; + const ICollisionDamageInfo *cdi=CollisionDamageInfo(); + if(cdi->HitCallback())cdi->HitCallback()->call(static_cast(m_character->PhysicsRefObject()),fMinCrashSpeed,fMaxCrashSpeed,fContactSpeed,gcontact_HealthLost,CollisionDamageInfo()); + + TraceBorder(previous_position); + CheckEnvironment(vPosition); + bSleep=false; + m_character->Reinit(); +} + +void CPHMovementControl::Calculate(const xr_vector& path,float speed, u32& travel_point, float& precision ) +{ +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&(!!pObject->cName())&&stricmp(PH_DBG_ObjectTrack(),*pObject->cName())==0) + { + Msg("CPHMovementControl::Calculate in %s (Object Position) %f,%f,%f",PH_DBG_ObjectTrack(),pObject->Position().x,pObject->Position().y,pObject->Position().z); + Msg("CPHMovementControl::Calculate in %s (CPHMovementControl::vPosition) %f,%f,%f",PH_DBG_ObjectTrack(),vPosition.x,vPosition.y,vPosition.z); + } +#endif + if(!m_character->b_exist) return; + + Fvector new_position; + m_character->IPosition(new_position); + + int index=0;//nearest point + //float distance;//distance + + bool near_line; + m_path_size=path.size(); + Fvector dir; + dir.set(0,0,0); + if(m_path_size==0) + { + speed=0; + vPosition.set(new_position); + } + else if(b_exect_position) + { + m_start_index=travel_point; + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + if((m_path_size-1)>(int)travel_point) + dir.sub(path[travel_point+1].position,path[travel_point].position); + else + dir.sub(path[travel_point].position,new_position); + m_start_index=travel_point; + dir.y=0.f; + dir.normalize_safe(); + vPosition.set(new_position); + m_path_distance=0; + SetPathDir (dir); + vPathPoint.set(vPosition); + + + } + else { + Fvector dif; + + dif.sub(new_position,vPathPoint); + float radius = dif.magnitude()*2.f; + if(m_path_size==1) + { + speed=0.f; + vPosition.set(new_position); //todo - insert it in PathNearestPoint + index=0; + vPathPoint.set(path[0].position); + Fvector _d; + _d.sub(path[0].position,new_position); + SetPathDir (_d); + m_path_distance=GetPathDir().magnitude(); + if(m_path_distance>EPS) + { + Fvector _d = GetPathDir(); + _d.mul(1.f/m_path_distance); + SetPathDir(_d); + } + near_line=false; + } + else + { + m_path_distance=dInfinity; + near_line=true; + if(m_start_indexradius) + { + m_start_index=0; + PathNearestPoint(path,new_position,index,near_line); + } + vPosition.set(new_position);//for PathDirLine && PathDirPoint + if(near_line) PathDIrLine(path,index,m_path_distance,precision,dir); + else PathDIrPoint(path,index,m_path_distance,precision,dir); + + + travel_point=(u32)index; + m_start_index=index; + if(fis_zero(speed)) dir.set(0,0,0); + } + + } + + dir.y=0.f; + //VERIFY(!(fis_zero(dir.magnitude())&&!fis_zero(speed))); + dir.normalize_safe(); + + ///////////////////////////////////////////////////////////////// + if(bExernalImpulse) + { + + //vAccel.add(vExternalImpulse); + Fvector V; + V.set(dir); + //V.mul(speed*fMass/fixed_step); + V.mul(speed*10.f); + V.add(vExternalImpulse); + m_character->ApplyForce(vExternalImpulse); + speed=V.magnitude(); + + if(!fis_zero(speed)) + { + dir.set(V); + dir.mul(1.f/speed); + } + speed/=10.f; + vExternalImpulse.set(0.f,0.f,0.f); + bExernalImpulse=false; + } + ///////////////////////// + //if(!PhyssicsOnlyMode()){ + // Fvector v;//m_character->GetVelocity(v); + // v.mul(dir,speed); + // SetVelocity(v);//hk + // + //} + ///////////////////////// + m_character->SetMaximumVelocity(speed); + m_character->SetAcceleration(dir); + ////////////////////////////////////////////////////// + m_character->GetSmothedVelocity(vVelocity); + fActualVelocity=vVelocity.magnitude(); + + gcontact_Was=m_character->ContactWas(); + const ICollisionDamageInfo* di=m_character->CollisionDamageInfo(); + fContactSpeed=0.f; + { + fContactSpeed=di->ContactVelocity(); + gcontact_Power = fContactSpeed/fMaxCrashSpeed; + gcontact_HealthLost = 0; + if (fContactSpeed>fMinCrashSpeed) + { + gcontact_HealthLost = + ((fContactSpeed-fMinCrashSpeed))/(fMaxCrashSpeed-fMinCrashSpeed); + } + } + + CheckEnvironment(vPosition); + bSleep=false; + b_exect_position=false; + //m_character->Reinit(); +} + +void CPHMovementControl::PathNearestPoint(const xr_vector &path, //in path + const Fvector &new_position, //in position + int &index, //in start from; out nearest + bool &near_line //out type + ) +{ + + Fvector from_first,from_second,dir; + bool after_line=true;//to check first point + + Fvector path_point,vtemp; + float temp; + + for(int i=0;icName())&&stricmp(PH_DBG_ObjectTrack(),*pObject->cName())==0) + { + Msg("CPHMovementControl::Calculate out %s (Object Position) %f,%f,%f",PH_DBG_ObjectTrack(),pObject->Position().x,pObject->Position().y,pObject->Position().z); + Msg("CPHMovementControl::Calculate out %s (CPHMovementControl::vPosition) %f,%f,%f",PH_DBG_ObjectTrack(),vPosition.x,vPosition.y,vPosition.z); + } +#endif + return; +} + + + +void CPHMovementControl::PathNearestPointFindUp(const xr_vector &path, //in path + const Fvector &new_position, //in position + int &index, //in start from; out nearest + float radius, //out m_path_distance in exit radius + bool &near_line //out type + ) +{ + + Fvector from_first,from_second,dir; + bool after_line=true;//to check first point + + Fvector path_point,vtemp; + float temp; + dir.set (0,0,1); + + for(int i=m_start_index;iradius) break;//exit test + after_line=false; + + } + else //after first + { + if(from_second_dir<0.f) //befor second && after first = near line + { + vtemp.set(dir); + vtemp.mul(from_first_dir); + path_point.add(vtemp,first); + vtemp.sub(path_point,new_position); + temp=vtemp.magnitude(); + if(tempradius) break;//exit test + } + else //after second = after this line + { + after_line=true; + if(from_second.magnitude()>radius) break;//exit test + } + } + } + + if(m_path_distance==dInfinity && i==m_path_size-1) + { + + R_ASSERT2 (after_line,"Must be after line"); + vtemp .sub (new_position,path[i].position) ; + m_path_distance =vtemp.magnitude () ; + SetPathDir (dir); + vPathPoint .set (path[i].position) ; + index =i ; + near_line =false ; + } + + + return; +} + + +void CPHMovementControl::PathNearestPointFindDown(const xr_vector &path, //in path + const Fvector &new_position, //in position + int &index, //in start from; out nearest + float radius, //out m_path_distance in exit radius + bool &near_line //out type + ) +{ + + Fvector from_first,from_second,dir; + bool after_line=true;//to check first point + + Fvector path_point,vtemp; + float temp; + //(going down) + dir.set(0,0,1); + for(int i=m_start_index;i>1;--i) + { + const Fvector &first=path[i-1].position, &second=path[i].position; + from_first.sub(new_position,first); + from_second.sub(new_position,second); + dir.sub(second,first); + dir.normalize_safe(); + float from_first_dir=from_first.dotproduct(dir); + float from_second_dir=from_second.dotproduct(dir); + + if(from_second_dir>0.f)//befor this line + { + temp=from_second.magnitude(); + if(after_line)//after previous line && befor this line = near second point (going down) + { + if(tempradius) break;//exit test + after_line=false; + + } + else //after second + { + + if(from_first_dir>0.f) //after second && before first = near line (going down) + { + vtemp.set(dir); + vtemp.mul(from_second_dir); + path_point.add(second,vtemp); //from_second_dir <0.f !! + vtemp.sub(path_point,new_position); + temp=vtemp.magnitude(); + if(tempradius) break;//exit test + } + else //after first = after this line(going down) + { + after_line=true; + if(from_first.magnitude()>radius) break;//exit test + } + } + } + + if(m_path_distance==dInfinity && i==1) + { + + R_ASSERT2 (after_line,"Must be after line"); + vtemp.sub (new_position,path[i].position); + m_path_distance =vtemp.magnitude(); + SetPathDir (dir); + vPathPoint.set (path[i].position); + index =i; + near_line =false; + } + + + return; +} + +void CPHMovementControl::CorrectPathDir (const Fvector &real_path_dir,const xr_vector & path,int index,Fvector &corrected_path_dir) +{ + const float epsilon=0.1f; + float plane_motion=dXZMag(real_path_dir); + if(fis_zero(plane_motion,epsilon)) + { + if(!fis_zero(plane_motion,EPS)) + { + corrected_path_dir.set(real_path_dir); + corrected_path_dir.y=0.f; + corrected_path_dir.mul(1.f/plane_motion); + } + else if(index!=m_path_size-1) + { + corrected_path_dir.sub(path[index+1].position,path[index].position); + corrected_path_dir.normalize_safe(); + CorrectPathDir(corrected_path_dir,path,index+1,corrected_path_dir); + } + else + { + corrected_path_dir.set(real_path_dir); + } + } + else + { + corrected_path_dir.set(real_path_dir); + } +} +void CPHMovementControl::PathDIrLine(const xr_vector &path, int index, float distance, float precesition, Fvector &dir ) +{ + + Fvector to_path_point; + Fvector corrected_path_dir;CorrectPathDir(GetPathDir(),path,index,corrected_path_dir); + to_path_point.sub(vPathPoint,vPosition); //_new position + float mag=to_path_point.magnitude(); + if(magFootRadius())to_path_point.mul(precesition); + else to_path_point.mul(mag*precesition); + dir.add(corrected_path_dir,to_path_point); + dir.normalize_safe(); +} + +void CPHMovementControl::PathDIrPoint(const xr_vector &path, int index, float distance, float precesition, Fvector &dir ) +{ + Fvector to_path_point; + Fvector corrected_path_dir;CorrectPathDir(GetPathDir(),path,index,corrected_path_dir); + to_path_point.sub(vPathPoint,vPosition); //_new position + float mag=to_path_point.magnitude(); + + if(magEPS) + { + if(tangent.dotproduct(dir)<0.f)tangent.invert(); + } + else + { + if(tangent.dotproduct(corrected_path_dir)<0.f)tangent.invert(); + } + + if(mag>FootRadius())to_path_point.mul(precesition); + else to_path_point.mul(mag*precesition); + dir.add(tangent,to_path_point); + dir.normalize_safe(); +} +void CPHMovementControl::SetActorRestrictorRadius(CPHCharacter::ERestrictionType rt,float r) +{ + if(m_character&&eCharacterType==actor) + static_cast(m_character)->SetRestrictorRadius(rt,r); +} +void CPHMovementControl::Load (LPCSTR section){ + + //capture + + //strcpy(m_capture_bone,pSettings->r_string(section,"capture_bone")); + + Fbox bb; + + // m_PhysicMovementControl: BOX + Fvector vBOX1_center= pSettings->r_fvector3 (section,"ph_box1_center" ); + Fvector vBOX1_size = pSettings->r_fvector3 (section,"ph_box1_size" ); + bb.set (vBOX1_center,vBOX1_center); bb.grow(vBOX1_size); + SetBox (1,bb); + + // m_PhysicMovementControl: BOX + Fvector vBOX0_center= pSettings->r_fvector3 (section,"ph_box0_center" ); + Fvector vBOX0_size = pSettings->r_fvector3 (section,"ph_box0_size" ); + bb.set (vBOX0_center,vBOX0_center); bb.grow(vBOX0_size); + SetBox (0,bb); + + //// m_PhysicMovementControl: Foots + //Fvector vFOOT_center= pSettings->r_fvector3 (section,"ph_foot_center" ); + //Fvector vFOOT_size = pSettings->r_fvector3 (section,"ph_foot_size" ); + //bb.set (vFOOT_center,vFOOT_center); bb.grow(vFOOT_size); + //SetFoots (vFOOT_center,vFOOT_size); + + // m_PhysicMovementControl: Crash speed and mass + float cs_min = pSettings->r_float (section,"ph_crash_speed_min" ); + float cs_max = pSettings->r_float (section,"ph_crash_speed_max" ); + float mass = pSettings->r_float (section,"ph_mass" ); + xr_token retrictor_types[]={ + { "actor", CPHCharacter::rtActor}, + { "medium_monster", CPHCharacter::rtMonsterMedium}, + { "stalker", CPHCharacter::rtStalker }, + { "none", CPHCharacter::rtNone }, + { 0, 0} + }; + + if(pSettings->line_exist(section,"actor_restrictor")) + SetRestrictionType(CPHCharacter::ERestrictionType(pSettings->r_token(section,"actor_restrictor",retrictor_types))); + fCollisionDamageFactor=READ_IF_EXISTS(pSettings,r_float,section,"ph_collision_damage_factor",fCollisionDamageFactor); + R_ASSERT3(fCollisionDamageFactor<=1.f,"ph_collision_damage_factor >1.",section); + SetCrashSpeeds (cs_min,cs_max); + SetMass (mass); + + + // m_PhysicMovementControl: Frictions + //float af, gf, wf; + //af = pSettings->r_float (section,"ph_friction_air" ); + //gf = pSettings->r_float (section,"ph_friction_ground"); + //wf = pSettings->r_float (section,"ph_friction_wall" ); + //SetFriction (af,wf,gf); + + // BOX activate +// ActivateBox (0); +} + +void CPHMovementControl::CheckEnvironment(const Fvector &/**V/**/){ + eOldEnvironment=eEnvironment; + switch (m_character->CheckInvironment()){ +case peOnGround : eEnvironment=peOnGround;break; +case peInAir : eEnvironment=peInAir ;break; +case peAtWall : eEnvironment=peAtWall ;break; + } +} + +void CPHMovementControl::GroundNormal(Fvector & norm) +{ +if(m_character&&m_character->b_exist)m_character->GroundNormal(norm); +else norm.set(0.f,1.f,0.f); + +} + +void CPHMovementControl::SetEnvironment( int enviroment,int old_enviroment){ + switch(enviroment){ + case 0: eEnvironment=peOnGround; + break; + case 1: eEnvironment=peAtWall; + break; + case 2: eEnvironment=peInAir; + } + switch(old_enviroment){ + case 0: eOldEnvironment=peOnGround; + break; + case 1: eOldEnvironment=peAtWall; + break; + case 2: eOldEnvironment=peInAir; + } +} +void CPHMovementControl::SetPosition(const Fvector &P){ +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&(!!pObject->cName())&&stricmp(PH_DBG_ObjectTrack(),*pObject->cName())==0) + { + Msg("CPHMovementControl::SetPosition %s (Object Position) %f,%f,%f",PH_DBG_ObjectTrack(),pObject->Position().x,pObject->Position().y,pObject->Position().z); + Msg("CPHMovementControl::SetPosition %s (CPHMovementControl::vPosition) %f,%f,%f",PH_DBG_ObjectTrack(),vPosition.x,vPosition.y,vPosition.z); + } +#endif + vPosition.set (P); m_character->SetPosition(vPosition); + +} +bool CPHMovementControl:: TryPosition (Fvector& pos) +{ + +VERIFY_BOUNDARIES2(pos,phBoundaries,m_character->PhysicsRefObject(),"CPHMovementControl::TryPosition arqument pos"); + +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&(!!pObject->cName())&&stricmp(PH_DBG_ObjectTrack(),*pObject->cName())==0) + { + Msg("CPHMovementControl::TryPosition %s (Object Position) %f,%f,%f",PH_DBG_ObjectTrack(),pObject->Position().x,pObject->Position().y,pObject->Position().z); + Msg("CPHMovementControl::TryPosition %s (CPHMovementControl::vPosition) %f,%f,%f",PH_DBG_ObjectTrack(),vPosition.x,vPosition.y,vPosition.z); + } +#endif + if (m_character->b_exist) { + bool ret = m_character->TryPosition(pos,b_exect_position)&&!bExernalImpulse; + m_character->GetPosition(vPosition); + return (ret); + } + + vPosition.set (pos); + return (true); +} + + +void CPHMovementControl::GetPosition (Fvector &P) +{ +VERIFY_BOUNDARIES2(P,phBoundaries,m_character->PhysicsRefObject(),"CPHMovementControl::GetPosition arqument pos"); + +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&(!!pObject->cName())&&stricmp(PH_DBG_ObjectTrack(),*pObject->cName())==0) + { + Msg("CPHMovementControl::GetPosition %s (Object Position) %f,%f,%f",PH_DBG_ObjectTrack(),pObject->Position().x,pObject->Position().y,pObject->Position().z); + Msg("CPHMovementControl::GetPosition %s (CPHMovementControl::vPosition) %f,%f,%f",PH_DBG_ObjectTrack(),vPosition.x,vPosition.y,vPosition.z); + } +#endif + P.set (vPosition); +VERIFY_BOUNDARIES2(vPosition,phBoundaries,m_character->PhysicsRefObject(),"CPHMovementControl::GetPosition out pos"); +} + +void CPHMovementControl::AllocateCharacterObject(CharacterType type) +{ + switch(type) + { + case actor: m_character = new CPHActorCharacter() ; break; + case ai: m_character = new CPHAICharacter() ; break; + } + eCharacterType=type; + m_character->SetMas(fMass); + m_character->SetPosition(vPosition); +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&(!!pObject->cName())&&stricmp(PH_DBG_ObjectTrack(),*pObject->cName())==0) + { + Msg("CPHMovementControl::AllocateCharacterObject %s (Object Position) %f,%f,%f",PH_DBG_ObjectTrack(),pObject->Position().x,pObject->Position().y,pObject->Position().z); + Msg("CPHMovementControl::AllocateCharacterObject %s (CPHMovementControl::vPosition) %f,%f,%f",PH_DBG_ObjectTrack(),vPosition.x,vPosition.y,vPosition.z); + } +#endif +} + +void CPHMovementControl::PHCaptureObject(CPhysicsShellHolder* object) +{ +if(m_capture) return; + +if(!object||!object->PPhysicsShell()||!object->m_pPhysicsShell->isActive()) return; +m_capture= new CPHCapture(m_character, + object + ); +} + +void CPHMovementControl::PHCaptureObject(CPhysicsShellHolder* object,u16 element) +{ + if(m_capture) return; + + if(!object||!object->PPhysicsShell()||!object->PPhysicsShell()->isActive()) return; + m_capture= new CPHCapture(m_character, + object, + element + ); +} + +Fvector CPHMovementControl::PHCaptureGetNearestElemPos(const CPhysicsShellHolder* object) +{ + R_ASSERT3((object->m_pPhysicsShell != NULL), "NO Phisics Shell for object ", *object->cName()); + + CPhysicsElement *ph_elem = object->m_pPhysicsShell->NearestToPoint(vPosition); + + Fvector v; + ph_elem->GetGlobalPositionDynamic(&v); + + return v; +} + +Fmatrix CPHMovementControl::PHCaptureGetNearestElemTransform(CPhysicsShellHolder* object) +{ + CPhysicsElement *ph_elem = object->m_pPhysicsShell->NearestToPoint(vPosition); + + Fmatrix m; + ph_elem->GetGlobalTransformDynamic(&m); + + return m; +} + + +void CPHMovementControl::PHReleaseObject() +{ + if(m_capture) m_capture->Release(); +} + +void CPHMovementControl::DestroyCharacter() +{ + m_character->Destroy(); + xr_delete(m_capture); + //xr_delete(m_character); +} + +void CPHMovementControl::DeleteCharacterObject() +{ + xr_delete(m_character); + xr_delete(m_capture); +} + +void CPHMovementControl::JumpV(const Fvector &jump_velocity) +{ + m_character->Enable(); + m_character->Jump(jump_velocity); +} + +void CPHMovementControl::Jump(const Fvector &end_point, float time) +{ + //vPosition + Jump(smart_cast(m_character->PhysicsRefObject())->Position(),end_point,time); +} + + + +void CPHMovementControl::Jump(const Fvector &start_point,const Fvector &end_point, float time) +{ + Fvector velosity; + velosity.sub(end_point,start_point); + TransferenceToThrowVel(velosity,time,ph_world->Gravity()); + JumpV(velosity); +} +float CPHMovementControl::Jump(const Fvector &end_point) +{ + float time =JumpMinVelTime(end_point); + Jump(smart_cast(m_character->PhysicsRefObject())->Position(),end_point,time); + return time; +} +void CPHMovementControl::GetJumpMinVelParam(Fvector &min_vel,float &time,JumpType &type,const Fvector &end_point) +{ + time =JumpMinVelTime(end_point); + GetJumpParam(min_vel,type,end_point,time); +} + +float CPHMovementControl::JumpMinVelTime(const Fvector &end_point) +{ + return ThrowMinVelTime(Fvector().sub(end_point,smart_cast(m_character->PhysicsRefObject())->Position()),ph_world->Gravity()); +} + +void CPHMovementControl::GetJumpParam(Fvector &velocity, JumpType &type,const Fvector &end_point, float time) +{ + Fvector velosity;velosity.sub(smart_cast(m_character->PhysicsRefObject())->Position(),end_point); + TransferenceToThrowVel(velosity,time,ph_world->Gravity()); + if(velocity.y<0.f) + { + type=jtStrait; + return; + } + float rise_time=velosity.y/ph_world->Gravity(); + if(_abs(rise_time-time)time) + { + type=jtStrait; + } + else + { + type=jtCurved; + } + +} + +void CPHMovementControl::SetMaterial(u16 material) +{ + m_material=material; + if(m_character) + { + m_character->SetMaterial(material); + } +} +void CPHMovementControl::CreateCharacter() +{ + dVector3 size={aabb.x2-aabb.x1,aabb.y2-aabb.y1,aabb.z2-aabb.z1}; + m_character->Create(size); + m_character->SetMaterial(m_material); + m_character->SetAirControlFactor(fAirControlParam); +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&(!!pObject->cName())&&stricmp(PH_DBG_ObjectTrack(),*pObject->cName())==0) + { + Msg("CPHMovementControl::CreateCharacter %s (Object Position) %f,%f,%f",PH_DBG_ObjectTrack(),pObject->Position().x,pObject->Position().y,pObject->Position().z); + Msg("CPHMovementControl::CreateCharacter %s (CPHMovementControl::vPosition) %f,%f,%f",PH_DBG_ObjectTrack(),vPosition.x,vPosition.y,vPosition.z); + } +#endif + m_character->SetPosition(vPosition); + m_character->SetCollisionDamageFactor(fCollisionDamageFactor*fCollisionDamageFactor); + trying_times[0]=trying_times[1]=trying_times[2]=trying_times[3]=u32(-1); + trying_poses[0].set(vPosition); + trying_poses[1].set(vPosition); + trying_poses[2].set(vPosition); + trying_poses[3].set(vPosition); +} +CPHSynchronize* CPHMovementControl::GetSyncItem() +{ + if(m_character) return smart_cast(m_character); + else return 0; +} +void CPHMovementControl::Freeze() +{ + if(m_character)m_character->Freeze(); +} +void CPHMovementControl::UnFreeze() +{ + if(m_character) m_character->UnFreeze(); +} + +void CPHMovementControl::ActivateBox (DWORD id, BOOL Check/*false*/) +{ + if (Check && (m_dwCurBox == id)) return; + m_dwCurBox = id; + aabb.set(boxes[id]); + if(!m_character||!m_character->b_exist) return; + dVector3 size={aabb.x2-aabb.x1,aabb.y2-aabb.y1,aabb.z2-aabb.z1}; + m_character->SetBox(size); + //Fvector v; + //m_character->GetVelocity(v); + //m_character->Destroy(); + //CreateCharacter(); + //m_character->SetVelocity(v); + //m_character->SetPosition(vPosition); +} +void CPHMovementControl::InterpolateBox (DWORD id, float k) +{ + if (m_dwCurBox == id) return; + if(!m_character||!m_character->b_exist) return; + dVector3 size={aabb.x2-aabb.x1,aabb.y2-aabb.y1,aabb.z2-aabb.z1}; + dVector3 to_size={boxes[id].x2-boxes[id].x1,boxes[id].y2-boxes[id].y1,boxes[id].z2-boxes[id].z1}; + dVectorInterpolate(size,to_size,k); + m_character->SetBox(size); +} +void CPHMovementControl::ApplyHit(const Fvector& dir,const dReal P,ALife::EHitType hit_type) +{ + if(hit_type==ALife::eHitTypeExplosion||hit_type==ALife::eHitTypeWound) + ApplyImpulse(dir,P); +} + +void CPHMovementControl::SetFrictionFactor(float f) +{ + m_character->FrictionFactor()=f; +} +float CPHMovementControl::GetFrictionFactor() +{ + return m_character->FrictionFactor(); +} +void CPHMovementControl::MulFrictionFactor(float f) +{ + m_character->FrictionFactor()*=f; +} + +CElevatorState *CPHMovementControl::ElevatorState() +{ + if(!m_character || !m_character->b_exist)return NULL; + return m_character->ElevatorState(); + //m_character->SetElevator() +} + + +struct STraceBorderQParams +{ + CPHMovementControl* m_movement; + const Fvector &m_dir; + STraceBorderQParams(CPHMovementControl* movement,const Fvector &dir): + m_dir(dir) + { + m_movement=movement ; + } + STraceBorderQParams& operator = (STraceBorderQParams& p) {VERIFY(FALSE);return p;} +}; + +BOOL CPHMovementControl::BorderTraceCallback(collide::rq_result& result, LPVOID params) +{ + STraceBorderQParams& p = *(STraceBorderQParams*)params; + u16 mtl_idx = GAMEMTL_NONE_IDX; + CDB::TRI* T = NULL; + if(result.O){ + return true; + }else{ + //получить треугольник и узнать его материал + T = Level().ObjectSpace.GetStaticTris()+result.element; + mtl_idx = T->material; + } + VERIFY (T); + SGameMtl* mtl = GMLib.GetMaterialByIdx(mtl_idx); + if(mtl->Flags.test(SGameMtl::flInjurious)) + { + Fvector tri_norm; + GetNormal(T,tri_norm); + if(p.m_dir.dotproduct(tri_norm)<0.f)p.m_movement->in_dead_area_count++; + else p.m_movement->in_dead_area_count--; + } + return true; +} + +void CPHMovementControl::TraceBorder(const Fvector &prev_position) +{ + + const Fvector &from_pos =prev_position ; + const Fvector &to_position =vPosition ; + Fvector dir; dir .sub(to_position,from_pos) ; + float sq_mag = dir.square_magnitude() ; + if(sq_mag==0.f) return ; + float mag=_sqrt(sq_mag) ; + dir.mul(1.f/mag) ; + collide::ray_defs RD (from_pos,dir,mag,0,collide::rqtStatic) ; + VERIFY (!fis_zero(RD.dir.square_magnitude())) ; + + STraceBorderQParams p(this,dir); + storage.r_clear (); + g_pGameLevel->ObjectSpace.RayQuery(storage,RD,BorderTraceCallback,&p,NULL,static_cast(m_character->PhysicsRefObject())); +} + +void CPHMovementControl:: UpdateObjectBox(CPHCharacter *ach) +{ + if(!m_character||!m_character->b_exist) return; + if(!ach||!ach->b_exist) return; + Fvector cbox; + PKinematics(pObject->Visual())->CalculateBones(); + pObject->BoundingBox().getradius(cbox); + const Fvector &pa =cast_fv(dBodyGetPosition(ach->get_body())); + const Fvector &p =cast_fv(dBodyGetPosition(m_character->get_body())); + Fvector2 poses_dir;poses_dir.set(p.x-pa.x,p.z-pa.z);float plane_dist=poses_dir.magnitude(); + if(plane_dist>2.f) return; + if(plane_dist>EPS_S)poses_dir.mul(1.f/plane_dist); + Fvector2 plane_cam;plane_cam.set(Device.vCameraDirection.x,Device.vCameraDirection.z);plane_cam.normalize_safe(); + Fvector2 plane_i;plane_i.set(pObject->XFORM().i.x,pObject->XFORM().i.z); + Fvector2 plane_k;plane_k.set(pObject->XFORM().k.x,pObject->XFORM().k.z); + float R=_abs(poses_dir.dotproduct(plane_i)*cbox.x)+_abs(poses_dir.dotproduct(plane_k)*cbox.z); + R*=poses_dir.dotproduct(plane_cam); //(poses_dir.x*plane_cam.x+poses_dir.y*plane_cam.z); + Calculate(Fvector().set(0,0,0),Fvector().set(1,0,0),0,0,0,0); + m_character->SetObjectRadius(R); + ach->ChooseRestrictionType(CPHCharacter::rtStalker,1.f,m_character); + m_character->UpdateRestrictionType(ach); +} + +void CPHMovementControl::SetPathDir( const Fvector& v) +{ + _vPathDir = v; + + if(_abs(_vPathDir.x)>1000 || _abs(_vPathDir.y)>1000 || _abs(_vPathDir.z)>1000) + { + Log ("_vPathDir", _vPathDir ); + + } + VERIFY2 ( _abs(_vPathDir.x)<1000," incorrect SetPathDir "); + +} diff --git a/src/xrGameLA/PHMovementControl.h b/src/xrGameLA/PHMovementControl.h new file mode 100644 index 000000000..3568b8c1b --- /dev/null +++ b/src/xrGameLA/PHMovementControl.h @@ -0,0 +1,279 @@ +#pragma once +#ifndef CPHMOVEMENT_CONTROL_H +#define CPHMOVEMENT_CONTROL_H + +#include "PHCharacter.h" +#include "MathUtils.h" +namespace ALife { + enum EHitType; +}; + +namespace DetailPathManager { + struct STravelPathPoint; +}; + +class CPHAICharacter; +class CPHSimpleCharacter; +class CPHCapture; +class CPHSynchronize; +class ICollisionDamageInfo; +class CElevatorState; + +class CPHMovementControl +{ + collide::rq_results storage; + +static const int path_few_point=10; + +public: +CElevatorState *ElevatorState (); +void in_shedule_Update( u32 DT ); +void PHCaptureObject(CPhysicsShellHolder* object); +void PHCaptureObject(CPhysicsShellHolder* object,u16 element); +CPHCapture* PHCapture (){return m_capture;} +CPHCharacter* PHCharacter (){return m_character;} +void PHReleaseObject (); +Fvector PHCaptureGetNearestElemPos(const CPhysicsShellHolder* object); +Fmatrix PHCaptureGetNearestElemTransform(CPhysicsShellHolder* object); +void SetMaterial(u16 material); +void SetAirControlParam(float param){fAirControlParam=param;} +void SetActorRestrictorRadius(CPHCharacter::ERestrictionType rt, float r); +void SetRestrictionType(CPHCharacter::ERestrictionType rt){if(m_character)m_character->SetRestrictionType(rt);} +void SetActorMovable(bool v){if(m_character)m_character->SetActorMovable(v);} +void SetForcedPhysicsControl(bool v){if(m_character)m_character->SetForcedPhysicsControl(v);} +bool ForcedPhysicsControl(){return m_character&&m_character->ForcedPhysicsControl();} +void UpdateObjectBox(CPHCharacter *ach); +enum JumpType +{ + jtStrait, //end point before uppermost point + jtCurved, //end point after uppermost point + jtHigh //end point is uppermost point +}; +void JumpV(const Fvector &jump_velocity); +void Jump(const Fvector &start_point, const Fvector &end_point, float time); +void Jump(const Fvector &end_point, float time); +float Jump(const Fvector &end_point); +bool JumpState(){return (m_character&&m_character->b_exist&&m_character->IsEnabled()&&m_character->JumpState());}; +/// +bool PhyssicsOnlyMode(){return m_character&& m_character->b_exist&&m_character->IsEnabled()&&(m_character->JumpState()||m_character->ForcedPhysicsControl());} +void GetJumpMinVelParam(Fvector &min_vel,float &time,JumpType &type,const Fvector &end_point); //returns vector of velocity of jump with minimal start speed + //in min_vel and correspondent jump time in time +float JumpMinVelTime(const Fvector &end_point); // return time of jump with min start speed +// input: end_point and time; return velocity and type of jump +void GetJumpParam(Fvector &velocity, JumpType &type,const Fvector &end_point, float time); +bool b_exect_position; +int in_dead_area_count; +public: + + enum EEnvironment + { + peOnGround, + peAtWall, + peInAir + }; + enum CharacterType + { + actor, + ai + }; + bool isOutBorder (){return in_dead_area_count>0;} + void setOutBorder (){in_dead_area_count=1;} +private: + void TraceBorder (const Fvector &previous_position); + void CheckEnvironment (const Fvector& V); + + CharacterType eCharacterType; + CPHCharacter* m_character; + CPHCapture * m_capture; + + float m_fGroundDelayFactor; + BOOL bIsAffectedByGravity; + //------------------------------ + CObject* pObject; + EEnvironment eOldEnvironment; + EEnvironment eEnvironment; + Fbox aabb; + Fbox boxes [4]; + + u32 trying_times[4]; + Fvector trying_poses[4]; + DWORD m_dwCurBox; + + float fMass; + float fMinCrashSpeed; + float fMaxCrashSpeed; + float fCollisionDamageFactor; + float fAirControlParam; + + Fvector vVelocity; + Fvector vPosition; + + Fvector vPathPoint; + Fvector _vPathDir; + int m_path_size; + int m_start_index; + + + float m_path_distance; + u16 m_material; + + float fLastMotionMag; + + float fActualVelocity; + float fContactSpeed; + float fLastUpdateTime; + + +public: + Fvector vExternalImpulse; + bool bExernalImpulse; + BOOL bSleep; + + BOOL gcontact_Was; // Приземление + float gcontact_Power; // Насколько сильно ударились + float gcontact_HealthLost; // Скоко здоровья потеряли + +public: + void AllocateCharacterObject (CharacterType type) ; + void DeleteCharacterObject () ; + void CreateCharacter () ; + void DestroyCharacter () ; + void Load (LPCSTR section) ; +#ifdef DEBUG + void dbg_Draw(){ + if(m_character) + m_character->OnRender(); + }; +#endif + + void SetPLastMaterialIDX (u16* p){m_character->SetPLastMaterialIDX(p);} + dBodyID GetBody ( ) {if(m_character) return m_character->get_body(); else return NULL;} + const Fvector& GetVelocity ( ) { return vVelocity; } + const Fvector& GetPathDir ( ) { return _vPathDir; } + void SetPathDir ( const Fvector& v); + + + void GetCharacterVelocity (Fvector& velocity ) {if(m_character)m_character->GetVelocity(velocity); else velocity.set(0.f,0.f,0.f);} + float GetVelocityMagnitude () { return vVelocity.magnitude(); } + float GetVelocityActual () { return fActualVelocity; } + float GetXZVelocityActual () { return dXZMag(vVelocity);} + float GetActVelProj (const Fvector & dir){return vVelocity.dotproduct(dir);} + float GetActVelInGoingDir (){float r= GetActVelProj(GetPathDir());return r>0.f ? r : 0.f;} + float GetXZActVelInGoingDir (){ + float r= dXZDot(GetPathDir(),vVelocity); + return r>EPS_L ? r : 0.f; + } + void GetSmoothedVelocity (Fvector& v){if(m_character)m_character->GetSmothedVelocity(v);else v.set(0,0,0);} + float GetContactSpeed () { return fContactSpeed; } + void GroundNormal (Fvector &norm) ; + CPHSynchronize* GetSyncItem () ; + void Freeze () ; + void UnFreeze () ; + void SetVelocity (float x, float y, float z) {SetVelocity(Fvector().set(x,y,z));} + void SetVelocity (const Fvector& v) {vVelocity.set(v);SetCharacterVelocity(v);} + void SetCharacterVelocity (const Fvector& v) {if(m_character)m_character->SetVelocity(v);} + void SetPhysicsRefObject (CPhysicsShellHolder* ref_object){m_character->SetPhysicsRefObject(ref_object);}; + + void CalcMaximumVelocity (Fvector& /**dest/**/, Fvector& /**accel/**/, float /**friction/**/){}; + void CalcMaximumVelocity (float& /**dest/**/, float /**accel/**/, float /**friction/**/){}; + void ActivateBox (DWORD id, BOOL Check = false); + bool ActivateBoxDynamic (DWORD id,int num_it=9,int num_steps=5,float resolve_depth=0.01f); + void InterpolateBox (DWORD id,float k); + EEnvironment Environment ( ) { return eEnvironment; } + EEnvironment OldEnvironment ( ) { return eOldEnvironment; } + const Fbox& Box ( ) { return aabb; } + DWORD BoxID ( )const { return m_dwCurBox;} + const Fbox* Boxes ( ) {return boxes;} + float FootRadius ( ) {if(m_character)return m_character->FootRadius(); else return 0.f;}; + void CollisionEnable (BOOL enable){if(m_character) if(enable)m_character->collision_enable();else m_character->collision_disable();} + void SetBox (DWORD id, const Fbox &BB) { boxes[id].set(BB); aabb.set(BB); } + void SetMass (float M) { fMass = M; + if(m_character) + m_character->SetMas(fMass); + } + float GetMass () { return fMass; } + void SetCrashSpeeds (float min, float max) + { fMinCrashSpeed = min; fMaxCrashSpeed = max; } + + void SetPosition (const Fvector &P); + void GetPosition (Fvector &P); + void GetCharacterPosition(Fvector &P) + { m_character->GetPosition(P);} + void InterpolatePosition(Fvector &P) + { + VERIFY(m_character&&m_character->b_exist); + m_character->IPosition(P); + } + bool TryPosition (Fvector& pos); + bool IsCharacterEnabled () {return m_character->IsEnabled()||bExernalImpulse;} + void DisableCharacter (){m_character->Disable();} + void Calculate (Fvector& vAccel,const Fvector& camDir, float ang_speed, float jump, float dt, bool bLight); + void Calculate (const xr_vector& path, //in path + float speed, //in speed + u32& travel_point, //in- travel start, out - current trev point + float& precesition //in- tolerance, out - precesition + ); + void AddControlVel (const Fvector& vel); + void SetVelocityLimit (float val); + float VelocityLimit (); + void PathNearestPoint (const xr_vector &path, //in path + const Fvector &new_position, //in position + int &index, //out nearest + bool &type //out type + ); //return nearest point + void PathNearestPointFindUp(const xr_vector &path, //in path + const Fvector &new_position, //in position + int &index, //out nearest + float radius, //in exit radius + bool &near_line //out type + ); + void PathNearestPointFindDown(const xr_vector &path, //in path + const Fvector &new_position, //in position + int &index, //out nearest + float radius, //in exit radius + bool &near_line //out type + ); + + void PathDIrPoint (const xr_vector &path, //in path + int index, //in index + float distance, //in distance + float precesition,//in precesition + Fvector &dir //out dir + ); + void PathDIrLine (const xr_vector &path, //in path + int index, //in point + float distance, //in distance + float precesition,//in precesition + Fvector &dir //out dir + ); + void CorrectPathDir (const Fvector &real_path_dir,const xr_vector & path,int index,Fvector &corrected_path_dir); + + // void Move (Fvector& Dest, Fvector& Motion, BOOL bDynamic=FALSE){}; + void SetApplyGravity (BOOL flag) { bIsAffectedByGravity=flag;if(m_character&&m_character->b_exist)m_character->SetApplyGravity(flag); } + void GetDeathPosition (Fvector& pos) { m_character->DeathPosition(pos);} + void SetEnvironment ( int enviroment,int old_enviroment); + void SetFrictionFactor (float f); + float GetFrictionFactor (); + void MulFrictionFactor (float f); + void ApplyImpulse (const Fvector& dir,const dReal P) ; + void ApplyHit (const Fvector& dir,const dReal P,ALife::EHitType hit_type) ; + void SetJumpUpVelocity (float velocity) {m_character->SetJupmUpVelocity(velocity);} + void EnableCharacter () {if(m_character&&m_character->b_exist)m_character->Enable();} + void SetOjectContactCallback (ObjectContactCallbackFun* callback){if(m_character)m_character->SetObjectContactCallback(callback);} + void SetFootCallBack (ObjectContactCallbackFun* callback){VERIFY(m_character);m_character->SetWheelContactCallback(callback);} + static BOOL CPHMovementControl::BorderTraceCallback(collide::rq_result& result, LPVOID params); + ObjectContactCallbackFun* ObjectContactCallback(){if(m_character)return m_character->ObjectContactCallBack();else return NULL; } + u16 ContactBone (){return m_character->ContactBone();} + const ICollisionDamageInfo *CollisionDamageInfo ()const {VERIFY(m_character);return m_character->CollisionDamageInfo ();} + void GetDesiredPos (Fvector& dpos) + { + m_character->GetDesiredPosition(dpos); + } + bool CharacterExist() + { + return (m_character && m_character->b_exist); + } + CPHMovementControl(CObject* parent); + ~CPHMovementControl(void); +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHMovementDynamicActivate.cpp b/src/xrGameLA/PHMovementDynamicActivate.cpp new file mode 100644 index 000000000..f88209ea2 --- /dev/null +++ b/src/xrGameLA/PHMovementDynamicActivate.cpp @@ -0,0 +1,442 @@ + +#include "stdafx.h" + +#include "phmovementcontrol.h" + +#include "ExtendedGeom.h" +#include "MathUtils.h" +#include "Physics.h" +#include "Level.h" +#include "../GameMtlLib.h" +#include "PhysicsShellHolder.h" + +extern class CPHWorld *ph_world; +ObjectContactCallbackFun* saved_callback = 0 ; +static float max_depth = 0.f ; + +struct STestCallbackPars +{ + static float calback_friction_factor ; + static float depth_to_use_force ; + static float callback_force_factor ; + static float depth_to_change_softness_pars ; + static float callback_cfm_factor ; + static float callback_erp_factor ; + static float decrement_depth ; + static float max_real_depth ; +}; + + +float STestCallbackPars::calback_friction_factor = 0.0f ; +float STestCallbackPars::depth_to_use_force = 0.3f ; +float STestCallbackPars::callback_force_factor = 10.f ; +float STestCallbackPars::depth_to_change_softness_pars = 0.00f ; +float STestCallbackPars::callback_cfm_factor = world_cfm*0.00001f; +float STestCallbackPars::callback_erp_factor = 1.f ; +float STestCallbackPars::decrement_depth = 0.f ; +float STestCallbackPars::max_real_depth = 0.2f ; +struct STestFootCallbackPars +{ + static float calback_friction_factor ; + static float depth_to_use_force ; + static float callback_force_factor ; + static float depth_to_change_softness_pars ; + static float callback_cfm_factor ; + static float callback_erp_factor ; + static float decrement_depth ; + static float max_real_depth ; +}; + + +float STestFootCallbackPars::calback_friction_factor = 0.3f ; +float STestFootCallbackPars::depth_to_use_force = 0.3f ; +float STestFootCallbackPars::callback_force_factor = 10.f ; +float STestFootCallbackPars::depth_to_change_softness_pars = 0.00f ; +float STestFootCallbackPars::callback_cfm_factor = world_cfm*0.00001f; +float STestFootCallbackPars::callback_erp_factor = 1.f ; +float STestFootCallbackPars::decrement_depth = 0.05f ; +float STestFootCallbackPars::max_real_depth = 0.2f ; +template +void TTestDepthCallback (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + if(saved_callback)saved_callback(do_colide,bo1,c,material_1,material_2); + + if(do_colide&&!material_1->Flags.test(SGameMtl::flPassable) &&!material_2->Flags.test(SGameMtl::flPassable)) + { + float& depth=c.geom.depth; + float test_depth=depth-Pars::decrement_depth; + save_max(max_depth,test_depth); + c.surface.mu*=Pars::calback_friction_factor; + if(test_depth>Pars::depth_to_use_force) + { + float force = Pars::callback_force_factor*ph_world->Gravity(); + dBodyID b1=dGeomGetBody(c.geom.g1); + dBodyID b2=dGeomGetBody(c.geom.g2); + if(b1)dBodyAddForce(b1,c.geom.normal[0]*force,c.geom.normal[1]*force,c.geom.normal[2]*force); + if(b2)dBodyAddForce(b2,-c.geom.normal[0]*force,-c.geom.normal[1]*force,-c.geom.normal[2]*force); + dxGeomUserData* ud1=retrieveGeomUserData(c.geom.g1); + dxGeomUserData* ud2=retrieveGeomUserData(c.geom.g2); + + if(ud1) + { + CPhysicsShell* phsl=ud1->ph_ref_object->PPhysicsShell(); + if(phsl) phsl->Enable(); + } + + if(ud2) + { + CPhysicsShell* phsl=ud2->ph_ref_object->PPhysicsShell(); + if(phsl) phsl->Enable(); + } + + + do_colide=false; + } + else if(test_depth>Pars::depth_to_change_softness_pars) + { + c.surface.soft_cfm=Pars::callback_cfm_factor; + c.surface.soft_erp=Pars::callback_erp_factor; + } + limit_above(depth,Pars::max_real_depth); + } + +} + +ObjectContactCallbackFun* TestDepthCallback = &TTestDepthCallback; +ObjectContactCallbackFun* TestFootDepthCallback = &TTestDepthCallback; +/////////////////////////////////////////////////////////////////////////////////////// +class CVelocityLimiter : + public CPHUpdateObject +{ + dBodyID m_body; +public: + float l_limit; + float y_limit; +private: + dVector3 m_safe_velocity; + dVector3 m_safe_position; +public: + CVelocityLimiter(dBodyID b,float l,float yl) + { + R_ASSERT(b); + m_body=b; + dVectorSet(m_safe_velocity,dBodyGetLinearVel(m_body)); + dVectorSet(m_safe_position,dBodyGetPosition(m_body)); + l_limit=l; + y_limit=yl; + } + virtual ~CVelocityLimiter() + { + Deactivate(); + m_body =0; + } + + + bool VelocityLimit() + { + const float *linear_velocity =dBodyGetLinearVel(m_body); + //limit velocity + bool ret=false; + if(dV_valid(linear_velocity)) + { + dReal mag; + Fvector vlinear_velocity;vlinear_velocity.set(cast_fv(linear_velocity)); + mag=_sqrt(linear_velocity[0]*linear_velocity[0]+linear_velocity[2]*linear_velocity[2]);// + if(mag>l_limit) + { + dReal f=mag/l_limit; + //dBodySetLinearVel(m_body,linear_velocity[0]/f,linear_velocity[1],linear_velocity[2]/f);///f + vlinear_velocity.x/=f;vlinear_velocity.z/=f; + ret=true; + } + mag=_abs(linear_velocity[1]); + if(mag>y_limit) + { + vlinear_velocity.y=linear_velocity[1]/mag*y_limit; + ret=true; + } + dBodySetLinearVel(m_body,vlinear_velocity.x,vlinear_velocity.y,vlinear_velocity.z); + return ret; + } + else + { + dBodySetLinearVel(m_body,m_safe_velocity[0],m_safe_velocity[1],m_safe_velocity[2]); + return true; + } + } + virtual void PhDataUpdate(dReal step) + { + const float *linear_velocity =dBodyGetLinearVel(m_body); + + if(VelocityLimit()) + { + dBodySetPosition(m_body, + m_safe_position[0]+linear_velocity[0]*fixed_step, + m_safe_position[1]+linear_velocity[1]*fixed_step, + m_safe_position[2]+linear_velocity[2]*fixed_step); + } + + if(!dV_valid(dBodyGetPosition(m_body))) + dBodySetPosition(m_body,m_safe_position[0]-m_safe_velocity[0]*fixed_step, + m_safe_position[1]-m_safe_velocity[1]*fixed_step, + m_safe_position[2]-m_safe_velocity[2]*fixed_step); + + + dVectorSet(m_safe_position,dBodyGetPosition(m_body)); + dVectorSet(m_safe_velocity,linear_velocity); + } + + virtual void PhTune(dReal step) + { + + VelocityLimit(); + } + +}; +//////////////////////////////////////////////////////////////////////////////////// +class CGetContactForces: + public CPHUpdateObject +{ + dBodyID m_body; + float m_max_force_self; + float m_max_torque_self; + + float m_max_force_self_y; + float m_max_force_self_sd; + + float m_max_force_others; + float m_max_torque_others; +public: + CGetContactForces(dBodyID b) + { + R_ASSERT(b); + m_body=b; + InitValues(); + } + float mf_slf(){return m_max_force_self;} + float mf_othrs(){return m_max_force_others;} + float mt_slf(){return m_max_torque_self;} + float mt_othrs(){return m_max_torque_others;} + + float mf_slf_y(){return m_max_force_self_y;} + float mf_slf_sd(){return m_max_force_self_sd;} +protected: + virtual void PhTune(dReal step) + { + InitValues(); + int num=dBodyGetNumJoints(m_body); + for(int i=0;inode[1].body; + bool b_body_second=(b_joint->node[1].body==m_body); + dReal* self_force=feedback->f1; + dReal* self_torque=feedback->t1; + dReal* othrers_force=feedback->f2; + dReal* othrers_torque=feedback->t2; + if(b_body_second) + { + other_body=b_joint->node[0].body; + self_force=feedback->f2; + self_torque=feedback->t2; + othrers_force=feedback->f1; + othrers_torque=feedback->t1; + } + + save_max(m_max_force_self,_sqrt(dDOT( self_force,self_force))); + save_max(m_max_torque_self,_sqrt(dDOT( self_torque,self_torque))); + save_max(m_max_force_self_y,_abs(self_force[1])); + save_max(m_max_force_self_sd,_sqrt(self_force[0]*self_force[0]+self_force[2]*self_force[2])); + if(other_body) + { + dVector3 shoulder;dVectorSub(shoulder,dJointGetPositionContact(joint),dBodyGetPosition(other_body)); + dReal shoulder_lenght=_sqrt(dDOT(shoulder,shoulder)); + + save_max(m_max_force_others,_sqrt(dDOT( othrers_force,othrers_force))); + if(!fis_zero(shoulder_lenght)) + save_max(m_max_torque_others,_sqrt(dDOT( othrers_torque,othrers_torque))/shoulder_lenght); + } + } + } + } + +private: + void InitValues() + { + m_max_force_self = 0.f; + m_max_torque_self = 0.f; + m_max_force_others = 0.f; + m_max_torque_others = 0.f; + m_max_force_self_y = 0.f; + m_max_force_self_sd = 0.f; + } +}; +///////////////////////////////////////////////////////////////////////////////////// + +bool CPHMovementControl:: ActivateBoxDynamic(DWORD id,int num_it/*=8*/,int num_steps/*5*/,float resolve_depth/*=0.01f*/) +{ + bool character_exist=CharacterExist(); + if(character_exist&&trying_times[id]!=u32(-1)) + { + Fvector dif;dif.sub(trying_poses[id],cast_fv(dBodyGetPosition(m_character->get_body()))); + if(Device.dwTimeGlobal-trying_times[id]<500&&dif.magnitude()<0.05f) + return false; + } + if(!m_character||m_character->PhysicsRefObject()->PPhysicsShell())return false; + DWORD old_id=BoxID(); + + bool character_disabled=character_exist && !m_character->IsEnabled(); + if(character_exist&&id==old_id)return true; + + if(!character_exist) + { + CreateCharacter(); + } + + //m_PhysicMovementControl->ActivateBox(id); + m_character->CPHObject::activate(); + ph_world->Freeze(); + UnFreeze(); + + saved_callback=ObjectContactCallback(); + SetOjectContactCallback(TestDepthCallback); + SetFootCallBack(TestFootDepthCallback); + max_depth=0.f; + + + + //////////////////////////////////pars/////////////////////////////////////////// +// int num_it=8; +// int num_steps=5; +// float resolve_depth=0.01f; + + + if(!character_exist) + { + num_it=20; + num_steps=1; + resolve_depth=0.1f; + } + /////////////////////////////////////////////////////////////////////// + float fnum_it=float(num_it); + float fnum_steps=float(num_steps); + float fnum_steps_r=1.f/fnum_steps; + + Fvector vel; + Fvector pos; + GetCharacterVelocity(vel); + GetCharacterPosition(pos); + //const Fbox& box =Box(); + float pass= character_exist ? _abs(Box().getradius()-boxes[id].getradius()) : boxes[id].getradius(); + float max_vel=pass/2.f/fnum_it/fnum_steps/fixed_step; + float max_a_vel=M_PI/8.f/fnum_it/fnum_steps/fixed_step; + dBodySetForce(GetBody(),0.f,0.f,0.f); + dBodySetLinearVel(GetBody(),0.f,0.f,0.f); + Calculate(Fvector().set(0,0,0),Fvector().set(1,0,0),0,0,0,0); + CVelocityLimiter vl(GetBody(),max_vel,max_vel); + max_vel=1.f/fnum_it/fnum_steps/fixed_step; + + bool ret=false; + m_character->SwitchOFFInitContact(); + vl.Activate(); + vl.l_limit*=(fnum_it*fnum_steps/5.f); + vl.y_limit=vl.l_limit; +//////////////////////////////////// + for(int m=0;30>m;++m) + { + Calculate(Fvector().set(0,0,0),Fvector().set(1,0,0),0,0,0,0); + EnableCharacter(); + m_character->ApplyForce(0,ph_world->Gravity()*m_character->Mass(),0); + max_depth=0.f; + ph_world->Step(); + if(max_depth < resolve_depth) + { + break; + } + ph_world->CutVelocity(max_vel,max_a_vel); + } + vl.l_limit/=(fnum_it*fnum_steps/5.f); + vl.y_limit=vl.l_limit; +///////////////////////////////////// + + for(int m=0;num_steps>m;++m) + { + float param =fnum_steps_r*(1+m); + InterpolateBox(id,param); + ret=false; + for(int i=0;num_it>i;++i){ + max_depth=0.f; + Calculate(Fvector().set(0,0,0),Fvector().set(1,0,0),0,0,0,0); + EnableCharacter(); + m_character->ApplyForce(0,ph_world->Gravity()*m_character->Mass(),0); + ph_world->Step(); + ph_world->CutVelocity(max_vel,max_a_vel); + if(max_depth < resolve_depth) + { + ret=true; + break; + } + } + if(!ret) break; + } + m_character->SwitchInInitContact(); + vl.Deactivate(); + + ph_world->UnFreeze(); + if(!ret) + { + if(!character_exist)DestroyCharacter(); + else if(character_disabled)m_character->Disable(); + ActivateBox(old_id); + SetVelocity(vel); + dBodyID b=GetBody(); + if(b) + { + dMatrix3 R; + dRSetIdentity (R); + dBodySetAngularVel(b,0.f,0.f,0.f); + dBodySetRotation(b,R); + } + SetPosition(pos); + + //Msg("can not activate!"); + } + else + { + ActivateBox(id); + //Msg("activate!"); + } + + SetOjectContactCallback(saved_callback); + SetVelocity(vel); + saved_callback=0; + if(!ret&&character_exist) + { + trying_times[id]=Device.dwTimeGlobal; + trying_poses[id].set(cast_fv(dBodyGetPosition(m_character->get_body()))); + } + else + { + trying_times[id]=u32(-1); + } + return ret; +} diff --git a/src/xrGameLA/PHNetState.cpp b/src/xrGameLA/PHNetState.cpp new file mode 100644 index 000000000..be058e0ef --- /dev/null +++ b/src/xrGameLA/PHNetState.cpp @@ -0,0 +1,271 @@ +#include "stdafx.h" +#pragma hdrstop + +#include "PHNetState.h" +#include "../../xrNetServer/net_utils.h" + +//////////////////////////////////////8///////////////////////////////////////////////////// + +static void w_vec_q8(NET_Packet& P,const Fvector& vec,const Fvector& min,const Fvector& max) +{ + P.w_float_q8(vec.x,min.x,max.x); + P.w_float_q8(vec.y,min.y,max.y); + P.w_float_q8(vec.z,min.z,max.z); +} +template +static void r_vec_q8(src& P,Fvector& vec,const Fvector& min,const Fvector& max) +{ + vec.x=P.r_float_q8(min.x,max.x); + vec.y=P.r_float_q8(min.y,max.y); + vec.z=P.r_float_q8(min.z,max.z); + + clamp(vec.x,min.x,max.x); + clamp(vec.y,min.y,max.y); + clamp(vec.z,min.z,max.z); +} +static void w_qt_q8(NET_Packet& P,const Fquaternion& q) +{ + //Fvector Q; + //Q.set(q.x,q.y,q.z); + //if(q.w<0.f) Q.invert(); + //P.w_float_q8(Q.x,-1.f,1.f); + //P.w_float_q8(Q.y,-1.f,1.f); + //P.w_float_q8(Q.z,-1.f,1.f); +/////////////////////////////////////////////////// + P.w_float_q8(q.x,-1.f,1.f); + P.w_float_q8(q.y,-1.f,1.f); + P.w_float_q8(q.z,-1.f,1.f); + P.w_float_q8(q.w,-1.f,1.f); + +/////////////////////////////////////////// + + + //P.w_float_q8(q.x,-1.f,1.f); + //P.w_float_q8(q.y,-1.f,1.f); + //P.w_float_q8(q.z,-1.f,1.f); + //P.w(sign()) +} + +template +static void r_qt_q8(src& P,Fquaternion& q) +{ + //// x^2 + y^2 + z^2 + w^2 = 1 + //P.r_float_q8(q.x,-1.f,1.f); + //P.r_float_q8(q.y,-1.f,1.f); + //P.r_float_q8(q.z,-1.f,1.f); + //float w2=1.f-q.x*q.x-q.y*q.y-q.z*q.z; + //w2=w2<0.f ? 0.f : w2; + //q.w=_sqrt(w2); +///////////////////////////////////////////////////// + /////////////////////////////////////////////////// + q.x=P.r_float_q8(-1.f,1.f); + q.y=P.r_float_q8(-1.f,1.f); + q.z=P.r_float_q8(-1.f,1.f); + q.w=P.r_float_q8(-1.f,1.f); + + clamp(q.x,-1.f,1.f); + clamp(q.y,-1.f,1.f); + clamp(q.z,-1.f,1.f); + clamp(q.w,-1.f,1.f); +} + +#ifdef XRGAME_EXPORTS +/////////////////////////////////16//////////////////////////////////////////////////////////////// +static void w_vec_q16(NET_Packet& P,const Fvector& vec,const Fvector& min,const Fvector& max) +{ + P.w_float_q16(vec.x,min.x,max.x); + P.w_float_q16(vec.y,min.y,max.y); + P.w_float_q16(vec.z,min.z,max.z); +} +static void r_vec_q16(NET_Packet& P,Fvector& vec,const Fvector& min,const Fvector& max) +{ + P.r_float_q16(vec.x,min.x,max.x); + P.r_float_q16(vec.y,min.y,max.y); + P.r_float_q16(vec.z,min.z,max.z); + + //clamp(vec.x,min.x,max.x); + //clamp(vec.y,min.y,max.y); + //clamp(vec.z,min.z,max.z); +} +template +static void w_qt_q16(src& P,const Fquaternion& q) +{ + //Fvector Q; + //Q.set(q.x,q.y,q.z); + //if(q.w<0.f) Q.invert(); + //P.w_float_q16(Q.x,-1.f,1.f); + //P.w_float_q16(Q.y,-1.f,1.f); + //P.w_float_q16(Q.z,-1.f,1.f); + /////////////////////////////////////////// + P.w_float_q16(q.x,-1.f,1.f); + P.w_float_q16(q.y,-1.f,1.f); + P.w_float_q16(q.z,-1.f,1.f); + P.w_float_q16(q.w,-1.f,1.f); +} + +static void r_qt_q16(NET_Packet& P,Fquaternion& q) +{ + // x^2 + y^2 + z^2 + w^2 = 1 + //P.r_float_q16(q.x,-1.f,1.f); + //P.r_float_q16(q.y,-1.f,1.f); + //P.r_float_q16(q.z,-1.f,1.f); + //float w2=1.f-q.x*q.x-q.y*q.y-q.z*q.z; + //w2=w2<0.f ? 0.f : w2; + //q.w=_sqrt(w2); +/////////////////////////////////////////////////// + P.r_float_q16(q.x,-1.f,1.f); + P.r_float_q16(q.y,-1.f,1.f); + P.r_float_q16(q.z,-1.f,1.f); + P.r_float_q16(q.w,-1.f,1.f); + + //clamp(q.x,-1.f,1.f); + //clamp(q.y,-1.f,1.f); + //clamp(q.z,-1.f,1.f); + //clamp(q.w,-1.f,1.f); +} +#endif +/////////////////////////////////////////////////////////////////////////////////// +void SPHNetState::net_Export(NET_Packet& P) +{ + P.w_vec3(linear_vel); + //P.w_vec3(angular_vel); + //P.w_vec3(force); + //P.w_vec3(torque); + P.w_vec3(position); + P.w_vec4(*((Fvector4*)&quaternion)); + //P.w_vec4(*((Fvector4*)&previous_quaternion)); + P.w_u8 ((u8)enabled); + +} +template +void SPHNetState::read (src& P) +{ + linear_vel=P.r_vec3(); + angular_vel.set(0.f,0.f,0.f); //P.r_vec3(angular_vel); + force.set(0.f,0.f,0.f); //P.r_vec3(force); + torque.set(0.f,0.f,0.f); //P.r_vec3(torque); + position=P.r_vec3(); + *((Fvector4*)&quaternion)=P.r_vec4(); + previous_quaternion.set(quaternion);//P.r_vec4(*((Fvector4*)&previous_quaternion)); + enabled=!!P.r_u8 (); +} + +void SPHNetState::net_Import(NET_Packet& P) +{ + read(P); +} +void SPHNetState::net_Import(IReader& P) +{ + read(P); +} + +void SPHNetState::net_Save(NET_Packet &P) +{ + net_Export(P); +} + +void SPHNetState::net_Load(NET_Packet &P) +{ + net_Import(P); + previous_position.set(position); +} +void SPHNetState::net_Load(IReader &P) +{ + net_Import(P); + previous_position.set(position); +} +void SPHNetState::net_Save(NET_Packet &P,const Fvector& min,const Fvector& max) +{ + //P.w_vec3(linear_vel); + //P.w_vec3(angular_vel); + //P.w_vec3(force); + //P.w_vec3(torque); + //P.w_vec3(position); + w_vec_q8(P,position,min,max); + w_qt_q8(P,quaternion); + //P.w_vec4(*((Fvector4*)&quaternion)); + //P.w_vec4(*((Fvector4*)&previous_quaternion)); + P.w_u8 ((u8)enabled); +} +template +void SPHNetState::read(src &P,const Fvector& min,const Fvector& max) +{ +VERIFY( !(fsimilar(min.x,max.x)&&fsimilar(min.y,max.y)&&fsimilar(min.z,max.z)) ); + linear_vel.set(0.f,0.f,0.f); + angular_vel.set(0.f,0.f,0.f); + force.set(0.f,0.f,0.f); + torque.set(0.f,0.f,0.f); + r_vec_q8(P,position,min,max); + previous_position.set(position); + r_qt_q8(P,quaternion); + previous_quaternion.set(quaternion); + enabled=!!P.r_u8(); + +} + +void SPHNetState::net_Load(NET_Packet &P,const Fvector& min,const Fvector& max) +{ +VERIFY( !(fsimilar(min.x,max.x)&&fsimilar(min.y,max.y)&&fsimilar(min.z,max.z)) ); + read(P,min,max); +} +void SPHNetState::net_Load(IReader &P,const Fvector& min,const Fvector& max) +{ +VERIFY( !(fsimilar(min.x,max.x)&&fsimilar(min.y,max.y)&&fsimilar(min.z,max.z)) ); + read(P,min,max); +} +SPHBonesData::SPHBonesData() +{ + bones_mask =u64(-1); + root_bone =0; + + Fvector _mn, _mx; + + _mn.set (-100.f,-100.f,-100.f); + _mx.set (100.f,100.f,100.f); + set_min_max (_mn, _mx); +} +void SPHBonesData::net_Save(NET_Packet &P) +{ + P.w_u64 (bones_mask); + P.w_u16 (root_bone); + + P.w_vec3 (get_min()); + P.w_vec3 (get_max()); + P.w_u16 ((u16)bones.size());//bones number; + PHNETSTATE_I i=bones.begin(),e=bones.end(); + for(;e!=i;i++) + { + (*i).net_Save(P,get_min(),get_max()); + } + // this comment is added by Dima (correct me if this is wrong) + // if we call 2 times in a row StateWrite then we get different results + // WHY??? + // bones.clear (); +} + +void SPHBonesData::net_Load(NET_Packet &P) +{ + bones.clear (); + + bones_mask =P.r_u64(); + root_bone =P.r_u16(); + Fvector _mn, _mx; + P.r_vec3 (_mn); + P.r_vec3 (_mx); + set_min_max (_mn, _mx); + + u16 bones_number =P.r_u16();//bones number /**/ + for(int i=0;i + void read ( src& P); +template + void read ( src& P,const Fvector& min,const Fvector& max); +}; + +DEFINE_VECTOR(SPHNetState,PHNETSTATE_VECTOR,PHNETSTATE_I); + +struct SPHBonesData +{ + u64 bones_mask; + u16 root_bone; + PHNETSTATE_VECTOR bones; + Fvector m_min; + Fvector m_max; +public: + SPHBonesData () ; + void net_Save ( NET_Packet& P); + void net_Load ( NET_Packet& P); + void net_Load ( IReader& P); + void set_min_max (const Fvector& _min, const Fvector& _max); + const Fvector& get_min () const {return m_min;} + const Fvector& get_max () const {return m_max;} +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHObject.cpp b/src/xrGameLA/PHObject.cpp new file mode 100644 index 000000000..1a4071525 --- /dev/null +++ b/src/xrGameLA/PHObject.cpp @@ -0,0 +1,257 @@ +#include "stdafx.h" +#include "Physics.h" +#include "PHObject.h" +#include "PHWorld.h" +#include "PHMoveStorage.h" +#include "dRayMotions.h" +#include "PHCollideValidator.h" +#ifdef DEBUG +#include "phdebug.h" +#endif +extern CPHWorld* ph_world; + +CPHObject::CPHObject () : ISpatial(g_SpatialSpacePhysic) +{ + m_flags.flags = 0; + spatial.type |= STYPE_PHYSIC; + m_island.Init (); + m_check_count =0; + CPHCollideValidator::InitObject (*this); +} + +void CPHObject::activate() +{ + R_ASSERT2(dSpacedGeom(),"trying to activate destroyed or not created object!"); + if(m_flags.test(st_activated))return; + if(m_flags.test(st_freezed)) {UnFreeze();return;} + if(m_flags.test(st_recently_deactivated))remove_from_recently_deactivated(); + ph_world->AddObject(this); + vis_update_activate(); + m_flags.set(st_activated,TRUE); +} + +void CPHObject::EnableObject(CPHObject* obj) +{ + activate(); +} + +void CPHObject::deactivate() +{ + if(!m_flags.test(st_activated))return; + VERIFY2(m_island.IsActive(),"can not do it during processing"); + ph_world->RemoveObject(PH_OBJECT_I(this)); + vis_update_deactivate(); + m_flags.set(st_activated,FALSE); +} + +void CPHObject::put_in_recently_deactivated() +{ + VERIFY(!m_flags.test(st_activated)&&!m_flags.test(st_freezed)); + if(m_flags.test(st_recently_deactivated))return; + m_check_count=u8(ph_tri_clear_disable_count); + m_flags.set(st_recently_deactivated,TRUE); + ph_world->AddRecentlyDisabled(this); +} +void CPHObject::remove_from_recently_deactivated() +{ + if(!m_flags.test(st_recently_deactivated))return; + m_check_count=0; + m_flags.set(st_recently_deactivated,FALSE); + ph_world->RemoveFromRecentlyDisabled(PH_OBJECT_I(this)); +} +void CPHObject::check_recently_deactivated() +{ + if(m_check_count==0){ + ClearRecentlyDeactivated(); + remove_from_recently_deactivated(); + } + else m_check_count--; +} +void CPHObject::spatial_move() +{ + get_spatial_params(); + ISpatial::spatial_move(); + m_flags.set(st_dirty,TRUE); +} + +void CPHObject::Collide() +{ + if(m_flags.test(fl_ray_motions)) + { + CPHMoveStorage* tracers=MoveStorage(); + CPHMoveStorage::iterator I=tracers->begin(),E=tracers->end(); + for(;E!=I;I++) + { + const Fvector *from=0, *to=0; + Fvector dir; + I.Positions(from,to); + if(from->x==-dInfinity) continue; + dir.sub(*to,*from); + float magnitude=dir.magnitude(); + if(magnitudeq_ray(ph_world->r_spatial,0,STYPE_PHYSIC,*from,dir,magnitude);//|ISpatial_DB::O_ONLYFIRST +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawRayMotions)) + { + DBG_OpenCashedDraw(); + DBG_DrawLine(*from,Fvector().add(*from,Fvector().mul(dir,magnitude)),D3DCOLOR_XRGB(0,255,0)); + DBG_ClosedCashedDraw(30000); + } + +#endif + qResultVec& result=ph_world->r_spatial; + qResultIt i=result.begin(),e=result.end(); + for(;i!=e;++i) { + CPHObject* obj2=static_cast(*i); + if(obj2==this || !obj2->m_flags.test(st_dirty)) continue; + dGeomID motion_ray=ph_world->GetMotionRayGeom(); + dGeomRayMotionSetGeom(motion_ray,I.dGeom()); + dGeomRayMotionsSet(motion_ray,(const dReal*) from,(const dReal*)&dir,magnitude); + NearCallback(this,obj2,motion_ray,obj2->dSpacedGeom()); + } + } + } + CollideDynamics (); +/////////////////////////////// + if(CPHCollideValidator::DoCollideStatic(*this)) CollideStatic(dSpacedGeom(),this); + m_flags.set(st_dirty,FALSE); +} +void CPHObject:: CollideDynamics () +{ + g_SpatialSpacePhysic->q_box (ph_world->r_spatial,0,STYPE_PHYSIC,spatial.sphere.P,AABB); + qResultVec& result=ph_world->r_spatial ; + qResultIt i=result.begin(),e=result.end(); + for(;i!=e;++i) { + CPHObject* obj2=static_cast(*i); + if(obj2==this || !obj2->m_flags.test(st_dirty)) continue; + if(CPHCollideValidator::DoCollide(*this,*obj2)) NearCallback(this,obj2,dSpacedGeom(),obj2->dSpacedGeom()); + } +} +void CPHObject::reinit_single() +{ + IslandReinit (); + qResultVec& result=ph_world->r_spatial ; + qResultIt i=result.begin(),e=result.end(); + for(;i!=e;++i) + { + CPHObject* obj=static_cast(*i); + obj->IslandReinit(); + } + result.clear_not_free(); + dJointGroupEmpty(ContactGroup); + ContactFeedBacks.empty(); + ContactEffectors.empty(); +} + +void CPHObject:: step_prediction (float time) +{ + //general idea: + //perform normal step by time as local as possible for this object then return world to + //the pervious state + +} +bool CPHObject::step_single(dReal step) +{ + + CollideDynamics (); + bool ret=!m_island.IsObjGroun (); + if(ret) + { + //PhTune (step); + IslandStep (step); + reinit_single (); + //PhDataUpdate (step); + spatial_move (); + CollideDynamics (); + ret =!m_island.IsObjGroun (); + } + reinit_single (); + return ret ; +} + +void CPHObject:: step( float time ) //it is still not a true step for object because it collide the object only not subsequent collision is doing +{ + ph_world->r_spatial.clear_not_free (); + reinit_single (); + Collide (); + IslandStep ( time ); + reinit_single (); +} + +bool CPHObject:: DoCollideObj () +{ + CollideDynamics (); + bool ret=m_island.IsObjGroun (); + reinit_single(); + return ret; +} + +void CPHObject::FreezeContent() +{ + R_ASSERT(!m_flags.test(st_freezed)); + m_flags.set(st_freezed,TRUE); + m_flags.set(st_activated,FALSE); + vis_update_deactivate(); +} +void CPHObject::UnFreezeContent() +{ + R_ASSERT(m_flags.test(st_freezed)); + m_flags.set(st_freezed,FALSE); + m_flags.set(st_activated,TRUE); + vis_update_activate(); +} + +void CPHObject::spatial_register() +{ + get_spatial_params(); + ISpatial::spatial_register(); + m_flags.set(st_dirty,TRUE); +} + +void CPHObject::collision_disable() +{ + ISpatial::spatial_unregister(); +} +void CPHObject::collision_enable() +{ + ISpatial::spatial_register(); +} + +void CPHObject::Freeze() +{ + if(!m_flags.test(st_activated))return; + ph_world->RemoveObject(this); + ph_world->AddFreezedObject(this); + FreezeContent(); +} + +void CPHObject::UnFreeze() +{ + if(!m_flags.test(st_freezed)) return; + UnFreezeContent(); + ph_world->RemoveFreezedObject(this); + ph_world->AddObject(this); +} + + + +CPHUpdateObject::CPHUpdateObject() +{ + b_activated=false; +} + +void CPHUpdateObject::Activate() +{ + if(b_activated)return; + ph_world->AddUpdateObject(this); + b_activated=true; +} + +void CPHUpdateObject::Deactivate() +{ + if(!b_activated)return; + ph_world->RemoveUpdateObject(this); + b_activated=false; +} + diff --git a/src/xrGameLA/PHObject.h b/src/xrGameLA/PHObject.h new file mode 100644 index 000000000..3352d727c --- /dev/null +++ b/src/xrGameLA/PHObject.h @@ -0,0 +1,142 @@ +#pragma once +#ifndef CPHOBJECT +#define CPHOBJECT +#include "../../xrcdb/ispatial.h" +#include "PHItemList.h" +#include "PHIsland.h" +typedef u32 CLClassBits; +typedef u32 CLBits; +class ISpatial; +DEFINE_VECTOR(ISpatial*,qResultVec,qResultIt) +class CPHObject; +class CPHUpdateObject; +class CPHMoveStorage; +class CPHSynchronize; +typedef void CollideCallback(CPHObject* obj1,CPHObject* obj2, dGeomID o1, dGeomID o2); + +class CPHObject : + public ISpatial +{ +#ifdef DEBUG + friend void DBG_DrawPHObject(CPHObject* obj); +#endif + DECLARE_PHLIST_ITEM(CPHObject) + + Flags8 m_flags; + + enum{ + st_activated =(1<<0), + st_freezed =(1<<1), + st_dirty =(1<<2), + st_net_interpolation =(1<<3), + fl_ray_motions =(1<<4), + st_recently_deactivated =(1<<5) + }; + + CPHIsland m_island; + CLBits m_collide_bits; + u8 m_check_count; + _flags m_collide_class_bits; + +public: + enum ECastType + { + tpNotDefinite, + tpShell, + tpCharacter, + tpStaticShell + }; +protected: + Fvector AABB; +protected: + + virtual dGeomID dSpacedGeom () =0; + virtual void get_spatial_params () =0; + virtual void spatial_register () ; + void SetRayMotions () {m_flags.set(fl_ray_motions,TRUE);} + void UnsetRayMotions () {m_flags.set(fl_ray_motions,FALSE);} + + void SetPrefereExactIntegration () {m_island.SetPrefereExactIntegration();} + + + + CPHObject* SelfPointer () {return this;} +public: + IC BOOL IsRayMotion () {return m_flags.test(fl_ray_motions);} + void IslandReinit () {m_island.Unmerge();} + void IslandStep (dReal step) {m_island.Step(step);} + void MergeIsland (CPHObject* obj) {m_island.Merge(&obj->m_island);} + CPHIsland& Island () {return m_island;} + dWorldID DActiveWorld () {return m_island.DActiveWorld();} + CPHIsland* DActiveIsland () {return m_island.DActiveIsland();} + dWorldID DWorld () {return m_island.DWorld();} + + virtual void FreezeContent () ; + virtual void UnFreezeContent () ; + virtual void EnableObject (CPHObject* obj) ; + virtual bool DoCollideObj () ; + virtual bool step_single (dReal step) ; + void reinit_single () ; + void step_prediction (float time) ; + void step (float time) ; + virtual void PhDataUpdate (dReal step) =0; + virtual void PhTune (dReal step) =0; + virtual void spatial_move () ; + virtual void InitContact (dContact* c,bool& do_collide,u16 /*material_idx_1*/,u16 /*material_idx_2*/) =0; + virtual void CutVelocity (float l_limit,float a_limit) {}; + + void Freeze () ; + void UnFreeze () ; + IC bool IsFreezed () {return !!(m_flags.test(st_freezed));} + void NetInterpolationON () {m_flags.set(st_net_interpolation,TRUE);} + void NetInterpolationOFF () {m_flags.set(st_net_interpolation,TRUE);} + bool NetInterpolation () {return !!(m_flags.test(st_net_interpolation));} + virtual u16 get_elements_number () = 0; + virtual CPHSynchronize *get_element_sync (u16 element) = 0; + //virtual void StepFrameUpdate(dReal step)=0; + + + CPHObject () ; + void activate () ; + IC bool is_active () const {return !!m_flags.test(st_activated)/*b_activated*/;} + void deactivate () ; + void put_in_recently_deactivated () ; + void remove_from_recently_deactivated() ; + void check_recently_deactivated () ; + void collision_disable () ; + void collision_enable () ; +virtual void ClearRecentlyDeactivated () {;} +virtual void Collide () ; +virtual void near_callback (CPHObject* obj) {;} +virtual void RMotionsQuery (qResultVec &res) {;} +virtual CPHMoveStorage* MoveStorage () {return NULL;} +virtual ECastType CastType (){return tpNotDefinite;} +virtual void vis_update_activate () {} +virtual void vis_update_deactivate () {} +IC CLBits& collide_bits () {return m_collide_bits;} +IC _flags& collide_class_bits () {return m_collide_class_bits;} +IC const CLBits& collide_bits ()const {return m_collide_bits;} +IC const _flags& collide_class_bits ()const {return m_collide_class_bits;} + void CollideDynamics () ; +}; + + + +class CPHUpdateObject +{ + DECLARE_PHLIST_ITEM(CPHUpdateObject) + bool b_activated ; + +public: + CPHUpdateObject () ; + virtual ~CPHUpdateObject() {Deactivate();} + void Activate () ; + void Deactivate () ; +IC bool IsActive () {return b_activated;} + virtual void PhDataUpdate (dReal step) =0; + virtual void PhTune (dReal step) =0; +}; + +DEFINE_PHITEM_LIST(CPHObject,PH_OBJECT_STORAGE,PH_OBJECT_I) +DEFINE_PHITEM_LIST(CPHUpdateObject,PH_UPDATE_OBJECT_STORAGE,PH_UPDATE_OBJECT_I) +#endif//CPHOBJECT \ No newline at end of file diff --git a/src/xrGameLA/PHReqComparer.h b/src/xrGameLA/PHReqComparer.h new file mode 100644 index 000000000..75fff602c --- /dev/null +++ b/src/xrGameLA/PHReqComparer.h @@ -0,0 +1,24 @@ +#pragma once + +class CPHScriptCondition ; +class CPHScriptAction ; +class CPHScriptObjectCondition ; +class CPHScriptObjectAction ; +class CPHScriptObjectConditionN ; +class CPHScriptObjectActionN ; +class CPHScriptGameObjectCondition ; +class CPHScriptGameObjectAction ; +class CPHConstForceAction ; +class CPHReqComparerV +{ + public: + virtual bool compare(const CPHScriptCondition* v) const {return false;} + virtual bool compare(const CPHScriptAction* v) const {return false;} + virtual bool compare(const CPHScriptObjectCondition* v) const {return false;} + virtual bool compare(const CPHScriptObjectAction* v) const {return false;} + virtual bool compare(const CPHScriptObjectConditionN* v) const {return false;} + virtual bool compare(const CPHScriptObjectActionN* v) const {return false;} + virtual bool compare(const CPHScriptGameObjectAction* v) const {return false;} + virtual bool compare(const CPHScriptGameObjectCondition* v)const {return false;} + virtual bool compare(const CPHConstForceAction* v) const {return false;} +}; \ No newline at end of file diff --git a/src/xrGameLA/PHScriptCall.cpp b/src/xrGameLA/PHScriptCall.cpp new file mode 100644 index 000000000..349fec158 --- /dev/null +++ b/src/xrGameLA/PHScriptCall.cpp @@ -0,0 +1,174 @@ +#include "pch_script.h" +#include "PHCommander.h" +#include "script_space_forward.h" +#include "script_callback_ex.h" +#include "../xr_object.h" +#include "PHScriptCall.h" + +/* +IC bool compare_safe(const luabind::object &o1 , const luabind::object &o2) +{ + return (o1.type()==LUA_TNIL && o2.type()==LUA_TNIL) || o1==o2; +} +/**/ + +CPHScriptCondition::CPHScriptCondition(const luabind::functor &func) +{ + m_lua_function = new luabind::functor(func); +} + +CPHScriptCondition::CPHScriptCondition(const CPHScriptCondition &func) +{ + m_lua_function = new luabind::functor(*func.m_lua_function); +} + +CPHScriptCondition::~CPHScriptCondition() +{ + xr_delete (m_lua_function); +} + +bool CPHScriptCondition::is_true() +{ + return (*m_lua_function)(); +} + +bool CPHScriptCondition::obsolete()const +{ + return false; +} + +// +CPHScriptAction::CPHScriptAction(const luabind::functor &func) +{ + b_obsolete = false; + m_lua_function = new luabind::functor(func); +} + +CPHScriptAction::CPHScriptAction(const CPHScriptAction &action) +{ + b_obsolete = action.b_obsolete; + m_lua_function = new luabind::functor(*action.m_lua_function); +} + +CPHScriptAction::~CPHScriptAction() +{ + xr_delete(m_lua_function); +} + +void CPHScriptAction::run() +{ + (*m_lua_function)(); + b_obsolete=true; +} + +bool CPHScriptAction::obsolete()const +{ + return b_obsolete; +} + +///////////////////////////////////////////////////////////////////////////////////////////// +CPHScriptObjectAction::CPHScriptObjectAction(const luabind::object &lua_object, LPCSTR method) +{ + b_obsolete = false; + m_lua_object = new luabind::object(lua_object); + m_method_name = method; +} + +CPHScriptObjectAction::CPHScriptObjectAction(const CPHScriptObjectAction &object) +{ + b_obsolete = object.b_obsolete; + m_lua_object = new luabind::object(*object.m_lua_object); + m_method_name = object.m_method_name; +} + +CPHScriptObjectAction::~CPHScriptObjectAction() +{ + xr_delete(m_lua_object); +} + +bool CPHScriptObjectAction::compare(const CPHScriptObjectAction* v) const +{ + return m_method_name==v->m_method_name&&compare_safe(*m_lua_object,*(v->m_lua_object)); +} +void CPHScriptObjectAction::run() +{ + luabind::call_member(*m_lua_object,*m_method_name); + b_obsolete=true; +} + +bool CPHScriptObjectAction::obsolete()const +{ + return b_obsolete; +} + +// +CPHScriptObjectCondition::CPHScriptObjectCondition(const luabind::object &lua_object, LPCSTR method) +{ + m_lua_object = new luabind::object(lua_object); + m_method_name = method; +} + +CPHScriptObjectCondition::CPHScriptObjectCondition (const CPHScriptObjectCondition &object) +{ + m_lua_object = new luabind::object(*object.m_lua_object); + m_method_name = object.m_method_name; +} + +CPHScriptObjectCondition::~CPHScriptObjectCondition() +{ + xr_delete(m_lua_object); +} +bool CPHScriptObjectCondition::compare(const CPHScriptObjectCondition* v) const +{ + return m_method_name==v->m_method_name&&compare_safe(*m_lua_object,*(v->m_lua_object)); +} + +bool CPHScriptObjectCondition::is_true() +{ + return luabind::call_member(*m_lua_object,*m_method_name); +} +bool CPHScriptObjectCondition::obsolete()const +{ + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +CPHScriptObjectActionN::CPHScriptObjectActionN( const luabind::object &object,const luabind::functor &functor) +{ + m_callback.set(functor,object); +} + +CPHScriptObjectActionN::~CPHScriptObjectActionN() +{ + m_callback.clear(); +} + +void CPHScriptObjectActionN::run() +{ + m_callback(); + b_obsolete=true; +} + +bool CPHScriptObjectActionN::obsolete()const +{ + return b_obsolete; +} + +CPHScriptObjectConditionN::CPHScriptObjectConditionN(const luabind::object &object,const luabind::functor &functor) +{ + m_callback.set(functor,object); +} + +CPHScriptObjectConditionN::~CPHScriptObjectConditionN() +{ + m_callback.clear(); +} + +bool CPHScriptObjectConditionN::is_true() +{ + return m_callback(); +} +bool CPHScriptObjectConditionN::obsolete()const +{ + return false; +} \ No newline at end of file diff --git a/src/xrGameLA/PHScriptCall.h b/src/xrGameLA/PHScriptCall.h new file mode 100644 index 000000000..4e4410596 --- /dev/null +++ b/src/xrGameLA/PHScriptCall.h @@ -0,0 +1,171 @@ +#pragma once +#include "PHReqComparer.h" + +//template<> +//IC bool compare_safe(const functor<>& f1,const functor<>& f2) +//{ +// f1.typ +//} + +class CPHScriptCondition: + public CPHCondition, + public CPHReqComparerV +{ + luabind::functor *m_lua_function; + + CPHScriptCondition (const CPHScriptCondition &func); + +public: + CPHScriptCondition (const luabind::functor &func) ; + virtual ~CPHScriptCondition () ; + virtual bool is_true () ; + virtual bool obsolete () const ; + virtual bool compare (const CPHReqComparerV* v) const {return v->compare(this);} + virtual bool compare (const CPHScriptCondition*v) const {return v->m_lua_function==m_lua_function;} + ///virtual bool is_equal (CPHReqBase* v) ; + //virtual bool is_relative (CPHReqBase* v) ; + +}; + +class CPHScriptAction : + public CPHAction, + public CPHReqComparerV +{ + bool b_obsolete ; + luabind::functor *m_lua_function; +public: + CPHScriptAction (const luabind::functor &func) ; + CPHScriptAction (const CPHScriptAction &action) ; + virtual ~CPHScriptAction () ; + virtual void run () ; + virtual bool obsolete () const ; + virtual bool compare (const CPHReqComparerV* v) const {return v->compare(this);} + virtual bool compare (const CPHScriptAction* v) const {return *m_lua_function==*(v->m_lua_function);} +}; + + +class CPHScriptObjectCondition: + public CPHCondition, + public CPHReqComparerV +{ + luabind::object *m_lua_object; + shared_str m_method_name; +public: + CPHScriptObjectCondition (const luabind::object &lua_object, LPCSTR method) ; + CPHScriptObjectCondition (const CPHScriptObjectCondition &object) ; + virtual ~CPHScriptObjectCondition () ; + virtual bool is_true () ; + virtual bool obsolete () const ; + virtual bool compare (const luabind::object* v) const {return *m_lua_object==*v;} + virtual bool compare (const CPHReqComparerV* v) const {return v->compare(this);} + virtual bool compare (const CPHScriptObjectCondition* v) const ; +}; + +class CPHScriptObjectAction : + public CPHAction, + public CPHReqComparerV +{ + bool b_obsolete ; + luabind::object *m_lua_object; + shared_str m_method_name; +public: + CPHScriptObjectAction (const luabind::object &lua_object, LPCSTR method) ; + CPHScriptObjectAction (const CPHScriptObjectAction &object) ; + virtual ~CPHScriptObjectAction () ; + virtual void run () ; + virtual bool obsolete () const ; + virtual bool compare (const luabind::object* v) const {return *m_lua_object==*v;} + virtual bool compare (const CPHReqComparerV* v) const {return v->compare(this);} + virtual bool compare (const CPHScriptObjectAction* v) const ; +}; +////////////////////////////////////////////////////////////////////////////////////////// + +class CPHScriptObjectConditionN: + public CPHCondition, + public CPHReqComparerV +{ + CScriptCallbackEx m_callback; +public: + CPHScriptObjectConditionN ( const luabind::object &object,const luabind::functor &functor) ; + virtual ~CPHScriptObjectConditionN () ; + virtual bool is_true () ; + virtual bool obsolete () const ; + virtual bool compare (const luabind::object* v) const {return m_callback==(*v);} + virtual bool compare (const CPHReqComparerV* v) const {return v->compare(this);} + virtual bool compare (const CPHScriptObjectConditionN* v) const {return m_callback==v->m_callback;} +}; + +class CPHScriptObjectActionN : + public CPHAction, + public CPHReqComparerV +{ + bool b_obsolete ; + CScriptCallbackEx m_callback; +public: + CPHScriptObjectActionN ( const luabind::object &object,const luabind::functor &functor); + virtual ~CPHScriptObjectActionN () ; + virtual void run () ; + virtual bool obsolete () const ; + virtual bool compare (const luabind::object* v) const {return m_callback==*v;} + virtual bool compare (const CPHReqComparerV* v) const {return v->compare(this);} + virtual bool compare (const CPHScriptObjectActionN* v) const {return m_callback==v->m_callback;} +}; + +class CPHScriptGameObjectCondition : + public CPHScriptObjectConditionN +{ + CObject* m_obj; + bool b_obsolete; +public: + CPHScriptGameObjectCondition( const luabind::object &object,const luabind::functor &functor,CObject* gobj): + CPHScriptObjectConditionN(object,functor) + { + m_obj=gobj; + b_obsolete=false; + } + virtual bool is_true () {b_obsolete=CPHScriptObjectConditionN::is_true();return b_obsolete;} + virtual bool compare (const CObject* v) const {return m_obj->ID()==v->ID();} + virtual bool compare (const CPHReqComparerV* v) const {return v->compare(this);} + virtual bool obsolete () const {return b_obsolete;} +}; + +class CPHScriptGameObjectAction : + public CPHScriptObjectActionN +{ + CObject* m_obj; +public: + CPHScriptGameObjectAction( const luabind::object &object,const luabind::functor &functor,CObject* gobj): + CPHScriptObjectActionN(object,functor) + { + m_obj=gobj; + } + virtual bool compare (const CPHReqComparerV* v) const {return v->compare(this);} + virtual bool compare (const CObject* v) const {return m_obj->ID()==v->ID();} +}; + +class CPHSriptReqObjComparer : + public CPHReqComparerV +{ + luabind::object *m_lua_object; + +public: + CPHSriptReqObjComparer (const luabind::object & lua_object) {m_lua_object= new luabind::object(lua_object);} + CPHSriptReqObjComparer (const CPHSriptReqObjComparer &object) {m_lua_object= new luabind::object(*object.m_lua_object);} + virtual ~CPHSriptReqObjComparer () {xr_delete(m_lua_object);} + virtual bool compare (const CPHScriptObjectCondition* v) const {return v->compare(m_lua_object);} + virtual bool compare (const CPHScriptObjectAction* v) const {return v->compare(m_lua_object);} + virtual bool compare (const CPHScriptObjectConditionN* v) const {return v->compare(m_lua_object);} + virtual bool compare (const CPHScriptObjectActionN* v) const {return v->compare(m_lua_object);} +}; + + +class CPHSriptReqGObjComparer : + public CPHReqComparerV +{ + CObject *m_object; + +public: + CPHSriptReqGObjComparer (CObject* object) {m_object= object;} + virtual bool compare (const CPHScriptGameObjectAction* v) const {return v->compare(m_object);} + virtual bool compare (const CPHScriptGameObjectCondition* v)const {return v->compare(m_object);} +}; \ No newline at end of file diff --git a/src/xrGameLA/PHShell.cpp b/src/xrGameLA/PHShell.cpp new file mode 100644 index 000000000..320ad6f86 --- /dev/null +++ b/src/xrGameLA/PHShell.cpp @@ -0,0 +1,1707 @@ +///////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +#include "StdAfx.h" +#include "../bone.h" +#include "PHDynamicData.h" +#include "Physics.h" +#include "tri-colliderknoopc/dTriList.h" +#include "PHShellSplitter.h" +#include "PHFracture.h" +#include "PHJointDestroyInfo.h" +#include "SpaceUtils.h" +#include "MathUtils.h" +#include "PhysicsShellHolder.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHCollideValidator.h" +#include "../bone.h" +#include "game_object_space.h" +#include "ExtendedGeom.h" +#include "PHElement.h" +#include "PHShell.h" +#include "PHCollideValidator.h" +#include "PHElementInline.h" + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + #include "PhysicsShellAnimator.h" +#endif + +IC bool PhOutOfBoundaries (const Fvector& v) +{ + return v.y < phBoundaries.y1; +} +CPHShell::~CPHShell () +{ + m_pKinematics = 0; + VERIFY(!isActive()); + + xr_vector::iterator i; + for(i=elements.begin();elements.end()!=i;++i) + xr_delete(*i); + elements.clear(); + + xr_vector::iterator j; + for(j=joints.begin();joints.end()!=j;++j) + xr_delete(*j); + joints.clear(); + if(m_spliter_holder)xr_delete(m_spliter_holder); +} +CPHShell::CPHShell() +{ + //bActive=false; + //bActivating=false; + m_flags.assign(0); + m_flags.set(flActivating,FALSE); + m_flags.set(flActive,FALSE); + m_space=NULL; + m_pKinematics=NULL; + m_spliter_holder=NULL; + m_object_in_root.identity(); + m_active_count=0; + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + m_pPhysicsShellAnimatorC=NULL; +#endif + +} + +void CPHShell::EnableObject(CPHObject* obj) +{ + CPHObject::activate(); + if(m_spliter_holder)m_spliter_holder->Activate(); +} +void CPHShell::DisableObject() +{ + + CPhysicsShellHolder* ref_object=(*elements.begin())->PhysicsRefObject(); +//. if (!ref_object) return; + + if (ref_object) + ref_object->on_physics_disable (); + + //InterpolateGlobalTransform(&mXFORM); + CPHObject::deactivate(); + if(m_spliter_holder)m_spliter_holder->Deactivate(); + if(m_flags.test(flRemoveCharacterCollisionAfterDisable)) + DisableCharacterCollision (); +} +void CPHShell:: DisableCharacterCollision () +{ + CPHCollideValidator::SetCharacterClassNotCollide(*this); +} +void CPHShell::Disable() +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + DisableObject(); + for( ;i!=e;++i) + { + (*i)->Disable(); + } + ClearCashedTries(); +} +void CPHShell::DisableCollision() +{ + CPHObject::collision_disable(); +} +void CPHShell::EnableCollision() +{ + CPHObject::collision_enable(); +} +void CPHShell::ReanableObject() +{ + //if(b_contacts_saved) dJointGroupEmpty(m_saved_contacts); + //b_contacts_saved=false; + +} + +void CPHShell::vis_update_activate() +{ + ++m_active_count; + CPhysicsShellHolder* ref_object=(*elements.begin())->PhysicsRefObject(); + if(ref_object&&m_active_count>0) + { + m_active_count=0; + ref_object->processing_activate(); + } +} + +void CPHShell::vis_update_deactivate() +{ + --m_active_count; + //CPhysicsShellHolder* ref_object=(*elements.begin())->PhysicsRefObject(); + //if(ref_object&&!m_flags.test(flProcessigDeactivated)) + //{ + // //ref_object->processing_deactivate(); + // m_flags.set(flProcessigDeactivate,TRUE); + //} +} +void CPHShell::setDensity(float M) +{ + ELEMENT_I i; + //float volume=0.f; + //for(i=elements.begin();elements.end() != i;++i) volume+=(*i)->get_volume(); + + for(i=elements.begin();elements.end() != i;++i) + (*i)->setDensity(M); +} + + +void CPHShell::setMass(float M){ + ELEMENT_I i; + float volume=0.f; + for(i=elements.begin();elements.end() != i;++i) volume+=(*i)->get_volume(); + + for(i=elements.begin();elements.end() != i;++i) + (*i)->setMass( + (*i)->get_volume()/volume*M + ); +} + +void CPHShell::setMass1(float M){ + ELEMENT_I i; + + + for(i=elements.begin();elements.end() != i;++i) + (*i)->setMass( + M/elements.size() + ); +} +float CPHShell::getMass() +{ + float m=0.f; + + ELEMENT_I i; + + for(i=elements.begin();elements.end() != i;++i) m+=(*i)->getMass(); + + return m; +} + +void CPHShell::get_spatial_params() +{ + spatialParsFromDGeom((dGeomID)m_space,spatial.sphere.P,AABB,spatial.sphere.R); +} + +float CPHShell::getVolume() +{ + float v=0.f; + + ELEMENT_I i; + + for(i=elements.begin();elements.end() != i;++i) v+=(*i)->getVolume(); + + return v; +} + +float CPHShell::getDensity() +{ + return getMass()/getVolume(); +} + + + + +void CPHShell::PhDataUpdate(dReal step){ + + ELEMENT_I i=elements.begin(),e=elements.end(); + bool disable=true; + for(; e!=i ;++i) + { + (*i)->PhDataUpdate(step); + dBodyID body=(*i)->get_body(); + if(body&&disable&&dBodyIsEnabled(body)) + disable=false; + } + if(disable) + { + DisableObject(); + CPHObject::put_in_recently_deactivated(); + } + else ReanableObject(); + + if(PhOutOfBoundaries(cast_fv(dBodyGetPosition((*elements.begin())->get_body())))) + Disable(); +} + + + +void CPHShell::PhTune(dReal step){ + ELEMENT_I i=elements.begin(),e=elements.end(); + for(; e!=i ;++i) + (*i)->PhTune(step); +} + +void CPHShell::Update(){ + if(!isActive()) return; + if(m_flags.test(flActivating)) m_flags.set(flActivating,FALSE); + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + (*i)->Update(); + + mXFORM.set((*elements.begin())->mXFORM); + VERIFY2(_valid(mXFORM),"invalid position in update"); +} + +void CPHShell::Freeze() +{ + CPHObject::Freeze(); +} +void CPHShell::UnFreeze() +{ + CPHObject::UnFreeze(); +} +void CPHShell::FreezeContent() +{ + + CPHObject::FreezeContent(); + ELEMENT_I i=elements.begin(),e=elements.end(); + for(; e!=i ;++i) + (*i)->Freeze(); + +} +void CPHShell::UnFreezeContent() +{ + CPHObject::UnFreezeContent(); + ELEMENT_I i=elements.begin(),e=elements.end(); + for(; e!=i ;++i) + (*i)->UnFreeze(); + +} +void CPHShell:: applyForce (const Fvector& dir, float val) +{ + if(!isActive()) return; + ELEMENT_I i=elements.begin(),e=elements.end(); + val/=getMass(); + for(; e!=i ;++i) + (*i)->applyForce( dir, val*(*i)->getMass()); + EnableObject(0); +}; +void CPHShell:: applyForce (float x,float y,float z) +{ +Fvector dir;dir.set(x,y,z); +float val=dir.magnitude(); + if(!fis_zero(val)) + { + dir.mul(1.f/val); + applyForce(dir,val); + } +}; +void CPHShell:: applyImpulse (const Fvector& dir, float val) +{ + if(!isActive()) return; + (*elements.begin())->applyImpulse ( dir, val); + EnableObject(0); +}; +void CPHShell:: applyImpulseTrace (const Fvector& pos, const Fvector& dir, float val){ + if(!isActive()) return; + (*elements.begin())->applyImpulseTrace ( pos, dir, val, 0); + EnableObject(0); +} + +void CPHShell:: applyImpulseTrace (const Fvector& pos, const Fvector& dir, float val,const u16 id){ + if(!isActive()) return; + VERIFY(m_pKinematics); + CBoneInstance& instance=m_pKinematics->LL_GetBoneInstance (id); + if(instance.callback_type()!=bctPhysics || !instance.callback_param()) return; + + ((CPhysicsElement*)instance.callback_param())->applyImpulseTrace ( pos, dir, val, id); + EnableObject(0); +} + +CPhysicsElement* CPHShell::get_Element (const shared_str & bone_name) +{ + VERIFY(m_pKinematics); + return get_Element(m_pKinematics->LL_BoneID(bone_name)); +} +CPhysicsElement* CPHShell::get_Element (LPCSTR bone_name) +{ + return get_Element((const shared_str&)(bone_name)); +} +CPhysicsElement* CPHShell::get_ElementByStoreOrder(u16 num) +{ + R_ASSERT2(num(elements[element]); +} + +CPhysicsElement* CPHShell::get_Element(u16 bone_id) +{ + if(m_pKinematics&& isActive()) + { + CBoneInstance& instance=m_pKinematics->LL_GetBoneInstance (bone_id); + if(instance.callback()==BonesCallback||instance.callback()==StataticRootBonesCallBack) + { + return (instance.callback_type()==bctPhysics)?(CPhysicsElement*)instance.callback_param():NULL; + } + } + + ELEMENT_I i=elements.begin(),e=elements.end(); + for(; e!=i ;++i) + if((*i)->m_SelfID==bone_id) + return (CPhysicsElement*)(*i); + return NULL; +} + +CPhysicsJoint* CPHShell::get_Joint(u16 bone_id) +{ + JOINT_I i= joints.begin(),e=joints.end(); + for(;e!=i;i++) + if((*i)->BoneID()==bone_id) + return (CPhysicsJoint*)(*i); + return NULL; +} +CPhysicsJoint* CPHShell::get_Joint(const shared_str &bone_name) +{ + VERIFY(m_pKinematics); + return get_Joint(m_pKinematics->LL_BoneID(bone_name)); +} + +CPhysicsJoint* CPHShell::get_Joint(LPCSTR bone_name) +{ + return get_Joint((const shared_str&)bone_name); +} + +CPhysicsJoint* CPHShell::get_JointByStoreOrder (u16 num) +{ + return (CPhysicsJoint*) joints[num]; +} + +u16 CPHShell::get_JointsNumber () +{ + return u16(joints.size()); +} +void CPHShell:: BonesCallback (CBoneInstance* B){ + ///CPHElement* E = smart_cast (static_cast(B->Callback_Param)); + + CPHElement* E = cast_PHElement(B->callback_param()); + E->BonesCallBack(B); +} + + +void CPHShell::StataticRootBonesCallBack (CBoneInstance* B){ + ///CPHElement* E = smart_cast (static_cast(B->Callback_Param)); + + CPHElement* E = cast_PHElement(B->callback_param()); + E->StataticRootBonesCallBack(B); +} + + +void CPHShell::SetTransform (const Fmatrix& m0){ + + mXFORM.set(m0); + ELEMENT_I i=elements.begin(); + for( ;elements.end() != i; ++i) + { + (*i)->SetTransform(m0); + } + spatial_move(); +} + + +void CPHShell::Enable() +{ + if(!isActive()) + return; + + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + //if(dBodyIsEnabled((*i)->get_body())) return; + for( ;i!=e;++i) + (*i)->Enable(); + EnableObject(0); +} + +void CPHShell::set_PhysicsRefObject (CPhysicsShellHolder* ref_object) +{ + + + if(elements.empty()) return; + if((*elements.begin())->PhysicsRefObject()==ref_object) return; + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + { + (*i)->set_PhysicsRefObject(ref_object); + } + + + +} + + + +void CPHShell::set_ContactCallback(ContactCallbackFun* callback) +{ + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + (*i)->set_ContactCallback(callback); +} + + +void CPHShell::set_ObjectContactCallback(ObjectContactCallbackFun* callback) +{ + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + (*i)->set_ObjectContactCallback(callback); +} +void CPHShell::add_ObjectContactCallback(ObjectContactCallbackFun* callback) +{ + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + (*i)->add_ObjectContactCallback(callback); +} +void CPHShell::remove_ObjectContactCallback(ObjectContactCallbackFun* callback) +{ + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + (*i)->remove_ObjectContactCallback(callback); +} +void CPHShell::set_CallbackData(void *cd) +{ + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + (*i)->set_CallbackData(cd); +} +void CPHShell::SetPhObjectInElements() +{ + if(!isActive()) return; + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i ) + (*i)->SetPhObjectInGeomData((CPHObject*)this); +} + +void CPHShell::SetMaterial(LPCSTR m) +{ + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + { + (*i)->SetMaterial(m); + } +} + +void CPHShell::SetMaterial(u16 m) +{ + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + { + (*i)->SetMaterial(m); + } +} + +void CPHShell::get_LinearVel(Fvector& velocity) +{ + + (*elements.begin())->get_LinearVel(velocity); +} + +void CPHShell::get_AngularVel(Fvector& velocity) +{ + + (*elements.begin())->get_AngularVel(velocity); +} + +void CPHShell::set_LinearVel(const Fvector& velocity) +{ + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;i++) (*i)->set_LinearVel(velocity); +} + +void CPHShell::set_AngularVel(const Fvector& velocity) +{ + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;i++) (*i)->set_AngularVel(velocity); +} + + +void CPHShell::TransformPosition(const Fmatrix &form) +{ + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;i++) (*i)->TransformPosition(form); +} + +void CPHShell::SetGlTransformDynamic(const Fmatrix &form) +{ + VERIFY(isActive()); + VERIFY(_valid(form)); + Fmatrix current,replace; + GetGlobalTransformDynamic(¤t); + current.invert(); + replace.mul(form,current); + TransformPosition(replace); +} +void CPHShell::SmoothElementsInertia(float k) +{ + dMass m_avrg; + dReal krc=1.f-k; + dMassSetZero(&m_avrg); + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + { + + dMassAdd(&m_avrg,(*i)->getMassTensor()); + + } + int n = (int)elements.size(); + m_avrg.mass*=k/float(n); + for(int j=0;j<4*3;++j) m_avrg.I[j]*=k/float(n); + + for(i=elements.begin();elements.end() != i;++i) + { + dVector3 tmp; + dMass* m=(*i)->getMassTensor(); + dVectorSet(tmp,m->c); + + m->mass*=krc; + for(int j=0;j<4*3;++j) m->I[j]*=krc; + dMassAdd(m,&m_avrg); + + dVectorSet(m->c,tmp); + } +} + +void CPHShell::setEquelInertiaForEls(const dMass& M) +{ + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;++i) + { + (*i)->setInertia(M); + } +} + +void CPHShell::addEquelInertiaToEls(const dMass& M) +{ + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;++i) + { + (*i)->addInertia(M); + } +} +static BONE_P_MAP* spGetingMap=NULL; +void CPHShell::build_FromKinematics(IKinematics* K,BONE_P_MAP* p_geting_map) +{ + m_pKinematics =K; + spGetingMap =p_geting_map; + //CBoneData& bone_data = m_pKinematics->LL_GetData(0); + if(!m_spliter_holder) m_spliter_holder= new CPHShellSplitterHolder(this); + bool vis_check = false; + AddElementRecursive(0,m_pKinematics->LL_GetBoneRoot(),Fidentity,0,&vis_check); + //R_ASSERT2((*elements.begin())->numberOfGeoms(),"No physics shapes was assigned for model or no shapes in main root bone!!!"); + //SetCallbacks(BonesCallback); + if(m_spliter_holder->isEmpty())ClearBreakInfo(); +} + +void CPHShell::preBuild_FromKinematics(IKinematics* K,BONE_P_MAP* p_geting_map) +{ + m_pKinematics =K; + spGetingMap =p_geting_map; + //CBoneData& bone_data = m_pKinematics->LL_GetData(0); + if(!m_spliter_holder) m_spliter_holder= new CPHShellSplitterHolder(this); + bool vis_check=false; + AddElementRecursive(0,m_pKinematics->LL_GetBoneRoot(),Fidentity,0,&vis_check); + R_ASSERT2((*elements.begin())->numberOfGeoms(),"No physics shapes was assigned for model or no shapes in main root bone!!!"); + if(m_spliter_holder->isEmpty())ClearBreakInfo(); + m_pKinematics=0; +} +void CPHShell::ClearBreakInfo() +{ + { + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;++i)(*i)->ClearDestroyInfo(); + } + + { + JOINT_I i=joints.begin(),e=joints.end(); + for(;i!=e;++i) (*i)->ClearDestroyInfo(); + } + xr_delete(m_spliter_holder); +} +ICF bool no_physics_shape(const SBoneShape& shape) +{ + return shape.type==SBoneShape::stNone||shape.flags.test(SBoneShape::sfNoPhysics); +} +void CPHShell::AddElementRecursive(CPhysicsElement* root_e, u16 id,Fmatrix global_parent,u16 element_number,bool* vis_check) +{ + + //CBoneInstance& B = m_pKinematics->LL_GetBoneInstance(u16(id)); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + const IBoneData& bone_data= m_pKinematics->GetBoneData(u16(id)); + const SJointIKData& joint_data=bone_data.get_IK_data(); + Fmatrix fm_position; + fm_position.set (bone_data.get_bind_transform()); + fm_position.mulA_43 (global_parent); + Flags64 mask; + mask.assign(m_pKinematics->LL_GetBonesVisible()); + bool no_visible=!mask.is(1ui64<<(u64)id); + bool lvis_check=false; + if(no_visible) + { + + //for (vecBonesIt it=bone_data.children.begin(); bone_data.children.end() != it; ++it) + //AddElementRecursive (root_e,(*it)->GetSelfID(),fm_position,element_number,&lvis_check); + //IBoneData &ibone_data = bone_data; + u16 num_children =bone_data.GetNumChildren(); + for(u16 i = 0;imXFORM); + vs_root_position.invert(); + vs_root_position.mulB_43(fm_position); + + E=root_e; + if(breakable) + { + CPHFracture fracture; + fracture.m_bone_id =id; + R_ASSERT2(id<64,"ower 64 bones in breacable are not supported"); + fracture.m_start_geom_num =E->numberOfGeoms(); + fracture.m_end_geom_num =u16(-1); + fracture.m_start_el_num =u16(elements.size()); + fracture.m_start_jt_num =u16(joints.size()); + fracture.MassSetFirst (*(E->getMassTensor())); + fracture.m_pos_in_element .set(vs_root_position.c); + VERIFY (u16(-1)!=fracture.m_start_geom_num); + fracture.m_break_force =joint_data.break_force; + fracture.m_break_torque =joint_data.break_torque; + root_e->add_Shape(bone_data.get_shape(),vs_root_position); + root_e->add_Mass(bone_data.get_shape(),vs_root_position,bone_data.get_center_of_mass(),bone_data.get_mass(),&fracture); + + fracture_num=E->setGeomFracturable(fracture); + } + else + { + root_e->add_Shape(bone_data.get_shape(),vs_root_position); + root_e->add_Mass(bone_data.get_shape(),vs_root_position,bone_data.get_center_of_mass(),bone_data.get_mass()); + } + + + //B.Callback_Param=root_e; + //B.Callback=NULL; + + } + else // + { + E = P_create_Element(); + E->m_SelfID=id; + E->mXFORM.set (fm_position); + E->SetMaterial (bone_data.get_game_mtl_idx()); + //Fvector mc; + //fm_position.transform_tiny(mc,bone_data.center_of_mass); + E->set_ParentElement(root_e); + ///B.set_callback(BonesCallback1,E); + if(!no_physics_shape(bone_data.get_shape())){ + E->add_Shape(bone_data.get_shape()); + E->setMassMC(bone_data.get_mass(),bone_data.get_center_of_mass()); + } + element_number=u16(elements.size()); + add_Element(E); + element_added=true; + + + if(root_e) + { + switch(joint_data.type) + { + case jtSlider: + { + J= P_create_Joint(CPhysicsJoint::slider,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetJointSDfactors(joint_data.spring_factor,joint_data.damping_factor); + J->SetLimits(joint_data.limits[0].limit.x,joint_data.limits[0].limit.y,0); + J->SetAxisSDfactors(joint_data.limits[0].spring_factor,joint_data.limits[0].damping_factor,0); + if(joint_data.limits[1].limit.y-joint_data.limits[1].limit.xSetLimits(joint_data.limits[1].limit.x,joint_data.limits[1].limit.y,1); + J->SetAxisSDfactors(joint_data.limits[1].spring_factor,joint_data.limits[1].damping_factor,1); + } + break; + } + case jtCloth: + { + J= P_create_Joint(CPhysicsJoint::ball,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetJointSDfactors(joint_data.spring_factor,joint_data.damping_factor); + break; + } + case jtJoint: + { + bool eqx=!!fsimilar(joint_data.limits[0].limit.x,joint_data.limits[0].limit.y), + eqy=!!fsimilar(joint_data.limits[1].limit.x,joint_data.limits[1].limit.y), + eqz=!!fsimilar(joint_data.limits[2].limit.x,joint_data.limits[2].limit.y); + + if(eqx) + { + if(eqy) + { + J= P_create_Joint(CPhysicsJoint::hinge,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetJointSDfactors(joint_data.spring_factor,joint_data.damping_factor); + J->SetAxisDirVsSecondElement (0.f,0.f,1.f,0); + if(joint_data.limits[2].limit.y-joint_data.limits[2].limit.xSetLimits(joint_data.limits[2].limit.x,joint_data.limits[2].limit.y,0); + J->SetAxisSDfactors(joint_data.limits[2].spring_factor,joint_data.limits[2].damping_factor,0); + } + break; + } + if(eqz) + { + J= P_create_Joint(CPhysicsJoint::hinge,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetJointSDfactors(joint_data.spring_factor,joint_data.damping_factor); + J->SetAxisDirVsSecondElement(0,1,0,0); + if(joint_data.limits[1].limit.y-joint_data.limits[1].limit.xSetLimits(joint_data.limits[1].limit.x,joint_data.limits[1].limit.y,0); + J->SetAxisSDfactors(joint_data.limits[1].spring_factor,joint_data.limits[1].damping_factor,0); + } + break; + } + J= P_create_Joint(CPhysicsJoint::full_control,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetJointSDfactors(joint_data.spring_factor,joint_data.damping_factor); + J->SetAxisDirVsSecondElement(0.f,0.f,1.f,0);//2-0 + //0-1 + J->SetAxisDirVsSecondElement(0.f,1.f,0.f,2);//1-2 + + if(joint_data.limits[2].limit.y-joint_data.limits[2].limit.xSetLimits(joint_data.limits[2].limit.x,joint_data.limits[2].limit.y,0); + J->SetAxisSDfactors(joint_data.limits[2].spring_factor,joint_data.limits[2].damping_factor,0); + } + if(joint_data.limits[0].limit.y-joint_data.limits[0].limit.xSetLimits(joint_data.limits[0].limit.x,joint_data.limits[0].limit.y,1); + J->SetAxisSDfactors(joint_data.limits[0].spring_factor,joint_data.limits[0].damping_factor,1); + } + + if(joint_data.limits[1].limit.y-joint_data.limits[1].limit.xSetLimits(joint_data.limits[1].limit.x,joint_data.limits[1].limit.y,2); + J->SetAxisSDfactors(joint_data.limits[1].spring_factor,joint_data.limits[1].damping_factor,2); + } + + break; + + } + + if(eqy) + { + if(eqz) + { + J= P_create_Joint(CPhysicsJoint::hinge,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetJointSDfactors(joint_data.spring_factor,joint_data.damping_factor); + J->SetAxisDirVsSecondElement(1,0,0,0); + if(joint_data.limits[0].limit.y-joint_data.limits[0].limit.xSetLimits(joint_data.limits[0].limit.x,joint_data.limits[0].limit.y,0); + J->SetAxisSDfactors(joint_data.limits[0].spring_factor,joint_data.limits[0].damping_factor,0); + } + break; + } + + J= P_create_Joint(CPhysicsJoint::full_control,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetJointSDfactors(joint_data.spring_factor,joint_data.damping_factor); + J->SetAxisDirVsSecondElement(0.f,0.f,1.f,0);//2-0 + //1-1 + J->SetAxisDirVsSecondElement(1.f,0.f,0.f,2);//0-2 + if(joint_data.limits[2].limit.y-joint_data.limits[2].limit.xSetLimits(joint_data.limits[2].limit.x,joint_data.limits[2].limit.y,0); + J->SetAxisSDfactors(joint_data.limits[2].spring_factor,joint_data.limits[2].damping_factor,0); + } + if(joint_data.limits[0].limit.y-joint_data.limits[0].limit.xSetLimits(joint_data.limits[0].limit.x,joint_data.limits[0].limit.y,2); + J->SetAxisSDfactors(joint_data.limits[0].spring_factor,joint_data.limits[0].damping_factor,2); + } + + if(joint_data.limits[1].limit.y-joint_data.limits[1].limit.xSetLimits(joint_data.limits[1].limit.x,joint_data.limits[1].limit.y,1); + J->SetAxisSDfactors(joint_data.limits[1].spring_factor,joint_data.limits[1].damping_factor,1); + } + break; + } + + if(eqz) + { + J= P_create_Joint(CPhysicsJoint::full_control,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetJointSDfactors(joint_data.spring_factor,joint_data.damping_factor); + J->SetAxisDirVsSecondElement(1.f,0.f,0.f,0);//0-0 + //2-1 + J->SetAxisDirVsSecondElement(0.f,1.f,0.f,2);//1-2 + if(joint_data.limits[2].limit.y-joint_data.limits[2].limit.xSetLimits(joint_data.limits[2].limit.x,joint_data.limits[2].limit.y,1); + J->SetAxisSDfactors(joint_data.limits[2].spring_factor,joint_data.limits[2].damping_factor,1); + } + if(joint_data.limits[0].limit.y-joint_data.limits[0].limit.xSetLimits(joint_data.limits[0].limit.x,joint_data.limits[0].limit.y,0); + J->SetAxisSDfactors(joint_data.limits[0].spring_factor,joint_data.limits[0].damping_factor,0); + } + + if(joint_data.limits[1].limit.y-joint_data.limits[1].limit.xSetLimits(joint_data.limits[1].limit.x,joint_data.limits[1].limit.y,2); + J->SetAxisSDfactors(joint_data.limits[1].spring_factor,joint_data.limits[1].damping_factor,2); + } + break; + } + J= P_create_Joint(CPhysicsJoint::full_control,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetJointSDfactors(joint_data.spring_factor,joint_data.damping_factor); + J->SetAxisDirVsSecondElement(0.f,0.f,1.f,0);//2-0 + //0-1 + J->SetAxisDirVsSecondElement(0.f,1.f,0.f,2);//1-2 + if(joint_data.limits[2].limit.y-joint_data.limits[2].limit.xSetLimits(joint_data.limits[2].limit.x,joint_data.limits[2].limit.y,0); + J->SetAxisSDfactors(joint_data.limits[2].spring_factor,joint_data.limits[2].damping_factor,0); + } + if(joint_data.limits[0].limit.y-joint_data.limits[0].limit.xSetLimits(joint_data.limits[0].limit.x,joint_data.limits[0].limit.y,1); + J->SetAxisSDfactors(joint_data.limits[0].spring_factor,joint_data.limits[0].damping_factor,1); + } + + if(joint_data.limits[1].limit.y-joint_data.limits[1].limit.xSetLimits(joint_data.limits[1].limit.x,joint_data.limits[1].limit.y,2); + J->SetAxisSDfactors(joint_data.limits[1].spring_factor,joint_data.limits[1].damping_factor,2); + } + + break; + } + case jtWheel: + { + J= P_create_Joint(CPhysicsJoint::hinge2,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetJointSDfactors(joint_data.spring_factor,joint_data.damping_factor); + J->SetAxisDirVsSecondElement(1,0,0,0); + J->SetAxisDirVsSecondElement(0,0,1,1); + if(joint_data.limits[0].limit.y-joint_data.limits[0].limit.xSetLimits(joint_data.limits[0].limit.x,joint_data.limits[0].limit.y,0); + J->SetAxisSDfactors(joint_data.limits[0].spring_factor,joint_data.limits[0].damping_factor,0); + } + break; + } + + case jtNone: break; + + default: NODEFAULT; + } + if(J) + { + + J->SetForceAndVelocity(0.f);//joint_data.friction + SetJointRootGeom(root_e,J); + J->SetBoneID(id); + add_Joint (J); + if(breakable) + { + setEndJointSplitter(); + J->SetBreakable(joint_data.break_force,joint_data.break_torque); + } + } + } + if(m_spliter_holder) + { + splitter_position=u16(m_spliter_holder->m_splitters.size()); + } + } + } + else + { + //B.set_callback(0,root_e); + E=root_e; + } + + if(!no_physics_shape(bone_data.get_shape())) + { + CODEGeom* added_geom = E->last_geom(); + if(added_geom) added_geom->set_bone_id(id); + } +#ifdef DEBUG + if(E->last_geom()) + VERIFY(E->last_geom()->bone_id()!=u16(-1)); +#endif + if(m_spliter_holder&&E->has_geoms()) + { + m_spliter_holder->AddToGeomMap(mk_pair(id,E->last_geom())); + } + + if(spGetingMap) + { + const BONE_P_PAIR_IT c_iter= spGetingMap->find(id); + if(spGetingMap->end()!=c_iter) + { + c_iter->second.joint=J; + c_iter->second.element=E; + } + } + + + ///////////////////////////////////////////////////////////////////////////////////// + //for (vecBonesIt it=bone_data.children.begin(); bone_data.children.end() != it; ++it) + // AddElementRecursive (E,(*it)->GetSelfID(),fm_position,element_number,arg_check); + //IBoneData &ibone_data = bone_data; + u16 num_children = bone_data.GetNumChildren(); + for(u16 i = 0;iFracture(fracture_num); + fracture.m_bone_id =id; + fracture.m_end_geom_num =E->numberOfGeoms(); + fracture.m_end_el_num =u16(elements.size());//just after this el = current+1 + fracture.m_end_jt_num =u16(joints.size()); //current+1 + + } + else + { + if(J) + { + J->JointDestroyInfo()->m_end_element=u16(elements.size()); + J->JointDestroyInfo()->m_end_joint=u16(joints.size()); + } + } + } + + if(element_added&&E->isBreakable())setElementSplitter(element_number,splitter_position); +#ifdef DEBUG + bool bbb = lvis_check||(!breakable && root_e); + if(!bbb) + { + IKinematics* K = m_pKinematics; + + Msg("all bones transform:--------"); + + for(u16 ii=0; iiLL_BoneCount();++ii){ + Fmatrix tr; + + tr = K->LL_GetTransform(ii); + Log("bone ",K->LL_BoneName_dbg(ii)); + Log("bone_matrix",tr); + } + Log("end-------"); + } + + //VERIFY3(bbb,*dbg_obj->cNameVisual(),"has breaking parts with no vertexes or size less than 1mm");// +#endif + +} + +void CPHShell::ResetCallbacks(u16 id,Flags64 &mask) +{ + ResetCallbacksRecursive(id,u16(-1),mask); +} + +void CPHShell::ResetCallbacksRecursive(u16 id,u16 element,Flags64 &mask) +{ + + //if(elements.size()==element) return; + CBoneInstance& B = m_pKinematics->LL_GetBoneInstance(u16(id)); + const IBoneData& bone_data= m_pKinematics->GetBoneData(u16(id)); + const SJointIKData& joint_data=bone_data.get_IK_data(); + + if(mask.is(1ui64<<(u64)id)) + { + + if(no_physics_shape(bone_data.get_shape())||joint_data.type==jtRigid&& element!=u16(-1)) + { + + B.set_callback(bctPhysics,0,cast_PhysicsElement(elements[element])); + } + else + { + + element++; + R_ASSERT2(elementGetSelfID(),element,mask); + //IBoneData &ibone_data = bone_data; + u16 num_children = bone_data.GetNumChildren(); + for(u16 i = 0;iLL_GetBoneInstance((*i)->m_SelfID); + B.set_callback_overwrite(TRUE); + } + }else ZeroCallbacks(); +} + +template< typename T> +void for_each_bone_id( IKinematics &K, T op ) +{ + u16 bn = K.LL_BoneCount(); + for(u16 i = 0; i < bn; ++i ) + op( i ); +} +static u16 element_position_in_set_calbacks=u16(-1); +static BoneCallbackFun* bones_callback;//temp ror SetCallbacksRecursive +void CPHShell::SetCallbacks(BoneCallbackFun* callback) +{ + element_position_in_set_calbacks=u16(-1); + bones_callback=callback; + SetCallbacksRecursive(m_pKinematics->LL_GetBoneRoot(),element_position_in_set_calbacks); +} + +void CPHShell::SetCallbacksRecursive(u16 id,u16 element) +{ + //if(elements.size()==element) return; + CBoneInstance& B = m_pKinematics->LL_GetBoneInstance(u16(id)); + const IBoneData& bone_data= m_pKinematics->GetBoneData(u16(id)); + const SJointIKData& joint_data=bone_data.get_IK_data(); + Flags64 mask; + mask.assign(m_pKinematics->LL_GetBonesVisible()); + if(mask.is(1ui64<<(u64)id)) + { + if((no_physics_shape(bone_data.get_shape())||joint_data.type==jtRigid) && element!=u16(-1)){ + B.set_callback(bctPhysics,0,cast_PhysicsElement(elements[element])); + }else{ + element_position_in_set_calbacks++; + element=element_position_in_set_calbacks; + R_ASSERT2(elementGetSelfID(),element); + //IBoneData &ibone_data = bone_data; + u16 num_children = bone_data.GetNumChildren(); + for(u16 i = 0;iLL_GetBoneRoot()); +} +void CPHShell::ZeroCallbacksRecursive(u16 id) +{ + CBoneInstance& B = m_pKinematics->LL_GetBoneInstance(u16(id)); + const IBoneData& bone_data= m_pKinematics->GetBoneData(u16(id)); + if(B.callback_type() == bctPhysics) + { + B.reset_callback (); + } + //for (vecBonesIt it=bone_data.children.begin(); bone_data.children.end() != it; ++it) + // ZeroCallbacksRecursive ((*it)->GetSelfID()); + //IBoneData &ibone_data = bone_data; + u16 num_children = bone_data.GetNumChildren(); + for(u16 i = 0;iset_DynamicLimits(l_limit,w_limit); +} + +void CPHShell::set_DynamicScales(float l_scale/* =default_l_scale */,float w_scale/* =default_w_scale */) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + (*i)->set_DynamicScales(l_scale,w_scale); +} + +void CPHShell::set_DisableParams(const SAllDDOParams& params) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + (*i)->set_DisableParams(params); +} + +void CPHShell::UpdateRoot() +{ + + ELEMENT_I i=elements.begin(); + if( !(*i)->isFullActive()) return; + + (*i)->InterpolateGlobalTransform(&mXFORM); + +} + +void CPHShell::InterpolateGlobalTransform(Fmatrix* m) +{ + + //if(!CPHObject::is_active()&&!CPHObject::NetInterpolation()) return; + + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + (*i)->InterpolateGlobalTransform(&(*i)->mXFORM); + m->set((*elements.begin())->mXFORM); + m->mulB_43 (m_object_in_root); + mXFORM.set(*m); + VERIFY2(_valid(*m),"not valide transform"); + CPhysicsShellHolder* ref_object=(*elements.begin())->PhysicsRefObject(); + if(ref_object&&m_active_count<0) + { + ref_object->processing_deactivate(); + m_active_count=0; + } +} + +void CPHShell::GetGlobalTransformDynamic(Fmatrix* m) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + (*i)->GetGlobalTransformDynamic(&(*i)->mXFORM); + m->set((*elements.begin())->mXFORM); + m->mulB_43 (m_object_in_root); + VERIFY2(_valid(*m),"not valide transform"); +} +void CPHShell::InterpolateGlobalPosition(Fvector* v) +{ + (*elements.begin())->InterpolateGlobalPosition(v); + v->add(m_object_in_root.c); + VERIFY2(_valid(*v),"not valide result position"); +} + +void CPHShell::GetGlobalPositionDynamic(Fvector* v) +{ + (*elements.begin())->GetGlobalPositionDynamic(v); + VERIFY2(_valid(*v),"not valide result position"); +} + + +void CPHShell::ObjectToRootForm(const Fmatrix& form) +{ + Fmatrix M; + Fmatrix ILF; + (*elements.begin())->InverceLocalForm(ILF); + M.mul(m_object_in_root,ILF); + M.invert(); + mXFORM.mul(form,M); + VERIFY2(_valid(form),"not valide transform"); + +} +CPhysicsElement* CPHShell::NearestToPoint(const Fvector& point) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + float min_distance =dInfinity; + CPHElement* nearest_element=NULL; + for( ;i!=e;++i) + { + Fvector tmp; + float distance; + (*i)->GetGlobalPositionDynamic(&tmp); + tmp.sub(point); + distance=tmp.magnitude(); + if(distanceelements.empty()) (*i_from)->set_ParentElement(dest->elements.back()); + else (*i_from)->set_ParentElement(NULL); + } + for(ELEMENT_I i=i_from;i!=e;++i) + { + dGeomID spaced_geom=(*i)->dSpacedGeometry(); + if(spaced_geom)//for active elems + { + dSpaceRemove (m_space,spaced_geom ); + dSpaceAdd(dest->m_space,spaced_geom); + } + VERIFY(_valid(dest->mXFORM)); + (*i)->SetShell(dest); + } + dest->elements.insert(dest->elements.end(),i_from,e); + elements.erase(i_from,e); + +} + +void CPHShell::PassEndJoints(u16 from,u16 to,CPHShell *dest) +{ + JOINT_I i_from=joints.begin()+from,e=joints.begin()+to; + JOINT_I i=i_from; + for(;i!=e;i++) + { + (*i)->SetShell(dest); + } + dest->joints.insert(dest->joints.end(),i_from,e); + joints.erase(i_from,e); +} + +void CPHShell::DeleteElement(u16 element) +{ + elements[element]->Deactivate(); + xr_delete(elements[element]); + elements.erase(elements.begin()+element); +} +void CPHShell::DeleteJoint(u16 joint) +{ + joints[joint]->Deactivate(); + xr_delete(joints[joint]); + joints.erase(joints.begin()+joint); +} + +void CPHShell::setEndElementSplitter() +{ + + if(!elements.back()->FracturesHolder())//adding fracture for element supposed before adding splitter. Need only one splitter for an element + AddSplitter(CPHShellSplitter::splElement,u16(elements.size()-1),u16(joints.size()-1)); +} + +void CPHShell::setElementSplitter(u16 element_number,u16 splitter_position) +{ + AddSplitter(CPHShellSplitter::splElement,element_number,element_number-1,splitter_position); +} +void CPHShell::AddSplitter(CPHShellSplitter::EType type,u16 element,u16 joint) +{ + if(!m_spliter_holder) m_spliter_holder= new CPHShellSplitterHolder(this); + m_spliter_holder->AddSplitter(type,element,joint); +} + +void CPHShell::AddSplitter(CPHShellSplitter::EType type,u16 element,u16 joint,u16 position) +{ + if(!m_spliter_holder) m_spliter_holder= new CPHShellSplitterHolder(this); + m_spliter_holder->AddSplitter(type,element,joint,position); +} +void CPHShell::setEndJointSplitter() +{ + if(!joints.back()->JointDestroyInfo())//setting joint breacable supposed before adding splitter. Need only one splitter for a joint + AddSplitter(CPHShellSplitter::splJoint,u16(elements.size()-1),u16(joints.size()-1)); +} + +bool CPHShell::isBreakable() +{ + return (m_spliter_holder&&!m_spliter_holder->IsUnbreakable()); +} + +bool CPHShell::isFractured() +{ + return(m_spliter_holder&&m_spliter_holder->Breaked()); +} + +void CPHShell::SplitProcess(PHSHELL_PAIR_VECTOR &out_shels) +{ + if(! m_spliter_holder) return; + m_spliter_holder->SplitProcess(out_shels); + if(!m_spliter_holder->m_splitters.size()) xr_delete(m_spliter_holder); +} + +u16 CPHShell::BoneIdToRootGeom(u16 id) +{ + if(! m_spliter_holder)return u16(-1); + return m_spliter_holder->FindRootGeom(id); +} + +void CPHShell::SetJointRootGeom(CPhysicsElement* root_e,CPhysicsJoint* J) +{ + R_ASSERT(root_e); + R_ASSERT( J ); + CPHElement* e=cast_PHElement(root_e); + CPHJoint* j=static_cast(J); + + CPHFracturesHolder* f_holder=e->FracturesHolder(); + if(!f_holder) return; + j->RootGeom()=e->Geom(f_holder->LastFracture().m_start_geom_num); +} + +void CPHShell::set_ApplyByGravity(bool flag) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + (*i)->set_ApplyByGravity(flag); +} + +bool CPHShell::get_ApplyByGravity() +{ + if (elements.empty()) + return (false); + + VERIFY (elements.front()); + return (elements.front()->get_ApplyByGravity()); +} + +void CPHShell::applyGravityAccel(const Fvector& accel) +{ + if(!isActive())return; + ELEMENT_I i,e; + Fvector a; + a.set(accel); + a.mul((float)elements.size()); + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + (*i)->applyGravityAccel(a); + EnableObject(0); +} + +void CPHShell::PlaceBindToElForms() +{ + Flags64 mask; + mask.assign(m_pKinematics->LL_GetBonesVisible()); + PlaceBindToElFormsRecursive(Fidentity,m_pKinematics->LL_GetBoneRoot(),0,mask); +} +void CPHShell:: setTorque (const Fvector& torque) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + (*i)->setTorque(torque); +} +void CPHShell:: setForce (const Fvector& force) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + (*i)->setForce(force); +} +void CPHShell::PlaceBindToElFormsRecursive(Fmatrix parent,u16 id,u16 element,Flags64 &mask) +{ + + + CBoneData& bone_data= m_pKinematics->LL_GetData(u16(id)); + SJointIKData& joint_data=bone_data.IK_data; + + if(mask.is(1ui64<<(u64)id)) + { + + if(no_physics_shape(bone_data.shape)||joint_data.type==jtRigid&& element!=u16(-1)) + { + + + } + else + { + + element++; + R_ASSERT2(elementmXFORM.mul(parent,bone_data.bind_transform); + + } + } + //for (vecBonesIt it=bone_data.children.begin(); it!=bone_data.children.end(); ++it) + // PlaceBindToElFormsRecursive(mXFORM,(*it)->GetSelfID(),element,mask); + IBoneData &ibone_data = bone_data; + u16 num_children = ibone_data.GetNumChildren(); + for(u16 i = 0;iLL_GetBoneInstance(id); + CBoneData& bone_data= m_pKinematics->LL_GetData(u16(id)); + + bone_instance.mTransform.mul(parent,bone_data.bind_transform); + +// for (vecBonesIt it=bone_data.children.begin(); it!=bone_data.children.end(); ++it) +// BonesBindCalculateRecursive(bone_instance.mTransform,(*it)->GetSelfID()); + IBoneData &ibone_data = bone_data; + u16 num_children = ibone_data.GetNumChildren(); + for(u16 i = 0;iGeom(geom); + g->set_ph_object(this); + m_traced_geoms.add(g); + +} +void CPHShell::SetAllGeomTraced() +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + { + + u16 gn=(*i)->numberOfGeoms(); + for(u16 j=0;jGeom(j); + g->set_ph_object(this); + m_traced_geoms.add(g); + } + } + CPHObject::SetRayMotions(); +} +void CPHShell::SetPrefereExactIntegration() +{ + CPHObject::SetPrefereExactIntegration(); +} + +void CPHShell::add_Element (CPhysicsElement* E) { + CPHElement* ph_element=cast_PHElement(E); + ph_element->SetShell(this); + elements.push_back(ph_element); + +} + +void CPHShell::add_Joint (CPhysicsJoint* J) { + if(!J)return; + joints.push_back(static_cast(J)); + joints.back()->SetShell(this); +} + +CODEGeom* CPHShell::get_GeomByID(u16 bone_id) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + { + CODEGeom* ret=(*i)->GeomByBoneID(bone_id); + if(ret) return ret; + } + return NULL; +} +void CPHShell::PureStep(float step) +{ + CPHObject::Island().Step(step); + PhDataUpdate(step); +} +void CPHShell::CollideAll() +{ + CPHObject::Collide(); + CPHObject::reinit_single(); +} + +void CPHShell::RegisterToCLGroup (CGID g) +{ + CPHCollideValidator::RegisterObjToGroup(g,*static_cast(this)); +} + +bool CPHShell::IsGroupObject() +{ + return CPHCollideValidator::IsGroupObject(*this); +}; + +void CPHShell::SetIgnoreStatic() +{ + CPHCollideValidator::SetStaticNotCollide(*this); +} + +void CPHShell::SetIgnoreDynamic() +{ + CPHCollideValidator::SetDynamicNotCollide(*this); +} + +void CPHShell::SetRagDoll() +{ + CPHCollideValidator::SetRagDollClass(*this); +} + +void CPHShell::SetIgnoreRagDoll() +{ + CPHCollideValidator::SetRagDollClassNotCollide(*this); +} + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + //Делает данный физический объек анимированным + void CPHShell::SetAnimated() + { + //Для фильтра коллизий относим данный объект к классу анимированных + CPHCollideValidator::SetAnimatedClass(*this); + m_pPhysicsShellAnimatorC= new CPhysicsShellAnimator(this); + //m_pPhysicsShellAnimatorC->ResetCallbacks(); + } + + //Настраивает фильтр коллизий на игнорирование столкновенний данного + //физического объекта с анимированным физическим объектом + void CPHShell::SetIgnoreAnimated() + { + //Для фильтра коллизий указываем, что данный + //физический объект игнорирует анимированные физические тела + + CPHCollideValidator::SetAnimatedClassNotCollide(*this); + } + + //Выдает информацию о том является ли данный объект анимированным + bool CPHShell::Animated() + { + return CPHCollideValidator::IsAnimatedObject(*this); + } +#endif + +void CPHShell:: SetSmall() +{ + CPHCollideValidator::SetClassSmall(*this); +} + +void CPHShell:: SetIgnoreSmall() +{ + CPHCollideValidator::SetClassSmallNotCollide(*this); +} +void CPHShell::CutVelocity(float l_limit,float a_limit) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i)(*i)->CutVelocity(l_limit,a_limit); +} + +void CPHShell::ClearRecentlyDeactivated() +{ + ClearCashedTries(); +} + +void CPHShell::ClearCashedTries() +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i)(*i)->clear_cashed_tries(); +} + +void CPHShell::get_Extensions(const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) +{ + lo_ext=dInfinity;hi_ext=-dInfinity; + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;++i) + { + float temp_lo_ext,temp_hi_ext; + (*i)->get_Extensions(axis,center_prg,temp_lo_ext,temp_hi_ext); + if(lo_ext>temp_lo_ext)lo_ext=temp_lo_ext; + if(hi_extget_CallbackData(); +} + +void CPHShell:: SetBonesCallbacksOverwrite(bool v) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + (*i)->SetBoneCallbackOverwrite(v); +} + + +void CPHShell:: ToAnimBonesPositions () +{ + VERIFY(PKinematics()); + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + for( ;i!=e;++i) + (*i)->ToBonePos(&PKinematics()->LL_GetBoneInstance((*i)->m_SelfID)); + +} + +bool CPHShell:: AnimToVelocityState ( float dt, float l_limit, float a_limit ) +{ + ELEMENT_I i,e; + i=elements.begin(); e=elements.end(); + bool ret = true; + for( ;i!=e;++i) + ret =(*i)->AnimToVel(dt,l_limit,a_limit) && ret; + return ret; +} \ No newline at end of file diff --git a/src/xrGameLA/PHShell.h b/src/xrGameLA/PHShell.h new file mode 100644 index 000000000..f2c531ad2 --- /dev/null +++ b/src/xrGameLA/PHShell.h @@ -0,0 +1,267 @@ +/////////////////////////////////////////////////////////////////////// + +#ifndef PH_SHELL +#define PH_SHELL + +class CPHShell; +class CPHShellSplitterHolder; +class IKinematics; +#include "PHJoint.h" +#include "PHElement.h" +#include "PHDefs.h" +#include "PHShellSplitter.h" +#include "phmovestorage.h" + +//#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + class CPhysicsShellAnimator; +//#endif + +class CPHShell: public CPhysicsShell,public CPHObject { + + friend class CPHShellSplitterHolder; + enum + { + flActive = 1<<0, + flActivating = 1<<1, + flRemoveCharacterCollisionAfterDisable = 1<<2, + }; + s16 m_active_count; + Flags8 m_flags; + ELEMENT_STORAGE elements; + JOINT_STORAGE joints; + CPHShellSplitterHolder *m_spliter_holder; + CPHMoveStorage m_traced_geoms; + +//#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + CPhysicsShellAnimator* m_pPhysicsShellAnimatorC; +//#endif + +protected: + dSpaceID m_space; +public: + Fmatrix m_object_in_root; + CPHShell (); + virtual ~CPHShell (); + virtual void applyImpulseTrace (const Fvector& pos, const Fvector& dir, float val,const u16 id); + virtual void applyHit (const Fvector& pos, const Fvector& dir, float val,const u16 id,ALife::EHitType hit_type); + + static void __stdcall BonesCallback (CBoneInstance* B); + static void __stdcall StataticRootBonesCallBack (CBoneInstance* B); + virtual BoneCallbackFun* GetBonesCallback () {return BonesCallback ;} + virtual BoneCallbackFun* GetStaticObjectBonesCallback() { VERIFY( false ); return StataticRootBonesCallBack; } + virtual void add_Element (CPhysicsElement* E); + virtual void ToAnimBonesPositions (); + virtual bool AnimToVelocityState (float dt, float l_limit, float a_limit ); + virtual void SetBonesCallbacksOverwrite(bool v); + void SetPhObjectInElements (); + virtual void EnableObject (CPHObject* obj); + virtual void DisableObject (); + virtual void SetAirResistance (dReal linear=default_k_l, dReal angular=default_k_w) + { + xr_vector::iterator i; + for(i=elements.begin();elements.end()!=i;++i) + (*i)->SetAirResistance(linear,angular); + } + virtual void GetAirResistance (float& linear, float& angular) + { + (*elements.begin())->GetAirResistance(linear,angular); + } + virtual void add_Joint (CPhysicsJoint* J); + + virtual CPHIsland* PIsland (){return &Island();}; + virtual void applyImpulseTrace (const Fvector& pos, const Fvector& dir, float val) ; + + virtual void Update () ; + + virtual void Activate (const Fmatrix& m0, float dt01, const Fmatrix& m2,bool disable=false); + virtual void Activate (const Fmatrix &transform,const Fvector& lin_vel,const Fvector& ang_vel,bool disable=false); + virtual void Activate (bool disable=false); + virtual void Activate (const Fmatrix& start_from, bool disable=false){}; + + + virtual CPhysicsShellAnimator* PPhysicsShellAnimator(){return m_pPhysicsShellAnimatorC;}; + + +private: + void activate (bool disable); +public: + virtual void Build (bool disable=false); + virtual void RunSimulation (bool place_current_forms=true); + virtual void net_Import (NET_Packet& P); + virtual void net_Export (NET_Packet& P); + void PresetActive (); + void AfterSetActive (); + void PureActivate (); + virtual void Deactivate (); + virtual const CGID& GetCLGroup ()const; + virtual void RegisterToCLGroup (CGID g) ; + virtual bool IsGroupObject () ; + virtual void SetIgnoreStatic () ; + virtual void SetIgnoreDynamic () ; + virtual void SetRagDoll () ; + virtual void SetIgnoreRagDoll () ; + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + virtual void SetAnimated () ; + virtual void SetIgnoreAnimated () ; + virtual bool Animated () ; +#endif + + virtual void SetSmall () ; + virtual void SetIgnoreSmall () ; + virtual void setMass (float M) ; + + virtual void setMass1 (float M) ; + virtual void setEquelInertiaForEls (const dMass& M) ; + virtual void addEquelInertiaToEls (const dMass& M) ; + virtual float getMass () ; + virtual void setDensity (float M) ; + virtual float getDensity () ; + virtual float getVolume () ; + virtual void get_Extensions (const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext); + virtual void applyForce (const Fvector& dir, float val) ; + virtual void applyForce (float x,float y,float z) ; + virtual void applyImpulse (const Fvector& dir, float val) ; + virtual void __stdcall applyGravityAccel (const Fvector& accel); + virtual void setTorque (const Fvector& torque); + virtual void setForce (const Fvector& force); + virtual void set_JointResistance (float force) + { + JOINT_I i; + for(i=joints.begin();joints.end() != i;++i) + { + (*i)->SetForce(force); + (*i)->SetVelocity(); + } + //(*i)->SetForceAndVelocity(force); + } + virtual void set_DynamicLimits (float l_limit=default_l_limit,float w_limit=default_w_limit); + virtual void set_DynamicScales (float l_scale=default_l_scale,float w_scale=default_w_scale); + virtual void set_ContactCallback (ContactCallbackFun* callback); + virtual void set_ObjectContactCallback (ObjectContactCallbackFun* callback); + virtual void add_ObjectContactCallback (ObjectContactCallbackFun* callback); + virtual void remove_ObjectContactCallback (ObjectContactCallbackFun* callback); + virtual void set_CallbackData (void * cd); + virtual void *get_CallbackData (); + virtual void set_PhysicsRefObject (CPhysicsShellHolder* ref_object); + + //breakbable interface + virtual bool isBreakable (); + virtual bool isFractured (); + virtual CPHShellSplitterHolder* SplitterHolder (){return m_spliter_holder;} + virtual void SplitProcess (PHSHELL_PAIR_VECTOR &out_shels); + virtual void BlockBreaking (){if(m_spliter_holder)m_spliter_holder->SetUnbreakable();} + virtual void UnblockBreaking (){if(m_spliter_holder)m_spliter_holder->SetBreakable();} + virtual bool IsBreakingBlocked (){return m_spliter_holder&&m_spliter_holder->IsUnbreakable();} + /////// //////////////////////////////////////////////////////////////////////////////////////////// + virtual void get_LinearVel (Fvector& velocity); + virtual void get_AngularVel (Fvector& velocity); + virtual void set_LinearVel (const Fvector& velocity); + virtual void set_AngularVel (const Fvector& velocity); + virtual void TransformPosition (const Fmatrix &form); + virtual void SetGlTransformDynamic (const Fmatrix &form); + virtual void set_ApplyByGravity (bool flag); + virtual bool get_ApplyByGravity (); + virtual void SetMaterial (u16 m); + virtual void SetMaterial (LPCSTR m); + virtual ELEMENT_STORAGE &Elements (){return elements;} + virtual CPhysicsElement *get_Element (u16 bone_id); + virtual CPhysicsElement *get_Element (const shared_str & bone_name); + virtual CPhysicsElement *get_Element (LPCSTR bone_name); + virtual CPhysicsElement *get_ElementByStoreOrder (u16 num); + virtual u16 get_ElementsNumber (){return (u16)elements.size();} + virtual CPHSynchronize *get_ElementSync (u16 element); + virtual u16 get_elements_number (){return get_ElementsNumber();} + virtual CPHSynchronize *get_element_sync (u16 element){return get_ElementSync(element);} + virtual CPhysicsElement *NearestToPoint (const Fvector& point); + virtual CPhysicsJoint *get_Joint (u16 bone_id); + virtual CPhysicsJoint *get_Joint (const shared_str & bone_name); + virtual CPhysicsJoint *get_Joint (LPCSTR bone_name); + virtual CPhysicsJoint *get_JointByStoreOrder (u16 num); + virtual u16 get_JointsNumber (); + virtual CODEGeom *get_GeomByID (u16 bone_id); + + virtual void Enable (); + virtual void Disable (); + virtual void DisableCollision (); + virtual void EnableCollision (); + virtual void DisableCharacterCollision (); + virtual void SetRemoveCharacterCollLADisable (){m_flags.set(flRemoveCharacterCollisionAfterDisable,TRUE);} + virtual bool isEnabled ()const {return CPHObject::is_active();} + virtual bool isActive ()const {return !!m_flags.test(flActive);} + virtual bool isFullActive ()const {return isActive()&&!m_flags.test(flActivating);} + void SetNotActivating (){m_flags.set(flActivating,FALSE);} +//CPHObject + virtual void vis_update_activate (); + virtual void vis_update_deactivate (); + virtual void PureStep (float step); + virtual void CollideAll (); + virtual void PhDataUpdate (dReal step); + virtual void PhTune (dReal step); + virtual void InitContact (dContact* c,bool &do_collide,u16 /*material_idx_1*/,u16 /*material_idx_2*/){}; + virtual void FreezeContent (); + virtual void UnFreezeContent (); + virtual void Freeze (); + virtual void UnFreeze (); + virtual void NetInterpolationModeON (){CPHObject::NetInterpolationON();} + virtual void NetInterpolationModeOFF (){CPHObject::NetInterpolationOFF();} + virtual void StepFrameUpdate (dReal step){}; + virtual CPHMoveStorage* MoveStorage (){return &m_traced_geoms;} + virtual void build_FromKinematics (IKinematics* K,BONE_P_MAP* p_geting_map=NULL); + virtual void preBuild_FromKinematics (IKinematics* K,BONE_P_MAP* p_geting_map); + virtual void ZeroCallbacks (); + virtual void ResetCallbacks (u16 id,Flags64 &mask); + void PlaceBindToElForms (); + virtual void SetCallbacks (BoneCallbackFun* callback); + virtual void EnabledCallbacks (BOOL val); + virtual void set_DisableParams (const SAllDDOParams& params); + virtual void UpdateRoot (); + virtual void SmoothElementsInertia (float k); + virtual void __stdcall InterpolateGlobalTransform (Fmatrix* m); + virtual void InterpolateGlobalPosition (Fvector* v); + virtual void GetGlobalTransformDynamic (Fmatrix* m); + virtual void GetGlobalPositionDynamic (Fvector* v); + virtual Fmatrix& ObjectInRoot (){return m_object_in_root;} + virtual void ObjectToRootForm (const Fmatrix& form); + virtual dSpaceID dSpace (){return m_space;} + virtual void SetTransform (const Fmatrix& m0); + virtual void AddTracedGeom (u16 element=0,u16 geom=0); + virtual void SetAllGeomTraced (); + virtual void SetPrefereExactIntegration (); + virtual void CutVelocity (float l_limit,float a_limit); +/////////// ////////////////////////////////////////////////////////////////////////////////////////// + void CreateSpace () ; + void PassEndElements (u16 from,u16 to,CPHShell *dest) ; + void PassEndJoints (u16 from,u16 to,CPHShell *dest) ; + void DeleteElement (u16 element) ; + void DeleteJoint (u16 joint) ; + u16 BoneIdToRootGeom (u16 id) ; +///////////////////////////////////////////////////////////////////////////////////////////////////// +protected: + virtual void get_spatial_params () ; + virtual dGeomID dSpacedGeom () {return (dGeomID)m_space;} + + virtual void ClearRecentlyDeactivated () ; + void ClearCashedTries () ; +private: + //breakable + void setEndElementSplitter () ; + void setElementSplitter (u16 element_number,u16 splitter_position) ; + void setEndJointSplitter () ; + void AddSplitter (CPHShellSplitter::EType type,u16 element,u16 joint) ; + void AddSplitter (CPHShellSplitter::EType type,u16 element,u16 joint,u16 position) ; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void AddElementRecursive (CPhysicsElement* root_e, u16 id,Fmatrix global_parent,u16 element_number,bool *vis_check) ; + void PlaceBindToElFormsRecursive (Fmatrix parent,u16 id,u16 element,Flags64 &mask); + void BonesBindCalculate (u16 id_from=0); + void BonesBindCalculateRecursive (Fmatrix parent,u16 id); + void ZeroCallbacksRecursive (u16 id) ; + void SetCallbacksRecursive (u16 id,u16 element) ; + void ResetCallbacksRecursive (u16 id,u16 element,Flags64 &mask) ; + void SetJointRootGeom (CPhysicsElement* root_e,CPhysicsJoint* J) ; + void ReanableObject () ; + void ExplosionHit (const Fvector& pos, const Fvector& dir, float val,const u16 id) ; + void ClearBreakInfo (); +IC CPHElement &root_element () { VERIFY( !elements.empty() ); return *(*elements.begin()); } +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHShellActivate.cpp b/src/xrGameLA/PHShellActivate.cpp new file mode 100644 index 000000000..32e08df11 --- /dev/null +++ b/src/xrGameLA/PHShellActivate.cpp @@ -0,0 +1,280 @@ +#include "StdAfx.h" +#include "PHDynamicData.h" +#include "Physics.h" +#include "tri-colliderknoopc/dTriList.h" +#include "PHShellSplitter.h" +#include "PHFracture.h" +#include "PHJointDestroyInfo.h" +#include "PHCollideValidator.h" +#include "Level.h" +#include "physicsshellholder.h" +#include "PhysicsShellAnimator.h" +#include "../Include/xrRender/Kinematics.h" + +/////////////////////////////////////////////////////////////// +///#pragma warning(disable:4995) +//#include "../../xrODE/ode/src/collision_kernel.h" +//#include "../../xrODE/ode/src/joint.h" +//#include "../../xrODE/ode/src/objects.h" + +//#pragma warning(default:4995) +/////////////////////////////////////////////////////////////////// + +#include "ExtendedGeom.h" + +#include "PHElement.h" +#include "PHShell.h" +void CPHShell::activate(bool disable) +{ + PresetActive(); + if(!CPHObject::is_active()) vis_update_deactivate(); + if(!disable)EnableObject(0); + +} +void CPHShell::Activate(const Fmatrix &m0,float dt01,const Fmatrix &m2,bool disable){ + + if(isActive())return; + activate(disable); +// ELEMENT_I i; + mXFORM.set(m0); + //for(i=elements.begin();elements.end() != i;++i){ + + // (*i)->Activate(m0,dt01, m2, disable); + //} + + { + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;++i)(*i)->Activate(mXFORM,disable); + } + + { + JOINT_I i=joints.begin(),e=joints.end(); + for(;i!=e;++i) (*i)->Activate(); + } + + Fmatrix m; + GetGlobalTransformDynamic (&m); + m.invert();m.mulA_43 (mXFORM); + TransformPosition(m); + if(PKinematics()) + { + SetCallbacks(GetBonesCallback()); + } + + //bActive=true; + //bActivating=true; + m_flags.set(flActive,TRUE); + m_flags.set(flActivating,TRUE); + spatial_register(); +/////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + //mXFORM.set(m0); + //Activate(disable); + Fvector lin_vel; + lin_vel.sub(m2.c,m0.c); + set_LinearVel(lin_vel); +} + + + +void CPHShell::Activate(const Fmatrix &transform,const Fvector& lin_vel,const Fvector& ang_vel,bool disable){ + + if(isActive())return; + activate(disable); + + ELEMENT_I i; + mXFORM.set(transform); + for(i=elements.begin();elements.end() != i;++i){ + (*i)->Activate(transform,lin_vel, ang_vel); + } + + { + JOINT_I i=joints.begin(),e=joints.end(); + for(;i!=e;++i) (*i)->Activate(); + } + + if(PKinematics()) + { + SetCallbacks(GetBonesCallback()); + } + spatial_register(); + //bActive=true; + //bActivating=true; + m_flags.set(flActivating,TRUE); + m_flags.set(flActive,TRUE); +///////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////// + //mXFORM.set(transform); + //Activate(disable); + //set_LinearVel(lin_vel); + //set_AngularVel(ang_vel); + +} + + + +void CPHShell::Activate(bool disable) +{ + if(isActive())return; + + activate(disable); + { + IKinematics* K = m_pKinematics; + + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;++i)(*i)->Activate(mXFORM,disable); + m_pKinematics = K; + } + + { + JOINT_I i=joints.begin(),e=joints.end(); + for(;i!=e;++i) (*i)->Activate(); + } + + if(PKinematics()) + { + SetCallbacks(GetBonesCallback()); + } + spatial_register(); + m_flags.set(flActivating,TRUE); + m_flags.set(flActive,TRUE); + +} + + +void CPHShell::Build(bool disable/*false*/) +{ + if(isActive())return; + + PresetActive(); + m_flags.set(flActivating,TRUE); + m_flags.set(flActive,TRUE); + + { + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;++i) + { + (*i)->build(disable); + } + } + + { + JOINT_I i=joints.begin(),e=joints.end(); + for(;i!=e;++i) (*i)->Create(); + } + +} + +void CPHShell::RunSimulation(bool place_current_forms/*true*/) +{ + if(!CPHObject::is_active()) vis_update_deactivate(); + EnableObject(0); + + + dSpaceSetCleanup(m_space,0); + + { + ELEMENT_I i=elements.begin(),e=elements.end(); + if(place_current_forms) for(;i!=e;++i)(*i)->RunSimulation(mXFORM); + } + { + JOINT_I i=joints.begin(),e=joints.end(); + for(;i!=e;++i) (*i)->RunSimulation(); + } + + spatial_register(); +} + +void CPHShell::AfterSetActive() +{ + if(isActive()) return; + PureActivate(); + //bActive=true; + m_flags.set(flActive,TRUE); + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;++i)(*i)->PresetActive(); + +} + +void CPHShell::PureActivate() +{ + if(isActive()) return; + //bActive=true; + m_flags.set(flActive,TRUE); + if(!CPHObject::is_active()) vis_update_deactivate(); + EnableObject(0); + m_object_in_root.identity(); + spatial_register(); +} + +void CPHShell::PresetActive() +{ + VERIFY(!isActive()); + if(!m_space) + { + m_space=dSimpleSpaceCreate(0); + dSpaceSetCleanup(m_space,0); + + } +} + + + +void CPHShell::Deactivate(){ + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + if (m_pPhysicsShellAnimatorC) + { + xr_delete(m_pPhysicsShellAnimatorC); + } +#endif + + if(!isActive())return; + R_ASSERT2(!ph_world->Processing(),"can not deactivate physics shell during physics processing!!!"); + R_ASSERT2(!ph_world->IsFreezed(),"can not deactivate physics shell when ph world is freezed!!!"); + R_ASSERT2(!CPHObject::IsFreezed(),"can not deactivate freezed !!!"); + ZeroCallbacks(); + VERIFY(ph_world&&ph_world->Exist()); + if(isFullActive()) + { + vis_update_deactivate(); + CPHObject::activate(); + ph_world->Freeze(); + CPHObject::UnFreeze(); + ph_world->StepTouch(); + ph_world->UnFreeze(); + //Fmatrix m; + //InterpolateGlobalTransform(&m); + } + spatial_unregister(); + + vis_update_activate(); + //if(ref_object && !CPHObject::is_active() && m_active_count == 0) + //{ + // ref_object->processing_activate(); + //} + DisableObject(); + CPHObject::remove_from_recently_deactivated(); + + + ELEMENT_I i; + for(i=elements.begin();elements.end() != i;++i) + (*i)->Deactivate(); + + JOINT_I j; + for(j=joints.begin();joints.end() != j;++j) + (*j)->Deactivate(); + + + + if(m_space) { + dSpaceDestroy(m_space); + m_space=NULL; + } + //bActive=false; + //bActivating=false; + m_flags.set(flActivating,FALSE); + m_flags.set(flActive,FALSE); + m_traced_geoms.clear(); + CPHObject::UnsetRayMotions(); +} \ No newline at end of file diff --git a/src/xrGameLA/PHShellCreator.cpp b/src/xrGameLA/PHShellCreator.cpp new file mode 100644 index 000000000..b6040602f --- /dev/null +++ b/src/xrGameLA/PHShellCreator.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "PHShellCreator.h" +#include "PhysicsShell.h" +#include "gameobject.h" +#include "physicsshellholder.h" +#include "../../Include/xrRender/Kinematics.h" + +void CPHShellSimpleCreator::CreatePhysicsShell() +{ + CPhysicsShellHolder* owner = smart_cast(this); VERIFY(owner); + if (!owner->Visual()) return; + + IKinematics* pKinematics = smart_cast(owner->Visual()); + VERIFY (pKinematics); + + if(owner->PPhysicsShell()) return; + owner->PPhysicsShell() = P_create_Shell(); + + owner->m_pPhysicsShell->build_FromKinematics (pKinematics,0); + + owner->PPhysicsShell()->set_PhysicsRefObject (owner); + //m_pPhysicsShell->SmoothElementsInertia(0.3f); + owner->PPhysicsShell()->mXFORM.set (owner->XFORM()); + owner->PPhysicsShell()->SetAirResistance (0.001f, 0.02f); +} \ No newline at end of file diff --git a/src/xrGameLA/PHShellCreator.h b/src/xrGameLA/PHShellCreator.h new file mode 100644 index 000000000..a2ac5daeb --- /dev/null +++ b/src/xrGameLA/PHShellCreator.h @@ -0,0 +1,13 @@ +#ifndef PHSHELL_CREATOR_H +#define PHSHELL_CREATOR_H + +#include "ph_shell_interface.h" + +class CPHShellSimpleCreator: public IPhysicShellCreator +{ +public: + virtual void CreatePhysicsShell(); +protected: +private: +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHShellNetState.cpp b/src/xrGameLA/PHShellNetState.cpp new file mode 100644 index 000000000..4ebacd86e --- /dev/null +++ b/src/xrGameLA/PHShellNetState.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "physicsshell.h" +#include "phinterpolation.h" +#include "phobject.h" +#include "phworld.h" +#include "phshell.h" + +void CPHShell::net_Import(NET_Packet& P) +{ + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;++i) + { + (*i)->net_Import(P); + } +} + +void CPHShell::net_Export(NET_Packet& P) +{ + ELEMENT_I i=elements.begin(),e=elements.end(); + for(;i!=e;++i) + { + (*i)->net_Export(P); + } +} \ No newline at end of file diff --git a/src/xrGameLA/PHShellSplitter.cpp b/src/xrGameLA/PHShellSplitter.cpp new file mode 100644 index 000000000..ebc02eda1 --- /dev/null +++ b/src/xrGameLA/PHShellSplitter.cpp @@ -0,0 +1,529 @@ +#include "stdafx.h" +#include "Physics.h" +#include "PHShell.h" +#include "PHShellSplitter.h" +#include "PHFracture.h" +#include "PHJointDestroyInfo.h" +#include "Geometry.h" +#include "MathUtils.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHCollideValidator.h" + +CPHShellSplitterHolder::CPHShellSplitterHolder(CPHShell* shell) +{ + m_pShell=shell; + m_has_breaks=false; + m_unbreakable=false; +} + +CPHShellSplitterHolder::~CPHShellSplitterHolder() +{ + Deactivate(); + m_splitters.clear(); + m_geom_root_map.clear(); +} +//the simpliest case - a joint to be destroied +shell_root CPHShellSplitterHolder::SplitJoint(u16 aspl) +{ + //create _new physics shell + + CPhysicsShell *new_shell=P_create_Shell(); + CPHShell *new_shell_desc=smart_cast(new_shell); + new_shell_desc->mXFORM.set(m_pShell->mXFORM); + new_shell_desc->m_object_in_root.set(m_pShell->m_object_in_root); + SPLITTER_I splitter=m_splitters.begin()+aspl; + u16 start_element=splitter->m_element; + u16 start_joint=splitter->m_joint; + + u16 end_element=m_pShell->joints[start_joint]->JointDestroyInfo()->m_end_element; + u16 end_joint=m_pShell->joints[start_joint]->JointDestroyInfo()->m_end_joint; + + + shell_root ret = mk_pair(new_shell,(m_pShell->joints[start_joint])->BoneID()); + + + + CShellSplitInfo split_inf; + split_inf.m_bone_id=m_pShell->joints[start_joint]->BoneID(); + split_inf.m_start_el_num=start_element; + split_inf.m_end_el_num=end_element; + split_inf.m_start_jt_num=start_joint; + split_inf.m_end_jt_num=end_joint; + + m_splitters.erase(splitter); + PassEndSplitters(split_inf,new_shell_desc,1,0); + + InitNewShell(new_shell_desc); + m_pShell->PassEndElements(start_element,end_element,new_shell_desc); + m_pShell->PassEndJoints(start_joint+1,end_joint,new_shell_desc); + new_shell_desc->set_PhysicsRefObject(0); + new_shell_desc->PureActivate(); + //new_shell_desc->ObjectInRoot().identity(); + m_pShell->DeleteJoint(start_joint); + new_shell->set_ObjectContactCallback(NULL); + new_shell->set_PhysicsRefObject(NULL); + return ret; +} + +void CPHShellSplitterHolder::PassEndSplitters(const CShellSplitInfo& spl_inf,CPHShell* dest,u16 jt_add_shift,u16 el_add_shift) +{ + + + CPHShellSplitterHolder* &dest_holder=dest->m_spliter_holder; + if(!dest_holder)dest_holder=new CPHShellSplitterHolder(dest); + + ELEMENT_STORAGE &source_elements=m_pShell->elements; + ELEMENT_STORAGE &dest_elements=dest->elements; + ELEMENT_I i_elem=source_elements.begin(),e_elem=source_elements.begin()+spl_inf.m_start_el_num; + u16 shift_e=spl_inf.m_end_el_num-spl_inf.m_start_el_num; + u16 shift_j=spl_inf.m_end_jt_num-spl_inf.m_start_jt_num; + + R_ASSERT2(source_elements.size()>=spl_inf.m_start_el_num&&source_elements.size()>=spl_inf.m_end_el_num,"wrong spl_inf"); + + + for(;i_elem!=e_elem;++i_elem) //until start elem in both joint or elem split fractures + //end elems have to be corrected + //if grater then end elem in moving diapason + { + CPHFracturesHolder *fracturesHolder=(*i_elem)->FracturesHolder(); + if(!fracturesHolder) continue; + FRACTURE_I f_i=fracturesHolder->m_fractures.begin(),f_e=fracturesHolder->m_fractures.end(); + for(;f_i!=f_e;++f_i) + { + + u16 &end_el_num=f_i->m_end_el_num; + u16 &start_el_num=f_i->m_start_el_num; + if(end_el_num>=spl_inf.m_end_el_num) end_el_num=end_el_num-shift_e; + if(start_el_num>=spl_inf.m_end_el_num) start_el_num=start_el_num-shift_e; + + u16 &end_jt_num=f_i->m_end_jt_num; + u16 &start_jt_num=f_i->m_start_jt_num; + if(end_jt_num>=spl_inf.m_end_jt_num) end_jt_num=end_jt_num-shift_j; + if(start_jt_num>=spl_inf.m_end_jt_num) start_jt_num=start_jt_num-shift_j; + } + } + + //same for joints + JOINT_STORAGE &source_joints=m_pShell->joints; + JOINT_I i_joint=source_joints.begin(),e_joint; + if(u16(-1)!=spl_inf.m_start_jt_num) + { + R_ASSERT2(source_joints.size()>=spl_inf.m_start_jt_num&&source_joints.size()>=spl_inf.m_end_jt_num,"wrong spl_inf"); + e_joint=source_joints.begin()+spl_inf.m_start_jt_num; + for(;i_joint!=e_joint;i_joint++) + { + CPHJointDestroyInfo* jointDestroyInfo=(*i_joint)->JointDestroyInfo(); + if(!jointDestroyInfo) continue; + u16 &end_element = jointDestroyInfo->m_end_element; + if(end_element>=spl_inf.m_end_el_num) end_element=end_element-shift_e; + u16 &end_joint = jointDestroyInfo->m_end_joint; + if(end_joint>=spl_inf.m_end_jt_num) end_joint=end_joint-shift_j; + } + } + + + //now process diapason that tobe unsplited + + e_elem=source_elements.begin()+spl_inf.m_end_el_num; + u16 passed_shift_e=spl_inf.m_start_el_num-u16(dest_elements.size()); + u16 passed_shift_j = u16(-1) & (spl_inf.m_start_jt_num + jt_add_shift); + for(;i_elem!=e_elem;++i_elem) + + { + CPHFracturesHolder *fracturesHolder=(*i_elem)->FracturesHolder(); + if(!fracturesHolder) continue; + FRACTURE_I f_i=fracturesHolder->m_fractures.begin(),f_e=fracturesHolder->m_fractures.end(); + for(;f_i!=f_e;++f_i) + { + u16 &end_el_num=f_i->m_end_el_num; + u16 &start_el_num=f_i->m_start_el_num; + end_el_num=end_el_num-passed_shift_e; + start_el_num=start_el_num-passed_shift_e; + + u16 &end_jt_num=f_i->m_end_jt_num; + u16 &start_jt_num=f_i->m_start_jt_num; + end_jt_num=end_jt_num-passed_shift_j; + start_jt_num=start_jt_num-passed_shift_j; + } + } + +//////correct data in fractures for elements allready added to dest with fractures from source/////// +ELEMENT_I i_dest_elem=dest_elements.begin(),e_dest_elem=dest_elements.end(); +for(;i_dest_elem!=e_dest_elem;++i_dest_elem) +{ + CPHFracturesHolder *fracturesHolder=(*i_dest_elem)->FracturesHolder(); + if(!fracturesHolder) continue; + FRACTURE_I f_i=fracturesHolder->m_fractures.begin(),f_e=fracturesHolder->m_fractures.end(); + for(;f_i!=f_e;f_i++) + { + u16 &end_el_num=f_i->m_end_el_num; + u16 &start_el_num=f_i->m_start_el_num; + end_el_num=end_el_num-passed_shift_e; + start_el_num=start_el_num-passed_shift_e; + + u16 &end_jt_num=f_i->m_end_jt_num; + u16 &start_jt_num=f_i->m_start_jt_num; + end_jt_num=end_jt_num-passed_shift_j; + start_jt_num=start_jt_num-passed_shift_j; + } +} +///////////////////////////////////////////////////////////////////////////////////////////////////// + +if(spl_inf.m_end_jt_num!=u16(-1)) +{ + e_joint=source_joints.begin()+spl_inf.m_end_jt_num; + for(;i_joint!=e_joint;++i_joint) + { + CPHJointDestroyInfo* jointDestroyInfo=(*i_joint)->JointDestroyInfo(); + if(!jointDestroyInfo) continue; + u16 &end_element = jointDestroyInfo->m_end_element; + u16 &end_joint = jointDestroyInfo->m_end_joint; + end_element=end_element-passed_shift_e; + end_joint=end_joint-passed_shift_j; + } +} + //the rest unconditionaly shift end & begin + e_elem=source_elements.end(); + for(;i_elem!=e_elem;++i_elem) + + { + CPHFracturesHolder *fracturesHolder=(*i_elem)->FracturesHolder(); + if(!fracturesHolder) continue; + FRACTURE_I f_i=fracturesHolder->m_fractures.begin(),f_e=fracturesHolder->m_fractures.end(); + for(;f_i!=f_e;++f_i) + { + u16 &end_el_num=f_i->m_end_el_num; + u16 &start_el_num=f_i->m_start_el_num; + end_el_num=end_el_num-shift_e; + start_el_num=start_el_num-shift_e; + + u16 &end_jt_num=f_i->m_end_jt_num; + u16 &start_jt_num=f_i->m_start_jt_num; + end_jt_num=end_jt_num-shift_j; + start_jt_num=start_jt_num-shift_j; + } + } + + e_joint=source_joints.end(); + for(;i_joint!=e_joint;++i_joint) + { + CPHJointDestroyInfo* jointDestroyInfo=(*i_joint)->JointDestroyInfo(); + if(!jointDestroyInfo) continue; + u16 &end_element = jointDestroyInfo->m_end_element; + u16 &end_joint = jointDestroyInfo->m_end_joint; + if(end_element>spl_inf.m_end_el_num) end_element=end_element-shift_e; + if(end_joint>spl_inf.m_end_jt_num) end_joint=end_joint-shift_j; + } + // at rest!! pass splitters it is very similar passing fractures + // correct data for splitters before passed and find start splitter to pass + SPLITTER_I spl_e=m_splitters.end(),spl_i=m_splitters.begin(); + for(;spl_i!=spl_e;++spl_i) + { + u16 &elem = spl_i->m_element; + u16 &joint = spl_i->m_joint; + if(spl_i->m_type==CPHShellSplitter::splElement) + { + if( elem!=u16(-1) && elem>=spl_inf.m_start_el_num) break;//we at begining + } + else + { + if( joint!=u16(-1) && joint>=spl_inf.m_start_jt_num) break;//we at begining + } + if(elem!=u16(-1) && elem>spl_inf.m_end_el_num) elem=elem-shift_e; + if(joint!=u16(-1) && joint>spl_inf.m_end_jt_num) joint=joint-shift_j; + } + SPLITTER_I i_from = spl_i; + //correct data for passing splitters and find last splitter to pass + for(;spl_i!=spl_e;++spl_i) + { + u16 &elem = spl_i->m_element; + u16 &joint = spl_i->m_joint; + if(spl_i->m_type==CPHShellSplitter::splElement) + { + if(elem!=u16(-1) && elem>=spl_inf.m_end_el_num) break;//we after begining + } + else + { + if(joint!=u16(-1) && joint>=spl_inf.m_end_jt_num) break;//we after begining + } + if(elem!=u16(-1) ) elem=elem-passed_shift_e; + if(joint!=u16(-1)) joint=joint-passed_shift_j; + + } + + SPLITTER_I i_to = spl_i; + + //corect data for all rest splitters + for(;spl_i!=spl_e;++spl_i) + { + u16 &elem = spl_i->m_element; + u16 &joint = spl_i->m_joint; + if(elem!=u16(-1)) elem=elem-shift_e; + if(joint!=u16(-1)) joint=joint-shift_j; + } + + dest_holder->m_splitters.insert(dest_holder->m_splitters.end(),i_from,i_to); + m_splitters.erase(i_from,i_to); +} + +static ELEMENT_PAIR_VECTOR new_elements; +DEFINE_VECTOR(Fmatrix,TRANSFORM_VECTOR,TRANSFORM_I) +static TRANSFORM_VECTOR bones_bind_forms; +shell_root CPHShellSplitterHolder::ElementSingleSplit(const element_fracture &split_elem,const CPHElement* source_element) +{ + + //const CPHShellSplitter& splitter=m_splitters[aspl]; + //CPHElement* element=m_pShell->elements[splitter.m_element]; + CPhysicsShell *new_shell_last=P_create_Shell(); + CPHShell *new_shell_last_desc=smart_cast(new_shell_last); + new_shell_last->mXFORM.set(m_pShell->mXFORM); const u16 start_joint=split_elem.second.m_start_jt_num; + R_ASSERT(_valid(new_shell_last->mXFORM)); + const u16 end_joint=split_elem.second.m_end_jt_num; + //it is not right for multiple joints attached to the unsplited part becource all these need to be reattached + if(start_joint!=end_joint) + { + JOINT_STORAGE& joints=m_pShell->joints; + JOINT_I i=joints.begin()+ start_joint,e=joints.begin()+ end_joint; + for(;i!=e;++i) + { + + CPHJoint* joint=(*i); + if(joint->PFirst_element()==source_element) + { + IKinematics* K = m_pShell->PKinematics(); + dVector3 safe_pos1, safe_pos2; + dQuaternion safe_q1, safe_q2; + CPhysicsElement* el1=cast_PhysicsElement(split_elem.first),*el2=joint->PSecond_element(); + dBodyID body1=el1->get_body(), body2=el2->get_body(); + dVectorSet(safe_pos1,dBodyGetPosition(body1)); + dVectorSet(safe_pos2,dBodyGetPosition(body2)); + + dQuaternionSet(safe_q1,dBodyGetQuaternion(body1)); + dQuaternionSet(safe_q2,dBodyGetQuaternion(body2)); + + //m_pShell->PlaceBindToElForms(); + + K->LL_GetBindTransform(bones_bind_forms); + el1->SetTransform(bones_bind_forms[el1->m_SelfID]); + el2->SetTransform(bones_bind_forms[el2->m_SelfID]); + joint->ReattachFirstElement(split_elem.first); + + dVectorSet(const_cast(dBodyGetPosition(body1)),safe_pos1); + dVectorSet(const_cast(dBodyGetPosition(body2)),safe_pos2); + + dQuaternionSet(const_cast(dBodyGetQuaternion(body1)),safe_q1); + dQuaternionSet(const_cast(dBodyGetQuaternion(body2)),safe_q2); + + dBodySetPosition(body1,safe_pos1[0],safe_pos1[1],safe_pos1[2]); + dBodySetPosition(body2,safe_pos2[0],safe_pos2[1],safe_pos2[2]); + dBodySetQuaternion(body1,safe_q1); + dBodySetQuaternion(body2,safe_q2); + } + } + // m_pShell->joints[split_elem.second.m_start_jt_num]->ReattachFirstElement(split_elem.first); + } + + //the last new shell will have all splitted old elements end joints and one new element reattached to old joint + //m_splitters.erase(m_splitters.begin()+aspl); + //now aspl points to the next splitter + if((split_elem.first)->FracturesHolder())//if this element can be splitted add a splitter for it + new_shell_last_desc->AddSplitter(CPHShellSplitter::splElement,0,u16(-1));// + + new_shell_last_desc->add_Element(split_elem.first); + //pass splitters taking into account that one element was olready added + PassEndSplitters(split_elem.second,new_shell_last_desc,0,0); + + + InitNewShell(new_shell_last_desc); + m_pShell->PassEndElements(split_elem.second.m_start_el_num,split_elem.second.m_end_el_num,new_shell_last_desc); + + + + m_pShell->PassEndJoints(split_elem.second.m_start_jt_num,split_elem.second.m_end_jt_num,new_shell_last_desc); + new_shell_last_desc->set_PhysicsRefObject(0); +///////////////////temporary for initialization set old Kinematics in new shell///////////////// + new_shell_last->set_Kinematics(m_pShell->PKinematics()); + new_shell_last_desc->AfterSetActive(); + new_shell_last->set_Kinematics(NULL); + VERIFY2(split_elem.second.m_bone_id<64,"strange root"); + VERIFY(_valid(new_shell_last->mXFORM)); + VERIFY(dBodyStateValide(source_element->get_bodyConst())); + VERIFY(dBodyStateValide(split_elem.first->get_body())); + new_shell_last->set_ObjectContactCallback(NULL); + new_shell_last->set_PhysicsRefObject(NULL); + return mk_pair(new_shell_last,split_elem.second.m_bone_id); + +} + + +IC void correct_diapasones(ELEMENT_PAIR_VECTOR& element_pairs) +{ + ELEMENT_PAIR_I i,b=element_pairs.begin(),e=element_pairs.end(); + + for(i=b;i!=e;++i) + { + ELEMENT_PAIR_I j=i+1; + for(;j!=e;++j) + { + j->second.sub_diapasone(CShellSplitInfo(i->second)); + } + + } + +} + +void CPHShellSplitterHolder::SplitElement(u16 aspl,PHSHELL_PAIR_VECTOR &out_shels) +{ + + new_elements.clear(); + SPLITTER_I spl_i=(m_splitters.begin()+aspl); + CPHElement* E= m_pShell->elements[spl_i->m_element]; + E->SplitProcess(new_elements); + + correct_diapasones(new_elements); + + ELEMENT_PAIR_I i=new_elements.begin(),e=new_elements.end(); + + for(;i!=e;++i) + { + out_shels.push_back(ElementSingleSplit(*i,E)); + VERIFY(dBodyStateValide(out_shels.back().first->get_ElementByStoreOrder(0)->get_body())); + } + + if(!E->FracturesHolder()) m_splitters.erase(spl_i);//delete splitter if the element no longer have fractures + else spl_i->m_breaked=false;//it is no longer breaked + +} + +void CPHShellSplitterHolder::SplitProcess(PHSHELL_PAIR_VECTOR &out_shels) +{ + //any split process must start from the end of the elment storage + //this based on that all childs in the bone hierarchy was added after their parrent + + u16 i=u16(m_splitters.size()-1); + for(;u16(-1)!=i;--i) + { + if(m_splitters[i].m_breaked) + switch(m_splitters[i].m_type) + { + case CPHShellSplitter::splJoint: + out_shels.push_back(SplitJoint(i)); + + break; + case CPHShellSplitter::splElement: + SplitElement(i,out_shels); + break; + default: NODEFAULT; + } + } + //VERIFY(dBodyStateValide(out_shels.back().first->get_ElementByStoreOrder(0)->get_body())); + m_has_breaks=false; +} +void CPHShellSplitterHolder::InitNewShell(CPHShell* shell) +{ + shell->PresetActive(); + if(m_pShell->IsGroupObject()) + CPHCollideValidator::RegisterObjToGroup(m_pShell->collide_bits(),*static_cast(shell)); +} + +void CPHShellSplitterHolder::PhTune(dReal step) +{ + SPLITTER_I i=m_splitters.begin(),e=m_splitters.end(); + for(;i!=e;++i) + { + switch(i->m_type) + { + case CPHShellSplitter::splElement: + { + CPHElement* element=m_pShell->elements[i->m_element]; + element->FracturesHolder()->PhTune(element->get_body()); + break; + } + case CPHShellSplitter::splJoint: + { + break; + } + default: NODEFAULT; + } + } +} +void CPHShellSplitterHolder::PhDataUpdate(dReal step) +{ + SPLITTER_I i=m_splitters.begin(),e=m_splitters.end(); + for(;i!=e;++i) + { + switch(i->m_type) + { + case CPHShellSplitter::splElement: + { + CPHElement* element=m_pShell->elements[i->m_element]; + dBodyID body=element->get_body();//!element->EnabledStateOnStep() + if(!dBodyIsEnabled(body)) return;// + i->m_breaked=(element->FracturesHolder()->PhDataUpdate(element))||i->m_breaked; + break; + } + case CPHShellSplitter::splJoint: + { + CPHJoint *j=m_pShell->joints[i->m_joint]; + //if(j->bActive) + i->m_breaked=j->JointDestroyInfo()->Update()||i->m_breaked; + break; + } + default: NODEFAULT; + } + m_has_breaks=m_has_breaks||i->m_breaked; + } +} +void CPHShellSplitterHolder::Activate() +{ + if(m_unbreakable) return; + CPHUpdateObject::Activate(); + if(m_pShell->isActive())PhTune(fixed_step); +} + +void CPHShellSplitterHolder::Deactivate() +{ + CPHUpdateObject::Deactivate(); +} +void CPHShellSplitterHolder::AddSplitter(CPHShellSplitter::EType type,u16 element,u16 joint) +{ + m_splitters.push_back(CPHShellSplitter(type,element,joint)); +} +void CPHShellSplitterHolder::AddSplitter(CPHShellSplitter::EType type,u16 element,u16 joint,u16 position) +{ + m_splitters.insert(m_splitters.begin()+position,CPHShellSplitter(type,element,joint)); +} +CPHShellSplitter::CPHShellSplitter(CPHShellSplitter::EType type,u16 element,u16 joint) +{ + m_breaked=false; + m_type=type; + m_element=element; + m_joint=joint; +} + +void CPHShellSplitterHolder::AddToGeomMap(const id_geom& id_rootgeom) +{ + m_geom_root_map.insert(id_rootgeom); +} + +u16 CPHShellSplitterHolder::FindRootGeom(u16 bone_id) +{ + GEOM_MAP_I iter=m_geom_root_map.find(bone_id); + if(iter==m_geom_root_map.end()) return u16(-1); + + return iter->second->element_position(); + +} +void CPHShellSplitterHolder::SetUnbreakable() +{ + Deactivate();m_unbreakable=true; +} +void CPHShellSplitterHolder::SetBreakable() +{ + m_unbreakable=false;if(m_pShell->isEnabled())Activate(); +} +CPHShellSplitter::CPHShellSplitter() +{ + m_breaked=false; +} + diff --git a/src/xrGameLA/PHShellSplitter.h b/src/xrGameLA/PHShellSplitter.h new file mode 100644 index 000000000..7e67fbc80 --- /dev/null +++ b/src/xrGameLA/PHShellSplitter.h @@ -0,0 +1,65 @@ +#ifndef PHSHELL_SPLITTER_H +#define PHSHELL_SPLITTER_H +#include "PHDefs.h" +#include "PHObject.h" + +class CPHShellSplitter +{ + friend class CPHShellSplitterHolder; + friend class CPHShell; + +public: + enum EType {splElement,splJoint} ; +private: + bool m_breaked ; + EType m_type ; + u16 m_element ; + u16 m_joint ; + CPHShellSplitter (CPHShellSplitter::EType type,u16 element,u16 joint) ; + CPHShellSplitter () ; +}; + +//class CPHShellSplitter; +class CPHShell; +class CODEGeom; +typedef std::pair id_geom; +DEFINE_MAP(u16,CODEGeom*,GEOM_MAP,GEOM_MAP_I) +DEFINE_VECTOR(CPHShellSplitter,SPLITTER_STORAGE,SPLITTER_I) +typedef xr_vector::reverse_iterator SPLITTER_RI; + +class CPHShellSplitterHolder : public CPHUpdateObject //call all Fractures and Breakable Joints Updates +{ +friend class CPHShell; +bool m_has_breaks ; +bool m_unbreakable ; +CPHShell* m_pShell ; //purpose: to extract elements and joints corresponded splitters +SPLITTER_STORAGE m_splitters ; // +GEOM_MAP m_geom_root_map ; //to find geom pointer by bone id +virtual void PhTune (dReal step) ; //call fractures PhTune for element splitters m_pShell->m_elements[m_splitters[i]->m_element]->m_pFracturesHolder->PhTune() +virtual void PhDataUpdate (dReal step) ; //call fractures PhDataUpdate for element splitters m_pShell->m_elements[m_splitters[i]->m_element]->m_pFracturesHolder->PhDataUpdate() + bool CheckSplitter (u16 aspl) ; // + shell_root SplitJoint (u16 aspl) ; //create new shell moving into it departed elements and joints + shell_root ElementSingleSplit (const element_fracture &split_elem,const CPHElement* source_element) ; + void SplitElement (u16 aspl,PHSHELL_PAIR_VECTOR &out_shels) ;// + void PassEndSplitters (const CShellSplitInfo& spl_inf,CPHShell* dest,u16 jt_add_shift,u16 el_add_shift) ; + void InitNewShell (CPHShell* shell) ; //inits new active shell +public: + CPHShellSplitterHolder (CPHShell* shell) ; +virtual ~CPHShellSplitterHolder () ; + void Activate () ; + void Deactivate () ; + void AddSplitter (CPHShellSplitter::EType type,u16 element,u16 joint) ; + void AddSplitter (CPHShellSplitter::EType type,u16 element,u16 joint,u16 position) ; + void SplitProcess (PHSHELL_PAIR_VECTOR &out_shels) ; + void AddToGeomMap (const id_geom& id_rootgeom) ; + u16 FindRootGeom (u16 bone_id) ; +IC bool Breaked () {return m_has_breaks;} +IC bool isEmpty () {return m_splitters.empty() ;} + void SetUnbreakable () ; + void SetBreakable () ; + bool IsUnbreakable () {return m_unbreakable ;} +}; + + + +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHSimpleCalls.cpp b/src/xrGameLA/PHSimpleCalls.cpp new file mode 100644 index 000000000..89566689e --- /dev/null +++ b/src/xrGameLA/PHSimpleCalls.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" +#include "physicsshell.h" +#include "phsimplecalls.h" +#include "phobject.h" +#include "PHWorld.h" +extern CPHWorld *ph_world; + + +CPHCallOnStepCondition::CPHCallOnStepCondition() +{ + if(ph_world)set_step(ph_world->m_steps_num); + else set_step(0); +} + +IC bool CPHCallOnStepCondition::time_out() const +{ + return ph_world->m_steps_num>m_step; +} + +bool CPHCallOnStepCondition::is_true() +{ + return time_out(); +} +bool CPHCallOnStepCondition::obsolete() const +{ + return time_out(); +} + + +void CPHCallOnStepCondition::set_steps_interval(u64 steps) +{ + set_step(ph_world->m_steps_num+steps); +} +void CPHCallOnStepCondition::set_time_interval(float time) +{ + set_steps_interval(iCeil(time/fixed_step)); +} +void CPHCallOnStepCondition::set_time_interval(u32 time) +{ + set_time_interval(float(time)/1000.f); +} +void CPHCallOnStepCondition::set_global_time(float time) +{ + float time_interval=Device.fTimeGlobal-time; + if(time_interval<0.f)set_step(ph_world->m_steps_num); + set_time_interval(time_interval); +} +void CPHCallOnStepCondition::set_global_time(u32 time) +{ + set_global_time(float(time)/1000.f); +} + +CPHShellBasedAction::CPHShellBasedAction(CPhysicsShell *shell) +{ + VERIFY(shell&&shell->isActive()); + m_shell=shell; +} +bool CPHShellBasedAction::obsolete() const +{ + return !m_shell||!m_shell->isActive(); +} + +CPHConstForceAction::CPHConstForceAction(CPhysicsShell *shell, const Fvector &force) +:CPHShellBasedAction(shell) +{ + m_force.set(force); +} + + +void CPHConstForceAction::run() +{ + m_shell->applyForce(m_force.x,m_force.y,m_force.z); +} + +CPHReqComparerHasShell::CPHReqComparerHasShell(CPhysicsShell *shell) +{ + VERIFY(shell); + m_shell=shell; +} +//CPHTimeCondition::CPHTimeCondition(u32 time) +//{ +// //m_step=u64(ph_world->CalcNumSteps(time))+ph_world->m_steps_num; +//} +// +//CPHTimeCondition::CPHTimeCondition(float time) +//{ +// ///if (dTime < m_frame_time*1000) return 0; +// u32 res = iCeil((float(dTime) - m_frame_time*1000) / (fixed_step*1000)); +// m_step= +//} + diff --git a/src/xrGameLA/PHSimpleCalls.h b/src/xrGameLA/PHSimpleCalls.h new file mode 100644 index 000000000..fe2593f37 --- /dev/null +++ b/src/xrGameLA/PHSimpleCalls.h @@ -0,0 +1,82 @@ +#pragma once + +#include "PHCommander.h" +#include "PHReqComparer.h" +#include "alife_space.h" +#include "script_export_space.h" +class CPhysicsShell; +class CPHCallOnStepCondition: + public CPHCondition +{ + u64 m_step ; +public: + CPHCallOnStepCondition () ; + virtual bool obsolete () const ; + virtual bool is_true () ; +IC void set_step (u64 step) {m_step=step;} + void set_steps_interval (u64 steps) ; + void set_time_interval (u32 time) ; + void set_time_interval (float time) ; + void set_global_time (float time) ; + void set_global_time (u32 time) ; +private: +IC bool time_out () const ; +DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CPHCallOnStepCondition) +#undef script_type_list +#define script_type_list save_type_list(CPHCallOnStepCondition) + +class CPHExpireOnStepCondition: + public CPHCallOnStepCondition +{ + +public: + + virtual bool is_true () {return true;} +DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list( CPHExpireOnStepCondition) +#undef script_type_list +#define script_type_list save_type_list( CPHExpireOnStepCondition) + +class CPHShellBasedAction: + public CPHAction +{ +protected: + CPhysicsShell *m_shell; +public: + CPHShellBasedAction (CPhysicsShell *shell) ; + + + virtual bool compare (const CPhysicsShell * shl) const {return shl==m_shell;} + virtual bool obsolete () const ; +}; + +class CPHConstForceAction: + public CPHShellBasedAction +{ + + Fvector m_force; +public: + CPHConstForceAction (CPhysicsShell *shell,const Fvector &force) ; + virtual void run () ; + + virtual bool compare (const CPHReqComparerV * v) const {return v->compare(this);} + virtual bool compare (const CPhysicsShell * shl) const {return CPHShellBasedAction::compare(shl);} +DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list( CPHConstForceAction) +#undef script_type_list +#define script_type_list save_type_list( CPHConstForceAction) + +class CPHReqComparerHasShell : + public CPHReqComparerV +{ + CPhysicsShell *m_shell ; +public: + CPHReqComparerHasShell (CPhysicsShell *shl) ; + virtual bool compare (const CPHConstForceAction* v) const {return v->compare(m_shell);} +}; \ No newline at end of file diff --git a/src/xrGameLA/PHSimpleCallsScript.cpp b/src/xrGameLA/PHSimpleCallsScript.cpp new file mode 100644 index 000000000..fa47eaa7a --- /dev/null +++ b/src/xrGameLA/PHSimpleCallsScript.cpp @@ -0,0 +1,42 @@ +#include "pch_script.h" +#include "PHSimpleCalls.h" +#include "PhysicsShell.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CPHCallOnStepCondition::script_register(lua_State *L) +{ + module(L) + [ + class_("phcondition_callonstep") + .def("set_step", &CPHCallOnStepCondition::set_step) + .def("set_steps_interval", &CPHCallOnStepCondition::set_steps_interval) + .def("set_global_time_ms", (void(CPHCallOnStepCondition::*)(u32))(&CPHCallOnStepCondition::set_global_time)) + .def("set_global_time_s", (void(CPHCallOnStepCondition::*)(float))(&CPHCallOnStepCondition::set_global_time)) + .def("set_time_interval_ms", (void(CPHCallOnStepCondition::*)(u32))(&CPHCallOnStepCondition::set_time_interval)) + .def("set_time_interval_s", (void(CPHCallOnStepCondition::*)(float))(&CPHCallOnStepCondition::set_time_interval)) + .def(constructor<>()) + ]; +} + +void CPHExpireOnStepCondition::script_register(lua_State *L) +{ + module(L) + [ + class_("phcondition_expireonstep") + .def(constructor<>()) + ]; +} + +void CPHConstForceAction::script_register(lua_State *L) +{ + module(L) + [ + class_("phaction_constforce") + .def(constructor()) + ]; +} +//(CPhysicsJoint*(CPhysicsShell::*)(u16))(&CPhysicsShell::get_Joint)) +//.def("set_gravity", &CPHWorld::SetGravity), +//.def("add_call", &CPHWorld::AddCall) \ No newline at end of file diff --git a/src/xrGameLA/PHSimpleCharacter.cpp b/src/xrGameLA/PHSimpleCharacter.cpp new file mode 100644 index 000000000..de8ae9c1a --- /dev/null +++ b/src/xrGameLA/PHSimpleCharacter.cpp @@ -0,0 +1,1852 @@ +#include "stdafx.h" + +#include "PHDynamicData.h" +#include "ExtendedGeom.h" +#include "../cl_intersect.h" +#include "tri-colliderKNoOPC\__aabb_tri.h" +#include "PHSimpleCharacter.h" +#include "PHContactBodyEffector.h" +#include "ui/uistatic.h" +#include "SpaceUtils.h" +#include "PhysicsGamePars.h" +#include "MathUtils.h" +#include "level.h" +#include "../gamemtllib.h" +#include "gameobject.h" +#include "physicsshellholder.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHSimpleCharacterInline.h" +#include "DamageSource.h" +#include "PHCollideValidator.h" +#include "CalculateTriangle.h" +#include "game_base_space.h" +#include "mathutilsode.h" + +//#include "phvalide.h" + + + +IC bool PhOutOfBoundaries (const Fvector& v) +{ + return v.y < phBoundaries.y1; +} +#ifdef DEBUG +# include "debug_renderer.h" +#endif + +const float LOSE_CONTROL_DISTANCE=0.5f; //fly distance to lose control +const float CLAMB_DISTANCE=0.5f; +const float CLIMB_GETUP_HEIGHT=0.3f; + +float IC sgn(float v) +{ + return v<0.f ? -1.f:1.f; +} +bool test_sides(const Fvector ¢er,const Fvector &side_dir,const Fvector &fv_dir,const Fvector &box,int tri_id) +{ + Triangle tri; + InitTriangle(Level().ObjectSpace.GetStaticTris()+tri_id,tri); + Fvector* verts=Level().ObjectSpace.GetStaticVerts(); + { + float dist=cast_fv(tri.norm).dotproduct(center)-tri.dist; + //if(dist<0.f)return false; + //////////////////////////////////////////////tri norm + float fvn=cast_fv(tri.norm).dotproduct(fv_dir);float sg_fvn= sgn(fvn); + float sdn=cast_fv(tri.norm).dotproduct(side_dir);float sg_sdn= sgn(sdn); + if(sg_fvn*fvn*box.z+sg_sdn*sdn*box.x+_abs(tri.norm[1])*box.y>_abs(dist))return false; + } + { + + ///////////////////////////////////////////////side/////////////////////////////////////////////////////////////////////////////////////////////////// + float sdc=side_dir.dotproduct(center); + float sd0=cast_fv(tri.side0).dotproduct(side_dir);float sg_sd0=sgn(sd0); + float sd1=cast_fv(tri.side1).dotproduct(side_dir);float sg_sd1=sgn(sd1); + float abs_sd0=sg_sd0*sd0; + float abs_sd1=sg_sd1*sd1; + + float sd,abs_sd,sg_sd; + u32 v; + if(sg_sd0==sg_sd1) + { + sd=-sd0-sd1; + abs_sd=abs_sd0+abs_sd1; + sg_sd=-sg_sd0; + v=tri.T->verts[2]; + }else if(abs_sd0>abs_sd1) + { + sd=sd0;abs_sd=abs_sd0;sg_sd=sg_sd0; + v=tri.T->verts[0]; + }else + { + sd=sd1;abs_sd=abs_sd1;sg_sd=sg_sd1; + v=tri.T->verts[1]; + } + + float vp=side_dir.dotproduct(verts[v]); + float dist=vp-sdc; + float sg_dist=sgn(dist); + float abs_dist=sg_dist*dist; + + if(sg_dist!=sg_sd) + { + if(abs_dist-abs_sd>box.x)return false; + }else + { + if(abs_dist>box.x) return false; + } + } +////sides cross/////////////////////////////////////////////////////////////////////////////////////////////// + Fvector crses[3]; + crses[0].set(-tri.side0[2],0,tri.side0[0]); + crses[1].set(-tri.side1[2],0,tri.side1[0]); + const Fvector& v2=verts[tri.T->verts[2]]; + const Fvector& v0=verts[tri.T->verts[0]]; + crses[2].x=-(v0.z-v2.z); + crses[2].y=0.f; + crses[2].z=v0.x-v2.x; + for(u8 i=0;3>i;++i) + { + const Fvector &crs=crses[i]; + u32 sv=tri.T->verts[i%3]; + u32 ov=tri.T->verts[(i+2)%3]; + + float c_prg=crs.dotproduct(center); + float sv_prg=crs.dotproduct(verts[sv]); + float ov_prg=crs.dotproduct(verts[ov]); + float dist=c_prg-sv_prg; + float sg_dist=sgn(dist); + if(sgn(ov_prg-c_prg)!=sg_dist) + { + if(_abs(fv_dir.dotproduct(crs))*box.z+_abs(side_dir.dotproduct(crs))*box.x(retrieveGeomUserData(c.geom.g1)->ph_object); + }else + { + ch=static_cast(retrieveGeomUserData(c.geom.g2)->ph_object); + } + VERIFY(ch); + ch->b_side_contact=true; +} + +void CPHSimpleCharacter::SetBox(const dVector3 &sizes) +{ + m_radius=_min(sizes[0],sizes[2])/2.f; + m_cyl_hight=sizes[1]-2.f*m_radius; + if (m_cyl_hight<0.f) m_cyl_hight=0.01f; + const dReal k=1.20f; + dReal doun=m_radius*_sqrt(1.f-1.f/k/k)/2.f; + //m_geom_shell=dCreateCylinder(0,m_radius/k,m_cyl_hight+doun); + dGeomCylinderSetParams(m_geom_shell,m_radius/k,m_cyl_hight+doun); + //m_wheel=dCreateSphere(0,m_radius); + dGeomSphereSetRadius(m_wheel,m_radius); + //m_hat=dCreateSphere(0,m_radius/k); + dGeomSphereSetRadius(m_hat,m_radius/k); + + dGeomSetPosition(m_hat,0.f,m_cyl_hight,0.f); + dGeomSetPosition(m_geom_shell,0.f,m_cyl_hight/2.f-doun,0.f); + + float test_radius=m_radius*2.f; + float test_height=test_radius+m_radius/2.f; + //m_cap=dCreateSphere(0,test_radius); + dGeomSphereSetRadius(m_cap,test_radius); + dGeomSetPosition(m_cap,0.f,test_height,0.f); +} +void CPHSimpleCharacter::Create(dVector3 sizes){ + + if(b_exist) return; + + b_air_contact_state = false ; + lastMaterialIDX = u16(-1) ; + m_creation_step = ph_world->m_steps_num; + //////////////////////////////////////////////////////// + + m_radius=_min(sizes[0],sizes[2])/2.f; + m_current_object_radius=m_radius; + m_cyl_hight=sizes[1]-2.f*m_radius; + if (m_cyl_hight<0.f) m_cyl_hight=0.01f; + + b_exist=true; + const dReal k=1.20f; + dReal doun=m_radius*_sqrt(1.f-1.f/k/k)/2.f; + + m_geom_shell=dCreateCylinder(0,m_radius/k,m_cyl_hight+doun); + + m_wheel=dCreateSphere(0,m_radius); + m_hat=dCreateSphere(0,m_radius/k); + + + + + m_shell_transform=dCreateGeomTransform(0); + dGeomTransformSetCleanup(m_shell_transform,0); + m_hat_transform=dCreateGeomTransform(0); + dGeomTransformSetCleanup(m_hat_transform,0); + //m_wheel_transform=dCreateGeomTransform(0); + + dGeomTransformSetInfo(m_shell_transform,1); + dGeomTransformSetInfo(m_hat_transform,1); + //dGeomTransformSetInfo(m_wheel_transform,1); + //////////////////////////////////////////////////////////////////////// + + + dGeomSetPosition(m_hat,0.f,m_cyl_hight,0.f); + //////////////////////////////////////////////////////////////////////// + + //dGeomSetPosition(chSphera2,0.f,-0.7f,0.f); + + //////////////////////////////////////////////////////////////////////// + dGeomSetPosition(m_geom_shell,0.f,m_cyl_hight/2.f-doun,0.f); + + dGeomTransformSetGeom(m_hat_transform,m_hat); + + dGeomTransformSetGeom(m_shell_transform,m_geom_shell); + m_body=dBodyCreate(0); + Island().AddBody(m_body); + + dGeomSetBody(m_shell_transform,m_body); + dGeomSetBody(m_hat_transform,m_body); + + dGeomSetBody(m_wheel,m_body); + + dGeomCreateUserData(m_geom_shell); + + dGeomCreateUserData(m_wheel); + dGeomCreateUserData(m_hat); + + dGeomUserDataSetPhObject(m_wheel,(CPHObject*)this); + dGeomUserDataSetPhObject(m_geom_shell,(CPHObject*)this); + dGeomUserDataSetPhObject(m_hat,(CPHObject*)this); + + ///////////////////////////////////////////////////////////////////////// + dMass m; + dMassSetBox(&m,1,1000000.f,1000000.f,1000000.f); + dMassAdjust(&m,m_mass); + dBodySetMass(m_body,&m); + + m_space=dSimpleSpaceCreate(0); + //dGeomGroupAdd(m_geom_group,m_wheel_transform); + dSpaceAdd(m_space,m_wheel); + + dSpaceAdd(m_space,m_shell_transform); + dSpaceAdd(m_space,m_hat_transform); + //dGeomGroupAdd(chRGeomGroup,chRCylinder); + m_body_interpolation.SetBody(m_body); + + + + float test_radius=m_radius*2.f; + float test_height=test_radius+m_radius/2.f; + m_cap=dCreateSphere(0,test_radius); + dGeomSetPosition(m_cap,0.f,test_height,0.f); + m_cap_transform=dCreateGeomTransform(0); + dGeomTransformSetCleanup(m_cap_transform,0); + dGeomTransformSetInfo(m_cap_transform,1); + dGeomTransformSetGeom(m_cap_transform,m_cap); + dGeomCreateUserData(m_cap); + dGeomGetUserData(m_cap)->b_static_colide=false; + + dGeomUserDataSetPhObject(m_cap,(CPHObject*)this); + dSpaceAdd(m_space,m_cap_transform); + dGeomSetBody(m_cap_transform,m_body); + dGeomUserDataSetObjectContactCallback(m_cap,TestPathCallback); + dGeomGetUserData(m_cap)->b_static_colide=false; + if(m_phys_ref_object) + { + SetPhysicsRefObject(m_phys_ref_object); + } + if(m_object_contact_callback) + { + SetObjectContactCallback(m_object_contact_callback); + } + SetStaticContactCallBack(CharacterContactShotMark); + m_elevator_state.SetCharacter(static_cast(this)); + CPHObject::activate(); + spatial_register(); + m_last_move.set(0,0,0) ; + CPHCollideValidator::SetCharacterClass(*this); + m_collision_damage_info.Construct(); +} +void CPHSimpleCharacter::SwitchOFFInitContact() +{ + VERIFY(b_exist); + dGeomUserDataSetPhObject(m_wheel,0); + dGeomUserDataSetPhObject(m_geom_shell,0); + dGeomUserDataSetPhObject(m_hat,0); + b_lose_control=true; + b_any_contacts=false; + is_contact=false; + b_foot_mtl_check =true ; + b_on_ground=b_valide_ground_contact=false; +} +void CPHSimpleCharacter::SwitchInInitContact() +{ + VERIFY(b_exist); + dGeomUserDataSetPhObject(m_wheel,(CPHObject*)this); + dGeomUserDataSetPhObject(m_geom_shell,(CPHObject*)this); + dGeomUserDataSetPhObject(m_hat,(CPHObject*)this); + +} +void CPHSimpleCharacter::Destroy(){ + if(!b_exist) return; + b_exist=false; + R_ASSERT2(!ph_world->Processing(),"can not deactivate physics character shell during physics processing!!!");//if(ph_world) + R_ASSERT2(!ph_world->IsFreezed(),"can not deactivate physics character when ph world is freezed!!!"); + R_ASSERT2(!CPHObject::IsFreezed(),"can not deactivate freezed !!!"); + m_elevator_state.Deactivate(); + + spatial_unregister(); + CPHObject::deactivate(); + + + if(m_cap) { + dGeomDestroyUserData(m_cap); + dGeomDestroy(m_cap); + m_cap=NULL; + } + + if(m_cap_transform){ + dGeomDestroyUserData(m_cap_transform); + dGeomDestroy(m_cap_transform); + m_cap_transform=NULL; + } + + if(m_geom_shell){ + dGeomDestroyUserData(m_geom_shell); + dGeomDestroy(m_geom_shell); + m_geom_shell=NULL; + } + + if(m_wheel) { + dGeomDestroyUserData(m_wheel); + dGeomDestroy(m_wheel); + m_wheel=NULL; + } + + if(m_shell_transform){ + dGeomDestroyUserData(m_shell_transform); + dGeomDestroy(m_shell_transform); + m_shell_transform=NULL; + } + + + + + if(m_wheel_transform){ + dGeomDestroyUserData(m_wheel_transform); + dGeomDestroy(m_wheel_transform); + m_wheel_transform=NULL; + } + + + if(m_hat){ + dGeomDestroyUserData(m_hat); + dGeomDestroy(m_hat); + m_hat=NULL; + } + if(m_hat_transform){ + dGeomDestroyUserData(m_hat_transform); + dGeomDestroy(m_hat_transform); + m_hat_transform=NULL; + } + + if(m_space){ + dSpaceDestroy(m_space); + m_space=NULL; + } + + if(m_body) { + Island().RemoveBody(m_body); + dBodyDestroy(m_body); + m_body=NULL; + } + + +} +const static u64 impulse_time_constant=30; +void CPHSimpleCharacter::ApplyImpulse(const Fvector& dir,dReal P) +{ + if(!b_exist||b_external_impulse) return; + //if(!dBodyIsEnabled(m_body)) dBodyEnable(m_body); + m_ext_imulse.set(dir); + if(b_lose_control||b_jumping||b_jump) + { + //m_ext_imulse.y-=3.f ; + //m_ext_imulse.normalize_safe() ; + m_ext_imulse.set(0,-1,0); + //P*=0.3f ; + } + Enable(); + b_lose_control=true; + b_external_impulse=true; + m_ext_impuls_stop_step=ph_world->m_steps_num+impulse_time_constant; + //m_ext_imulse.set(Fvector().mul(dir,P/fixed_step/impulse_time_constant)); + dBodySetForce(m_body,m_ext_imulse.x*P/fixed_step,m_ext_imulse.y*P/fixed_step,m_ext_imulse.z*P/fixed_step); +} + +void CPHSimpleCharacter::ApplyForce(const Fvector& force) +{ + ApplyForce(force.x,force.y,force.z); +} + +void CPHSimpleCharacter::ApplyForce(float x, float y, float z) +{ + if(!b_exist) return; + Enable(); + dBodyAddForce(m_body,x,y,z); + //BodyCutForce(m_body,5.f,0.f); +} + +void CPHSimpleCharacter::ApplyForce(const Fvector& dir,float force) +{ + ApplyForce(dir.x*force,dir.y*force,dir.z*force); +} + +void CPHSimpleCharacter::PhDataUpdate(dReal /**step/**/){ + /////////////////// + + + SafeAndLimitVelocity(); + + if( !dBodyIsEnabled(m_body)) { + if(!ph_world->IsFreezed())b_lose_control=false; + return; + } + if(is_contact&&!is_control&&!b_lose_ground) + Disabling(); + + + /////////////////////// + if(ph_world->m_steps_num>m_ext_impuls_stop_step) + { + b_external_impulse = false ; + m_ext_impuls_stop_step = u64(-1) ; + m_ext_imulse .set(0,0,0) ; + Fvector vel ; + GetVelocity (vel) ; + dVectorLimit (cast_fp(vel),m_max_velocity,cast_fp(vel)); + SetVelocity(Fvector().set(0,0,0)) ; + } + was_contact = is_contact ; + was_control = is_control ; + b_was_side_contact = b_side_contact ; + is_contact = false ; + b_side_contact = false ; + b_any_contacts = false ; + b_valide_ground_contact = false ; + b_valide_wall_contact = false ; + + + b_was_on_object = b_on_object ; + b_on_object = false ; + b_death_pos = false ; + m_contact_count = 0 ; + m_friction_factor = 0.f ; + b_collision_restrictor_touch= false ; + b_foot_mtl_check = true ; + dMatrix3 R; + dRSetIdentity (R); + dBodySetAngularVel(m_body,0.f,0.f,0.f); + dBodySetRotation(m_body,R); + + dMass mass; + const float *linear_velocity =dBodyGetLinearVel(m_body); + dReal linear_velocity_mag =_sqrt(dDOT(linear_velocity,linear_velocity)); + dBodyGetMass(m_body,&mass); + dReal l_air=linear_velocity_mag*default_k_l;//force/velocity !!! + if(l_air>mass.mass/fixed_step) l_air=mass.mass/fixed_step;//validate + + if(!fis_zero(l_air)) + dBodyAddForce( + m_body, + -linear_velocity[0]*l_air, + -linear_velocity[1]*l_air, + -linear_velocity[2]*l_air + ); + m_last_move.sub(cast_fv(dBodyGetPosition(m_body)),m_last_move); + m_last_move.mul(1.f/fixed_step); + VERIFY2(dBodyStateValide(m_body),"WRONG BODYSTATE IN PhDataUpdate"); + if(PhOutOfBoundaries(cast_fv(dBodyGetPosition(m_body))))Disable(); + VERIFY_BOUNDARIES(cast_fv(dBodyGetPosition(m_body)),phBoundaries,PhysicsRefObject()); + m_body_interpolation.UpdatePositions(); +} + +void CPHSimpleCharacter::PhTune(dReal step){ + + m_last_move.set(cast_fv(dBodyGetPosition(m_body))); + m_elevator_state.PhTune(step); + b_air_contact_state=!is_contact; + + +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgCharacterControl)) + { + if(b_air_contact_state)DBG_DrawPoint(cast_fv(dBodyGetPosition(m_body)),m_radius,D3DCOLOR_XRGB(255,0,0)); + + } +#endif + bool b_good_graund=b_valide_ground_contact&&m_ground_contact_normal[1]>M_SQRT1_2; + + dxGeomUserData *ud=dGeomGetUserData(m_wheel); + if((ud->pushing_neg||ud->pushing_b_neg)&&!b_death_pos) + { + b_death_pos=true; +//#ifdef DEBUG +// Msg("death pos %f2.2,%f2.2,%f2.2",ud->last_pos[0],ud->last_pos[1],ud->last_pos[2]); +//#endif + Fvector pos;pos.set(cast_fv(dBodyGetPosition(m_body))); + Fvector d;d.set(cast_fv(dBodyGetLinearVel(m_body)));d.mul(fixed_step); + pos.sub(d); + if(!ud->pushing_b_neg) + { + //Fvector movement;movement.sub(cast_fv(dGeomGetPosition(m_wheel)),cast_fv(ud->last_pos)); + + dVectorSet(m_death_position,cast_fp(pos)); + } + else + { + dVectorSet(m_death_position,cast_fp(pos)); + } + } + + if(b_death_pos&&!(ud->pushing_neg||ud->pushing_b_neg)) + { + b_death_pos=false; + } + + CPHContactBodyEffector* contact_effector= + (CPHContactBodyEffector*) dBodyGetData(m_body); + if(contact_effector)contact_effector->Apply(); + + if(!dBodyIsEnabled(m_body)) + { + if(!ph_world->IsFreezed())b_lose_control=false; + return; + } + + if(m_acceleration.magnitude()>0.1f) is_control=true; + else is_control=false; + + b_depart=was_contact&&(!is_contact); + b_stop_control=was_control&&(!is_control); + b_meet=(!was_contact)&&(is_contact); + if(b_lose_control&&(is_contact||m_elevator_state.ClimbingState())) + b_meet_control=true; + b_on_ground=b_valide_ground_contact ||(b_meet&&(!b_depart)); + + + if(m_elevator_state.ClimbingState()) + { + b_side_contact=false; + m_friction_factor=1.f; + if(b_stop_control) + dBodySetLinearVel(m_body,0.f,0.f,0.f); + } + + //save depart position + if(b_depart) + dVectorSet(m_depart_position,dBodyGetPosition(m_body)); + + const dReal* velocity=dBodyGetLinearVel(m_body); + dReal linear_vel_smag=dDOT(velocity,velocity); + if(b_lose_control && + ( + b_on_ground && + m_ground_contact_normal[1]>M_SQRT1_2/2.f + //&& +// !b_external_impulse + /*&& + dSqrt(velocity[0]*velocity[0]+velocity[2]*velocity[2])<5.*/|| + fis_zero(linear_vel_smag) || + m_elevator_state.ClimbingState() + ) + ) + b_lose_control=false; + + + if(b_jumping&&b_good_graund ||(m_elevator_state.ClimbingState()&&b_valide_wall_contact)) //b_good_graund=b_valide_ground_contact&&m_ground_contact_normal[1]>M_SQRT1_2 + b_jumping=false; + + //deside if control lost + if(!b_on_ground && !m_elevator_state.ClimbingState()) + { + const dReal* current_pos=dBodyGetPosition(m_body); + dVector3 dif={current_pos[0]-m_depart_position[0], + current_pos[1]-m_depart_position[1], + current_pos[2]-m_depart_position[2]}; + if(dDOT(dif,dif)> + LOSE_CONTROL_DISTANCE* + LOSE_CONTROL_DISTANCE&&dFabs(dif[1])>0.1) + { + b_lose_control=true; + b_depart_control=true; + } + } + + ValidateWalkOn(); + + //jump + if(b_jump) + { + b_lose_control=true; + dBodySetLinearVel(m_body,m_jump_accel.x,m_jump_accel.y,m_jump_accel.z);//vel[1]+ + //Log("jmp",m_jump_accel); + dVectorSet(m_jump_depart_position,dBodyGetPosition(m_body)); + //m_jump_accel=m_acceleration; + b_jump=false; + b_jumping=true; + m_elevator_state.Depart(); + Enable(); + } + + b_lose_ground=!(b_good_graund||m_elevator_state.ClimbingState())||b_lose_control; + + + + ApplyAcceleration(); + + + + dReal* chVel=const_cast(dBodyGetLinearVel(m_body)); + //if(b_jump) + // dBodyAddForce(m_body,0.f,m_control_force[1],0.f);//+2.f*9.8f*70.f + //else + dMass m; + dBodyGetMass(m_body,&m); + if(is_control){ + dVector3 sidedir; + dVector3 y={0.,1.,0.}; + dCROSS(sidedir,=,m_control_force,y); + accurate_normalize(sidedir); + dReal vProj=dDOT(sidedir,chVel); + + dBodyAddForce(m_body,m_control_force[0],m_control_force[1],m_control_force[2]);//+2.f*9.8f*70.f + if(!b_lose_control||b_clamb_jump) + dBodyAddForce(m_body, + -sidedir[0]*vProj*(500.f+200.f*b_clamb_jump)*m_friction_factor, + -m.mass*(50.f)*(!b_lose_control&&!(is_contact||(b_any_contacts))),//&&!b_climb + -sidedir[2]*vProj*(500.f+200.f*b_clamb_jump)*m_friction_factor + ); +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgCharacterControl)) + { + DBG_DrawLine(cast_fv(dBodyGetPosition(m_body)),Fvector().add(cast_fv(dBodyGetPosition(m_body)),Fvector().mul(cast_fv(sidedir),1.f)),D3DCOLOR_XRGB(0,0,255)); + DBG_DrawLine(cast_fv(dBodyGetPosition(m_body)),Fvector().add(cast_fv(dBodyGetPosition(m_body)),Fvector().mul(cast_fv(m_control_force),1.f/19.6f)),D3DCOLOR_XRGB(0,0,255)); + DBG_DrawLine(cast_fv(dBodyGetPosition(m_body)),Fvector().add(cast_fv(dBodyGetPosition(m_body)),Fvector().mul(cast_fv(dBodyGetForce(m_body)),1.f/19.6f)),D3DCOLOR_XRGB(255,0,0)); + } +#endif + //if(b_clamb_jump){ + //accurate_normalize(m_control_force); + //dReal proj=dDOT(m_control_force,chVel); + //if(proj<0.f) + // dBodyAddForce(m_body,-chVel[0]*500.f,-chVel[1]*500.f,-chVel[2]*500.f); + // } + } + + + if(b_jumping) + { + + dReal proj=m_acceleration.x*chVel[0]+m_acceleration.z*chVel[2]; + + const dReal* current_pos=dBodyGetPosition(m_body); + dVector3 dif={current_pos[0]-m_jump_depart_position[0], + current_pos[1]-m_jump_depart_position[1], + current_pos[2]-m_jump_depart_position[2]}; + dReal amag =_sqrt(m_acceleration.x*m_acceleration.x+m_acceleration.z*m_acceleration.z); + if(amag>0.f) + if(dif[0]*m_acceleration.x/amag+dif[2]*m_acceleration.z/amag<0.3f) + { + dBodyAddForce(m_body,m_acceleration.x/amag*1000.f,0,m_acceleration.z/amag*1000.f); + } + if(proj<0.f){ + + dReal vmag=chVel[0]*chVel[0]+chVel[2]*chVel[2]; + dBodyAddForce(m_body,chVel[0]/vmag/amag*proj*3000.f,0,chVel[2]/vmag/amag*proj*3000.f); + } + } + //else + //dBodyAddForce(m_body,-chVel[0]*10.f,-20.f*70.f*(!is_contact),-chVel[2]*10.f); + + //if(b_depart&&!b_clamb_jump&&!b_jump&&!b_jumping&&is_control&&!b_external_impulse&&!m_elevator_state.Active()){ + // dBodyAddForce(m_body,0,-m.mass*ph_world->Gravity()/fixed_step,0); + //} + + BodyCutForce(m_body,5.f,0.f); +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgCharacterControl)) + { + DBG_DrawLine(cast_fv(dBodyGetPosition(m_body)),Fvector().add(cast_fv(dBodyGetPosition(m_body)),Fvector().mul(cast_fv(dBodyGetForce(m_body)),1.f/19.6f)),D3DCOLOR_XRGB(255,0,128)); + } +#endif +} + + + +const float CHWON_ACCLEL_SHIFT=0.4f; +const float CHWON_AABB_FACTOR =1.f; +const float CHWON_ANG_COS =M_SQRT1_2; +const float CHWON_CALL_UP_SHIFT=0.05f; +const float CHWON_CALL_FB_HIGHT=1.5f; +const float CHWON_AABB_FB_FACTOR =1.f; + +void CPHSimpleCharacter::ValidateWalkOn() +{ + if(b_on_object||b_was_on_object) + { + b_clamb_jump=ValidateWalkOnMesh(); + ValidateWalkOnObject(); + } + else b_clamb_jump=ValidateWalkOnMesh()&&!m_elevator_state.NearDown(); +} +bool CPHSimpleCharacter::ValidateWalkOnObject() +{ + //deside to stop clamb + if(b_clamb_jump){ + const dReal* current_pos=dBodyGetPosition(m_body); + dVector3 dif={current_pos[0]-m_clamb_depart_position[0], + current_pos[1]-m_clamb_depart_position[1], + current_pos[2]-m_clamb_depart_position[2]}; + if( //!b_valide_wall_contact|| + //(dDOT(dif,dif)>CLAMB_DISTANCE*CLAMB_DISTANCE)) // (m_wall_contact_normal[1]> M_SQRT1_2) || ] + dFabs(dif[1])>CLAMB_DISTANCE){ + b_clamb_jump=false; + // dBodySetLinearVel(m_body,0.f,0.f,0.f); + } + } + + //decide to clamb + if(!m_elevator_state.Active() &&b_valide_wall_contact && (m_contact_count>1)&&(m_wall_contact_normal[1]0.001f) + if(((m_wall_contact_position[0]-m_ground_contact_position[0])*m_control_force[0]+ + (m_wall_contact_position[2]-m_ground_contact_position[2])*m_control_force[2])>0.05f && + m_wall_contact_position[1]-m_ground_contact_position[1]>0.01f) + b_clamb_jump=true; + + + } + + if(b_valide_wall_contact && (m_contact_count>1)&& b_clamb_jump) + if( + dFabs((m_wall_contact_position[0]-m_ground_contact_position[0])+ //*m_control_force[0] + (m_wall_contact_position[2]-m_ground_contact_position[2]))>0.05f &&//0.01f//*m_control_force[2] + m_wall_contact_position[1]-m_ground_contact_position[1]>0.01f) + dVectorSet(m_clamb_depart_position,dBodyGetPosition(m_body)); + return b_clamb_jump; +} +bool CPHSimpleCharacter::ValidateWalkOnMesh() +{ + Fvector AABB,AABB_forbid,center,center_forbid,accel_add,accel; + + AABB.x=m_radius; + AABB.y=m_radius; + AABB.z=m_radius; + + AABB_forbid.set(AABB); + //AABB_forbid.x*=0.7f; + //AABB_forbid.z*=0.7f; + AABB_forbid.y+=m_radius; + AABB_forbid.mul(CHWON_AABB_FB_FACTOR); + + accel_add.set(m_acceleration); + float mag=accel_add.magnitude(); + if(!(mag>0.f)) return + true; + accel_add.mul(CHWON_ACCLEL_SHIFT/mag); + accel.set(accel_add); + accel.div(CHWON_ACCLEL_SHIFT); + AABB.mul(CHWON_AABB_FACTOR); + GetPosition(center); + center.add(accel_add); + center_forbid.set(center); + center_forbid.y+=CHWON_CALL_FB_HIGHT; + center.y+=m_radius+CHWON_CALL_UP_SHIFT; + + // perform single query / two usages + Fbox query,tmp ; + Fvector q_c, q_d ; + query.set (center_forbid,center_forbid); + query.grow (AABB_forbid ); + tmp.set (center,center ); + tmp.grow (AABB ); + query.merge (tmp); + query.get_CD (q_c,q_d); + + XRC.box_options (0); + XRC.box_query (Level().ObjectSpace.GetStaticModel(),q_c,q_d); + //Fvector fv_dir;fv_dir.mul(accel,1.f/mag); + Fvector sd_dir;sd_dir.set(-accel.z,0,accel.x); + Fvector obb_fb;obb_fb.set(m_radius*0.5f,m_radius*2.f,m_radius*0.7f); + Fvector obb;obb.set(m_radius*0.5f,m_radius,m_radius*0.7f); +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgCharacterControl)) + { + Fmatrix m;m.identity(); + m.i.set(sd_dir); + m.k.set(accel); + m.c.set(center); + DBG_DrawOBB(m,obb,D3DCOLOR_XRGB(0,255,0)); + m.c.set(center_forbid); + DBG_DrawOBB(m,obb_fb,D3DCOLOR_XRGB(255,0,0)); + } +#endif + + //if(XRC.r_end()!=XRC.r_begin()) return false; + CDB::RESULT* R_begin = XRC.r_begin(); + CDB::RESULT* R_end = XRC.r_end(); + for (CDB::RESULT* Res=R_begin; Res!=R_end; ++Res) + { + SGameMtl* m = GMLib.GetMaterialByIdx(Res->material); + if(m->Flags.test(SGameMtl::flPassable))continue; + //CDB::TRI* T = T_array + Res->id; + Point vertices[3]={Point((dReal*)&Res->verts[0]),Point((dReal*)&Res->verts[1]),Point((dReal*)&Res->verts[2])}; + if(__aabb_tri(Point((float*)¢er_forbid),Point((float*)&AABB_forbid),vertices)) + { + + + if( test_sides(center_forbid,sd_dir,accel,obb_fb,Res->id)) + { +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgCharacterControl)) + { + DBG_DrawTri(Res,D3DCOLOR_XRGB(255,0,0)); + } +#endif + b_side_contact=true; + return + false; + } + //cast_fv(side0).sub(Res->verts[1],Res->verts[0]); + //cast_fv(side1).sub(Res->verts[2],Res->verts[1]); + //dCROSS(norm,=,side0,side1);//optimize it !!! + //cast_fv(norm).normalize(); + + //if(dDOT(norm,(float*)&accel)<-CHWON_ANG_COS) + + } + } + + for (CDB::RESULT* Res=R_begin; Res!=R_end; ++Res) + { + //CDB::TRI* T = T_array + Res->id; + SGameMtl* m = GMLib.GetMaterialByIdx(Res->material); + if(m->Flags.test(SGameMtl::flPassable))continue; + Point vertices[3]={Point((dReal*)&Res->verts[0]),Point((dReal*)&Res->verts[1]),Point((dReal*)&Res->verts[2])}; + if(__aabb_tri(Point((float*)¢er),Point((float*)&AABB),vertices)){ + if(test_sides(center,sd_dir,accel,obb,Res->id)) + { +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgCharacterControl)) + { + DBG_DrawTri(Res,D3DCOLOR_XRGB(0,255,0)); + } +#endif + return true; + } + } + } + return + false; +} +void CPHSimpleCharacter::SetAcceleration(Fvector accel){ + if(!b_exist) return; + + if(!dBodyIsEnabled( m_body)) + if(!fsimilar(0.f,accel.magnitude())) + Enable(); + m_acceleration=accel; +} +void CPHSimpleCharacter::SetCamDir(const Fvector& cam_dir) +{ + m_cam_dir.set(cam_dir); +} +static const float pull_force=25.f; +void CPHSimpleCharacter::ApplyAcceleration() +{ + + dVectorSetZero(m_control_force); + //if(m_max_velocityM_SQRT1_2)){//M_SQRT1_2//0.8660f + dCROSS(fvdir,=,sidedir,m_ground_contact_normal); + accurate_normalize(fvdir); + dVectorAddMul(m_control_force,fvdir,m.mass*pull_force); + } + else{ + dVectorSet(fvdir,accel); + accurate_normalize(fvdir); + dVectorAddMul(m_control_force,fvdir,m.mass*pull_force*1.5f); + } + + } + if(!m_elevator_state.ClimbingState()&&b_clamb_jump){//&&m_wall_contact_normal[1]=0.f ? m_control_force[0] : -m_control_force[0]; + m_control_force[2]=m_control_force[2]*accel[2]>=0.f ? m_control_force[2] : -m_control_force[2]; + } + dVectorMul(m_control_force,m_friction_factor); +} + + +void CPHSimpleCharacter::IPosition(Fvector& pos) { + + if(!b_exist){ + pos.set(cast_fv(m_safe_position)); + } + else{ + m_body_interpolation.InterpolatePosition(pos); + pos.y-=m_radius; + } + VERIFY_BOUNDARIES(pos,phBoundaries,PhysicsRefObject()); + return; +} + +void CPHSimpleCharacter::SetPosition(Fvector pos){ + VERIFY_BOUNDARIES(pos,phBoundaries,PhysicsRefObject()); + if(!b_exist) return; + m_death_position[0]=pos.x; + m_death_position[1]=pos.y+m_radius; + m_death_position[2]=pos.z; + m_safe_position[0]=pos.x; + m_safe_position[1]=pos.y+m_radius; + m_safe_position[2]=pos.z; + b_death_pos=false; + + dGeomGetUserData(m_wheel)->pushing_b_neg=false; + dGeomGetUserData(m_hat)->pushing_b_neg=false; + dGeomGetUserData(m_geom_shell)->pushing_b_neg=false; + dGeomGetUserData(m_hat)->pushing_b_neg=false; + dGeomGetUserData(m_wheel)->pushing_neg=false; + dGeomGetUserData(m_hat)->pushing_neg=false; + dGeomGetUserData(m_geom_shell)->pushing_neg=false; + dGeomGetUserData(m_hat)->pushing_neg=false; + + dBodySetPosition(m_body,pos.x,pos.y+m_radius,pos.z); + CPHDisablingTranslational::Reinit(); + m_body_interpolation.ResetPositions(); + CPHObject::spatial_move(); +} + + + +void CPHSimpleCharacter::GetPosition(Fvector& vpos){ + if(!b_exist){ + vpos.set(m_safe_position[0],m_safe_position[1]-m_radius,m_safe_position[2]); + } + else{ + const dReal* pos=dBodyGetPosition(m_body); + dVectorSet((dReal*)&vpos,pos); + vpos.y-=m_radius; + } + + VERIFY_BOUNDARIES(vpos,phBoundaries,PhysicsRefObject()); +} +void CPHSimpleCharacter:: GetPreviousPosition (Fvector& pos) +{ + VERIFY(b_exist); + VERIFY(!ph_world->Processing()); + m_body_interpolation.GetPosition(pos,0); +} +void CPHSimpleCharacter::GetVelocity(Fvector& vvel){ + if(!b_exist){ + + vvel.set(m_safe_velocity[0],m_safe_velocity[1],m_safe_velocity[2]); + return ; + } + const dReal* vel=dBodyGetLinearVel(m_body); + dVectorSet((dReal*)&vvel,vel); + return; +} + + +void CPHSimpleCharacter::SetVelocity(Fvector vel){ + if(!b_exist) return; + float sq_mag=vel.square_magnitude(); + if(sq_mag>default_l_limit*default_l_limit) + { + float mag=_sqrt(sq_mag); + vel.mul(default_l_limit/mag); +#ifdef DEBUG + Msg("set velocity magnitude is too large %f",mag); +#endif + } + dBodySetLinearVel(m_body,vel.x,vel.y,vel.z); +} + + +void CPHSimpleCharacter::SetMas(dReal mass){ + m_mass=mass; + if(!b_exist) return; + dMass m; + //dMassSetBox(&m,1,1000000.f,1000000.f,1000000.f); + dMassSetSphere(&m,1,1000000.f/2.f); + dMassAdjust(&m,mass); + dBodySetMass(m_body,&m); +} +#ifdef DEBUG +void CPHSimpleCharacter::OnRender(){ + if(!b_exist) return; + Fmatrix m; + m.identity(); + Fvector n=*(Fvector*)m_ground_contact_normal; + n.mul(100.f); + Fvector pos; + GetPosition(pos); + pos.y+=m_radius; + Level().debug_renderer().draw_line(m,pos,*(Fvector*)m_control_force, color_rgba(256,0,0,1)); + Level().debug_renderer().draw_line(m,pos,n, 0xefffffff); + + + Fvector scale; + scale.set(0.35f,0.35f,0.35f); + Fmatrix M; + M.identity(); + M.scale(scale); + M.c.set(pos); + + + Level().debug_renderer().draw_ellipse(M, 0xffffffff); + +#ifdef DRAW_BOXES + Level().debug_renderer().draw_aabb (m_bcenter,m_AABB.x,m_AABB.y,m_AABB.z,D3DCOLOR_XRGB(0,0,255)); + Level().debug_renderer().draw_aabb (m_bcenter_forbid,m_AABB_forbid.x,m_AABB_forbid.y,m_AABB_forbid.z,D3DCOLOR_XRGB(255,0,0)); +#endif + ///M.c.set(0.f,1.f,0.f); + //Level().debug_renderer().draw_ellipse(M, 0xffffffff); +} +#endif + + +EEnvironment CPHSimpleCharacter::CheckInvironment(){ + //if(b_on_ground||(is_control&&!b_lose_control)) + if(b_lose_control) + return peInAir; + //else return peAtWall; + else if(m_elevator_state.ClimbingState()) + return peAtWall; + + return peOnGround; + +} + + + +void CPHSimpleCharacter::SetPhysicsRefObject (CPhysicsShellHolder* ref_object) +{ + m_phys_ref_object=ref_object; + if(b_exist) + { + dGeomUserDataSetPhysicsRefObject(m_geom_shell,ref_object); + dGeomUserDataSetPhysicsRefObject(m_wheel,ref_object); + dGeomUserDataSetPhysicsRefObject(m_hat,ref_object); + dGeomUserDataSetPhysicsRefObject(m_cap,m_phys_ref_object); + } +} + + + +void CPHSimpleCharacter::CaptureObject(dBodyID body,const dReal* anchor) +{ + m_capture_joint=dJointCreateBall(0,0); + Island().AddJoint(m_capture_joint); + dJointAttach(m_capture_joint,m_body,body); + dJointSetBallAnchor(m_capture_joint,anchor[0],anchor[1],anchor[2]); + dJointSetFeedback(m_capture_joint,&m_capture_joint_feedback); +} + +void CPHSimpleCharacter::CapturedSetPosition(const dReal* position) +{ + if(!m_capture_joint) return; + dJointSetBallAnchor(m_capture_joint,position[0],position[1],position[2]); +} + +void CPHSimpleCharacter::CheckCaptureJoint() +{ + //m_capture_joint_feedback +} + +void CPHSimpleCharacter::doCaptureExist(bool& do_exist) +{ + do_exist=!!m_capture_joint; +} + +void CPHSimpleCharacter::SafeAndLimitVelocity() +{ + + const float *linear_velocity =dBodyGetLinearVel(m_body); + if(dV_valid(linear_velocity)) + { + dReal mag=_sqrt(linear_velocity[0]*linear_velocity[0]+linear_velocity[1]*linear_velocity[1]+linear_velocity[2]*linear_velocity[2]);//; + //limit velocity + dReal l_limit; + if(is_control&&!b_lose_control) + l_limit = m_max_velocity/phTimefactor; + else + l_limit=default_l_limit; + + if(b_external_impulse) + { + float sq_mag=m_acceleration.square_magnitude(); + float ll_limit=m_ext_imulse.dotproduct(cast_fv(linear_velocity))*10.f/fixed_step; + if(sq_mag>EPS_L) + { + Fvector acc;acc.set(Fvector().mul(m_acceleration,1.f/_sqrt(sq_mag))); + Fvector vll;vll.mul(cast_fv(linear_velocity),1.f/mag); + float mxa=vll.dotproduct(acc); + if(mxa*ll_limit>l_limit&&!fis_zero(mxa)){ + ll_limit=l_limit/mxa; + } + + } + if(ll_limit>l_limit) + l_limit=ll_limit; + } +//////////////////////////////////////////////////////////////////////////////////////////// + m_mean_y=m_mean_y*0.9999f+linear_velocity[1]*0.0001f; + if(mag>l_limit) + { //CutVelocity(m_l_limit,m_w_limit); + if(!fis_zero(l_limit)) + { + + dReal f=mag/l_limit; + + if(b_lose_ground&&linear_velocity[1]<0.f&&linear_velocity[1]>-default_l_limit) + dBodySetLinearVel(m_body,linear_velocity[0]/f,linear_velocity[1],linear_velocity[2]/f);///f + else + CutVelocity(l_limit,0.f); + //dBodySetLinearVel(m_body,linear_velocity[0]/f,linear_velocity[1]/f,linear_velocity[2]/f);///f + if(is_control&&!b_lose_control) + { + dBodySetPosition(m_body, + m_safe_position[0]+linear_velocity[0]*fixed_step, + m_safe_position[1]+linear_velocity[1]*fixed_step, + m_safe_position[2]+linear_velocity[2]*fixed_step); + VERIFY_BOUNDARIES(cast_fv(dBodyGetPosition(m_body)),phBoundaries,PhysicsRefObject()) + } + }else dBodySetLinearVel(m_body,0,0,0); + + + } + } + else + { + dBodySetLinearVel(m_body,m_safe_velocity[0],m_safe_velocity[1],m_safe_velocity[2]); + } + + if(!dV_valid(dBodyGetPosition(m_body))) + dBodySetPosition(m_body,m_safe_position[0]-m_safe_velocity[0]*fixed_step, + m_safe_position[1]-m_safe_velocity[1]*fixed_step, + m_safe_position[2]-m_safe_velocity[2]*fixed_step); + + + dVectorSet(m_safe_position,dBodyGetPosition(m_body)); + dVectorSet(m_safe_velocity,linear_velocity); + +} + +void CPHSimpleCharacter::SetObjectContactCallback(ObjectContactCallbackFun* callback) +{ + m_object_contact_callback=callback; + if(!b_exist)return; + + dGeomUserDataSetObjectContactCallback(m_hat,callback); + dGeomUserDataSetObjectContactCallback(m_geom_shell,callback); + dGeomUserDataSetObjectContactCallback(m_wheel,callback); +} + +void CPHSimpleCharacter::AddObjectContactCallback(ObjectContactCallbackFun* callback) +{ + + VERIFY(b_exist); + + dGeomUserDataAddObjectContactCallback(m_hat,callback); + dGeomUserDataAddObjectContactCallback(m_geom_shell,callback); + dGeomUserDataAddObjectContactCallback(m_wheel,callback); +} +void CPHSimpleCharacter::RemoveObjectContactCallback(ObjectContactCallbackFun* callback) +{ + VERIFY(m_object_contact_callback!=callback); + if(!b_exist) return; + + dGeomUserDataRemoveObjectContactCallback(m_hat,callback); + dGeomUserDataRemoveObjectContactCallback(m_geom_shell,callback); + dGeomUserDataRemoveObjectContactCallback(m_wheel,callback); +} + +void CPHSimpleCharacter::Disable() +{ + //if(is_contact&&!is_control&&!b_lose_ground) + { + dGeomGetUserData(m_wheel)->pushing_neg =false; + dGeomGetUserData(m_wheel)->pushing_b_neg =false; + dGeomGetUserData(m_geom_shell)->pushing_neg =false; + dGeomGetUserData(m_geom_shell)->pushing_b_neg =false; + dGeomGetUserData(m_hat)->pushing_neg =false; + dGeomGetUserData(m_hat)->pushing_b_neg =false; + dGeomGetUserData(m_cap)->pushing_neg =false; + dGeomGetUserData(m_cap)->pushing_b_neg =false; + CPHCharacter::Disable(); + } +} +void CPHSimpleCharacter::Enable() +{ + if(!b_exist)return; + dGeomGetUserData(m_wheel)->pushing_neg =false; + dGeomGetUserData(m_wheel)->pushing_b_neg =false; + dGeomGetUserData(m_geom_shell)->pushing_neg =false; + dGeomGetUserData(m_geom_shell)->pushing_b_neg =false; + dGeomGetUserData(m_hat)->pushing_neg =false; + dGeomGetUserData(m_hat)->pushing_b_neg =false; + dGeomGetUserData(m_cap)->pushing_neg =false; + dGeomGetUserData(m_cap)->pushing_b_neg =false; + CPHCharacter::Enable(); +} +void CPHSimpleCharacter::EnableObject(CPHObject* obj) +{ + dGeomGetUserData(m_wheel)->pushing_neg =false; + dGeomGetUserData(m_wheel)->pushing_b_neg =false; + dGeomGetUserData(m_geom_shell)->pushing_neg =false; + dGeomGetUserData(m_geom_shell)->pushing_b_neg =false; + dGeomGetUserData(m_hat)->pushing_neg =false; + dGeomGetUserData(m_hat)->pushing_b_neg =false; + dGeomGetUserData(m_cap)->pushing_neg =false; + dGeomGetUserData(m_cap)->pushing_b_neg =false; + CPHCharacter::EnableObject(obj); +} +void CPHSimpleCharacter::SetWheelContactCallback (ObjectContactCallbackFun* callback) +{ + VERIFY(b_exist); + dGeomUserDataSetObjectContactCallback(m_wheel,callback); +} +void CPHSimpleCharacter::SetStaticContactCallBack(ContactCallbackFun* callback) +{ + if(!b_exist) return; + + dGeomUserDataSetContactCallback(m_hat,callback); + dGeomUserDataSetContactCallback(m_geom_shell,callback); + dGeomUserDataSetContactCallback(m_wheel,callback); +} + +ObjectContactCallbackFun* CPHSimpleCharacter::ObjectContactCallBack() +{ + return m_object_contact_callback; +} + +u16 CPHSimpleCharacter::RetriveContactBone() +{ + Fvector dir; + m_collision_damage_info.HitDir(dir); + collide::ray_defs Q(m_collision_damage_info.HitPos(), dir, m_radius, CDB::OPT_ONLYNEAREST|CDB::OPT_CULL,collide::rqtBoth); // CDB::OPT_ONLYFIRST CDB::OPT_ONLYNEAREST + RQR.r_clear (); + u16 contact_bone = 0; + CObject* object = smart_cast(m_phys_ref_object); + VERIFY (object) ; + VERIFY (!fis_zero(Q.dir.square_magnitude())); + if (g_pGameLevel->ObjectSpace.RayQuery(RQR,object->collidable.model,Q)) { + collide::rq_result* R = RQR.r_begin() ; + contact_bone=(u16)R->element ; + //int y=result.r_count(); + //for (int k=0; kelement,i)) + // { + // i->second.Use(); + // return false; + + // } + //} + + } + else + { + IKinematics* K=smart_cast(object->Visual()); + u16 count=K->LL_BoneCount(); + CBoneInstance* bone_instances=&K->LL_GetBoneInstance(0); + Fvector pos_in_object; + pos_in_object.sub(m_collision_damage_info.HitPos(),object->Position());//vector from object center to contact position currently in global frame + Fmatrix object_form; + object_form.set(object->XFORM()); + object_form.transpose(); + object_form.transform_dir(pos_in_object); //project pos_in_object on object axes now it is position of contact in object frame + float sq_dist=dInfinity; + for(u16 i=0;igeom.normal; + const dReal* pos=c->geom.pos; + const dGeomID g1=c->geom.g1; + const dGeomID g2=c->geom.g2; + bool bo1=(g1==m_wheel)||g1==m_cap_transform||g1==m_shell_transform||g1==m_hat_transform; + + //SGameMtl* tri_material=GMLib.GetMaterialByIdx((u16)c->surface.mode); + + u16 contact_material=bo1 ? material_idx_2:material_idx_1; + SGameMtl* tri_material=GMLib.GetMaterialByIdx(contact_material); + + bool bClimable=!!tri_material->Flags.test(SGameMtl::flClimable); + if(is_control&&m_elevator_state.ClimbingState()) + { + c->surface.mu=0.f; + c->surface.soft_cfm=world_cfm*2.f; + c->surface.soft_erp=world_erp; + b_any_contacts=true; + is_contact=true; + } + u16 foot_material_idx = ((dxGeomUserData*)dGeomGetData(m_wheel))->tri_material; + if(tri_material->Flags.test(SGameMtl::flPassable)&&!do_collide) + { + + UpdateStaticDamage(c,tri_material,bo1); + foot_material_update(contact_material,foot_material_idx); + return; + } + if(do_collide) + { + b_any_contacts=true; + is_contact=true; + } + dReal spring_rate=def_spring_rate; + dReal dumping_rate=def_dumping_rate; + bool object=(dGeomGetBody(g1)&&dGeomGetBody(g2)); + b_on_object=b_on_object||object; + + +////////////////////////нужно сместить колижен!! +////////////// + FootProcess(c,do_collide,bo1); + if(!do_collide) return; + if(g1==m_hat_transform||g2==m_hat_transform) + { + b_side_contact=true; + MulSprDmp(c->surface.soft_cfm,c->surface.soft_erp,spring_rate,dumping_rate); + c->surface.mu =0.00f; + } + + if(object){ + spring_rate*=10.f; + dBodyID b; + u16 obj_material_idx=u16(-1); + if(bo1) + { + b=dGeomGetBody(c->geom.g2); + obj_material_idx=material_idx_2; + } + else + { + b=dGeomGetBody(c->geom.g1); + obj_material_idx=material_idx_1; + } + UpdateDynamicDamage(c,obj_material_idx,b,bo1); + if(g1==m_wheel||g2==m_wheel) + { + dxGeomUserData *ud=bo1 ? retrieveGeomUserData(c->geom.g2) : retrieveGeomUserData(c->geom.g1); + foot_material_idx = ud->material; + } + contact_material=obj_material_idx; + } + + foot_material_update(contact_material,foot_material_idx); + + //if(!((g1==m_wheel) || (g2==m_wheel)||(m_elevator_state.ClimbingState()) ))// + // return; + + float friction=1.f; + if(!object && !b_side_contact) + { + friction=c->surface.mu; + } + if(m_friction_factorm_ground_contact_normal[1]||!b_valide_ground_contact)// + { + dVectorSet(m_ground_contact_normal,normal); + dVectorSet(m_ground_contact_position,pos); + b_valide_ground_contact=true; + } + if(dXZDot(normal,cast_fp(m_acceleration))geom.normal); + dVectorSet(m_wall_contact_position,c->geom.pos); + b_valide_wall_contact=true; + } + } + else{ + + if(normal[1]<-m_ground_contact_normal[1]||!b_valide_ground_contact)// + { + dVectorSetInvert(m_ground_contact_normal,normal); + dVectorSet(m_ground_contact_position,pos); + b_valide_ground_contact=true; + } + if(dXZDot(normal,cast_fp(m_acceleration)) > -dXZDot(m_wall_contact_normal,cast_fp(m_acceleration))||!b_valide_wall_contact)// + { + dVectorSetInvert(m_wall_contact_normal,normal); + dVectorSet(m_wall_contact_position,pos); + b_valide_wall_contact=true; + } + } + float soft_param=dumping_rate+normal[1]*(1.f-dumping_rate);//=(1.f-normal[1])*dumping_rate +normal[1] + if(is_control){//&&!b_lose_control||b_jumping + if(g1==m_wheel||g2==m_wheel&&!bClimable) + { + c->surface.mu = 0.f;//0.00f; + + } + else{ + c->surface.mu = 0.00f; + } + MulSprDmp(c->surface.soft_cfm,c->surface.soft_erp ,spring_rate,soft_param); + } + else + { + MulSprDmp(c->surface.soft_cfm,c->surface.soft_erp,spring_rate,soft_param); + c->surface.mu *= (1.f+b_clamb_jump*3.f)*m_friction_factor; + } + UpdateStaticDamage(c,tri_material,bo1); +} + +void CPHSimpleCharacter::FootProcess(dContact* c,bool &do_collide ,bool bo) +{ + + + const dGeomID g1 =c->geom.g1 ; + const dGeomID g2 =c->geom.g2 ; + dGeomID g =g1 ; + float sign =1.f ; + dReal* normal =c->geom.normal ; + if(!bo) {g=g2; sign=-1.f; } + if(m_elevator_state.ClimbingState() ||!is_control||!b_clamb_jump||b_was_side_contact||b_side_contact||b_jumping||b_jump) + { + if(g==m_wheel&&sign*normal[1]<0.f) do_collide=false; + return; + } + +/////////////////////////////////////////////////////////////////////////////////// + + dReal* pos =c->geom.pos ; + + + float c_pos =pos[1]-dBodyGetPosition(m_body)[1] ; + + +///////////////////////////////////////////////////////////////////////////////////// + + + if(dXZDot(m_acceleration,cast_fv(normal))*sign>0.f)return; + if(g==m_wheel) + { + if(sign*normal[1]<=0.f) + { + normal[0]=normal[2]=0.f;normal[1]=sign; + c->geom.depth=c_pos+m_radius; + } + } + + if(g==m_shell_transform) + { + if(c_pos<0.f) + { + normal[0]=normal[2]=0.f;normal[1]=sign; + c->geom.depth=c_pos+m_radius; + } + } +} +void CPHSimpleCharacter::GroundNormal(Fvector &norm) +{ + if(m_elevator_state.ClimbingState()) + { + m_elevator_state.GetLeaderNormal(norm); + + } + else + { + norm.set(*((Fvector*)m_ground_contact_normal)); + } +} +u16 CPHSimpleCharacter::ContactBone() +{ + return RetriveContactBone(); +} +void CPHSimpleCharacter::SetMaterial (u16 material) +{ + if(!b_exist) return; + dGeomGetUserData(m_geom_shell)->material=material; + dGeomGetUserData(m_wheel)->material =material; + dGeomGetUserData(m_cap)->material =material; + dGeomGetUserData(m_hat)->material =material; +} +void CPHSimpleCharacter::get_State(SPHNetState& state) +{ + CPHCharacter::get_State(state); + + state.previous_position.y-=m_radius; +} +void CPHSimpleCharacter::set_State(const SPHNetState& state) +{ + CPHCharacter::set_State (state); +} + +void CPHSimpleCharacter::get_spatial_params() +{ + spatialParsFromDGeom((dGeomID)m_space,spatial.sphere.P,AABB,spatial.sphere.R); +} + +float CPHSimpleCharacter::FootRadius() +{ + if(b_exist) return m_radius; + else return 0.f; + +} +void CPHSimpleCharacter::DeathPosition(Fvector& deathPos) +{ + if(!b_exist)return; + + if(b_death_pos) + { + deathPos.set(m_death_position); + deathPos.y=m_death_position[1]-m_radius; + } + else + { + deathPos.set(cast_fv(dBodyGetPosition(m_body))); + if(!_valid(deathPos))deathPos.set(m_safe_position); + } +} +void CPHSimpleCharacter:: AddControlVel (const Fvector& vel) +{ + m_acceleration.add(vel); + m_max_velocity+=vel.magnitude(); +} + +u16 CPHSimpleCharacter::DamageInitiatorID()const +{ + u16 ret=u16(-1);//m_collision_damage_info.DamageInitiatorID(); + + CPhysicsShellHolder* object =static_cast(Level().Objects.net_Find(m_collision_damage_info.m_obj_id)); + if(object&&!object->getDestroy()) + { + IDamageSource* ds=object->cast_IDamageSource(); + if(ds) ret= ds->Initiator(); + } + // return u16(-1); + + if(ret==u16(-1)) ret = m_phys_ref_object->ID(); + return ret; +} + +CObject* CPHSimpleCharacter::DamageInitiator() const +{ + VERIFY(m_phys_ref_object); + if(m_collision_damage_info.m_dmc_type==SCollisionDamageInfo::ctStatic) return (CObject*) (m_phys_ref_object); + u16 initiator_id=DamageInitiatorID(); + VERIFY(initiator_id!=u16(-1)); + if(initiator_id==m_phys_ref_object->ID()) + return static_cast (m_phys_ref_object); + else + { + return Level().Objects.net_Find(initiator_id); + } +} + +CPHSimpleCharacter::SCollisionDamageInfo::SCollisionDamageInfo() +{ + Construct(); +} +void CPHSimpleCharacter::SCollisionDamageInfo::Construct() +{ + m_contact_velocity=0.f; + SCollisionDamageInfo::Reinit(); + //m_damege_contact; + + //m_dmc_signum; + //m_dmc_type; + +} +float CPHSimpleCharacter::SCollisionDamageInfo::ContactVelocity()const +{ + dReal ret= m_contact_velocity; + m_contact_velocity=0; + return ret; +} + +void CPHSimpleCharacter::SCollisionDamageInfo::HitDir(Fvector& dir) const +{ + dir.set(m_damege_contact.geom.normal[0]*m_dmc_signum,m_damege_contact.geom.normal[1]*m_dmc_signum,m_damege_contact.geom.normal[2]*m_dmc_signum); +} + +//u16 CPHSimpleCharacter::SCollisionDamageInfo::DamageInitiatorID() const +//{ +// //if(!m_object) +// //return u16(-1); +// CPhysicsShellHolder* object =static_cast(Level().Objects.net_Find(m_obj_id)); +// if(!object)return u16(-1); +// IDamageSource* ds=m_object->cast_IDamageSource(); +// if(ds) return ds->Initiator(); +// return u16(-1); +//} +void CPHSimpleCharacter::SCollisionDamageInfo::Reinit() +{ + //m_damege_contact; + + m_obj_id =u16(-1); + m_hit_callback=NULL; + m_contact_velocity=0; + //float m_dmc_signum; + //enum{ctStatic,ctObject} m_dmc_type; +} +void CPHSimpleCharacter::GetSmothedVelocity(Fvector& vvel) +{ + if(!b_exist) {vvel.set(0,0,0);return;} + vvel.set(m_last_move); + + //if(IsEnabled()&&m_countFlags.test(SGameMtl::flInjurious)&&IsGameTypeSingle()) + return ALife::eHitTypeRadiation; + else +// return ALife::eHitTypeStrike; + return (GameID() == GAME_SINGLE) ? ALife::eHitTypeStrike : ALife::eHitTypePhysicStrike; +}// +CElevatorState* CPHSimpleCharacter::ElevatorState() +{ + return &m_elevator_state; +} + +SCollisionHitCallback* CPHSimpleCharacter::HitCallback ()const +{ + return m_collision_damage_info.m_hit_callback; +} +const float resolve_depth=0.05f; +static float restrictor_depth=0.f; +void CPHSimpleCharacter:: TestRestrictorContactCallbackFun (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2) +{ + + dGeomID g_this=NULL; + dGeomID g_obj=NULL; + if(bo1) + { + g_this=c.geom.g1; + g_obj=c.geom.g2; + } + else + { + g_this=c.geom.g2; + g_obj=c.geom.g1; + } + dxGeomUserData* obj_data=retrieveGeomUserData(g_obj); + if(!obj_data) return; + if(!obj_data->ph_object) return; + if(obj_data->ph_object->CastType()!=tpCharacter) return; + CPHActorCharacter *actor_character =(static_cast(obj_data->ph_object))->CastActorCharacter(); + if(!actor_character) return; + CPHSimpleCharacter *ch_this =static_cast(retrieveGeomUserData(g_this)->ph_object); + VERIFY(ch_this); + save_max(restrictor_depth,c.geom.depth); + do_colide=true; + c.surface.mu=0.f; +} + +bool CPHSimpleCharacter:: UpdateRestrictionType(CPHCharacter* ach) +{ + VERIFY(ph_world); + VERIFY(ph_world->Exist()); + if(m_restriction_type==m_new_restriction_type) return true; + ach->Enable(); + Enable(); + restrictor_depth=0.f; + //bool state=IsEnabled(); + + ph_world->Freeze(); + ERestrictionType old=m_restriction_type; + m_restriction_type=m_new_restriction_type; + AddObjectContactCallback(TestRestrictorContactCallbackFun); + UnFreeze(); + ph_world->StepTouch(); + ach->SwitchOFFInitContact(); + if(restrictor_depthUnFreeze(); + ach->SwitchInInitContact(); + //if(!state)Disable(); + return true; + } + u16 num_steps=2*(u16)iCeil(restrictor_depth/resolve_depth); + for(u16 i=0;num_steps>i;++i) + { + //Calculate(Fvector().set(0,0,0),Fvector().set(1,0,0),0,0,0,0); + restrictor_depth=0.f; + ach->Enable(); + Enable(); + //ach->ApplyForce(0,ph_world->Gravity()*ach->Mass(),0); + ph_world->Step(); + + if(restrictor_depthUnFreeze(); + ach->SwitchInInitContact(); + //if(!state)Disable(); + return true; + } + } + RemoveObjectContactCallback(TestRestrictorContactCallbackFun); + ach->SwitchInInitContact(); + ph_world->UnFreeze(); + //if(!state)Disable(); + m_new_restriction_type=old; +#ifdef DEBUG + if(ph_dbg_draw_mask1.test(ph_m1_DbgActorRestriction)) + Msg("restriction can not change change small -> large"); +#endif + return false; +} +bool CPHSimpleCharacter:: TouchRestrictor (ERestrictionType rttype) +{ + b_collision_restrictor_touch=true; + return rttype==RestrictionType(); +} diff --git a/src/xrGameLA/PHSimpleCharacter.h b/src/xrGameLA/PHSimpleCharacter.h new file mode 100644 index 000000000..a47255ee0 --- /dev/null +++ b/src/xrGameLA/PHSimpleCharacter.h @@ -0,0 +1,233 @@ +#pragma once +#include "PHCharacter.h" +#include "Physics.h" +#include "MathUtils.h" +#include "ElevatorState.h" +#include "IColisiondamageInfo.h" +#include "../GameMtlLib.h" +namespace ALife { + enum EHitType; +}; +#ifdef DEBUG +#include "PHDebug.h" +#endif + + +class CPHSimpleCharacter : + public CPHCharacter, + ICollisionDamageInfo +{ +private: + collide::rq_results RQR; + +protected: + CElevatorState m_elevator_state; + ////////////////////////////damage//////////////////////////////////////// + struct SCollisionDamageInfo + { + SCollisionDamageInfo () ; + void Construct () ; + float ContactVelocity () const ; + void HitDir (Fvector &dir) const ; + IC const Fvector& HitPos () const {return cast_fv(m_damege_contact.geom.pos);} + void Reinit () ; + dContact m_damege_contact; + SCollisionHitCallback *m_hit_callback; + u16 m_obj_id; + float m_dmc_signum; + enum{ctStatic,ctObject} m_dmc_type; + mutable float m_contact_velocity; + }; + SCollisionDamageInfo m_collision_damage_info; + /////////////////////////// callback + ObjectContactCallbackFun* m_object_contact_callback; + ////////////////////////// geometry + Fvector m_last_move; + dGeomID m_geom_shell; + dGeomID m_wheel; + dGeomID m_hat; + dGeomID m_cap; + + dGeomID m_hat_transform; + dGeomID m_wheel_transform; + dGeomID m_shell_transform; + dGeomID m_cap_transform; + + dSpaceID m_space; + + dReal m_radius; + dReal m_cyl_hight; + /////////////////////////////////// + dJointID m_capture_joint; + dJointFeedback m_capture_joint_feedback; + ////////////////////////// movement + dVector3 m_control_force; + Fvector m_acceleration; + Fvector m_cam_dir; + dVector3 m_wall_contact_normal; + dVector3 m_ground_contact_normal; + dVector3 m_clamb_depart_position; + dVector3 m_depart_position; + dVector3 m_wall_contact_position; + dVector3 m_ground_contact_position; + dReal jump_up_velocity;//=6.0f;//5.6f; + dReal m_collision_damage_factor; + dReal m_max_velocity; + + float m_air_control_factor; + + dVector3 m_jump_depart_position; + dVector3 m_death_position; + Fvector m_jump_accel; + + //movement state + bool is_contact ; + bool was_contact ; + bool b_depart ; + bool b_meet ; + bool b_side_contact ; + bool b_was_side_contact ; + bool b_any_contacts ; + bool b_air_contact_state ; + + bool b_valide_ground_contact ; + bool b_valide_wall_contact ; + bool b_on_object ; + bool b_was_on_object ; + bool b_on_ground ; + bool b_lose_ground ; + bool b_collision_restrictor_touch; + u32 m_contact_count ; + + bool is_control ; + bool b_meet_control ; + bool b_lose_control ; + bool was_control ; + bool b_stop_control ; + bool b_depart_control ; + bool b_jump ; + bool b_jumping ; + bool b_clamb_jump ; + bool b_external_impulse ; + u64 m_ext_impuls_stop_step ; + Fvector m_ext_imulse ; + bool b_death_pos ; + bool b_foot_mtl_check ; + dReal m_friction_factor; + +public: + CPHSimpleCharacter () ; + virtual ~CPHSimpleCharacter () {Destroy();} + + /////////////////CPHObject////////////////////////////////////////////// + virtual void PhDataUpdate (dReal step) ; + virtual void PhTune (dReal step) ; + virtual void InitContact (dContact* c,bool &do_collide,u16 /*material_idx_1*/,u16 /*material_idx_2*/) ; + virtual dSpaceID dSpace () {return m_space;} + virtual dGeomID dSpacedGeom () {return (dGeomID)m_space;} + virtual void get_spatial_params () ; + /////////////////CPHCharacter//////////////////////////////////////////// +public: + //update + + + //Check state + virtual bool ContactWas () {if(b_meet_control) {b_meet_control=false;return true;} else return false;} + virtual EEnvironment CheckInvironment () ; + virtual void GroundNormal (Fvector &norm) ; + virtual const ICollisionDamageInfo *CollisionDamageInfo ()const {return this;} +private: + virtual float ContactVelocity ()const {return m_collision_damage_info.ContactVelocity();} + virtual void HitDir (Fvector& dir)const {return m_collision_damage_info.HitDir(dir);} + virtual const Fvector& HitPos ()const {return m_collision_damage_info.HitPos();} + virtual u16 DamageInitiatorID ()const ; + virtual CObject *DamageInitiator ()const ; + virtual ALife::EHitType HitType ()const ; + virtual SCollisionHitCallback *HitCallback ()const ; + virtual void Reinit () {m_collision_damage_info.Reinit();}; +public: + //Creating + virtual void Create (dVector3 sizes) ; + virtual void Destroy (void) ; + virtual void Disable () ; + virtual void EnableObject (CPHObject* obj) ; + virtual void Enable () ; + virtual void SetBox (const dVector3 &sizes); + virtual bool UpdateRestrictionType (CPHCharacter* ach); + //get-set + virtual void SetObjectContactCallback (ObjectContactCallbackFun* callback); + virtual void SetWheelContactCallback (ObjectContactCallbackFun* callback); +private: + void RemoveObjectContactCallback (ObjectContactCallbackFun* callback); + void AddObjectContactCallback (ObjectContactCallbackFun* callback); +static void TestRestrictorContactCallbackFun (bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2); +public: + virtual ObjectContactCallbackFun* ObjectContactCallBack (); + void SetStaticContactCallBack (ContactCallbackFun* calback); + virtual void SwitchOFFInitContact () ; + virtual void SwitchInInitContact () ; + virtual void SetAcceleration (Fvector accel) ; + virtual Fvector GetAcceleration () { return m_acceleration; }; + virtual void SetCamDir (const Fvector& cam_dir); + virtual const Fvector& CamDir ()const {return m_cam_dir;} + virtual void SetMaterial (u16 material) ; + virtual void SetPosition (Fvector pos) ; + virtual void GetVelocity (Fvector& vvel) ; + virtual void GetSmothedVelocity (Fvector& vvel) ; + virtual void SetVelocity (Fvector vel) ; + virtual void SetAirControlFactor (float factor) {m_air_control_factor=factor;} + virtual void SetElevator (CClimableObject* climable){m_elevator_state.SetElevator(climable);}; + virtual CElevatorState *ElevatorState () ; + virtual void SetCollisionDamageFactor (float f) {m_collision_damage_factor=f;} + virtual void GetPosition (Fvector& vpos) ; + virtual void GetPreviousPosition (Fvector& pos) ; + virtual float FootRadius () ; + virtual void DeathPosition (Fvector& deathPos) ; + virtual void IPosition (Fvector& pos) ; + virtual u16 ContactBone (); + virtual void ApplyImpulse (const Fvector& dir, const dReal P); + virtual void ApplyForce (const Fvector& force); + virtual void ApplyForce (const Fvector& dir,float force); + virtual void ApplyForce (float x,float y, float z); + virtual void AddControlVel (const Fvector& vel); + virtual void SetMaximumVelocity (dReal vel) {m_max_velocity=vel;} + virtual dReal GetMaximumVelocity () { return m_max_velocity;} + virtual void SetJupmUpVelocity (dReal velocity) {jump_up_velocity=velocity;} + virtual bool JumpState () { + return b_jumping||b_jump; + }; + virtual const Fvector& ControlAccel ()const {return m_acceleration;} + virtual bool TouchRestrictor (ERestrictionType rttype); + virtual float &FrictionFactor () {return m_friction_factor;} + virtual void SetMas (dReal mass) ; + virtual float Mass () {return m_mass;}; + virtual void SetPhysicsRefObject (CPhysicsShellHolder* ref_object); + + virtual void CaptureObject (dBodyID body,const dReal* anchor); + virtual void CapturedSetPosition (const dReal* position); + virtual void doCaptureExist (bool& do_exist); + + virtual void get_State ( SPHNetState& state) ; + virtual void set_State (const SPHNetState& state) ; + virtual void ValidateWalkOn (); + bool ValidateWalkOnMesh () ; + bool ValidateWalkOnObject () ; +private: + void CheckCaptureJoint () ; + void ApplyAcceleration () ; + + u16 RetriveContactBone () ; + void SafeAndLimitVelocity () ; + void UpdateStaticDamage (dContact* c,SGameMtl* tri_material,bool bo1); + void UpdateDynamicDamage (dContact* c,u16 obj_material_idx,dBodyID b,bool bo1); +IC void FootProcess (dContact* c,bool &do_collide ,bool bo); +IC void foot_material_update (u16 tri_material,u16 foot_material_idx); + static void TestPathCallback(bool& do_colide,bool bo1,dContact& c,SGameMtl * /*material_1*/,SGameMtl * /*material_2*/); +public: +#ifdef DEBUG + virtual void OnRender () ; +#endif +}; + +const dReal def_spring_rate=0.5f; +const dReal def_dumping_rate=20.1f; \ No newline at end of file diff --git a/src/xrGameLA/PHSimpleCharacterInline.h b/src/xrGameLA/PHSimpleCharacterInline.h new file mode 100644 index 000000000..4f6a9ae80 --- /dev/null +++ b/src/xrGameLA/PHSimpleCharacterInline.h @@ -0,0 +1,146 @@ +void CPHSimpleCharacter::UpdateStaticDamage(dContact* c,SGameMtl* tri_material,bool bo1) +{ + const dReal *v = dBodyGetLinearVel(m_body); + dReal norm_prg = dFabs(dDOT(v,c->geom.normal)); + dReal smag = dDOT(v,v); + dReal plane_pgr = _sqrt(smag-norm_prg*norm_prg); + dReal mag = 0.f; + if(tri_material->Flags.test(SGameMtl::flPassable)) + { + mag = _sqrt(smag)*tri_material->fBounceDamageFactor; + } + else + { + float vel_prg;vel_prg=_max(plane_pgr*tri_material->fPHFriction,norm_prg); + mag = (vel_prg)*tri_material->fBounceDamageFactor; + } + if(mag>m_collision_damage_info.m_contact_velocity) + { + m_collision_damage_info.m_contact_velocity = mag; + m_collision_damage_info.m_dmc_signum = bo1 ? 1.f : -1.f; + m_collision_damage_info.m_dmc_type = SCollisionDamageInfo::ctStatic; + m_collision_damage_info.m_damege_contact = *c; + //m_collision_damage_info.m_object = 0; + m_collision_damage_info.m_obj_id = u16(-1); + } +} + +void CPHSimpleCharacter::UpdateDynamicDamage(dContact* c,u16 obj_material_idx,dBodyID b,bool bo1) +{ + + //if(ph_world ->IsFreezed()) + //return; + const dReal* vel=dBodyGetLinearVel(m_body); + dReal c_vel; + dMass m; + dBodyGetMass(b,&m); + + const dReal* obj_vel=dBodyGetLinearVel(b); + const dReal* norm=c->geom.normal; + dReal norm_vel=dDOT(vel,norm); + dReal norm_obj_vel=dDOT(obj_vel,norm); + + if((bo1&&norm_vel>norm_obj_vel)|| + (!bo1&&norm_obj_vel>norm_vel) + ) return ; + + + dVector3 Pc={vel[0]*m_mass+obj_vel[0]*m.mass,vel[1]*m_mass+obj_vel[1]*m.mass,vel[2]*m_mass+obj_vel[2]*m.mass}; + //dVectorMul(Vc,1.f/(m_mass+m.mass)); + //dVector3 vc_obj={obj_vel[0]-Vc[0],obj_vel[1]-Vc[1],obj_vel[2]-Vc[2]}; + //dVector3 vc_self={vel[0]-Vc[0],vel[1]-Vc[1],vel[2]-Vc[2]}; + //dReal vc_obj_norm=dDOT(vc_obj,norm); + //dReal vc_self_norm=dDOT(vc_self,norm); + + dReal Kself=norm_vel*norm_vel*m_mass/2.f; + dReal Kobj=norm_obj_vel*norm_obj_vel*m.mass/2.f; + + dReal Pcnorm=dDOT(Pc,norm); + dReal KK=Pcnorm*Pcnorm/(m_mass+m.mass)/2.f; + dReal accepted_energy=Kself*m_collision_damage_factor+Kobj*object_damage_factor-KK; + //DeltaK=m1*m2*(v1-v2)^2/(2*(m1+m2)) + if(accepted_energy>0.f) + { + SGameMtl *obj_material=GMLib.GetMaterialByIdx(obj_material_idx); + c_vel=dSqrt(accepted_energy/m_mass*2.f)*obj_material->fBounceDamageFactor; + } + else c_vel=0.f; +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDispObjCollisionDammage)&&c_vel>dbg_vel_collid_damage_to_display) + { + float dbg_my_norm_vell=norm_vel; + float dbg_obj_norm_vell=norm_obj_vel; + float dbg_my_kinetic_e=Kself; + float dbg_obj_kinetic_e=Kobj; + float dbg_my_effective_e=Kself*m_collision_damage_factor; + float dbg_obj_effective_e=Kobj*object_damage_factor; + float dbg_free_energy=KK; + LPCSTR name= PhysicsRefObject()->cName().c_str(); + + Msg("-----------------------------------------------------------------------------------------"); + Msg("cd %s -effective vell %f", name, c_vel); + Msg("cd %s -my_norm_vell %f", name, dbg_my_norm_vell); + Msg("cd %s -obj_norm_vell %f", name, dbg_obj_norm_vell); + Msg("cd %s -my_kinetic_e %f", name, dbg_my_kinetic_e); + Msg("cd %s -obj_kinetic_e %f", name, dbg_obj_kinetic_e); + Msg("cd %s -my_effective_e %f", name, dbg_my_effective_e); + Msg("cd %s -obj_effective_e %f", name, dbg_obj_effective_e); + Msg("cd %s -effective_acceted_e %f",name, accepted_energy); + Msg("cd %s -real_acceted_e %f", name, Kself+Kobj-KK); + Msg("cd %s -free_energy %f", name, dbg_free_energy); + Msg("-----------------------------------------------------------------------------------------"); + /* + static float dbg_my_norm_vell=0.f; + static float dbg_obj_norm_vell=0.f; + static float dbg_my_kinetic_e=0.f; + static float dbg_obj_kinetic_e=0.f; + static float dbg_my_effective_e=0.f; + static float dbg_obj_effective_e=0.f; + static float dbg_free_energy=0.f; + if() + dbg_my_norm_vell=norm_vel; + dbg_obj_norm_vell=norm_obj_vel; + dbg_my_kinetic_e=Kself; + dbg_obj_kinetic_e=Kobj; + dbg_my_effective_e=Kself*m_collision_damage_factor; + dbg_obj_effective_e=Kobj*object_damage_factor; + dbg_free_energy=KK; + DBG_OutText("-----dbg obj collision damage-------"); + DBG_OutText("my_norm_vell %f",dbg_my_norm_vell); + DBG_OutText("obj_norm_vell %f",dbg_obj_norm_vell); + DBG_OutText("my_kinetic_e %f",dbg_my_kinetic_e); + DBG_OutText("obj_kinetic_e %f", dbg_obj_kinetic_e); + DBG_OutText("my_effective_e %f",dbg_my_effective_e); + DBG_OutText("obj_effective_e %f",dbg_obj_effective_e); + DBG_OutText("free_energy %f",dbg_free_energy); + DBG_OutText("-----------------------------------"); + */ + } +#endif + if(c_vel>m_collision_damage_info.m_contact_velocity) + { + CPhysicsShellHolder* obj=bo1 ? retrieveRefObject(c->geom.g2) : retrieveRefObject(c->geom.g1); + VERIFY(obj); + if(!obj->getDestroy()) + { + m_collision_damage_info.m_contact_velocity=c_vel; + m_collision_damage_info.m_dmc_signum=bo1 ? 1.f : -1.f; + m_collision_damage_info.m_dmc_type=SCollisionDamageInfo::ctObject; + m_collision_damage_info.m_damege_contact=*c; + m_collision_damage_info.m_hit_callback=obj->get_collision_hit_callback(); + m_collision_damage_info.m_obj_id=obj->ID(); + } + } +} + + +IC void CPHSimpleCharacter::foot_material_update(u16 contact_material_idx,u16 foot_material_idx) +{ + if(*p_lastMaterialIDX!=u16(-1)&&GMLib.GetMaterialByIdx( *p_lastMaterialIDX)->Flags.test(SGameMtl:: flPassable)&&!b_foot_mtl_check) return ; + b_foot_mtl_check =false ; + if(GMLib.GetMaterialByIdx(contact_material_idx)->Flags.test(SGameMtl:: flPassable)) + *p_lastMaterialIDX=contact_material_idx ; + else + *p_lastMaterialIDX=foot_material_idx ; + +} \ No newline at end of file diff --git a/src/xrGameLA/PHSkeleton.cpp b/src/xrGameLA/PHSkeleton.cpp new file mode 100644 index 000000000..0e42f046e --- /dev/null +++ b/src/xrGameLA/PHSkeleton.cpp @@ -0,0 +1,442 @@ +#include "stdafx.h" +#include "PHSkeleton.h" +#include "PhysicsShellHolder.h" +#include "xrServer_Objects_ALife.h" +#include "Level.h" +#include "PHDefs.h" +#include "PhysicsShell.h" +#include "PHSynchronize.h" +#include "MathUtils.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHObject.h" +#include "PHCollideValidator.h" +#include "ai_object_location.h" +#include "ai_space.h" +#include "game_graph.h" +#include "PHDestroyable.h" +#define F_MAX 3.402823466e+38F + +u32 CPHSkeleton::existence_time=5000; + +bool IC CheckObjectSize(IKinematics* K) +{ + u16 bcount=K->LL_BoneCount(); + for(u16 i=0;iLL_GetBoneVisible(i)) + { + Fobb obb=K->LL_GetBox(i); + if(check_obb_sise(obb))return true; + } + } + return false; +} + +CPHSkeleton::CPHSkeleton() +{ + Init(); +} + +CPHSkeleton::~CPHSkeleton() +{ + ClearUnsplited(); +} + +void CPHSkeleton::RespawnInit() +{ + IKinematics* K = smart_cast(PPhysicsShellHolder()->Visual()); + if(K) + { + K->LL_SetBoneRoot(0); + K->LL_SetBonesVisible(0xffffffffffffffffL); + K->CalculateBones_Invalidate(); + K->CalculateBones(TRUE); + } + Init(); + ClearUnsplited(); +} + +void CPHSkeleton::Init() +{ + m_remove_time = u32(-1); + b_removing=false; + m_startup_anim=NULL; +} + +bool CPHSkeleton::Spawn(CSE_Abstract *D) +{ + + CSE_PHSkeleton *po = smart_cast(D); + VERIFY (po); + + m_flags = po->_flags; + CSE_Visual *visual = smart_cast(D); + VERIFY (visual); + m_startup_anim = visual->startup_animation; + + if(po->_flags.test(CSE_PHSkeleton::flSpawnCopy)) + { + CPHSkeleton* source=smart_cast(Level().Objects.net_Find(po->source_id)); + R_ASSERT2(source,"no source"); + source->UnsplitSingle(this); + m_flags.set (CSE_PHSkeleton::flSpawnCopy,FALSE); + po->_flags.set (CSE_PHSkeleton::flSpawnCopy,FALSE); + po->source_id =BI_NONE; + return true; + } + else + { + CPhysicsShellHolder *obj = PPhysicsShellHolder(); + IKinematics *K = NULL; + if (obj->Visual()) + { + K= smart_cast(obj->Visual()); + if(K) + { + K->LL_SetBoneRoot(po->saved_bones.root_bone); + K->LL_SetBonesVisible(po->saved_bones.bones_mask); + } + } + SpawnInitPhysics(D); + RestoreNetState(po); + if(obj->PPhysicsShell()&&obj->PPhysicsShell()->isFullActive()) + obj->PPhysicsShell()->GetGlobalTransformDynamic(&obj->XFORM()); + + CPHDestroyableNotificate::spawn_notificate(D); + + if(K) + { + CInifile* ini=K->LL_UserData(); + if(ini&&ini->section_exist("collide")) + { + if(ini->line_exist("collide","not_collide_parts")) + { + CGID gr= CPHCollideValidator::RegisterGroup(); + obj->PPhysicsShell()->RegisterToCLGroup(gr); + } + } + if(ini&&ini->section_exist("collide_parts")) + { + if(ini->line_exist("collide_parts","small_object")) + { + obj->PPhysicsShell()->SetSmall(); + } + if(ini->line_exist("collide_parts","ignore_small_objects")) + { + obj->PPhysicsShell()->SetIgnoreSmall(); + } + } + } + } + return false; +} + +void CPHSkeleton::Load(LPCSTR section) +{ + existence_time= pSettings->r_u32(section,"remove_time")*1000; +} + +void CPHSkeleton::Update(u32 dt) +{ + CPhysicsShellHolder* obj=PPhysicsShellHolder(); + CPhysicsShell* pPhysicsShell=obj->PPhysicsShell(); + if ( pPhysicsShell && pPhysicsShell->isFractured()) //!ai().get_alife() && + { + PHSplit(); + } + + if(b_removing&& + Device.dwTimeGlobal>m_remove_time&& + //(Device.dwTimeGlobal-m_unsplit_time)*phTimefactor>remove_time&& + m_unsplited_shels.empty()) + { + if (obj->Local()) obj->DestroyObject (); + b_removing=false; + } + +} +void CPHSkeleton::SaveNetState(NET_Packet& P) +{ + + CPhysicsShellHolder* obj=PPhysicsShellHolder(); + CPhysicsShell* pPhysicsShell=obj->PPhysicsShell(); + IKinematics* K =smart_cast(obj->Visual()); + if(pPhysicsShell&&pPhysicsShell->isActive()) m_flags.set(CSE_PHSkeleton::flActive,pPhysicsShell->isEnabled()); + + P.w_u8 (m_flags.get()); + if(K) + { + P.w_u64(K->LL_GetBonesVisible()); + P.w_u16(K->LL_GetBoneRoot()); + } + else + { + P.w_u64(u64(-1)); + P.w_u16(0); + } + ///////////////////////////// + Fvector min,max; + + min.set(F_MAX,F_MAX,F_MAX); + max.set(-F_MAX,-F_MAX,-F_MAX); + ///////////////////////////////////// + + u16 bones_number=obj->PHGetSyncItemsNumber(); + for(u16 i=0;iPHGetSyncItem(i)->get_State(state); + Fvector& p=state.position; + if(p.xmax.x)max.x=p.x; + if(p.y>max.y)max.y=p.y; + if(p.z>max.z)max.z=p.z; + } + + min.sub(2.f*EPS_L); + max.add(2.f*EPS_L); + + P.w_vec3(min); + P.w_vec3(max); + + P.w_u16(bones_number); + + for(u16 i=0;iPHGetSyncItem(i)->get_State(state); + state.net_Save(P,min,max); + } +} + +void CPHSkeleton::LoadNetState(NET_Packet& P) +{ + CPhysicsShellHolder* obj=PPhysicsShellHolder(); + IKinematics* K=smart_cast(obj->Visual()); + P.r_u8 (m_flags.flags); + if(K) + { + K->LL_SetBonesVisible(P.r_u64()); + K->LL_SetBoneRoot(P.r_u16()); + } + + u16 bones_number=P.r_u16(); + for(u16 i=0;iPHGetSyncItem(i)->set_State(state); + } +} +void CPHSkeleton::RestoreNetState(CSE_PHSkeleton* po) +{ + if(!po->_flags.test(CSE_PHSkeleton::flSavedData))return; + CPhysicsShellHolder* obj=PPhysicsShellHolder(); + PHNETSTATE_VECTOR& saved_bones=po->saved_bones.bones; + PHNETSTATE_I i=saved_bones.begin(),e=saved_bones.end(); + if(obj->PPhysicsShell()&&obj->PPhysicsShell()->isActive()) + { + obj->PPhysicsShell()->Disable(); + } + for(u16 bone=0;e!=i;i++,bone++) + { + R_ASSERT2(bonePHGetSyncItemsNumber(), make_string("bone>obj->PHGetSyncItemsNumber() - bone[%d], visual name[%s], id[%d]", bone, *obj->cNameVisual(), obj->ID())); + + obj->PHGetSyncItem(bone)->set_State(*i); + } + saved_bones.clear(); + po->_flags.set(CSE_PHSkeleton::flSavedData,FALSE); + m_flags.set(CSE_PHSkeleton::flSavedData,FALSE); +} + + +void CPHSkeleton::ClearUnsplited() +{ + SHELL_PAIR_I i=m_unsplited_shels.begin(),e=m_unsplited_shels.end(); + for(;i!=e;++i) + { + i->first->Deactivate(); + xr_delete(i->first); + } + m_unsplited_shels.clear(); +} + +void CPHSkeleton::SpawnCopy() +{ + if(PPhysicsShellHolder()->Local()) { + CSE_Abstract* D = F_entity_Create("ph_skeleton_object");//*cNameSect() + R_ASSERT (D); + ///////////////////////////////////////////////////////////////////////////////////////////// + CSE_ALifePHSkeletonObject *l_tpALifePhysicObject = smart_cast(D); + R_ASSERT (l_tpALifePhysicObject); + l_tpALifePhysicObject->_flags.set (CSE_PHSkeleton::flSpawnCopy,1); + ///////////////////////////////////////////////////////////////////////////////////////////// + InitServerObject (D); + // Send + NET_Packet P; + D->Spawn_Write (P,TRUE); + Level().Send (P,net_flags(TRUE)); + // Destroy + F_entity_Destroy (D); + } +} +PHSHELL_PAIR_VECTOR new_shells; +void CPHSkeleton::PHSplit() +{ + + + u16 spawned=u16(m_unsplited_shels.size()); + PPhysicsShellHolder()->PPhysicsShell()->SplitProcess(m_unsplited_shels); + u16 i=u16(m_unsplited_shels.size())-spawned; + // Msg("%o,spawned,%d",this,i); + for(;i;--i) SpawnCopy(); + + +} + + + + +void CPHSkeleton::UnsplitSingle(CPHSkeleton* SO) +{ + //Msg("%o,received has %d,",this,m_unsplited_shels.size()); + if (0==m_unsplited_shels.size()) return; //. hack + CPhysicsShellHolder* obj = PPhysicsShellHolder(); + CPhysicsShellHolder* O =SO->PPhysicsShellHolder(); + VERIFY2(m_unsplited_shels.size(),"NO_SHELLS !!"); + VERIFY2(!O->m_pPhysicsShell,"this has shell already!!!"); + CPhysicsShell* newPhysicsShell=m_unsplited_shels.front().first; + O->m_pPhysicsShell=newPhysicsShell; + VERIFY(_valid(newPhysicsShell->mXFORM)); + IKinematics *newKinematics=smart_cast(O->Visual()); + IKinematics *pKinematics =smart_cast(obj->Visual()); + + Flags64 mask0,mask1; + u16 split_bone=m_unsplited_shels.front().second; + mask1.assign(pKinematics->LL_GetBonesVisible());//source bones mask + pKinematics->LL_SetBoneVisible(split_bone,FALSE,TRUE); + + pKinematics->CalculateBones_Invalidate (); + pKinematics->CalculateBones (TRUE); + + mask0.assign(pKinematics->LL_GetBonesVisible());//first part mask + VERIFY2(mask0.flags,"mask0 -Zero"); + mask0.invert(); + mask1.and(mask0.flags);//second part mask + + + newKinematics->LL_SetBoneRoot (split_bone); + VERIFY2(mask1.flags,"mask1 -Zero"); + newKinematics->LL_SetBonesVisible (mask1.flags); + + newKinematics->CalculateBones_Invalidate (); + newKinematics->CalculateBones (TRUE); + + newPhysicsShell->set_Kinematics(newKinematics); + VERIFY(_valid(newPhysicsShell->mXFORM)); + newPhysicsShell->ResetCallbacks(split_bone,mask1); + VERIFY(_valid(newPhysicsShell->mXFORM)); + + newPhysicsShell->ObjectInRoot().identity(); + + if(!newPhysicsShell->isEnabled())O->processing_deactivate(); + newPhysicsShell->set_PhysicsRefObject(O); + + m_unsplited_shels.erase(m_unsplited_shels.begin()); + O->setVisible(TRUE); + O->setEnabled(TRUE); + + + SO->CopySpawnInit (); + CopySpawnInit (); + VERIFY3(CheckObjectSize(pKinematics),*(O->cNameVisual()),"Object unsplit whith no size"); + VERIFY3(CheckObjectSize(newKinematics),*(O->cNameVisual()),"Object unsplit whith no size"); + +} + +void CPHSkeleton::CopySpawnInit() +{ + if(ReadyForRemove()) + SetAutoRemove(); +} + + + + +void CPHSkeleton::SetAutoRemove(u32 time/*=CSE_PHSkeleton::existence_time*/) +{ + b_removing=true; + m_remove_time=Device.dwTimeGlobal+iFloor(time/phTimefactor); + SetNotNeedSave(); + PPhysicsShellHolder()->SheduleRegister(); +} + +static bool removable;//for RecursiveBonesCheck +void CPHSkeleton::RecursiveBonesCheck(u16 id) +{ + if(!removable) return; + CPhysicsShellHolder* obj=PPhysicsShellHolder(); + IKinematics* K = smart_cast(obj->Visual()); + CBoneData& BD = K->LL_GetData(u16(id)); + ////////////////////////////////////////// + Flags64 mask; + mask.assign(K->LL_GetBonesVisible()); + /////////////////////////////////////////// + if( + mask.is(1ui64<<(u64)id)&& + !(BD.shape.flags.is(SBoneShape::sfRemoveAfterBreak)) + ) { + removable = false; + return; + } + /////////////////////////////////////////////// + for (vecBonesIt it=BD.children.begin(); BD.children.end() != it; ++it){ + RecursiveBonesCheck ((*it)->GetSelfID()); + } +} +bool CPHSkeleton::ReadyForRemove() +{ + removable=true; + CPhysicsShellHolder* obj=PPhysicsShellHolder(); + RecursiveBonesCheck(smart_cast(obj->Visual())->LL_GetBoneRoot()); + return removable; +} +void CPHSkeleton::InitServerObject(CSE_Abstract * D) +{ + + CPhysicsShellHolder* obj=PPhysicsShellHolder(); + CSE_ALifeDynamicObject *l_tpALifeDynamicObject = smart_cast(D); + R_ASSERT (l_tpALifeDynamicObject); + CSE_ALifePHSkeletonObject *l_tpALifePhysicObject = smart_cast(D); + R_ASSERT (l_tpALifePhysicObject); + + l_tpALifePhysicObject->m_tGraphID =obj->ai_location().game_vertex_id(); + l_tpALifeDynamicObject->m_tNodeID = obj->ai_location().level_vertex_id(); + l_tpALifePhysicObject->set_visual (*obj->cNameVisual()); + + l_tpALifePhysicObject->source_id = u16(obj->ID()); + l_tpALifePhysicObject->startup_animation=m_startup_anim; + D->s_name = "ph_skeleton_object";//*cNameSect() + D->set_name_replace (""); + D->s_gameid = u8(GameID()); + D->s_RP = 0xff; + D->ID = 0xffff; + D->ID_Parent = 0xffff;//u16(ID());// + D->ID_Phantom = 0xffff; + D->o_Position = obj->Position(); + if (ai().get_alife()) + l_tpALifeDynamicObject->m_tGraphID = ai().game_graph().current_level_vertex(); + else + l_tpALifeDynamicObject->m_tGraphID = 0xffff; + obj->XFORM().getHPB (D->o_Angle); + D->s_flags.assign (M_SPAWN_OBJECT_LOCAL); + D->RespawnTime = 0; +} + +void CPHSkeleton::SetNotNeedSave () +{ + m_flags.set(CSE_PHSkeleton::flNotSave,TRUE); +} \ No newline at end of file diff --git a/src/xrGameLA/PHSkeleton.h b/src/xrGameLA/PHSkeleton.h new file mode 100644 index 000000000..b040aae65 --- /dev/null +++ b/src/xrGameLA/PHSkeleton.h @@ -0,0 +1,63 @@ +#ifndef PH_SKELETON_H +#define PH_SKELETON_H + +#include "PHDefs.h" +#include "PHDestroyableNotificate.h" +class CPhysicsShellHolder; +class CSE_ALifePHSkeletonObject; +class CSE_Abstract; +class CSE_PHSkeleton; +class NET_Packet; +class CPHSkeleton : + public CPHDestroyableNotificate +{ + bool b_removing; + static u32 existence_time; + u32 m_remove_time; + PHSHELL_PAIR_VECTOR m_unsplited_shels; + + shared_str m_startup_anim; + flags8 m_flags; + +private: + //Creating + + void Init () ; + + void ClearUnsplited () ; + //Splitting + + void PHSplit () ; + + void SpawnCopy () ; + //Autoremove + bool ReadyForRemove () ; + void RecursiveBonesCheck (u16 id) ; + +protected: + void LoadNetState (NET_Packet& P) ; + void UnsplitSingle (CPHSkeleton* SO) ; + +protected: + virtual CPhysicsShellHolder* PPhysicsShellHolder () =0; + virtual CPHSkeleton *PHSkeleton () {return this;} + virtual void SpawnInitPhysics (CSE_Abstract *D) =0; + virtual void SaveNetState (NET_Packet& P) ; + virtual void RestoreNetState (CSE_PHSkeleton* po) ; + + virtual void InitServerObject (CSE_Abstract *D) ;// + virtual void CopySpawnInit () ; + void RespawnInit () ;//net_Destroy + bool Spawn (CSE_Abstract *D) ;//net_spawn + void Update (u32 dt) ;//shedule update + void Load (LPCSTR section) ;//client load +public: + void SetAutoRemove (u32 time=existence_time) ; + void SetNotNeedSave () ; +IC bool IsRemoving (){return b_removing;} + u32 DefaultExitenceTime () {return existence_time;} + CPHSkeleton () ; + virtual ~CPHSkeleton () ; +}; + +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHSoundPlayer.cpp b/src/xrGameLA/PHSoundPlayer.cpp new file mode 100644 index 000000000..939c2bc82 --- /dev/null +++ b/src/xrGameLA/PHSoundPlayer.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" + +#include "PHSoundPlayer.h" +#include "PhysicsShellHolder.h" +CPHSoundPlayer::CPHSoundPlayer(CPhysicsShellHolder* obj) +{ + + m_object=obj; +} + +CPHSoundPlayer::~CPHSoundPlayer() +{ + m_sound.stop(); + m_object=NULL; +} + +void CPHSoundPlayer::Play(SGameMtlPair* mtl_pair,const Fvector& pos) +{ + + if(!m_sound._feedback()) + { + Fvector vel;m_object->PHGetLinearVell(vel); + if(vel.square_magnitude()>0.01f) + { + CLONE_MTL_SOUND(m_sound, mtl_pair, CollideSounds); + m_sound.play_at_pos(smart_cast(m_object),pos); + } + } +} + + diff --git a/src/xrGameLA/PHSoundPlayer.h b/src/xrGameLA/PHSoundPlayer.h new file mode 100644 index 000000000..4b2ebe1bc --- /dev/null +++ b/src/xrGameLA/PHSoundPlayer.h @@ -0,0 +1,17 @@ +#pragma once +#include "../GameMtlLib.h" + +class CPhysicsShellHolder; + +class CPHSoundPlayer +{ + ref_sound m_sound ; + CPhysicsShellHolder *m_object; +public: + void Play (SGameMtlPair* mtl_pair,const Fvector& pos) ; + CPHSoundPlayer (CPhysicsShellHolder *m_object) ; +virtual ~CPHSoundPlayer () ; + + +private: +}; \ No newline at end of file diff --git a/src/xrGameLA/PHSplitedShell.cpp b/src/xrGameLA/PHSplitedShell.cpp new file mode 100644 index 000000000..3d8740397 --- /dev/null +++ b/src/xrGameLA/PHSplitedShell.cpp @@ -0,0 +1,28 @@ +#include "stdafx.h" +#include "PhysicsShell.h" +#include "PHObject.h" +#include "PHWorld.h" +#include "PHInterpolation.h" +#include "PHShell.h" +#include "PHJoint.h" +#include "PHElement.h" +#include "PHSplitedShell.h" +#include "Physics.h" +#include "SpaceUtils.h" +void CPHSplitedShell::Collide() +{ + /////////////////////////////// + CollideStatic(dSpacedGeom(),CPHObject::SelfPointer()); + //near_callback(this,0,(dGeomID)dSpace(),ph_world->GetMeshGeom()); +} + +void CPHSplitedShell::get_spatial_params() +{ + spatialParsFromDGeom((dGeomID)m_space,spatial.sphere.P,AABB,spatial.sphere.R); + if(spatial.sphere.R>m_max_AABBradius) spatial.sphere.R=m_max_AABBradius; +} + +void CPHSplitedShell::DisableObject() +{ + CPHObject::deactivate(); +} diff --git a/src/xrGameLA/PHSplitedShell.h b/src/xrGameLA/PHSplitedShell.h new file mode 100644 index 000000000..71a8ddfa3 --- /dev/null +++ b/src/xrGameLA/PHSplitedShell.h @@ -0,0 +1,20 @@ +#ifndef PH_SPLITED_SELL +#define PH_SPLITED_SELL + +#include "PHShell.h" + +class CPHSplitedShell: + public CPHShell +{ + float m_max_AABBradius; + virtual void SetMaxAABBRadius (float size){m_max_AABBradius=size;} +protected: + virtual void Collide () ; + virtual void get_spatial_params () ; + virtual void DisableObject () ; +private: +public: + CPHSplitedShell (){m_max_AABBradius=dInfinity;} +}; + +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHStaticGeomShell.cpp b/src/xrGameLA/PHStaticGeomShell.cpp new file mode 100644 index 000000000..ef3895195 --- /dev/null +++ b/src/xrGameLA/PHStaticGeomShell.cpp @@ -0,0 +1,88 @@ +#include "stdafx.h" +#include "PHStaticGeomShell.h" +#include "SpaceUtils.h" +#include "GameObject.h" +#include "PhysicsShellHolder.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHCollideValidator.h" +void CPHStaticGeomShell::get_spatial_params() +{ + Fvector AABB; + spatialParsFromDGeom (dSpacedGeometry(),spatial.sphere.P,AABB,spatial.sphere.R); +} + +void CPHStaticGeomShell::PhDataUpdate (dReal step) +{ + Island().Step(step); + Island().Unmerge(); + PhysicsRefObject()->enable_notificate(); + CPHUpdateObject::Deactivate(); +} +void CPHStaticGeomShell::Activate(const Fmatrix& form) +{ + build(); + setStaticForm(form); + get_spatial_params(); + spatial_register(); +} + +void CPHStaticGeomShell::Deactivate() +{ + spatial_unregister(); + CPHUpdateObject::Deactivate(); + destroy(); +} + +CPHStaticGeomShell::CPHStaticGeomShell() +{ + spatial.type|=STYPE_PHYSIC; +} + +void __stdcall cb(CBoneInstance* B) +{ + +} + +void P_BuildStaticGeomShell(CPHStaticGeomShell* pUnbrokenObject,CGameObject* obj,ObjectContactCallbackFun* object_contact_callback,Fobb &b) +{ + pUnbrokenObject->add_Box (b); + pUnbrokenObject->Activate (obj->XFORM()); + + pUnbrokenObject->set_PhysicsRefObject(smart_cast(obj)); + //m_pUnbrokenObject->SetPhObjectInGeomData(m_pUnbrokenObject); + pUnbrokenObject->set_ObjectContactCallback(object_contact_callback); + CPHCollideValidator::SetNonDynamicObject(*pUnbrokenObject); +} +CPHStaticGeomShell* P_BuildStaticGeomShell(CGameObject* obj,ObjectContactCallbackFun* object_contact_callback,Fobb &b) +{ + CPHStaticGeomShell* pUnbrokenObject= new CPHStaticGeomShell(); + P_BuildStaticGeomShell(pUnbrokenObject,obj,object_contact_callback,b); + return pUnbrokenObject; +} + +CPHStaticGeomShell* P_BuildStaticGeomShell(CGameObject* obj,ObjectContactCallbackFun* object_contact_callback) +{ + Fobb b; + //IRenderVisual* V=obj->ObjectVisual(); + //R_ASSERT2(V,"need visual to build"); + IKinematics* K = smart_cast(obj->Visual()); + R_ASSERT2(K,"need visual to build"); + K->CalculateBones (TRUE); //. bForce - was TRUE + + //V->getVisData().box.getradius (b.m_halfsize); + K->GetBox().getradius (b.m_halfsize); + + b.xform_set (Fidentity); + CPHStaticGeomShell* pUnbrokenObject =P_BuildStaticGeomShell(obj,object_contact_callback,b); + + + //IKinematics* K=smart_cast(V); VERIFY(K); + K->CalculateBones(TRUE); + for (u16 k=0; kLL_BoneCount(); k++){ + K->LL_GetBoneInstance(k).set_callback( bctPhysics,cb,K->LL_GetBoneInstance(k).callback_param(), TRUE); + //K->LL_GetBoneInstance(k).Callback_overwrite = TRUE; + //K->LL_GetBoneInstance(k).Callback = cb; + } + return pUnbrokenObject; +} + diff --git a/src/xrGameLA/PHStaticGeomShell.h b/src/xrGameLA/PHStaticGeomShell.h new file mode 100644 index 000000000..7b3f3293e --- /dev/null +++ b/src/xrGameLA/PHStaticGeomShell.h @@ -0,0 +1,28 @@ +#ifndef PH_STATIC_GEOM_SHELL_H +#define PH_STATIC_GEOM_SHELL_H +#include "PHGeometryOwner.h" +#include "PHObject.h" + +class CPHStaticGeomShell: + public CPHGeometryOwner, + public CPHObject, + public CPHUpdateObject +{ + void get_spatial_params (); +virtual void EnableObject (CPHObject* obj){CPHUpdateObject::Activate();} +virtual dGeomID dSpacedGeom (){return dSpacedGeometry();} +virtual void PhDataUpdate (dReal step); +virtual void PhTune (dReal step){} +virtual void InitContact (dContact* c,bool& do_collide,u16 /*material_idx_1*/,u16 /*material_idx_2*/){} +virtual u16 get_elements_number () {return 0;}; +virtual CPHSynchronize *get_element_sync (u16 element) {return NULL;}; +public: + void Activate (const Fmatrix& form); + void Deactivate (); + CPHStaticGeomShell (); +}; + +CPHStaticGeomShell* P_BuildStaticGeomShell(CGameObject* obj,ObjectContactCallbackFun* object_contact_callback); +CPHStaticGeomShell* P_BuildStaticGeomShell(CGameObject* obj,ObjectContactCallbackFun* object_contact_callback,Fobb &b); +void P_BuildStaticGeomShell(CPHStaticGeomShell* shell,CGameObject* obj,ObjectContactCallbackFun* object_contact_callback,Fobb &b); +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHSynchronize.cpp b/src/xrGameLA/PHSynchronize.cpp new file mode 100644 index 000000000..bef76e58f --- /dev/null +++ b/src/xrGameLA/PHSynchronize.cpp @@ -0,0 +1,4 @@ +#include "stdafx.h" +#include "PHSynchronize.h" + + diff --git a/src/xrGameLA/PHSynchronize.h b/src/xrGameLA/PHSynchronize.h new file mode 100644 index 000000000..0105c30ec --- /dev/null +++ b/src/xrGameLA/PHSynchronize.h @@ -0,0 +1,23 @@ +#ifndef PH_SYNCHRONIZE_H +#define PH_SYNCHRONIZE_H + +#include "PHNetState.h" + +class NET_Packet; + +class CPHSynchronize +{ +public: + virtual void net_Export ( NET_Packet& P) {}; // export to server + virtual void net_Import ( NET_Packet& P) {}; + virtual void get_State ( SPHNetState& state) =0; + virtual void set_State (const SPHNetState& state) =0; + virtual void cv2obj_Xfrom (const Fquaternion& q,const Fvector& pos, Fmatrix& xform) =0; + virtual void cv2bone_Xfrom (const Fquaternion& q,const Fvector& pos, Fmatrix& xform) =0; +protected: +private: +}; + + + +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHValideValues.h b/src/xrGameLA/PHValideValues.h new file mode 100644 index 000000000..ed3575b43 --- /dev/null +++ b/src/xrGameLA/PHValideValues.h @@ -0,0 +1,134 @@ +#ifndef PH_VALIDE_VALUES +#define PH_VALIDE_VALUES + +class CSafeValue +{ + float m_safe_value ; +public: + CSafeValue (float val) + { + R_ASSERT(_valid(val));m_safe_value=val; + } + CSafeValue () + { + m_safe_value=0.f; + } + IC void new_val (float& val) + { + if(_valid(val))m_safe_value=val;else val=m_safe_value; + } + +}; + + +class CSafeVector3 +{ + CSafeValue m_safe_values [3] ; +public: + + IC void new_val (float *val) + { + m_safe_values[0].new_val(val[0]); + m_safe_values[1].new_val(val[1]); + m_safe_values[2].new_val(val[2]); + } +}; + +class CSafeVector4 +{ + CSafeValue m_safe_values [4] ; +public: + + IC void new_val (float *val) + { + m_safe_values[0].new_val(val[0]); + m_safe_values[1].new_val(val[1]); + m_safe_values[2].new_val(val[2]); + m_safe_values[3].new_val(val[3]); + } +}; + +class CSafeBodyLinearState +{ + CSafeVector3 m_safe_position ; + CSafeVector3 m_safe_linear_vel ; +public: + IC void create (dBodyID b) + { + R_ASSERT(dBodyStateValide(b)); + new_state(b); + } + IC void new_state (dBodyID b) + { + dVector3 v; + dVectorSet(v,dBodyGetPosition(b)); + m_safe_position.new_val(v); + dBodySetPosition(b,v[0],v[1],v[2]); + + dVectorSet(v,dBodyGetLinearVel(b)); + m_safe_linear_vel.new_val(v); + dBodySetLinearVel(b,v[0],v[1],v[2]); + + } +}; + +class CSafeFixedRotationState +{ + CSafeBodyLinearState m_safe_linear_state; +public: + IC void create (dBodyID b) + { + R_ASSERT(dBodyStateValide(b)); + new_state(b); + } + IC void new_state (dBodyID b) + { + dMatrix3 m;dRSetIdentity(m); + dBodySetRotation(b,m); + dBodySetAngularVel(b,0.f,0.f,0.f); + m_safe_linear_state.new_state(b); + } +}; +class CSafeBodyAngularState +{ + CSafeVector3 m_safe_angular_vel ; + CSafeVector4 m_safe_quaternion ; +public: + IC void create (dBodyID b) + { + R_ASSERT(dBodyStateValide(b)); + new_state(b); + } + IC void new_state (dBodyID b) + { + dVector3 v; + + dVectorSet(v,dBodyGetAngularVel(b)); + m_safe_angular_vel.new_val(v); + dBodySetAngularVel(b,v[0],v[1],v[2]); + + dQuaternion q; + dQuaternionSet(q,dBodyGetQuaternion(b)); + m_safe_quaternion.new_val(q); + dBodySetQuaternion(b,q); + } +}; + +class CSafeBodyState +{ + CSafeBodyLinearState m_safe_linear_state; + CSafeBodyAngularState m_safe_angular_state; +public: + IC void create (dBodyID b) + { + R_ASSERT(dBodyStateValide(b)); + new_state(b); + } + IC void new_state (dBodyID b) + { + m_safe_linear_state.new_state(b); + m_safe_angular_state.new_state(b); + } + +}; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHWorld.cpp b/src/xrGameLA/PHWorld.cpp new file mode 100644 index 000000000..faec3d1fa --- /dev/null +++ b/src/xrGameLA/PHWorld.cpp @@ -0,0 +1,511 @@ +#include "stdafx.h" + +#include "PHWorld.h" +#include "tri-colliderknoopc/dTriList.h" +#include "PhysicsCommon.h" +#include "Level.h" +#include "ExtendedGeom.h" +#include "draymotions.h" +#include "PHCollideValidator.h" +#include "../gamemtllib.h" +#ifdef DEBUG +# include "PHDebug.h" +#endif +#include "PHCommander.h" +#include "PHSimpleCalls.h" +#include "PHSynchronize.h" +#include "phnetstate.h" +////////////////////////////////////////////////////////////// +//////////////CPHMesh/////////////////////////////////////////// +/////////////////////////////////////////////////////////// +BOOL g_bDebugDumpPhysicsStep = 0; + +void CPHMesh ::Create(dSpaceID space, dWorldID world){ + Geom = dCreateTriList(space, 0, 0); + +} +///////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// + +void CPHMesh ::Destroy(){ + + dGeomDestroy(Geom); + dTriListClass=-1; + +} + + + +//////////////////////////////////////////////////////////////////////////// +///////////CPHWorld///////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +//#define PH_PLAIN +#ifdef PH_PLAIN +dGeomID plane; +#endif + +#ifdef DEBUG + +void CPHWorld::OnRender() +{ + + PH_DBG_Render(); +} +#endif +CPHWorld::CPHWorld() +{ + disable_count=0; + m_frame_time=0.f; + m_previous_frame_time=0.f; + b_frame_mark=false; + m_steps_num=0; + m_frame_sum=0.f; + m_delay=0; + m_previous_delay=0; + m_reduce_delay=0; + m_update_delay_count=0; + b_world_freezed=false; + b_processing=false; + m_gravity =default_world_gravity; + b_exist=false; +} +void CPHWorld::SetStep(dReal s) +{ + fixed_step = s; + world_cfm = CFM(SPRING_S(base_cfm,base_erp,base_fixed_step),DAMPING(base_cfm,base_erp)); + world_erp = ERP(SPRING_S(base_cfm,base_erp,base_fixed_step),DAMPING(base_cfm,base_erp)); + world_spring = 1.0f*SPRING (world_cfm,world_erp); + world_damping = 1.0f*DAMPING(world_cfm,world_erp); + if(ph_world&&ph_world->Exist()) + { + float frame_time = Device.fTimeDelta; + u32 it_number = iFloor (frame_time /fixed_step); + frame_time -= it_number*fixed_step; + ph_world->m_previous_frame_time = frame_time; + ph_world->m_frame_time = frame_time; + } +} +void CPHWorld::Create() +{ + dWorldID phWorld=0; + + if (psDeviceFlags.test(mtPhysics)) Device.seqFrameMT.Add (this,REG_PRIORITY_HIGH); + else Device.seqFrame.Add (this,REG_PRIORITY_LOW); + + m_commander =new CPHCommander(); + //dVector3 extensions={2048,256,2048}; + /* + Fbox level_box = Level().ObjectSpace.GetBoundingVolume(); + Fvector level_size,level_center; + level_box . getsize (level_size); + level_box . getcenter (level_center); + dVector3 extensions = { level_size.x ,256.f,level_size.z}; + dVector3 center = {level_center.x,0.f,level_center.z}; + */ + +#ifdef ODE_SLOW_SOLVER +#else + + dWorldSetAutoEnableDepthSF1(phWorld, 100000000); + ///dWorldSetContactSurfaceLayer(phWorld,0.f); + //phWorld->contactp.min_depth =0.f; + +#endif + ContactGroup = dJointGroupCreate(0); + dWorldSetGravity (phWorld, 0,-Gravity(), 0);//-2.f*9.81f + Mesh.Create (0,phWorld); +#ifdef PH_PLAIN + plane=dCreatePlane(Space,0,1,0,0.3f); +#endif + + //const dReal k_p=2400000.f;//550000.f;///1000000.f; + //const dReal k_d=200000.f; + dWorldSetERP(phWorld, ERP(world_spring,world_damping) ); + dWorldSetCFM(phWorld, CFM(world_spring,world_damping)); + //dWorldSetERP(phWorld, 0.2f); + //dWorldSetCFM(phWorld, 0.000001f); + disable_count=0; + m_motion_ray=dCreateRayMotions(0); + phBoundaries.set(Level().ObjectSpace.GetBoundingVolume()); + phBoundaries.y1-=30.f; + CPHCollideValidator::Init(); + b_exist=true; +} + +///////////////////////////////////////////////////////////////////////////// + +void CPHWorld::Destroy() +{ + r_spatial.clear(); + xr_delete(m_commander); + Mesh.Destroy(); +#ifdef PH_PLAIN + dGeomDestroy(plane); +#endif +#ifdef DEBUG + PH_DBG_Clear(); +#endif + dGeomDestroy(m_motion_ray); + dJointGroupEmpty(ContactGroup); + dJointGroupDestroy(ContactGroup); + ContactFeedBacks.clear(); + ContactEffectors.clear(); + dCloseODE(); + dCylinderClassUser=-1; + dRayMotionsClassUser=-1; + Device.seqFrameMT.Remove (this); + Device.seqFrame.Remove (this); + b_exist=false; +} +void CPHWorld::SetGravity(float g) +{ + m_gravity =g; + dWorldID phWorld =0; + dWorldSetGravity (phWorld, 0,-m_gravity, 0);//-2.f*9.81f + +} + +void CPHWorld::OnFrame() +{ + // Msg ("------------- physics: %d / %d",u32(Device.dwFrame),u32(m_steps_num)); + //просчитать полет пуль + /* + Device.Statistic->TEST0.Begin (); + Level().BulletManager().Update (); + Device.Statistic->TEST0.End (); + */ +#ifdef DEBUG + DBG_DrawFrameStart(); + DBG_DrawStatBeforeFrameStep(); +#endif + Device.Statistic->Physics.Begin (); + FrameStep (Device.fTimeDelta); + Device.Statistic->Physics.End (); +#ifdef DEBUG + DBG_DrawStatAfterFrameStep(); + +#endif +} + +////////////////////////////////////////////////////////////////////////////// +//static dReal frame_time=0.f; +static u32 start_time=0; +void CPHWorld::Step() +{ +#ifdef DEBUG + dbg_reused_queries_per_step =0 ; + dbg_new_queries_per_step =0 ; +#endif + + VERIFY(b_processing||IsFreezed()); + + PH_OBJECT_I i_object; + PH_UPDATE_OBJECT_I i_update_object; + + if(disable_count==0) + { + disable_count=worldDisablingParams.objects_params.L2frames; + for(i_object=m_recently_disabled_objects.begin();m_recently_disabled_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + obj->check_recently_deactivated(); + ++i_object; + } + } + --disable_count; + + ++m_steps_num; + Device.Statistic->ph_collision.Begin (); + + for(i_object=m_objects.begin();m_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + obj->Collide(); + + ++i_object; + } + Device.Statistic->ph_collision.End (); + +#ifdef DEBUG + for(i_object=m_objects.begin();m_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + DBG_DrawPHObject(obj); + ++i_object; + } +#endif + + for(i_object=m_objects.begin();m_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + ++i_object; + obj->PhTune(fixed_step); + } + + for(i_update_object=m_update_objects.begin();m_update_objects.end() != i_update_object;) + { CPHUpdateObject* obj=(*i_update_object); + ++i_update_object; + obj->PhTune(fixed_step); + } + + Device.Statistic->ph_core.Begin (); +#ifdef DEBUG + dbg_bodies_num=0; + dbg_joints_num=0; + dbg_islands_num=0; +#endif +////////////////////////////////////////////////////////////////////// + m_commander ->update(); +////////////////////////////////////////////////////////////////////// + for(i_object=m_objects.begin();m_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + ++i_object; +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawObjectStatistics)) + { + if(obj->Island().IsActive()) + { + dbg_islands_num++; + dbg_joints_num+=obj->Island().nj; + dbg_bodies_num+=obj->Island().nb; + } + } +#endif + + obj->IslandStep(fixed_step); + } + + Device.Statistic->ph_core.End (); + + + for(i_object=m_objects.begin();m_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + ++i_object; + obj->IslandReinit(); + obj->PhDataUpdate(fixed_step); + obj->spatial_move(); + } + + for(i_update_object=m_update_objects.begin();m_update_objects.end() != i_update_object;) + { CPHUpdateObject* obj=*i_update_object; + ++i_update_object; + obj->PhDataUpdate(fixed_step); + } +#ifdef DEBUG + dbg_contacts_num=ContactGroup->num; +#endif + dJointGroupEmpty(ContactGroup);//this is to be called after PhDataUpdate!!!-the order is critical!!! + ContactFeedBacks.empty(); + ContactEffectors.empty(); + + + + if(physics_step_time_callback) + { + physics_step_time_callback(start_time,start_time+u32(fixed_step*1000)); + start_time += u32(fixed_step*1000); + }; + + +} + +void CPHWorld::StepTouch() +{ + PH_OBJECT_I i_object; + for(i_object=m_objects.begin();m_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + obj->Collide(); + + ++i_object; + } + + for(i_object=m_objects.begin();m_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + ++i_object; + obj->Island().Enable(); + } + for(i_object=m_objects.begin();m_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + ++i_object; + obj->IslandReinit(); + obj->spatial_move(); + } + dJointGroupEmpty(ContactGroup); + ContactFeedBacks.empty(); + ContactEffectors.empty(); +} + +u32 CPHWorld::CalcNumSteps (u32 dTime) +{ + if (dTime < m_frame_time*1000) return 0; + u32 res = iCeil((float(dTime) - m_frame_time*1000) / (fixed_step*1000)); +// if (dTime < fixed_step*1000) return 0; +// u32 res = iFloor((float(dTime) / 1000 / fixed_step)+0.5f); + return res; +}; + +void CPHWorld::FrameStep(dReal step) +{ + if(IsFreezed()) return; + + VERIFY (_valid(step)) ; + step *= phTimefactor ; + // compute contact joints and forces + + //step+=astep; + + //const dReal k_p=24000000.f;//550000.f;///1000000.f; + //const dReal k_d=400000.f; + u32 it_number; + float frame_time=m_frame_time; + frame_time+=step; + //m_frame_sum+=step; +#ifdef DEBUG + if(ph_dbg_draw_mask.test(phDbgDrawObjectStatistics)) + { + static float dbg_iterations=0.f; + dbg_iterations=dbg_iterations*0.9f+step/fixed_step*0.1f; + b_processing=true; + DBG_OutText("phys steps per frame %2.1f",dbg_iterations); + b_processing=false; + } +#endif + if(!(frame_time20)Msg("!!!TOO MANY PHYSICS STEPS PER FRAME = %d !!!",it_number); + for(UINT i=0; i ::iterator i= m_objects.end(); + //return (--m_objects.end()); +}; +void CPHWorld::AddRecentlyDisabled(CPHObject* object) +{ + m_recently_disabled_objects.push_back(object); +} +void CPHWorld::RemoveFromRecentlyDisabled(PH_OBJECT_I i) +{ + m_recently_disabled_objects.erase(i); +} + +void CPHWorld::AddUpdateObject(CPHUpdateObject* object) +{ +//. if(object->IsFreezed())m_freezed_update_objects.erase(i); + m_update_objects.push_back(object); +} + +void CPHWorld::RemoveUpdateObject(PH_UPDATE_OBJECT_I i) +{ + m_update_objects.erase(i); +} + +void CPHWorld::RemoveObject(PH_OBJECT_I i){ + m_objects.erase((i)); +}; + +void CPHWorld::AddFreezedObject(CPHObject* obj) +{ + m_freezed_objects.push_back(obj); +} + +void CPHWorld::RemoveFreezedObject(PH_OBJECT_I i) +{ + m_freezed_objects.erase(i); +} +void CPHWorld::Freeze() +{ + R_ASSERT2(!b_world_freezed,"already freezed!!!"); + m_freezed_objects.move_items(m_objects); + PH_OBJECT_I iter=m_freezed_objects.begin(), + e= m_freezed_objects.end() ; + + for(; e != iter;++iter) + (*iter)->FreezeContent(); + m_freezed_update_objects.move_items(m_update_objects); + b_world_freezed=true; +} +void CPHWorld::UnFreeze() +{ + R_ASSERT2(b_world_freezed,"is not freezed!!!"); + PH_OBJECT_I iter=m_freezed_objects.begin(), + e= m_freezed_objects.end() ; + for(; e != iter;++iter) + (*iter)->UnFreezeContent(); + m_objects.move_items(m_freezed_objects); + m_update_objects.move_items(m_freezed_update_objects); + b_world_freezed=false; +} +bool CPHWorld::IsFreezed() +{ + return b_world_freezed; +} + +void CPHWorld::CutVelocity(float l_limit,float a_limit) +{ + PH_OBJECT_I i_object; + for(i_object=m_objects.begin();m_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + obj->CutVelocity(l_limit,a_limit); + ++i_object; + } +} +void CPHWorld::NetRelcase(CPhysicsShell *s) +{ + CPHReqComparerHasShell c(s); + m_commander->remove_calls(&c); +} +void CPHWorld::AddCall(CPHCondition*c,CPHAction*a) +{ + m_commander->add_call(c,a); +} +u16 CPHWorld::ObjectsNumber() +{ + return m_objects.count(); +} +u16 CPHWorld::UpdateObjectsNumber() +{ + return m_update_objects.count(); +} +void CPHWorld::GetState(V_PH_WORLD_STATE& state) +{ + state.clear(); + PH_OBJECT_I i_object; + for(i_object=m_objects.begin();m_objects.end() != i_object;) + { + CPHObject* obj=(*i_object); + const u16 els=obj->get_elements_number(); + for(u16 i=0;els>i;++i) + { + std::pair s; + s.first=obj->get_element_sync(i); + s.first->get_State(s.second); + state.push_back(s); + } + ++i_object; + } +} \ No newline at end of file diff --git a/src/xrGameLA/PHWorld.h b/src/xrGameLA/PHWorld.h new file mode 100644 index 000000000..3b831420d --- /dev/null +++ b/src/xrGameLA/PHWorld.h @@ -0,0 +1,107 @@ +#ifndef PH_WORLD_H +#define PH_WORLD_H +#include "Physics.h" + +// refs +struct SGameMtlPair; +class CPHCommander; +class CPHCondition; +class CPHAction; +struct SPHNetState; +class CPHSynchronize; +typedef xr_vector > V_PH_WORLD_STATE; +class CPHMesh { + dGeomID Geom; +public: + dGeomID GetGeom(){return Geom;} + void Create(dSpaceID space, dWorldID world); + void Destroy(); +}; + +#define PHWORLD_SOUND_CACHE_SIZE 8 + +//////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + +class CPHWorld : public pureFrame +#ifdef DEBUG +, public pureRender +#endif +{ + double m_start_time ; + u32 m_delay ; + u32 m_previous_delay ; + u32 m_reduce_delay ; + u32 m_update_delay_count ; + bool b_world_freezed ; + bool b_processing; + bool b_exist; + static const u32 update_delay=1 ; +/// dSpaceID Space ; + + CPHMesh Mesh ; + PH_OBJECT_STORAGE m_objects ; + PH_OBJECT_STORAGE m_freezed_objects ; + PH_OBJECT_STORAGE m_recently_disabled_objects ; + PH_UPDATE_OBJECT_STORAGE m_update_objects ; + PH_UPDATE_OBJECT_STORAGE m_freezed_update_objects ; + dGeomID m_motion_ray; + CPHCommander *m_commander; +public: + xr_vector r_spatial; +public: + u64 m_steps_num ; + double m_frame_sum ; + dReal m_previous_frame_time ; + bool b_frame_mark ; + dReal m_frame_time ; + float m_update_time ; + u16 disable_count ; + float m_gravity ; + CPHWorld () ; + virtual ~CPHWorld (){} ; + +//IC dSpaceID GetSpace () {return Space;} ; +IC bool Exist () {return b_exist ;} + void Create () ; + void SetGravity (float g) ; +IC float Gravity () {return m_gravity;} + void AddObject (CPHObject* object) ; + void AddUpdateObject (CPHUpdateObject* object) ; + void AddRecentlyDisabled (CPHObject* object) ; + void RemoveFromRecentlyDisabled (PH_OBJECT_I i) ; + void RemoveObject (PH_OBJECT_I i) ; + void RemoveUpdateObject (PH_UPDATE_OBJECT_I i) ; + dGeomID GetMeshGeom () {return Mesh.GetGeom();} +IC dGeomID GetMotionRayGeom () {return m_motion_ray;} + void static SetStep (dReal s) ; + void Destroy () ; +IC float FrameTime (bool frame_mark){return b_frame_mark==frame_mark ? m_frame_time :m_previous_frame_time;} + + void FrameStep (dReal step=0.025f) ; + void Step () ; + void StepTouch () ; + void CutVelocity (float l_limit, float a_limit); + void GetState (V_PH_WORLD_STATE& state) ; + void Freeze () ; + void UnFreeze () ; + void AddFreezedObject (CPHObject* obj) ; + void RemoveFreezedObject (PH_OBJECT_I i) ; + bool IsFreezed () ; +IC bool Processing () {return b_processing;} + u32 CalcNumSteps (u32 dTime) ; + u16 ObjectsNumber () ; + u16 UpdateObjectsNumber () ; + void NetRelcase (CPhysicsShell* s) ; + void AddCall (CPHCondition*c,CPHAction*a); +#ifdef DEBUG + virtual void OnRender () ; +#endif + virtual void _BCL OnFrame () ; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CPHWorld) +#undef script_type_list +#define script_type_list save_type_list(CPHWorld) +#endif \ No newline at end of file diff --git a/src/xrGameLA/PHWorldScript.cpp b/src/xrGameLA/PHWorldScript.cpp new file mode 100644 index 000000000..bdf1bfd7b --- /dev/null +++ b/src/xrGameLA/PHWorldScript.cpp @@ -0,0 +1,18 @@ +#include "pch_script.h" +#include "Physics.h" +#include "PHWorld.h" +#include "PHCommander.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CPHWorld::script_register(lua_State *L) +{ + module(L) + [ + class_("physics_world") + .def("set_gravity", &CPHWorld::SetGravity) + .def("gravity", &CPHWorld::Gravity) + .def("add_call", &CPHWorld::AddCall) + ]; +} diff --git a/src/xrGameLA/ParticlesObject.cpp b/src/xrGameLA/ParticlesObject.cpp new file mode 100644 index 000000000..c4ae180f2 --- /dev/null +++ b/src/xrGameLA/ParticlesObject.cpp @@ -0,0 +1,291 @@ +//---------------------------------------------------- +// file: PSObject.cpp +//---------------------------------------------------- +#include "stdafx.h" +#pragma hdrstop + +#include "ParticlesObject.h" +#include "../defines.h" +#include "../../Include/xrRender/RenderVisual.h" +#include "../../Include/xrRender/ParticleCustom.h" +#include "../render.h" +#include "../IGame_Persistent.h" +#include "GamePersistent.h" + +const Fvector zero_vel = {0.f,0.f,0.f}; + +CParticlesObject::CParticlesObject (LPCSTR p_name, BOOL bAutoRemove, bool destroy_on_game_load) : + inherited (destroy_on_game_load) +{ + Init (p_name,0,bAutoRemove); +} + +void CParticlesObject::Init (LPCSTR p_name, IRender_Sector* S, BOOL bAutoRemove) +{ + m_bLooped = false; + m_bStopping = false; + m_bAutoRemove = bAutoRemove; + float time_limit = 0.0f; + + if(!g_dedicated_server) + { + // create visual + renderable.visual = Render->model_CreateParticles(p_name); + VERIFY (renderable.visual); + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + time_limit = V->GetTimeLimit(); + }else + { + time_limit = 1.0f; + } + + if(time_limit > 0.f) + { + m_iLifeTime = iFloor(time_limit*1000.f); + } + else + { + if(bAutoRemove) + { + R_ASSERT3 (!m_bAutoRemove,"Can't set auto-remove flag for looped particle system.",p_name); + } + else + { + m_iLifeTime = 0; + m_bLooped = true; + } + } + + + // spatial + spatial.type = 0; + spatial.sector = S; + + // sheduled + shedule.t_min = 20; + shedule.t_max = 50; + shedule_register (); + + dwLastTime = Device.dwTimeGlobal; + mt_dt = 0; +} + +//---------------------------------------------------- +CParticlesObject::~CParticlesObject() +{ + VERIFY (0==mt_dt); + +// we do not need this since CPS_Instance does it +// shedule_unregister (); +} + +void CParticlesObject::UpdateSpatial() +{ + if(g_dedicated_server) return; + + // spatial (+ workaround occasional bug inside particle-system) + vis_data &vis = renderable.visual->getVisData(); + if (_valid(vis.sphere)) + { + Fvector P; float R; + renderable.xform.transform_tiny (P,vis.sphere.P); + R = vis.sphere.R; + if (0==spatial.type) { + // First 'valid' update - register + spatial.type = STYPE_RENDERABLE; + spatial.sphere.set (P,R); + spatial_register (); + } else { + BOOL bMove = FALSE; + if (!P.similar(spatial.sphere.P,EPS_L*10.f)) bMove = TRUE; + if (!fsimilar(R,spatial.sphere.R,0.15f)) bMove = TRUE; + if (bMove) { + spatial.sphere.set (P, R); + spatial_move (); + } + } + } +} + +const shared_str CParticlesObject::Name() +{ + if(g_dedicated_server) return ""; + + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + return (V) ? V->Name() : ""; +} + +//---------------------------------------------------- +void CParticlesObject::Play (bool bHudMode) +{ + if(g_dedicated_server) return; + if (m_bCamReady == false) return; + + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + if (bHudMode) + V->SetHudMode(bHudMode); + + V->Play (); + dwLastTime = Device.dwTimeGlobal-33ul; + mt_dt = 0; + PerformAllTheWork (0); + m_bStopping = false; +} + +void CParticlesObject::PlayStatic(bool bHudMode) +{ + if (g_dedicated_server) return; + + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + if (bHudMode) + V->SetHudMode(bHudMode); + + V->Play(); + dwLastTime = Device.dwTimeGlobal - 33ul; + mt_dt = 0; + PerformAllTheWork(0); + m_bStopping = false; +} + +void CParticlesObject::play_at_pos(const Fvector& pos, BOOL xform) +{ + if(g_dedicated_server) return; + if (m_bCamReady == false) return; + + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + Fmatrix m; m.translate (pos); + V->UpdateParent (m,zero_vel,xform); + V->Play (); + dwLastTime = Device.dwTimeGlobal-33ul; + mt_dt = 0; + PerformAllTheWork (0); + m_bStopping = false; +} + +void CParticlesObject::Stop (BOOL bDefferedStop) +{ + if(g_dedicated_server) return; + + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + V->Stop (bDefferedStop); + m_bStopping = true; +} + +void CParticlesObject::shedule_Update (u32 _dt) +{ + inherited::shedule_Update (_dt); + + if(g_dedicated_server) return; + + // Update + if (m_bDead) return; + u32 dt = Device.dwTimeGlobal - dwLastTime; + if (dt) { + if (0){//.psDeviceFlags.test(mtParticles)) { //. AlexMX comment this line// NO UNCOMMENT - DON'T WORK PROPERLY + mt_dt = dt; + fastdelegate::FastDelegate0<> delegate (this,&CParticlesObject::PerformAllTheWork_mt); + Device.seqParallel.push_back (delegate); + } else { + mt_dt = 0; + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + V->OnFrame (dt); + } + dwLastTime = Device.dwTimeGlobal; + } + UpdateSpatial (); +} + +void CParticlesObject::PerformAllTheWork(u32 _dt) +{ + if(g_dedicated_server) return; + + // Update + u32 dt = Device.dwTimeGlobal - dwLastTime; + if (dt) { + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + V->OnFrame (dt); + dwLastTime = Device.dwTimeGlobal; + } + UpdateSpatial (); +} + +void CParticlesObject::PerformAllTheWork_mt() +{ + if(g_dedicated_server) return; + + if (0==mt_dt) return; //??? + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + V->OnFrame (mt_dt); + mt_dt = 0; +} + +void CParticlesObject::SetXFORM (const Fmatrix& m) +{ + if(g_dedicated_server) return; + + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + V->UpdateParent (m,zero_vel,TRUE); + renderable.xform.set(m); + UpdateSpatial (); +} + +void CParticlesObject::UpdateParent (const Fmatrix& m, const Fvector& vel) +{ + if(g_dedicated_server) return; + + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + V->UpdateParent (m,vel,FALSE); + UpdateSpatial (); +} + +Fvector& CParticlesObject::Position () +{ + if(g_dedicated_server) + { + static Fvector _pos = Fvector().set(0,0,0); + return _pos; + } + vis_data &vis = renderable.visual->getVisData(); + return vis.sphere.P; +} + +float CParticlesObject::shedule_Scale () +{ + if(g_dedicated_server) return 5.0f; + + return Device.vCameraPosition.distance_to(Position())/200.f; +} + +void CParticlesObject::renderable_Render () +{ + VERIFY (renderable.visual); + u32 dt = Device.dwTimeGlobal - dwLastTime; + if (dt){ + IParticleCustom* V = smart_cast(renderable.visual); VERIFY(V); + V->OnFrame (dt); + dwLastTime = Device.dwTimeGlobal; + } + ::Render->set_Transform (&renderable.xform); + ::Render->add_Visual (renderable.visual); +} +bool CParticlesObject::IsAutoRemove () +{ + if(m_bAutoRemove) return true; + else return false; +} +void CParticlesObject::SetAutoRemove (bool auto_remove) +{ + VERIFY(m_bStopping || !IsLooped()); + m_bAutoRemove = auto_remove; +} + +//играются ли партиклы, отличается от PSI_Alive, тем что после +//остановки Stop партиклы могут еще доигрывать анимацию IsPlaying = true +bool CParticlesObject::IsPlaying() +{ + if(g_dedicated_server) return false; + + IParticleCustom* V = smart_cast(renderable.visual); + VERIFY(V); + return !!V->IsPlaying(); +} diff --git a/src/xrGameLA/ParticlesObject.h b/src/xrGameLA/ParticlesObject.h new file mode 100644 index 000000000..ec9132433 --- /dev/null +++ b/src/xrGameLA/ParticlesObject.h @@ -0,0 +1,67 @@ +#ifndef ParticlesObjectH +#define ParticlesObjectH + +#include "../PS_instance.h" + +extern const Fvector zero_vel; + +class CParticlesObject : public CPS_Instance +{ + typedef CPS_Instance inherited; + + u32 dwLastTime; + void Init (LPCSTR p_name, IRender_Sector* S, BOOL bAutoRemove); + void UpdateSpatial (); + +protected: + bool m_bLooped; //флаг, что система зациклена + bool m_bStopping; //вызвана функция Stop() + +protected: + u32 mt_dt; + +protected: + virtual ~CParticlesObject (); + +public: + CParticlesObject (LPCSTR p_name, BOOL bAutoRemove, bool destroy_on_game_load); + + virtual bool shedule_Needed () {return true;}; + virtual float shedule_Scale () ; + virtual void shedule_Update (u32 dt); + virtual void renderable_Render (); + void PerformAllTheWork (u32 dt); + void __stdcall PerformAllTheWork_mt(); + + Fvector& Position (); + void SetXFORM (const Fmatrix& m); + IC Fmatrix& XFORM () {return renderable.xform;} + void UpdateParent (const Fmatrix& m, const Fvector& vel); + + void play_at_pos (const Fvector& pos, BOOL xform=FALSE); + virtual void Play (bool bHudMode); + virtual void PlayStatic (bool bHudMode); + void Stop (BOOL bDefferedStop=TRUE); + virtual BOOL Locked () { return mt_dt; } + + bool IsLooped () {return m_bLooped;} + bool IsAutoRemove (); + bool IsPlaying (); + void SetAutoRemove (bool auto_remove); + + const shared_str Name (); +public: + static CParticlesObject* Create (LPCSTR p_name, BOOL bAutoRemove=TRUE, bool remove_on_game_load = true) + { + return new CParticlesObject(p_name, bAutoRemove, remove_on_game_load); + } + static void Destroy (CParticlesObject*& p) + { + if (p){ + p->PSI_destroy (); + p = 0; + } + } +}; + +#endif /*ParticlesObjectH*/ diff --git a/src/xrGameLA/ParticlesPlayer.cpp b/src/xrGameLA/ParticlesPlayer.cpp new file mode 100644 index 000000000..a56b82531 --- /dev/null +++ b/src/xrGameLA/ParticlesPlayer.cpp @@ -0,0 +1,323 @@ +/////////////////////////////////////////////////////////////// +// ParticlesPlayer.cpp +// интерфейс для проигрывания партиклов на объекте +/////////////////////////////////////////////////////////////// +#include "stdafx.h" +#include "ParticlesPlayer.h" +#include "../xr_object.h" +#include "../Include/xrRender/Kinematics.h" +//------------------------------------------------------------------------------------- +static void generate_orthonormal_basis(const Fvector& dir,Fmatrix &result) +{ + result.identity (); + result.k.normalize (dir); + Fvector::generate_orthonormal_basis(result.k, result.j, result.i); +} +CParticlesPlayer::SParticlesInfo* CParticlesPlayer::SBoneInfo::FindParticles(const shared_str& ps_name) +{ + for (ParticlesInfoListIt it=particles.begin(); it!=particles.end(); it++) + if (it->ps && it->ps->Name()==ps_name) return &(*it); + return 0; +} +CParticlesPlayer::SParticlesInfo* CParticlesPlayer::SBoneInfo::AppendParticles(CObject* object, const shared_str& ps_name) +{ + SParticlesInfo* pi = FindParticles(ps_name); + if (pi) return pi; + particles.push_back (SParticlesInfo()); + pi = &particles.back(); + pi->ps = CParticlesObject::Create(*ps_name,FALSE); + return pi; +} +void CParticlesPlayer::SBoneInfo::StopParticles(const shared_str& ps_name, bool bDestroy) +{ + SParticlesInfo* pi = FindParticles(ps_name); + if (pi){ + if(!bDestroy) + pi->ps->Stop(); + else + CParticlesObject::Destroy(pi->ps); + } +} + +void CParticlesPlayer::SBoneInfo::StopParticles(u16 sender_id, bool bDestroy) +{ + if (!particles.size()) + return; + for (ParticlesInfoListIt it=particles.begin(); it!=particles.end(); it++) + { + if (it->sender_id==sender_id) + { + if(!bDestroy) + it->ps->Stop(); + else + CParticlesObject::Destroy(it->ps); + } + } +} +//------------------------------------------------------------------------------------- + +CParticlesPlayer::CParticlesPlayer () +{ + bone_mask = u64(1)<LL_UserData(); + if(ini&&ini->section_exist("particle_bones")){ + bone_mask = 0; + CInifile::Sect& data = ini->r_section("particle_bones"); + for (CInifile::SectCIt I=data.Data.begin(); I!=data.Data.end(); I++){ + const CInifile::Item& item = *I; + u16 index = K->LL_BoneID(*item.first); + R_ASSERT3(index != BI_NONE, "Particles bone not found", *item.first); + Fvector offs; + sscanf (*item.second,"%f,%f,%f",&offs.x,&offs.y,&offs.z); + m_Bones.push_back (SBoneInfo(index,offs)); + bone_mask |= u64(1)<LL_GetBoneRoot(),Fvector().set(0,0,0))); + } +} +//уничтожение партиклов на net_Destroy +void CParticlesPlayer::net_DestroyParticles () +{ + VERIFY(m_self_object); + + for(BoneInfoVecIt b_it=m_Bones.begin(); b_it!=m_Bones.end(); b_it++) + { + SBoneInfo& b_info = *b_it; + + for (ParticlesInfoListIt p_it=b_info.particles.begin(); p_it!=b_info.particles.end(); p_it++) + { + SParticlesInfo& p_info = *p_it; + CParticlesObject::Destroy(p_info.ps); + } + b_info.particles.clear(); + } + + m_self_object = 0; +} + +CParticlesPlayer::SBoneInfo* CParticlesPlayer::get_nearest_bone_info(IKinematics* K, u16 bone_index) +{ + u16 play_bone = bone_index; + while((BI_NONE!=play_bone)&&!(bone_mask&(u64(1)<LL_GetData(play_bone).GetParentID(); + } + return get_bone_info(play_bone); +} + + +void CParticlesPlayer::StartParticles(const shared_str& particles_name, u16 bone_num, const Fvector& dir, u16 sender_id, int life_time, bool auto_stop) +{ + Fmatrix xform; + generate_orthonormal_basis(dir,xform); + StartParticles(particles_name,bone_num,xform,sender_id,life_time,auto_stop); +} +void CParticlesPlayer::StartParticles(const shared_str& particles_name, u16 bone_num, const Fmatrix& xform, u16 sender_id, int life_time, bool auto_stop) +{ + VERIFY(fis_zero(xform.c.magnitude())); + R_ASSERT(*particles_name); + + CObject* object = m_self_object; + VERIFY(object); + + SBoneInfo* pBoneInfo = get_nearest_bone_info(smart_cast(object->Visual()),bone_num); + if(!pBoneInfo) return; + + SParticlesInfo &particles_info =*pBoneInfo->AppendParticles(object,particles_name); + + particles_info.sender_id = sender_id; + + particles_info.life_time=auto_stop ? life_time : u32(-1); + xform.getHPB(particles_info.angles); + + Fmatrix m;m.setHPB(particles_info.angles.x,particles_info.angles.y,particles_info.angles.z); + GetBonePos(object,pBoneInfo->index,pBoneInfo->offset,m.c); + particles_info.ps->UpdateParent(m,zero_vel); + if(!particles_info.ps->IsPlaying()) + particles_info.ps->Play (false); + + m_bActiveBones = true; +} + +void CParticlesPlayer::StartParticles(const shared_str& ps_name, const Fmatrix& xform, u16 sender_id, int life_time, bool auto_stop) +{ + CObject* object = m_self_object; + VERIFY(object); + for(BoneInfoVecIt it = m_Bones.begin(); it!=m_Bones.end(); it++){ + + SParticlesInfo &particles_info =*it->AppendParticles(object,ps_name); + particles_info.sender_id = sender_id; + + particles_info.life_time=auto_stop ? life_time : u32(-1); + xform.getHPB(particles_info.angles); + //начать играть партиклы + + Fmatrix m;m.set(xform); + GetBonePos(object,it->index,it->offset,m.c); + particles_info.ps->UpdateParent(m,zero_vel); + if(!particles_info.ps->IsPlaying()) + particles_info.ps->Play (false); + } + + m_bActiveBones = true; +} + +void CParticlesPlayer::StartParticles(const shared_str& ps_name, const Fvector& dir, u16 sender_id, int life_time, bool auto_stop) +{ + Fmatrix xform; + generate_orthonormal_basis(dir,xform); + StartParticles(ps_name,xform,sender_id,life_time,auto_stop); +} + + +void CParticlesPlayer::StopParticles(u16 sender_id, u16 bone_id, bool bDestroy) +{ + if (BI_NONE==bone_id){ + for(BoneInfoVecIt it=m_Bones.begin(); it!=m_Bones.end(); it++) + it->StopParticles (sender_id, bDestroy); + }else{ + SBoneInfo* bi = get_bone_info(bone_id); VERIFY(bi); + bi->StopParticles (sender_id, bDestroy); + } + UpdateParticles(); +} + +void CParticlesPlayer::StopParticles(const shared_str& ps_name, u16 bone_id, bool bDestroy) +{ + if (BI_NONE==bone_id){ + for(BoneInfoVecIt it=m_Bones.begin(); it!=m_Bones.end(); it++) + it->StopParticles (ps_name, bDestroy); + }else{ + SBoneInfo* bi = get_bone_info(bone_id); VERIFY(bi); + bi->StopParticles (ps_name, bDestroy); + } + UpdateParticles(); +} + +//остановка партиклов, по истечении их времени жизни +void CParticlesPlayer::AutoStopParticles(const shared_str& ps_name, u16 bone_id,u32 life_time) +{ + if (BI_NONE==bone_id){ + for(BoneInfoVecIt it=m_Bones.begin(); it!=m_Bones.end(); it++) + { + SParticlesInfo* pInfo = it->FindParticles (ps_name); + if(pInfo) pInfo->life_time = life_time; + } + }else{ + SBoneInfo* bi = get_bone_info(bone_id); VERIFY(bi); + SParticlesInfo* pInfo = bi->FindParticles (ps_name); + if(pInfo) pInfo->life_time = life_time; + } +} +struct SRP +{ + bool operator () (CParticlesPlayer::SParticlesInfo& pi) + { + return ! pi.ps; + } +}; +void CParticlesPlayer::UpdateParticles() +{ + if (!m_bActiveBones) return; + m_bActiveBones = false; + + CObject* object = m_self_object; + VERIFY (object); + + for(BoneInfoVecIt b_it=m_Bones.begin(); b_it!=m_Bones.end(); b_it++){ + SBoneInfo& b_info = *b_it; + + for (ParticlesInfoListIt p_it=b_info.particles.begin(); p_it!=b_info.particles.end(); p_it++){ + SParticlesInfo& p_info = *p_it; + if(!p_info.ps) continue; + //обновить позицию партиклов + Fmatrix xform; + xform.setHPB(p_info.angles.x,p_info.angles.y,p_info.angles.z); + GetBonePos(object,b_info.index,b_info.offset,xform.c); + p_info.ps->UpdateParent(xform, parent_vel); + + //обновить время существования + if(p_info.life_time!=u32(-1)) + { + if(p_info.life_time>Device.dwTimeDelta) p_info.life_time-=Device.dwTimeDelta; + else + { + p_info.ps->Stop(); + p_info.life_time=u32(-1); + } + } + if(!p_info.ps->IsPlaying()){ + CParticlesObject::Destroy(p_info.ps); + } + else + m_bActiveBones = true; + } + + ParticlesInfoListIt RI=std::remove_if(b_info.particles.begin(),b_info.particles.end(),SRP()); + b_info.particles.erase(RI,b_info.particles.end()); + } +} + + +void CParticlesPlayer::GetBonePos (CObject* pObject, u16 bone_id, const Fvector& offset, Fvector& result) +{ + VERIFY(pObject); + IKinematics* pKinematics = smart_cast(pObject->Visual()); VERIFY(pKinematics); + CBoneInstance& l_tBoneInstance = pKinematics->LL_GetBoneInstance(bone_id); + + result = offset; + l_tBoneInstance.mTransform.transform_tiny(result); + pObject->XFORM().transform_tiny(result); +} + +void CParticlesPlayer::MakeXFORM (CObject* pObject, u16 bone_id, const Fvector& dir, const Fvector& offset, Fmatrix& result) +{ + generate_orthonormal_basis(dir,result); + GetBonePos(pObject, bone_id, offset, result.c); +} + +u16 CParticlesPlayer::GetNearestBone (IKinematics* K, u16 bone_id) +{ + u16 play_bone = bone_id; + + while((BI_NONE!=play_bone)&&!(bone_mask&(u64(1)<LL_GetData(play_bone).GetParentID(); + } + return play_bone; +} + +void CParticlesPlayer::net_SpawnParticles () +{ + VERIFY (!m_self_object); + m_self_object = smart_cast(this); + VERIFY (m_self_object); + +} diff --git a/src/xrGameLA/ParticlesPlayer.h b/src/xrGameLA/ParticlesPlayer.h new file mode 100644 index 000000000..68790a21d --- /dev/null +++ b/src/xrGameLA/ParticlesPlayer.h @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////// +// ParticlesPlayer.h +// интерфейс для проигрывания партиклов на объекте +/////////////////////////////////////////////////////////////// + +#pragma once + +#include "ParticlesObject.h" +#include "../bone.h" + + +DEFINE_VECTOR(CParticlesObject*, PARTICLES_PTR_VECTOR, PARTICLES_PTR_VECTOR_IT); + +class CObject; +class IKinematics; + +class CParticlesPlayer +{ + +public: + //структура с внутренней информацией о партикле + struct SParticlesInfo + { + CParticlesObject* ps; + //Fvector dir; + //Fmatrix x_form; + Fvector angles; + u16 sender_id; //id - объекта, который запустил партиклы + u32 life_time; //время жизни партикла (-1) - бесконечно + + //int cur_time; //текущее время существования партикла + //bool auto_stop; //автоматическая остановка партиклов, когда закончится время + }; + DEFINE_VECTOR (SParticlesInfo,ParticlesInfoList,ParticlesInfoListIt); + + //структура для косточки с списком запущенных партиклов + struct SBoneInfo + { + u16 index; + Fvector offset; + ParticlesInfoList particles; + SParticlesInfo* FindParticles (const shared_str& ps_name); + public: + SBoneInfo (u16 idx, const Fvector& offs):index(idx),offset(offs){;} + SParticlesInfo* AppendParticles (CObject* object, const shared_str& ps_name); + void StopParticles (const shared_str& ps_name, bool bDestroy); + void StopParticles (u16 sender_id, bool bDestroy); + }; + DEFINE_VECTOR (SBoneInfo,BoneInfoVec,BoneInfoVecIt); + +private: + // список костей + u64 bone_mask; // используемые кости + BoneInfoVec m_Bones; + CObject *m_self_object; + +protected : + bool m_bActiveBones; //есть ли косточки на которых играются партиклы + +public: + IC SBoneInfo* get_bone_info (u16 bone_index) + { + if (BI_NONE==bone_index) return 0; + for (BoneInfoVecIt it=m_Bones.begin(); it!=m_Bones.end(); it++) + if (it->index==bone_index) return &(*it); + return 0; + } + SBoneInfo* get_nearest_bone_info (IKinematics* K, u16 bone_index); + Fvector parent_vel; +public: + CParticlesPlayer (void); + virtual ~CParticlesPlayer (void); + void LoadParticles (IKinematics* K); + + void net_DestroyParticles (); + void net_SpawnParticles (); + + void UpdateParticles (); + + void StartParticles (const shared_str& ps_name, u16 bone_num, const Fvector& dir, u16 sender_id, int life_time = -1, bool auto_stop = true); + void StartParticles (const shared_str& ps_name, const Fvector& dir, u16 sender_id, int life_time = -1, bool auto_stop = true); + + void StartParticles (const shared_str& ps_name, u16 bone_num, const Fmatrix& dir, u16 sender_id, int life_time = -1, bool auto_stop = true); + void StartParticles (const shared_str& ps_name, const Fmatrix& dir, u16 sender_id, int life_time = -1, bool auto_stop = true); + + + void StopParticles (u16 sender_ID, u16 bone_id, bool bDestroy); + void StopParticles (const shared_str& particles_name, u16 bone_id, bool bDestroy); + void AutoStopParticles (const shared_str& ps_name, u16 bone_id,u32 life_time); + + static void MakeXFORM (CObject* pObject, u16 bone_id, const Fvector& dir, const Fvector& offset, Fmatrix& result); + static void GetBonePos (CObject* pObject, u16 bone_id, const Fvector& offset, Fvector& result); + u16 GetNearestBone (IKinematics* K, u16 bone_id); + IC u16 GetRandomBone (){ u16 l_PBCount=u16(m_Bones.size()); if(l_PBCount) return m_Bones[(u16)Random.randI(l_PBCount)].index; else return BI_NONE;} + + void SetParentVel (const Fvector& vel) {parent_vel = vel;} + + bool IsPlaying () {return m_bActiveBones;} + virtual CParticlesPlayer* cast_particles_player () {return this;} +}; \ No newline at end of file diff --git a/src/xrGameLA/PdaMsg.h b/src/xrGameLA/PdaMsg.h new file mode 100644 index 000000000..f14b076df --- /dev/null +++ b/src/xrGameLA/PdaMsg.h @@ -0,0 +1,45 @@ +///////////////////////////////////////////////////// +// +// PdaMsg.h - сообщение посылаемое при помощи PDA +// +///////////////////////////////////////////////////// + +#pragma once +#include "alife_space.h" +#include "pda_space.h" + +//структура для описания сообщения PDA, +//используется для ведения логов +typedef struct tagSPdaMessage +{ + EPdaMsg msg; + + //true если мы получали сообщение + //и false если мы его посылали + bool receive; + + //true, если сообщение - вопрос + //и false, если ответ + bool question; + + // информаци + shared_str info_id; + + //время получения/отправки сообщения + ALife::_TIME_ID time; + +} SPdaMessage; + + +//информация о контактах персонажей по PDA и во время диалога +struct TALK_CONTACT_DATA +{ + TALK_CONTACT_DATA():id(u16(-1)),time(0){}; + TALK_CONTACT_DATA(u16 contact_id, ALife::_TIME_ID contact_time):id(contact_id),time(contact_time){}; + //время контакта + ALife::_TIME_ID time; + //id персонажа с которым говорили + u16 id; +}; + +DEFINE_VECTOR(TALK_CONTACT_DATA, TALK_CONTACT_VECTOR, TALK_CONTACT_VECTOR_IT); diff --git a/src/xrGameLA/PdaScript.cpp b/src/xrGameLA/PdaScript.cpp new file mode 100644 index 000000000..d208ff76c --- /dev/null +++ b/src/xrGameLA/PdaScript.cpp @@ -0,0 +1,13 @@ +#include "pch_script.h" +#include "PDA.h" + +using namespace luabind; + +void CPda::script_register (lua_State *L) +{ + module(L) + [ + class_("CPda") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/Phrase.cpp b/src/xrGameLA/Phrase.cpp new file mode 100644 index 000000000..39b54fe39 --- /dev/null +++ b/src/xrGameLA/Phrase.cpp @@ -0,0 +1,33 @@ +/////////////////////////////////////////////////////////////// +// Phrase.cpp +// класс, описывающий фразу (элемент диалога) +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "phrase.h" + +#include "ai_space.h" +#include "gameobject.h" +#include "script_game_object.h" + +CPhrase::CPhrase (void) +{ + m_ID = ""; + m_iGoodwillLevel = 0; +} +CPhrase::~CPhrase (void) +{ +} + +LPCSTR CPhrase::GetText () const +{ + return m_text.c_str(); +} + +bool CPhrase::IsDummy() const +{ + if( xr_strlen(GetText()) == 0 ) + return true; + + return false; +} diff --git a/src/xrGameLA/Phrase.h b/src/xrGameLA/Phrase.h new file mode 100644 index 000000000..15b249e43 --- /dev/null +++ b/src/xrGameLA/Phrase.h @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////// +// Phrase.h +// класс, описывающий фразу (элемент диалога) +/////////////////////////////////////////////////////////////// + +#pragma once + +#include "PhraseScript.h" + +class CPhraseDialog; +class CGameObject; + +class CPhrase +{ +private: + friend CPhraseDialog; +public: + CPhrase(void); + virtual ~CPhrase(void); + + void SetText (LPCSTR text) {m_text = text;} + LPCSTR GetText () const; + + void SetID (const shared_str& id) {m_ID = id;} + const shared_str& GetID () const {return m_ID;} + + int GoodwillLevel () const {return m_iGoodwillLevel;} + + bool IsDummy () const; + CPhraseScript* GetPhraseScript () {return &m_PhraseScript;}; + +protected: + //уникальный индекс в списке фраз диалога + shared_str m_ID; + //текстовое представление фразы + xr_string m_text; + + //минимальный уровень благосклоггости, необходимый для того + //чтоб фразу можно было сказать + int m_iGoodwillLevel; + + //для вызова скриптовых функций + CPhraseScript m_PhraseScript; +}; \ No newline at end of file diff --git a/src/xrGameLA/PhraseDialog.cpp b/src/xrGameLA/PhraseDialog.cpp new file mode 100644 index 000000000..ce2fa0a5d --- /dev/null +++ b/src/xrGameLA/PhraseDialog.cpp @@ -0,0 +1,333 @@ +#include "pch_script.h" +#include "phrasedialog.h" +#include "phrasedialogmanager.h" +#include "gameobject.h" +#include "ai_debug.h" + +SPhraseDialogData::SPhraseDialogData () +{ + m_PhraseGraph.clear (); + m_iPriority = 0; +} + +SPhraseDialogData::~SPhraseDialogData () +{} + + + +CPhraseDialog::CPhraseDialog() +{ + m_bFinished = false; + m_pSpeakerFirst = NULL; + m_pSpeakerSecond = NULL; + m_DialogId = NULL; +} + +CPhraseDialog::~CPhraseDialog() +{ +} + + +void CPhraseDialog::Init(CPhraseDialogManager* speaker_first, + CPhraseDialogManager* speaker_second) +{ + THROW(!IsInited()); + + m_pSpeakerFirst = speaker_first; + m_pSpeakerSecond = speaker_second; + + m_SaidPhraseID = ""; + m_PhraseVector.clear(); + + CPhraseGraph::CVertex* phrase_vertex = data()->m_PhraseGraph.vertex("0"); + THROW(phrase_vertex); + m_PhraseVector.push_back(phrase_vertex->data()); + + m_bFinished = false; + m_bFirstIsSpeaking = true; +} + +//обнуляем все связи +void CPhraseDialog::Reset () +{ +} + +CPhraseDialogManager* CPhraseDialog::OurPartner (CPhraseDialogManager* dialog_manager) const +{ + if(FirstSpeaker() == dialog_manager) + return SecondSpeaker(); + else + return FirstSpeaker(); +} + + +CPhraseDialogManager* CPhraseDialog::CurrentSpeaker() const +{ + return FirstIsSpeaking()?m_pSpeakerFirst:m_pSpeakerSecond; +} +CPhraseDialogManager* CPhraseDialog::OtherSpeaker () const +{ + return (!FirstIsSpeaking())?m_pSpeakerFirst:m_pSpeakerSecond; +} + + +//предикат для сортировки вектора фраз +static bool PhraseGoodwillPred(const CPhrase* phrase1, const CPhrase* phrase2) +{ + return phrase1->GoodwillLevel()>phrase2->GoodwillLevel(); +} + +bool CPhraseDialog::SayPhrase (DIALOG_SHARED_PTR& phrase_dialog, const shared_str& phrase_id) +{ + THROW(phrase_dialog->IsInited()); + + phrase_dialog->m_SaidPhraseID = phrase_id; + + bool first_is_speaking = phrase_dialog->FirstIsSpeaking(); + phrase_dialog->m_bFirstIsSpeaking = !phrase_dialog->m_bFirstIsSpeaking; + + const CGameObject* pSpeakerGO1 = smart_cast(phrase_dialog->FirstSpeaker()); VERIFY(pSpeakerGO1); + const CGameObject* pSpeakerGO2 = smart_cast(phrase_dialog->SecondSpeaker()); VERIFY(pSpeakerGO2); + if(!first_is_speaking) std::swap(pSpeakerGO1, pSpeakerGO2); + + CPhraseGraph::CVertex* phrase_vertex = phrase_dialog->data()->m_PhraseGraph.vertex(phrase_dialog->m_SaidPhraseID); + THROW(phrase_vertex); + + CPhrase* last_phrase = phrase_vertex->data(); + + //вызвать скриптовую присоединенную функцию + //активируется после сказанной фразы + //первый параметр - тот кто говорит фразу, второй - тот кто слушает + last_phrase->m_PhraseScript.Action(pSpeakerGO1, pSpeakerGO2, *phrase_dialog->m_DialogId, phrase_id.c_str() ); + + //больше нет фраз, чтоб говорить + phrase_dialog->m_PhraseVector.clear(); + if(phrase_vertex->edges().empty()) + { + phrase_dialog->m_bFinished = true; + } + else + { + //обновить список фраз, которые сейчас сможет говорить собеседник + for(xr_vector::const_iterator it = phrase_vertex->edges().begin(); + it != phrase_vertex->edges().end(); + it++) + { + const CPhraseGraph::CEdge& edge = *it; + CPhraseGraph::CVertex* next_phrase_vertex = phrase_dialog->data()->m_PhraseGraph.vertex(edge.vertex_id()); + THROW (next_phrase_vertex); + shared_str next_phrase_id = next_phrase_vertex->vertex_id(); + if(next_phrase_vertex->data()->m_PhraseScript.Precondition(pSpeakerGO2, pSpeakerGO1, *phrase_dialog->m_DialogId, phrase_id.c_str(), next_phrase_id.c_str())) + { + phrase_dialog->m_PhraseVector.push_back(next_phrase_vertex->data()); +#ifdef DEBUG + if(psAI_Flags.test(aiDialogs)){ + LPCSTR phrase_text = next_phrase_vertex->data()->GetText(); + shared_str id = next_phrase_vertex->data()->GetID(); + Msg("----added phrase text [%s]phrase_id=[%s] id=[%s] to dialog [%s]",phrase_text, phrase_id.c_str(), *id, *phrase_dialog->m_DialogId); + } +#endif + } + + } + + if (phrase_dialog->m_PhraseVector.empty()) { + Msg("!! Error: No available phrase to say"); + Msg("!! [%s]:phrase stack:",phrase_id.c_str()); + for(xr_vector::const_iterator it = phrase_vertex->edges().begin(); + it != phrase_vertex->edges().end(); + it++) { + const CPhraseGraph::CEdge& edge = *it; + CPhraseGraph::CVertex* next_phrase_vertex = phrase_dialog->data()->m_PhraseGraph.vertex(edge.vertex_id()); + THROW (next_phrase_vertex); + shared_str next_phrase_id = next_phrase_vertex->vertex_id(); + Msg(" - %s:%d",next_phrase_id.c_str(),next_phrase_vertex->data()->GetText()); + } + R_ASSERT2 ( + 0, + make_string( + "No available phrase to say, dialog[%s]", + *phrase_dialog->m_DialogId + ) + ); + } + + //упорядочить списко по убыванию благосклонности + std::sort(phrase_dialog->m_PhraseVector.begin(), + phrase_dialog->m_PhraseVector.end(), PhraseGoodwillPred); + } + + + + //сообщить CDialogManager, что сказана фраза + //и ожидается ответ + if(first_is_speaking) + phrase_dialog->SecondSpeaker()->ReceivePhrase(phrase_dialog); + else + phrase_dialog->FirstSpeaker()->ReceivePhrase(phrase_dialog); + + + return phrase_dialog?!phrase_dialog->m_bFinished:true; +} + +LPCSTR CPhraseDialog::GetPhraseText (const shared_str& phrase_id, bool current_speaking) +{ + + CPhraseGraph::CVertex* phrase_vertex = data()->m_PhraseGraph.vertex(phrase_id); + THROW(phrase_vertex); + + return phrase_vertex->data()->GetText(); +} + +LPCSTR CPhraseDialog::DialogCaption() +{ + return data()->m_sCaption.size()?*data()->m_sCaption:GetPhraseText("0"); +} + + +int CPhraseDialog::Priority() +{ + return data()->m_iPriority; +} + + +void CPhraseDialog::Load(shared_str dialog_id) +{ + m_DialogId = dialog_id; + inherited_shared::load_shared(m_DialogId, NULL); +} + +#include "script_engine.h" +#include "ai_space.h" + +void CPhraseDialog::load_shared (LPCSTR) +{ + const ITEM_DATA& item_data = *id_to_index::GetById(m_DialogId); + + CUIXml* pXML = item_data._xml; + pXML->SetLocalRoot (pXML->GetRoot()); + + //loading from XML + XML_NODE* dialog_node = pXML->NavigateToNode(id_to_index::tag_name, item_data.pos_in_file); + THROW3(dialog_node, "dialog id=", *item_data.id); + + pXML->SetLocalRoot(dialog_node); + + + SetPriority ( pXML->ReadAttribInt(dialog_node, "priority", 0) ); + + //заголовок + SetCaption ( pXML->Read(dialog_node, "caption", 0, NULL) ); + + //предикаты начала диалога + data()->m_PhraseScript.Load(pXML, dialog_node); + + //заполнить граф диалога фразами + data()->m_PhraseGraph.clear(); + + XML_NODE* phrase_list_node = pXML->NavigateToNode(dialog_node, "phrase_list", 0); + if(NULL == phrase_list_node){ + LPCSTR func = pXML->Read(dialog_node, "init_func", 0, ""); + + luabind::functor lua_function; + bool functor_exists = ai().script_engine().functor(func ,lua_function); + THROW3(functor_exists, "Cannot find precondition", func); + lua_function (this); + return; + } + + int phrase_num = pXML->GetNodesNum(phrase_list_node, "phrase"); + THROW3(phrase_num, "dialog %s has no phrases at all", *item_data.id); + + pXML->SetLocalRoot(phrase_list_node); + +#ifdef DEBUG // debug & mixed + LPCSTR wrong_phrase_id = pXML->CheckUniqueAttrib(phrase_list_node, "phrase", "id"); + THROW3(wrong_phrase_id == NULL, *item_data.id, wrong_phrase_id); +#endif + + //ищем стартовую фразу + XML_NODE* phrase_node = pXML->NavigateToNodeWithAttribute("phrase", "id", "0"); + THROW (phrase_node); + AddPhrase (pXML, phrase_node, "0", ""); +} + +void CPhraseDialog::SetCaption (LPCSTR str) +{ + data()->m_sCaption = str; +} + +void CPhraseDialog::SetPriority (int val) +{ + data()->m_iPriority = val; +} + +CPhrase* CPhraseDialog::AddPhrase (LPCSTR text, const shared_str& phrase_id, const shared_str& prev_phrase_id, int goodwil_level) +{ + CPhrase* phrase = NULL; + CPhraseGraph::CVertex* _vertex = data()->m_PhraseGraph.vertex(phrase_id); + if(!_vertex) + { + phrase = new CPhrase(); VERIFY(phrase); + phrase->SetID (phrase_id); + + phrase->SetText (text); + phrase->m_iGoodwillLevel = goodwil_level; + + data()->m_PhraseGraph.add_vertex (phrase, phrase_id); + } + + if(prev_phrase_id != "") + data()->m_PhraseGraph.add_edge (prev_phrase_id, phrase_id, 0.f); + + return phrase; +} + +void CPhraseDialog::AddPhrase (CUIXml* pXml, XML_NODE* phrase_node, const shared_str& phrase_id, const shared_str& prev_phrase_id) +{ + + LPCSTR sText = pXml->Read (phrase_node, "text", 0, ""); + int gw = pXml->ReadInt (phrase_node, "goodwill", 0, -10000); + CPhrase* ph = AddPhrase (sText, phrase_id, prev_phrase_id, gw); + if(!ph) return; + + ph->m_PhraseScript.Load (pXml, phrase_node); + + //фразы которые собеседник может говорить после этой + int next_num = pXml->GetNodesNum(phrase_node, "next"); + for(int i=0; iRead(phrase_node, "next", i, ""); + XML_NODE* next_phrase_node = pXml->NavigateToNodeWithAttribute("phrase", "id", next_phrase_id_str); + R_ASSERT2 (next_phrase_node, make_string("next phrase[%s] is not exist in dialog",next_phrase_id_str)); +//. int next_phrase_id = atoi(next_phrase_id_str); + + AddPhrase (pXml, next_phrase_node, next_phrase_id_str, phrase_id); + } +} + +bool CPhraseDialog::Precondition(const CGameObject* pSpeaker1, const CGameObject* pSpeaker2) +{ + return data()->m_PhraseScript.Precondition(pSpeaker1, pSpeaker2, m_DialogId.c_str(), "", ""); +} + +void CPhraseDialog::InitXmlIdToIndex() +{ + if(!id_to_index::tag_name) + id_to_index::tag_name = "dialog"; + if(!id_to_index::file_str) + id_to_index::file_str = pSettings->r_string("dialogs", "files"); +} + +bool CPhraseDialog::allIsDummy () +{ + PHRASE_VECTOR_IT it = m_PhraseVector.begin(); + bool bAllIsDummy = true; + for(;it!=m_PhraseVector.end();++it) + { + if( !(*it)->IsDummy() ) + bAllIsDummy=false; + } + + return bAllIsDummy; +} diff --git a/src/xrGameLA/PhraseDialog.h b/src/xrGameLA/PhraseDialog.h new file mode 100644 index 000000000..5d9361a6b --- /dev/null +++ b/src/xrGameLA/PhraseDialog.h @@ -0,0 +1,139 @@ +#pragma once + +#include "shared_data.h" +#include "phrase.h" +#include "graph_abstract.h" +#include "PhraseDialogDefs.h" +#include "xml_str_id_loader.h" + +typedef CGraphAbstract CPhraseGraph; + + +struct SPhraseDialogData : CSharedResource +{ + SPhraseDialogData (); + virtual ~SPhraseDialogData (); + + //заголовок диалога, если NULL, то принимается за стартовую фразу + shared_str m_sCaption; + + //однонаправленый граф фраз + //описывает все возможные варианты развития диалога + CPhraseGraph m_PhraseGraph; + + //список скриптовых предикатов, выполнение, которых необходимо + //для начала диалога + CPhraseScript m_PhraseScript; + + //произвольное число - приоритет диалога (0 по умолчанию), может быть отрицательным + //в окне выбора у актера диалоги будут сортироваться по этому значению от меньшего (снизу) к большему (сверху) + int m_iPriority; +}; + +DEFINE_VECTOR(CPhrase*, PHRASE_VECTOR, PHRASE_VECTOR_IT); + +class CPhraseDialog; +class CPhraseDialogManager; + +class CPhraseDialog : public CSharedClass, + public CXML_IdToIndex, + public intrusive_base +{ +private: + typedef CSharedClass inherited_shared; + typedef CXML_IdToIndex id_to_index; + + friend id_to_index; +public: + CPhraseDialog (); + virtual ~CPhraseDialog (); + + CPhraseDialog (const CPhraseDialog& pharase_dialog) {*this = pharase_dialog;} + CPhraseDialog& operator = (const CPhraseDialog& pharase_dialog) {*this = pharase_dialog; return *this;} + + + virtual void Load (shared_str dialog_id); + + //связь диалога между двумя DialogManager + virtual void Init (CPhraseDialogManager* speaker_first, CPhraseDialogManager* speaker_second); + + IC bool IsInited () const {return ((FirstSpeaker()!=NULL)&& (SecondSpeaker()!=NULL));} + + //реинициализация диалога + virtual void Reset (); + + //список предикатов начала диалога + virtual bool Precondition (const CGameObject* pSpeaker1, const CGameObject* pSpeaker2); + + //список доступных в данный момент фраз + virtual const PHRASE_VECTOR& PhraseList () const {return m_PhraseVector;} + bool allIsDummy (); + //сказать фразу и перейти к следующей стадии диалога + //если вернули false, то считаем, что диалог закончился + //(сделано статическим, так как мы должны передавать имеенно DIALOG_SHARED_PTR&, + //а не обычный указатель) + static bool SayPhrase (DIALOG_SHARED_PTR& phrase_dialog, const shared_str& phrase_id); + + LPCSTR GetPhraseText (const shared_str& phrase_id, bool current_speaking = true); + LPCSTR GetLastPhraseText () {return GetPhraseText(m_SaidPhraseID, false);} + const shared_str& GetDialogID () const {return m_DialogId;} + + //заголовок, диалога, если не задан, то 0-я фраза + const shared_str& GetLastPhraseID () {return m_SaidPhraseID;} + LPCSTR DialogCaption (); + int Priority (); + + + bool IsFinished () const {return m_bFinished;} + + IC CPhraseDialogManager* FirstSpeaker () const {return m_pSpeakerFirst;} + IC CPhraseDialogManager* SecondSpeaker () const {return m_pSpeakerSecond;} + + //кто собирается говорить и кто слушать + CPhraseDialogManager* CurrentSpeaker () const; + CPhraseDialogManager* OtherSpeaker () const; + //кто последний сказал фразу + CPhraseDialogManager* LastSpeaker () const {return m_bFirstIsSpeaking?SecondSpeaker():FirstSpeaker();} + + IC bool FirstIsSpeaking () const {return m_bFirstIsSpeaking;} + IC bool SecondIsSpeaking () const {return !m_bFirstIsSpeaking;} + + IC bool IsWeSpeaking (CPhraseDialogManager* dialog_manager) const {return (FirstSpeaker()==dialog_manager && FirstIsSpeaking()) || + (SecondSpeaker()==dialog_manager && SecondIsSpeaking());} + CPhraseDialogManager* OurPartner (CPhraseDialogManager* dialog_manager) const; + +protected: + //идентификатор диалога + shared_str m_DialogId; + + //ID последней сказанной фразы в диалоге, "" если такой не было + shared_str m_SaidPhraseID; + //диалог закончен + bool m_bFinished; + + //список указателей на фразы доступные в данный момент + PHRASE_VECTOR m_PhraseVector; + + //указатели на собеседников в диалоге + CPhraseDialogManager* m_pSpeakerFirst; + CPhraseDialogManager* m_pSpeakerSecond; + bool m_bFirstIsSpeaking; + + const SPhraseDialogData* data () const { VERIFY(inherited_shared::get_sd()); return inherited_shared::get_sd();} + SPhraseDialogData* data () { VERIFY(inherited_shared::get_sd()); return inherited_shared::get_sd();} + + //загрузка диалога из XML файла + virtual void load_shared (LPCSTR); + + //рекурсивное добавление фраз в граф + void AddPhrase (CUIXml* pXml, XML_NODE* phrase_node, const shared_str& phrase_id, const shared_str& prev_phrase_id); +public: + CPhrase* AddPhrase (LPCSTR text, const shared_str& phrase_id, const shared_str& prev_phrase_id, int goodwil_level); + CPhrase* AddPhrase_script (LPCSTR text, LPCSTR phrase_id, LPCSTR prev_phrase_id, int goodwil_level){return AddPhrase(text, phrase_id, prev_phrase_id, goodwil_level);}; + void SetCaption (LPCSTR str); + void SetPriority (int val); + +protected: + + static void InitXmlIdToIndex(); +}; diff --git a/src/xrGameLA/PhraseDialogDefs.h b/src/xrGameLA/PhraseDialogDefs.h new file mode 100644 index 000000000..175768c17 --- /dev/null +++ b/src/xrGameLA/PhraseDialogDefs.h @@ -0,0 +1,17 @@ +#pragma once +//#include "boost/shared_ptr.hpp" + +class CPhraseDialog; +//typedef boost::shared_ptr DIALOG_SHARED_PTR; +typedef intrusive_ptr DIALOG_SHARED_PTR; + + +//. typedef int PHRASE_ID; + +//. typedef shared_str PHRASE_DIALOG_ID; + + DEFINE_VECTOR(shared_str, DIALOG_ID_VECTOR, DIALOG_ID_IT); +//. #define NO_PHRASE -1 +//. #define START_PHRASE 0 +//. #define START_PHRASE_STR "0" +#include "PhraseDialog.h" \ No newline at end of file diff --git a/src/xrGameLA/PhraseDialogManager.cpp b/src/xrGameLA/PhraseDialogManager.cpp new file mode 100644 index 000000000..f202430f5 --- /dev/null +++ b/src/xrGameLA/PhraseDialogManager.cpp @@ -0,0 +1,116 @@ +/////////////////////////////////////////////////////////////// +// PhraseDialogManager.cpp +// Класс, от которого наследуются персонажи, ведущие диалог +// между собой +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "PhraseDialogManager.h" +#include "PhraseDialog.h" + +#include "ai_space.h" +#include "script_engine.h" +#include "gameobject.h" +#include "script_game_object.h" + +CPhraseDialogManager::CPhraseDialogManager (void) +{ +} +CPhraseDialogManager::~CPhraseDialogManager (void) +{ +} + +const DIALOG_SHARED_PTR& CPhraseDialogManager::GetDialogByID(const shared_str& dialog_id) const +{ + R_ASSERT2(HaveAvailableDialog(dialog_id),dialog_id.c_str()); + DIALOG_VECTOR::const_iterator it = m_AvailableDialogs.begin(); + DIALOG_VECTOR::const_iterator it_e = m_AvailableDialogs.end(); + + for(; it!=it_e; ++it) + { + const DIALOG_SHARED_PTR& dialog = *it; + if(dialog->GetDialogID()==dialog_id) + return dialog; + } + return m_AvailableDialogs.front(); +} + +bool CPhraseDialogManager::HaveAvailableDialog(const shared_str& dialog_id) const +{ + DIALOG_VECTOR::const_iterator it = m_AvailableDialogs.begin(); + DIALOG_VECTOR::const_iterator it_e = m_AvailableDialogs.end(); + + for(; it!=it_e; ++it) + { + const DIALOG_SHARED_PTR& dialog = *it; + if(dialog->GetDialogID()==dialog_id) + return true; + } + + return false; +} + +void CPhraseDialogManager::InitDialog (CPhraseDialogManager* dialog_partner, DIALOG_SHARED_PTR& phrase_dialog) +{ + phrase_dialog->Init(this, dialog_partner); + AddDialog(phrase_dialog); + dialog_partner->AddDialog(phrase_dialog); +} + +void CPhraseDialogManager::AddDialog(DIALOG_SHARED_PTR& phrase_dialog) +{ + DIALOG_VECTOR_IT it = std::find(m_ActiveDialogs.begin(), m_ActiveDialogs.end(), phrase_dialog); + THROW(m_ActiveDialogs.end() == it); + m_ActiveDialogs.push_back(phrase_dialog); +} + +void CPhraseDialogManager::ReceivePhrase(DIALOG_SHARED_PTR& phrase_dialog) +{ +} + +void CPhraseDialogManager::SayPhrase(DIALOG_SHARED_PTR& phrase_dialog, const shared_str& phrase_id) +{ + DIALOG_VECTOR_IT it = std::find(m_ActiveDialogs.begin(), m_ActiveDialogs.end(), phrase_dialog); + THROW(m_ActiveDialogs.end() != it); + + THROW(phrase_dialog->IsWeSpeaking (this)); + bool coninue_talking = CPhraseDialog::SayPhrase(phrase_dialog, phrase_id); + + if(!coninue_talking) + m_ActiveDialogs.erase(it); +} + + + +static bool dialog_priority (DIALOG_SHARED_PTR dialog1, DIALOG_SHARED_PTR dialog2) +{ + if(dialog1->Priority() > dialog2->Priority()) + return true; + else + return false; +} + +void CPhraseDialogManager::UpdateAvailableDialogs(CPhraseDialogManager* partner) +{ + std::sort(m_AvailableDialogs.begin(), m_AvailableDialogs.end(), dialog_priority); +} + +bool CPhraseDialogManager::AddAvailableDialog(shared_str dialog_id, CPhraseDialogManager* partner) +{ +// PHRASE_DIALOG_INDEX dialog_index = CPhraseDialog::IdToIndex(dialog_id); + if(std::find(m_CheckedDialogs.begin(), m_CheckedDialogs.end(), dialog_id) != m_CheckedDialogs.end()) + return false; + m_CheckedDialogs.push_back(dialog_id); + + DIALOG_SHARED_PTR phrase_dialog(new CPhraseDialog()); + phrase_dialog->Load(dialog_id); + + //вызвать скриптовую присоединенную функцию + //активируется после сказанной фразы + const CGameObject* pSpeakerGO1 = smart_cast(this); VERIFY(pSpeakerGO1); + const CGameObject* pSpeakerGO2 = smart_cast(partner); VERIFY(pSpeakerGO2); + + bool predicate_result = phrase_dialog->Precondition(pSpeakerGO1, pSpeakerGO2); + if(predicate_result) m_AvailableDialogs.push_back(phrase_dialog); + return predicate_result; +} \ No newline at end of file diff --git a/src/xrGameLA/PhraseDialogManager.h b/src/xrGameLA/PhraseDialogManager.h new file mode 100644 index 000000000..787b9f229 --- /dev/null +++ b/src/xrGameLA/PhraseDialogManager.h @@ -0,0 +1,41 @@ +#pragma once + +//#include "PhraseDialogDefs.h" +#include "PhraseDialog.h" + +class CPhraseDialogManager +{ +public: + CPhraseDialogManager (void); + virtual ~CPhraseDialogManager (void); + + virtual void InitDialog (CPhraseDialogManager* dialog_partner, DIALOG_SHARED_PTR& phrase_dialog); + virtual void AddDialog (DIALOG_SHARED_PTR& phrase_dialog); + + //получение фразы, виртуальная функция, + //должна быть переопределена для сталкеров и актера + virtual void ReceivePhrase (DIALOG_SHARED_PTR& phrase_dialog); + //ответить на сказанную фразу в диалоге + virtual void SayPhrase (DIALOG_SHARED_PTR& phrase_dialog, const shared_str& phrase_id); + + //виртуальная функция, заполняет массив, тем диалогами, которые + //персонаж может инициировать в данный момент + virtual void UpdateAvailableDialogs(CPhraseDialogManager* partner); + + DEFINE_VECTOR (DIALOG_SHARED_PTR, DIALOG_VECTOR, DIALOG_VECTOR_IT); + const DIALOG_VECTOR& AvailableDialogs () {return m_AvailableDialogs;} + const DIALOG_SHARED_PTR& GetDialogByID (const shared_str& dialog_id) const; + bool HaveAvailableDialog (const shared_str& dialog_id) const; + +protected: + virtual bool AddAvailableDialog (shared_str dialog_id, CPhraseDialogManager* partner); + + //буфферный список диалогов, которые были проверены + //во время UpdateAvailableDialogs + DIALOG_ID_VECTOR m_CheckedDialogs; + + //список активных диалогов + DIALOG_VECTOR m_ActiveDialogs; + //список доступных диалогов + DIALOG_VECTOR m_AvailableDialogs; +}; \ No newline at end of file diff --git a/src/xrGameLA/PhraseDialog_script.cpp b/src/xrGameLA/PhraseDialog_script.cpp new file mode 100644 index 000000000..f42ec09b3 --- /dev/null +++ b/src/xrGameLA/PhraseDialog_script.cpp @@ -0,0 +1,52 @@ +#include "pch_script.h" +#include "PhraseDialog_script.h" +#include "PhraseDialog.h" + +using namespace luabind; + +void CPhraseScript::AddPrecondition (LPCSTR str) +{ + m_Preconditions.push_back (str); +} +void CPhraseScript::AddAction (LPCSTR str) +{ + m_ScriptActions.push_back (str); +} +void CPhraseScript::AddHasInfo (LPCSTR str) +{ + m_HasInfo.push_back (str); +} +void CPhraseScript::AddDontHasInfo (LPCSTR str) +{ + m_DontHasInfo.push_back (str); +} +void CPhraseScript::AddGiveInfo (LPCSTR str) +{ + m_GiveInfo.push_back (str); +} +void CPhraseScript::AddDisableInfo (LPCSTR str) +{ + m_DisableInfo.push_back (str); +} + + +#pragma optimize("s",on) +void CPhraseDialogExporter::script_register(lua_State *L) +{ + module(L) + [ + class_("CPhrase") + .def("GetPhraseScript", &CPhrase::GetPhraseScript), + + class_("CPhraseDialog") + .def("AddPhrase", &CPhraseDialog::AddPhrase_script ), + + class_("CPhraseScript") + .def("AddPrecondition", &CPhraseScript::AddPrecondition) + .def("AddAction", &CPhraseScript::AddAction) + .def("AddHasInfo", &CPhraseScript::AddHasInfo) + .def("AddDontHasInfo", &CPhraseScript::AddDontHasInfo) + .def("AddGiveInfo", &CPhraseScript::AddGiveInfo) + .def("AddDisableInfo", &CPhraseScript::AddDisableInfo) + ]; +} diff --git a/src/xrGameLA/PhraseDialog_script.h b/src/xrGameLA/PhraseDialog_script.h new file mode 100644 index 000000000..6d22622b3 --- /dev/null +++ b/src/xrGameLA/PhraseDialog_script.h @@ -0,0 +1,11 @@ +#pragma once +#include "script_export_space.h" + +struct CPhraseDialogExporter +{ + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CPhraseDialogExporter) +#undef script_type_list +#define script_type_list save_type_list(CPhraseDialogExporter) diff --git a/src/xrGameLA/PhraseScript.cpp b/src/xrGameLA/PhraseScript.cpp new file mode 100644 index 000000000..e3170656b --- /dev/null +++ b/src/xrGameLA/PhraseScript.cpp @@ -0,0 +1,231 @@ +#include "pch_script.h" +#include "PhraseScript.h" +#include "script_engine.h" +#include "ai_space.h" +#include "gameobject.h" +#include "script_game_object.h" +#include "infoportion.h" +#include "inventoryowner.h" +#include "ai_debug.h" +#include "ui/xrUIXmlParser.h" +#include "actor.h" + +CPhraseScript::CPhraseScript () +{ +} +CPhraseScript::~CPhraseScript () +{ +} + +//загрузка из XML файла +void CPhraseScript::Load (CUIXml* uiXml, XML_NODE* phrase_node) +{ +// m_sScriptTextFunc = uiXml.Read(phrase_node, "script_text", 0, NULL); + + LoadSequence(uiXml,phrase_node, "precondition", m_Preconditions); + LoadSequence(uiXml,phrase_node, "action", m_ScriptActions); + + LoadSequence(uiXml,phrase_node, "has_info", m_HasInfo); + LoadSequence(uiXml,phrase_node, "dont_has_info", m_DontHasInfo); + + LoadSequence(uiXml,phrase_node, "give_info", m_GiveInfo); + LoadSequence(uiXml,phrase_node, "disable_info", m_DisableInfo); +} + +template +void CPhraseScript::LoadSequence (CUIXml* uiXml, XML_NODE* phrase_node, + LPCSTR tag, T& str_vector) +{ + int tag_num = uiXml->GetNodesNum(phrase_node, tag); + str_vector.clear(); + for(int i=0; iRead(phrase_node, tag, i, NULL); + str_vector.push_back(tag_text); + } +} + +bool CPhraseScript::CheckInfo (const CInventoryOwner* pOwner) const +{ + THROW(pOwner); + + for(u32 i=0; iAndy how to check infoportion existence in XML ?") +/* INFO_INDEX result = CInfoPortion::IdToIndex(m_HasInfo[i],NO_INFO_INDEX,true); + if (result == NO_INFO_INDEX) { + ai().script_engine().script_log(eLuaMessageTypeError,"XML item not found : \"%s\"",*m_HasInfo[i]); + break; + } +*/ +//. if (!pOwner->HasInfo(m_HasInfo[i])) { + if (!Actor()->HasInfo(m_HasInfo[i])) { +#ifdef DEBUG + if(psAI_Flags.test(aiDialogs) ) + Msg("----rejected: [%s] has info %s", pOwner->Name(), *m_HasInfo[i]); +#endif + return false; + } + } + + for(i=0; iHasInfo(m_DontHasInfo[i])) { + if (Actor()->HasInfo(m_DontHasInfo[i])) { +#ifdef DEBUG + if(psAI_Flags.test(aiDialogs) ) + Msg("----rejected: [%s] dont has info %s", pOwner->Name(), *m_DontHasInfo[i]); +#endif + return false; + } + } + return true; +} + + +void CPhraseScript::TransferInfo (const CInventoryOwner* pOwner) const +{ + THROW(pOwner); + + for(u32 i=0; iTransferInfo(m_GiveInfo[i], true); + Actor()->TransferInfo(m_GiveInfo[i], true); + + for(i=0; iTransferInfo(m_DisableInfo[i],false); + Actor()->TransferInfo(m_DisableInfo[i], false); +} + + + +bool CPhraseScript::Precondition(const CGameObject* pSpeakerGO, LPCSTR dialog_id, LPCSTR phrase_id) const +{ + bool predicate_result = true; + if(!CheckInfo(smart_cast(pSpeakerGO))) + { + #ifdef DEBUG + if (psAI_Flags.test(aiDialogs)) + Msg("dialog [%s] phrase[%s] rejected by CheckInfo",dialog_id,phrase_id); + #endif + return false; + } + + for(u32 i = 0; i lua_function; + THROW(*Preconditions()[i]); + + //tatarinrafa: added availability to return reversed precondition result; for script optimization + bool functor_exists; + if (strstr(*Preconditions()[i], ".ReturnReverse")){ + + xr_string preconditionScript = *Preconditions()[i]; + preconditionScript.erase(preconditionScript.size() - 14); + Msg("single param %s precondition Reversed ", preconditionScript.c_str()); + + functor_exists = ai().script_engine().functor(*Preconditions()[i], lua_function); + THROW3(functor_exists, "Cannot find precondition", *Preconditions()[i]); + predicate_result = !lua_function(pSpeakerGO->lua_game_object()); + } + else{ + + functor_exists = ai().script_engine().functor(*Preconditions()[i], lua_function); + THROW3(functor_exists, "Cannot find precondition", *Preconditions()[i]); + predicate_result = lua_function(pSpeakerGO->lua_game_object()); + + } + + if(!predicate_result){ + #ifdef DEBUG + if (psAI_Flags.test(aiDialogs)) + Msg("dialog [%s] phrase[%s] rejected by script predicate", dialog_id, phrase_id); + #endif + break; + } + } + return predicate_result; +} + +void CPhraseScript::Action(const CGameObject* pSpeakerGO, LPCSTR dialog_id, LPCSTR phrase_id) const +{ + + for(u32 i = 0; i lua_function; + THROW(*Actions()[i]); + bool functor_exists = ai().script_engine().functor(*Actions()[i] ,lua_function); + R_ASSERT2 (functor_exists, make_string("Cannot find script function[%s] in phrase dialog[%s][%s]", *Actions()[i], dialog_id, phrase_id)); + lua_function (pSpeakerGO->lua_game_object(), dialog_id); + } + TransferInfo(smart_cast(pSpeakerGO)); +} + +bool CPhraseScript::Precondition ( const CGameObject* pSpeakerGO1, + const CGameObject* pSpeakerGO2, + LPCSTR dialog_id, + LPCSTR phrase_id, + LPCSTR next_phrase_id) const +{ + bool predicate_result = true; + if(!CheckInfo(smart_cast(pSpeakerGO1))){ + #ifdef DEBUG + if (psAI_Flags.test(aiDialogs)) + Msg("dialog [%s] phrase[%s] rejected by CheckInfo",dialog_id,phrase_id); + #endif + return false; + } + for(u32 i = 0; i lua_function; + THROW(*Preconditions()[i]); + + //tatarinrafa: added availability to return reversed precondition result; for script optimization + bool functor_exists; + if (strstr(*Preconditions()[i], ".ReturnReverse")){ + + xr_string preconditionScript = *Preconditions()[i]; + preconditionScript.erase(preconditionScript.size() - 14); + Msg("double param %s precondition Reversed ", preconditionScript.c_str()); + + functor_exists = ai().script_engine().functor(preconditionScript.c_str(), lua_function); + THROW3(functor_exists, "Cannot find phrase precondition", *Preconditions()[i]); + predicate_result = !lua_function(pSpeakerGO1->lua_game_object(), pSpeakerGO2->lua_game_object(), dialog_id, phrase_id, next_phrase_id); + } + else{ + functor_exists = ai().script_engine().functor(*Preconditions()[i], lua_function); + THROW3(functor_exists, "Cannot find phrase precondition", *Preconditions()[i]); + predicate_result = lua_function(pSpeakerGO1->lua_game_object(), pSpeakerGO2->lua_game_object(), dialog_id, phrase_id, next_phrase_id); + } + + if(!predicate_result) + { + #ifdef DEBUG + if (psAI_Flags.test(aiDialogs)) + Msg("dialog [%s] phrase[%s] rejected by script predicate",dialog_id,phrase_id); + #endif + break; + } + } + return predicate_result; +} + +void CPhraseScript::Action(const CGameObject* pSpeakerGO1, const CGameObject* pSpeakerGO2, LPCSTR dialog_id, LPCSTR phrase_id) const +{ + TransferInfo(smart_cast(pSpeakerGO1)); + + for(u32 i = 0; i lua_function; + THROW(*Actions()[i]); + bool functor_exists = ai().script_engine().functor(*Actions()[i] ,lua_function); + R_ASSERT2 (functor_exists, make_string("Cannot find script function [%s] in phrase dialog [%s][%s]", *Actions()[i], dialog_id, phrase_id)); + try { + lua_function (pSpeakerGO1->lua_game_object(), pSpeakerGO2->lua_game_object(), dialog_id, phrase_id); + } catch (...) { + } + } +} diff --git a/src/xrGameLA/PhraseScript.h b/src/xrGameLA/PhraseScript.h new file mode 100644 index 000000000..df2a99807 --- /dev/null +++ b/src/xrGameLA/PhraseScript.h @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////// +// PhraseScript.h +// классы для связи диалогов со скриптами +/////////////////////////////////////////////////////////////// + + +#include "InfoPortionDefs.h" + +#pragma once + +class CGameObject; +class CInventoryOwner; +class TiXmlNode; +class CUIXml; + +typedef TiXmlNode XML_NODE; + +class CPhraseScript +{ +public: + CPhraseScript (); + virtual ~CPhraseScript (); + + //загрузка из XML файла + virtual void Load (CUIXml* ui_xml, XML_NODE* phrase_node); + + //вызов с одним параметром (info_portion) + virtual bool Precondition (const CGameObject* pSpeaker, LPCSTR dialog_id, LPCSTR phrase_id) const; + virtual void Action (const CGameObject* pSpeaker, LPCSTR dialog_id, LPCSTR phrase_id) const; + //вызов с двумя параметрами (dialog, phrase) + virtual bool Precondition (const CGameObject* pSpeaker1, const CGameObject* pSpeaker2, LPCSTR dialog_id, LPCSTR phrase_id, LPCSTR next_phrase_id) const; + virtual void Action (const CGameObject* pSpeaker1, const CGameObject* pSpeaker2, LPCSTR dialog_id, LPCSTR phrase_id) const; + //текст из скриптовой функции +// virtual LPCSTR Text (LPCSTR original_text, const CGameObject* pSpeaker1, const CGameObject* pSpeaker2, LPCSTR dialog_id, int phrase_num) const; +// virtual bool HasText () const {return *m_sScriptTextFunc!=NULL;} + + + DEFINE_VECTOR (shared_str, PRECONDITION_VECTOR, PRECONDITION_VECTOR_IT); + virtual const PRECONDITION_VECTOR& Preconditions () const {return m_Preconditions;} + + DEFINE_VECTOR(shared_str, ACTION_NAME_VECTOR, ACTION_NAME_VECTOR_IT); + virtual const ACTION_NAME_VECTOR& Actions() const {return m_ScriptActions;} + + + void AddPrecondition (LPCSTR str); + void AddAction (LPCSTR str); + void AddHasInfo (LPCSTR str); + void AddDontHasInfo (LPCSTR str); + void AddGiveInfo (LPCSTR str); + void AddDisableInfo (LPCSTR str); +protected: + //загрузка содержания последовательности тагов в контейнер строк + template + void LoadSequence (CUIXml* ui_xml, XML_NODE* phrase_node, LPCSTR tag, T& str_vector); + + //манипуляции с информацией во время вызовов Precondition и Action + virtual bool CheckInfo (const CInventoryOwner* pOwner) const; + virtual void TransferInfo (const CInventoryOwner* pOwner) const; + + //имя скриптовой функции, которая возвращает какой-то текст +// shared_str m_sScriptTextFunc; + + //скриптовые действия, которые активируется после того как + //говорится фраза + DEFINE_VECTOR (shared_str, ACTION_NAME_VECTOR, ACTION_NAME_VECTOR_IT); + ACTION_NAME_VECTOR m_ScriptActions; + + DEFINE_VECTOR (shared_str, INFO_VECTOR, INFO_VECTOR_IT); + + INFO_VECTOR m_GiveInfo; + INFO_VECTOR m_DisableInfo; + + //список скриптовых предикатов, выполнение, которых необходимо + //для того чтоб фраза стала доступной + DEFINE_VECTOR (shared_str, PRECONDITION_VECTOR, PRECONDITION_VECTOR_IT); + + PRECONDITION_VECTOR m_Preconditions; + //проверка наличия/отсутствия информации + INFO_VECTOR m_HasInfo; + INFO_VECTOR m_DontHasInfo; +}; \ No newline at end of file diff --git a/src/xrGameLA/PhysicObject.cpp b/src/xrGameLA/PhysicObject.cpp new file mode 100644 index 000000000..4d766b247 --- /dev/null +++ b/src/xrGameLA/PhysicObject.cpp @@ -0,0 +1,319 @@ +#include "pch_script.h" +#include "physicobject.h" +#include "PhysicsShell.h" +#include "Physics.h" +#include "xrserver_objects_alife.h" +#include "Level.h" +#include "../Include/xrRender/Kinematics.h" +#include "../Include/xrRender/KinematicsAnimated.h" +#include "../xr_collide_form.h" +#include "game_object_space.h" + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + #include "PhysicsShellAnimator.h" +#endif + + +CPhysicObject::CPhysicObject(void) +{ + m_type = epotBox; + m_mass = 10.f; + m_collision_hit_callback= NULL; +} + +CPhysicObject::~CPhysicObject(void) +{ +} + +BOOL CPhysicObject::net_Spawn(CSE_Abstract* DC) +{ + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeObjectPhysic *po = smart_cast(e); + R_ASSERT (po); + m_type = EPOType(po->type); + m_mass = po->mass; + m_collision_hit_callback= NULL; + inherited::net_Spawn (DC); + xr_delete(collidable.model); + switch(m_type) { + case epotBox: + case epotFixedChain: + case epotFreeChain : + case epotSkeleton : collidable.model = new CCF_Skeleton(this); break; + + default: NODEFAULT; + } + + CPHSkeleton::Spawn(e); + setVisible(TRUE); + setEnabled(TRUE); + + if (!PPhysicsShell()->isBreakable()&&!CScriptBinder::object()&&!CPHSkeleton::IsRemoving()) + SheduleUnregister(); + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + if (PPhysicsShell()->Animated()) + { + processing_activate(); + } +#endif + + return TRUE; +} + +void CPhysicObject::SpawnInitPhysics (CSE_Abstract* D) +{ + CreatePhysicsShell(D); + RunStartupAnim(D); + +} +void CPhysicObject::RunStartupAnim(CSE_Abstract *D) +{ + if(Visual()&&smart_cast(Visual())) + { + // CSE_PHSkeleton *po = smart_cast(D); + IKinematicsAnimated* PKinematicsAnimated=NULL; + R_ASSERT (Visual()&&smart_cast(Visual())); + PKinematicsAnimated =smart_cast(Visual()); + if(PKinematicsAnimated) + { + CSE_Visual *visual = smart_cast(D); + R_ASSERT (visual); + R_ASSERT2 (*visual->startup_animation,"no startup animation"); + PKinematicsAnimated->PlayCycle(*visual->startup_animation); + } + smart_cast(Visual())->CalculateBones_Invalidate(); + smart_cast(Visual())->CalculateBones (TRUE); + + } +} +void CPhysicObject::net_Destroy() +{ +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + if (PPhysicsShell()->Animated()) + { + processing_deactivate(); + } +#endif + + inherited::net_Destroy (); + CPHSkeleton::RespawnInit(); +} + +void CPhysicObject::net_Save(NET_Packet& P) +{ + inherited::net_Save(P); + CPHSkeleton::SaveNetState(P); +} +void CPhysicObject::CreatePhysicsShell(CSE_Abstract* e) +{ + CSE_ALifeObjectPhysic *po = smart_cast(e); + CreateBody(po); +} + +void CPhysicObject::CreateSkeleton(CSE_ALifeObjectPhysic* po) +{ + if(m_pPhysicsShell) return; + if(!Visual()) return; + LPCSTR fixed_bones=*po->fixed_bones; + m_pPhysicsShell=P_build_Shell(this,!po->_flags.test(CSE_PHSkeleton::flActive),fixed_bones); + ApplySpawnIniToPhysicShell(&po->spawn_ini(),m_pPhysicsShell,fixed_bones[0]!='\0'); + ApplySpawnIniToPhysicShell(smart_cast(Visual())->LL_UserData(),m_pPhysicsShell,fixed_bones[0]!='\0'); +} + +void CPhysicObject::Load(LPCSTR section) +{ + inherited::Load(section); + CPHSkeleton::Load(section); +} + + +void CPhysicObject::shedule_Update (u32 dt) +{ + inherited::shedule_Update(dt); + CPHSkeleton::Update(dt); + +} +void CPhysicObject::UpdateCL() +{ + inherited::UpdateCL(); + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + //Если наш физический объект анимированный, то + //двигаем объект за анимацией + if (m_pPhysicsShell->PPhysicsShellAnimator()) + { + m_pPhysicsShell->PPhysicsShellAnimator()->OnFrame(); + } +#endif + + PHObjectPositionUpdate(); +} +void CPhysicObject::PHObjectPositionUpdate () +{ + + if(m_pPhysicsShell) + { + + + if(m_type==epotBox) + { + m_pPhysicsShell->Update(); + XFORM().set (m_pPhysicsShell->mXFORM); + } + else + m_pPhysicsShell->InterpolateGlobalTransform(&XFORM()); + } + + +} + +void CPhysicObject::AddElement(CPhysicsElement* root_e, int id) +{ + IKinematics* K = smart_cast(Visual()); + + CPhysicsElement* E = P_create_Element(); + CBoneInstance& B = K->LL_GetBoneInstance(u16(id)); + E->mXFORM.set (K->LL_GetTransform(u16(id))); + Fobb bb = K->LL_GetBox(u16(id)); + + + if(bb.m_halfsize.magnitude()<0.05f) + { + bb.m_halfsize.add(0.05f); + + } + E->add_Box (bb); + E->setMass (10.f); + E->set_ParentElement(root_e); + B.set_callback (bctPhysics,m_pPhysicsShell->GetBonesCallback(),E); + m_pPhysicsShell->add_Element (E); + if( !(m_type==epotFreeChain && root_e==0) ) + { + CPhysicsJoint* J= P_create_Joint(CPhysicsJoint::full_control,root_e,E); + J->SetAnchorVsSecondElement (0,0,0); + J->SetAxisDirVsSecondElement (1,0,0,0); + J->SetAxisDirVsSecondElement (0,1,0,2); + J->SetLimits (-M_PI/2,M_PI/2,0); + J->SetLimits (-M_PI/2,M_PI/2,1); + J->SetLimits (-M_PI/2,M_PI/2,2); + m_pPhysicsShell->add_Joint (J); + } + + CBoneData& BD = K->LL_GetData(u16(id)); + for (vecBonesIt it=BD.children.begin(); BD.children.end() != it; ++it){ + AddElement (E,(*it)->GetSelfID()); + } +} + + +void CPhysicObject::CreateBody(CSE_ALifeObjectPhysic* po) { + + if(m_pPhysicsShell) return; + IKinematics* pKinematics=smart_cast(Visual()); + switch(m_type) { + case epotBox : { + m_pPhysicsShell=P_build_SimpleShell(this,m_mass,!po->_flags.test(CSE_ALifeObjectPhysic::flActive)); + } break; + case epotFixedChain : + case epotFreeChain : + { + m_pPhysicsShell = P_create_Shell(); + m_pPhysicsShell->set_Kinematics(pKinematics); + AddElement(0,pKinematics->LL_GetBoneRoot()); + m_pPhysicsShell->setMass1(m_mass); + } break; + + case epotSkeleton: + { + //pKinematics->LL_SetBoneRoot(0); + CreateSkeleton(po); + }break; + + default : { + } break; + + } + + m_pPhysicsShell->mXFORM.set(XFORM()); + m_pPhysicsShell->SetAirResistance(0.001f, 0.02f); + if(pKinematics) + { + + SAllDDOParams disable_params; + disable_params.Load(pKinematics->LL_UserData()); + m_pPhysicsShell->set_DisableParams(disable_params); + } + //m_pPhysicsShell->SetAirResistance(0.002f, 0.3f); + + +} + + + + + + +BOOL CPhysicObject::net_SaveRelevant() +{ + return TRUE;//!m_flags.test(CSE_ALifeObjectPhysic::flSpawnCopy); +} + + +BOOL CPhysicObject::UsedAI_Locations() +{ + return (FALSE); +} + + + +void CPhysicObject::InitServerObject(CSE_Abstract * D) +{ + CPHSkeleton::InitServerObject(D); + CSE_ALifeObjectPhysic *l_tpALifePhysicObject = smart_cast(D); + if(!l_tpALifePhysicObject)return; + l_tpALifePhysicObject->type = u32(m_type); +} +SCollisionHitCallback* CPhysicObject:: get_collision_hit_callback () +{ + return m_collision_hit_callback; +} +bool CPhysicObject:: set_collision_hit_callback (SCollisionHitCallback *cc) +{ + if(!cc) + { + m_collision_hit_callback=NULL; + return true; + } + if(PPhysicsShell()) + { + VERIFY2(cc->m_collision_hit_callback!=0,"No callback function"); + m_collision_hit_callback=cc; + return true; + } + else return false; +} + +////////////////////////////////////////////////////////////////////////// +/* +DEFINE_MAP_PRED (LPCSTR, CPhysicsJoint*, JOINT_P_MAP, JOINT_P_PAIR_IT, pred_str); + +JOINT_P_MAP *l_tpJointMap = xr_new(); + +l_tpJointMap->insert(mk_pair(bone_name,joint*)); +JOINT_P_PAIR_IT I = l_tpJointMap->find(bone_name); +if (l_tpJointMap->end()!=I){ +//bone_name is found and is an pair_iterator +(*I).second +} + +JOINT_P_PAIR_IT I = l_tpJointMap->begin(); +JOINT_P_PAIR_IT E = l_tpJointMap->end(); +for ( ; I != E; ++I) { +(*I).second->joint_method(); +Msg("%s",(*I).first); +} + +*/ + +////////////////////////////////////////////////////////////////////////// diff --git a/src/xrGameLA/PhysicObject.h b/src/xrGameLA/PhysicObject.h new file mode 100644 index 000000000..b66f2b0a4 --- /dev/null +++ b/src/xrGameLA/PhysicObject.h @@ -0,0 +1,54 @@ +#pragma once + +#include "gameobject.h" +#include "physicsshellholder.h" +#include "physicsskeletonobject.h" +#include "PHSkeleton.h" +#include "script_export_space.h" + +class CSE_ALifeObjectPhysic; +class CPhysicsElement; + +class CPhysicObject : + public CPhysicsShellHolder, + public CPHSkeleton +{ + typedef CPhysicsShellHolder inherited; + EPOType m_type ; + float m_mass ; + SCollisionHitCallback *m_collision_hit_callback; +private: + //Creating + void CreateBody (CSE_ALifeObjectPhysic *po) ; + void CreateSkeleton (CSE_ALifeObjectPhysic *po) ; + void AddElement (CPhysicsElement* root_e, int id) ; + + +public: + CPhysicObject(void); + virtual ~CPhysicObject(void); + + virtual BOOL net_Spawn ( CSE_Abstract* DC) ; + virtual void CreatePhysicsShell (CSE_Abstract* e) ; + virtual void net_Destroy () ; + virtual void Load (LPCSTR section) ; + virtual void shedule_Update (u32 dt) ; // + virtual void UpdateCL () ; + virtual void net_Save (NET_Packet& P) ; + virtual BOOL net_SaveRelevant () ; + virtual BOOL UsedAI_Locations () ; + virtual SCollisionHitCallback *get_collision_hit_callback () ; + virtual bool set_collision_hit_callback (SCollisionHitCallback *cc) ; +protected: + virtual void SpawnInitPhysics (CSE_Abstract *D) ; + virtual void RunStartupAnim (CSE_Abstract *D) ; + virtual CPhysicsShellHolder *PPhysicsShellHolder () {return PhysicsShellHolder();} + virtual CPHSkeleton *PHSkeleton () {return this;} + virtual void InitServerObject (CSE_Abstract *po) ; + virtual void PHObjectPositionUpdate () ; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CPhysicObject) +#undef script_type_list +#define script_type_list save_type_list(CPhysicObject) diff --git a/src/xrGameLA/PhysicObject_script.cpp b/src/xrGameLA/PhysicObject_script.cpp new file mode 100644 index 000000000..1d478c5f8 --- /dev/null +++ b/src/xrGameLA/PhysicObject_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "PhysicObject.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CPhysicObject::script_register(lua_State *L) +{ + module(L) + [ + class_("CPhysicObject") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/Physics.cpp b/src/xrGameLA/Physics.cpp new file mode 100644 index 000000000..a391b6ebc --- /dev/null +++ b/src/xrGameLA/Physics.cpp @@ -0,0 +1,544 @@ +#include "StdAfx.h" +#include "PHDynamicData.h" +#include "Physics.h" +#include "tri-colliderknoopc/dTriList.h" +#include "PHContactBodyEffector.h" +#include "../gamemtllib.h" +#include "gameobject.h" +#include "PhysicsShellHolder.h" +#include "PHCollideValidator.h" +#ifdef DEBUG +#include "PHDebug.h" +#endif +/////////////////////////////////////////////////////////////// +#pragma warning(disable:4995) +#pragma warning(disable:4267) +#include "../../xrODE/ode/src/collision_kernel.h" +#include "../../xrODE/ode/src/joint.h" +#include "../../xrODE/ode/src/objects.h" +#pragma warning(default:4267) +#pragma warning(default:4995) + +extern CPHWorld *ph_world; +/////////////////////////////////////////////////////////////////// + +#include "ExtendedGeom.h" +//union dInfBytes dInfinityValue = {{0,0,0x80,0x7f}}; +PhysicsStepTimeCallback *physics_step_time_callback = 0; + +const float default_w_limit = 9.8174770f;//(M_PI/16.f/(fixed_step=0.02f)); +const float default_l_limit = 150.f;//(3.f/fixed_step=0.02f); +const float default_l_scale = 1.01f; +const float default_w_scale = 1.01f; +const float default_k_l = 0.0002f;//square resistance !! +const float default_k_w = 0.05f; + +const float mass_limit = 10000.f;//some conventional value used as evaluative param (there is no code restriction on mass) +extern const u16 max_joint_allowed_for_exeact_integration = 30; + +//base params +const float base_fixed_step = 0.02f ; +const float base_erp = 0.54545456f ; +const float base_cfm = 1.1363636e-006f ; +//base params +float fixed_step = 0.01f; +float world_cfm = CFM(SPRING_S(base_cfm,base_erp,base_fixed_step),DAMPING(base_cfm,base_erp)); +float world_erp = ERP(SPRING_S(base_cfm,base_erp,base_fixed_step),DAMPING(base_cfm,base_erp)); +float world_spring = 1.0f*SPRING (world_cfm,world_erp); +float world_damping = 1.0f*DAMPING(world_cfm,world_erp); + + +const float default_world_gravity = 2*9.81f; + + +///////////////////////////////////////////////////// + +int phIterations = 18; +float phTimefactor = 1.f; +float phBreakCommonFactor = 0.01f; +float phRigidBreakWeaponFactor = 1.f; +Fbox phBoundaries = {1000.f,1000.f,-1000.f,-1000.f}; +float ph_tri_query_ex_aabb_rate = 1.3f; +int ph_tri_clear_disable_count = 10; +dWorldID phWorld; + +///////////////////////////////////// +dJointGroupID ContactGroup; +CBlockAllocator ContactFeedBacks; +CBlockAllocator ContactEffectors; + +/////////////////////////////////////////////////////////// +class SApplyBodyEffectorPred +{ + +public: + SApplyBodyEffectorPred() + { + } + + void operator ()(CPHContactBodyEffector* pointer) const + { + pointer->Apply(); + } +}; +///////////////////////////////////////////////////////////////////////////// +IC void add_contact_body_effector(dBodyID body,const dContact& c,SGameMtl* material) +{ + CPHContactBodyEffector* effector=(CPHContactBodyEffector*)dBodyGetData(body); + if(effector) + effector->Merge(c,material); + else + { + effector=ContactEffectors.add(); + effector->Init(body,c,material); + dBodySetData(body,(void*)effector); + } +} + + + + + + + + +IC static int CollideIntoGroup(dGeomID o1, dGeomID o2,dJointGroupID jointGroup,CPHIsland* world,const int &MAX_CONTACTS) +{ + const int RS= 800+10; + const int N = RS; + + static dContact contacts[RS]; + int collided_contacts=0; + // get the contacts up to a maximum of N contacts + int n; + + VERIFY (o1); + VERIFY (o2); + VERIFY(&contacts[0].geom); + n = dCollide(o1, o2, N, &contacts[0].geom, sizeof(dContact)); + + if(n>N-1) + n=N-1; + int i; + + + for(i = 0; i < n; ++i) + { + dContact &c =contacts[i]; + dContactGeom &cgeom =c.geom; + dSurfaceParameters &surface=c.surface; + dGeomID g1 =cgeom.g1; + dGeomID g2 =cgeom.g2; + bool pushing_neg= false; + bool do_collide = true; + dxGeomUserData* usr_data_1 =NULL; + dxGeomUserData* usr_data_2 =NULL; + u16 material_idx_1 =0; + u16 material_idx_2 =0; + + surface.mu =1.f;// 5000.f; + surface.soft_erp=1.f;//ERP(world_spring,world_damping); + surface.soft_cfm=1.f;//CFM(world_spring,world_damping); + surface.bounce = 0.01f;//0.1f; + surface.bounce_vel =1.5f;//0.005f; + usr_data_1 = retrieveGeomUserData(g1); + usr_data_2 = retrieveGeomUserData(g2); + /////////////////////////////////////////////////////////////////////////////////////////////////// + if(usr_data_2) material_idx_2=usr_data_2->material; + if(usr_data_1) material_idx_1=usr_data_1->material; + bool is_tri_1=dTriListClass == dGeomGetClass(g1); + bool is_tri_2=dTriListClass == dGeomGetClass(g2); + if(!is_tri_2&&!is_tri_1) surface.mode=0; + if(is_tri_1) material_idx_1=(u16)surface.mode; + if(is_tri_2) material_idx_2=(u16)surface.mode; + SGameMtl* material_1=GMLib.GetMaterialByIdx(material_idx_1); + SGameMtl* material_2=GMLib.GetMaterialByIdx(material_idx_2); + ////////////////params can be changed in callbacks////////////////////////////////////////////////////////////////////////// + surface.mode =dContactApprox1|dContactSoftERP|dContactSoftCFM; + float spring =material_2->fPHSpring*material_1->fPHSpring*world_spring; + float damping =material_2->fPHDamping*material_1->fPHDamping*world_damping; + surface.soft_erp=ERP(spring,damping); + surface.soft_cfm=CFM(spring,damping); + surface.mu=material_2->fPHFriction*material_1->fPHFriction; + ///////////////////////////////////////////////////////////////////////////////////////////////// + + + Flags32 &flags_1=material_1->Flags; + Flags32 &flags_2=material_2->Flags; + + if(is_tri_1) + { +#pragma warning(push) +#pragma warning(disable:4245) + if(material_1->Flags.test(SGameMtl::flSlowDown)&&!(usr_data_2->pushing_neg||usr_data_2->pushing_b_neg)) +#pragma warning(pop) + { + dBodyID body=dGeomGetBody(g2); + R_ASSERT2(body,"static - static collision !!!"); + if(material_1->Flags.test(SGameMtl::flLiquid)) + { + add_contact_body_effector(body,c,material_1); + } + else + { + if(!usr_data_2 || !usr_data_2->ph_object || !usr_data_2->ph_object->IsRayMotion()) + { + add_contact_body_effector(body,c,material_1); + } + } + + } + if(material_1->Flags.test(SGameMtl::flPassable)) + do_collide=false; + // if(material_2->Flags.is(SGameMtl::flClimable)) + // do_collide=false; + } + if(is_tri_2) + { +#pragma warning(push) +#pragma warning(disable:4245) + if(material_2->Flags.test(SGameMtl::flSlowDown)&&!(usr_data_1->pushing_neg||usr_data_1->pushing_b_neg)) +#pragma warning(pop) + { + + dBodyID body=dGeomGetBody(g1); + R_ASSERT2(body,"static - static collision !!!"); + if(material_2->Flags.test(SGameMtl::flLiquid)) + { + add_contact_body_effector(body,c,material_2); + } + else + { + if(!usr_data_1 || !usr_data_1->ph_object || !usr_data_1->ph_object->IsRayMotion()) + { + add_contact_body_effector(body,c,material_2); + } + } + + } + if(material_2->Flags.test(SGameMtl::flPassable)) + do_collide=false; + + } + + if(flags_1.test(SGameMtl::flBounceable)&&flags_2.test(SGameMtl::flBounceable)) + { + surface.mode |= dContactBounce; + surface.bounce_vel = _max(material_1->fPHBounceStartVelocity,material_2->fPHBounceStartVelocity); + surface.bounce = _min(material_1->fPHBouncing,material_2->fPHBouncing); + } + ///////////////////////////////////////////////////////////////////////////////////////////////// + if(usr_data_2&&usr_data_2->object_callbacks){ + usr_data_2->object_callbacks->Call(do_collide,false,c,material_1,material_2); + } + + if(usr_data_1&&usr_data_1->object_callbacks){ + usr_data_1->object_callbacks->Call(do_collide,true,c,material_1,material_2); + } + + if(usr_data_2){ + usr_data_2->pushing_b_neg = usr_data_2->pushing_b_neg && !GMLib.GetMaterialByIdx(usr_data_2->b_neg_tri->material)->Flags.test(SGameMtl::flPassable); + usr_data_2->pushing_neg = usr_data_2->pushing_neg && !GMLib.GetMaterialByIdx(usr_data_2->neg_tri->material)->Flags.test(SGameMtl::flPassable); + pushing_neg=usr_data_2->pushing_b_neg||usr_data_2->pushing_neg; + if(usr_data_2->ph_object){ + usr_data_2->ph_object->InitContact(&c,do_collide,material_idx_1,material_idx_2); + } + + } + /////////////////////////////////////////////////////////////////////////////////////// + if(usr_data_1){ + usr_data_1->pushing_b_neg = usr_data_1->pushing_b_neg && !GMLib.GetMaterialByIdx(usr_data_1->b_neg_tri->material)->Flags.test(SGameMtl::flPassable); + usr_data_1->pushing_neg = usr_data_1->pushing_neg && !GMLib.GetMaterialByIdx(usr_data_1->neg_tri->material)->Flags.test(SGameMtl::flPassable); + pushing_neg=usr_data_1->pushing_b_neg||usr_data_1->pushing_neg; + if(usr_data_1->ph_object){ + usr_data_1->ph_object->InitContact(&c,do_collide,material_idx_1,material_idx_2); + + } + } + + + if (pushing_neg) + surface.mu=dInfinity; + if (do_collide && collided_contactsConnectJoint(contact_joint); + dJointAttach (contact_joint, dGeomGetBody(g1), dGeomGetBody(g2)); + } + } + return collided_contacts; +} +void NearCallback(CPHObject* obj1,CPHObject* obj2, dGeomID o1, dGeomID o2) +{ + if (obj2 == NULL) + { + Msg("! Skipping physic callback for removed second object"); + return; + } + + CPHIsland* island1=obj1->DActiveIsland(); + CPHIsland* island2=obj2->DActiveIsland(); + obj2->near_callback(obj1); + int MAX_CONTACTS=-1; + if(!island1->CanMerge(island2,MAX_CONTACTS)) return; + if(CollideIntoGroup(o1,o2,ContactGroup,island1,MAX_CONTACTS)!=0) + { + obj1->MergeIsland(obj2); + if(!obj2->is_active())obj2->EnableObject(obj1); + } +} +void CollideStatic(dGeomID o2,CPHObject* obj2) +{ + + CPHIsland* island2=obj2->DActiveIsland(); + CollideIntoGroup(ph_world->GetMeshGeom(),o2,ContactGroup,island2,island2->MaxJoints()); +} + + + + +//half square time + +/* +void BodyCutForce(dBodyID body) +{ +dReal linear_limit=l_limit; +//applyed force +const dReal* force= dBodyGetForce(body); + +//body mass +dMass m; +dBodyGetMass(body,&m); + +//accceleration correspondent to the force +dVector3 a={force[0]/m.mass,force[1]/m.mass,force[2]/m.mass}; + +//current velocity +const dReal* start_vel=dBodyGetLinearVel(body); + +//velocity adding during one step +dVector3 add_vel={a[0]*fixed_step,a[1]*fixed_step,a[2]*fixed_step}; + +//result velocity +dVector3 vel={start_vel[0]+add_vel[0],start_vel[1]+add_vel[1],start_vel[2]+add_vel[2]}; + +//result velocity magnitude +dReal speed=dSqrt(dDOT(vel,vel)); + +if(speed>linear_limit) //then we need to cut applied force +{ +//solve the triangle - cutted velocity - current veocity - add velocity +//to find cutted adding velocity + +//add_vell magnitude +dReal add_speed=dSqrt(dDOT(add_vel,add_vel)); + +//current velocity magnitude +dReal start_speed=dSqrt(dDOT(start_vel,start_vel)); + +//cosinus of the angle between vel ang add_vell +dReal cosinus1=dFabs(dDOT(add_vel,start_vel)/start_speed/add_speed); + +dReal cosinus1_2=cosinus1*cosinus1; +if(cosinus1_2>1.f) +cosinus1_2=1.f; +dReal cutted_add_speed; +if(cosinus1_2==1.f) +cutted_add_speed=linear_limit-start_speed; +else +{ +//sinus +dReal sinus1_2=1.f-cosinus1_2; + + +dReal sinus1=dSqrt(sinus1_2); + + +//sinus of the angle between cutted velocity and adding velociity (sinus theorem) +dReal sinus2=sinus1/linear_limit*start_speed; +dReal sinus2_2=sinus2*sinus2; +if(sinus2_2>1.f)sinus2_2=1.f; + +dReal cosinus2_2=1.f-sinus2_2; +dReal cosinus2=dSqrt(cosinus2_2); + +//sinus of 180-ang1-ang2 +dReal sinus3=sinus1*cosinus2+cosinus1*sinus2; + +//cutted adding velocity magnitude (sinus theorem) + +cutted_add_speed=linear_limit/sinus1*sinus3; +} +//substitude force + +dBodyAddForce(body, +cutted_add_speed/add_speed/fixed_step*m.mass*add_vel[0]-force[0], +cutted_add_speed/add_speed/fixed_step*m.mass*add_vel[1]-force[1], +cutted_add_speed/add_speed/fixed_step*m.mass*add_vel[2]-force[2] +); +} + +} +*/ +//limit for angular accel +void dBodyAngAccelFromTorqu(const dBodyID body, dReal* ang_accel, const dReal* torque){ + dMass m; + dMatrix3 invI; + dBodyGetMass(body,&m); + dInvertPDMatrix (m.I, invI, 3); + dMULTIPLY1_333(ang_accel,invI, torque); +} +void FixBody(dBodyID body,float ext_param,float mass_param) +{ + dMass m; + dMassSetSphere(&m,1.f,ext_param); + dMassAdjust(&m,mass_param); + dBodySetMass(body,&m); + dBodySetGravityMode(body,0); + dBodySetLinearVel(body,0,0,0); + dBodySetAngularVel(body,0,0,0); + dBodySetForce(body,0,0,0); + dBodySetTorque(body,0,0,0); +} +void FixBody(dBodyID body) +{ + FixBody(body,fix_ext_param,fix_mass_param); +} + + +void BodyCutForce(dBodyID body,float l_limit,float w_limit) +{ + const dReal wa_limit=w_limit/fixed_step; + const dReal* force= dBodyGetForce(body); + dReal force_mag=dSqrt(dDOT(force,force)); + + //body mass + dMass m; + dBodyGetMass(body,&m); + + dReal force_limit =l_limit/fixed_step*m.mass; + + if(force_mag>force_limit) + { + dBodySetForce(body, + force[0]/force_mag*force_limit, + force[1]/force_mag*force_limit, + force[2]/force_mag*force_limit + ); + } + + + const dReal* torque=dBodyGetTorque(body); + dReal torque_mag=dSqrt(dDOT(torque,torque)); + + if(torque_mag<0.001f) return; + + dMatrix3 tmp,invI,I; + + // compute inertia tensor in global frame + dMULTIPLY2_333 (tmp,m.I,body->R); + dMULTIPLY0_333 (I,body->R,tmp); + + // compute inverse inertia tensor in global frame + dMULTIPLY2_333 (tmp,body->invI,body->R); + dMULTIPLY0_333 (invI,body->R,tmp); + + //angular accel + dVector3 wa; + dMULTIPLY0_331(wa,invI,torque); + dReal wa_mag=dSqrt(dDOT(wa,wa)); + + if(wa_mag>wa_limit) + { + //scale w + for(int i=0;i<3;++i)wa[i]*=wa_limit/wa_mag; + dVector3 new_torqu; + + dMULTIPLY0_331(new_torqu,I,wa); + + dBodySetTorque + ( + body, + new_torqu[0], + new_torqu[1], + new_torqu[2] + ); + } +} + +void dMassSub(dMass *a,const dMass *b) +{ + int i; + VERIFY (a && b); + dReal denom = dRecip (a->mass-b->mass); + for (i=0; i<3; ++i) a->c[i] = (a->c[i]*a->mass - b->c[i]*b->mass)*denom; + a->mass-=b->mass; + for (i=0; i<12; ++i) a->I[i] -= b->I[i]; +} + + +////Energy of non Elastic collision; +//body - static case +float E_NlS(dBodyID body,const dReal* norm,float norm_sign)//if body c.geom.g1 norm_sign + else - +{ //norm*norm_sign - to body + const dReal* vel=dBodyGetLinearVel(body); + dReal prg=-dDOT(vel,norm)*norm_sign; + prg=prg<0.f ? prg=0.f : prg; + dMass mass; + dBodyGetMass(body,&mass); + return mass.mass*prg*prg/2; +} + +//body - body case +float E_NLD(dBodyID b1,dBodyID b2,const dReal* norm)// norm - from 2 to 1 +{ + dMass m1,m2; + dBodyGetMass(b1,&m1);dBodyGetMass(b2,&m2); + const dReal* vel1 =dBodyGetLinearVel(b1); + const dReal* vel2 =dBodyGetLinearVel(b2); + + dReal vel_pr1=dDOT(vel1,norm); + dReal vel_pr2=dDOT(vel2,norm); + + if(vel_pr1>vel_pr2) return 0.f; //exit if the bodies are departing + + dVector3 impuls1={vel1[0]*m1.mass,vel1[1]*m1.mass,vel1[2]*m1.mass}; + dVector3 impuls2={vel2[0]*m2.mass,vel2[1]*m2.mass,vel2[2]*m2.mass}; + + dVector3 c_mas_impuls={impuls1[0]+impuls2[0],impuls1[1]+impuls2[1],impuls1[2]+impuls2[2]}; + dReal cmass=m1.mass+m2.mass; + dVector3 c_mass_vel={c_mas_impuls[0]/cmass,c_mas_impuls[1]/cmass,c_mas_impuls[2]/cmass}; + + dReal c_mass_vel_prg=dDOT(c_mass_vel,norm); + + dReal kin_energy_start=vel_pr1*vel_pr1*m1.mass/2.f+vel_pr2*vel_pr2*m2.mass/2.f; + dReal kin_energy_end=c_mass_vel_prg*c_mass_vel_prg*cmass/2.f; + + return (kin_energy_start-kin_energy_end); +} +float E_NL(dBodyID b1,dBodyID b2,const dReal* norm) +{ + VERIFY(b1||b2); + if(b1) + { + if(b2)return E_NLD(b1,b2,norm);else return E_NlS(b1,norm,1); + }else return E_NlS(b2,norm,-1); +} +void ApplyGravityAccel(dBodyID body,const dReal* accel) +{ + dMass m; + dBodyGetMass(body,&m); + dBodyAddForce(body,accel[0]*m.mass,accel[1]*m.mass,accel[2]*m.mass); +} + +const dReal* dJointGetPositionContact(dJointID joint) +{ + VERIFY2(dJointGetType(joint)==dJointTypeContact,"not a contact!"); + dxJointContact* c_joint=(dxJointContact*)joint; + return c_joint->contact.geom.pos; + +} diff --git a/src/xrGameLA/Physics.h b/src/xrGameLA/Physics.h new file mode 100644 index 000000000..94b5b7d85 --- /dev/null +++ b/src/xrGameLA/Physics.h @@ -0,0 +1,52 @@ +#ifndef PHYSICS_H +#define PHYSICS_H + + +#include "dCylinder/dCylinder.h" +#include "PhysicsShell.h" +#include "PHObject.h" +#include "PHInterpolation.h" +#include "../xrcore/_cylinder.h" +#include "BlockAllocator.h" +#include "PhysicsCommon.h" +#include "PHWorld.h" +#include "PHContactBodyEffector.h" +#include "phvalide.h" +//#define ODE_SLOW_SOLVER +/////////////////////////////////////////////////////////////////////////////// + + void BodyCutForce (dBodyID body,float l_limit,float w_limit) ; + void dBodyAngAccelFromTorqu (const dBodyID body, dReal* ang_accel, const dReal* torque) ; + float E_NlS (dBodyID body,const dReal* norm,float norm_sign) ; + float E_NLD (dBodyID b1,dBodyID b2,const dReal* norm); + float E_NL (dBodyID b1,dBodyID b2,const dReal* norm); + void ApplyGravityAccel (dBodyID body,const dReal* accel); +const dReal fix_ext_param =10000.f; +const dReal fix_mass_param =100000000.f; + void FixBody (dBodyID body) ; + void dMassSub (dMass *a,const dMass *b) ; + void SaveContacts (dGeomID o1, dGeomID o2,dJointGroupID jointGroup) ; +const dReal *dJointGetPositionContact (dJointID joint); + + + +//const dReal world_spring=24000000.f;//2400000.f;//550000.f;///1000000.f;; +//const dReal world_damping=400000.f;//erp/cfm1.1363636e-006f,0.54545456f + + +extern class CBlockAllocator ContactFeedBacks; +extern CBlockAllocator ContactEffectors; +//void NearCallback(void* /*data*/, dGeomID o1, dGeomID o2); +void NearCallback(CPHObject* obj1,CPHObject* obj2, dGeomID o1, dGeomID o2); +void CollideStatic(dGeomID o2,CPHObject* obj2); + + + +class CPHElement; +class CPHShell; +extern dJointGroupID ContactGroup; +extern Fbox phBoundaries; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif PHYSICS_H \ No newline at end of file diff --git a/src/xrGameLA/PhysicsCommon.h b/src/xrGameLA/PhysicsCommon.h new file mode 100644 index 000000000..9ae347348 --- /dev/null +++ b/src/xrGameLA/PhysicsCommon.h @@ -0,0 +1,71 @@ +#ifndef PHYSICS_COMMON_H +#define PHYSICS_COMMON_H + +#include "DisablingParams.h" +#include "ode_include.h" + +extern const float default_l_limit ; +extern const float default_w_limit ; +extern const float default_k_l ; +extern const float default_k_w ; +extern const float default_l_scale ; +extern const float default_w_scale ; + +extern const float base_fixed_step ; +extern const float base_erp ; +extern const float base_cfm ; + +extern float fixed_step ; +extern float world_cfm ; +extern float world_erp ; +extern float world_spring ; +extern float world_damping ; + +extern const float mass_limit ; +extern const u16 max_joint_allowed_for_exeact_integration ; +extern const float default_world_gravity ; +extern float phTimefactor ; +extern int phIterations ; +extern float phBreakCommonFactor ; +extern float phRigidBreakWeaponFactor ; +extern float ph_tri_query_ex_aabb_rate ; +extern int ph_tri_clear_disable_count ; + +struct SGameMtl; +#define ERP_S(k_p,k_d,s) ((s*(k_p)) / (((s)*(k_p)) + (k_d))) +#define CFM_S(k_p,k_d,s) (1.f / (((s)*(k_p)) + (k_d))) +#define SPRING_S(cfm,erp,s) ((erp)/(cfm)/s) + +////////////////////////////////////////////////////////////////////////////////// +#define DAMPING(cfm,erp) ((1.f-(erp))/(cfm)) +#define ERP(k_p,k_d) ERP_S(k_p,k_d,fixed_step) +#define CFM(k_p,k_d) CFM_S(k_p,k_d,fixed_step) +#define SPRING(cfm,erp) SPRING_S(cfm,erp,fixed_step) + + + + +IC float Erp(float k_p,float k_d,float s=fixed_step) {return ((s*(k_p)) / (((s)*(k_p)) + (k_d)));} +IC float Cfm(float k_p,float k_d,float s=fixed_step) {return (1.f / (((s)*(k_p)) + (k_d)));} +IC float Spring(float cfm,float erp,float s=fixed_step) {return ((erp)/(cfm)/s);} +IC float Damping(float cfm,float erp) {return ((1.f-(erp))/(cfm));} +IC void MulSprDmp(float &cfm,float &erp,float mul_spring,float mul_damping) +{ + float factor=1.f/(mul_spring*erp+mul_damping*(1-erp)); + cfm*=factor; + erp*=(factor*mul_spring); +} +typedef void ContactCallbackFun(CDB::TRI* T,dContactGeom* c); +typedef void ObjectContactCallbackFun(bool& do_colide,bool bo1,dContact& c,SGameMtl* material_1,SGameMtl* material_2); + + +typedef void __stdcall BoneCallbackFun(CBoneInstance* B); + + + +extern ContactCallbackFun *ContactShotMark; +extern ContactCallbackFun *CharacterContactShotMark; + +typedef void PhysicsStepTimeCallback (u32 step_start,u32 step_end); +extern PhysicsStepTimeCallback *physics_step_time_callback; +#endif //PHYSICS_COMMON_H \ No newline at end of file diff --git a/src/xrGameLA/PhysicsGamePars.cpp b/src/xrGameLA/PhysicsGamePars.cpp new file mode 100644 index 000000000..a3e86ebcc --- /dev/null +++ b/src/xrGameLA/PhysicsGamePars.cpp @@ -0,0 +1,27 @@ +#include "stdafx.h" +#include "PhysicsGamePars.h" +float object_damage_factor = 1.f ; //times increace damage from object collision +float collide_volume_max = 200.f ; //max collide sound level +float collide_volume_min = 0.f ; //min collide sound level + +//float vel_cret_sound = 10.f ; //min vel_cret for collide sound +//float vel_cret_wallmark = 30.f ; //min vel_cret for wallmark +//float vel_cret_particles = 15.f ; //................... + +const float EffectPars::vel_cret_sound=10.f; +const float EffectPars::vel_cret_particles=15.f; +const float EffectPars::vel_cret_wallmark=30.f; + + + +const float CharacterEffectPars::vel_cret_sound=20.f; +const float CharacterEffectPars::vel_cret_particles=60.f; +const float CharacterEffectPars::vel_cret_wallmark=100.f; + +void LoadPhysicsGameParams() +{ + collide_volume_min=pSettings->r_float("sound","snd_collide_min_volume"); + collide_volume_max=pSettings->r_float("sound","snd_collide_max_volume"); + object_damage_factor=pSettings->r_float("physics","object_damage_factor"); + object_damage_factor*=object_damage_factor; +} \ No newline at end of file diff --git a/src/xrGameLA/PhysicsGamePars.h b/src/xrGameLA/PhysicsGamePars.h new file mode 100644 index 000000000..a1ffdaa94 --- /dev/null +++ b/src/xrGameLA/PhysicsGamePars.h @@ -0,0 +1,23 @@ +#ifndef PHYSICS_GAME_PARS_H +#define PHYSICS_GAME_PARS_H +extern float object_damage_factor; +extern float collide_volume_max; +extern float collide_volume_min; + + +struct EffectPars +{ + const static float vel_cret_sound; + const static float vel_cret_particles; + const static float vel_cret_wallmark; +}; + +struct CharacterEffectPars +{ + const static float vel_cret_sound; + const static float vel_cret_particles; + const static float vel_cret_wallmark; +}; + +void LoadPhysicsGameParams () ; +#endif \ No newline at end of file diff --git a/src/xrGameLA/PhysicsShell.cpp b/src/xrGameLA/PhysicsShell.cpp new file mode 100644 index 000000000..e54c747c5 --- /dev/null +++ b/src/xrGameLA/PhysicsShell.cpp @@ -0,0 +1,245 @@ +#include "stdafx.h" +#pragma hdrstop +#include "physicsshell.h" +#include "PHDynamicData.h" +#include "Physics.h" +#include "PHJoint.h" +#include "PHShell.h" +#include "PHJoint.h" +#include "PHJointDestroyInfo.h" +#include "PHSplitedShell.h" +#include "gameobject.h" +#include "physicsshellholder.h" + +#include "../Include/xrRender/Kinematics.h" +#include "../xr_object.h" +#include "../bone.h" +extern CPHWorld *ph_world; +CPhysicsShell::~CPhysicsShell() +{ + if(ph_world)ph_world->NetRelcase(this); +} + +CPhysicsElement* P_create_Element () +{ + CPHElement* element=new CPHElement(); + return element; +} + +CPhysicsShell* P_create_Shell () +{ + CPhysicsShell* shell=new CPHShell(); + return shell; +} + +CPhysicsShell* P_create_splited_Shell () +{ + CPhysicsShell* shell=new CPHSplitedShell(); + return shell; +} + +CPhysicsJoint* P_create_Joint (CPhysicsJoint::enumType type ,CPhysicsElement* first,CPhysicsElement* second) +{ + CPhysicsJoint* joint=new CPHJoint( type , first, second); + return joint; +} + + +CPhysicsShell* P_build_Shell (CGameObject* obj,bool not_active_state,BONE_P_MAP* bone_map) +{ + IKinematics* pKinematics=smart_cast(obj->Visual()); + + CPhysicsShell* pPhysicsShell = P_create_Shell(); + + pPhysicsShell->build_FromKinematics(pKinematics,bone_map); + + pPhysicsShell->set_PhysicsRefObject(smart_cast(obj)); + pPhysicsShell->mXFORM.set(obj->XFORM()); + pPhysicsShell->Activate(not_active_state);//, + //m_pPhysicsShell->SmoothElementsInertia(0.3f); + pPhysicsShell->SetAirResistance();//0.0014f,1.5f + + return pPhysicsShell; +} + +void fix_bones(LPCSTR fixed_bones,CPhysicsShell* shell ) +{ + VERIFY(fixed_bones); + VERIFY(shell); + IKinematics *pKinematics = shell->PKinematics(); + VERIFY(pKinematics); + int count = _GetItemCount(fixed_bones); + for (int i=0 ;iLL_BoneID(fixed_bone) ; + R_ASSERT2(BI_NONE!=fixed_bone_id,"wrong fixed bone") ; + CPhysicsElement* E = shell->get_Element(fixed_bone_id) ; + if(E) + E->Fix(); + } +} +CPhysicsShell* P_build_Shell (CGameObject* obj,bool not_active_state,BONE_P_MAP* p_bone_map,LPCSTR fixed_bones) +{ + CPhysicsShell* pPhysicsShell; + IKinematics* pKinematics=smart_cast(obj->Visual()); + if(fixed_bones) + { + + + int count = _GetItemCount(fixed_bones); + for (int i=0 ;iLL_BoneID(fixed_bone) ; + R_ASSERT2(BI_NONE!=fixed_bone_id,make_string("[%s] wrong fixed bone [%d]", obj->Name(), fixed_bone_id)); + p_bone_map->insert(mk_pair(fixed_bone_id,physicsBone())) ; + } + + pPhysicsShell=P_build_Shell(obj,not_active_state,p_bone_map); + + //m_pPhysicsShell->add_Joint(P_create_Joint(CPhysicsJoint::enumType::full_control,0,fixed_element)); + } + else + pPhysicsShell=P_build_Shell(obj,not_active_state); + + + BONE_P_PAIR_IT i=p_bone_map->begin(),e=p_bone_map->end(); + if(i!=e) pPhysicsShell->SetPrefereExactIntegration(); + for(;i!=e;i++) + { + CPhysicsElement* fixed_element=i->second.element; + R_ASSERT2(fixed_element,"fixed bone has no physics"); + //if(!fixed_element) continue; + fixed_element->Fix(); + } + return pPhysicsShell; +} + +CPhysicsShell* P_build_Shell (CGameObject* obj,bool not_active_state,LPCSTR fixed_bones) +{ + U16Vec f_bones; + if(fixed_bones){ + IKinematics* K = smart_cast(obj->Visual()); + int count = _GetItemCount(fixed_bones); + for (int i=0 ;iLL_BoneID(fixed_bone)); + R_ASSERT2(BI_NONE!=f_bones.back(),make_string("[%s] wrong fixed bone %d", obj->cName().c_str(), f_bones.back())); + } + } + return P_build_Shell (obj,not_active_state,f_bones); +} + +static BONE_P_MAP bone_map=BONE_P_MAP(); +CPhysicsShell* P_build_Shell (CGameObject* obj,bool not_active_state,U16Vec& fixed_bones) +{ + bone_map.clear (); + CPhysicsShell* pPhysicsShell; + if(!fixed_bones.empty()) + for (U16It it=fixed_bones.begin(); it!=fixed_bones.end(); it++) + bone_map.insert(mk_pair(*it,physicsBone())); + pPhysicsShell=P_build_Shell(obj,not_active_state,&bone_map); + + // fix bones + BONE_P_PAIR_IT i=bone_map.begin(),e=bone_map.end(); + if(i!=e) pPhysicsShell->SetPrefereExactIntegration(); + for(;i!=e;i++){ + CPhysicsElement* fixed_element=i->second.element; + //R_ASSERT2(fixed_element,"fixed bone has no physics"); + if(!fixed_element) continue; + fixed_element->Fix(); + } + return pPhysicsShell; +} + +CPhysicsShell* P_build_SimpleShell(CGameObject* obj,float mass,bool not_active_state) +{ + CPhysicsShell* pPhysicsShell = P_create_Shell(); + + Fobb obb; + smart_cast(obj->Visual())->GetBox().get_CD( obb.m_translate, obb.m_halfsize ); + obb.m_rotate.identity(); + CPhysicsElement* E = P_create_Element(); R_ASSERT(E); E->add_Box(obb); + pPhysicsShell->add_Element(E); + pPhysicsShell->setMass(mass); + pPhysicsShell->set_PhysicsRefObject(smart_cast(obj)); + if(!obj->H_Parent()) + pPhysicsShell->Activate(obj->XFORM(),0,obj->XFORM(),not_active_state); + return pPhysicsShell; +} + +void ApplySpawnIniToPhysicShell(const CInifile* ini,CPhysicsShell* physics_shell,bool fixed) +{ + if(!ini) + return; + if(ini->section_exist("physics_common")) + { + fixed = fixed || (ini->line_exist("physics_common","fixed_bones")) ; +#pragma todo("not ignore static if non realy fixed! ") + fix_bones(ini->r_string("physics_common","fixed_bones"),physics_shell); + } + if(ini->section_exist("collide")) + { +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + if((ini->line_exist("collide","ignore_static")&&fixed)||(ini->line_exist("collide","ignore_static")&&ini->section_exist("animated_object"))) +#else + if(ini->line_exist("collide","ignore_static")&&fixed) +#endif + { + physics_shell->SetIgnoreStatic(); + } + if(ini->line_exist("collide","small_object")) + { + physics_shell->SetSmall(); + } + if(ini->line_exist("collide","ignore_small_objects")) + { + physics_shell->SetIgnoreSmall(); + } + if(ini->line_exist("collide","ignore_dynamic_objects")) + { + physics_shell->SetIgnoreDynamic(); + } + if(ini->line_exist("collide","ignore_ragdoll")) + { + physics_shell->SetIgnoreRagDoll(); + } + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + //If need, then show here that it is needed to ignore collisions with "animated_object" + if (ini->line_exist("collide","ignore_animated_objects")) + { + physics_shell->SetIgnoreAnimated(); + } +#endif + + } + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + //If next section is available then given "PhysicShell" is classified + //as animated and we read options for his animation + + if (ini->section_exist("animated_object")) + { + //Show that given "PhysicShell" animated + physics_shell->SetAnimated(); + } +#endif + +} + +void get_box(CPhysicsShell* shell,const Fmatrix& form, Fvector& sz,Fvector& c) +{ + c.set(0,0,0); + for(int i=0;3>i;++i) + { + float lo,hi; + const Fvector &ax=cast_fv(((const float*)&form+i*4)); + shell->get_Extensions(ax,0,lo,hi); + sz[i]=hi-lo;c.add(Fvector().mul(ax,(lo+hi)/2)); + } +} \ No newline at end of file diff --git a/src/xrGameLA/PhysicsShell.h b/src/xrGameLA/PhysicsShell.h new file mode 100644 index 000000000..aa058e258 --- /dev/null +++ b/src/xrGameLA/PhysicsShell.h @@ -0,0 +1,355 @@ +#ifndef PhysicsShellH +#define PhysicsShellH +#pragma once + +#include "PHDefs.h" +#include "PhysicsCommon.h" +#include "alife_space.h" +#include "script_export_space.h" + +class CPhysicsJoint; +class CPhysicsElement; +class CPhysicsShell; +class CPHFracture; +class CPHJointDestroyInfo; +class CODEGeom; +class CPHSynchronize; +class CPhysicsShellHolder; +class CGameObject; +class NET_Packet; +struct SBoneShape; +class CPHShellSplitterHolder; +class IKinematics; +typedef u32 CLClassBits; +typedef u32 CLBits; +typedef u32 CGID; +struct physicsBone +{ + CPhysicsJoint* joint; + CPhysicsElement* element; + physicsBone() + { + joint=NULL; + element=NULL; + } +}; +DEFINE_MAP (u16, physicsBone, BONE_P_MAP, BONE_P_PAIR_IT); +typedef const BONE_P_MAP :: iterator BONE_P_PAIR_CIT; +// ABSTRACT: +class CPhysicsBase +{ +public: + Fmatrix mXFORM ; // In parent space +public: + virtual void Activate (const Fmatrix& m0, float dt01, const Fmatrix& m2,bool disable=false) = 0; + virtual void Activate (const Fmatrix &transform,const Fvector& lin_vel,const Fvector& ang_vel,bool disable=false) = 0; + virtual void Activate (bool disable=false) = 0; + virtual void Activate (const Fmatrix& form,bool disable=false) = 0; + virtual const Fmatrix &XFORM ()const { return mXFORM; } + virtual void get_xform ( Fmatrix& form ) const { form.set( XFORM() ); } + virtual void __stdcall InterpolateGlobalTransform (Fmatrix* m) = 0; + virtual void GetGlobalTransformDynamic (Fmatrix* m) = 0; + virtual void InterpolateGlobalPosition (Fvector* v) = 0; + virtual void net_Import (NET_Packet& P) = 0; + virtual void net_Export (NET_Packet& P) = 0; + virtual void GetGlobalPositionDynamic (Fvector* v) = 0; + virtual bool isBreakable () = 0; + virtual bool isEnabled () const = 0; + virtual bool isActive () const = 0; + virtual bool isFullActive () const = 0; + virtual void Deactivate () = 0; + virtual void Enable () = 0; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void setMass (float M) = 0; + virtual void setDensity (float M) = 0; + virtual float getMass () = 0; + virtual float getDensity () = 0; + virtual float getVolume () = 0; + virtual void get_Extensions (const Fvector& axis,float center_prg,float& lo_ext, float& hi_ext) = 0; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void applyForce (const Fvector& dir, float val) = 0; + virtual void applyForce (float x,float y,float z) = 0; + virtual void applyImpulse (const Fvector& dir, float val) = 0; + virtual void setTorque (const Fvector& torque) = 0; + virtual void setForce (const Fvector& force) = 0; + virtual void __stdcall applyGravityAccel (const Fvector& accel) = 0; + virtual void SetAirResistance (float linear=default_k_l, float angular=default_k_w) = 0; + virtual void GetAirResistance (float &linear, float &angular) = 0; + virtual void set_DynamicLimits (float l_limit=default_l_limit,float w_limit=default_w_limit) = 0; + virtual void set_DynamicScales (float l_scale=default_l_scale,float w_scale=default_w_scale) = 0; + virtual void set_ContactCallback (ContactCallbackFun* callback) = 0; + virtual void set_ObjectContactCallback (ObjectContactCallbackFun* callback) = 0; + virtual void add_ObjectContactCallback (ObjectContactCallbackFun* callback) = 0; + virtual void remove_ObjectContactCallback (ObjectContactCallbackFun* callback) = 0; + virtual void set_CallbackData (void * cd) = 0; + virtual void *get_CallbackData () = 0; + virtual void set_PhysicsRefObject (CPhysicsShellHolder* ref_object) = 0; + virtual void get_LinearVel (Fvector& velocity) = 0; + virtual void get_AngularVel (Fvector& velocity) = 0; + virtual void set_LinearVel (const Fvector& velocity) = 0; + virtual void set_AngularVel (const Fvector& velocity) = 0; + virtual void TransformPosition (const Fmatrix &form) = 0; + virtual void set_ApplyByGravity (bool flag) = 0; + virtual bool get_ApplyByGravity () = 0; + + virtual void SetMaterial (u16 m) = 0; + virtual void SetMaterial (LPCSTR m) = 0; + virtual void set_DisableParams (const SAllDDOParams& params) = 0; + virtual void SetTransform (const Fmatrix& m0) = 0; + virtual ~CPhysicsBase () {}; +}; + +// ABSTRACT: +// Element is fully Rigid and consists of one or more forms, such as sphere, box, cylinder, etc. +class CPhysicsElement : public CPhysicsBase +{ + +public: + u16 m_SelfID ; + virtual CPhysicsShell *PhysicsShell () = 0; + virtual void set_ContactCallback (ContactCallbackFun *callback) = 0; + virtual CPhysicsShellHolder *PhysicsRefObject () = 0; + virtual void add_Sphere (const Fsphere& V) = 0; + virtual void add_Box (const Fobb& V) = 0; + virtual void add_Cylinder (const Fcylinder& V) = 0; + virtual void add_Shape (const SBoneShape& shape) = 0; + virtual void add_Shape (const SBoneShape& shape,const Fmatrix& offset) = 0; + virtual CODEGeom *last_geom () = 0; + virtual bool has_geoms () = 0; + virtual void add_Mass (const SBoneShape& shape,const Fmatrix& offset,const Fvector& mass_center,float mass,CPHFracture* fracture=NULL) = 0; + virtual void set_ParentElement (CPhysicsElement* p) = 0; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void set_BoxMass (const Fobb& box, float mass) = 0; + virtual void setInertia (const dMass& M) = 0; + virtual void addInertia (const dMass& M) = 0; + virtual void setMassMC (float M,const Fvector& mass_center) = 0; + virtual void applyImpulseVsMC (const Fvector& pos,const Fvector& dir, float val) = 0; + virtual void applyImpulseVsGF (const Fvector& pos,const Fvector& dir, float val) = 0; + virtual void applyImpulseTrace (const Fvector& pos, const Fvector& dir, float val,const u16 id) = 0; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void setDensityMC (float M,const Fvector& mass_center) = 0; + virtual u16 setGeomFracturable (CPHFracture &fracture) = 0; + virtual CPHFracture &Fracture (u16 num) = 0; +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual u16 numberOfGeoms () = 0; + virtual dBodyID get_body () = 0; + virtual const Fvector &mass_Center () = 0; + virtual const Fvector &local_mass_Center () = 0; + virtual float getRadius () = 0; + virtual dMass *getMassTensor () = 0; + virtual void get_MaxAreaDir (Fvector& dir) = 0; + virtual ObjectContactCallbackFun *get_ObjectContactCallback () = 0; + virtual void Fix () = 0; + virtual void ReleaseFixed () = 0; + virtual bool isFixed () = 0; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual ~CPhysicsElement () {}; + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CPhysicsElement) +#undef script_type_list +#define script_type_list save_type_list(CPhysicsElement) + +//ABSTRACT: +// Joint between two elements + +class CPhysicsJoint +{ + +public: + bool bActive ; + enum eVs { //coordinate system + vs_first , //in first local + vs_second , //in second local + vs_global //in global + }; + enum enumType{ //joint type + ball , // ball-socket + hinge , // standart hinge 1 - axis + hinge2 , // for car wheels 2-axes + full_control , // 3 - axes control (eiler - angles) + slider + }; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + enumType eType ; //type of the joint +public: + virtual ~CPhysicsJoint () {}; + virtual u16 BoneID () =0; + virtual void SetBoneID (u16 bone_id) =0; + virtual CPhysicsElement* PFirst_element () =0; + virtual CPhysicsElement* PSecond_element () =0; + virtual u16 GetAxesNumber () =0; + virtual void Activate () =0; + virtual void Create () =0; + virtual void RunSimulation () =0; + virtual void Deactivate () =0; + virtual void SetBackRef (CPhysicsJoint** j) =0; + virtual void SetAnchor (const Fvector& position) =0; + virtual void SetAxisSDfactors (float spring_factor,float damping_factor,int axis_num) =0; + virtual void SetJointSDfactors (float spring_factor,float damping_factor) =0; + virtual void SetAnchorVsFirstElement (const Fvector& position) =0; + virtual void SetAnchorVsSecondElement (const Fvector& position) =0; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetAxisDir (const Fvector& orientation,const int axis_num) =0; + virtual void SetAxisDirVsFirstElement (const Fvector& orientation,const int axis_num) =0; + virtual void SetAxisDirVsSecondElement (const Fvector& orientation,const int axis_num) =0; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetAnchor (const float x,const float y,const float z) =0; + virtual void SetAnchorVsFirstElement (const float x,const float y,const float z) =0; + virtual void SetAnchorVsSecondElement (const float x,const float y,const float z) =0; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetAxisDir (const float x,const float y,const float z,const int axis_num) =0; + virtual void SetAxisDirVsFirstElement (const float x,const float y,const float z,const int axis_num) =0; + virtual void SetAxisDirVsSecondElement (const float x,const float y,const float z,const int axis_num) =0; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetLimits (const float low,const float high,const int axis_num) =0; + virtual void SetLimitsVsFirstElement (const float low,const float high,const int axis_num) =0; + virtual void SetLimitsVsSecondElement (const float low,const float high,const int axis_num) =0; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void SetBreakable (float force, float torque) =0; + virtual CPHJointDestroyInfo *JointDestroyInfo () =0; + virtual bool isBreakable () =0; + virtual void SetForceAndVelocity (const float force,const float velocity=0.f,const int axis_num=-1) =0; + virtual void GetMaxForceAndVelocity (float &force,float &velocity,int axis_num) =0; + virtual float GetAxisAngle (int axis_num) =0; + virtual dJointID GetDJoint () =0; + virtual void GetAxisSDfactors (float& spring_factor,float& damping_factor,int axis_num) =0; + virtual void GetJointSDfactors (float& spring_factor,float& damping_factor) =0; + virtual void GetLimits (float& lo_limit,float& hi_limit,int axis_num) =0; + virtual void GetAxisDir (int num,Fvector& axis,eVs& vs) =0; + virtual void GetAxisDirDynamic (int num,Fvector& axis) =0; + virtual void GetAnchorDynamic (Fvector& anchor) =0; + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CPhysicsJoint) +#undef script_type_list +#define script_type_list save_type_list(CPhysicsJoint) +// ABSTRACT: +class CPHIsland; + + +class CPhysicsShellAnimator; + + +class CPhysicsShell : public CPhysicsBase +{ +protected: + IKinematics *m_pKinematics ; +public: + +public: +IC IKinematics *PKinematics () {return m_pKinematics ;} +////////////////////////////////////////////////////IPhysicsShell/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +virtual const Fmatrix &XFORM ()const { return CPhysicsBase::XFORM(); } +virtual const CPhysicsElement &Element ( u16 index ) { return *get_ElementByStoreOrder( index ); }; +virtual void GetGlobalTransformDynamic (Fmatrix* m) = 0; +//virtual u16 get_ElementsNumber ( ) const = 0; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual CPhysicsShellAnimator* PPhysicsShellAnimator () = 0; + void set_Kinematics (IKinematics* p) {m_pKinematics=p ;} + virtual void set_JointResistance (float force) = 0; + virtual void add_Element (CPhysicsElement* E) = 0; + virtual void add_Joint (CPhysicsJoint* E) = 0; + virtual CPHIsland *PIsland () = 0; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////// + virtual const CGID &GetCLGroup ()const = 0; + virtual void RegisterToCLGroup (CGID g) = 0; + virtual bool IsGroupObject () = 0; + virtual void SetIgnoreStatic () = 0; + virtual void SetIgnoreDynamic () = 0; + virtual void SetRagDoll () = 0; + virtual void SetIgnoreRagDoll () = 0; + +#ifdef ANIMATED_PHYSICS_OBJECT_SUPPORT + virtual void SetAnimated () = 0; + virtual void SetIgnoreAnimated () = 0; + virtual bool Animated () = 0; +#endif + + virtual void SetSmall () = 0; + virtual void SetIgnoreSmall () = 0; + virtual bool isFractured () = 0; + virtual CPHShellSplitterHolder *SplitterHolder () = 0; + virtual void SplitProcess (PHSHELL_PAIR_VECTOR &out_shels) = 0; + virtual void BlockBreaking () = 0; + virtual void UnblockBreaking () = 0; + virtual bool IsBreakingBlocked () = 0; + virtual void applyImpulseTrace (const Fvector& pos, const Fvector& dir, float val) = 0; + virtual void applyImpulseTrace (const Fvector& pos, const Fvector& dir, float val,const u16 id) = 0; + virtual void applyHit (const Fvector& pos, const Fvector& dir, float val,const u16 id,ALife::EHitType hit_type) = 0; + virtual BoneCallbackFun* GetBonesCallback () = 0; + virtual BoneCallbackFun* GetStaticObjectBonesCallback () = 0; + virtual void Update () = 0; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual void setMass1 (float M) = 0; + virtual void SmoothElementsInertia (float k) = 0; + virtual void setEquelInertiaForEls (const dMass& M) = 0; + virtual void addEquelInertiaToEls (const dMass& M) = 0; + virtual ELEMENT_STORAGE &Elements () = 0; + virtual CPhysicsElement *get_Element (u16 bone_id) = 0; + virtual CPhysicsElement *get_Element (const shared_str & bone_name) = 0; + virtual CPhysicsElement *get_Element (LPCSTR bone_name) = 0; + virtual CPhysicsElement *get_ElementByStoreOrder (u16 num) = 0; + virtual u16 get_ElementsNumber () = 0; + virtual CPHSynchronize *get_ElementSync (u16 element) = 0; + virtual CPhysicsJoint *get_Joint (u16 bone_id) = 0; + virtual CPhysicsJoint *get_Joint (const shared_str & bone_name) = 0; + virtual CPhysicsJoint *get_Joint (LPCSTR bone_name) = 0; + virtual CPhysicsJoint *get_JointByStoreOrder (u16 num) = 0; + virtual u16 get_JointsNumber () = 0; + virtual CODEGeom *get_GeomByID (u16 bone_id) = 0; + virtual void Freeze () = 0; + virtual void UnFreeze () = 0; + virtual void NetInterpolationModeON () = 0; + virtual void NetInterpolationModeOFF () = 0; + virtual void Disable () = 0; + virtual void DisableCollision () = 0; + virtual void EnableCollision () = 0; + virtual void SetRemoveCharacterCollLADisable () = 0; + virtual void DisableCharacterCollision () = 0; + virtual void PureStep (float step = fixed_step) = 0; + virtual void SetGlTransformDynamic (const Fmatrix &form) = 0; + virtual void CollideAll () = 0; + virtual CPhysicsElement *NearestToPoint (const Fvector& point) = 0; + virtual void build_FromKinematics (IKinematics* K,BONE_P_MAP* p_geting_map=NULL) = 0; + virtual void preBuild_FromKinematics (IKinematics* K,BONE_P_MAP* p_geting_map=NULL) = 0; + virtual void Build (bool disable=false) = 0; + virtual void SetMaxAABBRadius (float size) {}; + virtual void AddTracedGeom (u16 element=0,u16 geom=0) = 0; + virtual void SetAllGeomTraced () = 0; + virtual void RunSimulation (bool place_current_forms=true) = 0; + virtual void UpdateRoot () = 0; + virtual void ZeroCallbacks () = 0; + virtual void ResetCallbacks (u16 id,Flags64 &mask) = 0; + virtual void SetCallbacks (BoneCallbackFun* callback) = 0; + virtual void EnabledCallbacks (BOOL val) = 0; + virtual void ToAnimBonesPositions () = 0; + virtual bool AnimToVelocityState (float dt, float l_limit, float a_limit ) = 0; + virtual void SetBonesCallbacksOverwrite (bool v) = 0; + virtual Fmatrix &ObjectInRoot () = 0; + virtual void ObjectToRootForm (const Fmatrix& form) = 0; + virtual void SetPrefereExactIntegration () = 0; + virtual ~CPhysicsShell () ; + DECLARE_SCRIPT_REGISTER_FUNCTION + }; +add_to_type_list(CPhysicsShell) +#undef script_type_list +#define script_type_list save_type_list(CPhysicsShell) + +void get_box(CPhysicsShell* shell,const Fmatrix& form, Fvector& sz,Fvector& c); + +// Implementation creator +CPhysicsJoint* P_create_Joint (CPhysicsJoint::enumType type ,CPhysicsElement* first,CPhysicsElement* second) ; +CPhysicsElement* P_create_Element () ; +CPhysicsShell* P_create_Shell () ; +CPhysicsShell* P_create_splited_Shell () ; +CPhysicsShell* P_build_Shell (CGameObject* obj,bool not_active_state,LPCSTR fixed_bones) ; +CPhysicsShell* P_build_Shell (CGameObject* obj,bool not_active_state,U16Vec& fixed_bones) ; +CPhysicsShell* P_build_Shell (CGameObject* obj,bool not_active_state,BONE_P_MAP* bone_map,LPCSTR fixed_bones) ; +CPhysicsShell* P_build_Shell (CGameObject* obj,bool not_active_state,BONE_P_MAP* bone_map=NULL) ; +CPhysicsShell* P_build_SimpleShell (CGameObject* obj,float mass,bool not_active_state) ; + void ApplySpawnIniToPhysicShell (const CInifile* ini,CPhysicsShell* physics_shell,bool fixed) ; + void fix_bones (LPCSTR fixed_bones,CPhysicsShell* shell ) ; +#endif // PhysicsShellH \ No newline at end of file diff --git a/src/xrGameLA/PhysicsShellAnimator.cpp b/src/xrGameLA/PhysicsShellAnimator.cpp new file mode 100644 index 000000000..49ee53623 --- /dev/null +++ b/src/xrGameLA/PhysicsShellAnimator.cpp @@ -0,0 +1,72 @@ +#include "StdAfx.h" +#include "PhysicsShellAnimator.h" +#include "PhysicsShellAnimatorBoneData.h" +#include "../Include/xrRender/KinematicsAnimated.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHDynamicData.h" +#include "game_object_space.h" +#include "PhysicsShellHolder.h" +#include "../bone.h" + + +ICF bool no_physics_shape( const SBoneShape& shape ); + +CPhysicsShellAnimator::CPhysicsShellAnimator( CPhysicsShell* _pPhysicsShell ) : m_pPhysicsShell( _pPhysicsShell ) +{ + + for (xr_vector::iterator i=m_pPhysicsShell->Elements().begin();i!=m_pPhysicsShell->Elements().end();i++) + { + CPhysicsShellAnimatorBoneData PhysicsShellAnimatorBoneDataC; + PhysicsShellAnimatorBoneDataC.m_element=*i; + CBoneInstance& B=m_pPhysicsShell->PKinematics()->LL_GetBoneInstance(PhysicsShellAnimatorBoneDataC.m_element->m_SelfID); + B.reset_callback(); + PhysicsShellAnimatorBoneDataC.m_anim_fixed_dJointID=dJointCreateFixed(0,0); + ((CPHShell*)(m_pPhysicsShell))->Island().DActiveIsland()->AddJoint(PhysicsShellAnimatorBoneDataC.m_anim_fixed_dJointID); + dJointAttach(PhysicsShellAnimatorBoneDataC.m_anim_fixed_dJointID,PhysicsShellAnimatorBoneDataC.m_element->get_body(),0); + dJointSetFixed(PhysicsShellAnimatorBoneDataC.m_anim_fixed_dJointID); + m_bones_data.push_back(PhysicsShellAnimatorBoneDataC); + } + + for (u16 i=0;iget_JointsNumber();i++) + { + ((CPHShell*)(m_pPhysicsShell))->DeleteJoint(i); + } + +} + +CPhysicsShellAnimator::~CPhysicsShellAnimator() +{ + for (xr_vector::iterator i=m_bones_data.begin();i!=m_bones_data.end();i++) + { + ((CPHShell*)(m_pPhysicsShell))->Island().DActiveIsland()->RemoveJoint(i->m_anim_fixed_dJointID); + dJointDestroy(i->m_anim_fixed_dJointID); + } +} + +void CPhysicsShellAnimator::OnFrame() +{ + + + m_pPhysicsShell->Enable(); + + for (xr_vector::iterator i=m_bones_data.begin();i!=m_bones_data.end();i++) + { + Fmatrix target_obj_posFmatrixS; + CBoneInstance& B=m_pPhysicsShell->PKinematics()->LL_GetBoneInstance(i->m_element->m_SelfID); + + target_obj_posFmatrixS.mul_43((*(m_pPhysicsShell->Elements().begin()))->PhysicsRefObject()->XFORM(),B.mTransform); + + Fmatrix parent; + parent.invert (m_pPhysicsShell->mXFORM); + B.mTransform.mul_43(parent,i->m_element->mXFORM);//restore actual physic position for display + + dQuaternion target_obj_quat_dQuaternionS; + dMatrix3 ph_mat; + PHDynamicData::FMXtoDMX(target_obj_posFmatrixS,ph_mat); + dQfromR(target_obj_quat_dQuaternionS,ph_mat); + Fvector mc; + i->m_element->CPHGeometryOwner::get_mc_vs_transform(mc,target_obj_posFmatrixS); + dJointSetFixedQuaternionPos(i->m_anim_fixed_dJointID,target_obj_quat_dQuaternionS,&mc.x); + } + (*(m_pPhysicsShell->Elements().begin()))->PhysicsRefObject()->XFORM().set(m_pPhysicsShell->mXFORM); +} \ No newline at end of file diff --git a/src/xrGameLA/PhysicsShellAnimator.h b/src/xrGameLA/PhysicsShellAnimator.h new file mode 100644 index 000000000..277680b50 --- /dev/null +++ b/src/xrGameLA/PhysicsShellAnimator.h @@ -0,0 +1,19 @@ +#pragma once + +#include "PhysicsShell.h" +#include "PhysicsShellAnimatorBoneData.h" +class animation_movement_controller; +class CPhysicsShellAnimator +{ + friend class CPhysicsShellAnimatorBoneData; + xr_vector m_bones_data; + CPhysicsShell* m_pPhysicsShell; + + +public: + CPhysicsShellAnimator (CPhysicsShell* _pPhysicsShell); + ~CPhysicsShellAnimator (); + void OnFrame (); +}; + + diff --git a/src/xrGameLA/PhysicsShellAnimatorBoneData.h b/src/xrGameLA/PhysicsShellAnimatorBoneData.h new file mode 100644 index 000000000..c59a63dd9 --- /dev/null +++ b/src/xrGameLA/PhysicsShellAnimatorBoneData.h @@ -0,0 +1,10 @@ +#pragma once + +#include "PHShell.h" + +class CPhysicsShellAnimatorBoneData//Содержит информацию об целевой матрице анимации +{ + friend class CPhysicsShellAnimator; + dJointID m_anim_fixed_dJointID; + CPHElement* m_element; +}; \ No newline at end of file diff --git a/src/xrGameLA/PhysicsShellHolder.cpp b/src/xrGameLA/PhysicsShellHolder.cpp new file mode 100644 index 000000000..16e2a9045 --- /dev/null +++ b/src/xrGameLA/PhysicsShellHolder.cpp @@ -0,0 +1,387 @@ +#include "pch_script.h" +#include "PhysicsShellHolder.h" +#include "PhysicsShell.h" +#include "xrMessages.h" +#include "ph_shell_interface.h" +#include "../Include/xrRender/Kinematics.h" +#include "script_callback_ex.h" +#include "Level.h" +#include "PHCommander.h" +#include "PHScriptCall.h" +#include "CustomRocket.h" +#include "Grenade.h" +#include "phworld.h" +#include "phactivationshape.h" +#include "phvalide.h" +CPhysicsShellHolder::CPhysicsShellHolder() +{ + init(); +} + +void CPhysicsShellHolder::net_Destroy() +{ + //remove calls + CPHSriptReqGObjComparer cmpr(this); + Level().ph_commander_scripts().remove_calls(&cmpr); + //удалить партиклы из ParticlePlayer + CParticlesPlayer::net_DestroyParticles (); + inherited::net_Destroy (); + b_sheduled = false; + deactivate_physics_shell (); + xr_delete (m_pPhysicsShell); +} + +static enum EEnableState +{ + stEnable =0 , + stDisable , + stNotDefitnite +}; +static u8 st_enable_state=(u8)stNotDefitnite; +BOOL CPhysicsShellHolder::net_Spawn (CSE_Abstract* DC) +{ + CParticlesPlayer::net_SpawnParticles (); + st_enable_state=(u8)stNotDefitnite; + b_sheduled = true; + BOOL ret=inherited::net_Spawn (DC);//load + //create_physic_shell (); + if(PPhysicsShell()&&PPhysicsShell()->isFullActive()) + { + PPhysicsShell()->GetGlobalTransformDynamic(&XFORM()); + PPhysicsShell()->mXFORM = XFORM(); + switch (EEnableState(st_enable_state)) + { + case stEnable : PPhysicsShell()->Enable() ;break; + case stDisable : PPhysicsShell()->Disable() ;break; + case stNotDefitnite : ;break; + } + ApplySpawnIniToPhysicShell(pSettings,PPhysicsShell(),false); + st_enable_state=(u8)stNotDefitnite; + } + return ret; +} + +void CPhysicsShellHolder::PHHit(float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type /* ALife::eHitTypeWound*/) +{ + if(impulse>0) + if(m_pPhysicsShell) m_pPhysicsShell->applyHit(p_in_object_space,dir,impulse,element,hit_type); +} + +//void CPhysicsShellHolder::Hit(float P, Fvector &dir, CObject* who, s16 element, +// Fvector p_in_object_space, float impulse, ALife::EHitType hit_type) +void CPhysicsShellHolder::Hit (SHit* pHDS) +{ + PHHit(pHDS->damage(),pHDS->dir,pHDS->who,pHDS->boneID,pHDS->p_in_bone_space,pHDS->impulse,pHDS->hit_type); +} + +void CPhysicsShellHolder::create_physic_shell () +{ + VERIFY(!m_pPhysicsShell); + IPhysicShellCreator *shell_creator = smart_cast(this); + if (shell_creator) + shell_creator->CreatePhysicsShell(); +} + +void CPhysicsShellHolder::init () +{ + m_pPhysicsShell = NULL ; + b_sheduled = false ; +} +void CPhysicsShellHolder::correct_spawn_pos() +{ + VERIFY (PPhysicsShell()); + + Fvector size; + Fvector c; + get_box (PPhysicsShell(),XFORM(),size,c); + + CPHActivationShape activation_shape; + activation_shape.Create (c,size,this); + activation_shape.set_rotation (XFORM()); + PPhysicsShell()->DisableCollision (); + activation_shape.Activate (size,1,1.f,M_PI/8.f); +//// VERIFY (valid_pos(activation_shape.Position(),phBoundaries)); +// if (!valid_pos(activation_shape.Position(),phBoundaries)) { +// CPHActivationShape activation_shape; +// activation_shape.Create (c,size,this); +// activation_shape.set_rotation (XFORM()); +// activation_shape.Activate (size,1,1.f,M_PI/8.f); +//// VERIFY (valid_pos(activation_shape.Position(),phBoundaries)); +// } + + PPhysicsShell()->EnableCollision (); + + Fvector ap = activation_shape.Position(); +#ifdef DEBUG + if (!valid_pos(ap,phBoundaries)) { + Msg("not valid position %f,%f,%f",ap.x,ap.y,ap.z); + Msg("size %f,%f,%f",size.x,size.y,size.z); + Msg("Object: %s",Name()); + Msg("Visual: %s",*(cNameVisual())); + Msg("Object pos %f,%f,%f",Position().x,Position().y,Position().z); + } +#endif // DEBUG + VERIFY (valid_pos(activation_shape.Position(),phBoundaries)); + + Fmatrix trans; + trans.identity (); + trans.c.sub (ap,c); + PPhysicsShell()->TransformPosition (trans); + PPhysicsShell()->GetGlobalTransformDynamic(&XFORM()); + activation_shape.Destroy (); +} + +void CPhysicsShellHolder::activate_physic_shell() +{ + VERIFY (!m_pPhysicsShell); + create_physic_shell (); + Fvector l_fw, l_up; + l_fw.set (XFORM().k); + l_up.set (XFORM().j); + l_fw.mul (2.f); + l_up.mul (2.f); + + Fmatrix l_p1, l_p2; + l_p1.set (XFORM()); + l_p2.set (XFORM()); + l_fw.mul (2.f); + l_p2.c.add (l_fw); + + m_pPhysicsShell->Activate (l_p1, 0, l_p2); + if(H_Parent()&&H_Parent()->Visual()) + { + smart_cast(H_Parent()->Visual())->CalculateBones_Invalidate (); + smart_cast(H_Parent()->Visual())->CalculateBones (TRUE); + } + smart_cast(Visual())->CalculateBones_Invalidate (); + smart_cast(Visual())->CalculateBones(TRUE); + if(!IsGameTypeSingle()) + { + if(!smart_cast(this)&&!smart_cast(this)) PPhysicsShell()->SetIgnoreDynamic(); + } +// XFORM().set (l_p1); + correct_spawn_pos(); + +m_pPhysicsShell->set_LinearVel(l_fw); + m_pPhysicsShell->GetGlobalTransformDynamic(&XFORM()); + if(H_Parent()&&H_Parent()->Visual()) + { + smart_cast(H_Parent()->Visual())->CalculateBones_Invalidate (); + smart_cast(H_Parent()->Visual())->CalculateBones (TRUE); + } +} + +void CPhysicsShellHolder::setup_physic_shell () +{ + VERIFY (!m_pPhysicsShell); + create_physic_shell (); + m_pPhysicsShell->Activate (XFORM(),0,XFORM()); + smart_cast(Visual())->CalculateBones_Invalidate (); + smart_cast(Visual())->CalculateBones(TRUE); + m_pPhysicsShell->GetGlobalTransformDynamic(&XFORM()); +} + +void CPhysicsShellHolder::deactivate_physics_shell() +{ + if (m_pPhysicsShell) + m_pPhysicsShell->Deactivate (); + xr_delete(m_pPhysicsShell); +} +void CPhysicsShellHolder::PHSetMaterial(u16 m) +{ + if(m_pPhysicsShell) + m_pPhysicsShell->SetMaterial(m); +} + +void CPhysicsShellHolder::PHSetMaterial(LPCSTR m) +{ + if(m_pPhysicsShell) + m_pPhysicsShell->SetMaterial(m); +} + +void CPhysicsShellHolder::PHGetLinearVell (Fvector& velocity) +{ + if(!m_pPhysicsShell) + { + velocity.set(0,0,0); + return; + } + m_pPhysicsShell->get_LinearVel(velocity); +} + +void CPhysicsShellHolder::PHSetLinearVell(Fvector& velocity) +{ + if(!m_pPhysicsShell) + { + return; + } + m_pPhysicsShell->set_LinearVel(velocity); +} + + + +f32 CPhysicsShellHolder::GetMass() +{ + return m_pPhysicsShell ? m_pPhysicsShell->getMass() : 0; +} + +u16 CPhysicsShellHolder::PHGetSyncItemsNumber() +{ + if(m_pPhysicsShell) return m_pPhysicsShell->get_ElementsNumber(); + else return 0; +} + +CPHSynchronize* CPhysicsShellHolder::PHGetSyncItem (u16 item) +{ + if(m_pPhysicsShell) return m_pPhysicsShell->get_ElementSync(item); + else return 0; +} +void CPhysicsShellHolder::PHUnFreeze () +{ + if(m_pPhysicsShell) m_pPhysicsShell->UnFreeze(); +} + + +void CPhysicsShellHolder::PHFreeze() +{ + if(m_pPhysicsShell) m_pPhysicsShell->Freeze(); +} + +void CPhysicsShellHolder::OnChangeVisual() +{ + inherited::OnChangeVisual(); + if (0==renderable.visual) + { + if(m_pPhysicsShell)m_pPhysicsShell->Deactivate(); + xr_delete(m_pPhysicsShell); + VERIFY(0==m_pPhysicsShell); + } +} + +void CPhysicsShellHolder::UpdateCL () +{ + inherited::UpdateCL (); + //обновить присоединенные партиклы + UpdateParticles (); +} +float CPhysicsShellHolder::EffectiveGravity() +{ + return ph_world->Gravity(); +} + +void CPhysicsShellHolder:: save (NET_Packet &output_packet) +{ + inherited::save(output_packet); + u8 enable_state=(u8)stNotDefitnite; + if(PPhysicsShell()&&PPhysicsShell()->isActive()) + { + enable_state=u8(PPhysicsShell()->isEnabled() ? stEnable:stDisable); + } + output_packet.w_u8(enable_state); +} + +void CPhysicsShellHolder:: load (IReader &input_packet) +{ + inherited::load(input_packet); + st_enable_state=input_packet.r_u8(); + +} + +void CPhysicsShellHolder::PHSaveState(NET_Packet &P) +{ + + //CPhysicsShell* pPhysicsShell=PPhysicsShell(); + IKinematics* K =smart_cast(Visual()); + //Flags8 lflags; + //if(pPhysicsShell&&pPhysicsShell->isActive()) lflags.set(CSE_PHSkeleton::flActive,pPhysicsShell->isEnabled()); + +// P.w_u8 (lflags.get()); + if(K) + { + P.w_u64(K->LL_GetBonesVisible()); + P.w_u16(K->LL_GetBoneRoot()); + } + else + { + P.w_u64(u64(-1)); + P.w_u16(0); + } + ///////////////////////////// + Fvector min,max; + + min.set(flt_max,flt_max,flt_max); + max.set(-flt_max,-flt_max,-flt_max); + ///////////////////////////////////// + + u16 bones_number=PHGetSyncItemsNumber(); + for(u16 i=0;iget_State(state); + Fvector& p=state.position; + if(p.xmax.x)max.x=p.x; + if(p.y>max.y)max.y=p.y; + if(p.z>max.z)max.z=p.z; + } + + min.sub(2.f*EPS_L); + max.add(2.f*EPS_L); + + VERIFY(!min.similar(max)); + P.w_vec3(min); + P.w_vec3(max); + + P.w_u16(bones_number); + + for(u16 i=0;iget_State(state); + state.net_Save(P,min,max); + } +} +void +CPhysicsShellHolder::PHLoadState(IReader &P) +{ + +// Flags8 lflags; + IKinematics* K=smart_cast(Visual()); +// P.r_u8 (lflags.flags); + if(K) + { + K->LL_SetBonesVisible(P.r_u64()); + K->LL_SetBoneRoot(P.r_u16()); + } + + Fvector min=P.r_vec3(); + Fvector max=P.r_vec3(); + + VERIFY(!min.similar(max)); + + u16 bones_number=P.r_u16(); + for(u16 i=0;iset_State(state); + } +} + +bool CPhysicsShellHolder::register_schedule () const +{ + return (b_sheduled); +} + +void CPhysicsShellHolder::on_physics_disable() +{ + if (IsGameTypeSingle()) + return; + + NET_Packet net_packet; + u_EventGen (net_packet,GE_FREEZE_OBJECT,ID()); + Level().Send (net_packet,net_flags(TRUE,TRUE)); +} diff --git a/src/xrGameLA/PhysicsShellHolder.h b/src/xrGameLA/PhysicsShellHolder.h new file mode 100644 index 000000000..f9a6e6619 --- /dev/null +++ b/src/xrGameLA/PhysicsShellHolder.h @@ -0,0 +1,123 @@ +#ifndef PHYSICSSHELL_HOLDER_H +#define PHYSICSSHELL_HOLDER_H + +#include "GameObject.h" +#include "ParticlesPlayer.h" + + + +class CPHDestroyable; +class CPHCollisionDamageReceiver; +class CPHSoundPlayer; +class IDamageSource; +class CPHSkeleton; +class CCharacterPhysicsSupport; +class ICollisionDamageInfo; +class CIKLimbsController; +struct SCollisionHitCallback +{ + typedef void CollisionHitCallbackFun (CGameObject* obj,float min_cs,float max_cs,float &cs,float &hl,const ICollisionDamageInfo* di,SCollisionHitCallback* slf) ; + CollisionHitCallbackFun *m_collision_hit_callback ; + void *m_data ; + SCollisionHitCallback() + { + m_collision_hit_callback =NULL; + m_data =NULL; + } + SCollisionHitCallback(CollisionHitCallbackFun* cc,void* data) + { + VERIFY(cc); + m_collision_hit_callback=cc; + m_data=data; + } + void call(CGameObject* obj,float min_cs,float max_cs,float &cs,float &hl,const ICollisionDamageInfo* di) + { + VERIFY(m_collision_hit_callback); + m_collision_hit_callback(obj,min_cs,max_cs,cs,hl,di,this); + } +}; +class CPhysicsShellHolder: public CGameObject, + public CParticlesPlayer + +{ + bool b_sheduled; +public: + void SheduleRegister (){if(!IsSheduled())shedule_register();b_sheduled=true;} + void SheduleUnregister (){if(IsSheduled())shedule_unregister();b_sheduled=false;} +IC bool IsSheduled (){return b_sheduled;} +public: + + typedef CGameObject inherited; + + + CPhysicsShell *m_pPhysicsShell; + + + CPhysicsShellHolder (); + + + + IC CPhysicsShell *&PPhysicsShell () + { + return m_pPhysicsShell; + } + + IC CPhysicsShellHolder* PhysicsShellHolder () + { + return this; + } + virtual CPHDestroyable *ph_destroyable () {return NULL;} + virtual CPHCollisionDamageReceiver *PHCollisionDamageReceiver () {return NULL;} + virtual CPHSkeleton *PHSkeleton () {return NULL;} + virtual CPhysicsShellHolder *cast_physics_shell_holder () {return this;} + virtual CParticlesPlayer *cast_particles_player () {return this;} + virtual IDamageSource *cast_IDamageSource () {return NULL;} + virtual CPHSoundPlayer *ph_sound_player () {return NULL;} + virtual CCharacterPhysicsSupport *character_physics_support () {return NULL;} + virtual CCharacterPhysicsSupport *character_physics_support () const {return NULL;} + virtual CIKLimbsController *character_ik_controller () {return NULL;} + virtual SCollisionHitCallback *get_collision_hit_callback () {return NULL;} + virtual bool set_collision_hit_callback (SCollisionHitCallback *cc) {return false;} + virtual void __stdcall enable_notificate () {;} +public: + + virtual void PHGetLinearVell (Fvector& velocity); + virtual void PHSetLinearVell (Fvector& velocity); + virtual void PHSetMaterial (LPCSTR m); + virtual void PHSetMaterial (u16 m); + void PHSaveState (NET_Packet &P); + void PHLoadState (IReader &P); + virtual f32 GetMass (); + virtual void PHHit (float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type=ALife::eHitTypeWound); + virtual void Hit (SHit* pHDS); +/////////////////////////////////////////////////////////////////////// + virtual u16 PHGetSyncItemsNumber(); + virtual CPHSynchronize* PHGetSyncItem (u16 item); + virtual void PHUnFreeze (); + virtual void PHFreeze (); + virtual float EffectiveGravity (); +/////////////////////////////////////////////////////////////// + virtual void create_physic_shell (); + virtual void activate_physic_shell (); + virtual void setup_physic_shell (); + virtual void deactivate_physics_shell (); + + virtual void net_Destroy (); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + void init (); + + virtual void OnChangeVisual (); + //для наследования CParticlesPlayer + virtual void UpdateCL (); + void correct_spawn_pos (); + +public: + virtual bool register_schedule () const; + +public: + virtual void on_physics_disable (); +}; + +#endif \ No newline at end of file diff --git a/src/xrGameLA/PhysicsShellScript.cpp b/src/xrGameLA/PhysicsShellScript.cpp new file mode 100644 index 000000000..9303f68d7 --- /dev/null +++ b/src/xrGameLA/PhysicsShellScript.cpp @@ -0,0 +1,85 @@ +#include "pch_script.h" +#include "physicsshell.h" + +using namespace luabind; + +Fmatrix global_transform(CPhysicsElement* E) +{ + Fmatrix m; + E->GetGlobalTransformDynamic(&m); + return m; +} + +#pragma optimize("s",on) +void CPhysicsShell::script_register(lua_State *L) +{ + module(L) + [ + class_("physics_shell") + .def("apply_force", (void (CPhysicsShell::*)(float,float,float))(&CPhysicsShell::applyForce)) + .def("get_element_by_bone_name", (CPhysicsElement*(CPhysicsShell::*)(LPCSTR))(&CPhysicsShell::get_Element)) + .def("get_element_by_bone_id", (CPhysicsElement*(CPhysicsShell::*)(u16))(&CPhysicsShell::get_Element)) + .def("get_element_by_order", &CPhysicsShell::get_ElementByStoreOrder) + .def("get_elements_number", &CPhysicsShell::get_ElementsNumber) + .def("get_joint_by_bone_name", (CPhysicsJoint*(CPhysicsShell::*)(LPCSTR))(&CPhysicsShell::get_Joint)) + .def("get_joint_by_bone_id", (CPhysicsJoint*(CPhysicsShell::*)(u16))(&CPhysicsShell::get_Joint)) + .def("get_joint_by_order", &CPhysicsShell::get_JointByStoreOrder) + .def("get_joints_number", &CPhysicsShell::get_JointsNumber) + .def("block_breaking", &CPhysicsShell::BlockBreaking) + .def("unblock_breaking", &CPhysicsShell::UnblockBreaking) + .def("is_breaking_blocked", &CPhysicsShell::IsBreakingBlocked) + .def("is_breakable", &CPhysicsShell::isBreakable) + .def("get_linear_vel", &CPhysicsShell::get_LinearVel) + .def("get_angular_vel", &CPhysicsShell::get_AngularVel) + .def("apply_gravity", &CPhysicsShell::set_ApplyByGravity) + .def("get_mass", &CPhysicsShell::getMass) + ]; +} + +void CPhysicsElement::script_register(lua_State *L) +{ + module(L) + [ + class_("physics_element") + .def("apply_force", (void (CPhysicsElement::*)(float,float,float))(&CPhysicsElement::applyForce)) + .def("is_breakable", &CPhysicsElement::isBreakable) + .def("get_linear_vel", &CPhysicsElement::get_LinearVel) + .def("get_angular_vel", &CPhysicsElement::get_AngularVel) + .def("get_mass", &CPhysicsElement::getMass) + .def("get_density", &CPhysicsElement::getDensity) + .def("get_volume", &CPhysicsElement::getVolume) + .def("fix", &CPhysicsElement::Fix) + .def("release_fixed", &CPhysicsElement::ReleaseFixed) + .def("is_fixed", &CPhysicsElement::isFixed) + .def("global_transform", &global_transform) + .def("apply_impulse", &CPhysicsElement::applyImpulse) + ]; +} + +void CPhysicsJoint::script_register(lua_State *L) +{ + module(L) + [ + class_("physics_joint") + .def("get_bone_id", &CPhysicsJoint::BoneID) + .def("get_first_element", &CPhysicsJoint::PFirst_element) + .def("get_stcond_element", &CPhysicsJoint::PSecond_element) + .def("set_anchor_global", (void(CPhysicsJoint::*)(const float,const float,const float))(&CPhysicsJoint::SetAnchor)) + .def("set_anchor_vs_first_element", (void(CPhysicsJoint::*)(const float,const float,const float))(&CPhysicsJoint::SetAnchorVsFirstElement)) + .def("set_anchor_vs_second_element", (void(CPhysicsJoint::*)(const float,const float,const float))(&CPhysicsJoint::SetAnchorVsSecondElement)) + .def("get_axes_number", &CPhysicsJoint::GetAxesNumber) + .def("set_axis_spring_dumping_factors", &CPhysicsJoint::SetAxisSDfactors) + .def("set_joint_spring_dumping_factors", &CPhysicsJoint::SetJointSDfactors) + .def("set_axis_dir_global", (void(CPhysicsJoint::*)(const float,const float,const float,const int ))(&CPhysicsJoint::SetAxisDir)) + .def("set_axis_dir_vs_first_element", (void(CPhysicsJoint::*)(const float,const float,const float,const int ))(&CPhysicsJoint::SetAxisDirVsFirstElement)) + .def("set_axis_dir_vs_second_element", (void(CPhysicsJoint::*)(const float,const float,const float,const int ))(&CPhysicsJoint::SetAxisDirVsSecondElement)) + .def("set_limits", &CPhysicsJoint::SetLimits) + .def("set_max_force_and_velocity", &CPhysicsJoint::SetForceAndVelocity) + .def("get_max_force_and_velocity", &CPhysicsJoint::GetMaxForceAndVelocity) + .def("get_axis_angle", &CPhysicsJoint::GetAxisAngle) + .def("get_limits", &CPhysicsJoint::GetLimits,out_value(_2) + out_value(_3)) + .def("get_axis_dir", &CPhysicsJoint::GetAxisDirDynamic) + .def("get_anchor", &CPhysicsJoint::GetAnchorDynamic) + .def("is_breakable", &CPhysicsJoint::isBreakable) + ]; +} \ No newline at end of file diff --git a/src/xrGameLA/PhysicsSkeletonObject.cpp b/src/xrGameLA/PhysicsSkeletonObject.cpp new file mode 100644 index 000000000..13882d3e5 --- /dev/null +++ b/src/xrGameLA/PhysicsSkeletonObject.cpp @@ -0,0 +1,108 @@ +#include "stdafx.h" +#include "physicsskeletonobject.h" +#include "PhysicsShell.h" +#include "phsynchronize.h" +#include "xrserver_objects_alife.h" +#include "../Include/xrRender/Kinematics.h" +#include "../xr_collide_form.h" + +CPhysicsSkeletonObject::CPhysicsSkeletonObject() +{ + +} + +CPhysicsSkeletonObject::~CPhysicsSkeletonObject() +{ + +} + + +BOOL CPhysicsSkeletonObject::net_Spawn(CSE_Abstract* DC) +{ + CSE_Abstract *e = (CSE_Abstract*)(DC); + + inherited::net_Spawn (DC); + xr_delete(collidable.model); + collidable.model = new CCF_Skeleton(this); + CPHSkeleton::Spawn(e); + setVisible(TRUE); + setEnabled(TRUE); + if(!PPhysicsShell()->isBreakable()) + SheduleUnregister (); + return TRUE; +} + +void CPhysicsSkeletonObject::SpawnInitPhysics (CSE_Abstract *D) +{ + CreatePhysicsShell(D); + IKinematics* K=smart_cast (Visual()); + if(K) + { + K->CalculateBones_Invalidate(); + K->CalculateBones (TRUE); + } +} + +void CPhysicsSkeletonObject::net_Destroy() +{ + + inherited::net_Destroy (); + CPHSkeleton::RespawnInit (); + +} + +void CPhysicsSkeletonObject::Load(LPCSTR section) +{ + inherited::Load(section); + CPHSkeleton::Load(section); +} + +void CPhysicsSkeletonObject::CreatePhysicsShell(CSE_Abstract* e) +{ + CSE_PHSkeleton *po=smart_cast(e); + if(m_pPhysicsShell) return; + if (!Visual()) return; + m_pPhysicsShell=P_build_Shell(this,!po->_flags.test(CSE_PHSkeleton::flActive)); + +} + + +void CPhysicsSkeletonObject::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + + CPHSkeleton::Update(dt); +} + +void CPhysicsSkeletonObject::net_Save(NET_Packet &P) +{ + inherited::net_Save(P); + CPHSkeleton::SaveNetState (P); +} + + + +BOOL CPhysicsSkeletonObject::net_SaveRelevant() +{ + return TRUE;//!m_flags.test(CSE_ALifeObjectPhysic::flSpawnCopy); +} + + +BOOL CPhysicsSkeletonObject::UsedAI_Locations() +{ + return (FALSE); +} + +void CPhysicsSkeletonObject::UpdateCL() +{ + inherited::UpdateCL (); + PHObjectPositionUpdate (); +} + +void CPhysicsSkeletonObject:: PHObjectPositionUpdate() +{ + if(m_pPhysicsShell) + { + m_pPhysicsShell->InterpolateGlobalTransform(&XFORM()); + } +} \ No newline at end of file diff --git a/src/xrGameLA/PhysicsSkeletonObject.h b/src/xrGameLA/PhysicsSkeletonObject.h new file mode 100644 index 000000000..fa55fa249 --- /dev/null +++ b/src/xrGameLA/PhysicsSkeletonObject.h @@ -0,0 +1,38 @@ +#ifndef PHYSICS_SKELETON_OBJECT_H +#define PHYSICS_SKELETON_OBJECT_H +#include "physicsshellholder.h" +#include "PHSkeleton.h" + + +class CSE_ALifePHSkeletonObject; +class CPhysicsSkeletonObject : + public CPhysicsShellHolder, + public CPHSkeleton + +{ +typedef CPhysicsShellHolder inherited; + +public: + CPhysicsSkeletonObject(void); + virtual ~CPhysicsSkeletonObject(void); + + + virtual BOOL net_Spawn ( CSE_Abstract* DC) ; + virtual void net_Destroy () ; + virtual void Load (LPCSTR section) ; + virtual void UpdateCL ( ) ;// Called each frame, so no need for dt + virtual void shedule_Update (u32 dt) ; // + virtual void net_Save (NET_Packet& P) ; + virtual BOOL net_SaveRelevant () ; + virtual BOOL UsedAI_Locations () ; +protected: + virtual CPhysicsShellHolder *PPhysicsShellHolder() {return PhysicsShellHolder();} + virtual CPHSkeleton *PHSkeleton () {return this;} + virtual void SpawnInitPhysics (CSE_Abstract *D) ; + virtual void PHObjectPositionUpdate() ; + virtual void CreatePhysicsShell (CSE_Abstract *e) ; +}; + + + +#endif \ No newline at end of file diff --git a/src/xrGameLA/PostprocessAnimator.cpp b/src/xrGameLA/PostprocessAnimator.cpp new file mode 100644 index 000000000..a4f32ad73 --- /dev/null +++ b/src/xrGameLA/PostprocessAnimator.cpp @@ -0,0 +1,450 @@ +#include "stdafx.h" +#include "postprocessanimator.h" +#ifndef _PP_EDITOR_ +#include "ActorEffector.h" +#endif + +// postprocess value LOAD method implementation +void CPostProcessValue::load (IReader &pReader) +{ + m_Value.Load_2 (pReader); +} + +void CPostProcessValue::save (IWriter &pWriter) +{ + m_Value.Save (pWriter); +} + +// postprocess color LOAD method implementation +void CPostProcessColor::load (IReader &pReader) +{ + m_fBase = pReader.r_float (); + m_Red.Load_2 (pReader); + m_Green.Load_2 (pReader); + m_Blue.Load_2 (pReader); +} + +void CPostProcessColor::save (IWriter &pWriter) +{ + pWriter.w_float (m_fBase); + m_Red.Save (pWriter); + m_Green.Save (pWriter); + m_Blue.Save (pWriter); +} + +//main PostProcessAnimator class + +CPostprocessAnimator::CPostprocessAnimator() +{ + Create (); +} + +CPostprocessAnimator::CPostprocessAnimator(int id, bool cyclic) +#ifndef _PP_EDITOR_ +:CEffectorPP((EEffectorPPType)id, 100000, true), +m_bCyclic(cyclic) +#endif +{ + Create (); +} + +CPostprocessAnimator::~CPostprocessAnimator () +{ + Clear (); +} + +BOOL CPostprocessAnimator::Valid() +{ + if(m_bCyclic) return TRUE; + + return CEffectorPP::Valid (); +} + +void CPostprocessAnimator::Clear () +{ + for (int a = 0; a < POSTPROCESS_PARAMS_COUNT; a++) + xr_delete (m_Params[a]); +} + +void CPostprocessAnimator::Load (LPCSTR name) +{ + m_Name = name; +#ifndef _PP_EDITOR_ + string_path full_path; + if (!FS.exist (full_path, "$level$", name)) + if (!FS.exist (full_path, "$game_anims$", name)) + Debug.fatal (DEBUG_INFO,"Can't find motion file '%s'.", name); +#else /*_PP_EDITOR_*/ + string_path full_path; + strcpy (full_path, name); +#endif /*_PP_EDITOR_*/ + + LPCSTR ext = strext(full_path); + if (ext) + { + if (!xr_strcmp (ext,POSTPROCESS_FILE_EXTENSION)) + { + IReader* F = FS.r_open (full_path); + u32 dwVersion = F->r_u32(); + //VERIFY (dwVersion == POSTPROCESS_FILE_VERSION); + //load base color + VERIFY (m_Params[0]); + m_Params[0]->load (*F); + //load add color + VERIFY (m_Params[1]); + m_Params[1]->load (*F); + //load gray color + VERIFY (m_Params[2]); + m_Params[2]->load (*F); + //load gray value + VERIFY (m_Params[3]); + m_Params[3]->load (*F); + //load blur value + VERIFY (m_Params[4]); + m_Params[4]->load (*F); + //load duality horizontal + VERIFY (m_Params[5]); + m_Params[5]->load (*F); + //load duality vertical + VERIFY (m_Params[6]); + m_Params[6]->load (*F); + //load noise intensity + VERIFY (m_Params[7]); + m_Params[7]->load (*F); + //load noise granularity + VERIFY (m_Params[8]); + m_Params[8]->load (*F); + //load noise fps + VERIFY (m_Params[9]); + m_Params[9]->load (*F); + //close reader + FS.r_close (F); + } + else + FATAL ("ERROR: Can't support files with many animations set. Incorrect file."); + } + + f_length = GetLength (); +#ifndef _PP_EDITOR_ + if(!m_bCyclic) fLifeTime = f_length; +#endif + +} + +void CPostprocessAnimator::Stop (float sp) +{ + if(m_bStop) return; + m_bStop = true; + VERIFY (_valid(sp)); + m_factor_speed = sp; +} + +float CPostprocessAnimator::GetLength () +{ + float v = 0.0f; + for (int a = 0; a < POSTPROCESS_PARAMS_COUNT; a++) + { + float t = m_Params[a]->get_length(); + v = _max(t,v); + } + return v; +} + +void CPostprocessAnimator::Update (float tm) +{ + for (int a = 0; a < POSTPROCESS_PARAMS_COUNT; a++) + m_Params[a]->update (tm); +} +void CPostprocessAnimator::SetDesiredFactor (float f, float sp) +{ + m_dest_factor=f; + m_factor_speed=sp; + VERIFY (_valid(m_factor)); + VERIFY (_valid(m_dest_factor)); +}; + +void CPostprocessAnimator::SetCurrentFactor (float f) +{ + m_factor=f; + m_dest_factor=f; + VERIFY (_valid(m_factor)); + VERIFY (_valid(m_dest_factor)); +}; + +#ifndef _PP_EDITOR_ +BOOL CPostprocessAnimator::Process(SPPInfo &PPInfo) +{ + if(m_bCyclic) + fLifeTime = 100000; + + CEffectorPP::Process (PPInfo); + + + if(m_start_time<0.0f)m_start_time=Device.fTimeGlobal; + if(m_bCyclic &&((Device.fTimeGlobal-m_start_time)>f_length)) m_start_time+=f_length; + + Update (Device.fTimeGlobal-m_start_time); + + VERIFY (_valid(m_factor)); + VERIFY (_valid(m_factor_speed)); + VERIFY (_valid(m_dest_factor)); + if(m_bStop) + m_factor -= Device.fTimeDelta*m_factor_speed; + else + m_factor += m_factor_speed*Device.fTimeDelta*(m_dest_factor-m_factor); + + clamp (m_factor, 0.0001f, 1.0f); + + VERIFY (_valid(m_factor)); + VERIFY (_valid(m_factor_speed)); + + m_EffectorParams.color_base += pp_identity.color_base; + m_EffectorParams.color_gray += pp_identity.color_gray; + m_EffectorParams.color_add += pp_identity.color_add; + + if(0==m_Params[pp_noise_i]->get_keys_count()){ + m_EffectorParams.noise.intensity = pp_identity.noise.intensity; + } + + if(0==m_Params[pp_noise_g]->get_keys_count()){ + m_EffectorParams.noise.grain = pp_identity.noise.grain; + } + + if(0==m_Params[pp_noise_f]->get_keys_count()){ + m_EffectorParams.noise.fps = pp_identity.noise.fps; + }else + m_EffectorParams.noise.fps *= 100.0f; + + PPInfo.lerp (pp_identity, m_EffectorParams, m_factor); + + if(PPInfo.noise.grain<=0.0f){ + R_ASSERT3(0,"noise.grain cant be zero! see postprocess",*m_Name); + } + + if(fsimilar(m_factor,0.0001f,EPS_S)) + return FALSE; + + return TRUE; +} +#else +BOOL CPostprocessAnimator::Process(float dt, SPPInfo &PPInfo) +{ + + Update (dt); + + //if(m_bStop) +// m_factor -= dt*m_stop_speed; + + clamp (m_factor, 0.001f, 1.0f); + + PPInfo = m_EffectorParams; +// PPInfo.lerp (pp_identity, m_EffectorParams, m_factor); + +// if(fsimilar(m_factor,0.001f,EPS_S)) +// return FALSE; + + return TRUE; +} +#endif /*_PP_EDITOR_*/ + +void CPostprocessAnimator::Create () +{ + m_factor = 1.0f; + m_dest_factor = 1.0f; + m_bStop = false; + m_start_time = -1.0f; + m_factor_speed = 1.0f; + f_length = 0.0f; + + m_Params[0] = new CPostProcessColor(&m_EffectorParams.color_base); //base color + VERIFY (m_Params[0]); + m_Params[1] = new CPostProcessColor(&m_EffectorParams.color_add); //add color + VERIFY (m_Params[1]); + m_Params[2] = new CPostProcessColor(&m_EffectorParams.color_gray); //gray color + VERIFY (m_Params[2]); + m_Params[3] = new CPostProcessValue(&m_EffectorParams.gray); //gray value + VERIFY (m_Params[3]); + m_Params[4] = new CPostProcessValue(&m_EffectorParams.blur); //blur value + VERIFY (m_Params[4]); + m_Params[5] = new CPostProcessValue(&m_EffectorParams.duality.h); //duality horizontal + VERIFY (m_Params[5]); + m_Params[6] = new CPostProcessValue(&m_EffectorParams.duality.v); //duality vertical + VERIFY (m_Params[6]); + m_Params[7] = new CPostProcessValue(&m_EffectorParams.noise.intensity); //noise intensity + VERIFY (m_Params[7]); + m_Params[8] = new CPostProcessValue(&m_EffectorParams.noise.grain); //noise granularity + VERIFY (m_Params[8]); + m_Params[9] = new CPostProcessValue(&m_EffectorParams.noise.fps); //noise fps + VERIFY (m_Params[9]); +} + +#ifdef _PP_EDITOR_ +CPostProcessParam* CPostprocessAnimator::GetParam (pp_params param) +{ + VERIFY (param >= pp_base_color && param <= pp_noise_f); + return m_Params[param]; +} +void CPostprocessAnimator::Save (LPCSTR name) +{ + IWriter *W = FS.w_open (name); + VERIFY (W); + W->w_u32(POSTPROCESS_FILE_VERSION); + m_Params[0]->save (*W); + m_Params[1]->save (*W); + m_Params[2]->save (*W); + m_Params[3]->save (*W); + m_Params[4]->save (*W); + m_Params[5]->save (*W); + m_Params[6]->save (*W); + m_Params[7]->save (*W); + m_Params[8]->save (*W); + m_Params[9]->save (*W); + FS.w_close (W); + +} +//----------------------------------------------------------------------- +void CPostprocessAnimator::ResetParam (pp_params param) +{ + xr_delete (m_Params[param]); + switch (param) + { + case pp_base_color: + m_Params[0] = new CPostProcessColor(&m_EffectorParams.color_base); //base color + break; + case pp_add_color: + m_Params[1] = new CPostProcessColor(&m_EffectorParams.color_add); //add color + break; + case pp_gray_color: + m_Params[2] = new CPostProcessColor(&m_EffectorParams.color_gray); //gray color + break; + case pp_gray_value: + m_Params[3] = new CPostProcessValue(&m_EffectorParams.gray); //gray value + break; + case pp_blur: + m_Params[4] = new CPostProcessValue(&m_EffectorParams.blur); //blur value + break; + case pp_dual_h: + m_Params[5] = new CPostProcessValue(&m_EffectorParams.duality.h); //duality horizontal + break; + case pp_dual_v: + m_Params[6] = new CPostProcessValue(&m_EffectorParams.duality.v); //duality vertical + break; + case pp_noise_i: + m_Params[7] = new CPostProcessValue(&m_EffectorParams.noise.intensity); //noise intensity + break; + case pp_noise_g: + m_Params[8] = new CPostProcessValue(&m_EffectorParams.noise.grain); //noise granularity + break; + case pp_noise_f: + m_Params[9] = new CPostProcessValue(&m_EffectorParams.noise.fps); //noise fps + break; + } + VERIFY (m_Params[param]); +} + +void CPostProcessColor::add_value (float time, float value, float t, float c, float b, int index) +{ + KeyIt i; + if (0 == index) + { + m_Red.InsertKey (time, value); + i = m_Red.FindKey (time, 0.01f); + } + else if (1 == index) + { + m_Green.InsertKey (time, value); + i = m_Green.FindKey (time, 0.01f); + } + else + { + m_Blue.InsertKey (time, value); + i = m_Blue.FindKey (time, 0.01f); + } + (*i)->tension = t; + (*i)->continuity = c; + (*i)->bias = b; +} +void CPostProcessColor::update_value (float time, float value, float t, float c, float b, int index) +{ + KeyIt i; + if (0 == index) i = m_Red.FindKey (time, 0.01f); + else if (1 == index) i = m_Green.FindKey (time, 0.01f); + else i = m_Blue.FindKey (time, 0.01f); + (*i)->value = value; + (*i)->tension = t; + (*i)->continuity = c; + (*i)->bias = b; +} +void CPostProcessColor::get_value (float time, float &value, float &t, float &c, float &b, int index) +{ + KeyIt i; + if (0 == index) i = m_Red.FindKey (time, 0.01f); + else if (1 == index) i = m_Green.FindKey (time, 0.01f); + else i = m_Blue.FindKey (time, 0.01f); + value = (*i)->value; + t = (*i)->tension; + c = (*i)->continuity; + b = (*i)->bias; +} + +void CPostProcessValue::add_value (float time, float value, float t, float c, float b, int index) +{ + m_Value.InsertKey (time, value); + KeyIt i = m_Value.FindKey (time, 0.01f); + (*i)->tension = t; + (*i)->continuity = c; + (*i)->bias = b; +} +void CPostProcessValue::update_value (float time, float value, float t, float c, float b, int index) +{ + KeyIt i = m_Value.FindKey (time, 0.01f); + (*i)->value = value; + (*i)->tension = t; + (*i)->continuity = c; + (*i)->bias = b; +} +void CPostProcessValue::get_value (float time, float &value, float &t, float &c, float &b, int index) +{ + KeyIt i = m_Value.FindKey (time, 0.01f); + value = (*i)->value; + t = (*i)->tension; + c = (*i)->continuity; + b = (*i)->bias; +} +#endif /*_PP_EDITOR_*/ + + +#ifndef _PP_EDITOR_ +BOOL CPostprocessAnimatorLerp::Process(SPPInfo &PPInfo) +{ + if(!m_bStop) + m_factor = m_get_factor_func (); + return CPostprocessAnimator::Process (PPInfo); +} + + +BOOL CPostprocessAnimatorLerpConst::Process(SPPInfo &PPInfo) +{ + if(!m_bStop) + m_factor = m_power; + return CPostprocessAnimator::Process (PPInfo); +} + +CPostprocessAnimatorControlled::CPostprocessAnimatorControlled(CEffectorController* c) +:m_controller(c) +{ + m_controller->SetPP(this); + SetFactorFunc(fastdelegate::FastDelegate0(m_controller, &CEffectorController::GetFactor)); +} + +CPostprocessAnimatorControlled::~CPostprocessAnimatorControlled() +{ + m_controller->SetPP(NULL); +} + +BOOL CPostprocessAnimatorControlled::Valid() +{ + return m_controller->Valid (); +} + +#endif \ No newline at end of file diff --git a/src/xrGameLA/PostprocessAnimator.h b/src/xrGameLA/PostprocessAnimator.h new file mode 100644 index 000000000..f8537443d --- /dev/null +++ b/src/xrGameLA/PostprocessAnimator.h @@ -0,0 +1,212 @@ +#ifndef __ppanimator_included__ +#define __ppanimator_included__ +#pragma once + +#ifndef _PP_EDITOR_ + #include "../envelope.h" + #include "../EffectorPP.h" + #include "../cameramanager.h" + + class CEffectorController; +#else + #include "envelope.h" + #include "EffectorPP.h" + #include "CameraManager.h" +#endif /*_PP_EDITOR_*/ + +#define POSTPROCESS_PARAMS_COUNT 10 +#define POSTPROCESS_FILE_VERSION 0x0001 + +#define POSTPROCESS_FILE_EXTENSION ".ppe" + + +typedef enum _pp_params +{ + pp_unknown = -1, + pp_base_color = 0, + pp_add_color = 1, + pp_gray_color = 2, + pp_gray_value = 3, + pp_blur = 4, + pp_dual_h = 5, + pp_dual_v = 6, + pp_noise_i = 7, + pp_noise_g = 8, + pp_noise_f = 9, + pp_force_dword = 0x7fffffff +} pp_params; + + +class CPostProcessParam +{ +protected: +public: + virtual void update (float dt) = 0; + virtual void load (IReader &pReader) = 0; + virtual void save (IWriter &pWriter) = 0; + virtual float get_length () = 0; + virtual size_t get_keys_count () = 0; +#ifdef _PP_EDITOR_ + virtual void add_value (float time, float value, float t, float c, float b, int index = 0) = 0; + virtual void update_value (float time, float value, float t, float c, float b, int index = 0) = 0; + virtual void get_value (float time, float &value, float &t, float &c, float &b, int index = 0) = 0; + virtual float get_key_time (size_t index) = 0; +#endif /*_PP_EDITOR_*/ +}; + +class CPostProcessValue : public CPostProcessParam +{ +protected: + CEnvelope m_Value; + float *m_pfParam; +public: + CPostProcessValue (float *pfparam) { m_pfParam = pfparam; } + virtual void update (float dt) + { + *m_pfParam = m_Value.Evaluate (dt); + } + virtual void load (IReader &pReader); + virtual void save (IWriter &pWriter); + virtual float get_length () + { + float mn, mx; + return m_Value.GetLength (&mn, &mx); + } + virtual size_t get_keys_count () + { + return m_Value.keys.size (); + } +#ifdef _PP_EDITOR_ + virtual void add_value (float time, float value, float t, float c, float b, int index = 0); + virtual void update_value (float time, float value, float t, float c, float b, int index = 0); + virtual void get_value (float time, float &value, float &t, float &c, float &b, int index = 0); + virtual float get_key_time (size_t index) + { + VERIFY (index < get_keys_count ()); + return m_Value.keys[index]->time; + } +#endif /*_PP_EDITOR_*/ +}; + + +class CPostProcessColor : public CPostProcessParam +{ +protected: + float m_fBase; + CEnvelope m_Red; + CEnvelope m_Green; + CEnvelope m_Blue; + SPPInfo::SColor *m_pColor; +public: + CPostProcessColor (SPPInfo::SColor *pcolor) { m_pColor = pcolor; } + virtual void update (float dt) + { + m_pColor->r = m_Red.Evaluate (dt); + m_pColor->g = m_Green.Evaluate (dt); + m_pColor->b = m_Blue.Evaluate (dt); + } + virtual void load (IReader &pReader); + virtual void save (IWriter &pWriter); + virtual float get_length () + { + float mn, mx, + r = m_Red.GetLength (&mn, &mx), + g = m_Green.GetLength (&mn, &mx), + b = m_Blue.GetLength (&mn, &mx); + mn = (r > g ? r : g); + return mn > b ? mn : b; + } + virtual size_t get_keys_count () + { + return m_Red.keys.size (); + } +#ifdef _PP_EDITOR_ + virtual void add_value (float time, float value, float t, float c, float b, int index = 0); + virtual void update_value (float time, float value, float t, float c, float b, int index = 0); + virtual void get_value (float time, float &value, float &t, float &c, float &b, int index = 0); + virtual float get_key_time (size_t index) + { + VERIFY (index < get_keys_count ()); + return m_Red.keys[index]->time; + } +#endif /*_PP_EDITOR_*/ +}; + + +#ifndef _PP_EDITOR_ +class CPostprocessAnimator :public CEffectorPP +#else +class CPostprocessAnimator +#endif +{ +protected: + CPostProcessParam *m_Params[POSTPROCESS_PARAMS_COUNT]; + shared_str m_Name; + SPPInfo m_EffectorParams; + float m_factor; + float m_dest_factor; + bool m_bStop; + float m_factor_speed; + bool m_bCyclic; + float m_start_time; + float f_length; + + void Update (float tm); +public: + CPostprocessAnimator (int id, bool cyclic); + CPostprocessAnimator (); + virtual ~CPostprocessAnimator (); + void Clear (); + void Load (LPCSTR name); + IC LPCSTR Name (){return *m_Name;} + virtual void Stop (float speed); + void SetDesiredFactor (float f, float sp); + void SetCurrentFactor (float f); + void SetCyclic (bool b) {m_bCyclic=b;} + float GetLength (); +virtual BOOL Valid (); +#ifndef _PP_EDITOR_ +virtual BOOL Process (SPPInfo &PPInfo); +#else +virtual BOOL Process (float dt, SPPInfo &PPInfo); +#endif /*_PP_EDITOR_*/ + void Create (); +#ifdef _PP_EDITOR_ + CPostProcessParam* GetParam (pp_params param); + void ResetParam (pp_params param); + void Save (LPCSTR name); +#endif /*_PP_EDITOR_*/ +}; + +#ifndef _PP_EDITOR_ +class CPostprocessAnimatorLerp :public CPostprocessAnimator +{ +protected: + fastdelegate::FastDelegate0 m_get_factor_func; +public: + void SetFactorFunc (fastdelegate::FastDelegate0 f) {m_get_factor_func=f;} +virtual BOOL Process (SPPInfo &PPInfo); +}; + +class CPostprocessAnimatorLerpConst :public CPostprocessAnimator +{ +protected: + float m_power; +public: + CPostprocessAnimatorLerpConst () {m_power = 1.0f;} + void SetPower (float val) {m_power=val;} +virtual BOOL Process (SPPInfo &PPInfo); +}; + +class CPostprocessAnimatorControlled :public CPostprocessAnimatorLerp +{ + CEffectorController* m_controller; +public: + virtual ~CPostprocessAnimatorControlled (); + CPostprocessAnimatorControlled (CEffectorController* c); + virtual BOOL Valid (); +}; + +#endif + +#endif /*__ppanimator_included__*/ diff --git a/src/xrGameLA/PropertiesListHelper.h b/src/xrGameLA/PropertiesListHelper.h new file mode 100644 index 000000000..a5ba62bff --- /dev/null +++ b/src/xrGameLA/PropertiesListHelper.h @@ -0,0 +1,77 @@ +//--------------------------------------------------------------------------- +#ifndef PropertiesListHelperH +#define PropertiesListHelperH + +// refs +class ListItem; + +//--------------------------------------------------------------------------- +class CPropHelper:public IPropHelper{ + PropItem* CreateItem (PropItemVec& items, const shared_str& key, EPropType type, u32 item_flags=0); + PropValue* AppendValue (PropItemVec& items, const shared_str& key, PropValue* val, EPropType type, u32 item_flags=0); +public: + virtual PropItem* __stdcall FindItem (PropItemVec& items, shared_str key, EPropType type=PROP_UNDEF); +public: +//------------------------------------------------------------------------------ +// predefind event routines + virtual bool __stdcall FvectorRDOnAfterEdit(PropValue* sender, Fvector& edit_val); + virtual void __stdcall FvectorRDOnBeforeEdit(PropValue* sender,Fvector& edit_val); + virtual void __stdcall FvectorRDOnDraw (PropValue* sender, xr_string& draw_val); + virtual bool __stdcall floatRDOnAfterEdit (PropValue* sender, float& edit_val); + virtual void __stdcall floatRDOnBeforeEdit (PropValue* sender, float& edit_val); + virtual void __stdcall floatRDOnDraw (PropValue* sender, xr_string& draw_val); +// R-name edit + virtual void __stdcall NameBeforeEdit (PropValue* sender, shared_str& edit_val); + virtual bool __stdcall NameAfterEdit (PropValue* sender, shared_str& edit_val); + virtual void __stdcall NameDraw (PropValue* sender, xr_string& draw_val); +// C-name edit + virtual void __stdcall CNameBeforeEdit (PropValue* sender, xr_string& edit_val); + virtual bool __stdcall CNameAfterEdit (PropValue* sender, xr_string& edit_val); + virtual void __stdcall CNameDraw (PropValue* sender, xr_string& draw_val); +public: + virtual CaptionValue* __stdcall CreateCaption (PropItemVec& items, shared_str key, shared_str val); + virtual CanvasValue* __stdcall CreateCanvas (PropItemVec& items, shared_str key, shared_str val, int height); + virtual ButtonValue* __stdcall CreateButton (PropItemVec& items, shared_str key, shared_str val, u32 flags); + virtual ChooseValue* __stdcall CreateChoose (PropItemVec& items, shared_str key, shared_str* val, u32 mode, LPCSTR path=0, void* fill_param=0, u32 sub_item_count=1, u32 choose_flags=cfAllowNone); + virtual S8Value* __stdcall CreateS8 (PropItemVec& items, shared_str key, s8* val, s8 mn=0, s8 mx=100, s8 inc=1); + virtual S16Value* __stdcall CreateS16 (PropItemVec& items, shared_str key, s16* val, s16 mn=0, s16 mx=100, s16 inc=1); + virtual S32Value* __stdcall CreateS32 (PropItemVec& items, shared_str key, s32* val, s32 mn=0, s32 mx=100, s32 inc=1); + virtual U8Value* __stdcall CreateU8 (PropItemVec& items, shared_str key, u8* val, u8 mn=0, u8 mx=100, u8 inc=1); + virtual U16Value* __stdcall CreateU16 (PropItemVec& items, shared_str key, u16* val, u16 mn=0, u16 mx=100, u16 inc=1); + virtual U32Value* __stdcall CreateU32 (PropItemVec& items, shared_str key, u32* val, u32 mn=0, u32 mx=100, u32 inc=1); + virtual FloatValue* __stdcall CreateFloat (PropItemVec& items, shared_str key, float* val, float mn=0.f, float mx=1.f, float inc=0.01f, int decim=2); + virtual BOOLValue* __stdcall CreateBOOL (PropItemVec& items, shared_str key, BOOL* val); + virtual VectorValue* __stdcall CreateVector (PropItemVec& items, shared_str key, Fvector* val, float mn=0.f, float mx=1.f, float inc=0.01f, int decim=2); + virtual Flag8Value* __stdcall CreateFlag8 (PropItemVec& items, shared_str key, Flags8* val, u8 mask, LPCSTR c0=0, LPCSTR c1=0, u32 flags=0); + virtual Flag16Value* __stdcall CreateFlag16 (PropItemVec& items, shared_str key, Flags16* val, u16 mask, LPCSTR c0=0, LPCSTR c1=0, u32 flags=0); + virtual Flag32Value* __stdcall CreateFlag32 (PropItemVec& items, shared_str key, Flags32* val, u32 mask, LPCSTR c0=0, LPCSTR c1=0, u32 flags=0); + virtual Token8Value* __stdcall CreateToken8 (PropItemVec& items, shared_str key, u8* val, xr_token* token); + virtual Token16Value* __stdcall CreateToken16 (PropItemVec& items, shared_str key, u16* val, xr_token* token); + virtual Token32Value* __stdcall CreateToken32 (PropItemVec& items, shared_str key, u32* val, xr_token* token); + virtual RToken8Value* __stdcall CreateRToken8 (PropItemVec& items, shared_str key, u8* val, xr_rtoken* token, u32 t_cnt); + virtual RToken16Value* __stdcall CreateRToken16 (PropItemVec& items, shared_str key, u16* val, xr_rtoken* token, u32 t_cnt); + virtual RToken32Value* __stdcall CreateRToken32 (PropItemVec& items, shared_str key, u32* val, xr_rtoken* token, u32 t_cnt); + virtual RListValue* __stdcall CreateRList (PropItemVec& items, shared_str key, shared_str* val, shared_str* lst, u32 cnt); + virtual U32Value* __stdcall CreateColor (PropItemVec& items, shared_str key, u32* val); + virtual ColorValue* __stdcall CreateFColor (PropItemVec& items, shared_str key, Fcolor* val); + virtual VectorValue* __stdcall CreateVColor (PropItemVec& items, shared_str key, Fvector* val); + virtual RTextValue* __stdcall CreateRText (PropItemVec& items, shared_str key, shared_str* val); + virtual STextValue* __stdcall CreateSText (PropItemVec& items, shared_str key, xr_string* val); + virtual WaveValue* __stdcall CreateWave (PropItemVec& items, shared_str key, WaveForm* val); + virtual FloatValue* __stdcall CreateTime (PropItemVec& items, shared_str key, float* val, float mn=0.f, float mx=86400.f); + virtual ShortcutValue* __stdcall CreateShortcut (PropItemVec& items, shared_str key, xr_shortcut* val); + + virtual FloatValue* __stdcall CreateAngle (PropItemVec& items, shared_str key, float* val, float mn=flt_min, float mx=flt_max, float inc=0.01f, int decim=2); + virtual VectorValue* __stdcall CreateAngle3 (PropItemVec& items, shared_str key, Fvector* val, float mn=flt_min, float mx=flt_max, float inc=0.01f, int decim=2); + virtual RTextValue* __stdcall CreateName (PropItemVec& items, shared_str key, shared_str* val, ListItem* owner); + virtual RTextValue* __stdcall CreateNameCB (PropItemVec& items, shared_str key, shared_str* val, TOnDrawTextEvent=0, RTextValue::TOnBeforeEditEvent=0, RTextValue::TOnAfterEditEvent=0); + + // obsolette + virtual CTextValue* __stdcall CreateCText (PropItemVec& items, shared_str key, LPSTR val, u32 sz); + virtual CListValue* __stdcall CreateCList (PropItemVec& items, shared_str key, LPSTR val, u32 sz, xr_string* lst, u32 cnt); + virtual CTextValue* __stdcall CreateCName (PropItemVec& items, shared_str key, LPSTR val, u32 sz, ListItem* owner); + virtual TokenValueSH* __stdcall CreateTokenSH (PropItemVec& items, shared_str key, u32* val, const TokenValueSH::Item* lst, u32 cnt); + virtual CTextValue* __stdcall CreateTexture (PropItemVec& items, shared_str key, LPSTR val, u32 sz); +}; +//--------------------------------------------------------------------------- +#endif diff --git a/src/xrGameLA/PropertiesListTypes.h b/src/xrGameLA/PropertiesListTypes.h new file mode 100644 index 000000000..055c555b6 --- /dev/null +++ b/src/xrGameLA/PropertiesListTypes.h @@ -0,0 +1,682 @@ +//--------------------------------------------------------------------------- +#ifndef PropertiesListTypesH +#define PropertiesListTypesH + +#include "WaveForm.H" + +#ifdef __BORLANDC__ +# include "ElTree.hpp" +#endif + +#pragma pack( push,1 ) + +//--------------------------------------------------------------------------- +enum EPropType{ + PROP_UNDEF = -1, + PROP_CAPTION = 0x1000, + PROP_SHORTCUT, + PROP_BUTTON, + PROP_CHOOSE, + PROP_NUMERIC, // {u8,u16,u32,s8,s16,s32,f32} + PROP_BOOLEAN, + PROP_FLAG, + PROP_VECTOR, + PROP_TOKEN, + PROP_RTOKEN, + PROP_RLIST, + PROP_COLOR, + PROP_FCOLOR, + PROP_VCOLOR, + PROP_RTEXT, + PROP_STEXT, + PROP_WAVE, + PROP_CANVAS, + PROP_TIME, + + PROP_CTEXT, + PROP_CLIST, + PROP_SH_TOKEN, + PROP_TEXTURE2, +}; +// refs +struct xr_token; +class PropValue; +class PropItem; +DEFINE_VECTOR (PropItem*,PropItemVec,PropItemIt); + +//------------------------------------------------------------------------------ +#include "ChooseTypes.H" +#include "fastdelegate.h" +//------------------------------------------------------------------------------ +typedef fastdelegate::FastDelegate2 TOnDrawTextEvent; +typedef fastdelegate::FastDelegate1 TOnClick; +//------------------------------------------------------------------------------ + +class PropValue +{ + friend class CPropHelper; + friend class PropItem; +protected: + PropItem* m_Owner; +public: + u32 tag; +public: + // base events + typedef fastdelegate::FastDelegate1 TOnChange; + TOnChange OnChangeEvent; +public: + PropValue ():tag(0),m_Owner(0),OnChangeEvent(0){;} + virtual ~PropValue (){} + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText)=0; + virtual void ResetValue ()=0; + virtual bool Equal (PropValue* prop)=0; + IC PropItem* Owner (){return m_Owner;} +}; +//------------------------------------------------------------------------------ + +template +IC void set_value(T& val, const T& _val) +{ + val = _val; +}; + +template +class CustomValue: public PropValue +{ +public: + typedef T TYPE; +public: + TYPE init_value; + TYPE* value; +public: + typedef fastdelegate::FastDelegate2 TOnBeforeEditEvent; + typedef fastdelegate::FastDelegate2 TOnAfterEditEvent; + TOnBeforeEditEvent OnBeforeEditEvent; + TOnAfterEditEvent OnAfterEditEvent; +public: + CustomValue (T* val) + { + OnBeforeEditEvent = 0; + OnAfterEditEvent = 0; + set_value (value,val); + set_value (init_value,*val); + }; + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText){return "";} + virtual bool Equal (PropValue* val) + { + CustomValue* prop = (CustomValue*)val; + return (*value==*prop->value); + } + virtual const T& GetValue (){return *value; } + virtual void ResetValue (){set_value(*value,init_value);} + bool ApplyValue (const T& val) + { + if (!(*value==val)){ + set_value (*value,val); + return true; + } + return false; + } +}; + +class PropItem +{ + friend class CPropHelper; + friend class TProperties; + shared_str key; + EPropType type; + void* item; +public: + DEFINE_VECTOR (PropValue*,PropValueVec,PropValueIt); +private: + PropValueVec values; + TProperties* m_Owner; +// events +public: + typedef fastdelegate::FastDelegate1 TOnPropItemFocused; + TOnDrawTextEvent OnDrawTextEvent; + TOnPropItemFocused OnItemFocused; + TOnClick OnClickEvent; +public: + u32 prop_color; + u32 val_color; + Irect draw_rect; +public: + enum{ + flDisabled = (1<<0), + flShowCB = (1<<1), + flCBChecked = (1<<2), + flMixed = (1<<3), + flDrawThumbnail = (1<<4), + flSorted = (1<<5), + }; + Flags32 m_Flags; +public: + PropItem (EPropType _type):type(_type),prop_color(0),val_color(0),item(0),key(0),OnClickEvent(0),OnDrawTextEvent(0),OnItemFocused(0){m_Flags.zero();} + virtual ~PropItem () + { + for (PropValueIt it=values.begin(); values.end() != it; ++it) + xr_delete (*it); + }; + IC TProperties* Owner (){return m_Owner;} + void SetName (const shared_str& name) + { + key=name; + } + IC void ResetValues () + { + for (PropValueIt it=values.begin(); values.end() != it; ++it) + (*it)->ResetValue(); + CheckMixed (); + } + IC void AppendValue (PropValue* value) + { + if (!values.empty()&&!value->Equal(values.front())) + m_Flags.set (flMixed,TRUE); + values.push_back(value); + } + IC xr_string GetDrawText () + { + VERIFY(!values.empty()); + return m_Flags.is(flMixed)?xr_string("(mixed)"):values.front()->GetDrawText(OnDrawTextEvent); + } + IC void CheckMixed () + { + m_Flags.set (flMixed,FALSE); + if (values.size()>1){ + PropValueIt F = values.begin(); + PropValueIt it = F; ++it; + for (; values.end() != it; ++it){ + if (!(*it)->Equal(*F)){ + m_Flags.set(flMixed,TRUE); + break; + } + } + } + } + + template + IC void BeforeEdit (T2& val) + { + T1* CV = smart_cast(values.front()); VERIFY(CV); + if (!CV->OnBeforeEditEvent.empty()) CV->OnBeforeEditEvent(CV,val); + } + template + IC bool AfterEdit (T2& val) + { + T1* CV = smart_cast(values.front()); VERIFY(CV); + if (!CV->OnAfterEditEvent.empty()) return CV->OnAfterEditEvent(CV,val); + return true; + } + template + IC bool ApplyValue (const T2& val) + { + bool bChanged = false; + m_Flags.set (flMixed,FALSE); + for (PropValueIt it=values.begin(); values.end() != it; ++it){ + T1* CV = smart_cast(*it); VERIFY(CV); + if (CV->ApplyValue(val)){ + bChanged = true; + if (!CV->OnChangeEvent.empty()) CV->OnChangeEvent(*it); + } + if (!CV->Equal(values.front())) + m_Flags.set (flMixed,TRUE); + } + return bChanged; + } + IC PropValueVec& Values (){return values;} + IC PropValue* GetFrontValue (){VERIFY(!values.empty()); return values.front(); }; + IC EPropType Type (){return type;} +#ifdef __BORLANDC__ + IC TElTreeItem* Item (){return (TElTreeItem*)item;} +#endif + IC LPCSTR Key (){return key.c_str();} + IC void Enable (BOOL val){m_Flags.set(flDisabled,!val);} + IC BOOL Enabled (){return !m_Flags.is(flDisabled);} + IC void OnChange () + { + for (PropValueIt it=values.begin(); values.end() != it; ++it) + if (!(*it)->OnChangeEvent.empty()) + (*it)->OnChangeEvent(*it); + } +/* + template + IC void OnBeforeEdit () + { + for (PropValueIt it=values.begin(); values.end() != it; ++it){ + T1* CV = smart_cast(*it); VERIFY(CV); + if (CV->OnChangeEvent) CV->OnChangeEvent(*it); + } + } +*/ +}; + +//------------------------------------------------------------------------------ +// values +//------------------------------------------------------------------------------ +class CaptionValue: public PropValue{ + shared_str value; +public: + CaptionValue (const shared_str& val){value=val;} + virtual xr_string GetDrawText (TOnDrawTextEvent) {return value.c_str()?value.c_str():"";} + virtual void ResetValue (){;} + virtual bool Equal (PropValue* val) {return (value==((CaptionValue*)val)->value);} + bool ApplyValue (const shared_str& val){value=val; return false;} +}; + +class CanvasValue: public PropValue{ + shared_str value; +public: + typedef fastdelegate::FastDelegate3 TOnTestEqual; + typedef fastdelegate::FastDelegate3 TOnDrawCanvasEvent; +public: + int height; + TOnTestEqual OnTestEqual; + TOnDrawCanvasEvent OnDrawCanvasEvent; +public: + CanvasValue (const shared_str& val, int h):OnDrawCanvasEvent(0),OnTestEqual(0),height(h){value=val;} + virtual xr_string GetDrawText (TOnDrawTextEvent){return value.c_str()?value.c_str():"";} + virtual void ResetValue (){;} + virtual bool Equal (PropValue* val) + { + if (!OnTestEqual.empty()){bool res=true; OnTestEqual(this,(CanvasValue*)val,res); return res;} + return false; + } +}; + +class ButtonValue: public PropValue{ +public: + RStringVec value; + int btn_num; + typedef fastdelegate::FastDelegate3 TOnBtnClick; + TOnBtnClick OnBtnClickEvent; + enum{ + flFirstOnly = (1<<0) + }; + Flags32 m_Flags; +public: + ButtonValue (const shared_str& val, u32 flags) + { + m_Flags.assign (flags); + OnBtnClickEvent = 0; + btn_num = -1; + xr_string v; + int cnt =_GetItemCount(val.c_str()); + for (int k=0; k{ +public: + typedef fastdelegate::FastDelegate3 TOnValidateResult; + TOnValidateResult OnValidateResultEvent; +public: + ShortcutValue (TYPE* val):CustomValue(val){} + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText); + bool ApplyValue (const xr_shortcut& val) + { + if (!(*value==val)){ + bool allow = true; + if (!OnValidateResultEvent.empty()) + OnValidateResultEvent(this,val,allow); + if (allow){ + set_value (*value,val); + return true; + } + } + return false; + } +}; +class RTextValue: public CustomValue{ +public: + RTextValue (TYPE* val):CustomValue(val){}; + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText) + { + xr_string txt = GetValue().c_str()?GetValue().c_str():""; + if (!OnDrawText.empty())OnDrawText(this, txt); + return txt; + } +}; +class STextValue: public CustomValue{ +public: + STextValue (TYPE* val):CustomValue(val){}; + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText) + { + xr_string txt = GetValue(); + if (!OnDrawText.empty())OnDrawText(this, txt); + return txt; + } +}; + +class CTextValue: public PropValue{ + xr_string init_value; +public: + LPSTR value; +public: + typedef fastdelegate::FastDelegate2 TOnBeforeEditEvent; + typedef fastdelegate::FastDelegate2 TOnAfterEditEvent; + TOnBeforeEditEvent OnBeforeEditEvent; + TOnAfterEditEvent OnAfterEditEvent; +public: + int lim; +public: + CTextValue (LPSTR val, int _lim):value(val),init_value(val),lim(_lim) + { + OnBeforeEditEvent = 0; + OnAfterEditEvent = 0; + }; + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText) + { + xr_string txt = GetValue(); + if (!OnDrawText.empty())OnDrawText(this, txt); + return txt; + } + virtual bool Equal (PropValue* val) + { + return (0==xr_strcmp(value,((CTextValue*)val)->value)); + } + bool ApplyValue (LPCSTR val) + { + if (0!=xr_strcmp(value,val)){ + xr_strcpy (value,xr_strlen(val)+1,val); + return true; + } + return false; + } + LPSTR GetValue (){return value;} + virtual void ResetValue (){xr_strcpy(value,init_value.size()+1,init_value.c_str());} +}; +//------------------------------------------------------------------------------ + +class ChooseValue: public RTextValue{ +public: + int subitem; + u32 m_ChooseID; + u32 m_ChooseFlags; + shared_str m_StartPath; + ChooseItemVec* m_Items; + typedef fastdelegate::FastDelegate1 TOnChooseValueFill; + TOnChooseValueFill OnChooseFillEvent; + TOnDrawThumbnail OnDrawThumbnailEvent; + void* m_FillParam; +// utils + void AppendChooseItem (LPCSTR name, LPCSTR hint){VERIFY(m_Items); m_Items->push_back(SChooseItem(name,hint));} +public: + ChooseValue (shared_str* val, u32 cid, LPCSTR path, void* param, u32 sub_item_count, u32 choose_flags):RTextValue(val),m_ChooseID(cid),m_StartPath(path),subitem(sub_item_count),m_Items(0),m_FillParam(param),OnChooseFillEvent(0),OnDrawThumbnailEvent(0),m_ChooseFlags(choose_flags){} +}; + +typedef CustomValue BOOLValue; +//------------------------------------------------------------------------------ + +IC bool operator == (const WaveForm& A, const WaveForm& B){return !!A.Similar(B);} +class WaveValue: public CustomValue{ +public: + WaveValue (TYPE* val):CustomValue(val){}; + virtual xr_string GetDrawText (TOnDrawTextEvent){return "[Wave]";} +}; +//------------------------------------------------------------------------------ + +IC bool operator == (const Fcolor& A, const Fcolor& B) +{ return !!A.similar_rgba(B); } +typedef CustomValue ColorValue; +//------------------------------------------------------------------------------ + +template +class NumericValue: public CustomValue +{ +public: + T lim_mn; + T lim_mx; + T inc; + int dec; +public: + NumericValue (T* val):CustomValue(val) + { + value = val; + init_value = *value; + dec = 0; + }; + NumericValue (T* val, T mn, T mx, T increm, int decim):CustomValue(val),lim_mn(mn),lim_mx(mx),inc(increm),dec(decim) + { + clamp (*val,lim_mn,lim_mx); + value = val; + init_value = *value; + }; + bool ApplyValue (const T& _val) + { + T val = _val; + clamp (val,lim_mn,lim_mx); + return CustomValue::ApplyValue(val); + } + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText) + { + xr_string draw_val; + if (!OnDrawText.empty()) OnDrawText(this, draw_val); + else draw_sprintf (draw_val,*value,dec); + return draw_val; + } +}; + +//------------------------------------------------------------------------------ +template +IC xr_string draw_sprintf(xr_string& s, const T& V, int tag) +{ string256 tmp; xr_sprintf(tmp,"%d",V); s=tmp; return s;} +//------------------------------------------------------------------------------ +IC xr_string draw_sprintf(xr_string& s, const float& V, int dec) +{ + string32 fmt; xr_sprintf(fmt,"%%.%df",dec); + string256 tmp; xr_sprintf(tmp,fmt,V); + s = tmp; + return s; +} +//------------------------------------------------------------------------------ +IC bool operator == (const Fvector& A, const Fvector& B) +{ return !!A.similar(B); } +IC void clamp(Fvector& V, const Fvector& mn, const Fvector& mx) +{ + clamp(V.x,mn.x,mx.x); + clamp(V.y,mn.y,mx.y); + clamp(V.z,mn.z,mx.z); +} +IC xr_string draw_sprintf(xr_string& s, const Fvector& V, int dec) +{ + string128 fmt; xr_sprintf(fmt,"{%%.%df, %%.%df, %%.%df}",dec,dec,dec); + string256 tmp; xr_sprintf(tmp,fmt,V.x,V.y,V.z); + s = tmp; + return s; +} +//------------------------------------------------------------------------------ +typedef NumericValue U8Value; +typedef NumericValue U16Value; +typedef NumericValue U32Value; +typedef NumericValue S8Value; +typedef NumericValue S16Value; +typedef NumericValue S32Value; +typedef NumericValue FloatValue; +class VectorValue: public NumericValue{ +public: + VectorValue (Fvector* val, float mn, float mx, float increment, int decimal):NumericValue(val) + { + lim_mn.set (mn,mn,mn); + lim_mx.set (mx,mx,mx); + inc.set (increment,increment,increment); + dec = decimal; + }; +}; +//------------------------------------------------------------------------------ + +class FlagValueCustom +{ +public: + shared_str caption[2]; + enum{ + flInvertedDraw = (1<<0), + }; + Flags32 m_Flags; +public: + FlagValueCustom (u32 mask, LPCSTR c0, LPCSTR c1) + { + caption[0] = c0; + caption[1] = c1; + m_Flags.assign(mask); + } + virtual bool HaveCaption (){return caption[0].size()&&caption[1].size();} + virtual bool GetValueEx ()=0; +}; + +template +class FlagValue: public CustomValue, public FlagValueCustom +{ +public: + typedef T TYPE; + typedef typename T::TYPE FLAG_TYPE; +public: + FLAG_TYPE mask; +public: + FlagValue (T* val, FLAG_TYPE _mask, LPCSTR c0, LPCSTR c1, u32 flags):CustomValue(val),FlagValueCustom(flags,c0,c1),mask(_mask){} + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText) + { + xr_string draw_val; + if (!OnDrawText.empty()) OnDrawText(this, draw_val); + else return HaveCaption()?caption[GetValueEx()?1:0].c_str():""; + return draw_val; + } + virtual bool Equal (PropValue* val){return !!value->equal(*((FlagValue*)val)->value,mask);} + virtual const T& GetValue () {return *value; } + virtual void ResetValue () {value->set(mask,init_value.is(mask));} + virtual bool GetValueEx () {return !!value->is(mask);} + bool ApplyValue (const T& val) + { + if (!val.equal(*value,mask)){ + value->set (mask,val.is(mask)); + return true; + } + return false; + } +}; +//------------------------------------------------------------------------------ +typedef FlagValue Flag8Value; +typedef FlagValue Flag16Value; +typedef FlagValue Flag32Value; +//------------------------------------------------------------------------------ +template +bool operator == (_flags const & A, _flags const & B){return A.flags==B.flags;} +//------------------------------------------------------------------------------ + +class TokenValueCustom{ +public: + xr_token* token; + TokenValueCustom(xr_token* _token):token(_token){;} +}; +template +class TokenValue: public CustomValue, public TokenValueCustom +{ +public: + TokenValue (T* val, xr_token* _token):TokenValueCustom(_token),CustomValue(val){}; + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText) + { + xr_string draw_val; + if (!OnDrawText.empty()) OnDrawText(this, draw_val); + else for(int i=0; token[i].name; i++) if (token[i].id==(int)GetValue()) return token[i].name; + return draw_val; + } +}; +//------------------------------------------------------------------------------ +typedef TokenValue Token8Value; +typedef TokenValue Token16Value; +typedef TokenValue Token32Value; +//------------------------------------------------------------------------------ + +class RTokenValueCustom{ +public: + xr_rtoken* token; + u32 token_count; + RTokenValueCustom(xr_rtoken* _token, u32 _t_cnt):token(_token),token_count(_t_cnt){;} +}; +template +class RTokenValue: public CustomValue, public RTokenValueCustom +{ +public: + RTokenValue (T* val, xr_rtoken* _token, u32 _t_cnt):CustomValue(val),RTokenValueCustom(_token,_t_cnt){}; + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText) + { + xr_string draw_val; + if (!OnDrawText.empty()) OnDrawText(this, draw_val); + else for(u32 k=0; k RToken8Value; +typedef RTokenValueRToken16Value; +typedef RTokenValueRToken32Value; +//------------------------------------------------------------------------------ + +class TokenValueSH: public CustomValue{ +public: + struct Item { + u32 ID; + string64 str; + }; + u32 cnt; + const Item* items; +public: + TokenValueSH (u32* val, const Item* _items, u32 _cnt):CustomValue(val),cnt(_cnt),items(_items){}; + virtual xr_string GetDrawText (TOnDrawTextEvent OnDrawText) + { + u32 draw_val = GetValue(); + for(u32 i=0; iitems){ + m_Owner->m_Flags.set(PropItem::flDisabled,TRUE); + return false; + } + return RTextValue::Equal(val); + } +}; +class CListValue: public CTextValue{ +public: + xr_string* items; + u32 item_count; +public: + CListValue (LPSTR val, u32 sz, xr_string* _items, u32 cnt):CTextValue(val,sz),items(_items),item_count(cnt){}; + virtual bool Equal (PropValue* val) + { + if (items!=((CListValue*)val)->items){ + m_Owner->m_Flags.set(PropItem::flDisabled,TRUE); + return false; + } + return CTextValue::Equal(val); + } +}; +//------------------------------------------------------------------------------ +#pragma pack( pop ) + +#endif + + + + diff --git a/src/xrGameLA/RGD5.cpp b/src/xrGameLA/RGD5.cpp new file mode 100644 index 000000000..cf9a214aa --- /dev/null +++ b/src/xrGameLA/RGD5.cpp @@ -0,0 +1,25 @@ +#include "pch_script.h" +#include "rgd5.h" + +CRGD5::CRGD5(void) +{ + m_flags.set (Fbelt, TRUE); + m_weight = .1f; + m_baseSlot = GRENADE_SLOT; +} + +CRGD5::~CRGD5(void) +{ +} + +using namespace luabind; + +#pragma optimize("s",on) +void CRGD5::script_register (lua_State *L) +{ + module(L) + [ + class_("CRGD5") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/RGD5.h b/src/xrGameLA/RGD5.h new file mode 100644 index 000000000..bcf200bcb --- /dev/null +++ b/src/xrGameLA/RGD5.h @@ -0,0 +1,18 @@ +#pragma once + +#include "grenade.h" +#include "script_export_space.h" + +class CRGD5 : + public CGrenade +{ + typedef CGrenade inherited; +public: + CRGD5(void); + virtual ~CRGD5(void); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CRGD5) +#undef script_type_list +#define script_type_list save_type_list(CRGD5) diff --git a/src/xrGameLA/RadioactiveZone.cpp b/src/xrGameLA/RadioactiveZone.cpp new file mode 100644 index 000000000..e5a5fa70a --- /dev/null +++ b/src/xrGameLA/RadioactiveZone.cpp @@ -0,0 +1,129 @@ +#include "stdafx.h" +#include "radioactivezone.h" +#include "hudmanager.h" +#include "level.h" +#include "xrmessages.h" +#include "../bone.h" +#include "clsid_game.h" +#include "game_base_space.h" +#include "Hit.h" +#include "../xr_collide_form.h" + +CRadioactiveZone::CRadioactiveZone(void) +{} + +CRadioactiveZone::~CRadioactiveZone(void) +{} + +void CRadioactiveZone::Load(LPCSTR section) +{ + inherited::Load(section); +} + +bool CRadioactiveZone::BlowoutState () +{ + bool result = inherited::BlowoutState(); + if(!result) UpdateBlowout(); + return result; +} + +void CRadioactiveZone::Affect(SZoneObjectInfo* O) +{ + // вермя срабатывания не чаще, чем заданный период + if(m_dwDeltaTime < m_dwPeriod) return; +//. m_dwDeltaTime = 0; + + CGameObject *GO = O->object; + + if(GO) + { + Fvector pos; + XFORM().transform_tiny(pos,CFORM()->getSphere().P); + +#ifdef DEBUG + char pow[255]; + xr_sprintf(pow, "zone hit. %.3f", Power(GO->Position().distance_to(pos))); + if(bDebug) Msg("%s %s", *GO->cName(), pow); +#endif + + Fvector dir; + dir.set(0,0,0); + + Fvector position_in_bone_space; + float power = (GameID() == GAME_SINGLE) ? Power(GO->Position().distance_to(pos)) : 0.0f; + float impulse = 0.f; + if(power > EPS) + { +//. m_dwDeltaTime = 0; + position_in_bone_space.set(0.f,0.f,0.f); + + CreateHit(GO->ID(),ID(),dir,power,BI_NONE,position_in_bone_space,impulse,ALife::eHitTypeRadiation); + } + } +} + +void CRadioactiveZone::feel_touch_new (CObject* O ) +{ + inherited::feel_touch_new(O); + if (GameID() != GAME_SINGLE) + { + if (O->CLS_ID == CLSID_OBJECT_ACTOR) + { + CreateHit(O->ID(),ID(),Fvector().set(0, 0, 0),0.0f,BI_NONE,Fvector().set(0, 0, 0),0.0f,ALife::eHitTypeRadiation); + } + }; +}; + +#include "actor.h" +BOOL CRadioactiveZone::feel_touch_contact(CObject* O) +{ + + CActor* A = smart_cast(O); + if ( A ) + { + if (!((CCF_Shape*)CFORM())->Contact(O)) return FALSE; + return A->feel_touch_on_contact(this); + }else + return FALSE; +} + +void CRadioactiveZone::UpdateWorkload (u32 dt) +{ + if (IsEnabled() && GameID() != GAME_SINGLE) + { + OBJECT_INFO_VEC_IT it; + Fvector pos; + XFORM().transform_tiny(pos,CFORM()->getSphere().P); + for(it = m_ObjectInfoMap.begin(); m_ObjectInfoMap.end() != it; ++it) + { + if( !(*it).object->getDestroy() && (*it).object->CLS_ID == CLSID_OBJECT_ACTOR) + { + //===================================== + NET_Packet l_P; + l_P.write_start(); + l_P.read_start(); + + float dist = (*it).object->Position().distance_to(pos); + float power = Power(dist)*dt/1000; +/// Msg("Zone Dist %f, Radiation Power %f, ", dist, power); + + SHit HS; + HS.GenHeader(GE_HIT, (*it).object->ID()); + HS.whoID =ID(); + HS.weaponID = ID(); + HS.dir = Fvector().set(0,0,0); + HS.power = power; + HS.boneID = BI_NONE; + HS.p_in_bone_space = Fvector().set(0, 0, 0); + HS.impulse = 0.0f; + HS.hit_type = ALife::eHitTypeRadiation; + + HS.Write_Packet_Cont(l_P); + + (*it).object->OnEvent(l_P, HS.PACKET_TYPE); + //===================================== + }; + } + } + inherited::UpdateWorkload(dt); +} \ No newline at end of file diff --git a/src/xrGameLA/RadioactiveZone.h b/src/xrGameLA/RadioactiveZone.h new file mode 100644 index 000000000..09fadfc3f --- /dev/null +++ b/src/xrGameLA/RadioactiveZone.h @@ -0,0 +1,22 @@ +#pragma once +#include "customzone.h" + +class CRadioactiveZone : public CCustomZone +{ +private: + typedef CCustomZone inherited; +public: + CRadioactiveZone(void); + virtual ~CRadioactiveZone(void); + + virtual void Load (LPCSTR section); + virtual void Affect (SZoneObjectInfo* O); + virtual bool EnableEffector () {return true;} + + virtual void feel_touch_new (CObject* O ); + virtual void UpdateWorkload (u32 dt ); // related to fast-mode optimizations + virtual BOOL feel_touch_contact (CObject* O ); + +protected: + virtual bool BlowoutState (); +}; diff --git a/src/xrGameLA/RegistryFuncs.cpp b/src/xrGameLA/RegistryFuncs.cpp new file mode 100644 index 000000000..086f3dcc6 --- /dev/null +++ b/src/xrGameLA/RegistryFuncs.cpp @@ -0,0 +1,171 @@ +#include "StdAfx.h" +#include "RegistryFuncs.h" +#include "xrGameSpy/xrGameSpy_MainDefs.h" + +#define REGISTRY_BASE HKEY_LOCAL_MACHINE + +bool ReadRegistryValue(LPCSTR rKeyName, DWORD rKeyType, void* value ) +{ + HKEY hKey = 0; + long res = RegOpenKeyEx(REGISTRY_BASE, + REGISTRY_PATH, 0, KEY_READ, &hKey); + + if (res != ERROR_SUCCESS) + { + Msg ("! Unable to find %s in registry", REGISTRY_PATH); + return false; + } + + if (!hKey) + { + Msg ("! Unable to find %s entry in registry", REGISTRY_PATH); + return false; + } + + string64 rBuf; + DWORD KeyValueSize = 0; + switch (rKeyType) + { + case REG_DWORD: + { + KeyValueSize = 4; + }break; + case REG_SZ: + { + KeyValueSize = 64; + }break; + default: + { + Msg ("! Unknown registry data type."); + return false; + }break; + }; + + res = RegQueryValueEx(hKey, rKeyName, NULL, &rKeyType, (LPBYTE)rBuf, &KeyValueSize); + if (hKey != 0) RegCloseKey(hKey); + + if (res != ERROR_SUCCESS) + { + Msg ("! Unable to find %s entry in registry", rKeyName); + return false; + } + + memcpy(value, rBuf, KeyValueSize); + return true; +}; + +bool WriteRegistryValue (LPCSTR rKeyName, DWORD rKeyType, const void* value) +{ + HKEY hKey; + + long res = RegOpenKeyEx(REGISTRY_BASE, + REGISTRY_PATH, 0, KEY_WRITE, &hKey); + + if (res != ERROR_SUCCESS) + { + Msg ("! Unable to find %s in registry", REGISTRY_PATH); + return false; + } + + if (!hKey) + { + Msg ("! Unable to find %s entry in registry", REGISTRY_PATH); + return false; + } + + DWORD KeyValueSize = 0; + switch (rKeyType) + { + case REG_DWORD: + { + KeyValueSize = 4; + }break; + case REG_SZ: + { + KeyValueSize = 64; + }break; + default: + { + Msg ("! Unknown registry data type."); + return false; + }break; + }; + + res = RegSetValueEx(hKey, rKeyName, NULL, rKeyType, (LPBYTE)value, KeyValueSize); + + if (hKey) RegCloseKey(hKey); + return true; +}; + +bool ReadRegistry_StrValue (LPCSTR rKeyName, char* value ) +{ + return ReadRegistryValue(rKeyName, REG_SZ, value); +} + +void WriteRegistry_StrValue (LPCSTR rKeyName, const char* value ) +{ + WriteRegistryValue(rKeyName, REG_SZ, value); +} + +void ReadRegistry_DWValue (LPCSTR rKeyName, DWORD& value ) +{ + ReadRegistryValue(rKeyName, REG_DWORD, &value); +} +void WriteRegistry_DWValue (LPCSTR rKeyName, const DWORD& value ) +{ + WriteRegistryValue(rKeyName, REG_DWORD, &value); +} + +u32 const ReadRegistry_BinaryValue (LPCSTR rKeyName, u8 * buffer_dest, u32 const buffer_size) +{ + HKEY hKey = 0; + long res = RegOpenKeyEx(REGISTRY_BASE, REGISTRY_PATH, 0, KEY_READ, &hKey); + + if (res != ERROR_SUCCESS) + { + Msg ("! Unable to find %s in registry", REGISTRY_PATH); + return 0; + } + if (!hKey) + { + Msg ("! Unable to find %s entry in registry", REGISTRY_PATH); + return 0; + } + + DWORD value_type = REG_BINARY; + DWORD tmp_buffer_size = buffer_size; + + res = RegQueryValueEx(hKey, rKeyName, NULL, &value_type, buffer_dest, &tmp_buffer_size); + + if (res != ERROR_SUCCESS) + { + Msg ("! Unable to find %s entry in registry", rKeyName); + return 0; + } + + return static_cast(tmp_buffer_size); +} + +void WriteRegistry_BinaryValue (LPCSTR rKeyName, u8 const * buffer_src, u32 const buffer_size) +{ + HKEY hKey; + + long res = RegOpenKeyEx(REGISTRY_BASE, + REGISTRY_PATH, 0, KEY_WRITE, &hKey); + + if (res != ERROR_SUCCESS) + { + Msg ("! Unable to find %s in registry", REGISTRY_PATH); + return; + } + + if (!hKey) + { + Msg ("! Unable to find %s entry in registry", REGISTRY_PATH); + return; + } + + res = RegSetValueEx(hKey, rKeyName, NULL, REG_BINARY, buffer_src, buffer_size); + + RegCloseKey(hKey); +} diff --git a/src/xrGameLA/RegistryFuncs.h b/src/xrGameLA/RegistryFuncs.h new file mode 100644 index 000000000..b60471f89 --- /dev/null +++ b/src/xrGameLA/RegistryFuncs.h @@ -0,0 +1,10 @@ +#pragma once + +bool ReadRegistry_StrValue (LPCSTR rKeyName, char* value ); +void WriteRegistry_StrValue (LPCSTR rKeyName, const char* value ); + +u32 const ReadRegistry_BinaryValue (LPCSTR rKeyName, u8 * buffer_dest, u32 const buffer_size); +void WriteRegistry_BinaryValue (LPCSTR rKeyName, u8 const * buffer_src, u32 const buffer_size); + +void ReadRegistry_DWValue (LPCSTR rKeyName, DWORD& value ); +void WriteRegistry_DWValue (LPCSTR rKeyName, const DWORD& value ); \ No newline at end of file diff --git a/src/xrGameLA/RocketLauncher.cpp b/src/xrGameLA/RocketLauncher.cpp new file mode 100644 index 000000000..453aa134e --- /dev/null +++ b/src/xrGameLA/RocketLauncher.cpp @@ -0,0 +1,133 @@ +////////////////////////////////////////////////////////////////////// +// RocketLauncher.cpp: интерфейс для семейства объектов +// стреляющих гранатами и ракетами +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "RocketLauncher.h" +#include "CustomRocket.h" +#include "xrserver_objects_alife_items.h" +#include "level.h" +#include "ai_object_location.h" +#include "../IGame_Persistent.h" + +CRocketLauncher::CRocketLauncher() +{ +// m_pRocket = NULL; +} +CRocketLauncher::~CRocketLauncher() +{ +} +void CRocketLauncher::Load (LPCSTR section) +{ + m_fLaunchSpeed = pSettings->r_float(section, "launch_speed"); +} + +void CRocketLauncher::SpawnRocket(LPCSTR rocket_section, CGameObject* parent_rocket_launcher) +{ +// VERIFY(m_pRocket == NULL); + if (OnClient()) return; + + CSE_Abstract* D = F_entity_Create(rocket_section); + R_ASSERT (D); + CSE_Temporary *l_tpTemporary = smart_cast(D); + R_ASSERT (l_tpTemporary); + l_tpTemporary->m_tNodeID = (g_dedicated_server)?u32(-1) : parent_rocket_launcher->ai_location().level_vertex_id(); + // Fill + D->s_name = rocket_section; + D->set_name_replace (""); + + D->s_gameid = u8(GameID()); + D->s_RP = 0xff; + D->ID = 0xffff; + D->ID_Parent = parent_rocket_launcher->ID(); + D->ID_Phantom = 0xffff; + D->s_flags.assign (M_SPAWN_OBJECT_LOCAL); + D->RespawnTime = 0; + + // Send + NET_Packet P; + D->Spawn_Write (P,TRUE); + Level().Send (P,net_flags(TRUE)); + // Destroy + F_entity_Destroy (D); +} + +void CRocketLauncher::AttachRocket(u16 rocket_id, CGameObject* parent_rocket_launcher) +{ + CCustomRocket * pRocket = smart_cast(Level().Objects.net_Find(rocket_id)); + pRocket->m_pOwner = smart_cast(parent_rocket_launcher->H_Root()); + VERIFY(pRocket->m_pOwner); + pRocket->H_SetParent(parent_rocket_launcher); + m_rockets.push_back(pRocket); +} + +void CRocketLauncher::DetachRocket(u16 rocket_id, bool bLaunch) +{ + CObject *O = Level().Objects.net_Find(rocket_id); + if (!O) return; //skyloader: object can dissapear + + CCustomRocket *pRocket = smart_cast(O); + if (!pRocket && OnClient()) return; + + VERIFY(pRocket); + ROCKETIT It = std::find(m_rockets.begin(), m_rockets.end(),pRocket); + ROCKETIT It_l = std::find(m_launched_rockets.begin(), m_launched_rockets.end(),pRocket); + + if (OnServer()) + { + VERIFY( (It != m_rockets.end())|| + (It_l != m_launched_rockets.end()) ); + }; + + if( It != m_rockets.end() ) + { + (*It)->m_bLaunched = bLaunch; + (*It)->H_SetParent (NULL); + m_rockets.erase (It); + }; + + if( It_l != m_launched_rockets.end() ) + { + (*It)->m_bLaunched = bLaunch; + (*It_l)->H_SetParent (NULL); + m_launched_rockets.erase (It_l); + } +} + + + + +void CRocketLauncher::LaunchRocket(const Fmatrix& xform, + const Fvector& vel, + const Fvector& angular_vel) +{ +/* VERIFY(m_pRocket != NULL); + m_pRocket->SetLaunchParams(xform, vel, angular_vel); + m_pRocket->H_SetParent(NULL); +*/ + VERIFY2(_valid(xform),"CRocketLauncher::LaunchRocket. Invalid xform argument!"); + getCurrentRocket()->SetLaunchParams(xform, vel, angular_vel); +// Msg("---------Launched rocket [%d] frame [%d]",getCurrentRocket()->ID(), Device.dwFrame); +// getCurrentRocket()->H_SetParent(NULL); + m_launched_rockets.push_back( getCurrentRocket() ); + //m_rockets.pop_back(); +} + +CCustomRocket* CRocketLauncher::getCurrentRocket() +{ + if( m_rockets.size() ) + return m_rockets.back(); + else + return (CCustomRocket*)0; +} + +void CRocketLauncher::dropCurrentRocket() +{ + m_rockets.pop_back(); +} + +u32 CRocketLauncher::getRocketCount() +{ + return m_rockets.size(); +} diff --git a/src/xrGameLA/RocketLauncher.h b/src/xrGameLA/RocketLauncher.h new file mode 100644 index 000000000..beadbf89d --- /dev/null +++ b/src/xrGameLA/RocketLauncher.h @@ -0,0 +1,32 @@ +#pragma once + +class CCustomRocket; +class CGameObject; + +class CRocketLauncher +{ +public: + CRocketLauncher (); + ~CRocketLauncher (); + + virtual void Load (LPCSTR section); + + void AttachRocket (u16 rocket_id, CGameObject* parent_rocket_launcher); + void DetachRocket (u16 rocket_id, bool bLaunch); + + void SpawnRocket (LPCSTR rocket_section, CGameObject* parent_rocket_launcher); + void LaunchRocket (const Fmatrix& xform, const Fvector& vel, const Fvector& angular_vel); + +protected: + DEFINE_VECTOR(CCustomRocket*, ROCKET_VECTOR, ROCKETIT); + ROCKET_VECTOR m_rockets; + ROCKET_VECTOR m_launched_rockets; + + CCustomRocket* getCurrentRocket(); + void dropCurrentRocket(); + u32 getRocketCount(); + //начальная скорость, придаваемая ракете во время + //старта + float m_fLaunchSpeed; + +}; \ No newline at end of file diff --git a/src/xrGameLA/RustyHairArtifact.cpp b/src/xrGameLA/RustyHairArtifact.cpp new file mode 100644 index 000000000..2faed37b7 --- /dev/null +++ b/src/xrGameLA/RustyHairArtifact.cpp @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////// +// RustyHairArtifact.cpp +// RustyHairArtefact - артефакт ржавые волосы +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "RustyHairArtifact.h" + + +CRustyHairArtefact::CRustyHairArtefact(void) +{ +} + +CRustyHairArtefact::~CRustyHairArtefact(void) +{ +} + +void CRustyHairArtefact::Load(LPCSTR section) +{ + inherited::Load(section); +} + diff --git a/src/xrGameLA/RustyHairArtifact.h b/src/xrGameLA/RustyHairArtifact.h new file mode 100644 index 000000000..263650e55 --- /dev/null +++ b/src/xrGameLA/RustyHairArtifact.h @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////// +// RustyHairArtifact.h +// RustyHairArtefact - артефакт ржавые волосы +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CRustyHairArtefact : public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CRustyHairArtefact(void); + virtual ~CRustyHairArtefact(void); + + virtual void Load (LPCSTR section); + +protected: +}; \ No newline at end of file diff --git a/src/xrGameLA/ScientificOutfit.cpp b/src/xrGameLA/ScientificOutfit.cpp new file mode 100644 index 000000000..a55aaea92 --- /dev/null +++ b/src/xrGameLA/ScientificOutfit.cpp @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////// +// ScientificOutfit.cpp +// ScientificOutfit - защитный костюм ученого +/////////////////////////////////////////////////////////////// + +#pragma once + +#include "stdafx.h" +#include "scientificoutfit.h" + +CScientificOutfit::CScientificOutfit() +{ +} + +CScientificOutfit::~CScientificOutfit() +{ +} \ No newline at end of file diff --git a/src/xrGameLA/ScientificOutfit.h b/src/xrGameLA/ScientificOutfit.h new file mode 100644 index 000000000..a030c43a4 --- /dev/null +++ b/src/xrGameLA/ScientificOutfit.h @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////// +// ScientificOutfit.h +// ScientificOutfit - защитный костюм ученого +/////////////////////////////////////////////////////////////// + + +#pragma once + +#include "customoutfit.h" + +class CScientificOutfit: public CCustomOutfit +{ +private: + typedef CCustomOutfit inherited; +public: + CScientificOutfit(void); + virtual ~CScientificOutfit(void); +}; diff --git a/src/xrGameLA/Scope.cpp b/src/xrGameLA/Scope.cpp new file mode 100644 index 000000000..8f5560908 --- /dev/null +++ b/src/xrGameLA/Scope.cpp @@ -0,0 +1,22 @@ +#include "pch_script.h" +#include "scope.h" + +CScope::CScope () +{ +} + +CScope::~CScope () +{ +} + +using namespace luabind; + +#pragma optimize("s",on) +void CScope::script_register (lua_State *L) +{ + module(L) + [ + class_("CScope") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/Scope.h b/src/xrGameLA/Scope.h new file mode 100644 index 000000000..cc78569ea --- /dev/null +++ b/src/xrGameLA/Scope.h @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////// +// Scope.h +// Scope - апгрейд оружия снайперский прицел +/////////////////////////////////////////////////////////////// + +#pragma once + +#include "inventory_item_object.h" +#include "script_export_space.h" + +class CScope : public CInventoryItemObject { +private: + typedef CInventoryItemObject inherited; +public: + CScope (); + virtual ~CScope(); + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CScope) +#undef script_type_list +#define script_type_list save_type_list(CScope) \ No newline at end of file diff --git a/src/xrGameLA/ScriptXMLInit.cpp b/src/xrGameLA/ScriptXMLInit.cpp new file mode 100644 index 000000000..37006dae4 --- /dev/null +++ b/src/xrGameLA/ScriptXMLInit.cpp @@ -0,0 +1,277 @@ +#include "pch_script.h" +#include "ScriptXmlInit.h" +#include "ui\UIXmlInit.h" +#include "ui\UITextureMaster.h" +#include "ui\UICheckButton.h" +#include "ui\UISpinNum.h" +#include "ui\UISpinText.h" +#include "ui\UIComboBox.h" +#include "ui\UITabControl.h" +#include "ui\UIFrameWindow.h" +#include "ui\UILabel.h" +#include "ui\UIKeyBinding.h" +#include "ui\UIEditBox.h" +#include "ui\UIAnimatedStatic.h" +#include "ui\UITrackBar.h" +#include "ui\UIMMShniaga.h" +#include "ui\UIScrollView.h" +#include "ui\UIProgressBar.h" +#include "ui\UIListWnd.h" + +using namespace luabind; + +void _attach_child(CUIWindow* _child, CUIWindow* _parent) +{ + if(!_parent) return; + + _child->SetAutoDelete (true); + CUIScrollView* _parent_scroll = smart_cast(_parent); + if(_parent_scroll) + _parent_scroll->AddWindow (_child, true); + else + _parent->AttachChild (_child); +} + +void CScriptXmlInit::ParseFile(LPCSTR xml_file) +{ + m_xml.Load(CONFIG_PATH, UI_PATH, xml_file); +} + +// lost alpha start +void CScriptXmlInit::ParseFile(LPCSTR xml_path, LPCSTR xml_file) +{ + m_xml.Load(CONFIG_PATH, xml_path, xml_file); +} + + +void CScriptXmlInit::InitWindow(LPCSTR path, int index, CUIWindow* pWnd) +{ + CUIXmlInit::InitWindow(m_xml, path, index, pWnd); +} + + +CUIFrameWindow* CScriptXmlInit::InitFrame(LPCSTR path, CUIWindow* parent) +{ + CUIFrameWindow* pWnd = new CUIFrameWindow(); + CUIXmlInit::InitFrameWindow(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + + +CUIFrameLineWnd* CScriptXmlInit::InitFrameLine(LPCSTR path, CUIWindow* parent) +{ + CUIFrameLineWnd* pWnd = new CUIFrameLineWnd(); + CUIXmlInit::InitFrameLine(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +void CScriptXmlInit::InitAutoStaticGroup(LPCSTR path, CUIWindow* pWnd) +{ + CUIXmlInit::InitAutoStaticGroup(m_xml, path, 0, pWnd); +} + +CUILabel* CScriptXmlInit::InitLabel(LPCSTR path, CUIWindow* parent) +{ + CUILabel* pWnd = new CUILabel(); + CUIXmlInit::InitLabel(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUIEditBox* CScriptXmlInit::InitEditBox(LPCSTR path, CUIWindow* parent) +{ + CUIEditBox* pWnd = new CUIEditBox(); + CUIXmlInit::InitEditBox(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUIStatic* CScriptXmlInit::InitStatic(LPCSTR path, CUIWindow* parent) +{ + CUIStatic* pWnd = new CUIStatic(); + CUIXmlInit::InitStatic(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUITextWnd* CScriptXmlInit::InitTextWnd(LPCSTR path, CUIWindow* parent) +{ + CUITextWnd* pWnd = new CUITextWnd(); + CUIXmlInit::InitTextWnd(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUIStatic* CScriptXmlInit::InitAnimStatic(LPCSTR path, CUIWindow* parent) +{ + CUIAnimatedStatic* pWnd = new CUIAnimatedStatic(); + CUIXmlInit::InitAnimatedStatic(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUIScrollView* CScriptXmlInit::InitScrollView(LPCSTR path, CUIWindow* parent) +{ + CUIScrollView* pWnd = new CUIScrollView(); + CUIXmlInit::InitScrollView(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUIListBox* CScriptXmlInit::InitListBox(LPCSTR path, CUIWindow* parent) +{ + CUIListBox* pWnd = new CUIListBox(); + CUIXmlInit::InitListBox(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUIListWnd* CScriptXmlInit::InitListWnd(LPCSTR path, CUIWindow* parent) +{ + CUIListWnd* pWnd = new CUIListWnd(); + CUIXmlInit::InitListWnd(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUICheckButton* CScriptXmlInit::InitCheck(LPCSTR path, CUIWindow* parent) +{ + CUICheckButton* pWnd = new CUICheckButton(); + CUIXmlInit::InitCheck(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUISpinNum* CScriptXmlInit::InitSpinNum(LPCSTR path, CUIWindow* parent) +{ + CUISpinNum* pWnd = new CUISpinNum(); + CUIXmlInit::InitSpin(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUISpinFlt* CScriptXmlInit::InitSpinFlt(LPCSTR path, CUIWindow* parent) +{ + CUISpinFlt* pWnd = new CUISpinFlt(); + CUIXmlInit::InitSpin(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUISpinText* CScriptXmlInit::InitSpinText(LPCSTR path, CUIWindow* parent) +{ + CUISpinText* pWnd = new CUISpinText(); + CUIXmlInit::InitSpin(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUIComboBox* CScriptXmlInit::InitComboBox(LPCSTR path, CUIWindow* parent) +{ + CUIComboBox* pWnd = new CUIComboBox(); + CUIXmlInit::InitComboBox(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUIButton* CScriptXmlInit::InitButton(LPCSTR path, CUIWindow* parent) +{ + CUIButton* pWnd = new CUIButton(); + CUIXmlInit::InitButton(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + + +CUI3tButton* CScriptXmlInit::Init3tButton(LPCSTR path, CUIWindow* parent) +{ + CUI3tButton* pWnd = new CUI3tButton(); + CUIXmlInit::Init3tButton(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +CUITabControl* CScriptXmlInit::InitTab(LPCSTR path, CUIWindow* parent) +{ + CUITabControl* pWnd = new CUITabControl(); + CUIXmlInit::InitTabControl(m_xml, path, 0, pWnd); + _attach_child(pWnd, parent); + return pWnd; +} + +void CScriptXmlInit::ParseShTexInfo(LPCSTR xml_file) +{ + CUITextureMaster::ParseShTexInfo(xml_file); +} + + +CUIMMShniaga* CScriptXmlInit::InitMMShniaga(LPCSTR path, CUIWindow* parent) +{ + CUIMMShniaga* pWnd = new CUIMMShniaga(); + pWnd->InitShniaga (m_xml, path); + _attach_child (pWnd, parent); + return pWnd; +} + + +CUIWindow* CScriptXmlInit::InitKeyBinding(LPCSTR path, CUIWindow* parent){ + CUIKeyBinding* pWnd = new CUIKeyBinding(); + pWnd->InitFromXml (m_xml, path); + _attach_child (pWnd, parent); + return pWnd; +} + +CUITrackBar* CScriptXmlInit::InitTrackBar(LPCSTR path, CUIWindow* parent){ + CUITrackBar* pWnd = new CUITrackBar(); + CUIXmlInit::InitTrackBar (m_xml, path, 0, pWnd); + _attach_child (pWnd, parent); + return pWnd; +} + +CUIProgressBar* CScriptXmlInit::InitProgressBar(LPCSTR path, CUIWindow* parent) +{ + CUIProgressBar* pWnd = new CUIProgressBar(); + CUIXmlInit::InitProgressBar (m_xml, path, 0, pWnd); + _attach_child (pWnd, parent); + return pWnd; +} + +#pragma optimize("s",on) +void CScriptXmlInit::script_register(lua_State *L){ + module(L) + [ + class_ ("CScriptXmlInit") + .def( constructor<>()) + .def("ParseFile", (void(CScriptXmlInit::*)(LPCSTR))(&CScriptXmlInit::ParseFile)) + .def("ParseFile", (void(CScriptXmlInit::*)(LPCSTR, LPCSTR))(&CScriptXmlInit::ParseFile)) + .def("ParseShTexInfo", &CScriptXmlInit::ParseShTexInfo) + .def("InitWindow", &CScriptXmlInit::InitWindow) + .def("InitFrame", &CScriptXmlInit::InitFrame) + .def("InitFrameLine", &CScriptXmlInit::InitFrameLine) + .def("InitLabel", &CScriptXmlInit::InitLabel) + .def("InitEditBox", &CScriptXmlInit::InitEditBox) + .def("InitStatic", &CScriptXmlInit::InitStatic) + .def("InitTextWnd", &CScriptXmlInit::InitTextWnd) + .def("InitAnimStatic", &CScriptXmlInit::InitAnimStatic) + .def("Init3tButton", &CScriptXmlInit::Init3tButton) + .def("InitCheck", &CScriptXmlInit::InitCheck) + .def("InitSpinNum", &CScriptXmlInit::InitSpinNum) + .def("InitSpinFlt", &CScriptXmlInit::InitSpinFlt) + .def("InitSpinText", &CScriptXmlInit::InitSpinText) + .def("InitComboBox", &CScriptXmlInit::InitComboBox) + .def("InitButton", &CScriptXmlInit::InitButton) + .def("Init3tButton", &CScriptXmlInit::Init3tButton) + .def("InitTab", &CScriptXmlInit::InitTab) + .def("InitTrackBar", &CScriptXmlInit::InitTrackBar) + .def("InitKeyBinding", &CScriptXmlInit::InitKeyBinding) + .def("InitMMShniaga", &CScriptXmlInit::InitMMShniaga) + .def("InitScrollView", &CScriptXmlInit::InitScrollView) + .def("InitListBox", &CScriptXmlInit::InitListBox) + .def("InitList", &CScriptXmlInit::InitListWnd) + .def("InitAutoStaticGroup", &CScriptXmlInit::InitAutoStaticGroup) + .def("InitProgressBar", &CScriptXmlInit::InitProgressBar) + ]; + +} diff --git a/src/xrGameLA/ScriptXMLInit.h b/src/xrGameLA/ScriptXMLInit.h new file mode 100644 index 000000000..9a4dc2b12 --- /dev/null +++ b/src/xrGameLA/ScriptXMLInit.h @@ -0,0 +1,67 @@ +#pragma once + +#include "script_export_space.h" +#include "ui\xrUIXmlParser.h" + +class CUIWindow; +class CUIFrameWindow; +class CUIStatic; +class CUITextWnd; +class CUICheckButton; +class CUISpinNum; +class CUISpinText; +class CUISpinFlt; +class CUIComboBox; +class CUIButton; +class CUI3tButton; +class CUICheckButton; +class CUITabControl; +class CUIFrameLineWnd; +class CUILabel; +class CUIEditBox; +class CUIMultiTextStatic; +class CUIAnimatedStatic; +class CUIArtefactPanel; +class CUITrackBar; +class CUIMMShniaga; +class CUIScrollView; +class CUIListBox; +class CUIListWnd; +class CUIProgressBar; + +class CScriptXmlInit +{ +public: + void ParseFile (LPCSTR xml_file); + void ParseShTexInfo (LPCSTR xml_file); + void ParseFile (LPCSTR xml_path, LPCSTR xml_file); + void InitWindow(LPCSTR path, int index, CUIWindow* pWnd); + CUIFrameWindow* InitFrame(LPCSTR path, CUIWindow* parent); + CUIFrameLineWnd* InitFrameLine(LPCSTR path, CUIWindow* parent); + CUILabel* InitLabel(LPCSTR path, CUIWindow* parent); + CUIEditBox* InitEditBox(LPCSTR path, CUIWindow* parent); + CUIStatic* InitStatic(LPCSTR path, CUIWindow* parent); + CUIStatic* InitAnimStatic(LPCSTR path, CUIWindow* parent); + CUITextWnd* InitTextWnd(LPCSTR path, CUIWindow* parent); + CUICheckButton* InitCheck(LPCSTR path, CUIWindow* parent); + CUISpinNum* InitSpinNum(LPCSTR path, CUIWindow* parent); + CUISpinFlt* InitSpinFlt(LPCSTR path, CUIWindow* parent); + CUISpinText* InitSpinText(LPCSTR path, CUIWindow* parent); + CUIComboBox* InitComboBox(LPCSTR path, CUIWindow* parent); + CUIButton* InitButton(LPCSTR path, CUIWindow* parent); + CUI3tButton* Init3tButton(LPCSTR path, CUIWindow* parent); + CUITabControl* InitTab(LPCSTR path, CUIWindow* parent); + CUITrackBar* InitTrackBar(LPCSTR path, CUIWindow* parent); + CUIMMShniaga* InitMMShniaga(LPCSTR path, CUIWindow* parent); + CUIWindow* InitKeyBinding(LPCSTR path, CUIWindow* parent); + CUIScrollView* InitScrollView(LPCSTR path, CUIWindow* parent); + CUIListBox* InitListBox(LPCSTR path, CUIWindow* parent); + CUIListWnd* InitListWnd(LPCSTR path, CUIWindow* parent); + CUIProgressBar* InitProgressBar(LPCSTR path, CUIWindow* parent); + void InitAutoStaticGroup(LPCSTR path, CUIWindow* parent); + +protected: + CUIXml m_xml; +public: + DECLARE_SCRIPT_REGISTER_FUNCTION +}; diff --git a/src/xrGameLA/ShapeData.h b/src/xrGameLA/ShapeData.h new file mode 100644 index 000000000..0e7c67936 --- /dev/null +++ b/src/xrGameLA/ShapeData.h @@ -0,0 +1,24 @@ +#ifndef ShapeDataH +#define ShapeDataH + +struct CShapeData +{ + enum{ + cfSphere=0, + cfBox + }; + union shape_data + { + Fsphere sphere; + Fmatrix box; + }; + struct shape_def + { + u8 type; + shape_data data; + }; + DEFINE_VECTOR (shape_def,ShapeVec,ShapeIt); + ShapeVec shapes; +}; + +#endif \ No newline at end of file diff --git a/src/xrGameLA/ShellHit.cpp b/src/xrGameLA/ShellHit.cpp new file mode 100644 index 000000000..2918d61ea --- /dev/null +++ b/src/xrGameLA/ShellHit.cpp @@ -0,0 +1,65 @@ +#include "StdAfx.h" +#include "PHDynamicData.h" +#include "Physics.h" +#include "tri-colliderknoopc/dTriList.h" +#include "PHShellSplitter.h" +#include "PHFracture.h" +#include "PHJointDestroyInfo.h" +#include "ExtendedGeom.h" + +#include "PHElement.h" +#include "PHShell.h" + + +void CPHShell::applyHit(const Fvector& pos, const Fvector& dir, float val,const u16 id,ALife::EHitType hit_type) +{ + if(id==u16(-1)) return;// +#pragma todo("Kosya to kosya:this code shold treat all hit types") + if(!m_pKinematics) + { + applyImpulseTrace(pos,dir,val); + return; + } + switch(hit_type) { + case ALife::eHitTypeExplosion: + ExplosionHit(pos,dir,val,id); + break; + default: applyImpulseTrace(pos,dir,val,id); + } +} + +void CPHShell::ExplosionHit(const Fvector& pos, const Fvector& dir, float val,const u16 id) +{ + if(!isActive()) return; + EnableObject(0); + //Fvector local_pos;local_pos.set(0.f,0.f,0.f); + ELEMENT_I i=elements.begin(),e=elements.end(); + float impulse=val/_sqrt(_sqrt((float)elements.size())); + for(;i!=e;i++) + { + //Fvector max_area_dir; + CPHElement* element=(*i); + //element->get_MaxAreaDir(max_area_dir); + //float sign=max_area_dir.dotproduct(dir)>0.f ? 1.f : -1.f; + //max_area_dir.mul(sign); + u16 gn=element->CPHGeometryOwner::numberOfGeoms(); + float g_impulse=impulse/gn; + for(u16 j=0;jgetRadius(); + r_box.set(rad,rad,rad); + r_pos.random_point(r_box); + r_dir.random_dir(); + if(!fis_zero(pos.magnitude(),EPS_L)) + { + r_dir.mul(0.5f); + r_dir.add(dir); + } + + r_dir.normalize_safe();//safe??? + element->applyImpulseTrace(r_pos,r_dir,g_impulse,element->CPHGeometryOwner::Geom(j)->bone_id()); + } + } +} diff --git a/src/xrGameLA/ShootingHitEffector.h b/src/xrGameLA/ShootingHitEffector.h new file mode 100644 index 000000000..dcc8141d3 --- /dev/null +++ b/src/xrGameLA/ShootingHitEffector.h @@ -0,0 +1,55 @@ +////////////////////////////////////////////////////////////////////// +// ShootingHitEffector.h: эффектор, который запускается во время атаки актера +////////////////////////////////////////////////////////////////////// + + + #pragma once + +#include "../effectorPP.h" +#include "../effector.h" + + // CMonsterEffector + class CShootingHitEffectorPP : public CEffectorPP { + typedef CEffectorPP inherited; + + SPPInfo state; // current state + float m_total; // total PP time + float m_attack; // attack time in percents [0..1] + float m_release; // release time in percents [0..1] + +public: + CShootingHitEffectorPP (const SPPInfo &ppi, float life_time, float attack_time = 0.0f, float release_time = 0.0f); + virtual BOOL Process (SPPInfo& pp); + }; +/* +// CMonsterEffectorHit +class CShootingHitEffector : public CEffector { + typedef CEffector inherited; + + float total; + float max_amp; + float period_number; + float power; + + Fvector offset; + public: + CShootingHitEffector (float time, float amp, float periods, float power); + virtual BOOL Process (Fvector &p, Fvector &d, Fvector &n, float& fFov, float& fFar, float& fAspect); + }; +*/ + +struct SShootingEffector { + SPPInfo ppi; + float time; + float time_attack; + float time_release; + + // camera effects + float ce_time; + float ce_amplitude; + float ce_period_number; + float ce_power; + }; + + + diff --git a/src/xrGameLA/ShootingObject.cpp b/src/xrGameLA/ShootingObject.cpp new file mode 100644 index 000000000..7258fb9ea --- /dev/null +++ b/src/xrGameLA/ShootingObject.cpp @@ -0,0 +1,507 @@ +////////////////////////////////////////////////////////////////////// +// ShootingObject.cpp: интерфейс для семейства стреляющих объектов +// (оружие и осколочные гранаты) +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ShootingObject.h" +#include "ParticlesObject.h" +#include "WeaponAmmo.h" +#include "actor.h" +#include "game_cl_base.h" +#include "level.h" +#include "level_bullet_manager.h" +#include "clsid_game.h" +#include "game_cl_single.h" + +#define HIT_POWER_EPSILON 0.05f +#define WALLMARK_SIZE 0.04f + +CShootingObject::CShootingObject(void) +{ + fTime = 0; + fTimeToFire = 0; + //fHitPower = 0.0f; + fvHitPower.set (0.0f,0.0f,0.0f,0.0f); + fvHitPowerCritical.set (0.0f,0.0f,0.0f,0.0f); + m_fStartBulletSpeed = 1000.f; + + m_vCurrentShootDir.set (0,0,0); + m_vCurrentShootPos.set (0,0,0); + m_iCurrentParentID = 0xFFFF; + + m_fPredBulletTime = 0.0f; + m_bUseAimBullet = false; + m_fTimeToAim = 0.0f; + + //particles + m_sFlameParticlesCurrent = m_sFlameParticles = NULL; + m_sSmokeParticlesCurrent = m_sSmokeParticles = NULL; + m_sShellParticles = NULL; + + bWorking = false; + + light_render = 0; + + reinit(); + +} +CShootingObject::~CShootingObject(void) +{ +} + +void CShootingObject::reinit() +{ + m_pFlameParticles = NULL; +} + +void CShootingObject::Load (LPCSTR section) +{ + if(pSettings->line_exist(section,"light_disabled")) + { + m_bLightShotEnabled = !pSettings->r_bool(section,"light_disabled"); + }else + m_bLightShotEnabled = true; + + //время затрачиваемое на выстрел + fTimeToFire = pSettings->r_float (section,"rpm"); + VERIFY(fTimeToFire>0.f); + fTimeToFire = 60.f / fTimeToFire; + + LoadFireParams (section, ""); + LoadLights (section, ""); + LoadShellParticles (section, ""); + LoadFlameParticles (section, ""); + + m_air_resistance_factor = READ_IF_EXISTS(pSettings,r_float,section,"air_resistance_factor",1.f); +} + +void CShootingObject::Light_Create () +{ + //lights + light_render = ::Render->light_create(); + if (::Render->get_generation()==IRender_interface::GENERATION_R2) light_render->set_shadow (true); + else light_render->set_shadow (false); +} + +void CShootingObject::Light_Destroy () +{ + light_render.destroy (); +} + +void CShootingObject::LoadFireParams (LPCSTR section, LPCSTR prefix) +{ + string256 full_name; + string256 full_name_critical; + string32 buffer; + shared_str s_sHitPower; + shared_str s_sHitPowerCritical; + + //базовая дисперсия оружия + fireDispersionBase = deg2rad( pSettings->r_float (section,"fire_dispersion_base" ) ); + + //сила выстрела и его мощьность + s_sHitPower = pSettings->r_string_wb(section,strconcat(sizeof(full_name),full_name, prefix, "hit_power"));//читаем строку силы хита пули оружия + s_sHitPowerCritical = pSettings->r_string_wb(section, strconcat(sizeof(full_name_critical), full_name_critical, prefix, "hit_power_critical")); + fvHitPower[egdMaster] = (float)atof(_GetItem(*s_sHitPower,0,buffer));//первый параметр - это хит для уровня игры мастер + fvHitPowerCritical[egdMaster] = (float)atof(_GetItem(*s_sHitPowerCritical,0,buffer));//первый параметр - это хит для уровня игры мастер + + fvHitPower[egdNovice] = fvHitPower[egdStalker] = fvHitPower[egdVeteran] = fvHitPower[egdMaster];//изначально параметры для других уровней сложности такие же + fvHitPowerCritical[egdNovice] = fvHitPowerCritical[egdStalker] = fvHitPowerCritical[egdVeteran] = fvHitPowerCritical[egdMaster];//изначально параметры для других уровней сложности такие же + + int num_game_diff_param=_GetItemCount(*s_sHitPower);//узнаём колличество параметров для хитов + if (num_game_diff_param>1)//если задан второй параметр хита + { + fvHitPower[egdVeteran] = (float)atof(_GetItem(*s_sHitPower,1,buffer));//то вычитываем его для уровня ветерана + } + if (num_game_diff_param>2)//если задан третий параметр хита + { + fvHitPower[egdStalker] = (float)atof(_GetItem(*s_sHitPower,2,buffer));//то вычитываем его для уровня сталкера + } + if (num_game_diff_param>3)//если задан четвёртый параметр хита + { + fvHitPower[egdNovice] = (float)atof(_GetItem(*s_sHitPower,3,buffer));//то вычитываем его для уровня новичка + } + + num_game_diff_param=_GetItemCount(*s_sHitPowerCritical);//узнаём колличество параметров + if (num_game_diff_param>1)//если задан второй параметр хита + { + fvHitPowerCritical[egdVeteran] = (float)atof(_GetItem(*s_sHitPowerCritical,1,buffer));//то вычитываем его для уровня ветерана + } + if (num_game_diff_param>2)//если задан третий параметр хита + { + fvHitPowerCritical[egdStalker] = (float)atof(_GetItem(*s_sHitPowerCritical,2,buffer));//то вычитываем его для уровня сталкера + } + if (num_game_diff_param>3)//если задан четвёртый параметр хита + { + fvHitPowerCritical[egdNovice] = (float)atof(_GetItem(*s_sHitPowerCritical,3,buffer));//то вычитываем его для уровня новичка + } + + fHitImpulse = pSettings->r_float (section, "hit_impulse" ); + //максимальное расстояние полета пули + fireDistance = pSettings->r_float (section,strconcat(sizeof(full_name),full_name, prefix, "fire_distance")); + //начальная скорость пули + m_fStartBulletSpeed = pSettings->r_float (section,strconcat(sizeof(full_name),full_name, prefix, "bullet_speed")); + m_bUseAimBullet = pSettings->r_bool (section,strconcat(sizeof(full_name),full_name, prefix, "use_aim_bullet")); + if (m_bUseAimBullet) + { + m_fTimeToAim = pSettings->r_float (section,strconcat(sizeof(full_name),full_name, prefix, "time_to_aim")); + } +} + +void CShootingObject::LoadLights (LPCSTR section, LPCSTR prefix) +{ + string256 full_name; + // light + if(m_bLightShotEnabled) + { + Fvector clr = pSettings->r_fvector3 (section, strconcat(sizeof(full_name),full_name, prefix, "light_color")); + light_base_color.set(clr.x,clr.y,clr.z,1); + light_base_range = pSettings->r_float (section, strconcat(sizeof(full_name),full_name, prefix, "light_range") ); + light_var_color = pSettings->r_float (section, strconcat(sizeof(full_name),full_name, prefix, "light_var_color") ); + light_var_range = pSettings->r_float (section, strconcat(sizeof(full_name),full_name, prefix, "light_var_range") ); + light_lifetime = pSettings->r_float (section, strconcat(sizeof(full_name),full_name, prefix, "light_time") ); + light_time = -1.f; + } +} + +void CShootingObject::Light_Start () +{ + if(!light_render) Light_Create(); + + if (Device.dwFrame != light_frame) + { + light_frame = Device.dwFrame; + light_time = light_lifetime; + + light_build_color.set (Random.randFs(light_var_color,light_base_color.r),Random.randFs(light_var_color,light_base_color.g),Random.randFs(light_var_color,light_base_color.b),1); + light_build_range = Random.randFs(light_var_range,light_base_range); + } +} + +void CShootingObject::Light_Render (const Fvector& P) +{ + float light_scale = light_time/light_lifetime; + R_ASSERT(light_render); + + light_render->set_position (P); + light_render->set_color (light_build_color.r*light_scale,light_build_color.g*light_scale,light_build_color.b*light_scale); + light_render->set_range (light_build_range*light_scale); + + if( !light_render->get_active() ) + { + light_render->set_active (true); + } +} + + +////////////////////////////////////////////////////////////////////////// +// Particles +////////////////////////////////////////////////////////////////////////// + +void CShootingObject::StartParticles (CParticlesObject*& pParticles, LPCSTR particles_name, + const Fvector& pos, const Fvector& vel, bool auto_remove_flag) +{ + if(!particles_name) return; + if(pParticles != NULL) + { + UpdateParticles(pParticles, pos, vel); + return; + } + pParticles = CParticlesObject::Create(particles_name,(BOOL)auto_remove_flag); + UpdateParticles(pParticles, pos, vel); + bool in_hud_mode = IsHudModeNow(); + pParticles->Play(in_hud_mode); +} + +void CShootingObject::StopParticles (CParticlesObject*& pParticles) +{ + if(pParticles == NULL) return; + + pParticles->Stop (); + CParticlesObject::Destroy(pParticles); +} + +void CShootingObject::UpdateParticles (CParticlesObject*& pParticles, + const Fvector& pos, const Fvector& vel) +{ + if(!pParticles) return; + + Fmatrix particles_pos; + particles_pos.set (get_ParticlesXFORM()); + particles_pos.c.set (pos); + + pParticles->SetXFORM(particles_pos); + + if(!pParticles->IsAutoRemove() && !pParticles->IsLooped() + && !pParticles->PSI_alive()) + { + pParticles->Stop (); + CParticlesObject::Destroy(pParticles); + } +} + + +void CShootingObject::LoadShellParticles (LPCSTR section, LPCSTR prefix) +{ + string256 full_name; + strconcat(sizeof(full_name),full_name, prefix, "shell_particles"); + + if(pSettings->line_exist(section,full_name)) + { + m_sShellParticles = pSettings->r_string (section,full_name); + vLoadedShellPoint = pSettings->r_fvector3 (section,strconcat(sizeof(full_name),full_name, prefix, "shell_point")); + } +} + +void CShootingObject::LoadFlameParticles (LPCSTR section, LPCSTR prefix) +{ + string256 full_name; + + // flames + strconcat(sizeof(full_name),full_name, prefix, "flame_particles"); + if(pSettings->line_exist(section, full_name)) + m_sFlameParticles = pSettings->r_string (section, full_name); + + strconcat(sizeof(full_name),full_name, prefix, "smoke_particles"); + if(pSettings->line_exist(section, full_name)) + m_sSmokeParticles = pSettings->r_string (section, full_name); + + strconcat(sizeof(full_name),full_name, prefix, "shot_particles"); + if(pSettings->line_exist(section, full_name)) + m_sShotParticles = pSettings->r_string (section, full_name); + + + //текущие партиклы + m_sFlameParticlesCurrent = m_sFlameParticles; + m_sSmokeParticlesCurrent = m_sSmokeParticles; +} + + +void CShootingObject::OnShellDrop (const Fvector& play_pos, + const Fvector& parent_vel) +{ + if(!m_sShellParticles) return; + if( Device.vCameraPosition.distance_to_sqr(play_pos)>2*2 ) return; + + CParticlesObject* pShellParticles = CParticlesObject::Create(*m_sShellParticles,TRUE); + + Fmatrix particles_pos; + particles_pos.set (get_ParticlesXFORM()); + particles_pos.c.set (play_pos); + + pShellParticles->UpdateParent (particles_pos, parent_vel); + bool in_hud_mode = IsHudModeNow(); + pShellParticles->Play (in_hud_mode); +} + + +//партиклы дыма +void CShootingObject::StartSmokeParticles (const Fvector& play_pos, + const Fvector& parent_vel) +{ + CParticlesObject* pSmokeParticles = NULL; + StartParticles(pSmokeParticles, *m_sSmokeParticlesCurrent, play_pos, parent_vel, true); +} + + +void CShootingObject::StartFlameParticles () +{ + if(0==m_sFlameParticlesCurrent.size()) return; + + //если партиклы циклические + if(m_pFlameParticles && m_pFlameParticles->IsLooped() && + m_pFlameParticles->IsPlaying()) + { + UpdateFlameParticles(); + return; + } + + StopFlameParticles(); + m_pFlameParticles = CParticlesObject::Create(*m_sFlameParticlesCurrent,FALSE); + UpdateFlameParticles(); + bool in_hud_mode = IsHudModeNow(); + m_pFlameParticles->Play(in_hud_mode); + +} +void CShootingObject::StopFlameParticles () +{ + if(0==m_sFlameParticlesCurrent.size()) return; + if(m_pFlameParticles == NULL) return; + + m_pFlameParticles->SetAutoRemove(true); + m_pFlameParticles->Stop(); + m_pFlameParticles = NULL; +} + +void CShootingObject::UpdateFlameParticles () +{ + if(0==m_sFlameParticlesCurrent.size()) return; + if(!m_pFlameParticles) return; + + Fmatrix pos; + pos.set (get_ParticlesXFORM() ); + pos.c.set (get_CurrentFirePoint() ); + + VERIFY(_valid(pos)); + + m_pFlameParticles->SetXFORM (pos); + + if(!m_pFlameParticles->IsLooped() && + !m_pFlameParticles->IsPlaying() && + !m_pFlameParticles->PSI_alive()) + { + m_pFlameParticles->Stop(); + CParticlesObject::Destroy(m_pFlameParticles); + } +} + +//подсветка от выстрела +void CShootingObject::UpdateLight() +{ + if (light_render && light_time>0) + { + light_time -= Device.fTimeDelta; + if (light_time<=0) StopLight(); + } +} + +void CShootingObject::StopLight () +{ + if(light_render){ + light_render->set_active(false); + } +} + +void CShootingObject::RenderLight() +{ + if ( light_render && light_time>0 ) + { + Light_Render(get_CurrentFirePoint()); + } +} + +bool CShootingObject::SendHitAllowed (CObject* pUser) +{ + if (Game().IsServerControlHits()) + return OnServer(); + + if (OnServer()) + { + if (pUser->CLS_ID == CLSID_OBJECT_ACTOR) + { + if (Level().CurrentControlEntity() != pUser) + { + return false; + } + } + return true; + } + else + { + if (pUser->CLS_ID == CLSID_OBJECT_ACTOR) + { + if (Level().CurrentControlEntity() == pUser) + { + return true; + } + } + return false; + } +}; + +extern void random_dir(Fvector& tgt_dir, const Fvector& src_dir, float dispersion); + +void CShootingObject::FireBullet(const Fvector& pos, + const Fvector& shot_dir, + float fire_disp, + const CCartridge& cartridge, + u16 parent_id, + u16 weapon_id, + bool send_hit) +{ + Fvector dir; + random_dir(dir,shot_dir,fire_disp); + + m_vCurrentShootDir = dir; + m_vCurrentShootPos = pos; + m_iCurrentParentID = parent_id; + + bool aim_bullet; + if (m_bUseAimBullet) + { + if (ParentMayHaveAimBullet()) + { + if (m_fPredBulletTime==0.0) + { + aim_bullet=true; + } + else + { + if ((Device.fTimeGlobal-m_fPredBulletTime)>=m_fTimeToAim) + { + aim_bullet=true; + } + else + { + aim_bullet=false; + } + } + } + else + { + aim_bullet=false; + } + } + else + { + aim_bullet=false; + } + m_fPredBulletTime = Device.fTimeGlobal; + + float l_fHitPower = 0.0f; + if (ParentIsActor())//если из оружия стреляет актёр(игрок) + { + if (GameID() == GAME_SINGLE) + { + l_fHitPower=fvHitPower[g_SingleGameDifficulty]; + } + else + { + l_fHitPower=fvHitPower[egdMaster]; + } + } + else + { + l_fHitPower=fvHitPower[egdMaster]; + } + + Level().BulletManager().AddBullet( pos, + dir, + m_fStartBulletSpeed * cur_silencer_koef.bullet_speed, + l_fHitPower * cur_silencer_koef.hit_power, + fHitImpulse * cur_silencer_koef.hit_impulse, + parent_id, + weapon_id, + ALife::eHitTypeFireWound, + fireDistance, + cartridge, + m_air_resistance_factor, + send_hit, + aim_bullet); +} +void CShootingObject::FireStart () +{ + bWorking=true; +} +void CShootingObject::FireEnd () +{ + bWorking=false; +} + +void CShootingObject::StartShotParticles () +{ + CParticlesObject* pSmokeParticles = NULL; + StartParticles(pSmokeParticles, *m_sShotParticles, + m_vCurrentShootPos, m_vCurrentShootDir, true); +} \ No newline at end of file diff --git a/src/xrGameLA/ShootingObject.h b/src/xrGameLA/ShootingObject.h new file mode 100644 index 000000000..27431e333 --- /dev/null +++ b/src/xrGameLA/ShootingObject.h @@ -0,0 +1,201 @@ +////////////////////////////////////////////////////////////////////// +// ShootingObject.h: интерфейс для семейства стреляющих объектов +// (оружие и осколочные гранаты) +// обеспечивает набор хитов, звуков рикошетп +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "alife_space.h" +#include "../render.h" + +class CCartridge; +class CParticlesObject; +class IRender_Sector; + +extern const Fvector zero_vel; + +#define WEAPON_MATERIAL_NAME "objects\\bullet" + +class CShootingObject +{ +protected: + CShootingObject (); + virtual ~CShootingObject (); + + void reinit (); + void reload (LPCSTR section) {}; + void Load (LPCSTR section); + + Fvector m_vCurrentShootDir; + Fvector m_vCurrentShootPos; + + float m_air_resistance_factor; + +protected: + //ID персонажа который иницировал действие + u16 m_iCurrentParentID; + + +////////////////////////////////////////////////////////////////////////// +// Fire Params +////////////////////////////////////////////////////////////////////////// +protected: + virtual void LoadFireParams (LPCSTR section, LPCSTR prefix); + virtual bool SendHitAllowed (CObject* pUser); + virtual void FireBullet (const Fvector& pos, + const Fvector& dir, + float fire_disp, + const CCartridge& cartridge, + u16 parent_id, + u16 weapon_id, + bool send_hit); + void SetBulletSpeed(float new_speed) {m_fStartBulletSpeed = new_speed;} + float GetBulletSpeed() {return m_fStartBulletSpeed;} + + virtual void FireStart (); + virtual void FireEnd (); +public: + IC BOOL IsWorking () const {return bWorking;} + virtual BOOL ParentMayHaveAimBullet() {return FALSE;} + virtual BOOL ParentIsActor() {return FALSE;} + +protected: + // Weapon fires now + bool bWorking; + + float fTimeToFire; + Fvector4 fvHitPower; + Fvector4 fvHitPowerCritical; + float fHitImpulse; + + //скорость вылета пули из ствола + float m_fStartBulletSpeed; + //максимальное расстояние стрельбы + float fireDistance; + + //рассеивание во время стрельбы + float fireDispersionBase; + + //счетчик времени, затрачиваемого на выстрел + float fTime; + + struct SilencerKoeffs // value *= koef; + { + float hit_power; + float hit_impulse; + float bullet_speed; + float fire_dispersion; + float cam_dispersion; + float cam_disper_inc; + + SilencerKoeffs() { Reset(); } + IC void Reset() + { + hit_power = 1.0f; + hit_impulse = 1.0f; + bullet_speed = 1.0f; + fire_dispersion = 1.0f; + cam_dispersion = 1.0f; + cam_disper_inc = 1.0f; + } + };// SilencerKoeffs + SilencerKoeffs m_silencer_koef; + +public: + SilencerKoeffs cur_silencer_koef; + +protected: + //для сталкеров, чтоб они знали эффективные границы использования + //оружия + float m_fMinRadius; + float m_fMaxRadius; + + +////////////////////////////////////////////////////////////////////////// +// Lights +////////////////////////////////////////////////////////////////////////// +protected: + Fcolor light_base_color; + float light_base_range; + Fcolor light_build_color; + float light_build_range; + ref_light light_render; + float light_var_color; + float light_var_range; + float light_lifetime; + u32 light_frame; + float light_time; + //включение подсветки во время выстрела + bool m_bLightShotEnabled; +protected: + void Light_Create (); + void Light_Destroy (); + + void Light_Start (); + void Light_Render (const Fvector& P); + + virtual void LoadLights (LPCSTR section, LPCSTR prefix); + virtual void RenderLight (); + virtual void UpdateLight (); + virtual void StopLight (); + virtual bool IsHudModeNow () { return false; }; + +////////////////////////////////////////////////////////////////////////// +// партикловая система +////////////////////////////////////////////////////////////////////////// +protected: + //функции родительского объекта + virtual const Fvector& get_CurrentFirePoint() = 0; + virtual const Fmatrix& get_ParticlesXFORM() = 0; + virtual void ForceUpdateFireParticles (){}; + + //////////////////////////////////////////////// + //общие функции для работы с партиклами оружия + virtual void StartParticles (CParticlesObject*& pParticles, LPCSTR particles_name, const Fvector& pos, const Fvector& vel = zero_vel, bool auto_remove_flag = false); + virtual void StopParticles (CParticlesObject*& pParticles); + virtual void UpdateParticles (CParticlesObject*& pParticles, const Fvector& pos, const Fvector& vel = zero_vel); + + virtual void LoadShellParticles (LPCSTR section, LPCSTR prefix); + virtual void LoadFlameParticles (LPCSTR section, LPCSTR prefix); + + //////////////////////////////////////////////// + //спецефические функции для партиклов + //партиклы огня + virtual void StartFlameParticles (); + virtual void StopFlameParticles (); + virtual void UpdateFlameParticles(); + + //партиклы дыма + virtual void StartSmokeParticles (const Fvector& play_pos, + const Fvector& parent_vel); + + //партиклы полосы от пули + virtual void StartShotParticles (); + + //партиклы гильз + virtual void OnShellDrop (const Fvector& play_pos, + const Fvector& parent_vel); +protected: + //имя пратиклов для гильз + shared_str m_sShellParticles; +public: + Fvector vLoadedShellPoint; + float m_fPredBulletTime; + float m_fTimeToAim; + BOOL m_bUseAimBullet; +protected: + //имя пратиклов для огня + shared_str m_sFlameParticlesCurrent; + //для выстрела 1м и 2м видом стрельбы + shared_str m_sFlameParticles; + //объект партиклов огня + CParticlesObject* m_pFlameParticles; + + //имя пратиклов для дыма + shared_str m_sSmokeParticlesCurrent; + shared_str m_sSmokeParticles; + + //имя партиклов следа от пули + shared_str m_sShotParticles; +}; diff --git a/src/xrGameLA/Silencer.cpp b/src/xrGameLA/Silencer.cpp new file mode 100644 index 000000000..c5e2afe8e --- /dev/null +++ b/src/xrGameLA/Silencer.cpp @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////// +// Silencer.cpp +// Silencer - апгрейд оружия глушитель +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "silencer.h" +//#include "PhysicsShell.h" + +CSilencer::CSilencer() +{ +} + +CSilencer::~CSilencer() +{ +} + +BOOL CSilencer::net_Spawn(CSE_Abstract* DC) +{ + return (inherited::net_Spawn(DC)); +} + +void CSilencer::Load(LPCSTR section) +{ + inherited::Load(section); +} + +void CSilencer::net_Destroy() +{ + inherited::net_Destroy(); +} + +void CSilencer::UpdateCL() +{ + inherited::UpdateCL(); +} + +void CSilencer::OnH_A_Chield() +{ + inherited::OnH_A_Chield (); +} + +void CSilencer::OnH_B_Independent(bool just_before_destroy) +{ + inherited::OnH_B_Independent(just_before_destroy); +} + +void CSilencer::renderable_Render() +{ + inherited::renderable_Render(); +} \ No newline at end of file diff --git a/src/xrGameLA/Silencer.h b/src/xrGameLA/Silencer.h new file mode 100644 index 000000000..0d56ee2f4 --- /dev/null +++ b/src/xrGameLA/Silencer.h @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////// +// Silencer.h +// Silencer - апгрейд оружия глушитель +/////////////////////////////////////////////////////////////// + +#pragma once +#include "inventory_item_object.h" + +class CSilencer : public CInventoryItemObject { +private: + typedef CInventoryItemObject inherited; +public: + CSilencer (void); + virtual ~CSilencer(void); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void Load (LPCSTR section); + virtual void net_Destroy (); + + virtual void OnH_A_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + + virtual void UpdateCL (); + virtual void renderable_Render (); + +}; \ No newline at end of file diff --git a/src/xrGameLA/SimpleDetector.cpp b/src/xrGameLA/SimpleDetector.cpp new file mode 100644 index 000000000..dc462f92b --- /dev/null +++ b/src/xrGameLA/SimpleDetector.cpp @@ -0,0 +1,12 @@ +#include "stdafx.h" +#include "simpledetector.h" + +CSimpleDetectorR::CSimpleDetectorR(void) +{ + m_weight = .5f; + //m_belt = true; +} + +CSimpleDetectorR::~CSimpleDetectorR(void) +{ +} diff --git a/src/xrGameLA/SimpleDetector.h b/src/xrGameLA/SimpleDetector.h new file mode 100644 index 000000000..e493cee0e --- /dev/null +++ b/src/xrGameLA/SimpleDetector.h @@ -0,0 +1,10 @@ +#pragma once +#include "customdetector.h" + +class CSimpleDetectorR : + public CCustomDetector +{ +public: + CSimpleDetectorR(void); + virtual ~CSimpleDetectorR(void); +}; diff --git a/src/xrGameLA/SimpleDetector2.cpp b/src/xrGameLA/SimpleDetector2.cpp new file mode 100644 index 000000000..2d65d5965 --- /dev/null +++ b/src/xrGameLA/SimpleDetector2.cpp @@ -0,0 +1,418 @@ +#include "stdafx.h" +#include "simpledetector2.h" +#include "ui/ArtefactDetectorUI.h" +//#include "../Include/xrRender/Kinematics.h" +//#include "../LightAnimLibrary.h" +#include "player_hud.h" +//#include "clsid_game.h" +#include "artifact.h" +//#include "ai\monsters\ai_monster_utils.h" + +CSimpleDetector::CSimpleDetector(void) +{ +} + +CSimpleDetector::~CSimpleDetector(void) +{} + +void CSimpleDetector::CreateUI() +{ + R_ASSERT(NULL==m_ui); + m_ui = new CUIArtefactDetectorSimple(); + ui().construct (this); + + if (for_test){ + Fvector P; + P.set(this->Position()); + feel_touch_update(P, fortestrangetocheck); + + + for (xr_vector::iterator it = feel_touch.begin(); it != feel_touch.end(); it++) + { + CObject* item = *it; + if (item){ + Fvector dir, to; + + CInventoryItem* invitem = smart_cast(item); + item->Center(to); + float range = dir.sub(to, this->Position()).magnitude(); + Msg("Artefact %s, distance is %f", invitem->object().cNameSect().c_str(), range); + } + } + + } +} + +//---------------------------------------------------------------------- +void CSimpleDetector::feel_touch_new (CObject* O) +{ +} + +void CSimpleDetector::feel_touch_delete (CObject* O) +{ +} + +BOOL CSimpleDetector::feel_touch_contact (CObject *O) +{ + + bool is_visibleforDetector = false; + CArtefact* artefact = smart_cast(O); + + if (artefact) + { + for (u16 id = 0; id < af_types.size(); ++id) + { + + if ((xr_strcmp(O->cNameSect(), af_types[id]) == 0) || (xr_strcmp(af_types[id], "all") == 0)) + { + return true; + } + } + return false; + } + else{ return false; } + +} + +//---------------------------------------------------------------------- + +void CSimpleDetector::UpdateAf() +{ + LPCSTR closest_art = "null"; + CArtefact* pCurrentAf; + feel_touch_update_delay = feel_touch_update_delay + 1; + if (feel_touch_update_delay >= 5)//Как то снизить нагрузку на кадр поможет + { + feel_touch_update_delay = 0; + Fvector P; + P.set(this->Position()); + feel_touch_update(P, foverallrangetocheck); + } + + float disttoclosestart = 0.0; + + for (xr_vector::iterator it = feel_touch.begin(); it != feel_touch.end(); it++) + { + float disttoart = DetectorFeel(*it); + if (disttoart != -10.0) + { + + //Если переменная досих пор не заданаа(первый проход цикла), то даем ей значение + if (disttoclosestart <= 0.0) + { + disttoclosestart = disttoart; + closest_art = closestart; + pCurrentAf = smart_cast(*it); + } + //нашли более близкий арт... + if (disttoclosestart > disttoart) + { + disttoclosestart = disttoart; + closest_art = closestart; + pCurrentAf = smart_cast(*it); + } + } + } + + //определить текущую частоту срабатывания сигнала + if (disttoclosestart == 0.0) + { + return; + } + else + { + cur_periodperiod = disttoclosestart / (fdetect_radius * pCurrentAf->detect_radius_koef); + } + + //чтобы не перегружать звук. движок + if (cur_periodperiod < 0.11) + { + cur_periodperiod = 0.11; + } + + if (snd_timetime > cur_periodperiod) + { + if (!reaction_sound_off) + { + if (detect_sndsnd_line || pCurrentAf->custom_detect_sound_string) + { + //Добавил врзможность задать разные звуки для различных артов + freqq = 1.8 - cur_periodperiod; + + if (freqq < 0.8) + freqq = 0.8; + + if (pCurrentAf->custom_detect_sound_string) + { + pCurrentAf->custom_detect_sound.play_at_pos(this, this->Position()); + pCurrentAf->custom_detect_sound.set_frequency(freqq); + } + else if (detect_sndsnd_line) + { + detect_snd.play_at_pos(this, this->Position()); + detect_snd.set_frequency(freqq); + } + + snd_timetime = 0; + } + } + ui().Flash(true, cur_periodperiod/10); + } + else + snd_timetime += Device.fTimeDelta; +} + +//Просто для удобства вынес в отдельную функцию, а то и так месево там + +float CSimpleDetector::DetectorFeel(CObject* item) +{ + Fvector dir, to; + + item->Center(to); + float range = dir.sub(to, Position()).magnitude(); + CInventoryItem* invitem = smart_cast(item); + CArtefact* artefact = smart_cast(item); + + float gogogo = fdetect_radius * artefact->detect_radius_koef; + if (rangeobject().cNameSect_str(), invitem->object().cNameSect().c_str()); + closestart = invitem->object().cNameSect_str(); + return range; + } + return -10.0; +} + +//---------------UI(Лампочки)------------------ +CUIArtefactDetectorSimple& CSimpleDetector::ui() +{ + return *((CUIArtefactDetectorSimple*)m_ui); +} + + +void CUIArtefactDetectorSimple::construct(CSimpleDetector* p) +{ + m_parent = p; + m_flash_bone = BI_NONE; + m_on_off_bone = BI_NONE; + Flash (false,0.0f); +} + +CUIArtefactDetectorSimple::~CUIArtefactDetectorSimple() +{ + //Svet_Dosviduli (m_flash_light); + //Svet_Dosviduli (m_on_off_light); +} + +// Функция срытия показа кости у модели, берется название кости в виде строки и модель. +void CUIArtefactDetectorSimple::SetBoneVisible(IKinematics* model, CSimpleDetector* parent, LPCSTR bonename, bool visible, bool someshit) +{ + if(!parent->HudItemData()) return; + + u16 bone = BI_NONE; + + IKinematics* modeltouse = model; + R_ASSERT (modeltouse); + R_ASSERT (bone==BI_NONE); + bone = model->LL_BoneID (bonename); + if(visible == true) + { + model->LL_SetBoneVisible(bone, TRUE, TRUE); + }else + { + model->LL_SetBoneVisible(bone, FALSE, TRUE); + } +} + +// Функция звукового сигнала +//void CUIArtefactDetectorSimple::ZvukovojSignal(HUD_SOUND_ITEM sound, float fvector1, float fvector2, float fvector3, CSimpleDetector* parent, bool bool1, bool bool2) +//{ +//} + +// Функция смены частоты звука +//void CUIArtefactDetectorSimple::ZvukovojSignalChastota(HUD_SOUND_ITEM sound, float freq) +//{ +//} + +// Функция создания света +ref_light CUIArtefactDetectorSimple::Svet_Sozdat() +{ + ref_light lightvar; + //R_ASSERT (!light); + lightvar = ::Render->light_create(); + lightvar->set_type (IRender_Light::POINT); + return (lightvar); +} + +// Функция тень света +void CUIArtefactDetectorSimple::Svet_Ten(ref_light light, bool danet) +{ + if(danet == true) + { + light->set_shadow (true); + }else + { + light->set_shadow (false); + } +} + +// Функция область света +void CUIArtefactDetectorSimple::Svet_Dalnost(ref_light light, float range) +{ + light->set_range (range); +} + +// Функция худ-мод света +void CUIArtefactDetectorSimple::Svet_HUDmode(ref_light light, bool danet) +{ + if(danet == true) + { + light->set_hud_mode (true); + }else + { + light->set_hud_mode (false); + } +} + +// Функция вкл/выкл свет + +void CUIArtefactDetectorSimple::Svet_VklVykl(ref_light light, bool danet) +{ + light->set_active(danet); +} + +// Получалка get вкл/выкл свет +bool CUIArtefactDetectorSimple::Svet_Get_VklVykl(ref_light light) +{ + bool danet = light->get_active(); + return (danet); +} + +// Функция цвет света +void CUIArtefactDetectorSimple::Svet_Cvet(ref_light light, float alfa, float k, float z, float s, bool useU32, u32 clr) +{ + + Fcolor fclr; + if(useU32 == true) + { + fclr.set (clr); + }else + { + fclr.a =alfa; + fclr.r =k; + fclr.g =z; + fclr.b =s; + } + light->set_color(fclr); +} + +// Функция задать позицию света +void CUIArtefactDetectorSimple::Svet_Position(ref_light light, Fvector pos) +{ + light->set_position(pos); +} + +// Функция разрушить свет +void CUIArtefactDetectorSimple::Svet_Dosviduli(ref_light light) +{ + light.destroy(); +} + +#pragma todo("Свет спавнится не в том месте + вызывает падение фпс на статике при стрельбе и использовании ножа. Возможно проблемма с точками огня, которые используются как позииции для спавна света| пока отключил свет") +#pragma todo("Lights spawn in incorrect points, also causes fps drop on static renderer. might be fire poits, that are used to position the light| disabled light til fixed") +void CUIArtefactDetectorSimple::Flash(bool bOn, float fRelPower) +{ + if(!m_parent->HudItemData()) return; + + + IKinematics* K = m_parent->HudItemData()->m_model; + R_ASSERT (K); + LPCSTR boner = "light_bone_2"; + if(bOn) + { + SetBoneVisible(K,m_parent,boner,true,true); + m_turn_off_flash_time = Device.dwTimeGlobal+iFloor(fRelPower*1000.0f); + }else + { + SetBoneVisible(K,m_parent,boner,false,true); + m_turn_off_flash_time = 0; + } +//if (bOn != Svet_Get_VklVykl(m_flash_light)){ +//#pragma todo("Свет спавнится не в том месте|Lights spawn in incorrect points") +// Svet_VklVykl(m_flash_light, bOn); + //} +} + +void CUIArtefactDetectorSimple::setup_internals() +{ + //m_flash_light = Svet_Sozdat(); + //Svet_Ten (m_flash_light, true); + //Svet_Dalnost (m_flash_light, (0.1)); + //Svet_HUDmode (m_flash_light, true); + + //m_on_off_light = Svet_Sozdat(); + //Svet_Ten (m_on_off_light, false); + //Svet_Dalnost (m_on_off_light, (0.1)); + //Svet_HUDmode (m_on_off_light, true); + + IKinematics* K = m_parent->HudItemData()->m_model; + R_ASSERT (K); + + R_ASSERT (m_flash_bone==BI_NONE); + + m_flash_bone = K->LL_BoneID ("light_bone_2"); + LPCSTR boner = "light_bone_2"; + SetBoneVisible(K,m_parent,boner,false,true); + boner = "light_bone_1"; + SetBoneVisible(K,m_parent,boner,true,true); + + //m_pOnOfLAnim = LALib.FindItem("det_on_off"); + //m_pFlashLAnim = LALib.FindItem("det_flash"); +} + +void CUIArtefactDetectorSimple::update() +{ + inherited::update (); + + if(m_parent->HudItemData()) + { + if(m_flash_bone==BI_NONE) + setup_internals(); + + if(m_turn_off_flash_time && m_turn_off_flash_timePosition()); + + //Svet_Position(m_on_off_light, get_bone_position(m_parent, "light_bone_1")); + //Svet_Position(m_on_off_light, m_parent->Position()); + //if(!Svet_Get_VklVykl(m_on_off_light)) + + // Svet_VklVykl(m_on_off_light, true); + + //if (!Svet_Get_VklVykl(m_flash_light)) + + // Svet_VklVykl(m_flash_light, true); + +// u32 clr = m_pOnOfLAnim->CalculateRGB(Device.fTimeGlobal,frame); + /* + firedeps fd; + m_parent->HudItemData()->setup_firedeps(fd); + if (m_flash_light->get_active()) + m_flash_light->set_position(fd.vLastFP); + + m_on_off_light->set_position(fd.vLastFP2); + if (!m_on_off_light->get_active()) + m_on_off_light->set_active(true); + + //int frame = 0; + Svet_Cvet(m_on_off_light, 0.0, 0.0, 0.0, 0.0, true, 16500000); + //u32 clr = m_pOnOfLAnim->CalculateRGB(Device.fTimeGlobal, frame); + //Fcolor fclr; + //fclr.set(clr); + //m_on_off_light->set_color(fclr); + */ + } +} \ No newline at end of file diff --git a/src/xrGameLA/SimpleDetector2.h b/src/xrGameLA/SimpleDetector2.h new file mode 100644 index 000000000..495dd369d --- /dev/null +++ b/src/xrGameLA/SimpleDetector2.h @@ -0,0 +1,27 @@ +#pragma once +#include "customdetector2.h" +#include "../feel_touch.h" + +class CUIArtefactDetectorSimple; + +class CSimpleDetector : public CCustomDetectorR, + public Feel::Touch +{ + typedef CCustomDetectorR inherited; +public: + CSimpleDetector (); + virtual ~CSimpleDetector (); + + virtual void feel_touch_new (CObject* O); + virtual void feel_touch_delete (CObject* O); + virtual BOOL feel_touch_contact (CObject* O); + //virtual BOOL feel_touch_on_contact (CObject* O); + + float DetectorFeel(CObject* object); + +protected: +//. virtual void UpdateZones (); + virtual void UpdateAf (); + virtual void CreateUI (); + CUIArtefactDetectorSimple& ui (); +}; diff --git a/src/xrGameLA/SleepEffector.cpp b/src/xrGameLA/SleepEffector.cpp new file mode 100644 index 000000000..4acf7a7cf --- /dev/null +++ b/src/xrGameLA/SleepEffector.cpp @@ -0,0 +1,94 @@ +////////////////////////////////////////////////////////////////////// +// SleepEffector.cpp: эффектор, который запускается во время сна +// актера +////////////////////////////////////////////////////////////////////// + + +#include "stdafx.h" +#include "SleepEffector.h" + + +////////////////////////////////////////////////////////////////////////// +// CMonsterEffector +////////////////////////////////////////////////////////////////////////// +CSleepEffectorPP::CSleepEffectorPP(const SPPInfo &ppi, float life_time, float attack_time, float release_time) +: CEffectorPP(EEffectorPPType(SLEEP_EFFECTOR_TYPE_ID), life_time) +{ + state = ppi; + m_total = life_time; + + m_attack = ((fis_zero(attack_time)) ? 0.5f : attack_time); + m_release = ((fis_zero(release_time)) ? 0.5f : release_time); + + VERIFY(!fsimilar(m_release, 1.0f)); + VERIFY(!fis_zero(m_attack)); + + m_eSleepState = BEGIN_SLEEP; +} + +BOOL CSleepEffectorPP::Process(SPPInfo& pp) +{ + inherited::Process(pp); + + // amount of time passed in percents + float time_past_perc = (m_total - fLifeTime) / m_total; + float factor; + + + if (time_past_perc < m_attack) + { + factor = time_past_perc / m_attack; + m_eSleepState = BEGIN_SLEEP; + } + else if (BEGIN_SLEEP == m_eSleepState && + (time_past_perc >= m_attack) && + (time_past_perc <= m_release)) + { + factor = 1.0f; + m_eSleepState = BEFORE_SLEEPING; + } + else if (SLEEPING == m_eSleepState) + { + //не изменять значение fLifeTime пока спим + fLifeTime = m_attack*m_total; + factor = 1.0f; + } + else if (AWAKING == m_eSleepState)//просыпаемся + factor = (1.0f - time_past_perc) / (1.0f - m_release); + + clamp(factor,0.01f, 1.0f); + + if(m_eSleepState==SLEEPING) return TRUE; + + SPPInfo def; + + pp.duality.h = def.duality.h + (state.duality.h - def.duality.h) * factor; + pp.duality.v = def.duality.v + (state.duality.v - def.duality.v) * factor; + pp.gray = def.gray + (state.gray - def.gray) * factor; + pp.blur = def.blur + (state.blur - def.blur) * factor; + pp.noise.intensity = def.noise.intensity + (state.noise.intensity - def.noise.intensity) * factor; + pp.noise.grain = def.noise.grain + (state.noise.grain - def.noise.grain) * factor; + pp.noise.fps = def.noise.fps + (state.noise.fps - def.noise.fps) * factor; + VERIFY(!fis_zero(pp.noise.fps)); + + + pp.color_base.set ( + def.color_base.r + (state.color_base.r - def.color_base.r) * factor, + def.color_base.g + (state.color_base.g - def.color_base.g) * factor, + def.color_base.b + (state.color_base.b - def.color_base.b) * factor + ); + + pp.color_gray.set ( + def.color_gray.r + (state.color_gray.r - def.color_gray.r) * factor, + def.color_gray.g + (state.color_gray.g - def.color_gray.g) * factor, + def.color_gray.b + (state.color_gray.b - def.color_gray.b) * factor + ); + + pp.color_add.set ( + def.color_add.r + (state.color_add.r - def.color_add.r) * factor, + def.color_add.g + (state.color_add.g - def.color_add.g) * factor, + def.color_add.b + (state.color_add.b - def.color_add.b) * factor + ); + + return TRUE; +} \ No newline at end of file diff --git a/src/xrGameLA/SleepEffector.h b/src/xrGameLA/SleepEffector.h new file mode 100644 index 000000000..9e9118bce --- /dev/null +++ b/src/xrGameLA/SleepEffector.h @@ -0,0 +1,45 @@ +////////////////////////////////////////////////////////////////////// +// SleepEffector.h: эффектор, который запускается во время сна +// актера +////////////////////////////////////////////////////////////////////// + + +#pragma once + +#include "../effectorPP.h" +#include "../effector.h" +#include "../cameramanager.h" + + +#define SLEEP_EFFECTOR_TYPE_ID 8 + +#define FATIGUE_EFFECTOR_TYPE_ID 33 + +class CSleepEffectorPP : public CEffectorPP { + typedef CEffectorPP inherited; + + SPPInfo state; // current state + float m_total; // total PP time + float m_attack; // attack time in percents [0..1] + float m_release; // release time in percents [0..1] + +public: + CSleepEffectorPP (const SPPInfo &ppi, float life_time, float attack_time = 0.0f, float release_time = 0.0f); + virtual BOOL Process (SPPInfo& pp); + + + //текущий статус сна + enum SLEEP_STATE { BEGIN_SLEEP, + BEFORE_SLEEPING, + SLEEPING, + AWAKING}; + SLEEP_STATE m_eSleepState; +}; + + +struct SSleepEffector { + SPPInfo ppi; + float time; + float time_attack; + float time_release; +}; \ No newline at end of file diff --git a/src/xrGameLA/SpaceUtils.h b/src/xrGameLA/SpaceUtils.h new file mode 100644 index 000000000..b909459de --- /dev/null +++ b/src/xrGameLA/SpaceUtils.h @@ -0,0 +1,21 @@ +#ifndef SPACE_UTILS_H +#define SPACE_UTILS_H +#pragma warning(disable:4995) +#pragma warning(disable:4267) +#include "../../xrODE/ode/src/collision_kernel.h" +#pragma warning(default:4995) +#pragma warning(default:4267) +IC void spatialParsFromDGeom(dGeomID d_space,Fvector& center,Fvector& AABB,float& radius) +{ + + d_space->computeAABB(); + dReal* dAABB=d_space->aabb; + center.set( + (dAABB[0]+dAABB[1])/2.f, + (dAABB[2]+dAABB[3])/2.f, + (dAABB[4]+dAABB[5])/2.f + ); + AABB.x=dAABB[1]-center.x,AABB.y=dAABB[3]-center.y,AABB.z=dAABB[5]-center.z; + radius=_max(AABB.x,_max(AABB.y,AABB.z)); +} +#endif \ No newline at end of file diff --git a/src/xrGameLA/StalkerOutfit.cpp b/src/xrGameLA/StalkerOutfit.cpp new file mode 100644 index 000000000..63dfe7f01 --- /dev/null +++ b/src/xrGameLA/StalkerOutfit.cpp @@ -0,0 +1,22 @@ +#include "pch_script.h" +#include "StalkerOutfit.h" + +CStalkerOutfit::CStalkerOutfit() +{ +} + +CStalkerOutfit::~CStalkerOutfit() +{ +} + +using namespace luabind; + +#pragma optimize("s",on) +void CStalkerOutfit::script_register (lua_State *L) +{ + module(L) + [ + class_("CStalkerOutfit") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/StalkerOutfit.h b/src/xrGameLA/StalkerOutfit.h new file mode 100644 index 000000000..1cf455bd7 --- /dev/null +++ b/src/xrGameLA/StalkerOutfit.h @@ -0,0 +1,23 @@ +/////////////////////////////////////////////////////////////// +// StalkerOutfit.cpp +// StalkerOutfit - защитный костюм сталкера +/////////////////////////////////////////////////////////////// + + +#pragma once + +#include "customoutfit.h" +#include "script_export_space.h" + +class CStalkerOutfit : public CCustomOutfit { +private: + typedef CCustomOutfit inherited; +public: + CStalkerOutfit(void); + virtual ~CStalkerOutfit(void); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CStalkerOutfit) +#undef script_type_list +#define script_type_list save_type_list(CStalkerOutfit) diff --git a/src/xrGameLA/StdAfx.cpp b/src/xrGameLA/StdAfx.cpp new file mode 100644 index 000000000..b7599703b --- /dev/null +++ b/src/xrGameLA/StdAfx.cpp @@ -0,0 +1,2 @@ +#pragma warning(disable:4503) +#include "stdafx.h" \ No newline at end of file diff --git a/src/xrGameLA/StdAfx.h b/src/xrGameLA/StdAfx.h new file mode 100644 index 000000000..7fceaca77 --- /dev/null +++ b/src/xrGameLA/StdAfx.h @@ -0,0 +1,37 @@ +#pragma once + +#pragma warning(disable:4995) +#include "../stdafx.h" +#include +#pragma warning(default:4995) +#pragma warning( 4 : 4018 ) +#pragma warning( 4 : 4244 ) +#pragma warning(disable:4505) + +// this include MUST be here, since smart_cast is used >1800 times in the project +#include "smart_cast.h" + +#if XRAY_EXCEPTIONS +IC xr_string string2xr_string(LPCSTR s) {return *shared_str(s ? s : "");} +IC void throw_and_log(const xr_string &s) {Msg("! %s",s.c_str()); throw *shared_str(s.c_str());} +# define THROW(xpr) if (!(xpr)) {throw_and_log (__FILE__LINE__" Expression \""#xpr"\"");} +# define THROW2(xpr,msg0) if (!(xpr)) {throw *shared_str(xr_string(__FILE__LINE__).append(" \"").append(#xpr).append(string2xr_string(msg0)).c_str());} +# define THROW3(xpr,msg0,msg1) if (!(xpr)) {throw *shared_str(xr_string(__FILE__LINE__).append(" \"").append(#xpr).append(string2xr_string(msg0)).append(", ").append(string2xr_string(msg1)).c_str());} +#else +# define THROW VERIFY +# define THROW2 VERIFY2 +# define THROW3 VERIFY3 +#endif + +#include "../gamefont.h" +#include "../xr_object.h" +#include "../igame_level.h" + +#define REGISTRY_VALUE_GSCDKEY "InstallCDKEY" +#define REGISTRY_VALUE_VERSION "InstallVers" +#define REGISTRY_VALUE_USERNAME "InstallUserName" + + +#ifndef DEBUG +# define MASTER_GOLD +#endif // DEBUG \ No newline at end of file diff --git a/src/xrGameLA/Store.cpp b/src/xrGameLA/Store.cpp new file mode 100644 index 000000000..b834d8092 --- /dev/null +++ b/src/xrGameLA/Store.cpp @@ -0,0 +1,644 @@ +#include "stdafx.h" +#include "Store.h" +#include "pch_script.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_space.h" +#include "script_engine_space.h" +#include "script_engine.h" + +CStoreHouse::CStoreHouse() +{ +// m_buffer = xr_new (); +// m_buffer->write_start (); +} + +CStoreHouse::~CStoreHouse() +{ +// xr_delete (m_buffer); +} +/* +void CStoreHouse::add(shared_str name, void* ptr_data, u32 size, TypeOfData _type) +{ + //add_data_exist(name); + if (update(name, ptr_data, size, _type)) return; + + void* ptr = xr_malloc(size); + xr_memcpy(ptr,ptr_data,size); + + StoreData d; + d.data = ptr; + d.type = _type; + data[name] = d; +} + +void CStoreHouse::add_boolean(LPCSTR name,bool b) +{ + add(name,&b,sizeof(b),lua_bool); +} + +void CStoreHouse::add_number(LPCSTR name, double number) +{ + add(name,&number,sizeof(number),lua_number); +} + +void CStoreHouse::add_string(LPCSTR name, LPCSTR string) +{ + add(name,(void*)string,xr_strlen(string)+1,lua_string); +} + +void CStoreHouse::add_vector(LPCSTR name, Fvector v) +{ + add(name,&v,sizeof(float)*3,lua_vector); +} + +void CStoreHouse::add_table(LPCSTR name, LPCSTR string) +{ + add(name,(void*)string,xr_strlen(string)+1,lua_table); +} + +bool CStoreHouse::update(shared_str name, void *ptr_data, u32 size, TypeOfData _type) +{ + xr_map::iterator it = data.find(name); + if (it == data.end()) + return false; +// it->second.type = _type; + xr_delete(it->second.data); + it->second.data = xr_malloc(size); + xr_memcpy(it->second.data,ptr_data,size); + return true; +} + +void CStoreHouse::add_data_exist(shared_str name) +{ + xr_map::iterator it = data.find(name); + R_ASSERT3(data.find(name)==data.end(),"Can't save data with the same name ",name.c_str()); +} + +void CStoreHouse::get_data_exist(shared_str name) +{ + xr_map::iterator it = data.find(name); + xr_map::iterator it_end = data.end(); + + R_ASSERT3(data.find(name)!=data.end(),"Data doesn't exist! ",name.c_str()); +} + +void CStoreHouse::delete_data(LPCSTR c_name) +{ + shared_str name(c_name); + xr_map::iterator it = data.find(name); + R_ASSERT3(it!=data.end(),"Data doesn't exist! ",name.c_str()); + xr_delete(it->second.data); + data.erase(it); +} + +void CStoreHouse::get(shared_str name,void* p,u32 size) +{ + get_data_exist(name); + xr_memcpy(p,data[name].data,size); +} + +bool CStoreHouse::get_boolean(LPCSTR name) +{ + bool tmp; + get(name,&tmp,sizeof(bool)); + return tmp; +} + +double CStoreHouse::get_number(LPCSTR name) +{ + double tmp; + get(name,&tmp,sizeof(tmp)); + return tmp; +} + +LPCSTR CStoreHouse::get_string(LPCSTR name) +{ + shared_str s; + get_data_exist(name); + return LPCSTR(data[name].data); +} + +Fvector CStoreHouse::get_vector(LPCSTR name) +{ + Fvector v; + get(name,&v,sizeof(float)*3); + return v; +} + +LPCSTR CStoreHouse::get_table(LPCSTR name) +{ + shared_str s; + get_data_exist(name); + return LPCSTR(data[name].data); +} + +bool CStoreHouse::data_exist(LPCSTR name) +{ + shared_str str(name); + xr_map::iterator it= data.find(str); + xr_map::iterator it_end = data.end(); + bool result = it!=it_end; + return result; +} + +LPCSTR CStoreHouse::get_data_type(LPCSTR name) +{ + shared_str s_name(name); + xr_map::iterator it = data.find(s_name); + if (it!=data.end()){ + return get_data_type(it->second.type); + } + return "ERROR"; +} + +LPCSTR CStoreHouse::get_data_type(TypeOfData d) +{ + switch(d) { + case lua_bool : return "boolean"; + case lua_vector : return "vector"; + case lua_string : return "string"; + case lua_nil : return "nil"; + case lua_number : return "number"; + case lua_table : return "table"; + case lua_u32: return "u32"; + case lua_s32: return "s32"; + case lua_u16: return "u16"; + case lua_s16: return "s16"; + case lua_u8: return "u8"; + case lua_s8: return "s8"; + case lua_float: return "float"; + case lua_ctime: return "CTime"; + } + return "ERROR"; +} + +u32 CStoreHouse::type_to_size(StoreData d) +{ + switch (d.type) { + case lua_bool: return sizeof(bool); + case lua_table: + case lua_string: return xr_strlen((char*)d.data) * sizeof(char)+1; + case lua_vector: return 3*sizeof(float); + case lua_number: return sizeof(double); + case lua_u32: return sizeof(u32); + case lua_s32: return sizeof(s32); + case lua_u16: return sizeof(u16); + case lua_s16: return sizeof(s16); + case lua_u8: return sizeof(u8); + case lua_s8: return sizeof(s8); + case lua_float: return sizeof(float); + case lua_ctime: return sizeof(u64); + }; + R_ASSERT2(0,make_string("StoreHouse unknown type [%d]", d.type)); + return u32(-1); +} + + +void CStoreHouse::save(IWriter &memory_stream) +{ + Msg("* Writing Store..."); + memory_stream.open_chunk (STORE_CHUNK_DATA); + xr_map::iterator it, last; + + + if (ai().script_engine().ready()) + { + luabind::functor func; + string256 func_name; + xr_strcpy(func_name,pSettings->r_string("lost_alpha_cfg","on_save_store_callback")); + R_ASSERT (ai().script_engine().functor(func_name,func)); + func (); + } + + memory_stream.w_u16(data.size()); + for (it=data.begin(),last=data.end();it!=last;++it) + { + memory_stream.w_stringZ(it->first); + switch (it->second.type) + { + case lua_number: + { + TypeOfData num_type; + double number; + xr_memcpy(&number, it->second.data, sizeof(double)); + int i = (int) number; + int d = ((int) (i * prec) % prec); + if (!d && i <= type_max(u32) && i >= type_min(s32)) + { + if (i >= 0) + { + if (i <= type_max(u8)) + num_type = lua_u8; + else if (i <= type_max(u16)) + num_type = lua_u16; + else + num_type = lua_u32; + } + else + { + if (i >= type_min(s8) && i <= type_max(s8)) + num_type = lua_s8; + else if (i >= type_min(s16) && i <= type_max(s16)) + num_type = lua_s16; + else + num_type = lua_s32; + } + it->second.type = num_type; + } + break; + } + default: + { + break; + } + } + memory_stream.w_u8(it->second.type); + memory_stream.w(it->second.data, type_to_size(it->second)); + } + memory_stream.close_chunk (); + + Msg("* %d store values successfully saved", data.size()); +} + +#define CAST_HELPER_MACRO(type, T) case type: T val##T; xr_memcpy(&val##T, d.data, sizeof(T)); f(name, type_name, val##T); break; + +namespace detail +{ + + static void CallHelper(luabind::functor& f, LPCSTR name, StoreData &d, LPCSTR type_name) + { + switch (d.type) + { + CAST_HELPER_MACRO(lua_bool, bool); + + CAST_HELPER_MACRO(lua_number, double); + CAST_HELPER_MACRO(lua_vector, Fvector); + + CAST_HELPER_MACRO(lua_u32, u32); + CAST_HELPER_MACRO(lua_s32, s32); + CAST_HELPER_MACRO(lua_u16, u16); + CAST_HELPER_MACRO(lua_s16, s16); + CAST_HELPER_MACRO(lua_u8, u8); + CAST_HELPER_MACRO(lua_s8, s8); + CAST_HELPER_MACRO(lua_float, float); + case lua_ctime: u64 val; xr_memcpy(&val, d.data, sizeof(u64)); f(name, type_name, xrTime(val)); break; + case lua_table: + case lua_string: f(name, type_name, LPCSTR(d.data)); break; + + } + } + +}; + +void CStoreHouse::load(IReader &file_stream) +{ + R_ASSERT2 (file_stream.find_chunk(STORE_CHUNK_DATA),"Can't find chunk STORE_CHUNK_DATA!"); + luabind::functor func; + + if (pSettings->section_exist("lost_alpha_cfg") && pSettings->line_exist("lost_alpha_cfg","on_load_store_callback")) + { + string256 func_name; + xr_strcpy(func_name,pSettings->r_string("lost_alpha_cfg","on_load_store_callback")); + R_ASSERT (ai().script_engine().functor(func_name,func)); + } + Msg("* Loading Store..."); + u16 count = file_stream.r_u16(); + for (u16 i=0;itime(); + add(name,&val,sizeof(u64),lua_ctime); +} + +*/ + +void CStoreHouse::save(IWriter &memory_stream) +{ +// u16 size = 0; + Msg("* Writing Store..."); +// m_buffer->write_start (); +// OnSave (); +// m_buffer->read_start (); +// size = m_buffer->r_u16 (); + memory_stream.open_chunk (STORE_CHUNK_DATA); +// memory_stream.w (m_buffer->B.data, m_buffer->B.count); + + string256 fn; + luabind::functor callback; + xr_strcpy (fn, pSettings->r_string("lost_alpha_cfg", "on_save_store_callback")); + R_ASSERT (ai().script_engine().functor(fn, callback)); + + LPCSTR str = callback(); + + memory_stream.w_stringZ (str); + + memory_stream.close_chunk (); + +// Msg("* %d store values successfully saved", size); +} +/* + +#define LOAD_HELPER_NUMBER(type, T) \ + case type: { \ + T val##T; \ + file_stream.r(&val##T, sizeof(T)); \ + lua_pushstring(m_lua, *str); \ + lua_pushnumber(m_lua, val##T); \ + lua_settable(m_lua, -3 * (i + 1)); \ + break; \ +} +*/ + +void CStoreHouse::load(IReader &file_stream) +{ + R_ASSERT2 (file_stream.find_chunk(STORE_CHUNK_DATA),"Can't find chunk STORE_CHUNK_DATA!"); + Msg ("* Loading Store..."); + + string256 fn; + luabind::functor callback; + + xr_strcpy (fn, pSettings->r_string("lost_alpha_cfg", "on_load_store_callback")); + R_ASSERT (ai().script_engine().functor(fn, callback)); + + xr_string str; + + file_stream.r_stringZ (str); + + callback (str.c_str()); + + + +// u16 count = OnLoad (file_stream); +// Msg ("* %d store values successfully loaded", count); + +} +/* +u16 CStoreHouse::OnLoad(IReader &file_stream) +{ + u16 total = 0; + string256 fn; + luabind::functor callback; + m_lua = ai().script_engine().lua(); + xr_strcpy (fn, pSettings->r_string("lost_alpha_cfg", "on_load_store_callback")); + R_ASSERT (ai().script_engine().functor(fn, callback)); + + luabind::object& table = UnserializeTable (file_stream, total); + + callback (table); + + return total; + +} + +luabind::object& CStoreHouse::UnserializeTable(IReader &file_stream, u16& total) +{ + u16 count = file_stream.r_u16 (); + lua_newtable (m_lua); + for (u16 i = 0, l = count; i < l; ++i) + { + shared_str key = ""; + u8 marker = 0; + file_stream.r_stringZ (key); + marker = file_stream.r_u8 (); + switch (marker) + { + case lua_bool: + { + bool b = file_stream.r_bool(); + lua_pushstring(m_lua, *str); + lua_pushboolean(m_lua, b); + lua_settable(m_lua, -3 * (i + 1)); + break; + } + LOAD_HELPER_NUMBER(lua_number, double); + // LOAD_HELPER(lua_vector, Fvector3); + + LOAD_HELPER_NUMBER(lua_u32, u32); + LOAD_HELPER_NUMBER(lua_s32, s32); + LOAD_HELPER_NUMBER(lua_u16, u16); + LOAD_HELPER_NUMBER(lua_s16, s16); + LOAD_HELPER_NUMBER(lua_u8, u8); + LOAD_HELPER_NUMBER(lua_s8, s8); + LOAD_HELPER_NUMBER(lua_float, float); + //case lua_ctime: u64 val; file_stream.r(&val, sizeof(u64)); tbl[*key] = xrTime(val); break; + //case lua_table: UnserializeTable(file_stream, total); break; + //case lua_string: shared_str s; file_stream.r_stringZ(s); tbl[*key] = *s; break; + case lua_vector: + { + Fvector3 v = file_stream.r_vec3(v); + lua_pushstring(m_lua, *str); + Fvector3* obj = (Fvector3*) lua_newuserdata(m_lua, sizeof(Fvector3)); + obj->add(v); + lua_settable(m_lua, -3 * (i + 1)); + break; + } + case lua_ctime: + { + u64 val = file_stream.r_u64(); + lua_pushstring(m_lua, *str); + xrTime* obj = (xrTime*) lua_newuserdata(m_lua, sizeof(xrTime)); + obj->add(xrTime(val)); + lua_settable(m_lua, -3 * (i + 1)); + break; + } + case lua_bool: + { + bool b = file_stream.r_bool(); + lua_pushstring(m_lua, *str); + lua_pushboolean(m_lua, b); + lua_settable(m_lua, -3 * (i + 1)); + break; + } + case lua_bool: + { + bool b = file_stream.r_bool(); + lua_pushstring(m_lua, *str); + lua_pushboolean(m_lua, b); + lua_settable(m_lua, -3 * (i + 1)); + break; + } + } + } + total += count; + return tbl; +} + +static long prec = 1e16; + +void CStoreHouse::OnSave() +{ + R_ASSERT(ai().script_engine().ready()); +// luabind::object& table = luabind::newtable (ai().script_engine().lua()); + + luabind::functor func; + string256 func_name; + xr_strcpy (func_name,pSettings->r_string("lost_alpha_cfg","on_save_store_callback")); + R_ASSERT (ai().script_engine().functor(func_name,func)); + + luabind::object table = func (); + + SerializeTable (table); + +} + +void CStoreHouse::SerializeTable(luabind::object& tbl) +{ + R_ASSERT (tbl.type() == LUA_TTABLE); + u16 pos = m_buffer->w_tell (); + m_buffer->w_u16 (u16(0)); + u16 cnt = 0; + for (luabind::object::iterator it = tbl.begin(), end = tbl.end(); it != end; ++it, ++cnt) + { + shared_str k = ""; + try + { + k = luabind::object_cast (it.key()); + } + catch (...) + { + try + { + k.sprintf("%d", luabind::object_cast (it.key())); + } + catch (...) + { + FATAL("wtf"); + } + } + LPCSTR str = *k; + luabind::object val = *it; + switch (val.type()) + { + case LUA_TNUMBER: + { + double number = luabind::object_cast(val); + int i = (int) number; + int d = ((int) (i * prec) % prec); + if (!d && i <= type_max(u32) && i >= type_min(s32)) + { + if (i >= 0) + { + if (i <= type_max(u8)) + SStorageHelper::Write(m_buffer, str, i); + else if (i <= type_max(u16)) + SStorageHelper::Write(m_buffer, str, i); + else + SStorageHelper::Write(m_buffer, str, i); + } + else + { + if (i >= type_min(s8) && i <= type_max(s8)) + SStorageHelper::Write(m_buffer, str, i); + else if (i >= type_min(s16) && i <= type_max(s16)) + SStorageHelper::Write(m_buffer, str, i); + else + SStorageHelper::Write(m_buffer, str, i); + } + } + SStorageHelper::Write(m_buffer, str, number); // engine is based on float values + break; + } + case LUA_TSTRING: + { + LPCSTR value = luabind::object_cast (val); + SStorageHelper::Write (m_buffer, str, value); + break; + } + case LUA_TBOOLEAN: + { + bool value = luabind::object_cast (val); + SStorageHelper::Write (m_buffer, str, value); + break; + } + case LUA_TNIL: + { + break; + } + case LUA_TUSERDATA: + { + // only xrTime/Fvector3 supported so far + try + { + xrTime value = luabind::object_cast (val); + SStorageHelper::Write (m_buffer, str, value); + } + catch (...) + { + try + { + Fvector3 value = luabind::object_cast(val); + SStorageHelper::Write (m_buffer, str, value); + } + catch (...) + { + R_ASSERT2(0, make_string("unsupported userdata '%s'", str)); + } + } + break; + } + case LUA_TTABLE: + { + m_buffer->w_stringZ (str); + m_buffer->w_u8 (lua_table); + SerializeTable (val); + break; + } + } + + } + m_buffer->w_seek (pos, &cnt, sizeof(u16)); +} + + +*/ + diff --git a/src/xrGameLA/Store.h b/src/xrGameLA/Store.h new file mode 100644 index 000000000..fca96d87f --- /dev/null +++ b/src/xrGameLA/Store.h @@ -0,0 +1,105 @@ +#pragma once +#ifndef __STORE_H__ +#define __STORE_H__ +#include "object_interfaces.h" +#include "script_export_space.h" +#include "alife_space.h" +//#include "..\xrNETServer\NET_utils.h" +//#include "xr_time.h" +/* +enum TypeOfData +{ + lua_nil, + lua_bool, + lua_string, + lua_number, + lua_vector, + lua_table, + lua_u32, + lua_u16, + lua_u8, + lua_s32, + lua_s16, + lua_s8, + lua_float, + lua_ctime +}; + +namespace luabind +{ + class object; +}; + +struct lua_State; + +#define WRITE_TEMPLATE(T, type, method) \ + template <> static void Write(NET_Packet *P, shared_str str, T val) { \ + P->w_stringZ (*str); \ + P->w_u8 (type); \ + P->method (val); \ + Msg("~ %s %d", *str, type); \ + } +*/ +class CStoreHouse : public IPureSerializeObject +{ + public: + CStoreHouse (); + virtual ~CStoreHouse (); + virtual void load (IReader& stream); + virtual void save (IWriter& stream); + +// private: +// void OnSave (); +// u16 OnLoad (IReader &file_stream); +// luabind::object& UnserializeTable(IReader &file_stream, u16& total); +// void SerializeTable (luabind::object& tbl); + + public: + DECLARE_SCRIPT_REGISTER_FUNCTION; +/* + private: + struct SStorageHelper + { + template static void Write(NET_Packet *P, shared_str str, T val) + { + FATAL("unsupported type"); + } + + WRITE_TEMPLATE(bool, lua_bool, w_bool); + + WRITE_TEMPLATE(double, lua_number, w_double); + WRITE_TEMPLATE(Fvector3, lua_vector, w_vec3); + + WRITE_TEMPLATE(u32, lua_u32, w_u32); + WRITE_TEMPLATE(s32, lua_s32, w_s32); + WRITE_TEMPLATE(u16, lua_u16, w_u16); + WRITE_TEMPLATE(s16, lua_s16, w_s16); + WRITE_TEMPLATE(u8, lua_u8, w_u8); + WRITE_TEMPLATE(s8, lua_s8, w_s8); + WRITE_TEMPLATE(float, lua_float, w_float); + + WRITE_TEMPLATE(LPCSTR, lua_string, w_stringZ); + WRITE_TEMPLATE(shared_str, lua_string, w_stringZ); + + template <> static void CStoreHouse::SStorageHelper::Write(NET_Packet *P, shared_str str, xrTime& val) + { + P->w_stringZ (*str); + P->w_u8 (lua_ctime); + P->w_u64 (val.time()); + } + + // static void* Read (NET_Packet *P, shared_str* str); + }; + + NET_Packet* m_buffer; + lua_State* m_lua; +*/ +}; + + + +add_to_type_list(CStoreHouse) +#undef script_type_list +#define script_type_list save_type_list(CStoreHouse) + +#endif \ No newline at end of file diff --git a/src/xrGameLA/Store_script.cpp b/src/xrGameLA/Store_script.cpp new file mode 100644 index 000000000..258102e48 --- /dev/null +++ b/src/xrGameLA/Store_script.cpp @@ -0,0 +1,50 @@ +#include "stdafx.h" +#include "Store.h" +#include "pch_script.h" + +using namespace luabind; + +#pragma optimize("s",on) + + +//void script_call_add_u64(CStoreHouse* self,LPCSTR name)//,unsigned __int64 value) +//{ +// self->add_u64(0); +//} + +void CStoreHouse::script_register(lua_State *L) +{ +/* + module(L) + [ + class_("StoreHouse") + .def(constructor<>()) + + .def("add_string", &CStoreHouse::add_string) + + .def("add_vector", &CStoreHouse::add_vector) + .def("add_boolean", &CStoreHouse::add_boolean) + .def("add_number", &CStoreHouse::add_number) + .def("add_table", &CStoreHouse::add_table) + + + + .def("get_string", &CStoreHouse::get_string) + + .def("get_vector", &CStoreHouse::get_vector) + .def("get_number", &CStoreHouse::get_number) + .def("get_boolean", &CStoreHouse::get_boolean) + .def("get_table", &CStoreHouse::get_table) + + // .def("get_data_type", &CStoreHouse::get_data_type) + .def("data_exist", &CStoreHouse::data_exist) + .def("get_vector", &CStoreHouse::get_vector) + + .def("delete_data", &CStoreHouse::delete_data) + + + ]; +*/ +} + + diff --git a/src/xrGameLA/TeleWhirlwind.cpp b/src/xrGameLA/TeleWhirlwind.cpp new file mode 100644 index 000000000..cffb2029a --- /dev/null +++ b/src/xrGameLA/TeleWhirlwind.cpp @@ -0,0 +1,354 @@ +#include "stdafx.h" +#include "telewhirlwind.h" +#include "PhysicsShell.h" +#include "PhysicsShellHolder.h" +#include "level.h" +#include "hit.h" +#include "phdestroyable.h" +#include "xrmessages.h" +#include "../Include/xrRender/Kinematics.h" +#include "PHWorld.h" +CTeleWhirlwind ::CTeleWhirlwind () +{ + m_owner_object=NULL; + m_center.set(0.f,0.f,0.f); + m_keep_radius=1.f; + m_throw_power=100.f; + +} + +CTelekineticObject* CTeleWhirlwind::activate(CPhysicsShellHolder *obj, float strength, float height, u32 max_time_keep, bool rot) +{ + if(inherited::activate(obj,strength,height,max_time_keep,rot)) + { + CTeleWhirlwindObject*o=smart_cast(objects.back()); + VERIFY(o); + o->set_throw_power(m_throw_power); + return o; + } + else + return 0; +} +void CTeleWhirlwind::clear_impacts() +{ + m_saved_impacts.clear(); +} +void CTeleWhirlwind::clear() +{ + inherited::clear(); + +} + +void CTeleWhirlwind::add_impact(const Fvector& dir,float val) +{ + Fvector force,point; + force.set(dir); + force.mul(val); + point.set(0.f,0.f,0.f); + m_saved_impacts.push_back(SPHImpact(force,point,0)); +} +void CTeleWhirlwind::set_throw_power(float throw_pow) +{ + m_throw_power=throw_pow; +} + +void CTeleWhirlwind::draw_out_impact(Fvector& dir,float& val) +{ + VERIFY2(m_saved_impacts.size(),"NO IMPACTS ADDED!"); + dir.set(m_saved_impacts[0].force); + val=dir.magnitude(); + if(!fis_zero(val))dir.mul(1.f/val); + m_saved_impacts.erase(m_saved_impacts.begin()); +} + +static bool RemovePred(CTelekineticObject *tele_object) +{ + return (!tele_object->get_object() || + tele_object->get_object()->getDestroy()); +} + +void CTeleWhirlwind::clear_notrelevant() +{ + //убрать все объеты со старыми параметрами + objects.erase ( + std::remove_if( + objects.begin(), + objects.end(), + &RemovePred + ), + objects.end() + ); +} + + +void CTeleWhirlwind::play_destroy(CTeleWhirlwindObject *obj) +{ + +} + CTeleWhirlwindObject:: CTeleWhirlwindObject() +{ + m_telekinesis=0; + throw_power=0.f; + +} + + +bool CTeleWhirlwindObject:: init(CTelekinesis* tele,CPhysicsShellHolder *obj, float s, float h, u32 ttk,bool rot) +{ + bool result =inherited::init(tele,obj,s,h,ttk,rot); + m_telekinesis =static_cast(tele); + + throw_power =strength; + if(m_telekinesis->is_active_object(obj)) + { + return false; + } + if(obj->PPhysicsShell()) + { + obj->PPhysicsShell()->SetAirResistance(0.f,0.f); + obj->m_pPhysicsShell->set_ApplyByGravity(TRUE); + } + + if(object->ph_destroyable()&&object->ph_destroyable()->CanDestroy()) + b_destroyable=true; + else + b_destroyable=false; + + return result; +} +void CTeleWhirlwindObject:: raise_update () +{ + + //u32 time=Device.dwTimeGlobal; +// if (time_raise_started + 100000 < time) release(); + +} + +void CTeleWhirlwindObject:: release () +{ + if (!object ||object->getDestroy() ||!object->m_pPhysicsShell || !object->m_pPhysicsShell->isActive()) return; + + + Fvector dir_inv; + dir_inv.sub(object->Position(),m_telekinesis->Center()); + float magnitude = dir_inv.magnitude(); + + + // включить гравитацию + //Fvector zer;zer.set(0,0,0); + //object->m_pPhysicsShell->set_LinearVel(zer); + object->m_pPhysicsShell->set_ApplyByGravity(TRUE); +///////////////////////////////////// + float impulse=0.f; + if(magnitude>0.2f) + { + dir_inv.mul(1.f/magnitude); + impulse=throw_power/magnitude/magnitude; + } + else + { + dir_inv.random_dir(); + impulse=throw_power*100.f; + } +///////////////////////////////////////////////// + bool b_destroyed=false; + if(magnitude<2.f*object->Radius()) + { + b_destroyed=destroy_object(dir_inv,throw_power*100.f); + } + + + + if(!b_destroyed)object->m_pPhysicsShell->applyImpulse(dir_inv,impulse); + switch_state(TS_None); +} + +bool CTeleWhirlwindObject::destroy_object (const Fvector dir,float val) +{ + CPHDestroyable* D=object->ph_destroyable(); + if(D) + { + D->PhysicallyRemoveSelf(); + D->Destroy(m_telekinesis->OwnerObject()->ID()); + + xr_vector::iterator i = D->m_destroyed_obj_visual_names.begin(); + xr_vector::iterator e = D->m_destroyed_obj_visual_names.end(); + if (IsGameTypeSingle()) + { + for(;e!=i;i++) + m_telekinesis->add_impact(dir,val*10.f); + }; + + + CParticlesPlayer* PP = smart_cast(object); + if(PP) + { + u16 root=(smart_cast(object->Visual()))->LL_GetBoneRoot(); + PP->StartParticles(m_telekinesis->destroing_particles(),root, Fvector().set(0,1,0),m_telekinesis->OwnerObject()->ID()); + } + return true; + } + return false; +} + +void CTeleWhirlwindObject:: raise (float step) +{ + + CPhysicsShell* p = get_object() ->PPhysicsShell(); + + if(!p||!p->isActive()) + return; + else + { + p->SetAirResistance(0.f,0.f); + p->set_ApplyByGravity(TRUE); + } + u16 element_number = p ->get_ElementsNumber(); + Fvector center = m_telekinesis ->Center(); + CPhysicsElement* maxE=p->get_ElementByStoreOrder(0); + for(u16 element=0;elementget_ElementByStoreOrder(element); + if(maxE->getMass()getMass()) maxE=E; + if (!E->isActive()) continue; + Fvector pos=E->mass_Center(); + + Fvector diff; + diff.sub(center,pos); + float mag=_sqrt(diff.x*diff.x+diff.z*diff.z); + Fvector lc;lc.set(center); + if(mag>1.f) + { + lc.y/=mag; + } + diff.sub(lc,pos); + mag=diff.magnitude(); + float accel=k/mag/mag/mag;//*E->getMass() + Fvector dir; + if(magset_LinearVel(zer); + dir.random_dir(); + } + else + { + dir.set(diff);dir.mul(1.f/mag); + } + Fvector vel; + E->get_LinearVel(vel); + float delta_v=accel*fixed_step; + Fvector delta_vel; delta_vel.set(dir);delta_vel.mul(delta_v); + Fvector predict_vel;predict_vel.add(vel,delta_vel); + Fvector delta_pos;delta_pos.set(predict_vel);delta_pos.mul(fixed_step); + Fvector predict_pos;predict_pos.add(pos,delta_pos); + + Fvector predict_diff;predict_diff.sub(lc,predict_pos); + float predict_mag=predict_diff.magnitude(); + float predict_v=predict_vel.magnitude(); + + Fvector force;force.set(dir); + if(predict_mag>mag && predict_vel.dotproduct(dir)>0.f && predict_v>predict_v_eps) + { + + Fvector motion_dir;motion_dir.set(predict_vel);motion_dir.mul(1.f/predict_v); + float needed_d=diff.dotproduct(motion_dir); + Fvector needed_diff;needed_diff.set(motion_dir);needed_diff.mul(needed_d); + Fvector nearest_p;nearest_p.add(pos,needed_diff);// + Fvector needed_vel;needed_vel.set(needed_diff);needed_vel.mul(1.f/fixed_step); + force.sub(needed_vel,vel); + force.mul(E->getMass()/fixed_step); + } + else + { + force.mul(accel*E->getMass()); + } + + + E->applyForce(force.x,force.y+get_object()->EffectiveGravity()*E->getMass(),force.z); + } + Fvector dist;dist.sub(center,maxE->mass_Center()); + if(dist.magnitude()keep_radius()&&b_destroyable) + { + p->setTorque(Fvector().set(0,0,0)); + p->setForce(Fvector().set(0,0,0)); + p->set_LinearVel(Fvector().set(0,0,0)); + p->set_AngularVel(Fvector().set(0,0,0)); + switch_state(TS_Keep); + } +} + + +void CTeleWhirlwindObject:: keep () +{ + CPhysicsShell* p = get_object() ->PPhysicsShell(); + if(!p||!p->isActive()) + return; + else + { + p->SetAirResistance(0.f,0.f); + p->set_ApplyByGravity(FALSE); + } + + u16 element_number = p ->get_ElementsNumber(); + Fvector center = m_telekinesis ->Center(); + + CPhysicsElement* maxE=p->get_ElementByStoreOrder(0); + for(u16 element=0;elementget_ElementByStoreOrder(element); + if(maxE->getMass()getMass())maxE=E; + Fvector dir;dir.sub(center,E->mass_Center()); + dir.normalize_safe(); + Fvector vel; + E->get_LinearVel(vel); + float force=dir.dotproduct(vel)*E->getMass()/2.f; + if(force<0.f) + { + dir.mul(force); + } + } + + maxE->setTorque(Fvector().set(0,500.f,0)); + + Fvector dist;dist.sub(center,maxE->mass_Center()); + if(dist.magnitude()>m_telekinesis->keep_radius()*1.5f) + { + p->setTorque(Fvector().set(0,0,0)); + p->setForce(Fvector().set(0,0,0)); + p->set_LinearVel(Fvector().set(0,0,0)); + p->set_AngularVel(Fvector().set(0,0,0)); + p->set_ApplyByGravity(TRUE); + switch_state(TS_Raise); + } + +} +void CTeleWhirlwindObject:: fire (const Fvector &target) +{ + //inherited::fire(target); +} +void CTeleWhirlwindObject:: fire (const Fvector &target, float power) +{ + //inherited:: fire(target,power); +} + +void CTeleWhirlwindObject::set_throw_power(float throw_pow) +{ + throw_power=throw_pow; +} +void CTeleWhirlwindObject::switch_state(ETelekineticState new_state) +{ + inherited::switch_state(new_state); +} + +bool CTeleWhirlwindObject::can_activate(CPhysicsShellHolder *obj) +{ + return (obj!=NULL); +} + diff --git a/src/xrGameLA/TeleWhirlwind.h b/src/xrGameLA/TeleWhirlwind.h new file mode 100644 index 000000000..3db5e87e6 --- /dev/null +++ b/src/xrGameLA/TeleWhirlwind.h @@ -0,0 +1,65 @@ +#ifndef TELE_WHIRLWIND +#define TELE_WHIRLWIND +#include "ai/monsters/telekinesis.h" +#include "ai/monsters/telekinetic_object.h" +#include "PHImpact.h" + +class CTeleWhirlwind; +class CGameObject; +class CTeleWhirlwindObject : public CTelekineticObject +{ + typedef CTelekineticObject inherited; + CTeleWhirlwind *m_telekinesis; + bool b_destroyable; + float throw_power; + +public: + virtual ~CTeleWhirlwindObject (){}; + CTeleWhirlwindObject (); + virtual bool init (CTelekinesis* tele,CPhysicsShellHolder *obj, float s, float h, u32 ttk, bool rot = true); + void set_throw_power (float throw_pow); + virtual bool can_activate (CPhysicsShellHolder *obj); + virtual void raise (float step); + virtual void raise_update (); + virtual void keep (); + virtual void release (); + virtual void fire (const Fvector &target); + virtual void fire (const Fvector &target, float power); + virtual void switch_state (ETelekineticState new_state); + virtual bool destroy_object (const Fvector dir,float val); + + +}; + +class CTeleWhirlwind : public CTelekinesis +{ +typedef CTelekinesis inherited; + Fvector m_center; + float m_keep_radius; + float m_throw_power; + CGameObject* m_owner_object; + PH_IMPACT_STORAGE m_saved_impacts; + shared_str m_destroying_particles; + +public: + CTeleWhirlwind (); + CGameObject* OwnerObject ()const {return m_owner_object;} + const Fvector& Center ()const {return m_center;} + void SetCenter (const Fvector center) {m_center.set(center);} + void SetOwnerObject (CGameObject* owner_object) {m_owner_object=owner_object;} + void add_impact (const Fvector& dir,float val) ; + void draw_out_impact (Fvector& dir,float& val) ; + void clear_impacts () ; + void set_destroing_particles (const shared_str& destroying_particles){m_destroying_particles=destroying_particles;} + const shared_str& destroing_particles () {return m_destroying_particles;} + void play_destroy (CTeleWhirlwindObject* obj); +virtual CTelekineticObject* activate (CPhysicsShellHolder *obj, float strength, float height, u32 max_time_keep, bool rot = true); +virtual void clear () ; +virtual void clear_notrelevant () ; +virtual CTelekineticObject* alloc_tele_object () {return static_cast(new CTeleWhirlwindObject());} + float keep_radius () {return m_keep_radius;} + void set_throw_power (float throw_pow); +}; + + +#endif \ No newline at end of file diff --git a/src/xrGameLA/ThornArtifact.cpp b/src/xrGameLA/ThornArtifact.cpp new file mode 100644 index 000000000..b548e3eeb --- /dev/null +++ b/src/xrGameLA/ThornArtifact.cpp @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////// +// ThornArtifact.cpp +// ThornArtefact - артефакт колючка +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ThornArtifact.h" +#include "PhysicsShell.h" + + +CThornArtefact::CThornArtefact(void) +{ +} + +CThornArtefact::~CThornArtefact(void) +{ +} + +void CThornArtefact::Load(LPCSTR section) +{ + inherited::Load(section); +} diff --git a/src/xrGameLA/ThornArtifact.h b/src/xrGameLA/ThornArtifact.h new file mode 100644 index 000000000..baefc4fb9 --- /dev/null +++ b/src/xrGameLA/ThornArtifact.h @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////// +// ThornArtifact.h +// ThornArtefact - артефакт колючка +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CThornArtefact : public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CThornArtefact(void); + virtual ~CThornArtefact(void); + + virtual void Load (LPCSTR section); + +protected: +}; \ No newline at end of file diff --git a/src/xrGameLA/Torch.cpp b/src/xrGameLA/Torch.cpp new file mode 100644 index 000000000..ff85bae76 --- /dev/null +++ b/src/xrGameLA/Torch.cpp @@ -0,0 +1,747 @@ +#include "stdafx.h" +#include "torch.h" +#include "entity.h" +#include "actor.h" +#include "../LightAnimLibrary.h" +#include "PhysicsShell.h" +#include "xrserver_objects_alife_items.h" +#include "ai_sounds.h" +#include "HUDManager.h" +#include "level.h" +#include "../../Include/xrRender/Kinematics.h" +#include "../camerabase.h" +#include "inventory.h" +#include "game_base_space.h" +#include "UIGameCustom.h" +#include "actorEffector.h" +#include "../xr_collide_form.h" +#include "CustomOutfit.h" + +static const float TIME_2_HIDE = 5.f; +static const float TORCH_INERTION_CLAMP = PI_DIV_6; +static const float TORCH_INERTION_SPEED_MAX = 7.5f; +static const float TORCH_INERTION_SPEED_MIN = 0.5f; +static Fvector TORCH_OFFSET = {-0.2f,+0.1f,-0.3f}; +static const Fvector OMNI_OFFSET = {-0.2f,+0.1f,-0.1f}; +static const float OPTIMIZATION_DISTANCE = 100.f; + +static bool stalker_use_dynamic_lights = false; + +ENGINE_API int g_current_renderer; + +CTorch::CTorch(void) : m_current_battery_state(0) +{ + light_render = ::Render->light_create(); + light_render->set_type (IRender_Light::SPOT); + light_render->set_shadow (true); + light_omni = ::Render->light_create(); + light_omni->set_type (IRender_Light::POINT); + light_omni->set_shadow (false); + + m_switched_on = false; + glow_render = ::Render->glow_create(); + lanim = 0; + time2hide = 0; + fBrightness = 1.f; + + m_RangeMax = 20.f; + m_RangeCurve = 20.f; + + m_prev_hp.set (0,0); + m_delta_h = 0; + m_night_vision = NULL; + + // Disabling shift by x and z axes for 1st render, + // because we don't have dynamic lighting in it. + if( g_current_renderer == 1 ) + { + TORCH_OFFSET.x = 0; + TORCH_OFFSET.z = 0; + } + +#pragma todo("lets skip AlifeItem stuff") + m_battery_state = 0.f; + fUnchanreRate = 0.f; +} + +CTorch::~CTorch(void) +{ + light_render.destroy (); + light_omni.destroy (); + glow_render.destroy (); + HUD_SOUND_ITEM::DestroySound (m_FlashlightSwitchSnd); + sndBreaking.destroy(); + xr_delete (m_night_vision); +} + +inline bool CTorch::can_use_dynamic_lights () +{ + if (!H_Parent()) + return (true); + + CInventoryOwner *owner = smart_cast(H_Parent()); + if (!owner) + return (true); + + return (owner->can_use_dynamic_lights()); +} + +void CTorch::Load(LPCSTR section) +{ + inherited::Load (section); + light_trace_bone = pSettings->r_string(section,"light_trace_bone"); + HUD_SOUND_ITEM::LoadSound(section, "snd_flashlight_switch_on", m_FlashlightSwitchSnd, SOUND_TYPE_ITEM_USING); + m_battery_duration = pSettings->r_u32(section, "battery_duration"); + m_bNightVisionEnabled = !!pSettings->r_bool(section,"night_vision"); + if(pSettings->line_exist(section, "break_sound")) + sndBreaking.create(pSettings->r_string(section, "break_sound"),st_Effect,sg_SourceType); + + Settings_from_ltx = READ_IF_EXISTS(pSettings, r_u8, section, "settings_from_ltx", 0); + + //tatarinrafa: Добавил возможность прописать это в конфиге + if (Settings_from_ltx == 1) + { + LoadLightSettings(pSettings, section); + } + + fUnchanreRate = READ_IF_EXISTS(pSettings, r_float, section, "uncharge_rate", 20.f); + //lets give some more realistics to batteries. Now found tourches will have 70 to 100 precent battery status + float rondo = ::Random.randF(0.7f, 1.0f); + m_battery_state = m_battery_duration * rondo; +} + +void CTorch::LoadLightSettings(CInifile* ini, LPCSTR section) +{ + bool isR2 = !!psDeviceFlags.test(rsR2) || !!psDeviceFlags.test(rsR3) || !!psDeviceFlags.test(rsR4); + + IKinematics* K = smart_cast(Visual()); + lanim = LALib.FindItem(READ_IF_EXISTS(ini, r_string, section, "color_animator", "nill")); + guid_bone = K->LL_BoneID(ini->r_string(section, "guide_bone")); + VERIFY(guid_bone != BI_NONE); + + Fcolor clr = ini->r_fcolor(section, (isR2) ? "color_r2" : "color"); + fBrightness = clr.intensity(); + m_RangeMax = ini->r_float(section, (isR2) ? "range_max_r2" : "range_max"); + // m_RangeMin = ini->r_float(section, (isR2) ? "range_min_r2" : "range_min"); + m_RangeCurve = ini->r_float(section, "range_curve"); + + light_render->set_color(clr); + light_render->set_range(m_RangeMax); + + Fcolor clr_o = ini->r_fcolor(section, (isR2) ? "omni_color_r2" : "omni_color"); + float range_o = ini->r_float(section, (isR2) ? "omni_range_r2" : "omni_range"); + light_omni->set_color(clr_o); + light_omni->set_range(range_o); + + light_render->set_cone(deg2rad(ini->r_float(section, "spot_angle"))); + light_render->set_texture(ini->r_string(section, "spot_texture")); + + glow_render->set_texture(ini->r_string(section, "glow_texture")); + glow_render->set_color(clr); + glow_render->set_radius(ini->r_float(section, "glow_radius")); +} + +void CTorch::Switch() +{ + if (OnClient()) return; + bool bActive = !m_switched_on; + Switch (bActive); +} + +void CTorch::Broke() +{ + if (OnClient()) return; + if (m_switched_on) + { + sndBreaking.play_at_pos(0, Position(), false); + Switch(false); + } +} + +void CTorch::SetBatteryStatus(u32 val) +{ + //Msg("SetBatteryStatus=[%f], [%d], [%d], [%d], [%d], [%d], [%f]", condition, m_current_battery_state, val, m_battery_duration, m_current_battery_state - val, (m_current_battery_state - val)/m_battery_duration*100, (m_current_battery_state - val)/m_battery_duration); + m_battery_state = val;//m_current_battery_state = val; + float condition = 1.f * m_battery_state / m_battery_duration; + SetCondition(condition); +} + +void CTorch::Switch (bool light_on) +{ + CActor *pA = smart_cast(H_Parent()); + m_actor_item = (pA) ? true : false; + if (light_on && m_battery_state < 0 && m_actor_item)//!m_current_battery_state && m_actor_item) + { + light_on = false; + + SDrawStaticStruct* s = HUD().GetGameUI()->AddCustomStatic("torch_battery_low", true); + s->m_endTime = Device.fTimeGlobal+3.0f;// 3sec + } + m_switched_on = light_on; + if (can_use_dynamic_lights()) + { + light_render->set_active(light_on); + if(!pA)light_omni->set_active(light_on); + } + glow_render->set_active (light_on); + + if (*light_trace_bone) + { + IKinematics* pVisual = smart_cast(Visual()); VERIFY(pVisual); + u16 bi = pVisual->LL_BoneID(light_trace_bone); + + pVisual->LL_SetBoneVisible (bi, light_on, TRUE); + pVisual->CalculateBones (); + + pVisual->LL_SetBoneVisible (bi, light_on, TRUE); //hack + } + if (m_switched_on && m_actor_item) + HUD_SOUND_ITEM::PlaySound(m_FlashlightSwitchSnd, pA->Position(), pA, true, false); +} + +void CTorch::SwitchNightVision() +{ + if (OnClient()) return; + SwitchNightVision(!m_bNightVisionOn); +} + +void CTorch::SwitchNightVision(bool vision_on, bool use_sounds) +{ + if(!m_bNightVisionEnabled) return; + + m_bNightVisionOn = vision_on; + + CActor *pA = smart_cast(H_Parent()); + if(!pA) return; + + if(!m_night_vision) + m_night_vision = new CNightVisionEffector(cNameSect()); + + + LPCSTR disabled_names = pSettings->r_string(cNameSect(),"disabled_maps"); + LPCSTR curr_map = *Level().name(); + u32 cnt = _GetItemCount(disabled_names); + bool b_allow = true; + string512 tmp; + for(u32 i=0; i(pA->inventory().ItemFromSlot(HELMET_SLOT)); + CInventoryItem * itemfromhelmetslot = pA->inventory().ItemFromSlot(HELMET_SLOT); + CInventoryItem * itemfromPNVslot = pA->inventory().ItemFromSlot(PNV_SLOT); + CCustomOutfit* pOutfit = smart_cast(pA->inventory().ItemFromSlot(OUTFIT_SLOT)); + + /*if(pHelmet && pHelmet->m_NightVisionSect.size() && !b_allow) + { + m_night_vision->OnDisabled(pA, use_sounds); + return; + } + else*/ + if (pOutfit && pOutfit->m_NightVisionSect.size() && !b_allow) + { + + m_night_vision->OnDisabled(pA, use_sounds); + + return; + } + if (itemfromPNVslot && itemfromPNVslot->m_SectNightVision.size() && !b_allow) + { + + m_night_vision->OnDisabled(pA, use_sounds); + + return; + } + if (itemfromhelmetslot && itemfromhelmetslot->m_SectNightVision.size() && !b_allow) + { + + m_night_vision->OnDisabled(pA, use_sounds); + + return; + } + + bool bIsActiveNow = m_night_vision->IsActive(); + + if(m_bNightVisionOn) + { + + if(!bIsActiveNow) + { + /*if(pHelmet && pHelmet->m_NightVisionSect.size()) + { + m_night_vision->Start(pHelmet->m_NightVisionSect, pA, use_sounds); + return; + } + else*/ + if(pOutfit && pOutfit->m_NightVisionSect.size()) + { + m_night_vision->Start(pOutfit->m_NightVisionSect, pA, use_sounds); + + + CurrentNightVisionItem = pA->inventory().ItemFromSlot(OUTFIT_SLOT); + return; + } + + //tatarinrafa: Добавил пнв для шлемов и слота ПНВ(набудущее) + else if (itemfromhelmetslot && itemfromhelmetslot->m_SectNightVision.size()) + { + m_night_vision->Start(itemfromhelmetslot->m_SectNightVision, pA, use_sounds); + + + CurrentNightVisionItem = itemfromhelmetslot; + return; + } + + else if (itemfromPNVslot && itemfromPNVslot->m_SectNightVision.size()) + { + m_night_vision->Start(itemfromPNVslot->m_SectNightVision, pA, use_sounds); + + + CurrentNightVisionItem = itemfromPNVslot; + return; + } + + m_bNightVisionOn = false; // in case if there is no nightvision in helmet and outfit + } + }else + { + if(bIsActiveNow) + { + m_night_vision->Stop(100000.0f, use_sounds); + CurrentNightVisionItem = NULL; + } + } +} + + +BOOL CTorch::net_Spawn(CSE_Abstract* DC) +{ + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeItemTorch *torch = smart_cast(e); + R_ASSERT (torch); + cNameVisual_set (torch->get_visual()); + + R_ASSERT (!CFORM()); + R_ASSERT (smart_cast(Visual())); + collidable.model = new CCF_Skeleton(this); + + if (!inherited::net_Spawn(DC)) + return (FALSE); + + //tatarinrafa: Добавил возможность прописать это в конфиге + if (Settings_from_ltx == 0) + { + IKinematics* K = smart_cast(Visual()); + CInifile* pUserData = K->LL_UserData(); + R_ASSERT3(pUserData, "Empty Torch user data!", torch->get_visual()); + + LoadLightSettings(pUserData, "torch_definition"); + } + + //включить/выключить фонарик + Switch (torch->m_active); + VERIFY (!torch->m_active || (torch->ID_Parent != 0xffff)); + + if(torch->ID_Parent == 0) + SwitchNightVision (torch->m_nightvision_active, false); + //else + // SwitchNightVision (false, false); + + m_delta_h = PI_DIV_2-atan((m_RangeMax*0.5f)/_abs(TORCH_OFFSET.x)); + + m_current_battery_state = torch->m_battery_state; + return (TRUE); +} + +void CTorch::net_Destroy() +{ + Switch (false); + SwitchNightVision (false); + + inherited::net_Destroy (); +} + +void CTorch::OnH_A_Chield() +{ + inherited::OnH_A_Chield (); + m_focus.set (Position()); +} + +void CTorch::OnH_B_Independent (bool just_before_destroy) +{ + inherited::OnH_B_Independent (just_before_destroy); + time2hide = TIME_2_HIDE; + + Switch (false); +} + +void CTorch::Recharge(void) +{ + m_battery_state = m_battery_duration; + SetCondition(1.f); + //m_current_battery_state = m_battery_state; +} + +void CTorch::UpdateBattery(void) +{ + if (m_switched_on) + { + //m_current_battery_state--; + float minus = fUnchanreRate * Device.fTimeDelta; + //Msg("UpdateBattery %f %f", m_battery_state, minus); + m_battery_state -= minus; + //Msg("UpdateBattery %f", m_battery_state); + + float condition = 1.f * m_battery_state / m_battery_duration; + SetCondition(condition); + //Msg("UpdateBattery condition %f", condition); + + float rangeCoef = atan(m_RangeCurve * m_battery_state / m_battery_duration) / PI_DIV_2; + clamp(rangeCoef, 0.f, 1.f); + float range = m_RangeMax * rangeCoef; + // Msg("set_range [%f]", range); + light_render->set_range (range); + m_delta_h = PI_DIV_2-atan((range*0.5f)/_abs(TORCH_OFFSET.x)); + + if (m_battery_state < 0)//!m_current_battery_state) + { + Switch(false); + return; + } + } +} + +void CTorch::UpdateCL() +{ + inherited::UpdateCL (); + + if (m_night_vision && m_night_vision->IsActive()) + { + if (CurrentNightVisionItem){ + CActor *pA = smart_cast(H_Parent()); + if (pA && CurrentNightVisionItem != pA->inventory().ItemFromSlot(PNV_SLOT) && CurrentNightVisionItem != pA->inventory().ItemFromSlot(HELMET_SLOT) && CurrentNightVisionItem != pA->inventory().ItemFromSlot(OUTFIT_SLOT)){ + CurrentNightVisionItem = NULL; + SwitchNightVision(); + } + } + } + if (!m_switched_on) return; + + CBoneInstance &BI = smart_cast(Visual())->LL_GetBoneInstance(guid_bone); + Fmatrix M; + + if (H_Parent()) + { + CActor* actor = smart_cast(H_Parent()); + if (actor) smart_cast(H_Parent()->Visual())->CalculateBones_Invalidate (); + + if (H_Parent()->XFORM().c.distance_to_sqr(Device.vCameraPosition)<_sqr(OPTIMIZATION_DISTANCE) || GameID() != GAME_SINGLE) { + // near camera + smart_cast(H_Parent()->Visual())->CalculateBones (); + M.mul_43 (XFORM(),BI.mTransform); + } else { + // approximately the same + M = H_Parent()->XFORM (); + H_Parent()->Center (M.c); + M.c.y += H_Parent()->Radius ()*2.f/3.f; + } + + if (actor) + { + if (actor->IsLookAt()) + { + m_prev_hp.x = angle_inertion_var(m_prev_hp.x,-actor->cam_Active()->yaw,TORCH_INERTION_SPEED_MIN,TORCH_INERTION_SPEED_MAX,TORCH_INERTION_CLAMP,Device.fTimeDelta); + m_prev_hp.y = angle_inertion_var(m_prev_hp.y,-actor->cam_Active()->pitch,TORCH_INERTION_SPEED_MIN,TORCH_INERTION_SPEED_MAX,TORCH_INERTION_CLAMP,Device.fTimeDelta); + } else { + m_prev_hp.x = angle_inertion_var(m_prev_hp.x,-actor->cam_FirstEye()->yaw,TORCH_INERTION_SPEED_MIN,TORCH_INERTION_SPEED_MAX,TORCH_INERTION_CLAMP,Device.fTimeDelta); + m_prev_hp.y = angle_inertion_var(m_prev_hp.y,-actor->cam_FirstEye()->pitch,TORCH_INERTION_SPEED_MIN,TORCH_INERTION_SPEED_MAX,TORCH_INERTION_CLAMP,Device.fTimeDelta); + } + + Fvector dir,right,up; + dir.setHP (m_prev_hp.x+m_delta_h,m_prev_hp.y); + Fvector::generate_orthonormal_basis_normalized(dir,up,right); + + + if (true) + { + Fvector offset = M.c; + offset.mad (M.i,TORCH_OFFSET.x); + offset.mad (M.j,TORCH_OFFSET.y); + offset.mad (M.k,TORCH_OFFSET.z); + light_render->set_position (offset); + + if(true /*false*/) + { + offset = M.c; + offset.mad (M.i,OMNI_OFFSET.x); + offset.mad (M.j,OMNI_OFFSET.y); + offset.mad (M.k,OMNI_OFFSET.z); + light_omni->set_position (offset); + } + }//if (true) + glow_render->set_position (M.c); + + if (true) + { + light_render->set_rotation (dir, right); + + if(true /*false*/) + { + light_omni->set_rotation (dir, right); + } + }//if (true) + glow_render->set_direction (dir); + + }// if(actor) + else + { + if (can_use_dynamic_lights()) + { + light_render->set_position (M.c); + light_render->set_rotation (M.k,M.i); + + Fvector offset = M.c; + offset.mad (M.i,OMNI_OFFSET.x); + offset.mad (M.j,OMNI_OFFSET.y); + offset.mad (M.k,OMNI_OFFSET.z); + light_omni->set_position (M.c); + light_omni->set_rotation (M.k,M.i); + }//if (can_use_dynamic_lights()) + + glow_render->set_position (M.c); + glow_render->set_direction (M.k); + } + }//if(HParent()) + else + { + if (getVisible() && m_pPhysicsShell) + { + M.mul (XFORM(),BI.mTransform); + + m_switched_on = false; + light_render->set_active(false); + light_omni->set_active(false); + glow_render->set_active (false); + }//if (getVisible() && m_pPhysicsShell) + } + + if (!m_switched_on) return; + + // calc color animator + if (!lanim) return; + + int frame; + // возвращает в формате BGR + u32 clr = lanim->CalculateBGR(Device.fTimeGlobal,frame); + + Fcolor fclr; + fclr.set ((float)color_get_B(clr),(float)color_get_G(clr),(float)color_get_R(clr),1.f); + fclr.mul_rgb (fBrightness/255.f); + if (can_use_dynamic_lights()) + { + light_render->set_color (fclr); + light_omni->set_color (fclr); + } + glow_render->set_color (fclr); + +} + +void CTorch::create_physic_shell() +{ + CPhysicsShellHolder::create_physic_shell(); +} + +void CTorch::activate_physic_shell() +{ + CPhysicsShellHolder::activate_physic_shell(); +} + +void CTorch::setup_physic_shell () +{ + CPhysicsShellHolder::setup_physic_shell(); +} + +void CTorch::net_Export (NET_Packet& P) +{ + inherited::net_Export (P); +// P.w_u8 (m_switched_on ? 1 : 0); + + + BYTE F = 0; + F |= (m_switched_on ? eTorchActive : 0); + F |= (m_bNightVisionOn ? eNightVisionActive : 0); + const CActor *pA = smart_cast(H_Parent()); + if (pA) + { + if (pA->attached(this)) + F |= eAttached; + } + P.w_u8(F); + P.w_u16(m_current_battery_state); +} + +void CTorch::net_Import (NET_Packet& P) +{ + inherited::net_Import (P); + + BYTE F = P.r_u8(); + bool new_m_switched_on = !!(F & eTorchActive); + bool new_m_bNightVisionOn = !!(F & eNightVisionActive); + + if (new_m_switched_on != m_switched_on) Switch (new_m_switched_on); + if (new_m_bNightVisionOn != m_bNightVisionOn) + { +// Msg("CTorch::net_Import - NV[%d]", new_m_bNightVisionOn); + + const CActor *pA = smart_cast(H_Parent()); + if (pA) + { + SwitchNightVision (new_m_bNightVisionOn); + } + } + + m_current_battery_state = P.r_u16(); +} + + +void CTorch::save(NET_Packet &output_packet) +{ + inherited::save(output_packet); + save_data(m_battery_state, output_packet); + +} + +void CTorch::load(IReader &input_packet) +{ + inherited::load(input_packet); + load_data(m_battery_state, input_packet); +} + +bool CTorch::can_be_attached () const +{ +// if( !inherited::can_be_attached() ) return false; + + const CActor *pA = smart_cast(H_Parent()); + if (pA) + { +// if(pA->inventory().Get(ID(), false)) + //check slot and belt + bool b = false; + int slot = BaseSlot(); + if (slot != NO_ACTIVE_SLOT) + b = (const CTorch*)smart_cast(pA->inventory().m_slots[slot].m_pIItem) == this; + b = b || pA->inventory().InBelt(const_cast(this)); + if(b) + return true; + else + return false; + } + return true; +} +void CTorch::afterDetach () +{ + inherited::afterDetach (); + Switch (false); +} + +void CTorch::renderable_Render() +{ + inherited::renderable_Render(); +} + +u32 CTorch::GetBatteryLifetime() const +{ + return m_battery_duration; +} + +u32 CTorch::GetBatteryStatus() const +{ + //return m_current_battery_state; + return m_battery_state; +} + +bool CTorch::IsSwitchedOn() const +{ + return m_switched_on; +} + +CNightVisionEffector::CNightVisionEffector(const shared_str& section) +:m_pActor(NULL) +{ + m_sounds.LoadSound(section.c_str(),"snd_night_vision_on", "NightVisionOnSnd", false, SOUND_TYPE_ITEM_USING); + m_sounds.LoadSound(section.c_str(),"snd_night_vision_off", "NightVisionOffSnd", false, SOUND_TYPE_ITEM_USING); + m_sounds.LoadSound(section.c_str(),"snd_night_vision_idle", "NightVisionIdleSnd", false, SOUND_TYPE_ITEM_USING); + m_sounds.LoadSound(section.c_str(),"snd_night_vision_broken", "NightVisionBrokenSnd", false, SOUND_TYPE_ITEM_USING); +} + +void CNightVisionEffector::Start(const shared_str& sect, CActor* pA, bool play_sound) +{ + m_pActor = pA; + AddEffector (m_pActor, effNightvision, sect); + if(play_sound) + { + PlaySounds(eStartSound); + PlaySounds(eIdleSound); + } +} + +void CNightVisionEffector::Stop(const float factor, bool play_sound) +{ + if(!m_pActor) return; + CEffectorPP* pp = m_pActor->Cameras().GetPPEffector((EEffectorPPType)effNightvision); + if(pp) + { + pp->Stop (factor); + if(play_sound) + PlaySounds(eStopSound); + + m_sounds.StopSound("NightVisionIdleSnd"); + } +} + +bool CNightVisionEffector::IsActive() +{ + //Msg("IsActive"); + if(!m_pActor) return false; + CEffectorPP* pp = m_pActor->Cameras().GetPPEffector((EEffectorPPType)effNightvision); + return (pp!=NULL); +} + +void CNightVisionEffector::OnDisabled(CActor* pA, bool play_sound) +{ + m_pActor = pA; + if(play_sound) + PlaySounds(eBrokeSound); +} + +void CNightVisionEffector::PlaySounds(EPlaySounds which) +{ + if(!m_pActor) + return; + + bool bPlaySoundFirstPerson = !!m_pActor->HUDview(); + switch(which) + { + case eStartSound: + { + m_sounds.PlaySound("NightVisionOnSnd", m_pActor->Position(), NULL, bPlaySoundFirstPerson); + }break; + case eStopSound: + { + m_sounds.PlaySound("NightVisionOffSnd", m_pActor->Position(), NULL, bPlaySoundFirstPerson); + }break; + case eIdleSound: + { + m_sounds.PlaySound("NightVisionIdleSnd", m_pActor->Position(), NULL, bPlaySoundFirstPerson, true); + }break; + case eBrokeSound: + { + m_sounds.PlaySound("NightVisionBrokenSnd", m_pActor->Position(), NULL, bPlaySoundFirstPerson); + }break; + default: NODEFAULT; + } +} diff --git a/src/xrGameLA/Torch.h b/src/xrGameLA/Torch.h new file mode 100644 index 000000000..96c3529f6 --- /dev/null +++ b/src/xrGameLA/Torch.h @@ -0,0 +1,137 @@ +#pragma once + +#include "inventory_item_object.h" +//#include "night_vision_effector.h" +#include "hudsound.h" +#include "script_export_space.h" + +class CLAItem; +class CNightVisionEffector; +class CMonsterEffector; + +class CTorch : public CInventoryItemObject { +private: + typedef CInventoryItemObject inherited; + +protected: + float fBrightness; + CLAItem* lanim; + float time2hide; + + u16 guid_bone; + shared_str light_trace_bone; + + float m_delta_h; + Fvector2 m_prev_hp; + bool m_switched_on; + ref_light light_render; + ref_light light_omni; + ref_glow glow_render; + Fvector m_focus; + // lost alpha start + u32 m_battery_duration; + u16 m_current_battery_state; + float m_battery_state; //tatarinrafa:no AlifeItem stuff.. + float fUnchanreRate; + bool m_actor_item; +private: + inline bool can_use_dynamic_lights (); + +public: + CTorch (); + virtual ~CTorch (); + + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Export (NET_Packet& P); // export to server + virtual void net_Import (NET_Packet& P); // import from server + + virtual void OnH_A_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + + virtual void UpdateCL (); + + void UpdateBattery (void); + void Switch (); + void Switch (bool light_on); + void Broke (); + + void Recharge(void); + u32 GetBatteryStatus(void) const; + void SetBatteryStatus(u32 val); + bool IsSwitchedOn(void) const; + u32 GetBatteryLifetime(void) const; + + virtual bool can_be_attached () const; + +public: + void SwitchNightVision (); + void SwitchNightVision (bool light_on, bool use_sounds=true); + + bool GetNightVisionStatus () const { return m_bNightVisionOn; } + CInventoryItem * CurrentNightVisionItem; +CNightVisionEffector* GetNightVision () { return m_night_vision; } +protected: + bool m_bNightVisionEnabled; + bool m_bNightVisionOn; + + CNightVisionEffector* m_night_vision; + HUD_SOUND_COLLECTION m_sounds; + ref_sound sndBreaking; + + HUD_SOUND_ITEM m_FlashlightSwitchSnd; + + float m_RangeMax; + // float m_RangeMin; + float m_RangeCurve; + + enum EStats{ + eTorchActive = (1<<0), + eNightVisionActive = (1<<1), + eAttached = (1<<2) + }; + + u8 Settings_from_ltx; + + void LoadLightSettings (CInifile* ini, LPCSTR section); + +public: + + virtual bool use_parent_ai_locations () const + { + return (!H_Parent()); + } + virtual void create_physic_shell (); + virtual void activate_physic_shell (); + virtual void setup_physic_shell (); + + virtual void afterDetach (); + virtual void renderable_Render (); + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +class CNightVisionEffector +{ + CActor* m_pActor; + HUD_SOUND_COLLECTION m_sounds; +public: + enum EPlaySounds{ + eStartSound = 0, + eStopSound, + eIdleSound, + eBrokeSound + }; + CNightVisionEffector(const shared_str& sect); + void Start (const shared_str& sect, CActor* pA, bool play_sound=true); + void Stop (const float factor, bool play_sound=true); + bool IsActive (); + void OnDisabled (CActor* pA, bool play_sound=true); + void PlaySounds (EPlaySounds which); +}; + +add_to_type_list(CTorch) +#undef script_type_list +#define script_type_list save_type_list(CTorch) diff --git a/src/xrGameLA/TorridZone.cpp b/src/xrGameLA/TorridZone.cpp new file mode 100644 index 000000000..3b24082f1 --- /dev/null +++ b/src/xrGameLA/TorridZone.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "torridZone.h" +#include "../objectanimator.h" +#include "xrServer_Objects_ALife_Monsters.h" + +CTorridZone::CTorridZone() +{ + m_animator = new CObjectAnimator(); +} + +CTorridZone::~CTorridZone() +{ + xr_delete (m_animator); +} + +BOOL CTorridZone::net_Spawn(CSE_Abstract* DC) +{ + if (!inherited::net_Spawn(DC)) + return (FALSE); + + CSE_Abstract *abstract=(CSE_Abstract*)(DC); + CSE_ALifeTorridZone *zone = smart_cast(abstract); + VERIFY (zone); + + m_animator->Load (zone->get_motion()); + m_animator->Play (true); + + return (TRUE); +} + +void CTorridZone::UpdateWorkload(u32 dt) +{ + inherited::UpdateWorkload (dt); + m_animator->Update (float(dt)/1000.f); + XFORM().set (m_animator->XFORM()); + OnMove (); +} + +void CTorridZone::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + + if(m_idle_sound._feedback()) m_idle_sound.set_position (XFORM().c); + if(m_blowout_sound._feedback()) m_blowout_sound.set_position (XFORM().c); + if(m_hit_sound._feedback()) m_hit_sound.set_position (XFORM().c); + if(m_entrance_sound._feedback()) m_entrance_sound.set_position (XFORM().c); +} + +void CTorridZone::GoEnabledState() +{ + inherited::GoEnabledState(); + m_animator->Stop (); + m_animator->Play (true); +} + +void CTorridZone::GoDisabledState() +{ + inherited::GoDisabledState(); +} + diff --git a/src/xrGameLA/TorridZone.h b/src/xrGameLA/TorridZone.h new file mode 100644 index 000000000..ab3bdb9b2 --- /dev/null +++ b/src/xrGameLA/TorridZone.h @@ -0,0 +1,21 @@ +#pragma once +#include "mosquitobald.h" + +class CObjectAnimator; + +class CTorridZone :public CMosquitoBald +{ +private: + typedef CCustomZone inherited; + CObjectAnimator *m_animator; +public: + CTorridZone (); + virtual ~CTorridZone (); + virtual void UpdateWorkload (u32 dt); + virtual void shedule_Update (u32 dt); + BOOL net_Spawn (CSE_Abstract* DC); + + virtual bool IsVisibleForZones () { return true; } + virtual void GoEnabledState (); + virtual void GoDisabledState (); +}; \ No newline at end of file diff --git a/src/xrGameLA/Tracer.cpp b/src/xrGameLA/Tracer.cpp new file mode 100644 index 000000000..225ce728d --- /dev/null +++ b/src/xrGameLA/Tracer.cpp @@ -0,0 +1,142 @@ +// exxZERO Time Stamp AddIn. Document modified at : Thursday, March 07, 2002 14:11:06 , by user : Oles , from computer : OLES +// Tracer.cpp: implementation of the CTracer class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "Tracer.h" +#include "../render.h" + +#include "../../Include/xrRender/UIShader.h" +#include "../../Include/xrRender/UIRender.h" + +const u32 MAX_TRACERS = (1024*5); +const float TRACER_SIZE = 0.13f; + +CTracer::CTracer() +{ + LPCSTR sh_name = pSettings->r_string("bullet_manager", "tracer_shader"); + LPCSTR tx_name = pSettings->r_string("bullet_manager", "tracer_texture"); + m_circle_size_k = pSettings->r_float("bullet_manager", "fire_circle_k"); + + sh_Tracer->create (sh_name, tx_name); + + m_aColors.clear (); + string64 LineName; + + for (u8 i=0; i<255; i++) + { + xr_sprintf (LineName, "color_%d", i); + if (!pSettings->line_exist("tracers_color_table", LineName)) + break; + u32 clr = pSettings->r_color ("tracers_color_table", LineName); + + m_aColors.push_back (clr); + }; +} + +IC void FillSprite_Circle (const Fvector& pos, const float width, const float length, u32 color) +{ + const Fvector& T = Device.vCameraTop; + const Fvector& R = Device.vCameraRight; + Fvector Vr, Vt; + Vr.x = R.x*width; + Vr.y = R.y*width; + Vr.z = R.z*width; + Vt.x = T.x*length; + Vt.y = T.y*length; + Vt.z = T.z*length; + + Fvector a,b,c,d; + a.sub (Vt,Vr); + b.add (Vt,Vr); + c.invert (a); + d.invert (b); + + Fbox2 t_crcl; + t_crcl.min.set (32.0f/64.0f, 0.0f); + t_crcl.max.set (1.0f, 32.0f/512.0f); + + // TODO: return code back to indexed rendering since we use quads + // Tri 1 + UIRender->PushPoint(d.x+pos.x, d.y+pos.y, d.z+pos.z, color, t_crcl.min.x, t_crcl.max.y); // 0.f,1.f); + UIRender->PushPoint(a.x+pos.x, a.y+pos.y, a.z+pos.z, color, t_crcl.min.x, t_crcl.min.y); // 0.f,0.f); + UIRender->PushPoint(c.x+pos.x, c.y+pos.y, c.z+pos.z, color, t_crcl.max.x, t_crcl.max.y); // 1.f,1.f); + // Tri 2 + UIRender->PushPoint(c.x+pos.x, c.y+pos.y, c.z+pos.z, color, t_crcl.max.x, t_crcl.max.y); // 1.f,1.f); + UIRender->PushPoint(a.x+pos.x, a.y+pos.y, a.z+pos.z, color, t_crcl.min.x, t_crcl.min.y); // 0.f,0.f); + UIRender->PushPoint(b.x+pos.x, b.y+pos.y, b.z+pos.z, color, t_crcl.max.x, t_crcl.min.y); // 1.f,0.f); + + //pv->set (d.x+pos.x,d.y+pos.y,d.z+pos.z, color, 0.f,1.f); pv++; + //pv->set (a.x+pos.x,a.y+pos.y,a.z+pos.z, color, 0.f,0.f); pv++; + //pv->set (c.x+pos.x,c.y+pos.y,c.z+pos.z, color, 1.f,1.f); pv++; + //pv->set (b.x+pos.x,b.y+pos.y,b.z+pos.z, color, 1.f,0.f); pv++; +} + +IC void FillSprite_Line (const Fvector& pos, const Fvector& dir, const float width, const float length, u32 color) +{ + const Fvector& T = dir; + Fvector R; + R.crossproduct (T,Device.vCameraDirection).normalize_safe(); + + Fvector Vr, Vt; + Vr.x = R.x*width; + Vr.y = R.y*width; + Vr.z = R.z*width; + + Vt.x = T.x*length; + Vt.y = T.y*length; + Vt.z = T.z*length; + + Fvector a,b,c,d; + a.sub (Vt,Vr); + b.add (Vt,Vr); + c.invert (a); + d.invert (b); + + + Fbox2 t_tracer; + t_tracer.min.set (0.0f, 1.0f); + t_tracer.max.set (16.0f/64.0f, 0.0f); + + // TODO: return code back to indexed rendering since we use quads + // Tri 1 + UIRender->PushPoint(d.x+pos.x, d.y+pos.y, d.z+pos.z, color, t_tracer.min.x, t_tracer.max.y); + UIRender->PushPoint(a.x+pos.x, a.y+pos.y, a.z+pos.z, color, t_tracer.min.x, t_tracer.min.y); + UIRender->PushPoint(c.x+pos.x, c.y+pos.y, c.z+pos.z, color, t_tracer.max.x, t_tracer.max.y); + // Tri 2 + UIRender->PushPoint(c.x+pos.x, c.y+pos.y, c.z+pos.z, color, t_tracer.max.x, t_tracer.max.y); + UIRender->PushPoint(a.x+pos.x, a.y+pos.y, a.z+pos.z, color, t_tracer.min.x, t_tracer.min.y); + UIRender->PushPoint(b.x+pos.x, b.y+pos.y, b.z+pos.z, color, t_tracer.max.x, t_tracer.min.y); + + + //pv->set (d.x+pos.x,d.y+pos.y,d.z+pos.z, color, 0.f,1.f); pv++; + //pv->set (a.x+pos.x,a.y+pos.y,a.z+pos.z, color, 0.f,0.5f); pv++; + //pv->set (c.x+pos.x,c.y+pos.y,c.z+pos.z, color, 1.f,1.f); pv++; + //pv->set (b.x+pos.x,b.y+pos.y,b.z+pos.z, color, 1.f,0.5f); pv++; +} + +void CTracer::Render (const Fvector& pos, + const Fvector& center, + const Fvector& dir, + float length, + float width, + u8 colorID, + float speed, + bool bActor) +{ + if (::Render->ViewBase.testSphere_dirty((Fvector&)center,length*.5f) ) + { + R_ASSERT (colorID < m_aColors.size() ); + + if(bActor) + { + float k_speed = speed/1000.0f; +// float f_distance = Device.vCameraPosition.distance_to(pos); + + FillSprite_Circle (pos, k_speed*width*m_circle_size_k, k_speed*width*m_circle_size_k, m_aColors[colorID]); + } + + FillSprite_Line (center, dir, width*.5f, length*.5f, m_aColors[colorID]); + } +} \ No newline at end of file diff --git a/src/xrGameLA/Tracer.h b/src/xrGameLA/Tracer.h new file mode 100644 index 000000000..65e638ed2 --- /dev/null +++ b/src/xrGameLA/Tracer.h @@ -0,0 +1,32 @@ +// Tracer.h: interface for the CTracer class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_TRACER_H__AC263474_060A_489E_B84A_E879CD630A1E__INCLUDED_) +#define AFX_TRACER_H__AC263474_060A_489E_B84A_E879CD630A1E__INCLUDED_ +#pragma once + +#include "ui_defs.h" + +class CBulletManager; + +class CTracer +{ + friend CBulletManager; +protected: + ui_shader sh_Tracer; + xr_vector m_aColors; + float m_circle_size_k; +public: + CTracer (); + void Render ( const Fvector& pos, + const Fvector& center, + const Fvector& dir, + float length, + float width, + u8 colorID, + float speed, + bool bActor); +}; + +#endif // !defined(AFX_TRACER_H__AC263474_060A_489E_B84A_E879CD630A1E__INCLUDED_) diff --git a/src/xrGameLA/UIActorStateIcons.cpp b/src/xrGameLA/UIActorStateIcons.cpp new file mode 100644 index 000000000..a0503ccc1 --- /dev/null +++ b/src/xrGameLA/UIActorStateIcons.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "UIActorStateIcons.h" +#include "UI/UIXmlInit.h" +#include "hudmanager.h" + +CUIActorStateIcons::CUIActorStateIcons() +{ + +} + +CUIActorStateIcons::~CUIActorStateIcons() +{ + +} + +#define ATTACH_AND_INIT(icon_state, name) \ + AttachChild (&m_icons[icon_state]); \ + xml_init.InitStatic (xml, name, 0, &m_icons[icon_state]); \ + m_icons[icon_state].Show (false); + +void CUIActorStateIcons::Init() +{ + CUIXml xml; + + string128 STATE_ICONS_XML; + xr_sprintf (STATE_ICONS_XML, "actor_state_icon_%d.xml", ui_hud_type); + + bool result = xml.Load (CONFIG_PATH, UI_PATH, STATE_ICONS_XML); + R_ASSERT3 (result, "xml file not found", STATE_ICONS_XML); + + CUIXmlInit xml_init; + + ATTACH_AND_INIT (eJammedInactive, "weapon_jamming_icon:inactive"); + ATTACH_AND_INIT (eJammedRed, "weapon_jamming_icon:red"); + ATTACH_AND_INIT (eJammedYellow, "weapon_jamming_icon:yellow"); + ATTACH_AND_INIT (eJammedGreen, "weapon_jamming_icon:green"); + + ATTACH_AND_INIT (eRadiationInactive, "radiation_icon:inactive"); + ATTACH_AND_INIT (eRadiationRed, "radiation_icon:red"); + ATTACH_AND_INIT (eRadiationYellow, "radiation_icon:yellow"); + ATTACH_AND_INIT (eRadiationGreen, "radiation_icon:green"); + + ATTACH_AND_INIT (eBleedingInactive, "bleeding_icon:inactive"); + ATTACH_AND_INIT (eBleedingRed, "bleeding_icon:red"); + ATTACH_AND_INIT (eBleedingYellow, "bleeding_icon:yellow"); + ATTACH_AND_INIT (eBleedingGreen, "bleeding_icon:green"); + + ATTACH_AND_INIT (eHungerInactive, "hunger_icon:inactive"); + ATTACH_AND_INIT (eHungerRed, "hunger_icon:red"); + ATTACH_AND_INIT (eHungerYellow, "hunger_icon:yellow"); + ATTACH_AND_INIT (eHungerGreen, "hunger_icon:green"); + + ATTACH_AND_INIT (eThirstInactive, "thirst_icon:inactive"); + ATTACH_AND_INIT (eThirstRed, "thirst_icon:red"); + ATTACH_AND_INIT (eThirstYellow, "thirst_icon:yellow"); + ATTACH_AND_INIT (eThirstGreen, "thirst_icon:green"); + + ATTACH_AND_INIT (ePsyHealthInactive, "psi_damage_icon:inactive"); + ATTACH_AND_INIT (ePsyHealthRed, "psi_damage_icon:red"); + ATTACH_AND_INIT (ePsyHealthYellow, "psi_damage_icon:yellow"); + ATTACH_AND_INIT (ePsyHealthGreen, "psi_damage_icon:green"); + + ATTACH_AND_INIT (eSleepInactive, "sleep_icon:inactive"); + ATTACH_AND_INIT (eSleepRed, "sleep_icon:red"); + ATTACH_AND_INIT (eSleepYellow, "sleep_icon:yellow"); + ATTACH_AND_INIT (eSleepGreen, "sleep_icon:green"); +} + +void CUIActorStateIcons::SetIcons(Flags32 set) +{ + for (u16 i = 0; i < MAX_ICONS; i++) + { + m_icons[i].Show (set.test(1 << i) ? true : false); + } +} \ No newline at end of file diff --git a/src/xrGameLA/UIActorStateIcons.h b/src/xrGameLA/UIActorStateIcons.h new file mode 100644 index 000000000..e9cce1a7c --- /dev/null +++ b/src/xrGameLA/UIActorStateIcons.h @@ -0,0 +1,23 @@ +#pragma once +#ifndef __UIACTORSTATEICONS_H__ +#define __UIACTORSTATEICONS_H__ + +#include "ActorState.h" +#include "UI\UIStatic.h" + +class CUIActorStateIcons : public CUIStatic +{ + private: + typedef CUIWindow inherited; + enum { MAX_ICONS = 28, }; + public: + + CUIActorStateIcons (); + virtual ~CUIActorStateIcons (); + virtual void Init (); + void SetIcons (Flags32 set); + private: + CUIStatic m_icons[MAX_ICONS]; +}; + +#endif \ No newline at end of file diff --git a/src/xrGameLA/UICursor.cpp b/src/xrGameLA/UICursor.cpp new file mode 100644 index 000000000..253821132 --- /dev/null +++ b/src/xrGameLA/UICursor.cpp @@ -0,0 +1,127 @@ +#include "stdafx.h" +#include "uicursor.h" + +#include "ui/UIStatic.h" +#include "ui/UIBtnHint.h" + + +#define C_DEFAULT D3DCOLOR_XRGB(0xff,0xff,0xff) + +CUICursor::CUICursor() +:m_static(NULL),m_b_use_win_cursor(false) +{ + bVisible = false; + vPrevPos.set (0.0f, 0.0f); + vPos.set (0.f,0.f); + InitInternal (); + Device.seqRender.Add (this,-3/*2*/); + Device.seqResolutionChanged.Add(this); +} +//-------------------------------------------------------------------- +CUICursor::~CUICursor () +{ + xr_delete (m_static); + Device.seqRender.Remove (this); + Device.seqResolutionChanged.Remove(this); +} + +void CUICursor::OnScreenResolutionChanged() +{ + xr_delete (m_static); + InitInternal (); +} + +void CUICursor::InitInternal() +{ + m_static = new CUIStatic(); + m_static->InitTextureEx ("ui\\ui_ani_cursor", "hud\\cursor"); + Frect rect; + rect.set (0.0f,0.0f,40.0f,40.0f); + m_static->SetTextureRect (rect); + Fvector2 sz; + sz.set (rect.rb); + sz.x *= UI().get_current_kx(); + + m_static->SetWndSize (sz); + m_static->SetStretchTexture (true); + + u32 screen_size_x = GetSystemMetrics( SM_CXSCREEN ); + u32 screen_size_y = GetSystemMetrics( SM_CYSCREEN ); + m_b_use_win_cursor = (screen_size_y >=Device.dwHeight && screen_size_x>=Device.dwWidth); +} + +//-------------------------------------------------------------------- +u32 last_render_frame = 0; +void CUICursor::OnRender () +{ + g_btnHint->OnRender(); + g_statHint->OnRender(); + + if( !IsVisible() ) return; +#ifdef DEBUG + VERIFY(last_render_frame != Device.dwFrame); + last_render_frame = Device.dwFrame; + + if(bDebug) + { + CGameFont* F = UI().Font().pFontDI; + F->SetAligment (CGameFont::alCenter); + F->SetHeightI (0.02f); + F->OutSetI (0.f,-0.9f); + F->SetColor (0xffffffff); + Fvector2 pt = GetCursorPosition(); + F->OutNext ("%f-%f",pt.x, pt.y); + } +#endif + + m_static->SetWndPos (vPos); + m_static->Update (); + m_static->Draw (); +} + +Fvector2 CUICursor::GetCursorPosition() +{ + return vPos; +} + +Fvector2 CUICursor::GetCursorPositionDelta() +{ + Fvector2 res_delta; + + res_delta.x = vPos.x - vPrevPos.x; + res_delta.y = vPos.y - vPrevPos.y; + return res_delta; +} + +void CUICursor::UpdateCursorPosition(int _dx, int _dy) +{ + Fvector2 p; + vPrevPos = vPos; + if(m_b_use_win_cursor) + { + POINT pti; + BOOL r = GetCursorPos(&pti); + if(!r) return; + p.x = (float)pti.x; + p.y = (float)pti.y; + vPos.x = p.x * (UI_BASE_WIDTH/(float)Device.dwWidth); + vPos.y = p.y * (UI_BASE_HEIGHT/(float)Device.dwHeight); + }else + { + float sens = 1.0f; + vPos.x += _dx*sens; + vPos.y += _dy*sens; + } + clamp (vPos.x, 0.f, UI_BASE_WIDTH); + clamp (vPos.y, 0.f, UI_BASE_HEIGHT); +} + +void CUICursor::SetUICursorPosition(Fvector2 pos) +{ + vPos = pos; + POINT p; + p.x = iFloor(vPos.x / (UI_BASE_WIDTH/(float)Device.dwWidth)); + p.y = iFloor(vPos.y / (UI_BASE_HEIGHT/(float)Device.dwHeight)); + + SetCursorPos(p.x, p.y); +} diff --git a/src/xrGameLA/UICursor.h b/src/xrGameLA/UICursor.h new file mode 100644 index 000000000..3fe32a80a --- /dev/null +++ b/src/xrGameLA/UICursor.h @@ -0,0 +1,30 @@ +#pragma once + +#include "ui_base.h" +class CUIStatic; + +class CUICursor: public pureRender, + public pureScreenResolutionChanged +{ + bool bVisible; + Fvector2 vPos; + Fvector2 vPrevPos; + bool m_b_use_win_cursor; + CUIStatic* m_static; + void InitInternal (); +public: + CUICursor (); + virtual ~CUICursor (); + virtual void OnRender (); + + Fvector2 GetCursorPositionDelta (); + + Fvector2 GetCursorPosition (); + void SetUICursorPosition (Fvector2 pos); + void UpdateCursorPosition (int _dx, int _dy); + virtual void OnScreenResolutionChanged (); + + bool IsVisible () {return bVisible;} + void Show () {bVisible = true;} + void Hide () {bVisible = false;} +}; diff --git a/src/xrGameLA/UIDialogHolder.cpp b/src/xrGameLA/UIDialogHolder.cpp new file mode 100644 index 000000000..fc29da1d6 --- /dev/null +++ b/src/xrGameLA/UIDialogHolder.cpp @@ -0,0 +1,396 @@ +#include "stdafx.h" +#include "UIDialogHolder.h" +#include "ui/UIDialogWnd.h" +#include "UIGameCustom.h" +#include "UICursor.h" +#include "level.h" +#include "actor.h" +#include "xr_level_controller.h" +#include "../CustomHud.h" + +dlgItem::dlgItem(CUIWindow* pWnd) +{ + wnd = pWnd; + enabled = true; +} + +bool dlgItem::operator < (const dlgItem& itm) const +{ + return (int)enabled > (int)itm.enabled; +} + +bool operator == (const dlgItem& i1, const dlgItem& i2) +{ + return (i1.wnd == i2.wnd) && (i1.enabled == i2.enabled); +} + +recvItem::recvItem(CUIDialogWnd* r) +{ + m_item = r; + m_flags.zero (); +} +bool operator == (const recvItem& i1, const recvItem& i2) +{ + return i1.m_item == i2.m_item; +} + +CDialogHolder::CDialogHolder() +{ + m_b_in_update = false; +} + +CDialogHolder::~CDialogHolder() +{ +} + +void CDialogHolder::StartMenu(CUIDialogWnd* pDialog, bool bDoHideIndicators) +{ + R_ASSERT ( pDialog && !pDialog->IsShown() ); + + AddDialogToRender (pDialog); + SetMainInputReceiver (pDialog, false); + + if(UseIndicators()) + { + bool b = !!psHUD_Flags.test(HUD_CROSSHAIR_RT); + m_input_receivers.back().m_flags.set(recvItem::eCrosshair, b); + + b = CurrentGameUI()->GameIndicatorsShown(); + m_input_receivers.back().m_flags.set(recvItem::eIndicators, b); + + if(bDoHideIndicators){ + psHUD_Flags.set (HUD_CROSSHAIR_RT, FALSE); + CurrentGameUI()->ShowGameIndicators(false); + } + } + pDialog->SetHolder (this); + + if( pDialog->NeedCursor() ) + GetUICursor().Show(); + + if(g_pGameLevel) + { + CActor* A = smart_cast( Level().CurrentViewEntity() ); + if ( A && pDialog->StopAnyMove() ) + { + A->StopAnyMove (); + A->PickupModeOff (); + }; + if(A) + { + A->IR_OnKeyboardRelease (kWPN_ZOOM); + A->IR_OnKeyboardRelease (kWPN_FIRE); + } + } +} + + +void CDialogHolder::StopMenu(CUIDialogWnd* pDialog) +{ + R_ASSERT( pDialog && pDialog->IsShown() ); + + if( TopInputReceiver()==pDialog ) + { + if(UseIndicators()) + { + bool b = !!m_input_receivers.back().m_flags.test(recvItem::eCrosshair); + psHUD_Flags.set (HUD_CROSSHAIR_RT, b); + b = !!m_input_receivers.back().m_flags.test(recvItem::eIndicators); + CurrentGameUI()->ShowGameIndicators(b); + } + + SetMainInputReceiver (NULL, false); + }else + SetMainInputReceiver (pDialog, true); + + RemoveDialogToRender (pDialog); + pDialog->SetHolder (NULL); + + if(!TopInputReceiver() || !TopInputReceiver()->NeedCursor() ) + GetUICursor().Hide(); +} + +void CDialogHolder::AddDialogToRender(CUIWindow* pDialog) +{ + dlgItem itm (pDialog); + itm.enabled = true; + + bool bAdd = (m_dialogsToRender_new.end() == std::find(m_dialogsToRender_new.begin(),m_dialogsToRender_new.end(),itm)); + if(!bAdd) return; + + bAdd = (m_dialogsToRender.end() == std::find(m_dialogsToRender.begin(),m_dialogsToRender.end(),itm)); + if(!bAdd) return; + + if(m_b_in_update) + m_dialogsToRender_new.push_back(itm); + else + m_dialogsToRender.push_back(itm); + + pDialog->Show(true); +} + +void CDialogHolder::RemoveDialogToRender(CUIWindow* pDialog) +{ + dlgItem itm (pDialog); + itm.enabled = true; + xr_vector::iterator it = std::find(m_dialogsToRender.begin(),m_dialogsToRender.end(),itm); + + if(it!=m_dialogsToRender.end()) + { + (*it).wnd->Show(false); + (*it).wnd->Enable(false); + (*it).enabled = false; + } +} + + + +void CDialogHolder::DoRenderDialogs() +{ + xr_vector::iterator it = m_dialogsToRender.begin(); + for(; it!=m_dialogsToRender.end();++it){ + if( (*it).enabled && (*it).wnd->IsShown() ) + (*it).wnd->Draw(); + } +} + +void CDialogHolder::OnExternalHideIndicators() +{ + xr_vector::iterator it = m_input_receivers.begin(); + xr_vector::iterator it_e = m_input_receivers.end(); + for(;it!=it_e;++it) + { + (*it).m_flags.set(recvItem::eIndicators, FALSE); + (*it).m_flags.set(recvItem::eCrosshair, FALSE); + } +} + +CUIDialogWnd* CDialogHolder::TopInputReceiver() +{ + if ( !m_input_receivers.empty() ) + return m_input_receivers.back().m_item; + + return NULL; +}; + +void CDialogHolder::SetMainInputReceiver (CUIDialogWnd* ir, bool _find_remove) +{ + if( TopInputReceiver() == ir ) return; + + if(!ir || _find_remove){ + if(m_input_receivers.empty()) return; + + if(!ir) + m_input_receivers.pop_back(); + else{ + VERIFY(ir && _find_remove); + + u32 cnt = m_input_receivers.size(); + for(;cnt>0;--cnt) + if( m_input_receivers[cnt-1].m_item == ir ){ + m_input_receivers[cnt].m_flags.set(recvItem::eCrosshair, m_input_receivers[cnt-1].m_flags.test(recvItem::eCrosshair) ); + m_input_receivers[cnt].m_flags.set(recvItem::eIndicators, m_input_receivers[cnt-1].m_flags.test(recvItem::eIndicators) ); + xr_vector::iterator it = m_input_receivers.begin(); + std::advance (it,cnt-1); + m_input_receivers.erase (it); + break; + } + + } + + }else{ + m_input_receivers.push_back(recvItem(ir)); + } +}; + +void CDialogHolder::StartStopMenu(CUIDialogWnd* pDialog, bool bDoHideIndicators) +{ + if (!pDialog) return; + + if( pDialog->IsShown() ) + StopDialog(pDialog); + else + StartDialog(pDialog, bDoHideIndicators); +} + +void CDialogHolder::StartDialog(CUIDialogWnd* pDialog, bool bDoHideIndicators) +{ + if (!pDialog) return; + + if (pDialog && pDialog->NeedCenterCursor()) + { + GetUICursor().SetUICursorPosition (Fvector2().set(512.0f,384.0f)); + } + StartMenu(pDialog, bDoHideIndicators); +} + +void CDialogHolder::StopDialog(CUIDialogWnd* pDialog) +{ + if (!pDialog) return; + + StopMenu(pDialog); +} + +void CDialogHolder::OnFrame() +{ + m_b_in_update = true; + CUIDialogWnd* wnd = TopInputReceiver(); + if ( wnd && wnd->IsEnabled() ) + { + wnd->Update(); + } + //else + { + xr_vector::iterator it = m_dialogsToRender.begin(); + for(; it!=m_dialogsToRender.end();++it) + if((*it).enabled && (*it).wnd->IsEnabled()) + (*it).wnd->Update(); + } + + m_b_in_update = false; + if(!m_dialogsToRender_new.empty()) + { + m_dialogsToRender.insert (m_dialogsToRender.end(),m_dialogsToRender_new.begin(),m_dialogsToRender_new.end()); + m_dialogsToRender_new.clear (); + } + + std::sort (m_dialogsToRender.begin(), m_dialogsToRender.end()); + while (!m_dialogsToRender.empty() && (!m_dialogsToRender[m_dialogsToRender.size()-1].enabled)) + m_dialogsToRender.pop_back(); +} + +void CDialogHolder::CleanInternals() +{ + while( !m_input_receivers.empty() ) + m_input_receivers.pop_back(); + + m_dialogsToRender.clear (); + GetUICursor().Hide (); +} + +bool CDialogHolder::IR_UIOnKeyboardPress(int dik) +{ + CUIDialogWnd* TIR = TopInputReceiver(); + if(!TIR) return false; + if(!TIR->IR_process()) return false; + //mouse click + if(dik==MOUSE_1 || dik==MOUSE_2 || dik==MOUSE_3) + { + Fvector2 cp = GetUICursor().GetCursorPosition(); + EUIMessages action = (dik==MOUSE_1)?WINDOW_LBUTTON_DOWN :(dik==MOUSE_2)?WINDOW_RBUTTON_DOWN:WINDOW_CBUTTON_DOWN; + if (TIR->OnMouseAction(cp.x,cp.y, action)) + return true; + } + + if (TIR->OnKeyboardAction(dik, WINDOW_KEY_PRESSED)) + return true; + + if( !TIR->StopAnyMove() && g_pGameLevel ) + { + CObject* O = Level().CurrentEntity(); + if( O ){ + IInputReceiver* IR = smart_cast( smart_cast(O) ); + if (IR) + { + EGameActions action = get_binded_action(dik); +#pragma todo("tatarinrafa:Может закомнетить эту хрень?|May be we dont even need this?") + //if (action != kDETECTOR) // Не могу понять почему вылетает именно детектор и зачем вообще этот вторичный вызов нужен + //IR->IR_OnKeyboardPress(action); + } + return (false); + } + } + return true; +} + +bool CDialogHolder::IR_UIOnKeyboardRelease(int dik) +{ + CUIDialogWnd* TIR = TopInputReceiver(); + if(!TIR) return false; + if(!TIR->IR_process()) return false; + + //mouse click + if(dik==MOUSE_1 || dik==MOUSE_2 || dik==MOUSE_3) + { + Fvector2 cp = GetUICursor().GetCursorPosition(); + EUIMessages action = (dik==MOUSE_1)?WINDOW_LBUTTON_UP :(dik==MOUSE_2)?WINDOW_RBUTTON_UP:WINDOW_CBUTTON_UP; + if (TIR->OnMouseAction(cp.x, cp.y, action)) + return true; + } + + if (TIR->OnKeyboardAction(dik, WINDOW_KEY_RELEASED)) + return true; + + if( !TIR->StopAnyMove() && g_pGameLevel ) + { + CObject* O = Level().CurrentEntity(); + if( O ) + { + IInputReceiver* IR = smart_cast( smart_cast(O) ); + if (IR) + IR->IR_OnKeyboardRelease(get_binded_action(dik)); + return (false); + } + } + return true; +} + +bool CDialogHolder::IR_UIOnKeyboardHold(int dik) +{ + CUIDialogWnd* TIR = TopInputReceiver(); + if(!TIR) return false; + if(!TIR->IR_process()) return false; + + if(TIR->OnKeyboardHold(dik)) + return true; + + if(!TIR->StopAnyMove() && g_pGameLevel ) + { + CObject* O = Level().CurrentEntity(); + if(O) + { + IInputReceiver* IR = smart_cast( smart_cast(O) ); + if(IR) + IR->IR_OnKeyboardHold(get_binded_action(dik)); + return false; + } + } + return true; +} + +bool CDialogHolder::IR_UIOnMouseWheel (int direction) +{ + CUIDialogWnd* TIR = TopInputReceiver(); + if(!TIR) return false; + if(!TIR->IR_process()) return false; + + Fvector2 pos = GetUICursor().GetCursorPosition(); + + TIR->OnMouseAction (pos.x,pos.y,(direction>0)?WINDOW_MOUSE_WHEEL_UP:WINDOW_MOUSE_WHEEL_DOWN); + return true; +} + +bool CDialogHolder::IR_UIOnMouseMove(int dx, int dy) +{ + CUIDialogWnd* TIR = TopInputReceiver(); + if(!TIR) return false; + if(!TIR->IR_process()) return false; + if (GetUICursor().IsVisible()) + { + GetUICursor().UpdateCursorPosition(dx, dy); + Fvector2 cPos = GetUICursor().GetCursorPosition(); + TIR->OnMouseAction (cPos.x, cPos.y , WINDOW_MOUSE_MOVE); + }else + if(!TIR->StopAnyMove() && g_pGameLevel ) + { + CObject* O = Level().CurrentEntity(); + if(O) + { + IInputReceiver* IR = smart_cast( smart_cast(O) ); + if (IR) + IR->IR_OnMouseMove (dx,dy); + return false; + } + }; + return true; +} diff --git a/src/xrGameLA/UIDialogHolder.h b/src/xrGameLA/UIDialogHolder.h new file mode 100644 index 000000000..6439cc9b1 --- /dev/null +++ b/src/xrGameLA/UIDialogHolder.h @@ -0,0 +1,62 @@ +#pragma once +#include "../dedicated_server_only.h" +#include "../no_single.h" + +class CUIDialogWnd; +class CUIWindow; + +class dlgItem{ +public: + dlgItem (CUIWindow* pWnd); + CUIWindow* wnd; + bool enabled; + bool operator < (const dlgItem& itm) const; +}; + +class recvItem{ +public: + enum{ eCrosshair = (1<<0), + eIndicators = (1<<1)}; + recvItem (CUIDialogWnd*); + CUIDialogWnd* m_item; + Flags8 m_flags; +}; + +class PROTECT_API CDialogHolder :public pureFrame +{ + //dialogs + xr_vector m_input_receivers; + xr_vector m_dialogsToRender; + xr_vector m_dialogsToRender_new; + bool m_b_in_update; + + void StartMenu (CUIDialogWnd* pDialog, bool bDoHideIndicators); + void StopMenu (CUIDialogWnd* pDialog); + void SetMainInputReceiver (CUIDialogWnd* ir, bool _find_remove); +protected: + void DoRenderDialogs (); + void CleanInternals (); +public: + CDialogHolder (); + virtual ~CDialogHolder (); + + //dialogs + void OnExternalHideIndicators (); + CUIDialogWnd* TopInputReceiver (); + void AddDialogToRender (CUIWindow* pDialog); + void RemoveDialogToRender (CUIWindow* pDialog); + virtual void _BCL OnFrame (); + virtual bool UseIndicators () {return true;} + + virtual void StartStopMenu (CUIDialogWnd* pDialog, bool bDoHideIndicators); + virtual void StartDialog (CUIDialogWnd* pDialog, bool bDoHideIndicators); + virtual void StopDialog (CUIDialogWnd* pDialog); + virtual bool IgnorePause () {return false;} + + virtual bool IR_UIOnKeyboardPress (int dik); + virtual bool IR_UIOnKeyboardRelease (int dik); + virtual bool IR_UIOnMouseMove (int dx, int dy); + virtual bool IR_UIOnMouseWheel (int direction); + virtual bool IR_UIOnKeyboardHold (int dik); + +}; diff --git a/src/xrGameLA/UIGameClock.cpp b/src/xrGameLA/UIGameClock.cpp new file mode 100644 index 000000000..c97ffa68f --- /dev/null +++ b/src/xrGameLA/UIGameClock.cpp @@ -0,0 +1,9 @@ +// File: UIGameClock.cpp +// Description: Timer window on hud +// Created: 16.02.2011 +// Author: Gr1ph00n + +#include "StdAfx.h" +#include "UIGameClock.h" +#include "UIInventoryUtilities.h" + diff --git a/src/xrGameLA/UIGameClock.h b/src/xrGameLA/UIGameClock.h new file mode 100644 index 000000000..3b9b930b2 --- /dev/null +++ b/src/xrGameLA/UIGameClock.h @@ -0,0 +1,18 @@ +// File: UIGameClock.h +// Description: Timer window on hud +// Created: 16.02.2011 +// Author: Gr1ph00n + +#include "UIStatic.h" + +class CUIGameClock +{ + public: + CUIGameClock(); + virtual ~CUIGameClock(); + void Init(); + void Update(); + protected: + CUIStatic m_staticDate; + CUIStatic m_staticTime; +}; diff --git a/src/xrGameLA/UIGameCustom.cpp b/src/xrGameLA/UIGameCustom.cpp new file mode 100644 index 000000000..28422c7ec --- /dev/null +++ b/src/xrGameLA/UIGameCustom.cpp @@ -0,0 +1,483 @@ +#include "pch_script.h" +#include "UIGameCustom.h" +#include "level.h" +#include "ui/UIXmlInit.h" +#include "ui/UIStatic.h" +#include "ui/UIMultiTextStatic.h" +#include "object_broker.h" +#include "string_table.h" + +#include "InventoryOwner.h" + +#include "ui/UIPdaWnd.h" +#include "ui/UIMainIngameWnd.h" +#include "ui/UIMessagesWindow.h" +#include "ui/UIInventoryWnd.h" +#include "ui/UICarBodyWnd.h" + +#include "actor.h" +#include "inventory.h" +#include "game_cl_base.h" + +#include "../x_ray.h" + +bool predicate_sort_stat(const SDrawStaticStruct* s1, const SDrawStaticStruct* s2) +{ + return ( s1->IsActual() > s2->IsActual() ); +} + +struct predicate_find_stat +{ + LPCSTR m_id; + predicate_find_stat(LPCSTR id):m_id(id) {} + bool operator() (SDrawStaticStruct* s) + { + return ( s->m_name==m_id ); + } +}; + +CUIGameCustom::CUIGameCustom() + :m_pgameCaptions(NULL), m_msgs_xml(NULL), m_actor_menu_item(NULL), m_window(NULL), m_InventoryMenu(NULL), m_PdaMenu(NULL), m_UICarBodyMenu(NULL), UIMainIngameWnd(NULL), m_pMessagesWnd(NULL), m_WeatherEditor(NULL) +{ + ShowGameIndicators (true); + ShowCrosshair (true); +} +bool g_b_ClearGameCaptions = false; + +CUIGameCustom::~CUIGameCustom() +{ + delete_data (m_custom_statics); + g_b_ClearGameCaptions = false; +} + + +void CUIGameCustom::OnFrame() +{ + CDialogHolder::OnFrame(); + + st_vec_it it = m_custom_statics.begin(); + st_vec_it it_e = m_custom_statics.end(); + for(;it!=it_e;++it) + (*it)->Update(); + + std::sort( it, it_e, predicate_sort_stat ); + + while(!m_custom_statics.empty() && !m_custom_statics.back()->IsActual()) + { + delete_data (m_custom_statics.back()); + m_custom_statics.pop_back (); + } + + if(g_b_ClearGameCaptions) + { + delete_data (m_custom_statics); + g_b_ClearGameCaptions = false; + } + m_window->Update(); + + //update windows + if( GameIndicatorsShown() && psHUD_Flags.is(HUD_DRAW|HUD_DRAW_RT) ) + UIMainIngameWnd->Update (); + + m_pMessagesWnd->Update(); +} + +void CUIGameCustom::Render() +{ + GameCaptions()->Draw(); + + st_vec_it it = m_custom_statics.begin(); + st_vec_it it_e = m_custom_statics.end(); + for(;it!=it_e;++it) + (*it)->Draw(); + + m_window->Draw(); + + CEntity* pEntity = smart_cast(Level().CurrentEntity()); + if (pEntity) + { + CActor* pActor = smart_cast(pEntity); + if (pActor && pActor->HUDview() && pActor->g_Alive() && + psHUD_Flags.is(HUD_WEAPON | HUD_WEAPON_RT | HUD_WEAPON_RT2)) + { + + CInventory& inventory = pActor->inventory(); + for (auto slot_idx = inventory.FirstSlot(); slot_idx <= inventory.LastSlot(); ++slot_idx) + { + CInventoryItem* item = inventory.ItemFromSlot(slot_idx); + if (item && item->render_item_ui_query()) + { + item->render_item_ui(); + } + } + } + + if( GameIndicatorsShown() && psHUD_Flags.is(HUD_DRAW | HUD_DRAW_RT) ) + { + UIMainIngameWnd->Draw(); + m_pMessagesWnd->Draw(); + } else { //hack - draw messagess wnd in scope mode + if (!m_PdaMenu->GetVisible()) + m_pMessagesWnd->Draw(); + } + } + else + m_pMessagesWnd->Draw(); + + DoRenderDialogs(); +} + +void CUIGameCustom::AddCustomMessage (LPCSTR id, float x, float y, float font_size, CGameFont *pFont, u16 alignment, u32 color) +{ + GameCaptions()->addCustomMessage(id,x,y,font_size,pFont,(CGameFont::EAligment)alignment,color,""); +} + +void CUIGameCustom::AddCustomMessage (LPCSTR id, float x, float y, float font_size, CGameFont *pFont, u16 alignment, u32 color, float flicker ) +{ + AddCustomMessage(id,x,y,font_size, pFont, alignment, color); + GameCaptions()->customizeMessage(id, CUITextBanner::tbsFlicker)->fPeriod = flicker; +} + +void CUIGameCustom::CustomMessageOut(LPCSTR id, LPCSTR msg, u32 color) +{ + GameCaptions()->setCaption(id,msg,color,true); +} + +void CUIGameCustom::RemoveCustomMessage (LPCSTR id) +{ + GameCaptions()->removeCustomMessage(id); +} + + +SDrawStaticStruct* CUIGameCustom::AddCustomStatic(LPCSTR id, bool bSingleInstance) +{ + if(bSingleInstance) + { + st_vec::iterator it = std::find_if(m_custom_statics.begin(),m_custom_statics.end(), predicate_find_stat(id) ); + if(it!=m_custom_statics.end()) + return (*it); + } + + CUIXmlInit xml_init; + m_custom_statics.push_back ( new SDrawStaticStruct() ); + SDrawStaticStruct* sss = m_custom_statics.back(); + + sss->m_static = new CUIStatic(); + sss->m_name = id; + xml_init.InitStatic (*m_msgs_xml, id, 0, sss->m_static); + float ttl = m_msgs_xml->ReadAttribFlt(id, 0, "ttl", -1); + if(ttl>0.0f) + sss->m_endTime = Device.fTimeGlobal + ttl; + + return sss; +} + +SDrawStaticStruct* CUIGameCustom::GetCustomStatic(LPCSTR id) +{ + st_vec::iterator it = std::find_if(m_custom_statics.begin(),m_custom_statics.end(), predicate_find_stat(id)); + if(it!=m_custom_statics.end()) + return (*it); + + return NULL; +} + +void CUIGameCustom::RemoveCustomStatic(LPCSTR id) +{ + st_vec::iterator it = std::find_if(m_custom_statics.begin(),m_custom_statics.end(), predicate_find_stat(id) ); + if(it!=m_custom_statics.end()) + { + delete_data (*it); + m_custom_statics.erase (it); + } +} + +#include "ui/UIGameTutorial.h" + +extern CUISequencer* g_tutorial; +extern CUISequencer* g_tutorial2; + +void CUIGameCustom::reset_ui() +{ + if(g_tutorial2) + { + g_tutorial2->Destroy (); + xr_delete (g_tutorial2); + } + + if(g_tutorial) + { + g_tutorial->Destroy (); + xr_delete(g_tutorial); + } +} + +void CUIGameCustom::SetClGame(game_cl_GameState* g) +{ + g->SetGameUI(this); +} + +void CUIGameCustom::UnLoad() +{ + xr_delete (m_pgameCaptions); + xr_delete (m_msgs_xml); + xr_delete (m_actor_menu_item); + xr_delete (m_window); + xr_delete (UIMainIngameWnd); + xr_delete (m_pMessagesWnd); + xr_delete (m_InventoryMenu); + xr_delete (m_PdaMenu); + xr_delete (m_UICarBodyMenu); + xr_delete (m_WeatherEditor); +} + +void CUIGameCustom::Load() +{ + if(g_pGameLevel) + { + R_ASSERT (NULL==m_pgameCaptions); + m_pgameCaptions = new CUICaption(); + + R_ASSERT (NULL==m_msgs_xml); + m_msgs_xml = new CUIXml(); + m_msgs_xml->Load (CONFIG_PATH, UI_PATH, "ui_custom_msgs.xml"); + + R_ASSERT (NULL==m_actor_menu_item); + m_actor_menu_item = new CUIXml(); + m_actor_menu_item->Load (CONFIG_PATH, UI_PATH, "actor_menu_item.xml"); + + R_ASSERT (NULL==m_PdaMenu); + m_PdaMenu = new CUIPdaWnd (); + + R_ASSERT (NULL==m_window); + m_window = new CUIWindow (); + + R_ASSERT (NULL==UIMainIngameWnd); + UIMainIngameWnd = new CUIMainIngameWnd (); + UIMainIngameWnd->Init (); + + R_ASSERT (NULL==m_InventoryMenu); + m_InventoryMenu = new CUIInventoryWnd (); + + R_ASSERT (NULL==m_UICarBodyMenu); + m_UICarBodyMenu = new CUICarBodyWnd (); + + R_ASSERT (NULL==m_pMessagesWnd); + m_pMessagesWnd = new CUIMessagesWindow(); + + R_ASSERT (NULL==m_WeatherEditor); + m_WeatherEditor = new CUIWeatherEditor(); + + Init (0); + Init (1); + Init (2); + } +} + +void CUIGameCustom::OnConnected() +{ + if(g_pGameLevel) + { + if(!UIMainIngameWnd) + Load(); + + UIMainIngameWnd->OnConnected(); + } +} + +void CUIGameCustom::CommonMessageOut(LPCSTR text) +{ + m_pMessagesWnd->AddLogMessage(text); +} + +SDrawStaticStruct::SDrawStaticStruct () +{ + m_static = NULL; + m_endTime = -1.0f; +} + +void SDrawStaticStruct::destroy() +{ + delete_data(m_static); +} + +bool SDrawStaticStruct::IsActual() const +{ + if(m_endTime<0) return true; + return (Device.fTimeGlobal < m_endTime); +} + +void SDrawStaticStruct::SetText(LPCSTR text) +{ + m_static->Show(text!=NULL); + if(text) + { + m_static->TextItemControl()->SetTextST(text); + m_static->ResetColorAnimation(); + } +} + + +void SDrawStaticStruct::Draw() +{ + if(m_static->IsShown()) + m_static->Draw(); +} + +void SDrawStaticStruct::Update() +{ + if(IsActual() && m_static->IsShown()) + m_static->Update(); +} + +CMapListHelper gMapListHelper; +xr_token game_types[]; + +void CMapListHelper::Load() +{ + string_path fn; + FS.update_path (fn, "$game_config$", "mp\\map_list.ltx"); + CInifile map_list_cfg (fn); + + //read weathers set + CInifile::Sect w = map_list_cfg.r_section("weather"); + CInifile::SectCIt wi = w.Data.begin(); + CInifile::SectCIt wi_e = w.Data.end(); + for( ;wi!=wi_e; ++wi) + { + m_weathers.resize (m_weathers.size()+1); + SGameWeathers& gw = m_weathers.back(); + gw.m_weather_name = (*wi).first; + gw.m_start_time = (*wi).second; + } + + //read original maps from config + CInifile::RootIt it = map_list_cfg.sections().begin(); + CInifile::RootIt it_e = map_list_cfg.sections().end(); + for( ;it!=it_e; ++it) + { + m_storage.resize (m_storage.size()+1); + SGameTypeMaps& Itm = m_storage.back(); + Itm.m_game_type_name = (*it)->Name; + Itm.m_game_type_id = (EGameTypes)get_token_id(game_types, Itm.m_game_type_name.c_str() ); + + CInifile::SectCIt sit = (*it)->Data.begin(); + CInifile::SectCIt sit_e = (*it)->Data.end(); + + for( ;sit!=sit_e; ++sit) + { + SGameTypeMaps::SMapItm Itm_map; + Itm_map.map_name = (*sit).first; + + if(Itm.m_map_names.end()!=std::find(Itm.m_map_names.begin(),Itm.m_map_names.end(),Itm_map)) + { + Msg("! duplicate map found [%s]", (*sit).first.c_str()); + } else { +//#ifndef MASTER_GOLD + Msg("added map [%s]", (*sit).first.c_str()); +//#endif // #ifndef MASTER_GOLD + Itm.m_map_names.push_back (Itm_map); + } + } + } + + // scan for additional maps + FS_FileSet fset; + FS.file_list (fset,"$game_levels$",FS_ListFiles,"*level.ltx"); + + FS_FileSetIt fit = fset.begin(); + FS_FileSetIt fit_e = fset.end(); + + for( ;fit!=fit_e; ++fit) + { + string_path map_cfg_fn; + FS.update_path (map_cfg_fn, "$game_levels$", (*fit).name.c_str()); + CInifile map_ini (map_cfg_fn); + + if(map_ini.section_exist("map_usage")) + { + CInifile::Sect S = map_ini.r_section("map_usage"); + CInifile::SectCIt si = S.Data.begin(); + CInifile::SectCIt si_e = S.Data.end(); + for( ;si!=si_e; ++si) + { + const shared_str& game_type = (*si).first; + SGameTypeMaps* M = GetMapListInt(game_type); + if(!M) + { + Msg ("--unknown game type-%s",game_type.c_str()); + m_storage.resize (m_storage.size()+1); + SGameTypeMaps& Itm = m_storage.back(); + Itm.m_game_type_name = game_type; + Itm.m_game_type_id = (EGameTypes)get_token_id(game_types, game_type.c_str() ); + M = &m_storage.back(); + } + + shared_str _map_name = (*fit).name.substr(0,(*fit).name.find('\\')).c_str(); + shared_str _map_ver = "1"; + + SGameTypeMaps::SMapItm Itm; + Itm.map_name = _map_name; + Itm.map_ver = _map_ver; + + if(M->m_map_names.end()!=std::find(M->m_map_names.begin(),M->m_map_names.end(),Itm)) + { + Msg("! duplicate map found [%s] [%s]", _map_name.c_str(), _map_ver.c_str()); + } else { +//#ifndef MASTER_GOLD + Msg("added map [%s] [%s]", _map_name.c_str(), _map_ver.c_str()); +//#endif // #ifndef MASTER_GOLD + M->m_map_names.push_back (Itm); + } + } + } + } + + R_ASSERT2 (m_storage.size(), "unable to fill map list"); + R_ASSERT2 (m_weathers.size(), "unable to fill weathers list"); +} + +const SGameTypeMaps& CMapListHelper::GetMapListFor(const shared_str& game_type) +{ + if( !m_storage.size() ) + Load (); + + return *GetMapListInt(game_type); +} + +SGameTypeMaps* CMapListHelper::GetMapListInt(const shared_str& game_type) +{ + + TSTORAGE_CIT it = m_storage.begin(); + TSTORAGE_CIT it_e = m_storage.end(); + for( ;it!=it_e; ++it) + { + if(game_type==(*it).m_game_type_name ) + return &(*it); + } + return NULL; +} + +const SGameTypeMaps& CMapListHelper::GetMapListFor(const EGameTypes game_id) +{ + if( !m_storage.size() ) + { + Load (); + R_ASSERT2 (m_storage.size(), "unable to fill map list"); + } + TSTORAGE_CIT it = m_storage.begin(); + TSTORAGE_CIT it_e = m_storage.end(); + for( ;it!=it_e; ++it) + { + if(game_id==(*it).m_game_type_id ) + return (*it); + } + return m_storage[0]; +} + +const GAME_WEATHERS& CMapListHelper::GetGameWeathers() +{ + if(!m_weathers.size()) + Load(); + + return m_weathers; +} \ No newline at end of file diff --git a/src/xrGameLA/UIGameCustom.h b/src/xrGameLA/UIGameCustom.h new file mode 100644 index 000000000..e0899297d --- /dev/null +++ b/src/xrGameLA/UIGameCustom.h @@ -0,0 +1,162 @@ +#ifndef __XR_UIGAMECUSTOM_H__ +#define __XR_UIGAMECUSTOM_H__ +#pragma once + +#include "script_export_space.h" +#include "object_interfaces.h" +#include "inventory_space.h" +#include "UIDialogHolder.h" +#include "../CustomHUD.h" +#include "ui/UIMessages.h" +#include "ui/UIWeatherEditor.h" + +// refs +class CUI; +class CTeamBaseZone; +class game_cl_GameState; +class CUIDialogWnd; +class CUICaption; +class CUIStatic; +class CUIWindow; +class CUIXml; +class CUIInventoryWnd; +class CUITradeWnd; +class CUIPdaWnd; +class CUICarBodyWnd; +struct KillMessageStruct; +class CUIMainIngameWnd; +class CUIMessagesWindow; +class CUIWeatherEditor; + +enum EGameTypes; + +struct SDrawStaticStruct :public IPureDestroyableObject +{ + SDrawStaticStruct (); + virtual void destroy (); + CUIStatic* m_static; + float m_endTime; + shared_str m_name; + void Draw(); + void Update(); + CUIStatic* wnd() {return m_static;} + bool IsActual() const; + void SetText (LPCSTR); +}; + +struct SGameTypeMaps +{ + shared_str m_game_type_name; + EGameTypes m_game_type_id; + struct SMapItm{ + shared_str map_name; + shared_str map_ver; + bool operator ==(const SMapItm& other){return map_name==other.map_name && map_ver==other.map_ver;} + }; + xr_vector m_map_names; +}; + +struct SGameWeathers +{ + shared_str m_weather_name; + shared_str m_start_time; +}; +typedef xr_vector GAME_WEATHERS; +typedef xr_vector::iterator GAME_WEATHERS_IT; +typedef xr_vector::const_iterator GAME_WEATHERS_CIT; + +class CMapListHelper +{ + typedef xr_vector TSTORAGE; + typedef TSTORAGE::iterator TSTORAGE_IT; + typedef TSTORAGE::iterator TSTORAGE_CIT; + TSTORAGE m_storage; + GAME_WEATHERS m_weathers; + + void Load (); + SGameTypeMaps* GetMapListInt (const shared_str& game_type); +public: + const SGameTypeMaps& GetMapListFor (const shared_str& game_type); + const SGameTypeMaps& GetMapListFor (const EGameTypes game_id); + const GAME_WEATHERS& GetGameWeathers (); +}; + +extern CMapListHelper gMapListHelper; + +class CUIGameCustom :public DLL_Pure, public CDialogHolder +{ +protected: + CUIWindow* m_window; + CUICaption* GameCaptions () {return m_pgameCaptions;} + CUICaption* m_pgameCaptions; + CUIXml* m_msgs_xml; + + typedef xr_vector st_vec; + typedef st_vec::iterator st_vec_it; + st_vec m_custom_statics; + + bool m_bShowGameIndicators; + +public: + CUIMainIngameWnd* UIMainIngameWnd; + CUIInventoryWnd* m_InventoryMenu; + CUIMessagesWindow* m_pMessagesWnd; + CUIPdaWnd* m_PdaMenu; + CUICarBodyWnd* m_UICarBodyMenu; + CUIWeatherEditor* m_WeatherEditor; + + virtual void shedule_Update (u32 dt) {}; + virtual void SetClGame (game_cl_GameState* g); + + CUIGameCustom (); + virtual ~CUIGameCustom (); + + virtual void Init (int stage) {}; + + virtual void Render (); + virtual void _BCL OnFrame (); + + + void ShowGameIndicators (bool b) {m_bShowGameIndicators = b;}; + bool GameIndicatorsShown () {return m_bShowGameIndicators;}; + void ShowCrosshair (bool b) {psHUD_Flags.set (HUD_CROSSHAIR_RT, b);} + bool CrosshairShown () {return !!psHUD_Flags.test (HUD_CROSSHAIR_RT);} + + virtual void HideShownDialogs (){}; + virtual void ReInitShownUI (){}; + virtual void ReinitDialogs (){}; + + virtual void AddCustomMessage (LPCSTR id, float x, float y, float font_size, CGameFont *pFont, u16 alignment, u32 color); + virtual void AddCustomMessage (LPCSTR id, float x, float y, float font_size, CGameFont *pFont, u16 alignment, u32 color, float flicker ); + virtual void CustomMessageOut (LPCSTR id, LPCSTR msg, u32 color); + virtual void RemoveCustomMessage (LPCSTR id); + + SDrawStaticStruct* AddCustomStatic (LPCSTR id, bool bSingleInstance); + SDrawStaticStruct* GetCustomStatic (LPCSTR id); + void RemoveCustomStatic (LPCSTR id); + + void CommonMessageOut (LPCSTR text); + + IC CUIPdaWnd& PdaMenu () const { return *m_PdaMenu; } + + virtual void ChangeTotalMoneyIndicator(LPCSTR newMoneyString) {}; + virtual void DisplayMoneyChange (LPCSTR deltaMoney) {}; + virtual void DisplayMoneyBonus (KillMessageStruct* bonus) {}; + + virtual bool OnKeyboardAction (int dik, EUIMessages keyboard_action) {return false;}; + virtual bool OnMouseAction (float x, float y, EUIMessages mouse_action) {return false;}; + + virtual void UnLoad (); + void Load (); + + virtual void reset_ui (); + void OnConnected (); + + CUIXml* m_actor_menu_item; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; // class CUIGameCustom +extern CUIGameCustom* CurrentGameUI(); + + +#endif \ No newline at end of file diff --git a/src/xrGameLA/UIGameCustom_script.cpp b/src/xrGameLA/UIGameCustom_script.cpp new file mode 100644 index 000000000..c44dac1ea --- /dev/null +++ b/src/xrGameLA/UIGameCustom_script.cpp @@ -0,0 +1,39 @@ +#include "pch_script.h" +#include "UIGameCustom.h" +#include "level.h" +#include "hudmanager.h" +#include "ui/uistatic.h" + +using namespace luabind; + +CUIGameCustom* get_hud(){ + return CurrentGameUI(); +} + +#pragma optimize("s",on) +void CUIGameCustom::script_register(lua_State *L) +{ + module(L) + [ + class_< SDrawStaticStruct >("SDrawStaticStruct") + .def_readwrite("m_endTime", &SDrawStaticStruct::m_endTime) + .def("wnd", &SDrawStaticStruct::wnd), + + class_< CUIGameCustom >("CUIGameCustom") + .def("AddDialogToRender", &CUIGameCustom::AddDialogToRender) + .def("RemoveDialogToRender", &CUIGameCustom::RemoveDialogToRender) + .def("AddCustomMessage", (void(CUIGameCustom::*)(LPCSTR, float, float, float, CGameFont*, u16, u32))&CUIGameCustom::AddCustomMessage) + .def("AddCustomMessage", (void(CUIGameCustom::*)(LPCSTR, float, float, float, CGameFont*, u16, u32, float))&CUIGameCustom::AddCustomMessage) + .def("CustomMessageOut", &CUIGameCustom::CustomMessageOut) + .def("RemoveCustomMessage", &CUIGameCustom::RemoveCustomMessage) + .def("CommonMessageOut", &CUIGameCustom::CommonMessageOut) + .def("AddCustomStatic", &CUIGameCustom::AddCustomStatic) + .def("RemoveCustomStatic", &CUIGameCustom::RemoveCustomStatic) + .def("ShowGameIndicators", &CUIGameCustom::ShowGameIndicators) + .def("GameIndicatorsShown", &CUIGameCustom::GameIndicatorsShown) + .def("GetCustomStatic", &CUIGameCustom::GetCustomStatic) + .def("OnKeyboardAction", &CUIGameCustom::OnKeyboardAction) + .def("OnMouseAction", &CUIGameCustom::OnMouseAction), + def("get_hud", &get_hud) + ]; +} diff --git a/src/xrGameLA/UIGameSP.cpp b/src/xrGameLA/UIGameSP.cpp new file mode 100644 index 000000000..27affc7c8 --- /dev/null +++ b/src/xrGameLA/UIGameSP.cpp @@ -0,0 +1,334 @@ +#include "pch_script.h" +#include "uigamesp.h" +#include "actor.h" +#include "level.h" +#include "../xr_input.h" + +#include "game_cl_Single.h" +#include "ui/UIPdaAux.h" +#include "xr_level_controller.h" +#include "actorcondition.h" +#include "../xr_ioconsole.h" +#include "object_broker.h" +#include "GameTaskManager.h" +#include "GameTask.h" +#include "inventory.h" +#include "ui/UITradeWnd.h" +#include "ui/UITalkWnd.h" +#include "ui/UIMessageBox.h" +#include "ui/UIInventoryWnd.h" +#include "ui/UIPdaWnd.h" +#include "ui/UICarBodyWnd.h" +#include "ui/UIMainIngameWnd.h" + +CUIGameSP::CUIGameSP() +{ + m_game = NULL; + StoredInvBox = NULL; + + TalkMenu = new CUITalkWnd (); + UIChangeLevelWnd= new CChangeLevelWnd (); +} + +CUIGameSP::~CUIGameSP() +{ + delete_data(TalkMenu); + delete_data(UIChangeLevelWnd); +} + +void CUIGameSP::HideShownDialogs() +{ + CUIDialogWnd* mir = TopInputReceiver(); + if(mir && (mir==m_InventoryMenu || mir==m_PdaMenu || mir==TalkMenu || mir==m_UICarBodyMenu || mir == m_WeatherEditor)) + mir->HideDialog(); +} + +void CUIGameSP::SetClGame (game_cl_GameState* g) +{ + inherited::SetClGame (g); + m_game = smart_cast(g); + R_ASSERT (m_game); +} +#ifdef DEBUG + void hud_adjust_mode_keyb(int dik); + void hud_draw_adjust_mode(); +#endif + +void hud_draw_adjust_mode(); + +void CUIGameSP::ReInitShownUI() +{ + if (m_InventoryMenu->IsShown()) + m_InventoryMenu->InitInventory_delayed(); + else if(m_UICarBodyMenu->IsShown()) + m_UICarBodyMenu->UpdateLists_delayed(); + +}; + +bool CUIGameSP::IR_UIOnKeyboardPress(int dik) +{ + if(inherited::IR_UIOnKeyboardPress(dik)) return true; + if( Device.Paused() ) return false; + +#ifdef DEBUG + hud_adjust_mode_keyb (dik); +#endif + + CInventoryOwner* pInvOwner = smart_cast( Level().CurrentEntity() ); + if ( !pInvOwner ) return false; + CEntityAlive* EA = smart_cast(Level().CurrentEntity()); + if (!EA || !EA->g_Alive() ) return false; + + CActor *pActor = smart_cast(pInvOwner); + if( !pActor ) + return false; + + if( !pActor->g_Alive() ) + return false; + + if(UIMainIngameWnd->OnKeyboardPress(dik)) + return true; + + switch ( get_binded_action(dik) ) + { + case kINVENTORY: + if((!TopInputReceiver() || TopInputReceiver()==m_InventoryMenu) && !pActor->inventory().IsHandsOnly()) + { + if (!m_InventoryMenu->IsShown()) + m_InventoryMenu->ShowDialog (true); + else + m_InventoryMenu->HideDialog (); + break; + } + + case kACTIVE_JOBS: + if( !TopInputReceiver() || TopInputReceiver()==m_PdaMenu) + { + if (!m_PdaMenu->IsShown()) + m_PdaMenu->ShowDialog (true, eptQuests); + else + m_PdaMenu->HideDialog (); + }break; + + case kMAP: + if( !TopInputReceiver() || TopInputReceiver()==m_PdaMenu) + { + if (!m_PdaMenu->IsShown()) + m_PdaMenu->ShowDialog (true, eptMap); + else + m_PdaMenu->HideDialog (); + }break; + + case kCONTACTS: + if( !TopInputReceiver() || TopInputReceiver()==m_PdaMenu) + { + if (!m_PdaMenu->IsShown()) + m_PdaMenu->ShowDialog (true, eptContacts); + else + m_PdaMenu->HideDialog (); + break; + }break; + + case kSCORES: + { + SDrawStaticStruct* ss = AddCustomStatic("main_task", true); + SGameTaskObjective* o = pActor->GameTaskManager().ActiveObjective(); + if(!o) + ss->m_static->TextItemControl()->SetTextST ("st_no_active_task"); + else + ss->m_static->TextItemControl()->SetTextST (*(o->description)); + }break; + } + + return false; +} + +void CUIGameSP::Render() +{ + inherited::Render(); + hud_draw_adjust_mode(); +} + +bool CUIGameSP::IR_UIOnKeyboardRelease(int dik) +{ + if(inherited::IR_UIOnKeyboardRelease(dik)) return true; + + if( is_binded(kSCORES, dik)) + RemoveCustomStatic ("main_task"); + + return false; +} + +void CUIGameSP::StartTalk(bool disable_break) +{ + RemoveCustomStatic ("main_task"); + + TalkMenu->b_disable_break = disable_break; + TalkMenu->ShowDialog (true); +} + +void CUIGameSP::StartStashUI(CInventoryOwner* pActorInv, CInventoryOwner* pOtherOwner) //Труп, богажник ... +{ + if( TopInputReceiver() ) return; + + m_UICarBodyMenu->InitCustomInventory (pActorInv, pOtherOwner); + m_UICarBodyMenu->ShowDialog (true); +} + +void CUIGameSP::StartStashUI(CInventoryOwner* pActorInv, CInventoryBox* pBox) //Ящик, Сэйф +{ + if( TopInputReceiver() ) return; + + m_UICarBodyMenu->InitInventoryBox (pActorInv, pBox); + m_UICarBodyMenu->ShowDialog (true); +} +//Нужно проверить что будет, если гг убьют +void CUIGameSP::OpenSafe() +{ + if (TopInputReceiver()) return; + + if (StoredInvBox){ + m_UICarBodyMenu->InitInventoryBox(Actor(), StoredInvBox); + m_UICarBodyMenu->ShowDialog(true); + } + +} + + +extern ENGINE_API BOOL bShowPauseString; +void CUIGameSP::ChangeLevel( GameGraph::_GRAPH_ID game_vert_id, + u32 level_vert_id, + Fvector pos, + Fvector ang, + Fvector pos2, + Fvector ang2, + bool b_use_position_cancel) +{ + if(TopInputReceiver()!=UIChangeLevelWnd) + { + UIChangeLevelWnd->m_game_vertex_id = game_vert_id; + UIChangeLevelWnd->m_level_vertex_id = level_vert_id; + UIChangeLevelWnd->m_position = pos; + UIChangeLevelWnd->m_angles = ang; + UIChangeLevelWnd->m_position_cancel = pos2; + UIChangeLevelWnd->m_angles_cancel = ang2; + UIChangeLevelWnd->m_b_position_cancel = b_use_position_cancel; + + UIChangeLevelWnd->ShowDialog (true); + } +} + +void CUIGameSP::EnableSkills(bool val) +{ + m_PdaMenu->EnableSkills(val); +} + +void CUIGameSP::EnableDownloads(bool val) +{ + m_PdaMenu->EnableDownloads(val); +} + +void CUIGameSP::ReinitDialogs() +{ + delete_data(m_InventoryMenu); + m_InventoryMenu = new CUIInventoryWnd(); + + delete_data(TalkMenu); + TalkMenu = new CUITalkWnd(); +} + +CChangeLevelWnd::CChangeLevelWnd () +{ + m_messageBox = new CUIMessageBox(); + m_messageBox->SetAutoDelete(true); + AttachChild (m_messageBox); +} + +void CChangeLevelWnd::SendMessage(CUIWindow *pWnd, s16 msg, void *pData) +{ + if(pWnd==m_messageBox){ + if(msg==MESSAGE_BOX_YES_CLICKED){ + OnOk (); + }else + if(msg==MESSAGE_BOX_NO_CLICKED || msg==MESSAGE_BOX_OK_CLICKED) + { + OnCancel (); + } + }else + inherited::SendMessage(pWnd, msg, pData); +} + +void CChangeLevelWnd::OnOk() +{ + HideDialog (); + NET_Packet p; + p.w_begin (M_CHANGE_LEVEL); + p.w (&m_game_vertex_id,sizeof(m_game_vertex_id)); + p.w (&m_level_vertex_id,sizeof(m_level_vertex_id)); + p.w_vec3 (m_position); + p.w_vec3 (m_angles); + + Level().Send (p,net_flags(TRUE)); +} + +void CChangeLevelWnd::OnCancel() +{ + HideDialog(); + if(m_b_position_cancel) + Actor()->MoveActor(m_position_cancel, m_angles_cancel); +} + +bool CChangeLevelWnd::OnKeyboardAction(int dik, EUIMessages keyboard_action) +{ + if(keyboard_action==WINDOW_KEY_PRESSED) + { + if(is_binded(kQUIT, dik) ) + OnCancel (); + return true; + } + return inherited::OnKeyboardAction(dik, keyboard_action); +} + +#include "ai_space.h" +#include "script_engine.h" + +bool g_block_pause = false; +void CChangeLevelWnd::ShowDialog(bool bDoHideIndicators) +{ + luabind::functor lua_bool; + bool call_result = false; + + R_ASSERT2(ai().script_engine().functor("level_weathers.is_blowout_active", lua_bool), "Can't find level_weathers.set_weather_manualy"); + + if (lua_bool.is_valid()){ + call_result = lua_bool(); + } + + if (call_result) + { + m_messageBox->InitMessageBox("message_box_change_level_blowout"); //if blowout - show rejection message + } + else + { + m_messageBox->InitMessageBox("message_box_change_level"); //else ask if player wants to move to other level + } + + SetWndPos (m_messageBox->GetWndPos()); + m_messageBox->SetWndPos (Fvector2().set(0.0f,0.0f)); + SetWndSize (m_messageBox->GetWndSize()); + + g_block_pause = true; + Device.Pause (TRUE, TRUE, TRUE, "CChangeLevelWnd_show"); + bShowPauseString = FALSE; + + inherited::ShowDialog(bDoHideIndicators); +} + +void CChangeLevelWnd::HideDialog() +{ + g_block_pause = false; + Device.Pause (FALSE, TRUE, TRUE, "CChangeLevelWnd_hide"); + + inherited::HideDialog(); +} + diff --git a/src/xrGameLA/UIGameSP.h b/src/xrGameLA/UIGameSP.h new file mode 100644 index 000000000..1e4900054 --- /dev/null +++ b/src/xrGameLA/UIGameSP.h @@ -0,0 +1,73 @@ +#pragma once +#include "uigamecustom.h" +#include "ui/UIDialogWnd.h" +#include "../../xrNetServer/net_utils.h" +#include "game_graph_space.h" + +class CUITradeWnd; +class CInventory; + +class game_cl_Single; +class CUITalkWnd; +class CChangeLevelWnd; +class CUIMessageBox; +class CInventoryBox; +class CInventoryOwner; + +class CUIGameSP : public CUIGameCustom +{ +private: + game_cl_Single* m_game; + typedef CUIGameCustom inherited; +public: + CUIGameSP (); + virtual ~CUIGameSP (); + + virtual void SetClGame (game_cl_GameState* g); + virtual bool IR_UIOnKeyboardPress (int dik); + virtual bool IR_UIOnKeyboardRelease (int dik); + + void StartTalk (bool disable_break); + void StartStashUI (CInventoryOwner* pActorInv, CInventoryOwner* pOtherOwner); + void StartStashUI (CInventoryOwner* pActorInv, CInventoryBox* pBox); + void OpenSafe (); + void ChangeLevel (GameGraph::_GRAPH_ID game_vert_id, u32 level_vert_id, Fvector pos, Fvector ang, Fvector pos2, Fvector ang2, bool b); + + virtual void HideShownDialogs (); + virtual void ReInitShownUI (); + virtual void ReinitDialogs (); + + virtual void Render (); + + void EnableSkills (bool val); + void EnableDownloads (bool val); + + CUITalkWnd* TalkMenu; + CChangeLevelWnd* UIChangeLevelWnd; + CInventoryBox* StoredInvBox; +}; + + +class CChangeLevelWnd :public CUIDialogWnd +{ + CUIMessageBox* m_messageBox; + typedef CUIDialogWnd inherited; + void OnCancel (); + void OnOk (); +public: + GameGraph::_GRAPH_ID m_game_vertex_id; + u32 m_level_vertex_id; + Fvector m_position; + Fvector m_angles; + Fvector m_position_cancel; + Fvector m_angles_cancel; + bool m_b_position_cancel; + + CChangeLevelWnd (); + virtual ~CChangeLevelWnd () {}; + virtual void SendMessage (CUIWindow *pWnd, s16 msg, void *pData); + virtual bool WorkInPause ()const {return true;} + virtual void ShowDialog (bool bDoHideIndicators); + virtual void HideDialog (); + virtual bool OnKeyboardAction (int dik, EUIMessages keyboard_action); +}; \ No newline at end of file diff --git a/src/xrGameLA/UIGameTDM.cpp b/src/xrGameLA/UIGameTDM.cpp new file mode 100644 index 000000000..36e5eee81 --- /dev/null +++ b/src/xrGameLA/UIGameTDM.cpp @@ -0,0 +1,166 @@ +#include "stdafx.h" +#include "UIGameTDM.h" + +#include "game_cl_base.h" + +#include "game_cl_TeamDeathmatch.h" + +#include "ui/TeamInfo.h" +#include + +#include "object_broker.h" + +#include "UITeamPanels.h" +#include "ui/UIMoneyIndicator.h" +#include "ui/UIRankIndicator.h" + +#define MSGS_OFFS 510 +#define TEAM_PANELS_TDM_XML_NAME "ui_team_panels_tdm.xml" + +//-------------------------------------------------------------------- +CUIGameTDM::CUIGameTDM() +:m_game(NULL) +{ +} + +void CUIGameTDM::SetClGame (game_cl_GameState* g) +{ + inherited::SetClGame(g); + m_game = smart_cast(g); + R_ASSERT(m_game); +} + +void CUIGameTDM::Init (int stage) +{ + if(stage==0) + { //shared + m_pUITeamSelectWnd = new CUISpawnWnd (); + m_team1_icon = new CUIStatic(); + m_team2_icon = new CUIStatic(); + m_team1_score = new CUITextWnd(); + m_team1_score->SetAutoDelete (true); + m_team2_score = new CUITextWnd(); + m_team2_score->SetAutoDelete (true); + m_buy_msg_caption = new CUITextWnd(); + m_buy_msg_caption->SetAutoDelete(true); + + inherited::Init (stage); + CUIXmlInit::InitTextWnd (*m_msgs_xml, "mp_tdm_buy",0, m_buy_msg_caption); + } + if(stage==1) + { //unique + m_pTeamPanels->Init (TEAM_PANELS_TDM_XML_NAME, "team_panels_wnd"); + + CUIXml uiXml, xml2; + uiXml.Load (CONFIG_PATH, UI_PATH, "ui_game_tdm.xml"); + + CUIXmlInit::InitWindow (uiXml, "global", 0, m_window); + CUIXmlInit::InitStatic (uiXml, "team1_icon", 0, m_team1_icon); + CUIXmlInit::InitStatic (uiXml, "team2_icon", 0, m_team2_icon); + CUIXmlInit::InitTextWnd (uiXml, "team1_score", 0, m_team1_score); + CUIXmlInit::InitTextWnd (uiXml, "team2_score", 0, m_team2_score); + CUIXmlInit::InitTextWnd (uiXml, "fraglimit", 0, m_pFragLimitIndicator); + + m_pMoneyIndicator->InitFromXML (uiXml); + m_pRankIndicator->InitFromXml (uiXml); + } + if(stage==2) + { //after + inherited::Init(stage); + m_window->AttachChild (m_team1_score); + m_window->AttachChild (m_team2_score); + m_window->AttachChild (m_buy_msg_caption); + } +} + +void CUIGameTDM::UnLoad() +{ + inherited::UnLoad (); + xr_delete (m_team1_icon); + xr_delete (m_team2_icon); + delete_data (m_pUITeamSelectWnd); +} + +CUIGameTDM::~CUIGameTDM() +{ +} + +bool CUIGameTDM::IR_UIOnKeyboardPress(int dik) +{ + switch (dik) { + case DIK_CAPSLOCK : + { + if (m_game) + { + if (m_game->Get_ShowPlayerNamesEnabled()) + m_game->Set_ShowPlayerNames( !m_game->Get_ShowPlayerNames() ); + else + m_game->Set_ShowPlayerNames(true); + return true; + }; + }break; + } + return inherited::IR_UIOnKeyboardPress(dik); +} + +bool CUIGameTDM::IR_UIOnKeyboardRelease(int dik) +{ + switch (dik) { + case DIK_CAPSLOCK : + { + if (m_game) + { + if (!m_game->Get_ShowPlayerNamesEnabled()) + m_game->Set_ShowPlayerNames(false); + return true; + }; + }break; + } + + return inherited::IR_UIOnKeyboardRelease(dik); +} + +void CUIGameTDM::OnFrame() +{ + inherited::OnFrame(); + m_team1_icon->Update(); + m_team2_icon->Update(); +} + +void CUIGameTDM::Render() +{ + m_team1_icon->Draw(); + m_team2_icon->Draw(); + inherited::Render(); +} + +void CUIGameTDM::SetScoreCaption(int t1, int t2) +{ + string32 str; + xr_sprintf (str,"%d", t1); + m_team1_score->SetText (str); + + xr_sprintf (str,"%d", t2); + m_team2_score->SetText (str); + + m_pTeamPanels->SetArtefactsCount(t1, t2); +} + +void CUIGameTDM::SetFraglimit(int local_frags, int fraglimit) +{ + string64 str; + if(fraglimit) + xr_sprintf(str,"%d", fraglimit); + else + xr_sprintf(str,"%s", "--"); + + m_pFragLimitIndicator->SetText(str); +} + +void CUIGameTDM::SetBuyMsgCaption(LPCSTR str) +{ + if (!str) + m_buy_msg_caption->SetText(""); + else + m_buy_msg_caption->SetTextST(str); +} \ No newline at end of file diff --git a/src/xrGameLA/UIGameTDM.h b/src/xrGameLA/UIGameTDM.h new file mode 100644 index 000000000..ad8692ba5 --- /dev/null +++ b/src/xrGameLA/UIGameTDM.h @@ -0,0 +1,48 @@ +#pragma once + +#include "UIGameCustom.h" +#include "UIGameDM.h" + +#include "ui/UIDialogWnd.h" +#include "ui/UISpawnWnd.h" + +// refs + +class CUITDMFragList; +class CUITDMPlayerList; +class CUISkinSelectorWnd; +class game_cl_TeamDeathmatch; +class CUIStatic; +class CUITextWnd; +class CUISpawnWnd; + +class CUIGameTDM: public CUIGameDM +{ +private: + game_cl_TeamDeathmatch * m_game; + typedef CUIGameDM inherited; + +public: + CUISpawnWnd* m_pUITeamSelectWnd; + +protected: + CUIStatic* m_team1_icon; + CUIStatic* m_team2_icon; + CUITextWnd* m_team1_score; + CUITextWnd* m_team2_score; + CUITextWnd* m_buy_msg_caption; +public: + CUIGameTDM (); + virtual ~CUIGameTDM (); + virtual void SetClGame (game_cl_GameState* g); + virtual void UnLoad (); + virtual void Init (int stage); + void SetScoreCaption (int t1, int t2); + void SetBuyMsgCaption (LPCSTR str); + virtual void SetFraglimit (int local_frags, int fraglimit); + virtual void Render (); + virtual void _BCL OnFrame (); + + virtual bool IR_UIOnKeyboardPress (int dik); + virtual bool IR_UIOnKeyboardRelease (int dik); +}; diff --git a/src/xrGameLA/UIGame_custom_script.cpp b/src/xrGameLA/UIGame_custom_script.cpp new file mode 100644 index 000000000..155c3a01a --- /dev/null +++ b/src/xrGameLA/UIGame_custom_script.cpp @@ -0,0 +1,29 @@ +#include "pch_script.h" +#include "UIGame_custom_script.h" +#include "xrServer_script_macroses.h" +#include "ui/UIMultiTextStatic.h" + +using namespace luabind; + +template +struct CWrapperBase : public T, public luabind::wrap_base { + typedef T inherited; + typedef CWrapperBase self_type; + + DEFINE_LUA_WRAPPER_METHOD_V0(Init) + DEFINE_LUA_WRAPPER_METHOD_V1(SetClGame, game_cl_GameState*) +}; + +#pragma optimize("s",on) +void UIGame_custom_script::script_register(lua_State *L) +{ + typedef CWrapperBase WrapType; + typedef UIGame_custom_script BaseType; + module(L) + [ + class_< UIGame_custom_script, CUIGameCustom, WrapType >("UIGame_custom_script") + .def( constructor<>()) + .def("Init", &BaseType::Init, &WrapType::Init_static) + .def("SetClGame", &BaseType::SetClGame, &WrapType::SetClGame_static) + ]; +} diff --git a/src/xrGameLA/UIGame_custom_script.h b/src/xrGameLA/UIGame_custom_script.h new file mode 100644 index 000000000..b2d055a0b --- /dev/null +++ b/src/xrGameLA/UIGame_custom_script.h @@ -0,0 +1,18 @@ +#pragma once +#include "UIGameCustom.h" +#include "script_export_space.h" +#include "game_cl_Base.h" + +class UIGame_custom_script : public CUIGameCustom +{ + typedef CUIGameCustom inherited; +public: + UIGame_custom_script ():inherited(){}; + virtual void SetClGame (game_cl_GameState* g){inherited::SetClGame(g);}; + virtual void Init (){}; + virtual void ReInitShownUI (){}; + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(UIGame_custom_script) +#undef script_type_list +#define script_type_list save_type_list(UIGame_custom_script) diff --git a/src/xrGameLA/UIStaticItem.cpp b/src/xrGameLA/UIStaticItem.cpp new file mode 100644 index 000000000..0c14c7e25 --- /dev/null +++ b/src/xrGameLA/UIStaticItem.cpp @@ -0,0 +1,221 @@ +#include "stdafx.h" +#include "UIStaticItem.h" +#include "ui_base.h" + + +void CreateUIGeom() +{ + UIRender->CreateUIGeom(); +} + +void DestroyUIGeom() +{ + UIRender->DestroyUIGeom(); +} + +CUIStaticItem::CUIStaticItem() +{ + uFlags.zero (); + vSize.set (0,0); + TextureRect.set (0,0,0,0); + vHeadingPivot.set (0,0); + vHeadingOffset.set (0,0); + dwColor = 0xffffffff; +} + +void CUIStaticItem::ResetHeadingPivot() +{ + uFlags.set(flValidHeadingPivot, FALSE); + uFlags.set(flFixedLTWhileHeading,FALSE); +} + +void CUIStaticItem::SetHeadingPivot(const Fvector2& p, const Fvector2& offset, bool fixedLT) +{ + vHeadingPivot=p; + vHeadingOffset=offset; + uFlags.set(flValidHeadingPivot, TRUE); + if(fixedLT) + uFlags.set(flFixedLTWhileHeading,TRUE); + else + uFlags.set(flFixedLTWhileHeading,FALSE); +} + +void CUIStaticItem::RenderInternal(const Fvector2& in_pos) +{ + Fvector2 pos; + UI().ClientToScreenScaled (pos, in_pos.x, in_pos.y); + UI().AlignPixel (pos.x); + UI().AlignPixel (pos.y); + + Fvector2 ts; + UIRender->GetActiveTextureResolution(ts); + + if(!uFlags.test(flValidSize)) + SetSize(ts); + + if(!uFlags.test(flValidTextureRect)) + SetTextureRect(Frect().set(0,0,ts.x,ts.y)); + + Fvector2 LTp,RBp; + Fvector2 LTt,RBt; + //координаты на экране в пикселях + LTp.set (pos); + + UI().ClientToScreenScaled (RBp, vSize.x, vSize.y); + RBp.add (pos); + + //текстурные координаты + LTt.set ( TextureRect.x1/ts.x, TextureRect.y1/ts.y); + RBt.set ( TextureRect.x2/ts.x, TextureRect.y2/ts.y); + + float offset = -0.5f; + if(UI().m_currentPointType==IUIRender::pttLIT) + offset = 0.0f; + + // clip poly + sPoly2D S; + S.resize (4); + + LTp.x +=offset; + LTp.y +=offset; + RBp.x +=offset; + RBp.y +=offset; + + S[0].set (LTp.x, LTp.y, LTt.x, LTt.y); // LT + S[1].set (RBp.x, LTp.y, RBt.x, LTt.y); // RT + S[2].set (RBp.x, RBp.y, RBt.x, RBt.y); // RB + S[3].set (LTp.x, RBp.y, LTt.x, RBt.y); // LB + + sPoly2D D; + sPoly2D* R = NULL; + + if(UI().m_currentPointType!=IUIRender::pttLIT) + R = UI().ScreenFrustum().ClipPoly(S,D); + else + { + R = UI().ScreenFrustumLIT().ClipPoly(S,D); + } + + if (R && R->size()) + { + for (u32 k=0; ksize()-2; ++k) + { + UIRender->PushPoint((*R)[0+0].pt.x, (*R)[0+0].pt.y, 0, dwColor, (*R)[0+0].uv.x, (*R)[0+0].uv.y); + UIRender->PushPoint((*R)[k+1].pt.x, (*R)[k+1].pt.y, 0, dwColor, (*R)[k+1].uv.x, (*R)[k+1].uv.y); + UIRender->PushPoint((*R)[k+2].pt.x, (*R)[k+2].pt.y, 0, dwColor, (*R)[k+2].uv.x, (*R)[k+2].uv.y); + } + } +} + +void CUIStaticItem::RenderInternal(float angle) +{ + Fvector2 ts; + Fvector2 hp; + + UIRender->GetActiveTextureResolution(ts); + hp.set (0.5f/ts.x,0.5f/ts.y); + + if(!uFlags.test(flValidSize)) + SetSize(ts); + + if(!uFlags.test(flValidTextureRect)) + SetTextureRect(Frect().set(0,0,ts.x,ts.y)); + + Fvector2 pivot,offset,SZ; + SZ.set (vSize); + + + float cosA = _cos(angle); + float sinA = _sin(angle); + + // Rotation + if( !uFlags.test(flValidHeadingPivot) ) + pivot.set(vSize.x/2.f, vSize.y/2.f); + else + pivot.set(vHeadingPivot.x, vHeadingPivot.y); + + offset.set (vPos); + offset.add (vHeadingOffset); + + Fvector2 LTt,RBt; + LTt.set (TextureRect.x1/ts.x+hp.x, TextureRect.y1/ts.y+hp.y); + RBt.set (TextureRect.x2/ts.x+hp.x, TextureRect.y2/ts.y+hp.y); + + float kx = UI().get_current_kx(); + + // clip poly + sPoly2D S; + S.resize (4); + // LT + S[0].set (0.f,0.f,LTt.x,LTt.y); + S[0].rotate_pt (pivot,cosA,sinA,kx); + S[0].pt.add (offset); + + // RT + S[1].set (SZ.x,0.f,RBt.x,LTt.y); + S[1].rotate_pt (pivot,cosA,sinA,kx); + S[1].pt.add (offset); + // RB + S[2].set (SZ.x,SZ.y,RBt.x,RBt.y); + S[2].rotate_pt (pivot,cosA,sinA,kx); + S[2].pt.add (offset); + // LB + S[3].set (0.f,SZ.y,LTt.x,RBt.y); + S[3].rotate_pt (pivot,cosA,sinA,kx); + S[3].pt.add (offset); + + for(int i=0; i<4;++i) + UI().ClientToScreenScaled (S[i].pt); + + sPoly2D D; + sPoly2D* R = UI().ScreenFrustum().ClipPoly(S,D); + if (R&&R->size()){ + for (u32 k=0; ksize()-2; k++) + { + UIRender->PushPoint((*R)[0+0].pt.x, (*R)[0+0].pt.y, 0, dwColor, (*R)[0+0].uv.x, (*R)[0+0].uv.y); + UIRender->PushPoint((*R)[k+1].pt.x, (*R)[k+1].pt.y, 0, dwColor, (*R)[k+1].uv.x, (*R)[k+1].uv.y); + UIRender->PushPoint((*R)[k+2].pt.x, (*R)[k+2].pt.y, 0, dwColor, (*R)[k+2].uv.x, (*R)[k+2].uv.y); + } + } +} + +//---from static-item + +void CUIStaticItem::Render() +{ + VERIFY (g_bRendering); + UIRender->SetShader (*hShader); + UIRender->StartPrimitive (8, IUIRender::ptTriList, UI().m_currentPointType); + RenderInternal (vPos); + UIRender->FlushPrimitive (); +} + +void CUIStaticItem::Render(float angle) +{ + VERIFY (g_bRendering); + + UIRender->SetShader (*hShader); + UIRender->StartPrimitive (32, IUIRender::ptTriList, UI().m_currentPointType); + RenderInternal (angle); + UIRender->FlushPrimitive (); +} + + +void CUIStaticItem::CreateShader(LPCSTR tex, LPCSTR sh) +{ + hShader->create(sh,tex); + +#ifdef DEBUG + dbg_tex_name = tex; +#endif + uFlags.set(flValidSize, FALSE); + uFlags.set(flValidTextureRect, FALSE); +} + + +void CUIStaticItem::Init(LPCSTR tex, LPCSTR sh, float left, float top) +{ + uFlags.set (flValidSize, FALSE); + CreateShader (tex,sh); + SetPos (left,top); +} diff --git a/src/xrGameLA/UIStaticItem.h b/src/xrGameLA/UIStaticItem.h new file mode 100644 index 000000000..83f0866d4 --- /dev/null +++ b/src/xrGameLA/UIStaticItem.h @@ -0,0 +1,61 @@ +#pragma once +#include "ui_defs.h" + + +class CUIStaticItem +{ +protected: +public: + enum { + flValidSize =(1<<0), + flValidTextureRect =(1<<1), + flValidHeadingPivot =(1<<2), + flFixedLTWhileHeading =(1<<3), + }; + + Frect TextureRect; + Fvector2 vHeadingPivot; + Fvector2 vHeadingOffset; + Flags8 uFlags; + + ui_shader hShader; + Fvector2 vPos; + Fvector2 vSize; + u32 dwColor; +#ifdef DEBUG + shared_str dbg_tex_name; +#endif + + void CreateShader (LPCSTR tex, LPCSTR sh = "hud\\default"); + void SetShader (const ui_shader& sh) {hShader = sh;}; + void Init (LPCSTR tex, LPCSTR sh, float left, float top); + void Render (); + void Render (float angle); + + IC void SetPos (float left, float top) {vPos.set(left,top);} + IC void SetPos (const Fvector2& pos) {vPos.set(pos);} + IC float GetPosX () {return vPos.x;} + IC float GetPosY () {return vPos.y;} + + IC void SetTextureColor (u32 clr) {dwColor= clr;} + IC u32 GetTextureColor () const {return dwColor;} + ui_shader& GetShader () {return hShader;} + +public: + CUIStaticItem (); + IC void SetSize (const Fvector2& sz) {vSize.set(sz); uFlags.set(flValidSize, TRUE); } + void SetTextureRect (const Frect& r) {TextureRect = r; uFlags.set(flValidTextureRect,TRUE);} + void SetTextureRect (float x, float y, float w, float h) { SetTextureRect(Frect().set(x,y,w,h)); } + const Frect& GetTextureRect () const {return TextureRect;}; + + IC Fvector2 GetSize () {return vSize;} + + void SetHeadingPivot (const Fvector2& p, const Fvector2& offset, bool fixedLT); + void ResetHeadingPivot (); + IC bool GetFixedLTWhileHeading () const {return !!uFlags.test(flFixedLTWhileHeading);} + Fvector2 GetHeadingPivot () {return vHeadingPivot;} + +private: + void RenderInternal (const Fvector2& pos); + void RenderInternal (float angle); +}; diff --git a/src/xrGameLA/UIZoneMap.cpp b/src/xrGameLA/UIZoneMap.cpp new file mode 100644 index 000000000..d6b7f85db --- /dev/null +++ b/src/xrGameLA/UIZoneMap.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "uizonemap.h" + +#include "hudmanager.h" + + +#include "InfoPortion.h" +#include "Pda.h" + +#include "Grenade.h" +#include "level.h" +#include "game_cl_base.h" + +#include "actor.h" +#include "ai_space.h" +#include "game_graph.h" + +#include "ui/UIMap.h" +#include "ui/UIXmlInit.h" +////////////////////////////////////////////////////////////////////////// + +CUIZoneMap::CUIZoneMap() +{} + +CUIZoneMap::~CUIZoneMap() +{ + +} + +void CUIZoneMap::Init() +{ + + CUIXml uiXml; + string128 XmlName; + if (!ui_hud_type) + ui_hud_type = 1; + + xr_sprintf (XmlName, "zone_map_%d.xml", ui_hud_type); + + bool xml_result = uiXml.Load(CONFIG_PATH, UI_PATH, XmlName); + R_ASSERT3(xml_result, "xml file not found", XmlName); + + // load map background + CUIXmlInit xml_init; + xml_init.InitStatic (uiXml, "minimap:background", 0, &m_background); + + if(IsGameTypeSingle()){ + xml_init.InitStatic (uiXml, "minimap:background:dist_text", 0, &m_pointerDistanceText); + m_background.AttachChild (&m_pointerDistanceText); + } + + xml_init.InitStatic(uiXml, "minimap:level_frame", 0, &m_clipFrame); + + xml_init.InitStatic(uiXml, "minimap:center", 0, &m_center); + + + m_activeMap = new CUIMiniMap(); + m_clipFrame.AttachChild (m_activeMap); + m_activeMap->SetAutoDelete (true); + + m_activeMap->EnableHeading (true); + xml_init.InitStatic (uiXml, "minimap:compass", 0, &m_compass); + +// m_background.AttachChild(&m_compass); + + m_clipFrame.AttachChild (&m_center); + m_center.SetWndPos (m_clipFrame.GetWidth()/2,m_clipFrame.GetHeight()/2); +} + +void CUIZoneMap::Render () +{ + m_clipFrame.Draw (); + m_background.Draw (); + m_compass.Draw (); +} + +void CUIZoneMap::SetHeading (float angle) +{ + m_activeMap->SetHeading(angle); + m_compass.SetHeading(angle); +}; + +void CUIZoneMap::UpdateRadar (Fvector pos) +{ + m_clipFrame.Update(); + m_background.Update(); + m_activeMap->SetActivePoint( pos ); + + if(IsGameTypeSingle()){ + if(m_activeMap->GetPointerDistance()>0.5f){ + string64 str; + xr_sprintf (str,"%.1f m.",m_activeMap->GetPointerDistance()); + m_pointerDistanceText.SetText(str); + }else{ + m_pointerDistanceText.SetText(""); + } + } +} + +bool CUIZoneMap::ZoomIn() +{ + return true; +} + +bool CUIZoneMap::ZoomOut() +{ + return true; +} + +void CUIZoneMap::SetupCurrentMap() +{ + CInifile* pLtx = pGameIni; + + if(!pLtx->section_exist(Level().name())) + pLtx = Level().pLevel; + + m_activeMap->Init (Level().name(),*pLtx,"hud\\default"); + + Frect r; + m_clipFrame.GetAbsoluteRect (r); + m_activeMap->WorkingArea().set (r); + + Fvector2 wnd_size; + float zoom_factor = float(m_clipFrame.GetWndRect().width())/100.0f; + wnd_size.x = m_activeMap->BoundRect().width()*zoom_factor; + wnd_size.y = m_activeMap->BoundRect().height()*zoom_factor; + m_activeMap->SetWndSize (wnd_size); +} diff --git a/src/xrGameLA/UIZoneMap.h b/src/xrGameLA/UIZoneMap.h new file mode 100644 index 000000000..64825a6b0 --- /dev/null +++ b/src/xrGameLA/UIZoneMap.h @@ -0,0 +1,41 @@ +#pragma once + + +#include "ui/UIStatic.h" + +class CActor; +class CUICustomMap; +////////////////////////////////////////////////////////////////////////// + + +class CUIZoneMap +{ + CUICustomMap* m_activeMap; + float m_fScale; + + CUIStatic m_background; + CUIStatic m_center; + CUIStatic m_compass; + CUIStatic m_clipFrame; + CUIStatic m_pointerDistanceText; + +public: + CUIZoneMap (); + virtual ~CUIZoneMap (); + + void SetHeading (float angle); + void Init (); + + void Render (); + void UpdateRadar (Fvector pos); + + void SetScale (float s) {m_fScale = s;} + float GetScale () {return m_fScale;} + + bool ZoomIn (); + bool ZoomOut (); + + CUIStatic& Background () {return m_background;}; + void SetupCurrentMap (); +}; + diff --git a/src/xrGameLA/UsableScriptObject.cpp b/src/xrGameLA/UsableScriptObject.cpp new file mode 100644 index 000000000..f7a61fc25 --- /dev/null +++ b/src/xrGameLA/UsableScriptObject.cpp @@ -0,0 +1,50 @@ +#include "pch_script.h" +#include "UsableScriptObject.h" +#include "GameObject.h" +#include "script_game_object.h" +#include "script_callback_ex.h" +#include "game_object_space.h" + +using namespace luabind; + +CUsableScriptObject::CUsableScriptObject() +{ + m_bNonscriptUsable = true; + set_tip_text_default(); +} + +CUsableScriptObject::~CUsableScriptObject() +{ +} + +bool CUsableScriptObject::use(CGameObject* who_use) +{ + VERIFY(who_use); + CGameObject* pThis = smart_cast(this); VERIFY(pThis); + + pThis->callback(GameObject::eUseObject)(pThis->lua_game_object(),who_use->lua_game_object()); + + return true; +} + +LPCSTR CUsableScriptObject::tip_text () +{ + return *m_sTipText; +} +void CUsableScriptObject::set_tip_text (LPCSTR new_text) +{ + m_sTipText = new_text; +} +void CUsableScriptObject::set_tip_text_default () +{ + m_sTipText = NULL; +} + +bool CUsableScriptObject::nonscript_usable () +{ + return m_bNonscriptUsable; +} +void CUsableScriptObject::set_nonscript_usable (bool usable) +{ + m_bNonscriptUsable = usable; +} diff --git a/src/xrGameLA/UsableScriptObject.h b/src/xrGameLA/UsableScriptObject.h new file mode 100644 index 000000000..027bb6ae8 --- /dev/null +++ b/src/xrGameLA/UsableScriptObject.h @@ -0,0 +1,25 @@ +#pragma once + +#include "script_space_forward.h" + +class CGameObject; + +class CUsableScriptObject +{ +public: + CUsableScriptObject (); + ~CUsableScriptObject (); + virtual bool use (CGameObject* who_use); + + //строчка появляющаяся при наведении на объект (если NULL, то нет) + virtual LPCSTR tip_text (); + void set_tip_text (LPCSTR new_text); + virtual void set_tip_text_default (); + + //можно ли использовать объект стандартным (не скриптовым) образом + bool nonscript_usable (); + void set_nonscript_usable (bool usable); +private: + shared_str m_sTipText; + bool m_bNonscriptUsable; +}; diff --git a/src/xrGameLA/WaveForm.h b/src/xrGameLA/WaveForm.h new file mode 100644 index 000000000..302f4716f --- /dev/null +++ b/src/xrGameLA/WaveForm.h @@ -0,0 +1,64 @@ +#ifndef WAVEFORM_H +#define WAVEFORM_H +#pragma once + +#pragma pack(push,4) +struct WaveForm +{ + enum EFunction + { + fCONSTANT = 0, + fSIN, + fTRIANGLE, + fSQUARE, + fSAWTOOTH, + fINVSAWTOOTH, + fFORCE32 = u32(-1) + }; + IC float signf (float t) { return t/_abs(t); } + IC float Func (float t) + { + switch (F) + { + case fCONSTANT: return 0; + case fSIN: return _sin(t*PI_MUL_2); + case fTRIANGLE: return asinf(_sin((t-0.25f)*PI_MUL_2))/PI_DIV_2; + case fSQUARE: return signf(_cos(t*PI)); + case fSAWTOOTH: return atanf(tanf((t+0.5f)*PI))/PI_DIV_2; + case fINVSAWTOOTH: return -(atanf(tanf((t+0.5f)*PI))/PI_DIV_2); + } + return 0.f; + } +public: + EFunction F; + float arg[4]; + + IC float Calculate (float t) + { + // y = arg0 + arg1*func( (time+arg2)*arg3 ) + float x = (t+arg[2])*arg[3]; + return arg[0] + arg[1]*Func(x-floorf(x)); + } + + WaveForm() { + F = fCONSTANT; + arg[0] = 0; + arg[1] = 1; + arg[2] = 0; + arg[3] = 1; + } + + IC BOOL Similar (const WaveForm& W) const + { + if (!fsimilar(arg[0],W.arg[0],EPS_L)) return FALSE; + if (!fsimilar(arg[1],W.arg[1],EPS_L)) return FALSE; + if (fis_zero(arg[1],EPS_L)) return TRUE; + if (F != W.F) return FALSE; + if (!fsimilar(arg[2],W.arg[2],EPS_L)) return FALSE; + if (!fsimilar(arg[3],W.arg[3],EPS_L)) return FALSE; + return TRUE; + } +}; + +#pragma pack(pop) +#endif diff --git a/src/xrGameLA/Weapon.cpp b/src/xrGameLA/Weapon.cpp new file mode 100644 index 000000000..d898290e4 --- /dev/null +++ b/src/xrGameLA/Weapon.cpp @@ -0,0 +1,2061 @@ +// Weapon.cpp: implementation of the CWeapon class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "Weapon.h" +#include "ParticlesObject.h" +#include "HUDManager.h" +#include "player_hud.h" +#include "entity_alive.h" +#include "inventory_item_impl.h" +#include "gamepersistent.h" +#include "inventory.h" +#include "xrserver_objects_alife_items.h" + +#include "actor.h" +#include "actoreffector.h" +#include "level.h" + +#include "xr_level_controller.h" +#include "game_cl_base.h" +#include "../Include/xrRender/Kinematics.h" +#include "ai_object_location.h" +#include "clsid_game.h" +#include "mathutils.h" +#include "object_broker.h" +#include "../igame_persistent.h" +#include "weaponBinocularsVision.h" +#include "ui/UIWindow.h" +#include "ui/UIXmlInit.h" + +#include "debug_renderer.h" + +#include "CustomOutfit.h" + +#include "ai\trader\ai_trader.h" + +#define WEAPON_REMOVE_TIME 60000 +#define ROTATION_TIME 0.25f + +bool m_bDraw_off = true; +bool m_bHolster_off = true; + +extern CUIXml* pWpnScopeXml; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +CWeapon::SZoomParams::SZoomParams() +{ + m_bZoomEnabled = true; + m_bHideCrosshairInZoom = true; + m_bIsZoomModeNow = false; + m_fCurrentZoomFactor = 50.f; + m_fZoomRotateTime = 0.15; + m_fIronSightZoomFactor = 50.f; + m_fScopeZoomFactor = 50.f; + m_fZoomRotationFactor = 0.f; + m_bUseDynamicZoom = FALSE; + m_pVision = nullptr; + m_pNight_vision = nullptr; +} + +CWeapon::CWeapon(LPCSTR name) +{ + SetState (eHidden); + SetNextState (eHidden); + m_sub_state = eSubstateReloadBegin; + m_bTriStateReload = false; + SetDefaults (); + + m_Offset.identity (); + m_StrapOffset.identity (); + + iAmmoCurrent = 0; + m_dwAmmoCurrentCalcFrame= 0; + + iAmmoElapsed = -1; + maxMagazineSize_ = -1; + m_ammoType = 0; + m_ammoName = NULL; + + eHandDependence = hdNone; + + m_zoom_params.m_fCurrentZoomFactor = g_fov; + m_zoom_params.m_fZoomRotationFactor = 0.f; + m_zoom_params.m_pVision = NULL; + m_zoom_params.m_pNight_vision = NULL; + + m_pAmmo = NULL; + + + m_pFlameParticles2 = NULL; + m_sFlameParticles2 = NULL; + + + m_fCurrentCartirdgeDisp = 1.f; + + m_strap_bone0 = 0; + m_strap_bone1 = 0; + m_strap_bone0_id = -1; + m_strap_bone1_id = -1; + m_StrapOffset.identity (); + m_strapped_mode = false; + m_strapped_mode_rifle = false; + m_can_be_strapped_rifle = false; + m_can_be_strapped = false; + m_ef_main_weapon_type = u32(-1); + m_ef_weapon_type = u32(-1); + m_UIScope = NULL; + m_set_next_ammoType_on_reload = u8(-1); + m_cur_scope = 0; + m_bRememberActorNVisnStatus = false; + m_fRTZoomFactor = 0.f; + m_ai_weapon_rank = u32(-1); +} + +CWeapon::~CWeapon () +{ + xr_delete (m_UIScope); +} + +//void CWeapon::Hit(float P, Fvector &dir, +// CObject* who, s16 element, +// Fvector position_in_object_space, +// float impulse, +// ALife::EHitType hit_type) +void CWeapon::Hit (SHit* pHDS) +{ +// inherited::Hit(P, dir, who, element, position_in_object_space,impulse,hit_type); + inherited::Hit(pHDS); +} + + + +void CWeapon::UpdateXForm () +{ + if (Device.dwFrame == dwXF_Frame) + return; + + dwXF_Frame = Device.dwFrame; + + if (!H_Parent()) + return; + + // Get access to entity and its visual + CEntityAlive* E = smart_cast(H_Parent()); + CAI_Trader* T = smart_cast(H_Parent()); + + if (T) return; + + if (!E) { + if (!IsGameTypeSingle()) + UpdatePosition (H_Parent()->XFORM()); + + return; + } + + const CInventoryOwner *parent = smart_cast(E); + if (!parent || parent->use_simplified_visual()) + return; + + if (!m_can_be_strapped_rifle) + { + if (parent->attached(this)) + return; + } + + IKinematics* V = smart_cast (E->Visual()); + VERIFY (V); + + // Get matrices + int boneL = -1, boneR = -1, boneR2 = -1; + + if ((m_strap_bone0_id == -1 || m_strap_bone1_id == -1) && m_can_be_strapped_rifle) + { + m_strap_bone0_id = V->LL_BoneID(m_strap_bone0); + m_strap_bone1_id = V->LL_BoneID(m_strap_bone1); + } + + u32 activeSlot = parent->inventory().GetActiveSlot(); + if (activeSlot != CurrSlot() && m_can_be_strapped_rifle && parent->inventory().InSlot(this)) + { + boneR = m_strap_bone0_id; + boneR2 = m_strap_bone1_id; + boneL = boneR; + + if (!m_strapped_mode_rifle) m_strapped_mode_rifle = true; + } else { + E->g_WeaponBones (boneL,boneR,boneR2); + + if (m_strapped_mode_rifle) m_strapped_mode_rifle = false; + } + + if (boneR == -1) return; + + if ((HandDependence() == hd1Hand) || (GetState() == eReload) || (!E->g_Alive())) + boneL = boneR2; + + if (boneL == -1) return; + + V->CalculateBones (); + Fmatrix& mL = V->LL_GetTransform(u16(boneL)); + Fmatrix& mR = V->LL_GetTransform(u16(boneR)); + // Calculate + Fmatrix mRes; + Fvector R,D,N; + D.sub (mL.c,mR.c); + + if(fis_zero(D.magnitude())) { + mRes.set (E->XFORM()); + mRes.c.set (mR.c); + } + else { + D.normalize (); + R.crossproduct (mR.j,D); + + N.crossproduct (D,R); + N.normalize(); + + mRes.set (R,N,D,mR.c); + mRes.mulA_43 (E->XFORM()); + } + + UpdatePosition (mRes); +} + +void CWeapon::UpdateFireDependencies_internal() +{ + if (Device.dwFrame!=dwFP_Frame) + { + dwFP_Frame = Device.dwFrame; + + Fmatrix xform = XFORM(); + try { + UpdateXForm(); + } + catch (...) + { + Msg("! Failed to update xform for [%s] parent [%s]", cNameSect().c_str(), H_Parent() ? H_Parent()->cName().c_str() : "NULL Parent"); + XFORM().set(xform); // last matrix + } + + if ( GetHUDmode() ) + { + HudItemData()->setup_firedeps (m_firedeps); + VERIFY(_valid(m_firedeps.m_FireParticlesXForm)); + } else { + // 3rd person or no parent + Fmatrix& parent = XFORM(); + Fvector& fp = vLoadedFirePoint; + Fvector& fp2 = vLoadedFirePoint2; + Fvector& sp = vLoadedShellPoint; + + parent.transform_tiny (m_firedeps.vLastFP,fp); + parent.transform_tiny (m_firedeps.vLastFP2,fp2); + parent.transform_tiny (m_firedeps.vLastSP,sp); + + m_firedeps.vLastFD.set (0.f,0.f,1.f); + parent.transform_dir (m_firedeps.vLastFD); + + m_firedeps.m_FireParticlesXForm.set(parent); + } + } +} + +void CWeapon::ForceUpdateFireParticles() +{ + if ( !GetHUDmode() ) + {//update particlesXFORM real bullet direction + + if (!H_Parent()) return; + + Fvector p, d; + smart_cast(H_Parent())->g_fireParams (this, p,d); + + Fmatrix _pxf; + _pxf.k = d; + _pxf.i.crossproduct (Fvector().set(0.0f,1.0f,0.0f), _pxf.k); + _pxf.j.crossproduct (_pxf.k, _pxf.i); + _pxf.c = XFORM().c; + + m_firedeps.m_FireParticlesXForm.set (_pxf); + + } + +} + +void CWeapon::Load (LPCSTR section) +{ + inherited::Load (section); + CShootingObject::Load (section); + + + if(pSettings->line_exist(section, "flame_particles_2")) + m_sFlameParticles2 = pSettings->r_string(section, "flame_particles_2"); + +#ifdef DEBUG + { + Fvector pos,ypr; + pos = pSettings->r_fvector3 (section,"position"); + ypr = pSettings->r_fvector3 (section,"orientation"); + ypr.mul (PI/180.f); + + m_Offset.setHPB (ypr.x,ypr.y,ypr.z); + m_Offset.translate_over (pos); + } + + m_StrapOffset = m_Offset; + if (pSettings->line_exist(section,"strap_position") && pSettings->line_exist(section,"strap_orientation")) { + Fvector pos,ypr; + pos = pSettings->r_fvector3 (section,"strap_position"); + ypr = pSettings->r_fvector3 (section,"strap_orientation"); + ypr.mul (PI/180.f); + + m_StrapOffset.setHPB (ypr.x,ypr.y,ypr.z); + m_StrapOffset.translate_over (pos); + } +#endif + + // load ammo classes + m_ammoTypes.clear (); + LPCSTR S = pSettings->r_string(section,"ammo_class"); + if (S && S[0]) + { + string128 _ammoItem; + int count = _GetItemCount (S); + for (int it=0; itr_string(*m_ammoTypes[0],"inv_name_short"); + } + else + m_ammoName = 0; + + iAmmoElapsed = pSettings->r_s32 (section,"ammo_elapsed" ); + maxMagazineSize_ = pSettings->r_s32 (section,"ammo_mag_size" ); + + //////////////////////////////////////////////////// + // дисперсия стрельбы + + //подбрасывание камеры во время отдачи + u8 rm = READ_IF_EXISTS( pSettings, r_u8, section, "cam_return", 1 ); + cam_recoil.camReturnMode = (rm == 1); + + rm = READ_IF_EXISTS( pSettings, r_u8, section, "cam_return_stop", 0 ); + cam_recoil.camStopReturn = (rm == 1); + + float temp_f = 0.0f; + temp_f = pSettings->r_float( section,"cam_relax_speed" ); + cam_recoil.camRelaxSpeed = _abs(deg2rad(temp_f)); + VERIFY(!fis_zero(cam_recoil.camRelaxSpeed)); + if (fis_zero(cam_recoil.camRelaxSpeed)) + { + cam_recoil.camRelaxSpeed = EPS_L; + } + + cam_recoil.camRelaxSpeed_AI = cam_recoil.camRelaxSpeed; + if ( pSettings->line_exist( section, "cam_relax_speed_ai" ) ) + { + temp_f = pSettings->r_float( section, "cam_relax_speed_ai" ); + cam_recoil.camRelaxSpeed_AI = _abs(deg2rad(temp_f)); + VERIFY(!fis_zero(cam_recoil.camRelaxSpeed_AI)); + if (fis_zero(cam_recoil.camRelaxSpeed_AI)) + { + cam_recoil.camRelaxSpeed_AI = EPS_L; + } + } + temp_f = pSettings->r_float( section, "cam_max_angle" ); + cam_recoil.camMaxAngleVert = _abs(deg2rad(temp_f)); + VERIFY(!fis_zero(cam_recoil.camMaxAngleVert)); + if (fis_zero(cam_recoil.camMaxAngleVert)) + { + cam_recoil.camMaxAngleVert = EPS; + } + + temp_f = pSettings->r_float( section, "cam_max_angle_horz" ); + cam_recoil.camMaxAngleHorz = _abs(deg2rad(temp_f)); + VERIFY(!fis_zero(cam_recoil.camMaxAngleHorz)); + if (fis_zero(cam_recoil.camMaxAngleHorz)) + { + cam_recoil.camMaxAngleHorz = EPS; + } + + temp_f = pSettings->r_float( section, "cam_step_angle_horz" ); + cam_recoil.camStepAngleHorz = deg2rad(temp_f); + + cam_recoil.camDispersionFrac = _abs(READ_IF_EXISTS(pSettings, r_float, section, "cam_dispersion_frac", 0.7f)); + + //подбрасывание камеры во время отдачи в режиме zoom ==> ironsight or scope + //zoom_cam_recoil.Clone( cam_recoil ); ==== нельзя !!!!!!!!!! + zoom_cam_recoil.camRelaxSpeed = cam_recoil.camRelaxSpeed; + zoom_cam_recoil.camRelaxSpeed_AI = cam_recoil.camRelaxSpeed_AI; + zoom_cam_recoil.camDispersionFrac = cam_recoil.camDispersionFrac; + zoom_cam_recoil.camMaxAngleVert = cam_recoil.camMaxAngleVert; + zoom_cam_recoil.camMaxAngleHorz = cam_recoil.camMaxAngleHorz; + zoom_cam_recoil.camStepAngleHorz = cam_recoil.camStepAngleHorz; + + zoom_cam_recoil.camReturnMode = cam_recoil.camReturnMode; + zoom_cam_recoil.camStopReturn = cam_recoil.camStopReturn; + + + if ( pSettings->line_exist( section, "zoom_cam_relax_speed" ) ) + { + zoom_cam_recoil.camRelaxSpeed = _abs(deg2rad(pSettings->r_float(section, "zoom_cam_relax_speed"))); + VERIFY(!fis_zero(zoom_cam_recoil.camRelaxSpeed)); + if (fis_zero(zoom_cam_recoil.camRelaxSpeed)) + { + zoom_cam_recoil.camRelaxSpeed = EPS_L; + } + } + if ( pSettings->line_exist( section, "zoom_cam_relax_speed_ai" ) ) + { + zoom_cam_recoil.camRelaxSpeed_AI = _abs(deg2rad(pSettings->r_float(section, "zoom_cam_relax_speed_ai"))); + VERIFY(!fis_zero(zoom_cam_recoil.camRelaxSpeed_AI)); + if (fis_zero(zoom_cam_recoil.camRelaxSpeed_AI)) + { + zoom_cam_recoil.camRelaxSpeed_AI = EPS_L; + } + } + if ( pSettings->line_exist( section, "zoom_cam_max_angle" ) ) + { + zoom_cam_recoil.camMaxAngleVert = _abs(deg2rad(pSettings->r_float(section, "zoom_cam_max_angle"))); + VERIFY(!fis_zero(zoom_cam_recoil.camMaxAngleVert)); + if (fis_zero(zoom_cam_recoil.camMaxAngleVert)) + { + zoom_cam_recoil.camMaxAngleVert = EPS; + } + } + if ( pSettings->line_exist( section, "zoom_cam_max_angle_horz" ) ) + { + zoom_cam_recoil.camMaxAngleHorz = _abs(deg2rad(pSettings->r_float(section, "zoom_cam_max_angle_horz"))); + VERIFY(!fis_zero(zoom_cam_recoil.camMaxAngleHorz)); + if (fis_zero(zoom_cam_recoil.camMaxAngleHorz)) + { + zoom_cam_recoil.camMaxAngleHorz = EPS; + } + } + if ( pSettings->line_exist( section, "zoom_cam_step_angle_horz" ) ) { + zoom_cam_recoil.camStepAngleHorz = deg2rad(pSettings->r_float(section, "zoom_cam_step_angle_horz")); + } + if ( pSettings->line_exist( section, "zoom_cam_dispersion_frac" ) ) { + zoom_cam_recoil.camDispersionFrac = _abs(pSettings->r_float(section, "zoom_cam_dispersion_frac")); + } + + m_pdm.m_fPDM_disp_base = pSettings->r_float( section, "PDM_disp_base" ); + m_pdm.m_fPDM_disp_vel_factor = pSettings->r_float( section, "PDM_disp_vel_factor" ); + m_pdm.m_fPDM_disp_accel_factor = pSettings->r_float( section, "PDM_disp_accel_factor" ); + m_pdm.m_fPDM_disp_crouch = READ_IF_EXISTS(pSettings, r_float, section, "PDM_disp_crouch", 1.0f); //pSettings->r_float( section, "PDM_disp_crouch" ); + m_pdm.m_fPDM_disp_crouch_no_acc = READ_IF_EXISTS(pSettings, r_float, section, "PDM_disp_crouch_no_acc", 1.0f); //pSettings->r_float( section, "PDM_disp_crouch_no_acc" ); + // [8/2/2005] + + m_fZoomInertCoef = READ_IF_EXISTS(pSettings, r_float, section, "zoom_inertion_factor", 1.0f); + + fireDispersionConditionFactor = pSettings->r_float(section,"fire_dispersion_condition_factor"); +// modified by Peacemaker [17.10.08] +// misfireProbability = pSettings->r_float(section,"misfire_probability"); +// misfireConditionK = READ_IF_EXISTS(pSettings, r_float, section, "misfire_condition_k", 1.0f); + misfireStartCondition = pSettings->r_float(section, "misfire_start_condition"); + misfireEndCondition = READ_IF_EXISTS(pSettings, r_float, section, "misfire_end_condition", 0.f); + misfireStartProbability = READ_IF_EXISTS(pSettings, r_float, section, "misfire_start_prob", 0.f); + misfireEndProbability = pSettings->r_float(section, "misfire_end_prob"); + conditionDecreasePerShot = pSettings->r_float(section,"condition_shot_dec"); + + + vLoadedFirePoint = pSettings->r_fvector3 (section,"fire_point" ); + + if(pSettings->line_exist(section,"fire_point2")) + vLoadedFirePoint2= pSettings->r_fvector3 (section,"fire_point2"); + else + vLoadedFirePoint2= vLoadedFirePoint; + + // hands + eHandDependence = EHandDependence(pSettings->r_s32(section,"hand_dependence")); + m_bIsSingleHanded = true; + if (pSettings->line_exist(section, "single_handed")) + m_bIsSingleHanded = !!pSettings->r_bool(section, "single_handed"); + // + m_fMinRadius = pSettings->r_float (section,"min_radius"); + m_fMaxRadius = pSettings->r_float (section,"max_radius"); + + + // информация о возможных апгрейдах и их визуализации в инвентаре + m_eScopeStatus = (ALife::EWeaponAddonStatus)pSettings->r_s32(section,"scope_status"); + m_eSilencerStatus = (ALife::EWeaponAddonStatus)pSettings->r_s32(section,"silencer_status"); + m_eGrenadeLauncherStatus = (ALife::EWeaponAddonStatus)pSettings->r_s32(section,"grenade_launcher_status"); + + m_zoom_params.m_bZoomEnabled = !!pSettings->r_bool(section,"zoom_enabled"); + m_zoom_params.m_fZoomRotateTime = pSettings->r_float(section,"zoom_rotate_time"); + m_zoom_params.m_fIronSightZoomFactor = READ_IF_EXISTS(pSettings, r_float, section, "ironsight_zoom_factor", 50.0f); + + m_bScopeForceIcon = !!READ_IF_EXISTS(pSettings, r_bool, section, "scope_force_icon", FALSE); + m_bSilencerForceIcon = !!READ_IF_EXISTS(pSettings, r_bool, section, "silencer_force_icon", FALSE); + m_bGrenadeLauncherForceIcon = !!READ_IF_EXISTS(pSettings, r_bool, section, "grenade_launcher_force_icon", FALSE); + + if (pSettings->line_exist(section, "scopes_sect")) + { + LPCSTR str = pSettings->r_string(section, "scopes_sect"); + for(int i = 0, count = _GetItemCount(str); i < count; ++i ) + { + string128 scope_section; + _GetItem (str, i, scope_section); + m_scopes.push_back (scope_section); + } + } + else if (pSettings->line_exist(section, "scope_name")) + { + m_scopes.push_back(section); + } + + if (m_eScopeStatus == ALife::eAddonPermanent) + { + shared_str scope_tex_name = pSettings->r_string(section, "scope_texture"); + m_zoom_params.m_fScopeZoomFactor = pSettings->r_float(section, "scope_zoom_factor"); + + m_zoom_params.m_bUseDynamicZoom = READ_IF_EXISTS(pSettings, r_bool, section, "scope_dynamic_zoom", FALSE); + + m_zoom_params.m_sUseZoomPostprocess = READ_IF_EXISTS(pSettings, r_string, section, "scope_nightvision", 0); + m_zoom_params.m_sUseBinocularVision = READ_IF_EXISTS(pSettings, r_string, section, "scope_alive_detector", 0); + + m_UIScope = new CUIWindow(); + if (!pWpnScopeXml) + { + pWpnScopeXml = new CUIXml(); + pWpnScopeXml->Load(CONFIG_PATH, UI_PATH, "scopes.xml"); + } + + CUIXmlInit::InitWindow(*pWpnScopeXml, scope_tex_name.c_str(), 0, m_UIScope); + } + + m_sSilencerName = READ_IF_EXISTS(pSettings, r_string, section, "silencer_name", ""); + m_iSilencerX = READ_IF_EXISTS(pSettings, r_s32, section, "silencer_x", 0); + m_iSilencerY = READ_IF_EXISTS(pSettings, r_s32, section, "silencer_y", 0); + if (m_eSilencerStatus == ALife::eAddonAttachable || m_bSilencerForceIcon) + { + R_ASSERT(m_sSilencerName.size() > 0); + } + + m_sGrenadeLauncherName = READ_IF_EXISTS(pSettings, r_string, section, "grenade_launcher_name", ""); + m_iGrenadeLauncherX = READ_IF_EXISTS(pSettings, r_s32, section,"grenade_launcher_x", 0); + m_iGrenadeLauncherY = READ_IF_EXISTS(pSettings, r_s32, section,"grenade_launcher_y", 0); + if (m_eGrenadeLauncherStatus == ALife::eAddonAttachable || m_bGrenadeLauncherForceIcon) + { + R_ASSERT(m_sGrenadeLauncherName.size() > 0); + } + + InitAddons(); + if(pSettings->line_exist(section,"weapon_remove_time")) + m_dwWeaponRemoveTime = pSettings->r_u32(section,"weapon_remove_time"); + else + m_dwWeaponRemoveTime = WEAPON_REMOVE_TIME; + ////////////////////////////////////// + if(pSettings->line_exist(section,"auto_spawn_ammo")) + m_bAutoSpawnAmmo = pSettings->r_bool(section,"auto_spawn_ammo"); + else + m_bAutoSpawnAmmo = TRUE; + ////////////////////////////////////// + + + m_zoom_params.m_bHideCrosshairInZoom = true; + + if(pSettings->line_exist(hud_sect, "zoom_hide_crosshair")) + m_zoom_params.m_bHideCrosshairInZoom = !!pSettings->r_bool(hud_sect, "zoom_hide_crosshair"); + + Fvector def_dof; + def_dof.set (-1,-1,-1); +// m_zoom_params.m_ZoomDof = READ_IF_EXISTS(pSettings, r_fvector3, section, "zoom_dof", Fvector().set(-1,-1,-1)); +// m_zoom_params.m_bZoomDofEnabled = !def_dof.similar(m_zoom_params.m_ZoomDof); + + m_bHasTracers = !!READ_IF_EXISTS(pSettings, r_bool, section, "tracers", true); + m_u8TracerColorID = READ_IF_EXISTS(pSettings, r_u8, section, "tracers_color_ID", u8(-1)); + + string256 temp; + for (int i=egdNovice; ir_float( section,"cam_dispersion" ) ); + cam_recoil.camDispersionInc = 0.0f; + + if ( pSettings->line_exist( section, "cam_dispersion_inc" ) ) { + cam_recoil.camDispersionInc = deg2rad( pSettings->r_float( section, "cam_dispersion_inc" ) ); + } + + zoom_cam_recoil.camDispersion = cam_recoil.camDispersion; + zoom_cam_recoil.camDispersionInc = cam_recoil.camDispersionInc; + + if ( pSettings->line_exist( section, "zoom_cam_dispersion" ) ) { + zoom_cam_recoil.camDispersion = deg2rad( pSettings->r_float( section, "zoom_cam_dispersion" ) ); + } + if ( pSettings->line_exist( section, "zoom_cam_dispersion_inc" ) ) { + zoom_cam_recoil.camDispersionInc = deg2rad( pSettings->r_float( section, "zoom_cam_dispersion_inc" ) ); + } + + CShootingObject::LoadFireParams(section, prefix); +}; + +BOOL CWeapon::net_Spawn (CSE_Abstract* DC) +{ +// if (pSettings->line_exist(cNameSect(), "scope_zoom_factor")) { +// m_zoom_params.m_fScopeZoomFactor = pSettings->r_float(cNameSect(), "scope_zoom_factor"); +// } +// else { m_zoom_params.m_fScopeZoomFactor = 0; } + + BOOL bResult = inherited::net_Spawn(DC); + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeItemWeapon *E = smart_cast(e); + + //iAmmoCurrent = E->a_current; + iAmmoElapsed = E->a_elapsed; + m_flagsAddOnState = E->m_addon_flags.get(); + m_cur_scope = E->m_cur_scope; + m_ammoType = E->ammo_type; + SetState (E->wpn_state); + SetNextState (E->wpn_state); + + R_ASSERT2(m_cur_scope <= m_scopes.size(), make_string("m_scopes.size = %u, m_cur_scope = %u", m_scopes.size(), m_cur_scope)); + + if (m_ammoType >= m_ammoTypes.size()) + { + Msg("!ammo index [%d] is out of ammo types, should be less than %d. Item section is [%s]. Probably default ammo upgrade is the reason", m_ammoType, m_ammoTypes.size(), cNameSect_str()); + m_ammoType = 0; + } + + m_DefaultCartridge.Load(*m_ammoTypes[m_ammoType], u8(m_ammoType)); + if(iAmmoElapsed) + { + m_fCurrentCartirdgeDisp = m_DefaultCartridge.param_s.kDisp; + for(int i = 0; i < iAmmoElapsed; ++i) + m_magazine.push_back(m_DefaultCartridge); + } + + UpdateAddonsVisibility(); + InitAddons(); + + m_dwWeaponIndependencyTime = 0; + + VERIFY((u32)iAmmoElapsed == m_magazine.size()); + m_bAmmoWasSpawned = false; + + return bResult; +} + +void CWeapon::net_Destroy () +{ + inherited::net_Destroy (); + + //удалить объекты партиклов + StopFlameParticles (); + StopFlameParticles2 (); + StopLight (); + Light_Destroy (); + + while (m_magazine.size()) m_magazine.pop_back(); +} + +BOOL CWeapon::IsUpdating() +{ + bool bIsActiveItem = m_pCurrentInventory && m_pCurrentInventory->ActiveItem()==this; + return bIsActiveItem || bWorking || IsPending() || getVisible(); +} + +void CWeapon::net_Export(NET_Packet& P) +{ + inherited::net_Export(P); + + u8 need_upd = IsUpdating() ? 1 : 0; + P.w_u8 (need_upd); + P.w_u16 (u16(iAmmoElapsed)); + P.w_u8 (m_cur_scope); + P.w_u8 (m_flagsAddOnState); + P.w_u8 ((u8)m_ammoType); + P.w_u8 ((u8)GetState()); + P.w_u8 ((u8)IsZoomed()); +} + +void CWeapon::net_Import(NET_Packet& P) +{ + inherited::net_Import(P); + + u8 flags = 0; + P.r_u8 (flags); + + u16 ammo_elapsed = 0; + P.r_u16 (ammo_elapsed); + + P.r_u8 (m_cur_scope); + + u8 NewAddonState; + P.r_u8 (NewAddonState); + + m_flagsAddOnState = NewAddonState; + UpdateAddonsVisibility (); + + u8 ammoType, wstate; + P.r_u8 (ammoType); + P.r_u8 (wstate); + + u8 Zoom; + P.r_u8 ((u8)Zoom); + + if (H_Parent() && H_Parent()->Remote()) + { + if (Zoom) OnZoomIn(); + else OnZoomOut(); + }; + switch (wstate) + { + case eFire: + case eFire2: + case eSwitch: + case eReload: + { + }break; + default: + { + if (ammoType >= m_ammoTypes.size()) + Msg("!! Weapon [%d], State - [%d]", ID(), wstate); + else + { + m_ammoType = ammoType; + SetAmmoElapsed((ammo_elapsed)); + } + }break; + } + + VERIFY((u32)iAmmoElapsed == m_magazine.size()); +} + +void CWeapon::net_Relcase(CObject* object) +{ + inherited::net_Relcase(object); + if (m_zoom_params.m_pVision) + m_zoom_params.m_pVision->remove_links(object); +} + +void CWeapon::save(NET_Packet &output_packet) +{ + inherited::save (output_packet); + + save_data (m_zoom_params.m_bIsZoomModeNow,output_packet); + save_data (m_bRememberActorNVisnStatus, output_packet); + save_data (m_fRTZoomFactor, output_packet); +} + +void CWeapon::load(IReader &input_packet) +{ + inherited::load (input_packet); + + load_data (m_zoom_params.m_bIsZoomModeNow,input_packet); + + if (m_zoom_params.m_bIsZoomModeNow) + OnZoomIn(); + else + OnZoomOut(); + + load_data (m_bRememberActorNVisnStatus, input_packet); + load_data (m_fRTZoomFactor, input_packet); + + UpdateAddonsVisibility(); +} + + +void CWeapon::OnEvent(NET_Packet& P, u16 type) +{ + switch (type) + { + case GE_ADDON_CHANGE: + { + P.r_u8 (m_flagsAddOnState); + InitAddons(); + UpdateAddonsVisibility(); + }break; + + case GE_WPN_STATE_CHANGE: + { + u8 state; + P.r_u8 (state); + P.r_u8 (m_sub_state); +// u8 NewAmmoType = + P.r_u8(); + u8 AmmoElapsed = P.r_u8(); + u8 NextAmmo = P.r_u8(); + if (NextAmmo == u8(-1)) + m_set_next_ammoType_on_reload = u8(-1); + else + m_set_next_ammoType_on_reload = u8(NextAmmo); + + if (OnClient()) SetAmmoElapsed(int(AmmoElapsed)); + OnStateSwitch (u32(state)); + } + break; + default: + { + inherited::OnEvent(P,type); + }break; + } +}; + +void CWeapon::shedule_Update (u32 dT) +{ + // Queue shrink +// u32 dwTimeCL = Level().timeServer()-NET_Latency; +// while ((NET.size()>2) && (NET[1].dwTimeStamp(H_Parent()); + if(pActor && this==pActor->inventory().ActiveItem()) + { + CEntity::SEntityState st; + pActor->g_State(st); + + if (!st.bSprint && + hud_adj_mode==0 && + GetState()==eIdle && + (Device.dwTimeGlobal-m_dw_curr_substate_time>20000) && + !IsZoomed()&& + g_player_hud->attached_item(1)==NULL) + { + if(AllowBore()) + SwitchState (eBore); + + ResetSubStateTime (); + } + } + } + + if(m_zoom_params.m_pNight_vision && !need_renderable()) + { + if(!m_zoom_params.m_pNight_vision->IsActive()) + { + CActor *pA = smart_cast(H_Parent()); + R_ASSERT(pA); + CTorch* pTorch = smart_cast(pA->inventory().ItemFromSlot(TORCH_SLOT)); + if ( pTorch && pTorch->GetNightVisionStatus() ) + { + m_bRememberActorNVisnStatus = pTorch->GetNightVisionStatus(); + pTorch->SwitchNightVision(false, false); + } + m_zoom_params.m_pNight_vision->Start(m_zoom_params.m_sUseZoomPostprocess, pA, false); + } + + } + else if(m_bRememberActorNVisnStatus) + { + m_bRememberActorNVisnStatus = false; + EnableActorNVisnAfterZoom(); + } + + if(m_zoom_params.m_pVision) + m_zoom_params.m_pVision->Update(); +} +void CWeapon::EnableActorNVisnAfterZoom() +{ + CActor *pA = smart_cast(H_Parent()); + if(IsGameTypeSingle() && !pA) + pA = g_actor; + /* + if(pA) + { + CTorch* pTorch = smart_cast( pA->inventory().ItemFromSlot(TORCH_SLOT) ); + if ( pTorch ) + { + pTorch->SwitchNightVision(true, false); + pTorch->GetNightVision()->PlaySounds(CNightVisionEffector::eIdleSound); + } + } + */ +} + +bool CWeapon::need_renderable() +{ + return !( IsZoomed() && ZoomTexture() && !IsRotatingToZoom() ); +} +void CWeapon::renderable_Render () +{ + UpdateXForm (); + + //нарисовать подсветку + + RenderLight (); + + //если мы в режиме снайперки, то сам HUD рисовать не надо + if(IsZoomed() && !IsRotatingToZoom() && ZoomTexture() && !hud_adj_mode) + RenderHud (FALSE); + else + RenderHud (TRUE); + + inherited::renderable_Render (); +} + +void CWeapon::signal_HideComplete() +{ + if(H_Parent()) + setVisible(FALSE); + + SetPending (FALSE); +} + +void CWeapon::SetDefaults() +{ + bWorking2 = false; + SetPending (FALSE); + + m_flags.set (FUsingCondition, TRUE); + bMisfire = false; + m_flagsAddOnState = 0; + m_zoom_params.m_bIsZoomModeNow = false; +} + +void CWeapon::UpdatePosition(const Fmatrix& trans) +{ + Position().set (trans.c); + if (m_strapped_mode || m_strapped_mode_rifle) + XFORM().mul (trans,m_StrapOffset); + else + XFORM().mul (trans,m_Offset); + VERIFY (!fis_zero(DET(renderable.xform))); +} + + +bool CWeapon::Action(u16 cmd, u32 flags) +{ + if(inherited::Action(cmd, flags)) return true; + + + switch(cmd) + { + case kWPN_FIRE: + { + //если оружие чем-то занято, то ничего не делать + { + if(IsPending()) + return false; + + if(flags&CMD_START) + FireStart (); + else + FireEnd (); + }; + + } + return true; + case kWPN_NEXT: + { + if(IsPending() || OnClient()) + { + return false; + } + + if(flags&CMD_START) + { + u32 l_newType = m_ammoType; + bool b1,b2; + do + { + l_newType = (l_newType+1)%m_ammoTypes.size(); + b1 = l_newType != m_ammoType; + b2 = unlimited_ammo() ? false : (!m_pCurrentInventory->GetAny(*m_ammoTypes[l_newType])); + } while( b1 && b2); + + if(l_newType != m_ammoType) + { + m_set_next_ammoType_on_reload = l_newType; +/* m_ammoType = l_newType; + m_pAmmo = NULL; + if (unlimited_ammo()) + { + m_DefaultCartridge.Load(*m_ammoTypes[m_ammoType], u8(m_ammoType)); + }; +*/ + if(OnServer()) Reload(); + } + } + } + return true; + + case kWPN_ZOOM: + if(IsZoomEnabled()) + { + if(flags&CMD_START && !IsPending()) + OnZoomIn(); + else if(IsZoomed()) + OnZoomOut(); + return true; + }else + return false; + + case kWPN_ZOOM_INC: + case kWPN_ZOOM_DEC: + if(IsZoomEnabled() && IsZoomed()) + { + if(cmd==kWPN_ZOOM_INC) + ZoomInc(); + else + ZoomDec(); + return true; + }else + return false; + } + return false; +} + +void CWeapon::SpawnAmmo(u32 boxCurr, LPCSTR ammoSect, u32 ParentID) +{ + if(!m_ammoTypes.size()) return; + if (OnClient()) return; + m_bAmmoWasSpawned = true; + + int l_type = 0; + l_type %= m_ammoTypes.size(); + + if(!ammoSect) ammoSect = *m_ammoTypes[l_type]; + + ++l_type; + l_type %= m_ammoTypes.size(); + + CSE_Abstract *D = F_entity_Create(ammoSect); + + if (D->m_tClassID==CLSID_OBJECT_AMMO || + D->m_tClassID==CLSID_OBJECT_A_M209 || + D->m_tClassID==CLSID_OBJECT_A_VOG25 || + D->m_tClassID==CLSID_OBJECT_A_OG7B) + { + CSE_ALifeItemAmmo *l_pA = smart_cast(D); + R_ASSERT (l_pA); + l_pA->m_boxSize = (u16)pSettings->r_s32(ammoSect, "box_size"); + D->s_name = ammoSect; + D->set_name_replace (""); + D->s_gameid = u8(GameID()); + D->s_RP = 0xff; + D->ID = 0xffff; + if (ParentID == 0xffffffff) + D->ID_Parent = (u16)H_Parent()->ID(); + else + D->ID_Parent = (u16)ParentID; + + D->ID_Phantom = 0xffff; + D->s_flags.assign (M_SPAWN_OBJECT_LOCAL); + D->RespawnTime = 0; + l_pA->m_tNodeID = g_dedicated_server ? u32(-1) : ai_location().level_vertex_id(); + + if(boxCurr == 0xffffffff) + boxCurr = l_pA->m_boxSize; + + while(boxCurr) + { + l_pA->a_elapsed = (u16)(boxCurr > l_pA->m_boxSize ? l_pA->m_boxSize : boxCurr); + NET_Packet P; + D->Spawn_Write (P, TRUE); + Level().Send (P,net_flags(TRUE)); + + if(boxCurr > l_pA->m_boxSize) + boxCurr -= l_pA->m_boxSize; + else + boxCurr = 0; + } + }; + F_entity_Destroy (D); +} + +int CWeapon::GetAmmoCurrent(bool use_item_to_spawn) const +{ + int l_count = iAmmoElapsed; + if(!m_pCurrentInventory) return l_count; + + //чтоб не делать лишних пересчетов + if(m_pCurrentInventory->ModifyFrame()<=m_dwAmmoCurrentCalcFrame) + return l_count + iAmmoCurrent; + + m_dwAmmoCurrentCalcFrame = Device.dwFrame; + iAmmoCurrent = 0; + + for ( u8 i = 0; i < u8(m_ammoTypes.size()); ++i ) + { + LPCSTR l_ammoType = *m_ammoTypes[i]; + + for(TIItemContainer::iterator l_it = m_pCurrentInventory->m_belt.begin(); m_pCurrentInventory->m_belt.end() != l_it; ++l_it) + { + CWeaponAmmo *l_pAmmo = smart_cast(*l_it); + + if(l_pAmmo && !xr_strcmp(l_pAmmo->cNameSect(), l_ammoType)) + { + iAmmoCurrent = iAmmoCurrent + l_pAmmo->m_boxCurr; + } + } + + if (g_actor->m_inventory != m_pCurrentInventory) + { + for(TIItemContainer::iterator l_it = m_pCurrentInventory->m_ruck.begin(); m_pCurrentInventory->m_ruck.end() != l_it; ++l_it) + { + CWeaponAmmo *l_pAmmo = smart_cast(*l_it); + if(l_pAmmo && !xr_strcmp(l_pAmmo->cNameSect(), l_ammoType)) + { + iAmmoCurrent = iAmmoCurrent + l_pAmmo->m_boxCurr; + } + } + } + + if (!use_item_to_spawn) + continue; + + if (!inventory_owner().item_to_spawn()) + continue; + + iAmmoCurrent += inventory_owner().ammo_in_box_to_spawn(); + } + return l_count + iAmmoCurrent; +} + +float CWeapon::GetConditionMisfireProbability() const +{ +// modified by Peacemaker [17.10.08] +// if(GetCondition() > 0.95f) +// return 0.0f; + if(GetCondition() > misfireStartCondition) + return 0.0f; + if(GetCondition() < misfireEndCondition) + return misfireEndProbability; +// float mis = misfireProbability+powf(1.f-GetCondition(), 3.f)*misfireConditionK; + float mis = misfireStartProbability + ( + (misfireStartCondition - GetCondition()) * // condition goes from 1.f to 0.f + (misfireEndProbability - misfireStartProbability) / // probability goes from 0.f to 1.f + ((misfireStartCondition == misfireEndCondition) ? // !!!say "No" to devision by zero + misfireStartCondition : + (misfireStartCondition - misfireEndCondition)) + ); + clamp(mis,0.0f,0.99f); + return mis; +} + +BOOL CWeapon::CheckForMisfire () +{ + if (OnClient()) return FALSE; + + float rnd = ::Random.randF(0.f,1.f); + float mp = GetConditionMisfireProbability(); + if(rnd < mp) + { + FireEnd(); + + bMisfire = true; + SwitchState(eMisfire); + + return TRUE; + } + else + { + return FALSE; + } +} + +BOOL CWeapon::IsMisfire() const +{ + return bMisfire; +} +void CWeapon::Reload() +{ + OnZoomOut(); + + if (m_pCurrentInventory == Actor()->m_inventory) + { + Actor()->BreakSprint(); + Actor()->trySprintCounter_ = 0; + } +} + + +bool CWeapon::IsGrenadeLauncherAttached() const +{ + return (CSE_ALifeItemWeapon::eAddonAttachable == m_eGrenadeLauncherStatus && + 0 != (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher)) || + CSE_ALifeItemWeapon::eAddonPermanent == m_eGrenadeLauncherStatus; +} + +bool CWeapon::IsScopeAttached() const +{ + return (CSE_ALifeItemWeapon::eAddonAttachable == m_eScopeStatus && + 0 != (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonScope)) || + CSE_ALifeItemWeapon::eAddonPermanent == m_eScopeStatus; + +} + +bool CWeapon::IsSilencerAttached() const +{ + return (CSE_ALifeItemWeapon::eAddonAttachable == m_eSilencerStatus && + 0 != (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonSilencer)) || + CSE_ALifeItemWeapon::eAddonPermanent == m_eSilencerStatus; +} + +bool CWeapon::GrenadeLauncherAttachable() +{ + return (CSE_ALifeItemWeapon::eAddonAttachable == m_eGrenadeLauncherStatus); +} +bool CWeapon::ScopeAttachable() +{ + return (CSE_ALifeItemWeapon::eAddonAttachable == m_eScopeStatus); +} +bool CWeapon::SilencerAttachable() +{ + return (CSE_ALifeItemWeapon::eAddonAttachable == m_eSilencerStatus); +} + +shared_str wpn_scope = "wpn_scope"; +shared_str wpn_silencer = "wpn_silencer"; +shared_str wpn_grenade_launcher = "wpn_launcher"; + + +void CWeapon::UpdateHUDAddonsVisibility() +{//actor only + if(!GetHUDmode()) return; + if(ScopeAttachable()) + { + HudItemData()->set_bone_visible(wpn_scope, IsScopeAttached() ); + } + + if(m_eScopeStatus==ALife::eAddonDisabled ) + { + HudItemData()->set_bone_visible(wpn_scope, FALSE, TRUE ); + }else + if(m_eScopeStatus==ALife::eAddonPermanent) + HudItemData()->set_bone_visible(wpn_scope, TRUE, TRUE ); + + if(SilencerAttachable()) + { + HudItemData()->set_bone_visible(wpn_silencer, IsSilencerAttached()); + } + if(m_eSilencerStatus==ALife::eAddonDisabled ) + { + HudItemData()->set_bone_visible(wpn_silencer, FALSE, TRUE); + } + else + if(m_eSilencerStatus==ALife::eAddonPermanent) + HudItemData()->set_bone_visible(wpn_silencer, TRUE, TRUE); + + if(GrenadeLauncherAttachable()) + { + HudItemData()->set_bone_visible(wpn_grenade_launcher, IsGrenadeLauncherAttached()); + } + if(m_eGrenadeLauncherStatus==ALife::eAddonDisabled ) + { + HudItemData()->set_bone_visible(wpn_grenade_launcher, FALSE, TRUE); + }else + if(m_eGrenadeLauncherStatus==ALife::eAddonPermanent) + HudItemData()->set_bone_visible(wpn_grenade_launcher, TRUE, TRUE); + +} + +void CWeapon::UpdateAddonsVisibility() +{ + IKinematics* pWeaponVisual = smart_cast(Visual()); R_ASSERT(pWeaponVisual); + + u16 bone_id; + UpdateHUDAddonsVisibility (); + + pWeaponVisual->CalculateBones_Invalidate (); + + bone_id = pWeaponVisual->LL_BoneID (wpn_scope); + if(ScopeAttachable()) + { + if(IsScopeAttached()) + { + if(!pWeaponVisual->LL_GetBoneVisible (bone_id)) + pWeaponVisual->LL_SetBoneVisible (bone_id,TRUE,TRUE); + }else{ + if(pWeaponVisual->LL_GetBoneVisible (bone_id)) + pWeaponVisual->LL_SetBoneVisible (bone_id,FALSE,TRUE); + } + } + if(m_eScopeStatus==CSE_ALifeItemWeapon::eAddonDisabled && bone_id!=BI_NONE && + pWeaponVisual->LL_GetBoneVisible(bone_id) ) + { + pWeaponVisual->LL_SetBoneVisible (bone_id,FALSE,TRUE); +// Log("scope", pWeaponVisual->LL_GetBoneVisible (bone_id)); + } + bone_id = pWeaponVisual->LL_BoneID (wpn_silencer); + if(SilencerAttachable()) + { + if(IsSilencerAttached()){ + if(!pWeaponVisual->LL_GetBoneVisible (bone_id)) + pWeaponVisual->LL_SetBoneVisible (bone_id,TRUE,TRUE); + }else{ + if( pWeaponVisual->LL_GetBoneVisible (bone_id)) + pWeaponVisual->LL_SetBoneVisible (bone_id,FALSE,TRUE); + } + } + if(m_eSilencerStatus==CSE_ALifeItemWeapon::eAddonDisabled && bone_id!=BI_NONE && + pWeaponVisual->LL_GetBoneVisible(bone_id) ) + { + pWeaponVisual->LL_SetBoneVisible (bone_id,FALSE,TRUE); +// Log("silencer", pWeaponVisual->LL_GetBoneVisible (bone_id)); + } + + bone_id = pWeaponVisual->LL_BoneID (wpn_grenade_launcher); + if(GrenadeLauncherAttachable()) + { + if(IsGrenadeLauncherAttached()) + { + if(!pWeaponVisual->LL_GetBoneVisible (bone_id)) + pWeaponVisual->LL_SetBoneVisible (bone_id,TRUE,TRUE); + }else{ + if(pWeaponVisual->LL_GetBoneVisible (bone_id)) + pWeaponVisual->LL_SetBoneVisible (bone_id,FALSE,TRUE); + } + } + if(m_eGrenadeLauncherStatus==CSE_ALifeItemWeapon::eAddonDisabled && bone_id!=BI_NONE && + pWeaponVisual->LL_GetBoneVisible(bone_id) ) + { + pWeaponVisual->LL_SetBoneVisible (bone_id,FALSE,TRUE); + } + + + pWeaponVisual->CalculateBones_Invalidate (); + pWeaponVisual->CalculateBones (TRUE); +} + +void CWeapon::InitAddons() +{ +} + +float CWeapon::CurrentZoomFactor() +{ + return IsScopeAttached() ? m_zoom_params.m_fScopeZoomFactor : m_zoom_params.m_fIronSightZoomFactor; +}; +void GetZoomData(const float scope_factor, float& delta, float& min_zoom_factor); +void CWeapon::OnZoomIn() +{ + m_zoom_params.m_bIsZoomModeNow = true; + + if (IsScopeAttached() && m_zoom_params.m_bUseDynamicZoom) + { + if (m_fRTZoomFactor == 0.f) + {// For a removable scope, if m_fRTZoomFactor is not set, take factor from m_zoom_params.m_fScopeZoomFactor + m_fRTZoomFactor = m_zoom_params.m_fScopeZoomFactor; + } + + SetZoomFactor(m_fRTZoomFactor); + } + else + m_zoom_params.m_fCurrentZoomFactor = CurrentZoomFactor(); + + EnableHudInertion (FALSE); + + //if(m_zoom_params.m_bZoomDofEnabled && !IsScopeAttached()) + // GamePersistent().SetEffectorDOF (m_zoom_params.m_ZoomDof); + + if(GetHUDmode()) + GamePersistent().SetPickableEffectorDOF(true); + + if (m_zoom_params.m_sUseBinocularVision.size() && IsScopeAttached() && m_zoom_params.m_pVision == nullptr) + { + m_zoom_params.m_pVision = new CBinocularsVision(m_zoom_params.m_sUseBinocularVision); + } + + if(m_zoom_params.m_sUseZoomPostprocess.size() && IsScopeAttached()) + { + CActor *pA = smart_cast(H_Parent()); + if(pA) + { + if(NULL==m_zoom_params.m_pNight_vision) + { + m_zoom_params.m_pNight_vision = new CNightVisionEffector(m_zoom_params.m_sUseZoomPostprocess/*"device_torch"*/); + } + } + } +} + +void CWeapon::OnZoomOut() +{ + m_zoom_params.m_bIsZoomModeNow = false; + m_fRTZoomFactor = GetZoomFactor();//store current + m_zoom_params.m_fCurrentZoomFactor = g_fov; + EnableHudInertion (TRUE); + +// GamePersistent().RestoreEffectorDOF (); + + if(GetHUDmode()) + GamePersistent().SetPickableEffectorDOF(false); + + //ResetSubStateTime (); + + + xr_delete (m_zoom_params.m_pVision); + if(m_zoom_params.m_pNight_vision) + { + m_zoom_params.m_pNight_vision->Stop(100000.0f, false); + xr_delete(m_zoom_params.m_pNight_vision); + } +} + +CUIWindow* CWeapon::ZoomTexture() +{ + if (UseScopeTexture() && !hud_adj_mode) + return m_UIScope; + else + return NULL; +} + +void CWeapon::SwitchState(u32 S) +{ + if (OnClient()) return; + +#ifndef MASTER_GOLD + if ( bDebug ) + { + Msg("---Server is going to send GE_WPN_STATE_CHANGE to [%d], weapon_section[%s], parent[%s]", + S, cNameSect().c_str(), H_Parent() ? H_Parent()->cName().c_str() : "NULL Parent"); + } +#endif // #ifndef MASTER_GOLD + SetNextState ( S ); // Very-very important line of code!!! :) + if (CHudItem::object().Local() && !CHudItem::object().getDestroy()/* && (S!=NEXT_STATE)*/ + && m_pCurrentInventory && OnServer()) + { + // !!! Just single entry for given state !!! + NET_Packet P; + CHudItem::object().u_EventGen (P,GE_WPN_STATE_CHANGE,CHudItem::object().ID()); + P.w_u8 (u8(S)); + P.w_u8 (u8(m_sub_state)); + P.w_u8 (m_ammoType); + P.w_u8 (u8(iAmmoElapsed & 0xff)); + P.w_u8 (m_set_next_ammoType_on_reload); + CHudItem::object().u_EventSend (P, net_flags(TRUE, TRUE, FALSE, TRUE)); + } +} + +void CWeapon::OnMagazineEmpty () +{ + VERIFY((u32)iAmmoElapsed == m_magazine.size()); +} + + +void CWeapon::reinit () +{ + CShootingObject::reinit (); + CHudItemObject::reinit (); +} + +void CWeapon::reload (LPCSTR section) +{ + CShootingObject::reload (section); + CHudItemObject::reload (section); + + m_can_be_strapped = true; + m_can_be_strapped_rifle = (BaseSlot() == RIFLE_SLOT || BaseSlot() == RIFLE_2_SLOT); + m_strapped_mode = false; + m_strapped_mode_rifle = false; + + if (pSettings->line_exist(section,"strap_bone0")) + { + m_strap_bone0 = pSettings->r_string(section,"strap_bone0"); + } else { + m_can_be_strapped = false; + m_can_be_strapped_rifle = false; + } + + if (pSettings->line_exist(section,"strap_bone1")) + { + m_strap_bone1 = pSettings->r_string(section,"strap_bone1"); + } else { + m_can_be_strapped = false; + m_can_be_strapped_rifle = false; + } + + if (m_eScopeStatus == ALife::eAddonAttachable) { + m_addon_holder_range_modifier = READ_IF_EXISTS(pSettings, r_float, GetScopeName(), "holder_range_modifier", m_holder_range_modifier); + m_addon_holder_fov_modifier = READ_IF_EXISTS(pSettings, r_float, GetScopeName(), "holder_fov_modifier", m_holder_fov_modifier); + } + else { + m_addon_holder_range_modifier = m_holder_range_modifier; + m_addon_holder_fov_modifier = m_holder_fov_modifier; + } + + + { + Fvector pos,ypr; + pos = pSettings->r_fvector3 (section,"position"); + ypr = pSettings->r_fvector3 (section,"orientation"); + ypr.mul (PI/180.f); + + m_Offset.setHPB (ypr.x,ypr.y,ypr.z); + m_Offset.translate_over (pos); + } + + m_StrapOffset = m_Offset; + if (pSettings->line_exist(section,"strap_position") && pSettings->line_exist(section,"strap_orientation")) { + Fvector pos,ypr; + pos = pSettings->r_fvector3 (section,"strap_position"); + ypr = pSettings->r_fvector3 (section,"strap_orientation"); + ypr.mul (PI/180.f); + + m_StrapOffset.setHPB (ypr.x,ypr.y,ypr.z); + m_StrapOffset.translate_over (pos); + } else { + m_can_be_strapped = false; + m_can_be_strapped_rifle = false; + } + + m_ef_main_weapon_type = READ_IF_EXISTS(pSettings,r_u32,section,"ef_main_weapon_type",u32(-1)); + m_ef_weapon_type = READ_IF_EXISTS(pSettings,r_u32,section,"ef_weapon_type",u32(-1)); +} + +void CWeapon::create_physic_shell() +{ + CPhysicsShellHolder::create_physic_shell(); +} + +void CWeapon::activate_physic_shell() +{ + UpdateXForm(); + CPhysicsShellHolder::activate_physic_shell(); +} + +void CWeapon::setup_physic_shell() +{ + CPhysicsShellHolder::setup_physic_shell(); +} + +int g_iWeaponRemove = 1; + +bool CWeapon::NeedToDestroyObject() const +{ + if (GameID() == GAME_SINGLE) return false; + if (Remote()) return false; + if (H_Parent()) return false; + if (g_iWeaponRemove == -1) return false; + if (g_iWeaponRemove == 0) return true; + if (TimePassedAfterIndependant() > m_dwWeaponRemoveTime) + return true; + + return false; +} + +ALife::_TIME_ID CWeapon::TimePassedAfterIndependant() const +{ + if(!H_Parent() && m_dwWeaponIndependencyTime != 0) + return Level().timeServer() - m_dwWeaponIndependencyTime; + else + return 0; +} + +bool CWeapon::can_kill () const +{ + if (GetAmmoCurrent(true) || m_ammoTypes.empty()) + return (true); + + return (false); +} + +CInventoryItem *CWeapon::can_kill (CInventory *inventory) const +{ + if (GetAmmoElapsed() || m_ammoTypes.empty()) + return (const_cast(this)); + + TIItemContainer::iterator I = inventory->m_all.begin(); + TIItemContainer::iterator E = inventory->m_all.end(); + for ( ; I != E; ++I) { + CInventoryItem *inventory_item = smart_cast(*I); + if (!inventory_item) + continue; + + xr_vector::const_iterator i = std::find(m_ammoTypes.begin(),m_ammoTypes.end(),inventory_item->object().cNameSect()); + if (i != m_ammoTypes.end()) + return (inventory_item); + } + + return (0); +} + +const CInventoryItem *CWeapon::can_kill (const xr_vector &items) const +{ + if (m_ammoTypes.empty()) + return (this); + + xr_vector::const_iterator I = items.begin(); + xr_vector::const_iterator E = items.end(); + for ( ; I != E; ++I) { + const CInventoryItem *inventory_item = smart_cast(*I); + if (!inventory_item) + continue; + + xr_vector::const_iterator i = std::find(m_ammoTypes.begin(),m_ammoTypes.end(),inventory_item->object().cNameSect()); + if (i != m_ammoTypes.end()) + return (inventory_item); + } + + return (0); +} + +bool CWeapon::ready_to_kill () const +{ + return ( + !IsMisfire() && + ((GetState() == eIdle) || (GetState() == eFire) || (GetState() == eFire2)) && + GetAmmoElapsed() + ); +} + + +void CWeapon::UpdateHudAdditonal (Fmatrix& trans) +{ + CActor* pActor = smart_cast(H_Parent()); + if(!pActor) return; + + if( (IsZoomed() && m_zoom_params.m_fZoomRotationFactor<=1.f) || + (!IsZoomed() && m_zoom_params.m_fZoomRotationFactor>0.f)) + { + u8 idx = GetCurrentHudOffsetIdx(); +// if(idx==0) return; + + attachable_hud_item* hi = HudItemData(); + R_ASSERT (hi); + Fvector curr_offs, curr_rot; + curr_offs = hi->m_measures.m_hands_offset[0][idx];//pos,aim + curr_rot = hi->m_measures.m_hands_offset[1][idx];//rot,aim + curr_offs.mul (m_zoom_params.m_fZoomRotationFactor); + curr_rot.mul (m_zoom_params.m_fZoomRotationFactor); + + Fmatrix hud_rotation; + hud_rotation.identity (); + hud_rotation.rotateX (curr_rot.x); + + Fmatrix hud_rotation_y; + hud_rotation_y.identity (); + hud_rotation_y.rotateY (curr_rot.y); + hud_rotation.mulA_43 (hud_rotation_y); + + hud_rotation_y.identity (); + hud_rotation_y.rotateZ (curr_rot.z); + hud_rotation.mulA_43 (hud_rotation_y); + + hud_rotation.translate_over (curr_offs); + trans.mulB_43 (hud_rotation); + + if(pActor->IsZoomAimingMode()) + m_zoom_params.m_fZoomRotationFactor += Device.fTimeDelta/m_zoom_params.m_fZoomRotateTime; + else + m_zoom_params.m_fZoomRotationFactor -= Device.fTimeDelta/m_zoom_params.m_fZoomRotateTime; + + clamp(m_zoom_params.m_fZoomRotationFactor, 0.f, 1.f); + } +} + +void CWeapon::SetAmmoElapsed (int ammo_count) +{ + iAmmoElapsed = ammo_count; + + u32 uAmmo = u32(iAmmoElapsed); + + if (uAmmo != m_magazine.size()) + { + if (uAmmo > m_magazine.size()) + { + CCartridge l_cartridge; + l_cartridge.Load (*m_ammoTypes[m_ammoType], u8(m_ammoType)); + while (uAmmo > m_magazine.size()) + m_magazine.push_back(l_cartridge); + } + else + { + while (uAmmo < m_magazine.size()) + m_magazine.pop_back(); + }; + }; +} + +u32 CWeapon::ef_main_weapon_type () const +{ + VERIFY (m_ef_main_weapon_type != u32(-1)); + return (m_ef_main_weapon_type); +} + +u32 CWeapon::ef_weapon_type () const +{ + VERIFY (m_ef_weapon_type != u32(-1)); + return (m_ef_weapon_type); +} + +bool CWeapon::IsNecessaryItem (const shared_str& item_sect) +{ + return (std::find(m_ammoTypes.begin(), m_ammoTypes.end(), item_sect) != m_ammoTypes.end() ); +} + +void CWeapon::modify_holder_params (float &range, float &fov) const +{ + if (!IsScopeAttached()) { + inherited::modify_holder_params (range,fov); + return; + } + range *= m_addon_holder_range_modifier; + fov *= m_addon_holder_fov_modifier; +} + +bool CWeapon::render_item_ui_query() +{ + bool b_is_active_item = (m_pCurrentInventory->ActiveItem()==this); + bool res = b_is_active_item && IsZoomed() && ZoomHideCrosshair() && ZoomTexture() && !IsRotatingToZoom(); + return res; +} + +void CWeapon::render_item_ui() +{ + if(m_zoom_params.m_pVision) + m_zoom_params.m_pVision->Draw(); + + ZoomTexture()->Update (); + ZoomTexture()->Draw (); +} + +bool CWeapon::unlimited_ammo() +{ + return psActorFlags.test(AF_UNLIMITEDAMMO) && + m_DefaultCartridge.m_flags.test(CCartridge::cfCanBeUnlimited); + +}; + +LPCSTR CWeapon::GetCurrentAmmo_ShortName () +{ + if (m_magazine.empty()) return (""); + CCartridge &l_cartridge = m_magazine.back(); + return *(l_cartridge.m_InvShortName); +} + +float CWeapon::Weight() const +{ + float res = CInventoryItemObject::Weight(); + if(IsGrenadeLauncherAttached()&&GetGrenadeLauncherName().size()){ + res += pSettings->r_float(GetGrenadeLauncherName(),"inv_weight"); + } + if(IsScopeAttached()&&m_scopes.size()){ + res += pSettings->r_float(GetScopeName(),"inv_weight"); + } + if(IsSilencerAttached()&&GetSilencerName().size()){ + res += pSettings->r_float(GetSilencerName(),"inv_weight"); + } + + if(iAmmoElapsed) + { + float w = pSettings->r_float(m_ammoTypes[m_ammoType].c_str(),"inv_weight"); + float bs = pSettings->r_float(m_ammoTypes[m_ammoType].c_str(),"box_size"); + + res += w*(iAmmoElapsed/bs); + } + return res; +} +void CWeapon::Hide () +{ + if(IsGameTypeSingle()) + if (m_bHolster_off == true) { + SwitchState(eHiding); + } + else + SwitchState(eHidden); + + OnZoomOut(); +} + +void CWeapon::Show () +{ + if (m_bDraw_off == true) { + SwitchState(eShowing); + } +} + +bool CWeapon::show_crosshair() +{ + return !IsPending() && ( !IsZoomed() || !ZoomHideCrosshair() ); +} + +bool CWeapon::show_indicators() +{ + return ! ( IsZoomed() && ZoomTexture() ); +} + +float CWeapon::GetConditionToShow () const +{ + return (GetCondition());//powf(GetCondition(),4.0f)); +} + +BOOL CWeapon::ParentMayHaveAimBullet () +{ + CObject* O=H_Parent(); + CEntityAlive* EA=smart_cast(O); + return EA->cast_actor()!=0; +} + +BOOL CWeapon::ParentIsActor () +{ + CObject* O = H_Parent(); + if (!O) + return FALSE; + + CEntityAlive* EA = smart_cast(O); + if (!EA) + return FALSE; + + return EA->cast_actor()!=0; +} + +void CWeapon::debug_draw_firedeps() +{ +#ifdef DEBUG + if(hud_adj_mode==5||hud_adj_mode==6||hud_adj_mode==7) + { + CDebugRenderer &render = Level().debug_renderer(); + + if(hud_adj_mode==5) + render.draw_aabb(get_LastFP(), 0.005f,0.005f,0.005f,D3DCOLOR_XRGB(255,0,0)); + + if(hud_adj_mode==6) + render.draw_aabb(get_LastFP2(), 0.005f,0.005f,0.005f,D3DCOLOR_XRGB(0,0,255)); + + if(hud_adj_mode==7) + render.draw_aabb(get_LastSP(), 0.005f,0.005f,0.005f,D3DCOLOR_XRGB(0,255,0)); + } +#endif // DEBUG +} + +const float &CWeapon::hit_probability () const +{ + VERIFY ((g_SingleGameDifficulty >= egdNovice) && (g_SingleGameDifficulty <= egdMaster)); + return (m_hit_probability[g_SingleGameDifficulty]); +} + +// gr1ph: added zoom in/out + +void CWeapon::GetZoomData(const float scope_factor, float& delta, float& min_zoom_factor) +{ + float def_fov = (float) g_fov; + float min_zoom_k = 0.3f; + float zoom_step_count = 3.0f; + float delta_factor_total = def_fov - scope_factor; + min_zoom_factor = def_fov - delta_factor_total * min_zoom_k; + delta = (delta_factor_total * (1 - min_zoom_k)) / zoom_step_count; +} + +u8 CWeapon::GetCurrentHudOffsetIdx() +{ + CActor* pActor = smart_cast(H_Parent()); + if(!pActor) return 0; + + bool b_aiming = ((IsZoomed() && m_zoom_params.m_fZoomRotationFactor<=1.f) || + (!IsZoomed() && m_zoom_params.m_fZoomRotationFactor>0.f)); + + if(!b_aiming) + return 0; + else + return 1; +} + +void CWeapon::render_hud_mode() +{ + RenderLight(); +} + +bool CWeapon::MovingAnimAllowedNow() +{ + return !IsZoomed(); +} + +bool CWeapon::IsHudModeNow() +{ + return (HudItemData()!=NULL); +} + +void CWeapon::ZoomDec() +{ + //if (m_bCustomZoomEnabled) + //{ + //float delta, min_zoom_factor; + //if (!IsScopeAttached()) + // return; + //GetZoomData(m_fScopeZoomFactor, delta, min_zoom_factor); + //m_fZoomFactor -= delta; + //clamp(m_fZoomFactor, m_fScopeZoomFactor, min_zoom_factor); + //} + if (!IsScopeAttached()) return; + if (!m_zoom_params.m_bUseDynamicZoom) return; + float delta, min_zoom_factor; + GetZoomData(m_zoom_params.m_fScopeZoomFactor, delta, min_zoom_factor); + + float f = GetZoomFactor() + delta; + clamp(f, m_zoom_params.m_fScopeZoomFactor, min_zoom_factor); + SetZoomFactor(f); +} + +void CWeapon::ZoomInc() +{ + /* + if (m_bCustomZoomEnabled) + { + float delta, min_zoom_factor; + if (!IsScopeAttached()) + return; + GetZoomData(m_fScopeZoomFactor, delta, min_zoom_factor); + m_fZoomFactor += delta; + clamp(m_fZoomFactor, m_fScopeZoomFactor, min_zoom_factor); + } + */ + if (!IsScopeAttached()) return; + if (!m_zoom_params.m_bUseDynamicZoom) return; + float delta, min_zoom_factor; + GetZoomData(m_zoom_params.m_fScopeZoomFactor, delta, min_zoom_factor); + float f = GetZoomFactor() - delta; + clamp(f, m_zoom_params.m_fScopeZoomFactor, min_zoom_factor); + SetZoomFactor(f); +} + +u32 CWeapon::Cost() const +{ + u32 res = CInventoryItem::Cost(); + if(IsGrenadeLauncherAttached()&&GetGrenadeLauncherName().size()){ + res += pSettings->r_u32(GetGrenadeLauncherName(),"cost"); + } + if(IsScopeAttached()&&m_scopes.size()){ + res += pSettings->r_u32(GetScopeName(),"cost"); + } + if(IsSilencerAttached()&&GetSilencerName().size()){ + res += pSettings->r_u32(GetSilencerName(),"cost"); + } + + if(iAmmoElapsed) + { + float w = pSettings->r_float(m_ammoTypes[m_ammoType].c_str(),"cost"); + float bs = pSettings->r_float(m_ammoTypes[m_ammoType].c_str(),"box_size"); + + res += iFloor(w*(iAmmoElapsed/bs)); + } + return res; +} \ No newline at end of file diff --git a/src/xrGameLA/Weapon.h b/src/xrGameLA/Weapon.h new file mode 100644 index 000000000..085ec9098 --- /dev/null +++ b/src/xrGameLA/Weapon.h @@ -0,0 +1,571 @@ +// Weapon.h: interface for the CWeapon class. +#pragma once + +#include "PhysicsShell.h" +#include "weaponammo.h" +#include "PHShellCreator.h" + +#include "ShootingObject.h" +#include "hud_item_object.h" +#include "Actor_Flags.h" +#include "../../Include/xrRender/KinematicsAnimated.h" +#include "game_cl_single.h" +#include "firedeps.h" + +// refs +class CEntity; +class ENGINE_API CMotionDef; +class CSE_ALifeItemWeapon; +class CSE_ALifeItemWeaponAmmo; +class CWeaponMagazined; +class CParticlesObject; +class CUIWindow; + +class CNightVisionEffector; +class CBinocularsVision; + +class CWeapon : public CHudItemObject, + public CShootingObject +{ +private: + typedef CHudItemObject inherited; + +public: + CWeapon (LPCSTR name); + virtual ~CWeapon (); + + // Generic + virtual void Load (LPCSTR section); + + // Happens when object gets online. Import saved data from server here. Do oject initialization based on data imported from server + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + // constant export of various weapon data to server. Here should be stuff, needed to be saved before object switches offline. Than it should be imported in net_spawn, when object gets online again + virtual void net_Export (NET_Packet& P); + // not happens in Single Player + virtual void net_Import (NET_Packet& P); + + void net_Relcase (CObject *object) override; + + virtual CWeapon *cast_weapon () {return this;} + virtual CWeaponMagazined*cast_weapon_magazined () {return 0;} + + + // save stuff that is needed while weapon is in alife radius + virtual void save (NET_Packet &output_packet); + // load stuff needed in alife radius + virtual void load (IReader &input_packet); + + virtual BOOL net_SaveRelevant () {return inherited::net_SaveRelevant();} + + virtual void UpdateCL (); + virtual void shedule_Update (u32 dt); + + virtual void renderable_Render (); + virtual void render_hud_mode (); + virtual bool need_renderable (); + + virtual void render_item_ui (); + virtual bool render_item_ui_query(); + + virtual void OnH_B_Chield (); + virtual void OnH_A_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + virtual void OnH_A_Independent (); + virtual void OnEvent (NET_Packet& P, u16 type);// {inherited::OnEvent(P,type);} + + virtual void Hit (SHit* pHDS); + + virtual void reinit (); + virtual void reload (LPCSTR section); + virtual void create_physic_shell (); + virtual void activate_physic_shell(); + virtual void setup_physic_shell (); + + virtual void SwitchState (u32 S); + + virtual void Hide (); + virtual void Show (); + + //инициализация если вещь в активном слоте или спрятана на OnH_B_Chield + virtual void OnActiveItem (); + virtual void OnHiddenItem (); + virtual void SendHiddenItem (); + + IC void ForceUpdateAmmo() { m_dwAmmoCurrentCalcFrame = 0; } +////////////////////////////////////////////////////////////////////////// +// Network +////////////////////////////////////////////////////////////////////////// + +public: + virtual bool can_kill () const; + virtual CInventoryItem *can_kill (CInventory *inventory) const; + virtual const CInventoryItem *can_kill (const xr_vector &items) const; + virtual bool ready_to_kill () const; + virtual bool NeedToDestroyObject () const; + virtual ALife::_TIME_ID TimePassedAfterIndependant() const; +protected: + //время удаления оружия + ALife::_TIME_ID m_dwWeaponRemoveTime; + ALife::_TIME_ID m_dwWeaponIndependencyTime; + + virtual bool IsHudModeNow (); + +////////////////////////////////////////////////////////////////////////// +// Animation +////////////////////////////////////////////////////////////////////////// +public: + void signal_HideComplete (); + +////////////////////////////////////////////////////////////////////////// +// InventoryItem methods +////////////////////////////////////////////////////////////////////////// +public: + virtual bool Action(u16 cmd, u32 flags); + +////////////////////////////////////////////////////////////////////////// +// Weapon state +////////////////////////////////////////////////////////////////////////// +public: + enum EWeaponStates { + eFire = eLastBaseState+1, + eFire2, + eReload, + eMisfire, + eMagEmpty, + eSwitch, + ePreFire, + }; + enum EWeaponSubStates{ + eSubstateReloadBegin =0, + eSubstateReloadInProcess, + eSubstateReloadEnd, + }; + + IC BOOL IsValid () const { return iAmmoElapsed; } + // Does weapon need's update? + BOOL IsUpdating (); + + + BOOL IsMisfire () const; + BOOL CheckForMisfire (); + + virtual bool MovingAnimAllowedNow (); + + BOOL AutoSpawnAmmo () const { return m_bAutoSpawnAmmo; }; + bool IsTriStateReload () const { return m_bTriStateReload;} + EWeaponSubStates GetReloadState () const { return (EWeaponSubStates)m_sub_state;} +protected: + bool m_bTriStateReload; + u8 m_sub_state; + // Weapon fires now + bool bWorking2; + // a misfire happens, you'll need to rearm weapon + bool bMisfire; + + BOOL m_bAutoSpawnAmmo; + + virtual bool AllowBore (); + +////////////////////////////////////////////////////////////////////////// +// Weapon Addons +////////////////////////////////////////////////////////////////////////// +public: + /////////////////////////////////////////// + // работа с аддонами к оружию + ////////////////////////////////////////// + + + bool IsGrenadeLauncherAttached () const; + bool IsScopeAttached () const; + bool IsSilencerAttached () const; + + virtual bool GrenadeLauncherAttachable(); + virtual bool ScopeAttachable(); + virtual bool SilencerAttachable(); + virtual bool UseScopeTexture() {return true;}; + + ALife::EWeaponAddonStatus get_GrenadeLauncherStatus () const { return m_eGrenadeLauncherStatus; } + ALife::EWeaponAddonStatus get_ScopeStatus () const { return m_eScopeStatus; } + ALife::EWeaponAddonStatus get_SilencerStatus () const { return m_eSilencerStatus; } + + //обновление видимости для косточек аддонов + void UpdateAddonsVisibility(); + void UpdateHUDAddonsVisibility(); + //инициализация свойств присоединенных аддонов + virtual void InitAddons(); + + //для отоброажения иконок апгрейдов в интерфейсе + int GetScopeX() {return pSettings->r_s32(m_scopes[m_cur_scope], "scope_x");} + int GetScopeY() {return pSettings->r_s32(m_scopes[m_cur_scope], "scope_y");} + int GetSilencerX() {return m_iSilencerX;} + int GetSilencerY() {return m_iSilencerY;} + int GetGrenadeLauncherX() {return m_iGrenadeLauncherX;} + int GetGrenadeLauncherY() {return m_iGrenadeLauncherY;} + + const shared_str& GetGrenadeLauncherName () const {return m_sGrenadeLauncherName;} + const shared_str GetScopeName () const { return pSettings->r_string(m_scopes[m_cur_scope], "scope_name"); } + const shared_str& GetSilencerName () const {return m_sSilencerName;} + + bool IsScopeIconForced () const { return m_bScopeForceIcon; } + bool IsSilencerIconForced () const { return m_bSilencerForceIcon; } + bool IsGrenadeLauncherIconForced () const { return m_bGrenadeLauncherForceIcon; } + + u8 GetAddonsState () const {return m_flagsAddOnState;}; + void SetAddonsState (u8 st) {m_flagsAddOnState=st;}//dont use!!! for buy menu only!!! +protected: + //состояние подключенных аддонов + u8 m_flagsAddOnState; + + //возможность подключения различных аддонов + ALife::EWeaponAddonStatus m_eScopeStatus; + ALife::EWeaponAddonStatus m_eSilencerStatus; + ALife::EWeaponAddonStatus m_eGrenadeLauncherStatus; + + //названия секций подключаемых аддонов + shared_str m_sScopeName; + shared_str m_sSilencerName; + shared_str m_sGrenadeLauncherName; + + //смещение иконов апгрейдов в инвентаре + int m_iScopeX, m_iScopeY; + int m_iSilencerX, m_iSilencerY; + int m_iGrenadeLauncherX, m_iGrenadeLauncherY; + + //рисовать ли иконку аддона вне зависимости от статуса (например для permanent) + bool m_bScopeForceIcon; + bool m_bSilencerForceIcon; + bool m_bGrenadeLauncherForceIcon; + +/////////////////////////////////////////////////// +// для режима приближения и снайперского прицела +/////////////////////////////////////////////////// +protected: + + struct SZoomParams + { + bool m_bZoomEnabled; //разрешение режима приближения + bool m_bHideCrosshairInZoom; +// bool m_bZoomDofEnabled; + + bool m_bIsZoomModeNow; //когда режим приближения включен + float m_fCurrentZoomFactor; //текущий фактор приближения + float m_fZoomRotateTime; //время приближения + + float m_fIronSightZoomFactor; //коэффициент увеличения прицеливания + float m_fScopeZoomFactor; //коэффициент увеличения прицела + + float m_fZoomRotationFactor; + +// Fvector m_ZoomDof; +// Fvector4 m_ReloadDof; + BOOL m_bUseDynamicZoom; + shared_str m_sUseZoomPostprocess; + shared_str m_sUseBinocularVision; + CBinocularsVision* m_pVision; + CNightVisionEffector* m_pNight_vision; + + SZoomParams(); + + } m_zoom_params; + + float m_fRTZoomFactor; //run-time zoom factor + CUIWindow* m_UIScope; +public: + + IC bool IsZoomEnabled () const {return m_zoom_params.m_bZoomEnabled;} + virtual void ZoomInc (); + virtual void ZoomDec (); + virtual void OnZoomIn (); + virtual void OnZoomOut (); + IC bool IsZoomed () const {return m_zoom_params.m_bIsZoomModeNow;}; + CUIWindow* ZoomTexture (); + + + bool ZoomHideCrosshair () {return m_zoom_params.m_bHideCrosshairInZoom || ZoomTexture();} + + IC float GetZoomFactor () const {return m_zoom_params.m_fCurrentZoomFactor;} + IC void SetZoomFactor (float f) {m_zoom_params.m_fCurrentZoomFactor = f;} + + virtual float CurrentZoomFactor (); + //показывает, что оружие находится в соостоянии поворота для приближенного прицеливания + bool IsRotatingToZoom () const { return (m_zoom_params.m_fZoomRotationFactor<1.f);} + + virtual u8 GetCurrentHudOffsetIdx (); + + virtual float Weight () const; + virtual u32 Cost () const; +//gr1ph: +protected: + virtual void GetZoomData (const float scope_factor, float& delta, float& min_zoom_factor); +public: + virtual EHandDependence HandDependence () const { return eHandDependence;} + bool IsSingleHanded () const { return m_bIsSingleHanded; } + +public: + IC LPCSTR strap_bone0 () const {return m_strap_bone0;} + IC LPCSTR strap_bone1 () const {return m_strap_bone1;} + IC void strapped_mode (bool value) {m_strapped_mode = value;} + IC bool strapped_mode () const {return m_strapped_mode;} + +protected: + int m_strap_bone0_id; + int m_strap_bone1_id; + bool m_strapped_mode_rifle; + LPCSTR m_strap_bone0; + LPCSTR m_strap_bone1; + Fmatrix m_StrapOffset; + bool m_strapped_mode; + bool m_can_be_strapped; + bool m_can_be_strapped_rifle; + + Fmatrix m_Offset; + // 0-используется без участия рук, 1-одна рука, 2-две руки + EHandDependence eHandDependence; + bool m_bIsSingleHanded; + +public: + //загружаемые параметры + Fvector vLoadedFirePoint ; + Fvector vLoadedFirePoint2 ; + +private: + firedeps m_firedeps; + +protected: + virtual void UpdateFireDependencies_internal (); + virtual void UpdatePosition (const Fmatrix& transform); //. + virtual void UpdateXForm (); + virtual void UpdateHudAdditonal (Fmatrix&); + IC void UpdateFireDependencies () { if (dwFP_Frame==Device.dwFrame) return; UpdateFireDependencies_internal(); }; + + virtual void LoadFireParams (LPCSTR section, LPCSTR prefix); +public: + IC const Fvector& get_LastFP () { UpdateFireDependencies(); return m_firedeps.vLastFP; } + IC const Fvector& get_LastFP2 () { UpdateFireDependencies(); return m_firedeps.vLastFP2; } + IC const Fvector& get_LastFD () { UpdateFireDependencies(); return m_firedeps.vLastFD; } + IC const Fvector& get_LastSP () { UpdateFireDependencies(); return m_firedeps.vLastSP; } + + virtual const Fvector& get_CurrentFirePoint () { return get_LastFP(); } + virtual const Fvector& get_CurrentFirePoint2 () { return get_LastFP2(); } + virtual const Fmatrix& get_ParticlesXFORM () { UpdateFireDependencies(); return m_firedeps.m_FireParticlesXForm; } + virtual void ForceUpdateFireParticles(); + virtual void debug_draw_firedeps (); + + ////////////////////////////////////////////////////////////////////////// + // Weapon fire + ////////////////////////////////////////////////////////////////////////// +protected: + virtual void SetDefaults (); + + //трассирование полета пули + virtual void FireTrace (const Fvector& P, const Fvector& D); + virtual float GetWeaponDeterioration (); + + virtual void FireStart () {CShootingObject::FireStart();} + virtual void FireEnd ();// {CShootingObject::FireEnd();} + + virtual void Fire2Start (); + virtual void Fire2End (); + virtual void Reload (); + virtual void StopShooting (); + + + // обработка визуализации выстрела + virtual void OnShot (){}; + virtual void AddShotEffector (); + virtual void RemoveShotEffector (); + virtual void ClearShotEffector (); + +public: + float GetFireDispersion (bool with_cartridge); + virtual float GetFireDispersion (float cartridge_k); + virtual int ShotsFired () { return 0; } + virtual int GetCurrentFireMode () { return 1; } + + //параметы оружия в зависимоти от его состояния исправности + float GetConditionDispersionFactor () const; + float GetConditionMisfireProbability () const; + virtual float GetConditionToShow () const; + +public: + //отдача при стрельбе + struct CameraRecoil + { + public: + float camRelaxSpeed; + float camRelaxSpeed_AI; + float camDispersion; + float camDispersionInc; + float camDispersionFrac; + float camMaxAngleVert; // new + float camMaxAngleHorz; + float camStepAngleHorz; + bool camReturnMode; // new + bool camStopReturn; // new + } cam_recoil, zoom_cam_recoil; + + +protected: + //фактор увеличения дисперсии при максимальной изношености + //(на сколько процентов увеличится дисперсия) + float fireDispersionConditionFactor; + //вероятность осечки при максимальной изношености + +// modified by Peacemaker [17.10.08] +// float misfireProbability; +// float misfireConditionK; + float misfireStartCondition; //изношенность, при которой появляется шанс осечки + float misfireEndCondition; //изношеность при которой шанс осечки становится константным + float misfireStartProbability; //шанс осечки при изношености больше чем misfireStartCondition + float misfireEndProbability; //шанс осечки при изношености больше чем misfireEndCondition + float conditionDecreasePerShot; //увеличение изношености при одиночном выстреле + +public: + float GetMisfireStartCondition () const {return misfireStartCondition;}; + float GetMisfireEndCondition () const {return misfireEndCondition;}; + +protected: + struct SPDM + { + float m_fPDM_disp_base ; + float m_fPDM_disp_vel_factor ; + float m_fPDM_disp_accel_factor ; + float m_fPDM_disp_crouch ; + float m_fPDM_disp_crouch_no_acc ; + }; + SPDM m_pdm; + + // Коэффициент силы покачивания ствола при прицеливании, умножается на разброс Актора (вычисляемый из PDM) + float m_fZoomInertCoef; + +protected: + //для отдачи оружия + Fvector m_vRecoilDeltaAngle; + + //для сталкеров, чтоб они знали эффективные границы использования + //оружия + float m_fMinRadius; + float m_fMaxRadius; + +////////////////////////////////////////////////////////////////////////// +// партиклы +////////////////////////////////////////////////////////////////////////// + +protected: + //для второго ствола + void StartFlameParticles2(); + void StopFlameParticles2 (); + void UpdateFlameParticles2(); +protected: + shared_str m_sFlameParticles2; + //объект партиклов для стрельбы из 2-го ствола + CParticlesObject* m_pFlameParticles2; + +////////////////////////////////////////////////////////////////////////// +// Weapon and ammo +////////////////////////////////////////////////////////////////////////// +public: + IC int GetAmmoElapsed () const { return /*int(m_magazine.size())*/iAmmoElapsed;} + IC int GetAmmoMagSize () const { return maxMagazineSize_; } + int GetAmmoCurrent (bool use_item_to_spawn = false) const; + + void SetAmmoElapsed (int ammo_count); + + virtual void OnMagazineEmpty (); + void SpawnAmmo (u32 boxCurr = 0xffffffff, + LPCSTR ammoSect = NULL, + u32 ParentID = 0xffffffff); + + virtual float Get_PDM_Base () const { return m_pdm.m_fPDM_disp_base ; }; + virtual float Get_PDM_Vel_F () const { return m_pdm.m_fPDM_disp_vel_factor ; }; + virtual float Get_PDM_Accel_F () const { return m_pdm.m_fPDM_disp_accel_factor ; }; + virtual float Get_PDM_Crouch () const { return m_pdm.m_fPDM_disp_crouch ; }; + virtual float Get_PDM_Crouch_NA () const { return m_pdm.m_fPDM_disp_crouch_no_acc ; }; + + virtual float GetZoomInertion () const { return m_fZoomInertCoef; }; + //virtual float GetCrosshairInertion() const { return m_crosshair_inertion; }; + // float GetFirstBulletDisp () const { return m_first_bullet_controller.get_fire_dispertion(); }; +protected: + int iAmmoElapsed; // ammo in magazine, currently + int maxMagazineSize_; // size (in bullets) of magazine + + //для подсчета в GetAmmoCurrent + mutable int iAmmoCurrent; + mutable u32 m_dwAmmoCurrentCalcFrame; //кадр на котором просчитали кол-во патронов + // [10/5/2005] + bool m_bAmmoWasSpawned; + // [10/5/2005] + + virtual bool IsNecessaryItem (const shared_str& item_sect); + +public: + xr_vector m_ammoTypes; + + DEFINE_VECTOR(shared_str, SCOPES_VECTOR, SCOPES_VECTOR_IT); + SCOPES_VECTOR m_scopes; + u8 m_cur_scope; + CWeaponAmmo* m_pAmmo; + u8 m_ammoType; + shared_str m_ammoName; + bool m_bHasTracers; + u8 m_u8TracerColorID; + u8 m_set_next_ammoType_on_reload; + // Multitype ammo support + xr_vector m_magazine; + CCartridge m_DefaultCartridge; + float m_fCurrentCartirdgeDisp; + + bool unlimited_ammo (); + IC bool can_be_strapped () const {return m_can_be_strapped;}; + + LPCSTR GetCurrentAmmo_ShortName (); + +protected: + u32 m_ef_main_weapon_type; + u32 m_ef_weapon_type; + u32 m_ai_weapon_rank; + +public: + virtual u32 ef_main_weapon_type () const; + virtual u32 ef_weapon_type () const; + virtual u32 ai_weapon_rank () const { return m_ai_weapon_rank; }; + +protected: + // This is because when scope is attached we can't ask scope for these params + // therefore we should hold them by ourself :-(( + float m_addon_holder_range_modifier; + float m_addon_holder_fov_modifier; +public: + virtual void modify_holder_params (float &range, float &fov) const; + virtual bool use_crosshair () const {return true;} + bool show_crosshair (); + bool show_indicators (); + virtual BOOL ParentMayHaveAimBullet (); + virtual BOOL ParentIsActor (); + +private: + bool install_upgrade_disp ( LPCSTR section, bool test ); + bool install_upgrade_hit ( LPCSTR section, bool test ); + bool install_upgrade_addon ( LPCSTR section, bool test ); +protected: + virtual bool install_upgrade_ammo_class ( LPCSTR section, bool test ); + virtual bool install_upgrade_impl ( LPCSTR section, bool test ); + bool ProcessRpmUpgrade ( LPCSTR section, LPCSTR paramName, float &timeToFireProperty, bool test); + bool process_if_exists_deg2rad ( LPCSTR section, LPCSTR name, float& value, bool test ); + +private: + float m_hit_probability[egdCount]; + +public: + const float &hit_probability () const; +private: + bool m_bRememberActorNVisnStatus; +public: + bool GetRememberActorNVisnStatus () {return m_bRememberActorNVisnStatus;}; + virtual void EnableActorNVisnAfterZoom (); +}; + + +extern bool m_bDraw_off; +extern bool m_bHolster_off; diff --git a/src/xrGameLA/WeaponAK74.cpp b/src/xrGameLA/WeaponAK74.cpp new file mode 100644 index 000000000..5751a195d --- /dev/null +++ b/src/xrGameLA/WeaponAK74.cpp @@ -0,0 +1,23 @@ +#include "pch_script.h" +#include "WeaponAK74.h" +#include "player_hud.h" + +CWeaponAK74::CWeaponAK74(LPCSTR name, ESoundTypes eSoundType) : CWeaponMagazinedWGrenade(name, eSoundType) +{ +} + +CWeaponAK74::~CWeaponAK74() +{ +} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponAK74::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponAK74") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponAK74.h b/src/xrGameLA/WeaponAK74.h new file mode 100644 index 000000000..9d627c5e2 --- /dev/null +++ b/src/xrGameLA/WeaponAK74.h @@ -0,0 +1,23 @@ +#ifndef __XR_WEAPON_AK74_H__ +#define __XR_WEAPON_AK74_H__ + +#pragma once + +#include "WeaponMagazinedWGrenade.h" +#include "script_export_space.h" + +class CWeaponAK74: public CWeaponMagazinedWGrenade +{ +private: + typedef CWeaponMagazinedWGrenade inherited; +public: + CWeaponAK74 (LPCSTR name="AK74",ESoundTypes eSoundType=SOUND_TYPE_WEAPON_SUBMACHINEGUN); + virtual ~CWeaponAK74 (); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponAK74) +#undef script_type_list +#define script_type_list save_type_list(CWeaponAK74) + +#endif //__XR_WEAPON_AK74_H__ diff --git a/src/xrGameLA/WeaponAmmo.cpp b/src/xrGameLA/WeaponAmmo.cpp new file mode 100644 index 000000000..a653ffc52 --- /dev/null +++ b/src/xrGameLA/WeaponAmmo.cpp @@ -0,0 +1,249 @@ +#include "stdafx.h" +#include "weaponammo.h" +#include "PhysicsShell.h" +#include "xrserver_objects_alife_items.h" +#include "Actor_Flags.h" +#include "inventory.h" +#include "weapon.h" +#include "level_bullet_manager.h" +#include "ai_space.h" +#include "../gamemtllib.h" +#include "level.h" +#include "string_table.h" + +#define BULLET_MANAGER_SECTION "bullet_manager" + +CCartridge::CCartridge() +{ + m_flags.assign (cfTracer | cfRicochet); + m_ammoSect = NULL; + param_s.Init(); + bullet_material_idx = u16(-1); +} + +void CCartridge::Load(LPCSTR section, u8 LocalAmmoType) +{ + m_ammoSect = section; + m_LocalAmmoType = LocalAmmoType; + param_s.kDist = pSettings->r_float(section, "k_dist"); + param_s.kDisp = pSettings->r_float(section, "k_disp"); + param_s.kHit = pSettings->r_float(section, "k_hit"); +//. param_s.kCritical = pSettings->r_float(section, "k_hit_critical"); + param_s.kImpulse = pSettings->r_float(section, "k_impulse"); + //m_kPierce = pSettings->r_float(section, "k_pierce"); + param_s.kAP = pSettings->r_float(section, "k_ap"); + param_s.u8ColorID = READ_IF_EXISTS(pSettings, r_u8, section, "tracer_color_ID", 0); + + if (pSettings->line_exist(section, "k_air_resistance")) + param_s.kAirRes = pSettings->r_float(section, "k_air_resistance"); + else + param_s.kAirRes = pSettings->r_float(BULLET_MANAGER_SECTION, "air_resistance_k"); + + param_s.kSpeed = READ_IF_EXISTS(pSettings, r_float, section, "k_speed", 1.0f); + + m_flags.set (cfTracer, pSettings->r_bool(section, "tracer")); + param_s.buckShot = pSettings->r_s32( section, "buck_shot"); + param_s.impair = pSettings->r_float(section, "impair"); + param_s.fWallmarkSize = pSettings->r_float(section, "wm_size"); + + m_flags.set (cfCanBeUnlimited | cfRicochet, TRUE); + m_flags.set (cfMagneticBeam, FALSE); + + if (pSettings->line_exist(section, "allow_ricochet")) + { + if (!pSettings->r_bool(section, "allow_ricochet")) + m_flags.set(cfRicochet, FALSE); + } + if (pSettings->line_exist(section, "magnetic_beam_shot")) + { + if (pSettings->r_bool(section, "magnetic_beam_shot")) + m_flags.set(cfMagneticBeam, TRUE); + } + + if(pSettings->line_exist(section,"can_be_unlimited")) + m_flags.set(cfCanBeUnlimited, pSettings->r_bool(section, "can_be_unlimited")); + + m_flags.set (cfExplosive, pSettings->r_bool(section, "explosive")); + + bullet_material_idx = GMLib.GetMaterialIdx(WEAPON_MATERIAL_NAME); + VERIFY (u16(-1)!=bullet_material_idx); + VERIFY (param_s.fWallmarkSize>0); + + m_InvShortName = CStringTable().translate( pSettings->r_string(section, "inv_name_short")); + //Msg("Allow Ricochet for %s %s", section, (m_flags.test(cfRicochet)) ? "true" : "false"); +} + +CWeaponAmmo::CWeaponAmmo(void) +{ +} + +CWeaponAmmo::~CWeaponAmmo(void) +{ +} + +void CWeaponAmmo::Load(LPCSTR section) +{ + inherited::Load (section); + + cartridge_param.kDist = pSettings->r_float(section, "k_dist"); + cartridge_param.kDisp = pSettings->r_float(section, "k_disp"); + cartridge_param.kHit = pSettings->r_float(section, "k_hit"); +//. cartridge_param.kCritical = pSettings->r_float(section, "k_hit_critical"); + cartridge_param.kImpulse = pSettings->r_float(section, "k_impulse"); + //m_kPierce = pSettings->r_float(section, "k_pierce"); + cartridge_param.kAP = pSettings->r_float(section, "k_ap"); + cartridge_param.u8ColorID = READ_IF_EXISTS(pSettings, r_u8, section, "tracer_color_ID", 0); + + if (pSettings->line_exist(section, "k_air_resistance")) + cartridge_param.kAirRes = pSettings->r_float(section, "k_air_resistance"); + else + cartridge_param.kAirRes = pSettings->r_float(BULLET_MANAGER_SECTION, "air_resistance_k"); + + cartridge_param.kSpeed = READ_IF_EXISTS(pSettings, r_float, section, "k_speed", 1.0f); + m_tracer = !!pSettings->r_bool(section, "tracer"); + cartridge_param.buckShot = pSettings->r_s32( section, "buck_shot"); + cartridge_param.impair = pSettings->r_float(section, "impair"); + cartridge_param.fWallmarkSize = pSettings->r_float(section, "wm_size"); + R_ASSERT (cartridge_param.fWallmarkSize>0); + + m_boxSize = (u16)pSettings->r_s32(section, "box_size"); + m_boxCurr = m_boxSize; +} + +BOOL CWeaponAmmo::net_Spawn(CSE_Abstract* DC) +{ + BOOL bResult = inherited::net_Spawn (DC); + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeItemAmmo* l_pW = smart_cast(e); + m_boxCurr = l_pW->a_elapsed; + + if(m_boxCurr > m_boxSize) + l_pW->a_elapsed = m_boxCurr = m_boxSize; + + return bResult; +} + +void CWeaponAmmo::net_Destroy() +{ + inherited::net_Destroy (); +} + +void CWeaponAmmo::OnH_B_Chield() +{ + inherited::OnH_B_Chield (); +} + +void CWeaponAmmo::OnH_B_Independent(bool just_before_destroy) +{ + if(!Useful()) { + + if (Local()){ + DestroyObject (); + } + m_ready_to_destroy = true; + } + inherited::OnH_B_Independent(just_before_destroy); +} + + +bool CWeaponAmmo::Useful() const +{ + // Если IItem еще не полностью использованый, вернуть true + return !!m_boxCurr; +} +/* +s32 CWeaponAmmo::Sort(PIItem pIItem) +{ + // Если нужно разместить IItem после this - вернуть 1, если + // перед - -1. Если пофиг то 0. + CWeaponAmmo *l_pA = smart_cast(pIItem); + if(!l_pA) return 0; + if(xr_strcmp(cNameSect(), l_pA->cNameSect())) return 0; + if(m_boxCurr <= l_pA->m_boxCurr) return 1; + else return -1; +} +*/ +bool CWeaponAmmo::Get(CCartridge &cartridge) +{ + if(!m_boxCurr) return false; + cartridge.m_ammoSect = cNameSect(); + + cartridge.param_s = cartridge_param; + + cartridge.m_flags.set(CCartridge::cfTracer ,m_tracer); + cartridge.bullet_material_idx = GMLib.GetMaterialIdx(WEAPON_MATERIAL_NAME); + cartridge.m_InvShortName = NameShort(); + --m_boxCurr; + if(m_pCurrentInventory) + m_pCurrentInventory->InvalidateState(); + return true; +} + +void CWeaponAmmo::renderable_Render() +{ + if(!m_ready_to_destroy) + inherited::renderable_Render(); +} + +void CWeaponAmmo::UpdateCL() +{ + VERIFY2 (_valid(renderable.xform),*cName()); + inherited::UpdateCL (); + VERIFY2 (_valid(renderable.xform),*cName()); + + if(!IsGameTypeSingle()) + make_Interpolation (); + + VERIFY2 (_valid(renderable.xform),*cName()); + +} + +void CWeaponAmmo::net_Export(NET_Packet& P) +{ + inherited::net_Export (P); + + P.w_u16 (m_boxCurr); +} + +void CWeaponAmmo::net_Import(NET_Packet& P) +{ + inherited::net_Import (P); + + P.r_u16 (m_boxCurr); +} + +CInventoryItem *CWeaponAmmo::can_make_killing (const CInventory *inventory) const +{ + VERIFY (inventory); + + TIItemContainer::const_iterator I = inventory->m_all.begin(); + TIItemContainer::const_iterator E = inventory->m_all.end(); + for ( ; I != E; ++I) { + CWeapon *weapon = smart_cast(*I); + if (!weapon) + continue; + xr_vector::const_iterator i = std::find(weapon->m_ammoTypes.begin(),weapon->m_ammoTypes.end(),cNameSect()); + if (i != weapon->m_ammoTypes.end()) + return (weapon); + } + + return (0); +} + +float CWeaponAmmo::Weight() const +{ + float res = inherited::Weight(); + + res *= (float)m_boxCurr/(float)m_boxSize; + + return res; +} + +u32 CWeaponAmmo::Cost() const +{ + u32 res = inherited::Cost(); + + res = iFloor(res*(float)m_boxCurr/(float)m_boxSize+0.5f); + + return res; +} diff --git a/src/xrGameLA/WeaponAmmo.h b/src/xrGameLA/WeaponAmmo.h new file mode 100644 index 000000000..9f8c40c84 --- /dev/null +++ b/src/xrGameLA/WeaponAmmo.h @@ -0,0 +1,82 @@ +#pragma once +#include "inventory_item_object.h" + +struct SCartridgeParam +{ + float kDist, kDisp, kHit/*, kCritical*/, kImpulse, kAP, kAirRes, kSpeed; + int buckShot; + float impair; + float fWallmarkSize; + u8 u8ColorID; + + IC void Init() + { + kDist = kDisp = kHit = kImpulse = kSpeed = 1.0f; +// kCritical = 0.0f; + kAP = 0.0f; + kAirRes = 0.0f; + buckShot = 1; + impair = 1.0f; + fWallmarkSize = 0.0f; + u8ColorID = 0; + } +}; + +class CCartridge // : public IAnticheatDumpable +{ +public: + CCartridge(); + void Load(LPCSTR section, u8 LocalAmmoType); + + shared_str m_ammoSect; + enum{ + cfTracer = (1<<0), + cfRicochet = (1<<1), + cfCanBeUnlimited = (1<<2), + cfExplosive = (1<<3), + cfMagneticBeam = (1<<4), + }; + SCartridgeParam param_s; + + u8 m_LocalAmmoType; + + + u16 bullet_material_idx; + Flags8 m_flags; + + shared_str m_InvShortName; +}; + +class CWeaponAmmo : + public CInventoryItemObject { + typedef CInventoryItemObject inherited; +public: + CWeaponAmmo (void); + virtual ~CWeaponAmmo (void); + + virtual CWeaponAmmo *cast_weapon_ammo () {return this;} + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Export (NET_Packet& P); + virtual void net_Import (NET_Packet& P); + virtual void OnH_B_Chield (); + virtual void OnH_B_Independent (bool just_before_destroy); + virtual void UpdateCL (); + virtual void renderable_Render (); + + virtual bool Useful () const; + virtual float Weight () const; + virtual u32 Cost () const; + + bool Get (CCartridge &cartridge); + + SCartridgeParam cartridge_param; + + u16 m_boxSize; + u16 m_boxCurr; + bool m_tracer; + +public: + virtual CInventoryItem *can_make_killing (const CInventory *inventory) const; +}; diff --git a/src/xrGameLA/WeaponAutomaticShotgun.cpp b/src/xrGameLA/WeaponAutomaticShotgun.cpp new file mode 100644 index 000000000..9ad73746e --- /dev/null +++ b/src/xrGameLA/WeaponAutomaticShotgun.cpp @@ -0,0 +1,247 @@ +#include "stdafx.h" +#include "WeaponAutomaticShotgun.h" +#include "entity.h" +#include "ParticlesObject.h" +#include "xr_level_controller.h" +#include "inventory.h" +#include "level.h" +#include "actor.h" + +CWeaponAutomaticShotgun::CWeaponAutomaticShotgun() +{ + m_eSoundClose = ESoundTypes(SOUND_TYPE_WEAPON_SHOOTING); + m_eSoundAddCartridge = ESoundTypes(SOUND_TYPE_WEAPON_SHOOTING); +} + +CWeaponAutomaticShotgun::~CWeaponAutomaticShotgun() +{ +} + +void CWeaponAutomaticShotgun::Load(LPCSTR section) +{ + inherited::Load(section); + + if(pSettings->line_exist(section, "tri_state_reload")){ + m_bTriStateReload = !!pSettings->r_bool(section, "tri_state_reload"); + }; + if(m_bTriStateReload){ + m_sounds.LoadSound(section, "snd_open_weapon", "sndOpen", false, m_eSoundOpen); + + m_sounds.LoadSound(section, "snd_add_cartridge", "sndAddCartridge", false, m_eSoundAddCartridge); + + m_sounds.LoadSound(section, "snd_close_weapon", "sndClose", false, m_eSoundClose); + }; + +} + +bool CWeaponAutomaticShotgun::Action(u16 cmd, u32 flags) +{ + if(inherited::Action(cmd, flags)) return true; + + if( m_bTriStateReload && GetState()==eReload && + cmd==kWPN_FIRE && flags&CMD_START && + m_sub_state==eSubstateReloadInProcess )//остановить перезагрузку + { + AddCartridge(1); + m_sub_state = eSubstateReloadEnd; + return true; + } + return false; +} + +void CWeaponAutomaticShotgun::OnAnimationEnd(u32 state) +{ + if(!m_bTriStateReload || state != eReload) + return inherited::OnAnimationEnd(state); + + switch(m_sub_state){ + case eSubstateReloadBegin:{ + m_sub_state = eSubstateReloadInProcess; + SwitchState(eReload); + }break; + + case eSubstateReloadInProcess:{ + if( 0 != AddCartridge(1) ){ + m_sub_state = eSubstateReloadEnd; + } + SwitchState(eReload); + }break; + + case eSubstateReloadEnd:{ + m_sub_state = eSubstateReloadBegin; + SwitchState(eIdle); + }break; + + }; +} + +void CWeaponAutomaticShotgun::Reload() +{ + if(m_bTriStateReload){ + TriStateReload(); + }else + inherited::Reload(); +} + +void CWeaponAutomaticShotgun::TriStateReload() +{ + if (m_magazine.size() == (u32)maxMagazineSize_ || !HaveCartridgeInInventory(1))return; + CWeapon::Reload (); + m_sub_state = eSubstateReloadBegin; + SwitchState (eReload); +} + +void CWeaponAutomaticShotgun::OnStateSwitch (u32 S) +{ + if(!m_bTriStateReload || S != eReload){ + inherited::OnStateSwitch(S); + return; + } + + CWeapon::OnStateSwitch(S); + + if (m_magazine.size() == (u32)maxMagazineSize_ || !HaveCartridgeInInventory(1)){ + switch2_EndReload (); + m_sub_state = eSubstateReloadEnd; + return; + }; + + switch (m_sub_state) + { + case eSubstateReloadBegin: + if( HaveCartridgeInInventory(1) ) + switch2_StartReload (); + break; + case eSubstateReloadInProcess: + if( HaveCartridgeInInventory(1) ) + switch2_AddCartgidge (); + break; + case eSubstateReloadEnd: + switch2_EndReload (); + break; + }; +} + +void CWeaponAutomaticShotgun::switch2_StartReload() +{ + PlaySound ("sndOpen",get_LastFP()); + PlayAnimOpenWeapon (); + SetPending (TRUE); +} + +void CWeaponAutomaticShotgun::switch2_AddCartgidge () +{ + PlaySound ("sndAddCartridge",get_LastFP()); + PlayAnimAddOneCartridgeWeapon(); + SetPending (TRUE); +} + +void CWeaponAutomaticShotgun::switch2_EndReload () +{ + SetPending (FALSE); + PlaySound ("sndClose",get_LastFP()); + PlayAnimCloseWeapon (); +} + +void CWeaponAutomaticShotgun::PlayAnimOpenWeapon() +{ + VERIFY(GetState()==eReload); + PlayHUDMotion("anim_open",FALSE,this,GetState()); +} +void CWeaponAutomaticShotgun::PlayAnimAddOneCartridgeWeapon() +{ + VERIFY(GetState()==eReload); + PlayHUDMotion("anim_add_cartridge",FALSE,this,GetState()); +} +void CWeaponAutomaticShotgun::PlayAnimCloseWeapon() +{ + VERIFY(GetState()==eReload); + PlayHUDMotion("anim_close",FALSE,this,GetState()); +} + +bool CWeaponAutomaticShotgun::HaveCartridgeInInventory(u8 cnt) +{ + if (unlimited_ammo()) return true; + m_pAmmo = NULL; + if (m_pCurrentInventory) + { + //попытаться найти в инвентаре патроны текущего типа + m_pAmmo = smart_cast(m_pCurrentInventory->GetAny(*m_ammoTypes[m_ammoType])); + + if (!m_pAmmo) + { + for (u32 i = 0; i < m_ammoTypes.size(); ++i) + { + //проверить патроны всех подходящих типов + m_pAmmo = smart_cast(m_pCurrentInventory->GetAny(*m_ammoTypes[i])); + if (m_pAmmo) + { + m_ammoType = i; + break; + } + } + } + } + return (m_pAmmo != NULL) && (m_pAmmo->m_boxCurr >= cnt); +} + + +u8 CWeaponAutomaticShotgun::AddCartridge (u8 cnt) +{ + if (IsMisfire()) bMisfire = false; + + if (m_set_next_ammoType_on_reload != u8(-1)){ + m_ammoType = m_set_next_ammoType_on_reload; + m_set_next_ammoType_on_reload = u8(-1); + + } + + if (!HaveCartridgeInInventory(1)) + return 0; + + VERIFY((u32)iAmmoElapsed == m_magazine.size()); + + + if (m_DefaultCartridge.m_LocalAmmoType != m_ammoType) + m_DefaultCartridge.Load(*m_ammoTypes[m_ammoType], u8(m_ammoType)); + CCartridge l_cartridge = m_DefaultCartridge; + while (cnt)// && m_pAmmo->Get(l_cartridge)) + { + if (!unlimited_ammo()) + { + if (!m_pAmmo->Get(l_cartridge)) break; + } + --cnt; + ++iAmmoElapsed; + l_cartridge.m_LocalAmmoType = u8(m_ammoType); + m_magazine.push_back(l_cartridge); + // m_fCurrentCartirdgeDisp = l_cartridge.m_kDisp; + } + m_ammoName = (m_pAmmo) ? m_pAmmo->m_nameShort : NULL; + + VERIFY((u32)iAmmoElapsed == m_magazine.size()); + + //выкинуть коробку патронов, если она пустая + if (m_pAmmo && !m_pAmmo->m_boxCurr && OnServer()) + m_pAmmo->SetDropManual(TRUE); + + return cnt; +} + +void CWeaponAutomaticShotgun::net_Export (NET_Packet& P) +{ + inherited::net_Export(P); + P.w_u8(u8(m_magazine.size())); + for (u32 i=0; iPosition(), H_Parent(), b_hud_mode); + } + inherited::OnZoomIn (); +} + +void CWeaponBinoculars::OnZoomOut () +{ + if(H_Parent() && IsZoomed() && !IsRotatingToZoom()) + { + m_sounds.StopSound("sndZoomIn"); + bool b_hud_mode = (Level().CurrentEntity() == H_Parent()); + m_sounds.PlaySound("sndZoomOut", H_Parent()->Position(), H_Parent(), b_hud_mode); + } + + inherited::OnZoomOut(); +} + +BOOL CWeaponBinoculars::net_Spawn (CSE_Abstract* DC) +{ + inherited::net_Spawn (DC); + return TRUE; +} + +void GetZoomData(const float scope_factor, float& delta, float& min_zoom_factor) +{ + float def_fov = float(g_fov); + float min_zoom_k = 0.3f; + float zoom_step_count = 3.0f; + float delta_factor_total = def_fov-scope_factor; + VERIFY(delta_factor_total>0); + min_zoom_factor = def_fov-delta_factor_total*min_zoom_k; + delta = (delta_factor_total*(1-min_zoom_k) )/zoom_step_count; + +} + +void CWeaponBinoculars::ZoomInc() +{ + float delta,min_zoom_factor; + GetZoomData(m_zoom_params.m_fScopeZoomFactor, delta, min_zoom_factor); + + float f = GetZoomFactor()-delta; + clamp (f,m_zoom_params.m_fScopeZoomFactor,min_zoom_factor); + SetZoomFactor ( f ); +} + +void CWeaponBinoculars::ZoomDec() +{ + float delta,min_zoom_factor; + GetZoomData(m_zoom_params.m_fScopeZoomFactor,delta,min_zoom_factor); + + float f = GetZoomFactor()+delta; + clamp (f,m_zoom_params.m_fScopeZoomFactor,min_zoom_factor); + SetZoomFactor ( f ); + +} + +//tatarinrafa: Cweapon alredy has this + +//void CWeaponBinoculars::save(NET_Packet &output_packet) +//{ +// inherited::save(output_packet); +// save_data (m_fRTZoomFactor,output_packet); +//} + +//void CWeaponBinoculars::load(IReader &input_packet) +//{ +// inherited::load(input_packet); +// load_data (m_fRTZoomFactor,input_packet); +//} + +void CWeaponBinoculars::GetBriefInfo(xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count) +{ + str_name = NameShort(); + str_count = ""; + icon_sect_name = *cNameSect(); +} diff --git a/src/xrGameLA/WeaponBinoculars.h b/src/xrGameLA/WeaponBinoculars.h new file mode 100644 index 000000000..fb55b598b --- /dev/null +++ b/src/xrGameLA/WeaponBinoculars.h @@ -0,0 +1,43 @@ +#pragma once + +#include "WeaponCustomPistol.h" +#include "script_export_space.h" + +class CUIFrameWindow; +class CUIStatic; +class CBinocularsVision; + +// CBinocularsVision usage moved to CWeapon +class CWeaponBinoculars: public CWeaponCustomPistol +{ +private: + typedef CWeaponCustomPistol inherited; + +public: + CWeaponBinoculars (); + virtual ~CWeaponBinoculars (); + + void Load (LPCSTR section); + + virtual void OnZoomIn (); + virtual void OnZoomOut (); + virtual void ZoomInc (); + virtual void ZoomDec (); + virtual BOOL net_Spawn (CSE_Abstract* DC); + + //tatarinrafa: Uncoment when needed +// virtual void save (NET_Packet &output_packet); +// virtual void load (IReader &input_packet); + + virtual bool Action (u16 cmd, u32 flags); + virtual bool use_crosshair () const {return false;} + virtual void GetBriefInfo (xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count); + + virtual LPCSTR GetCurrentFireModeStr () {return " ";}; +protected: + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponBinoculars) +#undef script_type_list +#define script_type_list save_type_list(CWeaponBinoculars) diff --git a/src/xrGameLA/WeaponBinocularsVision.cpp b/src/xrGameLA/WeaponBinocularsVision.cpp new file mode 100644 index 000000000..cb0632f99 --- /dev/null +++ b/src/xrGameLA/WeaponBinocularsVision.cpp @@ -0,0 +1,292 @@ +#include "stdafx.h" +#include "WeaponBinocularsVision.h" +#include "WeaponBinoculars.h" +#include "ui\UIFrameWindow.h" +#include "entity_alive.h" +#include "visual_memory_manager.h" +#include "actor.h" +#include "actor_memory.h" +#include "relation_registry.h" +#include "object_broker.h" + +#include "game_base_space.h" +#include "Level.h" +#include "game_cl_base.h" +#include "AI/Monsters/BaseMonster/base_monster.h" +#include "../igame_persistent.h" + +#define RECT_SIZE 16 + +extern u32 C_ON_ENEMY; +extern u32 C_ON_NEUTRAL; +extern u32 C_ON_FRIEND; + +struct FindVisObjByObject{ + const CObject* O; + FindVisObjByObject(const CObject* o):O(o){} + bool operator () (const SBinocVisibleObj* vis){ + return (O==vis->m_object); + } +}; + +void SBinocVisibleObj::create_default(u32 color) +{ + Frect r = {0,0,RECT_SIZE,RECT_SIZE}; + m_lt.InitTexture ("ui\\ui_enemy_frame");m_lt.SetWndRect(r);m_lt.SetAlignment(waCenter); + m_lb.InitTexture ("ui\\ui_enemy_frame");m_lb.SetWndRect(r);m_lb.SetAlignment(waCenter); + m_rt.InitTexture ("ui\\ui_enemy_frame");m_rt.SetWndRect(r);m_rt.SetAlignment(waCenter); + m_rb.InitTexture ("ui\\ui_enemy_frame");m_rb.SetWndRect(r);m_rb.SetAlignment(waCenter); + + m_lt.SetTextureRect (Frect().set(0, 0, RECT_SIZE, RECT_SIZE) ); + m_lb.SetTextureRect (Frect().set(0, 32-RECT_SIZE, RECT_SIZE, 32) ); + m_rt.SetTextureRect (Frect().set(32-RECT_SIZE, 0, 32, RECT_SIZE) ); + m_rb.SetTextureRect (Frect().set(32-RECT_SIZE, 32-RECT_SIZE, 32, 32) ); + + + u32 clr = subst_alpha(color,128); + m_lt.SetTextureColor (clr); + m_lb.SetTextureColor (clr); + m_rt.SetTextureColor (clr); + m_rb.SetTextureColor (clr); + + cur_rect.set (0,0, UI_BASE_WIDTH,UI_BASE_HEIGHT); + + m_flags.zero (); +} + +void SBinocVisibleObj::Draw() +{ + if(m_flags.test(flVisObjNotValid)) return; + + m_lt.Draw (); + m_lb.Draw (); + m_rt.Draw (); + m_rb.Draw (); +} + +void SBinocVisibleObj::Update() +{ + m_flags.set ( flVisObjNotValid,TRUE); + + if(!m_object->Visual()) return; + + Fbox b = m_object->Visual()->getVisData().box; + + Fmatrix xform; + xform.mul (Device.mFullTransform,m_object->XFORM()); + Fvector2 mn ={flt_max,flt_max},mx={flt_min,flt_min}; + + for (u32 k=0; k<8; ++k){ + Fvector p; + b.getpoint (k,p); + xform.transform (p); + mn.x = _min(mn.x,p.x); + mn.y = _min(mn.y,p.y); + mx.x = _max(mx.x,p.x); + mx.y = _max(mx.y,p.y); + } + static Frect screen_rect={-1.0f, -1.0f, 1.0f, 1.0f}; + + Frect new_rect; + new_rect.lt = mn; + new_rect.rb = mx; + + if( FALSE == screen_rect.intersected(new_rect) ) return; + if( new_rect.in(screen_rect.lt) && new_rect.in(screen_rect.rb) ) return; + + std::swap (mn.y,mx.y); + mn.x = (1.f + mn.x)/2.f * UI_BASE_WIDTH; + mx.x = (1.f + mx.x)/2.f * UI_BASE_WIDTH; + mn.y = (1.f - mn.y)/2.f * UI_BASE_HEIGHT; + mx.y = (1.f - mx.y)/2.f * UI_BASE_HEIGHT; + + if(mx.x-mn.x (Level().CurrentViewEntity()); + } + } + if (pActor) + { + //----------------------------------------------------- + + CInventoryOwner* our_inv_owner = smart_cast(pActor); + CInventoryOwner* others_inv_owner = smart_cast(m_object); + CBaseMonster *monster = smart_cast(m_object); + + if(our_inv_owner && others_inv_owner && !monster){ + if (IsGameTypeSingle()) + { + switch(RELATION_REGISTRY().GetRelationType(others_inv_owner, our_inv_owner)) + { + case ALife::eRelationTypeEnemy: + clr = C_ON_ENEMY; break; + case ALife::eRelationTypeNeutral: + clr = C_ON_NEUTRAL; break; + case ALife::eRelationTypeFriend: + clr = C_ON_FRIEND; break; + } + } + else + { + CEntityAlive* our_ealive = smart_cast(pActor); + CEntityAlive* others_ealive = smart_cast(m_object); + if (our_ealive && others_ealive) + { + if (Game().IsEnemy(our_ealive, others_ealive)) + clr = C_ON_ENEMY; + else + clr = C_ON_FRIEND; + } + } + } + } + + m_lt.SetTextureColor (clr); + m_lb.SetTextureColor (clr); + m_rt.SetTextureColor (clr); + m_rb.SetTextureColor (clr); + } + } + + m_lt.SetWndPos ( Fvector2().set((cur_rect.lt.x), (cur_rect.lt.y)) ); + m_lb.SetWndPos ( Fvector2().set((cur_rect.lt.x), (cur_rect.rb.y)) ); + m_rt.SetWndPos ( Fvector2().set((cur_rect.rb.x), (cur_rect.lt.y)) ); + m_rb.SetWndPos ( Fvector2().set((cur_rect.rb.x), (cur_rect.rb.y)) ); + + m_flags.set (flVisObjNotValid, FALSE); +} + + +CBinocularsVision::CBinocularsVision(const shared_str& sect) +{ + Load (sect); +} + +CBinocularsVision::~CBinocularsVision() +{ + delete_data (m_active_objects); +} + +void CBinocularsVision::Update() +{ + if (g_dedicated_server) + return; + //----------------------------------------------------- + const CActor* pActor = NULL; + if (IsGameTypeSingle()) pActor = Actor(); + else + { + if (Level().CurrentViewEntity()) + { + pActor = smart_cast (Level().CurrentViewEntity()); + } + } + if (!pActor) return; + //----------------------------------------------------- + const CVisualMemoryManager::VISIBLES& vVisibles = pActor->memory().visual().objects(); + + VIS_OBJECTS_IT it = m_active_objects.begin(); + for(;it!=m_active_objects.end();++it) + (*it)->m_flags.set (flVisObjNotValid, TRUE) ; + + + CVisualMemoryManager::VISIBLES::const_iterator v_it = vVisibles.begin(); + for (; v_it!=vVisibles.end(); ++v_it) + { + const CObject* _object_ = (*v_it).m_object; + if (!pActor->memory().visual().visible_now(smart_cast(_object_))) + continue; + + CObject* object_ = const_cast(_object_); + + + CEntityAlive* EA = smart_cast(object_); + if(!EA || !EA->g_Alive()) continue; + + + FindVisObjByObject f (object_); + VIS_OBJECTS_IT found; + found = std::find_if (m_active_objects.begin(),m_active_objects.end(),f); + + if( found != m_active_objects.end() ){ + (*found)->m_flags.set (flVisObjNotValid,FALSE); + }else{ + m_active_objects.push_back (new SBinocVisibleObj() ); + SBinocVisibleObj* new_vis_obj = m_active_objects.back(); + new_vis_obj->m_flags.set (flVisObjNotValid,FALSE); + new_vis_obj->m_object = object_; + new_vis_obj->create_default (m_frame_color.get()); + new_vis_obj->m_upd_speed = m_rotating_speed; + + m_sounds.PlaySound ("found_snd", Fvector().set(0,0,0), NULL, true); + } + } + std::sort (m_active_objects.begin(), m_active_objects.end()); + + while(m_active_objects.size() && m_active_objects.back()->m_flags.test(flVisObjNotValid)){ + xr_delete (m_active_objects.back()); + m_active_objects.pop_back (); + } + + it = m_active_objects.begin(); + for(;it!=m_active_objects.end();++it) + { + SBinocVisibleObj* visObj = (*it); + + BOOL bLocked = visObj->m_flags.test(flTargetLocked); + + (*it)->Update (); + + if(bLocked != visObj->m_flags.test(flTargetLocked)) + m_sounds.PlaySound ("catch_snd", Fvector().set(0,0,0), NULL, true); + } + +} + +void CBinocularsVision::Draw() +{ + VIS_OBJECTS_IT it = m_active_objects.begin(); + for(;it!=m_active_objects.end();++it) + (*it)->Draw (); +} + +void CBinocularsVision::Load(const shared_str& section) +{ + m_rotating_speed = pSettings->r_float(section,"vis_frame_speed"); + m_frame_color = pSettings->r_fcolor(section,"vis_frame_color"); + m_sounds.LoadSound (section.c_str(),"found_snd", "found_snd", false, SOUND_TYPE_NO_SOUND); + m_sounds.LoadSound (section.c_str(),"catch_snd", "catch_snd", false, SOUND_TYPE_NO_SOUND); +} + +void CBinocularsVision::remove_links(CObject *object) +{ + VIS_OBJECTS::iterator I = std::find_if(m_active_objects.begin(),m_active_objects.end(),FindVisObjByObject(object)); + if (I == m_active_objects.end()) + return; + + m_active_objects.erase (I); +} diff --git a/src/xrGameLA/WeaponBinocularsVision.h b/src/xrGameLA/WeaponBinocularsVision.h new file mode 100644 index 000000000..72a65895d --- /dev/null +++ b/src/xrGameLA/WeaponBinocularsVision.h @@ -0,0 +1,46 @@ +#pragma once +#include "ui\uistatic.h" +#include "HudSound.h" + +class CObject; + + +enum{ + flVisObjNotValid =(1<<0), + flTargetLocked =(1<<1), +}; +struct SBinocVisibleObj{ + SBinocVisibleObj () {}; + CObject* m_object; + CUIStatic m_lt; + CUIStatic m_lb; + CUIStatic m_rt; + CUIStatic m_rb; + Frect cur_rect; + + float m_upd_speed; + Flags8 m_flags; + void create_default (u32 color); + void Draw (); + void Update (); + bool operator < (const SBinocVisibleObj& other) const{ return m_flags.test(flVisObjNotValid) < other.m_flags.test(flVisObjNotValid);} //move non-actual to tail +}; + +class CBinocularsVision +{ + typedef xr_vector VIS_OBJECTS; + typedef VIS_OBJECTS::iterator VIS_OBJECTS_IT; + VIS_OBJECTS m_active_objects; +public: + CBinocularsVision (const shared_str& sect); + ~CBinocularsVision (); + void Update (); + void Draw (); + void remove_links (CObject *object); + +protected : + Fcolor m_frame_color; + float m_rotating_speed; + void Load (const shared_str& section); + HUD_SOUND_COLLECTION m_sounds; +}; \ No newline at end of file diff --git a/src/xrGameLA/WeaponBinoculars_script.cpp b/src/xrGameLA/WeaponBinoculars_script.cpp new file mode 100644 index 000000000..e1f637ea4 --- /dev/null +++ b/src/xrGameLA/WeaponBinoculars_script.cpp @@ -0,0 +1,13 @@ +#include "pch_script.h" +#include "weaponbinoculars.h" + +using namespace luabind; + +void CWeaponBinoculars::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponBinoculars") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponCustomPistol.cpp b/src/xrGameLA/WeaponCustomPistol.cpp new file mode 100644 index 000000000..21bd21037 --- /dev/null +++ b/src/xrGameLA/WeaponCustomPistol.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" + +#include "Entity.h" +#include "WeaponCustomPistol.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +CWeaponCustomPistol::CWeaponCustomPistol(LPCSTR name) : CWeaponMagazined(name,SOUND_TYPE_WEAPON_PISTOL) +{ +} + +CWeaponCustomPistol::~CWeaponCustomPistol() +{ +} + + + \ No newline at end of file diff --git a/src/xrGameLA/WeaponCustomPistol.h b/src/xrGameLA/WeaponCustomPistol.h new file mode 100644 index 000000000..0af3739da --- /dev/null +++ b/src/xrGameLA/WeaponCustomPistol.h @@ -0,0 +1,15 @@ +#pragma once + +#include "WeaponMagazined.h" + +#define SND_RIC_COUNT 5 + +class CWeaponCustomPistol: public CWeaponMagazined +{ +private: + typedef CWeaponMagazined inherited; +public: + CWeaponCustomPistol (LPCSTR name); + virtual ~CWeaponCustomPistol(); +protected: +}; diff --git a/src/xrGameLA/WeaponDispersion.cpp b/src/xrGameLA/WeaponDispersion.cpp new file mode 100644 index 000000000..564bfaae3 --- /dev/null +++ b/src/xrGameLA/WeaponDispersion.cpp @@ -0,0 +1,89 @@ +// WeaponDispersion.cpp: разбос при стрельбе +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" + +#include "weapon.h" +#include "inventoryowner.h" +#include "actor.h" +#include "inventory_item_impl.h" + +#include "actoreffector.h" +#include "effectorshot.h" +#include "EffectorShotX.h" + + +//возвращает 1, если оружие в отличном состоянии и >1 если повреждено +float CWeapon::GetConditionDispersionFactor() const +{ + return (1.f + fireDispersionConditionFactor*(1.f-GetCondition())); +} + +float CWeapon::GetFireDispersion (bool with_cartridge) +{ + if (!with_cartridge) return GetFireDispersion(1.0f); + if (!m_magazine.empty()) m_fCurrentCartirdgeDisp = m_magazine.back().param_s.kDisp; + return GetFireDispersion (m_fCurrentCartirdgeDisp); +} + +// Разброс оружия зажатого в тисках с учетом глушителя, патрона и износа +float CWeapon::GetFireDispersion(float cartridge_k) +{ + return fireDispersionBase * cur_silencer_koef.fire_dispersion * cartridge_k * GetConditionDispersionFactor(); +} + +////////////////////////////////////////////////////////////////////////// +// Для эффекта отдачи оружия +void CWeapon::AddShotEffector () +{ + inventory_owner().on_weapon_shot_start (this); +/** + CActor* pActor = smart_cast(H_Parent()); + if(pActor){ + CCameraShotEffector* S = smart_cast (pActor->EffectorManager().GetEffector(eCEShot)); + if (!S) S = (CCameraShotEffector*)pActor->EffectorManager().AddEffector(xr_new (camMaxAngleVert,camRelaxSpeed, camMaxAngleHorz, camStepAngleHorz, camDispertionFrac)); + R_ASSERT (S); + S->SetRndSeed(pActor->GetShotRndSeed()); + S->SetActor(pActor); + S->Shot (camDispersion+camDispersionInc*float(ShotsFired())); + } +/**/ +} + +void CWeapon::RemoveShotEffector () +{ + CInventoryOwner* pInventoryOwner = smart_cast(H_Parent()); + if (pInventoryOwner) + pInventoryOwner->on_weapon_shot_stop (this); +} + +void CWeapon::ClearShotEffector () +{ + CInventoryOwner* pInventoryOwner = smart_cast(H_Parent()); + if (pInventoryOwner) + pInventoryOwner->on_weapon_hide (this); + +}; + +/** +const Fvector& CWeapon::GetRecoilDeltaAngle() +{ + CActor* pActor = smart_cast(H_Parent()); + + CCameraShotEffector* S = NULL; + if(pActor) + S = smart_cast(pActor->EffectorManager().GetEffector(eCEShot)); + + if(S) + { + S->GetDeltaAngle(m_vRecoilDeltaAngle); + return m_vRecoilDeltaAngle; + } + else + { + m_vRecoilDeltaAngle.set(0.f,0.f,0.f); + return m_vRecoilDeltaAngle; + } +} +/**/ \ No newline at end of file diff --git a/src/xrGameLA/WeaponFN2000.cpp b/src/xrGameLA/WeaponFN2000.cpp new file mode 100644 index 000000000..cb9209121 --- /dev/null +++ b/src/xrGameLA/WeaponFN2000.cpp @@ -0,0 +1,25 @@ +#include "pch_script.h" +#include "WeaponFN2000.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +CWeaponFN2000::CWeaponFN2000() : CWeaponMagazined("FN2000",SOUND_TYPE_WEAPON_SNIPERRIFLE) +{} + +CWeaponFN2000::~CWeaponFN2000() +{ +} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponFN2000::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponFN2000") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponFN2000.h b/src/xrGameLA/WeaponFN2000.h new file mode 100644 index 000000000..e9a3d1730 --- /dev/null +++ b/src/xrGameLA/WeaponFN2000.h @@ -0,0 +1,28 @@ +#ifndef __XR_WEAPON_FN2000_H__ +#define __XR_WEAPON_FN2000_H__ + +#pragma once + +#include "WeaponMagazined.h" +#include "script_export_space.h" + +class CWeaponFN2000: public CWeaponMagazined +{ +private: + typedef CWeaponMagazined inherited; +public: + CWeaponFN2000 (); + virtual ~CWeaponFN2000 (); +/* virtual void Load (LPCSTR section); + virtual void Fire2Start (); + virtual void Fire2End (); + + virtual bool Action(u16 cmd, u32 flags);*/ + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponFN2000) +#undef script_type_list +#define script_type_list save_type_list(CWeaponFN2000) + +#endif //__XR_WEAPON_FN2000_H__ diff --git a/src/xrGameLA/WeaponFORT.cpp b/src/xrGameLA/WeaponFORT.cpp new file mode 100644 index 000000000..8d85b626c --- /dev/null +++ b/src/xrGameLA/WeaponFORT.cpp @@ -0,0 +1,22 @@ +#include "pch_script.h" +#include "WeaponFORT.h" + +CWeaponFORT::CWeaponFORT() : CWeaponPistol("FORT") +{ +} + +CWeaponFORT::~CWeaponFORT() +{ +} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponFORT::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponFORT") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponFORT.h b/src/xrGameLA/WeaponFORT.h new file mode 100644 index 000000000..bafd94e11 --- /dev/null +++ b/src/xrGameLA/WeaponFORT.h @@ -0,0 +1,26 @@ +#ifndef __XR_WEAPON_FORT_H__ +#define __XR_WEAPON_FORT_H__ + +#pragma once + +#include "WeaponPistol.h" +#include "script_export_space.h" + +#define SND_RIC_COUNT 5 + +class CWeaponFORT: public CWeaponPistol +{ +private: + typedef CWeaponPistol inherited; +protected: +public: + CWeaponFORT (); + virtual ~CWeaponFORT (); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponFORT) +#undef script_type_list +#define script_type_list save_type_list(CWeaponFORT) + +#endif //__XR_WEAPON_FORT_H__ \ No newline at end of file diff --git a/src/xrGameLA/WeaponFire.cpp b/src/xrGameLA/WeaponFire.cpp new file mode 100644 index 000000000..193e39aec --- /dev/null +++ b/src/xrGameLA/WeaponFire.cpp @@ -0,0 +1,151 @@ +// WeaponFire.cpp: implementation of the CWeapon class. +// function responsible for firing with CWeapon +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "Weapon.h" +#include "player_hud.h" +#include "ParticlesObject.h" +#include "HUDManager.h" +#include "entity.h" +#include "actor.h" + +#include "actoreffector.h" +#include "effectorshot.h" + +#include "level_bullet_manager.h" + +#define FLAME_TIME 0.05f + + +float _nrand(float sigma) +{ +#define ONE_OVER_SIGMA_EXP (1.0f / 0.7975f) + + if(sigma == 0) return 0; + + float y; + do{ + y = -logf(Random.randF()); + }while(Random.randF() > expf(-_sqr(y - 1.0f)*0.5f)); + if(rand() & 0x1) return y * sigma * ONE_OVER_SIGMA_EXP; + else return -y * sigma * ONE_OVER_SIGMA_EXP; +} + +void random_dir(Fvector& tgt_dir, const Fvector& src_dir, float dispersion) +{ + float sigma = dispersion/3.f; + float alpha = clampr (_nrand(sigma),-dispersion,dispersion); + float theta = Random.randF (0,PI); + float r = tan (alpha); + Fvector U,V,T; + Fvector::generate_orthonormal_basis (src_dir,U,V); + U.mul (r*_sin(theta)); + V.mul (r*_cos(theta)); + T.add (U,V); + tgt_dir.add (src_dir,T).normalize(); +} + +float CWeapon::GetWeaponDeterioration () +{ + return conditionDecreasePerShot; +}; + +void CWeapon::FireTrace (const Fvector& P, const Fvector& D) +{ + VERIFY (m_magazine.size()); + + CCartridge &l_cartridge = m_magazine.back(); +// Msg("ammo - %s", l_cartridge.m_ammoSect.c_str()); + VERIFY (u16(-1) != l_cartridge.bullet_material_idx); + //------------------------------------------------------------- + bool is_tracer = m_bHasTracers && !!l_cartridge.m_flags.test(CCartridge::cfTracer); + if ( is_tracer && !IsGameTypeSingle() ) + is_tracer = is_tracer /*&& (m_magazine.size() % 3 == 0)*/ && !IsSilencerAttached(); + + l_cartridge.m_flags.set (CCartridge::cfTracer, is_tracer ); + if (m_u8TracerColorID != u8(-1)) + l_cartridge.param_s.u8ColorID = m_u8TracerColorID; + //------------------------------------------------------------- + //повысить изношенность оружия с учетом влияния конкретного патрона +// float Deterioration = GetWeaponDeterioration(); +// Msg("Deterioration = %f", Deterioration); + ChangeCondition(-GetWeaponDeterioration()*l_cartridge.param_s.impair); + + // Разброс самого ствола + float barrel_disp = GetFireDispersion(true); + // Разброс вносимый стрелком + const CInventoryOwner* pOwner = smart_cast(H_Parent()); + float shooter_disp = pOwner->GetWeaponAccuracy(); + + bool SendHit = SendHitAllowed(H_Parent()); + Fvector3 shootDir; + random_dir(shootDir, D, shooter_disp); + //выстрелить пулю (с учетом возможной стрельбы дробью) + for(int i = 0; i < l_cartridge.param_s.buckShot; ++i) + { + FireBullet(P, shootDir, barrel_disp, l_cartridge, H_Parent()->ID(), ID(), SendHit); + } + + StartShotParticles (); + + if(m_bLightShotEnabled) + Light_Start (); + + + // Ammo + m_magazine.pop_back (); + --iAmmoElapsed; + + //проверить не произошла ли осечка + CheckForMisfire(); + + VERIFY((u32)iAmmoElapsed == m_magazine.size()); +} + +void CWeapon::Fire2Start() +{ + bWorking2=true; +} +void CWeapon::Fire2End () +{ + //принудительно останавливать зацикленные партиклы + if(m_pFlameParticles2 && m_pFlameParticles2->IsLooped()) + StopFlameParticles2 (); + + bWorking2=false; +} + +void CWeapon::StopShooting () +{ + SetPending (TRUE); + + //принудительно останавливать зацикленные партиклы + if(m_pFlameParticles && m_pFlameParticles->IsLooped()) + StopFlameParticles (); + + SwitchState(eIdle); + + bWorking = false; + //if(IsWorking()) FireEnd(); +} + +void CWeapon::FireEnd () +{ + CShootingObject::FireEnd(); + ClearShotEffector(); +} + + +void CWeapon::StartFlameParticles2 () +{ + CShootingObject::StartParticles (m_pFlameParticles2, *m_sFlameParticles2, get_LastFP2()); +} +void CWeapon::StopFlameParticles2 () +{ + CShootingObject::StopParticles (m_pFlameParticles2); +} +void CWeapon::UpdateFlameParticles2 () +{ + if (m_pFlameParticles2) CShootingObject::UpdateParticles (m_pFlameParticles2, get_LastFP2()); +} diff --git a/src/xrGameLA/WeaponGroza.cpp b/src/xrGameLA/WeaponGroza.cpp new file mode 100644 index 000000000..987e14f9f --- /dev/null +++ b/src/xrGameLA/WeaponGroza.cpp @@ -0,0 +1,22 @@ +#include "pch_script.h" +#include "weapongroza.h" +#include "player_hud.h" + +CWeaponGroza::CWeaponGroza() : CWeaponMagazinedWGrenade("GROZA",SOUND_TYPE_WEAPON_SUBMACHINEGUN) +{} + +CWeaponGroza::~CWeaponGroza() +{ +} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponGroza::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponGroza") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponGroza.h b/src/xrGameLA/WeaponGroza.h new file mode 100644 index 000000000..80946fb5e --- /dev/null +++ b/src/xrGameLA/WeaponGroza.h @@ -0,0 +1,18 @@ +#pragma once + +#include "weaponmagazinedwgrenade.h" +#include "script_export_space.h" + +class CWeaponGroza : + public CWeaponMagazinedWGrenade +{ + typedef CWeaponMagazinedWGrenade inherited; +public: + CWeaponGroza(); + virtual ~CWeaponGroza(); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponGroza) +#undef script_type_list +#define script_type_list save_type_list(CWeaponGroza) diff --git a/src/xrGameLA/WeaponHPSA.cpp b/src/xrGameLA/WeaponHPSA.cpp new file mode 100644 index 000000000..c03c51f33 --- /dev/null +++ b/src/xrGameLA/WeaponHPSA.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" + +#include "WeaponHPSA.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +CWeaponHPSA::CWeaponHPSA() : CWeaponPistol("HPSA") +{ +} + +CWeaponHPSA::~CWeaponHPSA() +{ +} diff --git a/src/xrGameLA/WeaponHPSA.h b/src/xrGameLA/WeaponHPSA.h new file mode 100644 index 000000000..8943043f4 --- /dev/null +++ b/src/xrGameLA/WeaponHPSA.h @@ -0,0 +1,24 @@ +#ifndef __XR_WEAPON_HPSA_H__ +#define __XR_WEAPON_HPSA_H__ + +#pragma once + +#include "WeaponPistol.h" +#include "script_export_space.h" + +class CWeaponHPSA: public CWeaponPistol +{ +private: + typedef CWeaponPistol inherited; +protected: +public: + CWeaponHPSA (); + virtual ~CWeaponHPSA (); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponHPSA) +#undef script_type_list +#define script_type_list save_type_list(CWeaponHPSA) + +#endif //__XR_WEAPON_HPSA_H__ diff --git a/src/xrGameLA/WeaponHPSA_script.cpp b/src/xrGameLA/WeaponHPSA_script.cpp new file mode 100644 index 000000000..af3942b92 --- /dev/null +++ b/src/xrGameLA/WeaponHPSA_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "WeaponHPSA.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponHPSA::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponHPSA") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponHUD.cpp b/src/xrGameLA/WeaponHUD.cpp new file mode 100644 index 000000000..12f1b8d9d --- /dev/null +++ b/src/xrGameLA/WeaponHUD.cpp @@ -0,0 +1,241 @@ +// WeaponHUD.cpp: HUD для оружия и прочих предметов, которые +// могут держать в руках персонажи, также используется +// для синхронизации анимаций с видом от 3-го лица +////////////////////////////////////////////////////////////////////// +#include "stdafx.h" +#include "WeaponHUD.h" +#include "Weapon.h" +#include "actor.h" +#include "../Motion.h" +#include "../Include/xrRender/Kinematics.h" +#include "level.h" +#include "MathUtils.h" +#include "hudmanager.h" +weapon_hud_container* g_pWeaponHUDContainer=0; + +BOOL weapon_hud_value::load(const shared_str& section, CHudItem* owner) +{ + // Geometry and transform + Fvector pos,ypr; + pos = pSettings->r_fvector3(section,"position"); + ypr = pSettings->r_fvector3(section,"orientation"); + ypr.mul (PI/180.f); + + m_offset.setHPB (ypr.x,ypr.y,ypr.z); + m_offset.translate_over (pos); + + // Visual + LPCSTR visual_name = pSettings->r_string(section, "visual"); + IRenderVisual *pV = ::Render->model_Create(visual_name); + m_animations = smart_cast(pV); + IKinematics *pK = smart_cast(pV); + + // fire bone + if(smart_cast(owner)){ + LPCSTR fire_bone = pSettings->r_string (section,"fire_bone"); + m_fire_bone = pK->LL_BoneID (fire_bone); + if (m_fire_bone>=pK->LL_BoneCount()) + Debug.fatal (DEBUG_INFO,"There is no '%s' bone for weapon '%s'.",fire_bone, *section); + m_fp_offset = pSettings->r_fvector3 (section,"fire_point"); + if(pSettings->line_exist(section,"fire_point2")) + m_fp2_offset = pSettings->r_fvector3 (section,"fire_point2"); + else + m_fp2_offset = m_fp_offset; + if(pSettings->line_exist(owner->object().cNameSect(), "shell_particles")) + m_sp_offset = pSettings->r_fvector3 (section,"shell_point"); + else + m_sp_offset.set (0,0,0); + }else{ + m_fire_bone = -1; + m_fp_offset.set (0,0,0); + m_fp2_offset.set (0,0,0); + m_sp_offset.set (0,0,0); + } + return TRUE; +} + +weapon_hud_value::~weapon_hud_value() +{ + //::Render->model_Delete (m_animations); + IRenderVisual *pVisual = smart_cast(m_animations); + ::Render->model_Delete (pVisual); + // model_Delete clears the pointer + m_animations = 0; +} + +u32 shared_weapon_hud::motion_length(MotionID M) +{ + IKinematicsAnimated *skeleton_animated = p_->m_animations; + VERIFY (skeleton_animated); + CMotionDef *motion_def = skeleton_animated->LL_GetMotionDef(M); + VERIFY (motion_def); + + if (motion_def->flags & esmStopAtEnd) { + CMotion* motion = skeleton_animated->LL_GetRootMotion(M); + return iFloor(0.5f + 1000.f*motion->GetLength()/ motion_def->Dequantize(motion_def->speed)); + } + return 0; +} + +MotionID shared_weapon_hud::motion_id(LPCSTR name) +{ + return p_->m_animations->ID_Cycle_Safe(name); +} + +CWeaponHUD::CWeaponHUD (CHudItem* pHudItem) +{ + m_bVisible = false; + m_bCollideHud = true; + m_pParentWeapon = pHudItem; + m_bHidden = true; + m_bStopAtEndAnimIsRunning = false; + m_pCallbackItem = NULL; + m_Transform.identity (); +#ifdef WPN_BOBBING + m_bobbing = new CWeaponBobbing(); +#endif + m_collision = new CWeaponCollision(); +} + +CWeaponHUD::~CWeaponHUD() +{ +#ifdef WPN_BOBBING + xr_delete(m_bobbing); +#endif + xr_delete(m_collision); +} + +void CWeaponHUD::Load(LPCSTR section) +{ + m_shared_data.create (section,m_pParentWeapon); + + //SkyLoader: collision of hud + if(pSettings->line_exist(section, "collide_hud")) + m_bCollideHud = !!pSettings->r_bool(section,"collide_hud"); +} + +void CWeaponHUD::Init() +{ + m_bStopAtEndAnimIsRunning = false; + m_pCallbackItem = NULL; +} + + +void CWeaponHUD::net_DestroyHud() +{ + m_bStopAtEndAnimIsRunning = false; + m_pCallbackItem = NULL; + Visible (false); +} + +void CWeaponHUD::UpdatePosition(const Fmatrix& trans) +{ + Fmatrix xform = trans; +#ifdef WPN_BOBBING + ApplyBobbing(xform); +#endif + + Fmatrix offset = m_shared_data.get_value()->m_offset; + collide::rq_result& RQ = HUD().GetCurrentRayQuery(); + CActor* pActor = smart_cast(m_pParentWeapon->object().H_Parent()); + if (m_bCollideHud && pActor) + m_collision->Update(offset, RQ.range, pActor->IsZoomAimingMode()); + + m_Transform.mul (xform,offset); + VERIFY (!fis_zero(DET(m_Transform))); +} + +MotionID CWeaponHUD::animGet(LPCSTR name) +{ + return m_shared_data.motion_id (name); +} + +void CWeaponHUD::animDisplay(MotionID M, BOOL bMixIn) +{ + if(m_bVisible){ + IRenderVisual *pV = Visual(); + IKinematicsAnimated* PKinematicsAnimated = smart_cast(pV); + IKinematics* pK = smart_cast(pV); + VERIFY (PKinematicsAnimated); + PKinematicsAnimated->PlayCycle (M,bMixIn); + pK->CalculateBones_Invalidate (); + } +} +void CWeaponHUD::animPlay (MotionID M, BOOL bMixIn, CHudItem* W, u32 state) +{ +//. if(m_bStopAtEndAnimIsRunning) +//. StopCurrentAnim (); + + + m_startedAnimState = state; + Show (); + animDisplay (M, bMixIn); + u32 anim_time = m_shared_data.motion_length(M); + if (anim_time>0){ + m_bStopAtEndAnimIsRunning = true; + m_pCallbackItem = W; + if (m_pCallbackItem) + m_pCallbackItem->OnAnimationStart(state, anim_time); + m_dwAnimEndTime = Device.dwTimeGlobal + anim_time; + }else{ + m_pCallbackItem = NULL; + } +// Msg("%d:animPlay %d state %d start %d end %d for %s", Device.dwTimeGlobal, M.idx, state, anim_time, m_dwAnimEndTime, m_pParentWeapon->object().cName().c_str()); +} + +void CWeaponHUD::Update () +{ + if(m_bStopAtEndAnimIsRunning && Device.dwTimeGlobal > m_dwAnimEndTime) + StopCurrentAnim (); + if(m_bVisible) + smart_cast(Visual())->UpdateTracks (); +} + +void CWeaponHUD::StopCurrentAnim() +{ + m_dwAnimEndTime = 0; + m_bStopAtEndAnimIsRunning = false; + if(m_pCallbackItem) + { + m_pCallbackItem->OnAnimationEnd (m_startedAnimState); + //Msg("CWeaponHUD::StopCurrentAnim OnAnimationEnd(%d)[%s]", m_startedAnimState, m_pParentWeapon->object().cName().c_str()); + } +} + +void CWeaponHUD::StopCurrentAnimWithoutCallback () +{ + m_dwAnimEndTime = 0; + m_bStopAtEndAnimIsRunning = false; + + m_pCallbackItem = NULL; +} + +void CWeaponHUD::CreateSharedContainer () +{ + VERIFY(0==g_pWeaponHUDContainer); + g_pWeaponHUDContainer = new weapon_hud_container(); +} +void CWeaponHUD::DestroySharedContainer () +{ + xr_delete (g_pWeaponHUDContainer); +} +void CWeaponHUD::CleanSharedContainer () +{ + VERIFY(g_pWeaponHUDContainer); + g_pWeaponHUDContainer->clean(false); +} + +MotionID random_anim(MotionSVec& v) +{ + return v[Random.randI(v.size())]; +} + +#ifdef WPN_BOBBING +extern Flags32 psActorFlags; +void CWeaponHUD::ApplyBobbing(Fmatrix &m) +{ + if (psActorFlags.test(AF_WPN_BOBBING)) + m_bobbing->Update(m); +} + +#endif \ No newline at end of file diff --git a/src/xrGameLA/WeaponHUD.h b/src/xrGameLA/WeaponHUD.h new file mode 100644 index 000000000..0c03a1c9b --- /dev/null +++ b/src/xrGameLA/WeaponHUD.h @@ -0,0 +1,148 @@ +#pragma once +#define WPN_BOBBING // comment out this line to exclude the feature +#include "../../Include/xrRender/KinematicsAnimated.h" +class CHudItem; +#ifdef WPN_BOBBING +#include "weapon_bobbing.h" +#endif + +#include "weapon_collision.h" + +struct weapon_hud_value: public shared_value +{ + //IKinematicsAnimated* m_animations; + IKinematicsAnimated* m_animations; +public: + int m_fire_bone; + Fvector m_fp_offset; + Fvector m_fp2_offset; + Fvector m_sp_offset; + + Fmatrix m_offset; +public: + virtual ~weapon_hud_value (); + BOOL load (const shared_str& section, CHudItem* owner); +}; + +typedef shared_container weapon_hud_container; +extern weapon_hud_container* g_pWeaponHUDContainer; + +class shared_weapon_hud: public shared_item +{ +protected: + struct on_new_pred{ + CHudItem* owner; + on_new_pred (CHudItem* _owner):owner(_owner){} + BOOL operator() (const shared_str& key, weapon_hud_value* val) const {return val->load(key,owner);} + }; +public: + void create (shared_str key, CHudItem* owner) + { + shared_item::create (key,g_pWeaponHUDContainer,on_new_pred(owner)); + } + //IKinematicsAnimated* animations (){return p_->m_animations;} + IKinematicsAnimated* animations (){return p_->m_animations;} + u32 motion_length (MotionID M); + MotionID motion_id (LPCSTR name); +}; +//--------------------------------------------------------------------------- + +class CWeaponHUD +{ + //родительский объект HUD + CHudItem* m_pParentWeapon; + //флаг, если hud спрятан не показывается + bool m_bHidden; + bool m_bVisible; + //if need to collide weapon hud + bool m_bCollideHud; + + Fmatrix m_Transform; + + //shared HUD data + shared_weapon_hud m_shared_data; + + //таймеры для проигрывания анимаций + u32 m_dwAnimTime; + u32 m_dwAnimEndTime; + bool m_bStopAtEndAnimIsRunning; + u32 m_startedAnimState; +// CInventoryItem* m_pCallbackItem; + CHudItem* m_pCallbackItem; + + //остановление таймера текущей анимации, и вызов callback + void StopCurrentAnim (); + + //поворот и смещение для режима приближения + float m_fZoomRotateX; + float m_fZoomRotateY; + Fvector m_fZoomOffset; +public: + CWeaponHUD (CHudItem* pHudItem); + ~CWeaponHUD (); + + // misc + void Load (LPCSTR section); + void net_DestroyHud (); + void Init (); + + IC IRenderVisual* Visual () { return m_shared_data.animations()->dcast_RenderVisual(); } + IC Fmatrix& Transform () { return m_Transform; } + + int FireBone () {return m_shared_data.get_value()->m_fire_bone; } + const Fvector& FirePoint () {return m_shared_data.get_value()->m_fp_offset; } + const Fvector& FirePoint2 () {return m_shared_data.get_value()->m_fp2_offset;} + const Fvector& ShellPoint () {return m_shared_data.get_value()->m_sp_offset; } + const Fmatrix& HudOffsetMatrix () {return m_shared_data.get_value()->m_offset;} + + const Fvector& ZoomOffset () const {return m_fZoomOffset;} + float ZoomRotateX () const {return m_fZoomRotateX;} + float ZoomRotateY () const {return m_fZoomRotateY;} + void SetZoomOffset (const Fvector& zoom_offset) { m_fZoomOffset = zoom_offset;} + void SetZoomRotateX (float zoom_rotate_x) { m_fZoomRotateX = zoom_rotate_x;} + void SetZoomRotateY (float zoom_rotate_y) { m_fZoomRotateY = zoom_rotate_y;} + + + // Animations + void animPlay (MotionID M, BOOL bMixIn/*=TRUE*/, CHudItem* W /*=0*/, u32 state); + void animDisplay (MotionID M, BOOL bMixIn); + MotionID animGet (LPCSTR name); + + void UpdatePosition (const Fmatrix& transform); + + bool IsHidden () {return m_bHidden;} + void Hide () {m_bHidden = true;} + void Show () {m_bHidden = false;} + + void Visible (bool val){m_bVisible=val;} + + //обновление HUD должно вызываться на каждом кадре + void Update (); + + void StopCurrentAnimWithoutCallback (); + +public: + static void CreateSharedContainer (); + static void DestroySharedContainer (); + static void CleanSharedContainer (); +//#ifdef DEBUG +public: + void dbg_SetFirePoint (const Fvector &fp) {((weapon_hud_value*)m_shared_data.get_value())->m_fp_offset.set(fp);} + void dbg_SetFirePoint2 (const Fvector &fp) {((weapon_hud_value*)m_shared_data.get_value())->m_fp2_offset.set(fp);} + void dbg_SetShellPoint (const Fvector &sp) {((weapon_hud_value*)m_shared_data.get_value())->m_sp_offset.set(sp);} + void SetHudOffsetMatrix (const Fmatrix &offset) {((weapon_hud_value*)m_shared_data.get_value())->m_offset.set(offset);} +//#endif + // lost alpha start +#ifdef WPN_BOBBING + private: + void ApplyBobbing(Fmatrix &m); + private: + CWeaponBobbing *m_bobbing; +#endif + private: + CWeaponCollision *m_collision; +}; + +#define MAX_ANIM_COUNT 8 +typedef svector MotionSVec; +MotionID random_anim (MotionSVec& v); diff --git a/src/xrGameLA/WeaponKnife.cpp b/src/xrGameLA/WeaponKnife.cpp new file mode 100644 index 000000000..30e710dc7 --- /dev/null +++ b/src/xrGameLA/WeaponKnife.cpp @@ -0,0 +1,348 @@ +#include "stdafx.h" + +#include "WeaponKnife.h" +#include "player_hud.h" +#include "Entity.h" +#include "Actor.h" +#include "level.h" +#include "xr_level_controller.h" +#include "game_cl_base.h" +#include "../Include/xrRender/Kinematics.h" +#include "../gamemtllib.h" +#include "level_bullet_manager.h" +#include "ai_sounds.h" +#include "game_cl_single.h" + +#define KNIFE_MATERIAL_NAME "objects\\knife" + +CWeaponKnife::CWeaponKnife() : CWeapon("KNIFE") +{ + m_attackStart = false; + SetState ( eHidden ); + SetNextState ( eHidden ); + knife_material_idx = (u16)-1; + bPlaySoundAtStart = false; + bHasShoot2Sound = false; +} +CWeaponKnife::~CWeaponKnife() +{ +} + +void CWeaponKnife::Load (LPCSTR section) +{ + // verify class + inherited::Load (section); + + fWallmarkSize = pSettings->r_float(section,"wm_size"); + + m_sounds.LoadSound(section,"snd_shoot" , "sndShot" , false, SOUND_TYPE_WEAPON_SHOOTING ); + if (pSettings->line_exist(section, "snd_shoot_2")) + { + m_sounds.LoadSound(section,"snd_shoot_2", "sndShot2" , false, SOUND_TYPE_WEAPON_SHOOTING ); + bHasShoot2Sound = true; + } + + knife_material_idx = GMLib.GetMaterialIdx(KNIFE_MATERIAL_NAME); + + bPlaySoundAtStart = !!READ_IF_EXISTS(pSettings, r_bool, section, "snd_shoot_play_at_start", FALSE); +} + +void CWeaponKnife::OnStateSwitch (u32 S) +{ + inherited::OnStateSwitch(S); + switch (S) + { + case eIdle: + switch2_Idle (); + break; + case eShowing: + switch2_Showing (); + break; + case eHiding: + switch2_Hiding (); + break; + case eHidden: + switch2_Hidden (); + break; + case eFire: + { + //------------------------------------------- + m_eHitType = m_eHitType_1; + //fHitPower = fHitPower_1; + if (ParentIsActor()) + { + if (GameID() == GAME_SINGLE) + { + fCurrentHit = fvHitPower_1[g_SingleGameDifficulty]; + } + else + { + fCurrentHit = fvHitPower_1[egdMaster]; + } + } + else + { + fCurrentHit = fvHitPower_1[egdMaster]; + } + fHitImpulse = fHitImpulse_1; + fCurrentAP = fAP_1; + //------------------------------------------- + switch2_Attacking (S); + }break; + case eFire2: + { + //------------------------------------------- + m_eHitType = m_eHitType_2; + //fHitPower = fHitPower_2; + if (ParentIsActor()) + { + if (GameID() == GAME_SINGLE) + { + fCurrentHit = fvHitPower_2[g_SingleGameDifficulty]; + } + else + { + fCurrentHit = fvHitPower_2[egdMaster]; + } + } + else + { + fCurrentHit = fvHitPower_2[egdMaster]; + } + fHitImpulse = fHitImpulse_2; + fCurrentAP = fAP_2; + //------------------------------------------- + switch2_Attacking (S); + }break; + } +} + + +void CWeaponKnife::KnifeStrike(const Fvector& pos, const Fvector& dir) +{ + CCartridge cartridge; + cartridge.param_s.buckShot = 1; + cartridge.param_s.impair = 1.0f; + cartridge.param_s.kDisp = 1.0f; + cartridge.param_s.kHit = 1.0f; + cartridge.param_s.kSpeed = 1.0f; +//. cartridge.param_s.kCritical = 1.0f; + cartridge.param_s.kImpulse = 1.0f; + cartridge.param_s.kAP = fCurrentAP; + cartridge.m_flags.set (CCartridge::cfTracer, FALSE); + cartridge.m_flags.set (CCartridge::cfRicochet, FALSE); + cartridge.param_s.fWallmarkSize = fWallmarkSize; + cartridge.bullet_material_idx = knife_material_idx; + + while(m_magazine.size() < 2) m_magazine.push_back(cartridge); + iAmmoElapsed = m_magazine.size(); + bool SendHit = SendHitAllowed(H_Parent()); + + if (!bPlaySoundAtStart) + { + PlayShootSound(); + } + + CActor* pActor = smart_cast(H_Parent()); + if (pActor && pActor->IsFirstEye()) + Level().BulletManager().AddBullet( pos, + dir, + m_fStartBulletSpeed, + fCurrentHit, + fHitImpulse, + H_Parent()->ID(), + ID(), + m_eHitType, + (fireDistance), + cartridge, + 1.f, + SendHit); + else + Level().BulletManager().AddBullet( pos, + dir, + m_fStartBulletSpeed, + fCurrentHit, + fHitImpulse, + H_Parent()->ID(), + ID(), + m_eHitType, + fireDistance+1.2f, + cartridge, + 1.f, + SendHit); +} + + +void CWeaponKnife::OnAnimationEnd(u32 state) +{ + switch (state) + { + case eHiding: SwitchState(eHidden); break; + case eFire: + case eFire2: + { + if(m_attackStart) + { + m_attackStart = false; + if(GetState()==eFire) + PlayHUDMotion("anim_shoot1_end", TRUE, this, GetState()); + else + PlayHUDMotion("anim_shoot2_end", TRUE, this, GetState()); + + Fvector p1, d; + p1.set(get_LastFP()); + d.set(get_LastFD()); + + if(H_Parent()) + smart_cast(H_Parent())->g_fireParams(this, p1,d); + else break; + + KnifeStrike(p1,d); + } + else + SwitchState(eIdle); + }break; + case eShowing: + case eIdle: + SwitchState(eIdle); break; + default: inherited::OnAnimationEnd(state); + } +} + +void CWeaponKnife::state_Attacking (float) +{ +} + +void CWeaponKnife::switch2_Attacking (u32 state) +{ + if(IsPending()) return; + + if(state==eFire) + PlayHUDMotion("anim_shoot1_start", FALSE, this, state); + else + PlayHUDMotion("anim_shoot2_start", FALSE, this, state); + + m_attackStart = true; + SetPending (TRUE); + + if (bPlaySoundAtStart) + { + PlayShootSound(); + } +} + +void CWeaponKnife::switch2_Idle () +{ + VERIFY(GetState()==eIdle); + + PlayAnimIdle (); + SetPending (FALSE); +} + +void CWeaponKnife::switch2_Hiding () +{ + FireEnd (); + VERIFY(GetState()==eHiding); + PlayHUDMotion("anim_hide", TRUE, this, GetState()); +} + +void CWeaponKnife::switch2_Hidden() +{ + signal_HideComplete (); + SetPending (FALSE); +} + +void CWeaponKnife::switch2_Showing () +{ + VERIFY(GetState()==eShowing); + PlayHUDMotion("anim_show", FALSE, this, GetState()); +} + + +void CWeaponKnife::FireStart() +{ + inherited::FireStart(); + SwitchState (eFire); +} + +void CWeaponKnife::Fire2Start () +{ + CActor* pActor = smart_cast(H_Parent()); + if(pActor) + { + CEntity::SEntityState st; + pActor->g_State(st); + if(!st.bSprint) + { + inherited::Fire2Start(); + SwitchState(eFire2); + } + } +} + + +bool CWeaponKnife::Action(u16 cmd, u32 flags) +{ + if(inherited::Action(cmd, flags)) return true; + switch(cmd) + { + + case kWPN_ZOOM : + if(flags&CMD_START) Fire2Start(); + else Fire2End(); + return true; + } + return false; +} + +void CWeaponKnife::LoadFireParams(LPCSTR section, LPCSTR prefix) +{ + inherited::LoadFireParams(section, prefix); + + string256 full_name; + string32 buffer; + shared_str s_sHitPower_2; + //fHitPower_1 = fHitPower; + fvHitPower_1 = fvHitPower; + fHitImpulse_1 = fHitImpulse; + m_eHitType_1 = ALife::g_tfString2HitType(pSettings->r_string(section, "hit_type")); + + //fHitPower_2 = pSettings->r_float (section,strconcat(full_name, prefix, "hit_power_2")); + s_sHitPower_2 = pSettings->r_string_wb (section,strconcat(sizeof(full_name),full_name, prefix, "hit_power_2")); + fvHitPower_2[egdMaster] = (float)atof(_GetItem(*s_sHitPower_2,0,buffer));//первый параметр - это хит для уровня игры мастер + + fvHitPower_2[egdVeteran] = fvHitPower_2[egdMaster];//изначально параметры для других уровней + fvHitPower_2[egdStalker] = fvHitPower_2[egdMaster];//сложности + fvHitPower_2[egdNovice] = fvHitPower_2[egdMaster];//такие же + + int num_game_diff_param=_GetItemCount(*s_sHitPower_2);//узнаём колличество параметров для хитов + if (num_game_diff_param>1)//если задан второй параметр хита + { + fvHitPower_2[egdVeteran] = (float)atof(_GetItem(*s_sHitPower_2,1,buffer));//то вычитываем его для уровня ветерана + } + if (num_game_diff_param>2)//если задан третий параметр хита + { + fvHitPower_2[egdStalker] = (float)atof(_GetItem(*s_sHitPower_2,2,buffer));//то вычитываем его для уровня сталкера + } + if (num_game_diff_param>3)//если задан четвёртый параметр хита + { + fvHitPower_2[egdNovice] = (float)atof(_GetItem(*s_sHitPower_2,3,buffer));//то вычитываем его для уровня новичка + } + + fHitImpulse_2 = pSettings->r_float (section,strconcat(sizeof(full_name),full_name, prefix, "hit_impulse_2")); + m_eHitType_2 = ALife::g_tfString2HitType(pSettings->r_string(section, "hit_type_2")); + fAP_1 = READ_IF_EXISTS(pSettings, r_float, section, "hit_ap", EPS_L); + fAP_2 = READ_IF_EXISTS(pSettings, r_float, section, "hit_ap_2", EPS_L); +} + +void CWeaponKnife::PlayShootSound() +{ + PlaySound((bHasShoot2Sound && GetState() == eFire2) ? "sndShot2" : "sndShot", get_LastFP()); +} + +void CWeaponKnife::GetBriefInfo(xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count) +{ + str_name = NameShort(); + str_count = ""; + icon_sect_name = *cNameSect(); +} diff --git a/src/xrGameLA/WeaponKnife.h b/src/xrGameLA/WeaponKnife.h new file mode 100644 index 000000000..1be174f13 --- /dev/null +++ b/src/xrGameLA/WeaponKnife.h @@ -0,0 +1,73 @@ +#pragma once + +#include "WeaponCustomPistol.h" +#include "script_export_space.h" + +class CWeaponKnife: public CWeapon { +private: + typedef CWeapon inherited; +protected: + bool m_attackStart; + +protected: + + virtual void switch2_Idle (); + virtual void switch2_Hiding (); + virtual void switch2_Hidden (); + virtual void switch2_Showing (); + void switch2_Attacking (u32 state); + + virtual void OnAnimationEnd (u32 state); + virtual void OnStateSwitch (u32 S); + + void state_Attacking (float dt); + + virtual void KnifeStrike (const Fvector& pos, const Fvector& dir); + + float fWallmarkSize; + u16 knife_material_idx; + +protected: + ALife::EHitType m_eHitType; + + ALife::EHitType m_eHitType_1; + //float fHitPower_1; + Fvector4 fvHitPower_1; + float fHitImpulse_1; + float fAP_1; + + ALife::EHitType m_eHitType_2; + //float fHitPower_2; + Fvector4 fvHitPower_2; + float fHitImpulse_2; + float fAP_2; + + // проигрывать звук удара с анимации начала удара, а не конца + bool bPlaySoundAtStart; + bool bHasShoot2Sound; + + float fCurrentHit; + float fCurrentAP; + +protected: + virtual void LoadFireParams (LPCSTR section, LPCSTR prefix); + void PlayShootSound (); + +public: + CWeaponKnife(); + virtual ~CWeaponKnife(); + + void Load (LPCSTR section); + + virtual void Fire2Start (); + virtual void FireStart (); + + + virtual bool Action (u16 cmd, u32 flags); + virtual void GetBriefInfo (xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponKnife) +#undef script_type_list +#define script_type_list save_type_list(CWeaponKnife) diff --git a/src/xrGameLA/WeaponKnife_script.cpp b/src/xrGameLA/WeaponKnife_script.cpp new file mode 100644 index 000000000..3aeaf9b99 --- /dev/null +++ b/src/xrGameLA/WeaponKnife_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "WeaponKnife.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponKnife::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponKnife") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponLR300.cpp b/src/xrGameLA/WeaponLR300.cpp new file mode 100644 index 000000000..4694d0319 --- /dev/null +++ b/src/xrGameLA/WeaponLR300.cpp @@ -0,0 +1,20 @@ +#include "pch_script.h" +#include "WeaponLR300.h" + +CWeaponLR300::CWeaponLR300 () : CWeaponMagazined("LR300",SOUND_TYPE_WEAPON_SUBMACHINEGUN) +{} + +CWeaponLR300::~CWeaponLR300 () +{} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponLR300::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponLR300") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponLR300.h b/src/xrGameLA/WeaponLR300.h new file mode 100644 index 000000000..fffbbc457 --- /dev/null +++ b/src/xrGameLA/WeaponLR300.h @@ -0,0 +1,30 @@ +#ifndef __XR_WEAPON_LR300_H__ +#define __XR_WEAPON_LR300_H__ +#pragma once + +#include "WeaponMagazined.h" +#include "script_export_space.h" + +class CWeaponLR300: public CWeaponMagazined +{ +private: + typedef CWeaponMagazined inherited; +public: + /* + virtual void UpdateCL (); + virtual void renderable_Render (); + virtual void spatial_move (); + virtual void spatial_register (); + virtual void spatial_unregister (); + */ + + CWeaponLR300 (); + virtual ~CWeaponLR300 (); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponLR300) +#undef script_type_list +#define script_type_list save_type_list(CWeaponLR300) + +#endif //__XR_WEAPON_LR300_H__ diff --git a/src/xrGameLA/WeaponMagazined.cpp b/src/xrGameLA/WeaponMagazined.cpp new file mode 100644 index 000000000..28c318a4a --- /dev/null +++ b/src/xrGameLA/WeaponMagazined.cpp @@ -0,0 +1,1385 @@ +#include "stdafx.h" +#include "player_hud.h" +#include "WeaponMagazined.h" +#include "entity.h" +#include "actor.h" +#include "ParticlesObject.h" +#include "scope.h" +#include "silencer.h" +#include "GrenadeLauncher.h" +#include "inventory.h" +#include "xrserver_objects_alife_items.h" +#include "ActorEffector.h" +#include "EffectorZoomInertion.h" +#include "xr_level_controller.h" +#include "level.h" +#include "object_broker.h" +#include "string_table.h" +#include "ui/UIXmlInit.h" +#include "ui/UIWindow.h" +#include "UIGameCustom.h" +#include "hudmanager.h" +#include "..\CameraBase.h" + +CUIXml* pWpnScopeXml = NULL; + +void createWpnScopeXML() +{ + if(!pWpnScopeXml) + { + pWpnScopeXml = new CUIXml(); + pWpnScopeXml->Load (CONFIG_PATH, UI_PATH, "scopes.xml"); + } +} + +CWeaponMagazined::CWeaponMagazined(LPCSTR name, ESoundTypes eSoundType) : CWeapon(name) +{ + m_eSoundShow = ESoundTypes(SOUND_TYPE_ITEM_TAKING | eSoundType); + m_eSoundHide = ESoundTypes(SOUND_TYPE_ITEM_HIDING | eSoundType); + m_eSoundShot = ESoundTypes(SOUND_TYPE_WEAPON_SHOOTING | eSoundType); + m_eSoundEmptyClick = ESoundTypes(SOUND_TYPE_WEAPON_EMPTY_CLICKING | eSoundType); + m_eSoundReload = ESoundTypes(SOUND_TYPE_WEAPON_RECHARGING | eSoundType); + + m_sounds_enabled = true; + + m_sSndShotCurrent = NULL; + m_sSilencerFlameParticles = m_sSilencerSmokeParticles = NULL; + + m_bFireSingleShot = false; + m_iShotNum = 0; + m_iQueueSize = WEAPON_ININITE_QUEUE; + m_bLockType = false; +} + +CWeaponMagazined::~CWeaponMagazined() +{ +} + +void CWeaponMagazined::net_Destroy() +{ + inherited::net_Destroy(); +} + + +void CWeaponMagazined::Load (LPCSTR section) +{ + inherited::Load (section); + + // Sounds + m_sounds.LoadSound(section,"snd_draw", "sndShow" , false, m_eSoundShow ); + m_sounds.LoadSound(section,"snd_holster", "sndHide" , false, m_eSoundHide ); + m_sounds.LoadSound(section,"snd_shoot", "sndShot" , false, m_eSoundShot ); + m_sounds.LoadSound(section,"snd_empty", "sndEmptyClick" , false, m_eSoundEmptyClick ); + m_sounds.LoadSound(section,"snd_reload", "sndReload" , true, m_eSoundReload ); + + m_sSndShotCurrent = "sndShot"; + + //звуки и партиклы глушителя, еслит такой есть + if (pSettings->line_exist(section, "silencer_flame_particles")) + { + m_sSilencerFlameParticles = pSettings->r_string(section, "silencer_flame_particles"); + } + if (pSettings->line_exist(section, "silencer_smoke_particles")) + { + m_sSilencerSmokeParticles = pSettings->r_string(section, "silencer_smoke_particles"); + } + if (pSettings->line_exist(section, "snd_silncer_shot")) + { + m_sounds.LoadSound(section,"snd_silncer_shot", "sndSilencerShot", false, m_eSoundShot); + } + + m_iRecoilStartShotNum = READ_IF_EXISTS(pSettings, r_u8, section, "dispersion_start", 1); + m_fNoRecoilTimeToFire = READ_IF_EXISTS(pSettings, r_float, section, "rpm_no_disp", 0); + m_fNoRecoilTimeToFire = (m_fNoRecoilTimeToFire > 0.f) + ? 60.f / m_fNoRecoilTimeToFire + : fTimeToFire; + + m_fTimeToFireSemi = READ_IF_EXISTS(pSettings, r_float, section, "rpm_semi", 0); + m_fTimeToFireSemi = (m_fTimeToFireSemi > 0.f) + ? 60.f / m_fTimeToFireSemi + : fTimeToFire; + + m_fShotMaxDelay = READ_IF_EXISTS(pSettings, r_float, section, "shot_max_delay", 0); + + m_conditionDecreasePerQueueShot = READ_IF_EXISTS(pSettings, r_float, section, "condition_queue_shot_dec", conditionDecreasePerShot); + + Fvector3 nullVec = {0.f, 0.f, 0.f}; + m_vFireDirectionOffset = READ_IF_EXISTS(pSettings, r_fvector3, section, "fire_direction_offset", nullVec); + + if (pSettings->line_exist(section, "fire_modes")) + { + m_bHasDifferentFireModes = true; + shared_str FireModesList = pSettings->r_string(section, "fire_modes"); + int ModesCount = _GetItemCount(FireModesList.c_str()); + m_aFireModes.clear(); + for (int i=0; i(this->H_Parent()) && (Level().CurrentViewEntity()==H_Parent()) ) + { + SDrawStaticStruct* s = HUD().GetGameUI()->AddCustomStatic("gun_jammed", true); + s->m_endTime = Device.fTimeGlobal+3.0f;// 3sec + } + + OnEmptyClick(); + } +} + +void CWeaponMagazined::FireEnd() +{ + if (!IsPending()) + { + if (m_iShotNum > 0) + { + // induce semi-auto delay after stopped shooting (eg. fire button unpressed) + fTime = m_fTimeToFireSemi; + + // Make sure recoil is applied even if stopped firing before RecoilStartShotNum + if (GetCurrentFireMode() == -1 && m_iShotNum < m_iRecoilStartShotNum) + { + AddShotEffector(); + } + } + inherited::FireEnd(); + + CActor *actor = smart_cast(H_Parent()); + if(m_pCurrentInventory && !iAmmoElapsed && actor && GetState()!=eReload) + Reload(); + } +} + +void CWeaponMagazined::Reload() +{ + inherited::Reload(); + + TryReload(); +} + +bool CWeaponMagazined::TryReload() +{ + if(m_pCurrentInventory) + { + m_pAmmo = smart_cast(m_pCurrentInventory->GetAny(*m_ammoTypes[m_ammoType] )); + + + if(IsMisfire() && iAmmoElapsed) + { + SetPending (TRUE); + SwitchState(eReload); + return true; + } + + if(m_pAmmo || unlimited_ammo()) + { + SetPending (TRUE); + SwitchState(eReload); + return true; + } + else for(u32 i = 0; i < m_ammoTypes.size(); ++i) + { + m_pAmmo = smart_cast(m_pCurrentInventory->GetAny( *m_ammoTypes[i] )); + if(m_pAmmo) + { + m_ammoType = i; + SetPending (TRUE); + SwitchState(eReload); + return true; + } + } + } + + if(GetState()!=eIdle) + SwitchState(eIdle); + + return false; +} + +bool CWeaponMagazined::IsAmmoAvailable() +{ + if (smart_cast(m_pCurrentInventory->GetAny(*m_ammoTypes[m_ammoType]))) + return (true); + else + for(u32 i = 0; i < m_ammoTypes.size(); ++i) + if (smart_cast(m_pCurrentInventory->GetAny(*m_ammoTypes[i]))) + return (true); + return (false); +} + +void CWeaponMagazined::OnMagazineEmpty() +{ + //попытка стрелять когда нет патронов + if(GetState() == eIdle) + { + OnEmptyClick (); + return; + } + + if( GetNextState() != eMagEmpty && GetNextState() != eReload) + { + SwitchState(eMagEmpty); + } + + inherited::OnMagazineEmpty(); +} + +void CWeaponMagazined::UnloadMagazine(bool spawn_ammo) +{ + xr_map l_ammo; + + while(!m_magazine.empty()) + { + CCartridge &l_cartridge = m_magazine.back(); + xr_map::iterator l_it; + for(l_it = l_ammo.begin(); l_ammo.end() != l_it; ++l_it) + { + if(!xr_strcmp(*l_cartridge.m_ammoSect, l_it->first)) + { + ++(l_it->second); + break; + } + } + + if(l_it == l_ammo.end()) l_ammo[*l_cartridge.m_ammoSect] = 1; + m_magazine.pop_back(); + --iAmmoElapsed; + } + + VERIFY((u32)iAmmoElapsed == m_magazine.size()); + + if (!spawn_ammo) + return; + + xr_map::iterator l_it; + for(l_it = l_ammo.begin(); l_ammo.end() != l_it; ++l_it) + { + if(m_pCurrentInventory) + { + CWeaponAmmo *l_pA = smart_cast(m_pCurrentInventory->GetAny(l_it->first)); + if(l_pA) + { + u16 l_free = l_pA->m_boxSize - l_pA->m_boxCurr; + l_pA->m_boxCurr = l_pA->m_boxCurr + (l_free < l_it->second ? l_free : l_it->second); + l_it->second = l_it->second - (l_free < l_it->second ? l_free : l_it->second); + } + } + if(l_it->second && !unlimited_ammo()) SpawnAmmo(l_it->second, l_it->first); + } +} + +void CWeaponMagazined::ReloadMagazine() +{ + m_dwAmmoCurrentCalcFrame = 0; + + //устранить осечку при перезарядке + if(IsMisfire()) bMisfire = false; + + //переменная блокирует использование + //только разных типов патронов +// static bool l_lockType = false; + if (!m_bLockType) { + m_ammoName = NULL; + m_pAmmo = NULL; + } + + if (!m_pCurrentInventory) return; + + if(m_set_next_ammoType_on_reload != u8(-1)){ + m_ammoType = m_set_next_ammoType_on_reload; + m_set_next_ammoType_on_reload = u8(-1); + } + + if(!unlimited_ammo()) + { + //попытаться найти в инвентаре патроны текущего типа + m_pAmmo = smart_cast(m_pCurrentInventory->GetAny(*m_ammoTypes[m_ammoType])); + + if(!m_pAmmo && !m_bLockType) + { + for(u32 i = 0; i < m_ammoTypes.size(); ++i) + { + //проверить патроны всех подходящих типов + m_pAmmo = smart_cast(m_pCurrentInventory->GetAny(*m_ammoTypes[i])); + if(m_pAmmo) + { + m_ammoType = i; + break; + } + } + } + } + + + //нет патронов для перезарядки + if(!m_pAmmo && !unlimited_ammo() ) return; + + //разрядить магазин, если загружаем патронами другого типа + if(!m_bLockType && !m_magazine.empty() && + (!m_pAmmo || xr_strcmp(m_pAmmo->cNameSect(), + *m_magazine.back().m_ammoSect))) + UnloadMagazine(); + + VERIFY((u32)iAmmoElapsed == m_magazine.size()); + + if (m_DefaultCartridge.m_LocalAmmoType != m_ammoType) + m_DefaultCartridge.Load(*m_ammoTypes[m_ammoType], u8(m_ammoType)); + CCartridge l_cartridge = m_DefaultCartridge; + while (iAmmoElapsed < maxMagazineSize_) + { + if (!unlimited_ammo()) + { + if (!m_pAmmo->Get(l_cartridge)) break; + } + ++iAmmoElapsed; + l_cartridge.m_LocalAmmoType = u8(m_ammoType); + m_magazine.push_back(l_cartridge); + } + m_ammoName = (m_pAmmo) ? m_pAmmo->m_nameShort : NULL; + + VERIFY((u32)iAmmoElapsed == m_magazine.size()); + + //выкинуть коробку патронов, если она пустая + if(m_pAmmo && !m_pAmmo->m_boxCurr && OnServer()) + m_pAmmo->SetDropManual(TRUE); + + if (maxMagazineSize_ > iAmmoElapsed) + { + m_bLockType = true; + ReloadMagazine(); + m_bLockType = false; + } + + VERIFY((u32)iAmmoElapsed == m_magazine.size()); +} + +void CWeaponMagazined::OnStateSwitch (u32 S) +{ + inherited::OnStateSwitch(S); + switch (S) + { + case eIdle: + switch2_Idle (); + break; + case eFire: + switch2_Fire (); + break; + case eMisfire: + if(smart_cast(this->H_Parent()) && (Level().CurrentViewEntity()==H_Parent()) ) + { + SDrawStaticStruct* s = CurrentGameUI()->AddCustomStatic("gun_jammed", true); + s->m_endTime = Device.fTimeGlobal+3.0f;// 3sec + } + break; + case eMagEmpty: + switch2_Empty (); + break; + case eReload: + switch2_Reload (); + break; + case eShowing: + switch2_Showing (); + break; + case eHiding: + switch2_Hiding (); + break; + case eHidden: + switch2_Hidden (); + break; + } +} + +void CWeaponMagazined::UpdateCL () +{ + inherited::UpdateCL (); + float dt = Device.fTimeDelta; + + + + //когда происходит апдейт состояния оружия + //ничего другого не делать + if(GetNextState() == GetState()) + { + switch (GetState()) + { + case eShowing: + case eHiding: + case eReload: + case eIdle: + fTime -= dt; + if (fTime<0) + fTime = 0; + break; + case eFire: + if(iAmmoElapsed>0) + state_Fire (dt); + + if(fTime<=0) + { + if(iAmmoElapsed == 0) + OnMagazineEmpty(); + StopShooting(); + } + else + { + fTime -= dt; + } + + break; + case eMisfire: state_Misfire (dt); break; + case eMagEmpty: state_MagEmpty (dt); break; + case eHidden: break; + } + } + + UpdateSounds (); +} + +void CWeaponMagazined::UpdateSounds () +{ + if (Device.dwFrame == dwUpdateSounds_Frame) + return; + + dwUpdateSounds_Frame = Device.dwFrame; + + Fvector P = get_LastFP(); + m_sounds.SetPosition("sndShow", P); + m_sounds.SetPosition("sndHide", P); + m_sounds.SetPosition("sndReload", P); +} + +void CWeaponMagazined::state_Fire (float dt) +{ + if(iAmmoElapsed > 0) + { + VERIFY(fTimeToFire>0.f); + + if (!H_Parent()) return; + /*if (smart_cast(H_Parent()) != NULL) + { + Msg("! WARNING: state_Fire of object [%d][%s] while parent is CMPPlayerBag...", ID(), cNameSect().c_str()); + return; + }*/ + + Fvector p1, d; + p1.set(get_LastFP()); + d.set(get_LastFD()); + + CInventoryOwner* io = smart_cast(H_Parent()); + if(NULL == io->inventory().ActiveItem()) + { + Log("current_state", GetState() ); + Log("next_state", GetNextState()); + Log("item_sect", cNameSect().c_str()); + Log("H_Parent", H_Parent()->cNameSect().c_str()); + } + + CEntity* E = smart_cast(H_Parent()); + E->g_fireParams (this, p1,d); + + if( !E->g_stateFire() ) + StopShooting(); + + d.add(m_vFireDirectionOffset); + + int fireMode = GetCurrentFireMode(); + VERIFY(!m_magazine.empty()); + + // NOTE: Queue size equals to fire mode (number of shots or -1 for full-auto) for Actor but different for NPC's (like number of shots in a row for semi-auto) + while (!m_magazine.empty() && fTime<=0 && (IsWorking() || m_bFireSingleShot) && (m_iQueueSize < 0 || m_iShotNum < m_iQueueSize)) + { + if( CheckForMisfire() ) + { + StopShooting(); + return; + } + m_bFireSingleShot = false; + + VERIFY(fTimeToFire>0.f); + VERIFY(m_fNoRecoilTimeToFire>0.f); + + ++m_iShotNum; + + if (fireMode > 0 && (m_iShotNum % fireMode) == 0) + { + // last shot in controlled burst or semi-automatic mode + fTime += m_fTimeToFireSemi; + OnShot (true); + } + else if (m_iShotNum < m_iRecoilStartShotNum) + { + // no-recoil shot (for Abakan) + fTime += m_fNoRecoilTimeToFire; + OnShot (false); + } + else + { + // normal shot + fTime += fTimeToFire; + OnShot (true); + } + FireTrace(p1, d); + + if (m_iShotNum == m_iQueueSize) + { + m_bStopedAfterQueueFired = true; + } + } + + UpdateSounds (); + } +} + +void CWeaponMagazined::state_Misfire (float dt) +{ + OnEmptyClick (); + SwitchState (eIdle); + + bMisfire = true; + + UpdateSounds (); +} + +void CWeaponMagazined::state_MagEmpty (float dt) +{ +} + +void CWeaponMagazined::SetDefaults () +{ + CWeapon::SetDefaults (); +} + + +void CWeaponMagazined::OnShot (bool hasRecoil) +{ + // Sound + PlaySound (m_sSndShotCurrent.c_str(), get_LastFP()); + + if (hasRecoil) + { + // Camera + AddShotEffector (); + } + + // Animation + PlayAnimShoot (); + + // Shell Drop + Fvector vel; + PHGetLinearVell(vel); + OnShellDrop (get_LastSP(), vel); + + // Огонь из ствола + StartFlameParticles (); + + //дым из ствола + ForceUpdateFireParticles (); + StartSmokeParticles (get_LastFP(), vel); +} + + +void CWeaponMagazined::OnEmptyClick () +{ + PlaySound ("sndEmptyClick",get_LastFP()); +} + +void CWeaponMagazined::OnAnimationEnd(u32 state) +{ + switch(state) + { + case eReload: ReloadMagazine(); SwitchState(eIdle); break; // End of reload animation + + case eHiding: SwitchState(eHidden); break; // End of Hide + case eShowing: SwitchState(eIdle); break; // End of Show + case eIdle: switch2_Idle(); break; // Keep showing idle + + } + inherited::OnAnimationEnd(state); +} +void CWeaponMagazined::switch2_Idle () +{ + m_iShotNum = 0; + SetPending (FALSE); + PlayAnimIdle (); +} + +#ifdef DEBUG +#include "ai\stalker\ai_stalker.h" +#include "object_handler_planner.h" +#endif +void CWeaponMagazined::switch2_Fire () +{ + CInventoryOwner* io = smart_cast(H_Parent()); + CInventoryItem* ii = smart_cast(this); +#ifdef LOG_ACTION + if (!io) + return; + //VERIFY2 (io,make_string("no inventory owner, item %s",*cName())); + + if (ii != io->inventory().ActiveItem()) + Msg ("! not an active item, item %s, owner %s, active item %s",*cName(),*H_Parent()->cName(),io->inventory().ActiveItem() ? *io->inventory().ActiveItem()->object().cName() : "no_active_item"); + + if ( !(io && (ii == io->inventory().ActiveItem())) ) + { + CAI_Stalker *stalker = smart_cast(H_Parent()); + if (stalker) { + stalker->planner().show (); + stalker->planner().show_current_world_state (); + stalker->planner().show_target_world_state (); + } + } +#else + if (!io) + return; +#endif // DEBUG + +// +// VERIFY2( +// io && (ii == io->inventory().ActiveItem()), +// make_string( +// "item[%s], parent[%s]", +// *cName(), +// H_Parent() ? *H_Parent()->cName() : "no_parent" +// ) +// ); + + m_bStopedAfterQueueFired = false; + m_bFireSingleShot = DelayedShotIsAllowed(); + m_iShotNum = 0; + + if((OnClient() || Level().IsDemoPlay())&& !IsWorking()) + FireStart(); +} + +void CWeaponMagazined::switch2_Empty() +{ + OnZoomOut(); + + if(!TryReload()) + { + OnEmptyClick(); + } + else + { + inherited::FireEnd(); + } +} +void CWeaponMagazined::PlayReloadSound() +{ + if(m_sounds_enabled) + { + PlaySound ("sndReload",get_LastFP()); + } +} + +void CWeaponMagazined::switch2_Reload() +{ + CWeapon::FireEnd(); + + PlayReloadSound (); + PlayAnimReload (); + SetPending (TRUE); +} +void CWeaponMagazined::switch2_Hiding() +{ + OnZoomOut(); + CWeapon::FireEnd(); + + if(m_sounds_enabled) + PlaySound ("sndHide",get_LastFP()); + + PlayAnimHide(); + SetPending (TRUE); +} + +void CWeaponMagazined::switch2_Hidden() +{ + CWeapon::FireEnd(); + + StopCurrentAnimWithoutCallback(); + + signal_HideComplete (); + RemoveShotEffector (); + RemoveZoomInertionEffector(); +} + +void CWeaponMagazined::switch2_Showing() +{ + if(m_sounds_enabled) + PlaySound ("sndShow",get_LastFP()); + + SetPending (TRUE); + PlayAnimShow (); +} + +bool CWeaponMagazined::Action(u16 cmd, u32 flags) +{ + if(inherited::Action(cmd, flags)) return true; + + //если оружие чем-то занято, то ничего не делать + if(IsPending()) return false; + + switch(cmd) + { + case kWPN_RELOAD: + { + if(flags&CMD_START) + if (iAmmoElapsed < maxMagazineSize_ || IsMisfire()) + Reload(); + } + return true; + case kWPN_FIREMODE_PREV: + { + if(flags&CMD_START) + { + OnPrevFireMode(); + return true; + }; + }break; + case kWPN_FIREMODE_NEXT: + { + if(flags&CMD_START) + { + OnNextFireMode(); + return true; + }; + }break; + } + return false; +} + +bool CWeaponMagazined::CanAttach(PIItem pIItem) +{ + CScope* pScope = smart_cast(pIItem); + CSilencer* pSilencer = smart_cast(pIItem); + CGrenadeLauncher* pGrenadeLauncher = smart_cast(pIItem); + + if( pScope && + m_eScopeStatus == CSE_ALifeItemWeapon::eAddonAttachable && + (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonScope) == 0) + { + SCOPES_VECTOR_IT it = m_scopes.begin(); + for(; it!=m_scopes.end(); it++) + { + if(pSettings->r_string((*it),"scope_name")==pIItem->object().cNameSect()) + return true; + } + return false; + } + else if( pSilencer && + m_eSilencerStatus == CSE_ALifeItemWeapon::eAddonAttachable && + (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonSilencer) == 0 && + (m_sSilencerName == pIItem->object().cNameSect()) ) + return true; + else if ( pGrenadeLauncher && + m_eGrenadeLauncherStatus == CSE_ALifeItemWeapon::eAddonAttachable && + (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher) == 0 && + (m_sGrenadeLauncherName == pIItem->object().cNameSect()) ) + return true; + else + return inherited::CanAttach(pIItem); +} + +bool CWeaponMagazined::CanDetach(const char* item_section_name) +{ + if( m_eScopeStatus == CSE_ALifeItemWeapon::eAddonAttachable && + 0 != (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonScope)) + { + SCOPES_VECTOR_IT it = m_scopes.begin(); + for(; it!=m_scopes.end(); it++) + { + if(pSettings->r_string((*it),"scope_name")==item_section_name) + return true; + } + return false; + } + else if(m_eSilencerStatus == CSE_ALifeItemWeapon::eAddonAttachable && + 0 != (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonSilencer) && + (m_sSilencerName == item_section_name)) + return true; + else if(m_eGrenadeLauncherStatus == CSE_ALifeItemWeapon::eAddonAttachable && + 0 != (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher) && + (m_sGrenadeLauncherName == item_section_name)) + return true; + else + return inherited::CanDetach(item_section_name); +} + +bool CWeaponMagazined::Attach(PIItem pIItem, bool b_send_event) +{ + bool result = false; + + CScope* pScope = smart_cast(pIItem); + CSilencer* pSilencer = smart_cast(pIItem); + CGrenadeLauncher* pGrenadeLauncher = smart_cast(pIItem); + + if(pScope && + m_eScopeStatus == CSE_ALifeItemWeapon::eAddonAttachable && + (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonScope) == 0) + { + SCOPES_VECTOR_IT it = m_scopes.begin(); + for(; it!=m_scopes.end(); it++) + { + if(pSettings->r_string((*it),"scope_name")==pIItem->object().cNameSect()) + m_cur_scope = u8(it-m_scopes.begin()); + } + m_flagsAddOnState |= CSE_ALifeItemWeapon::eWeaponAddonScope; + result = true; + } + else if(pSilencer && + m_eSilencerStatus == CSE_ALifeItemWeapon::eAddonAttachable && + (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonSilencer) == 0 && + (m_sSilencerName == pIItem->object().cNameSect())) + { + m_flagsAddOnState |= CSE_ALifeItemWeapon::eWeaponAddonSilencer; + result = true; + } + else if(pGrenadeLauncher && + m_eGrenadeLauncherStatus == CSE_ALifeItemWeapon::eAddonAttachable && + (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher) == 0 && + (m_sGrenadeLauncherName == pIItem->object().cNameSect())) + { + m_flagsAddOnState |= CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher; + result = true; + } + + if(result) + { + if (b_send_event && OnServer()) + { + //уничтожить подсоединенную вещь из инвентаря +//. pIItem->Drop (); + pIItem->object().DestroyObject (); + }; + + UpdateAddonsVisibility(); + InitAddons(); + + return true; + } + else + return inherited::Attach(pIItem, b_send_event); +} + +bool CWeaponMagazined::DetachScope(const char* item_section_name, bool b_spawn_item) +{ + bool detached = false; + SCOPES_VECTOR_IT it = m_scopes.begin(); + for(; it!=m_scopes.end(); it++) + { + LPCSTR iter_scope_name = pSettings->r_string((*it),"scope_name"); + if(!xr_strcmp(iter_scope_name, item_section_name)) + { + m_cur_scope = 0; + detached = true; + } + } + return detached; +} + +bool CWeaponMagazined::Detach(const char* item_section_name, bool b_spawn_item) +{ + if( m_eScopeStatus == CSE_ALifeItemWeapon::eAddonAttachable && + DetachScope(item_section_name, b_spawn_item)) + { + if ((m_flagsAddOnState & CSE_ALifeItemWeapon::eWeaponAddonScope) == 0) + { + Msg("ERROR: scope addon already detached."); + return true; + } + m_flagsAddOnState &= ~CSE_ALifeItemWeapon::eWeaponAddonScope; + + UpdateAddonsVisibility(); + InitAddons(); + + return CInventoryItemObject::Detach(item_section_name, b_spawn_item); + } + else if(m_eSilencerStatus == CSE_ALifeItemWeapon::eAddonAttachable && + 0 != (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonSilencer) && + (m_sSilencerName == item_section_name)) + { + m_flagsAddOnState &= ~CSE_ALifeItemWeapon::eWeaponAddonSilencer; + + UpdateAddonsVisibility(); + InitAddons(); + return CInventoryItemObject::Detach(item_section_name, b_spawn_item); + } + else if(m_eGrenadeLauncherStatus == CSE_ALifeItemWeapon::eAddonAttachable && + 0 != (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher) && + (m_sGrenadeLauncherName == item_section_name)) + { + m_flagsAddOnState &= ~CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher; + + UpdateAddonsVisibility(); + InitAddons(); + return CInventoryItemObject::Detach(item_section_name, b_spawn_item); + } + else + return inherited::Detach(item_section_name, b_spawn_item);; +} + +ENGINE_API bool g_dedicated_server; + +#include "UIStaticItem.h" +void CWeaponMagazined::InitAddons() +{ + if (IsScopeAttached()) + { + if (m_eScopeStatus == ALife::eAddonAttachable) + { + //m_scopes[cur_scope]->m_sScopeName = pSettings->r_string(cNameSect(), "scope_name"); + //m_scopes[cur_scope]->m_iScopeX = pSettings->r_s32(cNameSect(),"scope_x"); + //m_scopes[cur_scope]->m_iScopeY = pSettings->r_s32(cNameSect(),"scope_y"); + + shared_str scope_tex_name; + scope_tex_name = pSettings->r_string(GetScopeName(), "scope_texture"); + m_zoom_params.m_fScopeZoomFactor = pSettings->r_float(GetScopeName(), "scope_zoom_factor"); + m_zoom_params.m_bUseDynamicZoom = READ_IF_EXISTS(pSettings, r_bool, GetScopeName(), "scope_dynamic_zoom", FALSE); + + m_zoom_params.m_sUseZoomPostprocess = READ_IF_EXISTS(pSettings, r_string, GetScopeName(), "scope_nightvision", 0); + m_zoom_params.m_sUseBinocularVision = READ_IF_EXISTS(pSettings, r_string, GetScopeName(), "scope_alive_detector", 0); + + if (m_UIScope) + { + xr_delete(m_UIScope); + } + + m_UIScope = xr_new (); + createWpnScopeXML(); + + CUIXmlInit::InitWindow(*pWpnScopeXml, scope_tex_name.c_str(), 0, m_UIScope); + } + } + else + { + if (m_UIScope) + { + xr_delete(m_UIScope); + } + } + + if ( IsSilencerAttached() && SilencerAttachable()) //skyloader: dont touch SilencerAttachable(), it needs for pb, vss, val + { + m_sFlameParticlesCurrent = m_sSilencerFlameParticles; + m_sSmokeParticlesCurrent = m_sSilencerSmokeParticles; + m_sSndShotCurrent = "sndSilencerShot"; + + //подсветка от выстрела + LoadLights(*cNameSect(), "silencer_"); + ApplySilencerKoeffs(); + } + else + { + m_sFlameParticlesCurrent = m_sFlameParticles; + m_sSmokeParticlesCurrent = m_sSmokeParticles; + m_sSndShotCurrent = "sndShot"; + + //подсветка от выстрела + LoadLights(*cNameSect(), ""); + ResetSilencerKoeffs(); + } + + inherited::InitAddons(); +} + +void CWeaponMagazined::LoadSilencerKoeffs() +{ + if (m_eSilencerStatus == ALife::eAddonAttachable) + { + LPCSTR sect = m_sSilencerName.c_str(); + m_silencer_koef.hit_power = READ_IF_EXISTS(pSettings, r_float, sect, "bullet_hit_power_k", 1.0f); + m_silencer_koef.hit_impulse = READ_IF_EXISTS(pSettings, r_float, sect, "bullet_hit_impulse_k", 1.0f); + m_silencer_koef.bullet_speed = READ_IF_EXISTS(pSettings, r_float, sect, "bullet_speed_k", 1.0f); + m_silencer_koef.fire_dispersion = READ_IF_EXISTS(pSettings, r_float, sect, "fire_dispersion_base_k", 1.0f); + m_silencer_koef.cam_dispersion = READ_IF_EXISTS(pSettings, r_float, sect, "cam_dispersion_k", 1.0f); + m_silencer_koef.cam_disper_inc = READ_IF_EXISTS(pSettings, r_float, sect, "cam_dispersion_inc_k", 1.0f); + } + + clamp(m_silencer_koef.hit_power, 0.0f, 1.0f); + clamp(m_silencer_koef.hit_impulse, 0.0f, 1.0f); + clamp(m_silencer_koef.bullet_speed, 0.0f, 1.0f); + clamp(m_silencer_koef.fire_dispersion, 0.0f, 3.0f); + clamp(m_silencer_koef.cam_dispersion, 0.0f, 1.0f); + clamp(m_silencer_koef.cam_disper_inc, 0.0f, 1.0f); +} + +void CWeaponMagazined::ApplySilencerKoeffs() +{ + cur_silencer_koef = m_silencer_koef; +} + +void CWeaponMagazined::ResetSilencerKoeffs() +{ + cur_silencer_koef.Reset(); +} + +//виртуальные функции для проигрывания анимации HUD +void CWeaponMagazined::PlayAnimShow() +{ + VERIFY(GetState()==eShowing); + PlayHUDMotion("anim_show", FALSE, this, GetState()); +} + +void CWeaponMagazined::PlayAnimHide() +{ + VERIFY(GetState()==eHiding); + PlayHUDMotion("anim_hide", TRUE, this, GetState()); +} + + +void CWeaponMagazined::PlayAnimReload() +{ + VERIFY(GetState()==eReload); + + PlayHUDMotion("anim_reload", TRUE, this, GetState()); +} + +void CWeaponMagazined::PlayAnimAim() +{ + PlayHUDMotion("anim_idle_aim", TRUE, NULL, GetState()); +} + +void CWeaponMagazined::PlayAnimIdle() +{ + if(GetState()!=eIdle) return; + if(IsZoomed()) + { + PlayAnimAim(); + }else + inherited::PlayAnimIdle(); +} + +void CWeaponMagazined::PlayAnimShoot() +{ + VERIFY(GetState()==eFire); + PlayHUDMotion("anim_shots", FALSE, this, GetState()); +} + +void CWeaponMagazined::OnZoomIn () +{ + inherited::OnZoomIn(); + + if(GetState() == eIdle) + PlayAnimIdle(); + + + CActor* pActor = smart_cast(H_Parent()); + if(pActor) + { + CEffectorZoomInertion* S = smart_cast (pActor->Cameras().GetCamEffector(eCEZoom)); + if (!S) + { + S = (CEffectorZoomInertion*)pActor->Cameras().AddCamEffector(new CEffectorZoomInertion()); + S->Init(this); + }; + S->SetRndSeed(pActor->GetZoomRndSeed()); + S->Enable(true); + R_ASSERT (S); + } +} +void CWeaponMagazined::OnZoomOut () +{ + if (!IsZoomed()) + return; + + inherited::OnZoomOut(); + + if(GetState() == eIdle) + PlayAnimIdle(); + + CActor* pActor = smart_cast(H_Parent()); + if(pActor) + { + auto S = smart_cast(pActor->Cameras().GetCamEffector(eCEZoom)); + if (S) + { + S->Enable(false, m_zoom_params.m_fZoomRotateTime); + } + } +} + +//переключение режимов стрельбы одиночными и очередями +bool CWeaponMagazined::SwitchMode () +{ + if(eIdle != GetState() || IsPending()) return false; + + if(SingleShotMode()) + m_iQueueSize = WEAPON_ININITE_QUEUE; + else + m_iQueueSize = 1; + + PlaySound ("sndEmptyClick", get_LastFP()); + + return true; +} + +LPCSTR CWeaponMagazined::getAmmoName() +{ + return m_ammoTypes[m_ammoType].c_str(); +} + +void CWeaponMagazined::OnNextFireMode () +{ + if (!m_bHasDifferentFireModes) return; + if (GetState() != eIdle) return; + m_iCurFireMode = (m_iCurFireMode+1+m_aFireModes.size()) % m_aFireModes.size(); + SetQueueSize(GetCurrentFireMode()); +}; + +void CWeaponMagazined::OnPrevFireMode () +{ + if (!m_bHasDifferentFireModes) return; + if (GetState() != eIdle) return; + m_iCurFireMode = (m_iCurFireMode-1+m_aFireModes.size()) % m_aFireModes.size(); + SetQueueSize(GetCurrentFireMode()); +}; + +void CWeaponMagazined::OnH_A_Chield () +{ + if (m_bHasDifferentFireModes) + { + CActor *actor = smart_cast(H_Parent()); + if (!actor) SetQueueSize(-1); + else SetQueueSize(GetCurrentFireMode()); + }; + inherited::OnH_A_Chield(); +} + +void CWeaponMagazined::OnH_B_Independent(bool jbd) +{ + RemoveZoomInertionEffector(); + inherited::OnH_B_Independent(jbd); +} + +void CWeaponMagazined::SetQueueSize (int size) +{ + m_iQueueSize = size; + if (m_iQueueSize == -1) + xr_strcpy(m_sCurFireMode, "A"); + else + xr_sprintf(m_sCurFireMode, "%d", m_iQueueSize); +}; + +float CWeaponMagazined::GetWeaponDeterioration () +{ + if (!m_bHasDifferentFireModes || m_iPrefferedFireMode == -1 || u32(GetCurrentFireMode()) <= u32(m_iPrefferedFireMode)) + { + return (u32(GetCurrentFireMode()) > 1) + ? m_conditionDecreasePerQueueShot + : inherited::GetWeaponDeterioration(); + } + return m_iShotNum*m_conditionDecreasePerQueueShot; +} + +void CWeaponMagazined::RemoveZoomInertionEffector() +{ + CActor* pActor = smart_cast(H_Parent()); + if (pActor) + { + pActor->Cameras().RemoveCamEffector(eCEZoom); + } +} + +BOOL CWeaponMagazined::net_Spawn(CSE_Abstract* server_entity) +{ + BOOL bResult = inherited::net_Spawn(server_entity); + CSE_ALifeItemWeaponMagazined* weapon_mgznd = smart_cast(server_entity); + + R_ASSERT(weapon_mgznd); + + m_iCurFireMode = weapon_mgznd->m_u8CurFireMode; + + return bResult; +} + +void CWeaponMagazined::save(NET_Packet &output_packet) +{ + inherited::save (output_packet); + save_data (m_iQueueSize, output_packet); + save_data (m_iShotNum, output_packet); +} + +void CWeaponMagazined::load(IReader &input_packet) +{ + inherited::load (input_packet); + load_data (m_iQueueSize, input_packet);SetQueueSize(m_iQueueSize); + load_data (m_iShotNum, input_packet); +} + +void CWeaponMagazined::net_Export (NET_Packet& P) +{ + inherited::net_Export (P); + + P.w_u8(u8(m_iCurFireMode&0x00ff)); +} + +void CWeaponMagazined::net_Import (NET_Packet& P) +{ + inherited::net_Import (P); + + m_iCurFireMode = P.r_u8(); + SetQueueSize(GetCurrentFireMode()); +} +#include "string_table.h" +void CWeaponMagazined::GetBriefInfo(xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count) +{ + int AE = GetAmmoElapsed(); + int AC = GetAmmoCurrent(); + + if(AE==0 || 0==m_magazine.size() ) + icon_sect_name = *m_ammoTypes[m_ammoType]; + else + icon_sect_name = *m_ammoTypes[m_magazine.back().m_LocalAmmoType]; + + string256 sItemName; + xr_strcpy (sItemName, *CStringTable().translate(pSettings->r_string(icon_sect_name.c_str(), "inv_name_short"))); + + str_name = sItemName; + + { + if (!unlimited_ammo()) + xr_sprintf (sItemName, "%d/%d",AE,AC - AE); + else + xr_sprintf (sItemName, "%d/--",AE); + + str_count = sItemName; + } +} + +bool CWeaponMagazined::install_upgrade_impl( LPCSTR section, bool test ) +{ + bool result = inherited::install_upgrade_impl( section, test ); + + LPCSTR str; + // fire_modes = 1, 2, -1 + bool result2 = process_if_exists_set( section, "fire_modes", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + int ModesCount = _GetItemCount( str ); + m_aFireModes.clear(); + for ( int i = 0; i < ModesCount; ++i ) + { + string16 sItem; + _GetItem( str, i, sItem ); + m_aFireModes.push_back( (s8)atoi(sItem) ); + } + m_iCurFireMode = ModesCount - 1; + } + result |= result2; + + result |= process_if_exists_set(section, "dispersion_start", &CInifile::r_s32, m_iRecoilStartShotNum, test); + result |= ProcessRpmUpgrade( section, "rpm_no_disp", m_fNoRecoilTimeToFire, test ); + result |= ProcessRpmUpgrade( section, "rpm_semi", m_fTimeToFireSemi, test ); + + // sounds (name of the sound, volume (0.0 - 1.0), delay (sec)) + result2 = process_if_exists_set( section, "snd_draw", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + m_sounds.LoadSound( section, "snd_draw" , "sndShow" , false, m_eSoundShow ); + } + result |= result2; + + result2 = process_if_exists_set( section, "snd_holster", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + m_sounds.LoadSound( section, "snd_holster" , "sndHide" , false, m_eSoundHide ); + } + result |= result2; + + result2 = process_if_exists_set( section, "snd_shoot", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + m_sounds.LoadSound( section, "snd_shoot" , "sndShot" , false, m_eSoundShot ); + } + result |= result2; + + result2 = process_if_exists_set( section, "snd_empty", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + m_sounds.LoadSound( section, "snd_empty" , "sndEmptyClick" , false, m_eSoundEmptyClick); + } + result |= result2; + + result2 = process_if_exists_set( section, "snd_reload", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + m_sounds.LoadSound( section, "snd_reload" , "sndReload" , true, m_eSoundReload ); + } + result |= result2; + + if ( m_eSilencerStatus == ALife::eAddonAttachable || m_eSilencerStatus == ALife::eAddonPermanent ) + { + result |= process_if_exists_set( section, "silencer_flame_particles", &CInifile::r_string, m_sSilencerFlameParticles, test ); + result |= process_if_exists_set( section, "silencer_smoke_particles", &CInifile::r_string, m_sSilencerSmokeParticles, test ); + + result2 = process_if_exists_set( section, "snd_silncer_shot", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + m_sounds.LoadSound( section, "snd_silncer_shot" , "sndSilencerShot", false, m_eSoundShot ); + } + result |= result2; + } + + // fov for zoom mode + result |= process_if_exists( section, "ironsight_zoom_factor", &CInifile::r_float, m_zoom_params.m_fIronSightZoomFactor, test ); + + if( IsScopeAttached() ) + { + //if ( m_eScopeStatus == ALife::eAddonAttachable ) + { + result |= process_if_exists( section, "scope_zoom_factor", &CInifile::r_float, m_zoom_params.m_fScopeZoomFactor, test ); + } + } + + result |= process_if_exists(section, "condition_queue_shot_dec", &CInifile::r_float, m_conditionDecreasePerQueueShot, test ); + + return result; +} + +bool CWeaponMagazined::DelayedShotIsAllowed() +{ + return fTime <= m_fShotMaxDelay; +} + diff --git a/src/xrGameLA/WeaponMagazined.h b/src/xrGameLA/WeaponMagazined.h new file mode 100644 index 000000000..1ed82b533 --- /dev/null +++ b/src/xrGameLA/WeaponMagazined.h @@ -0,0 +1,199 @@ +#ifndef __XR_WEAPON_MAG_H__ +#define __XR_WEAPON_MAG_H__ +#pragma once + +#include "weapon.h" +#include "hudsound.h" +#include "ai_sounds.h" + +class ENGINE_API CMotionDef; + +//размер очереди считается бесконечность +//заканчиваем стрельбу, только, если кончились патроны +#define WEAPON_ININITE_QUEUE -1 + + +class CWeaponMagazined: public CWeapon +{ +private: + typedef CWeapon inherited; +protected: + //звук текущего выстрела + shared_str m_sSndShotCurrent; + + //дополнительная информация о глушителе + LPCSTR m_sSilencerFlameParticles; + LPCSTR m_sSilencerSmokeParticles; + + ESoundTypes m_eSoundShow; + ESoundTypes m_eSoundHide; + ESoundTypes m_eSoundShot; + ESoundTypes m_eSoundEmptyClick; + ESoundTypes m_eSoundReload; + ESoundTypes m_eSoundReloadJammed; + ESoundTypes m_eSoundReloadNotEmpty; + + bool m_sounds_enabled; + + // General + //кадр момента пересчета UpdateSounds + u32 dwUpdateSounds_Frame; +protected: + virtual void OnMagazineEmpty (); + + virtual void switch2_Idle (); + virtual void switch2_Fire (); + virtual void switch2_Empty (); + virtual void switch2_Reload (); + virtual void switch2_Hiding (); + virtual void switch2_Hidden (); + virtual void switch2_Showing (); + + virtual void OnShot () { OnShot(true); } + void OnShot (bool hasRecoil); + + virtual void OnEmptyClick (); + + virtual void OnAnimationEnd (u32 state); + virtual void OnStateSwitch (u32 S); + + virtual void UpdateSounds (); + + bool TryReload (); + +protected: + virtual void ReloadMagazine(); + void ApplySilencerKoeffs(); + void ResetSilencerKoeffs(); + + virtual void state_Fire (float dt); + virtual void state_MagEmpty (float dt); + virtual void state_Misfire (float dt); + virtual bool DelayedShotIsAllowed(); +public: + CWeaponMagazined (LPCSTR name="AK74",ESoundTypes eSoundType=SOUND_TYPE_WEAPON_SUBMACHINEGUN); + virtual ~CWeaponMagazined (); + + virtual void Load (LPCSTR section); + void LoadSilencerKoeffs(); + virtual CWeaponMagazined*cast_weapon_magazined () {return this;} + + virtual void SetDefaults (); + virtual void FireStart (); + virtual void FireEnd (); + virtual void Reload (); + + + virtual void UpdateCL (); + // Happens when object gets online. Import saved data from server here. Do oject initialization based on data imported from server + virtual BOOL net_Spawn (CSE_Abstract* server_entity); + virtual void net_Destroy (); + // constant export of various weapon data to server. Here should be stuff, needed to be saved before object switches offline. Than it should be imported in net_spawn, when object gets online again + virtual void net_Export (NET_Packet& P); + // not happens in Single Player + virtual void net_Import (NET_Packet& P); + + // save stuff that is needed while weapon is in alife radius + virtual void save(NET_Packet &output_packet); + // load stuff needed in alife radius + virtual void load(IReader &input_packet); + + virtual void OnH_A_Chield (); + void OnH_B_Independent (bool) override; + + virtual bool Attach (PIItem pIItem, bool b_send_event); + virtual bool Detach (const char* item_section_name, bool b_spawn_item); + bool DetachScope (const char* item_section_name, bool b_spawn_item); + virtual bool CanAttach (PIItem pIItem); + virtual bool CanDetach (const char* item_section_name); + + virtual void InitAddons(); + + virtual bool Action (u16 cmd, u32 flags); + virtual LPCSTR getAmmoName (); + bool IsAmmoAvailable (); + virtual void UnloadMagazine (bool spawn_ammo = true); + + virtual void GetBriefInfo (xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count); + + + ////////////////////////////////////////////// + // для стрельбы очередями или одиночными + ////////////////////////////////////////////// +public: + virtual bool SwitchMode (); + virtual bool SingleShotMode () {return 1 == m_iQueueSize;} + virtual void SetQueueSize (int size); + IC int GetQueueSize () const {return m_iQueueSize;}; + virtual bool StopedAfterQueueFired () {return m_bStopedAfterQueueFired; } + virtual void StopedAfterQueueFired (bool value){m_bStopedAfterQueueFired = value; } + +protected: + //максимальный размер очереди, которой можно стрельнуть + int m_iQueueSize; + //количество реально выстреляных патронов + int m_iShotNum; + //после какого выстрела, при непрерывной стрельбе, начинается отдача (сделано из-за Абакана) + int m_iRecoilStartShotNum; + //скорострельность для патронов, на которые не влияет отдача (сделано из-за Абакана) + float m_fNoRecoilTimeToFire; + //минимальная задержка (скорострельность) между очередями или одиночными выстрелами + float m_fTimeToFireSemi; + //смещение угла линии стрельбы относительно линии прицеливания + Fvector m_vFireDirectionOffset; + // [7/20/2005] + //флаг того, что мы остановились после того как выстреляли + //ровно столько патронов, сколько было задано в m_iQueueSize + bool m_bStopedAfterQueueFired; + //флаг того, что хотя бы один выстрел мы должны сделать + //(даже если очень быстро нажали на курок и вызвалось FireEnd) + bool m_bFireSingleShot; + //максимальная допустимая задержка между началом стрельбы (кликом) и первым выстрелом + float m_fShotMaxDelay; + //режимы стрельбы + bool m_bHasDifferentFireModes; + xr_vector m_aFireModes; + int m_iCurFireMode; + string16 m_sCurFireMode; + int m_iPrefferedFireMode; + + float m_conditionDecreasePerQueueShot; //увеличение изношености при выстреле очередью + + //переменная блокирует использование + //только разных типов патронов + bool m_bLockType; + + ////////////////////////////////////////////// + // режим приближения + ////////////////////////////////////////////// +public: + virtual void OnZoomIn (); + virtual void OnZoomOut (); + virtual void OnNextFireMode (); + virtual void OnPrevFireMode (); + virtual bool HasFireModes () { return m_bHasDifferentFireModes; }; + virtual int GetCurrentFireMode () { return m_aFireModes[m_iCurFireMode]; }; + virtual LPCSTR GetCurrentFireModeStr () {return m_sCurFireMode;}; + +protected: + virtual bool install_upgrade_impl( LPCSTR section, bool test ); + +protected: + virtual bool AllowFireWhileWorking() {return false;} + + //виртуальные функции для проигрывания анимации HUD + virtual void PlayAnimShow (); + virtual void PlayAnimHide (); + virtual void PlayAnimReload (); + virtual void PlayAnimIdle (); + virtual void PlayAnimShoot (); + virtual void PlayReloadSound (); + virtual void PlayAnimAim (); + + virtual int ShotsFired () { return m_iShotNum; } + virtual float GetWeaponDeterioration (); + virtual void RemoveZoomInertionEffector(); + +}; + +#endif //__XR_WEAPON_MAG_H__ diff --git a/src/xrGameLA/WeaponMagazinedWGrenade.cpp b/src/xrGameLA/WeaponMagazinedWGrenade.cpp new file mode 100644 index 000000000..51619e7b7 --- /dev/null +++ b/src/xrGameLA/WeaponMagazinedWGrenade.cpp @@ -0,0 +1,876 @@ +#include "stdafx.h" +#include "weaponmagazinedwgrenade.h" +#include "player_hud.h" +#include "HUDManager.h" +#include "entity.h" +#include "ParticlesObject.h" +#include "GrenadeLauncher.h" +#include "xrserver_objects_alife_items.h" +#include "ExplosiveRocket.h" +#include "Actor_Flags.h" +#include "xr_level_controller.h" +#include "level.h" +#include "../../Include/xrRender/Kinematics.h" +#include "object_broker.h" +#include "game_base_space.h" +#include "MathUtils.h" +#include "clsid_game.h" +#ifdef DEBUG +#include "phdebug.h" +#endif + +CWeaponMagazinedWGrenade::CWeaponMagazinedWGrenade(LPCSTR name,ESoundTypes eSoundType) : CWeaponMagazined(name, eSoundType) +{ + inactiveAmmoIndex_ = 0; + grenadeMode_ = false; +} + +CWeaponMagazinedWGrenade::~CWeaponMagazinedWGrenade(void) +{ +} +void CWeaponMagazinedWGrenade::Load (LPCSTR section) +{ + inherited::Load (section); + CRocketLauncher::Load (section); + + + m_sounds.LoadSound(section,"snd_shoot_grenade" , "sndShotG" , false, m_eSoundShot); + m_sounds.LoadSound(section,"snd_reload_grenade" , "sndReloadG" , true, m_eSoundReload); + m_sounds.LoadSound(section,"snd_switch" , "sndSwitch" , true, m_eSoundReload); + + m_sFlameParticles2 = pSettings->r_string(section, "grenade_flame_particles"); + + if(m_eGrenadeLauncherStatus == ALife::eAddonPermanent) + { + CRocketLauncher::m_fLaunchSpeed = pSettings->r_float(section, "grenade_vel"); + } + + // load ammo classes SECOND (grenade_class) + ammoList2_.clear(); + LPCSTR S = pSettings->r_string(section,"grenade_class"); + if (S && S[0]) + { + string128 _ammoItem; + int count = _GetItemCount (S); + for (int it=0; it l_magazine; + while(m_magazine.size()) { l_magazine.push_back(m_magazine.back()); m_magazine.pop_back(); } + while (inactiveMagazine_.size()) { m_magazine.push_back(inactiveMagazine_.back()); inactiveMagazine_.pop_back(); } + while (l_magazine.size()) { inactiveMagazine_.push_back(l_magazine.back()); l_magazine.pop_back(); } + iAmmoElapsed = (int)m_magazine.size(); + + m_dwAmmoCurrentCalcFrame = 0; +} + +bool CWeaponMagazinedWGrenade::Action(u16 cmd, u32 flags) +{ + if (grenadeMode_ && cmd == kWPN_FIRE) + { + if(IsPending()) + return false; + + if(flags&CMD_START) + { + if(iAmmoElapsed) + LaunchGrenade (); + else + Reload (); + + if(GetState() == eIdle) + OnEmptyClick (); + } + return true; + } + if(inherited::Action(cmd, flags)) + return true; + + switch(cmd) + { + case kWPN_FUNC: + { + if(flags&CMD_START && !IsPending()) + SwitchState(eSwitch); + return true; + } + } + return false; +} + +#include "inventory.h" +#include "actor.h" +#include "inventoryOwner.h" +void CWeaponMagazinedWGrenade::state_Fire(float dt) +{ + VERIFY(fTimeToFire>0.f); + + inherited::state_Fire(dt); +} + +void CWeaponMagazinedWGrenade::OnEvent(NET_Packet& P, u16 type) +{ + inherited::OnEvent(P,type); + u16 id; + switch (type) + { + case GE_OWNERSHIP_TAKE: + { + P.r_u16(id); + CRocketLauncher::AttachRocket(id, this); + } + break; + case GE_OWNERSHIP_REJECT : + case GE_LAUNCH_ROCKET : + { + bool bLaunch = (type==GE_LAUNCH_ROCKET); + P.r_u16 (id); + CRocketLauncher::DetachRocket(id, bLaunch); + if(bLaunch) + { + PlayAnimShoot (); + PlaySound ("sndShotG", get_LastFP2()); + AddShotEffector (); + StartFlameParticles2(); + } + break; + } + } +} + +void CWeaponMagazinedWGrenade::LaunchGrenade() +{ + if(!getRocketCount()) return; + R_ASSERT(grenadeMode_); + + { + Fvector p1, d; + p1.set (get_LastFP2()); + d.set (get_LastFD()); + CEntity* E = smart_cast(H_Parent()); + + if (E){ + CInventoryOwner* io = smart_cast(H_Parent()); + if(NULL == io->inventory().ActiveItem()) + { + Log("current_state", GetState() ); + Log("next_state", GetNextState()); + Log("item_sect", cNameSect().c_str()); + Log("H_Parent", H_Parent()->cNameSect().c_str()); + } + E->g_fireParams (this, p1,d); + } + if (IsGameTypeSingle()) + p1.set (get_LastFP2()); + + Fmatrix launch_matrix; + launch_matrix.identity(); + launch_matrix.k.set(d); + Fvector::generate_orthonormal_basis(launch_matrix.k, + launch_matrix.j, + launch_matrix.i); + + launch_matrix.c.set (p1); + + if(IsGameTypeSingle() && IsZoomed() && smart_cast(H_Parent())) + { + H_Parent()->setEnabled(FALSE); + setEnabled(FALSE); + + collide::rq_result RQ; + BOOL HasPick = Level().ObjectSpace.RayPick(p1, d, 300.0f, collide::rqtStatic, RQ, this); + + setEnabled(TRUE); + H_Parent()->setEnabled(TRUE); + + if (HasPick) + { + Fvector Transference; + Transference.mul(d, RQ.range); + Fvector res[2]; +#ifdef DEBUG +//. DBG_OpenCashedDraw(); +//. DBG_DrawLine(p1,Fvector().add(p1,d),D3DCOLOR_XRGB(255,0,0)); +#endif + u8 canfire0 = TransferenceAndThrowVelToThrowDir(Transference, + CRocketLauncher::m_fLaunchSpeed, + EffectiveGravity(), + res); +#ifdef DEBUG +//. if(canfire0>0)DBG_DrawLine(p1,Fvector().add(p1,res[0]),D3DCOLOR_XRGB(0,255,0)); +//. if(canfire0>1)DBG_DrawLine(p1,Fvector().add(p1,res[1]),D3DCOLOR_XRGB(0,0,255)); +//. DBG_ClosedCashedDraw(30000); +#endif + + if (canfire0 != 0) + { + d = res[0]; + }; + } + }; + + d.normalize(); + d.mul(CRocketLauncher::m_fLaunchSpeed); + VERIFY2(_valid(launch_matrix),"CWeaponMagazinedWGrenade::SwitchState. Invalid launch_matrix!"); + CRocketLauncher::LaunchRocket(launch_matrix, d, zero_vel); + + CExplosiveRocket* pGrenade = smart_cast(getCurrentRocket()/*m_pRocket*/); + VERIFY(pGrenade); + pGrenade->SetInitiator(H_Parent()->ID()); + + + if (Local() && OnServer()) + { + VERIFY (m_magazine.size()); + m_magazine.pop_back (); + --iAmmoElapsed; + VERIFY((u32)iAmmoElapsed == m_magazine.size()); + + NET_Packet P; + u_EventGen (P,GE_LAUNCH_ROCKET,ID()); + P.w_u16 (getCurrentRocket()->ID()); + u_EventSend (P); + }; + + } +} + +void CWeaponMagazinedWGrenade::FireEnd() +{ + if (grenadeMode_) + { + CWeapon::FireEnd(); + }else + inherited::FireEnd(); +} + +void CWeaponMagazinedWGrenade::OnMagazineEmpty() +{ + if(GetState() == eIdle) + { + OnEmptyClick (); + } +} + +void CWeaponMagazinedWGrenade::ReloadMagazine() +{ + inherited::ReloadMagazine(); + + //перезарядка подствольного гранатомета + if (iAmmoElapsed && !getRocketCount() && grenadeMode_) + { + shared_str fake_grenade_name = pSettings->r_string(m_ammoTypes[m_ammoType].c_str(), "fake_grenade_name"); + + CRocketLauncher::SpawnRocket(*fake_grenade_name, this); + } +} + + +void CWeaponMagazinedWGrenade::OnStateSwitch(u32 S) +{ + + switch (S) + { + case eSwitch: + { + if( !SwitchMode() ){ + SwitchState(eIdle); + return; + } + }break; + } + + inherited::OnStateSwitch(S); + UpdateGrenadeVisibility(!!iAmmoElapsed || S == eReload); +} + + +void CWeaponMagazinedWGrenade::OnAnimationEnd(u32 state) +{ + switch (state) + { + case eSwitch: + { + SwitchState(eIdle); + }break; + case eFire: + { + if (grenadeMode_) + Reload(); + }break; + } + inherited::OnAnimationEnd(state); +} + + +void CWeaponMagazinedWGrenade::OnH_B_Independent(bool just_before_destroy) +{ + inherited::OnH_B_Independent(just_before_destroy); + + SetPending (FALSE); + if (grenadeMode_) { + SetState ( eIdle ); + SetPending (FALSE); + } +} + +bool CWeaponMagazinedWGrenade::CanAttach(PIItem pIItem) +{ + CGrenadeLauncher* pGrenadeLauncher = smart_cast(pIItem); + + if(pGrenadeLauncher && + CSE_ALifeItemWeapon::eAddonAttachable == m_eGrenadeLauncherStatus && + 0 == (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher) && + !xr_strcmp(*m_sGrenadeLauncherName, pIItem->object().cNameSect())) + return true; + else + return inherited::CanAttach(pIItem); +} + +bool CWeaponMagazinedWGrenade::CanDetach(const char* item_section_name) +{ + if(CSE_ALifeItemWeapon::eAddonAttachable == m_eGrenadeLauncherStatus && + 0 != (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher) && + !xr_strcmp(*m_sGrenadeLauncherName, item_section_name)) + return true; + else + return inherited::CanDetach(item_section_name); +} + +bool CWeaponMagazinedWGrenade::Attach(PIItem pIItem, bool b_send_event) +{ + CGrenadeLauncher* pGrenadeLauncher = smart_cast(pIItem); + + if(pGrenadeLauncher && + CSE_ALifeItemWeapon::eAddonAttachable == m_eGrenadeLauncherStatus && + 0 == (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher) && + !xr_strcmp(*m_sGrenadeLauncherName, pIItem->object().cNameSect())) + { + m_flagsAddOnState |= CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher; + + CRocketLauncher::m_fLaunchSpeed = pGrenadeLauncher->GetGrenadeVel(); + + //уничтожить подствольник из инвентаря + if(b_send_event) + { + if (OnServer()) + pIItem->object().DestroyObject (); + } + InitAddons (); + UpdateAddonsVisibility (); + + if(GetState()==eIdle) + PlayAnimIdle (); + + return true; + } + else + return inherited::Attach(pIItem, b_send_event); +} + +bool CWeaponMagazinedWGrenade::Detach(const char* item_section_name, bool b_spawn_item) +{ + if (CSE_ALifeItemWeapon::eAddonAttachable == m_eGrenadeLauncherStatus && + 0 != (m_flagsAddOnState&CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher) && + !xr_strcmp(*m_sGrenadeLauncherName, item_section_name)) + { + m_flagsAddOnState &= ~CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher; + if (!grenadeMode_) + { + PerformSwitchGL(); + } + UnloadMagazine(); + PerformSwitchGL(); + + UpdateAddonsVisibility(); + + if(GetState()==eIdle) + PlayAnimIdle (); + + return CInventoryItemObject::Detach(item_section_name, b_spawn_item); + } + else + return inherited::Detach(item_section_name, b_spawn_item); +} + + + + +void CWeaponMagazinedWGrenade::InitAddons() +{ + inherited::InitAddons(); + + if(GrenadeLauncherAttachable()) + { + if(IsGrenadeLauncherAttached()) + { + CRocketLauncher::m_fLaunchSpeed = pSettings->r_float(*m_sGrenadeLauncherName,"grenade_vel"); + } + } +} + +bool CWeaponMagazinedWGrenade::UseScopeTexture() +{ + if (IsGrenadeLauncherAttached() && grenadeMode_) return false; + + return true; +}; + +float CWeaponMagazinedWGrenade::CurrentZoomFactor () +{ + if (IsGrenadeLauncherAttached() && grenadeMode_) return m_zoom_params.m_fIronSightZoomFactor; + return inherited::CurrentZoomFactor(); +} + +//виртуальные функции для проигрывания анимации HUD +void CWeaponMagazinedWGrenade::PlayAnimShow() +{ + VERIFY(GetState()==eShowing); + if(IsGrenadeLauncherAttached()) + { + if (!grenadeMode_) + PlayHUDMotion("anim_show_w_gl", FALSE, this, GetState()); + else + PlayHUDMotion("anim_show_g", FALSE, this, GetState()); + } + else + PlayHUDMotion("anim_show", FALSE, this, GetState()); +} + +void CWeaponMagazinedWGrenade::PlayAnimHide() +{ + VERIFY(GetState()==eHiding); + + if(IsGrenadeLauncherAttached()) + if (!grenadeMode_) + PlayHUDMotion("anim_hide_w_gl", TRUE, this, GetState()); + else + PlayHUDMotion("anim_hide_g", TRUE, this, GetState()); + + else + PlayHUDMotion("anim_hide", TRUE, this, GetState()); +} + +void CWeaponMagazinedWGrenade::PlayAnimReload() +{ + VERIFY(GetState()==eReload); + + if(IsGrenadeLauncherAttached()) + PlayHUDMotion("anim_reload_w_gl", TRUE, this, GetState()); + else + inherited::PlayAnimReload(); +} + +void CWeaponMagazinedWGrenade::PlayAnimIdle() +{ + if(IsGrenadeLauncherAttached()) + { + if(IsZoomed()) + { + if (grenadeMode_) + PlayHUDMotion("anim_idle_g_aim", TRUE, NULL, GetState()); + else + PlayHUDMotion("anim_idle_w_gl_aim", TRUE, NULL, GetState()); + }else + { + int act_state = 0; + CActor* pActor = smart_cast(H_Parent()); + if(pActor) + { + CEntity::SEntityState st; + pActor->g_State(st); + if(st.bSprint) + { + act_state = 1; + }else + if(pActor->AnyMove()) + { + act_state = 2; + } + } + + if (grenadeMode_) + { + if(act_state==0) + PlayHUDMotion("anim_idle_g", TRUE, NULL, GetState()); + else + if(act_state==1) + PlayHUDMotion("anim_idle_sprint_g", TRUE, NULL,GetState()); + else + if(act_state==2) + PlayHUDMotion("anim_idle_moving_g", TRUE, NULL,GetState()); + + }else + { + if(act_state==0) + PlayHUDMotion("anim_idle_w_gl", TRUE, NULL, GetState()); + else + if(act_state==1) + PlayHUDMotion("anim_idle_sprint_w_gl", TRUE, NULL,GetState()); + else + if(act_state==2) + PlayHUDMotion("anim_idle_moving_w_gl", TRUE, NULL,GetState()); + } + + } + } + else + inherited::PlayAnimIdle(); +} + +void CWeaponMagazinedWGrenade::PlayAnimShoot() +{ + if (grenadeMode_) + { + PlayHUDMotion("anim_shots_g" ,FALSE, this, eFire); + } + else + { + VERIFY(GetState()==eFire); + if(IsGrenadeLauncherAttached()) + PlayHUDMotion("anim_shots_w_gl" ,FALSE, this, GetState()); + + else + inherited::PlayAnimShoot(); + } +} + +void CWeaponMagazinedWGrenade::PlayAnimModeSwitch() +{ + if (grenadeMode_) + PlayHUDMotion("anim_switch_g" , FALSE, this, eSwitch); + else + PlayHUDMotion("anim_switch" , FALSE, this, eSwitch); +} + +void CWeaponMagazinedWGrenade::PlayAnimBore() +{ + if(IsGrenadeLauncherAttached()) + { + if (grenadeMode_) + PlayHUDMotion ("anim_bore_g", TRUE, this, GetState()); + else + PlayHUDMotion ("anim_bore_w_gl", TRUE, this, GetState()); + }else + inherited::PlayAnimBore(); +} + + +void CWeaponMagazinedWGrenade::UpdateSounds () +{ + inherited::UpdateSounds (); + + Fvector P = get_LastFP(); + m_sounds.SetPosition("sndShotG", P); + m_sounds.SetPosition("sndReloadG", P); + m_sounds.SetPosition("sndSwitch", P); +} + +void CWeaponMagazinedWGrenade::UpdateGrenadeVisibility(bool visibility) +{ + if(!GetHUDmode()) return; + HudItemData()->set_bone_visible ("grenade", visibility, TRUE); +} + +BOOL CWeaponMagazinedWGrenade::net_Spawn(CSE_Abstract* server_entity) +{ + BOOL bResult = CHudItemObject::net_Spawn(server_entity); // обходим нет спавн базового оружия(чтобы не перетолковать одни и те же переменные) отрабатываем нетспавн худ итема и нетспавним этот класс по собственному алгоритму + + CSE_ALifeItemWeaponMagazinedWGL* const weapon_gl = smart_cast(server_entity); + + R_ASSERT(weapon_gl); + + // Загрузить с сервера то, что мы экспортировали через ::net_export + m_iCurFireMode = weapon_gl->m_u8CurFireMode; + + m_flagsAddOnState = weapon_gl->m_addon_flags.get(); + m_cur_scope = weapon_gl->m_cur_scope; + + SetState (weapon_gl->wpn_state); + SetNextState (weapon_gl->wpn_state); + + grenadeMode_ = weapon_gl->grenadeMode_server; + + m_magazine.clear(); + inactiveMagazine_.clear(); + + // Тут немного сложно, но пока по другому никак. В зависимости от того, включен ли режим гранатомета, для магазина патронов и магазина подствола выберается активный или неактивный магазины для хранения зарядов + xr_vector& grenade_mag = grenadeMode_ ? m_magazine : inactiveMagazine_; // указатель на переменную хронящую заряды для подствольника + xr_vector& regular_ammo_mag = grenadeMode_ ? inactiveMagazine_ : m_magazine; // указатель на переменную хронящую заряды для обычного магазина + + u8& grenade_ammo_index = grenadeMode_ ? m_ammoType : inactiveAmmoIndex_; // указатель на переменную хронящую тип гранат + u8& regular_ammo_index = grenadeMode_ ? inactiveAmmoIndex_ : m_ammoType; // указатель на переменную хронящую тип патронов + + if (grenadeMode_) + maxMagazineSize_ = 1; + + // Загрузить подствольник + grenade_ammo_index = weapon_gl->grndID_; + + if (grenade_ammo_index >= ammoList2_.size()) + { + Msg("! grenade ammo index %d is out of ammo types, should be less than %d. Item section is [%s]. Probably default ammo upgrade is the reason", m_ammoType, ammoList2_.size(), cNameSect_str()); + grenade_ammo_index = 0; + } + + if (weapon_gl->grndIsLoaded_) + { + m_DefaultCartridge2.Load(ammoList2_[grenade_ammo_index].c_str(), grenade_ammo_index); + grenade_mag.push_back(m_DefaultCartridge2); + } + + + // Загрузить обычный магазин + regular_ammo_index = weapon_gl->ammo_type; + if (regular_ammo_index >= m_ammoTypes.size()) + { + Msg("! ammo index %d is out of ammo types, should be less than %d. Item section is [%s]. Probably default ammo upgrade is the reason", regular_ammo_index, m_ammoTypes.size(), cNameSect_str()); + grenade_ammo_index = 0; + } + + m_DefaultCartridge.Load(m_ammoTypes[regular_ammo_index].c_str(), regular_ammo_index); + if (weapon_gl->a_elapsed) + { + m_fCurrentCartirdgeDisp = m_DefaultCartridge.param_s.kDisp; + for (int i = 0; i < weapon_gl->a_elapsed; ++i) + regular_ammo_mag.push_back(m_DefaultCartridge); + } + + R_ASSERT(weapon_gl->grndIsLoaded_ == grenade_mag.size()); + R_ASSERT(weapon_gl->a_elapsed == regular_ammo_mag.size()); + + iAmmoElapsed = grenadeMode_ ? grenade_mag.size() : regular_ammo_mag.size(); + + //Get apropriate ammo list for active magazine + if (grenadeMode_) + m_ammoTypes.swap(ammoList2_); // if not grenade mode - no need to swap because default is regular ammo list + + PlayAnimModeSwitch(); // switch to apropriate animation type + + UpdateGrenadeVisibility(grenade_mag.size()>0); + UpdateAddonsVisibility(); + + SetPending(FALSE); + + InitAddons(); + + m_dwWeaponIndependencyTime = 0; + + if (weapon_gl->grndIsLoaded_) // load actual missle into gr launcher, if its magazine is not empty + { + shared_str fake_grenade_name = pSettings->r_string(grenade_mag.back().m_ammoSect, "fake_grenade_name"); + + CRocketLauncher::SpawnRocket(*fake_grenade_name, this); + } + + return bResult; +} + +void CWeaponMagazinedWGrenade::net_Export(NET_Packet& P) +{ + // for weapon with grenade addon we cant call inherited weapon export, we have to store values in different way taking in attention active/inactive magazines + CHudItemObject::net_Export(P); + + //server weapon data + u8 need_upd = IsUpdating() ? 1 : 0; + P.w_u8 (need_upd); + P.w_u16 (grenadeMode_ ? inactiveMagazine_.size() : m_magazine.size()); + P.w_u8 (m_cur_scope); + P.w_u8 (m_flagsAddOnState); + P.w_u8 (grenadeMode_ ? inactiveAmmoIndex_ : m_ammoType); + P.w_u8 ((u8)GetState()); + P.w_u8 ((u8)IsZoomed()); + + //server w_magazined data + P.w_u8 (u8(m_iCurFireMode & 0x00ff)); + + //server w_grenade_laucher data + P.w_u8 (grenadeMode_ ? 1 : 0); + P.w_u8 (grenadeMode_ ? m_magazine.size() : inactiveMagazine_.size()); + P.w_u8 (grenadeMode_ ? m_ammoType : inactiveAmmoIndex_); + + //Remove this debug after couple weaks of tests =) + //Msg("need_upd = %u, m_magazine.size = %u, m_cur_scope = %u, m_flagsAddOnState = %u, m_ammoType = %u, inactiveAmmoIndex_ %u", need_upd, m_magazine.size(), m_cur_scope, m_flagsAddOnState, m_ammoType, inactiveAmmoIndex_); + //Msg("GetState() = %u, IsZoomed() = %d, m_iCurFireMode = %u, grenadeMode_ = %u, inactiveMagazine_.size = %u", GetState(), IsZoomed(), (m_iCurFireMode & 0x00ff), grenadeMode_, inactiveMagazine_.size()); +} + +void CWeaponMagazinedWGrenade::net_Import(NET_Packet& P) +{ + inherited::net_Import(P); + + Msg("CWeaponMagazinedWGrenade::net_Import not active in SP"); +} + +void CWeaponMagazinedWGrenade::save(NET_Packet &output_packet) +{ + inherited::save (output_packet); +} + +void CWeaponMagazinedWGrenade::load(IReader &input_packet) +{ + inherited::load (input_packet); +} + +bool CWeaponMagazinedWGrenade::IsNecessaryItem (const shared_str& item_sect) +{ + return ( std::find(m_ammoTypes.begin(), m_ammoTypes.end(), item_sect) != m_ammoTypes.end() || + std::find(ammoList2_.begin(), ammoList2_.end(), item_sect) != ammoList2_.end() + ); +} + +u8 CWeaponMagazinedWGrenade::GetCurrentHudOffsetIdx() +{ + bool b_aiming = ((IsZoomed() && m_zoom_params.m_fZoomRotationFactor<=1.f) || + (!IsZoomed() && m_zoom_params.m_fZoomRotationFactor>0.f)); + + if(!b_aiming) + return 0; + else + if(grenadeMode_) + return 2; + else + return 1; +} + +bool CWeaponMagazinedWGrenade::install_upgrade_ammo_class ( LPCSTR section, bool test ) +{ + LPCSTR str; + + bool result = process_if_exists(section, "ammo_mag_size", &CInifile::r_s32, inactiveMagMaxSize_, test); + maxMagazineSize_ = grenadeMode_ ? 1 : inactiveMagMaxSize_; + + // ammo_class = ammo_5.45x39_fmj, ammo_5.45x39_ap // name of the ltx-section of used ammo + bool result2 = process_if_exists_set( section, "ammo_class", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + xr_vector& ammo_types = grenadeMode_ ? ammoList2_ : m_ammoTypes; + ammo_types.clear (); + for ( int i = 0, count = _GetItemCount( str ); i < count; ++i ) + { + string128 ammo_item; + _GetItem ( str, i, ammo_item ); + ammo_types.push_back ( ammo_item ); + } + + m_ammoType = 0; + inactiveAmmoIndex_ = 0; + } + result |= result2; + + return result2; +} + +bool CWeaponMagazinedWGrenade::install_upgrade_impl( LPCSTR section, bool test ) +{ + LPCSTR str; + bool result = inherited::install_upgrade_impl( section, test ); + + // grenade_class = ammo_vog-25, ammo_vog-25p // name of the ltx-section of used grenades + bool result2 = process_if_exists_set( section, "grenade_class", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + xr_vector& ammo_types = !grenadeMode_ ? ammoList2_ : m_ammoTypes; + ammo_types.clear (); + for ( int i = 0, count = _GetItemCount( str ); i < count; ++i ) + { + string128 ammo_item; + _GetItem ( str, i, ammo_item ); + ammo_types.push_back ( ammo_item ); + } + + m_ammoType = 0; + inactiveAmmoIndex_ = 0; + } + result |= result2; + + result |= process_if_exists( section, "launch_speed", &CInifile::r_float, m_fLaunchSpeed, test ); + + result2 = process_if_exists_set( section, "snd_shoot_grenade", &CInifile::r_string, str, test ); + if ( result2 && !test ) { m_sounds.LoadSound( section, "snd_shoot_grenade", "sndShotG", false, m_eSoundShot ); } + result |= result2; + + result2 = process_if_exists_set( section, "snd_reload_grenade", &CInifile::r_string, str, test ); + if ( result2 && !test ) { m_sounds.LoadSound( section, "snd_reload_grenade", "sndReloadG", true, m_eSoundReload ); } + result |= result2; + + result2 = process_if_exists_set( section, "snd_switch", &CInifile::r_string, str, test ); + if ( result2 && !test ) { m_sounds.LoadSound( section, "snd_switch", "sndSwitch", true, m_eSoundReload ); } + result |= result2; + + return result; +} diff --git a/src/xrGameLA/WeaponMagazinedWGrenade.h b/src/xrGameLA/WeaponMagazinedWGrenade.h new file mode 100644 index 000000000..028ff2dc3 --- /dev/null +++ b/src/xrGameLA/WeaponMagazinedWGrenade.h @@ -0,0 +1,95 @@ +#pragma once +#include "weaponmagazined.h" +#include "rocketlauncher.h" + + +class CWeaponFakeGrenade; + + +class CWeaponMagazinedWGrenade : public CWeaponMagazined, + public CRocketLauncher +{ + typedef CWeaponMagazined inherited; +public: + CWeaponMagazinedWGrenade (LPCSTR name="AK74",ESoundTypes eSoundType=SOUND_TYPE_WEAPON_SUBMACHINEGUN); + virtual ~CWeaponMagazinedWGrenade (); + + virtual void Load (LPCSTR section); + + // Happens when object gets online. Import saved data from server here. Do oject initialization based on data imported from server + virtual BOOL net_Spawn (CSE_Abstract* DC); // Note, this particular class does not call inherided weapon net Spawn, instead it calls huditem object net Spawn directly + virtual void net_Destroy (); + // constant export of various weapon data to server. Here should be stuff, needed to be saved before object switches offline. Than it should be imported in net_spawn, when object gets online again + virtual void net_Export (NET_Packet& P); // Note, this particular class does not call inherided weapon net Export, instead it calls huditem object net Export directly + // not happens in Single Player + virtual void net_Import (NET_Packet& P); + + virtual void OnH_B_Independent (bool just_before_destroy); + + // save stuff that is needed while weapon is in alife radius + virtual void save (NET_Packet &output_packet); + // load stuff needed in alife radius + virtual void load (IReader &input_packet); + + + virtual bool Attach(PIItem pIItem, bool b_send_event); + virtual bool Detach(const char* item_section_name, bool b_spawn_item); + virtual bool CanAttach(PIItem pIItem); + virtual bool CanDetach(const char* item_section_name); + virtual void InitAddons(); + virtual bool UseScopeTexture(); + virtual float CurrentZoomFactor (); + + virtual u8 GetCurrentHudOffsetIdx(); + virtual void FireEnd (); + void LaunchGrenade (); + + virtual void OnStateSwitch (u32 S); + + virtual void switch2_Reload (); + virtual void state_Fire (float dt); + virtual void OnShot (); + virtual void OnEvent (NET_Packet& P, u16 type); + virtual void ReloadMagazine (); + + virtual bool Action (u16 cmd, u32 flags); + + virtual void UpdateSounds (); + + //переключение в режим подствольника + virtual bool SwitchMode (); + void PerformSwitchGL (); + void OnAnimationEnd (u32 state); + virtual void OnMagazineEmpty (); + + virtual bool IsNecessaryItem (const shared_str& item_sect); + + //виртуальные функции для проигрывания анимации HUD + virtual void PlayAnimShow (); + virtual void PlayAnimHide (); + virtual void PlayAnimReload (); + virtual void PlayAnimIdle (); + virtual void PlayAnimShoot (); + virtual void PlayAnimModeSwitch (); + virtual void PlayAnimBore (); + + //Fake grenade visability + void UpdateGrenadeVisibility(bool visibility); + +private: + virtual bool install_upgrade_impl ( LPCSTR section, bool test ); + virtual bool install_upgrade_ammo_class ( LPCSTR section, bool test ); + +public: + //дополнительные параметры патронов + //для подствольника +//- CWeaponAmmo* m_pAmmo2; + xr_vector ammoList2_; // переменная для временного хранения типов патронов неактивного магазина. Так же при инициализации хранит типы патронов для подствольника + u8 inactiveAmmoIndex_; // переменная для временного хранения индекса типа патрона неактивного магазина. + + int inactiveMagMaxSize_; // переменная для временного хранения макс размера неактивного магазина + xr_vector inactiveMagazine_; // переменная для временного хранения содержимого неактивного магазина + bool grenadeMode_; + + CCartridge m_DefaultCartridge2; +}; \ No newline at end of file diff --git a/src/xrGameLA/WeaponMounted.cpp b/src/xrGameLA/WeaponMounted.cpp new file mode 100644 index 000000000..6e2708b21 --- /dev/null +++ b/src/xrGameLA/WeaponMounted.cpp @@ -0,0 +1,373 @@ +#include "stdafx.h" +#pragma hdrstop + +#include "WeaponMounted.h" +#include "xrServer_Objects_ALife.h" +#include "camerafirsteye.h" +#include "actor.h" +#include "weaponammo.h" + + +#include "actoreffector.h" +#include "effectorshot.h" +#include "ai_sounds.h" +#include "level.h" +#include "xr_level_controller.h" +#include "../Include/xrRender/Kinematics.h" +#include "game_object_space.h" + +//---------------------------------------------------------------------------------------- + +void CWeaponMounted::BoneCallbackX(CBoneInstance *B) +{ + CWeaponMounted *P = static_cast(B->callback_param()); + + if (P->Owner()){ + Fmatrix rX; rX.rotateX (P->camera->pitch+P->m_dAngle.y); + B->mTransform.mulB_43(rX); + } +} + +void CWeaponMounted::BoneCallbackY(CBoneInstance *B) +{ + CWeaponMounted *P = static_cast(B->callback_param()); + + if (P->Owner()){ + Fmatrix rY; rY.rotateY (P->camera->yaw+P->m_dAngle.x); + B->mTransform.mulB_43(rY); + } +} +//---------------------------------------------------------------------------------------- + +CWeaponMounted::CWeaponMounted() +{ + camera = new CCameraFirstEye(this, CCameraBase::flRelativeLink|CCameraBase::flPositionRigid|CCameraBase::flDirectionRigid); + camera->Load ("mounted_weapon_cam"); +} + +CWeaponMounted::~CWeaponMounted() +{ + xr_delete(camera); +} + +void CWeaponMounted::Load(LPCSTR section) +{ + inherited::Load(section); + CShootingObject::Load (section); + + HUD_SOUND_ITEM::LoadSound(section,"snd_shoot", sndShot, SOUND_TYPE_WEAPON_SHOOTING); + + //тип используемых патронов + m_sAmmoType = pSettings->r_string(section, "ammo_class"); + m_CurrentAmmo.Load(*m_sAmmoType, 0); + + //подбрасывание камеры во время отдачи + camMaxAngle = pSettings->r_float (section,"cam_max_angle" ); + camMaxAngle = deg2rad (camMaxAngle); + camRelaxSpeed = pSettings->r_float (section,"cam_relax_speed" ); + camRelaxSpeed = deg2rad (camRelaxSpeed); + +} + +BOOL CWeaponMounted::net_Spawn(CSE_Abstract* DC) +{ + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeMountedWeapon *mw = smart_cast(e); + R_ASSERT (mw); + + if (!inherited::net_Spawn(DC)) + return (FALSE); + + R_ASSERT (Visual() && smart_cast(Visual())); + + IKinematics* K = smart_cast(Visual()); + CInifile* pUserData = K->LL_UserData(); + + R_ASSERT3 (pUserData,"Empty MountedWeapon user data!",mw->get_visual()); + + fire_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","fire_bone")); + actor_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","actor_bone")); + rotate_x_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","rotate_x_bone")); + rotate_y_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","rotate_y_bone")); + camera_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","camera_bone")); + + CBoneData& bdX = K->LL_GetData(rotate_x_bone); VERIFY(bdX.IK_data.type==jtJoint); + camera->lim_pitch.set (bdX.IK_data.limits[0].limit.x,bdX.IK_data.limits[0].limit.y); + CBoneData& bdY = K->LL_GetData(rotate_y_bone); VERIFY(bdY.IK_data.type==jtJoint); + camera->lim_yaw.set (bdY.IK_data.limits[1].limit.x,bdY.IK_data.limits[1].limit.y); + + U16Vec fixed_bones; + fixed_bones.push_back (K->LL_GetBoneRoot()); + PPhysicsShell() = P_build_Shell(this,false,fixed_bones); + K ->CalculateBones_Invalidate(); + K ->CalculateBones(); + + CShootingObject::Light_Create(); + + setVisible (TRUE); + setEnabled (TRUE); + + + + return TRUE; +} + +void CWeaponMounted::net_Destroy() +{ + CShootingObject::Light_Destroy(); + + inherited::net_Destroy(); + xr_delete(m_pPhysicsShell); +} + +void CWeaponMounted::net_Export(NET_Packet& P) +{ + inherited::net_Export(P); +} + +void CWeaponMounted::net_Import(NET_Packet& P) +{ + inherited::net_Import(P); +} + +void CWeaponMounted::UpdateCL() +{ + inherited::UpdateCL (); + if (Owner()){ + IKinematics* K = smart_cast(Visual()); + K->CalculateBones (); + // update fire pos & fire_dir + fire_bone_xform = K->LL_GetTransform(fire_bone); + fire_bone_xform.mulA_43 (XFORM()); + fire_pos.set (0,0,0); + fire_bone_xform.transform_tiny (fire_pos); + fire_dir.set (0,0,1); + fire_bone_xform.transform_dir (fire_dir); + + UpdateFire (); + + if(OwnerActor() && OwnerActor()->IsMyCamera()) + { + cam_Update(Device.fTimeDelta, g_fov); + OwnerActor()->Cameras().UpdateFromCamera(Camera()); + OwnerActor()->Cameras().ApplyDevice(VIEWPORT_NEAR); + } + } +} + +void CWeaponMounted::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); +} + +void CWeaponMounted::renderable_Render() +{ + //нарисовать подсветку + RenderLight(); + + inherited::renderable_Render (); +} + +void CWeaponMounted::OnMouseMove (int dx, int dy) +{ + if (Remote()) return; + + CCameraBase* C = camera; + float scale = (C->f_fov/g_fov)*psMouseSens * psMouseSensScale/50.f; + if (dx){ + float d = float(dx)*scale; + C->Move ((d<0)?kLEFT:kRIGHT, _abs(d)); + } + if (dy){ + float d = ((psMouseInvert.test(1))?-1:1)*float(dy)*scale*3.f/4.f; + C->Move ((d>0)?kUP:kDOWN, _abs(d)); + } +} +void CWeaponMounted::OnKeyboardPress (int dik) +{ + if (Remote()) return; + + switch (dik) + { + case kWPN_FIRE: + FireStart(); + break; + }; + +} +void CWeaponMounted::OnKeyboardRelease (int dik) +{ + if (Remote()) return; + switch (dik) + { + case kWPN_FIRE: + FireEnd(); + break; + }; +} +void CWeaponMounted::OnKeyboardHold (int dik) +{ + if (Remote()) return; + +// switch(dik) +// { +// } +} + +void CWeaponMounted::cam_Update (float dt, float fov) +{ + Fvector P,Da; + Da.set (0,0,0); + + IKinematics* K = smart_cast(Visual()); + K->CalculateBones_Invalidate (); + K->CalculateBones (); + const Fmatrix& C = K->LL_GetTransform(camera_bone); + XFORM().transform_tiny (P,C.c); + + if(OwnerActor()){ + // rotate head + OwnerActor()->Orientation().yaw = -Camera()->yaw; + OwnerActor()->Orientation().pitch = -Camera()->pitch; + } + Camera()->Update (P,Da); + Level().Cameras().UpdateFromCamera (Camera()); +} + +bool CWeaponMounted::Use (const Fvector& pos,const Fvector& dir,const Fvector& foot_pos) +{ + return !Owner(); +} +bool CWeaponMounted::attach_Actor (CGameObject* actor) +{ + m_dAngle.set(0.0f,0.0f); + CHolderCustom::attach_Actor(actor); + IKinematics* K = smart_cast(Visual()); + // убрать оружие из рук + // disable shell callback + m_pPhysicsShell->EnabledCallbacks(FALSE); + // enable actor rotate callback + CBoneInstance& biX = smart_cast(Visual())->LL_GetBoneInstance(rotate_x_bone); + biX.set_callback (bctCustom,BoneCallbackX,this); + CBoneInstance& biY = smart_cast(Visual())->LL_GetBoneInstance(rotate_y_bone); + biY.set_callback (bctCustom,BoneCallbackY,this); + // set actor to mounted position + const Fmatrix& A = K->LL_GetTransform(actor_bone); + Fvector ap; + XFORM().transform_tiny (ap,A.c); + Fmatrix AP; AP.translate(ap); + if(OwnerActor()) OwnerActor()->SetPhPosition (AP); + processing_activate (); + return true; +} +void CWeaponMounted::detach_Actor () +{ + CHolderCustom::detach_Actor(); + // disable actor rotate callback + CBoneInstance& biX = smart_cast(Visual())->LL_GetBoneInstance(rotate_x_bone); + biX.reset_callback (); + CBoneInstance& biY = smart_cast(Visual())->LL_GetBoneInstance(rotate_y_bone); + biY.reset_callback (); + // enable shell callback + m_pPhysicsShell->EnabledCallbacks(TRUE); + + //закончить стрельбу + FireEnd(); + + processing_deactivate (); +} + +Fvector CWeaponMounted::ExitPosition () +{ + return XFORM().c; +} + +CCameraBase* CWeaponMounted::Camera () +{ + return camera; +} + + +void CWeaponMounted::FireStart() +{ + m_dAngle.set(0.0f,0.0f); + CShootingObject::FireStart(); +} +void CWeaponMounted::FireEnd() +{ + m_dAngle.set(0.0f,0.0f); + CShootingObject::FireEnd(); + StopFlameParticles (); + RemoveShotEffector (); +} + + +void CWeaponMounted::OnShot () +{ + VERIFY(Owner()); + + FireBullet(get_CurrentFirePoint(),fire_dir, + fireDispersionBase, + m_CurrentAmmo, Owner()->ID(),ID(), SendHitAllowed(Owner())); + + StartShotParticles (); + + if(m_bLightShotEnabled) + Light_Start (); + + StartFlameParticles(); + StartSmokeParticles(fire_pos, zero_vel); + OnShellDrop(fire_pos, zero_vel); + + bool b_hud_mode = (Level().CurrentEntity() == smart_cast(Owner())); + HUD_SOUND_ITEM::PlaySound(sndShot, fire_pos, Owner(), b_hud_mode); + + //добавить эффектор стрельбы + AddShotEffector (); + m_dAngle.set( ::Random.randF(-fireDispersionBase,fireDispersionBase), + ::Random.randF(-fireDispersionBase,fireDispersionBase)); +} + +void CWeaponMounted::UpdateFire() +{ + fTime -= Device.fTimeDelta; + + + CShootingObject::UpdateFlameParticles(); + CShootingObject::UpdateLight(); + + if(!IsWorking()){ + if(fTime<0) fTime = 0.f; + return; + } + + if(fTime<=0){ + OnShot(); + fTime += fTimeToFire; + }else{ + angle_lerp (m_dAngle.x,0.f,5.f,Device.fTimeDelta); + angle_lerp (m_dAngle.y,0.f,5.f,Device.fTimeDelta); + } +} + +const Fmatrix& CWeaponMounted::get_ParticlesXFORM () +{ + return fire_bone_xform; +} + +void CWeaponMounted::AddShotEffector () +{ + if(OwnerActor()) + { + CCameraShotEffector* S = smart_cast(OwnerActor()->Cameras().GetCamEffector(eCEShot)); + if (!S) S = (CCameraShotEffector*)OwnerActor()->Cameras().AddCamEffector(new CCameraShotEffector(camMaxAngle,camRelaxSpeed, 0.25f, 0.01f, 0.7f)); + R_ASSERT (S); + S->Shot (0.01f); + } +} + +void CWeaponMounted::RemoveShotEffector () +{ + if(OwnerActor()) + OwnerActor()->Cameras().RemoveCamEffector (eCEShot); +} \ No newline at end of file diff --git a/src/xrGameLA/WeaponMounted.h b/src/xrGameLA/WeaponMounted.h new file mode 100644 index 000000000..0229250bc --- /dev/null +++ b/src/xrGameLA/WeaponMounted.h @@ -0,0 +1,104 @@ +#ifndef WeaponMountedH +#define WeaponMountedH +#pragma once + +#include "holder_custom.h" +#include "shootingobject.h" + +#include "hudsound.h" +#include "weaponammo.h" +#include "physicsshellholder.h" + +class CWeaponMounted : public CPhysicsShellHolder, + public CHolderCustom, + public CShootingObject +{ +private: + ////////////////////////////////////////////////////////////////////////// + // General + ////////////////////////////////////////////////////////////////////////// + typedef CPhysicsShellHolder inherited; + CCameraBase* camera; + u16 fire_bone; + u16 actor_bone; + u16 rotate_x_bone; + u16 rotate_y_bone; + u16 camera_bone; + + Fvector fire_pos, fire_dir; + Fmatrix fire_bone_xform; + Fvector2 m_dAngle; + static void __stdcall BoneCallbackX (CBoneInstance *B); + static void __stdcall BoneCallbackY (CBoneInstance *B); +public: + CWeaponMounted (); + virtual ~CWeaponMounted (); + + // for shooting object + virtual const Fvector& get_CurrentFirePoint() {return fire_pos;} + virtual const Fmatrix& get_ParticlesXFORM() ; + + virtual void net_Export (NET_Packet& P); // export to server + virtual void net_Import (NET_Packet& P); // import from server + + + ////////////////////////////////////////////////// + // непосредственно обработка стрельбы + ////////////////////////////////////////////////// +protected: + virtual void FireStart (); + virtual void FireEnd (); + virtual void UpdateFire (); + virtual void OnShot (); + void AddShotEffector (); + void RemoveShotEffector (); +protected: + shared_str m_sAmmoType; + CCartridge m_CurrentAmmo; + + //звук стрельбы + HUD_SOUND_ITEM sndShot; + + //для отдачи + float camRelaxSpeed; + float camMaxAngle; + + virtual bool IsHudModeNow (){return false;}; + + ///////////////////////////////////////////////// + // Generic + ///////////////////////////////////////////////// +public: + virtual CHolderCustom *cast_holder_custom () {return this;} + virtual void Load (LPCSTR section); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + + virtual void UpdateCL (); + virtual void shedule_Update (u32 dt); + + virtual void renderable_Render (); + + virtual BOOL UsedAI_Locations (){return FALSE;} + + // control functions + virtual void OnMouseMove (int x, int y); + virtual void OnKeyboardPress (int dik); + virtual void OnKeyboardRelease (int dik); + virtual void OnKeyboardHold (int dik); + + virtual CInventory* GetInventory (){return 0;} + + virtual void cam_Update (float dt, float fov=90.0f); + + virtual bool Use (const Fvector& pos,const Fvector& dir,const Fvector& foot_pos); + virtual bool attach_Actor (CGameObject* actor); + virtual void detach_Actor (); + virtual Fvector ExitPosition (); + virtual bool allowWeapon () const {return false;}; + virtual bool HUDView () const {return true;}; + + virtual CCameraBase* Camera (); +}; +#endif // WeaponMountedH diff --git a/src/xrGameLA/WeaponPM.cpp b/src/xrGameLA/WeaponPM.cpp new file mode 100644 index 000000000..16acb4a82 --- /dev/null +++ b/src/xrGameLA/WeaponPM.cpp @@ -0,0 +1,23 @@ +#include "pch_script.h" +#include "WeaponPM.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +CWeaponPM::CWeaponPM() : CWeaponPistol("PM") +{} + +CWeaponPM::~CWeaponPM() +{} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponPM::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponPM") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponPM.h b/src/xrGameLA/WeaponPM.h new file mode 100644 index 000000000..a4c666c22 --- /dev/null +++ b/src/xrGameLA/WeaponPM.h @@ -0,0 +1,24 @@ +#ifndef __XR_WEAPON_PM_H__ +#define __XR_WEAPON_PM_H__ + +#pragma once + +#include "WeaponPistol.h" +#include "script_export_space.h" + +class CWeaponPM: public CWeaponPistol +{ +private: + typedef CWeaponPistol inherited; +protected: +public: + CWeaponPM (); + virtual ~CWeaponPM (); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponPM) +#undef script_type_list +#define script_type_list save_type_list(CWeaponPM) + +#endif //__XR_WEAPON_PM_H__ diff --git a/src/xrGameLA/WeaponPistol.cpp b/src/xrGameLA/WeaponPistol.cpp new file mode 100644 index 000000000..6133db6c5 --- /dev/null +++ b/src/xrGameLA/WeaponPistol.cpp @@ -0,0 +1,168 @@ +#include "stdafx.h" +#include "weaponpistol.h" +#include "ParticlesObject.h" +#include "actor.h" + +CWeaponPistol::CWeaponPistol(LPCSTR name) : CWeaponCustomPistol(name) +{ + m_eSoundClose = ESoundTypes(SOUND_TYPE_WEAPON_RECHARGING); + SetPending (FALSE); +} + +CWeaponPistol::~CWeaponPistol(void) +{ +} + +void CWeaponPistol::net_Destroy() +{ + inherited::net_Destroy(); +} + + +void CWeaponPistol::Load (LPCSTR section) +{ + inherited::Load (section); + + m_sounds.LoadSound(section, "snd_close", "sndClose", false, m_eSoundClose); +} + +void CWeaponPistol::OnH_B_Chield () +{ + inherited::OnH_B_Chield (); +} + +void CWeaponPistol::PlayAnimShow () +{ + VERIFY(GetState()==eShowing); + + if(iAmmoElapsed==0) + PlayHUDMotion("anim_show_empty", FALSE, this, GetState()); + else + inherited::PlayAnimShow(); +} + +void CWeaponPistol::PlayAnimBore() +{ + if(iAmmoElapsed==0) + PlayHUDMotion ("anim_bore_empty", TRUE, this, GetState()); + else + inherited::PlayAnimBore(); +} + +void CWeaponPistol::PlayAnimIdleSprint() +{ + if(iAmmoElapsed==0) + { + PlayHUDMotion("anim_idle_sprint_empty", TRUE, NULL, GetState()); + }else{ + inherited::PlayAnimIdleSprint(); + } +} + +void CWeaponPistol::PlayAnimIdleMoving() +{ + if(iAmmoElapsed==0) + { + PlayHUDMotion("anim_idle_moving_empty", TRUE, NULL, GetState()); + }else{ + inherited::PlayAnimIdleMoving(); + } +} + + +void CWeaponPistol::PlayAnimIdle() +{ + if (TryPlayAnimIdle()) return; + + if(iAmmoElapsed==0) + { + PlayHUDMotion("anim_idle_empty", TRUE, NULL, GetState()); + }else{ + inherited::PlayAnimIdle (); + } +} + +void CWeaponPistol::PlayAnimAim() +{ + if(iAmmoElapsed==0) + PlayHUDMotion("anim_idle_aim_empty", TRUE, NULL, GetState()); + else + inherited::PlayAnimAim(); +} + +void CWeaponPistol::PlayAnimReload() +{ + VERIFY(GetState()==eReload); + if(iAmmoElapsed==0) + { + PlayHUDMotion("anim_reload_empty", TRUE, this, GetState()); + }else{ + PlayHUDMotion("anim_reload", TRUE, this, GetState()); + } +} + + +void CWeaponPistol::PlayAnimHide() +{ + VERIFY(GetState()==eHiding); + if(iAmmoElapsed==0) + { + PlaySound ("sndClose", get_LastFP()); + PlayHUDMotion ("anim_hide_empty" , TRUE, this, GetState()); + } + else + inherited::PlayAnimHide(); +} + +void CWeaponPistol::PlayAnimShoot () +{ + VERIFY(GetState()==eFire); + if(iAmmoElapsed > 1) + { + PlayHUDMotion("anim_shots" , FALSE, this, GetState()); + } + else + { + PlayHUDMotion("anim_shot_l", FALSE, this, GetState()); + } +} + + +void CWeaponPistol::switch2_Reload() +{ + inherited::switch2_Reload(); +} + +void CWeaponPistol::OnAnimationEnd(u32 state) +{ + inherited::OnAnimationEnd(state); +} + +void CWeaponPistol::OnShot () +{ + PlaySound (m_sSndShotCurrent.c_str(),get_LastFP()); + + AddShotEffector (); + + PlayAnimShoot (); + + // Shell Drop + Fvector vel; + PHGetLinearVell(vel); + OnShellDrop (get_LastSP(), vel); + + // Огонь из ствола + + StartFlameParticles (); + R_ASSERT2(!m_pFlameParticles || !m_pFlameParticles->IsLooped(), + "can't set looped particles system for shoting with pistol"); + + //дым из ствола + StartSmokeParticles (get_LastFP(), vel); +} + +void CWeaponPistol::UpdateSounds() +{ + inherited::UpdateSounds(); + m_sounds.SetPosition("sndClose", get_LastFP()); +} \ No newline at end of file diff --git a/src/xrGameLA/WeaponPistol.h b/src/xrGameLA/WeaponPistol.h new file mode 100644 index 000000000..3318c7f61 --- /dev/null +++ b/src/xrGameLA/WeaponPistol.h @@ -0,0 +1,37 @@ +#pragma once +#include "weaponcustompistol.h" + +class CWeaponPistol : + public CWeaponCustomPistol +{ + typedef CWeaponCustomPistol inherited; +public: + CWeaponPistol (LPCSTR name); + virtual ~CWeaponPistol (); + + virtual void Load (LPCSTR section); + + virtual void switch2_Reload (); + + virtual void OnShot (); + virtual void OnAnimationEnd (u32 state); + virtual void net_Destroy (); + virtual void OnH_B_Chield (); + + //анимации + virtual void PlayAnimShow (); + virtual void PlayAnimIdle (); + virtual void PlayAnimIdleMoving (); + virtual void PlayAnimIdleSprint (); + virtual void PlayAnimHide (); + virtual void PlayAnimReload (); + virtual void PlayAnimShoot (); + virtual void PlayAnimBore (); + virtual void PlayAnimAim (); + + virtual void UpdateSounds (); +protected: + virtual bool AllowFireWhileWorking() {return true;} + + ESoundTypes m_eSoundClose; +}; diff --git a/src/xrGameLA/WeaponRG6.cpp b/src/xrGameLA/WeaponRG6.cpp new file mode 100644 index 000000000..9bd4528ef --- /dev/null +++ b/src/xrGameLA/WeaponRG6.cpp @@ -0,0 +1,172 @@ +#include "stdafx.h" +#include "WeaponRG6.h" +#include "entity.h" +#include "explosiveRocket.h" +#include "level.h" +#include "clsid_game.h" +#include "actor.h" + +#include "MathUtils.h" +#ifdef DEBUG +#include "phdebug.h" +#endif + + +CWeaponRG6::~CWeaponRG6() +{ +} + +BOOL CWeaponRG6::net_Spawn (CSE_Abstract* DC) +{ + BOOL l_res = inheritedSG::net_Spawn(DC); + if (!l_res) return l_res; + + if (iAmmoElapsed && !getCurrentRocket()) + { + shared_str grenade_name = m_ammoTypes[0]; + shared_str fake_grenade_name = pSettings->r_string(grenade_name, "fake_grenade_name"); + + if (fake_grenade_name.size()) + { + int k=iAmmoElapsed; + while (k) + { + k--; + inheritedRL::SpawnRocket(*fake_grenade_name, this); + } + } +// inheritedRL::SpawnRocket(*fake_grenade_name, this); + } + + + + return l_res; +}; + +void CWeaponRG6::Load(LPCSTR section) +{ + inheritedRL::Load(section); + inheritedSG::Load(section); +} +#include "inventory.h" +#include "inventoryOwner.h" + +bool CWeaponRG6::install_upgrade_impl(LPCSTR section, bool test) +{ + bool result = inheritedSG::install_upgrade_impl( section, test ); + + result |= process_if_exists( section, "launch_speed", &CInifile::r_float, m_fLaunchSpeed, test ); + return result; +} + +void CWeaponRG6::LaunchGrenade(const Fvector& p1, const Fvector& d1) +{ + if (getRocketCount()) + { + Fvector p, d; + p = p1; + d = d1; + + Fmatrix launch_matrix; + launch_matrix.identity(); + launch_matrix.k.set(d); + Fvector::generate_orthonormal_basis(launch_matrix.k, + launch_matrix.j, launch_matrix.i); + launch_matrix.c.set(p); + + if (IsGameTypeSingle() && IsZoomed() && smart_cast(H_Parent())) + { + H_Parent()->setEnabled(FALSE); + setEnabled(FALSE); + + collide::rq_result RQ; + BOOL HasPick = Level().ObjectSpace.RayPick(p, d, 300.0f, collide::rqtStatic, RQ, this); + + setEnabled(TRUE); + H_Parent()->setEnabled(TRUE); + + if (HasPick) + { + // collide::rq_result& RQ = HUD().GetCurrentRayQuery(); + Fvector Transference; + //Transference.add(p1, Fvector().mul(d, RQ.range)); + Transference.mul(d, RQ.range); + Fvector res[2]; +#ifdef DEBUG + DBG_OpenCashedDraw(); + DBG_DrawLine(p1,Fvector().add(p,d),D3DCOLOR_XRGB(255,0,0)); +#endif + u8 canfire0 = TransferenceAndThrowVelToThrowDir(Transference, CRocketLauncher::m_fLaunchSpeed, EffectiveGravity(), res); +#ifdef DEBUG + if(canfire0>0)DBG_DrawLine(p1,Fvector().add(p1,res[0]),D3DCOLOR_XRGB(0,255,0)); + if(canfire0>1)DBG_DrawLine(p1,Fvector().add(p1,res[1]),D3DCOLOR_XRGB(0,0,255)); + DBG_ClosedCashedDraw(30000); +#endif + if (canfire0 != 0) + { +// Msg ("d[%f,%f,%f] - res [%f,%f,%f]", d.x, d.y, d.z, res[0].x, res[0].y, res[0].z); + d = res[0]; + }; + } + }; + + d.normalize(); + d.mul(m_fLaunchSpeed); + VERIFY2(_valid(launch_matrix),"CWeaponRG6::LaunchGrenade. Invalid launch_matrix"); + CRocketLauncher::LaunchRocket(launch_matrix, d, zero_vel); + + CExplosiveRocket* pGrenade = smart_cast(getCurrentRocket()); + VERIFY(pGrenade); + pGrenade->SetInitiator(H_Parent()->ID()); + + if (OnServer()) + { + NET_Packet P; + u_EventGen(P,GE_LAUNCH_ROCKET,ID()); + P.w_u16(u16(getCurrentRocket()->ID())); + u_EventSend(P); + } + dropCurrentRocket(); + } +} + +void CWeaponRG6::FireTrace(const Fvector& P, const Fvector& D) +{ + inheritedSG::FireTrace(P, D); + if (!IsMisfire()) + { + LaunchGrenade(P, D); + } +} + +u8 CWeaponRG6::AddCartridge (u8 cnt) +{ + u8 t = inheritedSG::AddCartridge(cnt); + u8 k = cnt-t; + shared_str fake_grenade_name = pSettings->r_string(*m_ammoTypes[m_ammoType], "fake_grenade_name"); + while(k){ + --k; + inheritedRL::SpawnRocket(*fake_grenade_name, this); + } + return k; +} + +void CWeaponRG6::OnEvent(NET_Packet& P, u16 type) +{ + inheritedSG::OnEvent(P,type); + + u16 id; + switch (type) { + case GE_OWNERSHIP_TAKE : { + P.r_u16(id); + inheritedRL::AttachRocket(id, this); + } break; + case GE_OWNERSHIP_REJECT : + case GE_LAUNCH_ROCKET : + { + bool bLaunch = (type==GE_LAUNCH_ROCKET); + P.r_u16 (id); + inheritedRL::DetachRocket (id, bLaunch); + } break; + } +} diff --git a/src/xrGameLA/WeaponRG6.h b/src/xrGameLA/WeaponRG6.h new file mode 100644 index 000000000..dd940b0c6 --- /dev/null +++ b/src/xrGameLA/WeaponRG6.h @@ -0,0 +1,29 @@ +#pragma once + +#include "rocketlauncher.h" +#include "weaponShotgun.h" +#include "script_export_space.h" + +class CWeaponRG6 : public CRocketLauncher, + public CWeaponShotgun +{ + typedef CRocketLauncher inheritedRL; + typedef CWeaponShotgun inheritedSG; + +public: + virtual ~CWeaponRG6 (); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void Load (LPCSTR section); + virtual void OnEvent (NET_Packet& P, u16 type); +protected: + virtual u8 AddCartridge (u8 cnt); + virtual void FireTrace (const Fvector& P, const Fvector& D); + virtual void LaunchGrenade (const Fvector& P, const Fvector& D); + + bool install_upgrade_impl(LPCSTR section, bool test) override; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponRG6) +#undef script_type_list +#define script_type_list save_type_list(CWeaponRG6) diff --git a/src/xrGameLA/WeaponRG6_script.cpp b/src/xrGameLA/WeaponRG6_script.cpp new file mode 100644 index 000000000..c0368d50d --- /dev/null +++ b/src/xrGameLA/WeaponRG6_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "WeaponRG6.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponRG6::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponRG6") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponRPG7.cpp b/src/xrGameLA/WeaponRPG7.cpp new file mode 100644 index 000000000..d852a162e --- /dev/null +++ b/src/xrGameLA/WeaponRPG7.cpp @@ -0,0 +1,187 @@ +#include "stdafx.h" +#include "weaponrpg7.h" +#include "xrserver_objects_alife_items.h" +#include "explosiverocket.h" +#include "entity.h" +#include "level.h" +#include "player_hud.h" +#include "hudmanager.h" + +CWeaponRPG7::CWeaponRPG7() : CWeaponCustomPistol("RPG7") +{ +} + +CWeaponRPG7::~CWeaponRPG7() +{ +} + +void CWeaponRPG7::Load (LPCSTR section) +{ + inherited::Load (section); + CRocketLauncher::Load (section); + + m_zoom_params.m_fScopeZoomFactor = pSettings->r_float (section,"max_zoom_factor"); + + m_sRocketSection = pSettings->r_string (section,"rocket_class"); +} + +bool CWeaponRPG7::install_upgrade_impl(LPCSTR section, bool test) +{ + bool result = inherited::install_upgrade_impl( section, test ); + + result |= process_if_exists( section, "launch_speed", &CInifile::r_float, m_fLaunchSpeed, test ); + + LPCSTR str = nullptr; + bool result2; + result2 = process_if_exists_set( section, "rocket_class", &CInifile::r_string, str, test ); + if (result2 && !test) + { + m_sRocketSection = str; + } + result |= result2; + return result; +} + +bool CWeaponRPG7::AllowBore() +{ + return inherited::AllowBore() && 0!=iAmmoElapsed; +} + +void CWeaponRPG7::FireTrace(const Fvector& P, const Fvector& D) +{ + inherited::FireTrace (P, D); + if (!IsMisfire()) + { + LaunchGrenade(P, D); + } + UpdateMissileVisibility (); +} + +void CWeaponRPG7::on_a_hud_attach() +{ + inherited::on_a_hud_attach (); + UpdateMissileVisibility (); +} + +void CWeaponRPG7::UpdateMissileVisibility() +{ + bool vis_hud,vis_weap; + vis_hud = (!!iAmmoElapsed || GetState()==eReload); + vis_weap = !!iAmmoElapsed; + + if(GetHUDmode()) + { + HudItemData()->set_bone_visible("grenade",vis_hud,TRUE); + } + + IKinematics* pWeaponVisual = smart_cast(Visual()); + VERIFY (pWeaponVisual); + pWeaponVisual->LL_SetBoneVisible(pWeaponVisual->LL_BoneID("grenade"), vis_weap, TRUE); +} + +BOOL CWeaponRPG7::net_Spawn(CSE_Abstract* DC) +{ + BOOL l_res = inherited::net_Spawn(DC); + + UpdateMissileVisibility(); + if(iAmmoElapsed && !getCurrentRocket()) + CRocketLauncher::SpawnRocket(*m_sRocketSection, this); + + return l_res; +} + +void CWeaponRPG7::OnStateSwitch(u32 S) +{ + inherited::OnStateSwitch(S); + UpdateMissileVisibility(); +} + +void CWeaponRPG7::UnloadMagazine(bool spawn_ammo) +{ + inherited::UnloadMagazine (spawn_ammo); + UpdateMissileVisibility (); +} + +void CWeaponRPG7::ReloadMagazine() +{ + inherited::ReloadMagazine(); + + if(iAmmoElapsed && !getRocketCount()) + CRocketLauncher::SpawnRocket(*m_sRocketSection, this); +} + +#include "inventory.h" +#include "inventoryOwner.h" +void CWeaponRPG7::switch2_Fire() +{ + m_iShotNum = 0; + m_bFireSingleShot = DelayedShotIsAllowed(); + bWorking = false; +} + +void CWeaponRPG7::LaunchGrenade(const Fvector& p1, const Fvector& d1) +{ + if (getRocketCount()) + { + Fvector p, d; + p = p1; + d = d1; + + Fmatrix launch_matrix; + launch_matrix.identity (); + launch_matrix.k.set (d); + Fvector::generate_orthonormal_basis(launch_matrix.k, + launch_matrix.j, launch_matrix.i); + launch_matrix.c.set (p); + + d.normalize (); + d.mul (m_fLaunchSpeed); + + CRocketLauncher::LaunchRocket (launch_matrix, d, zero_vel); + + CExplosiveRocket* pGrenade = smart_cast(getCurrentRocket()); + VERIFY (pGrenade); + pGrenade->SetInitiator (H_Parent()->ID()); + + if (OnServer()) + { + NET_Packet P; + u_EventGen (P,GE_LAUNCH_ROCKET,ID()); + P.w_u16 (u16(getCurrentRocket()->ID())); + u_EventSend (P); + } + } +} + +void CWeaponRPG7::PlayAnimReload() +{ + VERIFY(GetState()==eReload); + PlayHUDMotion("anim_reload", FALSE, this, GetState()); +} + +void CWeaponRPG7::OnEvent(NET_Packet& P, u16 type) +{ + inherited::OnEvent(P,type); + u16 id; + switch (type) { + case GE_OWNERSHIP_TAKE : { + P.r_u16(id); + CRocketLauncher::AttachRocket(id, this); + } break; + case GE_OWNERSHIP_REJECT: + case GE_LAUNCH_ROCKET : + { + bool bLaunch = (type==GE_LAUNCH_ROCKET); + P.r_u16(id); + CRocketLauncher::DetachRocket(id, bLaunch); + if(bLaunch) + UpdateMissileVisibility(); + } break; + } +} + +void CWeaponRPG7::net_Import( NET_Packet& P) +{ + inherited::net_Import (P); + UpdateMissileVisibility (); +} diff --git a/src/xrGameLA/WeaponRPG7.h b/src/xrGameLA/WeaponRPG7.h new file mode 100644 index 000000000..efefbb3d4 --- /dev/null +++ b/src/xrGameLA/WeaponRPG7.h @@ -0,0 +1,42 @@ +#pragma once + +#include "weaponpistol.h" +#include "rocketlauncher.h" +#include "script_export_space.h" + +class CWeaponRPG7 : public CWeaponCustomPistol, + public CRocketLauncher +{ +private: + typedef CWeaponCustomPistol inherited; +public: + CWeaponRPG7 (); + virtual ~CWeaponRPG7 (); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void OnStateSwitch (u32 S); + virtual void OnEvent (NET_Packet& P, u16 type); + virtual void ReloadMagazine (); + virtual void Load (LPCSTR section); + virtual void switch2_Fire (); + virtual void FireTrace (const Fvector& P, const Fvector& D); + virtual void on_a_hud_attach(); + + void UpdateMissileVisibility (); + virtual void UnloadMagazine (bool spawn_ammo = true); + + virtual void net_Import ( NET_Packet& P); // import from server +protected: + virtual void LaunchGrenade (const Fvector& P, const Fvector& D); + virtual bool AllowBore (); + virtual void PlayAnimReload (); + + bool install_upgrade_impl(LPCSTR section, bool test) override; + + shared_str m_sRocketSection; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponRPG7) +#undef script_type_list +#define script_type_list save_type_list(CWeaponRPG7) diff --git a/src/xrGameLA/WeaponRPG7_script.cpp b/src/xrGameLA/WeaponRPG7_script.cpp new file mode 100644 index 000000000..ed0d23fa8 --- /dev/null +++ b/src/xrGameLA/WeaponRPG7_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "WeaponRPG7.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponRPG7::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponRPG7") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponSVD.cpp b/src/xrGameLA/WeaponSVD.cpp new file mode 100644 index 000000000..3cfff27fd --- /dev/null +++ b/src/xrGameLA/WeaponSVD.cpp @@ -0,0 +1,40 @@ +#include "pch_script.h" +#include "weaponsvd.h" + +CWeaponSVD::CWeaponSVD(void) : CWeaponCustomPistol("SVD") +{} + +CWeaponSVD::~CWeaponSVD(void) +{} + +void CWeaponSVD::switch2_Fire () +{ + m_bFireSingleShot = DelayedShotIsAllowed(); + bWorking = false; + SetPending ((int)m_bFireSingleShot); + m_iShotNum = 0; + m_bStopedAfterQueueFired = false; +} + +void CWeaponSVD::OnAnimationEnd(u32 state) +{ + switch(state) + { + case eFire: { + SetPending (FALSE); + }break; // End of reload animation + } + inherited::OnAnimationEnd(state); +} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponSVD::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponSVD") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponSVD.h b/src/xrGameLA/WeaponSVD.h new file mode 100644 index 000000000..4c5962f8f --- /dev/null +++ b/src/xrGameLA/WeaponSVD.h @@ -0,0 +1,21 @@ +#pragma once + +#include "weaponcustompistol.h" +#include "script_export_space.h" + +class CWeaponSVD : + public CWeaponCustomPistol +{ + typedef CWeaponCustomPistol inherited; +protected: + virtual void switch2_Fire (); + virtual void OnAnimationEnd (u32 state); +public: + CWeaponSVD(void); + virtual ~CWeaponSVD(void); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponSVD) +#undef script_type_list +#define script_type_list save_type_list(CWeaponSVD) diff --git a/src/xrGameLA/WeaponSVU.cpp b/src/xrGameLA/WeaponSVU.cpp new file mode 100644 index 000000000..6bec1fcc1 --- /dev/null +++ b/src/xrGameLA/WeaponSVU.cpp @@ -0,0 +1,20 @@ +#include "pch_script.h" +#include "weaponsvu.h" + +CWeaponSVU::CWeaponSVU(void) : CWeaponCustomPistol("SVU") +{} + +CWeaponSVU::~CWeaponSVU(void) +{} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponSVU::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponSVU") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponSVU.h b/src/xrGameLA/WeaponSVU.h new file mode 100644 index 000000000..dd4f26b84 --- /dev/null +++ b/src/xrGameLA/WeaponSVU.h @@ -0,0 +1,18 @@ +#pragma once + +#include "weaponcustompistol.h" +#include "script_export_space.h" + +class CWeaponSVU : + public CWeaponCustomPistol +{ + typedef CWeaponCustomPistol inherited; +public: + CWeaponSVU(void); + virtual ~CWeaponSVU(void); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponSVU) +#undef script_type_list +#define script_type_list save_type_list(CWeaponSVU) diff --git a/src/xrGameLA/WeaponShotgun.cpp b/src/xrGameLA/WeaponShotgun.cpp new file mode 100644 index 000000000..4a162d13d --- /dev/null +++ b/src/xrGameLA/WeaponShotgun.cpp @@ -0,0 +1,298 @@ +#include "stdafx.h" +#include "weaponshotgun.h" +#include "entity.h" +#include "ParticlesObject.h" +#include "xr_level_controller.h" +#include "inventory.h" +#include "level.h" +#include "actor.h" + +CWeaponShotgun::CWeaponShotgun() : CWeaponCustomPistol("TOZ34") +{ + m_eSoundClose = ESoundTypes(SOUND_TYPE_WEAPON_SHOOTING); + m_eSoundAddCartridge = ESoundTypes(SOUND_TYPE_WEAPON_SHOOTING); +} + +CWeaponShotgun::~CWeaponShotgun() +{ +} + +void CWeaponShotgun::net_Destroy() +{ + inherited::net_Destroy(); +} + +void CWeaponShotgun::Load (LPCSTR section) +{ + inherited::Load (section); + + if(pSettings->line_exist(section, "tri_state_reload")){ + m_bTriStateReload = !!pSettings->r_bool(section, "tri_state_reload"); + }; + if(m_bTriStateReload){ + m_sounds.LoadSound(section, "snd_open_weapon", "sndOpen", false, m_eSoundOpen); + + m_sounds.LoadSound(section, "snd_add_cartridge", "sndAddCartridge", false, m_eSoundAddCartridge); + + m_sounds.LoadSound(section, "snd_close_weapon", "sndClose", false, m_eSoundClose); + }; + +} + +void CWeaponShotgun::switch2_Fire () +{ + inherited::switch2_Fire (); + // bWorking = false; +} + + +bool CWeaponShotgun::Action (u16 cmd, u32 flags) +{ + if(inherited::Action(cmd, flags)) return true; + + if( m_bTriStateReload && GetState()==eReload && + cmd==kWPN_FIRE && flags&CMD_START && + m_sub_state==eSubstateReloadInProcess )//остановить перезагрузку + { + AddCartridge(1); + m_sub_state = eSubstateReloadEnd; + return true; + } + return false; +} + +void CWeaponShotgun::OnAnimationEnd(u32 state) +{ + if(!m_bTriStateReload || state != eReload) + return inherited::OnAnimationEnd(state); + + switch(m_sub_state){ + case eSubstateReloadBegin:{ + m_sub_state = eSubstateReloadInProcess; + SwitchState(eReload); + }break; + + case eSubstateReloadInProcess:{ + if( 0 != AddCartridge(1) ){ + m_sub_state = eSubstateReloadEnd; + } + SwitchState(eReload); + }break; + + case eSubstateReloadEnd:{ + m_sub_state = eSubstateReloadBegin; + SwitchState(eIdle); + }break; + + }; +} + +void CWeaponShotgun::Reload() +{ + if(m_bTriStateReload){ + TriStateReload(); + }else + inherited::Reload(); +} + +void CWeaponShotgun::TriStateReload() +{ + if (m_magazine.size() == (u32)maxMagazineSize_ || !HaveCartridgeInInventory(1))return; + CWeapon::Reload (); + m_sub_state = eSubstateReloadBegin; + SwitchState (eReload); +} + +void CWeaponShotgun::OnStateSwitch (u32 S) +{ + if(!m_bTriStateReload || S != eReload){ + inherited::OnStateSwitch(S); + return; + } + + CWeapon::OnStateSwitch(S); + + if (m_magazine.size() == (u32)maxMagazineSize_ || !HaveCartridgeInInventory(1)){ + switch2_EndReload (); + m_sub_state = eSubstateReloadEnd; + return; + }; + + switch (m_sub_state) + { + case eSubstateReloadBegin: + if( HaveCartridgeInInventory(1) ) + switch2_StartReload (); + break; + case eSubstateReloadInProcess: + if( HaveCartridgeInInventory(1) ) + switch2_AddCartgidge (); + break; + case eSubstateReloadEnd: + switch2_EndReload (); + break; + }; +} + +void CWeaponShotgun::switch2_StartReload() +{ + PlaySound ("sndOpen",get_LastFP()); + PlayAnimOpenWeapon (); + SetPending (TRUE); +} + +void CWeaponShotgun::switch2_AddCartgidge () +{ + PlaySound ("sndAddCartridge",get_LastFP()); + PlayAnimAddOneCartridgeWeapon(); + SetPending (TRUE); +} + +void CWeaponShotgun::switch2_EndReload () +{ + SetPending (FALSE); + PlaySound ("sndClose",get_LastFP()); + PlayAnimCloseWeapon (); +} + +void CWeaponShotgun::PlayAnimOpenWeapon() +{ + VERIFY(GetState()==eReload); + PlayHUDMotion("anim_open",FALSE,this,GetState()); +} +void CWeaponShotgun::PlayAnimAddOneCartridgeWeapon() +{ + VERIFY(GetState()==eReload); + PlayHUDMotion("anim_add_cartridge",FALSE,this,GetState()); +} +void CWeaponShotgun::PlayAnimCloseWeapon() +{ + VERIFY(GetState()==eReload); + + PlayHUDMotion("anim_close",FALSE,this,GetState()); +} + +bool CWeaponShotgun::HaveCartridgeInInventory(u8 cnt) +{ + if (unlimited_ammo()) return true; + + m_pAmmo = NULL; + if(m_pCurrentInventory) + { + //попытаться найти в инвентаре патроны текущего типа + m_pAmmo = smart_cast(m_pCurrentInventory->GetAny(*m_ammoTypes[m_ammoType])); + + if(!m_pAmmo ) + { + for(u32 i = 0; i < m_ammoTypes.size(); ++i) + { + //проверить патроны всех подходящих типов + m_pAmmo = smart_cast(m_pCurrentInventory->GetAny(*m_ammoTypes[i])); + if(m_pAmmo) + { + m_ammoType = i; + break; + } + } + } + } + return (m_pAmmo!=NULL)&&(m_pAmmo->m_boxCurr>=cnt) ; +} + +u8 CWeaponShotgun::AddCartridge (u8 cnt) +{ + if(IsMisfire()) bMisfire = false; + + if ( m_set_next_ammoType_on_reload != u8(-1) ) + { + m_ammoType = m_set_next_ammoType_on_reload; + m_set_next_ammoType_on_reload = u8(-1); + } + + if( !HaveCartridgeInInventory(1) ) + return 0; + + m_pAmmo = smart_cast(m_pCurrentInventory->GetAny( m_ammoTypes[m_ammoType].c_str() )); + VERIFY((u32)iAmmoElapsed == m_magazine.size()); + + + if (m_DefaultCartridge.m_LocalAmmoType != m_ammoType) + m_DefaultCartridge.Load(m_ammoTypes[m_ammoType].c_str(), m_ammoType); + + CCartridge l_cartridge = m_DefaultCartridge; + while(cnt) + { + if (!unlimited_ammo()) + { + if (!m_pAmmo->Get(l_cartridge)) break; + } + --cnt; + ++iAmmoElapsed; + l_cartridge.m_LocalAmmoType = m_ammoType; + m_magazine.push_back(l_cartridge); +// m_fCurrentCartirdgeDisp = l_cartridge.m_kDisp; + } + + VERIFY((u32)iAmmoElapsed == m_magazine.size()); + + //выкинуть коробку патронов, если она пустая + if(m_pAmmo && !m_pAmmo->m_boxCurr && OnServer()) + m_pAmmo->SetDropManual(TRUE); + + return cnt; +} + +BOOL CWeaponShotgun::net_Spawn(CSE_Abstract* server_entity) +{ + BOOL spawn_res = inherited::net_Spawn(server_entity); + + CSE_ALifeItemWeaponShotGun* const shot_gun = smart_cast(server_entity); + R_ASSERT(shot_gun); + + // load data exported to server in ::net_Export + if (shot_gun->m_AmmoIDs.size() > 0) //override CWeapon code for spawning magagine (shotguns can have deverced ammo in magazine) + { + m_magazine.clear(); + + for (u8 i = 0; i < shot_gun->m_AmmoIDs.size(); i++) + { + u8 ammo_type = shot_gun->m_AmmoIDs[i]; + + if (ammo_type >= m_ammoTypes.size()) + { + Msg("!m_ammoType index %u is out of ammo indexes (should be less than %d); Weapon section is [%s]. Probably default ammo upgrade is the reason", ammo_type, m_ammoTypes.size(), cNameSect_str()); + ammo_type = 0; + } + + if (m_DefaultCartridge.m_LocalAmmoType != ammo_type) // if cartridge type loaded in CWeapon is not same or if shotgun has deverced ammo types in magazine - load new + m_DefaultCartridge.Load(m_ammoTypes[ammo_type].c_str(), ammo_type); + + CCartridge l_cartridge = m_DefaultCartridge; + + l_cartridge.m_LocalAmmoType = ammo_type; + m_magazine.push_back(l_cartridge); + } + + R_ASSERT((u32)iAmmoElapsed == m_magazine.size()); + } + + return spawn_res; +} + +void CWeaponShotgun::net_Export (NET_Packet& P) +{ + inherited::net_Export(P); + P.w_u8(u8(m_magazine.size())); + for (u32 i=0; i(B->callback_param()); + Fmatrix rX; rX.rotateX (P->m_cur_x_rot); + B->mTransform.mulB_43(rX); +} + +void CWeaponStatMgun::BoneCallbackY (CBoneInstance *B) +{ + CWeaponStatMgun *P = static_cast(B->callback_param()); + Fmatrix rY; rY.rotateY (P->m_cur_y_rot); + B->mTransform.mulB_43(rY); +} + +CWeaponStatMgun::CWeaponStatMgun() +{ + m_Ammo = new CCartridge(); + camera = new CCameraFirstEye(this, CCameraBase::flRelativeLink|CCameraBase::flPositionRigid|CCameraBase::flDirectionRigid); + camera->Load("mounted_weapon_cam"); +} + +CWeaponStatMgun::~CWeaponStatMgun() +{ + delete_data(m_Ammo); + xr_delete(camera); +} + +void CWeaponStatMgun::SetBoneCallbacks() +{ + m_pPhysicsShell->EnabledCallbacks(FALSE); + + CBoneInstance& biX = smart_cast(Visual())->LL_GetBoneInstance(m_rotate_x_bone); + biX.set_callback (bctCustom,BoneCallbackX,this); + CBoneInstance& biY = smart_cast(Visual())->LL_GetBoneInstance(m_rotate_y_bone); + biY.set_callback (bctCustom,BoneCallbackY,this); +} + +void CWeaponStatMgun::ResetBoneCallbacks() +{ + CBoneInstance& biX = smart_cast(Visual())->LL_GetBoneInstance(m_rotate_x_bone); + biX.reset_callback (); + CBoneInstance& biY = smart_cast(Visual())->LL_GetBoneInstance(m_rotate_y_bone); + biY.reset_callback (); + + m_pPhysicsShell->EnabledCallbacks(TRUE); +} + +void CWeaponStatMgun::Load(LPCSTR section) +{ + inheritedPH::Load(section); + inheritedShooting::Load (section); + + m_sounds.LoadSound(section,"snd_shoot", "sndShot", false, SOUND_TYPE_WEAPON_SHOOTING); + m_Ammo->Load(pSettings->r_string(section, "ammo_class"), 0); + camMaxAngle = pSettings->r_float (section,"cam_max_angle" ); + camMaxAngle = deg2rad (camMaxAngle); + camRelaxSpeed = pSettings->r_float (section,"cam_relax_speed" ); + camRelaxSpeed = deg2rad (camRelaxSpeed); + +} + +BOOL CWeaponStatMgun::net_Spawn(CSE_Abstract* DC) +{ + if(!inheritedPH::net_Spawn (DC)) return FALSE; + + + + IKinematics* K = smart_cast(Visual()); + CInifile* pUserData = K->LL_UserData(); + + R_ASSERT2 (pUserData,"Empty WeaponStatMgun user data!"); + + m_rotate_x_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","rotate_x_bone")); + m_rotate_y_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","rotate_y_bone")); + m_fire_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","fire_bone")); + m_camera_bone = K->LL_BoneID (pUserData->r_string("mounted_weapon_definition","camera_bone")); + + U16Vec fixed_bones; + fixed_bones.push_back (K->LL_GetBoneRoot()); + PPhysicsShell() = P_build_Shell(this,false,fixed_bones); + + CBoneData& bdX = K->LL_GetData(m_rotate_x_bone); VERIFY(bdX.IK_data.type==jtJoint); + m_lim_x_rot.set (bdX.IK_data.limits[0].limit.x,bdX.IK_data.limits[0].limit.y); + CBoneData& bdY = K->LL_GetData(m_rotate_y_bone); VERIFY(bdY.IK_data.type==jtJoint); + m_lim_y_rot.set (bdY.IK_data.limits[1].limit.x,bdY.IK_data.limits[1].limit.y); + + + xr_vector matrices; + K->LL_GetBindTransform (matrices); + m_i_bind_x_xform.invert (matrices[m_rotate_x_bone]); + m_i_bind_y_xform.invert (matrices[m_rotate_y_bone]); + m_bind_x_rot = matrices[m_rotate_x_bone].k.getP(); + m_bind_y_rot = matrices[m_rotate_y_bone].k.getH(); + m_bind_x.set (matrices[m_rotate_x_bone].c); + m_bind_y.set (matrices[m_rotate_y_bone].c); + + m_cur_x_rot = m_bind_x_rot; + m_cur_y_rot = m_bind_y_rot; + m_destEnemyDir.setHP (m_bind_y_rot,m_bind_x_rot); + XFORM().transform_dir (m_destEnemyDir); + + inheritedShooting::Light_Create(); + + processing_activate (); + setVisible (TRUE); + setEnabled (TRUE); + return TRUE; +} + +void CWeaponStatMgun::net_Destroy() +{ + inheritedPH::net_Destroy (); + processing_deactivate (); +} + +void CWeaponStatMgun::net_Export(NET_Packet& P) // export to server +{ + inheritedPH::net_Export (P); + P.w_u8 (IsWorking() ? 1 : 0); + save_data (m_destEnemyDir, P); +} + +void CWeaponStatMgun::net_Import(NET_Packet& P) // import from server +{ + inheritedPH::net_Import (P); + u8 state = P.r_u8(); + load_data (m_destEnemyDir, P); + + if(TRUE==IsWorking()&&!state) FireEnd (); + if(FALSE==IsWorking()&&state) FireStart (); + +} + +void CWeaponStatMgun::UpdateCL() +{ + inheritedPH::UpdateCL (); + UpdateBarrelDir (); + UpdateFire (); + + if(OwnerActor() && OwnerActor()->IsMyCamera()) + { + cam_Update(Device.fTimeDelta, g_fov); + OwnerActor()->Cameras().UpdateFromCamera(Camera()); + OwnerActor()->Cameras().ApplyDevice(VIEWPORT_NEAR); + } + +} + +//void CWeaponStatMgun::Hit( float P, Fvector &dir, CObject* who, +// s16 element,Fvector p_in_object_space, +// float impulse, ALife::EHitType hit_type) +void CWeaponStatMgun::Hit(SHit* pHDS) +{ + if(NULL==Owner()) +// inheritedPH::Hit(P,dir,who,element,p_in_object_space,impulse,hit_type); + inheritedPH::Hit(pHDS); +} + +void CWeaponStatMgun::UpdateBarrelDir() +{ + IKinematics* K = smart_cast(Visual()); + m_fire_bone_xform = K->LL_GetTransform(m_fire_bone); + + m_fire_bone_xform.mulA_43 (XFORM()); + m_fire_pos.set (0,0,0); + m_fire_bone_xform.transform_tiny(m_fire_pos); + m_fire_dir.set (0,0,1); + m_fire_bone_xform.transform_dir (m_fire_dir); + + m_allow_fire = true; + Fmatrix XFi; + XFi.invert (XFORM()); + Fvector dep; + XFi.transform_dir (dep,m_destEnemyDir); + {// x angle + m_i_bind_x_xform.transform_dir(dep); dep.normalize(); + m_tgt_x_rot = angle_normalize_signed(m_bind_x_rot-dep.getP()); + float sv_x = m_tgt_x_rot; + + clamp (m_tgt_x_rot,-m_lim_x_rot.y,-m_lim_x_rot.x); + if (!fsimilar(sv_x,m_tgt_x_rot,EPS_L)) m_allow_fire=FALSE; + } + {// y angle + m_i_bind_y_xform.transform_dir(dep); dep.normalize(); + m_tgt_y_rot = angle_normalize_signed(m_bind_y_rot-dep.getH()); + float sv_y = m_tgt_y_rot; + clamp (m_tgt_y_rot,-m_lim_y_rot.y,-m_lim_y_rot.x); + if (!fsimilar(sv_y,m_tgt_y_rot,EPS_L)) m_allow_fire=FALSE; + } + + m_cur_x_rot = angle_inertion_var(m_cur_x_rot,m_tgt_x_rot,0.5f,3.5f,PI_DIV_6,Device.fTimeDelta); + m_cur_y_rot = angle_inertion_var(m_cur_y_rot,m_tgt_y_rot,0.5f,3.5f,PI_DIV_6,Device.fTimeDelta); +} + +void CWeaponStatMgun::cam_Update (float dt, float fov) +{ + Fvector P,Da; + Da.set (0,0,0); + + IKinematics* K = smart_cast(Visual()); + K->CalculateBones_Invalidate (); + K->CalculateBones (TRUE); + const Fmatrix& C = K->LL_GetTransform(m_camera_bone); + XFORM().transform_tiny (P,C.c); + + Fvector d = C.k; + XFORM().transform_dir (d); + Fvector2 des_cam_dir; + + d.getHP(des_cam_dir.x, des_cam_dir.y); + des_cam_dir.mul(-1.0f); + + + Camera()->yaw = angle_inertion_var(Camera()->yaw, des_cam_dir.x, 0.5f, 7.5f, PI_DIV_6, Device.fTimeDelta); + Camera()->pitch = angle_inertion_var(Camera()->pitch, des_cam_dir.y, 0.5f, 7.5f, PI_DIV_6, Device.fTimeDelta); + + + + + if(OwnerActor()){ + // rotate head + OwnerActor()->Orientation().yaw = -Camera()->yaw; + OwnerActor()->Orientation().pitch = -Camera()->pitch; + } + + + Camera()->Update (P,Da); + Level().Cameras().UpdateFromCamera (Camera()); + +} + +void CWeaponStatMgun::renderable_Render () +{ + inheritedPH::renderable_Render (); + + RenderLight(); +} + +void CWeaponStatMgun::SetDesiredDir (float h, float p) +{ + m_destEnemyDir.setHP (h,p); +} + +void CWeaponStatMgun::Action (int id, u32 flags) +{ + inheritedHolder::Action(id,flags); + switch (id){ + case kWPN_FIRE:{ + if(flags==CMD_START) FireStart (); + else FireEnd (); + }break; + } +} + +void CWeaponStatMgun::SetParam (int id, Fvector2 val) +{ + inheritedHolder::SetParam(id, val); + switch (id){ + case DESIRED_DIR: + SetDesiredDir(val.x,val.y); + break; + } +} + +bool CWeaponStatMgun::attach_Actor (CGameObject* actor) +{ + inheritedHolder::attach_Actor (actor); + SetBoneCallbacks (); + FireEnd (); + return true; +} + +void CWeaponStatMgun::detach_Actor () +{ + inheritedHolder::detach_Actor (); + ResetBoneCallbacks (); + FireEnd (); +} diff --git a/src/xrGameLA/WeaponStatMgun.h b/src/xrGameLA/WeaponStatMgun.h new file mode 100644 index 000000000..910344dd3 --- /dev/null +++ b/src/xrGameLA/WeaponStatMgun.h @@ -0,0 +1,104 @@ +#pragma once + +#include "holder_custom.h" +#include "shootingobject.h" +#include "physicsshellholder.h" +#include "hudsound.h" +class CCartridge; +class CCameraBase; + +#define DESIRED_DIR 1 + +class CWeaponStatMgun: public CPhysicsShellHolder, + public CHolderCustom, + public CShootingObject +{ +private: + typedef CPhysicsShellHolder inheritedPH; + typedef CHolderCustom inheritedHolder; + typedef CShootingObject inheritedShooting; + +private: + CCameraBase* camera; + // + static void __stdcall BoneCallbackX (CBoneInstance *B); + static void __stdcall BoneCallbackY (CBoneInstance *B); + void SetBoneCallbacks (); + void ResetBoneCallbacks (); + + HUD_SOUND_COLLECTION m_sounds; +//casts +public: + virtual CHolderCustom *cast_holder_custom () {return this;} + +//general +public: + CWeaponStatMgun (); + virtual ~CWeaponStatMgun (); + + virtual void Load (LPCSTR section); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Export (NET_Packet& P); // export to server + virtual void net_Import (NET_Packet& P); // import from server + + virtual void UpdateCL (); + + virtual void Hit (SHit* pHDS); + +//shooting +private: + u16 m_rotate_x_bone, m_rotate_y_bone, m_fire_bone, m_camera_bone; + float m_tgt_x_rot, m_tgt_y_rot, m_cur_x_rot, m_cur_y_rot, m_bind_x_rot, m_bind_y_rot; + Fvector m_bind_x, m_bind_y; + Fvector m_fire_dir,m_fire_pos; + + Fmatrix m_i_bind_x_xform, m_i_bind_y_xform, m_fire_bone_xform; + Fvector2 m_lim_x_rot, m_lim_y_rot; //in bone space + CCartridge* m_Ammo; + float m_barrel_speed; + Fvector2 m_dAngle; + Fvector m_destEnemyDir; + bool m_allow_fire; + HUD_SOUND_ITEM sndShot; + float camRelaxSpeed; + float camMaxAngle; + +protected: + void UpdateBarrelDir (); + virtual const Fvector& get_CurrentFirePoint(); + virtual const Fmatrix& get_ParticlesXFORM (); + + virtual void FireStart (); + virtual void FireEnd (); + virtual void UpdateFire (); + virtual void OnShot (); + void AddShotEffector (); + void RemoveShotEffector (); + void SetDesiredDir (float h, float p); + virtual bool IsHudModeNow (){return false;}; + +//HolderCustom +public: + virtual bool Use (const Fvector& pos,const Fvector& dir,const Fvector& foot_pos) {return !Owner();}; + virtual void OnMouseMove (int x, int y); + virtual void OnKeyboardPress (int dik); + virtual void OnKeyboardRelease (int dik); + virtual void OnKeyboardHold (int dik); + virtual CInventory* GetInventory () {return NULL;}; + virtual void cam_Update (float dt, float fov=90.0f); + + virtual void renderable_Render (); + + virtual bool attach_Actor (CGameObject* actor); + virtual void detach_Actor (); + virtual bool allowWeapon () const {return false;}; + virtual bool HUDView () const {return true;}; + virtual Fvector ExitPosition () {return Fvector().set(0.0f,0.0f,0.0f);}; + + virtual CCameraBase* Camera () {return camera;}; + + virtual void Action (int id, u32 flags); + virtual void SetParam (int id, Fvector2 val); +}; \ No newline at end of file diff --git a/src/xrGameLA/WeaponStatMgunFire.cpp b/src/xrGameLA/WeaponStatMgunFire.cpp new file mode 100644 index 000000000..5ab7b5e15 --- /dev/null +++ b/src/xrGameLA/WeaponStatMgunFire.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "WeaponStatMgun.h" +#include "level.h" +#include "entity_alive.h" +#include "hudsound.h" +#include "actor.h" +#include "actorEffector.h" +#include "EffectorShot.h" + +const Fvector& CWeaponStatMgun::get_CurrentFirePoint() +{ + return m_fire_pos; +} + +const Fmatrix& CWeaponStatMgun::get_ParticlesXFORM () +{ + return m_fire_bone_xform; +} + +void CWeaponStatMgun::FireStart() +{ + m_dAngle.set(0.0f,0.0f); + inheritedShooting::FireStart(); +} + +void CWeaponStatMgun::FireEnd() +{ + m_dAngle.set(0.0f,0.0f); + inheritedShooting::FireEnd(); + StopFlameParticles (); + RemoveShotEffector (); +} + +void CWeaponStatMgun::UpdateFire() +{ + fTime -= Device.fTimeDelta; + + + inheritedShooting::UpdateFlameParticles(); + inheritedShooting::UpdateLight(); + + if(!IsWorking()){ + if(fTime<0) fTime = 0.f; + return; + } + + if(fTime<=0){ + OnShot(); + fTime += fTimeToFire; + }else{ + angle_lerp (m_dAngle.x,0.f,5.f,Device.fTimeDelta); + angle_lerp (m_dAngle.y,0.f,5.f,Device.fTimeDelta); + } +} + + +void CWeaponStatMgun::OnShot() +{ + VERIFY(Owner()); + + FireBullet ( m_fire_pos, m_fire_dir, fireDispersionBase, *m_Ammo, + Owner()->ID(),ID(), SendHitAllowed(Owner())); + + StartShotParticles (); + + if(m_bLightShotEnabled) + Light_Start (); + + StartFlameParticles (); + StartSmokeParticles (m_fire_pos, zero_vel); + OnShellDrop (m_fire_pos, zero_vel); + + bool b_hud_mode = (Level().CurrentEntity() == smart_cast(Owner())); + m_sounds.PlaySound ("sndShot", m_fire_pos, Owner(), b_hud_mode); + + AddShotEffector (); + m_dAngle.set ( ::Random.randF(-fireDispersionBase,fireDispersionBase), + ::Random.randF(-fireDispersionBase,fireDispersionBase)); +} + +void CWeaponStatMgun::AddShotEffector () +{ + if(OwnerActor()) + { + CCameraShotEffector* S = smart_cast(OwnerActor()->Cameras().GetCamEffector(eCEShot)); + if (!S) S = (CCameraShotEffector*)OwnerActor()->Cameras().AddCamEffector(new CCameraShotEffector(camMaxAngle,camRelaxSpeed, 0.25f, 0.01f, 0.7f)); + R_ASSERT (S); + S->Shot (0.01f); + } +} + +void CWeaponStatMgun::RemoveShotEffector () +{ + if(OwnerActor()) + OwnerActor()->Cameras().RemoveCamEffector (eCEShot); +} \ No newline at end of file diff --git a/src/xrGameLA/WeaponStatMgunIR.cpp b/src/xrGameLA/WeaponStatMgunIR.cpp new file mode 100644 index 000000000..4d6e56a93 --- /dev/null +++ b/src/xrGameLA/WeaponStatMgunIR.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" +#include "WeaponStatMgun.h" +#include "xr_level_controller.h" + + +void CWeaponStatMgun::OnMouseMove (int dx, int dy) +{ + if (Remote()) return; + + float scale = psMouseSens * psMouseSensScale/50.f; + float h,p; + m_destEnemyDir.getHP(h,p); + if (dx){ + float d = float(dx)*scale; + h -= d; + SetDesiredDir (h,p); + } + if (dy){ + float d = ((psMouseInvert.test(1))?-1:1)*float(dy)*scale*3.f/4.f; + p -= d; + SetDesiredDir (h,p); + } +} + +void CWeaponStatMgun::OnKeyboardPress (int dik) +{ + if (Remote()) return; + + switch (dik) + { + case kWPN_FIRE: + FireStart(); + break; + }; +} + +void CWeaponStatMgun::OnKeyboardRelease (int dik) +{ + if (Remote()) return; + switch (dik) + { + case kWPN_FIRE: + FireEnd(); + break; + }; +} + +void CWeaponStatMgun::OnKeyboardHold (int dik) +{} diff --git a/src/xrGameLA/WeaponUSP45.cpp b/src/xrGameLA/WeaponUSP45.cpp new file mode 100644 index 000000000..a9b2dff6b --- /dev/null +++ b/src/xrGameLA/WeaponUSP45.cpp @@ -0,0 +1,20 @@ +#include "pch_script.h" +#include "weaponusp45.h" + +CWeaponUSP45::CWeaponUSP45(void) : CWeaponPistol("USP") +{} + +CWeaponUSP45::~CWeaponUSP45(void) +{} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponUSP45::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponUSP45") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponUSP45.h b/src/xrGameLA/WeaponUSP45.h new file mode 100644 index 000000000..961dbcd7d --- /dev/null +++ b/src/xrGameLA/WeaponUSP45.h @@ -0,0 +1,18 @@ +#pragma once + +#include "weaponpistol.h" +#include "script_export_space.h" + +class CWeaponUSP45 : + public CWeaponPistol +{ + typedef CWeaponPistol inherited; +public: + CWeaponUSP45(void); + virtual ~CWeaponUSP45(void); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponUSP45) +#undef script_type_list +#define script_type_list save_type_list(CWeaponUSP45) diff --git a/src/xrGameLA/WeaponUpgrade.cpp b/src/xrGameLA/WeaponUpgrade.cpp new file mode 100644 index 000000000..c7448966e --- /dev/null +++ b/src/xrGameLA/WeaponUpgrade.cpp @@ -0,0 +1,259 @@ + +#include "stdafx.h" +#include "Weapon.h" + +bool CWeapon::process_if_exists_deg2rad( LPCSTR section, LPCSTR name, float& value, bool test ) +{ + return process_if_exists(section, name, value, test, deg2rad, nullptr); +} + +bool CWeapon::install_upgrade_impl( LPCSTR section, bool test ) +{ + //inherited::install_upgrade( section ); + bool result = CInventoryItemObject::install_upgrade_impl( section, test ); + + result |= install_upgrade_ammo_class( section, test ); + result |= install_upgrade_disp ( section, test ); + result |= install_upgrade_hit ( section, test ); + result |= install_upgrade_addon ( section, test ); + return result; +} + +bool CWeapon::install_upgrade_ammo_class( LPCSTR section, bool test ) +{ + LPCSTR str; + + bool result = process_if_exists(section, "ammo_mag_size", &CInifile::r_s32, maxMagazineSize_, test); + + // ammo_class = ammo_5.45x39_fmj, ammo_5.45x39_ap // name of the ltx-section of used ammo + bool result2 = process_if_exists_set( section, "ammo_class", &CInifile::r_string, str, test ); + if ( result2 && !test ) + { + m_ammoTypes.clear(); + string128 ammoItem; + int count = _GetItemCount( str ); + for ( int i = 0; i < count; ++i ) + { + _GetItem( str, i, ammoItem ); + m_ammoTypes.push_back( ammoItem ); + } + m_ammoType = 0; + } + result |= result2; + + return result; +} + +bool CWeapon::install_upgrade_disp( LPCSTR section, bool test ) +{ + bool result = process_if_exists( section, "fire_dispersion_condition_factor", &CInifile::r_float, fireDispersionConditionFactor, test ); + result |= process_if_exists( section, "fire_distance", &CInifile::r_float, fireDistance, test ); + + + u8 rm = (cam_recoil.camReturnMode) ? 1 : 0; + result |= process_if_exists_set( section, "cam_return", &CInifile::r_u8, rm, test ); + cam_recoil.camReturnMode = (rm == 1); + + rm = (cam_recoil.camStopReturn) ? 1 : 0; + result |= process_if_exists_set( section, "cam_return_stop", &CInifile::r_u8, rm, test ); + cam_recoil.camStopReturn = (rm == 1); + + result |= process_if_exists_deg2rad( section, "fire_dispersion_base", fireDispersionBase, test ); + + result |= process_if_exists_deg2rad(section, "cam_relax_speed", cam_recoil.camRelaxSpeed, test); + result |= process_if_exists_deg2rad(section, "cam_relax_speed_ai", cam_recoil.camRelaxSpeed_AI, test); + result |= process_if_exists_deg2rad(section, "cam_dispersion", cam_recoil.camDispersion, test); + result |= process_if_exists_deg2rad(section, "cam_dispersion_inc", cam_recoil.camDispersionInc, test); + + result |= process_if_exists(section, "cam_dispersion_frac", &CInifile::r_float, cam_recoil.camDispersionFrac, test); + + result |= process_if_exists_deg2rad(section, "cam_max_angle", cam_recoil.camMaxAngleVert, test); + result |= process_if_exists_deg2rad(section, "cam_max_angle_horz", cam_recoil.camMaxAngleHorz, test); + result |= process_if_exists_deg2rad(section, "cam_step_angle_horz", cam_recoil.camStepAngleHorz, test); + + VERIFY(!fis_zero(cam_recoil.camRelaxSpeed)); + VERIFY(!fis_zero(cam_recoil.camRelaxSpeed_AI)); + VERIFY(!fis_zero(cam_recoil.camMaxAngleVert)); + VERIFY(!fis_zero(cam_recoil.camMaxAngleHorz)); + + result |= process_if_exists_deg2rad(section, "zoom_cam_relax_speed", zoom_cam_recoil.camRelaxSpeed, test);// zoom_ ... + result |= process_if_exists_deg2rad(section, "zoom_cam_relax_speed_ai", zoom_cam_recoil.camRelaxSpeed_AI, test); + result |= process_if_exists_deg2rad(section, "zoom_cam_dispersion", zoom_cam_recoil.camDispersion, test); + result |= process_if_exists_deg2rad(section, "zoom_cam_dispersion_inc", zoom_cam_recoil.camDispersionInc, test); + + result |= process_if_exists(section, "zoom_cam_dispersion_frac", &CInifile::r_float, zoom_cam_recoil.camDispersionFrac, test); + + result |= process_if_exists_deg2rad(section, "zoom_cam_max_angle", zoom_cam_recoil.camMaxAngleVert, test); + result |= process_if_exists_deg2rad(section, "zoom_cam_max_angle_horz", zoom_cam_recoil.camMaxAngleHorz, test); + result |= process_if_exists_deg2rad(section, "zoom_cam_step_angle_horz", zoom_cam_recoil.camStepAngleHorz, test); + + VERIFY(!fis_zero(zoom_cam_recoil.camRelaxSpeed)); + VERIFY(!fis_zero(zoom_cam_recoil.camRelaxSpeed_AI)); + VERIFY(!fis_zero(zoom_cam_recoil.camMaxAngleVert)); + VERIFY(!fis_zero(zoom_cam_recoil.camMaxAngleHorz)); + + result |= process_if_exists( section, "PDM_disp_base", &CInifile::r_float, m_pdm.m_fPDM_disp_base, test ); + result |= process_if_exists( section, "PDM_disp_vel_factor", &CInifile::r_float, m_pdm.m_fPDM_disp_vel_factor, test ); + result |= process_if_exists( section, "PDM_disp_accel_factor", &CInifile::r_float, m_pdm.m_fPDM_disp_accel_factor, test ); + result |= process_if_exists( section, "PDM_disp_crouch", &CInifile::r_float, m_pdm.m_fPDM_disp_crouch, test ); + result |= process_if_exists( section, "PDM_disp_crouch_no_acc", &CInifile::r_float, m_pdm.m_fPDM_disp_crouch_no_acc, test ); + + result |= process_if_exists( section, "zoom_inertion_factor", &CInifile::r_float, m_fZoomInertCoef, test ); + +// result |= process_if_exists( section, "misfire_probability", &CInifile::r_float, misfireProbability, test ); +// result |= process_if_exists( section, "misfire_condition_k", &CInifile::r_float, misfireConditionK, test ); + result |= process_if_exists( section, "condition_shot_dec", &CInifile::r_float, conditionDecreasePerShot, test ); + result |= process_if_exists( section, "misfire_start_condition", &CInifile::r_float, misfireStartCondition, test ); + result |= process_if_exists( section, "misfire_end_condition", &CInifile::r_float, misfireEndCondition, test ); + result |= process_if_exists( section, "misfire_start_prob", &CInifile::r_float, misfireStartProbability, test ); + result |= process_if_exists( section, "misfire_end_prob", &CInifile::r_float, misfireEndProbability, test ); + + BOOL value = m_zoom_params.m_bZoomEnabled; + bool result2 = process_if_exists_set( section, "zoom_enabled", &CInifile::r_bool, value, test ); + if ( result2 && !test ) + { + m_zoom_params.m_bZoomEnabled = !!value; + } + result |= result2; + + return result; +} + +bool CWeapon::ProcessRpmUpgrade( LPCSTR section, LPCSTR paramName, float &timeToFireProperty, bool test) +{ + bool result2; + float rpm = 60.0f / timeToFireProperty; + result2 = process_if_exists( section, paramName, &CInifile::r_float, rpm, test ); + if ( result2 && !test ) + { + VERIFY( rpm > 0.0f ); + timeToFireProperty = 60.0f / rpm; + } + return result2; +} + +bool CWeapon::install_upgrade_hit( LPCSTR section, bool test ) +{ + bool result = false; + + shared_str s_sHitPower; + bool result2 = process_if_exists_set( section, "hit_power", &CInifile::r_string_wb, s_sHitPower, test ); + if ( result2 && !test ) + { + string32 buffer; + fvHitPower[egdMaster] = (float)atof( _GetItem( *s_sHitPower, 0, buffer ) ); + fvHitPower[egdNovice] = fvHitPower[egdStalker] = fvHitPower[egdVeteran] = fvHitPower[egdMaster]; + + int num_game_diff_param = _GetItemCount( *s_sHitPower ); + if ( num_game_diff_param > 1 ) { fvHitPower[egdVeteran] = (float)atof( _GetItem( *s_sHitPower, 1, buffer ) ); } + if ( num_game_diff_param > 2 ) { fvHitPower[egdStalker] = (float)atof( _GetItem( *s_sHitPower, 2, buffer ) ); } + if ( num_game_diff_param > 3 ) { fvHitPower[egdNovice] = (float)atof( _GetItem( *s_sHitPower, 3, buffer ) ); } + } + result |= result2; + + shared_str s_sHitPowerCritical; + result2 = process_if_exists_set( section, "hit_power_critical", &CInifile::r_string_wb, s_sHitPower, test ); + if ( result2 && !test ) + { + string32 buffer; + fvHitPowerCritical[egdMaster] = (float)atof(_GetItem(*s_sHitPowerCritical,0,buffer)); + fvHitPowerCritical[egdNovice] = fvHitPowerCritical[egdStalker] = fvHitPowerCritical[egdVeteran] = fvHitPowerCritical[egdMaster]; + + int num_game_diff_param = _GetItemCount(*s_sHitPowerCritical); + if ( num_game_diff_param > 1 ) { fvHitPowerCritical[egdVeteran] = (float)atof(_GetItem(*s_sHitPowerCritical,1,buffer)); } + if ( num_game_diff_param > 2 ) { fvHitPowerCritical[egdStalker] = (float)atof(_GetItem(*s_sHitPowerCritical,2,buffer)); } + if ( num_game_diff_param > 3 ) { fvHitPowerCritical[egdNovice] = (float)atof(_GetItem(*s_sHitPowerCritical,3,buffer)); } + } + result |= result2; + + result |= process_if_exists( section, "hit_impulse", &CInifile::r_float, fHitImpulse, test ); + result |= process_if_exists( section, "bullet_speed", &CInifile::r_float, m_fStartBulletSpeed, test ); + result |= process_if_exists( section, "air_resistance_factor", &CInifile::r_float, m_air_resistance_factor, test ); + + /* + silencer_hit_power = 0.55, 0.55, 0.55, 0.55 + silencer_hit_impulse = 120 + silencer_fire_distance = 600 + silencer_bullet_speed = 310 + */ + + result |= process_if_exists_set( section, "use_aim_bullet", &CInifile::r_bool, m_bUseAimBullet, test ); + if ( m_bUseAimBullet ) // first super bullet + { + result |= process_if_exists( section, "time_to_aim", &CInifile::r_float, m_fTimeToAim, test ); + } + +// LPCSTR weapon_section = cNameSect().c_str(); + //pSettings->r_float( weapon_section, "rpm" ); // fOneShotTime * 60.0f; + + result |= ProcessRpmUpgrade( section, "rpm", fTimeToFire, test ); + + return result; +} + + +bool CWeapon::install_upgrade_addon( LPCSTR section, bool test ) +{ + bool result = false; + bool result2; + //LPCSTR weapon_section = cNameSect().c_str(); + + // 0 - no addon // 1 - permanent // 2 - attachable + int temp_int; + + result |= result2 = process_if_exists_set(section, "scope_force_icon", &CInifile::r_bool, temp_int, test); + if (result2 && !test) m_bScopeForceIcon = !!temp_int; + result |= result2 = process_if_exists_set(section, "silencer_force_icon", &CInifile::r_bool, temp_int, test); + if (result2 && !test) m_bSilencerForceIcon = !!temp_int; + result |= result2 = process_if_exists_set(section, "grenade_launcher_force_icon", &CInifile::r_bool, temp_int, test); + if (result2 && !test) m_bGrenadeLauncherForceIcon = !!temp_int; + + result |= result2 = process_if_exists_set( section, "scope_status", &CInifile::r_s32, temp_int, test ); + if (result2 && !test) m_eScopeStatus = (ALife::EWeaponAddonStatus)temp_int; + + result |= process_if_exists( section, "holder_range_modifier", &CInifile::r_float, m_addon_holder_range_modifier, test ); + result |= process_if_exists( section, "holder_fov_modifier", &CInifile::r_float, m_addon_holder_fov_modifier, test ); + + if ( m_eScopeStatus == ALife::eAddonAttachable || m_bScopeForceIcon ) + { + if (pSettings->line_exist(section, "scopes_sect")) + { + LPCSTR str = pSettings->r_string(section, "scopes_sect"); + for (int i = 0, count = _GetItemCount(str); i < count; ++i ) + { + string128 scope_section; + _GetItem (str, i, scope_section); + m_scopes.push_back (scope_section); + } + } + else if (pSettings->line_exist(section, "scope_name")) + { + m_scopes.push_back(section); + // This allows to change "forced scope icon" via upgrade + if (m_eScopeStatus != ALife::eAddonAttachable) + { + m_cur_scope = u8(m_scopes.size() - 1); + } + } + } + + result |= process_if_exists_set( section, "scope_dynamic_zoom", &CInifile::r_bool, m_zoom_params.m_bUseDynamicZoom, test ); + result |= process_if_exists_set( section, "scope_nightvision", &CInifile::r_string_wb, m_zoom_params.m_sUseZoomPostprocess, test ); + result |= process_if_exists_set( section, "scope_alive_detector", &CInifile::r_string_wb, m_zoom_params.m_sUseBinocularVision, test ); + + result |= result2 = process_if_exists_set( section, "silencer_status", &CInifile::r_s32, temp_int, test ); + if (result2 && !test) m_eSilencerStatus = (ALife::EWeaponAddonStatus)temp_int; + + result |= process_if_exists_set( section, "silencer_name", &CInifile::r_string, m_sSilencerName, test ); + result |= process_if_exists_set( section, "silencer_x", &CInifile::r_s32, m_iSilencerX, test ); + result |= process_if_exists_set( section, "silencer_y", &CInifile::r_s32, m_iSilencerY, test ); + + result |= result2 = process_if_exists_set( section, "grenade_launcher_status", &CInifile::r_s32, temp_int, test ); + if (result2 && !test) m_eGrenadeLauncherStatus = (ALife::EWeaponAddonStatus)temp_int; + + result |= process_if_exists_set( section, "grenade_launcher_name", &CInifile::r_string, m_sGrenadeLauncherName, test ); + result |= process_if_exists_set( section, "grenade_launcher_x", &CInifile::r_s32, m_iGrenadeLauncherX, test ); + result |= process_if_exists_set( section, "grenade_launcher_y", &CInifile::r_s32, m_iGrenadeLauncherY, test ); + + return result; +} diff --git a/src/xrGameLA/WeaponVal.cpp b/src/xrGameLA/WeaponVal.cpp new file mode 100644 index 000000000..e88f7f871 --- /dev/null +++ b/src/xrGameLA/WeaponVal.cpp @@ -0,0 +1,20 @@ +#include "pch_script.h" +#include "weaponval.h" + +CWeaponVal::CWeaponVal(void) : CWeaponMagazined("VAL",SOUND_TYPE_WEAPON_SUBMACHINEGUN) +{} + +CWeaponVal::~CWeaponVal(void) +{} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponVal::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponVal") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponVal.h b/src/xrGameLA/WeaponVal.h new file mode 100644 index 000000000..d7dfd2e75 --- /dev/null +++ b/src/xrGameLA/WeaponVal.h @@ -0,0 +1,18 @@ +#pragma once + +#include "weaponmagazined.h" +#include "script_export_space.h" + +class CWeaponVal : + public CWeaponMagazined +{ + typedef CWeaponMagazined inherited; +public: + CWeaponVal(void); + virtual ~CWeaponVal(void); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponVal) +#undef script_type_list +#define script_type_list save_type_list(CWeaponVal) diff --git a/src/xrGameLA/WeaponVintorez.cpp b/src/xrGameLA/WeaponVintorez.cpp new file mode 100644 index 000000000..0658a9cdb --- /dev/null +++ b/src/xrGameLA/WeaponVintorez.cpp @@ -0,0 +1,20 @@ +#include "pch_script.h" +#include "weaponvintorez.h" + +CWeaponVintorez::CWeaponVintorez(void) : CWeaponMagazined("VINTOREZ",SOUND_TYPE_WEAPON_SNIPERRIFLE) +{} + +CWeaponVintorez::~CWeaponVintorez(void) +{} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponVintorez::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponVintorez") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponVintorez.h b/src/xrGameLA/WeaponVintorez.h new file mode 100644 index 000000000..2916d65b5 --- /dev/null +++ b/src/xrGameLA/WeaponVintorez.h @@ -0,0 +1,18 @@ +#pragma once + +#include "weaponmagazined.h" +#include "script_export_space.h" + +class CWeaponVintorez : + public CWeaponMagazined +{ + typedef CWeaponMagazined inherited; +public: + CWeaponVintorez(void); + virtual ~CWeaponVintorez(void); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponVintorez) +#undef script_type_list +#define script_type_list save_type_list(CWeaponVintorez) diff --git a/src/xrGameLA/WeaponWalther.cpp b/src/xrGameLA/WeaponWalther.cpp new file mode 100644 index 000000000..212b233e8 --- /dev/null +++ b/src/xrGameLA/WeaponWalther.cpp @@ -0,0 +1,20 @@ +#include "pch_script.h" +#include "weaponwalther.h" + +CWeaponWalther::CWeaponWalther(void) : CWeaponPistol("WALTHER") +{} + +CWeaponWalther::~CWeaponWalther(void) +{} + +using namespace luabind; + +#pragma optimize("s",on) +void CWeaponWalther::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponWalther") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/WeaponWalther.h b/src/xrGameLA/WeaponWalther.h new file mode 100644 index 000000000..06adcfe91 --- /dev/null +++ b/src/xrGameLA/WeaponWalther.h @@ -0,0 +1,18 @@ +#pragma once + +#include "weaponpistol.h" +#include "script_export_space.h" + +class CWeaponWalther : + public CWeaponPistol +{ + typedef CWeaponPistol inherited; +public: + CWeaponWalther(void); + virtual ~CWeaponWalther(void); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CWeaponWalther) +#undef script_type_list +#define script_type_list save_type_list(CWeaponWalther) diff --git a/src/xrGameLA/WeaponZoomable.cpp b/src/xrGameLA/WeaponZoomable.cpp new file mode 100644 index 000000000..be0e2820f --- /dev/null +++ b/src/xrGameLA/WeaponZoomable.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "WeaponZoomable.h" + +CWeaponZoomable::CWeaponZoomable() : CWeaponBinoculars() +{ +} + +CWeaponZoomable::~CWeaponZoomable() +{ +} + +void CWeaponZoomable::Load (LPCSTR section) +{ + inherited::Load(section); +} + +void CWeaponZoomable::GetBriefInfo(xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count) +{ + CWeaponCustomPistol::GetBriefInfo (str_name, icon_sect_name, str_count); +} + +bool CWeaponZoomable::Action(u16 cmd, u32 flags) +{ + return CWeaponCustomPistol::Action(cmd, flags); +} + +void CWeaponZoomable::OnZoomIn () +{ + inherited::OnZoomIn(); +} + +void CWeaponZoomable::OnZoomOut () +{ + inherited::OnZoomOut(); +} + +void CWeaponZoomable::ZoomInc() +{ + inherited::ZoomInc(); +} + +void CWeaponZoomable::ZoomDec() +{ + inherited::ZoomDec(); +} \ No newline at end of file diff --git a/src/xrGameLA/WeaponZoomable.h b/src/xrGameLA/WeaponZoomable.h new file mode 100644 index 000000000..8dd1cb568 --- /dev/null +++ b/src/xrGameLA/WeaponZoomable.h @@ -0,0 +1,28 @@ +#pragma once +#include "weaponbinoculars.h" +#include "script_export_space.h" + +class CWeaponZoomable : public CWeaponBinoculars +{ +private: + typedef CWeaponBinoculars inherited; +public: + CWeaponZoomable(); + virtual ~CWeaponZoomable(); + + void Load (LPCSTR section); + + virtual void OnZoomIn (); + virtual void OnZoomOut (); + virtual void ZoomInc (); + virtual void ZoomDec (); + + virtual bool Action (u16 cmd, u32 flags); + virtual void GetBriefInfo (xr_string& str_name, xr_string& icon_sect_name, xr_string& str_count); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CWeaponZoomable) +#undef script_type_list +#define script_type_list save_type_list(CWeaponZoomable) \ No newline at end of file diff --git a/src/xrGameLA/WeaponZoomable_script.cpp b/src/xrGameLA/WeaponZoomable_script.cpp new file mode 100644 index 000000000..f406e9482 --- /dev/null +++ b/src/xrGameLA/WeaponZoomable_script.cpp @@ -0,0 +1,13 @@ +#include "pch_script.h" +#include "Weaponzoomable.h" + +using namespace luabind; + +void CWeaponZoomable::script_register (lua_State *L) +{ + module(L) + [ + class_("CWeaponZoomable") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/Wound.cpp b/src/xrGameLA/Wound.cpp new file mode 100644 index 000000000..a70816915 --- /dev/null +++ b/src/xrGameLA/Wound.cpp @@ -0,0 +1,99 @@ +// Wound.cpp: класс описания раны +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "wound.h" +#include "../../xrNetServer/net_utils.h" +#include "../bone.h" + +CWound::CWound(u16 bone_num) +{ + m_bToBeDestroy = false; + + m_Wounds.resize (ALife::eHitTypeMax); + for(int i=0; i=0.0f && m_Wounds[i]<=WOUND_MAX); + } +} + + +float CWound::TotalSize() +{ + float total_size = 0.f; + for(int i=0; iStop (FALSE); + CParticlesObject::Destroy (m_pDisabledParticles); + } + + m_disabled_sound.stop (); + m_disabled_sound.destroy (); + + LPCSTR str = pSettings->r_string(cNameSect(),"enabling_particles"); + m_pEnablingParticles = CParticlesObject::Create(str,FALSE); + m_pEnablingParticles->UpdateParent(XFORM(),zero_vel); + m_pEnablingParticles->Play (false); +} + +void CZoneCampfire::GoDisabledState() +{ + inherited::GoDisabledState (); + + R_ASSERT (NULL==m_pDisabledParticles); + LPCSTR str = pSettings->r_string(cNameSect(),"disabled_particles"); + m_pDisabledParticles = CParticlesObject::Create(str,FALSE); + m_pDisabledParticles->UpdateParent (XFORM(),zero_vel); + m_pDisabledParticles->Play (false); + + + str = pSettings->r_string (cNameSect(),"disabled_sound"); + m_disabled_sound.create (str, st_Effect,sg_SourceType); + m_disabled_sound.play_at_pos (0, Position(), true); +} + +#define OVL_TIME 3000 +void CZoneCampfire::turn_on_script() +{ + if( psDeviceFlags.test(rsR2|rsR3|rsR4) ) + { + m_turn_time = Device.dwTimeGlobal+OVL_TIME; + m_turned_on = true; + GoEnabledState (); + } +} + +void CZoneCampfire::turn_off_script() +{ + if( psDeviceFlags.test(rsR2|rsR3|rsR4) ) + { + m_turn_time = Device.dwTimeGlobal+OVL_TIME; + m_turned_on = false; + GoDisabledState (); + } +} + +bool CZoneCampfire::is_on() +{ + return m_turned_on; +} + +void CZoneCampfire::shedule_Update(u32 dt) +{ + if (!IsEnabled() && m_turn_time) + { + UpdateWorkload (dt); + } + + if(m_pIdleParticles) + { + Fvector vel; + vel.mul(Fvector().set(1.f,0.f,0.f), GamePersistent().Environment().wind_strength_factor); + m_pIdleParticles->UpdateParent(XFORM(),vel); + } + inherited::shedule_Update(dt); +} + + +void CZoneCampfire::PlayIdleParticles() +{ + if(m_turn_time==0 || m_turn_time-Device.dwTimeGlobal<(OVL_TIME-2000)) + { + inherited::PlayIdleParticles(); + if(m_pEnablingParticles) + { + m_pEnablingParticles->Stop (FALSE); + CParticlesObject::Destroy (m_pEnablingParticles); + } + } +} + +void CZoneCampfire::StopIdleParticles() +{ + if(m_turn_time==0 || m_turn_time-Device.dwTimeGlobal<(OVL_TIME-500)) + inherited::StopIdleParticles(); +} + +BOOL CZoneCampfire::AlwaysTheCrow() +{ + if(m_turn_time) + return TRUE; + else + return inherited::AlwaysTheCrow(); +} + +void CZoneCampfire::UpdateWorkload(u32 dt) +{ + inherited::UpdateWorkload(dt); + if(m_turn_time>Device.dwTimeGlobal) + { + float k = float(m_turn_time-Device.dwTimeGlobal)/float(OVL_TIME); + + if(m_turned_on) + { + k = 1.0f-k; + PlayIdleParticles (); + StartIdleLight (); + }else + { + StopIdleParticles (); + } + + if(m_pIdleLight && m_pIdleLight->get_active()) + { + VERIFY(m_pIdleLAnim); + int frame = 0; + u32 clr = m_pIdleLAnim->CalculateBGR(Device.fTimeGlobal,frame); + Fcolor fclr; + fclr.set ( ((float)color_get_B(clr)/255.f)*k, + ((float)color_get_G(clr)/255.f)*k, + ((float)color_get_R(clr)/255.f)*k, + 1.f); + + float range = m_fIdleLightRange + 0.25f*::Random.randF(-1.f,1.f); + range *= k; + + m_pIdleLight->set_range (range); + m_pIdleLight->set_color (fclr); + } + + + } + else if(m_turn_time) + { + m_turn_time = 0; + if(m_turned_on) + { + PlayIdleParticles(); + }else + { + StopIdleParticles(); + } + } +} \ No newline at end of file diff --git a/src/xrGameLA/ZoneCampfire.h b/src/xrGameLA/ZoneCampfire.h new file mode 100644 index 000000000..0436b020e --- /dev/null +++ b/src/xrGameLA/ZoneCampfire.h @@ -0,0 +1,34 @@ +#pragma once +#include "MosquitoBald.h" + +class CZoneCampfire : public CMosquitoBald +{ + typedef CMosquitoBald inherited; +protected: + CParticlesObject* m_pEnablingParticles; + CParticlesObject* m_pDisabledParticles; + ref_sound m_disabled_sound; + bool m_turned_on; + u32 m_turn_time; + + virtual void PlayIdleParticles (); + virtual void StopIdleParticles (); + virtual BOOL AlwaysTheCrow (); + virtual void UpdateWorkload (u32 dt); + +public: + CZoneCampfire (); + virtual ~CZoneCampfire (); + virtual void Load (LPCSTR section); + virtual void GoEnabledState (); + virtual void GoDisabledState (); + + void turn_on_script (); + void turn_off_script (); + bool is_on (); + virtual void shedule_Update (u32 dt ); + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CZoneCampfire) +#undef script_type_list +#define script_type_list save_type_list(CZoneCampfire) \ No newline at end of file diff --git a/src/xrGameLA/ZoneGalantine.cpp b/src/xrGameLA/ZoneGalantine.cpp new file mode 100644 index 000000000..70cd582aa --- /dev/null +++ b/src/xrGameLA/ZoneGalantine.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "zonegalantine.h" + +CZoneGalantine::CZoneGalantine(void) +{ + m_fActorBlowoutRadiusPercent=0.5f; +} + +CZoneGalantine::~CZoneGalantine(void) +{ +} + +BOOL CZoneGalantine::net_Spawn(CSE_Abstract* DC) +{ + return inherited::net_Spawn(DC); +} \ No newline at end of file diff --git a/src/xrGameLA/ZoneGalantine.h b/src/xrGameLA/ZoneGalantine.h new file mode 100644 index 000000000..51edecc2e --- /dev/null +++ b/src/xrGameLA/ZoneGalantine.h @@ -0,0 +1,19 @@ +#pragma once + +#include "mincer.h" + +class CZoneGalantine : public CMincer +{ +private: + typedef CMincer inherited; +public: + CZoneGalantine (); + virtual ~CZoneGalantine (); + virtual BOOL net_Spawn (CSE_Abstract* DC); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CZoneGalantine) +#undef script_type_list +#define script_type_list save_type_list(CZoneGalantine) \ No newline at end of file diff --git a/src/xrGameLA/ZoneMine.cpp b/src/xrGameLA/ZoneMine.cpp new file mode 100644 index 000000000..e36fc443b --- /dev/null +++ b/src/xrGameLA/ZoneMine.cpp @@ -0,0 +1,92 @@ +#include "stdafx.h" +#include "zonemine.h" +#include "hudmanager.h" +#include "ParticlesObject.h" +#include "level.h" +#include "physicsshellholder.h" +#include "../xr_collide_form.h" + +CZoneMine::CZoneMine(void) +{ + m_dwDeltaTime = 0; + m_fHitImpulseScale = 1.f; + + m_bLastBlowoutUpdate = false; +} + +CZoneMine::~CZoneMine(void) +{ +} + +void CZoneMine::Load(LPCSTR section) +{ + inherited::Load(section); +} + + +void CZoneMine::Postprocess(f32 /**val/**/) +{ +} + +bool CZoneMine::BlowoutState() +{ + bool result = inherited::BlowoutState(); + if(!result) + { + m_bLastBlowoutUpdate = false; + UpdateBlowout(); + } + else if(!m_bLastBlowoutUpdate) + { + m_bLastBlowoutUpdate = true; + UpdateBlowout(); + } + + return result; +} + +void CZoneMine::Affect(SZoneObjectInfo* O) +{ + CPhysicsShellHolder *pGameObject = smart_cast(O->object); + if(!pGameObject) return; + + if(O->zone_ignore) return; + + Fvector P; + XFORM().transform_tiny(P,CFORM()->getSphere().P); + +#ifdef DEBUG + char l_pow[255]; + xr_sprintf(l_pow, "zone hit. %.1f", Power(pGameObject->Position().distance_to(P))); + if(bDebug) Msg("%s %s",*pGameObject->cName(), l_pow); +#endif + + Fvector hit_dir; + hit_dir.set(::Random.randF(-.5f,.5f), + ::Random.randF(.0f,1.f), + ::Random.randF(-.5f,.5f)); + hit_dir.normalize(); + + + Fvector position_in_bone_space; + + VERIFY(!pGameObject->getDestroy()); + + float dist = pGameObject->Position().distance_to(P) - pGameObject->Radius(); + float power = Power(dist>0.f?dist:0.f); + float impulse = m_fHitImpulseScale*power*pGameObject->GetMass(); + + //статистика по объекту + O->total_damage += power; + O->hit_num++; + + if(power > 0.01f) + { + m_dwDeltaTime = 0; + position_in_bone_space.set(0.f,0.f,0.f); + + CreateHit(pGameObject->ID(),ID(),hit_dir,power,0,position_in_bone_space,impulse,m_eHitTypeBlowout); + + PlayHitParticles(pGameObject); + } +} diff --git a/src/xrGameLA/ZoneMine.h b/src/xrGameLA/ZoneMine.h new file mode 100644 index 000000000..23138e107 --- /dev/null +++ b/src/xrGameLA/ZoneMine.h @@ -0,0 +1,35 @@ +/* +Gr1ph00n +30/06/12 +*/ +#pragma once + +#include "customzone.h" +#include "script_export_space.h" + +class CZoneMine : public CCustomZone +{ +private: + typedef CCustomZone inherited; +public: + CZoneMine(void); + virtual ~CZoneMine(void); + + virtual void Load(LPCSTR section); + virtual void Postprocess(f32 val); + virtual bool EnableEffector() {return true;} + + + virtual void Affect(SZoneObjectInfo* O); + +protected: + virtual bool BlowoutState(); + //для того чтобы blowout обновился один раз + //после того как зона перключилась в другое состояние + bool m_bLastBlowoutUpdate; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CZoneMine) +#undef script_type_list +#define script_type_list save_type_list(CZoneMine) \ No newline at end of file diff --git a/src/xrGameLA/ZoneVisual.cpp b/src/xrGameLA/ZoneVisual.cpp new file mode 100644 index 000000000..b04d387b7 --- /dev/null +++ b/src/xrGameLA/ZoneVisual.cpp @@ -0,0 +1,80 @@ +#include "stdafx.h" +#include "CustomZone.h" +#include "ZoneVisual.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "../../Include/xrRender/RenderVisual.h" +#include "../../Include/xrRender/KinematicsAnimated.h" + +CVisualZone::CVisualZone () +{ +} +CVisualZone::~CVisualZone () +{ + +} + +BOOL CVisualZone:: net_Spawn (CSE_Abstract* DC) +{ + BOOL ret = inherited::net_Spawn(DC); + + CSE_Abstract* entity = (CSE_Abstract*)(DC); + + LPCSTR attack_anim_name = pSettings->r_string(cNameSect_str(), "attack_anim_name"); + LPCSTR idle_anim_name = pSettings->r_string(cNameSect_str(), "idle_anim_name"); + + IKinematicsAnimated* animated = smart_cast(Visual()); + + R_ASSERT2(animated, make_string("object is not animated %s", cNameVisual().c_str())); + + m_attack_animation = animated->ID_Cycle(attack_anim_name); + m_idle_animation = animated->ID_Cycle(idle_anim_name); + + R_ASSERT2(m_attack_animation.valid(), make_string("! Can't find anim cycle: %s %s %s", DC->s_name.c_str(), cNameVisual().c_str(), attack_anim_name)); + R_ASSERT2(m_idle_animation.valid(), make_string("! Can't find anim cycle: %s %s %s", DC->s_name.c_str(), cNameVisual().c_str(), idle_anim_name)); + + animated->PlayCycle(m_idle_animation); + + setVisible(TRUE); + + return ret; +} +void CVisualZone::net_Destroy() +{ + inherited::net_Destroy(); + +} +void CVisualZone:: AffectObjects () +{ + inherited::AffectObjects (); +// smart_cast(Visual())->PlayCycle(*m_attack_animation); +} +void CVisualZone::SwitchZoneState(EZoneState new_state) +{ + if(m_eZoneState==eZoneStateBlowout && new_state != eZoneStateBlowout) + { + // CKinematicsAnimated* SA=smart_cast(Visual()); + smart_cast(Visual())->PlayCycle(m_idle_animation); + } + + inherited::SwitchZoneState(new_state); + +} +void CVisualZone::Load(LPCSTR section) +{ + inherited::Load(section); + m_dwAttackAnimaionStart =pSettings->r_u32(section,"attack_animation_start"); + m_dwAttackAnimaionEnd =pSettings->r_u32(section,"attack_animation_end"); + VERIFY2(m_dwAttackAnimaionStart=(u32)m_iPreviousStateTime && + m_dwAttackAnimaionStart <(u32)m_iStateTime) + smart_cast(Visual())->PlayCycle(m_attack_animation); + + if(m_dwAttackAnimaionEnd >=(u32)m_iPreviousStateTime && + m_dwAttackAnimaionEnd <(u32)m_iStateTime) + smart_cast(Visual())->PlayCycle(m_idle_animation); +} \ No newline at end of file diff --git a/src/xrGameLA/ZoneVisual.h b/src/xrGameLA/ZoneVisual.h new file mode 100644 index 000000000..8039709d6 --- /dev/null +++ b/src/xrGameLA/ZoneVisual.h @@ -0,0 +1,24 @@ +#pragma once + +#include "../../Include/xrRender/animation_motion.h " + +class CVisualZone : + public CCustomZone +{ + typedef CCustomZone inherited ; + MotionID m_idle_animation ; + MotionID m_attack_animation ; + u32 m_dwAttackAnimaionStart ; + u32 m_dwAttackAnimaionEnd ; +public: + CVisualZone () ; + virtual ~CVisualZone () ; + virtual BOOL net_Spawn (CSE_Abstract* DC) ; + virtual void net_Destroy () ; + virtual void AffectObjects () ; + virtual void SwitchZoneState (EZoneState new_state) ; + virtual void Load (LPCSTR section) ; + virtual void UpdateBlowout () ; +protected: +private: +}; diff --git a/src/xrGameLA/ZudaArtifact.cpp b/src/xrGameLA/ZudaArtifact.cpp new file mode 100644 index 000000000..dd99d31c5 --- /dev/null +++ b/src/xrGameLA/ZudaArtifact.cpp @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////// +// ZudaArtifact.cpp +// ZudaArtefact - артефакт "зуда" +/////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ZudaArtifact.h" +#include "PhysicsShell.h" + + +CZudaArtefact::CZudaArtefact(void) +{ +} + +CZudaArtefact::~CZudaArtefact(void) +{ +} + +void CZudaArtefact::Load(LPCSTR section) +{ + inherited::Load(section); +} diff --git a/src/xrGameLA/ZudaArtifact.h b/src/xrGameLA/ZudaArtifact.h new file mode 100644 index 000000000..d8ae2b643 --- /dev/null +++ b/src/xrGameLA/ZudaArtifact.h @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////// +// ZudaArtifact.h +// ZudaArtefact - артефакт зуда +/////////////////////////////////////////////////////////////// + +#pragma once +#include "artifact.h" + +class CZudaArtefact : public CArtefact +{ +private: + typedef CArtefact inherited; +public: + CZudaArtefact(void); + virtual ~CZudaArtefact(void); + + virtual void Load (LPCSTR section); + +protected: +}; \ No newline at end of file diff --git a/src/xrGameLA/a_star.h b/src/xrGameLA/a_star.h new file mode 100644 index 000000000..7b9a35233 --- /dev/null +++ b/src/xrGameLA/a_star.h @@ -0,0 +1,135 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : a_star.h +// Created : 21.03.2002 +// Modified : 02.03.2004 +// Author : Dmitriy Iassenev +// Description : Implementation of the A* (a-star) algorithm +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "vertex_path.h" +#include "data_storage_constructor.h" +#include "dijkstra.h" + +namespace AStar { + template < + typename _dist_type, + template class T1 + > + struct _Vertex { + template + struct _vertex : public T1 { + typedef _dist_type _dist_type; + + _dist_type _g; + _dist_type _h; + + IC _dist_type &g() + { + return (_g); + } + + IC _dist_type &h() + { + return (_h); + } + }; + }; +} + +template < + typename _dist_type, + typename _priority_queue, + typename _vertex_manager, + typename _vertex_allocator, + bool euclidian_heuristics = true, + typename _data_storage_base = CVertexPath, + template class _vertex = CEmptyClassTemplate, + template < + typename _1, + typename _2 + > + class _builder_allocator_constructor = CBuilderAllocatorConstructor, + template < + typename _1, + typename _2, + typename _3, + template < + typename _1, + typename _2 + > + class _4 + > + class _manager_builder_allocator_constructor = CManagerBuilderAllocatorConstructor, + template < + typename _algorithm, + typename _manager, + typename _builder, + typename _allocator, + template class _vertex, + template < + typename _1, + typename _2 + > + class _builder_allocator_constructor = CBuilderAllocatorConstructor, + template < + typename _1, + typename _2, + typename _3, + template < + typename _1, + typename _2 + > + class _4 + > + class _manager_builder_allocator_constructor = CManagerBuilderAllocatorConstructor + > + class _data_storage_constructor = CDataStorageConstructor, + typename _iteration_type = u32 +> class CAStar : public CDijkstra < + _dist_type, + _priority_queue, + _vertex_manager, + _vertex_allocator, + euclidian_heuristics, + _data_storage_base, + AStar::_Vertex<_dist_type,_vertex>::_vertex, + _builder_allocator_constructor, + _manager_builder_allocator_constructor, + _data_storage_constructor, + _iteration_type + > +{ +protected: + typedef CDijkstra < + _dist_type, + _priority_queue, + _vertex_manager, + _vertex_allocator, + euclidian_heuristics, + _data_storage_base, + AStar::_Vertex<_dist_type,_vertex>::_vertex, + _builder_allocator_constructor, + _manager_builder_allocator_constructor, + _data_storage_constructor, + _iteration_type + > inherited; + typedef typename CDataStorage::CGraphVertex CGraphVertex; + typedef typename CGraphVertex::_dist_type _dist_type; + typedef typename CGraphVertex::_index_type _index_type; + +protected: + template + IC void initialize (_PathManager &path_manager); + template + IC bool step (_PathManager &path_manager); + +public: + IC CAStar (const u32 max_vertex_count); + virtual ~CAStar (); + template + IC bool find (_PathManager &path_manager); +}; + +#include "a_star_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/a_star_inline.h b/src/xrGameLA/a_star_inline.h new file mode 100644 index 000000000..a316fe2b4 --- /dev/null +++ b/src/xrGameLA/a_star_inline.h @@ -0,0 +1,265 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : a_star_inline.h +// Created : 21.03.2002 +// Modified : 02.03.2004 +// Author : Dmitriy Iassenev +// Description : Implementation of the A* (a-star) algorithm : inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#define TEMPLATE_SPECIALIZATION \ + template <\ + typename _dist_type,\ + typename _priority_queue,\ + typename _vertex_manager,\ + typename _vertex_allocator,\ + bool euclidian_heuristics,\ + typename _data_storage_base,\ + template class _vertex,\ + template <\ + typename _1,\ + typename _2\ + >\ + class _builder_allocator_constructor,\ + template <\ + typename _1,\ + typename _2,\ + typename _3,\ + template <\ + typename _1,\ + typename _2\ + >\ + class _4\ + >\ + class _manager_builder_allocator_constructor,\ + template <\ + typename _algorithm,\ + typename _manager,\ + typename _builder,\ + typename _allocator,\ + template class _vertex,\ + template <\ + typename _1,\ + typename _2\ + >\ + class _builder_allocator_constructor,\ + template <\ + typename _1,\ + typename _2,\ + typename _3,\ + template <\ + typename _1,\ + typename _2\ + >\ + class _4\ + >\ + class _manager_builder_allocator_constructor\ + >\ + class _data_storage_constructor,\ + typename _iteration_type\ + > + +#define CSAStar CAStar<\ + _dist_type,\ + _priority_queue,\ + _vertex_manager,\ + _vertex_allocator,\ + euclidian_heuristics,\ + _data_storage_base,\ + _vertex,\ + _builder_allocator_constructor,\ + _manager_builder_allocator_constructor,\ + _data_storage_constructor,\ + _iteration_type\ +> + +TEMPLATE_SPECIALIZATION +IC CSAStar::CAStar (const u32 max_vertex_count) : + inherited (max_vertex_count) +{ +} + +TEMPLATE_SPECIALIZATION +CSAStar::~CAStar () +{ +} + +TEMPLATE_SPECIALIZATION +template +IC void CSAStar::initialize (_PathManager &path_manager) +{ + THROW2 (!m_search_started,"Recursive graph engine usage is not allowed!"); + m_search_started = true; + // initialize data structures before we started path search + data_storage().init (); + + // initialize path manager before we started path search + path_manager.init (); + + // create a node + CGraphVertex &start = data_storage().create_vertex(path_manager.start_node()); + + // assign correspoding values to the created node + start.g() = _dist_type(0); + start.h() = path_manager.estimate(start.index()); + start.f() = start.g() + start.h(); + + // assign null parent to the start node + data_storage().assign_parent (start,0); + + // add start node to the opened list + data_storage().add_opened (start); +} + +TEMPLATE_SPECIALIZATION +template +IC bool CSAStar::step (_PathManager &path_manager) +{ + // get the best node, i.e. a node with the minimum 'f' + CGraphVertex &best = data_storage().get_best(); + + // check if this node is the one we are searching for + if (path_manager.is_goal_reached(best.index())) { + // we reached the goal, so we have to create a path + path_manager.init_path (); + path_manager.create_path (best); + // and return success + return (true); + } + + // put best node to the closed list + data_storage().add_best_closed(); + // and remove this node from the opened one + data_storage().remove_best_opened(); + + // iterating on the best node neighbours + _PathManager::const_iterator i; + _PathManager::const_iterator e; + path_manager.begin (best.index(),i,e); + for ( ; i != e; ++i) { + const _index_type &neighbour_index = path_manager.get_value(i); + // check if neighbour is accessible + if (!path_manager.is_accessible(neighbour_index)) + continue; + // check if neighbour is visited, i.e. is in the opened or + // closed lists + if (data_storage().is_visited(neighbour_index)) { + // so, this neighbour node has been already visited + // therefore get the pointer to this node + CGraphVertex &neighbour = data_storage().get_node(neighbour_index); + // check if this node is in the opened list + if (data_storage().is_opened(neighbour)) { + // compute 'g' for the node + _dist_type g = best.g() + path_manager.evaluate(best.index(),neighbour_index,i); + // check if new path is better than the older one + if (neighbour.g() > g) { + // so, new path is better + // assign corresponding values to the node + _dist_type d = neighbour.f(); + neighbour.g() = g; + neighbour.f() = neighbour.g() + neighbour.h(); + // assign correct parent to the node to be able + // to retreive a path + data_storage().assign_parent (neighbour,&best,path_manager.edge(i)); + // notify data storage about node decreasing value + data_storage().decrease_opened(neighbour,d); + // continue iterating on neighbours + continue; + } + // so, new path is worse + // continue iterating on neighbours + continue; + } + // so, our node is in the closed list + // here is a _nuance_ : if we don't use any heuristics, + // i.e. it is not A*, but Dijkstra algorithm, or we use + // a heuristics which _guarantees_ that found path is + // the best among the others (if we have a path_manager with + // euclidian metrics and use distance between current + // and goal points as an estimation value), then it is + // impossible that we can find a better path for a node + // which is in the closed list and therefore we have to do + // nothing here. + if (!path_manager.is_metric_euclidian()) { + // so, we use a heurictics which doesn't gurantee that + // found path is the best, then we have to update all + // of the our node successors but we still can't be sure + // that when the condition 'is_goal_reached' is true, + // then we found the _best_ path + + // check if new path is better than the older one + _dist_type g = best.g() + path_manager.evaluate(best.index(),neighbour_index,i); + if (neighbour.g() > g) { + // so, new path is better + // assign corresponding values to the node + neighbour.g() = g; + neighbour.f() = neighbour.g() + neighbour.h(); + // assign correct parent to the node to be able + // to retreive a path + data_storage().assign_parent (neighbour,&best,path_manager.edge(i)); + // notify data storage about node decreasing value + // to make it modify all the node successors + data_storage().update_successors(neighbour); + // continue iterating on neighbours + continue; + } + // so, new path is worse + // continue iterating on neighbours + continue; + } + // continue iterating on neighbours + continue; + } + else { + // so, this neighbour node is not in the opened or closed lists + // put neighbour node to the opened list + CGraphVertex &neighbour = data_storage().create_vertex(neighbour_index); + // fill the corresponding node parameters + neighbour.g() = best.g() + path_manager.evaluate(best.index(),neighbour_index,i); + neighbour.h() = path_manager.estimate(neighbour.index()); + neighbour.f() = neighbour.g() + neighbour.h(); + // assign best node as its parent + data_storage().assign_parent(neighbour,&best,path_manager.edge(i)); + // add start node to the opened list + data_storage().add_opened (neighbour); + // continue iterating on neighbours + continue; + } + } + + // this iteration haven't got the goal node, therefore return failure + return (false); +} + +TEMPLATE_SPECIALIZATION +template +IC bool CSAStar::find (_PathManager &path_manager) +{ + // initialize data structures with new search + initialize (path_manager); + // iterate while opened list is not empty + for (_iteration_type i = _iteration_type(0); !data_storage().is_opened_empty(); ++i) { + // check if we reached limit + if (path_manager.is_limit_reached(i)) { + // so we reached limit, return failure + finalize (path_manager); + return (false); + } + + // so, limit is not reached + // check if new step will get us success + if (step(path_manager)) { + // so this step reached the goal, return success + finalize (path_manager); + return (true); + } + } + + // so, opened list is empty, return failure + finalize (path_manager); + return (false); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CSAStar \ No newline at end of file diff --git a/src/xrGameLA/abstract_location_selector.h b/src/xrGameLA/abstract_location_selector.h new file mode 100644 index 000000000..49292d596 --- /dev/null +++ b/src/xrGameLA/abstract_location_selector.h @@ -0,0 +1,70 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : abstract_location_selector.h +// Created : 02.10.2001 +// Modified : 19.11.2003 +// Author : Dmitriy Iassenev +// Description : Abstract location selector +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "restricted_object.h" + +template < + typename _Graph, + typename _VertexEvaluator, + typename _vertex_id_type +> +class CAbstractLocationSelector { +protected: + bool m_failed; + _VertexEvaluator *m_evaluator; + _vertex_id_type m_selected_vertex_id; + const _Graph *m_graph; + u32 m_last_query_time; + u32 m_query_interval; + xr_vector<_vertex_id_type> *m_path; + _vertex_id_type *dest_vertex_id; + CRestrictedObject *m_restricted_object; + +protected: + IC void perform_search (const _vertex_id_type game_vertex_id); + IC virtual void before_search (_vertex_id_type &vertex_id); + IC virtual void after_search (); + +public: + IC CAbstractLocationSelector (CRestrictedObject *object); + IC virtual ~CAbstractLocationSelector (); + IC virtual void reinit (const _Graph *graph = 0); + + IC _vertex_id_type get_selected_vertex_id () const; + + IC void set_query_interval (const u32 query_interval); + + IC void set_evaluator (_VertexEvaluator *evaluator); + + IC bool failed () const; + IC bool actual (const _vertex_id_type start_vertex_id, bool path_completed); + IC bool used () const; + IC void select_location (const _vertex_id_type start_vertex_id, bool path_completed); + // При поиске ноды сохранить найденный _кратчайший_ путь и найденную ноду + IC void set_dest_path (xr_vector<_vertex_id_type> &path); + IC void set_dest_vertex (_vertex_id_type &vertex_id); +}; + +#include "abstract_location_selector_inline.h" + +template < + typename _Graph, + typename _VertexEvaluator, + typename _vertex_id_type +> +class + CBaseLocationSelector : + public CAbstractLocationSelector < + _Graph, + _VertexEvaluator, + _vertex_id_type + > +{ +}; diff --git a/src/xrGameLA/abstract_location_selector_inline.h b/src/xrGameLA/abstract_location_selector_inline.h new file mode 100644 index 000000000..ff24f2b74 --- /dev/null +++ b/src/xrGameLA/abstract_location_selector_inline.h @@ -0,0 +1,151 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : location_selector_abstract_inline.h +// Created : 02.10.2001 +// Modified : 19.11.2003 +// Author : Dmitriy Iassenev +// Description : Abstract location selector inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "profiler.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Graph,\ + typename _VertexEvaluator,\ + typename _vertex_id_type\ +> + +#define CSelectorTemplate CAbstractLocationSelector<_Graph,_VertexEvaluator,_vertex_id_type> + +TEMPLATE_SPECIALIZATION +IC CSelectorTemplate::CAbstractLocationSelector (CRestrictedObject *object) +{ + m_restricted_object = object; + VERIFY (m_restricted_object); +} + +TEMPLATE_SPECIALIZATION +IC CSelectorTemplate::~CAbstractLocationSelector () +{ +} + +TEMPLATE_SPECIALIZATION +IC void CSelectorTemplate::reinit (const _Graph *graph) +{ + m_failed = false; + m_selected_vertex_id = _vertex_id_type(-1); + m_evaluator = 0; + m_last_query_time = 0; + m_query_interval = 0; + m_graph = graph; + m_path = 0; + dest_vertex_id = 0; +} + +TEMPLATE_SPECIALIZATION +IC _vertex_id_type CSelectorTemplate::get_selected_vertex_id() const +{ + VERIFY (m_graph->valid_vertex_id(m_selected_vertex_id)); + return (m_selected_vertex_id); +} + +TEMPLATE_SPECIALIZATION +IC void CSelectorTemplate::set_evaluator (_VertexEvaluator *evaluator) +{ + m_evaluator = evaluator; +} + +TEMPLATE_SPECIALIZATION +IC void CSelectorTemplate::set_query_interval(const u32 query_interval) +{ + m_query_interval = query_interval; +} + +TEMPLATE_SPECIALIZATION +IC bool CSelectorTemplate::actual(const _vertex_id_type start_vertex_id, bool path_completed) +{ + if (!used() || (((m_last_query_time + m_query_interval) > Device.dwTimeGlobal) && !path_completed)) + return (true); + + perform_search (start_vertex_id); + if (!failed() && dest_vertex_id) + *dest_vertex_id = m_selected_vertex_id; + + return (failed()); +} + +TEMPLATE_SPECIALIZATION +IC bool CSelectorTemplate::failed() const +{ + return (m_failed); +} + +TEMPLATE_SPECIALIZATION +IC bool CSelectorTemplate::used() const +{ + return (m_evaluator && m_graph); +} + +TEMPLATE_SPECIALIZATION +IC void CSelectorTemplate::select_location (const _vertex_id_type start_vertex_id, bool path_completed) +{ + if (used() && (((m_last_query_time + m_query_interval) <= Device.dwTimeGlobal) || path_completed)) { + perform_search (start_vertex_id); + if (!failed() && dest_vertex_id) + *dest_vertex_id = m_selected_vertex_id; + } + else m_failed = false; +} + +TEMPLATE_SPECIALIZATION +IC void CSelectorTemplate::perform_search (const _vertex_id_type vertex_id) +{ + START_PROFILE("Build Path/Selector Path"); + + VERIFY (m_evaluator && m_graph); + + _vertex_id_type start_vertex_id = vertex_id; + before_search (start_vertex_id); + + m_last_query_time = Device.dwTimeGlobal; + + m_evaluator->m_path = m_path; + ai().graph_engine().search (*m_graph,start_vertex_id,start_vertex_id,0,*m_evaluator); + m_failed = + !m_graph->valid_vertex_id(m_evaluator->selected_vertex_id()) || + (m_evaluator->selected_vertex_id() == m_selected_vertex_id); + + if (!failed()) + m_selected_vertex_id = m_evaluator->selected_vertex_id(); + + after_search (); + + STOP_PROFILE; +} + +TEMPLATE_SPECIALIZATION +IC void CSelectorTemplate::set_dest_path (xr_vector<_vertex_id_type> &path) +{ + m_path = &path; +} + +TEMPLATE_SPECIALIZATION +IC void CSelectorTemplate::set_dest_vertex (_vertex_id_type &vertex_id) +{ + dest_vertex_id = &vertex_id; +} + +TEMPLATE_SPECIALIZATION +IC void CSelectorTemplate::before_search (_vertex_id_type &vertex_id) +{ +} + +TEMPLATE_SPECIALIZATION +IC void CSelectorTemplate::after_search () +{ +} + + +#undef CSelectorTemplate +#undef TEMPLATE_SPECIALIZATION \ No newline at end of file diff --git a/src/xrGameLA/abstract_path_manager.h b/src/xrGameLA/abstract_path_manager.h new file mode 100644 index 000000000..a9a1e0046 --- /dev/null +++ b/src/xrGameLA/abstract_path_manager.h @@ -0,0 +1,87 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : abstract_path_manager.h +// Created : 02.10.2001 +// Modified : 19.11.2003 +// Author : Dmitriy Iassenev +// Description : Abstract path manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "restricted_object.h" + +template < + typename _Graph, + typename _VertexEvaluator, + typename _vertex_id_type, + typename _index_type +> +class CAbstractPathManager { +public: + typedef xr_vector<_vertex_id_type> PATH; + +private: + const _Graph *m_graph; + _VertexEvaluator *m_evaluator; + +protected: + _index_type m_current_index; + _index_type m_intermediate_index; + _vertex_id_type m_dest_vertex_id; + bool m_actuality; + bool m_failed; + PATH m_path; + CRestrictedObject *m_object; + +protected: + _vertex_id_type m_failed_start_vertex_id; + _vertex_id_type m_failed_dest_vertex_id; + +protected: + IC _vertex_id_type intermediate_vertex_id () const; + + IC void build_path (const _vertex_id_type start_vertex_id, const _vertex_id_type dest_vertex_id); + IC const _VertexEvaluator *evaluator () const; + IC void make_inactual (); + IC virtual void before_search (const _vertex_id_type start_vertex_id, const _vertex_id_type dest_vertex_id); + IC virtual void after_search (); + IC virtual bool check_vertex (const _vertex_id_type vertex_id) const; + +public: + IC CAbstractPathManager (CRestrictedObject *object); + IC virtual ~CAbstractPathManager (); + IC void reinit (const _Graph *graph = 0); + IC bool actual (const _vertex_id_type start_vertex_id, const _vertex_id_type dest_vertex_id) const; + IC void set_evaluator (_VertexEvaluator *evaluator); + IC void set_dest_vertex (const _vertex_id_type vertex_id); + IC _vertex_id_type dest_vertex_id () const; + IC virtual bool completed () const; + IC bool failed () const; + IC void reset (); + IC virtual void select_intermediate_vertex (); + IC CRestrictedObject &object () const; + +public: + IC const PATH &path () const; + IC u32 intermediate_index () const; + + friend class CMovementManager; +}; + +#include "abstract_path_manager_inline.h" + +template < + typename _Graph, + typename _VertexEvaluator, + typename _vertex_id_type, + typename _index_type +> +class CBasePathManager : + public CAbstractPathManager< + _Graph, + _VertexEvaluator, + _vertex_id_type, + _index_type + > +{ +}; \ No newline at end of file diff --git a/src/xrGameLA/abstract_path_manager_inline.h b/src/xrGameLA/abstract_path_manager_inline.h new file mode 100644 index 000000000..25e171aef --- /dev/null +++ b/src/xrGameLA/abstract_path_manager_inline.h @@ -0,0 +1,186 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : abstract_path_manager.h +// Created : 02.10.2001 +// Modified : 19.11.2003 +// Author : Dmitriy Iassenev +// Description : Abstract path manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "abstract_path_manager.h" +#include "ai_space.h" +#include "graph_engine.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Graph,\ + typename _VertexEvaluator,\ + typename _vertex_id_type,\ + typename _index_type\ +> + +#define CPathManagerTemplate CAbstractPathManager<_Graph,_VertexEvaluator,_vertex_id_type,_index_type> + +TEMPLATE_SPECIALIZATION +IC CPathManagerTemplate::CAbstractPathManager (CRestrictedObject *object) +{ + m_object = object; +} + +TEMPLATE_SPECIALIZATION +IC CPathManagerTemplate::~CAbstractPathManager () +{ +} + +TEMPLATE_SPECIALIZATION +IC void CPathManagerTemplate::reinit (const _Graph *graph) +{ + m_actuality = false; + m_failed = false; + m_evaluator = 0; + m_graph = graph; + m_current_index = _index_type(-1); + m_intermediate_index = _index_type(-1); + m_dest_vertex_id = _index_type(-1); + m_path.clear (); + m_failed_start_vertex_id= _vertex_id_type(-1); + m_failed_dest_vertex_id = _vertex_id_type(-1); +} + +TEMPLATE_SPECIALIZATION +IC void CPathManagerTemplate::build_path (const _vertex_id_type start_vertex_id, const _vertex_id_type dest_vertex_id) +{ + VERIFY (m_graph && m_evaluator && m_graph->valid_vertex_id(start_vertex_id) && m_graph->valid_vertex_id(dest_vertex_id)); + + if ((m_failed_start_vertex_id == start_vertex_id) && (m_failed_dest_vertex_id == dest_vertex_id)) { + before_search (start_vertex_id,dest_vertex_id); + m_failed = true; + after_search (); + m_current_index = _index_type(-1); + m_intermediate_index= _index_type(-1); + m_actuality = !failed(); + return; + } + + before_search (start_vertex_id,dest_vertex_id); + m_failed = !ai().graph_engine().search(*m_graph,start_vertex_id,dest_vertex_id,&m_path,*m_evaluator); + after_search (); + m_current_index = _index_type(-1); + m_intermediate_index = _index_type(-1); + m_actuality = !failed(); + + if (!m_failed) + return; + + m_failed_start_vertex_id= start_vertex_id; + m_failed_dest_vertex_id = dest_vertex_id; +} + +TEMPLATE_SPECIALIZATION +IC void CPathManagerTemplate::select_intermediate_vertex() +{ + VERIFY (!failed() && !m_path.empty()); + m_intermediate_index = m_path.size() - 1; +} + +TEMPLATE_SPECIALIZATION +IC _vertex_id_type CPathManagerTemplate::intermediate_vertex_id() const +{ + VERIFY (m_intermediate_index < m_path.size()); + return (m_path[intermediate_index()]); +} + +TEMPLATE_SPECIALIZATION +IC u32 CPathManagerTemplate::intermediate_index() const +{ + return (m_intermediate_index); +} + +TEMPLATE_SPECIALIZATION +IC bool CPathManagerTemplate::actual(const _vertex_id_type /*start_vertex_id*/, const _vertex_id_type /*dest_vertex_id*/) const +{ + return (m_actuality); +} + +TEMPLATE_SPECIALIZATION +IC bool CPathManagerTemplate::completed() const +{ + return (m_intermediate_index == m_path.size() - 1); +} + +TEMPLATE_SPECIALIZATION +IC bool CPathManagerTemplate::failed() const +{ + return (m_failed); +} + +TEMPLATE_SPECIALIZATION +IC void CPathManagerTemplate::set_evaluator(_VertexEvaluator *evaluator) +{ + if ((evaluator != m_evaluator) || !m_evaluator->actual()) + m_actuality = false; + m_evaluator = evaluator; +} + +TEMPLATE_SPECIALIZATION +IC const typename CPathManagerTemplate::PATH &CPathManagerTemplate::path () const +{ + return (m_path); +} + +TEMPLATE_SPECIALIZATION +IC _vertex_id_type CPathManagerTemplate::dest_vertex_id() const +{ + return (m_dest_vertex_id); +} + +TEMPLATE_SPECIALIZATION +IC void CPathManagerTemplate::set_dest_vertex(const _vertex_id_type vertex_id) +{ + VERIFY (check_vertex(vertex_id)); + m_actuality = m_actuality && (dest_vertex_id() == vertex_id); + m_dest_vertex_id = vertex_id; +} + +TEMPLATE_SPECIALIZATION +IC const _VertexEvaluator *CPathManagerTemplate::evaluator () const +{ + return (m_evaluator); +} + +TEMPLATE_SPECIALIZATION +IC void CPathManagerTemplate::make_inactual () +{ + m_actuality = false; +} + +TEMPLATE_SPECIALIZATION +IC void CPathManagerTemplate::before_search (const _vertex_id_type start_vertex_id, const _vertex_id_type dest_vertex_id) +{ +} + +TEMPLATE_SPECIALIZATION +IC void CPathManagerTemplate::after_search () +{ +} + +TEMPLATE_SPECIALIZATION +IC bool CPathManagerTemplate::check_vertex (const _vertex_id_type vertex_id) const +{ + return (m_graph->valid_vertex_id(vertex_id)); +} + +TEMPLATE_SPECIALIZATION +IC CRestrictedObject &CPathManagerTemplate::object () const +{ + VERIFY (m_object); + return (*m_object); +} +TEMPLATE_SPECIALIZATION +IC void CPathManagerTemplate::reset() +{ + m_failed = false; +} + +#undef CPathManagerTemplate +#undef TEMPLATE_SPECIALIZATION \ No newline at end of file diff --git a/src/xrGameLA/action_base.h b/src/xrGameLA/action_base.h new file mode 100644 index 000000000..f2557781e --- /dev/null +++ b/src/xrGameLA/action_base.h @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_base.h +// Created : 28.01.2004 +// Modified : 10.03.2004 +// Author : Dmitriy Iassenev +// Description : Base action +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "action_management_config.h" +#include "property_storage.h" +#include "script_export_space.h" +#include "operator_abstract.h" +#include "alife_space.h" + +class CScriptGameObject; + +template +class CActionBase : public GraphEngineSpace::CWorldOperator { +protected: + typedef GraphEngineSpace::CWorldOperator inherited; + typedef GraphEngineSpace::CWorldProperty COperatorCondition; + typedef GraphEngineSpace::_solver_condition_type _condition_type; + typedef GraphEngineSpace::_solver_value_type _value_type; + +protected: + enum EActionStates { + eActionStateConstructed = u32(0), + eActionStateSetup, + eActionStateInitialized, + eActionStateExecuted, + eActionStateFinalized, + eActionStateDummy = u32(-1), + }; + +public: + _object_type *m_object; + CPropertyStorage *m_storage; + +protected: + u32 m_start_level_time; + u32 m_start_game_time; + u32 m_inertia_time; + mutable _edge_value_type m_weight; + bool m_first_time; + +#ifdef LOG_PLANNER +public: + LPCSTR m_action_name; +#ifdef LOG_ACTION + bool m_use_log; + bool m_switched; + +public: + virtual void debug_log (const EActionStates state_state) const; + virtual void set_use_log (bool value); + virtual void show (LPCSTR offset = ""); +#endif + +#endif + +public: + IC CActionBase (const xr_vector &conditions, const xr_vector &effects, _object_type *object = 0, LPCSTR action_name = ""); + IC CActionBase (_object_type *object, LPCSTR action_name = ""); + virtual ~CActionBase (); + IC void init (_object_type *object, LPCSTR action_name); + virtual void setup (_object_type *object, CPropertyStorage *storage); + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual _edge_value_type weight (const CSConditionState &condition0, const CSConditionState &condition1) const; + IC void set_inertia_time (u32 inertia_time); + IC u32 start_level_time () const; + IC u32 inertia_time () const; + IC bool completed () const; + IC void set_property (const _condition_type &condition_id, const _value_type &value); + IC const _value_type &property (const _condition_type &condition_id) const; + IC void set_weight (const _edge_value_type &weight); + IC bool first_time () const; + + virtual void save (NET_Packet &packet) {} + virtual void load (IReader &packet) {} + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +typedef CActionBase CScriptActionBase; +add_to_type_list(CScriptActionBase) +#undef script_type_list +#define script_type_list save_type_list(CScriptActionBase) + +#include "action_base_inline.h" diff --git a/src/xrGameLA/action_base_inline.h b/src/xrGameLA/action_base_inline.h new file mode 100644 index 000000000..4b5dd3d0d --- /dev/null +++ b/src/xrGameLA/action_base_inline.h @@ -0,0 +1,206 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_base_inline.h +// Created : 28.01.2004 +// Modified : 10.03.2004 +// Author : Dmitriy Iassenev +// Description : Base action inline function +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "level.h" +#include "ai_debug.h" + +#define TEMPLATE_SPECIALIZATION template +#define CBaseAction CActionBase<_object_type> + +TEMPLATE_SPECIALIZATION +IC CBaseAction::CActionBase (const xr_vector &conditions, const xr_vector &effects, _object_type *object, LPCSTR action_name) : + inherited (conditions,effects) +{ + init (object,action_name); +} + +TEMPLATE_SPECIALIZATION +IC CBaseAction::CActionBase (_object_type *object, LPCSTR action_name) +{ + init (object,action_name); +} + +TEMPLATE_SPECIALIZATION +CBaseAction::~CActionBase () +{ +} + +TEMPLATE_SPECIALIZATION +void CBaseAction::init (_object_type *object, LPCSTR action_name) +{ + m_storage = 0; + m_object = object; + m_weight = _edge_value_type(1); + +#ifdef LOG_PLANNER + m_action_name = action_name; +#ifdef LOG_ACTION + m_use_log = false; + + m_switched = false; +// if (xr_strlen(m_action_name)) +// debug_log (eActionStateConstructed); +#endif +#endif +} + +TEMPLATE_SPECIALIZATION +void CBaseAction::setup (_object_type *object, CPropertyStorage *storage) +{ + VERIFY (object); + VERIFY (storage); + m_object = object; + m_storage = storage; + m_inertia_time = 0; +#ifdef LOG_ACTION + m_switched = false; + if (m_use_log && xr_strlen(m_action_name)) + debug_log (eActionStateSetup); +#endif +} + +TEMPLATE_SPECIALIZATION +void CBaseAction::initialize () +{ +#ifdef LOG_ACTION + VERIFY3 (!m_switched,m_action_name,"::initialize()"); + m_switched = true; + if (m_use_log && psAI_Flags.test(aiGOAP) && xr_strlen(m_action_name)) + debug_log (eActionStateInitialized); +#endif + m_start_level_time = Device.dwTimeGlobal; + m_first_time = true; +} + +TEMPLATE_SPECIALIZATION +void CBaseAction::execute () +{ + m_first_time = false; +#ifdef LOG_ACTION + if (m_use_log && psAI_Flags.test(aiGOAP) && xr_strlen(m_action_name) && m_switched) + debug_log (eActionStateExecuted); + m_switched = false; +#endif +} + +TEMPLATE_SPECIALIZATION +void CBaseAction::finalize () +{ +#ifdef LOG_ACTION + VERIFY3 (!m_switched,m_action_name,"::finalize()"); + if (m_use_log && psAI_Flags.test(aiGOAP) && xr_strlen(m_action_name)) + debug_log (eActionStateFinalized); +#endif +} + +TEMPLATE_SPECIALIZATION +bool CBaseAction::completed () const +{ + return (m_start_level_time + m_inertia_time <= Device.dwTimeGlobal); +} + +TEMPLATE_SPECIALIZATION +IC u32 CBaseAction::start_level_time () const +{ + return (m_start_level_time); +} + +TEMPLATE_SPECIALIZATION +IC u32 CBaseAction::inertia_time () const +{ + return (m_inertia_time); +} + +TEMPLATE_SPECIALIZATION +IC void CBaseAction::set_inertia_time (u32 inertia_time) +{ + m_inertia_time = inertia_time; +} + +#ifdef LOG_ACTION +TEMPLATE_SPECIALIZATION +IC void CBaseAction::debug_log (const EActionStates state_state) const +{ + switch (state_state) { + case eActionStateConstructed : { + Msg ("[%6d] action %s is constructed",Device.dwTimeGlobal,m_action_name); + break; + } + case eActionStateSetup : { + Msg ("[%6d] action %s is setup",Device.dwTimeGlobal,m_action_name); + break; + } + case eActionStateInitialized : { + Msg ("[%6d] action %s is initialized",Device.dwTimeGlobal,m_action_name); + break; + } + case eActionStateExecuted : { + Msg ("[%6d] action %s is executed",Device.dwTimeGlobal,m_action_name); + break; + } + case eActionStateFinalized : { + Msg ("[%6d] action %s is finalized",Device.dwTimeGlobal,m_action_name); + break; + } + default : NODEFAULT; + } +} + +TEMPLATE_SPECIALIZATION +IC void CBaseAction::set_use_log (bool value) +{ + m_use_log = value; +} +#endif + +TEMPLATE_SPECIALIZATION +IC void CBaseAction::set_property (const _condition_type &condition_id, const _value_type &value) +{ + VERIFY (m_storage); + m_storage->set_property (condition_id,value); +} + +TEMPLATE_SPECIALIZATION +IC const typename CBaseAction::_value_type &CBaseAction::property (const _condition_type &condition_id) const +{ + VERIFY (m_storage); + return (m_storage->property(condition_id)); +} + +TEMPLATE_SPECIALIZATION +IC void CBaseAction::set_weight (const _edge_value_type &weight) +{ + m_weight = _max(min_weight(),weight); +} + +TEMPLATE_SPECIALIZATION +typename CBaseAction::_edge_value_type CBaseAction::weight (const CSConditionState &condition0, const CSConditionState &condition1) const +{ + _edge_value_type _min_weight = min_weight(); + if (m_weight < _min_weight) + m_weight = _min_weight; + return (m_weight); +} + +#ifdef LOG_ACTION +TEMPLATE_SPECIALIZATION +IC void CBaseAction::show (LPCSTR offset) +{ +} +#endif + +TEMPLATE_SPECIALIZATION +IC bool CBaseAction::first_time () const +{ + return (m_first_time); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CBaseAction \ No newline at end of file diff --git a/src/xrGameLA/action_base_script.cpp b/src/xrGameLA/action_base_script.cpp new file mode 100644 index 000000000..48982f31f --- /dev/null +++ b/src/xrGameLA/action_base_script.cpp @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_base_script.cpp +// Created : 28.01.2004 +// Modified : 10.03.2004 +// Author : Dmitriy Iassenev +// Description : Base action script export +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "script_action_wrapper.h" +#include "script_game_object.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CActionBase::script_register(lua_State *L) +{ + module(L) + [ + class_("action_base") + .def_readonly("object", &CScriptActionBase::m_object) + .def_readonly("storage", &CScriptActionBase::m_storage) + .def( constructor<>()) + .def( constructor()) + .def( constructor()) + .def("add_precondition", (void (CScriptActionBase::*)(const CScriptActionBase::COperatorCondition &))(&CScriptActionBase::add_condition)) + .def("add_effect", (void (CScriptActionBase::*)(const CScriptActionBase::COperatorCondition &))(&CScriptActionBase::add_effect)) + .def("remove_precondition", (void (CScriptActionBase::*)(const CScriptActionBase::COperatorCondition::_condition_type &))(&CScriptActionBase::remove_condition)) + .def("remove_effect", (void (CScriptActionBase::*)(const CScriptActionBase::COperatorCondition::_condition_type &))(&CScriptActionBase::remove_effect)) + .def("setup", &CScriptActionBase::setup, &CScriptActionWrapper::setup_static) + .def("initialize", &CScriptActionBase::initialize, &CScriptActionWrapper::initialize_static) + .def("execute", &CScriptActionBase::execute, &CScriptActionWrapper::execute_static) + .def("finalize", &CScriptActionBase::finalize, &CScriptActionWrapper::finalize_static) + .def("weight", &CScriptActionBase::weight, &CScriptActionWrapper::weight_static) + .def("set_weight", &CScriptActionBase::set_weight) +#ifdef LOG_ACTION + .def("show", &CScriptActionBase::show) +#endif + ]; +} diff --git a/src/xrGameLA/action_management_config.h b/src/xrGameLA/action_management_config.h new file mode 100644 index 000000000..1d9504fa7 --- /dev/null +++ b/src/xrGameLA/action_management_config.h @@ -0,0 +1,14 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_management_config.h +// Created : 28.01.2004 +// Modified : 10.03.2004 +// Author : Dmitriy Iassenev +// Description : Action management configuration file +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifdef DEBUG +//# define LOG_ACTION +//# define LOG_PLANNER +#endif diff --git a/src/xrGameLA/action_planner.h b/src/xrGameLA/action_planner.h new file mode 100644 index 000000000..501251ee7 --- /dev/null +++ b/src/xrGameLA/action_planner.h @@ -0,0 +1,118 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_planner.h +// Created : 28.01.2004 +// Modified : 10.03.2004 +// Author : Dmitriy Iassenev +// Description : Action planner +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "problem_solver.h" +#include "action_base.h" +#include "property_evaluator.h" +#include "property_storage.h" +#include "script_export_space.h" +#include "ai_debug.h" + +class CScriptGameObject; + +template < + typename _object_type, + bool _reverse_search = false, + typename _world_operator = CActionBase<_object_type>, + typename _condition_evaluator = CPropertyEvaluator<_object_type>, + typename _world_operator_ptr = _world_operator*, + typename _condition_evaluator_ptr = _condition_evaluator* +> +class CActionPlanner : + public CProblemSolver< + GraphEngineSpace::CWorldProperty, + GraphEngineSpace::CWorldState, + _world_operator, + _condition_evaluator, + u32, + _reverse_search, + _world_operator_ptr, + _condition_evaluator_ptr + > +{ +public: + typedef CProblemSolver< + GraphEngineSpace::CWorldProperty, + GraphEngineSpace::CWorldState, + _world_operator, + _condition_evaluator, + u32, + _reverse_search, + _world_operator_ptr, + _condition_evaluator_ptr + > CProblemSolver; + typedef CProblemSolver inherited; + typedef typename inherited::_edge_type _action_id_type; + typedef GraphEngineSpace::CWorldProperty CWorldProperty; + typedef GraphEngineSpace::CWorldState CWorldState; + typedef _world_operator _world_operator; + +protected: + bool m_initialized; + _action_id_type m_current_action_id; + +#ifdef LOG_PLANNER +public: + string64 m_temp_string; + +#ifdef LOG_ACTION + bool m_use_log; + + public: + + virtual void set_use_log (bool value); +#endif + +#endif + +public: + _object_type *m_object; + CPropertyStorage m_storage; + bool m_loaded; + +#ifdef LOG_PLANNER +public: + virtual LPCSTR action2string (const _action_id_type &action_id); + virtual LPCSTR property2string (const _condition_type &action_id); + virtual LPCSTR object_name () const; +#ifdef LOG_ACTION + virtual void show (LPCSTR offset = ""); + IC void show_current_world_state(); + IC void show_target_world_state (); +#endif + +#endif + +public: + CActionPlanner (); + virtual ~CActionPlanner (); + virtual void setup (_object_type *object); + virtual void update (); + IC COperator &action (const _action_id_type &action_id); + IC CConditionEvaluator &evaluator (const _condition_type &evaluator_id); + IC _action_id_type current_action_id () const; + IC COperator ¤t_action (); + IC bool initialized () const; + IC void add_condition (_world_operator *action, _condition_type condition_id, _value_type condition_value); + IC void add_effect (_world_operator *action, _condition_type condition_id, _value_type condition_value); + IC virtual void add_operator (const _edge_type &operator_id, _operator_ptr _operator); + IC virtual void add_evaluator (const _condition_type &condition_id, _condition_evaluator_ptr evaluator); + IC _object_type &object () const; + virtual void save (NET_Packet &packet); + virtual void load (IReader &packet); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +typedef CActionPlanner CScriptActionPlanner; +add_to_type_list(CScriptActionPlanner) +#undef script_type_list +#define script_type_list save_type_list(CScriptActionPlanner) + +#include "action_planner_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/action_planner_action.h b/src/xrGameLA/action_planner_action.h new file mode 100644 index 000000000..d91da8d7c --- /dev/null +++ b/src/xrGameLA/action_planner_action.h @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_planner_action.h +// Created : 28.01.2004 +// Modified : 10.03.2004 +// Author : Dmitriy Iassenev +// Description : Action planner action +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "action_base.h" +#include "action_planner.h" +#include "script_export_space.h" + +class CScriptGameObject; + +template +class CActionPlannerAction : + public CActionPlanner<_object_type>, + public CActionBase<_object_type> +{ +protected: + typedef CActionPlanner<_object_type> inherited_planner; + typedef CActionBase<_object_type> inherited_action; + typedef typename inherited_action::_edge_value_type _edge_value_type; + typedef typename inherited_action::_condition_type _condition_type; + typedef typename inherited_action::_value_type _value_type; + +public: + typedef typename inherited_action::COperatorCondition COperatorCondition; + +#ifdef LOG_ACTION +public: + virtual void set_use_log (bool value); + virtual void show (LPCSTR offset = ""); +#endif + +public: + IC CActionPlannerAction (_object_type *object = 0, LPCSTR action_name = ""); + virtual ~CActionPlannerAction (); + virtual void setup (_object_type *object, CPropertyStorage *storage); + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual bool completed () const; + IC void add_condition (_world_operator *action, _condition_type condition_id, _value_type condition_value); + IC void add_effect (_world_operator *action, _condition_type condition_id, _value_type condition_value); + + virtual void save (NET_Packet &packet) {inherited_planner::save(packet); inherited_action::save(packet);} + virtual void load (IReader &packet) {inherited_planner::load(packet); inherited_action::load(packet);} + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +typedef CActionPlannerAction CScriptActionPlannerAction; +add_to_type_list(CScriptActionPlannerAction) +#undef script_type_list +#define script_type_list save_type_list(CScriptActionPlannerAction) + +#include "action_planner_action_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/action_planner_action_inline.h b/src/xrGameLA/action_planner_action_inline.h new file mode 100644 index 000000000..8af9210b3 --- /dev/null +++ b/src/xrGameLA/action_planner_action_inline.h @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_planner_action_inline.h +// Created : 28.01.2004 +// Modified : 10.03.2004 +// Author : Dmitriy Iassenev +// Description : Action planner action inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#define TEMPLATE_SPECIALIZATION template +#define CPlanner CActionPlannerAction<_object_type> + +TEMPLATE_SPECIALIZATION +IC CPlanner::CActionPlannerAction (_object_type *object, LPCSTR action_name) : + inherited_action (object,action_name) +{ +} + +TEMPLATE_SPECIALIZATION +CPlanner::~CActionPlannerAction () +{ +} + +TEMPLATE_SPECIALIZATION +void CPlanner::setup (_object_type *object, CPropertyStorage *storage) +{ + inherited_planner::setup (object); + inherited_action::setup (object,storage); + set_target_state (effects()); +} + +TEMPLATE_SPECIALIZATION +void CPlanner::initialize () +{ + inherited_action::initialize (); +} + +TEMPLATE_SPECIALIZATION +void CPlanner::finalize () +{ + current_action().finalize (); + inherited_action::finalize (); + m_initialized = false; +} + +TEMPLATE_SPECIALIZATION +bool CPlanner::completed () const +{ + return (inherited_action::completed()); +} + +#ifdef LOG_ACTION +TEMPLATE_SPECIALIZATION +IC void CPlanner::set_use_log (bool value) +{ + inherited_action::set_use_log (value); + inherited_planner::set_use_log (value); +} +#endif + +TEMPLATE_SPECIALIZATION +void CPlanner::execute () +{ + inherited_action::execute (); + update (); +} + +TEMPLATE_SPECIALIZATION +IC void CPlanner::add_condition (_world_operator *action, _condition_type condition_id, _value_type condition_value) +{ + inherited_planner::add_condition (action,condition_id,condition_value); +} + +TEMPLATE_SPECIALIZATION +IC void CPlanner::add_effect (_world_operator *action, _condition_type condition_id, _value_type condition_value) +{ + inherited_planner::add_effect (action,condition_id,condition_value); +} + +#ifdef LOG_ACTION +TEMPLATE_SPECIALIZATION +IC void CPlanner::show (LPCSTR offset) +{ + inherited_action::show (offset); + inherited_planner::show (offset); +} +#endif + +#undef TEMPLATE_SPECIALIZATION +#undef CPlanner \ No newline at end of file diff --git a/src/xrGameLA/action_planner_action_script.cpp b/src/xrGameLA/action_planner_action_script.cpp new file mode 100644 index 000000000..89449220b --- /dev/null +++ b/src/xrGameLA/action_planner_action_script.cpp @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_planner_action_script.cpp +// Created : 28.01.2004 +// Modified : 10.03.2004 +// Author : Dmitriy Iassenev +// Description : Action planner action script export +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "script_action_planner_action_wrapper.h" +#include "script_game_object.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CActionPlannerAction::script_register(lua_State *L) +{ + module(L) + [ + class_ >("planner_action") + .def( constructor<>()) + .def( constructor()) + .def( constructor()) + .def("setup", &CScriptActionPlannerAction::setup, &CScriptActionPlannerActionWrapper::setup_static) + .def("initialize", &CScriptActionPlannerAction::initialize, &CScriptActionPlannerActionWrapper::initialize_static) + .def("execute", &CScriptActionPlannerAction::execute, &CScriptActionPlannerActionWrapper::execute_static) + .def("finalize", &CScriptActionPlannerAction::finalize, &CScriptActionPlannerActionWrapper::finalize_static) +#ifdef LOG_ACTION + .def("show", &CScriptActionPlannerAction::show) +#endif + .def("weight", &CScriptActionPlannerAction::weight, &CScriptActionPlannerActionWrapper::weight_static) + ]; +} diff --git a/src/xrGameLA/action_planner_action_script.h b/src/xrGameLA/action_planner_action_script.h new file mode 100644 index 000000000..f05657eae --- /dev/null +++ b/src/xrGameLA/action_planner_action_script.h @@ -0,0 +1,32 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_planner_action_script.h +// Created : 07.07.2004 +// Modified : 07.07.2004 +// Author : Dmitriy Iassenev +// Description : Action planner action with script support +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "action_planner_action.h" + +class CScriptGameObject; + +template +class CActionPlannerActionScript : public CScriptActionPlannerAction { +protected: + typedef CScriptActionPlannerAction inherited; + +public: + _object_type *m_object; + +public: + IC CActionPlannerActionScript (const xr_vector &conditions, const xr_vector &effects, _object_type *object = 0, LPCSTR action_name = ""); + IC CActionPlannerActionScript (_object_type *object = 0, LPCSTR action_name = ""); + virtual ~CActionPlannerActionScript (); + virtual void setup (_object_type *object, CPropertyStorage *storage); + virtual void setup (CScriptGameObject *object, CPropertyStorage *storage); + IC _object_type &object () const; +}; + +#include "action_planner_action_script_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/action_planner_action_script_inline.h b/src/xrGameLA/action_planner_action_script_inline.h new file mode 100644 index 000000000..b35e07d95 --- /dev/null +++ b/src/xrGameLA/action_planner_action_script_inline.h @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_planner_action_script_inline.h +// Created : 07.07.2004 +// Modified : 07.07.2004 +// Author : Dmitriy Iassenev +// Description : Action planner action with script support inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#define TEMPLATE_SPECIALIZATION template +#define CSActionPlannerActionScript CActionPlannerActionScript<_object_type> + +TEMPLATE_SPECIALIZATION +IC CSActionPlannerActionScript::CActionPlannerActionScript (const xr_vector &conditions, const xr_vector &effects, _object_type *object, LPCSTR action_name) : + inherited (conditions,effects,object ? object->lua_game_object() : 0,action_name) +{ + m_object = object; +} + +TEMPLATE_SPECIALIZATION +IC CSActionPlannerActionScript::CActionPlannerActionScript (_object_type *object, LPCSTR action_name) : + inherited (object ? object->lua_game_object() : 0,action_name) +{ + m_object = object; +} + +TEMPLATE_SPECIALIZATION +CSActionPlannerActionScript::~CActionPlannerActionScript () +{ +} + +TEMPLATE_SPECIALIZATION +void CSActionPlannerActionScript::setup (_object_type *object, CPropertyStorage *storage) +{ + VERIFY (object); + VERIFY (m_object); +} + +TEMPLATE_SPECIALIZATION +void CSActionPlannerActionScript::setup (CScriptGameObject *object, CPropertyStorage *storage) +{ + VERIFY (object); + inherited::setup (object,storage); + m_object = smart_cast<_object_type*>(&object->object()); + VERIFY (m_object); + setup (m_object,storage); +} + +TEMPLATE_SPECIALIZATION +IC _object_type &CSActionPlannerActionScript::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CSActionPlannerActionScript \ No newline at end of file diff --git a/src/xrGameLA/action_planner_inline.h b/src/xrGameLA/action_planner_inline.h new file mode 100644 index 000000000..ee6988da0 --- /dev/null +++ b/src/xrGameLA/action_planner_inline.h @@ -0,0 +1,341 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_planner_inline.h +// Created : 28.01.2004 +// Modified : 10.03.2004 +// Author : Dmitriy Iassenev +// Description : Action planner inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifdef LOG_ACTION + #include "ai_debug.h" +#endif + +#define TEMPLATE_SPECIALIZATION \ + template <\ + typename _object_type,\ + bool _reverse_search,\ + typename _world_operator,\ + typename _condition_evaluator,\ + typename _world_operator_ptr,\ + typename _condition_evaluator_ptr\ + > + +#define CPlanner \ + CActionPlanner <\ + _object_type,\ + _reverse_search,\ + _world_operator,\ + _condition_evaluator,\ + _world_operator_ptr,\ + _condition_evaluator_ptr\ + > + +TEMPLATE_SPECIALIZATION +IC CPlanner::CActionPlanner () +{ + m_initialized = false; +#ifdef LOG_ACTION + m_use_log = false; +#endif +} + +TEMPLATE_SPECIALIZATION +IC CPlanner::~CActionPlanner () +{ + m_object = 0; +} + +TEMPLATE_SPECIALIZATION +void CPlanner::setup (_object_type *object) +{ + inherited::setup (); + m_object = object; + m_current_action_id = _action_id_type(-1); + m_storage.clear (); + m_initialized = false; + m_loaded = false; +} + +TEMPLATE_SPECIALIZATION +IC _object_type &CPlanner::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +TEMPLATE_SPECIALIZATION +void CPlanner::update () +{ + solve (); + +#ifdef LOG_ACTION + // printing solution + if (m_use_log && psAI_Flags.test(aiGOAP)) { + if (m_solution_changed) { + show_current_world_state(); + show_target_world_state (); + Msg ("%6d : Solution for object %s [%d vertices searched]",Device.dwTimeGlobal,object_name(),ai().graph_engine().solver_algorithm().data_storage().get_visited_node_count()); + for (int i=0; i<(int)solution().size(); ++i) + Msg ("%s",action2string(solution()[i])); + } + } +#endif + +#ifdef LOG_ACTION + if (m_failed) { + // printing current world state + show (); + + Msg ("! ERROR : there is no action sequence, which can transfer current world state to the target one"); + Msg ("Time : %6d",Device.dwTimeGlobal); + Msg ("Object : %s",object_name()); + + show_current_world_state (); + show_target_world_state (); +// VERIFY2 (!m_failed,"Problem solver couldn't build a valid path - verify your conditions, effects and goals!"); + } +#endif + + THROW (!solution().empty()); + + if (initialized()) { + if (current_action_id() != solution().front()) { + current_action().finalize (); + m_current_action_id = solution().front(); + current_action().initialize (); + } + } + else { + m_initialized = true; + m_current_action_id = solution().front(); + current_action().initialize (); + } + + current_action().execute (); +} + +TEMPLATE_SPECIALIZATION +IC typename CPlanner::COperator &CPlanner::action (const _action_id_type &action_id) +{ + return (*get_operator(action_id)); +} + +TEMPLATE_SPECIALIZATION +IC typename CPlanner::CConditionEvaluator &CPlanner::evaluator (const _condition_type &evaluator_id) +{ + return (*inherited::evaluator(evaluator_id)); +} + +TEMPLATE_SPECIALIZATION +IC typename CPlanner::_action_id_type CPlanner::current_action_id () const +{ + VERIFY (initialized()); + return (m_current_action_id); +} + +TEMPLATE_SPECIALIZATION +IC typename CPlanner::COperator &CPlanner::current_action () +{ + return (action(current_action_id())); +} + +TEMPLATE_SPECIALIZATION +IC bool CPlanner::initialized () const +{ + return (m_initialized); +} + +TEMPLATE_SPECIALIZATION +IC void CPlanner::add_condition (_world_operator *action, _condition_type condition_id, _value_type condition_value) +{ + action->add_condition (CWorldProperty(condition_id,condition_value)); +} + +TEMPLATE_SPECIALIZATION +IC void CPlanner::add_effect (_world_operator *action, _condition_type condition_id, _value_type condition_value) +{ + action->add_effect (CWorldProperty(condition_id,condition_value)); +} + +#ifdef LOG_PLANNER +TEMPLATE_SPECIALIZATION +LPCSTR CPlanner::action2string (const _action_id_type &action_id) +{ + return (action(action_id).m_action_name); +} + +TEMPLATE_SPECIALIZATION +LPCSTR CPlanner::property2string (const _condition_type &property_id) +{ + return (evaluator(property_id).m_evaluator_name);//itoa(property_id,m_temp_string,10)); +} + +TEMPLATE_SPECIALIZATION +LPCSTR CPlanner::object_name () const +{ + return (*m_object->cName()); +} +#endif + +TEMPLATE_SPECIALIZATION +IC void CPlanner::add_operator (const _edge_type &operator_id, _operator_ptr _operator) +{ + inherited::add_operator (operator_id,_operator); + _operator->setup (m_object,&m_storage); +#ifdef LOG_ACTION + _operator->set_use_log (m_use_log); +#endif +} + +TEMPLATE_SPECIALIZATION +IC void CPlanner::add_evaluator (const _condition_type &condition_id, _condition_evaluator_ptr evaluator) +{ + inherited::add_evaluator(condition_id,evaluator); + evaluator->setup (m_object,&m_storage); +} + +#ifdef LOG_ACTION +TEMPLATE_SPECIALIZATION +IC void CPlanner::set_use_log (bool value) +{ + m_use_log = value; + OPERATOR_VECTOR::iterator I = m_operators.begin(); + OPERATOR_VECTOR::iterator E = m_operators.end(); + for ( ; I != E; ++I) + (*I).get_operator()->set_use_log(m_use_log); +} + +TEMPLATE_SPECIALIZATION +IC void CPlanner::show_current_world_state () +{ + Msg ("Current world state :"); + EVALUATORS::const_iterator I = evaluators().begin(); + EVALUATORS::const_iterator E = evaluators().end(); + for ( ; I != E; ++I) { + xr_vector::const_iterator J = std::lower_bound(current_state().conditions().begin(),current_state().conditions().end(),CWorldProperty((*I).first,false)); + char temp = '?'; + if ((J != current_state().conditions().end()) && ((*J).condition() == (*I).first)) { + temp = (*J).value() ? '+' : '-'; + Msg ("%5c : [%d][%s]",temp,(*I).first,property2string((*I).first)); + } + } +} + +TEMPLATE_SPECIALIZATION +IC void CPlanner::show_target_world_state () +{ + Msg ("Target world state :"); + EVALUATORS::const_iterator I = evaluators().begin(); + EVALUATORS::const_iterator E = evaluators().end(); + for ( ; I != E; ++I) { + xr_vector::const_iterator J = std::lower_bound(target_state().conditions().begin(),target_state().conditions().end(),CWorldProperty((*I).first,false)); + char temp = '?'; + if ((J != target_state().conditions().end()) && ((*J).condition() == (*I).first)) { + temp = (*J).value() ? '+' : '-'; + Msg ("%5c : [%d][%s]",temp,(*I).first,property2string((*I).first)); + } + } +} + +TEMPLATE_SPECIALIZATION +IC void CPlanner::show (LPCSTR offset) +{ + string256 temp; + strconcat (sizeof(temp),temp,offset," "); + { + Msg ("\n%sEVALUATORS : %d\n",offset,evaluators().size()); + EVALUATORS::const_iterator I = evaluators().begin(); + EVALUATORS::const_iterator E = evaluators().end(); + for ( ; I != E; ++I) + Msg ("%sevaluator [%d][%s]",offset,(*I).first,property2string((*I).first)); + } + { + Msg ("\n%sOPERATORS : %d\n",offset,operators().size()); + OPERATOR_VECTOR::const_iterator I = operators().begin(); + OPERATOR_VECTOR::const_iterator E = operators().end(); + for ( ; I != E; ++I) { + Msg ("%soperator [%d][%s]",offset,(*I).m_operator_id,(*I).m_operator->m_action_name); + + { + xr_vector::const_iterator i = (*I).m_operator->conditions().conditions().begin(); + xr_vector::const_iterator e = (*I).m_operator->conditions().conditions().end(); + for ( ; i != e; ++i) + Msg ("%s condition [%d][%s] = %s",offset,(*i).condition(),property2string((*i).condition()),(*i).value() ? "TRUE" : "FALSE"); + } + { + xr_vector::const_iterator i = (*I).m_operator->effects().conditions().begin(); + xr_vector::const_iterator e = (*I).m_operator->effects().conditions().end(); + for ( ; i != e; ++i) + Msg ("%s effect [%d][%s] = %s",offset,(*i).condition(),property2string((*i).condition()),(*i).value() ? "TRUE" : "FALSE"); + } + + (*I).m_operator->show(temp); + Msg (" "); + } + } +} +#endif + +TEMPLATE_SPECIALIZATION +IC void CPlanner::save (NET_Packet &packet) +{ + { + EVALUATORS::iterator I = m_evaluators.begin(); + EVALUATORS::iterator E = m_evaluators.end(); + for ( ; I != E; ++I) + (*I).second->save (packet); + } + + { + OPERATOR_VECTOR::iterator I = m_operators.begin(); + OPERATOR_VECTOR::iterator E = m_operators.end(); + for ( ; I != E; ++I) + (*I).m_operator->save (packet); + } + + { + packet.w_u32 (m_storage.m_storage.size()); + typedef CPropertyStorage::CConditionStorage CConditionStorage; + CConditionStorage::const_iterator I = m_storage.m_storage.begin(); + CConditionStorage::const_iterator E = m_storage.m_storage.end(); + for ( ; I != E; ++I) { + packet.w (&(*I).m_condition,sizeof((*I).m_condition)); + packet.w (&(*I).m_value,sizeof((*I).m_value)); + } + } +} + +TEMPLATE_SPECIALIZATION +IC void CPlanner::load (IReader &packet) +{ + { + EVALUATORS::iterator I = m_evaluators.begin(); + EVALUATORS::iterator E = m_evaluators.end(); + for ( ; I != E; ++I) + (*I).second->load (packet); + } + + { + OPERATOR_VECTOR::iterator I = m_operators.begin(); + OPERATOR_VECTOR::iterator E = m_operators.end(); + for ( ; I != E; ++I) + (*I).m_operator->load (packet); + } + + { + u32 count = packet.r_u32(); + GraphEngineSpace::_solver_condition_type condition; + GraphEngineSpace::_solver_value_type value; + for (u32 i=0; iset_target_state (*world_state); +} + +bool get_actual(const CScriptActionPlanner *action_planner) +{ + return (action_planner->actual()); +} + +CScriptActionPlanner *cast_planner(CScriptActionBase *action) +{ + return (smart_cast(action)); +} + +#pragma optimize("s",on) +void CActionPlanner::script_register(lua_State *L) +{ + module(L) + [ + class_("action_planner") + .def_readonly("object", &CScriptActionPlanner::m_object) + .def_readonly("storage", &CScriptActionPlanner::m_storage) + .def( constructor<>()) + .def("actual", &get_actual) + .def("setup", &CScriptActionPlanner::setup, &CScriptActionPlannerWrapper::setup_static) + .def("update", &CScriptActionPlanner::update, &CScriptActionPlannerWrapper::update_static) + .def("add_action", &CScriptActionPlanner::add_operator,adopt(_3)) + .def("remove_action", (void (CScriptActionPlanner::*)(const CScriptActionPlanner::_edge_type &))(&CScriptActionPlanner::remove_operator)) + .def("action", &CScriptActionPlanner::action) + .def("add_evaluator", &CScriptActionPlanner::add_evaluator,adopt(_3)) + .def("remove_evaluator", (void (CScriptActionPlanner::*)(const CScriptActionPlanner::_condition_type &))(&CScriptActionPlanner::remove_evaluator)) + .def("evaluator", &CScriptActionPlanner::evaluator) + .def("current_action_id", &CScriptActionPlanner::current_action_id) + .def("current_action", &CScriptActionPlanner::current_action) + .def("initialized", &CScriptActionPlanner::initialized) + .def("set_goal_world_state", &set_goal_world_state) + .def("clear", &CScriptActionPlanner::clear) +#ifdef LOG_ACTION + .def("show", &CScriptActionPlanner::show) +#endif + ,def("cast_planner", &cast_planner) + ]; +} \ No newline at end of file diff --git a/src/xrGameLA/action_planner_script.h b/src/xrGameLA/action_planner_script.h new file mode 100644 index 000000000..8944f69de --- /dev/null +++ b/src/xrGameLA/action_planner_script.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_planner_script.h +// Created : 28.03.2004 +// Modified : 28.03.2004 +// Author : Dmitriy Iassenev +// Description : Action planner with script support +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "action_planner.h" + +class CScriptGameObject; + +template +class CActionPlannerScript : public CScriptActionPlanner { +protected: + typedef CScriptActionPlanner inherited; + +public: + _object_type *m_object; + +public: + IC CActionPlannerScript (); + virtual void setup (_object_type *object); +}; + +#include "action_planner_script_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/action_planner_script_inline.h b/src/xrGameLA/action_planner_script_inline.h new file mode 100644 index 000000000..3b347f5c5 --- /dev/null +++ b/src/xrGameLA/action_planner_script_inline.h @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_planner_script_inline.h +// Created : 28.03.2004 +// Modified : 28.03.2004 +// Author : Dmitriy Iassenev +// Description : Action planner with script support inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#define TEMPLATE_SPECIALIZATION template +#define _CActionPlannerScript CActionPlannerScript<_object_type> + +TEMPLATE_SPECIALIZATION +IC _CActionPlannerScript::CActionPlannerScript () +{ + m_object = 0; +} + +TEMPLATE_SPECIALIZATION +void _CActionPlannerScript::setup (_object_type *object) +{ + VERIFY (object); + inherited::setup (object->lua_game_object()); + m_object = object; +} + +#undef TEMPLATE_SPECIALIZATION +#undef _CActionPlannerScript \ No newline at end of file diff --git a/src/xrGameLA/action_script_base.h b/src/xrGameLA/action_script_base.h new file mode 100644 index 000000000..f7f092f82 --- /dev/null +++ b/src/xrGameLA/action_script_base.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_script_base.h +// Created : 28.03.2004 +// Modified : 28.03.2004 +// Author : Dmitriy Iassenev +// Description : Base action with script support +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "action_base.h" + +class CScriptGameObject; + +template +class CActionScriptBase : public CScriptActionBase { +protected: + typedef CScriptActionBase inherited; + +public: + _object_type *m_object; + +public: + IC CActionScriptBase (const xr_vector &conditions, const xr_vector &effects, _object_type *object = 0, LPCSTR action_name = ""); + IC CActionScriptBase (_object_type *object = 0, LPCSTR action_name = ""); + virtual ~CActionScriptBase (); + virtual void setup (_object_type *object, CPropertyStorage *storage); + virtual void setup (CScriptGameObject *object, CPropertyStorage *storage); +}; + +#include "action_script_base_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/action_script_base_inline.h b/src/xrGameLA/action_script_base_inline.h new file mode 100644 index 000000000..08b736e76 --- /dev/null +++ b/src/xrGameLA/action_script_base_inline.h @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : action_script_base_inline.h +// Created : 28.03.2004 +// Modified : 28.03.2004 +// Author : Dmitriy Iassenev +// Description : Base action with script support (inline functions) +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#define TEMPLATE_SPECIALIZATION template +#define CScriptBaseAction CActionScriptBase<_object_type> + +TEMPLATE_SPECIALIZATION +IC CScriptBaseAction::CActionScriptBase (const xr_vector &conditions, const xr_vector &effects, _object_type *object, LPCSTR action_name) : + inherited (conditions,effects,object ? object->lua_game_object() : 0,action_name) +{ + m_object = object; +} + +TEMPLATE_SPECIALIZATION +IC CScriptBaseAction::CActionScriptBase (_object_type *object, LPCSTR action_name) : + inherited (object ? object->lua_game_object() : 0,action_name) +{ + m_object = object; +} + +TEMPLATE_SPECIALIZATION +CScriptBaseAction::~CActionScriptBase () +{ +} + +TEMPLATE_SPECIALIZATION +void CScriptBaseAction::setup (_object_type *object, CPropertyStorage *storage) +{ + VERIFY (object); + m_object = object; +} + +TEMPLATE_SPECIALIZATION +void CScriptBaseAction::setup (CScriptGameObject *object, CPropertyStorage *storage) +{ + VERIFY (object); + inherited::setup (object,storage); + setup (smart_cast<_object_type*>(&object->object()),storage); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CScriptBaseAction \ No newline at end of file diff --git a/src/xrGameLA/actor_anim_defs.h b/src/xrGameLA/actor_anim_defs.h new file mode 100644 index 000000000..befa93a72 --- /dev/null +++ b/src/xrGameLA/actor_anim_defs.h @@ -0,0 +1,97 @@ +#pragma once + +#include "../Include/xrRender/KinematicsAnimated.h" + +struct SAnimState +{ + MotionID legs_fwd; + MotionID legs_back; + MotionID legs_ls; + MotionID legs_rs; + void Create (IKinematicsAnimated* K, LPCSTR base0, LPCSTR base1); +}; + +struct STorsoWpn{ + enum eMovingState{eIdle, eWalk, eRun, eSprint, eTotal}; + MotionID moving[eTotal]; + + MotionID zoom; + MotionID holster; + MotionID draw; + MotionID drop; + MotionID reload; + MotionID reload_1; + MotionID reload_2; + MotionID attack; + MotionID attack_zoom; + MotionID fire_idle; + MotionID fire_end; + + //анимации для атаки для всего тела (когда мы стоим на месте) + MotionID all_attack_0; + MotionID all_attack_1; + MotionID all_attack_2; + void Create (IKinematicsAnimated* K, LPCSTR base0, LPCSTR base1); +}; + +#define _total_anim_slots_ 14 + +struct SActorState +{ + + MotionID legs_idle; + MotionID jump_begin; + MotionID jump_idle; + MotionID landing[2]; + MotionID legs_turn; + MotionID death; + SAnimState m_walk; + SAnimState m_run; + STorsoWpn m_torso[_total_anim_slots_]; + MotionID m_torso_idle; + MotionID m_head_idle; + + MotionID m_damage[DAMAGE_FX_COUNT]; + void Create (IKinematicsAnimated* K, LPCSTR base); + void CreateClimb (IKinematicsAnimated* K); +}; + +struct SActorSprintState +{ + //leg anims + MotionID legs_fwd; + MotionID legs_ls; + MotionID legs_rs; + void Create (IKinematicsAnimated* K); +}; + +struct SActorMotions +{ + MotionID m_dead_stop; + SActorState m_normal; + SActorState m_crouch; + SActorState m_climb; + SActorSprintState m_sprint; + void Create(IKinematicsAnimated* K); +}; + +//vehicle anims +struct SVehicleAnimCollection +{ + static const u16 MAX_IDLES = 3; + u16 idles_num; + MotionID idles[MAX_IDLES]; + MotionID steer_left; + MotionID steer_right; + SVehicleAnimCollection (); + void Create (IKinematicsAnimated* K,u16 num); +}; +struct SActorVehicleAnims +{ + static const int TYPES_NUMBER=10; + SVehicleAnimCollection m_vehicles_type_collections [TYPES_NUMBER]; + SActorVehicleAnims (); + void Create (IKinematicsAnimated* K); +}; + + diff --git a/src/xrGameLA/actor_communication.cpp b/src/xrGameLA/actor_communication.cpp new file mode 100644 index 000000000..aadeba129 --- /dev/null +++ b/src/xrGameLA/actor_communication.cpp @@ -0,0 +1,344 @@ +#include "pch_script.h" +#include "actor.h" +#include "UIGameSP.h" +#include "PDA.h" +#include "HUDManager.h" +#include "level.h" +#include "string_table.h" +#include "PhraseDialog.h" +#include "character_info.h" +#include "relation_registry.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_registry_container.h" +#include "script_game_object.h" +#include "game_cl_base.h" +#include "xrServer.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "alife_registry_wrappers.h" +#include "map_manager.h" +#include "ui/UIMainIngameWnd.h" +#include "ui/UIPdaWnd.h" +#include "ui/UIDiaryWnd.h" +#include "ui/UITalkWnd.h" +#include "game_object_space.h" +#include "script_callback_ex.h" +#include "encyclopedia_article.h" +#include "GameTaskManager.h" +#include "GameTaskdefs.h" +#include "infoportion.h" +#include "ai/monsters/basemonster/base_monster.h" +#include "ai/trader/ai_trader.h" +#include "ai/stalker/ai_stalker.h" +#include "map_location.h" + +void CActor::AddEncyclopediaArticle (const CInfoPortion* info_portion) const +{ + VERIFY(info_portion); + ARTICLE_VECTOR& article_vector = encyclopedia_registry->registry().objects(); + + ARTICLE_VECTOR::iterator last_end = article_vector.end(); + ARTICLE_VECTOR::iterator B = article_vector.begin(); + ARTICLE_VECTOR::iterator E = last_end; + + for(ARTICLE_ID_VECTOR::const_iterator it = info_portion->ArticlesDisable().begin(); + it != info_portion->ArticlesDisable().end(); it++) + { + FindArticleByIDPred pred(*it); + last_end = std::remove_if(B, last_end, pred); + } + article_vector.erase(last_end, E); + + + for(ARTICLE_ID_VECTOR::const_iterator it = info_portion->Articles().begin(); + it != info_portion->Articles().end(); it++) + { + FindArticleByIDPred pred(*it); + if( std::find_if(article_vector.begin(), article_vector.end(), pred) != article_vector.end() ) continue; + + CEncyclopediaArticle article; + + article.Load(*it); + + article_vector.push_back(ARTICLE_DATA(*it, Level().GetGameTime(), article.data()->articleType)); + LPCSTR g,n; + int _atype = article.data()->articleType; + g = *(article.data()->group); + n = *(article.data()->name); + callback(GameObject::eArticleInfo)(lua_game_object(), g, n, _atype); + + if(CurrentGameUI()){ + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + pda_section::part p = pda_section::encyclopedia; + switch (article.data()->articleType){ + case ARTICLE_DATA::eEncyclopediaArticle: p = pda_section::encyclopedia; break; + case ARTICLE_DATA::eJournalArticle: p = pda_section::journal; break; + case ARTICLE_DATA::eInfoArticle: p = pda_section::info; break; + case ARTICLE_DATA::eTaskArticle: p = pda_section::quests; break; + default: NODEFAULT; + }; + pGameSP->m_PdaMenu->PdaContentsChanged (p); + } + + } + +} + + +void CActor::AddGameTask (const CInfoPortion* info_portion) const +{ + VERIFY(info_portion); + + if(info_portion->GameTasks().empty()) return; + for(TASK_ID_VECTOR::const_iterator it = info_portion->GameTasks().begin(); + it != info_portion->GameTasks().end(); it++) + { + GameTaskManager().GiveGameTaskToActor(*it, 0); + } +} + + +void CActor::AddGameNews (GAME_NEWS_DATA& news_data) +{ + + GAME_NEWS_VECTOR& news_vector = game_news_registry->registry().objects(); + news_data.receive_time = Level().GetGameTime(); + news_vector.push_back (news_data); + + if(CurrentGameUI()){ + CurrentGameUI()->UIMainIngameWnd->ReceiveNews(&news_data); + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if(pGameSP) + pGameSP->m_PdaMenu->PdaContentsChanged (pda_section::news); + } +} + + +bool CActor::OnReceiveInfo(shared_str info_id) const +{ + if(!CInventoryOwner::OnReceiveInfo(info_id)) + return false; + + if (CInfoPortion::ValidInfoPortion(*info_id)) + { + CInfoPortion info_portion; + info_portion.Load(info_id); + AddEncyclopediaArticle (&info_portion); + AddGameTask (&info_portion); + } + + callback(GameObject::eInventoryInfo)(lua_game_object(), *info_id); + + if(!CurrentGameUI()) + return false; + //только если находимся в режиме single + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if(!pGameSP) return false; + + if(pGameSP->TalkMenu && pGameSP->TalkMenu->IsShown()) + { + pGameSP->TalkMenu->NeedUpdateQuestions(); + } + + + return true; +} + +void CActor::OnDisableInfo(shared_str info_id) const +{ + CInventoryOwner::OnDisableInfo(info_id); + + if(!CurrentGameUI()) + return; + + //только если находимся в режиме single + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if(!pGameSP) return; + + if(pGameSP->TalkMenu && pGameSP->TalkMenu->IsShown()) + pGameSP->TalkMenu->NeedUpdateQuestions(); +} + +void CActor::ReceivePhrase (DIALOG_SHARED_PTR& phrase_dialog) +{ + //только если находимся в режиме single + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if(!pGameSP) return; + + if(pGameSP->TalkMenu && pGameSP->TalkMenu->IsShown()) + pGameSP->TalkMenu->NeedUpdateQuestions(); + + CPhraseDialogManager::ReceivePhrase(phrase_dialog); +} + +void CActor::UpdateAvailableDialogs (CPhraseDialogManager* partner) +{ + m_AvailableDialogs.clear(); + m_CheckedDialogs.clear(); + + if(CInventoryOwner::m_known_info_registry->registry().objects_ptr()) + { + for(KNOWN_INFO_VECTOR::const_iterator it = CInventoryOwner::m_known_info_registry->registry().objects_ptr()->begin(); + CInventoryOwner::m_known_info_registry->registry().objects_ptr()->end() != it; ++it) + { + CInfoPortion info_portion; + + if (!CInfoPortion::ValidInfoPortion(*((*it).info_id))) + continue; + + info_portion.Load((*it).info_id); + + for(u32 i = 0; i(partner); VERIFY(pInvOwnerPartner); + + for(u32 i = 0; iCharacterInfo().ActorDialogs().size(); i++) + AddAvailableDialog(pInvOwnerPartner->CharacterInfo().ActorDialogs()[i], partner); + + CPhraseDialogManager::UpdateAvailableDialogs(partner); +} + +void CActor::TryToTalk() +{ + VERIFY(m_pPersonWeLookingAt); + + if(!IsTalking()) + { + RunTalkDialog(m_pPersonWeLookingAt); + } +} + +void CActor::RunTalkDialog(CInventoryOwner* talk_partner) +{ + //предложить поговорить с нами + if(talk_partner->OfferTalk(this)) + { + StartTalk(talk_partner); + //только если находимся в режиме single + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if(pGameSP) + { + if(pGameSP->TopInputReceiver()) + pGameSP->TopInputReceiver()->HideDialog(); + pGameSP->StartTalk(talk_partner->bDisableBreakDialog); + } + } +} + +void CActor::StartTalk (CInventoryOwner* talk_partner) +{ + CGameObject* GO = smart_cast(talk_partner); VERIFY(GO); + //обновить информацию о контакте +//. UpdateContact(GO->ID()); + + CInventoryOwner::StartTalk(talk_partner); +} +/* +void CActor::UpdateContact (u16 contact_id) +{ + if(ID() == contact_id) return; + + TALK_CONTACT_VECTOR& contacts = contacts_registry->registry().objects(); + for(TALK_CONTACT_VECTOR_IT it = contacts.begin(); contacts.end() != it; ++it) + if((*it).id == contact_id) break; + + if(contacts.end() == it) + { + TALK_CONTACT_DATA contact_data(contact_id, Level().GetGameTime()); + contacts.push_back(contact_data); + } + else + { + (*it).time = Level().GetGameTime(); + } +} +*/ +void CActor::NewPdaContact(CInventoryOwner* pInvOwner) +{ + if (!IsGameTypeSingle()) return; + + if (pInvOwner && pInvOwner->special_map_spot && pInvOwner->special_map_spot->IsShown()) //if we assigned map spot in character_desc then dont add regular map spot + { + return; + } + + bool b_alive = !!(smart_cast(pInvOwner))->g_Alive(); + + CAI_Stalker* pStalker = smart_cast(pInvOwner); + if (pStalker && pStalker->IsGhost()) return; + + CurrentGameUI()->UIMainIngameWnd->AnimateContacts(b_alive); + + Level().MapManager().AddRelationLocation ( pInvOwner ); + + if(CurrentGameUI()){ + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + + if(pGameSP) + pGameSP->m_PdaMenu->PdaContentsChanged (pda_section::contacts); + } +} + +void CActor::LostPdaContact (CInventoryOwner* pInvOwner) +{ + CGameObject* GO = smart_cast(pInvOwner); + if (GO){ + + for(int t = ALife::eRelationTypeFriend; tID()); + } + Level().MapManager().RemoveMapLocation("deadbody_location", GO->ID()); + }; + + if( CurrentGameUI() ){ + CUIGameSP* pGameSP = smart_cast(CurrentGameUI()); + if(pGameSP){ + pGameSP->m_PdaMenu->PdaContentsChanged (pda_section::contacts); + } + } + +} + +void CActor::AddGameNews_deffered (GAME_NEWS_DATA& news_data, u32 delay) +{ + GAME_NEWS_DATA * d = new GAME_NEWS_DATA(news_data); + //*d = news_data; + m_defferedMessages.push_back( SDefNewsMsg() ); + m_defferedMessages.back().news_data = d; + m_defferedMessages.back().time = Device.dwTimeGlobal+delay; + std::sort(m_defferedMessages.begin(), m_defferedMessages.end() ); +} +void CActor::UpdateDefferedMessages() +{ + while( m_defferedMessages.size() ){ + SDefNewsMsg& M = m_defferedMessages.back(); + if(M.time <=Device.dwTimeGlobal){ + AddGameNews(*M.news_data); + xr_delete(M.news_data); + m_defferedMessages.pop_back(); + }else + break; + } +} + +bool CActor::OnDialogSoundHandlerStart(CInventoryOwner *inv_owner, LPCSTR phrase) +{ + CAI_Trader *trader = smart_cast(inv_owner); + if (!trader) return false; + + trader->dialog_sound_start(phrase); + return true; +} +bool CActor::OnDialogSoundHandlerStop(CInventoryOwner *inv_owner) +{ + CAI_Trader *trader = smart_cast(inv_owner); + if (!trader) return false; + + trader->dialog_sound_stop(); + return true; +} diff --git a/src/xrGameLA/actor_defs.h b/src/xrGameLA/actor_defs.h new file mode 100644 index 000000000..5b5a33362 --- /dev/null +++ b/src/xrGameLA/actor_defs.h @@ -0,0 +1,137 @@ +#include "PHSynchronize.h" +#include "xrserver_space.h" + +#pragma once + + +#define ACTOR_HEIGHT 1.75f +#define ACTOR_LOOKOUT_ANGLE PI_DIV_4 +#define ACTOR_LOOKOUT_SPEED 2.f + +namespace ACTOR_DEFS +{ + +enum ESoundCcount { +// SND_HIT_COUNT=8, + SND_DIE_COUNT=4 +}; + +enum EActorCameras { + eacFirstEye = 0, + eacLookAt, + eacFreeLook, + eacMaxCam +}; +enum EDamages {DAMAGE_FX_COUNT = 12}; + + +enum EMoveCommand +{ + mcFwd = (1ul<<0ul), + mcBack = (1ul<<1ul), + mcLStrafe = (1ul<<2ul), + mcRStrafe = (1ul<<3ul), + mcCrouch = (1ul<<4ul), + mcAccel = (1ul<<5ul), + mcTurn = (1ul<<6ul), + mcJump = (1ul<<7ul), + mcFall = (1ul<<8ul), + mcLanding = (1ul<<9ul), + mcLanding2 = (1ul<<10ul), + mcClimb = (1ul<<11ul), + mcSprint = (1ul<<12ul), + mcLLookout = (1ul<<13ul), + mcRLookout = (1ul<<14ul), + mcAnyMove = (mcFwd|mcBack|mcLStrafe|mcRStrafe), + mcAnyAction = (mcAnyMove|mcJump|mcFall|mcLanding|mcLanding2), //mcTurn| + mcAnyState = (mcCrouch|mcAccel|mcClimb|mcSprint), + mcLookout = (mcLLookout|mcRLookout), +}; + +// enum для определения действия над вещью на которую наведен в текущее время прицел. +// Используется для показа всплывающих динамических подсказок +enum EActorAction +{ + eaaNoAction = 0, + eaaPickup, + eaaTalk, + eaaOpenDoor, + eaaSearchCorpse, +}; + +typedef const char* EActorSleep; +extern EActorSleep easCanSleepResult; +/* +//результат функции GoSleep у актера +enum EActorSleep +{ + easCanSleep = 0, + easNotSolidGround, + easEnemies +}; +*/ + +//--------------------------------------------- +// ввод с клавиатуры и мыши +struct net_input +{ + u32 m_dwTimeStamp; + + u32 mstate_wishful; + + u8 cam_mode; + float cam_yaw; + float cam_pitch; + float cam_roll; + + bool operator < (const u32 Time) + { + return m_dwTimeStamp < Time; + }; +}; + +//------------------------------ +struct net_update +{ + u32 dwTimeStamp; // server(game) timestamp + float o_model; // model yaw + SRotation o_torso; // torso in world coords + Fvector p_pos; // in world coords + Fvector p_accel; // in world coords + Fvector p_velocity; // in world coords + u32 mstate; + int weapon; + float fHealth; +// float fArmor; + + net_update() { + dwTimeStamp = 0; + p_pos.set (0,0,0); + p_accel.set (0,0,0); + p_velocity.set (0,0,0); + } + + void lerp (net_update& A,net_update& B, float f); +}; + +/////////////////////////////////////////////////////// +// апдайт с данными физики +struct net_update_A +{ + u32 dwTimeStamp; +// u32 dwTime0; +// u32 dwTime1; + SPHNetState State; +}; + +/////////////////////////////////////////////////////// +// данные для интерполяции +struct InterpData +{ + Fvector Pos; + Fvector Vel; + float o_model; // model yaw + SRotation o_torso; // torso in world coords +}; + +}; \ No newline at end of file diff --git a/src/xrGameLA/actor_input_handler.cpp b/src/xrGameLA/actor_input_handler.cpp new file mode 100644 index 000000000..ec5efb26c --- /dev/null +++ b/src/xrGameLA/actor_input_handler.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "actor_input_handler.h" +#include "actor.h" +#include "level.h" + +void CActorInputHandler::reinit() +{ + m_actor = 0; +} + + +void CActorInputHandler::install() +{ + m_actor = smart_cast (Level().CurrentEntity()); + VERIFY(m_actor); + + m_actor->set_input_external_handler(this); +} + +void CActorInputHandler::install(CActor *actor) +{ + m_actor = actor; + VERIFY(m_actor); + actor->set_input_external_handler(this); +} + +void CActorInputHandler::release() +{ + VERIFY(m_actor); + + m_actor->set_input_external_handler(0); + m_actor = 0; +} + + + diff --git a/src/xrGameLA/actor_input_handler.h b/src/xrGameLA/actor_input_handler.h new file mode 100644 index 000000000..6df65d284 --- /dev/null +++ b/src/xrGameLA/actor_input_handler.h @@ -0,0 +1,18 @@ +#pragma once + +class CActor; + +class CActorInputHandler { +public: + virtual void reinit (); + + virtual void install (); + virtual void install (CActor *); + virtual void release (); + + virtual bool authorized (int cmd){return true;} + virtual float mouse_scale_factor (){return 1.f;} + +protected: + CActor *m_actor; +}; diff --git a/src/xrGameLA/actor_memory.cpp b/src/xrGameLA/actor_memory.cpp new file mode 100644 index 000000000..53c28333a --- /dev/null +++ b/src/xrGameLA/actor_memory.cpp @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : actor_memory.cpp +// Created : 15.09.2005 +// Modified : 15.09.2005 +// Author : Dmitriy Iassenev +// Description : actor memory +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "actor_memory.h" +#include "actor.h" +#include "../camerabase.h" +#include "gamepersistent.h" + +CActorMemory::CActorMemory (CActor *actor) : + inherited ( + actor, + 100 + ), + m_actor (actor) +{ + VERIFY (m_actor); +} + +BOOL CActorMemory::feel_vision_isRelevant (CObject* O) +{ + CEntityAlive *entity_alive = smart_cast(O); + if (!entity_alive) + return (FALSE); + + return (TRUE); +} + +void CActorMemory::camera ( + Fvector &position, + Fvector &direction, + Fvector &normal, + float &field_of_view, + float &aspect_ratio, + float &near_plane, + float &far_plane + ) +{ + CCameraBase &camera = *m_actor->cam_Active(); + camera.Get (position,direction,normal); + field_of_view = deg2rad(camera.f_fov); + aspect_ratio = camera.f_aspect; + near_plane = .1f; + far_plane = g_pGamePersistent->Environment().CurrentEnv->far_plane; +} diff --git a/src/xrGameLA/actor_memory.h b/src/xrGameLA/actor_memory.h new file mode 100644 index 000000000..4d0014903 --- /dev/null +++ b/src/xrGameLA/actor_memory.h @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : actor_memory.h +// Created : 15.09.2005 +// Modified : 15.09.2005 +// Author : Dmitriy Iassenev +// Description : actor memory +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "vision_client.h" + +class CActor; + +class CActorMemory : public vision_client { +private: + typedef vision_client inherited; + +private: + CActor *m_actor; + +public: + CActorMemory (CActor *actor); + virtual BOOL feel_vision_isRelevant (CObject *object); + virtual void camera ( + Fvector &position, + Fvector &direction, + Fvector &normal, + float &field_of_view, + float &aspect_ratio, + float &near_plane, + float &far_plane + ); +}; diff --git a/src/xrGameLA/actor_mp_client.cpp b/src/xrGameLA/actor_mp_client.cpp new file mode 100644 index 000000000..d1094a405 --- /dev/null +++ b/src/xrGameLA/actor_mp_client.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "actor_mp_client.h" +#include "actorcondition.h" + +CActorMP::CActorMP () +{ + m_i_am_dead = false; +} + +void CActorMP::OnEvent ( NET_Packet &P, u16 type) +{ + inherited::OnEvent (P,type); +} + +void CActorMP::Die (CObject *killer) +{ + m_i_am_dead = true; + conditions().health() = 0.f; + inherited::Die (killer); + + if(OnServer()) + { //transfer all items to bag + + } +} diff --git a/src/xrGameLA/actor_mp_client.h b/src/xrGameLA/actor_mp_client.h new file mode 100644 index 000000000..e22734c6a --- /dev/null +++ b/src/xrGameLA/actor_mp_client.h @@ -0,0 +1,29 @@ +#ifndef ACTOR_MP_CLIENT_H +#define ACTOR_MP_CLIENT_H + +#include "actor.h" +#include "actor_mp_state.h" + +class CActorMP : public CActor { +private: + typedef CActor inherited; + +private: + actor_mp_state_holder m_state_holder; + bool m_i_am_dead; + +private: + void fill_state (actor_mp_state &state); + void process_packet (net_update &N); + void postprocess_packet (net_update_A &packet); + +public: + CActorMP (); + virtual void net_Export (NET_Packet &packet); + virtual void net_Import (NET_Packet &packet); + virtual BOOL net_Relevant (); + virtual void OnEvent (NET_Packet &packet, u16 type); + virtual void Die (CObject *killer); +}; + +#endif // ACTOR_MP_CLIENT_H \ No newline at end of file diff --git a/src/xrGameLA/actor_mp_client_export.cpp b/src/xrGameLA/actor_mp_client_export.cpp new file mode 100644 index 000000000..e6d7c2f93 --- /dev/null +++ b/src/xrGameLA/actor_mp_client_export.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "actor_mp_client.h" +#include "CharacterPhysicsSupport.h" +#include "inventory.h" + +/// DONE (111 bytes cut from 138 bytes = 27 bytes, total 511.11% or 19.56%) +// 3 health is form 0.f to 1.f, so use only 8 bits for it, the rest one we will use for mask +// 1 removed flags +// 12 removed logical position +// 12 quantized camera stuff and model yaw +// 3 removed team, squad, group +// 0.125 body state flags +// 6 removed logical acceleration +// 6 removed logical velocity +// 3.5 quantized radiation +// 0.5 quantized active_slot +// 2 dead actors are not synchronized at all, number of synchronization items is no more +// 0.875 physics state enabled +// 12 removed angular velocity +// 9 quantized linear velocity +// 12 removed force +// 12 removed torque +// 16 removed quaternion + +/// TODO +// 4 u32 move time to global packet (+ much less QPC calls) +// 12 3*float write unaffected_r_torso stuff only in case when greande is an active slot (?) +// X mask all the changes we sent + +/// LEFT +// 4 time +// 12 position +// 3 linear velocity +// 1 model yaw +// 3 camera +// 4 inventory_active_slot,body_state_flags,health,radiation,physics_state_enabled + +void CActorMP::fill_state (actor_mp_state &state) +{ + if (OnClient()) + { + R_ASSERT (g_Alive()); + R_ASSERT2 (PHGetSyncItemsNumber() == 1,make_string("PHGetSyncItemsNumber() returned %d, health = %.2f",PHGetSyncItemsNumber(),GetfHealth())); + } + + SPHNetState State; + PHGetSyncItem(0)->get_State (State); + +// static test = false; +// if (test) { +#if 0 + Msg ("Frame [%d], object [%d]",Device.dwFrame,ID()); +// Msg ("quaternion : [%f][%f][%f][%f]",State.quaternion.x,State.quaternion.y,State.quaternion.z,State.quaternion.w); +// Msg ("angular : [%f][%f][%f]",State.angular_vel.x,State.angular_vel.y,State.angular_vel.z); + Msg ("linear : [%f][%f][%f]",State.linear_vel.x,State.linear_vel.y,State.linear_vel.z); +// Msg ("force : [%f][%f][%f]",State.force.x,State.force.y,State.force.z); +// Msg ("torque : [%f][%f][%f]",State.torque.x,State.torque.y,State.torque.z); +// Msg ("acceleration : [%f][%f][%f]",NET_SavedAccel.x,NET_SavedAccel.y,NET_SavedAccel.z); + Msg ("model_yaw : [%f]",angle_normalize(r_model_yaw)); + Msg ("camera_yaw : [%f]",angle_normalize(unaffected_r_torso.yaw)); +// Msg ("camera_pitch : [%f]",angle_normalize(unaffected_r_torso.pitch)); +// Msg ("camera_roll : [%f]",angle_normalize(unaffected_r_torso.roll)); +// } +#endif // 0 + + state.physics_quaternion = State.quaternion; + state.physics_angular_velocity = State.angular_vel; + state.physics_linear_velocity = State.linear_vel; + state.physics_force = State.force; + state.physics_torque = State.torque; + state.physics_position = State.position; + + state.position = Position(); + + state.logic_acceleration = NET_SavedAccel; + + state.model_yaw = angle_normalize(r_model_yaw); + state.camera_yaw = angle_normalize(unaffected_r_torso.yaw); + state.camera_pitch = angle_normalize(unaffected_r_torso.pitch); + state.camera_roll = angle_normalize(unaffected_r_torso.roll); + + state.time = Level().timeServer(); + + state.inventory_active_slot = inventory().GetActiveSlot(); + state.body_state_flags = mstate_real & 0x0000ffff; + state.health = GetfHealth(); + state.radiation = g_Radiation()/100.0f; + state.physics_state_enabled = State.enabled ? 1 : 0; +} + +BOOL CActorMP::net_Relevant () +{ + if (OnClient()) + { + if (!g_Alive()) + return (false); + + if (m_i_am_dead) + return (false); + } + + if (character_physics_support()->IsRemoved()) + return (false); + + actor_mp_state state; + fill_state (state); + return (m_state_holder.relevant(state)); +} + +void CActorMP::net_Export (NET_Packet &packet) +{ + if (OnClient()) + { + R_ASSERT (g_Alive()); + R_ASSERT (PHGetSyncItemsNumber() == 1); + } + m_state_holder.write (packet); +} diff --git a/src/xrGameLA/actor_mp_client_import.cpp b/src/xrGameLA/actor_mp_client_import.cpp new file mode 100644 index 000000000..c6b726ae8 --- /dev/null +++ b/src/xrGameLA/actor_mp_client_import.cpp @@ -0,0 +1,134 @@ +#include "stdafx.h" +#include "actor_mp_client.h" +#include "inventory.h" +#include "../camerabase.h" + +void CActorMP::net_Import ( NET_Packet &P) +{ + net_update N; + + m_state_holder.read (P); + + if (m_i_am_dead) + return; + + if (OnClient()) + SetfHealth (m_state_holder.state().health); + + if (OnClient()) + SetfRadiation (m_state_holder.state().radiation*100.0f); + + u8 ActiveSlot = m_state_holder.state().inventory_active_slot; + if (OnClient()) + { +//. if (ActiveSlot >= SLOTS_TOTAL) inventory().SetActiveSlot(NO_ACTIVE_SLOT); + if (ActiveSlot > LAST_SLOT) inventory().Activate(NO_ACTIVE_SLOT); + else + { + if (inventory().GetActiveSlot() != u32(ActiveSlot) ) + inventory().Activate(u32(ActiveSlot), eImportUpdate); + }; + } + + N.mstate = m_state_holder.state().body_state_flags; + + N.dwTimeStamp = m_state_holder.state().time; + N.p_pos = m_state_holder.state().position; + + N.o_model = m_state_holder.state().model_yaw; + N.o_torso.yaw = m_state_holder.state().camera_yaw; + N.o_torso.pitch = m_state_holder.state().camera_pitch; + N.o_torso.roll = m_state_holder.state().camera_roll; + + if (N.o_torso.roll > PI) + N.o_torso.roll -= PI_MUL_2; + + { + if (Level().IsDemoPlay() || OnServer() || Remote()) + { + unaffected_r_torso.yaw = N.o_torso.yaw; + unaffected_r_torso.pitch = N.o_torso.pitch; + unaffected_r_torso.roll = N.o_torso.roll; + + cam_Active()->yaw = -N.o_torso.yaw; + cam_Active()->pitch = N.o_torso.pitch; + }; + }; + + //CSE_ALifeCreatureActor + N.p_accel = m_state_holder.state().logic_acceleration; + + process_packet (N); + + net_update_A N_A; + m_States.clear (); + + N_A.State.enabled = m_state_holder.state().physics_state_enabled; + N_A.State.angular_vel = m_state_holder.state().physics_angular_velocity; + N_A.State.linear_vel = m_state_holder.state().physics_linear_velocity; + N_A.State.force = m_state_holder.state().physics_force; + N_A.State.torque = m_state_holder.state().physics_torque; + N_A.State.position = m_state_holder.state().physics_position; + N_A.State.quaternion = m_state_holder.state().physics_quaternion; + + // interpolcation + postprocess_packet (N_A); +} + +void CActorMP::postprocess_packet (net_update_A &N_A) +{ + + if (!NET.empty()) + N_A.dwTimeStamp = NET.back().dwTimeStamp; + else + N_A.dwTimeStamp = Level().timeServer(); + + N_A.State.previous_position = N_A.State.position; + N_A.State.previous_quaternion = N_A.State.quaternion; + + if (Local() && OnClient() || !g_Alive()) return; + + { + //----------------------------------------------- + if (!NET_A.empty() && N_A.dwTimeStamp < NET_A.back().dwTimeStamp) return; + if (!NET_A.empty() && N_A.dwTimeStamp == NET_A.back().dwTimeStamp) + { + NET_A.back() = N_A; + } + else + { + NET_A.push_back (N_A); + if (NET_A.size()>5) NET_A.pop_front(); + }; + + if (!NET_A.empty()) m_bInterpolate = true; + }; + + Level().AddObject_To_Objects4CrPr (this); + CrPr_SetActivated (false); + CrPr_SetActivationStep (0); +} + +void CActorMP::process_packet (net_update &N) +{ + if (Local() && OnClient()) + return; + + if (!NET.empty() && (N.dwTimeStamp < NET.back().dwTimeStamp)) + return; + + if (g_Alive()) { + setVisible ((BOOL)!HUDview ()); + setEnabled (TRUE); + }; + + if (!NET.empty() && (N.dwTimeStamp == NET.back().dwTimeStamp)) { + NET.back() = N; + return; + } + + NET.push_back (N); + + if (NET.size() > 5) + NET.pop_front (); +} diff --git a/src/xrGameLA/actor_mp_server.cpp b/src/xrGameLA/actor_mp_server.cpp new file mode 100644 index 000000000..ab72a9919 --- /dev/null +++ b/src/xrGameLA/actor_mp_server.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "actor_mp_server.h" + +CSE_ActorMP::CSE_ActorMP (LPCSTR section) : + inherited (section) +{ + m_ready_to_update = false; +} + +void CSE_ActorMP::STATE_Read (NET_Packet &packet, u16 size) +{ + inherited::STATE_Read (packet,size); +} + +void CSE_ActorMP::STATE_Write (NET_Packet &packet) +{ + inherited::STATE_Write (packet); +} + +BOOL CSE_ActorMP::Net_Relevant () +{ + if (fHealth<=0) return (false); + return (inherited::Net_Relevant()); +} + + +#ifdef XRGAME_EXPORTS +void CSE_ActorMP::on_death (CSE_Abstract *killer) +{ + inherited::on_death(killer); + + actor_mp_state state; + fill_state (state); + m_state_holder.relevant (state); +} +#endif \ No newline at end of file diff --git a/src/xrGameLA/actor_mp_server.h b/src/xrGameLA/actor_mp_server.h new file mode 100644 index 000000000..87fd5794d --- /dev/null +++ b/src/xrGameLA/actor_mp_server.h @@ -0,0 +1,31 @@ +#ifndef ACTOR_MP_SERVER_H +#define ACTOR_MP_SERVER_H + +#include "xrServer_Objects_ALife_Monsters.h" +#include "actor_mp_state.h" + +class CSE_ActorMP : public CSE_ALifeCreatureActor { +private: + typedef CSE_ALifeCreatureActor inherited; + +private: + actor_mp_state_holder m_state_holder; + bool m_ready_to_update; + +private: + void fill_state (actor_mp_state &state); + +public: + CSE_ActorMP (LPCSTR section); + virtual void UPDATE_Read (NET_Packet &packet); + virtual void UPDATE_Write (NET_Packet &packet); + virtual void STATE_Read (NET_Packet &packet, u16 size); + virtual void STATE_Write (NET_Packet &packet); + virtual BOOL Net_Relevant (); + +#ifdef XRGAME_EXPORTS + virtual void on_death (CSE_Abstract *killer); +#endif +}; + +#endif // ACTOR_MP_SERVER_H \ No newline at end of file diff --git a/src/xrGameLA/actor_mp_server_export.cpp b/src/xrGameLA/actor_mp_server_export.cpp new file mode 100644 index 000000000..c3ea3cc62 --- /dev/null +++ b/src/xrGameLA/actor_mp_server_export.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" +#include "actor_mp_server.h" +#include "../../xrNetServer/net_utils.h" + +void CSE_ActorMP::fill_state (actor_mp_state &state) +{ + state.physics_quaternion = m_AliveState.quaternion; + state.physics_angular_velocity = m_AliveState.angular_vel; + state.physics_linear_velocity = m_AliveState.linear_vel; + state.physics_force = m_AliveState.force; + state.physics_torque = m_AliveState.torque; + state.physics_position = m_AliveState.position; + + state.position = o_Position; + + state.logic_acceleration = accel; + + state.model_yaw = angle_normalize(o_model); + state.camera_yaw = angle_normalize(o_torso.yaw); + state.camera_pitch = angle_normalize(o_torso.pitch); + state.camera_roll = angle_normalize(o_torso.roll); + + state.time = timestamp; + + state.inventory_active_slot = weapon; + state.body_state_flags = mstate; + state.health = fHealth; + state.radiation = fRadiation; + state.physics_state_enabled = m_AliveState.enabled ? 1 : 0; + + m_ready_to_update = true; +} + +void CSE_ActorMP::UPDATE_Write (NET_Packet &packet) +{ + if (!m_ready_to_update) { + actor_mp_state state; + fill_state (state); + m_state_holder.relevant (state); + } + + m_state_holder.write (packet); +} diff --git a/src/xrGameLA/actor_mp_server_import.cpp b/src/xrGameLA/actor_mp_server_import.cpp new file mode 100644 index 000000000..9f1a2532e --- /dev/null +++ b/src/xrGameLA/actor_mp_server_import.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" +#include "actor_mp_server.h" +#include "../../xrNetServer/net_utils.h" + +void CSE_ActorMP::UPDATE_Read (NET_Packet &packet) +{ + flags = 0; + m_u16NumItems = 1; + velocity.set (0.f,0.f,0.f); + + if (fHealth<=0) + { + actor_mp_state_holder tmp_state_holder; + tmp_state_holder.read (packet); + return; + } + m_state_holder.read (packet); + + m_AliveState.quaternion = m_state_holder.state().physics_quaternion; + m_AliveState.angular_vel = m_state_holder.state().physics_angular_velocity; + m_AliveState.linear_vel = m_state_holder.state().physics_linear_velocity; + m_AliveState.force = m_state_holder.state().physics_force; + m_AliveState.torque = m_state_holder.state().physics_torque; + m_AliveState.position = m_state_holder.state().physics_position; + + o_Position = m_state_holder.state().position; + + accel = m_state_holder.state().logic_acceleration; + + o_model = m_state_holder.state().model_yaw; + o_torso.yaw = m_state_holder.state().camera_yaw; + o_torso.pitch = m_state_holder.state().camera_pitch; + o_torso.roll = m_state_holder.state().camera_roll; + + timestamp = m_state_holder.state().time; + + weapon = m_state_holder.state().inventory_active_slot; + mstate = m_state_holder.state().body_state_flags; + fHealth = m_state_holder.state().health; + fRadiation = m_state_holder.state().radiation; + m_AliveState.enabled = m_state_holder.state().physics_state_enabled; + + m_ready_to_update = true; +} diff --git a/src/xrGameLA/actor_mp_state.cpp b/src/xrGameLA/actor_mp_state.cpp new file mode 100644 index 000000000..db61d1a5a --- /dev/null +++ b/src/xrGameLA/actor_mp_state.cpp @@ -0,0 +1,263 @@ +#include "stdafx.h" +#include "actor_mp_state.h" +#include "../../xrNetServer/net_utils.h" + +#define USE_LOGIC_ACCELERATION + +enum { + physics_linear_velocity_x_flag = u32(1) << 0, + physics_linear_velocity_y_flag = u32(1) << 1, + physics_linear_velocity_z_flag = u32(1) << 2, + physics_position_x_flag = u32(1) << 3, + physics_position_y_flag = u32(1) << 4, + physics_position_z_flag = u32(1) << 5, + model_yaw_flag = u32(1) << 6, + camera_yaw_flag = u32(1) << 7, + camera_pitch_flag = u32(1) << 8, + camera_roll_flag = u32(1) << 9, + inventory_active_slot_flag = u32(1) << 10, + body_state_flags_flag = u32(1) << 11, + health_flag = u32(1) << 12, + radiation_flag = u32(1) << 13, + physics_state_enabled_flag = u32(1) << 14, +#ifdef USE_LOGIC_ACCELERATION + logic_acceleration_flag = u32(1) << 15, +#endif // USE_LOGIC_ACCELERATION +}; + +#if 0 +enum { + is_acceleration_null = u32(1) << 0, + is_roll_null = u32(1) << 1, + is_camera_yaw_the_same_as_model_yaw = u32(1) << 2, + is_physics_state_enabled = u32(1) << 3, +}; +#endif // 0 + +enum { + inventory_active_slot_bits = u32(4), + body_state_flags_bits = u32(15), + health_bits = u32(8), + radiation_bits = u32(4), + physics_state_enabled_bits = u32(1), +}; + +static const float min_linear_velocity_component = -32.f; +static const float max_linear_velocity_component = 32.f; + +#ifdef USE_DIFFERENCES +IC bool is_similar (const Fvector &_0, const Fvector &_1) +{ + return ( + fsimilar(_0.x,_1.x,EPS) && + fsimilar(_0.y,_1.y,EPS) && + fsimilar(_0.z,_1.z,EPS) + ); +} + +IC bool is_similar (const float &_0, const float &_1) +{ + return (!!fsimilar(_0,_1,EPS)); +} + +IC bool is_similar (const u32 &_0, const u32 &_1) +{ + return (_0 == _1); +} + +IC bool is_similar (const u16 &_0, const u16 &_1) +{ + return (_0 == _1); +} +#endif // USE_DIFFERENCES + +IC bool actor_mp_state_holder::check (const int &flag) const +{ +#ifdef USE_DIFFERENCES + return (!!(m_mask & flag)); +#else // USE_DIFFERENCES + return (true); +#endif // USE_DIFFERENCES +} + +IC void write (const u32 &bit_count, const u32 &value, u32 ¤t, u32 &output) +{ + output |= ((value & ((u32(1) << bit_count) - 1)) << current); + current += bit_count; + VERIFY (current <= 32); +} + +IC u32 read (const u32 &bit_count, u32 ¤t, const u32 &output) +{ + u32 result = (output >> current) & ((u32(1) << bit_count) - 1); + current += bit_count; + VERIFY (current <= 32); + return (result); +} + +IC float unpack (const u32 &packed_value, const u32 bit_count) +{ + u32 max_value = (u32(1) << bit_count) - 1; + float result = float(packed_value) / (float(max_value) + .0001f); + return (result); +} + +IC u32 pack (const float &unpacked_value, const u32 bit_count) +{ + float inValue = unpacked_value; + clamp(inValue, 0.f, 1.f); + u32 max_value = (u32(1) << bit_count) - 1; + u32 result = iFloor(float(max_value)*inValue + .5f); + + if (bit_count>1) + if (result == 0 && unpacked_value != 0) + result += 1; + + clamp (result,u32(0),max_value); + return (result); +} + +bool actor_mp_state_holder::relevant (const actor_mp_state &state) +{ +#ifdef USE_DIFFERENCES + m_mask = 0; + + m_mask |= is_similar(m_state.physics_linear_velocity.x ,state.physics_linear_velocity.x) ? physics_linear_velocity_x_flag : 0; + m_mask |= is_similar(m_state.physics_linear_velocity.y ,state.physics_linear_velocity.y) ? physics_linear_velocity_y_flag : 0; + m_mask |= is_similar(m_state.physics_linear_velocity.z ,state.physics_linear_velocity.z) ? physics_linear_velocity_z_flag : 0; + m_mask |= is_similar(m_state.physics_position.x ,state.physics_position.x) ? physics_position_x_flag : 0; + m_mask |= is_similar(m_state.physics_position.y ,state.physics_position.y) ? physics_position_y_flag : 0; + m_mask |= is_similar(m_state.physics_position.z ,state.physics_position.z) ? physics_position_z_flag : 0; + m_mask |= is_similar(m_state.model_yaw ,state.model_yaw) ? model_yaw_flag : 0; + m_mask |= is_similar(m_state.camera_yaw ,state.camera_yaw) ? camera_yaw_flag : 0; + m_mask |= is_similar(m_state.camera_pitch ,state.camera_pitch) ? camera_pitch_flag : 0; + m_mask |= is_similar(m_state.camera_roll ,state.camera_roll) ? camera_roll_flag : 0; + m_mask |= is_similar(m_state.inventory_active_slot ,state.inventory_active_slot) ? inventory_active_slot_flag : 0; + m_mask |= is_similar(m_state.body_state_flags ,state.body_state_flags) ? body_state_flags_flag : 0; + m_mask |= is_similar(m_state.health ,state.health) ? health_flag : 0; + m_mask |= is_similar(m_state.radiation ,state.radiation) ? radiation_flag : 0; + m_mask |= is_similar(m_state.physics_state_enabled ,state.physics_state_enabled) ? physics_state_enabled_flag : 0; +# ifdef USE_LOGIC_ACCELERATION + m_mask |= is_similar(m_state.logic_acceleration ,state.logic_acceleration) ? logic_acceleration_flag : 0; +# endif // USE_LOGIC_ACCELERATION +#endif // USE_DIFFERENCES + m_state = state; + +#ifdef USE_DIFFERENCES + return (!!m_mask); +#else // USE_DIFFERENCES + return (true); +#endif // USE_DIFFERENCES +} + +void actor_mp_state_holder::write (NET_Packet &packet) +{ +#ifdef USE_DIFFERENCES + R_ASSERT (m_mask); + packet.w_u32(m_mask); +#endif // USE_DIFFERENCES + + packet.w_u32(m_state.time); + + clamp (m_state.physics_linear_velocity.x,min_linear_velocity_component,max_linear_velocity_component); + clamp (m_state.physics_linear_velocity.y,min_linear_velocity_component,max_linear_velocity_component); + clamp (m_state.physics_linear_velocity.z,min_linear_velocity_component,max_linear_velocity_component); + + if (check(physics_linear_velocity_x_flag )) packet.w_float_q8 (m_state.physics_linear_velocity.x ,min_linear_velocity_component ,max_linear_velocity_component); + if (check(physics_linear_velocity_y_flag )) packet.w_float_q8 (m_state.physics_linear_velocity.y ,min_linear_velocity_component ,max_linear_velocity_component); + if (check(physics_linear_velocity_z_flag )) packet.w_float_q8 (m_state.physics_linear_velocity.z ,min_linear_velocity_component ,max_linear_velocity_component); + if (check(physics_position_x_flag )) packet.w_float (m_state.physics_position.x ); + if (check(physics_position_y_flag )) packet.w_float (m_state.physics_position.y ); + if (check(physics_position_z_flag )) packet.w_float (m_state.physics_position.z ); + if (check(model_yaw_flag )) packet.w_float_q8 (m_state.model_yaw ,0.f ,PI_MUL_2); + if (check(camera_yaw_flag )) packet.w_float_q8 (m_state.camera_yaw ,0.f ,PI_MUL_2); + if (check(camera_pitch_flag )) packet.w_float_q8 (m_state.camera_pitch ,0.f ,PI_MUL_2); + if (check(camera_roll_flag )) packet.w_float_q8 (m_state.camera_roll ,0.f ,PI_MUL_2); + +#ifdef USE_LOGIC_ACCELERATION + if (check(logic_acceleration_flag )) packet.w_sdir (m_state.logic_acceleration ); +#else // USE_LOGIC_ACCELERATION + m_state.logic_acceleration.set (0.f,0.f,0.f); +#endif // USE_LOGIC_ACCELERATION + + u32 current = 0; + u32 output = 0; + + if (check(inventory_active_slot_flag )) ::write(inventory_active_slot_bits ,m_state.inventory_active_slot ,current,output); + if (check(body_state_flags_flag )) ::write(body_state_flags_bits ,m_state.body_state_flags ,current,output); + if (check(health_flag )) ::write(health_bits ,pack(m_state.health,health_bits) ,current,output); + if (check(radiation_flag )) ::write(radiation_bits ,pack(m_state.radiation,radiation_bits) ,current,output); + if (check(physics_state_enabled_flag )) ::write(physics_state_enabled_bits ,m_state.physics_state_enabled ,current,output); + + packet.w_u8 (u8((output & 0x000000ff) >> 0)); + if (current <= 8) + return; + + packet.w_u8 (u8((output & 0x0000ff00) >> 8)); + if (current <= 16) + return; + + packet.w_u8 (u8((output & 0x00ff0000) >> 16)); + if (current <= 24) + return; + + packet.w_u8 (u8((output & 0xff000000) >> 24)); +} + +void actor_mp_state_holder::read (NET_Packet &packet) +{ +#ifdef USE_DIFFERENCES + packet.r_u32(m_mask); +#endif // USE_DIFFERENCES + + packet.r_u32(m_state.time); + + if (check(physics_linear_velocity_x_flag )) packet.r_float_q8 (m_state.physics_linear_velocity.x ,min_linear_velocity_component ,max_linear_velocity_component); + if (check(physics_linear_velocity_y_flag )) packet.r_float_q8 (m_state.physics_linear_velocity.y ,min_linear_velocity_component ,max_linear_velocity_component); + if (check(physics_linear_velocity_z_flag )) packet.r_float_q8 (m_state.physics_linear_velocity.z ,min_linear_velocity_component ,max_linear_velocity_component); + if (check(physics_position_x_flag )) packet.r_float (m_state.physics_position.x ); + if (check(physics_position_y_flag )) packet.r_float (m_state.physics_position.y ); + if (check(physics_position_z_flag )) packet.r_float (m_state.physics_position.z ); + if (check(model_yaw_flag )) packet.r_float_q8 (m_state.model_yaw,0.f,PI_MUL_2 ); + if (check(camera_yaw_flag )) packet.r_float_q8 (m_state.camera_yaw,0.f,PI_MUL_2 ); + if (check(camera_pitch_flag )) packet.r_float_q8 (m_state.camera_pitch,0.f,PI_MUL_2 ); + if (check(camera_roll_flag )) packet.r_float_q8 (m_state.camera_roll,0.f,PI_MUL_2 ); + +#ifdef USE_LOGIC_ACCELERATION + if (check(logic_acceleration_flag )) packet.r_sdir (m_state.logic_acceleration ); +#else // USE_LOGIC_ACCELERATION + m_state.logic_acceleration.set (0.f,0.f,0.f); +#endif // USE_LOGIC_ACCELERATION + + m_state.position = m_state.physics_position; + + u32 current = 0; + u32 total_bit_count = 0; + u32 output = 0; + + if (check(inventory_active_slot_flag )) total_bit_count += inventory_active_slot_bits; + if (check(body_state_flags_flag )) total_bit_count += body_state_flags_bits; + if (check(health_flag )) total_bit_count += health_bits; + if (check(radiation_flag )) total_bit_count += radiation_bits; + if (check(physics_state_enabled_flag )) total_bit_count += physics_state_enabled_bits; + + if (!total_bit_count) + return; + + output |= u32(packet.r_u8()) << 0; + + if (total_bit_count > 8) + output |= u32(packet.r_u8()) << 8; + + if (total_bit_count > 16) + output |= u32(packet.r_u8()) << 16; + + if (total_bit_count > 24) + output |= u32(packet.r_u8()) << 24; + + if (check(inventory_active_slot_flag )) m_state.inventory_active_slot = ::read(inventory_active_slot_bits ,current,output); + if (check(body_state_flags_flag )) m_state.body_state_flags = ::read(body_state_flags_bits ,current,output); + if (check(health_flag )) m_state.health = unpack( ::read(health_bits ,current,output),health_bits); + if (check(radiation_flag )) m_state.radiation = unpack( ::read(radiation_bits ,current,output),radiation_bits); + if (check(physics_state_enabled_flag )) m_state.physics_state_enabled = ::read(physics_state_enabled_bits ,current,output); +} diff --git a/src/xrGameLA/actor_mp_state.h b/src/xrGameLA/actor_mp_state.h new file mode 100644 index 000000000..41580a61c --- /dev/null +++ b/src/xrGameLA/actor_mp_state.h @@ -0,0 +1,51 @@ +#ifndef ACTOR_MP_STATE_H +#define ACTOR_MP_STATE_H + +//#define USE_DIFFERENCES + +struct actor_mp_state { + Fquaternion physics_quaternion; + Fvector physics_angular_velocity; + Fvector physics_linear_velocity; + Fvector physics_force; + Fvector physics_torque; + Fvector physics_position; + Fvector position; // should be removed in future(?) + Fvector logic_acceleration; + + float model_yaw; + float camera_yaw; // should be removed in future + float camera_pitch; // should be removed in future + float camera_roll; // should be removed in future + + u32 time; // should be removed + + float health; + float radiation; + + u32 inventory_active_slot : 4; + u32 body_state_flags : 15; + u32 physics_state_enabled : 1; +}; + +class actor_mp_state_holder { +private: +#ifdef USE_DIFFERENCES + u32 m_mask; +#endif // USE_DIFFERENCES + actor_mp_state m_state; + +private: + IC bool check (const int &flag) const; + +public: + IC actor_mp_state_holder (); + void write (NET_Packet &packet); + void read (NET_Packet &packet); + bool relevant (const actor_mp_state &state); + IC const actor_mp_state &state () const; +}; + +#include "actor_mp_state_inline.h" + +#endif // ACTOR_MP_STATE_H \ No newline at end of file diff --git a/src/xrGameLA/actor_mp_state_inline.h b/src/xrGameLA/actor_mp_state_inline.h new file mode 100644 index 000000000..4cc410b91 --- /dev/null +++ b/src/xrGameLA/actor_mp_state_inline.h @@ -0,0 +1,15 @@ +#ifndef ACTOR_MP_STATE_INLINE_H +#define ACTOR_MP_STATE_INLINE_H + +IC actor_mp_state_holder::actor_mp_state_holder () +{ + ZeroMemory (&m_state,sizeof(m_state)); + m_state.physics_quaternion.z = 1.f; +} + +IC const actor_mp_state &actor_mp_state_holder::state () const +{ + return (m_state); +} + +#endif // ACTOR_MP_STATE_INLINE_H \ No newline at end of file diff --git a/src/xrGameLA/actor_statistic_defs.h b/src/xrGameLA/actor_statistic_defs.h new file mode 100644 index 000000000..5a3718624 --- /dev/null +++ b/src/xrGameLA/actor_statistic_defs.h @@ -0,0 +1,33 @@ +#pragma once +#include "alife_abstract_registry.h" + +extern xr_token actor_stats_token[]; + + +struct SStatDetailBData: public IPureSerializeObject +{ + shared_str key; + s32 int_count; + s32 int_points; + shared_str str_value; + + virtual void save (IWriter &stream); + virtual void load (IReader &stream); +}; + + +typedef xr_vector vStatDetailData; + +struct SStatSectionData: public IPureSerializeObject +{ + shared_str key; + vStatDetailData data; + + SStatDetailBData& GetData (const shared_str&); + s32 GetTotalPoints () const; + virtual void save (IWriter &stream); + virtual void load (IReader &stream); +}; + +typedef xr_vector vStatSectionData; +typedef CALifeAbstractRegistry CActorStatisticRegistry; \ No newline at end of file diff --git a/src/xrGameLA/actor_statistic_mgr.cpp b/src/xrGameLA/actor_statistic_mgr.cpp new file mode 100644 index 000000000..d33c6df00 --- /dev/null +++ b/src/xrGameLA/actor_statistic_mgr.cpp @@ -0,0 +1,184 @@ +#include "stdafx.h" +#include "actor_statistic_mgr.h" +#include "alife_registry_wrappers.h" +#include "alife_simulator_header.h" +/* +xr_token actor_stats_token[]={ + {"total", 100}, + {"stalkerkills", 1}, + {"monsterkills", 2}, + {"quests", 3}, + {"artefacts", 4}, + {"reputation", 5}, + {"foo", 0}, + {0, 0} +}; +*/ +void SStatDetailBData::save(IWriter &stream) +{ + save_data (key, stream); + save_data (int_count, stream); + save_data (int_points, stream); + save_data (str_value, stream); +} + +void SStatDetailBData::load(IReader &stream) +{ + load_data (key, stream); + load_data (int_count, stream); + load_data (int_points, stream); + + if(ai().get_alife()->header().version()>0x0002) + load_data (str_value, stream); +} + + +//////////////////////////////////////////////// +void SStatSectionData::save(IWriter &stream) +{ + save_data (data, stream); + save_data (key, stream); +}; + +void SStatSectionData::load(IReader &stream) +{ + load_data (data, stream); + if(ai().get_alife()->header().version()==0x0002) + { + int tmp; + load_data (tmp, stream); + switch(tmp) + { + case 100: + key = "total"; + break; + case 1: + key = "stalkerkills"; + break; + case 2: + key = "monsterkills"; + break; + case 3: + key = "quests"; + break; + case 4: + key = "artefacts"; + break; + case 5: + key = "reputation"; + break; + case 0: + key = "foo"; + break; + } + s32 tmp2; + load_data (tmp2, stream);// old total_points + }else + load_data (key, stream); +}; + +SStatDetailBData& SStatSectionData::GetData (const shared_str& key) +{ + vStatDetailData::iterator it = data.begin(); + vStatDetailData::iterator it_e = data.end(); + + for(;it!=it_e;++it){ + if((*it).key == key) + return *it; + } + data.resize (data.size()+1); + data.back ().key = key; + data.back ().int_count = 0; + data.back ().int_points = 0; + return data.back (); +} + +s32 SStatSectionData::GetTotalPoints() const +{ + s32 res = 0; + vStatDetailData::const_iterator it = data.begin(); + vStatDetailData::const_iterator it_e = data.end(); + for(;it!=it_e;++it) + { + if((*it).str_value.size()!=0) + return -1; + + res += (*it).int_count*(*it).int_points; + } + return res; + +} + +CActorStatisticMgr::CActorStatisticMgr () +{ + m_actor_stats_wrapper = new CActorStatisticsWrapper(); + m_actor_stats_wrapper->registry().init(0); +} + +CActorStatisticMgr::~CActorStatisticMgr () +{ + xr_delete(m_actor_stats_wrapper); +} + +vStatSectionData& CActorStatisticMgr::GetStorage () +{ + return m_actor_stats_wrapper->registry().objects(); +} + +const vStatSectionData& CActorStatisticMgr::GetCStorage () +{ + return m_actor_stats_wrapper->registry().objects(); +} + +SStatSectionData& CActorStatisticMgr::GetSection (const shared_str& key) +{ + vStatSectionData& d = GetStorage(); + vStatSectionData::iterator it = d.begin(); + vStatSectionData::iterator it_e = d.end(); + for(;it!=it_e;++it){ + if((*it).key==key) + return *it; + } + d.resize (d.size()+1); + d.back().key = key; + return d.back (); +} + +void CActorStatisticMgr::AddPoints(const shared_str& key, const shared_str& detail_key, const shared_str& str_value) +{ + SStatSectionData& sect = GetSection (key); + SStatDetailBData& d = sect.GetData (detail_key); + d.str_value = str_value; +} + +void CActorStatisticMgr::AddPoints(const shared_str& key, const shared_str& detail_key, s32 cnt, s32 pts) +{ + SStatSectionData& sect = GetSection (key); + SStatDetailBData& d = sect.GetData (detail_key); + d.int_count += cnt; + d.int_points += cnt*pts; +} + +s32 CActorStatisticMgr::GetSectionPoints(const shared_str& key) +{ + if( key != "total" ) + return GetSection(key).GetTotalPoints(); + else{//total + s32 _total = -1; + vStatSectionData& d = GetStorage(); + vStatSectionData::iterator it = d.begin(); + vStatSectionData::iterator it_e = d.end(); + for(;it!=it_e;++it) + { + s32 _p = (*it).GetTotalPoints(); + + if(_p !=-1) + { + if(_total==-1) _total=0; + + _total += _p; + } + } + return _total; + } +} diff --git a/src/xrGameLA/actor_statistic_mgr.h b/src/xrGameLA/actor_statistic_mgr.h new file mode 100644 index 000000000..89625109e --- /dev/null +++ b/src/xrGameLA/actor_statistic_mgr.h @@ -0,0 +1,19 @@ +#pragma once +#include "actor_statistic_defs.h" + +class CActorStatisticsWrapper; +class CActorStatisticMgr +{ +private: + CActorStatisticsWrapper* m_actor_stats_wrapper; + vStatSectionData& GetStorage (); +public: + CActorStatisticMgr (); + ~CActorStatisticMgr (); + SStatSectionData& GetSection (const shared_str& key); + + void AddPoints (const shared_str& key, const shared_str& detail_key, const shared_str& str_value); + void AddPoints (const shared_str& key, const shared_str& detail_key, s32 cnt, s32 pts); + s32 GetSectionPoints (const shared_str& key); + const vStatSectionData& GetCStorage (); +}; \ No newline at end of file diff --git a/src/xrGameLA/agent_corpse_manager.cpp b/src/xrGameLA/agent_corpse_manager.cpp new file mode 100644 index 000000000..89c7490c5 --- /dev/null +++ b/src/xrGameLA/agent_corpse_manager.cpp @@ -0,0 +1,120 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_corpse_manager.cpp +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent corpse manager +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "agent_corpse_manager.h" +#include "member_order.h" +#include "ai/stalker/ai_stalker.h" +#include "memory_manager.h" +#include "visual_memory_manager.h" +#include "agent_manager.h" +#include "agent_member_manager.h" + +struct CRemoveMemberCorpsesPredicate { + IC bool operator() (CMemberCorpse &corpse) const + { + return (!!corpse.reactor()); + } +}; + +struct CRemoveOfflineCorpsesPredicate { + CObject *m_object; + IC CRemoveOfflineCorpsesPredicate (CObject *object) + { + VERIFY (object); + m_object = object; + } + + IC bool operator() (CMemberCorpse &corpse) const + { + return (corpse.corpse()->ID() == m_object->ID()); + } +}; + +bool CAgentCorpseManager::process_corpse (CMemberOrder &member) +{ + float min_dist_sqr = flt_max; + CMemberCorpse *best_corpse = 0; + xr_vector::iterator I = m_corpses.begin(); + xr_vector::iterator E = m_corpses.end(); + for ( ; I != E; ++I) { + if (!member.object().memory().visual().visible_now((*I).corpse())) + continue; + + float dist_sqr = (*I).corpse()->Position().distance_to_sqr(member.object().Position()); + if (dist_sqr < min_dist_sqr) { + if ( + (*I).reactor() && + ((*I).reactor()->Position().distance_to_sqr((*I).corpse()->Position()) <= min_dist_sqr) + ) + continue; + min_dist_sqr = dist_sqr; + best_corpse = &*I; + } + } + + if (!best_corpse) + return (false); + + best_corpse->reactor (&member.object()); + return (true); +} + +void CAgentCorpseManager::react_on_member_death () +{ + for (;;) { + bool changed = false; + CAgentMemberManager::MEMBER_STORAGE::iterator I = object().member().combat_members().begin(); + CAgentMemberManager::MEMBER_STORAGE::iterator E = object().member().combat_members().end(); + for ( ; I != E; ++I) + if (!(*I)->member_death_reaction().m_processing) + changed = process_corpse(**I); + + if (!changed) + break; + } + + { + MEMBER_CORPSES::iterator I = m_corpses.begin(); + MEMBER_CORPSES::iterator E = m_corpses.end(); + for ( ; I != E; ++I) { + if (!(*I).reactor()) + continue; + + CMemberOrder::CMemberDeathReaction &reaction = object().member().member((*I).reactor()).member_death_reaction(); + reaction.m_member = (*I).corpse(); + reaction.m_time = (*I).time(); + reaction.m_processing = true; + } + + m_corpses.erase ( + std::remove_if( + m_corpses.begin(), + m_corpses.end(), + CRemoveMemberCorpsesPredicate() + ), + m_corpses.end() + ); + } +} + +void CAgentCorpseManager::remove_links (CObject *object) +{ + m_corpses.erase ( + std::remove_if( + m_corpses.begin(), + m_corpses.end(), + CRemoveOfflineCorpsesPredicate(object) + ), + m_corpses.end() + ); +} + +void CAgentCorpseManager::update () +{ +} diff --git a/src/xrGameLA/agent_corpse_manager.h b/src/xrGameLA/agent_corpse_manager.h new file mode 100644 index 000000000..92df6fe8a --- /dev/null +++ b/src/xrGameLA/agent_corpse_manager.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_corpse_manager.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent corpse manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "member_corpse.h" + +class CAgentManager; +class CMemberOrder; + +class CAgentCorpseManager { +public: + typedef xr_vector MEMBER_CORPSES; + +private: + MEMBER_CORPSES m_corpses; + CAgentManager *m_object; + +protected: + IC CAgentManager &object () const; + bool process_corpse (CMemberOrder &member); + +public: + IC CAgentCorpseManager (CAgentManager *object); + IC void register_corpse (CAI_Stalker *corpse); + IC MEMBER_CORPSES &corpses (); + IC void clear (); + void react_on_member_death (); + void update (); + void remove_links (CObject *object); +}; + +#include "agent_corpse_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/agent_corpse_manager_inline.h b/src/xrGameLA/agent_corpse_manager_inline.h new file mode 100644 index 000000000..b88712528 --- /dev/null +++ b/src/xrGameLA/agent_corpse_manager_inline.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_corpse_manager_inline.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent corpse manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CAgentCorpseManager::CAgentCorpseManager (CAgentManager *object) +{ + VERIFY (object); + m_object = object; +} + +IC CAgentManager &CAgentCorpseManager::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +IC void CAgentCorpseManager::register_corpse (CAI_Stalker *corpse) +{ + MEMBER_CORPSES::iterator I = std::find(m_corpses.begin(),m_corpses.end(),corpse); + VERIFY2 (I == m_corpses.end(),"Cannot register corpse more than a time!"); + m_corpses.push_back (CMemberCorpse(corpse,0,Device.dwTimeGlobal)); +} + +IC CAgentCorpseManager::MEMBER_CORPSES &CAgentCorpseManager::corpses () +{ + return (m_corpses); +} + +IC void CAgentCorpseManager::clear () +{ + m_corpses.clear (); +} diff --git a/src/xrGameLA/agent_enemy_manager.cpp b/src/xrGameLA/agent_enemy_manager.cpp new file mode 100644 index 000000000..94e6597e9 --- /dev/null +++ b/src/xrGameLA/agent_enemy_manager.cpp @@ -0,0 +1,721 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_enemy_manager.cpp +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent enemy manager +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "agent_enemy_manager.h" +#include "agent_manager.h" +#include "agent_memory_manager.h" +#include "agent_member_manager.h" +#include "ai_space.h" +#include "ef_storage.h" +#include "ef_pattern.h" +#include "member_order.h" +#include "ai/stalker/ai_stalker.h" + +#include "memory_manager.h" +#include "visual_memory_manager.h" +#include "sound_memory_manager.h" +#include "hit_memory_manager.h" +#include "enemy_manager.h" +#include "memory_space_impl.h" + +#pragma warning(push) +#pragma warning(disable:4995) +#include +#pragma warning(pop) + +const float wounded_enemy_reached_distance = 3.f; + +const unsigned __int32 __c0 = 0x55555555; +const unsigned __int32 __c1 = 0x33333333; +const unsigned __int32 __c2 = 0x0f0f0f0f; +const unsigned __int32 __c3 = 0x00ff00ff; +const unsigned __int32 __c4 = 0x0000003f; + +IC u32 population(const u32 &b) { + u32 a = b; + a = (a & __c0) + ((a >> 1) & __c0); + a = (a & __c1) + ((a >> 2) & __c1); + a = (a + (a >> 4)) & __c2; + a = (a + (a >> 8)) & __c3; + a = (a + (a >> 16)) & __c4; + return (a); +} + +IC u32 population(const u64 &b) { + return ( population( (u32)b ) + population(u32(b >> 32)) ); +} + +struct CEnemyFiller { + typedef CAgentEnemyManager::ENEMIES ENEMIES; + ENEMIES *m_enemies; + squad_mask_type m_mask; + + IC CEnemyFiller (ENEMIES *enemies, squad_mask_type mask) + { + m_enemies = enemies; + m_mask = mask; + } + + IC void operator() (const CEntityAlive *enemy) const + { + ENEMIES::iterator I = std::find(m_enemies->begin(),m_enemies->end(),enemy); + if (I == m_enemies->end()) { + m_enemies->push_back (CMemberEnemy(enemy,m_mask)); + return; + } + + (*I).m_mask.set (m_mask,TRUE); + } +}; + +struct remove_wounded_predicate { + IC bool operator() (const CMemberEnemy &enemy) const + { + const CAI_Stalker *stalker = smart_cast(enemy.m_object); + if (!stalker) + return (false); + + if (!stalker->wounded()) + return (false); + + return (true); + } +}; + +void CAgentEnemyManager::fill_enemies () +{ + m_enemies.clear (); + + { + CAgentMemberManager::iterator I = object().member().combat_members().begin(); + CAgentMemberManager::iterator E = object().member().combat_members().end(); + for ( ; I != E; ++I) { + (*I)->probability (1.f); + (*I)->object().memory().fill_enemies (CEnemyFiller(&m_enemies,object().member().mask(&(*I)->object()))); + } + } + + if (m_enemies.empty()) + return; + + VERIFY (!m_enemies.empty()); + + { + for (int i=0, n=(int)m_wounded.size(); i((*I).m_object); + if (!stalker || !stalker->wounded()) + m_only_wounded_left = false; + else + m_is_any_wounded = true; + } + else { + if (!m_is_any_wounded) { + const CAI_Stalker *stalker = smart_cast((*I).m_object); + if (stalker && stalker->wounded()) + m_is_any_wounded = true; + } + } + + memory.object_information ((*I).m_object,(*I).m_level_time,(*I).m_enemy_position); + } + } + + if (!m_only_wounded_left && m_is_any_wounded) { + enemies().erase ( + std::remove_if( + enemies().begin(), + enemies().end(), + remove_wounded_predicate() + ), + enemies().end() + ); + } + + VERIFY (!m_enemies.empty()); +} + +float CAgentEnemyManager::evaluate (const CEntityAlive *object0, const CEntityAlive *object1) const +{ + ai().ef_storage().non_alife().member_item() = 0; + ai().ef_storage().non_alife().enemy_item() = 0; + ai().ef_storage().non_alife().member() = object0; + ai().ef_storage().non_alife().enemy() = object1; + return (ai().ef_storage().m_pfVictoryProbability->ffGetValue()/100.f); +} + +void CAgentEnemyManager::exchange_enemies (CMemberOrder &member0, CMemberOrder &member1) +{ + u32 enemy0 = member0.selected_enemy(); + u32 enemy1 = member1.selected_enemy(); + squad_mask_type mask0 = object().member().mask(&member0.object()); + squad_mask_type mask1 = object().member().mask(&member1.object()); + m_enemies[enemy0].m_distribute_mask.set(mask0,FALSE); + m_enemies[enemy1].m_distribute_mask.set(mask1,FALSE); + m_enemies[enemy0].m_distribute_mask.set(mask1,TRUE); + m_enemies[enemy1].m_distribute_mask.set(mask0,TRUE); + member0.selected_enemy (enemy1); + member1.selected_enemy (enemy0); +} + +void CAgentEnemyManager::compute_enemy_danger () +{ + ENEMIES::iterator I = m_enemies.begin(); + ENEMIES::iterator E = m_enemies.end(); + for ( ; I != E; ++I) { + float best = -1.f; + CAgentMemberManager::const_iterator i = object().member().combat_members().begin(); + CAgentMemberManager::const_iterator e = object().member().combat_members().end(); + for ( ; i != e; ++i) { + float value = evaluate((*I).m_object,&(*i)->object()); + if (value > best) + best = value; + } + (*I).m_probability = best; + } + + std::sort (m_enemies.begin(),m_enemies.end()); +} + +void CAgentEnemyManager::assign_enemies () +{ + for (;;) { + squad_mask_type J, K, N = 0; + float best = flt_max; + + ENEMIES::iterator I = m_enemies.begin(); + ENEMIES::iterator E = m_enemies.end(); + for ( ; I != E; ++I) { + J = (*I).m_mask.get(); + N = 0; + best = -1.f; + for ( ; J; J &= J - 1) { + K = (J & (J - 1)) ^ J; + CAgentMemberManager::iterator i = object().member().member(K); + if (!fsimilar((*i)->probability(),1.f)) + continue; + + float value = evaluate(&(*i)->object(),(*I).m_object); + if (value > best) { + best = value; + N = K; + } + } + if (N) + break; + } + if (!N) + break; + + (*I).m_distribute_mask.set (N,TRUE); + CAgentMemberManager::iterator i = object().member().member(N); + (*i)->probability (best); + (*I).m_probability *= 1.f - best; + + // recovering sort order + for (u32 i=0, n = m_enemies.size() - 1; ienemies().clear (); + // setup procesed flag + (*I)->processed (false); + // get member squad mask + squad_mask_type member_mask = object().member().mask(&(*I)->object()); + // setup if player has enemy + bool enemy_selected = false; + // iterate on enemies + ENEMIES::const_iterator i = m_enemies.begin(), b = i; + ENEMIES::const_iterator e = m_enemies.end(); + for ( ; i != e; ++i) { + if ((*i).m_mask.is(member_mask)) + (*I)->enemies().push_back (u32(i - b)); + + if ((*i).m_distribute_mask.is(member_mask)) { + (*I)->selected_enemy (u32(i - b)); + enemy_selected = true; + } + } + // if there is enemy - all is ok + if (enemy_selected) + continue; + + // otherwise temporary make the member processed + (*I)->processed (true); + } + + // perform permutations + bool changed; + do { + changed = false; + CAgentMemberManager::iterator I = object().member().combat_members().begin(); + CAgentMemberManager::iterator E = object().member().combat_members().end(); + for ( ; I != E; ++I) { + // if member is processed the continue; + if ((*I)->processed()) + continue; + + float best = (*I)->object().Position().distance_to(m_enemies[(*I)->selected_enemy()].m_object->Position()); + bool found = false; + xr_vector::const_iterator i = (*I)->enemies().begin(); + xr_vector::const_iterator e = (*I)->enemies().end(); + for ( ; i != e; ++i) { + if ((*I)->selected_enemy() == *i) + continue; + float my_distance = (*I)->object().Position().distance_to(m_enemies[*i].m_object->Position()); + if (my_distance < best) { + // check if we can exchange enemies + squad_mask_type J = m_enemies[*i].m_distribute_mask.get(), K; + // iterating on members, whose current enemy is the new one + for ( ; J; J &= J - 1) { + K = (J & (J - 1)) ^ J; + CAgentMemberManager::iterator j = object().member().member(K); + xr_vector::iterator ii = std::find((*j)->enemies().begin(),(*j)->enemies().end(),(*I)->selected_enemy()); + // check if member can my current enemy + if (ii == (*j)->enemies().end()) + continue; + + // check if I'm closer to the enemy + float member_distance = (*j)->object().Position().distance_to(m_enemies[*i].m_object->Position()); + if (member_distance <= my_distance) + continue; + + // check if our effectiveness is near the same + float my_to_his = evaluate(&(*I)->object(),m_enemies[(*j)->selected_enemy()].m_object); + float his_to_my = evaluate(&(*j)->object(),m_enemies[(*I)->selected_enemy()].m_object); + if (!fsimilar(my_to_his,(*j)->probability()) || !fsimilar(his_to_my,(*I)->probability())) + continue; + + exchange_enemies (**I,**j); + + found = true; + best = my_distance; + break; + } + } + + if (found) + break; + } + + if (!found) { + (*I)->processed (true); + continue; + } + + changed = true; + } + } + while (changed); + + VERIFY (!m_enemies.empty()); + if (!m_only_wounded_left) { + CAgentMemberManager::iterator I = object().member().combat_members().begin(); + CAgentMemberManager::iterator E = object().member().combat_members().end(); + for ( ; I != E; ++I) { + ENEMIES::iterator i = m_enemies.begin(); + ENEMIES::iterator e = m_enemies.end(); + for ( ; i != e; ++i) + if ((*I)->object().memory().visual().visible_now((*i).m_object)) + (*i).m_distribute_mask.assign((*i).m_distribute_mask.get() | object().member().mask(&(*I)->object())); + } + } +} + +template +IC void CAgentEnemyManager::setup_mask (xr_vector &objects, CMemberEnemy &enemy, const squad_mask_type &non_combat_members) +{ + xr_vector::iterator I = std::find(objects.begin(),objects.end(),enemy.m_object->ID()); + if (I != objects.end()) { + (*I).m_squad_mask.assign ( + (*I).m_squad_mask.get() | + enemy.m_distribute_mask.get() + ); + } +} + +IC void CAgentEnemyManager::setup_mask (CMemberEnemy &enemy, const squad_mask_type &non_combat_members) +{ + setup_mask (object().memory().visibles(),enemy,non_combat_members); + setup_mask (object().memory().sounds(),enemy,non_combat_members); + setup_mask (object().memory().hits(),enemy,non_combat_members); +} + +void CAgentEnemyManager::assign_enemy_masks () +{ + { + ENEMIES::iterator I = m_enemies.begin(); + ENEMIES::iterator E = m_enemies.end(); + for ( ; I != E; ++I) { + CAgentMemberManager::MEMBER_STORAGE::const_iterator i = object().member().combat_members().begin(); + CAgentMemberManager::MEMBER_STORAGE::const_iterator e = object().member().combat_members().end(); + for ( ; i != e; ++i) + (*i)->object().memory().make_object_visible_somewhen((*I).m_object); + } + } + + squad_mask_type non_combat_members = object().member().non_combat_members_mask(); + + ENEMIES::iterator I = m_enemies.begin(); + ENEMIES::iterator E = m_enemies.end(); + for ( ; I != E; ++I) + setup_mask (*I,non_combat_members); +} + +void CAgentEnemyManager::assign_wounded () +{ + VERIFY (m_only_wounded_left); + +#if 0//def DEBUG + u32 enemy_mask = 0; + ENEMIES::iterator I = m_enemies.begin(); + ENEMIES::iterator E = m_enemies.end(); + for ( ; I != E; ++I) { + VERIFY (!(*I).m_distribute_mask.get()); + enemy_mask |= (*I).m_mask.get(); + } + VERIFY (enemy_mask == object().member().combat_mask()); +#endif // DEBUG + + u32 previous_wounded_count = m_wounded.size(); + WOUNDED_ENEMY *previous_wounded = (WOUNDED_ENEMY*)_alloca(previous_wounded_count*sizeof(WOUNDED_ENEMY)); + std::copy (m_wounded.begin(),m_wounded.end(),previous_wounded); + m_wounded.clear (); + +#ifdef DEBUG + { + ENEMIES::iterator I = m_enemies.begin(); + ENEMIES::iterator E = m_enemies.end(); + for ( ; I != E; ++I) { + VERIFY (!(*I).m_distribute_mask.get()); + VERIFY ((*I).m_mask.get()); + } + } +#endif // DEBUG + + squad_mask_type assigned = 0; + { + WOUNDED_ENEMY *I = previous_wounded; + WOUNDED_ENEMY *E = previous_wounded + previous_wounded_count; + for ( ; I != E; ++I) { + ENEMIES::iterator J = std::find(m_enemies.begin(),m_enemies.end(),(*I).first); + if (J == m_enemies.end()) + continue; + + CMemberOrder *member_order = object().member().get_member((*I).second.first); + if (!member_order) + continue; + + squad_mask_type mask = object().member().mask((*I).second.first); + if (!(object().member().combat_mask() & mask)) + continue; + + CAgentMemberManager::iterator i = object().member().member(mask); + if ((*I).first->Position().distance_to_sqr((*i)->object().Position()) > _sqr(wounded_enemy_reached_distance)) + continue; + + if (wounded_processor((*J).m_object) != ALife::_OBJECT_ID(-1)) + continue; + + wounded_processor ((*J).m_object,(*I).second.first); + (*J).m_distribute_mask.set (mask,TRUE); + VERIFY ((assigned | mask) != assigned); + assigned |= mask; + } + } + + u32 combat_member_count = population(object().member().combat_mask()); + VERIFY (combat_member_count == object().member().combat_members().size()); + + u32 population_level = 0; + while (population(assigned) < combat_member_count) { + CMemberEnemy *enemy = 0; + const CAI_Stalker *processor = 0; + float best_distance_sqr = flt_max; + + for (int i=0; i<2; ++i) { + ENEMIES::iterator I = m_enemies.begin(); + ENEMIES::iterator E = m_enemies.end(); + for ( ; I != E; ++I) { + if (population((*I).m_distribute_mask.get()) > population_level) + continue; + + squad_mask_type J = (*I).m_mask.get(); + J &= (assigned ^ squad_mask_type(-1)); + for ( ; J; J &= J - 1) { + squad_mask_type K = (J & (J - 1)) ^ J; + CAgentMemberManager::iterator i = object().member().member(K); + float distance_sqr = (*i)->object().Position().distance_to_sqr((*I).m_object->Position()); + if (distance_sqr < best_distance_sqr) { + best_distance_sqr = distance_sqr; + enemy = &*I; + processor = &(*i)->object(); + } + } + } + + if (enemy) + break; + + ++population_level; + } + +#ifdef DEBUG + if (!enemy) { + Msg (" "); + Msg (" "); + Msg ("error will occur now, dumping valuable info"); + Msg ("wounded enemies(%d):",m_enemies.size()); + { + typedef ENEMIES::iterator iterator; + iterator I = m_enemies.begin(); + iterator E = m_enemies.end(); + for ( ; I != E; ++I) + Msg ( + " [%s][0x%08x][0x%08x][%.2f]", + *(*I).m_object->cName(), + (*I).m_mask.get(), + (*I).m_distribute_mask.get(), + (*I).m_probability + ); + } + Msg ("combat members(%d):",object().member().combat_members().size()); + { + typedef CAgentMemberManager::MEMBER_STORAGE::const_iterator const_iterator; + const_iterator I = object().member().combat_members().begin(); + const_iterator E = object().member().combat_members().end(); + for ( ; I != E; ++I) + Msg ( + " [%s][0x%08x][0x%08x]", + *(*I)->object().cName(), + object().member().mask(&(*I)->object()), + (*I)->selected_enemy() + ); + } + } +#endif + +// VERIFY (enemy); +// VERIFY (processor); + + // this situation is possible + // for example + // 2 soldiers in group + // has 2 different enemies + // one of the enemy is going offline + // soldier, whose enemy went offline + // nulls the selected enemy + // agent manager updates before + // soldier update and soldier knows nothing about the second enemy + // we have situation where agent_manager + // is trying to assign wounded for the soldier + // who doesn't have enemies at the moment + // since the last enemy went offline and he knows nothing about the second one + // so, in this case we just need to stop iterating + // since nest procedure (setup_enemy_masks) + // will make the second enemy known for the soldier + // and on its update he will select it + if (!enemy) + return; + +// Msg ("wounded enemy [%s] is assigned to member [%s]",*enemy->m_object->cName(),*processor->cName()); + + if (wounded_processor(enemy->m_object) == ALife::_OBJECT_ID(-1)) + wounded_processor (enemy->m_object,processor->ID()); + + squad_mask_type mask = object().member().mask(processor); + enemy->m_distribute_mask.set(mask,TRUE); + VERIFY ((assigned | mask) != assigned); + assigned |= mask; + } + +// Msg ("[%6d] assigned = %x",Device.dwTimeGlobal,assigned); +// ENEMIES::iterator I = m_enemies.begin(); +// ENEMIES::iterator E = m_enemies.end(); +// for ( ; I != E; ++I) +// Msg ("[%6d] [%s] = %x",Device.dwTimeGlobal,*(*I).m_object->cName(),(*I).m_distribute_mask.get()); +} + +void CAgentEnemyManager::distribute_enemies () +{ + if (!object().member().combat_mask()) + return; + + fill_enemies (); + + if (m_enemies.empty()) + return; + + if (m_only_wounded_left) + assign_wounded (); + else { + compute_enemy_danger (); + assign_enemies (); + permutate_enemies (); + } + + assign_enemy_masks (); +} + +struct wounded_predicate { + CObject *m_object; + + IC wounded_predicate (CObject *object) + { + VERIFY (object); + m_object = object; + } + + IC bool operator() (const CAgentEnemyManager::WOUNDED_ENEMY &wounded_enemy) const + { + if (wounded_enemy.first == m_object) + return (true); + + if (wounded_enemy.second.first == m_object->ID()) + return (true); + + return (false); + } +}; + +void CAgentEnemyManager::remove_links (CObject *object) +{ + m_wounded.erase ( + std::remove_if( + m_wounded.begin(), + m_wounded.end(), + wounded_predicate(object) + ), + m_wounded.end() + ); +} + +void CAgentEnemyManager::update () +{ +} + +ALife::_OBJECT_ID CAgentEnemyManager::wounded_processor (const CEntityAlive *object) +{ + WOUNDED_ENEMIES::const_iterator I = m_wounded.begin(); + WOUNDED_ENEMIES::const_iterator E = m_wounded.end(); + for ( ; I != E; ++I) { + if ((*I).first == object) + return ((*I).second.first); + } + + return (ALife::_OBJECT_ID(-1)); +} + +class find_wounded_predicate { +private: + const CEntityAlive *m_object; + +public: + IC find_wounded_predicate (const CEntityAlive *object) + { + m_object = object; + VERIFY (m_object); + } + + IC bool operator() (const CAgentEnemyManager::WOUNDED_ENEMY &enemy) const + { + return (enemy.first == m_object); + } +}; + +void CAgentEnemyManager::wounded_processor (const CEntityAlive *object, const ALife::_OBJECT_ID &wounded_processor_id) +{ + VERIFY ( + std::find_if( + m_wounded.begin(), + m_wounded.end(), + find_wounded_predicate(object) + ) == + m_wounded.end() + ); + m_wounded.push_back (std::make_pair(object,std::make_pair(wounded_processor_id,false))); +} + +void CAgentEnemyManager::wounded_processed (const CEntityAlive *object, bool value) +{ + VERIFY (value); + WOUNDED_ENEMIES::iterator I = std::find_if(m_wounded.begin(),m_wounded.end(),find_wounded_predicate(object)); + if (I == m_wounded.end()) + return; + VERIFY ((*I).second.first != ALife::_OBJECT_ID(-1)); + VERIFY (!(*I).second.second); + (*I).second.second = true; +} + +bool CAgentEnemyManager::wounded_processed (const CEntityAlive *object) const +{ + WOUNDED_ENEMIES::const_iterator I = std::find_if(m_wounded.begin(),m_wounded.end(),find_wounded_predicate(object)); + if (I == m_wounded.end()) + return (false); + return ((*I).second.second); +} + +bool CAgentEnemyManager::assigned_wounded (const CEntityAlive *wounded, const CAI_Stalker *member) +{ + ENEMIES::const_iterator I = m_enemies.begin(); + ENEMIES::const_iterator E = m_enemies.end(); + for ( ; I != E; ++I) { + if ((*I).m_object != wounded) + continue; + + return ( + !!(*I).m_distribute_mask.test( + object().member().mask(member) + ) + ); + } + + return (false); +} + +bool CAgentEnemyManager::useful_enemy (const CEntityAlive *enemy, const CAI_Stalker *member) const +{ + if (!object().member().registered_in_combat(member)) + return (true); + + ENEMIES::const_iterator I = std::find(m_enemies.begin(),m_enemies.end(),enemy); + if (I == m_enemies.end()) + return (true); + + return (!!(*I).m_distribute_mask.test(object().member().mask(member))); +} diff --git a/src/xrGameLA/agent_enemy_manager.h b/src/xrGameLA/agent_enemy_manager.h new file mode 100644 index 000000000..40b710703 --- /dev/null +++ b/src/xrGameLA/agent_enemy_manager.h @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_enemy_manager.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent enemy manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "member_enemy.h" + +class CAgentManager; +class CMemberOrder; +class CEntityAlive; +class CAI_Stalker; + +class CAgentEnemyManager { +public: + typedef xr_vector ENEMIES; + typedef MemorySpace::squad_mask_type squad_mask_type; + typedef std::pair WOUNDED; + typedef std::pair WOUNDED_ENEMY; + typedef xr_vector WOUNDED_ENEMIES; + +private: + CAgentManager *m_object; + ENEMIES m_enemies; + WOUNDED_ENEMIES m_wounded; + bool m_only_wounded_left; + bool m_is_any_wounded; + +protected: + template + IC void setup_mask (xr_vector &objects, CMemberEnemy &enemy, const squad_mask_type &non_combat_members); + IC void setup_mask (CMemberEnemy &enemy, const squad_mask_type &non_combat_members); + void fill_enemies (); + void compute_enemy_danger(); + void assign_enemies (); + void permutate_enemies (); + void assign_wounded (); + void assign_enemy_masks (); + float evaluate (const CEntityAlive *object0, const CEntityAlive *object1) const; + void exchange_enemies (CMemberOrder &member0, CMemberOrder &member1); + IC CAgentManager &object () const; + +public: + IC CAgentEnemyManager (CAgentManager *object); + void update (); + void distribute_enemies (); + IC ENEMIES &enemies (); + void remove_links (CObject *object); + +private: + void wounded_processor (const CEntityAlive *object, const ALife::_OBJECT_ID &wounded_processor_id); + +public: + ALife::_OBJECT_ID wounded_processor (const CEntityAlive *object); + void wounded_processed (const CEntityAlive *object, bool value); + bool wounded_processed (const CEntityAlive *object) const; + bool assigned_wounded (const CEntityAlive *wounded, const CAI_Stalker *member); + bool useful_enemy (const CEntityAlive *enemy, const CAI_Stalker *member) const; +}; + +#include "agent_enemy_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/agent_enemy_manager_inline.h b/src/xrGameLA/agent_enemy_manager_inline.h new file mode 100644 index 000000000..e6ccdb265 --- /dev/null +++ b/src/xrGameLA/agent_enemy_manager_inline.h @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_enemy_manager_inline.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent enemy manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CAgentEnemyManager::CAgentEnemyManager (CAgentManager *object) +{ + VERIFY (object); + m_object = object; + m_only_wounded_left = false; + m_is_any_wounded = false; +} + +IC CAgentManager &CAgentEnemyManager::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +IC CAgentEnemyManager::ENEMIES &CAgentEnemyManager::enemies () +{ + return (m_enemies); +} diff --git a/src/xrGameLA/agent_explosive_manager.cpp b/src/xrGameLA/agent_explosive_manager.cpp new file mode 100644 index 000000000..6e22cf2c8 --- /dev/null +++ b/src/xrGameLA/agent_explosive_manager.cpp @@ -0,0 +1,137 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_explosive_manager.cpp +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent explosive manager +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "agent_explosive_manager.h" +#include "agent_manager.h" +#include "agent_location_manager.h" +#include "agent_member_manager.h" +#include "missile.h" +#include "explosive.h" +#include "member_order.h" +#include "ai/stalker/ai_stalker.h" +#include "memory_manager.h" +#include "visual_memory_manager.h" +#include "danger_object_location.h" + +const float GRENADE_RADIUS = 10.f; +const u32 AFTER_GRENADE_DESTROYED_INTERVAL = 1000; + +struct CRemoveExplosivesPredicate { + IC bool operator() (CDangerExplosive &explosive) const + { + return (!!explosive.m_reactor); + } +}; + +void CAgentExplosiveManager::remove_links (CObject *object) +{ + TO_BE_DESTROYED::iterator I = std::find(m_explosives_to_remove.begin(),m_explosives_to_remove.end(),object->ID()); + if (I != m_explosives_to_remove.end()) + m_explosives_to_remove.erase(I); + + EXPLOSIVES::iterator J = std::find(m_explosives.begin(),m_explosives.end(),object->ID()); + if (J != m_explosives.end()) + m_explosives.erase (J); +} + +void CAgentExplosiveManager::register_explosive (const CExplosive *explosive, const CGameObject *game_object) +{ + { + xr_vector::iterator I = std::find(m_explosives.begin(),m_explosives.end(),explosive); + if (I != m_explosives.end()) + return; + } + { + TO_BE_DESTROYED::iterator I = std::find(m_explosives_to_remove.begin(),m_explosives_to_remove.end(),game_object->ID()); + if (I != m_explosives_to_remove.end()) + return; + } + + m_explosives_to_remove.push_back (game_object->ID()); + m_explosives.push_back (CDangerExplosive(explosive,game_object,0,Device.dwTimeGlobal)); + + u32 interval = AFTER_GRENADE_DESTROYED_INTERVAL; + const CMissile *missile = smart_cast(explosive); + if (missile && (missile->destroy_time() > Device.dwTimeGlobal)) + interval = missile->destroy_time() - Device.dwTimeGlobal + AFTER_GRENADE_DESTROYED_INTERVAL; + + object().location().add (new CDangerObjectLocation(game_object,Device.dwTimeGlobal,interval,GRENADE_RADIUS)); +} + +bool CAgentExplosiveManager::process_explosive (CMemberOrder &member) +{ + float min_dist_sqr = flt_max; + CDangerExplosive *best_grenade = 0; + xr_vector::iterator I = m_explosives.begin(); + xr_vector::iterator E = m_explosives.end(); + for ( ; I != E; ++I) { + if (!member.object().memory().visual().visible_now((*I).m_game_object)) + continue; + + float dist_sqr = (*I).m_game_object->Position().distance_to_sqr(member.object().Position()); + if (dist_sqr < min_dist_sqr) { + if ( + (*I).m_reactor && + ((*I).m_reactor->Position().distance_to_sqr((*I).m_game_object->Position()) <= min_dist_sqr) + ) + continue; + min_dist_sqr = dist_sqr; + best_grenade = &*I; + } + } + + if (!best_grenade) + return (false); + + best_grenade->m_reactor = &member.object(); + return (true); +} + +void CAgentExplosiveManager::react_on_explosives () +{ + for (;;) { + bool changed = false; + CAgentMemberManager::iterator I = object().member().combat_members().begin(); + CAgentMemberManager::iterator E = object().member().combat_members().end(); + for ( ; I != E; ++I) + if (!(*I)->grenade_reaction().m_processing) + changed = process_explosive(**I); + + if (!changed) + break; + } + + { + EXPLOSIVES::iterator I = m_explosives.begin(); + EXPLOSIVES::iterator E = m_explosives.end(); + for ( ; I != E; ++I) { + if (!(*I).m_reactor) + continue; + + CMemberOrder::CGrenadeReaction &reaction = object().member().member((*I).m_reactor).grenade_reaction(); + reaction.m_grenade = (*I).m_grenade; + reaction.m_game_object = (*I).m_game_object; + reaction.m_time = (*I).m_time; + reaction.m_processing = true; + } + + m_explosives.erase ( + std::remove_if( + m_explosives.begin(), + m_explosives.end(), + CRemoveExplosivesPredicate() + ), + m_explosives.end() + ); + } +} + +void CAgentExplosiveManager::update () +{ +} diff --git a/src/xrGameLA/agent_explosive_manager.h b/src/xrGameLA/agent_explosive_manager.h new file mode 100644 index 000000000..feab248c5 --- /dev/null +++ b/src/xrGameLA/agent_explosive_manager.h @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_explosive_manager.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent explosive manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "danger_explosive.h" + +class CObject; +class CAgentManager; +class CMemberOrder; + +class CAgentExplosiveManager { +public: + typedef xr_vector EXPLOSIVES; + typedef xr_vector TO_BE_DESTROYED; + +private: + CAgentManager *m_object; + EXPLOSIVES m_explosives; + TO_BE_DESTROYED m_explosives_to_remove; + +protected: + IC CAgentManager &object () const; + IC EXPLOSIVES &explosives (); + bool process_explosive (CMemberOrder &member); + +public: + IC CAgentExplosiveManager (CAgentManager *object); + void remove_links (CObject *object); + void register_explosive (const CExplosive *explosive, const CGameObject *game_object); + void react_on_explosives (); + void update (); +}; + +#include "agent_explosive_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/agent_explosive_manager_inline.h b/src/xrGameLA/agent_explosive_manager_inline.h new file mode 100644 index 000000000..a77dbdd3c --- /dev/null +++ b/src/xrGameLA/agent_explosive_manager_inline.h @@ -0,0 +1,26 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_explosive_manager_inline.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent explosive manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CAgentExplosiveManager::CAgentExplosiveManager (CAgentManager *object) +{ + VERIFY (object); + m_object = object; +} + +IC CAgentManager &CAgentExplosiveManager::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +IC CAgentExplosiveManager::EXPLOSIVES &CAgentExplosiveManager::explosives () +{ + return (m_explosives); +} diff --git a/src/xrGameLA/agent_location_manager.cpp b/src/xrGameLA/agent_location_manager.cpp new file mode 100644 index 000000000..ae0d8f85c --- /dev/null +++ b/src/xrGameLA/agent_location_manager.cpp @@ -0,0 +1,208 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_location_manager.cpp +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent location manager +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "agent_location_manager.h" +#include "agent_manager.h" +#include "agent_member_manager.h" +#include "agent_enemy_manager.h" +#include "ai/stalker/ai_stalker.h" +#include "cover_point.h" + +const float MIN_SUITABLE_ENEMY_DISTANCE = 3.f;//10.f; + +struct CRemoveOldDangerCover { + typedef CAgentMemberManager::MEMBER_STORAGE MEMBER_STORAGE; + + CAgentMemberManager *m_members; + + IC CRemoveOldDangerCover (CAgentMemberManager *members) + { + VERIFY (members); + m_members = members; + } + + IC bool operator() (const CAgentLocationManager::CDangerLocationPtr &location) const + { + if (!location->useful()) { + MEMBER_STORAGE::iterator I = m_members->members().begin(); + MEMBER_STORAGE::iterator E = m_members->members().end(); + for ( ; I != E; ++I) { + if (!location->mask().test(m_members->mask(&(*I)->object()))) + continue; + + (*I)->object().on_danger_location_remove (*location); + } + } + + return (!location->useful()); + } +}; + +struct CDangerLocationPredicate { + Fvector m_position; + + IC CDangerLocationPredicate (const Fvector &position) + { + m_position = position; + } + + IC bool operator() (const CAgentLocationManager::CDangerLocationPtr &location) const + { + return (*location == m_position); + } +}; + +IC CAgentLocationManager::CDangerLocationPtr CAgentLocationManager::location (const Fvector &position) +{ + LOCATIONS::iterator I = std::find_if(m_danger_locations.begin(),m_danger_locations.end(),CDangerLocationPredicate(position)); + if (I != m_danger_locations.end()) + return (*I); + return (0); +} + +bool CAgentLocationManager::suitable (CAI_Stalker *object, const CCoverPoint *location, bool use_enemy_info) const +{ + CAgentMemberManager::const_iterator I = this->object().member().members().begin(); + CAgentMemberManager::const_iterator E = this->object().member().members().end(); + for ( ; I != E; ++I) { + if ((*I)->object().ID() == object->ID()) + continue; + +// if ((*I)->object().Position().distance_to_sqr(location->position()) <= _sqr(5.f)) +// return (false); + + if (!(*I)->cover()) + continue; + + // check if member cover is too close + if ((*I)->cover()->m_position.distance_to_sqr(location->position()) <= _sqr(5.f)) + // so member cover is too close +// if ((*I)->object().Position().distance_to_sqr(location->position()) <= object->Position().distance_to_sqr(location->position())) + // check if member to its cover is more close than we to our cover + if ((*I)->object().Position().distance_to_sqr((*I)->cover()->m_position) <= object->Position().distance_to_sqr(location->position()) + 2.f) + return (false); + } + + if (use_enemy_info) { + CAgentEnemyManager::ENEMIES::const_iterator I = this->object().enemy().enemies().begin(); + CAgentEnemyManager::ENEMIES::const_iterator E = this->object().enemy().enemies().end(); + for ( ; I != E; ++I) + if ((*I).m_enemy_position.distance_to_sqr(location->position()) < _sqr(MIN_SUITABLE_ENEMY_DISTANCE)) + return (false); + } + + return (true); +} + +void CAgentLocationManager::make_suitable (CAI_Stalker *object, const CCoverPoint *location) const +{ + this->object().member().member(object).cover(location); + + if (!location) + return; + + CAgentMemberManager::const_iterator I = this->object().member().members().begin(); + CAgentMemberManager::const_iterator E = this->object().member().members().end(); + for ( ; I != E; ++I) { + if ((*I)->object().ID() == object->ID()) + continue; + + if (!(*I)->cover()) + continue; + + // check if member cover is too close + if ((*I)->cover()->m_position.distance_to_sqr(location->position()) <= _sqr(5.f)) { +// Msg ("%6d : object [%s] disabled cover for object [%s]",Device.dwFrame,*object->cName(),*(*I)->object().cName()); + (*I)->object().on_cover_blocked ((*I)->cover()); + (*I)->cover (0); + } + } +} + +void CAgentLocationManager::add (CDangerLocationPtr location) +{ + typedef CAgentMemberManager::MEMBER_STORAGE MEMBER_STORAGE; + MEMBER_STORAGE::iterator I = object().member().members().begin(); + MEMBER_STORAGE::iterator E = object().member().members().end(); + for ( ; I != E; ++I) { + if (!location->mask().test(object().member().mask(&(*I)->object()))) + continue; + + (*I)->object().on_danger_location_add (*location); + } + + CDangerLocationPtr danger = this->location(location->position()); + if (!danger) { + m_danger_locations.push_back(location); + return; + } + + danger->m_level_time = location->m_level_time; + + if (danger->m_interval < location->m_interval) + danger->m_interval = location->m_interval; + + if (danger->m_radius < location->m_radius) + danger->m_radius = location->m_radius; +} + +void CAgentLocationManager::remove_old_danger_covers () +{ + m_danger_locations.erase ( + std::remove_if( + m_danger_locations.begin(), + m_danger_locations.end(), + CRemoveOldDangerCover( + &object().member() + ) + ), + m_danger_locations.end() + ); +} + +float CAgentLocationManager::danger (const CCoverPoint *cover, CAI_Stalker *member) const +{ + float result = 1; + squad_mask_type mask = object().member().mask(member); + LOCATIONS::const_iterator I = m_danger_locations.begin(); + LOCATIONS::const_iterator E = m_danger_locations.end(); + for ( ; I != E; ++I) { + if (Device.dwTimeGlobal > (*I)->m_level_time + (*I)->m_interval) + continue; + + if (!(*I)->mask().test(mask)) + continue; + + float distance = 1.f + (*I)->position().distance_to(cover->position()); + if (distance > (*I)->m_radius) + continue; + + result *= + float(Device.dwTimeGlobal - (*I)->m_level_time)/float((*I)->m_interval); + } + + return (result); +} + +void CAgentLocationManager::update () +{ + remove_old_danger_covers (); +} + +void CAgentLocationManager::remove_links(CObject *object) +{ + m_danger_locations.erase ( + std::remove_if( + m_danger_locations.begin(), + m_danger_locations.end(), + CRemoveDangerObject(object) + ), + m_danger_locations.end() + ); +} diff --git a/src/xrGameLA/agent_location_manager.h b/src/xrGameLA/agent_location_manager.h new file mode 100644 index 000000000..c58003c3e --- /dev/null +++ b/src/xrGameLA/agent_location_manager.h @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_location_manager.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent location manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "danger_location.h" + +class CAI_Stalker; +class CCoverPoint; +class CAgentManager; +class CDangerLocation; +class CObject; + +class CAgentLocationManager { +public: + typedef intrusive_ptr CDangerLocationPtr; + typedef xr_vector LOCATIONS; + +private: + CAgentManager *m_object; + LOCATIONS m_danger_locations; + +protected: + IC CAgentManager &object () const; + void remove_old_danger_covers(); + + +public: + IC CAgentLocationManager (CAgentManager *object); + IC CDangerLocationPtr location (const Fvector &position); + IC CDangerLocationPtr location (const CObject *object); + IC void clear (); + void update (); + void add (CDangerLocationPtr location); + float danger (const CCoverPoint *cover, CAI_Stalker *member) const; + bool suitable (CAI_Stalker *object, const CCoverPoint *location, bool use_enemy_info) const; + void make_suitable (CAI_Stalker *object, const CCoverPoint *location) const; + void remove_links (CObject *object); + IC const LOCATIONS &locations () const; +}; + +#include "agent_location_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/agent_location_manager_inline.h b/src/xrGameLA/agent_location_manager_inline.h new file mode 100644 index 000000000..7111a06dd --- /dev/null +++ b/src/xrGameLA/agent_location_manager_inline.h @@ -0,0 +1,54 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_location_manager_inline.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent location manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +struct CRemoveDangerObject { + const CObject *m_object; + + IC CRemoveDangerObject (const CObject *object) + { + m_object = object; + } + + IC bool operator() (const CAgentLocationManager::CDangerLocationPtr &location) const + { + return (*location == m_object); + } +}; + +IC CAgentLocationManager::CAgentLocationManager(CAgentManager *object) +{ + VERIFY (object); + m_object = object; +} + +IC CAgentManager &CAgentLocationManager::object() const +{ + VERIFY (m_object); + return (*m_object); +} + +IC void CAgentLocationManager::clear () +{ + m_danger_locations.clear (); +} + +IC CAgentLocationManager::CDangerLocationPtr CAgentLocationManager::location (const CObject *object) +{ + LOCATIONS::iterator I = std::find_if(m_danger_locations.begin(),m_danger_locations.end(),CRemoveDangerObject(object)); + if (I != m_danger_locations.end()) + return (*I); + return (0); +} + +const CAgentLocationManager::LOCATIONS &CAgentLocationManager::locations () const +{ + return (m_danger_locations); +} + diff --git a/src/xrGameLA/agent_manager.cpp b/src/xrGameLA/agent_manager.cpp new file mode 100644 index 000000000..a47106461 --- /dev/null +++ b/src/xrGameLA/agent_manager.cpp @@ -0,0 +1,132 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager.cpp +// Created : 24.05.2004 +// Modified : 24.05.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "agent_manager.h" +#include "agent_corpse_manager.h" +#include "agent_enemy_manager.h" +#include "agent_explosive_manager.h" +#include "agent_location_manager.h" +#include "agent_member_manager.h" +#include "agent_memory_manager.h" +#include "agent_manager_planner.h" +#include "profiler.h" + +CAgentManager::CAgentManager () +{ + init_scheduler (); + init_components (); +} + +CAgentManager::~CAgentManager () +{ + VERIFY (member().members().empty()); +#ifdef USE_SCHEDULER_IN_AGENT_MANAGER + remove_scheduler (); +#endif // USE_SCHEDULER_IN_AGENT_MANAGER + remove_components (); +} + +void CAgentManager::init_scheduler () +{ +#ifdef USE_SCHEDULER_IN_AGENT_MANAGER + shedule.t_min = 1000; + shedule.t_max = 1000; + shedule_register (); +#else // USE_SCHEDULER_IN_AGENT_MANAGER + m_last_update_time = 0; + m_update_rate = 1000; +#endif // USE_SCHEDULER_IN_AGENT_MANAGER +} + +void CAgentManager::init_components () +{ + m_corpse = new CAgentCorpseManager(this); + m_enemy = new CAgentEnemyManager(this); + m_explosive = new CAgentExplosiveManager(this); + m_location = new CAgentLocationManager(this); + m_member = new CAgentMemberManager(this); + m_memory = new CAgentMemoryManager(this); + m_brain = new CAgentManagerPlanner(); + brain().setup (this); +} + +#ifdef USE_SCHEDULER_IN_AGENT_MANAGER +void CAgentManager::remove_scheduler () +{ + shedule_unregister (); +} +#endif // USE_SCHEDULER_IN_AGENT_MANAGER + +void CAgentManager::remove_components () +{ + xr_delete (m_corpse); + xr_delete (m_enemy); + xr_delete (m_explosive); + xr_delete (m_location); + xr_delete (m_member); + xr_delete (m_memory); + xr_delete (m_brain); +} + +void CAgentManager::remove_links (CObject *object) +{ + corpse().remove_links (object); + enemy().remove_links (object); + explosive().remove_links (object); + location().remove_links (object); + member().remove_links (object); + memory().remove_links (object); + brain().remove_links (object); +} + +void CAgentManager::update_impl () +{ + VERIFY (!member().members().empty()); + + memory().update (); + corpse().update (); + enemy().update (); + explosive().update (); + location().update (); + member().update (); + brain().update (); +} + +#ifdef USE_SCHEDULER_IN_AGENT_MANAGER +void CAgentManager::shedule_Update (u32 time_delta) +{ + START_PROFILE("Agent_Manager") + + ISheduled::shedule_Update (time_delta); + + update_impl (); + + STOP_PROFILE +} + +float CAgentManager::shedule_Scale () +{ + return (.5f); +} + +#else // USE_SCHEDULER_IN_AGENT_MANAGER + +void CAgentManager::update () +{ + if (Device.dwTimeGlobal <= m_last_update_time) + return; + + if (Device.dwTimeGlobal - m_last_update_time < m_update_rate) + return; + + m_last_update_time = Device.dwTimeGlobal; + update_impl (); +} + +#endif // USE_SCHEDULER_IN_AGENT_MANAGER \ No newline at end of file diff --git a/src/xrGameLA/agent_manager.h b/src/xrGameLA/agent_manager.h new file mode 100644 index 000000000..514a86b03 --- /dev/null +++ b/src/xrGameLA/agent_manager.h @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager.h +// Created : 24.05.2004 +// Modified : 24.05.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +class CAgentCorpseManager; +class CAgentEnemyManager; +class CAgentExplosiveManager; +class CAgentLocationManager; +class CAgentMemberManager; +class CAgentMemoryManager; +class CAgentManagerPlanner; + +//#define USE_SCHEDULER_IN_AGENT_MANAGER + +#ifdef USE_SCHEDULER_IN_AGENT_MANAGER + class CAgentManager : public ISheduled { +#else // USE_SCHEDULER_IN_AGENT_MANAGER + class CAgentManager { +#endif // USE_SCHEDULER_IN_AGENT_MANAGER + +private: + CAgentCorpseManager *m_corpse; + CAgentEnemyManager *m_enemy; + CAgentExplosiveManager *m_explosive; + CAgentLocationManager *m_location; + CAgentMemberManager *m_member; + CAgentMemoryManager *m_memory; + CAgentManagerPlanner *m_brain; + +#ifndef USE_SCHEDULER_IN_AGENT_MANAGER +private: + u32 m_last_update_time; + u32 m_update_rate; +#endif // USE_SCHEDULER_IN_AGENT_MANAGER + +private: + void init_scheduler (); + void init_components (); + void remove_components (); + void update_impl (); + +#ifdef USE_SCHEDULER_IN_AGENT_MANAGER +private: + void remove_scheduler (); +#endif // USE_SCHEDULER_IN_AGENT_MANAGER + +public: + CAgentManager (); + // final class, no virtual destructor needed + ~CAgentManager (); +#ifdef USE_SCHEDULER_IN_AGENT_MANAGER + virtual bool shedule_Needed () {return true;}; + virtual float shedule_Scale (); + virtual void shedule_Update (u32 time_delta); + virtual shared_str shedule_Name () const {return shared_str("agent_manager"); }; +#else // USE_SCHEDULER_IN_AGENT_MANAGER + void update (); +#endif // USE_SCHEDULER_IN_AGENT_MANAGER + shared_str cName () const; + void remove_links (CObject *object); + +public: + IC CAgentCorpseManager &corpse () const; + IC CAgentEnemyManager &enemy () const; + IC CAgentExplosiveManager &explosive () const; + IC CAgentLocationManager &location () const; + IC CAgentMemberManager &member () const; + IC CAgentMemoryManager &memory () const; + IC CAgentManagerPlanner &brain () const; +}; + +#include "agent_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/agent_manager_actions.cpp b/src/xrGameLA/agent_manager_actions.cpp new file mode 100644 index 000000000..b6c01e461 --- /dev/null +++ b/src/xrGameLA/agent_manager_actions.cpp @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager_actions.cpp +// Created : 25.05.2004 +// Modified : 25.05.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager actions +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "agent_manager_actions.h" +#include "agent_manager.h" +#include "agent_member_manager.h" +#include "agent_location_manager.h" +#include "agent_corpse_manager.h" +#include "agent_explosive_manager.h" +#include "agent_enemy_manager.h" +#include "ai/stalker/ai_stalker.h" +#include "sight_action.h" +#include "inventory.h" + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerActionNoOrders +////////////////////////////////////////////////////////////////////////// + +CAgentManagerActionNoOrders::CAgentManagerActionNoOrders (CAgentManager *object, LPCSTR action_name) : + inherited (object,action_name) +{ +} + +void CAgentManagerActionNoOrders::finalize () +{ + inherited::finalize (); + m_object->corpse().clear (); +} + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerActionGatherItems +////////////////////////////////////////////////////////////////////////// + +CAgentManagerActionGatherItems::CAgentManagerActionGatherItems (CAgentManager *object, LPCSTR action_name) : + inherited (object,action_name) +{ +} + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerActionKillEnemy +////////////////////////////////////////////////////////////////////////// + +CAgentManagerActionKillEnemy::CAgentManagerActionKillEnemy (CAgentManager *object, LPCSTR action_name) : + inherited (object,action_name) +{ +} + +void CAgentManagerActionKillEnemy::initialize () +{ + inherited::initialize (); + + m_object->location().clear (); +} + +void CAgentManagerActionKillEnemy::finalize () +{ + inherited::finalize (); + +// m_object->enemy().distribute_enemies (); +} + +void CAgentManagerActionKillEnemy::execute () +{ + inherited::execute (); + + m_object->enemy().distribute_enemies (); + m_object->explosive().react_on_explosives (); + m_object->corpse().react_on_member_death (); +} + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerActionReactOnDanger +////////////////////////////////////////////////////////////////////////// + +CAgentManagerActionReactOnDanger::CAgentManagerActionReactOnDanger (CAgentManager *object, LPCSTR action_name) : + inherited (object,action_name) +{ +} + +void CAgentManagerActionReactOnDanger::initialize () +{ + inherited::initialize (); + + m_object->location().clear (); +} + +void CAgentManagerActionReactOnDanger::execute () +{ + inherited::execute (); + + m_object->explosive().react_on_explosives (); + m_object->corpse().react_on_member_death (); +} diff --git a/src/xrGameLA/agent_manager_actions.h b/src/xrGameLA/agent_manager_actions.h new file mode 100644 index 000000000..c7fd978b9 --- /dev/null +++ b/src/xrGameLA/agent_manager_actions.h @@ -0,0 +1,69 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager_actions.h +// Created : 25.05.2004 +// Modified : 25.05.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager actions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "action_base.h" + +class CAgentManager; + +typedef CActionBase CAgentManagerActionBase; + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerActionNoOrders +////////////////////////////////////////////////////////////////////////// + +class CAgentManagerActionNoOrders : public CAgentManagerActionBase { +protected: + typedef CAgentManagerActionBase inherited; + +public: + CAgentManagerActionNoOrders (CAgentManager *object, LPCSTR action_name = ""); + virtual void finalize (); +}; + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerActionGatherItems +////////////////////////////////////////////////////////////////////////// + +class CAgentManagerActionGatherItems : public CAgentManagerActionBase { +protected: + typedef CAgentManagerActionBase inherited; + +public: + CAgentManagerActionGatherItems (CAgentManager *object, LPCSTR action_name = ""); +}; + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerActionKillEnemy +////////////////////////////////////////////////////////////////////////// + +class CAgentManagerActionKillEnemy : public CAgentManagerActionBase { +protected: + typedef CAgentManagerActionBase inherited; + +public: + CAgentManagerActionKillEnemy(CAgentManager *object, LPCSTR action_name = ""); + virtual void initialize (); + virtual void finalize (); + virtual void execute (); +}; + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerActionReactOnDanger +////////////////////////////////////////////////////////////////////////// + +class CAgentManagerActionReactOnDanger : public CAgentManagerActionBase { +protected: + typedef CAgentManagerActionBase inherited; + +public: + CAgentManagerActionReactOnDanger(CAgentManager *object, LPCSTR action_name = ""); + virtual void initialize (); + virtual void execute (); +}; diff --git a/src/xrGameLA/agent_manager_inline.h b/src/xrGameLA/agent_manager_inline.h new file mode 100644 index 000000000..15f1a755d --- /dev/null +++ b/src/xrGameLA/agent_manager_inline.h @@ -0,0 +1,56 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager_inline.h +// Created : 24.05.2004 +// Modified : 24.05.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC shared_str CAgentManager::cName () const +{ + return ("agent_manager"); +} + +IC CAgentCorpseManager &CAgentManager::corpse () const +{ + VERIFY (m_corpse); + return (*m_corpse); +} + +IC CAgentEnemyManager &CAgentManager::enemy () const +{ + VERIFY (m_enemy); + return (*m_enemy); +} + +IC CAgentExplosiveManager &CAgentManager::explosive () const +{ + VERIFY (m_explosive); + return (*m_explosive); +} + +IC CAgentLocationManager &CAgentManager::location () const +{ + VERIFY (m_location); + return (*m_location); +} + +IC CAgentMemberManager &CAgentManager::member () const +{ + VERIFY (m_member); + return (*m_member); +} + +IC CAgentMemoryManager &CAgentManager::memory () const +{ + VERIFY (m_memory); + return (*m_memory); +} + +IC CAgentManagerPlanner &CAgentManager::brain () const +{ + VERIFY (m_brain); + return (*m_brain); +} diff --git a/src/xrGameLA/agent_manager_planner.cpp b/src/xrGameLA/agent_manager_planner.cpp new file mode 100644 index 000000000..d7d27fdc2 --- /dev/null +++ b/src/xrGameLA/agent_manager_planner.cpp @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager_planner.cpp +// Created : 24.05.2004 +// Modified : 02.12.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager planner +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "agent_manager.h" +#include "agent_manager_space.h" +#include "agent_manager_actions.h" +#include "agent_manager_properties.h" +#include "agent_manager_planner.h" + +using namespace AgentManager; + +void CAgentManagerPlanner::setup (CAgentManager *object) +{ + inherited::setup (object); + + clear (); + add_evaluators (); + add_actions (); + + CWorldState goal; + goal.clear (); + goal.add_condition (CWorldProperty(ePropertyOrders,true)); + set_target_state (goal); +} + +void CAgentManagerPlanner::add_evaluators () +{ + add_evaluator (ePropertyOrders, new CAgentManagerPropertyEvaluatorConst(false,"property_order")); + add_evaluator (ePropertyItem , new CAgentManagerPropertyEvaluatorItem(&object(),"property_item")); + add_evaluator (ePropertyEnemy , new CAgentManagerPropertyEvaluatorEnemy(&object(),"property_enemy")); + add_evaluator (ePropertyDanger, new CAgentManagerPropertyEvaluatorDanger(&object(),"property_danger")); +} + +void CAgentManagerPlanner::add_actions () +{ + CAgentManagerActionBase *action; + + action = new CAgentManagerActionNoOrders(&object(),"no_orders"); + add_condition (action,ePropertyOrders, false); + add_condition (action,ePropertyItem, false); + add_condition (action,ePropertyDanger, false); + add_condition (action,ePropertyEnemy, false); + add_effect (action,ePropertyOrders, true); + add_operator (eOperatorNoOrders, action); + + action = new CAgentManagerActionGatherItems(&object(),"gather_items"); + add_condition (action,ePropertyItem, true); + add_condition (action,ePropertyEnemy, false); + add_condition (action,ePropertyDanger, false); + add_effect (action,ePropertyItem, false); + add_operator (eOperatorGatherItem, action); + + action = new CAgentManagerActionKillEnemy(&object(),"kill_enemy"); + add_condition (action,ePropertyEnemy, true); + add_effect (action,ePropertyEnemy, false); + add_operator (eOperatorKillEnemy, action); + + action = new CAgentManagerActionReactOnDanger(&object(),"react_on_danger"); + add_condition (action,ePropertyEnemy, false); + add_condition (action,ePropertyDanger, true); + add_effect (action,ePropertyDanger, false); + add_operator (eOperatorReactOnDanger, action); +} + +void CAgentManagerPlanner::remove_links (CObject *object) +{ +} diff --git a/src/xrGameLA/agent_manager_planner.h b/src/xrGameLA/agent_manager_planner.h new file mode 100644 index 000000000..cede05ad9 --- /dev/null +++ b/src/xrGameLA/agent_manager_planner.h @@ -0,0 +1,25 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager_planner.h +// Created : 24.05.2004 +// Modified : 02.12.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager planner +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "action_planner.h" + +class CAgentManager; +class CObject; + +class CAgentManagerPlanner : public CActionPlanner { +private: + typedef CActionPlanner inherited; + +public: + virtual void setup (CAgentManager *object); + void add_evaluators (); + void add_actions (); + void remove_links (CObject *object); +}; diff --git a/src/xrGameLA/agent_manager_properties.cpp b/src/xrGameLA/agent_manager_properties.cpp new file mode 100644 index 000000000..861f9124f --- /dev/null +++ b/src/xrGameLA/agent_manager_properties.cpp @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager_properties.cpp +// Created : 25.05.2004 +// Modified : 25.05.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager properties +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "agent_manager_properties.h" +#include "agent_manager.h" +#include "agent_member_manager.h" +#include "agent_manager_space.h" +#include "ai/stalker/ai_stalker.h" +#include "memory_manager.h" +#include "item_manager.h" +#include "enemy_manager.h" +#include "danger_manager.h" + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerPropertyEvaluatorGlobal +////////////////////////////////////////////////////////////////////////// + +CAgentManagerPropertyEvaluatorItem::_value_type CAgentManagerPropertyEvaluatorItem::evaluate () +{ + CAgentMemberManager::iterator I = m_object->member().members().begin(); + CAgentMemberManager::iterator E = m_object->member().members().end(); + for ( ; I != E; ++I) { + VERIFY (*I); + if ((*I)->object().memory().item().selected()) + return (true); + } + return (false); +} + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerPropertyEvaluatorEnemy +////////////////////////////////////////////////////////////////////////// + +CAgentManagerPropertyEvaluatorEnemy::_value_type CAgentManagerPropertyEvaluatorEnemy::evaluate () +{ + CAgentMemberManager::iterator I = m_object->member().combat_members().begin(); + CAgentMemberManager::iterator E = m_object->member().combat_members().end(); + for ( ; I != E; ++I) { + VERIFY (*I); + if ((*I)->object().memory().enemy().selected()) + return (true); + } + return (false); +} + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerPropertyEvaluatorDanger +////////////////////////////////////////////////////////////////////////// + +CAgentManagerPropertyEvaluatorDanger::_value_type CAgentManagerPropertyEvaluatorDanger::evaluate () +{ + CAgentMemberManager::iterator I = m_object->member().members().begin(); + CAgentMemberManager::iterator E = m_object->member().members().end(); + for ( ; I != E; ++I) { + VERIFY (*I); + if ((*I)->object().memory().danger().selected()) + return (true); + } + return (false); +} diff --git a/src/xrGameLA/agent_manager_properties.h b/src/xrGameLA/agent_manager_properties.h new file mode 100644 index 000000000..8d0bcbe67 --- /dev/null +++ b/src/xrGameLA/agent_manager_properties.h @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager_properties.h +// Created : 25.05.2004 +// Modified : 25.05.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager properties +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "property_evaluator_const.h" +#include "property_evaluator_member.h" + +class CAgentManager; + +typedef CPropertyEvaluator CAgentManagerPropertyEvaluator; +typedef CPropertyEvaluatorConst CAgentManagerPropertyEvaluatorConst; +typedef CPropertyEvaluatorMember CAgentManagerPropertyEvaluatorMember; + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerPropertyEvaluatorItem +////////////////////////////////////////////////////////////////////////// + +class CAgentManagerPropertyEvaluatorItem : public CAgentManagerPropertyEvaluator { +protected: + typedef CAgentManagerPropertyEvaluator inherited; + +public: + IC CAgentManagerPropertyEvaluatorItem (CAgentManager *object = 0, LPCSTR evaluator_name = ""); + virtual _value_type evaluate (); +}; + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerPropertyEvaluatorEnemy +////////////////////////////////////////////////////////////////////////// + +class CAgentManagerPropertyEvaluatorEnemy : public CAgentManagerPropertyEvaluator { +protected: + typedef CAgentManagerPropertyEvaluator inherited; + +public: + IC CAgentManagerPropertyEvaluatorEnemy (CAgentManager *object = 0, LPCSTR evaluator_name = ""); + virtual _value_type evaluate (); +}; + +////////////////////////////////////////////////////////////////////////// +// CAgentManagerPropertyEvaluatorDanger +////////////////////////////////////////////////////////////////////////// + +class CAgentManagerPropertyEvaluatorDanger : public CAgentManagerPropertyEvaluator { +protected: + typedef CAgentManagerPropertyEvaluator inherited; + +public: + IC CAgentManagerPropertyEvaluatorDanger (CAgentManager *object = 0, LPCSTR evaluator_name = ""); + virtual _value_type evaluate (); +}; + +#include "agent_manager_properties_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/agent_manager_properties_inline.h b/src/xrGameLA/agent_manager_properties_inline.h new file mode 100644 index 000000000..27ab1f754 --- /dev/null +++ b/src/xrGameLA/agent_manager_properties_inline.h @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager_properties_inline.h +// Created : 25.05.2004 +// Modified : 25.05.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager properties inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CAgentManagerPropertyEvaluatorItem::CAgentManagerPropertyEvaluatorItem (CAgentManager *object, LPCSTR evaluator_name) : + inherited(object,evaluator_name) +{ +} + +IC CAgentManagerPropertyEvaluatorEnemy::CAgentManagerPropertyEvaluatorEnemy (CAgentManager *object, LPCSTR evaluator_name) : + inherited(object,evaluator_name) +{ +} + +IC CAgentManagerPropertyEvaluatorDanger::CAgentManagerPropertyEvaluatorDanger (CAgentManager *object, LPCSTR evaluator_name) : + inherited(object,evaluator_name) +{ +} diff --git a/src/xrGameLA/agent_manager_space.h b/src/xrGameLA/agent_manager_space.h new file mode 100644 index 000000000..ff51e6e32 --- /dev/null +++ b/src/xrGameLA/agent_manager_space.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_manager_space.h +// Created : 25.05.2004 +// Modified : 25.05.2004 +// Author : Dmitriy Iassenev +// Description : Agent manager space +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +namespace AgentManager { + enum EProperties { + ePropertyOrders = u32(0), + ePropertyItem, + ePropertyEnemy, + ePropertyDanger, + + ePropertyScript, + ePropertyDummy = u32(-1), + }; + + enum EOperators { + eOperatorNoOrders = u32(0), + eOperatorGatherItem, + eOperatorKillEnemy, + eOperatorReactOnDanger, + + eOperatorScript, + eOperatorDummy = u32(-1), + }; +}; \ No newline at end of file diff --git a/src/xrGameLA/agent_member_manager.cpp b/src/xrGameLA/agent_member_manager.cpp new file mode 100644 index 000000000..27aee4d51 --- /dev/null +++ b/src/xrGameLA/agent_member_manager.cpp @@ -0,0 +1,281 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_member_manager.cpp +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent member manager +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "agent_member_manager.h" +#include "ai/stalker/ai_stalker.h" +#include "object_broker.h" +#include "agent_manager.h" +#include "agent_memory_manager.h" +#include "explosive.h" +#include "sound_player.h" +#include "grenade.h" + +class CMemberPredicate2 { +protected: + ALife::_OBJECT_ID m_object_id; + +public: + IC CMemberPredicate2 (const ALife::_OBJECT_ID &object_id) + { + m_object_id = object_id; + } + + IC bool operator() (const CMemberOrder *order) const + { + return (order->object().ID() == m_object_id); + } +}; + +CAgentMemberManager::~CAgentMemberManager () +{ + delete_data (m_members); +} + +void CAgentMemberManager::add (CEntity *member) +{ + CAI_Stalker *stalker = smart_cast(member); + if (!stalker || !stalker->g_Alive()) + return; +#pragma todo("change it to verify once fixed..") + R_ASSERT2 ( + sizeof(squad_mask_type)*8 > members().size(), + make_string( + "adding [%s]: too many stalkers in group ([team:%d][squad:%d][group:%d]!", + *stalker->cName(), + m_members.front()->object().g_Team(), + m_members.front()->object().g_Squad(), + m_members.front()->object().g_Group() + ) + ); + stalker->set_agent_manager (m_object); + iterator I = std::find_if(m_members.begin(),m_members.end(), CMemberPredicate(stalker)); + VERIFY (I == m_members.end()); + m_members.push_back (new CMemberOrder(stalker)); +} + +void CAgentMemberManager::remove (CEntity *member) +{ + CAI_Stalker *stalker = smart_cast(member); + if (!stalker) + return; + + if (registered_in_combat(stalker)) + unregister_in_combat (stalker); + + stalker->set_agent_manager (NULL); + squad_mask_type m = mask(stalker); + object().memory().update_memory_masks (m); + object().memory().update_memory_mask (m,m_combat_mask); + + iterator I = std::find_if(m_members.begin(),m_members.end(), CMemberPredicate(stalker)); + VERIFY (I != m_members.end()); + xr_delete (*I); + m_members.erase (I); +} + +void CAgentMemberManager::update () +{ +} + +void CAgentMemberManager::remove_links (CObject *object) +{ + MEMBER_STORAGE::iterator I = m_members.begin(); + MEMBER_STORAGE::iterator E = m_members.end(); + for ( ; I != E; ++I) { + if ((*I)->grenade_reaction().m_grenade) { + const CGameObject *explosive =smart_cast((*I)->grenade_reaction().m_grenade); + VERIFY (explosive); + if (explosive->ID() == object->ID()) + (*I)->grenade_reaction().clear(); + else { + CGrenade const* grenade = smart_cast(explosive); + if (grenade && grenade->CurrentParentID() == object->ID()) + (*I)->grenade_reaction().clear(); + } + } + + if ((*I)->grenade_reaction().m_game_object && ((*I)->grenade_reaction().m_game_object->ID() == object->ID())) + (*I)->grenade_reaction().clear(); + + if ((*I)->member_death_reaction().m_member && ((*I)->member_death_reaction().m_member->ID() == object->ID())) + (*I)->member_death_reaction().clear(); + } + +} + +void CAgentMemberManager::register_in_combat (const CAI_Stalker *object) +{ +// if (!object->group_behaviour()) +// return; + +#if 0//def DEBUG + Msg ( + "%6d registering stalker %s in combat: 0x%08x -> 0x%08x", + Device.dwTimeGlobal, + *object->cName(), + m_combat_mask, + m_combat_mask | mask(object) + ); +#endif // DEBUG + + squad_mask_type m = mask(object); + m_actuality = m_actuality && ((m_combat_mask | m) == m_combat_mask); + m_combat_mask |= m; +} + +void CAgentMemberManager::unregister_in_combat (const CAI_Stalker *object) +{ +// if (!object->group_behaviour()) { +// VERIFY (!registered_in_combat(object)); +// return; +// } + +#if 0//def DEBUG + Msg ( + "%6d UNregistering stalker %s in combat: 0x%08x -> 0x%08x", + Device.dwTimeGlobal, + *object->cName(), + m_combat_mask, + (m_combat_mask & (squad_mask_type(-1) ^ mask(object))) + ); +#endif // DEBUG + + squad_mask_type m = mask(object); + m_actuality = m_actuality && ((m_combat_mask & (squad_mask_type(-1) ^ m)) == m_combat_mask); + m_combat_mask &= squad_mask_type(-1) ^ m; +} + +bool CAgentMemberManager::registered_in_combat (const CAI_Stalker *object) const +{ + return (!!(m_combat_mask & mask(object))); +} + +CAgentMemberManager::MEMBER_STORAGE &CAgentMemberManager::combat_members () +{ + if (m_actuality) + return (m_combat_members); + + m_actuality = true; + + m_combat_members.clear (); + MEMBER_STORAGE::iterator I = members().begin(); + MEMBER_STORAGE::iterator E = members().end(); + for ( ; I != E; ++I) { + if (registered_in_combat(&(*I)->object())) + m_combat_members.push_back (*I); + } + + return (m_combat_members); +} + +CAgentMemberManager::squad_mask_type CAgentMemberManager::non_combat_members_mask () const +{ + squad_mask_type result = 0; + + MEMBER_STORAGE::const_iterator I = members().begin(); + MEMBER_STORAGE::const_iterator E = members().end(); + for ( ; I != E; ++I) { + if (!registered_in_combat(&(*I)->object())) + result |= mask(&(*I)->object()); + } + + return (result); +} + +u32 CAgentMemberManager::in_detour () const +{ + u32 in_detour = 0; + MEMBER_STORAGE::const_iterator I = members().begin(); + MEMBER_STORAGE::const_iterator E = members().end(); + for ( ; I != E; ++I) + if ((*I)->detour()) + ++in_detour; + + return (in_detour); +} + +bool CAgentMemberManager::can_detour () const +{ + u32 in_detour_count = in_detour(); + return (!in_detour_count || (in_detour_count < (members().size()/2))); +} + +bool CAgentMemberManager::cover_detouring () const +{ + MEMBER_STORAGE::const_iterator I = members().begin(); + MEMBER_STORAGE::const_iterator E = members().end(); + for ( ; I != E; ++I) + if ((*I)->detour()) + return (true); + return (false); +} + +bool CAgentMemberManager::can_cry_noninfo_phrase() const +{ + MEMBER_STORAGE::const_iterator I = members().begin(); + MEMBER_STORAGE::const_iterator E = members().end(); + for ( ; I != E; ++I) { + if (!registered_in_combat(&(*I)->object())) + continue; + + if ((*I)->object().sound().active_sound_count(false)) + return (false); + } + + return (true); +} + +MemorySpace::squad_mask_type CAgentMemberManager::mask (const ALife::_OBJECT_ID &object_id) const +{ + const_iterator I = std::find_if(members().begin(),members().end(), CMemberPredicate2(object_id)); + VERIFY (I != members().end()); + return (MemorySpace::squad_mask_type(1) << (I - members().begin())); +} + +CMemberOrder *CAgentMemberManager::get_member (const ALife::_OBJECT_ID &object_id) +{ + iterator I = std::find_if(members().begin(),members().end(), CMemberPredicate2(object_id)); + if (I == members().end()) + return (0); + + return (&**I); +} + +bool CAgentMemberManager::can_throw_grenade (const Fvector &location) const +{ + if (Device.dwTimeGlobal <= m_last_throw_time + m_throw_time_interval) + return (false); + + typedef CAgentMemberManager::MEMBER_STORAGE MEMBER_STORAGE; + const float member_danger_radius_sqr = _sqr(5.f); +// if (members().size() <= 1) +// return true; +// const float cover_danger_radius_sqr = _sqr(5.f); + MEMBER_STORAGE::const_iterator I = members().begin(); + MEMBER_STORAGE::const_iterator E = members().end(); + for ( ; I != E; ++I) + { + if ((*I)->object().Position().distance_to_sqr(location) <= member_danger_radius_sqr) + return (false); + /* + if (!(*I)->cover()) + continue; + + if ((*I)->cover()->m_position.distance_to_sqr(location) <= cover_danger_radius_sqr) + return (false); + */ + } + + return (true); +} + +void CAgentMemberManager::on_throw_completed () +{ + m_last_throw_time = Device.dwTimeGlobal; +} diff --git a/src/xrGameLA/agent_member_manager.h b/src/xrGameLA/agent_member_manager.h new file mode 100644 index 000000000..cdff88a4b --- /dev/null +++ b/src/xrGameLA/agent_member_manager.h @@ -0,0 +1,67 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_member_manager.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent member manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "member_order.h" +#include "memory_space.h" + +class CAgentManager; +class CEntity; + +class CAgentMemberManager { +public: + typedef xr_vector MEMBER_STORAGE; + typedef MEMBER_STORAGE::iterator iterator; + typedef MEMBER_STORAGE::const_iterator const_iterator; + typedef MemorySpace::squad_mask_type squad_mask_type; + +private: + CAgentManager *m_object; + MEMBER_STORAGE m_members; + MEMBER_STORAGE m_combat_members; + bool m_actuality; + squad_mask_type m_combat_mask; + u32 m_last_throw_time; + u32 m_throw_time_interval; + +protected: + IC CAgentManager &object () const; + +public: + IC CAgentMemberManager (CAgentManager *object); + virtual ~CAgentMemberManager (); + void update (); + void add (CEntity *member); + void remove (CEntity *member); + IC CMemberOrder &member (const CAI_Stalker *object); + CMemberOrder *get_member (const ALife::_OBJECT_ID &id); + IC const MEMBER_STORAGE &members () const; + IC MEMBER_STORAGE &members (); + IC squad_mask_type mask (const CAI_Stalker *object) const; + squad_mask_type mask (const ALife::_OBJECT_ID &id) const; + IC bool group_behaviour () const; + IC iterator member (squad_mask_type mask); + void remove_links (CObject *object); + void register_in_combat (const CAI_Stalker *object); + void unregister_in_combat (const CAI_Stalker *object); + bool registered_in_combat (const CAI_Stalker *object) const; + IC const squad_mask_type &combat_mask () const; + squad_mask_type non_combat_members_mask () const; + MEMBER_STORAGE &combat_members (); + u32 in_detour () const; + bool can_detour () const; + bool cover_detouring () const; + bool can_cry_noninfo_phrase () const; + bool can_throw_grenade (const Fvector &location) const; + void on_throw_completed (); + IC const u32 &throw_time_interval () const; + IC void throw_time_interval (const u32 &value); +}; + +#include "agent_member_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/agent_member_manager_inline.h b/src/xrGameLA/agent_member_manager_inline.h new file mode 100644 index 000000000..2d23d8b54 --- /dev/null +++ b/src/xrGameLA/agent_member_manager_inline.h @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_member_manager_inline.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent member manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +class CMemberPredicate { +protected: + const CAI_Stalker *m_object; + +public: + IC CMemberPredicate (const CAI_Stalker *object) + { + m_object = object; + } + + IC bool operator() (const CMemberOrder *order) const + { + return (&order->object() == m_object); + } +}; + +IC CAgentMemberManager::CAgentMemberManager (CAgentManager *object) : + m_last_throw_time (0), + m_throw_time_interval (0) +{ + VERIFY (object); + m_object = object; + m_actuality = true; + m_combat_mask = 0; +} + +IC CAgentManager &CAgentMemberManager::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +IC const CAgentMemberManager::MEMBER_STORAGE &CAgentMemberManager::members () const +{ + return (m_members); +} + +IC CAgentMemberManager::MEMBER_STORAGE &CAgentMemberManager::members () +{ + return (m_members); +} + +IC CMemberOrder &CAgentMemberManager::member (const CAI_Stalker *object) +{ + iterator I = std::find_if(members().begin(), members().end(), CMemberPredicate(object)); + VERIFY (I != members().end()); + return (**I); +} + +IC MemorySpace::squad_mask_type CAgentMemberManager::mask(const CAI_Stalker *object) const +{ + const_iterator I = std::find_if(members().begin(),members().end(), CMemberPredicate(object)); + VERIFY (I != members().end()); + return (MemorySpace::squad_mask_type(1) << (I - members().begin())); +} + +IC CAgentMemberManager::iterator CAgentMemberManager::member (MemorySpace::squad_mask_type mask) +{ + iterator I = m_members.begin(); + iterator E = m_members.end(); + for ( ; I != E; ++I, mask >>= 1) + if (mask == 1) + return (I); + NODEFAULT; +#ifdef DEBUG + return (E); +#endif +} + +IC bool CAgentMemberManager::group_behaviour () const +{ + return (members().size() > 1); +} + +IC const CAgentMemberManager::squad_mask_type &CAgentMemberManager::combat_mask() const +{ + return (m_combat_mask); +} + +IC const u32 &CAgentMemberManager::throw_time_interval () const +{ + return (m_throw_time_interval); +} + +IC void CAgentMemberManager::throw_time_interval (const u32 &value) +{ + m_throw_time_interval = value; +} \ No newline at end of file diff --git a/src/xrGameLA/agent_memory_manager.cpp b/src/xrGameLA/agent_memory_manager.cpp new file mode 100644 index 000000000..85f6d7367 --- /dev/null +++ b/src/xrGameLA/agent_memory_manager.cpp @@ -0,0 +1,96 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_memory_manager.cpp +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent memory manager +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "agent_memory_manager.h" +#include "agent_manager.h" +#include "agent_member_manager.h" +#include "ai_object_location.h" +#include "level_graph.h" +#include "entity_alive.h" +#include "memory_space_impl.h" + +void CAgentMemoryManager::update () +{ + reset_memory_masks (); +} + +void CAgentMemoryManager::remove_links (CObject *object) +{ +} + +template +IC void CAgentMemoryManager::reset_memory_masks (T &objects) +{ + typename T::iterator I = objects.begin(); + typename T::iterator E = objects.end(); + for ( ; I != E; ++I) + if (object().member().combat_mask() & (*I).m_squad_mask.get()) + (*I).m_squad_mask.assign((*I).m_squad_mask.get() | object().member().combat_mask()); +} + +void CAgentMemoryManager::reset_memory_masks () +{ + reset_memory_masks (visibles()); + reset_memory_masks (sounds()); + reset_memory_masks (hits()); +} + +template +IC void CAgentMemoryManager::update_memory_masks (const squad_mask_type &mask, T &objects) +{ + typename T::iterator I = objects.begin(); + typename T::iterator E = objects.end(); + for ( ; I != E; ++I) { + squad_mask_type m = (*I).m_squad_mask.get(); + update_memory_mask (mask,m); + (*I).m_squad_mask.assign(m); + } +} + +void CAgentMemoryManager::update_memory_masks (const squad_mask_type &mask) +{ + update_memory_masks (mask,visibles()); + update_memory_masks (mask,sounds()); + update_memory_masks (mask,hits()); + + VISIBLES::iterator I = visibles().begin(); + VISIBLES::iterator E = visibles().end(); + for ( ; I != E; ++I) { + squad_mask_type m = (*I).m_visible.get(); + update_memory_mask (mask,m); + (*I).m_visible.assign(m); + } +} + +void CAgentMemoryManager::object_information (const CObject *object, u32 &level_time, Fvector &position) +{ + { + VISIBLES::const_iterator I = std::find(visibles().begin(),visibles().end(),object_id(object)); + if (visibles().end() != I) { + level_time = (*I).m_last_level_time; + position = (*I).m_object_params.m_position; + } + } + + { + SOUNDS::const_iterator I = std::find(sounds().begin(),sounds().end(),object_id(object)); + if ((sounds().end() != I) && (level_time < (*I).m_last_level_time)) { + level_time = (*I).m_last_level_time; + position = (*I).m_object_params.m_position; + } + } + + { + HITS::const_iterator I = std::find(hits().begin(),hits().end(),object_id(object)); + if ((hits().end() != I) && (level_time < (*I).m_last_level_time)) { + level_time = (*I).m_last_level_time; + position = (*I).m_object_params.m_position; + } + } +} \ No newline at end of file diff --git a/src/xrGameLA/agent_memory_manager.h b/src/xrGameLA/agent_memory_manager.h new file mode 100644 index 000000000..19f324082 --- /dev/null +++ b/src/xrGameLA/agent_memory_manager.h @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_memory_manager_inline.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent memory manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "memory_space.h" + +class CAgentManager; + +class CAgentMemoryManager { +public: + typedef MemorySpace::CVisibleObject CVisibleObject; + typedef MemorySpace::CSoundObject CSoundObject; + typedef MemorySpace::CHitObject CHitObject; + +public: + typedef xr_vector VISIBLES; + typedef xr_vector SOUNDS; + typedef xr_vector HITS; + typedef MemorySpace::squad_mask_type squad_mask_type; + + +protected: + CAgentManager *m_object; + VISIBLES *m_visible_objects; + SOUNDS *m_sound_objects; + HITS *m_hit_objects; + +protected: + IC CAgentManager &object () const; + +public: + IC CAgentMemoryManager (CAgentManager *object); + void update (); + void remove_links (CObject *object); + +public: + IC void set_squad_objects (VISIBLES *objects); + IC void set_squad_objects (SOUNDS *objects); + IC void set_squad_objects (HITS *objects); + +public: + IC VISIBLES &visibles () const; + IC SOUNDS &sounds () const; + IC HITS &hits () const; + +public: + template + IC void reset_memory_masks (T &objects); + void reset_memory_masks (); + + template + IC void update_memory_masks (const squad_mask_type &mask, T &objects); + IC void update_memory_mask (const squad_mask_type &mask, squad_mask_type ¤t); + void update_memory_masks (const squad_mask_type &mask); + void object_information (const CObject *object, u32 &level_time, Fvector &position); +}; + +#include "agent_memory_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/agent_memory_manager_inline.h b/src/xrGameLA/agent_memory_manager_inline.h new file mode 100644 index 000000000..04a4150e0 --- /dev/null +++ b/src/xrGameLA/agent_memory_manager_inline.h @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : agent_memory_manager.h +// Created : 24.05.2004 +// Modified : 14.01.2005 +// Author : Dmitriy Iassenev +// Description : Agent memory manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CAgentMemoryManager::CAgentMemoryManager (CAgentManager *object) +{ + VERIFY (object); + m_object = object; +} + +IC CAgentManager &CAgentMemoryManager::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +IC void CAgentMemoryManager::set_squad_objects (VISIBLES *objects) +{ + m_visible_objects = objects; +} + +IC void CAgentMemoryManager::set_squad_objects (SOUNDS *objects) +{ + m_sound_objects = objects; +} + +IC void CAgentMemoryManager::set_squad_objects (HITS *objects) +{ + m_hit_objects = objects; +} + +IC CAgentMemoryManager::VISIBLES &CAgentMemoryManager::visibles () const +{ + VERIFY (m_visible_objects); + return (*m_visible_objects); +} + +IC CAgentMemoryManager::SOUNDS &CAgentMemoryManager::sounds () const +{ + VERIFY (m_sound_objects); + return (*m_sound_objects); +} + +IC CAgentMemoryManager::HITS &CAgentMemoryManager::hits () const +{ + VERIFY (m_hit_objects); + return (*m_hit_objects); +} + +IC void CAgentMemoryManager::update_memory_mask (const squad_mask_type &mask, squad_mask_type ¤t) +{ + // this function removes specified bit and shifts all the others + current = (((mask ^ squad_mask_type(-1) ^ (mask - 1)) & current) >> 1) | (current & (mask - 1)); +} + diff --git a/src/xrGameLA/ai/ai_monsters_anims.h b/src/xrGameLA/ai/ai_monsters_anims.h new file mode 100644 index 000000000..7bb4dd54e --- /dev/null +++ b/src/xrGameLA/ai/ai_monsters_anims.h @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_monsters_anims.h +// Created : 23.05.2003 +// Modified : 23.05.2003 +// Author : Serge Zhem +// Description : Animation templates for all of the monsters +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "../../Include/xrRender/KinematicsAnimated.h" +#include "../ai_debug.h" + +DEFINE_VECTOR (MotionID,ANIM_VECTOR, ANIM_IT); + +class CAniVector { +public: + ANIM_VECTOR A; + + void Load (IKinematicsAnimated *tpKinematics, LPCSTR caBaseName); +}; + +template class CAniFVector { +public: + ANIM_VECTOR A; + + IC void Load(IKinematicsAnimated *tpKinematics, LPCSTR caBaseName) + { + A.clear (); + string256 S; + for (int j=0; caBaseNames[j]; ++j); + A.resize (j); + for (int i=0; iID_Cycle_Safe(S); +#ifdef DEBUG + if (A[i] && psAI_Flags.test(aiAnimation)) + Msg ("* Loaded animation %s",S); +#endif + } + } +}; + +template class CAniCollection { +public: + xr_vector A; + + IC void Load(IKinematicsAnimated *tpKinematics, LPCSTR caBaseName) + { + A.clear (); + string256 S; + for (int j=0; caBaseNames[j]; ++j); + A.resize (j); + for (int i=0; i &VisibleEnemies, float fMinProbability, CBaseFunction &fSuccessProbabilityFunction) +{ + int i = 0, j = 0, I = (int)Members.size(), J = (int)VisibleEnemies.size(); + xr_vector::const_iterator II = VisibleEnemies.begin(); + for ( ; (i < I) && (j < J); ) { + ai().ef_storage().non_alife().member() = smart_cast(Members[i]); + if (!(ai().ef_storage().non_alife().member()) || !(ai().ef_storage().non_alife().member()->g_Alive())) { + ++i; + continue; + } + ai().ef_storage().non_alife().enemy() = *II; + if (!(ai().ef_storage().non_alife().enemy()) || !(ai().ef_storage().non_alife().enemy()->g_Alive())) { + ++j; + ++II; + continue; + } + float fProbability = fSuccessProbabilityFunction.ffGetValue()/100.f, fCurrentProbability; + if (fProbability > fMinProbability) { + fCurrentProbability = fProbability; + for (++j; (i < I) && (j < J); ++j) { + ai().ef_storage().non_alife().enemy() = *II; + if (!(ai().ef_storage().non_alife().enemy()) || !(ai().ef_storage().non_alife().enemy()->g_Alive())) { + ++j; + ++II; + continue; + } + fProbability = fSuccessProbabilityFunction.ffGetValue()/100.f; + if (fCurrentProbability*fProbability < fMinProbability) { + ++i; + break; + } + else + fCurrentProbability *= fProbability; + } + } + else { + fCurrentProbability = 1.0f - fProbability; + for (++i; (i < I) && (j < J); ++i) { + ai().ef_storage().non_alife().member() = smart_cast(Members[i]); + if (!(ai().ef_storage().non_alife().member()) || !(ai().ef_storage().non_alife().member()->g_Alive())) { + ++i; + continue; + } + fProbability = 1.0f - fSuccessProbabilityFunction.ffGetValue()/100.f; + if (fCurrentProbability*fProbability < (1.f - fMinProbability)) { + ++II; + ++j; + break; + } + else + fCurrentProbability *= fProbability; + } + } + } + return(j >= J); +} + +u32 dwfChooseAction(u32 dwActionRefreshRate, float fMinProbability0, float fMinProbability1, float fMinProbability2, float fMinProbability3, u32 dwTeam, u32 dwSquad, u32 dwGroup, u32 a0, u32 a1, u32 a2, u32 a3, u32 a4, CEntity *tpEntity, float fGroupDistance) +{ + if ( fis_zero(fMinProbability0) ) + return ( 0 ); + + CGroupHierarchyHolder &Group = Level().seniority_holder().team(dwTeam).squad(dwSquad).group(dwGroup); + + if (Device.dwTimeGlobal - Group.m_dwLastActionTime < dwActionRefreshRate) { + switch (Group.m_dwLastAction) { + case 0: return(a0); + case 1: return(a1); + case 2: return(a2); + case 3: return(a3); + case 4: return(a4); + default: return(a4); + } + } + + const CCustomMonster *monster = smart_cast(tpEntity); + VERIFY (monster); + const CAI_Stalker *stalker = smart_cast(monster); + const xr_vector &VisibleEnemies = monster->memory().enemy().objects(); + + GroupHierarchyHolder::MEMBER_REGISTRY Members; + if (!tpEntity) + for (int k=0; k<(int)Group.members().size(); ++k) { + if (Group.members()[k]->g_Alive() && ((Group.members()[k]->spatial.type & STYPE_VISIBLEFORAI) == STYPE_VISIBLEFORAI)) + Members.push_back(Group.members()[k]); + } + else + for (int k=0; k<(int)Group.members().size(); ++k) { + if (Group.members()[k]->g_Alive() && ((Group.members()[k]->spatial.type & STYPE_VISIBLEFORAI) == STYPE_VISIBLEFORAI)) + if (tpEntity->Position().distance_to(Group.members()[k]->Position()) < fGroupDistance) { + + if (!stalker) { + Members.push_back (Group.members()[k]); + continue; + } + + const CAI_Stalker *member = smart_cast(Group.members()[k]); + if (!member) { + Members.push_back (Group.members()[k]); + continue; + } + + if (Group.agent_manager().member().registered_in_combat(member)) + Members.push_back (Group.members()[k]); + else + if (member->ID() == tpEntity->ID()) + Members.push_back (Group.members()[k]); + } + } + + ai().ef_storage().non_alife().member_item() = 0; + ai().ef_storage().non_alife().enemy_item() = 0; + + WRITE_QUERY_TO_LOG("\nNew query"); + if (bfGetActionSuccessProbability(Members,VisibleEnemies,fMinProbability0,*ai().ef_storage().m_pfVictoryProbability)) { + Group.m_dwLastActionTime = Device.dwTimeGlobal; + Group.m_dwLastAction = 0; + WRITE_QUERY_TO_LOG ("Attack"); + return (a0); + } + else + if (bfGetActionSuccessProbability(Members,VisibleEnemies,fMinProbability1,*ai().ef_storage().m_pfVictoryProbability)) { + Group.m_dwLastActionTime = Device.dwTimeGlobal; + Group.m_dwLastAction = 1; + WRITE_QUERY_TO_LOG ("Attack 1"); + return (a1); + } + else + if (bfGetActionSuccessProbability(Members,VisibleEnemies,fMinProbability2,*ai().ef_storage().m_pfVictoryProbability)) { + Group.m_dwLastActionTime = Device.dwTimeGlobal; + Group.m_dwLastAction = 2; + WRITE_QUERY_TO_LOG ("Defend"); + return (a2); + } + else + if (bfGetActionSuccessProbability(Members,VisibleEnemies,fMinProbability3,*ai().ef_storage().m_pfVictoryProbability)) { + Group.m_dwLastActionTime = Device.dwTimeGlobal; + Group.m_dwLastAction = 3; + WRITE_QUERY_TO_LOG ("Defend 1"); + return (a3); + } + else { + Group.m_dwLastActionTime = Device.dwTimeGlobal; + Group.m_dwLastAction = 4; + WRITE_QUERY_TO_LOG ("Retreat"); + return (a4); + } +} + +void CAniVector::Load(IKinematicsAnimated *tpKinematics, LPCSTR caBaseName) +{ + A.clear (); + string256 S1, S2; + MotionID tpMotionDef; + for (int i=0; ; ++i) + if (!!(tpMotionDef = tpKinematics->ID_Cycle_Safe(strconcat(sizeof(S1),S1,caBaseName,itoa(i,S2,10))))) { + A.push_back(tpMotionDef); +#ifdef DEBUG + if (psAI_Flags.test(aiAnimation)) + Msg ("* Loaded animation %s",S1); +#endif + } + else + if (!!(tpMotionDef = tpKinematics->ID_FX_Safe(strconcat(sizeof(S1),S1,caBaseName,itoa(i,S2,10))))) { + A.push_back(tpMotionDef); +#ifdef DEBUG + if (psAI_Flags.test(aiAnimation)) + Msg ("* Loaded animation fx %s",S1); +#endif + } + else + if (i<10) + continue; + else + break; +} diff --git a/src/xrGameLA/ai/ai_monsters_misc.h b/src/xrGameLA/ai/ai_monsters_misc.h new file mode 100644 index 000000000..2a69fde91 --- /dev/null +++ b/src/xrGameLA/ai/ai_monsters_misc.h @@ -0,0 +1,105 @@ + +// Module : ai_monsters_misc.cpp +// Created : 23.07.2002 +// Modified : 23.07.2002 +// Author : Dmitriy Iassenev +// Description : Miscellanious routines for monsters +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +class CBaseFunction; +class CEntity; +class CEntityAlive; + +namespace GroupHierarchyHolder { + typedef xr_vector MEMBER_REGISTRY; +}; + +#define WRITE_LOG + +#ifndef DEBUG +#undef WRITE_LOG +#endif + +#ifdef WRITE_LOG + #define WRITE_TO_LOG(S) {\ + Msg("%s,%s,%d,p[%.2f,%.2f,%.2f],%.2f,h[%.2f,%.2f],t[%.2f,%.2f]",*cName(),S,Device.dwTimeGlobal,Position().x,Position().y,Position().z,m_fCurSpeed,m_head.current.yaw,m_head.target.yaw,m_body.current.yaw,m_body.target.yaw);\ + vfUpdateDynamicObjects();\ + m_bStopThinking = true;\ + } + #define WRITE_QUERY_TO_LOG(S) ;//Msg(S); + // Msg("%d",Level().Teams[g_Team()].Squads[g_Squad()].Groups[g_Group()].m_tpaSuspiciousNodes.size());\ + +#else + #define WRITE_QUERY_TO_LOG(S) + #define WRITE_TO_LOG(S) {\ + vfUpdateDynamicObjects();\ + m_bStopThinking = true;\ + } +#endif + +#define GO_TO_NEW_STATE(a) {\ + m_tStateStack.top() = m_eCurrentState = a;\ + return;\ +} + +#define GO_TO_PREV_STATE {\ + m_tStateStack.pop();\ + m_eCurrentState = m_tStateStack.top();\ + return;\ +} + +#define SWITCH_TO_NEW_STATE(a) {\ + m_tStateStack.push(a);\ + GO_TO_NEW_STATE(a);\ +} + +#define CHECK_IF_SWITCH_TO_NEW_STATE(a,b) {\ + if (a)\ + SWITCH_TO_NEW_STATE(b);\ +} + +#define CHECK_IF_GO_TO_PREV_STATE(a) {\ + if (a)\ + GO_TO_PREV_STATE;\ +} + +#define CHECK_IF_GO_TO_NEW_STATE(a,b) {\ + if (a)\ + GO_TO_NEW_STATE(b);\ +} + +////////////////////////////////////////////////////////////////////////// +#define GO_TO_NEW_STATE_THIS_UPDATE(a) {\ + m_bStopThinking = false;\ + GO_TO_NEW_STATE(a);\ +} + +#define GO_TO_PREV_STATE_THIS_UPDATE {\ + m_bStopThinking = false;\ + GO_TO_PREV_STATE;\ +} + +#define SWITCH_TO_NEW_STATE_THIS_UPDATE(a) {\ + m_tStateStack.push(m_eCurrentState = a);\ + GO_TO_NEW_STATE_THIS_UPDATE(a);\ +} + +#define CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE(a,b) {\ + if (a)\ + SWITCH_TO_NEW_STATE_THIS_UPDATE(b);\ +} + +#define CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE(a) {\ + if (a)\ + GO_TO_PREV_STATE_THIS_UPDATE;\ +} + +#define CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE(a,b) \ + if (a)\ + GO_TO_NEW_STATE_THIS_UPDATE(b); + + +extern bool bfGetActionSuccessProbability (GroupHierarchyHolder::MEMBER_REGISTRY &Members, const xr_set &VisibleEnemies, float fMinProbability, CBaseFunction &fSuccessProbabilityFunction); +extern u32 dwfChooseAction (u32 dwActionRefreshRate, float fMinProbability0, float fMinProbability1, float fMinProbability2, float fMinProbability3, u32 dwTeam, u32 dwSquad, u32 dwGroup, u32 a0, u32 a1, u32 a2, u32 a3, u32 a4, CEntity *tpEntity=0, float fGroupDistance = 100.f); diff --git a/src/xrGameLA/ai/crow/ai_crow.cpp b/src/xrGameLA/ai/crow/ai_crow.cpp new file mode 100644 index 000000000..d78b11525 --- /dev/null +++ b/src/xrGameLA/ai/crow/ai_crow.cpp @@ -0,0 +1,441 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_crow.cpp +// Created : 13.05.2002 +// Modified : 13.05.2002 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Crow" +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "../../physicsshell.h" +#include "ai_crow.h" +//#include "../../hudmanager.h" +#include "../../level.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../../Include/xrRender/Kinematics.h" +#include "../../../../Include/xrRender/animation_blend.h" +#include "../../actor.h" + + + +void CAI_Crow::SAnim::Load (IKinematicsAnimated* visual, LPCSTR prefix) +{ + const MotionID &M = visual->ID_Cycle_Safe(prefix); + if (M) m_Animations.push_back(M); + for (int i=0; (iID_Cycle_Safe(sh_anim); + if (M) m_Animations.push_back(M); + } + R_ASSERT (m_Animations.size()); +} + +void CAI_Crow::SSound::Load (LPCSTR prefix) +{ + string_path fn; + if (FS.exist(fn,"$game_sounds$",prefix,".ogg")){ + m_Sounds.push_back (ref_sound()); + ::Sound->create (m_Sounds.back(),prefix,st_Effect,sg_SourceType); + } + for (int i=0; (icreate(m_Sounds.back(),name,st_Effect,sg_SourceType); + } + } + R_ASSERT(m_Sounds.size()); +} + +void CAI_Crow::SSound::SetPosition (const Fvector& pos) +{ + for (int i=0; i<(int)m_Sounds.size(); ++i) + if (m_Sounds[i]._feedback()) + m_Sounds[i].set_position(pos); +} + +void CAI_Crow::SSound::Unload () +{ + for (int i=0; i<(int)m_Sounds.size(); ++i) + ::Sound->destroy (m_Sounds[i]); +} + +void cb_OnHitEndPlaying (CBlend* B) +{ + ((CAI_Crow*)B->CallbackParam)->OnHitEndPlaying(B); +} + +void CAI_Crow::OnHitEndPlaying (CBlend* /**B/**/) +{ + bPlayDeathIdle = true; +} + +CAI_Crow::CAI_Crow () +{ + init (); +} + +CAI_Crow::~CAI_Crow () +{ + // removing all data no more being neded + m_Sounds.m_idle.Unload (); +} + +void CAI_Crow::init () +{ + st_current = eUndef; + st_target = eFlyIdle; + vGoalDir.set (10.0f*(Random.randF()-Random.randF()),10.0f*(Random.randF()-Random.randF()),10.0f*(Random.randF()-Random.randF())); + vCurrentDir.set (0,0,1); + vHPB.set (0,0,0); + fDHeading = 0; + fGoalChangeDelta = 10.f; + fGoalChangeTime = 0.f; + fSpeed = 5.f; + fASpeed = 0.2f; + fMinHeight = 40.f; + vVarGoal.set (10.f,10.f,100.f); + fIdleSoundDelta = 10.f; + fIdleSoundTime = fIdleSoundDelta; + bPlayDeathIdle = false; +} + +void CAI_Crow::Load( LPCSTR section ) +{ + inherited::Load (section); + ////////////////////////////////////////////////////////////////////////// + ISpatial* self = smart_cast (this); + if (self) { + self->spatial.type &=~STYPE_VISIBLEFORAI; + self->spatial.type &=~STYPE_REACTTOSOUND; + } + ////////////////////////////////////////////////////////////////////////// + + // sounds + m_Sounds.m_idle.Load ("monsters\\crow\\idle"); + // play defaut + + fSpeed = pSettings->r_float (section,"speed"); + fASpeed = pSettings->r_float (section,"angular_speed"); + fGoalChangeDelta = pSettings->r_float (section,"goal_change_delta"); + fMinHeight = pSettings->r_float (section,"min_height"); + vVarGoal = pSettings->r_fvector3 (section,"goal_variability"); + fIdleSoundDelta = pSettings->r_float (section,"idle_sound_delta"); + fIdleSoundTime = fIdleSoundDelta+fIdleSoundDelta*Random.randF(-.5f,.5f); + + +} + +BOOL CAI_Crow::net_Spawn (CSE_Abstract* DC) +{ + BOOL R = inherited::net_Spawn (DC); + setVisible (TRUE); + setEnabled (TRUE); + + // animations + IKinematicsAnimated* M = smart_cast(Visual()); R_ASSERT(M); + m_Anims.m_death.Load (M,"norm_death"); + m_Anims.m_death_dead.Load (M,"norm_death_dead"); + m_Anims.m_death_idle.Load (M,"norm_death_idle"); + m_Anims.m_fly.Load (M,"norm_fly_fwd"); + m_Anims.m_idle.Load (M,"norm_idle"); + + // disable UpdateCL, enable only on HIT + processing_deactivate (); + + auto tmp = Actor()->Position(); + tmp.x = tmp.x + ::Random.randF(-50.0f, 50.0f); + tmp.y = tmp.y + ::Random.randF(20.0f, 50.0f); + tmp.z = tmp.z + ::Random.randF(-50.0f, 50.0f); + Position().set(tmp); + + return R; +} + +void CAI_Crow::net_Destroy () +{ + inherited::net_Destroy (); + + m_Anims.m_death.m_Animations.clear (); + m_Anims.m_death_dead.m_Animations.clear (); + m_Anims.m_death_idle.m_Animations.clear (); + m_Anims.m_fly.m_Animations.clear (); + m_Anims.m_idle.m_Animations.clear (); +} + +// crow update +void CAI_Crow::switch2_FlyUp() +{ + smart_cast(Visual())->PlayCycle (m_Anims.m_fly.GetRandom()); +} +void CAI_Crow::switch2_FlyIdle() +{ + smart_cast(Visual())->PlayCycle (m_Anims.m_idle.GetRandom()); +} +void CAI_Crow::switch2_DeathDead() +{ + // AI need to pickup this + ISpatial* self = smart_cast (this); + if (self) self->spatial.type |= STYPE_VISIBLEFORAI; + // + smart_cast(Visual())->PlayCycle (m_Anims.m_death_dead.GetRandom()); +} +void CAI_Crow::switch2_DeathFall() +{ + Fvector V; + V.mul(XFORM().k,fSpeed); +// m_PhysicMovementControl->SetVelocity(V); + smart_cast(Visual())->PlayCycle (m_Anims.m_death.GetRandom(),TRUE,cb_OnHitEndPlaying,this); +} + +void CAI_Crow::state_Flying (float fdt) +{ + // Update position and orientation of the planes + float fAT = fASpeed * fdt ; + Fvector& vDirection = XFORM().k ; + + // Tweak orientation based on last position and goal + Fvector vOffset; + vOffset.sub(vGoalDir,Position()); + + // First, tweak the pitch + if( vOffset.y > 1.0){ // We're too low + vHPB.y += fAT; + if( vHPB.y > 0.8f ) vHPB.y = 0.8f; + }else if( vOffset.y < -1.0){ // We're too high + vHPB.y -= fAT; + if( vHPB.y < -0.8f )vHPB.y = -0.8f; + }else // Add damping + vHPB.y *= 0.95f; + + // Now figure out yaw changes + vOffset.y = 0.0f; + vDirection.y = 0.0f; + + vDirection.normalize(); + vOffset.normalize (); + + float fDot = vDirection.dotproduct(vOffset); + fDot = (1.0f-fDot)/2.0f * fAT * 10.0f; + + vOffset.crossproduct(vOffset,vDirection); + + if( vOffset.y > 0.01f ) fDHeading = ( fDHeading * 9.0f + fDot ) * 0.1f; + else if( vOffset.y < 0.01f )fDHeading = ( fDHeading * 9.0f - fDot ) * 0.1f; + + vHPB.x += fDHeading; + vHPB.z = -fDHeading * 9.0f; + + + // Update position + vOldPosition.set(Position()); + XFORM().setHPB (vHPB.x,vHPB.y,vHPB.z); + Position().mad (vOldPosition,vDirection,fSpeed*fdt); +} + +static Fvector vV={0,0,0}; +void CAI_Crow::state_DeathFall() +{ + Fvector tAcceleration ; + tAcceleration.set (0,-10.f,0); + if (m_pPhysicsShell) + { + Fvector velocity; + m_pPhysicsShell->get_LinearVel(velocity); + if(velocity.y>-0.001f) st_target = eDeathDead; + } + if (bPlayDeathIdle){ + smart_cast(Visual())->PlayCycle (m_Anims.m_death_idle.GetRandom()); + bPlayDeathIdle = false; + } +} + +void CAI_Crow::Die (CObject* who) +{ + inherited::Die (who) ; + processing_activate () ; // enable UpdateCL for dead crows - especially for physics support + // and do it especially before Creating physics shell or it definitely throws processing enable/disable calls: underflow + CreateSkeleton () ; + +}; +void CAI_Crow::UpdateWorkload (float fdt) +{ + if (o_workload_frame == Device.dwFrame) return; + o_workload_frame = Device.dwFrame ; + switch (st_current) { + case eFlyIdle : + case eFlyUp : + state_Flying (fdt); + break; + case eDeathFall : + state_DeathFall (); + break; + } +} +void CAI_Crow::UpdateCL () +{ + inherited::UpdateCL (); + if (m_pPhysicsShell) { + m_pPhysicsShell->Update (); + XFORM().set (m_pPhysicsShell->mXFORM); + } +} +void CAI_Crow::renderable_Render () +{ + UpdateWorkload (Device.fTimeDelta); + inherited::renderable_Render (); + o_workload_rframe = Device.dwFrame ; +} +void CAI_Crow::shedule_Update (u32 DT) +{ + float fDT = float(DT)/1000.F; + spatial.type &=~STYPE_VISIBLEFORAI; + + inherited::shedule_Update(DT); + + if (st_target!=st_current) { + switch(st_target) { + case eFlyUp: switch2_FlyUp(); break; + case eFlyIdle: switch2_FlyIdle(); break; + case eDeathFall: switch2_DeathFall();break; + case eDeathDead: switch2_DeathDead();break; + } + st_current = st_target; + } + + switch (st_current) { + case eFlyIdle: if (Position().y>vOldPosition.y) st_target = eFlyUp; break; + case eFlyUp: if (Position().y<=vOldPosition.y) st_target = eFlyIdle; break; + case eDeathFall: state_DeathFall(); break; + } + if ((eDeathFall!=st_current)&&(eDeathDead!=st_current)){ + // At random times, change the direction (goal) of the plane + if(fGoalChangeTime<=0) { + fGoalChangeTime += fGoalChangeDelta+fGoalChangeDelta*Random.randF(-0.5f,0.5f); + Fvector vP; + vP.set(Device.vCameraPosition.x,Device.vCameraPosition.y+fMinHeight,Device.vCameraPosition.z); + vGoalDir.x = vP.x+vVarGoal.x*Random.randF(-0.5f,0.5f); + vGoalDir.y = vP.y+vVarGoal.y*Random.randF(-0.5f,0.5f); + vGoalDir.z = vP.z+vVarGoal.z*Random.randF(-0.5f,0.5f); + } + fGoalChangeTime -= fDT; + // sounds + if (fIdleSoundTime<=0){ + fIdleSoundTime = fIdleSoundDelta+fIdleSoundDelta*Random.randF(-0.5f,0.5f); + //if (st_current==eFlyIdle) + ::Sound->play_at_pos(m_Sounds.m_idle.GetRandom(),H_Root(),Position()); + } + fIdleSoundTime -= fDT; + } + m_Sounds.m_idle.SetPosition (Position()); + + // work + if (o_workload_rframe == (Device.dwFrame-1)) ; + else UpdateWorkload (fDT); +} + +// Core events +void CAI_Crow::net_Export (NET_Packet& P) // export to server +{ + // export + R_ASSERT (Local()); + + u8 flags = 0; + P.w_float (GetfHealth()); + + P.w_float (0); + P.w_u32 (0); + P.w_u32 (0); + + P.w_u32 (Level().timeServer()); + P.w_u8 (flags); + + float yaw, pitch, bank; + XFORM().getHPB (yaw,pitch,bank); + P.w_float /*w_angle8*/ (yaw); + P.w_float /*w_angle8*/ (yaw); + P.w_float /*w_angle8*/ (pitch); + P.w_float /*w_angle8*/ (0); + P.w_u8 (u8(g_Team())); + P.w_u8 (u8(g_Squad())); + P.w_u8 (u8(g_Group())); +} +//--------------------------------------------------------------------- +void CAI_Crow::net_Import (NET_Packet& P) +{ + // import + R_ASSERT (Remote()); + + u8 flags; + + float health; + P.r_float (health); + SetfHealth (health); + + float fDummy; + u32 dwDummy; + P.r_float (fDummy); + P.r_u32 (dwDummy); + P.r_u32 (dwDummy); + + P.r_u32 (dwDummy); + P.r_u8 (flags); + + float yaw, pitch, bank = 0, roll = 0; + + P.r_float /*r_angle8*/ (yaw); + P.r_float /*r_angle8*/ (yaw); + P.r_float /*r_angle8*/ (pitch); + P.r_float /*r_angle8*/ (roll); + + id_Team = P.r_u8(); + id_Squad = P.r_u8(); + id_Group = P.r_u8(); + + XFORM().setHPB (yaw,pitch,bank); +} +//--------------------------------------------------------------------- +void CAI_Crow::HitSignal (float /**HitAmount/**/, Fvector& /**local_dir/**/, CObject* who, s16 /**element/**/) +{ + //bool first_time = !!g_Alive(); +// bool first_time = !PPhysicsShell(); + SetfHealth (0); + //set_death_time () ; + if (eDeathDead!=st_current) + { +// if (first_time) Die (who); + st_target = eDeathFall; + } + else smart_cast(Visual())->PlayCycle(m_Anims.m_death_dead.GetRandom()); +} +//--------------------------------------------------------------------- +void CAI_Crow::HitImpulse (float /**amount/**/, Fvector& /**vWorldDir/**/, Fvector& /**vLocalDir/**/) +{ +} +//--------------------------------------------------------------------- +void CAI_Crow::CreateSkeleton() +{ + m_pPhysicsShell=P_build_SimpleShell(this,0.3f,false); + m_pPhysicsShell->SetMaterial(smart_cast(Visual())->LL_GetData(smart_cast(Visual())->LL_GetBoneRoot()).game_mtl_idx); +} + +//void CAI_Crow::Hit (float P, Fvector &dir, CObject* who, s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type) +void CAI_Crow::Hit (SHit* pHDS) +{ +// inherited::Hit (P,dir,who,element,p_in_object_space,impulse/100.f, hit_type); + SHit HDS = *pHDS; + HDS.impulse /= 100.f; + inherited::Hit(&HDS); +} + +BOOL CAI_Crow::UsedAI_Locations() +{ + return (FALSE); +} + +void CAI_Crow::create_physic_shell() +{ + // do not delete!!! +} diff --git a/src/xrGameLA/ai/crow/ai_crow.h b/src/xrGameLA/ai/crow/ai_crow.h new file mode 100644 index 000000000..498d08ba1 --- /dev/null +++ b/src/xrGameLA/ai/crow/ai_crow.h @@ -0,0 +1,140 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_crow.h +// Description : AI Behaviour for monster "Crow" +//////////////////////////////////////////////////////////////////////////// + +#ifndef __XRAY_AI_CROW__ +#define __XRAY_AI_CROW__ + +#include "../../entity_alive.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" + +class CMotionDef; +class CBlend; +class IKinematicsAnimated; + +class CAI_Crow : public CEntity +{ + typedef CEntity inherited; + enum ECrowStates { + eUndef = -1, + eDeathFall = 0, + eDeathDead, + eFlyIdle, + eFlyUp + }; + + // constants and types + enum { MAX_ANIM_COUNT = 8 }; + enum { MAX_SND_COUNT = 8 }; + + // animations + struct SAnim + { + typedef svector MotionSVec; + MotionSVec m_Animations; + const MotionID &GetRandom (){return m_Animations[Random.randI(0,m_Animations.size())];} + void Load (IKinematicsAnimated* visual, LPCSTR prefix); + }; + + struct SSound + { + typedef svector SoundSVec; + SoundSVec m_Sounds; + ref_sound& GetRandom () {return m_Sounds[Random.randI(0,m_Sounds.size())];} + void Load (LPCSTR prefix); + void SetPosition (const Fvector& pos); + void Unload (); + }; +public: + void OnHitEndPlaying (CBlend* B); +protected: + + struct SCrowAnimations + { + SAnim m_idle; + SAnim m_fly; + SAnim m_death; + SAnim m_death_idle; + SAnim m_death_dead; + }; + SCrowAnimations m_Anims; + struct SCrowSounds + { + SSound m_idle; + }; + SCrowSounds m_Sounds; + + Fvector vOldPosition; + ECrowStates st_current, st_target; + // parameters block + Fvector vGoalDir; + Fvector vCurrentDir; + Fvector vHPB; + float fDHeading; + + // constants + float fGoalChangeDelta; + float fSpeed; + float fASpeed; + float fMinHeight; + Fvector vVarGoal; + float fIdleSoundDelta; + + // variables + float fGoalChangeTime; + float fIdleSoundTime; + + // + bool bPlayDeathIdle; + + void switch2_FlyUp (); + void switch2_FlyIdle (); + void switch2_DeathFall (); + void switch2_DeathDead (); + + void state_DeathFall (); + void state_Flying (float dt); + + void CreateSkeleton (); + + void UpdateWorkload (float DT); + +public: + u32 o_workload_frame ; + u32 o_workload_rframe ; +public: + CAI_Crow(); + virtual ~CAI_Crow(); + virtual void Load ( LPCSTR section ); + void init (); + virtual BOOL net_Spawn ( CSE_Abstract* DC ); + virtual void net_Destroy (); + virtual BOOL renderable_ShadowGenerate () { return FALSE; } + virtual BOOL renderable_ShadowReceive () { return FALSE; } + virtual void renderable_Render (); + virtual void shedule_Update (u32 DT); + virtual void UpdateCL (); + + virtual CEntity*cast_entity () {return this;} + + virtual void net_Export (NET_Packet& P); + virtual void net_Import (NET_Packet& P); + + virtual void g_fireParams (const CHudItem* /**pHudItem/**/, Fvector& /**P/**/, Fvector& /**D/**/) {}; + virtual void g_WeaponBones (int &/**L/**/, int &/**R1/**/, int &/**R2/**/) {}; + + virtual void HitSignal (float HitAmount, Fvector& local_dir, CObject* who, s16 element); + virtual void HitImpulse (float amount, Fvector& vWorldDir, Fvector& vLocalDir); + virtual void Hit (SHit* pHDS); + virtual void Die (CObject* who); + virtual float ffGetFov () const {return 150.f; } + virtual float ffGetRange () const {return 30.f; } + + virtual BOOL IsVisibleForHUD () { return FALSE; } + virtual bool IsVisibleForZones() { return false; } + virtual BOOL UsedAI_Locations() ; + virtual void create_physic_shell () ; +}; + +#endif \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/ai_monster_bones.cpp b/src/xrGameLA/ai/monsters/ai_monster_bones.cpp new file mode 100644 index 000000000..7a1c4e123 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_bones.cpp @@ -0,0 +1,169 @@ +#include "stdafx.h" +#include "ai_monster_bones.h" +#include "../../../../Include/xrRender/Kinematics.h" +#include "../../../bone.h" + + +//**************************************************************************************************** +// class bonesBone +//**************************************************************************************************** +void bonesBone::Set(CBoneInstance *b, u8 a, float ty, float cy, float r_s) +{ + bone = b; + axis = a; + + params.target_yaw = ty; + params.cur_yaw = cy; + params.r_speed = r_s; + params.dist_yaw = _abs(ty-cy); +} + + +bool bonesBone::NeedTurn() +{ + if (!fsimilar(params.cur_yaw, params.target_yaw, EPS_L)) return true; + return false; +} + +void bonesBone::Turn(u32 dt) +{ + float PI_DIV_2m = 8 * PI_DIV_6 / 3; + float PIm = PI_DIV_2m * 2; + + float cur_speed = params.r_speed * _cos(PI_DIV_2m - PIm * _abs(params.target_yaw - params.cur_yaw) / params.dist_yaw); + + float dy; + dy = cur_speed * dt / 1000; // учитываем милисек и радианную меры + + if (_abs(params.target_yaw - params.cur_yaw) < dy) params.cur_yaw = params.target_yaw; + else params.cur_yaw += ((params.target_yaw > params.cur_yaw) ? dy : -dy); + +} + +void bonesBone::Apply() +{ + float x = 0.f, y = 0.f, z = 0.f; + + if ((axis & AXIS_X) == AXIS_X) x = params.cur_yaw; + if ((axis & AXIS_Y) == AXIS_Y) y = params.cur_yaw; + if ((axis & AXIS_Z) == AXIS_Z) z = params.cur_yaw; + + // создать матрицу вращения и умножить на mTransform боны + Fmatrix M; + M.setHPB (-y, -x, -z); + bone->mTransform.mulB_43 (M); +} + +//**************************************************************************************************** +// class bonesManipulation +//**************************************************************************************************** + +void bonesManipulation::Reset() +{ + time_started = 0; + time_last_update = 0; + in_return_state = false; + bActive = false; + freeze_time = 0; + time_last_delta = 1; +} + +void bonesManipulation::AddBone (CBoneInstance *bone, u8 axis_used) +{ + bonesBone tempB; + + tempB.Set(bone, axis_used,0.f,0.f,1.f); + + m_Bones.push_back(tempB); +} + +void bonesManipulation::SetMotion(CBoneInstance *bone, u8 axis, float target_yaw, float r_speed, u32 t) +{ + int index = -1; + // найти бону bone в m_Bones + for (u32 i=0; i freeze_time) freeze_time = t; + + bActive = true; + in_return_state = false; + time_started = 0; +} + + + +void bonesManipulation::Update(CBoneInstance *bone, u32 cur_time) +{ + // провести обработку всех костей + bool bones_were_turned = false; + + // вычисление dt + u32 dt; + if (cur_time == time_last_update) { + dt = time_last_delta; + } else dt = cur_time - time_last_update; + time_last_delta = dt; + time_last_update = cur_time; + + for (u32 i=0; i 0)) { // начинаем ждать + time_started = cur_time; + } + + if ((0 != time_started) && (time_started + freeze_time < cur_time)) { // время вышло? + time_started = 0; + + // делаем возврат + in_return_state = true; + // установить у всех костей в m_Bone таргеты в 0 + for (u32 i = 0; i m_Bones; + u32 freeze_time; + + bool in_return_state; // если идёт возврат к исходному положению + u32 time_started; + u32 time_last_update; + u32 time_last_delta; + + bool bActive; +public: + void Reset (); + + void AddBone (CBoneInstance *bone, u8 axis_used); + void SetMotion (CBoneInstance *bone, u8 axis_used, float target_yaw, float r_speed, u32 t); + + void Update (CBoneInstance *bone, u32 cur_time); + bool IsActive () {return bActive;} + bool IsReturn () {return in_return_state;} + + bonesAxis &GetBoneParams (CBoneInstance *bone, u8 axis_used); +}; + + diff --git a/src/xrGameLA/ai/monsters/ai_monster_defs.h b/src/xrGameLA/ai/monsters/ai_monster_defs.h new file mode 100644 index 000000000..e6cba6529 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_defs.h @@ -0,0 +1,513 @@ +#pragma once + +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../cameramanager.h" + +typedef u32 TTime; + +#define COLOR D3DCOLOR_XRGB +#define COLOR_RED COLOR(255,0,0) +#define COLOR_GREEN COLOR(0,255,0) +#define COLOR_BLUE COLOR(0,0,255) + +class CBlend; + +// специальные параметры анимаций (animation spec params) +#define ASP_MOVE_BKWD (1 << 0) +#define ASP_DRAG_CORPSE (1 << 1) +#define ASP_CHECK_CORPSE (1 << 2) +#define ASP_ATTACK_RAT (1 << 3) +#define ASP_ATTACK_RAT_JUMP (1 << 4) +#define ASP_STAND_SCARED (1 << 5) +#define ASP_THREATEN (1 << 6) +#define ASP_BACK_ATTACK (1 << 7) +#define ASP_ROTATION_JUMP (1 << 8) +#define ASP_ROTATION_RUN_LEFT (1 << 9) +#define ASP_ROTATION_RUN_RIGHT (1 << 10) +#define ASP_ATTACK_RUN (1 << 11) +#define ASP_PSI_ATTACK (1 << 12) +#define ASP_UPPER_STATE (1 << 13) +#define ASP_MOVE_SMELLING (1 << 14) + +#define AA_FLAG_ATTACK_RAT (1 << 0) // аттака крыс? +#define AA_FLAG_FIRE_ANYWAY (1 << 1) // трассировка не нужна + +#define CRITICAL_STAND_TIME 1400 +#define TIME_STAND_RECHECK 2000 + + + +#define HIT_SIDE_COUNT 2 +#define HIT_BACK 0 +#define HIT_FRONT 1 + +#define HIT_HEIGHT_COUNT 2 +#define HIT_LOW 0 +#define HIT_HIGH 1 + + +// Enemy flags +#define FLAG_ENEMY_DIE ( 1 << 0 ) +#define FLAG_ENEMY_LOST_SIGHT ( 1 << 1 ) +#define FLAG_ENEMY_GO_CLOSER ( 1 << 2 ) +#define FLAG_ENEMY_GO_FARTHER ( 1 << 3 ) +#define FLAG_ENEMY_GO_CLOSER_FAST ( 1 << 4 ) +#define FLAG_ENEMY_GO_FARTHER_FAST ( 1 << 5 ) +#define FLAG_ENEMY_STANDING ( 1 << 6 ) +#define FLAG_ENEMY_HIDING ( 1 << 7 ) +#define FLAG_ENEMY_RUN_AWAY ( 1 << 8 ) +#define FLAG_ENEMY_DOESNT_KNOW_ABOUT_ME ( 1 << 9 ) +#define FLAG_ENEMY_GO_OFFLINE ( 1 << 10 ) +#define FLAG_ENEMY_DOESNT_SEE_ME ( 1 << 11 ) +#define FLAG_ENEMY_STATS_NOT_READY ( 1 << 12 ) + +#define SOUND_ATTACK_HIT_MIN_DELAY 1000 +#define MORALE_NORMAL 0.5f + +#define STANDART_ATTACK -PI_DIV_6,PI_DIV_6,-PI_DIV_6,PI_DIV_6,3.5f +#define SIMPLE_ENEMY_HIT_TEST + + +// StepSounds +struct SStepSound { + float vol; + float freq; +}; + +struct SAttackEffector { + SPPInfo ppi; + float time; + float time_attack; + float time_release; + + // camera effects + float ce_time; + float ce_amplitude; + float ce_period_number; + float ce_power; +}; + +struct SVelocityParam { + struct { + float linear; + float angular_path; + float angular_real; + } velocity; + float min_factor; + float max_factor; + + SVelocityParam() { + velocity.linear = 0.f; + velocity.angular_real = 0.f; + velocity.angular_path = 0.f; + min_factor = 1.0f; + max_factor = 1.0f; + } + + void Load (LPCSTR section, LPCSTR line) { + string32 buffer; + velocity.linear = float(atof(_GetItem(pSettings->r_string(section,line),0,buffer))); + velocity.angular_real = float(atof(_GetItem(pSettings->r_string(section,line),1,buffer))); + velocity.angular_path = float(atof(_GetItem(pSettings->r_string(section,line),2,buffer))); + min_factor = float(atof(_GetItem(pSettings->r_string(section,line),3,buffer))); + max_factor = float(atof(_GetItem(pSettings->r_string(section,line),4,buffer))); + } +}; + + +// Activities +enum EMotionAnim { + eAnimStandIdle = u32(0), + eAnimCapturePrepare, + eAnimStandTurnLeft, + eAnimStandTurnRight, + + eAnimSitIdle, + eAnimLieIdle, + + eAnimSitToSleep, + eAnimLieToSleep, + eAnimStandSitDown, + eAnimStandLieDown, + eAnimLieStandUp, + eAnimSitStandUp, + eAnimStandLieDownEat, + eAnimSitLieDown, + eAnimLieSitUp, + eAnimSleepStandUp, + + eAnimWalkFwd, + eAnimWalkBkwd, + eAnimWalkTurnLeft, + eAnimWalkTurnRight, + + eAnimRun, + eAnimRunTurnLeft, + eAnimRunTurnRight, + eAnimFastTurn, + + eAnimAttack, + eAnimAttackFromBack, + eAnimAttackRun, + + eAnimEat, + eAnimSleep, + eAnimSleepStanding, + eAnimDie, + + eAnimDragCorpse, + eAnimCheckCorpse, + eAnimScared, + eAnimAttackJump, + + eAnimLookAround, + + eAnimPrepareAttack, + eAnimJump, + eAnimSteal, + + eAnimJumpStart, + eAnimJumpGlide, + eAnimJumpFinish, + + eAnimJumpLeft, + eAnimJumpRight, + + eAnimStandDamaged, + eAnimWalkDamaged, + eAnimRunDamaged, + + eAnimSniff, + eAnimHowling, + eAnimThreaten, + + eAnimMiscAction_00, + eAnimMiscAction_01, + + eAnimUpperStandIdle, + eAnimUpperStandTurnLeft, + eAnimUpperStandTurnRight, + + eAnimStandToUpperStand, + eAnimUppperStandToStand, + + eAnimUpperWalkFwd, + eAnimUpperThreaten, + eAnimUpperAttack, + + eAnimAttackPsi, + + eAnimTeleRaise, + eAnimTeleFire, + eAnimGraviPrepare, + eAnimGraviFire, + eAnimShieldStart, + eAnimShieldContinue, + + eAnimTelekinesis, + + //mob home animations + /*eAnimHomeIdleDigGround, + eAnimHomeIdleHowl, + eAnimHomeIdleShake, + eAnimHomeIdleSmellingUp, + eAnimHomeIdleSmellingDown, + eAnimHomeIdleSmellingLookAround, + eAnimHomeIdleGrowl,*/ + eAnimHomeWalkGrowl, + eAnimHomeWalkSmelling, + + + //end mob home animations + + eAnimAttackOnRunLeft, + eAnimAttackOnRunRight, + + eAnimAntiAimAbility, + eAnimFastStandTurnLeft, + eAnimFastStandTurnRight, + + eAnimCount, + eAnimUndefined = u32(-1) +}; + +// Generic actions +enum EAction { + ACT_STAND_IDLE = u32(0), + ACT_SIT_IDLE, + ACT_LIE_IDLE, + ACT_WALK_FWD, + ACT_WALK_BKWD, + ACT_RUN, + ACT_CAPTURE_PREPARE, + ACT_EAT, + ACT_SLEEP, + ACT_REST, + ACT_DRAG, + ACT_ATTACK, + ACT_STEAL, + ACT_LOOK_AROUND, + ACT_HOME_WALK_GROWL, + ACT_HOME_WALK_SMELLING, + ACT_NONE = u32(-1) +}; + +enum EPState { + PS_STAND, + PS_SIT, + PS_LIE, + PS_STAND_UPPER +}; + +typedef shared_str anim_string; +#define DEFAULT_ANIM eAnimStandIdle + +// элемент анимации +struct SAnimItem { + + anim_string target_name; // "stand_idle_" + int spec_id; // (-1) - any, (0 - ...) - идентификатор 3 + u8 count; // количество анимаций : "idle_0", "idle_1", "idle_2" + + SVelocityParam velocity; + + EPState pos_state; + + struct { + anim_string front; + anim_string back; + anim_string left; + anim_string right; + } fxs; +}; + +#define SKIP_IF_AGGRESSIVE true + +// описание перехода +struct STransition { + + struct { + bool state_used; + EMotionAnim anim; + EPState state; + } from, target; + + EMotionAnim anim_transition; + bool chain; + bool skip_if_aggressive; +}; + +// элемент движения +struct SMotionItem { + EMotionAnim anim; + bool is_turn_params; + + struct{ + EMotionAnim anim_left; // speed, r_speed got from turn_left member + EMotionAnim anim_right; + float min_angle; + } turn; +}; + +// подмена анимаций (если *flag == true, то необходимо заменить анимацию) +struct SReplacedAnim { + EMotionAnim cur_anim; + EMotionAnim new_anim; + bool *flag; +}; + +// Определение времени аттаки по анимации +typedef struct { + EMotionAnim anim; // параметры конкретной анимации + u32 anim_i3; + + TTime time_from; // диапазон времени когда можно наносить hit (от) + TTime time_to; // диапазон времени когда можно наносить hit (до) + + Fvector trace_from; // направление трассировки (относительно центра) + Fvector trace_to; + + u32 flags; // специальные флаги + + float damage; // урон при данной атаке + Fvector hit_dir; // угол направления приложения силы к объекту + + //----------------------------------------- + // temp + float yaw_from; + float yaw_to; + float pitch_from; + float pitch_to; + float dist; + +} SAttackAnimation; + + +struct SAAParam { + MotionID motion; + float time; + float hit_power; // damage + float impulse; + Fvector impulse_dir; + + // field of hit + struct { + float from_yaw; + float to_yaw; + float from_pitch; + float to_pitch; + } foh; + + float dist; +}; + +DEFINE_VECTOR(SAAParam, AA_VECTOR, AA_VECTOR_IT); + +struct SCurrentAnimationInfo { + shared_str name; + + EMotionAnim motion; + u8 index; + + TTime time_started; + + struct { + IC void _set_current (float v) { current=v; VERIFY2(_abs(v)<1000,"_set_current(). monster speed is too big"); } + IC void _set_target (float v) { target=v; VERIFY2(_abs(v)<1000,"_set_target(). monster speed is too big");} + IC float _get_current () { return current; } + IC float _get_target () { return target; } + private: + float current; + float target; + } speed; + + float speed_change_vel; + CBlend *blend; +}; + + + +////////////////////////////////////////////////////////////////////////// + +struct t_fx_index { + s8 front; + s8 back; +}; + +enum EHitSide { + eSideFront = u32(0), + eSideBack, + eSideLeft, + eSideRight +}; + + +DEFINE_VECTOR (SAnimItem*, ANIM_ITEM_VECTOR, ANIM_ITEM_VECTOR_IT); +DEFINE_VECTOR (STransition, TRANSITION_ANIM_VECTOR, TRANSITION_ANIM_VECTOR_IT); +DEFINE_MAP (EAction, SMotionItem, MOTION_ITEM_MAP, MOTION_ITEM_MAP_IT); +DEFINE_VECTOR (EMotionAnim, SEQ_VECTOR, SEQ_VECTOR_IT); +DEFINE_VECTOR (SAttackAnimation, ATTACK_ANIM, ATTACK_ANIM_IT); +DEFINE_VECTOR (SReplacedAnim, REPLACED_ANIM, REPLACED_ANIM_IT); + +DEFINE_MAP (u16, t_fx_index, FX_MAP_U16, FX_MAP_U16_IT); +DEFINE_MAP (shared_str, t_fx_index, FX_MAP_STRING, FX_MAP_STRING_IT); + + +DEFINE_VECTOR (SEQ_VECTOR, VELOCITY_CHAIN_VEC, VELOCITY_CHAIN_VEC_IT); + + +struct SVelocity { + float current; + float target; + + void set (float c, float t) {current = c; target = t;} +}; + +struct SMotionVel { + float linear; + float angular; + void set (float l, float a) {linear = l; angular = a;} +}; + +enum EAccelType { + eAT_Calm, + eAT_Aggressive +}; + +enum EAccelValue { + eAV_Accel, + eAV_Braking +}; + + +#define deg(x) (x * PI / 180) + + +/////////////////////////////////////////////////////////////////////////////// +// State Management +#define DO_ONCE_BEGIN(flag) if (!flag) {flag = true; +#define DO_ONCE_END() } + +#define TIME_OUT(a,b) a+bIsPathEnd(2,0.5f) + + +// тип монстра (по количеству ног) +#define QUADRUPEDAL 4 +#define BIPEDAL 2 + + +struct SMonsterEnemy { + Fvector position; + u32 vertex; + TTime time; + float danger; +}; + +class CEntityAlive; + +DEFINE_MAP(const CEntityAlive *,SMonsterEnemy,ENEMIES_MAP, ENEMIES_MAP_IT); + +struct SMonsterCorpse { + Fvector position; + u32 vertex; + TTime time; +}; + +DEFINE_MAP(const CEntityAlive *,SMonsterCorpse,CORPSE_MAP, CORPSE_MAP_IT); + + + +struct SMonsterHit { + CObject *object; + TTime time; + EHitSide side; + Fvector position; + + bool operator==(const CObject *obj) { + return (object == obj); + } +}; + +DEFINE_VECTOR(SMonsterHit,MONSTER_HIT_VECTOR, MONSTER_HIT_VECTOR_IT); + + +enum EDangerType { + eWeak, + eNormal, + eStrong, + eVeryStrong, + eNone +}; + +DEFINE_MAP(MotionID, shared_str, ANIM_TO_MOTION_MAP, ANIM_TO_MOTION_MAP_IT); + + + + diff --git a/src/xrGameLA/ai/monsters/ai_monster_effector.cpp b/src/xrGameLA/ai/monsters/ai_monster_effector.cpp new file mode 100644 index 000000000..b959efc13 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_effector.cpp @@ -0,0 +1,100 @@ +#include "stdafx.h" +#include "ai_monster_effector.h" + +#define MONSTER_EFFECTOR_TYPE_ID 5 + +////////////////////////////////////////////////////////////////////////// +// CMonsterEffector +////////////////////////////////////////////////////////////////////////// +CMonsterEffector::CMonsterEffector(const SPPInfo &ppi, float life_time, float attack_time, float release_time, float spec_factor) : + CEffectorPP(EEffectorPPType(eCEHit), life_time) +{ + state = ppi; + m_total = life_time; + + m_attack = ((fis_zero(attack_time)) ? 0.5f : attack_time); + m_release = ((fis_zero(release_time)) ? 0.5f : release_time); + + m_spec_factor = spec_factor; + + VERIFY(!fsimilar(m_release, 1.0f)); + VERIFY(!fis_zero(m_attack)); +} + +BOOL CMonsterEffector::Process(SPPInfo& pp) +{ + inherited::Process(pp); + + // amount of time passed in percents + float time_past_perc = (m_total - fLifeTime) / m_total; + + float factor; + if (time_past_perc < m_attack) + factor = time_past_perc / m_attack; + else if ((time_past_perc >= m_attack) && (time_past_perc <= m_release)) + factor = 1.0f; + else + factor = (1.0f - time_past_perc) / (1.0f - m_release); + + clamp(factor,0.01f,1.0f); + + pp.lerp (pp_identity, state, factor * m_spec_factor); + return TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +// CMonsterEffectorHit +////////////////////////////////////////////////////////////////////////// + +CMonsterEffectorHit::CMonsterEffectorHit(float time, float amp, float periods, float power) + : CEffectorCam(eCEMonsterHit, time) +{ + SetHudAffect (false); + + total = time; + + max_amp = amp * power; + period_number = periods; + this->power = power; + + offset.set (Random.randF(1,2),Random.randF(1,6),Random.randF(1,6)); +} + +BOOL CMonsterEffectorHit::ProcessCam (SCamEffectorInfo& info) +{ + fLifeTime -= Device.fTimeDelta; if(fLifeTime<0) return FALSE; + + // процент оставшегося времени + float time_left_perc = fLifeTime / total; + + // Инициализация + Fmatrix Mdef; + Mdef.identity (); + Mdef.j.set (info.n); + Mdef.k.set (info.d); + Mdef.i.crossproduct (info.n,info.d); + Mdef.c.set (info.p); + + float period_all = period_number * PI_MUL_2; // макс. значение цикла + float cur_amp = max_amp * (PI / 180) * time_left_perc; + + + Fvector dangle; + dangle.x = cur_amp/offset.x * _sin(period_all/offset.x * (1.0f - time_left_perc)); + dangle.y = cur_amp/offset.y * _cos(period_all/offset.y * (1.0f - time_left_perc)); + dangle.z = cur_amp/offset.z * _sin(period_all/offset.z * (1.0f - time_left_perc)); + + // Установить углы смещения + Fmatrix R; + R.setHPB (dangle.x,dangle.y,dangle.z); + + Fmatrix mR; + mR.mul (Mdef,R); + + info.d.set (mR.k); + info.n.set (mR.j); + + return TRUE; +} + diff --git a/src/xrGameLA/ai/monsters/ai_monster_effector.h b/src/xrGameLA/ai/monsters/ai_monster_effector.h new file mode 100644 index 000000000..1d80a37a1 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_effector.h @@ -0,0 +1,41 @@ +#pragma once + +#include "../../ActorEffector.h" + +////////////////////////////////////////////////////////////////////////// +// CMonsterEffector +////////////////////////////////////////////////////////////////////////// +class CMonsterEffector : public CEffectorPP +{ + typedef CEffectorPP inherited; + + SPPInfo state; // current state + float m_total; // total PP time + float m_attack; // attack time in percents [0..1] + float m_release; // release time in percents [0..1] + + float m_spec_factor; + +public: + CMonsterEffector (const SPPInfo &ppi, float life_time, float attack_time = 0.0f, float release_time = 0.0f, float spec_factor = 1.f); + virtual BOOL Process (SPPInfo& pp); +}; + +////////////////////////////////////////////////////////////////////////// +// CMonsterEffectorHit +////////////////////////////////////////////////////////////////////////// +class CMonsterEffectorHit : public CEffectorCam +{ + + float total; + float max_amp; + float period_number; + float power; + + Fvector offset; +public: + CMonsterEffectorHit (float time, float amp, float periods, float power); + virtual BOOL ProcessCam (SCamEffectorInfo& info); +}; + + diff --git a/src/xrGameLA/ai/monsters/ai_monster_motion_stats.cpp b/src/xrGameLA/ai/monsters/ai_monster_motion_stats.cpp new file mode 100644 index 000000000..b23fd6a82 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_motion_stats.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include "ai_monster_motion_stats.h" +#include "BaseMonster/base_monster.h" + +void CMotionStats::update() +{ + elem _new; + _new.speed = pMonster->control().movement().velocity_current(); + _new.position = pMonster->Position(); + _new.time = pMonster->m_dwCurrentTime; + _data[index] = _new; + + // обновить значение index + if ((index+1) >= MAX_ELEMS) { + for (u32 i=0; i < (MAX_ELEMS-1); i++) { + _data[i] = _data[i+1]; + } + } else index++; +} + +bool CMotionStats::is_good_motion(u32 elems_checked) +{ + u32 from_index; + u32 to_index; + + if (index == 0) return true; + else from_index = index-1; + + if (s32(index - elems_checked) < 0) return true; + else to_index = index - elems_checked; + + bool bGood = true; + float test_speed = _data[from_index].speed; + + for (u32 i=from_index; i>to_index;i--) { + + // считать только, если все элементы содержат одинаковые скорости + if (!fsimilar(test_speed,_data[i].speed)) break; + + float cur_dist = _data[i].position.distance_to(_data[i-1].position); + TTime delta_t = _data[i].time - _data[i-1].time; + float speed = cur_dist * 1000.f / float(delta_t); + + if (fsimilar(_data[i-1].speed,0.0f)) continue; + + if (speed * 5.f < _data[i].speed) { + bGood = false; + break; + } + } + return bGood; +} + diff --git a/src/xrGameLA/ai/monsters/ai_monster_motion_stats.h b/src/xrGameLA/ai/monsters/ai_monster_motion_stats.h new file mode 100644 index 000000000..3ecb66439 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_motion_stats.h @@ -0,0 +1,25 @@ +#pragma once +#include "ai_monster_defs.h" + +class CBaseMonster; + +class CMotionStats { + CBaseMonster *pMonster; + + struct elem { + float speed; + Fvector position; + TTime time; + }; + + enum {MAX_ELEMS = 10}; + + elem _data[MAX_ELEMS]; + u32 index; // индекс всегда указывает на подготовленное место в массиве + +public: + CMotionStats(CBaseMonster *pM) : pMonster(pM),index(0) {}; + + void update (); + bool is_good_motion (u32 elems_checked); +}; diff --git a/src/xrGameLA/ai/monsters/ai_monster_shared_data.h b/src/xrGameLA/ai/monsters/ai_monster_shared_data.h new file mode 100644 index 000000000..060c1a3f8 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_shared_data.h @@ -0,0 +1,41 @@ +#pragma once +#include "ai_monster_defs.h" + +struct SMonsterSettings { + + // float speed factors + + float m_fDistToCorpse; + float m_fDamagedThreshold; // порог здоровья, ниже которого устанавливается флаг m_bDamaged + + // ------------------------------------------------------- + + u32 m_dwIdleSndDelay; + u32 m_dwEatSndDelay; + u32 m_dwAttackSndDelay; + + u32 m_dwDistantIdleSndDelay; + float m_fDistantIdleSndRange; + + // ------------------------------------------------------- + + u32 m_dwDayTimeBegin; + u32 m_dwDayTimeEnd; + float satiety_threshold; + + // ----------------------------------------------------------- + + float m_fSoundThreshold; + + float m_fEatFreq; + float m_fEatSlice; + float m_fEatSliceWeight; + + u8 m_legs_number; + SAttackEffector m_attack_effector; + + float m_max_hear_dist; + + float m_run_attack_path_dist; + float m_run_attack_start_dist; +}; diff --git a/src/xrGameLA/ai/monsters/ai_monster_squad.cpp b/src/xrGameLA/ai/monsters/ai_monster_squad.cpp new file mode 100644 index 000000000..9b290e429 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_squad.cpp @@ -0,0 +1,218 @@ +#include "stdafx.h" +#include "ai_monster_squad.h" +#include "../../entity.h" + +CMonsterSquad::CMonsterSquad() : leader(0) +{ + m_locked_covers.reserve (20); + m_locked_corpses.reserve(10); +} + +CMonsterSquad::~CMonsterSquad() +{ +} + +void CMonsterSquad::RegisterMember(CEntity *pE) +{ + // Добавить цель + SMemberGoal G; + m_goals.insert (mk_pair(pE, G)); + + // Добавить команду + SSquadCommand C; + C.type = SC_NONE; + m_commands.insert (mk_pair(pE, C)); + + // установить лидера + if (!leader) leader = pE; +} + +void CMonsterSquad::RemoveMember(CEntity *pE) +{ + // удалить из целей + MEMBER_GOAL_MAP_IT it_goal = m_goals.find(pE); + if (it_goal == m_goals.end()) return; + m_goals.erase(it_goal); + + // удалить из команд + MEMBER_COMMAND_MAP_IT it_command = m_commands.find(pE); + if (it_command == m_commands.end()) return; + m_commands.erase(it_command); + + // если удаляемый елемент является лидером - переназначить лидера + if (leader == pE) { + if (m_goals.empty()) leader = 0; + else leader = m_goals.begin()->first; + } + + // усли последний элемент, очистить залоченные каверы + if (m_goals.empty()) { + m_locked_covers.clear (); + m_locked_corpses.clear (); + } +} + +bool CMonsterSquad::SquadActive() +{ + if (!leader) return false; + + // проверить количество живых объектов в группе + u32 alive_num = 0; + for (MEMBER_GOAL_MAP_IT it = m_goals.begin(); it != m_goals.end(); it++) + if (it->first->g_Alive()) alive_num++; + + if (alive_num < 2) return false; + + return true; +} + + +void CMonsterSquad::UpdateGoal(CEntity *pE, const SMemberGoal &goal) +{ + MEMBER_GOAL_MAP_IT it = m_goals.find(pE); + VERIFY(it != m_goals.end()); + + it->second = goal; +} + +void CMonsterSquad::UpdateCommand(const CEntity *pE, const SSquadCommand &com) +{ + MEMBER_COMMAND_MAP_IT it = m_commands.find(pE); + VERIFY(it != m_commands.end()); + + it->second = com; +} + +SMemberGoal &CMonsterSquad::GetGoal(CEntity *pE) +{ + MEMBER_GOAL_MAP_IT it = m_goals.find(pE); + VERIFY(it != m_goals.end()); + + return it->second; +} + +SSquadCommand &CMonsterSquad::GetCommand(CEntity *pE) +{ + MEMBER_COMMAND_MAP_IT it = m_commands.find(pE); + VERIFY(it != m_commands.end()); + return it->second; +} + +void CMonsterSquad::GetGoal(CEntity *pE, SMemberGoal &goal) +{ + goal = GetGoal(pE); +} + +void CMonsterSquad::GetCommand(CEntity *pE, SSquadCommand &com) +{ + com = GetCommand(pE); +} + +void CMonsterSquad::UpdateSquadCommands() +{ + // Отменить все команды в группе + for (MEMBER_COMMAND_MAP_IT it = m_commands.begin(); it != m_commands.end(); it++) { + it->second.type = SC_NONE; + } + + // Удалить все цели, объекты которых невалидны или ушли в оффлайн + for (MEMBER_GOAL_MAP_IT it_goal = m_goals.begin(); it_goal != m_goals.end(); ++it_goal) { + SMemberGoal goal = it_goal->second; + if (!goal.entity || goal.entity->getDestroy()) { + it_goal->second.type = MG_None; + } + } + + ProcessAttack (); + ProcessIdle (); +} + +void CMonsterSquad::remove_links(CObject *O) +{ + // Удалить все цели, объекты которых невалидны или ушли в оффлайн + for (MEMBER_GOAL_MAP_IT it_goal = m_goals.begin(); it_goal != m_goals.end(); ++it_goal) { + SMemberGoal goal = it_goal->second; + if (goal.entity == O) { + it_goal->second.entity = 0; + it_goal->second.type = MG_None; + } + } + + // Удалить все цели, объекты которых невалидны или ушли в оффлайн + for (MEMBER_COMMAND_MAP_IT it = m_commands.begin(); it != m_commands.end(); it++) { + SSquadCommand com = it->second; + if (com.entity == O) { + it->second.entity = 0; + it->second.type = SC_NONE; + } + } +} + + +bool CMonsterSquad::is_locked_cover(u32 node) +{ + return ( + std::find( + m_locked_covers.begin(), + m_locked_covers.end(), + node + ) + != + m_locked_covers.end() + ); +} + +void CMonsterSquad::lock_cover(u32 node) +{ + m_locked_covers.push_back(node); +} + +void CMonsterSquad::unlock_cover(u32 node) +{ + NODES_VECTOR_IT it = std::find(m_locked_covers.begin(), m_locked_covers.end(), node); + if (it != m_locked_covers.end()) + m_locked_covers.erase(it); +} + +u8 CMonsterSquad::get_count(const CEntity *object, float radius) +{ + u8 count = 0; + + for (MEMBER_GOAL_MAP_IT it_goal = m_goals.begin(); it_goal != m_goals.end(); ++it_goal) { + SMemberGoal goal = it_goal->second; + if ((goal.entity != 0) && (goal.entity != object) && (goal.entity->g_Alive())) { + if (goal.entity->Position().distance_to(object->Position()) < radius) count++; + } + } + + return count; +} + +////////////////////////////////////////////////////////////////////////// +// Corpses +////////////////////////////////////////////////////////////////////////// +bool CMonsterSquad::is_locked_corpse(const CEntityAlive *corpse) +{ + return ( + std::find( + m_locked_corpses.begin(), + m_locked_corpses.end(), + corpse + ) + != + m_locked_corpses.end() + ); +} + +void CMonsterSquad::lock_corpse(const CEntityAlive *corpse) +{ + m_locked_corpses.push_back(corpse); +} + +void CMonsterSquad::unlock_corpse(const CEntityAlive *corpse) +{ + CORPSES_VECTOR_IT it = std::find(m_locked_corpses.begin(), m_locked_corpses.end(), corpse); + if (it != m_locked_corpses.end()) + m_locked_corpses.erase(it); +} +////////////////////////////////////////////////////////////////////////// diff --git a/src/xrGameLA/ai/monsters/ai_monster_squad.h b/src/xrGameLA/ai/monsters/ai_monster_squad.h new file mode 100644 index 000000000..63b6a9e4e --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_squad.h @@ -0,0 +1,183 @@ +#pragma once +class CEntity; +class CEntityAlive; +class CBaseMonster; +////////////////////////////////////////////////////////////////////////// +// Member local goal notification +////////////////////////////////////////////////////////////////////////// +enum EMemberGoalType +{ + MG_AttackEnemy, // entity + MG_PanicFromEnemy, // entity + MG_InterestingSound, // position + MG_DangerousSound, // position + MG_WalkGraph, // node + MG_Rest, // node, position + MG_None, +}; + +struct SMemberGoal +{ + EMemberGoalType type; + CEntity *entity; + Fvector position; + u32 node; + + SMemberGoal () { + type = MG_None; + entity = 0; + } +}; + +////////////////////////////////////////////////////////////////////////// +// Squad command +////////////////////////////////////////////////////////////////////////// +enum ESquadCommandType +{ + SC_EXPLORE, + SC_ATTACK, + SC_THREATEN, + SC_COVER, + SC_FOLLOW, + SC_FEEL_DANGER, + SC_EXPLICIT_ACTION, + SC_REST, + SC_NONE, +}; + +struct SSquadCommand +{ + ESquadCommandType type; // тип команды + + const CEntity *entity; + Fvector position; + u32 node; + Fvector direction; + +}; + + +///////////////////////////////////////////////////////////////////////////////////////// +// MonsterSquad Class +class CMonsterSquad +{ +public: + DEFINE_MAP (const CEntity*, SSquadCommand, MEMBER_COMMAND_MAP, MEMBER_COMMAND_MAP_IT); + +private: + CEntity *leader; + DEFINE_MAP (CEntity*, SMemberGoal, MEMBER_GOAL_MAP, MEMBER_GOAL_MAP_IT); + + // карта целей членов группы (обновляется со стороны объекта) + MEMBER_GOAL_MAP m_goals; + + // карта комманд членов группы (обновляется со стороны squad manager) + MEMBER_COMMAND_MAP m_commands; + + DEFINE_VECTOR (u32, NODES_VECTOR, NODES_VECTOR_IT); + NODES_VECTOR m_locked_covers; + + DEFINE_VECTOR (const CEntityAlive*, CORPSES_VECTOR, CORPSES_VECTOR_IT); + CORPSES_VECTOR m_locked_corpses; + +public: + + CMonsterSquad (); + ~CMonsterSquad (); + + // ----------------------------------------------------------------- + + void RegisterMember (CEntity *pE); + void RemoveMember (CEntity *pE); + + bool SquadActive (); + + // ----------------------------------------------------------------- + + void SetLeader (CEntity *pE) {leader = pE;} + CEntity *GetLeader () {return leader;} + + // ----------------------------------------------------------------- + + void UpdateGoal (CEntity *pE, const SMemberGoal &goal); + void UpdateCommand (const CEntity *pE, const SSquadCommand &com); + + + void GetGoal (CEntity *pE, SMemberGoal &goal); + void GetCommand (CEntity *pE, SSquadCommand &com); + SMemberGoal &GetGoal (CEntity *pE); + SSquadCommand &GetCommand (CEntity *pE); + + // ----------------------------------------------------------------- + + + void UpdateSquadCommands (); + + void remove_links (CObject *O); + + // return count of monsters in radius for object + u8 get_count (const CEntity *object, float radius); + + + /////////////////////////////////////////////////////////////////////////////////////// + // Общие данные + ////////////////////////////////////////////////////////////////////////////////////// + + DEFINE_VECTOR (CEntity*, ENTITY_VEC, ENTITY_VEC_IT); + ENTITY_VEC m_temp_entities; + + /////////////////////////////////////////////////////////////////////////////////////// + // Атака группой монстров + ////////////////////////////////////////////////////////////////////////////////////// + + DEFINE_MAP (const CEntity*, ENTITY_VEC, ENEMY_MAP, ENEMY_MAP_IT); + + ENEMY_MAP m_enemy_map; + + void ProcessAttack (); + + + // -- Temp -- + struct _elem { + CEntity *pE; + Fvector p_from; + float yaw; + }; + xr_vector<_elem> lines; + // ------------ + + void Attack_AssignTargetDir (ENTITY_VEC& members , const CEntity *enemy); + + //////////////////////////////////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////////////////////////////////// + // групповой idle + ////////////////////////////////////////////////////////////////////////////////////// + ENTITY_VEC front, back, left, right; + + void ProcessIdle (); + void Idle_AssignAction (ENTITY_VEC &members); + + //////////////////////////////////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////////////////////////////////// + // Covers + ////////////////////////////////////////////////////////////////////////////////////// + bool is_locked_cover (u32 node); + void lock_cover (u32 node); + void unlock_cover (u32 node); + //////////////////////////////////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////////////////////////////////// + // Corpses + ////////////////////////////////////////////////////////////////////////////////////// + bool is_locked_corpse (const CEntityAlive*); + void lock_corpse (const CEntityAlive*); + void unlock_corpse (const CEntityAlive*); + //////////////////////////////////////////////////////////////////////////////////////// + + +}; diff --git a/src/xrGameLA/ai/monsters/ai_monster_squad_attack.cpp b/src/xrGameLA/ai/monsters/ai_monster_squad_attack.cpp new file mode 100644 index 000000000..af1dd3b6c --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_squad_attack.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" +#include "ai_monster_squad.h" +#include "../../entity.h" + +void CMonsterSquad::ProcessAttack() +{ + m_enemy_map.clear (); + m_temp_entities.clear (); + + // Выделить элементы с общими врагами и состянием атаки + for (MEMBER_GOAL_MAP_IT it_goal = m_goals.begin(); it_goal != m_goals.end(); it_goal++) { +// CEntity *member = it_goal->first; + SMemberGoal goal = it_goal->second; + + if (goal.type == MG_AttackEnemy) { + VERIFY(goal.entity && !goal.entity->getDestroy()); + + ENEMY_MAP_IT it = m_enemy_map.find(goal.entity); + if (it != m_enemy_map.end()) { + it->second.push_back(it_goal->first); + } else { + m_temp_entities.push_back (it_goal->first); + m_enemy_map.insert (mk_pair(goal.entity, m_temp_entities)); + } + } + } + + // Пройти по всем группам и назначить углы всем елементам в группе + for (ENEMY_MAP_IT it_enemy = m_enemy_map.begin(); it_enemy != m_enemy_map.end(); it_enemy++) { + Attack_AssignTargetDir(it_enemy->second,it_enemy->first); + } +} + + +struct sort_predicate { + const CEntity *enemy; + + + sort_predicate (const CEntity *pEnemy) : enemy(pEnemy) {} + + bool operator() (const CEntity *pE1, const CEntity *pE2) const + { + return (pE1->Position().distance_to(enemy->Position()) > + pE2->Position().distance_to(enemy->Position())); + }; +}; + +void CMonsterSquad::Attack_AssignTargetDir(ENTITY_VEC &members, const CEntity *enemy) +{ + _elem first; + _elem last; + + lines.clear(); + + // сортировать по убыванию расстояния от npc до врага + std::sort(members.begin(), members.end(), sort_predicate(enemy)); + if (members.empty()) return; + + float delta_yaw = PI_MUL_2 / members.size(); + + // обработать ближний элемент + first.pE = members.back(); + first.p_from = first.pE->Position(); + first.yaw = 0; + members.pop_back(); + + lines.push_back(first); + + // обработать дальний элемент + if (!members.empty()) { + last.pE = members[0]; + last.p_from = last.pE->Position(); + last.yaw = PI; + members.erase (members.begin()); + + lines.push_back(last); + } + + Fvector target_pos = enemy->Position(); + float next_right_yaw = delta_yaw; + float next_left_yaw = delta_yaw; + + // проходим с конца members в начало (начиная с наименьшего расстояния) + while (!members.empty()) { + CEntity *pCur; + + pCur = members.back(); + members.pop_back(); + + _elem cur_line; + cur_line.p_from = pCur->Position(); + cur_line.pE = pCur; + + // определить cur_line.yaw + + float h1,p1,h2,p2; + Fvector dir; + dir.sub(target_pos, first.p_from); + dir.getHP(h1,p1); + dir.sub(target_pos, cur_line.p_from); + dir.getHP(h2,p2); + + bool b_add_left = false; + + if (angle_normalize_signed(h2 - h1) > 0) { // right + if ((next_right_yaw < PI) && !fsimilar(next_right_yaw, PI, PI/60.f)) b_add_left = false; + else b_add_left = true; + } else { // left + if ((next_left_yaw < PI) && !fsimilar(next_left_yaw, PI, PI/60.f)) b_add_left = true; + else b_add_left = false; + } + + if (b_add_left) { + cur_line.yaw = -next_left_yaw; + next_left_yaw += delta_yaw; + } else { + cur_line.yaw = next_right_yaw; + next_right_yaw += delta_yaw; + } + + lines.push_back(cur_line); + } + + // Пройти по всем линиям и заполнить таргеты у npc + float first_h, first_p; + Fvector d; d.sub(target_pos,first.p_from); + d.getHP(first_h, first_p); + + for (u32 i = 0; i < lines.size(); i++){ + SSquadCommand command; + command.type = SC_ATTACK; + command.entity = enemy; + command.direction.setHP (first_h + lines[i].yaw, first_p); + UpdateCommand(lines[i].pE, command); + } +} diff --git a/src/xrGameLA/ai/monsters/ai_monster_squad_manager.cpp b/src/xrGameLA/ai/monsters/ai_monster_squad_manager.cpp new file mode 100644 index 000000000..b919d28d3 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_squad_manager.cpp @@ -0,0 +1,112 @@ +#include "stdafx.h" +#include "ai_monster_squad_manager.h" +#include "ai_monster_squad.h" +#include "../../entity.h" + +////////////////////////////////////////////////////////////////////////// +// SQUAD MANAGER Implementation +////////////////////////////////////////////////////////////////////////// +CMonsterSquadManager *g_monster_squad = 0; + +CMonsterSquadManager::CMonsterSquadManager() +{ +} +CMonsterSquadManager::~CMonsterSquadManager() +{ + for (u32 team_id=0; team_id= team.size()) { + team.resize (team_id + 1); + team[team_id].resize (squad_id + 1); + team[team_id][squad_id].resize (group_id + 1); + + for (u32 i=0; i= team[team_id].size()) { + + team[team_id].resize (squad_id + 1); + team[team_id][squad_id].resize (group_id + 1); + + for (u32 i=0; i= team[team_id][squad_id].size()) { + + u32 prev_size = team[team_id][squad_id].size(); + team[team_id][squad_id].resize (group_id + 1); + + for (u32 i = prev_size; i < group_id; i++) + team[team_id][squad_id][i] = 0; + + pSquad = new CMonsterSquad(); + team[team_id][squad_id][group_id] = pSquad; + } else { + if (team[team_id][squad_id][group_id] == 0) { + pSquad = new CMonsterSquad(); + team[team_id][squad_id][group_id] = pSquad; + } else { + // TODO: Verify IT! + pSquad = team[team_id][squad_id][group_id]; + } + } + + pSquad->RegisterMember(e); +} + +void CMonsterSquadManager::remove_member(u8 team_id, u8 squad_id, u8 group_id, CEntity *e) +{ + get_squad(team_id, squad_id, group_id)->RemoveMember(e); +} + +CMonsterSquad *CMonsterSquadManager::get_squad(u8 team_id, u8 squad_id, u8 group_id) +{ + VERIFY((team_id < team.size()) && (squad_id < team[team_id].size()) && (group_id < team[team_id][squad_id].size())); + return team[team_id][squad_id][group_id]; +} + +CMonsterSquad *CMonsterSquadManager::get_squad(const CEntity *entity) +{ + return get_squad((u8)entity->g_Team(),(u8)entity->g_Squad(),(u8)entity->g_Group()); +} + +void CMonsterSquadManager::update(CEntity *entity) +{ + CMonsterSquad *squad = monster_squad().get_squad(entity); + if (squad && squad->SquadActive() && (squad->GetLeader() == entity)) { + squad->UpdateSquadCommands(); + } +} + +void CMonsterSquadManager::remove_links(CObject *O) +{ + for (u32 team_id=0; team_idremove_links(O); + } + } + } + +} diff --git a/src/xrGameLA/ai/monsters/ai_monster_squad_manager.h b/src/xrGameLA/ai/monsters/ai_monster_squad_manager.h new file mode 100644 index 000000000..d62ad9e37 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_squad_manager.h @@ -0,0 +1,40 @@ +#pragma once + +class CMonsterSquad; +class CEntity; + +class CMonsterSquadManager { + + //------------------------------------------------------------------------ + // Monster classification: Team -> Level -> Squad + // Note: Its names differ from global ones, which are: Team -> Squad -> Group + // but nesting hierarchy logically means the same + // Team->Level->Squad used only for private members and functions + //------------------------------------------------------------------------ + + DEFINE_VECTOR(CMonsterSquad*, MONSTER_SQUAD_VEC, MONSTER_SQUAD_VEC_IT); + DEFINE_VECTOR(MONSTER_SQUAD_VEC, MONSTER_LEVEL_VEC,MONSTER_LEVEL_VEC_IT); + DEFINE_VECTOR(MONSTER_LEVEL_VEC, MONSTER_TEAM_VEC,MONSTER_TEAM_VEC_IT); + + MONSTER_TEAM_VEC team; + +public: + CMonsterSquadManager (); + ~CMonsterSquadManager (); + + void register_member (u8 team_id, u8 squad_id, u8 group_id, CEntity *e); + void remove_member (u8 team_id, u8 squad_id, u8 group_id, CEntity *e); + + CMonsterSquad *get_squad (u8 team_id, u8 squad_id, u8 group_id); + CMonsterSquad *get_squad (const CEntity *entity); + + void update (CEntity *entity); + + void remove_links (CObject *O); +}; + + +IC CMonsterSquadManager &monster_squad(); +extern CMonsterSquadManager *g_monster_squad; + +#include "ai_monster_squad_manager_inline.h" diff --git a/src/xrGameLA/ai/monsters/ai_monster_squad_manager_inline.h b/src/xrGameLA/ai/monsters/ai_monster_squad_manager_inline.h new file mode 100644 index 000000000..9389f779b --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_squad_manager_inline.h @@ -0,0 +1,9 @@ +#pragma once + +IC CMonsterSquadManager &monster_squad() +{ + if (!g_monster_squad) + g_monster_squad = new CMonsterSquadManager(); + return (*g_monster_squad); +} + diff --git a/src/xrGameLA/ai/monsters/ai_monster_squad_rest.cpp b/src/xrGameLA/ai/monsters/ai_monster_squad_rest.cpp new file mode 100644 index 000000000..6b1861f71 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_squad_rest.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" +#include "ai_monster_squad.h" +#include "../../entity.h" +#include "../../ai_object_location.h" + +void CMonsterSquad::ProcessIdle() +{ + m_temp_entities.clear(); + VERIFY(leader && !leader->getDestroy()); + + // Выделить элементы с общими врагами и состянием атаки + for (MEMBER_GOAL_MAP_IT it_goal = m_goals.begin(); it_goal != m_goals.end(); it_goal++) { + SMemberGoal goal = it_goal->second; + if ((goal.type == MG_Rest) || (goal.type == MG_WalkGraph)) { + m_temp_entities.push_back(it_goal->first); + } + } + + Idle_AssignAction(m_temp_entities); + +} + +struct CPredicateSideSort { + Fvector target; + + CPredicateSideSort (Fvector pos) {target = pos;} + + bool operator() (CEntity *e1, CEntity *e2) { + return (e1->Position().distance_to_sqr(target) > e2->Position().distance_to_sqr(target)); + } +}; + +#define CENTER_CIRCLE_DIST 20 +#define CIRCLE_RADIUS_MIN 10 +#define CIRCLE_RADIUS_MAX 15 + +void CMonsterSquad::Idle_AssignAction(ENTITY_VEC &members) +{ + // получить цель лидера + SMemberGoal &goal = GetGoal(leader); + + if (goal.type == MG_WalkGraph) { + + front.clear(); back.clear(); left.clear(); right.clear(); + + for (ENTITY_VEC_IT IT = members.begin(); IT != members.end(); IT++) { + if ((*IT) == leader) continue; + + front.push_back (*IT); + back.push_back (*IT); + left.push_back (*IT); + right.push_back (*IT); + } + + Fvector front_pos; + Fvector back_pos; + Fvector left_pos; + Fvector right_pos; + + Fvector dir = leader->Direction(); + front_pos.mad(leader->Position(), dir, CENTER_CIRCLE_DIST); + std::sort(front.begin(),front.end(), CPredicateSideSort(front_pos)); + + dir.invert(); + back_pos.mad(leader->Position(), dir, CENTER_CIRCLE_DIST); + std::sort(back.begin(),back.end(), CPredicateSideSort(back_pos)); + + dir = leader->XFORM().i; + right_pos.mad(leader->Position(), dir, CENTER_CIRCLE_DIST); + std::sort(right.begin(),right.end(), CPredicateSideSort(right_pos)); + + dir.invert(); + left_pos.mad(leader->Position(), dir, CENTER_CIRCLE_DIST); + std::sort(left.begin(),left.end(), CPredicateSideSort(left_pos)); + + SSquadCommand command; + command.type = SC_FOLLOW; + command.entity = leader; + command.direction = leader->Direction(); + + u8 cur_type = 0; + while (!front.empty()) { + float random_r; + Fvector random_dir; + + random_dir.random_dir (); + random_r = Random.randF(CIRCLE_RADIUS_MIN, CIRCLE_RADIUS_MAX); + + CEntity *entity = 0; + switch (cur_type) { + case 0: // front + entity = front.back (); front.pop_back(); + for (u32 i=0; i 3) cur_type = 0; + + UpdateCommand(entity, command); + } + + } else if (goal.type == MG_Rest) { + // пересчитать положение в команде в соответствие с целью лидера + for (ENTITY_VEC_IT it = members.begin(); it != members.end(); it++) { + if ((*it) == leader) continue; + + SSquadCommand command; + command.type = SC_REST; + command.position = leader->Position(); + command.node = leader->ai_location().level_vertex_id(); + command.entity = 0; + + UpdateCommand (*it, command); + } + } +} + diff --git a/src/xrGameLA/ai/monsters/ai_monster_utils.cpp b/src/xrGameLA/ai/monsters/ai_monster_utils.cpp new file mode 100644 index 000000000..8db5e8b8f --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_utils.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "ai_monster_utils.h" +#include "../../entity.h" +#include "../../ai_object_location.h" +#include "../../ai_space.h" +#include "../../level_graph.h" +#include "../../../../Include/xrRender/Kinematics.h" + +// проверить, находится ли объект entity на ноде +// возвращает позицию объекта, если он находится на ноде, или центр его ноды +Fvector get_valid_position(const CEntity *entity, const Fvector &actual_position) +{ + if ( + ai().level_graph().valid_vertex_id(entity->ai_location().level_vertex_id()) && + ai().level_graph().valid_vertex_position(entity->Position()) && + ai().level_graph().inside(entity->ai_location().level_vertex_id(), entity->Position()) + ) + return (actual_position); + else + return (ai().level_graph().vertex_position(entity->ai_location().level_vertex())); +} + +// возвращает true, если объект entity находится на ноде +bool object_position_valid(const CEntity *entity) +{ + return ( + ai().level_graph().valid_vertex_id(entity->ai_location().level_vertex_id()) && + ai().level_graph().valid_vertex_position(entity->Position()) && + ai().level_graph().inside(entity->ai_location().level_vertex_id(), entity->Position()) + ); +} + +Fvector get_bone_position (CObject *object, LPCSTR bone_name) +{ + u16 bone_id = smart_cast(object->Visual())->LL_BoneID (bone_name); + CBoneInstance &bone = smart_cast(object->Visual())->LL_GetBoneInstance (bone_id); + + Fmatrix global_transform; + global_transform.mul (object->XFORM(),bone.mTransform); + + return (global_transform.c); +} diff --git a/src/xrGameLA/ai/monsters/ai_monster_utils.h b/src/xrGameLA/ai/monsters/ai_monster_utils.h new file mode 100644 index 000000000..3e19aae4b --- /dev/null +++ b/src/xrGameLA/ai/monsters/ai_monster_utils.h @@ -0,0 +1,103 @@ +#pragma once + +// проверить, находится ли объект entity на ноде +// возвращает позицию объекта, если он находится на ноде, или центр его ноды +class CEntity; +extern Fvector get_valid_position(const CEntity *entity, const Fvector &actual_position); + +// возвращает true, если объект entity находится на ноде +extern bool object_position_valid(const CEntity *entity); + +IC Fvector random_position(const Fvector ¢er, float R) +{ + Fvector v; + v = center; + v.x += ::Random.randF(-R,R); + v.z += ::Random.randF(-R,R); + + return v; +} + +IC bool from_right(float ty, float cy) +{ + return ((angle_normalize_signed(ty - cy) > 0)); +} + +IC bool is_angle_between(float yaw, float yaw_from, float yaw_to) +{ + float diff = angle_difference(yaw_from,yaw_to); + R_ASSERT(diff < PI); + + if ((angle_difference(yaw,yaw_from) < diff) && (angle_difference(yaw,yaw_to) _cur) { + _cur += _accel * _dt; + if (_cur > _target) _cur = _target; + } else { + _cur -= _accel * _dt; + if (_cur < 0) _cur = 0.f; + } +} + +IC void def_lerp(float &_cur, float _target, float _vel, float _dt) +{ + if (fsimilar(_cur, _target)) return; + + if (_target > _cur) { + _cur += _vel * _dt; + if (_cur > _target) _cur = _target; + } else { + _cur -= _vel * _dt; + if (_cur < _target) _cur = _target; + } +} + +IC u32 time() +{ + return Device.dwTimeGlobal; +} + +////////////////////////////////////////////////////////////////////////// +// bone routines +////////////////////////////////////////////////////////////////////////// +extern Fvector get_bone_position (CObject *object, LPCSTR bone_name); + +IC Fvector get_head_position(CObject *object) +{ + return get_bone_position(object, "bip01_head"); +} + +////////////////////////////////////////////////////////////////////////// +// LTX routines +////////////////////////////////////////////////////////////////////////// +IC void read_delay(LPCSTR section, LPCSTR name, u32 &delay_min, u32 &delay_max) +{ + LPCSTR delay = pSettings->r_string(section,name); + string128 tempst; + + if (_GetItemCount(delay) == 2) { + delay_min = u32(atoi(_GetItem(delay,0,tempst))); + delay_max = u32(atoi(_GetItem(delay,1,tempst))); + } else { + delay_min = 0; + delay_max = u32(atoi(delay)); + } +} + +IC void read_distance(LPCSTR section, LPCSTR name, float &dist_min, float &dist_max) +{ + LPCSTR dist = pSettings->r_string(section,name); + string128 tempst; + + VERIFY (_GetItemCount(dist) == 2); + + dist_min = float(atof(_GetItem(dist,0,tempst))); + dist_max = float(atof(_GetItem(dist,1,tempst))); +} + diff --git a/src/xrGameLA/ai/monsters/anim_triple.cpp b/src/xrGameLA/ai/monsters/anim_triple.cpp new file mode 100644 index 000000000..177f2fbbe --- /dev/null +++ b/src/xrGameLA/ai/monsters/anim_triple.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "anim_triple.h" +#include "control_manager.h" + +char *dbg_states[] = { + "eStatePrepare", + "eStateExecute", + "eStateFinalize", + "eStateNone" +}; + +void CAnimationTriple::reset_data() +{ + m_data.capture_type = 0; +} + +void CAnimationTriple::on_capture() +{ + m_current_state = eStateNone; + + m_man->capture (this, ControlCom::eControlAnimation); + m_man->subscribe (this, ControlCom::eventAnimationEnd); + +} + +void CAnimationTriple::on_release() +{ + m_man->release (this, ControlCom::eControlAnimation); + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); + + if ((m_data.capture_type & ControlCom::eCapturePath) == ControlCom::eCapturePath) + m_man->release (this, ControlCom::eControlPath); + + if ((m_data.capture_type & ControlCom::eCaptureMovement) == ControlCom::eCaptureMovement) + m_man->release (this, ControlCom::eControlMovement); + + if ((m_data.capture_type & ControlCom::eCaptureDir) == ControlCom::eCaptureDir) + m_man->release (this, ControlCom::eControlDir); +} + +bool CAnimationTriple::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured(ControlCom::eControlAnimation)) return false; + + return true; +} + +void CAnimationTriple::activate() +{ + if ((m_data.capture_type & ControlCom::eCapturePath) == ControlCom::eCapturePath) { + m_man->capture (this, ControlCom::eControlPath); + m_man->path_stop (this); + } + + if ((m_data.capture_type & ControlCom::eCaptureMovement) == ControlCom::eCaptureMovement) { + m_man->capture (this, ControlCom::eControlMovement); + m_man->move_stop (this); + } + + if ((m_data.capture_type & ControlCom::eCaptureDir) == ControlCom::eCaptureDir) { + m_man->capture (this, ControlCom::eControlDir); + m_man->dir_stop (this); + } + + m_current_state = m_data.skip_prepare ? eStateExecute : eStatePrepare; + m_previous_state = m_data.skip_prepare ? eStatePrepare : eStateNone; + select_next_state (); +} + +void CAnimationTriple::on_event(ControlCom::EEventType, ControlCom::IEventData*) +{ + select_next_state(); +} + +void CAnimationTriple::pointbreak() +{ + m_current_state = eStateFinalize; + select_next_state (); +} + +void CAnimationTriple::select_next_state() +{ + if (m_current_state == eStateNone) { + STripleAnimEventData event(m_current_state); + m_man->notify (ControlCom::eventTAChange, &event); + return; + } + + if ((m_current_state == eStateExecute) && + m_data.execute_once && + (m_previous_state == eStateExecute)) + return; + + play_selected (); + + // raise event + if ((m_current_state != eStateExecute) || + ((m_current_state == eStateExecute) && (m_previous_state != eStateExecute))) { + + STripleAnimEventData event (m_current_state); + m_man->notify (ControlCom::eventTAChange, &event); + } + + m_previous_state = m_current_state; + if (m_current_state != eStateExecute) + m_current_state = EStateAnimTriple(m_current_state + 1); +} + +void CAnimationTriple::play_selected() +{ + // start new animation + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_data); + + ctrl_data->global.motion = m_data.pool[m_current_state]; + ctrl_data->global.actual = false; +} diff --git a/src/xrGameLA/ai/monsters/anim_triple.h b/src/xrGameLA/ai/monsters/anim_triple.h new file mode 100644 index 000000000..4f7f4d8af --- /dev/null +++ b/src/xrGameLA/ai/monsters/anim_triple.h @@ -0,0 +1,47 @@ +#pragma once + +#include "control_combase.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" + +enum EStateAnimTriple { + eStatePrepare, + eStateExecute, + eStateFinalize, + eStateNone +}; + +#define TA_SKIP_PREPARE true +#define TA_DONT_SKIP_PREPARE false +#define TA_EXECUTE_ONCE true +#define TA_EXECUTE_LOOPED false + + +struct STripleAnimEventData : public ControlCom::IEventData { + u32 m_current_state; + IC STripleAnimEventData(u32 state) : m_current_state(state) {} +}; + +struct SAnimationTripleData : public ControlCom::IComData { + MotionID pool[3]; + bool skip_prepare; + bool execute_once; + u32 capture_type; +}; + +class CAnimationTriple : public CControl_ComCustom{ + EStateAnimTriple m_current_state; + EStateAnimTriple m_previous_state; +public: + virtual void reset_data (); + virtual void on_capture (); + virtual void on_release (); + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + virtual bool check_start_conditions (); + virtual void activate (); + + void pointbreak (); +private: + void select_next_state (); + void play_selected (); +}; + diff --git a/src/xrGameLA/ai/monsters/anomaly_detector.cpp b/src/xrGameLA/ai/monsters/anomaly_detector.cpp new file mode 100644 index 000000000..cd078d14a --- /dev/null +++ b/src/xrGameLA/ai/monsters/anomaly_detector.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "anomaly_detector.h" +#include "BaseMonster/base_monster.h" +#include "../../restricted_object.h" +#include "../../customzone.h" +#include "../../level.h" +#include "../../space_restriction_manager.h" + +CAnomalyDetector::CAnomalyDetector(CBaseMonster *monster) : m_object(monster) +{ +} + +CAnomalyDetector::~CAnomalyDetector() +{ +} + +void CAnomalyDetector::load(LPCSTR section) +{ + m_radius = READ_IF_EXISTS(pSettings,r_float,section,"Anomaly_Detect_Radius",15.f); + m_time_to_rememeber = READ_IF_EXISTS(pSettings,r_u32,section,"Anomaly_Detect_Time_Remember",30000); +} + +void CAnomalyDetector::reinit() +{ + m_storage.clear(); + + m_active = true; +} + + +void CAnomalyDetector::update_schedule() +{ + if (m_active) + m_object->feel_touch_update(m_object->Position(), m_radius); + + if (m_storage.empty()) + return; + + xr_vector temp_out_restrictors; + xr_vector temp_in_restrictors; + + temp_in_restrictors.reserve(m_storage.size()); + + // add new restrictions + for (ANOMALY_INFO_VEC_IT it = m_storage.begin(); it != m_storage.end(); it++) { + if (it->time_registered == 0) { + temp_in_restrictors.push_back(it->object->ID()); + it->time_registered = time(); + } + } + + m_object->control().path_builder().restrictions().add_restrictions(temp_out_restrictors,temp_in_restrictors); + + // remove old restrictions + temp_in_restrictors.clear(); + for (ANOMALY_INFO_VEC_IT it = m_storage.begin(); it != m_storage.end(); it++) { + if (it->time_registered + m_time_to_rememeber < time()) { + temp_in_restrictors.push_back(it->object->ID()); + } + } + + m_object->control().path_builder().restrictions().remove_restrictions(temp_out_restrictors,temp_in_restrictors); + + + // remove from storage + m_storage.erase ( + std::remove_if( + m_storage.begin(), + m_storage.end(), + remove_predicate(m_time_to_rememeber) + ), + m_storage.end() + ); +} + +void CAnomalyDetector::on_contact(CObject *obj) +{ + if (!m_active) return; + + CCustomZone *custom_zone = smart_cast(obj); + if (!custom_zone) return; + + // if its NOT A restrictor - skip + if (custom_zone->restrictor_type() == RestrictionSpace::eRestrictorTypeNone) return; + + if (Level().space_restriction_manager().restriction_presented( + m_object->control().path_builder().restrictions().in_restrictions(),custom_zone->cName())) return; + + ANOMALY_INFO_VEC_IT it = std::find(m_storage.begin(), m_storage.end(), custom_zone); + if (it != m_storage.end()) return; + + SAnomalyInfo info; + info.object = obj; + info.time_registered = 0; + m_storage.push_back (info); +} diff --git a/src/xrGameLA/ai/monsters/anomaly_detector.h b/src/xrGameLA/ai/monsters/anomaly_detector.h new file mode 100644 index 000000000..4f4bdbbb1 --- /dev/null +++ b/src/xrGameLA/ai/monsters/anomaly_detector.h @@ -0,0 +1,47 @@ +#pragma once + +class CBaseMonster; + +class CAnomalyDetector { + CBaseMonster *m_object; + + float m_radius; + u32 m_time_to_rememeber; + + bool m_active; + + struct SAnomalyInfo { + CObject *object; + u32 time_registered; + + bool operator == (CObject *obj) { + return (object == obj); + } + }; + + struct remove_predicate { + u32 time_remember; + remove_predicate (u32 time) : time_remember(time){} + + IC bool operator() (const SAnomalyInfo &info) { + return (info.time_registered + time_remember < Device.dwTimeGlobal); + } + }; + + DEFINE_VECTOR (SAnomalyInfo, ANOMALY_INFO_VEC, ANOMALY_INFO_VEC_IT); + ANOMALY_INFO_VEC m_storage; + +public: + CAnomalyDetector (CBaseMonster *monster); + virtual ~CAnomalyDetector (); + + void load (LPCSTR section); + void reinit (); + + void update_schedule (); + void on_contact (CObject *obj); + + void activate (){m_active = true;} + void deactivate (){m_active = false;} + bool active () const { return m_active; } +}; \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster.cpp b/src/xrGameLA/ai/monsters/basemonster/base_monster.cpp new file mode 100644 index 000000000..ff77d07a2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster.cpp @@ -0,0 +1,584 @@ +#include "stdafx.h" +#include "base_monster.h" +#include "../../../PhysicsShell.h" +#include "../../../hit.h" +#include "../../../PHDestroyable.h" +#include "../../../CharacterPhysicsSupport.h" +#include "../../../game_level_cross_table.h" +#include "../../../game_graph.h" +#include "../../../phmovementcontrol.h" +#include "../ai_monster_squad_manager.h" +#include "../../../xrserver_objects_alife_monsters.h" +#include "../corpse_cover.h" +#include "../../../cover_evaluators.h" +#include "../../../seniority_hierarchy_holder.h" +#include "../../../team_hierarchy_holder.h" +#include "../../../squad_hierarchy_holder.h" +#include "../../../group_hierarchy_holder.h" +#include "../../../phdestroyable.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +//#include "../../../hudmanager.h" +#include "../../../memory_manager.h" +#include "../../../visual_memory_manager.h" +#include "../monster_velocity_space.h" +#include "../../../entitycondition.h" +#include "../../../sound_player.h" +#include "../../../level.h" +//#include "../../../ui/UIMainIngameWnd.h" +#include "../state_manager.h" +#include "../controlled_entity.h" +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" +#include "../anomaly_detector.h" +#include "../monster_cover_manager.h" +#include "../monster_home.h" +#include "../../../inventory.h" +#include "../../../xrserver.h" +#include "../ai_monster_squad.h" +#include "../../../actor.h" +#include "../../../ai_object_location.h" +#include "../../../ai_space.h" +#include "../../../level_graph.h" +#include "../../../script_engine.h" + +#pragma warning (disable:4355) +#pragma warning (push) +CBaseMonster::CBaseMonster() +{ + m_pPhysics_support=new CCharacterPhysicsSupport(CCharacterPhysicsSupport::etBitting,this); + + m_pPhysics_support ->in_Init(); + + // Components external init + + m_control_manager = new CControl_Manager(this); + + + EnemyMemory.init_external (this, 20000); + SoundMemory.init_external (this, 20000); + CorpseMemory.init_external (this, 20000); + HitMemory.init_external (this, 50000); + + EnemyMan.init_external (this); + CorpseMan.init_external (this); + + // Инициализация параметров анимации + + StateMan = 0; + + MeleeChecker.init_external (this); + Morale.init_external (this); + + m_controlled = 0; + + + control().add (&m_com_manager, ControlCom::eControlCustom); + + m_com_manager.add_ability (ControlCom::eControlSequencer); + m_com_manager.add_ability (ControlCom::eControlTripleAnimation); + + + m_anomaly_detector = new CAnomalyDetector(this); + CoverMan = new CMonsterCoverManager(this); + + Home = new CMonsterHome(this); + + com_man().add_ability (ControlCom::eComCriticalWound); +} + + +CBaseMonster::~CBaseMonster() +{ + xr_delete(m_pPhysics_support); + xr_delete(m_corpse_cover_evaluator); + xr_delete(m_enemy_cover_evaluator); + xr_delete(m_cover_evaluator_close_point); + + xr_delete(m_control_manager); + + xr_delete(m_anim_base); + xr_delete(m_move_base); + xr_delete(m_path_base); + xr_delete(m_dir_base); + + xr_delete(m_anomaly_detector); + xr_delete(CoverMan); + xr_delete(Home); +} + +void CBaseMonster::UpdateCL() +{ + inherited::UpdateCL(); + + if (g_Alive()) { + CStepManager::update (); + } + + control().update_frame(); + + m_pPhysics_support->in_UpdateCL(); +} + +void CBaseMonster::shedule_Update(u32 dt) +{ + inherited::shedule_Update (dt); + control().update_schedule (); + + Morale.update_schedule (dt); + + m_anomaly_detector->update_schedule(); + + m_pPhysics_support->in_shedule_Update(dt); + +#ifdef DEBUG + show_debug_info(); +#endif +} + +void CBaseMonster::anomaly_detector_enable(bool state) +{ + if (state) + anomaly_detector().activate(); + else + anomaly_detector().deactivate(); +} + +bool CBaseMonster::anomaly_detector_enabled() +{ + return anomaly_detector().active(); +} + +////////////////////////////////////////////////////////////////////// +// Other functions +////////////////////////////////////////////////////////////////////// + + +void CBaseMonster::Die(CObject* who) +{ + if (StateMan) StateMan->critical_finalize(); + + inherited::Die(who); + + if (is_special_killer(who)) + sound().play (MonsterSound::eMonsterSoundDieInAnomaly); + else + sound().play (MonsterSound::eMonsterSoundDie); + + monster_squad().remove_member ((u8)g_Team(),(u8)g_Squad(),(u8)g_Group(),this); + + if (m_controlled) m_controlled->on_die(); +} + + +//void CBaseMonster::Hit(float P,Fvector &dir,CObject*who,s16 element,Fvector p_in_object_space,float impulse, ALife::EHitType hit_type) +void CBaseMonster::Hit (SHit* pHDS) +{ + if (ignore_collision_hit && (pHDS->hit_type == ALife::eHitTypeStrike)) return; + + if (invulnerable()) + return; + + if (g_Alive()) + if (!critically_wounded()) + update_critical_wounded(pHDS->boneID,pHDS->power); + + +#pragma todo("tatarinrafa: add m_fSkinArmor and m_fHitFracMonster later") + pHDS->add_wound = true; + +// inherited::Hit(P,dir,who,element,p_in_object_space,impulse,hit_type); + inherited::Hit(pHDS); +} + +void CBaseMonster::PHHit(float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type /*=ALife::eHitTypeWound*/) +{ + m_pPhysics_support->in_Hit(P,dir,who,element,p_in_object_space,impulse,hit_type); +} + +CPHDestroyable* CBaseMonster:: ph_destroyable () +{ + return smart_cast(character_physics_support()); +} + +bool CBaseMonster::useful(const CItemManager *manager, const CGameObject *object) const +{ + if (!movement().restrictions().accessible(object->Position())) + return (false); + + if (!ai().level_graph().valid_vertex_id(object->ai_location().level_vertex_id())) + return (false); + + if (!movement().restrictions().accessible(object->ai_location().level_vertex_id())) + return (false); + + const CEntityAlive *pCorpse = smart_cast(object); + if (!pCorpse) return false; + + if (!pCorpse->g_Alive()) return true; + return false; +} + +float CBaseMonster::evaluate(const CItemManager *manager, const CGameObject *object) const +{ + return (0.f); +} + +////////////////////////////////////////////////////////////////////////// + +void CBaseMonster::ChangeTeam(int team, int squad, int group) +{ + if ((team == g_Team()) && (squad == g_Squad()) && (group == g_Group())) return; + +#ifdef DEBUG + if (!g_Alive()) { + ai().script_engine().print_stack (); + VERIFY2 (g_Alive(),"you are trying to change team of a dead entity"); + } +#endif // DEBUG + + // remove from current team + monster_squad().remove_member ((u8)g_Team(),(u8)g_Squad(),(u8)g_Group(),this); + inherited::ChangeTeam (team,squad,group); + monster_squad().register_member ((u8)g_Team(),(u8)g_Squad(),(u8)g_Group(), this); +} + + +void CBaseMonster::SetTurnAnimation(bool turn_left) +{ + (turn_left) ? anim().SetCurAnim(eAnimStandTurnLeft) : anim().SetCurAnim(eAnimStandTurnRight); +} + +void CBaseMonster::set_state_sound(u32 type, bool once) +{ + if (once) { + + sound().play(type); + + } else { + + // handle situation, when monster want to play attack sound for the first time + if ((type == MonsterSound::eMonsterSoundAggressive) && + (m_prev_sound_type != MonsterSound::eMonsterSoundAggressive)) { + + sound().play(MonsterSound::eMonsterSoundAttackHit); + + } else { + // get count of monsters in squad + u8 objects_count = monster_squad().get_squad(this)->get_count(this, 20.f); + + // include myself + objects_count++; + VERIFY(objects_count > 0); + + u32 delay = 0; + switch (type) { + case MonsterSound::eMonsterSoundIdle : + // check distance to actor + + if (Actor()->Position().distance_to(Position()) > db().m_fDistantIdleSndRange) { + delay = u32(float(db().m_dwDistantIdleSndDelay) * _sqrt(float(objects_count))); + type = MonsterSound::eMonsterSoundIdleDistant; + } else { + delay = u32(float(db().m_dwIdleSndDelay) * _sqrt(float(objects_count))); + } + + break; + case MonsterSound::eMonsterSoundEat: + delay = u32(float(db().m_dwEatSndDelay) * _sqrt(float(objects_count))); + break; + case MonsterSound::eMonsterSoundAggressive: + case MonsterSound::eMonsterSoundPanic: + delay = u32(float(db().m_dwAttackSndDelay) * _sqrt(float(objects_count))); + break; + } + + sound().play(type, 0, 0, delay); + } + } + + m_prev_sound_type = type; +} + +BOOL CBaseMonster::feel_touch_on_contact (CObject *O) +{ + return (inherited::feel_touch_on_contact(O)); +} + +BOOL CBaseMonster::feel_touch_contact(CObject *O) +{ + m_anomaly_detector->on_contact(O); + return inherited::feel_touch_contact(O); +} + +void CBaseMonster::TranslateActionToPathParams() +{ + bool bEnablePath = true; + u32 vel_mask = 0; + u32 des_mask = 0; + + switch (anim().m_tAction) { + case ACT_STAND_IDLE: + case ACT_SIT_IDLE: + case ACT_LIE_IDLE: + case ACT_EAT: + case ACT_SLEEP: + case ACT_REST: + case ACT_LOOK_AROUND: + case ACT_ATTACK: + bEnablePath = false; + break; + + case ACT_WALK_FWD: + if (m_bDamaged) { + vel_mask = MonsterMovement::eVelocityParamsWalkDamaged; + des_mask = MonsterMovement::eVelocityParameterWalkDamaged; + } else { + vel_mask = MonsterMovement::eVelocityParamsWalk; + des_mask = MonsterMovement::eVelocityParameterWalkNormal; + } + break; + case ACT_WALK_BKWD: + break; + case ACT_RUN: + if (m_bDamaged) { + vel_mask = MonsterMovement::eVelocityParamsRunDamaged; + des_mask = MonsterMovement::eVelocityParameterRunDamaged; + } else { + vel_mask = MonsterMovement::eVelocityParamsRun; + des_mask = MonsterMovement::eVelocityParameterRunNormal; + } + break; + case ACT_DRAG: + vel_mask = MonsterMovement::eVelocityParamsDrag; + des_mask = MonsterMovement::eVelocityParameterDrag; + + anim().SetSpecParams(ASP_MOVE_BKWD); + + break; + case ACT_STEAL: + vel_mask = MonsterMovement::eVelocityParamsSteal; + des_mask = MonsterMovement::eVelocityParameterSteal; + break; + } + + if (state_invisible) { + vel_mask = MonsterMovement::eVelocityParamsInvisible; + des_mask = MonsterMovement::eVelocityParameterInvisible; + } + + if (m_force_real_speed) vel_mask = des_mask; + + if (bEnablePath) { + path().set_velocity_mask (vel_mask); + path().set_desirable_mask (des_mask); + path().enable_path (); + } else { + path().disable_path (); + } +} + +u32 CBaseMonster::get_attack_rebuild_time() +{ + float dist = EnemyMan.get_enemy()->Position().distance_to(Position()); + return (100 + u32(50.f * dist)); +} + +void CBaseMonster::on_kill_enemy(const CEntity *obj) +{ + const CEntityAlive *entity = smart_cast(obj); + + // добавить в список трупов + CorpseMemory.add_corpse (entity); + + // удалить всю информацию о хитах + HitMemory.remove_hit_info (entity); + + // удалить всю информацию о звуках + SoundMemory.clear (); +} + +CMovementManager *CBaseMonster::create_movement_manager () +{ + m_movement_manager = new CControlPathBuilder(this); + + control().add (m_movement_manager, ControlCom::eControlPath); + control().install_path_manager (m_movement_manager); + control().set_base_controller (m_path_base, ControlCom::eControlPath); + + return (m_movement_manager); +} + +DLL_Pure *CBaseMonster::_construct () +{ + create_base_controls (); + + control().add (m_anim_base, ControlCom::eControlAnimationBase); + control().add (m_move_base, ControlCom::eControlMovementBase); + control().add (m_path_base, ControlCom::eControlPathBase); + control().add (m_dir_base, ControlCom::eControlDirBase); + + control().set_base_controller (m_anim_base, ControlCom::eControlAnimation); + control().set_base_controller (m_move_base, ControlCom::eControlMovement); + control().set_base_controller (m_dir_base, ControlCom::eControlDir); + + inherited::_construct (); + CStepManager::_construct (); + CInventoryOwner::_construct (); + return (this); +} + +void CBaseMonster::net_Relcase(CObject *O) +{ + inherited::net_Relcase(O); + + // TODO: do not clear, remove only object O + if (g_Alive()) { + EnemyMemory.remove_links (O); + SoundMemory.remove_links (O); + CorpseMemory.remove_links (O); + HitMemory.remove_hit_info (O); + + EnemyMan.reinit (); + CorpseMan.reinit (); + + UpdateMemory (); + + monster_squad().remove_links(O); + } + m_pPhysics_support->in_NetRelcase(O); +} + +void CBaseMonster::create_base_controls() +{ + m_anim_base = new CControlAnimationBase(); + m_move_base = new CControlMovementBase(); + m_path_base = new CControlPathBuilderBase(); + m_dir_base = new CControlDirectionBase(); +} + +void CBaseMonster::set_action(EAction action) +{ + anim().m_tAction = action; +} + +CParticlesObject* CBaseMonster::PlayParticles(const shared_str& name, const Fvector &position, const Fvector &dir, BOOL auto_remove, BOOL xformed) +{ + CParticlesObject* ps = CParticlesObject::Create(name.c_str(),auto_remove); + + // вычислить позицию и направленность партикла + Fmatrix matrix; + + matrix.identity (); + matrix.k.set (dir); + Fvector::generate_orthonormal_basis_normalized(matrix.k,matrix.j,matrix.i); + matrix.translate_over (position); + + (xformed) ? ps->SetXFORM (matrix) : ps->UpdateParent(matrix,zero_vel); + ps->Play (false); + + return ps; +} + +void CBaseMonster::on_restrictions_change() +{ + inherited::on_restrictions_change(); + + if (StateMan) StateMan->reinit(); +} + +void CBaseMonster::load_effector(LPCSTR section, LPCSTR line, SAttackEffector &effector) +{ + LPCSTR ppi_section = pSettings->r_string(section, line); + effector.ppi.duality.h = pSettings->r_float(ppi_section,"duality_h"); + effector.ppi.duality.v = pSettings->r_float(ppi_section,"duality_v"); + effector.ppi.gray = pSettings->r_float(ppi_section,"gray"); + effector.ppi.blur = pSettings->r_float(ppi_section,"blur"); + effector.ppi.noise.intensity = pSettings->r_float(ppi_section,"noise_intensity"); + effector.ppi.noise.grain = pSettings->r_float(ppi_section,"noise_grain"); + effector.ppi.noise.fps = pSettings->r_float(ppi_section,"noise_fps"); + VERIFY(!fis_zero(effector.ppi.noise.fps)); + + sscanf(pSettings->r_string(ppi_section,"color_base"), "%f,%f,%f", &effector.ppi.color_base.r, &effector.ppi.color_base.g, &effector.ppi.color_base.b); + sscanf(pSettings->r_string(ppi_section,"color_gray"), "%f,%f,%f", &effector.ppi.color_gray.r, &effector.ppi.color_gray.g, &effector.ppi.color_gray.b); + sscanf(pSettings->r_string(ppi_section,"color_add"), "%f,%f,%f", &effector.ppi.color_add.r, &effector.ppi.color_add.g, &effector.ppi.color_add.b); + + effector.time = pSettings->r_float(ppi_section,"time"); + effector.time_attack = pSettings->r_float(ppi_section,"time_attack"); + effector.time_release = pSettings->r_float(ppi_section,"time_release"); + + effector.ce_time = pSettings->r_float(ppi_section,"ce_time"); + effector.ce_amplitude = pSettings->r_float(ppi_section,"ce_amplitude"); + effector.ce_period_number = pSettings->r_float(ppi_section,"ce_period_number"); + effector.ce_power = pSettings->r_float(ppi_section,"ce_power"); +} + +bool CBaseMonster::check_start_conditions(ControlCom::EControlType type) +{ + if (type == ControlCom::eControlRotationJump) { + EMonsterState state = StateMan->get_state_type(); + if (state != eStateAttack_Run) return false; + } if (type == ControlCom::eControlMeleeJump) { + EMonsterState state = StateMan->get_state_type(); + if (!is_state(state, eStateAttack_Run) && !is_state(state, eStateAttack_Melee)) return false; + } + return true; +} + +void CBaseMonster::OnEvent(NET_Packet& P, u16 type) +{ + inherited::OnEvent (P,type); + CInventoryOwner::OnEvent (P,type); + + u16 id; + switch (type){ + case GE_OWNERSHIP_TAKE: + { + P.r_u16 (id); + bool duringSpawn = !P.r_eof() && P.r_u8(); + CObject *O = Level().Objects.net_Find (id); + VERIFY (O); + + CGameObject *GO = smart_cast(O); + CInventoryItem *pIItem = smart_cast(GO); + VERIFY (inventory().CanTakeItem(pIItem)); + pIItem->SetCurrPlace(eItemPlaceRuck); + + O->H_SetParent (this); + inventory().Take (GO, true, true, duringSpawn); + break; + } + case GE_TRADE_SELL: + + case GE_OWNERSHIP_REJECT: + { + P.r_u16 (id); + CObject* O = Level().Objects.net_Find (id); + VERIFY (O); + + bool just_before_destroy = !P.r_eof() && P.r_u8(); + O->SetTmpPreDestroy (just_before_destroy); + if (inventory().DropItem(smart_cast(O)) && !O->getDestroy()) + { + O->H_SetParent (0,just_before_destroy); + feel_touch_deny (O,2000); + } + } + break; + + case GE_KILL_SOMEONE: + P.r_u16 (id); + CObject* O = Level().Objects.net_Find (id); + + if (O) { + CEntity *pEntity = smart_cast(O); + if (pEntity) on_kill_enemy(pEntity); + } + + break; + } +} + + diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster.h b/src/xrGameLA/ai/monsters/basemonster/base_monster.h new file mode 100644 index 000000000..548977d37 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster.h @@ -0,0 +1,465 @@ +#pragma once + +#include "../../../CustomMonster.h" + +#include "../monster_enemy_memory.h" +#include "../monster_corpse_memory.h" +#include "../monster_sound_memory.h" +#include "../monster_hit_memory.h" + +#include "../monster_enemy_manager.h" +#include "../monster_corpse_manager.h" + +#include "../../../step_manager.h" +#include "../monster_event_manager.h" +#include "../melee_checker.h" +#include "../monster_morale.h" + +#include "../control_manager.h" +#include "../control_sequencer.h" + +#include "../ai_monster_utils.h" +#include "../../../../../xrCore/_vector3d_ext.h" +#include "../control_manager_custom.h" +#include "../ai_monster_shared_data.h" +#include "../monster_sound_defs.h" +#include "../../../inventoryowner.h" + +class CCharacterPhysicsSupport; +class CMonsterCorpseCoverEvaluator; +class CCoverEvaluatorFarFromEnemy; +class CCoverEvaluatorCloseToEnemy; +class CMonsterEventManager; +class CJumping; +class CControlledEntityBase; +class CMovementManager; +class IStateManagerBase; +class CAnomalyDetector; + +class CControlAnimationBase; +class CControlMovementBase; +class CControlPathBuilderBase; +class CControlDirectionBase; +class CMonsterCoverManager; + +class CMonsterHome; + +class CBaseMonster : public CCustomMonster, public CStepManager, public CInventoryOwner +{ + typedef CCustomMonster inherited; + +public: + CBaseMonster (); + virtual ~CBaseMonster (); + +public: + virtual Feel::Sound* dcast_FeelSound () { return this; } + virtual CCharacterPhysicsSupport* character_physics_support () {return m_pPhysics_support;} + virtual CPHDestroyable* ph_destroyable (); + virtual CEntityAlive* cast_entity_alive () {return this;} + virtual CEntity* cast_entity () {return this;} + virtual CPhysicsShellHolder* cast_physics_shell_holder () {return this;} + virtual CParticlesPlayer* cast_particles_player () {return this;} + virtual CCustomMonster* cast_custom_monster () {return this;} + virtual CScriptEntity* cast_script_entity () {return this;} + virtual CBaseMonster* cast_base_monster () {return this;} + + virtual CInventoryOwner *cast_inventory_owner () {return this;} + virtual CGameObject* cast_game_object () {return this;} + +public: + + virtual BOOL renderable_ShadowReceive () { return TRUE; } + virtual void Die (CObject* who); + virtual void HitSignal (float amount, Fvector& vLocalDir, CObject* who, s16 element); + virtual void Hit (SHit* pHDS); + virtual void PHHit (float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type = ALife::eHitTypeWound); + virtual void SelectAnimation (const Fvector& _view, const Fvector& _move, float speed ); + + virtual void Load (LPCSTR section); + virtual DLL_Pure *_construct (); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Save (NET_Packet& P); + virtual BOOL net_SaveRelevant (); + virtual void net_Export (NET_Packet& P); + virtual void net_Import (NET_Packet& P); + virtual void net_Relcase (CObject *O); + + //save/load server serialization + virtual void save (NET_Packet &output_packet) {inherited::save(output_packet);} + virtual void load (IReader &input_packet) {inherited::load(input_packet);} + + + virtual void UpdateCL (); + virtual void shedule_Update (u32 dt); + + virtual void InitThink () {} + virtual void Think (); + virtual void reinit (); + virtual void reload (LPCSTR section); + + virtual void init () {} + + virtual void feel_sound_new (CObject* who, int eType, CSound_UserDataPtr user_data, const Fvector &Position, float power); + virtual BOOL feel_vision_isRelevant (CObject* O); + virtual BOOL feel_touch_on_contact (CObject* O); + virtual BOOL feel_touch_contact (CObject *); + + virtual bool useful (const CItemManager *manager, const CGameObject *object) const; + virtual float evaluate (const CItemManager *manager, const CGameObject *object) const; + + virtual void OnEvent (NET_Packet& P, u16 type); + virtual void OnHUDDraw (CCustomHUD* hud) {return inherited::OnHUDDraw(hud);} + virtual u16 PHGetSyncItemsNumber () {return inherited::PHGetSyncItemsNumber();} + virtual CPHSynchronize* PHGetSyncItem (u16 item) {return inherited::PHGetSyncItem(item);} + virtual void PHUnFreeze () {return inherited::PHUnFreeze();} + virtual void PHFreeze () {return inherited::PHFreeze();} + virtual BOOL UsedAI_Locations () {return inherited::UsedAI_Locations();} + + virtual const SRotation Orientation () const {return inherited::Orientation();} + virtual void renderable_Render () {return inherited::renderable_Render();} + + virtual void on_restrictions_change (); + + virtual void SetAttackEffector (); + + virtual void update_fsm (); + + virtual void post_fsm_update (); + void squad_notify (); + + virtual bool IsTalkEnabled () {return false;} + + virtual void HitEntity (const CEntity *pEntity, float fDamage, float impulse, Fvector &dir); + virtual void HitEntityInJump (const CEntity *pEntity) {} + + virtual void on_before_sell (CInventoryItem *item); + float GetSatiety () {return 0.5f;} + void ChangeSatiety (float v) {} + float m_vampire_want_speed; + // --------------------------------------------------------------------------------- + // Process scripts + // --------------------------------------------------------------------------------- + virtual bool bfAssignMovement (CScriptEntityAction *tpEntityAction); + virtual bool bfAssignObject (CScriptEntityAction *tpEntityAction); + virtual bool bfAssignWatch (CScriptEntityAction *tpEntityAction); + virtual bool bfAssignAnimation (CScriptEntityAction *tpEntityAction); + virtual bool bfAssignMonsterAction (CScriptEntityAction *tpEntityAction); + virtual bool bfAssignSound (CScriptEntityAction *tpEntityAction); + + virtual void vfFinishAction (CScriptEntityAction *tpEntityAction); + + virtual void ProcessScripts (); + + virtual CEntity *GetCurrentEnemy (); + virtual CEntity *GetCurrentCorpse (); + virtual int get_enemy_strength (); + + virtual void SetScriptControl (const bool bScriptControl, shared_str caSciptName); + + + bool m_force_real_speed; + bool m_script_processing_active; + bool m_script_state_must_execute; + + + virtual void jump (const Fvector &position, float factor) {} + + bool m_skip_transfer_enemy; + IC void skip_transfer_enemy (bool value){m_skip_transfer_enemy = value;} + + IC int Rank (){return m_rank;} + + //---------------------------------------------------------------------------------- + + virtual void SetTurnAnimation (bool turn_left); + + // установка специфических анимаций + virtual void CheckSpecParams (u32 /**spec_params/**/) {} + virtual void ForceFinalAnimation () {} + virtual void LookPosition (Fvector to_point, float angular_speed = PI_DIV_3); // каждый монстр может по-разному реализвать эту функ (e.g. кровосос с поворотом головы и т.п.) + + // Team + virtual void ChangeTeam (int team, int squad, int group); + + // --------------------------------------------------------------------------------- + // Abilities + // --------------------------------------------------------------------------------- + virtual bool ability_invisibility () {return false;} + virtual bool ability_can_drag () {return false;} + virtual bool ability_psi_attack () {return false;} + virtual bool ability_earthquake () {return false;} + virtual bool ability_can_jump () {return false;} + virtual bool ability_distant_feel () {return false;} + virtual bool ability_run_attack () {return false;} + virtual bool ability_rotation_jump () {return false;} + virtual bool ability_jump_over_physics () {return false;} + virtual bool ability_pitch_correction () {return true;} + // --------------------------------------------------------------------------------- + + virtual void event_on_step () {} + virtual void on_threaten_execute () {} + // --------------------------------------------------------------------------------- + // Memory + void UpdateMemory (); + + // Cover + bool GetCorpseCover (Fvector &position, u32 &vertex_id); + bool GetCoverFromEnemy (const Fvector &enemy_pos, Fvector &position, u32 &vertex_id); + bool GetCoverFromPoint (const Fvector &pos, Fvector &position, u32 &vertex_id, float min_dist, float max_dist, float radius); + bool GetCoverCloseToPoint (const Fvector &dest_pos, float min_dist, float max_dist, float deviation, float radius ,Fvector &position, u32 &vertex_id); + + + + + + // Movement Manager +protected: + CControlPathBuilder *m_movement_manager; +protected: + virtual CMovementManager *create_movement_manager(); + +// members +public: + // -------------------------------------------------------------------------------------- + // Monster Settings + ref_smem m_base_settings; + ref_smem m_current_settings; + + void settings_read (const CInifile *ini, LPCSTR section, SMonsterSettings &data); + void settings_load (LPCSTR section); + void settings_overrides (); + + SMonsterSettings &db () {return *(*m_current_settings);} + // -------------------------------------------------------------------------------------- + + CCharacterPhysicsSupport *m_pPhysics_support; + + + CMonsterCorpseCoverEvaluator *m_corpse_cover_evaluator; + CCoverEvaluatorFarFromEnemy *m_enemy_cover_evaluator; + CCoverEvaluatorCloseToEnemy *m_cover_evaluator_close_point; + + // --------------------------------------------------------------------------------- + IStateManagerBase *StateMan; + // --------------------------------------------------------------------------------- + + CMonsterEnemyMemory EnemyMemory; + CMonsterSoundMemory SoundMemory; + CMonsterCorpseMemory CorpseMemory; + CMonsterHitMemory HitMemory; + + CMonsterEnemyManager EnemyMan; + CMonsterCorpseManager CorpseMan; + + bool hear_dangerous_sound; + bool hear_interesting_sound; + + // ----------------------------------------------------------------------------- + CMonsterEventManager EventMan; + // ----------------------------------------------------------------------------- + + CMeleeChecker MeleeChecker; + CMonsterMorale Morale; + + // ----------------------------------------------------------------------------- + + CMonsterCoverManager *CoverMan; + + // ----------------------------------------------------------------------------- + + CControlledEntityBase *m_controlled; + + // ----------------------------------------------------------------------------- + enum EMonsterType { + eMonsterTypeUniversal = u32(0), + eMonsterTypeIndoor, + eMonsterTypeOutdoor, + } m_monster_type; + + // ----------------------------------------------------------------------------- + // Home + CMonsterHome *Home; + + + // ----------------------------------------------------------------------------- + // Anomaly Detector +private: + CAnomalyDetector *m_anomaly_detector; + +public: + CAnomalyDetector &anomaly_detector () {return (*m_anomaly_detector);} + // ----------------------------------------------------------------------------- + + void anomaly_detector_enable (bool state); + bool anomaly_detector_enabled(); + +// //----------------------------------------------------------------- +// // Spawn Inventory Item +// //----------------------------------------------------------------- +//private: +// LPCSTR m_item_section; +// float m_spawn_probability; + + //-------------------------------------------------------------------- + // Berserk + //-------------------------------------------------------------------- +public: + u32 time_berserk_start; + IC void set_berserk () {time_berserk_start = time();} + bool berserk_always; + + //-------------------------------------------------------------------- + // Panic Threshold (extension for scripts) + //-------------------------------------------------------------------- + + float m_default_panic_threshold; + IC void set_custom_panic_threshold (float value); + IC void set_default_panic_threshold (); + //-------------------------------------------------------------------- + + + + + ////////////////////////////////////////////////////////////////////////// + // ----------------------------------------------------------------------------- + // Special Services (refactoring needed) + + void on_kill_enemy (const CEntity *obj); + void Hit_Psy (CObject *object, float value); + void Hit_Wound (CObject *object, float value, const Fvector &dir, float impulse); + CParticlesObject *PlayParticles (const shared_str& name, const Fvector &position, const Fvector &dir, BOOL auto_remove = TRUE, BOOL xformed = TRUE); + void load_effector (LPCSTR section, LPCSTR line, SAttackEffector &effector); + + // -------------------------------------------------------------------------------------- + // Kill From Here + // -------------------------------------------------------------------------------------- + // State flags + bool m_bDamaged; + bool m_bAngry; + bool m_bGrowling; + bool m_bAggressive; + bool m_bSleep; + bool m_bRunTurnLeft; + bool m_bRunTurnRight; + + + void set_aggressive (bool val = true) {m_bAggressive = val;} + + //--------------------------------------------------------------------------------------- + + + u32 m_prev_sound_type; + u32 get_attack_rebuild_time (); + + IC virtual EAction CustomVelocityIndex2Action (u32 velocity_index) {return ACT_STAND_IDLE;} + virtual void TranslateActionToPathParams (); + + bool state_invisible; + + void set_action (EAction action); + void set_state_sound (u32 type, bool once = false); +IC void fall_asleep (){m_bSleep = true;} +IC void wake_up (){m_bSleep = false;} + + // Temp + u32 m_time_last_attack_success; + int m_rank; + float m_melee_rotation_factor; + +private: + bool ignore_collision_hit; + +public: + IC void set_ignore_collision_hit (bool value) {ignore_collision_hit = value;} + // ----------------------------------------------------------------------------- + ////////////////////////////////////////////////////////////////////////// + + +public: + CControl_Manager &control() {return (*m_control_manager);} + + CControlAnimationBase &anim (){return (*m_anim_base);} + CControlMovementBase &move (){return (*m_move_base);} + CControlPathBuilderBase &path (){return (*m_path_base);} + CControlDirectionBase &dir (){return (*m_dir_base);} + + CControlManagerCustom &com_man() {return m_com_manager;} + + virtual bool check_start_conditions (ControlCom::EControlType); + virtual void on_activate_control (ControlCom::EControlType){} + +protected: + CControl_Manager *m_control_manager; + + CControlAnimationBase *m_anim_base; + CControlMovementBase *m_move_base; + CControlPathBuilderBase *m_path_base; + CControlDirectionBase *m_dir_base; + + CControlManagerCustom m_com_manager; + + virtual void create_base_controls (); + + + + ////////////////////////////////////////////////////////////////////////// + // Critical Wounded + ////////////////////////////////////////////////////////////////////////// + enum { + critical_wound_type_head = u32(0), + critical_wound_type_torso, + critical_wound_type_legs + }; + + virtual void load_critical_wound_bones (); + virtual bool critical_wound_external_conditions_suitable (); + virtual void critical_wounded_state_start (); + + void fill_bones_body_parts (LPCSTR body_part, CriticalWoundType wound_type); + + LPCSTR m_critical_wound_anim_head; + LPCSTR m_critical_wound_anim_torso; + LPCSTR m_critical_wound_anim_legs; + + ////////////////////////////////////////////////////////////////////////// + + +public: + + +////////////////////////////////////////////////////////////////////////// +// DEBUG stuff +#ifdef DEBUG +public: + struct SDebugInfo { + bool active; + float x; + float y; + float delta_y; + u32 color; + u32 delimiter_color; + + SDebugInfo() : active(false) {} + SDebugInfo(float px, float py, float dy, u32 c, u32 dc) : active(true), x(px), y(py), delta_y(dy), color (c), delimiter_color(dc) {} + }; + + u8 m_show_debug_info; // 0 - none, 1 - first column, 2 - second column + void set_show_debug_info (u8 show = 1){m_show_debug_info = show;} + virtual SDebugInfo show_debug_info (); + + void debug_fsm (); +#endif + +#ifdef _DEBUG + virtual void debug_on_key (int key) {} +#endif +////////////////////////////////////////////////////////////////////////// + +public: + virtual bool can_be_seen () const { return true; } +}; + +#include "base_monster_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster_anim.cpp b/src/xrGameLA/ai/monsters/basemonster/base_monster_anim.cpp new file mode 100644 index 000000000..7b5e5ede6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster_anim.cpp @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : base_monster_anim.cpp +// Created : 22.05.2003 +// Modified : 23.09.2003 +// Author : Serge Zhem +// Description : Animations for monsters of biting class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "base_monster.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../sound_player.h" +#include "../../../ai_monster_space.h" +#include "../control_animation_base.h" + +// Установка анимации +void CBaseMonster::SelectAnimation(const Fvector &/**_view/**/, const Fvector &/**_move/**/, float /**speed/**/) +{ + control().animation().update_frame(); +} + diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster_debug.cpp b/src/xrGameLA/ai/monsters/basemonster/base_monster_debug.cpp new file mode 100644 index 000000000..01006eae4 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster_debug.cpp @@ -0,0 +1,288 @@ +#include "stdafx.h" +#include "base_monster.h" +#include "../../../level.h" +#include "../../../level_debug.h" +#include "../../../entitycondition.h" +#include "../../../ai_debug.h" +#include "../state_defs.h" +#include "../state_manager.h" +#include "../../../phmovementcontrol.h" +#include "../../../characterphysicssupport.h" +#include "../../../actor.h" + +#ifdef DEBUG +CBaseMonster::SDebugInfo CBaseMonster::show_debug_info() +{ + if (!g_Alive()) return SDebugInfo(); + + if (m_show_debug_info == 0) { + DBG().text(this).clear(); + return SDebugInfo(); + } + + float y = 200; + float x = (m_show_debug_info == 1) ? 40.f : float(::Render->getTarget()->get_width() / 2) + 40.f; + const float delta_y = 12; + + string256 text; + + u32 color = D3DCOLOR_XRGB(0,255,0); + u32 delimiter_color = D3DCOLOR_XRGB(0,0,255); + + DBG().text(this).clear (); + DBG().text(this).add_item("---------------------------------------", x, y+=delta_y, delimiter_color); + + xr_sprintf(text, "-- Monster : [%s] Current Time = [%u]", *cName(), Device.dwTimeGlobal); + DBG().text(this).add_item(text, x, y+=delta_y, color); + DBG().text(this).add_item("----------- PROPERTIES ------------", x, y+=delta_y, delimiter_color); + + xr_sprintf(text, "Health = [%f]", conditions().GetHealth()); + DBG().text(this).add_item(text, x, y+=delta_y, color); + + xr_sprintf(text, "Morale = [%f]", Morale.get_morale()); + DBG().text(this).add_item(text, x, y+=delta_y, color); + + + DBG().text(this).add_item("----------- MEMORY ----------------", x, y+=delta_y, delimiter_color); + + if (EnemyMan.get_enemy()) { + xr_sprintf(text, "Current Enemy = [%s]", *EnemyMan.get_enemy()->cName()); + } else + xr_sprintf(text, "Current Enemy = [NONE]"); + DBG().text(this).add_item(text, x, y+=delta_y, color); + + if (EnemyMan.get_enemy()) { + xr_sprintf(text, "SeeEnemy[%u] EnemySeeMe[%u] TimeLastSeen[%u]", EnemyMan.see_enemy_now(),EnemyMan.enemy_see_me_now(),EnemyMan.get_enemy_time_last_seen()); + DBG().text(this).add_item(text, x, y+=delta_y, color); + } + + if (CorpseMan.get_corpse()) { + xr_sprintf(text, "Current Corpse = [%s] Satiety = [%.2f]", *CorpseMan.get_corpse()->cName(), GetSatiety()); + } else + xr_sprintf(text, "Current Corpse = [NONE] Satiety = [%.2f]", GetSatiety()); + + DBG().text(this).add_item(text, x, y+=delta_y, color); + + // Sound + if (SoundMemory.IsRememberSound()) { + SoundElem sound_elem; + bool dangerous_sound; + SoundMemory.GetSound(sound_elem, dangerous_sound); + + string128 s_type; + + switch(sound_elem.type){ + case WEAPON_SHOOTING: xr_strcpy(s_type,"WEAPON_SHOOTING"); break; + case MONSTER_ATTACKING: xr_strcpy(s_type,"MONSTER_ATTACKING"); break; + case WEAPON_BULLET_RICOCHET: xr_strcpy(s_type,"WEAPON_BULLET_RICOCHET"); break; + case WEAPON_RECHARGING: xr_strcpy(s_type,"WEAPON_RECHARGING"); break; + + case WEAPON_TAKING: xr_strcpy(s_type,"WEAPON_TAKING"); break; + case WEAPON_HIDING: xr_strcpy(s_type,"WEAPON_HIDING"); break; + case WEAPON_CHANGING: xr_strcpy(s_type,"WEAPON_CHANGING"); break; + case WEAPON_EMPTY_CLICKING: xr_strcpy(s_type,"WEAPON_EMPTY_CLICKING"); break; + + case MONSTER_DYING: xr_strcpy(s_type,"MONSTER_DYING"); break; + case MONSTER_INJURING: xr_strcpy(s_type,"MONSTER_INJURING"); break; + case MONSTER_WALKING: xr_strcpy(s_type,"MONSTER_WALKING"); break; + case MONSTER_JUMPING: xr_strcpy(s_type,"MONSTER_JUMPING"); break; + case MONSTER_FALLING: xr_strcpy(s_type,"MONSTER_FALLING"); break; + case MONSTER_TALKING: xr_strcpy(s_type,"MONSTER_TALKING"); break; + + case DOOR_OPENING: xr_strcpy(s_type,"DOOR_OPENING"); break; + case DOOR_CLOSING: xr_strcpy(s_type,"DOOR_CLOSING"); break; + case OBJECT_BREAKING: xr_strcpy(s_type,"OBJECT_BREAKING"); break; + case OBJECT_FALLING: xr_strcpy(s_type,"OBJECT_FALLING"); break; + case NONE_DANGEROUS_SOUND: xr_strcpy(s_type,"NONE_DANGEROUS_SOUND"); break; + } + + if (sound_elem.who) + xr_sprintf(text,"Sound: type[%s] time[%u] power[%.3f] val[%i] src[+]", s_type, sound_elem.time, sound_elem.power, sound_elem.value); + else + xr_sprintf(text,"Sound: type[%s] time[%u] power[%.3f] val[%i] src[?]", s_type, sound_elem.time, sound_elem.power, sound_elem.value); + + + } else + xr_sprintf(text, "Sound: NONE"); + + DBG().text(this).add_item(text, x, y+=delta_y, color); + + // Hit + if (HitMemory.is_hit()) { + if (HitMemory.get_last_hit_object()) { + xr_sprintf(text,"Hit Info: object=[%s] time=[%u]", *(HitMemory.get_last_hit_object()->cName()), HitMemory.get_last_hit_time()); + } else { + xr_sprintf(text,"Hit Info: object=[NONE] time=[%u]", HitMemory.get_last_hit_time()); + } + } else + xr_sprintf(text, "Hit Info: NONE"); + + DBG().text(this).add_item(text, x, y+=delta_y, color); + + DBG().text(this).add_item("----------- MOVEMENT ------------", x, y+=delta_y, delimiter_color); + + xr_sprintf(text, "Actual = [%u] Enabled = [%u]", control().path_builder().actual(), control().path_builder().enabled()); + DBG().text(this).add_item(text, x, y+=delta_y, color); + + xr_sprintf(text, "Speed: Linear = [%.3f] Angular = [%.3f]", control().movement().velocity_current(), 0.f); + DBG().text(this).add_item(text, x, y+=delta_y, color); + + DBG().text(this).add_item("------- Attack Distances -------------", x, y+=delta_y, delimiter_color); + xr_sprintf(text, "MinDist[%.3f] MaxDist[%.3f] As_Step[%.3f] As_MinDist[%.3f]", + MeleeChecker.get_min_distance(), + MeleeChecker.get_max_distance(), + MeleeChecker.dbg_as_step(), + MeleeChecker.dbg_as_min_dist() + ); + DBG().text(this).add_item(text, x, y+=delta_y, color); + + + if (EnemyMan.get_enemy()) { + xr_sprintf(text, "Current Enemy = [%s]", *EnemyMan.get_enemy()->cName()); + } else + xr_sprintf(text, "Current Enemy = [NONE]"); + DBG().text(this).add_item(text, x, y+=delta_y, color); + + + + return SDebugInfo(x, y, delta_y, color, delimiter_color); +} + +void CBaseMonster::debug_fsm() +{ + if (!g_Alive()) return; + + if (!psAI_Flags.test(aiMonsterDebug)) { + DBG().object_info(this,this).clear (); + return; + } + + EMonsterState state = StateMan->get_state_type(); + + string128 st; + + switch (state) { + case eStateRest_WalkGraphPoint: xr_sprintf(st,"Rest :: Walk Graph"); break; + case eStateRest_Idle: xr_sprintf(st,"Rest :: Idle"); break; + case eStateRest_Fun: xr_sprintf(st,"Rest :: Fun"); break; + case eStateRest_Sleep: xr_sprintf(st,"Rest :: Sleep"); break; + case eStateRest_MoveToHomePoint: xr_sprintf(st,"Rest :: MoveToHomePoint"); break; + case eStateRest_WalkToCover: xr_sprintf(st,"Rest :: WalkToCover"); break; + case eStateRest_LookOpenPlace: xr_sprintf(st,"Rest :: LookOpenPlace"); break; + + case eStateEat_CorpseApproachRun: xr_sprintf(st,"Eat :: Corpse Approach Run"); break; + case eStateEat_CorpseApproachWalk: xr_sprintf(st,"Eat :: Corpse Approach Walk"); break; + case eStateEat_CheckCorpse: xr_sprintf(st,"Eat :: Check Corpse"); break; + case eStateEat_Eat: xr_sprintf(st,"Eat :: Eating"); break; + case eStateEat_WalkAway: xr_sprintf(st,"Eat :: Walk Away"); break; + case eStateEat_Rest: xr_sprintf(st,"Eat :: Rest After Meal"); break; + case eStateEat_Drag: xr_sprintf(st,"Eat :: Drag"); break; + + case eStateAttack_Run: xr_sprintf(st,"Attack :: Run"); break; + case eStateAttack_Melee: xr_sprintf(st,"Attack :: Melee"); break; + case eStateAttack_RunAttack: xr_sprintf(st,"Attack :: Run Attack"); break; + case eStateAttack_RunAway: xr_sprintf(st,"Attack :: Run Away"); break; + case eStateAttack_FindEnemy: xr_sprintf(st,"Attack :: Find Enemy"); break; + case eStateAttack_Steal: xr_sprintf(st,"Attack :: Steal"); break; + case eStateAttack_AttackHidden: xr_sprintf(st,"Attack :: Attack Hidden"); break; + + case eStateAttackCamp_Hide: xr_sprintf(st,"Attack Camp:: Hide"); break; + case eStateAttackCamp_Camp: xr_sprintf(st,"Attack Camp:: Camp"); break; + case eStateAttackCamp_StealOut: xr_sprintf(st,"Attack Camp:: Steal Out"); break; + + case eStateAttack_HideInCover: xr_sprintf(st,"Attack :: Hide In Cover"); break; + case eStateAttack_MoveOut: xr_sprintf(st,"Attack :: Move Out From Cover");break; + case eStateAttack_CampInCover: xr_sprintf(st,"Attack :: Camp In Cover"); break; + + case eStateAttack_Psy: xr_sprintf(st,"Attack :: Psy"); break; + case eStateAttack_MoveToHomePoint: xr_sprintf(st,"Attack :: Move To Home Point"); break; + case eStateAttack_HomePoint_Hide: xr_sprintf(st,"Attack :: Home Point :: Hide"); break; + case eStateAttack_HomePoint_Camp: xr_sprintf(st,"Attack :: Home Point :: Camp"); break; + case eStateAttack_HomePoint_LookOpenPlace: xr_sprintf(st,"Attack :: Home Point :: Look Open Place"); break; + + case eStatePanic_Run: xr_sprintf(st,"Panic :: Run Away"); break; + case eStatePanic_FaceUnprotectedArea: xr_sprintf(st,"Panic :: Face Unprotected Area"); break; + case eStatePanic_HomePoint_Hide: xr_sprintf(st,"Panic :: Home Point :: Hide"); break; + case eStatePanic_HomePoint_LookOpenPlace: xr_sprintf(st,"Panic :: Home Point :: Look Open Place"); break; + case eStatePanic_HomePoint_Camp: xr_sprintf(st,"Panic :: Home Point :: Camp"); break; + + case eStateHitted_Hide: xr_sprintf(st,"Hitted :: Hide"); break; + case eStateHitted_MoveOut: xr_sprintf(st,"Hitted :: MoveOut"); break; + case eStateHitted_Home: xr_sprintf(st,"Hitted :: Home"); break; + + case eStateHearDangerousSound_Hide: xr_sprintf(st,"Dangerous Snd :: Hide"); break; + case eStateHearDangerousSound_FaceOpenPlace: xr_sprintf(st,"Dangerous Snd :: FaceOpenPlace"); break; + case eStateHearDangerousSound_StandScared: xr_sprintf(st,"Dangerous Snd :: StandScared"); break; + case eStateHearDangerousSound_Home: xr_sprintf(st,"Dangerous Snd :: Home"); break; + + case eStateHearInterestingSound_MoveToDest: xr_sprintf(st,"Interesting Snd :: MoveToDest"); break; + case eStateHearInterestingSound_LookAround: xr_sprintf(st,"Interesting Snd :: LookAround"); break; + + case eStateHearHelpSound: xr_sprintf(st,"Hear Help Sound"); break; + case eStateHearHelpSound_MoveToDest: xr_sprintf(st,"Hear Help Sound :: MoveToDest"); break; + case eStateHearHelpSound_LookAround: xr_sprintf(st,"Hear Help Sound :: LookAround"); break; + + case eStateControlled_Follow_Wait: xr_sprintf(st,"Controlled :: Follow : Wait"); break; + case eStateControlled_Follow_WalkToObject: xr_sprintf(st,"Controlled :: Follow : WalkToObject"); break; + case eStateControlled_Attack: xr_sprintf(st,"Controlled :: Attack"); break; + case eStateThreaten: xr_sprintf(st,"Threaten :: "); break; + case eStateFindEnemy_Run: xr_sprintf(st,"Find Enemy :: Run"); break; + case eStateFindEnemy_LookAround_MoveToPoint: xr_sprintf(st,"Find Enemy :: Look Around : Move To Point"); break; + case eStateFindEnemy_LookAround_LookAround: xr_sprintf(st,"Find Enemy :: Look Around : Look Around"); break; + case eStateFindEnemy_LookAround_TurnToPoint: xr_sprintf(st,"Find Enemy :: Look Around : Turn To Point"); break; + case eStateFindEnemy_Angry: xr_sprintf(st,"Find Enemy :: Angry"); break; + case eStateFindEnemy_WalkAround: xr_sprintf(st,"Find Enemy :: Walk Around"); break; + case eStateSquad_Rest_Idle: xr_sprintf(st,"Squad :: Rest : Idle"); break; + case eStateSquad_Rest_WalkAroundLeader: xr_sprintf(st,"Squad :: Rest : WalkAroundLeader"); break; + case eStateSquad_RestFollow_Idle: xr_sprintf(st,"Squad :: Follow Leader : Idle"); break; + case eStateSquad_RestFollow_WalkToPoint: xr_sprintf(st,"Squad :: Follow Leader : WalkToPoint"); break; + case eStateCustom_Vampire: xr_sprintf(st,"Attack :: Vampire"); break; + case eStateVampire_ApproachEnemy: xr_sprintf(st,"Vampire :: Approach to enemy"); break; + case eStateVampire_Execute: xr_sprintf(st,"Vampire :: Hit"); break; + case eStateVampire_RunAway: xr_sprintf(st,"Vampire :: Run Away"); break; + case eStateVampire_Hide: xr_sprintf(st,"Vampire :: Hide"); break; + case eStatePredator: xr_sprintf(st,"Predator"); break; + case eStatePredator_MoveToCover: xr_sprintf(st,"Predator :: MoveToCover"); break; + case eStatePredator_LookOpenPlace: xr_sprintf(st,"Predator :: Look Open Place"); break; + case eStatePredator_Camp: xr_sprintf(st,"Predator :: Camp"); break; + case eStateBurerAttack_Tele: xr_sprintf(st,"Attack :: Telekinesis"); break; + case eStateBurerAttack_Gravi: xr_sprintf(st,"Attack :: Gravi Wave"); break; + case eStateBurerAttack_RunAround: xr_sprintf(st,"Attack :: Run Around"); break; + case eStateBurerAttack_FaceEnemy: xr_sprintf(st,"Attack :: Face Enemy"); break; + case eStateBurerAttack_Melee: xr_sprintf(st,"Attack :: Melee"); break; + case eStateBurerScanning: xr_sprintf(st,"Attack :: Scanning"); break; + case eStateGhostBossAttack_Tele: xr_sprintf(st,"GhostBoss::Attack :: Telekinesis"); break; + case eStateGhostBossAttack_Gravi: xr_sprintf(st,"GhostBoss::Attack :: Gravi Wave"); break; + case eStateGhostBossAttack_RunAround: xr_sprintf(st,"GhostBoss::Attack :: Run Around"); break; + case eStateGhostBossAttack_FaceEnemy: xr_sprintf(st,"GhostBoss::Attack :: Face Enemy"); break; + case eStateGhostBossAttack_Melee: xr_sprintf(st,"GhostBoss::Attack :: Melee"); break; + case eStateGhostBossScanning: xr_sprintf(st,"GhostBoss::Attack :: Scanning"); break; + case eStateCustomMoveToRestrictor: xr_sprintf(st,"Moving To Restrictor :: Position not accessible"); break; + case eStateSmartTerrainTask: xr_sprintf(st,"ALIFE"); break; + case eStateSmartTerrainTaskGamePathWalk: xr_sprintf(st,"ALIFE :: Game Path Walk"); break; + case eStateSmartTerrainTaskLevelPathWalk: xr_sprintf(st,"ALIFE :: Level Path Walk"); break; + case eStateSmartTerrainTaskWaitCapture: xr_sprintf(st,"ALIFE :: Wait till smart terrain will capture me"); break; + case eStateUnknown: xr_sprintf(st,"Unknown State :: "); break; + default: xr_sprintf(st,"Undefined State ::"); break; + } + + DBG().object_info(this,this).remove_item (u32(0)); + DBG().object_info(this,this).remove_item (u32(1)); + DBG().object_info(this,this).remove_item (u32(2)); + + DBG().object_info(this,this).add_item (*cName(), D3DCOLOR_XRGB(255,0,0), 0); + DBG().object_info(this,this).add_item (st, D3DCOLOR_XRGB(255,0,0), 1); + + xr_sprintf(st, "Team[%u]Squad[%u]Group[%u]", g_Team(), g_Squad(), g_Group()); + DBG().object_info(this,this).add_item (st, D3DCOLOR_XRGB(255,0,0), 2); + + CEntityAlive *entity = smart_cast(Level().CurrentEntity()); + if (entity && entity->character_physics_support()->movement()) { + xr_sprintf(st,"VELOCITY [%f,%f,%f] Value[%f]",VPUSH(entity->character_physics_support()->movement()->GetVelocity()),entity->character_physics_support()->movement()->GetVelocityActual()); + DBG().text(this).clear(); + DBG().text(this).add_item(st,200,100,COLOR_GREEN,100); + } +} + + +#endif diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster_feel.cpp b/src/xrGameLA/ai/monsters/basemonster/base_monster_feel.cpp new file mode 100644 index 000000000..6cb666862 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster_feel.cpp @@ -0,0 +1,336 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : base_monster_feel.cpp +// Created : 26.05.2003 +// Modified : 26.05.2003 +// Author : Serge Zhem +// Description : Visibility and look for all the biting monsters +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "base_monster.h" +#include "../../../actor.h" +#include "../../../ActorEffector.h" +#include "../ai_monster_effector.h" +#include "../../../hudmanager.h" +#include "../../../clsid_game.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../sound_player.h" +#include "../../../level.h" +#include "../../../script_callback_ex.h" +#include "../../../script_game_object.h" +#include "../../../game_object_space.h" +#include "../../../ai_monster_space.h" +#include "../control_animation_base.h" +#include "../../../UIGameCustom.h" +#include "../../../UI/UIStatic.h" +#include "../../../ai_object_location.h" +#include "../../../profiler.h" +#include "../../../ActorEffector.h" +#include "../../../../CameraBase.h" + +void CBaseMonster::feel_sound_new(CObject* who, int eType, CSound_UserDataPtr user_data, const Fvector &Position, float power) +{ + if (!g_Alive()) return; + + // ignore my sounds + if (this == who) return; + + if (user_data) + user_data->accept (sound_user_data_visitor()); + + // ignore unknown sounds + if (eType == 0xffffffff) return; + + // ignore distant sounds + Fvector center; + Center (center); + float dist = center.distance_to(Position); + if (dist > db().m_max_hear_dist) return; + + // ignore sounds if not from enemies and not help sounds + CEntityAlive* entity = smart_cast (who); + + // ignore sound if enemy drop a weapon on death + if (!entity && ((eType & SOUND_TYPE_ITEM_HIDING) == SOUND_TYPE_ITEM_HIDING)) return; + + if (entity && (!EnemyMan.is_enemy(entity))) { + SoundMemory.check_help_sound(eType, entity->ai_location().level_vertex_id()); + return; + } + + if ((eType & SOUND_TYPE_WEAPON_SHOOTING) == SOUND_TYPE_WEAPON_SHOOTING) power = 1.f; + + if (((eType & SOUND_TYPE_WEAPON_BULLET_HIT) == SOUND_TYPE_WEAPON_BULLET_HIT) && (dist < 2.f)) + HitMemory.add_hit(who,eSideFront); + + // execute callback + sound_callback (who,eType,Position,power); + + // register in sound memory + if (power >= db().m_fSoundThreshold) { + SoundMemory.HearSound(who,eType,Position,power,Device.dwTimeGlobal); + } +} +#define MAX_LOCK_TIME 2.f + +void CBaseMonster::HitEntity(const CEntity *pEntity, float fDamage, float impulse, Fvector &dir) +{ + if (!g_Alive()) return; + if (!pEntity || pEntity->getDestroy()) return; + + if (!EnemyMan.get_enemy()) return; + + if (EnemyMan.get_enemy() == pEntity) { + Fvector position_in_bone_space; + position_in_bone_space.set(0.f,0.f,0.f); + + // перевод из локальных координат в мировые вектора направления импульса + Fvector hit_dir; + XFORM().transform_dir (hit_dir,dir); + hit_dir.normalize (); + + CEntity *pEntityNC = const_cast(pEntity); + VERIFY (pEntityNC); + + NET_Packet l_P; + SHit HS; + HS.GenHeader(GE_HIT, pEntityNC->ID()); // u_EventGen (l_P,GE_HIT, pEntityNC->ID()); + HS.whoID = (ID()); // l_P.w_u16 (ID()); + HS.weaponID = (ID()); // l_P.w_u16 (ID()); + HS.dir = (hit_dir); // l_P.w_dir (hit_dir); + HS.power = (fDamage); // l_P.w_float (fDamage); + HS.boneID = (smart_cast(pEntityNC->Visual())->LL_GetBoneRoot());// l_P.w_s16 (smart_cast(pEntityNC->Visual())->LL_GetBoneRoot()); + HS.p_in_bone_space = (position_in_bone_space); // l_P.w_vec3 (position_in_bone_space); + HS.impulse = (impulse); // l_P.w_float (impulse); + HS.hit_type = (ALife::eHitTypeWound); // l_P.w_u16 ( u16(ALife::eHitTypeWound) ); + HS.Write_Packet(l_P); + u_EventSend (l_P); + + if (pEntityNC == Actor()) { + START_PROFILE("BaseMonster/Animation/HitEntity"); + SDrawStaticStruct* s = CurrentGameUI()->AddCustomStatic("monster_claws", false); + s->m_endTime = Device.fTimeGlobal+3.0f;// 3sec + + float h1,p1; + Device.vCameraDirection.getHP (h1,p1); + + Fvector hd = hit_dir; + hd.mul(-1); + float d = -h1 + hd.getH(); + s->wnd()->SetHeading (d); + s->wnd()->SetHeadingPivot (Fvector2().set(256,512), Fvector2().set(0,0), false); + + STOP_PROFILE; + + SetAttackEffector (); + + float time_to_lock = fDamage * MAX_LOCK_TIME; + clamp (time_to_lock, 0.f, MAX_LOCK_TIME); + Actor()->lock_accel_for (int(time_to_lock * 1000)); + + ////////////////////////////////////////////////////////////////////////// + // + ////////////////////////////////////////////////////////////////////////// + + /*CEffectorCam* ce = Actor()->Cameras().GetCamEffector((ECamEffectorType)effBigMonsterHit); + if(!ce) + { + const shared_str& eff_sect = pSettings->r_string(cNameSect(), "actor_hit_effect"); + if(eff_sect.c_str()) + { + int id = -1; + Fvector cam_pos,cam_dir,cam_norm; + Actor()->cam_Active()->Get (cam_pos,cam_dir,cam_norm); + cam_dir.normalize_safe (); + dir.normalize_safe (); + + float ang_diff = angle_difference (cam_dir.getH(), dir.getH()); + Fvector cp; + cp.crossproduct (cam_dir,dir); + bool bUp =(cp.y>0.0f); + + Fvector cross; + cross.crossproduct (cam_dir, dir); + VERIFY (ang_diff>=0.0f && ang_diff<=PI); + + float _s1 = PI_DIV_8; + float _s2 = _s1+PI_DIV_4; + float _s3 = _s2+PI_DIV_4; + float _s4 = _s3+PI_DIV_4; + + if(ang_diff<=_s1){ + id = 2; + }else { + if(ang_diff>_s1 && ang_diff<=_s2){ + id = (bUp)?5:7; + }else + if(ang_diff>_s2 && ang_diff<=_s3){ + id = (bUp)?3:1; + }else + if(ang_diff>_s3 && ang_diff<=_s4){ + id = (bUp)?4:6; + }else + if(ang_diff>_s4){ + id = 0; + }else{ + VERIFY(0); + } + } + + string64 sect_name; + + xr_sprintf (sect_name,"%s_%d",eff_sect.c_str(), id); + AddEffector (Actor(), effBigMonsterHit, sect_name, fDamage); + } + } + */ + ////////////////////////////////////////////////////////////////////////// + + + } + + Morale.on_attack_success(); + + m_time_last_attack_success = Device.dwTimeGlobal; + } +} + + +BOOL CBaseMonster::feel_vision_isRelevant(CObject* O) +{ + if (!g_Alive()) return FALSE; + if (0==smart_cast(O)) return FALSE; + + if ((O->spatial.type & STYPE_VISIBLEFORAI) != STYPE_VISIBLEFORAI) return FALSE; + + // если спит, то ничего не видит + if (m_bSleep) return FALSE; + + // если не враг - не видит + CEntityAlive* entity = smart_cast (O); + if (entity && entity->g_Alive()) { + if (!EnemyMan.is_enemy(entity)) { + // если видит друга - проверить наличие у него врагов + CBaseMonster *monster = smart_cast(entity); + if (monster && !m_skip_transfer_enemy) EnemyMan.transfer_enemy(monster); + return FALSE; + } + } + + return TRUE; +} + +void CBaseMonster::HitSignal(float amount, Fvector& vLocalDir, CObject* who, s16 element) +{ + if (!g_Alive()) return; + + feel_sound_new(who,SOUND_TYPE_WEAPON_SHOOTING,0,who->Position(),1.f); + if (g_Alive()) sound().play(MonsterSound::eMonsterSoundTakeDamage); + + if (element < 0) return; + + // Определить направление хита (перед || зад || лево || право) + float yaw,pitch; + vLocalDir.getHP(yaw,pitch); + + yaw = angle_normalize(yaw); + + EHitSide hit_side = eSideFront; + if ((yaw >= PI_DIV_4) && (yaw <= 3*PI_DIV_4)) hit_side = eSideLeft; + else if ((yaw >= 3 * PI_DIV_4) && (yaw <= 5*PI_DIV_4)) hit_side = eSideBack; + else if ((yaw >= 5 * PI_DIV_4) && (yaw <= 7*PI_DIV_4)) hit_side = eSideRight; + + anim().FX_Play (hit_side, 1.0f); + + HitMemory.add_hit (who,hit_side); + + Morale.on_hit (); + + callback(GameObject::eHit)( + lua_game_object(), + amount, + vLocalDir, + smart_cast(who)->lua_game_object(), + element + ); + + // если нейтрал - добавить как врага + CEntityAlive *obj = smart_cast(who); + if (obj && (tfGetRelationType(obj) == ALife::eRelationTypeNeutral)) EnemyMan.add_enemy(obj); +} + +void CBaseMonster::SetAttackEffector() +{ + CActor *pA = smart_cast(Level().CurrentEntity()); + if (pA) { + Actor()->Cameras().AddCamEffector(new CMonsterEffectorHit(db().m_attack_effector.ce_time,db().m_attack_effector.ce_amplitude,db().m_attack_effector.ce_period_number,db().m_attack_effector.ce_power)); + Actor()->Cameras().AddPPEffector(new CMonsterEffector(db().m_attack_effector.ppi, db().m_attack_effector.time, db().m_attack_effector.time_attack, db().m_attack_effector.time_release)); + } +} + +void CBaseMonster::Hit_Psy(CObject *object, float value) +{ + NET_Packet P; + SHit HS; + HS.GenHeader (GE_HIT, object->ID()); // // u_EventGen (P,GE_HIT, object->ID()); // + HS.whoID = (ID()); // own // P.w_u16 (ID()); // own + HS.weaponID = (ID()); // own // P.w_u16 (ID()); // own + HS.dir = (Fvector().set(0.f,1.f,0.f)); // direction // P.w_dir (Fvector().set(0.f,1.f,0.f)); // direction + HS.power = (value); // hit value // P.w_float (value); // hit value + HS.boneID = (BI_NONE); // bone // P.w_s16 (BI_NONE); // bone + HS.p_in_bone_space = (Fvector().set(0.f,0.f,0.f)); // P.w_vec3 (Fvector().set(0.f,0.f,0.f)); + HS.impulse = (0.f); // P.w_float (0.f); + HS.hit_type = (ALife::eHitTypeTelepatic); // P.w_u16 (u16(ALife::eHitTypeTelepatic)); + HS.Write_Packet (P); + u_EventSend (P); +} + +void CBaseMonster::Hit_Wound(CObject *object, float value, const Fvector &dir, float impulse) +{ + NET_Packet P; + SHit HS; + HS.GenHeader(GE_HIT, object->ID()); // u_EventGen (P,GE_HIT, object->ID()); + HS.whoID = (ID()); // P.w_u16 (ID()); + HS.weaponID = (ID()); // P.w_u16 (ID()); + HS.dir = (dir); // P.w_dir (dir); + HS.power = (value); // P.w_float (value); + HS.boneID = (smart_cast(object->Visual())->LL_GetBoneRoot()); // P.w_s16 (smart_cast(object->Visual())->LL_GetBoneRoot()); + HS.p_in_bone_space = (Fvector().set(0.f,0.f,0.f)); // P.w_vec3 (Fvector().set(0.f,0.f,0.f)); + HS.impulse = (impulse); // P.w_float (impulse); + HS.hit_type = (ALife::eHitTypeWound); // P.w_u16 (u16(ALife::eHitTypeWound)); + HS.Write_Packet(P); + u_EventSend (P); +} + +bool CBaseMonster::critical_wound_external_conditions_suitable () +{ + if (!control().check_start_conditions(ControlCom::eControlSequencer)) + return false; + + if (!anim().IsStandCurAnim()) return false; + + return true; +} + +void CBaseMonster::critical_wounded_state_start() +{ + VERIFY (m_critical_wound_type != u32(-1)); + + LPCSTR anim = 0; + switch (m_critical_wound_type) { + case critical_wound_type_head: + anim = m_critical_wound_anim_head; + break; + case critical_wound_type_torso: + anim = m_critical_wound_anim_torso; + break; + case critical_wound_type_legs: + anim = m_critical_wound_anim_legs; + break; + } + + VERIFY (anim); + com_man().critical_wound(anim); +} + + diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster_inline.h b/src/xrGameLA/ai/monsters/basemonster/base_monster_inline.h new file mode 100644 index 000000000..4c3199ae4 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster_inline.h @@ -0,0 +1,11 @@ +#pragma once + +IC void CBaseMonster::set_custom_panic_threshold(float value) +{ + m_panic_threshold = value; +} + +IC void CBaseMonster::set_default_panic_threshold() +{ + m_panic_threshold = m_default_panic_threshold; +} diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster_misc.cpp b/src/xrGameLA/ai/monsters/basemonster/base_monster_misc.cpp new file mode 100644 index 000000000..8f879e709 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster_misc.cpp @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : base_monster_misc.cpp +// Created : 26.05.2003 +// Modified : 26.05.2003 +// Author : Serge Zhem +// Description : Miscellanious functions for all the biting monsters +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "base_monster.h" +#include "../../../entitycondition.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// Входные воздействия +// Зрение, слух, вероятность победы, выгодность противника +void CBaseMonster::UpdateMemory() +{ + // Обновить память + EnemyMemory.update (); + SoundMemory.UpdateHearing (); + CorpseMemory.update (); + HitMemory.update (); + + // обновить менеджеры врагов и трупов + EnemyMan.update (); + CorpseMan.update (); + + // remove hit info from objects that are corpses + + + hear_dangerous_sound = hear_interesting_sound = false; + SoundElem se; + + if (SoundMemory.IsRememberSound()) { + SoundMemory.GetSound(se,hear_dangerous_sound); + hear_interesting_sound = !hear_dangerous_sound; + } + + // Setup is own additional flags + m_bDamaged = ((conditions().GetHealth() < db().m_fDamagedThreshold) ? true : false); + + m_bAggressive = hear_dangerous_sound || (EnemyMan.get_enemies_count() > 0) || + HitMemory.is_hit(); + +} + + diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster_net.cpp b/src/xrGameLA/ai/monsters/basemonster/base_monster_net.cpp new file mode 100644 index 000000000..f69924da9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster_net.cpp @@ -0,0 +1,108 @@ +#include "stdafx.h" +#include "base_monster.h" + +#include "../../../ai_object_location.h" +#include "../../../game_graph.h" +#include "../../../../../xrNetServer/net_utils.h" +#include "../../../ai_space.h" +#include "../../../hit.h" +#include "../../../PHDestroyable.h" +#include "../../../CharacterPhysicsSupport.h" +void CBaseMonster::net_Save (NET_Packet& P) +{ + inherited::net_Save(P); + m_pPhysics_support->in_NetSave(P); +} + +BOOL CBaseMonster::net_SaveRelevant () +{ + return (inherited::net_SaveRelevant() || BOOL(PPhysicsShell()!=NULL)); +} + +void CBaseMonster::net_Export(NET_Packet& P) +{ + R_ASSERT (Local()); + + // export last known packet + R_ASSERT (!NET.empty()); + net_update& N = NET.back(); + P.w_float (GetfHealth()); + P.w_u32 (N.dwTimeStamp); + P.w_u8 (0); + P.w_vec3 (N.p_pos); + P.w_float /*w_angle8*/ (N.o_model); + P.w_float /*w_angle8*/ (N.o_torso.yaw); + P.w_float /*w_angle8*/ (N.o_torso.pitch); + P.w_float /*w_angle8*/ (N.o_torso.roll); + P.w_u8 (u8(g_Team())); + P.w_u8 (u8(g_Squad())); + P.w_u8 (u8(g_Group())); + + GameGraph::_GRAPH_ID l_game_vertex_id = ai_location().game_vertex_id(); + P.w (&l_game_vertex_id, sizeof(l_game_vertex_id)); + P.w (&l_game_vertex_id, sizeof(l_game_vertex_id)); +// P.w (&m_fGoingSpeed, sizeof(m_fGoingSpeed)); +// P.w (&m_fGoingSpeed, sizeof(m_fGoingSpeed)); + float f1 = 0; + if (ai().game_graph().valid_vertex_id(l_game_vertex_id)) { + f1 = Position().distance_to (ai().game_graph().vertex(l_game_vertex_id)->level_point()); + P.w (&f1, sizeof(f1)); + f1 = Position().distance_to (ai().game_graph().vertex(l_game_vertex_id)->level_point()); + P.w (&f1, sizeof(f1)); + } + else { + P.w (&f1, sizeof(f1)); + P.w (&f1, sizeof(f1)); + } + +} + +void CBaseMonster::net_Import(NET_Packet& P) +{ + R_ASSERT (Remote()); + net_update N; + + u8 flags; + + float health; + P.r_float (health); + SetfHealth (health); + + P.r_u32 (N.dwTimeStamp); + P.r_u8 (flags); + P.r_vec3 (N.p_pos); + P.r_float /*r_angle8*/ (N.o_model); + P.r_float /*r_angle8*/ (N.o_torso.yaw); + P.r_float /*r_angle8*/ (N.o_torso.pitch); + P.r_float /*r_angle8*/ (N.o_torso.roll ); + id_Team = P.r_u8(); + id_Squad = P.r_u8(); + id_Group = P.r_u8(); + + GameGraph::_GRAPH_ID l_game_vertex_id = ai_location().game_vertex_id(); + P.r (&l_game_vertex_id, sizeof(l_game_vertex_id)); + P.r (&l_game_vertex_id, sizeof(l_game_vertex_id)); + + if (NET.empty() || (NET.back().dwTimeStamplevel_point()); + P.r (&f1, sizeof(f1)); + f1 = Position().distance_to (ai().game_graph().vertex(l_game_vertex_id)->level_point()); + P.r (&f1, sizeof(f1)); + } + else { + P.r (&f1, sizeof(f1)); + P.r (&f1, sizeof(f1)); + } + + + setVisible (TRUE); + setEnabled (TRUE); +} diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster_path.cpp b/src/xrGameLA/ai/monsters/basemonster/base_monster_path.cpp new file mode 100644 index 000000000..448c70be5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster_path.cpp @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : base_monster_path.cpp +// Created : 26.05.2003 +// Modified : 26.05.2003 +// Author : Serge Zhem +// Description : Path finding, curve building, position prediction +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "base_monster.h" +#include "../corpse_cover.h" +#include "../../../cover_manager.h" +#include "../../../cover_point.h" +#include "../../../ai_space.h" +#include "../control_direction_base.h" + +// каждый монстр может по-разному реализвать эту функ (e.g. кровосос с поворотом головы и т.п.) +void CBaseMonster::LookPosition(Fvector to_point, float angular_speed) +{ + // по-умолчанию просто изменить movement().m_body.target.yaw + Fvector d; + d.set(to_point); + d.sub(Position()); + + // установить текущий угол + dir().set_heading(angle_normalize(-d.getH())); +} + +////////////////////////////////////////////////////////////////////////// +// Covers +////////////////////////////////////////////////////////////////////////// + +bool CBaseMonster::GetCorpseCover(Fvector &position, u32 &vertex_id) +{ + m_corpse_cover_evaluator->setup(10.f,50.f); + const CCoverPoint *point = ai().cover_manager().best_cover(Position(),30.f,*m_corpse_cover_evaluator); + if (!point) return false; + + position = point->m_position; + vertex_id = point->m_level_vertex_id; + return true; +} + +bool CBaseMonster::GetCoverFromEnemy(const Fvector &enemy_pos, Fvector &position, u32 &vertex_id) +{ + m_enemy_cover_evaluator->setup(enemy_pos, 30.f,50.f); + const CCoverPoint *point = ai().cover_manager().best_cover(Position(),40.f,*m_enemy_cover_evaluator); + if (!point) return false; + + position = point->m_position; + vertex_id = point->m_level_vertex_id; + return true; +} + +bool CBaseMonster::GetCoverFromPoint(const Fvector &pos, Fvector &position, u32 &vertex_id, float min_dist, float max_dist, float radius) +{ + m_enemy_cover_evaluator->setup(pos, min_dist,max_dist); + const CCoverPoint *point = ai().cover_manager().best_cover(Position(),radius,*m_enemy_cover_evaluator); + if (!point) return false; + + position = point->m_position; + vertex_id = point->m_level_vertex_id; + return true; +} + +bool CBaseMonster::GetCoverCloseToPoint(const Fvector &dest_pos, float min_dist, float max_dist, float deviation, float radius ,Fvector &position, u32 &vertex_id) +{ + m_cover_evaluator_close_point->setup(dest_pos,min_dist, max_dist,deviation); + const CCoverPoint *point = ai().cover_manager().best_cover(Position(),radius,*m_cover_evaluator_close_point); + if (!point) return false; + + position = point->m_position; + vertex_id = point->m_level_vertex_id; + return true; +} diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster_script.cpp b/src/xrGameLA/ai/monsters/basemonster/base_monster_script.cpp new file mode 100644 index 000000000..cb70d0e39 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster_script.cpp @@ -0,0 +1,347 @@ +#include "pch_script.h" +#include "base_monster.h" +#include "../../../script_entity_action.h" +#include "../../../phmovementcontrol.h" +#include "../../../sight_manager.h" +#include "../../../detail_path_manager.h" +#include "../../../ai_object_location.h" +#include "../../../sound_player.h" +#include "../../../ai_monster_space.h" +#include "../state_manager.h" + +#include "../../../ai_debug.h" +#include "../../../level.h" +#include "../../../level_debug.h" + +#include "../control_animation_base.h" +#include "../control_path_builder_base.h" + +#include "../../../patrol_path_manager.h" +#include "../../../patrol_path_manager_space.h" + + +using namespace MonsterSpace; +using namespace MonsterSound; + +////////////////////////////////////////////////////////////////////////// +bool CBaseMonster::bfAssignMovement (CScriptEntityAction *tpEntityAction) +{ + CScriptMovementAction &l_tMovementAction = tpEntityAction->m_tMovementAction; + + // check if completed + if (l_tMovementAction.m_bCompleted) return(false); + + // check if alive + CEntityAlive *entity_alive = smart_cast(this); + if (entity_alive && !entity_alive->g_Alive()) { + l_tMovementAction.m_bCompleted = true; + return (false); + } + + if (control().path_builder().detail().time_path_built() >= tpEntityAction->m_tActionCondition.m_tStartTime) { + if ((l_tMovementAction.m_fDistToEnd > 0) && control().path_builder().is_path_end(l_tMovementAction.m_fDistToEnd)) { + l_tMovementAction.m_bCompleted = true; + + } + if (control().path_builder().actual_all() && control().path_builder().path_completed()) { + l_tMovementAction.m_bCompleted = true; + return false; + } + } + + // translate script.action into anim().action + switch (l_tMovementAction.m_tMoveAction) { + case eMA_WalkFwd: anim().m_tAction = ACT_WALK_FWD; break; + case eMA_WalkBkwd: anim().m_tAction = ACT_WALK_BKWD; break; + case eMA_Run: anim().m_tAction = ACT_RUN; break; + case eMA_Drag: anim().m_tAction = ACT_DRAG; break; + case eMA_Steal: anim().m_tAction = ACT_STEAL; break; + } + + m_force_real_speed = (l_tMovementAction.m_tSpeedParam == eSP_ForceSpeed); + + switch (l_tMovementAction.m_tGoalType) { + + case CScriptMovementAction::eGoalTypeObject : { + CGameObject *l_tpGameObject = smart_cast(l_tMovementAction.m_tpObjectToGo); + path().set_target_point (l_tpGameObject->Position(), l_tpGameObject->ai_location().level_vertex_id()); + break; + } + case CScriptMovementAction::eGoalTypePatrolPath : + path().set_patrol_path_type (); + control().path_builder().set_path_type (MovementManager::ePathTypePatrolPath); + control().path_builder().patrol().set_path (l_tMovementAction.m_path,l_tMovementAction.m_path_name); + control().path_builder().patrol().set_start_type (l_tMovementAction.m_tPatrolPathStart); + control().path_builder().patrol().set_route_type (l_tMovementAction.m_tPatrolPathStop); + control().path_builder().patrol().set_random (l_tMovementAction.m_bRandom); + if (l_tMovementAction.m_previous_patrol_point != u32(-1)) { + control().path_builder().patrol().set_previous_point(l_tMovementAction.m_previous_patrol_point); + } + break; + + case CScriptMovementAction::eGoalTypePathPosition : + case CScriptMovementAction::eGoalTypeNoPathPosition : + path().set_target_point (l_tMovementAction.m_tDestinationPosition); + break; + case CScriptMovementAction::eGoalTypePathNodePosition : + path().set_target_point (l_tMovementAction.m_tDestinationPosition, l_tMovementAction.m_tNodeID); + break; + } + + return (true); +} + +/////////////////////////////////////////////////////////////////////////// +bool CBaseMonster::bfAssignObject(CScriptEntityAction *tpEntityAction) +{ + if (!inherited::bfAssignObject(tpEntityAction)) + return (false); + +// CScriptObjectAction &l_tObjectAction = tpEntityAction->m_tObjectAction; +// if (!l_tObjectAction.m_tpObject) +// return (false == (l_tObjectAction.m_bCompleted = true)); +// +// CEntityAlive *l_tpEntity = smart_cast(l_tObjectAction.m_tpObject); +// if (!l_tpEntity) return (false == (l_tObjectAction.m_bCompleted = true)); +// +// switch (l_tObjectAction.m_tGoalType) { +// case eObjectActionTake: +// m_PhysicMovementControl->PHCaptureObject(l_tpEntity); +// break; +// case eObjectActionDrop: +// m_PhysicMovementControl->PHReleaseObject(); +// break; +// } +// +// l_tObjectAction.m_bCompleted = true; + return (true); +} + + +bool CBaseMonster::bfAssignWatch(CScriptEntityAction *tpEntityAction) +{ + if (!inherited::bfAssignWatch(tpEntityAction)) + return (false); + + // Инициализировать action + anim().m_tAction = ACT_STAND_IDLE; + + CScriptWatchAction &l_tWatchAction = tpEntityAction->m_tWatchAction; + if (l_tWatchAction.completed()) return false; + + Fvector new_pos; + switch (l_tWatchAction.m_tWatchType) { + case SightManager::eSightTypePosition: + LookPosition(l_tWatchAction.m_tWatchVector); + break; + case SightManager::eSightTypeDirection: + new_pos.mad(Position(), l_tWatchAction.m_tWatchVector, 2.f); + LookPosition(new_pos); + break; + } + + + if (!control().direction().is_turning()) + l_tWatchAction.m_bCompleted = true; + else + l_tWatchAction.m_bCompleted = false; + + return (!l_tWatchAction.m_bCompleted); +} + +bool CBaseMonster::bfAssignAnimation(CScriptEntityAction *tpEntityAction) +{ + if (!inherited::bfAssignAnimation(tpEntityAction)) + return (false); + + CScriptAnimationAction &l_tAnimAction = tpEntityAction->m_tAnimationAction; + if (l_tAnimAction.completed()) return false; + + // translate animation.action into anim().action + switch (l_tAnimAction.m_tAnimAction) { + case eAA_StandIdle: anim().m_tAction = ACT_STAND_IDLE; break; + case eAA_SitIdle: anim().m_tAction = ACT_SIT_IDLE; break; + case eAA_LieIdle: anim().m_tAction = ACT_LIE_IDLE; break; + case eAA_Eat: anim().m_tAction = ACT_EAT; break; + case eAA_Sleep: anim().m_tAction = ACT_SLEEP; break; + case eAA_Rest: anim().m_tAction = ACT_REST; break; + case eAA_Attack: anim().m_tAction = ACT_ATTACK; break; + case eAA_LookAround: anim().m_tAction = ACT_LOOK_AROUND; break; + } + + return (true); +} + +bool CBaseMonster::bfAssignSound(CScriptEntityAction *tpEntityAction) +{ + CScriptSoundAction &l_tAction = tpEntityAction->m_tSoundAction; + if (l_tAction.completed()) return false; + + if (l_tAction.m_monster_sound == MonsterSound::eMonsterSoundDummy) { + if (!inherited::bfAssignSound(tpEntityAction)) + return (false); + } + + switch (l_tAction.m_monster_sound) { + case eMonsterSoundIdle: sound().play(eMonsterSoundIdle, 0, 0, (l_tAction.m_monster_sound_delay == int(-1)) ? db().m_dwIdleSndDelay : l_tAction.m_monster_sound_delay); break; + case eMonsterSoundEat: sound().play(eMonsterSoundEat, 0, 0, (l_tAction.m_monster_sound_delay == int(-1)) ? db().m_dwEatSndDelay : l_tAction.m_monster_sound_delay); break; + case eMonsterSoundAggressive: sound().play(eMonsterSoundAggressive, 0, 0, (l_tAction.m_monster_sound_delay == int(-1)) ? db().m_dwAttackSndDelay : l_tAction.m_monster_sound_delay); break; + case eMonsterSoundAttackHit: sound().play(eMonsterSoundAttackHit); break; + case eMonsterSoundTakeDamage: sound().play(eMonsterSoundTakeDamage); break; + case eMonsterSoundDie: sound().play(eMonsterSoundDie); break; + case eMonsterSoundThreaten: sound().play(eMonsterSoundThreaten, 0, 0, (l_tAction.m_monster_sound_delay == int(-1)) ? db().m_dwAttackSndDelay : l_tAction.m_monster_sound_delay); break; + case eMonsterSoundSteal: sound().play(eMonsterSoundSteal, 0, 0, (l_tAction.m_monster_sound_delay == int(-1)) ? db().m_dwAttackSndDelay : l_tAction.m_monster_sound_delay); break; + case eMonsterSoundPanic: sound().play(eMonsterSoundPanic, 0, 0, (l_tAction.m_monster_sound_delay == int(-1)) ? db().m_dwAttackSndDelay : l_tAction.m_monster_sound_delay); break; + } + + return (true); +} + +bool CBaseMonster::bfAssignMonsterAction(CScriptEntityAction *tpEntityAction) +{ + if (!inherited::bfAssignMonsterAction(tpEntityAction)) return false; + + CScriptMonsterAction &l_tAction = tpEntityAction->m_tMonsterAction; + if (l_tAction.completed()) return false; + + CEntityAlive *pE = smart_cast(l_tAction.m_tObject); + + switch(l_tAction.m_tAction) { + case eGA_Rest: + StateMan->force_script_state(eStateRest); + break; + case eGA_Eat: + if (pE && !pE->getDestroy() && !pE->g_Alive()){ + CorpseMan.force_corpse(pE); + StateMan->force_script_state(eStateEat); + } else StateMan->force_script_state(eStateRest); + + break; + case eGA_Attack: + if (pE && !pE->getDestroy() && pE->g_Alive()){ + EnemyMan.force_enemy(pE); + StateMan->force_script_state(eStateAttack); + } else StateMan->force_script_state(eStateRest); + + break; + case eGA_Panic: + if (pE && !pE->getDestroy() && pE->g_Alive()){ + EnemyMan.force_enemy (pE); + StateMan->force_script_state (eStatePanic); + } else StateMan->force_script_state (eStateRest); + break; + } + + m_script_state_must_execute = true; + return (!l_tAction.m_bCompleted); +} + + + +void CBaseMonster::ProcessScripts() +{ + if (!g_Alive()) return; + if (m_script_processing_active) return; + + m_script_processing_active = true; + + //movement().Update_Initialize (); + + // Выполнить скриптовые actions + m_script_state_must_execute = false; + inherited::ProcessScripts (); + + Device.dwTimeGlobal = Device.dwTimeGlobal; + + // обновить мир (память, враги, объекты) + UpdateMemory (); + + anim().accel_deactivate (); + + // если из скрипта выбрано действие по универсальной схеме, выполнить его + if (m_script_state_must_execute) + StateMan->execute_script_state (); + + TranslateActionToPathParams (); + + // обновить путь + //movement().Update_Execute (); + + //anim().Update (); + + // установить текущую скорость + //movement().Update_Finalize (); + + // Удалить все враги и объекты, которые были принудительно установлены + // во время выполнения скриптового действия + if (m_script_state_must_execute) { + EnemyMan.unforce_enemy(); + CorpseMan.unforce_corpse(); + } + + m_force_real_speed = false; + m_script_processing_active = false; + +#ifdef DEBUG + if (psAI_Flags.test(aiMonsterDebug)) { + DBG().object_info(this,this).remove_item (u32(0)); + DBG().object_info(this,this).remove_item (u32(1)); + DBG().object_info(this,this).add_item (*cName(), D3DCOLOR_XRGB(255,0,0), 0); + DBG().object_info(this,this).add_item ("Under script", D3DCOLOR_XRGB(255,0,0), 1); + } else { + DBG().object_info(this,this).clear (); + } +#endif + +} + +CEntity *CBaseMonster::GetCurrentEnemy() +{ + CEntity *enemy = 0; + + if (EnemyMan.get_enemy()) + enemy = const_cast(smart_cast(EnemyMan.get_enemy())); + + if (!enemy || enemy->getDestroy() || !enemy->g_Alive()) enemy = 0; + + return (enemy); +} + +CEntity *CBaseMonster::GetCurrentCorpse() +{ + CEntity *corpse = 0; + + if (CorpseMan.get_corpse()) + corpse = const_cast(smart_cast(CorpseMan.get_corpse())); + + if (!corpse || corpse->getDestroy() || corpse->g_Alive()) corpse = 0; + + return (corpse); +} + +void CBaseMonster::SetScriptControl(const bool bScriptControl, shared_str caScriptName) +{ + if (StateMan) StateMan->critical_finalize(); + + CScriptEntity::SetScriptControl(bScriptControl, caScriptName); +} + +int CBaseMonster::get_enemy_strength() +{ + if (EnemyMan.get_enemy()) { + switch (EnemyMan.get_danger_type()) { + case eVeryStrong : return (4); + case eStrong : return (3); + case eNormal : return (2); + case eWeak : return (1); + } + } + + return (0); +} + +void CBaseMonster::vfFinishAction(CScriptEntityAction *tpEntityAction) +{ + inherited::vfFinishAction(tpEntityAction); +} + diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster_startup.cpp b/src/xrGameLA/ai/monsters/basemonster/base_monster_startup.cpp new file mode 100644 index 000000000..78cc7cf68 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster_startup.cpp @@ -0,0 +1,393 @@ +#include "stdafx.h" +#include "base_monster.h" +#include "../../../ai_space.h" +#include "../../../hit.h" +#include "../../../PHDestroyable.h" +#include "../../../CharacterPhysicsSupport.h" +#include "../../../phmovementcontrol.h" +#include "../ai_monster_squad_manager.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../level_graph.h" +#include "../corpse_cover.h" +#include "../../../cover_evaluators.h" +#include "../../../sound_player.h" + +#include "../state_manager.h" +#include "../controlled_entity.h" +#include "../anomaly_detector.h" +#include "../monster_cover_manager.h" +#include "../monster_home.h" +#include "../../../ai_object_location.h" +#include "../../../level.h" +#include "../../../xrserver_objects_alife_monsters.h" +#include "../../../alife_simulator.h" +#include "../../../alife_object_registry.h" +#include "../../../xrServer.h" +#include "../../../inventory_item.h" +#include "../../../xrServer_Objects_ALife.h" + +void CBaseMonster::Load(LPCSTR section) +{ + // load parameters from ".ltx" file + inherited::Load (section); + + m_corpse_cover_evaluator = new CMonsterCorpseCoverEvaluator(&movement().restrictions()); + m_enemy_cover_evaluator = new CCoverEvaluatorFarFromEnemy(&movement().restrictions()); + m_cover_evaluator_close_point = new CCoverEvaluatorCloseToEnemy(&movement().restrictions()); + + movement().Load (section); + + MeleeChecker.load (section); + Morale.load (section); + + m_pPhysics_support ->in_Load(section); + + SetfHealth ( (float)pSettings->r_u32 (section,"Health")); + + m_controlled = smart_cast(this); + + settings_load (section); + + control().load (section); + + m_anomaly_detector->load (section); + CoverMan->load (); + + m_rank = (pSettings->line_exist(section,"rank")) ? int(pSettings->r_u32(section,"rank")) : 0; + +// if (pSettings->line_exist(section,"Spawn_Inventory_Item_Section")) { +// m_item_section = pSettings->r_string(section,"Spawn_Inventory_Item_Section"); +// m_spawn_probability = pSettings->r_float(section,"Spawn_Inventory_Item_Probability"); +// } else m_spawn_probability = 0.f; + + m_melee_rotation_factor = READ_IF_EXISTS(pSettings,r_float,section,"Melee_Rotation_Factor", 1.5f); + berserk_always = !!READ_IF_EXISTS(pSettings,r_bool,section,"berserk_always", false); + + if (pSettings->line_exist(section,"vampire_speed")) { + m_vampire_want_speed = !!pSettings->r_float (section,"vampire_speed"); + } + +} + +// if sound is absent just do not load that one +#define LOAD_SOUND(sound_name,_type,_prior,_mask,_int_type) \ + if (pSettings->line_exist(section,sound_name)) \ + sound().add(pSettings->r_string(section,sound_name), DEFAULT_SAMPLE_COUNT,_type,_prior,u32(_mask),_int_type,"bip01_head"); + +void CBaseMonster::reload (LPCSTR section) +{ + CCustomMonster::reload (section); + + if (!CCustomMonster::use_simplified_visual()) + CStepManager::reload (section); + + CInventoryOwner::reload (section); + movement().reload (section); + + // load base sounds + LOAD_SOUND("sound_idle", SOUND_TYPE_MONSTER_TALKING, MonsterSound::eLowPriority, MonsterSound::eBaseChannel, MonsterSound::eMonsterSoundIdle); + LOAD_SOUND("sound_distant_idle", SOUND_TYPE_MONSTER_TALKING, MonsterSound::eLowPriority+1, MonsterSound::eBaseChannel, MonsterSound::eMonsterSoundIdleDistant); + LOAD_SOUND("sound_eat", SOUND_TYPE_MONSTER_TALKING, MonsterSound::eNormalPriority + 4, MonsterSound::eBaseChannel, MonsterSound::eMonsterSoundEat); + LOAD_SOUND("sound_aggressive", SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eNormalPriority + 3, MonsterSound::eBaseChannel, MonsterSound::eMonsterSoundAggressive); + LOAD_SOUND("sound_attack_hit", SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority + 1, MonsterSound::eCaptureAllChannels, MonsterSound::eMonsterSoundAttackHit); + LOAD_SOUND("sound_take_damage", SOUND_TYPE_MONSTER_INJURING, MonsterSound::eHighPriority, MonsterSound::eCaptureAllChannels, MonsterSound::eMonsterSoundTakeDamage); + LOAD_SOUND("sound_strike", SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eNormalPriority, MonsterSound::eChannelIndependent, MonsterSound::eMonsterSoundStrike); + LOAD_SOUND("sound_die", SOUND_TYPE_MONSTER_DYING, MonsterSound::eCriticalPriority, MonsterSound::eCaptureAllChannels, MonsterSound::eMonsterSoundDie); + LOAD_SOUND("sound_die_in_anomaly", SOUND_TYPE_MONSTER_DYING, MonsterSound::eCriticalPriority, MonsterSound::eCaptureAllChannels, MonsterSound::eMonsterSoundDieInAnomaly); + LOAD_SOUND("sound_threaten", SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eNormalPriority, MonsterSound::eBaseChannel, MonsterSound::eMonsterSoundThreaten); + LOAD_SOUND("sound_steal", SOUND_TYPE_MONSTER_STEP, MonsterSound::eNormalPriority + 1, MonsterSound::eBaseChannel, MonsterSound::eMonsterSoundSteal); + LOAD_SOUND("sound_panic", SOUND_TYPE_MONSTER_STEP, MonsterSound::eNormalPriority + 2, MonsterSound::eBaseChannel, MonsterSound::eMonsterSoundPanic); + + control().reload (section); + + // load monster type + m_monster_type = eMonsterTypeUniversal; + if (pSettings->line_exist(section,"monster_type")) { + if (xr_strcmp(pSettings->r_string(section,"monster_type"), "indoor") == 0) + m_monster_type = eMonsterTypeIndoor; + else if (xr_strcmp(pSettings->r_string(section,"monster_type"), "outdoor") == 0) + m_monster_type = eMonsterTypeOutdoor; + } + + Home->load ("home"); + + // save panic_threshold + m_default_panic_threshold = m_panic_threshold; +} + + +void CBaseMonster::reinit() +{ + inherited::reinit (); + CInventoryOwner::reinit (); + + EnemyMemory.clear (); + SoundMemory.clear (); + CorpseMemory.clear (); + HitMemory.clear (); + + EnemyMan.reinit (); + CorpseMan.reinit (); + + StateMan->reinit (); + + Morale.reinit (); + + m_bDamaged = false; + m_bAngry = false; + m_bAggressive = false; + m_bSleep = false; + m_bRunTurnLeft = false; + m_bRunTurnRight = false; + + state_invisible = false; + + m_force_real_speed = false; + m_script_processing_active = false; + + if (m_controlled) m_controlled->on_reinit(); + + ignore_collision_hit = false; + + control().reinit (); + + m_anomaly_detector->reinit (); + + m_skip_transfer_enemy = false; + + MeleeChecker.init_attack (); + + time_berserk_start = 0; + + m_prev_sound_type = MonsterSound::eMonsterSoundIdle; + +#ifdef DEBUG + m_show_debug_info = 0; +#endif + +} + + +BOOL CBaseMonster::net_Spawn (CSE_Abstract* DC) +{ + if (!inherited::net_Spawn(DC)) + return(FALSE); + + CSE_Abstract *e = (CSE_Abstract*)(DC); +#ifndef PRIQUEL + m_pPhysics_support->in_NetSpawn (e);//этот выззов с послудующими не связан, + //но там есть хак - запуск анимации на всякий случай если никто больше ее не запустил + //поэтому в основной версии на всякий случай пусть будет здесь, + //но для animation movement controllr он должен быть в конце чтобы знать что он создался на споне +#endif + + R_ASSERT2 (ai().get_level_graph() && ai().get_cross_table() && (ai().level_graph().level_id() != u32(-1)),"There is no AI-Map, level graph, cross table, or graph is not compiled into the game graph!"); + + monster_squad().register_member ((u8)g_Team(),(u8)g_Squad(),(u8)g_Group(), this); + + settings_overrides (); + +#ifdef PRIQUEL + if (GetScriptControl()) { + m_control_manager->animation().reset_data (); + ProcessScripts (); + } + m_pPhysics_support->in_NetSpawn (e); +#endif + + + // spawn inventory item +// if (ai().get_alife()) { +// +// CSE_ALifeMonsterBase *se_monster = smart_cast(ai().alife().objects().object(ID())); +// VERIFY (se_monster); +// +// if (se_monster->m_flags.is(CSE_ALifeMonsterBase::flNeedCheckSpawnItem)) { +// float prob = Random.randF(); +// if ((prob < m_spawn_probability) || fsimilar(m_spawn_probability,1.f)) +// se_monster->m_flags.set(CSE_ALifeMonsterBase::flSkipSpawnItem, FALSE); +// +// se_monster->m_flags.set(CSE_ALifeMonsterBase::flNeedCheckSpawnItem, FALSE); +// } +// +// if (!se_monster->m_flags.is(CSE_ALifeMonsterBase::flSkipSpawnItem)) { +// CSE_Abstract *object = Level().spawn_item (m_item_section,Position(),ai_location().level_vertex_id(),ID(),true); +// CSE_ALifeObject *alife_object = smart_cast(object); +// if (alife_object) +// alife_object->m_flags.set (CSE_ALifeObject::flCanSave,FALSE); +// +// { +// NET_Packet P; +// object->Spawn_Write (P,TRUE); +// Level().Send (P,net_flags(TRUE)); +// F_entity_Destroy (object); +// } +// } +// } + + return(TRUE); +} + +void CBaseMonster::net_Destroy() +{ + // функция должена быть вызвана перед inherited + if (m_controlled) m_controlled->on_destroy (); + if (StateMan) StateMan->critical_finalize (); + + inherited::net_Destroy (); + + CInventoryOwner::net_Destroy (); + + m_pPhysics_support->in_NetDestroy (); + + monster_squad().remove_member ((u8)g_Team(),(u8)g_Squad(),(u8)g_Group(),this); + +#ifdef DEBUG + m_show_debug_info = 0; +#endif + +} + +#define READ_SETTINGS(var,name,method,ltx,section) {\ + if (ltx == pSettings) var = ltx->method(section,name); \ + else if (ltx->line_exist(section,name)) var = ltx->method(section,name);\ +} + +void CBaseMonster::settings_read(const CInifile *ini, LPCSTR section, SMonsterSettings &data) +{ + READ_SETTINGS(data.m_fSoundThreshold, "SoundThreshold", r_float, ini, section); + + if (ability_run_attack()) { + READ_SETTINGS(data.m_run_attack_path_dist, "RunAttack_PathDistance", r_float, ini, section); + READ_SETTINGS(data.m_run_attack_start_dist, "RunAttack_StartDistance", r_float, ini, section); + } + + READ_SETTINGS(data.m_dwDayTimeBegin, "DayTime_Begin", r_u32, ini, section); + READ_SETTINGS(data.m_dwDayTimeEnd, "DayTime_End", r_u32, ini, section); + + READ_SETTINGS(data.m_fDistToCorpse, "distance_to_corpse", r_float, ini, section); + READ_SETTINGS(data.satiety_threshold, "satiety_threshold", r_float, ini, section); + + READ_SETTINGS(data.m_fDamagedThreshold, "DamagedThreshold", r_float, ini, section); + + READ_SETTINGS(data.m_dwIdleSndDelay, "idle_sound_delay", r_u32, ini, section); + READ_SETTINGS(data.m_dwEatSndDelay, "eat_sound_delay", r_u32, ini, section); + READ_SETTINGS(data.m_dwAttackSndDelay, "attack_sound_delay", r_u32, ini, section); + + READ_SETTINGS(data.m_dwDistantIdleSndDelay, "distant_idle_sound_delay", r_u32, ini, section); + READ_SETTINGS(data.m_fDistantIdleSndRange, "distant_idle_sound_range", r_float, ini, section); + + READ_SETTINGS(data.m_fEatFreq, "eat_freq", r_float, ini, section); + READ_SETTINGS(data.m_fEatSlice, "eat_slice", r_float, ini, section); + READ_SETTINGS(data.m_fEatSliceWeight, "eat_slice_weight", r_float, ini, section); + + READ_SETTINGS(data.m_legs_number, "LegsCount", r_u8, ini, section); + READ_SETTINGS(data.m_max_hear_dist, "max_hear_dist", r_float, ini, section); + + // Load attack postprocess + if (ini->line_exist(section,"attack_effector")) { + + LPCSTR ppi_section = ini->r_string(section, "attack_effector"); + + READ_SETTINGS(data.m_attack_effector.ppi.duality.h, "duality_h", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.ppi.duality.v, "duality_v", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.ppi.gray, "gray", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.ppi.blur, "blur", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.ppi.noise.intensity, "noise_intensity", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.ppi.noise.grain, "noise_grain", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.ppi.noise.fps, "noise_fps", r_float, ini, ppi_section); + + VERIFY(!fis_zero(data.m_attack_effector.ppi.noise.fps)); + + if (ini->line_exist(ppi_section,"color_base")) + sscanf(ini->r_string(ppi_section,"color_base"), "%f,%f,%f", &data.m_attack_effector.ppi.color_base.r, &data.m_attack_effector.ppi.color_base.g, &data.m_attack_effector.ppi.color_base.b); + if (ini->line_exist(ppi_section,"color_base")) + sscanf(ini->r_string(ppi_section,"color_gray"), "%f,%f,%f", &data.m_attack_effector.ppi.color_gray.r, &data.m_attack_effector.ppi.color_gray.g, &data.m_attack_effector.ppi.color_gray.b); + if (ini->line_exist(ppi_section,"color_base")) + sscanf(ini->r_string(ppi_section,"color_add"), "%f,%f,%f", &data.m_attack_effector.ppi.color_add.r, &data.m_attack_effector.ppi.color_add.g, &data.m_attack_effector.ppi.color_add.b); + + READ_SETTINGS(data.m_attack_effector.time, "time", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.time_attack, "time_attack", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.time_release, "time_release", r_float, ini, ppi_section); + + READ_SETTINGS(data.m_attack_effector.ce_time, "ce_time", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.ce_amplitude, "ce_amplitude", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.ce_period_number, "ce_period_number", r_float, ini, ppi_section); + READ_SETTINGS(data.m_attack_effector.ce_power, "ce_power", r_float, ini, ppi_section); + } +} + +void CBaseMonster::settings_load(LPCSTR section) +{ + SMonsterSettings data; + + settings_read (pSettings, section, data); + + u32 crc = crc32(&data,sizeof(SMonsterSettings)); + m_base_settings.create (crc,1,&data); +} + + +void CBaseMonster::settings_overrides() +{ + SMonsterSettings *data; + data = *m_base_settings; + + if (spawn_ini() && spawn_ini()->section_exist("settings_overrides")) { + settings_read (spawn_ini(),"settings_overrides", (*data)); + } + + u32 crc = crc32(data,sizeof(SMonsterSettings)); + m_current_settings.create (crc,1,data); +} + +void CBaseMonster::on_before_sell (CInventoryItem *item) +{ + // since there can be only single item in the monster inventory + CSE_Abstract *object = Level().Server->game->get_entity_from_eid(item->object().ID()); + VERIFY (object); + CSE_ALifeObject *alife_object = smart_cast(object); + if (alife_object) + alife_object->m_flags.set (CSE_ALifeObject::flCanSave,TRUE); +} + +void CBaseMonster::load_critical_wound_bones() +{ + // animation does not exist - no bones loaded + if (pSettings->line_exist(cNameSect(),"critical_wound_anim_head")) { + fill_bones_body_parts ("critical_wound_bones_head", critical_wound_type_head); + m_critical_wound_anim_head = pSettings->r_string(cNameSect(),"critical_wound_anim_head"); + } + + if (pSettings->line_exist(cNameSect(),"critical_wound_anim_torso")) { + fill_bones_body_parts ("critical_wound_bones_torso", critical_wound_type_torso); + m_critical_wound_anim_torso = pSettings->r_string(cNameSect(),"critical_wound_anim_torso"); + + } + + if (pSettings->line_exist(cNameSect(),"critical_wound_anim_legs")) { + fill_bones_body_parts ("critical_wound_bones_legs", critical_wound_type_legs); + m_critical_wound_anim_legs = pSettings->r_string(cNameSect(),"critical_wound_anim_legs"); + } +} + +void CBaseMonster::fill_bones_body_parts (LPCSTR body_part, CriticalWoundType wound_type) +{ + LPCSTR body_parts_section = pSettings->r_string(cNameSect(),body_part); + + IKinematics *kinematics = smart_cast(Visual()); + VERIFY (kinematics); + + CInifile::Sect &body_part_section = pSettings->r_section(body_parts_section); + CInifile::SectCIt I = body_part_section.Data.begin(); + CInifile::SectCIt E = body_part_section.Data.end(); + for ( ; I != E; ++I) + m_bones_body_parts.insert ( + std::make_pair( + kinematics->LL_BoneID((*I).first), + u32(wound_type) + ) + ); +} + diff --git a/src/xrGameLA/ai/monsters/basemonster/base_monster_think.cpp b/src/xrGameLA/ai/monsters/basemonster/base_monster_think.cpp new file mode 100644 index 000000000..722b80b32 --- /dev/null +++ b/src/xrGameLA/ai/monsters/basemonster/base_monster_think.cpp @@ -0,0 +1,112 @@ +#include "stdafx.h" +#include "base_monster.h" +#include "../ai_monster_squad.h" +#include "../ai_monster_squad_manager.h" +#include "../../../profiler.h" +#include "../state_manager.h" +#include "../../../PhysicsShell.h" +#include "../../../detail_path_manager.h" +#include "../monster_velocity_space.h" +#include "../../../level.h" +#include "../control_animation_base.h" + +void CBaseMonster::Think() +{ + START_PROFILE("Base Monster/Think"); + + if (!g_Alive() || getDestroy()) return; + + // Инициализировать + InitThink (); + anim().ScheduledInit (); + + // Обновить память + START_PROFILE("Base Monster/Think/Update Memory"); + UpdateMemory (); + STOP_PROFILE; + + // Обновить сквад + START_PROFILE("Base Monster/Think/Update Squad"); + monster_squad().update (this); + STOP_PROFILE; + + // Запустить FSM + START_PROFILE("Base Monster/Think/FSM"); + update_fsm (); + STOP_PROFILE; + + STOP_PROFILE; +} + +void CBaseMonster::update_fsm() +{ + StateMan->update (); + + // завершить обработку установленных в FSM параметров + post_fsm_update (); + + TranslateActionToPathParams (); + + // информировать squad о своих целях + squad_notify (); + +#ifdef DEBUG + debug_fsm (); +#endif +} + +void CBaseMonster::post_fsm_update() +{ + if (!EnemyMan.get_enemy()) return; + + EMonsterState state = StateMan->get_state_type(); + + + // Look at enemy while running + m_bRunTurnLeft = m_bRunTurnRight = false; + + + if (is_state(state, eStateAttack) && control().path_builder().is_moving_on_path()) { + + float dir_yaw = control().path_builder().detail().direction().getH(); + float yaw_target = Fvector().sub(EnemyMan.get_enemy()->Position(), Position()).getH(); + + float angle_diff = angle_difference(yaw_target, dir_yaw); + + if ((angle_diff > PI_DIV_3) && (angle_diff < 5 * PI_DIV_6)) { + if (from_right(dir_yaw, yaw_target)) m_bRunTurnRight = true; + else m_bRunTurnLeft = true; + } + } +} + +void CBaseMonster::squad_notify() +{ + CMonsterSquad *squad = monster_squad().get_squad(this); + SMemberGoal goal; + + EMonsterState state = StateMan->get_state_type(); + + if (is_state(state, eStateAttack)) { + + goal.type = MG_AttackEnemy; + goal.entity = const_cast(EnemyMan.get_enemy()); + + } else if (is_state(state, eStateRest)) { + goal.entity = squad->GetLeader(); + + if (state == eStateRest_Idle) goal.type = MG_Rest; + else if (state == eStateRest_WalkGraphPoint) goal.type = MG_WalkGraph; + else if (state == eStateRest_MoveToHomePoint) goal.type = MG_WalkGraph; + else if (state == eStateCustomMoveToRestrictor) goal.type = MG_WalkGraph; + else if (state == eStateRest_WalkToCover) goal.type = MG_WalkGraph; + else if (state == eStateRest_LookOpenPlace) goal.type = MG_Rest; + else goal.entity = 0; + + } else if (is_state(state, eStateSquad)) { + goal.type = MG_Rest; + goal.entity = squad->GetLeader(); + } + + squad->UpdateGoal(this, goal); +} diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker.cpp b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker.cpp new file mode 100644 index 000000000..0ee67d630 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker.cpp @@ -0,0 +1,517 @@ +#include "stdafx.h" +#include "bloodsucker.h" +#include "bloodsucker_state_manager.h" +#include "../../../actor.h" +#include "../../../ActorEffector.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../level.h" +#include "../../../material_manager.h" +#include "bloodsucker_vampire_effector.h" +#include "../../../detail_path_manager.h" +#include "../../../level_debug.h" +#include "../monster_velocity_space.h" +#include "../../../gamepersistent.h" +#include "../../../game_object_space.h" + +#include "../control_animation_base.h" +#include "../control_movement_base.h" +#include "../control_rotation_jump.h" + +#include "../../../sound_player.h" +#include "../../../../camerabase.h" +#include "../../../xr_level_controller.h" +#include "../../../ActorCondition.h" + +#include "../../../PHDestroyable.h" +#include "../../../CharacterPhysicsSupport.h" + +#ifdef DEBUG +#include +#endif + + + +CAI_Bloodsucker::CAI_Bloodsucker() +{ + StateMan = new CStateManagerBloodsucker(this); + m_alien_control.init_external (this); + + com_man().add_ability (ControlCom::eControlRunAttack); + com_man().add_ability (ControlCom::eControlRotationJump); + com_man().add_ability (ControlCom::eControlThreaten); + + invisible_vel.set (0.1f, 0.1f); + + EnemyMemory.init_external (this, 40000); +} + +CAI_Bloodsucker::~CAI_Bloodsucker() +{ + xr_delete (StateMan); +} + +void CAI_Bloodsucker::Load(LPCSTR section) +{ + inherited::Load(section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimStandIdle, eAnimStandDamaged); + anim().AddReplacedAnim(&m_bRunTurnLeft, eAnimRun, eAnimRunTurnLeft); + anim().AddReplacedAnim(&m_bRunTurnRight, eAnimRun, eAnimRunTurnRight); + + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkFwd, eAnimRunTurnLeft); + anim().accel_chain_add (eAnimWalkFwd, eAnimRunTurnRight); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandDamaged, "stand_damaged_", -1, &velocity_none, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSleep, "lie_sleep_", -1, &velocity_none, PS_LIE, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRun, "stand_run_", -1, &velocity_run, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimRunTurnLeft, "stand_run_turn_left_", -1, &velocity_run, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRunTurnRight, "stand_run_turn_right_",-1, &velocity_run, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimScared, "stand_scared_", -1, &velocity_none, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimEat, "sit_eat_", -1, &velocity_none, PS_SIT, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimDie, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimAttackRun, "stand_attack_run_", -1, &velocity_run, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimLookAround, "stand_look_around_", -1, &velocity_none, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSitToSleep, "sit_sleep_down_", -1, &velocity_none, PS_SIT, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimThreaten, "stand_threaten_", -1, &velocity_none, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimMiscAction_00, "stand_to_aggressive_", -1, &velocity_none, PS_STAND, "fx_run_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + // define transitions + // anim().AddTransition(PS_STAND, eAnimThreaten, eAnimMiscAction_00, false); + anim().AddTransition(eAnimStandSitDown, eAnimSleep, eAnimSitToSleep, false); + anim().AddTransition(PS_STAND, eAnimSleep, eAnimStandSitDown, true); + anim().AddTransition(PS_STAND, PS_SIT, eAnimStandSitDown, false); + anim().AddTransition(PS_STAND, PS_LIE, eAnimStandSitDown, false); + anim().AddTransition(PS_SIT, PS_STAND, eAnimSitStandUp, false); + anim().AddTransition(PS_LIE, PS_STAND, eAnimSitStandUp, false); + + // define links from Action to animations + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkBkwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSleep); + anim().LinkAction(ACT_REST, eAnimSitIdle); + anim().LinkAction(ACT_DRAG, eAnimWalkBkwd); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimLookAround); + + #ifdef DEBUG + anim().accel_chain_test (); + #endif + + // load other misc stuff + invisible_vel.set (pSettings->r_float(section,"Velocity_Invisible_Linear"),pSettings->r_float(section,"Velocity_Invisible_Angular")); + movement().detail().add_velocity(MonsterMovement::eVelocityParameterInvisible,CDetailPathManager::STravelParams(invisible_vel.linear, invisible_vel.angular)); + + LoadVampirePPEffector (pSettings->r_string(section,"vampire_effector")); + + m_visual_predator = pSettings->r_string(section,"Predator_Visual"); + + m_vampire_want_speed = pSettings->r_float(section,"Vampire_Want_Speed"); + m_vampire_wound = pSettings->r_float(section,"Vampire_Wound"); + + m_vampire_runaway_time = pSettings->r_float(section,"Vampire_RunAway_Time"); + m_vampire_runaway_distance = pSettings->r_float(section,"Vampire_RunAway_Distance"); + + + invisible_particle_name = pSettings->r_string(section,"Particle_Invisible"); +} + + +void CAI_Bloodsucker::reinit() +{ + inherited::reinit (); + CControlledActor::reinit (); + + if(CCustomMonster::use_simplified_visual()) return; + + Bones.Reset (); + + com_man().ta_fill_data(anim_triple_vampire, "vampire_0", "vampire_1", "vampire_2", TA_EXECUTE_LOOPED, TA_DONT_SKIP_PREPARE, ControlCom::eCapturePath | ControlCom::eCaptureMovement); + + start_threaten = false; + com_man().set_threaten_data ("stand_threaten_0", 0.63f); + + m_alien_control.reinit(); + + state_invisible = false; + + //com_man().add_rotation_jump_data("run_turn_r_0","run_turn_r_1","run_turn_r_0","run_turn_r_1", PI - 0.01f, SControlRotationJumpData::eStopAtOnce | SControlRotationJumpData::eRotateOnce); + com_man().add_rotation_jump_data("run_turn_l_0","run_turn_l_1","run_turn_r_0","run_turn_r_1", PI_DIV_2); + + // save visual + m_visual_default = cNameVisual(); + + m_vampire_want_value = 0.f; + m_threaten_time = 0; + m_predator = false; +} + +void CAI_Bloodsucker::reload(LPCSTR section) +{ + inherited::reload(section); + + sound().add(pSettings->r_string(section,"Sound_Vampire_Grasp"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority + 4, MonsterSound::eBaseChannel, eVampireGrasp, "bip01_head"); + sound().add(pSettings->r_string(section,"Sound_Vampire_Sucking"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority + 3, MonsterSound::eBaseChannel, eVampireSucking, "bip01_head"); + sound().add(pSettings->r_string(section,"Sound_Vampire_Hit"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority + 2, MonsterSound::eBaseChannel, eVampireHit, "bip01_head"); + sound().add(pSettings->r_string(section,"Sound_Vampire_StartHunt"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority + 5, MonsterSound::eBaseChannel, eVampireStartHunt, "bip01_head"); + sound().add(pSettings->r_string(section,"Sound_Invisibility_Change_State"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eNormalPriority, MonsterSound::eChannelIndependent << 1, eChangeVisibility, "bip01_head"); + sound().add(pSettings->r_string(section,"Sound_Growl"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority + 6, MonsterSound::eBaseChannel, eGrowl, "bip01_head"); + sound().add(pSettings->r_string(section,"Sound_Alien"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eCriticalPriority, u32(MonsterSound::eCaptureAllChannels), eAlien, "bip01_head"); +} + + +void CAI_Bloodsucker::LoadVampirePPEffector(LPCSTR section) +{ + pp_vampire_effector.duality.h = pSettings->r_float(section,"duality_h"); + pp_vampire_effector.duality.v = pSettings->r_float(section,"duality_v"); + pp_vampire_effector.gray = pSettings->r_float(section,"gray"); + pp_vampire_effector.blur = pSettings->r_float(section,"blur"); + pp_vampire_effector.noise.intensity = pSettings->r_float(section,"noise_intensity"); + pp_vampire_effector.noise.grain = pSettings->r_float(section,"noise_grain"); + pp_vampire_effector.noise.fps = pSettings->r_float(section,"noise_fps"); + VERIFY(!fis_zero(pp_vampire_effector.noise.fps)); + + sscanf(pSettings->r_string(section,"color_base"), "%f,%f,%f", &pp_vampire_effector.color_base.r, &pp_vampire_effector.color_base.g, &pp_vampire_effector.color_base.b); + sscanf(pSettings->r_string(section,"color_gray"), "%f,%f,%f", &pp_vampire_effector.color_gray.r, &pp_vampire_effector.color_gray.g, &pp_vampire_effector.color_gray.b); + sscanf(pSettings->r_string(section,"color_add"), "%f,%f,%f", &pp_vampire_effector.color_add.r, &pp_vampire_effector.color_add.g, &pp_vampire_effector.color_add.b); +} + + +void CAI_Bloodsucker::BoneCallback(CBoneInstance *B) +{ + CAI_Bloodsucker* this_class = static_cast (B->callback_param()); + + this_class->Bones.Update(B, Device.dwTimeGlobal); +} + + +void CAI_Bloodsucker::vfAssignBones() +{ + // Установка callback на кости + + bone_spine = &smart_cast(Visual())->LL_GetBoneInstance(smart_cast(Visual())->LL_BoneID("bip01_spine")); + bone_head = &smart_cast(Visual())->LL_GetBoneInstance(smart_cast(Visual())->LL_BoneID("bip01_head")); + if(!PPhysicsShell())//нельзя ставить колбеки, если создан физ шел - у него стоят свои колбеки!!! + { + bone_spine->set_callback(bctCustom,BoneCallback,this); + bone_head->set_callback(bctCustom,BoneCallback,this); + } + + // Bones settings + Bones.Reset(); + Bones.AddBone(bone_spine, AXIS_X); Bones.AddBone(bone_spine, AXIS_Y); + Bones.AddBone(bone_head, AXIS_X); Bones.AddBone(bone_head, AXIS_Y); +} + + +//#define MAX_BONE_ANGLE PI_DIV_4 + +void CAI_Bloodsucker::LookDirection(Fvector to_dir, float bone_turn_speed) +{ + //// получаем вектор направления к источнику звука и его мировые углы + //float yaw,pitch; + //to_dir.getHP(yaw,pitch); + + //// установить параметры вращения по yaw + //float cur_yaw = -movement().m_body.current.yaw; // текущий мировой угол монстра + //float bone_angle; // угол для боны + + //float dy = _abs(angle_normalize_signed(yaw - cur_yaw)); // дельта, на которую нужно поворачиваться + + //if (angle_difference(cur_yaw,yaw) <= MAX_BONE_ANGLE) { // bone turn only + // bone_angle = dy; + //} else { // torso & bone turn + // if (movement().IsMoveAlongPathFinished() || !movement().enabled()) movement().m_body.target.yaw = angle_normalize(-yaw); + // if (dy / 2 < MAX_BONE_ANGLE) bone_angle = dy / 2; + // else bone_angle = MAX_BONE_ANGLE; + //} + + //bone_angle /= 2; + //if (from_right(yaw,cur_yaw)) bone_angle *= -1.f; + + //Bones.SetMotion(bone_spine, AXIS_X, bone_angle, bone_turn_speed, 100); + //Bones.SetMotion(bone_head, AXIS_X, bone_angle, bone_turn_speed, 100); + + //// установить параметры вращения по pitch + //clamp(pitch, -MAX_BONE_ANGLE, MAX_BONE_ANGLE); + //pitch /= 2; + + //Bones.SetMotion(bone_spine, AXIS_Y, pitch, bone_turn_speed, 100); + //Bones.SetMotion(bone_head, AXIS_Y, pitch, bone_turn_speed, 100); +} + +void CAI_Bloodsucker::ActivateVampireEffector() +{ + Actor()->Cameras().AddCamEffector(new CVampireCameraEffector(6.0f, get_head_position(this), get_head_position(Actor()))); + Actor()->Cameras().AddPPEffector(new CVampirePPEffector(pp_vampire_effector, 6.0f)); +} + + +void CAI_Bloodsucker::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_CHECK_CORPSE) == ASP_CHECK_CORPSE) { + com_man().seq_run(anim().get_motion_id(eAnimCheckCorpse)); + } + + if ((spec_params & ASP_THREATEN) == ASP_THREATEN) { + anim().SetCurAnim(eAnimThreaten); + return; + } + + if ((spec_params & ASP_STAND_SCARED) == ASP_STAND_SCARED) { + if (Random.randI(100) < 60) + anim().SetCurAnim(eAnimLookAround); + else + anim().SetCurAnim(eAnimScared); + return; + } + +} + +BOOL CAI_Bloodsucker::net_Spawn (CSE_Abstract* DC) +{ + if (!inherited::net_Spawn(DC)) + return(FALSE); + + vfAssignBones (); + + return(TRUE); +} + +void CAI_Bloodsucker::UpdateCL() +{ + inherited::UpdateCL (); + CControlledActor::frame_update (); + + // update vampire need + m_vampire_want_value += m_vampire_want_speed * client_update_fdelta(); + clamp(m_vampire_want_value,0.f,1.f); + + if (m_threaten_time + 1670 < Device.dwTimeGlobal) + m_threaten_time = 0; +} + + +void CAI_Bloodsucker::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + + if (!g_Alive()) setVisible(TRUE); + + if (m_alien_control.active()) sound().play(eAlien); +} + +void CAI_Bloodsucker::Die(CObject* who) +{ + predator_stop (); + inherited::Die (who); +} + +void CAI_Bloodsucker::post_fsm_update() +{ + inherited::post_fsm_update(); + + //EMonsterState state = StateMan->get_state_type(); + // + // установить агрессивность + //bool aggressive = (is_state(state, eStateAttack)) || + // (is_state(state, eStatePanic)) || + // (is_state(state, eStateHitted)); + +} + +bool CAI_Bloodsucker::check_start_conditions(ControlCom::EControlType type) +{ + if (!inherited::check_start_conditions(type)) return false; + + if (type == ControlCom::eControlRunAttack) + return (!state_invisible); + + if (type == ControlCom::eControlThreaten) { + if (!start_threaten) return false; + + start_threaten = false; + + m_threaten_time = Device.dwTimeGlobal; + + if (Random.randI(100) < 70) return false; + + } + + return true; +} + +void CAI_Bloodsucker::set_alien_control(bool val) +{ + val ? m_alien_control.activate() : m_alien_control.deactivate(); +} + +void CAI_Bloodsucker::predator_start() +{ + if (m_predator) return; + cNameVisual_set (m_visual_predator); + CDamageManager::reload(*cNameSect(),"damage",pSettings); + + control().animation().restart (); + + CParticlesPlayer::StartParticles(invisible_particle_name,Fvector().set(0.0f,0.1f,0.0f),ID()); + sound().play (CAI_Bloodsucker::eChangeVisibility); + + m_predator = true; + state_invisible = false; +} + +void CAI_Bloodsucker::predator_stop() +{ + if (!m_predator) return; + + cNameVisual_set (*m_visual_default); + character_physics_support()->in_ChangeVisual(); + + CDamageManager::reload(*cNameSect(),"damage",pSettings); + + control().animation().restart (); + + CParticlesPlayer::StartParticles(invisible_particle_name,Fvector().set(0.0f,0.1f,0.0f),ID()); + sound().play (CAI_Bloodsucker::eChangeVisibility); + m_predator = false; +} + +void CAI_Bloodsucker::predator_freeze() +{ + control().animation().freeze (); +} + +void CAI_Bloodsucker::predator_unfreeze() +{ + control().animation().unfreeze(); +} + +void CAI_Bloodsucker::move_actor_cam() +{ + float turn_angle = PI_DIV_3; + if (Actor()->cam_Active()) { + Actor()->cam_Active()->Move(Random.randI(2) ? kRIGHT : kLEFT, turn_angle); //Random.randF(turn_angle)); + Actor()->cam_Active()->Move(Random.randI(2) ? kUP : kDOWN, turn_angle); //Random.randF(turn_angle)); + } +} + +void CAI_Bloodsucker::HitEntity(const CEntity *pEntity, float fDamage, float impulse, Fvector &dir) +{ + inherited::HitEntity(pEntity,fDamage,impulse,dir); + + EMonsterState state = StateMan->get_state_type(); + if (is_state(state, eStateVampire_Execute)) { + VERIFY(Actor()); + + move_actor_cam(); + + u16 bone_id = smart_cast(Actor()->Visual())->LL_BoneID("bip01_head"); + Actor()->conditions().AddWound(m_vampire_wound, ALife::eHitTypeWound, bone_id); + } +} + +void CAI_Bloodsucker::start_invisible_predator() +{ + state_invisible = true; + predator_start (); +} +void CAI_Bloodsucker::stop_invisible_predator() +{ + state_invisible = false; + predator_stop (); +} + +void CAI_Bloodsucker::manual_activate() +{ + state_invisible = true; + setVisible (FALSE); +} + +void CAI_Bloodsucker::manual_deactivate() +{ + state_invisible = false; + setVisible (TRUE); +} + +void CAI_Bloodsucker::on_activate_control(ControlCom::EControlType type) +{ + if (type == ControlCom::eControlThreaten) { + sound().play (MonsterSound::eMonsterSoundThreaten); + } +} + + + +#ifdef DEBUG +CBaseMonster::SDebugInfo CAI_Bloodsucker::show_debug_info() +{ + CBaseMonster::SDebugInfo info = inherited::show_debug_info(); + if (!info.active) return CBaseMonster::SDebugInfo(); + + string128 text; + xr_sprintf(text, "Vampire Want Value = [%f] Speed = [%f]", m_vampire_want_value, m_vampire_want_speed); + DBG().text(this).add_item(text, info.x, info.y+=info.delta_y, info.color); + DBG().text(this).add_item("---------------------------------------", info.x, info.y+=info.delta_y, info.delimiter_color); + + return CBaseMonster::SDebugInfo(); +} + +#ifdef _DEBUG +void CAI_Bloodsucker::debug_on_key(int key) +{ + switch (key){ + case DIK_MINUS: + Actor()->cam_Active()->Move(Random.randI(2) ? kRIGHT : kLEFT, PI_DIV_2); + //set_alien_control(true); + break; + case DIK_EQUALS: + Actor()->cam_Active()->Move(Random.randI(2) ? kUP : kDOWN, PI_DIV_2); + //set_alien_control(false); + break; + } +} +#endif + + +#endif + diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker.h new file mode 100644 index 000000000..50cc1becf --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker.h @@ -0,0 +1,165 @@ +#pragma once + +#include "../BaseMonster/base_monster.h" +#include "../ai_monster_bones.h" +#include "../controlled_actor.h" +#include "../anim_triple.h" +#include "../../../script_export_space.h" +#include "bloodsucker_alien.h" + +class CAI_Bloodsucker : public CBaseMonster, + public CControlledActor { + + typedef CBaseMonster inherited; + +public: + CAI_Bloodsucker (); + virtual ~CAI_Bloodsucker(); + + virtual void reinit (); + virtual void reload (LPCSTR section); + + virtual void UpdateCL (); + virtual void shedule_Update (u32 dt); + virtual void Die (CObject* who); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void Load (LPCSTR section); + + virtual void CheckSpecParams (u32 spec_params); + virtual bool ability_invisibility () {return true;} + virtual bool ability_pitch_correction() {return false;} + virtual void post_fsm_update (); + + virtual bool use_center_to_aim () const {return true;} + virtual bool check_start_conditions (ControlCom::EControlType); + virtual void on_activate_control (ControlCom::EControlType); + virtual void HitEntity (const CEntity *pEntity, float fDamage, float impulse, Fvector &dir); + + //-------------------------------------------------------------------- + // Utils + //-------------------------------------------------------------------- + void move_actor_cam (); + + //-------------------------------------------------------------------- + // Bones + //-------------------------------------------------------------------- +private: + static void __stdcall BoneCallback (CBoneInstance *B); + void vfAssignBones (); + void LookDirection (Fvector to_dir, float bone_turn_speed); + + + bonesManipulation Bones; + + CBoneInstance *bone_spine; + CBoneInstance *bone_head; + + //-------------------------------------------------------------------- + // Invisibility + //-------------------------------------------------------------------- +private: + SMotionVel invisible_vel; + LPCSTR invisible_particle_name; + +public: + void start_invisible_predator (); + void stop_invisible_predator (); + u32 threaten_time () {return m_threaten_time;} + + //-------------------------------------------------------------------- + // Vampire + //-------------------------------------------------------------------- +public: + + SAnimationTripleData anim_triple_vampire; + + SPPInfo pp_vampire_effector; + + + void ActivateVampireEffector (); + IC bool WantVampire () {return m_vampire_want_value >= 1.f;} + IC void SatisfyVampire () {m_vampire_want_value = 0.f;} + +private: + float m_vampire_want_value; + float m_vampire_want_speed; // load from ltx + float m_vampire_wound; + + void LoadVampirePPEffector (LPCSTR section); + //-------------------------------------------------------------------- + // Threaten + //-------------------------------------------------------------------- + + u32 m_threaten_time; + + //-------------------------------------------------------------------- + // Alien + //-------------------------------------------------------------------- +public: + CBloodsuckerAlien m_alien_control; + u32 m_time_lunge; + float m_vampire_runaway_time; + u32 m_vampire_runaway_distance; + + void set_alien_control (bool val); + + + //-------------------------------------------------------------------- + // Predator + //-------------------------------------------------------------------- +public: + shared_str m_visual_default; + LPCSTR m_visual_predator; + bool m_predator; + + void predator_start (); + void predator_stop (); + void predator_freeze (); + void predator_unfreeze (); + + //-------------------------------------------------------------------- + // Sounds + //-------------------------------------------------------------------- +public: + + enum EBloodsuckerSounds { + eAdditionalSounds = MonsterSound::eMonsterSoundCustom, + + eVampireGrasp = eAdditionalSounds | 0, + eVampireSucking = eAdditionalSounds | 1, + eVampireHit = eAdditionalSounds | 2, + eVampireStartHunt = eAdditionalSounds | 3, + + eGrowl = eAdditionalSounds | 5, + + eChangeVisibility = eAdditionalSounds | 6, + + eAlien = eAdditionalSounds | 7, + }; + + //-------------------------------------------------------------------- + +public: + void set_manual_control (bool value) {} + void manual_activate (); + void manual_deactivate (); + bool start_threaten; + +#ifdef DEBUG + virtual CBaseMonster::SDebugInfo show_debug_info(); + +#ifdef _DEBUG + void debug_on_key (int key); +#endif + +#endif + +public: + virtual bool can_be_seen () const { return !state_invisible; } + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CAI_Bloodsucker) +#undef script_type_list +#define script_type_list save_type_list(CAI_Bloodsucker) \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_alien.cpp b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_alien.cpp new file mode 100644 index 000000000..fc21f96de --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_alien.cpp @@ -0,0 +1,267 @@ +#include "stdafx.h" +#include "bloodsucker_alien.h" +#include "bloodsucker.h" +#include "../../../level.h" +#include "../../../actor.h" +#include "../../../ActorEffector.h" +#include "../../../inventory.h" +#include "../../../HudItem.h" +#include "../../../../CustomHUD.h" + +#define EFFECTOR_ID_GEN(type) (type( u32(u64(this) & u32(-1)) )) + + +//////////////////////////////////////////////////////////////////////////////////// +// CAlienEffectorPP +//////////////////////////////////////////////////////////////////////////////////// +class CAlienEffectorPP : public CEffectorPP { + typedef CEffectorPP inherited; + + SPPInfo state; + float factor; + float target_factor; + +public: + CAlienEffectorPP (const SPPInfo &ppi, EEffectorPPType type); + virtual ~CAlienEffectorPP (); + + void Update (float new_factor) {factor = new_factor;} + void Destroy (); + +private: + virtual BOOL Process (SPPInfo& pp); +}; + + +CAlienEffectorPP::CAlienEffectorPP(const SPPInfo &ppi, EEffectorPPType type) : +CEffectorPP(type, flt_max, false) +{ + state = ppi; + factor = 0.f; + target_factor = 1.f; +} + +CAlienEffectorPP::~CAlienEffectorPP() +{ +} + +#define PERIOD_SPEED 0.3f + +BOOL CAlienEffectorPP::Process(SPPInfo& pp) +{ + inherited::Process(pp); + + if (fsimilar(factor, target_factor)) { + target_factor = (target_factor > 0.5f) ? .3f : .6f; + } + + def_lerp (factor,target_factor, PERIOD_SPEED, Device.fTimeDelta); + pp.lerp (pp_identity,state,factor); + + return TRUE; +} + +void CAlienEffectorPP::Destroy() +{ + fLifeTime = 0.f; + CAlienEffectorPP *self = this; + xr_delete (self); +} + + + +////////////////////////////////////////////////////////////////////////// +// Alien Camera Effector +////////////////////////////////////////////////////////////////////////// + +class CAlienEffector : public CEffectorCam { + typedef CEffectorCam inherited; + + float m_time_total; + Fvector dangle_target; + Fvector dangle_current; + + CAI_Bloodsucker *monster; + + float m_current_fov; + Fmatrix m_prev_eye_matrix; + float m_inertion; + +public: + CAlienEffector (ECamEffectorType type, CAI_Bloodsucker *obj); + virtual BOOL ProcessCam (SCamEffectorInfo& info); +}; + + +#define DELTA_ANGLE_X 10 * PI / 180 +#define DELTA_ANGLE_Y 10 * PI / 180 +#define DELTA_ANGLE_Z 10 * PI / 180 +#define ANGLE_SPEED 0.2f + +#define MIN_FOV 70.f +#define MAX_FOV 175.f +#define FOV_SPEED 80.f +#define MAX_CAMERA_DIST 3.5f + + +CAlienEffector::CAlienEffector(ECamEffectorType type, CAI_Bloodsucker *obj) : + inherited(type, flt_max) +{ + dangle_target.set (angle_normalize(Random.randFs(DELTA_ANGLE_X)),angle_normalize(Random.randFs(DELTA_ANGLE_Y)),angle_normalize(Random.randFs(DELTA_ANGLE_Z))); + dangle_current.set (0.f, 0.f, 0.f); + + monster = obj; + + m_prev_eye_matrix.c = get_head_position(monster); + m_prev_eye_matrix.k = monster->Direction(); + Fvector::generate_orthonormal_basis(m_prev_eye_matrix.k,m_prev_eye_matrix.j,m_prev_eye_matrix.i); + m_inertion = 1.f; + m_current_fov = MIN_FOV; +} + +BOOL CAlienEffector::ProcessCam(SCamEffectorInfo& info) +{ + // Инициализация + Fmatrix Mdef; + Mdef.identity (); + Mdef.j.set (info.n); + Mdef.k.set (info.d); + Mdef.i.crossproduct (info.n, info.d); + Mdef.c.set (info.p); + + + // set angle + if (angle_lerp(dangle_current.x, dangle_target.x, ANGLE_SPEED, Device.fTimeDelta)) { + dangle_target.x = angle_normalize(Random.randFs(DELTA_ANGLE_X)); + } + + if (angle_lerp(dangle_current.y, dangle_target.y, ANGLE_SPEED, Device.fTimeDelta)) { + dangle_target.y = angle_normalize(Random.randFs(DELTA_ANGLE_Y)); + } + + if (angle_lerp(dangle_current.z, dangle_target.z, ANGLE_SPEED, Device.fTimeDelta)) { + dangle_target.z = angle_normalize(Random.randFs(DELTA_ANGLE_Z)); + } + + // update inertion + Fmatrix cur_matrix; + cur_matrix.k = monster->Direction(); + cur_matrix.c = get_head_position(monster); + + float rel_dist = m_prev_eye_matrix.c.distance_to(cur_matrix.c) / MAX_CAMERA_DIST; + clamp (rel_dist, 0.f, 1.f); + + def_lerp(m_inertion, 1 - rel_dist, rel_dist, Device.fTimeDelta); + + // set pos and dir with inertion + m_prev_eye_matrix.c.inertion(cur_matrix.c, m_inertion); + m_prev_eye_matrix.k.inertion(cur_matrix.k, m_inertion); + Fvector::generate_orthonormal_basis_normalized(m_prev_eye_matrix.k,m_prev_eye_matrix.j,m_prev_eye_matrix.i); + + // apply position and direction + Mdef = m_prev_eye_matrix; + + //set fov + float rel_speed = monster->m_fCurSpeed / 15.f; + clamp (rel_speed,0.f,1.f); + + float m_target_fov = MIN_FOV + (MAX_FOV-MIN_FOV) * rel_speed; + def_lerp(m_current_fov, m_target_fov, FOV_SPEED, Device.fTimeDelta); + + info.fFov = m_current_fov; + ////////////////////////////////////////////////////////////////////////// + + // Установить углы смещения + Fmatrix R; + R.setHPB (dangle_current.x,dangle_current.y,dangle_current.z); + + Fmatrix mR; + mR.mul (Mdef,R); + + info.d.set (mR.k); + info.n.set (mR.j); + info.p.set (mR.c); + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// +/////////////////////////////////////////////////////////////////////////////////////////// + + +CBloodsuckerAlien::CBloodsuckerAlien() +{ + m_object = 0; +} + +CBloodsuckerAlien::~CBloodsuckerAlien() +{ +} + +void CBloodsuckerAlien::init_external(CAI_Bloodsucker *obj) +{ + m_object = obj; +} + +void CBloodsuckerAlien::reinit() +{ + m_active = false; + m_crosshair_show = false; +} + +void CBloodsuckerAlien::activate() +{ + if (m_active) return; + + VERIFY (Actor()); + m_object->CControlledActor::install (Actor()); + m_object->CControlledActor::dont_need_turn (); + + if (!m_object->EnemyMan.get_enemy()) m_object->EnemyMan.add_enemy(Actor()); + +//. Actor()->inventory().setSlotsBlocked (true); + Actor()->SetWeaponHideState(INV_STATE_BLOCK_ALL, true); + + // hide crosshair + m_crosshair_show = !!psHUD_Flags.is(HUD_CROSSHAIR_RT); + if (m_crosshair_show) psHUD_Flags.set(HUD_CROSSHAIR_RT,FALSE); + + // Start effector + m_effector_pp = new CAlienEffectorPP(m_object->pp_vampire_effector, EFFECTOR_ID_GEN(EEffectorPPType)); + Actor()->Cameras().AddPPEffector (m_effector_pp); + + m_effector = new CAlienEffector(EFFECTOR_ID_GEN(ECamEffectorType),m_object); + Actor()->Cameras().AddCamEffector (m_effector); + + // make invisible + m_object->state_invisible = true; + m_object->setVisible (false); + + m_active = true; +} + +void CBloodsuckerAlien::deactivate() +{ + if (!m_active) return; + + m_object->CControlledActor::release (); + + Actor()->SetWeaponHideState(INV_STATE_BLOCK_ALL, false); + if (m_crosshair_show) psHUD_Flags.set(HUD_CROSSHAIR_RT,TRUE); + + // Stop camera effector + Actor()->Cameras().RemoveCamEffector (EFFECTOR_ID_GEN(ECamEffectorType)); + m_effector = 0; + + // Stop postprocess effector + Actor()->Cameras().RemovePPEffector (EFFECTOR_ID_GEN(EEffectorPPType)); + m_effector_pp->Destroy (); + m_effector_pp = 0; + + m_active = false; + + // make visible + m_object->state_invisible = false; + m_object->setVisible (true); +} diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_alien.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_alien.h new file mode 100644 index 000000000..5281ddfbe --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_alien.h @@ -0,0 +1,30 @@ +#pragma once + +class CAI_Bloodsucker; +class CAlienEffector; +class CAlienEffectorPP; + +class CBloodsuckerAlien { + + CAI_Bloodsucker *m_object; + + bool m_active; + + CAlienEffector *m_effector; + CAlienEffectorPP *m_effector_pp; + + bool m_crosshair_show; + +public: + CBloodsuckerAlien (); + ~CBloodsuckerAlien (); + + void init_external (CAI_Bloodsucker *obj); + void reinit (); + + void activate (); + void deactivate (); + + bool active () {return m_active;} + +}; \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state.h new file mode 100644 index 000000000..eb1e63a46 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state.h @@ -0,0 +1,26 @@ +#pragma once +#include "../states/monster_state_attack.h" + +template +class CBloodsuckerStateAttack : public CStateMonsterAttack<_Object> { + typedef CStateMonsterAttack<_Object> inherited_attack; + + u32 m_time_stop_invis; + Fvector m_dir_point; + +public: + CBloodsuckerStateAttack (_Object *obj); + virtual ~CBloodsuckerStateAttack (); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual void setup_substates (); +private: + void update_invisibility (); + bool check_hiding (); +}; + +#include "bloodsucker_attack_state_inline.h" diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_hide.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_hide.h new file mode 100644 index 000000000..e050e9c2f --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_hide.h @@ -0,0 +1,29 @@ +#pragma once +#include "../state.h" + +template +class CBloodsuckerStateAttackHide : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + u32 m_target_node; + +public: + CBloodsuckerStateAttackHide (_Object *obj); + + virtual void reinit (); + + virtual void initialize (); + virtual void reselect_state (); + virtual void finalize (); + virtual void critical_finalize (); + virtual bool check_completion (); + + virtual void setup_substates (); + virtual void check_force_state (); + +private: + void select_camp_point (); +}; + +#include "bloodsucker_attack_state_hide_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_hide_inline.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_hide_inline.h new file mode 100644 index 000000000..1a56b5bf4 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_hide_inline.h @@ -0,0 +1,149 @@ +#pragma once + +#pragma once + +#include "../states/state_move_to_point.h" +#include "bloodsucker_predator_lite.h" + +#include "../../../cover_point.h" +#include "../monster_cover_manager.h" +#include "../monster_home.h" + +#include "../../../actor.h" +#include "../../../actor_memory.h" +#include "../../../visual_memory_manager.h" + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CBloodsuckerStateAttackHideAbstract CBloodsuckerStateAttackHide<_Object> + +TEMPLATE_SPECIALIZATION +CBloodsuckerStateAttackHideAbstract::CBloodsuckerStateAttackHide(_Object *obj) : inherited(obj) +{ + add_state (eStateAttack_HideInCover, new CStateMonsterMoveToPointEx<_Object>(obj)); + add_state (eStateAttack_CampInCover, new CStateBloodsuckerPredatorLite<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackHideAbstract::reinit() +{ + inherited::reinit (); +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackHideAbstract::initialize() +{ + inherited::initialize (); + + m_target_node = u32(-1); + + object->start_invisible_predator(); +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackHideAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) { + select_state(eStateAttack_HideInCover); + return; + } + + select_state(eStateAttack_CampInCover); +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackHideAbstract::finalize() +{ + inherited::finalize (); + + if (m_target_node != u32(-1)) + monster_squad().get_squad(object)->unlock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackHideAbstract::critical_finalize() +{ + inherited::critical_finalize (); + + if (m_target_node != u32(-1)) + monster_squad().get_squad(object)->unlock_cover(m_target_node); +} + + +TEMPLATE_SPECIALIZATION +bool CBloodsuckerStateAttackHideAbstract::check_completion() +{ + if (current_substate == eStateAttack_CampInCover) + return (get_state_current()->check_completion()); + + return false; +} + + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackHideAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateAttack_HideInCover) { + select_camp_point(); + + SStateDataMoveToPointEx data; + + data.vertex = m_target_node; + data.point = ai().level_graph().vertex_position(data.vertex); + data.action.action = ACT_RUN; + data.action.time_out = 0; // do not use time out + data.completion_dist = 0.f; // get exactly to the point + data.time_to_rebuild = 0; // do not rebuild + data.accelerated = true; + data.braking = true; + data.accel_type = eAT_Aggressive; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + return; + } + +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackHideAbstract::check_force_state() +{ +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackHideAbstract::select_camp_point() +{ + if (m_target_node != u32(-1)) + monster_squad().get_squad(object)->unlock_cover(m_target_node); + + m_target_node = u32(-1); + if (object->Home->has_home()) { + m_target_node = object->Home->get_place_in_cover(); + if (m_target_node == u32(-1)) { + m_target_node = object->Home->get_place(); + } + } + + if (m_target_node == u32(-1)) { + const CCoverPoint *point = object->CoverMan->find_cover(object->Position(),10.f,30.f); + if (point) { + m_target_node = point->level_vertex_id (); + } + } + + if (m_target_node == u32(-1)) + m_target_node = object->ai_location().level_vertex_id(); + + + monster_squad().get_squad(object)->lock_cover(m_target_node); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CBloodsuckerStateAttackHideAbstract + diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_inline.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_inline.h new file mode 100644 index 000000000..919a940a5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_attack_state_inline.h @@ -0,0 +1,213 @@ +#pragma once + +//#include "bloodsucker_attack_state_hide.h" +#include "../states/state_move_to_point.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CBloodsuckerStateAttackAbstract CBloodsuckerStateAttack<_Object> + +TEMPLATE_SPECIALIZATION +CBloodsuckerStateAttackAbstract::CBloodsuckerStateAttack(_Object *obj) : inherited_attack(obj) +{ + //add_state(eStateAttack_Hide, new CBloodsuckerStateAttackHide<_Object>(obj)); + add_state (eStateAttack_Hide, new CStateMonsterMoveToPointEx<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CBloodsuckerStateAttackAbstract::~CBloodsuckerStateAttack() +{ +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackAbstract::initialize() +{ + inherited::initialize (); + m_time_stop_invis = 0; +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackAbstract::finalize() +{ + inherited::finalize(); + object->stop_invisible_predator(); +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackAbstract::critical_finalize() +{ + inherited::critical_finalize(); + object->stop_invisible_predator(); +} + +#define INVIS_ACTIVATE_DELAY 3000 +#define INVIS_DIST_TO_ENEMY 5.f + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackAbstract::execute() +{ + bool selected = false; + + if (check_home_point()) { + select_state (eStateAttack_MoveToHomePoint); + selected = true; + } else if (check_hiding()) { + select_state (eStateAttack_Hide); + selected = true; + } else if (check_steal_state()) { + select_state (eStateAttack_Steal); + selected = true; + } else if (check_camp_state()) { + select_state (eStateAttackCamp); + selected = true; + } else if (check_find_enemy_state()) { + select_state (eStateAttack_FindEnemy); + selected = true; + } else if (check_run_attack_state()) { + select_state (eStateAttack_RunAttack); + selected = true; + } + + if (!selected) { + // определить тип атаки + bool b_melee = false; + + if (prev_substate == eStateAttack_Melee) { + if (!get_state_current()->check_completion()) { + b_melee = true; + } + } else if (get_state(eStateAttack_Melee)->check_start_conditions()) { + b_melee = true; + } + + if (!b_melee && (prev_substate == eStateAttack_Melee)) { + select_state (eStateAttack_Hide); + } else + // установить целевое состояние + if (b_melee) { + // check if enemy is behind me for a long time + // [TODO] make specific state and replace run_away state (to avoid ratation jumps) + //if (check_behinder()) + // select_state(eStateAttack_RunAway); + //else + select_state(eStateAttack_Melee); + } + else select_state(eStateAttack_Run); + } + + // clear behinder var if not melee state selected + if (current_substate != eStateAttack_Melee) m_time_start_check_behinder = 0; + update_invisibility (); + + get_state_current()->execute (); + prev_substate = current_substate; + + // Notify squad + CMonsterSquad *squad = monster_squad().get_squad(object); + if (squad) { + SMemberGoal goal; + + goal.type = MG_AttackEnemy; + goal.entity = const_cast(object->EnemyMan.get_enemy()); + + squad->UpdateGoal (object, goal); + } + ////////////////////////////////////////////////////////////////////////// +} + + + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackAbstract::update_invisibility() +{ + if (object->threaten_time() > 0) + { + object->stop_invisible_predator (); + return; + } + + if (object->state_invisible) { + // check conditions to stop invis + if (current_substate == eStateAttack_Melee) { + object->stop_invisible_predator (); + m_time_stop_invis = time(); + } + } else { + // check conditions to start invis + if (current_substate == eStateAttack_Hide) { + object->start_invisible_predator(); + } else + if ((current_substate == eStateAttack_Run) && (object->EnemyMan.get_enemy()->Position().distance_to(object->Position()) > INVIS_DIST_TO_ENEMY)) { + if (m_time_stop_invis + INVIS_ACTIVATE_DELAY < time()) + object->start_invisible_predator(); + } + } +} + +TEMPLATE_SPECIALIZATION +bool CBloodsuckerStateAttackAbstract::check_hiding() +{ + if (current_substate == eStateAttack_Hide) + if (!get_state(eStateAttack_Melee)->check_start_conditions()) + if (!get_state_current()->check_completion()) { + //object->path().set_use_dest_orient (true); + //object->path().set_dest_direction (Fvector().sub(object->EnemyMan.get_enemy()->Position(),m_dir_point)); + return true; + } + + return false; + + + //if (current_substate == eStateAttack_Melee) { + // if (prev_substate != eStateAttack_Melee) { + // object->stop_invisible_predator (); + // m_time_stop_invis = time(); + // } + // + // if (get_state_current()->check_completion()) ret_value = true; + //} else + //if (current_substate == eStateAttack_Run) { + // if (object->EnemyMan.get_enemy()->Position().distance_to(object->Position()) > INVIS_DIST_TO_ENEMY) { + // if (!object->state_invisible && (m_time_stop_invis + INVIS_ACTIVATE_DELAY < time())) + // object->start_invisible_predator(); + // } + //} + // + //return ret_value; +} + +TEMPLATE_SPECIALIZATION +void CBloodsuckerStateAttackAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateAttack_Hide) { + + SStateDataMoveToPointEx data; + + Fvector target_dir = Random.randI(2) ? object->XFORM().i : Fvector().set(object->XFORM().i).invert(); + m_dir_point = Fvector().mad(object->Position(), target_dir, 2.5f); + + data.vertex = 0; + data.point = m_dir_point; + data.action.action = ACT_RUN; + data.action.time_out = 1500; // do not use time out + data.completion_dist = 1.f; // get exactly to the point + data.time_to_rebuild = object->get_attack_rebuild_time(); + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Aggressive; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + + return; + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CBloodsuckerStateAttackAbstract + diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator.h new file mode 100644 index 000000000..2351e3154 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator.h @@ -0,0 +1,31 @@ +#pragma once +#include "../state.h" + +template +class CStateBloodsuckerPredator : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + u32 m_target_node; + u32 m_time_start_camp; + +public: + CStateBloodsuckerPredator (_Object *obj); + + virtual void reinit (); + + virtual void initialize (); + virtual void reselect_state (); + virtual void finalize (); + virtual void critical_finalize (); + virtual bool check_start_conditions (); + virtual bool check_completion (); + + virtual void setup_substates (); + virtual void check_force_state (); + +private: + void select_camp_point (); +}; + +#include "bloodsucker_predator_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_inline.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_inline.h new file mode 100644 index 000000000..74cbab445 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_inline.h @@ -0,0 +1,220 @@ +#pragma once + +#include "../states/state_move_to_point.h" +#include "../states/state_look_point.h" +#include "../states/state_custom_action.h" +#include "../../../cover_point.h" +#include "../monster_cover_manager.h" +#include "../monster_home.h" + +#include "../../../actor.h" +#include "../../../actor_memory.h" +#include "../../../visual_memory_manager.h" + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBloodsuckerPredatorAbstract CStateBloodsuckerPredator<_Object> + +TEMPLATE_SPECIALIZATION +CStateBloodsuckerPredatorAbstract::CStateBloodsuckerPredator(_Object *obj) : inherited(obj) +{ + add_state (eStatePredator_MoveToCover, new CStateMonsterMoveToPointEx<_Object>(obj)); + add_state (eStatePredator_LookOpenPlace, new CStateMonsterLookToPoint<_Object>(obj)); + add_state (eStatePredator_Camp, new CStateMonsterCustomAction<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorAbstract::reinit() +{ + inherited::reinit (); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorAbstract::initialize() +{ + inherited::initialize (); + + object->predator_start (); + + select_camp_point (); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) { + select_state(eStatePredator_MoveToCover); + return; + } + + if (prev_substate == eStatePredator_MoveToCover) { + select_state(eStatePredator_LookOpenPlace); + return; + } + + if (prev_substate == eStatePredator_LookOpenPlace) { + select_state(eStatePredator_Camp); + return; + } + + select_state(eStatePredator_Camp); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorAbstract::finalize() +{ + inherited::finalize (); + + object->predator_stop (); + object->predator_unfreeze (); + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover(m_target_node); + +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorAbstract::critical_finalize() +{ + inherited::critical_finalize (); + + object->predator_stop (); + object->predator_unfreeze (); + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +bool CStateBloodsuckerPredatorAbstract::check_start_conditions() +{ + if (Actor()->memory().visual().visible_now(object)) return false; + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateBloodsuckerPredatorAbstract::check_completion() +{ + if (object->HitMemory.get_last_hit_time() > time_state_started) return true; + if (object->EnemyMan.get_enemy() && object->EnemyMan.see_enemy_now() && (object->Position().distance_to(object->EnemyMan.get_enemy()->Position()) < 4.f)) return true; + + return false; +} + + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStatePredator_Camp) { + object->predator_freeze (); + m_time_start_camp = time(); + + } else { + object->predator_unfreeze(); + } + + if (current_substate == eStatePredator_MoveToCover) { + SStateDataMoveToPointEx data; + + data.vertex = m_target_node; + data.point = ai().level_graph().vertex_position(data.vertex); + data.action.action = ACT_RUN; + data.action.time_out = 0; // do not use time out + data.completion_dist = 0.f; // get exactly to the point + data.time_to_rebuild = 0; // do not rebuild + data.accelerated = true; + data.braking = true; + data.accel_type = eAT_Aggressive; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + return; + } + + if (current_substate == eStatePredator_LookOpenPlace) { + + SStateDataLookToPoint data; + + Fvector dir; + object->CoverMan->less_cover_direction(dir); + + data.point.mad (object->Position(),dir,10.f); + data.action.action = ACT_STAND_IDLE; + data.action.time_out = 2000; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.face_delay = 0; + + state->fill_data_with(&data, sizeof(SStateDataLookToPoint)); + return; + } + + if (current_substate == eStatePredator_Camp) { + + SStateDataAction data; + + data.action = ACT_STAND_IDLE; + data.time_out = 0; // do not use time out + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } +} + +#define TIME_TO_RESELECT_CAMP 15000 + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorAbstract::check_force_state() +{ + if ((current_substate == eStatePredator_Camp) && (m_time_start_camp + TIME_TO_RESELECT_CAMP < time())) { + if (current_substate != u32(-1)) + get_state_current()->critical_finalize(); + + prev_substate = u32(-1); + current_substate = u32(-1); + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover (m_target_node); + + select_camp_point (); + } +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorAbstract::select_camp_point() +{ + m_target_node = u32(-1); + if (object->Home->has_home()) { + m_target_node = object->Home->get_place_in_cover(); + if (m_target_node == u32(-1)) { + m_target_node = object->Home->get_place(); + } + } + + if (m_target_node == u32(-1)) { + const CCoverPoint *point = object->CoverMan->find_cover(object->Position(),10.f,30.f); + if (point) { + m_target_node = point->level_vertex_id (); + } + } + + if (m_target_node == u32(-1)) + m_target_node = object->ai_location().level_vertex_id(); + + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->lock_cover(m_target_node); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBloodsuckerPredatorAbstract + diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_lite.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_lite.h new file mode 100644 index 000000000..4cb075692 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_lite.h @@ -0,0 +1,32 @@ +#pragma once +#include "../state.h" + +template +class CStateBloodsuckerPredatorLite : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + u32 m_target_node; + bool m_freezed; + +public: + CStateBloodsuckerPredatorLite (_Object *obj); + + virtual void reinit (); + + virtual void initialize (); + virtual void reselect_state (); + virtual void finalize (); + virtual void critical_finalize (); + virtual bool check_completion (); + + virtual void setup_substates (); + virtual void check_force_state (); + +private: + void select_camp_point (); + bool enemy_see_me (); + +}; + +#include "bloodsucker_predator_lite_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_lite_inline.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_lite_inline.h new file mode 100644 index 000000000..1ee19d773 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_predator_lite_inline.h @@ -0,0 +1,236 @@ +#pragma once + +#include "../states/state_move_to_point.h" +#include "../states/state_look_point.h" +#include "../states/state_custom_action.h" +#include "../../../cover_point.h" +#include "../monster_cover_manager.h" +#include "../monster_home.h" + +#include "../../../actor.h" +#include "../../../actor_memory.h" +#include "../../../visual_memory_manager.h" + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBloodsuckerPredatorLiteAbstract CStateBloodsuckerPredatorLite<_Object> + +TEMPLATE_SPECIALIZATION +CStateBloodsuckerPredatorLiteAbstract::CStateBloodsuckerPredatorLite(_Object *obj) : inherited(obj) +{ + add_state (eStatePredator_Camp, new CStateMonsterCustomAction<_Object>(obj)); + add_state (eStatePredator_MoveToCover, new CStateMonsterMoveToPointEx<_Object>(obj)); + add_state (eStatePredator_LookOpenPlace, new CStateMonsterLookToPoint<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorLiteAbstract::reinit() +{ + inherited::reinit (); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorLiteAbstract::initialize() +{ + inherited::initialize (); + + object->predator_start (); + + m_target_node = u32(-1); + m_freezed = false; +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorLiteAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) { + if (enemy_see_me()) select_state(eStatePredator_MoveToCover); + else select_state(eStatePredator_LookOpenPlace); + return; + } + + if (prev_substate == eStatePredator_MoveToCover) { + if (enemy_see_me()) { + select_state(eStatePredator_MoveToCover); + object->set_berserk(); + } + else select_state(eStatePredator_LookOpenPlace); + return; + } + + if (prev_substate == eStatePredator_LookOpenPlace) { + select_state(eStatePredator_Camp); + return; + } + + if (prev_substate == eStatePredator_Camp) { + select_state(eStatePredator_MoveToCover); + return; + } + + select_state(eStatePredator_MoveToCover); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorLiteAbstract::finalize() +{ + inherited::finalize (); + object->predator_stop (); + if (m_freezed) object->predator_unfreeze (); + + if (m_target_node != u32(-1)) + monster_squad().get_squad(object)->unlock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorLiteAbstract::critical_finalize() +{ + inherited::critical_finalize (); + object->predator_stop (); + if (m_freezed) object->predator_unfreeze (); + + if (m_target_node != u32(-1)) + monster_squad().get_squad(object)->unlock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +bool CStateBloodsuckerPredatorLiteAbstract::check_completion() +{ + if (object->EnemyMan.see_enemy_now() && (object->Position().distance_to(object->EnemyMan.get_enemy()->Position()) < 4.f)) { + object->set_berserk(); + return true; + } + if (object->conditions().health() > 0.9f) return true; + + return false; +} + + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorLiteAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStatePredator_Camp) { + object->predator_freeze (); + m_freezed = true; + } else { + object->predator_unfreeze (); + m_freezed = false; + } + + if (current_substate == eStatePredator_MoveToCover) { + select_camp_point (); + + SStateDataMoveToPointEx data; + data.vertex = m_target_node; + data.point = ai().level_graph().vertex_position(data.vertex); + data.action.action = ACT_RUN; + data.action.time_out = 0; // do not use time out + data.completion_dist = 0.f; // get exactly to the point + data.time_to_rebuild = 0; // do not rebuild + data.accelerated = true; + data.braking = true; + data.accel_type = eAT_Aggressive; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + return; + } + + if (current_substate == eStatePredator_LookOpenPlace) { + + SStateDataLookToPoint data; + + Fvector dir; + object->CoverMan->less_cover_direction(dir); + + data.point.mad (object->Position(),dir,10.f); + data.action.action = ACT_STAND_IDLE; + data.action.time_out = 2000; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.face_delay = 0; + + state->fill_data_with(&data, sizeof(SStateDataLookToPoint)); + return; + } + + if (current_substate == eStatePredator_Camp) { + + SStateDataAction data; + + data.action = ACT_STAND_IDLE; + data.time_out = 0; // do not use time out + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } +} + +#define TIME_TO_RESELECT_CAMP 15000 + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorLiteAbstract::check_force_state() +{ + if (prev_substate == eStatePredator_Camp) { + if (object->HitMemory.get_last_hit_time() > time_state_started) { + + if (object->EnemyMan.get_enemy() && + (object->EnemyMan.get_enemy()->Position().distance_to(object->Position()) < 10.f)) { + object->set_berserk (); + } else + current_substate = u32(-1); + + } + } +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerPredatorLiteAbstract::select_camp_point() +{ + if (m_target_node != u32(-1)) + monster_squad().get_squad(object)->unlock_cover(m_target_node); + + m_target_node = u32(-1); + if (object->Home->has_home()) { + m_target_node = object->Home->get_place_in_cover(); + if (m_target_node == u32(-1)) { + m_target_node = object->Home->get_place(); + } + } + + if (m_target_node == u32(-1)) { + const CCoverPoint *point = object->CoverMan->find_cover(object->Position(),20.f,30.f); + if (point) { + m_target_node = point->level_vertex_id (); + } + } + + if (m_target_node == u32(-1)) + m_target_node = object->ai_location().level_vertex_id(); + + monster_squad().get_squad(object)->lock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +bool CStateBloodsuckerPredatorLiteAbstract::enemy_see_me() +{ + //if (object->EnemyMan.get_enemy() == Actor()) + // return (Actor()->memory().visual().visible_now(object)); + + // if I see enemy then probably enemy see me :-) + return object->EnemyMan.enemy_see_me_now(); +} + + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBloodsuckerPredatorLiteAbstract + diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_script.cpp b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_script.cpp new file mode 100644 index 000000000..d8e9f4cf9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "bloodsucker.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CAI_Bloodsucker::script_register(lua_State *L) +{ + module(L) + [ + class_("CAI_Bloodsucker") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_state_manager.cpp b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_state_manager.cpp new file mode 100644 index 000000000..278ad6471 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_state_manager.cpp @@ -0,0 +1,90 @@ +#include "stdafx.h" +#include "bloodsucker_state_manager.h" +#include "bloodsucker.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hitted.h" + +#include "bloodsucker_vampire.h" +#include "bloodsucker_predator.h" + +#include "bloodsucker_attack_state.h" + +CStateManagerBloodsucker::CStateManagerBloodsucker(CAI_Bloodsucker *monster) : inherited(monster) +{ + add_state(eStateRest, new CStateMonsterRest(monster)); + add_state(eStatePanic, new CStateMonsterPanic(monster)); + add_state(eStateAttack, new CBloodsuckerStateAttack(monster)); + add_state(eStateEat, new CStateMonsterEat(monster)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(monster)); + add_state(eStateHitted, new CStateMonsterHitted(monster)); + + add_state(eStateCustom_Vampire, new CStateBloodsuckerVampire(monster)); + //add_state(eStateCustom_Vampire, new CStateBloodsuckerVampireExecute(monster)); +} + +void CStateManagerBloodsucker::execute() +{ + u32 state_id = u32(-1); + + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + + +if (enemy) { + if (check_state(eStateCustom_Vampire)) { + state_id = eStateCustom_Vampire; + } else { + + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + } + + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (object->hear_dangerous_sound || object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + + /////////////////////////////////////////////////////////////////////////////// + // Additional + /////////////////////////////////////////////////////////////////////////////// + + // check if start interesting sound state + if ((prev_substate != eStateHearInterestingSound) && (state_id == eStateHearInterestingSound)){ + object->predator_start(); + } else + // check if stop interesting sound state + if ((prev_substate == eStateHearInterestingSound) && (state_id != eStateHearInterestingSound)) { + object->predator_stop(); + } + /////////////////////////////////////////////////////////////////////////////// + + + select_state(state_id); + + if ((current_substate == eStateAttack) && (current_substate != prev_substate)) { + object->predator_stop(); + object->start_threaten = true; + } + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; + +} diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_state_manager.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_state_manager.h new file mode 100644 index 000000000..6edd8f829 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_state_manager.h @@ -0,0 +1,12 @@ +#pragma once +#include "../monster_state_manager.h" + +class CAI_Bloodsucker; + +class CStateManagerBloodsucker : public CMonsterStateManager { + typedef CMonsterStateManager inherited; + +public: + CStateManagerBloodsucker (CAI_Bloodsucker *monster); + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire.h new file mode 100644 index 000000000..5ab9d7d9b --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire.h @@ -0,0 +1,33 @@ +#pragma once +#include "../state.h" + +template +class CStateBloodsuckerVampire : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + u32 m_time_last_vampire; + + +public: + CStateBloodsuckerVampire (_Object *obj); + + virtual void reinit (); + + virtual void initialize (); + virtual void reselect_state (); + virtual void finalize (); + virtual void critical_finalize (); + virtual bool check_start_conditions (); + virtual bool check_completion (); + + virtual void setup_substates (); + virtual void check_force_state (); + + const CEntityAlive *m_enemy; + const CAI_Bloodsucker *monster; +// float b_max_reach_distance; +}; + + +#include "bloodsucker_vampire_inline.h" diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_approach.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_approach.h new file mode 100644 index 000000000..b2127b4e2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_approach.h @@ -0,0 +1,16 @@ +#pragma once +#include "../state.h" + +template +class CStateBloodsuckerVampireApproach : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateBloodsuckerVampireApproach (_Object *obj); + virtual ~CStateBloodsuckerVampireApproach (); + + virtual void initialize (); + virtual void execute (); +}; + +#include "bloodsucker_vampire_approach_inline.h" diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_approach_inline.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_approach_inline.h new file mode 100644 index 000000000..8153f9ba8 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_approach_inline.h @@ -0,0 +1,39 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBloodsuckerVampireApproachAbstract CStateBloodsuckerVampireApproach<_Object> + +TEMPLATE_SPECIALIZATION +CStateBloodsuckerVampireApproachAbstract::CStateBloodsuckerVampireApproach(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateBloodsuckerVampireApproachAbstract::~CStateBloodsuckerVampireApproach() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireApproachAbstract::initialize() +{ + inherited::initialize(); + object->path().prepare_builder (); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireApproachAbstract::execute() +{ + // установка параметров функциональных блоков + object->set_action (ACT_RUN); + object->anim().accel_activate (eAT_Aggressive); + object->anim().accel_set_braking (false); + object->path().set_target_point (object->EnemyMan.get_enemy()->Position(), object->EnemyMan.get_enemy()->ai_location().level_vertex_id()); + object->path().set_rebuild_time (object->get_attack_rebuild_time()); + object->path().set_use_covers (false); + object->path().set_distance_to_end (0.1f); + object->set_state_sound (MonsterSound::eMonsterSoundAggressive); +} + diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_effector.cpp b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_effector.cpp new file mode 100644 index 000000000..7277e9787 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_effector.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" +#include "bloodsucker_vampire_effector.h" + +////////////////////////////////////////////////////////////////////////// +// Vampire Postprocess Effector +////////////////////////////////////////////////////////////////////////// + +CVampirePPEffector::CVampirePPEffector(const SPPInfo &ppi, float life_time) : + inherited(EEffectorPPType(eCEHit), life_time) +{ + state = ppi; + m_total = life_time; +} + +#define TIME_ATTACK 0.2f +#define PERIODS 2 +#define RAD_TO_PERC(rad) ((rad - PI_DIV_2) / (PERIODS * PI_MUL_2)) +#define PERC_TO_RAD(perc) (perc * (PERIODS * PI_MUL_2) + PI_DIV_2) + +BOOL CVampirePPEffector::Process(SPPInfo& pp) +{ + inherited::Process(pp); + + // amount of time passed in percents + float time_past_perc = (m_total - fLifeTime) / m_total; + + float factor; + if (time_past_perc < TIME_ATTACK) { + factor = 0.75f * time_past_perc / TIME_ATTACK; + } else if (time_past_perc > (1 - TIME_ATTACK)) { + factor = 0.75f * (1-time_past_perc) / TIME_ATTACK; + } else { + float time_past_sine_perc = (time_past_perc - TIME_ATTACK) * (1 / ( 1 - TIME_ATTACK + TIME_ATTACK)); + factor = 0.5f + 0.25f * _sin(PERC_TO_RAD(time_past_sine_perc)); + } + + clamp(factor,0.01f,1.0f); + pp.lerp (pp_identity, state, factor); + + return TRUE; +} + +////////////////////////////////////////////////////////////////////////// +// Vampire Camera Effector +////////////////////////////////////////////////////////////////////////// +#define DELTA_ANGLE_X 10 * PI / 180 +#define DELTA_ANGLE_Y DELTA_ANGLE_X +#define DELTA_ANGLE_Z DELTA_ANGLE_X +#define ANGLE_SPEED 0.2f +#define BEST_DISTANCE 0.3f +CVampireCameraEffector::CVampireCameraEffector(float time, const Fvector &src, const Fvector &tgt) : + inherited(eCEVampire, time) +{ + fLifeTime = time; + m_time_total = time; + + m_dist = src.distance_to(tgt); + + if (m_dist < BEST_DISTANCE) { + m_direction.sub (src,tgt); + m_dist = BEST_DISTANCE - m_dist; + } else { + m_direction.sub (tgt,src); + m_dist = m_dist - BEST_DISTANCE; + } + + m_direction.normalize (); + + dangle_target.set (Random.randFs(DELTA_ANGLE_X),Random.randFs(DELTA_ANGLE_Y),Random.randFs(DELTA_ANGLE_Z)); + dangle_current.set (0.f, 0.f, 0.f); +} + +BOOL CVampireCameraEffector::ProcessCam(SCamEffectorInfo& info) +{ + fLifeTime -= Device.fTimeDelta; + if(fLifeTime<0) + return FALSE; + + // процент оставшегося времени + float time_left_perc = fLifeTime / m_time_total; + + // Инициализация + Fmatrix Mdef; + Mdef.identity (); + Mdef.j.set (info.n); + Mdef.k.set (info.d); + Mdef.i.crossproduct (info.n, info.d); + Mdef.c.set (info.p); + + + ////////////////////////////////////////////////////////////////////////// + // using formula: y = k - 2*k*abs(x-1/2) k - max distance + //float cur_dist = m_dist * (1 - 2*_abs((1-time_left_perc) - 0.5f)); + //float time_passed = 1-time_left_perc; + //float cur_dist = m_dist * (_sqrt(0.5f*0.5f - (time_passed - 0.5f)*(time_passed - 0.5f)) ); + + // m_dist is a distance to target + // make camera being close enough to be held in his hands, but not too close + float cur_dist = .32 * m_dist - _abs(m_dist * (time_left_perc - 0.5f)); + + Mdef.c.mad(m_direction, cur_dist); + + // check the time to return + if (time_left_perc < 0.2f) { + + dangle_target.x = 0.f; + dangle_target.y = 0.f; + dangle_target.z = 0.f; + + angle_lerp(dangle_current.x, dangle_target.x, _abs(dangle_current.x / fLifeTime + 0.001f), Device.fTimeDelta); + angle_lerp(dangle_current.y, dangle_target.y, _abs(dangle_current.y / fLifeTime + 0.001f), Device.fTimeDelta); + angle_lerp(dangle_current.z, dangle_target.z, _abs(dangle_current.z / fLifeTime + 0.001f), Device.fTimeDelta); + + } else { + + if (angle_lerp(dangle_current.x, dangle_target.x, ANGLE_SPEED, Device.fTimeDelta)) { + dangle_target.x = Random.randFs(DELTA_ANGLE_X); + } + + if (angle_lerp(dangle_current.y, dangle_target.y, ANGLE_SPEED, Device.fTimeDelta)) { + dangle_target.y = Random.randFs(DELTA_ANGLE_Y); + } + + if (angle_lerp(dangle_current.z, dangle_target.z, ANGLE_SPEED, Device.fTimeDelta)) { + dangle_target.z = Random.randFs(DELTA_ANGLE_Z); + } + } + + ////////////////////////////////////////////////////////////////////////// + + // Установить углы смещения + Fmatrix R; + R.setHPB (dangle_current.x,dangle_current.y,dangle_current.z); + + Fmatrix mR; + mR.mul (Mdef,R); + + info.d.set (mR.k); + info.n.set (mR.j); + info.p.set (mR.c); + + return TRUE; +} + diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_effector.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_effector.h new file mode 100644 index 000000000..830741c29 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_effector.h @@ -0,0 +1,41 @@ +#pragma once + +#include "../../../../effectorPP.h" +#include "../../../CameraEffector.h" +#include "../../../../cameramanager.h" + +////////////////////////////////////////////////////////////////////////// +// Vampire Postprocess Effector +////////////////////////////////////////////////////////////////////////// +class CVampirePPEffector : public CEffectorPP { + typedef CEffectorPP inherited; + + SPPInfo state; //current state + float m_total; // total PP time + +public: + CVampirePPEffector (const SPPInfo &ppi, float life_time); + virtual BOOL Process (SPPInfo& pp); +}; + +////////////////////////////////////////////////////////////////////////// +// Vampire Camera Effector +////////////////////////////////////////////////////////////////////////// +class CVampireCameraEffector : public CEffectorCam { + typedef CEffectorCam inherited; + + float m_time_total; + Fvector dangle_target; + Fvector dangle_current; + + float m_dist; + Fvector m_direction; + +public: + CVampireCameraEffector (float time, const Fvector &src, const Fvector &tgt); + virtual BOOL ProcessCam (SCamEffectorInfo& info); + +}; + + + diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_execute.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_execute.h new file mode 100644 index 000000000..f48787b4e --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_execute.h @@ -0,0 +1,40 @@ +#pragma once +#include "../state.h" + +template +class CStateBloodsuckerVampireExecute : public CState<_Object> { + typedef CState<_Object> inherited; + + enum { + eActionPrepare, + eActionContinue, + eActionFire, + eActionWaitTripleEnd, + eActionCompleted + } m_action; + + u32 time_vampire_started; + + bool m_effector_activated; + +public: + CStateBloodsuckerVampireExecute (_Object *obj) : inherited(obj) {} + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + virtual bool check_start_conditions (); + virtual bool check_completion (); + +private: + void execute_vampire_prepare (); + void execute_vampire_continue (); + void execute_vampire_hit (); + + void look_head (); + void show_hud (); + void cleanup (); +}; + +#include "bloodsucker_vampire_execute_inline.h" diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_execute_inline.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_execute_inline.h new file mode 100644 index 000000000..aacb064c9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_execute_inline.h @@ -0,0 +1,210 @@ +#pragma once + +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../actor.h" +#include "../../../../CameraBase.h" +#include "../../../../CustomHUD.h" +#include "../../../../../xrCore/_vector3d_ext.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBloodsuckerVampireExecuteAbstract CStateBloodsuckerVampireExecute<_Object> + +#define VAMPIRE_TIME_HOLD 4000 +#define VAMPIRE_HIT_IMPULSE 40.f +#define VAMPIRE_MIN_DIST 0.5f +#define VAMPIRE_MAX_DIST 1.f +extern bool g_bDisableAllInput; + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireExecuteAbstract::initialize() +{ + controlling_value = 1; + + inherited::initialize (); + + object->CControlledActor::install (); + + look_head (); + + m_action = eActionPrepare; + time_vampire_started = 0; + + psHUD_Flags.set(HUD_DRAW, FALSE); + g_bDisableAllInput = true; + +//#pragma todo("SkyLoader: SetWeaponHideState() doesn't work properly in this scheme, need to fix it") +//#if 0 + Actor()->SetWeaponHideState(INV_STATE_BLOCK_ALL, true); +//#endif + + object->stop_invisible_predator (); + + m_effector_activated = false; +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireExecuteAbstract::execute() +{ + if (!object->CControlledActor::is_turning() && !m_effector_activated) { + object->ActivateVampireEffector (); + m_effector_activated = true; + } + + look_head (); + + switch (m_action) { + case eActionPrepare: + execute_vampire_prepare(); + m_action = eActionContinue; + break; + + case eActionContinue: + execute_vampire_continue(); + break; + + case eActionFire: + execute_vampire_hit(); + m_action = eActionWaitTripleEnd; + break; + + case eActionWaitTripleEnd: + if (!object->com_man().ta_is_active()) { + m_action = eActionCompleted; + } + + case eActionCompleted: + controlling_value = 0; + break; + } + object->set_action (ACT_STAND_IDLE); + object->dir().face_target (object->EnemyMan.get_enemy()); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireExecuteAbstract::show_hud() +{ + psHUD_Flags.set(HUD_DRAW, TRUE); + Actor()->SetWeaponHideState(INV_STATE_BLOCK_ALL, false); + g_bDisableAllInput = false; +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireExecuteAbstract::cleanup() +{ + if ( object->com_man().ta_is_active() ) + object->com_man().ta_deactivate(); + + if (object->CControlledActor::is_controlling()) + object->CControlledActor::release (); + + show_hud(); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireExecuteAbstract::finalize() +{ + inherited::finalize(); + cleanup(); + object->start_invisible_predator (); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireExecuteAbstract::critical_finalize() +{ + inherited::critical_finalize(); + cleanup(); + object->start_invisible_predator (); +} + +TEMPLATE_SPECIALIZATION +bool CStateBloodsuckerVampireExecuteAbstract::check_start_conditions() +{ + const CEntityAlive *enemy = object->EnemyMan.get_enemy(); + + if (enemy->CLS_ID != CLSID_OBJECT_ACTOR) return false; + + // проверить дистанцию + float dist = object->MeleeChecker.distance_to_enemy(enemy); + if ((dist > VAMPIRE_MAX_DIST) || (dist < VAMPIRE_MIN_DIST)) return false; + + if (object->CControlledActor::is_controlling()) return false; + if (current_substate == eStateAttack_RunAttack) return false; + if (object->threaten_time() > 0) return false; + + const CActor *m_actor = smart_cast(enemy); + VERIFY(m_actor); + if (m_actor->input_external_handler_installed()) return false; + + if (controlling_value == 1) return false; + + // проверить направление на врага + if (!object->control().direction().is_face_target(enemy, PI_DIV_6)) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateBloodsuckerVampireExecuteAbstract::check_completion() +{ + return (m_action == eActionCompleted); +} + +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireExecuteAbstract::execute_vampire_prepare() +{ + object->com_man().ta_activate (object->anim_triple_vampire); + time_vampire_started = Device.dwTimeGlobal; + + object->sound().play(CAI_Bloodsucker::eVampireGrasp); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireExecuteAbstract::execute_vampire_continue() +{ + if (object->Position().distance_to(Actor()->Position()) > 2.f) { + + if (object->com_man().ta_is_active()) + object->com_man().ta_deactivate(); + m_action = eActionCompleted; + return; + } + + object->sound().play(CAI_Bloodsucker::eVampireSucking); + + // проверить на грави удар + if (time_vampire_started + VAMPIRE_TIME_HOLD < Device.dwTimeGlobal) { + m_action = eActionFire; + } +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireExecuteAbstract::execute_vampire_hit() +{ + object->com_man().ta_pointbreak (); + object->sound().play (CAI_Bloodsucker::eVampireHit); + object->SatisfyVampire (); +} + +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireExecuteAbstract::look_head() +{ + IKinematics *pK = smart_cast(object->Visual()); + Fmatrix bone_transform; + bone_transform = pK->LL_GetTransform(pK->LL_BoneID("bip01_head")); + + Fmatrix global_transform; + global_transform.mul_43(object->XFORM(),bone_transform); + + object->CControlledActor::look_point (global_transform.c); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBloodsuckerVampireExecuteAbstract + diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_hide.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_hide.h new file mode 100644 index 000000000..41e5be0bf --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_hide.h @@ -0,0 +1,17 @@ +#pragma once +#include "../state.h" + +template +class CStateBloodsuckerVampireHide : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + +public: + CStateBloodsuckerVampireHide (_Object *obj); + + virtual void reselect_state (); + virtual void setup_substates (); + virtual bool check_completion (); +}; + +#include "bloodsucker_vampire_hide_inline.h" diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_hide_inline.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_hide_inline.h new file mode 100644 index 000000000..97cb55de0 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_hide_inline.h @@ -0,0 +1,56 @@ +#pragma once +#include "../states/state_hide_from_point.h" +#include "bloodsucker_predator.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBloodsuckerVampireHideAbstract CStateBloodsuckerVampireHide<_Object> + +TEMPLATE_SPECIALIZATION +CStateBloodsuckerVampireHideAbstract::CStateBloodsuckerVampireHide(_Object *obj) : inherited(obj) +{ + add_state (eStateVampire_RunAway, new CStateMonsterHideFromPoint<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireHideAbstract::reselect_state() +{ + select_state(eStateVampire_RunAway); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireHideAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateVampire_RunAway) { + SStateHideFromPoint data; + data.point = object->EnemyMan.get_enemy_position(); + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Aggressive; + data.distance = object->m_vampire_runaway_distance; + data.action.action = ACT_RUN; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwAttackSndDelay; + data.action.time_out = object->m_vampire_runaway_time; + + state->fill_data_with(&data, sizeof(SStateHideFromPoint)); + + return; + } +} + +TEMPLATE_SPECIALIZATION +bool CStateBloodsuckerVampireHideAbstract::check_completion() +{ + if ((current_substate == eStateVampire_RunAway) && + get_state_current()->check_completion()) return true; + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBloodsuckerVampireHideAbstract diff --git a/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_inline.h b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_inline.h new file mode 100644 index 000000000..977f95a67 --- /dev/null +++ b/src/xrGameLA/ai/monsters/bloodsucker/bloodsucker_vampire_inline.h @@ -0,0 +1,169 @@ +#pragma once + +#include "bloodsucker_vampire_execute.h" +#include "../states/state_hide_from_point.h" +#include "bloodsucker_vampire_approach.h" +#include "bloodsucker_vampire_hide.h" +#include "../../../clsid_game.h" +#include "../../../entity_alive.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBloodsuckerVampireAbstract CStateBloodsuckerVampire<_Object> + +#define RUN_AWAY_DISTANCE 5.f +#define MAX_DISTANCE_TO_ENEMY 2.f + +TEMPLATE_SPECIALIZATION + + +CStateBloodsuckerVampireAbstract::CStateBloodsuckerVampire(_Object *obj) : inherited(obj) +{ + add_state (eStateVampire_ApproachEnemy, new CStateBloodsuckerVampireApproach<_Object>(obj)); + add_state (eStateVampire_Execute, new CStateBloodsuckerVampireExecute<_Object>(obj)); + add_state (eStateVampire_RunAway, new CStateMonsterHideFromPoint<_Object>(obj)); + add_state (eStateVampire_Hide, new CStateBloodsuckerVampireHide<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireAbstract::reinit() +{ + inherited::reinit (); + + m_time_last_vampire = 0; +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireAbstract::initialize() +{ + inherited::initialize (); + object->start_invisible_predator (); + + m_enemy = object->EnemyMan.get_enemy (); + + object->sound().play (CAI_Bloodsucker::eVampireStartHunt); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireAbstract::reselect_state() +{ + u32 state_id = u32(-1); + + // check if we executed + if (prev_substate == eStateVampire_ApproachEnemy) { + if (get_state(eStateVampire_Execute)->check_start_conditions()) + state_id = eStateVampire_Execute; + } + + // check if reach time in vampire state is out - then hide + if (prev_substate == eStateVampire_Execute) + state_id = eStateVampire_Hide; + + // else just + if (state_id == u32(-1)) + state_id = eStateVampire_ApproachEnemy; + + select_state(state_id); +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireAbstract::check_force_state() +{ + // check if we can start execute + if (prev_substate == eStateVampire_ApproachEnemy) { + if (get_state(eStateVampire_Execute)->check_start_conditions()) + current_substate = u32(-1); + } +} + + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireAbstract::finalize() +{ + inherited::finalize(); + + object->stop_invisible_predator (); + m_time_last_vampire = Device.dwTimeGlobal; +} + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireAbstract::critical_finalize() +{ + inherited::critical_finalize (); + + object->stop_invisible_predator (); + m_time_last_vampire = Device.dwTimeGlobal; +} + +TEMPLATE_SPECIALIZATION +bool CStateBloodsuckerVampireAbstract::check_start_conditions() +{ + if (!object->WantVampire()) return false; + if (object->m_bDamaged) return false; + + const CEntityAlive *m_enemy = object->EnemyMan.get_enemy(); + if (m_enemy->CLS_ID != CLSID_OBJECT_ACTOR) return false; + if (!object->EnemyMan.see_enemy_now()) return false; + if (object->CControlledActor::is_installed() && object->CControlledActor::is_controlling()) return false; + if (current_substate == eStateAttack_RunAttack) return false; + if (object->threaten_time() > 0) return false; + + const CActor *m_actor = smart_cast(m_enemy); + VERIFY(m_actor); + if (m_actor->input_external_handler_installed()) return false; + + float dist_to_enemy = object->EnemyMan.get_enemy_position().distance_to(object->Position()); + if (dist_to_enemy > MAX_DISTANCE_TO_ENEMY) return false; + + if (controlling_value == 1) return false; + + return true; + } + +TEMPLATE_SPECIALIZATION +bool CStateBloodsuckerVampireAbstract::check_completion() +{ + // если убежал + if ((current_substate == eStateVampire_Hide) && + get_state_current()->check_completion()) return true; + + // если враг изменился + if (m_enemy != object->EnemyMan.get_enemy()) return true; + + // если актера уже контролит другой кровосос + if ((current_substate != eStateVampire_Execute) && + object->CControlledActor::is_controlling()) return true; + + return false; +} + + +TEMPLATE_SPECIALIZATION +void CStateBloodsuckerVampireAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateVampire_RunAway) { + + SStateHideFromPoint data; + data.point = object->EnemyMan.get_enemy_position(); + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Aggressive; + data.distance = RUN_AWAY_DISTANCE; + data.action.action = ACT_RUN; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwAttackSndDelay; + data.action.time_out = 3000; + + state->fill_data_with(&data, sizeof(SStateHideFromPoint)); + + return; + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBloodsuckerVampireAbstract + diff --git a/src/xrGameLA/ai/monsters/boar/boar.cpp b/src/xrGameLA/ai/monsters/boar/boar.cpp new file mode 100644 index 000000000..a509f7ecf --- /dev/null +++ b/src/xrGameLA/ai/monsters/boar/boar.cpp @@ -0,0 +1,184 @@ +#include "stdafx.h" +#include "boar.h" +#include "boar_state_manager.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../monster_velocity_space.h" +#include "../../../game_object_space.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" + + +CAI_Boar::CAI_Boar() +{ + StateMan = new CStateManagerBoar(this); + + CControlled::init_external(this); + com_man().add_ability(ControlCom::eControlRotationJump); + com_man().add_ability(ControlCom::eControlRunAttack); +} + +CAI_Boar::~CAI_Boar() +{ + xr_delete(StateMan); +} + + + +void CAI_Boar::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + anim().AddReplacedAnim(&m_bRunTurnLeft, eAnimRun, eAnimRunTurnLeft); + anim().AddReplacedAnim(&m_bRunTurnRight, eAnimRun, eAnimRunTurnRight); + + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkFwd, eAnimRunTurnLeft); + anim().accel_chain_add (eAnimWalkFwd, eAnimRunTurnRight); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + + anim().AddAnim(eAnimLieIdle, "lie_sleep_", -1, &velocity_none, PS_LIE ); + anim().AddAnim(eAnimSleep, "lie_sleep_", -1, &velocity_none, PS_LIE ); + + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND); + anim().AddAnim(eAnimRun, "stand_run_fwd_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND); + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandLieDown, "stand_lie_down_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimLieStandUp, "lie_stand_up_", -1, &velocity_none, PS_LIE ); + anim().AddAnim(eAnimLieToSleep, "lie_to_sleep_", -1, &velocity_none, PS_LIE ); + anim().AddAnim(eAnimDragCorpse, "stand_drag_", -1, &velocity_drag, PS_STAND); + anim().AddAnim(eAnimLookAround, "stand_idle_", 2, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimDie, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimJumpLeft, "stand_jump_left_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimJumpRight, "stand_jump_right_", -1, &velocity_turn, PS_STAND); +// anim().AddAnim(eAnimAttackRun, "stand_run_attack_", -1, &velocity_run, PS_STAND); + + anim().AddAnim(eAnimRunTurnLeft, "stand_run_look_left_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunTurnRight, "stand_run_look_right_", -1, &velocity_run, PS_STAND); + + + // define transitions + anim().AddTransition(eAnimStandLieDown, eAnimSleep, eAnimLieToSleep, false); + anim().AddTransition(PS_STAND, eAnimSleep, eAnimStandLieDown, true); + anim().AddTransition(PS_STAND, PS_LIE, eAnimStandLieDown, false); + anim().AddTransition(PS_LIE, PS_STAND, eAnimLieStandUp, false, SKIP_IF_AGGRESSIVE); + + + // define links from Action to animations + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimDragCorpse); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSleep); + anim().LinkAction(ACT_REST, eAnimLieIdle); + anim().LinkAction(ACT_DRAG, eAnimDragCorpse); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimLookAround); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +void CAI_Boar::reinit() +{ + inherited::reinit(); + if(CCustomMonster::use_simplified_visual()) return; + com_man().add_rotation_jump_data("stand_jump_left_0",0,"stand_jump_right_0",0, PI - PI_DIV_6, SControlRotationJumpData::eStopAtOnce | SControlRotationJumpData::eRotateOnce); +} + + +void CAI_Boar::BoneCallback(CBoneInstance *B) +{ + CAI_Boar *P = static_cast(B->callback_param()); + + if (!P->look_at_enemy) return; + + Fmatrix M; + M.setHPB (0.0f,-P->_cur_delta,0.0f); + B->mTransform.mulB_43(M); +} + +BOOL CAI_Boar::net_Spawn (CSE_Abstract* DC) +{ + if (!inherited::net_Spawn(DC)) + return(FALSE); + + if(!PPhysicsShell())//нельзя ставить колбеки, если создан физ шел - у него стоят свои колбеки!!! + { + CBoneInstance& BI = smart_cast(Visual())->LL_GetBoneInstance(smart_cast(Visual())->LL_BoneID("bip01_head")); + BI.set_callback(bctCustom,BoneCallback,this); + } + + _cur_delta = _target_delta = 0.f; + _velocity = PI; + look_at_enemy = false; + return TRUE; +} + +void CAI_Boar::CheckSpecParams(u32 spec_params) +{ + //if ((spec_params & ASP_ROTATION_JUMP) == ASP_ROTATION_JUMP) { + // float yaw, pitch; + // Fvector().sub(EnemyMan.get_enemy()->Position(), Position()).getHP(yaw,pitch); + // yaw *= -1; + // yaw = angle_normalize(yaw); + + // EMotionAnim anim = eAnimJumpLeft; + // if (from_right(yaw,movement().m_body.current.yaw)) { + // anim = eAnimJumpRight; + // yaw = angle_normalize(yaw + PI / 20); + // } else yaw = angle_normalize(yaw - PI / 20); + + // anim().Seq_Add(anim); + // anim().Seq_Switch(); + + // movement().m_body.target.yaw = yaw; + + // // calculate angular speed + // float new_angular_velocity; + // float delta_yaw = angle_difference(yaw,movement().m_body.current.yaw); + // float time = anim().GetCurAnimTime(); + // new_angular_velocity = 2.5f * delta_yaw / time; + + // anim().ForceAngularSpeed(new_angular_velocity); + + // return; + //} + + //if ((spec_params & ASP_ATTACK_RUN) == ASP_ATTACK_RUN) { + // anim().SetCurAnim(eAnimAttackRun); + //} +} + +void CAI_Boar::UpdateCL() +{ + inherited::UpdateCL(); + angle_lerp(_cur_delta, _target_delta, _velocity, client_update_fdelta()); +} diff --git a/src/xrGameLA/ai/monsters/boar/boar.h b/src/xrGameLA/ai/monsters/boar/boar.h new file mode 100644 index 000000000..3bce2a91b --- /dev/null +++ b/src/xrGameLA/ai/monsters/boar/boar.h @@ -0,0 +1,41 @@ +#pragma once + +#include "../BaseMonster/base_monster.h" +#include "../controlled_entity.h" +#include "../../../script_export_space.h" + +class CAI_Boar : public CBaseMonster, + public CControlledEntity { + + typedef CBaseMonster inherited; + typedef CControlledEntity CControlled; + +public: + CAI_Boar (); + virtual ~CAI_Boar (); + + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void reinit (); + + virtual void UpdateCL (); + + virtual bool CanExecRotationJump () {return true;} + virtual void CheckSpecParams (u32 spec_params); + + // look at enemy + static void __stdcall BoneCallback (CBoneInstance *B); + + float _velocity; + float _cur_delta, _target_delta; + bool look_at_enemy; + + virtual bool ability_can_drag () {return true;} + + DECLARE_SCRIPT_REGISTER_FUNCTION + +}; + +add_to_type_list(CAI_Boar) +#undef script_type_list +#define script_type_list save_type_list(CAI_Boar) \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/boar/boar_script.cpp b/src/xrGameLA/ai/monsters/boar/boar_script.cpp new file mode 100644 index 000000000..e9d53dc36 --- /dev/null +++ b/src/xrGameLA/ai/monsters/boar/boar_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "boar.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CAI_Boar::script_register(lua_State *L) +{ + module(L) + [ + class_("CAI_Boar") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/boar/boar_state_manager.cpp b/src/xrGameLA/ai/monsters/boar/boar_state_manager.cpp new file mode 100644 index 000000000..9ca1d16f2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/boar/boar_state_manager.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "boar.h" +#include "boar_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../states/monster_state_controlled.h" +#include "../states/monster_state_help_sound.h" + +CStateManagerBoar::CStateManagerBoar(CAI_Boar *monster) : inherited(monster) +{ + add_state(eStateRest, new CStateMonsterRest(monster)); + add_state(eStatePanic, new CStateMonsterPanic(monster)); + add_state(eStateAttack, new CStateMonsterAttack(monster)); + add_state(eStateEat, new CStateMonsterEat(monster)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(monster)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(monster)); + add_state(eStateHitted, new CStateMonsterHitted(monster)); + add_state(eStateControlled, new CStateMonsterControlled(monster)); + add_state(eStateHearHelpSound, new CStateMonsterHearHelpSound(monster)); +} + +void CStateManagerBoar::execute() +{ + u32 state_id = u32(-1); + + if (!object->is_under_control()) { + + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (check_state(eStateHearHelpSound)) { + state_id = eStateHearHelpSound; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + } else state_id = eStateControlled; + + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} diff --git a/src/xrGameLA/ai/monsters/boar/boar_state_manager.h b/src/xrGameLA/ai/monsters/boar/boar_state_manager.h new file mode 100644 index 000000000..29089b057 --- /dev/null +++ b/src/xrGameLA/ai/monsters/boar/boar_state_manager.h @@ -0,0 +1,14 @@ +#pragma once +#include "../monster_state_manager.h" + +class CAI_Boar; + +class CStateManagerBoar : public CMonsterStateManager { + typedef CMonsterStateManager inherited; + +public: + + CStateManagerBoar (CAI_Boar *monster); + + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/burer/burer.cpp b/src/xrGameLA/ai/monsters/burer/burer.cpp new file mode 100644 index 000000000..5d46d3aba --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer.cpp @@ -0,0 +1,393 @@ +#include "stdafx.h" +#include "burer.h" +#include "../../../PhysicsShell.h" +#include "../../../characterphysicssupport.h" +#include "../../../actor.h" +#include "burer_state_manager.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../sound_player.h" +#include "../../../level.h" +#include "../../../ai_monster_space.h" +#include "../../../level_debug.h" +#include "../monster_velocity_space.h" +#include "../../../GamePersistent.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" +#include "burer_fast_gravi.h" + +bool CBurer::can_scan = true; + +CBurer::CBurer() +{ + StateMan = new CStateManagerBurer(this); + TScanner::init_external(this); + + m_fast_gravi = new CBurerFastGravi(); + control().add (m_fast_gravi, ControlCom::eComCustom1); + +} + +CBurer::~CBurer() +{ + xr_delete(StateMan); + xr_delete(m_fast_gravi); +} + + +void CBurer::reinit() +{ + inherited::reinit (); + TScanner::reinit (); + + DeactivateShield(); + + time_last_scan = 0; +} + +void CBurer::net_Destroy() +{ + inherited::net_Destroy(); + TScanner::on_destroy(); +} + +void CBurer::reload(LPCSTR section) +{ + inherited::reload (section); + + // add specific sounds + sound().add (pSettings->r_string(section,"sound_gravi_attack"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority + 2, u32(MonsterSound::eBaseChannel), eMonsterSoundGraviAttack, "bip01_head"); + sound().add (pSettings->r_string(section,"sound_tele_attack"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority + 3, u32(MonsterSound::eBaseChannel), eMonsterSoundTeleAttack, "bip01_head"); + + // add triple animations + com_man().ta_fill_data(anim_triple_gravi, "stand_gravi_0", "stand_gravi_1", "stand_gravi_2", TA_EXECUTE_ONCE, TA_DONT_SKIP_PREPARE, ControlCom::eCapturePath | ControlCom::eCaptureMovement); + com_man().ta_fill_data(anim_triple_tele, "stand_tele_0", "stand_tele_1", "stand_tele_2", TA_EXECUTE_ONCE, TA_DONT_SKIP_PREPARE, ControlCom::eCapturePath | ControlCom::eCaptureMovement); + com_man().ta_fill_data(anim_triple_shield, "idle_to_shield_0", "shield_0", "shield_0_idle_0", TA_EXECUTE_LOOPED, TA_DONT_SKIP_PREPARE, ControlCom::eCapturePath | ControlCom::eCaptureMovement); +} + +void CBurer::Load(LPCSTR section) +{ + inherited::Load (section); + TScanner::load (section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimStandIdle, eAnimStandDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + + particle_gravi_wave = pSettings->r_string(section,"Particle_Gravi_Wave"); + particle_gravi_prepare = pSettings->r_string(section,"Particle_Gravi_Prepare"); + particle_tele_object = pSettings->r_string(section,"Particle_Tele_Object"); + + ::Sound->create(sound_gravi_wave, pSettings->r_string(section,"sound_gravi_wave"),st_Effect,SOUND_TYPE_WORLD); + ::Sound->create(sound_tele_hold, pSettings->r_string(section,"sound_tele_hold"), st_Effect,SOUND_TYPE_WORLD); + ::Sound->create(sound_tele_throw, pSettings->r_string(section,"sound_tele_throw"),st_Effect,SOUND_TYPE_WORLD); + + m_gravi_speed = pSettings->r_u32(section,"Gravi_Speed"); + m_gravi_step = pSettings->r_u32(section,"Gravi_Step"); + m_gravi_time_to_hold = pSettings->r_u32(section,"Gravi_Time_To_Hold"); + m_gravi_radius = pSettings->r_float(section,"Gravi_Radius"); + m_gravi_impulse_to_objects = pSettings->r_float(section,"Gravi_Impulse_To_Objects"); + m_gravi_impulse_to_enemy = pSettings->r_float(section,"Gravi_Impulse_To_Enemy"); + m_gravi_hit_power = pSettings->r_float(section,"Gravi_Hit_Power"); + + m_tele_max_handled_objects = pSettings->r_u32(section,"Tele_Max_Handled_Objects"); + m_tele_time_to_hold = pSettings->r_u32(section,"Tele_Time_To_Hold"); + m_tele_object_min_mass = pSettings->r_float(section,"Tele_Object_Min_Mass"); + m_tele_object_max_mass = pSettings->r_float(section,"Tele_Object_Max_Mass"); + m_tele_find_radius = pSettings->r_float(section,"Tele_Find_Radius"); + + m_shield_cooldown = READ_IF_EXISTS(pSettings,r_u32,section,"shield_cooldown",4000); + m_shield_time = READ_IF_EXISTS(pSettings,r_u32,section,"shield_time",3000); + m_shield_keep_particle = READ_IF_EXISTS(pSettings,r_string,section,"shield_keep_particle",0); + m_shield_keep_particle_period = READ_IF_EXISTS(pSettings,r_u32,section,"shield_keep_particle_period",1000); + particle_fire_shield = pSettings->r_string(section,"Particle_Shield"); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); +// SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandDamaged, "stand_idle_dmg_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRun, "stand_run_fwd_", -1, &velocity_run, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimDie, "stand_die_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimScared, "stand_scared_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimEat, "sit_eat_", -1, &velocity_none, PS_SIT, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimCheckCorpse, "sit_check_corpse_", -1, &velocity_none, PS_SIT, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddTransition(PS_SIT, PS_STAND, eAnimSitStandUp, false); + anim().AddTransition(PS_STAND, PS_SIT, eAnimStandSitDown, false); + + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSitIdle); + anim().LinkAction(ACT_REST, eAnimSitIdle); + anim().LinkAction(ACT_DRAG, eAnimWalkFwd); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimScared); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +void CBurer::shedule_Update(u32 dt) +{ + inherited::shedule_Update (dt); + + TTelekinesis::schedule_update (); + TScanner::schedule_update (); + + (!EnemyMan.get_enemy()) ? TScanner::enable() : TScanner::disable(); +} + +void CBurer::CheckSpecParams(u32 spec_params) +{ +} + +void CBurer::UpdateGraviObject() +{ + if (!m_gravi_object.active) return; + + if (!m_gravi_object.enemy || (m_gravi_object.enemy && m_gravi_object.enemy->getDestroy())) { + m_gravi_object.deactivate(); + return; + } + + if (m_gravi_object.from_pos.distance_to(m_gravi_object.cur_pos) > (m_gravi_object.from_pos.distance_to(m_gravi_object.target_pos))) { + m_gravi_object.deactivate(); + return; + } + + float dt = float(Device.dwTimeGlobal - m_gravi_object.time_last_update); + float dist = dt * float(m_gravi_speed)/1000.f; + + if (dist < m_gravi_step) return; + + Fvector new_pos; + Fvector dir; + dir.sub(m_gravi_object.target_pos,m_gravi_object.cur_pos); + dir.normalize(); + + new_pos.mad(m_gravi_object.cur_pos,dir,dist); + + // Trace to enemy + Fvector enemy_center; + m_gravi_object.enemy->Center(enemy_center); + dir.sub(enemy_center, new_pos); + dir.normalize(); + + float trace_dist = float(m_gravi_step); + + collide::rq_result l_rq; + if (Level().ObjectSpace.RayPick(new_pos, dir, trace_dist, collide::rqtBoth, l_rq, NULL)) { + const CObject *enemy = smart_cast(m_gravi_object.enemy); + if ((l_rq.O == enemy) && (l_rq.range < trace_dist)) { + + // check for visibility + bool b_enemy_visible = false; + xr_vector visible_objects; + feel_vision_get(visible_objects); + + // find object + for (u32 i = 0; iUpdateParent(pos, zero_vel); + ps->Play(false); + + // hit objects + m_nearest.clear_not_free (); + Level().ObjectSpace.GetNearest (m_nearest,m_gravi_object.cur_pos, m_gravi_radius, NULL); + //xr_vector &m_nearest = Level().ObjectSpace.q_nearest; + + for (u32 i=0;i(m_nearest[i]); + if (!obj || !obj->m_pPhysicsShell) continue; + + Fvector dir; + dir.sub(obj->Position(), m_gravi_object.cur_pos); + dir.normalize(); + obj->m_pPhysicsShell->applyImpulse(dir,m_gravi_impulse_to_objects * obj->m_pPhysicsShell->getMass()); + } + + // играть звук + Fvector snd_pos = m_gravi_object.cur_pos; + snd_pos.y += 0.5f; + if (sound_gravi_wave._feedback()) { + sound_gravi_wave.set_position (snd_pos); + } else ::Sound->play_at_pos (sound_gravi_wave,0,snd_pos); +} + +void CBurer::UpdateCL() +{ + inherited::UpdateCL(); + TScanner::frame_update(Device.dwTimeDelta); + + UpdateGraviObject(); + + + //if (m_fast_gravi->check_start_conditions()) + // control().activate(ControlCom::eComCustom1); + +} + +void CBurer::StartGraviPrepare() +{ + const CEntityAlive *enemy = EnemyMan.get_enemy(); + if (!enemy) return; + + CActor *pA = const_cast(smart_cast(enemy)); + if (!pA) return; + + pA->CParticlesPlayer::StartParticles(particle_gravi_prepare,Fvector().set(0.0f,0.1f,0.0f),pA->ID()); +} +void CBurer::StopGraviPrepare() +{ + CActor *pA = smart_cast(Level().CurrentEntity()); + if (!pA) return; + + pA->CParticlesPlayer::StopParticles(particle_gravi_prepare, BI_NONE, true); +} + +void CBurer::StartTeleObjectParticle(CGameObject *pO) +{ + CParticlesPlayer* PP = smart_cast(pO); + if(!PP) return; + PP->StartParticles(particle_tele_object,Fvector().set(0.0f,0.1f,0.0f),pO->ID()); +} +void CBurer::StopTeleObjectParticle(CGameObject *pO) +{ + CParticlesPlayer* PP = smart_cast(pO); + if(!PP) return; + PP->StopParticles(particle_tele_object, BI_NONE, true); +} + +//void CBurer::Hit(float P,Fvector &dir,CObject*who,s16 element,Fvector p_in_object_space,float impulse, ALife::EHitType hit_type) +void CBurer::Hit (SHit* pHDS) +{ + if (m_shield_active && (pHDS->hit_type == ALife::eHitTypeFireWound) && (Device.dwFrame != last_hit_frame)) { + + // вычислить позицию и направленность партикла + Fmatrix pos; + //CParticlesPlayer::MakeXFORM(this,element,Fvector().set(0.f,0.f,1.f),p_in_object_space,pos); + CParticlesPlayer::MakeXFORM(this,pHDS->bone(),pHDS->dir,pHDS->p_in_bone_space,pos); + + // установить particles + CParticlesObject* ps = CParticlesObject::Create(particle_fire_shield,TRUE); + + ps->UpdateParent(pos,Fvector().set(0.f,0.f,0.f)); + GamePersistent().ps_needtoplay.push_back(ps); + + } else if (!m_shield_active) +// inherited::Hit(P,dir,who,element,p_in_object_space,impulse,hit_type); + inherited::Hit(pHDS); + + last_hit_frame = Device.dwFrame; +} + + +void CBurer::Die(CObject* who) +{ + inherited::Die(who); + TScanner::on_destroy(); + + if (com_man().ta_is_active()) com_man().ta_deactivate(); + CTelekinesis::Deactivate(); +} + +void CBurer::on_scanning() +{ + time_last_scan = Device.dwTimeGlobal; +} + +void CBurer::on_scan_success() +{ + CActor *pA = smart_cast(Level().CurrentEntity()); + if (!pA) return; + + EnemyMan.add_enemy(pA); +} + +void CBurer::net_Relcase(CObject *O) +{ + inherited::net_Relcase (O); + + TTelekinesis::remove_links (O); +} + + +#ifdef DEBUG +CBaseMonster::SDebugInfo CBurer::show_debug_info() +{ + CBaseMonster::SDebugInfo info = inherited::show_debug_info(); + if (!info.active) return CBaseMonster::SDebugInfo(); + + string128 text; + xr_sprintf(text, "Scan Value = [%f]", TScanner::get_scan_value()); + DBG().text(this).add_item(text, info.x, info.y+=info.delta_y, info.color); + DBG().text(this).add_item("---------------------------------------", info.x, info.y+=info.delta_y, info.delimiter_color); + + return CBaseMonster::SDebugInfo(); +} +#endif diff --git a/src/xrGameLA/ai/monsters/burer/burer.h b/src/xrGameLA/ai/monsters/burer/burer.h new file mode 100644 index 000000000..e33202742 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer.h @@ -0,0 +1,159 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../telekinesis.h" +#include "../anim_triple.h" +#include "../scanning_ability.h" +#include "../../../script_export_space.h" + +class CCharacterPhysicsSupport; +class CBurerFastGravi; + +class CBurer : public CBaseMonster, + public CTelekinesis, + public CScanningAbility { + + typedef CBaseMonster inherited; + +private: + xr_vector m_nearest; + +public: + typedef CScanningAbility TScanner; + + + static bool can_scan; + + u32 last_hit_frame; + u32 time_last_scan; + + + typedef CTelekinesis TTelekinesis; + + struct GraviObject { + bool active; + Fvector cur_pos; + Fvector target_pos; + Fvector from_pos; + + u32 time_last_update; + + const CEntityAlive *enemy; + + GraviObject() { + active = false; + enemy = 0; + } + + + void activate(const CEntityAlive *e, const Fvector &cp, const Fvector &tp) { + active = true; + from_pos = cp; + cur_pos = cp; + target_pos = tp; + time_last_update = Device.dwTimeGlobal; + enemy = e; + } + + void deactivate() { + active = false; + } + + } m_gravi_object; + + LPCSTR particle_gravi_wave; + LPCSTR particle_gravi_prepare; + LPCSTR particle_tele_object; + + ////////////////////////////////////////////////////////////////////////// + // Sounds + ref_sound sound_gravi_wave; + ref_sound sound_scan; + + ref_sound sound_tele_hold; + ref_sound sound_tele_throw; + + enum EBurerSounds { + eAdditionalSounds = MonsterSound::eMonsterSoundCustom, + + eMonsterSoundGraviAttack = eAdditionalSounds | 0, + eMonsterSoundTeleAttack = eAdditionalSounds | 1, + }; + ////////////////////////////////////////////////////////////////////////// + + + + u32 m_gravi_speed; + u32 m_gravi_step; + u32 m_gravi_time_to_hold; + float m_gravi_radius; + float m_gravi_impulse_to_objects; + float m_gravi_impulse_to_enemy; + float m_gravi_hit_power; + + + u32 m_tele_max_handled_objects; + u32 m_tele_time_to_hold; + float m_tele_object_min_mass; + float m_tele_object_max_mass; + float m_tele_find_radius; + + u32 m_shield_cooldown; + u32 m_shield_time; + bool m_shield_active; + LPCSTR m_shield_keep_particle; + u32 m_shield_keep_particle_period; + LPCSTR particle_fire_shield; + + CBurerFastGravi *m_fast_gravi; + +public: + CBurer (); + virtual ~CBurer (); + + + virtual void reinit (); + virtual void reload (LPCSTR section); + + virtual void Load (LPCSTR section); + + virtual void net_Destroy (); + virtual void net_Relcase (CObject *O); + virtual void shedule_Update (u32 dt); + virtual void UpdateCL (); + virtual void Hit (SHit* pHDS); + virtual void Die (CObject* who); + void ProcessTurn (); + virtual void CheckSpecParams (u32 spec_params); + + void UpdateGraviObject (); + + void StartGraviPrepare (); + void StopGraviPrepare (); + + void StartTeleObjectParticle(CGameObject *pO); + void StopTeleObjectParticle(CGameObject *pO); + + void ActivateShield () {m_shield_active = true;} + void DeactivateShield () {m_shield_active = false;} + + virtual bool ability_distant_feel() {return true;} + + virtual void on_scanning (); + virtual void on_scan_success (); + +public: + SAnimationTripleData anim_triple_gravi; + SAnimationTripleData anim_triple_tele; + SAnimationTripleData anim_triple_shield; + +#ifdef DEBUG + virtual CBaseMonster::SDebugInfo show_debug_info(); +#endif + + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CBurer) +#undef script_type_list +#define script_type_list save_type_list(CBurer) diff --git a/src/xrGameLA/ai/monsters/burer/burer_fast_gravi.cpp b/src/xrGameLA/ai/monsters/burer/burer_fast_gravi.cpp new file mode 100644 index 000000000..ff5afd8ed --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_fast_gravi.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include "burer_fast_gravi.h" +#include "burer.h" +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" + +bool CBurerFastGravi::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured_pure()) return false; + if (!m_object->EnemyMan.get_enemy()) return false; + + return true; +} + +void CBurerFastGravi::activate() +{ + CBurer *burer = smart_cast(m_object); + m_man->subscribe (this, ControlCom::eventTAChange); + m_object->com_man().ta_activate(burer->anim_triple_gravi); + + m_object->dir().face_target(m_object->EnemyMan.get_enemy()); +} + +void CBurerFastGravi::deactivate() +{ + m_man->unsubscribe (this, ControlCom::eventTAChange); +} + +void CBurerFastGravi::on_event(ControlCom::EEventType type, ControlCom::IEventData *data) +{ + if (type == ControlCom::eventTAChange) { + STripleAnimEventData *event_data = (STripleAnimEventData *)data; + if (event_data->m_current_state == eStateExecute) { + process_hit(); + m_object->com_man().ta_pointbreak(); + m_man->deactivate (this); + } + } +} + +void CBurerFastGravi::process_hit() +{ + m_object->HitEntity(m_object->EnemyMan.get_enemy(), 1.f, 100.f, m_object->Direction()); +} + diff --git a/src/xrGameLA/ai/monsters/burer/burer_fast_gravi.h b/src/xrGameLA/ai/monsters/burer/burer_fast_gravi.h new file mode 100644 index 000000000..d8e61d8b0 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_fast_gravi.h @@ -0,0 +1,18 @@ +#pragma once +#include "../control_combase.h" + +class CBurerFastGravi : public CControl_ComCustom<> { + typedef CControl_ComCustom<> inherited; + +public: + + virtual bool check_start_conditions (); + virtual void activate (); + virtual void deactivate (); + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + +private: + void process_hit (); + +}; + diff --git a/src/xrGameLA/ai/monsters/burer/burer_script.cpp b/src/xrGameLA/ai/monsters/burer/burer_script.cpp new file mode 100644 index 000000000..4f17af3bc --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "burer.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CBurer::script_register(lua_State *L) +{ + module(L) + [ + class_("CBurer") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack.h new file mode 100644 index 000000000..31d9b40be --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack.h @@ -0,0 +1,21 @@ +#pragma once +#include "../state.h" + +template +class CStateBurerAttack : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object> *state_ptr; + + bool m_force_gravi; + +public: + CStateBurerAttack (_Object *obj); + + virtual void initialize (); + + virtual void reselect_state (); + virtual void setup_substates (); + virtual void check_force_state (); +}; + +#include "burer_state_attack_inline.h" diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_gravi.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_gravi.h new file mode 100644 index 000000000..68f5c363d --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_gravi.h @@ -0,0 +1,38 @@ +#pragma once +#include "../state.h" + +template +class CStateBurerAttackGravi : public CState<_Object> { + typedef CState<_Object> inherited; + + enum { + ACTION_GRAVI_STARTED, + ACTION_GRAVI_CONTINUE, + ACTION_GRAVI_FIRE, + ACTION_WAIT_TRIPLE_END, + ACTION_COMPLETED, + } m_action; + + u32 time_gravi_started; + +public: + CStateBurerAttackGravi (_Object *obj); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); + +private: + // выполнять состояние + void ExecuteGraviStart (); + void ExecuteGraviContinue (); + void ExecuteGraviFire (); + +}; + +#include "burer_state_attack_gravi_inline.h" + diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_gravi_inline.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_gravi_inline.h new file mode 100644 index 000000000..6767c09ca --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_gravi_inline.h @@ -0,0 +1,160 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBurerAttackGraviAbstract CStateBurerAttackGravi<_Object> + +#define GOOD_DISTANCE_FOR_GRAVI 6.f + +TEMPLATE_SPECIALIZATION +CStateBurerAttackGraviAbstract::CStateBurerAttackGravi(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackGraviAbstract::initialize() +{ + inherited::initialize (); + + m_action = ACTION_GRAVI_STARTED; + + time_gravi_started = 0; + + object->set_script_capture (false); +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackGraviAbstract::execute() +{ + switch (m_action) { + /************************/ + case ACTION_GRAVI_STARTED: + /************************/ + + ExecuteGraviStart(); + m_action = ACTION_GRAVI_CONTINUE; + + break; + /************************/ + case ACTION_GRAVI_CONTINUE: + /************************/ + + ExecuteGraviContinue(); + + break; + + /************************/ + case ACTION_GRAVI_FIRE: + /************************/ + + ExecuteGraviFire(); + m_action = ACTION_WAIT_TRIPLE_END; + + break; + /***************************/ + case ACTION_WAIT_TRIPLE_END: + /***************************/ + if (!object->com_man().ta_is_active()) { + m_action = ACTION_COMPLETED; + } + + /*********************/ + case ACTION_COMPLETED: + /*********************/ + + break; + } + + object->anim().m_tAction = ACT_STAND_IDLE; + object->dir().face_target (object->EnemyMan.get_enemy(), 500); +} +TEMPLATE_SPECIALIZATION +void CStateBurerAttackGraviAbstract::finalize() +{ + inherited::finalize(); + + object->com_man().ta_pointbreak (); +// object->DeactivateShield (); + object->set_script_capture (true); +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackGraviAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + object->com_man().ta_pointbreak (); +// object->DeactivateShield (); + object->StopGraviPrepare (); + object->set_script_capture (false); +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerAttackGraviAbstract::check_start_conditions() +{ + // обработать объекты + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + if (dist < GOOD_DISTANCE_FOR_GRAVI) return false; + if (!object->EnemyMan.see_enemy_now()) return false; + if (!object->control().direction().is_face_target(object->EnemyMan.get_enemy(), deg(45))) return false; + if (object->com_man().ta_is_active()) return false; + + // всё ок, можно начать грави атаку + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerAttackGraviAbstract::check_completion() +{ + return (m_action == ACTION_COMPLETED); +} + +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackGraviAbstract::ExecuteGraviStart() +{ + object->com_man().ta_activate(object->anim_triple_gravi); + + time_gravi_started = Device.dwTimeGlobal; + + object->StartGraviPrepare(); +// object->ActivateShield(); +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackGraviAbstract::ExecuteGraviContinue() +{ + // проверить на грави удар + + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + float time_to_hold = (abs(dist - GOOD_DISTANCE_FOR_GRAVI)/GOOD_DISTANCE_FOR_GRAVI); + clamp(time_to_hold, 0.f, 1.f); + time_to_hold *= float(object->m_gravi_time_to_hold); + + if (time_gravi_started + u32(time_to_hold) < Device.dwTimeGlobal) { + m_action = ACTION_GRAVI_FIRE; + } +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackGraviAbstract::ExecuteGraviFire() +{ + object->com_man().ta_pointbreak(); + + Fvector from_pos; + Fvector target_pos; + from_pos = object->Position(); from_pos.y += 0.5f; + target_pos = object->EnemyMan.get_enemy()->Position(); target_pos.y += 0.5f; + + object->m_gravi_object.activate(object->EnemyMan.get_enemy(), from_pos, target_pos); + + object->StopGraviPrepare (); + object->sound().play (CBurer::eMonsterSoundGraviAttack); +// object->DeactivateShield (); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBurerAttackGraviAbstract \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_inline.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_inline.h new file mode 100644 index 000000000..c8fc74716 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_inline.h @@ -0,0 +1,134 @@ +#pragma once + +#include "burer_state_attack_tele.h" +#include "burer_state_attack_gravi.h" +#include "burer_state_attack_melee.h" +#include "burer_state_attack_shield.h" +#include "../states/state_look_point.h" +#include "../states/state_move_to_restrictor.h" +#include "burer_state_attack_run_around.h" + +#define GRAVI_PERCENT 80 +#define TELE_PERCENT 50 +#define RUN_AROUND_PERCENT 20 + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBurerAttackAbstract CStateBurerAttack<_Object> + +TEMPLATE_SPECIALIZATION +CStateBurerAttackAbstract::CStateBurerAttack(_Object *obj) : inherited(obj) +{ + add_state(eStateBurerAttack_Tele, new CStateBurerAttackTele<_Object>(obj)); + add_state(eStateBurerAttack_Gravi, new CStateBurerAttackGravi<_Object>(obj)); + add_state(eStateBurerAttack_Melee, new CStateBurerAttackMelee<_Object>(obj)); + + add_state(eStateBurerAttack_FaceEnemy, new CStateMonsterLookToPoint<_Object>(obj)); + add_state(eStateBurerAttack_RunAround, new CStateBurerAttackRunAround<_Object>(obj)); + + add_state(eStateCustomMoveToRestrictor, new CStateMonsterMoveToRestrictor<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackAbstract::initialize() +{ + inherited::initialize (); + m_force_gravi = false; +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackAbstract::reselect_state() +{ + if (get_state(eStateBurerAttack_Melee)->check_start_conditions()) { + select_state(eStateBurerAttack_Melee); + return; + } + + if (m_force_gravi) { + m_force_gravi = false; + + if (get_state(eStateBurerAttack_Gravi)->check_start_conditions()) { + select_state (eStateBurerAttack_Gravi); + return; + } + } + + if (get_state(eStateCustomMoveToRestrictor)->check_start_conditions()) { + select_state(eStateCustomMoveToRestrictor); + return; + } + + bool enable_gravi = false;//get_state(eStateBurerAttack_Gravi)->check_start_conditions (); + bool enable_tele = get_state(eStateBurerAttack_Tele)->check_start_conditions (); + + if (!enable_gravi && !enable_tele) { + if (prev_substate == eStateBurerAttack_RunAround) + select_state(eStateBurerAttack_FaceEnemy); + else + select_state(eStateBurerAttack_RunAround); + return; + } + + if (enable_gravi && enable_tele) { + + u32 rnd_val = ::Random.randI(GRAVI_PERCENT + TELE_PERCENT + RUN_AROUND_PERCENT); + u32 cur_val = GRAVI_PERCENT; + + if (rnd_val < cur_val) { + select_state(eStateBurerAttack_Gravi); + return; + } + + cur_val += TELE_PERCENT; + if (rnd_val < cur_val) { + select_state(eStateBurerAttack_Tele); + return; + } + + select_state(eStateBurerAttack_RunAround); + return; + } + + if ((prev_substate == eStateBurerAttack_RunAround) || (prev_substate == eStateBurerAttack_FaceEnemy)) { + if (enable_gravi) select_state(eStateBurerAttack_Gravi); + else select_state(eStateBurerAttack_Tele); + } else { + select_state(eStateBurerAttack_RunAround); + } +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateBurerAttack_FaceEnemy) { + SStateDataLookToPoint data; + + data.point = object->EnemyMan.get_enemy()->Position(); + data.action.action = ACT_STAND_IDLE; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwAttackSndDelay; + + state->fill_data_with (&data, sizeof(SStateDataLookToPoint)); + return; + } + +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackAbstract::check_force_state() +{ + // check if we can start execute + if ((current_substate == eStateCustomMoveToRestrictor) || (prev_substate == eStateBurerAttack_RunAround)) { + if (get_state(eStateBurerAttack_Gravi)->check_start_conditions()) { + current_substate = u32(-1); + m_force_gravi = true; + } + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBurerAttackAbstract diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_melee.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_melee.h new file mode 100644 index 000000000..8b05e56c7 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_melee.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../state.h" +#include "../states/monster_state_attack.h" + +template +class CStateBurerAttackMelee : public CStateMonsterAttack<_Object> { + typedef CStateMonsterAttack<_Object> inherited; + +public: + CStateBurerAttackMelee (_Object *obj); + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "burer_state_attack_melee_inline.h" diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_melee_inline.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_melee_inline.h new file mode 100644 index 000000000..84e263561 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_melee_inline.h @@ -0,0 +1,37 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBurerAttackMeleeAbstract CStateBurerAttackMelee<_Object> + +#define MIN_DIST_MELEE_ATTACK 5.f +#define MAX_DIST_MELEE_ATTACK 9.f + +TEMPLATE_SPECIALIZATION +CStateBurerAttackMeleeAbstract::CStateBurerAttackMelee(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerAttackMeleeAbstract::check_start_conditions() +{ + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + if (dist > MIN_DIST_MELEE_ATTACK) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerAttackMeleeAbstract::check_completion() +{ + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + if (dist < MAX_DIST_MELEE_ATTACK) return false; + + return true; + +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBurerAttackMeleeAbstract diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_run_around.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_run_around.h new file mode 100644 index 000000000..a652f13fe --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_run_around.h @@ -0,0 +1,22 @@ +#pragma once +#include "../state.h" + +template +class CStateBurerAttackRunAround : public CState<_Object> { + typedef CState<_Object> inherited; + + Fvector selected_point; + u32 time_started; + + Fvector dest_direction; + +public: + CStateBurerAttackRunAround (_Object *obj); + virtual void initialize (); + virtual void execute (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "burer_state_attack_run_around_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_run_around_inline.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_run_around_inline.h new file mode 100644 index 000000000..765fec336 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_run_around_inline.h @@ -0,0 +1,89 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBurerAttackRunAroundAbstract CStateBurerAttackRunAround<_Object> + + +#define DIST_QUANT 10.f +#define TIME_RUN_AWAY 3500 + +TEMPLATE_SPECIALIZATION +CStateBurerAttackRunAroundAbstract::CStateBurerAttackRunAround(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackRunAroundAbstract::initialize() +{ + inherited::initialize (); + + time_started = Device.dwTimeGlobal; + dest_direction.set (0.f,0.f,0.f); + + // select point + Fvector dir_to_enemy, dir_from_enemy; + dir_to_enemy.sub (object->EnemyMan.get_enemy()->Position(),object->Position()); + dir_to_enemy.normalize (); + + dir_from_enemy.sub (object->Position(),object->EnemyMan.get_enemy()->Position()); + dir_from_enemy.normalize (); + + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + + if (dist > 30.f) { // бежать к врагу + selected_point.mad(object->Position(),dir_to_enemy,DIST_QUANT); + } else if ((dist < 20.f) && (dist > 4.f)) { // убегать от врага + selected_point.mad(object->Position(),dir_from_enemy,DIST_QUANT); + dest_direction.sub (object->EnemyMan.get_enemy()->Position(),selected_point); + dest_direction.normalize (); + } else { // выбрать случайную позицию + selected_point = random_position(object->Position(), DIST_QUANT); + dest_direction.sub (object->EnemyMan.get_enemy()->Position(),selected_point); + dest_direction.normalize (); + } + + object->path().prepare_builder(); +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackRunAroundAbstract::execute() +{ + if (!fis_zero(dest_direction.square_magnitude())) { + object->path().set_use_dest_orient (true); + object->path().set_dest_direction (dest_direction); + } else object->path().set_use_dest_orient (false); + + + object->set_action (ACT_RUN); + object->path().set_target_point (selected_point); + object->path().set_generic_parameters (); + object->path().set_use_covers (false); + + object->set_state_sound (MonsterSound::eMonsterSoundAggressive); +} + + +TEMPLATE_SPECIALIZATION +bool CStateBurerAttackRunAroundAbstract::check_start_conditions() +{ + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerAttackRunAroundAbstract::check_completion() +{ + if ((time_started + TIME_RUN_AWAY < Device.dwTimeGlobal) || + (object->control().path_builder().is_moving_on_path() && object->control().path_builder().is_path_end(2.f))) { + + object->dir().face_target(object->EnemyMan.get_enemy()); + return true; + } + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBurerAttackRunAroundAbstract diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_shield.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_shield.h new file mode 100644 index 000000000..3ce72fb9b --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_shield.h @@ -0,0 +1,33 @@ +#pragma once +#include "../state.h" + + +template +class CStateBurerShield : public CState<_Object> { + typedef CState<_Object> inherited; + + u32 m_last_shield_started; + u32 m_next_particle_allowed; + float m_shield_start_anim_length_sec; + enum { + ACTION_IDLE, + ACTION_SHIELD_STARTED, + ACTION_WAIT_TRIPLE_END, + ACTION_COMPLETED, + } m_action; + +public: + CStateBurerShield (_Object *obj); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); + +private: +}; + +#include "burer_state_attack_shield_inline.h" diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_shield_inline.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_shield_inline.h new file mode 100644 index 000000000..af16b8065 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_shield_inline.h @@ -0,0 +1,119 @@ +#pragma once + +#include "../../../level.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBurerShieldAbstract CStateBurerShield<_Object> + +TEMPLATE_SPECIALIZATION +CStateBurerShieldAbstract::CStateBurerShield(_Object *obj) : inherited(obj) +{ + m_shield_start_anim_length_sec = 0.0f; + m_last_shield_started = 0; + m_next_particle_allowed = 0; + m_action = ACTION_IDLE; +} + +TEMPLATE_SPECIALIZATION +void CStateBurerShieldAbstract::initialize() +{ + inherited::initialize (); + + m_last_shield_started = Device.dwTimeGlobal; + m_next_particle_allowed = 0; + m_action = ACTION_IDLE; +// m_shield_start_anim_length_sec = object->anim().motion_time(eAnimShieldStart, 0, object->Visual()); +} + +TEMPLATE_SPECIALIZATION +void CStateBurerShieldAbstract::execute() +{ + switch (m_action) + { + case ACTION_IDLE: + object->ActivateShield(); + object->com_man().ta_activate(object->anim_triple_shield); + m_action = ACTION_SHIELD_STARTED; + break; + + case ACTION_SHIELD_STARTED: + if (object->m_shield_keep_particle && Device.dwTimeGlobal > m_next_particle_allowed) + { + CParticlesPlayer* PP = smart_cast(object); + if(!PP) return; + PP->StartParticles(object->m_shield_keep_particle,Fvector().set(0.0f,0.1f,0.0f),object->ID()); + m_next_particle_allowed = Device.dwTimeGlobal + object->m_shield_keep_particle_period; + } + break; +// case ACTION_WAIT_TRIPLE_END: +// if (!object->com_man().ta_is_active()) +// { +// m_action = ACTION_COMPLETED; +// } +// break; + case ACTION_COMPLETED: + break; + } + + if (object->EnemyMan.get_enemy()) + object->dir().face_target(object->EnemyMan.get_enemy(), 500); + else { + Fvector pos; + pos.mad(object->Position(), Fvector().set(0.1f,0.0f,0.0f)); + object->dir().face_target(pos, 500); + } + object->set_action(ACT_STAND_IDLE); +} + +TEMPLATE_SPECIALIZATION +void CStateBurerShieldAbstract::finalize() +{ + inherited::finalize(); + + object->com_man().ta_pointbreak (); + object->DeactivateShield(); + object->set_script_capture(true); +} + +TEMPLATE_SPECIALIZATION +void CStateBurerShieldAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + object->com_man().ta_pointbreak (); + object->DeactivateShield(); + object->set_script_capture(false); +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerShieldAbstract::check_start_conditions() +{ + if (Device.dwTimeGlobal > m_last_shield_started + object->m_shield_time + object->m_shield_cooldown) + { + if (object->EnemyMan.get_enemy() && !object->EnemyMan.enemy_see_me_now()) return false; + } + else + return false; + + if (object->com_man().ta_is_active()) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerShieldAbstract::check_completion() +{ + if (Device.dwTimeGlobal <= m_last_shield_started + object->m_shield_time) + { + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + if ((enemy && enemy != Actor()) || !Actor()->IsReloadingWeapon()) + return enemy == 0; + } + return true; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBurerShieldAbstract diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_tele.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_tele.h new file mode 100644 index 000000000..b479da524 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_tele.h @@ -0,0 +1,58 @@ +#pragma once +#include "../state.h" + + +template +class CStateBurerAttackTele : public CState<_Object> { + typedef CState<_Object> inherited; + + xr_vector tele_objects; + CPhysicsShellHolder *selected_object; + xr_vector m_nearest; + + u32 time_started; + + enum { + ACTION_TELE_STARTED, + ACTION_TELE_CONTINUE, + ACTION_TELE_FIRE, + ACTION_WAIT_TRIPLE_END, + ACTION_COMPLETED, + } m_action; + +public: + CStateBurerAttackTele (_Object *obj); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); + + +private: + // Поиск объектов для телекинеза + void FindObjects (); + + // выполнять состояние + void ExecuteTeleStart (); + void ExecuteTeleContinue (); + void ExecuteTeleFire (); + + // Проверка, есть ли хоть один объект под контролем + bool IsActiveObjects (); + + // Проверить, может ли стартовать телекинез + bool CheckTeleStart (); + // Выбор подходящих объектов для телекинеза + void SelectObjects (); + + // internal for FindObjects + void FindFreeObjects (xr_vector &tpObjects, const Fvector &pos); + +private: +}; + +#include "burer_state_attack_tele_inline.h" diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_attack_tele_inline.h b/src/xrGameLA/ai/monsters/burer/burer_state_attack_tele_inline.h new file mode 100644 index 000000000..b8b038798 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_attack_tele_inline.h @@ -0,0 +1,363 @@ +#pragma once + +#include "../../../level.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateBurerAttackTeleAbstract CStateBurerAttackTele<_Object> + + +#define GOOD_DISTANCE_FOR_TELE 15.f +#define TELE_DELAY 4000 + +#define MAX_TIME_CHECK_FAILURE 6000 + +TEMPLATE_SPECIALIZATION +CStateBurerAttackTeleAbstract::CStateBurerAttackTele(_Object *obj) : inherited(obj) +{ + +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackTeleAbstract::initialize() +{ + inherited::initialize (); + + m_action = ACTION_TELE_STARTED; + selected_object = 0; + + SelectObjects (); + + time_started = 0; + + // запретить взятие скриптом + object->set_script_capture (false); + +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackTeleAbstract::execute() +{ + switch (m_action) { + /************************/ + case ACTION_TELE_STARTED: + /************************/ + + ExecuteTeleStart(); + m_action = ACTION_TELE_CONTINUE; + + break; + /************************/ + case ACTION_TELE_CONTINUE: + /************************/ + + ExecuteTeleContinue(); + + break; + + /************************/ + case ACTION_TELE_FIRE: + /************************/ + + ExecuteTeleFire(); + m_action = ACTION_WAIT_TRIPLE_END; + + break; + /***************************/ + case ACTION_WAIT_TRIPLE_END: + /***************************/ + + if (!object->com_man().ta_is_active()) { + if (IsActiveObjects()) + m_action = ACTION_TELE_STARTED; + else + m_action = ACTION_COMPLETED; + } + + /*********************/ + case ACTION_COMPLETED: + /*********************/ + + break; + } + + object->anim().m_tAction = ACT_STAND_IDLE; + object->dir().face_target (object->EnemyMan.get_enemy(), 700); + +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackTeleAbstract::finalize() +{ + inherited::finalize (); + + tele_objects.clear (); +// object->DeactivateShield (); + + // clear particles on active objects + if (object->CTelekinesis::is_active()) { + for (u32 i=0; iCTelekinesis::get_objects_count(); i++) { + object->StopTeleObjectParticle(object->CTelekinesis::get_object_by_index(i).get_object()); + } + } + + // отменить запрет на взятие скриптом + object->set_script_capture (true); +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackTeleAbstract::critical_finalize() +{ + inherited::critical_finalize (); + + object->com_man().ta_pointbreak (); + object->CTelekinesis::Deactivate (); +// object->DeactivateShield (); + + tele_objects.clear (); + + // clear particles on active objects + if (object->CTelekinesis::is_active()) { + for (u32 i=0; iCTelekinesis::get_objects_count(); i++) { + object->StopTeleObjectParticle(object->CTelekinesis::get_object_by_index(i).get_object()); + } + } + + // отменить запрет на взятие скриптом + object->set_script_capture (true); +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerAttackTeleAbstract::check_start_conditions() +{ + return CheckTeleStart(); +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerAttackTeleAbstract::check_completion() +{ + return (m_action == ACTION_COMPLETED); +} + +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackTeleAbstract::FindFreeObjects(xr_vector &tpObjects, const Fvector &pos) +{ + Level().ObjectSpace.GetNearest (tpObjects, pos, object->m_tele_find_radius, NULL); + + for (u32 i=0;i(tpObjects[i]); + CCustomMonster *custom_monster = smart_cast(tpObjects[i]); + if (!obj || + !obj->PPhysicsShell() || + !obj->PPhysicsShell()->isActive()|| + custom_monster || + (obj->spawn_ini() && obj->spawn_ini()->section_exist("ph_heavy")) || + (obj->m_pPhysicsShell->getMass() < object->m_tele_object_min_mass) || + (obj->m_pPhysicsShell->getMass() > object->m_tele_object_max_mass) || + (obj == object) || + object->CTelekinesis::is_active_object(obj) || + !obj->m_pPhysicsShell->get_ApplyByGravity()) continue; + + tele_objects.push_back(obj); + } +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackTeleAbstract::FindObjects () +{ + u32 res_size = tele_objects.size (); + tele_objects.clear_and_reserve (); + + // получить список объектов вокруг врага + m_nearest.clear_not_free (); + m_nearest.reserve (res_size); + FindFreeObjects (m_nearest, object->EnemyMan.get_enemy()->Position()); + + // получить список объектов вокруг монстра + FindFreeObjects (m_nearest, object->Position()); + + // получить список объектов между монстром и врагом + float dist = object->EnemyMan.get_enemy()->Position().distance_to(object->Position()); + Fvector dir; + dir.sub(object->EnemyMan.get_enemy()->Position(), object->Position()); + dir.normalize(); + + Fvector pos; + pos.mad (object->Position(), dir, dist / 2.f); + FindFreeObjects (m_nearest, pos); + + + // оставить уникальные объекты + tele_objects.erase ( + std::unique( + tele_objects.begin(), + tele_objects.end() + ), + tele_objects.end() + ); +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackTeleAbstract::ExecuteTeleStart() +{ + object->com_man().ta_activate(object->anim_triple_tele); + time_started = Device.dwTimeGlobal; +// object->ActivateShield(); + +} + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackTeleAbstract::ExecuteTeleContinue() +{ + if (time_started + object->m_tele_time_to_hold > Device.dwTimeGlobal) return; + + // найти объект для атаки + bool object_found = false; + CTelekineticObject tele_object; + + u32 i=0; + while (i < object->CTelekinesis::get_objects_count()) { + tele_object = object->CTelekinesis::get_object_by_index(i); + + if ((tele_object.get_state() == TS_Keep) && (tele_object.time_keep_started + 1500 < Device.dwTimeGlobal)) { + + object_found = true; + break; + + } else i++; + + } + + if (object_found) { + m_action = ACTION_TELE_FIRE; + selected_object = tele_object.get_object(); + } else { + if (!IsActiveObjects() || (time_started + MAX_TIME_CHECK_FAILURE < Device.dwTimeGlobal)) { + object->com_man().ta_deactivate (); + m_action = ACTION_COMPLETED; + } + } +} + +#define HEAD_OFFSET_INDOOR 1.f +#define HEAD_OFFSET_OUTDOOR 5.f + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackTeleAbstract::ExecuteTeleFire() +{ + object->com_man().ta_pointbreak(); + + Fvector enemy_pos; + enemy_pos = get_head_position(const_cast(object->EnemyMan.get_enemy())); + object->CTelekinesis::fire_t(selected_object,enemy_pos, 0.55f); + + object->StopTeleObjectParticle (selected_object); + object->sound().play (CBurer::eMonsterSoundTeleAttack); +// object->DeactivateShield (); +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerAttackTeleAbstract::IsActiveObjects() +{ + return (object->CTelekinesis::get_objects_count() > 0); +} + +TEMPLATE_SPECIALIZATION +bool CStateBurerAttackTeleAbstract::CheckTeleStart() +{ + if (object->com_man().ta_is_active()) return false; + + // проверка на текущую активность + if (IsActiveObjects()) return false; + + // проверить дистанцию до врага + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + if (dist < GOOD_DISTANCE_FOR_TELE) return false; + + // найти телекинетические объекты + FindObjects(); + + // если нет объектов + if (tele_objects.empty()) return false; + + // всё ок можно начинать телекинез + return true; + +} + +////////////////////////////////////////////////////////////////////////// +// Выбор подходящих объектов для телекинеза +////////////////////////////////////////////////////////////////////////// +class best_object_predicate { + Fvector enemy_pos; + Fvector monster_pos; +public: + best_object_predicate(const Fvector &m_pos, const Fvector &pos) { + monster_pos = m_pos; + enemy_pos = pos; + } + + bool operator() (const CGameObject *tpObject1, const CGameObject *tpObject2) const + { + + float dist1 = monster_pos.distance_to(tpObject1->Position()); + float dist2 = enemy_pos.distance_to(tpObject2->Position()); + float dist3 = enemy_pos.distance_to(monster_pos); + + return ((dist1 < dist3) && (dist2 > dist3)); + }; +}; + +class best_object_predicate2 { + Fvector enemy_pos; + Fvector monster_pos; +public: + best_object_predicate2(const Fvector &m_pos, const Fvector &pos) { + monster_pos = m_pos; + enemy_pos = pos; + } + + bool operator() (const CGameObject *tpObject1, const CGameObject *tpObject2) const + { + float dist1 = enemy_pos.distance_to(tpObject1->Position()); + float dist2 = enemy_pos.distance_to(tpObject2->Position()); + + return (dist1 < dist2); + }; +}; + + +TEMPLATE_SPECIALIZATION +void CStateBurerAttackTeleAbstract::SelectObjects() +{ + std::sort(tele_objects.begin(),tele_objects.end(),best_object_predicate2(object->Position(), object->EnemyMan.get_enemy()->Position())); + + // выбрать объект + for (u32 i=0; im_monster_type == CBaseMonster::eMonsterTypeIndoor) ? 1.3f : 2.f; + bool rotate = (object->m_monster_type == CBaseMonster::eMonsterTypeIndoor) ? false : true; + + CTelekineticObject *tele_obj = object->CTelekinesis::activate (obj, 3.f, height, 10000, rotate); + tele_obj->set_sound (object->sound_tele_hold,object->sound_tele_throw); + + object->StartTeleObjectParticle (obj); + + // удалить из списка + tele_objects[i] = tele_objects[tele_objects.size()-1]; + tele_objects.pop_back(); + + if (object->CTelekinesis::get_objects_count() >= object->m_tele_max_handled_objects) break; + } +} + + +#undef TEMPLATE_SPECIALIZATION +#undef CStateBurerAttackTeleAbstract diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_manager.cpp b/src/xrGameLA/ai/monsters/burer/burer_state_manager.cpp new file mode 100644 index 000000000..a1b0cde75 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_manager.cpp @@ -0,0 +1,90 @@ +#include "stdafx.h" +#include "burer.h" +#include "burer_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../states/state_custom_action.h" + +#include "burer_state_attack.h" + + +CStateManagerBurer::CStateManagerBurer(CBurer *monster) : inherited(monster) +{ + add_state(eStateRest, new CStateMonsterRest(monster)); + add_state(eStatePanic, new CStateMonsterPanic(monster)); + add_state(eStateAttack, new CStateBurerAttack(monster)); + add_state(eStateEat, new CStateMonsterEat(monster)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(monster)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(monster)); + add_state(eStateHitted, new CStateMonsterHitted(monster)); + add_state(eStateBurerScanning, new CStateMonsterCustomAction(monster)); + add_state(eStateBurerAttack_Shield, new CStateBurerShield(monster)); + m_last_health = 1.f; +} + +#define SCAN_STATE_TIME 4000 + +void CStateManagerBurer::execute() +{ + u32 state = u32(-1); + + bool lost_health = false; + if (m_last_health - 0.01 > object->GetfHealth()) + { + m_last_health = object->GetfHealth(); + lost_health = true; + } + + if (current_substate == eStateBurerAttack_Shield && check_state(eStateBurerAttack_Shield)) + { + state = eStateBurerAttack_Shield; + } + else if (lost_health && check_state(eStateBurerAttack_Shield)) { + state = eStateBurerAttack_Shield; + } + else if (object->EnemyMan.get_enemy()) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state = eStatePanic; break; + case eWeak: state = eStateAttack; break; + } + } else if (object->HitMemory.is_hit() && (object->HitMemory.get_last_hit_time() + 10000 > Device.dwTimeGlobal)) + state = eStateHitted; + else if (object->hear_dangerous_sound || object->hear_interesting_sound) { + state = eStateHearInterestingSound; + } else if (object->time_last_scan + SCAN_STATE_TIME > Device.dwTimeGlobal){ + state = eStateBurerScanning; + } else if (can_eat()) { + state = eStateEat; + } else state = eStateRest; + + select_state(state); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} + +void CStateManagerBurer::setup_substates() +{ + if (current_substate == eStateBurerScanning) { + SStateDataAction data; + + data.action = ACT_LOOK_AROUND; + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + + get_state_current()->fill_data_with(&data, sizeof(SStateDataAction)); + return; + } +} diff --git a/src/xrGameLA/ai/monsters/burer/burer_state_manager.h b/src/xrGameLA/ai/monsters/burer/burer_state_manager.h new file mode 100644 index 000000000..ec1e1e760 --- /dev/null +++ b/src/xrGameLA/ai/monsters/burer/burer_state_manager.h @@ -0,0 +1,16 @@ +#pragma once +#include "../monster_state_manager.h" + +class CBurer; + +class CStateManagerBurer : public CMonsterStateManager { + typedef CMonsterStateManager inherited; +public: + CStateManagerBurer (CBurer *monster); + virtual void execute (); + virtual void setup_substates (); + +private: + float m_last_health; +}; + diff --git a/src/xrGameLA/ai/monsters/cat/cat.cpp b/src/xrGameLA/ai/monsters/cat/cat.cpp new file mode 100644 index 000000000..2714983dd --- /dev/null +++ b/src/xrGameLA/ai/monsters/cat/cat.cpp @@ -0,0 +1,158 @@ +#include "stdafx.h" +#include "cat.h" +#include "cat_state_manager.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../monster_velocity_space.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" + + +CCat::CCat() +{ + StateMan = new CStateManagerCat(this); +} + +CCat::~CCat() +{ + xr_delete(StateMan); +} + +void CCat::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + anim().AddReplacedAnim(&m_bDamaged, eAnimStandIdle, eAnimStandDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandDamaged, "stand_idle_dmg_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_dmg_", -1, &velocity_walk_dmg, PS_STAND); + anim().AddAnim(eAnimRun, "stand_run_fwd_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND); + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimLookAround, "stand_look_around_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimLieIdle, "lie_idle_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimLieStandUp, "lie_stand_up_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimDragCorpse, "stand_drag_", -1, &velocity_drag, PS_STAND); + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimStandLieDown, "stand_lie_down_", -1, &velocity_none, PS_STAND); + + anim().AddAnim(eAnimJumpLeft, "stand_jump_ls_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimJumpRight, "stand_jump_rs_", -1, &velocity_none, PS_STAND); + + anim().AddTransition(PS_LIE, PS_STAND, eAnimLieStandUp, false); + anim().AddTransition(PS_STAND, PS_LIE, eAnimStandLieDown, false); + + + // link action + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimLieIdle); + anim().LinkAction(ACT_REST, eAnimLieIdle); + anim().LinkAction(ACT_DRAG, eAnimDragCorpse); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimLookAround); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + + //***************************************************************************** +} + +void CCat::reinit() +{ + inherited::reinit(); + + MotionID def1, def2, def3; + IKinematicsAnimated *pSkel = smart_cast(Visual()); + + def1 = pSkel->ID_Cycle_Safe("jump_attack_0"); VERIFY(def1); + def2 = pSkel->ID_Cycle_Safe("jump_attack_1"); VERIFY(def2); + def3 = pSkel->ID_Cycle_Safe("jump_attack_2"); VERIFY(def3); + + //CJumpingAbility::reinit(def1, def2, def3); +} + +void CCat::try_to_jump() +{ + CObject *target = const_cast(EnemyMan.get_enemy()); + if (!target || !EnemyMan.see_enemy_now()) return; +} + +void CCat::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_CHECK_CORPSE) == ASP_CHECK_CORPSE) { + com_man().seq_run(anim().get_motion_id(eAnimCheckCorpse)); + } + + if ((spec_params & ASP_ROTATION_JUMP) == ASP_ROTATION_JUMP) { + //float yaw, pitch; + //Fvector().sub(EnemyMan.get_enemy()->Position(), Position()).getHP(yaw,pitch); + //yaw *= -1; + //yaw = angle_normalize(yaw); + + //EMotionAnim anim = eAnimJumpLeft; + //if (from_right(yaw,movement().m_body.current.yaw)) { + // anim = eAnimJumpRight; + // yaw = angle_normalize(yaw + PI / 20); + //} else yaw = angle_normalize(yaw - PI / 20); + + //anim().Seq_Add(anim); + //anim().Seq_Switch(); + + //movement().stop_linear (); + //movement().m_body.target.yaw = yaw; + + //// calculate angular speed + //float new_angular_velocity; + //float delta_yaw = angle_difference(yaw,movement().m_body.current.yaw); + //float time = anim().GetCurAnimTime(); + //new_angular_velocity = delta_yaw / time; + + //anim().ForceAngularSpeed(new_angular_velocity); + + //return; + } + + +} + +void CCat::UpdateCL() +{ + inherited::UpdateCL (); +} + +void CCat::HitEntityInJump(const CEntity *pEntity) +{ + SAAParam ¶ms = anim().AA_GetParams("jump_attack_2"); + HitEntity (pEntity, params.hit_power, params.impulse, params.impulse_dir); +} + + diff --git a/src/xrGameLA/ai/monsters/cat/cat.h b/src/xrGameLA/ai/monsters/cat/cat.h new file mode 100644 index 000000000..333ddda60 --- /dev/null +++ b/src/xrGameLA/ai/monsters/cat/cat.h @@ -0,0 +1,29 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../../../script_export_space.h" + +class CCat : public CBaseMonster{ + typedef CBaseMonster inherited; +public: + CCat (); + virtual ~CCat (); + + virtual void Load (LPCSTR section); + virtual void reinit (); + + virtual void UpdateCL (); + + virtual void CheckSpecParams (u32 spec_params); + + void try_to_jump (); + + virtual void HitEntityInJump (const CEntity *pEntity); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CCat) +#undef script_type_list +#define script_type_list save_type_list(CCat) + + diff --git a/src/xrGameLA/ai/monsters/cat/cat_script.cpp b/src/xrGameLA/ai/monsters/cat/cat_script.cpp new file mode 100644 index 000000000..cdeb763b6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/cat/cat_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "cat.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CCat::script_register(lua_State *L) +{ + module(L) + [ + class_("CCat") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/cat/cat_state_manager.cpp b/src/xrGameLA/ai/monsters/cat/cat_state_manager.cpp new file mode 100644 index 000000000..70ea2d28d --- /dev/null +++ b/src/xrGameLA/ai/monsters/cat/cat_state_manager.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" +#include "cat.h" +#include "cat_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../../../clsid_game.h" +#include "../states/state_test_look_actor.h" +#include "../../../entitycondition.h" +#include "../states/monster_state_help_sound.h" + +CStateManagerCat::CStateManagerCat(CCat *obj) : inherited(obj) +{ + add_state(eStateRest, new CStateMonsterRest(obj)); + add_state(eStatePanic, new CStateMonsterPanic(obj)); + add_state(eStateAttack, new CStateMonsterAttack(obj)); + add_state(eStateEat, new CStateMonsterEat(obj)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(obj)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(obj)); + add_state(eStateHitted, new CStateMonsterHitted(obj)); + + add_state(eStateThreaten, new CStateMonsterLookActor(obj)); + add_state(eStateHearHelpSound, new CStateMonsterHearHelpSound(obj)); + + m_rot_jump_last_time = 0; +} + +CStateManagerCat::~CStateManagerCat() +{ +} + +#define ROTATION_JUMP_DELAY 3000 + +void CStateManagerCat::execute() +{ + u32 state_id = u32(-1); + + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + } + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (check_state(eStateHearHelpSound)) { + state_id = eStateHearHelpSound; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} + diff --git a/src/xrGameLA/ai/monsters/cat/cat_state_manager.h b/src/xrGameLA/ai/monsters/cat/cat_state_manager.h new file mode 100644 index 000000000..6f7c24798 --- /dev/null +++ b/src/xrGameLA/ai/monsters/cat/cat_state_manager.h @@ -0,0 +1,17 @@ +#pragma once +#include "../monster_state_manager.h" + +class CCat; + +class CStateManagerCat : public CMonsterStateManager { + + typedef CMonsterStateManager inherited; + + u32 m_rot_jump_last_time; + +public: + CStateManagerCat (CCat *obj); + virtual ~CStateManagerCat (); + + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/chimera/chimera.cpp b/src/xrGameLA/ai/monsters/chimera/chimera.cpp new file mode 100644 index 000000000..0070fc0e9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera.cpp @@ -0,0 +1,246 @@ +#include "stdafx.h" +#include "chimera.h" +#include "chimera_state_manager.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../monster_velocity_space.h" +#include "../../../level.h" +#include "../../../PhysicsShell.h" +#include "../../../sound_player.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + + +CChimera::CChimera() +{ + StateMan = new CStateManagerChimera(this); + com_man().add_ability(ControlCom::eControlJump); +} + +CChimera::~CChimera() +{ + xr_delete (StateMan); +} + +void CChimera::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + + anim().AddAnim(eAnimLieIdle, "lie_sleep_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimSleep, "lie_sleep_", -1, &velocity_none, PS_LIE); + + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND); + anim().AddAnim(eAnimRun, "stand_run_fwd_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND); + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + + anim().AddAnim(eAnimDragCorpse, "stand_drag_", -1, &velocity_drag, PS_STAND); + anim().AddAnim(eAnimLookAround, "stand_idle_", 2, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimDie, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimThreaten, "stand_threaten_", -1, &velocity_none, PS_STAND); + + anim().AddAnim(eAnimAttackRun, "stand_run_attack_", -1, &velocity_run, PS_STAND); + + ////////////////////////////////////////////////////////////////////////// + + anim().AddAnim(eAnimUpperStandIdle, "stand_up_idle_", -1, &velocity_none, PS_STAND_UPPER); + anim().AddAnim(eAnimUpperStandTurnLeft, "stand_up_turn_ls_", -1, &velocity_turn, PS_STAND_UPPER); + anim().AddAnim(eAnimUpperStandTurnRight, "stand_up_turn_rs_", -1, &velocity_turn, PS_STAND_UPPER); + + anim().AddAnim(eAnimStandToUpperStand, "stand_upper_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimUppperStandToStand, "stand_up_to_down_", -1, &velocity_none, PS_STAND_UPPER); + + anim().AddAnim(eAnimUpperWalkFwd, "stand_up_walk_fwd_", -1, &m_fsVelocityWalkUpper, PS_STAND_UPPER); + anim().AddAnim(eAnimUpperThreaten, "stand_up_threaten_", -1, &velocity_none, PS_STAND_UPPER); + anim().AddAnim(eAnimUpperAttack, "stand_up_attack_", -1, &velocity_turn, PS_STAND_UPPER); + + ////////////////////////////////////////////////////////////////////////// + // define transitions + anim().AddTransition(PS_STAND, PS_STAND_UPPER, eAnimStandToUpperStand, false); + anim().AddTransition(PS_STAND_UPPER, PS_STAND, eAnimUppperStandToStand, false); + + // link action + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimDragCorpse); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSleep); + anim().LinkAction(ACT_REST, eAnimLieIdle); + anim().LinkAction(ACT_DRAG, eAnimDragCorpse); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimLookAround); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + + //***************************************************************************** + + m_fsVelocityWalkUpper.Load (section, "Velocity_Walk_Upper"); +} + +void CChimera::reinit() +{ + inherited::reinit(); + b_upper_state = false; + + movement().detail().add_velocity(MonsterMovement::eChimeraVelocityParameterUpperWalkFwd, CDetailPathManager::STravelParams(m_fsVelocityWalkUpper.velocity.linear, m_fsVelocityWalkUpper.velocity.angular_path, m_fsVelocityWalkUpper.velocity.angular_real)); + move().load_velocity(*cNameSect(), "Velocity_JumpGround",MonsterMovement::eChimeraVelocityParameterJumpGround); + + com_man().load_jump_data("jump_attack_0",0, "jump_attack_1", "jump_attack_2", u32(-1), MonsterMovement::eChimeraVelocityParameterJumpGround,0); +} + +void CChimera::SetTurnAnimation(bool turn_left) +{ + if (b_upper_state) + (turn_left) ? anim().SetCurAnim(eAnimUpperStandTurnLeft) : anim().SetCurAnim(eAnimUpperStandTurnRight); + else + (turn_left) ? anim().SetCurAnim(eAnimStandTurnLeft) : anim().SetCurAnim(eAnimStandTurnRight); +} + +void CChimera::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_THREATEN) == ASP_THREATEN) { + if (b_upper_state) + anim().SetCurAnim(eAnimUpperThreaten); + else + anim().SetCurAnim(eAnimThreaten); + } + + if ((spec_params & ASP_ATTACK_RUN) == ASP_ATTACK_RUN) { + anim().SetCurAnim(eAnimAttackRun); + } + + if (b_upper_state) { + switch (anim().GetCurAnim()) { + case eAnimAttack: anim().SetCurAnim(eAnimUpperAttack); break; + case eAnimRun: + case eAnimWalkFwd: anim().SetCurAnim(eAnimUpperWalkFwd); break; + case eAnimStandTurnLeft: anim().SetCurAnim(eAnimUpperStandTurnLeft); break; + case eAnimStandTurnRight: anim().SetCurAnim(eAnimUpperStandTurnRight); break; + case eAnimThreaten: anim().SetCurAnim(eAnimUpperThreaten); break; + case eAnimStandIdle: anim().SetCurAnim(eAnimUpperStandIdle); break; + } + } +} + +EAction CChimera::CustomVelocityIndex2Action(u32 velocity_index) +{ + switch (velocity_index) { + case MonsterMovement::eChimeraVelocityParameterUpperWalkFwd: return ACT_WALK_FWD; + } + + return ACT_STAND_IDLE; +} + +void CChimera::TranslateActionToPathParams() +{ + bool bEnablePath = true; + u32 vel_mask = 0; + u32 des_mask = 0; + + switch (anim().m_tAction) { + case ACT_STAND_IDLE: + case ACT_SIT_IDLE: + case ACT_LIE_IDLE: + case ACT_EAT: + case ACT_SLEEP: + case ACT_REST: + case ACT_LOOK_AROUND: + case ACT_ATTACK: + bEnablePath = false; + break; + case ACT_WALK_FWD: + if (b_upper_state) { + vel_mask = MonsterMovement::eChimeraVelocityParamsUpperWalkFwd; + des_mask = MonsterMovement::eChimeraVelocityParameterUpperWalkFwd; + } else { + if (m_bDamaged) { + vel_mask = MonsterMovement::eVelocityParamsWalkDamaged; + des_mask = MonsterMovement::eVelocityParameterWalkDamaged; + } else { + vel_mask = MonsterMovement::eVelocityParamsWalk; + des_mask = MonsterMovement::eVelocityParameterWalkNormal; + } + } + break; + case ACT_WALK_BKWD: + break; + case ACT_RUN: + if (b_upper_state) { + vel_mask = MonsterMovement::eChimeraVelocityParamsUpperWalkFwd; + des_mask = MonsterMovement::eChimeraVelocityParameterUpperWalkFwd; + } else { + if (m_bDamaged) { + vel_mask = MonsterMovement::eVelocityParamsRunDamaged; + des_mask = MonsterMovement::eVelocityParameterRunDamaged; + } else { + vel_mask = MonsterMovement::eVelocityParamsRun; + des_mask = MonsterMovement::eVelocityParameterRunNormal; + } + } + break; + case ACT_DRAG: + vel_mask = MonsterMovement::eVelocityParamsDrag; + des_mask = MonsterMovement::eVelocityParameterDrag; + + anim().SetSpecParams(ASP_MOVE_BKWD); + + break; + case ACT_STEAL: + vel_mask = MonsterMovement::eVelocityParamsSteal; + des_mask = MonsterMovement::eVelocityParameterSteal; + break; + } + + if (m_force_real_speed) vel_mask = des_mask; + + if (bEnablePath) { + path().set_velocity_mask (vel_mask); + path().set_desirable_mask (des_mask); + path().enable_path (); + } else { + path().disable_path (); + } +} + +void CChimera::HitEntityInJump(const CEntity *pEntity) +{ + SAAParam ¶ms = anim().AA_GetParams("jump_attack_1"); + HitEntity (pEntity, params.hit_power, params.impulse, params.impulse_dir); +} + +void CChimera::UpdateCL() +{ + inherited::UpdateCL (); +} diff --git a/src/xrGameLA/ai/monsters/chimera/chimera.h b/src/xrGameLA/ai/monsters/chimera/chimera.h new file mode 100644 index 000000000..71553f8ba --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera.h @@ -0,0 +1,36 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../../../script_export_space.h" + +class CChimera : public CBaseMonster { + typedef CBaseMonster inherited; + + bool b_upper_state; + + + SVelocityParam m_fsVelocityWalkUpper; + SVelocityParam m_fsVelocityJumpGround; + SVelocityParam m_fsVelocityRunAttack; + +public: + CChimera (); + virtual ~CChimera (); + + virtual void Load (LPCSTR section); + virtual void reinit (); + virtual void UpdateCL (); + + virtual void SetTurnAnimation (bool turn_left); + virtual void CheckSpecParams (u32 spec_params); + virtual EAction CustomVelocityIndex2Action (u32 velocity_index); + virtual void TranslateActionToPathParams (); + virtual void HitEntityInJump (const CEntity *pEntity); + + IC void SetUpperState (bool state = true) {b_upper_state = state;} + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CChimera) +#undef script_type_list +#define script_type_list save_type_list(CChimera) diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_script.cpp b/src/xrGameLA/ai/monsters/chimera/chimera_script.cpp new file mode 100644 index 000000000..1f227b2ae --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "chimera.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CChimera::script_register(lua_State *L) +{ + module(L) + [ + class_("CChimera") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting.h new file mode 100644 index 000000000..e62781e0d --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting.h @@ -0,0 +1,24 @@ +#pragma once +#include "../state.h" + +template +class CStateChimeraHunting : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + enum { + eStateMoveToCover, + eStateComeOut + }; + +public: + CStateChimeraHunting (_Object *obj); + + virtual void reselect_state (); + virtual bool check_start_conditions (); + virtual bool check_completion (); + +}; + +#include "chimera_state_hunting_inline.h" diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_come_out.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_come_out.h new file mode 100644 index 000000000..95210829a --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_come_out.h @@ -0,0 +1,18 @@ +#pragma once +#include "../state.h" + +template +class CStateChimeraHuntingComeOut : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + +public: + CStateChimeraHuntingComeOut (_Object *obj); + + virtual void reselect_state (); + virtual bool check_start_conditions (); + virtual bool check_completion (); + +}; + +#include "chimera_state_hunting_come_out_inline.h" diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_come_out_inline.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_come_out_inline.h new file mode 100644 index 000000000..8ab356100 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_come_out_inline.h @@ -0,0 +1,37 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateChimeraHuntingMoveToCoverAbstract CStateChimeraHuntingMoveToCover<_Object> + +TEMPLATE_SPECIALIZATION +CStateChimeraHuntingMoveToCoverAbstract::CStateChimeraHuntingMoveToCover(_Object *obj) : inherited(obj) +{ +} + + +TEMPLATE_SPECIALIZATION +bool CStateChimeraHuntingMoveToCoverAbstract::check_start_conditions() +{ + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateChimeraHuntingMoveToCoverAbstract::check_completion() +{ + return false; +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraHuntingMoveToCoverAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) select_state(eStateMoveToCover); + else if (prev_substate == eStateMoveToCover) select_state(eStateComeOut); + else select_state(eStateMoveToCover); +} + + +#undef TEMPLATE_SPECIALIZATION +#undef CStateChimeraHuntingMoveToCoverAbstract diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_inline.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_inline.h new file mode 100644 index 000000000..6f13a2d93 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_inline.h @@ -0,0 +1,42 @@ +#pragma once + +#include "chimera_state_hunting_move_to_cover.h" +#include "chimera_state_hunting_come_out.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateChimeraHuntingAbstract CStateChimeraHunting<_Object> + +TEMPLATE_SPECIALIZATION +CStateChimeraHuntingAbstract::CStateChimeraHunting(_Object *obj) : inherited(obj) +{ + add_state(eStateMoveToCover, new CStateChimeraHuntingMoveToCover<_Object>(obj)); + add_state(eStateComeOut, new CStateChimeraHuntingComeOut<_Object>(obj)); +} + + +TEMPLATE_SPECIALIZATION +bool CStateChimeraHuntingAbstract::check_start_conditions() +{ + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateChimeraHuntingAbstract::check_completion() +{ + return false; +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraHuntingAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) select_state(eStateMoveToCover); + else if (prev_substate == eStateMoveToCover) select_state(eStateComeOut); + else select_state(eStateMoveToCover); +} + + +#undef TEMPLATE_SPECIALIZATION +#undef CStateChimeraHuntingAbstract diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_move_to_cover.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_move_to_cover.h new file mode 100644 index 000000000..c86291e94 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_move_to_cover.h @@ -0,0 +1,18 @@ +#pragma once +#include "../state.h" + +template +class CStateChimeraHuntingMoveToCover : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + +public: + CStateChimeraHuntingMoveToCover (_Object *obj); + + virtual void initialize (); + virtual void execute (); + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "chimera_state_hunting_move_to_cover_inline.h" diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_move_to_cover_inline.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_move_to_cover_inline.h new file mode 100644 index 000000000..48066f9d6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_hunting_move_to_cover_inline.h @@ -0,0 +1,36 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateChimeraHuntingMoveToCoverAbstract CStateChimeraHuntingMoveToCover<_Object> + +TEMPLATE_SPECIALIZATION +CStateChimeraHuntingMoveToCoverAbstract::CStateChimeraHuntingMoveToCover(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraHuntingMoveToCoverAbstract::initialize() +{ + inherited::initialize(); + + +} + +TEMPLATE_SPECIALIZATION +bool CStateChimeraHuntingMoveToCoverAbstract::check_completion() +{ + return false; +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraHuntingMoveToCoverAbstract::execute() +{ + +} + + +#undef TEMPLATE_SPECIALIZATION +#undef CStateChimeraHuntingMoveToCoverAbstract diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_manager.cpp b/src/xrGameLA/ai/monsters/chimera/chimera_state_manager.cpp new file mode 100644 index 000000000..507ef7a1d --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_manager.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "chimera.h" +#include "chimera_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "chimera_state_threaten.h" +#include "../states/state_test_state.h" + +CStateManagerChimera::CStateManagerChimera(CChimera *obj) : inherited(obj) +{ + add_state(eStateRest, new CStateMonsterRest(obj)); + add_state(eStatePanic, new CStateMonsterPanic(obj)); + add_state(eStateAttack, new CStateMonsterAttack(obj)); + add_state(eStateEat, new CStateMonsterEat(obj)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(obj)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(obj)); + add_state(eStateHitted, new CStateMonsterHitted(obj)); + add_state(eStateThreaten, new CStateChimeraThreaten(obj)); + add_state(eStateCustom, new CStateMonsterTestState(obj)); +} + +CStateManagerChimera::~CStateManagerChimera() +{ +} + +void CStateManagerChimera::execute() +{ + u32 state_id = u32(-1); + + const CEntityAlive* enemy = object->EnemyMan.get_enemy (); + + if (enemy) { + //if (check_state(eStateThreaten)) state_id = eStateThreaten; + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + + //state_id = eStateCustom; + + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} + diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_manager.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_manager.h new file mode 100644 index 000000000..8f7f511e5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_manager.h @@ -0,0 +1,15 @@ +#pragma once +#include "../monster_state_manager.h" + +class CChimera; + +class CStateManagerChimera : public CMonsterStateManager { + + typedef CMonsterStateManager inherited; + +public: + CStateManagerChimera (CChimera *obj); + virtual ~CStateManagerChimera (); + + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten.h new file mode 100644 index 000000000..716ee762e --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten.h @@ -0,0 +1,35 @@ +#pragma once +#include "../state.h" + +template +class CStateChimeraThreaten : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + enum { + eStateWalk = u32(0), + eStateFaceEnemy, + eStateThreaten, + eStateSteal + }; + + u32 m_last_time_threaten; + +public: + CStateChimeraThreaten (_Object *obj); + virtual ~CStateChimeraThreaten (); + + virtual void reinit (); + + virtual void initialize (); + + virtual void reselect_state (); + virtual void finalize (); + virtual void critical_finalize (); + virtual bool check_start_conditions (); + virtual bool check_completion (); + +}; + +#include "chimera_state_threaten_inline.h" diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_inline.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_inline.h new file mode 100644 index 000000000..591810c7b --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_inline.h @@ -0,0 +1,113 @@ +#pragma once + +#include "chimera_state_threaten_steal.h" +#include "chimera_state_threaten_walk.h" +#include "chimera_state_threaten_roar.h" + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateChimeraThreatenAbstract CStateChimeraThreaten<_Object> + +TEMPLATE_SPECIALIZATION +CStateChimeraThreatenAbstract::CStateChimeraThreaten(_Object *obj) : inherited(obj) +{ + add_state(eStateWalk, new CStateChimeraThreatenWalk<_Object>(obj)); + add_state(eStateThreaten, new CStateChimeraThreatenRoar<_Object>(obj)); + add_state(eStateSteal, new CStateChimeraThreatenSteal<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateChimeraThreatenAbstract::~CStateChimeraThreaten() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenAbstract::reinit() +{ + inherited::reinit (); + + m_last_time_threaten = 0; +} + + +#define MIN_DIST_TO_ENEMY 3.f +#define MORALE_THRESHOLD 0.8f +#define THREATEN_DELAY 10000 + +TEMPLATE_SPECIALIZATION +bool CStateChimeraThreatenAbstract::check_start_conditions() +{ + if (object->tfGetRelationType(object->EnemyMan.get_enemy()) == ALife::eRelationTypeWorstEnemy) return false; + if (object->Position().distance_to(object->EnemyMan.get_enemy_position()) < MIN_DIST_TO_ENEMY) return false; + if (object->HitMemory.is_hit()) return false; + if (object->hear_dangerous_sound) return false; + if (m_last_time_threaten + THREATEN_DELAY > Device.dwTimeGlobal) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateChimeraThreatenAbstract::check_completion() +{ + if (object->Position().distance_to(object->EnemyMan.get_enemy_position()) < MIN_DIST_TO_ENEMY) return true; + if (object->HitMemory.is_hit()) return true; + if (object->tfGetRelationType(object->EnemyMan.get_enemy()) == ALife::eRelationTypeWorstEnemy) return true; + + return false; +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenAbstract::initialize() +{ + inherited::initialize (); + object->SetUpperState (); +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) { + select_state(eStateThreaten); + return; + } + + if (prev_substate == eStateSteal) { + select_state(eStateThreaten); + return; + } + + if (prev_substate == eStateThreaten) { + if (get_state(eStateSteal)->check_start_conditions()) { + select_state(eStateSteal); + return; + } else if (get_state(eStateWalk)->check_start_conditions()) { + select_state(eStateWalk); + return; + } + } + + select_state(eStateThreaten); +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenAbstract::finalize() +{ + inherited::finalize (); + object->SetUpperState (false); + m_last_time_threaten = Device.dwTimeGlobal; +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenAbstract::critical_finalize() +{ + inherited::critical_finalize(); + object->SetUpperState (false); + m_last_time_threaten = Device.dwTimeGlobal; +} + + +#undef TEMPLATE_SPECIALIZATION +#undef CStateChimeraThreatenAbstract diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_roar.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_roar.h new file mode 100644 index 000000000..32a5b4867 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_roar.h @@ -0,0 +1,16 @@ +#pragma once +#include "../state.h" + +template +class CStateChimeraThreatenRoar : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + IC CStateChimeraThreatenRoar (_Object *obj) : inherited(obj){} + + virtual void initialize (); + virtual void execute (); + virtual bool check_completion (); +}; + +#include "chimera_state_threaten_roar_inline.h" diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_roar_inline.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_roar_inline.h new file mode 100644 index 000000000..180695ea6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_roar_inline.h @@ -0,0 +1,36 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStateChimeraThreatenRoarAbstract CStateChimeraThreatenRoar<_Object> + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenRoarAbstract::initialize() +{ + inherited::initialize (); + +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenRoarAbstract::execute() +{ + + object->set_action (ACT_STAND_IDLE); + object->anim().SetSpecParams (ASP_THREATEN); + object->set_state_sound (MonsterSound::eMonsterSoundThreaten); + object->dir().face_target (object->EnemyMan.get_enemy(), 1200); +} + +#define STATE_TIME_OUT 4000 + +TEMPLATE_SPECIALIZATION +bool CStateChimeraThreatenRoarAbstract::check_completion() +{ + if (time_state_started + STATE_TIME_OUT < Device.dwTimeGlobal) return true; + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateChimeraThreatenRoarAbstract + diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_steal.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_steal.h new file mode 100644 index 000000000..975e16605 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_steal.h @@ -0,0 +1,17 @@ +#pragma once +#include "../state.h" + +template +class CStateChimeraThreatenSteal : public CStateMonsterMoveToPointEx<_Object> { + typedef CStateMonsterMoveToPointEx<_Object> inherited; + +public: + IC CStateChimeraThreatenSteal (_Object *obj) : inherited(obj){} + virtual void initialize (); + virtual void finalize (); + virtual void execute (); + virtual bool check_completion (); + virtual bool check_start_conditions (); +}; + +#include "chimera_state_threaten_steal_inline.h" diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_steal_inline.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_steal_inline.h new file mode 100644 index 000000000..5a24e21b1 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_steal_inline.h @@ -0,0 +1,67 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStateChimeraThreatenStealAbstract CStateChimeraThreatenSteal<_Object> + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenStealAbstract::initialize() +{ + inherited::initialize(); + + object->SetUpperState (false); + + data.action.action = ACT_STEAL; + + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Calm; + + data.completion_dist = 2.f; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; +} + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenStealAbstract::finalize() +{ + inherited::finalize(); + object->SetUpperState (); +} + + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenStealAbstract::execute() +{ + data.point = object->EnemyMan.get_enemy_position (); + data.vertex = object->EnemyMan.get_enemy_vertex (); + data.time_to_rebuild = object->get_attack_rebuild_time (); + + inherited::execute(); +} + +#define MIN_DISTANCE_TO_ENEMY 8.f + +TEMPLATE_SPECIALIZATION +bool CStateChimeraThreatenStealAbstract::check_completion() +{ + if (inherited::check_completion()) return true; + + float dist_to_enemy = object->EnemyMan.get_enemy_position().distance_to(object->Position()); + if (dist_to_enemy < MIN_DISTANCE_TO_ENEMY) return true; + + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateChimeraThreatenStealAbstract::check_start_conditions() +{ + float dist_to_enemy = object->EnemyMan.get_enemy_position().distance_to(object->Position()); + if (dist_to_enemy > MIN_DISTANCE_TO_ENEMY) return true; + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateChimeraThreatenStealAbstract +#undef MIN_DISTANCE_TO_ENEMY diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_walk.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_walk.h new file mode 100644 index 000000000..382534f8b --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_walk.h @@ -0,0 +1,17 @@ +#pragma once +#include "../state.h" + +template +class CStateChimeraThreatenWalk : public CStateMonsterMoveToPointEx<_Object> { + typedef CStateMonsterMoveToPointEx<_Object> inherited; + +public: + IC CStateChimeraThreatenWalk (_Object *obj) : inherited(obj){} + virtual void initialize (); + virtual void execute (); + virtual bool check_completion (); + virtual bool check_start_conditions (); + +}; + +#include "chimera_state_threaten_walk_inline.h" diff --git a/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_walk_inline.h b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_walk_inline.h new file mode 100644 index 000000000..ec4eb0894 --- /dev/null +++ b/src/xrGameLA/ai/monsters/chimera/chimera_state_threaten_walk_inline.h @@ -0,0 +1,65 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStateChimeraThreatenWalkAbstract CStateChimeraThreatenWalk<_Object> + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenWalkAbstract::initialize() +{ + inherited::initialize(); + + object->SetUpperState (); + + data.point = object->EnemyMan.get_enemy_position (); + data.vertex = object->EnemyMan.get_enemy_vertex (); + + data.action.action = ACT_WALK_FWD; + + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Calm; + + data.completion_dist = 2.f; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.time_to_rebuild = 1500; +} + + +TEMPLATE_SPECIALIZATION +void CStateChimeraThreatenWalkAbstract::execute() +{ + data.point = object->EnemyMan.get_enemy_position (); + data.vertex = object->EnemyMan.get_enemy_vertex (); + + inherited::execute(); +} + +#define DISTANCE_TO_ENEMY 5.f + +TEMPLATE_SPECIALIZATION +bool CStateChimeraThreatenWalkAbstract::check_completion() +{ + if (inherited::check_completion()) return true; + + float dist_to_enemy = object->EnemyMan.get_enemy_position().distance_to(object->Position()); + if (dist_to_enemy < DISTANCE_TO_ENEMY) return true; + + return false; +} + +#define MAX_DISTANCE_TO_ENEMY 8.f + +TEMPLATE_SPECIALIZATION +bool CStateChimeraThreatenWalkAbstract::check_start_conditions() +{ + float dist_to_enemy = object->EnemyMan.get_enemy_position().distance_to(object->Position()); + if (dist_to_enemy < MAX_DISTANCE_TO_ENEMY) return true; + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateChimeraThreatenWalkAbstract + diff --git a/src/xrGameLA/ai/monsters/control_animation.cpp b/src/xrGameLA/ai/monsters/control_animation.cpp new file mode 100644 index 000000000..87286a8f3 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_animation.cpp @@ -0,0 +1,257 @@ +#include "stdafx.h" +#include "control_animation.h" +#include "BaseMonster/base_monster.h" +#include "control_manager.h" +#include "../../profiler.h" + +//#ifdef _DEBUG +//#include "control_animation_base.h" +//#endif + +void CControlAnimation::reinit() +{ + inherited::reinit (); + + m_skeleton_animated = smart_cast(m_object->Visual()); + + m_anim_events.clear (); + + m_global_animation_end = false; + m_legs_animation_end = false; + m_torso_animation_end = false; + + m_freeze = false; +} + +void CControlAnimation::reset_data() +{ + m_data.global.init (); + m_data.legs.init (); + m_data.torso.init (); + m_data.set_speed (-1.f); +} + +void CControlAnimation::update_frame() +{ + if (m_freeze) return; + + // move to schedule update + START_PROFILE("BaseMonster/Animation/Update Tracks"); + m_skeleton_animated->UpdateTracks (); + STOP_PROFILE; + + START_PROFILE("BaseMonster/Animation/Check callbacks"); + check_callbacks (); + STOP_PROFILE; + + START_PROFILE("BaseMonster/Animation/Play"); + play (); + STOP_PROFILE; + + START_PROFILE("BaseMonster/Animation/Check Events"); + check_events (m_data.global); + check_events (m_data.torso); + check_events (m_data.legs); + STOP_PROFILE; +} + +static void global_animation_end_callback(CBlend* B) +{ + CControlAnimation *controller = (CControlAnimation *)B->CallbackParam; + controller->m_global_animation_end = true; +} +static void legs_animation_end_callback(CBlend* B) +{ + CControlAnimation *controller = (CControlAnimation *)B->CallbackParam; + controller->m_legs_animation_end = true; +} +static void torso_animation_end_callback(CBlend* B) +{ + CControlAnimation *controller = (CControlAnimation *)B->CallbackParam; + controller->m_torso_animation_end = true; +} + +void CControlAnimation::play() +{ + if (!m_data.global.actual) { + play_part (m_data.global, global_animation_end_callback); + if (m_data.global.blend) m_saved_global_speed = m_data.global.blend->speed; + } + + if (!m_data.legs.actual) + play_part(m_data.legs, legs_animation_end_callback); + if (!m_data.torso.actual) + play_part(m_data.torso, torso_animation_end_callback); + + // speed only for global + if (m_data.global.blend) { + if (m_data.get_speed() > 0) + { + m_data.global.blend->speed = m_data.get_speed(); // TODO: make factor + }else + { + m_data.global.blend->speed = m_saved_global_speed; + } + } +} + +void CControlAnimation::play_part(SAnimationPart &part, PlayCallback callback) +{ + VERIFY (part.motion.valid()); + + u16 bone_or_part = m_skeleton_animated->LL_GetMotionDef(part.motion)->bone_or_part; + if (bone_or_part == u16(-1)) bone_or_part = m_skeleton_animated->LL_PartID("default"); + + // initialize synchronization of prev and current animation + float pos = -1.f; + if (part.blend && !part.blend->stop_at_end) + pos = fmod(part.blend->timeCurrent,part.blend->timeTotal)/part.blend->timeTotal; +#ifdef DEBUG + //CKinematicsAnimated * K = m_object->Visual()->dcast_PKinematicsAnimated(); + //Msg ("%6d Playing animation : %s , %s , Object %s",Device.dwTimeGlobal, K->LL_MotionDefName_dbg(part.motion).first,K->LL_MotionDefName_dbg(part.motion).second, *(m_object->cName())); +#endif + + part.blend = m_skeleton_animated->LL_PlayCycle(bone_or_part,part.motion, TRUE, callback, this); + + +/////////////////////////////////////////////////////////////////////////////// +//#ifdef _DEBUG +// Msg("Monster[%s] Time[%u] Anim[%s]",*(m_object->cName()), Device.dwTimeGlobal,*(m_object->anim().GetAnimTranslation(part.motion))); +//#endif +/////////////////////////////////////////////////////////////////////////////// + + // synchronize prev and current animations + if ((pos > 0) && part.blend && !part.blend->stop_at_end) + { + part.blend->timeCurrent = part.blend->timeTotal*pos; + } + + part.time_started = Device.dwTimeGlobal; + part.actual = true; + + m_man->notify (ControlCom::eventAnimationStart, 0); + + if ((part.motion != m_data.torso.motion) && part.blend) + m_object->CStepManager::on_animation_start(part.motion, part.blend); + + + ANIMATION_EVENT_MAP_IT it = m_anim_events.find(part.motion); + if (it != m_anim_events.end()) { + for (ANIMATION_EVENT_VEC_IT event_it = it->second.begin(); event_it != it->second.end(); ++event_it) { + event_it->handled = false; + } + } +} + + +void CControlAnimation::add_anim_event(MotionID motion, float time_perc, u32 id) +{ + // if there is already event with exact timing - return + ANIMATION_EVENT_MAP_IT it = m_anim_events.find(motion); + if (it != m_anim_events.end()) { + ANIMATION_EVENT_VEC &anim_vec = it->second; + + for (ANIMATION_EVENT_VEC_IT I = anim_vec.begin(); I != anim_vec.end(); ++I) { + if (fsimilar(I->time_perc, time_perc)) return; + } + } + + SAnimationEvent event; + event.time_perc = time_perc; + event.event_id = id; + + m_anim_events[motion].push_back(event); +} + +void CControlAnimation::check_events(SAnimationPart &part) +{ + if (part.motion.valid() && part.actual && part.blend) { + ANIMATION_EVENT_MAP_IT it = m_anim_events.find(part.motion); + if (it != m_anim_events.end()) { + + float cur_perc = float(Device.dwTimeGlobal - part.time_started) / ((part.blend->timeTotal / part.blend->speed) * 1000); + + for (ANIMATION_EVENT_VEC_IT event_it = it->second.begin(); event_it != it->second.end(); ++event_it) { + SAnimationEvent &event = *event_it; + if (!event.handled && (event.time_perc < cur_perc)) { + event.handled = true; + + // gen event + SAnimationSignalEventData anim_event(part.motion, event.time_perc, event.event_id); + m_man->notify (ControlCom::eventAnimationSignal, &anim_event); + } + } + } + } +} + +void CControlAnimation::check_callbacks() +{ + if (m_global_animation_end) { + m_man->notify (ControlCom::eventAnimationEnd, 0); + m_global_animation_end = false; + } + + if (m_legs_animation_end) { + m_man->notify (ControlCom::eventLegsAnimationEnd, 0); + m_legs_animation_end = false; + } + + if (m_torso_animation_end) { + m_man->notify (ControlCom::eventTorsoAnimationEnd, 0); + m_torso_animation_end = false; + } + +} + +void CControlAnimation::restart(SAnimationPart &part, PlayCallback callback) +{ + VERIFY (part.motion.valid()); + VERIFY (part.blend); + + u16 bone_or_part = m_skeleton_animated->LL_GetMotionDef(part.motion)->bone_or_part; + if (bone_or_part == u16(-1)) bone_or_part = m_skeleton_animated->LL_PartID("default"); + + //save + float time_saved = part.blend->timeCurrent; + + // start + part.blend = m_skeleton_animated->LL_PlayCycle(bone_or_part,part.motion, TRUE, callback, this); + + // restore + part.blend->timeCurrent = time_saved; +} + +void CControlAnimation::restart() +{ + m_skeleton_animated = smart_cast(m_object->Visual()); + + if (m_data.global.blend) restart(m_data.global,global_animation_end_callback); + if (m_data.legs.blend) restart(m_data.legs,legs_animation_end_callback); + if (m_data.torso.blend) restart(m_data.torso,torso_animation_end_callback); +} +void CControlAnimation::freeze() +{ + if (m_freeze) return; + m_freeze = true; + + if (m_data.global.blend) {m_saved_global_speed = m_data.global.blend->speed; m_data.global.blend->speed = 0.f;} + if (m_data.legs.blend) {m_saved_legs_speed = m_data.legs.blend->speed; m_data.legs.blend->speed = 0.f;} + if (m_data.torso.blend) {m_saved_torso_speed = m_data.torso.blend->speed; m_data.torso.blend->speed = 0.f;} +} + +void CControlAnimation::unfreeze() +{ + if (!m_freeze) return; + m_freeze = false; + + if (m_data.global.blend) { + m_data.global.blend->speed = m_saved_global_speed; + } + if (m_data.legs.blend) { + m_data.legs.blend->speed = m_saved_legs_speed; + } + if (m_data.torso.blend) { + m_data.torso.blend->speed = m_saved_torso_speed; + } +} diff --git a/src/xrGameLA/ai/monsters/control_animation.h b/src/xrGameLA/ai/monsters/control_animation.h new file mode 100644 index 000000000..498070e45 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_animation.h @@ -0,0 +1,109 @@ +#pragma once + +#include "control_combase.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../../Include/xrRender/RenderVisual.h" + +struct SAnimationPart { + MotionID motion; + CBlend *blend; + bool actual; + u32 time_started; + + void init () { + motion.invalidate (); + blend = 0; + actual = true; + time_started = 0; + } +}; + +struct SControlAnimationData : public ControlCom::IComData { + float _speed; + IC void set_speed (float v) {_speed=v; VERIFY2(_abs(_speed)<1000,"SControlAnimationData::set_speed too big");}; + IC float get_speed () {return _speed;}; + SAnimationPart global; + SAnimationPart legs; + SAnimationPart torso; +}; + +struct SAnimationSignalEventData : public ControlCom::IEventData { + MotionID motion; + float time_perc; + u32 event_id; + IC SAnimationSignalEventData(MotionID m, float perc, u32 id) : time_perc(perc), event_id(id), motion(m) {} +}; + + +class CControlAnimation : public CControl_ComPure { + typedef CControl_ComPure inherited; + + IKinematicsAnimated *m_skeleton_animated; + + // animation events + struct SAnimationEvent{ + float time_perc; + u32 event_id; + bool handled; + }; + + DEFINE_VECTOR (SAnimationEvent, ANIMATION_EVENT_VEC, ANIMATION_EVENT_VEC_IT); + DEFINE_MAP (MotionID, ANIMATION_EVENT_VEC, ANIMATION_EVENT_MAP, ANIMATION_EVENT_MAP_IT); + ANIMATION_EVENT_MAP m_anim_events; + + bool m_freeze; + float m_saved_global_speed; + float m_saved_legs_speed; + float m_saved_torso_speed; + +public: + + bool m_global_animation_end; + bool m_legs_animation_end; + bool m_torso_animation_end; + +public: + virtual void reinit (); + virtual void update_frame (); + virtual void reset_data (); + + void add_anim_event (MotionID, float, u32); + + CBlend *current_blend () {return m_data.global.blend;} + + void restart (); + + void freeze (); + void unfreeze (); + + // Services + IC float motion_time (MotionID motion_id, IRenderVisual *visual); + + +private: + void play (); + void play_part (SAnimationPart &part, PlayCallback callback); + void check_events (SAnimationPart &part); + void check_callbacks (); + + void restart (SAnimationPart &part, PlayCallback callback); + +public: + enum EAnimationEventType { + eAnimationHit = u32(0), + eAnimationCustom + }; +}; + +// get motion time, when just MotionID available +IC float CControlAnimation::motion_time(MotionID motion_id, IRenderVisual *visual) +{ + IKinematicsAnimated *skeleton_animated = smart_cast(visual); + VERIFY (skeleton_animated); + CMotionDef *motion_def = skeleton_animated->LL_GetMotionDef(motion_id); + VERIFY (motion_def); + CMotion* motion = skeleton_animated->LL_GetRootMotion(motion_id); + VERIFY (motion); + return (motion->GetLength() / motion_def->Speed()); +} + diff --git a/src/xrGameLA/ai/monsters/control_animation_base.cpp b/src/xrGameLA/ai/monsters/control_animation_base.cpp new file mode 100644 index 000000000..e2e83ee5f --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_animation_base.cpp @@ -0,0 +1,605 @@ +#include "stdafx.h" +#include "control_animation_base.h" +#include "control_direction_base.h" +#include "control_movement_base.h" +#include "BaseMonster/base_monster.h" +#include "../../PHMovementControl.h" +#include "anim_triple.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../detail_path_manager.h" +#include "monster_velocity_space.h" +#include "monster_event_manager.h" +#include "control_jump.h" +#include "../../sound_player.h" + +// DEBUG purpose only +char *dbg_action_name_table[] = { + "ACT_STAND_IDLE", + "ACT_SIT_IDLE", + "ACT_LIE_IDLE", + "ACT_WALK_FWD", + "ACT_WALK_BKWD", + "ACT_RUN", + "ACT_EAT", + "ACT_SLEEP", + "ACT_REST", + "ACT_DRAG", + "ACT_ATTACK", + "ACT_STEAL", + "ACT_LOOK_AROUND", + "ACT_JUMP" +}; + +CControlAnimationBase::CControlAnimationBase() +{ + init_anim_storage (); +} + +CControlAnimationBase::~CControlAnimationBase() +{ + free_anim_storage (); +} + +void CControlAnimationBase::reinit() +{ + inherited::reinit (); + + m_tAction = ACT_STAND_IDLE; + spec_params = 0; + + fx_time_last_play = 0; + + accel_init (); + + aa_time_last_attack = 0; + + // обновить количество анимаций + m_anim_motion_map.clear (); + UpdateAnimCount (); + + // инициализация информации о текущей анимации + m_cur_anim.motion = eAnimStandIdle; + m_cur_anim.index = 0; + m_cur_anim.time_started = 0; + m_cur_anim.speed._set_current (-1.f); + m_cur_anim.speed._set_target (-1.f); + m_cur_anim.blend = 0; + m_cur_anim.speed_change_vel = 1.f; + + prev_motion = cur_anim_info().motion; + + m_prev_character_velocity = 0.01f; + + spec_anim = eAnimUndefined; + + // test + m_man->capture (this, ControlCom::eControlAnimation); + m_man->subscribe (this, ControlCom::eventAnimationSignal); + + AA_reload (pSettings->r_string(*(m_object->cNameSect()), "attack_params")); + + braking_mode = false; + + m_state_attack = false; +} + +void CControlAnimationBase::on_start_control(ControlCom::EControlType type) +{ + switch (type) { + case ControlCom::eControlAnimation: + m_man->subscribe (this, ControlCom::eventAnimationEnd); + m_state_attack = false; + select_animation (); + break; + } +} + +void CControlAnimationBase::on_stop_control (ControlCom::EControlType type) +{ + switch (type) { + case ControlCom::eControlAnimation: + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); + m_state_attack = false; + break; + } +} + +void CControlAnimationBase::on_event(ControlCom::EEventType type, ControlCom::IEventData *data) +{ + switch (type) { + case ControlCom::eventAnimationEnd: select_animation(true); m_state_attack = false; break; + case ControlCom::eventAnimationSignal: + { + SAnimationSignalEventData *event_data = (SAnimationSignalEventData *)data; + if (event_data->event_id == CControlAnimation::eAnimationHit) check_hit(event_data->motion,event_data->time_perc); break; + } + } +} + +void CControlAnimationBase::select_animation(bool anim_end) +{ + // start new animation + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + if (!ctrl_data) return; + + if (m_state_attack && !anim_end) return; + + if (cur_anim_info().motion == eAnimAttack) m_state_attack = true; + else m_state_attack = false; + + + // перекрыть все определения и установть анимацию + m_object->ForceFinalAnimation(); + + // получить элемент SAnimItem, соответствующий текущей анимации + SAnimItem *anim_it = m_anim_storage[cur_anim_info().motion]; + VERIFY(anim_it); + + // определить необходимый индекс + int index; + if (-1 != anim_it->spec_id) index = anim_it->spec_id; + else { + VERIFY(anim_it->count != 0); + index = ::Random.randI(anim_it->count); + } + + // установить анимацию + string128 s1,s2; + MotionID cur_anim = smart_cast(m_object->Visual())->ID_Cycle_Safe(strconcat(sizeof(s2),s2,*anim_it->target_name,itoa(index,s1,10))); + + // Setup Com + ctrl_data->global.motion = cur_anim; + ctrl_data->global.actual = false; + ctrl_data->set_speed (m_cur_anim.speed._get_target()); + + // Заполнить текущую анимацию + string64 st,tmp; + strconcat (sizeof(st),st,*anim_it->target_name,itoa(index,tmp,10)); + // xr_sprintf (st, "%s%d", *anim_it->second.target_name, index); + m_cur_anim.name = st; + m_cur_anim.index = u8(index); + m_cur_anim.time_started = Device.dwTimeGlobal; + m_cur_anim.speed._set_current (1.f); + m_cur_anim.speed._set_target (-1.f); +} + +// проверить существует ли переход из анимации from в to +bool CControlAnimationBase::CheckTransition(EMotionAnim from, EMotionAnim to) +{ + if (!m_man->check_start_conditions(ControlCom::eControlSequencer)) return false; + + // поиск соответствующего перехода + bool b_activated = false; + EMotionAnim cur_from = from; + EPState state_from = GetState(cur_from); + EPState state_to = GetState(to); + + TRANSITION_ANIM_VECTOR_IT I = m_tTransitions.begin(); + bool bVectEmpty = m_tTransitions.empty(); + + while (!bVectEmpty) { // вход в цикл, если вектор переходов не пустой + + bool from_is_good = ((I->from.state_used) ? (I->from.state == state_from) : (I->from.anim == cur_from)); + bool target_is_good = ((I->target.state_used) ? (I->target.state == state_to) : (I->target.anim == to)); + + if (from_is_good && target_is_good) { + + //if (I->skip_if_aggressive && m_object->m_bAggressive) return; + + // переход годится + if (!b_activated) { + m_object->com_man().seq_init(); + } + + m_object->com_man().seq_add(get_motion_id(I->anim_transition)); + b_activated = true; + + + if (I->chain) { + cur_from = I->anim_transition; + state_from = GetState(cur_from); + I = m_tTransitions.begin(); // начать сначала + continue; + } else break; + } + if (m_tTransitions.end() == ++I) break; + } + + if (b_activated) { + m_object->com_man().seq_switch (); + return true; + } + + return false; +} + +void CControlAnimationBase::CheckReplacedAnim() +{ + for (REPLACED_ANIM_IT it=m_tReplacedAnims.begin(); m_tReplacedAnims.end()!=it ;++it) + if ((cur_anim_info().motion == it->cur_anim) && (*(it->flag) == true)) { + cur_anim_info().motion = it->new_anim; + return; + } +} + +SAAParam &CControlAnimationBase::AA_GetParams(LPCSTR anim_name) +{ + // искать текущую анимацию в AA_VECTOR + MotionID motion = smart_cast(m_object->Visual())->LL_MotionID(anim_name); + + for (AA_VECTOR_IT it = m_attack_anims.begin(); it != m_attack_anims.end(); it++) { + if (it->motion == motion) return (*it); + } + + VERIFY3(FALSE, "Error! No animation in AA_VECTOR! Animation = ", anim_name); + return (*(m_attack_anims.begin())); +} + +SAAParam &CControlAnimationBase::AA_GetParams(MotionID motion, float time_perc) +{ + // искать текущую анимацию в AA_VECTOR + for (AA_VECTOR_IT it = m_attack_anims.begin(); it != m_attack_anims.end(); it++) { + if ((it->motion == motion) && (it->time == time_perc)) return (*it); + } + + VERIFY2(FALSE, "Error! No animation in AA_VECTOR! Animation = [UNKNOWN]"); + return (*(m_attack_anims.begin())); +} + + +EPState CControlAnimationBase::GetState (EMotionAnim a) +{ + // найти анимацию + SAnimItem *item_it = m_anim_storage[a]; + VERIFY(item_it); + + return item_it->pos_state; +} + +#define FX_CAN_PLAY_MIN_INTERVAL 50 + +void CControlAnimationBase::FX_Play(EHitSide side, float amount) +{ + if (fx_time_last_play + FX_CAN_PLAY_MIN_INTERVAL > m_object->m_dwCurrentTime) return; + + SAnimItem *anim_it = m_anim_storage[cur_anim_info().motion]; + VERIFY(anim_it); + + clamp(amount,0.f,1.f); + + shared_str *p_str = 0; + switch (side) { + case eSideFront: p_str = &anim_it->fxs.front; break; + case eSideBack: p_str = &anim_it->fxs.back; break; + case eSideLeft: p_str = &anim_it->fxs.left; break; + case eSideRight: p_str = &anim_it->fxs.right; break; + } + + if (p_str && p_str->size()) smart_cast(m_object->Visual())->PlayFX(*(*p_str), amount); + + fx_time_last_play = m_object->m_dwCurrentTime; +} + +float CControlAnimationBase::GetAnimSpeed(EMotionAnim anim) +{ + SAnimItem *anim_it = m_anim_storage[anim]; + VERIFY(anim_it); + + CMotionDef *def = get_motion_def(anim_it, 0); + + return (def->Dequantize(def->speed)); +} + + +bool CControlAnimationBase::IsTurningCurAnim() +{ + SAnimItem *item_it = m_anim_storage[cur_anim_info().motion]; + VERIFY(item_it); + + if (!fis_zero(item_it->velocity.velocity.angular_real)) return true; + return false; +} + +bool CControlAnimationBase::IsStandCurAnim() +{ + SAnimItem *item_it = m_anim_storage[cur_anim_info().motion]; + VERIFY(item_it); + + if (fis_zero(item_it->velocity.velocity.linear)) return true; + return false; +} + + +EAction CControlAnimationBase::VelocityIndex2Action(u32 velocity_index) +{ + switch (velocity_index) { + case MonsterMovement::eVelocityParameterStand: return ACT_STAND_IDLE; + case MonsterMovement::eVelocityParameterWalkNormal: return ACT_WALK_FWD; + case MonsterMovement::eVelocityParameterRunNormal: return ACT_RUN; + case MonsterMovement::eVelocityParameterWalkDamaged: return ACT_WALK_FWD; + case MonsterMovement::eVelocityParameterRunDamaged: return ACT_RUN; + case MonsterMovement::eVelocityParameterSteal: return ACT_STEAL; + case MonsterMovement::eVelocityParameterDrag: return ACT_DRAG; + case MonsterMovement::eVelocityParameterInvisible: return ACT_RUN; + } + + return m_object->CustomVelocityIndex2Action(velocity_index); +} + +EAction CControlAnimationBase::GetActionFromPath() +{ + EAction action; + + u32 cur_point_velocity_index = m_object->movement().detail().path()[m_object->movement().detail().curr_travel_point_index()].velocity; + action = VelocityIndex2Action(cur_point_velocity_index); + + u32 next_point_velocity_index = u32(-1); + if (m_object->movement().detail().path().size() > m_object->movement().detail().curr_travel_point_index() + 1) + next_point_velocity_index = m_object->movement().detail().path()[m_object->movement().detail().curr_travel_point_index() + 1].velocity; + + if ((cur_point_velocity_index == MonsterMovement::eVelocityParameterStand) && (next_point_velocity_index != u32(-1))) { + if (!m_object->control().direction().is_turning(deg(1))) + action = VelocityIndex2Action(next_point_velocity_index); + } + + return action; +} + + + +////////////////////////////////////////////////////////////////////////// +// Debug + +LPCSTR CControlAnimationBase::GetAnimationName(EMotionAnim anim) +{ + SAnimItem *item_it = m_anim_storage[anim]; + VERIFY(item_it); + + return *item_it->target_name; +} + +LPCSTR CControlAnimationBase::GetActionName(EAction action) +{ + return dbg_action_name_table[action]; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +void CControlAnimationBase::ValidateAnimation() +{ + SAnimItem *item_it = m_anim_storage[cur_anim_info().motion]; + + bool is_moving_anim = !fis_zero(item_it->velocity.velocity.linear); + bool is_moving_on_path = m_object->control().path_builder().is_moving_on_path(); + + if (is_moving_on_path && is_moving_anim) { + m_object->dir().use_path_direction(cur_anim_info().motion == eAnimDragCorpse); + return; + } + + if (!is_moving_on_path && is_moving_anim) { + cur_anim_info().motion = eAnimStandIdle; + m_object->move().stop (); + return; + } + + if (is_moving_on_path && !is_moving_anim) { + m_object->move().stop (); + return; + } + + if (!m_object->control().direction().is_turning() && ((cur_anim_info().motion == eAnimStandTurnLeft) || (cur_anim_info().motion == eAnimStandTurnRight))) { + cur_anim_info().motion = eAnimStandIdle; + return; + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +void CControlAnimationBase::UpdateAnimCount() +{ + IKinematicsAnimated *skel = smart_cast(m_object->Visual()); + + for (ANIM_ITEM_VECTOR_IT it = m_anim_storage.begin(); it != m_anim_storage.end(); it++) { + if (!(*it)) continue; + + // проверить, были ли уже загружены данные + if ((*it)->count != 0) return; + + string128 s, s_temp; + u8 count = 0; + + for (int i=0; ; ++i) { + strconcat (sizeof(s_temp),s_temp, *((*it)->target_name),itoa(i,s,10)); + LPCSTR name = s_temp; + MotionID id = skel->ID_Cycle_Safe(name); + + if (id.valid()) { + count++; + AddAnimTranslation(id,name); + } + else break; + } + + if (count != 0) (*it)->count = count; + else { + xr_sprintf(s, "Error! No animation: %s for monster %s", *((*it)->target_name), *m_object->cName()); + R_ASSERT2(count != 0, s); + } + } +} + +CMotionDef *CControlAnimationBase::get_motion_def(SAnimItem *it, u32 index) +{ + string128 s1,s2; + IKinematicsAnimated *skeleton_animated = smart_cast(m_object->Visual()); + const MotionID &motion_id = skeleton_animated->ID_Cycle_Safe(strconcat(sizeof(s2),s2,*it->target_name,itoa(index,s1,10))); + return (skeleton_animated->LL_GetMotionDef(motion_id)); +} + +void CControlAnimationBase::AddAnimTranslation(const MotionID &motion, LPCSTR str) +{ + m_anim_motion_map.insert(mk_pair(motion, str)); +} +shared_str CControlAnimationBase::GetAnimTranslation(const MotionID &motion) +{ + shared_str ret_value; + + ANIM_TO_MOTION_MAP_IT anim_it = m_anim_motion_map.find(motion); + if (anim_it != m_anim_motion_map.end()) ret_value = anim_it->second; + + return ret_value; +} + +MotionID CControlAnimationBase::get_motion_id(EMotionAnim a, u32 index) +{ + // получить элемент SAnimItem, соответствующий текущей анимации + SAnimItem *anim_it = m_anim_storage[a]; + VERIFY(anim_it); + + // определить необходимый индекс + if (index == u32(-1)) { + if (-1 != anim_it->spec_id) index = anim_it->spec_id; + else { + VERIFY(anim_it->count != 0); + index = ::Random.randI(anim_it->count); + } + } + + string128 s1,s2; + return (smart_cast(m_object->Visual())->ID_Cycle_Safe(strconcat(sizeof(s2),s2,*anim_it->target_name,itoa(index,s1,10)))); +} + +void CControlAnimationBase::stop_now() +{ + m_object->move().stop (); +// m_object->path().disable_path (); +} + +void CControlAnimationBase::set_animation_speed() +{ + // Setup Com + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + if (!ctrl_data) return; + ctrl_data->set_speed (m_cur_anim.speed._get_target() ); +} + +void CControlAnimationBase::check_hit(MotionID motion, float time_perc) +{ + if (!m_object->EnemyMan.get_enemy()) return; + const CEntityAlive *enemy = m_object->EnemyMan.get_enemy(); + + SAAParam ¶ms = AA_GetParams(motion,time_perc); + + m_object->sound().play (MonsterSound::eMonsterSoundAttackHit); + + bool should_hit = true; + // определить дистанцию до врага + Fvector d; + d.sub(enemy->Position(),m_object->Position()); + if (d.magnitude() > params.dist) should_hit = false; + + // проверка на Field-Of-Hit + float my_h,my_p; + float h,p; + + m_object->Direction().getHP(my_h,my_p); + d.getHP(h,p); + + float from = angle_normalize(my_h + params.foh.from_yaw); + float to = angle_normalize(my_h + params.foh.to_yaw); + + if (!is_angle_between(h, from, to)) should_hit = false; + + from = angle_normalize(my_p + params.foh.from_pitch); + to = angle_normalize(my_p + params.foh.to_pitch); + + if (!is_angle_between(p, from, to)) should_hit = false; + + if (!m_object->EnemyMan.see_enemy_now()) should_hit = false; + + if (should_hit) m_object->HitEntity(enemy, params.hit_power, params.impulse, params.impulse_dir); + + m_object->MeleeChecker.on_hit_attempt(should_hit); +} + +void parse_anim_params(LPCSTR val, SAAParam &anim) +{ + string16 cur_elem; + + _GetItem (val,0,cur_elem); anim.time = float(atof(cur_elem)); + _GetItem (val,1,cur_elem); anim.hit_power = float(atof(cur_elem)); + _GetItem (val,2,cur_elem); anim.impulse = float(atof(cur_elem)); + _GetItem (val,3,cur_elem); anim.impulse_dir.x = float(atof(cur_elem)); + _GetItem (val,4,cur_elem); anim.impulse_dir.y = float(atof(cur_elem)); + _GetItem (val,5,cur_elem); anim.impulse_dir.z = float(atof(cur_elem)); + _GetItem (val,6,cur_elem); anim.foh.from_yaw = float(atof(cur_elem)); + _GetItem (val,7,cur_elem); anim.foh.to_yaw = float(atof(cur_elem)); + _GetItem (val,8,cur_elem); anim.foh.from_pitch = float(atof(cur_elem)); + _GetItem (val,9,cur_elem); anim.foh.to_pitch = float(atof(cur_elem)); + _GetItem (val,10,cur_elem); anim.dist = float(atof(cur_elem)); + + anim.impulse_dir.normalize(); + + float clamp_val = PI_DIV_2 - EPS_L; + clamp(anim.foh.from_yaw, -clamp_val, clamp_val); + clamp(anim.foh.to_yaw, -clamp_val, clamp_val); + clamp(anim.foh.from_pitch, -clamp_val, clamp_val); + clamp(anim.foh.to_pitch, -clamp_val, clamp_val); + +} + +void CControlAnimationBase::AA_reload(LPCSTR section) +{ + if (!pSettings->section_exist(section)) return; + + m_attack_anims.clear(); + + SAAParam anim; + LPCSTR anim_name,val; + + IKinematicsAnimated *skel_animated = smart_cast(m_object->Visual()); + + for (u32 i=0; pSettings->r_line(section,i,&anim_name,&val); ++i) { + + + anim.motion = skel_animated->LL_MotionID(anim_name); + if (!anim.motion.valid()) continue; + + // check if it is compound (if there is one item, mean it as a section) + if (_GetItemCount(val) == 1) { + LPCSTR compound_section = val; + LPCSTR unused_line_name; + + for (u32 k=0; pSettings->r_line(compound_section,k,&unused_line_name,&val); ++k) { + parse_anim_params (val, anim); + + m_attack_anims.push_back(anim); + m_man->animation().add_anim_event(anim.motion, anim.time, CControlAnimation::eAnimationHit); + } + } else { + parse_anim_params(val, anim); + + m_attack_anims.push_back(anim); + m_man->animation().add_anim_event(anim.motion, anim.time, CControlAnimation::eAnimationHit); + } + } +} + +void CControlAnimationBase::init_anim_storage() +{ + m_anim_storage.reserve(eAnimCount); + for (u32 i=0; ir_float(section, "Accel_Calm"); + m_accel.aggressive = pSettings->r_float(section, "Accel_Aggressive"); +} + +void CControlAnimationBase::accel_activate(EAccelType type) +{ + m_accel.active = true; + m_accel.type = type; + + m_accel.enable_braking = true; +} + +float CControlAnimationBase::accel_get(EAccelValue val) +{ + if (!accel_active(val)) return flt_max; + + switch(m_accel.type) { + case eAT_Calm: return m_accel.calm; + case eAT_Aggressive: return m_accel.aggressive; + default: return m_accel.calm; + } +} + +// ----------------------------------------------------------------------------------------- + +void CControlAnimationBase::accel_chain_add(EMotionAnim anim1, EMotionAnim anim2) +{ + SEQ_VECTOR v_temp; + v_temp.push_back(anim1); + v_temp.push_back(anim2); + + m_accel.chain.push_back(v_temp); +} + +bool CControlAnimationBase::accel_chain_get(float cur_speed, EMotionAnim target_anim, EMotionAnim &new_anim, float &a_speed) +{ + VERIFY2(_abs(cur_speed)<1000, "CControlAnimationBase cur_speed too big"); + + VELOCITY_CHAIN_VEC_IT B = m_accel.chain.begin(), I; + VELOCITY_CHAIN_VEC_IT E = m_accel.chain.end(); + + // пройти по всем Chain-векторам + for (I = B; I != E; I++) { + SEQ_VECTOR_IT IT_B = I->begin(), IT; + SEQ_VECTOR_IT IT_E = I->end(); + SEQ_VECTOR_IT best_anim = IT_E; + SVelocityParam *best_param = 0; + + bool found = false; + + // Пройти по текущему вектору + for (IT = IT_B; IT != IT_E; IT++) { + + SAnimItem *item_it = m_anim_storage[*IT]; + VERIFY(item_it); + + SVelocityParam *param = &item_it->velocity; + float from = param->velocity.linear * param->min_factor; + float to = param->velocity.linear * param->max_factor; + + if ( ((from <= cur_speed+EPS_L) && (cur_speed <= to + EPS_L)) || + ((cur_speed < from) && (IT == I->begin())) || + ((cur_speed + EPS_L >= to) && (IT+1 == I->end())) ) { + best_anim = IT; + best_param = &item_it->velocity; + } + + if ((*IT) == target_anim) found = true; + if (found && best_param) break; + } + + if (!found) continue; + + R_ASSERT2(best_param,"probably incompatible speed ranges"); + // calc anim_speed + new_anim = *best_anim; + float tmp = GetAnimSpeed(new_anim); + VERIFY2(_abs(tmp)<1000, "CControlAnimationBase GetAnimSpeed returns too big speed"); + a_speed = tmp * cur_speed / best_param->velocity.linear; + VERIFY2(_abs(a_speed)<1000, "CControlAnimationBase a_speed too big"); + return true; + } + return false; +} + +bool CControlAnimationBase::accel_chain_test() +{ + string256 error_msg; + + // пройти по всем Chain-векторам + for (VELOCITY_CHAIN_VEC_IT I = m_accel.chain.begin(); I != m_accel.chain.end(); I++) { + + VERIFY2(I->size() >= 2, error_msg); + + SAnimItem *anim_from = m_anim_storage[*(I->begin())]; + SAnimItem *anim_to; + VERIFY(anim_from); + + // Пройти по текущему вектору + for (SEQ_VECTOR_IT IT = I->begin() + 1; IT != I->end(); IT++) { + anim_to = m_anim_storage[*IT]; + + float from = anim_from->velocity.velocity.linear * anim_from->velocity.max_factor; + float to = anim_to->velocity.velocity.linear * anim_to->velocity.min_factor; + + xr_sprintf(error_msg,"Incompatible speed ranges. Monster[%s] From animation [%s] To animation [%s]",*m_object->cName(),*anim_from->target_name, *anim_to->target_name); + VERIFY2(to < from, error_msg); + + anim_from = anim_to; + } + } + + return true; +} + +bool CControlAnimationBase::accel_check_braking(float before_interval, float nominal_speed) +{ + if (!m_man->path_builder().is_moving_on_path()) return (braking_mode = false); + if (!accel_active(eAV_Braking)) return (braking_mode = false); + + float acceleration = accel_get(eAV_Braking); + float braking_dist = (nominal_speed * ((braking_mode) ? nominal_speed : m_man->movement().velocity_current())) / (2 * acceleration); + + braking_dist += before_interval; + if (m_man->path_builder().is_path_end(braking_dist)) return (braking_mode = true); + + // проверить точки пути, где необходимо остановиться + float dist = 0.f; // дистанция до найденной точки + for (u32 i=m_man->path_builder().detail().curr_travel_point_index()+1; i < m_man->path_builder().detail().path().size(); i++) { + dist += m_man->path_builder().detail().path()[i].position.distance_to(m_man->path_builder().detail().path()[i-1].position); + + if (m_man->path_builder().detail().path()[i].velocity == MonsterMovement::eVelocityParameterStand) { + if (dist < braking_dist) return (braking_mode = true); + else break; + } + } + + return (braking_mode = false); +} + + diff --git a/src/xrGameLA/ai/monsters/control_animation_base_load.cpp b/src/xrGameLA/ai/monsters/control_animation_base_load.cpp new file mode 100644 index 000000000..b860d521a --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_animation_base_load.cpp @@ -0,0 +1,143 @@ +#include "stdafx.h" +#include "control_animation_base.h" + +void CControlAnimationBase::AddAnim(EMotionAnim ma, LPCSTR tn, int s_id, SVelocityParam *vel, EPState p_s, LPCSTR fx_front, LPCSTR fx_back, LPCSTR fx_left, LPCSTR fx_right) +{ + SAnimItem *new_item = new SAnimItem(); + + new_item->target_name = tn; + new_item->spec_id = s_id; + new_item->velocity = *vel; + new_item->pos_state = p_s; + + new_item->fxs.front = fx_front; + new_item->fxs.back = fx_back; + new_item->fxs.left = fx_left; + new_item->fxs.right = fx_right; + + new_item->count = 0; + + m_anim_storage[ma] = new_item; +} + +void CControlAnimationBase::AddAnim(EMotionAnim ma, LPCSTR tn, int s_id, SVelocityParam *vel, EPState p_s) +{ + SAnimItem *new_item = new SAnimItem(); + + new_item->target_name = tn; + new_item->spec_id = s_id; + new_item->velocity = *vel; + new_item->pos_state = p_s; + + new_item->count = 0; + + m_anim_storage[ma] = new_item; +} + +void CControlAnimationBase::AddTransition(EMotionAnim from, EMotionAnim to, EMotionAnim trans, bool chain, bool skip_aggressive) +{ + STransition new_item; + + new_item.from.state_used = false; + new_item.from.anim = from; + + new_item.target.state_used = false; + new_item.target.anim = to; + + new_item.anim_transition = trans; + new_item.chain = chain; + + new_item.skip_if_aggressive = skip_aggressive; + + m_tTransitions.push_back(new_item); +} + + +void CControlAnimationBase::AddTransition(EMotionAnim from, EPState to, EMotionAnim trans, bool chain, bool skip_aggressive) +{ + STransition new_item; + + new_item.from.state_used = false; + new_item.from.anim = from; + + new_item.target.state_used = true; + new_item.target.state = to; + + new_item.anim_transition = trans; + new_item.chain = chain; + new_item.skip_if_aggressive = skip_aggressive; + + m_tTransitions.push_back(new_item); +} + + +void CControlAnimationBase::AddTransition(EPState from, EMotionAnim to, EMotionAnim trans, bool chain, bool skip_aggressive) +{ + STransition new_item; + + new_item.from.state_used = true; + new_item.from.state = from; + + new_item.target.state_used = false; + new_item.target.anim = to; + + + new_item.anim_transition = trans; + new_item.chain = chain; + new_item.skip_if_aggressive = skip_aggressive; + + m_tTransitions.push_back(new_item); +} + + +void CControlAnimationBase::AddTransition(EPState from, EPState to, EMotionAnim trans, bool chain, bool skip_aggressive) +{ + STransition new_item; + + new_item.from.state_used = true; + new_item.from.state = from; + + new_item.target.state_used = true; + new_item.target.state = to; + + new_item.anim_transition = trans; + new_item.chain = chain; + new_item.skip_if_aggressive = skip_aggressive; + + m_tTransitions.push_back(new_item); +} + +void CControlAnimationBase::LinkAction(EAction act, EMotionAnim pmt_motion, EMotionAnim pmt_left, EMotionAnim pmt_right, float pmt_angle) +{ + + SMotionItem new_item; + + new_item.anim = pmt_motion; + new_item.is_turn_params = true; + new_item.turn.anim_left = pmt_left; + new_item.turn.anim_right = pmt_right; + new_item.turn.min_angle = pmt_angle; + + m_tMotions.insert (mk_pair(act, new_item)); +} + +void CControlAnimationBase::LinkAction(EAction act, EMotionAnim pmt_motion) +{ + SMotionItem new_item; + + new_item.anim = pmt_motion; + new_item.is_turn_params = false; + + m_tMotions.insert (mk_pair(act, new_item)); +} + +void CControlAnimationBase::AddReplacedAnim(bool *b_flag, EMotionAnim pmt_cur_anim, EMotionAnim pmt_new_anim) +{ + SReplacedAnim ra; + + ra.flag = b_flag; + ra.cur_anim = pmt_cur_anim; + ra.new_anim = pmt_new_anim; + + m_tReplacedAnims.push_back(ra); +} diff --git a/src/xrGameLA/ai/monsters/control_animation_base_update.cpp b/src/xrGameLA/ai/monsters/control_animation_base_update.cpp new file mode 100644 index 000000000..5098d3efa --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_animation_base_update.cpp @@ -0,0 +1,303 @@ +#include "stdafx.h" +#include "control_animation_base.h" +#include "control_direction_base.h" +#include "control_movement_base.h" +#include "BaseMonster/base_monster.h" +#include "../../CharacterPhysicsSupport.h" +#include "../../PHMovementControl.h" +#include "../../detail_path_manager.h" +#include "monster_velocity_space.h" +#include "control_path_builder_base.h" + +// DEBUG purpose only +char *dbg_anim_name_table[] = { + "eAnimStandIdle", + "eAnimStandTurnLeft", + "eAnimStandTurnRight", + + "eAnimSitIdle", + "eAnimLieIdle", + + "eAnimSitToSleep", + "eAnimLieToSleep", + "eAnimStandSitDown", + "eAnimStandLieDown", + "eAnimLieStandUp", + "eAnimSitStandUp", + "eAnimStandLieDownEat", + "eAnimSitLieDown", + "eAnimLieSitUp", + "eAnimSleepStandUp", + + "eAnimWalkFwd", + "eAnimWalkBkwd", + "eAnimWalkTurnLeft", + "eAnimWalkTurnRight", + + "eAnimRun", + "eAnimRunTurnLeft", + "eAnimRunTurnRight", + "eAnimFastTurn", + + "eAnimAttack", + "eAnimAttackFromBack", + "eAnimAttackRun", + + "eAnimEat", + "eAnimSleep", + "eAnimDie", + + "eAnimDragCorpse", + "eAnimCheckCorpse", + "eAnimScared", + "eAnimAttackJump", + + "eAnimLookAround", + + "eAnimJump", + "eAnimSteal", + + "eAnimJumpStart", + "eAnimJumpGlide", + "eAnimJumpFinish", + + "eAnimJumpLeft", + "eAnimJumpRight", + + "eAnimStandDamaged", + "eAnimWalkDamaged", + "eAnimRunDamaged", + + "eAnimSniff", + "eAnimHowling", + "eAnimThreaten", + + "eAnimMiscAction_00", + "eAnimMiscAction_01", + + "eAnimUpperStandIdle", + "eAnimUpperStandTurnLeft", + "eAnimUpperStandTurnRight", + + "eAnimStandToUpperStand", + "eAnimUppperStandToStand", + + "eAnimUpperWalkFwd", + "eAnimUpperThreaten", + "eAnimUpperAttack", + + "eAnimAttackPsi", + + "eAnimTeleRaise", + "eAnimTeleFire", + "eAnimGraviPrepare", + "eAnimGraviFire", + + "eAnimCount", + "eAnimUndefined" +}; + + + +////////////////////////////////////////////////////////////////////////// +// m_tAction processing +////////////////////////////////////////////////////////////////////////// + +void CControlAnimationBase::update_frame() +{ + update(); + + // raise event on velocity bounce + CheckVelocityBounce (); +} + +void CControlAnimationBase::update() +{ + if (m_state_attack) return; + + // Установка Yaw + if (m_object->control().path_builder().is_moving_on_path() && m_object->path().enabled()) m_object->dir().use_path_direction( ((spec_params & ASP_MOVE_BKWD) == ASP_MOVE_BKWD) ); + + SelectAnimation (); + SelectVelocities (); + + // применить + if (prev_motion != cur_anim_info().motion) { + prev_motion = cur_anim_info().motion; + select_animation(); + } +} + + +////////////////////////////////////////////////////////////////////////// +// SelectAnimation +// In: path, target_yaw, m_tAction +// Out: установить анимацию в cur_anim_info().motion +void CControlAnimationBase::SelectAnimation() +{ + EAction action = m_tAction; + if (m_object->control().path_builder().is_moving_on_path() && m_object->path().enabled()) action = GetActionFromPath(); + + cur_anim_info().motion = m_tMotions[action].anim; + + m_object->CheckSpecParams (spec_params); + if (prev_motion != cur_anim_info().motion) + if (CheckTransition(prev_motion, cur_anim_info().motion)) return; + + CheckReplacedAnim (); + SetTurnAnimation (); +} + +#define MOVE_TURN_ANGLE deg(30) + +void CControlAnimationBase::SetTurnAnimation() +{ + float yaw_current, yaw_target; + m_man->direction().get_heading(yaw_current, yaw_target); + float delta_yaw = angle_difference(yaw_target, yaw_current); + + bool turn_left = true; + if (from_right(yaw_target, yaw_current)) turn_left = false; + + EPState anim_state = GetState(cur_anim_info().motion); + if (IsStandCurAnim() && (anim_state == PS_STAND) && (!fis_zero(delta_yaw))) { + m_object->SetTurnAnimation(turn_left); + return; + } + + if (m_object->control().path_builder().is_moving_on_path() && m_object->path().enabled() && (delta_yaw > MOVE_TURN_ANGLE)) { + m_object->SetTurnAnimation(turn_left); + return; + } +} + +////////////////////////////////////////////////////////////////////////// +// SelectVelocities +// In: path, target_yaw, анимация +// Out: установить linear и angular velocities, +// по скорости движения выбрать финальную анимацию из Velocity_Chain +// установить скорость анимации в соответствие с физ скоростью +void CControlAnimationBase::SelectVelocities() +{ + // получить скорости движения по пути + bool b_moving = m_object->control().path_builder().is_moving_on_path(); + SMotionVel path_vel; path_vel.set(0.f,0.f); + SMotionVel anim_vel; anim_vel.set(0.f,0.f); + + if (b_moving) { + + u32 cur_point_velocity_index = m_object->movement().detail().path()[m_object->movement().detail().curr_travel_point_index()].velocity; + + u32 next_point_velocity_index = u32(-1); + if (m_object->movement().detail().path().size() > m_object->movement().detail().curr_travel_point_index() + 1) + next_point_velocity_index = m_object->movement().detail().path()[m_object->movement().detail().curr_travel_point_index() + 1].velocity; + + // если сейчас стоит на месте и есть след точка (т.е. должен быть в движении), + // то реализовать поворот на месте, а дальше форсировать скорость со следующей точки + if ((cur_point_velocity_index == MonsterMovement::eVelocityParameterStand) && (next_point_velocity_index != u32(-1))) { + if (!m_object->control().direction().is_turning()) + cur_point_velocity_index = next_point_velocity_index; + } + + const CDetailPathManager::STravelParams ¤t_velocity = m_object->movement().detail().velocity(cur_point_velocity_index); + path_vel.set(_abs(current_velocity.linear_velocity), current_velocity.real_angular_velocity); + } + + SAnimItem *item_it = m_anim_storage[cur_anim_info().motion]; + VERIFY(item_it); + + // получить скорости движения по анимации + anim_vel.set(item_it->velocity.velocity.linear, item_it->velocity.velocity.angular_real); + + // // проверить на совпадение + // R_ASSERT(fsimilar(path_vel.linear, anim_vel.linear)); + // R_ASSERT(fsimilar(path_vel.angular, anim_vel.angular)); + + // установка линейной скорости + if (m_object->state_invisible) { + // если невидимый, то установить скорость из пути + m_object->move().set_velocity(_abs(path_vel.linear)); + } else { + + if (fis_zero(_abs(anim_vel.linear))) stop_now(); + else { + // - проверить на возможность торможения + if (!accel_check_braking(-2.f, _abs(anim_vel.linear))) { + m_object->move().set_velocity(_abs(anim_vel.linear)); + //no braking mode + } else { + m_object->move().stop_accel(); + //braking mode + } + } + } + + // финальная корректировка скорости анимации по физической скорости + + + if (!m_object->state_invisible && !fis_zero(anim_vel.linear)) { + + EMotionAnim new_anim; + float a_speed; + + if (accel_chain_get(m_man->movement().real_velocity(), cur_anim_info().motion, new_anim, a_speed)) { + cur_anim_info().motion = new_anim; + + if (a_speed < 0.5f) a_speed += 0.5f; + + cur_anim_info().speed._set_target (a_speed); + } else + cur_anim_info().speed._set_target (-1.f); + } else + cur_anim_info().speed._set_target (-1.f); + + set_animation_speed (); + + // установка угловой скорости + if (m_object->state_invisible) + m_object->dir().set_heading_speed(path_vel.angular); + else { + item_it = m_anim_storage[cur_anim_info().motion]; + VERIFY(item_it); + + // Melee? + if (m_tAction == ACT_ATTACK) { + float vel = item_it->velocity.velocity.angular_real; + m_object->dir().set_heading_speed(vel * m_object->m_melee_rotation_factor); // todo: make as an external factor + } else + m_object->dir().set_heading_speed(item_it->velocity.velocity.angular_real); + } +} + +#define VELOCITY_BOUNCE_THRESHOLD 1.5f + +void CControlAnimationBase::CheckVelocityBounce() +{ + Fvector temp_vec; + m_object->character_physics_support()->movement()->GetCharacterVelocity(temp_vec); + float prev_speed = m_prev_character_velocity; + float cur_speed = temp_vec.magnitude(); + + // prepare + if (fis_zero(prev_speed)) prev_speed = 0.01f; + if (fis_zero(cur_speed)) cur_speed = 0.01f; + + float ratio = ((prev_speed > cur_speed) ? (prev_speed / cur_speed) : (cur_speed / prev_speed)); + + if (ratio > VELOCITY_BOUNCE_THRESHOLD) { + if (prev_speed > cur_speed) ratio = -ratio; + + // prepare event + SEventVelocityBounce event(ratio); + m_man->notify (ControlCom::eventVelocityBounce, &event); + + } + m_prev_character_velocity = cur_speed; +} + + +void CControlAnimationBase::ScheduledInit() +{ + spec_params = 0; + accel_deactivate (); +} diff --git a/src/xrGameLA/ai/monsters/control_com_defs.h b/src/xrGameLA/ai/monsters/control_com_defs.h new file mode 100644 index 000000000..cb4997d04 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_com_defs.h @@ -0,0 +1,74 @@ +#pragma once + +namespace ControlCom { + enum EControlType { + // 1st level + eControlMovement = u32(0), // linear velocity + eControlPath, // path builder + eControlDir, // model direction + eControlAnimation, // animation manager +// eControlSound, // sound manager + + // 2nd level + eControlSequencer, // capture: anim + eControlTripleAnimation, // capture: anim + + //// 3rd level + eControlJump, // capture: path, movement, triple_anim disable : fsm, dir + eControlRotationJump, // capture: path, movement, sequencer, dir + eControlRunAttack, // capture: path, movement, sequencer + eControlThreaten, + eControlMeleeJump, // capture: path, movement, sequencer, dir + + eControlAnimationBase, + eControlMovementBase, + eControlPathBase, + eControlDirBase, + + eControlCustom, + + eComCustom1, + eComCriticalWound, + + eControllersCount, + + eControlInvalid = u32(-1) + }; + + struct IComData {}; + struct IEventData {}; + + enum EEventType { + eventAnimationStart = u32(0), + eventAnimationEnd, + eventLegsAnimationEnd, + eventTorsoAnimationEnd, + eventAnimationSignal, + eventSoundStart, + eventSoundEnd, + eventParticlesStart, + eventParticlesEnd, + eventStep, + eventTAChange, + eventVelocityBounce, + eventSequenceEnd, + eventRotationEnd, + eventTravelPointChange, + eventPathBuilt, + eventJumpEnd, + eventRotationJumpEnd, + eventMeleeJumpEnd, + eventRunAttackEnd, + eventPathUpdated, + eventPathSelectorFailed, + eventThreatenEnd, + eventCriticalWoundEnd + }; + + enum ECaptureType { + eCapturePath = u32(1) << 0, + eCaptureMovement = u32(1) << 1, + eCaptureDir = u32(1) << 2, + }; +}; + diff --git a/src/xrGameLA/ai/monsters/control_combase.h b/src/xrGameLA/ai/monsters/control_combase.h new file mode 100644 index 000000000..66f21259c --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_combase.h @@ -0,0 +1,150 @@ +#pragma once +#include "control_com_defs.h" + +class CControl_Manager; +class CBaseMonster; + +class CControl_ComControlled; +class CControl_ComControlling; + +////////////////////////////////////////////////////////////////////////// +// Base +class CControl_Com { +public: + CControl_Com () {m_inited = false;} + virtual ~CControl_Com () {} + // common routines + void init_external (CControl_Manager *cm, CBaseMonster *obj) {m_man = cm; m_object = obj;} + virtual void load (LPCSTR section) {} + virtual void reinit () {m_active = false; m_inited = true;} + virtual void reload (LPCSTR section) {} + + // update + virtual void update_schedule () {} + virtual void update_frame () {} + + virtual CControl_ComControlled *ced () {return 0;} + virtual CControl_ComControlling *cing () {return 0;} + + void set_active (bool val = true) {m_active = val; val ? activate() : deactivate();} + bool is_active () {return m_active;} + bool is_inited () {return m_inited;} + + virtual bool check_start_conditions () {return true;} + +protected: + virtual void activate () {} + virtual void deactivate () {} + +protected: + CControl_Manager *m_man; + CBaseMonster *m_object; + +private: + bool m_active; + bool m_inited; +}; + +////////////////////////////////////////////////////////////////////////// +// Controlled with data +class CControl_ComControlled { +public: + virtual void reinit () {m_locked = false; m_capturer = 0; reset_data();} + virtual void reset_data (){} + + virtual ControlCom::IComData *data () {return 0;} + + // init/deinit current work + virtual void on_capture () {reset_data();} + virtual void on_release () {} + + bool is_locked () {return m_locked;} + void set_locked (bool val = true) {m_locked = val;} + CControl_Com *capturer () {return m_capturer;} + void set_capturer(CControl_Com *com){m_capturer = com;} +private: + CControl_Com *m_capturer; + bool m_locked; +}; + +////////////////////////////////////////////////////////////////////////// +// Controlling +class CControl_ComControlling { +public: + virtual ~CControl_ComControlling () {} + virtual void reinit () {} + + // initialize/finalize controlling com + virtual void on_start_control (ControlCom::EControlType type) {} + virtual void on_stop_control (ControlCom::EControlType type) {} + + // event handling + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*) {} + +protected: + DEFINE_VECTOR (CControl_Com*, CONTROLLERS_VECTOR, CONTROLLERS_VECTOR_IT); + CONTROLLERS_VECTOR m_controlled; +}; + +////////////////////////////////////////////////////////////////////////// +// Data Storage +template +class CControl_ComControlledStorage : public CControl_ComControlled { +public: + virtual ControlCom::IComData *data () {return &m_data;} +protected: + T m_data; +}; + +////////////////////////////////////////////////////////////////////////// +// Pure +template +class CControl_ComPure : + public CControl_Com, + public CControl_ComControlledStorage +{ +public: + virtual CControl_ComControlled *ced () {return this;} + virtual void reinit () + { + CControl_Com::reinit (); + CControl_ComControlledStorage::reinit (); + set_active (); + } +}; +////////////////////////////////////////////////////////////////////////// +// Base +class CControl_ComBase : + public CControl_Com, + public CControl_ComControlling +{ +public: + virtual CControl_ComControlling *cing () {return this;} + virtual void reinit () + { + CControl_Com::reinit (); + CControl_ComControlling::reinit (); + set_active (); + } +}; +////////////////////////////////////////////////////////////////////////// +// Custom +template +class CControl_ComCustom : + public CControl_Com, + public CControl_ComControlledStorage, + public CControl_ComControlling +{ +public: + virtual CControl_ComControlled *ced () {return this;} + virtual CControl_ComControlling *cing () {return this;} + virtual void reinit () + { + CControl_Com::reinit (); + CControl_ComControlledStorage::reinit (); + CControl_ComControlling::reinit (); + } + + virtual bool check_start_conditions () {return false;} +}; + diff --git a/src/xrGameLA/ai/monsters/control_critical_wound.cpp b/src/xrGameLA/ai/monsters/control_critical_wound.cpp new file mode 100644 index 000000000..7b72f1e97 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_critical_wound.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "control_critical_wound.h" +#include "BaseMonster/base_monster.h" +#include "control_animation_base.h" +#include "control_direction_base.h" +#include "control_movement_base.h" + +void CControlCriticalWound::activate() +{ + m_man->capture_pure (this); + m_man->subscribe (this, ControlCom::eventAnimationEnd); + + m_man->path_stop (this); + m_man->move_stop (this); + m_man->dir_stop (this); + + IKinematicsAnimated *skel = smart_cast(m_object->Visual()); + + SControlAnimationData *ctrl_anim = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_anim); + ctrl_anim->global.motion = skel->ID_Cycle_Safe(m_data.animation); + ctrl_anim->global.actual = false; +} + + + +void CControlCriticalWound::on_release() +{ + m_man->release_pure (this); + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); + + m_object->critical_wounded_state_stop(); + +} + +bool CControlCriticalWound::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured_pure()) return false; + + return true; +} + +void CControlCriticalWound::on_event(ControlCom::EEventType type, ControlCom::IEventData *dat) +{ + switch (type) { + case ControlCom::eventAnimationEnd: + m_man->notify (ControlCom::eventCriticalWoundEnd, 0); + break; + } +} diff --git a/src/xrGameLA/ai/monsters/control_critical_wound.h b/src/xrGameLA/ai/monsters/control_critical_wound.h new file mode 100644 index 000000000..8526265cf --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_critical_wound.h @@ -0,0 +1,17 @@ +#pragma once +#include "control_combase.h" + +struct SControlCriticalWoundData : public ControlCom::IComData { + LPCSTR animation; +}; + +class CControlCriticalWound : public CControl_ComCustom { + typedef CControl_ComCustom inherited; + +public: + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + virtual void activate (); + virtual void on_release (); + virtual bool check_start_conditions (); +}; + diff --git a/src/xrGameLA/ai/monsters/control_direction.cpp b/src/xrGameLA/ai/monsters/control_direction.cpp new file mode 100644 index 000000000..7e424e623 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_direction.cpp @@ -0,0 +1,194 @@ +#include "stdafx.h" +#include "control_direction.h" +#include "BaseMonster/base_monster.h" +#include "control_manager.h" + +#include "../../detail_path_manager.h" +#include "../../level_graph.h" +#include "../../ai_space.h" +#include "../../ai_object_location.h" + +#include "../../detail_path_manager_space.h" + +void CControlDirection::reinit() +{ + inherited::reinit (); + + m_heading.init (); + m_heading.current_angle = m_man->path_builder().m_body.current.yaw; + + m_pitch.init (); + m_pitch.current_angle = m_man->path_builder().m_body.current.pitch; + + m_data.heading.target_angle = m_man->path_builder().m_body.target.yaw; + m_data.pitch.target_angle = m_man->path_builder().m_body.target.pitch; + m_data.linear_dependency = true; +} + +////////////////////////////////////////////////////////////////////////// +// Update +////////////////////////////////////////////////////////////////////////// + +void CControlDirection::update_frame() +{ + pitch_correction (); + + SRotationEventData event_data; + event_data.angle = 0; + + bool heading_similar = false; + bool pitch_similar = false; + + // difference + float diff = angle_difference(m_pitch.current_angle, m_data.pitch.target_angle) * 4.0f; + clamp(diff, PI_DIV_6, 5 * PI_DIV_6); + + m_data.pitch.target_speed = m_pitch.current_speed = diff; + + // поправка угловой скорости в соответствии с текущей и таргетовой линейной скоростями + // heading speed correction + if (!fis_zero(m_man->movement().velocity_current()) && !fis_zero(m_man->movement().velocity_target()) && m_data.linear_dependency) + m_heading.current_speed = m_data.heading.target_speed * m_man->movement().velocity_current() / (m_man->movement().velocity_target() + EPS_L); + else + velocity_lerp (m_heading.current_speed, m_data.heading.target_speed, m_heading.current_acc, m_object->client_update_fdelta()); + + m_heading.current_angle = angle_normalize(m_heading.current_angle); + m_data.heading.target_angle = angle_normalize(m_data.heading.target_angle); + + if (fsimilar(m_heading.current_angle, m_data.heading.target_angle)) heading_similar = true; + angle_lerp(m_heading.current_angle, m_data.heading.target_angle, m_heading.current_speed, m_object->client_update_fdelta()); + if (!heading_similar && fsimilar(m_heading.current_angle, m_data.heading.target_angle)) { + event_data.angle |= SRotationEventData::eHeading; + } + + // update pitch + velocity_lerp (m_pitch.current_speed, m_data.pitch.target_speed, m_pitch.current_acc, m_object->client_update_fdelta()); + + m_pitch.current_angle = angle_normalize_signed (m_pitch.current_angle); + m_data.pitch.target_angle = angle_normalize_signed (m_data.pitch.target_angle); + + if (fsimilar(m_pitch.current_angle, m_data.pitch.target_angle)) pitch_similar = true; + angle_lerp (m_pitch.current_angle, m_data.pitch.target_angle, m_pitch.current_speed, m_object->client_update_fdelta()); + if (!pitch_similar && fsimilar(m_pitch.current_angle, m_data.pitch.target_angle)) { + event_data.angle |= SRotationEventData::ePitch; + } + + // set + m_man->path_builder().m_body.speed = m_heading.current_speed; + m_man->path_builder().m_body.current.yaw = m_heading.current_angle; + m_man->path_builder().m_body.target.yaw = m_heading.current_angle; + m_man->path_builder().m_body.current.pitch = m_pitch.current_angle; + m_man->path_builder().m_body.target.pitch = m_pitch.current_angle; + + // save object position + Fvector P = m_object->Position(); + // set angles + if(!m_object->animation_movement_controlled()) + m_object->XFORM().setHPB (-m_man->path_builder().m_body.current.yaw,-m_man->path_builder().m_body.current.pitch,0); + // restore object position + m_object->Position() = P; + + + // if there is an event + if (event_data.angle) m_man->notify(ControlCom::eventRotationEnd, &event_data); +} + +void CControlDirection::pitch_correction() +{ + if (!m_object->ability_pitch_correction()) return; + + // extended feature to pitch by path (wall climbing) + // distance between two travel point must be more than 1.f + if (m_object->control().path_builder().is_moving_on_path() && + (m_object->movement().detail().path().size() > m_object->movement().detail().curr_travel_point_index() + 1)) { + + const DetailPathManager::STravelPathPoint cur_point = m_object->movement().detail().path()[m_object->movement().detail().curr_travel_point_index()]; + const DetailPathManager::STravelPathPoint next_point = m_object->movement().detail().path()[m_object->movement().detail().curr_travel_point_index()+1]; + + if (cur_point.position.distance_to_sqr(next_point.position) > 1) { + // получаем искомый вектор направления + Fvector target_dir; + target_dir.sub (next_point.position,cur_point.position); + m_data.pitch.target_angle = -target_dir.getP(); + return; + } + } + + // get current plane + u32 node = m_object->ai_location().level_vertex_id(); + Fplane P; + pvDecompress (P.n,ai().level_graph().vertex(node)->plane()); + P.d = -P.n.dotproduct(ai().level_graph().vertex_position(node)); + + Fvector position_on_plane; + P.project (position_on_plane,m_object->Position()); + + // находим проекцию точки, лежащей на векторе текущего направления + Fvector dir_point, proj_point; + dir_point.mad (position_on_plane, m_object->Direction(), 1.f); + P.project (proj_point,dir_point); + + // получаем искомый вектор направления + Fvector target_dir; + target_dir.sub (proj_point,position_on_plane); + + float yaw,pitch; + target_dir.getHP (yaw,pitch); + + m_data.pitch.target_angle = -pitch; +} + +////////////////////////////////////////////////////////////////////////// +// Services +////////////////////////////////////////////////////////////////////////// + +bool CControlDirection::is_face_target(const Fvector &position, float eps_angle) +{ + float target_h = Fvector().sub(position, m_object->Position()).getH(); + float my_h = m_object->Direction().getH(); + + if (angle_difference(target_h,my_h) > eps_angle) return false; + + return true; +} + +bool CControlDirection::is_face_target(const CObject *obj, float eps_angle) +{ + return is_face_target(obj->Position(), eps_angle); +} + +bool CControlDirection::is_from_right(const Fvector &position) +{ + float yaw, pitch; + Fvector().sub (position, m_object->Position()).getHP(yaw,pitch); + yaw *= -1; + + return (from_right(yaw,m_heading.current_angle)); +} +bool CControlDirection::is_from_right(float yaw) +{ + return (from_right(yaw,m_heading.current_angle)); +} + +bool CControlDirection::is_turning(float eps_angle) +{ + return (!fsimilar(m_heading.current_angle,m_data.heading.target_angle, eps_angle)); +} +void CControlDirection::get_heading(float ¤t, float &target) +{ + current = m_heading.current_angle; + target = m_data.heading.target_angle; +} + +float CControlDirection::get_heading_current() +{ + return m_heading.current_angle; +} + +float CControlDirection::angle_to_target(const Fvector &position) +{ + float angle = Fvector().sub(position, m_object->Position()).getH(); + angle *= -1; + + return (angle_normalize(angle)); +} diff --git a/src/xrGameLA/ai/monsters/control_direction.h b/src/xrGameLA/ai/monsters/control_direction.h new file mode 100644 index 000000000..1770da405 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_direction.h @@ -0,0 +1,58 @@ +#pragma once + +#include "control_combase.h" + +struct SControlDirectionData : public ControlCom::IComData { + struct { + float target_angle; + float target_speed; + } heading, pitch; + + bool linear_dependency; +}; + +struct SRotationEventData : public ControlCom::IEventData { + + enum RotType { + eHeading = u32(1) << 0, + ePitch = u32(1) << 1, + }; + u8 angle; +}; + +class CControlDirection : public CControl_ComPure { + typedef CControl_ComPure inherited; + + struct { + float current_angle; + float current_speed; // current speed + float current_acc; + + void init () { + current_angle = 0; + current_speed = 0; + current_acc = flt_max; + } + } m_heading, m_pitch; + +public: + + virtual void reinit (); + virtual void update_frame (); + + // services + bool is_face_target (const Fvector &position, float eps_angle); + bool is_face_target (const CObject *obj, float eps_angle); + + bool is_from_right (const Fvector &position); + bool is_from_right (float yaw); + + bool is_turning (float eps_angle = EPS); + + void get_heading (float ¤t, float &target); + float get_heading_current (); + + float angle_to_target (const Fvector &position); +private: + void pitch_correction (); +}; diff --git a/src/xrGameLA/ai/monsters/control_direction_base.cpp b/src/xrGameLA/ai/monsters/control_direction_base.cpp new file mode 100644 index 000000000..695844871 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_direction_base.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" +#include "control_direction_base.h" +#include "basemonster/base_monster.h" +#include "../../detail_path_manager.h" + +void CControlDirectionBase::reinit() +{ + inherited::reinit (); + + m_delay = 0; + m_time_last_faced = 0; + + m_heading.init (); + m_heading.target = m_man->path_builder().m_body.target.yaw; + + m_pitch.init (); + m_pitch.target = m_man->path_builder().m_body.target.pitch; + + m_man->capture (this, ControlCom::eControlDir); +} + +void CControlDirectionBase::face_target(const Fvector &position, u32 delay, float add_yaw) +{ + if (m_time_last_faced + delay > Device.dwTimeGlobal) return; + + m_delay = delay; + + float yaw, pitch; + Fvector dir; + + dir.sub (position, m_object->Position()); + dir.getHP (yaw,pitch); + yaw *= -1; + + yaw += (m_man->direction().is_from_right(position)) ? add_yaw : -add_yaw; + yaw = angle_normalize(yaw); + + m_heading.target = yaw; + + m_time_last_faced = Device.dwTimeGlobal; +} +void CControlDirectionBase::face_target(const CObject *obj, u32 delay, float add_yaw) +{ + face_target (obj->Position(), delay, add_yaw); +} + +void CControlDirectionBase::use_path_direction(bool reversed) +{ + float yaw,pitch; + m_man->path_builder().detail().direction().getHP (yaw,pitch); + + if (fsimilar(yaw,0.f,EPS_S)) return; + + m_heading.target = angle_normalize((reversed) ? (-yaw + PI) : (-yaw)); +} + +void CControlDirectionBase::set_heading_speed(float value, bool force) +{ + m_heading.speed_target = value; +} + +void CControlDirectionBase::set_heading(float value, bool force) +{ + m_heading.target = value; +} +////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////// +void CControlDirectionBase::update_frame() +{ + SControlDirectionData *ctrl_data = (SControlDirectionData *)m_man->data(this, ControlCom::eControlDir); + if (!ctrl_data) return; + + ctrl_data->heading.target_angle = m_heading.target; + ctrl_data->heading.target_speed = m_heading.speed_target; +} diff --git a/src/xrGameLA/ai/monsters/control_direction_base.h b/src/xrGameLA/ai/monsters/control_direction_base.h new file mode 100644 index 000000000..d006b9629 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_direction_base.h @@ -0,0 +1,38 @@ +#pragma once +#include "control_combase.h" + + +class CControlDirectionBase : public CControl_ComBase { + typedef CControl_ComBase inherited; + + u32 m_time_last_faced; + u32 m_delay; + +public: + struct SAxis { + float target; + float acceleration; + float speed_target; + + void init () { + target = 0; + speed_target = 0; + acceleration = flt_max; + } + } m_heading, m_pitch; + +public: + virtual void reinit (); + virtual void update_frame (); + + void use_path_direction (bool reversed = false); + + virtual void face_target (const Fvector &position, u32 delay = 0, float add_yaw = 0.f); + virtual void face_target (const CObject *obj, u32 delay = 0, float add_yaw = 0.f); + IC void set_delay (u32 delay) {m_delay = delay;} + + void set_heading_speed (float value, bool force = false); + void set_heading (float value, bool force = false); + + IC const SAxis &heading () {return m_heading;} +}; diff --git a/src/xrGameLA/ai/monsters/control_jump.cpp b/src/xrGameLA/ai/monsters/control_jump.cpp new file mode 100644 index 000000000..02ede3ae3 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_jump.cpp @@ -0,0 +1,528 @@ +#include "stdafx.h" +#include "control_jump.h" +#include "BaseMonster/base_monster.h" +#include "control_manager.h" +#include "../../PHMovementControl.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../detail_path_manager.h" +#include "../../level.h" +#include "control_animation_base.h" +#include "control_direction_base.h" +#include "control_movement_base.h" +#include "control_path_builder_base.h" +#include "monster_velocity_space.h" +#include "../../ai_space.h" +#include "../../level_graph.h" +#include "../../ai_object_location.h" +#include "../../CharacterPhysicsSupport.h" +#ifdef DEBUG +#include "../../level_debug.h" +#endif +#include "../../../../xrCore/_vector3d_ext.h" + +void CControlJump::reinit() +{ + inherited::reinit (); + + m_time_started = 0; + m_time_next_allowed = 0; +} + + +void CControlJump::load(LPCSTR section) +{ + m_delay_after_jump = pSettings->r_u32 (section,"jump_delay"); + m_jump_factor = pSettings->r_float(section,"jump_factor"); + m_trace_ground_range = pSettings->r_float(section,"jump_ground_trace_range"); + m_hit_trace_range = pSettings->r_float(section,"jump_hit_trace_range"); + m_build_line_distance = pSettings->r_float(section,"jump_build_line_distance"); + m_min_distance = pSettings->r_float(section,"jump_min_distance"); + m_max_distance = pSettings->r_float(section,"jump_max_distance"); + m_max_angle = pSettings->r_float(section,"jump_max_angle"); + m_max_height = pSettings->r_float(section,"jump_max_height"); +} + +bool CControlJump::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured_pure()) return false; + + + return true; +} + +void CControlJump::activate() +{ + m_man->capture_pure (this); + m_man->subscribe (this, ControlCom::eventAnimationEnd); + m_man->subscribe (this, ControlCom::eventAnimationStart); + m_man->subscribe (this, ControlCom::eventVelocityBounce); + + if (m_data.target_object) + start_jump (get_target(m_data.target_object)); + else + start_jump (m_data.target_position); + +} + +void CControlJump::on_release() +{ + m_man->unlock (this, ControlCom::eControlPath); + + SControlDirectionData *ctrl_data_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_data_dir); + ctrl_data_dir->linear_dependency = true; + + m_man->release_pure (this); + m_man->unsubscribe (this, ControlCom::eventVelocityBounce); + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); + m_man->unsubscribe (this, ControlCom::eventAnimationStart); + + m_object->path().prepare_builder (); + m_object->set_ignore_collision_hit (false); +} + +////////////////////////////////////////////////////////////////////////// +// Start jump +////////////////////////////////////////////////////////////////////////// +void CControlJump::start_jump(const Fvector &point) +{ + // initialize internals + m_velocity_bounced = false; + m_object_hitted = false; + + m_target_position = point; + m_blend_speed = -1.f; + + m_time_started = 0; + m_jump_time = 0; + + // ignore collision hit when object is landing + m_object->set_ignore_collision_hit (true); + + + // select correct state + if (is_flag(SControlJumpData::ePrepareSkip)) { + m_anim_state_current = eStateGlide; + m_anim_state_prev = eStatePrepare; + } else { + // check if can prepare in move + bool prepared = false; + + if (is_flag(SControlJumpData::ePrepareInMove)) { + // get animation time + float time = m_man->animation().motion_time(m_data.state_prepare_in_move.motion, m_object->Visual()); + // set acceleration and velocity + SVelocityParam &vel = m_object->move().get_velocity(m_data.state_prepare_in_move.velocity_mask); + float dist = time * vel.velocity.linear; + + // check nodes in direction + Fvector target_point; + target_point.mad(m_object->Position(), m_object->Direction(), dist); + if (m_man->path_builder().accessible(target_point)) { + // нода в прямой видимости? + m_man->path_builder().restrictions().add_border(m_object->Position(), target_point); + u32 node = ai().level_graph().check_position_in_direction(m_object->ai_location().level_vertex_id(),m_object->Position(),target_point); + m_man->path_builder().restrictions().remove_border(); + + if (ai().level_graph().valid_vertex_id(node) && m_man->path_builder().accessible(node)) + prepared = true; + } + + // node is checked, so try to build path + if (prepared) { + if (m_man->build_path_line(this, target_point, u32(-1), m_data.state_prepare_in_move.velocity_mask | MonsterMovement::eVelocityParameterStand)) { + //--------------------------------------------------------------------------------------------------- + // set path params + SControlPathBuilderData *ctrl_path = (SControlPathBuilderData*)m_man->data(this, ControlCom::eControlPath); + VERIFY (ctrl_path); + ctrl_path->enable = true; + m_man->lock (this, ControlCom::eControlPath); + //--------------------------------------------------------------------------------------------------- + + m_anim_state_current = eStatePrepareInMove; + m_anim_state_prev = eStateNone; + + m_man->dir_stop (this); + + } else prepared = false; + } + } + + // if cannot perform prepare in move + if (!prepared) { + VERIFY(m_data.state_prepare.motion.valid() || is_flag(SControlJumpData::eGlideOnPrepareFailed)); + + if (m_data.state_prepare.motion.valid()) { + m_anim_state_current = eStatePrepare; + m_anim_state_prev = eStateNone; + + m_man->path_stop (this); + m_man->move_stop (this); + + } else { + m_anim_state_current = eStateGlide; + m_anim_state_prev = eStatePrepare; + } + } + } + + select_next_anim_state (); +} + +////////////////////////////////////////////////////////////////////////// +// Animation startup +////////////////////////////////////////////////////////////////////////// +void CControlJump::select_next_anim_state() +{ + if (m_anim_state_current == eStateNone) { + stop(); + return; + } + + // check gliding state + if ((m_anim_state_current == eStateGlide) && (m_anim_state_prev == eStateGlide)) + if (is_flag(SControlJumpData::eGlidePlayAnimOnce)) return; + + //--------------------------------------------------------------------------------------------------- + // start new animation + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_data); + ctrl_data->global.actual = false; + + switch (m_anim_state_current) { + case eStatePrepare: ctrl_data->global.motion = m_data.state_prepare.motion; break; + case eStatePrepareInMove: ctrl_data->global.motion = m_data.state_prepare_in_move.motion; break; + case eStateGlide: ctrl_data->global.motion = m_data.state_glide.motion; break; + case eStateGround: ctrl_data->global.motion = m_data.state_ground.motion; break; + default: NODEFAULT; + } + //--------------------------------------------------------------------------------------------------- + + // switch state if needed + m_anim_state_prev = m_anim_state_current; + if (m_anim_state_current != eStateGlide) { + if (m_anim_state_current != eStatePrepare) + m_anim_state_current = EStateAnimJump(m_anim_state_current + 1); + else + m_anim_state_current = eStateGlide; + } +} + + +////////////////////////////////////////////////////////////////////////// +// Frame update jump state +////////////////////////////////////////////////////////////////////////// +void CControlJump::update_frame() +{ + // check if all jump stages are ended + if (m_velocity_bounced && m_man->path_builder().is_path_end(0.f)) { + stop(); + return; + } + + // trace enemy for hit + hit_test (); + + // set velocity from path if we are on it + if (m_man->path_builder().is_moving_on_path()) { + //--------------------------------------------------------------------------------------------------------------------------------- + // Set Velocity from path + //--------------------------------------------------------------------------------------------------------------------------------- + SControlMovementData *ctrl_move = (SControlMovementData*)m_man->data(this, ControlCom::eControlMovement); + VERIFY (ctrl_move); + + ctrl_move->velocity_target = m_object->move().get_velocity_from_path(); + ctrl_move->acc = flt_max; + //--------------------------------------------------------------------------------------------------------------------------------- + } + + // check if we landed + if (is_on_the_ground()) grounding(); +} + +////////////////////////////////////////////////////////////////////////// +// Trace ground to check if we have already landed +////////////////////////////////////////////////////////////////////////// +bool CControlJump::is_on_the_ground() +{ + if (m_time_started == 0) return false; + if (m_time_started + (m_jump_time*1000) > time()) return false; + + Fvector direction; + direction.set(0.f, -1.f, 0.f); + Fvector trace_from; + m_object->Center(trace_from); + + collide::rq_result l_rq; + + bool on_the_ground = false; + if (Level().ObjectSpace.RayPick(trace_from, direction, m_trace_ground_range, collide::rqtStatic, l_rq, m_object)) { + if (l_rq.range < m_trace_ground_range) on_the_ground = true; + } + return (on_the_ground); +} + +////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////// + +void CControlJump::grounding() +{ + if ((m_data.state_ground.velocity_mask == u32(-1)) || is_flag(SControlJumpData::eGroundSkip) || !m_data.state_ground.motion.valid()) { + stop(); + return; + } + + Fvector target_position; + target_position.mad(m_object->Position(), m_object->Direction(), m_build_line_distance); + + if (!m_man->build_path_line(this, target_position, u32(-1), m_data.state_ground.velocity_mask | MonsterMovement::eVelocityParameterStand)) stop(); + else { + SControlPathBuilderData *ctrl_path = (SControlPathBuilderData*)m_man->data(this, ControlCom::eControlPath); + VERIFY (ctrl_path); + ctrl_path->enable = true; + m_man->lock (this, ControlCom::eControlPath); + + // lock dir + m_man->dir_stop (this); + + m_time_started = 0; + m_jump_time = 0; + m_anim_state_current = eStateGround; + select_next_anim_state (); + } +} + +void CControlJump::stop() +{ + m_man->notify (ControlCom::eventJumpEnd, 0); +} + +////////////////////////////////////////////////////////////////////////// +// Get target point in world space +Fvector CControlJump::get_target(CObject *obj) +{ + u16 bone_id = smart_cast(obj->Visual())->LL_GetBoneRoot (); + CBoneInstance &bone = smart_cast(obj->Visual())->LL_GetBoneInstance (bone_id); + + Fmatrix global_transform; + global_transform.mul (obj->XFORM(),bone.mTransform); + + if (m_object->m_monster_type == CBaseMonster::eMonsterTypeOutdoor) + return (predict_position(obj, global_transform.c)); + else + return (global_transform.c); +} + +void CControlJump::on_event(ControlCom::EEventType type, ControlCom::IEventData *data) +{ + if (type == ControlCom::eventVelocityBounce) { + SEventVelocityBounce *event_data = (SEventVelocityBounce *)data; + if ((event_data->m_ratio < 0) && !m_velocity_bounced && (m_jump_time != 0)) { + if (is_on_the_ground()) { + m_velocity_bounced = true; + grounding(); + } else stop(); + } + } else if (type == ControlCom::eventAnimationEnd) { + select_next_anim_state(); + } else if (type == ControlCom::eventAnimationStart) { + + // start new animation + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_data); + + if ((m_anim_state_current == eStateGlide) && (m_anim_state_prev == eStateGlide)) { + //--------------------------------------------------------------------------------- + // start jump here + //--------------------------------------------------------------------------------- + // получить время физ.прыжка + float ph_time = m_object->character_physics_support()->movement()->JumpMinVelTime(m_target_position); + // выполнить прыжок в соответствии с делителем времени + float cur_factor = ((m_data.force_factor > 0) ? m_data.force_factor : m_jump_factor); + m_jump_time = ph_time/cur_factor; + m_object->character_physics_support()->movement()->Jump(m_target_position,m_jump_time); + m_time_started = time(); + m_time_next_allowed = m_time_started + m_delay_after_jump; + //--------------------------------------------------------------------------------- + + // set angular speed in exclusive force mode + SControlDirectionData *ctrl_data_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_data_dir); + + ctrl_data_dir->heading.target_angle = m_man->direction().angle_to_target(m_target_position); + + float cur_yaw,target_yaw; + m_man->direction().get_heading (cur_yaw, target_yaw); + ctrl_data_dir->heading.target_speed = angle_difference(cur_yaw,target_yaw)/ m_jump_time; + ctrl_data_dir->linear_dependency = false; + //--------------------------------------------------------------------------------- + + ctrl_data->set_speed (m_man->animation().current_blend()->timeTotal/ m_man->animation().current_blend()->speed / m_jump_time); + + } else + ctrl_data->set_speed (-1.f); + + } +} + +void CControlJump::hit_test() +{ + if (m_object_hitted) return; + if (!m_data.target_object) return; + + // Проверить на нанесение хита во время прыжка + Fvector trace_from; + m_object->Center(trace_from); + + collide::rq_result l_rq; + + if (Level().ObjectSpace.RayPick(trace_from, m_object->Direction(), m_hit_trace_range, collide::rqtObject, l_rq, m_object)) { + if ((l_rq.O == m_data.target_object) && (l_rq.range < m_hit_trace_range)) { + m_object_hitted = true; + } + } + + if (!m_object_hitted && m_data.target_object) { + + m_object_hitted = true; + // определить дистанцию до врага + Fvector d; + d.sub(m_data.target_object->Position(),m_object->Position()); + if (d.magnitude() > m_hit_trace_range) m_object_hitted = false; + + // проверка на Field-Of-Hit + float my_h,my_p; + float h,p; + + m_object->Direction().getHP(my_h,my_p); + d.getHP(h,p); + + float from = angle_normalize(my_h - PI_DIV_6); + float to = angle_normalize(my_h + PI_DIV_6); + + if (!is_angle_between(h, from, to)) m_object_hitted = false; + + from = angle_normalize(my_p - PI_DIV_6); + to = angle_normalize(my_p + PI_DIV_6); + + if (!is_angle_between(p, from, to)) m_object_hitted = false; + + } + + if (m_object_hitted) + m_object->HitEntityInJump(smart_cast(m_data.target_object)); +} + +bool CControlJump::can_jump(CObject *target) +{ + if (m_time_next_allowed > Device.dwTimeGlobal) return false; + + Fvector source_position = m_object->Position (); + Fvector target_position; + target->Center (target_position); + + // проверка на dist + float dist = source_position.distance_to(target_position); + if ((dist < m_min_distance) || (dist > m_max_distance)) return false; + + // получить вектор направления и его мир угол + float dir_yaw = Fvector().sub(target_position, source_position).getH(); + dir_yaw = angle_normalize(-dir_yaw); + + // проверка на angle + float yaw_current, yaw_target; + m_object->control().direction().get_heading(yaw_current, yaw_target); + if (angle_difference(yaw_current, dir_yaw) > m_max_angle) return false; + + // check if target on the same floor etc + if (_abs(target_position.y-source_position.y) > m_max_height) return false; + + // проверка prepare + if (!is_flag(SControlJumpData::ePrepareSkip) && !is_flag(SControlJumpData::eGlideOnPrepareFailed)) { + if (!is_flag(SControlJumpData::ePrepareInMove)) { + VERIFY(m_data.state_prepare.motion.valid()); + } else { + VERIFY(m_data.state_prepare_in_move.motion.valid()); + VERIFY(m_data.state_prepare_in_move.velocity_mask != u32(-1)); + + // try to trace distance according to prepare animation + bool good_trace_res = false; + + // get animation time + float time = m_man->animation().motion_time(m_data.state_prepare_in_move.motion, m_object->Visual()); + // set acceleration and velocity + SVelocityParam &vel = m_object->move().get_velocity(m_data.state_prepare_in_move.velocity_mask); + float dist = time * vel.velocity.linear; + + // check nodes in direction + Fvector target_point; + target_point.mad(m_object->Position(), m_object->Direction(), dist); + + if (m_man->path_builder().accessible(target_point)) { + // нода в прямой видимости? + m_man->path_builder().restrictions().add_border(m_object->Position(), target_point); + u32 node = ai().level_graph().check_position_in_direction(m_object->ai_location().level_vertex_id(),m_object->Position(),target_point); + m_man->path_builder().restrictions().remove_border(); + + if (ai().level_graph().valid_vertex_id(node) && m_man->path_builder().accessible(node)) + good_trace_res = true; + } + + if (!good_trace_res) { + // cannot prepare in move, so check if can prepare in stand state + if (!m_data.state_prepare.motion.valid()) return false; + } + } + } + + return true; +} + +Fvector CControlJump::predict_position(CObject *obj, const Fvector &pos) +{ + return pos; + + //CEntityAlive *entity_alive = smart_cast(obj); + //VERIFY(entity_alive); + // + //float velocity = entity_alive->movement_control()->GetVelocityActual(); + //float jump_time = m_object->movement_control()->JumpMinVelTime(pos); + //float prediction_dist = jump_time * velocity; + + // + + //Fvector dir; + //dir.set (entity->movement_control()->GetVelocity()); + //float speed = dir.magnitude(); + //dir.normalize_safe (); + + //Fvector prediction_pos; + ////prediction_pos.mad (pos, dir, prediction_dist); + //prediction_pos.mad (pos, dir, speed * jump_time / 2); + + //// проверить prediction_pos на дистанцию и угол + //float dist = m_object->Position().distance_to(prediction_pos); + //if ((dist < m_min_distance) || (dist > m_max_distance)) return pos; + + //// получить вектор направления и его мир угол + //float dir_yaw, dir_pitch; + + //dir.sub (prediction_pos, m_object->Position()); + //dir.getHP (dir_yaw, dir_pitch); + + //// проверка на angle и на dist + //float yaw_current, yaw_target; + //m_object->control().direction().get_heading(yaw_current, yaw_target); + //if (angle_difference(yaw_current, -dir_yaw) > m_max_angle) return pos; + +//#ifdef DEBUG +// DBG().level_info(this).clear (); +// DBG().level_info(this).add_item (pos, 0.35f, D3DCOLOR_XRGB(0,0,255)); +// DBG().level_info(this).add_item (prediction_pos, 0.35f, D3DCOLOR_XRGB(255,0,0)); +//#endif +// +// return prediction_pos; +} + diff --git a/src/xrGameLA/ai/monsters/control_jump.h b/src/xrGameLA/ai/monsters/control_jump.h new file mode 100644 index 000000000..c1cbcd445 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_jump.h @@ -0,0 +1,127 @@ +#pragma once +#include "control_combase.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" + +struct SControlJumpData : public ControlCom::IComData { + CObject *target_object; + Fvector target_position; + float force_factor; + + enum EFlags { + eEnablePredictPosition = u32(1) << 0, + ePrepareSkip = u32(1) << 1, // do not use prepare state + ePrepareInMove = u32(1) << 2, + eGlideOnPrepareFailed = u32(1) << 3, // if not set then cannot start jump + eGlidePlayAnimOnce = u32(1) << 4, + eGroundSkip = u32(1) << 5, + }; + + flags32 flags; + + struct _prepare{ + MotionID motion; + } state_prepare; + + struct _prepare_in_move{ + MotionID motion; + u32 velocity_mask; + } state_prepare_in_move; + + + struct _glide{ + MotionID motion; + } state_glide; + + struct _ground{ + MotionID motion; + u32 velocity_mask; + } state_ground; +}; + +class CControlJump : public CControl_ComCustom { + typedef CControl_ComCustom inherited; + + enum EStateAnimJump { + eStatePrepare, + eStatePrepareInMove, + eStateGlide, + eStateGround, + eStateNone + }; + + + // loadable parameters + u32 m_delay_after_jump; + float m_jump_factor; + float m_trace_ground_range; + float m_hit_trace_range; + float m_build_line_distance; + float m_min_distance; + float m_max_distance; + float m_max_angle; + float m_max_height; + + // run-time params + u32 m_time_next_allowed; + u32 m_time_started; // time jump started + float m_jump_time; // physical-counted time of jump + float m_blend_speed; // current anim blend speed + Fvector m_target_position; // save target position for internal needs + + + // state flags + bool m_object_hitted; + bool m_velocity_bounced; + + // animation + EStateAnimJump m_anim_state_prev; + EStateAnimJump m_anim_state_current; + +public: + virtual void load (LPCSTR section); + virtual void reinit (); + virtual bool check_start_conditions (); + virtual void activate (); + virtual void on_release (); + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + + + // process jump + virtual void update_frame (); + + // check for distance and angle difference + virtual bool can_jump (CObject *target); + + // stop/break jump and all of jumping states + virtual void stop (); + +SControlJumpData &setup_data () {return m_data;} + +private: + // service routines + // build path after jump + void grounding (); + // get target position according to object center point + Fvector get_target (CObject *obj); + // check for hit object + void hit_test (); + + // check current jump state + bool is_on_the_ground (); + + // position prediction + Fvector predict_position (CObject *obj, const Fvector &pos); + + void start_jump (const Fvector &point); + + // animation control method + void select_next_anim_state (); + + IC bool is_flag (SControlJumpData::EFlags flag); +}; + + +IC bool CControlJump::is_flag(SControlJumpData::EFlags flag) +{ + return (m_data.flags.is(flag) == TRUE); +} diff --git a/src/xrGameLA/ai/monsters/control_manager.cpp b/src/xrGameLA/ai/monsters/control_manager.cpp new file mode 100644 index 000000000..a6815e460 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_manager.cpp @@ -0,0 +1,419 @@ +#include "stdafx.h" +#include "control_manager.h" +#include "control_combase.h" +#include "BaseMonster/base_monster.h" + +enum EActiveComAction { + eRemove = u32(0), + eAdd +}; + + +// DEBUG purpose only +char *dbg_control_name_table[] = { + "Control_Movement", + "Control_Path", + "Control_Dir", + "Control_Animation", + "Control_Sequencer", + "Control_RotationJump", + "Control_Animation_BASE", + "Control_Movement_BASE", + "Control_Path_BASE", + "Control_Dir_BASE" +}; + + +CControl_Manager::CControl_Manager(CBaseMonster *obj) +{ + m_object = obj; + + m_animation = new CControlAnimation(); + m_movement = new CControlMovement(); + m_direction = new CControlDirection(); + + add (m_animation, ControlCom::eControlAnimation); + add (m_direction, ControlCom::eControlDir); + add (m_movement, ControlCom::eControlMovement); +} + +CControl_Manager::~CControl_Manager() +{ + xr_delete(m_animation); + xr_delete(m_direction); + xr_delete(m_movement); +} + +void CControl_Manager::init_external() +{ + //for (CONTROLLERS_MAP_IT it = m_control_elems.begin(); it != m_control_elems.end(); ++it) + // it->second->init_external(this, m_object); +} + +void CControl_Manager::load(LPCSTR section) +{ + init_external (); + + for (CONTROLLERS_MAP_IT it = m_control_elems.begin(); it != m_control_elems.end(); ++it) + it->second->load(section); +} +void CControl_Manager::reload(LPCSTR section) +{ + for (CONTROLLERS_MAP_IT it = m_control_elems.begin(); it != m_control_elems.end(); ++it) + it->second->reload(section); +} + +void CControl_Manager::reinit() +{ + if( m_object->CCustomMonster::use_simplified_visual() ) return; + // todo: make it simpler + // reinit pure first, base second, custom third + CONTROLLERS_MAP_IT it; + + for (it = m_control_elems.begin(); it != m_control_elems.end(); ++it) + if (is_pure(it->second)) it->second->reinit(); + + for (it = m_control_elems.begin(); it != m_control_elems.end(); ++it) + if (is_base(it->second)) it->second->reinit(); + + for (it = m_control_elems.begin(); it != m_control_elems.end(); ++it) + if (!is_pure(it->second) && !is_base(it->second)) it->second->reinit(); + + // fill active elems + m_active_elems.clear (); + m_active_elems.reserve (ControlCom::eControllersCount); + for (it = m_control_elems.begin(); it != m_control_elems.end(); ++it) { + if (it->second->is_active() && !is_locked(it->second)) { + m_active_elems.push_back(it->second); + } + } + +} + +struct predicate_remove { + IC bool operator() (const CControl_Com *com) { + return (com == 0); + } +}; + +void CControl_Manager::update_frame() +{ + if (!m_object->g_Alive()) return; + + for (COM_VEC_IT it = m_active_elems.begin(); it != m_active_elems.end(); ++it) { + // update coms + if ((*it)) (*it)->update_frame(); + } + + m_active_elems.erase ( + std::remove_if( + m_active_elems.begin(), + m_active_elems.end(), + predicate_remove() + ), + m_active_elems.end() + ); +} + +void CControl_Manager::update_schedule() +{ + if (!m_object->g_Alive()) return; + + for (COM_VEC_IT it = m_active_elems.begin(); it != m_active_elems.end(); ++it) { + // update coms + if ((*it)) (*it)->update_schedule(); + } + + m_active_elems.erase ( + std::remove_if( + m_active_elems.begin(), + m_active_elems.end(), + predicate_remove() + ), + m_active_elems.end() + ); +} + +ControlCom::EControlType CControl_Manager::com_type(CControl_Com *com) +{ + for (CONTROLLERS_MAP_IT it = m_control_elems.begin(); it != m_control_elems.end(); ++it) + if (it->second == com) return it->first; + + return ControlCom::eControlInvalid; +} + + +void CControl_Manager::notify(ControlCom::EEventType event, ControlCom::IEventData *data) +{ + CONTROLLERS_VECTOR &vect = m_listeners[event]; + + for (u32 i = 0; i < vect.size(); i++) { + VERIFY(vect[i]->cing()); + vect[i]->cing()->on_event(event, data); + } +} + +////////////////////////////////////////////////////////////////////////// +// Messaging +////////////////////////////////////////////////////////////////////////// +void CControl_Manager::subscribe(CControl_Com *com, ControlCom::EEventType type) +{ + m_listeners[type].push_back(com); +} + +void CControl_Manager::unsubscribe(CControl_Com *com, ControlCom::EEventType type) +{ + CONTROLLERS_VECTOR &vect = m_listeners[type]; + + for (u32 i = 0; i < vect.size(); i++) { + if (vect[i] == com) { + vect[i] = vect.back(); + vect.pop_back (); + return; + } + } +} + +ControlCom::IComData *CControl_Manager::data(CControl_Com *who, ControlCom::EControlType type) +{ + CControl_Com *target = m_control_elems[type]; + + // get_capturer + CControl_Com *capturer = target->ced()->capturer(); + if (capturer == who) { + return target->ced()->data(); + } + + return 0; +} + +// TODO: check construction of SControl_Element and check init in add-function +void CControl_Manager::add(CControl_Com *com, ControlCom::EControlType type) +{ + m_control_elems[type] = com; + com->init_external(this, m_object); +} +void CControl_Manager::set_base_controller(CControl_Com *com, ControlCom::EControlType type) +{ + m_base_elems[type] = com; +} + +void CControl_Manager::install_path_manager(CControlPathBuilder *pman) +{ + m_path = pman; +} + +bool CControl_Manager::is_pure(CControl_Com *com) +{ + return (com->cing() == 0); +} + +bool CControl_Manager::is_base(CControl_Com *com) +{ + return (com->ced() == 0); +} +bool CControl_Manager::is_locked(CControl_Com *com) +{ + return (com->ced() && com->ced()->is_locked()); +} + +// capture +void CControl_Manager::capture(CControl_Com *com, ControlCom::EControlType type) // who, type +{ + CControl_Com *target = m_control_elems[type]; + + // 1. Check if can capture + CControl_Com *capturer = target->ced()->capturer(); + VERIFY(!capturer || is_base(capturer)); + + if (target->is_active()) { + target->ced()->on_release (); + // if there is base capturer - stop control com + if (capturer) capturer->cing()->on_stop_control (type); + } + + // 3. + target->ced()->set_capturer (com); + + // 4. + target->ced()->on_capture (); + + // 5. + com->cing()->on_start_control (type); +} + +void CControl_Manager::release(CControl_Com *com, ControlCom::EControlType type) // who, type +{ + CControl_Com *target = m_control_elems[type]; + CControl_Com *capturer = target->ced()->capturer(); + + if (capturer == com) { + // select new capture if there is a base controller + CONTROLLERS_MAP_IT it = m_base_elems.find(type); + if (it != m_base_elems.end()) { + com->cing()->on_stop_control(type); + target->ced()->set_capturer (0); + + capture(m_base_elems[type], type); + } else { + // if active - finalize + if (target->is_active()) { + target->ced()->on_release (); + deactivate (type); + } + + com->cing()->on_stop_control (type); + target->ced()->set_capturer (0); + } + } +} + +void CControl_Manager::capture_pure(CControl_Com *com) +{ + capture(com,ControlCom::eControlPath); + capture(com,ControlCom::eControlAnimation); + capture(com,ControlCom::eControlMovement); + capture(com,ControlCom::eControlDir); +} + +void CControl_Manager::release_pure(CControl_Com *com) +{ + release(com,ControlCom::eControlPath); + release(com,ControlCom::eControlAnimation); + release(com,ControlCom::eControlMovement); + release(com,ControlCom::eControlDir); +} + + +void CControl_Manager::activate(ControlCom::EControlType type) +{ + m_control_elems[type]->set_active (); + check_active_com (m_control_elems[type], eAdd); + m_object->on_activate_control (type); +} +void CControl_Manager::deactivate(ControlCom::EControlType type) +{ + m_control_elems[type]->set_active (false); + check_active_com (m_control_elems[type], eRemove); +} +void CControl_Manager::deactivate(CControl_Com *com) +{ + deactivate(com_type(com)); +} + +bool CControl_Manager::is_captured(ControlCom::EControlType type) +{ + CControl_Com *capturer = m_control_elems[type]->ced()->capturer(); + if (!capturer || is_base(capturer)) return false; + + return true; +} + +bool CControl_Manager::is_captured_pure() +{ + return (is_captured(ControlCom::eControlPath) || + is_captured(ControlCom::eControlAnimation) || + is_captured(ControlCom::eControlMovement) || + is_captured(ControlCom::eControlDir)); +} + + +void CControl_Manager::lock(CControl_Com *com, ControlCom::EControlType type) +{ + VERIFY (is_pure(m_control_elems[type])); + VERIFY (m_control_elems[type]->ced()->capturer() == com); + + m_control_elems[type]->ced()->set_locked(); + + // it's now locked so remove from active list + check_active_com(m_control_elems[type], eRemove); +} + +void CControl_Manager::unlock(CControl_Com *com, ControlCom::EControlType type) +{ + VERIFY (is_pure(m_control_elems[type])); + VERIFY (m_control_elems[type]->ced()->capturer() == com); + + m_control_elems[type]->ced()->set_locked(false); + + // it's unlocked so add to active list + check_active_com(m_control_elems[type], eAdd); +} + +void CControl_Manager::path_stop(CControl_Com *com) +{ + SControlPathBuilderData *ctrl_path = (SControlPathBuilderData*)data(com, ControlCom::eControlPath); + VERIFY (ctrl_path); + ctrl_path->enable = false; +} + +void CControl_Manager::move_stop(CControl_Com *com) +{ + SControlMovementData *ctrl_move = (SControlMovementData*)data(com, ControlCom::eControlMovement); + VERIFY (ctrl_move); + ctrl_move->velocity_target = 0; + ctrl_move->acc = flt_max; + +} +void CControl_Manager::dir_stop(CControl_Com *com) +{ + SControlDirectionData *ctrl_dir = (SControlDirectionData*)data(com, ControlCom::eControlDir); + VERIFY (ctrl_dir); + ctrl_dir->heading.target_speed = 0; +} + +bool CControl_Manager::check_start_conditions(ControlCom::EControlType type) +{ + return m_control_elems[type]->check_start_conditions(); +} + +bool CControl_Manager::build_path_line(CControl_Com *com, const Fvector &target, u32 node, u32 vel_mask) +{ + CControl_Com *path = m_control_elems[ControlCom::eControlPath]; + VERIFY (com == path->ced()->capturer()); + + return (path_builder().build_special(target, node, vel_mask)); +} + +void CControl_Manager::check_active_com(CControl_Com *com, bool b_add) +{ + if (b_add){ + if (com->is_active() && !com->ced()->is_locked()) { + COM_VEC_IT it = std::find(m_active_elems.begin(),m_active_elems.end(),com); + if (it == m_active_elems.end()) m_active_elems.push_back(com); + } + } else { + COM_VEC_IT it = std::find(m_active_elems.begin(),m_active_elems.end(),com); + if (it != m_active_elems.end()) (*it) = 0; // do not remove just mark + } +} + + +void CControl_Manager::dump(CControl_Com *com, LPCSTR action, ControlCom::EControlType type) +{ + Msg("---------------------------------------------------------------------------"); + Msg("-- [%s] %s [%s]",dbg_control_name_table[com_type(com)], action, dbg_control_name_table[type]); + Msg("-- Dump: -----------------------------------------------------------------"); + + u32 index = 0; + for (CONTROLLERS_MAP_IT it = m_control_elems.begin(); it != m_control_elems.end(); ++it, index++) { + string128 st; st[0]=0; + if (!it->second->is_inited()) continue; + + if (it->second->ced()) { + string128 str; + if (it->second->ced()->capturer()) + xr_strcpy(str, dbg_control_name_table[com_type(it->second->ced()->capturer())]); + else + xr_strcpy(str, "NONE"); + + xr_sprintf(st, "Locked[%u] Capturer[%s]", it->second->ced()->is_locked(), str); + } + + Msg("[%u] %s: Active[%u] %s",index+1, dbg_control_name_table[it->first], it->second->is_active(),st); + } +} + + + + diff --git a/src/xrGameLA/ai/monsters/control_manager.h b/src/xrGameLA/ai/monsters/control_manager.h new file mode 100644 index 000000000..c2d36639f --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_manager.h @@ -0,0 +1,104 @@ +#pragma once +#include "control_com_defs.h" + +#include "control_animation.h" +#include "control_direction.h" +#include "control_path_builder.h" +#include "control_movement.h" + + +class CBaseMonster; +class CControl_Com; + +class CControl_Manager { + CBaseMonster *m_object; + xr_vector m_nearest; + + DEFINE_VECTOR (CControl_Com *, CONTROLLERS_VECTOR, CONTROLLERS_VECTOR_IT); + DEFINE_MAP (ControlCom::EEventType, CONTROLLERS_VECTOR, LISTENERS_MAP, LISTENERS_MAP_IT); + LISTENERS_MAP m_listeners; + + // contains all available controllers + DEFINE_MAP (ControlCom::EControlType, CControl_Com*, CONTROLLERS_MAP, CONTROLLERS_MAP_IT); + CONTROLLERS_MAP m_control_elems; + CONTROLLERS_MAP m_base_elems; + + DEFINE_VECTOR (CControl_Com*, COM_VEC, COM_VEC_IT); + COM_VEC m_active_elems; + + CControlAnimation *m_animation; + CControlDirection *m_direction; + CControlMovement *m_movement; + CControlPathBuilder *m_path; + +public: + CControl_Manager (CBaseMonster *); + ~CControl_Manager (); + + // common routines + void init_external (); + void load (LPCSTR section); + void reinit (); + void reload (LPCSTR section); + void update_schedule (); + void update_frame (); + + // event handling routines + void notify (ControlCom::EEventType, ControlCom::IEventData*); + void subscribe (CControl_Com*, ControlCom::EEventType); + void unsubscribe (CControl_Com*, ControlCom::EEventType); + + // add new control block + void add (CControl_Com*, ControlCom::EControlType); + void set_base_controller (CControl_Com*, ControlCom::EControlType); + + // capturing/releasing + void capture (CControl_Com*, ControlCom::EControlType); // who, type + void release (CControl_Com*, ControlCom::EControlType); // who, type + + void capture_pure (CControl_Com*); + void release_pure (CControl_Com*); + + void activate (ControlCom::EControlType type); + void deactivate (ControlCom::EControlType type); + void deactivate (CControl_Com*); + + void lock (CControl_Com*, ControlCom::EControlType); + void unlock (CControl_Com*, ControlCom::EControlType); + + ControlCom::IComData *data (CControl_Com*, ControlCom::EControlType); + + CControlAnimation &animation () {return (*m_animation);} + CControlDirection &direction () {return (*m_direction);} + CControlPathBuilder &path_builder () {return (*m_path);} + CControlMovement &movement () {return (*m_movement);} + + void install_path_manager (CControlPathBuilder *); + + bool is_captured (ControlCom::EControlType); + bool is_captured_pure (); + + // utilities + + void path_stop (CControl_Com*); + void move_stop (CControl_Com*); + void dir_stop (CControl_Com*); + + bool check_start_conditions (ControlCom::EControlType); + + // path buidler specials + bool build_path_line (CControl_Com*, const Fvector &target, u32 node, u32 vel_mask); + +private: + ControlCom::EControlType com_type (CControl_Com*); + + bool is_pure (CControl_Com*); + bool is_base (CControl_Com*); + bool is_locked (CControl_Com*); + + void dump (CControl_Com *com, LPCSTR action, ControlCom::EControlType type); + + void check_active_com (CControl_Com *com, bool b_add); +}; + + diff --git a/src/xrGameLA/ai/monsters/control_manager_custom.cpp b/src/xrGameLA/ai/monsters/control_manager_custom.cpp new file mode 100644 index 000000000..f37531bef --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_manager_custom.cpp @@ -0,0 +1,619 @@ +#include "stdafx.h" +#include "control_manager_custom.h" +#include "BaseMonster/base_monster.h" +#include "control_sequencer.h" +#include "control_run_attack.h" +#include "control_threaten.h" +#include "../../PhysicsShell.h" +#include "../../detail_path_manager.h" +#include "../../level.h" +#include "control_animation_base.h" +#include "control_critical_wound.h" + + +CControlManagerCustom::CControlManagerCustom() +{ + m_sequencer = 0; + m_triple_anim = 0; + m_rotation_jump = 0; + m_jump = 0; + m_run_attack = 0; + m_threaten = 0; + m_melee_jump = 0; +} + +CControlManagerCustom::~CControlManagerCustom() +{ + xr_delete (m_sequencer); + xr_delete (m_triple_anim); + xr_delete (m_rotation_jump); + xr_delete (m_jump); + xr_delete (m_run_attack); + xr_delete (m_threaten); + xr_delete (m_melee_jump); +} + +void CControlManagerCustom::reinit() +{ + inherited::reinit (); + m_rot_jump_data.clear (); +} + +void CControlManagerCustom::add_ability(ControlCom::EControlType type) +{ + switch (type) { + case ControlCom::eControlSequencer: + m_sequencer = new CAnimationSequencer(); + m_man->add (m_sequencer, ControlCom::eControlSequencer); + break; + case ControlCom::eControlTripleAnimation: + m_triple_anim = new CAnimationTriple(); + m_man->add (m_triple_anim, ControlCom::eControlTripleAnimation); + break; + case ControlCom::eControlRotationJump: + m_rotation_jump = new CControlRotationJump(); + m_man->add (m_rotation_jump, ControlCom::eControlRotationJump); + break; + case ControlCom::eControlJump: + m_jump = new CControlJump(); + m_man->add (m_jump, ControlCom::eControlJump); + break; + case ControlCom::eControlRunAttack: + m_run_attack = new CControlRunAttack(); + m_man->add (m_run_attack, ControlCom::eControlRunAttack); + break; + case ControlCom::eControlThreaten: + m_threaten = new CControlThreaten(); + m_man->add (m_threaten, ControlCom::eControlThreaten); + set_threaten_data (0,0.f); + break; + case ControlCom::eControlMeleeJump: + m_melee_jump = new CControlMeleeJump(); + m_man->add (m_melee_jump, ControlCom::eControlMeleeJump); + break; + case ControlCom::eComCriticalWound: + m_critical_wound = new CControlCriticalWound(); + m_man->add (m_critical_wound, ControlCom::eComCriticalWound); + break; + + } +} + +////////////////////////////////////////////////////////////////////////// + +void CControlManagerCustom::on_start_control(ControlCom::EControlType type) +{ + switch (type) { + case ControlCom::eControlSequencer: m_man->subscribe (this, ControlCom::eventSequenceEnd); break; + case ControlCom::eControlTripleAnimation: m_man->subscribe (this, ControlCom::eventTAChange); break; + case ControlCom::eControlJump: m_man->subscribe (this, ControlCom::eventJumpEnd); break; + case ControlCom::eControlRotationJump: m_man->subscribe (this, ControlCom::eventRotationJumpEnd); break; + case ControlCom::eControlMeleeJump: m_man->subscribe (this, ControlCom::eventMeleeJumpEnd); break; + case ControlCom::eControlRunAttack: m_man->subscribe (this, ControlCom::eventRunAttackEnd); break; + case ControlCom::eControlThreaten: m_man->subscribe (this, ControlCom::eventThreatenEnd); break; + case ControlCom::eComCriticalWound: m_man->subscribe (this, ControlCom::eventCriticalWoundEnd); break; + } +} + +void CControlManagerCustom::on_stop_control (ControlCom::EControlType type) +{ + switch (type) { + case ControlCom::eControlSequencer: m_man->unsubscribe (this, ControlCom::eventSequenceEnd); break; + case ControlCom::eControlTripleAnimation: m_man->unsubscribe (this, ControlCom::eventTAChange); break; + case ControlCom::eControlJump: m_man->unsubscribe (this, ControlCom::eventJumpEnd); break; + case ControlCom::eControlRotationJump: m_man->unsubscribe (this, ControlCom::eventRotationJumpEnd);break; + case ControlCom::eControlMeleeJump: m_man->unsubscribe (this, ControlCom::eventMeleeJumpEnd); break; + case ControlCom::eControlRunAttack: m_man->unsubscribe (this, ControlCom::eventRunAttackEnd); break; + case ControlCom::eControlThreaten: m_man->unsubscribe (this, ControlCom::eventThreatenEnd); break; + case ControlCom::eComCriticalWound: m_man->unsubscribe (this, ControlCom::eventCriticalWoundEnd); break; + } +} + +void CControlManagerCustom::on_event(ControlCom::EEventType type, ControlCom::IEventData *data) +{ + switch (type) { + case ControlCom::eventSequenceEnd: m_man->release(this, ControlCom::eControlSequencer); break; + case ControlCom::eventTAChange: + { + STripleAnimEventData *event_data = (STripleAnimEventData *)data; + if (event_data->m_current_state == eStateNone) + m_man->release(this, ControlCom::eControlTripleAnimation); + + break; + } + case ControlCom::eventJumpEnd: m_man->release(this, ControlCom::eControlJump); break; + case ControlCom::eventRotationJumpEnd: m_man->release(this, ControlCom::eControlRotationJump); break; + case ControlCom::eventMeleeJumpEnd: m_man->release(this, ControlCom::eControlMeleeJump); break; + case ControlCom::eventRunAttackEnd: m_man->release(this, ControlCom::eControlRunAttack); break; + case ControlCom::eventThreatenEnd: m_man->release(this, ControlCom::eControlThreaten); break; + case ControlCom::eventCriticalWoundEnd: m_man->release(this, ControlCom::eComCriticalWound); break; + } +} + + +void CControlManagerCustom::update_frame() +{ + +} + +void CControlManagerCustom::update_schedule() +{ + if (m_threaten) check_threaten (); + if (m_jump) { + check_attack_jump (); + //check_jump_over_physics (); + } + if (m_rotation_jump) check_rotation_jump (); + if (m_run_attack) check_run_attack (); + if (m_melee_jump) check_melee_jump (); +} + +////////////////////////////////////////////////////////////////////////// +void CControlManagerCustom::ta_fill_data(SAnimationTripleData &data, LPCSTR s1, LPCSTR s2, LPCSTR s3, bool execute_once, bool skip_prep, u32 capture_type) +{ + // Load triple animations + IKinematicsAnimated *skel_animated = smart_cast(m_object->Visual()); + data.pool[0] = skel_animated->ID_Cycle_Safe(s1); VERIFY(data.pool[0]); + data.pool[1] = skel_animated->ID_Cycle_Safe(s2); VERIFY(data.pool[1]); + data.pool[2] = skel_animated->ID_Cycle_Safe(s3); VERIFY(data.pool[2]); + data.execute_once = execute_once; + data.skip_prepare = skip_prep; + data.capture_type = capture_type; +} + + +void CControlManagerCustom::ta_activate(const SAnimationTripleData &data) +{ + if (!m_man->check_start_conditions(ControlCom::eControlTripleAnimation)) + return; + + m_man->capture (this, ControlCom::eControlTripleAnimation); + + SAnimationTripleData *ctrl_data = (SAnimationTripleData*)m_man->data(this, ControlCom::eControlTripleAnimation); + VERIFY (ctrl_data); + + ctrl_data->pool[0] = data.pool[0]; + ctrl_data->pool[1] = data.pool[1]; + ctrl_data->pool[2] = data.pool[2]; + ctrl_data->skip_prepare = data.skip_prepare; + ctrl_data->execute_once = data.execute_once; + ctrl_data->capture_type = data.capture_type; + + m_man->activate (ControlCom::eControlTripleAnimation); +} + +void CControlManagerCustom::ta_pointbreak() +{ + if (ta_is_active()) m_triple_anim->pointbreak(); +} + +bool CControlManagerCustom::ta_is_active() +{ + return (m_triple_anim->is_active()); +} + +bool CControlManagerCustom::ta_is_active(const SAnimationTripleData &data) +{ + if (!m_triple_anim->is_active()) return false; + + SAnimationTripleData *ctrl_data = (SAnimationTripleData*)m_man->data(this, ControlCom::eControlTripleAnimation); + VERIFY (ctrl_data); + + return ( + (ctrl_data->pool[0] == data.pool[0]) && + (ctrl_data->pool[1] == data.pool[1]) && + (ctrl_data->pool[2] == data.pool[2]) + ); +} + +void CControlManagerCustom::ta_deactivate() +{ + m_man->release(this, ControlCom::eControlTripleAnimation); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Работа с последовательностями +void CControlManagerCustom::seq_init() +{ + m_man->capture (this, ControlCom::eControlSequencer); + + SAnimationSequencerData *ctrl_data = (SAnimationSequencerData*)m_man->data(this, ControlCom::eControlSequencer); + if (!ctrl_data) return; + + ctrl_data->motions.clear (); +} + +void CControlManagerCustom::seq_add(MotionID motion) +{ + SAnimationSequencerData *ctrl_data = (SAnimationSequencerData*)m_man->data(this, ControlCom::eControlSequencer); + if (!ctrl_data) return; + + ctrl_data->motions.push_back(motion); +} + +void CControlManagerCustom::seq_switch() +{ + m_man->activate(ControlCom::eControlSequencer); +} + +void CControlManagerCustom::seq_run(MotionID motion) +{ + if (!m_man->check_start_conditions(ControlCom::eControlSequencer)) + return; + + m_man->capture (this, ControlCom::eControlSequencer); + + SAnimationSequencerData *ctrl_data = (SAnimationSequencerData*)m_man->data(this, ControlCom::eControlSequencer); + if (!ctrl_data) return; + + ctrl_data->motions.clear (); + ctrl_data->motions.push_back(motion); + + m_man->activate(ControlCom::eControlSequencer); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Jumping +/////////////////////////////////////////////////////////////////////////////////////////////////// +void CControlManagerCustom::jump(CObject *obj, const SControlJumpData &ta) +{ + if (!m_man->check_start_conditions(ControlCom::eControlJump)) + return; + + if (m_object->GetScriptControl()) + return; + + m_man->capture (this, ControlCom::eControlJump); + + SControlJumpData *ctrl_data = (SControlJumpData *) m_man->data(this, ControlCom::eControlJump); + VERIFY (ctrl_data); + + ctrl_data->target_object = obj; + ctrl_data->target_position = obj->Position(); + ctrl_data->flags = ta.flags; + ctrl_data->state_prepare.motion = ta.state_prepare.motion; + ctrl_data->state_prepare_in_move.motion = ta.state_prepare_in_move.motion; + ctrl_data->state_prepare_in_move.velocity_mask = ta.state_prepare_in_move.velocity_mask; + ctrl_data->state_glide.motion = ta.state_glide.motion; + ctrl_data->state_ground.motion = ta.state_ground.motion; + ctrl_data->state_ground.velocity_mask = ta.state_ground.velocity_mask; + ctrl_data->force_factor = -1.f; + + m_man->activate (ControlCom::eControlJump); +} + +void CControlManagerCustom::load_jump_data(LPCSTR s1, LPCSTR s2, LPCSTR s3, LPCSTR s4, u32 vel_mask_prepare, u32 vel_mask_ground, u32 flags) +{ + IKinematicsAnimated *skel_animated = smart_cast(m_object->Visual()); + if ( !skel_animated ) + { + return; // monster is dead, so no skeleton (early return due to bug: 18755) + } + + m_jump->setup_data().flags.assign(flags); + + if (s1) { + m_jump->setup_data().state_prepare.motion = skel_animated->ID_Cycle_Safe(s1); + VERIFY(m_jump->setup_data().state_prepare.motion); + } else m_jump->setup_data().state_prepare.motion.invalidate(); + + if (s2) { + m_jump->setup_data().state_prepare_in_move.motion = skel_animated->ID_Cycle_Safe(s2); + VERIFY(m_jump->setup_data().state_prepare_in_move.motion); + m_jump->setup_data().flags.or(SControlJumpData::ePrepareInMove); + } else m_jump->setup_data().state_prepare_in_move.motion.invalidate(); + + m_jump->setup_data().state_glide.motion = skel_animated->ID_Cycle_Safe(s3); + VERIFY(m_jump->setup_data().state_glide.motion); + + if (s4) { + m_jump->setup_data().state_ground.motion = skel_animated->ID_Cycle_Safe(s4); + VERIFY(m_jump->setup_data().state_ground.motion); + } else { + m_jump->setup_data().state_ground.motion.invalidate(); + m_jump->setup_data().flags.or(SControlJumpData::eGroundSkip); + } + + if (!s1 && !s2) { + m_jump->setup_data().flags.or(SControlJumpData::ePrepareSkip); + } + + m_jump->setup_data().flags.or(SControlJumpData::eGlidePlayAnimOnce); + m_jump->setup_data().flags.or(SControlJumpData::eGlideOnPrepareFailed); + + m_jump->setup_data().state_prepare_in_move.velocity_mask = vel_mask_prepare; + m_jump->setup_data().state_ground.velocity_mask = vel_mask_ground; + + m_jump->setup_data().force_factor = -1.f; +} + + + +void CControlManagerCustom::jump(const SControlJumpData &ta) +{ + if (!m_man->check_start_conditions(ControlCom::eControlJump)) + return; + + if (m_object->GetScriptControl()) return; + + m_man->capture (this, ControlCom::eControlJump); + + SControlJumpData *ctrl_data = (SControlJumpData *) m_man->data(this, ControlCom::eControlJump); + VERIFY (ctrl_data); + + ctrl_data->target_object = ta.target_object; + ctrl_data->target_position = ta.target_position; + ctrl_data->flags = ta.flags; + ctrl_data->state_prepare.motion = ta.state_prepare.motion; + ctrl_data->state_prepare_in_move.motion = ta.state_prepare_in_move.motion; + ctrl_data->state_prepare_in_move.velocity_mask = ta.state_prepare_in_move.velocity_mask; + ctrl_data->state_glide.motion = ta.state_glide.motion; + ctrl_data->state_ground.motion = ta.state_ground.motion; + ctrl_data->state_ground.velocity_mask = ta.state_ground.velocity_mask; + ctrl_data->force_factor = -1.f; + + m_man->activate (ControlCom::eControlJump); +} + +void CControlManagerCustom::jump(const Fvector &position) +{ + if (!m_man->check_start_conditions(ControlCom::eControlJump)) + return; + + m_man->capture (this, ControlCom::eControlJump); + + SControlJumpData *ctrl_data = (SControlJumpData *) m_man->data(this, ControlCom::eControlJump); + VERIFY (ctrl_data); + + ctrl_data->target_object = 0; + ctrl_data->target_position = position; + ctrl_data->flags.or (SControlJumpData::ePrepareSkip); + ctrl_data->force_factor = -1.f; + + m_man->activate (ControlCom::eControlJump); +} + + +void CControlManagerCustom::script_jump(const Fvector &position, float factor) +{ + if (!m_man->check_start_conditions(ControlCom::eControlJump)) return; + + m_man->capture (this, ControlCom::eControlJump); + + SControlJumpData *ctrl_data = (SControlJumpData *) m_man->data(this, ControlCom::eControlJump); + VERIFY (ctrl_data); + + ctrl_data->target_object = 0; + ctrl_data->target_position = position; + ctrl_data->force_factor = factor; + + m_man->activate (ControlCom::eControlJump); +} + +////////////////////////////////////////////////////////////////////////// +// Services +////////////////////////////////////////////////////////////////////////// +void CControlManagerCustom::check_attack_jump() +{ + if (!m_object->EnemyMan.get_enemy()) return; + if (m_object->GetScriptControl()) return; + if (!m_object->check_start_conditions(ControlCom::eControlJump)) return; + if (!m_object->EnemyMan.see_enemy_now())return; + + CEntityAlive *target = const_cast(m_object->EnemyMan.get_enemy()); + if (!m_jump->can_jump(target)) return; + + if (m_man->check_start_conditions(ControlCom::eControlJump)) { + + m_jump->setup_data().flags.set (SControlJumpData::ePrepareSkip, false); + m_jump->setup_data().target_object = target; + m_jump->setup_data().target_position = target->Position(); + + jump(m_jump->setup_data()); + } +} + +#define MAX_DIST_SUM 6.f + +void CControlManagerCustom::check_jump_over_physics() +{ + if (!m_man->path_builder().is_moving_on_path()) return; + if (!m_man->check_start_conditions(ControlCom::eControlJump)) return; + if (!m_object->check_start_conditions(ControlCom::eControlJump)) return; + if (m_object->GetScriptControl()) return; + + Fvector prev_pos = m_object->Position(); + float dist_sum = 0.f; + + for(u32 i = m_man->path_builder().detail().curr_travel_point_index(); ipath_builder().detail().path().size();i++) { + const DetailPathManager::STravelPathPoint &travel_point = m_man->path_builder().detail().path()[i]; + + // получить список объектов вокруг врага + m_nearest.clear_not_free (); + Level().ObjectSpace.GetNearest (m_nearest,travel_point.position, m_object->Radius(), NULL); + + for (u32 k=0;k(m_nearest[k]); + if (!obj || !obj->PPhysicsShell() || !obj->PPhysicsShell()->isActive() || (obj->Radius() < 0.5f)) continue; + if (m_object->Position().distance_to(obj->Position()) < MAX_DIST_SUM / 2) continue; + + Fvector dir = Fvector().sub(travel_point.position, m_object->Position()); + + // проверка на Field-Of-View + float my_h = m_object->Direction().getH(); + float h = dir.getH(); + + float from = angle_normalize(my_h - deg(8)); + float to = angle_normalize(my_h + deg(8)); + + if (!is_angle_between(h, from, to)) continue; + + dir = Fvector().sub(obj->Position(), m_object->Position()); + + // вычислить целевую позицию для прыжка + Fvector target; + obj->Center(target); + target.y += obj->Radius(); + // -------------------------------------------------------- + + m_jump->setup_data().flags.set (SControlJumpData::ePrepareSkip, true); + m_jump->setup_data().target_object = 0; + m_jump->setup_data().target_position = target; + + jump(m_jump->setup_data()); + + return; + } + + dist_sum += prev_pos.distance_to(travel_point.position); + if (dist_sum > MAX_DIST_SUM) break; + + prev_pos = travel_point.position; + } +} + +////////////////////////////////////////////////////////////////////////// +// Rotation Jump +////////////////////////////////////////////////////////////////////////// + +void CControlManagerCustom::check_rotation_jump() +{ + if (!m_man->check_start_conditions(ControlCom::eControlRotationJump)) return; + if (!m_object->check_start_conditions(ControlCom::eControlRotationJump)) return; + + VERIFY (!m_rot_jump_data.empty()); + + m_man->capture (this, ControlCom::eControlRotationJump); + + SControlRotationJumpData *ctrl_data = (SControlRotationJumpData *) m_man->data(this, ControlCom::eControlRotationJump); + VERIFY (ctrl_data); + + (*ctrl_data) = m_rot_jump_data[Random.randI(m_rot_jump_data.size())]; + + m_man->activate (ControlCom::eControlRotationJump); +} + +void CControlManagerCustom::add_rotation_jump_data(LPCSTR left1,LPCSTR left2,LPCSTR right1,LPCSTR right2, float angle, u32 flags) +{ + SControlRotationJumpData data; + fill_rotation_data (data,left1,left2,right1,right2,angle,flags); + + m_rot_jump_data.push_back (data); +} +////////////////////////////////////////////////////////////////////////// + +void CControlManagerCustom::check_run_attack() +{ + if (!m_man->check_start_conditions(ControlCom::eControlRunAttack)) return; + if (!m_object->check_start_conditions(ControlCom::eControlRunAttack)) return; + + m_man->capture (this, ControlCom::eControlRunAttack); + m_man->activate (ControlCom::eControlRunAttack); +} + +void CControlManagerCustom::check_threaten() +{ + if (!m_man->check_start_conditions(ControlCom::eControlThreaten)) return; + if (!m_object->check_start_conditions(ControlCom::eControlThreaten)) return; + + m_man->capture (this, ControlCom::eControlThreaten); + + SControlThreatenData *ctrl_data = (SControlThreatenData *) m_man->data(this, ControlCom::eControlThreaten); + VERIFY (ctrl_data); + ctrl_data->animation = m_threaten_anim; + ctrl_data->time = m_threaten_time; + + m_man->activate (ControlCom::eControlThreaten); +} + +////////////////////////////////////////////////////////////////////////// +// MELEE JUMP +////////////////////////////////////////////////////////////////////////// + +void CControlManagerCustom::add_melee_jump_data(LPCSTR left,LPCSTR right) +{ + IKinematicsAnimated *skel_animated = smart_cast(m_object->Visual()); + m_melee_jump_data.anim_ls = skel_animated->ID_Cycle_Safe(left); + m_melee_jump_data.anim_rs = skel_animated->ID_Cycle_Safe(right); +} + +void CControlManagerCustom::check_melee_jump() +{ + if (!m_man->check_start_conditions(ControlCom::eControlMeleeJump)) return; + if (!m_object->check_start_conditions(ControlCom::eControlMeleeJump)) return; + + m_man->capture (this, ControlCom::eControlMeleeJump); + + SControlMeleeJumpData *ctrl_data = (SControlMeleeJumpData *) m_man->data(this, ControlCom::eControlMeleeJump); + VERIFY (ctrl_data); + + (*ctrl_data) = m_melee_jump_data; + + m_man->activate (ControlCom::eControlMeleeJump); +} + +////////////////////////////////////////////////////////////////////////// +// Fill Rotation Data +////////////////////////////////////////////////////////////////////////// +void CControlManagerCustom::fill_rotation_data(SControlRotationJumpData &data, LPCSTR left1,LPCSTR left2,LPCSTR right1,LPCSTR right2, float angle, u32 flags) +{ + VERIFY (m_object->Visual()); + IKinematicsAnimated *skeleton_animated = smart_cast(m_object->Visual()); + + data.flags.assign (flags); + data.turn_angle = angle; + + MotionID motion; + if (left1) { + motion = skeleton_animated->ID_Cycle_Safe(left1); + data.anim_stop_ls = motion; + m_object->anim().AddAnimTranslation(motion,left1); + } else { + data.anim_stop_ls.invalidate(); + } + + if (left2) { + motion = skeleton_animated->ID_Cycle_Safe(left2); + data.anim_run_ls = motion; + m_object->anim().AddAnimTranslation(motion,left2); + } else { + data.anim_run_ls.invalidate(); + } + + if (right1) { + motion = skeleton_animated->ID_Cycle_Safe(right1); + data.anim_stop_rs = motion; + m_object->anim().AddAnimTranslation(motion,right1); + } else { + data.anim_stop_rs.invalidate(); + } + + if (right2) { + motion = skeleton_animated->ID_Cycle_Safe(right2); + data.anim_run_rs = motion; + m_object->anim().AddAnimTranslation(motion,right2); + } else { + data.anim_run_rs.invalidate(); + } +} + +////////////////////////////////////////////////////////////////////////// +void CControlManagerCustom::critical_wound(LPCSTR anim) +{ + if (!m_man->check_start_conditions(ControlCom::eComCriticalWound)) + return; + + m_man->capture (this, ControlCom::eComCriticalWound); + + SControlCriticalWoundData *ctrl_data = (SControlCriticalWoundData*)m_man->data(this, ControlCom::eComCriticalWound); + if (!ctrl_data) return; + + ctrl_data->animation = anim; + + m_man->activate(ControlCom::eComCriticalWound); +} +////////////////////////////////////////////////////////////////////////// + + + diff --git a/src/xrGameLA/ai/monsters/control_manager_custom.h b/src/xrGameLA/ai/monsters/control_manager_custom.h new file mode 100644 index 000000000..e16680c8d --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_manager_custom.h @@ -0,0 +1,99 @@ +#pragma once +#include "control_combase.h" + +#include "anim_triple.h" +#include "control_jump.h" +#include "control_rotation_jump.h" +#include "control_melee_jump.h" + + +class CAnimationSequencer; +class CControlRotationJump; +class CControlRunAttack; +class CControlThreaten; +class CControlCriticalWound; + +class CControlManagerCustom : public CControl_ComBase { + typedef CControl_ComBase inherited; + + xr_vector m_nearest; + + CAnimationSequencer *m_sequencer; + CAnimationTriple *m_triple_anim; + + CControlRotationJump *m_rotation_jump; + CControlJump *m_jump; + CControlRunAttack *m_run_attack; + CControlThreaten *m_threaten; + CControlMeleeJump *m_melee_jump; + CControlCriticalWound *m_critical_wound; + + DEFINE_VECTOR (SControlRotationJumpData, ROT_JUMP_DATA_VEC, ROT_JUMP_DATA_VEC_IT); + ROT_JUMP_DATA_VEC m_rot_jump_data; + + SControlMeleeJumpData m_melee_jump_data; + + LPCSTR m_threaten_anim; + float m_threaten_time; + +public: + CControlManagerCustom (); + ~CControlManagerCustom (); + + virtual void reinit (); + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + virtual void on_start_control (ControlCom::EControlType type); + virtual void on_stop_control (ControlCom::EControlType type); + virtual void update_frame (); + virtual void update_schedule (); + + void add_ability (ControlCom::EControlType); + + //------------------------------------------------------------------------------- + // Sequencer + void seq_init (); + void seq_add (MotionID motion); + void seq_switch (); // Перейти в следующее состояние, если такового не имеется - завершить + void seq_run (MotionID motion); + + //------------------------------------------------------------------------------- + // Triple Animation + void ta_activate (const SAnimationTripleData &data); + void ta_pointbreak (); + void ta_fill_data (SAnimationTripleData &data, LPCSTR s1, LPCSTR s2, LPCSTR s3, bool execute_once, bool skip_prep, u32 capture_type = ControlCom::eCaptureDir | ControlCom::eCapturePath | ControlCom::eCaptureMovement); + bool ta_is_active (); + bool ta_is_active (const SAnimationTripleData &data); + void ta_deactivate (); + + //------------------------------------------------------------------------------- + // Jump + void jump (CObject *obj, const SControlJumpData &ta); + void jump (const SControlJumpData &ta); + void jump (const Fvector &position); + void load_jump_data (LPCSTR s1, LPCSTR s2, LPCSTR s3, LPCSTR s4, u32 vel_mask_prepare, u32 vel_mask_ground, u32 flags); + + void script_jump (const Fvector &position, float factor); + + //------------------------------------------------------------------------------- + // Rotation Jump + void add_rotation_jump_data (LPCSTR left1,LPCSTR left2,LPCSTR right1,LPCSTR right2, float angle, u32 flags = 0); + void add_melee_jump_data (LPCSTR left,LPCSTR right); + + //------------------------------------------------------------------------------- + // Threaten Animation + void set_threaten_data (LPCSTR anim, float time) {m_threaten_anim = anim; m_threaten_time = time;} + + void critical_wound (LPCSTR anim); + +private: + + void check_attack_jump (); + void check_jump_over_physics (); + void check_rotation_jump (); + void check_melee_jump (); + void check_run_attack (); + void check_threaten (); + + void fill_rotation_data (SControlRotationJumpData &data, LPCSTR left1,LPCSTR left2,LPCSTR right1,LPCSTR right2, float angle, u32 flags); +}; + diff --git a/src/xrGameLA/ai/monsters/control_melee_jump.cpp b/src/xrGameLA/ai/monsters/control_melee_jump.cpp new file mode 100644 index 000000000..47d3fd170 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_melee_jump.cpp @@ -0,0 +1,93 @@ +#include "stdafx.h" +#include "control_melee_jump.h" +#include "BaseMonster/base_monster.h" +#include "control_manager.h" + +#define CHECK_YAW 165 * PI / 180 +#define ROTATION_JUMP_DELAY_MIN 500 +#define ROTATION_JUMP_DELAY_MAX 1000 +#define MAX_DISTANCE_TO_ENEMY 4.f + +void CControlMeleeJump::reinit() +{ + inherited::reinit(); + + m_time_next_melee_jump = 0; +} + + +bool CControlMeleeJump::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured_pure()) return false; + + if (!m_object->EnemyMan.get_enemy()) return false; + if (m_time_next_melee_jump > Device.dwTimeGlobal) return false; + + Fvector enemy_position; + enemy_position.set (m_object->EnemyMan.get_enemy()->Position()); + if (m_man->direction().is_face_target(enemy_position, CHECK_YAW)) return false; + if (enemy_position.distance_to(m_object->Position()) > MAX_DISTANCE_TO_ENEMY) return false; + + return true; +} + +void CControlMeleeJump::activate() +{ + m_man->capture_pure (this); + m_man->subscribe (this, ControlCom::eventAnimationEnd); + + // disable path builder and movement + m_man->path_stop (this); + m_man->move_stop (this); + + // get direction to enemy + Fvector dir_to_enemy; + dir_to_enemy.set (m_object->Direction()); + dir_to_enemy.sub (m_object->EnemyMan.get_enemy()->Position(), m_object->Position()); + dir_to_enemy.normalize (); + + float target_yaw = angle_normalize(-dir_to_enemy.getH()); + MotionID motion = ((m_man->direction().is_from_right(target_yaw)) ? m_data.anim_rs : m_data.anim_ls ); + float anim_time = m_man->animation().motion_time(motion, m_object->Visual()); + + // set yaw + SControlDirectionData *ctrl_data_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_data_dir); + ctrl_data_dir->heading.target_angle = target_yaw; + + // set angular speed + float cur_yaw; + m_man->direction().get_heading (cur_yaw, target_yaw); + ctrl_data_dir->heading.target_speed = angle_difference(cur_yaw,target_yaw)/ anim_time; + ctrl_data_dir->linear_dependency = false; + VERIFY (!fis_zero(ctrl_data_dir->heading.target_speed)); + + // set animation + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_data); + + ctrl_data->global.motion = motion; + ctrl_data->global.actual = false; +} + +void CControlMeleeJump::on_release() +{ + SControlDirectionData *ctrl_data_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_data_dir); + ctrl_data_dir->linear_dependency = true; + + m_man->release_pure (this); + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); + + m_time_next_melee_jump = Device.dwTimeGlobal + Random.randI(ROTATION_JUMP_DELAY_MIN,ROTATION_JUMP_DELAY_MAX); +} + +void CControlMeleeJump::on_event(ControlCom::EEventType type, ControlCom::IEventData *dat) +{ + switch (type) { + case ControlCom::eventAnimationEnd: + m_man->notify (ControlCom::eventMeleeJumpEnd, 0); + break; + } +} diff --git a/src/xrGameLA/ai/monsters/control_melee_jump.h b/src/xrGameLA/ai/monsters/control_melee_jump.h new file mode 100644 index 000000000..dc9da4a82 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_melee_jump.h @@ -0,0 +1,24 @@ +#pragma once + +#include "control_combase.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" + +struct SControlMeleeJumpData : public ControlCom::IComData { + MotionID anim_ls; + MotionID anim_rs; +}; + +class CControlMeleeJump : public CControl_ComCustom { + typedef CControl_ComCustom inherited; + + u32 m_time_next_melee_jump; + +public: + virtual void reinit (); + + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + virtual void activate (); + virtual void on_release (); + virtual bool check_start_conditions (); +}; + diff --git a/src/xrGameLA/ai/monsters/control_movement.cpp b/src/xrGameLA/ai/monsters/control_movement.cpp new file mode 100644 index 000000000..f66935f31 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_movement.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" +#include "control_movement.h" +#include "BaseMonster/base_monster.h" +#include "control_manager.h" +#include "../../PHMovementControl.h" +#include "../../CharacterPhysicsSupport.h" +void CControlMovement::reinit() +{ + inherited::reinit (); + + m_velocity_current = 0.f; + + m_data.velocity_target = 0.f; + m_data.acc = flt_max; +} + +void CControlMovement::update_frame() +{ + velocity_lerp (m_velocity_current, m_data.velocity_target, m_data.acc, m_object->client_update_fdelta()); + + m_object->m_fCurSpeed = m_velocity_current; + m_man->path_builder().set_desirable_speed (m_velocity_current); +} + +float CControlMovement::real_velocity() +{ + CPHMovementControl *movement_control = m_object->character_physics_support()->movement(); + VERIFY (movement_control); + + if (movement_control->IsCharacterEnabled()){ + float tmp = movement_control->GetXZActVelInGoingDir(); +#ifdef DEBUG + if (_abs(tmp) > 1000) { + Log ("! GetVelocity",movement_control->GetVelocity()); + Log ("! GetPathDir",movement_control->GetPathDir()); + } +#endif // DEBUG + clamp (tmp, 0.0f, 15.0f); + VERIFY2 (_abs(tmp)<1000,"movement_control->GetXZActVelInGoingDir() returns too big speed"); + return tmp; + } + + return m_velocity_current; +} \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/control_movement.h b/src/xrGameLA/ai/monsters/control_movement.h new file mode 100644 index 000000000..17c2347b8 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_movement.h @@ -0,0 +1,24 @@ +#pragma once + +#include "control_combase.h" + +struct SControlMovementData : public ControlCom::IComData { + float velocity_target; + float acc; +}; + +class CControlMovement : public CControl_ComPure { + typedef CControl_ComPure inherited; + + float m_velocity_current; + +public: + virtual void reinit (); + virtual void update_frame (); + + float velocity_current () {return m_velocity_current;} + float velocity_target () {return m_data.velocity_target;} + + // return object's real velocity + float real_velocity (); +}; \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/control_movement_base.cpp b/src/xrGameLA/ai/monsters/control_movement_base.cpp new file mode 100644 index 000000000..af12ab7f5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_movement_base.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "control_movement_base.h" +#include "control_animation_base.h" +#include "control_direction_base.h" +#include "BaseMonster/base_monster.h" +#include "monster_velocity_space.h" +#include "../../detail_path_manager.h" + +using namespace MonsterMovement; + +void CControlMovementBase::reinit() +{ + inherited::reinit (); + + stop (); + + m_man->capture (this, ControlCom::eControlMovement); +} + +void CControlMovementBase::load(LPCSTR section) +{ + load_velocity (section, "Velocity_Stand", eVelocityParameterStand); + load_velocity (section, "Velocity_WalkFwdNormal", eVelocityParameterWalkNormal); + load_velocity (section, "Velocity_RunFwdNormal", eVelocityParameterRunNormal); + load_velocity (section, "Velocity_WalkFwdDamaged",eVelocityParameterWalkDamaged); + load_velocity (section, "Velocity_RunFwdDamaged", eVelocityParameterRunDamaged); + load_velocity (section, "Velocity_Steal", eVelocityParameterSteal); + load_velocity (section, "Velocity_Drag", eVelocityParameterDrag); + load_velocity (section, "Velocity_RunAttack", eVelocityParameterRunAttack); + + // add idle velocity + SVelocityParam velocity_param; + m_velocities.insert(mk_pair(eVelocityParameterIdle, velocity_param)); + m_man->path_builder().detail().add_velocity(eVelocityParameterIdle, CDetailPathManager::STravelParams(velocity_param.velocity.linear,velocity_param.velocity.angular_path,velocity_param.velocity.angular_real)); +} + +void CControlMovementBase::load_velocity(LPCSTR section, LPCSTR line, u32 velocity_id) +{ + SVelocityParam velocity_param; + if(pSettings->line_exist(section,line)) velocity_param.Load(section,line); + m_velocities.insert(mk_pair(velocity_id, velocity_param)); + + m_man->path_builder().detail().add_velocity(velocity_id, CDetailPathManager::STravelParams(velocity_param.velocity.linear,velocity_param.velocity.angular_path,velocity_param.velocity.angular_real)); +} + +SVelocityParam &CControlMovementBase::get_velocity(u32 velocity_id) +{ + VELOCITY_MAP_IT it = m_velocities.find(velocity_id); + VERIFY(it != m_velocities.end()); + + return it->second; +} + +void CControlMovementBase::update_frame() +{ + SControlMovementData *ctrl_data = (SControlMovementData *)m_man->data(this, ControlCom::eControlMovement); + if (!ctrl_data) return; + + ctrl_data->velocity_target = m_velocity; + ctrl_data->acc = m_accel; +} + +void CControlMovementBase::set_velocity(float val, bool max_acc) +{ + m_velocity = val; + if (max_acc) m_accel = flt_max; + else { + m_accel = ((m_man->movement().velocity_current() > m_velocity) ? + m_object->anim().accel_get(eAV_Braking) : + m_object->anim().accel_get(eAV_Accel)); + } +} + +void CControlMovementBase::stop() +{ + m_velocity = 0.f; + m_accel = flt_max; +} + +void CControlMovementBase::stop_accel() +{ + m_velocity = 0.f; + m_accel = ((m_man->movement().velocity_current() > m_velocity) ? + m_object->anim().accel_get(eAV_Braking) : + flt_max); +} + +float CControlMovementBase::get_velocity_from_path() +{ + if (m_man->path_builder().path().empty()) return 0.f; + if (!m_man->path_builder().enabled()) return 0.f; + + // get target velocity from path + float velocity = 0.f; + + CDetailPathManager &detail = m_man->path_builder().detail(); + + u32 cur_point_velocity_index = detail.path()[detail.curr_travel_point_index()].velocity; + u32 next_point_velocity_index = u32(-1); + + if (detail.path().size() > detail.curr_travel_point_index() + 1) + next_point_velocity_index = detail.path()[detail.curr_travel_point_index() + 1].velocity; + + const CDetailPathManager::STravelParams ¤t_velocity = detail.velocity(cur_point_velocity_index); + if (fis_zero(current_velocity.linear_velocity) && (next_point_velocity_index != u32(-1))) { + const CDetailPathManager::STravelParams &next_velocity = detail.velocity(next_point_velocity_index); + velocity = _abs(next_velocity.linear_velocity); + m_object->dir().set_heading_speed (next_velocity.real_angular_velocity); + } else { + velocity = _abs(current_velocity.linear_velocity); + m_object->dir().set_heading_speed (current_velocity.real_angular_velocity); + } + + return velocity; + //m_accel = ((m_man->movement().velocity_current() > velocity) ? + // m_object->anim().accel_get(eAV_Braking) : + //m_object->anim().accel_get(eAV_Accel)); +} diff --git a/src/xrGameLA/ai/monsters/control_movement_base.h b/src/xrGameLA/ai/monsters/control_movement_base.h new file mode 100644 index 000000000..356ba9cbc --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_movement_base.h @@ -0,0 +1,32 @@ +#pragma once + +#include "control_combase.h" +#include "ai_monster_defs.h" + +class CControlMovementBase : public CControl_ComBase { + typedef CControl_ComBase inherited; + + DEFINE_MAP (u32, SVelocityParam, VELOCITY_MAP, VELOCITY_MAP_IT); + VELOCITY_MAP m_velocities; + + float m_velocity; + float m_accel; + +public: + virtual void load (LPCSTR section); + + virtual void reinit (); + virtual void update_frame (); + + void load_velocity (LPCSTR section, LPCSTR line, u32 param); + SVelocityParam &get_velocity (u32 velocity_id); + + + void stop (); + void stop_accel (); + void set_velocity (float val, bool max_acc = false); + void set_accel (float val) {m_accel = val;} + + // services + float get_velocity_from_path (); +}; \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/control_path_builder.cpp b/src/xrGameLA/ai/monsters/control_path_builder.cpp new file mode 100644 index 000000000..03a81275a --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_path_builder.cpp @@ -0,0 +1,297 @@ +#include "stdafx.h" +#include "control_path_builder.h" +#include "control_manager.h" +#include "BaseMonster/base_monster.h" +#include "../../game_location_selector.h" +#include "../../level_location_selector.h" +#include "../../detail_path_manager.h" +#include "../../ai_object_location.h" +#include "../../ai_space.h" +#include "../../movement_manager_space.h" +#include "../../level_path_manager.h" +#include "../../actor.h" +#include "../../Actor_Memory.h" +#include "../../visual_memory_manager.h" + +#ifdef DEBUG +extern bool show_restrictions(CRestrictedObject *object); +#endif + +CControlPathBuilder::CControlPathBuilder(CCustomMonster *monster) : CMovementManager(monster) +{ +} + +CControlPathBuilder::~CControlPathBuilder() +{ +} + +void CControlPathBuilder::load(LPCSTR section) +{ +} + +void CControlPathBuilder::reinit() +{ + // todo: remove call twice [CustomMonster reinit && control_manager reinit] + inherited::reinit (); + inherited_com::reinit (); + + m_data.use_dest_orientation = false; + m_data.dest_orientation.set (0.f,0.f,0.f); + + m_data.try_min_time = true; + + m_data.target_position.set (0.f,0.f,0.f); + m_data.target_node = u32(-1); + + m_data.enable = false; + m_data.extrapolate = false; + + m_data.velocity_mask = u32(-1); + m_data.desirable_mask = u32(-1); + + m_data.path_type = MovementManager::ePathTypeLevelPath; + m_data.game_graph_target_vertex = u32(-1); +} + +void CControlPathBuilder::update_schedule() +{ + START_PROFILE("Base Monster/Path Builder/Schedule Update"); + + // the one and only reason is because of the restriction-change, so wait until + // position and node will be in valid state + if (m_data.path_type != MovementManager::ePathTypePatrolPath) { + if (!accessible(m_data.target_position) || + ( (m_data.target_node != u32(-1)) && (!accessible(m_data.target_node)) )) { + return; + } + } + + if (m_data.reset_actuality) make_inactual(); + + // set enabled + enable_movement (m_data.enable); + + // set params only if enable params + if (m_data.enable) { + detail().set_path_type (eDetailPathTypeSmooth); + + // установить direction + detail().set_use_dest_orientation (m_data.use_dest_orientation); + if (m_data.use_dest_orientation) detail().set_dest_direction (m_data.dest_orientation); + + detail().set_try_min_time (m_data.try_min_time); + + extrapolate_path (m_data.extrapolate); + + detail().set_velocity_mask (m_data.velocity_mask); + detail().set_desirable_mask (m_data.desirable_mask); + + set_path_type (m_data.path_type); + if (m_data.path_type == MovementManager::ePathTypeGamePath) { + // check if we have alife task + if (m_data.game_graph_target_vertex != u32(-1)) { + set_game_dest_vertex (GameGraph::_GRAPH_ID(m_data.game_graph_target_vertex)); + game_selector().set_selection_type (eSelectionTypeMask); + } else + // else just wandering through the game graph + game_selector().set_selection_type (eSelectionTypeRandomBranching); + } else if (m_data.path_type != MovementManager::ePathTypePatrolPath) { + // set target + // TODO: make it VERIFY +// VERIFY3(m_data.target_node != u32(-1), "Error: Object set wrong path params! Object name:",*inherited_com::m_object->cName()); + if (m_data.target_node == u32(-1)) return; + + detail().set_dest_position (m_data.target_position); + set_level_dest_vertex (m_data.target_node); + } + } + + update_path (); + + m_man->notify (ControlCom::eventPathUpdated, 0); + + STOP_PROFILE; +} + +void CControlPathBuilder::on_travel_point_change(const u32 &previous_travel_point_index) +{ + inherited::on_travel_point_change (previous_travel_point_index); + m_man->notify (ControlCom::eventTravelPointChange, 0); +} + +void CControlPathBuilder::on_build_path() +{ + m_man->notify (ControlCom::eventPathBuilt, 0); +} + + +////////////////////////////////////////////////////////////////////////// +// Special Build Path +////////////////////////////////////////////////////////////////////////// +bool CControlPathBuilder::build_special(const Fvector &target, u32 node, u32 vel_mask) +{ + if (!accessible(target)) return false; + + if (node == u32(-1)) { + // нода в прямой видимости? + restrictions().add_border(object().Position(), target); + node = ai().level_graph().check_position_in_direction(object().ai_location().level_vertex_id(),object().Position(),target); + restrictions().remove_border(); + + if (!ai().level_graph().valid_vertex_id(node) || !accessible(node)) return false; + } + + enable_movement (true); + + detail().set_velocity_mask (vel_mask); + detail().set_desirable_mask (vel_mask); + + detail().set_try_min_time (false); + detail().set_use_dest_orientation (false); + + detail().set_path_type (eDetailPathTypeSmooth); + set_path_type (MovementManager::ePathTypeLevelPath); + + detail().set_dest_position (target); + set_level_dest_vertex (node); + + set_build_path_at_once (); + update_path (); + + // check if path built successfully + if (!path_completed() && (detail().time_path_built() >= Device.dwTimeGlobal)) return true; + + return false; +} + +////////////////////////////////////////////////////////////////////////// +// Services +////////////////////////////////////////////////////////////////////////// + +bool CControlPathBuilder::is_path_end(float dist_to_end) +{ + if (!is_path_built()) return false; + if (path_completed()) return true; + if (!is_moving_on_path()) return true; + + u32 cur_point_idx = detail().curr_travel_point_index(); + u32 path_size = detail().path().size(); + if (path_size < 2) return true; + if (cur_point_idx + 1 >= path_size) return true; + + // count distance from current object position to the path end + float cur_dist_to_end = object().Position().distance_to(detail().path()[detail().curr_travel_point_index()+1].position); + for (u32 i=detail().curr_travel_point_index()+1; i dist_to_end) break; + } + + if ((cur_dist_to_end < dist_to_end)) return true; + return false; +} + +bool CControlPathBuilder::valid_destination(const Fvector &pos, u32 node) +{ + return ( + ai().level_graph().valid_vertex_id(node) && + ai().level_graph().valid_vertex_position(pos) && + ai().level_graph().inside(node, pos) + ); +} + +bool CControlPathBuilder::valid_and_accessible(Fvector &pos, u32 node) +{ + if (!valid_destination(pos, node) || !accessible(node)) return false; + + //if (m_data.game_graph_target_vertex == u32(0)) return false; + + fix_position(Fvector().set(pos),node,pos); + return true; +} + + + +void CControlPathBuilder::fix_position(const Fvector &pos, u32 node, Fvector &res_pos) +{ + VERIFY(accessible(node)); + VERIFY(ai().level_graph().inside(node, pos)); + + res_pos.set (pos); + res_pos.y = ai().level_graph().vertex_plane_y(node,res_pos.x,res_pos.z); + + if (!accessible(res_pos)) { + u32 level_vertex_id = restrictions().accessible_nearest(Fvector().set(res_pos),res_pos); + +#ifdef DEBUG + if (level_vertex_id != node) { + Msg ("! src_node[%d] res_node[%d] src_pos[%f,%f,%f] res_pos[%f,%f,%f]",node,level_vertex_id,VPUSH(pos),VPUSH(res_pos)); + } + VERIFY3((level_vertex_id == node) || show_restrictions(m_restricted_object),"Invalid restrictions (see log for details) for object ",*(CControl_Com::m_object->cName())); +#endif + } +} + +bool CControlPathBuilder::is_moving_on_path() +{ + return (!detail().completed(inherited_com::m_object->Position()) && enabled()); +} + +bool CControlPathBuilder::get_node_in_radius(u32 src_node, float min_radius, float max_radius, u32 attempts, u32 &dest_node) +{ + Fvector vertex_position = ai().level_graph().vertex_position(src_node); + + for (u32 i=0; imemory().visual().visible_right_now(inherited_com::m_object)) return false; + return inherited::can_use_distributed_computations(option); +} + +u32 CControlPathBuilder::find_nearest_vertex (const u32 &level_vertex_id, const Fvector &target_position, const float &range) +{ + xr_vector temp; + + ai().graph_engine().search ( + ai().level_graph(), + level_vertex_id, + level_vertex_id, + &temp, + GraphEngineSpace::CNearestVertexParameters( + target_position, + range + ) + ); + + VERIFY (!temp.empty()); + VERIFY (temp.size() == 1); + return (temp.front()); +} + +bool CControlPathBuilder::is_path_built() +{ + return actual_all(); +} + diff --git a/src/xrGameLA/ai/monsters/control_path_builder.h b/src/xrGameLA/ai/monsters/control_path_builder.h new file mode 100644 index 000000000..b8d8a7213 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_path_builder.h @@ -0,0 +1,68 @@ +#pragma once + +#include "control_combase.h" +#include "../../movement_manager.h" + +class CCustomMonster; +class CControl_Manager; + +struct SControlPathBuilderData : public ControlCom::IComData { + bool use_dest_orientation; + Fvector dest_orientation; + + bool try_min_time; + + Fvector target_position; + u32 target_node; + + bool enable; + bool extrapolate; + + u32 velocity_mask; + u32 desirable_mask; + + bool reset_actuality; + + MovementManager::EPathType path_type; + u32 game_graph_target_vertex; +}; + + +class CControlPathBuilder : + public CControl_ComPure, + public CMovementManager +{ + typedef CMovementManager inherited; + typedef CControl_ComPure inherited_com; + + friend class CControl_Manager; + +public: + CControlPathBuilder (CCustomMonster *monster); + virtual ~CControlPathBuilder (); + + virtual void load (LPCSTR section); + virtual void reinit (); + virtual void update_schedule (); + + virtual void on_travel_point_change (const u32 &previous_travel_point_index); + virtual void on_build_path (); + virtual bool can_use_distributed_computations (u32 option) const; + + // services + bool is_path_end (float dist_to_end); + bool valid_destination (const Fvector &pos, u32 node); + bool valid_and_accessible (Fvector &pos, u32 node); // validate with a small correction + bool is_moving_on_path (); + + bool get_node_in_radius (u32 src_node, float min_radius, float max_radius, u32 attempts, u32 &dest_node); + void fix_position (const Fvector &pos, u32 node, Fvector &res_pos); + + static u32 find_nearest_vertex (const u32 &level_vertex_id, const Fvector &target_position, const float &range); + + bool is_path_built (); + +private: + bool build_special (const Fvector &target, u32 node, u32 vel_mask); + void make_inactual (); +}; diff --git a/src/xrGameLA/ai/monsters/control_path_builder_base.cpp b/src/xrGameLA/ai/monsters/control_path_builder_base.cpp new file mode 100644 index 000000000..33cd1c845 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_path_builder_base.cpp @@ -0,0 +1,167 @@ +#include "stdafx.h" +#include "control_path_builder_base.h" +#include "BaseMonster/base_monster.h" +#include "../../phmovementcontrol.h" +#include "../../cover_evaluators.h" +#include "../../level_path_manager.h" +#include "../../detail_path_manager.h" +#include "../../level_location_selector.h" +#include "../../ai_object_location.h" + +const u32 pmt_global_failed_duration = 3000; + +////////////////////////////////////////////////////////////////////////// +// Construction / Destruction +////////////////////////////////////////////////////////////////////////// + +CControlPathBuilderBase::CControlPathBuilderBase() +{ + m_cover_approach = 0; +} + +CControlPathBuilderBase::~CControlPathBuilderBase() +{ + xr_delete(m_cover_approach); +} + +void CControlPathBuilderBase::reinit() +{ + inherited::reinit (); + + if (!m_cover_approach) + m_cover_approach = new CCoverEvaluatorCloseToEnemy(&m_man->path_builder().restrictions()); + + reset (); + + m_man->capture (this, ControlCom::eControlPath); +} + +void CControlPathBuilderBase::reset() +{ + m_try_min_time = false; + m_use_dest_orient = false; + m_enable = false; + m_path_type = MovementManager::ePathTypeLevelPath; + m_dest_dir.set (0.f, 0.f, 0.f); + m_extrapolate = false; + m_velocity_mask = u32(-1); + m_desirable_mask = u32(-1); + m_last_time_dir_set = 0; + m_last_time_target_set = 0; + m_reset_actuality = false; + m_game_graph_target_vertex = u32(-1); + + prepare_builder (); + + m_path_end = false; +} + + +void CControlPathBuilderBase::on_event(ControlCom::EEventType type, ControlCom::IEventData *data) +{ + switch (type) { + case ControlCom::eventPathBuilt: on_path_built(); break; + case ControlCom::eventPathUpdated: on_path_updated(); break; + case ControlCom::eventTravelPointChange: travel_point_changed(); break; + } +} + +void CControlPathBuilderBase::on_start_control(ControlCom::EControlType type) +{ + switch (type) { + case ControlCom::eControlPath: m_man->subscribe (this, ControlCom::eventPathBuilt); + m_man->subscribe (this, ControlCom::eventTravelPointChange); + m_man->subscribe (this, ControlCom::eventPathUpdated); + break; + } +} + +void CControlPathBuilderBase::on_stop_control(ControlCom::EControlType type) +{ + switch (type) { + case ControlCom::eControlPath: m_man->unsubscribe (this, ControlCom::eventPathBuilt); + m_man->unsubscribe (this, ControlCom::eventTravelPointChange); + m_man->unsubscribe (this, ControlCom::eventPathUpdated); + break; + } +} + +void CControlPathBuilderBase::detour_graph_points(u32 game_graph_vertex_id) +{ + m_game_graph_target_vertex = game_graph_vertex_id; + set_game_path_type (); +} + +void CControlPathBuilderBase::set_dest_direction(const Fvector &dir) +{ + if (m_last_time_dir_set + m_time > time()) return; + m_dest_dir.set (dir); + m_last_time_dir_set = time(); +} + +void CControlPathBuilderBase::set_target_accessible(STarget &target, const Fvector &position) +{ + if (!m_man->path_builder().accessible(position)) + { + Fvector new_position; + target.set_node ( m_man->path_builder().restrictions().accessible_nearest( position, new_position ) ); + target.set_position( new_position ); + } else { + target.set_node ( u32(-1) ); + target.set_position ( position ); + } +} + +// обновит ь информацию о построенном пути (m_failed) +void CControlPathBuilderBase::on_path_built() +{ + // проверка на конец пути + if (!m_man->path_builder().detail().path().empty() && + (m_man->path_builder().detail().curr_travel_point_index() < m_man->path_builder().detail().path().size() - 1)) + m_path_end = false; + +} + +void CControlPathBuilderBase::on_path_updated() +{ + // если level_path_manager failed + if (m_man->path_builder().level_path().failed()) { + m_failed = true; + m_man->path_builder().level_path().reset (); + VERIFY(!m_man->path_builder().level_path().failed()); + } + + // если level_path_manager failed + if (m_man->path_builder().detail().failed()) + m_failed = true; + + + // проверка на конец пути, если этот путь не конечный + if ((m_man->path_builder().detail().path().empty() || + (m_man->path_builder().detail().curr_travel_point_index() >= m_man->path_builder().detail().path().size() - 1)) && + m_man->path_builder().detail().actual() && + m_man->path_builder().enabled() && + // конечный путь? + m_target_set.node() != m_object->ai_location().level_vertex_id() && m_target_actual) { + m_failed = true; + } + + m_time_path_updated_external = time(); +} + +void CControlPathBuilderBase::on_path_end() +{ + m_path_end = true; +} + +void CControlPathBuilderBase::travel_point_changed() +{ + if (m_man->path_builder().detail().curr_travel_point_index() >= m_man->path_builder().detail().path().size() - 1) { + on_path_end(); + } +} + +bool CControlPathBuilderBase::global_failed() +{ + return (m_time_global_failed_started + pmt_global_failed_duration > time()); +} \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/control_path_builder_base.h b/src/xrGameLA/ai/monsters/control_path_builder_base.h new file mode 100644 index 000000000..d201c4941 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_path_builder_base.h @@ -0,0 +1,178 @@ +#pragma once + +#include "ai_monster_defs.h" +#include "control_combase.h" +#include "../../movement_manager_space.h" + +class CMotionStats; +class CCoverEvaluatorCloseToEnemy; + +class CControlPathBuilderBase : public CControl_ComBase { + typedef CControl_ComBase inherited; + + // ----------------------------------------------------------- + // external setup + bool m_try_min_time; + bool m_enable; + bool m_use_dest_orient; + Fvector m_dest_dir; + MovementManager::EPathType m_path_type; + bool m_extrapolate; + u32 m_velocity_mask; + u32 m_desirable_mask; + bool m_reset_actuality; + u32 m_game_graph_target_vertex; + + // ----------------------------------------------------------- + // build path members + // ----------------------------------------------------------- + + class STarget { + Fvector _position; + u32 _node; + public: + STarget() + { + _position.set( -FLT_MAX, -FLT_MAX, -FLT_MAX ); + _node =u32(-1); + } + void init () { + _position.set (0.f,0.f,0.f); + _node = u32(-1); + } + + void set (const Fvector &pos, u32 vertex) { + _position.set (pos); + _node = vertex; + } + IC const Fvector &position ()const { return _position; } + //IC Fvector &position () { return _position; } + IC u32 node ()const { return _node; } + IC void set_node ( u32 node_ ) { _node = node_ ; } + IC void set_position( const Fvector &p ){ _position.set(p); } + } m_target_set, m_target_found; + + u32 m_time; // время перестроения пути + u32 m_last_time_target_set; + float m_distance_to_path_end; + bool m_failed; + u32 m_last_time_dir_set; + + bool m_target_actual; // устанавливаемый таргет соответствует предыдущему + + struct { + bool use_covers; + float min_dist; + float max_dist; + float deviation; + float radius; + } m_cover_info; + + enum { + eMoveToTarget, + eRetreatFromTarget, + } m_target_type; + + CCoverEvaluatorCloseToEnemy *m_cover_approach; + + // ----------------------------------------------------------- + + enum { + eStatePathValid = u32(1) << 0, + eStateWaitNewPath = u32(1) << 1, + eStatePathEnd = u32(1) << 2, + eStateNoPath = u32(1) << 3, + eStatePathFailed = u32(1) << 4 + }; + u32 m_state; + + bool m_path_end; + + // состояние, в котором path_builder работает независимо + u32 m_time_global_failed_started; + u32 m_time_path_updated_external; + +public: + CControlPathBuilderBase (); + virtual ~CControlPathBuilderBase (); + + // ------------------------------------------------------------------- + // Control Interface + virtual void reinit (); + virtual void update_frame (); + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + virtual void on_start_control (ControlCom::EControlType type); + virtual void on_stop_control (ControlCom::EControlType type); + + // ------------------------------------------------------------------- + + void pre_update (); + + + // ------------------------------------------------------------------- + IC void set_try_min_time (bool new_val) {m_try_min_time = new_val;} + IC void set_use_dest_orient (bool new_val) {m_use_dest_orient = new_val;} + IC void disable_path () {m_enable = false;} + IC void enable_path () {m_enable = true;} + IC void extrapolate_path (bool val) {m_extrapolate = val;} + IC void set_level_path_type () {m_path_type = MovementManager::ePathTypeLevelPath;} + IC void set_game_path_type () {m_path_type = MovementManager::ePathTypeGamePath;} + IC void set_patrol_path_type () {m_path_type = MovementManager::ePathTypePatrolPath;} + IC void set_velocity_mask (u32 mask) {m_velocity_mask = mask;} + IC void set_desirable_mask (u32 mask) {m_desirable_mask = mask;} + void set_dest_direction (const Fvector &dir); + + IC bool enabled () {return m_enable;} + // ------------------------------------------------------------------- + // Set methods + void set_target_point (const Fvector &position, u32 node = u32(-1)); + void set_target_point (u32 node); + void set_retreat_from_point (const Fvector &position); + + IC void set_rebuild_time (u32 time); + IC void set_cover_params (float min, float max, float dev, float radius); + IC void set_use_covers (bool val = true); + IC void set_distance_to_end (float dist); + + void prepare_builder (); + void detour_graph_points (u32 game_graph_vertex_id = u32(-1)); + IC void set_generic_parameters (); + + + bool is_target_actual () const {return m_target_actual;} + Fvector get_target_found () {return m_target_found.position();} + u32 get_target_found_node () const {return m_target_found.node();} + Fvector get_target_set () {return m_target_set.position();} + + // ------------------------------------------------------------------- + // Services + void set_target_accessible (STarget &target, const Fvector &position); + +private: + // functional + void update_path_builder_state (); + void update_target_point (); + void check_failure (); + + bool target_point_need_update (); + void find_target_point_set (); + void find_target_point_failed (); + + void select_target (); // выбрать + + void set_path_builder_params (); // set params to control + + void reset (); + + void travel_point_changed (); + void on_path_built (); + void on_path_end (); + void on_path_updated (); + + // нашли позицию, найти ноду + void find_node (); + + bool global_failed (); +}; + +#include "control_path_builder_base_inline.h" diff --git a/src/xrGameLA/ai/monsters/control_path_builder_base_inline.h b/src/xrGameLA/ai/monsters/control_path_builder_base_inline.h new file mode 100644 index 000000000..89bbaa03b --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_path_builder_base_inline.h @@ -0,0 +1,32 @@ +#pragma once + +IC void CControlPathBuilderBase::set_cover_params(float min, float max, float dev, float radius) +{ + m_cover_info.min_dist = min; + m_cover_info.max_dist = max; + m_cover_info.deviation = dev; + m_cover_info.radius = radius; +} + +IC void CControlPathBuilderBase::set_use_covers(bool val) +{ + m_cover_info.use_covers = val; +} + +IC void CControlPathBuilderBase::set_rebuild_time(u32 time) +{ + m_time = time; +} + +IC void CControlPathBuilderBase::set_distance_to_end(float dist) +{ + m_distance_to_path_end = dist; +} + +IC void CControlPathBuilderBase::set_generic_parameters() +{ + CControlPathBuilderBase::set_rebuild_time (5000); + CControlPathBuilderBase::set_distance_to_end (3.f); + CControlPathBuilderBase::set_use_covers (); + CControlPathBuilderBase::set_cover_params (5.f, 30.f, 1.f, 30.f); +} diff --git a/src/xrGameLA/ai/monsters/control_path_builder_base_path.cpp b/src/xrGameLA/ai/monsters/control_path_builder_base_path.cpp new file mode 100644 index 000000000..8be4421cf --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_path_builder_base_path.cpp @@ -0,0 +1,199 @@ +#include "stdafx.h" +#include "control_path_builder_base.h" +#include "../../cover_point.h" +#include "../../cover_manager.h" +#include "../../cover_evaluators.h" +#include "BaseMonster/base_monster.h" +#include "../../detail_path_manager.h" +#include "../../level_location_selector.h" +#include "../../level_path_manager.h" +#include "../../ai_object_location.h" + +const float pmt_find_point_dist = 30.f; +const u32 pmt_find_random_pos_attempts = 5; + +////////////////////////////////////////////////////////////////////////// +bool CControlPathBuilderBase::target_point_need_update() +{ + if ((m_state & eStatePathFailed) == eStatePathFailed) + return true; + else if (m_state == eStatePathValid) { + + // если путь ещё не завершен + if (!m_man->path_builder().is_path_end(m_distance_to_path_end)) { + + if (m_target_actual && !global_failed()) return false; // если global_failed - игнорировать актуальность + + // если первый раз строим + if (m_last_time_target_set == 0) return true; + + // если время движения по пути не вышло, не перестраивать + return (m_last_time_target_set + m_time < time()); + } + + //return (!m_target_actual); // логический конец пути + return (true); + //} else if ((m_state & eStateWaitParamsApplied) == eStateWaitParamsApplied) { + // return false; + } else if ((m_state & eStateWaitNewPath) == eStateWaitNewPath) { + return false; + } else if ((m_state & eStateNoPath) == eStateNoPath) { + return true; + } else if ((m_state & eStatePathEnd) == eStatePathEnd) { + if (m_target_set.node() != m_object->ai_location().level_vertex_id()) + return true; // физический конец пути + } + + return false; +} + +////////////////////////////////////////////////////////////////////////// +// Нахождение m_target_found +// На входе есть установленные нода и позиция m_target_set +void CControlPathBuilderBase::find_target_point_set() +{ + m_target_found.set(m_target_set.position(),m_target_set.node()); + + //--------------------------------------------------- + // Быстрые тесты + + if (m_target_type == eMoveToTarget) { + // 1. быстрый тест на достижимость цели + Fvector new_position = m_target_found.position(); + if (m_man->path_builder().valid_and_accessible( new_position, m_target_found.node())) + { + m_target_found.set_position( new_position ); + return; + } + m_target_found.set_position( new_position ); + // 2. быстрый тест на недостижимость цели (выбрать случайную позицию) + if (!m_man->path_builder().accessible(m_target_found.position())) { + Fvector new_position = m_target_found.position(); + m_target_found.set_node( m_man->path_builder().restrictions().accessible_nearest(m_target_found.position(), new_position ) ); + m_target_found.set_position( new_position ); + Fvector pos_random; + Fvector dir; + dir.random_dir (); + + pos_random.mad (m_object->Position(), dir, pmt_find_point_dist); + set_target_accessible (m_target_found, pos_random); + + if (m_target_found.node() != u32(-1)) return; + } + } + + m_target_found.set_node (u32(-1)); + + //--------------------------------------------------- + // I. Выбрать позицию + + if (m_target_type == eRetreatFromTarget) { + Fvector dir; + + dir.sub (m_object->Position(), m_target_found.position() ); + dir.normalize_safe (); + m_target_found.set_position( Fvector( m_target_found.position() ).mad (m_object->Position(), dir, pmt_find_point_dist) ); + } + + // проверить позицию на accessible + if (!m_man->path_builder().accessible(m_target_found.position())) { + Fvector new_position = m_target_found.position(); + m_target_found.set_node ( m_man->path_builder().restrictions().accessible_nearest( Fvector().set( m_target_found.position() ), new_position ) ); + m_target_found.set_position( new_position ); + } + + // если новая позиция = позиции монстра - выбрать рандомную валидную позицию + for (u32 i = 0; i < pmt_find_random_pos_attempts; i++ ) { + if (m_target_found.position().similar(m_object->Position(), 0.5f)) { + + Fvector pos_random; + Fvector dir; + dir.random_dir (); + + pos_random.mad (m_object->Position(), dir, pmt_find_point_dist); + set_target_accessible (m_target_found, pos_random); + } else break; + } + + if (m_target_found.node() != u32(-1)) return; + + if (!ai().level_graph().valid_vertex_position(m_target_found.position())) + { + find_target_point_failed(); + return; + } + //--------------------------------------------------- + // II. Выбрана позиция, ищем ноду + + find_node(); +} + +////////////////////////////////////////////////////////////////////////// +// if path FAILED +void CControlPathBuilderBase::find_target_point_failed() +{ + // если новая позиция = позиции монстра - выбрать рандомную валидную позицию + for (u32 i = 0; i < pmt_find_random_pos_attempts; i++ ) { + Fvector pos_random; + Fvector dir; + dir.random_dir (); + + pos_random.mad (m_object->Position(), dir, pmt_find_point_dist); + set_target_accessible (m_target_found, pos_random); + + if (!m_target_found.position().similar(m_object->Position(), 0.5f)) break; + } + + if (m_target_found.node() != u32(-1)) return; + + //--------------------------------------------------- + // II. Выбрана позиция, ищем ноду + find_node(); +} + + + +void CControlPathBuilderBase::find_node() +{ + // нода в прямой видимости? + m_man->path_builder().restrictions().add_border (m_object->Position(), m_target_found.position()); + m_target_found.set_node ( ai().level_graph().check_position_in_direction(m_object->ai_location().level_vertex_id(),m_object->Position(),m_target_found.position()) ); + m_man->path_builder().restrictions().remove_border (); + + if (ai().level_graph().valid_vertex_id(m_target_found.node()) && m_man->path_builder().accessible(m_target_found.node())) { + // корректировка позиции + Fvector new_position=m_target_found.position(); + m_man->path_builder().fix_position(Fvector().set(m_target_found.position()), m_target_found.node(), new_position); + m_target_found.set_position( new_position ); + return; + } + + // искать ноду по прямому запросу + if (ai().level_graph().valid_vertex_position(m_target_found.position())) { + m_target_found.set_node ( ai().level_graph().vertex_id(m_target_found.position()) ); + if (ai().level_graph().valid_vertex_id(m_target_found.node()) && m_man->path_builder().accessible(m_target_found.node())) { + // корректировка позиции + Fvector new_position = m_target_found.position(); + m_man->path_builder().fix_position(Fvector().set(m_target_found.position()), m_target_found.node(), new_position ); + m_target_found.set_position( new_position ); + return; + } + } + + // находим с помощью каверов + if (m_cover_info.use_covers) { + m_cover_approach->setup (m_target_found.position(), m_cover_info.min_dist, m_cover_info.max_dist, m_cover_info.deviation); + const CCoverPoint *point = ai().cover_manager().best_cover(m_object->Position(),m_cover_info.radius,*m_cover_approach); + // нашли кавер? + if (point) { + m_target_found.set_node(point->m_level_vertex_id); + m_target_found.set_position ( point->m_position ); + return; + } + } + + // нода не найдена. на следующем этапе будет использован селектор + m_target_found.set_node ( m_man->path_builder().find_nearest_vertex(m_object->ai_location().level_vertex_id(),m_target_found.position(),30.f) ); + m_target_found.set_position ( ai().level_graph().vertex_position(m_target_found.node()) ); +} + diff --git a/src/xrGameLA/ai/monsters/control_path_builder_base_set.cpp b/src/xrGameLA/ai/monsters/control_path_builder_base_set.cpp new file mode 100644 index 000000000..d0edf6278 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_path_builder_base_set.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "control_path_builder_base.h" +#include "BaseMonster/base_monster.h" +#include "../../level_graph.h" +#include "../../ai_space.h" + +////////////////////////////////////////////////////////////////////////// +// Method: prepare_builder +// Desc: reset path builder params to defaults +////////////////////////////////////////////////////////////////////////// +void CControlPathBuilderBase::prepare_builder() +{ + m_time = 0; + m_distance_to_path_end = 1.f; + m_failed = false; + m_cover_info.use_covers = false; + + m_target_actual = false; + + m_target_set.init (); + + set_target_accessible (m_target_found, m_object->Position()); + + m_last_time_target_set = 0; + + m_time_global_failed_started = 0; + m_time_path_updated_external = 0; + + m_game_graph_target_vertex = u32(-1); +} + +////////////////////////////////////////////////////////////////////////// +// Method: set_target_point +// Desc: just set desirable position and update actuality +// all checkings will be made on update stage +////////////////////////////////////////////////////////////////////////// +void CControlPathBuilderBase::set_target_point(const Fvector &position, u32 node) +{ + // обновить актуальность + m_target_actual = m_target_actual && (m_target_set.position().similar(position) && (m_target_set.node() == node)); + + // установить позицию + m_target_set.set (position,node); + + // установить глобальные параметры передвижения + m_target_type = eMoveToTarget; + + set_level_path_type (); +} + +void CControlPathBuilderBase::set_target_point(u32 node) +{ + set_target_point(ai().level_graph().vertex_position(node),node); +} + +void CControlPathBuilderBase::set_retreat_from_point(const Fvector &position) +{ + // обновить актуальность + m_target_actual = m_target_actual && (m_target_set.position().similar(position)); + + // установить позицию + m_target_set.set (position,u32(-1)); + + // установить глобальные параметры передвижения + m_target_type = eRetreatFromTarget; + + set_level_path_type (); +} + diff --git a/src/xrGameLA/ai/monsters/control_path_builder_base_update.cpp b/src/xrGameLA/ai/monsters/control_path_builder_base_update.cpp new file mode 100644 index 000000000..f5c82f5f4 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_path_builder_base_update.cpp @@ -0,0 +1,111 @@ +#include "stdafx.h" +#include "control_path_builder_base.h" +#include "BaseMonster/base_monster.h" +#include "../../detail_path_manager.h" +#include "../../profiler.h" + +void CControlPathBuilderBase::update_frame() +{ + START_PROFILE("Base Monster/Path Builder Base/Frame Update"); + + // обновить состояние билдера + update_path_builder_state (); + + // обновить / установить целевую позицию + update_target_point (); + + // set params + set_path_builder_params (); + + STOP_PROFILE; +} + +void CControlPathBuilderBase::update_target_point() +{ + m_reset_actuality = false; + + if (!m_enable) return; + if (m_path_type != MovementManager::ePathTypeLevelPath) return; + + // проверить условия, когда путь строить не нужно + if (!target_point_need_update()) return; + + STarget saved_target; + saved_target.set(m_target_found.position(), m_target_found.node()); + + if (global_failed()) + find_target_point_failed (); + else + // выбрать ноду и позицию в соответствии с желаемыми нодой и позицией + find_target_point_set (); + + + //----------------------------------------------------------------------- + // postprocess target_point + if (m_target_found.node() == saved_target.node()) { + // level_path останется актуальным - сбросить актуальность + m_reset_actuality = true; + } + //----------------------------------------------------------------------- + + // сохранить текущее время + m_last_time_target_set = Device.dwTimeGlobal; + + // параметры установлены, включаем актуальность + m_target_actual = true; +} + +void CControlPathBuilderBase::set_path_builder_params() +{ + SControlPathBuilderData *ctrl_data = (SControlPathBuilderData *)m_man->data(this, ControlCom::eControlPath); + if (!ctrl_data) return; + + ctrl_data->use_dest_orientation = m_use_dest_orient; + ctrl_data->dest_orientation = m_dest_dir; + ctrl_data->target_node = m_target_found.node(); + ctrl_data->target_position = m_target_found.position(); + ctrl_data->try_min_time = m_try_min_time; + ctrl_data->enable = m_enable; + ctrl_data->path_type = m_path_type; + ctrl_data->extrapolate = m_extrapolate; + ctrl_data->velocity_mask = m_velocity_mask; + ctrl_data->desirable_mask = m_desirable_mask; + ctrl_data->reset_actuality = m_reset_actuality; + ctrl_data->game_graph_target_vertex = m_game_graph_target_vertex; +} + + +void CControlPathBuilderBase::update_path_builder_state() +{ +// u32 state_prev = m_state; + + m_state = eStatePathValid; + + // нет пути + if (m_man->path_builder().detail().path().empty()) { + m_state = eStateNoPath; + } + // проверка на конец пути + else if (m_path_end) { + m_state = eStatePathEnd; + } + + // ждать пока не будет построен путь (путь должен быть гарантированно построен) + if ((m_last_time_target_set > m_time_path_updated_external) || + (!m_man->path_builder().detail().actual() && (m_man->path_builder().detail().time_path_built() < m_last_time_target_set))) { + m_state |= eStateWaitNewPath; + } + + if (m_failed) { + // set + m_state |= eStatePathFailed; + // clear + m_state &= ~eStatePathValid; + m_state &= ~eStateWaitNewPath; + + m_failed = false; + + m_time_global_failed_started = time(); + } + +} diff --git a/src/xrGameLA/ai/monsters/control_rotation_jump.cpp b/src/xrGameLA/ai/monsters/control_rotation_jump.cpp new file mode 100644 index 000000000..61a9efdcb --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_rotation_jump.cpp @@ -0,0 +1,255 @@ +#include "stdafx.h" +#include "control_rotation_jump.h" +#include "BaseMonster/base_monster.h" +#include "control_manager.h" +#include "monster_velocity_space.h" +#include "control_direction_base.h" +#include "control_movement_base.h" +#include "control_animation_base.h" + +#define ROTATION_JUMP_DELAY_MIN 3000 +#define ROTATION_JUMP_DELAY_MAX 5000 +#define CHECK_YAW 150 * PI / 180 +#define START_SPEED_DELTA 2.f + +void CControlRotationJump::reinit() +{ + inherited::reinit(); + + m_time_next_rotation_jump = 0; + m_skeleton_animated = smart_cast(m_object->Visual()); +} + + +void CControlRotationJump::activate() +{ + m_man->capture_pure (this); + m_man->subscribe (this, ControlCom::eventAnimationEnd); + + // disable path builder and movement + m_man->path_stop (this); + m_man->move_stop (this); + + float yaw = Fvector().sub(m_object->EnemyMan.get_enemy()->Position(), m_object->Position()).getH(); + m_right_side = m_man->direction().is_from_right(angle_normalize(-yaw)); + + ////////////////////////////////////////////////////////////////////////// + if (m_data.flags.is(SControlRotationJumpData::eStopAtOnce)) + stop_at_once (); + else + build_line_first (); + ////////////////////////////////////////////////////////////////////////// +} + +void CControlRotationJump::on_release() +{ + m_man->unlock (this, ControlCom::eControlPath); + + SControlDirectionData *ctrl_data_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_data_dir); + ctrl_data_dir->linear_dependency = true; + + m_man->release_pure (this); + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); + + m_time_next_rotation_jump = Device.dwTimeGlobal + Random.randI(ROTATION_JUMP_DELAY_MIN,ROTATION_JUMP_DELAY_MAX); +} + +bool CControlRotationJump::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured_pure()) return false; + + if (!m_object->EnemyMan.get_enemy()) return false; + if (m_time_next_rotation_jump > Device.dwTimeGlobal) return false; + + Fvector enemy_position; + enemy_position.set (m_object->EnemyMan.get_enemy()->Position()); + if (m_man->direction().is_face_target(enemy_position, CHECK_YAW)) return false; + + SVelocityParam &velocity_run = m_object->move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + if (!fsimilar(m_man->movement().velocity_current(), velocity_run.velocity.linear, START_SPEED_DELTA)) return false; + + return true; +} + +void CControlRotationJump::on_event(ControlCom::EEventType type, ControlCom::IEventData *dat) +{ + switch (type) { + case ControlCom::eventAnimationEnd: + if ((m_stage == eStop) && (m_data.flags.is(SControlRotationJumpData::eRotateOnce) == FALSE)) + build_line_second(); + else + m_man->notify (ControlCom::eventRotationJumpEnd, 0); + break; + } +} + +void CControlRotationJump::stop_at_once() +{ + m_time = m_man->animation().motion_time(m_right_side ? m_data.anim_stop_rs : m_data.anim_stop_ls, m_object->Visual()); + + // set angular speed in exclusive force mode + SControlDirectionData *ctrl_data_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_data_dir); + + float target_yaw; + if (m_data.flags.is(SControlRotationJumpData::eRotateOnce) && m_object->EnemyMan.get_enemy()) { + // if rotate once so rotate to enemy + Fvector dir_to_enemy; + dir_to_enemy.sub (m_object->EnemyMan.get_enemy()->Position(), m_object->Position()); + dir_to_enemy.normalize (); + target_yaw = angle_normalize(-dir_to_enemy.getH()); + } else { + target_yaw = angle_normalize(-m_object->Direction().getH() + (m_right_side ? m_data.turn_angle : -m_data.turn_angle)); + } + + ctrl_data_dir->heading.target_angle = target_yaw; + + float cur_yaw; + m_man->direction().get_heading (cur_yaw, target_yaw); + ctrl_data_dir->heading.target_speed = angle_difference(cur_yaw,target_yaw)/ m_time; + ctrl_data_dir->linear_dependency = false; + VERIFY (!fis_zero(ctrl_data_dir->heading.target_speed)); + + m_stage = eStop; + + // start new animation + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_data); + + ctrl_data->global.motion = (m_right_side ? m_data.anim_stop_rs : m_data.anim_stop_ls); + ctrl_data->global.actual = false; +} + +void CControlRotationJump::build_line_first() +{ + // get animation time + m_time = m_man->animation().motion_time(m_right_side ? m_data.anim_stop_rs : m_data.anim_stop_ls, m_object->Visual()); + // set acceleration and velocity + m_start_velocity = m_man->movement().velocity_current(); + m_target_velocity = 0.f; + + // acceleration + m_accel = (m_target_velocity - m_start_velocity) / m_time; + + // path distance + m_dist = (m_target_velocity*m_target_velocity - m_start_velocity*m_start_velocity) / (2*m_accel); + + + // set angular speed in exclusive force mode + SControlDirectionData *ctrl_data_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_data_dir); + + float target_yaw = angle_normalize(-m_object->Direction().getH() + (m_right_side ? m_data.turn_angle : -m_data.turn_angle)); + ctrl_data_dir->heading.target_angle = target_yaw; + + float cur_yaw; + m_man->direction().get_heading (cur_yaw, target_yaw); + ctrl_data_dir->heading.target_speed = angle_difference(cur_yaw,target_yaw)/ m_time; + ctrl_data_dir->linear_dependency = false; + + VERIFY (!fis_zero(ctrl_data_dir->heading.target_speed)); + + u32 velocity_mask = MonsterMovement::eVelocityParameterStand | MonsterMovement::eVelocityParameterRunNormal; + m_stage = eStop; + + Fvector target_position; + target_position.mad(m_object->Position(), m_object->Direction(), m_dist); + + if (!m_man->build_path_line(this, target_position, u32(-1), velocity_mask)) { + m_man->notify (ControlCom::eventRotationJumpEnd, 0); + } else { + // enable path + SControlPathBuilderData *ctrl_path = (SControlPathBuilderData*)m_man->data(this, ControlCom::eControlPath); + VERIFY (ctrl_path); + ctrl_path->enable = true; + + m_man->lock (this, ControlCom::eControlPath); + + SControlMovementData *ctrl_move = (SControlMovementData*)m_man->data(this, ControlCom::eControlMovement); + VERIFY (ctrl_move); + ctrl_move->velocity_target = m_target_velocity; + ctrl_move->acc = _abs(m_accel); + + // start new animation + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_data); + + ctrl_data->global.motion = (m_right_side ? m_data.anim_stop_rs : m_data.anim_stop_ls); + ctrl_data->global.actual = false; + } +} + +void CControlRotationJump::build_line_second() +{ + if (!m_object->EnemyMan.get_enemy()) { + m_man->notify (ControlCom::eventRotationJumpEnd, 0); + return; + } + + // set acceleration and velocity + m_target_velocity = m_start_velocity; + m_start_velocity = 0; + + // get animation time + m_time = m_man->animation().motion_time(m_right_side ? m_data.anim_run_rs : m_data.anim_run_ls, m_object->Visual()); + + // acceleration + m_accel = (m_target_velocity - m_start_velocity) / m_time; + + // path distance + m_dist = (m_target_velocity*m_target_velocity - m_start_velocity*m_start_velocity) / (2*m_accel); + + // set angular speed in exclusive force mode + SControlDirectionData *ctrl_data_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_data_dir); + + Fvector dir_to_enemy; + dir_to_enemy.sub (m_object->EnemyMan.get_enemy()->Position(), m_object->Position()); + dir_to_enemy.normalize (); + + float target_yaw = dir_to_enemy.getH(); + target_yaw = angle_normalize(-target_yaw); + ctrl_data_dir->heading.target_angle = target_yaw; + + float cur_yaw; + m_man->direction().get_heading (cur_yaw, target_yaw); + ctrl_data_dir->heading.target_speed = angle_difference(cur_yaw,target_yaw)/ m_time; + ctrl_data_dir->linear_dependency = false; + + VERIFY (!fis_zero(ctrl_data_dir->heading.target_speed)); + + + // Velocity mask + u32 velocity_mask = MonsterMovement::eVelocityParameterStand | MonsterMovement::eVelocityParameterRunNormal; + + m_stage = eRun; + + Fvector target_position; + target_position.mad(m_object->Position(), dir_to_enemy, m_dist); + + if (!m_man->build_path_line(this, target_position, u32(-1), velocity_mask)) { + m_man->notify (ControlCom::eventRotationJumpEnd, 0); + } else { + // enable path + SControlPathBuilderData *ctrl_path = (SControlPathBuilderData*)m_man->data(this, ControlCom::eControlPath); + VERIFY (ctrl_path); + ctrl_path->enable = true; + + m_man->lock (this, ControlCom::eControlPath); + + SControlMovementData *ctrl_move = (SControlMovementData*)m_man->data(this, ControlCom::eControlMovement); + VERIFY (ctrl_move); + ctrl_move->velocity_target = m_target_velocity; + ctrl_move->acc = m_accel; + + // start new animation + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_data); + + ctrl_data->global.motion = (m_right_side ? m_data.anim_run_rs : m_data.anim_run_ls); + ctrl_data->global.actual = false; + } +} + diff --git a/src/xrGameLA/ai/monsters/control_rotation_jump.h b/src/xrGameLA/ai/monsters/control_rotation_jump.h new file mode 100644 index 000000000..b54a38a91 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_rotation_jump.h @@ -0,0 +1,51 @@ +#pragma once +#include "control_combase.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" + +struct SControlRotationJumpData : public ControlCom::IComData { + MotionID anim_stop_ls,anim_run_ls; + MotionID anim_stop_rs,anim_run_rs; + float turn_angle; + + enum EFlags { + eStopAtOnce = u32(1) << 0, // stop at once + eRotateOnce = u32(1) << 1, // use only the first stage + }; + + flags32 flags; +}; + +class CControlRotationJump : public CControl_ComCustom { + typedef CControl_ComCustom inherited; + u32 m_time_next_rotation_jump; + + float m_target_velocity; + float m_start_velocity; + float m_accel; + float m_dist; + float m_time; + + bool m_right_side; + + enum EStage { + eStop, + eRun, + eNone + } m_stage; + + IKinematicsAnimated *m_skeleton_animated; + +public: + virtual void reinit (); + + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + virtual void activate (); + virtual void on_release (); + virtual bool check_start_conditions (); + +private: + void build_line_first (); + void build_line_second (); + void stop_at_once (); +}; + diff --git a/src/xrGameLA/ai/monsters/control_run_attack.cpp b/src/xrGameLA/ai/monsters/control_run_attack.cpp new file mode 100644 index 000000000..f972f7f80 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_run_attack.cpp @@ -0,0 +1,129 @@ +#include "stdafx.h" +#include "control_run_attack.h" +#include "BaseMonster/base_monster.h" +#include "monster_velocity_space.h" +#include "control_animation_base.h" +#include "control_direction_base.h" +#include "control_movement_base.h" + +void CControlRunAttack::load(LPCSTR section) +{ + read_distance (section,"Run_Attack_Dist", m_min_dist, m_max_dist); + read_delay (section,"Run_Attack_Delay", m_min_delay, m_max_delay); +} + +void CControlRunAttack::reinit() +{ + CControl_ComCustom<>::reinit(); + + m_time_next_attack = 0; +} + +void CControlRunAttack::activate() +{ + m_man->capture_pure (this); + m_man->subscribe (this, ControlCom::eventAnimationEnd); + m_man->subscribe (this, ControlCom::eventAnimationStart); + + m_man->path_stop (this); + m_man->move_stop (this); + + ////////////////////////////////////////////////////////////////////////// + + SControlDirectionData *ctrl_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_dir); + ctrl_dir->heading.target_speed = 3.f; + ctrl_dir->heading.target_angle = m_man->direction().angle_to_target(m_object->EnemyMan.get_enemy()->Position()); + + ////////////////////////////////////////////////////////////////////////// + + SControlAnimationData *ctrl_anim = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_anim); + + ctrl_anim->global.motion = smart_cast(m_object->Visual())->ID_Cycle_Safe("stand_attack_run_0"); + ctrl_anim->global.actual = false; +} + +void CControlRunAttack::on_release() +{ + m_man->unlock (this, ControlCom::eControlPath); + m_man->release_pure (this); + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); + m_man->unsubscribe (this, ControlCom::eventAnimationStart); +} + +bool CControlRunAttack::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured_pure()) return false; + + const CEntityAlive *enemy = m_object->EnemyMan.get_enemy(); + if (!enemy) return false; + // check if faced enemy + if (!m_man->direction().is_face_target(enemy, PI_DIV_6)) return false; + + float dist = enemy->Position().distance_to(m_object->Position()); + // check distance to enemy + if ((dist > m_max_dist) || (dist < m_min_dist)) return false; + + // check if run state, speed + SVelocityParam &velocity_run = m_object->move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + if (!fsimilar(m_man->movement().velocity_current(), velocity_run.velocity.linear, 2.f)) return false; + + if (m_time_next_attack > time()) return false; + + return true; +} + +void CControlRunAttack::on_event(ControlCom::EEventType type, ControlCom::IEventData *dat) +{ + switch (type) { + case ControlCom::eventAnimationEnd: + m_time_next_attack = time() + Random.randI(m_min_delay,m_max_delay); + m_man->notify (ControlCom::eventRunAttackEnd, 0); + break; + case ControlCom::eventAnimationStart: // handle blend params + { + // set animation speed + SControlAnimationData *ctrl_data_anim = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_data_anim); + + CBlend *blend = m_man->animation().current_blend(); + VERIFY (blend); + + // animation time + float anim_time = blend->timeTotal / blend->speed; + + // run velocity + u32 velocity_mask = MonsterMovement::eVelocityParameterRunNormal; + SVelocityParam &velocity = m_object->move().get_velocity(velocity_mask); + + // distance + float path_dist = anim_time * velocity.velocity.linear; + + Fvector dir; + dir.sub (m_object->EnemyMan.get_enemy()->Position(), m_object->Position()); + dir.normalize_safe (); + + Fvector target_position; + target_position.mad (m_object->Position(), dir, path_dist); + + if (!m_man->build_path_line (this, target_position, u32(-1), velocity_mask | MonsterMovement::eVelocityParameterStand)) { + m_man->notify (ControlCom::eventRunAttackEnd, 0); + } else { + // enable path + SControlPathBuilderData *ctrl_path = (SControlPathBuilderData*)m_man->data(this, ControlCom::eControlPath); + VERIFY (ctrl_path); + ctrl_path->enable = true; + + m_man->lock (this, ControlCom::eControlPath); + + SControlMovementData *ctrl_move = (SControlMovementData*)m_man->data(this, ControlCom::eControlMovement); + VERIFY (ctrl_move); + ctrl_move->velocity_target = velocity.velocity.linear; + ctrl_move->acc = flt_max; + } + } + break; + } +} diff --git a/src/xrGameLA/ai/monsters/control_run_attack.h b/src/xrGameLA/ai/monsters/control_run_attack.h new file mode 100644 index 000000000..3e099d946 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_run_attack.h @@ -0,0 +1,22 @@ +#pragma once +#include "control_combase.h" + +class CControlRunAttack : public CControl_ComCustom<> { + float m_min_dist; + float m_max_dist; + + u32 m_min_delay; + u32 m_max_delay; + + u32 m_time_next_attack; + +public: + virtual void load (LPCSTR section); + virtual void reinit (); + + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + virtual void activate (); + virtual void on_release (); + virtual bool check_start_conditions (); +}; + diff --git a/src/xrGameLA/ai/monsters/control_sequencer.cpp b/src/xrGameLA/ai/monsters/control_sequencer.cpp new file mode 100644 index 000000000..be9d67821 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_sequencer.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "control_sequencer.h" +#include "control_manager.h" + +void CAnimationSequencer::reset_data() +{ + m_data.motions.clear(); +} + +void CAnimationSequencer::on_capture() +{ + m_man->capture_pure (this); + m_man->subscribe (this, ControlCom::eventAnimationEnd); + + m_man->path_stop (this); + m_man->move_stop (this); + m_man->dir_stop (this); +} + +void CAnimationSequencer::activate() +{ + m_index = 0; + play_selected (); + +} + +void CAnimationSequencer::on_event(ControlCom::EEventType type, ControlCom::IEventData *data) +{ + if (type == ControlCom::eventAnimationEnd) { + if (m_index + 1 < m_data.motions.size()) { + m_index++; + play_selected (); + } else { + m_man->notify (ControlCom::eventSequenceEnd, 0); + } + return; + } +} + +void CAnimationSequencer::on_release() +{ + m_man->release_pure (this); + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); +} + +void CAnimationSequencer::play_selected() +{ + // start new animation + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_data); + + ctrl_data->global.motion = m_data.motions[m_index]; + ctrl_data->global.actual = false; +} + +bool CAnimationSequencer::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured_pure()) return false; + + return true; +} diff --git a/src/xrGameLA/ai/monsters/control_sequencer.h b/src/xrGameLA/ai/monsters/control_sequencer.h new file mode 100644 index 000000000..92a82dc42 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_sequencer.h @@ -0,0 +1,24 @@ +#pragma once + +#include "control_combase.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" + +struct SAnimationSequencerData : public ControlCom::IComData { + xr_vector motions; +}; + +class CAnimationSequencer : public CControl_ComCustom { + u32 m_index; +public: + virtual void reset_data (); + virtual void on_capture (); + virtual void on_release (); + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + + virtual bool check_start_conditions (); + + virtual void activate (); +private: + void play_selected (); +}; + diff --git a/src/xrGameLA/ai/monsters/control_threaten.cpp b/src/xrGameLA/ai/monsters/control_threaten.cpp new file mode 100644 index 000000000..3616e3f10 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_threaten.cpp @@ -0,0 +1,86 @@ +#include "stdafx.h" +#include "control_threaten.h" +#include "BaseMonster/base_monster.h" +#include "control_animation_base.h" +#include "control_direction_base.h" +#include "control_movement_base.h" + +void CControlThreaten::reinit() +{ + inherited::reinit(); +} + +void CControlThreaten::activate() +{ + m_man->capture_pure (this); + m_man->subscribe (this, ControlCom::eventAnimationSignal); + m_man->subscribe (this, ControlCom::eventAnimationEnd); + + m_man->path_stop (this); + m_man->move_stop (this); + + + ////////////////////////////////////////////////////////////////////////// + // set direction + SControlDirectionData *ctrl_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_dir); + ctrl_dir->heading.target_speed = 1.f; + ctrl_dir->heading.target_angle = m_man->direction().angle_to_target(m_object->EnemyMan.get_enemy()->Position()); + + ////////////////////////////////////////////////////////////////////////// + IKinematicsAnimated *skel = smart_cast(m_object->Visual()); + + SControlAnimationData *ctrl_anim = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_anim); + ctrl_anim->global.motion = skel->ID_Cycle_Safe(m_data.animation); + ctrl_anim->global.actual = false; + + m_man->animation().add_anim_event(skel->LL_MotionID(m_data.animation),m_data.time,CControlAnimation::eAnimationCustom); +} + + +void CControlThreaten::update_schedule() +{ + // update direction (face to enemy here) + if (m_object->EnemyMan.get_enemy()) { + SControlDirectionData *ctrl_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_dir); + ctrl_dir->heading.target_angle = m_man->direction().angle_to_target(m_object->EnemyMan.get_enemy()->Position()); + } +} + +void CControlThreaten::on_release() +{ + m_man->release_pure (this); + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); + m_man->unsubscribe (this, ControlCom::eventAnimationSignal); +} + +bool CControlThreaten::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured_pure()) return false; + + const CEntityAlive *enemy = m_object->EnemyMan.get_enemy(); + if (!enemy) return false; + // check if faced enemy + if (!m_man->direction().is_face_target(enemy, PI_DIV_6)) return false; + + return true; +} + +void CControlThreaten::on_event(ControlCom::EEventType type, ControlCom::IEventData *dat) +{ + switch (type) { + case ControlCom::eventAnimationEnd: + m_man->notify (ControlCom::eventThreatenEnd, 0); + break; + case ControlCom::eventAnimationSignal: + { + SAnimationSignalEventData *event_data = (SAnimationSignalEventData *)dat; + if (event_data->event_id == CControlAnimation::eAnimationCustom) + m_object->on_threaten_execute(); + break; + } + } +} diff --git a/src/xrGameLA/ai/monsters/control_threaten.h b/src/xrGameLA/ai/monsters/control_threaten.h new file mode 100644 index 000000000..8f04816b7 --- /dev/null +++ b/src/xrGameLA/ai/monsters/control_threaten.h @@ -0,0 +1,20 @@ +#pragma once +#include "control_combase.h" + +struct SControlThreatenData : public ControlCom::IComData { + LPCSTR animation; + float time; +}; + +class CControlThreaten : public CControl_ComCustom { + typedef CControl_ComCustom inherited; + +public: + virtual void reinit (); + virtual void update_schedule (); + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + virtual void activate (); + virtual void on_release (); + virtual bool check_start_conditions (); +}; + diff --git a/src/xrGameLA/ai/monsters/controlled_actor.cpp b/src/xrGameLA/ai/monsters/controlled_actor.cpp new file mode 100644 index 000000000..e4f19b104 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controlled_actor.cpp @@ -0,0 +1,125 @@ +#include "stdafx.h" +#include "controlled_actor.h" +#include "../../actor.h" +#include "../../../CameraBase.h" +#include "../../xr_level_controller.h" +#include "../../level.h" +#include "ai_monster_utils.h" +#include "../../inventory.h" + +#define SPEED_MIN 0.5f +#define SPEED_MAX 4.f +#define EPS_ANGLE 1 * PI / 180 +#define MAX_DIST PI + +void CControlledActor::reinit() +{ + inherited::reinit(); + reset(); +} + +void CControlledActor::release() +{ + inherited::release(); + reset(); +} + +void CControlledActor::frame_update() +{ + if (is_controlling() && m_need_turn) { + update_turn(); + } +} + +void CControlledActor::install(CActor *a) +{ + inherited::install(a); + m_need_turn = true; +} + +void CControlledActor::install() +{ + inherited::install(); + m_need_turn = true; +} + +void CControlledActor::look_point(const Fvector &point) +{ + m_target_point = point; +} + +void CControlledActor::update_turn() +{ + // get yaw and pitch to target + float cam_target_yaw, cam_target_pitch; + + Fvector P,D,N; + m_actor->cam_Active()->Get (P,D,N); + Fvector().sub(m_target_point, P).getHP (cam_target_yaw, cam_target_pitch); + + // get yaw and pitch of current cam direction + float cam_current_yaw, cam_current_pitch; + D.getHP (cam_current_yaw, cam_current_pitch); + + // YAW + float speed_factor = angle_difference(cam_current_yaw, cam_target_yaw) / MAX_DIST; + clamp (speed_factor, 0.f, 1.f); + if (speed_factor > 0.5f) speed_factor = 1.f - speed_factor; + + float speed; + if (fsimilar(cam_current_yaw, cam_target_yaw, EPS_ANGLE)) { + m_turned_yaw = true; + } else { + speed = m_speed_min + speed_factor * (m_speed_max - m_speed_min); + + if (from_right(cam_target_yaw,cam_current_yaw)) + m_actor->cam_Active()->Move (kLEFT, speed * Device.fTimeDelta); + else + m_actor->cam_Active()->Move (kRIGHT, speed * Device.fTimeDelta); + + } + + // PITCH + speed_factor = angle_difference(cam_current_pitch, cam_target_pitch) / MAX_DIST; + clamp (speed_factor, 0.f, 1.f); + if (speed_factor > 0.5f) speed_factor = 1.f - speed_factor; + + if (fsimilar(cam_current_pitch, cam_target_pitch, EPS_ANGLE)) { + m_turned_pitch = true; + } else { + speed = m_speed_min + speed_factor * (m_speed_max - m_speed_min); + + if (from_right(cam_target_pitch,cam_current_pitch)) + m_actor->cam_Active()->Move (kDOWN, speed * Device.fTimeDelta); + else + m_actor->cam_Active()->Move (kUP, speed * Device.fTimeDelta); + } +} + +void CControlledActor::reset() +{ + m_turned_yaw = false; + m_turned_pitch = false; + m_speed_min = SPEED_MIN; + m_speed_max = SPEED_MAX; +} + +bool CControlledActor::is_turning() +{ + return (!m_turned_yaw || !m_turned_pitch); +} + +bool CControlledActor::is_installed() +{ + return !!m_actor; +} + +bool CControlledActor::authorized(int cmd) +{ + if (cmd == kWPN_1) return true; + if (cmd == kWPN_FIRE) { + if (m_actor->inventory().GetActiveSlot() == 0) return true; + } + + return false; +} diff --git a/src/xrGameLA/ai/monsters/controlled_actor.h b/src/xrGameLA/ai/monsters/controlled_actor.h new file mode 100644 index 000000000..4a86b6f86 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controlled_actor.h @@ -0,0 +1,44 @@ +#pragma once + +#include "../../actor_input_handler.h" + +class CControlledActor : public CActorInputHandler { + typedef CActorInputHandler inherited; + + Fvector m_target_point; + + bool m_turned_yaw; + bool m_turned_pitch; + + bool m_lock_run; + u32 m_lock_run_started; + u32 m_lock_run_period; + + bool m_need_turn; + + float m_speed_min; + float m_speed_max; +public: + virtual void reinit (); + virtual float mouse_scale_factor (){return flt_max;} + virtual void release (); + virtual void install (CActor *); + virtual void install (); + virtual bool authorized (int cmd); + + void look_point (const Fvector &point); + bool is_turning (); + bool is_installed (); + + void frame_update (); + bool is_controlling () {return m_actor != 0;} + + void dont_need_turn (){m_need_turn = false;} + + void set_max_speed (float value) {m_speed_max = value;} + void set_min_speed (float value) {m_speed_min = value;} +private: + void reset (); + void update_turn (); +}; + diff --git a/src/xrGameLA/ai/monsters/controlled_entity.h b/src/xrGameLA/ai/monsters/controlled_entity.h new file mode 100644 index 000000000..c12006e31 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controlled_entity.h @@ -0,0 +1,76 @@ +#pragma once +#include "controller/controller.h" + +class CEntity; + +enum ETask { + eTaskFollow = u32(0), + eTaskAttack, + eTaskNone = u32(-1) +}; + +struct SControlledInfo { + ETask m_task; + const CEntity *m_object; + Fvector m_position; + u32 m_node; + float m_radius; +}; + +class CControlledEntityBase { +public: + virtual bool is_under_control () = 0; + + virtual void set_data (const SControlledInfo &info) = 0; + virtual SControlledInfo &get_data () = 0; + + virtual void set_task_follow (const CEntity *e) = 0; + virtual void set_task_attack (const CEntity *e) = 0; + + virtual void set_under_control (CController *controller) = 0; + virtual void free_from_control () = 0; + + virtual void on_reinit () = 0; + virtual void on_die () = 0; + virtual void on_destroy () = 0; +}; + + +template +class CControlledEntity : public CControlledEntityBase { + + SControlledInfo m_data; + + struct SGroupID { + int team_id; + int squad_id; + int group_id; + } saved_id; + + _Object *m_object; + CController *m_controller; + +public: + + virtual bool is_under_control () {return (m_controller != 0);} + + virtual void set_data (const SControlledInfo &info) {m_data = info;} + virtual SControlledInfo &get_data (){return m_data;} + + virtual void set_task_follow (const CEntity *e); + virtual void set_task_attack (const CEntity *e); + + virtual void set_under_control (CController *controller); + virtual void free_from_control (); + + virtual void on_reinit (); + virtual void on_die (); + virtual void on_destroy (); + + void init_external (_Object *obj) {m_object = obj;} + + CController *get_controller () {return m_controller;} +}; + +#include "controlled_entity_inline.h" + diff --git a/src/xrGameLA/ai/monsters/controlled_entity_inline.h b/src/xrGameLA/ai/monsters/controlled_entity_inline.h new file mode 100644 index 000000000..897446466 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controlled_entity_inline.h @@ -0,0 +1,70 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CControlledEntityAbstract CControlledEntity<_Object> + +TEMPLATE_SPECIALIZATION +void CControlledEntityAbstract::on_reinit() +{ + m_data.m_object = 0; + m_controller = 0; +} + +TEMPLATE_SPECIALIZATION +void CControlledEntityAbstract::set_task_follow(const CEntity *e) +{ + m_data.m_object = e; + m_data.m_task = eTaskFollow; +} +TEMPLATE_SPECIALIZATION +void CControlledEntityAbstract::set_task_attack(const CEntity *e) +{ + m_data.m_object = e; + m_data.m_task = eTaskAttack; +} + +TEMPLATE_SPECIALIZATION +void CControlledEntityAbstract::set_under_control(CController *controller) +{ + m_controller = controller; + + saved_id.team_id = m_object->g_Team (); + saved_id.squad_id = m_object->g_Squad (); + saved_id.group_id = m_object->g_Group (); + + m_object->ChangeTeam(m_controller->g_Team(), m_controller->g_Squad(), m_controller->g_Group()); +} + +TEMPLATE_SPECIALIZATION +void CControlledEntityAbstract::free_from_control() +{ + m_object->ChangeTeam (saved_id.team_id, saved_id.squad_id, saved_id.group_id); + m_controller = 0; +} + +TEMPLATE_SPECIALIZATION +void CControlledEntityAbstract::on_die() +{ + if (!is_under_control()) return; + + m_controller->OnFreedFromControl (m_object); + m_controller = 0; +} +TEMPLATE_SPECIALIZATION +void CControlledEntityAbstract::on_destroy() +{ + if (!is_under_control()) return; + + m_object->ChangeTeam (saved_id.team_id, saved_id.squad_id, saved_id.group_id); + + m_controller->OnFreedFromControl (m_object); + m_controller = 0; +} + + + +#undef TEMPLATE_SPECIALIZATION +#undef CControlledEntityAbstract diff --git a/src/xrGameLA/ai/monsters/controller/controller.cpp b/src/xrGameLA/ai/monsters/controller/controller.cpp new file mode 100644 index 000000000..0d3b6bd63 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller.cpp @@ -0,0 +1,795 @@ +#include "stdafx.h" +#include "controller.h" +#include "controller_state_manager.h" +#include "../controlled_entity.h" +#include "../../../actor.h" +#include "../../../ActorEffector.h" +#include "../ai_monster_effector.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../level.h" +#include "../../../sound_player.h" +#include "../../../ai_monster_space.h" +//#include "../../../ui/UIMainIngameWnd.h" +#include "../../../UIGameCustom.h" +#include "../../../ui/UIStatic.h" + +#include "../monster_velocity_space.h" +#include "../../../level_debug.h" +#include "../../../game_object_space.h" +#include "../../../detail_path_manager.h" +#include "../../../ai_space.h" +#include "../../../cover_point.h" +#include "../../../cover_manager.h" + +#include "controller_animation.h" +#include "controller_direction.h" + +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../../../level_graph.h" +#include "../../../ai_object_location.h" + +#include "../../../monster_community.h" +#include "../../../character_community.h" +#include "../../../InventoryOwner.h" +#include "../../../character_info.h" + +#include "controller_psy_hit.h" +#include "../monster_cover_manager.h" +#include "controller_psy_aura.h" + +#ifdef _DEBUG +# include +#endif + +const u32 _pmt_psy_attack_delay = 2000; +const float _pmt_psy_attack_min_angle = deg(5); + + +CController::CController() +{ + StateMan = new CStateManagerController(this); + time_control_hit_started = 0; + + m_psy_hit = new CControllerPsyHit(); + + control().add (m_psy_hit, ControlCom::eComCustom1); + + m_aura = new CControllerAura(this); + + +#ifdef _DEBUG + P1.set(0.f,0.f,0.f); + P2.set(0.f,0.f,0.f); +#endif + +} + +CController::~CController() +{ + xr_delete(StateMan); + xr_delete(m_psy_hit); + xr_delete(m_aura); +} + +void CController::Load(LPCSTR section) +{ + inherited::Load (section); + + // Load Control FX texture +// m_UIControlFX.Init(pSettings->r_string(section, "control_fx_texture"), "hud\\default",0,0,0); +// m_UIControlFX2.Init(pSettings->r_string(section, "control_fx_texture2"), "hud\\default",0,0,0); + + m_max_controlled_number = pSettings->r_u8(section,"Max_Controlled_Count"); + m_controlled_objects.reserve (m_max_controlled_number); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + ::Sound->create(control_start_sound,pSettings->r_string(section,"sound_control_start"), st_Effect,SOUND_TYPE_WORLD); + ::Sound->create(control_hit_sound, pSettings->r_string(section,"sound_control_hit"), st_Effect,SOUND_TYPE_WORLD); + + anim().AddReplacedAnim(&m_bDamaged, eAnimStandIdle, eAnimStandDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + //anim().AddReplacedAnim(&m_bRunTurnLeft, eAnimRun, eAnimStandTurnLeft); + //anim().AddReplacedAnim(&m_bRunTurnRight, eAnimRun, eAnimStandTurnRight); + + // Load control postprocess -------------------------------------------------------- + LPCSTR ppi_section = pSettings->r_string(section, "control_effector"); + m_control_effector.ppi.duality.h = pSettings->r_float(ppi_section,"duality_h"); + m_control_effector.ppi.duality.v = pSettings->r_float(ppi_section,"duality_v"); + m_control_effector.ppi.gray = pSettings->r_float(ppi_section,"gray"); + m_control_effector.ppi.blur = pSettings->r_float(ppi_section,"blur"); + m_control_effector.ppi.noise.intensity = pSettings->r_float(ppi_section,"noise_intensity"); + m_control_effector.ppi.noise.grain = pSettings->r_float(ppi_section,"noise_grain"); + m_control_effector.ppi.noise.fps = pSettings->r_float(ppi_section,"noise_fps"); + VERIFY(!fis_zero(m_control_effector.ppi.noise.fps)); + + sscanf(pSettings->r_string(ppi_section,"color_base"), "%f,%f,%f", &m_control_effector.ppi.color_base.r, &m_control_effector.ppi.color_base.g, &m_control_effector.ppi.color_base.b); + sscanf(pSettings->r_string(ppi_section,"color_gray"), "%f,%f,%f", &m_control_effector.ppi.color_gray.r, &m_control_effector.ppi.color_gray.g, &m_control_effector.ppi.color_gray.b); + sscanf(pSettings->r_string(ppi_section,"color_add"), "%f,%f,%f", &m_control_effector.ppi.color_add.r, &m_control_effector.ppi.color_add.g, &m_control_effector.ppi.color_add.b); + + m_control_effector.time = pSettings->r_float(ppi_section,"time"); + m_control_effector.time_attack = pSettings->r_float(ppi_section,"time_attack"); + m_control_effector.time_release = pSettings->r_float(ppi_section,"time_release"); + + m_control_effector.ce_time = pSettings->r_float(ppi_section,"ce_time"); + m_control_effector.ce_amplitude = pSettings->r_float(ppi_section,"ce_amplitude"); + m_control_effector.ce_period_number = pSettings->r_float(ppi_section,"ce_period_number"); + m_control_effector.ce_power = pSettings->r_float(ppi_section,"ce_power"); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + //SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandDamaged, "stand_idle_dmg_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimEat, "sit_eat_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_", -1, &velocity_walk_dmg, PS_STAND); +// anim().AddAnim(eAnimRun, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); +// anim().AddAnim(eAnimRunDamaged, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimAttackRun, "stand_attack_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimDie, "stand_die_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimSleep, "sit_sleep_", -1, &velocity_none, PS_SIT); + + + + //anim().AddAnim(eAnimStandIdle, "new_torso_steal_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimStandTurnLeft, "new_torso_steal_", -1, &velocity_turn, PS_STAND); + //anim().AddAnim(eAnimStandTurnRight, "new_torso_steal_", -1, &velocity_turn, PS_STAND); + //anim().AddAnim(eAnimStandDamaged, "new_torso_steal_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT); + //anim().AddAnim(eAnimEat, "sit_eat_", -1, &velocity_none, PS_SIT); + //anim().AddAnim(eAnimWalkFwd, "new_torso_steal_", -1, &velocity_steal, PS_STAND); + //anim().AddAnim(eAnimWalkDamaged, "new_torso_steal_", -1, &velocity_steal, PS_STAND); + //anim().AddAnim(eAnimRun, "new_torso_steal_", -1, &velocity_steal, PS_STAND); + //anim().AddAnim(eAnimRunDamaged, "new_torso_steal_", -1, &velocity_steal, PS_STAND); + //anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + //anim().AddAnim(eAnimSteal, "new_torso_steal_", -1, &velocity_steal, PS_STAND); + //anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimDie, "stand_die_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT); + //anim().AddAnim(eAnimSleep, "sit_sleep_", -1, &velocity_none, PS_SIT); + + + //anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + //anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + //anim().AddAnim(eAnimStandDamaged, "stand_idle_dmg_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT); + //anim().AddAnim(eAnimEat, "sit_eat_", -1, &velocity_none, PS_SIT); + //anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + //anim().AddAnim(eAnimWalkDamaged, "stand_walk_dmg_", -1, &velocity_walk_dmg, PS_STAND); + anim().AddAnim(eAnimRun, "psy_walk_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_walk_dmg_", -1, &velocity_run_dmg, PS_STAND); + //anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + //anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND); + //anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimDie, "stand_die_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT); + //anim().AddAnim(eAnimSleep, "sit_sleep_", -1, &velocity_none, PS_SIT); + + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSleep); + anim().LinkAction(ACT_REST, eAnimSitIdle); + anim().LinkAction(ACT_DRAG, eAnimStandIdle); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimStandIdle); + + anim().AddTransition(PS_STAND, PS_SIT, eAnimStandSitDown, false); + anim().AddTransition(PS_SIT, PS_STAND, eAnimSitStandUp, false); + + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + + m_velocity_move_fwd.Load (section, "Velocity_MoveFwd"); + m_velocity_move_bkwd.Load (section, "Velocity_MoveBkwd"); + + load_friend_community_overrides(section); + + // load + m_sound_hit_fx.create ("affects\\tinnitus3a",st_Effect,sg_SourceType); + + m_sound_aura_left_channel.create ("monsters\\controller\\controller_psy_aura_l",st_Effect,sg_SourceType); + m_sound_aura_right_channel.create ("monsters\\controller\\controller_psy_aura_r",st_Effect,sg_SourceType); + m_sound_aura_hit_left_channel.create ("monsters\\controller\\controller_psy_hit_l",st_Effect,sg_SourceType); + m_sound_aura_hit_right_channel.create ("monsters\\controller\\controller_psy_hit_l",st_Effect,sg_SourceType); + + m_sound_tube_start.create ("monsters\\controller\\controller_first_hit",st_Effect,sg_SourceType); + m_sound_tube_pull.create ("monsters\\controller\\controller_whoosh",st_Effect,sg_SourceType); + m_sound_tube_hit_left.create ("monsters\\controller\\controller_final_hit_l",st_Effect,sg_SourceType); + m_sound_tube_hit_right.create ("monsters\\controller\\controller_final_hit_r",st_Effect,sg_SourceType); + + m_sound_tube_prepare.create ("monsters\\controller\\controller_tube_prepare",st_Effect,sg_SourceType); + + particles_fire = pSettings->r_string(section,"Control_Hit"); + + m_tube_damage = pSettings->r_float(section,"tube_damage"); + m_tube_min_dist = pSettings->r_float(section,"tube_min_dist"); + m_tube_at_once = !!pSettings->r_bool(section,"tube_at_once"); + + m_aura->load (section); +} + +void CController::load_friend_community_overrides(LPCSTR section) +{ + LPCSTR src = pSettings->r_string(section,"Friend_Community_Overrides"); + + // parse src + int item_count = _GetItemCount(src); + m_friend_community_overrides.resize(item_count); + for (int i=0; i(entity_alive); + if (!IO) return false; + if (const_cast(entity_alive)->cast_base_monster()) return false; + + return ( + std::find( + m_friend_community_overrides.begin(), + m_friend_community_overrides.end(), + IO->CharacterInfo().Community().id() + ) + != + m_friend_community_overrides.end() + ); +} + +BOOL CController::net_Spawn(CSE_Abstract *DC) +{ + if (!inherited::net_Spawn(DC)) + return(FALSE); + + return (TRUE); +} + + +void CController::UpdateControlled() +{ + // если есть враг, проверить может ли быть враг взят под контроль + if (EnemyMan.get_enemy()) { + CBaseMonster *Monster = smart_cast((const_cast(EnemyMan.get_enemy()))); + + if (Monster){ + CControlledEntityBase* ControlLogic = Monster->m_controlled; + if (ControlLogic) { + if (!ControlLogic->is_under_control() && (m_controlled_objects.size() < m_max_controlled_number)) { + // взять под контроль + ControlLogic->set_under_control(this); + ControlLogic->set_task_follow(this); + m_controlled_objects.push_back(const_cast(EnemyMan.get_enemy())); + } + } + } + } +} + +void CController::set_controlled_task(u32 task) +{ + if (!HasUnderControl()) return; + + const CEntity *object = ((((ETask)task) == eTaskNone) ? 0 : ((((ETask)task) == eTaskFollow) ? this : EnemyMan.get_enemy())); + + for (u32 i=0; i(m_controlled_objects[i]); + entity->get_data().m_object = object; + entity->get_data().m_task = (ETask)task; + } +} + +void CController::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_CHECK_CORPSE) == ASP_CHECK_CORPSE) { + com_man().seq_run(anim().get_motion_id(eAnimCheckCorpse)); + } +} + +void CController::InitThink() +{ + for (u32 i=0; i(m_controlled_objects[i]); + if (!base) continue; + if (base->EnemyMan.get_enemy()) + EnemyMemory.add_enemy (base->EnemyMan.get_enemy(), + base->EnemyMan.get_enemy_position(), + base->EnemyMan.get_enemy_vertex(), + base->EnemyMan.get_enemy_time_last_seen() + ); + } +} + +void CController::play_control_sound_start() +{ + Fvector pos = EnemyMan.get_enemy()->Position(); + pos.y += 1.5f; + + if (control_start_sound._feedback()) control_start_sound.stop(); + control_start_sound.play_at_pos(const_cast(EnemyMan.get_enemy()),pos); +} + +void CController::play_control_sound_hit() +{ + Fvector pos = EnemyMan.get_enemy()->Position(); + pos.y += 1.5f; + + if (control_hit_sound._feedback()) control_hit_sound.stop(); + control_hit_sound.play_at_pos(const_cast(EnemyMan.get_enemy()),pos); +} + +void CController::reload(LPCSTR section) +{ + inherited::reload (section); + com_man().ta_fill_data(anim_triple_control, "stand_sit_down_attack_0", "control_attack_0", "sit_stand_up_attack_0", true, false); +} + +void CController::reinit() +{ + // must be before inherited call because of its use in ControlAnimation com + m_mental_state = eStateIdle; + + inherited::reinit(); + + m_psy_fire_start_time = 0; + m_psy_fire_delay = _pmt_psy_attack_delay; + + control().path_builder().detail().add_velocity(MonsterMovement::eControllerVelocityParameterMoveFwd, CDetailPathManager::STravelParams(m_velocity_move_fwd.velocity.linear, m_velocity_move_fwd.velocity.angular_path, m_velocity_move_fwd.velocity.angular_real)); + control().path_builder().detail().add_velocity(MonsterMovement::eControllerVelocityParameterMoveBkwd, CDetailPathManager::STravelParams(m_velocity_move_bkwd.velocity.linear, m_velocity_move_bkwd.velocity.angular_path, m_velocity_move_bkwd.velocity.angular_real)); + + m_sndShockEffector = 0; + active_control_fx = false; + + m_time_last_tube = 0; +} + +void CController::control_hit() +{ + Hit_Psy (const_cast(EnemyMan.get_enemy()), 30.f); + + // start postprocess + CActor *pA = const_cast(smart_cast(EnemyMan.get_enemy())); + if (!pA) return; + + Actor()->Cameras().AddCamEffector(new CMonsterEffectorHit(m_control_effector.ce_time,m_control_effector.ce_amplitude,m_control_effector.ce_period_number,m_control_effector.ce_power)); + Actor()->Cameras().AddPPEffector(new CMonsterEffector(m_control_effector.ppi, m_control_effector.time, m_control_effector.time_attack, m_control_effector.time_release)); + + play_control_sound_hit (); +/* + active_control_fx = true; + time_control_hit_started = Device.dwTimeGlobal; +*/ +} + +#define TEXTURE_SIZE_PERCENT 2.f + +void CController::UpdateCL() +{ + inherited::UpdateCL(); + + if(m_sndShockEffector) + { + m_sndShockEffector->Update(); + if(!m_sndShockEffector->InWork()) + xr_delete(m_sndShockEffector); + } + + if (active_control_fx) { + u32 time_to_show = 150; + float percent = float((Device.dwTimeGlobal - time_control_hit_started)) / float(time_to_show); + float percent2 = 1 - (percent - TEXTURE_SIZE_PERCENT) / 2 ; + + + if (percent < TEXTURE_SIZE_PERCENT ) { + CurrentGameUI()->RemoveCustomStatic("controller_fx2"); + SDrawStaticStruct* s = CurrentGameUI()->AddCustomStatic("controller_fx", true); + + float x1 = Device.dwWidth / 2 - ((Device.dwWidth / 2) * percent); + float y1 = Device.dwHeight / 2 - ((Device.dwHeight / 2) * percent); + float x2 = Device.dwWidth / 2 + ((Device.dwWidth / 2) * percent); + float y2 = Device.dwHeight / 2 + ((Device.dwHeight / 2) * percent); + + s->wnd()->SetWndRect (Frect().set(x1,y1,x2-x1,y2-y1)); + } else if (percent2 > 0){ + CurrentGameUI()->RemoveCustomStatic("controller_fx"); + SDrawStaticStruct* s = CurrentGameUI()->AddCustomStatic("controller_fx2", true); + + float x1 = Device.dwWidth / 2 - ((Device.dwWidth / 2) * percent2); + float y1 = Device.dwHeight / 2 - ((Device.dwHeight / 2) * percent2); + float x2 = Device.dwWidth / 2 + ((Device.dwWidth / 2) * percent2); + float y2 = Device.dwHeight / 2 + ((Device.dwHeight / 2) * percent2); + + s->wnd()->SetWndRect (Frect().set(x1,y1,x2-x1,y2-y1)); + } else { + active_control_fx = false; + CurrentGameUI()->RemoveCustomStatic("controller_fx"); + CurrentGameUI()->RemoveCustomStatic("controller_fx2"); + } + } + + m_aura->update_frame(); + +} + +void CController::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + + if (g_Alive()) { + UpdateControlled(); + //bool can_melee = can_melee_at(); + //bool can_tube = can_tube_fire(); + //if (can_melee) melee_attack(); + //if (can_tube && !can_melee) tube_fire(); + //if (can_tube_fire()) tube_fire(); + } + + // DEBUG + test_covers(); + + m_aura->update_schedule(); +} + +void CController::Die(CObject* who) +{ + inherited::Die(who); + FreeFromControl(); + + m_aura->on_death (); + m_psy_hit->on_death (); +} + +void CController::net_Destroy() +{ + inherited::net_Destroy(); + + m_aura->on_destroy (); + FreeFromControl (); +} + +void CController::net_Relcase(CObject *O) +{ + inherited::net_Relcase(O); +} + +void CController::FreeFromControl() +{ + for (u32 i=0; i(m_controlled_objects[i])->free_from_control(); + m_controlled_objects.clear(); +} + +void CController::OnFreedFromControl(const CEntity *entity) +{ + for (u32 i=0; i(EnemyMan.get_enemy()); + if (!EnemyMan.see_enemy_now()) return; + + // вычислить позицию и направленность партикла + Fvector my_head_pos; + my_head_pos.set (get_head_position(this)); + + Fvector position; + position.set (get_head_position(enemy)); + position.y -= 0.5f; + + Fvector dir; + dir.sub (position, my_head_pos); + dir.normalize (); + + PlayParticles(particles_fire, my_head_pos, dir); + + // check probability + /*if (Random.randI(100) > 30)*/ + { +// Hit_Psy (enemy, m_tube_damage / 2.f); + play_control_sound_hit (); + } + + //m_sound_hit_fx.set_volume(10.0f); + //if(!m_sndShockEffector) + // m_sndShockEffector = xr_new(); + + //m_sndShockEffector->Start(m_sound_hit_fx._handle()->length_sec() *1000, 10.f ); + //m_sound_hit_fx.play_at_pos(this, Level().CurrentEntity()->Position()); +} + +void CController::psy_fire() +{ + if (!EnemyMan.get_enemy()) return; + + draw_fire_particles (); +/* + active_control_fx = true; + time_control_hit_started = Device.dwTimeGlobal; +*/ +} + +bool CController::can_psy_fire() +{ + if ( m_psy_fire_start_time + m_psy_fire_delay > time () ) + { + return false; + } + if ( !EnemyMan.get_enemy() ) + { + return false; + } + if ( !EnemyMan.see_enemy_now() ) + { + return false; + } + + float cur_yaw = custom_dir().get_head_orientation().current.yaw; + float dir_yaw = Fvector().sub(EnemyMan.get_enemy()->Position(), Position()).getH(); + dir_yaw = angle_normalize(-dir_yaw); + if ( angle_difference(cur_yaw, dir_yaw) > _pmt_psy_attack_min_angle ) + { + return false; + } + + m_psy_fire_start_time = time(); + return true; +} + +void CController::set_psy_fire_delay_zero() +{ + m_psy_fire_delay = 0; +} +void CController::set_psy_fire_delay_default() +{ + m_psy_fire_delay = _pmt_psy_attack_delay; +} + + +////////////////////////////////////////////////////////////////////////// +// TUBE +////////////////////////////////////////////////////////////////////////// + +#define SEE_ENEMY_DURATION 1000 +#define MIN_DELAY 10000 +#define TUBE_PROBABILITY 70 + +void CController::tube_fire() +{ + m_time_last_tube = time(); + + // missed + if (!m_tube_at_once && (Random.randI(100) > TUBE_PROBABILITY)) return; + + control().activate (ControlCom::eComCustom1); +} +/* +bool CController::can_melee_at() +{ + return (MeleeChecker.can_start_melee(EnemyMan.get_enemy()) && + EnemyMan.see_enemy_now()); +} + +void CController::melee_attack() +{ + set_action(ACT_ATTACK); + if (control().direction().is_face_target(EnemyMan.get_enemy(), PI_DIV_3)) + dir().face_target (EnemyMan.get_enemy(), 800); + else + dir().face_target (EnemyMan.get_enemy(), 0, deg(15)); + + set_state_sound (MonsterSound::eMonsterSoundAggressive); +} +*/ +bool CController::can_tube_fire() +{ + if (m_tube_at_once) { + if (EnemyMan.get_enemy() && + EnemyMan.see_enemy_now() && + m_psy_hit->check_start_conditions()) return true; + + return false; + } + + if (!EnemyMan.get_enemy()) return false; + if (m_time_last_tube + MIN_DELAY > time()) return false; + if (EnemyMan.see_enemy_duration() < SEE_ENEMY_DURATION) return false; + if (!m_psy_hit->check_start_conditions()) return false; + if (EnemyMan.get_enemy()->Position().distance_to(Position()) < m_tube_min_dist) return false; + + return true; +} + +////////////////////////////////////////////////////////////////////////// + + +const MonsterSpace::SBoneRotation &CController::head_orientation () const +{ + return m_custom_dir_base->get_head_orientation(); +} + +void CController::test_covers() +{ + ////////////////////////////////////////////////////////////////////////// + // update covers + ////////////////////////////////////////////////////////////////////////// +} + +void CController::create_base_controls() +{ + m_custom_dir_base = new CControllerDirection(); + + m_dir_base = m_custom_dir_base; + m_anim_base = new CControlAnimationBase(); + + m_move_base = new CControlMovementBase(); + m_path_base = new CControlPathBuilderBase(); +} + +void CController::TranslateActionToPathParams() +{ +// if ((anim().m_tAction != ACT_RUN) && (anim().m_tAction != ACT_WALK_FWD)) { + inherited::TranslateActionToPathParams(); + return; +/* } + + u32 vel_mask = (m_bDamaged ? MonsterMovement::eVelocityParamsWalkDamaged : MonsterMovement::eVelocityParamsWalk); + u32 des_mask = (m_bDamaged ? MonsterMovement::eVelocityParameterWalkDamaged : MonsterMovement::eVelocityParameterWalkNormal); + + if (m_force_real_speed) vel_mask = des_mask; + + path().set_velocity_mask (vel_mask); + path().set_desirable_mask (des_mask); + path().enable_path (); +*/ +} + +bool CController::is_relation_enemy(const CEntityAlive *tpEntityAlive) const +{ + // MONSTER_COMMUNITY_ID + if (xr_strcmp(*(tpEntityAlive->cNameSect()), "stalker_zombied") == 0) return false; + if (is_community_friend_overrides(tpEntityAlive)) return false; + + return inherited::is_relation_enemy(tpEntityAlive); +} + +void CController::set_mental_state(EMentalState state) +{ + if (m_mental_state == state) return; + + m_mental_state = state; + + //m_custom_anim_base->on_switch_controller (); +} + + + + +#ifdef DEBUG +CBaseMonster::SDebugInfo CController::show_debug_info() +{ + CBaseMonster::SDebugInfo info = inherited::show_debug_info(); + if (!info.active) return CBaseMonster::SDebugInfo(); + + + // Draw Controlled Lines + DBG().level_info(this).clear(); + + Fvector my_pos = Position(); + my_pos.y += 1.5f; + + + for (u32 i=0; i < m_controlled_objects.size(); i++) { + Fvector enemy_pos = m_controlled_objects[i]->Position(); + + Fvector dir; + dir.sub(enemy_pos, Position()); + dir.div(2.f); + Fvector new_pos; + new_pos.add(Position(),dir); + new_pos.y += 10.f; + + enemy_pos.y += 1.0f; + + DBG().level_info(this).add_item(my_pos, new_pos, D3DCOLOR_XRGB(0,255,255)); + DBG().level_info(this).add_item(enemy_pos, new_pos, D3DCOLOR_XRGB(0,255,255)); + } + + return CBaseMonster::SDebugInfo(); +} +#endif + +#ifdef _DEBUG +void CController::debug_on_key(int key) +{ + switch (key){ + case DIK_MINUS: + //m_sound_aura_left_channel.play_at_pos(Level().CurrentEntity(), Fvector().set(-1.f, 0.f, 1.f), sm_2D); + //m_sound_aura_right_channel.play_at_pos(Level().CurrentEntity(), Fvector().set(1.f, 0.f, 1.f), sm_2D); + + if (m_psy_hit->check_start_conditions()) { + control().activate(ControlCom::eComCustom1); + } + //P1.set (Actor()->Position()); + // + //DBG().level_info(this).remove_item (u32(0)); + //DBG().level_info(this).add_item(P1,0.5f,COLOR_BLUE,0); + + + //if (!fsimilar(P1.square_magnitude(),0.f) && + // !fsimilar(P2.square_magnitude(),0.f)) { + // const CCoverPoint *cover = CoverMan->find_cover(P1,P2,10.f,40.f); + // if (cover) { + // DBG().level_info(this).remove_item (3); + // DBG().level_info(this).add_item (cover->position(),0.8f,COLOR_RED,3); + // } + //} + + + break; + case DIK_EQUALS: + P2.set (Actor()->Position()); + DBG().level_info(this).remove_item (1); + DBG().level_info(this).add_item(P2,0.5f,COLOR_GREEN,1); + + if (!fsimilar(P1.square_magnitude(),0.f) && + !fsimilar(P2.square_magnitude(),0.f)) { + const CCoverPoint *cover = CoverMan->find_cover(P1,P2,10.f,40.f); + if (cover) { + DBG().level_info(this).remove_item (3); + DBG().level_info(this).add_item (cover->position(),0.8f,COLOR_RED,3); + } + } + + //m_sound_aura_hit_left_channel.play_at_pos(Level().CurrentEntity(), Fvector().set(-1.f, 0.f, 1.f), sm_2D); + //m_sound_aura_hit_right_channel.play_at_pos(Level().CurrentEntity(), Fvector().set(1.f, 0.f, 1.f), sm_2D); + break; + } +} +#endif + + + diff --git a/src/xrGameLA/ai/monsters/controller/controller.h b/src/xrGameLA/ai/monsters/controller/controller.h new file mode 100644 index 000000000..1a6cc2284 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller.h @@ -0,0 +1,190 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../anim_triple.h" +#include "../../../script_export_space.h" + +class CControllerAnimation; +class CControllerDirection; +class SndShockEffector; +class CControllerPsyHit; +class CControllerAura; + +class CController : public CBaseMonster { + typedef CBaseMonster inherited; + + u8 m_max_controlled_number; + ref_sound control_start_sound; // звук, который играется в голове у актера + ref_sound control_hit_sound; // звук, который играется в голове у актера + + ref_sound m_sound_hit_fx; + SndShockEffector* m_sndShockEffector; + + SAttackEffector m_control_effector; + + u32 time_control_hit_started; + bool active_control_fx; + + LPCSTR particles_fire; + + CControllerAnimation *m_custom_anim_base; + CControllerDirection *m_custom_dir_base; + + u32 m_psy_fire_start_time; + u32 m_psy_fire_delay; + + bool m_tube_at_once; + + + ////////////////////////////////////////////////////////////////////////// + // PsyAura + CControllerAura *m_aura; + + struct SAuraSound { + ref_sound left; + ref_sound right; + } aura_sound; + SAuraSound *current_aura_sound; + + +public: + float aura_radius; + float aura_damage; + + bool tube_at_once() { return m_tube_at_once; } + bool set_tube_at_once(bool val) { m_tube_at_once = val; } + ////////////////////////////////////////////////////////////////////////// + +public: + CControllerPsyHit *m_psy_hit; + + ref_sound m_sound_aura_left_channel; + ref_sound m_sound_aura_right_channel; + ref_sound m_sound_aura_hit_left_channel; + ref_sound m_sound_aura_hit_right_channel; + + ref_sound m_sound_tube_start; + ref_sound m_sound_tube_pull; + ref_sound m_sound_tube_hit_left; + ref_sound m_sound_tube_hit_right; + + ref_sound m_sound_tube_prepare; + +public: + SVelocityParam m_velocity_move_fwd; + SVelocityParam m_velocity_move_bkwd; + +public: + //CControllerAnimation &custom_anim() {return (*m_custom_anim_base);} + CControllerDirection &custom_dir() {return (*m_custom_dir_base);} + +public: + xr_vector m_controlled_objects; + +public: + CController (); + virtual ~CController (); + + virtual void Load (LPCSTR section); + virtual void reload (LPCSTR section); + virtual void reinit (); + virtual void UpdateCL (); + virtual void shedule_Update (u32 dt); + virtual void Die (CObject* who); + + virtual void net_Destroy (); + virtual BOOL net_Spawn (CSE_Abstract *DC); + virtual void net_Relcase (CObject *O); + + virtual void CheckSpecParams (u32 spec_params); + virtual void InitThink (); + + virtual void create_base_controls(); + + virtual const MonsterSpace::SBoneRotation &head_orientation () const; + + virtual void TranslateActionToPathParams (); + + virtual bool ability_pitch_correction () {return false;} + + //------------------------------------------------------------------- + + virtual bool is_relation_enemy (const CEntityAlive *tpEntityAlive) const; + xr_vector m_friend_community_overrides; + void load_friend_community_overrides (LPCSTR section); + bool is_community_friend_overrides (const CEntityAlive *tpEntityAlive) const; + //------------------------------------------------------------------- + // Controller ability + bool HasUnderControl () {return (!m_controlled_objects.empty());} + void TakeUnderControl (CEntity *); + void UpdateControlled (); + void FreeFromControl (); + void OnFreedFromControl (const CEntity *); // если монстр сам себя освободил (destroyed || die) + + void set_controlled_task (u32 task); + + + + void play_control_sound_start (); + void play_control_sound_hit (); + + void control_hit (); + + void psy_fire (); + bool can_psy_fire (); + + void tube_fire (); + bool can_tube_fire (); + //bool can_melee_at (); + //void melee_attack (); + u32 m_time_last_tube; + + //float m_psy_hit_damage; // superseeded by CControllerAura::aura_damage + float m_tube_damage; + float m_tube_min_dist; + + void set_psy_fire_delay_zero (); + void set_psy_fire_delay_default (); + + //------------------------------------------------------------------- + + + +public: + + void draw_fire_particles(); + void test_covers(); + + +public: + enum EMentalState { + eStateIdle, + eStateDanger + } m_mental_state; + + void set_mental_state (EMentalState state); + +public: + virtual bool use_center_to_aim () const {return true;} + + SAnimationTripleData anim_triple_control; + +#ifdef DEBUG + virtual CBaseMonster::SDebugInfo show_debug_info(); + +#endif + +private: +#ifdef _DEBUG + virtual void debug_on_key (int key); + + Fvector P1,P2; +#endif + + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CController) +#undef script_type_list +#define script_type_list save_type_list(CController) + diff --git a/src/xrGameLA/ai/monsters/controller/controller_animation.cpp b/src/xrGameLA/ai/monsters/controller/controller_animation.cpp new file mode 100644 index 000000000..a704a28c6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_animation.cpp @@ -0,0 +1,364 @@ +#include "stdafx.h" +#include "controller_animation.h" +#include "controller.h" +#include "../../../detail_path_manager.h" +#include "../../../level.h" +#include "../control_direction_base.h" +#include "../control_path_builder_base.h" +#include "controller_direction.h" +#include "../monster_velocity_space.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" + +const float _pmt_psy_attack_time = 0.5f; + +void CControllerAnimation::reinit() +{ + m_controller = smart_cast(m_object); + + load (); + inherited::reinit (); + + set_body_state (eTorsoIdle, eLegsTypeStand); + + m_man->animation().add_anim_event(m_torso[eTorsoPsyAttack], _pmt_psy_attack_time, CControlAnimation::eAnimationHit); + m_wait_torso_anim_end = false; + +} + +void CControllerAnimation::on_start_control(ControlCom::EControlType type) +{ + switch (type) { + case ControlCom::eControlAnimation: + m_man->subscribe (this, ControlCom::eventAnimationEnd); + m_man->subscribe (this, ControlCom::eventTorsoAnimationEnd); + m_man->subscribe (this, ControlCom::eventLegsAnimationEnd); + + on_switch_controller(); + break; + } +} + +void CControllerAnimation::on_stop_control (ControlCom::EControlType type) +{ + switch (type) { + case ControlCom::eControlAnimation: + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); + m_man->unsubscribe (this, ControlCom::eventTorsoAnimationEnd); + m_man->unsubscribe (this, ControlCom::eventLegsAnimationEnd); + break; + } +} + +void CControllerAnimation::on_event(ControlCom::EEventType type, ControlCom::IEventData *data) +{ + switch (type) { + case ControlCom::eventAnimationEnd: select_animation(); break; + case ControlCom::eventTorsoAnimationEnd: + m_wait_torso_anim_end = false; + select_torso_animation (); + break; + case ControlCom::eventLegsAnimationEnd: select_legs_animation(); break; + case ControlCom::eventAnimationSignal: + { + SAnimationSignalEventData *event_data = (SAnimationSignalEventData *)data; + if (event_data->event_id == CControlAnimation::eAnimationHit) { +// if (event_data->motion == m_torso[eTorsoPsyAttack]) +// m_controller->psy_fire(); +// else + check_hit(event_data->motion,event_data->time_perc); break; + } + } + } +} + +void CControllerAnimation::update_frame() +{ + + inherited::update_frame(); + return; + + + //if (m_controller->m_mental_state == CController::eStateIdle) { + // inherited::update_frame(); + // return; + //} + // + //if (is_moving()) set_path_direction(); + // + //select_legs_animation (); + //select_torso_animation (); + // + //select_velocity (); +} + +void CControllerAnimation::load() +{ + IKinematicsAnimated *skeleton = smart_cast(m_object->Visual()); + + m_legs[eLegsStand] = skeleton->ID_Cycle_Safe("new_idle_0"); + m_legs[eLegsSteal] = skeleton->ID_Cycle_Safe("new_cr_idle_0"); + m_legs[eLegsRun] = skeleton->ID_Cycle_Safe("new_run_fwd_0"); + m_legs[eLegsWalk] = skeleton->ID_Cycle_Safe("new_walk_0"); + m_legs[eLegsBackRun] = skeleton->ID_Cycle_Safe("new_run_beack_0"); + m_legs[eLegsRunFwdLeft] = skeleton->ID_Cycle_Safe("stand_fwd_ls"); + m_legs[eLegsRunFwdRight] = skeleton->ID_Cycle_Safe("stand_fwd_rs"); + m_legs[eLegsRunBkwdLeft] = skeleton->ID_Cycle_Safe("stand_bwd_ls"); + m_legs[eLegsRunBkwdRight] = skeleton->ID_Cycle_Safe("stand_bwd_rs"); + m_legs[eLegsStealFwd] = skeleton->ID_Cycle_Safe("new_walk_steal_0"); + m_legs[eLegsStealBkwd] = skeleton->ID_Cycle_Safe("new_walk_steal_beack_0"); + + m_legs[eLegsStealFwdLeft] = skeleton->ID_Cycle_Safe("steal_fwd_ls"); + m_legs[eLegsStealFwdRight] = skeleton->ID_Cycle_Safe("steal_fwd_rs"); + m_legs[eLegsStealBkwdLeft] = skeleton->ID_Cycle_Safe("steal_bwd_ls"); + m_legs[eLegsStealBkwdRight] = skeleton->ID_Cycle_Safe("steal_bwd_rs"); + + m_legs[eLegsStandDamaged] = skeleton->ID_Cycle_Safe("new_run_fwd_0"); + m_legs[eLegsRunDamaged] = skeleton->ID_Cycle_Safe("new_run_fwd_0"); + m_legs[eLegsWalkDamaged] = skeleton->ID_Cycle_Safe("new_run_fwd_0"); + m_legs[eLegsBackRunDamaged] = skeleton->ID_Cycle_Safe("new_run_fwd_0"); + m_legs[eLegsRunStrafeLeftDamaged] = skeleton->ID_Cycle_Safe("new_run_fwd_0"); + m_legs[eLegsRunStrafeRightDamaged] = skeleton->ID_Cycle_Safe("new_run_fwd_0"); + + m_torso[eTorsoIdle] = skeleton->ID_Cycle_Safe("new_torso_idle_0"); + m_torso[eTorsoSteal] = skeleton->ID_Cycle_Safe("new_torso_steal_0"); + m_torso[eTorsoPsyAttack] = skeleton->ID_Cycle_Safe("new_torso_attack_0"); + m_torso[eTorsoRun] = skeleton->ID_Cycle_Safe("new_torso_run_0"); + + add_path_rotation (eLegsTypeRun, 0, eLegsRun); + add_path_rotation (eLegsTypeRun, PI, eLegsBackRun); + add_path_rotation (eLegsTypeRun, PI_DIV_4, eLegsRunFwdLeft); + add_path_rotation (eLegsTypeRun, -PI_DIV_4, eLegsRunFwdRight); + add_path_rotation (eLegsTypeRun, (PI - PI_DIV_4), eLegsRunBkwdLeft); + add_path_rotation (eLegsTypeRun, -(PI - PI_DIV_4),eLegsRunBkwdRight); + + add_path_rotation (eLegsTypeStealMotion, 0, eLegsStealFwd); + add_path_rotation (eLegsTypeStealMotion, PI, eLegsStealBkwd); + add_path_rotation (eLegsTypeStealMotion, PI_DIV_4, eLegsStealFwdLeft); + add_path_rotation (eLegsTypeStealMotion, -PI_DIV_4, eLegsStealFwdRight); + add_path_rotation (eLegsTypeStealMotion, (PI - PI_DIV_4), eLegsStealBkwdLeft); + add_path_rotation (eLegsTypeStealMotion, -(PI - PI_DIV_4),eLegsStealBkwdRight); + + + // 1. link animation with action + // 2. link animation with velocities and path velocities + // 3. +} + +void CControllerAnimation::add_path_rotation(ELegsActionType action, float angle, ELegsActionType type) +{ + SPathRotations rot; + rot.angle = angle; + rot.legs_motion = type; + + PATH_ROTATIONS_MAP_IT map_it = m_path_rotations.find(action); + if (map_it == m_path_rotations.end()) { + PATH_ROTATIONS_VEC vec; + vec.push_back(rot); + m_path_rotations.insert(mk_pair(action, vec)); + } else { + map_it->second.push_back(rot); + } +} + +void CControllerAnimation::select_velocity() +{ + if (m_current_legs_action == eLegsTypeRun) { + + // if we are moving, get yaw from path + float cur_yaw, target_yaw; + m_man->direction().get_heading(cur_yaw, target_yaw); + SPathRotations path_rot = get_path_rotation(cur_yaw); + if ((path_rot.legs_motion == eLegsBackRun) || + (path_rot.legs_motion == eLegsRunBkwdLeft) || + (path_rot.legs_motion == eLegsRunBkwdRight)) { + m_man->path_builder().set_desirable_speed(2.f); + } else { + m_man->path_builder().set_desirable_speed(4.f); + } + + } + else if (eLegsTypeWalk == m_current_legs_action) + { m_man->path_builder().set_desirable_speed(1.3f); } + else if (m_current_legs_action == eLegsTypeStealMotion) + { m_man->path_builder().set_desirable_speed(1.1f); } + else + { m_man->path_builder().set_desirable_speed(0.f); } +} + +// set body direction using path_direction +// and according to point it has to look at +void CControllerAnimation::set_path_direction() +{ + float cur_yaw = Fvector().sub(m_controller->custom_dir().get_head_look_point(), m_object->Position()).getH(); + cur_yaw = angle_normalize(-cur_yaw); + + float target_yaw = m_man->path_builder().detail().direction().getH(); + target_yaw = angle_normalize(-target_yaw); + + SPathRotations path_rot = get_path_rotation(cur_yaw); + + m_object->dir().set_heading(angle_normalize(target_yaw + path_rot.angle)); + m_object->dir().set_heading_speed(PI); +} + +void CControllerAnimation::select_torso_animation() +{ + if (m_wait_torso_anim_end) return; + + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + if (!ctrl_data) return; + + MotionID target_motion; + + // check fire animation +// if (m_controller->can_psy_fire()) { +// target_motion = m_torso[eTorsoPsyAttack]; +// m_wait_torso_anim_end = true; +// } else { + target_motion = m_torso[m_current_torso_action]; +// } + + if ((ctrl_data->torso.motion != target_motion) || m_wait_torso_anim_end) { + ctrl_data->torso.motion = target_motion; + ctrl_data->torso.actual = false; + } + +} + +void CControllerAnimation::select_legs_animation() +{ + // select from action + ELegsActionType legs_action = eLegsUndefined; + + if (is_moving()) { + // if we are moving, get yaw from path + float cur_yaw, target_yaw; + m_man->direction().get_heading(cur_yaw, target_yaw); + + SPathRotations path_rot = get_path_rotation(cur_yaw); + legs_action = path_rot.legs_motion; + + } else { + // else select standing animation + for (LEGS_MOTION_MAP_IT it = m_legs.begin(); it != m_legs.end(); it++) { + if ((it->first & m_current_legs_action) == m_current_legs_action) { + legs_action = it->first; + break; + } + } + VERIFY(legs_action != eLegsUndefined); + } + + // start new animation + SControlAnimationData *ctrl_data = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + if (!ctrl_data) return; + + if (ctrl_data->legs.motion != m_legs[legs_action]) + ctrl_data->legs.actual = false; + + ctrl_data->legs.motion = m_legs[legs_action]; +} + +CControllerAnimation::SPathRotations CControllerAnimation::get_path_rotation(float cur_yaw) +{ + float target_yaw = m_man->path_builder().detail().direction().getH(); + target_yaw = angle_normalize(-target_yaw); + + float diff = angle_difference(cur_yaw,target_yaw); + if (from_right(target_yaw, cur_yaw)) diff = -diff; + + diff = angle_normalize(diff); + + PATH_ROTATIONS_VEC_IT it_best = m_path_rotations[m_current_legs_action].begin(); + float best_diff = flt_max; + for (PATH_ROTATIONS_VEC_IT it = m_path_rotations[m_current_legs_action].begin(); it != m_path_rotations[m_current_legs_action].end(); it++) { + float angle_diff = angle_normalize(it->angle); + + float cur_diff = angle_difference(angle_diff, diff); + if (cur_diff < best_diff) { + best_diff = cur_diff; + it_best = it; + } + } + + return (*it_best); +} + +void CControllerAnimation::set_body_state(ETorsoActionType torso, ELegsActionType legs) +{ + //m_current_legs_action = CControllerAnimation::eLegsTypeStealMotion; + //m_current_torso_action = CControllerAnimation::eTorsoSteal; + m_current_legs_action = legs; + m_current_torso_action = torso; +} + +bool CControllerAnimation::is_moving() +{ + if (!m_man->path_builder().is_moving_on_path()) return false; + + if (((m_current_legs_action & eLegsTypeStealMotion) != eLegsTypeStealMotion) && + ((m_current_legs_action & eLegsTypeWalk) != eLegsTypeWalk) && + ((m_current_legs_action & eLegsTypeRun) != eLegsTypeRun)) return false; + + + return true; +} + +// if we gonna build path in direction opposite which we look +// then set negative speed +void CControllerAnimation::set_path_params() +{ + bool moving_action = ((m_current_legs_action & eLegsTypeStealMotion) == eLegsTypeStealMotion) || + ((m_current_legs_action & eLegsTypeWalk) == eLegsTypeWalk) || + ((m_current_legs_action & eLegsTypeRun) == eLegsTypeRun); + + if (moving_action) { + + u32 vel_mask = 0; + u32 des_mask = 0; + + bool looking_fwd = true; + + Fvector target_pos = m_object->path().get_target_set(); + Fvector dir = Fvector().sub(target_pos, m_object->Position()); + if (!fis_zero(dir.square_magnitude())) { + + float target_yaw = dir.getH(); + target_yaw = angle_normalize(-target_yaw); + float cur_yaw = m_man->direction().get_heading_current(); + + if (angle_difference(target_yaw,cur_yaw) > PI_DIV_2) + looking_fwd = false; + } + + if (looking_fwd) { + vel_mask = MonsterMovement::eControllerVelocityParamsMoveFwd; + des_mask = MonsterMovement::eControllerVelocityParameterMoveFwd; + } else { + vel_mask = MonsterMovement::eControllerVelocityParamsMoveBkwd; + des_mask = MonsterMovement::eControllerVelocityParameterMoveBkwd; + } + + m_object->path().set_velocity_mask (vel_mask); + m_object->path().set_desirable_mask (des_mask); + + m_object->path().enable_path (); + } else { + m_object->path().disable_path (); + } +} + +void CControllerAnimation::on_switch_controller() +{ + if (m_controller->m_mental_state == CController::eStateDanger) { + m_wait_torso_anim_end = false; + set_body_state (eTorsoIdle, eLegsTypeStand); + + select_torso_animation (); + select_legs_animation (); + } else { + select_animation (); + } +} + + diff --git a/src/xrGameLA/ai/monsters/controller/controller_animation.h b/src/xrGameLA/ai/monsters/controller/controller_animation.h new file mode 100644 index 000000000..0bb20a6fb --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_animation.h @@ -0,0 +1,109 @@ +#pragma once + +#include "../control_animation_base.h" +#include "../ai_monster_defs.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" + +class CController; + +class CControllerAnimation : public CControlAnimationBase { + typedef CControlAnimationBase inherited; + + CController *m_controller; + +public: + enum ELegsActionType { + eLegsTypeBase = u32(1) << 15, + + // ----------------------------------------- + + eLegsTypeStand = eLegsTypeBase << 1, + eLegsTypeSteal = eLegsTypeBase << 2, + eLegsTypeStealMotion = eLegsTypeBase << 3, + eLegsTypeWalk = eLegsTypeBase << 4, + eLegsTypeRun = eLegsTypeBase << 5, + + // ----------------------------------------- + + eLegsStand = eLegsTypeStand | 1, + eLegsSteal = eLegsTypeSteal | 1, + eLegsRun = eLegsTypeRun | 1, + eLegsWalk = eLegsTypeWalk | 1, + eLegsBackRun = eLegsTypeRun | 2, + eLegsRunFwdLeft = eLegsTypeRun | 3, + eLegsRunFwdRight = eLegsTypeRun | 4, + eLegsRunBkwdLeft = eLegsTypeRun | 5, + eLegsRunBkwdRight = eLegsTypeRun | 6, + eLegsStealFwd = eLegsTypeStealMotion | 1, + eLegsStealBkwd = eLegsTypeStealMotion | 2, + eLegsStealFwdLeft = eLegsTypeStealMotion | 3, + eLegsStealFwdRight = eLegsTypeStealMotion | 4, + eLegsStealBkwdLeft = eLegsTypeStealMotion | 5, + eLegsStealBkwdRight = eLegsTypeStealMotion | 6, + + + eLegsStandDamaged = eLegsTypeStand | 2, + eLegsRunDamaged = eLegsTypeRun | 7, + eLegsWalkDamaged = eLegsTypeWalk | 2, + eLegsBackRunDamaged = eLegsTypeRun | 8, + eLegsRunStrafeLeftDamaged = eLegsTypeRun | 9, + eLegsRunStrafeRightDamaged = eLegsTypeRun | 10, + + eLegsUndefined = u32(-1), + }; + + enum ETorsoActionType { + eTorsoIdle, + eTorsoSteal, + eTorsoPsyAttack, + eTorsoRun + }; + +private: + ELegsActionType m_current_legs_action; + ETorsoActionType m_current_torso_action; + + DEFINE_MAP (ELegsActionType, MotionID, LEGS_MOTION_MAP, LEGS_MOTION_MAP_IT); + DEFINE_MAP (ETorsoActionType, MotionID, TORSO_MOTION_MAP, TORSO_MOTION_MAP_IT); + + LEGS_MOTION_MAP m_legs; + TORSO_MOTION_MAP m_torso; + + struct SPathRotations{ + float angle; + ELegsActionType legs_motion; + }; + + DEFINE_VECTOR (SPathRotations, PATH_ROTATIONS_VEC, PATH_ROTATIONS_VEC_IT); + DEFINE_MAP (ELegsActionType, PATH_ROTATIONS_VEC, PATH_ROTATIONS_MAP, PATH_ROTATIONS_MAP_IT); + PATH_ROTATIONS_MAP m_path_rotations; + + bool m_wait_torso_anim_end; + +public: + virtual void reinit (); + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + virtual void on_start_control (ControlCom::EControlType type); + virtual void on_stop_control (ControlCom::EControlType type); + virtual void update_frame (); + + // load + void load (); + void add_path_rotation (ELegsActionType action, float angle, ELegsActionType type); + + void set_body_state (ETorsoActionType, ELegsActionType); + + void set_path_params (); + void on_switch_controller (); +private: + void select_velocity (); + void set_path_direction (); + + void select_torso_animation (); + void select_legs_animation (); + + SPathRotations get_path_rotation (float cur_yaw); + + bool is_moving (); + +}; diff --git a/src/xrGameLA/ai/monsters/controller/controller_direction.cpp b/src/xrGameLA/ai/monsters/controller/controller_direction.cpp new file mode 100644 index 000000000..1ba0c087b --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_direction.cpp @@ -0,0 +1,124 @@ +#include "stdafx.h" +#include "controller_direction.h" +#include "controller.h" +#include "../../../game_object_space.h" + +const float _pmt_head_bone_limit = PI_DIV_6; +const float _pmt_torso_bone_limit = PI_DIV_3; +const float _pmt_rotation_speed = PI_MUL_3; +const float _pmt_min_speed = deg(10); + +void CControllerDirection::reinit() +{ + inherited::reinit (); + m_controller = smart_cast(m_object); + + assign_bones (); + + m_head_orient = m_man->path_builder().body_orientation(); + m_head_look_point.set (0.f,0.f,0.f); +} + +void CControllerDirection::bone_callback(CBoneInstance *B) +{ + CControllerDirection *this_class = static_cast (B->callback_param()); + this_class->m_bones.Update(B, time()); +} + +void CControllerDirection::assign_bones() +{ + // Установка callback на кости + IKinematics *kinematics = smart_cast(m_controller->Visual()); + + m_bone_spine = &kinematics->LL_GetBoneInstance(kinematics->LL_BoneID("bip01_spine")); + m_bone_head = &kinematics->LL_GetBoneInstance(kinematics->LL_BoneID("bip01_head")); + + if(!m_controller->PPhysicsShell()) { //нельзя ставить колбеки, если создан физ шел - у него стоят свои колбеки!!! + m_bone_spine->set_callback (bctCustom, bone_callback, this); + m_bone_head->set_callback (bctCustom, bone_callback, this); + } + + // Bones settings + m_bones.Reset(); + m_bones.AddBone(m_bone_spine, AXIS_X); m_bones.AddBone(m_bone_spine, AXIS_Y); + m_bones.AddBone(m_bone_head, AXIS_X); m_bones.AddBone(m_bone_head, AXIS_Y); +} + + +void CControllerDirection::update_head_orientation() +{ + m_head_orient.current.yaw = 0.f; + m_head_orient.current.pitch = 0.f; + m_head_orient.current.roll = 0.f; + + bonesAxis &x_spine = m_bones.GetBoneParams (m_bone_spine, AXIS_X); + bonesAxis &x_head = m_bones.GetBoneParams (m_bone_head, AXIS_X); + + float yaw = x_spine.cur_yaw + x_head.cur_yaw; + + // установить параметры вращения по yaw + m_head_orient.current.yaw = m_man->direction().get_heading_current() + yaw; +} + +void CControllerDirection::update_schedule() +{ + inherited::update_schedule (); + + update_head_orientation (); +} + + +void CControllerDirection::head_look_point(const Fvector &look_point) +{ + m_head_look_point = look_point; + + float dir_yaw,dir_pitch; + Fvector().sub (look_point, get_head_position(m_controller)).getHP(dir_yaw,dir_pitch); + dir_yaw = angle_normalize(-dir_yaw); + + float bone_angle_head; + float bone_angle_torso; + + // установить параметры вращения по heading + float cur_yaw = m_man->direction().get_heading_current(); + float dy = _abs(angle_normalize_signed(dir_yaw - cur_yaw)); // дельта, на которую нужно поворачиваться + + bone_angle_head = _pmt_head_bone_limit / (_pmt_head_bone_limit + _pmt_torso_bone_limit) * dy; + bone_angle_torso = _pmt_torso_bone_limit / (_pmt_head_bone_limit + _pmt_torso_bone_limit) * dy; + + clamp (bone_angle_head, 0.f, _pmt_head_bone_limit); + clamp (bone_angle_torso, 0.f, _pmt_torso_bone_limit); + + if (!from_right(dir_yaw,cur_yaw)) { + bone_angle_head *= -1.f; + bone_angle_torso *= -1.f; + } + + // setup speed + float bone_speed; + bonesAxis &x_spine = m_bones.GetBoneParams (m_bone_spine, AXIS_X); + bonesAxis &x_head = m_bones.GetBoneParams (m_bone_head, AXIS_X); + + float target_dy = _abs(bone_angle_head + bone_angle_torso); + if (fis_zero(target_dy)) + bone_speed = _pmt_min_speed; + else + bone_speed = _pmt_min_speed + _pmt_rotation_speed * + (_abs((x_spine.cur_yaw + x_head.cur_yaw) - (bone_angle_head + bone_angle_torso)) + / (2*(_pmt_head_bone_limit + _pmt_torso_bone_limit)) ); + // set motion + m_bones.SetMotion (m_bone_spine, AXIS_X, bone_angle_torso, bone_speed, 1000); + m_bones.SetMotion (m_bone_head, AXIS_X, bone_angle_head, bone_speed, 1000); + + + //// установить параметры вращения по pitch (более упрощеная схема, без расчета скорости вращения) + //bone_angle_head = _pmt_head_bone_limit / (_pmt_head_bone_limit + _pmt_torso_bone_limit) * dir_pitch; + //bone_angle_torso = _pmt_torso_bone_limit / (_pmt_head_bone_limit + _pmt_torso_bone_limit) * dir_pitch; + + //clamp (bone_angle_head, -_pmt_head_bone_limit, _pmt_head_bone_limit); + //clamp (bone_angle_torso, -_pmt_torso_bone_limit, _pmt_torso_bone_limit); + // + //m_bones.SetMotion(m_bone_spine, AXIS_Y, bone_angle_torso, _pmt_rotation_speed, 1000); + //m_bones.SetMotion(m_bone_head, AXIS_Y, bone_angle_head, _pmt_rotation_speed, 1000); +} + diff --git a/src/xrGameLA/ai/monsters/controller/controller_direction.h b/src/xrGameLA/ai/monsters/controller/controller_direction.h new file mode 100644 index 000000000..73da60692 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_direction.h @@ -0,0 +1,37 @@ +#pragma once + +#include "../control_direction_base.h" +#include "../ai_monster_bones.h" +#include "../../../ai_monster_space.h" + + +class CController; + +class CControllerDirection : public CControlDirectionBase { + typedef CControlDirectionBase inherited; + + CController *m_controller; + + bonesManipulation m_bones; + CBoneInstance *m_bone_spine; + CBoneInstance *m_bone_head; + + MonsterSpace::SBoneRotation m_head_orient; + + Fvector m_head_look_point; + +public: + virtual void reinit (); + virtual void update_schedule (); + + void head_look_point (const Fvector &look_point); + Fvector &get_head_look_point() {return m_head_look_point;} + + const MonsterSpace::SBoneRotation &get_head_orientation() const {return m_head_orient;} + +private: + static void __stdcall bone_callback (CBoneInstance *B); + + void assign_bones (); + void update_head_orientation (); +}; diff --git a/src/xrGameLA/ai/monsters/controller/controller_psy_aura.cpp b/src/xrGameLA/ai/monsters/controller/controller_psy_aura.cpp new file mode 100644 index 000000000..e0f812db2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_psy_aura.cpp @@ -0,0 +1,306 @@ +#include "stdafx.h" +#include "controller_psy_aura.h" +#include "controller.h" +#include "../../../actor.h" +#include "../../../level.h" +#include "../../../CameraEffector.h" +#include "../../../ActorEffector.h" +#include "../../../../xr_ioconsole.h" + +CPPEffectorControllerAura::CPPEffectorControllerAura(const SPPInfo &ppi, u32 time_to_fade, const ref_sound &snd_left, const ref_sound &snd_right) +: inherited(ppi) +{ + m_time_to_fade = time_to_fade; + m_effector_state = eStateFadeIn; + m_time_state_started = Device.dwTimeGlobal; + + m_snd_left.clone (snd_left,st_Effect,sg_SourceType); + m_snd_right.clone (snd_right,st_Effect,sg_SourceType); + + m_snd_left.play_at_pos (Actor(), Fvector().set(-1.f, 0.f, 1.f), sm_Looped | sm_2D); + m_snd_right.play_at_pos (Actor(), Fvector().set(0.f, -1.f, 1.f), sm_Looped | sm_2D); +} + +CPPEffectorControllerAura::~CPPEffectorControllerAura(){ + m_snd_left.destroy(); + m_snd_right.destroy(); +} + +void CPPEffectorControllerAura::switch_off() +{ + m_effector_state = eStateFadeOut; + m_time_state_started = Device.dwTimeGlobal; +} + +void CPPEffectorControllerAura::stop_snds() +{ + m_time_to_fade = 0; + if (m_snd_left._feedback()) m_snd_left.stop(); + if (m_snd_right._feedback()) m_snd_right.stop(); +} + +BOOL CPPEffectorControllerAura::update() +{ // update factor + if (m_time_to_fade != 0.0f) + { + if (m_effector_state == eStatePermanent) { + m_factor = 1.f; + } else { + m_factor = float(Device.dwTimeGlobal - m_time_state_started) / float(m_time_to_fade); + if (m_effector_state == eStateFadeOut) m_factor = 1 - m_factor; + + if (m_factor > 1) { + m_effector_state = eStatePermanent; + m_factor = 1.f; + } else if (m_factor < 0) { + if (m_snd_left._feedback()) m_snd_left.stop(); + if (m_snd_right._feedback()) m_snd_right.stop(); + + return FALSE; + } + } + + // start new or play again? + if (!m_snd_left._feedback() && !m_snd_right._feedback()) { + m_snd_left.play_at_pos (Actor(), Fvector().set(-1.f, 0.f, 1.f), sm_Looped | sm_2D); + m_snd_right.play_at_pos (Actor(), Fvector().set(0.f, -1.f, 1.f), sm_Looped | sm_2D); + } + + if (m_snd_left._feedback()) m_snd_left.set_volume (m_factor); + if (m_snd_right._feedback()) m_snd_right.set_volume (m_factor); + + return TRUE; + } else return FALSE; +} + +////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////// + +CControllerAura::~CControllerAura(){ + /*if (b_do_double_vision_effect){ + Console->Execute("r2_aa off"); + if (hack_mblur_controll){ + Console->Execute("r2_mblur_enable on"); + } + }*/ +} + +void CControllerAura::load(LPCSTR section) +{ + inherited::load(pSettings->r_string(section, "aura_effector")); + + aura_sound.left.create(pSettings->r_string(section, "PsyAura_SoundLeftPath"), st_Effect, sg_SourceType); + aura_sound.right.create(pSettings->r_string(section, "PsyAura_SoundRightPath"), st_Effect, sg_SourceType); + + aura_radius_min = READ_IF_EXISTS(pSettings, r_float, section, "PsyAura_Radius_min", 5.f); + aura_radius_max = READ_IF_EXISTS(pSettings, r_float, section, "PsyAura_Radius_max", 90.f); + aura_radius_max_y = READ_IF_EXISTS(pSettings, r_float, section, "PsyAura_Radius_max_y", 3.f); + aura_damage = READ_IF_EXISTS(pSettings, r_float, section, "PsyAura_Damage", 0.02f); + + aura_regular_effector_sect = pSettings->r_string(section, "aura_regular_effector_sect"); + aura_hit_effector_sect = pSettings->r_string(section, "aura_hit_effector_sect"); + aura_effector_max_factor = READ_IF_EXISTS(pSettings, r_float, section, "aura_effector_max_factor", 0.30f); + + m_time_fake_aura = 0; + + Ivector2 fakeAuraDefaultDuration{ 5000, 13000 }; + Ivector2 fakeAuraDefaultDelay{ 5000, 10000 }; + m_time_fake_aura_duration = READ_IF_EXISTS(pSettings, r_ivector2, section, "PsyAura_Fake_Duration", fakeAuraDefaultDuration); + m_time_fake_aura_delay = READ_IF_EXISTS(pSettings, r_ivector2, section, "PsyAura_Fake_Delay", fakeAuraDefaultDelay); + m_time_started = 0; + m_hit_state = eNone; + + m_pmt_hit_delay = READ_IF_EXISTS(pSettings, r_u32, section, "PsyAura_HitDelay", 1000); + m_pmt_pp_hit_delay = READ_IF_EXISTS(pSettings, r_u32, section, "PsyAura_PPHitDelay", 300); + + current_effector_strength = 0.f; +} + +void CControllerAura::update_schedule() +{ + if (!m_object->g_Alive()) return; + + float dist_to_actor = Actor()->Position().distance_to(m_object->Position()); + float height_distance = m_object->Position().y - Actor()->Position().y; + + if ((dist_to_actor < aura_radius_max || dist_to_actor < aura_radius_min) && (abs(height_distance) < aura_radius_max_y)) + { + //if actor is in regular effect range + if ((dist_to_actor < aura_radius_max)){ + // first time? + if (m_time_fake_aura == 0) { + m_time_fake_aura = time() + Random.randI(m_time_fake_aura_delay.x, m_time_fake_aura_delay.y); + + if (m_effector) { + m_effector->switch_off(); + m_effector = nullptr; + } + } + else + { + if (m_effector) { + // check to stop + if (m_time_fake_aura < time()) { + m_effector->switch_off(); + m_effector = nullptr; + + m_time_fake_aura = time() + Random.randI(m_time_fake_aura_delay.x, m_time_fake_aura_delay.y); + } + } + else { + // check to start + if (m_time_fake_aura < time()){ + + m_effector = new CPPEffectorControllerAura(m_state, m_time_fake_aura_duration.x, aura_sound.left, aura_sound.right); + Actor()->Cameras().AddPPEffector(m_effector); + + //tatarinrafa: Lets use a bug with r2_aa on dx10 and 11 as a cool effect + /*hack_mblur_controll = Console->GetBool("r2_mblur_enable"); + if (b_do_double_vision_effect){ + Console->Execute("r2_aa on"); + Console->Execute("r2_mblur_enable off"); + }*/ + + m_time_fake_aura = time() + Random.randI(m_time_fake_aura_duration.x, m_time_fake_aura_duration.y); + } + } + } + } + + // if actor is close enough to trigger "close" effects (and aura damage is not 0) + if (dist_to_actor < aura_radius_min && !fis_zero(aura_damage)) + { + if (m_hit_state == eNone){ + m_hit_state = eEffectoring; + m_time_started = time(); + } + } + else + { + m_hit_state = eNone; + } + } + //if actor is out of aura range, then remove effectors + else + { + current_effector_strength = 0.f; + if (m_effector) + { + m_effector->switch_off(); + m_effector = nullptr; + } + + m_hit_state = eNone; + + /*if (b_do_double_vision_effect){ + Console->Execute("r2_aa off"); + if (hack_mblur_controll){ + Console->Execute("r2_mblur_enable on"); + } + }*/ + } + + // Separately add camera shaking effector + if (aura_effector_max_factor > 0.f) + { + auto ce = Actor()->Cameras().GetCamEffector((ECamEffectorType)effControllerAura2); + if (active()) { + if (!ce) + { + AddEffector(Actor(), effControllerAura2, aura_regular_effector_sect, GET_KOEFF_FUNC(this, &CControllerAura::get_effector_strength)); + // calculate effector strength based on distance at the moment it started (to prevent camera jumps) + if (aura_effector_max_factor > 0.f) + { + current_effector_strength = aura_effector_max_factor * (1.f - (dist_to_actor - aura_radius_min) / (aura_radius_max - aura_radius_min)); + } + } + } else { + if (ce) + RemoveEffector(Actor(), effControllerAura2); + } + } +} + +void CControllerAura::update_frame() +{ + if (m_hit_state == eNone) return; + + //Add effector that happens when actor gets very close and do damage to actor + switch (m_hit_state){ + case eEffectoring: + if (m_time_started + m_pmt_hit_delay < time()) { + // launch effector + AddEffector(Actor(), effControllerAura, aura_hit_effector_sect); + m_hit_state = eHit; + m_time_started = time(); + } + break; + case eHit: + if (m_time_started + m_pmt_pp_hit_delay < time()) { + m_object->Hit_Psy(Actor(), aura_damage); + m_hit_state = eEffectoring; + } + break; + } +} + +float xr_stdcall CControllerAura::get_effector_strength() +{ + // make smooth transition out of the effect + u32 effectorFadeTime = m_time_fake_aura_duration.x / 2; + if (active() && time() > (m_time_fake_aura - effectorFadeTime)) + { + return time() < m_time_fake_aura + ? current_effector_strength * (m_time_fake_aura - time()) / effectorFadeTime + : 0.f; + } + return current_effector_strength; +} + +void CControllerAura::on_death() +{ + if (m_effector) { + CEffectorCam* ce = Actor()->Cameras().GetCamEffector((ECamEffectorType)effControllerAura2); + if (ce) + RemoveEffector(Actor(), effControllerAura2); + + m_effector->switch_off(); + + m_effector = nullptr; + + m_hit_state = eNone; + } + + /*if (b_do_double_vision_effect){ + Console->Execute("r2_aa off"); + if (hack_mblur_controll){ + Console->Execute("r2_mblur_enable on"); + } + }*/ +} + +void CControllerAura::on_destroy() +{ + if (m_effector) { + CEffectorCam* ce = Actor()->Cameras().GetCamEffector((ECamEffectorType)effControllerAura2); + if (ce) + RemoveEffector(Actor(), effControllerAura2); + + m_effector->stop_snds(); + + m_effector = nullptr; + + m_hit_state = eNone; + } + + /*if (b_do_double_vision_effect){ + Console->Execute("r2_aa off"); + if (hack_mblur_controll){ + Console->Execute("r2_mblur_enable on"); + } + }*/ +} + + + diff --git a/src/xrGameLA/ai/monsters/controller/controller_psy_aura.h b/src/xrGameLA/ai/monsters/controller/controller_psy_aura.h new file mode 100644 index 000000000..26385b7c9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_psy_aura.h @@ -0,0 +1,88 @@ +#pragma once +//////////////////////////////////////////////////////////////////////// +// Effector controlling class +//////////////////////////////////////////////////////////////////////// +#include "../../../pp_effector_custom.h" + +class CController; + +struct SAuraSound { + ref_sound left; + ref_sound right; +}; + + +class CPPEffectorControllerAura : public CPPEffectorCustom { + typedef CPPEffectorCustom inherited; + + enum {eStateFadeIn, eStateFadeOut, eStatePermanent} m_effector_state; + + u32 m_time_state_started; + u32 m_time_to_fade; + + ref_sound m_snd_left; + ref_sound m_snd_right; + +public: + CPPEffectorControllerAura (const SPPInfo &ppi, u32 time_to_fade, const ref_sound &snd_left, const ref_sound &snd_right); + ~CPPEffectorControllerAura (); + virtual BOOL update (); + void switch_off (); + void stop_snds (); +}; + +class CControllerAura : public CPPEffectorCustomController{ + typedef CPPEffectorCustomController inherited; + + CController *m_object; + u32 m_time_last_update; + //tatarinrafa: Lets use a bug with r2_aa on dx10 or 11 as a cool effect which happens when mblur is off + //bool hack_mblur_controll; + + SAuraSound aura_sound; + + float aura_radius_min; + float aura_radius_max; + float aura_radius_max_y; + float aura_damage; + float aura_effector_max_factor; + + float current_effector_strength; + + LPCSTR aura_regular_effector_sect; + LPCSTR aura_hit_effector_sect; + + u32 m_time_fake_aura; + + // min/max fake aura duration + Ivector2 m_time_fake_aura_duration; + // min/max fake aura delay + Ivector2 m_time_fake_aura_delay; + + //bool b_do_double_vision_effect; + + // hits + enum { + eNone, + eEffectoring, + eHit + } m_hit_state; + + u32 m_time_started; + u32 m_pmt_hit_delay; + u32 m_pmt_pp_hit_delay; + + +public: + CControllerAura (CController *monster) : m_object(monster){} + ~CControllerAura (); + virtual void load (LPCSTR section); + + void on_destroy (); + void on_death (); + void update_schedule (); + void update_frame (); + float xr_stdcall get_effector_strength (); +}; + + diff --git a/src/xrGameLA/ai/monsters/controller/controller_psy_hit.cpp b/src/xrGameLA/ai/monsters/controller/controller_psy_hit.cpp new file mode 100644 index 000000000..5d4b44853 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_psy_hit.cpp @@ -0,0 +1,330 @@ +#include "stdafx.h" +#include "controller_psy_hit.h" +#include "../BaseMonster/base_monster.h" +#include "controller.h" +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../../../level.h" +#include "../../../actor.h" +#include "../../../ActorEffector.h" +#include "../../../../CameraBase.h" +#include "../../../CharacterPhysicsSupport.h" +#include "../../../level_debug.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" + +void CControllerPsyHit::load(LPCSTR section) +{ + m_min_tube_dist = pSettings->r_float(section,"tube_min_dist"); +} + +void CControllerPsyHit::reinit() +{ + inherited::reinit(); + + IKinematicsAnimated *skel = smart_cast(m_object->Visual()); + m_stage[0] = skel->ID_Cycle_Safe("psy_attack_0"); VERIFY(m_stage[0]); + m_stage[1] = skel->ID_Cycle_Safe("psy_attack_1"); VERIFY(m_stage[1]); + m_stage[2] = skel->ID_Cycle_Safe("psy_attack_2"); VERIFY(m_stage[2]); + m_stage[3] = skel->ID_Cycle_Safe("psy_attack_3"); VERIFY(m_stage[3]); + m_current_index = 0; + + m_sound_state = eNone; +} + +bool CControllerPsyHit::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured_pure()) return false; + + if (Actor()->Cameras().GetCamEffector(eCEControllerPsyHit)) + return false; + + if (m_object->Position().distance_to(Actor()->Position()) < m_min_tube_dist) + return false; + + return true; +} + +void CControllerPsyHit::activate() +{ + m_man->capture_pure (this); + m_man->subscribe (this, ControlCom::eventAnimationEnd); + + m_man->path_stop (this); + m_man->move_stop (this); + + ////////////////////////////////////////////////////////////////////////// + // set direction + SControlDirectionData *ctrl_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_dir); + ctrl_dir->heading.target_speed = 3.f; + ctrl_dir->heading.target_angle = m_man->direction().angle_to_target(Actor()->Position()); + + ////////////////////////////////////////////////////////////////////////// + m_current_index = 0; + play_anim (); + + m_blocked = false; + + set_sound_state (ePrepare); +} + +void CControllerPsyHit::deactivate() +{ + m_man->release_pure (this); + m_man->unsubscribe (this, ControlCom::eventAnimationEnd); + + if (m_blocked) { + NET_Packet P; + + Actor()->u_EventGen (P, GEG_PLAYER_WEAPON_HIDE_STATE, Actor()->ID()); + P.w_u32 (INV_STATE_BLOCK_ALL); + P.w_u8 (u8(false)); + Actor()->u_EventSend(P); + } + + set_sound_state (eNone); +} + +void CControllerPsyHit::on_event(ControlCom::EEventType type, ControlCom::IEventData *data) +{ + if (type == ControlCom::eventAnimationEnd) { + if (m_current_index < 3) { + m_current_index++; + play_anim (); + + switch (m_current_index) { + case 1: death_glide_start(); break; + case 2: hit(); break; + case 3: death_glide_end(); break; + } + } else { + m_man->deactivate (this); + return; + } + } +} + +void CControllerPsyHit::play_anim() +{ + SControlAnimationData *ctrl_anim = (SControlAnimationData*)m_man->data(this, ControlCom::eControlAnimation); + VERIFY (ctrl_anim); + + ctrl_anim->global.motion = m_stage[m_current_index]; + ctrl_anim->global.actual = false; +} + +bool CControllerPsyHit::check_conditions_final() +{ + if (!m_object->g_Alive()) return false; + if (!Actor()) return false; + if (m_object->EnemyMan.get_enemy() != Actor()) return false; + if (!Actor()->g_Alive()) return false; + + if (!m_object->EnemyMan.see_enemy_now()) return false; + if (m_object->Position().distance_to(Actor()->Position()) < m_min_tube_dist) return false; + + //// trace enemy (extended check visibility) + // + //DBG().level_info(this).clear (); + + //// 1. head-2-head + //Fvector trace_from, trace_to; + //trace_from = get_head_position(m_object); + //trace_to = get_head_position(Actor()); + + //float dist = trace_from.distance_to(trace_to); + //Fvector trace_dir; + //trace_dir.sub(trace_to,trace_from); + + //collide::rq_result l_rq; + //if (Level().ObjectSpace.RayPick(trace_from, trace_dir, dist, collide::rqtBoth, l_rq, m_object)) { + // if (l_rq.O == Actor()) + // return true; + //} + + //trace_to.mad(trace_from,trace_dir,l_rq.range); + //DBG().level_info(this).add_item (trace_from,trace_to,COLOR_BLUE); + // + //// 2. head 2 center + //trace_from = get_head_position(m_object); + //Actor()->Center(trace_to); + + // + + //dist = trace_from.distance_to(trace_to); + //trace_dir.sub(trace_to,trace_from); + + //if (Level().ObjectSpace.RayPick(trace_from, trace_dir, dist, collide::rqtBoth, l_rq, m_object)) + // if (l_rq.O == Actor()) + // return true; + + //trace_to.mad(trace_from,trace_dir,l_rq.range); + //DBG().level_info(this).add_item (trace_from,trace_to,COLOR_RED); + + // + // + //// 3. center 2 head + //m_object->Center(trace_from); + //trace_to = get_head_position(Actor()); + + //dist = trace_from.distance_to(trace_to); + //trace_dir.sub(trace_to,trace_from); + + //if (Level().ObjectSpace.RayPick(trace_from, trace_dir, dist, collide::rqtBoth, l_rq, m_object)) + // if (l_rq.O == Actor()) return true; + + // + //trace_to.mad(trace_from,trace_dir,l_rq.range); + //DBG().level_info(this).add_item (trace_from,trace_to,COLOR_GREEN); + // + //// 4. center 2 center + //m_object->Center(trace_from); + //Actor()->Center (trace_to); + + //dist = trace_from.distance_to(trace_to); + //trace_dir.sub(trace_to,trace_from); + + //if (Level().ObjectSpace.RayPick(trace_from, trace_dir, dist, collide::rqtBoth, l_rq, m_object)) + // if (l_rq.O == Actor()) return true; + + // + //trace_to.mad(trace_from,trace_dir,l_rq.range); + //DBG().level_info(this).add_item (trace_from,trace_to,D3DCOLOR_XRGB(0,150,150)); + + return true; +} + + +void CControllerPsyHit::death_glide_start() +{ + if (!check_conditions_final()) { + m_man->deactivate (this); + return; + } + + // Start effector + CEffectorCam* ce = Actor()->Cameras().GetCamEffector(eCEControllerPsyHit); + + if (ce) return; + + Fvector src_pos = Actor()->cam_Active()->vPosition; + Fvector target_pos = m_object->Position(); + target_pos.y += 1.2f; + + Fvector dir; + dir.sub (target_pos,src_pos); + + float dist = dir.magnitude(); + dir.normalize (); + + target_pos.mad (src_pos,dir,dist-4.8f); + + Actor()->Cameras().AddCamEffector(new CControllerPsyHitCamEffector(eCEControllerPsyHit, src_pos,target_pos, m_man->animation().motion_time(m_stage[1], m_object->Visual()))); + smart_cast(m_object)->draw_fire_particles(); + + dir.sub(src_pos,target_pos); + dir.normalize(); + float h,p; + dir.getHP(h,p); + dir.setHP(h,p+PI_DIV_3); + Actor()->character_physics_support()->movement()->ApplyImpulse(dir,Actor()->GetMass() * 530.f); + + set_sound_state (eStart); + + NET_Packet P; + Actor()->u_EventGen (P, GEG_PLAYER_WEAPON_HIDE_STATE, Actor()->ID()); + P.w_u32 (INV_STATE_BLOCK_ALL); + P.w_u8 (u8(true)); + Actor()->u_EventSend(P); + + m_blocked = true; + + ////////////////////////////////////////////////////////////////////////// + // set direction + SControlDirectionData *ctrl_dir = (SControlDirectionData*)m_man->data(this, ControlCom::eControlDir); + VERIFY (ctrl_dir); + ctrl_dir->heading.target_speed = 3.f; + ctrl_dir->heading.target_angle = m_man->direction().angle_to_target(Actor()->Position()); + + ////////////////////////////////////////////////////////////////////////// +} + +void CControllerPsyHit::death_glide_end() +{ + // Stop camera effector + + CEffectorCam* ce = Actor()->Cameras().GetCamEffector(eCEControllerPsyHit); + VERIFY(ce); + Actor()->Cameras().RemoveCamEffector(eCEControllerPsyHit); + CController *monster = smart_cast(m_object); + monster->draw_fire_particles(); + + monster->m_sound_tube_hit_left.play_at_pos(Actor(), Fvector().set(-1.f, 0.f, 1.f), sm_2D); + monster->m_sound_tube_hit_right.play_at_pos(Actor(), Fvector().set(1.f, 0.f, 1.f), sm_2D); + + m_object->Hit_Psy (Actor(), monster->m_tube_damage); + //m_object->Hit_Wound (Actor(), monster->m_tube_damage,Fvector().set(0.0f,1.0f,0.0f),0.0f); + +} + +void CControllerPsyHit::update_frame() +{ + //if (m_sound_state == eStart) { + // CController *monster = smart_cast(m_object); + // if (!monster->m_sound_tube_start._feedback()) { + // m_sound_state = ePull; + // monster->m_sound_tube_pull.play_at_pos(Actor(), Fvector().set(0.f, 0.f, 0.f), sm_2D); + // } + //} +} + +void CControllerPsyHit::set_sound_state(ESoundState state) +{ + CController *monster = smart_cast(m_object); + if (state == ePrepare) { + monster->m_sound_tube_prepare.play_at_pos(Actor(), Fvector().set(0.f, 0.f, 0.f), sm_2D); + } else + if (state == eStart) { + if (monster->m_sound_tube_prepare._feedback()) monster->m_sound_tube_prepare.stop(); + + monster->m_sound_tube_start.play_at_pos(Actor(), Fvector().set(0.f, 0.f, 0.f), sm_2D); + monster->m_sound_tube_pull.play_at_pos(Actor(), Fvector().set(0.f, 0.f, 0.f), sm_2D); + } else + if (state == eHit) { + if (monster->m_sound_tube_start._feedback()) monster->m_sound_tube_start.stop(); + if (monster->m_sound_tube_pull._feedback()) monster->m_sound_tube_pull.stop(); + + //monster->m_sound_tube_hit_left.play_at_pos(Actor(), Fvector().set(-1.f, 0.f, 1.f), sm_2D); + //monster->m_sound_tube_hit_right.play_at_pos(Actor(), Fvector().set(1.f, 0.f, 1.f), sm_2D); + } else + if (state == eNone) { + if (monster->m_sound_tube_start._feedback()) monster->m_sound_tube_start.stop(); + if (monster->m_sound_tube_pull._feedback()) monster->m_sound_tube_pull.stop(); + if (monster->m_sound_tube_prepare._feedback()) monster->m_sound_tube_prepare.stop(); + } + + m_sound_state = state; +} + +void CControllerPsyHit::hit() +{ + //CController *monster = smart_cast(m_object); + + set_sound_state (eHit); + //m_object->Hit_Psy (Actor(), monster->m_tube_damage); +} + +void CControllerPsyHit::on_death() +{ + if (!is_active()) return; + + // Stop camera effector + CEffectorCam* ce = Actor()->Cameras().GetCamEffector(eCEControllerPsyHit); + if (ce) { + Actor()->Cameras().RemoveCamEffector(eCEControllerPsyHit); + } + + m_man->deactivate (this); +} diff --git a/src/xrGameLA/ai/monsters/controller/controller_psy_hit.h b/src/xrGameLA/ai/monsters/controller/controller_psy_hit.h new file mode 100644 index 000000000..6a56924e6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_psy_hit.h @@ -0,0 +1,52 @@ +#pragma once +#include "../control_combase.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" + +class CPsyHitEffectorCam; +class CPsyHitEffectorPP; + +class CControllerPsyHit : public CControl_ComCustom<> { + typedef CControl_ComCustom<> inherited; + + MotionID m_stage[4]; + u8 m_current_index; + + CPsyHitEffectorCam *m_effector_cam; + CPsyHitEffectorPP *m_effector_pp; + + enum ESoundState{ + ePrepare, + eStart, + ePull, + eHit, + eNone + } m_sound_state; + + + float m_min_tube_dist; + + // internal flag if weapon was hidden + bool m_blocked; + +public: + virtual void load (LPCSTR section); + virtual void reinit (); + virtual void update_frame (); + virtual bool check_start_conditions (); + virtual void activate (); + virtual void deactivate (); + + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + + void on_death (); +private: + + void play_anim (); + void death_glide_start (); + void death_glide_end (); + + void set_sound_state (ESoundState state); + void hit (); + bool check_conditions_final (); +}; + diff --git a/src/xrGameLA/ai/monsters/controller/controller_psy_hit_effector.cpp b/src/xrGameLA/ai/monsters/controller/controller_psy_hit_effector.cpp new file mode 100644 index 000000000..4fc378b62 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_psy_hit_effector.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" +#include "controller_psy_hit_effector.h" + + +//void CPsyHitEffectorPP::load(LPCSTR section) +//{ +// inherited::load(section); +// +// m_attack_perc = pSettings->r_float(section,"attack"); +// m_release_perc = pSettings->r_float(section,"release"); +//} +// +//bool CPsyHitEffectorPP::check_completion() +//{ +// return (smth); +//} +// +//bool CPsyHitEffectorPP::check_start_conditions() +//{ +// return (m_dist < m_radius * m_r_max_perc); +//} +// +//void CPsyHitEffectorPP::update_factor() +//{ +// float factor; +// factor = (m_radius * m_r_max_perc - m_dist) / (m_radius * m_r_max_perc - m_radius * m_r_min_perc); +// clamp(factor,0.01f,1.0f); +// +// m_effector->set_factor(factor); +//} +// +// +//CPPEffectorControlled *CPsyHitEffectorPP::create_effector() +//{ +// return xr_new(this,m_state); +//} + + + +////////////////////////////////////////////////////////////////////////// +// Alien Camera Effector +////////////////////////////////////////////////////////////////////////// + + diff --git a/src/xrGameLA/ai/monsters/controller/controller_psy_hit_effector.h b/src/xrGameLA/ai/monsters/controller/controller_psy_hit_effector.h new file mode 100644 index 000000000..117b4c514 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_psy_hit_effector.h @@ -0,0 +1,39 @@ +#pragma once +#include "../../../../effector.h" +#include "../../../pp_effector_custom.h" + +//////////////////////////////////////////////////////////////////////////////////// +// CPsyHitEffectorPP +//////////////////////////////////////////////////////////////////////////////////// + +//class CPsyHitEffectorPP : public CPPEffectorController { +// typedef CPPEffectorController inherited; +// +// float m_attack_perc; +// float m_release_perc; +// +//public: +// virtual void load (LPCSTR section); +// virtual bool check_completion (); +// virtual bool check_start_conditions (); +// virtual void update_factor (); +// +// virtual CPPEffectorControlled *create_effector (); +//}; + + +//////////////////////////////////////////////////////////////////////////////////// +// CPsyHitEffectorCam +//////////////////////////////////////////////////////////////////////////////////// + +//class CPsyHitEffectorCam : public CEffector { +// typedef CEffector inherited; +// +// float m_time_total; +// Fvector dangle_target; +// Fvector dangle_current; +// +//public: +// CPsyHitEffectorCam (EEffectorType type); +// virtual BOOL Process (Fvector &p, Fvector &d, Fvector &n, float& fFov, float& fFar, float& fAspect); +//}; diff --git a/src/xrGameLA/ai/monsters/controller/controller_script.cpp b/src/xrGameLA/ai/monsters/controller/controller_script.cpp new file mode 100644 index 000000000..9c533da90 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "controller.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CController::script_register(lua_State *L) +{ + module(L) + [ + class_("CController") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack.h new file mode 100644 index 000000000..1b5980746 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack.h @@ -0,0 +1,29 @@ +#pragma once + +//#include "../state.h" +#include "../../../ai_debug.h" +#include "../states/monster_state_attack.h" + + +template +class CStateControllerAttack : public CStateMonsterAttack<_Object> //public CState<_Object> +{ +protected: + typedef CStateMonsterAttack<_Object> inherited; +// typedef CState<_Object>* state_ptr; + +public: + CStateControllerAttack (_Object *obj); + virtual ~CStateControllerAttack () {} + + virtual void initialize (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual void execute (); + virtual void setup_substates (); + virtual void check_force_state (); + bool check_state (u32 state_id); +}; + +#include "controller_state_attack_inline.h" diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_camp.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_camp.h new file mode 100644 index 000000000..c05d83c41 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_camp.h @@ -0,0 +1,29 @@ +#pragma once + +template +class CStateControlCamp : public CState<_Object> { + typedef CState<_Object> inherited; + + float m_angle_from; + float m_angle_to; + + float m_target_angle; + u32 m_time_next_updated; + +public: + + CStateControlCamp (_Object *obj) : inherited(obj) {} + virtual ~CStateControlCamp () {} + + virtual void initialize (); + virtual void execute (); + virtual bool check_completion (); + virtual bool check_start_conditions (); + +private: + + virtual void update_target_angle (); + +}; + +#include "controller_state_attack_camp_inline.h" diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_camp_inline.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_camp_inline.h new file mode 100644 index 000000000..bcf770f15 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_camp_inline.h @@ -0,0 +1,107 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStateControlCampAbstract CStateControlCamp<_Object> + +#define ANGLE_DISP PI_DIV_2 +#define ANGLE_DISP_STEP deg(10) +#define TRACE_STATIC_DIST 3.f +#define TIME_POINT_CHANGE_MIN 2000 +#define TIME_POINT_CHANGE_MAX 5000 + + +TEMPLATE_SPECIALIZATION +void CStateControlCampAbstract::initialize() +{ + inherited::initialize (); + + float angle = ai().level_graph().vertex_cover_angle(object->ai_location().level_vertex_id(),deg(10), std::greater()); + + collide::rq_result l_rq; + + m_angle_from = angle_normalize(angle - ANGLE_DISP); + m_angle_to = angle_normalize(angle + ANGLE_DISP); + + Fvector trace_from; + object->Center (trace_from); + Fvector direction; + + // trace discretely left + for (float ang = angle; angle_difference(ang, angle) < ANGLE_DISP; ang = angle_normalize(ang - ANGLE_DISP_STEP)) { + + direction.setHP (ang, 0.f); + + if (Level().ObjectSpace.RayPick(trace_from, direction, TRACE_STATIC_DIST, collide::rqtStatic, l_rq, object)) { + if ((l_rq.range < TRACE_STATIC_DIST)) { + m_angle_from = ang; + break; + } + } + } + + // trace discretely right + for (float ang = angle; angle_difference(ang, angle) < ANGLE_DISP; ang = angle_normalize(ang + ANGLE_DISP_STEP)) { + + direction.setHP (ang, 0.f); + + if (Level().ObjectSpace.RayPick(trace_from, direction, TRACE_STATIC_DIST, collide::rqtStatic, l_rq, object)) { + if ((l_rq.range < TRACE_STATIC_DIST)) { + m_angle_to = ang; + break; + } + } + } + + m_time_next_updated = 0; + + m_target_angle = m_angle_from; + + Fvector pos; + pos.mad(object->Position(), Fvector().setHP(angle,0.f), 3.f); + object->dir().face_target(pos); +} + +TEMPLATE_SPECIALIZATION +void CStateControlCampAbstract::execute() +{ + update_target_angle (); + + Fvector point; + point.mad (object->Position(),Fvector().setHP(m_target_angle, 0.f), 3.f); + + object->custom_dir().head_look_point (point); + object->custom_anim().set_body_state (CControllerAnimation::eTorsoIdle,CControllerAnimation::eLegsTypeSteal); +} + +TEMPLATE_SPECIALIZATION +bool CStateControlCampAbstract::check_start_conditions() +{ + if (object->EnemyMan.see_enemy_now()) return false; + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateControlCampAbstract::check_completion() +{ + if (object->EnemyMan.see_enemy_now()) return true; + if (time_state_started + 2000 < time()) return true; + + return false; +} + +TEMPLATE_SPECIALIZATION +void CStateControlCampAbstract::update_target_angle() +{ + if (m_time_next_updated > time()) return; + m_time_next_updated = time() + Random.randI(TIME_POINT_CHANGE_MIN,TIME_POINT_CHANGE_MAX); + + if (fsimilar(m_target_angle, m_angle_from)) + m_target_angle = m_angle_to; + else + m_target_angle = m_angle_from; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateControlCampAbstract diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_fast_move.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_fast_move.h new file mode 100644 index 000000000..66f58e46e --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_fast_move.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../state.h" + +template +class CStateControllerFastMove : public CState<_Object> { +protected: + typedef CState<_Object> inherited; +public: + CStateControllerFastMove (_Object *obj) : inherited(obj) {} + virtual void initialize (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual void execute (); +}; + +#include "controller_state_attack_fast_move_inline.h" diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_fast_move_inline.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_fast_move_inline.h new file mode 100644 index 000000000..dd2591424 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_fast_move_inline.h @@ -0,0 +1,43 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateControllerFastMoveAbstract CStateControllerFastMove<_Object> + +TEMPLATE_SPECIALIZATION +void CStateControllerFastMoveAbstract::initialize() +{ + inherited::initialize(); + + object->set_mental_state(CController::eStateIdle); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerFastMoveAbstract::finalize() +{ + inherited::finalize(); + object->set_mental_state (CController::eStateDanger); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerFastMoveAbstract::critical_finalize() +{ + inherited::critical_finalize(); + object->set_mental_state (CController::eStateDanger); +} + + +TEMPLATE_SPECIALIZATION +void CStateControllerFastMoveAbstract::execute() +{ + object->set_action (ACT_RUN); + + //select another cover + + +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateControllerFastMoveAbstract diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_fire.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_fire.h new file mode 100644 index 000000000..012b4f174 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_fire.h @@ -0,0 +1,25 @@ +#pragma once + +template +class CStateControlFire : public CState<_Object> { + typedef CState<_Object> inherited; + + u32 m_time_started; + u32 m_time_state_last_execute; + +public: + + CStateControlFire (_Object *obj) : inherited(obj) {} + virtual ~CStateControlFire () {} + + virtual void reinit (); + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + virtual bool check_completion (); + virtual bool check_start_conditions (); + +}; + +#include "controller_state_attack_fire_inline.h" diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_fire_inline.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_fire_inline.h new file mode 100644 index 000000000..811e5ce4e --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_fire_inline.h @@ -0,0 +1,76 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStateControlFireAbstract CStateControlFire<_Object> + +#define MIN_ENEMY_DISTANCE 10.f +#define STATE_MAX_TIME 3000 +#define STATE_EXECUTE_DELAY 5000 + +TEMPLATE_SPECIALIZATION +void CStateControlFireAbstract::reinit() +{ + inherited::reinit(); + + m_time_state_last_execute = 0; + +} + +TEMPLATE_SPECIALIZATION +void CStateControlFireAbstract::initialize() +{ + inherited::initialize (); + object->set_psy_fire_delay_zero (); + m_time_started = time(); +} + +TEMPLATE_SPECIALIZATION +void CStateControlFireAbstract::execute() +{ + object->dir().face_target (object->EnemyMan.get_enemy()); + object->custom_dir().head_look_point (get_head_position(const_cast(object->EnemyMan.get_enemy()))); + + object->custom_anim().set_body_state (CControllerAnimation::eTorsoIdle,CControllerAnimation::eLegsTypeSteal); +} + +TEMPLATE_SPECIALIZATION +void CStateControlFireAbstract::finalize() +{ + inherited::finalize(); + object->set_psy_fire_delay_default (); + m_time_state_last_execute = time(); +} +TEMPLATE_SPECIALIZATION +void CStateControlFireAbstract::critical_finalize() +{ + inherited::critical_finalize(); + object->set_psy_fire_delay_default (); + m_time_state_last_execute = time(); +} + + +TEMPLATE_SPECIALIZATION +bool CStateControlFireAbstract::check_start_conditions() +{ + if (!object->EnemyMan.see_enemy_now()) return false; + if (object->EnemyMan.get_enemy()->Position().distance_to(object->Position()) < MIN_ENEMY_DISTANCE) return false; + if (m_time_state_last_execute + STATE_EXECUTE_DELAY > time()) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateControlFireAbstract::check_completion() +{ + if (!object->EnemyMan.see_enemy_now()) return true; + if (object->HitMemory.is_hit()) return true; + if (object->EnemyMan.get_enemy()->Position().distance_to(object->Position()) < MIN_ENEMY_DISTANCE) return true; + if (m_time_started + STATE_MAX_TIME < time()) return true; + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateControlFireAbstract diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide.h new file mode 100644 index 000000000..49a67cb08 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide.h @@ -0,0 +1,40 @@ +#pragma once + +template +class CStateControlHide : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + bool m_cover_reached; + + struct { + Fvector position; + u32 node; + } target; + + u32 m_time_finished; + + bool m_state_fast_run; + +public: + + CStateControlHide (_Object *obj) : inherited(obj) {} + virtual ~CStateControlHide () {} + + virtual void initialize (); + virtual void execute (); + + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); + + +private: + void select_target_point (); +}; + + +#include "controller_state_attack_hide_inline.h" + diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_inline.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_inline.h new file mode 100644 index 000000000..1f97e5a43 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_inline.h @@ -0,0 +1,107 @@ +#pragma once + +#include "../../../ai_space.h" +#include "../monster_cover_manager.h" +#include "../../../cover_point.h" +#include "../../../level.h" +#include "../../../level_debug.h" + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStateControllerHideAbstract CStateControlHide<_Object> + +TEMPLATE_SPECIALIZATION +void CStateControllerHideAbstract::initialize() +{ + inherited::initialize(); + + m_cover_reached = false; + select_target_point(); + object->path().prepare_builder(); + +} + +TEMPLATE_SPECIALIZATION +void CStateControllerHideAbstract::execute() +{ + if (m_state_fast_run) { + if (target.position.distance_to(object->Position()) < 5.f) { + m_state_fast_run = false; + object->set_mental_state (CController::eStateDanger); + } + } + + object->set_action (ACT_RUN); + + object->path().set_target_point (target.position, target.node); + object->path().set_rebuild_time (0); + object->path().set_distance_to_end (0.f); + object->path().set_use_covers (false); + + object->anim().accel_activate (eAT_Aggressive); + object->anim().accel_set_braking (false); + + object->sound().play (MonsterSound::eMonsterSoundAggressive, 0,0,object->db().m_dwAttackSndDelay); + + if (object->HitMemory.get_last_hit_time() > object->EnemyMan.get_enemy_time_last_seen()) { + Fvector pos; + pos.mad(object->Position(),object->HitMemory.get_last_hit_dir(),5.f); + pos.y += 1.5f; + object->custom_dir().head_look_point(pos); + } else + object->custom_dir().head_look_point(object->EnemyMan.get_enemy_position()); + + //object->custom_anim().set_body_state(CControllerAnimation::eTorsoRun,CControllerAnimation::eLegsTypeRun); +} + +TEMPLATE_SPECIALIZATION +bool CStateControllerHideAbstract::check_start_conditions() +{ + return (target.position.distance_to(object->Position()) < 5.f); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerHideAbstract::finalize() +{ + inherited::finalize(); + object->set_mental_state (CController::eStateDanger); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerHideAbstract::critical_finalize() +{ + inherited::finalize(); + object->set_mental_state (CController::eStateDanger); +} + +TEMPLATE_SPECIALIZATION +bool CStateControllerHideAbstract::check_completion() +{ + return ((object->ai_location().level_vertex_id() == target.node) && !object->control().path_builder().is_moving_on_path() && (target.position.distance_to(object->Position()) > 5.f)); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerHideAbstract::select_target_point() +{ +#ifdef DEBUG + DBG().level_info(this).clear(); +#endif + + const CCoverPoint *point = object->CoverMan->find_cover(object->EnemyMan.get_enemy_position(),10.f,30.f); + if (point) { + target.node = point->level_vertex_id (); + target.position = point->position (); + } else { + target.node = 0; + target.position = ai().level_graph().vertex_position(target.node); + } + + m_state_fast_run = (target.position.distance_to(object->Position()) > 20.f); + if (m_state_fast_run && (Random.randI(100) < 50)) + object->set_mental_state (CController::eStateIdle); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateControllerHideAbstract diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_lite.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_lite.h new file mode 100644 index 000000000..b1682c22d --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_lite.h @@ -0,0 +1,38 @@ +#pragma once + +// Hiding until enemy get out from its sight +template +class CStateControlHideLite : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + struct { + Fvector position; + u32 node; + } target; + + u32 m_time_finished; + +public: + + CStateControlHideLite (_Object *obj) : inherited(obj) {} + virtual ~CStateControlHideLite () {} + + virtual void reinit (); + + virtual void initialize (); + virtual void execute (); + + virtual void finalize (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); + + +private: + void select_target_point (); +}; + + +#include "controller_state_attack_hide_lite_inline.h" + diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_lite_inline.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_lite_inline.h new file mode 100644 index 000000000..7a3b6d335 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_hide_lite_inline.h @@ -0,0 +1,91 @@ +#pragma once + +#include "../../../ai_space.h" +#include "../monster_cover_manager.h" +#include "../../../cover_point.h" +#include "../../../level.h" +#include "../../../level_debug.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStateControllerHideLiteAbstract CStateControlHideLite<_Object> + +TEMPLATE_SPECIALIZATION +void CStateControllerHideLiteAbstract::initialize() +{ + inherited::initialize (); + + select_target_point (); + object->path().prepare_builder (); + +} + +TEMPLATE_SPECIALIZATION +void CStateControllerHideLiteAbstract::execute() +{ + object->path().set_target_point (target.position, target.node); + object->path().set_rebuild_time (0); + object->path().set_distance_to_end (0.f); + object->path().set_use_covers (true); + + object->anim().accel_activate (eAT_Aggressive); + object->anim().accel_set_braking (false); + + object->sound().play (MonsterSound::eMonsterSoundAggressive, 0,0,object->db().m_dwAttackSndDelay); + object->custom_dir().head_look_point(object->EnemyMan.get_enemy_position()); + + object->custom_anim().set_body_state(CControllerAnimation::eTorsoRun,CControllerAnimation::eLegsTypeRun); +} + +TEMPLATE_SPECIALIZATION +bool CStateControllerHideLiteAbstract::check_start_conditions() +{ + return true; +} + +TEMPLATE_SPECIALIZATION +void CStateControllerHideLiteAbstract::reinit() +{ + inherited::reinit(); + m_time_finished = 0; +} + + +TEMPLATE_SPECIALIZATION +void CStateControllerHideLiteAbstract::finalize() +{ + inherited::finalize(); + m_time_finished = Device.dwTimeGlobal; +} + + +TEMPLATE_SPECIALIZATION +bool CStateControllerHideLiteAbstract::check_completion() +{ + if ((object->ai_location().level_vertex_id() == target.node) && + !object->control().path_builder().is_moving_on_path()) return true; + + return (!object->EnemyMan.see_enemy_now()); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerHideLiteAbstract::select_target_point() +{ +#ifdef DEBUG + DBG().level_info(this).clear(); +#endif + + const CCoverPoint *point = object->CoverMan->find_cover(object->EnemyMan.get_enemy_position(),10.f,30.f); + //VERIFY(point); + if (point) { + target.node = point->level_vertex_id (); + target.position = point->position (); + } else { + target.node = 0; + target.position = ai().level_graph().vertex_position(target.node); + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateControllerHideLiteAbstract diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_inline.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_inline.h new file mode 100644 index 000000000..bb7303cf9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_inline.h @@ -0,0 +1,124 @@ +#pragma once + +#include "controller_state_attack_hide.h" +#include "controller_state_attack_hide_lite.h" +#include "controller_state_attack_moveout.h" +#include "controller_state_attack_camp.h" +#include "controller_state_attack_fire.h" +#include "controller_tube.h" + +#define CONTROL_FIRE_PERC 80 +#define CONTROL_TUBE_PERC 20 + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateControllerAttackAbstract CStateControllerAttack<_Object> + +TEMPLATE_SPECIALIZATION +CStateControllerAttackAbstract::CStateControllerAttack(_Object *obj) : inherited(obj) +{ +/* + add_state (eStateAttack_HideInCover, xr_new > (obj)); + add_state (eStateAttack_HideInCoverLite, xr_new > (obj)); + add_state (eStateAttack_MoveOut, xr_new > (obj)); + add_state (eStateAttack_CampInCover, xr_new > (obj)); + add_state (eStateAttack_ControlFire, xr_new > (obj)); + add_state (eStateAttack_ControlTube, xr_new > (obj)); +*/ + +// add_state (eStateAttack_Run, xr_new> (obj)); +// add_state (eStateAttack_Melee, xr_new> (obj)); +// add_state (eStateAttack_ControlTube, xr_new> (obj)); + +// add_state (eStateAttack_Hide, xr_new> (obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerAttackAbstract::execute() +{ + /* + if (get_state(eStateAttack_ControlTube)->check_start_conditions()) + { + select_state (eStateAttack_ControlTube); + get_state_current()->execute (); + prev_substate = current_substate; + return; + } + */ + inherited::execute (); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerAttackAbstract::setup_substates() +{ + inherited::setup_substates(); + /* + state_ptr state = get_state_current(); + + if (current_substate == eStateFaceEnemy) { + SStateDataLookToPoint data; + + data.point = object->EnemyMan.get_enemy_position(); + data.action.action = ACT_STAND_IDLE; + + state->fill_data_with(&data, sizeof(SStateDataLookToPoint)); + + object->sound().play(MonsterSound::eMonsterSoundAggressive, 0,0,object->db().m_dwAttackSndDelay); + return; + } + */ +} + +TEMPLATE_SPECIALIZATION +void CStateControllerAttackAbstract::check_force_state() +{ + float dist_to_enemy = -1.f; + if (current_substate == eStateAttack_Run) + { + dist_to_enemy = object->Position().distance_to(object->EnemyMan.get_enemy_position()); + if (dist_to_enemy > 10.f) + { + get_state_current()->critical_finalize(); + current_substate = u32(-1); + } + } +} + +TEMPLATE_SPECIALIZATION +void CStateControllerAttackAbstract::initialize() +{ + inherited::initialize (); + object->set_mental_state (CController::eStateDanger); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerAttackAbstract::finalize() +{ + inherited::finalize(); + object->set_mental_state(CController::eStateIdle); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerAttackAbstract::critical_finalize() +{ + inherited::critical_finalize(); + object->set_mental_state(CController::eStateIdle); +} + +TEMPLATE_SPECIALIZATION +bool CStateControllerAttackAbstract::check_state(u32 state_id) +{ + if (prev_substate == state_id) { + if (!get_state_current()->check_completion()) return true; + } else { + if (get_state(state_id)->check_start_conditions()) return true; + } + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateControllerAttackAbstract \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_moveout.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_moveout.h new file mode 100644 index 000000000..edbde237c --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_moveout.h @@ -0,0 +1,41 @@ +#pragma once + +template +class CStateControlMoveOut : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + Fvector m_look_point; + + u32 m_last_time_look_point_updated; + u32 m_current_delay; + + enum { + eMoveToNodeEnemyLastSeen, + eMoveToLastEnemyLocation + } m_state; + + Fvector m_target_position; + u32 m_target_node; + + u32 m_enemy_vertex; + +public: + + CStateControlMoveOut (_Object *obj) : inherited(obj) {} + virtual ~CStateControlMoveOut () {} + + virtual void initialize (); + virtual void execute (); + virtual bool check_completion (); + virtual bool check_start_conditions (); + + +private: + void update_target_point (); + void update_look_point (); + +}; + +#include "controller_state_attack_moveout_inline.h" + diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_attack_moveout_inline.h b/src/xrGameLA/ai/monsters/controller/controller_state_attack_moveout_inline.h new file mode 100644 index 000000000..e34a24e1c --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_attack_moveout_inline.h @@ -0,0 +1,118 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStateControlMoveOutAbstract CStateControlMoveOut<_Object> + +#define MAX_STATE_TIME 10000 +#define DEFAULT_LOOK_POINT_CHANGE_DELAY 2000 +#define LOOK_COVER_PROBABILITY 30 + +TEMPLATE_SPECIALIZATION +void CStateControlMoveOutAbstract::initialize() +{ + inherited::initialize (); + + object->path().prepare_builder (); + m_last_time_look_point_updated = 0; + + m_state = eMoveToNodeEnemyLastSeen; + + m_current_delay = DEFAULT_LOOK_POINT_CHANGE_DELAY; + + // cheating here + m_enemy_vertex = object->EnemyMan.get_enemy()->ai_location().level_vertex_id(); +} + +TEMPLATE_SPECIALIZATION +void CStateControlMoveOutAbstract::execute() +{ + + update_target_point (); + + object->path().set_target_point (m_target_position, m_target_node); + object->path().set_rebuild_time (20000); + object->path().set_distance_to_end (0.f); + object->path().set_use_covers (false); + + + object->anim().m_tAction = ACT_STEAL; + object->anim().accel_deactivate (); + object->anim().accel_set_braking (false); + object->set_state_sound (MonsterSound::eMonsterSoundAggressive); + + // look into very open point where we go + update_look_point (); + object->custom_dir().head_look_point (m_look_point); + object->custom_anim().set_body_state (CControllerAnimation::eTorsoSteal,CControllerAnimation::eLegsTypeStealMotion); +} + +TEMPLATE_SPECIALIZATION +bool CStateControlMoveOutAbstract::check_start_conditions() +{ + if (object->EnemyMan.see_enemy_now()) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateControlMoveOutAbstract::check_completion() +{ + if (object->EnemyMan.see_enemy_now()) return true; + if (time_state_started + 10000 < time()) return true; + if (object->HitMemory.is_hit() && + object->HitMemory.get_last_hit_time() > time_state_started) return true; + + if (object->EnemyMan.get_enemy_position().distance_to(object->Position()) < 1.f) return true; + + return false; +} + +TEMPLATE_SPECIALIZATION +void CStateControlMoveOutAbstract::update_target_point() +{ + if (m_state == eMoveToNodeEnemyLastSeen) { + if (object->Position().similar(m_target_position,0.05f)) m_state = eMoveToLastEnemyLocation; + } + + if (m_state == eMoveToNodeEnemyLastSeen) { + if (object->EnemyMan.get_my_vertex_enemy_last_seen() != u32(-1)) + m_target_node = object->EnemyMan.get_my_vertex_enemy_last_seen(); + else + m_target_node = m_enemy_vertex; + m_target_position = ai().level_graph().vertex_position (m_target_node); + } else { + m_target_node = m_enemy_vertex; + m_target_position = ai().level_graph().vertex_position (m_target_node); + } +} + + +TEMPLATE_SPECIALIZATION +void CStateControlMoveOutAbstract::update_look_point() +{ + if (object->HitMemory.get_last_hit_time() > object->EnemyMan.get_enemy_time_last_seen()) { + m_look_point.mad (object->Position(),object->HitMemory.get_last_hit_dir(),5.f); + m_look_point.y += 1.5f; + m_last_time_look_point_updated = time(); + return; + } + + if (m_last_time_look_point_updated + m_current_delay > time()) return; + + if ((Random.randI(100) < LOOK_COVER_PROBABILITY) && (m_last_time_look_point_updated != 0)) { + float angle = ai().level_graph().vertex_cover_angle(object->ai_location().level_vertex_id(),deg(10), std::greater()); + m_look_point.mad (object->Position(), Fvector().setHP(angle,0.f), 3.f); + m_current_delay = DEFAULT_LOOK_POINT_CHANGE_DELAY; + } else { + m_look_point = object->EnemyMan.get_enemy_position(); + m_current_delay = DEFAULT_LOOK_POINT_CHANGE_DELAY * 2; + } + + m_look_point.y += 1.5f; + m_last_time_look_point_updated = time(); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateControlMoveOutAbstract diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_control_hit.h b/src/xrGameLA/ai/monsters/controller/controller_state_control_hit.h new file mode 100644 index 000000000..a7e37952f --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_control_hit.h @@ -0,0 +1,37 @@ +#pragma once + +template +class CStateControlAttack : public CState<_Object> { + typedef CState<_Object> inherited; + + enum { + eActionPrepare, + eActionContinue, + eActionFire, + eActionWaitTripleEnd, + eActionCompleted + } m_action; + + u32 time_control_started; + +public: + + CStateControlAttack (_Object *p); + virtual ~CStateControlAttack (); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); + +private: + + void execute_hit_fire (); + void execute_hit_continue (); + void execute_hit_prepare (); +}; + +#include "controller_state_control_hit_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_control_hit_inline.h b/src/xrGameLA/ai/monsters/controller/controller_state_control_hit_inline.h new file mode 100644 index 000000000..5e29ec131 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_control_hit_inline.h @@ -0,0 +1,126 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateControllerControlHitAbstract CStateControlAttack<_Object> + +#define GOOD_DISTANCE_FOR_CONTROL_HIT 8.f +#define CONTROL_PREPARE_TIME 2900 + +TEMPLATE_SPECIALIZATION +CStateControllerControlHitAbstract::CStateControlAttack(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateControllerControlHitAbstract::~CStateControlAttack() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateControllerControlHitAbstract::initialize() +{ + inherited::initialize(); + + m_action = eActionPrepare; + time_control_started = 0; +} + +TEMPLATE_SPECIALIZATION +void CStateControllerControlHitAbstract::execute() +{ + switch (m_action) { + case eActionPrepare: + execute_hit_prepare(); + m_action = eActionContinue; + break; + + case eActionContinue: + execute_hit_continue(); + break; + + case eActionFire: + execute_hit_fire(); + m_action = eActionWaitTripleEnd; + break; + + case eActionWaitTripleEnd: + if (!object->com_man().ta_is_active()) { + m_action = eActionCompleted; + } + + case eActionCompleted: + break; + } + + object->anim().m_tAction = ACT_STAND_IDLE; + object->dir().face_target(object->EnemyMan.get_enemy(), 1200); + + object->sound().play(MonsterSound::eMonsterSoundAggressive, 0,0,object->db().m_dwAttackSndDelay); +} + +TEMPLATE_SPECIALIZATION +bool CStateControllerControlHitAbstract::check_start_conditions() +{ + float dist = object->Position().distance_to(object->EnemyMan.get_enemy_position()); + if (dist < GOOD_DISTANCE_FOR_CONTROL_HIT) return false; + + if (!object->EnemyMan.see_enemy_now()) return false; + + // всё ок, можно начать атаку + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateControllerControlHitAbstract::check_completion() +{ + return (m_action == eActionCompleted); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerControlHitAbstract::finalize() +{ + inherited::finalize(); +} + +TEMPLATE_SPECIALIZATION +void CStateControllerControlHitAbstract::critical_finalize() +{ + inherited::critical_finalize(); +} + +////////////////////////////////////////////////////////////////////////// +// Processing +////////////////////////////////////////////////////////////////////////// + + +TEMPLATE_SPECIALIZATION +void CStateControllerControlHitAbstract::execute_hit_prepare() +{ + object->com_man().ta_activate(object->anim_triple_control); + object->play_control_sound_start(); + + time_control_started = Device.dwTimeGlobal; +} + +TEMPLATE_SPECIALIZATION +void CStateControllerControlHitAbstract::execute_hit_continue() +{ + // проверить на грави удар + if (time_control_started + CONTROL_PREPARE_TIME < Device.dwTimeGlobal) { + m_action = eActionFire; + } +} + +TEMPLATE_SPECIALIZATION +void CStateControllerControlHitAbstract::execute_hit_fire() +{ + object->com_man().ta_pointbreak(); + + if (object->EnemyMan.see_enemy_now()) object->control_hit(); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateControllerControlHitAbstract diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_manager.cpp b/src/xrGameLA/ai/monsters/controller/controller_state_manager.cpp new file mode 100644 index 000000000..b9a2aa411 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_manager.cpp @@ -0,0 +1,94 @@ +#include "stdafx.h" +#include "controller.h" +#include "controller_state_manager.h" + +#include "controller_animation.h" +#include "controller_direction.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../controlled_entity.h" + +#include "../states/monster_state_rest.h" +#include "controller_state_attack.h" +#include "../states/monster_state_attack_melee.h" +#include "../states/monster_state_attack_run.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../states/monster_state_attack.h" + +#include "../../../entitycondition.h" + +#include "../states/state_test_state.h" + +#include "controller_tube.h" + +CStateManagerController::CStateManagerController(CController *obj) : inherited(obj) +{ + add_state(eStateRest, new CStateMonsterRest(obj)); + add_state(eStatePanic, new CStateMonsterPanic(obj)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(obj)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(obj)); + add_state(eStateHitted, new CStateMonsterHitted(obj)); + // gr1ph + add_state(eStateAttack, new CStateControllerAttack(obj)); + add_state(eStateEat, new CStateMonsterEat(obj)); + add_state(eStateCustom, new CStateControlHide(obj)); + add_state(eStateCustom_Tubeman, new CStateControllerTube(obj)); +} + +CStateManagerController::~CStateManagerController() +{ +} + +void CStateManagerController::reinit() +{ + inherited::reinit(); + object->set_mental_state(CController::eStateIdle); +} + + +#define FIND_ENEMY_TIME_ENEMY_HIDDEN 5000 +#define FIND_ENEMY_MAX_DISTANCE 10.f + +void CStateManagerController::execute() +{ + u32 state_id = u32(-1); + + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) + { + if (get_state(eStateCustom_Tubeman)->check_start_conditions()) + { + state_id = eStateCustom_Tubeman; + } + else + { + state_id = eStateAttack; + } + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + + if (enemy) object->set_controlled_task(eTaskAttack); + else object->set_controlled_task(eTaskFollow); + + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_manager.h b/src/xrGameLA/ai/monsters/controller/controller_state_manager.h new file mode 100644 index 000000000..02a0e5dbe --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_manager.h @@ -0,0 +1,16 @@ +#pragma once +#include "../monster_state_manager.h" + +class CController; + +class CStateManagerController : public CMonsterStateManager { + + typedef CMonsterStateManager inherited; + +public: + CStateManagerController (CController *obj); + virtual ~CStateManagerController (); + + virtual void reinit (); + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/controller/controller_state_panic.h b/src/xrGameLA/ai/monsters/controller/controller_state_panic.h new file mode 100644 index 000000000..97bbd1ec7 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_state_panic.h @@ -0,0 +1,23 @@ +#pragma once +#include "../state.h" + +template +class CStateControllerPanic : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + enum { + eStateRun = u32(0), + eStateSteal, + eStateLookAround, + }; + +public: + CStateControllerPanic (_Object *obj); + virtual ~CStateControllerPanic (); + + virtual void reselect_state (); +}; + +#include "controller_state_panic_inline.h" diff --git a/src/xrGameLA/ai/monsters/controller/controller_tube.h b/src/xrGameLA/ai/monsters/controller/controller_tube.h new file mode 100644 index 000000000..46179726b --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_tube.h @@ -0,0 +1,16 @@ +#pragma once +#include "../state.h" + +template +class CStateControllerTube : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateControllerTube (_Object *obj) : inherited(obj){} + virtual void execute (); + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "controller_tube_inline.h" + diff --git a/src/xrGameLA/ai/monsters/controller/controller_tube_inline.h b/src/xrGameLA/ai/monsters/controller/controller_tube_inline.h new file mode 100644 index 000000000..fc9c1def2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/controller/controller_tube_inline.h @@ -0,0 +1,47 @@ +#pragma once + +#include "controller_psy_hit.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateControllerTubeAbstract CStateControllerTube<_Object> + +#define TUBE_PROBABILITY 20 + +TEMPLATE_SPECIALIZATION +void CStateControllerTubeAbstract::execute() +{ +// object->control().activate (ControlCom::eComCustom1); +// object->set_action (ACT_STAND_IDLE); + object->m_time_last_tube = time(); + + // missed + //if (!object->tube_at_once() && (Random.randI(100) > TUBE_PROBABILITY)) return; + + object->control().activate (ControlCom::eComCustom1); +} + +#define SEE_ENEMY_DURATION 1000 + +TEMPLATE_SPECIALIZATION +bool CStateControllerTubeAbstract::check_start_conditions() +{ + /* + if (object->EnemyMan.see_enemy_duration() < SEE_ENEMY_DURATION) return false; + if (!object->m_psy_hit->check_start_conditions()) return false; + + return true; + */ + return object->can_tube_fire(); +} + +TEMPLATE_SPECIALIZATION +bool CStateControllerTubeAbstract::check_completion() +{ + return (!object->m_psy_hit->is_active()); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateControllerTubeAbstract diff --git a/src/xrGameLA/ai/monsters/corpse_cover.cpp b/src/xrGameLA/ai/monsters/corpse_cover.cpp new file mode 100644 index 000000000..c2cef11ff --- /dev/null +++ b/src/xrGameLA/ai/monsters/corpse_cover.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" +#include "corpse_cover.h" +#include "../../cover_point.h" +#include "../../ai_space.h" +#include "../../level_graph.h" + +void CMonsterCorpseCoverEvaluator::evaluate (const CCoverPoint *cover_point, float weight) +{ + float my_distance = m_start_position.distance_to(cover_point->position()); + + if (my_distance <= m_min_distance) + return; + + if (my_distance >= m_max_distance) + return; + + Fvector direction; + float y,p; + direction.sub (m_start_position,cover_point->position()); + direction.getHP (y,p); + + float cover_value = ai().level_graph().cover_in_direction(y,cover_point->level_vertex_id()); + if (cover_value >= 2.f*m_best_value) + return; + + m_selected = cover_point; + m_best_value = cover_value; +} + diff --git a/src/xrGameLA/ai/monsters/corpse_cover.h b/src/xrGameLA/ai/monsters/corpse_cover.h new file mode 100644 index 000000000..2843e8f78 --- /dev/null +++ b/src/xrGameLA/ai/monsters/corpse_cover.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../../cover_evaluators.h" + +class CMonsterCorpseCoverEvaluator: public CCoverEvaluatorBase { +protected: + typedef CCoverEvaluatorBase inherited; + +protected: + float m_min_distance; + float m_max_distance; + +public: + + IC CMonsterCorpseCoverEvaluator (CRestrictedObject *object) : inherited(object) + { + } + + IC void setup (float min_enemy_distance, float max_enemy_distance){ + inherited::setup (); + m_min_distance = min_enemy_distance; + m_max_distance = max_enemy_distance; + } + + void evaluate (const CCoverPoint *cover_point, float weight); +}; + + + + + diff --git a/src/xrGameLA/ai/monsters/custom_events.h b/src/xrGameLA/ai/monsters/custom_events.h new file mode 100644 index 000000000..87daa140e --- /dev/null +++ b/src/xrGameLA/ai/monsters/custom_events.h @@ -0,0 +1,22 @@ +#pragma once +#include "monster_event_manager_defs.h" + +////////////////////////////////////////////////////////////////////////// + +struct CEventTAPrepareAnimation : public IEventData { + u32 m_current_state; + + IC CEventTAPrepareAnimation(u32 state) : m_current_state(state) {} + +}; + +////////////////////////////////////////////////////////////////////////// + +struct CEventVelocityBounce : public IEventData { + float m_ratio; + + IC CEventVelocityBounce(float ratio) : m_ratio(ratio) {} + +}; + +////////////////////////////////////////////////////////////////////////// diff --git a/src/xrGameLA/ai/monsters/dog/dog.cpp b/src/xrGameLA/ai/monsters/dog/dog.cpp new file mode 100644 index 000000000..b519954e5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/dog/dog.cpp @@ -0,0 +1,166 @@ +#include "stdafx.h" +#include "dog.h" +#include "dog_state_manager.h" +#include "../monster_velocity_space.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" + +#ifdef _DEBUG +# include +#endif + +CAI_Dog::CAI_Dog() +{ + StateMan = new CStateManagerDog(this); + + CControlled::init_external (this); + + com_man().add_ability(ControlCom::eControlRotationJump); + com_man().add_ability(ControlCom::eControlMeleeJump); +} + +CAI_Dog::~CAI_Dog() +{ + xr_delete(StateMan); +} + +void CAI_Dog::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + anim().AddReplacedAnim(&m_bRunTurnLeft, eAnimRun, eAnimRunTurnLeft); + anim().AddReplacedAnim(&m_bRunTurnRight, eAnimRun, eAnimRunTurnRight); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkFwd, eAnimRunTurnLeft); + anim().accel_chain_add (eAnimWalkFwd, eAnimRunTurnRight); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + // define animation set + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSleep, "lie_sleep_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimLieIdle, "lie_idle_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_dmg_", -1, &velocity_walk_dmg, PS_STAND); + anim().AddAnim(eAnimRun, "stand_run_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND); + + anim().AddAnim(eAnimRunTurnLeft, "stand_run_turn_left_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunTurnRight, "stand_run_turn_right_",-1, &velocity_run, PS_STAND); + + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimDragCorpse, "stand_drag_", -1, &velocity_drag, PS_STAND); + //anim().AddAnim(eAnimSniff, "stand_sniff_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimHowling, "stand_howling_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimJumpGlide, "jump_glide_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimThreaten, "stand_threaten_", -1, &velocity_none, PS_STAND); + + anim().AddAnim(eAnimSitLieDown, "sit_lie_down_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT); + //anim().AddAnim(eAnimLieToSleep, "lie_to_sleep_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimLieSitUp, "lie_to_sit_", -1, &velocity_none, PS_LIE); + + anim().AddAnim(eAnimJumpLeft, "stand_jump_left_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimJumpRight, "stand_jump_right_", -1, &velocity_none, PS_STAND); + + // define transitions + // order : 1. [anim -> anim] 2. [anim->state] 3. [state -> anim] 4. [state -> state] + anim().AddTransition(PS_SIT, PS_LIE, eAnimSitLieDown, false); + anim().AddTransition(PS_STAND, PS_SIT, eAnimStandSitDown, false); + anim().AddTransition(PS_SIT, PS_STAND, eAnimSitStandUp, false, SKIP_IF_AGGRESSIVE); + anim().AddTransition(PS_LIE, PS_SIT, eAnimLieSitUp, false, SKIP_IF_AGGRESSIVE); + + // todo: stand -> lie + + // define links from Action to animations + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSleep); + anim().LinkAction(ACT_REST, eAnimSitIdle); + anim().LinkAction(ACT_DRAG, eAnimDragCorpse); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimStandIdle); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +void CAI_Dog::reinit() +{ + inherited::reinit(); + + if(CCustomMonster::use_simplified_visual()) return; + + com_man().add_rotation_jump_data("1","2","3","4", PI_DIV_2); + com_man().add_rotation_jump_data("5","6","7","8", deg(179)); + com_man().add_melee_jump_data("5","jump_right_0"); +} + + +void CAI_Dog::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_CHECK_CORPSE) == ASP_CHECK_CORPSE) { + com_man().seq_run(anim().get_motion_id(eAnimCheckCorpse)); + } + + if ((spec_params & ASP_THREATEN) == ASP_THREATEN) { + anim().SetCurAnim(eAnimThreaten); + } +} + + + +#ifdef _DEBUG +void CAI_Dog::debug_on_key(int key) +{ + CKinematicsAnimated *skel = smart_cast(Visual()); + + switch (key){ + case DIK_1: + Msg("Ohhhhhhhhhhhhhhh! Here it is!"); + // strafe left + //com_man().seq_run(skel->ID_Cycle_Safe("stand_turn_ls_0")); + break; + case DIK_2: + // strafe right + com_man().seq_run(skel->ID_Cycle_Safe("stand_turn_ls_0")); + break; + case DIK_3: + // threaten + com_man().seq_run(skel->ID_Cycle_Safe("stand_threaten_0")); + break; + case DIK_0: + Msg("Ohhhhhhhhhhhhhhh! Here it is!"); + break; + } +} +#endif + diff --git a/src/xrGameLA/ai/monsters/dog/dog.h b/src/xrGameLA/ai/monsters/dog/dog.h new file mode 100644 index 000000000..825230990 --- /dev/null +++ b/src/xrGameLA/ai/monsters/dog/dog.h @@ -0,0 +1,37 @@ +#pragma once + +#include "../BaseMonster/base_monster.h" +#include "../controlled_entity.h" +#include "../../../script_export_space.h" + +class CAI_Dog : public CBaseMonster, + public CControlledEntity { + + typedef CBaseMonster inherited; + typedef CControlledEntity CControlled; + +public: + CAI_Dog (); + virtual ~CAI_Dog (); + + virtual void Load (LPCSTR section); + virtual void reinit (); + + virtual void CheckSpecParams (u32 spec_params); + + virtual bool ability_can_drag () {return true;} + + +private: +#ifdef _DEBUG + virtual void debug_on_key (int key); +#endif + + + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CAI_Dog) +#undef script_type_list +#define script_type_list save_type_list(CAI_Dog) diff --git a/src/xrGameLA/ai/monsters/dog/dog_script.cpp b/src/xrGameLA/ai/monsters/dog/dog_script.cpp new file mode 100644 index 000000000..54a811b6c --- /dev/null +++ b/src/xrGameLA/ai/monsters/dog/dog_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "dog.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CAI_Dog::script_register(lua_State *L) +{ + module(L) + [ + class_("CAI_Dog") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/dog/dog_state_manager.cpp b/src/xrGameLA/ai/monsters/dog/dog_state_manager.cpp new file mode 100644 index 000000000..00471761e --- /dev/null +++ b/src/xrGameLA/ai/monsters/dog/dog_state_manager.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "dog.h" +#include "dog_state_manager.h" +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../states/monster_state_controlled.h" +#include "../states/monster_state_help_sound.h" + +CStateManagerDog::CStateManagerDog(CAI_Dog *monster) : inherited(monster) +{ + add_state(eStateRest, new CStateMonsterRest(monster)); + add_state(eStatePanic, new CStateMonsterPanic(monster)); + add_state(eStateAttack, new CStateMonsterAttack(monster)); + add_state(eStateEat, new CStateMonsterEat(monster)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(monster)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(monster)); + add_state(eStateHitted, new CStateMonsterHitted(monster)); + add_state(eStateControlled, new CStateMonsterControlled(monster)); + add_state(eStateHearHelpSound, new CStateMonsterHearHelpSound(monster)); +} + +void CStateManagerDog::execute() +{ + u32 state_id = u32(-1); + + if (!object->is_under_control()) { + + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (check_state(eStateHearHelpSound)) { + state_id = eStateHearHelpSound; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + } else state_id = eStateControlled; + + select_state (state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} diff --git a/src/xrGameLA/ai/monsters/dog/dog_state_manager.h b/src/xrGameLA/ai/monsters/dog/dog_state_manager.h new file mode 100644 index 000000000..fc1a6970c --- /dev/null +++ b/src/xrGameLA/ai/monsters/dog/dog_state_manager.h @@ -0,0 +1,13 @@ +#pragma once +#include "../monster_state_manager.h" + +class CAI_Dog; + +class CStateManagerDog : public CMonsterStateManager { + typedef CMonsterStateManager inherited; + +public: + + CStateManagerDog (CAI_Dog *monster); + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/energy_holder.cpp b/src/xrGameLA/ai/monsters/energy_holder.cpp new file mode 100644 index 000000000..0304bda16 --- /dev/null +++ b/src/xrGameLA/ai/monsters/energy_holder.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "energy_holder.h" +#include "../../gameobject.h" + +CEnergyHolder::CEnergyHolder() +{ + m_auto_activate = false; + m_auto_deactivate = false; + m_enable = true; +} + +CEnergyHolder::~CEnergyHolder() +{ +} + +void CEnergyHolder::reinit() +{ + m_active = true; + m_value = 1.0f; + m_time_last_update = Device.dwTimeGlobal; +} + +void CEnergyHolder::reload(LPCSTR section, LPCSTR prefix, LPCSTR suffix) +{ + string128 line_name; + + m_restore_vel = pSettings->r_float(section, strconcat(sizeof(line_name),line_name,prefix,"Energy_Restore_Velocity",suffix)); + m_decline_vel = pSettings->r_float(section, strconcat(sizeof(line_name),line_name,prefix,"Energy_Decline_Velocity",suffix)); + m_critical_value = pSettings->r_float(section, strconcat(sizeof(line_name),line_name,prefix,"Energy_Critical_Value",suffix)); + m_activate_value = pSettings->r_float(section, strconcat(sizeof(line_name),line_name,prefix,"Energy_Activate_Value",suffix)); + m_aggressive_restore_vel = pSettings->r_float(section, strconcat(sizeof(line_name),line_name,prefix,"Energy_Aggressive_Restore_Velocity",suffix)); + + m_aggressive = false; +} + +void CEnergyHolder::activate() +{ + if (!is_active()) on_activate(); + m_active = true; +} + +void CEnergyHolder::deactivate() +{ + if (is_active()) on_deactivate(); + m_active = false; +} + +void CEnergyHolder::schedule_update() +{ + if (!m_enable) return; + + // Обновить значение энергии + u32 cur_time = Device.dwTimeGlobal; + float dt = float(cur_time - m_time_last_update) / 1000.f; + + if (!is_active()) + m_value += m_aggressive ? m_aggressive_restore_vel * dt : m_restore_vel * dt; + else + m_value -= m_decline_vel * dt; + + clamp(m_value, 0.f, 1.f); + + // сохранить время последнего обновления + m_time_last_update = cur_time; + + // проверка на автоматическое включение/выключение поля + if (is_active() && should_deactivate() && m_auto_deactivate) deactivate (); + if (!is_active() && can_activate() && m_auto_activate) activate (); +} + +void CEnergyHolder::enable() +{ + m_enable = true; + m_time_last_update = Device.dwTimeGlobal; +} diff --git a/src/xrGameLA/ai/monsters/energy_holder.h b/src/xrGameLA/ai/monsters/energy_holder.h new file mode 100644 index 000000000..6ea65dfac --- /dev/null +++ b/src/xrGameLA/ai/monsters/energy_holder.h @@ -0,0 +1,59 @@ +#pragma once + +class CEnergyHolder { + + // энергия + float m_value; // текущее значение энергии + float m_restore_vel; // скорость восстановления (ltx-param) + float m_decline_vel; // скорость уменьшения энергии в активном состоянии (ltx-param) + float m_critical_value; // критическое значение энергии, меньше которого активность будет отключена (ltx-param) + float m_activate_value; // значение энергии, больше которого активность может быть восстановлена (ltx-param) + u32 m_time_last_update; // время последнего обновления энергии + float m_aggressive_restore_vel; // скорость агрессивного восстановления (ltx-param) + + // активность + bool m_active; + + // автоматическая активация/деактивация + bool m_auto_activate; + bool m_auto_deactivate; + + // обновлене энергии разрешено + bool m_enable; + + // состояние агрессивности + bool m_aggressive; + +public: + CEnergyHolder (); + virtual ~CEnergyHolder (); + + virtual void reinit (); + virtual void reload (LPCSTR section, LPCSTR prefix = "", LPCSTR suffix = ""); + + virtual void schedule_update (); + + virtual void on_activate () {}; + virtual void on_deactivate () {}; + + // активность поля + void activate (); + void deactivate (); + IC bool is_active (){return m_active;} + IC bool can_activate (){return (m_value > m_activate_value);} + IC bool should_deactivate (){return (m_value < m_critical_value);} + IC void set_auto_activate (bool b_auto = true) {m_auto_activate = b_auto;} + IC void set_auto_deactivate (bool b_auto = true) {m_auto_deactivate = b_auto;} + + void enable (); + IC void disable (){m_enable = false;} + + IC void set_aggressive (bool b_val = true) {m_aggressive = b_val;} + + + // DEBUG + IC float get_value () {return m_value;} +}; + + + diff --git a/src/xrGameLA/ai/monsters/flesh/flesh.cpp b/src/xrGameLA/ai/monsters/flesh/flesh.cpp new file mode 100644 index 000000000..861e224f6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/flesh/flesh.cpp @@ -0,0 +1,157 @@ +#include "stdafx.h" +#include "flesh.h" +#include "../../../ai_space.h" +#include "flesh_state_manager.h" +#include "../monster_velocity_space.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" + + +CAI_Flesh::CAI_Flesh() +{ + StateMan = new CStateManagerFlesh(this); + + m_fEyeShiftYaw = PI_DIV_6; + + CControlled::init_external(this); +} + +CAI_Flesh::~CAI_Flesh() +{ + xr_delete(StateMan); +} + +BOOL CAI_Flesh::net_Spawn (CSE_Abstract* DC) +{ + if (!inherited::net_Spawn(DC)) + return(FALSE); + + return TRUE; +} + +void CAI_Flesh::Load(LPCSTR section) +{ + inherited::Load(section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + // define animation set + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + + anim().AddAnim(eAnimLieIdle, "lie_idle_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimSleep, "lie_idle_", -1, &velocity_none, PS_LIE); + + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND); + + anim().AddAnim(eAnimRun, "stand_run_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND); + + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimAttackFromBack, "stand_attack_back_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimCheckCorpse, "stand_eat_", 1, &velocity_none, PS_STAND); + + anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimDie, "stand_die_", -1, &velocity_none, PS_STAND); + + anim().AddAnim(eAnimStandLieDown, "stand_lie_down_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimLieStandUp, "lie_stand_up_", -1, &velocity_none, PS_LIE); + + anim().AddAnim(eAnimSteal, "stand_crawl_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimDragCorpse, "stand_drag_", -1, &velocity_drag, PS_STAND); + + anim().AddAnim(eAnimScared, "stand_scared_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimThreaten, "stand_threaten_", -1, &velocity_none, PS_STAND); + + // define transitions + anim().AddTransition(PS_STAND, PS_LIE, eAnimStandLieDown, false); + anim().AddTransition(PS_LIE, PS_STAND, eAnimLieStandUp, false, SKIP_IF_AGGRESSIVE); + + // define links from Action to animations + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkBkwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSleep); + anim().LinkAction(ACT_REST, eAnimLieIdle); + anim().LinkAction(ACT_DRAG, eAnimDragCorpse); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimScared); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +// возвращает true, если после выполнения этой функции необходимо прервать обработку +// т.е. если активирована последовательность +void CAI_Flesh::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_DRAG_CORPSE) == ASP_DRAG_CORPSE) anim().SetCurAnim(eAnimDragCorpse); + + if ((spec_params & ASP_CHECK_CORPSE) == ASP_CHECK_CORPSE) { + com_man().seq_run(anim().get_motion_id(eAnimCheckCorpse)); } + + if ((spec_params & ASP_BACK_ATTACK) == ASP_BACK_ATTACK) { + com_man().seq_run(anim().get_motion_id(eAnimAttackFromBack)); + } + + if ((spec_params & ASP_THREATEN) == ASP_THREATEN) anim().SetCurAnim(eAnimThreaten); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////// +// Функция ConeSphereIntersection +// Пересечение конуса (не ограниченного) со сферой +// Необходима для определения пересечения копыта плоти с баунд-сферой крысы +// Параметры: ConeVertex - вершина конуса, ConeAngle - угол конуса (между поверхностью и высотой) +// ConeDir - направление конуса, SphereCenter - центр сферы, SphereRadius - радиус сферы +bool CAI_Flesh::ConeSphereIntersection(Fvector ConeVertex, float ConeAngle, Fvector ConeDir, Fvector SphereCenter, float SphereRadius) +{ + float fInvSin = 1.0f/_sin(ConeAngle); + float fCosSqr = _cos(ConeAngle)*_cos(ConeAngle); + + + Fvector kCmV; kCmV.sub(SphereCenter,ConeVertex); + Fvector kD = kCmV; + Fvector tempV = ConeDir; + tempV.mul (SphereRadius* fInvSin); + kD.add (tempV); + + float fDSqrLen = kD.square_magnitude(); + float fE = kD.dotproduct(ConeDir); + if ( fE > 0.0f && fE*fE >= fDSqrLen*fCosSqr ) { + + float fSinSqr = _sin(ConeAngle)*_sin(ConeAngle); + + fDSqrLen = kCmV.square_magnitude(); + fE = -kCmV.dotproduct(ConeDir); + if ( fE > 0.0f && fE*fE >= fDSqrLen*fSinSqr ) { + float fRSqr = SphereRadius*SphereRadius; + return fDSqrLen <= fRSqr; + } else return true; + } + + return false; +} diff --git a/src/xrGameLA/ai/monsters/flesh/flesh.h b/src/xrGameLA/ai/monsters/flesh/flesh.h new file mode 100644 index 000000000..4c92ff009 --- /dev/null +++ b/src/xrGameLA/ai/monsters/flesh/flesh.h @@ -0,0 +1,33 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../controlled_entity.h" +#include "../../../script_export_space.h" + +class CAI_Flesh : public CBaseMonster, + public CControlledEntity { + + typedef CBaseMonster inherited; + typedef CControlledEntity CControlled; + +public: + CAI_Flesh (); + virtual ~CAI_Flesh (); + + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + + virtual void CheckSpecParams (u32 spec_params); + + virtual bool ability_can_drag () {return true;} + +private: + bool ConeSphereIntersection (Fvector ConeVertex, float ConeAngle, Fvector ConeDir, + Fvector SphereCenter, float SphereRadius); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CAI_Flesh) +#undef script_type_list +#define script_type_list save_type_list(CAI_Flesh) + diff --git a/src/xrGameLA/ai/monsters/flesh/flesh_script.cpp b/src/xrGameLA/ai/monsters/flesh/flesh_script.cpp new file mode 100644 index 000000000..80021b7c2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/flesh/flesh_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "flesh.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CAI_Flesh::script_register(lua_State *L) +{ + module(L) + [ + class_("CAI_Flesh") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/flesh/flesh_state_manager.cpp b/src/xrGameLA/ai/monsters/flesh/flesh_state_manager.cpp new file mode 100644 index 000000000..df0dc1586 --- /dev/null +++ b/src/xrGameLA/ai/monsters/flesh/flesh_state_manager.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "flesh.h" +#include "flesh_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../states/monster_state_controlled.h" +#include "../states/monster_state_help_sound.h" + +CStateManagerFlesh::CStateManagerFlesh(CAI_Flesh *monster) : inherited(monster) +{ + add_state(eStateRest, new CStateMonsterRest(monster)); + add_state(eStatePanic, new CStateMonsterPanic(monster)); + add_state(eStateAttack, new CStateMonsterAttack(monster)); + add_state(eStateEat, new CStateMonsterEat(monster)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(monster)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(monster)); + add_state(eStateHitted, new CStateMonsterHitted(monster)); + add_state(eStateControlled, new CStateMonsterControlled(monster)); + add_state(eStateHearHelpSound, new CStateMonsterHearHelpSound(monster)); + +} + +void CStateManagerFlesh::execute() +{ + u32 state_id = u32(-1); + + if (!object->is_under_control()) { + + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (check_state(eStateHearHelpSound)) { + state_id = eStateHearHelpSound; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + + } + } else state_id = eStateControlled; + + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} diff --git a/src/xrGameLA/ai/monsters/flesh/flesh_state_manager.h b/src/xrGameLA/ai/monsters/flesh/flesh_state_manager.h new file mode 100644 index 000000000..d842f138a --- /dev/null +++ b/src/xrGameLA/ai/monsters/flesh/flesh_state_manager.h @@ -0,0 +1,13 @@ +#pragma once +#include "../monster_state_manager.h" + +class CAI_Flesh; + +class CStateManagerFlesh : public CMonsterStateManager { + typedef CMonsterStateManager inherited; + +public: + + CStateManagerFlesh (CAI_Flesh *monster); + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/fracture/fracture.cpp b/src/xrGameLA/ai/monsters/fracture/fracture.cpp new file mode 100644 index 000000000..fd46d0a5f --- /dev/null +++ b/src/xrGameLA/ai/monsters/fracture/fracture.cpp @@ -0,0 +1,93 @@ +#include "stdafx.h" +#include "fracture.h" +#include "fracture_state_manager.h" +#include "../monster_velocity_space.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" + + +CFracture::CFracture() +{ + StateMan = new CStateManagerFracture(this); +} + +CFracture::~CFracture() +{ + xr_delete(StateMan); +} + +void CFracture::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + //SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandDamaged, "stand_idle_dmg_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND); + anim().AddAnim(eAnimRun, "stand_run_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimDie, "stand_die_", 0, &velocity_none, PS_STAND); + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSteal, "stand_crawl_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimLookAround, "stand_look_around_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimEat, "sit_eat_", -1, &velocity_none, PS_SIT); + + anim().AddTransition(PS_STAND, PS_SIT, eAnimStandSitDown, false); + anim().AddTransition(PS_SIT, PS_STAND, eAnimSitStandUp, false); + + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSitIdle); + anim().LinkAction(ACT_REST, eAnimSitIdle); + anim().LinkAction(ACT_DRAG, eAnimStandIdle); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimLookAround); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +void CFracture::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_CHECK_CORPSE) == ASP_CHECK_CORPSE) { + com_man().seq_run(anim().get_motion_id(eAnimCheckCorpse)); + } + + if ((spec_params & ASP_STAND_SCARED) == ASP_STAND_SCARED) { + anim().SetCurAnim(eAnimLookAround); + return; + } +} + + diff --git a/src/xrGameLA/ai/monsters/fracture/fracture.h b/src/xrGameLA/ai/monsters/fracture/fracture.h new file mode 100644 index 000000000..1aae75dbb --- /dev/null +++ b/src/xrGameLA/ai/monsters/fracture/fracture.h @@ -0,0 +1,22 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../../../script_export_space.h" + +class CStateManagerFracture; + +class CFracture : public CBaseMonster { + typedef CBaseMonster inherited; + +public: + CFracture (); + virtual ~CFracture (); + + virtual void Load (LPCSTR section); + virtual void CheckSpecParams (u32 spec_params); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CFracture) +#undef script_type_list +#define script_type_list save_type_list(CFracture) diff --git a/src/xrGameLA/ai/monsters/fracture/fracture_script.cpp b/src/xrGameLA/ai/monsters/fracture/fracture_script.cpp new file mode 100644 index 000000000..bfd4446e5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/fracture/fracture_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "fracture.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CFracture::script_register(lua_State *L) +{ + module(L) + [ + class_("CFracture") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/fracture/fracture_state_manager.cpp b/src/xrGameLA/ai/monsters/fracture/fracture_state_manager.cpp new file mode 100644 index 000000000..0abb3f427 --- /dev/null +++ b/src/xrGameLA/ai/monsters/fracture/fracture_state_manager.cpp @@ -0,0 +1,63 @@ +#include "stdafx.h" +#include "fracture.h" +#include "fracture_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" + +#include "../../../entitycondition.h" + +CStateManagerFracture::CStateManagerFracture(CFracture *obj) : inherited(obj) +{ + add_state(eStateRest, new CStateMonsterRest(obj)); + add_state(eStateAttack, new CStateMonsterAttack(obj)); + add_state(eStateEat, new CStateMonsterEat(obj)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(obj)); + add_state(eStatePanic, new CStateMonsterPanic(obj)); + add_state(eStateHitted, new CStateMonsterHitted(obj)); +} + +CStateManagerFracture::~CStateManagerFracture() +{ +} + +void CStateManagerFracture::execute() +{ + u32 state_id = u32(-1); + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (object->hear_interesting_sound || object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else { + if (can_eat()) state_id = eStateEat; + else { + // Rest & Idle states here + state_id = eStateRest; + } + } + + // установить текущее состояние + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} + diff --git a/src/xrGameLA/ai/monsters/fracture/fracture_state_manager.h b/src/xrGameLA/ai/monsters/fracture/fracture_state_manager.h new file mode 100644 index 000000000..994cbfdbe --- /dev/null +++ b/src/xrGameLA/ai/monsters/fracture/fracture_state_manager.h @@ -0,0 +1,14 @@ +#pragma once +#include "../monster_state_manager.h" + +class CFracture; + +class CStateManagerFracture : public CMonsterStateManager { + typedef CMonsterStateManager inherited; + +public: + CStateManagerFracture (CFracture *obj); + virtual ~CStateManagerFracture (); + + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss.cpp b/src/xrGameLA/ai/monsters/ghostboss/ghostboss.cpp new file mode 100644 index 000000000..e10b26238 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss.cpp @@ -0,0 +1,464 @@ +#include "stdafx.h" +#include "ghostboss.h" +#include "../../../PhysicsShell.h" +#include "../../../characterphysicssupport.h" +#include "../../../actor.h" +#include "ghostboss_state_manager.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../sound_player.h" +#include "../../../level.h" +#include "../../../ai_monster_space.h" +#include "../../../level_debug.h" +#include "../monster_velocity_space.h" +#include "../../../GamePersistent.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" +#include "ghostboss_fast_gravi.h" +#include "../control_path_builder_base.h" +#include "../../../restricted_object.h" +#include "../../../detail_path_manager.h" + +bool CGhostBoss::can_scan = true; + +CGhostBoss::CGhostBoss() +{ + StateMan = new CStateManagerGhostBoss(this); + TScanner::init_external(this); + + m_fast_gravi = new CGhostBossFastGravi(); + control().add (m_fast_gravi, ControlCom::eComCustom1); + +} + +CGhostBoss::~CGhostBoss() +{ + xr_delete(StateMan); + xr_delete(m_fast_gravi); +} + + +void CGhostBoss::reinit() +{ + inherited::reinit (); + TScanner::reinit (); + + DeactivateShield(); + + time_last_scan = 0; +} + +void CGhostBoss::net_Destroy() +{ + inherited::net_Destroy(); + TScanner::on_destroy(); +} + +void CGhostBoss::reload(LPCSTR section) +{ + inherited::reload (section); + + // add specific sounds + sound().add (pSettings->r_string(section,"sound_gravi_attack"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority + 2, u32(MonsterSound::eBaseChannel), eMonsterSoundGraviAttack, "bip01_head"); + sound().add (pSettings->r_string(section,"sound_tele_attack"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority + 3, u32(MonsterSound::eBaseChannel), eMonsterSoundTeleAttack, "bip01_head"); + + // add triple animations + com_man().ta_fill_data(anim_triple_gravi, "stand_gravi_0", "stand_gravi_1", "stand_gravi_2", TA_EXECUTE_ONCE, TA_DONT_SKIP_PREPARE, ControlCom::eCapturePath | ControlCom::eCaptureMovement); + com_man().ta_fill_data(anim_triple_tele, "stand_tele_0", "stand_tele_1", "stand_tele_2", TA_EXECUTE_ONCE, TA_DONT_SKIP_PREPARE, ControlCom::eCapturePath | ControlCom::eCaptureMovement); + com_man().ta_fill_data(anim_triple_shield, "idle_to_shield_0", "shield_0", "shield_0_idle_0", TA_EXECUTE_LOOPED, TA_DONT_SKIP_PREPARE, ControlCom::eCapturePath | ControlCom::eCaptureMovement); +} + +void CGhostBoss::Load(LPCSTR section) +{ + inherited::Load (section); + TScanner::load (section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimStandIdle, eAnimStandDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + + particle_gravi_wave = pSettings->r_string(section,"Particle_Gravi_Wave"); + particle_gravi_prepare = pSettings->r_string(section,"Particle_Gravi_Prepare"); + particle_tele_object = pSettings->r_string(section,"Particle_Tele_Object"); + + ::Sound->create(sound_gravi_wave, pSettings->r_string(section,"sound_gravi_wave"),st_Effect,SOUND_TYPE_WORLD); + ::Sound->create(sound_tele_hold, pSettings->r_string(section,"sound_tele_hold"), st_Effect,SOUND_TYPE_WORLD); + ::Sound->create(sound_tele_throw, pSettings->r_string(section,"sound_tele_throw"),st_Effect,SOUND_TYPE_WORLD); + + m_gravi_speed = pSettings->r_u32(section,"Gravi_Speed"); + m_gravi_step = pSettings->r_u32(section,"Gravi_Step"); + m_gravi_time_to_hold = pSettings->r_u32(section,"Gravi_Time_To_Hold"); + m_gravi_radius = pSettings->r_float(section,"Gravi_Radius"); + m_gravi_impulse_to_objects = pSettings->r_float(section,"Gravi_Impulse_To_Objects"); + m_gravi_impulse_to_enemy = pSettings->r_float(section,"Gravi_Impulse_To_Enemy"); + m_gravi_hit_power = pSettings->r_float(section,"Gravi_Hit_Power"); + + m_tele_max_handled_objects = pSettings->r_u32(section,"Tele_Max_Handled_Objects"); + m_tele_time_to_hold = pSettings->r_u32(section,"Tele_Time_To_Hold"); + m_tele_object_min_mass = pSettings->r_float(section,"Tele_Object_Min_Mass"); + m_tele_object_max_mass = pSettings->r_float(section,"Tele_Object_Max_Mass"); + m_tele_find_radius = pSettings->r_float(section,"Tele_Find_Radius"); + + m_shield_cooldown = READ_IF_EXISTS(pSettings,r_u32,section,"shield_cooldown",4000); + m_shield_time = READ_IF_EXISTS(pSettings,r_u32,section,"shield_time",3000); + m_shield_keep_particle = READ_IF_EXISTS(pSettings,r_string,section,"shield_keep_particle",0); + m_shield_keep_particle_period = READ_IF_EXISTS(pSettings,r_u32,section,"shield_keep_particle_period",1000); + particle_fire_shield = pSettings->r_string(section,"Particle_Shield"); + + m_teleport_step = pSettings->r_u32(section,"Teleport_Step"); + m_teleport_length = pSettings->r_u32(section,"Teleport_Length"); + m_teleport_particle = pSettings->r_string(section,"Teleport_Particle"); + + //m_teleport_step += Random.randI(m_teleport_step/5) - m_teleport_step/10 + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); +// SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandDamaged, "stand_idle_dmg_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRun, "stand_run_fwd_", -1, &velocity_run, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimDie, "stand_die_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimScared, "stand_scared_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimEat, "sit_eat_", -1, &velocity_none, PS_SIT, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimCheckCorpse, "sit_check_corpse_", -1, &velocity_none, PS_SIT, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().AddTransition(PS_SIT, PS_STAND, eAnimSitStandUp, false); + anim().AddTransition(PS_STAND, PS_SIT, eAnimStandSitDown, false); + + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSitIdle); + anim().LinkAction(ACT_REST, eAnimSitIdle); + anim().LinkAction(ACT_DRAG, eAnimWalkFwd); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimScared); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +void CGhostBoss::shedule_Update(u32 dt) +{ + inherited::shedule_Update (dt); + + TTelekinesis::schedule_update (); + TScanner::schedule_update (); + + (!EnemyMan.get_enemy()) ? TScanner::enable() : TScanner::disable(); +} + +void CGhostBoss::CheckSpecParams(u32 spec_params) +{ +} + +void CGhostBoss::UpdateGraviObject() +{ + if (!m_gravi_object.active) return; + + if (!m_gravi_object.enemy || (m_gravi_object.enemy && m_gravi_object.enemy->getDestroy())) { + m_gravi_object.deactivate(); + return; + } + + if (m_gravi_object.from_pos.distance_to(m_gravi_object.cur_pos) > (m_gravi_object.from_pos.distance_to(m_gravi_object.target_pos))) { + m_gravi_object.deactivate(); + return; + } + + float dt = float(Device.dwTimeGlobal - m_gravi_object.time_last_update); + float dist = dt * float(m_gravi_speed)/1000.f; + + if (dist < m_gravi_step) return; + + Fvector new_pos; + Fvector dir; + dir.sub(m_gravi_object.target_pos,m_gravi_object.cur_pos); + dir.normalize(); + + new_pos.mad(m_gravi_object.cur_pos,dir,dist); + + // Trace to enemy + Fvector enemy_center; + m_gravi_object.enemy->Center(enemy_center); + dir.sub(enemy_center, new_pos); + dir.normalize(); + + float trace_dist = float(m_gravi_step); + + collide::rq_result l_rq; + if (Level().ObjectSpace.RayPick(new_pos, dir, trace_dist, collide::rqtBoth, l_rq, NULL)) { + const CObject *enemy = smart_cast(m_gravi_object.enemy); + if ((l_rq.O == enemy) && (l_rq.range < trace_dist)) { + + // check for visibility + bool b_enemy_visible = false; + xr_vector visible_objects; + feel_vision_get(visible_objects); + + // find object + for (u32 i = 0; iUpdateParent(pos, zero_vel); + ps->Play(false); + + // hit objects + m_nearest.clear_not_free (); + Level().ObjectSpace.GetNearest (m_nearest,m_gravi_object.cur_pos, m_gravi_radius, NULL); + //xr_vector &m_nearest = Level().ObjectSpace.q_nearest; + + for (u32 i=0;i(m_nearest[i]); + if (!obj || !obj->m_pPhysicsShell) continue; + + Fvector dir; + dir.sub(obj->Position(), m_gravi_object.cur_pos); + dir.normalize(); + obj->m_pPhysicsShell->applyImpulse(dir,m_gravi_impulse_to_objects * obj->m_pPhysicsShell->getMass()); + } + + // играть звук + Fvector snd_pos = m_gravi_object.cur_pos; + snd_pos.y += 0.5f; + if (sound_gravi_wave._feedback()) { + sound_gravi_wave.set_position (snd_pos); + } else ::Sound->play_at_pos (sound_gravi_wave,0,snd_pos); +} + +void CGhostBoss::UpdateCL() +{ + inherited::UpdateCL(); + TScanner::frame_update(Device.dwTimeDelta); + + UpdateGraviObject(); + + + //if (m_fast_gravi->check_start_conditions()) + // control().activate(ControlCom::eComCustom1); + +} + + +bool CGhostBoss::CanTeleport() +{ + Fvector position; + return GetTeleportPosition(position); +} + +bool CGhostBoss::GetTeleportPosition(Fvector &position) +{ + Fvector dir; + position = Position(); + u32 node = u32(-1); + + dir.sub (Actor()->Position(), position); + dir.normalize_safe (); + float distance = Actor()->Position().distance_to_xz(Position()); + distance += std::max(2.0f, distance/2); + + position.mad (Actor()->Position(), dir, distance); + + // проверить позицию на accessible + if(!movement().restrictions().accessible(position)) { + //node = movement().restrictions().accessible_nearest(Fvector().set(position), position); + } else node = 1; + + if (node==u32(-1)) return false; + return true; +} + +void CGhostBoss::ActivateTeleport() +{ + setVisible(false); + CParticlesPlayer::StartParticles(m_teleport_particle,Fvector().set(0.0f,0.1f,0.0f),ID()); +// CSE_ALifeDynamicObject* O = ai().alife().objects().object(ID(),true); +// O->m_bDirectControl = false; + + Fvector pos; + GetTeleportPosition(pos); + + + Fmatrix F = XFORM(); + F.c = pos; + movement().detail().make_inactual(); + if (animation_movement_controlled()) + destroy_anim_mov_ctrl(); + + if(!g_Alive()) return; + XFORM().set (F); + if(character_physics_support()->movement()->CharacterExist()) character_physics_support()->movement()->EnableCharacter (); + character_physics_support()->set_movement_position( F.c ); + character_physics_support()->movement()->SetVelocity (0,0,0); +} + +void CGhostBoss::DeactiveTeleport() +{ + setVisible(true); + + //CParticlesPlayer::StartParticles(m_teleport_particle,Fvector().set(0.0f,0.1f,0.0f),ID()); +// CSE_ALifeDynamicObject* O = ai().alife().objects().object(ID(),true); +// O->m_bDirectControl = false; +} + +void CGhostBoss::StartGraviPrepare() +{ + const CEntityAlive *enemy = EnemyMan.get_enemy(); + if (!enemy) return; + + CActor *pA = const_cast(smart_cast(enemy)); + if (!pA) return; + + pA->CParticlesPlayer::StartParticles(particle_gravi_prepare,Fvector().set(0.0f,0.1f,0.0f),pA->ID()); +} +void CGhostBoss::StopGraviPrepare() +{ + CActor *pA = smart_cast(Level().CurrentEntity()); + if (!pA) return; + + pA->CParticlesPlayer::StopParticles(particle_gravi_prepare, BI_NONE, true); +} + +void CGhostBoss::StartTeleObjectParticle(CGameObject *pO) +{ + CParticlesPlayer* PP = smart_cast(pO); + if(!PP) return; + PP->StartParticles(particle_tele_object,Fvector().set(0.0f,0.1f,0.0f),pO->ID()); +} +void CGhostBoss::StopTeleObjectParticle(CGameObject *pO) +{ + CParticlesPlayer* PP = smart_cast(pO); + if(!PP) return; + PP->StopParticles(particle_tele_object, BI_NONE, true); +} + +//void CGhostBoss::Hit(float P,Fvector &dir,CObject*who,s16 element,Fvector p_in_object_space,float impulse, ALife::EHitType hit_type) +void CGhostBoss::Hit (SHit* pHDS) +{ + if (m_shield_active && (pHDS->hit_type == ALife::eHitTypeFireWound) && (Device.dwFrame != last_hit_frame)) { + + // вычислить позицию и направленность партикла + Fmatrix pos; + //CParticlesPlayer::MakeXFORM(this,element,Fvector().set(0.f,0.f,1.f),p_in_object_space,pos); + CParticlesPlayer::MakeXFORM(this,pHDS->bone(),pHDS->dir,pHDS->p_in_bone_space,pos); + + // установить particles + CParticlesObject* ps = CParticlesObject::Create(particle_fire_shield,TRUE); + + ps->UpdateParent(pos,Fvector().set(0.f,0.f,0.f)); + GamePersistent().ps_needtoplay.push_back(ps); + + } else if (!m_shield_active) +// inherited::Hit(P,dir,who,element,p_in_object_space,impulse,hit_type); + inherited::Hit(pHDS); + + last_hit_frame = Device.dwFrame; +} + + +void CGhostBoss::Die(CObject* who) +{ + inherited::Die(who); + TScanner::on_destroy(); + + if (com_man().ta_is_active()) com_man().ta_deactivate(); + CTelekinesis::Deactivate(); +} + +void CGhostBoss::on_scanning() +{ + time_last_scan = Device.dwTimeGlobal; +} + +void CGhostBoss::on_scan_success() +{ + CActor *pA = smart_cast(Level().CurrentEntity()); + if (!pA) return; + + EnemyMan.add_enemy(pA); +} + +void CGhostBoss::net_Relcase(CObject *O) +{ + inherited::net_Relcase (O); + + TTelekinesis::remove_links (O); +} + + +#ifdef DEBUG +CBaseMonster::SDebugInfo CGhostBoss::show_debug_info() +{ + CBaseMonster::SDebugInfo info = inherited::show_debug_info(); + if (!info.active) return CBaseMonster::SDebugInfo(); + + string128 text; + xr_sprintf(text, "Scan Value = [%f]", TScanner::get_scan_value()); + DBG().text(this).add_item(text, info.x, info.y+=info.delta_y, info.color); + DBG().text(this).add_item("---------------------------------------", info.x, info.y+=info.delta_y, info.delimiter_color); + + return CBaseMonster::SDebugInfo(); +} +#endif diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss.h new file mode 100644 index 000000000..6d8562307 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss.h @@ -0,0 +1,171 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../telekinesis.h" +#include "../anim_triple.h" +#include "../scanning_ability.h" +#include "../../../script_export_space.h" +#include "../../../ai_space.h" +#include "../../../alife_simulator.h" +#include "../../../alife_object_registry.h" + +class CCharacterPhysicsSupport; +class CGhostBossFastGravi; + +class CGhostBoss : public CBaseMonster, + public CTelekinesis, + public CScanningAbility { + + typedef CBaseMonster inherited; + +private: + xr_vector m_nearest; + +public: + typedef CScanningAbility TScanner; + + + static bool can_scan; + + u32 last_hit_frame; + u32 time_last_scan; + + + typedef CTelekinesis TTelekinesis; + + struct GraviObject { + bool active; + Fvector cur_pos; + Fvector target_pos; + Fvector from_pos; + + u32 time_last_update; + + const CEntityAlive *enemy; + + GraviObject() { + active = false; + enemy = 0; + } + + + void activate(const CEntityAlive *e, const Fvector &cp, const Fvector &tp) { + active = true; + from_pos = cp; + cur_pos = cp; + target_pos = tp; + time_last_update = Device.dwTimeGlobal; + enemy = e; + } + + void deactivate() { + active = false; + } + + } m_gravi_object; + + LPCSTR particle_gravi_wave; + LPCSTR particle_gravi_prepare; + LPCSTR particle_tele_object; + + ////////////////////////////////////////////////////////////////////////// + // Sounds + ref_sound sound_gravi_wave; + ref_sound sound_scan; + + ref_sound sound_tele_hold; + ref_sound sound_tele_throw; + + enum EGhostBossSounds { + eAdditionalSounds = MonsterSound::eMonsterSoundCustom, + + eMonsterSoundGraviAttack = eAdditionalSounds | 0, + eMonsterSoundTeleAttack = eAdditionalSounds | 1, + }; + ////////////////////////////////////////////////////////////////////////// + + + u32 m_teleport_step; + u32 m_teleport_time; + u32 m_teleport_length; + LPCSTR m_teleport_particle; + + u32 m_gravi_speed; + u32 m_gravi_step; + u32 m_gravi_time_to_hold; + float m_gravi_radius; + float m_gravi_impulse_to_objects; + float m_gravi_impulse_to_enemy; + float m_gravi_hit_power; + + + u32 m_tele_max_handled_objects; + u32 m_tele_time_to_hold; + float m_tele_object_min_mass; + float m_tele_object_max_mass; + float m_tele_find_radius; + + u32 m_shield_cooldown; + u32 m_shield_time; + bool m_shield_active; + LPCSTR m_shield_keep_particle; + u32 m_shield_keep_particle_period; + LPCSTR particle_fire_shield; + + CGhostBossFastGravi *m_fast_gravi; + +public: + CGhostBoss (); + virtual ~CGhostBoss (); + + + virtual void reinit (); + virtual void reload (LPCSTR section); + + virtual void Load (LPCSTR section); + + virtual void net_Destroy (); + virtual void net_Relcase (CObject *O); + virtual void shedule_Update (u32 dt); + virtual void UpdateCL (); + virtual void Hit (SHit* pHDS); + virtual void Die (CObject* who); + void ProcessTurn (); + virtual void CheckSpecParams (u32 spec_params); + + void UpdateGraviObject (); + + void ActivateTeleport (); + void DeactiveTeleport (); + bool CanTeleport (); + bool GetTeleportPosition (Fvector &position); + + void StartGraviPrepare (); + void StopGraviPrepare (); + + void StartTeleObjectParticle(CGameObject *pO); + void StopTeleObjectParticle(CGameObject *pO); + + void ActivateShield () {m_shield_active = true;} + void DeactivateShield () {m_shield_active = false;} + + virtual bool ability_distant_feel() {return true;} + + virtual void on_scanning (); + virtual void on_scan_success (); + +public: + SAnimationTripleData anim_triple_gravi; + SAnimationTripleData anim_triple_tele; + SAnimationTripleData anim_triple_shield; + +#ifdef DEBUG + virtual CBaseMonster::SDebugInfo show_debug_info(); +#endif + + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CGhostBoss) +#undef script_type_list +#define script_type_list save_type_list(CGhostBoss) diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_fast_gravi.cpp b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_fast_gravi.cpp new file mode 100644 index 000000000..c99671770 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_fast_gravi.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include "ghostboss_fast_gravi.h" +#include "ghostboss.h" +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" + +bool CGhostBossFastGravi::check_start_conditions() +{ + if (is_active()) return false; + if (m_man->is_captured_pure()) return false; + if (!m_object->EnemyMan.get_enemy()) return false; + + return true; +} + +void CGhostBossFastGravi::activate() +{ + CGhostBoss *GhostBoss = smart_cast(m_object); + m_man->subscribe (this, ControlCom::eventTAChange); + m_object->com_man().ta_activate(GhostBoss->anim_triple_gravi); + + m_object->dir().face_target(m_object->EnemyMan.get_enemy()); +} + +void CGhostBossFastGravi::deactivate() +{ + m_man->unsubscribe (this, ControlCom::eventTAChange); +} + +void CGhostBossFastGravi::on_event(ControlCom::EEventType type, ControlCom::IEventData *data) +{ + if (type == ControlCom::eventTAChange) { + STripleAnimEventData *event_data = (STripleAnimEventData *)data; + if (event_data->m_current_state == eStateExecute) { + process_hit(); + m_object->com_man().ta_pointbreak(); + m_man->deactivate (this); + } + } +} + +void CGhostBossFastGravi::process_hit() +{ + m_object->HitEntity(m_object->EnemyMan.get_enemy(), 1.f, 100.f, m_object->Direction()); +} + diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_fast_gravi.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_fast_gravi.h new file mode 100644 index 000000000..5ac12268e --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_fast_gravi.h @@ -0,0 +1,18 @@ +#pragma once +#include "../control_combase.h" + +class CGhostBossFastGravi : public CControl_ComCustom<> { + typedef CControl_ComCustom<> inherited; + +public: + + virtual bool check_start_conditions (); + virtual void activate (); + virtual void deactivate (); + virtual void on_event (ControlCom::EEventType, ControlCom::IEventData*); + +private: + void process_hit (); + +}; + diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_script.cpp b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_script.cpp new file mode 100644 index 000000000..d6665bfb2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "ghostboss.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CGhostBoss::script_register(lua_State *L) +{ + module(L) + [ + class_("CGhostBoss") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack.h new file mode 100644 index 000000000..64bb193f9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack.h @@ -0,0 +1,21 @@ +#pragma once +#include "../state.h" + +template +class CStateGhostBossAttack : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object> *state_ptr; + + bool m_force_gravi; + +public: + CStateGhostBossAttack (_Object *obj); + + virtual void initialize (); + + virtual void reselect_state (); + virtual void setup_substates (); + virtual void check_force_state (); +}; + +#include "ghostboss_state_attack_inline.h" diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_gravi.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_gravi.h new file mode 100644 index 000000000..9fdee5cbc --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_gravi.h @@ -0,0 +1,38 @@ +#pragma once +#include "../state.h" + +template +class CStateGhostBossAttackGravi : public CState<_Object> { + typedef CState<_Object> inherited; + + enum { + ACTION_GRAVI_STARTED, + ACTION_GRAVI_CONTINUE, + ACTION_GRAVI_FIRE, + ACTION_WAIT_TRIPLE_END, + ACTION_COMPLETED, + } m_action; + + u32 time_gravi_started; + +public: + CStateGhostBossAttackGravi (_Object *obj); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); + +private: + // выполнять состояние + void ExecuteGraviStart (); + void ExecuteGraviContinue (); + void ExecuteGraviFire (); + +}; + +#include "GhostBoss_state_attack_gravi_inline.h" + diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_gravi_inline.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_gravi_inline.h new file mode 100644 index 000000000..3f5ae8fa9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_gravi_inline.h @@ -0,0 +1,160 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateGhostBossAttackGraviAbstract CStateGhostBossAttackGravi<_Object> + +#define GOOD_DISTANCE_FOR_GRAVI 6.f + +TEMPLATE_SPECIALIZATION +CStateGhostBossAttackGraviAbstract::CStateGhostBossAttackGravi(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackGraviAbstract::initialize() +{ + inherited::initialize (); + + m_action = ACTION_GRAVI_STARTED; + + time_gravi_started = 0; + + object->set_script_capture (false); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackGraviAbstract::execute() +{ + switch (m_action) { + /************************/ + case ACTION_GRAVI_STARTED: + /************************/ + + ExecuteGraviStart(); + m_action = ACTION_GRAVI_CONTINUE; + + break; + /************************/ + case ACTION_GRAVI_CONTINUE: + /************************/ + + ExecuteGraviContinue(); + + break; + + /************************/ + case ACTION_GRAVI_FIRE: + /************************/ + + ExecuteGraviFire(); + m_action = ACTION_WAIT_TRIPLE_END; + + break; + /***************************/ + case ACTION_WAIT_TRIPLE_END: + /***************************/ + if (!object->com_man().ta_is_active()) { + m_action = ACTION_COMPLETED; + } + + /*********************/ + case ACTION_COMPLETED: + /*********************/ + + break; + } + + object->anim().m_tAction = ACT_STAND_IDLE; + object->dir().face_target (object->EnemyMan.get_enemy(), 500); +} +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackGraviAbstract::finalize() +{ + inherited::finalize(); + + object->com_man().ta_pointbreak (); +// object->DeactivateShield (); + object->set_script_capture (true); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackGraviAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + object->com_man().ta_pointbreak (); +// object->DeactivateShield (); + object->StopGraviPrepare (); + object->set_script_capture (false); +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackGraviAbstract::check_start_conditions() +{ + // обработать объекты + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + if (dist < GOOD_DISTANCE_FOR_GRAVI) return false; + if (!object->EnemyMan.see_enemy_now()) return false; + if (!object->control().direction().is_face_target(object->EnemyMan.get_enemy(), deg(45))) return false; + if (object->com_man().ta_is_active()) return false; + + // всё ок, можно начать грави атаку + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackGraviAbstract::check_completion() +{ + return (m_action == ACTION_COMPLETED); +} + +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackGraviAbstract::ExecuteGraviStart() +{ + object->com_man().ta_activate(object->anim_triple_gravi); + + time_gravi_started = Device.dwTimeGlobal; + + object->StartGraviPrepare(); +// object->ActivateShield(); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackGraviAbstract::ExecuteGraviContinue() +{ + // проверить на грави удар + + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + float time_to_hold = (abs(dist - GOOD_DISTANCE_FOR_GRAVI)/GOOD_DISTANCE_FOR_GRAVI); + clamp(time_to_hold, 0.f, 1.f); + time_to_hold *= float(object->m_gravi_time_to_hold); + + if (time_gravi_started + u32(time_to_hold) < Device.dwTimeGlobal) { + m_action = ACTION_GRAVI_FIRE; + } +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackGraviAbstract::ExecuteGraviFire() +{ + object->com_man().ta_pointbreak(); + + Fvector from_pos; + Fvector target_pos; + from_pos = object->Position(); from_pos.y += 0.5f; + target_pos = object->EnemyMan.get_enemy()->Position(); target_pos.y += 0.5f; + + object->m_gravi_object.activate(object->EnemyMan.get_enemy(), from_pos, target_pos); + + object->StopGraviPrepare (); + object->sound().play (CGhostBoss::eMonsterSoundGraviAttack); +// object->DeactivateShield (); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateGhostBossAttackGraviAbstract \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_inline.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_inline.h new file mode 100644 index 000000000..3bdcfc0e5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_inline.h @@ -0,0 +1,135 @@ +#pragma once + +#include "ghostboss_state_attack_tele.h" +#include "ghostboss_state_attack_teleport.h" +#include "ghostboss_state_attack_gravi.h" +#include "ghostboss_state_attack_melee.h" +#include "ghostboss_state_attack_shield.h" +#include "../states/state_look_point.h" +#include "../states/state_move_to_restrictor.h" +#include "ghostboss_state_attack_run_around.h" + +#define GRAVI_PERCENT 80 +#define TELE_PERCENT 50 +#define RUN_AROUND_PERCENT 20 + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateGhostBossAttackAbstract CStateGhostBossAttack<_Object> + +TEMPLATE_SPECIALIZATION +CStateGhostBossAttackAbstract::CStateGhostBossAttack(_Object *obj) : inherited(obj) +{ + add_state(eStateGhostBossAttack_Tele, new CStateGhostBossAttackTele<_Object>(obj)); + add_state(eStateGhostBossAttack_Gravi, new CStateGhostBossAttackGravi<_Object>(obj)); + add_state(eStateGhostBossAttack_Melee, new CStateGhostBossAttackMelee<_Object>(obj)); + + add_state(eStateGhostBossAttack_FaceEnemy, new CStateMonsterLookToPoint<_Object>(obj)); + add_state(eStateGhostBossAttack_RunAround, new CStateGhostBossAttackRunAround<_Object>(obj)); + + add_state(eStateCustomMoveToRestrictor, new CStateMonsterMoveToRestrictor<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackAbstract::initialize() +{ + inherited::initialize (); + m_force_gravi = false; +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackAbstract::reselect_state() +{ + if (get_state(eStateGhostBossAttack_Melee)->check_start_conditions()) { + select_state(eStateGhostBossAttack_Melee); + return; + } + + if (m_force_gravi) { + m_force_gravi = false; + + if (get_state(eStateGhostBossAttack_Gravi)->check_start_conditions()) { + select_state (eStateGhostBossAttack_Gravi); + return; + } + } + + if (get_state(eStateCustomMoveToRestrictor)->check_start_conditions()) { + select_state(eStateCustomMoveToRestrictor); + return; + } + + bool enable_gravi = false;//get_state(eStateGhostBossAttack_Gravi)->check_start_conditions (); + bool enable_tele = get_state(eStateGhostBossAttack_Tele)->check_start_conditions (); + + if (!enable_gravi && !enable_tele) { + if (prev_substate == eStateGhostBossAttack_RunAround) + select_state(eStateGhostBossAttack_FaceEnemy); + else + select_state(eStateGhostBossAttack_RunAround); + return; + } + + if (enable_gravi && enable_tele) { + + u32 rnd_val = ::Random.randI(GRAVI_PERCENT + TELE_PERCENT + RUN_AROUND_PERCENT); + u32 cur_val = GRAVI_PERCENT; + + if (rnd_val < cur_val) { + select_state(eStateGhostBossAttack_Gravi); + return; + } + + cur_val += TELE_PERCENT; + if (rnd_val < cur_val) { + select_state(eStateGhostBossAttack_Tele); + return; + } + + select_state(eStateGhostBossAttack_RunAround); + return; + } + + if ((prev_substate == eStateGhostBossAttack_RunAround) || (prev_substate == eStateGhostBossAttack_FaceEnemy)) { + if (enable_gravi) select_state(eStateGhostBossAttack_Gravi); + else select_state(eStateGhostBossAttack_Tele); + } else { + select_state(eStateGhostBossAttack_RunAround); + } +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateGhostBossAttack_FaceEnemy) { + SStateDataLookToPoint data; + + data.point = object->EnemyMan.get_enemy()->Position(); + data.action.action = ACT_STAND_IDLE; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwAttackSndDelay; + + state->fill_data_with (&data, sizeof(SStateDataLookToPoint)); + return; + } + +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackAbstract::check_force_state() +{ + // check if we can start execute + if ((current_substate == eStateCustomMoveToRestrictor) || (prev_substate == eStateGhostBossAttack_RunAround)) { + if (get_state(eStateGhostBossAttack_Gravi)->check_start_conditions()) { + current_substate = u32(-1); + m_force_gravi = true; + } + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateGhostBossAttackAbstract diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_melee.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_melee.h new file mode 100644 index 000000000..ede6f622a --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_melee.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../state.h" +#include "../states/monster_state_attack.h" + +template +class CStateGhostBossAttackMelee : public CStateMonsterAttack<_Object> { + typedef CStateMonsterAttack<_Object> inherited; + +public: + CStateGhostBossAttackMelee (_Object *obj); + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "ghostboss_state_attack_melee_inline.h" diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_melee_inline.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_melee_inline.h new file mode 100644 index 000000000..88c44dcf5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_melee_inline.h @@ -0,0 +1,37 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateGhostBossAttackMeleeAbstract CStateGhostBossAttackMelee<_Object> + +#define MIN_DIST_MELEE_ATTACK 5.f +#define MAX_DIST_MELEE_ATTACK 9.f + +TEMPLATE_SPECIALIZATION +CStateGhostBossAttackMeleeAbstract::CStateGhostBossAttackMelee(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackMeleeAbstract::check_start_conditions() +{ + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + if (dist > MIN_DIST_MELEE_ATTACK) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackMeleeAbstract::check_completion() +{ + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + if (dist < MAX_DIST_MELEE_ATTACK) return false; + + return true; + +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateGhostBossAttackMeleeAbstract diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_run_around.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_run_around.h new file mode 100644 index 000000000..85a51e8aa --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_run_around.h @@ -0,0 +1,22 @@ +#pragma once +#include "../state.h" + +template +class CStateGhostBossAttackRunAround : public CState<_Object> { + typedef CState<_Object> inherited; + + Fvector selected_point; + u32 time_started; + + Fvector dest_direction; + +public: + CStateGhostBossAttackRunAround (_Object *obj); + virtual void initialize (); + virtual void execute (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "ghostboss_state_attack_run_around_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_run_around_inline.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_run_around_inline.h new file mode 100644 index 000000000..236119fdf --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_run_around_inline.h @@ -0,0 +1,89 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateGhostBossAttackRunAroundAbstract CStateGhostBossAttackRunAround<_Object> + + +#define DIST_QUANT 10.f +#define TIME_RUN_AWAY 3500 + +TEMPLATE_SPECIALIZATION +CStateGhostBossAttackRunAroundAbstract::CStateGhostBossAttackRunAround(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackRunAroundAbstract::initialize() +{ + inherited::initialize (); + + time_started = Device.dwTimeGlobal; + dest_direction.set (0.f,0.f,0.f); + + // select point + Fvector dir_to_enemy, dir_from_enemy; + dir_to_enemy.sub (object->EnemyMan.get_enemy()->Position(),object->Position()); + dir_to_enemy.normalize (); + + dir_from_enemy.sub (object->Position(),object->EnemyMan.get_enemy()->Position()); + dir_from_enemy.normalize (); + + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + + if (dist > 30.f) { // бежать к врагу + selected_point.mad(object->Position(),dir_to_enemy,DIST_QUANT); + } else if ((dist < 20.f) && (dist > 4.f)) { // убегать от врага + selected_point.mad(object->Position(),dir_from_enemy,DIST_QUANT); + dest_direction.sub (object->EnemyMan.get_enemy()->Position(),selected_point); + dest_direction.normalize (); + } else { // выбрать случайную позицию + selected_point = random_position(object->Position(), DIST_QUANT); + dest_direction.sub (object->EnemyMan.get_enemy()->Position(),selected_point); + dest_direction.normalize (); + } + + object->path().prepare_builder(); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackRunAroundAbstract::execute() +{ + if (!fis_zero(dest_direction.square_magnitude())) { + object->path().set_use_dest_orient (true); + object->path().set_dest_direction (dest_direction); + } else object->path().set_use_dest_orient (false); + + + object->set_action (ACT_RUN); + object->path().set_target_point (selected_point); + object->path().set_generic_parameters (); + object->path().set_use_covers (false); + + object->set_state_sound (MonsterSound::eMonsterSoundAggressive); +} + + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackRunAroundAbstract::check_start_conditions() +{ + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackRunAroundAbstract::check_completion() +{ + if ((time_started + TIME_RUN_AWAY < Device.dwTimeGlobal) || + (object->control().path_builder().is_moving_on_path() && object->control().path_builder().is_path_end(2.f))) { + + object->dir().face_target(object->EnemyMan.get_enemy()); + return true; + } + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateGhostBossAttackRunAroundAbstract diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_shield.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_shield.h new file mode 100644 index 000000000..29bd436f8 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_shield.h @@ -0,0 +1,33 @@ +#pragma once +#include "../state.h" + + +template +class CStateGhostBossShield : public CState<_Object> { + typedef CState<_Object> inherited; + + u32 m_last_shield_started; + u32 m_next_particle_allowed; + float m_shield_start_anim_length_sec; + enum { + ACTION_IDLE, + ACTION_SHIELD_STARTED, + ACTION_WAIT_TRIPLE_END, + ACTION_COMPLETED, + } m_action; + +public: + CStateGhostBossShield (_Object *obj); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); + +private: +}; + +#include "GhostBoss_state_attack_shield_inline.h" diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_shield_inline.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_shield_inline.h new file mode 100644 index 000000000..cde493959 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_shield_inline.h @@ -0,0 +1,119 @@ +#pragma once + +#include "../../../level.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateGhostBossShieldAbstract CStateGhostBossShield<_Object> + +TEMPLATE_SPECIALIZATION +CStateGhostBossShieldAbstract::CStateGhostBossShield(_Object *obj) : inherited(obj) +{ + m_shield_start_anim_length_sec = 0.0f; + m_last_shield_started = 0; + m_next_particle_allowed = 0; + m_action = ACTION_IDLE; +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossShieldAbstract::initialize() +{ + inherited::initialize (); + + m_last_shield_started = Device.dwTimeGlobal; + m_next_particle_allowed = 0; + m_action = ACTION_IDLE; +// m_shield_start_anim_length_sec = object->anim().motion_time(eAnimShieldStart, 0, object->Visual()); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossShieldAbstract::execute() +{ + switch (m_action) + { + case ACTION_IDLE: + object->ActivateShield(); + object->com_man().ta_activate(object->anim_triple_shield); + m_action = ACTION_SHIELD_STARTED; + break; + + case ACTION_SHIELD_STARTED: + if (object->m_shield_keep_particle && Device.dwTimeGlobal > m_next_particle_allowed) + { + CParticlesPlayer* PP = smart_cast(object); + if(!PP) return; + PP->StartParticles(object->m_shield_keep_particle,Fvector().set(0.0f,0.1f,0.0f),object->ID()); + m_next_particle_allowed = Device.dwTimeGlobal + object->m_shield_keep_particle_period; + } + break; +// case ACTION_WAIT_TRIPLE_END: +// if (!object->com_man().ta_is_active()) +// { +// m_action = ACTION_COMPLETED; +// } +// break; + case ACTION_COMPLETED: + break; + } + + if (object->EnemyMan.get_enemy()) + object->dir().face_target(object->EnemyMan.get_enemy(), 500); + else { + Fvector pos; + pos.mad(object->Position(), Fvector().set(0.1f,0.0f,0.0f)); + object->dir().face_target(pos, 500); + } + object->set_action(ACT_STAND_IDLE); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossShieldAbstract::finalize() +{ + inherited::finalize(); + + object->com_man().ta_pointbreak (); + object->DeactivateShield(); + object->set_script_capture(true); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossShieldAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + object->com_man().ta_pointbreak (); + object->DeactivateShield(); + object->set_script_capture(false); +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossShieldAbstract::check_start_conditions() +{ + if (Device.dwTimeGlobal > m_last_shield_started + object->m_shield_time + object->m_shield_cooldown) + { + if (object->EnemyMan.get_enemy() && !object->EnemyMan.enemy_see_me_now()) return false; + } + else + return false; + + if (object->com_man().ta_is_active()) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossShieldAbstract::check_completion() +{ + if (Device.dwTimeGlobal <= m_last_shield_started + object->m_shield_time) + { + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + if ((enemy && enemy != Actor()) || !Actor()->IsReloadingWeapon()) + return enemy == 0; + } + return true; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateGhostBossShieldAbstract diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_tele.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_tele.h new file mode 100644 index 000000000..71d46fb6a --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_tele.h @@ -0,0 +1,58 @@ +#pragma once +#include "../state.h" + + +template +class CStateGhostBossAttackTele : public CState<_Object> { + typedef CState<_Object> inherited; + + xr_vector tele_objects; + CPhysicsShellHolder *selected_object; + xr_vector m_nearest; + + u32 time_started; + + enum { + ACTION_TELE_STARTED, + ACTION_TELE_CONTINUE, + ACTION_TELE_FIRE, + ACTION_WAIT_TRIPLE_END, + ACTION_COMPLETED, + } m_action; + +public: + CStateGhostBossAttackTele (_Object *obj); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); + + +private: + // Поиск объектов для телекинеза + void FindObjects (); + + // выполнять состояние + void ExecuteTeleStart (); + void ExecuteTeleContinue (); + void ExecuteTeleFire (); + + // Проверка, есть ли хоть один объект под контролем + bool IsActiveObjects (); + + // Проверить, может ли стартовать телекинез + bool CheckTeleStart (); + // Выбор подходящих объектов для телекинеза + void SelectObjects (); + + // internal for FindObjects + void FindFreeObjects (xr_vector &tpObjects, const Fvector &pos); + +private: +}; + +#include "GhostBoss_state_attack_tele_inline.h" diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_tele_inline.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_tele_inline.h new file mode 100644 index 000000000..1a2b50b8c --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_tele_inline.h @@ -0,0 +1,363 @@ +#pragma once + +#include "../../../level.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateGhostBossAttackTeleAbstract CStateGhostBossAttackTele<_Object> + + +#define GOOD_DISTANCE_FOR_TELE 15.f +#define TELE_DELAY 4000 + +#define MAX_TIME_CHECK_FAILURE 6000 + +TEMPLATE_SPECIALIZATION +CStateGhostBossAttackTeleAbstract::CStateGhostBossAttackTele(_Object *obj) : inherited(obj) +{ + +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleAbstract::initialize() +{ + inherited::initialize (); + + m_action = ACTION_TELE_STARTED; + selected_object = 0; + + SelectObjects (); + + time_started = 0; + + // запретить взятие скриптом + object->set_script_capture (false); + +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleAbstract::execute() +{ + switch (m_action) { + /************************/ + case ACTION_TELE_STARTED: + /************************/ + + ExecuteTeleStart(); + m_action = ACTION_TELE_CONTINUE; + + break; + /************************/ + case ACTION_TELE_CONTINUE: + /************************/ + + ExecuteTeleContinue(); + + break; + + /************************/ + case ACTION_TELE_FIRE: + /************************/ + + ExecuteTeleFire(); + m_action = ACTION_WAIT_TRIPLE_END; + + break; + /***************************/ + case ACTION_WAIT_TRIPLE_END: + /***************************/ + + if (!object->com_man().ta_is_active()) { + if (IsActiveObjects()) + m_action = ACTION_TELE_STARTED; + else + m_action = ACTION_COMPLETED; + } + + /*********************/ + case ACTION_COMPLETED: + /*********************/ + + break; + } + + object->anim().m_tAction = ACT_STAND_IDLE; + object->dir().face_target (object->EnemyMan.get_enemy(), 700); + +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleAbstract::finalize() +{ + inherited::finalize (); + + tele_objects.clear (); +// object->DeactivateShield (); + + // clear particles on active objects + if (object->CTelekinesis::is_active()) { + for (u32 i=0; iCTelekinesis::get_objects_count(); i++) { + object->StopTeleObjectParticle(object->CTelekinesis::get_object_by_index(i).get_object()); + } + } + + // отменить запрет на взятие скриптом + object->set_script_capture (true); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleAbstract::critical_finalize() +{ + inherited::critical_finalize (); + + object->com_man().ta_pointbreak (); + object->CTelekinesis::Deactivate (); +// object->DeactivateShield (); + + tele_objects.clear (); + + // clear particles on active objects + if (object->CTelekinesis::is_active()) { + for (u32 i=0; iCTelekinesis::get_objects_count(); i++) { + object->StopTeleObjectParticle(object->CTelekinesis::get_object_by_index(i).get_object()); + } + } + + // отменить запрет на взятие скриптом + object->set_script_capture (true); +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackTeleAbstract::check_start_conditions() +{ + return CheckTeleStart(); +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackTeleAbstract::check_completion() +{ + return (m_action == ACTION_COMPLETED); +} + +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleAbstract::FindFreeObjects(xr_vector &tpObjects, const Fvector &pos) +{ + Level().ObjectSpace.GetNearest (tpObjects, pos, object->m_tele_find_radius, NULL); + + for (u32 i=0;i(tpObjects[i]); + CCustomMonster *custom_monster = smart_cast(tpObjects[i]); + if (!obj || + !obj->PPhysicsShell() || + !obj->PPhysicsShell()->isActive()|| + custom_monster || + (obj->spawn_ini() && obj->spawn_ini()->section_exist("ph_heavy")) || + (obj->m_pPhysicsShell->getMass() < object->m_tele_object_min_mass) || + (obj->m_pPhysicsShell->getMass() > object->m_tele_object_max_mass) || + (obj == object) || + object->CTelekinesis::is_active_object(obj) || + !obj->m_pPhysicsShell->get_ApplyByGravity()) continue; + + tele_objects.push_back(obj); + } +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleAbstract::FindObjects () +{ + u32 res_size = tele_objects.size (); + tele_objects.clear_and_reserve (); + + // получить список объектов вокруг врага + m_nearest.clear_not_free (); + m_nearest.reserve (res_size); + FindFreeObjects (m_nearest, object->EnemyMan.get_enemy()->Position()); + + // получить список объектов вокруг монстра + FindFreeObjects (m_nearest, object->Position()); + + // получить список объектов между монстром и врагом + float dist = object->EnemyMan.get_enemy()->Position().distance_to(object->Position()); + Fvector dir; + dir.sub(object->EnemyMan.get_enemy()->Position(), object->Position()); + dir.normalize(); + + Fvector pos; + pos.mad (object->Position(), dir, dist / 2.f); + FindFreeObjects (m_nearest, pos); + + + // оставить уникальные объекты + tele_objects.erase ( + std::unique( + tele_objects.begin(), + tele_objects.end() + ), + tele_objects.end() + ); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleAbstract::ExecuteTeleStart() +{ + object->com_man().ta_activate(object->anim_triple_tele); + time_started = Device.dwTimeGlobal; +// object->ActivateShield(); + +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleAbstract::ExecuteTeleContinue() +{ + if (time_started + object->m_tele_time_to_hold > Device.dwTimeGlobal) return; + + // найти объект для атаки + bool object_found = false; + CTelekineticObject tele_object; + + u32 i=0; + while (i < object->CTelekinesis::get_objects_count()) { + tele_object = object->CTelekinesis::get_object_by_index(i); + + if ((tele_object.get_state() == TS_Keep) && (tele_object.time_keep_started + 1500 < Device.dwTimeGlobal)) { + + object_found = true; + break; + + } else i++; + + } + + if (object_found) { + m_action = ACTION_TELE_FIRE; + selected_object = tele_object.get_object(); + } else { + if (!IsActiveObjects() || (time_started + MAX_TIME_CHECK_FAILURE < Device.dwTimeGlobal)) { + object->com_man().ta_deactivate (); + m_action = ACTION_COMPLETED; + } + } +} + +#define HEAD_OFFSET_INDOOR 1.f +#define HEAD_OFFSET_OUTDOOR 5.f + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleAbstract::ExecuteTeleFire() +{ + object->com_man().ta_pointbreak(); + + Fvector enemy_pos; + enemy_pos = get_head_position(const_cast(object->EnemyMan.get_enemy())); + object->CTelekinesis::fire_t(selected_object,enemy_pos, 0.55f); + + object->StopTeleObjectParticle (selected_object); + object->sound().play (CGhostBoss::eMonsterSoundTeleAttack); +// object->DeactivateShield (); +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackTeleAbstract::IsActiveObjects() +{ + return (object->CTelekinesis::get_objects_count() > 0); +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackTeleAbstract::CheckTeleStart() +{ + if (object->com_man().ta_is_active()) return false; + + // проверка на текущую активность + if (IsActiveObjects()) return false; + + // проверить дистанцию до врага + float dist = object->Position().distance_to(object->EnemyMan.get_enemy()->Position()); + if (dist < GOOD_DISTANCE_FOR_TELE) return false; + + // найти телекинетические объекты + FindObjects(); + + // если нет объектов + if (tele_objects.empty()) return false; + + // всё ок можно начинать телекинез + return true; + +} + +////////////////////////////////////////////////////////////////////////// +// Выбор подходящих объектов для телекинеза +////////////////////////////////////////////////////////////////////////// +class best_object_predicate { + Fvector enemy_pos; + Fvector monster_pos; +public: + best_object_predicate(const Fvector &m_pos, const Fvector &pos) { + monster_pos = m_pos; + enemy_pos = pos; + } + + bool operator() (const CGameObject *tpObject1, const CGameObject *tpObject2) const + { + + float dist1 = monster_pos.distance_to(tpObject1->Position()); + float dist2 = enemy_pos.distance_to(tpObject2->Position()); + float dist3 = enemy_pos.distance_to(monster_pos); + + return ((dist1 < dist3) && (dist2 > dist3)); + }; +}; + +class best_object_predicate2 { + Fvector enemy_pos; + Fvector monster_pos; +public: + best_object_predicate2(const Fvector &m_pos, const Fvector &pos) { + monster_pos = m_pos; + enemy_pos = pos; + } + + bool operator() (const CGameObject *tpObject1, const CGameObject *tpObject2) const + { + float dist1 = enemy_pos.distance_to(tpObject1->Position()); + float dist2 = enemy_pos.distance_to(tpObject2->Position()); + + return (dist1 < dist2); + }; +}; + + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleAbstract::SelectObjects() +{ + std::sort(tele_objects.begin(),tele_objects.end(),best_object_predicate2(object->Position(), object->EnemyMan.get_enemy()->Position())); + + // выбрать объект + for (u32 i=0; im_monster_type == CBaseMonster::eMonsterTypeIndoor) ? 1.3f : 2.f; + bool rotate = (object->m_monster_type == CBaseMonster::eMonsterTypeIndoor) ? false : true; + + CTelekineticObject *tele_obj = object->CTelekinesis::activate (obj, 3.f, height, 10000, rotate); + tele_obj->set_sound (object->sound_tele_hold,object->sound_tele_throw); + + object->StartTeleObjectParticle (obj); + + // удалить из списка + tele_objects[i] = tele_objects[tele_objects.size()-1]; + tele_objects.pop_back(); + + if (object->CTelekinesis::get_objects_count() >= object->m_tele_max_handled_objects) break; + } +} + + +#undef TEMPLATE_SPECIALIZATION +#undef CStateGhostBossAttackTeleAbstract diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_teleport.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_teleport.h new file mode 100644 index 000000000..8484680bf --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_teleport.h @@ -0,0 +1,28 @@ +#pragma once + +#include "../state.h" + +template +class CStateGhostBossTeleport : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateGhostBossTeleport (_Object *obj); + virtual bool check_start_conditions (); + virtual bool check_completion (); + virtual void initialize (); + virtual void finalize (); + virtual void critical_finalize (); + virtual void execute (); +private: + u32 m_last_teleport_started; + enum { + ACTION_IDLE, + ACTION_SHIELD_STARTED, + ACTION_WAIT_TRIPLE_END, + ACTION_COMPLETED, + } m_action; + //Fvector position; +}; + +#include "ghostboss_state_attack_teleport_inline.h" diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_teleport_inline.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_teleport_inline.h new file mode 100644 index 000000000..e9a2e4366 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_attack_teleport_inline.h @@ -0,0 +1,102 @@ +#pragma once + +#include "../../../level.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateGhostBossAttackTeleportAbstract CStateGhostBossTeleport<_Object> + +TEMPLATE_SPECIALIZATION + +CStateGhostBossAttackTeleportAbstract::CStateGhostBossTeleport(_Object *obj) : inherited(obj) +{ + m_last_teleport_started = 0; + m_action = ACTION_IDLE; +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleportAbstract::initialize() +{ + inherited::initialize (); + + m_last_teleport_started = Device.dwTimeGlobal; + m_action = ACTION_IDLE; +// m_shield_start_anim_length_sec = object->anim().motion_time(eAnimShieldStart, 0, object->Visual()); +} + + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleportAbstract::finalize() +{ + inherited::finalize(); + + object->DeactiveTeleport(); + object->set_script_capture(true); +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleportAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + object->DeactiveTeleport(); + object->set_script_capture(false); + +} + +TEMPLATE_SPECIALIZATION +void CStateGhostBossAttackTeleportAbstract::execute() +{ + switch (m_action) + { + case ACTION_IDLE: + object->ActivateTeleport(); + //object->com_man().ta_activate(object->anim_triple_shield); + m_action = ACTION_COMPLETED; + break; + case ACTION_COMPLETED: + break; + } + object->set_action(ACT_STAND_IDLE); +/* + if (object->EnemyMan.get_enemy()) + object->dir().face_target(object->EnemyMan.get_enemy(), 500); + else { + Fvector pos; + pos.mad(object->Position(), Fvector().set(0.1f,0.0f,0.0f)); + object->dir().face_target(pos, 500); + } + object->set_action(ACT_STAND_IDLE);*/ +} + + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackTeleportAbstract::check_start_conditions() +{ + if (Device.dwTimeGlobal > m_last_teleport_started + object->m_teleport_length) { + if (object->EnemyMan.get_enemy() && !object->EnemyMan.enemy_see_me_now()) return false; + } else { + return false; + } + + if (!object->CanTeleport()) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateGhostBossAttackTeleportAbstract::check_completion() +{ + if (Device.dwTimeGlobal <= m_last_teleport_started + object->m_teleport_length) + { + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + if ((enemy && enemy != Actor()) || !Actor()->IsReloadingWeapon()) + return enemy == 0; + } + return true; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateGhostBossShieldAbstract diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_manager.cpp b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_manager.cpp new file mode 100644 index 000000000..d6cb5fccf --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_manager.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "ghostboss.h" +#include "ghostboss_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../states/state_custom_action.h" + +#include "GhostBoss_state_attack.h" + + +CStateManagerGhostBoss::CStateManagerGhostBoss(CGhostBoss *monster) : inherited(monster) +{ + add_state(eStateRest, new CStateMonsterRest(monster)); + add_state(eStatePanic, new CStateMonsterPanic(monster)); + add_state(eStateAttack, new CStateGhostBossAttack(monster)); + add_state(eStateEat, new CStateMonsterEat(monster)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(monster)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(monster)); + add_state(eStateHitted, new CStateMonsterHitted(monster)); + add_state(eStateGhostBossScanning, new CStateMonsterCustomAction(monster)); + add_state(eStateGhostBossAttack_Shield, new CStateGhostBossShield(monster)); + add_state(eStateGhostBossAttack_Teleport, new CStateGhostBossTeleport(monster)); + m_last_health = 1.f; +} + +#define SCAN_STATE_TIME 4000 + +void CStateManagerGhostBoss::execute() +{ + u32 state = u32(-1); + + bool lost_health = false; + if (m_last_health - 0.01 > object->GetfHealth()) + { + m_last_health = object->GetfHealth(); + lost_health = true; + } + + if (current_substate == eStateGhostBossAttack_Teleport && check_state(eStateGhostBossAttack_Teleport)) { + state = eStateGhostBossAttack_Teleport; + } else if (current_substate == eStateGhostBossAttack_Shield && check_state(eStateGhostBossAttack_Shield)) + { + state = eStateGhostBossAttack_Shield; + } + else if (lost_health) { + if (object->CanTeleport()) + state = eStateGhostBossAttack_Teleport; + if (state==u32(-1) && check_state(eStateGhostBossAttack_Shield)) + state = eStateGhostBossAttack_Shield; + } + else if (object->EnemyMan.get_enemy()) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state = eStatePanic; break; + case eWeak: state = eStateAttack; break; + } + } else if (object->HitMemory.is_hit() && (object->HitMemory.get_last_hit_time() + 10000 > Device.dwTimeGlobal)) { + state = eStateHitted; + } else if (object->hear_dangerous_sound || object->hear_interesting_sound) { + state = eStateHearInterestingSound; + } else if (object->time_last_scan + SCAN_STATE_TIME > Device.dwTimeGlobal){ + state = eStateGhostBossScanning; + } else if (can_eat()) { + state = eStateEat; + } else state = eStateRest; + + select_state(state); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} + +void CStateManagerGhostBoss::setup_substates() +{ + if (current_substate == eStateGhostBossScanning) { + SStateDataAction data; + + data.action = ACT_LOOK_AROUND; + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + + get_state_current()->fill_data_with(&data, sizeof(SStateDataAction)); + return; + } +} diff --git a/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_manager.h b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_manager.h new file mode 100644 index 000000000..3ef848053 --- /dev/null +++ b/src/xrGameLA/ai/monsters/ghostboss/ghostboss_state_manager.h @@ -0,0 +1,16 @@ +#pragma once +#include "../monster_state_manager.h" + +class CGhostBoss; + +class CStateManagerGhostBoss : public CMonsterStateManager { + typedef CMonsterStateManager inherited; +public: + CStateManagerGhostBoss (CGhostBoss *monster); + virtual void execute (); + virtual void setup_substates (); + +private: + float m_last_health; +}; + diff --git a/src/xrGameLA/ai/monsters/invisibility.cpp b/src/xrGameLA/ai/monsters/invisibility.cpp new file mode 100644 index 000000000..d48af0117 --- /dev/null +++ b/src/xrGameLA/ai/monsters/invisibility.cpp @@ -0,0 +1,105 @@ +#include "stdafx.h" +#include "invisibility.h" + +void CInvisibility::reinit() +{ + m_time_start_blink = 0; + m_cur_visibility = false; + m_blink = false; + m_time_last_blink = 0; + + set_manual_control (false); + + m_active = false; + m_energy = 0.f; +} + +void CInvisibility::reload(LPCSTR section) +{ + timeBlink = pSettings->r_u32(section,"Invisibility_BlinkTime"); + timeBlinkInterval = pSettings->r_u32(section,"Invisibility_BlinkMicroInterval"); + m_speed = pSettings->r_float(section,"Invisibility_EnergySpeed"); +} + +void CInvisibility::activate() +{ + if (m_active) return; + start_blink(); + + m_active = true; + on_activate(); +} + +void CInvisibility::deactivate() +{ + if (!m_active) return; + start_blink(); + + m_active = false; + on_deactivate(); +} + +void CInvisibility::start_blink() +{ + m_blink = true; + m_time_start_blink = Device.dwTimeGlobal; + m_time_last_blink = 0; +} + +void CInvisibility::stop_blink() +{ + m_blink = false; + m_cur_visibility = !active(); + + on_change_visibility(m_cur_visibility); +} + +void CInvisibility::update_blink() +{ + if (!m_blink) return; + + u32 cur_time = Device.dwTimeGlobal; + + // check for whole blink time + if (m_time_start_blink + timeBlink < cur_time) { + stop_blink(); + return; + } + + // check for current blink interval time + if (m_time_last_blink + timeBlinkInterval < cur_time) { + // blink + m_time_last_blink = cur_time; + m_cur_visibility = !m_cur_visibility; + + on_change_visibility(m_cur_visibility); + } +} + +void CInvisibility::frame_update() +{ + update_blink(); + + if (!m_manual) { + if (m_active) m_energy -= m_speed * Device.fTimeDelta; + else m_energy += m_speed * Device.fTimeDelta; + clamp (m_energy,0.f,1.f); + } +} + +void CInvisibility::set_manual_control(bool b_man) +{ + m_manual = b_man; +} + +void CInvisibility::manual_activate() +{ + if (m_manual) + activate (); +} + +void CInvisibility::manual_deactivate() +{ + if (m_manual) + deactivate (); +} diff --git a/src/xrGameLA/ai/monsters/invisibility.h b/src/xrGameLA/ai/monsters/invisibility.h new file mode 100644 index 000000000..f44fb8592 --- /dev/null +++ b/src/xrGameLA/ai/monsters/invisibility.h @@ -0,0 +1,49 @@ +#pragma once + +class CInvisibility { + + u32 m_time_start_blink; + u32 m_time_last_blink; + bool m_blink; + bool m_cur_visibility; + + // external parameters + u32 timeBlink; + u32 timeBlinkInterval; + + bool m_manual; + + bool m_active; // + float m_energy; // [0..1] + float m_speed; // energy change speed (external) + +protected: + virtual void reload (LPCSTR section); + virtual void reinit (); + virtual void frame_update (); + + virtual void on_change_visibility (bool b_visibility){} + virtual void on_activate (){} + virtual void on_deactivate (){} + +public: + void activate (); + void deactivate (); + IC float energy () {return m_energy;} + IC bool active () {return m_active;} + IC bool full_energy () {return !!fsimilar(m_energy,1.f);} + + // manual switching + void set_manual_control (bool b_man = true); + void manual_activate (); + void manual_deactivate (); + bool is_manual_control () {return m_manual;} + +private: + void start_blink (); + void stop_blink (); + void update_blink (); +}; + + + diff --git a/src/xrGameLA/ai/monsters/karlik/karlik.cpp b/src/xrGameLA/ai/monsters/karlik/karlik.cpp new file mode 100644 index 000000000..47aa97d08 --- /dev/null +++ b/src/xrGameLA/ai/monsters/karlik/karlik.cpp @@ -0,0 +1,95 @@ +#include "stdafx.h" +#include "karlik.h" +#include "karlik_state_manager.h" +#include "../monster_velocity_space.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" + + +CKarlik::CKarlik() +{ + StateMan = new CStateManagerKarlik(this); +} + +CKarlik::~CKarlik() +{ + xr_delete(StateMan); +} + +void CKarlik::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + //SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandDamaged, "stand_idle_dmg_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND); + anim().AddAnim(eAnimRun, "stand_run_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimDie, "stand_die_", 0, &velocity_none, PS_STAND); + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSteal, "stand_crawl_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimLookAround, "stand_look_around_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimEat, "sit_eat_", -1, &velocity_none, PS_SIT); + + anim().AddTransition(PS_STAND, PS_SIT, eAnimStandSitDown, false); + anim().AddTransition(PS_SIT, PS_STAND, eAnimSitStandUp, false); + + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSitIdle); + anim().LinkAction(ACT_REST, eAnimSitIdle); + anim().LinkAction(ACT_DRAG, eAnimStandIdle); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimLookAround); + + m_fDeltaPower = pSettings->r_float(section,"MissPowerOnHit"); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +void CKarlik::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_CHECK_CORPSE) == ASP_CHECK_CORPSE) { + com_man().seq_run(anim().get_motion_id(eAnimCheckCorpse)); + } + + if ((spec_params & ASP_STAND_SCARED) == ASP_STAND_SCARED) { + anim().SetCurAnim(eAnimLookAround); + return; + } +} + + diff --git a/src/xrGameLA/ai/monsters/karlik/karlik.h b/src/xrGameLA/ai/monsters/karlik/karlik.h new file mode 100644 index 000000000..4871829b1 --- /dev/null +++ b/src/xrGameLA/ai/monsters/karlik/karlik.h @@ -0,0 +1,28 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../../../script_export_space.h" + +class CStateManagerKarlik; + +class CKarlik : public CBaseMonster { + typedef CBaseMonster inherited; + +public: + CKarlik (); + virtual ~CKarlik (); + + virtual void Load (LPCSTR section); + virtual void CheckSpecParams (u32 spec_params); + float GetDeltaPower () {return m_fDeltaPower;} + + +private: + float m_fDeltaPower; + +public: + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CKarlik) +#undef script_type_list +#define script_type_list save_type_list(CKarlik) diff --git a/src/xrGameLA/ai/monsters/karlik/karlik_script.cpp b/src/xrGameLA/ai/monsters/karlik/karlik_script.cpp new file mode 100644 index 000000000..ad4ceb796 --- /dev/null +++ b/src/xrGameLA/ai/monsters/karlik/karlik_script.cpp @@ -0,0 +1,15 @@ +#include "stdafx.h" +#include "../../../pch_script.h" +#include "karlik.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CKarlik::script_register(lua_State *L) +{ + module(L) + [ + class_("CKarlik") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/karlik/karlik_state_manager.cpp b/src/xrGameLA/ai/monsters/karlik/karlik_state_manager.cpp new file mode 100644 index 000000000..31262b68b --- /dev/null +++ b/src/xrGameLA/ai/monsters/karlik/karlik_state_manager.cpp @@ -0,0 +1,67 @@ +#include "stdafx.h" +#include "karlik.h" +#include "karlik_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../states/monster_state_hear_int_sound.h" + +#include "../../../entitycondition.h" + +CStateManagerKarlik::CStateManagerKarlik(CKarlik *obj) : inherited(obj) +{ + add_state(eStateRest, new CStateMonsterRest(obj)); + add_state(eStateAttack, new CStateMonsterAttack(obj)); + add_state(eStateEat, new CStateMonsterEat(obj)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(obj)); + add_state(eStatePanic, new CStateMonsterPanic(obj)); + add_state(eStateHitted, new CStateMonsterHitted(obj)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(obj)); +} + +CStateManagerKarlik::~CStateManagerKarlik() +{ +} + +void CStateManagerKarlik::execute() +{ + u32 state_id = u32(-1); + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else { + if (can_eat()) state_id = eStateEat; + else { + // Rest & Idle states here + state_id = eStateRest; + } + } + + // установить текущее состояние + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} + diff --git a/src/xrGameLA/ai/monsters/karlik/karlik_state_manager.h b/src/xrGameLA/ai/monsters/karlik/karlik_state_manager.h new file mode 100644 index 000000000..f7a54f9b2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/karlik/karlik_state_manager.h @@ -0,0 +1,17 @@ +#pragma once +#include "../monster_state_manager.h" + +//class CBurer; + +class CStateManagerKarlik : public CMonsterStateManager { + typedef CMonsterStateManager inherited; +public: + CStateManagerKarlik (CKarlik *monster); + virtual ~CStateManagerKarlik (); + + virtual void execute (); + +//private: + //float m_last_health; +}; + diff --git a/src/xrGameLA/ai/monsters/melee_checker.cpp b/src/xrGameLA/ai/monsters/melee_checker.cpp new file mode 100644 index 000000000..a0513dfd5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/melee_checker.cpp @@ -0,0 +1,54 @@ +#include "stdafx.h" +#include "melee_checker.h" +#include "basemonster/base_monster.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../xr_collide_form.h" + +#define MAX_TRACE_ENEMY_RANGE 6.f + +float CMeleeChecker::distance_to_enemy(const CEntity *enemy) +{ + float dist = enemy->Position().distance_to (m_object->Position()); + if (dist > MAX_TRACE_ENEMY_RANGE) return dist; + + Fvector enemy_center; + enemy->Center (enemy_center); + + Fvector my_head_pos = get_head_position(m_object); + + Fvector dir; + dir.sub (enemy_center, my_head_pos); + dir.normalize_safe (); + + collide::ray_defs r_query (my_head_pos, dir, MAX_TRACE_ENEMY_RANGE, CDB::OPT_CULL | CDB::OPT_ONLYNEAREST, collide::rqtObject); + r_res.r_clear (); + + if (m_object->CFORM()->_RayQuery(r_query, r_res)) { + if (r_res.r_begin()->O == enemy) + dist = r_res.r_begin()->range; + } + + return (dist); +} + +void CMeleeChecker::on_hit_attempt(bool hit_success) +{ + // добавить новый элемент в стек + for (u32 i=HIT_STACK_SIZE-1; i > 0; i--) m_hit_stack[i] = m_hit_stack[i-1]; + m_hit_stack[0] = hit_success; + + // проверить однородность стека + bool stack_similar = true; + for (u32 i=1; i < HIT_STACK_SIZE; i++) if (m_hit_stack[i] != hit_success) {stack_similar = false; break;} + + if (!stack_similar) return; + + // обновить m_current_min_distance + if (hit_success) { + if (m_current_min_distance + m_as_step < m_min_attack_distance) m_current_min_distance += m_as_step; + else m_current_min_distance = m_min_attack_distance; + } else { + if (m_current_min_distance > m_as_min_dist + m_as_step) m_current_min_distance -= m_as_step; + else m_current_min_distance = m_as_min_dist; + } +} diff --git a/src/xrGameLA/ai/monsters/melee_checker.h b/src/xrGameLA/ai/monsters/melee_checker.h new file mode 100644 index 000000000..a4c7fc074 --- /dev/null +++ b/src/xrGameLA/ai/monsters/melee_checker.h @@ -0,0 +1,49 @@ +#pragma once +class CBaseMonster; +class CEntity; + +#define HIT_STACK_SIZE 2 + +class CMeleeChecker { +private: + collide::rq_results r_res; + +private: + CBaseMonster *m_object; + + // ltx parameters + float m_min_attack_distance; + float m_max_attack_distance; + float m_as_min_dist; + float m_as_step; + + bool m_hit_stack[HIT_STACK_SIZE]; + + float m_current_min_distance; + +public: + void init_external (CBaseMonster *obj) {m_object = obj;} + IC void load (LPCSTR section); + + // инициализировано состояние атаки + IC void init_attack (); + void on_hit_attempt (bool hit_success); + + // Получить расстояние от fire_bone до врага + // Выполнить RayQuery от fire_bone в enemy.center + float distance_to_enemy (const CEntity *enemy); + + IC float get_min_distance (); + IC float get_max_distance (); + + IC bool can_start_melee (const CEntity *enemy); + IC bool should_stop_melee (const CEntity *enemy); + +#ifdef DEBUG + IC float dbg_as_min_dist (){return m_as_min_dist;} + IC float dbg_as_step (){return m_as_step;} +#endif + +}; + +#include "melee_checker_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/melee_checker_inline.h b/src/xrGameLA/ai/monsters/melee_checker_inline.h new file mode 100644 index 000000000..8b157d824 --- /dev/null +++ b/src/xrGameLA/ai/monsters/melee_checker_inline.h @@ -0,0 +1,35 @@ +#pragma once + +IC void CMeleeChecker::load(LPCSTR section) +{ + m_as_min_dist = pSettings->r_float(section,"as_min_dist"); + m_as_step = pSettings->r_float(section,"as_step"); + + m_min_attack_distance = pSettings->r_float(section,"MinAttackDist"); + m_max_attack_distance = pSettings->r_float(section,"MaxAttackDist"); +} + +IC void CMeleeChecker::init_attack() +{ + // инициализировать стек + for (u32 i=0; i < HIT_STACK_SIZE; i++) m_hit_stack[i] = true; + + m_current_min_distance = m_min_attack_distance; +} +IC float CMeleeChecker::get_min_distance() +{ + return m_current_min_distance; +} +IC float CMeleeChecker::get_max_distance() +{ + return (m_max_attack_distance - (m_min_attack_distance - m_current_min_distance)); +} +IC bool CMeleeChecker::can_start_melee(const CEntity *enemy) +{ + return (distance_to_enemy(enemy) < get_min_distance()); +} +IC bool CMeleeChecker::should_stop_melee(const CEntity *enemy) +{ + return (distance_to_enemy(enemy) > get_max_distance()); +} + diff --git a/src/xrGameLA/ai/monsters/monster_corpse_manager.cpp b/src/xrGameLA/ai/monsters/monster_corpse_manager.cpp new file mode 100644 index 000000000..1aaf6c4ab --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_corpse_manager.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "monster_corpse_manager.h" +#include "BaseMonster/base_monster.h" +#include "../../ai_object_location.h" + +CMonsterCorpseManager::CMonsterCorpseManager() +{ + monster = 0; + corpse = 0; + forced = false; +} + +CMonsterCorpseManager::~CMonsterCorpseManager() +{ + +} +void CMonsterCorpseManager::init_external(CBaseMonster *M) +{ + monster = M; +} + +void CMonsterCorpseManager::update() +{ + if (forced) { + if (corpse->m_fFood < 1) { + corpse = 0; + return; + } + } else { + corpse = monster->CorpseMemory.get_corpse(); + + if (corpse) { + SMonsterCorpse corpse_info = monster->CorpseMemory.get_corpse_info(); + position = corpse_info.position; + vertex = corpse_info.vertex; + time_last_seen = corpse_info.time; + } + } +} + +void CMonsterCorpseManager::force_corpse(const CEntityAlive *corpse) +{ + this->corpse = corpse; + position = corpse->Position(); + vertex = corpse->ai_location().level_vertex_id(); + time_last_seen = Device.dwTimeGlobal; + + forced = true; +} + +void CMonsterCorpseManager::unforce_corpse() +{ + corpse = monster->CorpseMemory.get_corpse(); + + if (corpse) { + SMonsterCorpse corpse_info = monster->CorpseMemory.get_corpse_info(); + position = corpse_info.position; + vertex = corpse_info.vertex; + time_last_seen = corpse_info.time; + } + + forced = false; +} + +void CMonsterCorpseManager::reinit() +{ + corpse = 0; + forced = false; + time_last_seen = 0; +} + diff --git a/src/xrGameLA/ai/monsters/monster_corpse_manager.h b/src/xrGameLA/ai/monsters/monster_corpse_manager.h new file mode 100644 index 000000000..06f920b0f --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_corpse_manager.h @@ -0,0 +1,32 @@ +#pragma once +#include "ai_monster_defs.h" + +class CBaseMonster; + +class CMonsterCorpseManager { + CBaseMonster *monster; + + const CEntityAlive *corpse; + Fvector position; + u32 vertex; + TTime time_last_seen; + + bool forced; + +public: + CMonsterCorpseManager (); + ~CMonsterCorpseManager (); + void init_external (CBaseMonster *M); + + void update (); + + void force_corpse (const CEntityAlive *corpse); + void unforce_corpse (); + + const CEntityAlive *get_corpse () {return corpse;} + const Fvector &get_corpse_position () {return position;} + u32 get_corpse_vertex () {return vertex;} + TTime get_corpse_time_last_seen() {return time_last_seen;} + + void reinit (); +}; diff --git a/src/xrGameLA/ai/monsters/monster_corpse_memory.cpp b/src/xrGameLA/ai/monsters/monster_corpse_memory.cpp new file mode 100644 index 000000000..28be00973 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_corpse_memory.cpp @@ -0,0 +1,117 @@ +#include "stdafx.h" +#include "monster_corpse_memory.h" +#include "BaseMonster/base_monster.h" +#include "../../memory_manager.h" +#include "../../visual_memory_manager.h" +#include "../../item_manager.h" +#include "../../ai_object_location.h" + +CMonsterCorpseMemory::CMonsterCorpseMemory() +{ + monster = 0; + time_memory = 10000; +} + +CMonsterCorpseMemory::~CMonsterCorpseMemory() +{ +} + +void CMonsterCorpseMemory::init_external(CBaseMonster *M, TTime mem_time) +{ + monster = M; + time_memory = mem_time; +} + + +void CMonsterCorpseMemory::update() +{ + for (xr_vector::const_iterator I = monster->memory().item().objects().begin(); I != monster->memory().item().objects().end(); ++I) { + if (monster->memory().visual().visible_now(*I)) { + const CEntityAlive *p_corpse = smart_cast(*I); + if (!p_corpse || p_corpse->g_Alive()) continue; + add_corpse(p_corpse); + } + } + + // удалить устаревших врагов + remove_non_actual(); +} + +void CMonsterCorpseMemory::add_corpse(const CEntityAlive *corpse) +{ + SMonsterCorpse corpse_info; + corpse_info.position = corpse->Position(); + corpse_info.vertex = corpse->ai_location().level_vertex_id(); + corpse_info.time = Device.dwTimeGlobal; + + CORPSE_MAP_IT it = m_objects.find(corpse); + if (it != m_objects.end()) { + // обновить данные о враге + it->second = corpse_info; + } else { + // добавить врага в список объектов + m_objects.insert(mk_pair(corpse, corpse_info)); + } +} + +void CMonsterCorpseMemory::remove_non_actual() +{ + TTime cur_time = Device.dwTimeGlobal; + + // удалить 'старых' врагов и тех, расстояние до которых > 30м и др. + for (CORPSE_MAP_IT it = m_objects.begin(), nit; it != m_objects.end(); it = nit) + { + nit = it; ++nit; + // проверить условия удаления + if (!it->first || + it->first->g_Alive() || + it->first->getDestroy() || + (it->second.time + time_memory < cur_time) || + (it->first->m_fFood < 1) + ) + m_objects.erase (it); + } +} + +const CEntityAlive *CMonsterCorpseMemory::get_corpse() +{ + CORPSE_MAP_IT it = find_best_corpse(); + if (it != m_objects.end()) return it->first; + return (0); +} + +SMonsterCorpse CMonsterCorpseMemory::get_corpse_info() +{ + SMonsterCorpse ret_val; + ret_val.time = 0; + + CORPSE_MAP_IT it = find_best_corpse(); + if (it != m_objects.end()) ret_val = it->second; + + return ret_val; +} + +CORPSE_MAP_IT CMonsterCorpseMemory::find_best_corpse() +{ + CORPSE_MAP_IT it = m_objects.end(); + float min_dist = flt_max; + + for (CORPSE_MAP_IT I = m_objects.begin(); I != m_objects.end(); I++) { + if (I->second.position.distance_to(monster->Position()) < min_dist) { + min_dist = I->second.position.distance_to(monster->Position()); + it = I; + } + } + + return it; +} + +void CMonsterCorpseMemory::remove_links(CObject *O) +{ + for (CORPSE_MAP_IT I = m_objects.begin();I!=m_objects.end();++I) { + if ((*I).first == O) { + m_objects.erase(I); + break; + } + } +} diff --git a/src/xrGameLA/ai/monsters/monster_corpse_memory.h b/src/xrGameLA/ai/monsters/monster_corpse_memory.h new file mode 100644 index 000000000..ca8bb378c --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_corpse_memory.h @@ -0,0 +1,34 @@ +#pragma once +#include "ai_monster_defs.h" + +class CBaseMonster; + +class CMonsterCorpseMemory { + CBaseMonster *monster; + TTime time_memory; + + CORPSE_MAP m_objects; + +public: + CMonsterCorpseMemory (); + ~CMonsterCorpseMemory (); + + void init_external (CBaseMonster *M, TTime mem_time); + void update (); + + // ----------------------------------------------------- + const CEntityAlive *get_corpse (); + SMonsterCorpse get_corpse_info (); + u32 get_corpse_count () {return m_objects.size();} + + void clear () {m_objects.clear();} + void remove_links (CObject *O); + + void add_corpse (const CEntityAlive *corpse); + +private: + void remove_non_actual (); + + CORPSE_MAP_IT find_best_corpse (); + +}; diff --git a/src/xrGameLA/ai/monsters/monster_cover_manager.cpp b/src/xrGameLA/ai/monsters/monster_cover_manager.cpp new file mode 100644 index 000000000..fff47f9d3 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_cover_manager.cpp @@ -0,0 +1,234 @@ +#include "stdafx.h" +#include "monster_cover_manager.h" +#include "BaseMonster/base_monster.h" +#include "../../cover_evaluators.h" +#include "../../cover_point.h" +#include "../../ai_space.h" +#include "../../level_graph.h" +#include "../../game_graph.h" +#include "../../game_level_cross_table.h" +#include "../../level.h" +#include "../../level_debug.h" +#include "../../cover_manager.h" +#include "../../ai_object_location.h" +#include "ai_monster_squad.h" +#include "ai_monster_squad_manager.h" +#include + +////////////////////////////////////////////////////////////////////////// +// CControllerCoverEvaluator +class CCoverEvaluator : public CCoverEvaluatorBase { + typedef CCoverEvaluatorBase inherited; + + Fvector m_dest_position; + float m_min_distance; + float m_max_distance; + float m_current_distance; + float m_deviation; + float m_best_distance; + + CBaseMonster *m_object; + +public: + CCoverEvaluator (CRestrictedObject *object); + + // setup by cover_manager + void initialize (const Fvector &start_position); + + // manual setup + void setup (CBaseMonster *object, const Fvector &position, float min_pos_distance, float max_pos_distance, float deviation = 0.f); + + void evaluate (const CCoverPoint *cover_point, float weight); +}; + +////////////////////////////////////////////////////////////////////////// +// CControllerCoverPredicate +class CCoverPredicate { +public: + // setup internals here + CCoverPredicate (); + // called from cover_manager for every cover (for suitable cover) + bool operator() (const CCoverPoint *cover) const + { + return true; + } + // must return a value that is transfered to cover evaluator + float weight (const CCoverPoint *cover) const + { + return 1.f; + } + + void finalize (const CCoverPoint *cover) const + { + } +}; + + +////////////////////////////////////////////////////////////////////////// +// CCoverEvaluator Implementation +////////////////////////////////////////////////////////////////////////// + +CCoverEvaluator::CCoverEvaluator(CRestrictedObject *object) : inherited(object) +{ + m_dest_position.set (flt_max,flt_max,flt_max); + m_deviation = flt_max; + m_min_distance = flt_max; + m_max_distance = flt_max; + m_best_distance = flt_max; + m_current_distance = flt_max; +} + +void CCoverEvaluator::setup(CBaseMonster *object, const Fvector &position, float min_pos_distance, float max_pos_distance, float deviation) +{ + inherited::setup(); + + m_object = object; + + m_dest_position = position; + + m_actuality = m_actuality && fsimilar(m_deviation,deviation); + m_deviation = deviation; + + m_actuality = m_actuality && fsimilar(m_min_distance,min_pos_distance); + m_min_distance = min_pos_distance; + + m_actuality = m_actuality && fsimilar(m_max_distance,max_pos_distance); + m_max_distance = max_pos_distance; + +} + +void CCoverEvaluator::initialize(const Fvector &start_position) +{ + inherited::initialize (start_position); + m_current_distance = m_start_position.distance_to(m_dest_position); +#ifdef DEBUG + DBG().level_info(this).clear(); +#endif +} + + +void CCoverEvaluator::evaluate(const CCoverPoint *cover_point, float weight) +{ +#ifdef DEBUG + //DBG().level_info(this).add_item(cover_point->position(), D3DCOLOR_XRGB(0,255,0)); +#endif + CMonsterSquad *squad = monster_squad().get_squad(m_object); + if (squad->is_locked_cover(cover_point->level_vertex_id())) return; + + if (fis_zero(weight)) + return; + + float dest_distance = m_dest_position.distance_to(cover_point->position()); + + if ((dest_distance <= m_min_distance) && (m_current_distance > dest_distance)) + return; + + if((dest_distance >= m_max_distance) && (m_current_distance < dest_distance)) + return; + + Fvector direction; + float y,p; + direction.sub (m_dest_position,cover_point->position()); + direction.getHP (y,p); + + float cover_value = ai().level_graph().cover_in_direction(y,cover_point->level_vertex_id()); + float value = cover_value; + if (ai().level_graph().neighbour_in_direction(direction,cover_point->level_vertex_id())) + value += 10.f; + + value /= weight; + + if (value >= m_best_value) + return; + + m_selected = cover_point; + m_best_value = value; +} + + +//============================================================================= +// Cover Manager +//============================================================================= + +CMonsterCoverManager::CMonsterCoverManager(CBaseMonster *monster) : m_object(monster) +{ + m_ce_best = 0; +} +CMonsterCoverManager::~CMonsterCoverManager() +{ + xr_delete(m_ce_best); +} + +void CMonsterCoverManager::load() +{ + m_ce_best = new CCoverEvaluator(&(m_object->control().path_builder().restrictions())); +} + +const CCoverPoint *CMonsterCoverManager::find_cover(const Fvector &position, float min_pos_distance, float max_pos_distance, float deviation) +{ + m_ce_best->setup (m_object, position,min_pos_distance,max_pos_distance,deviation); + const CCoverPoint *point = ai().cover_manager().best_cover(m_object->Position(),30.f,*m_ce_best); + + return point; +} + +// найти лучший ковер относительно "position" +const CCoverPoint *CMonsterCoverManager::find_cover(const Fvector &src_pos, const Fvector &dest_pos, float min_pos_distance, float max_pos_distance, float deviation) +{ + m_ce_best->setup (m_object, dest_pos, min_pos_distance,max_pos_distance,deviation); + const CCoverPoint *point = ai().cover_manager().best_cover(src_pos,30.f,*m_ce_best); + return point; +} + +////////////////////////////////////////////////////////////////////////// +// Find Less Cover Direction (e.g. look at the most open place) +////////////////////////////////////////////////////////////////////////// + +#define ANGLE_DISP PI_DIV_2 +#define ANGLE_DISP_STEP deg(10) +#define TRACE_STATIC_DIST 3.f + +void CMonsterCoverManager::less_cover_direction(Fvector &dir) +{ + float angle = ai().level_graph().vertex_cover_angle(m_object->ai_location().level_vertex_id(),deg(10), ::std::greater()); + + collide::rq_result l_rq; + + float angle_from = angle_normalize(angle - ANGLE_DISP); + float angle_to = angle_normalize(angle + ANGLE_DISP); + + Fvector trace_from; + m_object->Center (trace_from); + Fvector direction; + + // trace discretely left + for (float ang = angle; angle_difference(ang, angle) < ANGLE_DISP; ang = angle_normalize(ang - ANGLE_DISP_STEP)) { + + direction.setHP (ang, 0.f); + + if (Level().ObjectSpace.RayPick(trace_from, direction, TRACE_STATIC_DIST, collide::rqtStatic, l_rq,m_object)) { + if ((l_rq.range < TRACE_STATIC_DIST)) { + angle_from = ang; + break; + } + } + } + + // trace discretely right + for (float ang = angle; angle_difference(ang, angle) < ANGLE_DISP; ang = angle_normalize(ang + ANGLE_DISP_STEP)) { + + direction.setHP (ang, 0.f); + + if (Level().ObjectSpace.RayPick(trace_from, direction, TRACE_STATIC_DIST, collide::rqtStatic, l_rq,m_object)) { + if ((l_rq.range < TRACE_STATIC_DIST)) { + angle_to = ang; + break; + } + } + } + + angle = angle_normalize(angle_from + angle_difference(angle_from,angle_to) / 2); + dir.setHP (angle,0.f); +} +////////////////////////////////////////////////////////////////////////// + diff --git a/src/xrGameLA/ai/monsters/monster_cover_manager.h b/src/xrGameLA/ai/monsters/monster_cover_manager.h new file mode 100644 index 000000000..9d18cfeab --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_cover_manager.h @@ -0,0 +1,23 @@ +#pragma once + +class CCoverPoint; +class CCoverEvaluator; +class CBaseMonster; + +class CMonsterCoverManager { + CBaseMonster *m_object; + CCoverEvaluator *m_ce_best; + +public: + CMonsterCoverManager (CBaseMonster *monster); + ~CMonsterCoverManager (); + + void load (); + // найти лучший ковер относительно "position" + const CCoverPoint *find_cover (const Fvector &position, float min_pos_distance, float max_pos_distance, float deviation = 0.f); + + // найти лучший ковер относительно "src_pos" и "dest_pos" + const CCoverPoint *find_cover (const Fvector &src_pos, const Fvector &dest_pos, float min_pos_distance, float max_pos_distance, float deviation = 0.f); + + void less_cover_direction (Fvector &dir); +}; \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/monster_enemy_manager.cpp b/src/xrGameLA/ai/monsters/monster_enemy_manager.cpp new file mode 100644 index 000000000..293ec6cb9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_enemy_manager.cpp @@ -0,0 +1,251 @@ +#include "stdafx.h" +#include "monster_enemy_manager.h" +#include "BaseMonster/base_monster.h" +#include "../ai_monsters_misc.h" +#include "../../ai_object_location.h" +#include "../../memory_manager.h" +#include "../../visual_memory_manager.h" +#include "../../actor.h" +#include "../../actor_memory.h" + +CMonsterEnemyManager::CMonsterEnemyManager() +{ + monster = 0; + enemy = 0; + flags.zero (); + forced = false; + prev_enemy = 0; + danger_type = eNone; + my_vertex_enemy_last_seen = u32(-1); + enemy_vertex_enemy_last_seen = u32(-1); + m_time_updated = 0; + m_time_start_see_enemy = 0; +} + +CMonsterEnemyManager::~CMonsterEnemyManager() +{ + +} +void CMonsterEnemyManager::init_external(CBaseMonster *M) +{ + monster = M; +} + + +void CMonsterEnemyManager::update() +{ + if (forced) { + // проверить валидность force-объекта + if (!enemy || enemy->getDestroy() || !enemy->g_Alive()) { + enemy = 0; + return; + } + } else { + enemy = monster->EnemyMemory.get_enemy(); + + if (enemy) { + SMonsterEnemy enemy_info = monster->EnemyMemory.get_enemy_info(); + position = enemy_info.position; + vertex = enemy_info.vertex; + time_last_seen = enemy_info.time; + } + } + + if (!enemy) { + return; + } + + // обновить информацию о враге в соответствии со звуковой информацией + if (monster->SoundMemory.IsRememberSound()) { + SoundElem sound_elem; + if (monster->SoundMemory.get_sound_from_object (enemy, sound_elem)) { + if (sound_elem.time > time_last_seen) { + position = sound_elem.position; + vertex = u32(-1); + time_last_seen = sound_elem.time; + } + } + } + + // проверить видимость + enemy_see_me = is_faced(enemy, monster); + + // обновить опасность врага + danger_type = eNone; + + switch (dwfChooseAction(0, monster->panic_threshold(), 0.f, 0.f, 0.f, monster->g_Team(),monster->g_Squad(),monster->g_Group(),0,1,2,3,4, monster, 30.f)) { + case 4 : + case 3 : + case 2 : + case 1 : danger_type = eStrong; break; + case 0 : danger_type = eWeak; break; + } + + // обновить флаги + flags.zero(); + + if ((prev_enemy == enemy) && (time_last_seen != Device.dwTimeGlobal)) flags.or(FLAG_ENEMY_LOST_SIGHT); + if (prev_enemy && !prev_enemy->g_Alive()) flags.or(FLAG_ENEMY_DIE); + if (!enemy_see_me) flags.or(FLAG_ENEMY_DOESNT_SEE_ME); + + float dist_now, dist_prev; + if (prev_enemy == enemy) { + dist_now = position.distance_to(monster->Position()); + dist_prev = prev_enemy_position.distance_to(monster->Position()); + + if (_abs(dist_now - dist_prev) < 0.2f) flags.or(FLAG_ENEMY_STANDING); + else { + if (dist_now < dist_prev) flags.or(FLAG_ENEMY_GO_CLOSER); + else flags.or(FLAG_ENEMY_GO_FARTHER); + + if (_abs(dist_now - dist_prev) < 1.2f) { + if (dist_now < dist_prev) flags.or(FLAG_ENEMY_GO_CLOSER_FAST); + else flags.or(FLAG_ENEMY_GO_FARTHER_FAST); + } + } + + if (flags.is(FLAG_ENEMY_STANDING) && flags.is(FLAG_ENEMY_DOESNT_SEE_ME)) flags.or(FLAG_ENEMY_DOESNT_KNOW_ABOUT_ME); + } else flags.or(FLAG_ENEMY_STATS_NOT_READY); + + // сохранить текущего врага + prev_enemy = enemy; + prev_enemy_position = position; + + expediency = true; + + if (enemy && see_enemy_now()) { + my_vertex_enemy_last_seen = monster->ai_location().level_vertex_id(); + enemy_vertex_enemy_last_seen = enemy->ai_location().level_vertex_id(); + + if (m_time_start_see_enemy == 0) m_time_start_see_enemy = time(); + } else m_time_start_see_enemy = 0; + + m_time_updated = time(); +} + + + +void CMonsterEnemyManager::force_enemy (const CEntityAlive *enemy) +{ + this->enemy = enemy; + position = enemy->Position(); + vertex = enemy->ai_location().level_vertex_id(); + time_last_seen = time(); + + forced = true; + + update (); +} + +void CMonsterEnemyManager::unforce_enemy() +{ + enemy = monster->EnemyMemory.get_enemy(); + + if (enemy) { + SMonsterEnemy enemy_info = monster->EnemyMemory.get_enemy_info(); + position = enemy_info.position; + vertex = enemy_info.vertex; + time_last_seen = enemy_info.time; + } + + forced = false; + + update (); +} + + +u32 CMonsterEnemyManager::get_enemies_count() +{ + return monster->EnemyMemory.get_enemies_count(); +} + +void CMonsterEnemyManager::reinit() +{ + enemy = 0; + time_last_seen = 0; + flags.zero (); + forced = false; + prev_enemy = 0; + danger_type = eNone; + + my_vertex_enemy_last_seen = monster->ai_location().level_vertex_id(); + enemy_vertex_enemy_last_seen = u32(-1); + + m_time_updated = 0; + m_time_start_see_enemy = 0; +} + + +void CMonsterEnemyManager::add_enemy(const CEntityAlive *enemy) +{ + monster->EnemyMemory.add_enemy(enemy); +} + + +bool CMonsterEnemyManager::see_enemy_now() +{ + return (monster->memory().visual().visible_right_now(enemy)); +} + +bool CMonsterEnemyManager::enemy_see_me_now() +{ + if (Actor() == enemy) { + return (Actor()->memory().visual().visible_right_now(monster)); + } else { + CCustomMonster *cm = const_cast(enemy)->cast_custom_monster(); + if (cm) return (cm->memory().visual().visible_right_now(monster)); + } + + return false; +} + +bool CMonsterEnemyManager::is_faced(const CEntityAlive *object0, const CEntityAlive *object1) +{ + if (object0->Position().distance_to(object1->Position()) > object0->ffGetRange()) + return (false); + + float yaw1, pitch1, yaw2, pitch2, fYawFov, fPitchFov, fRange; + Fvector tPosition = object0->Position(); + + yaw1 = object0->Orientation().yaw; + pitch1 = object0->Orientation().pitch; + fYawFov = angle_normalize_signed(object0->ffGetFov()*PI/180.f); + fRange = object0->ffGetRange(); + + fYawFov = angle_normalize_signed((_abs(fYawFov) + _abs(atanf(1.f/tPosition.distance_to(object1->Position()))))/2.f); + fPitchFov = angle_normalize_signed(fYawFov*1.f); + tPosition.sub (object1->Position()); + tPosition.mul (-1); + tPosition.getHP (yaw2,pitch2); + yaw1 = angle_normalize_signed(yaw1); + pitch1 = angle_normalize_signed(pitch1); + yaw2 = angle_normalize_signed(yaw2); + pitch2 = angle_normalize_signed(pitch2); + if ((angle_difference(yaw1,yaw2) <= fYawFov) && (angle_difference(pitch1,pitch2) <= fPitchFov)) + return (true); + return (false); +} + +bool CMonsterEnemyManager::is_enemy(const CEntityAlive *obj) +{ + return ((monster->g_Team() != obj->g_Team()) && monster->is_relation_enemy(obj) && obj->g_Alive()); +} + +void CMonsterEnemyManager::transfer_enemy(CBaseMonster *friend_monster) +{ + // если у friend_monster нет врага + if (!friend_monster->EnemyMan.get_enemy()) return; + + monster->EnemyMemory.add_enemy( + friend_monster->EnemyMan.get_enemy(), + friend_monster->EnemyMan.get_enemy_position(), + friend_monster->EnemyMan.get_enemy_vertex(), + friend_monster->EnemyMan.get_enemy_time_last_seen() + ); +} + +u32 CMonsterEnemyManager::see_enemy_duration() +{ + return ((m_time_start_see_enemy == 0) ? 0 : (time() - m_time_start_see_enemy)); +} + diff --git a/src/xrGameLA/ai/monsters/monster_enemy_manager.h b/src/xrGameLA/ai/monsters/monster_enemy_manager.h new file mode 100644 index 000000000..751378e2c --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_enemy_manager.h @@ -0,0 +1,73 @@ +#pragma once +#include "ai_monster_defs.h" + +class CBaseMonster; + +class CMonsterEnemyManager { + CBaseMonster *monster; + + const CEntityAlive *enemy; + + Fvector position; + u32 vertex; + u32 time_last_seen; + + Flags32 flags; + bool forced; + + bool expediency; + + const CEntityAlive *prev_enemy; + Fvector prev_enemy_position; + + bool enemy_see_me; + + EDangerType danger_type; + + // node, where monster saw enemy last time + u32 my_vertex_enemy_last_seen; + // node, of enemy (its always valid unlike vertex) + u32 enemy_vertex_enemy_last_seen; + + u32 m_time_updated; + u32 m_time_start_see_enemy; + +public: + CMonsterEnemyManager (); + ~CMonsterEnemyManager (); + void init_external (CBaseMonster *M); + void reinit (); + + void update (); + + void force_enemy (const CEntityAlive *enemy); + void unforce_enemy (); + + const CEntityAlive *get_enemy () {return enemy;} + EDangerType get_danger_type () {return danger_type;} + const Fvector &get_enemy_position () {return position;} + u32 get_enemy_vertex () {return vertex;} + TTime get_enemy_time_last_seen () {return time_last_seen;} + + Flags32 &get_flags () {return flags;} + + bool see_enemy_now (); + bool enemy_see_me_now (); + + // вернуть количество врагов + u32 get_enemies_count (); + + void add_enemy (const CEntityAlive *); + bool is_faced (const CEntityAlive *object0, const CEntityAlive *object1); + + bool is_enemy (const CEntityAlive *obj); + + // обновить врага в соответствии с врагом у monster + void transfer_enemy (CBaseMonster *friend_monster); + + u32 get_my_vertex_enemy_last_seen () {return my_vertex_enemy_last_seen;} + u32 get_enemy_vertex_enemy_last_seen () {return enemy_vertex_enemy_last_seen;} + + u32 see_enemy_duration (); +}; + diff --git a/src/xrGameLA/ai/monsters/monster_enemy_memory.cpp b/src/xrGameLA/ai/monsters/monster_enemy_memory.cpp new file mode 100644 index 000000000..885dae0d0 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_enemy_memory.cpp @@ -0,0 +1,157 @@ +#include "pch_script.h" +#include "monster_enemy_memory.h" +#include "BaseMonster/base_monster.h" +#include "../../memory_manager.h" +#include "../../visual_memory_manager.h" +#include "../../enemy_manager.h" +#include "../../ai_object_location.h" +#include "monster_home.h" + +CMonsterEnemyMemory::CMonsterEnemyMemory() +{ + monster = 0; + time_memory = 15000; +} + +CMonsterEnemyMemory::~CMonsterEnemyMemory() +{ +} + +void CMonsterEnemyMemory::init_external(CBaseMonster *M, TTime mem_time) +{ + monster = M; + time_memory = mem_time; +} + + +void CMonsterEnemyMemory::update() +{ + VERIFY (monster->g_Alive()); + // Обновить врагов + for (xr_vector::const_iterator I = monster->memory().enemy().objects().begin(); I != monster->memory().enemy().objects().end(); ++I) { + if (monster->memory().visual().visible_now(*I)) add_enemy(*I); + } + + // удалить устаревших врагов + remove_non_actual(); + + // обновить опасность + for (ENEMIES_MAP_IT it = m_objects.begin(); it != m_objects.end(); it++) { + u8 relation_value = u8(monster->tfGetRelationType(it->first)); + float dist = monster->Position().distance_to(it->second.position); + it->second.danger = (1 + relation_value*relation_value*relation_value) / (1 + dist); + } +} + +void CMonsterEnemyMemory::add_enemy(const CEntityAlive *enemy) +{ + SMonsterEnemy enemy_info; + enemy_info.position = enemy->Position(); + enemy_info.vertex = enemy->ai_location().level_vertex_id(); + enemy_info.time = Device.dwTimeGlobal; + enemy_info.danger = 0.f; + + ENEMIES_MAP_IT it = m_objects.find(enemy); + if (it != m_objects.end()) { + // обновить данные о враге + it->second = enemy_info; + } else { + // добавить врага в список объектов + m_objects.insert(mk_pair(enemy, enemy_info)); + } +} + +void CMonsterEnemyMemory::add_enemy(const CEntityAlive *enemy, const Fvector &pos, u32 vertex, u32 time) +{ + SMonsterEnemy enemy_info; + enemy_info.position = pos; + enemy_info.vertex = vertex; + enemy_info.time = time; + enemy_info.danger = 0.f; + + ENEMIES_MAP_IT it = m_objects.find(enemy); + if (it != m_objects.end()) { + // обновить данные о враге + if (it->second.time < enemy_info.time) it->second = enemy_info; + } else { + // добавить врага в список объектов + m_objects.insert(mk_pair(enemy, enemy_info)); + } +} + +void CMonsterEnemyMemory::remove_non_actual() +{ + TTime cur_time = Device.dwTimeGlobal; + + // удалить 'старых' врагов и тех, расстояние до которых > 30м и др. + for (ENEMIES_MAP_IT it = m_objects.begin(), nit; it != m_objects.end(); it = nit) + { + nit = it; ++nit; + // проверить условия удаления + if ( !it->first || + !it->first->g_Alive() || + it->first->getDestroy() || + (it->second.time + time_memory < cur_time) || + (it->first->g_Team() == monster->g_Team()) + ) + m_objects.erase (it); + } +} + +const CEntityAlive *CMonsterEnemyMemory::get_enemy() +{ + ENEMIES_MAP_IT it = find_best_enemy(); + if (it != m_objects.end()) return it->first; + return (0); +} + +SMonsterEnemy CMonsterEnemyMemory::get_enemy_info() +{ + SMonsterEnemy ret_val; + ret_val.time = 0; + + ENEMIES_MAP_IT it = find_best_enemy(); + if (it != m_objects.end()) ret_val = it->second; + + return ret_val; +} + +ENEMIES_MAP_IT CMonsterEnemyMemory::find_best_enemy() +{ + ENEMIES_MAP_IT it = m_objects.end(); + float max_value = 0.f; + + // find best at home first + for (ENEMIES_MAP_IT I = m_objects.begin(); I != m_objects.end(); I++) { + if (!monster->Home->at_home(I->second.position)) continue; + if (I->second.danger > max_value) { + max_value = I->second.danger; + it = I; + } + } + + // there is no best enemies at home + if (it == m_objects.end()) { + // find any + max_value = 0.f; + for (ENEMIES_MAP_IT I = m_objects.begin(); I != m_objects.end(); I++) { + if (I->second.danger > max_value) { + max_value = I->second.danger; + it = I; + } + } + } + + return it; +} + +void CMonsterEnemyMemory::remove_links(CObject *O) +{ + for (ENEMIES_MAP_IT I = m_objects.begin();I!=m_objects.end();++I) { + if ((*I).first == O) { + m_objects.erase(I); + break; + } + } +} + diff --git a/src/xrGameLA/ai/monsters/monster_enemy_memory.h b/src/xrGameLA/ai/monsters/monster_enemy_memory.h new file mode 100644 index 000000000..09de712b2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_enemy_memory.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ai_monster_defs.h" + +class CBaseMonster; + +class CMonsterEnemyMemory { + CBaseMonster *monster; + TTime time_memory; + + ENEMIES_MAP m_objects; + +public: + CMonsterEnemyMemory (); + ~CMonsterEnemyMemory (); + + void init_external (CBaseMonster *M, TTime mem_time); + void update (); + + // ----------------------------------------------------- + const CEntityAlive *get_enemy (); + SMonsterEnemy get_enemy_info (); + u32 get_enemies_count () {return m_objects.size();} + + const ENEMIES_MAP &get_memory () {return m_objects;} + + void clear () {m_objects.clear();} + void remove_links (CObject *O); + + void add_enemy (const CEntityAlive *enemy); + void add_enemy (const CEntityAlive *enemy, const Fvector &pos, u32 vertex, u32 time); + +private: + + void remove_non_actual (); + + ENEMIES_MAP_IT find_best_enemy (); + +}; + diff --git a/src/xrGameLA/ai/monsters/monster_event_manager.cpp b/src/xrGameLA/ai/monsters/monster_event_manager.cpp new file mode 100644 index 000000000..6b93b3766 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_event_manager.cpp @@ -0,0 +1,58 @@ +#include "stdafx.h" +#include "monster_event_manager.h" + +CMonsterEventManager::CMonsterEventManager() +{ +} + +CMonsterEventManager::~CMonsterEventManager() +{ + clear (); +} + +void CMonsterEventManager::add_delegate(EEventType event, typeEvent delegate) +{ + EVENT_MAP_IT it = m_event_storage.find(event); + if (it == m_event_storage.end()) { + std::pair< EVENT_MAP_IT, bool > res; + res = m_event_storage.insert(mk_pair(event, EVENT_VECTOR())); + it = res.first; + } + + it->second.push_back(event_struc(delegate)); +} + +void CMonsterEventManager::remove_delegate(EEventType event, typeEvent delegate) +{ + EVENT_MAP_IT it = m_event_storage.find(event); + if (it == m_event_storage.end()) return; + + for (EVENT_VECTOR_IT it_del = it->second.begin(); it_del != it->second.end(); ++it_del) { + if (it_del->delegate == delegate) it_del->need_remove = true; + } +} + + +void CMonsterEventManager::raise(EEventType event, IEventData *data) +{ + EVENT_MAP_IT it = m_event_storage.find(event); + if (it == m_event_storage.end()) return; + + for (EVENT_VECTOR_IT I=it->second.begin(); I != it->second.end(); I++) { + if (!I->need_remove) (I->delegate)(data); + } + + EVENT_VECTOR_IT it_del = std::remove_if(it->second.begin(),it->second.end(), pred_remove()); + it->second.erase(it_del,it->second.end()); +} + +void CMonsterEventManager::clear() +{ + for (EVENT_MAP_IT I_map = m_event_storage.begin(); I_map != m_event_storage.end(); I_map++) { + I_map->second.clear(); + } + + m_event_storage.clear(); +} + + diff --git a/src/xrGameLA/ai/monsters/monster_event_manager.h b/src/xrGameLA/ai/monsters/monster_event_manager.h new file mode 100644 index 000000000..70ab463b9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_event_manager.h @@ -0,0 +1,38 @@ +#pragma once +#include "monster_event_manager_defs.h" +#include "../../fastdelegate.h" + +typedef fastdelegate::FastDelegate1 typeEvent; + +class CMonsterEventManager { + + // delayed remove + struct event_struc { + typeEvent delegate; + bool need_remove; + + event_struc (typeEvent e) : delegate(e) {need_remove = false;} + }; + + struct pred_remove { + bool operator() (const event_struc ¶m) {return param.need_remove;} + }; + + DEFINE_VECTOR (event_struc, EVENT_VECTOR, EVENT_VECTOR_IT); + DEFINE_MAP (EEventType, EVENT_VECTOR, EVENT_MAP, EVENT_MAP_IT); + + EVENT_MAP m_event_storage; +public: + CMonsterEventManager (); + ~CMonsterEventManager (); + + void add_delegate (EEventType event, typeEvent delegate); + void remove_delegate (EEventType event, typeEvent delegate); + + void raise (EEventType, IEventData *data = 0); + +private: + void clear (); +}; + + diff --git a/src/xrGameLA/ai/monsters/monster_event_manager_defs.h b/src/xrGameLA/ai/monsters/monster_event_manager_defs.h new file mode 100644 index 000000000..08718c5bc --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_event_manager_defs.h @@ -0,0 +1,17 @@ +#pragma once + +enum EEventType { + eventAnimationStart = u32(0), + eventAnimationEnd, + eventSoundStart, + eventSoundEnd, + eventParticlesStart, + eventParticlesEnd, + eventStep, + eventTAChange, + eventVelocityBounce, +}; + +class IEventData {}; + +#define DEFINE_DELEGATE void __stdcall diff --git a/src/xrGameLA/ai/monsters/monster_hit_memory.cpp b/src/xrGameLA/ai/monsters/monster_hit_memory.cpp new file mode 100644 index 000000000..54c29049f --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_hit_memory.cpp @@ -0,0 +1,178 @@ +#include "stdafx.h" +#include "monster_hit_memory.h" +#include "BaseMonster/base_monster.h" + +CMonsterHitMemory::CMonsterHitMemory() +{ + monster = 0; + time_memory = 10000; +} + +CMonsterHitMemory::~CMonsterHitMemory() +{ +} + +void CMonsterHitMemory::init_external(CBaseMonster *M, TTime mem_time) +{ + monster = M; + time_memory = mem_time; +} + + +void CMonsterHitMemory::update() +{ + // удалить устаревшие hits + remove_non_actual(); +} + +bool CMonsterHitMemory::is_hit(CObject *pO) +{ + return (std::find(m_hits.begin(),m_hits.end(),pO) != m_hits.end()); +} + +void CMonsterHitMemory::add_hit(CObject *who, EHitSide side) +{ + SMonsterHit new_hit_info; + new_hit_info.object = who; + new_hit_info.time = Device.dwTimeGlobal; + new_hit_info.side = side; + new_hit_info.position = monster->Position(); + + MONSTER_HIT_VECTOR_IT it = std::find(m_hits.begin(), m_hits.end(), who); + + if (it == m_hits.end()) m_hits.push_back(new_hit_info); + else *it = new_hit_info; +} + +struct predicate_old_hit { + TTime cur_time; + TTime mem_time; + + predicate_old_hit(TTime mem_time, TTime cur_time){ + this->cur_time = cur_time; + this->mem_time = mem_time; + } + + IC bool operator() (const SMonsterHit &hit_info) { + if ((mem_time + hit_info.time) < cur_time) return true; + if (hit_info.object) { + CEntityAlive *entity = smart_cast(hit_info.object); + if (entity && !entity->g_Alive()) return true; + } + return false; + } + +}; + +void CMonsterHitMemory::remove_non_actual() +{ + m_hits.erase ( + std::remove_if( + m_hits.begin(), + m_hits.end(), + predicate_old_hit( + time_memory, + Device.dwTimeGlobal + ) + ), + m_hits.end() + ); +} + +Fvector CMonsterHitMemory::get_last_hit_dir() +{ + Fvector dir = monster->Direction(); + + // найти последний по времени хит + SMonsterHit last_hit; + last_hit.time = 0; + last_hit.side = eSideFront; + + for (u32 i = 0; i < m_hits.size(); i++) { + if (m_hits[i].time > last_hit.time) last_hit = m_hits[i]; + } + + // если есть хит, вычислить направление + if (last_hit.time != 0) { + + float h,p; + dir.getHP(h,p); + + switch (last_hit.side) { + case eSideBack: + h += PI; + break; + case eSideLeft: + h += PI_DIV_2; + break; + case eSideRight: + h -= PI_DIV_2; + break; + } + + dir.setHP(h,p); + dir.normalize(); + } + + return dir; +} + +TTime CMonsterHitMemory::get_last_hit_time() +{ + SMonsterHit last_hit; + last_hit.time = 0; + + for (u32 i = 0; i < m_hits.size(); i++) { + if (m_hits[i].time > last_hit.time) last_hit = m_hits[i]; + } + + return last_hit.time; +} + +CObject *CMonsterHitMemory::get_last_hit_object() +{ + SMonsterHit last_hit; + last_hit.object = 0; + last_hit.time = 0; + + for (u32 i = 0; i < m_hits.size(); i++) { + if (m_hits[i].time > last_hit.time) last_hit = m_hits[i]; + } + + return last_hit.object; +} + +Fvector CMonsterHitMemory::get_last_hit_position() +{ + SMonsterHit last_hit; + last_hit.time = 0; + last_hit.position.set(0.f,0.f,0.f); + + for (u32 i = 0; i < m_hits.size(); i++) { + if (m_hits[i].time > last_hit.time) last_hit = m_hits[i]; + } + + return last_hit.position; +} + +struct predicate_old_info { + const CObject *object; + + predicate_old_info(const CObject *obj) : object(obj) {} + + IC bool operator() (const SMonsterHit &hit_info) { + return (object == hit_info.object); + } +}; + +void CMonsterHitMemory::remove_hit_info(const CObject *obj) +{ + m_hits.erase ( + std::remove_if( + m_hits.begin(), + m_hits.end(), + predicate_old_info(obj) + ), + m_hits.end() + ); +} diff --git a/src/xrGameLA/ai/monsters/monster_hit_memory.h b/src/xrGameLA/ai/monsters/monster_hit_memory.h new file mode 100644 index 000000000..b853d0e54 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_hit_memory.h @@ -0,0 +1,39 @@ +#pragma once +#include "ai_monster_defs.h" + +class CBaseMonster; + +class CMonsterHitMemory { + CBaseMonster *monster; + TTime time_memory; + + MONSTER_HIT_VECTOR m_hits; + +public: + CMonsterHitMemory (); + ~CMonsterHitMemory (); + + void init_external (CBaseMonster *M, TTime mem_time); + void update (); + + // ----------------------------------------------------- + bool is_hit () {return !m_hits.empty();} + bool is_hit (CObject *pO); + + + int get_num_hits () {return m_hits.size(); } + + void add_hit (CObject *who, EHitSide side); + + Fvector get_last_hit_dir (); + TTime get_last_hit_time (); + CObject *get_last_hit_object (); + Fvector get_last_hit_position (); + + void clear () {m_hits.clear();} + + void remove_hit_info (const CObject *obj); + +private: + void remove_non_actual (); +}; diff --git a/src/xrGameLA/ai/monsters/monster_home.cpp b/src/xrGameLA/ai/monsters/monster_home.cpp new file mode 100644 index 000000000..b7eaf9018 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_home.cpp @@ -0,0 +1,138 @@ +#include "stdafx.h" +#include "monster_home.h" +#include "BaseMonster/base_monster.h" +#include "../../ai_space.h" +#include "../../patrol_path_storage.h" +#include "../../patrol_path.h" +#include "../../level_graph.h" +#include "../../cover_point.h" +#include "monster_cover_manager.h" +#include "../../ai_object_location.h" +#include "../../restricted_object.h" + +#ifdef DEBUG +# include "../../game_graph.h" +void check_path (const CBaseMonster *monster, const CPatrolPath *path) +{ + VERIFY2 ( + ai().game_graph().vertex( + path->vertices().begin()->second->data().game_vertex_id() + )->level_id() + == + ai().level_graph().level_id(), + make_string( + "invalid patrol path [%s] as home specified for monster [%s]\nmonster is on level %s\npatrol path is on level %s", + *path->m_name, + *monster->cName(), + *ai().game_graph().header().level( + ai().game_graph().vertex( + monster->ai_location().game_vertex_id() + )->level_id() + ).name(), + *ai().game_graph().header().level( + ai().game_graph().vertex( + path->vertices().begin()->second->data().game_vertex_id() + )->level_id() + ).name() + ) + ); +} +#else // DEBUG +# define check_path(a,b) +#endif // DEBUG + +void CMonsterHome::load(LPCSTR line) +{ + m_path = 0; + m_radius_min = 20.f; + m_radius_max = 40.f; + + if (m_object->spawn_ini() && m_object->spawn_ini()->section_exist(line)) { + m_path = ai().patrol_paths().path(m_object->spawn_ini()->r_string(line,"path")); + check_path (m_object,m_path); + if (m_object->spawn_ini()->line_exist(line,"radius_min")) + m_radius_min = m_object->spawn_ini()->r_float(line,"radius_min"); + if (m_object->spawn_ini()->line_exist(line,"radius_max")) + m_radius_max = m_object->spawn_ini()->r_float(line,"radius_max"); + + VERIFY3(m_radius_max > m_radius_min, "Error: Wrong home point radius specified for monster ", *m_object->cName()); + } + + m_aggressive = false; +} + +void CMonsterHome::setup(LPCSTR path_name, float min_radius, float max_radius, bool aggressive) +{ + m_path = ai().patrol_paths().path(path_name); + check_path (m_object,m_path); + m_radius_min = min_radius; + m_radius_max = max_radius; + + m_aggressive = aggressive; +} + +u32 CMonsterHome::get_place() +{ + VERIFY (m_path); + u32 result = u32(-1); + + //get_random_point + const CPatrolPath::CVertex *vertex = m_path->vertex(Random.randI(m_path->vertex_count())); + + //get_random node + m_object->control().path_builder().get_node_in_radius(vertex->data().level_vertex_id(), m_radius_min, m_radius_min + (m_radius_max - m_radius_min)/2, 5, result); + + // if node was not found - return vertex selected + if (result == u32(-1)) { + + // TODO: find more acceptable decision, than return its level_vertex_id, if !accessible + if (m_object->control().path_builder().accessible(vertex->data().level_vertex_id())) + return vertex->data().level_vertex_id(); + else + return m_object->ai_location().level_vertex_id(); + } + + return result; +} + +u32 CMonsterHome::get_place_in_cover() +{ + VERIFY (m_path); + + //get_random_point + const CPatrolPath::CVertex *vertex = m_path->vertex(Random.randI(m_path->vertex_count())); + + // find cover + const CCoverPoint *point = m_object->CoverMan->find_cover(vertex->data().position(), vertex->data().position(), m_radius_min, m_radius_min + (m_radius_max - m_radius_min)/2); + if (point) return point->level_vertex_id(); + + return u32(-1); +} + + +bool CMonsterHome::at_home() +{ + return at_home(m_object->Position()); +} + +bool CMonsterHome::at_home(const Fvector &pos) +{ + if (!m_path) return true; + + // check every point and distance to it + for (u32 i=0; ivertex_count(); i++) { + const CPatrolPath::CVertex *vertex = m_path->vertex(i); + float dist = pos.distance_to(ai().level_graph().vertex_position(vertex->data().level_vertex_id())); + + if (dist < m_radius_max) return true; + } + + return false; +} + +void CMonsterHome::remove_home() +{ + m_path = 0; + m_aggressive = false; +} + diff --git a/src/xrGameLA/ai/monsters/monster_home.h b/src/xrGameLA/ai/monsters/monster_home.h new file mode 100644 index 000000000..e8bef1fba --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_home.h @@ -0,0 +1,28 @@ +#pragma once + +class CBaseMonster; +class CPatrolPath; + +class CMonsterHome { + CBaseMonster *m_object; + const CPatrolPath *m_path; + + float m_radius_min; + float m_radius_max; + + bool m_aggressive; + +public: + CMonsterHome (CBaseMonster *obj) {m_object = obj;} + + void load (LPCSTR line); + void setup (LPCSTR path_name, float min_radius, float max_radius, bool aggressive = false); + void remove_home (); + + u32 get_place (); + u32 get_place_in_cover (); + bool at_home (); + bool at_home (const Fvector &pos); + IC bool has_home () {return (m_path != 0);} + IC bool is_aggressive () {return m_aggressive;} +}; \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/monster_morale.cpp b/src/xrGameLA/ai/monsters/monster_morale.cpp new file mode 100644 index 000000000..6f33f1019 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_morale.cpp @@ -0,0 +1,48 @@ +#include "stdafx.h" +#include "monster_morale.h" + +void CMonsterMorale::init_external(CBaseMonster *obj) +{ + m_object = obj; +} +void CMonsterMorale::load(LPCSTR section) +{ + m_hit_quant = pSettings->r_float(section,"Morale_Hit_Quant"); + m_attack_success_quant = pSettings->r_float(section,"Morale_Attack_Success_Quant"); + m_v_taking_heart = pSettings->r_float(section,"Morale_Take_Heart_Speed"); + m_v_despondent = pSettings->r_float(section,"Morale_Despondent_Speed"); + m_v_stable = pSettings->r_float(section,"Morale_Stable_Speed"); + m_despondent_threshold = pSettings->r_float(section,"Morale_Despondent_Threashold"); +} + +void CMonsterMorale::reinit() +{ + m_state = eStable; + m_morale = 1.0f; +} + +void CMonsterMorale::on_hit() +{ + change(-m_hit_quant); +} + +void CMonsterMorale::on_attack_success() +{ + change(m_attack_success_quant); +} + +void CMonsterMorale::update_schedule(u32 dt) +{ + float cur_v = 1.f; + + switch (m_state) { + case eStable: cur_v = m_v_stable; break; + case eTakeHeart: cur_v = m_v_taking_heart; break; + case eDespondent: cur_v = -m_v_despondent; break; + } + + m_morale += cur_v * dt / 1000; + + clamp (m_morale, 0.f, 1.f); +} + diff --git a/src/xrGameLA/ai/monsters/monster_morale.h b/src/xrGameLA/ai/monsters/monster_morale.h new file mode 100644 index 000000000..51ad16da9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_morale.h @@ -0,0 +1,54 @@ +#pragma once + +class CBaseMonster; + +class CMonsterMorale { + + // external parameters + float m_hit_quant; + float m_attack_success_quant; + float m_team_mate_die; + float m_v_taking_heart; + float m_v_despondent; + float m_v_stable; + float m_despondent_threshold; + + + CBaseMonster *m_object; + + enum EState { + eStable, + eTakeHeart, + eDespondent + }; + + EState m_state; + + float m_morale; + +public: + CMonsterMorale (){} + ~CMonsterMorale (){} + + void init_external (CBaseMonster *obj); + void load (LPCSTR section); + void reinit (); + + void on_hit (); + void on_attack_success (); + + void update_schedule (u32 dt); + + IC void set_despondent (); + IC void set_take_heart (); + IC void set_normal_state (); + + IC bool is_despondent (); + + IC float get_morale (); + +private: + IC void change (float value); +}; + +#include "monster_morale_inline.h" diff --git a/src/xrGameLA/ai/monsters/monster_morale_inline.h b/src/xrGameLA/ai/monsters/monster_morale_inline.h new file mode 100644 index 000000000..af5013be8 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_morale_inline.h @@ -0,0 +1,32 @@ +#pragma once + +IC void CMonsterMorale::set_despondent() +{ + m_state = eDespondent; +} + +IC void CMonsterMorale::set_take_heart() +{ + m_state = eTakeHeart; +} + +IC void CMonsterMorale::set_normal_state() +{ + m_state = eStable; +} + +IC void CMonsterMorale::change(float value) +{ + m_morale += value; + clamp (m_morale,0.f,1.f); +} + +IC bool CMonsterMorale::is_despondent() +{ + return ((m_state == eDespondent) || (m_morale < m_despondent_threshold)); +} + +IC float CMonsterMorale::get_morale() +{ + return m_morale; +} diff --git a/src/xrGameLA/ai/monsters/monster_sound_defs.h b/src/xrGameLA/ai/monsters/monster_sound_defs.h new file mode 100644 index 000000000..f6fef5f1d --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_sound_defs.h @@ -0,0 +1,42 @@ +#pragma once + + +// monster base sound types +namespace MonsterSound { + enum EType { + eMonsterSoundBase = u32(0), + eMonsterSoundIdle = eMonsterSoundBase | 1, + eMonsterSoundEat = eMonsterSoundBase | 2, + eMonsterSoundAggressive = eMonsterSoundBase | 3, + eMonsterSoundAttackHit = eMonsterSoundBase | 4, + eMonsterSoundTakeDamage = eMonsterSoundBase | 5, + eMonsterSoundStrike = eMonsterSoundBase | 6, + + eMonsterSoundDie = eMonsterSoundBase | 7, + eMonsterSoundDieInAnomaly = eMonsterSoundBase | 8, + + eMonsterSoundThreaten = eMonsterSoundBase | 9, + eMonsterSoundSteal = eMonsterSoundBase | 10, + eMonsterSoundPanic = eMonsterSoundBase | 11, + eMonsterSoundIdleDistant = eMonsterSoundBase | 12, + + eMonsterSoundScript = u32(1) << 7, + eMonsterSoundCustom = eMonsterSoundScript << 7, + eMonsterSoundDummy = u32(-1), + }; + + enum EPriority { + eCriticalPriority = u32(1), + eHighPriority = u32(1) << 3, + eNormalPriority = u32(1) << 7, + eLowPriority = u32(1) << 15 + }; + + enum EVirtualChannels { + eBaseChannel = u32(1) << 7, + eChannelIndependent = u32(1) << 15, // can be played in any time with any conditions (need shift operator for every id) + eCaptureAllChannels = u32(-1), // play only this one + }; +}; + +#define DEFAULT_SAMPLE_COUNT 16 diff --git a/src/xrGameLA/ai/monsters/monster_sound_memory.cpp b/src/xrGameLA/ai/monsters/monster_sound_memory.cpp new file mode 100644 index 000000000..849011bee --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_sound_memory.cpp @@ -0,0 +1,213 @@ +#include "stdafx.h" +#include "monster_sound_memory.h" +#include "BaseMonster/base_monster.h" + +#define CHECK_SOUND_TYPE(a,b,c) { if ((a & b) == b) return c; } + +const u32 time_help_sound_remember = 10000; + +TSoundDangerValue tagSoundElement::ConvertSoundType(ESoundTypes stype) +{ + + if (((stype & SOUND_TYPE_WEAPON) != SOUND_TYPE_WEAPON) && + ((stype & SOUND_TYPE_MONSTER) != SOUND_TYPE_MONSTER) && + ((stype & SOUND_TYPE_WORLD) != SOUND_TYPE_WORLD)) return NONE_DANGEROUS_SOUND; + + CHECK_SOUND_TYPE(stype, SOUND_TYPE_WEAPON_RECHARGING, WEAPON_RECHARGING); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_WEAPON_SHOOTING, WEAPON_SHOOTING); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_ITEM_TAKING, WEAPON_TAKING); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_ITEM_HIDING, WEAPON_HIDING); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_WEAPON_EMPTY_CLICKING, WEAPON_EMPTY_CLICKING); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_WEAPON_BULLET_HIT, WEAPON_BULLET_RICOCHET); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_MONSTER_DYING, MONSTER_DYING); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_MONSTER_INJURING, MONSTER_INJURING); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_MONSTER_STEP, MONSTER_WALKING); + + CHECK_SOUND_TYPE(stype, SOUND_TYPE_MONSTER_TALKING, MONSTER_TALKING); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_MONSTER_ATTACKING, MONSTER_ATTACKING); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_WORLD_OBJECT_BREAKING, OBJECT_BREAKING); + CHECK_SOUND_TYPE(stype, SOUND_TYPE_WORLD_OBJECT_COLLIDING, OBJECT_FALLING); + + + return NONE_DANGEROUS_SOUND; +} + + +CMonsterSoundMemory::CMonsterSoundMemory() +{ + Sounds.reserve (20); + m_time_help_sound = 0; + m_help_node = u32(-1); +} +CMonsterSoundMemory::~CMonsterSoundMemory() +{ + +} + +void CMonsterSoundMemory::init_external(CBaseMonster *M, TTime mem_time) +{ + monster = M; + time_memory = mem_time; +} + +void CMonsterSoundMemory::HearSound(const SoundElem &s) +{ + if (NONE_DANGEROUS_SOUND == s.type) return; + if (DOOR_OPENING <= s.type) return; + if ((s.type == MONSTER_WALKING) && !s.who) return; + + // поиск в массиве звука + xr_vector::iterator it; + + bool b_sound_replaced = false; + for (it = Sounds.begin(); Sounds.end() != it; ++it) { + if ((s.who == it->who) && (it->type == s.type)) { + if (s.time >= it->time) { + *it = s; + b_sound_replaced = true; + } + } + } + + if (!b_sound_replaced) Sounds.push_back(s); + +} + +void CMonsterSoundMemory::HearSound(const CObject* who, int eType, const Fvector &Position, float power, TTime time) +{ + SoundElem s; + s.SetConvert(who,eType,Position,power,time); + s.CalcValue(time,monster->Position()); + + HearSound(s); +} + +void CMonsterSoundMemory::GetSound(SoundElem &s, bool &bDangerous) +{ + VERIFY(!Sounds.empty()); + + // возврат самого опасного + s = GetSound(); + + if (s.type > WEAPON_EMPTY_CLICKING) bDangerous = false; + else bDangerous = true; +} + +SoundElem &CMonsterSoundMemory::GetSound() +{ + VERIFY(!Sounds.empty()); + + xr_vector::iterator it = std::max_element(Sounds.begin(), Sounds.end()); + return (*it); +} + + +struct pred_remove_nonactual_sounds { + TTime new_time; + + pred_remove_nonactual_sounds(TTime time) {new_time = time;} + + bool operator() (const SoundElem &x) { + + // удалить звуки от объектов, перешедших в оффлайн + if (x.who && x.who->getDestroy()) return true; + + // удалить 'старые' звуки + if (x.time < new_time) return true; + + // удалить звуки от неживых объектов + if (x.who) { + const CEntityAlive *pE = smart_cast (x.who); + if (pE && !pE->g_Alive()) return true; + } + + return false; + } +}; + + +void CMonsterSoundMemory::UpdateHearing() +{ + // удаление устаревших звуков + Sounds.erase ( + std::remove_if( + Sounds.begin(), + Sounds.end(), + pred_remove_nonactual_sounds( + Device.dwTimeGlobal - time_memory + ) + ), + Sounds.end() + ); + + // пересчитать value + for (xr_vector::iterator I = Sounds.begin(); I != Sounds.end(); ++I) I->CalcValue(Device.dwTimeGlobal, monster->Position()); + + // update help sound + if (m_time_help_sound + time_help_sound_remember < time()) m_time_help_sound = 0; +} + +bool CMonsterSoundMemory::is_loud_sound(float val) +{ + for (u32 i=0; i val) return true; + + return false; +} + +bool CMonsterSoundMemory::get_sound_from_object(const CObject* obj, SoundElem &value) +{ + for (u32 i=0; iwho = who; type = ConvertSoundType((ESoundTypes)eType); this->position = position; this->power = power; this->time = time; + } + TSoundDangerValue ConvertSoundType(ESoundTypes stype); + + void CalcValue(TTime cur_time, Fvector cur_pos) { + value = FACTOR_SOUND_TYPE * u32(NONE_DANGEROUS_SOUND - WEAPON_SHOOTING) - iFloor(FACTOR_DISTANCE * cur_pos.distance_to(position)) - FACTOR_DELTA_TIME * iFloor(float((cur_time - time) / 1000)) + FACTOR_SOUND_POWER * iFloor(power); + } + +} SoundElem; + +class CMonsterSoundMemory { + TTime time_memory; // время хранения звуков + xr_vector Sounds; + + CBaseMonster *monster; + + u32 m_time_help_sound; + u32 m_help_node; + +public: + CMonsterSoundMemory (); + virtual ~CMonsterSoundMemory (); + + void init_external (CBaseMonster *M, TTime mem_time); + + void HearSound (const SoundElem &s); + void HearSound (const CObject* who, int eType, const Fvector &Position, float power, TTime time); + IC bool IsRememberSound () {return (!Sounds.empty());} + void GetSound (SoundElem &s, bool &bDangerous); // возвращает самый опасный звук + SoundElem &GetSound (); + bool get_sound_from_object (const CObject* who, SoundElem &value); + + void UpdateHearing (); + + bool is_loud_sound (float val); + + void clear () {Sounds.clear();} + void remove_links (CObject *O); + + // help sounds + bool hear_help_sound (); + u32 hear_help_sound_node (){VERIFY(m_help_node != u32(-1)); return m_help_node;} + + void check_help_sound (int eType, u32 node); +}; + diff --git a/src/xrGameLA/ai/monsters/monster_state_manager.h b/src/xrGameLA/ai/monsters/monster_state_manager.h new file mode 100644 index 000000000..4940fba79 --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_state_manager.h @@ -0,0 +1,25 @@ +#pragma once +#include "state_manager.h" +#include "state.h" + +template +class CMonsterStateManager : public IStateManagerBase, public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CMonsterStateManager (_Object *obj) : inherited(obj) {} + virtual void reinit (); + virtual void update (); + virtual void force_script_state (EMonsterState state); + virtual void execute_script_state (); + virtual void critical_finalize (); + + virtual EMonsterState get_state_type (); + +protected: + bool can_eat (); + bool check_state (u32 state_id); +}; + +#include "monster_state_manager_inline.h" + diff --git a/src/xrGameLA/ai/monsters/monster_state_manager_inline.h b/src/xrGameLA/ai/monsters/monster_state_manager_inline.h new file mode 100644 index 000000000..1b355d1ac --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_state_manager_inline.h @@ -0,0 +1,67 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CMonsterStateManagerAbstract CMonsterStateManager<_Object> + +TEMPLATE_SPECIALIZATION +void CMonsterStateManagerAbstract::reinit() +{ + inherited::reinit(); +} + +TEMPLATE_SPECIALIZATION +void CMonsterStateManagerAbstract::update() +{ + execute(); +} +TEMPLATE_SPECIALIZATION +void CMonsterStateManagerAbstract::force_script_state(EMonsterState state) +{ + // установить текущее состояние + select_state(state); +} +TEMPLATE_SPECIALIZATION +void CMonsterStateManagerAbstract::execute_script_state() +{ + // выполнить текущее состояние + get_state_current()->execute(); +} + +TEMPLATE_SPECIALIZATION +bool CMonsterStateManagerAbstract::can_eat() +{ + if (!object->CorpseMan.get_corpse()) return false; + + return check_state(eStateEat); +} + +TEMPLATE_SPECIALIZATION +bool CMonsterStateManagerAbstract::check_state(u32 state_id) +{ + if (prev_substate == state_id) { + if (!get_state_current()->check_completion()) return true; + } else { + if (get_state(state_id)->check_start_conditions()) return true; + } + + return false; +} + +TEMPLATE_SPECIALIZATION +void CMonsterStateManagerAbstract::critical_finalize() +{ + inherited::critical_finalize(); +} + +TEMPLATE_SPECIALIZATION +EMonsterState CMonsterStateManagerAbstract::get_state_type() +{ + return inherited::get_state_type(); +} + + +#undef CMonsterStateManagerAbstract +#undef TEMPLATE_SPECIALIZATION \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/monster_velocity_space.h b/src/xrGameLA/ai/monsters/monster_velocity_space.h new file mode 100644 index 000000000..6831ec47d --- /dev/null +++ b/src/xrGameLA/ai/monsters/monster_velocity_space.h @@ -0,0 +1,59 @@ +#pragma once + +namespace MonsterMovement { + + enum EMovementParameters { + eVelocityParameterIdle = u32(1) << 1, + eVelocityParameterStand = u32(1) << 4, + eVelocityParameterWalkNormal = u32(1) << 3, + eVelocityParameterRunNormal = u32(1) << 2, + + eVelocityParameterWalkDamaged = u32(1) << 5, + eVelocityParameterRunDamaged = u32(1) << 6, + eVelocityParameterSteal = u32(1) << 7, + eVelocityParameterDrag = u32(1) << 8, + eVelocityParameterInvisible = u32(1) << 9, + eVelocityParameterRunAttack = u32(1) << 10, + + eVelocityParamsWalk = eVelocityParameterStand | eVelocityParameterWalkNormal, + eVelocityParamsWalkDamaged = eVelocityParameterStand | eVelocityParameterWalkDamaged, + eVelocityParamsRun = eVelocityParameterStand | eVelocityParameterWalkNormal | eVelocityParameterRunNormal, + eVelocityParamsRunDamaged = eVelocityParameterStand | eVelocityParameterWalkDamaged | eVelocityParameterRunDamaged, + eVelocityParamsAttackNorm = eVelocityParameterStand | eVelocityParameterWalkNormal | eVelocityParameterRunNormal, + eVelocityParamsAttackDamaged = eVelocityParameterStand | eVelocityParameterWalkDamaged | eVelocityParameterRunDamaged, + eVelocityParamsSteal = eVelocityParameterStand | eVelocityParameterSteal, + eVelocityParamsDrag = eVelocityParameterStand | eVelocityParameterDrag, + eVelocityParamsInvisible = eVelocityParameterInvisible | eVelocityParameterStand, + eVelocityParamsRunAttack = eVelocityParameterRunAttack | eVelocityParameterStand, + + + eVelocityParameterCustom = u32(1) << 12, + }; + + enum EMovementParametersChimera { + eChimeraVelocityParameterUpperWalkFwd = eVelocityParameterCustom << 1, + eChimeraVelocityParameterJumpGround = eVelocityParameterCustom << 2, + + eChimeraVelocityParamsUpperWalkFwd = eVelocityParameterStand | eChimeraVelocityParameterUpperWalkFwd, + }; + + enum EMovementParametersSnork { + eSnorkVelocityParameterJumpGround = eVelocityParameterCustom << 2, + }; + + enum EMovementParametersController { + eControllerVelocityParameterMoveFwd = eVelocityParameterCustom << 1, + eControllerVelocityParameterMoveBkwd = eVelocityParameterCustom << 2, + + eControllerVelocityParamsMoveFwd = eControllerVelocityParameterMoveFwd | eVelocityParameterStand, + eControllerVelocityParamsMoveBkwd = eControllerVelocityParameterMoveBkwd | eVelocityParameterStand, + }; + + enum EMovementParametersGiant { + eGiantVelocityParameterJumpPrepare = eVelocityParameterCustom << 1, + eGiantVelocityParameterJumpGround = eVelocityParameterCustom << 2, + }; + +}; + + diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist.cpp b/src/xrGameLA/ai/monsters/poltergeist/poltergeist.cpp new file mode 100644 index 000000000..1cfd3487f --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist.cpp @@ -0,0 +1,315 @@ +#include "stdafx.h" +#include "poltergeist.h" +#include "poltergeist_state_manager.h" +#include "../../../characterphysicssupport.h" +#include "../../../PHMovementControl.h" +#include "../../../PhysicsShellHolder.h" +#include "../../../ai_debug.h" +#include "poltergeist_movement.h" +#include "../../../detail_path_manager.h" +#include "../monster_velocity_space.h" +#include "../../../level.h" +#include "../../../level_debug.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" +#include "../../../PhysicsShell.h" + +#define HEIGHT_CHANGE_VELOCITY 0.5f +#define HEIGHT_CHANGE_MIN_TIME 3000 +#define HEIGHT_CHANGE_MAX_TIME 10000 +#define HEIGHT_MIN 0.4f +#define HEIGHT_MAX 2.0f + + +CPoltergeist::CPoltergeist() +{ + StateMan = new CStateManagerPoltergeist(this); + + invisible_vel.set (0.1f, 0.1f); + + m_flame = 0; + m_tele = 0; +} + +CPoltergeist::~CPoltergeist() +{ + xr_delete (StateMan); + xr_delete (m_flame); + xr_delete (m_tele); +} + +void CPoltergeist::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + + invisible_vel.set(pSettings->r_float(section,"Velocity_Invisible_Linear"),pSettings->r_float(section,"Velocity_Invisible_Angular")); + movement().detail().add_velocity(MonsterMovement::eVelocityParameterInvisible,CDetailPathManager::STravelParams(invisible_vel.linear, invisible_vel.angular)); + + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + //SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + //SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRun, "stand_run_fwd_", -1, &velocity_run, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimDie, "stand_idle_", 0, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimMiscAction_00, "fall_down_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimMiscAction_01, "fly_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimLookAround, "stand_look_around_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_dmg_", -1, &velocity_walk_dmg, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRunDamaged, "stand_walk_dmg_", -1, &velocity_run_dmg, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimStandIdle); + anim().LinkAction(ACT_REST, eAnimStandIdle); + anim().LinkAction(ACT_DRAG, eAnimStandIdle); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimWalkFwd); + anim().LinkAction(ACT_LOOK_AROUND, eAnimLookAround); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + + READ_IF_EXISTS(pSettings,r_u32,section,"PsyAura_Fake_Delay", 8000); + READ_IF_EXISTS(pSettings,r_float,section,"PsyAura_Fake_MaxAddDist", 90.f); + + LPCSTR polter_type = pSettings->r_string(section,"type"); + + if (xr_strcmp(polter_type,"flamer") == 0) { + m_flame = new CPolterFlame(this); + m_flame->load (section); + } else { + m_tele = new CPolterTele(this); + m_tele->load (section); + } + +} + +void CPoltergeist::reload(LPCSTR section) +{ + inherited::reload(section); + Energy::reload(section,"Invisible_"); +} + +void CPoltergeist::reinit() +{ + inherited::reinit(); + Energy::reinit(); + + m_current_position = Position(); + + target_height = 0.3f; + time_height_updated = 0; + + Energy::set_auto_activate(); + Energy::set_auto_deactivate(); + Energy::enable(); + + // start hidden + state_invisible = true; + setVisible (false); + + m_current_position = Position (); + character_physics_support()->movement()->DestroyCharacter(); + + m_height = 0.3f; + time_height_updated = 0; + + EnableHide (); +} + +void CPoltergeist::Hide() +{ + if (state_invisible) return; + + state_invisible = true; + setVisible (false); + + m_current_position = Position (); + character_physics_support()->movement()->DestroyCharacter(); + + ability()->on_hide (); +} + +void CPoltergeist::Show() +{ + if (!state_invisible) return; + + state_invisible = false; + + setVisible(TRUE); + + com_man().seq_run(anim().get_motion_id(eAnimMiscAction_00)); + + Position() = m_current_position; + character_physics_support()->movement()->SetPosition(Position()); + character_physics_support()->movement()->CreateCharacter(); + + ability()->on_show (); +} + +void CPoltergeist::UpdateCL() +{ + inherited::UpdateCL(); + def_lerp(m_height, target_height, HEIGHT_CHANGE_VELOCITY, client_update_fdelta()); + + ability()->update_frame (); +} + +void CPoltergeist::ForceFinalAnimation() +{ + if (state_invisible) + anim().SetCurAnim(eAnimMiscAction_01); +} + + +void CPoltergeist::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + CTelekinesis::schedule_update(); + Energy::schedule_update(); + + UpdateHeight(); + + ability()->update_schedule(); +} + + +BOOL CPoltergeist::net_Spawn (CSE_Abstract* DC) +{ + if (!inherited::net_Spawn(DC)) return(FALSE); + + // спаунится нивидимым + setVisible (false); + ability()->on_hide(); + + + return (TRUE); +} + +void CPoltergeist::net_Destroy() +{ + inherited::net_Destroy(); + Energy::disable(); + + ability()->on_destroy(); +} + +void CPoltergeist::Die(CObject* who) +{ + if (m_tele) { + if (state_invisible) { + setVisible(true); + + if (PPhysicsShell()) { + Fmatrix M; + M.set (XFORM()); + M.translate_over (m_current_position); + PPhysicsShell()->SetTransform (M); + } else + Position() = m_current_position; + } + } + + inherited::Die (who); + Energy::disable (); + + ability()->on_die (); +} + +void CPoltergeist::Hit(SHit* pHDS) +{ + ability()->on_hit(pHDS); + inherited::Hit(pHDS); +} + + + +void CPoltergeist::UpdateHeight() +{ + if (!state_invisible) return; + + u32 cur_time = Device.dwTimeGlobal; + + if (time_height_updated < cur_time) { + time_height_updated = cur_time + Random.randI(HEIGHT_CHANGE_MIN_TIME,HEIGHT_CHANGE_MAX_TIME); + target_height = Random.randF(HEIGHT_MIN, HEIGHT_MAX); + } +} + +void CPoltergeist::on_activate() +{ + if (m_disable_hide) return; + + Hide(); + + m_height = 0.3f; + time_height_updated = 0; +} + +void CPoltergeist::on_deactivate() +{ + if (m_disable_hide) return; + + Show(); +} + +CMovementManager *CPoltergeist::create_movement_manager () +{ + m_movement_manager = new CPoltergeisMovementManager(this); + + control().add (m_movement_manager, ControlCom::eControlPath); + control().install_path_manager (m_movement_manager); + control().set_base_controller (m_path_base, ControlCom::eControlPath); + + return (m_movement_manager); +} + + +void CPoltergeist::net_Relcase(CObject *O) +{ + inherited::net_Relcase (O); + CTelekinesis::remove_links (O); +} + + +#ifdef DEBUG +CBaseMonster::SDebugInfo CPoltergeist::show_debug_info() +{ + CBaseMonster::SDebugInfo info = inherited::show_debug_info(); + if (!info.active) return CBaseMonster::SDebugInfo(); + + string128 text; + xr_sprintf(text, "Invisibility Value = [%f]", Energy::get_value()); + DBG().text(this).add_item(text, info.x, info.y+=info.delta_y, info.color); + DBG().text(this).add_item("---------------------------------------", info.x, info.y+=info.delta_y, info.delimiter_color); + + return CBaseMonster::SDebugInfo(); +} +#endif + diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist.h b/src/xrGameLA/ai/monsters/poltergeist/poltergeist.h new file mode 100644 index 000000000..35adf18ff --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist.h @@ -0,0 +1,277 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../telekinesis.h" +#include "../energy_holder.h" +#include "../../../script_export_space.h" + +class CPhysicsShellHolder; +class CStateManagerPoltergeist; +class CPoltergeisMovementManager; +class CPolterSpecialAbility; + +////////////////////////////////////////////////////////////////////////// + +class CPoltergeist : public CBaseMonster , + public CTelekinesis, + public CEnergyHolder { + + typedef CBaseMonster inherited; + typedef CEnergyHolder Energy; + + friend class CPoltergeisMovementManager; + + float m_height; + bool m_disable_hide; + + SMotionVel invisible_vel; + + + CPolterSpecialAbility *m_flame; + CPolterSpecialAbility *m_tele; + + +public: + CPoltergeist (); + virtual ~CPoltergeist (); + + virtual void Load (LPCSTR section); + virtual void reload (LPCSTR section); + virtual void reinit (); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Relcase (CObject *O); + + virtual void UpdateCL (); + virtual void shedule_Update (u32 dt); + + virtual void Die (CObject* who); + + virtual CMovementManager *create_movement_manager(); + + virtual void ForceFinalAnimation (); + + virtual void on_activate (); + virtual void on_deactivate (); + virtual void Hit (SHit* pHDS); + + + IC CPolterSpecialAbility *ability() {return (m_flame ? m_flame : m_tele);} + + + IC bool is_hidden () {return state_invisible;} + + + // Poltergeist ability + void PhysicalImpulse (const Fvector &position); + void StrangeSounds (const Fvector &position); + + ref_sound m_strange_sound; + + // Movement + Fvector m_current_position; // Позиция на ноде + + // Dynamic Height + u32 time_height_updated; + float target_height; + + void UpdateHeight (); + + // Invisibility + + void EnableHide (){m_disable_hide = false;} + void DisableHide (){m_disable_hide = true;} + +private: + void Hide (); + void Show (); + + +public: +#ifdef DEBUG + virtual CBaseMonster::SDebugInfo show_debug_info(); +#endif + + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CPoltergeist) +#undef script_type_list +#define script_type_list save_type_list(CPoltergeist) + + +////////////////////////////////////////////////////////////////////////// +// Interface +////////////////////////////////////////////////////////////////////////// + +class CPolterSpecialAbility { + + CParticlesObject *m_particles_object; + CParticlesObject *m_particles_object_electro; + + LPCSTR m_particles_hidden; + LPCSTR m_particles_damage; + LPCSTR m_particles_death; + LPCSTR m_particles_idle; + + ref_sound m_sound_base; + u32 m_last_hit_frame; + +protected: + CPoltergeist *m_object; + +public: + CPolterSpecialAbility (CPoltergeist *polter); + virtual ~CPolterSpecialAbility (); + + virtual void load (LPCSTR section); + virtual void update_schedule (); + virtual void update_frame (); + virtual void on_hide (); + virtual void on_show (); + virtual void on_destroy (){} + virtual void on_die (); + virtual void on_hit (SHit* pHDS); +}; + + + +////////////////////////////////////////////////////////////////////////// +// Flame +////////////////////////////////////////////////////////////////////////// +class CPolterFlame : public CPolterSpecialAbility { + + typedef CPolterSpecialAbility inherited; + + ref_sound m_sound; + LPCSTR m_particles_prepare; + LPCSTR m_particles_fire; + LPCSTR m_particles_stop; + u32 m_time_fire_delay; + u32 m_time_fire_play; + + float m_length; + float m_hit_value; + u32 m_hit_delay; + + u32 m_count; + u32 m_delay; // between 2 flames + + u32 m_time_flame_started; + + float m_min_flame_dist; + float m_max_flame_dist; + float m_min_flame_height; + float m_max_flame_height; + + float m_pmt_aura_radius; + + + // Scanner + float m_scan_radius; + u32 m_scan_delay_min; + u32 m_scan_delay_max; + + SPPInfo m_scan_effector_info; + float m_scan_effector_time; + float m_scan_effector_time_attack; + float m_scan_effector_time_release; + ref_sound m_scan_sound; + + bool m_state_scanning; + u32 m_scan_next_time; + + + enum EFlameState { + ePrepare, + eFire, + eStop + }; + + +public: + struct SFlameElement { + const CObject *target_object; + Fvector position; + Fvector target_dir; + u32 time_started; + ref_sound sound; + CParticlesObject *particles_object; + EFlameState state; + u32 time_last_hit; + }; + + +private: + DEFINE_VECTOR (SFlameElement*, FLAME_ELEMS_VEC, FLAME_ELEMS_IT); + FLAME_ELEMS_VEC m_flames; + +public: + CPolterFlame (CPoltergeist *polter); + virtual ~CPolterFlame (); + + virtual void load (LPCSTR section); + virtual void update_schedule (); + virtual void on_destroy (); + virtual void on_die (); + +private: + void select_state (SFlameElement *elem, EFlameState state); + bool get_valid_flame_position (const CObject *target_object, Fvector &res_pos); + void create_flame (const CObject *target_object); +}; + + +////////////////////////////////////////////////////////////////////////// +// TELE +////////////////////////////////////////////////////////////////////////// +class CPolterTele : public CPolterSpecialAbility { + typedef CPolterSpecialAbility inherited; + + xr_vector m_nearest; + + // external params + float m_pmt_radius; + float m_pmt_object_min_mass; + float m_pmt_object_max_mass; + u32 m_pmt_object_count; + u32 m_pmt_time_to_hold; + u32 m_pmt_time_to_wait; + u32 m_pmt_time_to_wait_in_objects; + u32 m_pmt_raise_time_to_wait_in_objects; + float m_pmt_distance; + float m_pmt_object_height; + u32 m_pmt_time_object_keep; + float m_pmt_raise_speed; + float m_pmt_fly_velocity; + + ref_sound m_sound_tele_hold; + ref_sound m_sound_tele_throw; + + + enum ETeleState { + eStartRaiseObjects, + eRaisingObjects, + eFireObjects, + eWait + } m_state; + + u32 m_time; + u32 m_time_next; + +public: + CPolterTele (CPoltergeist *polter); + virtual ~CPolterTele (); + + virtual void load (LPCSTR section); + virtual void update_schedule (); + +private: + void tele_find_objects (xr_vector &objects, const Fvector &pos); + bool tele_raise_objects (); + void tele_fire_objects (); + + bool trace_object (CObject *obj, const Fvector &target); +}; + diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_ability.cpp b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_ability.cpp new file mode 100644 index 000000000..e374c71ad --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_ability.cpp @@ -0,0 +1,157 @@ +#include "stdafx.h" +#include "poltergeist.h" +#include "../../../PhysicsShell.h" +#include "../../../level.h" +#include "../../../material_manager.h" +#include "../../../level_debug.h" + + +CPolterSpecialAbility::CPolterSpecialAbility(CPoltergeist *polter) +{ + m_object = polter; + + m_particles_object = 0; + m_particles_object_electro = 0; +} + + +CPolterSpecialAbility::~CPolterSpecialAbility() +{ + CParticlesObject::Destroy (m_particles_object); + CParticlesObject::Destroy (m_particles_object_electro); +} + +void CPolterSpecialAbility::load(LPCSTR section) +{ + m_particles_hidden = pSettings->r_string(section,"Particles_Hidden"); + m_particles_damage = pSettings->r_string(section,"Particles_Damage"); + m_particles_death = pSettings->r_string(section,"Particles_Death"); + m_particles_idle = pSettings->r_string(section,"Particles_Idle"); + + m_sound_base.create (pSettings->r_string(section,"Sound_Idle"), st_Effect, SOUND_TYPE_MONSTER_TALKING); + + m_last_hit_frame = 0; +} + +void CPolterSpecialAbility::update_schedule() +{ + if (m_object->g_Alive()) { + if (!m_sound_base._feedback()) m_sound_base.play_at_pos(m_object, m_object->Position()); + else m_sound_base.set_position(m_object->Position()); + } +} + +void CPolterSpecialAbility::on_hide() +{ + VERIFY(m_particles_object == 0); + if (!m_object->g_Alive()) + return; + m_particles_object = m_object->PlayParticles (m_particles_hidden, m_object->Position(),Fvector().set(0.0f,0.1f,0.0f), false); + m_particles_object_electro = m_object->PlayParticles (m_particles_idle, m_object->Position(),Fvector().set(0.0f,0.1f,0.0f), false); +} + +void CPolterSpecialAbility::on_show() +{ + if (m_particles_object) CParticlesObject::Destroy(m_particles_object); + if (m_particles_object_electro) CParticlesObject::Destroy(m_particles_object_electro); +} + +void CPolterSpecialAbility::update_frame() +{ + if (m_particles_object) m_particles_object->SetXFORM (m_object->XFORM()); + if (m_particles_object_electro) m_particles_object_electro->SetXFORM(m_object->XFORM()); +} + +void CPolterSpecialAbility::on_die() +{ + Fvector particles_position = m_object->m_current_position; + particles_position.y += m_object->target_height; + + m_object->PlayParticles (m_particles_death, particles_position, Fvector().set(0.0f,1.0f,0.0f), TRUE, FALSE); + + CParticlesObject::Destroy (m_particles_object_electro); + CParticlesObject::Destroy (m_particles_object); +} + +void CPolterSpecialAbility::on_hit(SHit* pHDS) +{ + if (m_object->g_Alive() && (pHDS->hit_type == ALife::eHitTypeFireWound) && (Device.dwFrame != m_last_hit_frame)) { + if(BI_NONE != pHDS->bone()) { + + //вычислить координаты попадания + IKinematics* V = smart_cast(m_object->Visual()); + + Fvector start_pos = pHDS->bone_space_position(); + Fmatrix& m_bone = V->LL_GetBoneInstance(pHDS->bone()).mTransform; + m_bone.transform_tiny (start_pos); + m_object->XFORM().transform_tiny (start_pos); + + m_object->PlayParticles(m_particles_damage, start_pos, Fvector().set(0.f,1.f,0.f)); + } + } + + m_last_hit_frame = Device.dwFrame; +} + + +////////////////////////////////////////////////////////////////////////// +// Other +////////////////////////////////////////////////////////////////////////// + + +#define IMPULSE 10.f +#define IMPULSE_RADIUS 5.f +#define TRACE_DISTANCE 10.f +#define TRACE_ATTEMPT_COUNT 3 + +void CPoltergeist::PhysicalImpulse (const Fvector &position) +{ + m_nearest.clear_not_free (); + Level().ObjectSpace.GetNearest (m_nearest,position, IMPULSE_RADIUS, NULL); + //xr_vector &m_nearest = Level().ObjectSpace.q_nearest; + if (m_nearest.empty()) return; + + u32 index = Random.randI (m_nearest.size()); + + CPhysicsShellHolder *obj = smart_cast(m_nearest[index]); + if (!obj || !obj->m_pPhysicsShell) return; + + Fvector dir; + dir.sub(obj->Position(), position); + dir.normalize(); + + CPhysicsElement* E=obj->m_pPhysicsShell->get_ElementByStoreOrder(u16(Random.randI(obj->m_pPhysicsShell->get_ElementsNumber()))); + //E->applyImpulse(dir,IMPULSE * obj->m_pPhysicsShell->getMass()); + E->applyImpulse(dir,IMPULSE * E->getMass()); +} + +void CPoltergeist::StrangeSounds(const Fvector &position) +{ + if (m_strange_sound._feedback()) return; + + for (u32 i = 0; i < TRACE_ATTEMPT_COUNT; i++) { + Fvector dir; + dir.random_dir(); + + collide::rq_result l_rq; + if (Level().ObjectSpace.RayPick(position, dir, TRACE_DISTANCE, collide::rqtStatic, l_rq, NULL)) { + if (l_rq.range < TRACE_DISTANCE) { + + // Получить пару материалов + CDB::TRI* pTri = Level().ObjectSpace.GetStaticTris() + l_rq.element; + SGameMtlPair* mtl_pair = GMLib.GetMaterialPair(material().self_material_idx(),pTri->material); + if (!mtl_pair) continue; + + // Играть звук + if (!mtl_pair->CollideSounds.empty()) { + CLONE_MTL_SOUND(m_strange_sound, mtl_pair, CollideSounds); + Fvector pos; + pos.mad(position, dir, ((l_rq.range - 0.1f > 0) ? l_rq.range - 0.1f : l_rq.range)); + m_strange_sound.play_at_pos(this,pos); + return; + } + } + } + } +} + diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_flame_thrower.cpp b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_flame_thrower.cpp new file mode 100644 index 000000000..166b29d2e --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_flame_thrower.cpp @@ -0,0 +1,340 @@ +#include "stdafx.h" +#include "poltergeist.h" +#include "../../../xrmessages.h" +#include "../../../ai_object_location.h" +#include "../../../level_graph.h" +#include "../../../level.h" +#include "../../../../../xrNetServer/net_utils.h" +#include "../../../ai_space.h" +#include "../../../restricted_object.h" +#include "../../../actor.h" +#include "../../../actorEffector.h" +#include "../ai_monster_effector.h" + +CPolterFlame::CPolterFlame(CPoltergeist *polter) : inherited (polter) +{ +} + +CPolterFlame::~CPolterFlame() +{ +} + + + +void CPolterFlame::load(LPCSTR section) +{ + inherited::load(section); + + m_sound.create (pSettings->r_string(section,"flame_sound"), st_Effect,SOUND_TYPE_WORLD); + + m_particles_prepare = pSettings->r_string(section,"flame_particles_prepare"); + m_particles_fire = pSettings->r_string(section,"flame_particles_fire"); + m_particles_stop = pSettings->r_string(section,"flame_particles_stop"); + + m_time_fire_delay = pSettings->r_u32(section,"flame_fire_time_delay"); + m_time_fire_play = pSettings->r_u32(section,"flame_fire_time_play"); + + m_length = pSettings->r_float(section,"flame_length"); + m_hit_value = pSettings->r_float(section,"flame_hit_value"); + m_hit_delay = pSettings->r_u32(section,"flame_hit_delay"); + + m_count = pSettings->r_u32(section,"flames_count"); + m_delay = pSettings->r_u32(section,"flames_delay"); + + m_min_flame_dist = pSettings->r_float(section,"flame_min_dist"); + m_max_flame_dist = pSettings->r_float(section,"flame_max_dist"); + m_min_flame_height = pSettings->r_float(section,"flame_min_height"); + m_max_flame_height = pSettings->r_float(section,"flame_max_height"); + + m_pmt_aura_radius = pSettings->r_float(section,"flame_aura_radius"); + + //----------------------------------------------------------------------------------------- + // Scanner + m_scan_radius = pSettings->r_float(section,"flame_scan_radius"); + read_delay (section,"flame_scan_delay_min_max",m_scan_delay_min, m_scan_delay_max); + + + // load scan effector + LPCSTR ppi_section = pSettings->r_string(section, "flame_scan_effector_section"); + m_scan_effector_info.duality.h = pSettings->r_float(ppi_section,"duality_h"); + m_scan_effector_info.duality.v = pSettings->r_float(ppi_section,"duality_v"); + m_scan_effector_info.gray = pSettings->r_float(ppi_section,"gray"); + m_scan_effector_info.blur = pSettings->r_float(ppi_section,"blur"); + m_scan_effector_info.noise.intensity = pSettings->r_float(ppi_section,"noise_intensity"); + m_scan_effector_info.noise.grain = pSettings->r_float(ppi_section,"noise_grain"); + m_scan_effector_info.noise.fps = pSettings->r_float(ppi_section,"noise_fps"); + VERIFY(!fis_zero(m_scan_effector_info.noise.fps)); + + sscanf(pSettings->r_string(ppi_section,"color_base"), "%f,%f,%f", &m_scan_effector_info.color_base.r, &m_scan_effector_info.color_base.g, &m_scan_effector_info.color_base.b); + sscanf(pSettings->r_string(ppi_section,"color_gray"), "%f,%f,%f", &m_scan_effector_info.color_gray.r, &m_scan_effector_info.color_gray.g, &m_scan_effector_info.color_gray.b); + sscanf(pSettings->r_string(ppi_section,"color_add"), "%f,%f,%f", &m_scan_effector_info.color_add.r, &m_scan_effector_info.color_add.g, &m_scan_effector_info.color_add.b); + + m_scan_effector_time = pSettings->r_float(ppi_section,"time"); + m_scan_effector_time_attack = pSettings->r_float(ppi_section,"time_attack"); + m_scan_effector_time_release = pSettings->r_float(ppi_section,"time_release"); + + m_scan_sound.create (pSettings->r_string(section,"flame_scan_sound"), st_Effect,SOUND_TYPE_WORLD); + //----------------------------------------------------------------------------------------- + + m_state_scanning = false; + m_scan_next_time = 0; + + + m_time_flame_started = 0; +} + +void CPolterFlame::create_flame(const CObject *target_object) +{ + Fvector position; + if (!get_valid_flame_position(target_object, position)) return; + + SFlameElement *element = new SFlameElement(); + + element->position = position; + element->target_object = target_object; + element->time_started = time(); + element->sound.clone (m_sound, st_Effect,SOUND_TYPE_WORLD); + element->sound.play_at_pos (m_object,element->position); + element->particles_object = 0; + element->time_last_hit = 0; + + Fvector target_point = get_head_position(const_cast(target_object)); + element->target_dir.sub (target_point, element->position); + element->target_dir.normalize (); + + m_flames.push_back (element); + select_state (element, ePrepare); + + m_time_flame_started = time(); +} + +void CPolterFlame::select_state(SFlameElement *elem, EFlameState state) +{ + elem->state = state; + elem->time_started = time(); + + switch(elem->state) { + case ePrepare: + // start prepare particles + m_object->PlayParticles(m_particles_prepare,elem->position,elem->target_dir,TRUE); + break; + case eFire: + // start fire particles + elem->particles_object = m_object->PlayParticles(m_particles_fire,elem->position,elem->target_dir,FALSE); + break; + case eStop: + // stop fire particles + if (elem->particles_object) CParticlesObject::Destroy(elem->particles_object); + + // start finish particles + m_object->PlayParticles(m_particles_stop,elem->position,elem->target_dir,TRUE); + + break; + } +} + +struct remove_predicate { + bool operator() (CPolterFlame::SFlameElement *element) { + return (!element); + } +}; + + +void CPolterFlame::update_schedule() +{ + inherited::update_schedule(); + + //--------------------------------------------------------------------- + // Update Scanner + + if (m_object->g_Alive()) { + + // check the start of scanning + if (!m_state_scanning && !m_object->EnemyMan.get_enemy()) { + // check radius + if (Actor()->Position().distance_to(m_object->Position()) < m_scan_radius) { + // check timing + if (m_scan_next_time < time()) { + // start here + m_state_scanning = true; + + // играть звук + //m_scan_sound.play_at_pos(m_object, get_head_position(Actor()),sm_2D); + ::Sound->play_at_pos(m_scan_sound, 0, Actor()->Position()); + + // постпроцесс + Actor()->Cameras().AddPPEffector(new CMonsterEffector(m_scan_effector_info, m_scan_effector_time, m_scan_effector_time_attack, m_scan_effector_time_release)); + } + + } + } + // check stop of scanning (it currently scans) + else { + if (!m_scan_sound._feedback()) { + // stop here + m_state_scanning = false; + + // count next scan time + m_scan_next_time = time() + Random.randI(m_scan_delay_min,m_scan_delay_max); + } + } + } + //--------------------------------------------------------------------- + + + // check all flames + for (FLAME_ELEMS_IT it = m_flames.begin();it != m_flames.end();it++) { + SFlameElement *elem = *it; + + // test switches to states + switch(elem->state) { + case ePrepare: + // check if time_out + if (elem->time_started + m_time_fire_delay < time()) select_state(elem,eFire); + break; + case eFire: + if (elem->time_started + m_time_fire_play < time()) select_state(elem,eStop); + else { + + // check if we need test hit to enemy + if (elem->time_last_hit + m_hit_delay < time()) { + // test hit + collide::rq_result rq; + if (Level().ObjectSpace.RayPick(elem->position, elem->target_dir, m_length, collide::rqtBoth, rq, NULL)) { + if ((rq.O == elem->target_object) && (rq.range < m_length)) { + float hit_value; + hit_value = m_hit_value - m_hit_value * rq.range / m_length; + + NET_Packet P; + SHit HS; + HS.GenHeader (GE_HIT, elem->target_object->ID()); // u_EventGen (P,GE_HIT, element->target_object->ID()); + HS.whoID = (m_object->ID()); // P.w_u16 (ID()); + HS.weaponID = (m_object->ID()); // P.w_u16 (ID()); + HS.dir = (elem->target_dir); // P.w_dir (element->target_dir); + HS.power = (hit_value); // P.w_float (m_flame_hit_value); + HS.boneID = (BI_NONE); // P.w_s16 (BI_NONE); + HS.p_in_bone_space = (Fvector().set(0.f,0.f,0.f)); // P.w_vec3 (Fvector().set(0.f,0.f,0.f)); + HS.impulse = (0.f); // P.w_float (0.f); + HS.hit_type = (ALife::eHitTypeBurn); // P.w_u16 (u16(ALife::eHitTypeBurn)); + + HS.Write_Packet (P); + m_object->u_EventSend (P); + + elem->time_last_hit = time(); + } + } + } + } + break; + case eStop: + xr_delete(*it); + break; + } + } + + // remove all flames in state stop + + // удалить все элементы, выполнение которых закончено + m_flames.erase ( + std::remove_if( + m_flames.begin(), + m_flames.end(), + remove_predicate() + ), + m_flames.end() + ); + + // check if we can create another flame + if (m_object->g_Alive() && m_object->EnemyMan.get_enemy() && (m_flames.size() < m_count)) { + // check aura radius and accessibility + float dist = m_object->EnemyMan.get_enemy()->Position().distance_to(m_object->Position()); + if ((dist < m_pmt_aura_radius) && m_object->control().path_builder().accessible(m_object->EnemyMan.get_enemy()->Position())) { + // check timing + if (m_time_flame_started + m_delay < time()) { + create_flame(m_object->EnemyMan.get_enemy()); + } + } + } + + + +} +void CPolterFlame::on_destroy() +{ + inherited::on_destroy(); + + FLAME_ELEMS_IT I = m_flames.begin(); + FLAME_ELEMS_IT E = m_flames.end(); + + // Пройти по всем объектам и проверить на хит врага + for ( ;I != E; ++I) { + if ((*I)->sound._feedback()) (*I)->sound.stop(); + if ((*I)->particles_object) CParticlesObject::Destroy((*I)->particles_object); + + xr_delete((*I)); + } + + m_flames.clear(); + + if (m_scan_sound._feedback()) m_scan_sound.stop(); +} + +void CPolterFlame::on_die() +{ + inherited::on_die(); + if (m_scan_sound._feedback()) m_scan_sound.stop(); +} + + + +#define FIND_POINT_ATTEMPT_COUNT 5 + +bool CPolterFlame::get_valid_flame_position(const CObject *target_object, Fvector &res_pos) +{ + const CGameObject *Obj = smart_cast(target_object); + if (!Obj) return (false); + + Fvector dir; + float h,p; + + Fvector vertex_position; + Fvector new_pos; + + for (u32 i=0; iDirection().getHP(h,p); + h = Random.randF(0, PI_MUL_2); + dir.setHP(h,p); + dir.normalize(); + + vertex_position = ai().level_graph().vertex_position(Obj->ai_location().level_vertex_id()); + new_pos.mad(vertex_position, dir, Random.randF(m_min_flame_dist, m_max_flame_dist)); + + u32 node = ai().level_graph().check_position_in_direction(Obj->ai_location().level_vertex_id(), vertex_position, new_pos); + if (node != u32(-1)) { + res_pos = ai().level_graph().vertex_position(node); + res_pos.y += Random.randF(m_min_flame_height, m_max_flame_height); + return (true); + } + } + + + float angle = ai().level_graph().vertex_cover_angle(Obj->ai_location().level_vertex_id(),PI_DIV_6,std::less()); + + dir.set(1.f,0.f,0.f); + dir.setHP(angle + PI, 0.f); + dir.normalize(); + + vertex_position = ai().level_graph().vertex_position(Obj->ai_location().level_vertex_id()); + new_pos.mad(vertex_position, dir, Random.randF(m_min_flame_dist, m_max_flame_dist)); + + u32 node = ai().level_graph().check_position_in_direction(Obj->ai_location().level_vertex_id(), vertex_position, new_pos); + if (node != u32(-1)) { + res_pos = ai().level_graph().vertex_position(node); + res_pos.y += Random.randF(m_min_flame_height, m_max_flame_height); + return (true); + } + + return (false); +} + diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_movement.cpp b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_movement.cpp new file mode 100644 index 000000000..65522a5d4 --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_movement.cpp @@ -0,0 +1,114 @@ +#include "stdafx.h" +#include "poltergeist_movement.h" +#include "poltergeist.h" +#include "../../../detail_path_manager.h" + +void CPoltergeisMovementManager::move_along_path(CPHMovementControl *movement_control, Fvector &dest_position, float time_delta) +{ + if (!m_monster->is_hidden()) { + inherited::move_along_path(movement_control, dest_position, time_delta); + return; + } + + dest_position = m_monster->m_current_position; + + // Если нет движения по пути + if (!enabled() || + path_completed() || + detail().path().empty() || + detail().completed(m_monster->m_current_position,true) || + (detail().curr_travel_point_index() >= detail().path().size() - 1) || + fis_zero(old_desirable_speed()) + ) + { + m_speed = 0.f; + dest_position = CalculateRealPosition(); + return; + } + + if (time_delta < EPS) { + dest_position = CalculateRealPosition(); + return; + } + + // Вычислить пройденную дистанцию, определить целевую позицию на маршруте, + // изменить detail().curr_travel_point_index() + + float desirable_speed = old_desirable_speed(); // желаемая скорость объекта + float dist = desirable_speed * time_delta; // пройденное расстояние в соостветствие с желаемой скоростью + float desirable_dist = dist; + + // определить целевую точку + Fvector target; + + u32 prev_cur_point_index = detail().curr_travel_point_index(); + + // обновить detail().curr_travel_point_index() в соответствие с текущей позицией + while (detail().curr_travel_point_index() < detail().path().size() - 2) { + + float pos_dist_to_cur_point = dest_position.distance_to(detail().path()[detail().curr_travel_point_index()].position); + float pos_dist_to_next_point = dest_position.distance_to(detail().path()[detail().curr_travel_point_index()+1].position); + float cur_point_dist_to_next_point = detail().path()[detail().curr_travel_point_index()].position.distance_to(detail().path()[detail().curr_travel_point_index()+1].position); + + if ((pos_dist_to_cur_point > cur_point_dist_to_next_point) && (pos_dist_to_cur_point > pos_dist_to_next_point)) { + ++detail().m_current_travel_point; + } else break; + } + + target.set (detail().path()[detail().curr_travel_point_index() + 1].position); + // определить направление к целевой точке + Fvector dir_to_target; + dir_to_target.sub (target, dest_position); + + // дистанция до целевой точки + float dist_to_target = dir_to_target.magnitude(); + + while (dist > dist_to_target) { + dest_position.set (target); + + if (detail().curr_travel_point_index() + 1 >= detail().path().size()) break; + else { + dist -= dist_to_target; + ++detail().m_current_travel_point; + if ((detail().curr_travel_point_index()+1) >= detail().path().size()) + break; + target.set (detail().path()[detail().curr_travel_point_index() + 1].position); + dir_to_target.sub (target, dest_position); + dist_to_target = dir_to_target.magnitude(); + } + } + + if (prev_cur_point_index != detail().curr_travel_point_index()) on_travel_point_change(prev_cur_point_index); + + if (dist_to_target < EPS_L) { + detail().m_current_travel_point = detail().path().size() - 1; + m_speed = 0.f; + dest_position = CalculateRealPosition(); + return; + } + + // установить позицию + Fvector motion; + motion.mul (dir_to_target, dist / dist_to_target); + dest_position.add (motion); + + // установить скорость + float real_motion = motion.magnitude() + desirable_dist - dist; + float real_speed = real_motion / time_delta; + + m_speed = 0.5f * desirable_speed + 0.5f * real_speed; + + // Обновить позицию + m_monster->m_current_position = dest_position; + m_monster->Position() = CalculateRealPosition(); + dest_position = m_monster->Position(); +} + +Fvector CPoltergeisMovementManager::CalculateRealPosition() +{ + Fvector ret_val = m_monster->m_current_position; + ret_val.y += m_monster->m_height; + return (ret_val); +} + + diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_movement.h b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_movement.h new file mode 100644 index 000000000..fbbeea538 --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_movement.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../control_path_builder.h" + +class CPoltergeist; +class CCustomMonster; + +class CPoltergeisMovementManager : public CControlPathBuilder { + typedef CControlPathBuilder inherited; + + CPoltergeist *m_monster; + +public: + CPoltergeisMovementManager (CPoltergeist *monster) : inherited((CCustomMonster*)monster), m_monster(monster) {} + virtual ~CPoltergeisMovementManager (){} + + virtual void move_along_path (CPHMovementControl *movement_control, Fvector &dest_position, float time_delta); + + Fvector CalculateRealPosition (); +}; + diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_script.cpp b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_script.cpp new file mode 100644 index 000000000..b4cdbb510 --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "poltergeist.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CPoltergeist::script_register(lua_State *L) +{ + module(L) + [ + class_("CPoltergeist") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_attack_hidden.h b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_attack_hidden.h new file mode 100644 index 000000000..36d251eb7 --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_attack_hidden.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../state.h" + +template +class CStatePoltergeistAttackHidden : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + struct { + Fvector point; + u32 node; + } m_target; + + +public: + CStatePoltergeistAttackHidden (_Object *obj) : inherited(obj) {} + virtual ~CStatePoltergeistAttackHidden () {} + + + virtual void initialize (); + virtual void execute (); + +private: + + void select_target_point (); +}; + +#include "poltergeist_state_attack_hidden_inline.h" diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_attack_hidden_inline.h b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_attack_hidden_inline.h new file mode 100644 index 000000000..7b61190da --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_attack_hidden_inline.h @@ -0,0 +1,56 @@ +#pragma once + +#include "../../../sound_player.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStatePoltergeistAttackHiddenAbstract CStatePoltergeistAttackHidden<_Object> + +#define FLY_AROUND_DIST 15.f +#define DIST_TO_PATH_END 1.5f + + +TEMPLATE_SPECIALIZATION +void CStatePoltergeistAttackHiddenAbstract::initialize() +{ + inherited::initialize(); + select_target_point(); + object->path().prepare_builder(); +} + +TEMPLATE_SPECIALIZATION +void CStatePoltergeistAttackHiddenAbstract::execute() +{ + // проверить на завершение пути + if (object->control().path_builder().detail().time_path_built() > time_state_started) { + if (object->control().path_builder().is_path_end(DIST_TO_PATH_END)) select_target_point(); + } + + object->path().set_target_point (m_target.point, m_target.node); + object->path().set_rebuild_time (5000); + object->path().set_distance_to_end (3.f); + object->path().set_use_covers (false); + + object->anim().m_tAction = ACT_RUN; + object->anim().accel_activate (eAT_Aggressive); + object->anim().accel_set_braking (false); + object->sound().play (MonsterSound::eMonsterSoundAggressive, 0,0,object->db().m_dwAttackSndDelay); +} + +TEMPLATE_SPECIALIZATION +void CStatePoltergeistAttackHiddenAbstract::select_target_point() +{ + float dist = object->Position().distance_to(object->EnemyMan.get_enemy_position()); + if (dist > FLY_AROUND_DIST) { + m_target.point = object->EnemyMan.get_enemy_position(); + m_target.node = object->EnemyMan.get_enemy_vertex(); + } else { + m_target.point = random_position(object->EnemyMan.get_enemy_position(), FLY_AROUND_DIST / 2); + m_target.node = u32(-1); + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStatePoltergeistAttackHiddenAbstract \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_manager.cpp b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_manager.cpp new file mode 100644 index 000000000..9bd63fa7a --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_manager.cpp @@ -0,0 +1,127 @@ +#include "stdafx.h" +#include "poltergeist.h" +#include "poltergeist_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "poltergeist_state_rest.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "poltergeist_state_attack_hidden.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../../../entitycondition.h" + +CStateManagerPoltergeist::CStateManagerPoltergeist(CPoltergeist *obj) : inherited(obj) +{ + add_state(eStateRest, new CPoltergeistStateRest(obj)); + add_state(eStateEat, new CStateMonsterEat(obj)); + add_state(eStateAttack, new CStateMonsterAttack(obj)); + add_state(eStateAttack_AttackHidden, new CStatePoltergeistAttackHidden(obj)); + add_state(eStatePanic, new CStateMonsterPanic(obj)); + add_state(eStateHitted, new CStateMonsterHitted(obj)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(obj)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(obj)); +} + +CStateManagerPoltergeist::~CStateManagerPoltergeist() +{ +} + +void CStateManagerPoltergeist::reinit() +{ + inherited::reinit(); + + time_next_flame_attack = 0; + time_next_tele_attack = 0; + time_next_scare_attack = 0; + +} + +void CStateManagerPoltergeist::execute() +{ + u32 state_id = u32(-1); + + //const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + //if (enemy) { + // if (object->is_hidden()) state_id = eStateAttack_AttackHidden; + // else { + // switch (object->EnemyMan.get_danger_type()) { + // case eStrong: state_id = eStatePanic; break; + // case eWeak: state_id = eStateAttack; break; + // } + // } + //} else if (object->HitMemory.is_hit() && !object->is_hidden()) { + // state_id = eStateHitted; + //} else if (object->hear_dangerous_sound) { + // if (!object->is_hidden()) state_id = eStateHearDangerousSound; + // else state_id = eStateHearInterestingSound; + //} else if (object->hear_interesting_sound ) { + // state_id = eStateHearInterestingSound; + //} else { + // if (can_eat()) state_id = eStateEat; + // else state_id = eStateRest; + // + // if (state_id == eStateEat) { + // if (object->CorpseMan.get_corpse()->Position().distance_to(object->Position()) < 5.f) { + // if (object->is_hidden()) { + // object->CEnergyHolder::deactivate(); + // } + // + // object->DisableHide(); + // } + // } + + //} + + ////if (state_id == eStateAttack_AttackHidden) polter_attack(); + + //if ((prev_substate == eStateEat) && (state_id != eStateEat)) + // object->EnableHide(); + + state_id = eStateRest; + + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} + +#define TIME_SEEN_FOR_FIRE 5000 + +void CStateManagerPoltergeist::polter_attack() +{ + //u32 cur_time = Device.dwTimeGlobal; + //const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + // + //bool b_aggressive = object->conditions().GetHealth() < 0.5f; + + //if ((time_next_flame_attack < cur_time) && (object->EnemyMan.get_enemy_time_last_seen() + TIME_SEEN_FOR_FIRE > cur_time)) { + // + + // object->FireFlame(enemy); + // time_next_flame_attack = cur_time + Random.randI(object->m_flame_delay.min, (b_aggressive) ? object->m_flame_delay.aggressive : object->m_flame_delay.normal); + //} + + //if (time_next_tele_attack < cur_time) { + // //object->ProcessTelekinesis(enemy); + // time_next_tele_attack = cur_time + Random.randI(object->m_tele_delay.min, (b_aggressive) ? object->m_tele_delay.aggressive : object->m_tele_delay.normal); + //} + + //if (time_next_scare_attack < cur_time) { + // if (Random.randI(2)) + // object->PhysicalImpulse(enemy->Position()); + // else + // object->StrangeSounds(enemy->Position()); + // + // time_next_scare_attack = cur_time + Random.randI(object->m_scare_delay.min, (b_aggressive) ? object->m_scare_delay.aggressive : object->m_scare_delay.normal); + //} +} diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_manager.h b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_manager.h new file mode 100644 index 000000000..41dc57cbd --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_manager.h @@ -0,0 +1,27 @@ +#pragma once +#include "../monster_state_manager.h" + +class CPoltergeist; + +class CStateManagerPoltergeist : public CMonsterStateManager { + typedef CMonsterStateManager inherited; + + +public: + CStateManagerPoltergeist (CPoltergeist *obj); + virtual ~CStateManagerPoltergeist (); + + virtual void reinit (); + virtual void execute (); + +private: + + u32 time_next_flame_attack; + u32 time_next_tele_attack; + u32 time_next_scare_attack; + + void polter_attack (); + + + +}; diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_rest.h b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_rest.h new file mode 100644 index 000000000..5b53390ca --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_state_rest.h @@ -0,0 +1,56 @@ +#pragma once + +#include "../states/monster_state_rest.h" + +template +class CPoltergeistStateRest : public CStateMonsterRest<_Object> { +protected: + typedef CStateMonsterRest<_Object> inherited; +public: + CPoltergeistStateRest (_Object *obj) : inherited(obj) {} + virtual void execute (); +}; + +template +void CPoltergeistStateRest<_Object>::execute() +{ + // check alife control + bool captured_by_smart_terrain = false; + + if (prev_substate == eStateSmartTerrainTask) { + if (!get_state(eStateSmartTerrainTask)->check_completion()) + captured_by_smart_terrain = true; + } else if (get_state(eStateSmartTerrainTask)->check_start_conditions()) + captured_by_smart_terrain = true; + + if (captured_by_smart_terrain) select_state(eStateSmartTerrainTask); + else { + // check restrictions + bool move_to_restrictor = false; + + if (prev_substate == eStateCustomMoveToRestrictor) { + if (!get_state(eStateCustomMoveToRestrictor)->check_completion()) + move_to_restrictor = true; + } else if (get_state(eStateCustomMoveToRestrictor)->check_start_conditions()) + move_to_restrictor = true; + + if (move_to_restrictor) select_state(eStateCustomMoveToRestrictor); + else { + // check home point + bool move_to_home_point = false; + + if (prev_substate == eStateRest_MoveToHomePoint) { + if (!get_state(eStateRest_MoveToHomePoint)->check_completion()) + move_to_home_point = true; + } else if (get_state(eStateRest_MoveToHomePoint)->check_start_conditions()) + move_to_home_point = true; + + if (move_to_home_point) select_state (eStateRest_MoveToHomePoint); + else select_state (eStateRest_WalkGraphPoint); + } + } + + get_state_current()->execute(); + prev_substate = current_substate; +} + diff --git a/src/xrGameLA/ai/monsters/poltergeist/poltergeist_telekinesis.cpp b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_telekinesis.cpp new file mode 100644 index 000000000..524e4f03e --- /dev/null +++ b/src/xrGameLA/ai/monsters/poltergeist/poltergeist_telekinesis.cpp @@ -0,0 +1,268 @@ +#include "stdafx.h" +#include "poltergeist.h" +#include "../../../PhysicsShellHolder.h" +#include "../../../level.h" +#include "../../../actor.h" + +CPolterTele::CPolterTele(CPoltergeist *polter) : inherited (polter) +{ +} + +CPolterTele::~CPolterTele() +{ +} + +void CPolterTele::load(LPCSTR section) +{ + inherited::load(section); + + + m_pmt_radius = READ_IF_EXISTS(pSettings,r_float,section, "Tele_Find_Radius", 10.f); + m_pmt_object_min_mass = READ_IF_EXISTS(pSettings,r_float,section, "Tele_Object_Min_Mass", 40.f); + m_pmt_object_max_mass = READ_IF_EXISTS(pSettings,r_float,section, "Tele_Object_Max_Mass", 500.f); + m_pmt_object_count = READ_IF_EXISTS(pSettings,r_u32,section, "Tele_Object_Count", 10); + m_pmt_time_to_hold = READ_IF_EXISTS(pSettings,r_u32,section, "Tele_Hold_Time", 3000); + m_pmt_time_to_wait = READ_IF_EXISTS(pSettings,r_u32,section, "Tele_Wait_Time", 3000); + m_pmt_time_to_wait_in_objects = READ_IF_EXISTS(pSettings,r_u32,section, "Tele_Delay_Between_Objects_Time", 500); + m_pmt_distance = READ_IF_EXISTS(pSettings,r_float,section, "Tele_Distance", 50.f); + m_pmt_object_height = READ_IF_EXISTS(pSettings,r_float,section, "Tele_Object_Height", 10.f); + m_pmt_time_object_keep = READ_IF_EXISTS(pSettings,r_u32,section, "Tele_Time_Object_Keep", 10000); + m_pmt_raise_speed = READ_IF_EXISTS(pSettings,r_float,section, "Tele_Raise_Speed", 3.f); + m_pmt_raise_time_to_wait_in_objects = READ_IF_EXISTS(pSettings,r_u32,section, "Tele_Delay_Between_Objects_Raise_Time", 500); + m_pmt_fly_velocity = READ_IF_EXISTS(pSettings,r_float,section, "Tele_Fly_Velocity", 30.f); + + ::Sound->create (m_sound_tele_hold, pSettings->r_string(section,"sound_tele_hold"), st_Effect,SOUND_TYPE_WORLD); + ::Sound->create (m_sound_tele_throw, pSettings->r_string(section,"sound_tele_throw"),st_Effect,SOUND_TYPE_WORLD); + + + m_state = eWait; + m_time = 0; + m_time_next = 0; + +} + +void CPolterTele::update_schedule() +{ + inherited::update_schedule(); + + if (!m_object->g_Alive() || !Actor() || !Actor()->g_Alive()) return; + if (Actor()->Position().distance_to(m_object->Position()) > m_pmt_distance) return; + + switch (m_state) { + case eStartRaiseObjects: + if (m_time + m_time_next < time()) { + if (!tele_raise_objects()) + m_state = eRaisingObjects; + + m_time = time(); + m_time_next = m_pmt_raise_time_to_wait_in_objects / 2 + Random.randI(m_pmt_raise_time_to_wait_in_objects / 2); + } + + if (m_state == eStartRaiseObjects) { + if (m_object->CTelekinesis::get_objects_count() >= m_pmt_object_count) { + m_state = eRaisingObjects; + m_time = time(); + } + } + + break; + case eRaisingObjects: + if (m_time + m_pmt_time_to_hold > time()) break; + + m_time = time(); + m_time_next = 0; + m_state = eFireObjects; + case eFireObjects: + if (m_time + m_time_next < time()) { + tele_fire_objects (); + + m_time = time(); + m_time_next = m_pmt_time_to_wait_in_objects / 2 + Random.randI(m_pmt_time_to_wait_in_objects / 2); + } + + if (m_object->CTelekinesis::get_objects_count() == 0) { + m_state = eWait; + m_time = time(); + } + break; + case eWait: + if (m_time + m_pmt_time_to_wait < time()) { + m_time_next = 0; + m_state = eStartRaiseObjects; + } + break; + } +} + + +////////////////////////////////////////////////////////////////////////// +// Выбор подходящих объектов для телекинеза +////////////////////////////////////////////////////////////////////////// +class best_object_predicate { + Fvector enemy_pos; + Fvector monster_pos; +public: + best_object_predicate(const Fvector &m_pos, const Fvector &pos) { + monster_pos = m_pos; + enemy_pos = pos; + } + + bool operator() (const CGameObject *tpObject1, const CGameObject *tpObject2) const + { + + float dist1 = monster_pos.distance_to(tpObject1->Position()); + float dist2 = enemy_pos.distance_to(tpObject2->Position()); + float dist3 = enemy_pos.distance_to(monster_pos); + + return ((dist1 < dist3) && (dist2 > dist3)); + }; +}; + +class best_object_predicate2 { + Fvector enemy_pos; + Fvector monster_pos; +public: + typedef CObject* CObject_ptr; + + best_object_predicate2(const Fvector &m_pos, const Fvector &pos) { + monster_pos = m_pos; + enemy_pos = pos; + } + + bool operator() (const CObject_ptr &tpObject1, const CObject_ptr &tpObject2) const + { + float dist1 = enemy_pos.distance_to(tpObject1->Position()); + float dist2 = enemy_pos.distance_to(tpObject2->Position()); + + return (dist1 < dist2); + }; +}; + +////////////////////////////////////////////////////////////////////////// + +bool CPolterTele::trace_object(CObject *obj, const Fvector &target) +{ + Fvector trace_from; + obj->Center (trace_from); + + Fvector dir; + float range; + dir.sub (target, trace_from); + + range = dir.magnitude(); + dir.normalize (); + + collide::rq_result l_rq; + if (Level().ObjectSpace.RayPick(trace_from, dir, range, collide::rqtBoth, l_rq, obj)) { + if (l_rq.O == Actor()) return true; + } + + return false; +} + + +void CPolterTele::tele_find_objects(xr_vector &objects, const Fvector &pos) +{ + m_nearest.clear_not_free (); + Level().ObjectSpace.GetNearest (m_nearest, pos, m_pmt_radius, NULL); + + for (u32 i=0;i(m_nearest[i]); + CCustomMonster *custom_monster = smart_cast(m_nearest[i]); + if (!obj || + !obj->PPhysicsShell() || + !obj->PPhysicsShell()->isActive()|| + custom_monster || + (obj->spawn_ini() && obj->spawn_ini()->section_exist("ph_heavy")) || + (obj->m_pPhysicsShell->getMass() < m_pmt_object_min_mass) || + (obj->m_pPhysicsShell->getMass() > m_pmt_object_max_mass) || + (obj == m_object) || + m_object->CTelekinesis::is_active_object(obj) || + !obj->m_pPhysicsShell->get_ApplyByGravity()) continue; + + + Fvector center; + Actor()->Center(center); + + if (trace_object(obj, center) || trace_object(obj, get_head_position(Actor()))) + objects.push_back(obj); + } +} + + +bool CPolterTele::tele_raise_objects() +{ + // find objects near actor + xr_vector tele_objects; + tele_objects.reserve (20); + + // получить список объектов вокруг врага + tele_find_objects (tele_objects, Actor()->Position()); + + // получить список объектов вокруг монстра + tele_find_objects (tele_objects, m_object->Position()); + + // получить список объектов между монстром и врагом + float dist = Actor()->Position().distance_to(m_object->Position()); + Fvector dir; + dir.sub (Actor()->Position(), m_object->Position()); + dir.normalize (); + + Fvector pos; + pos.mad (m_object->Position(), dir, dist / 2.f); + tele_find_objects (tele_objects, pos); + + // сортировать и оставить только необходимое количество объектов + std::sort(tele_objects.begin(),tele_objects.end(),best_object_predicate2(m_object->Position(), Actor()->Position())); + + // оставить уникальные объекты + tele_objects.erase ( + std::unique( + tele_objects.begin(), + tele_objects.end() + ), + tele_objects.end() + ); + + // оставить необходимое количество объектов + //if (tele_objects.size() > m_pmt_tele_object_count) tele_objects.resize (m_pmt_tele_object_count); + + //// активировать + //for (u32 i=0; i(tele_objects[i]); + + // // применить телекинез на объект + // bool rotate = false; + + // CTelekinesis::activate (obj, m_pmt_tele_raise_speed, m_pmt_tele_object_height, m_pmt_tele_time_object_keep, rotate); + //} + + if (!tele_objects.empty()) { + CPhysicsShellHolder *obj = smart_cast(tele_objects[0]); + + // применить телекинез на объект + bool rotate = false; + + CTelekineticObject *tele_obj = m_object->CTelekinesis::activate (obj, m_pmt_raise_speed, m_pmt_object_height, m_pmt_time_object_keep, rotate); + tele_obj->set_sound (m_sound_tele_hold,m_sound_tele_throw); + + return true; + } + + return false; +} + +void CPolterTele::tele_fire_objects() +{ + for (u32 i=0; i < m_object->CTelekinesis::get_objects_total_count();i++) { + CTelekineticObject tele_object = m_object->CTelekinesis::get_object_by_index(i); + //if (tele_object.get_state() != TS_Fire) { + if ((tele_object.get_state() == TS_Raise) || (tele_object.get_state() == TS_Keep)) { + Fvector enemy_pos; + enemy_pos = get_head_position(Actor()); + m_object->CTelekinesis::fire_t (tele_object.get_object(),enemy_pos, tele_object.get_object()->Position().distance_to(enemy_pos) / m_pmt_fly_velocity); + return; + } + } +} + diff --git a/src/xrGameLA/ai/monsters/pseudodog/pseudodog.cpp b/src/xrGameLA/ai/monsters/pseudodog/pseudodog.cpp new file mode 100644 index 000000000..e0958eb48 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/pseudodog.cpp @@ -0,0 +1,183 @@ +#include "stdafx.h" +#include "pseudodog.h" +#include "pseudodog_state_manager.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../sound_player.h" +#include "../../../level.h" +#include "../../../ai_monster_space.h" +#include "../monster_velocity_space.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" + +#ifdef _DEBUG +# include +# include "../../../ai_object_location.h" +# include "../../../level_debug.h" +# include "../../../level_graph.h" +# include "../../../ai_space.h" +# include "../../../alife_simulator.h" +# include "../../../xrServer_Object_Base.h" +# include "../../../xrserver.h" +#endif + +CAI_PseudoDog::CAI_PseudoDog() +{ + com_man().add_ability(ControlCom::eControlJump); + com_man().add_ability(ControlCom::eControlRotationJump); +} + +DLL_Pure *CAI_PseudoDog::_construct() +{ + inherited::_construct (); + StateMan = create_state_manager (); + return (this); +} + +CAI_PseudoDog::~CAI_PseudoDog() +{ + xr_delete(StateMan); +} + +void CAI_PseudoDog::reinit() +{ + inherited::reinit(); + + m_time_became_angry = 0; + time_growling = 0; + + if(CCustomMonster::use_simplified_visual()) return; + com_man().add_rotation_jump_data ("1","2","3","4", deg(90)); +} + +void CAI_PseudoDog::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + anim().AddReplacedAnim(&m_bRunTurnLeft, eAnimRun, eAnimRunTurnLeft); + anim().AddReplacedAnim(&m_bRunTurnRight, eAnimRun, eAnimRunTurnRight); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + m_anger_hunger_threshold = pSettings->r_float(section, "anger_hunger_threshold"); + m_anger_loud_threshold = pSettings->r_float(section, "anger_loud_threshold"); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + + // define animation set + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSleep, "lie_sleep_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimLieIdle, "lie_idle_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_dmg_", -1, &velocity_walk_dmg, PS_STAND); + anim().AddAnim(eAnimRun, "stand_run_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND); + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimDragCorpse, "stand_drag_", -1, &velocity_drag, PS_STAND); + anim().AddAnim(eAnimSniff, "stand_sniff_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimHowling, "stand_howling_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimJumpGlide, "jump_glide_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimDie, "stand_die_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSitLieDown, "sit_lie_down_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT); + anim().AddAnim(eAnimLieToSleep, "lie_to_sleep_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimSleepStandUp, "lie_to_stand_up_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimAttackPsi, "stand_psi_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimThreaten, "stand_howling_", -1, &velocity_none, PS_STAND); + + anim().AddAnim(eAnimRunTurnLeft, "stand_run_turn_left_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunTurnRight, "stand_run_turn_right_",-1, &velocity_run, PS_STAND); + + + // define transitions + // order : 1. [anim -> anim] 2. [anim->state] 3. [state -> anim] 4. [state -> state] + anim().AddTransition(eAnimLieIdle, eAnimSleep, eAnimLieToSleep, false); + anim().AddTransition(eAnimSleep, PS_STAND, eAnimSleepStandUp, false); + anim().AddTransition(PS_SIT, PS_LIE, eAnimSitLieDown, false); + anim().AddTransition(PS_STAND, PS_SIT, eAnimStandSitDown, false); + anim().AddTransition(PS_SIT, PS_STAND, eAnimSitStandUp, false); + + // define links from Action to animations + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkBkwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSleep); + anim().LinkAction(ACT_REST, eAnimSitIdle); + anim().LinkAction(ACT_DRAG, eAnimDragCorpse); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimWalkFwd); + anim().LinkAction(ACT_LOOK_AROUND, eAnimSniff); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + + +void CAI_PseudoDog::reload(LPCSTR section) +{ + inherited::reload (section); + + if(CCustomMonster::use_simplified_visual()) return; + + // load additional sounds + sound().add (pSettings->r_string(section,"sound_psy_attack"), DEFAULT_SAMPLE_COUNT, SOUND_TYPE_MONSTER_ATTACKING, MonsterSound::eHighPriority+3, MonsterSound::eBaseChannel, ePsyAttack, "bip01_head"); + + // load jump params + com_man().load_jump_data (0,"run_jamp_0", "run_jamp_1", "run_jamp_2", MonsterMovement::eVelocityParameterRunNormal,MonsterMovement::eVelocityParameterRunNormal,0); + +} + +void CAI_PseudoDog::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_PSI_ATTACK) == ASP_PSI_ATTACK) { + com_man().seq_run(anim().get_motion_id(eAnimAttackPsi)); + } + + if ((spec_params & ASP_THREATEN) == ASP_THREATEN) { + anim().SetCurAnim(eAnimThreaten); + } +} + + +void CAI_PseudoDog::HitEntityInJump (const CEntity *pEntity) +{ + SAAParam ¶ms = anim().AA_GetParams("run_jamp_1"); + HitEntity (pEntity, params.hit_power, params.impulse, params.impulse_dir); +} + + +#ifdef _DEBUG +void CAI_PseudoDog::debug_on_key(int key) +{ +} +#endif + +IStateManagerBase *CAI_PseudoDog::create_state_manager() +{ + return new CStateManagerPseudodog(this); +} + diff --git a/src/xrGameLA/ai/monsters/pseudodog/pseudodog.h b/src/xrGameLA/ai/monsters/pseudodog/pseudodog.h new file mode 100644 index 000000000..6530eb445 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/pseudodog.h @@ -0,0 +1,52 @@ +#pragma once + +#include "../BaseMonster/base_monster.h" +#include "../../../script_export_space.h" + +class CAI_PseudoDog : public CBaseMonster { + typedef CBaseMonster inherited; + +public: + + float m_anger_hunger_threshold; + float m_anger_loud_threshold; + + TTime m_time_became_angry; + + TTime time_growling; // время нахождения в состоянии пугания + + enum { + eAdditionalSounds = MonsterSound::eMonsterSoundCustom, + ePsyAttack = eAdditionalSounds | 0, + }; +public: + CAI_PseudoDog (); + virtual ~CAI_PseudoDog (); + + virtual DLL_Pure *_construct (); + + virtual void Load (LPCSTR section); + + virtual void reinit (); + virtual void reload (LPCSTR section); + + virtual bool ability_can_drag () {return true;} + virtual bool ability_psi_attack () {return true;} + + virtual void CheckSpecParams (u32 spec_params); + //virtual void play_effect_sound (); + + virtual void HitEntityInJump (const CEntity *pEntity); + + virtual IStateManagerBase *create_state_manager (); +private: +#ifdef _DEBUG + virtual void debug_on_key (int key); +#endif + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CAI_PseudoDog) +#undef script_type_list +#define script_type_list save_type_list(CAI_PseudoDog) diff --git a/src/xrGameLA/ai/monsters/pseudodog/pseudodog_psi_effector.cpp b/src/xrGameLA/ai/monsters/pseudodog/pseudodog_psi_effector.cpp new file mode 100644 index 000000000..2b44504ec --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/pseudodog_psi_effector.cpp @@ -0,0 +1,2 @@ +#include "stdafx.h" +#include "pseudodog_psi_effector.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/pseudodog/pseudodog_psi_effector.h b/src/xrGameLA/ai/monsters/pseudodog/pseudodog_psi_effector.h new file mode 100644 index 000000000..7b9637ef9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/pseudodog_psi_effector.h @@ -0,0 +1 @@ +#pragma once \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/pseudodog/pseudodog_script.cpp b/src/xrGameLA/ai/monsters/pseudodog/pseudodog_script.cpp new file mode 100644 index 000000000..805ecf10f --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/pseudodog_script.cpp @@ -0,0 +1,33 @@ +#include "pch_script.h" +#include "pseudodog.h" +#include "psy_dog.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CAI_PseudoDog::script_register(lua_State *L) +{ + module(L) + [ + class_("CAI_PseudoDog") + .def(constructor<>()) + ]; +} + +void CPsyDog::script_register(lua_State *L) +{ + module(L) + [ + class_("CPsyDog") + .def(constructor<>()) + ]; +} + +void CPsyDogPhantom::script_register(lua_State *L) +{ + module(L) + [ + class_("CPsyDogPhantom") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/pseudodog/pseudodog_state_manager.cpp b/src/xrGameLA/ai/monsters/pseudodog/pseudodog_state_manager.cpp new file mode 100644 index 000000000..b8218c416 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/pseudodog_state_manager.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "pseudodog.h" +#include "pseudodog_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../../../actor.h" +#include "../../stalker/ai_stalker.h" +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" + + +CStateManagerPseudodog::CStateManagerPseudodog(CAI_PseudoDog *monster) : inherited(monster) +{ + add_state(eStateRest, new CStateMonsterRest(monster)); + add_state(eStatePanic, new CStateMonsterPanic(monster)); + add_state(eStateAttack, new CStateMonsterAttack(monster)); + add_state(eStateEat, new CStateMonsterEat(monster)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(monster)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(monster)); + add_state(eStateHitted, new CStateMonsterHitted(monster)); +} + +#define MIN_ANGRY_TIME 10000 +#define MAX_GROWLING_TIME 20000 + +void CStateManagerPseudodog::execute() +{ + u32 state_id = u32(-1); + + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} + diff --git a/src/xrGameLA/ai/monsters/pseudodog/pseudodog_state_manager.h b/src/xrGameLA/ai/monsters/pseudodog/pseudodog_state_manager.h new file mode 100644 index 000000000..f45308f71 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/pseudodog_state_manager.h @@ -0,0 +1,13 @@ +#pragma once +#include "../monster_state_manager.h" + +class CAI_PseudoDog; + +class CStateManagerPseudodog : public CMonsterStateManager { + typedef CMonsterStateManager inherited; + +public: + + CStateManagerPseudodog (CAI_PseudoDog *monster); + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/pseudodog/psy_dog.cpp b/src/xrGameLA/ai/monsters/pseudodog/psy_dog.cpp new file mode 100644 index 000000000..a3e7e7f16 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/psy_dog.cpp @@ -0,0 +1,311 @@ +#include "stdafx.h" +#include "psy_dog.h" +#include "../../../level_graph.h" +#include "../../../ai_space.h" +#include "../../../alife_simulator.h" +#include "../../../xrServer_Object_Base.h" +#include "../../../xrserver.h" +#include "../../../ai_object_location.h" +#include "../../../level.h" +#include "../control_movement_base.h" +#include "../monster_velocity_space.h" +#include "../../../restricted_object.h" +#include "../../../actor.h" +#include "../ai_monster_effector.h" +#include "../../../ActorEffector.h" +#include "psy_dog_aura.h" +#include "psy_dog_state_manager.h" +#include "../../../alife_object_registry.h" +#include "../../../xrServer_Objects_Alife_Monsters.h" + +CPsyDog::CPsyDog() +{ + m_aura = new CPsyDogAura(this); +} +CPsyDog::~CPsyDog() +{ + xr_delete(m_aura); +} + +void CPsyDog::Load(LPCSTR section) +{ + inherited::Load(section); + + m_aura->load(pSettings->r_string(section,"aura_effector")); + + m_phantoms_max = pSettings->r_u8(section,"Max_Phantoms_Count"); + m_phantoms_min = pSettings->r_u8(section,"Min_Phantoms_Count"); + m_time_phantom_appear = pSettings->r_u32(section,"Time_Phantom_Appear"); +} + +BOOL CPsyDog::net_Spawn(CSE_Abstract *dc) +{ + if (!inherited::net_Spawn(dc)) return FALSE; + + return TRUE; +} +void CPsyDog::reinit() +{ + inherited::reinit (); + m_aura->reinit (); + m_time_last_phantom_appear = 0; +} +void CPsyDog::reload(LPCSTR section) +{ + inherited::reload(section); +} + + +////////////////////////////////////////////////////////////////////////// +// Register/Unregister +////////////////////////////////////////////////////////////////////////// + +void CPsyDog::register_phantom(CPsyDogPhantom *phantom) +{ + m_storage.push_back(phantom); +} + +void CPsyDog::unregister_phantom(CPsyDogPhantom *phantom) +{ + xr_vector::iterator it = std::find(m_storage.begin(),m_storage.end(), phantom); + VERIFY(it != m_storage.end()); + m_storage.erase(it); +} + +////////////////////////////////////////////////////////////////////////// +// Spawn phantom +////////////////////////////////////////////////////////////////////////// + +bool CPsyDog::spawn_phantom() +{ + u32 node; + if (!control().path_builder().get_node_in_radius(ai_location().level_vertex_id(), 4,8,5,node)) return false; + + // set id to created server object + CSE_Abstract *phantom = Level().spawn_item("psy_dog_phantom", ai().level_graph().vertex_position(node), node, 0xffff, true); + CSE_ALifeMonsterBase *pSE_Monster = smart_cast(phantom); + VERIFY(pSE_Monster); + + pSE_Monster->m_spec_object_id = ID(); + + // spawn here + NET_Packet P; + phantom->Spawn_Write (P,TRUE); + Level().Send (P,net_flags(TRUE)); + F_entity_Destroy (phantom); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +// Destroy all phantoms +////////////////////////////////////////////////////////////////////////// +void CPsyDog::delete_all_phantoms() +{ + for (xr_vector::iterator it = m_storage.begin(); it != m_storage.end(); it++) + (*it)->destroy_from_parent(); + + m_storage.clear(); +} + + +void CPsyDog::Think() +{ + inherited::Think(); + if (!g_Alive()) return; + + m_aura->update_schedule(); + + // check spawn / destroy phantoms + if (EnemyMan.get_enemy() && (get_phantoms_count() < m_phantoms_max) && (m_time_last_phantom_appear + m_time_phantom_appear < time())){ + if (spawn_phantom()) m_time_last_phantom_appear = time(); + }else { + if (!EnemyMan.get_enemy() && !m_storage.empty()) { + delete_all_phantoms(); + } + } +} + + +void CPsyDog::net_Destroy() +{ + delete_all_phantoms (); + inherited::net_Destroy(); +} + +void CPsyDog::Die(CObject* who) +{ + inherited::Die (who); + m_aura->on_death (); + delete_all_phantoms (); +} + +IStateManagerBase *CPsyDog::create_state_manager() +{ + return new CStateManagerPsyDog(this); +} + +u8 CPsyDog::get_phantoms_count() +{ + return u8(m_storage.size()); +} + +////////////////////////////////////////////////////////////////////////// +// Phantom Psy Dog +////////////////////////////////////////////////////////////////////////// +CPsyDogPhantom::CPsyDogPhantom() +{ +} +CPsyDogPhantom::~CPsyDogPhantom() +{ +} +BOOL CPsyDogPhantom::net_Spawn(CSE_Abstract *dc) +{ + if (!inherited::net_Spawn(dc)) return FALSE; + + CSE_ALifeMonsterBase *se_monster = smart_cast(dc); + m_parent_id = se_monster->m_spec_object_id; + m_parent = 0; + VERIFY2(m_parent_id != 0xffff, *cName()); + + try_to_register_to_parent(); + + setVisible (FALSE); + setEnabled (FALSE); + + // load effector + // Load psi postprocess -------------------------------------------------------- + + load_effector(*cNameSect(), "appear_effector",m_appear_effector); + + // -------------------------------------------------------------------------------- + m_particles_appear = pSettings->r_string(*cNameSect(), "particles_appear"); + m_particles_disappear = pSettings->r_string(*cNameSect(), "particles_disappear"); + + m_time_spawned = time(); + + return (TRUE); +} + +const u32 pmt_time_wait_parent = 10000; + +void CPsyDogPhantom::Think() +{ + if (is_wait_to_destroy_object()) return; + inherited::Think(); + + try_to_register_to_parent(); + + // still have no parent ? + if (!m_parent) { + // if there is no parent long period of time - destroy me + if (m_time_spawned + pmt_time_wait_parent > time()) destroy_me(); + return; + } + + if (m_state != eWaitToAppear) return; + + EnemyMan.transfer_enemy(m_parent); + + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + if (control().movement().real_velocity() < 2*velocity_run.velocity.linear/3) return; + if (!EnemyMan.get_enemy()) return; + if (!control().direction().is_face_target(EnemyMan.get_enemy(), PI_DIV_6)) return; + + Fvector target; + target.mad(Position(),Direction(), 10.f); + + // нода в прямой видимости? + control().path_builder().restrictions().add_border(Position(), target); + u32 node = ai().level_graph().check_position_in_direction(ai_location().level_vertex_id(),Position(),target); + control().path_builder().restrictions().remove_border(); + + if (!ai().level_graph().valid_vertex_id(node) || !control().path_builder().accessible(node)) return; + + target.y += 1.f; + com_man().jump (target); + m_state = eAttack; + + setVisible (TRUE); + setEnabled (TRUE); + + CParticlesPlayer::StartParticles(m_particles_appear,Fvector().set(0.0f,0.1f,0.0f),ID()); + + if (EnemyMan.get_enemy() != Actor()) + return; + + Actor()->Cameras().AddCamEffector(new CMonsterEffectorHit(m_appear_effector.ce_time,m_appear_effector.ce_amplitude,m_appear_effector.ce_period_number,m_appear_effector.ce_power)); + Actor()->Cameras().AddPPEffector(new CMonsterEffector(m_appear_effector.ppi, m_appear_effector.time, m_appear_effector.time_attack, m_appear_effector.time_release)); +} + +//void CPsyDogPhantom::Hit(float P,Fvector &dir,CObject*who,s16 element,Fvector p_in_object_space,float impulse, ALife::EHitType hit_type) +void CPsyDogPhantom::Hit (SHit* pHDS) +{ + if (is_wait_to_destroy_object()) return; + if ((pHDS->who == EnemyMan.get_enemy()) && (pHDS->who != 0)) destroy_me(); +} + +void CPsyDogPhantom::net_Destroy() +{ + Fvector center; + Center(center); + PlayParticles(m_particles_disappear,center,Fvector().set(0.f,1.f,0.f)); + + if (m_parent && !is_wait_to_destroy_object()) { + m_parent->unregister_phantom (this); + m_parent = 0; + m_parent_id = 0xffff; + } + + inherited::net_Destroy(); +} + +void CPsyDogPhantom::Die(CObject* who) +{ + inherited::Die (who); + destroy_me (); +} + +void CPsyDogPhantom::try_to_register_to_parent() +{ + // parent not ready yet + if(m_parent) return; + + CObject *obj = Level().Objects.net_Find(m_parent_id); + if (obj) { + CPsyDog *dog = smart_cast(obj); + VERIFY(dog); + + m_parent = dog; + m_parent->register_phantom (this); + + movement().restrictions().add_restrictions( m_parent->movement().restrictions().out_restrictions(), + m_parent->movement().restrictions().in_restrictions() ); + + m_state = eWaitToAppear; + } +} + +void CPsyDogPhantom::destroy_me() +{ + VERIFY(!is_wait_to_destroy_object()); + + if (m_parent) { + m_parent->unregister_phantom (this); + m_parent = 0; + m_parent_id = 0xffff; + } + + NET_Packet P; + u_EventGen (P,GE_DESTROY,ID()); + u_EventSend (P); +} + +void CPsyDogPhantom::destroy_from_parent() +{ + m_parent_id = 0xffff; + + NET_Packet P; + u_EventGen (P,GE_DESTROY,ID()); + u_EventSend (P); +} diff --git a/src/xrGameLA/ai/monsters/pseudodog/psy_dog.h b/src/xrGameLA/ai/monsters/pseudodog/psy_dog.h new file mode 100644 index 000000000..c597fd9fc --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/psy_dog.h @@ -0,0 +1,109 @@ +#pragma once +#include "pseudodog.h" +#include "../../../script_export_space.h" + +class CPsyDogPhantom; + +class CPsyDog : public CAI_PseudoDog { + typedef CAI_PseudoDog inherited; + + friend class CPsyDogPhantom; + friend class CPsyDogAura; + + // эффектор у актера при нахождении в поле + CPsyDogAura *m_aura; + + // enemy transfered from phantom + CActor *m_enemy; + + // externals + u8 m_phantoms_max; + u8 m_phantoms_min; + u32 m_time_phantom_appear; + + //internals + u32 m_time_last_phantom_appear; + + +public: + CPsyDog (); + virtual ~CPsyDog (); + + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract *dc); + virtual void reinit (); + virtual void reload (LPCSTR section); + virtual void net_Destroy (); + virtual void Die (CObject* who); + + virtual void Think (); +// void on_phantom_appear (); + virtual IStateManagerBase *create_state_manager (); + + u8 get_phantoms_count (); + bool must_hide () {return (get_phantoms_count() < m_phantoms_min);} +private: + bool spawn_phantom (); + void delete_phantom (CPsyDogPhantom*); + void register_phantom (CPsyDogPhantom*); + void unregister_phantom (CPsyDogPhantom*); + + void delete_all_phantoms (); + +private: + xr_vector m_storage; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CPsyDog) +#undef script_type_list +#define script_type_list save_type_list(CPsyDog) + +////////////////////////////////////////////////////////////////////////// +// Phantom Psy Dog +////////////////////////////////////////////////////////////////////////// + +class CPsyDogPhantom : public CAI_PseudoDog { + typedef CAI_PseudoDog inherited; + + CPsyDog *m_parent; + + enum { + eWaitToAppear, + eAttack + }m_state; + + SAttackEffector m_appear_effector; + + LPCSTR m_particles_appear; + LPCSTR m_particles_disappear; + + u16 m_parent_id; + + u32 m_time_spawned; + +public: + CPsyDogPhantom (); + virtual ~CPsyDogPhantom (); + virtual BOOL net_Spawn (CSE_Abstract *dc); + virtual void Think (); + virtual void Hit (SHit* pHDS); + + virtual void net_Destroy (); + virtual void Die (CObject* who); + + void destroy_from_parent (); +private: + void destroy_me (); + void try_to_register_to_parent (); + bool is_wait_to_destroy_object () {return (m_parent_id == 0xffff);} + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CPsyDogPhantom) +#undef script_type_list +#define script_type_list save_type_list(CPsyDogPhantom) + + diff --git a/src/xrGameLA/ai/monsters/pseudodog/psy_dog_aura.cpp b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_aura.cpp new file mode 100644 index 000000000..8f1bc0ec0 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_aura.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "psy_dog_aura.h" +#include "psy_dog.h" +#include "../../../actor.h" +#include "../../../ActorEffector.h" +#include "../../../actor_memory.h" +#include "../../../visual_memory_manager.h" +#include "../../../level.h" + +CPPEffectorPsyDogAura::CPPEffectorPsyDogAura(const SPPInfo &ppi, u32 time_to_fade) +: inherited(ppi) +{ + m_time_to_fade = time_to_fade; + m_effector_state = eStateFadeIn; + m_time_state_started = Device.dwTimeGlobal; + +} + +void CPPEffectorPsyDogAura::switch_off() +{ + m_effector_state = eStateFadeOut; + m_time_state_started = Device.dwTimeGlobal; +} + + +BOOL CPPEffectorPsyDogAura::update() +{ + // update factor + if (m_effector_state == eStatePermanent) { + m_factor = 1.f; + } else { + m_factor = float(Device.dwTimeGlobal - m_time_state_started) / float(m_time_to_fade); + if (m_effector_state == eStateFadeOut) m_factor = 1 - m_factor; + + if (m_factor > 1) { + m_effector_state = eStatePermanent; + m_factor = 1.f; + } else if (m_factor < 0) { + return FALSE; + } + } + return TRUE; +} + +////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////// + +void CPsyDogAura::reinit() +{ + m_time_actor_saw_phantom = 0; + m_time_phantom_saw_actor = 0; + + m_actor = smart_cast(Level().CurrentEntity()); + VERIFY (m_actor); +} + +void CPsyDogAura::update_schedule() +{ + if (!m_object->g_Alive()) return; + + m_time_phantom_saw_actor = 0; + + // check memory of actor and check memory of phantoms + CVisualMemoryManager::VISIBLES::const_iterator I = m_actor->memory().visual().objects().begin(); + CVisualMemoryManager::VISIBLES::const_iterator E = m_actor->memory().visual().objects().end(); + for ( ; I != E; ++I) { + const CGameObject *obj = (*I).m_object; + if (smart_cast(obj)) { + if (m_actor->memory().visual().visible_now(obj)) + m_time_actor_saw_phantom = time(); + } + } + + // check memory and enemy manager of phantoms whether they see actor + xr_vector::iterator it = m_object->m_storage.begin(); + for (; it != m_object->m_storage.end();++it) { + if ((*it)->EnemyMan.get_enemy() == m_actor) + m_time_phantom_saw_actor = time(); + else { + ENEMIES_MAP::const_iterator I = (*it)->EnemyMemory.get_memory().begin(); + ENEMIES_MAP::const_iterator E = (*it)->EnemyMemory.get_memory().end(); + for (; I != E; ++I) { + if (I->first == m_actor) { + m_time_phantom_saw_actor = I->second.time; + break; + } + } + } + + if (m_time_phantom_saw_actor == time()) + break; + } + + bool need_be_active = (m_time_actor_saw_phantom + 2000 > time()) || (m_time_phantom_saw_actor != 0); + if (active()) { + if (!need_be_active) { + m_effector->switch_off (); + m_effector = 0; + } + } else { + if (need_be_active) { + // create effector + m_effector = new CPPEffectorPsyDogAura(m_state, 5000); + Actor()->Cameras().AddPPEffector (m_effector); + } + } + +} + +void CPsyDogAura::on_death() +{ + if (active()) { + m_effector->switch_off (); + m_effector = 0; + } +} + diff --git a/src/xrGameLA/ai/monsters/pseudodog/psy_dog_aura.h b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_aura.h new file mode 100644 index 000000000..073efd8c4 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_aura.h @@ -0,0 +1,38 @@ + #pragma once +//////////////////////////////////////////////////////////////////////// +// Effector controlling class +//////////////////////////////////////////////////////////////////////// +#include "../../../pp_effector_custom.h" + +class CPPEffectorPsyDogAura : public CPPEffectorCustom { + typedef CPPEffectorCustom inherited; + + enum {eStateFadeIn, eStateFadeOut, eStatePermanent} m_effector_state; + + u32 m_time_state_started; + u32 m_time_to_fade; + +public: + CPPEffectorPsyDogAura (const SPPInfo &ppi, u32 time_to_fade); + virtual BOOL update (); + void switch_off (); +}; + +class CPsyDog; +class CActor; + +class CPsyDogAura : public CPPEffectorCustomController{ + + CPsyDog *m_object; + CActor *m_actor; + + u32 m_time_actor_saw_phantom; + u32 m_time_phantom_saw_actor; + +public: + CPsyDogAura (CPsyDog *dog) : m_object(dog){} + void reinit (); + void on_death (); + void update_schedule (); +}; + diff --git a/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_manager.cpp b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_manager.cpp new file mode 100644 index 000000000..548e57e61 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_manager.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "psy_dog.h" +#include "psy_dog_state_manager.h" +#include "../../../actor.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" +#include "../control_animation_base.h" +#include "../../../ai_object_location.h" +#include "../../../sound_player.h" +#include "../../../level_graph.h" + +#include "psy_dog_state_psy_attack.h" + +CStateManagerPsyDog::CStateManagerPsyDog(CAI_PseudoDog *monster) : inherited(monster) +{ + add_state(eStateAttack_Psy, new CStatePsyDogPsyAttack(monster)); +} + +void CStateManagerPsyDog::execute() +{ + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy && dynamic_cast(enemy) && smart_cast(object)->must_hide()) { + + select_state(eStateAttack_Psy); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; + } else { + inherited::execute(); + } +} + diff --git a/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_manager.h b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_manager.h new file mode 100644 index 000000000..5b4ed9621 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_manager.h @@ -0,0 +1,9 @@ +#pragma once +#include "pseudodog_state_manager.h" + +class CStateManagerPsyDog : public CStateManagerPseudodog { + typedef CStateManagerPseudodog inherited; +public: + CStateManagerPsyDog (CAI_PseudoDog *monster); + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack.h b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack.h new file mode 100644 index 000000000..a28b922c8 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../state.h" + +template +class CStatePsyDogPsyAttack : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + +public: + CStatePsyDogPsyAttack (_Object *obj); + virtual ~CStatePsyDogPsyAttack () {} + + virtual void reselect_state (); +}; + +#include "psy_dog_state_psy_attack_inline.h" diff --git a/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_hide.h b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_hide.h new file mode 100644 index 000000000..a41db34cc --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_hide.h @@ -0,0 +1,29 @@ +#pragma once + +template +class CStatePsyDogHide : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + struct { + Fvector position; + u32 node; + } target; + + +public: + CStatePsyDogHide (_Object *obj) : inherited(obj) {} + virtual ~CStatePsyDogHide () {} + + virtual void initialize (); + virtual void execute (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); + +private: + void select_target_point (); +}; + +#include "psy_dog_state_psy_attack_hide_inline.h" + diff --git a/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_hide_inline.h b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_hide_inline.h new file mode 100644 index 000000000..55ba7c263 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_hide_inline.h @@ -0,0 +1,69 @@ +#pragma once + +#include "../../../ai_space.h" +#include "../monster_cover_manager.h" +#include "../../../cover_point.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStatePsyDogHideAbstract CStatePsyDogHide<_Object> + +TEMPLATE_SPECIALIZATION +void CStatePsyDogHideAbstract::initialize() +{ + inherited::initialize(); + + select_target_point(); + object->path().prepare_builder(); + +} + +TEMPLATE_SPECIALIZATION +void CStatePsyDogHideAbstract::execute() +{ + object->set_action (ACT_RUN); + object->path().set_target_point (target.position, target.node); + object->path().set_rebuild_time (0); + object->path().set_distance_to_end (0); + object->path().set_use_covers (false); + + object->anim().accel_activate (eAT_Aggressive); + object->anim().accel_set_braking (false); + + object->sound().play (MonsterSound::eMonsterSoundAggressive, 0,0,object->db().m_dwAttackSndDelay); +} + +TEMPLATE_SPECIALIZATION +bool CStatePsyDogHideAbstract::check_start_conditions() +{ + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStatePsyDogHideAbstract::check_completion() +{ + return ((object->ai_location().level_vertex_id() == target.node) && !object->control().path_builder().is_moving_on_path()); +} + +TEMPLATE_SPECIALIZATION +void CStatePsyDogHideAbstract::select_target_point() +{ + const CCoverPoint *point = object->CoverMan->find_cover(object->EnemyMan.get_enemy_position(),10.f,30.f); + if (point && (object->Position().distance_to(point->position()) > 2.f)) { + target.node = point->level_vertex_id (); + target.position = point->position (); + } else { + const CCoverPoint *point = object->CoverMan->find_cover(object->Position(),10.f,30.f); + if (point && (object->Position().distance_to(point->position()) > 2.f)) { + target.node = point->level_vertex_id (); + target.position = point->position (); + } else { + target.node = 0; + target.position = ai().level_graph().vertex_position(target.node); + } + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStatePsyDogHideAbstract diff --git a/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_inline.h b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_inline.h new file mode 100644 index 000000000..79027572e --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudodog/psy_dog_state_psy_attack_inline.h @@ -0,0 +1,24 @@ +#pragma once + +#include "psy_dog_state_psy_attack_hide.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStatePsyDogPsyAttackAbstract CStatePsyDogPsyAttack<_Object> + +TEMPLATE_SPECIALIZATION +CStatePsyDogPsyAttackAbstract::CStatePsyDogPsyAttack(_Object *obj) : inherited(obj) +{ + add_state (eStateAttack_HideInCover, new CStatePsyDogHide<_Object>(obj)); +} +TEMPLATE_SPECIALIZATION +void CStatePsyDogPsyAttackAbstract::reselect_state() +{ + select_state(eStateAttack_HideInCover); +} + + +#undef TEMPLATE_SPECIALIZATION +#undef CStatePsyDogPsyAttackAbstract \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant.cpp b/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant.cpp new file mode 100644 index 000000000..11bc2cefd --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant.cpp @@ -0,0 +1,323 @@ +#include "stdafx.h" +#include "pseudo_gigant.h" +#include "pseudo_gigant_step_effector.h" +#include "../../../actor.h" +#include "../../../ActorEffector.h" +#include "../../../level.h" +#include "pseudogigant_state_manager.h" +#include "../monster_velocity_space.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" +#include "../ai_monster_effector.h" +#include "../../../../CameraBase.h" +#include "../../../xr_level_controller.h" +#include "../../../detail_path_manager_space.h" +#include "../../../detail_path_manager.h" +#include "../../../CharacterPhysicsSupport.h" +#include "../control_path_builder_base.h" + + +CPseudoGigant::CPseudoGigant() +{ + CControlled::init_external(this); + + StateMan = new CStateManagerGigant(this); + + com_man().add_ability(ControlCom::eControlRunAttack); + com_man().add_ability(ControlCom::eControlThreaten); + //com_man().add_ability(ControlCom::eControlJump); + com_man().add_ability(ControlCom::eControlRotationJump); +} + +CPseudoGigant::~CPseudoGigant() +{ + xr_delete(StateMan); +} + + +void CPseudoGigant::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + //anim().AddReplacedAnim(&m_bRunTurnLeft, eAnimRun, eAnimRunTurnLeft); + //anim().AddReplacedAnim(&m_bRunTurnRight, eAnimRun, eAnimRunTurnRight); + + anim().accel_load (section); + //anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + //anim().accel_chain_add (eAnimWalkFwd, eAnimRunTurnLeft); + //anim().accel_chain_add (eAnimWalkFwd, eAnimRunTurnRight); + //anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + step_effector.time = pSettings->r_float(section, "step_effector_time"); + step_effector.amplitude = pSettings->r_float(section, "step_effector_amplitude"); + step_effector.period_number = pSettings->r_float(section, "step_effector_period_number"); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); +// SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimLieIdle, "stand_sleep_", -1, &velocity_none, PS_LIE, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSleep, "stand_sleep_", -1, &velocity_none, PS_LIE, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRun, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRunDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimLookAround, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimDie, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandLieDown, "stand_lie_down_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimLieToSleep, "lie_to_sleep_", -1, &velocity_none, PS_LIE, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + + //anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimLieIdle, "stand_sleep_", -1, &velocity_none, PS_LIE, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimSleep, "stand_sleep_", -1, &velocity_none, PS_LIE, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimRun, "stand_run_fwd_", -1, &velocity_run, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimLookAround, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimDie, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimStandLieDown, "stand_lie_down_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimLieToSleep, "lie_to_sleep_", -1, &velocity_none, PS_LIE, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + //anim().AddAnim(eAnimThreaten, "stand_kick_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + //anim().AddAnim(eAnimRunTurnLeft, "stand_run_left_", -1, &velocity_run, PS_STAND); + //anim().AddAnim(eAnimRunTurnRight, "stand_run_right_", -1, &velocity_run, PS_STAND); + + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimSitIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSleep); + anim().LinkAction(ACT_REST, eAnimSleep); + anim().LinkAction(ACT_DRAG, eAnimWalkFwd); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimStandIdle); + + // define transitions + anim().AddTransition(eAnimStandLieDown, eAnimSleep, eAnimLieToSleep, false); + anim().AddTransition(PS_STAND, eAnimSleep, eAnimStandLieDown, true); + anim().AddTransition(PS_STAND, PS_LIE, eAnimStandLieDown, false); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + + + // Load psi postprocess -------------------------------------------------------- + LPCSTR ppi_section = pSettings->r_string(section, "threaten_effector"); + m_threaten_effector.ppi.duality.h = pSettings->r_float(ppi_section,"duality_h"); + m_threaten_effector.ppi.duality.v = pSettings->r_float(ppi_section,"duality_v"); + m_threaten_effector.ppi.gray = pSettings->r_float(ppi_section,"gray"); + m_threaten_effector.ppi.blur = pSettings->r_float(ppi_section,"blur"); + m_threaten_effector.ppi.noise.intensity = pSettings->r_float(ppi_section,"noise_intensity"); + m_threaten_effector.ppi.noise.grain = pSettings->r_float(ppi_section,"noise_grain"); + m_threaten_effector.ppi.noise.fps = pSettings->r_float(ppi_section,"noise_fps"); + VERIFY(!fis_zero(m_threaten_effector.ppi.noise.fps)); + + sscanf(pSettings->r_string(ppi_section,"color_base"), "%f,%f,%f", &m_threaten_effector.ppi.color_base.r, &m_threaten_effector.ppi.color_base.g, &m_threaten_effector.ppi.color_base.b); + sscanf(pSettings->r_string(ppi_section,"color_gray"), "%f,%f,%f", &m_threaten_effector.ppi.color_gray.r, &m_threaten_effector.ppi.color_gray.g, &m_threaten_effector.ppi.color_gray.b); + sscanf(pSettings->r_string(ppi_section,"color_add"), "%f,%f,%f", &m_threaten_effector.ppi.color_add.r, &m_threaten_effector.ppi.color_add.g, &m_threaten_effector.ppi.color_add.b); + + m_threaten_effector.time = pSettings->r_float(ppi_section,"time"); + m_threaten_effector.time_attack = pSettings->r_float(ppi_section,"time_attack"); + m_threaten_effector.time_release = pSettings->r_float(ppi_section,"time_release"); + + m_threaten_effector.ce_time = pSettings->r_float(ppi_section,"ce_time"); + m_threaten_effector.ce_amplitude = pSettings->r_float(ppi_section,"ce_amplitude"); + m_threaten_effector.ce_period_number = pSettings->r_float(ppi_section,"ce_period_number"); + m_threaten_effector.ce_power = pSettings->r_float(ppi_section,"ce_power"); + + // -------------------------------------------------------------------------------- + + + ::Sound->create(m_sound_threaten_hit,pSettings->r_string(section,"sound_threaten_hit"), st_Effect,SOUND_TYPE_WORLD); + ::Sound->create(m_sound_start_threaten,pSettings->r_string(section,"sound_threaten_start"), st_Effect,SOUND_TYPE_MONSTER_ATTACKING); + + m_kick_damage = pSettings->r_float(section,"HugeKick_Damage"); + m_kick_particles = pSettings->r_string(section,"HugeKick_Particles"); + read_distance (section,"HugeKick_MinMaxDist", m_threaten_dist_min, m_threaten_dist_max); + read_delay (section,"HugeKick_MinMaxDelay", m_threaten_delay_min, m_threaten_delay_max); + + m_time_kick_actor_slow_down = pSettings->r_u32(section,"HugeKick_Time_SlowDown"); +} + +void CPseudoGigant::reinit() +{ + inherited::reinit(); + + m_time_next_threaten = 0; + + if(CCustomMonster::use_simplified_visual()) return; + + move().load_velocity(*cNameSect(), "Velocity_JumpPrepare",MonsterMovement::eGiantVelocityParameterJumpPrepare); + move().load_velocity(*cNameSect(), "Velocity_JumpGround",MonsterMovement::eGiantVelocityParameterJumpGround); + + //com_man().load_jump_data(0,"jump_attack_0", "jump_attack_1", "jump_attack_2", MonsterMovement::eGiantVelocityParameterJumpPrepare, MonsterMovement::eGiantVelocityParameterJumpGround,0); + com_man().add_rotation_jump_data("1","2","3","4", PI_DIV_2); + + com_man().set_threaten_data ("stand_kick_0", 0.43f); +} + + + +#define MAX_STEP_RADIUS 60.f + +void CPseudoGigant::event_on_step() +{ + ////////////////////////////////////////////////////////////////////////// + // Earthquake Effector ////////////// + CActor* pActor = smart_cast(Level().CurrentEntity()); + if(pActor) + { + float dist_to_actor = pActor->Position().distance_to(Position()); + float max_dist = MAX_STEP_RADIUS; + if (dist_to_actor < max_dist) + Actor()->Cameras().AddCamEffector(new CPseudogigantStepEffector( + step_effector.time, + step_effector.amplitude, + step_effector.period_number, + (max_dist - dist_to_actor) / (1.2f * max_dist)) + ); + } + ////////////////////////////////// +} + +bool CPseudoGigant::check_start_conditions(ControlCom::EControlType type) +{ + if (!inherited::check_start_conditions(type)) return false; + + if (type == ControlCom::eControlRunAttack) + return true; + + if (type == ControlCom::eControlThreaten) { + if (m_time_next_threaten > time()) return false; + + // check distance to enemy + float dist = EnemyMan.get_enemy()->Position().distance_to(Position()); + if ((dist > m_threaten_dist_max) || (dist < m_threaten_dist_min)) return false; + } + + return true; +} + +void CPseudoGigant::on_activate_control(ControlCom::EControlType type) +{ + if (type == ControlCom::eControlThreaten) { + m_sound_start_threaten.play_at_pos(this,get_head_position(this)); + m_time_next_threaten = time() + Random.randI(m_threaten_delay_min,m_threaten_delay_max); + } +} + +void CPseudoGigant::on_threaten_execute() +{ + // разбросить объекты + m_nearest.clear_not_free (); + Level().ObjectSpace.GetNearest (m_nearest,Position(), 15.f, NULL); + for (u32 i=0;i(m_nearest[i]); + if (!obj || !obj->m_pPhysicsShell) continue; + + Fvector dir; + Fvector pos; + pos.set(obj->Position()); + pos.y += 2.f; + dir.sub(pos, Position()); + dir.normalize(); + obj->m_pPhysicsShell->applyImpulse(dir,20 * obj->m_pPhysicsShell->getMass()); + } + + // играть звук + Fvector pos; + pos.set (Position()); + pos.y += 0.1f; + m_sound_threaten_hit.play_at_pos(this,pos); + + // играть партиклы + PlayParticles(m_kick_particles, pos, Direction()); + + CActor *pA = const_cast(smart_cast(EnemyMan.get_enemy())); + if (!pA) return; + if (pA->is_jump()) return; + + float dist_to_enemy = pA->Position().distance_to(Position()); + float hit_value; + hit_value = m_kick_damage - m_kick_damage * dist_to_enemy / m_threaten_dist_max; + clamp (hit_value,0.f,1.f); + + // запустить эффектор + Actor()->Cameras().AddCamEffector(new CMonsterEffectorHit(m_threaten_effector.ce_time,m_threaten_effector.ce_amplitude * hit_value,m_threaten_effector.ce_period_number,m_threaten_effector.ce_power * hit_value)); + Actor()->Cameras().AddPPEffector(new CMonsterEffector(m_threaten_effector.ppi, m_threaten_effector.time, m_threaten_effector.time_attack, m_threaten_effector.time_release, hit_value)); + + // развернуть камеру + if (pA->cam_Active()) { + pA->cam_Active()->Move(Random.randI(2) ? kRIGHT : kLEFT, Random.randF(0.3f * hit_value)); + pA->cam_Active()->Move(Random.randI(2) ? kUP : kDOWN, Random.randF(0.3f * hit_value)); + } + + Actor()->lock_accel_for (m_time_kick_actor_slow_down); + + // Нанести хит + NET_Packet l_P; + SHit HS; + + HS.GenHeader (GE_HIT, pA->ID()); // u_EventGen (l_P,GE_HIT, pA->ID()); + HS.whoID = (ID()); // l_P.w_u16 (ID()); + HS.weaponID = (ID()); // l_P.w_u16 (ID()); + HS.dir = (Fvector().set(0.f,1.f,0.f)); // l_P.w_dir (Fvector().set(0.f,1.f,0.f)); + HS.power = (hit_value); // l_P.w_float (m_kick_damage); + HS.boneID = (smart_cast(pA->Visual())->LL_GetBoneRoot()); // l_P.w_s16 (smart_cast(pA->Visual())->LL_GetBoneRoot()); + HS.p_in_bone_space = (Fvector().set(0.f,0.f,0.f)); // l_P.w_vec3 (Fvector().set(0.f,0.f,0.f)); + HS.impulse = (80 * pA->character_physics_support()->movement()->GetMass()); // l_P.w_float (20 * pA->movement_control()->GetMass()); + HS.hit_type = ( ALife::eHitTypeStrike); // l_P.w_u16 ( u16(ALife::eHitTypeWound) ); + HS.Write_Packet (l_P); + u_EventSend (l_P); +} + +void CPseudoGigant::HitEntityInJump (const CEntity *pEntity) +{ + SAAParam ¶ms = anim().AA_GetParams("jump_attack_1"); + HitEntity (pEntity, params.hit_power, params.impulse, params.impulse_dir); +} + +void CPseudoGigant::TranslateActionToPathParams() +{ + if ((anim().m_tAction != ACT_RUN) && (anim().m_tAction != ACT_WALK_FWD)) { + inherited::TranslateActionToPathParams(); + return; + } + + u32 vel_mask = (m_bDamaged ? MonsterMovement::eVelocityParamsWalkDamaged : MonsterMovement::eVelocityParamsWalk); + u32 des_mask = (m_bDamaged ? MonsterMovement::eVelocityParameterWalkDamaged : MonsterMovement::eVelocityParameterWalkNormal); + + if (m_force_real_speed) vel_mask = des_mask; + + path().set_velocity_mask (vel_mask); + path().set_desirable_mask (des_mask); + path().enable_path (); +} + diff --git a/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant.h b/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant.h new file mode 100644 index 000000000..f4b801196 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant.h @@ -0,0 +1,68 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../controlled_entity.h" +#include "../../../script_export_space.h" + + +class CPseudoGigant : public CBaseMonster, + public CControlledEntity { + + typedef CBaseMonster inherited; + typedef CControlledEntity CControlled; + +private: + xr_vector m_nearest; + + // step_effector + struct { + float time; + float amplitude; + float period_number; + } step_effector; + + SAttackEffector m_threaten_effector; + ref_sound m_sound_threaten_hit; // звук, который играется в голове у актера + ref_sound m_sound_start_threaten; // звук, который играется в голове у актера + + u32 m_time_next_threaten; + + u32 m_threaten_delay_min; + u32 m_threaten_delay_max; + float m_threaten_dist_min; + float m_threaten_dist_max; + + float m_kick_damage; + + u32 m_time_kick_actor_slow_down; + + SVelocityParam m_fsVelocityJumpPrepare; + SVelocityParam m_fsVelocityJumpGround; + + LPCSTR m_kick_particles; + + +public: + CPseudoGigant (); + virtual ~CPseudoGigant (); + + virtual void Load (LPCSTR section); + virtual void reinit (); + + virtual bool ability_earthquake () {return true;} + virtual void event_on_step (); + + virtual bool check_start_conditions (ControlCom::EControlType type); + virtual void on_activate_control (ControlCom::EControlType); + + virtual void on_threaten_execute (); + + virtual void HitEntityInJump (const CEntity *pEntity); + virtual void TranslateActionToPathParams (); + + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CPseudoGigant) +#undef script_type_list +#define script_type_list save_type_list(CPseudoGigant) diff --git a/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant_step_effector.cpp b/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant_step_effector.cpp new file mode 100644 index 000000000..63e5ec244 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant_step_effector.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include "pseudo_gigant_step_effector.h" + +CPseudogigantStepEffector::CPseudogigantStepEffector(float time, float amp, float periods, float power) + : CEffectorCam(eCEPseudoGigantStep, time) +{ + total = time; + + max_amp = amp * power; + period_number = periods; + this->power = power; +} + +BOOL CPseudogigantStepEffector::Process(Fvector &p, Fvector &d, Fvector &n, float& fFov, float& fFar, float& fAspect) +{ + fLifeTime -= Device.fTimeDelta; + if(fLifeTime<0) + return FALSE; + + // процент оставшегося времени + float time_left_perc = fLifeTime / total; + + // Инициализация + Fmatrix Mdef; + Mdef.identity (); + Mdef.j.set (n); + Mdef.k.set (d); + Mdef.i.crossproduct (n,d); + Mdef.c.set (p); + + float period_all = period_number * PI_MUL_2; // макс. значение цикла + float k = 1 - time_left_perc + EPS_L + (1 - power); + float cur_amp = max_amp * (PI / 180) / (10 * k * k); + + Fvector dangle; + dangle.x = cur_amp/2 * _sin(period_all * (1.0f - time_left_perc)); + dangle.y = cur_amp * _cos(period_all/2 * (1.0f - time_left_perc)); + dangle.z = cur_amp/4 * _sin(period_all/4 * (1.0f - time_left_perc)); + + // Установить углы смещения + Fmatrix R; + R.setHPB (dangle.x,dangle.y,dangle.z); + + Fmatrix mR; + mR.mul (Mdef,R); + + d.set (mR.k); + n.set (mR.j); + + return TRUE; +} + + diff --git a/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant_step_effector.h b/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant_step_effector.h new file mode 100644 index 000000000..cb6efd298 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudogigant/pseudo_gigant_step_effector.h @@ -0,0 +1,15 @@ +#pragma once +#include "../../../CameraEffector.h" + +class CPseudogigantStepEffector : public CEffectorCam { + typedef CEffectorCam inherited; + + float total; + float max_amp; + float period_number; + float power; + +public: + CPseudogigantStepEffector (float time, float amp, float periods, float power); + virtual BOOL Process (Fvector &p, Fvector &d, Fvector &n, float& fFov, float& fFar, float& fAspect); +}; diff --git a/src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_script.cpp b/src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_script.cpp new file mode 100644 index 000000000..dc7f15d48 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "pseudo_gigant.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CPseudoGigant::script_register(lua_State *L) +{ + module(L) + [ + class_("CPseudoGigant") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_state_manager.cpp b/src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_state_manager.cpp new file mode 100644 index 000000000..64ccfba88 --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_state_manager.cpp @@ -0,0 +1,67 @@ +#include "stdafx.h" +#include "pseudo_gigant.h" +#include "pseudogigant_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../../../entitycondition.h" +#include "../states/monster_state_controlled.h" +#include "../states/monster_state_help_sound.h" + +CStateManagerGigant::CStateManagerGigant(CPseudoGigant *monster) : inherited(monster) +{ + add_state(eStateRest, new CStateMonsterRest(monster)); + add_state(eStatePanic, new CStateMonsterPanic(monster)); + add_state(eStateAttack, new CStateMonsterAttack(monster)); + add_state(eStateEat, new CStateMonsterEat(monster)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(monster)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(monster)); + add_state(eStateHitted, new CStateMonsterHitted(monster)); + add_state(eStateControlled, new CStateMonsterControlled(monster)); + add_state(eStateHearHelpSound, new CStateMonsterHearHelpSound(monster)); +} + +void CStateManagerGigant::execute() +{ + u32 state_id = u32(-1); + + if (!object->is_under_control()) { + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (check_state(eStateHearHelpSound)) { + state_id = eStateHearHelpSound; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + } else state_id = eStateControlled; + + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} diff --git a/src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_state_manager.h b/src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_state_manager.h new file mode 100644 index 000000000..798c7adcc --- /dev/null +++ b/src/xrGameLA/ai/monsters/pseudogigant/pseudogigant_state_manager.h @@ -0,0 +1,12 @@ +#pragma once +#include "../monster_state_manager.h" + +class CPseudoGigant; + +class CStateManagerGigant : public CMonsterStateManager { + typedef CMonsterStateManager inherited; +public: + + CStateManagerGigant (CPseudoGigant *monster); + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/psy_aura.cpp b/src/xrGameLA/ai/monsters/psy_aura.cpp new file mode 100644 index 000000000..6287b7445 --- /dev/null +++ b/src/xrGameLA/ai/monsters/psy_aura.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "psy_aura.h" +#include "BaseMonster/base_monster.h" + +CPsyAura::CPsyAura() +{ + m_object = 0; + m_radius = 1.f; +} + +CPsyAura::~CPsyAura() +{ +} + +void CPsyAura::schedule_update() +{ + inherited::schedule_update(); + + if (is_active()){ + feel_touch_update(m_object->Position(), m_radius); + process_objects_in_aura(); + } +} diff --git a/src/xrGameLA/ai/monsters/psy_aura.h b/src/xrGameLA/ai/monsters/psy_aura.h new file mode 100644 index 000000000..e158a2696 --- /dev/null +++ b/src/xrGameLA/ai/monsters/psy_aura.h @@ -0,0 +1,34 @@ +#pragma once +#include "../../../feel_touch.h" +#include "energy_holder.h" + +class CBaseMonster; + +class CPsyAura : public Feel::Touch, public CEnergyHolder { + + typedef CEnergyHolder inherited; + + // владелец поля + CBaseMonster *m_object; + + // радиус поля + float m_radius; + +public: + CPsyAura (); + virtual ~CPsyAura (); + + void init_external (CBaseMonster *obj) {m_object = obj;} + virtual BOOL feel_touch_contact (CObject* O){return FALSE;} + virtual void schedule_update (); + virtual void process_objects_in_aura () {} + + // свойства поля + void set_radius (float R) {m_radius = R;} + float get_radius (){return m_radius;} + + CBaseMonster *get_object (){return m_object;} +}; + + + diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat.cpp b/src/xrGameLA/ai/monsters/rats/ai_rat.cpp new file mode 100644 index 000000000..bab7cb1c6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat.cpp @@ -0,0 +1,705 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat.cpp +// Created : 23.04.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../PhysicsShell.h" +#include "../../../game_graph.h" +#include "../../../game_level_cross_table.h" +#include "../../../xrserver_objects_alife_monsters.h" + +#include "ai_rat_space.h" +#include "../../../../KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../ai_object_location.h" +#include "../../../movement_manager.h" +#include "../../../location_manager.h" +#include "../../../ai_sounds.h" +#include "../../../sound_player.h" +#include "ai_rat_impl.h" +#include "../../../ai_space.h" + +#include "../../../rat_state_manager.h" +#include "../../../rat_states.h" +#include "../../../object_broker.h" +#include "../ai_monster_squad_manager.h" +#include "../ai_monster_squad.h" +#include "../../../patrol_path_storage.h" +#include "../../../patrol_path.h" +#include "../../../Actor.h" + + +#ifdef DEBUG +#include "HudManager.h" +#endif + +using namespace RatSpace; + +// #include "../../../steering_behaviour_manager.h" +// #include "../../../steering_behaviour_cohesion.h" +// #include "../../../steering_behaviour_alignment.h" +// #include "../../../steering_behaviour_separation.h" + +CAI_Rat::CAI_Rat() : + m_behaviour_manager (0) +{ + init (); +// m_behaviour_manager = xr_new(this); +// m_behaviour_manager->add (xr_new(this), .5f); +// m_behaviour_manager->add (xr_new(this), .5f); +// m_behaviour_manager->add (xr_new(this), .5f); +} + +CAI_Rat::~CAI_Rat() +{ + delete_data (m_state_manager); +} + +void CAI_Rat::init() +{ + m_tAction = eRatActionNone; + m_hit_direction.set (0,0,1); + m_hit_time = 0; + m_tpCurrentGlobalAnimation.invalidate(); + m_tpCurrentGlobalBlend = 0; + m_bActionStarted = false; + m_bFiring = false; + m_previous_query_time = 0; + m_tGoalDir.set (10.0f*(Random.randF()-Random.randF()),10.0f*(Random.randF()-Random.randF()),10.0f*(Random.randF()-Random.randF())); + m_tCurrentDir = m_tGoalDir; + m_tHPB.set (0,0,0); + m_fDHeading = 0; + m_fGoalChangeTime = 0.f; + m_tLastSound.tpEntity = 0; + m_tLastSound.dwTime = 0; + m_tLastSound.eSoundType = SOUND_TYPE_NO_SOUND; + m_bNoWay = false; + m_dwMoraleLastUpdateTime = 0; + m_bStanding = false; + m_bActive = false; + m_dwStartAttackTime = 0; + m_saved_impulse = 0.f; + m_bMoving = false; + m_bCanAdjustSpeed = false; + m_bStraightForward = false; + m_turning = false; + time_to_next_attack = 2000; + time_old_attack = 0; + m_squad_count = u32(-1); + m_current_way_point = u32(-1); +} + +void CAI_Rat::init_state_manager () +{ + m_state_manager = new rat_state_manager(); + m_state_manager->construct (this); + fire(false); + + m_state_manager->add_state (aiRatDeath, new rat_state_death ()); + m_state_manager->add_state (aiRatFreeActive, new rat_state_free_active ()); + m_state_manager->add_state (aiRatFreePassive, new rat_state_free_passive ()); + m_state_manager->add_state (aiRatAttackRange, new rat_state_attack_range ()); + m_state_manager->add_state (aiRatAttackMelee, new rat_state_attack_melee ()); + m_state_manager->add_state (aiRatUnderFire, new rat_state_under_fire ()); + m_state_manager->add_state (aiRatRetreat, new rat_state_retreat ()); + m_state_manager->add_state (aiRatPursuit, new rat_state_pursuit ()); + m_state_manager->add_state (aiRatFreeRecoil, new rat_state_free_recoil ()); + m_state_manager->add_state (aiRatReturnHome, new rat_state_return_home ()); + m_state_manager->add_state (aiRatEatCorpse, new rat_state_eat_corpse ()); + m_state_manager->add_state (aiRatNoWay, new rat_state_no_way ()); + + m_state_manager->push_state (aiRatFreeActive); +} + +void CAI_Rat::reinit () +{ + inherited::reinit (); + CEatableItem::reinit (); + init (); + init_state_manager (); +} + +void CAI_Rat::reload (LPCSTR section) +{ + inherited::reload (section); + CEatableItem::reload (section); + LPCSTR head_bone_name = pSettings->r_string(section,"bone_head"); + sound().add (pSettings->r_string(section,"sound_death"), 100, SOUND_TYPE_MONSTER_DYING, 0, u32(eRatSoundMaskDie), eRatSoundDie, head_bone_name); + sound().add (pSettings->r_string(section,"sound_hit"), 100, SOUND_TYPE_MONSTER_INJURING, 1, u32(eRatSoundMaskInjuring), eRatSoundInjuring, head_bone_name); + sound().add (pSettings->r_string(section,"sound_attack"), 100, SOUND_TYPE_MONSTER_ATTACKING, 2, u32(eRatSoundMaskAttack), eRatSoundAttack, head_bone_name); + sound().add (pSettings->r_string(section,"sound_voice"), 100, SOUND_TYPE_MONSTER_TALKING, 4, u32(eRatSoundMaskVoice), eRatSoundVoice, head_bone_name); + sound().add (pSettings->r_string(section,"sound_eat"), 100, SOUND_TYPE_MONSTER_EATING , 3, u32(eRatSoundMaskEat), eRatSoundEat, head_bone_name); +} + +void CAI_Rat::Die(CObject* who) +{ + inherited::Die(who); + + m_eCurrentState = aiRatDeath; + + m_flags.set (FCanTake, TRUE); + + SelectAnimation(XFORM().k,movement().detail().direction(),movement().speed()); + + sound().play (eRatSoundDie); + + update_morale_broadcast(m_fMoraleDeathQuant,m_fMoraleDeathDistance); + + CGroupHierarchyHolder &Group = Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()); + vfRemoveActiveMember(); + vfRemoveStandingMember(); + --(Group.m_dwAliveCount); + m_eCurrentState = aiRatDeath; +} + +void CAI_Rat::Load(LPCSTR section) +{ + init(); + + inherited::Load(section); + CEatableItem::Load(section); + + // initialize start position + Fvector P = Position(); + P.x += ::Random.randF(); + P.z += ::Random.randF(); + + // active\passive + m_fChangeActiveStateProbability = pSettings->r_float (section,"ChangeActiveStateProbability"); + m_dwPassiveScheduleMin = pSettings->r_s32 (section,"PassiveScheduleMin"); + m_dwPassiveScheduleMax = pSettings->r_s32 (section,"PassiveScheduleMax"); + m_dwActiveCountPercent = pSettings->r_s32 (section,"ActiveCountPercent"); + m_dwStandingCountPercent = pSettings->r_s32 (section,"StandingCountPercent"); + + // eye shift + m_tEyeShift.y = pSettings->r_float (section,"EyeYShift"); + + // former constants + m_dwLostMemoryTime = pSettings->r_s32 (section,"LostMemoryTime"); + m_dwLostRecoilTime = pSettings->r_s32 (section,"LostRecoilTime"); + m_fUnderFireDistance = pSettings->r_float (section,"UnderFireDistance"); + m_dwRetreatTime = pSettings->r_s32 (section,"RetreatTime"); + m_fRetreatDistance = pSettings->r_float (section,"RetreatDistance"); + m_fAttackStraightDistance = pSettings->r_float (section,"AttackStraightDistance"); + m_fStableDistance = pSettings->r_float (section,"StableDistance"); + m_fWallMinTurnValue = pSettings->r_float (section,"WallMinTurnValue")/180.f*PI; + m_fWallMaxTurnValue = pSettings->r_float (section,"WallMaxTurnValue")/180.f*PI; + + m_fAngleSpeed = pSettings->r_float (section,"AngleSpeed"); + m_fSafeGoalChangeDelta = pSettings->r_float (section,"GoalChangeDelta"); + m_tGoalVariation = pSettings->r_fvector3(section,"GoalVariation"); + + m_fMoraleDeathDistance = pSettings->r_float (section,"MoraleDeathDistance"); + m_dwActionRefreshRate = pSettings->r_s32 (section,"ActionRefreshRate"); + + SetMaxHealth (pSettings->r_float (section,"MaxHealthValue")); + m_fSoundThreshold = pSettings->r_float (section,"SoundThreshold"); + + m_bEatMemberCorpses = pSettings->r_bool (section,"EatMemberCorpses"); + m_bCannibalism = pSettings->r_bool (section,"Cannibalism"); + m_dwEatCorpseInterval = pSettings->r_s32 (section,"EatCorpseInterval"); + + m_fNullASpeed = pSettings->r_float (section,"AngularStandSpeed")/180.f*PI;//PI_MUL_2 + m_fMinASpeed = pSettings->r_float (section,"AngularMinSpeed")/180.f*PI;//PI_MUL_2 + m_fMaxASpeed = pSettings->r_float (section,"AngularMaxSpeed")/180.f*PI;//.2f + m_fAttackASpeed = pSettings->r_float (section,"AngularAttackSpeed")/180.f*PI;//.15f; + + m_phMass = pSettings->r_float (section,"corp_mass"); + m_dwActiveScheduleMin = shedule.t_min; + m_dwActiveScheduleMax = shedule.t_max; +} + +BOOL CAI_Rat::net_Spawn (CSE_Abstract* DC) +{ + ////////////////////////////////////////////////////////////////////////// + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeMonsterRat *tpSE_Rat = smart_cast(e); + + // model + if (!inherited::net_Spawn(DC)) + return(FALSE); + // model + if (!CEatableItem::net_Spawn(DC)) + return(FALSE); + + monster_squad().register_member ((u8)g_Team(),(u8)g_Squad(),(u8)g_Group(), this); + + // personal characteristics + movement().m_body.current.yaw = movement().m_body.target.yaw = -tpSE_Rat->o_torso.yaw; + movement().m_body.current.pitch = movement().m_body.target.pitch = 0; + movement().m_body.speed = PI_MUL_2; + + eye_fov = tpSE_Rat->fEyeFov; + eye_range = tpSE_Rat->fEyeRange; + SetfHealth (tpSE_Rat->get_health()); + m_fMinSpeed = tpSE_Rat->fMinSpeed; + m_fMaxSpeed = tpSE_Rat->fMaxSpeed; + m_fAttackSpeed = tpSE_Rat->fAttackSpeed; + m_fMaxPursuitRadius = tpSE_Rat->fMaxPursuitRadius; + m_fMaxHomeRadius = tpSE_Rat->fMaxHomeRadius; + // morale + m_fMoraleSuccessAttackQuant = tpSE_Rat->fMoraleSuccessAttackQuant; + m_fMoraleDeathQuant = tpSE_Rat->fMoraleDeathQuant; + m_fMoraleFearQuant = tpSE_Rat->fMoraleFearQuant; + m_fMoraleRestoreQuant = tpSE_Rat->fMoraleRestoreQuant; + m_dwMoraleRestoreTimeInterval = tpSE_Rat->u16MoraleRestoreTimeInterval; + m_fMoraleMinValue = tpSE_Rat->fMoraleMinValue; + m_fMoraleMaxValue = tpSE_Rat->fMoraleMaxValue; + m_fMoraleNormalValue = tpSE_Rat->fMoraleNormalValue; + // attack + m_fHitPower = tpSE_Rat->fHitPower; + m_dwHitInterval = tpSE_Rat->u16HitInterval; + m_fAttackDistance = tpSE_Rat->fAttackDistance; + m_fAttackAngle = tpSE_Rat->fAttackAngle/180.f*PI; + m_fAttackSuccessProbability = tpSE_Rat->fAttackSuccessProbability; + + // m_tCurGP = tpSE_Rat->m_tGraphID; + // m_tNextGP = tpSE_Rat->m_tNextGraphID; + m_current_graph_point = m_next_graph_point = ai_location().game_vertex_id(); + + int iPointCount = (int)movement().locations().vertex_types().size(); + for (int j=0; jvertex_type())) { + m_time_to_change_graph_point= Device.dwTimeGlobal + ::Random32.random(60000) + 60000; + break; + } + + ////////////////////////////////////////////////////////////////////////// + + m_fSpeed = m_fCurSpeed = m_fMaxSpeed; + + if (g_Alive()) + m_tSpawnPosition.set (Level().seniority_holder().team(g_Team()).squad(g_Squad()).leader()->Position()); + else + m_tSpawnPosition.set (Position()); + + m_home_position.set (m_tSpawnPosition); + m_tStateStack.push (m_eCurrentState = aiRatFreeActive); + if (g_Alive()) + add_active_member (true); + + m_bStateChanged = true; + ai_location().game_vertex (ai().cross_table().vertex(ai_location().level_vertex_id()).game_vertex_id()); + + m_tHPB.x = -movement().m_body.current.yaw; + m_tHPB.y = -movement().m_body.current.pitch; + m_tHPB.z = 0; + + movement().enable_movement (false); + + load_animations (); + + if (g_Alive()) + Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()).m_dwLastActionTime = 0; + + m_flags.set (FCanTake, FALSE); + + CInifile *m_spawn_ini = spawn_ini(); + + if (!m_spawn_ini || !m_spawn_ini->section_exist("patrol") || !m_spawn_ini->line_exist("patrol","way")) + { + m_walk_on_way = false; + } else { + m_walk_on_way = true; + } + + if (m_walk_on_way) + { + m_current_way_point = 0; + m_path = ai().patrol_paths().path(m_spawn_ini->r_string("patrol","way")); + } + + return (TRUE); +} + +void CAI_Rat::net_Destroy() +{ + inherited::net_Destroy(); + CEatableItem::net_Destroy(); + monster_squad().remove_member ((u8)g_Team(),(u8)g_Squad(),(u8)g_Group(),this); +} + +void CAI_Rat::net_Export(NET_Packet& P) +{ + R_ASSERT (Local()); + + // export last known packet + R_ASSERT (!NET.empty()); + net_update& N = NET.back(); + P.w_float (GetfHealth()); + P.w_u32 (N.dwTimeStamp); + P.w_u8 (0); + P.w_vec3 (N.p_pos); + P.w_float (N.o_model); + P.w_float (N.o_torso.yaw); + P.w_float (N.o_torso.pitch); + P.w_float (N.o_torso.roll); + P.w_u8 (u8(g_Team())); + P.w_u8 (u8(g_Squad())); + P.w_u8 (u8(g_Group())); + + GameGraph::_GRAPH_ID l_game_vertex_id = ai_location().game_vertex_id(); + P.w (&l_game_vertex_id, sizeof(l_game_vertex_id)); + P.w (&l_game_vertex_id, sizeof(l_game_vertex_id)); +// P.w (&m_fGoingSpeed, sizeof(m_fGoingSpeed)); +// P.w (&m_fGoingSpeed, sizeof(m_fGoingSpeed)); + float f1 = 0; + if (ai().game_graph().valid_vertex_id(l_game_vertex_id)) { + f1 = Position().distance_to (ai().game_graph().vertex(l_game_vertex_id)->level_point()); + P.w (&f1, sizeof(f1)); + f1 = Position().distance_to (ai().game_graph().vertex(l_game_vertex_id)->level_point()); + P.w (&f1, sizeof(f1)); + } + else { + P.w (&f1, sizeof(f1)); + P.w (&f1, sizeof(f1)); + } + + CEatableItem::net_Export(P); +} + +void CAI_Rat::net_Import(NET_Packet& P) +{ + R_ASSERT (Remote()); + net_update N; + + u8 flags; + + float health; + P.r_float (health); + SetfHealth (health); + + P.r_u32 (N.dwTimeStamp); + P.r_u8 (flags); + P.r_vec3 (N.p_pos); + P.r_angle8 (N.o_model); + P.r_angle8 (N.o_torso.yaw); + P.r_angle8 (N.o_torso.pitch); + P.r_angle8 (N.o_torso.roll ); + id_Team = P.r_u8(); + id_Squad = P.r_u8(); + id_Group = P.r_u8(); + + GameGraph::_GRAPH_ID t; + P.r (&t, sizeof(t)); + P.r (&t, sizeof(t)); + ai_location().game_vertex (t); + + if (NET.empty() || (NET.back().dwTimeStampadd_Box(box); + //Fsphere sphere; + //sphere.P.set(0,0,0); + //sphere.R=0.25; + //element->add_Sphere(sphere); + element->setDensity(m_phMass); + element->SetMaterial(smart_cast(Visual())->LL_GetData(smart_cast(Visual())->LL_GetBoneRoot()).game_mtl_idx); + m_pPhysicsShell=P_create_Shell(); + m_pPhysicsShell->add_Element(element); + m_pPhysicsShell->Activate(XFORM(),0,XFORM()); + m_pPhysicsShell->set_PhysicsRefObject( this ); + if(!fsimilar(0.f,m_saved_impulse)){ + m_pPhysicsShell->applyHit(m_saved_hit_position,m_saved_hit_dir,m_saved_impulse,0,m_saved_hit_type); + } + /* + CKinematics* M = smart_cast(Visual()); VERIFY(M); + m_pPhysicsShell = P_create_Shell(); + + //get bone instance + int id=M->LL_BoneID("bip01_pelvis"); + CBoneInstance& instance=M->LL_GetBoneInstance (id); + + //create root element + CPhysicsElement* element=P_create_Element (); + element->mXFORM.identity(); + instance.set_callback(m_pPhysicsShell->GetBonesCallback(),element); + Fobb box; + box.m_rotate.identity(); + box.m_translate.set(0,0,0); + box.m_halfsize.set(0.10f,0.085f,0.25f); + element->add_Box(box); + + element->setDensity(200.f); + m_pPhysicsShell->add_Element(element); + element->SetMaterial("materials/skel1"); + + //set shell start position + Fmatrix m; + m.set(mRotate); + m.c.set(Position()); + m_pPhysicsShell->mXFORM.set(m); + */ +} + +void CAI_Rat::shedule_Update(u32 dt) +{ + if (!monster_squad().get_squad(this)->GetLeader() || !monster_squad().get_squad(this)->GetLeader()->g_Alive()) + { + monster_squad().get_squad(this)->SetLeader(this); + } + inherited::shedule_Update (dt); +} + +void CAI_Rat::UpdateCL () +{ + + /////////////////////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG + if (monster_squad().get_squad(this)->GetLeader() == this) { + + if (m_walk_on_way && m_path) draw_way(); + + } +#endif + /////////////////////////////////////////////////////////////////////////////////////// + + if (!m_pPhysicsShell && !g_Alive()) + CreateSkeleton (); + + if (!Useful()) { + inherited::UpdateCL (); + Exec_Look (Device.fTimeDelta); + + CMonsterSquad *squad = monster_squad().get_squad(this); + + if (squad && + ((squad->GetLeader() != this && !squad->GetLeader()->g_Alive()) || + squad->get_index(this) == u32(-1)) + ) squad->SetLeader(this); + + if (squad && + squad->SquadActive() && + squad->GetLeader() == this && + m_squad_count != squad->squad_alife_count() + ) + { + squad->set_rat_squad_index(squad->GetLeader()); + m_squad_count = squad->squad_alife_count(); + } + } + else { + if (!H_Parent() && m_pPhysicsShell && m_pPhysicsShell->isActive()) + m_pPhysicsShell->InterpolateGlobalTransform(&XFORM()); + + CPhysicsShellHolder::UpdateCL (); + CEatableItem::UpdateCL (); + } +} + +void CAI_Rat::UpdatePositionAnimation() +{ + + Fmatrix l_tSavedTransform = XFORM(); + m_fTimeUpdateDelta = Device.fTimeDelta; + move (m_bCanAdjustSpeed,m_bStraightForward); + float y,p,b; + XFORM().getHPB (y,p,b); + NET_Last.p_pos = Position(); + NET_Last.o_model = -y; + NET_Last.o_torso.yaw = -y; + NET_Last.o_torso.pitch = -p; + XFORM() = l_tSavedTransform; + + if (!bfScriptAnimation()) + SelectAnimation (XFORM().k,Fvector().set(1,0,0),m_fSpeed); +} + +//void CAI_Rat::Hit(float P,Fvector &dir,CObject*who,s16 element,Fvector p_in_object_space,float impulse, ALife::EHitType hit_type /*= ALife::eHitTypeWound*/) +void CAI_Rat::Hit (SHit* pHDS) +{ +// inherited::Hit (P,dir,who,element,p_in_object_space,impulse, hit_type); + inherited::Hit (pHDS); + if (!m_pPhysicsShell) { + m_saved_impulse = pHDS->impulse; + m_saved_hit_dir.set (pHDS->dir); + m_saved_hit_type = pHDS->hit_type; + m_saved_hit_position.set(pHDS->p_in_bone_space); + } + else { +// CEatableItem::Hit (P,dir,who,element,p_in_object_space,impulse, hit_type); + CEatableItem::Hit (pHDS); + } +} + +void CAI_Rat::feel_touch_new(CObject* O) +{ +} + +///////////////////////////////////// +// Rat as eatable item +///////////////////////////////////// +void CAI_Rat::OnH_A_Chield () +{ + inherited::OnH_A_Chield (); + CEatableItem::OnH_A_Chield (); +} + +void CAI_Rat::OnH_B_Chield () +{ + setVisible (FALSE); + setEnabled (FALSE); + + if (m_pPhysicsShell) + m_pPhysicsShell->Deactivate (); + + CEatableItem::OnH_B_Chield (); +} + +void CAI_Rat::OnH_B_Independent () +{ + CEatableItem::OnH_B_Independent (TRUE); + inherited::OnH_B_Independent (TRUE); + + if (!Useful()) + return; + + setVisible (TRUE); + setEnabled (TRUE); + + if (m_pPhysicsShell) + activate_physic_shell (); +} + +void CAI_Rat::OnH_A_Independent () +{ + CEatableItem::OnH_A_Independent (); + inherited::OnH_A_Independent (); +} + +bool CAI_Rat::Useful() const +{ + if (!g_Alive()) + { + return CEatableItem::Useful(); + } + + return false; +} + +#ifdef DEBUG +void CAI_Rat::OnRender() +{ +// inherited::OnRender(); +// CEatableItem::OnRender(); +} +#endif + +BOOL CAI_Rat::UsedAI_Locations() +{ + return (TRUE); +} + +void CAI_Rat::make_Interpolation () +{ + inherited::make_Interpolation(); + CEatableItem::make_Interpolation(); +} + +void CAI_Rat::PH_B_CrPr () +{ + inherited::PH_B_CrPr (); + CEatableItem::PH_B_CrPr (); +} + +void CAI_Rat::PH_I_CrPr () +{ + inherited::PH_I_CrPr (); + CEatableItem::PH_I_CrPr (); +} + +#ifdef DEBUG +void CAI_Rat::PH_Ch_CrPr () +{ + inherited::PH_Ch_CrPr (); + CEatableItem::PH_Ch_CrPr (); +} +#endif + + +void CAI_Rat::PH_A_CrPr () +{ + inherited::PH_A_CrPr (); + CEatableItem::PH_A_CrPr (); +} + +void CAI_Rat::create_physic_shell() +{ + // do not delete!!! +} + +void CAI_Rat::setup_physic_shell() +{ + // do not delete!!! +} + +void CAI_Rat::activate_physic_shell () +{ + CEatableItem::activate_physic_shell(); +} + +void CAI_Rat::on_activate_physic_shell () +{ + CObject *object = smart_cast(H_Parent()); + R_ASSERT (object); + XFORM().set (object->XFORM()); + inherited::activate_physic_shell(); +} + +float CAI_Rat::get_custom_pitch_speed (float def_speed) +{ + if (fsimilar(m_fSpeed,0.f)) + return (PI_DIV_6); + else + if (fsimilar(m_fSpeed,m_fMinSpeed)) + return (PI_DIV_4); + else + if (fsimilar(m_fSpeed,m_fMaxSpeed)) + return (PI_DIV_3); + else + if (fsimilar(m_fSpeed,m_fAttackSpeed)) + return (PI_DIV_2); + + Debug.fatal (DEBUG_INFO,"Impossible RAT speed!"); + return (PI_DIV_2); +} + +BOOL CAI_Rat::renderable_ShadowReceive () +{ + return TRUE; +} + +BOOL CAI_Rat::renderable_ShadowGenerate () +{ + return FALSE; +} + +DLL_Pure *CAI_Rat::_construct () +{ + CCustomMonster::_construct (); + CEatableItem::_construct (); + return (this); +} \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat.h b/src/xrGameLA/ai/monsters/rats/ai_rat.h new file mode 100644 index 000000000..007df7719 --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat.h @@ -0,0 +1,444 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat.h +// Created : 23.04.2002 +// Modified : 27.07.2004 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "../../../CustomMonster.h" +#include "../../../eatable_item.h" +#include "../../../seniority_hierarchy_holder.h" +#include "../../../team_hierarchy_holder.h" +#include "../../../squad_hierarchy_holder.h" +#include "../../../group_hierarchy_holder.h" +#include "../../../game_graph_space.h" + +class CBlend; +class CPatrolPath; +enum ESoundTypes; +class rat_state_manager; + +namespace steering_behaviour { + class manager; +} // namespace steering_behaviour +class CAI_Rat : public CCustomMonster, public CEatableItem +{ +private: + typedef CCustomMonster inherited; + const CPatrolPath *m_path; + +private: + Fvector m_home_position; + GameGraph::_GRAPH_ID m_current_graph_point; + GameGraph::_GRAPH_ID m_next_graph_point; + u32 m_time_to_change_graph_point; +public: + rat_state_manager *m_state_manager; + +public: + void init_state_manager (); + void select_speed (); + void move (bool bCanAdjustSpeed = true, bool bStraightForward = false); + void make_turn (); + void update_morale_broadcast (float const &value, float const &radius); + void update_morale (); + void load_animations (); + void fire (bool const &value); + void movement_type (float const &velocity); + void update_home_position (); + void select_next_home_position (); +//for state manager + SRotation sub_rotation (); + + bool switch_to_attack_melee (); + bool switch_if_enemy (); + bool switch_to_free_recoil (); + bool switch_to_eat (); + bool switch_if_position (); + bool switch_if_diff (); + bool switch_if_porsuit (); + bool switch_if_no_enemy (); + bool switch_if_alife (); + bool switch_if_dist_no_angle (); + bool switch_if_dist_angle (); + bool switch_if_lost_time (); + bool switch_if_lost_rtime (); + bool switch_if_time (); + bool switch_if_home (); + + bool get_morale (); + bool get_if_dw_time (); + bool get_if_tp_entity (); + bool get_alife (); + + + void set_previous_query_time (); + void set_dir (); + void set_dir_m (); + void set_sp_dir (); + void set_way_point (); + void set_home_pos (); + void set_rew_position (); + void set_rew_cur_position (); + void set_goal_time (float f_val = 0.f); + void set_movement_type (bool bCanAdjustSpeed = true, bool bStraightForward = false); + void set_firing (bool b_val=false); + + void activate_state_free_active (); + void activate_state_free_passive (); + void activate_state_move (); + void activate_move (); + void activate_turn (); + void activate_state_attack_range (); + void activate_state_free_recoil (); + void activate_state_home (); + void activate_state_eat (); + + void init_state_under_fire (); + void init_free_recoil (); + void init_free_active (); + + bool calc_node (const Fvector &next_position); + Fvector calc_position (); + void set_position (Fvector m_position); + void set_pitch (float pitch, float yaw); + const CEntityAlive *get_enemy (); + bool check_completion_no_way (); + enum ERatStates { + aiRatDeath = 0, + aiRatFreeActive, + aiRatFreePassive, + aiRatAttackRange, + aiRatAttackMelee, + aiRatUnderFire, + aiRatRetreat, + aiRatPursuit, + aiRatFreeRecoil, + aiRatReturnHome, + aiRatEatCorpse, + aiRatNoWay, + }; + ERatStates get_state (); + float m_newPitch; + float m_newYaw; + float m_newCorrect; + u32 time_to_next_attack; + u32 time_old_attack; + u32 time_attack_rebuild; + u32 m_squad_count; + bool m_attack_rebuild; + bool m_walk_on_way; + u32 m_current_way_point; + bool m_bWayCanAdjustSpeed; + bool m_bWayStraightForward; + +//end for state manager + +protected: + ////////////////////////// + // STRUCTURES + ////////////////////////// + #define TIME_TO_GO 2000 + #define TIME_TO_RETURN 500 + + typedef struct tagSNormalGlobalAnimations{ + MotionID tpaDeath[2]; + MotionID tpaAttack[3]; + MotionID tpaIdle[2]; + SAnimState tWalk; + SAnimState tRun; + MotionID tRunAttack; + MotionID tpTurnLeft; + MotionID tpTurnRight; + } SNormalGlobalAnimations; + + // normal animations + typedef struct tagSNormalAnimations{ + SNormalGlobalAnimations tGlobal; + } SNormalAnimations; + + typedef struct tagSRatAnimations{ + SNormalAnimations tNormal; + } SRatAnimations; + ////////////////////////// + // END OF STRUCTURES + ////////////////////////// + + ////////////////////////// + // CLASS MEMBERS + ////////////////////////// + + // Graph + enum ERatAction { + eRatActionNone = u32(0), + eRatActionAttackBegin, + eRatActionAttackEnd, + }; + + ERatAction m_tAction; + bool m_turning; + + // FSM + xr_stack m_tStateStack; + ERatStates m_eCurrentState; + ERatStates m_ePreviousState; + bool m_bStopThinking; + bool m_bStateChanged; + + // ANIMATIONS + SRatAnimations m_tRatAnimations; + MotionID m_tpCurrentGlobalAnimation; + CBlend* m_tpCurrentGlobalBlend; + + // ATTACK + bool m_bActionStarted; + bool m_bFiring; + u32 m_dwStartAttackTime; + float m_fAttackSpeed; + // HIT + u32 m_hit_time; + Fvector m_hit_direction; + Fvector m_tHitPosition; + float m_fHitPower; + u32 m_dwHitInterval; + //HIT PHYS + float m_saved_impulse; + Fvector m_saved_hit_position; + Fvector m_saved_hit_dir; + Fvector m_tNewPosition; + Fvector m_tOldPosition; + ALife::EHitType m_saved_hit_type; + //PHYS + float m_phMass; + + typedef struct tagSSimpleSound { + ESoundTypes eSoundType; + u32 dwTime; + float fPower; + Fvector tSavedPosition; + CEntityAlive *tpEntity; + } SSimpleSound; + + float m_fMaxSpeed; + float m_fMinSpeed; + // SOUND BEING FELT + SSimpleSound m_tLastSound; + + // BEHAVIOUR + Fvector m_tGoalDir; + Fvector m_tNewDir; + Fvector m_tCurrentDir; + Fvector m_tHPB; + float m_fDHeading; + Fvector m_tRecoilPosition; + + // constants + float m_fGoalChangeDelta; + float m_fSafeSpeed; + float m_fASpeed; + Fvector m_tVarGoal; + float m_fIdleSoundDelta; + Fvector m_tSpawnPosition; + float m_fAngleSpeed; + float m_fSafeGoalChangeDelta; + Fvector m_tGoalVariation; + float m_fNullASpeed; + float m_fMinASpeed; + float m_fMaxASpeed; + float m_fAttackASpeed; + + // variables + float m_fGoalChangeTime; + bool m_bNoWay; + + // Morale + float m_fMoraleSuccessAttackQuant; + float m_fMoraleDeathQuant; + float m_fMoraleFearQuant; + float m_fMoraleRestoreQuant; + u32 m_dwMoraleRestoreTimeInterval; + u32 m_dwMoraleLastUpdateTime; + float m_fMoraleMinValue; + float m_fMoraleMaxValue; + float m_fMoraleNormalValue; + float m_fMoraleDeathDistance; + + // active + float m_fChangeActiveStateProbability; + u32 m_dwActiveCountPercent; + u32 m_dwActiveScheduleMin; + u32 m_dwActiveScheduleMax; + u32 m_dwPassiveScheduleMin; + u32 m_dwPassiveScheduleMax; + u32 m_dwStandingCountPercent; + bool m_bStanding; + bool m_bActive; + + // attack parameters + float m_fAttackDistance; + float m_fAttackAngle; + float m_fMaxPursuitRadius; + float m_fMaxHomeRadius; + + // DDD + u32 m_dwActionRefreshRate; + float m_fAttackSuccessProbability; + + // former constants + u32 m_dwLostMemoryTime; + u32 m_dwLostRecoilTime; + float m_fUnderFireDistance; + u32 m_dwRetreatTime; + float m_fRetreatDistance; + float m_fAttackStraightDistance; + float m_fStableDistance; + float m_fWallMinTurnValue; + float m_fWallMaxTurnValue; + float m_fSoundThreshold; + + // eat troops + BOOL m_bEatMemberCorpses; + BOOL m_bCannibalism; + u32 m_dwEatCorpseInterval; + u32 m_previous_query_time; + + +public: + float m_fSpeed; + bool m_bMoving; + bool m_bCanAdjustSpeed; + bool m_bStraightForward; + +public: + IC void vfChangeGoal (); + IC bool bfCheckIfGoalChanged (bool bForceChangeGoal = true); + IC void vfChooseNewSpeed (); + IC void vfUpdateTime (float fTimeDelta); + IC void add_active_member (bool bForceActive = false); + IC void vfRemoveActiveMember (); + IC void vfAddStandingMember (); + IC void vfRemoveStandingMember (); + IC bool bfCheckIfSoundFrightful (); + IC bool bfCheckIfOutsideAIMap (Fvector &tTemp1); + + ////////////////////////// + // FSM STATES + ////////////////////////// + void Death (); + void FreeHuntingActive (); + void FreeHuntingPassive (); + void AttackFire (); + void AttackRun (); + void UnderFire (); + void Retreat (); + void Pursuit (); + void FreeRecoil (); + void ReturnHome (); + void EatCorpse (); + void test_movement (); + void init (); +public: + CAI_Rat (); + virtual ~CAI_Rat (); + virtual DLL_Pure *_construct (); + +public: + virtual CGameObject* cast_game_object () {return this;}; + virtual CInventoryItem* cast_inventory_item () {return this;} + virtual CAttachableItem* cast_attachable_item () {return this;} + virtual CEatableItem* cast_eatable_item () {return this;} + virtual CEntityAlive* cast_entity_alive () {return this;} + virtual CEntity* cast_entity () {return this;} + virtual CPhysicsShellHolder*cast_physics_shell_holder () {return this;} + virtual CParticlesPlayer* cast_particles_player () {return this;} + virtual CCustomMonster* cast_custom_monster () {return this;} + virtual CScriptEntity* cast_script_entity () {return this;} + virtual CWeapon* cast_weapon () {return NULL;} + virtual CAI_Rat *dcast_Rat () {return this;}; + +public: + + virtual BOOL renderable_ShadowReceive(); + virtual BOOL renderable_ShadowGenerate(); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Destroy (); + virtual void net_Export (NET_Packet& P); + virtual void net_Import (NET_Packet& P); + virtual void HitSignal (float amount, Fvector& vLocalDir, CObject* who, s16 element); + virtual void Die (CObject* who); + virtual void Load (LPCSTR section); + virtual void Think (); + virtual void SelectAnimation (const Fvector& _view, const Fvector& _move, float speed ); + virtual void Exec_Action (float dt); + virtual void feel_sound_new (CObject* who, int type, CSound_UserDataPtr user_data, const Fvector &Position, float power); + virtual void feel_touch_new (CObject* O); + virtual BOOL feel_touch_on_contact (CObject* O); + virtual BOOL feel_vision_isRelevant (CObject*); + virtual void shedule_Update (u32 dt); + virtual void UpdateCL (); + virtual void Hit (SHit* pHDS); + void CreateSkeleton (); + + virtual void UpdatePositionAnimation (); + + ///////////////////////////////////// + //rat as eatable item + virtual void OnHUDDraw (CCustomHUD* hud) {inherited::OnHUDDraw(hud);} + virtual void OnH_B_Chield (); + virtual void OnH_B_Independent (); + virtual void OnH_A_Independent (); + virtual void OnEvent (NET_Packet& P, u16 type) {inherited::OnEvent(P,type);} + virtual bool Useful () const; + virtual BOOL UsedAI_Locations (); + /////////////////////////////////////////////////////////////////////// + virtual u16 PHGetSyncItemsNumber () {return inherited ::PHGetSyncItemsNumber();} + virtual CPHSynchronize* PHGetSyncItem (u16 item) {return inherited ::PHGetSyncItem(item);} + virtual void PHUnFreeze () {return inherited ::PHUnFreeze();} + virtual void PHFreeze () {return inherited ::PHFreeze();} + /////////////////////////////////////////////////////////////////////// +#ifdef DEBUG + virtual void OnRender (); +#endif + virtual bool useful (const CItemManager *manager, const CGameObject *object) const; + virtual float evaluate (const CItemManager *manager, const CGameObject *object) const; + virtual void reinit (); + virtual void reload (LPCSTR section); + virtual const SRotation Orientation () const + { + return (inherited::Orientation()); + }; +public: + virtual void make_Interpolation (); + virtual void PH_B_CrPr (); // actions & operations before physic correction-prediction steps + virtual void PH_I_CrPr (); // actions & operations after correction before prediction steps +#ifdef DEBUG + virtual void PH_Ch_CrPr (); // +#endif + virtual void PH_A_CrPr (); // actions & operations after phisic correction-prediction steps + virtual void OnH_A_Chield (); + virtual void create_physic_shell (); + virtual void setup_physic_shell (); + virtual void activate_physic_shell (); + virtual void on_activate_physic_shell(); + virtual Feel::Sound* dcast_FeelSound () { return this; } + virtual bool use_model_pitch () const; + virtual float get_custom_pitch_speed (float def_speed); + + //serialization + virtual void save (NET_Packet &output_packet) {inherited::save(output_packet);} + virtual void load (IReader &input_packet) {inherited::load(input_packet);} + virtual BOOL net_SaveRelevant () {return inherited::net_SaveRelevant();} + bool can_stand_here (); + bool can_stand_in_position (); + Fvector get_next_target_point (); +#ifdef _DEBUG + void draw_way (); +#endif +private: + steering_behaviour::manager *m_behaviour_manager; +}; + +#include "ai_rat_inline.h" diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat_animations.cpp b/src/xrGameLA/ai/monsters/rats/ai_rat_animations.cpp new file mode 100644 index 000000000..ccb718734 --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat_animations.cpp @@ -0,0 +1,101 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_animations.cpp +// Created : 21.06.2002 +// Modified : 06.11.2002 +// Author : Dmitriy Iassenev +// Description : Animations, Bone transformations and Sounds for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_rat.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../ai_debug.h" +#include "../../../movement_manager.h" + +#define MIN_TURN_ANGLE PI_DIV_6*.5f + +// animations +void CAI_Rat::load_animations () +{ + IKinematicsAnimated* tpVisualObject = smart_cast(Visual()); + + // loading normal animations + m_tRatAnimations.tNormal.tGlobal.tpaDeath[0] = tpVisualObject->ID_Cycle("norm_death"); + m_tRatAnimations.tNormal.tGlobal.tpaDeath[1] = tpVisualObject->ID_Cycle("norm_death_2"); + + m_tRatAnimations.tNormal.tGlobal.tpaAttack[0] = tpVisualObject->ID_Cycle("attack_1"); + m_tRatAnimations.tNormal.tGlobal.tpaAttack[1] = tpVisualObject->ID_Cycle("attack_2"); + m_tRatAnimations.tNormal.tGlobal.tpaAttack[2] = tpVisualObject->ID_Cycle("attack_3"); + + m_tRatAnimations.tNormal.tGlobal.tpaIdle[0] = tpVisualObject->ID_Cycle("norm_idle_1"); + m_tRatAnimations.tNormal.tGlobal.tpaIdle[1] = tpVisualObject->ID_Cycle("norm_idle_2"); + + m_tRatAnimations.tNormal.tGlobal.tpTurnLeft = tpVisualObject->ID_Cycle("norm_turn_ls"); + m_tRatAnimations.tNormal.tGlobal.tpTurnRight = tpVisualObject->ID_Cycle("norm_turn_rs"); + + m_tRatAnimations.tNormal.tGlobal.tWalk.Create(tpVisualObject, "norm_walk"); + + m_tRatAnimations.tNormal.tGlobal.tRun.Create(tpVisualObject, "norm_run"); + m_tRatAnimations.tNormal.tGlobal.tRunAttack = tpVisualObject->ID_Cycle("norm_run_fwd_1"); + + tpVisualObject->PlayCycle(m_tRatAnimations.tNormal.tGlobal.tpaIdle[0]); +} + +void CAI_Rat::SelectAnimation(const Fvector& /**_view/**/, const Fvector& /**_move/**/, float /**speed/**/) +{ + IKinematicsAnimated *tpVisualObject = smart_cast(Visual()); + MotionID tpGlobalAnimation; + + if (!g_Alive()) { + for (int i=0 ;i<2; ++i) { + if (m_tRatAnimations.tNormal.tGlobal.tpaDeath[i] == m_tpCurrentGlobalAnimation) { + tpGlobalAnimation = m_tpCurrentGlobalAnimation; + break; + } + } + if (!tpGlobalAnimation) { + if (m_tpCurrentGlobalAnimation == m_tRatAnimations.tNormal.tGlobal.tpaIdle[1]) + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaDeath[0]; + else + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaDeath[::Random.randI(0,2)]; + } + } + else { + if (m_bFiring) + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaAttack[2]; + else + if (angle_difference(movement().m_body.target.yaw,movement().m_body.current.yaw) <= MIN_TURN_ANGLE) + if (m_fSpeed < 0.2f) { + if (m_bStanding) + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaIdle[1]; + else + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaIdle[0]; + } + else + if (_abs(m_fSpeed - m_fAttackSpeed) < EPS_L) + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tRunAttack; + else + if (_abs(m_fSpeed - m_fMaxSpeed) < EPS_L) + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tRun.fwd; + else + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tWalk.fwd; + else { + if (left_angle(-movement().m_body.target.yaw,-movement().m_body.current.yaw)) +// tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaIdle[0]; + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpTurnLeft; + else +// tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaIdle[0]; + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpTurnRight; + } + } + + if (tpGlobalAnimation != m_tpCurrentGlobalAnimation) + m_tpCurrentGlobalBlend = tpVisualObject->PlayCycle(m_tpCurrentGlobalAnimation = tpGlobalAnimation); + +#ifdef DEBUG + if (psAI_Flags.is(aiAnimation)) { + IKinematicsAnimated *skeleton_animated = smart_cast(Visual()); + Msg ("%6d %s animation : %s (%f,%f)",Device.dwTimeGlobal,"Global",skeleton_animated->LL_MotionDefName_dbg(m_tpCurrentGlobalAnimation),movement().m_body.current.yaw,movement().m_body.target.yaw); + } +#endif +} diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat_behaviour.cpp b/src/xrGameLA/ai/monsters/rats/ai_rat_behaviour.cpp new file mode 100644 index 000000000..e12a5bbba --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat_behaviour.cpp @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_fsm.cpp +// Created : 25.04.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_rat.h" +#include "rat_state_manager.h" +#include "ai_space.h" +#include "game_level_cross_table.h" +#include "ai_object_location.h" +#include "game_graph.h" + +void CAI_Rat::update_home_position () +{ + if (!g_Alive()) + return; + + CEntity *leader = Level().seniority_holder().team(g_Team()).squad(g_Squad()).leader(); + VERIFY (leader); + + if (ID() != leader->ID()) { + CAI_Rat *rat_leader = smart_cast(leader); + VERIFY (rat_leader); + if (m_home_position.distance_to(rat_leader->m_home_position) > EPS_L) + add_active_member (true); + + m_home_position = rat_leader->m_home_position; + } + + if (Device.dwTimeGlobal < m_time_to_change_graph_point) + return; + + if (ai().cross_table().vertex(ai_location().level_vertex_id()).game_vertex_id() != m_next_graph_point) + return; + + m_next_graph_point = ai().cross_table().vertex(ai_location().level_vertex_id()).game_vertex_id(); + select_next_home_position (); + m_home_position.set (ai().game_graph().vertex(m_next_graph_point)->level_point()); +} + +void CAI_Rat::Think() +{ + update_morale (); + update_home_position (); + m_state_manager->update (); +} \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat_feel.cpp b/src/xrGameLA/ai/monsters/rats/ai_rat_feel.cpp new file mode 100644 index 000000000..772f1f59e --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat_feel.cpp @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_feel.cpp +// Created : 23.07.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : Visibility and look for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_rat.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../../xrServerEntities/ai_sounds.h" + +BOOL CAI_Rat::feel_vision_isRelevant(CObject* O) +{ + CEntityAlive* E = smart_cast (O); + if (!E) return FALSE; + if ((E->g_Team() == g_Team()) && (E->g_Alive())) return FALSE; + return TRUE; +} + +void CAI_Rat::feel_sound_new(CObject* who, int eType, CSound_UserDataPtr user_data, const Fvector &Position, float power) +{ + if (!g_Alive()) + return; + + if ((eType & SOUND_TYPE_WEAPON_SHOOTING) == SOUND_TYPE_WEAPON_SHOOTING) + power = 1.f; + + if (power >= m_fSoundThreshold) { + if ((this != who) && ((m_tLastSound.dwTime <= m_dwLastUpdateTime) || (m_tLastSound.fPower <= power))) { + m_tLastSound.eSoundType = ESoundTypes(eType); + m_tLastSound.dwTime = Device.dwTimeGlobal; + m_tLastSound.fPower = power; + m_tLastSound.tSavedPosition = Position; + m_tLastSound.tpEntity = smart_cast(who); + if ((eType & SOUND_TYPE_MONSTER_DYING) == SOUND_TYPE_MONSTER_DYING) + m_fMorale += m_fMoraleDeathQuant; + else + if (((eType & SOUND_TYPE_WEAPON_SHOOTING) == SOUND_TYPE_WEAPON_SHOOTING) && !memory().enemy().selected()) + m_fMorale += m_fMoraleFearQuant;///fDistance; + else + if ((eType & SOUND_TYPE_MONSTER_ATTACKING) == SOUND_TYPE_MONSTER_ATTACKING) + m_fMorale += m_fMoraleSuccessAttackQuant;///fDistance; + } + } + + inherited::feel_sound_new (who,eType,user_data,Position,power); +} + +BOOL CAI_Rat::feel_touch_on_contact (CObject *O) +{ + return (inherited::feel_touch_on_contact(O)); +} diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat_fire.cpp b/src/xrGameLA/ai/monsters/rats/ai_rat_fire.cpp new file mode 100644 index 000000000..8b0cddcf0 --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat_fire.cpp @@ -0,0 +1,159 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_fire.cpp +// Created : 23.07.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : Fire and enemy parameters for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_rat.h" +#include "ai_rat_impl.h" +#include "ai_rat_space.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../item_manager.h" +#include "../../../sound_player.h" + +using namespace RatSpace; + +void CAI_Rat::Exec_Action(float /**dt/**/) +{ + switch (m_tAction) { + case eRatActionAttackBegin : { + u32 dwTime = Device.dwTimeGlobal; + sound().play (eRatSoundAttack);//,0,0,m_dwHitInterval+1,m_dwHitInterval); + if (memory().enemy().selected() && memory().enemy().selected()->g_Alive() && (dwTime - m_dwStartAttackTime > m_dwHitInterval)) { + m_bActionStarted = true; + m_dwStartAttackTime = dwTime; + Fvector tDirection; + Fvector position_in_bone_space; + position_in_bone_space.set(0.f,0.f,0.f); + tDirection.sub(memory().enemy().selected()->Position(),this->Position()); + vfNormalizeSafe(tDirection); + + if (this->Local() && memory().enemy().selected()) { + CEntityAlive *entity_alive = const_cast(memory().enemy().selected()); + VERIFY (entity_alive); + +// entity_alive->Hit(m_fHitPower,tDirection,this,0,position_in_bone_space,0); + u16 id_to = entity_alive->ID(); + u16 id_from = ID(); + NET_Packet l_P; + SHit HS; + HS.GenHeader(GE_HIT, id_to); // u_EventGen (l_P,GE_HIT, id_to); + HS.whoID = (id_from); // l_P.w_u16 (id_from); + HS.weaponID = (id_from); // l_P.w_u16 (id_from); + HS.dir = (tDirection); // l_P.w_dir (tDirection); + HS.power = (m_fHitPower); // l_P.w_float (m_fHitPower); + HS.boneID = (0); // l_P.w_s16 (0); + HS.p_in_bone_space = (position_in_bone_space); // l_P.w_vec3 (position_in_bone_space); + HS.impulse = (0.f); // l_P.w_float (0.f); + HS.hit_type = (ALife::eHitTypeWound); // l_P.w_u16 ((u16)ALife::eHitTypeWound); + HS.Write_Packet(l_P); + u_EventSend (l_P); + } + } + else + m_bActionStarted = false; + break; + } + case eRatActionAttackEnd : { + m_bActionStarted = false; + break; + } + default: + break; + } +} + +void CAI_Rat::HitSignal(float amount, Fvector& vLocalDir, CObject* who, s16 /**element/**/) +{ + // Save event + Fvector D; + XFORM().transform_dir(D,vLocalDir); + m_hit_time = Device.dwTimeGlobal; + m_hit_direction.set(D); + m_hit_direction.normalize(); + m_tHitPosition = who->Position(); + + // Play hit sound + if (!AlreadyDie()) + sound().play (eRatSoundInjuring); +} + +bool CAI_Rat::useful (const CItemManager *manager, const CGameObject *object) const +{ + if (g_Alive()) + return (false); + + if (!memory().item().useful(object)) + return (false); + + const CEntityAlive *entity_alive = smart_cast(object); + if (!entity_alive) + return (false); + + return (true); +} + +float CAI_Rat::evaluate (const CItemManager *manager, const CGameObject *object) const +{ + const CEntityAlive *entity_alive = smart_cast(object); + VERIFY (entity_alive); + if (!entity_alive->g_Alive()) { + if ((Device.dwTimeGlobal - entity_alive->GetLevelDeathTime() < m_dwEatCorpseInterval) && (entity_alive->m_fFood > 0) && (m_bEatMemberCorpses || (entity_alive->g_Team() != g_Team())) && (m_bCannibalism || (entity_alive->CLS_ID != CLS_ID))) + return (entity_alive->m_fFood*entity_alive->m_fFood)*Position().distance_to(entity_alive->Position()); + else + return (flt_max); + } + else + return (flt_max); +} + +void CAI_Rat::update_morale () +{ + u32 dwCurTime = Device.dwTimeGlobal; + clamp (m_fMorale, m_fMoraleMinValue, m_fMoraleMaxValue); + + if (dwCurTime - m_dwMoraleLastUpdateTime <= m_dwMoraleRestoreTimeInterval) + return; + + m_dwMoraleLastUpdateTime = dwCurTime; + float fDistance = Position().distance_to(m_home_position); + fDistance = fDistance < 1.f ? 1.f : fDistance; + switch (m_eCurrentState) { + case aiRatFreeActive : + case aiRatFreePassive : { + if (m_fMorale < m_fMoraleNormalValue) { + m_fMorale += m_fMoraleRestoreQuant;//*(1.f - fDistance/m_fMoraleNullRadius); + if (m_fMorale > m_fMoraleNormalValue) + m_fMorale = m_fMoraleNormalValue; + } + else + if (m_fMorale > m_fMoraleNormalValue) { + m_fMorale -= m_fMoraleRestoreQuant;//*(fDistance/m_fMoraleNullRadius); + if (m_fMorale < m_fMoraleNormalValue) + m_fMorale = m_fMoraleNormalValue; + } + break; + } + case aiRatUnderFire : + case aiRatRetreat : { + //m_fMorale += fDistance <= m_fMoraleNullRadius ? m_fMoraleRestoreQuant : 0; + //m_fMorale += m_fMoraleRestoreQuant*(m_fMoraleNullRadius/fDistance); + m_fMorale += m_fMoraleRestoreQuant; + break; + } + case aiRatAttackRange : + case aiRatAttackMelee : + case aiRatReturnHome : { + //m_fMorale += m_fMoraleRestoreQuant*(1.f - fDistance/m_fMoraleNullRadius); + //m_fMorale += m_fMoraleRestoreQuant*(m_fMoraleNullRadius/fDistance); + m_fMorale += m_fMoraleRestoreQuant; + break; + } + } + + clamp (m_fMorale, m_fMoraleMinValue, m_fMoraleMaxValue); +} diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat_fsm.cpp b/src/xrGameLA/ai/monsters/rats/ai_rat_fsm.cpp new file mode 100644 index 000000000..a48b641f6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat_fsm.cpp @@ -0,0 +1,661 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_fsm.cpp +// Created : 25.04.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../game_level_cross_table.h" +#include "../../../game_graph.h" +#include "ai_rat_space.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../item_manager.h" +#include "../../../memory_space.h" +#include "../../../ai_object_location.h" +#include "../../../movement_manager.h" +#include "../../../sound_player.h" +#include "ai_rat_impl.h" +#include "../../../ai_space.h" + +using namespace RatSpace; + +#undef WRITE_TO_LOG +#define WRITE_TO_LOG(s) m_bStopThinking = true; +/* if (!visible_objects().size())\ +Msg("* No objects in frustum",visible_objects().size());\ +else {\ +Msg("* Objects in frustum (%d) :",visible_objects().size());\ +for (int i=0; i<(int)visible_objects().size(); ++i)\ +Msg("* %s",*visible_objects()[i]->cName());\ +}\ +/** +#define WRITE_TO_LOG(s) {\ + Msg("Monster %s : \n* State : %s\n* Time delta : %7.3f\n* Global time : %7.3f",*cName(),s,m_fTimeUpdateDelta,float(Device.dwTimeGlobal)/1000.f);\ + m_bStopThinking = true;\ +} +/**/ + +#ifndef DEBUG + #undef WRITE_TO_LOG + #define WRITE_TO_LOG(s) m_bStopThinking = true; +#endif + +void CAI_Rat::Think() +{ + m_thinking = true; + update_morale (); + vfUpdateSpawnPosition(); + m_bStopThinking = false; + do { + m_ePreviousState = m_eCurrentState; + switch(m_eCurrentState) { + case aiRatDie : { + Death(); + break; + } + case aiRatFreeActive : { //aiRatFreeHuntingActive + FreeHuntingActive(); + break; + } + case aiRatFreePassive : { // aiRatFreeHuntingPassive + FreeHuntingPassive(); + break; + } + case aiRatAttackRange : { //aiRatAttackFire + AttackFire(); + break; + } + case aiRatAttackMelee : { //aiRatAttackRun + AttackRun(); + break; + } + case aiRatUnderFire : { + UnderFire(); + break; + } + case aiRatRetreat : { + Retreat(); + break; + } + case aiRatPursuit : { + Pursuit(); + break; + } + case aiRatFreeRecoil : { + FreeRecoil(); + break; + } + case aiRatReturnHome : { + ReturnHome(); + break; + } + case aiRatEatCorpse : { + EatCorpse(); + break; + } + } + m_bStateChanged = m_ePreviousState != m_eCurrentState; + } + while (!m_bStopThinking); + m_thinking = false; +} + +void CAI_Rat::Death() +{ + //WRITE_TO_LOG ("Dying..."); + vfSetFire (false); + + m_bStopThinking = true; + + vfSetFire (false); + + SelectAnimation (XFORM().k,movement().detail().direction(),0); + + if (m_fFood <= 0) { + if (m_previous_query_time <= GetLevelDeathTime()) + m_previous_query_time = Device.dwTimeGlobal; + setVisible(false); + if (Device.dwTimeGlobal - m_previous_query_time > 10000) { +// setEnabled (FALSE); +// NET_Packet P; +// u_EventGen (P,GE_DESTROY,ID()); +// u_EventSend (P); + } + } + +} + +void CAI_Rat::FreeHuntingActive() +{ + WRITE_TO_LOG("Free hunting active"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE((memory().enemy().selected() && ((memory().enemy().selected()->Position().distance_to(m_tSafeSpawnPosition) < m_fMaxPursuitRadius) || (Position().distance_to(m_tSafeSpawnPosition) > m_fMaxHomeRadius))),aiRatAttackRun); + + CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE(m_fMorale < m_fMoraleNormalValue,aiRatUnderFire); + + if ( + (m_tLastSound.dwTime >= m_dwLastUpdateTime) && + ( + !m_tLastSound.tpEntity || + ( + (!memory().item().selected() || (memory().item().selected()->ID() != m_tLastSound.tpEntity->ID())) && + (m_tLastSound.tpEntity->g_Team() != g_Team()) + ) + ) && + !memory().enemy().selected() + ) + { + SWITCH_TO_NEW_STATE_THIS_UPDATE(aiRatFreeRecoil); + } + + CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE(memory().item().selected(),aiRatEatCorpse); + + m_tSpawnPosition.set (m_tSafeSpawnPosition); + m_fGoalChangeDelta = m_fSafeGoalChangeDelta; + m_tVarGoal.set (m_tGoalVariation); + m_fASpeed = m_fAngleSpeed; + + if (bfCheckIfGoalChanged()) { + if (m_bStateChanged || (Position().distance_to(m_tSpawnPosition) > m_fStableDistance) || (::Random.randF(0,1) > m_fChangeActiveStateProbability)) + if (Position().distance_to(m_tSafeSpawnPosition) > m_fMaxHomeRadius) + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + else + vfChooseNewSpeed(); + else { + if (can_stand_here()) + vfRemoveActiveMember(); + } + } + + if (m_bStateChanged || (fis_zero(m_fSpeed) && (angle_difference(movement().m_body.target.yaw,movement().m_body.current.yaw) < PI_DIV_6))) + vfChooseNewSpeed(); + + vfUpdateTime(m_fTimeUpdateDelta); + + vfComputeNewPosition(false); + + sound().play (eRatSoundVoice,45*1000,15*1000); +} + +void CAI_Rat::FreeHuntingPassive() +{ + WRITE_TO_LOG("Free hunting passive"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + if (memory().enemy().selected()) { + m_fGoalChangeTime = 0; + add_active_member(true); + m_bStopThinking = false; + return; + } + + if (m_fMorale < m_fMoraleNormalValue) { + add_active_member(true); + m_bStopThinking = false; + return; + } + + if ((m_tLastSound.dwTime >= m_dwLastUpdateTime) && ((!m_tLastSound.tpEntity) || (m_tLastSound.tpEntity->g_Team() != g_Team()))) { + add_active_member(true); + m_bStopThinking = false; + return; + } + + m_fSpeed = 0.f; + + vfAddStandingMember(); + add_active_member(); + + sound().play (eRatSoundVoice,45*1000,15*1000); +} + +void CAI_Rat::UnderFire() +{ + WRITE_TO_LOG("UnderFire"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + // Msg ("%6d : Rat %s, %f -> %f [%f]",Device.dwTimeGlobal,*cName(),movement().m_body.current.pitch,movement().m_body.target.pitch,get_custom_pitch_speed(0.f)); + + vfSetFire(false); + + if (memory().enemy().selected()) { + GO_TO_NEW_STATE_THIS_UPDATE(aiRatAttackRun); + } + else { + if (m_tLastSound.dwTime >= m_dwLastUpdateTime) { + if (m_tLastSound.tpEntity && (m_tLastSound.tpEntity->g_Team() != g_Team()) && (!bfCheckIfSoundFrightful())) { + SWITCH_TO_NEW_STATE(aiRatAttackRun); + } + m_previous_query_time = Device.dwTimeGlobal; + if (m_bStateChanged) { + Fvector tTemp; + tTemp.setHP(-movement().m_body.current.yaw,-movement().m_body.current.pitch); + tTemp.normalize_safe(); + tTemp.mul(m_fUnderFireDistance); + m_tSpawnPosition.add(Position(),tTemp); + } + } + if (m_fMorale >= m_fMoraleNormalValue - EPS_L) { + GO_TO_PREV_STATE; + } + } + + vfUpdateTime(m_fTimeUpdateDelta); + + if (m_bStateChanged)//(Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir = m_tSpawnPosition; + + m_fSpeed = m_fAttackSpeed; + + vfComputeNewPosition(true,true); +} + +void CAI_Rat::AttackFire() +{ + WRITE_TO_LOG("Attacking enemy..."); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + // Msg ("%6d : Rat %s, %f -> %f [%f]",Device.dwTimeGlobal,*cName(),movement().m_body.current.pitch,movement().m_body.target.pitch,get_custom_pitch_speed(0.f)); + + //ERatStates eState = ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),m_eCurrentState,m_eCurrentState,aiRatRetreat,this,30.f)); + //if (eState != m_eCurrentState) + // GO_TO_NEW_STATE_THIS_UPDATE(eState); + + CHECK_IF_GO_TO_PREV_STATE(!memory().enemy().selected()); + + CHECK_IF_GO_TO_NEW_STATE((memory().enemy().selected()->Position().distance_to(Position()) > m_fAttackDistance),aiRatAttackRun) + + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + SRotation sTemp; + mk_rotation(tTemp,sTemp); + + CHECK_IF_GO_TO_NEW_STATE(angle_difference(movement().m_body.current.yaw,sTemp.yaw) > m_fAttackAngle,aiRatAttackRun) + + Fvector tDistance; + tDistance.sub (Position(),memory().enemy().selected()->Position()); + + m_fSpeed = 0.f; + + vfSetFire (true); + + vfSetMovementType(0); +} + +void CAI_Rat::AttackRun() +{ + WRITE_TO_LOG("Attack enemy"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + // Msg ("%6d : Rat %s, %f -> %f [%f]",Device.dwTimeGlobal,*cName(),movement().m_body.current.pitch,movement().m_body.target.pitch,get_custom_pitch_speed(0.f)); + vfSetFire(false); + + ERatStates eState = ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),m_eCurrentState,m_eCurrentState,m_eCurrentState,aiRatRetreat,aiRatRetreat,this,30.f)); + if (eState != m_eCurrentState) { + eState = ERatStates(dwfChooseAction(0*m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),m_eCurrentState,m_eCurrentState,m_eCurrentState,aiRatRetreat,aiRatRetreat,this,30.f)); + GO_TO_NEW_STATE_THIS_UPDATE(eState); + } + + CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE(memory().enemy().selected() && (m_tSafeSpawnPosition.distance_to(memory().enemy().selected()->Position()) > m_fMaxPursuitRadius),aiRatReturnHome); + + CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE(!memory().enemy().selected()); + + Fvector tDistance; + tDistance.sub(Position(),memory().enemy().selected()->Position()); + + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + SRotation sTemp; + mk_rotation(tTemp,sTemp); + + CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE((memory().enemy().selected()->Position().distance_to(Position()) <= m_fAttackDistance) && (angle_difference(movement().m_body.target.yaw, sTemp.yaw) <= m_fAttackAngle),aiRatAttackFire) + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) { + m_tGoalDir.set(memory().enemy().selected()->Position()); + } + + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + vfComputeNewPosition(true,true); +} + +void CAI_Rat::Retreat() +{ + WRITE_TO_LOG("Retreat"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + if (!memory().enemy().selected() || + (memory().enemy().selected() && + ( + !memory().enemy().selected()->g_Alive() + || + ( + (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time > m_dwRetreatTime) && + ( + (m_tLastSound.dwTime < m_dwLastUpdateTime) || + !m_tLastSound.tpEntity || + (m_tLastSound.tpEntity->g_Team() == g_Team()) || + !bfCheckIfSoundFrightful() + ) + ) + ) + ) + ) + { + memory().enable (memory().enemy().selected(),false); + GO_TO_PREV_STATE; + } + + if (memory().enemy().selected() && memory().enemy().selected()->g_Alive()) { + ERatStates eState = ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),aiRatAttackRun,aiRatAttackRun,aiRatAttackRun,aiRatRetreat,aiRatRetreat,this,30.f)); + // ERatStates eState = ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),aiRatAttackRun,aiRatAttackRun,aiRatAttackRun,aiRatAttackRun,aiRatAttackRun,this,30.f)); + if (eState != m_eCurrentState) { + eState = ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),aiRatAttackRun,aiRatAttackRun,aiRatAttackRun,aiRatRetreat,aiRatRetreat,this,30.f)); + GO_TO_NEW_STATE_THIS_UPDATE(eState); + } + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + SRotation sTemp; + mk_rotation(tTemp,sTemp); + + if ((memory().enemy().selected()->Position().distance_to(Position()) <= m_fAttackDistance) && (angle_difference(movement().m_body.target.yaw, sTemp.yaw) > m_fAttackAngle)) { + m_fSpeed = 0.f; + return; + } + + CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE((memory().enemy().selected()->Position().distance_to(Position()) <= m_fAttackDistance) && (angle_difference(movement().m_body.target.yaw, sTemp.yaw) <= m_fAttackAngle),aiRatAttackFire) + + tTemp.sub(Position(),memory().enemy().selected()->Position()); + tTemp.normalize_safe(); + tTemp.mul(m_fRetreatDistance); + m_tSpawnPosition.add(Position(),tTemp); + } + + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir = m_tSpawnPosition; + + vfComputeNewPosition(true,true); +} + +void CAI_Rat::Pursuit() +{ + WRITE_TO_LOG("Pursuit something"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + if (memory().enemy().selected() && (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time >= m_dwLostMemoryTime)) { + memory().enable(memory().enemy().selected(),false); + GO_TO_PREV_STATE_THIS_UPDATE; + } + + CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE(memory().enemy().selected(),aiRatAttackRun); + + CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE((m_fMorale < m_fMoraleNormalValue),aiRatUnderFire); + + // if ((m_tLastSound.dwTime >= m_dwLastUpdateTime) && ((m_tLastSound.eSoundType & SOUND_TYPE_WEAPON_BULLET_HIT) == SOUND_TYPE_WEAPON_BULLET_HIT)) { + if ( + (m_tLastSound.dwTime >= m_dwLastUpdateTime) && + ( + !m_tLastSound.tpEntity || + ( + (!memory().item().selected() || (memory().item().selected()->ID() != m_tLastSound.tpEntity->ID())) && + (m_tLastSound.tpEntity->g_Team() != g_Team()) + ) + ) && + !memory().enemy().selected() + ) + { + GO_TO_NEW_STATE_THIS_UPDATE(aiRatFreeRecoil); + } + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set(memory().memory(memory().enemy().selected()).m_object_params.m_position); + + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + vfComputeNewPosition(true,true); +} + +void CAI_Rat::FreeRecoil() +{ + WRITE_TO_LOG("Free hunting : Recoil from something"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + if (m_bStateChanged) { + m_dwLostRecoilTime = Device.dwTimeGlobal; + m_tRecoilPosition = m_tLastSound.tSavedPosition; + } + + vfSetFire(false); + + CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE(memory().enemy().selected()); + + CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE(m_dwLastUpdateTime > m_dwLostRecoilTime + 2000); + + CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE(memory().enemy().selected() && (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time >= m_dwLostRecoilTime),aiRatPursuit); + + if (m_bStateChanged) { + Fvector tTemp; + tTemp.setHP(-movement().m_body.current.yaw,-movement().m_body.current.pitch); + tTemp.normalize_safe(); + tTemp.mul(m_fUnderFireDistance); + m_tSpawnPosition.add(Position(),tTemp); + } + // else + // if (m_dwLastUpdateTime > m_dwLostRecoilTime + 2000) + // m_tSpawnPosition = m_tGoalDir = m_tRecoilPosition; + // + // m_fSafeSpeed = m_fSpeed = m_fMaxSpeed; + // + // vfUpdateTime(m_fTimeUpdateDelta); + // + // if (m_dwLastUpdateTime > m_dwLostRecoilTime + 2000) { + // m_fASpeed = m_fAngleSpeed; + // m_fSafeSpeed = m_fSpeed = m_fMinSpeed; + // bfCheckIfGoalChanged (); + // vfComputeNewPosition (false); + // } + // else + // vfComputeNewPosition(true,true); + // if (m_dwLastUpdateTime > m_dwLostRecoilTime + 2000) { + // m_tSpawnPosition = m_tRecoilPosition; + // m_fGoalChangeDelta = 1000;//m_fSafeGoalChangeDelta; + // m_tVarGoal.set (m_tGoalVariation); + // m_tVarGoal.mul (.1f); + // m_fASpeed = m_fAngleSpeed; + // m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + // + // bfCheckIfGoalChanged (); + // vfUpdateTime (m_fTimeUpdateDelta); + // vfComputeNewPosition (false); + // } + // else + { + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + vfComputeNewPosition (true,true); + } + + sound().play (eRatSoundVoice,45*1000,15*1000); +} + +void CAI_Rat::ReturnHome() +{ + WRITE_TO_LOG("Returning home"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + if (memory().enemy().selected() && (m_tSafeSpawnPosition.distance_to(memory().enemy().selected()->Position()) < m_fMaxPursuitRadius)) { + m_fGoalChangeTime = 0; + SWITCH_TO_NEW_STATE_THIS_UPDATE(aiRatAttackRun) + } + + if (memory().enemy().selected()) { + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + SRotation sTemp; + mk_rotation(tTemp,sTemp); + + if ((memory().enemy().selected()->Position().distance_to(Position()) <= m_fAttackDistance) && (angle_difference(movement().m_body.target.yaw, sTemp.yaw) > m_fAttackAngle)) { + m_fSpeed = 0.f; + return; + } + + CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE((memory().enemy().selected()->Position().distance_to(Position()) <= m_fAttackDistance) && (angle_difference(movement().m_body.target.yaw, sTemp.yaw) <= m_fAttackAngle),aiRatAttackFire) + } + + CHECK_IF_GO_TO_PREV_STATE(!memory().enemy().selected() || !memory().enemy().selected()->g_Alive() || Position().distance_to(m_tSafeSpawnPosition) < m_fMaxHomeRadius); + + m_tSpawnPosition.set (m_tSafeSpawnPosition); + m_fGoalChangeDelta = m_fSafeGoalChangeDelta; + m_tVarGoal.set (m_tGoalVariation); + m_fASpeed = m_fAngleSpeed; + m_fSpeed = m_fSafeSpeed = m_fAttackSpeed; + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set (m_tSafeSpawnPosition); + + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + vfComputeNewPosition(); +} + +void CAI_Rat::EatCorpse() +{ + WRITE_TO_LOG("Eating a corpse"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE((memory().enemy().selected() && ((memory().enemy().selected()->Position().distance_to(m_tSafeSpawnPosition) < m_fMaxPursuitRadius) || (Position().distance_to(m_tSafeSpawnPosition) > m_fMaxHomeRadius)))); + + CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE(!memory().item().selected() || (m_fMorale < m_fMoraleNormalValue)); + + m_fGoalChangeTime = 10.f; + + if ( + (m_tLastSound.dwTime >= m_dwLastUpdateTime) && + ( + !m_tLastSound.tpEntity || + ( + (!memory().item().selected() || (memory().item().selected()->ID() != m_tLastSound.tpEntity->ID())) && + (m_tLastSound.tpEntity->g_Team() != g_Team()) + ) + ) && + !memory().enemy().selected() + ) + { + SWITCH_TO_NEW_STATE_THIS_UPDATE(aiRatFreeRecoil); + } + + // IKinematicsAnimated *V= smart_cast(const_cast(memory().item().selected())->Visual()); + // R_ASSERT (V); + // u16 head_bone = V->LL_BoneID("bip01_head"); + // Fmatrix l_tMatrix; + // l_tMatrix.mul_43 (const_cast(memory().item().selected())->XFORM(),smart_cast(const_cast(memory().item().selected())->Visual())->LL_GetBoneInstance(head_bone).mTransform); + // Fvector temp_position = l_tMatrix.c; + Fvector temp_position; + memory().item().selected()->Center (temp_position); + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set (temp_position); + + vfUpdateTime (m_fTimeUpdateDelta); + + bool a = temp_position.distance_to(Position()) <= m_fAttackDistance; + Fvector direction; + direction.sub (temp_position,Position()); + float y,p; + direction.getHP (y,p); + if (a && angle_difference(y,-movement().m_body.current.yaw) < PI_DIV_6) { + m_fSpeed = 0; + if (Device.dwTimeGlobal - m_previous_query_time > m_dwHitInterval) { + m_previous_query_time = Device.dwTimeGlobal; + const CEntityAlive *const_corpse = smart_cast(memory().item().selected()); + VERIFY (const_corpse); + CEntityAlive *corpse = const_cast(const_corpse); + VERIFY (corpse); + corpse->m_fFood -= m_fHitPower/10.f; + } + m_bFiring = true; + vfComputeNewPosition (false); + sound().play (eRatSoundEat); + } + else { + sound().remove_active_sounds(u32(-1)); + if (!a) + m_fSpeed = m_fMaxSpeed; + else + m_fSpeed = 0.f; + vfComputeNewPosition (true,true); + } +} diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat_impl.h b/src/xrGameLA/ai/monsters/rats/ai_rat_impl.h new file mode 100644 index 000000000..56a7324aa --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat_impl.h @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_impl.h +// Created : 23.04.2002 +// Modified : 26.11.2002 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Rat" (inline functions implementation) +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "../../../level.h" +#include "../../../seniority_hierarchy_holder.h" +#include "../../../team_hierarchy_holder.h" +#include "../../../squad_hierarchy_holder.h" +#include "../../../group_hierarchy_holder.h" +#include "../../../ai_sounds.h" + +IC void CAI_Rat::add_active_member (bool bForceActive) +{ + CGroupHierarchyHolder &Group = Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()); + if (!m_bActive && (bForceActive || (Group.m_dwAliveCount*m_dwActiveCountPercent/100 >= Group.m_dwActiveCount))) { + m_bActive = true; + m_eCurrentState = aiRatFreeActive; + ++Group.m_dwActiveCount; + shedule.t_min = m_dwActiveScheduleMin; + shedule.t_max = m_dwActiveScheduleMax; + vfRemoveStandingMember(); + } + //Msg("* Group : alive[%2d], active[%2d]",Group.m_dwAliveCount,Group.m_dwActiveCount); +}; + +IC void CAI_Rat::vfRemoveActiveMember() +{ + CGroupHierarchyHolder &Group = Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()); + if (m_bActive) { + R_ASSERT(Group.m_dwActiveCount > 0); + --(Group.m_dwActiveCount); + m_bActive = false; + m_eCurrentState = aiRatFreePassive; + shedule.t_min = m_dwPassiveScheduleMin; + shedule.t_max = m_dwPassiveScheduleMax; + } + //Msg("* Group : alive[%2d], active[%2d]",Group.m_dwAliveCount,Group.m_dwActiveCount); +}; + +IC void CAI_Rat::vfAddStandingMember() +{ + CGroupHierarchyHolder &Group = Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()); + if ((Group.m_dwAliveCount*m_dwStandingCountPercent/100 >= Group.m_dwStandingCount) && (!m_bStanding)) { + ++Group.m_dwStandingCount; + m_bStanding = true; + } +}; + +IC void CAI_Rat::vfRemoveStandingMember() +{ + CGroupHierarchyHolder &Group = Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()); + if (m_bStanding) { + R_ASSERT(Group.m_dwStandingCount > 0); + --(Group.m_dwStandingCount); + m_bStanding = false; + } +}; + +IC bool CAI_Rat::bfCheckIfSoundFrightful() +{ + return(((m_tLastSound.eSoundType & SOUND_TYPE_WEAPON_BULLET_HIT) == SOUND_TYPE_WEAPON_BULLET_HIT) || ((m_tLastSound.eSoundType & SOUND_TYPE_WEAPON_SHOOTING) == SOUND_TYPE_WEAPON_SHOOTING)); +}; + +IC void CAI_Rat::update_morale_broadcast(float const &fValue, float const &/**fRadius/**/) +{ + CGroupHierarchyHolder &Group = Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()); + for (int i=0; i<(int)Group.members().size(); ++i) + if (Group.members()[i]->g_Alive()) + Group.members()[i]->m_fMorale += fValue; +} diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat_inline.h b/src/xrGameLA/ai/monsters/rats/ai_rat_inline.h new file mode 100644 index 000000000..dd518d218 --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat_inline.h @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_inline.h +// Created : 23.04.2002 +// Modified : 26.11.2002 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Rat" (inline functions) +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC void CAI_Rat::vfChangeGoal() +{ + Fvector vP; + vP.set(m_tSpawnPosition.x,m_tSpawnPosition.y,m_tSpawnPosition.z); + m_tGoalDir.x = vP.x+m_tVarGoal.x*::Random.randF(-0.5f,0.5f); + m_tGoalDir.y = vP.y+m_tVarGoal.y*::Random.randF(-0.5f,0.5f); + m_tGoalDir.z = vP.z+m_tVarGoal.z*::Random.randF(-0.5f,0.5f); +} + +IC bool CAI_Rat::bfCheckIfGoalChanged(bool bForceChangeGoal) +{ + if (m_fGoalChangeTime<=0){ + m_fGoalChangeTime += m_fGoalChangeDelta+m_fGoalChangeDelta*::Random.randF(-0.5f,0.5f); + if (bForceChangeGoal) + vfChangeGoal(); + return(true); + } + return(false); +}; + +IC void CAI_Rat::vfChooseNewSpeed() +{ + int iRandom = ::Random.randI(0,2); + switch (iRandom) { + case 0 : { + m_fSpeed = m_fMaxSpeed; + break; + } + case 1 : { + m_fSpeed = m_fMinSpeed; + break; + } + } + m_fSafeSpeed = m_fSpeed; +}; + +IC void CAI_Rat::vfUpdateTime(float fTimeDelta) +{ + m_fGoalChangeTime -= fTimeDelta > .1f ? .1f : fTimeDelta; +}; + +IC bool CAI_Rat::use_model_pitch () const +{ + return (!!g_Alive()); +} diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat_space.h b/src/xrGameLA/ai/monsters/rats/ai_rat_space.h new file mode 100644 index 000000000..a7dedfe96 --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat_space.h @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat+space.h +// Created : 27.07.2004 +// Modified : 27.07.2004 +// Author : Dmitriy Iassenev +// Description : Space for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +namespace RatSpace { + enum ERatSounds { + eRatSoundDie = u32(0), + eRatSoundInjuring, + eRatSoundAttack, + eRatSoundVoice, + eRatSoundEat, + eRatSoundDummy = u32(-1), + }; + + enum ERatSoundMasks { + eRatSoundMaskAnySound = u32(0), + eRatSoundMaskDie = u32(-1), + eRatSoundMaskInjuring = u32(-1), + eRatSoundMaskVoice = u32(1 << 31) | 1, + eRatSoundMaskAttack = u32(1 << 30) | 2, + eRatSoundMaskEat = u32(1 << 30) | 4, + eRatSoundMaskDummy = u32(-1), + }; +} \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/rats/ai_rat_templates.cpp b/src/xrGameLA/ai/monsters/rats/ai_rat_templates.cpp new file mode 100644 index 000000000..9fb4927da --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/ai_rat_templates.cpp @@ -0,0 +1,552 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_templates.cpp +// Created : 23.07.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : Templates for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../game_graph.h" +#include "../../../magic_box3.h" +#include "../../../../Include/xrRender/RenderVisual.h" +#include "../../../ai_object_location.h" +#include "../../../level_graph.h" +#include "../../../movement_manager.h" +#include "../../../location_manager.h" +#include "../../../level.h" +#include "../../../random32.h" +#include "../../../ai_space.h" +#include "../../../restricted_object.h" +#include "../../../patrol_path.h" +#include "../../../debug_renderer.h" +#include "../ai_monster_squad_manager.h" +#include "../ai_monster_squad.h" + +IC bool CAI_Rat::bfCheckIfOutsideAIMap(Fvector &tTemp1) +{ + u32 dwNewNode = ai_location().level_vertex_id(); + const CLevelGraph::CVertex *tpNewNode = ai_location().level_vertex(); + CLevelGraph::CPosition QueryPos; + if (!ai().level_graph().valid_vertex_position(tTemp1)) + return (false); + ai().level_graph().vertex_position(QueryPos,tTemp1); + if (!ai().level_graph().valid_vertex_id(dwNewNode) || !ai().level_graph().inside(*ai_location().level_vertex(),QueryPos)) { + dwNewNode = ai().level_graph().vertex(ai_location().level_vertex_id(),tTemp1); + tpNewNode = ai().level_graph().vertex(dwNewNode); + } + return(!ai().level_graph().valid_vertex_id(dwNewNode) || !ai().level_graph().inside(*tpNewNode,QueryPos)); +}; + +void CAI_Rat::fire (bool const &bFire) +{ + if (bFire) { + m_bFiring = true; + m_tAction = eRatActionAttackBegin; + } + else { + m_bFiring = false; + m_tAction = eRatActionAttackEnd; + } +} + +void CAI_Rat::movement_type (float const &fSpeed) +{ +// StandUp(); + m_bMoving = _abs(fSpeed) > EPS_L; + m_fSpeed = m_fCurSpeed = fSpeed; +} + +void CAI_Rat::select_speed () +{ + Fvector tTemp1, tTemp2; + tTemp1.sub (m_tGoalDir,Position()); + tTemp1.normalize_safe (); + float y,p; + tTemp1.getHP (y,p); + tTemp2 = XFORM().k; + tTemp2.normalize_safe (); + float fAngle = tTemp1.dotproduct(tTemp2); + clamp (fAngle,-.99999f,.99999f); + fAngle = acosf(fAngle); + + if (_abs(m_fSpeed - m_fMinSpeed) <= EPS_L) { + if (fAngle >= 2*PI_DIV_3) { + m_fSpeed = 0; + m_fASpeed = m_fNullASpeed; + movement().m_body.target.yaw = -y; + } + else + { + m_fSpeed = m_fMinSpeed; + m_fASpeed = m_fMinASpeed; + } + } + else + if (_abs(m_fSpeed - m_fMaxSpeed) <= EPS_L) { + if (fAngle >= 2*PI_DIV_3) { + m_fSpeed = 0; + m_fASpeed = m_fNullASpeed; + movement().m_body.target.yaw = -y; + } + else + if (fAngle >= PI_DIV_2) { + m_fSpeed = m_fMinSpeed; + m_fASpeed = m_fMinASpeed; + } + else { + m_fSpeed = m_fMaxSpeed; + m_fASpeed = m_fMaxASpeed; + } + } + else + if (_abs(m_fSpeed - m_fAttackSpeed) <= EPS_L) { +// if (fAngle >= 2*PI_DIV_3) { +// m_fSpeed = 0; +// m_fASpeed = m_fNullASpeed; +// movement().m_body.target.yaw = -y; +// } +// else + if (fAngle >= PI_DIV_2) { + m_fSpeed = m_fMinSpeed; + m_fASpeed = m_fMinASpeed; + } + else + if (fAngle >= PI_DIV_4) { + m_fSpeed = m_fMaxSpeed; + m_fASpeed = m_fMaxASpeed; + } + else { + m_fSpeed = m_fAttackSpeed; + m_fASpeed = m_fAttackASpeed; + } + } + else { + movement().m_body.target.yaw = -y; + m_fSpeed = 0; + m_fASpeed = m_fNullASpeed; + } + + tTemp2 = XFORM().k; + tTemp2.normalize_safe(); + + tTemp1 = Position(); + tTemp1.mad(tTemp2,1*m_fSpeed*m_fTimeUpdateDelta); + if (bfCheckIfOutsideAIMap(tTemp1)) { + tTemp1 = Position(); + if (_abs(m_fSpeed - m_fAttackSpeed) < EPS_L) { + tTemp1.mad(tTemp2,1*m_fMaxSpeed*m_fTimeUpdateDelta); + if (bfCheckIfOutsideAIMap(tTemp1)) { + m_fSpeed = m_fMinSpeed; + m_fASpeed = m_fMinASpeed; + } + else { + m_fSpeed = m_fMaxSpeed; + m_fASpeed = m_fMaxASpeed; + } + } + else + { + m_fSpeed = m_fMinSpeed; + m_fASpeed = m_fMinASpeed; + } + } +} + +void CAI_Rat::make_turn() +{ + m_fSpeed = m_fCurSpeed = 0.f; + if (m_bFiring && (angle_difference(movement().m_body.target.yaw,movement().m_body.current.yaw) < PI_DIV_6)) { +// movement().m_body.speed = 0.f; + return; + } + +// Msg ("%6d : Rat %s, %f -> %f [%f]",Device.dwTimeGlobal,*cName(),movement().m_body.current.pitch,movement().m_body.target.pitch,get_custom_pitch_speed(0.f)); + + m_turning = true; + movement().m_body.speed = PI_MUL_2; + + Fvector tSavedPosition = Position(); + m_tHPB.x = -movement().m_body.current.yaw; + m_tHPB.y = -movement().m_body.current.pitch; + + XFORM().setHPB (m_tHPB.x,m_tHPB.y,0.f);//m_tHPB.z); + Position() = tSavedPosition; +} + +void CAI_Rat::set_firing(bool b_val) +{ + m_bFiring = b_val; +} + +bool CAI_Rat::calc_node(Fvector const &next_position) +{ + u32 dwNewNode = ai_location().level_vertex_id(); + const CLevelGraph::CVertex *tpNewNode = ai_location().level_vertex(); + CLevelGraph::CPosition QueryPos; + bool a = !ai().level_graph().valid_vertex_id(dwNewNode) || !ai().level_graph().valid_vertex_position(next_position); + if (!a) { + ai().level_graph().vertex_position (QueryPos,next_position); + a = !ai().level_graph().inside(*ai_location().level_vertex(),QueryPos); + } + if (a) { + dwNewNode = ai().level_graph().vertex(ai_location().level_vertex_id(),next_position); + tpNewNode = ai().level_graph().vertex(dwNewNode); + } + if (ai().level_graph().valid_vertex_id(dwNewNode) && ai().level_graph().inside(*tpNewNode,QueryPos)) { + m_tNewPosition.y = ai().level_graph().vertex_plane_y(*tpNewNode,next_position.x,next_position.z); + if (movement().restrictions().accessible(m_tNewPosition)||!movement().restrictions().accessible(Position())) + return true; + } + return false; +} + +Fvector CAI_Rat::calc_position() +{ + Fvector tSavedPosition = Position(); + SRotation tSavedTorsoTarget = movement().m_body.target; + + // Update position and orientation of the planes + float fAT = m_fASpeed * m_fTimeUpdateDelta; + + Fvector& tDirection = XFORM().k; + + // Tweak orientation based on last position and goal + Fvector tOffset; + tOffset.sub(m_tGoalDir,Position()); + + if (!m_bStraightForward) { + if (tOffset.y > 1.0) { // We're too low + m_tHPB.y += fAT; + if (m_tHPB.y > 0.8f) + m_tHPB.y = 0.8f; + } + else + if (tOffset.y < -1.0) { // We're too high + m_tHPB.y -= fAT; + if (m_tHPB.y < -0.8f) + m_tHPB.y = -0.8f; + } + else // Add damping + m_tHPB.y *= 0.95f; + } + + tDirection.normalize_safe (); + tOffset.normalize_safe (); + + float fDot = tDirection.dotproduct(tOffset); + float fSafeDot = fDot; + + fDot = (1.0f-fDot)/2.0f * fAT * 10.0f; + + tOffset.crossproduct(tOffset,tDirection); + + if (m_bStraightForward) { + if (tOffset.y > 0.01f) { + if (fSafeDot > .95f) + m_fDHeading = 0.f; + else + if (fSafeDot > 0.75f) + m_fDHeading = .10f; + m_fDHeading = ( m_fDHeading * 9.0f + fDot )*0.1f; + } + else + if (tOffset.y < 0.01f) { + if (fSafeDot > .95f) + m_fDHeading = 0.f; + else + if (fSafeDot > 0.75f) + m_fDHeading = -.10f; + m_fDHeading = (m_fDHeading*9.0f - fDot)*0.1f; + } + } + else { + if (tOffset.y > 0.01f) + m_fDHeading = ( m_fDHeading * 9.0f + fDot )*0.1f; + else + if (tOffset.y < 0.01f) + m_fDHeading = (m_fDHeading*9.0f - fDot)*0.1f; + } + + + m_tHPB.x += m_fDHeading; + CLevelGraph::SContour contour; + ai().level_graph().contour(contour, ai_location().level_vertex_id()); + + Fplane P; + P.build(contour.v1,contour.v2,contour.v3); + + Fvector position_on_plane; + P.project(position_on_plane,Position()); + + // находим проекцию точки, лежащей на векторе текущего направления + Fvector dir_point, proj_point; + dir_point.mad(position_on_plane, Direction(), 1.f); + P.project(proj_point,dir_point); + + // получаем искомый вектор направления + Fvector target_dir; + target_dir.sub(proj_point,position_on_plane); + + float yaw,pitch; + target_dir.getHP(yaw,pitch); + + //movement().m_body.target.pitch = -pitch; + m_newPitch = -pitch; + + m_tHPB.x = angle_normalize_signed(m_tHPB.x); + m_tHPB.y = -movement().m_body.current.pitch; + return tSavedPosition.mad (tDirection,m_fSpeed*m_fTimeUpdateDelta); +} + +void CAI_Rat::set_position(Fvector m_position) +{ + XFORM().setHPB (m_tHPB.x,m_tHPB.y,0.f); + Position() = m_position; +} + +void CAI_Rat::set_pitch(float pitch, float yaw) +{ + movement().m_body.target.pitch = pitch; + movement().m_body.target.yaw = yaw; +} + + +void CAI_Rat::move (bool bCanAdjustSpeed, bool bStraightForward) +{ + m_bCanAdjustSpeed = bCanAdjustSpeed; + m_bStraightForward = bStraightForward; + + + Fvector tSafeHPB = m_tHPB; + Fvector tSavedPosition = Position(); + SRotation tSavedTorsoTarget = movement().m_body.target; + float fSavedDHeading = m_fDHeading; + + if (bCanAdjustSpeed) + select_speed (); + + if ((angle_difference(movement().m_body.target.yaw, movement().m_body.current.yaw) > PI_DIV_6)){ + make_turn (); + return; + } + + if (fis_zero(m_fSpeed)) + return; + + m_fCurSpeed = m_fSpeed; + + if (m_bNoWay) + { + m_tNewPosition = m_tOldPosition; + } else { + m_tNewPosition = calc_position(); + } + if (calc_node(m_tNewPosition)) + { + set_position(m_tNewPosition); + set_pitch(m_newPitch, -m_tHPB.x); + m_tOldPosition = tSavedPosition; + m_bNoWay = false; + } + else { + m_fSafeSpeed = m_fSpeed = EPS_S; + m_bNoWay = true; + m_tHPB = tSafeHPB; + set_position(tSavedPosition); + movement().m_body.target = tSavedTorsoTarget; + m_fDHeading = fSavedDHeading; + } + if (m_bNoWay && (!m_turning || (angle_difference(movement().m_body.target.yaw, movement().m_body.current.yaw) < EPS_L))) { + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_RETURN) || (!m_previous_query_time)) { + movement().m_body.target.yaw = movement().m_body.current.yaw + PI; + movement().m_body.target.yaw = angle_normalize(movement().m_body.target.yaw); + Fvector tTemp; + tTemp.setHP(-movement().m_body.target.yaw ,-movement().m_body.target.pitch); + if (m_bStraightForward) + { + tTemp.mul(100.f); + } + + if (!m_walk_on_way) m_tGoalDir.add(Position(),tTemp); + + m_previous_query_time = Device.dwTimeGlobal; + } + if (!m_walk_on_way) make_turn (); + } + m_turning = false; +} + +void CAI_Rat::select_next_home_position () +{ + GameGraph::_GRAPH_ID tGraphID = m_next_graph_point; + CGameGraph::const_iterator i,e; + ai().game_graph().begin (tGraphID,i,e); + int iPointCount = (int)movement().locations().vertex_types().size(); + int iBranches = 0; + for ( ; i != e; ++i) + for (int j=0; jvertex_type()) && ((*i).vertex_id() != m_current_graph_point)) + ++iBranches; + ai().game_graph().begin (tGraphID,i,e); + if (!iBranches) { + for ( ; i != e; ++i) { + for (int j=0; jvertex_type())) { + m_current_graph_point = m_next_graph_point; + m_next_graph_point = (*i).vertex_id(); + m_time_to_change_graph_point = Device.dwTimeGlobal + ::Random32.random(60000) + 60000; + return; + } + } + } + else { + int iChosenBranch = ::Random.randI(0,iBranches); + iBranches = 0; + for ( ; i != e; ++i) { + for (int j=0; jvertex_type()) && ((*i).vertex_id() != m_current_graph_point)) { + if (iBranches == iChosenBranch) { + m_current_graph_point = m_next_graph_point; + m_next_graph_point = (*i).vertex_id(); + m_time_to_change_graph_point = Device.dwTimeGlobal + ::Random32.random(60000) + 60000; + return; + } + ++iBranches; + } + } + } +} + +bool CAI_Rat::can_stand_in_position() +{ + xr_vector tpNearestList ; + //float m_radius = Radius(); + Level().ObjectSpace.GetNearest (tpNearestList, Position(), 0.2f, this) ; + if (tpNearestList.empty()) + return (true); + + Fvector c, d, C2; + Visual()->getVisData().box.get_CD (c,d); + Fmatrix M = XFORM(); + M.transform_tiny (C2,c); + M.c = C2; + MagicBox3 box(M,d); + + xr_vector::iterator I = tpNearestList.begin(); + xr_vector::iterator E = tpNearestList.end(); + for ( ; I != E; ++I) { + if (!smart_cast(*I)) + continue; + + (*I)->Visual()->getVisData().box.get_CD (c,d); + M = (*I)->XFORM(); + M.transform_tiny (C2,c); + M.c = C2; + + if (box.intersects(MagicBox3(M,d))) + return (false); + } + return (true); +} + +bool CAI_Rat::can_stand_here () +{ + xr_vector tpNearestList ; + Level().ObjectSpace.GetNearest (tpNearestList, Position(),Radius(),this) ; + //xr_vector &tpNearestList = Level().ObjectSpace.q_nearest; + if (tpNearestList.empty()) + return (true); + + Fvector c, d, C2; + Visual()->getVisData().box.get_CD (c,d); + Fmatrix M = XFORM(); + M.transform_tiny (C2,c); + M.c = C2; + MagicBox3 box(M,d); + + xr_vector::iterator I = tpNearestList.begin(); + xr_vector::iterator E = tpNearestList.end(); + for ( ; I != E; ++I) { + if (!smart_cast(*I)) + continue; + + (*I)->Visual()->getVisData().box.get_CD (c,d); + M = (*I)->XFORM(); + M.transform_tiny (C2,c); + M.c = C2; + + if (box.intersects(MagicBox3(M,d))) + return (false); + } + return (true); +} + +Fvector CAI_Rat::get_next_target_point() +{ + if (!m_path) + { + m_walk_on_way = false; + m_current_way_point = u32(-1); + return Position(); + } + + if (m_current_way_point == u32(-1)) + { + m_walk_on_way = false; + return Position(); + } + + const CPatrolPath::CVertex *vertex = m_path->vertex(m_current_way_point); + + if (Position().distance_to(ai().level_graph().vertex_position(vertex->data().level_vertex_id())) < 1.5f){ + + monster_squad().get_squad(this)->SetLeader(this); + + m_current_way_point++; + + if (m_current_way_point == m_path->vertex_count()) m_current_way_point = 0; + + vertex = m_path->vertex(m_current_way_point); + } + + return (ai().level_graph().vertex_position(vertex->data().level_vertex_id())); +} + +#ifdef _DEBUG +void CAI_Rat::draw_way() +{ + if (!m_path) return; + const CPatrolPath::CVertex *vertex; + Fvector P1, P2; + Fmatrix m_sphere; + + for (u32 i=1; ivertex_count(); i++) { + vertex = m_path->vertex(i-1); + P1 = ai().level_graph().vertex_position(vertex->data().level_vertex_id()); + vertex = m_path->vertex(i); + P2 = ai().level_graph().vertex_position(vertex->data().level_vertex_id()); + if (!fis_zero(P1.distance_to_sqr(P2),EPS_L)) + Level().debug_renderer().draw_line (Fidentity,P1,P2,D3DCOLOR_XRGB(0,0,255)); + m_sphere.identity(); + m_sphere.scale(0.25, 0.25, 0.25); + m_sphere.translate_add(P2); + Level().debug_renderer().draw_ellipse(m_sphere, D3DCOLOR_XRGB(0,255,255)); + //Level().debug_renderer().draw_aabb (P1,0.5f,0.5f,0.5f,D3DCOLOR_XRGB(0,255,255)); + } + + vertex = m_path->vertex(0); + P1 = ai().level_graph().vertex_position(vertex->data().level_vertex_id()); + vertex = m_path->vertex(m_path->vertex_count() - 1); + P2 = ai().level_graph().vertex_position(vertex->data().level_vertex_id()); + if (!fis_zero(P1.distance_to_sqr(P2),EPS_L)) + Level().debug_renderer().draw_line (Fidentity,P1,P2,D3DCOLOR_XRGB(0,0,255)); + m_sphere.identity(); + m_sphere.scale(0.25, 0.25, 0.25); + m_sphere.translate_add(P2); + Level().debug_renderer().draw_ellipse(m_sphere, D3DCOLOR_XRGB(0,255,255)); + //Level().debug_renderer().draw_aabb (P1,0.5f,0.5f,0.5f,D3DCOLOR_XRGB(0,255,255)); +} +#endif diff --git a/src/xrGameLA/ai/monsters/rats/rat_state_activation.cpp b/src/xrGameLA/ai/monsters/rats/rat_state_activation.cpp new file mode 100644 index 000000000..e914757ac --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/rat_state_activation.cpp @@ -0,0 +1,187 @@ + +#include "pch_script.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../game_level_cross_table.h" +#include "../../../game_graph.h" +#include "ai_rat_space.h" +#include "../../../../KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../item_manager.h" +#include "../../../memory_space.h" +#include "../../../ai_object_location.h" +#include "../../../movement_manager.h" +#include "../../../sound_player.h" +#include "ai_rat_impl.h" +#include "../../../ai_space.h" + +using namespace RatSpace; + +void CAI_Rat::activate_state_free_active() +{ + m_tSpawnPosition.set (m_home_position); + m_fGoalChangeDelta = m_fSafeGoalChangeDelta; + m_tVarGoal.set (m_tGoalVariation); + m_fASpeed = m_fAngleSpeed; + + if (bfCheckIfGoalChanged()) { + if ((Position().distance_to(m_tSpawnPosition) > m_fStableDistance) || (::Random.randF(0,1) > m_fChangeActiveStateProbability)) + if (Position().distance_to(m_home_position) > m_fMaxHomeRadius) + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + else + vfChooseNewSpeed(); + else { + if (can_stand_here()) + vfRemoveActiveMember(); + } + } + + if ((fis_zero(m_fSpeed) && (angle_difference(movement().m_body.target.yaw,movement().m_body.current.yaw) < PI_DIV_6))) + vfChooseNewSpeed(); + + vfUpdateTime(m_fTimeUpdateDelta); + + set_movement_type(false); + + sound().play (eRatSoundVoice, 45*1000,15*1000); +} + +void CAI_Rat::activate_state_free_passive() +{ + if (memory().enemy().selected()) { + m_fGoalChangeTime = 0; + add_active_member(true); + return; + } + + if (m_fMorale < m_fMoraleNormalValue) { + add_active_member(true); + return; + } + + if ((m_tLastSound.dwTime >= m_dwLastUpdateTime) && ((!m_tLastSound.tpEntity) || (m_tLastSound.tpEntity->g_Team() != g_Team()))) { + add_active_member(true); + return; + } + + m_fSpeed = 0.f; + + vfAddStandingMember(); + add_active_member(); + + sound().play (eRatSoundVoice,45*1000,15*1000); +} + +void CAI_Rat::activate_turn() +{ + float m_heading, m_pitch; + Fvector m_dest_direction; + Fvector m_enemy_position = get_enemy()->Position(); + Fvector m_temp = Position(); + m_dest_direction.x = (m_enemy_position.x - m_temp.x) / m_temp.distance_to(m_enemy_position); + m_dest_direction.y = (m_enemy_position.y - m_temp.y) / m_temp.distance_to(m_enemy_position); + m_dest_direction.z = (m_enemy_position.z - m_temp.z) / m_temp.distance_to(m_enemy_position); + m_dest_direction.getHP(m_heading, m_pitch); + set_pitch(m_pitch, m_heading); + m_tGoalDir = m_enemy_position; +} + +void CAI_Rat::activate_state_move() +{ + + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + set_movement_type(true,true); +} + +void CAI_Rat::activate_move() +{ + vfUpdateTime(m_fTimeUpdateDelta); + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + set_movement_type(m_bWayCanAdjustSpeed,m_bWayStraightForward); +} + +void CAI_Rat::activate_state_attack_range() +{ + if (!m_attack_rebuild) + { + time_attack_rebuild = Device.dwTimeGlobal; + m_attack_rebuild = true; + } + + if (m_attack_rebuild && Device.dwTimeGlobal - time_attack_rebuild > 5000) + { + m_attack_rebuild = false; + } + + m_fSpeed = 0.f; + + fire(true); + + set_movement_type(false); +} + +void CAI_Rat::activate_state_free_recoil() +{ + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + + set_movement_type(true,true); + + sound().play (eRatSoundVoice,45*1000,15*1000); +} +void CAI_Rat::activate_state_home() +{ + m_tSpawnPosition.set (m_home_position); + m_fGoalChangeDelta = m_fSafeGoalChangeDelta; + m_tVarGoal.set (m_tGoalVariation); + m_fASpeed = m_fAngleSpeed; + m_fSpeed = m_fSafeSpeed = m_fAttackSpeed; + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + set_movement_type(); +} + +void CAI_Rat::activate_state_eat() +{ + Fvector temp_position; + memory().item().selected()->Center (temp_position); + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set (temp_position); + + vfUpdateTime (m_fTimeUpdateDelta); + + bool a = temp_position.distance_to(Position()) <= m_fAttackDistance; + Fvector direction; + direction.sub (temp_position,Position()); + float y,p; + direction.getHP (y,p); + if (a && angle_difference(y,-movement().m_body.current.yaw) < PI_DIV_6) { + m_fSpeed = 0; + if (Device.dwTimeGlobal - m_previous_query_time > m_dwHitInterval) { + m_previous_query_time = Device.dwTimeGlobal; + const CEntityAlive *const_corpse = smart_cast(memory().item().selected()); + VERIFY (const_corpse); + CEntityAlive *corpse = const_cast(const_corpse); + VERIFY (corpse); + corpse->m_fFood -= m_fHitPower/10.f; + } + m_bFiring = true; + set_movement_type (false); + sound().play (eRatSoundEat); + } + else { + sound().remove_active_sounds(u32(-1)); + if (!a) + m_fSpeed = m_fMaxSpeed; + else + m_fSpeed = 0.f; + set_movement_type (true,true); + } +} \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/rats/rat_state_initialize.cpp b/src/xrGameLA/ai/monsters/rats/rat_state_initialize.cpp new file mode 100644 index 000000000..088b7c4f1 --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/rat_state_initialize.cpp @@ -0,0 +1,53 @@ +#include "pch_script.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../game_level_cross_table.h" +#include "../../../game_graph.h" +#include "ai_rat_space.h" +#include "../../../../KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../item_manager.h" +#include "../../../memory_space.h" +#include "../../../ai_object_location.h" +#include "../../../movement_manager.h" +#include "../../../sound_player.h" +#include "ai_rat_impl.h" +#include "../../../ai_space.h" + +void CAI_Rat::init_state_under_fire() +{ + if (!switch_if_enemy()&&get_if_dw_time()&&m_tLastSound.dwTime >= m_dwLastUpdateTime) + { + Fvector tTemp; + tTemp.setHP(-movement().m_body.current.yaw,-movement().m_body.current.pitch); + tTemp.normalize_safe(); + tTemp.mul(m_fUnderFireDistance); + m_tSpawnPosition.add(Position(),tTemp); + } + m_tGoalDir = m_tSpawnPosition; +} + +void CAI_Rat::init_free_recoil() +{ + m_dwLostRecoilTime = Device.dwTimeGlobal; + m_tRecoilPosition = m_tLastSound.tSavedPosition; + if (!switch_if_enemy()&&!switch_if_time()) + { + Fvector tTemp; + tTemp.setHP(-movement().m_body.current.yaw,-movement().m_body.current.pitch); + tTemp.normalize_safe(); + tTemp.mul(m_fUnderFireDistance); + m_tSpawnPosition.add(Position(),tTemp); + } +} + +void CAI_Rat::init_free_active() +{ + if (bfCheckIfGoalChanged()) { + if (Position().distance_to(m_home_position) > m_fMaxHomeRadius) + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + vfChooseNewSpeed(); + } +} \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/rats/rat_state_switch.cpp b/src/xrGameLA/ai/monsters/rats/rat_state_switch.cpp new file mode 100644 index 000000000..af8e59d47 --- /dev/null +++ b/src/xrGameLA/ai/monsters/rats/rat_state_switch.cpp @@ -0,0 +1,331 @@ +#include "pch_script.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../game_level_cross_table.h" +#include "../../../game_graph.h" +#include "ai_rat_space.h" +#include "../../../../KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../item_manager.h" +#include "../../../memory_space.h" +#include "../../../ai_object_location.h" +#include "../../../movement_manager.h" +#include "../../../sound_player.h" +#include "ai_rat_impl.h" +#include "../../../ai_space.h" +#include "ai/monsters/ai_monster_squad_manager.h" +#include "ai/monsters/ai_monster_squad.h" + +bool CAI_Rat::switch_to_attack_melee() +{ + if (!switch_if_porsuit() || !switch_if_home()) + { + return true; + } + return false; +} + +const CEntityAlive *CAI_Rat::get_enemy() +{ + return memory().enemy().selected(); +} + +bool CAI_Rat::switch_if_home() +{ + if (Position().distance_to(m_home_position) < m_fMaxHomeRadius) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_position() +{ + if (memory().enemy().selected()->Position().distance_to(Position()) > m_fAttackDistance) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_enemy() +{ + if (memory().enemy().selected()) + { + return true; + } + return false; +} + +bool CAI_Rat::get_morale() +{ + if (m_fMorale >= m_fMoraleNormalValue - EPS_L) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_to_eat() +{ + if (memory().item().selected()) + { + return true; + } + return false; +} + +bool CAI_Rat::get_if_dw_time() +{ + if (m_tLastSound.dwTime >= m_dwLastUpdateTime) + { + return true; + } + return false; +} + +bool CAI_Rat::get_if_tp_entity() +{ + if ( m_tLastSound.tpEntity && (m_tLastSound.tpEntity->g_Team() != g_Team()) && (!bfCheckIfSoundFrightful())) + { + return true; + } + return false; +} + +void CAI_Rat::set_previous_query_time() +{ + m_previous_query_time = Device.dwTimeGlobal; +} + +SRotation CAI_Rat::sub_rotation() +{ + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + SRotation sTemp; + mk_rotation(tTemp,sTemp); + return sTemp; +} + +CAI_Rat::ERatStates CAI_Rat::get_state() +{ + return ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),aiRatAttackMelee,aiRatAttackMelee,aiRatAttackMelee,aiRatRetreat,aiRatRetreat,this,30.f)); +} + +bool CAI_Rat::switch_if_porsuit() +{ + if (m_home_position.distance_to(memory().enemy().selected()->Position()) > m_fMaxPursuitRadius) + { + return true; + } + return false; +} + +void CAI_Rat::set_dir() +{ + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) { + CMonsterSquad *squad = monster_squad().get_squad(this); + Fvector m_enemy_position = memory().enemy().selected()->Position(); + if (squad && squad->SquadActive()) + { + float m_delta_Angle = angle_normalize((PI * 2) / squad->squad_alife_count()); + float m_heading, m_pitch; + Fvector m_temp, m_dest_direction; + m_temp = squad->GetLeader()->Position(); + m_dest_direction.x = (m_temp.x - m_enemy_position.x) / m_temp.distance_to(m_enemy_position); + m_dest_direction.y = (m_temp.y - m_enemy_position.y) / m_temp.distance_to(m_enemy_position); + m_dest_direction.z = (m_temp.z - m_enemy_position.z) / m_temp.distance_to(m_enemy_position); + m_dest_direction.getHP(m_heading, m_pitch); + m_heading = angle_normalize(m_heading + m_delta_Angle * squad->get_index(this)); + m_dest_direction.setHP(m_heading, m_pitch); + m_dest_direction.mul(0.5f); + m_enemy_position.add(m_enemy_position,m_dest_direction); + } + + m_tGoalDir.set(m_enemy_position); + } +} + +void CAI_Rat::set_dir_m() +{ + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set(memory().memory(memory().enemy().selected()).m_object_params.m_position); +} + +void CAI_Rat::set_sp_dir() +{ + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir = m_tSpawnPosition; +} + +void CAI_Rat::set_way_point() +{ + m_tGoalDir = get_next_target_point(); +} + +bool CAI_Rat::switch_if_no_enemy() +{ + if (!switch_if_enemy() || + (switch_if_enemy() && + ( + !switch_if_alife() + || + ( + (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time > m_dwRetreatTime) && + ( + (m_tLastSound.dwTime < m_dwLastUpdateTime) || + !m_tLastSound.tpEntity || + (m_tLastSound.tpEntity->g_Team() == g_Team()) || + !bfCheckIfSoundFrightful() + ) + ) + ) + ) + ) + { + memory().enable (memory().enemy().selected(),false); + return true; + } + return false; +} + +bool CAI_Rat::switch_to_free_recoil() +{ + if ( + (m_tLastSound.dwTime >= m_dwLastUpdateTime) && + ( + !m_tLastSound.tpEntity || + ( + (!switch_to_eat() || (memory().item().selected()->ID() != m_tLastSound.tpEntity->ID())) && + (m_tLastSound.tpEntity->g_Team() != g_Team()) + ) + ) && + !switch_if_enemy() + ) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_lost_time() +{ + if (switch_if_enemy() && (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time >= m_dwLostMemoryTime)) + { + memory().enable(memory().enemy().selected(),false); + return true; + } + return false; +} + +bool CAI_Rat::switch_if_lost_rtime() +{ + if (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time >= m_dwLostRecoilTime) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_alife() +{ + if (memory().enemy().selected()->g_Alive()) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_diff() +{ + SRotation sTemp = sub_rotation(); + if (angle_difference(movement().m_body.current.yaw,sTemp.yaw) > m_fAttackAngle) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_dist_no_angle() +{ + if (!switch_if_position() && switch_if_diff()) { + m_fSpeed = 0.f; + return true; + } + return false; +} + + +bool CAI_Rat::switch_if_dist_angle() +{ + if(!switch_if_position() && !switch_if_diff()) + { + return true; + } + return false; +} + +void CAI_Rat::set_rew_position() +{ + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + tTemp.sub(Position(),memory().enemy().selected()->Position()); + tTemp.normalize_safe(); + tTemp.mul(m_fRetreatDistance); + m_tSpawnPosition.add(Position(),tTemp); +} + +bool CAI_Rat::switch_if_time() +{ + if (m_dwLastUpdateTime > m_dwLostRecoilTime + 2000) + { + return true; + } + return false; +} + +void CAI_Rat::set_rew_cur_position() +{ + Fvector tTemp; + tTemp.setHP(-movement().m_body.current.yaw,-movement().m_body.current.pitch); + tTemp.normalize_safe(); + tTemp.mul(m_fUnderFireDistance); + m_tSpawnPosition.add(Position(),tTemp); +} + +void CAI_Rat::set_home_pos() +{ + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set (m_home_position); +} + +void CAI_Rat::set_goal_time(float f_val) +{ + m_fGoalChangeTime = 0; +} + +bool CAI_Rat::get_alife() +{ + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return false; + } + return true; +} + +void CAI_Rat::set_movement_type(bool bCanAdjustSpeed, bool bStraightForward) +{ + m_bCanAdjustSpeed = bCanAdjustSpeed; + m_bStraightForward = bStraightForward; +} + +bool CAI_Rat::check_completion_no_way() +{ + if(time_to_next_attack + time_old_attack < Device.dwTimeGlobal ) return true; + return false; +} \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/scanning_ability.h b/src/xrGameLA/ai/monsters/scanning_ability.h new file mode 100644 index 000000000..6d7f746c6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/scanning_ability.h @@ -0,0 +1,61 @@ +#pragma once + +template +class CScanningAbility { + _Object *object; + + // external members + float critical_value; + float scan_radius; + float scan_radius_y; + float velocity_threshold; + float decrease_value; + float scan_trace_time_freq; + + ref_sound sound_scan; + + // internal members + enum EScanState { + eStateDisabled, + eStateNotActive, + eStateScanning + } state; + + float scan_value; + + SPPInfo m_effector_info; + float m_effector_time; + float m_effector_time_attack; + float m_effector_time_release; + + u32 time_last_trace; + + bool m_this_scan; + +public: + void init_external (_Object *obj) {object = obj;} + void on_destroy (); + + void load (LPCSTR section); + virtual void reinit (); + + void schedule_update (); + void frame_update (u32 dt); + + void enable (); + void disable (); + + virtual void on_scan_success () {} + virtual void on_scanning () {} + +private: + float get_velocity (CObject *obj); + +#ifdef DEBUG +public: + float get_scan_value (){return scan_value;} +#endif + +}; + +#include "scanning_ability_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/scanning_ability_inline.h b/src/xrGameLA/ai/monsters/scanning_ability_inline.h new file mode 100644 index 000000000..3ba6911a9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/scanning_ability_inline.h @@ -0,0 +1,161 @@ +#pragma once +#include "ai_monster_effector.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CScanningAbilityAbstract CScanningAbility<_Object> + +TEMPLATE_SPECIALIZATION +void CScanningAbilityAbstract::on_destroy() +{ + if (m_this_scan) object->can_scan = true; + m_this_scan = false; +} + +TEMPLATE_SPECIALIZATION +void CScanningAbilityAbstract::load(LPCSTR section) +{ + ::Sound->create(sound_scan, pSettings->r_string(section,"scan_sound"), st_Effect,SOUND_TYPE_WORLD); + + critical_value = pSettings->r_float(section, "scan_critical_value"); + scan_radius = pSettings->r_float(section, "scan_radius"); + scan_radius_y = pSettings->r_float(section, "scan_radius_y"); + velocity_threshold = pSettings->r_float(section, "scan_velocity_threshold"); + decrease_value = pSettings->r_float(section, "scan_decrease_value"); + scan_trace_time_freq = pSettings->r_float(section, "scan_trace_time_freq"); + + VERIFY (!fis_zero(scan_trace_time_freq)); + + // load scan effector + LPCSTR ppi_section = pSettings->r_string(section, "scan_effector_section"); + m_effector_info.duality.h = pSettings->r_float(ppi_section, "duality_h"); + m_effector_info.duality.v = pSettings->r_float(ppi_section, "duality_v"); + m_effector_info.gray = pSettings->r_float(ppi_section, "gray"); + m_effector_info.blur = pSettings->r_float(ppi_section, "blur"); + m_effector_info.noise.intensity = pSettings->r_float(ppi_section, "noise_intensity"); + m_effector_info.noise.grain = pSettings->r_float(ppi_section, "noise_grain"); + m_effector_info.noise.fps = pSettings->r_float(ppi_section, "noise_fps"); + VERIFY(!fis_zero(m_effector_info.noise.fps)); + + sscanf(pSettings->r_string(ppi_section, "color_base"), "%f,%f,%f", &m_effector_info.color_base.r, &m_effector_info.color_base.g, &m_effector_info.color_base.b); + sscanf(pSettings->r_string(ppi_section, "color_gray"), "%f,%f,%f", &m_effector_info.color_gray.r, &m_effector_info.color_gray.g, &m_effector_info.color_gray.b); + sscanf(pSettings->r_string(ppi_section, "color_add"), "%f,%f,%f", &m_effector_info.color_add.r, &m_effector_info.color_add.g, &m_effector_info.color_add.b); + + m_effector_time = pSettings->r_float(ppi_section, "time"); + m_effector_time_attack = pSettings->r_float(ppi_section, "time_attack"); + m_effector_time_release = pSettings->r_float(ppi_section, "time_release"); +} + +TEMPLATE_SPECIALIZATION +void CScanningAbilityAbstract::reinit() +{ + state = eStateDisabled; + scan_value = 0.f; + + time_last_trace = 0; + + m_this_scan = false; +} + +TEMPLATE_SPECIALIZATION +void CScanningAbilityAbstract::schedule_update() +{ + // check if we end scanning + if (m_this_scan && !sound_scan._feedback()) { + object->can_scan = true; + m_this_scan = false; + } + + if (state == eStateDisabled) return; + if (!object->g_Alive()) return; + + CActor *scan_obj = smart_cast(Level().CurrentEntity()); + if (!scan_obj) return; + + // проверка на активность + if (state == eStateNotActive) { + float distance = scan_obj->Position().distance_to(object->Position()); + float height_distance = object->Position().y - scan_obj->Position().y; + if (distance < scan_radius && abs(height_distance) < scan_radius_y) + { + Msg("in radius"); state = eStateScanning; + } + } + + if (state == eStateNotActive) return; + + if (state == eStateScanning) { + // обновить scan_value + float vel = get_velocity(scan_obj); + if ( vel > velocity_threshold) { + + // трейсить не чаще, чем scan_trace_time_freq + if (time_last_trace + u32(1000 / scan_trace_time_freq) < Device.dwTimeGlobal) { + time_last_trace = Device.dwTimeGlobal; + scan_value += vel; + } + + if (sound_scan._feedback()) sound_scan.set_position(scan_obj->Position()); + else { + if (object->can_scan) { + // играть звук + ::Sound->play_at_pos(sound_scan, 0, scan_obj->Position()); + + // постпроцесс + // TODO: make this postprocess with static check (only one for all scanners) + Actor()->Cameras().AddPPEffector(new CMonsterEffector(m_effector_info, m_effector_time, m_effector_time_attack, m_effector_time_release)); + + object->can_scan = false; + m_this_scan = true; + } + } + on_scanning(); + } + } + + if (scan_value > critical_value) { + on_scan_success(); + state = eStateDisabled; + } + +} + +TEMPLATE_SPECIALIZATION +void CScanningAbilityAbstract::frame_update(u32 dt) +{ + if (state != eStateScanning) return; + + if (scan_value < 0) scan_value = 0.f; + else if (scan_value > 0) { + scan_value -= decrease_value * float(dt) / 1000; + } +} + + +TEMPLATE_SPECIALIZATION +float CScanningAbilityAbstract::get_velocity(CObject *obj) +{ + CActor *actor = smart_cast(obj); + return (actor->character_physics_support()->movement()->GetVelocityActual()); +} + +TEMPLATE_SPECIALIZATION +void CScanningAbilityAbstract::enable() +{ + if (state != eStateDisabled) return; + + state = eStateNotActive; + scan_value = 0.f; +} + +TEMPLATE_SPECIALIZATION +void CScanningAbilityAbstract::disable() +{ + state = eStateDisabled; + scan_value = 0.f; +} + + +#undef TEMPLATE_SPECIALIZATION \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/snork/snork.cpp b/src/xrGameLA/ai/monsters/snork/snork.cpp new file mode 100644 index 000000000..840159a3f --- /dev/null +++ b/src/xrGameLA/ai/monsters/snork/snork.cpp @@ -0,0 +1,306 @@ +#include "stdafx.h" +#include "snork.h" +#include "snork_state_manager.h" +#include "../../../detail_path_manager_space.h" +#include "../../../detail_path_manager.h" +#include "../../../level.h" +#include "../monster_velocity_space.h" +#include "../../../sound_player.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" +#include "../../../PHMovementControl.h" + +#ifdef _DEBUG +# include +# include "../../../actor.h" +# include "../../../ai_object_location.h" +# include "../../../level_debug.h" +# include "../../../cover_point.h" +# include "../monster_cover_manager.h" +#endif + + + + +CSnork::CSnork() +{ + StateMan = new CStateManagerSnork(this); + com_man().add_ability(ControlCom::eControlJump); + com_man().add_ability(ControlCom::eControlThreaten); +} + +CSnork::~CSnork() +{ + xr_delete (StateMan); +} + +void CSnork::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().accel_load (section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimStandIdle, eAnimStandDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandDamaged, "stand_idle_damaged_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimLieIdle, "lie_sleep_", -1, &velocity_none, PS_LIE ); + anim().AddAnim(eAnimSleep, "lie_sleep_", -1, &velocity_none, PS_LIE ); + anim().AddAnim(eAnimLieStandUp, "lie_stand_up_", -1, &velocity_none, PS_LIE ); + anim().AddAnim(eAnimLieToSleep, "lie_to_sleep_", -1, &velocity_none, PS_LIE ); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_damaged_", -1, &velocity_walk_dmg, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_run_damaged_", -1, &velocity_run_dmg, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimRun, "stand_run_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimDie, "stand_die_", 0, &velocity_none, PS_STAND); + anim().AddAnim(eAnimLookAround, "stand_look_around_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimSteal, "stand_steal_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimDragCorpse, "stand_drag_", -1, &velocity_drag, PS_STAND); + anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + + // define transitions + anim().AddTransition(eAnimStandLieDown, eAnimSleep, eAnimLieToSleep, false); + anim().AddTransition(PS_STAND, eAnimSleep, eAnimStandLieDown, true); + anim().AddTransition(PS_STAND, PS_LIE, eAnimStandLieDown, false); + anim().AddTransition(PS_LIE, PS_STAND, eAnimLieStandUp, false, SKIP_IF_AGGRESSIVE); + + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSleep); + anim().LinkAction(ACT_REST, eAnimStandIdle); + anim().LinkAction(ACT_DRAG, eAnimDragCorpse); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimLookAround); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +void CSnork::reinit() +{ + inherited::reinit (); + + if(CCustomMonster::use_simplified_visual() ) return; + move().load_velocity(*cNameSect(), "Velocity_JumpGround",MonsterMovement::eSnorkVelocityParameterJumpGround); + com_man().load_jump_data("stand_attack_2_0",0, "stand_attack_2_1", "stand_somersault_0", u32(-1), MonsterMovement::eSnorkVelocityParameterJumpGround,0); + + start_threaten = false; + com_man().set_threaten_data ("stand_threaten_0", 0.63f); + + // TODO: remove this + m_target_node = 0; +} + +void CSnork::UpdateCL() +{ + inherited::UpdateCL (); + + ////////////////////////////////////////////////////////////////////////// + //CObject *obj = Level().CurrentEntity(); + //if (!obj) return; + + //find_geometry (); + ////////////////////////////////////////////////////////////////////////// + +#ifdef _DEBUG + // test + CObject *obj = Level().CurrentEntity(); + if (!obj) return; + const CCoverPoint *point = CoverMan->find_cover(obj->Position(), 10.f, 30.f); + + DBG().level_info(this).clear(); + if (point) { + DBG().level_info(this).add_item (point->position(),COLOR_RED); + + Fvector pos; + pos.set(Position()); + pos.y+=5.f; + + DBG().level_info(this).add_item (Position(),pos,COLOR_GREEN); + } +#endif + +} + +#define TRACE_RANGE 30.f + +float CSnork::trace(const Fvector &dir) +{ + float ret_val = flt_max; + + collide::rq_result l_rq; + + Fvector trace_from; + Center (trace_from); + + float trace_dist = Radius() + TRACE_RANGE; + + if (Level().ObjectSpace.RayPick(trace_from, dir, trace_dist, collide::rqtStatic, l_rq, this)) { + if ((l_rq.range < trace_dist)) + ret_val = l_rq.range; + } + + return ret_val; +} + +#define JUMP_DISTANCE 10.f +bool CSnork::find_geometry(Fvector &dir) +{ + // 1. trace direction + dir = Direction(); + float range; + + if (trace_geometry(dir, range)) { + if (range < JUMP_DISTANCE) { + return true; + } + } + + return false; +} + +bool CSnork::trace_geometry(const Fvector &d, float &range) +{ + Fvector dir; + float h, p; + + Fvector Pl,Pc,Pr; + Fvector center; + Center (center); + + range = trace (d); + if (range > TRACE_RANGE) return false; + + float angle = asin(1.f / range); + + // trace center ray + dir = d; + + dir.getHP (h,p); + p += angle; + dir.setHP (h,p); + dir.normalize_safe (); + + range = trace (dir); + if (range > TRACE_RANGE) return false; + + Pc.mad (center, dir, range); + + // trace left ray + Fvector temp_p; + temp_p.mad (Pc, XFORM().i, Radius() / 2); + dir.sub (temp_p, center); + dir.normalize_safe (); + + range = trace (dir); + if (range > TRACE_RANGE) return false; + + Pl.mad (center, dir, range); + + // trace right ray + Fvector inv = XFORM().i; + inv.invert (); + temp_p.mad (Pc, inv, Radius() / 2); + dir.sub (temp_p, center); + dir.normalize_safe (); + + range = trace (dir); + if (range > TRACE_RANGE) return false; + + Pr.mad (center, dir, range); + + float h1,p1,h2,p2; + + Fvector().sub(Pl, Pc).getHP(h1,p1); + Fvector().sub(Pc, Pr).getHP(h2,p2); + + return (fsimilar(h1,h2,0.1f) && fsimilar(p1,p2,0.1f)); +} + +void CSnork::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_CHECK_CORPSE) == ASP_CHECK_CORPSE) { + com_man().seq_run(anim().get_motion_id(eAnimCheckCorpse)); + } + + if ((spec_params & ASP_STAND_SCARED) == ASP_STAND_SCARED) { + anim().SetCurAnim(eAnimLookAround); + return; + } +} + +void CSnork::HitEntityInJump(const CEntity *pEntity) +{ + + SAAParam ¶ms = anim().AA_GetParams("stand_attack_2_1"); + HitEntity (pEntity, params.hit_power, params.impulse, params.impulse_dir); +} + +////////////////////////////////////////////////////////////////////////// +void CSnork::jump(const Fvector &position, float factor) +{ + com_man().script_jump (position, factor); + sound().play (MonsterSound::eMonsterSoundAggressive); +} + +bool CSnork::check_start_conditions(ControlCom::EControlType type) +{ + if (!inherited::check_start_conditions(type)) return false; + + if (type == ControlCom::eControlThreaten) { + if (!start_threaten) return false; + + start_threaten = false; + + if (Random.randI(100) < 50) return false; + + } + + return true; +} + +void CSnork::on_activate_control(ControlCom::EControlType type) +{ + if (type == ControlCom::eControlThreaten) { + sound().play (MonsterSound::eMonsterSoundThreaten); + } +} +////////////////////////////////////////////////////////////////////////// + + +#ifdef _DEBUG +void CSnork::debug_on_key(int key) +{ + CActor *actor = smart_cast(Level().CurrentEntity()); + if (!actor) return; + + switch (key){ + case DIK_1: + m_target_node = actor->ai_location().level_vertex_id(); + } +} +#endif + diff --git a/src/xrGameLA/ai/monsters/snork/snork.h b/src/xrGameLA/ai/monsters/snork/snork.h new file mode 100644 index 000000000..afe888780 --- /dev/null +++ b/src/xrGameLA/ai/monsters/snork/snork.h @@ -0,0 +1,49 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../../../script_export_space.h" + +class CSnork : public CBaseMonster { + typedef CBaseMonster inherited; + + SVelocityParam m_fsVelocityJumpPrepare; + SVelocityParam m_fsVelocityJumpGround; + +public: + CSnork (); + virtual ~CSnork (); + + virtual void Load (LPCSTR section); + virtual void reinit (); + virtual void UpdateCL (); + virtual void CheckSpecParams (u32 spec_params); + virtual void jump (const Fvector &position, float factor); + virtual bool ability_jump_over_physics () {return true;} + virtual bool ability_distant_feel () {return true;} + virtual void HitEntityInJump (const CEntity *pEntity); + + bool find_geometry (Fvector &dir); + float trace (const Fvector &dir); + + bool trace_geometry (const Fvector &d, float &range); + + virtual bool ability_can_drag () {return true;} + + virtual bool check_start_conditions (ControlCom::EControlType type); + virtual void on_activate_control (ControlCom::EControlType); + +private: +#ifdef _DEBUG + virtual void debug_on_key (int key); +#endif + +public: + + u32 m_target_node; + bool start_threaten; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CSnork) +#undef script_type_list +#define script_type_list save_type_list(CSnork) diff --git a/src/xrGameLA/ai/monsters/snork/snork_jump.cpp b/src/xrGameLA/ai/monsters/snork/snork_jump.cpp new file mode 100644 index 000000000..bbd15a320 --- /dev/null +++ b/src/xrGameLA/ai/monsters/snork/snork_jump.cpp @@ -0,0 +1,178 @@ +#include "stdafx.h" +#include "snork.h" +#include "snork_jump.h" +//#include "../jump_ability.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../level.h" + +//CSnorkJump::CSnorkJump(CSnork *monster) +//{ +// m_object = monster; +// +// //m_jumper = xr_new(); +// //m_jumper->init_external (m_object); +// //m_jumper->reinit (MotionID(),MotionID(),MotionID()); +//} +// +//CSnorkJump::~CSnorkJump() +//{ +// xr_delete(m_jumper); +//} +// +//void CSnorkJump::load(LPCSTR section) +//{ +// //m_jumper->load(section); +//} +// +//void CSnorkJump::update_frame() +//{ +// //if (!m_jumper->active()) return; +// +// //if (m_specific_jump) { +// // float dist = trace_current(10.f); +// // if (dist > 10.f) { +// // m_jumper->stop(); +// // return; +// // } else if (dist < 2.f) { +// // m_jumper->stop(); +// // init_jump_specific(); +// // +// // Fvector dir; +// // float h,p; +// // float h2,p2; +// // Fvector().sub(m_target_object->Position(), m_object->Position()).getHP(h,p); +// // m_target_object->Direction().getHP(h2,p2); +// // dir.set (1,0,0); +// // dir.setHP (h,p2); +// // dir.normalize(); +// // +// // Fvector pos; +// // pos.mad (m_target_object->Position(), dir, 4.f); +// // pos.y+=2.f; +// +// // m_jumper->jump(pos, m_velocity_mask); +// // m_specific_jump = false; +// // m_jumper->disable_bounce (); +// // } +// //} +// // +// //m_jumper->update_frame(); +//} +// +//void CSnorkJump::try_to_jump(u32 velocity_mask) +//{ +// //CObject *target = const_cast(m_object->EnemyMan.get_enemy()); +// CObject *target = Level().CurrentEntity(); +// if (!target) return; +// +// m_specific_jump = false; +// m_target_object = target; +// m_velocity_mask = velocity_mask; +// +// // 1. Get enemy position, yaw +// Fvector source_position = m_object->Position(); +// Fvector target_position; +// target->Center (target_position); +// +// // получить вектор направления и его мир угол +// Fvector dir; +// float dir_yaw, dir_pitch; +// +// dir.sub (target_position, source_position); +// dir.getHP (dir_yaw, dir_pitch); +// +// // проверка на angle и на dist +// float yaw_current, yaw_target; +// m_object->control().direction().get_heading(yaw_current, yaw_target); +// if (angle_difference(yaw_current, -dir_yaw) < PI_DIV_6) { +// try_jump_normal(); +// return; +// } +// +// //try_jump_specific(); +//} +// +//void CSnorkJump::try_jump_normal() +//{ +// if (!m_object->EnemyMan.see_enemy_now()) return; +// +// if (m_jumper->can_jump(m_target_object)) { +// init_jump_normal (); +// m_jumper->jump (m_target_object, m_velocity_mask); +// } +//} +// +//void CSnorkJump::try_jump_specific() +//{ +// float yaw, pitch; +// Fvector().sub (m_target_object->Position(), m_object->Position()).getHP(yaw, pitch); +// +// // получить вектор направления и его мир угол +// //// проверка на angle и на dist +// //if (angle_difference(m_object->movement().m_body.current.yaw, -yaw) < PI_DIV_2) { +// // return; +// //} +// +// // 2. Trace geometry +// m_cur_dist = trace_current(10.f); +// if (m_cur_dist > 10.f) return; +// +// // 3. Setup jump +// init_jump_normal(); +// +// // 4. Perform jump +// Fvector pos; +// pos.mad (m_object->Position(), m_object->Direction(), 10.f); +// pos.y += 1.5; +// m_jumper->jump (pos, m_velocity_mask); +// +// m_jumper->disable_bounce(); +// +// m_specific_jump = true; +//} +// +//void CSnorkJump::init_jump_normal() +//{ +// MotionID def1, def2, def3; +// IKinematicsAnimated *pSkel = smart_cast(m_object->Visual()); +// +// def1 = pSkel->ID_Cycle_Safe("stand_attack_2_0"); VERIFY(def1); +// def2 = pSkel->ID_Cycle_Safe("stand_attack_2_1"); VERIFY(def2); +// def3 = pSkel->ID_Cycle_Safe("stand_somersault_0"); VERIFY(def3); +// +// m_jumper->reinit (def1, def2, def3); +//} +// +//void CSnorkJump::init_jump_specific() +//{ +// MotionID def1, def2, def3; +// IKinematicsAnimated *pSkel = smart_cast(m_object->Visual()); +// +// def1 = pSkel->ID_Cycle_Safe("stand_attack_2_0"); VERIFY(def1); +// def2 = pSkel->ID_Cycle_Safe("jump_rs_0"); VERIFY(def2); +// def3 = pSkel->ID_Cycle_Safe("stand_somersault_0"); VERIFY(def3); +// +// m_jumper->reinit (def1, def2, def3); +//} +// +//float CSnorkJump::trace_current(float dist) +//{ +// float ret_val = flt_max; +// +// BOOL enabled = m_object->getEnabled(); +// m_object->setEnabled(FALSE); +// collide::rq_result l_rq; +// +// Fvector trace_from; +// m_object->Center(trace_from); +// +// float trace_dist = m_object->Radius() + dist; +// +// if (Level().ObjectSpace.RayPick(trace_from, m_object->Direction(), trace_dist, collide::rqtStatic, l_rq)) { +// if ((l_rq.range < trace_dist)) ret_val = l_rq.range; +// } +// +// m_object->setEnabled(enabled); +// +// return ret_val; +//} diff --git a/src/xrGameLA/ai/monsters/snork/snork_jump.h b/src/xrGameLA/ai/monsters/snork/snork_jump.h new file mode 100644 index 000000000..4c1009941 --- /dev/null +++ b/src/xrGameLA/ai/monsters/snork/snork_jump.h @@ -0,0 +1,31 @@ +#pragma once + +//class CJumpingAbility; +class CSnork; + +class CSnorkJump { + CSnork *m_object; + //CJumpingAbility *m_jumper; + + float m_cur_dist; + bool m_specific_jump; + + CObject *m_target_object; + u32 m_velocity_mask; + +public: +// CSnorkJump (CSnork *monster); +// ~CSnorkJump (); +// void load (LPCSTR section); +// void update_frame (); +// void try_to_jump (u32 velocity_mask); +// +//private: +// void init_jump_normal (); +// void init_jump_specific (); +// void try_jump_normal (); +// void try_jump_specific (); +// +// +// float trace_current (float dist); +}; \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/snork/snork_script.cpp b/src/xrGameLA/ai/monsters/snork/snork_script.cpp new file mode 100644 index 000000000..56624f412 --- /dev/null +++ b/src/xrGameLA/ai/monsters/snork/snork_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "snork.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CSnork::script_register(lua_State *L) +{ + module(L) + [ + class_("CSnork") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/snork/snork_state_manager.cpp b/src/xrGameLA/ai/monsters/snork/snork_state_manager.cpp new file mode 100644 index 000000000..980a79f03 --- /dev/null +++ b/src/xrGameLA/ai/monsters/snork/snork_state_manager.cpp @@ -0,0 +1,83 @@ +#include "stdafx.h" +#include "snork.h" +#include "snork_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../../../level.h" +#include "../../../level_debug.h" +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../states/state_look_point.h" +#include "../states/state_test_look_actor.h" +#include "../states/state_test_state.h" +#include "../states/monster_state_help_sound.h" + +#include "../../../entitycondition.h" + +CStateManagerSnork::CStateManagerSnork(CSnork *obj) : inherited(obj) +{ + add_state(eStateRest, new CStateMonsterRest(obj)); + add_state(eStatePanic, new CStateMonsterPanic(obj)); + add_state(eStateAttack, new CStateMonsterAttack(obj)); + add_state(eStateEat, new CStateMonsterEat(obj)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(obj)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(obj)); + add_state(eStateHitted, new CStateMonsterHitted(obj)); + + add_state(eStateFindEnemy, new CStateMonsterTestCover(obj)); + add_state(eStateHearHelpSound, new CStateMonsterHearHelpSound(obj)); +} + +CStateManagerSnork::~CStateManagerSnork() +{ +} + +void CStateManagerSnork::execute() +{ + u32 state_id = u32(-1); + + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (check_state(eStateHearHelpSound)) { + state_id = eStateHearHelpSound; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + + + //state_id = eStateFindEnemy; + + select_state(state_id); + + if ((current_substate == eStateAttack) && (current_substate != prev_substate)) { + object->start_threaten = true; + } + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; + +} + diff --git a/src/xrGameLA/ai/monsters/snork/snork_state_manager.h b/src/xrGameLA/ai/monsters/snork/snork_state_manager.h new file mode 100644 index 000000000..df040b0a8 --- /dev/null +++ b/src/xrGameLA/ai/monsters/snork/snork_state_manager.h @@ -0,0 +1,16 @@ +#pragma once +#include "../monster_state_manager.h" + +class CSnork; + +class CStateManagerSnork : public CMonsterStateManager +{ +private: + typedef CMonsterStateManager inherited; + +public: + CStateManagerSnork (CSnork *obj); + virtual ~CStateManagerSnork (); + + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/state.h b/src/xrGameLA/ai/monsters/state.h new file mode 100644 index 000000000..872d5700f --- /dev/null +++ b/src/xrGameLA/ai/monsters/state.h @@ -0,0 +1,75 @@ +#pragma once + +#include "state_defs.h" + + +template +class CState { + typedef CState<_Object> CSState; +public: + CState (_Object *obj, void *data = 0); + virtual ~CState (); + + virtual void reinit (); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual void reset (); + + virtual bool check_completion () {return false;} + virtual bool check_start_conditions () {return true;} + + virtual void reselect_state () {} + virtual void check_force_state () {} + + CSState *get_state (u32 state_id); + CSState *get_state_current (); + + void fill_data_with (void *ptr_src, u32 size); + + u32 time_started (){return time_state_started;} +protected: + void select_state (u32 new_state_id); + void add_state (u32 state_id, CSState *s); + + virtual void setup_substates (){} + + EMonsterState get_state_type (); + + u32 current_substate; + u32 prev_substate; + + u32 time_state_started; + u32 controlling_value; + + _Object *object; + + void *_data; + +private: + void free_mem (); + + xr_map substates; + typedef typename xr_map::iterator STATE_MAP_IT; + +}; + +template +class CStateMove : public CState<_Object> { + typedef CState<_Object> inherited; +public: + CStateMove (_Object *obj, void *data = 0) : inherited(obj,data){} + virtual ~CStateMove (){} + virtual void initialize() { + inherited::initialize(); + object->path().prepare_builder(); + } +}; + + + +#include "state_inline.h" + diff --git a/src/xrGameLA/ai/monsters/state_defs.h b/src/xrGameLA/ai/monsters/state_defs.h new file mode 100644 index 000000000..4db24b572 --- /dev/null +++ b/src/xrGameLA/ai/monsters/state_defs.h @@ -0,0 +1,206 @@ +#pragma once + +enum EMonsterState +{ +#ifndef DEBUG + eGlobalState = u32(1) << 15, +#else + eGlobalState = 1 << 15, +#endif + // ------------------------------------------------------------- + + eStateRest = eGlobalState << 1, + + eStateRest_WalkGraphPoint = eStateRest | 1, + eStateRest_Idle = eStateRest | 2, + eStateRest_Fun = eStateRest | 3, + eStateRest_Sleep = eStateRest | 4, + eStateRest_MoveToHomePoint = eStateRest | 5, + eStateRest_WalkToCover = eStateRest | 6, + eStateRest_LookOpenPlace = eStateRest | 7, + + // ------------------------------------------------------------- + + eStateEat = eGlobalState << 2, + + eStateEat_CorpseApproachRun = eStateEat | 1, + eStateEat_CorpseApproachWalk = eStateEat | 2, + eStateEat_CheckCorpse = eStateEat | 3, + eStateEat_Eat = eStateEat | 4, + eStateEat_WalkAway = eStateEat | 5, + eStateEat_Rest = eStateEat | 6, + eStateEat_Drag = eStateEat | 7, + + // ------------------------------------------------------------- + + eStateAttack = eGlobalState << 3, + + eStateAttack_Run = eStateAttack | 1, + eStateAttack_Melee = eStateAttack | 2, + eStateAttack_RunAttack = eStateAttack | 3, + eStateAttack_RunAway = eStateAttack | 4, + eStateAttack_FindEnemy = eStateAttack | 5, + eStateAttack_Steal = eStateAttack | 6, + + eStateAttack_AttackHidden = eStateAttack | 7, + + eStateAttack_Hide = eStateAttack | 9, + + eStateAttack_HideInCover = eStateAttack | 10, + eStateAttack_MoveOut = eStateAttack | 11, + eStateAttack_CampInCover = eStateAttack | 12, + eStateAttack_ControlFire = eStateAttack | 13, + eStateAttack_ControlTube = eStateAttack | 14, + eStateAttack_HideInCoverLite = eStateAttack | 15, + + eStateAttackCamp = eStateAttack | 16, + eStateAttackCamp_Hide = eStateAttack | 17, + eStateAttackCamp_Camp = eStateAttack | 18, + eStateAttackCamp_StealOut = eStateAttack | 19, + + eStateAttack_Psy = eStateAttack | 20, + eStateAttack_MoveToHomePoint = eStateAttack | 21, + eStateAttack_HomePoint_Hide = eStateAttack | 22, + eStateAttack_HomePoint_Camp = eStateAttack | 23, + eStateAttack_HomePoint_LookOpenPlace = eStateAttack | 24, + + // ------------------------------------------------------------- + + eStatePanic = eGlobalState << 4, + + eStatePanic_Run = eStatePanic | 1, + eStatePanic_FaceUnprotectedArea = eStatePanic | 2, + eStatePanic_MoveToHomePoint = eStatePanic | 3, + + eStatePanic_HomePoint_Hide = eStatePanic | 4, + eStatePanic_HomePoint_LookOpenPlace = eStatePanic | 5, + eStatePanic_HomePoint_Camp = eStatePanic | 6, + + // ------------------------------------------------------------- + + eStateHitted = eGlobalState << 5, + + eStateHitted_Hide = eStateHitted | 1, + eStateHitted_MoveOut = eStateHitted | 2, + eStateHitted_Home = eStateHitted | 3, + + // ------------------------------------------------------------- + + eStateHearDangerousSound = eGlobalState << 6, + + eStateHearDangerousSound_Hide = eStateHearDangerousSound | 1, + eStateHearDangerousSound_FaceOpenPlace = eStateHearDangerousSound | 2, + eStateHearDangerousSound_StandScared = eStateHearDangerousSound | 3, + eStateHearDangerousSound_Home = eStateHearDangerousSound | 4, + + // ------------------------------------------------------------- + + eStateHearInterestingSound = eGlobalState << 7, + + eStateHearInterestingSound_MoveToDest = eStateHearInterestingSound | 1, + eStateHearInterestingSound_LookAround = eStateHearInterestingSound | 2, + + eStateHearHelpSound = eStateHearInterestingSound | 3, + eStateHearHelpSound_MoveToDest = eStateHearInterestingSound | 4, + eStateHearHelpSound_LookAround = eStateHearInterestingSound | 5, + + // ------------------------------------------------------------- + + eStateControlled = eGlobalState << 8, + + eStateControlled_Follow = eStateControlled | 1, + eStateControlled_Attack = eStateControlled | 2, + + eStateControlled_Follow_Wait = eStateControlled | 3, + eStateControlled_Follow_WalkToObject = eStateControlled | 4, + + + // ------------------------------------------------------------- + + eStateThreaten = eGlobalState << 9, + + // ------------------------------------------------------------- + + eStateFindEnemy = eGlobalState << 10, + + eStateFindEnemy_Run = eStateFindEnemy | 1, + eStateFindEnemy_LookAround = eStateFindEnemy | 2, + eStateFindEnemy_Angry = eStateFindEnemy | 3, + eStateFindEnemy_WalkAround = eStateFindEnemy | 4, + + eStateFindEnemy_LookAround_MoveToPoint = eStateFindEnemy | 5, + eStateFindEnemy_LookAround_LookAround = eStateFindEnemy | 6, + eStateFindEnemy_LookAround_TurnToPoint = eStateFindEnemy | 7, + + // ------------------------------------------------------------- + + eStateSquad = eGlobalState << 11, + + eStateSquad_Rest = eStateSquad | 1, + eStateSquad_RestFollow = eStateSquad | 2, + + eStateSquad_Rest_Idle = eStateSquad | 3, + eStateSquad_Rest_WalkAroundLeader = eStateSquad | 4, + + eStateSquad_RestFollow_Idle = eStateSquad | 5, + eStateSquad_RestFollow_WalkToPoint = eStateSquad | 6, + + // ------------------------------------------------------------- + + eStateCustom = eGlobalState << 15, + + eStateBurerScanning = eStateCustom | 1, + eStateCustomMoveToRestrictor = eStateCustom | 2, + + eStateSmartTerrainTask = eStateCustom | 3, + eStateSmartTerrainTaskGamePathWalk = eStateCustom | 4, + eStateSmartTerrainTaskLevelPathWalk = eStateCustom | 5, + eStateSmartTerrainTaskWaitCapture = eStateCustom | 6, + + eStateBurerAttack_Shield = eStateCustom | 7, + + eStateGhostBossScanning = eStateCustom | 9, + eStateGhostBossAttack_Shield = eStateCustom | 10, + + // ------------------------------------------------------------- + // custom attack states + + eStateCustom_Vampire = eStateCustom | eStateAttack | 1, + + eStateVampire_ApproachEnemy = eStateCustom | eStateAttack | 2, + eStateVampire_Execute = eStateCustom | eStateAttack | 3, + eStateVampire_RunAway = eStateCustom | eStateAttack | 4, + eStateVampire_Hide = eStateCustom | eStateAttack | 5, + + eStateBurerAttack_Tele = eStateCustom | eStateAttack | 6, + eStateBurerAttack_Gravi = eStateCustom | eStateAttack | 7, + eStateBurerAttack_RunAround = eStateCustom | eStateAttack | 8, + eStateBurerAttack_FaceEnemy = eStateCustom | eStateAttack | 9, + eStateBurerAttack_Melee = eStateCustom | eStateAttack | 10, + + eStatePredator_MoveToCover = eStateCustom | eStateAttack | 11, + eStatePredator_LookOpenPlace = eStateCustom | eStateAttack | 12, + eStatePredator_Camp = eStateCustom | eStateAttack | 13, + + eStatePredator = eStateCustom | eStateAttack | 14, + + eStateCustom_Tubeman = eStateCustom | eStateAttack | 15, + + eStateGhostBossAttack_Tele = eStateCustom | eStateAttack | 16, + eStateGhostBossAttack_Gravi = eStateCustom | eStateAttack | 17, + eStateGhostBossAttack_RunAround = eStateCustom | eStateAttack | 18, + eStateGhostBossAttack_FaceEnemy = eStateCustom | eStateAttack | 19, + eStateGhostBossAttack_Melee = eStateCustom | eStateAttack | 20, + eStateGhostBossAttack_Teleport = eStateCustom | eStateAttack | 21, + + eStateCustom_Choke = eStateCustom | eStateAttack | 22, + eStateChoke_Execute = eStateCustom | eStateAttack | 23, + + // ------------------------------------------------------------- + + + eStateUnknown = u32(-1), +}; + +#define is_state(state, type) (((state & type) == type) && (state != eStateUnknown)) + diff --git a/src/xrGameLA/ai/monsters/state_inline.h b/src/xrGameLA/ai/monsters/state_inline.h new file mode 100644 index 000000000..46073faaf --- /dev/null +++ b/src/xrGameLA/ai/monsters/state_inline.h @@ -0,0 +1,163 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateAbstract CState<_Object> + +TEMPLATE_SPECIALIZATION +CStateAbstract::CState(_Object *obj, void *data) +{ + reset (); + + object = obj; + _data = data; +} + +TEMPLATE_SPECIALIZATION +CStateAbstract::~CState() +{ + free_mem(); +} + +TEMPLATE_SPECIALIZATION +void CStateAbstract::reinit() +{ + if (current_substate != u32(-1)) get_state_current()->critical_finalize(); + + for (STATE_MAP_IT it = substates.begin(); it != substates.end(); it++) + it->second->reinit(); + + reset(); +} + + +TEMPLATE_SPECIALIZATION +void CStateAbstract::initialize() +{ + time_state_started = Device.dwTimeGlobal; + + current_substate = u32(-1); // means need reselect state + prev_substate = u32(-1); +} + +TEMPLATE_SPECIALIZATION +void CStateAbstract::execute() +{ + VERIFY(object->g_Alive()); + // проверить внешние условия изменения состояния + check_force_state(); + + // если состояние не выбрано, перевыбрать + if (current_substate == u32(-1)) { + reselect_state(); + VERIFY(current_substate != u32(-1)); + } + + // выполнить текущее состояние + CSState *state = get_state(current_substate); + state->execute(); + + // сохранить текущее состояние + prev_substate = current_substate; + + // проверить на завершение текущего состояния + if (state->check_completion()) { + state->finalize(); + current_substate = u32(-1); + } +} + +TEMPLATE_SPECIALIZATION +void CStateAbstract::finalize() +{ + reset(); +} + +TEMPLATE_SPECIALIZATION +void CStateAbstract::critical_finalize() +{ + if (current_substate != u32(-1)) get_state_current()->critical_finalize(); + reset(); +} + +TEMPLATE_SPECIALIZATION +void CStateAbstract::reset() +{ + current_substate = u32(-1); + prev_substate = u32(-1); + time_state_started = 0; +} + +TEMPLATE_SPECIALIZATION +void CStateAbstract::select_state(u32 new_state_id) +{ + if (current_substate == new_state_id) return; + CSState *state; + + // если предыдущее состояние активно, завершить его + if (current_substate != u32(-1)) { + state = get_state(current_substate); + state->critical_finalize(); + } + + // установить новое состояние + state = get_state(current_substate = new_state_id); + + // инициализировать новое состояние + setup_substates(); + + state->initialize(); +} + +TEMPLATE_SPECIALIZATION +CStateAbstract* CStateAbstract::get_state(u32 state_id) +{ + STATE_MAP_IT it = substates.find(state_id); + VERIFY(it != substates.end()); + + return it->second; +} + +TEMPLATE_SPECIALIZATION +void CStateAbstract::add_state(u32 state_id, CSState *s) +{ + substates.insert(mk_pair(state_id, s)); +} + +TEMPLATE_SPECIALIZATION +void CStateAbstract::free_mem() +{ + for (STATE_MAP_IT it = substates.begin(); it != substates.end(); it++) xr_delete(it->second); +} + +TEMPLATE_SPECIALIZATION +void CStateAbstract::fill_data_with (void *ptr_src, u32 size) +{ + VERIFY(ptr_src); + VERIFY(_data); + + CopyMemory(_data, ptr_src, size); +} + +TEMPLATE_SPECIALIZATION +CStateAbstract *CStateAbstract::get_state_current() +{ + if (substates.empty() || (current_substate == u32(-1))) return 0; + + STATE_MAP_IT it = substates.find(current_substate); + VERIFY(it != substates.end()); + + return it->second; +} +TEMPLATE_SPECIALIZATION +EMonsterState CStateAbstract::get_state_type() +{ + if (substates.empty() || (current_substate == u32(-1))) return eStateUnknown; + + EMonsterState state = get_state_current()->get_state_type(); + return ( (state == eStateUnknown) ? EMonsterState(current_substate) : state); +} + +#undef TEMPLATE_SPECIALIZATION \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/state_manager.h b/src/xrGameLA/ai/monsters/state_manager.h new file mode 100644 index 000000000..7a7907de8 --- /dev/null +++ b/src/xrGameLA/ai/monsters/state_manager.h @@ -0,0 +1,13 @@ +#pragma once +#include "state_defs.h" + +class IStateManagerBase { +public: + virtual ~IStateManagerBase () {}; + virtual void reinit () = 0; + virtual void update () = 0; + virtual void force_script_state (EMonsterState state) = 0; + virtual void execute_script_state () = 0; + virtual void critical_finalize () = 0; + virtual EMonsterState get_state_type () = 0; +}; diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack.h b/src/xrGameLA/ai/monsters/states/monster_state_attack.h new file mode 100644 index 000000000..00a03524d --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack.h @@ -0,0 +1,36 @@ +#pragma once + +#include "../state.h" +#include "../../../ai_debug.h" + +template +class CStateMonsterAttack : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + u32 m_time_next_run_away; + u32 m_time_start_check_behinder; + u32 m_time_start_behinder; + +public: + CStateMonsterAttack (_Object *obj, bool); + CStateMonsterAttack (_Object *obj); + CStateMonsterAttack (_Object *obj, state_ptr state_run, state_ptr state_melee); + virtual ~CStateMonsterAttack (); + + virtual void initialize (); + virtual void execute (); + virtual void setup_substates (); + +protected: + bool check_steal_state (); + bool check_find_enemy_state (); + bool check_run_away_state (); + bool check_run_attack_state (); + bool check_camp_state (); + bool check_home_point (); + bool check_behinder (); +}; + +#include "monster_state_attack_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_camp.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_camp.h new file mode 100644 index 000000000..d13fce72a --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_camp.h @@ -0,0 +1,27 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterAttackCamp : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object> *state_ptr; + + u32 m_target_node; +public: + CStateMonsterAttackCamp (_Object *obj); + + virtual void initialize (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); + + virtual void check_force_state (); + virtual void reselect_state (); + virtual void setup_substates (); + +}; + +#include "monster_state_attack_camp_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_camp_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_camp_inline.h new file mode 100644 index 000000000..bd550af23 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_camp_inline.h @@ -0,0 +1,170 @@ +#pragma once + +#include "state_move_to_point.h" +#include "state_look_point.h" +#include "monster_state_attack_camp_stealout.h" +#include "../../../cover_point.h" +#include "../monster_cover_manager.h" + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterAttackCampAbstract CStateMonsterAttackCamp<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterAttackCampAbstract::CStateMonsterAttackCamp(_Object *obj) : inherited(obj) +{ + add_state (eStateAttackCamp_Hide, new CStateMonsterMoveToPointEx<_Object>(obj)); + add_state (eStateAttackCamp_Camp, new CStateMonsterLookToPoint<_Object>(obj)); + add_state (eStateAttackCamp_StealOut, new CStateMonsterAttackCampStealOut<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackCampAbstract::initialize() +{ + inherited::initialize(); + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->lock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackCampAbstract::finalize() +{ + inherited::finalize(); + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackCampAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackCampAbstract::check_completion() +{ + if (current_substate == eStateAttackCamp_StealOut) { + return get_state_current()->check_completion(); + } + + if (current_substate == eStateAttackCamp_Camp) { + if (object->EnemyMan.see_enemy_now()) return true; + if (object->HitMemory.get_last_hit_time() > get_state_current()->time_started()) return true; + } + + if (object->EnemyMan.get_enemy()->Position().distance_to(object->Position()) < 5.f) return true; + + return false; +} + +#define MIN_DISTANCE_TO_ENEMY 20.f + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackCampAbstract::check_start_conditions() +{ + if (!object->ability_distant_feel()) return false; + + // check enemy + if (!object->EnemyMan.get_enemy()) return false; + + // check distance to enemy + if (object->Position().distance_to(object->EnemyMan.get_enemy_position()) < MIN_DISTANCE_TO_ENEMY) return false; + + // check if enemy see me + //if (EnemyMan.get_flags().is(FLAG_ENEMY_DOESNT_SEE_ME)) return false; + + // try to get cover + const CCoverPoint *point = object->CoverMan->find_cover(object->EnemyMan.get_enemy_position(), 10.f, 30.f); + if (!point) return false; + + m_target_node = point->level_vertex_id(); + + return true; +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackCampAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) { + select_state(eStateAttackCamp_Hide); + return; + } + + if (prev_substate == eStateAttackCamp_Hide) { + select_state(eStateAttackCamp_Camp); + return; + } + + if (prev_substate == eStateAttackCamp_Camp) { + if (get_state(eStateAttackCamp_StealOut)->check_start_conditions()) + select_state(eStateAttackCamp_StealOut); + else + select_state(eStateAttackCamp_Hide); + return; + } + + if (prev_substate == eStateAttackCamp_StealOut) { + select_state(eStateAttackCamp_Camp); + return; + } +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackCampAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateAttackCamp_Hide) { + SStateDataMoveToPointEx data; + + data.vertex = m_target_node; + data.point = ai().level_graph().vertex_position(data.vertex); + data.action.action = ACT_RUN; + data.action.time_out = 0; // do not use time out + data.completion_dist = 0.f; // get exactly to the point + data.time_to_rebuild = 0; // do not rebuild + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Aggressive; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + return; + } + + if (current_substate == eStateAttackCamp_Camp) { + + SStateDataLookToPoint data; + + Fvector dir; + object->CoverMan->less_cover_direction(dir); + + data.point.mad (object->Position(),dir,10.f); + data.action.action = ACT_STAND_IDLE; + data.action.time_out = 10000; // do not use time out + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.face_delay = 0; + + state->fill_data_with(&data, sizeof(SStateDataLookToPoint)); + return; + } +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackCampAbstract::check_force_state() +{ + +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterAttackCampAbstract +#undef MIN_DISTANCE_TO_ENEMY diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_camp_stealout.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_camp_stealout.h new file mode 100644 index 000000000..85e56d025 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_camp_stealout.h @@ -0,0 +1,17 @@ +#pragma once + + +template +class CStateMonsterAttackCampStealOut : public CStateMove<_Object> { + typedef CStateMove<_Object> inherited; + +public: + CStateMonsterAttackCampStealOut (_Object *obj); + + virtual void execute (); + virtual bool check_completion (); + virtual bool check_start_conditions (); + +}; + +#include "monster_state_attack_camp_stealout_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_camp_stealout_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_camp_stealout_inline.h new file mode 100644 index 000000000..c179570ec --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_camp_stealout_inline.h @@ -0,0 +1,57 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterAttackCampStealOutAbstract CStateMonsterAttackCampStealOut<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterAttackCampStealOutAbstract::CStateMonsterAttackCampStealOut(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackCampStealOutAbstract::execute() +{ + if (object->EnemyMan.get_my_vertex_enemy_last_seen() == u32(-1)) return; + + object->path().set_target_point (object->EnemyMan.get_my_vertex_enemy_last_seen()); + object->path().set_rebuild_time (0); + object->path().set_distance_to_end (0.f); + object->path().set_use_covers (false); + + object->set_action (ACT_STEAL); + object->anim().accel_deactivate (); + object->anim().accel_set_braking (false); + object->set_state_sound (MonsterSound::eMonsterSoundSteal); +} + +#define STATE_EXECUTE_TIME 8000 + + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackCampStealOutAbstract::check_completion() +{ + if (object->EnemyMan.get_my_vertex_enemy_last_seen() == u32(-1)) return true; + if (object->EnemyMan.see_enemy_now()) return true; + if (object->HitMemory.get_last_hit_time() > time_state_started) return true; + if (time_state_started + STATE_EXECUTE_TIME < time()) return true; + + Fvector pos = ai().level_graph().vertex_position(object->EnemyMan.get_my_vertex_enemy_last_seen()); + if ((object->Position().distance_to(pos) < 2.f) && object->control().path_builder().is_path_end(0.f)) return true; + + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackCampStealOutAbstract::check_start_conditions() +{ + if (object->EnemyMan.get_my_vertex_enemy_last_seen() == u32(-1)) return false; + if (object->EnemyMan.see_enemy_now()) return false; + return true; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterAttackCampStealOutAbstract + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_inline.h new file mode 100644 index 000000000..2c0b80e03 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_inline.h @@ -0,0 +1,290 @@ +#pragma once + +#include "monster_state_attack_melee.h" +#include "monster_state_attack_run.h" +#include "monster_state_attack_run_attack.h" +#include "state_hide_from_point.h" +#include "monster_state_find_enemy.h" +#include "monster_state_steal.h" +#include "monster_state_attack_camp.h" +#include "monster_state_home_point_attack.h" + +#include "../ai_monster_squad.h" +#include "../ai_monster_squad_manager.h" + +#include "../../../actor.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterAttackAbstract CStateMonsterAttack<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterAttackAbstract::CStateMonsterAttack(_Object *obj, bool) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterAttackAbstract::CStateMonsterAttack(_Object *obj) : inherited(obj) +{ + add_state (eStateAttack_Run, new CStateMonsterAttackRun<_Object>(obj)); + add_state (eStateAttack_Melee, new CStateMonsterAttackMelee<_Object>(obj)); + add_state (eStateAttack_RunAttack, new CStateMonsterAttackRunAttack<_Object>(obj)); + add_state (eStateAttack_RunAway, new CStateMonsterHideFromPoint<_Object>(obj)); + add_state (eStateAttack_FindEnemy, new CStateMonsterFindEnemy<_Object>(obj)); + add_state (eStateAttack_Steal, new CStateMonsterSteal<_Object>(obj)); + add_state (eStateAttackCamp, new CStateMonsterAttackCamp<_Object>(obj)); + add_state (eStateAttack_MoveToHomePoint, new CStateMonsterAttackMoveToHomePoint<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterAttackAbstract::CStateMonsterAttack(_Object *obj, state_ptr state_run, state_ptr state_melee) : inherited(obj) +{ + add_state (eStateAttack_Run, state_run); + add_state (eStateAttack_Melee, state_melee); + add_state (eStateAttack_RunAttack, new CStateMonsterAttackRunAttack<_Object>(obj)); + add_state (eStateAttack_RunAway, new CStateMonsterHideFromPoint<_Object>(obj)); + add_state (eStateAttack_FindEnemy, new CStateMonsterFindEnemy<_Object>(obj)); + add_state (eStateAttack_Steal, new CStateMonsterSteal<_Object>(obj)); + add_state (eStateAttackCamp, new CStateMonsterAttackCamp<_Object>(obj)); + add_state (eStateAttack_MoveToHomePoint, new CStateMonsterAttackMoveToHomePoint<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterAttackAbstract::~CStateMonsterAttack() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackAbstract::initialize() +{ + inherited::initialize (); + object->MeleeChecker.init_attack (); + + m_time_next_run_away = 0; + m_time_start_check_behinder = 0; + m_time_start_behinder = 0; +} + +#define FIND_ENEMY_DELAY 6000 + + + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackAbstract::execute() +{ + + bool selected = false; + + if (check_home_point()) { + select_state (eStateAttack_MoveToHomePoint); + selected = true; + }else if (check_steal_state()) { + select_state (eStateAttack_Steal); + selected = true; + } else if (check_camp_state()) { + select_state (eStateAttackCamp); + selected = true; + } else if (check_find_enemy_state()) { + select_state (eStateAttack_FindEnemy); + selected = true; + } else if (check_run_away_state()) { + select_state (eStateAttack_RunAway); + selected = true; + } else if (check_run_attack_state()) { + select_state (eStateAttack_RunAttack); + selected = true; + } + + if (!selected) { + // определить тип атаки + bool b_melee = false; + + if (prev_substate == eStateAttack_Melee) { + if (!get_state_current()->check_completion()) { + b_melee = true; + } + } else if (get_state(eStateAttack_Melee)->check_start_conditions()) { + b_melee = true; + } + + // установить целевое состояние + if (b_melee) { + // check if enemy is behind me for a long time + // [TODO] make specific state and replace run_away state (to avoid ratation jumps) + if (check_behinder()) + select_state(eStateAttack_RunAway); + else + select_state(eStateAttack_Melee); + } + else select_state(eStateAttack_Run); + } + + // clear behinder var if not melee state selected + if (current_substate != eStateAttack_Melee) m_time_start_check_behinder = 0; + + get_state_current()->execute(); + + prev_substate = current_substate; + + + // Notify squad + CMonsterSquad *squad = monster_squad().get_squad(object); + if (squad) { + SMemberGoal goal; + + goal.type = MG_AttackEnemy; + goal.entity = const_cast(object->EnemyMan.get_enemy()); + + squad->UpdateGoal (object, goal); + } + ////////////////////////////////////////////////////////////////////////// +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackAbstract::check_steal_state() +{ + if (prev_substate == u32(-1)) { + if (get_state(eStateAttack_Steal)->check_start_conditions()) return true; + } else if (prev_substate == eStateAttack_Steal) { + if (!get_state(eStateAttack_Steal)->check_completion()) return true; + } + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackAbstract::check_camp_state() +{ + if (prev_substate == u32(-1)) { + if (get_state(eStateAttackCamp)->check_start_conditions()) return true; + } else if (prev_substate == eStateAttackCamp) { + if (!get_state(eStateAttackCamp)->check_completion()) return true; + } + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackAbstract::check_find_enemy_state() +{ + // check state find enemy + if (object->EnemyMan.get_enemy_time_last_seen() + FIND_ENEMY_DELAY < Device.dwTimeGlobal) return true; + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackAbstract::check_run_away_state() +{ + if (m_time_start_behinder != 0) return false; + + if (prev_substate == eStateAttack_RunAway) { + if (!get_state(eStateAttack_RunAway)->check_completion()) return true; + else m_time_next_run_away = Device.dwTimeGlobal + 10000; + } else if ((object->EnemyMan.get_enemy() != Actor()) && object->Morale.is_despondent() && (m_time_next_run_away < Device.dwTimeGlobal)) { + return true; + } + + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackAbstract::check_run_attack_state() +{ + if (!object->ability_run_attack()) return false; + + if (prev_substate == eStateAttack_Run) { + if (get_state(eStateAttack_RunAttack)->check_start_conditions()) return true; + } else if (prev_substate == eStateAttack_RunAttack) { + if (!get_state(eStateAttack_RunAttack)->check_completion()) return true; + } + + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackAbstract::check_home_point() +{ + if (prev_substate != eStateAttack_MoveToHomePoint) { + if (get_state(eStateAttack_MoveToHomePoint)->check_start_conditions()) return true; + } else { + if (!get_state(eStateAttack_MoveToHomePoint)->check_completion()) return true; + } + + return false; +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateAttack_RunAway) { + + SStateHideFromPoint data; + data.point = object->EnemyMan.get_enemy_position(); + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Aggressive; + data.distance = 20.f; + data.action.action = ACT_RUN; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwAttackSndDelay; + data.action.time_out = 5000; + + state->fill_data_with(&data, sizeof(SStateHideFromPoint)); + + return; + } +} + +#define TIME_CHECK_BEHINDER 2000 +#define TIME_IN_BEHINDER 3000 +#define ANGLE_START_CHECK_BEHINDER 2 * PI_DIV_3 +#define ANGLE_CONTINUE_CHECK_BEHINDER PI_DIV_2 + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackAbstract::check_behinder() +{ + // if we are not in behinder state + if (m_time_start_behinder == 0) { + // check if we can start behinder + + + // - check if we start checking + if (m_time_start_check_behinder == 0) { + + // - check if object is behind + if (!object->control().direction().is_face_target(object->EnemyMan.get_enemy(), ANGLE_START_CHECK_BEHINDER)) { + m_time_start_check_behinder = time(); + } + + } else { + // if we already in check mode + + // - check if object is not behind (break checker) + if (object->control().direction().is_face_target(object->EnemyMan.get_enemy(), ANGLE_CONTINUE_CHECK_BEHINDER)) { + m_time_start_check_behinder = 0; + } + + // check if time is not out + if (m_time_start_check_behinder + TIME_CHECK_BEHINDER > time()) return false; + + m_time_start_behinder = time(); + m_time_start_check_behinder = 0; + } + } + + // if we are not in behinder state + + if (m_time_start_behinder != 0) { + if (m_time_start_behinder + TIME_IN_BEHINDER > time()) return true; + else m_time_start_behinder = 0; + } + + return false; +} + + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterAttackAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_melee.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_melee.h new file mode 100644 index 000000000..60114437e --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_melee.h @@ -0,0 +1,19 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterAttackMelee : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateMonsterAttackMelee (_Object *obj); + virtual ~CStateMonsterAttackMelee (); + + virtual void execute (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); +}; + +#include "monster_state_attack_melee_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_melee_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_melee_inline.h new file mode 100644 index 000000000..46a0df966 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_melee_inline.h @@ -0,0 +1,47 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterAttackMeleeAbstract CStateMonsterAttackMelee<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterAttackMeleeAbstract::CStateMonsterAttackMelee(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterAttackMeleeAbstract::~CStateMonsterAttackMelee() +{ +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackMeleeAbstract::execute() +{ + object->set_action (ACT_ATTACK); + if (object->control().direction().is_face_target(object->EnemyMan.get_enemy(), PI_DIV_3)) + object->dir().face_target (object->EnemyMan.get_enemy(), 800); + else + object->dir().face_target (object->EnemyMan.get_enemy(), 0, deg(15)); + + object->set_state_sound (MonsterSound::eMonsterSoundAggressive); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackMeleeAbstract::check_start_conditions() +{ + return ( + object->MeleeChecker.can_start_melee(object->EnemyMan.get_enemy()) && + object->EnemyMan.see_enemy_now() + ); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackMeleeAbstract::check_completion() +{ + return (object->MeleeChecker.should_stop_melee(object->EnemyMan.get_enemy())); +} + + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_run.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_run.h new file mode 100644 index 000000000..9b7395f8f --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_run.h @@ -0,0 +1,24 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterAttackRun : public CState<_Object> { + typedef CState<_Object> inherited; + + TTime m_time_path_rebuild; + +public: + IC CStateMonsterAttackRun (_Object *obj) : inherited(obj) {} + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); + +}; + +#include "monster_state_attack_run_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_run_attack.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_run_attack.h new file mode 100644 index 000000000..facf63554 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_run_attack.h @@ -0,0 +1,20 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterAttackRunAttack : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateMonsterAttackRunAttack (_Object *obj) : inherited(obj) {}; + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); +}; + +#include "monster_state_attack_run_attack_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_run_attack_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_run_attack_inline.h new file mode 100644 index 000000000..70fb28e9b --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_run_attack_inline.h @@ -0,0 +1,67 @@ +#pragma once +#include "../monster_velocity_space.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterAttackRunAttackAbstract CStateMonsterAttackRunAttack<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackRunAttackAbstract::initialize() +{ + inherited::initialize (); + + object->m_time_last_attack_success = 0; +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackRunAttackAbstract::execute() +{ + object->set_action (ACT_RUN); + object->set_state_sound (MonsterSound::eMonsterSoundAggressive); + object->anim().SetSpecParams (ASP_ATTACK_RUN); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackRunAttackAbstract::finalize() +{ + inherited::finalize (); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackRunAttackAbstract::critical_finalize() +{ + inherited::critical_finalize (); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackRunAttackAbstract::check_start_conditions() +{ + float dist = object->MeleeChecker.distance_to_enemy (object->EnemyMan.get_enemy()); + + if (dist > object->db().m_run_attack_start_dist) return false; + if (dist < object->MeleeChecker.get_min_distance()) return false; + + // check angle + if (!object->control().direction().is_face_target(object->EnemyMan.get_enemy(), deg(30))) return false; + + // try to build path + Fvector target_position; + target_position.mad(object->Position(), object->Direction(), object->db().m_run_attack_path_dist); + + //if (!object->control().path_builder().build_special(target_position, u32(-1), MonsterMovement::eVelocityParamsRunAttack)) return false; + //else object->path().enable_path(); + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackRunAttackAbstract::check_completion() +{ + if (!object->control().path_builder().is_moving_on_path() || (object->m_time_last_attack_success != 0)) return true; + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterAttackRunAttackAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_attack_run_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_attack_run_inline.h new file mode 100644 index 000000000..b17566a2b --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_attack_run_inline.h @@ -0,0 +1,87 @@ +#pragma once + +#include "../ai_monster_squad.h" +#include "../ai_monster_squad_manager.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterAttackRunAbstract CStateMonsterAttackRun<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackRunAbstract::initialize() +{ + inherited::initialize(); + object->path().prepare_builder (); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackRunAbstract::execute() +{ + // установка параметров функциональных блоков + object->set_action (ACT_RUN); + object->anim().accel_activate (eAT_Aggressive); + object->anim().accel_set_braking (false); + object->path().set_target_point (object->EnemyMan.get_enemy_position(), object->EnemyMan.get_enemy_vertex()); + object->path().set_rebuild_time (object->get_attack_rebuild_time()); + object->path().set_use_covers (); + object->path().set_cover_params (0.1f, 30.f, 1.f, 30.f); + object->path().set_try_min_time (false); + object->set_state_sound (MonsterSound::eMonsterSoundAggressive); + object->path().extrapolate_path (true); + + + // обработать squad инфо + object->path().set_use_dest_orient (false); + + CMonsterSquad *squad = monster_squad().get_squad(object); + if (squad && squad->SquadActive()) { + // Получить команду + SSquadCommand command; + squad->GetCommand(object, command); + + if (command.type == SC_ATTACK) { + object->path().set_use_dest_orient (true); + object->path().set_dest_direction (command.direction); + } + } +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackRunAbstract::finalize() +{ + inherited::finalize (); + object->path().extrapolate_path (false); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackRunAbstract::critical_finalize() +{ + inherited::critical_finalize (); + object->path().extrapolate_path (false); +} + + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackRunAbstract::check_completion() +{ + float m_fDistMin = object->MeleeChecker.get_min_distance (); + float dist = object->MeleeChecker.distance_to_enemy (object->EnemyMan.get_enemy()); + + if (dist < m_fDistMin) return true; + + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackRunAbstract::check_start_conditions() +{ + float m_fDistMax = object->MeleeChecker.get_max_distance (); + float dist = object->MeleeChecker.distance_to_enemy (object->EnemyMan.get_enemy()); + + if (dist > m_fDistMax) return true; + + return false; +} + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_controlled.h b/src/xrGameLA/ai/monsters/states/monster_state_controlled.h new file mode 100644 index 000000000..c35c66d03 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_controlled.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterControlled : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateMonsterControlled (_Object *obj); + virtual void execute (); +}; + +#include "monster_state_controlled_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_controlled_attack.h b/src/xrGameLA/ai/monsters/states/monster_state_controlled_attack.h new file mode 100644 index 000000000..de8d57060 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_controlled_attack.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../state.h" +#include "monster_state_attack.h" + +template +class CStateMonsterControlledAttack : public CStateMonsterAttack<_Object> { + typedef CStateMonsterAttack<_Object> inherited; + +public: + CStateMonsterControlledAttack (_Object *obj); + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + +private: + const CEntityAlive *get_enemy (); +}; + +#include "monster_state_controlled_attack_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_controlled_attack_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_controlled_attack_inline.h new file mode 100644 index 000000000..6373f60d2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_controlled_attack_inline.h @@ -0,0 +1,51 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterControlledAttackAbstract CStateMonsterControlledAttack<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterControlledAttackAbstract::CStateMonsterControlledAttack(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterControlledAttackAbstract::initialize() +{ + inherited::initialize(); + object->EnemyMan.force_enemy(get_enemy()); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterControlledAttackAbstract::execute() +{ + object->EnemyMan.force_enemy(get_enemy()); + inherited::execute(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterControlledAttackAbstract::finalize() +{ + inherited::finalize(); + object->EnemyMan.unforce_enemy(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterControlledAttackAbstract::critical_finalize() +{ + inherited::critical_finalize(); + object->EnemyMan.unforce_enemy(); +} + +TEMPLATE_SPECIALIZATION +const CEntityAlive *CStateMonsterControlledAttackAbstract::get_enemy() +{ + CControlledEntityBase *entity = smart_cast(object); + VERIFY(entity); + return smart_cast(entity->get_data().m_object); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterControlledAttackAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_controlled_follow.h b/src/xrGameLA/ai/monsters/states/monster_state_controlled_follow.h new file mode 100644 index 000000000..89241ce45 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_controlled_follow.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterControlledFollow : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + +public: + CStateMonsterControlledFollow (_Object *obj); + virtual void reselect_state (); + virtual void setup_substates (); +}; + +#include "monster_state_controlled_follow_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_controlled_follow_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_controlled_follow_inline.h new file mode 100644 index 000000000..13297c2ba --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_controlled_follow_inline.h @@ -0,0 +1,89 @@ +#pragma once + +#include "state_custom_action.h" +#include "state_move_to_point.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterControlledFollowAbstract CStateMonsterControlledFollow<_Object> + +#define STOP_DISTANCE 2.f +#define STAY_DISTANCE 5 * STOP_DISTANCE +#define MIN_TIME_OUT 4000 +#define MAX_TIME_OUT 6000 + + +TEMPLATE_SPECIALIZATION +CStateMonsterControlledFollowAbstract::CStateMonsterControlledFollow(_Object *obj) : inherited(obj) +{ + add_state (eStateControlled_Follow_Wait, new CStateMonsterCustomAction<_Object>(obj)); + add_state (eStateControlled_Follow_WalkToObject, new CStateMonsterMoveToPointEx<_Object>(obj)); +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterControlledFollowAbstract::reselect_state() +{ + CControlledEntityBase *entity = smart_cast(object); + VERIFY(entity); + const CEntity *target_object = entity->get_data().m_object; + + float dist = object->Position().distance_to(target_object->Position()); + select_state(dist < Random.randF(STOP_DISTANCE, STAY_DISTANCE) ? eStateControlled_Follow_Wait : eStateControlled_Follow_WalkToObject); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterControlledFollowAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateControlled_Follow_Wait) { + SStateDataAction data; + data.action = ACT_REST; + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + data.time_out = Random.randI(MIN_TIME_OUT, MAX_TIME_OUT); + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } + + if (current_substate == eStateControlled_Follow_WalkToObject) { + SStateDataMoveToPointEx data; + + CControlledEntityBase *entity = smart_cast(object); + VERIFY(entity); + const CEntity *target_object = entity->get_data().m_object; + + Fvector dest_pos = random_position(target_object->Position(), 10.f); + if (!object->control().path_builder().restrictions().accessible(dest_pos)) { + data.vertex = object->control().path_builder().restrictions().accessible_nearest(dest_pos, data.point); + } else { + data.point = dest_pos; + data.vertex = u32(-1); + } + + data.action.action = ACT_WALK_FWD; + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Calm; + data.completion_dist = STOP_DISTANCE; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.time_to_rebuild = u32(-1); + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + + return; + } +} + +#undef STOP_DISTANCE +#undef STAY_DISTANCE +#undef MIN_TIME_OUT +#undef MAX_TIME_OUT +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterControlledFollowAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_controlled_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_controlled_inline.h new file mode 100644 index 000000000..31994e052 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_controlled_inline.h @@ -0,0 +1,43 @@ +#pragma once + +#include "monster_state_controlled_attack.h" +#include "monster_state_controlled_follow.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterControlledAbstract CStateMonsterControlled<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterControlledAbstract::CStateMonsterControlled(_Object *obj) : inherited(obj) +{ + add_state (eStateControlled_Attack, new CStateMonsterControlledAttack<_Object>(obj)); + add_state (eStateControlled_Follow, new CStateMonsterControlledFollow<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterControlledAbstract::execute() +{ + switch (object->get_data().m_task) { + case eTaskFollow: select_state(eStateControlled_Follow); break; + case eTaskAttack: { + // проверить валидность данных атаки + const CEntity *enemy = object->get_data().m_object; + if (!enemy || enemy->getDestroy() || !enemy->g_Alive()) { + object->get_data().m_object = object->get_controller(); + select_state(eStateControlled_Follow); + } else + select_state(eStateControlled_Attack); break; + } + default: NODEFAULT; + } + + get_state_current()->execute(); + + prev_substate = current_substate; + +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterControlledAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_eat.h b/src/xrGameLA/ai/monsters/states/monster_state_eat.h new file mode 100644 index 000000000..1c231c07e --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_eat.h @@ -0,0 +1,35 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterEat : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + const CEntityAlive *corpse; + + u32 m_time_last_eat; + +public: + CStateMonsterEat (_Object *obj); + virtual ~CStateMonsterEat (); + + virtual void reinit (); + virtual void initialize (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual void reselect_state (); + virtual void setup_substates (); + virtual bool check_completion (); + virtual bool check_start_conditions (); + +private: + + bool hungry (); + +}; + +#include "monster_state_eat_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_eat_drag.h b/src/xrGameLA/ai/monsters/states/monster_state_eat_drag.h new file mode 100644 index 000000000..c3d6a2441 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_eat_drag.h @@ -0,0 +1,26 @@ +#pragma once + +template +class CStateMonsterDrag : public CState<_Object> { + typedef CState<_Object> inherited; + + Fvector m_cover_position; + u32 m_cover_vertex_id; + + bool m_failed; + Fvector m_corpse_start_position; + +public: + CStateMonsterDrag (_Object *obj); + virtual ~CStateMonsterDrag (); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_completion (); +}; + + +#include "monster_state_eat_drag_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/monster_state_eat_drag_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_eat_drag_inline.h new file mode 100644 index 000000000..1396cbf65 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_eat_drag_inline.h @@ -0,0 +1,113 @@ +#pragma once + +#include "../../../PHCharacter.h" +#include "../../../PHCapture.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterDragAbstract CStateMonsterDrag<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterDragAbstract::CStateMonsterDrag(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterDragAbstract::~CStateMonsterDrag() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterDragAbstract::initialize() +{ + inherited::initialize(); + + object->character_physics_support()->movement()->PHCaptureObject(const_cast(object->CorpseMan.get_corpse())); + + m_failed = false; + + CPHCapture *capture = object->character_physics_support()->movement()->PHCapture(); + if (capture && !capture->Failed()) { + + const CCoverPoint *point = object->CoverMan->find_cover(object->Position(), 10.f, 30.f); + if (point) { + m_cover_position = point->position(); + m_cover_vertex_id = point->level_vertex_id(); + } else { + m_cover_vertex_id = u32(-1); + } + } else m_failed = true; + + m_corpse_start_position = object->CorpseMan.get_corpse()->Position(); + + object->path().prepare_builder(); + +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterDragAbstract::execute() +{ + if (m_failed) return; + + // Установить параметры движения + object->set_action (ACT_DRAG); + object->anim().SetSpecParams (ASP_MOVE_BKWD); + + if (m_cover_vertex_id != u32(-1)) { + object->path().set_target_point (m_cover_position, m_cover_vertex_id); + } else { + object->path().set_retreat_from_point (object->CorpseMan.get_corpse()->Position()); + } + + object->path().set_generic_parameters (); + object->anim().accel_activate (eAT_Calm); + +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterDragAbstract::finalize() +{ + inherited::finalize(); + + // бросить труп + if (object->character_physics_support()->movement()->PHCapture()) + object->character_physics_support()->movement()->PHReleaseObject(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterDragAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + // бросить труп + if (object->character_physics_support()->movement()->PHCapture()) + object->character_physics_support()->movement()->PHReleaseObject(); + +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterDragAbstract::check_completion() +{ + if (m_failed) { + return true; + } + + if (!object->character_physics_support()->movement()->PHCapture()) { + return true; + } + + if (m_cover_vertex_id != u32(-1)) { // valid vertex so wait path end + if (object->Position().distance_to(m_cover_position) < 2.f) + return true; + } else { // invalid vertex so check distanced that passed + if (m_corpse_start_position.distance_to(object->Position()) > 20.f) + return true; + } + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterDragAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_eat_eat.h b/src/xrGameLA/ai/monsters/states/monster_state_eat_eat.h new file mode 100644 index 000000000..ba7bedf98 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_eat_eat.h @@ -0,0 +1,23 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterEating : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + + CEntityAlive *corpse; + u32 time_last_eat; + +public: + CStateMonsterEating (_Object *obj); + virtual ~CStateMonsterEating (); + + virtual void initialize (); + virtual void execute (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "monster_state_eat_eat_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_eat_eat_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_eat_eat_inline.h new file mode 100644 index 000000000..074f1f67e --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_eat_eat_inline.h @@ -0,0 +1,81 @@ +#pragma once +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterEatingAbstract CStateMonsterEating<_Object> + +#define TIME_TO_EAT 20000 + +TEMPLATE_SPECIALIZATION +CStateMonsterEatingAbstract::CStateMonsterEating(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterEatingAbstract::~CStateMonsterEating() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterEatingAbstract::initialize() +{ + inherited::initialize(); + time_last_eat = 0; +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterEatingAbstract::execute() +{ + if (object->CorpseMan.get_corpse() != corpse) return; + + object->set_action (ACT_EAT); + object->set_state_sound (MonsterSound::eMonsterSoundEat); + + // съесть часть + if (time_last_eat + u32(1000/object->db().m_fEatFreq) < Device.dwTimeGlobal) { + object->ChangeSatiety(object->db().m_fEatSlice); + corpse->m_fFood -= object->db().m_fEatSliceWeight; + time_last_eat = Device.dwTimeGlobal; + } +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterEatingAbstract::check_start_conditions() +{ + corpse = const_cast(object->CorpseMan.get_corpse()); + VERIFY (corpse); + + Fvector nearest_bone_pos; + if ((corpse->m_pPhysicsShell == NULL) || (!corpse->m_pPhysicsShell->isActive())) { + nearest_bone_pos = corpse->Position(); + } else nearest_bone_pos = object->character_physics_support()->movement()->PHCaptureGetNearestElemPos(corpse); + + float dist = nearest_bone_pos.distance_to(object->Position()); + float dist_to_corpse = object->db().m_fDistToCorpse; + + if (dist + 0.5f < dist_to_corpse) return true; + return false; +} + + +TEMPLATE_SPECIALIZATION +bool CStateMonsterEatingAbstract::check_completion() +{ + if (time_state_started + TIME_TO_EAT < time()) return true; + if (object->CorpseMan.get_corpse() != corpse) return true; + + Fvector nearest_bone_pos; + if ((corpse->m_pPhysicsShell == NULL) || (!corpse->m_pPhysicsShell->isActive())) { + nearest_bone_pos = corpse->Position(); + } else nearest_bone_pos = object->character_physics_support()->movement()->PHCaptureGetNearestElemPos(corpse); + + float dist = nearest_bone_pos.distance_to(object->Position()); + float dist_to_corpse = object->db().m_fDistToCorpse; + if (dist > dist_to_corpse+0.5f) return true; + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterEatingAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_eat_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_eat_inline.h new file mode 100644 index 000000000..0b3a51bef --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_eat_inline.h @@ -0,0 +1,258 @@ +#pragma once + +#include "state_data.h" +#include "state_move_to_point.h" +#include "state_hide_from_point.h" +#include "state_custom_action.h" +#include "monster_state_eat_eat.h" +#include "monster_state_eat_drag.h" +#include "../../../PhysicsShell.h" +#include "../../../PHMovementControl.h" +#include "../../../CharacterPhysicsSupport.h" +#ifdef _DEBUG +# include "../../../level.h" +# include "../../../level_debug.h" +#endif + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterEatAbstract CStateMonsterEat<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterEatAbstract::CStateMonsterEat(_Object *obj) : inherited(obj) +{ + add_state (eStateEat_CorpseApproachRun, new CStateMonsterMoveToPoint<_Object>(obj)); + add_state (eStateEat_CorpseApproachWalk, new CStateMonsterMoveToPoint<_Object>(obj)); + add_state (eStateEat_CheckCorpse, new CStateMonsterCustomAction<_Object>(obj)); + add_state (eStateEat_Eat, new CStateMonsterEating<_Object>(obj)); + add_state (eStateEat_WalkAway, new CStateMonsterHideFromPoint<_Object>(obj)); + add_state (eStateEat_Rest, new CStateMonsterCustomAction<_Object>(obj)); + add_state (eStateEat_Drag, new CStateMonsterDrag<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterEatAbstract::~CStateMonsterEat() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterEatAbstract::reinit() +{ + inherited::reinit(); + + m_time_last_eat = 0; +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterEatAbstract::initialize() +{ + inherited::initialize(); + corpse = object->CorpseMan.get_corpse(); + + monster_squad().get_squad(object)->lock_corpse(object->CorpseMan.get_corpse()); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterEatAbstract::finalize() +{ + inherited::finalize(); + + monster_squad().get_squad(object)->unlock_corpse(object->CorpseMan.get_corpse()); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterEatAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + monster_squad().get_squad(object)->unlock_corpse(object->CorpseMan.get_corpse()); +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterEatAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) {select_state(eStateEat_CorpseApproachRun);return;} + if (prev_substate == eStateEat_CorpseApproachRun) { select_state(eStateEat_CheckCorpse); return; } + + if (prev_substate == eStateEat_CheckCorpse) { + if (object->ability_can_drag()) select_state(eStateEat_Drag); + else { + if (get_state(eStateEat_Eat)->check_start_conditions()) + select_state(eStateEat_Eat); + else + select_state(eStateEat_CorpseApproachWalk); + } + return; + } + + if (prev_substate == eStateEat_Drag) { + if (get_state(eStateEat_Eat)->check_start_conditions()) + select_state(eStateEat_Eat); + else + select_state(eStateEat_CorpseApproachWalk); + return; + } + + if (prev_substate == eStateEat_Eat){ + m_time_last_eat = time(); + + if (!hungry()) + select_state(eStateEat_WalkAway); + else + select_state(eStateEat_CorpseApproachWalk); + return; + } + + if (prev_substate == eStateEat_CorpseApproachWalk){ + if (get_state(eStateEat_Eat)->check_start_conditions()) + select_state(eStateEat_Eat); + else + select_state(eStateEat_CorpseApproachWalk); + return; + } + + if (prev_substate == eStateEat_WalkAway) { select_state(eStateEat_Rest); return; } + if (prev_substate == eStateEat_Rest) { select_state(eStateEat_Rest); return; } +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterEatAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateEat_CorpseApproachRun) { + + // Определить позицию ближайшей боны у трупа + Fvector nearest_bone_pos; + const CEntityAlive *corpse = object->CorpseMan.get_corpse(); + if ((corpse->m_pPhysicsShell == NULL) || (!corpse->m_pPhysicsShell->isActive())) { + nearest_bone_pos = corpse->Position(); + } else nearest_bone_pos = object->character_physics_support()->movement()->PHCaptureGetNearestElemPos(corpse); + +#ifdef _DEBUG + DBG().level_info(this).clear (); + Fvector pos1; + pos1.set(nearest_bone_pos); + pos1.y+=20.f; + + DBG().level_info(this).add_item (nearest_bone_pos, pos1, COLOR_GREEN); +#endif + SStateDataMoveToPoint data; + data.point = nearest_bone_pos; + data.vertex = u32(-1); + data.action.action = ACT_RUN; + data.accelerated = true; + data.braking = true; + data.accel_type = eAT_Calm; + data.completion_dist= object->db().m_fDistToCorpse; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPoint)); + return; + } + + if (current_substate == eStateEat_CheckCorpse) { + SStateDataAction data; + data.action = ACT_STAND_IDLE; + data.spec_params = 0; + data.time_out = 1500; + data.sound_type = MonsterSound::eMonsterSoundEat; + data.sound_delay = object->db().m_dwEatSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } + + if (current_substate == eStateEat_WalkAway) { + SStateHideFromPoint data; + + data.point = object->CorpseMan.get_corpse_position(); + data.action.action = ACT_WALK_FWD; + data.distance = 15.f; + data.accelerated = true; + data.braking = true; + data.accel_type = eAT_Calm; + data.cover_min_dist = 20.f; + data.cover_max_dist = 30.f; + data.cover_search_radius = 25.f; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateHideFromPoint)); + + return; + } + + if (current_substate == eStateEat_Rest) { + SStateDataAction data; + data.action = ACT_REST; + data.spec_params = 0; + data.time_out = 8500; + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + return; + } + + if (current_substate == eStateEat_CorpseApproachWalk) { + + // Определить позицию ближайшей боны у трупа + Fvector nearest_bone_pos; + const CEntityAlive *corpse = object->CorpseMan.get_corpse(); + if ((corpse->m_pPhysicsShell == NULL) || (!corpse->m_pPhysicsShell->isActive())) { + nearest_bone_pos = corpse->Position(); + } else nearest_bone_pos = object->character_physics_support()->movement()->PHCaptureGetNearestElemPos(corpse); + + SStateDataMoveToPoint data; + data.point = nearest_bone_pos; + data.vertex = u32(-1); + data.action.action = ACT_WALK_FWD; + data.accelerated = true; + data.braking = true; + data.accel_type = eAT_Calm; + data.completion_dist= object->db().m_fDistToCorpse; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPoint)); + return; + } +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterEatAbstract::check_completion() +{ + if (corpse != object->CorpseMan.get_corpse()) return true; + if (!hungry()) return true; + + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterEatAbstract::check_start_conditions() +{ + return ( + object->CorpseMan.get_corpse() && + object->Home->at_home(object->CorpseMan.get_corpse()->Position()) && + hungry() && + !monster_squad().get_squad(object)->is_locked_corpse(object->CorpseMan.get_corpse()) + ); + +} + +#define TIME_NOT_HUNGRY 20000 + +TEMPLATE_SPECIALIZATION +bool CStateMonsterEatAbstract::hungry() +{ + return ((m_time_last_eat == 0) || (m_time_last_eat + TIME_NOT_HUNGRY < time())); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterEatAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_find_enemy.h b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy.h new file mode 100644 index 000000000..cadc32f68 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../state.h" + + +template +class CStateMonsterFindEnemy : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + +public: + CStateMonsterFindEnemy (_Object *obj); + virtual ~CStateMonsterFindEnemy (); + + virtual void reselect_state (); +}; + +#include "monster_state_find_enemy_inline.h" + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_angry.h b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_angry.h new file mode 100644 index 000000000..8f82e7579 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_angry.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterFindEnemyAngry : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateMonsterFindEnemyAngry (_Object *obj); + virtual ~CStateMonsterFindEnemyAngry(); + + virtual void execute (); + virtual bool check_completion (); +}; + +#include "monster_state_find_enemy_angry_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_angry_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_angry_inline.h new file mode 100644 index 000000000..eee6843f7 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_angry_inline.h @@ -0,0 +1,33 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterFindEnemyAngryAbstract CStateMonsterFindEnemyAngry<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterFindEnemyAngryAbstract::CStateMonsterFindEnemyAngry(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterFindEnemyAngryAbstract::~CStateMonsterFindEnemyAngry() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterFindEnemyAngryAbstract::execute() +{ + object->set_action (ACT_STAND_IDLE); + object->anim().SetSpecParams (ASP_THREATEN); + object->set_state_sound (MonsterSound::eMonsterSoundAggressive); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterFindEnemyAngryAbstract::check_completion() +{ + if (time_state_started + 4000 > Device.dwTimeGlobal) return false; + return true; +} + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_inline.h new file mode 100644 index 000000000..b365d97d6 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_inline.h @@ -0,0 +1,43 @@ +#pragma once + +#include "monster_state_find_enemy_run.h" +#include "monster_state_find_enemy_angry.h" +#include "monster_state_find_enemy_walk.h" +#include "monster_state_find_enemy_look.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterFindEnemyAbstract CStateMonsterFindEnemy<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterFindEnemyAbstract::CStateMonsterFindEnemy(_Object *obj) : inherited(obj) +{ + add_state (eStateFindEnemy_Run, new CStateMonsterFindEnemyRun<_Object>(obj)); + add_state (eStateFindEnemy_LookAround, new CStateMonsterFindEnemyLook<_Object>(obj)); + add_state (eStateFindEnemy_Angry, new CStateMonsterFindEnemyAngry<_Object>(obj)); + add_state (eStateFindEnemy_WalkAround, new CStateMonsterFindEnemyWalkAround<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterFindEnemyAbstract::~CStateMonsterFindEnemy() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterFindEnemyAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) { + select_state(eStateFindEnemy_Run); + return; + } + + switch (prev_substate) { + case eStateFindEnemy_Run: select_state(eStateFindEnemy_LookAround); break; + case eStateFindEnemy_LookAround: select_state(eStateFindEnemy_Angry); break; + case eStateFindEnemy_Angry: select_state(eStateFindEnemy_WalkAround); break; + case eStateFindEnemy_WalkAround: select_state(eStateFindEnemy_WalkAround); break; + } +} + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_look.h b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_look.h new file mode 100644 index 000000000..66e902f3c --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_look.h @@ -0,0 +1,34 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterFindEnemyLook : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + bool look_right_side; + u8 current_stage; + Fvector target_point; + + Fvector current_dir; + Fvector start_position; + + enum { + eMoveToPoint = u32(0), + eLookAround, + eTurnToPoint + } ; + +public: + CStateMonsterFindEnemyLook (_Object *obj); + virtual ~CStateMonsterFindEnemyLook (); + + virtual void initialize (); + virtual void reselect_state (); + virtual bool check_completion (); + + virtual void setup_substates (); +}; + +#include "monster_state_find_enemy_look_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_look_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_look_inline.h new file mode 100644 index 000000000..4f8f3d50f --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_look_inline.h @@ -0,0 +1,114 @@ +#pragma once + +#include "state_move_to_point.h" +#include "state_look_point.h" +#include "state_custom_action.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterFindEnemyLookAbstract CStateMonsterFindEnemyLook<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterFindEnemyLookAbstract::CStateMonsterFindEnemyLook(_Object *obj) : inherited(obj) +{ + add_state (eStateFindEnemy_LookAround_MoveToPoint, new CStateMonsterMoveToPoint<_Object>(obj)); + add_state (eStateFindEnemy_LookAround_LookAround, new CStateMonsterCustomAction<_Object>(obj)); + add_state (eStateFindEnemy_LookAround_TurnToPoint, new CStateMonsterLookToPoint<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterFindEnemyLookAbstract::~CStateMonsterFindEnemyLook() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterFindEnemyLookAbstract::initialize() +{ + inherited::initialize (); + + look_right_side = ((Random.randI(2)) ? true : false); + current_stage = 0; + target_point = Fvector().set(0.f,0.f,0.f); + + current_dir = object->Direction(); + start_position = object->Position(); +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterFindEnemyLookAbstract::reselect_state() +{ + if ((current_stage == 1) || (current_stage == 3)) { + float h,p; + current_dir.getHP(h,p); + + h += ((look_right_side) ? (-deg(120)) : deg(120)); + current_dir.setHP(h,p); + current_dir.normalize(); + target_point.mad(start_position, current_dir, Random.randF(4.f,5.f)); + select_state((Random.randI(2)) ? eStateFindEnemy_LookAround_MoveToPoint : eStateFindEnemy_LookAround_TurnToPoint); + + } else select_state(eStateFindEnemy_LookAround_LookAround); + + current_stage++; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterFindEnemyLookAbstract::check_completion() +{ + if (current_stage < 5) return false; + return true; +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterFindEnemyLookAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateFindEnemy_LookAround_MoveToPoint) { + + SStateDataMoveToPoint data; + data.point = target_point; + data.vertex = u32(-1); + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Aggressive; + data.action.action = ACT_RUN; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwAttackSndDelay; + + + state->fill_data_with(&data, sizeof(SStateDataMoveToPoint)); + + return; + } + + if (current_substate == eStateFindEnemy_LookAround_LookAround) { + SStateDataAction data; + + data.action = ACT_LOOK_AROUND; + data.time_out = 2000; + data.sound_type = MonsterSound::eMonsterSoundAggressive; + data.sound_delay = object->db().m_dwAttackSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } + + if (current_substate == eStateFindEnemy_LookAround_TurnToPoint) { + SStateDataLookToPoint data; + + data.point = target_point; + data.action.action = ACT_STAND_IDLE; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwAttackSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataLookToPoint)); + + return; + } +} + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_run.h b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_run.h new file mode 100644 index 000000000..441e0d91a --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_run.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterFindEnemyRun : public CState<_Object> { + typedef CState<_Object> inherited; + + Fvector target_point; + u32 target_vertex; + +public: + CStateMonsterFindEnemyRun (_Object *obj); + virtual ~CStateMonsterFindEnemyRun (); + + virtual void initialize (); + virtual void execute (); + virtual bool check_completion (); +}; + +#include "monster_state_find_enemy_run_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_run_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_run_inline.h new file mode 100644 index 000000000..0a653d23c --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_run_inline.h @@ -0,0 +1,69 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterFindEnemyRunAbstract CStateMonsterFindEnemyRun<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterFindEnemyRunAbstract::CStateMonsterFindEnemyRun(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterFindEnemyRunAbstract::~CStateMonsterFindEnemyRun() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterFindEnemyRunAbstract::initialize() +{ + inherited::initialize (); + + object->path().prepare_builder (); + + + target_point = object->EnemyMan.get_enemy_position(); + target_vertex = object->EnemyMan.get_enemy_vertex(); + + Fvector dir; + dir.sub(target_point, object->Position()); + dir.normalize(); + + Fvector test_position; + test_position.mad(target_point, dir, 10.f); + + // провериь возможность пробежать дальше + if (ai().level_graph().valid_vertex_position(test_position)) { + u32 vertex_id = ai().level_graph().vertex_id(test_position); + if (ai().level_graph().valid_vertex_id(vertex_id)) { + target_point = test_position; + target_vertex = vertex_id; + } + } +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterFindEnemyRunAbstract::execute() +{ + object->set_action (ACT_RUN); + object->anim().accel_activate (eAT_Aggressive); + object->anim().accel_set_braking (false); + object->path().set_target_point (target_point, target_vertex); + object->path().set_rebuild_time (0); + object->path().set_use_covers (); + object->path().set_cover_params (5.f, 30.f, 1.f, 30.f); + object->path().set_try_min_time (false); + object->set_state_sound (MonsterSound::eMonsterSoundAggressive); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterFindEnemyRunAbstract::check_completion() +{ + if ((object->ai_location().level_vertex_id() == target_vertex) && + !object->control().path_builder().is_moving_on_path()) return true; + + return false; +} + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_walk.h b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_walk.h new file mode 100644 index 000000000..c418e01fc --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_walk.h @@ -0,0 +1,15 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterFindEnemyWalkAround : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateMonsterFindEnemyWalkAround (_Object *obj) : inherited(obj) {} + virtual void execute (); + virtual bool check_completion () {return false;} +}; + +#include "monster_state_find_enemy_walk_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_walk_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_walk_inline.h new file mode 100644 index 000000000..29f9ce06d --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_find_enemy_walk_inline.h @@ -0,0 +1,15 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterFindEnemyWalkAbstract CStateMonsterFindEnemyWalkAround<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterFindEnemyWalkAbstract::execute() +{ + object->set_action (ACT_STAND_IDLE); + object->set_state_sound (MonsterSound::eMonsterSoundAggressive); +} + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_hear_danger_sound.h b/src/xrGameLA/ai/monsters/states/monster_state_hear_danger_sound.h new file mode 100644 index 000000000..59a6bacbd --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_hear_danger_sound.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterHearDangerousSound : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + +public: + CStateMonsterHearDangerousSound (_Object *obj); + virtual ~CStateMonsterHearDangerousSound () {} + + virtual void reselect_state (); + virtual void setup_substates (); + +}; + +#include "monster_state_hear_danger_sound_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/monster_state_hear_danger_sound_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_hear_danger_sound_inline.h new file mode 100644 index 000000000..d576f4cd4 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_hear_danger_sound_inline.h @@ -0,0 +1,99 @@ +#pragma once + +#include "state_hide_from_point.h" +#include "state_look_unprotected_area.h" +#include "state_custom_action.h" +#include "monster_state_home_point_danger.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterHearDangerousSoundAbstract CStateMonsterHearDangerousSound<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterHearDangerousSoundAbstract::CStateMonsterHearDangerousSound(_Object *obj) : inherited(obj) +{ + add_state (eStateHearDangerousSound_Hide, new CStateMonsterHideFromPoint<_Object>(obj)); + add_state (eStateHearDangerousSound_FaceOpenPlace, new CStateMonsterLookToUnprotectedArea<_Object>(obj)); + add_state (eStateHearDangerousSound_StandScared, new CStateMonsterCustomAction<_Object>(obj)); + add_state (eStateHearDangerousSound_Home, new CStateMonsterDangerMoveToHomePoint<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHearDangerousSoundAbstract::reselect_state() +{ + if (get_state(eStateHearDangerousSound_Home)->check_start_conditions()) { + select_state(eStateHearDangerousSound_Home); + return; + } + + if (prev_substate == u32(-1)){ + select_state(eStateHearDangerousSound_Hide); + return; + } + + if (prev_substate == eStateHearDangerousSound_Hide) { + select_state(eStateHearDangerousSound_FaceOpenPlace); + return; + } + + select_state(eStateHearDangerousSound_StandScared); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHearDangerousSoundAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateHearDangerousSound_Hide) { + SStateHideFromPoint data; + + Fvector run_away_point; + Fvector dir; + dir.sub (object->Position(), object->SoundMemory.GetSound().position); + dir.normalize_safe(); + run_away_point.mad(object->Position(), dir, 1.f); + + data.point = run_away_point; + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Aggressive; + data.distance = 40.f; + data.action.action = ACT_RUN; + data.action.sound_type = MonsterSound::eMonsterSoundPanic; + data.action.sound_delay = object->db().m_dwAttackSndDelay; + + state->fill_data_with(&data, sizeof(SStateHideFromPoint)); + + return; + } + + if (current_substate == eStateHearDangerousSound_FaceOpenPlace) { + SStateDataAction data; + data.action = ACT_STAND_IDLE; + data.spec_params = ASP_STAND_SCARED; + data.time_out = 2000; + data.sound_type = MonsterSound::eMonsterSoundPanic; + data.sound_delay = object->db().m_dwAttackSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } + + if (current_substate == eStateHearDangerousSound_StandScared) { + SStateDataAction data; + data.action = ACT_STAND_IDLE; + data.spec_params = ASP_STAND_SCARED; + data.sound_type = MonsterSound::eMonsterSoundPanic; + data.sound_delay = object->db().m_dwAttackSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterHearDangerousSoundAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_hear_int_sound.h b/src/xrGameLA/ai/monsters/states/monster_state_hear_int_sound.h new file mode 100644 index 000000000..8ca121215 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_hear_int_sound.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterHearInterestingSound : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + +public: + CStateMonsterHearInterestingSound (_Object *obj); + virtual ~CStateMonsterHearInterestingSound (){} + + virtual void reselect_state (); + virtual void setup_substates (); + +private: + Fvector get_target_position (); + +}; + +#include "monster_state_hear_int_sound_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/monster_state_hear_int_sound_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_hear_int_sound_inline.h new file mode 100644 index 000000000..2a1f87148 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_hear_int_sound_inline.h @@ -0,0 +1,81 @@ +#pragma once + +#include "state_custom_action_look.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterHearInterestingSoundAbstract CStateMonsterHearInterestingSound<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterHearInterestingSoundAbstract::CStateMonsterHearInterestingSound(_Object *obj) : inherited(obj) +{ + add_state (eStateHearInterestingSound_MoveToDest, new CStateMonsterMoveToPoint<_Object>(obj)); + add_state (eStateHearInterestingSound_LookAround, new CStateMonsterCustomActionLook<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHearInterestingSoundAbstract::reselect_state() +{ + if (prev_substate == u32(-1)){ + if (get_state(eStateHearInterestingSound_MoveToDest)->check_start_conditions()) + select_state(eStateHearInterestingSound_MoveToDest); + else + select_state(eStateHearInterestingSound_LookAround); + return; + } + + select_state(eStateHearInterestingSound_LookAround); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHearInterestingSoundAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateHearInterestingSound_MoveToDest) { + SStateDataMoveToPoint data; + data.point = get_target_position(); + data.vertex = u32(-1); + data.action.action = ACT_WALK_FWD; + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Calm; + data.completion_dist= 2.f; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + + state->fill_data_with(&data, sizeof(SStateDataMoveToPoint)); + + return; + } + + if (current_substate == eStateHearInterestingSound_LookAround) { + SStateDataActionLook data; + data.action = ACT_LOOK_AROUND; + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + + Fvector dir; + object->CoverMan->less_cover_direction(dir); + data.point.mad (object->Position(),dir,10.f); + + state->fill_data_with(&data, sizeof(SStateDataActionLook)); + + return; + } +} + +TEMPLATE_SPECIALIZATION +Fvector CStateMonsterHearInterestingSoundAbstract::get_target_position() +{ + Fvector snd_pos = object->SoundMemory.GetSound().position; + if (!object->Home->has_home() || object->Home->at_home(snd_pos)) return snd_pos; + + return ai().level_graph().vertex_position(object->Home->get_place()); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterHearInterestingSoundAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_help_sound.h b/src/xrGameLA/ai/monsters/states/monster_state_help_sound.h new file mode 100644 index 000000000..4290e55bc --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_help_sound.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterHearHelpSound : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + +public: + CStateMonsterHearHelpSound (_Object *obj); + virtual ~CStateMonsterHearHelpSound (){} + + virtual void reselect_state (); + virtual void setup_substates (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); + +}; + +#include "monster_state_help_sound_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/monster_state_help_sound_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_help_sound_inline.h new file mode 100644 index 000000000..f75673b61 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_help_sound_inline.h @@ -0,0 +1,84 @@ +#pragma once + +#include "state_move_to_point.h" +#include "state_custom_action_look.h" + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterHearHelpSoundAbstract CStateMonsterHearHelpSound<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterHearHelpSoundAbstract::CStateMonsterHearHelpSound(_Object *obj) : inherited(obj) +{ + add_state (eStateHearHelpSound_MoveToDest, new CStateMonsterMoveToPointEx<_Object>(obj)); + add_state (eStateHearHelpSound_LookAround, new CStateMonsterCustomActionLook<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterHearHelpSoundAbstract::check_start_conditions() +{ + if (!object->SoundMemory.hear_help_sound()) return false; + if (object->Home->has_home()) return object->Home->at_home(ai().level_graph().vertex_position(object->SoundMemory.hear_help_sound_node())); + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterHearHelpSoundAbstract::check_completion() +{ + if (current_substate == u32(-1)) return true; + return false; +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHearHelpSoundAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) + select_state(eStateHearHelpSound_MoveToDest); + else if (prev_substate == eStateHearHelpSound_MoveToDest) + select_state(eStateHearHelpSound_LookAround); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHearHelpSoundAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateHearHelpSound_MoveToDest) { + SStateDataMoveToPointEx data; + + data.vertex = object->SoundMemory.hear_help_sound_node(); + data.point = ai().level_graph().vertex_position(data.vertex); + data.action.action = ACT_RUN; + data.action.time_out = 0; // do not use time out + data.completion_dist = 0.f; // get exactly to the point + data.time_to_rebuild = 0; // do not rebuild + data.accelerated = true; + data.braking = true; + data.accel_type = eAT_Aggressive; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + + return; + } + + if (current_substate == eStateHearHelpSound_LookAround) { + SStateDataAction data; + data.action = ACT_LOOK_AROUND; + data.time_out = 3000; + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterHearHelpSoundAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_hitted.h b/src/xrGameLA/ai/monsters/states/monster_state_hitted.h new file mode 100644 index 000000000..65815cd80 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_hitted.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterHitted : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + +public: + CStateMonsterHitted (_Object *obj); + virtual ~CStateMonsterHitted (); + + virtual void reselect_state (); +}; + +#include "monster_state_hitted_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_hitted_hide.h b/src/xrGameLA/ai/monsters/states/monster_state_hitted_hide.h new file mode 100644 index 000000000..b59a33efa --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_hitted_hide.h @@ -0,0 +1,21 @@ +#pragma once + +template +class CStateMonsterHittedHide : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + +public: + + CStateMonsterHittedHide (_Object *obj) : inherited(obj) {} + virtual ~CStateMonsterHittedHide() {} + + virtual void initialize (); + virtual void execute (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); +}; + +#include "monster_state_hitted_hide_inline.h" + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_hitted_hide_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_hitted_hide_inline.h new file mode 100644 index 000000000..7e67720ff --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_hitted_hide_inline.h @@ -0,0 +1,55 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStateMonsterHittedHideAbstract CStateMonsterHittedHide<_Object> + +#define GOOD_DISTANCE_TO_ENEMY 10.f +#define GOOD_DISTANCE_IN_COVER 15.f +#define MIN_HIDE_TIME 3.f +#define DIST_TO_PATH_END 1.5f + +TEMPLATE_SPECIALIZATION +void CStateMonsterHittedHideAbstract::initialize() +{ + inherited::initialize(); + object->path().prepare_builder (); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHittedHideAbstract::execute() +{ + object->set_action (ACT_RUN); + object->set_state_sound (MonsterSound::eMonsterSoundPanic); + object->anim().accel_activate (eAT_Aggressive); + object->anim().accel_set_braking (false); + object->path().set_retreat_from_point (object->HitMemory.get_last_hit_position()); + object->path().set_generic_parameters (); + +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterHittedHideAbstract::check_start_conditions() +{ + if (object->HitMemory.is_hit() && !object->EnemyMan.get_enemy()) return true; + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterHittedHideAbstract::check_completion() +{ + float dist = object->Position().distance_to(object->HitMemory.get_last_hit_position()); + + // good dist + if (dist < GOOD_DISTANCE_IN_COVER) return false; + // +hide more than 3 sec + if (time_state_started + MIN_HIDE_TIME > Device.dwTimeGlobal) return false; + + return true; +} + +#undef GOOD_DISTANCE_IN_COVER +#undef DIST_TO_PATH_END +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterHittedHideAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_hitted_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_hitted_inline.h new file mode 100644 index 000000000..a92b41360 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_hitted_inline.h @@ -0,0 +1,48 @@ +#pragma once + +#include "monster_state_hitted_hide.h" +#include "monster_state_hitted_moveout.h" +#include "monster_state_home_point_danger.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterHittedAbstract CStateMonsterHitted<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterHittedAbstract::CStateMonsterHitted(_Object *obj) : inherited(obj) +{ + add_state (eStateHitted_Hide, new CStateMonsterHittedHide<_Object>(obj)); + add_state (eStateHitted_MoveOut, new CStateMonsterHittedMoveOut<_Object>(obj)); + add_state (eStateHitted_Home, new CStateMonsterDangerMoveToHomePoint<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterHittedAbstract::~CStateMonsterHitted() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHittedAbstract::reselect_state() +{ + if (get_state(eStateHitted_Home)->check_start_conditions()) { + select_state(eStateHitted_Home); + return; + } + + if (prev_substate == u32(-1)) { + select_state(eStateHitted_Hide); + return; + } + + if (prev_substate == eStateHitted_Hide) { + select_state(eStateHitted_MoveOut); + return; + } + + select_state(eStateHitted_Hide); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterHittedAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_hitted_moveout.h b/src/xrGameLA/ai/monsters/states/monster_state_hitted_moveout.h new file mode 100644 index 000000000..5e5efa48b --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_hitted_moveout.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../../../detail_path_manager.h" + +template +class CStateMonsterHittedMoveOut : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + struct { + Fvector position; + u32 node; + } target; + +public: + + CStateMonsterHittedMoveOut (_Object *obj) : inherited(obj) {} + virtual ~CStateMonsterHittedMoveOut () {} + + virtual void initialize (); + virtual void execute (); + virtual bool check_completion (); + +private: + void select_target (); + +}; + +#include "monster_state_hitted_moveout_inline.h" + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_hitted_moveout_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_hitted_moveout_inline.h new file mode 100644 index 000000000..f3f7cd5dd --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_hitted_moveout_inline.h @@ -0,0 +1,63 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> +#define CStateMonsterHittedMoveOutAbstract CStateMonsterHittedMoveOut<_Object> + +#define DIST_TO_PATH_END 1.5f +#define DIST_TO_HIT_POINT 3.f + +TEMPLATE_SPECIALIZATION +void CStateMonsterHittedMoveOutAbstract::initialize() +{ + inherited::initialize(); + select_target(); + object->path().prepare_builder (); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHittedMoveOutAbstract::execute() +{ + // проверить на завершение пути + if (object->control().path_builder().detail().time_path_built() > time_state_started) { + if (object->control().path_builder().is_path_end(DIST_TO_PATH_END)) + select_target (); + } + + if (target.node != u32(-1)) + object->path().set_target_point (target.position, target.node); + else + object->path().set_target_point (object->HitMemory.get_last_hit_position()); + + float dist = object->HitMemory.get_last_hit_position().distance_to(object->Position()); + + if (dist > 10.f) object->set_action (ACT_WALK_FWD); + else object->set_action (ACT_STEAL); + + object->anim().accel_deactivate (); + object->anim().accel_set_braking (false); + object->set_state_sound (MonsterSound::eMonsterSoundIdle); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterHittedMoveOutAbstract::check_completion() +{ + if (object->HitMemory.get_last_hit_time() > time_state_started) return true; + + float dist = object->HitMemory.get_last_hit_position().distance_to(object->Position()); + if (dist < DIST_TO_HIT_POINT) return true; + + return false; +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHittedMoveOutAbstract::select_target() +{ + if (!object->GetCoverCloseToPoint(object->HitMemory.get_last_hit_position(), 10.f, 20.f, 0.f, 15.f, target.position, target.node)){ + target.node = u32(-1); + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterHittedMoveOutAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_home_point_attack.h b/src/xrGameLA/ai/monsters/states/monster_state_home_point_attack.h new file mode 100644 index 000000000..8613e5bf0 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_home_point_attack.h @@ -0,0 +1,26 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterAttackMoveToHomePoint : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + u32 m_target_node; + bool m_skip_camp; + +public: + CStateMonsterAttackMoveToHomePoint(_Object *obj); + virtual void initialize (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); + + virtual void reselect_state (); + virtual void setup_substates (); +}; + +#include "monster_state_home_point_attack_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_home_point_attack_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_home_point_attack_inline.h new file mode 100644 index 000000000..b33322bec --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_home_point_attack_inline.h @@ -0,0 +1,167 @@ +#pragma once + +#include "state_move_to_point.h" +#include "state_look_point.h" +#include "state_custom_action.h" +#include "../../../cover_point.h" +#include "../monster_cover_manager.h" +#include "../monster_home.h" + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterAttackMoveToHomePointAbstract CStateMonsterAttackMoveToHomePoint<_Object> + +////////////////////////////////////////////////////////////////////////// +// Construct Substates +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +CStateMonsterAttackMoveToHomePointAbstract::CStateMonsterAttackMoveToHomePoint(_Object *obj) : inherited(obj) +{ + add_state (eStateAttack_HomePoint_Hide, new CStateMonsterMoveToPointEx<_Object>(obj)); + add_state (eStateAttack_HomePoint_LookOpenPlace, new CStateMonsterLookToPoint<_Object>(obj)); + add_state (eStateAttack_HomePoint_Camp, new CStateMonsterCustomAction<_Object>(obj)); +} + +////////////////////////////////////////////////////////////////////////// +// Initialize/Finalize +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackMoveToHomePointAbstract::initialize() +{ + inherited::initialize (); + m_target_node = object->Home->get_place_in_cover(); + m_skip_camp = false; + + if (m_target_node == u32(-1)) { + m_target_node = object->Home->get_place(); + m_skip_camp = true; + } + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->lock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackMoveToHomePointAbstract::finalize() +{ + inherited::finalize(); + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackMoveToHomePointAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover(m_target_node); +} + +////////////////////////////////////////////////////////////////////////// +// Check Start Conditions / Completion +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackMoveToHomePointAbstract::check_start_conditions() +{ + return (!object->Home->at_home() && !object->Home->at_home(object->EnemyMan.get_enemy_position())); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterAttackMoveToHomePointAbstract::check_completion() +{ + if (object->HitMemory.get_last_hit_time() > time_state_started) return true; + if (object->EnemyMan.see_enemy_now() && object->Home->at_home(object->EnemyMan.get_enemy()->Position())) return true; + if (m_skip_camp && (prev_substate != u32(-1)) && (prev_substate != eStateAttack_HomePoint_Hide) ) return true; + + return false; +} + +////////////////////////////////////////////////////////////////////////// +// Select Substate +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackMoveToHomePointAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) { + select_state(eStateAttack_HomePoint_Hide); + return; + } + + if (prev_substate == eStateAttack_HomePoint_Hide) { + select_state(eStateAttack_HomePoint_LookOpenPlace); + return; + } + + select_state(eStateAttack_HomePoint_Camp); +} + +////////////////////////////////////////////////////////////////////////// +// Setup Substates +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateMonsterAttackMoveToHomePointAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateAttack_HomePoint_Hide) { + SStateDataMoveToPointEx data; + + data.vertex = m_target_node; + data.point = ai().level_graph().vertex_position(data.vertex); + data.action.action = ACT_RUN; + data.action.time_out = 0; // do not use time out + data.completion_dist = 0.f; // get exactly to the point + data.time_to_rebuild = 0; // do not rebuild + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Aggressive; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwAttackSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + return; + } + + if (current_substate == eStateAttack_HomePoint_LookOpenPlace) { + + SStateDataLookToPoint data; + + Fvector dir; + object->CoverMan->less_cover_direction(dir); + + data.point.mad (object->Position(),dir,10.f); + data.action.action = ACT_STAND_IDLE; + data.action.time_out = 2000; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.face_delay = 0; + + state->fill_data_with(&data, sizeof(SStateDataLookToPoint)); + return; + } + + if (current_substate == eStateAttack_HomePoint_Camp) { + SStateDataAction data; + + data.action = ACT_LOOK_AROUND; + data.time_out = 0; // do not use time out + data.sound_type = MonsterSound::eMonsterSoundAggressive; + data.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterAttackMoveToHomePointAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_home_point_danger.h b/src/xrGameLA/ai/monsters/states/monster_state_home_point_danger.h new file mode 100644 index 000000000..30189e7b5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_home_point_danger.h @@ -0,0 +1,29 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterDangerMoveToHomePoint : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + u32 m_target_node; + bool m_skip_camp; + Fvector m_danger_pos; + +public: + CStateMonsterDangerMoveToHomePoint(_Object *obj); + virtual void initialize (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); + + virtual void reselect_state (); + virtual void setup_substates (); +private: + Fvector &get_most_danger_pos (); +}; + +#include "monster_state_home_point_danger_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_home_point_danger_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_home_point_danger_inline.h new file mode 100644 index 000000000..1d7bf9473 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_home_point_danger_inline.h @@ -0,0 +1,183 @@ +#pragma once + +#include "state_move_to_point.h" +#include "state_look_point.h" +#include "state_custom_action.h" +#include "../../../cover_point.h" +#include "../monster_cover_manager.h" +#include "../monster_home.h" + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterDangerMoveToHomePointAbstract CStateMonsterDangerMoveToHomePoint<_Object> + +////////////////////////////////////////////////////////////////////////// +// Construct Substates +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +CStateMonsterDangerMoveToHomePointAbstract::CStateMonsterDangerMoveToHomePoint(_Object *obj) : inherited(obj) +{ + add_state (eStatePanic_HomePoint_Hide, new CStateMonsterMoveToPointEx<_Object>(obj)); + add_state (eStatePanic_HomePoint_LookOpenPlace, new CStateMonsterLookToPoint<_Object>(obj)); + add_state (eStatePanic_HomePoint_Camp, new CStateMonsterCustomAction<_Object>(obj)); +} + +////////////////////////////////////////////////////////////////////////// +// Initialize/Finalize +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateMonsterDangerMoveToHomePointAbstract::initialize() +{ + inherited::initialize (); + + // get the most danger position + get_most_danger_pos (); + + m_target_node = object->Home->get_place_in_cover(); + m_skip_camp = false; + + if (m_target_node == u32(-1)) { + m_target_node = object->Home->get_place(); + m_skip_camp = true; + } + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->lock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterDangerMoveToHomePointAbstract::finalize() +{ + inherited::finalize(); + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterDangerMoveToHomePointAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover(m_target_node); +} + +////////////////////////////////////////////////////////////////////////// +// Check Start Conditions / Completion +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +bool CStateMonsterDangerMoveToHomePointAbstract::check_start_conditions() +{ + return (!object->Home->at_home() && !object->Home->at_home(get_most_danger_pos())); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterDangerMoveToHomePointAbstract::check_completion() +{ + if (object->HitMemory.get_last_hit_time() > time_state_started) return true; + if (m_skip_camp && (prev_substate != u32(-1)) && (prev_substate != eStatePanic_HomePoint_Hide) ) return true; + + return false; +} + +////////////////////////////////////////////////////////////////////////// +// Select Substate +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateMonsterDangerMoveToHomePointAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) { + select_state(eStatePanic_HomePoint_Hide); + return; + } + + if (prev_substate == eStatePanic_HomePoint_Hide) { + select_state(eStatePanic_HomePoint_LookOpenPlace); + return; + } + + select_state(eStatePanic_HomePoint_Camp); +} + +////////////////////////////////////////////////////////////////////////// +// Setup Substates +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateMonsterDangerMoveToHomePointAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStatePanic_HomePoint_Hide) { + SStateDataMoveToPointEx data; + + data.vertex = m_target_node; + data.point = ai().level_graph().vertex_position(data.vertex); + data.action.action = ACT_RUN; + data.action.time_out = 0; // do not use time out + data.completion_dist = 0.f; // get exactly to the point + data.time_to_rebuild = 0; // do not rebuild + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Aggressive; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwAttackSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + return; + } + + if (current_substate == eStatePanic_HomePoint_LookOpenPlace) { + + SStateDataLookToPoint data; + + Fvector dir; + object->CoverMan->less_cover_direction(dir); + + data.point.mad (object->Position(),dir,10.f); + data.action.action = ACT_STAND_IDLE; + data.action.time_out = 2000; + data.action.sound_type = MonsterSound::eMonsterSoundAggressive; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.face_delay = 0; + + state->fill_data_with(&data, sizeof(SStateDataLookToPoint)); + return; + } + + if (current_substate == eStatePanic_HomePoint_Camp) { + SStateDataAction data; + + data.action = ACT_LOOK_AROUND; + data.time_out = 0; // do not use time out + data.sound_type = MonsterSound::eMonsterSoundAggressive; + data.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } +} +TEMPLATE_SPECIALIZATION +Fvector &CStateMonsterDangerMoveToHomePointAbstract::get_most_danger_pos() +{ + m_danger_pos.set(0,0,0); + + if (object->HitMemory.is_hit()) { + m_danger_pos = object->HitMemory.get_last_hit_position(); + } else if (object->hear_dangerous_sound) { + m_danger_pos = object->SoundMemory.GetSound().position; + } + + return m_danger_pos; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterDangerMoveToHomePointAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_home_point_rest.h b/src/xrGameLA/ai/monsters/states/monster_state_home_point_rest.h new file mode 100644 index 000000000..3845c0aca --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_home_point_rest.h @@ -0,0 +1,20 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterRestMoveToHomePoint : public CStateMove<_Object> { +protected: + typedef CStateMove<_Object> inherited; + typedef CStateMove<_Object>* state_ptr; + + u32 m_target_node; + +public: + CStateMonsterRestMoveToHomePoint(_Object *obj) : inherited(obj){} + virtual void initialize (); + virtual void execute (); + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "monster_state_home_point_rest_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_home_point_rest_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_home_point_rest_inline.h new file mode 100644 index 000000000..3fe4b8099 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_home_point_rest_inline.h @@ -0,0 +1,45 @@ +#pragma once + +#include "../monster_home.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterRestMoveToHomePointAbstract CStateMonsterRestMoveToHomePoint<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestMoveToHomePointAbstract::initialize() +{ + inherited::initialize (); + m_target_node = object->Home->get_place(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestMoveToHomePointAbstract::execute() +{ + object->path().set_target_point (ai().level_graph().vertex_position(m_target_node), m_target_node); + object->anim().accel_activate (EAccelType(object->Home->is_aggressive() ? eAT_Aggressive : eAT_Calm)); + object->anim().accel_set_braking (true); + object->path().set_rebuild_time (0); + object->path().set_distance_to_end (0.f); + object->path().set_use_covers (false); + + object->set_action (object->Home->is_aggressive() ? ACT_RUN : ACT_WALK_FWD); + object->set_state_sound (object->Home->is_aggressive() ? MonsterSound::eMonsterSoundAggressive : MonsterSound::eMonsterSoundIdle); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterRestMoveToHomePointAbstract::check_start_conditions() +{ + return (!object->Home->at_home()); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterRestMoveToHomePointAbstract::check_completion() +{ + return ((object->ai_location().level_vertex_id() == m_target_node) && !object->control().path_builder().is_moving_on_path()); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterRestMoveToHomePointAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_panic.h b/src/xrGameLA/ai/monsters/states/monster_state_panic.h new file mode 100644 index 000000000..08c4a880d --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_panic.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterPanic : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + +public: + CStateMonsterPanic (_Object *obj); + virtual ~CStateMonsterPanic (); + + virtual void initialize (); + virtual void reselect_state (); + virtual void check_force_state (); + virtual void setup_substates (); +}; + +#include "monster_state_panic_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_panic_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_panic_inline.h new file mode 100644 index 000000000..9a36508bc --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_panic_inline.h @@ -0,0 +1,86 @@ +#pragma once + +#include "state_data.h" +#include "state_move_to_point.h" +#include "state_custom_action.h" +#include "state_look_unprotected_area.h" +#include "monster_state_panic_run.h" +#include "monster_state_home_point_attack.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterPanicAbstract CStateMonsterPanic<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterPanicAbstract::CStateMonsterPanic(_Object *obj) : inherited(obj) +{ + add_state(eStatePanic_Run, new CStateMonsterPanicRun<_Object>(obj)); + add_state(eStatePanic_FaceUnprotectedArea, new CStateMonsterLookToUnprotectedArea<_Object>(obj)); + add_state(eStatePanic_MoveToHomePoint, new CStateMonsterAttackMoveToHomePoint<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterPanicAbstract::~CStateMonsterPanic() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterPanicAbstract::initialize() +{ + inherited::initialize(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterPanicAbstract::reselect_state() +{ + if (get_state(eStatePanic_MoveToHomePoint)->check_start_conditions()) { + select_state(eStatePanic_MoveToHomePoint); + return; + } + + if (prev_substate == eStatePanic_Run) select_state(eStatePanic_FaceUnprotectedArea); + else select_state(eStatePanic_Run); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterPanicAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStatePanic_FaceUnprotectedArea) { + SStateDataAction data; + + data.action = ACT_STAND_IDLE; + data.spec_params = ASP_STAND_SCARED; + data.time_out = 3000; + data.sound_type = MonsterSound::eMonsterSoundPanic; + data.sound_delay = object->db().m_dwAttackSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } + +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterPanicAbstract::check_force_state() +{ + if ((current_substate == eStatePanic_FaceUnprotectedArea)){ + // если видит врага + if (object->EnemyMan.get_enemy_time_last_seen() == Device.dwTimeGlobal) { + select_state(eStatePanic_Run); + return; + } + // если получил hit + if (object->HitMemory.get_last_hit_time() + 5000 > Device.dwTimeGlobal) { + select_state(eStatePanic_Run); + return; + } + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterPanicAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_panic_run.h b/src/xrGameLA/ai/monsters/states/monster_state_panic_run.h new file mode 100644 index 000000000..1440ac40b --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_panic_run.h @@ -0,0 +1,17 @@ +#pragma once + +template +class CStateMonsterPanicRun : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateMonsterPanicRun (_Object *obj) : inherited(obj) {} + virtual ~CStateMonsterPanicRun () {} + + virtual void initialize (); + virtual void execute (); + + virtual bool check_completion (); +}; + +#include "monster_state_panic_run_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_panic_run_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_panic_run_inline.h new file mode 100644 index 000000000..05e787ba4 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_panic_run_inline.h @@ -0,0 +1,45 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterPanicRunAbstract CStateMonsterPanicRun<_Object> + +#define MIN_UNSEEN_TIME 15000 +#define MIN_DIST_TO_ENEMY 15.f + +TEMPLATE_SPECIALIZATION +void CStateMonsterPanicRunAbstract::initialize() +{ + inherited::initialize(); + + object->path().prepare_builder (); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterPanicRunAbstract::execute() +{ + object->set_action (ACT_RUN); + object->set_state_sound (MonsterSound::eMonsterSoundPanic); + object->anim().accel_activate (eAT_Aggressive); + object->anim().accel_set_braking (false); + object->path().set_retreat_from_point (object->EnemyMan.get_enemy_position()); + object->path().set_generic_parameters (); +} +TEMPLATE_SPECIALIZATION +bool CStateMonsterPanicRunAbstract::check_completion() +{ + float dist_to_enemy = object->Position().distance_to(object->EnemyMan.get_enemy_position()); + u32 time_delta = Device.dwTimeGlobal - object->EnemyMan.get_enemy_time_last_seen(); + + if (dist_to_enemy < MIN_DIST_TO_ENEMY) return false; + if (time_delta < MIN_UNSEEN_TIME) return false; + + return true; +} + +#undef DIST_TO_PATH_END +#undef MIN_DIST_TO_ENEMY +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterPanicRunAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_rest.h b/src/xrGameLA/ai/monsters/states/monster_state_rest.h new file mode 100644 index 000000000..4b93e3339 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_rest.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../state.h" +#include "../../../entitycondition.h" + +template +class CStateMonsterRest : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + u32 time_last_fun; + u32 time_idle_selected; + +public: + CStateMonsterRest (_Object *obj); + virtual ~CStateMonsterRest (); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); +}; + +#include "monster_state_rest_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_rest_fun.h b/src/xrGameLA/ai/monsters/states/monster_state_rest_fun.h new file mode 100644 index 000000000..00394123a --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_rest_fun.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../state.h" +#include "../../../ai_debug.h" + +template +class CStateMonsterRestFun : public CState<_Object> { + typedef CState<_Object> inherited; + + u32 time_last_hit; + +public: + CStateMonsterRestFun (_Object *obj); + virtual void initialize (); + virtual void execute (); + virtual bool check_completion (); + virtual bool check_start_conditions (); +}; + +#include "monster_state_rest_fun_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_rest_fun_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_rest_fun_inline.h new file mode 100644 index 000000000..654c439fc --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_rest_fun_inline.h @@ -0,0 +1,88 @@ +#pragma once + +#include "../../../PhysicsShell.h" +#include "../../../PHInterpolation.h" +#include "../../../PHElement.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterRestFunAbstract CStateMonsterRestFun<_Object> + +#define IMPULSE_TO_CORPSE 15.f +#define MIN_DELAY 100 +#define TIME_IN_STATE 8000 + +TEMPLATE_SPECIALIZATION +CStateMonsterRestFunAbstract::CStateMonsterRestFun(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestFunAbstract::initialize() +{ + inherited::initialize (); + + time_last_hit = 0; +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestFunAbstract::execute() +{ + Fvector point; + float dist; + + Fvector dir; + dir.sub (object->CorpseMan.get_corpse_position(), object->Position()); + dist = dir.magnitude(); + dir.normalize (); + point.mad (object->CorpseMan.get_corpse_position(), dir, 2.0f); + + object->set_action (ACT_RUN); + object->path().set_target_point (point); + object->path().set_rebuild_time (100 + u32(50.f * dist)); + object->path().set_use_covers (false); + object->path().set_distance_to_end (0.5f); + object->anim().accel_activate (eAT_Calm); + object->anim().accel_set_braking (false); + + object->set_state_sound (MonsterSound::eMonsterSoundIdle); + + if ((dist < object->db().m_fDistToCorpse + 0.5f) && (time_last_hit + MIN_DELAY < Device.dwTimeGlobal)) { + CEntityAlive *corpse = const_cast (object->CorpseMan.get_corpse()); + CPhysicsShellHolder *target = smart_cast (corpse); + + if (target && target->m_pPhysicsShell) { + Fvector dir; + dir.add (Fvector().sub(target->Position(), object->Position()), object->Direction()); + + float h,p; + dir.getHP (h,p); + dir.setHP (h, p + 5 * PI / 180); + dir.normalize (); + + // выполнить бросок + for (u32 i=0; im_pPhysicsShell->Elements().size();i++) { + target->m_pPhysicsShell->Elements()[i]->applyImpulse(dir, IMPULSE_TO_CORPSE * target->m_pPhysicsShell->getMass() / target->m_pPhysicsShell->Elements().size()); + } + + time_last_hit = Device.dwTimeGlobal; + } + } +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterRestFunAbstract::check_start_conditions() +{ + return ((object->CorpseMan.get_corpse() != 0) && object->Home->at_home(object->CorpseMan.get_corpse()->Position())); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterRestFunAbstract::check_completion() +{ + if (!object->CorpseMan.get_corpse()) return true; + if (time_state_started + TIME_IN_STATE < Device.dwTimeGlobal) return true; + return false; +} diff --git a/src/xrGameLA/ai/monsters/states/monster_state_rest_idle.h b/src/xrGameLA/ai/monsters/states/monster_state_rest_idle.h new file mode 100644 index 000000000..3ebddc14f --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_rest_idle.h @@ -0,0 +1,22 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterRestIdle : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object> *state_ptr; + + u32 m_target_node; + +public: + CStateMonsterRestIdle (_Object *obj); + virtual void initialize (); + virtual void finalize (); + virtual void critical_finalize (); + + virtual void reselect_state (); + virtual void setup_substates (); +}; + +#include "monster_state_rest_idle_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_rest_idle_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_rest_idle_inline.h new file mode 100644 index 000000000..768a8ce2a --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_rest_idle_inline.h @@ -0,0 +1,136 @@ +#pragma once + +#include "state_move_to_point.h" +#include "state_look_point.h" +#include "state_custom_action.h" +#include "../../../cover_point.h" +#include "../monster_cover_manager.h" + + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterRestIdleAbstract CStateMonsterRestIdle<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterRestIdleAbstract::CStateMonsterRestIdle(_Object *obj) : inherited(obj) +{ + add_state (eStateRest_WalkToCover, new CStateMonsterMoveToPointEx<_Object>(obj)); + add_state (eStateRest_LookOpenPlace, new CStateMonsterLookToPoint<_Object>(obj)); + add_state (eStateRest_Idle, new CStateMonsterCustomAction<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestIdleAbstract::initialize() +{ + inherited::initialize(); + + m_target_node = u32(-1); + + // try to get cover + const CCoverPoint *point = object->CoverMan->find_cover(object->Position(), 5.f, 10.f); + if (!point) { + point = object->CoverMan->find_cover(object->Position(), 10.f, 30.f); + if (!point) return; + } + + m_target_node = point->level_vertex_id(); + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->lock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestIdleAbstract::finalize() +{ + inherited::finalize(); + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover(m_target_node); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestIdleAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + CMonsterSquad *squad = monster_squad().get_squad(object); + squad->unlock_cover(m_target_node); +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestIdleAbstract::reselect_state() +{ + if ((prev_substate == u32(-1)) && (m_target_node != u32(-1))) { + select_state(eStateRest_WalkToCover); + return; + } + + if ((prev_substate == eStateRest_WalkToCover) || (prev_substate == u32(-1))) { + select_state(eStateRest_LookOpenPlace); + return; + } + + select_state(eStateRest_Idle); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestIdleAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateRest_WalkToCover) { + SStateDataMoveToPointEx data; + + data.vertex = m_target_node; + data.point = ai().level_graph().vertex_position(data.vertex); + data.action.action = ACT_WALK_FWD; + data.action.time_out = 0; // do not use time out + data.completion_dist = 0.f; // get exactly to the point + data.time_to_rebuild = 0; // do not rebuild + data.accelerated = true; + data.braking = true; + data.accel_type = eAT_Calm; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + return; + } + + if (current_substate == eStateRest_LookOpenPlace) { + + SStateDataLookToPoint data; + + Fvector dir; + object->CoverMan->less_cover_direction(dir); + + data.point.mad (object->Position(),dir,10.f); + data.action.action = ACT_STAND_IDLE; + data.action.time_out = 2000; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.face_delay = 0; + + state->fill_data_with(&data, sizeof(SStateDataLookToPoint)); + return; + } + + if (current_substate == eStateRest_Idle) { + SStateDataAction data; + + data.action = ACT_REST; + data.time_out = 0; // do not use time out + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } + +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterRestIdleAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_rest_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_rest_inline.h new file mode 100644 index 000000000..60501f2f7 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_rest_inline.h @@ -0,0 +1,134 @@ +#pragma once + +#include "monster_state_rest_sleep.h" +#include "monster_state_rest_walk_graph.h" +#include "monster_state_rest_idle.h" +#include "monster_state_rest_fun.h" +#include "monster_state_squad_rest.h" +#include "monster_state_squad_rest_follow.h" +#include "state_move_to_restrictor.h" +#include "../ai_monster_squad.h" +#include "../ai_monster_squad_manager.h" +#include "../anomaly_detector.h" +#include "monster_state_home_point_rest.h" +#include "monster_state_smart_terrain_task.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterRestAbstract CStateMonsterRest<_Object> + +#define TIME_DELAY_FUN 20000 +#define TIME_IDLE 60000 + +TEMPLATE_SPECIALIZATION +CStateMonsterRestAbstract::CStateMonsterRest(_Object *obj) : inherited(obj) +{ + add_state(eStateRest_Sleep, new CStateMonsterRestSleep<_Object>(obj)); + add_state(eStateRest_WalkGraphPoint, new CStateMonsterRestWalkGraph<_Object>(obj)); + add_state(eStateRest_Idle, new CStateMonsterRestIdle<_Object>(obj)); + add_state(eStateRest_Fun, new CStateMonsterRestFun<_Object>(obj)); + add_state(eStateSquad_Rest, new CStateMonsterSquadRest<_Object>(obj)); + add_state(eStateSquad_RestFollow, new CStateMonsterSquadRestFollow<_Object>(obj)); + add_state(eStateCustomMoveToRestrictor, new CStateMonsterMoveToRestrictor<_Object>(obj)); + add_state(eStateRest_MoveToHomePoint, new CStateMonsterRestMoveToHomePoint<_Object>(obj)); + add_state(eStateSmartTerrainTask, new CStateMonsterSmartTerrainTask<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterRestAbstract::~CStateMonsterRest () +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestAbstract::initialize() +{ + inherited::initialize (); + + time_last_fun = 0; + time_idle_selected = Random.randI(2) ? 0 : time(); + object->anomaly_detector().activate(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestAbstract::finalize() +{ + inherited::finalize(); + + object->anomaly_detector().deactivate(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestAbstract::critical_finalize() +{ + inherited::critical_finalize(); + + object->anomaly_detector().deactivate(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestAbstract::execute() +{ + // check alife control + bool captured_by_smart_terrain = false; + + if (prev_substate == eStateSmartTerrainTask) { + if (!get_state(eStateSmartTerrainTask)->check_completion()) + captured_by_smart_terrain = true; + } else if (get_state(eStateSmartTerrainTask)->check_start_conditions()) + captured_by_smart_terrain = true; + + if (captured_by_smart_terrain) select_state(eStateSmartTerrainTask); + else { + // check restrictions + bool move_to_restrictor = false; + + if (prev_substate == eStateCustomMoveToRestrictor) { + if (!get_state(eStateCustomMoveToRestrictor)->check_completion()) + move_to_restrictor = true; + } else if (get_state(eStateCustomMoveToRestrictor)->check_start_conditions()) + move_to_restrictor = true; + + if (move_to_restrictor) select_state(eStateCustomMoveToRestrictor); + else { + // check home point + bool move_to_home_point = false; + + if (prev_substate == eStateRest_MoveToHomePoint) { + if (!get_state(eStateRest_MoveToHomePoint)->check_completion()) + move_to_home_point = true; + } else if (get_state(eStateRest_MoveToHomePoint)->check_start_conditions()) + move_to_home_point = true; + + if (move_to_home_point) select_state(eStateRest_MoveToHomePoint); + else { + // check squad behaviour + bool use_squad = false; + + if (monster_squad().get_squad(object)->GetCommand(object).type == SC_REST) { + select_state (eStateSquad_Rest); + use_squad = true; + } else if (monster_squad().get_squad(object)->GetCommand(object).type == SC_FOLLOW) { + select_state (eStateSquad_RestFollow); + use_squad = true; + } + + if (!use_squad) { + if (time_idle_selected + TIME_IDLE > time()) select_state (eStateRest_Idle); + else if (time_idle_selected + TIME_IDLE + TIME_IDLE/2 > time()) select_state (eStateRest_WalkGraphPoint); + else { + time_idle_selected = time(); + select_state (eStateRest_Idle); + } + } + } + } + } + + get_state_current()->execute(); + prev_substate = current_substate; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterRestAbstract diff --git a/src/xrGameLA/ai/monsters/states/monster_state_rest_sleep.h b/src/xrGameLA/ai/monsters/states/monster_state_rest_sleep.h new file mode 100644 index 000000000..b05648188 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_rest_sleep.h @@ -0,0 +1,19 @@ +#pragma once + +#include "../state.h" +#include "../../../ai_debug.h" + +template +class CStateMonsterRestSleep : public CState<_Object> { + typedef CState<_Object> inherited; +public: + CStateMonsterRestSleep (_Object *obj); + virtual ~CStateMonsterRestSleep (); + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); +}; + +#include "monster_state_rest_sleep_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_rest_sleep_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_rest_sleep_inline.h new file mode 100644 index 000000000..9e196a766 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_rest_sleep_inline.h @@ -0,0 +1,46 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterRestSleepAbstract CStateMonsterRestSleep<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterRestSleepAbstract::CStateMonsterRestSleep(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterRestSleepAbstract::~CStateMonsterRestSleep () +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestSleepAbstract::initialize() +{ + inherited::initialize (); + object->fall_asleep (); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestSleepAbstract::execute() +{ + object->set_action (ACT_SLEEP); + object->set_state_sound (MonsterSound::eMonsterSoundIdle); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestSleepAbstract::finalize() +{ + inherited::finalize (); + object->wake_up (); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestSleepAbstract::critical_finalize() +{ + inherited::critical_finalize (); + object->wake_up (); +} + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_rest_walk_graph.h b/src/xrGameLA/ai/monsters/states/monster_state_rest_walk_graph.h new file mode 100644 index 000000000..cb5745083 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_rest_walk_graph.h @@ -0,0 +1,15 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterRestWalkGraph : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateMonsterRestWalkGraph (_Object *obj); + virtual ~CStateMonsterRestWalkGraph (); + + virtual void execute (); +}; + +#include "monster_state_rest_walk_graph_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_rest_walk_graph_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_rest_walk_graph_inline.h new file mode 100644 index 000000000..16f13f75b --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_rest_walk_graph_inline.h @@ -0,0 +1,26 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterRestWalkGraphAbstract CStateMonsterRestWalkGraph<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterRestWalkGraphAbstract::CStateMonsterRestWalkGraph(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterRestWalkGraphAbstract::~CStateMonsterRestWalkGraph () +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterRestWalkGraphAbstract::execute() +{ + object->path().detour_graph_points (); + object->set_action (ACT_WALK_FWD); + object->set_state_sound (MonsterSound::eMonsterSoundIdle); +} + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task.h b/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task.h new file mode 100644 index 000000000..7128bc2bf --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task.h @@ -0,0 +1,24 @@ +#pragma once +#include "../state.h" + +#include "../../../alife_smart_terrain_task.h" + +template +class CStateMonsterSmartTerrainTask : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + CALifeSmartTerrainTask *m_current_task; +public: + CStateMonsterSmartTerrainTask (_Object *obj); + virtual ~CStateMonsterSmartTerrainTask (); + + virtual void initialize (); + virtual void reselect_state (); + virtual bool check_start_conditions (); + virtual bool check_completion (); + virtual void setup_substates (); + virtual void check_force_state (); +}; + +#include "monster_state_smart_terrain_task_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_graph_walk.h b/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_graph_walk.h new file mode 100644 index 000000000..56650db76 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_graph_walk.h @@ -0,0 +1,18 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterSmartTerrainTaskGraphWalk : public CStateMove<_Object> { + typedef CStateMove<_Object> inherited; + + CALifeSmartTerrainTask *m_task; + +public: + CStateMonsterSmartTerrainTaskGraphWalk (_Object *obj) : inherited(obj) {} + virtual void initialize (); + virtual void execute (); + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "monster_state_smart_terrain_task_graph_walk_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_graph_walk_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_graph_walk_inline.h new file mode 100644 index 000000000..475268977 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_graph_walk_inline.h @@ -0,0 +1,59 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterSmartTerrainTaskGraphWalkAbstract CStateMonsterSmartTerrainTaskGraphWalk<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterSmartTerrainTaskGraphWalkAbstract::initialize() +{ + inherited::initialize (); + + CSE_ALifeMonsterAbstract *monster = smart_cast(ai().alife().objects().object(object->ID())); + VERIFY (monster); + VERIFY (monster->m_smart_terrain_id != 0xffff); + + // get task + m_task = monster->brain().smart_terrain().task(monster); + VERIFY (m_task); +} + + +TEMPLATE_SPECIALIZATION +bool CStateMonsterSmartTerrainTaskGraphWalkAbstract::check_start_conditions() +{ + CSE_ALifeMonsterAbstract *monster = smart_cast(ai().alife().objects().object(object->ID())); + VERIFY (monster); + + if (monster->m_smart_terrain_id == 0xffff) return false; + + m_task = monster->brain().smart_terrain().task(monster); + VERIFY3 (m_task, "Smart terrain selected, but task was not set for monster ", *object->cName()); + if (object->ai_location().game_vertex_id() == m_task->game_vertex_id()) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterSmartTerrainTaskGraphWalkAbstract::check_completion() +{ + // if we get to the graph point - work complete + if (object->ai_location().game_vertex_id() == m_task->game_vertex_id()) return true; + return false; +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterSmartTerrainTaskGraphWalkAbstract::execute() +{ + object->set_action (ACT_WALK_FWD); + object->set_state_sound (MonsterSound::eMonsterSoundIdle); + + object->path().detour_graph_points (m_task->game_vertex_id()); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterSmartTerrainTaskGraphWalkAbstract + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_inline.h new file mode 100644 index 000000000..fee07203a --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_smart_terrain_task_inline.h @@ -0,0 +1,170 @@ +#pragma once + +#include "../../../alife_simulator.h" +#include "../../../alife_object_registry.h" +#include "../../../xrServer_Objects_ALife_Monsters.h" +#include "../../../alife_monster_brain.h" + +#include "state_move_to_point.h" +#include "state_custom_action.h" +#include "monster_state_smart_terrain_task_graph_walk.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterSmartTerrainTaskAbstract CStateMonsterSmartTerrainTask<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterSmartTerrainTaskAbstract::CStateMonsterSmartTerrainTask(_Object *obj) : inherited(obj) +{ + add_state(eStateSmartTerrainTaskGamePathWalk, new CStateMonsterSmartTerrainTaskGraphWalk<_Object>(obj)); + add_state(eStateSmartTerrainTaskLevelPathWalk, new CStateMonsterMoveToPointEx<_Object>(obj)); + add_state(eStateSmartTerrainTaskWaitCapture, new CStateMonsterCustomAction<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterSmartTerrainTaskAbstract::~CStateMonsterSmartTerrainTask () +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterSmartTerrainTaskAbstract::initialize() +{ + inherited::initialize(); + + // save current task + CSE_ALifeMonsterAbstract *monster = smart_cast(ai().alife().objects().object(object->ID())); + VERIFY (monster); + VERIFY (monster->m_smart_terrain_id != 0xffff); + + m_current_task = monster->brain().smart_terrain().task(monster); +} + + +TEMPLATE_SPECIALIZATION +bool CStateMonsterSmartTerrainTaskAbstract::check_start_conditions() +{ + if (!ai().get_alife()) return false; + + CSE_ALifeMonsterAbstract *monster = smart_cast(ai().alife().objects().object(object->ID())); + VERIFY (monster); + + CSE_ALifePsyDogPhantom *phantom = smart_cast(monster); + if (phantom) return false; + + + monster->brain().select_task (); + + // there is no any available smart terrains + if (monster->m_smart_terrain_id == 0xffff) return false; + + // we dont need to reach task + if (monster->m_task_reached) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterSmartTerrainTaskAbstract::check_completion() +{ + CSE_ALifeMonsterAbstract *monster = smart_cast(ai().alife().objects().object(object->ID())); + VERIFY (monster); + + if (monster->m_smart_terrain_id == 0xffff) return true; + + // if we already reach the task + if (monster->m_task_reached) return true; + + return false; +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterSmartTerrainTaskAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateSmartTerrainTaskLevelPathWalk) { + SStateDataMoveToPointEx data; + + data.vertex = m_current_task->level_vertex_id(); + data.point = ai().level_graph().vertex_position(data.vertex); + data.action.action = ACT_WALK_FWD; + data.action.time_out = 0; // do not use time out + data.completion_dist = 0.f; // get exactly to the point + data.time_to_rebuild = 0; // do not rebuild + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Calm; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + return; + } + + if (current_substate == eStateSmartTerrainTaskWaitCapture) { + SStateDataAction data; + + data.action = ACT_REST; + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + data.time_out = 0; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + return; + } +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterSmartTerrainTaskAbstract::reselect_state() +{ + if (prev_substate == u32(-1)) { + if (get_state(eStateSmartTerrainTaskGamePathWalk)->check_start_conditions()) { + select_state(eStateSmartTerrainTaskGamePathWalk); + } else { + select_state(eStateSmartTerrainTaskLevelPathWalk); + } + return; + } + + if (prev_substate == eStateSmartTerrainTaskGamePathWalk) { + select_state(eStateSmartTerrainTaskLevelPathWalk); + return; + } + + if (prev_substate == eStateSmartTerrainTaskLevelPathWalk) { + select_state(eStateSmartTerrainTaskWaitCapture); + return; + } + + select_state(eStateSmartTerrainTaskWaitCapture); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterSmartTerrainTaskAbstract::check_force_state() +{ + // check smart terrain became busy + CSE_ALifeMonsterAbstract *monster = smart_cast(ai().alife().objects().object(object->ID())); + VERIFY (monster); + + if ((monster->m_smart_terrain_id == 0xffff) || monster->m_task_reached) { + select_state(eStateSmartTerrainTaskWaitCapture); + return; + } + + // check if task has changed + CALifeSmartTerrainTask *task = monster->brain().smart_terrain().task(monster); + if (!task || (m_current_task != task)) { + if (current_substate != u32(-1)) get_state_current()->critical_finalize(); + + current_substate = u32(-1); + prev_substate = u32(-1); + + m_current_task = task; + } +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterSmartTerrainTaskAbstract + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_squad_rest.h b/src/xrGameLA/ai/monsters/states/monster_state_squad_rest.h new file mode 100644 index 000000000..db6e3135d --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_squad_rest.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterSquadRest : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + u32 time_next_state_reselect; + +public: + CStateMonsterSquadRest (_Object *obj); + virtual ~CStateMonsterSquadRest (); + + virtual void reselect_state (); + virtual void setup_substates (); +}; + +#include "monster_state_squad_rest_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/monster_state_squad_rest_follow.h b/src/xrGameLA/ai/monsters/states/monster_state_squad_rest_follow.h new file mode 100644 index 000000000..6567ce368 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_squad_rest_follow.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../state.h" + +template +class CStateMonsterSquadRestFollow : public CState<_Object> { +protected: + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + Fvector last_point; + +public: + CStateMonsterSquadRestFollow (_Object *obj); + virtual ~CStateMonsterSquadRestFollow (); + + virtual void initialize (); + virtual void reselect_state (); + virtual void setup_substates (); + virtual void check_force_state (); +}; + +#include "monster_state_squad_rest_follow_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_squad_rest_follow_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_squad_rest_follow_inline.h new file mode 100644 index 000000000..ec25a0fa5 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_squad_rest_follow_inline.h @@ -0,0 +1,104 @@ +#pragma once + +#include "state_custom_action.h" +#include "state_move_to_point.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterSquadRestFollowAbstract CStateMonsterSquadRestFollow<_Object> + + +#define STOP_DISTANCE 2.f +#define STAY_DISTANCE 5 * STOP_DISTANCE +#define MIN_TIME_OUT 2000 +#define MAX_TIME_OUT 3000 + +TEMPLATE_SPECIALIZATION +CStateMonsterSquadRestFollowAbstract::CStateMonsterSquadRestFollow(_Object *obj) : inherited(obj) +{ + add_state (eStateSquad_RestFollow_Idle, new CStateMonsterCustomAction<_Object>(obj)); + add_state (eStateSquad_RestFollow_WalkToPoint, new CStateMonsterMoveToPointEx<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterSquadRestFollowAbstract::~CStateMonsterSquadRestFollow () +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterSquadRestFollowAbstract::initialize() +{ + inherited::initialize(); + + SSquadCommand &command = monster_squad().get_squad(object)->GetCommand(object); + last_point = command.position; +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterSquadRestFollowAbstract::reselect_state() +{ + SSquadCommand &command = monster_squad().get_squad(object)->GetCommand(object); + if (command.position.distance_to(object->Position()) < Random.randF(STOP_DISTANCE, STAY_DISTANCE)) { + select_state(eStateSquad_RestFollow_Idle); + } else { + select_state(eStateSquad_RestFollow_WalkToPoint); + } +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterSquadRestFollowAbstract::check_force_state() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterSquadRestFollowAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateSquad_RestFollow_Idle) { + SStateDataAction data; + data.action = ACT_REST; + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + data.time_out = Random.randI(MIN_TIME_OUT, MAX_TIME_OUT); + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } + + if (current_substate == eStateSquad_RestFollow_WalkToPoint) { + SStateDataMoveToPointEx data; + + Fvector dest_pos = monster_squad().get_squad(object)->GetCommand(object).position; + if (!object->control().path_builder().restrictions().accessible(dest_pos)) { + data.vertex = object->control().path_builder().restrictions().accessible_nearest(dest_pos, data.point); + } else { + data.point = dest_pos; + data.vertex = u32(-1); + } + + data.action.action = ACT_WALK_FWD; + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Calm; + data.completion_dist = STOP_DISTANCE; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.time_to_rebuild = u32(-1); + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + + return; + } +} + +#undef STOP_DISTANCE +#undef STAY_DISTANCE +#undef MIN_TIME_OUT +#undef MAX_TIME_OUT +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterSquadRestFollowAbstract + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_squad_rest_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_squad_rest_inline.h new file mode 100644 index 000000000..070f9bbcf --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_squad_rest_inline.h @@ -0,0 +1,90 @@ +#pragma once + +#include "../../../ai_space.h" +#include "../../../level_graph.h" +#include "../../../ai_object_location.h" +#include "state_custom_action.h" +#include "state_move_to_point.h" +#include "../../../restricted_object.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterSquadRestAbstract CStateMonsterSquadRest<_Object> + +#define MIN_TIME_IDLE 5000 +#define MAX_TIME_IDLE 10000 + +#define LEADER_RADIUS 20.f +#define FIND_POINT_ATTEMPTS 5 + + +TEMPLATE_SPECIALIZATION +CStateMonsterSquadRestAbstract::CStateMonsterSquadRest(_Object *obj) : inherited(obj) +{ + add_state (eStateSquad_Rest_Idle, new CStateMonsterCustomAction<_Object>(obj)); + add_state (eStateSquad_Rest_WalkAroundLeader, new CStateMonsterMoveToPoint<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +CStateMonsterSquadRestAbstract::~CStateMonsterSquadRest () +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterSquadRestAbstract::reselect_state() +{ + select_state(Random.randI(2) ? eStateSquad_Rest_Idle : eStateSquad_Rest_WalkAroundLeader); +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterSquadRestAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateSquad_Rest_Idle) { + SStateDataAction data; + data.action = ACT_REST; + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + data.time_out = Random.randI(MIN_TIME_IDLE,MAX_TIME_IDLE); + + state->fill_data_with(&data, sizeof(SStateDataAction)); + + return; + } + + if (current_substate == eStateSquad_Rest_WalkAroundLeader) { + SStateDataMoveToPoint data; + CMonsterSquad *squad = monster_squad().get_squad(object); + + if (object->control().path_builder().get_node_in_radius(squad->GetLeader()->ai_location().level_vertex_id(), 8.f, LEADER_RADIUS, FIND_POINT_ATTEMPTS, data.vertex)) { + data.point = ai().level_graph().vertex_position(data.vertex); + } else { + + Fvector dest_pos = random_position(squad->GetLeader()->Position(), LEADER_RADIUS); + if (!object->control().path_builder().restrictions().accessible(dest_pos)) { + data.vertex = object->control().path_builder().restrictions().accessible_nearest(dest_pos, data.point); + } else { + data.point = dest_pos; + data.vertex = u32(-1); + } + } + + data.action.action = ACT_WALK_FWD; + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Calm; + data.completion_dist= 2.f; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPoint)); + + return; + } +} + + diff --git a/src/xrGameLA/ai/monsters/states/monster_state_steal.h b/src/xrGameLA/ai/monsters/states/monster_state_steal.h new file mode 100644 index 000000000..e88d7edaa --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_steal.h @@ -0,0 +1,21 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterSteal : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateMonsterSteal (_Object *obj); + + virtual void initialize (); + virtual void execute (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); + +private: + bool check_conditions (); +}; + +#include "monster_state_steal_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/monster_state_steal_inline.h b/src/xrGameLA/ai/monsters/states/monster_state_steal_inline.h new file mode 100644 index 000000000..88e82e6bd --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/monster_state_steal_inline.h @@ -0,0 +1,86 @@ +#pragma once + +#include "../../../clsid_game.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterStealAbstract CStateMonsterSteal<_Object> + +#define STEAL_MIN_DISTANCE 4.f +#define STEAL_MAX_DISTANCE 15.f +#define STEAL_MAX_PATH_ANGLE PI_DIV_6 + + +TEMPLATE_SPECIALIZATION +CStateMonsterStealAbstract::CStateMonsterSteal(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterStealAbstract::initialize() +{ + inherited::initialize (); + object->path().prepare_builder (); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterStealAbstract::execute() +{ + object->set_action (ACT_STEAL); + object->anim().accel_activate (eAT_Calm); + object->anim().accel_set_braking (false); + object->path().set_target_point (object->EnemyMan.get_enemy_position(), object->EnemyMan.get_enemy_vertex()); + object->path().set_generic_parameters (); + object->set_state_sound (MonsterSound::eMonsterSoundSteal); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterStealAbstract::check_completion() +{ + return (!check_conditions()); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterStealAbstract::check_start_conditions() +{ + return (check_conditions()); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterStealAbstract::check_conditions() +{ + // if i see enemy + if (!object->EnemyMan.see_enemy_now()) return false; + + // This is the only enemy + if (object->EnemyMan.get_enemies_count() > 1) return false; + + // There is extended info about enemy? + if (!object->EnemyMan.get_flags().is(FLAG_ENEMY_STATS_NOT_READY)) { + // Enemy is not moving fast + if (object->EnemyMan.get_flags().is(FLAG_ENEMY_GO_FARTHER_FAST)) return false; + + // Enemy doesn't know about me + if (!object->EnemyMan.get_flags().is(FLAG_ENEMY_DOESNT_KNOW_ABOUT_ME)) return false; + } + + // Don't hear dangerous sounds + if (object->hear_dangerous_sound) return false; + + // Don't get hitted + if (object->HitMemory.is_hit()) return false; + + // Path with minimal deviation + //if (object->control().path_builder().detail().time_path_built() >= time_state_started) { + // if (object->path().get_path_angle() > STEAL_MAX_PATH_ANGLE) return false; + //} + + // check distance to enemy + float dist = object->MeleeChecker.distance_to_enemy(object->EnemyMan.get_enemy()); + if (dist < STEAL_MIN_DISTANCE) return false; + else if (dist > STEAL_MAX_DISTANCE) return false; + + return true; +} diff --git a/src/xrGameLA/ai/monsters/states/state_custom_action.h b/src/xrGameLA/ai/monsters/states/state_custom_action.h new file mode 100644 index 000000000..d4971713d --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_custom_action.h @@ -0,0 +1,19 @@ +#pragma once +#include "../state.h" +#include "state_data.h" + +template +class CStateMonsterCustomAction : public CState<_Object> { + typedef CState<_Object> inherited; + + SStateDataAction data; + +public: + CStateMonsterCustomAction (_Object *obj); + virtual ~CStateMonsterCustomAction (); + + virtual void execute (); + virtual bool check_completion (); +}; + +#include "state_custom_action_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_custom_action_inline.h b/src/xrGameLA/ai/monsters/states/state_custom_action_inline.h new file mode 100644 index 000000000..44c183541 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_custom_action_inline.h @@ -0,0 +1,46 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterCustomActionAbstract CStateMonsterCustomAction<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterCustomActionAbstract::CStateMonsterCustomAction(_Object *obj) : inherited(obj, &data) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterCustomActionAbstract::~CStateMonsterCustomAction() +{ +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterCustomActionAbstract::execute() +{ + object->anim().m_tAction = data.action; + object->anim().SetSpecParams(data.spec_params); + + if (data.sound_type != u32(-1)) { + if (data.sound_delay != u32(-1)) + object->sound().play(data.sound_type, 0,0,data.sound_delay); + else + object->sound().play(data.sound_type); + } + +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterCustomActionAbstract::check_completion() +{ + if (data.time_out) { + if (time_state_started + data.time_out < time()) return true; + } + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterCustomActionAbstract diff --git a/src/xrGameLA/ai/monsters/states/state_custom_action_look.h b/src/xrGameLA/ai/monsters/states/state_custom_action_look.h new file mode 100644 index 000000000..c7a5e1309 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_custom_action_look.h @@ -0,0 +1,19 @@ +#pragma once +#include "../state.h" +#include "state_data.h" + +template +class CStateMonsterCustomActionLook : public CState<_Object> { + typedef CState<_Object> inherited; + + SStateDataActionLook data; + +public: + CStateMonsterCustomActionLook (_Object *obj); + virtual ~CStateMonsterCustomActionLook (); + + virtual void execute (); + virtual bool check_completion (); +}; + +#include "state_custom_action_look_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_custom_action_look_inline.h b/src/xrGameLA/ai/monsters/states/state_custom_action_look_inline.h new file mode 100644 index 000000000..2e786801a --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_custom_action_look_inline.h @@ -0,0 +1,47 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterCustomActionLookAbstract CStateMonsterCustomActionLook<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterCustomActionLookAbstract::CStateMonsterCustomActionLook(_Object *obj) : inherited(obj, &data) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterCustomActionLookAbstract::~CStateMonsterCustomActionLook() +{ +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterCustomActionLookAbstract::execute() +{ + object->set_action (data.action); + object->anim().SetSpecParams(data.spec_params); + object->dir().face_target (data.point); + + if (data.sound_type != u32(-1)) { + if (data.sound_delay != u32(-1)) + object->sound().play(data.sound_type, 0,0,data.sound_delay); + else + object->sound().play(data.sound_type); + } + +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterCustomActionLookAbstract::check_completion() +{ + if (data.time_out) { + if (time_state_started + data.time_out < time()) return true; + } + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterCustomActionLookAbstract diff --git a/src/xrGameLA/ai/monsters/states/state_data.h b/src/xrGameLA/ai/monsters/states/state_data.h new file mode 100644 index 000000000..a01929e6d --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_data.h @@ -0,0 +1,136 @@ +#pragma once + +////////////////////////////////////////////////////////////////////////// +// SStateDataAction +////////////////////////////////////////////////////////////////////////// +struct SStateDataAction { + EAction action; + u32 spec_params; + u32 time_out; + u32 sound_type; + u32 sound_delay; + + SStateDataAction() { + action = ACT_STAND_IDLE; + spec_params = 0; + time_out = 0; + sound_type = u32(-1); + sound_delay = u32(-1); + } +}; + +////////////////////////////////////////////////////////////////////////// +// SStateDataMoveToPoint +////////////////////////////////////////////////////////////////////////// +struct SStateDataMoveToPoint { + Fvector point; + u32 vertex; + + bool accelerated; + bool braking; + u8 accel_type; + + float completion_dist; + + SStateDataAction action; + + SStateDataMoveToPoint() { + point.set (0.f,0.f,0.f); + vertex = u32(-1); + accelerated = false; + completion_dist = 0.f; + } +}; + +////////////////////////////////////////////////////////////////////////// +// SStateDataMoveToPointEx +////////////////////////////////////////////////////////////////////////// +struct SStateDataMoveToPointEx : public SStateDataMoveToPoint { + + u32 time_to_rebuild; //u32(-1) - не перестраивать, 0-по-умолчанию, ... + + SStateDataMoveToPointEx() { + time_to_rebuild = u32(-1); + } +}; + +////////////////////////////////////////////////////////////////////////// +// SStateHideFromPoint +////////////////////////////////////////////////////////////////////////// +struct SStateHideFromPoint { + Fvector point; + + bool accelerated; + bool braking; + u8 accel_type; + + float distance; + + float cover_min_dist; + float cover_max_dist; + float cover_search_radius; + + SStateDataAction action; + + SStateHideFromPoint() { + point.set (0.f,0.f,0.f); + + accelerated = false; + + distance = 1.f; + + cover_min_dist = 10.f; + cover_max_dist = 30.f; + cover_search_radius = 20.f; + } +}; + +////////////////////////////////////////////////////////////////////////// +// SStateDataLookToPoint +////////////////////////////////////////////////////////////////////////// +struct SStateDataLookToPoint { + Fvector point; + u32 face_delay; + SStateDataAction action; + + SStateDataLookToPoint() { + point.set (0.f,0.f,0.f); + face_delay = 0; + } +}; + +////////////////////////////////////////////////////////////////////////// +// SStateDataMoveAroundPoint +////////////////////////////////////////////////////////////////////////// +struct SStateDataMoveAroundPoint { + Fvector point; + u32 vertex; + + float radius; + + bool accelerated; + bool braking; + u8 accel_type; + + SStateDataAction action; + + SStateDataMoveAroundPoint() { + point.set (0.f,0.f,0.f); + vertex = u32(-1); + accelerated = false; + radius = 10.f; + } +}; + +////////////////////////////////////////////////////////////////////////// +// SStateDataActionLook +////////////////////////////////////////////////////////////////////////// +struct SStateDataActionLook : public SStateDataAction { + Fvector point; + + SStateDataActionLook() { + point.set (0.f,0.f,0.f); + } +}; + + diff --git a/src/xrGameLA/ai/monsters/states/state_hide_from_point.h b/src/xrGameLA/ai/monsters/states/state_hide_from_point.h new file mode 100644 index 000000000..5857de6e3 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_hide_from_point.h @@ -0,0 +1,22 @@ +#pragma once +#include "../state.h" +#include "state_data.h" + +template +class CStateMonsterHideFromPoint : public CState<_Object> { + typedef CState<_Object> inherited; + + SStateHideFromPoint data; + +public: + CStateMonsterHideFromPoint (_Object *obj) : inherited(obj, &data){} + virtual ~CStateMonsterHideFromPoint () {} + + virtual void initialize (); + virtual void execute (); + + virtual bool check_completion (); + +}; + +#include "state_hide_from_point_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_hide_from_point_inline.h b/src/xrGameLA/ai/monsters/states/state_hide_from_point_inline.h new file mode 100644 index 000000000..fe67e84d9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_hide_from_point_inline.h @@ -0,0 +1,55 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterHideFromPointAbstract CStateMonsterHideFromPoint<_Object> +#define DIST_TO_PATH_END 1.5f + +TEMPLATE_SPECIALIZATION +void CStateMonsterHideFromPointAbstract::initialize() +{ + inherited::initialize(); + + object->path().prepare_builder(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterHideFromPointAbstract::execute() +{ + object->set_action (data.action.action); + object->anim().SetSpecParams (data.action.spec_params); + + object->path().set_retreat_from_point (data.point); + object->path().set_generic_parameters (); + + if (data.accelerated) { + object->anim().accel_activate (EAccelType(data.accel_type)); + object->anim().accel_set_braking (data.braking); + } + + if (data.action.sound_type != u32(-1)) { + object->set_state_sound(data.action.sound_type, data.action.sound_delay == u32(-1)); + } +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterHideFromPointAbstract::check_completion() +{ + if (data.action.time_out !=0) { + if (time_state_started + data.action.time_out < Device.dwTimeGlobal) + return true; + } + + //if (!fis_zero(data.distance)) { + // if (object->Position().distance_to(data.point) > data.distance) + // return true; + //} + + return false; +} + +#undef DIST_TO_PATH_END +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterHideFromPointAbstract diff --git a/src/xrGameLA/ai/monsters/states/state_hit_object.h b/src/xrGameLA/ai/monsters/states/state_hit_object.h new file mode 100644 index 000000000..d9fda83cf --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_hit_object.h @@ -0,0 +1,21 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterHitObject : public CState<_Object> { + typedef CState<_Object> inherited; + + xr_vector m_nearest_objects; + CPhysicsShellHolder *target; + bool m_hitted; + +public: + CStateMonsterHitObject (_Object *obj) : inherited(obj) {} + + virtual void initialize (); + virtual void execute (); + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "state_hit_object_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_hit_object_inline.h b/src/xrGameLA/ai/monsters/states/state_hit_object_inline.h new file mode 100644 index 000000000..2dca0541b --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_hit_object_inline.h @@ -0,0 +1,91 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterHitObjectAbstract CStateMonsterHitObject<_Object> + +#define TEST_ANGLE PI_DIV_6 +#define TIME_OUT_STATE 1000 +#define TIME_POINTBREAK 500 +#define IMPULSE 20 + +TEMPLATE_SPECIALIZATION +void CStateMonsterHitObjectAbstract::initialize() +{ + inherited::initialize (); + + m_hitted = false; +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterHitObjectAbstract::execute() +{ + object->set_action (ACT_STAND_IDLE); + object->anim().SetSpecParams (ASP_CHECK_CORPSE); + + if (!m_hitted && (time_state_started + TIME_POINTBREAK < Device.dwTimeGlobal)) { + m_hitted = true; + + Fvector dir; + dir.add (Fvector().sub(target->Position(), object->Position()), object->Direction()); + dir.normalize (); + target->m_pPhysicsShell->applyImpulse(dir,IMPULSE * target->m_pPhysicsShell->getMass()); + } +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterHitObjectAbstract::check_start_conditions() +{ + target = 0; + + // получить физ. объекты в радиусе + m_nearest_objects.clear_not_free (); + Level().ObjectSpace.GetNearest (m_nearest_objects,object->Position(), object->Radius() - 0.5f, object()); + + xr_vector::iterator B = m_nearest_objects.begin(); + xr_vector::iterator E = m_nearest_objects.end(); + + for (xr_vector::iterator I = B; I != E; I++) { + CPhysicsShellHolder *obj = smart_cast(*I); + if (!obj || !obj->m_pPhysicsShell) continue; + + // определить дистанцию до врага + Fvector d; + d.sub(obj->Position(),object->Position()); + + // проверка на Field-Of-Hit + float my_h,my_p; + float h,p; + + object->Direction().getHP(my_h,my_p); + d.getHP(h,p); + + float from = angle_normalize(my_h - TEST_ANGLE); + float to = angle_normalize(my_h + TEST_ANGLE); + + if (!is_angle_between(h, from, to)) continue; + + from = angle_normalize(my_p - TEST_ANGLE); + to = angle_normalize(my_p + TEST_ANGLE); + + if (!is_angle_between(p, from, to)) continue; + + target = obj; + return true; + } + + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterHitObjectAbstract::check_completion() +{ + if (time_state_started + TIME_OUT_STATE < Device.dwTimeGlobal) return true; + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterHitObjectAbstract diff --git a/src/xrGameLA/ai/monsters/states/state_look_point.h b/src/xrGameLA/ai/monsters/states/state_look_point.h new file mode 100644 index 000000000..8a3bcb080 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_look_point.h @@ -0,0 +1,21 @@ +#pragma once +#include "../state.h" +#include "state_data.h" + +template +class CStateMonsterLookToPoint : public CState<_Object> { + typedef CState<_Object> inherited; + + SStateDataLookToPoint data; + +public: + CStateMonsterLookToPoint (_Object *obj); + virtual ~CStateMonsterLookToPoint (); + + virtual void initialize (); + virtual void execute (); + + virtual bool check_completion (); +}; + +#include "state_look_point_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_look_point_inline.h b/src/xrGameLA/ai/monsters/states/state_look_point_inline.h new file mode 100644 index 000000000..b64e27e67 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_look_point_inline.h @@ -0,0 +1,51 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterLookToPointAbstract CStateMonsterLookToPoint<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterLookToPointAbstract::CStateMonsterLookToPoint(_Object *obj) : inherited(obj, &data) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterLookToPointAbstract::~CStateMonsterLookToPoint() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterLookToPointAbstract::initialize() +{ + inherited::initialize(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterLookToPointAbstract::execute() +{ + object->anim().m_tAction = data.action.action; + object->anim().SetSpecParams (data.action.spec_params); + object->dir().face_target (data.point, data.face_delay); + + if (data.action.sound_type != u32(-1)) { + if (data.action.sound_delay != u32(-1)) + object->sound().play(data.action.sound_type, 0,0,data.action.sound_delay); + else + object->sound().play(data.action.sound_type); + } + +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterLookToPointAbstract::check_completion() +{ + if (data.action.time_out != 0) { + if (time_state_started + data.action.time_out < Device.dwTimeGlobal) return true; + } else if (!object->control().direction().is_turning()) return true; + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterLookToPointAbstract \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_look_unprotected_area.h b/src/xrGameLA/ai/monsters/states/state_look_unprotected_area.h new file mode 100644 index 000000000..4b4595ab4 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_look_unprotected_area.h @@ -0,0 +1,26 @@ +#pragma once +#include "../state.h" +#include "state_data.h" +#include "../../../ai_object_location.h" +#include "../../../ai_space.h" +#include "../../../level_graph.h" + +template +class CStateMonsterLookToUnprotectedArea : public CState<_Object> { + typedef CState<_Object> inherited; + + SStateDataAction data; + + Fvector target_point; + +public: + CStateMonsterLookToUnprotectedArea (_Object *obj); + virtual ~CStateMonsterLookToUnprotectedArea (); + + virtual void initialize (); + virtual void execute (); + + virtual bool check_completion (); +}; + +#include "state_look_unprotected_area_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_look_unprotected_area_inline.h b/src/xrGameLA/ai/monsters/states/state_look_unprotected_area_inline.h new file mode 100644 index 000000000..dee171175 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_look_unprotected_area_inline.h @@ -0,0 +1,67 @@ +#pragma once + +#include "../../../sound_player.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterLookToUnprotectedAreaAbstract CStateMonsterLookToUnprotectedArea<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterLookToUnprotectedAreaAbstract::CStateMonsterLookToUnprotectedArea(_Object *obj) : inherited(obj, &data) +{ +} + +TEMPLATE_SPECIALIZATION +CStateMonsterLookToUnprotectedAreaAbstract::~CStateMonsterLookToUnprotectedArea() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterLookToUnprotectedAreaAbstract::initialize() +{ + inherited::initialize(); + + Fvector position; + position = object->Position(); + position.y += 0.3f; + + float angle = ai().level_graph().vertex_cover_angle(object->ai_location().level_vertex_id(),PI_DIV_6,std::less()); + + Fvector dir; + dir.set(1.f,0.f,0.f); + dir.setHP(angle+PI, 0.f); + dir.normalize(); + + target_point.mad(object->Position(),dir, 1.f); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterLookToUnprotectedAreaAbstract::execute() +{ + object->anim().m_tAction = data.action; + object->anim().SetSpecParams (data.spec_params); + object->dir().face_target (target_point); + + if (data.sound_type != u32(-1)) { + if (data.sound_delay != u32(-1)) + object->sound().play(data.sound_type, 0,0,data.sound_delay); + else + object->sound().play(data.sound_type); + } + +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterLookToUnprotectedAreaAbstract::check_completion() +{ + if (data.time_out !=0) { + if (time_state_started + data.time_out < Device.dwTimeGlobal) return true; + } else if (!object->control().direction().is_turning()) return true; + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterLookToUnprotectedAreaAbstract diff --git a/src/xrGameLA/ai/monsters/states/state_move_around_point.h b/src/xrGameLA/ai/monsters/states/state_move_around_point.h new file mode 100644 index 000000000..24277d6a2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_move_around_point.h @@ -0,0 +1,22 @@ +#pragma once + +#include "../state.h" +#include "state_data.h" + +template +class CStateMonsterMoveAroundPoint : public CState<_Object> { + typedef CState<_Object> inherited; + + SStateDataMoveAroundPoint data; + +public: + CStateMonsterMoveAroundPoint (_Object *obj) : inherited(obj, &data) {} + virtual ~CStateMonsterMoveAroundPoint () {} + + virtual void initialize (); + virtual void execute (); + + virtual bool check_completion (); +}; + +#include "state_move_to_point_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_move_around_point_inline.h b/src/xrGameLA/ai/monsters/states/state_move_around_point_inline.h new file mode 100644 index 000000000..d0f36b830 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_move_around_point_inline.h @@ -0,0 +1,46 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterMoveAroundPointAbstract CStateMonsterMoveAroundPoint<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterMoveAroundPointAbstract::initialize() +{ + inherited::initialize(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterMoveAroundPointAbstract::execute() +{ + //if (data.vertex != u32(-1)) object->MoveToTarget(data.point, data.vertex); + //else object->MoveToTarget(data.point); + + //object->anim().m_tAction = data.action.action; + //object->anim().SetSpecParams (data.action.spec_params); + + //if (data.accelerated) { + // object->anim().accel_activate (EAccelType(data.accel_type)); + // object->anim().accel_set_braking (data.braking); + //} + + //if (data.action.sound_type != u32(-1)) { + // if (data.action.sound_delay != u32(-1)) + // object->sound().play(data.action.sound_type, 0,0,data.action.sound_delay); + // else + // object->sound().play(data.action.sound_type); + //} +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterMoveAroundPointAbstract::check_completion() +{ +// if (data.time_out) + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterMoveAroundPointAbstract diff --git a/src/xrGameLA/ai/monsters/states/state_move_to_point.h b/src/xrGameLA/ai/monsters/states/state_move_to_point.h new file mode 100644 index 000000000..7ee915551 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_move_to_point.h @@ -0,0 +1,39 @@ +#pragma once +#include "../state.h" +#include "state_data.h" + +template +class CStateMonsterMoveToPoint : public CState<_Object> { + typedef CState<_Object> inherited; + + SStateDataMoveToPoint data; + +public: + CStateMonsterMoveToPoint (_Object *obj) : inherited(obj, &data) {} + virtual ~CStateMonsterMoveToPoint () {} + + virtual void initialize (); + virtual void execute (); + + virtual bool check_completion (); + +}; + + +template +class CStateMonsterMoveToPointEx : public CState<_Object> { + typedef CState<_Object> inherited; + +protected: + + SStateDataMoveToPointEx data; + +public: + CStateMonsterMoveToPointEx (_Object *obj) : inherited(obj, &data) {} + virtual ~CStateMonsterMoveToPointEx () {} + virtual void initialize (); + virtual void execute (); + virtual bool check_completion (); +}; + +#include "state_move_to_point_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/state_move_to_point_inline.h b/src/xrGameLA/ai/monsters/states/state_move_to_point_inline.h new file mode 100644 index 000000000..4bc7b36e9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_move_to_point_inline.h @@ -0,0 +1,97 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterMoveToPointAbstract CStateMonsterMoveToPoint<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterMoveToPointAbstract::initialize() +{ + inherited::initialize(); + object->path().prepare_builder(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterMoveToPointAbstract::execute() +{ + object->set_action (data.action.action); + object->anim().SetSpecParams (data.action.spec_params); + + object->path().set_target_point (data.point,data.vertex); + object->path().set_generic_parameters (); + object->path().set_distance_to_end (data.completion_dist); + + if (data.accelerated) { + object->anim().accel_activate (EAccelType(data.accel_type)); + object->anim().accel_set_braking (data.braking); + } + + if (data.action.sound_type != u32(-1)) { + object->set_state_sound(data.action.sound_type, data.action.sound_delay == u32(-1)); + } +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterMoveToPointAbstract::check_completion() +{ + if (data.action.time_out !=0) { + if (time_state_started + data.action.time_out < Device.dwTimeGlobal) return true; + } + + bool real_path_end = ((fis_zero(data.completion_dist)) ? (data.point.distance_to_xz(object->Position()) < ai().level_graph().header().cell_size()) : true); + if (object->control().path_builder().is_path_end(data.completion_dist) && real_path_end) return true; + + return false; +} + + +////////////////////////////////////////////////////////////////////////// +// CStateMonsterMoveToPointEx with path rebuild options +////////////////////////////////////////////////////////////////////////// + +#define CStateMonsterMoveToPointExAbstract CStateMonsterMoveToPointEx<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterMoveToPointExAbstract::initialize() +{ + inherited::initialize(); + object->path().prepare_builder(); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterMoveToPointExAbstract::execute() +{ + object->set_action (data.action.action); + object->anim().SetSpecParams (data.action.spec_params); + + object->path().set_target_point (data.point,data.vertex); + object->path().set_rebuild_time (data.time_to_rebuild); + object->path().set_distance_to_end (data.completion_dist); + object->path().set_use_covers (); + object->path().set_cover_params (5.f, 30.f, 1.f, 30.f); + + if (data.accelerated) { + object->anim().accel_activate (EAccelType(data.accel_type)); + object->anim().accel_set_braking (data.braking); + } + + if (data.action.sound_type != u32(-1)) { + object->set_state_sound(data.action.sound_type, data.action.sound_delay == u32(-1)); + } +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterMoveToPointExAbstract::check_completion() +{ + if (data.action.time_out !=0) { + if (time_state_started + data.action.time_out < Device.dwTimeGlobal) return true; + } + + bool real_path_end = ((fis_zero(data.completion_dist)) ? (data.point.distance_to_xz(object->Position()) < ai().level_graph().header().cell_size()) : true); + if (object->control().path_builder().is_path_end(data.completion_dist) && real_path_end) return true; + + return false; +} + diff --git a/src/xrGameLA/ai/monsters/states/state_move_to_restrictor.h b/src/xrGameLA/ai/monsters/states/state_move_to_restrictor.h new file mode 100644 index 000000000..02f595957 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_move_to_restrictor.h @@ -0,0 +1,19 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterMoveToRestrictor : public CState<_Object> { + typedef CState<_Object> inherited; + +public: + CStateMonsterMoveToRestrictor (_Object *obj) : inherited(obj) {} + virtual ~CStateMonsterMoveToRestrictor () {} + + virtual void initialize (); + virtual void execute (); + + virtual bool check_start_conditions (); + virtual bool check_completion (); +}; + +#include "state_move_to_restrictor_inline.h" diff --git a/src/xrGameLA/ai/monsters/states/state_move_to_restrictor_inline.h b/src/xrGameLA/ai/monsters/states/state_move_to_restrictor_inline.h new file mode 100644 index 000000000..c36f9b8ae --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_move_to_restrictor_inline.h @@ -0,0 +1,43 @@ +#pragma once + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterMoveToRestrictorAbstract CStateMonsterMoveToRestrictor<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterMoveToRestrictorAbstract::initialize() +{ + inherited::initialize(); + object->path().prepare_builder(); + + Fvector position; + u32 node = object->control().path_builder().restrictions().accessible_nearest(object->Position(), position); + object->path().set_target_point (ai().level_graph().vertex_position(node), node); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterMoveToRestrictorAbstract::execute() +{ + object->set_action (ACT_RUN); + + object->anim().accel_activate (EAccelType(eAT_Aggressive)); + object->anim().accel_set_braking (true); + object->set_state_sound (MonsterSound::eMonsterSoundIdle); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterMoveToRestrictorAbstract::check_start_conditions() +{ + return (!object->control().path_builder().accessible(object->Position())); +} + +TEMPLATE_SPECIALIZATION +bool CStateMonsterMoveToRestrictorAbstract::check_completion() +{ + return (object->control().path_builder().accessible(object->Position())); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterMoveToRestrictorAbstract \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_test_look_actor.h b/src/xrGameLA/ai/monsters/states/state_test_look_actor.h new file mode 100644 index 000000000..3f67663d9 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_test_look_actor.h @@ -0,0 +1,30 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterLookActor : public CState<_Object> { + typedef CState<_Object> inherited; +public: + CStateMonsterLookActor (_Object *obj) : inherited(obj) {} + virtual void execute (); +}; + + +template +class CStateMonsterTurnAwayFromActor : public CState<_Object> { + typedef CState<_Object> inherited; +public: + CStateMonsterTurnAwayFromActor (_Object *obj) : inherited(obj) {} + virtual void execute (); +}; + + +template +class CStateMonstertTestIdle : public CState<_Object> { + typedef CState<_Object> inherited; +public: + CStateMonstertTestIdle (_Object *obj) : inherited(obj) {} + virtual void execute (); +}; + +#include "state_test_look_actor_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_test_look_actor_inline.h b/src/xrGameLA/ai/monsters/states/state_test_look_actor_inline.h new file mode 100644 index 000000000..b5d073a56 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_test_look_actor_inline.h @@ -0,0 +1,49 @@ +#pragma once + +#include "../../../level.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterLookActorAbstract CStateMonsterLookActor<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterLookActorAbstract::execute() +{ + object->set_action (ACT_STAND_IDLE); + object->dir().face_target (Level().CurrentEntity()->Position(), 1200); +} + + + +#define CStateMonsterTurnAwayFromActorAbstract CStateMonsterTurnAwayFromActor<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonsterTurnAwayFromActorAbstract::execute() +{ + Fvector point; + Fvector dir; + dir.sub (object->Position(), Level().CurrentEntity()->Position()); + dir.normalize (); + point.mad (object->Position(), dir, 2.f); + + object->set_action (ACT_STAND_IDLE); + object->dir().face_target (point, 1200); +} + + + +#define CStateMonstertTestIdleAbstract CStateMonstertTestIdle<_Object> + +TEMPLATE_SPECIALIZATION +void CStateMonstertTestIdleAbstract::execute() +{ + object->set_action (ACT_STAND_IDLE); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterLookActorAbstract +#undef CStateMonsterTurnAwayFromActorAbstract +#undef CStateMonstertTestIdleAbstract + diff --git a/src/xrGameLA/ai/monsters/states/state_test_state.h b/src/xrGameLA/ai/monsters/states/state_test_state.h new file mode 100644 index 000000000..8f8234e50 --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_test_state.h @@ -0,0 +1,31 @@ +#pragma once +#include "../state.h" + +template +class CStateMonsterTestState : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object> *state_ptr; + +public: + CStateMonsterTestState (_Object *obj); + virtual void reselect_state (); + virtual void setup_substates (); +}; + +template +class CStateMonsterTestCover : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object> *state_ptr; + + u32 m_last_node; + +public: + CStateMonsterTestCover (_Object *obj); + virtual void initialize (); + virtual void check_force_state (); + virtual void reselect_state (); + virtual void setup_substates (); + +}; + +#include "state_test_state_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/states/state_test_state_inline.h b/src/xrGameLA/ai/monsters/states/state_test_state_inline.h new file mode 100644 index 000000000..3c66a9dab --- /dev/null +++ b/src/xrGameLA/ai/monsters/states/state_test_state_inline.h @@ -0,0 +1,137 @@ +#pragma once + +#include "../../../level.h" +#include "state_move_to_point.h" +#include "state_custom_action.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateMonsterTestStateAbstract CStateMonsterTestState<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterTestStateAbstract::CStateMonsterTestState(_Object *obj) : inherited(obj) +{ + add_state(eStateCustom,new CStateMonsterMoveToPointEx<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterTestStateAbstract::reselect_state() +{ + select_state(eStateCustom); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterTestStateAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateCustom) { + SStateDataMoveToPointEx data; + + Fvector dest_pos = Level().CurrentEntity()->Position(); + dest_pos = random_position(dest_pos, 20.f); + + if (!object->control().path_builder().restrictions().accessible(dest_pos)) { + data.vertex = object->control().path_builder().restrictions().accessible_nearest(dest_pos, data.point); + } else { + data.point = dest_pos; + data.vertex = u32(-1); + } + + data.action.action = ACT_RUN; + data.action.time_out = 20000; + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Calm; + data.completion_dist = 3.f; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.time_to_rebuild = 0; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + + return; + } +} + + +#define CStateMonsterTestCoverAbstract CStateMonsterTestCover<_Object> + +TEMPLATE_SPECIALIZATION +CStateMonsterTestCoverAbstract::CStateMonsterTestCover(_Object *obj) : inherited(obj) +{ + add_state(eStateAttack_HideInCover, new CStateMonsterMoveToPointEx<_Object>(obj)); + add_state(eStateAttack_CampInCover, new CStateMonsterCustomAction<_Object>(obj)); +} +TEMPLATE_SPECIALIZATION +void CStateMonsterTestCoverAbstract::initialize() +{ + inherited::initialize(); + + m_last_node = object->m_target_node; +} + + +TEMPLATE_SPECIALIZATION +void CStateMonsterTestCoverAbstract::check_force_state() +{ + if (m_last_node != object->m_target_node) { + m_last_node = object->m_target_node; + current_substate = u32(-1); + return; + } + + if (current_substate == eStateAttack_CampInCover) + if (object->ai_location().level_vertex_id() != m_last_node) + current_substate = u32(-1); +} +TEMPLATE_SPECIALIZATION +void CStateMonsterTestCoverAbstract::reselect_state() +{ + if (object->ai_location().level_vertex_id() != m_last_node) + select_state(eStateAttack_HideInCover); + else + select_state(eStateAttack_CampInCover); +} + +TEMPLATE_SPECIALIZATION +void CStateMonsterTestCoverAbstract::setup_substates() +{ + state_ptr state = get_state_current(); + + if (current_substate == eStateAttack_HideInCover) { + SStateDataMoveToPointEx data; + data.vertex = m_last_node; + data.point = ai().level_graph().vertex_position(data.vertex); + data.action.action = ACT_RUN; + data.action.time_out = 200000; + data.accelerated = true; + data.braking = false; + data.accel_type = eAT_Aggressive; + data.completion_dist = 0.f; + data.action.sound_type = MonsterSound::eMonsterSoundIdle; + data.action.sound_delay = object->db().m_dwIdleSndDelay; + data.time_to_rebuild = 0; + + state->fill_data_with(&data, sizeof(SStateDataMoveToPointEx)); + return; + } + + if (current_substate == eStateAttack_CampInCover) { + SStateDataAction data; + data.action = ACT_STAND_IDLE; + data.sound_type = MonsterSound::eMonsterSoundIdle; + data.sound_delay = object->db().m_dwIdleSndDelay; + + state->fill_data_with(&data, sizeof(SStateDataAction)); + return; + } + +} + + +#undef TEMPLATE_SPECIALIZATION +#undef CStateMonsterTestStateAbstract +#undef CStateMonsterTestCoverAbstract diff --git a/src/xrGameLA/ai/monsters/swampbeast/swampbeast.cpp b/src/xrGameLA/ai/monsters/swampbeast/swampbeast.cpp new file mode 100644 index 000000000..a3425967e --- /dev/null +++ b/src/xrGameLA/ai/monsters/swampbeast/swampbeast.cpp @@ -0,0 +1,157 @@ +#include "stdafx.h" +#include "swampbeast.h" +#include "../../../ai_space.h" +#include "swampbeast_state_manager.h" +#include "../monster_velocity_space.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" + + +CAI_SwampBeast::CAI_SwampBeast() +{ + StateMan = new CStateManagerSwampBeast(this); + + m_fEyeShiftYaw = PI_DIV_6; + + CControlled::init_external(this); +} + +CAI_SwampBeast::~CAI_SwampBeast() +{ + xr_delete(StateMan); +} + +BOOL CAI_SwampBeast::net_Spawn (CSE_Abstract* DC) +{ + if (!inherited::net_Spawn(DC)) + return(FALSE); + + return TRUE; +} + +void CAI_SwampBeast::Load(LPCSTR section) +{ + inherited::Load(section); + + anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); + anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + // define animation set + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND); + + anim().AddAnim(eAnimLieIdle, "lie_idle_", -1, &velocity_none, PS_LIE); + anim().AddAnim(eAnimSleep, "lie_idle_", -1, &velocity_none, PS_LIE); + + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND); + + anim().AddAnim(eAnimRun, "stand_run_", -1, &velocity_run, PS_STAND); + anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND); + + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimAttackFromBack, "stand_attack_back_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimCheckCorpse, "stand_eat_", 1, &velocity_none, PS_STAND); + + anim().AddAnim(eAnimEat, "stand_eat_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimDie, "stand_die_", -1, &velocity_none, PS_STAND); + + anim().AddAnim(eAnimStandLieDown, "stand_lie_down_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimLieStandUp, "lie_stand_up_", -1, &velocity_none, PS_LIE); + + anim().AddAnim(eAnimSteal, "stand_crawl_", -1, &velocity_steal, PS_STAND); + anim().AddAnim(eAnimDragCorpse, "stand_drag_", -1, &velocity_drag, PS_STAND); + + anim().AddAnim(eAnimScared, "stand_scared_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimThreaten, "stand_threaten_", -1, &velocity_none, PS_STAND); + + // define transitions + anim().AddTransition(PS_STAND, PS_LIE, eAnimStandLieDown, false); + anim().AddTransition(PS_LIE, PS_STAND, eAnimLieStandUp, false, SKIP_IF_AGGRESSIVE); + + // define links from Action to animations + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimLieIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkBkwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimEat); + anim().LinkAction(ACT_SLEEP, eAnimSleep); + anim().LinkAction(ACT_REST, eAnimLieIdle); + anim().LinkAction(ACT_DRAG, eAnimDragCorpse); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimSteal); + anim().LinkAction(ACT_LOOK_AROUND, eAnimScared); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +// возвращает true, если после выполнения этой функции необходимо прервать обработку +// т.е. если активирована последовательность +void CAI_SwampBeast::CheckSpecParams(u32 spec_params) +{ + if ((spec_params & ASP_DRAG_CORPSE) == ASP_DRAG_CORPSE) anim().SetCurAnim(eAnimDragCorpse); + + if ((spec_params & ASP_CHECK_CORPSE) == ASP_CHECK_CORPSE) { + com_man().seq_run(anim().get_motion_id(eAnimCheckCorpse)); } + + if ((spec_params & ASP_BACK_ATTACK) == ASP_BACK_ATTACK) { + com_man().seq_run(anim().get_motion_id(eAnimAttackFromBack)); + } + + if ((spec_params & ASP_THREATEN) == ASP_THREATEN) anim().SetCurAnim(eAnimThreaten); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////// +// Функция ConeSphereIntersection +// Пересечение конуса (не ограниченного) со сферой +// Необходима для определения пересечения копыта плоти с баунд-сферой крысы +// Параметры: ConeVertex - вершина конуса, ConeAngle - угол конуса (между поверхностью и высотой) +// ConeDir - направление конуса, SphereCenter - центр сферы, SphereRadius - радиус сферы +bool CAI_SwampBeast::ConeSphereIntersection(Fvector ConeVertex, float ConeAngle, Fvector ConeDir, Fvector SphereCenter, float SphereRadius) +{ + float fInvSin = 1.0f/_sin(ConeAngle); + float fCosSqr = _cos(ConeAngle)*_cos(ConeAngle); + + + Fvector kCmV; kCmV.sub(SphereCenter,ConeVertex); + Fvector kD = kCmV; + Fvector tempV = ConeDir; + tempV.mul (SphereRadius* fInvSin); + kD.add (tempV); + + float fDSqrLen = kD.square_magnitude(); + float fE = kD.dotproduct(ConeDir); + if ( fE > 0.0f && fE*fE >= fDSqrLen*fCosSqr ) { + + float fSinSqr = _sin(ConeAngle)*_sin(ConeAngle); + + fDSqrLen = kCmV.square_magnitude(); + fE = -kCmV.dotproduct(ConeDir); + if ( fE > 0.0f && fE*fE >= fDSqrLen*fSinSqr ) { + float fRSqr = SphereRadius*SphereRadius; + return fDSqrLen <= fRSqr; + } else return true; + } + + return false; +} diff --git a/src/xrGameLA/ai/monsters/swampbeast/swampbeast.h b/src/xrGameLA/ai/monsters/swampbeast/swampbeast.h new file mode 100644 index 000000000..b7c05810c --- /dev/null +++ b/src/xrGameLA/ai/monsters/swampbeast/swampbeast.h @@ -0,0 +1,33 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../controlled_entity.h" +#include "../../../script_export_space.h" + +class CAI_SwampBeast : public CBaseMonster, + public CControlledEntity { + + typedef CBaseMonster inherited; + typedef CControlledEntity CControlled; + +public: + CAI_SwampBeast (); + virtual ~CAI_SwampBeast (); + + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + + virtual void CheckSpecParams (u32 spec_params); + + virtual bool ability_can_drag () {return true;} + +private: + bool ConeSphereIntersection (Fvector ConeVertex, float ConeAngle, Fvector ConeDir, + Fvector SphereCenter, float SphereRadius); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CAI_SwampBeast) +#undef script_type_list +#define script_type_list save_type_list(CAI_SwampBeast) + diff --git a/src/xrGameLA/ai/monsters/swampbeast/swampbeast_script.cpp b/src/xrGameLA/ai/monsters/swampbeast/swampbeast_script.cpp new file mode 100644 index 000000000..f5a63a772 --- /dev/null +++ b/src/xrGameLA/ai/monsters/swampbeast/swampbeast_script.cpp @@ -0,0 +1,15 @@ +#include "pch_script.h" +#include "stdafx.h" +#include "swampbeast.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CAI_SwampBeast::script_register(lua_State *L) +{ + module(L) + [ + class_("CAI_SwampBeast") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/swampbeast/swampbeast_state_manager.cpp b/src/xrGameLA/ai/monsters/swampbeast/swampbeast_state_manager.cpp new file mode 100644 index 000000000..afd6a6fef --- /dev/null +++ b/src/xrGameLA/ai/monsters/swampbeast/swampbeast_state_manager.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "swampbeast.h" +#include "swampbeast_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../states/monster_state_controlled.h" +#include "../states/monster_state_help_sound.h" + +CStateManagerSwampBeast::CStateManagerSwampBeast(CAI_SwampBeast *monster) : inherited(monster) +{ + add_state(eStateRest, new CStateMonsterRest(monster)); + add_state(eStatePanic, new CStateMonsterPanic(monster)); + add_state(eStateAttack, new CStateMonsterAttack(monster)); + add_state(eStateEat, new CStateMonsterEat(monster)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(monster)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(monster)); + add_state(eStateHitted, new CStateMonsterHitted(monster)); + add_state(eStateControlled, new CStateMonsterControlled(monster)); + add_state(eStateHearHelpSound, new CStateMonsterHearHelpSound(monster)); + +} + +void CStateManagerSwampBeast::execute() +{ + u32 state_id = u32(-1); + + if (!object->is_under_control()) { + + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStatePanic; break; + case eWeak: state_id = eStateAttack; break; + } + + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (check_state(eStateHearHelpSound)) { + state_id = eStateHearHelpSound; + } else if (object->hear_interesting_sound) { + state_id = eStateHearInterestingSound; + } else if (object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + + } + } else state_id = eStateControlled; + + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} diff --git a/src/xrGameLA/ai/monsters/swampbeast/swampbeast_state_manager.h b/src/xrGameLA/ai/monsters/swampbeast/swampbeast_state_manager.h new file mode 100644 index 000000000..fd8a066a1 --- /dev/null +++ b/src/xrGameLA/ai/monsters/swampbeast/swampbeast_state_manager.h @@ -0,0 +1,13 @@ +#pragma once +#include "../monster_state_manager.h" + +class CAI_SwampBeast; + +class CStateManagerSwampBeast : public CMonsterStateManager { + typedef CMonsterStateManager inherited; + +public: + + CStateManagerSwampBeast (CAI_SwampBeast *monster); + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/telekinesis.cpp b/src/xrGameLA/ai/monsters/telekinesis.cpp new file mode 100644 index 000000000..eb7273d9c --- /dev/null +++ b/src/xrGameLA/ai/monsters/telekinesis.cpp @@ -0,0 +1,230 @@ +#include "stdafx.h" +#include "telekinesis.h" +#include "../../entity_alive.h" +#include "../../PhysicsShell.h" + +CTelekinesis::CTelekinesis() +{ + active=false; +} +CTelekinesis::~CTelekinesis() +{ + for (TELE_OBJECTS_IT it = objects.begin(); it != objects.end(); ++it) { + (*it)->release(); + xr_delete(*it); + } +} + +CTelekineticObject* CTelekinesis::activate (CPhysicsShellHolder *obj, float strength, float height, u32 max_time_keep, bool rot) +{ + active = true; + + CTelekineticObject* tele_object=alloc_tele_object(); + if (!tele_object->init(this,obj,strength, height,max_time_keep,rot)) { + xr_delete(tele_object); + return 0; + } + + // добавить объект + objects.push_back(tele_object); + + if (!objects.empty()) CPHUpdateObject::Activate(); + return tele_object; +} + +void CTelekinesis::clear() +{ + objects.clear (); +} +void CTelekinesis::deactivate() +{ + active = false; + + // отпустить все объекты + // + for (TELE_OBJECTS_IT it = objects.begin(); it != objects.end(); ++it) { + (*it)->release(); + xr_delete(*it); + } + + clear(); + CPHUpdateObject::Deactivate(); +} + +void CTelekinesis::clear_deactivate() +{ + active = false; + + // отпустить все объекты + // + for (u32 i = 0; i < objects.size(); i++) + { + objects[i]->switch_state(TS_None); + xr_delete(objects[i]); + } + clear(); + CPHUpdateObject::Deactivate(); +} + +struct SFindPred +{ + CPhysicsShellHolder *obj; + SFindPred(CPhysicsShellHolder *aobj){obj=aobj;} + bool operator () (CTelekineticObject * tele_obj) + { + return tele_obj->get_object()==obj; + } +}; +void CTelekinesis::deactivate(CPhysicsShellHolder *obj) +{ + // найти объект + + TELE_OBJECTS_IT it = std::find_if(objects.begin(), objects.end(), SFindPred(obj)); + if (it == objects.end()) return; + + // отпустить объект + (*it)->release(); + + //remove from list, delete... + remove_object(it); +} + +void CTelekinesis::remove_object (CPhysicsShellHolder *obj) +{ + // найти объект + TELE_OBJECTS_IT it = std::find_if(objects.begin(), objects.end(), SFindPred(obj)); + if (it == objects.end()) return; + //remove from list, delete... + remove_object(it); +} + +void CTelekinesis::remove_object (TELE_OBJECTS_IT it) +{ + // release memory + xr_delete(*it); + + // удалить + objects.erase(it); + + // проверить на полную деактивацию + if (objects.empty()) { + clear(); + CPHUpdateObject::Deactivate(); + active = false; + } +} +void CTelekinesis::fire_all(const Fvector &target) +{ + if (!active) return; + + for (u32 i = 0; i < objects.size(); i++) objects[i]->fire(target,1.f); + + deactivate(); +} + +// бросить объект 'obj' в позицию 'target' с учетом коэф силы +void CTelekinesis::fire(CPhysicsShellHolder *obj, const Fvector &target, float power) +{ + // найти объект + + TELE_OBJECTS_IT it = std::find_if(objects.begin(), objects.end(),SFindPred(obj)); + if (it == objects.end()) return; + + // бросить объект + (*it)->fire(target,power); +} + +void CTelekinesis::fire_t(CPhysicsShellHolder *obj, const Fvector &target, float time) +{ + TELE_OBJECTS_IT it = std::find_if(objects.begin(), objects.end(),SFindPred(obj)); + if (it == objects.end()) return; + + // бросить объект + (*it)->fire_t(target,time); +} + +bool CTelekinesis::is_active_object(CPhysicsShellHolder *obj) +{ + // найти объект + TELE_OBJECTS_IT it = std::find_if(objects.begin(), objects.end(), SFindPred(obj)); + if (it == objects.end()) return false; + + return true; +} + +void CTelekinesis::schedule_update() +{ + if (!active) return; + + // обновить состояние объектов + for (u32 i = 0; i < objects.size(); i++) { + + CTelekineticObject *cur_obj = objects[i]; + cur_obj->update_state(); + if(cur_obj->is_released()) remove_object(objects.begin()+i); + } +} + +void CTelekinesis::PhDataUpdate(float step) +{ + if (!active) return; + + for (u32 i = 0; i < objects.size(); i++) { + switch (objects[i]->get_state()) { + case TS_Raise: objects[i]->raise(step); break; + case TS_Keep: objects[i]->keep(); break; + case TS_None: break; + } + } +} + +static bool RemovePred(CTelekineticObject *tele_object) +{ + return (!tele_object->get_object() || + tele_object->get_object()->getDestroy() || + !tele_object->get_object()->PPhysicsShell() || + !tele_object->get_object()->PPhysicsShell()->isActive()); +} + +void CTelekinesis::clear_notrelevant() +{ + //убрать все объеты со старыми параметрами + objects.erase ( + std::remove_if( + objects.begin(), + objects.end(), + &RemovePred + ), + objects.end() + ); +} + +void CTelekinesis::PhTune(float step) +{ + if (!active) return; + clear_notrelevant(); + for (u32 i = 0; i < objects.size(); i++) { + switch (objects[i]->get_state()) { + case TS_Raise: + case TS_Keep: objects[i]->enable(); + case TS_None: break; + } + } +} + +u32 CTelekinesis::get_objects_count() +{ + u32 count = 0; + for (u32 i=0;iget_state(); + if ((state == TS_Raise) || (state == TS_Keep)) count++; + } + + return count; +} + +// объект был удален - удалить все связи на объект +void CTelekinesis::remove_links(CObject *O) +{ + remove_object(smart_cast(O)); +} diff --git a/src/xrGameLA/ai/monsters/telekinesis.h b/src/xrGameLA/ai/monsters/telekinesis.h new file mode 100644 index 000000000..f87d90a59 --- /dev/null +++ b/src/xrGameLA/ai/monsters/telekinesis.h @@ -0,0 +1,82 @@ +#pragma once + +#include "telekinetic_object.h" +#include "../../PHObject.h" + + + + +class CTelekinesis : public CPHUpdateObject { + +protected: + DEFINE_VECTOR(CTelekineticObject*,TELE_OBJECTS,TELE_OBJECTS_IT); + TELE_OBJECTS objects; + xr_vector m_nearest; + bool active; + +public: + CTelekinesis (); + virtual ~CTelekinesis (); + + // allocates relevant TelekineticObject + + + // активировать объект +virtual CTelekineticObject* activate(CPhysicsShellHolder *obj, float strength, float height, u32 max_time_keep, bool rot = true); + + // деактивировать все объекты + void deactivate (); + + //clear objects (does not call release, but call switch to TS_None) + void clear_deactivate (); + // clear +virtual void clear (); +virtual void clear_notrelevant (); + // деактивировать объект + void deactivate (CPhysicsShellHolder *obj); + void remove_object (TELE_OBJECTS_IT it); + void remove_object (CPhysicsShellHolder *obj); + // бросить все объекты в позицию 'target' + void fire_all (const Fvector &target); + + // бросить объект 'obj' в позицию 'target' с учетом коэф силы + void fire (CPhysicsShellHolder *obj, const Fvector &target, float power); + + // бросить объект 'obj' в позицию 'target' с учетом коэф силы + void fire_t (CPhysicsShellHolder *obj, const Fvector &target, float time); + + + // вернуть активность телекинеза + bool is_active () {return active;} + + // вернуть активность объекта + bool is_active_object (CPhysicsShellHolder *obj); + + // вернуть количество контролируемых объектов (в состоянии TS_Raise & TS_Keep) + u32 get_objects_count (); + + // вернуть количество контролируемых объектов (всех) + u32 get_objects_total_count() {return objects.size();} + + + // вернуть объект по индексу в массиве + // a copy of the object! +CTelekineticObject get_object_by_index (u32 index) {VERIFY(objects.size() > index); return *objects[index];} + + // обновить состоняие на shedule_Update + void schedule_update (); + + // объект был удален - удалить все связи на объект + void remove_links (CObject *O); + +protected: + virtual CTelekineticObject* alloc_tele_object(){return new CTelekineticObject();} +private: + + // обновление на шагах физики + virtual void PhDataUpdate (float step); + virtual void PhTune (float step); + + +}; + diff --git a/src/xrGameLA/ai/monsters/telekinesis_inline.h b/src/xrGameLA/ai/monsters/telekinesis_inline.h new file mode 100644 index 000000000..5f199e02d --- /dev/null +++ b/src/xrGameLA/ai/monsters/telekinesis_inline.h @@ -0,0 +1,143 @@ +#pragma once + +template +CTelekinesis<_Object>::CTelekinesis() +{ + active = false; +} + +template +CTelekinesis<_Object>::~CTelekinesis() +{ + +} + +template +void CTelekinesis<_Object>::InitExtern(_Object *pO, float s, float h, u32 keep_time) +{ + control_object = pO; + strength = s; + height = h; + max_time_keep = keep_time; +} + +template +void CTelekinesis<_Object>::Activate() +{ + if (active) return; + VERIFY(objects.empty()); + + active = true; + + // получить список объектов + m_nearest.clear_not_free (); + Level().ObjectSpace.GetNearest (m_nearest,control_object->Position(),10.f); + //xr_vector &m_nearest = Level().ObjectSpace.q_nearest; + + // все объекты внести в список + for (u32 i = 0; i < m_nearest.size(); i++) { + + CGameObject *obj = smart_cast(m_nearest[i]); + if (!obj || !obj->m_pPhysicsShell) continue; + + // отключить гравитацию + obj->m_pPhysicsShell->set_ApplyByGravity(FALSE); + + CTelekineticObject tele_object; + + tele_object.init(obj,height); + // добавить объект + objects.push_back(tele_object); + } + + if (!objects.empty()) CPHUpdateObject::Activate(); +} + +template +void CTelekinesis<_Object>::Deactivate() +{ + active = false; + + for (u32 i = 0; i < objects.size(); i++) { + objects[i].release(); + } + + objects.clear (); + + CPHUpdateObject::Deactivate(); +} + + + +template +void CTelekinesis<_Object>::Throw(const Fvector &target) +{ + if (!active) return; + + for (u32 i = 0; i < objects.size(); i++) { + objects[i].fire(target); + } + + Deactivate(); +} + + +template +void CTelekinesis<_Object>::UpdateSched() +{ + if (!active) return; + + // обновить состояние объектов + for (u32 i = 0; i < objects.size(); i++) { + CTelekineticObject *cur_obj = &objects[i]; + switch (cur_obj->get_state()) { + case TS_Raise: + if (cur_obj->check_height()) cur_obj->prepare_keep();// начать удержание предмета + break; + case TS_Keep: + if (cur_obj->time_keep_elapsed()) { + cur_obj->release(); + + // удалить объект из массива + if (objects.size() > 1) { + if (i != (objects.size()-1)) objects[i] = objects.back(); + objects.pop_back(); + } else { + objects.clear(); + active = false; + } + } + break; + case TS_None: continue; + } + } +} + +template +void CTelekinesis<_Object>::PhDataUpdate(dReal step) +{ + if (!active) return; + + for (u32 i = 0; i < objects.size(); i++) { + switch (objects[i].get_state()) { + case TS_Raise: objects[i].raise(strength * step); break; + case TS_Keep: objects[i].keep(); break; + case TS_None: break; + } + } +} + + +template +void CTelekinesis<_Object>::PhTune(dReal step) +{ + for (u32 i = 0; i < objects.size(); i++) { + switch (objects[i].get_state()) { + case TS_Raise: + case TS_Keep: objects[i].get_object()->m_pPhysicsShell->Enable(); + case TS_None: break; + } + } +} + + diff --git a/src/xrGameLA/ai/monsters/telekinetic_object.cpp b/src/xrGameLA/ai/monsters/telekinetic_object.cpp new file mode 100644 index 000000000..087f3aa49 --- /dev/null +++ b/src/xrGameLA/ai/monsters/telekinetic_object.cpp @@ -0,0 +1,284 @@ +#include "stdafx.h" +#include "../../physicsshellholder.h" +#include "telekinetic_object.h" +#include "../../PhysicsShell.h" +#include "../../MathUtils.h" +#include "../../PHInterpolation.h" +#include "../../PHElement.h" +#include "../../level.h" +#include "../../gameobject.h" + +#define KEEP_IMPULSE_UPDATE 200 +#define FIRE_TIME 3000 +#define RAISE_MAX_TIME 5000 + +CTelekineticObject::CTelekineticObject() +{ + state = TS_None; + object = 0; + telekinesis = 0; + m_rotate = false; +} + +CTelekineticObject::~CTelekineticObject() +{ + +} + + + +bool CTelekineticObject::init(CTelekinesis* tele,CPhysicsShellHolder *obj, float s, float h, u32 ttk, bool rot) +{ + if(!can_activate(obj)) return false; + + //state = TS_Raise; + switch_state(TS_Raise); + object = obj; + + target_height = obj->Position().y + h; + + time_keep_started = 0; + time_keep_updated = 0; + time_to_keep = ttk; + + strength = s; + + time_fire_started = 0; + //time_raise_started = Device.dwTimeGlobal; + + m_rotate = rot; + + if(object->m_pPhysicsShell) + object->m_pPhysicsShell->set_ApplyByGravity(FALSE); + + + return true; +} + +void CTelekineticObject::set_sound(const ref_sound &snd_hold, const ref_sound &snd_throw) +{ + sound_hold.clone (snd_hold,st_Effect,sg_SourceType); + sound_throw.clone (snd_throw,st_Effect,sg_SourceType); +} + + +void CTelekineticObject::raise_update() +{ + if (check_height() || check_raise_time_out()) prepare_keep();// начать удержание предмета + //else if (check_raise_time_out()) release(); + else { + if (m_rotate) rotate(); + } +} +void CTelekineticObject::keep_update() +{ + if (time_keep_elapsed())release(); +} +void CTelekineticObject::fire_update() +{ + if (time_fire_elapsed())release(); +} +void CTelekineticObject::update_state() +{ + switch (get_state()) { + case TS_Raise: raise_update(); break; + case TS_Keep: keep_update(); break; + case TS_Fire: fire_update(); break; + case TS_None: break; + } +} + +void CTelekineticObject::switch_state(ETelekineticState new_state) +{ + u32 time=Device.dwTimeGlobal; + + switch (new_state) { + case TS_Raise: time_raise_started = time; break; + case TS_Keep: time_keep_started = time; break; + case TS_Fire: time_fire_started = time; break; + case TS_None: break; + } + state=new_state; +} +void CTelekineticObject::raise(float step) +{ + if (!object || !object->m_pPhysicsShell || !object->m_pPhysicsShell->isActive()) return; + + step *= strength; + + Fvector dir; + dir.set(0.f,1.0f,0.f); + + float elem_size = float(object->m_pPhysicsShell->Elements().size()); + dir.mul(elem_size*elem_size*strength); + + if (OnServer()) (object->m_pPhysicsShell->Elements()[0])->applyGravityAccel(dir); + + + update_hold_sound (); +} + +void CTelekineticObject::prepare_keep() +{ + //time_keep_started = Device.dwTimeGlobal; + //state = TS_Keep; + switch_state(TS_Keep); + time_keep_updated = 0; +} + +bool CTelekineticObject::time_keep_elapsed() +{ + if (time_keep_started + time_to_keep < Device.dwTimeGlobal) return true; + return false; +} + +bool CTelekineticObject::time_fire_elapsed() +{ + if (time_fire_started + FIRE_TIME < Device.dwTimeGlobal) return true; + return false; +} + + +void CTelekineticObject::keep() +{ + // проверить время последнего обновления + //if (time_keep_updated + KEEP_IMPULSE_UPDATE > Device.dwTimeGlobal) return; + + if (!object || !object->m_pPhysicsShell || !object->m_pPhysicsShell->isActive()) return; + + // проверить высоту + float cur_h = object->Position().y; + + // установить dir в соответствие с текущей высотой + Fvector dir; + if (cur_h > target_height+ 0.6f) dir.set(0.f,-1.0f,0.f); + else if (cur_h < target_height+ 0.6f) dir.set(0.f,1.0f,0.f); + else { + dir.set(Random.randF(-1.0f,1.0f), Random.randF(-1.0f,1.0f), Random.randF(-1.0f,1.0f)); + dir.normalize_safe(); + } + + //float elem_size = float(object->m_pPhysicsShell->Elements().size()); + dir.mul(5.0f); + + if (OnServer()) (object->m_pPhysicsShell->Elements()[0])->applyGravityAccel(dir); + + // установить время последнего обновления + time_keep_updated = Device.dwTimeGlobal; + + update_hold_sound (); +} + +void CTelekineticObject::release() +{ + if (!object || !object->m_pPhysicsShell || !object->m_pPhysicsShell->isActive()) return; + + + Fvector dir_inv; + dir_inv.set(0.f,-1.0f,0.f); + + // включить гравитацию + object->m_pPhysicsShell->set_ApplyByGravity(TRUE); + if (OnServer()) + { + // приложить небольшую силу для того, чтобы объект начал падать + object->m_pPhysicsShell->applyImpulse(dir_inv, 0.5f * object->m_pPhysicsShell->getMass()); + } + //state = TS_None; + switch_state(TS_None); +} + +void CTelekineticObject::fire_t(const Fvector &target, float time) +{ + switch_state(TS_Fire); + //time_fire_started = Device.dwTimeGlobal; + + if (!object || !object->m_pPhysicsShell || !object->m_pPhysicsShell->isActive()) return; + + // включить гравитацию + object->m_pPhysicsShell->set_ApplyByGravity(TRUE); + + Fvector transference; + transference.sub(target,object->Position()); + TransferenceToThrowVel(transference,time,object->EffectiveGravity()); + object->m_pPhysicsShell->set_LinearVel(transference); + + if (sound_throw._handle()) + sound_throw.play_at_pos(object,object->Position()); + + if (sound_hold._handle() && sound_hold._feedback()) + sound_hold.stop(); + +} +void CTelekineticObject::fire(const Fvector &target, float power) +{ + //state = TS_Fire; + switch_state(TS_Fire); + //time_fire_started = Device.dwTimeGlobal; + + if (!object || !object->m_pPhysicsShell || !object->m_pPhysicsShell->isActive()) return; + + // вычислить направление + Fvector dir; + dir.sub(target,object->Position()); + dir.normalize(); + + // включить гравитацию + object->m_pPhysicsShell->set_ApplyByGravity(TRUE); + + if (OnServer()) + { + // выполнить бросок + for (u32 i=0;im_pPhysicsShell->Elements().size();i++) + object->m_pPhysicsShell->Elements()[i]->applyImpulse(dir, power * 20.f * object->m_pPhysicsShell->getMass() / object->m_pPhysicsShell->Elements().size()); + + }; +}; + +bool CTelekineticObject::check_height() +{ + if (!object) return true; + + return (object->Position().y > target_height); +} +bool CTelekineticObject::check_raise_time_out() +{ + if (time_raise_started + RAISE_MAX_TIME < Device.dwTimeGlobal) + return true; + + return false; +} + + + +void CTelekineticObject::enable() +{ + if(object->m_pPhysicsShell)object->m_pPhysicsShell->Enable(); +} + +void CTelekineticObject::rotate() +{ + if (!object || !object->m_pPhysicsShell || !object->m_pPhysicsShell->isActive()) return; + + // вычислить направление + Fvector dir; + dir.random_dir(); + dir.normalize(); + + if (OnServer()) object->m_pPhysicsShell->applyImpulse(dir, 2.5f * object->m_pPhysicsShell->getMass()); +} + +bool CTelekineticObject::can_activate(CPhysicsShellHolder *obj) +{ + return (obj && obj->m_pPhysicsShell); +} + +void CTelekineticObject::update_hold_sound() +{ + if (!sound_hold._handle()) return; + + if (sound_hold._feedback()) + sound_hold.set_position(object->Position()); + else + sound_hold.play_at_pos(object,object->Position()); +} diff --git a/src/xrGameLA/ai/monsters/telekinetic_object.h b/src/xrGameLA/ai/monsters/telekinetic_object.h new file mode 100644 index 000000000..6ca728868 --- /dev/null +++ b/src/xrGameLA/ai/monsters/telekinetic_object.h @@ -0,0 +1,81 @@ +#pragma once + + +enum ETelekineticState { + TS_None, + TS_Raise, + TS_Keep, + TS_Fire, +}; + +class CGameObject; +class CPhysicsShellHolder; +class CTelekineticObject; +class CPHUpdateObject; +class CTelekinesis; +class CTelekineticObject { + + ETelekineticState state; +public: + CPhysicsShellHolder *object; + CTelekinesis *telekinesis; + float target_height; + + u32 time_keep_started; + u32 time_keep_updated; + u32 time_raise_started; + + u32 time_to_keep; + + u32 time_fire_started; + + float strength; + + bool m_rotate; + + ref_sound sound_hold; + ref_sound sound_throw; + +public: + CTelekineticObject (); + ~CTelekineticObject (); + +virtual bool init (CTelekinesis* tele,CPhysicsShellHolder *obj, float s, float h, u32 ttk, bool rot = true); + void set_sound (const ref_sound &snd_hold, const ref_sound &snd_throw); + +virtual void raise (float step); +virtual void raise_update (); + + void prepare_keep (); +virtual void keep (); +virtual void keep_update (); +virtual void release (); +virtual void fire (const Fvector &target, float power); + void fire_t (const Fvector &target, float time); +virtual void fire_update (); +virtual void update_state (); +virtual bool can_activate (CPhysicsShellHolder *obj); + bool is_released (){return state==TS_None;} + ETelekineticState get_state () {return state;} +virtual void switch_state (ETelekineticState new_state); + CPhysicsShellHolder *get_object () {return object;} + + bool check_height (); + bool check_raise_time_out (); + + bool time_keep_elapsed (); + bool time_fire_elapsed (); + + + + void enable (); + + bool operator== (const CPhysicsShellHolder *obj) { + return (object == obj); + } + + void rotate (); +private: + void update_hold_sound (); + +}; diff --git a/src/xrGameLA/ai/monsters/tushkano/tushkano.cpp b/src/xrGameLA/ai/monsters/tushkano/tushkano.cpp new file mode 100644 index 000000000..2653c189a --- /dev/null +++ b/src/xrGameLA/ai/monsters/tushkano/tushkano.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "tushkano.h" +#include "tushkano_state_manager.h" +#include "../monster_velocity_space.h" +#include "../control_animation_base.h" +#include "../control_movement_base.h" + + +CTushkano::CTushkano() +{ + StateMan = new CStateManagerTushkano(this); + + CControlled::init_external(this); +} + +CTushkano::~CTushkano() +{ + xr_delete(StateMan); +} + +void CTushkano::Load(LPCSTR section) +{ + inherited::Load (section); + +// anim().AddReplacedAnim(&m_bDamaged, eAnimRun, eAnimRunDamaged); +// anim().AddReplacedAnim(&m_bDamaged, eAnimWalkFwd, eAnimWalkDamaged); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + //anim().accel_chain_add (eAnimWalkDamaged, eAnimRunDamaged); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + //SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + //SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + //SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + //SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimStandDamaged, "stand_idle_dmg_", -1, &velocity_none, PS_STAND); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_left_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_right_", -1, &velocity_turn, PS_STAND); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND); + //anim().AddAnim(eAnimWalkDamaged, "stand_walk_fwd_dmg_", -1, &velocity_walk_dmg, PS_STAND); + anim().AddAnim(eAnimRun, "stand_run_", -1, &velocity_run, PS_STAND); + //anim().AddAnim(eAnimRunDamaged, "stand_run_dmg_", -1, &velocity_run_dmg, PS_STAND); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND); + //anim().AddAnim(eAnimDie, "stand_die_", 0, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimCheckCorpse, "stand_check_corpse_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimSteal, "stand_crawl_", -1, &velocity_steal, PS_STAND); + //anim().AddAnim(eAnimSitIdle, "sit_idle_", -1, &velocity_none, PS_SIT); + //anim().AddAnim(eAnimSitStandUp, "sit_stand_up_", -1, &velocity_none, PS_SIT); + //anim().AddAnim(eAnimStandSitDown, "stand_sit_down_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimLookAround, "stand_look_around_", -1, &velocity_none, PS_STAND); + //anim().AddAnim(eAnimEat, "sit_eat_", -1, &velocity_none, PS_SIT); + + //anim().AddTransition(PS_STAND, PS_SIT, eAnimStandSitDown, false); + //anim().AddTransition(PS_SIT, PS_STAND, eAnimSitStandUp, false); + + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimStandIdle); + anim().LinkAction(ACT_SLEEP, eAnimStandIdle); + anim().LinkAction(ACT_REST, eAnimStandIdle); + anim().LinkAction(ACT_DRAG, eAnimStandIdle); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimStandIdle); + anim().LinkAction(ACT_LOOK_AROUND, eAnimStandIdle); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +void CTushkano::CheckSpecParams(u32 spec_params) +{ + //if ((spec_params & ASP_CHECK_CORPSE) == ASP_CHECK_CORPSE) { + // anim().Seq_Add(eAnimCheckCorpse); + // anim().Seq_Switch(); + //} + + //if ((spec_params & ASP_STAND_SCARED) == ASP_STAND_SCARED) { + // anim().SetCurAnim(eAnimLookAround); + // return; + //} +} + + diff --git a/src/xrGameLA/ai/monsters/tushkano/tushkano.h b/src/xrGameLA/ai/monsters/tushkano/tushkano.h new file mode 100644 index 000000000..3a25cde3c --- /dev/null +++ b/src/xrGameLA/ai/monsters/tushkano/tushkano.h @@ -0,0 +1,25 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../controlled_entity.h" +#include "../../../script_export_space.h" + +class CTushkano : public CBaseMonster, + public CControlledEntity { + + + typedef CBaseMonster inherited; + typedef CControlledEntity CControlled; + +public: + CTushkano (); + virtual ~CTushkano (); + + virtual void Load (LPCSTR section); + virtual void CheckSpecParams (u32 spec_params); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CTushkano) +#undef script_type_list +#define script_type_list save_type_list(CTushkano) diff --git a/src/xrGameLA/ai/monsters/tushkano/tushkano_script.cpp b/src/xrGameLA/ai/monsters/tushkano/tushkano_script.cpp new file mode 100644 index 000000000..973e7c3d2 --- /dev/null +++ b/src/xrGameLA/ai/monsters/tushkano/tushkano_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "tushkano.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CTushkano::script_register(lua_State *L) +{ + module(L) + [ + class_("CTushkano") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/tushkano/tushkano_state_manager.cpp b/src/xrGameLA/ai/monsters/tushkano/tushkano_state_manager.cpp new file mode 100644 index 000000000..d3a1aeb3d --- /dev/null +++ b/src/xrGameLA/ai/monsters/tushkano/tushkano_state_manager.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "tushkano.h" +#include "tushkano_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_panic.h" +#include "../states/monster_state_hear_danger_sound.h" +#include "../states/monster_state_hitted.h" +#include "../states/monster_state_controlled.h" +#include "../states/monster_state_help_sound.h" + +#include "../../../entitycondition.h" + + +CStateManagerTushkano::CStateManagerTushkano(CTushkano *obj) : inherited(obj) +{ + add_state(eStateRest, new CStateMonsterRest(obj)); + add_state(eStateAttack, new CStateMonsterAttack(obj)); + add_state(eStateEat, new CStateMonsterEat(obj)); + add_state(eStateHearDangerousSound, new CStateMonsterHearDangerousSound(obj)); + add_state(eStatePanic, new CStateMonsterPanic(obj)); + add_state(eStateHitted, new CStateMonsterHitted(obj)); + add_state(eStateControlled, new CStateMonsterControlled(obj)); + add_state(eStateHearHelpSound, new CStateMonsterHearHelpSound(obj)); +} + +CStateManagerTushkano::~CStateManagerTushkano() +{ +} + +void CStateManagerTushkano::execute() +{ + u32 state_id = u32(-1); + + if (!object->is_under_control()) { + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); +// const CEntityAlive* corpse = + object->CorpseMan.get_corpse(); + + if (enemy) { + switch (object->EnemyMan.get_danger_type()) { + case eStrong: state_id = eStateAttack; break; + case eWeak: state_id = eStateAttack; break; + } + } else if (object->HitMemory.is_hit()) { + state_id = eStateHitted; + } else if (check_state(eStateHearHelpSound)) { + state_id = eStateHearHelpSound; + } else if (object->hear_interesting_sound || object->hear_dangerous_sound) { + state_id = eStateHearDangerousSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + } else state_id = eStateControlled; + + // установить текущее состояние + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} + diff --git a/src/xrGameLA/ai/monsters/tushkano/tushkano_state_manager.h b/src/xrGameLA/ai/monsters/tushkano/tushkano_state_manager.h new file mode 100644 index 000000000..162f32b52 --- /dev/null +++ b/src/xrGameLA/ai/monsters/tushkano/tushkano_state_manager.h @@ -0,0 +1,14 @@ +#pragma once +#include "../monster_state_manager.h" + +class CTushkano; + +class CStateManagerTushkano : public CMonsterStateManager { + typedef CMonsterStateManager inherited; + +public: + CStateManagerTushkano (CTushkano *obj); + virtual ~CStateManagerTushkano (); + + virtual void execute (); +}; diff --git a/src/xrGameLA/ai/monsters/zombie/zombie.cpp b/src/xrGameLA/ai/monsters/zombie/zombie.cpp new file mode 100644 index 000000000..d44813ba0 --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie.cpp @@ -0,0 +1,286 @@ +#include "stdafx.h" +#include "zombie.h" +#include "zombie_state_manager.h" +#include "../../../profiler.h" +#include "../../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../entitycondition.h" +#include "../monster_velocity_space.h" +#include "../../../characterphysicssupport.h" +#include "../../../PHMovementControl.h" +#include "../../../actor.h" +#include "../../../ActorEffector.h" +#include "zombie_choke_effector.h" +#include "../../../../camerabase.h" + +#include "../control_animation_base.h" +#include "../control_movement_base.h" +#ifdef _DEBUG +#include +#endif + + +CZombie::CZombie() +{ + StateMan = new CStateManagerZombie(this); + + CControlled::init_external(this); +} + +CZombie::~CZombie() +{ + xr_delete (StateMan); +} + +void CZombie::Load(LPCSTR section) +{ + inherited::Load (section); + + anim().accel_load (section); + anim().accel_chain_add (eAnimWalkFwd, eAnimRun); + + fake_death_count = 1 + u8(Random.randI(pSettings->r_u8(section,"FakeDeathCount"))); + health_death_threshold = pSettings->r_float(section,"StartFakeDeathHealthThreshold"); + + SVelocityParam &velocity_none = move().get_velocity(MonsterMovement::eVelocityParameterIdle); + SVelocityParam &velocity_turn = move().get_velocity(MonsterMovement::eVelocityParameterStand); + SVelocityParam &velocity_walk = move().get_velocity(MonsterMovement::eVelocityParameterWalkNormal); + SVelocityParam &velocity_run = move().get_velocity(MonsterMovement::eVelocityParameterRunNormal); + //SVelocityParam &velocity_walk_dmg = move().get_velocity(MonsterMovement::eVelocityParameterWalkDamaged); + //SVelocityParam &velocity_run_dmg = move().get_velocity(MonsterMovement::eVelocityParameterRunDamaged); + //SVelocityParam &velocity_steal = move().get_velocity(MonsterMovement::eVelocityParameterSteal); + //SVelocityParam &velocity_drag = move().get_velocity(MonsterMovement::eVelocityParameterDrag); + + + anim().AddAnim(eAnimStandIdle, "stand_idle_", -1, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnLeft, "stand_turn_ls_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimStandTurnRight, "stand_turn_rs_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimWalkFwd, "stand_walk_fwd_", -1, &velocity_walk, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimRun, "stand_run_", -1, &velocity_run, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimAttack, "stand_attack_", -1, &velocity_turn, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + anim().AddAnim(eAnimDie, "stand_die_", 0, &velocity_none, PS_STAND, "fx_stand_f", "fx_stand_b", "fx_stand_l", "fx_stand_r"); + + anim().LinkAction(ACT_STAND_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_SIT_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_LIE_IDLE, eAnimStandIdle); + anim().LinkAction(ACT_WALK_FWD, eAnimWalkFwd); + anim().LinkAction(ACT_WALK_BKWD, eAnimWalkFwd); + anim().LinkAction(ACT_RUN, eAnimRun); + anim().LinkAction(ACT_EAT, eAnimStandIdle); + anim().LinkAction(ACT_SLEEP, eAnimStandIdle); + anim().LinkAction(ACT_REST, eAnimStandIdle); + anim().LinkAction(ACT_DRAG, eAnimStandIdle); + anim().LinkAction(ACT_ATTACK, eAnimAttack); + anim().LinkAction(ACT_STEAL, eAnimWalkFwd); + anim().LinkAction(ACT_LOOK_AROUND, eAnimStandIdle); + + LoadChokePPEffector (pSettings->r_string(section,"choke_effector")); + m_choke_want_speed = pSettings->r_float(section,"Choke_Need_Speed"); + +#ifdef DEBUG + anim().accel_chain_test (); +#endif + +} + +void CZombie::reinit() +{ + inherited::reinit(); + + CControlledActor::reinit (); + + Bones.Reset(); + + com_man().ta_fill_data(anim_triple_choke, "stand_attack_4_", "stand_attack_5_", "stand_attack_6_", TA_EXECUTE_LOOPED, TA_DONT_SKIP_PREPARE, ControlCom::eCapturePath | ControlCom::eCaptureMovement); + + time_dead_start = 0; + last_hit_frame = 0; + time_resurrect = 0; + fakedeath_is_active = false; + fake_death_left = fake_death_count; + m_choke_want_value = 0.f; + + active_triple_idx = u8(-1); +} + +void CZombie::reload(LPCSTR section) +{ + inherited::reload(section); + + com_man().ta_fill_data(anim_triple_death[0], "fake_death_0_0", "fake_death_0_1", "fake_death_0_2", true, false); + com_man().ta_fill_data(anim_triple_death[1], "fake_death_1_0", "fake_death_1_1", "fake_death_1_2", true, false); + com_man().ta_fill_data(anim_triple_death[2], "fake_death_2_0", "fake_death_2_1", "fake_death_2_2", true, false); + com_man().ta_fill_data(anim_triple_death[3], "fake_death_3_0", "fake_death_3_1", "fake_death_3_2", true, false); +} + + +void CZombie::BoneCallback(CBoneInstance *B) +{ + CZombie* this_class = static_cast(B->callback_param()); + + START_PROFILE("Zombie/Bones Update"); + this_class->Bones.Update(B, Device.dwTimeGlobal); + STOP_PROFILE("AI/Zombie/Bones Update"); +} + + +void CZombie::vfAssignBones() +{ + // Установка callback на кости + bone_spine = &smart_cast(Visual())->LL_GetBoneInstance(smart_cast(Visual())->LL_BoneID("bip01_spine")); + bone_head = &smart_cast(Visual())->LL_GetBoneInstance(smart_cast(Visual())->LL_BoneID("bip01_head")); + //if(!PPhysicsShell())//нельзя ставить колбеки, если создан физ шел - у него стоят свои колбеки!!! + //{ + //bone_spine->set_callback(BoneCallback,this); + //bone_head->set_callback(BoneCallback,this); + //} + + // Bones settings + Bones.Reset(); + Bones.AddBone(bone_spine, AXIS_Z); Bones.AddBone(bone_spine, AXIS_Y); Bones.AddBone(bone_spine, AXIS_X); + Bones.AddBone(bone_head, AXIS_Z); Bones.AddBone(bone_head, AXIS_Y); +} + +BOOL CZombie::net_Spawn (CSE_Abstract* DC) +{ + if (!inherited::net_Spawn(DC)) + return(FALSE); + + vfAssignBones (); + + return(TRUE); +} + +#define TIME_FAKE_DEATH 5000 +#define TIME_RESURRECT_RESTORE 2000 + +//void CZombie::Hit(float P,Fvector &dir,CObject*who,s16 element,Fvector p_in_object_space,float impulse, ALife::EHitType hit_type) +void CZombie::Hit (SHit* pHDS) +{ +// inherited::Hit(P,dir,who,element,p_in_object_space,impulse,hit_type); + inherited::Hit(pHDS); + + if (!g_Alive()) return; + + if ((pHDS->hit_type == ALife::eHitTypeFireWound) && (Device.dwFrame != last_hit_frame)) { + if (!com_man().ta_is_active() && (time_resurrect + TIME_RESURRECT_RESTORE < Device.dwTimeGlobal) && (conditions().GetHealth() < health_death_threshold)) { + if (conditions().GetHealth() < (health_death_threshold - float(fake_death_count - fake_death_left) * health_death_threshold / fake_death_count)) { + active_triple_idx = u8(Random.randI(FAKE_DEATH_TYPES_COUNT)); + com_man().ta_activate (anim_triple_death[active_triple_idx]); + move().stop (); + if (g_Alive()) + character_physics_support()->movement()->DestroyCharacter(); + time_dead_start = Device.dwTimeGlobal; + fakedeath_is_active = true; + + if (fake_death_left == 0) fake_death_left = 1; + fake_death_left--; + } + } + } + + last_hit_frame = Device.dwFrame; +} + + +void CZombie::shedule_Update(u32 dt) +{ + inherited::shedule_Update(dt); + + if (time_dead_start != 0) { + if (time_dead_start + TIME_FAKE_DEATH < Device.dwTimeGlobal) { + time_dead_start = 0; + fakedeath_is_active = false; + + com_man().ta_pointbreak(); + if (g_Alive()) + character_physics_support()->CreateCharacter(); + + time_resurrect = Device.dwTimeGlobal; + } + } +} + +void CZombie::UpdateCL() +{ + inherited::UpdateCL (); + CControlledActor::frame_update (); + + // update choke need + m_choke_want_value += m_choke_want_speed * client_update_fdelta(); + clamp(m_choke_want_value,0.f,1.f); + //Msg("clamp=[%f]", m_choke_want_value); + +} + +void CZombie::LoadChokePPEffector(LPCSTR section) +{ + pp_choke_effector.duality.h = pSettings->r_float(section,"duality_h"); + pp_choke_effector.duality.v = pSettings->r_float(section,"duality_v"); + pp_choke_effector.gray = pSettings->r_float(section,"gray"); + pp_choke_effector.blur = pSettings->r_float(section,"blur"); + pp_choke_effector.noise.intensity = pSettings->r_float(section,"noise_intensity"); + pp_choke_effector.noise.grain = pSettings->r_float(section,"noise_grain"); + pp_choke_effector.noise.fps = pSettings->r_float(section,"noise_fps"); + VERIFY(!fis_zero(pp_choke_effector.noise.fps)); + + sscanf(pSettings->r_string(section,"color_base"), "%f,%f,%f", &pp_choke_effector.color_base.r, &pp_choke_effector.color_base.g, &pp_choke_effector.color_base.b); + sscanf(pSettings->r_string(section,"color_gray"), "%f,%f,%f", &pp_choke_effector.color_gray.r, &pp_choke_effector.color_gray.g, &pp_choke_effector.color_gray.b); + sscanf(pSettings->r_string(section,"color_add"), "%f,%f,%f", &pp_choke_effector.color_add.r, &pp_choke_effector.color_add.g, &pp_choke_effector.color_add.b); +} + +void CZombie::ActivateChokeEffector() +{ + Actor()->Cameras().AddPPEffector(new CChokePPEffector(pp_choke_effector, 6.0f)); +} + +bool CZombie::fake_death_fall_down() +{ + if (com_man().ta_is_active()) return false; + + com_man().ta_activate (anim_triple_death[u8(Random.randI(FAKE_DEATH_TYPES_COUNT))]); + move().stop (); + fakedeath_is_active = true; + if (g_Alive()) + character_physics_support()->movement()->DestroyCharacter(); + + return true; +} + +void CZombie::fake_death_stand_up() +{ + // check if state active + bool active = false; + for (u32 i=0; iCreateCharacter(); +} + + +#ifdef _DEBUG +void CZombie::debug_on_key(int key) +{ + switch (key){ + case DIK_MINUS: + { + fake_death_fall_down(); + } + break; + case DIK_EQUALS: + { + fake_death_stand_up(); + } + break; + } +} +#endif diff --git a/src/xrGameLA/ai/monsters/zombie/zombie.h b/src/xrGameLA/ai/monsters/zombie/zombie.h new file mode 100644 index 000000000..2600b14ac --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie.h @@ -0,0 +1,83 @@ +#pragma once +#include "../BaseMonster/base_monster.h" +#include "../controlled_entity.h" +#include "../controlled_actor.h" +#include "../ai_monster_bones.h" +#include "../anim_triple.h" +#include "../../../script_export_space.h" + +#define FAKE_DEATH_TYPES_COUNT 4 + +class CZombie : public CBaseMonster, + public CControlledEntity, public CControlledActor { + + typedef CBaseMonster inherited; + typedef CControlledEntity CControlled; + + bonesManipulation Bones; + +public: + CZombie (); + virtual ~CZombie (); + + virtual void Load (LPCSTR section); + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void reinit (); + virtual void reload (LPCSTR section); + + virtual void Hit (SHit* pHDS); + + virtual bool ability_pitch_correction () {return false;} + virtual void UpdateCL (); + virtual void shedule_Update (u32 dt); + + static void BoneCallback (CBoneInstance *B); + void vfAssignBones (); + + virtual bool use_center_to_aim () const {return true;} + + void ActivateChokeEffector (); + +private: + void LoadChokePPEffector (LPCSTR section); + +public: + + CBoneInstance *bone_spine; + CBoneInstance *bone_head; + + SAnimationTripleData anim_triple_death[FAKE_DEATH_TYPES_COUNT]; + SAnimationTripleData anim_triple_choke; + + SPPInfo pp_choke_effector; + float m_choke_want_value; + float m_choke_want_speed; + + u8 active_triple_idx; + + u32 time_dead_start; + u32 last_hit_frame; + u32 time_resurrect; + bool fakedeath_is_active; + + u8 fake_death_count; + float health_death_threshold; + u8 fake_death_left; + + bool fake_death_fall_down (); //return true if everything is ok + void fake_death_stand_up (); + virtual bool fake_death_is_active () const { return fakedeath_is_active;} + IC bool WantChoke () {return m_choke_want_value >= 1.f;} + IC void ChokeCompleted () {m_choke_want_value = 0.f;} + +#ifdef _DEBUG + virtual void debug_on_key (int key); +#endif + + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; + +add_to_type_list(CZombie) +#undef script_type_list +#define script_type_list save_type_list(CZombie) diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_choke.h b/src/xrGameLA/ai/monsters/zombie/zombie_choke.h new file mode 100644 index 000000000..1eafa1b5b --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_choke.h @@ -0,0 +1,29 @@ +#pragma once +#include "../state.h" + +template +class CStateZombieChoke : public CState<_Object> { + typedef CState<_Object> inherited; + typedef CState<_Object>* state_ptr; + + u32 m_time_last_choke; + + +public: + CStateZombieChoke (_Object *obj); + + virtual void reinit (); + + virtual void initialize (); + virtual void reselect_state (); + virtual void finalize (); + virtual void critical_finalize (); + virtual void check_force_state (); + virtual bool check_start_conditions (); + virtual bool check_completion (); + + const CEntityAlive *m_enemy; +}; + + +#include "zombie_choke_inline.h" diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_choke_effector.cpp b/src/xrGameLA/ai/monsters/zombie/zombie_choke_effector.cpp new file mode 100644 index 000000000..6aaf44d33 --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_choke_effector.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "zombie_choke_effector.h" + +////////////////////////////////////////////////////////////////////////// +// Choke PPE Effector by SkyLoader (based on vampire effector) +////////////////////////////////////////////////////////////////////////// + +CChokePPEffector::CChokePPEffector(const SPPInfo &ppi, float life_time) : + inherited(EEffectorPPType(eCEHit), life_time) +{ + state = ppi; + m_total = life_time; +} + +#define TIME_ATTACK 0.2f +#define PERIODS 2 +#define RAD_TO_PERC(rad) ((rad - PI_DIV_2) / (PERIODS * PI_MUL_2)) +#define PERC_TO_RAD(perc) (perc * (PERIODS * PI_MUL_2) + PI_DIV_2) + +BOOL CChokePPEffector::Process(SPPInfo& pp) +{ + inherited::Process(pp); + + // amount of time passed in percents + float time_past_perc = (m_total - fLifeTime) / m_total; + + float factor; + if (time_past_perc < TIME_ATTACK) { + factor = 0.75f * time_past_perc / TIME_ATTACK; + } else if (time_past_perc > (1 - TIME_ATTACK)) { + factor = 0.75f * (1-time_past_perc) / TIME_ATTACK; + } else { + float time_past_sine_perc = (time_past_perc - TIME_ATTACK) * (1 / ( 1 - TIME_ATTACK + TIME_ATTACK)); + factor = 0.5f + 0.25f * _sin(PERC_TO_RAD(time_past_sine_perc)); + } + + clamp(factor,0.01f,1.0f); + pp.lerp (pp_identity, state, factor); + + return TRUE; +} \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_choke_effector.h b/src/xrGameLA/ai/monsters/zombie/zombie_choke_effector.h new file mode 100644 index 000000000..234e67a8b --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_choke_effector.h @@ -0,0 +1,19 @@ +#pragma once + +#include "../../../../effectorPP.h" +#include "../../../CameraEffector.h" +#include "../../../../cameramanager.h" + +////////////////////////////////////////////////////////////////////////// +// Choke PPE Effector by SkyLoader (based on vampire effector) +////////////////////////////////////////////////////////////////////////// +class CChokePPEffector : public CEffectorPP { + typedef CEffectorPP inherited; + + SPPInfo state; //current state + float m_total; // total PP time + +public: + CChokePPEffector (const SPPInfo &ppi, float life_time); + virtual BOOL Process (SPPInfo& pp); +}; \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_choke_execute.h b/src/xrGameLA/ai/monsters/zombie/zombie_choke_execute.h new file mode 100644 index 000000000..212744669 --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_choke_execute.h @@ -0,0 +1,40 @@ +#pragma once +#include "../state.h" + +template +class CStateZombieChokeExecute : public CState<_Object> { + typedef CState<_Object> inherited; + + enum { + eActionPrepare, + eActionContinue, + eActionFire, + eActionWaitTripleEnd, + eActionCompleted + } m_action; + + u32 time_choke_started; + + bool m_effector_activated; + +public: + CStateZombieChokeExecute (_Object *obj) : inherited(obj) {} + + virtual void initialize (); + virtual void execute (); + virtual void finalize (); + virtual void critical_finalize (); + virtual bool check_start_conditions (); + virtual bool check_completion (); + +private: + void execute_choke_prepare (); + void execute_choke_continue (); + void execute_choke_hit (); + + void look_head (); + void show_hud (); + void cleanup (); +}; + +#include "zombie_choke_execute_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_choke_execute_inline.h b/src/xrGameLA/ai/monsters/zombie/zombie_choke_execute_inline.h new file mode 100644 index 000000000..429763fa1 --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_choke_execute_inline.h @@ -0,0 +1,200 @@ +#pragma once + +//#include "../../../../xrRender/skeletoncustom.h" +#include "../../../actor.h" +#include "../../../../CameraBase.h" +#include "../../../../CustomHUD.h" +#include "../../../../../xrCore/_vector3d_ext.h" +#include "../../../hudmanager.h" +#include "../../../UIGameCustom.h" +#include "../../../UI/UIStatic.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateZombieChokeExecuteAbstract CStateZombieChokeExecute<_Object> + +#define CHOKE_TIME_HOLD 4000 +#define CHOKE_HIT_IMPULSE 40.f +#define CHOKE_MIN_DIST 1.5f +#define CHOKE_MAX_DIST 2.f +extern bool g_bDisableAllInput; + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeExecuteAbstract::initialize() +{ + controlling_value = 1; + + inherited::initialize (); + + object->CControlledActor::install (); + object->CControlledActor::set_min_speed (3.f); + object->CControlledActor::set_max_speed (5.f); + + look_head (); + + m_action = eActionPrepare; + time_choke_started = 0; + + psHUD_Flags.set(HUD_DRAW, FALSE); + g_bDisableAllInput = true; + Actor()->SetWeaponHideState(INV_STATE_BLOCK_ALL, true); + HUD().GetGameUI()->AddCustomStatic("zombie_choke", true); + + m_effector_activated = false; +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeExecuteAbstract::execute() +{ + if (/*!object->CControlledActor::is_turning() &&*/ !m_effector_activated) { + object->ActivateChokeEffector (); + m_effector_activated = true; + } + + look_head (); + + switch (m_action) { + case eActionPrepare: + execute_choke_prepare(); + m_action = eActionContinue; + break; + + case eActionContinue: + execute_choke_continue(); + break; + + case eActionFire: + execute_choke_hit(); + m_action = eActionWaitTripleEnd; + break; + + case eActionWaitTripleEnd: + if (!object->com_man().ta_is_active()) { + m_action = eActionCompleted; + } + + case eActionCompleted: + controlling_value = 0; + break; + } + object->set_action (ACT_STAND_IDLE); + object->dir().face_target (object->EnemyMan.get_enemy()); +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeExecuteAbstract::show_hud() +{ + psHUD_Flags.set(HUD_DRAW, TRUE); + Actor()->SetWeaponHideState(INV_STATE_BLOCK_ALL, false); + g_bDisableAllInput = false; + HUD().GetGameUI()->RemoveCustomStatic("zombie_choke"); +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeExecuteAbstract::cleanup() +{ + if ( object->com_man().ta_is_active() ) + object->com_man().ta_deactivate(); + + if (object->CControlledActor::is_controlling()) + object->CControlledActor::release (); + + show_hud(); +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeExecuteAbstract::finalize() +{ + inherited::finalize(); + cleanup(); +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeExecuteAbstract::critical_finalize() +{ + inherited::critical_finalize(); + cleanup(); +} + +TEMPLATE_SPECIALIZATION +bool CStateZombieChokeExecuteAbstract::check_start_conditions() +{ + const CEntityAlive *enemy = object->EnemyMan.get_enemy(); + + if (enemy->CLS_ID != CLSID_OBJECT_ACTOR) return false; + + // проверить дистанцию + float dist = object->MeleeChecker.distance_to_enemy(enemy); + if ((dist > CHOKE_MAX_DIST) || (dist < CHOKE_MIN_DIST)) return false; + + if (object->CControlledActor::is_controlling()) return false; + + const CActor *m_actor = smart_cast(enemy); + VERIFY(m_actor); + if (m_actor->input_external_handler_installed()) return false; + + if (controlling_value == 1) return false; + + // проверить направление на врага + if (!object->control().direction().is_face_target(enemy, PI_DIV_6)) return false; + + return true; +} + +TEMPLATE_SPECIALIZATION +bool CStateZombieChokeExecuteAbstract::check_completion() +{ + return (m_action == eActionCompleted); +} + +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeExecuteAbstract::execute_choke_prepare() +{ + object->com_man().ta_activate (object->anim_triple_choke); + time_choke_started = Device.dwTimeGlobal; +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeExecuteAbstract::execute_choke_continue() +{ + if (object->Position().distance_to(Actor()->Position()) > 2.f) { + object->com_man().ta_deactivate(); + m_action = eActionCompleted; + return; + } + + // проверить на грави удар + if (time_choke_started + CHOKE_TIME_HOLD < Device.dwTimeGlobal) { + m_action = eActionFire; + } +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeExecuteAbstract::execute_choke_hit() +{ + object->com_man().ta_pointbreak (); + object->ChokeCompleted (); +} + +////////////////////////////////////////////////////////////////////////// + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeExecuteAbstract::look_head() +{ + IKinematics *pK = smart_cast(object->Visual()); + Fmatrix bone_transform; + bone_transform = pK->LL_GetTransform(pK->LL_BoneID("bip01_head")); + + Fmatrix global_transform; + global_transform.mul_43(object->XFORM(),bone_transform); + + object->CControlledActor::look_point (global_transform.c); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateZombieChokeExecuteAbstract + diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_choke_inline.h b/src/xrGameLA/ai/monsters/zombie/zombie_choke_inline.h new file mode 100644 index 000000000..6d77c881f --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_choke_inline.h @@ -0,0 +1,105 @@ +#pragma once + +#include "zombie_choke_execute.h" +#include "../../../clsid_game.h" +#include "../../../entity_alive.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateZombieChokeAbstract CStateZombieChoke<_Object> + +#define MAX_DISTANCE_TO_ENEMY 1.6f + +TEMPLATE_SPECIALIZATION + + +CStateZombieChokeAbstract::CStateZombieChoke(_Object *obj) : inherited(obj) +{ + add_state (eStateChoke_Execute, new CStateZombieChokeExecute<_Object>(obj)); +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeAbstract::reinit() +{ + inherited::reinit (); + + m_time_last_choke = 0; +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeAbstract::initialize() +{ + inherited::initialize (); + + m_enemy = object->EnemyMan.get_enemy (); +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeAbstract::reselect_state() +{ + if (get_state(eStateChoke_Execute)->check_start_conditions()) + select_state(eStateChoke_Execute); + + //ctd. here !!!! +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeAbstract::check_force_state() +{ + // check if we can start execute + if (prev_substate == eStateChoke_Execute) + { + if (get_state(eStateChoke_Execute)->check_start_conditions()) + current_substate = u32(-1); + } +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeAbstract::finalize() +{ + inherited::finalize(); + + m_time_last_choke = Device.dwTimeGlobal; +} + +TEMPLATE_SPECIALIZATION +void CStateZombieChokeAbstract::critical_finalize() +{ + inherited::critical_finalize (); + + m_time_last_choke = Device.dwTimeGlobal; +} + +TEMPLATE_SPECIALIZATION +bool CStateZombieChokeAbstract::check_start_conditions() +{ + if (!object->WantChoke()) return false; + + const CEntityAlive *m_enemy = object->EnemyMan.get_enemy(); + if (!object->EnemyMan.see_enemy_now()) return false; + + if (!get_state(eStateChoke_Execute)->check_start_conditions()) return false; + + return true; + } + +TEMPLATE_SPECIALIZATION +bool CStateZombieChokeAbstract::check_completion() +{ + // если враг изменился + if (m_enemy != object->EnemyMan.get_enemy()) return true; + + // если актера уже душит второй зомби + if ((current_substate != eStateChoke_Execute) && + object->CControlledActor::is_controlling()) return true; + + if (current_substate == eStateChoke_Execute && get_state(eStateChoke_Execute)->check_completion()) return true; + + return false; +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateZombieChokeAbstract + diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_script.cpp b/src/xrGameLA/ai/monsters/zombie/zombie_script.cpp new file mode 100644 index 000000000..a18a292ab --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "zombie.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CZombie::script_register(lua_State *L) +{ + module(L) + [ + class_("CZombie") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_state_attack_run.h b/src/xrGameLA/ai/monsters/zombie/zombie_state_attack_run.h new file mode 100644 index 000000000..700766bd1 --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_state_attack_run.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../state.h" + +template +class CStateZombieAttackRun : public CState<_Object> { + typedef CState<_Object> inherited; + + TTime m_time_action_change; + EAction action; + +public: + CStateZombieAttackRun (_Object *obj); + virtual ~CStateZombieAttackRun (); + + virtual void initialize (); + virtual void execute (); + + virtual bool check_completion (); + virtual bool check_start_conditions (); +private: + void choose_action (); + +}; + +#include "zombie_state_attack_run_inline.h" diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_state_attack_run_inline.h b/src/xrGameLA/ai/monsters/zombie/zombie_state_attack_run_inline.h new file mode 100644 index 000000000..1dd6f4490 --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_state_attack_run_inline.h @@ -0,0 +1,122 @@ +#pragma once + +#include "../../../sound_player.h" + +#define TEMPLATE_SPECIALIZATION template <\ + typename _Object\ +> + +#define CStateZombieAttackRunAbstract CStateZombieAttackRun<_Object> + +TEMPLATE_SPECIALIZATION +CStateZombieAttackRunAbstract::CStateZombieAttackRun(_Object *obj) : inherited(obj) +{ +} + +TEMPLATE_SPECIALIZATION +CStateZombieAttackRunAbstract::~CStateZombieAttackRun() +{ +} + +TEMPLATE_SPECIALIZATION +void CStateZombieAttackRunAbstract::initialize() +{ + inherited::initialize (); + m_time_action_change = 0; + action = ACT_WALK_FWD; + + object->path().prepare_builder (); +} + +TEMPLATE_SPECIALIZATION +void CStateZombieAttackRunAbstract::execute() +{ + float dist = object->EnemyMan.get_enemy()->Position().distance_to(object->Position()); + + object->path().set_try_min_time (false); + + // установка параметров функциональных блоков + object->path().set_target_point (object->EnemyMan.get_enemy_position(), object->EnemyMan.get_enemy_vertex()); + object->path().set_rebuild_time (100 + u32(50.f * dist)); + object->path().set_distance_to_end (2.5f); + object->path().set_use_covers (false); + + ////////////////////////////////////////////////////////////////////////// + // обработать squad-данные + ////////////////////////////////////////////////////////////////////////// + CMonsterSquad *squad = monster_squad().get_squad(object); + bool squad_active = squad && squad->SquadActive(); + + // Получить команду + SSquadCommand command; + squad->GetCommand(object, command); + if (!squad_active || (command.type != SC_ATTACK)) squad_active = false; + ////////////////////////////////////////////////////////////////////////// + + if (squad_active) { + object->path().set_use_dest_orient (true); + object->path().set_dest_direction (command.direction); + } else + object->path().set_use_dest_orient (false); + + choose_action (); + object->anim().m_tAction = action; + + if (action == ACT_RUN) + object->path().set_try_min_time (true); + + //object->sound().play (MonsterSound::eMonsterSoundAggressive, 0,0,object->db().m_dwAttackSndDelay); + object->anim().accel_activate (eAT_Aggressive); + object->anim().accel_set_braking (false); +} + +TEMPLATE_SPECIALIZATION +bool CStateZombieAttackRunAbstract::check_completion() +{ + float m_fDistMin = object->MeleeChecker.get_min_distance (); + float dist = object->MeleeChecker.distance_to_enemy (object->EnemyMan.get_enemy()); + + if (dist < m_fDistMin) return true; + + return false; +} + +TEMPLATE_SPECIALIZATION +bool CStateZombieAttackRunAbstract::check_start_conditions() +{ + float m_fDistMax = object->MeleeChecker.get_max_distance (); + float dist = object->MeleeChecker.distance_to_enemy (object->EnemyMan.get_enemy()); + + if (dist > m_fDistMax) return true; + + return false; +} + +#define CHANGE_ACTION_FROM_RUN 20000 + +TEMPLATE_SPECIALIZATION +void CStateZombieAttackRunAbstract::choose_action() +{ + // for test + //action = object->HitMemory.is_hit() ? ACT_RUN : ACT_WALK_FWD; + + //// check if its a strong monster + //if (object->Rank() > 50) { + // action = object->HitMemory.is_hit() ? ACT_RUN : ACT_WALK_FWD; + // return; + //} + // + if ((action == ACT_RUN) && (m_time_action_change + CHANGE_ACTION_FROM_RUN > time())) return; + + // установка параметров функциональных блоков + if (object->HitMemory.is_hit() && (object->conditions().GetHealth() < 0.5f)) + action = ACT_RUN; + else + action = ACT_WALK_FWD; + + m_time_action_change = time(); +} + +#undef TEMPLATE_SPECIALIZATION +#undef CStateZombieAttackRunAbstract + diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_state_manager.cpp b/src/xrGameLA/ai/monsters/zombie/zombie_state_manager.cpp new file mode 100644 index 000000000..752069dbe --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_state_manager.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "zombie.h" +#include "zombie_state_manager.h" + +#include "../control_animation_base.h" +#include "../control_direction_base.h" +#include "../control_movement_base.h" +#include "../control_path_builder_base.h" + +#include "../states/monster_state_rest.h" +#include "../states/monster_state_attack.h" +#include "../states/monster_state_eat.h" +#include "../states/monster_state_hear_int_sound.h" +#include "zombie_state_attack_run.h" +#include "../../../entitycondition.h" +#include "../../../detail_path_manager.h" +#include "../states/monster_state_controlled.h" +#include "../states/monster_state_help_sound.h" +#include "zombie_choke.h" + +CStateManagerZombie::CStateManagerZombie(CZombie *obj) : inherited(obj) +{ + add_state(eStateRest, new CStateMonsterRest(obj)); + add_state( + eStateAttack, + new CStateMonsterAttack(obj, + new CStateZombieAttackRun(obj), + new CStateMonsterAttackMelee(obj) + ) + ); + add_state(eStateEat, new CStateMonsterEat(obj)); + add_state(eStateHearInterestingSound, new CStateMonsterHearInterestingSound(obj)); + add_state(eStateControlled, new CStateMonsterControlled(obj)); + add_state(eStateHearHelpSound, new CStateMonsterHearHelpSound(obj)); + add_state(eStateCustom_Choke, new CStateZombieChoke(obj)); +} + +CStateManagerZombie::~CStateManagerZombie() +{ +} + +void CStateManagerZombie::execute() +{ + if (object->com_man().ta_is_active()) return; + + u32 state_id = u32(-1); + + if (!object->is_under_control()) { + + const CEntityAlive* enemy = object->EnemyMan.get_enemy(); + + if (enemy) { + //if (check_state(eStateCustom_Choke)) + // state_id = eStateCustom_Choke; //skyloader: disabled!! + //else + state_id = eStateAttack; + } else if (check_state(eStateHearHelpSound)) { + state_id = eStateHearHelpSound; + } else if (object->hear_interesting_sound || object->hear_dangerous_sound) { + state_id = eStateHearInterestingSound; + } else { + if (can_eat()) state_id = eStateEat; + else state_id = eStateRest; + } + } else state_id = eStateControlled; + + // установить текущее состояние + select_state(state_id); + + // выполнить текущее состояние + get_state_current()->execute(); + + prev_substate = current_substate; +} + diff --git a/src/xrGameLA/ai/monsters/zombie/zombie_state_manager.h b/src/xrGameLA/ai/monsters/zombie/zombie_state_manager.h new file mode 100644 index 000000000..54425af1b --- /dev/null +++ b/src/xrGameLA/ai/monsters/zombie/zombie_state_manager.h @@ -0,0 +1,15 @@ +#pragma once +#include "../monster_state_manager.h" + +class CZombie; + +class CStateManagerZombie : public CMonsterStateManager { + typedef CMonsterStateManager inherited; + +public: + CStateManagerZombie (CZombie *obj); + virtual ~CStateManagerZombie (); + + virtual void execute (); + +}; diff --git a/src/xrGameLA/ai/phantom/phantom.cpp b/src/xrGameLA/ai/phantom/phantom.cpp new file mode 100644 index 000000000..13a66aef6 --- /dev/null +++ b/src/xrGameLA/ai/phantom/phantom.cpp @@ -0,0 +1,397 @@ +#include "stdafx.h" +#include "phantom.h" +#include "../../level.h" +#include "../../xrServer_Objects_ALife_Monsters.h" +#include "../../../motion.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../../Include/xrRender/RenderVisual.h" + +CPhantom::CPhantom() +{ + fSpeed = 4.f; + fASpeed = 1.7f; + vHP.set (0,0); + fContactHit = 0.f; + m_CurState = stInvalid; + m_TgtState = stInvalid; +} + +CPhantom::~CPhantom() +{ +} + +//--------------------------------------------------------------------- +void CPhantom::Load( LPCSTR section ) +{ + inherited::Load (section); + ////////////////////////////////////////////////////////////////////////// + ISpatial* self = smart_cast (this); + if (self) { + self->spatial.type &=~STYPE_VISIBLEFORAI; + self->spatial.type &=~STYPE_REACTTOSOUND; + } + ////////////////////////////////////////////////////////////////////////// + fSpeed = pSettings->r_float(section,"speed"); + fASpeed = pSettings->r_float(section,"angular_speed"); + fContactHit = pSettings->r_float(section,"contact_hit"); + + LPCSTR snd_name = 0; + m_state_data[stBirth].particles = pSettings->r_string(section,"particles_birth"); + snd_name = pSettings->r_string(section,"sound_birth"); + if (snd_name&&snd_name[0]) m_state_data[stBirth].sound.create(snd_name,st_Effect,sg_SourceType); + + m_state_data[stFly].particles = pSettings->r_string(section,"particles_fly"); + snd_name = pSettings->r_string(section,"sound_fly"); + if (snd_name&&snd_name[0]) m_state_data[stFly].sound.create(snd_name,st_Effect,sg_SourceType); + + m_state_data[stContact].particles= pSettings->r_string(section,"particles_contact"); + snd_name = pSettings->r_string(section,"sound_contact"); + if (snd_name&&snd_name[0]) m_state_data[stContact].sound.create(snd_name,st_Effect,sg_SourceType); + + m_state_data[stShoot].particles = pSettings->r_string(section,"particles_shoot"); + snd_name = pSettings->r_string(section,"sound_shoot"); + if (snd_name&&snd_name[0]) m_state_data[stShoot].sound.create(snd_name,st_Effect,sg_SourceType); +} + +BOOL CPhantom::net_Spawn(CSE_Abstract* DC) +{ + CSE_ALifeCreaturePhantom* OBJ = smart_cast(DC); VERIFY(OBJ); + + // select visual at first + LPCSTR vis_name = OBJ->get_visual(); + if (!(vis_name&&vis_name[0])){ + LPCSTR visuals = pSettings->r_string(cNameSect(),"visuals"); + u32 cnt = _GetItemCount(visuals); + string256 tmp; + OBJ->set_visual (_GetItem(visuals,Random.randI(cnt),tmp)); + // inform server + NET_Packet P; + u_EventGen (P, GE_CHANGE_VISUAL, OBJ->ID); + P.w_stringZ (tmp); + u_EventSend (P); + } + + SwitchToState (stBirth); // initial state (changed on load method in inherited::) + + // inherited + if (!inherited::net_Spawn(DC)) return FALSE; + + m_enemy = Level().CurrentEntity(); + VERIFY (m_enemy); + + // default init + m_fly_particles = 0; + SetfHealth (0.001f); + + // orientate to enemy + XFORM().k.sub (m_enemy->Position(),Position()).normalize(); + XFORM().j.set (0,1,0); + XFORM().i.crossproduct (XFORM().j,XFORM().k); + XFORM().k.getHP (vHP.x,vHP.y); + + // set animation + IKinematicsAnimated *K = smart_cast(Visual()); + m_state_data[stBirth].motion = K->ID_Cycle("birth_0"); + m_state_data[stFly].motion = K->ID_Cycle("fly_0"); + m_state_data[stContact].motion = K->ID_Cycle("contact_0"); + m_state_data[stShoot].motion = K->ID_Cycle("shoot_0"); + + VERIFY(K->LL_GetMotionDef(m_state_data[stBirth].motion)->flags&esmStopAtEnd); + VERIFY(K->LL_GetMotionDef(m_state_data[stContact].motion)->flags&esmStopAtEnd); + VERIFY(K->LL_GetMotionDef(m_state_data[stShoot].motion)->flags&esmStopAtEnd); + + // set state + SwitchToState_internal(m_TgtState); + + setVisible (m_CurState>stIdle?TRUE:FALSE); + setEnabled (TRUE); + + return TRUE; +} + +void CPhantom::net_Destroy () +{ + inherited::net_Destroy (); + + // stop looped + SStateData& sdata = m_state_data[stFly]; + sdata.sound.stop (); + CParticlesObject::Destroy (m_fly_particles); +} + +//--------------------------------------------------------------------- +// Animation Callbacks +void CPhantom::animation_end_callback(CBlend* B) +{ + CPhantom *phantom = (CPhantom*)B->CallbackParam; + switch (phantom->m_CurState){ + case stBirth: phantom->SwitchToState(stFly); break; + case stContact: phantom->SwitchToState(stIdle); break; + case stShoot: phantom->SwitchToState(stIdle); break; + } +} + +//--------------------------------------------------------------------- +void CPhantom::SwitchToState_internal(EState new_state) +{ + if (new_state!=m_CurState){ + IKinematicsAnimated *K = smart_cast(Visual()); + Fmatrix xform = XFORM_center (); + UpdateEvent = 0; + // after event + switch (m_CurState){ + case stBirth: break; + case stFly: break; + case stContact:{ + SStateData& sdata = m_state_data[m_CurState]; + PlayParticles (sdata.particles.c_str(),FALSE,xform); + Fvector vE,vP; + m_enemy->Center (vE); + Center (vP); + }break; + case stShoot:{ + SStateData& sdata = m_state_data[m_CurState]; + PlayParticles (sdata.particles.c_str(),FALSE,xform); + }break; + case stIdle: break; + } + // before event + switch (new_state){ + case stBirth:{ + SStateData& sdata = m_state_data[new_state]; + PlayParticles (sdata.particles.c_str(),TRUE,xform); + sdata.sound.play_at_pos(0,xform.c); + K->PlayCycle (sdata.motion, TRUE, animation_end_callback, this); + }break; + case stFly:{ + UpdateEvent.bind (this,&CPhantom::OnFlyState); + SStateData& sdata = m_state_data[new_state]; + m_fly_particles = PlayParticles(sdata.particles.c_str(),FALSE,xform); + sdata.sound.play_at_pos(0,xform.c,sm_Looped); + K->PlayCycle (sdata.motion); + }break; + case stContact:{ + UpdateEvent.bind (this,&CPhantom::OnDeadState); + SStateData& sdata = m_state_data[new_state]; + sdata.sound.play_at_pos(0,xform.c); + K->PlayCycle (sdata.motion, TRUE, animation_end_callback, this); + if (!fis_zero(fContactHit)) + { + // hit enemy + PsyHit (m_enemy,fContactHit); + } + }break; + case stShoot:{ + UpdateEvent.bind (this,&CPhantom::OnDeadState); + SStateData& sdata = m_state_data[new_state]; + PlayParticles (sdata.particles.c_str(),TRUE,xform); + sdata.sound.play_at_pos(0,xform.c); + K->PlayCycle (sdata.motion, TRUE, animation_end_callback, this); + }break; + case stIdle:{ + UpdateEvent.bind (this,&CPhantom::OnIdleState); + SStateData& sdata = m_state_data[m_CurState]; + sdata.sound.stop (); + CParticlesObject::Destroy(m_fly_particles); + }break; + } + m_CurState = new_state; + } +} + +void CPhantom::OnIdleState() +{ + DestroyObject (); +} + +void CPhantom::OnFlyState() +{ + UpdateFlyMedia (); + if (g_Alive()){ + Fvector vE,vP; + m_enemy->Center (vE); + Center (vP); + if (vP.distance_to_sqr(vE)<_sqr(Radius()+m_enemy->Radius())){ + SwitchToState (stContact); + SHit HDS(1000.f,Fvector().set(0,0,1),this,BI_NONE,Fvector().set(0,0,0),100.f,ALife::eHitTypeFireWound); + Hit(&HDS); + } + } +} + +void CPhantom::OnDeadState() +{ + UpdateFlyMedia (); +} + +void CPhantom::UpdateFlyMedia() +{ + UpdatePosition (m_enemy->Position()); + Fmatrix xform = XFORM_center(); + // update particles + if (m_fly_particles){ + Fvector vel; + vel.sub (m_enemy->Position(),Position()).normalize_safe().mul(fSpeed); + m_fly_particles->UpdateParent(xform,vel); + } + // update sound + if (m_state_data[stFly].sound._feedback()) m_state_data[stFly].sound.set_position(xform.c); +} +//--------------------------------------------------------------------- + + +void CPhantom::shedule_Update(u32 DT) +{ + spatial.type &=~STYPE_VISIBLEFORAI; + + inherited::shedule_Update(DT); + + IKinematicsAnimated *K = smart_cast(Visual()); + K->UpdateTracks (); +} + +void CPhantom::UpdateCL() +{ + inherited::UpdateCL(); + + if (!UpdateEvent.empty()) UpdateEvent(); + if (m_TgtState!=m_CurState) SwitchToState_internal(m_TgtState); +} + +//--------------------------------------------------------------------- +//void CPhantom::Hit (float P, Fvector &dir, CObject* who, s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type) +void CPhantom::Hit (SHit* pHDS) +{ + if (m_TgtState==stFly) SwitchToState(stShoot); + if (g_Alive()){ + SetfHealth (-1.f); +// inherited::Hit (P,dir,who,element,p_in_object_space,impulse/100.f, hit_type); + inherited::Hit (pHDS); + } +} + +//--------------------------------------------------------------------- +Fmatrix CPhantom::XFORM_center() +{ + Fvector center; + Center (center); + Fmatrix xform = XFORM(); + return xform.translate_over(center); +} + +CParticlesObject* CPhantom::PlayParticles(const shared_str& name, BOOL bAutoRemove, const Fmatrix& xform) +{ + CParticlesObject* ps = CParticlesObject::Create(name.c_str(),bAutoRemove); + ps->UpdateParent (xform, zero_vel); + ps->Play (false); + return bAutoRemove?0:ps; +} + +//--------------------------------------------------------------------- +void CPhantom::UpdatePosition(const Fvector& tgt_pos) +{ + float tgt_h,tgt_p; + Fvector tgt_dir,cur_dir; + tgt_dir.sub (tgt_pos,Position()); + tgt_dir.getHP (tgt_h,tgt_p); + + angle_lerp (vHP.x,tgt_h,fASpeed,Device.fTimeDelta); + angle_lerp (vHP.y,tgt_p,fASpeed,Device.fTimeDelta); + + cur_dir.setHP (vHP.x,vHP.y); + + Fvector prev_pos=Position(); + XFORM().rotateY (-vHP.x); + Position().mad (prev_pos,cur_dir,fSpeed*Device.fTimeDelta); +} + +void CPhantom::PsyHit(const CObject *object, float value) +{ + NET_Packet P; + SHit HS; + HS.GenHeader (GE_HIT, object->ID()); // // u_EventGen (P,GE_HIT, object->ID()); + HS.whoID = (ID()); // own // P.w_u16 (object->ID()); + HS.weaponID = (ID()); // own // P.w_u16 (object->ID()); + HS.dir = (Fvector().set(0.f,1.f,0.f)); // direction // P.w_dir (Fvector().set(0.f,1.f,0.f)); + HS.power = (value); // hit value // P.w_float (value); + HS.boneID = (BI_NONE); // bone // P.w_s16 (BI_NONE); + HS.p_in_bone_space = (Fvector().set(0.f,0.f,0.f)); // P.w_vec3 (Fvector().set(0.f,0.f,0.f)); + HS.impulse = (0.f); // P.w_float (0.f); + HS.hit_type = (ALife::eHitTypeTelepatic); // P.w_u16 (u16(ALife::eHitTypeTelepatic)); + HS.Write_Packet (P); + + u_EventSend (P); +} + +//--------------------------------------------------------------------- +// Core events +void CPhantom::save(NET_Packet &output_packet) +{ + output_packet.w_s32 (s32(m_CurState)); +} + +void CPhantom::load(IReader &input_packet) +{ + SwitchToState (EState(input_packet.r_s32())); +} + +void CPhantom::net_Export (NET_Packet& P) // export to server +{ + // export + R_ASSERT (Local()); + + u8 flags = 0; + P.w_float (GetfHealth()); + + P.w_float (0); + P.w_u32 (0); + P.w_u32 (0); + + P.w_u32 (Device.dwTimeGlobal); + P.w_u8 (flags); + + float yaw, pitch, bank; + XFORM().getHPB (yaw,pitch,bank); + P.w_float /*w_angle8*/ (yaw); + P.w_float /*w_angle8*/ (yaw); + P.w_float /*w_angle8*/ (pitch); + P.w_float /*w_angle8*/ (0); + P.w_u8 (u8(g_Team())); + P.w_u8 (u8(g_Squad())); + P.w_u8 (u8(g_Group())); +} + +void CPhantom::net_Import (NET_Packet& P) +{ + // import + R_ASSERT (Remote()); + + u8 flags; + + float health; + P.r_float (health); + SetfHealth (health); + + float fDummy; + u32 dwDummy; + P.r_float (fDummy); + P.r_u32 (dwDummy); + P.r_u32 (dwDummy); + + P.r_u32 (dwDummy); + P.r_u8 (flags); + + float yaw, pitch, bank = 0, roll = 0; + + P.r_float /*r_angle8*/ (yaw); + P.r_float /*r_angle8*/ (yaw); + P.r_float /*r_angle8*/ (pitch); + P.r_float /*r_angle8*/ (roll); + + id_Team = P.r_u8(); + id_Squad = P.r_u8(); + id_Group = P.r_u8(); + + XFORM().setHPB (yaw,pitch,bank); +} + diff --git a/src/xrGameLA/ai/phantom/phantom.h b/src/xrGameLA/ai/phantom/phantom.h new file mode 100644 index 000000000..8a9b5e04f --- /dev/null +++ b/src/xrGameLA/ai/phantom/phantom.h @@ -0,0 +1,87 @@ +#pragma once + +#include "../../entity.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" + +class CParticlesObject; + +class CPhantom : public CEntity { + + typedef CEntity inherited; +private: + enum EState{ + stInvalid = -2, + stIdle = -1, + stBirth = 0, + stFly = 1, + stContact = 2, + stShoot = 3, + stCount + }; + EState m_CurState; + EState m_TgtState; + + void SwitchToState_internal (EState new_state); + void SwitchToState (EState new_state){m_TgtState=new_state;} + void __stdcall OnIdleState (); + void __stdcall OnFlyState (); + void __stdcall OnDeadState (); + + void UpdateFlyMedia (); + + fastdelegate::FastDelegate0<> UpdateEvent; +private: + struct SStateData{ + shared_str particles; + ref_sound sound; + MotionID motion; + }; + SStateData m_state_data[stCount]; +private: + CParticlesObject* m_fly_particles; + static void animation_end_callback (CBlend* B); +private: + CObject* m_enemy; + + float fSpeed; + float fASpeed; + Fvector2 vHP; + + float fContactHit; + + Fmatrix XFORM_center (); + + CParticlesObject* PlayParticles (const shared_str& name, BOOL bAutoRemove, const Fmatrix& xform); +// void PlayMotion (MotionID); + + void UpdatePosition (const Fvector& tgt_pos); + + void PsyHit (const CObject *object, float value); +public: + CPhantom (); + virtual ~CPhantom (); + + virtual void Load ( LPCSTR section ); + virtual BOOL net_Spawn ( CSE_Abstract* DC ); + virtual void net_Destroy (); + + virtual void net_Export (NET_Packet& P); + virtual void net_Import (NET_Packet& P); + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + + virtual void shedule_Update (u32 DT); + virtual void UpdateCL (); + + virtual void HitSignal (float HitAmount, Fvector& local_dir, CObject* who, s16 element){} + virtual void HitImpulse (float amount, Fvector& vWorldDir, Fvector& vLocalDir){} + virtual void Hit (SHit* pHDS); + + virtual BOOL IsVisibleForHUD () {return false;} + virtual bool IsVisibleForZones () {return false;} + + virtual BOOL UsedAI_Locations () {return false;} + + virtual CEntity* cast_entity () {return this;} +}; + diff --git a/src/xrGameLA/ai/rats/ai_rat.cpp b/src/xrGameLA/ai/rats/ai_rat.cpp new file mode 100644 index 000000000..9265e30a2 --- /dev/null +++ b/src/xrGameLA/ai/rats/ai_rat.cpp @@ -0,0 +1,705 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat.cpp +// Created : 23.04.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../PhysicsShell.h" +#include "../../../game_graph.h" +#include "../../../game_level_cross_table.h" +#include "../../../../xrServerEntities/xrserver_objects_alife_monsters.h" + +#include "ai_rat_space.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../ai_object_location.h" +#include "../../../movement_manager.h" +#include "../../../location_manager.h" +#include "../../../../xrServerEntities/ai_sounds.h" +#include "../../../sound_player.h" +#include "ai_rat_impl.h" +#include "../../../ai_space.h" + +#include "../../../rat_state_manager.h" +#include "../../../rat_states.h" +#include "object_broker.h" +#include "../ai_monster_squad_manager.h" +#include "../ai_monster_squad.h" +#include "../../../patrol_path_storage.h" +#include "../../../patrol_path.h" +#include "../../../Actor.h" + + +#ifdef DEBUG +#include "HudManager.h" +#endif + +using namespace RatSpace; + +// #include "../../../steering_behaviour_manager.h" +// #include "../../../steering_behaviour_cohesion.h" +// #include "../../../steering_behaviour_alignment.h" +// #include "../../../steering_behaviour_separation.h" + +CAI_Rat::CAI_Rat() : + m_behaviour_manager (0) +{ + init (); +// m_behaviour_manager = xr_new(this); +// m_behaviour_manager->add (xr_new(this), .5f); +// m_behaviour_manager->add (xr_new(this), .5f); +// m_behaviour_manager->add (xr_new(this), .5f); +} + +CAI_Rat::~CAI_Rat() +{ + delete_data (m_state_manager); +} + +void CAI_Rat::init() +{ + m_tAction = eRatActionNone; + m_hit_direction.set (0,0,1); + m_hit_time = 0; + m_tpCurrentGlobalAnimation.invalidate(); + m_tpCurrentGlobalBlend = 0; + m_bActionStarted = false; + m_bFiring = false; + m_previous_query_time = 0; + m_tGoalDir.set (10.0f*(Random.randF()-Random.randF()),10.0f*(Random.randF()-Random.randF()),10.0f*(Random.randF()-Random.randF())); + m_tCurrentDir = m_tGoalDir; + m_tHPB.set (0,0,0); + m_fDHeading = 0; + m_fGoalChangeTime = 0.f; + m_tLastSound.tpEntity = 0; + m_tLastSound.dwTime = 0; + m_tLastSound.eSoundType = SOUND_TYPE_NO_SOUND; + m_bNoWay = false; + m_dwMoraleLastUpdateTime = 0; + m_bStanding = false; + m_bActive = false; + m_dwStartAttackTime = 0; + m_saved_impulse = 0.f; + m_bMoving = false; + m_bCanAdjustSpeed = false; + m_bStraightForward = false; + m_turning = false; + time_to_next_attack = 2000; + time_old_attack = 0; + m_squad_count = u32(-1); + m_current_way_point = u32(-1); +} + +void CAI_Rat::init_state_manager () +{ + m_state_manager = new rat_state_manager(); + m_state_manager->construct (this); + fire(false); + + m_state_manager->add_state (aiRatDeath, new rat_state_death ()); + m_state_manager->add_state (aiRatFreeActive, new rat_state_free_active ()); + m_state_manager->add_state (aiRatFreePassive, new rat_state_free_passive ()); + m_state_manager->add_state (aiRatAttackRange, new rat_state_attack_range ()); + m_state_manager->add_state (aiRatAttackMelee, new rat_state_attack_melee ()); + m_state_manager->add_state (aiRatUnderFire, new rat_state_under_fire ()); + m_state_manager->add_state (aiRatRetreat, new rat_state_retreat ()); + m_state_manager->add_state (aiRatPursuit, new rat_state_pursuit ()); + m_state_manager->add_state (aiRatFreeRecoil, new rat_state_free_recoil ()); + m_state_manager->add_state (aiRatReturnHome, new rat_state_return_home ()); + m_state_manager->add_state (aiRatEatCorpse, new rat_state_eat_corpse ()); + m_state_manager->add_state (aiRatNoWay, new rat_state_no_way ()); + + m_state_manager->push_state (aiRatFreeActive); +} + +void CAI_Rat::reinit () +{ + inherited::reinit (); + CEatableItem::reinit (); + init (); + init_state_manager (); +} + +void CAI_Rat::reload (LPCSTR section) +{ + inherited::reload (section); + CEatableItem::reload (section); + LPCSTR head_bone_name = pSettings->r_string(section,"bone_head"); + sound().add (pSettings->r_string(section,"sound_death"), 100, SOUND_TYPE_MONSTER_DYING, 0, u32(eRatSoundMaskDie), eRatSoundDie, head_bone_name); + sound().add (pSettings->r_string(section,"sound_hit"), 100, SOUND_TYPE_MONSTER_INJURING, 1, u32(eRatSoundMaskInjuring), eRatSoundInjuring, head_bone_name); + sound().add (pSettings->r_string(section,"sound_attack"), 100, SOUND_TYPE_MONSTER_ATTACKING, 2, u32(eRatSoundMaskAttack), eRatSoundAttack, head_bone_name); + sound().add (pSettings->r_string(section,"sound_voice"), 100, SOUND_TYPE_MONSTER_TALKING, 4, u32(eRatSoundMaskVoice), eRatSoundVoice, head_bone_name); + sound().add (pSettings->r_string(section,"sound_eat"), 100, SOUND_TYPE_MONSTER_EATING , 3, u32(eRatSoundMaskEat), eRatSoundEat, head_bone_name); +} + +void CAI_Rat::Die(CObject* who) +{ + inherited::Die(who); + + m_eCurrentState = aiRatDeath; + + m_flags.set (FCanTake, TRUE); + + SelectAnimation(XFORM().k,movement().detail().direction(),movement().speed()); + + sound().play (eRatSoundDie); + + update_morale_broadcast(m_fMoraleDeathQuant,m_fMoraleDeathDistance); + + CGroupHierarchyHolder &Group = Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()); + vfRemoveActiveMember(); + vfRemoveStandingMember(); + --(Group.m_dwAliveCount); + m_eCurrentState = aiRatDeath; +} + +void CAI_Rat::Load(LPCSTR section) +{ + init(); + + inherited::Load(section); + CEatableItem::Load(section); + + // initialize start position + Fvector P = Position(); + P.x += ::Random.randF(); + P.z += ::Random.randF(); + + // active\passive + m_fChangeActiveStateProbability = pSettings->r_float (section,"ChangeActiveStateProbability"); + m_dwPassiveScheduleMin = pSettings->r_s32 (section,"PassiveScheduleMin"); + m_dwPassiveScheduleMax = pSettings->r_s32 (section,"PassiveScheduleMax"); + m_dwActiveCountPercent = pSettings->r_s32 (section,"ActiveCountPercent"); + m_dwStandingCountPercent = pSettings->r_s32 (section,"StandingCountPercent"); + + // eye shift + m_tEyeShift.y = pSettings->r_float (section,"EyeYShift"); + + // former constants + m_dwLostMemoryTime = pSettings->r_s32 (section,"LostMemoryTime"); + m_dwLostRecoilTime = pSettings->r_s32 (section,"LostRecoilTime"); + m_fUnderFireDistance = pSettings->r_float (section,"UnderFireDistance"); + m_dwRetreatTime = pSettings->r_s32 (section,"RetreatTime"); + m_fRetreatDistance = pSettings->r_float (section,"RetreatDistance"); + m_fAttackStraightDistance = pSettings->r_float (section,"AttackStraightDistance"); + m_fStableDistance = pSettings->r_float (section,"StableDistance"); + m_fWallMinTurnValue = pSettings->r_float (section,"WallMinTurnValue")/180.f*PI; + m_fWallMaxTurnValue = pSettings->r_float (section,"WallMaxTurnValue")/180.f*PI; + + m_fAngleSpeed = pSettings->r_float (section,"AngleSpeed"); + m_fSafeGoalChangeDelta = pSettings->r_float (section,"GoalChangeDelta"); + m_tGoalVariation = pSettings->r_fvector3(section,"GoalVariation"); + + m_fMoraleDeathDistance = pSettings->r_float (section,"MoraleDeathDistance"); + m_dwActionRefreshRate = pSettings->r_s32 (section,"ActionRefreshRate"); + + SetMaxHealth (pSettings->r_float (section,"MaxHealthValue")); + m_fSoundThreshold = pSettings->r_float (section,"SoundThreshold"); + + m_bEatMemberCorpses = pSettings->r_bool (section,"EatMemberCorpses"); + m_bCannibalism = pSettings->r_bool (section,"Cannibalism"); + m_dwEatCorpseInterval = pSettings->r_s32 (section,"EatCorpseInterval"); + + m_fNullASpeed = pSettings->r_float (section,"AngularStandSpeed")/180.f*PI;//PI_MUL_2 + m_fMinASpeed = pSettings->r_float (section,"AngularMinSpeed")/180.f*PI;//PI_MUL_2 + m_fMaxASpeed = pSettings->r_float (section,"AngularMaxSpeed")/180.f*PI;//.2f + m_fAttackASpeed = pSettings->r_float (section,"AngularAttackSpeed")/180.f*PI;//.15f; + + m_phMass = pSettings->r_float (section,"corp_mass"); + m_dwActiveScheduleMin = shedule.t_min; + m_dwActiveScheduleMax = shedule.t_max; +} + +BOOL CAI_Rat::net_Spawn (CSE_Abstract* DC) +{ + ////////////////////////////////////////////////////////////////////////// + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeMonsterRat *tpSE_Rat = smart_cast(e); + + // model + if (!inherited::net_Spawn(DC)) + return(FALSE); + // model + if (!CEatableItem::net_Spawn(DC)) + return(FALSE); + + monster_squad().register_member ((u8)g_Team(),(u8)g_Squad(),(u8)g_Group(), this); + + // personal characteristics + movement().m_body.current.yaw = movement().m_body.target.yaw = -tpSE_Rat->o_torso.yaw; + movement().m_body.current.pitch = movement().m_body.target.pitch = 0; + movement().m_body.speed = PI_MUL_2; + + eye_fov = tpSE_Rat->fEyeFov; + eye_range = tpSE_Rat->fEyeRange; + SetfHealth (tpSE_Rat->get_health()); + m_fMinSpeed = tpSE_Rat->fMinSpeed; + m_fMaxSpeed = tpSE_Rat->fMaxSpeed; + m_fAttackSpeed = tpSE_Rat->fAttackSpeed; + m_fMaxPursuitRadius = tpSE_Rat->fMaxPursuitRadius; + m_fMaxHomeRadius = tpSE_Rat->fMaxHomeRadius; + // morale + m_fMoraleSuccessAttackQuant = tpSE_Rat->fMoraleSuccessAttackQuant; + m_fMoraleDeathQuant = tpSE_Rat->fMoraleDeathQuant; + m_fMoraleFearQuant = tpSE_Rat->fMoraleFearQuant; + m_fMoraleRestoreQuant = tpSE_Rat->fMoraleRestoreQuant; + m_dwMoraleRestoreTimeInterval = tpSE_Rat->u16MoraleRestoreTimeInterval; + m_fMoraleMinValue = tpSE_Rat->fMoraleMinValue; + m_fMoraleMaxValue = tpSE_Rat->fMoraleMaxValue; + m_fMoraleNormalValue = tpSE_Rat->fMoraleNormalValue; + // attack + m_fHitPower = tpSE_Rat->fHitPower; + m_dwHitInterval = tpSE_Rat->u16HitInterval; + m_fAttackDistance = tpSE_Rat->fAttackDistance; + m_fAttackAngle = tpSE_Rat->fAttackAngle/180.f*PI; + m_fAttackSuccessProbability = tpSE_Rat->fAttackSuccessProbability; + + // m_tCurGP = tpSE_Rat->m_tGraphID; + // m_tNextGP = tpSE_Rat->m_tNextGraphID; + m_current_graph_point = m_next_graph_point = ai_location().game_vertex_id(); + + int iPointCount = (int)movement().locations().vertex_types().size(); + for (int j=0; jvertex_type())) { + m_time_to_change_graph_point= Device.dwTimeGlobal + ::Random32.random(60000) + 60000; + break; + } + + ////////////////////////////////////////////////////////////////////////// + + m_fSpeed = m_fCurSpeed = m_fMaxSpeed; + + if (g_Alive()) + m_tSpawnPosition.set (Level().seniority_holder().team(g_Team()).squad(g_Squad()).leader()->Position()); + else + m_tSpawnPosition.set (Position()); + + m_home_position.set (m_tSpawnPosition); + m_tStateStack.push (m_eCurrentState = aiRatFreeActive); + if (g_Alive()) + add_active_member (true); + + m_bStateChanged = true; + ai_location().game_vertex (ai().cross_table().vertex(ai_location().level_vertex_id()).game_vertex_id()); + + m_tHPB.x = -movement().m_body.current.yaw; + m_tHPB.y = -movement().m_body.current.pitch; + m_tHPB.z = 0; + + movement().enable_movement (false); + + load_animations (); + + if (g_Alive()) + Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()).m_dwLastActionTime = 0; + + m_flags.set (FCanTake, FALSE); + + CInifile *m_spawn_ini = spawn_ini(); + + if (!m_spawn_ini || !m_spawn_ini->section_exist("patrol") || !m_spawn_ini->line_exist("patrol","way")) + { + m_walk_on_way = false; + } else { + m_walk_on_way = true; + } + + if (m_walk_on_way) + { + m_current_way_point = 0; + m_path = ai().patrol_paths().path(m_spawn_ini->r_string("patrol","way")); + } + + return (TRUE); +} + +void CAI_Rat::net_Destroy() +{ + inherited::net_Destroy(); + CEatableItem::net_Destroy(); + monster_squad().remove_member ((u8)g_Team(),(u8)g_Squad(),(u8)g_Group(),this); +} + +void CAI_Rat::net_Export(NET_Packet& P) +{ + R_ASSERT (Local()); + + // export last known packet + R_ASSERT (!NET.empty()); + net_update& N = NET.back(); + P.w_float (GetfHealth()); + P.w_u32 (N.dwTimeStamp); + P.w_u8 (0); + P.w_vec3 (N.p_pos); + P.w_float (N.o_model); + P.w_float (N.o_torso.yaw); + P.w_float (N.o_torso.pitch); + P.w_float (N.o_torso.roll); + P.w_u8 (u8(g_Team())); + P.w_u8 (u8(g_Squad())); + P.w_u8 (u8(g_Group())); + + GameGraph::_GRAPH_ID l_game_vertex_id = ai_location().game_vertex_id(); + P.w (&l_game_vertex_id, sizeof(l_game_vertex_id)); + P.w (&l_game_vertex_id, sizeof(l_game_vertex_id)); +// P.w (&m_fGoingSpeed, sizeof(m_fGoingSpeed)); +// P.w (&m_fGoingSpeed, sizeof(m_fGoingSpeed)); + float f1 = 0; + if (ai().game_graph().valid_vertex_id(l_game_vertex_id)) { + f1 = Position().distance_to (ai().game_graph().vertex(l_game_vertex_id)->level_point()); + P.w (&f1, sizeof(f1)); + f1 = Position().distance_to (ai().game_graph().vertex(l_game_vertex_id)->level_point()); + P.w (&f1, sizeof(f1)); + } + else { + P.w (&f1, sizeof(f1)); + P.w (&f1, sizeof(f1)); + } + + CEatableItem::net_Export(P); +} + +void CAI_Rat::net_Import(NET_Packet& P) +{ + R_ASSERT (Remote()); + net_update N; + + u8 flags; + + float health; + P.r_float (health); + SetfHealth (health); + + P.r_u32 (N.dwTimeStamp); + P.r_u8 (flags); + P.r_vec3 (N.p_pos); + P.r_angle8 (N.o_model); + P.r_angle8 (N.o_torso.yaw); + P.r_angle8 (N.o_torso.pitch); + P.r_angle8 (N.o_torso.roll ); + id_Team = P.r_u8(); + id_Squad = P.r_u8(); + id_Group = P.r_u8(); + + GameGraph::_GRAPH_ID t; + P.r (&t, sizeof(t)); + P.r (&t, sizeof(t)); + ai_location().game_vertex (t); + + if (NET.empty() || (NET.back().dwTimeStampadd_Box(box); + //Fsphere sphere; + //sphere.P.set(0,0,0); + //sphere.R=0.25; + //element->add_Sphere(sphere); + element->setDensity(m_phMass); + element->SetMaterial(smart_cast(Visual())->LL_GetData(smart_cast(Visual())->LL_GetBoneRoot()).game_mtl_idx); + m_pPhysicsShell=P_create_Shell(); + m_pPhysicsShell->add_Element(element); + m_pPhysicsShell->Activate(XFORM(),0,XFORM()); + m_pPhysicsShell->set_PhysicsRefObject( this ); + if(!fsimilar(0.f,m_saved_impulse)){ + m_pPhysicsShell->applyHit(m_saved_hit_position,m_saved_hit_dir,m_saved_impulse,0,m_saved_hit_type); + } + /* + IKinematics* M = smart_cast(Visual()); VERIFY(M); + m_pPhysicsShell = P_create_Shell(); + + //get bone instance + int id=M->LL_BoneID("bip01_pelvis"); + CBoneInstance& instance=M->LL_GetBoneInstance (id); + + //create root element + CPhysicsElement* element=P_create_Element (); + element->mXFORM.identity(); + instance.set_callback(m_pPhysicsShell->GetBonesCallback(),element); + Fobb box; + box.m_rotate.identity(); + box.m_translate.set(0,0,0); + box.m_halfsize.set(0.10f,0.085f,0.25f); + element->add_Box(box); + + element->setDensity(200.f); + m_pPhysicsShell->add_Element(element); + element->SetMaterial("materials/skel1"); + + //set shell start position + Fmatrix m; + m.set(mRotate); + m.c.set(Position()); + m_pPhysicsShell->mXFORM.set(m); + */ +} + +void CAI_Rat::shedule_Update(u32 dt) +{ + if (!monster_squad().get_squad(this)->GetLeader() || !monster_squad().get_squad(this)->GetLeader()->g_Alive()) + { + monster_squad().get_squad(this)->SetLeader(this); + } + inherited::shedule_Update (dt); +} + +void CAI_Rat::UpdateCL () +{ + + /////////////////////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG + if (monster_squad().get_squad(this)->GetLeader() == this) { + + if (m_walk_on_way && m_path) draw_way(); + + } +#endif + /////////////////////////////////////////////////////////////////////////////////////// + + if (!m_pPhysicsShell && !g_Alive()) + CreateSkeleton (); + + if (!Useful()) { + inherited::UpdateCL (); + Exec_Look (Device.fTimeDelta); + + CMonsterSquad *squad = monster_squad().get_squad(this); + + if (squad && + ((squad->GetLeader() != this && !squad->GetLeader()->g_Alive()) || + squad->get_index(this) == u32(-1)) + ) squad->SetLeader(this); + + if (squad && + squad->SquadActive() && + squad->GetLeader() == this && + m_squad_count != squad->squad_alife_count() + ) + { + squad->set_rat_squad_index(squad->GetLeader()); + m_squad_count = squad->squad_alife_count(); + } + } + else { + if (!H_Parent() && m_pPhysicsShell && m_pPhysicsShell->isActive()) + m_pPhysicsShell->InterpolateGlobalTransform(&XFORM()); + + CPhysicsShellHolder::UpdateCL (); + CEatableItem::UpdateCL (); + } +} + +void CAI_Rat::UpdatePositionAnimation() +{ + + Fmatrix l_tSavedTransform = XFORM(); + m_fTimeUpdateDelta = Device.fTimeDelta; + move (m_bCanAdjustSpeed,m_bStraightForward); + float y,p,b; + XFORM().getHPB (y,p,b); + NET_Last.p_pos = Position(); + NET_Last.o_model = -y; + NET_Last.o_torso.yaw = -y; + NET_Last.o_torso.pitch = -p; + XFORM() = l_tSavedTransform; + + if (!bfScriptAnimation()) + SelectAnimation (XFORM().k,Fvector().set(1,0,0),m_fSpeed); +} + +//void CAI_Rat::Hit(float P,Fvector &dir,CObject*who,s16 element,Fvector p_in_object_space,float impulse, ALife::EHitType hit_type /*= ALife::eHitTypeWound*/) +void CAI_Rat::Hit (SHit* pHDS) +{ +// inherited::Hit (P,dir,who,element,p_in_object_space,impulse, hit_type); + inherited::Hit (pHDS); + if (!m_pPhysicsShell) { + m_saved_impulse = pHDS->impulse; + m_saved_hit_dir.set (pHDS->dir); + m_saved_hit_type = pHDS->hit_type; + m_saved_hit_position.set(pHDS->p_in_bone_space); + } + else { +// CEatableItem::Hit (P,dir,who,element,p_in_object_space,impulse, hit_type); + CEatableItem::Hit (pHDS); + } +} + +void CAI_Rat::feel_touch_new(CObject* O) +{ +} + +///////////////////////////////////// +// Rat as eatable item +///////////////////////////////////// +void CAI_Rat::OnH_A_Chield () +{ + inherited::OnH_A_Chield (); + CEatableItem::OnH_A_Chield (); +} + +void CAI_Rat::OnH_B_Chield () +{ + setVisible (FALSE); + setEnabled (FALSE); + + if (m_pPhysicsShell) + m_pPhysicsShell->Deactivate (); + + CEatableItem::OnH_B_Chield (); +} + +void CAI_Rat::OnH_B_Independent () +{ + CEatableItem::OnH_B_Independent (TRUE); + inherited::OnH_B_Independent (TRUE); + + if (!Useful()) + return; + + setVisible (TRUE); + setEnabled (TRUE); + + if (m_pPhysicsShell) + activate_physic_shell (); +} + +void CAI_Rat::OnH_A_Independent () +{ + CEatableItem::OnH_A_Independent (); + inherited::OnH_A_Independent (); +} + +bool CAI_Rat::Useful() const +{ + if (!g_Alive()) + { + return CEatableItem::Useful(); + } + + return false; +} + +#ifdef DEBUG +void CAI_Rat::OnRender() +{ +// inherited::OnRender(); +// CEatableItem::OnRender(); +} +#endif + +BOOL CAI_Rat::UsedAI_Locations() +{ + return (TRUE); +} + +void CAI_Rat::make_Interpolation () +{ + inherited::make_Interpolation(); + CEatableItem::make_Interpolation(); +} + +void CAI_Rat::PH_B_CrPr () +{ + inherited::PH_B_CrPr (); + CEatableItem::PH_B_CrPr (); +} + +void CAI_Rat::PH_I_CrPr () +{ + inherited::PH_I_CrPr (); + CEatableItem::PH_I_CrPr (); +} + +#ifdef DEBUG +void CAI_Rat::PH_Ch_CrPr () +{ + inherited::PH_Ch_CrPr (); + CEatableItem::PH_Ch_CrPr (); +} +#endif + + +void CAI_Rat::PH_A_CrPr () +{ + inherited::PH_A_CrPr (); + CEatableItem::PH_A_CrPr (); +} + +void CAI_Rat::create_physic_shell() +{ + // do not delete!!! +} + +void CAI_Rat::setup_physic_shell() +{ + // do not delete!!! +} + +void CAI_Rat::activate_physic_shell () +{ + CEatableItem::activate_physic_shell(); +} + +void CAI_Rat::on_activate_physic_shell () +{ + CObject *object = smart_cast(H_Parent()); + R_ASSERT (object); + XFORM().set (object->XFORM()); + inherited::activate_physic_shell(); +} + +float CAI_Rat::get_custom_pitch_speed (float def_speed) +{ + if (fsimilar(m_fSpeed,0.f)) + return (PI_DIV_6); + else + if (fsimilar(m_fSpeed,m_fMinSpeed)) + return (PI_DIV_4); + else + if (fsimilar(m_fSpeed,m_fMaxSpeed)) + return (PI_DIV_3); + else + if (fsimilar(m_fSpeed,m_fAttackSpeed)) + return (PI_DIV_2); + + Debug.fatal (DEBUG_INFO,"Impossible RAT speed!"); + return (PI_DIV_2); +} + +BOOL CAI_Rat::renderable_ShadowReceive () +{ + return TRUE; +} + +BOOL CAI_Rat::renderable_ShadowGenerate () +{ + return FALSE; +} + +DLL_Pure *CAI_Rat::_construct () +{ + CCustomMonster::_construct (); + CEatableItem::_construct (); + return (this); +} diff --git a/src/xrGameLA/ai/rats/ai_rat_animations.cpp b/src/xrGameLA/ai/rats/ai_rat_animations.cpp new file mode 100644 index 000000000..ccb718734 --- /dev/null +++ b/src/xrGameLA/ai/rats/ai_rat_animations.cpp @@ -0,0 +1,101 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_animations.cpp +// Created : 21.06.2002 +// Modified : 06.11.2002 +// Author : Dmitriy Iassenev +// Description : Animations, Bone transformations and Sounds for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_rat.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../ai_debug.h" +#include "../../../movement_manager.h" + +#define MIN_TURN_ANGLE PI_DIV_6*.5f + +// animations +void CAI_Rat::load_animations () +{ + IKinematicsAnimated* tpVisualObject = smart_cast(Visual()); + + // loading normal animations + m_tRatAnimations.tNormal.tGlobal.tpaDeath[0] = tpVisualObject->ID_Cycle("norm_death"); + m_tRatAnimations.tNormal.tGlobal.tpaDeath[1] = tpVisualObject->ID_Cycle("norm_death_2"); + + m_tRatAnimations.tNormal.tGlobal.tpaAttack[0] = tpVisualObject->ID_Cycle("attack_1"); + m_tRatAnimations.tNormal.tGlobal.tpaAttack[1] = tpVisualObject->ID_Cycle("attack_2"); + m_tRatAnimations.tNormal.tGlobal.tpaAttack[2] = tpVisualObject->ID_Cycle("attack_3"); + + m_tRatAnimations.tNormal.tGlobal.tpaIdle[0] = tpVisualObject->ID_Cycle("norm_idle_1"); + m_tRatAnimations.tNormal.tGlobal.tpaIdle[1] = tpVisualObject->ID_Cycle("norm_idle_2"); + + m_tRatAnimations.tNormal.tGlobal.tpTurnLeft = tpVisualObject->ID_Cycle("norm_turn_ls"); + m_tRatAnimations.tNormal.tGlobal.tpTurnRight = tpVisualObject->ID_Cycle("norm_turn_rs"); + + m_tRatAnimations.tNormal.tGlobal.tWalk.Create(tpVisualObject, "norm_walk"); + + m_tRatAnimations.tNormal.tGlobal.tRun.Create(tpVisualObject, "norm_run"); + m_tRatAnimations.tNormal.tGlobal.tRunAttack = tpVisualObject->ID_Cycle("norm_run_fwd_1"); + + tpVisualObject->PlayCycle(m_tRatAnimations.tNormal.tGlobal.tpaIdle[0]); +} + +void CAI_Rat::SelectAnimation(const Fvector& /**_view/**/, const Fvector& /**_move/**/, float /**speed/**/) +{ + IKinematicsAnimated *tpVisualObject = smart_cast(Visual()); + MotionID tpGlobalAnimation; + + if (!g_Alive()) { + for (int i=0 ;i<2; ++i) { + if (m_tRatAnimations.tNormal.tGlobal.tpaDeath[i] == m_tpCurrentGlobalAnimation) { + tpGlobalAnimation = m_tpCurrentGlobalAnimation; + break; + } + } + if (!tpGlobalAnimation) { + if (m_tpCurrentGlobalAnimation == m_tRatAnimations.tNormal.tGlobal.tpaIdle[1]) + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaDeath[0]; + else + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaDeath[::Random.randI(0,2)]; + } + } + else { + if (m_bFiring) + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaAttack[2]; + else + if (angle_difference(movement().m_body.target.yaw,movement().m_body.current.yaw) <= MIN_TURN_ANGLE) + if (m_fSpeed < 0.2f) { + if (m_bStanding) + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaIdle[1]; + else + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaIdle[0]; + } + else + if (_abs(m_fSpeed - m_fAttackSpeed) < EPS_L) + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tRunAttack; + else + if (_abs(m_fSpeed - m_fMaxSpeed) < EPS_L) + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tRun.fwd; + else + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tWalk.fwd; + else { + if (left_angle(-movement().m_body.target.yaw,-movement().m_body.current.yaw)) +// tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaIdle[0]; + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpTurnLeft; + else +// tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpaIdle[0]; + tpGlobalAnimation = m_tRatAnimations.tNormal.tGlobal.tpTurnRight; + } + } + + if (tpGlobalAnimation != m_tpCurrentGlobalAnimation) + m_tpCurrentGlobalBlend = tpVisualObject->PlayCycle(m_tpCurrentGlobalAnimation = tpGlobalAnimation); + +#ifdef DEBUG + if (psAI_Flags.is(aiAnimation)) { + IKinematicsAnimated *skeleton_animated = smart_cast(Visual()); + Msg ("%6d %s animation : %s (%f,%f)",Device.dwTimeGlobal,"Global",skeleton_animated->LL_MotionDefName_dbg(m_tpCurrentGlobalAnimation),movement().m_body.current.yaw,movement().m_body.target.yaw); + } +#endif +} diff --git a/src/xrGameLA/ai/rats/ai_rat_behaviour.cpp b/src/xrGameLA/ai/rats/ai_rat_behaviour.cpp new file mode 100644 index 000000000..e12a5bbba --- /dev/null +++ b/src/xrGameLA/ai/rats/ai_rat_behaviour.cpp @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_fsm.cpp +// Created : 25.04.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_rat.h" +#include "rat_state_manager.h" +#include "ai_space.h" +#include "game_level_cross_table.h" +#include "ai_object_location.h" +#include "game_graph.h" + +void CAI_Rat::update_home_position () +{ + if (!g_Alive()) + return; + + CEntity *leader = Level().seniority_holder().team(g_Team()).squad(g_Squad()).leader(); + VERIFY (leader); + + if (ID() != leader->ID()) { + CAI_Rat *rat_leader = smart_cast(leader); + VERIFY (rat_leader); + if (m_home_position.distance_to(rat_leader->m_home_position) > EPS_L) + add_active_member (true); + + m_home_position = rat_leader->m_home_position; + } + + if (Device.dwTimeGlobal < m_time_to_change_graph_point) + return; + + if (ai().cross_table().vertex(ai_location().level_vertex_id()).game_vertex_id() != m_next_graph_point) + return; + + m_next_graph_point = ai().cross_table().vertex(ai_location().level_vertex_id()).game_vertex_id(); + select_next_home_position (); + m_home_position.set (ai().game_graph().vertex(m_next_graph_point)->level_point()); +} + +void CAI_Rat::Think() +{ + update_morale (); + update_home_position (); + m_state_manager->update (); +} \ No newline at end of file diff --git a/src/xrGameLA/ai/rats/ai_rat_feel.cpp b/src/xrGameLA/ai/rats/ai_rat_feel.cpp new file mode 100644 index 000000000..772f1f59e --- /dev/null +++ b/src/xrGameLA/ai/rats/ai_rat_feel.cpp @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_feel.cpp +// Created : 23.07.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : Visibility and look for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_rat.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../../xrServerEntities/ai_sounds.h" + +BOOL CAI_Rat::feel_vision_isRelevant(CObject* O) +{ + CEntityAlive* E = smart_cast (O); + if (!E) return FALSE; + if ((E->g_Team() == g_Team()) && (E->g_Alive())) return FALSE; + return TRUE; +} + +void CAI_Rat::feel_sound_new(CObject* who, int eType, CSound_UserDataPtr user_data, const Fvector &Position, float power) +{ + if (!g_Alive()) + return; + + if ((eType & SOUND_TYPE_WEAPON_SHOOTING) == SOUND_TYPE_WEAPON_SHOOTING) + power = 1.f; + + if (power >= m_fSoundThreshold) { + if ((this != who) && ((m_tLastSound.dwTime <= m_dwLastUpdateTime) || (m_tLastSound.fPower <= power))) { + m_tLastSound.eSoundType = ESoundTypes(eType); + m_tLastSound.dwTime = Device.dwTimeGlobal; + m_tLastSound.fPower = power; + m_tLastSound.tSavedPosition = Position; + m_tLastSound.tpEntity = smart_cast(who); + if ((eType & SOUND_TYPE_MONSTER_DYING) == SOUND_TYPE_MONSTER_DYING) + m_fMorale += m_fMoraleDeathQuant; + else + if (((eType & SOUND_TYPE_WEAPON_SHOOTING) == SOUND_TYPE_WEAPON_SHOOTING) && !memory().enemy().selected()) + m_fMorale += m_fMoraleFearQuant;///fDistance; + else + if ((eType & SOUND_TYPE_MONSTER_ATTACKING) == SOUND_TYPE_MONSTER_ATTACKING) + m_fMorale += m_fMoraleSuccessAttackQuant;///fDistance; + } + } + + inherited::feel_sound_new (who,eType,user_data,Position,power); +} + +BOOL CAI_Rat::feel_touch_on_contact (CObject *O) +{ + return (inherited::feel_touch_on_contact(O)); +} diff --git a/src/xrGameLA/ai/rats/ai_rat_fire.cpp b/src/xrGameLA/ai/rats/ai_rat_fire.cpp new file mode 100644 index 000000000..8b0cddcf0 --- /dev/null +++ b/src/xrGameLA/ai/rats/ai_rat_fire.cpp @@ -0,0 +1,159 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_fire.cpp +// Created : 23.07.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : Fire and enemy parameters for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_rat.h" +#include "ai_rat_impl.h" +#include "ai_rat_space.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../item_manager.h" +#include "../../../sound_player.h" + +using namespace RatSpace; + +void CAI_Rat::Exec_Action(float /**dt/**/) +{ + switch (m_tAction) { + case eRatActionAttackBegin : { + u32 dwTime = Device.dwTimeGlobal; + sound().play (eRatSoundAttack);//,0,0,m_dwHitInterval+1,m_dwHitInterval); + if (memory().enemy().selected() && memory().enemy().selected()->g_Alive() && (dwTime - m_dwStartAttackTime > m_dwHitInterval)) { + m_bActionStarted = true; + m_dwStartAttackTime = dwTime; + Fvector tDirection; + Fvector position_in_bone_space; + position_in_bone_space.set(0.f,0.f,0.f); + tDirection.sub(memory().enemy().selected()->Position(),this->Position()); + vfNormalizeSafe(tDirection); + + if (this->Local() && memory().enemy().selected()) { + CEntityAlive *entity_alive = const_cast(memory().enemy().selected()); + VERIFY (entity_alive); + +// entity_alive->Hit(m_fHitPower,tDirection,this,0,position_in_bone_space,0); + u16 id_to = entity_alive->ID(); + u16 id_from = ID(); + NET_Packet l_P; + SHit HS; + HS.GenHeader(GE_HIT, id_to); // u_EventGen (l_P,GE_HIT, id_to); + HS.whoID = (id_from); // l_P.w_u16 (id_from); + HS.weaponID = (id_from); // l_P.w_u16 (id_from); + HS.dir = (tDirection); // l_P.w_dir (tDirection); + HS.power = (m_fHitPower); // l_P.w_float (m_fHitPower); + HS.boneID = (0); // l_P.w_s16 (0); + HS.p_in_bone_space = (position_in_bone_space); // l_P.w_vec3 (position_in_bone_space); + HS.impulse = (0.f); // l_P.w_float (0.f); + HS.hit_type = (ALife::eHitTypeWound); // l_P.w_u16 ((u16)ALife::eHitTypeWound); + HS.Write_Packet(l_P); + u_EventSend (l_P); + } + } + else + m_bActionStarted = false; + break; + } + case eRatActionAttackEnd : { + m_bActionStarted = false; + break; + } + default: + break; + } +} + +void CAI_Rat::HitSignal(float amount, Fvector& vLocalDir, CObject* who, s16 /**element/**/) +{ + // Save event + Fvector D; + XFORM().transform_dir(D,vLocalDir); + m_hit_time = Device.dwTimeGlobal; + m_hit_direction.set(D); + m_hit_direction.normalize(); + m_tHitPosition = who->Position(); + + // Play hit sound + if (!AlreadyDie()) + sound().play (eRatSoundInjuring); +} + +bool CAI_Rat::useful (const CItemManager *manager, const CGameObject *object) const +{ + if (g_Alive()) + return (false); + + if (!memory().item().useful(object)) + return (false); + + const CEntityAlive *entity_alive = smart_cast(object); + if (!entity_alive) + return (false); + + return (true); +} + +float CAI_Rat::evaluate (const CItemManager *manager, const CGameObject *object) const +{ + const CEntityAlive *entity_alive = smart_cast(object); + VERIFY (entity_alive); + if (!entity_alive->g_Alive()) { + if ((Device.dwTimeGlobal - entity_alive->GetLevelDeathTime() < m_dwEatCorpseInterval) && (entity_alive->m_fFood > 0) && (m_bEatMemberCorpses || (entity_alive->g_Team() != g_Team())) && (m_bCannibalism || (entity_alive->CLS_ID != CLS_ID))) + return (entity_alive->m_fFood*entity_alive->m_fFood)*Position().distance_to(entity_alive->Position()); + else + return (flt_max); + } + else + return (flt_max); +} + +void CAI_Rat::update_morale () +{ + u32 dwCurTime = Device.dwTimeGlobal; + clamp (m_fMorale, m_fMoraleMinValue, m_fMoraleMaxValue); + + if (dwCurTime - m_dwMoraleLastUpdateTime <= m_dwMoraleRestoreTimeInterval) + return; + + m_dwMoraleLastUpdateTime = dwCurTime; + float fDistance = Position().distance_to(m_home_position); + fDistance = fDistance < 1.f ? 1.f : fDistance; + switch (m_eCurrentState) { + case aiRatFreeActive : + case aiRatFreePassive : { + if (m_fMorale < m_fMoraleNormalValue) { + m_fMorale += m_fMoraleRestoreQuant;//*(1.f - fDistance/m_fMoraleNullRadius); + if (m_fMorale > m_fMoraleNormalValue) + m_fMorale = m_fMoraleNormalValue; + } + else + if (m_fMorale > m_fMoraleNormalValue) { + m_fMorale -= m_fMoraleRestoreQuant;//*(fDistance/m_fMoraleNullRadius); + if (m_fMorale < m_fMoraleNormalValue) + m_fMorale = m_fMoraleNormalValue; + } + break; + } + case aiRatUnderFire : + case aiRatRetreat : { + //m_fMorale += fDistance <= m_fMoraleNullRadius ? m_fMoraleRestoreQuant : 0; + //m_fMorale += m_fMoraleRestoreQuant*(m_fMoraleNullRadius/fDistance); + m_fMorale += m_fMoraleRestoreQuant; + break; + } + case aiRatAttackRange : + case aiRatAttackMelee : + case aiRatReturnHome : { + //m_fMorale += m_fMoraleRestoreQuant*(1.f - fDistance/m_fMoraleNullRadius); + //m_fMorale += m_fMoraleRestoreQuant*(m_fMoraleNullRadius/fDistance); + m_fMorale += m_fMoraleRestoreQuant; + break; + } + } + + clamp (m_fMorale, m_fMoraleMinValue, m_fMoraleMaxValue); +} diff --git a/src/xrGameLA/ai/rats/ai_rat_fsm.cpp b/src/xrGameLA/ai/rats/ai_rat_fsm.cpp new file mode 100644 index 000000000..a48b641f6 --- /dev/null +++ b/src/xrGameLA/ai/rats/ai_rat_fsm.cpp @@ -0,0 +1,661 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_fsm.cpp +// Created : 25.04.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../game_level_cross_table.h" +#include "../../../game_graph.h" +#include "ai_rat_space.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../item_manager.h" +#include "../../../memory_space.h" +#include "../../../ai_object_location.h" +#include "../../../movement_manager.h" +#include "../../../sound_player.h" +#include "ai_rat_impl.h" +#include "../../../ai_space.h" + +using namespace RatSpace; + +#undef WRITE_TO_LOG +#define WRITE_TO_LOG(s) m_bStopThinking = true; +/* if (!visible_objects().size())\ +Msg("* No objects in frustum",visible_objects().size());\ +else {\ +Msg("* Objects in frustum (%d) :",visible_objects().size());\ +for (int i=0; i<(int)visible_objects().size(); ++i)\ +Msg("* %s",*visible_objects()[i]->cName());\ +}\ +/** +#define WRITE_TO_LOG(s) {\ + Msg("Monster %s : \n* State : %s\n* Time delta : %7.3f\n* Global time : %7.3f",*cName(),s,m_fTimeUpdateDelta,float(Device.dwTimeGlobal)/1000.f);\ + m_bStopThinking = true;\ +} +/**/ + +#ifndef DEBUG + #undef WRITE_TO_LOG + #define WRITE_TO_LOG(s) m_bStopThinking = true; +#endif + +void CAI_Rat::Think() +{ + m_thinking = true; + update_morale (); + vfUpdateSpawnPosition(); + m_bStopThinking = false; + do { + m_ePreviousState = m_eCurrentState; + switch(m_eCurrentState) { + case aiRatDie : { + Death(); + break; + } + case aiRatFreeActive : { //aiRatFreeHuntingActive + FreeHuntingActive(); + break; + } + case aiRatFreePassive : { // aiRatFreeHuntingPassive + FreeHuntingPassive(); + break; + } + case aiRatAttackRange : { //aiRatAttackFire + AttackFire(); + break; + } + case aiRatAttackMelee : { //aiRatAttackRun + AttackRun(); + break; + } + case aiRatUnderFire : { + UnderFire(); + break; + } + case aiRatRetreat : { + Retreat(); + break; + } + case aiRatPursuit : { + Pursuit(); + break; + } + case aiRatFreeRecoil : { + FreeRecoil(); + break; + } + case aiRatReturnHome : { + ReturnHome(); + break; + } + case aiRatEatCorpse : { + EatCorpse(); + break; + } + } + m_bStateChanged = m_ePreviousState != m_eCurrentState; + } + while (!m_bStopThinking); + m_thinking = false; +} + +void CAI_Rat::Death() +{ + //WRITE_TO_LOG ("Dying..."); + vfSetFire (false); + + m_bStopThinking = true; + + vfSetFire (false); + + SelectAnimation (XFORM().k,movement().detail().direction(),0); + + if (m_fFood <= 0) { + if (m_previous_query_time <= GetLevelDeathTime()) + m_previous_query_time = Device.dwTimeGlobal; + setVisible(false); + if (Device.dwTimeGlobal - m_previous_query_time > 10000) { +// setEnabled (FALSE); +// NET_Packet P; +// u_EventGen (P,GE_DESTROY,ID()); +// u_EventSend (P); + } + } + +} + +void CAI_Rat::FreeHuntingActive() +{ + WRITE_TO_LOG("Free hunting active"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE((memory().enemy().selected() && ((memory().enemy().selected()->Position().distance_to(m_tSafeSpawnPosition) < m_fMaxPursuitRadius) || (Position().distance_to(m_tSafeSpawnPosition) > m_fMaxHomeRadius))),aiRatAttackRun); + + CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE(m_fMorale < m_fMoraleNormalValue,aiRatUnderFire); + + if ( + (m_tLastSound.dwTime >= m_dwLastUpdateTime) && + ( + !m_tLastSound.tpEntity || + ( + (!memory().item().selected() || (memory().item().selected()->ID() != m_tLastSound.tpEntity->ID())) && + (m_tLastSound.tpEntity->g_Team() != g_Team()) + ) + ) && + !memory().enemy().selected() + ) + { + SWITCH_TO_NEW_STATE_THIS_UPDATE(aiRatFreeRecoil); + } + + CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE(memory().item().selected(),aiRatEatCorpse); + + m_tSpawnPosition.set (m_tSafeSpawnPosition); + m_fGoalChangeDelta = m_fSafeGoalChangeDelta; + m_tVarGoal.set (m_tGoalVariation); + m_fASpeed = m_fAngleSpeed; + + if (bfCheckIfGoalChanged()) { + if (m_bStateChanged || (Position().distance_to(m_tSpawnPosition) > m_fStableDistance) || (::Random.randF(0,1) > m_fChangeActiveStateProbability)) + if (Position().distance_to(m_tSafeSpawnPosition) > m_fMaxHomeRadius) + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + else + vfChooseNewSpeed(); + else { + if (can_stand_here()) + vfRemoveActiveMember(); + } + } + + if (m_bStateChanged || (fis_zero(m_fSpeed) && (angle_difference(movement().m_body.target.yaw,movement().m_body.current.yaw) < PI_DIV_6))) + vfChooseNewSpeed(); + + vfUpdateTime(m_fTimeUpdateDelta); + + vfComputeNewPosition(false); + + sound().play (eRatSoundVoice,45*1000,15*1000); +} + +void CAI_Rat::FreeHuntingPassive() +{ + WRITE_TO_LOG("Free hunting passive"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + if (memory().enemy().selected()) { + m_fGoalChangeTime = 0; + add_active_member(true); + m_bStopThinking = false; + return; + } + + if (m_fMorale < m_fMoraleNormalValue) { + add_active_member(true); + m_bStopThinking = false; + return; + } + + if ((m_tLastSound.dwTime >= m_dwLastUpdateTime) && ((!m_tLastSound.tpEntity) || (m_tLastSound.tpEntity->g_Team() != g_Team()))) { + add_active_member(true); + m_bStopThinking = false; + return; + } + + m_fSpeed = 0.f; + + vfAddStandingMember(); + add_active_member(); + + sound().play (eRatSoundVoice,45*1000,15*1000); +} + +void CAI_Rat::UnderFire() +{ + WRITE_TO_LOG("UnderFire"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + // Msg ("%6d : Rat %s, %f -> %f [%f]",Device.dwTimeGlobal,*cName(),movement().m_body.current.pitch,movement().m_body.target.pitch,get_custom_pitch_speed(0.f)); + + vfSetFire(false); + + if (memory().enemy().selected()) { + GO_TO_NEW_STATE_THIS_UPDATE(aiRatAttackRun); + } + else { + if (m_tLastSound.dwTime >= m_dwLastUpdateTime) { + if (m_tLastSound.tpEntity && (m_tLastSound.tpEntity->g_Team() != g_Team()) && (!bfCheckIfSoundFrightful())) { + SWITCH_TO_NEW_STATE(aiRatAttackRun); + } + m_previous_query_time = Device.dwTimeGlobal; + if (m_bStateChanged) { + Fvector tTemp; + tTemp.setHP(-movement().m_body.current.yaw,-movement().m_body.current.pitch); + tTemp.normalize_safe(); + tTemp.mul(m_fUnderFireDistance); + m_tSpawnPosition.add(Position(),tTemp); + } + } + if (m_fMorale >= m_fMoraleNormalValue - EPS_L) { + GO_TO_PREV_STATE; + } + } + + vfUpdateTime(m_fTimeUpdateDelta); + + if (m_bStateChanged)//(Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir = m_tSpawnPosition; + + m_fSpeed = m_fAttackSpeed; + + vfComputeNewPosition(true,true); +} + +void CAI_Rat::AttackFire() +{ + WRITE_TO_LOG("Attacking enemy..."); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + // Msg ("%6d : Rat %s, %f -> %f [%f]",Device.dwTimeGlobal,*cName(),movement().m_body.current.pitch,movement().m_body.target.pitch,get_custom_pitch_speed(0.f)); + + //ERatStates eState = ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),m_eCurrentState,m_eCurrentState,aiRatRetreat,this,30.f)); + //if (eState != m_eCurrentState) + // GO_TO_NEW_STATE_THIS_UPDATE(eState); + + CHECK_IF_GO_TO_PREV_STATE(!memory().enemy().selected()); + + CHECK_IF_GO_TO_NEW_STATE((memory().enemy().selected()->Position().distance_to(Position()) > m_fAttackDistance),aiRatAttackRun) + + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + SRotation sTemp; + mk_rotation(tTemp,sTemp); + + CHECK_IF_GO_TO_NEW_STATE(angle_difference(movement().m_body.current.yaw,sTemp.yaw) > m_fAttackAngle,aiRatAttackRun) + + Fvector tDistance; + tDistance.sub (Position(),memory().enemy().selected()->Position()); + + m_fSpeed = 0.f; + + vfSetFire (true); + + vfSetMovementType(0); +} + +void CAI_Rat::AttackRun() +{ + WRITE_TO_LOG("Attack enemy"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + // Msg ("%6d : Rat %s, %f -> %f [%f]",Device.dwTimeGlobal,*cName(),movement().m_body.current.pitch,movement().m_body.target.pitch,get_custom_pitch_speed(0.f)); + vfSetFire(false); + + ERatStates eState = ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),m_eCurrentState,m_eCurrentState,m_eCurrentState,aiRatRetreat,aiRatRetreat,this,30.f)); + if (eState != m_eCurrentState) { + eState = ERatStates(dwfChooseAction(0*m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),m_eCurrentState,m_eCurrentState,m_eCurrentState,aiRatRetreat,aiRatRetreat,this,30.f)); + GO_TO_NEW_STATE_THIS_UPDATE(eState); + } + + CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE(memory().enemy().selected() && (m_tSafeSpawnPosition.distance_to(memory().enemy().selected()->Position()) > m_fMaxPursuitRadius),aiRatReturnHome); + + CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE(!memory().enemy().selected()); + + Fvector tDistance; + tDistance.sub(Position(),memory().enemy().selected()->Position()); + + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + SRotation sTemp; + mk_rotation(tTemp,sTemp); + + CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE((memory().enemy().selected()->Position().distance_to(Position()) <= m_fAttackDistance) && (angle_difference(movement().m_body.target.yaw, sTemp.yaw) <= m_fAttackAngle),aiRatAttackFire) + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) { + m_tGoalDir.set(memory().enemy().selected()->Position()); + } + + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + vfComputeNewPosition(true,true); +} + +void CAI_Rat::Retreat() +{ + WRITE_TO_LOG("Retreat"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + if (!memory().enemy().selected() || + (memory().enemy().selected() && + ( + !memory().enemy().selected()->g_Alive() + || + ( + (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time > m_dwRetreatTime) && + ( + (m_tLastSound.dwTime < m_dwLastUpdateTime) || + !m_tLastSound.tpEntity || + (m_tLastSound.tpEntity->g_Team() == g_Team()) || + !bfCheckIfSoundFrightful() + ) + ) + ) + ) + ) + { + memory().enable (memory().enemy().selected(),false); + GO_TO_PREV_STATE; + } + + if (memory().enemy().selected() && memory().enemy().selected()->g_Alive()) { + ERatStates eState = ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),aiRatAttackRun,aiRatAttackRun,aiRatAttackRun,aiRatRetreat,aiRatRetreat,this,30.f)); + // ERatStates eState = ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),aiRatAttackRun,aiRatAttackRun,aiRatAttackRun,aiRatAttackRun,aiRatAttackRun,this,30.f)); + if (eState != m_eCurrentState) { + eState = ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),aiRatAttackRun,aiRatAttackRun,aiRatAttackRun,aiRatRetreat,aiRatRetreat,this,30.f)); + GO_TO_NEW_STATE_THIS_UPDATE(eState); + } + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + SRotation sTemp; + mk_rotation(tTemp,sTemp); + + if ((memory().enemy().selected()->Position().distance_to(Position()) <= m_fAttackDistance) && (angle_difference(movement().m_body.target.yaw, sTemp.yaw) > m_fAttackAngle)) { + m_fSpeed = 0.f; + return; + } + + CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE((memory().enemy().selected()->Position().distance_to(Position()) <= m_fAttackDistance) && (angle_difference(movement().m_body.target.yaw, sTemp.yaw) <= m_fAttackAngle),aiRatAttackFire) + + tTemp.sub(Position(),memory().enemy().selected()->Position()); + tTemp.normalize_safe(); + tTemp.mul(m_fRetreatDistance); + m_tSpawnPosition.add(Position(),tTemp); + } + + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir = m_tSpawnPosition; + + vfComputeNewPosition(true,true); +} + +void CAI_Rat::Pursuit() +{ + WRITE_TO_LOG("Pursuit something"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + if (memory().enemy().selected() && (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time >= m_dwLostMemoryTime)) { + memory().enable(memory().enemy().selected(),false); + GO_TO_PREV_STATE_THIS_UPDATE; + } + + CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE(memory().enemy().selected(),aiRatAttackRun); + + CHECK_IF_SWITCH_TO_NEW_STATE_THIS_UPDATE((m_fMorale < m_fMoraleNormalValue),aiRatUnderFire); + + // if ((m_tLastSound.dwTime >= m_dwLastUpdateTime) && ((m_tLastSound.eSoundType & SOUND_TYPE_WEAPON_BULLET_HIT) == SOUND_TYPE_WEAPON_BULLET_HIT)) { + if ( + (m_tLastSound.dwTime >= m_dwLastUpdateTime) && + ( + !m_tLastSound.tpEntity || + ( + (!memory().item().selected() || (memory().item().selected()->ID() != m_tLastSound.tpEntity->ID())) && + (m_tLastSound.tpEntity->g_Team() != g_Team()) + ) + ) && + !memory().enemy().selected() + ) + { + GO_TO_NEW_STATE_THIS_UPDATE(aiRatFreeRecoil); + } + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set(memory().memory(memory().enemy().selected()).m_object_params.m_position); + + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + vfComputeNewPosition(true,true); +} + +void CAI_Rat::FreeRecoil() +{ + WRITE_TO_LOG("Free hunting : Recoil from something"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + if (m_bStateChanged) { + m_dwLostRecoilTime = Device.dwTimeGlobal; + m_tRecoilPosition = m_tLastSound.tSavedPosition; + } + + vfSetFire(false); + + CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE(memory().enemy().selected()); + + CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE(m_dwLastUpdateTime > m_dwLostRecoilTime + 2000); + + CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE(memory().enemy().selected() && (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time >= m_dwLostRecoilTime),aiRatPursuit); + + if (m_bStateChanged) { + Fvector tTemp; + tTemp.setHP(-movement().m_body.current.yaw,-movement().m_body.current.pitch); + tTemp.normalize_safe(); + tTemp.mul(m_fUnderFireDistance); + m_tSpawnPosition.add(Position(),tTemp); + } + // else + // if (m_dwLastUpdateTime > m_dwLostRecoilTime + 2000) + // m_tSpawnPosition = m_tGoalDir = m_tRecoilPosition; + // + // m_fSafeSpeed = m_fSpeed = m_fMaxSpeed; + // + // vfUpdateTime(m_fTimeUpdateDelta); + // + // if (m_dwLastUpdateTime > m_dwLostRecoilTime + 2000) { + // m_fASpeed = m_fAngleSpeed; + // m_fSafeSpeed = m_fSpeed = m_fMinSpeed; + // bfCheckIfGoalChanged (); + // vfComputeNewPosition (false); + // } + // else + // vfComputeNewPosition(true,true); + // if (m_dwLastUpdateTime > m_dwLostRecoilTime + 2000) { + // m_tSpawnPosition = m_tRecoilPosition; + // m_fGoalChangeDelta = 1000;//m_fSafeGoalChangeDelta; + // m_tVarGoal.set (m_tGoalVariation); + // m_tVarGoal.mul (.1f); + // m_fASpeed = m_fAngleSpeed; + // m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + // + // bfCheckIfGoalChanged (); + // vfUpdateTime (m_fTimeUpdateDelta); + // vfComputeNewPosition (false); + // } + // else + { + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + vfComputeNewPosition (true,true); + } + + sound().play (eRatSoundVoice,45*1000,15*1000); +} + +void CAI_Rat::ReturnHome() +{ + WRITE_TO_LOG("Returning home"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + if (memory().enemy().selected() && (m_tSafeSpawnPosition.distance_to(memory().enemy().selected()->Position()) < m_fMaxPursuitRadius)) { + m_fGoalChangeTime = 0; + SWITCH_TO_NEW_STATE_THIS_UPDATE(aiRatAttackRun) + } + + if (memory().enemy().selected()) { + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + SRotation sTemp; + mk_rotation(tTemp,sTemp); + + if ((memory().enemy().selected()->Position().distance_to(Position()) <= m_fAttackDistance) && (angle_difference(movement().m_body.target.yaw, sTemp.yaw) > m_fAttackAngle)) { + m_fSpeed = 0.f; + return; + } + + CHECK_IF_GO_TO_NEW_STATE_THIS_UPDATE((memory().enemy().selected()->Position().distance_to(Position()) <= m_fAttackDistance) && (angle_difference(movement().m_body.target.yaw, sTemp.yaw) <= m_fAttackAngle),aiRatAttackFire) + } + + CHECK_IF_GO_TO_PREV_STATE(!memory().enemy().selected() || !memory().enemy().selected()->g_Alive() || Position().distance_to(m_tSafeSpawnPosition) < m_fMaxHomeRadius); + + m_tSpawnPosition.set (m_tSafeSpawnPosition); + m_fGoalChangeDelta = m_fSafeGoalChangeDelta; + m_tVarGoal.set (m_tGoalVariation); + m_fASpeed = m_fAngleSpeed; + m_fSpeed = m_fSafeSpeed = m_fAttackSpeed; + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set (m_tSafeSpawnPosition); + + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + vfComputeNewPosition(); +} + +void CAI_Rat::EatCorpse() +{ + WRITE_TO_LOG("Eating a corpse"); + + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return; + } + + vfSetFire(false); + + CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE((memory().enemy().selected() && ((memory().enemy().selected()->Position().distance_to(m_tSafeSpawnPosition) < m_fMaxPursuitRadius) || (Position().distance_to(m_tSafeSpawnPosition) > m_fMaxHomeRadius)))); + + CHECK_IF_GO_TO_PREV_STATE_THIS_UPDATE(!memory().item().selected() || (m_fMorale < m_fMoraleNormalValue)); + + m_fGoalChangeTime = 10.f; + + if ( + (m_tLastSound.dwTime >= m_dwLastUpdateTime) && + ( + !m_tLastSound.tpEntity || + ( + (!memory().item().selected() || (memory().item().selected()->ID() != m_tLastSound.tpEntity->ID())) && + (m_tLastSound.tpEntity->g_Team() != g_Team()) + ) + ) && + !memory().enemy().selected() + ) + { + SWITCH_TO_NEW_STATE_THIS_UPDATE(aiRatFreeRecoil); + } + + // IKinematicsAnimated *V= smart_cast(const_cast(memory().item().selected())->Visual()); + // R_ASSERT (V); + // u16 head_bone = V->LL_BoneID("bip01_head"); + // Fmatrix l_tMatrix; + // l_tMatrix.mul_43 (const_cast(memory().item().selected())->XFORM(),smart_cast(const_cast(memory().item().selected())->Visual())->LL_GetBoneInstance(head_bone).mTransform); + // Fvector temp_position = l_tMatrix.c; + Fvector temp_position; + memory().item().selected()->Center (temp_position); + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set (temp_position); + + vfUpdateTime (m_fTimeUpdateDelta); + + bool a = temp_position.distance_to(Position()) <= m_fAttackDistance; + Fvector direction; + direction.sub (temp_position,Position()); + float y,p; + direction.getHP (y,p); + if (a && angle_difference(y,-movement().m_body.current.yaw) < PI_DIV_6) { + m_fSpeed = 0; + if (Device.dwTimeGlobal - m_previous_query_time > m_dwHitInterval) { + m_previous_query_time = Device.dwTimeGlobal; + const CEntityAlive *const_corpse = smart_cast(memory().item().selected()); + VERIFY (const_corpse); + CEntityAlive *corpse = const_cast(const_corpse); + VERIFY (corpse); + corpse->m_fFood -= m_fHitPower/10.f; + } + m_bFiring = true; + vfComputeNewPosition (false); + sound().play (eRatSoundEat); + } + else { + sound().remove_active_sounds(u32(-1)); + if (!a) + m_fSpeed = m_fMaxSpeed; + else + m_fSpeed = 0.f; + vfComputeNewPosition (true,true); + } +} diff --git a/src/xrGameLA/ai/rats/ai_rat_templates.cpp b/src/xrGameLA/ai/rats/ai_rat_templates.cpp new file mode 100644 index 000000000..9fb4927da --- /dev/null +++ b/src/xrGameLA/ai/rats/ai_rat_templates.cpp @@ -0,0 +1,552 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_rat_templates.cpp +// Created : 23.07.2002 +// Modified : 07.11.2002 +// Author : Dmitriy Iassenev +// Description : Templates for monster "Rat" +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../game_graph.h" +#include "../../../magic_box3.h" +#include "../../../../Include/xrRender/RenderVisual.h" +#include "../../../ai_object_location.h" +#include "../../../level_graph.h" +#include "../../../movement_manager.h" +#include "../../../location_manager.h" +#include "../../../level.h" +#include "../../../random32.h" +#include "../../../ai_space.h" +#include "../../../restricted_object.h" +#include "../../../patrol_path.h" +#include "../../../debug_renderer.h" +#include "../ai_monster_squad_manager.h" +#include "../ai_monster_squad.h" + +IC bool CAI_Rat::bfCheckIfOutsideAIMap(Fvector &tTemp1) +{ + u32 dwNewNode = ai_location().level_vertex_id(); + const CLevelGraph::CVertex *tpNewNode = ai_location().level_vertex(); + CLevelGraph::CPosition QueryPos; + if (!ai().level_graph().valid_vertex_position(tTemp1)) + return (false); + ai().level_graph().vertex_position(QueryPos,tTemp1); + if (!ai().level_graph().valid_vertex_id(dwNewNode) || !ai().level_graph().inside(*ai_location().level_vertex(),QueryPos)) { + dwNewNode = ai().level_graph().vertex(ai_location().level_vertex_id(),tTemp1); + tpNewNode = ai().level_graph().vertex(dwNewNode); + } + return(!ai().level_graph().valid_vertex_id(dwNewNode) || !ai().level_graph().inside(*tpNewNode,QueryPos)); +}; + +void CAI_Rat::fire (bool const &bFire) +{ + if (bFire) { + m_bFiring = true; + m_tAction = eRatActionAttackBegin; + } + else { + m_bFiring = false; + m_tAction = eRatActionAttackEnd; + } +} + +void CAI_Rat::movement_type (float const &fSpeed) +{ +// StandUp(); + m_bMoving = _abs(fSpeed) > EPS_L; + m_fSpeed = m_fCurSpeed = fSpeed; +} + +void CAI_Rat::select_speed () +{ + Fvector tTemp1, tTemp2; + tTemp1.sub (m_tGoalDir,Position()); + tTemp1.normalize_safe (); + float y,p; + tTemp1.getHP (y,p); + tTemp2 = XFORM().k; + tTemp2.normalize_safe (); + float fAngle = tTemp1.dotproduct(tTemp2); + clamp (fAngle,-.99999f,.99999f); + fAngle = acosf(fAngle); + + if (_abs(m_fSpeed - m_fMinSpeed) <= EPS_L) { + if (fAngle >= 2*PI_DIV_3) { + m_fSpeed = 0; + m_fASpeed = m_fNullASpeed; + movement().m_body.target.yaw = -y; + } + else + { + m_fSpeed = m_fMinSpeed; + m_fASpeed = m_fMinASpeed; + } + } + else + if (_abs(m_fSpeed - m_fMaxSpeed) <= EPS_L) { + if (fAngle >= 2*PI_DIV_3) { + m_fSpeed = 0; + m_fASpeed = m_fNullASpeed; + movement().m_body.target.yaw = -y; + } + else + if (fAngle >= PI_DIV_2) { + m_fSpeed = m_fMinSpeed; + m_fASpeed = m_fMinASpeed; + } + else { + m_fSpeed = m_fMaxSpeed; + m_fASpeed = m_fMaxASpeed; + } + } + else + if (_abs(m_fSpeed - m_fAttackSpeed) <= EPS_L) { +// if (fAngle >= 2*PI_DIV_3) { +// m_fSpeed = 0; +// m_fASpeed = m_fNullASpeed; +// movement().m_body.target.yaw = -y; +// } +// else + if (fAngle >= PI_DIV_2) { + m_fSpeed = m_fMinSpeed; + m_fASpeed = m_fMinASpeed; + } + else + if (fAngle >= PI_DIV_4) { + m_fSpeed = m_fMaxSpeed; + m_fASpeed = m_fMaxASpeed; + } + else { + m_fSpeed = m_fAttackSpeed; + m_fASpeed = m_fAttackASpeed; + } + } + else { + movement().m_body.target.yaw = -y; + m_fSpeed = 0; + m_fASpeed = m_fNullASpeed; + } + + tTemp2 = XFORM().k; + tTemp2.normalize_safe(); + + tTemp1 = Position(); + tTemp1.mad(tTemp2,1*m_fSpeed*m_fTimeUpdateDelta); + if (bfCheckIfOutsideAIMap(tTemp1)) { + tTemp1 = Position(); + if (_abs(m_fSpeed - m_fAttackSpeed) < EPS_L) { + tTemp1.mad(tTemp2,1*m_fMaxSpeed*m_fTimeUpdateDelta); + if (bfCheckIfOutsideAIMap(tTemp1)) { + m_fSpeed = m_fMinSpeed; + m_fASpeed = m_fMinASpeed; + } + else { + m_fSpeed = m_fMaxSpeed; + m_fASpeed = m_fMaxASpeed; + } + } + else + { + m_fSpeed = m_fMinSpeed; + m_fASpeed = m_fMinASpeed; + } + } +} + +void CAI_Rat::make_turn() +{ + m_fSpeed = m_fCurSpeed = 0.f; + if (m_bFiring && (angle_difference(movement().m_body.target.yaw,movement().m_body.current.yaw) < PI_DIV_6)) { +// movement().m_body.speed = 0.f; + return; + } + +// Msg ("%6d : Rat %s, %f -> %f [%f]",Device.dwTimeGlobal,*cName(),movement().m_body.current.pitch,movement().m_body.target.pitch,get_custom_pitch_speed(0.f)); + + m_turning = true; + movement().m_body.speed = PI_MUL_2; + + Fvector tSavedPosition = Position(); + m_tHPB.x = -movement().m_body.current.yaw; + m_tHPB.y = -movement().m_body.current.pitch; + + XFORM().setHPB (m_tHPB.x,m_tHPB.y,0.f);//m_tHPB.z); + Position() = tSavedPosition; +} + +void CAI_Rat::set_firing(bool b_val) +{ + m_bFiring = b_val; +} + +bool CAI_Rat::calc_node(Fvector const &next_position) +{ + u32 dwNewNode = ai_location().level_vertex_id(); + const CLevelGraph::CVertex *tpNewNode = ai_location().level_vertex(); + CLevelGraph::CPosition QueryPos; + bool a = !ai().level_graph().valid_vertex_id(dwNewNode) || !ai().level_graph().valid_vertex_position(next_position); + if (!a) { + ai().level_graph().vertex_position (QueryPos,next_position); + a = !ai().level_graph().inside(*ai_location().level_vertex(),QueryPos); + } + if (a) { + dwNewNode = ai().level_graph().vertex(ai_location().level_vertex_id(),next_position); + tpNewNode = ai().level_graph().vertex(dwNewNode); + } + if (ai().level_graph().valid_vertex_id(dwNewNode) && ai().level_graph().inside(*tpNewNode,QueryPos)) { + m_tNewPosition.y = ai().level_graph().vertex_plane_y(*tpNewNode,next_position.x,next_position.z); + if (movement().restrictions().accessible(m_tNewPosition)||!movement().restrictions().accessible(Position())) + return true; + } + return false; +} + +Fvector CAI_Rat::calc_position() +{ + Fvector tSavedPosition = Position(); + SRotation tSavedTorsoTarget = movement().m_body.target; + + // Update position and orientation of the planes + float fAT = m_fASpeed * m_fTimeUpdateDelta; + + Fvector& tDirection = XFORM().k; + + // Tweak orientation based on last position and goal + Fvector tOffset; + tOffset.sub(m_tGoalDir,Position()); + + if (!m_bStraightForward) { + if (tOffset.y > 1.0) { // We're too low + m_tHPB.y += fAT; + if (m_tHPB.y > 0.8f) + m_tHPB.y = 0.8f; + } + else + if (tOffset.y < -1.0) { // We're too high + m_tHPB.y -= fAT; + if (m_tHPB.y < -0.8f) + m_tHPB.y = -0.8f; + } + else // Add damping + m_tHPB.y *= 0.95f; + } + + tDirection.normalize_safe (); + tOffset.normalize_safe (); + + float fDot = tDirection.dotproduct(tOffset); + float fSafeDot = fDot; + + fDot = (1.0f-fDot)/2.0f * fAT * 10.0f; + + tOffset.crossproduct(tOffset,tDirection); + + if (m_bStraightForward) { + if (tOffset.y > 0.01f) { + if (fSafeDot > .95f) + m_fDHeading = 0.f; + else + if (fSafeDot > 0.75f) + m_fDHeading = .10f; + m_fDHeading = ( m_fDHeading * 9.0f + fDot )*0.1f; + } + else + if (tOffset.y < 0.01f) { + if (fSafeDot > .95f) + m_fDHeading = 0.f; + else + if (fSafeDot > 0.75f) + m_fDHeading = -.10f; + m_fDHeading = (m_fDHeading*9.0f - fDot)*0.1f; + } + } + else { + if (tOffset.y > 0.01f) + m_fDHeading = ( m_fDHeading * 9.0f + fDot )*0.1f; + else + if (tOffset.y < 0.01f) + m_fDHeading = (m_fDHeading*9.0f - fDot)*0.1f; + } + + + m_tHPB.x += m_fDHeading; + CLevelGraph::SContour contour; + ai().level_graph().contour(contour, ai_location().level_vertex_id()); + + Fplane P; + P.build(contour.v1,contour.v2,contour.v3); + + Fvector position_on_plane; + P.project(position_on_plane,Position()); + + // находим проекцию точки, лежащей на векторе текущего направления + Fvector dir_point, proj_point; + dir_point.mad(position_on_plane, Direction(), 1.f); + P.project(proj_point,dir_point); + + // получаем искомый вектор направления + Fvector target_dir; + target_dir.sub(proj_point,position_on_plane); + + float yaw,pitch; + target_dir.getHP(yaw,pitch); + + //movement().m_body.target.pitch = -pitch; + m_newPitch = -pitch; + + m_tHPB.x = angle_normalize_signed(m_tHPB.x); + m_tHPB.y = -movement().m_body.current.pitch; + return tSavedPosition.mad (tDirection,m_fSpeed*m_fTimeUpdateDelta); +} + +void CAI_Rat::set_position(Fvector m_position) +{ + XFORM().setHPB (m_tHPB.x,m_tHPB.y,0.f); + Position() = m_position; +} + +void CAI_Rat::set_pitch(float pitch, float yaw) +{ + movement().m_body.target.pitch = pitch; + movement().m_body.target.yaw = yaw; +} + + +void CAI_Rat::move (bool bCanAdjustSpeed, bool bStraightForward) +{ + m_bCanAdjustSpeed = bCanAdjustSpeed; + m_bStraightForward = bStraightForward; + + + Fvector tSafeHPB = m_tHPB; + Fvector tSavedPosition = Position(); + SRotation tSavedTorsoTarget = movement().m_body.target; + float fSavedDHeading = m_fDHeading; + + if (bCanAdjustSpeed) + select_speed (); + + if ((angle_difference(movement().m_body.target.yaw, movement().m_body.current.yaw) > PI_DIV_6)){ + make_turn (); + return; + } + + if (fis_zero(m_fSpeed)) + return; + + m_fCurSpeed = m_fSpeed; + + if (m_bNoWay) + { + m_tNewPosition = m_tOldPosition; + } else { + m_tNewPosition = calc_position(); + } + if (calc_node(m_tNewPosition)) + { + set_position(m_tNewPosition); + set_pitch(m_newPitch, -m_tHPB.x); + m_tOldPosition = tSavedPosition; + m_bNoWay = false; + } + else { + m_fSafeSpeed = m_fSpeed = EPS_S; + m_bNoWay = true; + m_tHPB = tSafeHPB; + set_position(tSavedPosition); + movement().m_body.target = tSavedTorsoTarget; + m_fDHeading = fSavedDHeading; + } + if (m_bNoWay && (!m_turning || (angle_difference(movement().m_body.target.yaw, movement().m_body.current.yaw) < EPS_L))) { + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_RETURN) || (!m_previous_query_time)) { + movement().m_body.target.yaw = movement().m_body.current.yaw + PI; + movement().m_body.target.yaw = angle_normalize(movement().m_body.target.yaw); + Fvector tTemp; + tTemp.setHP(-movement().m_body.target.yaw ,-movement().m_body.target.pitch); + if (m_bStraightForward) + { + tTemp.mul(100.f); + } + + if (!m_walk_on_way) m_tGoalDir.add(Position(),tTemp); + + m_previous_query_time = Device.dwTimeGlobal; + } + if (!m_walk_on_way) make_turn (); + } + m_turning = false; +} + +void CAI_Rat::select_next_home_position () +{ + GameGraph::_GRAPH_ID tGraphID = m_next_graph_point; + CGameGraph::const_iterator i,e; + ai().game_graph().begin (tGraphID,i,e); + int iPointCount = (int)movement().locations().vertex_types().size(); + int iBranches = 0; + for ( ; i != e; ++i) + for (int j=0; jvertex_type()) && ((*i).vertex_id() != m_current_graph_point)) + ++iBranches; + ai().game_graph().begin (tGraphID,i,e); + if (!iBranches) { + for ( ; i != e; ++i) { + for (int j=0; jvertex_type())) { + m_current_graph_point = m_next_graph_point; + m_next_graph_point = (*i).vertex_id(); + m_time_to_change_graph_point = Device.dwTimeGlobal + ::Random32.random(60000) + 60000; + return; + } + } + } + else { + int iChosenBranch = ::Random.randI(0,iBranches); + iBranches = 0; + for ( ; i != e; ++i) { + for (int j=0; jvertex_type()) && ((*i).vertex_id() != m_current_graph_point)) { + if (iBranches == iChosenBranch) { + m_current_graph_point = m_next_graph_point; + m_next_graph_point = (*i).vertex_id(); + m_time_to_change_graph_point = Device.dwTimeGlobal + ::Random32.random(60000) + 60000; + return; + } + ++iBranches; + } + } + } +} + +bool CAI_Rat::can_stand_in_position() +{ + xr_vector tpNearestList ; + //float m_radius = Radius(); + Level().ObjectSpace.GetNearest (tpNearestList, Position(), 0.2f, this) ; + if (tpNearestList.empty()) + return (true); + + Fvector c, d, C2; + Visual()->getVisData().box.get_CD (c,d); + Fmatrix M = XFORM(); + M.transform_tiny (C2,c); + M.c = C2; + MagicBox3 box(M,d); + + xr_vector::iterator I = tpNearestList.begin(); + xr_vector::iterator E = tpNearestList.end(); + for ( ; I != E; ++I) { + if (!smart_cast(*I)) + continue; + + (*I)->Visual()->getVisData().box.get_CD (c,d); + M = (*I)->XFORM(); + M.transform_tiny (C2,c); + M.c = C2; + + if (box.intersects(MagicBox3(M,d))) + return (false); + } + return (true); +} + +bool CAI_Rat::can_stand_here () +{ + xr_vector tpNearestList ; + Level().ObjectSpace.GetNearest (tpNearestList, Position(),Radius(),this) ; + //xr_vector &tpNearestList = Level().ObjectSpace.q_nearest; + if (tpNearestList.empty()) + return (true); + + Fvector c, d, C2; + Visual()->getVisData().box.get_CD (c,d); + Fmatrix M = XFORM(); + M.transform_tiny (C2,c); + M.c = C2; + MagicBox3 box(M,d); + + xr_vector::iterator I = tpNearestList.begin(); + xr_vector::iterator E = tpNearestList.end(); + for ( ; I != E; ++I) { + if (!smart_cast(*I)) + continue; + + (*I)->Visual()->getVisData().box.get_CD (c,d); + M = (*I)->XFORM(); + M.transform_tiny (C2,c); + M.c = C2; + + if (box.intersects(MagicBox3(M,d))) + return (false); + } + return (true); +} + +Fvector CAI_Rat::get_next_target_point() +{ + if (!m_path) + { + m_walk_on_way = false; + m_current_way_point = u32(-1); + return Position(); + } + + if (m_current_way_point == u32(-1)) + { + m_walk_on_way = false; + return Position(); + } + + const CPatrolPath::CVertex *vertex = m_path->vertex(m_current_way_point); + + if (Position().distance_to(ai().level_graph().vertex_position(vertex->data().level_vertex_id())) < 1.5f){ + + monster_squad().get_squad(this)->SetLeader(this); + + m_current_way_point++; + + if (m_current_way_point == m_path->vertex_count()) m_current_way_point = 0; + + vertex = m_path->vertex(m_current_way_point); + } + + return (ai().level_graph().vertex_position(vertex->data().level_vertex_id())); +} + +#ifdef _DEBUG +void CAI_Rat::draw_way() +{ + if (!m_path) return; + const CPatrolPath::CVertex *vertex; + Fvector P1, P2; + Fmatrix m_sphere; + + for (u32 i=1; ivertex_count(); i++) { + vertex = m_path->vertex(i-1); + P1 = ai().level_graph().vertex_position(vertex->data().level_vertex_id()); + vertex = m_path->vertex(i); + P2 = ai().level_graph().vertex_position(vertex->data().level_vertex_id()); + if (!fis_zero(P1.distance_to_sqr(P2),EPS_L)) + Level().debug_renderer().draw_line (Fidentity,P1,P2,D3DCOLOR_XRGB(0,0,255)); + m_sphere.identity(); + m_sphere.scale(0.25, 0.25, 0.25); + m_sphere.translate_add(P2); + Level().debug_renderer().draw_ellipse(m_sphere, D3DCOLOR_XRGB(0,255,255)); + //Level().debug_renderer().draw_aabb (P1,0.5f,0.5f,0.5f,D3DCOLOR_XRGB(0,255,255)); + } + + vertex = m_path->vertex(0); + P1 = ai().level_graph().vertex_position(vertex->data().level_vertex_id()); + vertex = m_path->vertex(m_path->vertex_count() - 1); + P2 = ai().level_graph().vertex_position(vertex->data().level_vertex_id()); + if (!fis_zero(P1.distance_to_sqr(P2),EPS_L)) + Level().debug_renderer().draw_line (Fidentity,P1,P2,D3DCOLOR_XRGB(0,0,255)); + m_sphere.identity(); + m_sphere.scale(0.25, 0.25, 0.25); + m_sphere.translate_add(P2); + Level().debug_renderer().draw_ellipse(m_sphere, D3DCOLOR_XRGB(0,255,255)); + //Level().debug_renderer().draw_aabb (P1,0.5f,0.5f,0.5f,D3DCOLOR_XRGB(0,255,255)); +} +#endif diff --git a/src/xrGameLA/ai/rats/rat_state_activation.cpp b/src/xrGameLA/ai/rats/rat_state_activation.cpp new file mode 100644 index 000000000..d73e81a0c --- /dev/null +++ b/src/xrGameLA/ai/rats/rat_state_activation.cpp @@ -0,0 +1,186 @@ +#include "pch_script.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../game_level_cross_table.h" +#include "../../../game_graph.h" +#include "ai_rat_space.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../item_manager.h" +#include "../../../memory_space.h" +#include "../../../ai_object_location.h" +#include "../../../movement_manager.h" +#include "../../../sound_player.h" +#include "ai_rat_impl.h" +#include "../../../ai_space.h" + +using namespace RatSpace; + +void CAI_Rat::activate_state_free_active() +{ + m_tSpawnPosition.set (m_home_position); + m_fGoalChangeDelta = m_fSafeGoalChangeDelta; + m_tVarGoal.set (m_tGoalVariation); + m_fASpeed = m_fAngleSpeed; + + if (bfCheckIfGoalChanged()) { + if ((Position().distance_to(m_tSpawnPosition) > m_fStableDistance) || (::Random.randF(0,1) > m_fChangeActiveStateProbability)) + if (Position().distance_to(m_home_position) > m_fMaxHomeRadius) + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + else + vfChooseNewSpeed(); + else { + if (can_stand_here()) + vfRemoveActiveMember(); + } + } + + if ((fis_zero(m_fSpeed) && (angle_difference(movement().m_body.target.yaw,movement().m_body.current.yaw) < PI_DIV_6))) + vfChooseNewSpeed(); + + vfUpdateTime(m_fTimeUpdateDelta); + + set_movement_type(false); + + sound().play (eRatSoundVoice, 45*1000,15*1000); +} + +void CAI_Rat::activate_state_free_passive() +{ + if (memory().enemy().selected()) { + m_fGoalChangeTime = 0; + add_active_member(true); + return; + } + + if (m_fMorale < m_fMoraleNormalValue) { + add_active_member(true); + return; + } + + if ((m_tLastSound.dwTime >= m_dwLastUpdateTime) && ((!m_tLastSound.tpEntity) || (m_tLastSound.tpEntity->g_Team() != g_Team()))) { + add_active_member(true); + return; + } + + m_fSpeed = 0.f; + + vfAddStandingMember(); + add_active_member(); + + sound().play (eRatSoundVoice,45*1000,15*1000); +} + +void CAI_Rat::activate_turn() +{ + float m_heading, m_pitch; + Fvector m_dest_direction; + Fvector m_enemy_position = get_enemy()->Position(); + Fvector m_temp = Position(); + m_dest_direction.x = (m_enemy_position.x - m_temp.x) / m_temp.distance_to(m_enemy_position); + m_dest_direction.y = (m_enemy_position.y - m_temp.y) / m_temp.distance_to(m_enemy_position); + m_dest_direction.z = (m_enemy_position.z - m_temp.z) / m_temp.distance_to(m_enemy_position); + m_dest_direction.getHP(m_heading, m_pitch); + set_pitch(m_pitch, m_heading); + m_tGoalDir = m_enemy_position; +} + +void CAI_Rat::activate_state_move() +{ + + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + set_movement_type(true,true); +} + +void CAI_Rat::activate_move() +{ + vfUpdateTime(m_fTimeUpdateDelta); + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + set_movement_type(m_bWayCanAdjustSpeed,m_bWayStraightForward); +} + +void CAI_Rat::activate_state_attack_range() +{ + if (!m_attack_rebuild) + { + time_attack_rebuild = Device.dwTimeGlobal; + m_attack_rebuild = true; + } + + if (m_attack_rebuild && Device.dwTimeGlobal - time_attack_rebuild > 5000) + { + m_attack_rebuild = false; + } + + m_fSpeed = 0.f; + + fire(true); + + set_movement_type(false); +} + +void CAI_Rat::activate_state_free_recoil() +{ + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + + set_movement_type(true,true); + + sound().play (eRatSoundVoice,45*1000,15*1000); +} +void CAI_Rat::activate_state_home() +{ + m_tSpawnPosition.set (m_home_position); + m_fGoalChangeDelta = m_fSafeGoalChangeDelta; + m_tVarGoal.set (m_tGoalVariation); + m_fASpeed = m_fAngleSpeed; + m_fSpeed = m_fSafeSpeed = m_fAttackSpeed; + vfUpdateTime(m_fTimeUpdateDelta); + + m_fSpeed = m_fAttackSpeed; + + set_movement_type(); +} + +void CAI_Rat::activate_state_eat() +{ + Fvector temp_position; + memory().item().selected()->Center (temp_position); + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set (temp_position); + + vfUpdateTime (m_fTimeUpdateDelta); + + bool a = temp_position.distance_to(Position()) <= m_fAttackDistance; + Fvector direction; + direction.sub (temp_position,Position()); + float y,p; + direction.getHP (y,p); + if (a && angle_difference(y,-movement().m_body.current.yaw) < PI_DIV_6) { + m_fSpeed = 0; + if (Device.dwTimeGlobal - m_previous_query_time > m_dwHitInterval) { + m_previous_query_time = Device.dwTimeGlobal; + const CEntityAlive *const_corpse = smart_cast(memory().item().selected()); + VERIFY (const_corpse); + CEntityAlive *corpse = const_cast(const_corpse); + VERIFY (corpse); + corpse->m_fFood -= m_fHitPower/10.f; + } + m_bFiring = true; + set_movement_type (false); + sound().play (eRatSoundEat); + } + else { + sound().remove_active_sounds(u32(-1)); + if (!a) + m_fSpeed = m_fMaxSpeed; + else + m_fSpeed = 0.f; + set_movement_type (true,true); + } +} \ No newline at end of file diff --git a/src/xrGameLA/ai/rats/rat_state_initialize.cpp b/src/xrGameLA/ai/rats/rat_state_initialize.cpp new file mode 100644 index 000000000..566bead16 --- /dev/null +++ b/src/xrGameLA/ai/rats/rat_state_initialize.cpp @@ -0,0 +1,53 @@ +#include "pch_script.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../game_level_cross_table.h" +#include "../../../game_graph.h" +#include "ai_rat_space.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../item_manager.h" +#include "../../../memory_space.h" +#include "../../../ai_object_location.h" +#include "../../../movement_manager.h" +#include "../../../sound_player.h" +#include "ai_rat_impl.h" +#include "../../../ai_space.h" + +void CAI_Rat::init_state_under_fire() +{ + if (!switch_if_enemy()&&get_if_dw_time()&&m_tLastSound.dwTime >= m_dwLastUpdateTime) + { + Fvector tTemp; + tTemp.setHP(-movement().m_body.current.yaw,-movement().m_body.current.pitch); + tTemp.normalize_safe(); + tTemp.mul(m_fUnderFireDistance); + m_tSpawnPosition.add(Position(),tTemp); + } + m_tGoalDir = m_tSpawnPosition; +} + +void CAI_Rat::init_free_recoil() +{ + m_dwLostRecoilTime = Device.dwTimeGlobal; + m_tRecoilPosition = m_tLastSound.tSavedPosition; + if (!switch_if_enemy()&&!switch_if_time()) + { + Fvector tTemp; + tTemp.setHP(-movement().m_body.current.yaw,-movement().m_body.current.pitch); + tTemp.normalize_safe(); + tTemp.mul(m_fUnderFireDistance); + m_tSpawnPosition.add(Position(),tTemp); + } +} + +void CAI_Rat::init_free_active() +{ + if (bfCheckIfGoalChanged()) { + if (Position().distance_to(m_home_position) > m_fMaxHomeRadius) + m_fSpeed = m_fSafeSpeed = m_fMaxSpeed; + vfChooseNewSpeed(); + } +} \ No newline at end of file diff --git a/src/xrGameLA/ai/rats/rat_state_switch.cpp b/src/xrGameLA/ai/rats/rat_state_switch.cpp new file mode 100644 index 000000000..d80ca035a --- /dev/null +++ b/src/xrGameLA/ai/rats/rat_state_switch.cpp @@ -0,0 +1,331 @@ +#include "pch_script.h" +#include "ai_rat.h" +#include "../../ai_monsters_misc.h" +#include "../../../game_level_cross_table.h" +#include "../../../game_graph.h" +#include "ai_rat_space.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../detail_path_manager.h" +#include "../../../memory_manager.h" +#include "../../../enemy_manager.h" +#include "../../../item_manager.h" +#include "../../../memory_space.h" +#include "../../../ai_object_location.h" +#include "../../../movement_manager.h" +#include "../../../sound_player.h" +#include "ai_rat_impl.h" +#include "../../../ai_space.h" +#include "ai/monsters/ai_monster_squad_manager.h" +#include "ai/monsters/ai_monster_squad.h" + +bool CAI_Rat::switch_to_attack_melee() +{ + if (!switch_if_porsuit() || !switch_if_home()) + { + return true; + } + return false; +} + +const CEntityAlive *CAI_Rat::get_enemy() +{ + return memory().enemy().selected(); +} + +bool CAI_Rat::switch_if_home() +{ + if (Position().distance_to(m_home_position) < m_fMaxHomeRadius) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_position() +{ + if (memory().enemy().selected()->Position().distance_to(Position()) > m_fAttackDistance) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_enemy() +{ + if (memory().enemy().selected()) + { + return true; + } + return false; +} + +bool CAI_Rat::get_morale() +{ + if (m_fMorale >= m_fMoraleNormalValue - EPS_L) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_to_eat() +{ + if (memory().item().selected()) + { + return true; + } + return false; +} + +bool CAI_Rat::get_if_dw_time() +{ + if (m_tLastSound.dwTime >= m_dwLastUpdateTime) + { + return true; + } + return false; +} + +bool CAI_Rat::get_if_tp_entity() +{ + if ( m_tLastSound.tpEntity && (m_tLastSound.tpEntity->g_Team() != g_Team()) && (!bfCheckIfSoundFrightful())) + { + return true; + } + return false; +} + +void CAI_Rat::set_previous_query_time() +{ + m_previous_query_time = Device.dwTimeGlobal; +} + +SRotation CAI_Rat::sub_rotation() +{ + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + SRotation sTemp; + mk_rotation(tTemp,sTemp); + return sTemp; +} + +CAI_Rat::ERatStates CAI_Rat::get_state() +{ + return ERatStates(dwfChooseAction(m_dwActionRefreshRate,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,m_fAttackSuccessProbability,g_Team(),g_Squad(),g_Group(),aiRatAttackMelee,aiRatAttackMelee,aiRatAttackMelee,aiRatRetreat,aiRatRetreat,this,30.f)); +} + +bool CAI_Rat::switch_if_porsuit() +{ + if (m_home_position.distance_to(memory().enemy().selected()->Position()) > m_fMaxPursuitRadius) + { + return true; + } + return false; +} + +void CAI_Rat::set_dir() +{ + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) { + CMonsterSquad *squad = monster_squad().get_squad(this); + Fvector m_enemy_position = memory().enemy().selected()->Position(); + if (squad && squad->SquadActive()) + { + float m_delta_Angle = angle_normalize((PI * 2) / squad->squad_alife_count()); + float m_heading, m_pitch; + Fvector m_temp, m_dest_direction; + m_temp = squad->GetLeader()->Position(); + m_dest_direction.x = (m_temp.x - m_enemy_position.x) / m_temp.distance_to(m_enemy_position); + m_dest_direction.y = (m_temp.y - m_enemy_position.y) / m_temp.distance_to(m_enemy_position); + m_dest_direction.z = (m_temp.z - m_enemy_position.z) / m_temp.distance_to(m_enemy_position); + m_dest_direction.getHP(m_heading, m_pitch); + m_heading = angle_normalize(m_heading + m_delta_Angle * squad->get_index(this)); + m_dest_direction.setHP(m_heading, m_pitch); + m_dest_direction.mul(0.5f); + m_enemy_position.add(m_enemy_position,m_dest_direction); + } + + m_tGoalDir.set(m_enemy_position); + } +} + +void CAI_Rat::set_dir_m() +{ + + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set(memory().memory(memory().enemy().selected()).m_object_params.m_position); +} + +void CAI_Rat::set_sp_dir() +{ + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir = m_tSpawnPosition; +} + +void CAI_Rat::set_way_point() +{ + m_tGoalDir = get_next_target_point(); +} + +bool CAI_Rat::switch_if_no_enemy() +{ + if (!switch_if_enemy() || + (switch_if_enemy() && + ( + !switch_if_alife() + || + ( + (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time > m_dwRetreatTime) && + ( + (m_tLastSound.dwTime < m_dwLastUpdateTime) || + !m_tLastSound.tpEntity || + (m_tLastSound.tpEntity->g_Team() == g_Team()) || + !bfCheckIfSoundFrightful() + ) + ) + ) + ) + ) + { + memory().enable (memory().enemy().selected(),false); + return true; + } + return false; +} + +bool CAI_Rat::switch_to_free_recoil() +{ + if ( + (m_tLastSound.dwTime >= m_dwLastUpdateTime) && + ( + !m_tLastSound.tpEntity || + ( + (!switch_to_eat() || (memory().item().selected()->ID() != m_tLastSound.tpEntity->ID())) && + (m_tLastSound.tpEntity->g_Team() != g_Team()) + ) + ) && + !switch_if_enemy() + ) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_lost_time() +{ + if (switch_if_enemy() && (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time >= m_dwLostMemoryTime)) + { + memory().enable(memory().enemy().selected(),false); + return true; + } + return false; +} + +bool CAI_Rat::switch_if_lost_rtime() +{ + if (Device.dwTimeGlobal - memory().memory(memory().enemy().selected()).m_level_time >= m_dwLostRecoilTime) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_alife() +{ + if (memory().enemy().selected()->g_Alive()) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_diff() +{ + SRotation sTemp = sub_rotation(); + if (angle_difference(movement().m_body.current.yaw,sTemp.yaw) > m_fAttackAngle) + { + return true; + } + return false; +} + +bool CAI_Rat::switch_if_dist_no_angle() +{ + if (!switch_if_position() && switch_if_diff()) { + m_fSpeed = 0.f; + return true; + } + return false; +} + + +bool CAI_Rat::switch_if_dist_angle() +{ + if(!switch_if_position() && !switch_if_diff()) + { + return true; + } + return false; +} + +void CAI_Rat::set_rew_position() +{ + Fvector tTemp; + tTemp.sub(memory().enemy().selected()->Position(),Position()); + vfNormalizeSafe(tTemp); + tTemp.sub(Position(),memory().enemy().selected()->Position()); + tTemp.normalize_safe(); + tTemp.mul(m_fRetreatDistance); + m_tSpawnPosition.add(Position(),tTemp); +} + +bool CAI_Rat::switch_if_time() +{ + if (m_dwLastUpdateTime > m_dwLostRecoilTime + 2000) + { + return true; + } + return false; +} + +void CAI_Rat::set_rew_cur_position() +{ + Fvector tTemp; + tTemp.setHP(-movement().m_body.current.yaw,-movement().m_body.current.pitch); + tTemp.normalize_safe(); + tTemp.mul(m_fUnderFireDistance); + m_tSpawnPosition.add(Position(),tTemp); +} + +void CAI_Rat::set_home_pos() +{ + if ((Device.dwTimeGlobal - m_previous_query_time > TIME_TO_GO) || !m_previous_query_time) + m_tGoalDir.set (m_home_position); +} + +void CAI_Rat::set_goal_time(float f_val) +{ + m_fGoalChangeTime = 0; +} + +bool CAI_Rat::get_alife() +{ + if (!g_Alive()) { + m_fSpeed = m_fSafeSpeed = 0; + return false; + } + return true; +} + +void CAI_Rat::set_movement_type(bool bCanAdjustSpeed, bool bStraightForward) +{ + m_bCanAdjustSpeed = bCanAdjustSpeed; + m_bStraightForward = bStraightForward; +} + +bool CAI_Rat::check_completion_no_way() +{ + if(time_to_next_attack + time_old_attack < Device.dwTimeGlobal ) return true; + return false; +} \ No newline at end of file diff --git a/src/xrGameLA/ai/stalker/ai_stalker.cpp b/src/xrGameLA/ai/stalker/ai_stalker.cpp new file mode 100644 index 000000000..121b68d5d --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker.cpp @@ -0,0 +1,1098 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker.cpp +// Created : 25.02.2003 +// Modified : 25.02.2003 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Stalker" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_stalker.h" +#include "../ai_monsters_misc.h" +#include "../../weapon.h" +#include "../../hit.h" +#include "../../phdestroyable.h" +#include "../../CharacterPhysicsSupport.h" +#include "../../script_entity_action.h" +#include "../../game_level_cross_table.h" +#include "../../game_graph.h" +#include "../../inventory.h" +#include "../../artifact.h" +#include "../../phmovementcontrol.h" +#include "../../xrserver_objects_alife_monsters.h" +#include "../../cover_evaluators.h" +#include "../../xrserver.h" +#include "../../xr_level_controller.h" +#include "../../hudmanager.h" +#include "../../clsid_game.h" +#include "../../../../Include/xrRender/Kinematics.h" +#include "../../character_info.h" +#include "../../actor.h" +#include "../../relation_registry.h" +#include "../../stalker_animation_manager.h" +#include "../../stalker_planner.h" +#include "../../script_game_object.h" +#include "../../detail_path_manager.h" +#include "../../agent_manager.h" +#include "../../agent_corpse_manager.h" +#include "../../object_handler_planner.h" +#include "../../object_handler_space.h" +#include "../../memory_manager.h" +#include "../../sight_manager.h" +#include "../../ai_object_location.h" +#include "../../stalker_movement_manager.h" +#include "../../entitycondition.h" +#include "../../script_engine.h" +#include "ai_stalker_impl.h" +#include "../../sound_player.h" +#include "../../stalker_sound_data.h" +#include "../../stalker_sound_data_visitor.h" +#include "ai_stalker_space.h" +#include "../../mt_config.h" +#include "../../effectorshot.h" +#include "../../visual_memory_manager.h" +#include "../../enemy_manager.h" +#include "../../alife_human_brain.h" +#include "../../profiler.h" +#include "../../BoneProtections.h" +#include "../../stalker_animation_names.h" +#include "../../stalker_decision_space.h" +#include "../../agent_member_manager.h" +#include "../../location_manager.h" + +#ifdef DEBUG +# include "../../alife_simulator.h" +# include "../../alife_object_registry.h" +# include "../../level.h" +# include "../../map_location.h" +# include "../../map_manager.h" +#endif // DEBUG + +using namespace StalkerSpace; + +extern int g_AI_inactive_time; + +CAI_Stalker::CAI_Stalker () +{ + m_sound_user_data_visitor = 0; + m_movement_manager = 0; + m_agent_manager = 0; + m_group_behaviour = true; + m_boneHitProtection = NULL; + m_power_fx_factor = flt_max; + m_wounded = false; + +#ifdef LOG_PLANNER + m_debug_planner = 0; +#endif // LOG_PLANNER + m_registered_in_combat_on_migration = false; +} + +CAI_Stalker::~CAI_Stalker () +{ + xr_delete (m_pPhysics_support); + xr_delete (m_animation_manager); + xr_delete (m_brain); + xr_delete (m_sight_manager); + xr_delete (m_weapon_shot_effector); + xr_delete (m_sound_user_data_visitor); +} + +void CAI_Stalker::reinit () +{ + CObjectHandler::reinit (this); + sight().reinit (); + CCustomMonster::reinit (); + animation().reinit (); + movement().reinit (); + + //загрузка спецевической звуковой схемы для сталкера согласно m_SpecificCharacter + sound().sound_prefix (SpecificCharacter().sound_voice_prefix()); + +#ifdef DEBUG_MEMORY_MANAGER + u32 start = 0; + if (g_bMEMO) + start = Memory.mem_usage(); +#endif // DEBUG_MEMORY_MANAGER + + LoadSounds (*cNameSect()); + +#ifdef DEBUG_MEMORY_MANAGER + if (g_bMEMO) + Msg ("CAI_Stalker::LoadSounds() : %d",Memory.mem_usage() - start); +#endif // DEBUG_MEMORY_MANAGER + + m_pPhysics_support->in_Init (); + + m_best_item_to_kill = 0; + m_best_item_value = 0.f; + m_best_ammo = 0; + m_best_found_item_to_kill = 0; + m_best_found_ammo = 0; + m_item_actuality = false; + m_sell_info_actuality = false; + + m_ce_close = new CCoverEvaluatorCloseToEnemy(&movement().restrictions()); + m_ce_far = new CCoverEvaluatorFarFromEnemy(&movement().restrictions()); + m_ce_best = new CCoverEvaluatorBest(&movement().restrictions()); + m_ce_angle = new CCoverEvaluatorAngle(&movement().restrictions()); + m_ce_safe = new CCoverEvaluatorSafe(&movement().restrictions()); + m_ce_random_game = new CCoverEvaluatorRandomGame(&movement().restrictions()); + m_ce_ambush = new CCoverEvaluatorAmbush(&movement().restrictions()); + m_ce_best_by_time = new CCoverEvaluatorBestByTime(&movement().restrictions()); + + m_ce_close->set_inertia (3000); + m_ce_far->set_inertia (3000); + m_ce_best->set_inertia (1000); + m_ce_angle->set_inertia (5000); + m_ce_safe->set_inertia (1000); + m_ce_random_game->set_inertia (3000); + m_ce_ambush->set_inertia (3000); + m_ce_best_by_time->set_inertia (1000); + + m_can_kill_enemy = false; + m_can_kill_member = false; + m_pick_distance = 0.f; + m_pick_frame_id = 0; + + m_weapon_shot_random_seed = s32(Level().timeServer_Async()); + + m_best_cover = 0; + m_best_cover_actual = false; + m_best_cover_value = flt_max; + + m_throw_actual = false; + m_computed_object_position = Fvector().set(flt_max,flt_max,flt_max); + m_computed_object_direction = Fvector().set(flt_max,flt_max,flt_max); + + m_throw_target_position = Fvector().set(flt_max,flt_max,flt_max); + m_throw_ignore_object = 0; + + m_throw_position = Fvector().set(flt_max,flt_max,flt_max); + m_throw_velocity = Fvector().set(flt_max,flt_max,flt_max); + + m_throw_collide_position = Fvector().set(flt_max,flt_max,flt_max); + m_throw_enabled = false; + + m_last_throw_time = 0; + + m_can_throw_grenades = true; + m_throw_time_interval = 20000; + + brain().CStalkerPlanner::m_storage.set_property (StalkerDecisionSpace::eWorldPropertyCriticallyWounded, false); + + { + m_critical_wound_weights.clear (); +// LPCSTR weights = pSettings->r_string(cNameSect(),"critical_wound_weights"); + LPCSTR weights = SpecificCharacter().critical_wound_weights(); + string16 temp; + for (int i=0, n=_GetItemCount(weights); ir_string(section,"bone_head"); + sound().add (pSettings->r_string(section,"sound_death"), 100, SOUND_TYPE_MONSTER_DYING, 0, u32(eStalkerSoundMaskDie), eStalkerSoundDie, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_anomaly_death"), 100, SOUND_TYPE_MONSTER_DYING, 0, u32(eStalkerSoundMaskDieInAnomaly), eStalkerSoundDieInAnomaly, head_bone_name, 0); + sound().add (pSettings->r_string(section,"sound_hit"), 100, SOUND_TYPE_MONSTER_INJURING, 1, u32(eStalkerSoundMaskInjuring), eStalkerSoundInjuring, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_friendly_fire"), 100, SOUND_TYPE_MONSTER_INJURING, 1, u32(eStalkerSoundMaskInjuringByFriend), eStalkerSoundInjuringByFriend, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_panic_human"), 100, SOUND_TYPE_MONSTER_TALKING, 2, u32(eStalkerSoundMaskPanicHuman), eStalkerSoundPanicHuman, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_panic_monster"), 100, SOUND_TYPE_MONSTER_TALKING, 2, u32(eStalkerSoundMaskPanicMonster), eStalkerSoundPanicMonster, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_grenade_alarm"), 100, SOUND_TYPE_MONSTER_TALKING, 3, u32(eStalkerSoundMaskGrenadeAlarm), eStalkerSoundGrenadeAlarm, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_friendly_grenade_alarm"), 100, SOUND_TYPE_MONSTER_TALKING, 3, u32(eStalkerSoundMaskFriendlyGrenadeAlarm), eStalkerSoundFriendlyGrenadeAlarm, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_tolls"), 100, SOUND_TYPE_MONSTER_TALKING, 4, u32(eStalkerSoundMaskTolls), eStalkerSoundTolls, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_alarm"), 100, SOUND_TYPE_MONSTER_TALKING, 5, u32(eStalkerSoundMaskAlarm), eStalkerSoundAlarm, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_attack_no_allies"), 100, SOUND_TYPE_MONSTER_TALKING, 5, u32(eStalkerSoundMaskAttackNoAllies), eStalkerSoundAttackNoAllies, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_attack_allies_single_enemy"), 100, SOUND_TYPE_MONSTER_TALKING, 5, u32(eStalkerSoundMaskAttackAlliesSingleEnemy), eStalkerSoundAttackAlliesSingleEnemy, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_attack_allies_several_enemies"),100, SOUND_TYPE_MONSTER_TALKING, 5, u32(eStalkerSoundMaskAttackAlliesSeveralEnemies),eStalkerSoundAttackAlliesSeveralEnemies,head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_backup"), 100, SOUND_TYPE_MONSTER_TALKING, 5, u32(eStalkerSoundMaskBackup), eStalkerSoundBackup, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_detour"), 100, SOUND_TYPE_MONSTER_TALKING, 5, u32(eStalkerSoundMaskDetour), eStalkerSoundDetour, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_search1_no_allies"), 100, SOUND_TYPE_MONSTER_TALKING, 5, u32(eStalkerSoundMaskSearch1NoAllies), eStalkerSoundSearch1NoAllies, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_search1_with_allies"), 100, SOUND_TYPE_MONSTER_TALKING, 5, u32(eStalkerSoundMaskSearch1WithAllies), eStalkerSoundSearch1WithAllies, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_humming"), 100, SOUND_TYPE_MONSTER_TALKING, 6, u32(eStalkerSoundMaskHumming), eStalkerSoundHumming, head_bone_name, 0); + sound().add (pSettings->r_string(section,"sound_need_backup"), 100, SOUND_TYPE_MONSTER_TALKING, 4, u32(eStalkerSoundMaskNeedBackup), eStalkerSoundNeedBackup, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_running_in_danger"), 100, SOUND_TYPE_MONSTER_TALKING, 6, u32(eStalkerSoundMaskMovingInDanger), eStalkerSoundRunningInDanger, head_bone_name, new CStalkerSoundData(this)); +// sound().add (pSettings->r_string(section,"sound_walking_in_danger"), 100, SOUND_TYPE_MONSTER_TALKING, 6, u32(eStalkerSoundMaskMovingInDanger), eStalkerSoundWalkingInDanger, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_kill_wounded"), 100, SOUND_TYPE_MONSTER_TALKING, 5, u32(eStalkerSoundMaskKillWounded), eStalkerSoundKillWounded, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_enemy_critically_wounded"), 100, SOUND_TYPE_MONSTER_TALKING, 4, u32(eStalkerSoundMaskEnemyCriticallyWounded), eStalkerSoundEnemyCriticallyWounded, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"sound_enemy_killed_or_wounded"), 100, SOUND_TYPE_MONSTER_TALKING, 4, u32(eStalkerSoundMaskEnemyKilledOrWounded), eStalkerSoundEnemyKilledOrWounded, head_bone_name, new CStalkerSoundData(this)); + sound().add (pSettings->r_string(section,"snd_throw_grenade"), 100, SOUND_TYPE_MONSTER_TALKING, 5, u32(eStalkerSoundMaskKillWounded), eStalkerSoundThrowGrenade, head_bone_name, new CStalkerSoundData(this)); +} + +void CAI_Stalker::reload (LPCSTR section) +{ +#ifdef DEBUG_MEMORY_MANAGER + u32 start = 0; + if (g_bMEMO) + start = Memory.mem_usage(); +#endif // DEBUG_MEMORY_MANAGER + + brain().setup (this); + +#ifdef DEBUG_MEMORY_MANAGER + if (g_bMEMO) + Msg ("brain().setup() : %d",Memory.mem_usage() - start); +#endif // DEBUG_MEMORY_MANAGER + + CCustomMonster::reload (section); + if (!already_dead()) + CStepManager::reload (section); + +// if (!already_dead()) + CObjectHandler::reload (section); + +// inventory().m_slots[OUTFIT_SLOT].m_bUsable = false; + + if (!already_dead()) + sight().reload (section); + + if (!already_dead()) + movement().reload (section); + + m_disp_walk_stand = pSettings->r_float(section,"disp_walk_stand"); + m_disp_walk_crouch = pSettings->r_float(section,"disp_walk_crouch"); + m_disp_run_stand = pSettings->r_float(section,"disp_run_stand"); + m_disp_run_crouch = pSettings->r_float(section,"disp_run_crouch"); + m_disp_stand_stand = pSettings->r_float(section,"disp_stand_stand"); + m_disp_stand_crouch = pSettings->r_float(section,"disp_stand_crouch"); + m_disp_stand_stand_zoom = pSettings->r_float(section,"disp_stand_stand_zoom"); + m_disp_stand_crouch_zoom = pSettings->r_float(section,"disp_stand_crouch_zoom"); + + m_min_queue_size_far = pSettings->r_u32(*cNameSect(),"weapon_min_queue_size_far"); // 1; + m_max_queue_size_far = pSettings->r_u32(*cNameSect(),"weapon_max_queue_size_far"); // 6; + m_min_queue_interval_far = pSettings->r_u32(*cNameSect(),"weapon_min_queue_interval_far"); // 500; + m_max_queue_interval_far = pSettings->r_u32(*cNameSect(),"weapon_max_queue_interval_far"); // 1000; + + m_min_queue_size_medium = pSettings->r_u32(*cNameSect(),"weapon_min_queue_size_medium"); // 4; + m_max_queue_size_medium = pSettings->r_u32(*cNameSect(),"weapon_max_queue_size_medium"); // 6; + m_min_queue_interval_medium = pSettings->r_u32(*cNameSect(),"weapon_min_queue_interval_medium"); // 500; + m_max_queue_interval_medium = pSettings->r_u32(*cNameSect(),"weapon_max_queue_interval_medium"); // 750; + + m_min_queue_size_close = pSettings->r_u32(*cNameSect(),"weapon_min_queue_size_close"); // 4; + m_max_queue_size_close = pSettings->r_u32(*cNameSect(),"weapon_max_queue_size_close"); // 10; + m_min_queue_interval_close = pSettings->r_u32(*cNameSect(),"weapon_min_queue_interval_close"); // 300; + m_max_queue_interval_close = pSettings->r_u32(*cNameSect(),"weapon_max_queue_interval_close"); // 500; + + m_power_fx_factor = pSettings->r_float(section,"power_fx_factor"); +} + +void CAI_Stalker::Die (CObject* who) +{ + notify_on_wounded_or_killed (who); + + SelectAnimation (XFORM().k,movement().detail().direction(),movement().speed()); + + sound().set_sound_mask (0); + if (is_special_killer(who)) + sound().play (eStalkerSoundDieInAnomaly); + else + sound().play (eStalkerSoundDie); + + m_hammer_is_clutched = m_clutched_hammer_enabled && !CObjectHandler::planner().m_storage.property(ObjectHandlerSpace::eWorldPropertyStrapped) && !::Random.randI(0,2); + + inherited::Die (who); + + //запретить использование слотов в инвенторе + inventory().SetSlotsUseful (false); +} + +void CAI_Stalker::Load (LPCSTR section) +{ + CCustomMonster::Load (section); + CObjectHandler::Load (section); + sight().Load (section); + movement().Load (section); + + // skeleton physics + m_pPhysics_support->in_Load (section); + + m_can_select_items = !!pSettings->r_bool(section,"can_select_items"); + m_bIsGhost = false; + if (pSettings->line_exist(section,"is_ghost")) + m_bIsGhost = !!pSettings->r_bool(section,"is_ghost"); +} + +BOOL CAI_Stalker::net_Spawn (CSE_Abstract* DC) +{ +#ifdef DEBUG_MEMORY_MANAGER + u32 start = 0; + if (g_bMEMO) + start = Memory.mem_usage(); +#endif // DEBUG_MEMORY_MANAGER + + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeHumanStalker *tpHuman = smart_cast(e); + R_ASSERT (tpHuman); + m_group_behaviour = !!tpHuman->m_flags.test(CSE_ALifeObject::flGroupBehaviour); + + if (!CObjectHandler::net_Spawn(DC) || !inherited::net_Spawn(DC)) + return (FALSE); + + set_money (tpHuman->m_dwMoney, false); + +#ifdef DEBUG_MEMORY_MANAGER + u32 _start = 0; + if (g_bMEMO) + _start = Memory.mem_usage(); +#endif // DEBUG_MEMORY_MANAGER + + animation().reload (this); + +#ifdef DEBUG_MEMORY_MANAGER + if (g_bMEMO) + Msg ("CStalkerAnimationManager::reload() : %d",Memory.mem_usage() - _start); +#endif // DEBUG_MEMORY_MANAGER + + movement().m_head.current.yaw = movement().m_head.target.yaw = movement().m_body.current.yaw = movement().m_body.target.yaw = angle_normalize_signed(-tpHuman->o_torso.yaw); + movement().m_body.current.pitch = movement().m_body.target.pitch = 0; + + if (ai().game_graph().valid_vertex_id(tpHuman->m_tGraphID)) + ai_location().game_vertex (tpHuman->m_tGraphID); + + if (ai().game_graph().valid_vertex_id(tpHuman->m_tNextGraphID) && movement().restrictions().accessible(ai().game_graph().vertex(tpHuman->m_tNextGraphID)->level_point())) + movement().set_game_dest_vertex (tpHuman->m_tNextGraphID); + + R_ASSERT2 ( + ai().get_game_graph() && + ai().get_level_graph() && + ai().get_cross_table() && + (ai().level_graph().level_id() != u32(-1)), + "There is no AI-Map, level graph, cross table, or graph is not compiled into the game graph!" + ); + + setEnabled (TRUE); + + + if (!Level().CurrentViewEntity()) + Level().SetEntity(this); + + if (!g_Alive()) + sound().set_sound_mask(u32(eStalkerSoundMaskDie)); + + //загрузить иммунитеты из модельки сталкера + IKinematics* pKinematics = smart_cast(Visual()); VERIFY(pKinematics); + CInifile* ini = pKinematics->LL_UserData(); + if(ini) + { + if(ini->section_exist("immunities")) + { + LPCSTR imm_sect = ini->r_string("immunities", "immunities_sect"); + conditions().LoadImmunities(imm_sect,pSettings); + } + + if(ini->line_exist("bone_protection","bones_protection_sect")){ + m_boneHitProtection = new SBoneProtections(); + m_boneHitProtection->reload (ini->r_string("bone_protection","bones_protection_sect"), pKinematics ); + } + } + + //вычислить иммунета в зависимости от ранга + static float novice_rank_immunity = pSettings->r_float("ranks_properties", "immunities_novice_k"); + static float expirienced_rank_immunity = pSettings->r_float("ranks_properties", "immunities_experienced_k"); + + static float novice_rank_visibility = pSettings->r_float("ranks_properties", "visibility_novice_k"); + static float expirienced_rank_visibility = pSettings->r_float("ranks_properties", "visibility_experienced_k"); + + static float novice_rank_dispersion = pSettings->r_float("ranks_properties", "dispersion_novice_k"); + static float expirienced_rank_dispersion = pSettings->r_float("ranks_properties", "dispersion_experienced_k"); + + + CHARACTER_RANK_VALUE rank = Rank(); + clamp(rank, 0, 100); + float rank_k = float(rank)/100.f; + m_fRankImmunity = novice_rank_immunity + (expirienced_rank_immunity - novice_rank_immunity) * rank_k; + m_fRankVisibility = novice_rank_visibility + (expirienced_rank_visibility - novice_rank_visibility) * rank_k; + m_fRankDisperison = expirienced_rank_dispersion + (expirienced_rank_dispersion - novice_rank_dispersion) * (1-rank_k); + + if (!fis_zero(SpecificCharacter().panic_threshold())) + m_panic_threshold = SpecificCharacter().panic_threshold(); + + sight().setup (CSightAction(SightManager::eSightTypeCurrentDirection)); + +#ifdef _DEBUG + if (ai().get_alife() && !Level().MapManager().HasMapLocation("debug_stalker",ID())) { + CMapLocation *map_location = + Level().MapManager().AddMapLocation( + "debug_stalker", + ID() + ); + + map_location->SetHint (cName()); + } +#endif // _DEBUG + +#ifdef DEBUG_MEMORY_MANAGER + if (g_bMEMO) { + Msg ("CAI_Stalker::net_Spawn() : %d",Memory.mem_usage() - start); + } +#endif // DEBUG_MEMORY_MANAGER + + if(SpecificCharacter().terrain_sect().size()) + { + movement().locations().Load(*SpecificCharacter().terrain_sect()); + } + + m_pPhysics_support->in_NetSpawn (e); + + // if (m_bIsGhost) character_physics_support()->movement()->DestroyCharacter(); //because stalkers cant walk + + return (TRUE); +} + +void CAI_Stalker::net_Destroy() +{ + inherited::net_Destroy (); + CInventoryOwner::net_Destroy (); + m_pPhysics_support->in_NetDestroy (); + + Device.remove_from_seq_parallel ( + fastdelegate::FastDelegate0<>( + this, + &CAI_Stalker::update_object_handler + ) + ); + +#ifdef DEBUG + fastdelegate::FastDelegate0<> f = fastdelegate::FastDelegate0<>(this,&CAI_Stalker::update_object_handler); + xr_vector >::const_iterator I; + I = std::find(Device.seqParallel.begin(),Device.seqParallel.end(),f); + VERIFY (I == Device.seqParallel.end()); +#endif // DEBUG + + xr_delete (m_ce_close); + xr_delete (m_ce_far); + xr_delete (m_ce_best); + xr_delete (m_ce_angle); + xr_delete (m_ce_safe); + xr_delete (m_ce_random_game); + xr_delete (m_ce_ambush); + xr_delete (m_ce_best_by_time); + xr_delete (m_boneHitProtection); +} + +void CAI_Stalker::net_Save (NET_Packet& P) +{ + inherited::net_Save(P); + m_pPhysics_support->in_NetSave(P); +} + +BOOL CAI_Stalker::net_SaveRelevant () +{ + return (inherited::net_SaveRelevant() || BOOL(PPhysicsShell()!=NULL)); +} + +void CAI_Stalker::net_Export (NET_Packet& P) +{ + R_ASSERT (Local()); + + // export last known packet + R_ASSERT (!NET.empty()); + net_update& N = NET.back(); +// P.w_float (inventory().TotalWeight()); +// P.w_u32 (m_dwMoney); + + P.w_float (GetfHealth()); + + P.w_u32 (N.dwTimeStamp); + P.w_u8 (0); + P.w_vec3 (N.p_pos); + P.w_float /*w_angle8*/ (N.o_model); + P.w_float /*w_angle8*/ (N.o_torso.yaw); + P.w_float /*w_angle8*/ (N.o_torso.pitch); + P.w_float /*w_angle8*/ (N.o_torso.roll); + P.w_u8 (u8(g_Team())); + P.w_u8 (u8(g_Squad())); + P.w_u8 (u8(g_Group())); + + + float f1 = 0; + GameGraph::_GRAPH_ID l_game_vertex_id = ai_location().game_vertex_id(); + P.w (&l_game_vertex_id, sizeof(l_game_vertex_id)); + P.w (&l_game_vertex_id, sizeof(l_game_vertex_id)); +// P.w (&f1, sizeof(f1)); +// P.w (&f1, sizeof(f1)); + if (ai().game_graph().valid_vertex_id(l_game_vertex_id)) { + f1 = Position().distance_to (ai().game_graph().vertex(l_game_vertex_id)->level_point()); + P.w (&f1, sizeof(f1)); + f1 = Position().distance_to (ai().game_graph().vertex(l_game_vertex_id)->level_point()); + P.w (&f1, sizeof(f1)); + } + else { + P.w (&f1, sizeof(f1)); + P.w (&f1, sizeof(f1)); + } + + P.w_stringZ (m_sStartDialog); +} + +void CAI_Stalker::net_Import (NET_Packet& P) +{ + R_ASSERT (Remote()); + net_update N; + + u8 flags; + + P.r_float (); + set_money ( P.r_u32(), false ); + + float health; + P.r_float (health); + SetfHealth (health); +// fEntityHealth = health; + + P.r_u32 (N.dwTimeStamp); + P.r_u8 (flags); + P.r_vec3 (N.p_pos); + P.r_float /*r_angle8*/ (N.o_model); + P.r_float /*r_angle8*/ (N.o_torso.yaw); + P.r_float /*r_angle8*/ (N.o_torso.pitch); + P.r_float /*r_angle8*/ (N.o_torso.roll ); + id_Team = P.r_u8(); + id_Squad = P.r_u8(); + id_Group = P.r_u8(); + + + GameGraph::_GRAPH_ID graph_vertex_id = movement().game_dest_vertex_id(); + P.r (&graph_vertex_id, sizeof(GameGraph::_GRAPH_ID)); + graph_vertex_id = ai_location().game_vertex_id(); + P.r (&graph_vertex_id, sizeof(GameGraph::_GRAPH_ID)); + + if (NET.empty() || (NET.back().dwTimeStampname()); + throw; + } +#endif + catch (std::exception &message) { + Msg ("! Expression \"%s\"",message.what()); + throw; + } + catch(...) { + throw; + } + } + catch(...) { + CObjectHandler::set_goal(eObjectActionIdle); + CObjectHandler::update (); + } +} + +void CAI_Stalker::create_anim_mov_ctrl (CBlend *b) +{ + inherited::create_anim_mov_ctrl (b); + + m_sight_enabled_before_animation_controller = sight().enabled(); + sight().enable (false); +} + +void CAI_Stalker::destroy_anim_mov_ctrl () +{ + inherited::destroy_anim_mov_ctrl(); + if (!g_Alive()) + return; + + sight().enable (m_sight_enabled_before_animation_controller); + + movement().m_head.current.yaw = movement().m_body.current.yaw; + movement().m_head.current.pitch = movement().m_body.current.pitch; + movement().m_head.target.yaw = movement().m_body.current.yaw; + movement().m_head.target.pitch = movement().m_body.current.pitch; +} + +void CAI_Stalker::UpdateCL() +{ + START_PROFILE("stalker") + START_PROFILE("stalker/client_update") + VERIFY2 (PPhysicsShell()||getEnabled(), *cName()); + + if (g_Alive()) { + if (g_mt_config.test(mtObjectHandler) && CObjectHandler::planner().initialized()) { + fastdelegate::FastDelegate0<> f = fastdelegate::FastDelegate0<>(this,&CAI_Stalker::update_object_handler); +#ifdef DEBUG + xr_vector >::const_iterator I; + I = std::find(Device.seqParallel.begin(),Device.seqParallel.end(),f); + VERIFY (I == Device.seqParallel.end()); +#endif + Device.seqParallel.push_back (fastdelegate::FastDelegate0<>(this,&CAI_Stalker::update_object_handler)); + } + else { + START_PROFILE("stalker/client_update/object_handler") + update_object_handler (); + STOP_PROFILE + } + + if ( + (movement().speed(character_physics_support()->movement()) > EPS_L) + && + (eMovementTypeStand != movement().movement_type()) + && + (eMentalStateDanger == movement().mental_state()) + ) { + if ( + (eBodyStateStand == movement().body_state()) + && + (eMovementTypeRun == movement().movement_type()) + ) { + sound().play (eStalkerSoundRunningInDanger); + } + else { +// sound().play (eStalkerSoundWalkingInDanger); + } + } + } + + START_PROFILE("stalker/client_update/inherited") + inherited::UpdateCL (); + STOP_PROFILE + + START_PROFILE("stalker/client_update/physics") + m_pPhysics_support->in_UpdateCL (); + STOP_PROFILE + + if (g_Alive()) { + START_PROFILE("stalker/client_update/sight_manager") + VERIFY (!m_pPhysicsShell); + try { + sight().update (); + } + catch(...) { + sight().setup (CSightAction(SightManager::eSightTypeCurrentDirection)); + sight().update (); + } + + Exec_Look (client_update_fdelta()); + STOP_PROFILE + + START_PROFILE("stalker/client_update/step_manager") + CStepManager::update (); + STOP_PROFILE + + START_PROFILE("stalker/client_update/weapon_shot_effector") + if (weapon_shot_effector().IsActive()) + weapon_shot_effector().Update (); + STOP_PROFILE + } + STOP_PROFILE + STOP_PROFILE +} + +void CAI_Stalker ::PHHit (float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type /*ALife::eHitTypeWound*/) +{ + if (m_bIsGhost) return; + m_pPhysics_support->in_Hit(P,dir,who,element,p_in_object_space,impulse,hit_type,!g_Alive()); +} + +CPHDestroyable* CAI_Stalker:: ph_destroyable () +{ + return smart_cast(character_physics_support()); +} + +#include "../../enemy_manager.h" + +void CAI_Stalker::shedule_Update ( u32 DT ) +{ + START_PROFILE("stalker") + START_PROFILE("stalker/schedule_update") + VERIFY2 (getEnabled()||PPhysicsShell(), *cName()); + + if (!CObjectHandler::planner().initialized()) { + START_PROFILE("stalker/client_update/object_handler") + update_object_handler (); + STOP_PROFILE + } +// if (Position().distance_to(Level().CurrentEntity()->Position()) <= 50.f) +// Msg ("[%6d][SH][%s]",Device.dwTimeGlobal,*cName()); + // Queue shrink + VERIFY (_valid(Position())); + u32 dwTimeCL = Level().timeServer()-NET_Latency; + VERIFY (!NET.empty()); + while ((NET.size()>2) && (NET[1].dwTimeStamp(this,&CCustomMonster::Exec_Visibility)); + else { + START_PROFILE("stalker/schedule_update/vision") + Exec_Visibility (); + STOP_PROFILE + } + + START_PROFILE("stalker/schedule_update/memory") + + START_PROFILE("stalker/schedule_update/memory/process") + process_enemies (); + STOP_PROFILE + + START_PROFILE("stalker/schedule_update/memory/update") + memory().update (dt); + STOP_PROFILE + + STOP_PROFILE + } + + START_PROFILE("stalker/schedule_update/inherited") + inherited::inherited::shedule_Update(DT); + STOP_PROFILE + + if (Remote()) { + } else { + // here is monster AI call + VERIFY (_valid(Position())); + m_fTimeUpdateDelta = dt; + Device.Statistic->AI_Think.Begin (); + if (GetScriptControl()) + ProcessScripts (); + else +#ifdef DEBUG + if (Device.dwFrame > (spawn_time() + g_AI_inactive_time)) +#endif + Think (); + m_dwLastUpdateTime = Device.dwTimeGlobal; + Device.Statistic->AI_Think.End (); + VERIFY (_valid(Position())); + + // Look and action streams + float temp = conditions().health(); + if (temp > 0) { + START_PROFILE("stalker/schedule_update/feel_touch") + Fvector C; float R; + Center(C); + R = Radius(); + feel_touch_update (C,R); + STOP_PROFILE + + START_PROFILE("stalker/schedule_update/net_update") + net_update uNext; + uNext.dwTimeStamp = Level().timeServer(); + uNext.o_model = movement().m_body.current.yaw; + uNext.o_torso = movement().m_head.current; + uNext.p_pos = vNewPosition; + uNext.fHealth = GetfHealth(); + NET.push_back (uNext); + STOP_PROFILE + } + else + { + START_PROFILE("stalker/schedule_update/net_update") + net_update uNext; + uNext.dwTimeStamp = Level().timeServer(); + uNext.o_model = movement().m_body.current.yaw; + uNext.o_torso = movement().m_head.current; + uNext.p_pos = vNewPosition; + uNext.fHealth = GetfHealth(); + NET.push_back (uNext); + STOP_PROFILE + } + } + VERIFY (_valid(Position())); + + START_PROFILE("stalker/schedule_update/inventory_owner") + UpdateInventoryOwner(DT); + STOP_PROFILE + +//#ifdef DEBUG +// if (psAI_Flags.test(aiALife)) { +// smart_cast(ai().alife().objects().object(ID()))->check_inventory_consistency(); +// } +//#endif + + START_PROFILE("stalker/schedule_update/physics") + VERIFY (_valid(Position())); + m_pPhysics_support->in_shedule_Update(DT); + VERIFY (_valid(Position())); + STOP_PROFILE + STOP_PROFILE + STOP_PROFILE +} + +float CAI_Stalker::Radius() const +{ + float R = inherited::Radius(); + CWeapon* W = smart_cast(inventory().ActiveItem()); + if (W) R += W->Radius(); + return R; +} + +void CAI_Stalker::spawn_supplies () +{ + inherited::spawn_supplies (); + CObjectHandler::spawn_supplies (); +} + +void CAI_Stalker::Think () +{ + START_PROFILE("stalker/schedule_update/think") + u32 update_delta = Device.dwTimeGlobal - m_dwLastUpdateTime; + + START_PROFILE("stalker/schedule_update/think/brain") +// try { +// try { + brain().update (update_delta); +// } +#ifdef DEBUG +// catch (luabind::cast_failed &message) { +// Msg ("! Expression \"%s\" from luabind::object to %s",message.what(),message.info()->name()); +// throw; +// } +#endif +// catch (std::exception &message) { +// Msg ("! Expression \"%s\"",message.what()); +// throw; +// } +// catch (...) { +// Msg ("! unknown exception occured"); +// throw; +// } +// } +// catch(...) { +#ifdef DEBUG +// Msg ("! Last action being executed : %s",brain().current_action().m_action_name); +#endif +// brain().setup (this); +// brain().update (update_delta); +// } + STOP_PROFILE + + START_PROFILE("stalker/schedule_update/think/movement") + if (!g_Alive()) + return; + +// try { + movement().update (update_delta); +// } +#if 0//def DEBUG + catch (luabind::cast_failed &message) { + Msg ("! Expression \"%s\" from luabind::object to %s",message.what(),message.info()->name()); + movement().initialize (); + movement().update (update_delta); + throw; + } + catch (std::exception &message) { + Msg ("! Expression \"%s\"",message.what()); + movement().initialize (); + movement().update (update_delta); + throw; + } + catch (...) { + Msg ("! unknown exception occured"); + movement().initialize (); + movement().update (update_delta); + throw; + } +#endif // DEBUG + + STOP_PROFILE + STOP_PROFILE +} + +void CAI_Stalker::SelectAnimation(const Fvector &view, const Fvector &move, float speed) +{ + if (!Device.Paused() && g_Alive()) + animation().update(); +} + +const SRotation CAI_Stalker::Orientation () const +{ + return (movement().m_head.current); +} + +const MonsterSpace::SBoneRotation &CAI_Stalker::head_orientation () const +{ + return (movement().head_orientation()); +} + +void CAI_Stalker::net_Relcase (CObject* O) +{ + inherited::net_Relcase (O); + + sight().remove_links (O); + + if (!g_Alive()) + return; + + agent_manager().remove_links (O); + m_pPhysics_support->in_NetRelcase (O); +} + +CMovementManager *CAI_Stalker::create_movement_manager () +{ + return (m_movement_manager = new CStalkerMovementManager(this)); +} + +CSound_UserDataVisitor *CAI_Stalker::create_sound_visitor () +{ + return (m_sound_user_data_visitor = new CStalkerSoundDataVisitor(this)); +} + +CMemoryManager *CAI_Stalker::create_memory_manager () +{ + return (new CMemoryManager(this,create_sound_visitor())); +} + +DLL_Pure *CAI_Stalker::_construct () +{ +#ifdef DEBUG_MEMORY_MANAGER + u32 start = 0; + if (g_bMEMO) + start = Memory.mem_usage(); +#endif // DEBUG_MEMORY_MANAGER + + m_pPhysics_support = new CCharacterPhysicsSupport(CCharacterPhysicsSupport::etStalker,this); + CCustomMonster::_construct (); + CObjectHandler::_construct (); + CStepManager::_construct (); + + + m_actor_relation_flags.zero (); + m_animation_manager = new CStalkerAnimationManager(); + m_brain = new CStalkerPlanner(); + m_sight_manager = new CSightManager(this); + m_weapon_shot_effector = new CWeaponShotEffector(); + +#ifdef DEBUG_MEMORY_MANAGER + if (g_bMEMO) + Msg ("CAI_Stalker::_construct() : %d",Memory.mem_usage() - start); +#endif // DEBUG_MEMORY_MANAGER + + return (this); +} + +bool CAI_Stalker::use_center_to_aim () const +{ + return (!wounded() && (movement().body_state() != eBodyStateCrouch)); +} + +void CAI_Stalker::UpdateCamera () +{ + //skyloader: build code + /*float new_range = eye_range, new_fov = eye_fov; + Fvector temp = eye_matrix.k; + if (g_Alive()) { + update_range_fov (new_range, new_fov, memory().visual().current_state().m_max_view_distance*eye_range, eye_fov); + if (weapon_shot_effector().IsActive()) + temp = weapon_shot_effector_direction(temp); + } + + g_pGameLevel->Cameras().Update (eye_matrix.c,temp,eye_matrix.j,new_fov,.75f,new_range);*/ + + //my code + + u16 bone_id = smart_cast(Visual())->LL_BoneID ("bip01_head"); + CBoneInstance &bone = smart_cast(Visual())->LL_GetBoneInstance (bone_id); + + Fmatrix global_transform; + global_transform.mul (XFORM(),bone.mTransform); + + g_pGameLevel->Cameras().Update (global_transform.c,global_transform.k,eye_matrix.j,g_fov,.75f,eye_range,0); +} + +bool CAI_Stalker::can_attach (const CInventoryItem *inventory_item) const +{ + if (already_dead()) + return (false); + + return (CObjectHandler::can_attach(inventory_item)); +} + +void CAI_Stalker::save (NET_Packet &packet) +{ + inherited::save (packet); + CInventoryOwner::save (packet); + brain().save (packet); +} + +void CAI_Stalker::load (IReader &packet) +{ + inherited::load (packet); + CInventoryOwner::load (packet); + brain().load (packet); +} + +void CAI_Stalker::load_critical_wound_bones() +{ + fill_bones_body_parts ("head", critical_wound_type_head); + fill_bones_body_parts ("torso", critical_wound_type_torso); + fill_bones_body_parts ("hand_left", critical_wound_type_hand_left); + fill_bones_body_parts ("hand_right", critical_wound_type_hand_right); + fill_bones_body_parts ("leg_left", critical_wound_type_leg_left); + fill_bones_body_parts ("leg_right", critical_wound_type_leg_right); +} + +void CAI_Stalker::fill_bones_body_parts (LPCSTR bone_id, const ECriticalWoundType &wound_type) +{ + LPCSTR body_parts_section_id = pSettings->r_string(cNameSect(),"body_parts_section_id"); + VERIFY (body_parts_section_id); + + LPCSTR body_part_section_id = pSettings->r_string(body_parts_section_id,bone_id); + VERIFY (body_part_section_id); + + IKinematics *kinematics = smart_cast(Visual()); + VERIFY (kinematics); + + CInifile::Sect &body_part_section = pSettings->r_section(body_part_section_id); + CInifile::SectCIt I = body_part_section.Data.begin(); + CInifile::SectCIt E = body_part_section.Data.end(); + for ( ; I != E; ++I) + m_bones_body_parts.insert ( + std::make_pair( + kinematics->LL_BoneID((*I).first), + u32(wound_type) + ) + ); +} + +void CAI_Stalker::on_before_change_team () +{ + m_registered_in_combat_on_migration = agent_manager().member().registered_in_combat(this); +} + +void CAI_Stalker::on_after_change_team () +{ + if (!m_registered_in_combat_on_migration) + return; + + agent_manager().member().register_in_combat (this); +} diff --git a/src/xrGameLA/ai/stalker/ai_stalker.h b/src/xrGameLA/ai/stalker/ai_stalker.h new file mode 100644 index 000000000..33f19c6e7 --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker.h @@ -0,0 +1,606 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker.h +// Created : 25.02.2003 +// Modified : 25.02.2003 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Stalker" +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "../../CustomMonster.h" +#include "../../object_handler.h" +#include "../../AI_PhraseDialogManager.h" +#include "../../step_manager.h" +#include "../../script_export_space.h" + +#ifdef DEBUG + template + class CActionBase; + + template + class CPropertyEvaluator; + + template < + typename _object_type, + bool _reverse_search, + typename _world_operator, + typename _condition_evaluator, + typename _world_operator_ptr, + typename _condition_evaluator_ptr + > + class CActionPlanner; + + typedef CActionPlanner< + CScriptGameObject, + false, + CActionBase, + CPropertyEvaluator, + CActionBase*, + CPropertyEvaluator* + > script_planner; +#endif + +namespace MonsterSpace { + enum EMovementDirection; +}; + +namespace StalkerSpace { + enum EBodyAction; +}; + +enum ECriticalWoundType; + +class CALifeSimulator; +class CCharacterPhysicsSupport; +class CWeapon; +class CCoverPoint; +class CCoverEvaluatorCloseToEnemy; +class CCoverEvaluatorFarFromEnemy; +class CCoverEvaluatorBest; +class CCoverEvaluatorAngle; +class CCoverEvaluatorSafe; +class CCoverEvaluatorRandomGame; +class CCoverEvaluatorAmbush; +class CCoverEvaluatorBestByTime; +class CAgentManager; +class CMotionDef; +class CStalkerAnimationManager; +class CStalkerPlanner; +class CSightManager; +class CStalkerMovementManager; +class CStalkerSoundDataVisitor; +class CWeaponShotEffector; +struct SBoneProtections; +class CDangerLocation; +class CRestrictedObject; + +class CAI_Stalker : + public CCustomMonster, + public CObjectHandler, + public CAI_PhraseDialogManager, + public CStepManager +{ +private: + typedef CCustomMonster inherited; + +public: + using inherited::useful; + using inherited::evaluate; + +private: + CStalkerAnimationManager *m_animation_manager; + CStalkerPlanner *m_brain; + CSightManager *m_sight_manager; + CStalkerMovementManager *m_movement_manager; + CAgentManager *m_agent_manager; + +#ifdef LOG_PLANNER + const script_planner *m_debug_planner; +#endif + + // ALife +private: + SBoneProtections* m_boneHitProtection; + + // weapon dispersion +private: + float m_disp_walk_stand; + float m_disp_walk_crouch; + float m_disp_run_stand; + float m_disp_run_crouch; + float m_disp_stand_stand; + float m_disp_stand_crouch; + float m_disp_stand_stand_zoom; + float m_disp_stand_crouch_zoom; + +private: + float m_power_fx_factor; + +private: + float m_fRankDisperison; + float m_fRankVisibility; + float m_fRankImmunity; + + // best item/ammo selection members +public: + bool m_item_actuality; + CInventoryItem *m_best_item_to_kill; + float m_best_item_value; + CInventoryItem *m_best_ammo; + const CInventoryItem *m_best_found_item_to_kill; + const CInventoryItem *m_best_found_ammo; + + // covers being used +public: + CCoverEvaluatorCloseToEnemy *m_ce_close; + CCoverEvaluatorFarFromEnemy *m_ce_far; + CCoverEvaluatorBest *m_ce_best; + CCoverEvaluatorAngle *m_ce_angle; + CCoverEvaluatorSafe *m_ce_safe; + CCoverEvaluatorRandomGame *m_ce_random_game; + CCoverEvaluatorAmbush *m_ce_ambush; + CCoverEvaluatorBestByTime *m_ce_best_by_time; + + // physics support +public: + CCharacterPhysicsSupport *m_pPhysics_support; + +public: + bool m_wounded; + +public: + CAI_Stalker (); + virtual ~CAI_Stalker (); + +public: + virtual CCharacterPhysicsSupport* character_physics_support () {return m_pPhysics_support;} + + virtual CPHDestroyable* ph_destroyable () ; + virtual CAttachmentOwner* cast_attachment_owner () {return this;} + virtual CInventoryOwner* cast_inventory_owner () {return this;} + virtual CEntityAlive* cast_entity_alive () {return this;} + virtual CEntity* cast_entity () {return this;} + virtual CGameObject* cast_game_object () {return this;} + virtual CPhysicsShellHolder* cast_physics_shell_holder () {return this;} + virtual CParticlesPlayer* cast_particles_player () {return this;} + virtual Feel::Sound* dcast_FeelSound () {return this;} + virtual CAI_Stalker* cast_stalker () {return this;} + virtual CCustomMonster* cast_custom_monster () {return this;} + virtual CScriptEntity* cast_script_entity () {return this;} + +public: + void init (); + virtual void Load (LPCSTR section ); + virtual void reinit (); + virtual void reload (LPCSTR section ); + virtual void LoadSounds (LPCSTR section ); + + virtual BOOL net_Spawn (CSE_Abstract* DC); + virtual void net_Export (NET_Packet& P); + virtual void net_Import (NET_Packet& P); + virtual void net_Destroy (); + virtual void net_Save (NET_Packet& P); + virtual BOOL net_SaveRelevant (); + virtual void net_Relcase (CObject* O); + + //save/load server serialization + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + + virtual void UpdateCL (); + virtual void shedule_Update (u32 dt); + virtual void Think (); + virtual void SelectAnimation (const Fvector& _view, const Fvector& _move, float speed ); + virtual BOOL UsedAI_Locations (); + + virtual void g_WeaponBones (int &L, int &R1, int &R2); + virtual void g_fireParams (const CHudItem* pHudItem, Fvector& P, Fvector& D); + virtual void HitSignal (float P, Fvector& vLocalDir, CObject* who, s16 element); + virtual void Die (CObject* who); + + virtual void OnEvent (NET_Packet& P, u16 type); + virtual void feel_touch_new (CObject* O); + + virtual void renderable_Render (); + virtual void Exec_Look (float dt); + virtual void Hit (SHit* pHDS); + virtual void PHHit (float P,Fvector &dir, CObject *who,s16 element,Fvector p_in_object_space, float impulse, ALife::EHitType hit_type = ALife::eHitTypeWound); + virtual BOOL feel_vision_isRelevant (CObject* who); + virtual float Radius () const; +#ifdef LOG_PLANNER + virtual void OnHUDDraw (CCustomHUD* hud); + virtual void OnRender (); +#endif + void SetDebug (bool state); + virtual bool useful (const CItemManager *manager, const CGameObject *object) const; + virtual float evaluate (const CItemManager *manager, const CGameObject *object) const; + virtual bool useful (const CEnemyManager *manager, const CEntityAlive *object) const; + + // PDA && Dialogs +// virtual void ReceivePdaMessage (u16 who, EPdaMsg msg, shared_str info_id); + virtual void UpdateAvailableDialogs (CPhraseDialogManager* partner); + + // scripts + virtual CWeapon *GetCurrentWeapon () const; + virtual u32 GetWeaponAmmo () const; +// virtual CInventoryItem *GetCurrentEquipment () const; <- moved to InventoryOwner::GetCurrentOutfit + virtual CInventoryItem *GetMedikit () const; + virtual CInventoryItem *GetFood () const; + virtual bool bfAssignMovement (CScriptEntityAction *tpEntityAction); + virtual bool bfAssignWatch (CScriptEntityAction *tpEntityAction); + virtual void ResetScriptData (void *P = 0); + virtual bool bfAssignObject (CScriptEntityAction *tpEntityAction); + virtual bool bfAssignAnimation (CScriptEntityAction *tpEntityAction); + + // physics + virtual u16 PHGetSyncItemsNumber () {return inherited ::PHGetSyncItemsNumber();} + virtual CPHSynchronize* PHGetSyncItem (u16 item) {return inherited ::PHGetSyncItem(item);} + virtual void PHUnFreeze () {return inherited ::PHUnFreeze();} + virtual void PHFreeze () {return inherited ::PHFreeze();} + + // miscellanious functions + void DropItemSendMessage (CObject *O); + bool bfCheckForNodeVisibility(u32 dwNodeID, bool bIfRyPick = false); + virtual ALife::ERelationType tfGetRelationType (const CEntityAlive *tpEntityAlive) const; + virtual const SRotation Orientation () const; + virtual const MonsterSpace::SBoneRotation &head_orientation () const; + + //InventoryOwner stuff + virtual bool CanPutInSlot (PIItem item, u32 slot) {return(slot!=OUTFIT_SLOT && slot!=RIFLE_2_SLOT);}; + + virtual SBoneProtections* GetBoneHitProtections () {return m_boneHitProtection;}; + + ////////////////////////////////////////////////////////////////////////// + // action/evaluators support functions + ////////////////////////////////////////////////////////////////////////// + +public: + void OnItemTake (CInventoryItem *inventory_item, bool duringSpawn) override; + virtual void OnItemDrop (CInventoryItem *inventory_item); + bool item_to_kill (); + bool item_can_kill (); + bool remember_item_to_kill (); + bool remember_ammo (); + bool ready_to_kill (); + bool ready_to_detour (); + void update_best_item_info (); + virtual float GetWeaponAccuracy () const; + virtual void spawn_supplies (); + IC CAgentManager &agent_manager () const; + IC void set_agent_manager (CAgentManager *manager) { m_agent_manager = manager; } + + virtual bool human_being () const + { + return (true); + } + + bool undetected_anomaly (); + bool inside_anomaly (); + bool IsGhost () {return m_bIsGhost;}; + +private: + bool m_can_kill_member; + bool m_can_kill_enemy; + float m_pick_distance; + u32 m_pick_frame_id; + collide::rq_results rq_storage; + +private: + void can_kill_entity (const Fvector &position, const Fvector &direction, float distance, collide::rq_results& rq_storage); + void can_kill_entity_from (const Fvector &position, Fvector direction, float distance); + void update_can_kill_info (); + +public: + bool can_kill_member (); + bool can_kill_enemy (); + float pick_distance (); + IC float start_pick_distance () const; + bool fire_make_sense (); + + virtual LPCSTR Name () const; + virtual BOOL feel_touch_contact (CObject* O); + virtual BOOL feel_touch_on_contact (CObject* O); + + //флаги, какие действия совершал актер по отношению к сталкеру + //(помог, атаковал и т.д.) + Flags32 m_actor_relation_flags; + + // ALife +private: + struct CTradeItem { + CInventoryItem *m_item; + ALife::_OBJECT_ID m_owner_id; + ALife::_OBJECT_ID m_new_owner_id; + + IC CTradeItem ( + CInventoryItem *item, + ALife::_OBJECT_ID owner_id, + ALife::_OBJECT_ID new_owner_id + ) + { + m_item = item; + m_owner_id = owner_id; + m_new_owner_id = new_owner_id; + } + + IC bool operator< (const CTradeItem &trade_item) const; + IC bool operator== (u16 id) const; + }; + +private: + CGameObject *m_trader_game_object; + CInventoryOwner *m_current_trader; + xr_vector m_temp_items; + u32 m_total_money; + bool m_sell_info_actuality; + +protected: + u32 fill_items (CInventory &inventory, CGameObject *old_owner, ALife::_OBJECT_ID new_owner_id); + + IC void buy_item_virtual (CTradeItem &item); + void attach_available_ammo (CWeapon *weapon); + void choose_food (); + void choose_weapon (ALife::EWeaponPriorityType weapon_priority_type); + void choose_medikit (); + void choose_detector (); + void choose_equipment (); + + void select_items (); + void transfer_item (CInventoryItem *item, CGameObject *old_owner, CGameObject *new_owner); + + void update_sell_info (); + bool tradable_item (CInventoryItem *inventory_item, const u16 ¤t_owner_id); +public: + bool can_sell (CInventoryItem const * item); + bool can_take (CInventoryItem const * item); +protected: + bool non_conflicted (const CInventoryItem *item, const CWeapon *new_weapon) const; + bool enough_ammo (const CWeapon *new_weapon) const; + bool conflicted (const CInventoryItem *item, const CWeapon *new_weapon, bool new_wepon_enough_ammo, int new_weapon_rank) const; + void update_conflicted (CInventoryItem *item, const CWeapon *new_weapon); + void remove_personal_only_ammo (const CInventoryItem *item); + void on_after_take (const CGameObject *object); + virtual bool AllowItemToTrade (CInventoryItem const * item, EItemPlace place) const; +public: + IC CStalkerAnimationManager &animation () const; + IC CStalkerPlanner &brain () const; + IC CSightManager &sight () const; + +private: + CStalkerSoundDataVisitor *m_sound_user_data_visitor; + +protected: + virtual CSound_UserDataVisitor *create_sound_visitor (); + virtual CMemoryManager *create_memory_manager (); + virtual CMovementManager *create_movement_manager (); + +public: + IC CStalkerMovementManager &movement () const; + virtual DLL_Pure *_construct (); + +private: + IC bool frame_check (u32 &frame); + virtual bool natural_weapon () const {return false;} + virtual bool natural_detector () const {return false;} + virtual bool use_center_to_aim () const; + void process_enemies (); + +private: + bool m_group_behaviour; + bool m_bIsGhost; + +public: + IC bool group_behaviour () const; + virtual void update_range_fov (float &new_range, float &new_fov, float start_range, float start_fov); + void __stdcall update_object_handler (); + bool zoom_state () const; + void react_on_grenades (); + void react_on_member_death (); +private: + CWeaponShotEffector *m_weapon_shot_effector; + s32 m_weapon_shot_random_seed; + +public: + virtual void on_weapon_shot_start (CWeapon *weapon); + virtual void on_weapon_shot_stop (CWeapon *weapon); + virtual void on_weapon_hide (CWeapon *weapon); + IC CWeaponShotEffector &weapon_shot_effector () const; + IC Fvector weapon_shot_effector_direction (const Fvector ¤t) const; + virtual void UpdateCamera (); + virtual bool can_attach (const CInventoryItem *inventory_item) const; + virtual bool use_simplified_visual () const {return (already_dead());}; +#ifdef LOG_PLANNER + void debug_planner (const script_planner *planner); +#endif + +private: + u32 m_min_queue_size_far; + u32 m_max_queue_size_far; + u32 m_min_queue_interval_far; + u32 m_max_queue_interval_far; + + u32 m_min_queue_size_medium; + u32 m_max_queue_size_medium; + u32 m_min_queue_interval_medium; + u32 m_max_queue_interval_medium; + + u32 m_min_queue_size_close; + u32 m_max_queue_size_close; + u32 m_min_queue_interval_close; + u32 m_max_queue_interval_close; + +public: + IC u32 min_queue_size_far () const; + IC u32 max_queue_size_far () const; + IC u32 min_queue_interval_far () const; + IC u32 max_queue_interval_far () const; + + IC u32 min_queue_size_medium () const; + IC u32 max_queue_size_medium () const; + IC u32 min_queue_interval_medium () const; + IC u32 max_queue_interval_medium () const; + + IC u32 min_queue_size_close () const; + IC u32 max_queue_size_close () const; + IC u32 min_queue_interval_close () const; + IC u32 max_queue_interval_close () const; + +public: + typedef fastdelegate::FastDelegate on_best_cover_changed_delegate; + +private: + typedef xr_vector cover_delegates; + +private: + cover_delegates m_cover_delegates; + const CCoverPoint *m_best_cover; + float m_best_cover_value; + bool m_best_cover_actual; + bool m_best_cover_can_try_advance; + const CCoverPoint *m_best_cover_advance_cover; + +private: + float best_cover_value (const Fvector &position_to_cover_from); + void compute_enemy_distances (float &minimum_enemy_distance, float &maximum_enemy_distance); + const CCoverPoint *find_best_cover (const Fvector &position_to_cover_from); + void update_best_cover_actuality (const Fvector &position_to_cover_from); + void on_best_cover_changed (const CCoverPoint *new_cover, const CCoverPoint *old_cover); + +public: + void best_cover_can_try_advance (); + const CCoverPoint *best_cover (const Fvector &position_to_cover_from); + +public: + void subscribe_on_best_cover_changed (const on_best_cover_changed_delegate &delegate); + void unsubscribe_on_best_cover_changed (const on_best_cover_changed_delegate &delegate); + +public: + virtual void on_enemy_change (const CEntityAlive *enemy); + virtual void on_restrictions_change (); + void on_cover_blocked (const CCoverPoint *cover); + void on_danger_location_add (const CDangerLocation &location); + void on_danger_location_remove (const CDangerLocation &location); + +public: + void wounded (bool value); + void wounded (bool value, bool dest); + bool wounded (const CRestrictedObject *object) const; + IC bool wounded () const; + +// throwing grenades +private: + // actuality parameters + bool m_throw_actual; + Fvector m_computed_object_position; + Fvector m_computed_object_direction; + // target parameters + Fvector m_throw_target_position; + CObject *m_throw_ignore_object; + // computed + Fvector m_throw_position; + Fvector m_throw_velocity; + // collision prediction + Fvector m_throw_collide_position; + bool m_throw_enabled; + + u32 m_last_throw_time; + u32 m_throw_time_interval; +public: + IC const bool &throw_enabled (); + +private: + bool m_can_throw_grenades; + +public: + IC const bool &can_throw_grenades () const; + IC void can_throw_grenades (const bool &value); + +private: + bool throw_check_error ( + float low, + float high, + const Fvector &start, + const Fvector &velocity, + const Fvector &gravity + ); + void check_throw_trajectory (const float &throw_time); + void throw_target_impl (const Fvector &position, CObject *throw_ignore_object ); + void compute_throw_miss ( u32 const vertex_id ); + +public: + virtual bool use_default_throw_force (); + virtual float missile_throw_force (); + virtual bool use_throw_randomness (); + void throw_target (const Fvector &position, CObject *throw_ignore_object ); + void throw_target (const Fvector &position, u32 const vertex_id, CObject *throw_ignore_object ); + IC const Fvector &throw_target () const; + void update_throw_params (); + void on_throw_completed (); + IC const u32 &last_throw_time () const; + +#ifdef LOG_PLANNER +public: + void dbg_draw_vision (); + void dbg_draw_visibility_rays (); +#endif + + + ////////////////////////////////////////////////////////////////////////// + // Critical Wounds + ////////////////////////////////////////////////////////////////////////// +private: + virtual void load_critical_wound_bones (); + virtual bool critical_wound_external_conditions_suitable (); + virtual void critical_wounded_state_start (); + + void fill_bones_body_parts (LPCSTR bone_id, const ECriticalWoundType &wound_type); +public: + typedef xr_vector CRITICAL_WOUND_WEIGHTS; + +private: + CRITICAL_WOUND_WEIGHTS m_critical_wound_weights; + +public: + bool critically_wounded (); + IC const CRITICAL_WOUND_WEIGHTS&critical_wound_weights () const; + +private: + bool can_cry_enemy_is_wounded () const; + void on_critical_wound_initiator (const CAI_Stalker *critically_wounded); + void on_enemy_wounded_or_killed (const CAI_Stalker *wounded_or_killed); + void notify_on_wounded_or_killed (CObject *object); + void notify_on_wounded_or_killed (); + void xr_stdcall remove_critical_hit (); +////////////////////////////////////////////////////////////////////////// +private: + bool m_registered_in_combat_on_migration; + +public: + virtual void on_before_change_team (); + virtual void on_after_change_team (); + +private: + bool m_sight_enabled_before_animation_controller; + +public: + virtual void create_anim_mov_ctrl (CBlend *b); + virtual void destroy_anim_mov_ctrl (); + +private: + bool m_can_select_items; +// gr1ph +public: + IC const u32 &throw_time_interval () const; + IC void throw_time_interval (const u32 &value); + +public: + bool too_far_to_kill_enemy (const Fvector &position); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CAI_Stalker) +#undef script_type_list +#define script_type_list save_type_list(CAI_Stalker) + +#include "ai_stalker_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai/stalker/ai_stalker_cover.cpp b/src/xrGameLA/ai/stalker/ai_stalker_cover.cpp new file mode 100644 index 000000000..690602037 --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker_cover.cpp @@ -0,0 +1,218 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_cover.cpp +// Created : 25.04.2006 +// Modified : 25.04.2006 +// Author : Dmitriy Iassenev +// Description : +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_stalker.h" +#include "../../cover_point.h" +#include "../../cover_evaluators.h" +#include "../../ai_space.h" +#include "../../cover_manager.h" +#include "../../stalker_movement_restriction.h" +#include "../../level_graph.h" +#include "../../Weapon.h" + +extern const float MIN_SUITABLE_ENEMY_DISTANCE = 3.f; + +#ifdef _DEBUG +static int g_advance_search_count = 0; +static int g_near_cover_search_count = 0; +static int g_far_cover_search_count = 0; +#endif + +void CAI_Stalker::subscribe_on_best_cover_changed (const on_best_cover_changed_delegate &delegate) +{ + VERIFY (m_cover_delegates.end() == std::find(m_cover_delegates.begin(),m_cover_delegates.end(),delegate)); + m_cover_delegates.push_back (delegate); +} + +void CAI_Stalker::unsubscribe_on_best_cover_changed (const on_best_cover_changed_delegate &delegate) +{ + cover_delegates::iterator I = std::find(m_cover_delegates.begin(),m_cover_delegates.end(),delegate); + VERIFY (I != m_cover_delegates.end()); + m_cover_delegates.erase (I); +} + +void CAI_Stalker::on_best_cover_changed (const CCoverPoint *new_cover, const CCoverPoint *old_cover) +{ + cover_delegates::const_iterator I = m_cover_delegates.begin(); + cover_delegates::const_iterator E = m_cover_delegates.end(); + for ( ; I != E; ++I) + (*I) (new_cover,old_cover); +} + +const CCoverPoint *CAI_Stalker::find_best_cover (const Fvector &position_to_cover_from) +{ +#ifdef _DEBUG +// Msg ("* [%6d][%s] search for new cover performed",Device.dwTimeGlobal,*cName()); +#endif +#ifdef _DEBUG + ++g_near_cover_search_count; +#endif + m_ce_best->setup (position_to_cover_from,MIN_SUITABLE_ENEMY_DISTANCE,170.f,MIN_SUITABLE_ENEMY_DISTANCE); + const CCoverPoint *point = ai().cover_manager().best_cover(Position(),10.f,*m_ce_best,CStalkerMovementRestrictor(this,true)); + if (point) + return (point); + +#ifdef _DEBUG + ++g_far_cover_search_count; +#endif + m_ce_best->setup (position_to_cover_from,10.f,170.f,10.f); + point = ai().cover_manager().best_cover(Position(),30.f,*m_ce_best,CStalkerMovementRestrictor(this,true)); + return (point); +} + +float CAI_Stalker::best_cover_value (const Fvector &position_to_cover_from) +{ + m_ce_best->setup (position_to_cover_from,MIN_SUITABLE_ENEMY_DISTANCE,170.f,MIN_SUITABLE_ENEMY_DISTANCE); + m_ce_best->initialize (Position(),true); + m_ce_best->evaluate (m_best_cover,CStalkerMovementRestrictor(this,true).weight(m_best_cover)); + return (m_ce_best->best_value()); +} + +void CAI_Stalker::best_cover_can_try_advance () +{ + if (!m_best_cover_actual) + return; + + if (m_best_cover_advance_cover == m_best_cover) + return; + + m_best_cover_can_try_advance = true; +} + +void CAI_Stalker::update_best_cover_actuality (const Fvector &position_to_cover_from) +{ + if (!m_best_cover_actual) + return; + + if (!m_best_cover) + return; + + if (m_best_cover->position().distance_to_sqr(position_to_cover_from) < _sqr(MIN_SUITABLE_ENEMY_DISTANCE)) { + m_best_cover_actual = false; +#if 0//def _DEBUG + Msg ("* [%6d][%s] enemy too close",Device.dwTimeGlobal,*cName()); +#endif + return; + } + + float cover_value = best_cover_value(position_to_cover_from); + if (cover_value >= m_best_cover_value + 1.f) { + m_best_cover_actual = false; +#if 0//def _DEBUG + Msg ("* [%6d][%s] cover became too bad",Device.dwTimeGlobal,*cName()); +#endif + return; + } + +// if (cover_value >= 1.5f*m_best_cover_value) { +// m_best_cover_actual = false; +// Msg ("* [%6d][%s] cover became too bad2",Device.dwTimeGlobal,*cName()); +// return; +// } + + if (!m_best_cover_can_try_advance) + return; + + if (m_best_cover_advance_cover == m_best_cover) + return; + + m_best_cover_advance_cover = m_best_cover; + m_best_cover_can_try_advance = false; + +#ifdef _DEBUG +// Msg ("* [%6d][%s] advance search performed",Device.dwTimeGlobal,*cName()); +#endif +#ifdef _DEBUG + ++g_advance_search_count; +#endif + m_ce_best->setup (position_to_cover_from,MIN_SUITABLE_ENEMY_DISTANCE,170.f,MIN_SUITABLE_ENEMY_DISTANCE); + m_best_cover = ai().cover_manager().best_cover(Position(),10.f,*m_ce_best,CStalkerMovementRestrictor(this,true)); +} + +const CCoverPoint *CAI_Stalker::best_cover (const Fvector &position_to_cover_from) +{ + update_best_cover_actuality (position_to_cover_from); + + if (m_best_cover_actual) + return (m_best_cover); + + m_best_cover_actual = true; + + const CCoverPoint *best_cover = find_best_cover(position_to_cover_from); + if (best_cover != m_best_cover) { + on_best_cover_changed (best_cover,m_best_cover); + m_best_cover = best_cover; + m_best_cover_advance_cover = 0; + m_best_cover_can_try_advance = false; + } + m_best_cover_value = m_best_cover ? best_cover_value(position_to_cover_from) : flt_max; + + return (m_best_cover); +} + +void CAI_Stalker::on_restrictions_change () +{ + inherited::on_restrictions_change (); + m_best_cover_actual = false; +#ifdef _DEBUG + Msg ("* [%6d][%s] on_restrictions_change",Device.dwTimeGlobal,*cName()); +#endif +} + +void CAI_Stalker::on_enemy_change (const CEntityAlive *enemy) +{ + inherited::on_enemy_change (enemy); + m_item_actuality = false; + m_best_cover_actual = false; +#ifdef _DEBUG +// Msg ("* [%6d][%s] on_enemy_change",Device.dwTimeGlobal,*cName()); +#endif +} + +void CAI_Stalker::on_danger_location_add (const CDangerLocation &location) +{ + if (!m_best_cover) + return; + + if (m_best_cover->position().distance_to_sqr(location.position()) <= _sqr(location.m_radius)) { +#ifdef _DEBUG +// Msg ("* [%6d][%s] on_danger_add",Device.dwTimeGlobal,*cName()); +#endif + m_best_cover_actual = false; + } +} + +void CAI_Stalker::on_danger_location_remove (const CDangerLocation &location) +{ + if (!m_best_cover) { + if (Position().distance_to_sqr(location.position()) <= _sqr(location.m_radius)) { +#ifdef _DEBUG +// Msg ("* [%6d][%s] on_danger_remove",Device.dwTimeGlobal,*cName()); +#endif + m_best_cover_actual = false; + } + + return; + } + + if (m_best_cover->position().distance_to_sqr(location.position()) <= _sqr(location.m_radius)) { +#ifdef _DEBUG +// Msg ("* [%6d][%s] on_danger_remove",Device.dwTimeGlobal,*cName()); +#endif + m_best_cover_actual = false; + } +} + +void CAI_Stalker::on_cover_blocked (const CCoverPoint *cover) +{ +#ifdef _DEBUG +// Msg ("* [%6d][%s] cover is blocked",Device.dwTimeGlobal,*cName()); +#endif + m_best_cover_actual = false; +} diff --git a/src/xrGameLA/ai/stalker/ai_stalker_debug.cpp b/src/xrGameLA/ai/stalker/ai_stalker_debug.cpp new file mode 100644 index 000000000..03a8aeda6 --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker_debug.cpp @@ -0,0 +1,1086 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_debug.cpp +// Created : 05.07.2005 +// Modified : 05.07.2005 +// Author : Dmitriy Iassenev +// Description : Debug functions for monster "Stalker" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" + +#ifdef LOG_PLANNER +#include "ai_stalker.h" +#include "../../hudmanager.h" +#include "../../memory_manager.h" +#include "../../visual_memory_manager.h" +#include "../../sound_memory_manager.h" +#include "../../hit_memory_manager.h" +#include "../../enemy_manager.h" +#include "../../danger_manager.h" +#include "../../item_manager.h" +#include "../../actor.h" +#include "../../stalker_planner.h" +#include "../../script_game_object.h" +#include "../../stalker_animation_manager.h" +#include "../../weapon.h" +#include "../../sound_player.h" +#include "../../inventory.h" +#include "../../object_handler_planner.h" +#include "../../stalker_movement_manager.h" +#include "../../movement_manager_space.h" +#include "../../patrol_path_manager.h" +#include "../../level_path_manager.h" +#include "../../game_path_manager.h" +#include "../../detail_path_manager.h" +#include "../../sight_manager.h" +#include "../../ai_object_location.h" +#include "../../entitycondition.h" +#include "../ai_monsters_misc.h" +#include "../../agent_manager.h" +#include "../../agent_member_manager.h" +#include "../../agent_enemy_manager.h" +#include "../../agent_corpse_manager.h" +#include "../../agent_location_manager.h" +#include "../../cover_point.h" +#include "../../../camerabase.h" +#include "../../clsid_game.h" +#include "../../mt_config.h" +#include "../../weaponmagazined.h" +#include "../../object_handler_space.h" +#include "../../debug_renderer.h" +#include "../../CharacterPhysicsSupport.h" +#include "../../ui_base.h" +#include "../../level.h" +#include "../../level_debug.h" + +CActor *g_debug_actor = 0; + +void try_change_current_entity() +{ + CActor *actor = smart_cast(Level().CurrentEntity()); + VERIFY (actor); + g_debug_actor = actor; + + CFrustum frustum; + frustum.CreateFromMatrix (Device.mFullTransform,FRUSTUM_P_LRTB|FRUSTUM_P_FAR); + + typedef xr_vector OBJECTS; + OBJECTS ISpatialResult; + g_SpatialSpace->q_frustum (ISpatialResult, 0, STYPE_COLLIDEABLE, frustum); + + float maxlen = 1000.0f; + CAI_Stalker* nearest_agent = 0; + + OBJECTS::const_iterator I = ISpatialResult.begin(); + OBJECTS::const_iterator E = ISpatialResult.end(); + for ( ; I != E; ++I) { + CAI_Stalker *current = smart_cast(*I); + if (!current) continue; + if (Level().CurrentEntity()==current) continue; + + Fvector A, B, tmp; + current->Center (A); + + tmp.sub (A, actor->cam_Active()->vPosition); + B.mad ( + actor->cam_Active()->vPosition, + actor->cam_Active()->vDirection, + tmp.dotproduct( + actor->cam_Active()->vDirection + ) + ); + float len = B.distance_to_sqr(A); + if (len > 1) continue; + + if (maxlen>len && !current->getDestroy()) + { + maxlen = len; + nearest_agent = current; + }; + } + + if (!nearest_agent) + return; + + Level().SetEntity (nearest_agent); + actor->inventory().Items_SetCurrentEntityHud(false); + + Engine.Sheduler.Unregister (actor); + Engine.Sheduler.Register (actor); + + Engine.Sheduler.Unregister (nearest_agent); + Engine.Sheduler.Register (nearest_agent, TRUE); +} + +void restore_actor() +{ + VERIFY (g_debug_actor); + VERIFY (Level().CurrentEntity()->CLS_ID != CLSID_OBJECT_ACTOR); + + Engine.Sheduler.Unregister (Level().CurrentEntity()); + Engine.Sheduler.Register (Level().CurrentEntity()); + + Level().SetEntity (g_debug_actor); + + Engine.Sheduler.Unregister (g_debug_actor); + Engine.Sheduler.Register (g_debug_actor, TRUE); + + g_debug_actor->inventory().Items_SetCurrentEntityHud(true); + + CHudItem* pHudItem = smart_cast(g_debug_actor->inventory().ActiveItem()); + if (pHudItem) + { + pHudItem->OnStateSwitch(pHudItem->GetState()); + } +} + +template +void draw_planner (const planner_type &brain, LPCSTR start_indent, LPCSTR indent, LPCSTR planner_id) +{ + planner_type &_brain = const_cast(brain); + if (brain.solution().empty()) + return; + + CScriptActionPlannerAction *planner = smart_cast(&_brain.action(brain.solution().front())); + if (planner) + draw_planner (*planner,start_indent,indent,_brain.action2string(brain.solution().front())); + + UI().Font().pFontStat->OutNext ("%s ",start_indent); + UI().Font().pFontStat->OutNext ("%splanner %s",start_indent,planner_id); + UI().Font().pFontStat->OutNext ("%s%sevaluators : %d",start_indent,indent,brain.evaluators().size()); + UI().Font().pFontStat->OutNext ("%s%soperators : %d",start_indent,indent,brain.operators().size()); + UI().Font().pFontStat->OutNext ("%s%sselected : %s",start_indent,indent,_brain.action2string(brain.solution().front())); + // solution + UI().Font().pFontStat->OutNext ("%s%ssolution",start_indent,indent); + for (int i=0; i<(int)brain.solution().size(); ++i) + UI().Font().pFontStat->OutNext("%s%s%s%s",start_indent,indent,indent,_brain.action2string(brain.solution()[i])); + // current + UI().Font().pFontStat->OutNext ("%s%scurrent world state",start_indent,indent); + planner_type::EVALUATORS::const_iterator I = brain.evaluators().begin(); + planner_type::EVALUATORS::const_iterator E = brain.evaluators().end(); + for ( ; I != E; ++I) { + xr_vector::const_iterator J = std::lower_bound(brain.current_state().conditions().begin(),brain.current_state().conditions().end(),planner_type::CWorldProperty((*I).first,false)); + char temp = '?'; + if ((J != brain.current_state().conditions().end()) && ((*J).condition() == (*I).first)) { + temp = (*J).value() ? '+' : '-'; + UI().Font().pFontStat->OutNext ("%s%s%s %5c : [%d][%s]",start_indent,indent,indent,temp,(*I).first,_brain.property2string((*I).first)); + } + } + // goal + UI().Font().pFontStat->OutNext ("%s%starget world state",start_indent,indent); + I = brain.evaluators().begin(); + for ( ; I != E; ++I) { + xr_vector::const_iterator J = std::lower_bound(brain.target_state().conditions().begin(),brain.target_state().conditions().end(),planner_type::CWorldProperty((*I).first,false)); + char temp = '?'; + if ((J != brain.target_state().conditions().end()) && ((*J).condition() == (*I).first)) { + temp = (*J).value() ? '+' : '-'; + UI().Font().pFontStat->OutNext ("%s%s%s %5c : [%d][%s]",start_indent,indent,indent,temp,(*I).first,_brain.property2string((*I).first)); + } + } +} + +LPCSTR animation_name(CAI_Stalker *self, const MotionID &animation) +{ + if (!animation) + return (""); + IKinematicsAnimated *skeleton_animated = smart_cast(self->Visual()); + VERIFY (skeleton_animated); + LPCSTR name = skeleton_animated->LL_MotionDefName_dbg(animation).first; + return (name); +} + +void draw_restrictions(const shared_str &restrictions, LPCSTR start_indent, LPCSTR indent, LPCSTR header) +{ + UI().Font().pFontStat->OutNext ("%s%s%s",start_indent,indent,header); + string256 temp; + for (u32 i=0, n=_GetItemCount(*restrictions); iOutNext("%s%s%s%s",start_indent,indent,indent,_GetItem(*restrictions,i,temp)); +} + +LPCSTR movement_type(const MonsterSpace::EMovementType &movement_type) +{ + switch (movement_type) { + case MonsterSpace::eMovementTypeStand : + return ("stand"); + case MonsterSpace::eMovementTypeWalk : + return ("walk"); + case MonsterSpace::eMovementTypeRun : + return ("run"); + default : NODEFAULT; + } + return ("invalid"); +} + +LPCSTR danger_type(const CDangerObject::EDangerType &danger_type) +{ + switch (danger_type) { + case CDangerObject::eDangerTypeBulletRicochet : return ("bullet ricochet"); + case CDangerObject::eDangerTypeAttackSound : return ("attack sound"); + case CDangerObject::eDangerTypeEntityAttacked : return ("entity attacked"); + case CDangerObject::eDangerTypeEntityDeath : return ("entity death"); + case CDangerObject::eDangerTypeFreshEntityCorpse : return ("fresh entity corpse"); + case CDangerObject::eDangerTypeAttacked : return ("I am attacked"); + case CDangerObject::eDangerTypeGrenade : return ("greande nearby"); + case CDangerObject::eDangerTypeEnemySound : return ("enemy sound"); + default : NODEFAULT; + }; + return (""); +} + +void CAI_Stalker::debug_planner (const script_planner *planner) +{ + m_debug_planner = planner; +} + +void CAI_Stalker::OnHUDDraw (CCustomHUD *hud) +{ + inherited::OnHUDDraw (hud); + + if (!psAI_Flags.test(aiStalker)) + return; + + CActor *actor = smart_cast(Level().Objects.net_Find(0)); + if (!actor) { + if (!g_debug_actor) + return; + + actor = g_debug_actor; + } + + float up_indent = 40.f; + LPCSTR indent = " "; + + UI().Font().pFontStat->SetColor (D3DCOLOR_XRGB(0,255,0)); + UI().Font().pFontStat->OutSet (0,up_indent); + // memory + UI().Font().pFontStat->OutNext ("memory"); + UI().Font().pFontStat->OutNext ("%sname : %s",indent,*cName()); + UI().Font().pFontStat->OutNext ("%sid : %d",indent,ID()); + UI().Font().pFontStat->OutNext ("%shealth : %f",indent,conditions().health()); + UI().Font().pFontStat->OutNext ("%swounded : %c",indent,wounded() ? '+' : '-'); + // visual + UI().Font().pFontStat->OutNext ("%svisual",indent); + + float object_range, object_fov; + update_range_fov (object_range,object_fov,eye_range,deg2rad(eye_fov)); + UI().Font().pFontStat->OutNext ("%s%seye range : %f",indent,indent,object_range); + UI().Font().pFontStat->OutNext ("%s%sFOV : %f",indent,indent,rad2deg(object_fov)); + if (g_Alive()) { + UI().Font().pFontStat->OutNext ("%s%sobjects : %d",indent,indent,memory().visual().objects().size()); + UI().Font().pFontStat->OutNext ("%s%snot yet : %d",indent,indent,memory().visual().not_yet_visible_objects().size()); + UI().Font().pFontStat->OutNext ("%s%sin frustum : %d",indent,indent,memory().visual().raw_objects().size()); + if (memory().visual().visible_now(actor)) + UI().Font().pFontStat->OutNext("%s%sactor : visible",indent,indent); + else { + MemorySpace::CNotYetVisibleObject *object = memory().visual().not_yet_visible_object(actor); + if (object && !fis_zero(object->m_value)) + UI().Font().pFontStat->OutNext("%s%sactor : not yet visible : %f",indent,indent,object->m_value); + else + UI().Font().pFontStat->OutNext("%s%sactor : not visible",indent,indent); + } + // sound + UI().Font().pFontStat->OutNext ("%ssound",indent); + UI().Font().pFontStat->OutNext ("%s%sobjects : %d",indent,indent,memory().sound().objects().size()); +#ifdef USE_SELECTED_SOUND + if (memory().sound().sound()) { + UI().Font().pFontStat->OutNext ("%s%sselected",indent,indent); + UI().Font().pFontStat->OutNext ("%s%s%stype",indent,indent,indent); + UI().Font().pFontStat->OutNext ("%s%s%spower : %f",indent,indent,indent,memory().sound().sound()->m_power); + UI().Font().pFontStat->OutNext ("%s%s%sobject : %s",indent,indent,indent,memory().sound().sound()->m_object ? *memory().sound().sound()->m_object->cName() : "unknown"); + if (g_Alive() && memory().sound().sound()->m_object) + UI().Font().pFontStat->OutNext("%s%s%svisible : %s",indent,indent,indent,memory().visual().visible_now(memory().sound().sound()->m_object) ? "+" : "-"); + } +#endif + // hit + UI().Font().pFontStat->OutNext ("%shit",indent); + UI().Font().pFontStat->OutNext ("%s%sobjects : %d",indent,indent,memory().hit().objects().size()); + ALife::_OBJECT_ID object_id = memory().hit().last_hit_object_id(); + UI().Font().pFontStat->OutNext ("%s%slast hit object id : %d",indent,indent,object_id); + CObject *object = (object_id == ALife::_OBJECT_ID(-1)) ? 0 : Level().Objects.net_Find(object_id); + UI().Font().pFontStat->OutNext ("%s%slast hit object name : %s",indent,indent,object ? *object->cName() : ""); +#ifdef USE_SELECTED_HIT + if (memory().hit().hit()) { + UI().Font().pFontStat->OutNext ("%s%sselected",indent,indent); + UI().Font().pFontStat->OutNext ("%s%s%spower : %f",indent,indent,indent,memory().hit().hit()->m_amount); + UI().Font().pFontStat->OutNext ("%s%s%sobject : %s",indent,indent,indent,memory().hit().hit()->m_object ? *memory().hit().hit()->m_object->cName() : "unknown"); + if (g_Alive() && memory().hit().hit()->m_object) + UI().Font().pFontStat->OutNext("%s%s%svisible : %s",indent,indent,indent,memory().visual().visible_now(memory().hit().hit()->m_object) ? "+" : "-"); + } +#endif + } + // enemy + UI().Font().pFontStat->OutNext ("%senemy",indent); + if (inventory().ActiveItem()) { + UI().Font().pFontStat->OutNext("%s%scan kill member : %s",indent,indent,can_kill_member() ? "+" : "-"); + UI().Font().pFontStat->OutNext("%s%scan kill enemy : %s",indent,indent,can_kill_enemy() ? "+" : "-"); + UI().Font().pFontStat->OutNext("%s%spick distance : %f",indent,indent,pick_distance()); + UI().Font().pFontStat->OutNext("%s%sfire make sense : %s",indent,indent,fire_make_sense() ? "+" : "-"); + UI().Font().pFontStat->OutNext("%s%sactor is enemy : %c",indent,indent,is_relation_enemy(actor) ? '+' : '-'); + UI().Font().pFontStat->OutNext("%s%sis enemy to actor : %c",indent,indent,actor->is_relation_enemy(this) ? '+' : '-'); + } + + UI().Font().pFontStat->OutNext ("%s%sobjects : %d",indent,indent,memory().enemy().objects().size()); + if (g_Alive()) { + CEnemyManager::OBJECTS::const_iterator I = memory().enemy().objects().begin(); + CEnemyManager::OBJECTS::const_iterator E = memory().enemy().objects().end(); + for ( ; I != E; ++I) + UI().Font().pFontStat->OutNext ("%s%s%s%s : %s",indent,indent,indent,*(*I)->cName(),memory().visual().visible_now(*I) ? "visible" : "invisible"); + } + + if (memory().enemy().selected()) { + UI().Font().pFontStat->OutNext ("%s%sselected",indent,indent); + + float fuzzy = 0.f; + xr_vector::iterator I=feel_visible.begin(),E=feel_visible.end(); + for (; I!=E; I++) + if (I->O->ID() == memory().enemy().selected()->ID()) { + fuzzy = I->fuzzy; + break; + } + + if (g_Alive()) { + if (!g_mt_config.test(mtAiVision)) + VERIFY (!memory().visual().visible_now(memory().enemy().selected()) || (fuzzy > 0.f)); + UI().Font().pFontStat->OutNext("%s%s%svisible : %s %f",indent,indent,indent,memory().visual().visible_now(memory().enemy().selected()) ? "+" : "-",fuzzy); + } + UI().Font().pFontStat->OutNext ("%s%s%sobject : %s",indent,indent,indent,*memory().enemy().selected()->cName()); + if (g_Alive()) { + float interval = (1.f - panic_threshold())*.25f, left = -1.f, right = -1.f; + LPCSTR description = "invalid"; + u32 result = dwfChooseAction( + 2000, + 1.f - interval, + 1.f - 2*interval, + 1.f - 3*interval, + panic_threshold(), + g_Team(), + g_Squad(), + g_Group(), + 0, + 1, + 2, + 3, + 4, + this, + 300.f + ); + switch (result) { + case 0 : { + description = "attack"; + left = 1.f; + right = 1.f - 1.f*interval; + break; + } + case 1 : { + description = "careful attack"; + left = 1.f - 1.f*interval; + right = 1.f - 2.f*interval; + break; + } + case 2 : { + description = "defend"; + left = 1.f - 2.f*interval; + right = 1.f - 3.f*interval; + break; + } + case 3 : { + description = "retreat"; + left = 1.f - 3*interval; + right = panic_threshold(); + break; + } + case 4 : { + description = "panic"; + left = panic_threshold(); + right = 0.f; + break; + } + default : NODEFAULT; + } + UI().Font().pFontStat->OutNext ("%s%s%svictory : [%5.2f%%,%5.2f%%] -> %s",indent,indent,indent,100.f*right,100.f*left,description); + } + } + // danger + UI().Font().pFontStat->OutNext ("%sdanger",indent); + UI().Font().pFontStat->OutNext ("%s%sobjects : %d",indent,indent,memory().danger().objects().size()); + if (memory().danger().selected() && memory().danger().selected()->object()) { + UI().Font().pFontStat->OutNext ("%s%sselected",indent,indent); + UI().Font().pFontStat->OutNext ("%s%s%stype : %s",indent,indent,indent,danger_type(memory().danger().selected()->type())); + UI().Font().pFontStat->OutNext ("%s%s%stime : %.3f (%.3f)",indent,indent,indent,float(memory().danger().selected()->time())/1000.f,float(Device.dwTimeGlobal - memory().danger().selected()->time())/1000.f); + UI().Font().pFontStat->OutNext ("%s%s%sinitiator : %s",indent,indent,indent,*memory().danger().selected()->object()->cName()); + if (g_Alive() && memory().danger().selected()->object()) + UI().Font().pFontStat->OutNext("%s%s%svisible : %s",indent,indent,indent,memory().visual().visible_now(memory().danger().selected()->object()) ? "+" : "-"); + + if (memory().danger().selected()->dependent_object() && !!memory().danger().selected()->dependent_object()->cName()) { + UI().Font().pFontStat->OutNext("%s%s%sdependent : %s",indent,indent,indent,*memory().danger().selected()->dependent_object()->cName()); + if (g_Alive()) + UI().Font().pFontStat->OutNext("%s%s%svisible : %s",indent,indent,indent,memory().visual().visible_now(smart_cast(memory().danger().selected()->dependent_object())) ? "+" : "-"); + } + } + + UI().Font().pFontStat->OutNext ("%sanomalies",indent); + UI().Font().pFontStat->OutNext ("%s%sundetected : %s",indent,indent,undetected_anomaly() ? "+" : "-"); + UI().Font().pFontStat->OutNext ("%s%sinside : %s",indent,indent,inside_anomaly() ? "+" : "-"); + + // agent manager + UI().Font().pFontStat->OutNext (" "); + UI().Font().pFontStat->OutNext ("agent manager"); + if (g_Alive()) { + UI().Font().pFontStat->OutNext ("%smembers : %d",indent,agent_manager().member().members().size()); + UI().Font().pFontStat->OutNext ("%senemies : %d",indent,agent_manager().enemy().enemies().size()); + UI().Font().pFontStat->OutNext ("%scorpses : %d",indent,agent_manager().corpse().corpses().size()); + UI().Font().pFontStat->OutNext ("%sdanger locations : %d",indent,agent_manager().location().locations().size()); + UI().Font().pFontStat->OutNext ("%smembers in combat : %d",indent,agent_manager().member().combat_members().size()); + if (g_Alive()) + UI().Font().pFontStat->OutNext("%sI am in combat : %s",indent,agent_manager().member().registered_in_combat(this) ? "+" : "-"); + UI().Font().pFontStat->OutNext ("%smembers in detour : %d",indent,agent_manager().member().in_detour()); + if (g_Alive()) + UI().Font().pFontStat->OutNext("%sI am in detour : %s",indent,agent_manager().member().member(this).detour() ? "+" : "-"); + + if (g_Alive()) { + if (agent_manager().member().member(this).cover()) + UI().Font().pFontStat->OutNext("%scover : [%f][%f][%f]",indent,VPUSH(agent_manager().member().member(this).cover()->position())); + + if (agent_manager().member().member(this).member_death_reaction().m_processing) + UI().Font().pFontStat->OutNext("%react on death : %s",indent,*agent_manager().member().member(this).member_death_reaction().m_member->cName()); + + if (agent_manager().member().member(this).grenade_reaction().m_processing) + UI().Font().pFontStat->OutNext("%react on grenade : %s",indent,agent_manager().member().member(this).grenade_reaction().m_game_object ? *agent_manager().member().member(this).grenade_reaction().m_game_object->cName() : "unknown"); + } + } + + // objects + UI().Font().pFontStat->OutNext (" "); + UI().Font().pFontStat->OutNext ("%sobjects",indent); + UI().Font().pFontStat->OutNext ("%s%sobjects : %d",indent,indent,inventory().m_all.size()); + UI().Font().pFontStat->OutNext ("%s%sactive item : %s",indent,indent,inventory().ActiveItem() ? *inventory().ActiveItem()->object().cName() : ""); + UI().Font().pFontStat->OutNext ("%s%sbest weapon : %s",indent,indent,best_weapon() ? *best_weapon()->object().cName() : ""); + UI().Font().pFontStat->OutNext ("%s%sitem to kill : %s",indent,indent,item_to_kill() ? *m_best_item_to_kill->object().cName() : ""); + UI().Font().pFontStat->OutNext ("%s%sitem can kill : %s",indent,indent,item_can_kill() ? "+" : "-"); + UI().Font().pFontStat->OutNext ("%s%smemory item to kill : %s",indent,indent,remember_item_to_kill() ? *m_best_found_item_to_kill->object().cName() : ""); + UI().Font().pFontStat->OutNext ("%s%smemory ammo : %s",indent,indent,remember_ammo() ? *m_best_found_ammo->object().cName() : ""); + UI().Font().pFontStat->OutNext ("%s%sinfinite ammo : %s",indent,indent,m_infinite_ammo ? "+" : "-"); + UI().Font().pFontStat->OutNext ("%s%sitem to spawn : %s",indent,indent,item_to_spawn().size() ? *item_to_spawn() : "no item to spawn"); + UI().Font().pFontStat->OutNext ("%s%sammo in box to spawn: %d",indent,indent,item_to_spawn().size() ? ammo_in_box_to_spawn() : 0); + + CWeaponMagazined *weapon = smart_cast(inventory().ActiveItem()); + if (weapon) { + CObjectHandlerPlanner &planner = CObjectHandler::planner(); + UI().Font().pFontStat->OutNext("%s%squeue size : %d",indent,indent,weapon->GetQueueSize()); + UI().Font().pFontStat->OutNext("%s%squeue interval : %d", + indent, + indent, + planner.action( + planner.uid( + weapon->ID(), + ObjectHandlerSpace::eWorldOperatorQueueWait1 + ) + ).inertia_time() + ); + } + + if (inventory().ActiveItem()) { + UI().Font().pFontStat->OutNext ("%s%sactive item",indent,indent); + UI().Font().pFontStat->OutNext ("%s%s%sobject : %s",indent,indent,indent,inventory().ActiveItem() ? *inventory().ActiveItem()->object().cName() : ""); + CWeapon *weapon = smart_cast(inventory().ActiveItem()); + if (weapon) { + UI().Font().pFontStat->OutNext("%s%s%sstrapped : %s",indent,indent,indent,weapon_strapped(weapon) ? "+" : "-"); + UI().Font().pFontStat->OutNext("%s%s%sunstrapped : %s",indent,indent,indent,weapon_unstrapped(weapon) ? "+" : "-"); + UI().Font().pFontStat->OutNext("%s%s%sammo : %d",indent,indent,indent,weapon->GetAmmoElapsed()); + UI().Font().pFontStat->OutNext("%s%s%smagazine : %d",indent,indent,indent,weapon->GetAmmoMagSize()); + UI().Font().pFontStat->OutNext("%s%s%stotal ammo : %d",indent,indent,indent,weapon->GetAmmoCurrent()); + } + } + + string256 temp; + + const CObjectHandlerPlanner &objects = planner(); + strconcat (sizeof(temp),temp,indent,indent); + draw_planner (objects,temp,indent,"root"); + + UI().Font().pFontStat->OutSet (330,up_indent); + + // brain + UI().Font().pFontStat->OutNext ("brain"); + + // actions + draw_planner (this->brain(),indent,indent,"root"); + +#ifdef LOG_PLANNER + // debug planner + if (m_debug_planner) + draw_planner (*m_debug_planner,indent,indent,"debug_planner"); +#endif + + UI().Font().pFontStat->OutSet (640,up_indent); + // brain + UI().Font().pFontStat->OutNext ("controls"); + // animations + UI().Font().pFontStat->OutNext ("%sanimations",indent); + + UI().Font().pFontStat->OutNext ("%s%shead : [%s][%f]",indent,indent, + animation_name(this,animation().head().animation()), + animation().head().blend() ? animation().head().blend()->timeCurrent : 0.f + ); + UI().Font().pFontStat->OutNext ("%s%storso : [%s][%f]",indent,indent, + animation_name(this,animation().torso().animation()), + animation().torso().blend() ? animation().torso().blend()->timeCurrent : 0.f + ); + UI().Font().pFontStat->OutNext ("%s%slegs : [%s][%f]",indent,indent, + animation_name(this,animation().legs().animation()), + animation().legs().blend() ? animation().legs().blend()->timeCurrent : 0.f + ); + UI().Font().pFontStat->OutNext ("%s%sglobal : [%s][%f]",indent,indent, + animation_name(this,animation().global().animation()), + animation().global().blend() ? animation().global().blend()->timeCurrent : 0.f + ); + UI().Font().pFontStat->OutNext ("%s%sscript : [%s][%f]",indent,indent, + animation_name(this,animation().script().animation()), + animation().script().blend() ? animation().script().blend()->timeCurrent : 0.f + ); + + // movement + UI().Font().pFontStat->OutNext (" "); + UI().Font().pFontStat->OutNext ("%smovement",indent); + UI().Font().pFontStat->OutNext ("%s%senabled : %s",indent,indent,movement().enabled() ? "+" : "-"); + + LPCSTR mental_state = "invalid"; + switch (movement().mental_state()) { + case MonsterSpace::eMentalStateFree : { + mental_state = "free"; + break; + } + case MonsterSpace::eMentalStateDanger : { + mental_state = "danger"; + break; + } + case MonsterSpace::eMentalStatePanic : { + mental_state = "panic"; + break; + } + default : NODEFAULT; + } + UI().Font().pFontStat->OutNext ("%s%smental state : %s",indent,indent,mental_state); + + LPCSTR body_state = "invalid"; + switch (movement().body_state()) { + case MonsterSpace::eBodyStateStand : { + body_state = "stand"; + break; + } + case MonsterSpace::eBodyStateCrouch : { + body_state = "crouch"; + break; + } + default : NODEFAULT; + } + UI().Font().pFontStat->OutNext ("%s%sbody state : %s",indent,indent,body_state); + UI().Font().pFontStat->OutNext ("%s%smovement type : %s (current)",indent,indent,movement_type(movement().movement_type())); + UI().Font().pFontStat->OutNext ("%s%smovement type : %s (target)",indent,indent, movement_type(movement().target_movement_type())); + + LPCSTR path_type = "invalid"; + switch (movement().path_type()) { + case MovementManager::ePathTypeGamePath : { + path_type = "game path"; + break; + } + case MovementManager::ePathTypeLevelPath : { + path_type = "level path"; + break; + } + case MovementManager::ePathTypePatrolPath : { + path_type = "patrol path"; + break; + } + case MovementManager::ePathTypeNoPath : { + path_type = "no path"; + break; + } + default : NODEFAULT; + } + UI().Font().pFontStat->OutNext ("%s%spath type : %s",indent,indent,path_type); + UI().Font().pFontStat->OutNext ("%s%sposition : [%f][%f][%f]",indent,indent,VPUSH(Position())); + UI().Font().pFontStat->OutNext ("%s%slevel vertex id : %d",indent,indent,ai_location().level_vertex_id()); + UI().Font().pFontStat->OutNext ("%s%sgame vertex id : %d",indent,indent,ai_location().game_vertex_id()); + UI().Font().pFontStat->OutNext ("%s%shead current : [%f][%f]",indent,indent,movement().head_orientation().current.yaw,movement().head_orientation().current.pitch); + UI().Font().pFontStat->OutNext ("%s%shead target : [%f][%f]",indent,indent,movement().head_orientation().target.yaw,movement().head_orientation().target.pitch); + UI().Font().pFontStat->OutNext ("%s%sbody current : [%f][%f]",indent,indent,movement().body_orientation().current.yaw,movement().body_orientation().current.pitch); + UI().Font().pFontStat->OutNext ("%s%sbody target : [%f][%f]",indent,indent,movement().body_orientation().target.yaw,movement().body_orientation().target.pitch); + + if (movement().path_type() == MovementManager::ePathTypePatrolPath) { + UI().Font().pFontStat->OutNext("%s%spatrol",indent,indent); + UI().Font().pFontStat->OutNext("%s%s%spath : %s",indent,indent,indent,*movement().patrol().path_name()); + UI().Font().pFontStat->OutNext("%s%s%scompleted : %s",indent,indent,indent,movement().patrol().completed() ? "+" : "-"); + UI().Font().pFontStat->OutNext("%s%s%scurrent point : %d",indent,indent,indent,movement().patrol().get_current_point_index()); + if ( + movement().patrol().get_path() + && + movement().patrol().get_path()->vertex(movement().patrol().get_current_point_index()) + ) + UI().Font().pFontStat->OutNext("%s%s%sextrapolate : %s",indent,indent,indent,movement().patrol().extrapolate_path() ? "+" : "-"); + else + UI().Font().pFontStat->OutNext("%s%s%sextrapolate : unknown",indent,indent,indent); + } + + if (movement().path_type() == MovementManager::ePathTypeGamePath) { + UI().Font().pFontStat->OutNext("%s%sgame",indent,indent); + UI().Font().pFontStat->OutNext("%s%s%scompleted : %s",indent,indent,indent,movement().game_path().completed() ? "+" : "-"); + UI().Font().pFontStat->OutNext("%s%s%spath size : %d",indent,indent,indent,movement().game_path().path().size()); + UI().Font().pFontStat->OutNext("%s%s%scurrent point : %d",indent,indent,indent,movement().game_path().intermediate_index()); + } + + UI().Font().pFontStat->OutNext ("%s%slevel",indent,indent); + UI().Font().pFontStat->OutNext ("%s%s%spath size : %d",indent,indent,indent,movement().level_path().path().size()); + UI().Font().pFontStat->OutNext ("%s%s%sstart vertex : %d",indent,indent,indent,movement().level_path().path().empty() ? -1 : movement().level_path().path().front()); + UI().Font().pFontStat->OutNext ("%s%s%sdest vertex : %d",indent,indent,indent,movement().level_path().path().empty() ? -1 : movement().level_path().path().back()); + + UI().Font().pFontStat->OutNext ("%s%sdetail",indent,indent); + UI().Font().pFontStat->OutNext ("%s%s%svelocities : %d",indent,indent,indent,movement().detail().velocities().size()); + UI().Font().pFontStat->OutNext ("%s%s%sextrapolate : %f",indent,indent,indent,movement().detail().extrapolate_length()); + UI().Font().pFontStat->OutNext ("%s%s%spath size : %d",indent,indent,indent,movement().detail().path().size()); + if (!movement().detail().path().empty()) { + UI().Font().pFontStat->OutNext ("%s%s%sstart point : [%f][%f][%f]",indent,indent,indent,VPUSH(movement().detail().path().front().position)); + UI().Font().pFontStat->OutNext ("%s%s%sdest point : [%f][%f][%f]",indent,indent,indent,VPUSH(movement().detail().path().back().position)); + UI().Font().pFontStat->OutNext ("%s%s%scurrent point",indent,indent,indent); + UI().Font().pFontStat->OutNext ("%s%s%s%sindex : %d",indent,indent,indent,indent,movement().detail().curr_travel_point_index()); + UI().Font().pFontStat->OutNext ("%s%s%s%sposition : [%f][%f][%f]",indent,indent,indent,indent,VPUSH(movement().detail().path()[movement().detail().curr_travel_point_index()].position)); + CDetailPathManager::STravelParams current_velocity = movement().detail().velocity(movement().detail().path()[movement().detail().curr_travel_point_index()].velocity); + UI().Font().pFontStat->OutNext ("%s%s%s%slinear : %f", indent,indent,indent,indent,current_velocity.linear_velocity); + UI().Font().pFontStat->OutNext ("%s%s%s%sangular : %f deg",indent,indent,indent,indent,rad2deg(current_velocity.angular_velocity)); + UI().Font().pFontStat->OutNext ("%s%s%s%sangular(R): %f deg",indent,indent,indent,indent,rad2deg(current_velocity.real_angular_velocity)); + UI().Font().pFontStat->OutNext ("%s%s%sspeed(calc) : %f",indent,indent,indent,movement().speed()); + UI().Font().pFontStat->OutNext ("%s%s%sspeed(physics): %f",indent,indent,indent,movement().speed(character_physics_support()->movement())); + } + + if (movement().detail().use_dest_orientation()) + UI().Font().pFontStat->OutNext("%s%s%sorientation : + [%f][%f][%f]",indent,indent,indent,VPUSH(movement().detail().dest_direction())); + else + UI().Font().pFontStat->OutNext("%s%s%sorientation : -",indent,indent,indent); + + if ( + movement().restrictions().out_restrictions().size() || + movement().restrictions().in_restrictions().size() || + movement().restrictions().base_out_restrictions().size() || + movement().restrictions().base_in_restrictions().size() + ) { + UI().Font().pFontStat->OutNext ("%s%srestrictions",indent,indent); + strconcat (sizeof(temp),temp,indent,indent,indent); + draw_restrictions (movement().restrictions().out_restrictions(),temp,indent,"out"); + draw_restrictions (movement().restrictions().in_restrictions(),temp,indent,"in"); + draw_restrictions (movement().restrictions().base_out_restrictions(),temp,indent,"base out"); + draw_restrictions (movement().restrictions().base_in_restrictions(),temp,indent,"base in"); + } + + // sounds + UI().Font().pFontStat->OutNext (" "); + UI().Font().pFontStat->OutNext ("%ssounds",indent); + UI().Font().pFontStat->OutNext ("%s%scollections : %d",indent,indent,sound().objects().size()); + + { + u32 object_count = 0; + CSoundPlayer::SOUND_COLLECTIONS::const_iterator I = sound().objects().begin(); + CSoundPlayer::SOUND_COLLECTIONS::const_iterator E = sound().objects().end(); + for ( ; I != E; ++I) + object_count += (*I).second.second->m_sounds.size(); + UI().Font().pFontStat->OutNext("%s%sobjects : %d",indent,indent,object_count); + } + { + xr_vector::const_iterator I = sound().playing_sounds().begin(); + xr_vector::const_iterator E = sound().playing_sounds().end(); + for ( ; I != E; ++I) + UI().Font().pFontStat->OutNext( + "%s%s%s[%s]%s", + indent, + indent, + indent, + (Device.dwTimeGlobal < (*I).m_start_time) + ? + "not yet started" + : + ( + (*I).m_sound->_feedback() + ? + "playing" + : + "already played" + ), + (*I).m_sound->_handle() ? (*I).m_sound->_handle()->file_name() : "no source" + ); + } + + // sight + UI().Font().pFontStat->OutNext (" "); + UI().Font().pFontStat->OutNext ("%ssight",indent); + + LPCSTR sight_type = "invalid"; + switch (sight().current_action().sight_type()) { + case SightManager::eSightTypeCurrentDirection : { + sight_type = "current direction"; + break; + } + case SightManager::eSightTypePathDirection : { + sight_type = "path direction"; + break; + } + case SightManager::eSightTypeDirection : { + sight_type = "direction"; + break; + } + case SightManager::eSightTypePosition : { + sight_type = "position"; + break; + } + case SightManager::eSightTypeObject : { + sight_type = "object"; + break; + } + case SightManager::eSightTypeCover : { + sight_type = "cover"; + break; + } + case SightManager::eSightTypeSearch : { + sight_type = "search"; + break; + } + case SightManager::eSightTypeLookOver : { + sight_type = "look over"; + break; + } + case SightManager::eSightTypeCoverLookOver : { + sight_type = "cover look over"; + break; + } + case SightManager::eSightTypeFireObject : { + sight_type = "fire object"; + break; + } + default : NODEFAULT; + } + + UI().Font().pFontStat->OutNext ("%s%stype : %s",indent,indent,sight_type); + UI().Font().pFontStat->OutNext ("%s%suse torso : %s",indent,indent,sight().current_action().use_torso_look() ? "+" : "-"); + + switch (sight().current_action().sight_type()) { + case SightManager::eSightTypeCurrentDirection : { + break; + } + case SightManager::eSightTypePathDirection : { + break; + } + case SightManager::eSightTypeDirection : { + UI().Font().pFontStat->OutNext ("%s%sdirection : [%f][%f][%f]",indent,indent,VPUSH(sight().current_action().vector3d())); + break; + } + case SightManager::eSightTypePosition : { + UI().Font().pFontStat->OutNext ("%s%sposition : [%f][%f][%f]",indent,indent,VPUSH(sight().current_action().vector3d())); + break; + } + case SightManager::eSightTypeObject : { + UI().Font().pFontStat->OutNext ("%s%sobject : %s",indent,indent,*sight().current_action().object().cName()); + UI().Font().pFontStat->OutNext ("%s%sposition : [%f][%f][%f]",indent,indent,VPUSH(sight().current_action().object().Position())); + break; + } + case SightManager::eSightTypeCover : { + sight_type = "cover"; + break; + } + case SightManager::eSightTypeSearch : { + sight_type = "search"; + break; + } + case SightManager::eSightTypeLookOver : { + sight_type = "look over"; + break; + } + case SightManager::eSightTypeCoverLookOver : { + sight_type = "cover look over"; + break; + } + case SightManager::eSightTypeFireObject : { + UI().Font().pFontStat->OutNext ("%s%sobject : %s",indent,indent,*sight().current_action().object().cName()); + UI().Font().pFontStat->OutNext ("%s%sposition : [%f][%f][%f]",indent,indent,VPUSH(sight().current_action().object().Position())); + UI().Font().pFontStat->OutNext ("%s%svisible point : %s",indent,indent,false ? "-" : "+"); + break; + } + default : NODEFAULT; + } +} + +void CAI_Stalker::OnRender () +{ + if (inventory().ActiveItem()) { + Fvector position, direction, temp; + g_fireParams (0,position,direction); + temp = direction; + temp.mul (1.f); + temp.add (position); + Level().debug_renderer().draw_line (Fidentity,position,temp,D3DCOLOR_XRGB(0*255,255,0*255)); + } + + if (IsMyCamera()) { + if (!g_Alive()) + return; + + if (!memory().enemy().selected() || !memory().visual().visible_now(memory().enemy().selected())) + return; + + xr_vector objects; + feel_vision_get (objects); + if (std::find(objects.begin(),objects.end(),memory().enemy().selected()) != objects.end()) { + Fvector position = feel_vision_get_vispoint(const_cast(memory().enemy().selected())); + Level().debug_renderer().draw_aabb (position,.05f,.05f,.05f,D3DCOLOR_XRGB(0*255,255,0*255)); + return; + } + + return; + } + + inherited::OnRender (); + + { + Fvector c0 = Position(),c1,t0 = Position(),t1; + c0.y += 2.f; + c1.setHP (-movement().m_body.current.yaw,-movement().m_body.current.pitch); + c1.add (c0); + Level().debug_renderer().draw_line (Fidentity,c0,c1,D3DCOLOR_XRGB(0,255,0)); + + t0.y += 2.f; + t1.setHP (-movement().m_body.target.yaw,-movement().m_body.target.pitch); + t1.add (t0); + Level().debug_renderer().draw_line (Fidentity,t0,t1,D3DCOLOR_XRGB(255,0,0)); + } + + if (memory().danger().selected() && ai().level_graph().valid_vertex_position(memory().danger().selected()->position())) { + Fvector position = memory().danger().selected()->position(); + u32 level_vertex_id = ai().level_graph().vertex_id(position); + float half_size = ai().level_graph().header().cell_size()*.5f; + position.y += 1.f; + Level().debug_renderer().draw_aabb (position,half_size - .01f,1.f,ai().level_graph().header().cell_size()*.5f-.01f,D3DCOLOR_XRGB(0*255,255,0*255)); + + if (ai().level_graph().valid_vertex_id(level_vertex_id)) { + LevelGraph::CVertex *v = ai().level_graph().vertex(level_vertex_id); + Fvector direction; + float best_value = -1.f; + + for (u32 i=0, j = 0; i<36; ++i) { + float value = ai().level_graph().cover_in_direction(float(10*i)/180.f*PI,v); + direction.setHP (float(10*i)/180.f*PI,0); + direction.normalize (); + direction.mul (value*half_size); + direction.add (position); + direction.y = position.y; + Level().debug_renderer().draw_line (Fidentity,position,direction,D3DCOLOR_XRGB(0,0,255)); + value = ai().level_graph().compute_square(float(10*i)/180.f*PI,PI/2.f,v); + if (value > best_value) { + best_value = value; + j = i; + } + } + + direction.set (position.x - half_size*float(v->cover(0))/15.f,position.y,position.z); + Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0)); + + direction.set (position.x,position.y,position.z + half_size*float(v->cover(1))/15.f); + Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0)); + + direction.set (position.x + half_size*float(v->cover(2))/15.f,position.y,position.z); + Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0)); + + direction.set (position.x,position.y,position.z - half_size*float(v->cover(3))/15.f); + Level().debug_renderer().draw_line(Fidentity,position,direction,D3DCOLOR_XRGB(255,0,0)); + + float value = ai().level_graph().cover_in_direction(float(10*j)/180.f*PI,v); + direction.setHP (float(10*j)/180.f*PI,0); + direction.normalize (); + direction.mul (value*half_size); + direction.add (position); + direction.y = position.y; + Level().debug_renderer().draw_line (Fidentity,position,direction,D3DCOLOR_XRGB(0,0,0)); + } + } +} + +void CAI_Stalker::dbg_draw_vision () +{ + if (!psAI_Flags.is(aiVision)) { + DBG().object_info(this, this).clear(); + return; + } + + VERIFY (!!psAI_Flags.is(aiVision)); + + if (!smart_cast(Level().CurrentEntity())) + return; + + string128 st; + + DBG().object_info(this, this).remove_item(u32(0)); + DBG().object_info(this, this).remove_item(u32(1)); + + DBG().object_info(this, this).add_item(*cName(), D3DCOLOR_XRGB(255, 0, 0), 0); + + xr_sprintf(st, "Team[%u]Squad[%u]Group[%u]", g_Team(), g_Squad(), g_Group()); + DBG().object_info(this, this).add_item(st, D3DCOLOR_XRGB(255, 0, 0), 1); + + Fvector shift; + shift.set (0.f,2.5f,0.f); + + Fmatrix res; + res.mul (Device.mFullTransform,XFORM()); + + Fvector4 v_res; + + res.transform (v_res,shift); + + if (v_res.z < 0 || v_res.w < 0) + return; + + if (v_res.x < -1.f || v_res.x > 1.f || v_res.y<-1.f || v_res.y>1.f) + return; + + float x = (1.f + v_res.x)/2.f * (Device.dwWidth); + float y = (1.f - v_res.y)/2.f * (Device.dwHeight); + + CNotYetVisibleObject *object = memory().visual().not_yet_visible_object(smart_cast(Level().CurrentEntity())); + string64 out_text; + xr_sprintf (out_text,"%.2f",object ? object->m_value : 0.f); + + UI().Font().pFontMedium->SetColor (D3DCOLOR_RGBA(255,0,0,95)); + UI().Font().pFontMedium->OutSet (x,y); + UI().Font().pFontMedium->OutNext (out_text); +} + +typedef xr_vector COLLIDE_POINTS; + +class ray_query_param { +public: + CCustomMonster *m_holder; + float m_power; + float m_power_threshold; + float m_pick_distance; + Fvector m_start_position; + Fvector m_direction; + COLLIDE_POINTS *m_points; + +public: + IC ray_query_param (CCustomMonster *holder, float power_threshold, float distance, const Fvector &start_position, const Fvector &direction, COLLIDE_POINTS &points) + { + m_holder = holder; + m_power = 1.f; + m_power_threshold = power_threshold; + m_pick_distance = distance; + m_start_position = start_position; + m_direction = direction; + m_points = &points; + } +}; + +BOOL _ray_query_callback (collide::rq_result& result, LPVOID params) +{ + ray_query_param *param = (ray_query_param*)params; + param->m_points->push_back ( + Fvector().mad( + param->m_start_position, + param->m_direction, + result.range + ) + ); + + float power = param->m_holder->feel_vision_mtl_transp(result.O,result.element); + param->m_power *= power; + if (param->m_power > param->m_power_threshold) + return (true); + + param->m_pick_distance = result.range; + return (false); +} + +void fill_points (CCustomMonster *self, const Fvector &position, const Fvector &direction, float distance, collide::rq_results& rq_storage, COLLIDE_POINTS &points, float &pick_distance) +{ + VERIFY (!fis_zero(direction.square_magnitude())); + + collide::ray_defs ray_defs(position,direction,distance,CDB::OPT_CULL,collide::rqtBoth); + VERIFY (!fis_zero(ray_defs.dir.square_magnitude())); + + ray_query_param params(self,self->memory().visual().transparency_threshold(),distance,position,direction,points); + + Level().ObjectSpace.RayQuery (rq_storage,ray_defs,_ray_query_callback,¶ms,NULL,self); + + pick_distance = params.m_pick_distance; +} + +void draw_visiblity_rays (CCustomMonster *self, const CObject *object, collide::rq_results& rq_storage) +{ + typedef Feel::Vision::feel_visible_Item feel_visible_Item; + typedef xr_vector VISIBLE_ITEMS; + + feel_visible_Item *item = 0; + { + VISIBLE_ITEMS::iterator I = self->feel_visible.begin(); + VISIBLE_ITEMS::iterator E = self->feel_visible.end(); + for ( ; I!=E; ++I) { + if ((*I).O == object) { + item = &*I; + break; + } + } + } + + if (!item) + return; + + Fvector start_position = self->eye_matrix.c; + Fvector dest_position = item->cp_LAST; + Fvector direction = Fvector().sub(dest_position,start_position); + float distance = direction.magnitude(); + direction.normalize (); + float pick_distance = flt_max; + rq_storage.r_clear (); + COLLIDE_POINTS points; + points.push_back (start_position); + fill_points ( + self, + start_position, + direction, + distance, + rq_storage, + points, + pick_distance + ); + +// VERIFY (fsimilar(pick_distance,distance)); + if (fsimilar(pick_distance,distance) && !dest_position.similar(points.back())) + points.push_back (dest_position); + + VERIFY (points.size() > 1); + + Fvector size = Fvector().set(.05f,.05f,.05f); + Level().debug_renderer().draw_aabb (points.front(),size.x,size.y,size.z,D3DCOLOR_XRGB(0,0,255)); + + { + COLLIDE_POINTS::const_iterator I = points.begin() + 1; + COLLIDE_POINTS::const_iterator E = points.end(); + for ( ; I != E; ++I) { + Level().debug_renderer().draw_line (Fidentity,*(I-1),*I,D3DCOLOR_XRGB(0,255,0)); + Level().debug_renderer().draw_aabb (*I,size.x,size.y,size.z,D3DCOLOR_XRGB(0,255,0)); + } + } + + Level().debug_renderer().draw_aabb (points.back(),size.x,size.y,size.z,D3DCOLOR_XRGB(255,0,0)); +} + +void CAI_Stalker::dbg_draw_visibility_rays () +{ + if (!g_Alive()) + return; + + const CEntityAlive *enemy = memory().enemy().selected(); + if (enemy) { + if (memory().visual().visible_now(enemy)) { + collide::rq_results rq_storage; + draw_visiblity_rays (this,enemy,rq_storage); + } + } +} + +#endif // DEBUG diff --git a/src/xrGameLA/ai/stalker/ai_stalker_events.cpp b/src/xrGameLA/ai/stalker/ai_stalker_events.cpp new file mode 100644 index 000000000..d7f82176d --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker_events.cpp @@ -0,0 +1,158 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_events.cpp +// Created : 26.02.2003 +// Modified : 26.02.2003 +// Author : Dmitriy Iassenev +// Description : Events handling for monster "Stalker" +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_stalker.h" +#include "../../pda.h" +#include "../../inventory.h" +#include "../../xrmessages.h" +#include "../../shootingobject.h" +#include "../../../../xrNetServer/net_utils.h" +#include "../../level.h" +#include "../../ai_monster_space.h" + +using namespace StalkerSpace; +using namespace MonsterSpace; + +#define SILENCE + +void CAI_Stalker::OnEvent (NET_Packet& P, u16 type) +{ + inherited::OnEvent (P,type); + CInventoryOwner::OnEvent (P,type); + + switch (type) + { + case GE_TRADE_BUY : + case GE_OWNERSHIP_TAKE : { + + u16 id; + P.r_u16 (id); + bool duringSpawn = !P.r_eof() && P.r_u8(); + CObject *O = Level().Objects.net_Find (id); + + R_ASSERT (O); + +#ifndef SILENCE + Msg("Trying to take - %s (%d)", *O->cName(),O->ID()); +#endif + CGameObject *_O = smart_cast(O); + if (inventory().CanTakeItem(smart_cast(_O))) { //GetScriptControl() + O->H_SetParent(this); + inventory().Take(_O,true, false, duringSpawn); + if (!inventory().ActiveItem() && GetScriptControl() && smart_cast(O)) + CObjectHandler::set_goal (eObjectActionIdle,_O); + + on_after_take (_O); +#ifndef SILENCE + Msg("TAKE - %s (%d)", *O->cName(),O->ID()); +#endif + } + else { +// DropItemSendMessage(O); + NET_Packet P; + u_EventGen (P,GE_OWNERSHIP_REJECT,ID()); + P.w_u16 (u16(O->ID())); + u_EventSend (P); + +#ifndef SILENCE + Msg("TAKE - can't take! - Dropping for valid server information %s (%d)", *O->cName(),O->ID()); +#endif + } + break; + } + case GE_TRADE_SELL : + case GE_OWNERSHIP_REJECT : { + u16 id; + P.r_u16 (id); + CObject *O = Level().Objects.net_Find(id); + +#pragma todo("Dima to Oles : how can this happen?") + if (!O) + break; + + bool just_before_destroy = !P.r_eof() && P.r_u8(); + O->SetTmpPreDestroy (just_before_destroy); + + if (inventory().DropItem(smart_cast(O)) && !O->getDestroy()) { + O->H_SetParent (0, just_before_destroy); + feel_touch_deny (O,2000); + } + + break; + } + } +} + +void CAI_Stalker::feel_touch_new (CObject* O) +{ +// Msg ("FEEL_TOUCH::NEW : %s",*O->cName()); + if (!g_Alive()) return; + if (Remote()) return; + if ((O->spatial.type | STYPE_VISIBLEFORAI) != O->spatial.type) return; + + // Now, test for game specific logical objects to minimize traffic + CInventoryItem *I = smart_cast (O); + + if (!wounded() && !critically_wounded() && I && I->useful_for_NPC() && can_take(I)) { +#ifndef SILENCE + Msg("Taking item %s (%d)!",*I->cName(),I->ID()); +#endif + NET_Packet P; + u_EventGen (P,GE_OWNERSHIP_TAKE,ID()); + P.w_u16 (u16(I->object().ID())); + u_EventSend (P); + } +} + +void CAI_Stalker::DropItemSendMessage(CObject *O) +{ + if (!O || !O->H_Parent() || (this != O->H_Parent())) + return; + +#ifndef SILENCE + Msg("Dropping item!"); +#endif + // We doesn't have similar weapon - pick up it + NET_Packet P; + u_EventGen (P,GE_OWNERSHIP_REJECT,ID()); + P.w_u16 (u16(O->ID())); + u_EventSend (P); +} + +///////////////////////// +//PDA functions +///////////////////////// +/* +void CAI_Stalker::ReceivePdaMessage(u16 who, EPdaMsg msg, shared_str info_id) +{ + CInventoryOwner::ReceivePdaMessage(who, msg, info_id); +}*/ + + +void CAI_Stalker::UpdateAvailableDialogs(CPhraseDialogManager* partner) +{ +/* m_AvailableDialogs.clear(); + m_CheckedDialogs.clear(); + + if(CInventoryOwner::m_known_info_registry->registry().objects_ptr()) + { + for(KNOWN_INFO_VECTOR::const_iterator it = CInventoryOwner::m_known_info_registry->registry().objects_ptr()->begin(); + CInventoryOwner::m_known_info_registry->registry().objects_ptr()->end() != it; ++it) + { + //подгрузить кусочек информации с которым мы работаем + CInfoPortion info_portion; + info_portion.Load((*it).id); + + for(u32 i = 0; i (O); + CInventoryItem* I = smart_cast (O); + if (!E && !I) return (FALSE); +// if (E && (E->g_Team() == g_Team())) return FALSE; + return(TRUE); +} + +void CAI_Stalker::renderable_Render () +{ + inherited::renderable_Render (); + + if (!already_dead()) + CInventoryOwner::renderable_Render (); + +#ifdef DEBUG + if (g_Alive()) { + if (psAI_Flags.test(aiAnimationStats)) + animation().add_animation_stats (); + } +#endif // DEBUG +} + +void CAI_Stalker::Exec_Look (float dt) +{ + if (animation_movement_controlled()) + return; + + sight().Exec_Look (dt); +} + +bool CAI_Stalker::bfCheckForNodeVisibility(u32 dwNodeID, bool bIfRayPick) +{ + return (memory().visual().visible(dwNodeID,movement().m_head.current.yaw,ffGetFov())); +} + +BOOL CAI_Stalker::feel_touch_contact (CObject *O) +{ + if (!inherited::feel_touch_contact(O)) + return (FALSE); + + CGameObject *game_object = smart_cast(O); + if (!game_object) + return (FALSE); + + return (game_object->feel_touch_on_contact(this)); +} + +BOOL CAI_Stalker::feel_touch_on_contact (CObject *O) +{ + if ((O->spatial.type | STYPE_VISIBLEFORAI) != O->spatial.type) + return (FALSE); + + return (inherited::feel_touch_on_contact(O)); +} diff --git a/src/xrGameLA/ai/stalker/ai_stalker_fire.cpp b/src/xrGameLA/ai/stalker/ai_stalker_fire.cpp new file mode 100644 index 000000000..808d98711 --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker_fire.cpp @@ -0,0 +1,1252 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_fire.cpp +// Created : 25.02.2003 +// Modified : 25.02.2003 +// Author : Dmitriy Iassenev +// Description : Fire and enemy parameters for monster "Stalker" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_stalker.h" +#include "ai_stalker_impl.h" +#include "../../script_entity_action.h" +#include "../../inventory.h" +#include "../../ef_storage.h" +#include "../../stalker_decision_space.h" +#include "../../script_game_object.h" +#include "../../customzone.h" +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../agent_manager.h" +#include "../../stalker_animation_manager.h" +#include "../../stalker_planner.h" +#include "../../ef_pattern.h" +#include "../../memory_manager.h" +#include "../../hit_memory_manager.h" +#include "../../enemy_manager.h" +#include "../../item_manager.h" +#include "../../stalker_movement_manager.h" +#include "../../entitycondition.h" +#include "../../sound_player.h" +#include "../../cover_point.h" +#include "../../agent_member_manager.h" +#include "../../agent_location_manager.h" +#include "../../danger_cover_location.h" +#include "../../object_handler_planner.h" +#include "../../object_handler_space.h" +#include "../../visual_memory_manager.h" +#include "../../weapon.h" +#include "ai_stalker_space.h" +#include "../../effectorshot.h" +#include "../../BoneProtections.h" +#include "../../RadioactiveZone.h" +#include "../../restricted_object.h" +#include "../../ai_object_location.h" +#include "../../missile.h" +#include "../../phworld.h" +#include "../../stalker_animation_names.h" +#include "../../agent_corpse_manager.h" +#include "../../CharacterPhysicsSupport.h" +#include "../../stalker_planner.h" +#include "../../stalker_decision_space.h" +#include "../../script_game_object.h" +#include "../../inventory.h" +#include "../../torch.h" + +#include "../../trajectories.h" + +using namespace StalkerSpace; + +static float const DANGER_DISTANCE = 3.f; +static u32 const DANGER_INTERVAL = 120000; + +static float const PRECISE_DISTANCE = 2.5f; +static float const FLOOR_DISTANCE = 2.f; +static float const NEAR_DISTANCE = 2.5f; +static u32 const FIRE_MAKE_SENSE_INTERVAL = 10000; + +//static float const min_throw_distance = 10.f; +static float const max_distance = 40.f; + +float CAI_Stalker::GetWeaponAccuracy () const +{ + float base = PI/180.f; + + //влияние ранга на меткость + base *= m_fRankDisperison; + + if (!movement().path_completed()) { + if (movement().movement_type() == eMovementTypeWalk) + if (movement().body_state() == eBodyStateStand) + return (base*m_disp_walk_stand); + else + return (base*m_disp_walk_crouch); + else + if (movement().movement_type() == eMovementTypeRun) + if (movement().body_state() == eBodyStateStand) + return (base*m_disp_run_stand); + else + return (base*m_disp_run_crouch); + } + + if (movement().body_state() == eBodyStateStand) + if (zoom_state()) + return (base*m_disp_stand_stand); + else + return (base*m_disp_stand_stand_zoom); + else + if (zoom_state()) + return (base*m_disp_stand_crouch); + else + return (base*m_disp_stand_crouch_zoom); +} + +void CAI_Stalker::g_fireParams(const CHudItem* pHudItem, Fvector& P, Fvector& D) +{ +//. VERIFY (inventory().ActiveItem()); + if (!inventory().ActiveItem()) { +#ifdef DEBUG + Msg ("! CAI_Stalker::g_fireParams() : VERIFY(inventory().ActiveItem())"); +#endif // DEBUG + P = Position(); + D = Fvector().set(0.f,0.f,1.f); + return; + } + + CWeapon *weapon = smart_cast(inventory().ActiveItem()); + if (!weapon) { + CMissile *missile = smart_cast(inventory().ActiveItem()); + if (missile) { + update_throw_params (); + P = m_throw_position; + D = Fvector().set(m_throw_velocity).normalize(); + VERIFY (!fis_zero(D.square_magnitude())); + return; + } + P = eye_matrix.c; + D = eye_matrix.k; + if (weapon_shot_effector().IsActive()) + D = weapon_shot_effector_direction(D); + VERIFY (!fis_zero(D.square_magnitude())); + return; + } + + if (!g_Alive() || !animation().script_animations().empty()) { + P = weapon->get_LastFP(); + D = weapon->get_LastFD(); + VERIFY (!fis_zero(D.square_magnitude())); + return; + } + + switch (movement().body_state()) { + case eBodyStateStand : { + if (movement().movement_type() == eMovementTypeStand) { + P = eye_matrix.c; + D = eye_matrix.k; + if (weapon_shot_effector().IsActive()) + D = weapon_shot_effector_direction(D); + VERIFY (!fis_zero(D.square_magnitude())); + } + else { + D.setHP (-movement().m_head.current.yaw,-movement().m_head.current.pitch); + if (weapon_shot_effector().IsActive()) + D = weapon_shot_effector_direction(D); + Center (P); + P.mad (D,.5f); + P.y += .50f; +// P = weapon->get_LastFP(); +// D = weapon->get_LastFD(); + VERIFY (!fis_zero(D.square_magnitude())); + } + return; + } + case eBodyStateCrouch : { + P = eye_matrix.c; + D = eye_matrix.k; + if (weapon_shot_effector().IsActive()) + D = weapon_shot_effector_direction(D); + VERIFY (!fis_zero(D.square_magnitude())); + return; + } + default : NODEFAULT; + } + +#ifdef DEBUG + P = weapon->get_LastFP(); + D = weapon->get_LastFD(); + VERIFY (!fis_zero(D.square_magnitude())); +#endif +} + +void CAI_Stalker::g_WeaponBones (int &L, int &R1, int &R2) +{ + int r_hand, r_finger2, l_finger1; + CObjectHandler::weapon_bones(r_hand, r_finger2, l_finger1); + R1 = r_hand; + R2 = r_finger2; + if (!animation().script_animations().empty() && animation().script_animations().front().hand_usage()) + L = R2; + else + L = l_finger1; +} + +void CAI_Stalker::Hit (SHit* pHDS) +{ + if (invulnerable()) + { + callback(GameObject::eHit)( + lua_game_object(), + pHDS->damage(), + pHDS->direction(), + smart_cast(pHDS->initiator())->lua_game_object(), + pHDS->bone() + ); + return; + } + + if (m_bIsGhost) return; + + //хит может меняться в зависимости от ранга (новички получают больше хита, чем ветераны) + SHit HDS = *pHDS; + HDS.add_wound = true; + float hit_power = HDS.power * m_fRankImmunity; + + if(m_boneHitProtection && HDS.hit_type == ALife::eHitTypeFireWound) + { + float BoneArmor = m_boneHitProtection->getBoneArmor(HDS.bone()); + float ap = HDS.ap; + if(!fis_zero(BoneArmor, EPS)) + { + if(ap > BoneArmor) + { + float d_hit_power = (ap - BoneArmor) / ap; + if(d_hit_power < m_boneHitProtection->m_fHitFracNpc) + d_hit_power = m_boneHitProtection->m_fHitFracNpc; + + hit_power *= d_hit_power; + VERIFY(hit_power>=0.0f); + } + else + { + hit_power *= m_boneHitProtection->m_fHitFracNpc; + HDS.add_wound = false; + } + } + + if ( wounded() ) //уже лежит => добивание + { + hit_power = 1000.f; + #ifdef DEBUG + Msg("Fire wound, hit_power = %.2f (%.2f), boneArmor = %.2f (bone %d), ap = %.2f", hit_power, HDS.power, BoneArmor, HDS.bone(), ap); + #endif + } + } + HDS.power = hit_power; + + if (g_Alive()) { + bool already_critically_wounded = critically_wounded(); + + if (!already_critically_wounded) { + const CCoverPoint *cover = agent_manager().member().member(this).cover(); + if ( !invulnerable() && cover && pHDS->initiator() && (pHDS->initiator()->ID() != ID()) && !fis_zero(pHDS->damage()) && brain().affect_cover()) + agent_manager().location().add (new CDangerCoverLocation(cover,Device.dwTimeGlobal,DANGER_INTERVAL,DANGER_DISTANCE)); + } + + const CEntityAlive *entity_alive = smart_cast(pHDS->initiator()); + if (entity_alive && !wounded()) { + if (is_relation_enemy(entity_alive)) + sound().play (eStalkerSoundInjuring); +// else +// sound().play (eStalkerSoundInjuringByFriend); + } + + int weapon_type = -1; + if (best_weapon()) + weapon_type = best_weapon()->object().ef_weapon_type(); + + if ( + !wounded() && + !already_critically_wounded) + { + bool became_critically_wounded = update_critical_wounded(HDS.boneID,HDS.power); + if ( + !became_critically_wounded && + animation().script_animations().empty() && + (pHDS->bone() != BI_NONE) + ) + { + Fvector D; + float yaw, pitch; + D.getHP (yaw,pitch); + + #pragma todo("Dima to Dima : forward-back bone impulse direction has been determined incorrectly!") + float power_factor = m_power_fx_factor*pHDS->damage()/100.f; + clamp (power_factor,0.f,1.f); + + //IKinematicsAnimated *tpKinematics = smart_cast(Visual()); + IKinematics *tpKinematics = smart_cast(Visual()); + #ifdef DEBUG + tpKinematics->LL_GetBoneInstance (pHDS->bone()); + if (pHDS->bone() >= tpKinematics->LL_BoneCount()) { + Msg ("tpKinematics has no bone_id %d",pHDS->bone()); + pHDS->_dump (); + } + #endif +// int fx_index = iFloor(tpKinematics->LL_GetBoneInstance(pHDS->bone()).get_param(1) + (angle_difference(movement().m_body.current.yaw,-yaw) <= PI_DIV_2 ? 0 : 1)); +// if (fx_index != -1) +// animation().play_fx (power_factor,fx_index); + } + else { + if (!already_critically_wounded && became_critically_wounded) { + if (HDS.who) { + CAI_Stalker *stalker = smart_cast(HDS.who); + if ( stalker && stalker->g_Alive() ) + stalker->on_critical_wound_initiator (this); + } + } + } + } + } else { + IKinematics *tpKinematics = smart_cast(Visual()); + tpKinematics->LL_GetBoneInstance (pHDS->bone()); + if (pHDS->bone() != BI_NONE && pHDS->bone() <= tpKinematics->LL_BoneCount()) { + if (pHDS->bone() == tpKinematics->LL_BoneID("bip01_head")) { + const xr_vector& all = CAttachmentOwner::attached_objects(); + xr_vector::const_iterator it = all.begin(); + xr_vector::const_iterator it_e = all.end(); + for(;it!=it_e;++it){ + CTorch* torch = smart_cast(*it); + if (torch){ + torch->Broke(); + torch->SetBatteryStatus(0); + break; + } + } + } + + } + } + + inherited::Hit (&HDS); +} + +void CAI_Stalker::HitSignal (float amount, Fvector& vLocalDir, CObject* who, s16 element) +{ + if (getDestroy()) + return; + + if (g_Alive()) + memory().hit().add (amount,vLocalDir,who,element); +} + +void CAI_Stalker::OnItemTake (CInventoryItem *inventory_item, bool duringSpawn) +{ + CObjectHandler::OnItemTake (inventory_item, duringSpawn); + m_item_actuality = false; + m_sell_info_actuality = false; +} + +void CAI_Stalker::OnItemDrop (CInventoryItem *inventory_item) +{ + CObjectHandler::OnItemDrop (inventory_item); + + m_item_actuality = false; + m_sell_info_actuality = false; + + if (!g_Alive()) + return; + + if (!critically_wounded()) + return; + +// VERIFY (inventory().ActiveItem()); + + if (inventory().ActiveItem() && (inventory().ActiveItem() != inventory_item)) + return; + + brain().CStalkerPlanner::m_storage.set_property(StalkerDecisionSpace::eWorldPropertyCriticallyWounded,false); +} + +void CAI_Stalker::update_best_item_info () +{ + ai().ef_storage().alife_evaluation(false); + + if ( + m_item_actuality && + m_best_item_to_kill && + m_best_item_to_kill->can_kill() + ) { + + if (!memory().enemy().selected()) + return; + + ai().ef_storage().non_alife().member() = this; + ai().ef_storage().non_alife().enemy() = memory().enemy().selected() ? memory().enemy().selected() : this; + ai().ef_storage().non_alife().member_item() = &m_best_item_to_kill->object(); + float value; + value = ai().ef_storage().m_pfWeaponEffectiveness->ffGetValue(); + if (fsimilar(value,m_best_item_value)) + return; + } + + // initialize parameters + m_item_actuality = true; + ai().ef_storage().non_alife().member() = this; + ai().ef_storage().non_alife().enemy() = memory().enemy().selected() ? memory().enemy().selected() : this; + m_best_item_to_kill = 0; + m_best_ammo = 0; + m_best_found_item_to_kill = 0; + m_best_found_ammo = 0; + m_best_item_value = 0.f; + + // try to find the best item which can kill + { + TIItemContainer::iterator I = inventory().m_all.begin(); + TIItemContainer::iterator E = inventory().m_all.end(); + for ( ; I != E; ++I) { + if ((*I)->can_kill()) { + ai().ef_storage().non_alife().member_item() = &(*I)->object(); + float value; + if (memory().enemy().selected()) + value = ai().ef_storage().m_pfWeaponEffectiveness->ffGetValue(); + else + value = (float)(*I)->object().ef_weapon_type(); + + if (!fsimilar(value,m_best_item_value) && (value < m_best_item_value)) + continue; + + if (!fsimilar(value,m_best_item_value) && (value > m_best_item_value)) { + m_best_item_value = value; + m_best_item_to_kill = *I; + continue; + } + + VERIFY (fsimilar(value,m_best_item_value)); + if (m_best_item_to_kill && ((*I)->object().ef_weapon_type() <= m_best_item_to_kill->object().ef_weapon_type())) + continue; + + m_best_item_value = value; + m_best_item_to_kill = *I; + } + } + } + + // check if we found + if (m_best_item_to_kill) { + m_best_ammo = m_best_item_to_kill; + return; + } + + // so we do not have such an item + // check if we remember we saw item which can kill + // or items which can make my item killing + { + xr_vector::const_iterator I = memory().item().objects().begin(); + xr_vector::const_iterator E = memory().item().objects().end(); + for ( ; I != E; ++I) { + const CInventoryItem *inventory_item = smart_cast(*I); + if (!inventory_item || !memory().item().useful(&inventory_item->object())) + continue; + CInventoryItem *item = inventory_item->can_kill(&inventory()); + if (item) { + ai().ef_storage().non_alife().member_item() = &inventory_item->object(); + float value = ai().ef_storage().m_pfWeaponEffectiveness->ffGetValue(); + if (value > m_best_item_value) { + m_best_item_value = value; + m_best_found_item_to_kill = inventory_item; + m_best_found_ammo = 0; + m_best_ammo = item; + } + } + else { + item = inventory_item->can_make_killing(&inventory()); + if (!item) + continue; + + ai().ef_storage().non_alife().member_item() = &item->object(); + float value = ai().ef_storage().m_pfWeaponEffectiveness->ffGetValue(); + if (value > m_best_item_value) { + m_best_item_value = value; + m_best_item_to_kill = item; + m_best_found_item_to_kill = 0; + m_best_found_ammo = inventory_item; + } + } + } + } + + // check if we found such an item + if (m_best_found_item_to_kill || m_best_found_ammo) + return; + + // check if we remember we saw item to kill + // and item which can make this item killing + xr_vector::const_iterator I = memory().item().objects().begin(); + xr_vector::const_iterator E = memory().item().objects().end(); + for ( ; I != E; ++I) { + const CInventoryItem *inventory_item = smart_cast(*I); + if (!inventory_item || !memory().item().useful(&inventory_item->object())) + continue; + const CInventoryItem *item = inventory_item->can_kill(memory().item().objects()); + if (item) { + ai().ef_storage().non_alife().member_item() = &inventory_item->object(); + float value = ai().ef_storage().m_pfWeaponEffectiveness->ffGetValue(); + if (value > m_best_item_value) { + m_best_item_value = value; + m_best_found_item_to_kill = inventory_item; + m_best_found_ammo = item; + } + } + } +} + +bool CAI_Stalker::item_to_kill () +{ + update_best_item_info (); + return (!!m_best_item_to_kill); +} + +bool CAI_Stalker::item_can_kill () +{ + update_best_item_info (); + return (!!m_best_ammo); +} + +bool CAI_Stalker::remember_item_to_kill () +{ + update_best_item_info (); + return (!!m_best_found_item_to_kill); +} + +bool CAI_Stalker::remember_ammo () +{ + update_best_item_info (); + return (!!m_best_found_ammo); +} + +bool CAI_Stalker::ready_to_kill () +{ + return ( + m_best_item_to_kill && + inventory().ActiveItem() && + (inventory().ActiveItem()->object().ID() == m_best_item_to_kill->object().ID()) && + m_best_item_to_kill->ready_to_kill() + ); +} + +bool CAI_Stalker::ready_to_detour () +{ + if (!ready_to_kill()) + return (false); + + CWeapon *weapon = smart_cast(m_best_item_to_kill); + if (!weapon) + return (false); + + return (weapon->GetAmmoElapsed() > weapon->GetAmmoMagSize()/2); +} + +class ray_query_param { +public: + CAI_Stalker *m_holder; + float m_power; + float m_power_threshold; + bool m_can_kill_enemy; + bool m_can_kill_member; + float m_pick_distance; + +public: + IC ray_query_param (const CAI_Stalker *holder, float power_threshold, float distance) + { + m_holder = const_cast(holder); + m_power_threshold = power_threshold; + m_power = 1.f; + m_can_kill_enemy = false; + m_can_kill_member = false; + m_pick_distance = distance; + } +}; + +IC BOOL ray_query_callback (collide::rq_result& result, LPVOID params) +{ + ray_query_param *param = (ray_query_param*)params; + float power = param->m_holder->feel_vision_mtl_transp(result.O,result.element); + param->m_power *= power; + +// if (power >= .05f) { +// param->m_pick_distance = result.range; +// return (true); +// } + + if (!result.O) { + if (param->m_power > param->m_power_threshold) + return (true); + + param->m_pick_distance = result.range; + return (false); + } + + CEntityAlive *entity_alive = smart_cast(result.O); + if (!entity_alive) { + if (param->m_power > param->m_power_threshold) + return (true); + + param->m_pick_distance = result.range; + return (false); + } + + if (param->m_holder->is_relation_enemy(entity_alive)) + param->m_can_kill_enemy = true; + else + param->m_can_kill_member = true; + + param->m_pick_distance = result.range; + return (false); +} + +void CAI_Stalker::can_kill_entity (const Fvector &position, const Fvector &direction, float distance, collide::rq_results& rq_storage) +{ + VERIFY (!fis_zero(direction.square_magnitude())); + + collide::ray_defs ray_defs(position,direction,distance,CDB::OPT_CULL,collide::rqtBoth); + VERIFY (!fis_zero(ray_defs.dir.square_magnitude())); + + ray_query_param params(this,memory().visual().transparency_threshold(),distance); + + Level().ObjectSpace.RayQuery (rq_storage,ray_defs,ray_query_callback,¶ms,NULL,this); + m_can_kill_enemy = m_can_kill_enemy || params.m_can_kill_enemy; + m_can_kill_member = m_can_kill_member || params.m_can_kill_member; + m_pick_distance = _max(m_pick_distance,params.m_pick_distance); +} + +void CAI_Stalker::can_kill_entity_from (const Fvector &position, Fvector direction, float distance) +{ + m_pick_distance = 0.f; + rq_storage.r_clear (); + can_kill_entity (position,direction,distance,rq_storage); + if (m_can_kill_member && m_can_kill_enemy) + return; + + float yaw, pitch, safety_fire_angle = 1.f*PI_DIV_8*.5f; + direction.getHP (yaw,pitch); + + direction.setHP (yaw - safety_fire_angle,pitch); + can_kill_entity (position,direction,distance,rq_storage); + if (m_can_kill_member && m_can_kill_enemy) + return; + + direction.setHP (yaw + safety_fire_angle,pitch); + can_kill_entity (position,direction,distance,rq_storage); + if (m_can_kill_member && m_can_kill_enemy) + return; + + direction.setHP (yaw,pitch - safety_fire_angle); + can_kill_entity (position,direction,distance,rq_storage); + if (m_can_kill_member && m_can_kill_enemy) + return; + + direction.setHP (yaw,pitch + safety_fire_angle); + can_kill_entity (position,direction,distance,rq_storage); +} + +IC float CAI_Stalker::start_pick_distance () const +{ + float result = 50.f; + if (!memory().enemy().selected()) + return (result); + + return ( + _max( + result, + memory().enemy().selected()->Position().distance_to(Position()) + 1.f + ) + ); +} + +float CAI_Stalker::pick_distance () +{ + if (!inventory().ActiveItem()) + return (start_pick_distance()); + + update_can_kill_info (); + return (m_pick_distance); +} + +void CAI_Stalker::update_can_kill_info () +{ + if (m_pick_frame_id == Device.dwFrame) + return; + + m_pick_frame_id = Device.dwFrame; + m_can_kill_member = false; + m_can_kill_enemy = false; + + Fvector position, direction; + VERIFY (inventory().ActiveItem()); + g_fireParams (0,position,direction); + can_kill_entity_from (position,direction,start_pick_distance()); +} + +bool CAI_Stalker::undetected_anomaly () +{ + return (inside_anomaly() || brain().CStalkerPlanner::m_storage.property(StalkerDecisionSpace::eWorldPropertyAnomaly)); +} + +bool CAI_Stalker::inside_anomaly () +{ + xr_vector::const_iterator I = feel_touch.begin(); + xr_vector::const_iterator E = feel_touch.end(); + for ( ; I != E; ++I) { + CCustomZone *zone = smart_cast(*I); + if (zone) { + if (smart_cast(zone)) + continue; + + return (true); + } + } + return (false); +} + +bool CAI_Stalker::zoom_state () const +{ + if (!inventory().ActiveItem()) + return (false); + + if ((movement().movement_type() != eMovementTypeStand) && (movement().body_state() != eBodyStateCrouch) && !movement().path_completed()) + return (false); + + switch (CObjectHandler::planner().current_action_state_id()) { + case ObjectHandlerSpace::eWorldOperatorAim1 : + case ObjectHandlerSpace::eWorldOperatorAim2 : + case ObjectHandlerSpace::eWorldOperatorAimingReady1 : + case ObjectHandlerSpace::eWorldOperatorAimingReady2 : + case ObjectHandlerSpace::eWorldOperatorQueueWait1 : + case ObjectHandlerSpace::eWorldOperatorQueueWait2 : + case ObjectHandlerSpace::eWorldOperatorFire1 : + // we need this 2 operators to prevent fov/range switching + case ObjectHandlerSpace::eWorldOperatorReload1 : + case ObjectHandlerSpace::eWorldOperatorReload2 : + case ObjectHandlerSpace::eWorldOperatorForceReload1 : + case ObjectHandlerSpace::eWorldOperatorForceReload2 : + return (true); + } + + return (false); +} + +void CAI_Stalker::update_range_fov (float &new_range, float &new_fov, float start_range, float start_fov) +{ + float range = start_range, fov = start_fov; + + if (zoom_state()) + inventory().ActiveItem()->modify_holder_params(range,fov); + + return (inherited::update_range_fov(new_range,new_fov,range,fov)); +} + +bool CAI_Stalker::fire_make_sense () +{ + // if we do not have an enemy + const CEntityAlive *enemy = memory().enemy().selected(); + if (!enemy) + return (false); + + if ((pick_distance() + PRECISE_DISTANCE) < Position().distance_to(enemy->Position())) + return (false); + + if (_abs(Position().y - enemy->Position().y) > FLOOR_DISTANCE) + return (false); + + if (pick_distance() < NEAR_DISTANCE) + return (false); + + if (memory().visual().visible_right_now(enemy)) + return (true); + + u32 last_time_seen = memory().visual().visible_object_time_last_seen(enemy); + if (last_time_seen == u32(-1)) + return (false); + + if (Device.dwTimeGlobal > last_time_seen + FIRE_MAKE_SENSE_INTERVAL) + return (false); + + // if we do not have a weapon + if (!best_weapon()) + return (false); + + // if we do not have automatic weapon + if (best_weapon()->object().ef_weapon_type() != 6) + return (false); + + return (true); +} + +// shot effector stuff +void CAI_Stalker::on_weapon_shot_start (CWeapon *weapon) +{ + weapon_shot_effector().SetRndSeed (m_weapon_shot_random_seed); + weapon_shot_effector().Shot(weapon->zoom_cam_recoil.camDispersion + weapon->zoom_cam_recoil.camDispersionInc*float(weapon->ShotsFired())); +} + +void CAI_Stalker::on_weapon_shot_stop (CWeapon *weapon) +{ +} + +void CAI_Stalker::on_weapon_hide (CWeapon *weapon) +{ +} + +void CAI_Stalker::notify_on_wounded_or_killed (CObject *object) +{ + CAI_Stalker *stalker = smart_cast(object); + if (!stalker) + return; + + if ( !stalker->g_Alive() ) + return; + + stalker->on_enemy_wounded_or_killed (this); + + typedef CAgentCorpseManager::MEMBER_CORPSES MEMBER_CORPSES; + + const MEMBER_CORPSES &corpses = agent_manager().corpse().corpses(); + if (std::find(corpses.begin(),corpses.end(),this) != corpses.end()) + return; + + agent_manager().corpse().register_corpse(this); +} + +void CAI_Stalker::notify_on_wounded_or_killed () +{ + ALife::_OBJECT_ID last_hit_object_id = memory().hit().last_hit_object_id(); + if (last_hit_object_id == ALife::_OBJECT_ID(-1)) + return; + + CObject *object = Level().Objects.net_Find(last_hit_object_id); + if (!object) + return; + + notify_on_wounded_or_killed (object); +} + +void CAI_Stalker::wounded (bool value) +{ + if (m_wounded == value) + return; + + if (!g_Alive()) + return; + + if (value) + notify_on_wounded_or_killed (); + + m_wounded = value; + + if(!m_wounded && g_Alive()) + character_physics_support()->CreateCharacter(); + + if (!m_wounded) + return; + + character_physics_support()->movement()->DestroyCharacter(); + + if (!agent_manager().member().registered_in_combat(this)) + return; + + agent_manager().member().unregister_in_combat (this); +} + +void CAI_Stalker::wounded (bool value, bool dest) +{ + if (m_wounded == value) + return; + + if (!g_Alive()) + return; + + if (value) + notify_on_wounded_or_killed (); + + m_wounded = value; + + if(!m_wounded && g_Alive()) + character_physics_support()->CreateCharacter(); + + if (!m_wounded) + return; + + if (dest) + character_physics_support()->movement()->DestroyCharacter(); + + if (!agent_manager().member().registered_in_combat(this)) + return; + + agent_manager().member().unregister_in_combat (this); +} + +bool CAI_Stalker::wounded (const CRestrictedObject *object) const +{ + if (!wounded()) + return (false); + + VERIFY (object); + if (!object->accessible(Position())) + return (false); + + if (!object->accessible(ai_location().level_vertex_id())) + return (false); + + return (true); +} + +bool CAI_Stalker::use_default_throw_force () +{ + return (false); +} + +bool CAI_Stalker::use_throw_randomness () +{ + return (false); +} + +float CAI_Stalker::missile_throw_force () +{ + update_throw_params (); + return (m_throw_velocity.magnitude()); +} + +void CAI_Stalker::compute_throw_miss ( u32 const vertex_id ) +{ + float const throw_miss_radius = ::Random.randF(2.f,5.f); + u32 const try_count = 6; + + CLevelGraph const& level_graph = ai().level_graph(); + for (u32 i = 0; i < try_count; ++i) { + Fvector const direction = Fvector().random_dir(); + Fvector const check_position = Fvector().mad( m_throw_target_position, direction, throw_miss_radius ); + u32 const check_vertex_id = level_graph.check_position_in_direction(vertex_id, m_throw_target_position, check_position); + if ( !level_graph.valid_vertex_id(check_vertex_id) ) + continue; + + m_throw_target_position = check_position; + return; + } +} + +static float rank_precision[] = { 2.0, 1.5, 1.0, 0.5 }; + +void CAI_Stalker::throw_target_impl (const Fvector &position, CObject *throw_ignore_object ) +{ + float distance_to_sqr = position.distance_to_sqr(m_throw_target_position); + float distance = position.distance_to(m_throw_target_position); + float precision = rank_precision[CharacterInfo().Rank().index()]; + float dist_factor = distance / std::min(distance, max_distance / 3); + float offset_x = dist_factor * precision * (::Random.randF(0.f, 1.f) - 0.5f) * 2; + float offset_z = dist_factor * precision * (::Random.randF(0.f, 1.f) - 0.5f) * 2; + m_throw_actual = m_throw_actual && (distance_to_sqr < _sqr(.1f)); + m_throw_target_position = position; + m_throw_target_position.x += offset_x; + m_throw_target_position.z += offset_z; + m_throw_ignore_object = throw_ignore_object; +} + +void CAI_Stalker::throw_target (const Fvector &position, CObject *throw_ignore_object ) +{ + throw_target_impl ( position, throw_ignore_object ); +} + +void CAI_Stalker::throw_target (const Fvector &position, u32 const vertex_id, CObject *throw_ignore_object ) +{ + throw_target_impl ( position, throw_ignore_object ); + compute_throw_miss ( vertex_id ); +} + +// delete this function!!!! +bool CAI_Stalker::throw_check_error ( + float low, + float high, + const Fvector &position, + const Fvector &velocity, + const Fvector &gravity + ) +{ + return true; +} + +void CAI_Stalker::check_throw_trajectory (const float &throw_time) +{ + m_throw_enabled = false; + + xr_vector * trajectory_picks = NULL; + xr_vector * collide_tris = NULL; +#ifdef DEBUG +// trajectory_picks = & m_throw_picks; +// collide_tris = & m_throw_collide_tris; +#endif // #ifdef DEBUG + + Fvector box_size = { 0.f, 0.f , 0.f }; + //Fvector box_size = { 0.1f, 0.1f , 0.1f }; + + if ( !trajectory_intersects_geometry(throw_time, + m_throw_position, + m_throw_target_position, + m_throw_velocity, + m_throw_collide_position, + this, + m_throw_ignore_object, + rq_storage, + trajectory_picks, + collide_tris, + box_size) ) + { + m_throw_collide_position = Fvector().set(flt_max,flt_max,flt_max); + m_throw_enabled = true; + } +} + +void CAI_Stalker::update_throw_params () +{ + if (m_throw_actual) { + if (m_computed_object_position.similar(Position())) { + if (m_computed_object_direction.similar(Direction())) { + VERIFY (_valid(m_throw_velocity)); + return; + } + } + } + + m_throw_actual = true; + m_computed_object_position = Position(); + m_computed_object_direction = Direction(); + +#if 0 + m_throw_position = eye_matrix.c; +#else + m_throw_position = Position(); + + CMissile* const pMissile = dynamic_cast(inventory().ActiveItem()); + if ( pMissile ) { + static const LPCSTR third_person_offset_id = "third_person_throw_point_offset"; + + if ( !pSettings->line_exist(pMissile->cNameSect(), third_person_offset_id ) ) { + m_throw_position.y += 2.f; + + if ( pMissile ) { + Fvector offset; + XFORM().transform_dir(offset, pMissile->throw_point_offset()); + m_throw_position.add(offset); + } + } + else + m_throw_position.add( pSettings->r_fvector3(pMissile->cNameSect(), third_person_offset_id) ); + } +#endif + +// static float const distances[] = { +// 30.f, 40.f, 50.f, 60.f +// }; +// VERIFY (g_SingleGameDifficulty < sizeof(distances)/sizeof(distances[0])); +// float const max_distance = 45.f; //distances[g_SingleGameDifficulty]; + + // computing velocity with minimum magnitude + m_throw_velocity.sub (m_throw_target_position,m_throw_position); + + if (m_throw_velocity.magnitude() > max_distance) { + m_throw_enabled = false; + +#ifdef DEBUG +// m_throw_picks.clear (); +#endif // DEBUG + return; + } + + float time = ThrowMinVelTime(m_throw_velocity, ph_world->Gravity()); + TransferenceToThrowVel (m_throw_velocity, time, ph_world->Gravity()); + + check_throw_trajectory (time); + + m_throw_velocity.mul(::Random.randF(.98f,1.02f)); +} + +void CAI_Stalker::on_throw_completed () +{ + agent_manager().member().on_throw_completed (); + m_last_throw_time = Device.dwTimeGlobal; +} + +bool CAI_Stalker::critically_wounded () +{ + if (critical_wound_type() == critical_wound_type_dummy) + return (false); + + if (!brain().CStalkerPlanner::m_storage.property(StalkerDecisionSpace::eWorldPropertyCriticallyWounded)) { + critical_wounded_state_stop (); + return (false); + } + + return (true); +} + +bool CAI_Stalker::critical_wound_external_conditions_suitable() +{ + if (movement().body_state() != eBodyStateStand) + return (false); + + if (animation().non_script_need_update()) + return (false); + + CWeapon *active_weapon = smart_cast(inventory().ActiveItem()); + if (!active_weapon) + return (false); + + switch (active_weapon->animation_slot()) { + case 1: // pistols + case 2: // automatic weapon + case 3: // rifles + break; + default: + return (false); + } + + if (!agent_manager().member().registered_in_combat(this)) + return (false); + +// Msg ("%6d executing critical hit",Device.dwTimeGlobal); + animation().global().make_inactual (); + return (true); +} + +void CAI_Stalker::remove_critical_hit () +{ + brain().CStalkerPlanner::m_storage.set_property ( + StalkerDecisionSpace::eWorldPropertyCriticallyWounded, + false + ); + + animation().global().remove_callback( + CStalkerAnimationPair::CALLBACK_ID( + this, + &CAI_Stalker::remove_critical_hit + ) + ); +} + +void CAI_Stalker::critical_wounded_state_start () +{ + brain().CStalkerPlanner::m_storage.set_property( + StalkerDecisionSpace::eWorldPropertyCriticallyWounded, + true + ); + + animation().global().add_callback ( + CStalkerAnimationPair::CALLBACK_ID( + this, + &CAI_Stalker::remove_critical_hit + ) + ); +} + +bool CAI_Stalker::can_cry_enemy_is_wounded () const +{ + if (!brain().initialized()) + return (false); + + if (brain().current_action_id() != StalkerDecisionSpace::eWorldOperatorCombatPlanner) + return (false); + + typedef CActionPlannerActionScript planner_type; + planner_type *planner = smart_cast(&brain().current_action()); + VERIFY (planner); + + switch (planner->current_action_id()) { + case StalkerDecisionSpace::eWorldOperatorGetReadyToKill: + case StalkerDecisionSpace::eWorldOperatorGetReadyToDetour: + case StalkerDecisionSpace::eWorldOperatorKillEnemy: + case StalkerDecisionSpace::eWorldOperatorTakeCover: + case StalkerDecisionSpace::eWorldOperatorLookOut: + case StalkerDecisionSpace::eWorldOperatorHoldPosition: + case StalkerDecisionSpace::eWorldOperatorDetourEnemy: + case StalkerDecisionSpace::eWorldOperatorSearchEnemy: + case StalkerDecisionSpace::eWorldOperatorKillEnemyIfNotVisible: + case StalkerDecisionSpace::eWorldOperatorKillEnemyIfCriticallyWounded: + case StalkerDecisionSpace::eWorldOperatorThrowGrenade: + return (true); + case StalkerDecisionSpace::eWorldOperatorGetItemToKill: + case StalkerDecisionSpace::eWorldOperatorRetreatFromEnemy: + case StalkerDecisionSpace::eWorldOperatorPostCombatWait: + case StalkerDecisionSpace::eWorldOperatorHideFromGrenade: + case StalkerDecisionSpace::eWorldOperatorSuddenAttack: + case StalkerDecisionSpace::eWorldOperatorKillEnemyIfPlayerOnThePath: + case StalkerDecisionSpace::eWorldOperatorKillWoundedEnemy: + case StalkerDecisionSpace::eWorldOperatorCriticallyWounded: + return (false); + default : NODEFAULT; + } +#ifdef DEBUG + return (false); +#endif // DEBUG +} + +void CAI_Stalker::on_critical_wound_initiator (const CAI_Stalker *critically_wounded) +{ + if ( !g_Alive() ) + return; + + if ( !can_cry_enemy_is_wounded() ) + return; + + sound().play (eStalkerSoundEnemyCriticallyWounded); +} + +void CAI_Stalker::on_enemy_wounded_or_killed (const CAI_Stalker *wounded_or_killed) +{ + if (!g_Alive()) + return; + + if (!can_cry_enemy_is_wounded()) + return; + + sound().play (eStalkerSoundEnemyKilledOrWounded); +} + +bool CAI_Stalker::can_kill_member () +{ + if (!animation().script_animations().empty()) + return (false); + + update_can_kill_info (); + return (m_can_kill_member); +} + +bool CAI_Stalker::can_kill_enemy () +{ + VERIFY (inventory().ActiveItem()); + update_can_kill_info (); + return (m_can_kill_enemy); +} + +bool CAI_Stalker::too_far_to_kill_enemy (const Fvector &position) +{ +#if 1 + return (false); +#else + VERIFY (memory().enemy().selected()); + VERIFY (best_weapon()); + + int weapon_type = best_weapon()->object().ef_weapon_type(); + float distance = position.distance_to(Position()); + switch (weapon_type) { + // knife + case 1 : return (distance > 2.f); + // pistols + case 5 : return (distance > 10.f); + // shotguns + case 9 : return (distance > 5.f); + // sniper rifles + case 11 : + case 12 : + return (distance > 70.f); + default: return (distance > 5.f); + } +#endif +} diff --git a/src/xrGameLA/ai/stalker/ai_stalker_impl.h b/src/xrGameLA/ai/stalker/ai_stalker_impl.h new file mode 100644 index 000000000..99c2a07a1 --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker_impl.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_impl.h +// Created : 25.02.2003 +// Modified : 25.02.2003 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Stalker" (inline functions implementation) +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "../../level.h" +#include "../../seniority_hierarchy_holder.h" +#include "../../team_hierarchy_holder.h" +#include "../../squad_hierarchy_holder.h" +#include "../../group_hierarchy_holder.h" +#include "../../effectorshot.h" + +IC CAgentManager &CAI_Stalker::agent_manager () const +{ +// gr1ph to all: this method is called on update, it d be better to set a callback in order to set the agent manager and access it directly +// return (Level().seniority_holder().team(g_Team()).squad(g_Squad()).group(g_Group()).agent_manager()); + VERIFY2(m_agent_manager, make_string("no agent manager for %s -> %d|%d|%d", *cName(), g_Team(), g_Squad(), g_Group())); + return *m_agent_manager; +} + +IC Fvector CAI_Stalker::weapon_shot_effector_direction (const Fvector ¤t) const +{ + VERIFY (weapon_shot_effector().IsActive()); + Fvector result; + weapon_shot_effector().GetDeltaAngle(result); + + float y,p; + current.getHP (y,p); + + result.setHP (-result.y + y, -result.x + p); + + return (result); +} diff --git a/src/xrGameLA/ai/stalker/ai_stalker_inline.h b/src/xrGameLA/ai/stalker/ai_stalker_inline.h new file mode 100644 index 000000000..9d2238a05 --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker_inline.h @@ -0,0 +1,173 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_inline.h +// Created : 25.02.2003 +// Modified : 25.02.2003 +// Author : Dmitriy Iassenev +// Description : AI Behaviour for monster "Stalker" (inline functions) +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC BOOL CAI_Stalker::UsedAI_Locations () +{ + return (TRUE); +} + +IC CStalkerAnimationManager &CAI_Stalker::animation () const +{ + VERIFY (m_animation_manager); + return (*m_animation_manager); +} + +IC CStalkerPlanner &CAI_Stalker::brain () const +{ + VERIFY (m_brain); + return (*m_brain); +} + +IC CSightManager &CAI_Stalker::sight () const +{ + VERIFY (m_sight_manager); + return (*m_sight_manager); +} + +IC LPCSTR CAI_Stalker::Name () const +{ + return (CInventoryOwner::Name()); +} + +IC CStalkerMovementManager &CAI_Stalker::movement () const +{ + VERIFY (m_movement_manager); + return (*m_movement_manager); +} + +IC bool CAI_Stalker::frame_check (u32 &frame) +{ + if (Device.dwFrame == frame) + return (false); + + frame = Device.dwFrame; + return (true); +} + +IC bool CAI_Stalker::group_behaviour () const +{ + return (m_group_behaviour); +} + +IC CWeaponShotEffector &CAI_Stalker::weapon_shot_effector () const +{ + VERIFY (m_weapon_shot_effector); + return (*m_weapon_shot_effector); +} + +IC u32 CAI_Stalker::min_queue_size_far () const +{ + return (m_min_queue_size_far); +} + +IC u32 CAI_Stalker::max_queue_size_far () const +{ + return (m_max_queue_size_far); +} + +IC u32 CAI_Stalker::min_queue_interval_far () const +{ + return (m_min_queue_interval_far); +} + +IC u32 CAI_Stalker::max_queue_interval_far () const +{ + return (m_max_queue_interval_far); +} + +IC u32 CAI_Stalker::min_queue_size_medium () const +{ + return (m_min_queue_size_medium); +} + +IC u32 CAI_Stalker::max_queue_size_medium () const +{ + return (m_max_queue_size_medium); +} + +IC u32 CAI_Stalker::min_queue_interval_medium () const +{ + return (m_min_queue_interval_medium); +} + +IC u32 CAI_Stalker::max_queue_interval_medium () const +{ + return (m_max_queue_interval_medium); +} + +IC u32 CAI_Stalker::min_queue_size_close () const +{ + return (m_min_queue_size_close); +} + +IC u32 CAI_Stalker::max_queue_size_close () const +{ + return (m_max_queue_size_close); +} + +IC u32 CAI_Stalker::min_queue_interval_close () const +{ + return (m_min_queue_interval_close); +} + +IC u32 CAI_Stalker::max_queue_interval_close () const +{ + return (m_max_queue_interval_close); +} + +IC bool CAI_Stalker::wounded () const +{ + return (m_wounded); +} + +IC const CAI_Stalker::CRITICAL_WOUND_WEIGHTS &CAI_Stalker::critical_wound_weights () const +{ + VERIFY (!m_critical_wound_weights.empty()); + return (m_critical_wound_weights); +} + +IC const bool &CAI_Stalker::throw_enabled () +{ + if (!m_throw_actual) + update_throw_params (); + + return (m_throw_enabled); +} + +IC const u32 &CAI_Stalker::last_throw_time () const +{ + return (m_last_throw_time); +} + +IC const bool &CAI_Stalker::can_throw_grenades () const +{ + return (m_can_throw_grenades); +} + +IC void CAI_Stalker::can_throw_grenades (const bool &value) +{ + m_can_throw_grenades = value; +} + +IC const u32 &CAI_Stalker::throw_time_interval () const +{ + return (m_throw_time_interval); +} + +IC void CAI_Stalker::throw_time_interval (const u32 &value) +{ + m_throw_time_interval = value; +} + +IC const Fvector &CAI_Stalker::throw_target () const +{ + return (m_throw_target_position); +} + diff --git a/src/xrGameLA/ai/stalker/ai_stalker_misc.cpp b/src/xrGameLA/ai/stalker/ai_stalker_misc.cpp new file mode 100644 index 000000000..539b31232 --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker_misc.cpp @@ -0,0 +1,189 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_misc.cpp +// Created : 27.02.2003 +// Modified : 27.02.2003 +// Author : Dmitriy Iassenev +// Description : Miscellaneous functions for monster "Stalker" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_stalker.h" +#include "ai_stalker_space.h" +#include "../../bolt.h" +#include "../../inventory.h" +#include "../../character_info.h" +#include "../../relation_registry.h" +#include "../../memory_manager.h" +#include "../../item_manager.h" +#include "../../stalker_movement_manager.h" +#include "../../explosive.h" +#include "../../agent_manager.h" +#include "../../agent_member_manager.h" +#include "../../agent_explosive_manager.h" +#include "../../agent_location_manager.h" +#include "../../danger_object_location.h" +#include "../../member_order.h" +#include "../../level.h" +#include "../../sound_player.h" +#include "../../enemy_manager.h" +#include "../../danger_manager.h" +#include "../../visual_memory_manager.h" +#include "../../agent_enemy_manager.h" + +const u32 TOLLS_INTERVAL = 2000; +const u32 GRENADE_INTERVAL = 0*1000; +const float FRIENDLY_GRENADE_ALARM_DIST = 5.f; +const u32 DANGER_INFINITE_INTERVAL = 60000000; +const float DANGER_EXPLOSIVE_DISTANCE = 10.f; + +bool CAI_Stalker::useful (const CItemManager *manager, const CGameObject *object) const +{ + const CExplosive *explosive = smart_cast(object); + + if (explosive && smart_cast(object)) + agent_manager().location().add (new CDangerObjectLocation(object,Device.dwTimeGlobal,DANGER_INFINITE_INTERVAL,DANGER_EXPLOSIVE_DISTANCE)); + + if (explosive && (explosive->CurrentParentID() != 0xffff)) { + agent_manager().explosive().register_explosive(explosive,object); + CEntityAlive *entity_alive = smart_cast(Level().Objects.net_Find(explosive->CurrentParentID())); + if (entity_alive) + memory().danger().add(CDangerObject(entity_alive,object->Position(),Device.dwTimeGlobal,CDangerObject::eDangerTypeGrenade,CDangerObject::eDangerPerceiveTypeVisual,object)); + } + + if (!memory().item().useful(object)) + return (false); + + const CInventoryItem *inventory_item = smart_cast(object); + if (!inventory_item || !inventory_item->useful_for_NPC()) + return (false); + + if (!const_cast(this)->can_take(inventory_item)) + return (false); + + const CBolt *bolt = smart_cast(object); + if (bolt) + return (false); + + CInventory *inventory_non_const = const_cast(&inventory()); + CInventoryItem *inventory_item_non_const = const_cast(inventory_item); + if (!inventory_non_const->CanTakeItem(inventory_item_non_const)) + return (false); + + return (true); +} + +float CAI_Stalker::evaluate (const CItemManager *manager, const CGameObject *object) const +{ + float distance = Position().distance_to_sqr(object->Position()); + distance = !fis_zero(distance) ? distance : EPS_L; + return (distance); +} + +bool CAI_Stalker::useful (const CEnemyManager *manager, const CEntityAlive *object) const +{ + if (!agent_manager().enemy().useful_enemy(object,this)) + return (false); + + return (memory().enemy().useful(object)); +} + +ALife::ERelationType CAI_Stalker::tfGetRelationType (const CEntityAlive *tpEntityAlive) const +{ + const CInventoryOwner* pOtherIO = smart_cast(tpEntityAlive); + + ALife::ERelationType relation = ALife::eRelationTypeDummy; + + if(pOtherIO && !(const_cast(tpEntityAlive)->cast_base_monster())) + relation = RELATION_REGISTRY().GetRelationType(static_cast(this), pOtherIO); + + if(ALife::eRelationTypeDummy != relation) + return relation; + else + return inherited::tfGetRelationType(tpEntityAlive); +} + +void CAI_Stalker::react_on_grenades () +{ + CMemberOrder::CGrenadeReaction &reaction = agent_manager().member().member(this).grenade_reaction(); + if (!reaction.m_processing) + return; + + if (Device.dwTimeGlobal < reaction.m_time + GRENADE_INTERVAL) + return; + +// u32 interval = AFTER_GRENADE_DESTROYED_INTERVAL; + const CMissile *missile = smart_cast(reaction.m_grenade); +// if (missile && (missile->destroy_time() > Device.dwTimeGlobal)) +// interval = missile->destroy_time() - Device.dwTimeGlobal + AFTER_GRENADE_DESTROYED_INTERVAL; +// m_object->agent_manager().add_danger_location(reaction.m_game_object->Position(),Device.dwTimeGlobal,interval,GRENADE_RADIUS); + + if (missile && agent_manager().member().group_behaviour()) { +// Msg ("%6d : Stalker %s : grenade reaction",Device.dwTimeGlobal,*m_object->cName()); + CEntityAlive *initiator = smart_cast(Level().Objects.net_Find(reaction.m_grenade->CurrentParentID())); + if (initiator) { + if (is_relation_enemy(initiator)) + sound().play (StalkerSpace::eStalkerSoundGrenadeAlarm); + else + if (missile->Position().distance_to(Position()) < FRIENDLY_GRENADE_ALARM_DIST) { + u32 const time = missile->destroy_time() >= Device.dwTimeGlobal ? u32(missile->destroy_time() - Device.dwTimeGlobal) : 0; + sound().play ( StalkerSpace::eStalkerSoundFriendlyGrenadeAlarm, time + 1500, time + 1000 ); + } + } + } + + reaction.clear (); +} + +void CAI_Stalker::react_on_member_death () +{ + CMemberOrder::CMemberDeathReaction &reaction = agent_manager().member().member(this).member_death_reaction(); + if (!reaction.m_processing) + return; + + if (Device.dwTimeGlobal < reaction.m_time + TOLLS_INTERVAL) + return; + + if (agent_manager().member().group_behaviour()) + sound().play (StalkerSpace::eStalkerSoundTolls); + + reaction.clear (); +} + +void CAI_Stalker::process_enemies () +{ + if (memory().enemy().selected()) + return; + + typedef MemorySpace::squad_mask_type squad_mask_type; + typedef CVisualMemoryManager::VISIBLES VISIBLES; + + bool found = false; + squad_mask_type mask = memory().visual().mask(); + VISIBLES::const_iterator I = memory().visual().objects().begin(); + VISIBLES::const_iterator E = memory().visual().objects().end(); + for ( ; I != E; ++I) { + if (!(*I).visible(mask)) + continue; + + const CAI_Stalker *member = smart_cast((*I).m_object); + if (!member) + continue; + + if (is_relation_enemy(member)) + continue; + + if (!member->g_Alive()) + continue; + + if (!member->memory().enemy().selected()) { + if (!memory().danger().selected() && member->memory().danger().selected()) + memory().danger().add(*member->memory().danger().selected()); + continue; + } + + memory().make_object_visible_somewhen (member->memory().enemy().selected()); + found = true; + break; + } +} + diff --git a/src/xrGameLA/ai/stalker/ai_stalker_script.cpp b/src/xrGameLA/ai/stalker/ai_stalker_script.cpp new file mode 100644 index 000000000..01b569d5c --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker_script.cpp @@ -0,0 +1,171 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_script.cpp +// Created : 29.09.2003 +// Modified : 29.09.2003 +// Author : Dmitriy Iassenev +// Description : Stalker script functions +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_stalker.h" +#include "../../stalker_decision_space.h" +#include "ai_stalker_space.h" +#include "../../script_game_object.h" +#include "../../stalker_planner.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CAI_Stalker::script_register(lua_State *L) +{ + module(L) + [ + class_("stalker_ids") + .enum_("properties") + [ + luabind::value("property_alive", StalkerDecisionSpace::eWorldPropertyAlive), + luabind::value("property_dead", StalkerDecisionSpace::eWorldPropertyDead), + luabind::value("property_already_dead", StalkerDecisionSpace::eWorldPropertyAlreadyDead), + luabind::value("property_alife", StalkerDecisionSpace::eWorldPropertyALife), + luabind::value("property_puzzle_solved", StalkerDecisionSpace::eWorldPropertyPuzzleSolved), + luabind::value("property_smart_terrain_task", StalkerDecisionSpace::eWorldPropertySmartTerrainTask), + luabind::value("property_items", StalkerDecisionSpace::eWorldPropertyItems), + luabind::value("property_enemy", StalkerDecisionSpace::eWorldPropertyEnemy), + luabind::value("property_danger", StalkerDecisionSpace::eWorldPropertyDanger), + luabind::value("property_item_to_kill", StalkerDecisionSpace::eWorldPropertyItemToKill), + luabind::value("property_found_item_to_kill", StalkerDecisionSpace::eWorldPropertyFoundItemToKill), + luabind::value("property_item_can_kill", StalkerDecisionSpace::eWorldPropertyItemCanKill), + luabind::value("property_found_ammo", StalkerDecisionSpace::eWorldPropertyFoundAmmo), + luabind::value("property_ready_to_kill", StalkerDecisionSpace::eWorldPropertyReadyToKill), + luabind::value("property_ready_to_detour", StalkerDecisionSpace::eWorldPropertyReadyToDetour), + luabind::value("property_see_enemy", StalkerDecisionSpace::eWorldPropertySeeEnemy), + luabind::value("property_panic", StalkerDecisionSpace::eWorldPropertyPanic), + luabind::value("property_in_cover", StalkerDecisionSpace::eWorldPropertyInCover), + luabind::value("property_looked_out", StalkerDecisionSpace::eWorldPropertyLookedOut), + luabind::value("property_position_holded", StalkerDecisionSpace::eWorldPropertyPositionHolded), + luabind::value("property_enemy_detoured", StalkerDecisionSpace::eWorldPropertyEnemyDetoured), + luabind::value("property_use_suddenness", StalkerDecisionSpace::eWorldPropertyUseSuddenness), + luabind::value("property_use_crouch_to_look_out", StalkerDecisionSpace::eWorldPropertyUseCrouchToLookOut), + luabind::value("property_critically_wounded", StalkerDecisionSpace::eWorldPropertyCriticallyWounded), + luabind::value("property_enemy_critically_wounded", StalkerDecisionSpace::eWorldPropertyEnemyCriticallyWounded), + + luabind::value("property_danger_unknown", StalkerDecisionSpace::eWorldPropertyDangerUnknown), + luabind::value("property_danger_in_direction", StalkerDecisionSpace::eWorldPropertyDangerInDirection), + luabind::value("property_danger_grenade", StalkerDecisionSpace::eWorldPropertyDangerGrenade), + luabind::value("property_danger_by_sound", StalkerDecisionSpace::eWorldPropertyDangerBySound), + + luabind::value("property_cover_actual", StalkerDecisionSpace::eWorldPropertyCoverActual), + luabind::value("property_cover_reached", StalkerDecisionSpace::eWorldPropertyCoverReached), + luabind::value("property_looked_around", StalkerDecisionSpace::eWorldPropertyLookedAround), + luabind::value("property_grenade_exploded", StalkerDecisionSpace::eWorldPropertyGrenadeExploded), + + luabind::value("property_anomaly", StalkerDecisionSpace::eWorldPropertyAnomaly), + luabind::value("property_inside_anomaly", StalkerDecisionSpace::eWorldPropertyInsideAnomaly), + luabind::value("property_pure_enemy", StalkerDecisionSpace::eWorldPropertyPureEnemy), + luabind::value("property_script", StalkerDecisionSpace::eWorldPropertyScript), + luabind::value("property_should_throw_grenade", StalkerDecisionSpace::eWorldPropertyShouldThrowGrenade) + ] + + .enum_("action") + [ + luabind::value("action_dead", StalkerDecisionSpace::eWorldOperatorDead), + luabind::value("action_dying", StalkerDecisionSpace::eWorldOperatorDying), + luabind::value("action_gather_items", StalkerDecisionSpace::eWorldOperatorGatherItems), + luabind::value("action_no_alife", StalkerDecisionSpace::eWorldOperatorALifeEmulation), + luabind::value("action_smart_terrain_task", StalkerDecisionSpace::eWorldOperatorSmartTerrainTask), + luabind::value("action_solve_zone_puzzle", StalkerDecisionSpace::eWorldOperatorSolveZonePuzzle), + luabind::value("action_reach_task_location", StalkerDecisionSpace::eWorldOperatorReachTaskLocation), + luabind::value("action_accomplish_task", StalkerDecisionSpace::eWorldOperatorAccomplishTask), + luabind::value("action_reach_customer_location", StalkerDecisionSpace::eWorldOperatorReachCustomerLocation), + luabind::value("action_communicate_with_customer", StalkerDecisionSpace::eWorldOperatorCommunicateWithCustomer), + luabind::value("get_out_of_anomaly", StalkerDecisionSpace::eWorldOperatorGetOutOfAnomaly), + luabind::value("detect_anomaly", StalkerDecisionSpace::eWorldOperatorDetectAnomaly), + luabind::value("action_get_item_to_kill", StalkerDecisionSpace::eWorldOperatorGetItemToKill), + luabind::value("action_find_item_to_kill", StalkerDecisionSpace::eWorldOperatorFindItemToKill), + luabind::value("action_make_item_killing", StalkerDecisionSpace::eWorldOperatorMakeItemKilling), + luabind::value("action_find_ammo", StalkerDecisionSpace::eWorldOperatorFindAmmo), + luabind::value("action_aim_enemy", StalkerDecisionSpace::eWorldOperatorAimEnemy), + luabind::value("action_get_ready_to_kill", StalkerDecisionSpace::eWorldOperatorGetReadyToKill), + luabind::value("action_kill_enemy", StalkerDecisionSpace::eWorldOperatorKillEnemy), + luabind::value("action_retreat_from_enemy", StalkerDecisionSpace::eWorldOperatorRetreatFromEnemy), + luabind::value("action_take_cover", StalkerDecisionSpace::eWorldOperatorTakeCover), + luabind::value("action_look_out", StalkerDecisionSpace::eWorldOperatorLookOut), + luabind::value("action_hold_position", StalkerDecisionSpace::eWorldOperatorHoldPosition), + luabind::value("action_get_distance", StalkerDecisionSpace::eWorldOperatorGetDistance), + luabind::value("action_detour_enemy", StalkerDecisionSpace::eWorldOperatorDetourEnemy), + luabind::value("action_search_enemy", StalkerDecisionSpace::eWorldOperatorSearchEnemy), + luabind::value("action_sudden_attack", StalkerDecisionSpace::eWorldOperatorSuddenAttack), + luabind::value("action_kill_enemy_if_not_visible", StalkerDecisionSpace::eWorldOperatorKillEnemyIfNotVisible), + luabind::value("action_reach_wounded_enemy", StalkerDecisionSpace::eWorldOperatorReachWoundedEnemy), + luabind::value("action_prepare_wounded_enemy", StalkerDecisionSpace::eWorldOperatorPrepareWoundedEnemy), + luabind::value("action_kill_wounded_enemy", StalkerDecisionSpace::eWorldOperatorKillWoundedEnemy), + luabind::value("action_kill_if_player_on_the_path", StalkerDecisionSpace::eWorldOperatorKillEnemyIfPlayerOnThePath), + luabind::value("action_critically_wounded", StalkerDecisionSpace::eWorldOperatorCriticallyWounded), + luabind::value("action_kill_if_enemy_critically_wounded", StalkerDecisionSpace::eWorldOperatorKillEnemyIfCriticallyWounded), + luabind::value("action_throw_grenade", StalkerDecisionSpace::eWorldOperatorThrowGrenade), + + luabind::value("action_danger_unknown_planner", StalkerDecisionSpace::eWorldOperatorDangerUnknownPlanner), + luabind::value("action_danger_in_direction_planner", StalkerDecisionSpace::eWorldOperatorDangerInDirectionPlanner), + luabind::value("action_danger_grenade_planner", StalkerDecisionSpace::eWorldOperatorDangerGrenadePlanner), + luabind::value("action_danger_by_sound_planner", StalkerDecisionSpace::eWorldOperatorDangerBySoundPlanner), + + luabind::value("action_danger_unknown_take_cover", StalkerDecisionSpace::eWorldOperatorDangerUnknownTakeCover), + luabind::value("action_danger_unknown_look_around", StalkerDecisionSpace::eWorldOperatorDangerUnknownLookAround), + luabind::value("action_danger_unknown_search", StalkerDecisionSpace::eWorldOperatorDangerUnknownSearchEnemy), + + luabind::value("action_danger_in_direction_take_cover", StalkerDecisionSpace::eWorldOperatorDangerInDirectionTakeCover), + luabind::value("action_danger_in_direction_look_out", StalkerDecisionSpace::eWorldOperatorDangerInDirectionLookOut), + luabind::value("action_danger_in_direction_hold_position", StalkerDecisionSpace::eWorldOperatorDangerInDirectionHoldPosition), + luabind::value("action_danger_in_direction_detour", StalkerDecisionSpace::eWorldOperatorDangerInDirectionDetourEnemy), + luabind::value("action_danger_in_direction_search", StalkerDecisionSpace::eWorldOperatorDangerInDirectionSearchEnemy), + + luabind::value("action_danger_grenade_take_cover", StalkerDecisionSpace::eWorldOperatorDangerGrenadeTakeCover), + luabind::value("action_danger_grenade_wait_for_explosion", StalkerDecisionSpace::eWorldOperatorDangerGrenadeWaitForExplosion), + luabind::value("action_danger_grenade_take_cover_after_explosion", StalkerDecisionSpace::eWorldOperatorDangerGrenadeTakeCoverAfterExplosion), + luabind::value("action_danger_grenade_look_around", StalkerDecisionSpace::eWorldOperatorDangerGrenadeLookAround), + luabind::value("action_danger_grenade_search", StalkerDecisionSpace::eWorldOperatorDangerGrenadeSearch), + + luabind::value("action_death_planner", StalkerDecisionSpace::eWorldOperatorDeathPlanner), + luabind::value("action_alife_planner", StalkerDecisionSpace::eWorldOperatorALifePlanner), + luabind::value("action_combat_planner", StalkerDecisionSpace::eWorldOperatorCombatPlanner), + luabind::value("action_anomaly_planner", StalkerDecisionSpace::eWorldOperatorAnomalyPlanner), + luabind::value("action_danger_planner", StalkerDecisionSpace::eWorldOperatorDangerPlanner), + luabind::value("action_post_combat_wait", StalkerDecisionSpace::eWorldOperatorPostCombatWait), + luabind::value("action_script", StalkerDecisionSpace::eWorldOperatorScript) + ] + + .enum_("sounds") + [ + luabind::value("sound_die", StalkerSpace::eStalkerSoundDie), + luabind::value("sound_die_in_anomaly", StalkerSpace::eStalkerSoundDieInAnomaly), + luabind::value("sound_injuring", StalkerSpace::eStalkerSoundInjuring), + luabind::value("sound_humming", StalkerSpace::eStalkerSoundHumming), + luabind::value("sound_alarm", StalkerSpace::eStalkerSoundAlarm), + luabind::value("sound_attack_no_allies", StalkerSpace::eStalkerSoundAttackNoAllies), + luabind::value("sound_attack_allies_single_enemy", StalkerSpace::eStalkerSoundAttackAlliesSingleEnemy), + luabind::value("sound_attack_allies_several_enemies", StalkerSpace::eStalkerSoundAttackAlliesSeveralEnemies), + luabind::value("sound_backup", StalkerSpace::eStalkerSoundBackup), + luabind::value("sound_detour", StalkerSpace::eStalkerSoundDetour), + luabind::value("sound_search1_no_allies", StalkerSpace::eStalkerSoundSearch1NoAllies), + luabind::value("sound_search1_with_allies", StalkerSpace::eStalkerSoundSearch1WithAllies), + luabind::value("sound_injuring_by_friend", StalkerSpace::eStalkerSoundInjuringByFriend), + luabind::value("sound_panic_human", StalkerSpace::eStalkerSoundPanicHuman), + luabind::value("sound_panic_monster", StalkerSpace::eStalkerSoundPanicMonster), + luabind::value("sound_tolls", StalkerSpace::eStalkerSoundTolls), + luabind::value("sound_grenade_alarm", StalkerSpace::eStalkerSoundGrenadeAlarm), + luabind::value("sound_friendly_grenade_alarm", StalkerSpace::eStalkerSoundFriendlyGrenadeAlarm), + luabind::value("sound_need_backup", StalkerSpace::eStalkerSoundNeedBackup), + + luabind::value("sound_running_in_danger", StalkerSpace::eStalkerSoundRunningInDanger), +// luabind::value("sound_walking_in_danger", StalkerSpace::eStalkerSoundWalkingInDanger), + luabind::value("sound_kill_wounded", StalkerSpace::eStalkerSoundKillWounded), + luabind::value("sound_enemy_critically_wounded", StalkerSpace::eStalkerSoundEnemyCriticallyWounded), + luabind::value("sound_enemy_killed_or_wounded", StalkerSpace::eStalkerSoundMaskEnemyKilledOrWounded), + + luabind::value("sound_script", StalkerSpace::eStalkerSoundScript) + ], + + class_("CAI_Stalker") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/stalker/ai_stalker_script_entity.cpp b/src/xrGameLA/ai/stalker/ai_stalker_script_entity.cpp new file mode 100644 index 000000000..68a565313 --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker_script_entity.cpp @@ -0,0 +1,307 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_script.cpp +// Created : 29.09.2003 +// Modified : 29.09.2003 +// Author : Dmitriy Iassenev +// Description : Stalker script functions +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai_stalker.h" +#include "../../stalker_animation_manager.h" +#include "../../script_entity_action.h" +#include "../../torch.h" +#include "../../inventory.h" +#include "../../weapon.h" +#include "../../weaponmagazined.h" +#include "../../../../Include/xrRender/Kinematics.h" +#include "../../script_engine.h" +#include "../../sight_manager.h" +#include "../../stalker_movement_manager.h" +#include "../../ai_space.h" + +CWeapon *CAI_Stalker::GetCurrentWeapon() const +{ + return (smart_cast(inventory().ActiveItem())); +} + +u32 CAI_Stalker::GetWeaponAmmo() const +{ + if (!GetCurrentWeapon()) + return (0); + return (GetCurrentWeapon()->GetAmmoCurrent(true)); +} + +//CInventoryItem *CAI_Stalker::GetCurrentEquipment() const +//{ +// return inventory().m_slots[OUTFIT_SLOT].m_pIItem; +//} + +CInventoryItem *CAI_Stalker::GetMedikit() const +{ +#pragma todo("Dima to Dima : Return correct medikit") + return (0); +} + +CInventoryItem *CAI_Stalker::GetFood() const +{ +#pragma todo("Dima to Dima : Return correct food") + return (0); +} + +void CAI_Stalker::ResetScriptData(void *P) +{ + inherited::ResetScriptData (P); +} + +bool CAI_Stalker::bfAssignMovement(CScriptEntityAction *tpEntityAction) +{ + if (!inherited::bfAssignMovement(tpEntityAction)) + return (false); + + CScriptMovementAction &l_tMovementAction = tpEntityAction->m_tMovementAction; + CScriptWatchAction &l_tWatchAction = tpEntityAction->m_tWatchAction; + CScriptAnimationAction &l_tAnimationAction = tpEntityAction->m_tAnimationAction; + CScriptObjectAction &l_tObjectAction = tpEntityAction->m_tObjectAction; + + CObjectHandler::set_goal (l_tObjectAction.m_tGoalType); + + movement().set_path_type (movement().path_type()); + movement().set_detail_path_type (l_tMovementAction.m_tPathType); + movement().set_body_state (l_tMovementAction.m_tBodyState); + movement().set_movement_type (l_tMovementAction.m_tMovementType); + movement().set_mental_state (l_tAnimationAction.m_tMentalState); + sight().setup (l_tWatchAction.m_tWatchType,&l_tWatchAction.m_tWatchVector); + movement().update (Device.dwTimeDelta); + sight().update (); + + return (true); +} + +bool CAI_Stalker::bfAssignWatch(CScriptEntityAction *tpEntityAction) +{ + if (!inherited::bfAssignWatch(tpEntityAction)) + return (false); + + CScriptWatchAction &l_tWatchAction = tpEntityAction->m_tWatchAction; + +// float &yaw = movement().m_head.target.yaw, &pitch = movement().m_head.target.pitch; + + switch (l_tWatchAction.m_tGoalType) { + case CScriptWatchAction::eGoalTypeObject : { + if (!xr_strlen(l_tWatchAction.m_bone_to_watch)) + l_tWatchAction.m_tpObjectToWatch->Center(l_tWatchAction.m_tWatchVector); + else { + CBoneInstance &l_tBoneInstance = smart_cast(l_tWatchAction.m_tpObjectToWatch->Visual())->LL_GetBoneInstance(smart_cast(l_tWatchAction.m_tpObjectToWatch->Visual())->LL_BoneID(l_tWatchAction.m_bone_to_watch)); + Fmatrix l_tMatrix; + + l_tMatrix = l_tBoneInstance.mTransform; + l_tMatrix.mulA_43 (l_tWatchAction.m_tpObjectToWatch->XFORM()); + + l_tWatchAction.m_tWatchVector = l_tMatrix.c; + } + sight().setup(l_tWatchAction.m_tWatchType,&l_tWatchAction.m_tWatchVector); + break; + } + case CScriptWatchAction::eGoalTypeDirection : { + sight().setup(l_tWatchAction.m_tWatchType,&l_tWatchAction.m_tWatchVector); + break; + } + case CScriptWatchAction::eGoalTypeWatchType : { + break; + } + case CScriptWatchAction::eGoalTypeCurrent : { + l_tWatchAction.m_tWatchType = SightManager::eSightTypeCurrentDirection; + l_tWatchAction.m_bCompleted = true; + return (false); + } + default : NODEFAULT; + } + + if ((CScriptWatchAction::eGoalTypeWatchType != l_tWatchAction.m_tGoalType) && (angle_difference(movement().m_head.target.yaw,movement().m_head.current.yaw) < EPS_L) && (angle_difference(movement().m_head.target.pitch,movement().m_head.current.pitch) < EPS_L)) + l_tWatchAction.m_bCompleted = true; + else + l_tWatchAction.m_bCompleted = false; + + return (!l_tWatchAction.m_bCompleted); +} + +bool CAI_Stalker::bfAssignObject(CScriptEntityAction *tpEntityAction) +{ + CScriptObjectAction &l_tObjectAction = tpEntityAction->m_tObjectAction; + CInventoryItem *l_tpInventoryItem = smart_cast(l_tObjectAction.m_tpObject); + + if (!inherited::bfAssignObject(tpEntityAction) || !l_tObjectAction.m_tpObject || !l_tpInventoryItem) { + if (!inventory().ActiveItem()) { + CObjectHandler::set_goal (eObjectActionIdle); + } + else { + CObjectHandler::set_goal (eObjectActionIdle,inventory().ActiveItem()); + } + + return ((l_tObjectAction.m_bCompleted = (CObjectHandler::goal_reached())) == false); + } + + if (!l_tpInventoryItem->object().H_Parent()) + return (true); + + CWeapon *l_tpWeapon = smart_cast(inventory().ActiveItem()); + CWeaponMagazined *l_tpWeaponMagazined = smart_cast(inventory().ActiveItem()); + + if (l_tpWeaponMagazined) + l_tpWeaponMagazined->SetQueueSize (l_tObjectAction.m_dwQueueSize); + + switch (l_tObjectAction.m_tGoalType) { + case eObjectActionIdle : { + if (!l_tpWeapon) + return ((l_tObjectAction.m_bCompleted = true) == false); + CObjectHandler::set_goal (eObjectActionIdle,l_tpInventoryItem); +// inventory().Action (kWPN_FIRE, CMD_STOP); + return ((l_tObjectAction.m_bCompleted = (CObjectHandler::goal_reached())) == false); + break; + } + case eObjectActionFire1 : { + CObjectHandler::set_goal (eObjectActionFire1,l_tpInventoryItem); +// if (!l_tpWeapon) +// return ((l_tObjectAction.m_bCompleted = true) == false); + if (inventory().ActiveItem() && l_tpWeapon) { + if (l_tpWeapon->GetAmmoElapsed()) { +// if (l_tpWeapon->GetAmmoMagSize() > 1) +// l_tpWeaponMagazined->SetQueueSize(l_tObjectAction.m_dwQueueSize); +// else +// l_tpWeaponMagazined->SetQueueSize(1); +// inventory().Action(kWPN_FIRE, CMD_START); + } + else { +// inventory().Action(kWPN_FIRE, CMD_STOP); + if (l_tpWeapon->GetAmmoCurrent()) { +// CObjectHandler::set_goal (eObjectActionFire1,l_tObjectAction.m_tpObject); +// inventory().Action(kWPN_RELOAD, CMD_START); + } + else + l_tObjectAction.m_bCompleted = true; + } + } + break; + } + case eObjectActionFire2 : { + CObjectHandler::set_goal (eObjectActionFire2,l_tpInventoryItem); +// if (!l_tpWeapon) +// return ((l_tObjectAction.m_bCompleted = true) == false); + if (inventory().ActiveItem()) { + if (l_tpWeapon->GetAmmoElapsed()) { +// if (l_tpWeapon->GetAmmoMagSize() > 1) +// l_tpWeaponMagazined->SetQueueSize(l_tObjectAction.m_dwQueueSize); +// else +// l_tpWeaponMagazined->SetQueueSize(1); +// inventory().Action(kWPN_FIRE, CMD_START); + } + else { +// inventory().Action(kWPN_FIRE, CMD_STOP); + if (l_tpWeapon->GetAmmoCurrent()) { +// CObjectHandler::set_goal (eObjectActionFire1,l_tObjectAction.m_tpObject); +// inventory().Action(kWPN_RELOAD, CMD_START); + } + else + l_tObjectAction.m_bCompleted = true; + } + } + break; + } + case eObjectActionReload2 : + case eObjectActionReload1 : { + if (!l_tpWeapon) + return ((l_tObjectAction.m_bCompleted = true) == false); + CObjectHandler::set_goal (eObjectActionReload1,l_tpInventoryItem); + if (inventory().ActiveItem()->object().ID() == l_tObjectAction.m_tpObject->ID()) { +// inventory().Action(kWPN_FIRE, CMD_STOP); + if (CWeapon::eReload != l_tpWeapon->GetState()) { +// inventory().Action(kWPN_RELOAD, CMD_START); + } + else + l_tObjectAction.m_bCompleted = true; + } + else + ai().script_engine().script_log(ScriptStorage::eLuaMessageTypeError,"cannot reload active item because it is not selected!"); + +// if (inventory().ActiveItem()) { +// inventory().Action(kWPN_FIRE, CMD_STOP); +// if (CWeapon::eReload != l_tpWeapon->STATE) +// inventory().Action(kWPN_RELOAD, CMD_START); +// else +// l_tObjectAction.m_bCompleted = true; +// } +// else +// ai().script_engine().script_log(ScriptStorage::eLuaMessageTypeError,"cannot reload active item because it is not selected!"); + break; + } + case eObjectActionActivate : { + CTorch *torch = smart_cast(l_tObjectAction.m_tpObject); + if (torch) { + torch->Switch(true); + break; + } + CObjectHandler::set_goal (eObjectActionIdle,l_tpInventoryItem); +// inventory().Slot(l_tpInventoryItem); +// inventory().Activate(l_tpInventoryItem->BaseSlot()); +// if (inventory().ActiveItem() && (inventory().ActiveItem()->ID() == l_tpInventoryItem->ID())) +// if (l_tpWeapon && (CWeapon::eIdle == l_tpWeapon->STATE)) +// l_tObjectAction.m_bCompleted = true; + return ((l_tObjectAction.m_bCompleted = (CObjectHandler::goal_reached())) == false); + + break; + } + case eObjectActionDeactivate : { + CTorch *torch = smart_cast(l_tObjectAction.m_tpObject); + if (torch) { + torch->Switch(false); + break; + } +// inventory().Activate(u32(-1)); + CObjectHandler::set_goal (eObjectActionIdle); + return ((l_tObjectAction.m_bCompleted = (CObjectHandler::goal_reached())) == false); + break; + } + case eObjectActionUse : { + CObjectHandler::set_goal (eObjectActionUse); + return ((l_tObjectAction.m_bCompleted = (CObjectHandler::goal_reached())) == false); + break; + } + case eObjectActionTake : { + if (inventory().GetItemFromInventory(*l_tObjectAction.m_tpObject->cName())) { + ai().script_engine().script_log(ScriptStorage::eLuaMessageTypeError,"item is already in the inventory!"); + return ((l_tObjectAction.m_bCompleted = true) == false); + } + feel_touch_new(l_tObjectAction.m_tpObject); + l_tObjectAction.m_bCompleted = true; + break; + } + case eObjectActionDrop : { + if (!inventory().GetItemFromInventory(*l_tObjectAction.m_tpObject->cName())) { + ai().script_engine().script_log(ScriptStorage::eLuaMessageTypeError,"item is not in the inventory!"); + return ((l_tObjectAction.m_bCompleted = true) == false); + } + DropItemSendMessage(l_tObjectAction.m_tpObject); + break; + } + default : NODEFAULT; + } + + return (true); +} + +bool CAI_Stalker::bfAssignAnimation(CScriptEntityAction *tpEntityAction) +{ + if (!inherited::bfAssignAnimation(tpEntityAction)) + return (false); + + if (xr_strlen(tpEntityAction->m_tAnimationAction.m_caAnimationToPlay)) { +#ifdef _DEBUG +// Msg ("%6d Assigning animation : %s",Device.dwTimeGlobal,*tpEntityAction->m_tAnimationAction.m_caAnimationToPlay); +#endif + animation().torso().reset(); + animation().legs().reset(); + } + + return (true); +} diff --git a/src/xrGameLA/ai/stalker/ai_stalker_space.h b/src/xrGameLA/ai/stalker/ai_stalker_space.h new file mode 100644 index 000000000..2fa327c3b --- /dev/null +++ b/src/xrGameLA/ai/stalker/ai_stalker_space.h @@ -0,0 +1,83 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_space.h +// Created : 28.03.2003 +// Modified : 28.03.2003 +// Author : Dmitriy Iassenev +// Description : Stalker types and structures +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#define MAX_HEAD_TURN_ANGLE (1.f*PI_DIV_4) + +namespace StalkerSpace { + enum EStalkerSounds { + eStalkerSoundDie = u32(0), + eStalkerSoundDieInAnomaly, + eStalkerSoundInjuring, + eStalkerSoundHumming, + eStalkerSoundAlarm, + eStalkerSoundAttackNoAllies, + eStalkerSoundAttackAlliesSingleEnemy, + eStalkerSoundAttackAlliesSeveralEnemies, + eStalkerSoundBackup, + eStalkerSoundDetour, + eStalkerSoundSearch1WithAllies, + eStalkerSoundSearch1NoAllies, + eStalkerSoundInjuringByFriend, + eStalkerSoundPanicHuman, + eStalkerSoundPanicMonster, + eStalkerSoundTolls, + eStalkerSoundGrenadeAlarm, + eStalkerSoundFriendlyGrenadeAlarm, + eStalkerSoundNeedBackup, + eStalkerSoundRunningInDanger, +// eStalkerSoundWalkingInDanger, + eStalkerSoundKillWounded, + eStalkerSoundEnemyCriticallyWounded, + eStalkerSoundEnemyKilledOrWounded, + eStalkerSoundThrowGrenade, + + eStalkerSoundScript, + eStalkerSoundDummy = u32(-1), + }; + + enum EStalkerSoundMasks { + eStalkerSoundMaskAnySound = u32(0), + eStalkerSoundMaskDie = u32(-1), + eStalkerSoundMaskDieInAnomaly = u32(-1), + eStalkerSoundMaskInjuring = u32(-1), + eStalkerSoundMaskInjuringByFriend = u32(-1), + eStalkerSoundMaskNonTriggered = u32(1 << 31) | (1 << 30), + eStalkerSoundMaskNoHumming = (1 << 29), + eStalkerSoundMaskFree = eStalkerSoundMaskNoHumming | eStalkerSoundMaskNonTriggered, + eStalkerSoundMaskHumming = 1 | eStalkerSoundMaskFree, + eStalkerSoundMaskNoDanger = (1 << 28), + eStalkerSoundMaskDanger = eStalkerSoundMaskNoDanger | eStalkerSoundMaskNonTriggered, + eStalkerSoundMaskAlarm = (1 << 0) | eStalkerSoundMaskDanger, + eStalkerSoundMaskAttackNoAllies = (1 << 1) | eStalkerSoundMaskDanger, + eStalkerSoundMaskAttackAlliesSingleEnemy = (1 << 2) | eStalkerSoundMaskDanger, + eStalkerSoundMaskAttackAlliesSeveralEnemies = (1 << 3) | eStalkerSoundMaskDanger, + eStalkerSoundMaskBackup = (1 << 4) | eStalkerSoundMaskDanger, + eStalkerSoundMaskDetour = (1 << 5) | eStalkerSoundMaskDanger, + eStalkerSoundMaskSearch1NoAllies = (1 << 6) | eStalkerSoundMaskDanger, + eStalkerSoundMaskSearch1WithAllies = (1 << 7) | eStalkerSoundMaskDanger, + eStalkerSoundMaskNeedBackup = (1 << 8) | eStalkerSoundMaskDanger, + eStalkerSoundMaskMovingInDanger = (1 << 9) | eStalkerSoundMaskDanger, + eStalkerSoundMaskKillWounded = (1 << 10) | eStalkerSoundMaskDanger, + eStalkerSoundMaskEnemyCriticallyWounded = (1 << 11) | eStalkerSoundMaskDanger, + eStalkerSoundMaskEnemyKilledOrWounded = (1 << 12) | eStalkerSoundMaskDanger, + eStalkerSoundMaskPanicHuman = eStalkerSoundMaskDanger, + eStalkerSoundMaskPanicMonster = eStalkerSoundMaskDanger, + eStalkerSoundMaskTolls = eStalkerSoundMaskDanger, + eStalkerSoundMaskGrenadeAlarm = eStalkerSoundMaskDanger, + eStalkerSoundMaskFriendlyGrenadeAlarm = eStalkerSoundMaskDanger, + eStalkerSoundMaskDummy = u32(-1), + }; + + enum EBodyAction { + eBodyActionNone = u32(0), + eBodyActionHello, + eBodyActionDummy = u32(-1), + }; +}; diff --git a/src/xrGameLA/ai/trader/ai_trader.cpp b/src/xrGameLA/ai/trader/ai_trader.cpp new file mode 100644 index 000000000..89ddd1518 --- /dev/null +++ b/src/xrGameLA/ai/trader/ai_trader.cpp @@ -0,0 +1,394 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_trader.cpp +// Created : 13.05.2002 +// Modified : 13.05.2002 +// Author : Jim +// Description : AI Behaviour for monster "Trader" +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "ai_trader.h" +#include "../../trade.h" +#include "../../script_entity_action.h" +#include "../../script_game_object.h" +#include "../../inventory.h" +#include "../../xrserver_objects_alife_monsters.h" +#include "../../artifact.h" +#include "../../xrserver.h" +#include "../../relation_registry.h" +#include "../../object_broker.h" +#include "../../sound_player.h" +#include "../../level.h" +#include "../../script_callback_ex.h" +#include "../../game_object_space.h" +#include "../../clsid_game.h" +#include "trader_animation.h" + +CAI_Trader::CAI_Trader() +{ + AnimMan = new CTraderAnimation(this); +} + +CAI_Trader::~CAI_Trader() +{ + xr_delete (m_sound_player); + xr_delete (AnimMan); +} + +void CAI_Trader::Load(LPCSTR section) +{ + // setEnabled (FALSE); + inherited::Load (section); + + //fHealth = pSettings->r_float (section,"Health"); + SetfHealth( pSettings->r_float (section,"Health") ); + + float max_weight = pSettings->r_float (section,"max_item_mass"); + inventory().SetMaxWeight(max_weight*1000); + // inventory().SetMaxRuck(1000000); + inventory().CalcTotalWeight(); +} + +void CAI_Trader::reinit () +{ + CScriptEntity::reinit (); + CEntityAlive::reinit (); + CInventoryOwner::reinit (); + sound().reinit (); + animation().reinit (); + + m_busy_now = false; +} + +void CAI_Trader::reload (LPCSTR section) +{ + CEntityAlive::reload (section); + CInventoryOwner::reload (section); + sound().reload (section); +} + + +bool CAI_Trader::bfAssignSound(CScriptEntityAction *tpEntityAction) +{ + if (!CScriptEntity::bfAssignSound(tpEntityAction)) { + //m_cur_head_anim_type = MonsterSpace::eHeadAnimNone; + return (false); + } + + //CScriptSoundAction &l_tAction = tpEntityAction->m_tSoundAction; + //m_cur_head_anim_type = l_tAction.m_tHeadAnimType; + + return (true); +} + +////////////////////////////////////////////////////////////////////////// +// Look At Actor +////////////////////////////////////////////////////////////////////////// +void CAI_Trader::BoneCallback(CBoneInstance *B) +{ + CAI_Trader* this_class = static_cast(B->callback_param()); + + this_class->LookAtActor(B); +} + +void CAI_Trader::LookAtActor(CBoneInstance *B) +{ + Fvector dir; + dir.sub(Level().CurrentEntity()->Position(),Position()); + + float yaw,pitch; + dir.getHP(yaw, pitch); + + float h,p,b; + XFORM().getHPB(h,p,b); + float cur_yaw = h; + float dy = _abs(angle_normalize_signed(yaw - cur_yaw)); + + if (angle_normalize_signed(yaw - cur_yaw) > 0) dy *= -1.f; + + Fmatrix M; + M.setHPB (0.f, -dy, 0.f); + B->mTransform.mulB_43(M); +} + +////////////////////////////////////////////////////////////////////////// + +BOOL CAI_Trader::net_Spawn (CSE_Abstract* DC) +{ + CSE_Abstract *e = (CSE_Abstract*)(DC); + CSE_ALifeTrader *l_tpTrader = smart_cast(e); + R_ASSERT (l_tpTrader); + + //проспавнить PDA у InventoryOwner + if (!CInventoryOwner::net_Spawn(DC)) + return (FALSE); + + if (!inherited::net_Spawn(DC) || !CScriptEntity::net_Spawn(DC)) + return (FALSE); + + setVisible (TRUE); + setEnabled (TRUE); + + set_money ( l_tpTrader->m_dwMoney, false ); + + // Установка callback на кости + CBoneInstance *bone_head = &smart_cast(Visual())->LL_GetBoneInstance(smart_cast(Visual())->LL_BoneID("bip01_head")); + bone_head->set_callback (bctCustom,BoneCallback,this); + + shedule.t_min = 100; + shedule.t_max = 2500; // This equaltiy is broken by Dima :-( // 30 * NET_Latency / 4; + + return (TRUE); +} + +void CAI_Trader::net_Export (NET_Packet& P) +{ + R_ASSERT (Local()); + + // P.w_float (inventory().TotalWeight()); + // P.w_u32 (m_dwMoney); +} + +void CAI_Trader::net_Import (NET_Packet& P) +{ + R_ASSERT (Remote()); + + float fDummy; + P.r_float (fDummy); + set_money ( P.r_u32(), false ); + + setVisible (TRUE); + setEnabled (TRUE); +} + +void CAI_Trader::OnEvent (NET_Packet& P, u16 type) +{ + inherited::OnEvent (P,type); + CInventoryOwner::OnEvent (P,type); + + u16 id; + CObject* Obj; + + switch (type) { + case GE_TRADE_BUY: + case GE_OWNERSHIP_TAKE: + { + P.r_u16 (id); + bool duringSpawn = !P.r_eof() && P.r_u8(); + Obj = Level().Objects.net_Find (id); + if(inventory().CanTakeItem(smart_cast(Obj))) { + Obj->H_SetParent(this); + inventory().Take(smart_cast(Obj), false, false, duringSpawn); + }else + { + NET_Packet P; + u_EventGen (P,GE_OWNERSHIP_REJECT,ID()); + P.w_u16 (u16(Obj->ID())); + u_EventSend (P); + } + }break; + case GE_TRADE_SELL: + case GE_OWNERSHIP_REJECT: + { + P.r_u16 (id); + Obj = Level().Objects.net_Find (id); + bool just_before_destroy = !P.r_eof() && P.r_u8(); + Obj->SetTmpPreDestroy (just_before_destroy); + if(inventory().DropItem(smart_cast(Obj))) + Obj->H_SetParent(0, just_before_destroy); + }break; + case GE_TRANSFER_AMMO: + break; + } +} + +void CAI_Trader::feel_touch_new (CObject* O) +{ + if (!g_Alive()) return; + if (Remote()) return; + + // Now, test for game specific logical objects to minimize traffic + CInventoryItem *I = smart_cast (O); + + if (I && I->useful_for_NPC()) { + Msg("Taking item %s!",*I->object().cName()); + NET_Packet P; + u_EventGen (P,GE_OWNERSHIP_TAKE,ID()); + P.w_u16 (u16(I->object().ID())); + u_EventSend (P); + } +} + +void CAI_Trader::DropItemSendMessage(CObject *O) +{ + if (!O || !O->H_Parent() || (this != O->H_Parent())) + return; + + Msg("Dropping item!"); + // We doesn't have similar weapon - pick up it + NET_Packet P; + u_EventGen (P,GE_OWNERSHIP_REJECT,ID()); + P.w_u16 (u16(O->ID())); + u_EventSend (P); +} + +void CAI_Trader::shedule_Update (u32 dt) +{ + inherited::shedule_Update (dt); + UpdateInventoryOwner(dt); + + if (GetScriptControl()) ProcessScripts(); + else Think(); +} + +void CAI_Trader::g_WeaponBones (int &L, int &R1, int &R2) +{ + IKinematics *V = smart_cast(Visual()); + R1 = V->LL_BoneID("bip01_r_hand"); + R2 = V->LL_BoneID("bip01_r_finger2"); + L = V->LL_BoneID("bip01_l_finger1"); +} + +void CAI_Trader::g_fireParams(const CHudItem* pHudItem, Fvector& P, Fvector& D) +{ + VERIFY (inventory().ActiveItem()); + if (g_Alive() && inventory().ActiveItem()) { + Center(P); + D.setHP(0,0); + D.normalize_safe(); + } +} + +void CAI_Trader::Think() +{ +} + +void CAI_Trader::Die (CObject* who) +{ + inherited::Die (who); +} + +void CAI_Trader::net_Destroy() +{ + inherited::net_Destroy (); + CScriptEntity::net_Destroy (); +} + +void CAI_Trader::UpdateCL() +{ + inherited::UpdateCL (); + sound().update (Device.fTimeDelta); + + + if (!GetScriptControl() && !bfScriptAnimation()) + animation().update_frame(); +} + +BOOL CAI_Trader::UsedAI_Locations() +{ + return (TRUE); +} + +void CAI_Trader::OnStartTrade() +{ + m_busy_now = true; + callback(GameObject::eTradeStart)(); +} + +void CAI_Trader::OnStopTrade() +{ + m_busy_now = false; + callback(GameObject::eTradeStop)(); +} + +//////////////////////////////////////////////////////////////////////////////////////////// + + +bool CAI_Trader::can_attach (const CInventoryItem *inventory_item) const +{ + return (false); +} + +bool CAI_Trader::use_bolts () const +{ + return (false); +} + +void CAI_Trader::spawn_supplies () +{ + inherited::spawn_supplies (); + CInventoryOwner::spawn_supplies (); +} + + +void CAI_Trader::save (NET_Packet &output_packet) +{ + inherited::save(output_packet); + CInventoryOwner::save(output_packet); +} +void CAI_Trader::load (IReader &input_packet) +{ + inherited::load(input_packet); + CInventoryOwner::load(input_packet); +} + + +//проверяет список артефактов в заказах +u32 CAI_Trader::ArtefactPrice (CArtefact* pArtefact) +{ + return pArtefact->Cost(); +} + +//продажа артефакта, с последуещим изменением списка заказов (true - если артефакт был в списке) +bool CAI_Trader::BuyArtefact (CArtefact* pArtefact) +{ + VERIFY(pArtefact); + return false; +} + +ALife::ERelationType CAI_Trader::tfGetRelationType (const CEntityAlive *tpEntityAlive) const +{ + const CInventoryOwner* pOtherIO = smart_cast(tpEntityAlive); + + ALife::ERelationType relation = ALife::eRelationTypeDummy; + + if(pOtherIO && !(const_cast(tpEntityAlive)->cast_base_monster())) + relation = RELATION_REGISTRY().GetRelationType(static_cast(this), pOtherIO); + + if(ALife::eRelationTypeDummy != relation) + return relation; + else + return inherited::tfGetRelationType(tpEntityAlive); +} + +DLL_Pure *CAI_Trader::_construct () +{ + m_sound_player = new CSoundPlayer(this); + + CEntityAlive::_construct (); + CInventoryOwner::_construct (); + CScriptEntity::_construct (); + + return (this); +} + +bool CAI_Trader::AllowItemToTrade (CInventoryItem const * item, EItemPlace place) const +{ + if (!g_Alive()) + return (true); + + if (item->object().CLS_ID == CLSID_DEVICE_PDA) + return (false); + + return (CInventoryOwner::AllowItemToTrade(item,place)); +} + +void CAI_Trader::dialog_sound_start(LPCSTR phrase) +{ + animation().external_sound_start(phrase); +} + +void CAI_Trader::dialog_sound_stop() +{ + animation().external_sound_stop(); +} diff --git a/src/xrGameLA/ai/trader/ai_trader.h b/src/xrGameLA/ai/trader/ai_trader.h new file mode 100644 index 000000000..94c7f3a66 --- /dev/null +++ b/src/xrGameLA/ai/trader/ai_trader.h @@ -0,0 +1,145 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_trader.h +// Created : 16.04.2003 +// Modified : 16.04.2003 +// Author : Jim +// Description : Trader class +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "../../CustomMonster.h" +#include "../../inventoryowner.h" +#include "../../script_entity.h" +#include "../../sound_player.h" +#include "../../AI_PhraseDialogManager.h" +#include "../../script_export_space.h" + +class CInventoryItem; +class CArtefact; +class CMotionDef; +class CBlend; +class CSoundPlayer; +class CTraderAnimation; + +class CAI_Trader : public CEntityAlive, + public CInventoryOwner, + public CScriptEntity, + public CAI_PhraseDialogManager +{ + + typedef CEntityAlive inherited; + +private: + bool m_busy_now; + +public: + CAI_Trader (); + virtual ~CAI_Trader (); + + virtual CAttachmentOwner* cast_attachment_owner () {return this;} + virtual CInventoryOwner* cast_inventory_owner () {return this;} + virtual CEntityAlive* cast_entity_alive () {return this;} + virtual CEntity* cast_entity () {return this;} + virtual CGameObject* cast_game_object () {return this;} + virtual CPhysicsShellHolder* cast_physics_shell_holder () {return this;} + virtual CParticlesPlayer* cast_particles_player () {return this;} + virtual CScriptEntity* cast_script_entity () {return this;} + + virtual DLL_Pure *_construct (); + virtual void Load ( LPCSTR section ); + virtual BOOL net_Spawn ( CSE_Abstract* DC ); + virtual void net_Export (NET_Packet& P); + virtual void net_Import (NET_Packet& P); + virtual void net_Destroy (); + + virtual void save (NET_Packet &output_packet); + virtual void load (IReader &input_packet); + virtual BOOL net_SaveRelevant() {return inherited::net_SaveRelevant();} + + virtual void Die (CObject* who); + virtual void Think (); + virtual void HitSignal (float /**P/**/, Fvector &/**local_dir/**/, CObject* /**who/**/, s16 /**element/**/){}; + virtual void HitImpulse (float /**P/**/, Fvector &/**vWorldDir/**/, Fvector& /**vLocalDir/**/){}; + virtual void Hit (SHit* pHDS){inherited::Hit(pHDS);} + virtual void UpdateCL (); + + virtual void g_fireParams (const CHudItem* pHudItem, Fvector& P, Fvector& D); + virtual void g_WeaponBones (int &L, int &R1, int &R2); + virtual float ffGetFov () const {return 150.f;} + virtual float ffGetRange () const {return 30.f;} + virtual void OnEvent (NET_Packet& P, u16 type); + virtual void feel_touch_new (CObject* O); + virtual void DropItemSendMessage (CObject *O); + virtual void shedule_Update (u32 dt); + + virtual BOOL UsedAI_Locations (); + + /////////////////////////////////////////////////////////////////////// + virtual u16 PHGetSyncItemsNumber () {return inherited ::PHGetSyncItemsNumber();} + virtual CPHSynchronize* PHGetSyncItem (u16 item) {return inherited ::PHGetSyncItem(item);} + virtual void PHUnFreeze () {return inherited ::PHUnFreeze();} + virtual void PHFreeze () {return inherited ::PHFreeze();} + /////////////////////////////////////////////////////////////////////// + + virtual void reinit (); + virtual void reload (LPCSTR section); + +static void __stdcall BoneCallback (CBoneInstance *B); + + void LookAtActor (CBoneInstance *B); + + void OnStartTrade (); + void OnStopTrade (); + + //игровое имя + virtual LPCSTR Name () const {return CInventoryOwner::Name();} + + virtual bool can_attach (const CInventoryItem *inventory_item) const; + virtual bool use_bolts () const; + virtual void spawn_supplies (); + + + virtual bool bfAssignSound (CScriptEntityAction *tpEntityAction); + + virtual ALife::ERelationType tfGetRelationType (const CEntityAlive *tpEntityAlive) const; + + ////////////////////////////////////////////////////////////////////////// + //генерируемые задания +public: + //проверяет список артефактов в заказах + virtual u32 ArtefactPrice (CArtefact* pArtefact); + //продажа артефакта, с последуещим изменением списка заказов (true - если артефакт был в списке) + virtual bool BuyArtefact (CArtefact* pArtefact); + +public: + IC bool busy_now () const + { + return (m_busy_now); + } + +private: + CSoundPlayer *m_sound_player; + +public: + IC CSoundPlayer &sound () const + { + VERIFY (m_sound_player); + return (*m_sound_player); + } + virtual bool natural_weapon () const {return false;} + virtual bool natural_detector () const {return false;} + virtual bool AllowItemToTrade (CInventoryItem const * item, EItemPlace place) const; + + void dialog_sound_start (LPCSTR phrase); + void dialog_sound_stop (); + +private: + CTraderAnimation *AnimMan; +public: + CTraderAnimation &animation () {return (*AnimMan);} + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CAI_Trader) +#undef script_type_list +#define script_type_list save_type_list(CAI_Trader) diff --git a/src/xrGameLA/ai/trader/ai_trader_script.cpp b/src/xrGameLA/ai/trader/ai_trader_script.cpp new file mode 100644 index 000000000..2092f684f --- /dev/null +++ b/src/xrGameLA/ai/trader/ai_trader_script.cpp @@ -0,0 +1,14 @@ +#include "pch_script.h" +#include "ai_trader.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CAI_Trader::script_register(lua_State *L) +{ + module(L) + [ + class_("CAI_Trader") + .def(constructor<>()) + ]; +} diff --git a/src/xrGameLA/ai/trader/trader_animation.cpp b/src/xrGameLA/ai/trader/trader_animation.cpp new file mode 100644 index 000000000..b14f841dc --- /dev/null +++ b/src/xrGameLA/ai/trader/trader_animation.cpp @@ -0,0 +1,131 @@ +#include "pch_script.h" +#include "trader_animation.h" +#include "ai_trader.h" +#include "../../script_callback_ex.h" +#include "../../game_object_space.h" + + +///////////////////////////////////////////////////////////////////////////////////////// +// Startup +///////////////////////////////////////////////////////////////////////////////////////// + +void CTraderAnimation::reinit() { + m_motion_head.invalidate (); + m_motion_global.invalidate (); + m_sound = 0; + m_external_sound = 0; + + m_anim_global = 0; + m_anim_head = 0; +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// Animation Callbacks +///////////////////////////////////////////////////////////////////////////////////////// +void CTraderAnimation::global_callback(CBlend* B) +{ + CTraderAnimation *trader = (CTraderAnimation*)B->CallbackParam; + trader->m_motion_global.invalidate (); +} + +void CTraderAnimation::head_callback(CBlend* B) +{ + CTraderAnimation *trader = (CTraderAnimation*)B->CallbackParam; + trader->m_motion_head.invalidate (); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Animation management +///////////////////////////////////////////////////////////////////////////////////////// +void CTraderAnimation::set_animation(LPCSTR anim) +{ + m_anim_global = anim; + + IKinematicsAnimated *kinematics_animated = smart_cast(m_trader->Visual()); + m_motion_global = kinematics_animated->ID_Cycle(m_anim_global); + kinematics_animated->PlayCycle (m_motion_global,TRUE,global_callback,this); +} + +void CTraderAnimation::set_head_animation(LPCSTR anim) +{ + m_anim_head = anim; + + // назначить анимацию головы + IKinematicsAnimated *kinematics_animated = smart_cast(m_trader->Visual()); + m_motion_head = kinematics_animated->ID_Cycle(m_anim_head); + kinematics_animated->PlayCycle (m_motion_head,TRUE,head_callback,this); +} + +////////////////////////////////////////////////////////////////////////// +// Sound management +////////////////////////////////////////////////////////////////////////// +void CTraderAnimation::set_sound(LPCSTR sound, LPCSTR anim) +{ + if (m_sound) remove_sound(); + + set_head_animation (anim); + + m_sound = new ref_sound(); + m_sound->create (sound,st_Effect,SOUND_TYPE_WORLD); + + Fvector pos = m_trader->cast_game_object()->Position(); + m_sound->play_at_pos(m_trader->cast_game_object(), pos); + m_sound->set_volume(20.0f); +} + +void CTraderAnimation::remove_sound() +{ + VERIFY (m_sound); + + if (m_sound->_feedback()) + m_sound->stop(); + + m_sound->destroy (); + xr_delete (m_sound); +} + +////////////////////////////////////////////////////////////////////////// +// Update +////////////////////////////////////////////////////////////////////////// +void CTraderAnimation::update_frame() +{ + if (m_sound && !m_sound->_feedback()) { + m_trader->callback (GameObject::eTraderSoundEnd)(); + remove_sound (); + } + + + if (!m_motion_global) { + m_trader->callback(GameObject::eTraderGlobalAnimationRequest)(); + if (m_anim_global) m_motion_head.invalidate(); + } + + // назначить анимацию головы + if (!m_motion_head) { + if (m_sound && m_sound->_feedback()) { + m_trader->callback(GameObject::eTraderHeadAnimationRequest)(); + } + } +} + +////////////////////////////////////////////////////////////////////////// +// External sound support +////////////////////////////////////////////////////////////////////////// +void CTraderAnimation::external_sound_start(LPCSTR phrase) +{ + if (m_sound) remove_sound(); + + m_sound = new ref_sound(); + m_sound->create (phrase,st_Effect,SOUND_TYPE_WORLD); + m_sound->play (NULL, sm_2D); + + m_motion_head.invalidate(); +} + +void CTraderAnimation::external_sound_stop() +{ + if (m_sound) remove_sound(); +} +////////////////////////////////////////////////////////////////////////// + diff --git a/src/xrGameLA/ai/trader/trader_animation.h b/src/xrGameLA/ai/trader/trader_animation.h new file mode 100644 index 000000000..daa7c65c8 --- /dev/null +++ b/src/xrGameLA/ai/trader/trader_animation.h @@ -0,0 +1,49 @@ +#pragma once + +#include "../../../../Include/xrRender/KinematicsAnimated.h" +#include "../../../../Include/xrRender/animation_motion.h" + + +class CAI_Trader; + +namespace MonsterSpace { + enum EMonsterHeadAnimType; +}; + +class CTraderAnimation { + CAI_Trader *m_trader; + + LPCSTR m_anim_global; + LPCSTR m_anim_head; + + MotionID m_motion_head; + MotionID m_motion_global; + + ref_sound *m_sound; + + bool m_external_sound; + +public: + CTraderAnimation (CAI_Trader *trader) : m_trader(trader) {} + + void reinit (); + + void set_animation (LPCSTR anim); + void set_head_animation (LPCSTR anim); + void set_sound (LPCSTR sound, LPCSTR head_anim); + + // Callbacks + static void global_callback (CBlend* B); + static void head_callback (CBlend* B); + + void update_frame (); + + void external_sound_start (LPCSTR phrase); + void external_sound_stop (); + +private: + void remove_sound (); + + +}; + diff --git a/src/xrGameLA/ai_debug.h b/src/xrGameLA/ai_debug.h new file mode 100644 index 000000000..15d167777 --- /dev/null +++ b/src/xrGameLA/ai_debug.h @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_debug.h +// Created : 02.10.2001 +// Modified : 11.11.2003 +// Author : Oles Shihkovtsov, Dmitriy Iassenev +// Description : Debug functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#ifdef DEBUG +# define aiDebug (1<<0) +# define aiBrain (1<<1) +# define aiMotion (1<<2) +# define aiFrustum (1<<3) +# define aiFuncs (1<<4) +# define aiALife (1<<5) +# define aiLua (1<<6) +# define aiGOAP (1<<7) +# define aiCover (1<<8) +# define aiAnimation (1<<9) +# define aiVision (1<<10) +# define aiMonsterDebug (1<<11) +# define aiStats (1<<12) +# define aiDestroy (1<<13) +# define aiSerialize (1<<14) +# define aiDialogs (1<<15) +# define aiInfoPortion (1<<16) +# define aiGOAPScript (1<<17) +# define aiGOAPObject (1<<18) +# define aiStalker (1<<19) +# define aiDrawGameGraph (1<<20) +# define aiDrawGameGraphStalkers (1<<21) +# define aiDrawGameGraphObjects (1<<22) +# define aiNilObjectAccess (1<<23) +# define aiDebugOnFrameAllocs (1<<25) +# define aiDrawVisibilityRays (1<<26) +# define aiAnimationStats (1<<27) +# define aiDrawGameGraphRealPos (1<<28) +#endif // DEBUG + +#ifndef MASTER_GOLD +# define aiIgnoreActor (1<<24) +# define aiObstaclesAvoiding (1<<28) +# define aiObstaclesAvoidingStatic (1<<29) + + extern Flags32 psAI_Flags; +#endif // MASTER_GOLD + +#ifdef DEBUG +# define aiDebugOnFrameAllocs (1<<25) +# define aiDrawVisibilityRays (1<<26) +# define aiAnimationStats (1<<27) +# define aiPath (1<<28) +# define aiTestCorrectness (1<<29) +# define aiDebugMsg (1<<30) +#endif // DEBUG \ No newline at end of file diff --git a/src/xrGameLA/ai_monster_space.h b/src/xrGameLA/ai_monster_space.h new file mode 100644 index 000000000..7def425dd --- /dev/null +++ b/src/xrGameLA/ai_monster_space.h @@ -0,0 +1,131 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_monster_space.h +// Created : 06.10.2003 +// Modified : 06.10.2003 +// Author : Dmitriy Iassenev +// Description : Monster types and structures +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "xrserver_space.h" + +namespace MonsterSpace { + enum EMentalState { + eMentalStateDanger = u32(0), + eMentalStateFree, + eMentalStatePanic, + }; + + enum EBodyState { + eBodyStateCrouch = u32(0), + eBodyStateStand, + eBodyStateDummy = u32(-1) + }; + + enum EMovementType { + eMovementTypeWalk = 0, + eMovementTypeRun, + eMovementTypeStand, + //lost alpha start +// eMovementTypeDrunk, + }; + + enum EMovementDirection { + eMovementDirectionForward = 0, + eMovementDirectionBackward, + eMovementDirectionLeft, + eMovementDirectionRight, + }; + + enum EObjectAction { + eObjectActionSwitch1, + eObjectActionSwitch2, + eObjectActionReload1, + eObjectActionReload2, + eObjectActionAim1, + eObjectActionAim2, + eObjectActionFire1, + eObjectActionFireNoReload, + eObjectActionFire2, + eObjectActionIdle, + eObjectActionStrapped, + eObjectActionDrop, + eObjectActionAimReady1, + eObjectActionAimReady2, + eObjectActionAimForceFull1, + eObjectActionAimForceFull2, + // for scripts only + eObjectActionActivate, + eObjectActionDeactivate, + eObjectActionUse, + eObjectActionTurnOn, + eObjectActionTurnOff, + // for old object handler only + eObjectActionShow, + eObjectActionHide, + eObjectActionTake, + eObjectActionMisfire1, + eObjectActionEmpty1, + eObjectActionNoItems = eObjectActionIdle | u16(-1), + // + eObjectActionDummy = u32(-1), + }; + + struct SBoneRotation { + SRotation current; + SRotation target; + float speed; + }; + + enum EScriptMonsterMoveAction { + eMA_WalkFwd, + eMA_WalkBkwd, + eMA_Run, + eMA_Drag, + eMA_Jump, + eMA_Steal + }; + + enum EScriptMonsterSpeedParam { + eSP_Default = u32(0), + eSP_ForceSpeed, + eSP_None = u32(-1), + }; + + enum EScriptMonsterAnimAction { + eAA_StandIdle, + eAA_CapturePrepare, + eAA_SitIdle, + eAA_LieIdle, + eAA_Eat, + eAA_Sleep, + eAA_Rest, + eAA_Attack, + eAA_LookAround, + eAA_Turn, + eAA_NoAction = u32(-1) + }; + + enum EScriptMonsterGlobalAction { + eGA_Rest = u32(0), + eGA_Eat, + eGA_Attack, + eGA_Panic, + eGA_None = u32(-1) + }; + + enum EScriptSoundAnim { + eAnimSoundCustom = u32(0), + eAnimSoundDefault, + }; + + enum EMonsterHeadAnimType { + eHeadAnimNormal = u32(0), + eHeadAnimAngry, + eHeadAnimGlad, + eHeadAnimKind, + + eHeadAnimNone = u32(-1), + }; +}; diff --git a/src/xrGameLA/ai_object_location.h b/src/xrGameLA/ai_object_location.h new file mode 100644 index 000000000..362c9f7a9 --- /dev/null +++ b/src/xrGameLA/ai_object_location.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_object_location.h +// Created : 27.11.2003 +// Modified : 27.11.2003 +// Author : Dmitriy Iassenev +// Description : AI object location +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "game_graph_space.h" + +namespace LevelGraph { + class CVertex; +}; + +class CAI_ObjectLocation { +private: + u32 m_level_vertex_id; + GameGraph::_GRAPH_ID m_game_vertex_id; + +public: + IC CAI_ObjectLocation (); + IC void init (); + IC virtual void reinit (); + IC void game_vertex (const GameGraph::CVertex *game_vertex); + IC void game_vertex (const GameGraph::_GRAPH_ID game_vertex_id); + IC const GameGraph::CVertex *game_vertex () const; + IC const GameGraph::_GRAPH_ID game_vertex_id () const; + IC void level_vertex (const LevelGraph::CVertex *level_vertex); + IC void level_vertex (const u32 level_vertex_id); + IC const LevelGraph::CVertex *level_vertex () const; + IC const u32 level_vertex_id () const; +}; + +#include "ai_object_location_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai_object_location_impl.h b/src/xrGameLA/ai_object_location_impl.h new file mode 100644 index 000000000..19dcb246e --- /dev/null +++ b/src/xrGameLA/ai_object_location_impl.h @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_object_location_impl.h +// Created : 27.11.2003 +// Modified : 27.11.2003 +// Author : Dmitriy Iassenev +// Description : AI object location implementation +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "ai_object_location.h" +#include "ai_space.h" +#include "game_graph.h" +#include "level_graph.h" + +IC void CAI_ObjectLocation::init() +{ + if (ai().get_level_graph()) + ai().level_graph().set_invalid_vertex (m_level_vertex_id); + else + m_level_vertex_id = u32(-1); + + if (ai().get_game_graph()) + ai().game_graph().set_invalid_vertex (m_game_vertex_id); + else + m_game_vertex_id = GameGraph::_GRAPH_ID(-1); +} + +IC void CAI_ObjectLocation::game_vertex(const CGameGraph::CVertex *game_vertex) +{ + VERIFY (ai().game_graph().valid_vertex_id(ai().game_graph().vertex_id(game_vertex))); + m_game_vertex_id = ai().game_graph().vertex_id(game_vertex); +} + +IC void CAI_ObjectLocation::game_vertex(const GameGraph::_GRAPH_ID game_vertex_id) +{ + VERIFY (ai().game_graph().valid_vertex_id(game_vertex_id)); + m_game_vertex_id = game_vertex_id; +} + +IC const CGameGraph::CVertex *CAI_ObjectLocation::game_vertex() const +{ + VERIFY (ai().game_graph().valid_vertex_id(m_game_vertex_id)); + return (ai().game_graph().vertex(m_game_vertex_id)); +} + +IC void CAI_ObjectLocation::level_vertex(const CLevelGraph::CVertex *level_vertex) +{ + VERIFY (ai().level_graph().valid_vertex_id(ai().level_graph().vertex_id(level_vertex))); + m_level_vertex_id = ai().level_graph().vertex_id(level_vertex); +} + +IC void CAI_ObjectLocation::level_vertex(const u32 level_vertex_id) +{ + VERIFY (ai().level_graph().valid_vertex_id(level_vertex_id)); + m_level_vertex_id = level_vertex_id; +} + +IC const CLevelGraph::CVertex *CAI_ObjectLocation::level_vertex() const +{ + VERIFY (ai().level_graph().valid_vertex_id(m_level_vertex_id)); + return (ai().level_graph().vertex(m_level_vertex_id)); +} diff --git a/src/xrGameLA/ai_object_location_inline.h b/src/xrGameLA/ai_object_location_inline.h new file mode 100644 index 000000000..f30a8b73d --- /dev/null +++ b/src/xrGameLA/ai_object_location_inline.h @@ -0,0 +1,29 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_object_location.h +// Created : 27.11.2003 +// Modified : 27.11.2003 +// Author : Dmitriy Iassenev +// Description : AI object location +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CAI_ObjectLocation::CAI_ObjectLocation() +{ + init (); +} + +IC void CAI_ObjectLocation::reinit() +{ + init (); +} + +IC const GameGraph::_GRAPH_ID CAI_ObjectLocation::game_vertex_id() const +{ + return (m_game_vertex_id); +} + +IC const u32 CAI_ObjectLocation::level_vertex_id() const +{ + return (m_level_vertex_id); +} diff --git a/src/xrGameLA/ai_sounds.cpp b/src/xrGameLA/ai_sounds.cpp new file mode 100644 index 000000000..5aa73f088 --- /dev/null +++ b/src/xrGameLA/ai_sounds.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" +#pragma hdrstop + +#include "ai_sounds.h" + +xr_token anomaly_type_token[]={ + { "undefined", sg_Undefined }, + { "Item picking up", SOUND_TYPE_ITEM_PICKING_UP }, + { "Item dropping", SOUND_TYPE_ITEM_DROPPING }, + { "Item taking", SOUND_TYPE_ITEM_TAKING }, + { "Item hiding", SOUND_TYPE_ITEM_HIDING }, + { "Item using", SOUND_TYPE_ITEM_USING }, + { "Weapon shooting", SOUND_TYPE_WEAPON_SHOOTING }, + { "Weapon empty clicking", SOUND_TYPE_WEAPON_EMPTY_CLICKING }, + { "Weapon bullet hit", SOUND_TYPE_WEAPON_BULLET_HIT }, + { "Weapon recharging", SOUND_TYPE_WEAPON_RECHARGING }, + { "NPC dying", SOUND_TYPE_MONSTER_DYING }, + { "NPC injuring", SOUND_TYPE_MONSTER_INJURING }, + { "NPC step", SOUND_TYPE_MONSTER_STEP }, + { "NPC talking", SOUND_TYPE_MONSTER_TALKING }, + { "NPC attacking", SOUND_TYPE_MONSTER_ATTACKING }, + { "NPC eating", SOUND_TYPE_MONSTER_EATING }, + { "Anomaly idle", SOUND_TYPE_ANOMALY_IDLE }, + { "Object breaking", SOUND_TYPE_WORLD_OBJECT_BREAKING }, + { "Object colliding", SOUND_TYPE_WORLD_OBJECT_COLLIDING }, + { "Object exploding", SOUND_TYPE_WORLD_OBJECT_EXPLODING }, + { "World ambient", SOUND_TYPE_WORLD_AMBIENT }, + { 0, 0} +}; diff --git a/src/xrGameLA/ai_sounds.h b/src/xrGameLA/ai_sounds.h new file mode 100644 index 000000000..8a6c09edb --- /dev/null +++ b/src/xrGameLA/ai_sounds.h @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_sounds.h +// Created : 15.08.2002 +// Modified : 15.08.2002 +// Author : Dmitriy Iassenev +// Description : Sounds for AI personalities +//////////////////////////////////////////////////////////////////////////// + +#ifndef ai_soundsH +#define ai_soundsH + +enum ESoundTypes { + SOUND_TYPE_NO_SOUND = 0x00000000ui32, + + SOUND_TYPE_WEAPON = 0x80000000ui32, + SOUND_TYPE_ITEM = 0x40000000ui32, + SOUND_TYPE_MONSTER = 0x20000000ui32, + SOUND_TYPE_ANOMALY = 0x10000000ui32, + SOUND_TYPE_WORLD = 0x08000000ui32, + + SOUND_TYPE_PICKING_UP = 0x04000000ui32, + SOUND_TYPE_DROPPING = 0x02000000ui32, + SOUND_TYPE_HIDING = 0x01000000ui32, + SOUND_TYPE_TAKING = 0x00800000ui32, + SOUND_TYPE_USING = 0x00400000ui32, + + SOUND_TYPE_SHOOTING = 0x00200000ui32, + SOUND_TYPE_EMPTY_CLICKING = 0x00100000ui32, + SOUND_TYPE_BULLET_HIT = 0x00080000ui32, + SOUND_TYPE_RECHARGING = 0x00040000ui32, + + SOUND_TYPE_DYING = 0x00020000ui32, + SOUND_TYPE_INJURING = 0x00010000ui32, + SOUND_TYPE_STEP = 0x00008000ui32, + SOUND_TYPE_TALKING = 0x00004000ui32, + SOUND_TYPE_ATTACKING = 0x00002000ui32, + SOUND_TYPE_EATING = 0x00001000ui32, + + SOUND_TYPE_IDLE = 0x00000800ui32, + + SOUND_TYPE_OBJECT_BREAKING = 0x00000400ui32, + SOUND_TYPE_OBJECT_COLLIDING = 0x00000200ui32, + SOUND_TYPE_OBJECT_EXPLODING = 0x00000100ui32, + SOUND_TYPE_AMBIENT = 0x00000080ui32, + + SOUND_TYPE_ITEM_PICKING_UP = SOUND_TYPE_ITEM | SOUND_TYPE_PICKING_UP, + SOUND_TYPE_ITEM_DROPPING = SOUND_TYPE_ITEM | SOUND_TYPE_DROPPING, + SOUND_TYPE_ITEM_HIDING = SOUND_TYPE_ITEM | SOUND_TYPE_HIDING, + SOUND_TYPE_ITEM_TAKING = SOUND_TYPE_ITEM | SOUND_TYPE_TAKING, + SOUND_TYPE_ITEM_USING = SOUND_TYPE_ITEM | SOUND_TYPE_USING, + + SOUND_TYPE_WEAPON_SHOOTING = SOUND_TYPE_WEAPON | SOUND_TYPE_SHOOTING, + SOUND_TYPE_WEAPON_EMPTY_CLICKING = SOUND_TYPE_WEAPON | SOUND_TYPE_EMPTY_CLICKING, + SOUND_TYPE_WEAPON_BULLET_HIT = SOUND_TYPE_WEAPON | SOUND_TYPE_BULLET_HIT, + SOUND_TYPE_WEAPON_RECHARGING = SOUND_TYPE_WEAPON | SOUND_TYPE_RECHARGING, + + SOUND_TYPE_MONSTER_DYING = SOUND_TYPE_MONSTER | SOUND_TYPE_DYING, + SOUND_TYPE_MONSTER_INJURING = SOUND_TYPE_MONSTER | SOUND_TYPE_INJURING, + SOUND_TYPE_MONSTER_STEP = SOUND_TYPE_MONSTER | SOUND_TYPE_STEP, + SOUND_TYPE_MONSTER_TALKING = SOUND_TYPE_MONSTER | SOUND_TYPE_TALKING, + SOUND_TYPE_MONSTER_ATTACKING = SOUND_TYPE_MONSTER | SOUND_TYPE_ATTACKING, + SOUND_TYPE_MONSTER_EATING = SOUND_TYPE_MONSTER | SOUND_TYPE_EATING, + + SOUND_TYPE_ANOMALY_IDLE = SOUND_TYPE_ANOMALY | SOUND_TYPE_IDLE, + + SOUND_TYPE_WORLD_OBJECT_BREAKING = SOUND_TYPE_WORLD | SOUND_TYPE_OBJECT_BREAKING, + SOUND_TYPE_WORLD_OBJECT_COLLIDING = SOUND_TYPE_WORLD | SOUND_TYPE_OBJECT_COLLIDING, + SOUND_TYPE_WORLD_OBJECT_EXPLODING = SOUND_TYPE_WORLD | SOUND_TYPE_OBJECT_EXPLODING, + SOUND_TYPE_WORLD_AMBIENT = SOUND_TYPE_WORLD | SOUND_TYPE_AMBIENT, + + SOUND_TYPE_WEAPON_PISTOL = SOUND_TYPE_WEAPON, + SOUND_TYPE_WEAPON_GUN = SOUND_TYPE_WEAPON, + SOUND_TYPE_WEAPON_SUBMACHINEGUN = SOUND_TYPE_WEAPON, + SOUND_TYPE_WEAPON_MACHINEGUN = SOUND_TYPE_WEAPON, + SOUND_TYPE_WEAPON_SNIPERRIFLE = SOUND_TYPE_WEAPON, + SOUND_TYPE_WEAPON_GRENADELAUNCHER = SOUND_TYPE_WEAPON, + SOUND_TYPE_WEAPON_ROCKETLAUNCHER = SOUND_TYPE_WEAPON, +}; + +#define CROUCH_SOUND_FACTOR .3f +#define ACCELERATED_SOUND_FACTOR .5f + +extern xr_token anomaly_type_token[]; + +#endif diff --git a/src/xrGameLA/ai_space.cpp b/src/xrGameLA/ai_space.cpp new file mode 100644 index 000000000..8aec0032c --- /dev/null +++ b/src/xrGameLA/ai_space.cpp @@ -0,0 +1,258 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_space.h +// Created : 12.11.2003 +// Modified : 12.11.2003 +// Author : Dmitriy Iassenev +// Description : AI space class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "game_graph.h" +#include "game_level_cross_table.h" +#include "level_graph.h" +#include "graph_engine.h" +#include "ef_storage.h" +#include "ai_space.h" +#include "cover_manager.h" +#include "cover_point.h" +#include "script_engine.h" +#include "patrol_path_storage.h" +#include "alife_simulator.h" + +ENGINE_API bool g_dedicated_server; + +CAI_Space *g_ai_space = 0; + +CAI_Space::CAI_Space () +{ + m_ef_storage = 0; + m_game_graph = 0; + m_graph_engine = 0; + m_cover_manager = 0; + m_level_graph = 0; +#ifndef PRIQUEL + m_cross_table = 0; +#endif // PRIQUEL + m_alife_simulator = 0; + m_patrol_path_storage = 0; + m_script_engine = 0; +} + +void CAI_Space::init () +{ + if (g_dedicated_server) + return; + + VERIFY (!m_ef_storage); + m_ef_storage = new CEF_Storage(); + +#ifndef PRIQUEL + VERIFY (!m_game_graph); + m_game_graph = new CGameGraph(); + + VERIFY (!m_graph_engine); + m_graph_engine = new CGraphEngine(game_graph().header().vertex_count()); +#else // PRIQUEL + VERIFY (!m_graph_engine); + m_graph_engine = new CGraphEngine(1024); +#endif // PRIQUEL + + VERIFY (!m_cover_manager); + m_cover_manager = new CCoverManager(); + + VERIFY (!m_patrol_path_storage); + m_patrol_path_storage = new CPatrolPathStorage(); + + VERIFY (!m_script_engine); + m_script_engine = new CScriptEngine(); + script_engine().init (); + + extern string4096 g_ca_stdout; + setvbuf (stderr,g_ca_stdout,_IOFBF,sizeof(g_ca_stdout)); +} + +CAI_Space::~CAI_Space () +{ + unload (); + + xr_delete (m_patrol_path_storage); + xr_delete (m_ef_storage); + +#ifdef PRIQUEL + VERIFY (!m_game_graph); +#else // PRIQUEL + xr_delete (m_game_graph); +#endif // PRIQUEL + + try { + xr_delete (m_script_engine); + } + catch(...) { + } + + xr_delete (m_cover_manager); + xr_delete (m_graph_engine); +} + +void CAI_Space::load (LPCSTR level_name) +{ +#ifdef PRIQUEL + VERIFY (m_game_graph); +#endif // PRIQUEL + + unload (true); + +#ifdef DEBUG + Memory.mem_compact (); + u32 mem_usage = Memory.mem_usage(); + CTimer timer; + timer.Start (); +#endif + + const CGameGraph::SLevel ¤t_level = game_graph().header().level(level_name); + + m_level_graph = new CLevelGraph(); +#ifndef PRIQUEL + m_cross_table = new CGameLevelCrossTable(); +#else // PRIQUEL + game_graph().set_current_level(current_level.id()); +#endif // PRIQUEL + R_ASSERT2 (cross_table().header().level_guid() == level_graph().header().guid(), "cross_table doesn't correspond to the AI-map"); + R_ASSERT2 (cross_table().header().game_guid() == game_graph().header().guid(), "graph doesn't correspond to the cross table"); + m_graph_engine = new CGraphEngine( + _max( + game_graph().header().vertex_count(), + level_graph().header().vertex_count() + ) + ); + + R_ASSERT2 (current_level.guid() == level_graph().header().guid(), "graph doesn't correspond to the AI-map"); + +#ifdef DEBUG + if (!xr_strcmp(current_level.name(),level_name)) + validate (current_level.id()); +#endif + + level_graph().level_id (current_level.id()); +#ifndef PRIQUEL + game_graph().set_current_level(current_level.id()); +#endif // PRIQUEL + + m_cover_manager->compute_static_cover (); + +#ifdef DEBUG + Msg ("* Loading ai space is successfully completed (%.3fs, %7.3f Mb)",timer.GetElapsed_sec(),float(Memory.mem_usage() - mem_usage)/1048576.0); +#endif +} + +void CAI_Space::unload (bool reload) +{ + if (g_dedicated_server) + return; + + script_engine().unload (); + xr_delete (m_graph_engine); + xr_delete (m_level_graph); +#ifndef PRIQUEL + xr_delete (m_cross_table); +#endif // PRIQUEL + if ( + !reload +#ifdef PRIQUEL + && m_game_graph +#endif // PRIQUEL + ) + m_graph_engine = new CGraphEngine(game_graph().header().vertex_count()); +} + +#ifdef DEBUG +void CAI_Space::validate (const u32 level_id) const +{ + VERIFY (level_graph().header().vertex_count() == cross_table().header().level_vertex_count()); + for (GameGraph::_GRAPH_ID i=0, n = game_graph().header().vertex_count(); ilevel_id()) && + (!level_graph().valid_vertex_id(game_graph().vertex(i)->level_vertex_id()) || + (cross_table().vertex(game_graph().vertex(i)->level_vertex_id()).game_vertex_id() != i) || + !level_graph().inside(game_graph().vertex(i)->level_vertex_id(),game_graph().vertex(i)->level_point()))) { + Msg ("! Graph doesn't correspond to the cross table"); + R_ASSERT2 (false,"Graph doesn't correspond to the cross table"); + } + +// Msg ("death graph point id : %d",cross_table().vertex(455236).game_vertex_id()); + + for (u32 i=0, n=game_graph().header().vertex_count(); ilevel_id()) + continue; + + CGameGraph::const_spawn_iterator I, E; + game_graph().begin_spawn (i,I,E); +// Msg ("vertex [%d] has %d death points",i,game_graph().vertex(i)->death_point_count()); + for ( ; I != E; ++I) { + VERIFY (cross_table().vertex((*I).level_vertex_id()).game_vertex_id() == i); + } + } + + +// Msg ("* Graph corresponds to the cross table"); +} +#endif + +void CAI_Space::patrol_path_storage_raw (IReader &stream) +{ + if (g_dedicated_server) + return; + + xr_delete (m_patrol_path_storage); + m_patrol_path_storage = new CPatrolPathStorage(); + m_patrol_path_storage->load_raw (get_level_graph(),get_cross_table(),get_game_graph(),stream); +} + +void CAI_Space::patrol_path_storage (IReader &stream) +{ + if (g_dedicated_server) + return; + + xr_delete (m_patrol_path_storage); + m_patrol_path_storage = new CPatrolPathStorage(); + m_patrol_path_storage->load (stream); +} + +void CAI_Space::set_alife (CALifeSimulator *alife_simulator) +{ + VERIFY ((!m_alife_simulator && alife_simulator) || (m_alife_simulator && !alife_simulator)); + m_alife_simulator = alife_simulator; + +#ifdef PRIQUEL + if (!alife_simulator) { + VERIFY (m_game_graph); + m_game_graph = 0; + xr_delete (m_graph_engine); + } + else + VERIFY (!m_game_graph); +#endif // PRIQUEL +} + +#ifdef PRIQUEL +void CAI_Space::game_graph (CGameGraph *game_graph) +{ + VERIFY (m_alife_simulator); + VERIFY (game_graph); + VERIFY (!m_game_graph); + m_game_graph = game_graph; + +// VERIFY (!m_graph_engine); + xr_delete (m_graph_engine); + m_graph_engine = new CGraphEngine(this->game_graph().header().vertex_count()); +} + +const CGameLevelCrossTable &CAI_Space::cross_table () const +{ + return (game_graph().cross_table()); +} + +const CGameLevelCrossTable *CAI_Space::get_cross_table () const +{ + return (&game_graph().cross_table()); +} +#endif // PRIQUEL \ No newline at end of file diff --git a/src/xrGameLA/ai_space.h b/src/xrGameLA/ai_space.h new file mode 100644 index 000000000..04ad86899 --- /dev/null +++ b/src/xrGameLA/ai_space.h @@ -0,0 +1,87 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_space.h +// Created : 12.11.2003 +// Modified : 12.11.2003 +// Author : Dmitriy Iassenev +// Description : AI space class +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +class CGameGraph; +class CGameLevelCrossTable; +class CLevelGraph; +class CGraphEngine; +class CEF_Storage; +class CALifeSimulator; +class CCoverManager; +class CScriptEngine; +class CPatrolPathStorage; + +class CAI_Space { +private: + friend class CALifeSimulator; + friend class CALifeGraphRegistry; + friend class CALifeSpawnRegistry; + friend class CALifeSpawnRegistry; + friend class CLevel; + +private: + CGameGraph *m_game_graph; +#ifndef PRIQUEL + CGameLevelCrossTable *m_cross_table; +#endif // PRIQUEL + CLevelGraph *m_level_graph; + CGraphEngine *m_graph_engine; + CEF_Storage *m_ef_storage; + CALifeSimulator *m_alife_simulator; + CCoverManager *m_cover_manager; + CScriptEngine *m_script_engine; + CPatrolPathStorage *m_patrol_path_storage; + +private: + void load (LPCSTR level_name); + void unload (bool reload = false); + void patrol_path_storage_raw (IReader &stream); + void patrol_path_storage (IReader &stream); + void set_alife (CALifeSimulator *alife_simulator); + +#ifdef PRIQUEL +private: + void game_graph (CGameGraph *game_graph); +#endif // PRIQUEL + +public: + CAI_Space (); + virtual ~CAI_Space (); + void init (); + IC CGameGraph &game_graph () const; + IC CGameGraph *get_game_graph () const; + IC CLevelGraph &level_graph () const; + IC const CLevelGraph *get_level_graph () const; +#ifdef PRIQUEL + const CGameLevelCrossTable &cross_table () const; + const CGameLevelCrossTable *get_cross_table () const; +#else // PRIQUEL + IC const CGameLevelCrossTable &cross_table () const; + IC const CGameLevelCrossTable *get_cross_table () const; +#endif // PRIQUEL + IC const CPatrolPathStorage &patrol_paths () const; + IC CEF_Storage &ef_storage () const; + IC CGraphEngine &graph_engine () const; + IC const CALifeSimulator &alife () const; +// IC CALifeSimulator *get_alife_nonconst (); + IC const CALifeSimulator *get_alife () const; + IC const CCoverManager &cover_manager () const; + IC CScriptEngine &script_engine () const; + +#ifdef DEBUG + void validate (const u32 level_id) const; +#endif +}; + +IC CAI_Space &ai (); + +extern CAI_Space *g_ai_space; + +#include "ai_space_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/ai_space_inline.h b/src/xrGameLA/ai_space_inline.h new file mode 100644 index 000000000..c1c6baa24 --- /dev/null +++ b/src/xrGameLA/ai_space_inline.h @@ -0,0 +1,99 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_space_inline.h +// Created : 12.11.2003 +// Modified : 25.11.2003 +// Author : Dmitriy Iassenev +// Description : AI space class inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CGameGraph &CAI_Space::game_graph () const +{ + VERIFY (m_game_graph); + return (*m_game_graph); +} + +IC CGameGraph *CAI_Space::get_game_graph () const +{ + return (m_game_graph); +} + +IC CLevelGraph &CAI_Space::level_graph () const +{ + VERIFY (m_level_graph); + return (*m_level_graph); +} + +IC const CLevelGraph *CAI_Space::get_level_graph () const +{ + return (m_level_graph); +} + +#ifndef PRIQUEL +IC const CGameLevelCrossTable &CAI_Space::cross_table () const +{ + VERIFY (m_cross_table); + return (*m_cross_table); +} + +IC const CGameLevelCrossTable *CAI_Space::get_cross_table () const +{ + return (m_cross_table); +} +#endif // PRIQUEL + +IC CEF_Storage &CAI_Space::ef_storage () const +{ + VERIFY (m_ef_storage); + return (*m_ef_storage); +} + +IC CGraphEngine &CAI_Space::graph_engine () const +{ + VERIFY (m_graph_engine); + return (*m_graph_engine); +} + +IC const CALifeSimulator &CAI_Space::alife () const +{ + VERIFY (m_alife_simulator); + return (*m_alife_simulator); +} +/* +IC CALifeSimulator *CAI_Space::get_alife_nonconst () +{ + return (m_alife_simulator); +} +*/ +IC const CALifeSimulator *CAI_Space::get_alife () const +{ + return (m_alife_simulator); +} + +IC const CCoverManager &CAI_Space::cover_manager () const +{ + VERIFY (m_cover_manager); + return (*m_cover_manager); +} + +IC CScriptEngine &CAI_Space::script_engine () const +{ + VERIFY (m_script_engine); + return (*m_script_engine); +} + +IC const CPatrolPathStorage &CAI_Space::patrol_paths () const +{ + VERIFY (m_patrol_path_storage); + return (*m_patrol_path_storage); +} + +IC CAI_Space &ai () +{ + if (!g_ai_space) { + g_ai_space = new CAI_Space(); + g_ai_space->init (); + } + return (*g_ai_space); +} diff --git a/src/xrGameLA/ai_stalker_alife.cpp b/src/xrGameLA/ai_stalker_alife.cpp new file mode 100644 index 000000000..b3ba6547c --- /dev/null +++ b/src/xrGameLA/ai_stalker_alife.cpp @@ -0,0 +1,465 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : ai_stalker_alife.cpp +// Created : 15.10.2004 +// Modified : 15.10.2004 +// Author : Dmitriy Iassenev +// Description : Stalker ALife functions +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "ai/stalker/ai_stalker.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_space.h" +#include "inventory.h" +#include "clsid_game.h" +#include "pda.h" +#include "eatable_item.h" +#include "medkit.h" +#include "weapon.h" +#include "Grenade.h" +#include "customdetector.h" +#include "ef_storage.h" +#include "ef_primary.h" +#include "ef_pattern.h" +#include "trade_parameters.h" + +static const int MAX_AMMO_ATTACH_COUNT = 10; +static const int enough_ammo_box_count = 1; + +IC bool CAI_Stalker::CTradeItem::operator< (const CTradeItem &trade_item) const +{ + return (m_item->object().ID() < trade_item.m_item->object().ID()); +} + +IC bool CAI_Stalker::CTradeItem::operator== (u16 id) const +{ + return (m_item->object().ID() == id); +} + +bool CAI_Stalker::tradable_item (CInventoryItem *inventory_item, const u16 ¤t_owner_id) +{ + if (!inventory_item->useful_for_NPC()) + return (false); + + if (CLSID_DEVICE_PDA == inventory_item->object().CLS_ID) { + CPda *pda = smart_cast(inventory_item); + VERIFY (pda); + if (pda->GetOriginalOwnerID() == current_owner_id) + return (false); + } + + return ( + trade_parameters().enabled( + CTradeParameters::action_sell(0), + inventory_item->object().cNameSect() + ) + ); +} + +u32 CAI_Stalker::fill_items (CInventory &inventory, CGameObject *old_owner, ALife::_OBJECT_ID new_owner_id) +{ + u32 result = 0; + TIItemContainer::iterator I = inventory.m_all.begin(); + TIItemContainer::iterator E = inventory.m_all.end(); + for ( ; I != E; ++I) { + if (!tradable_item(*I,old_owner->ID())) + continue; + + m_temp_items.push_back (CTradeItem(*I,old_owner->ID(),new_owner_id)); + result += (*I)->Cost(); + } + + return (result); +} + +void CAI_Stalker::transfer_item (CInventoryItem *item, CGameObject *old_owner, CGameObject *new_owner) +{ + NET_Packet P; + CGameObject *O = old_owner; + O->u_EventGen (P,GE_TRADE_SELL,O->ID()); + P.w_u16 (u16(item->object().ID())); + O->u_EventSend (P); + + O = new_owner; + O->u_EventGen (P,GE_TRADE_BUY,O->ID()); + P.w_u16 (u16(item->object().ID())); + O->u_EventSend (P); +} + +IC void CAI_Stalker::buy_item_virtual (CTradeItem &item) +{ + item.m_new_owner_id = ID(); + m_total_money -= item.m_item->Cost(); + if (m_current_trader) + m_current_trader->set_money(m_current_trader->get_money() + item.m_item->Cost(), true); +} + +void CAI_Stalker::choose_food () +{ + // stalker cannot change food due to the game design :-((( + return; +} + +void CAI_Stalker::attach_available_ammo (CWeapon *weapon) +{ + if (!weapon || weapon->m_ammoTypes.empty()) + return; + + u32 count = 0; + xr_vector::iterator I = m_temp_items.begin(); + xr_vector::iterator E = m_temp_items.end(); + for ( ; I != E; ++I) { + if (m_total_money < (*I).m_item->Cost()) + continue; + + if ( + std::find( + weapon->m_ammoTypes.begin(), + weapon->m_ammoTypes.end(), + (*I).m_item->object().cNameSect() + ) == + weapon->m_ammoTypes.end() + ) + continue; + + buy_item_virtual (*I); + + ++count; + if (count >= MAX_AMMO_ATTACH_COUNT) + break; + } +} + +void CAI_Stalker::choose_weapon (ALife::EWeaponPriorityType weapon_priority_type) +{ + CTradeItem *best_weapon = 0; + float best_value = -1.f; + ai().ef_storage().non_alife().member() = this; + + xr_vector::iterator I = m_temp_items.begin(); + xr_vector::iterator E = m_temp_items.end(); + for ( ; I != E; ++I) { + if (m_total_money < (*I).m_item->Cost()) + continue; + + ai().ef_storage().non_alife().member_item() = &(*I).m_item->object(); + int j = ai().ef_storage().m_pfPersonalWeaponType->dwfGetWeaponType(); + float current_value = -1.f; + switch (weapon_priority_type) { + case ALife::eWeaponPriorityTypeKnife : { + if (1 != j) + continue; + current_value = ai().ef_storage().m_pfItemValue->ffGetValue(); + break; + } + case ALife::eWeaponPriorityTypeSecondary : { + if (5 != j) + continue; + current_value = ai().ef_storage().m_pfSmallWeaponValue->ffGetValue(); + break; + } + case ALife::eWeaponPriorityTypePrimary : { + if ((6 != j) && (8 != j) && (9 != j)) + continue; + current_value = ai().ef_storage().m_pfMainWeaponValue->ffGetValue(); + break; + } + case ALife::eWeaponPriorityTypeGrenade : { + if (7 != j) + continue; + current_value = ai().ef_storage().m_pfItemValue->ffGetValue(); + break; + } + default : NODEFAULT; + } + + if ((current_value > best_value)) { + best_value = current_value; + best_weapon = &*I; + } + } + if (best_weapon) { + buy_item_virtual (*best_weapon); + attach_available_ammo (smart_cast(best_weapon->m_item)); + } +} + +void CAI_Stalker::choose_medikit () +{ + // stalker cannot change medikit due to the game design :-((( + return; +} + +void CAI_Stalker::choose_detector () +{ + CTradeItem *best_detector = 0; + float best_value = -1.f; + ai().ef_storage().non_alife().member() = this; + xr_vector::iterator I = m_temp_items.begin(); + xr_vector::iterator E = m_temp_items.end(); + for ( ; I != E; ++I) { + if (m_total_money < (*I).m_item->Cost()) + continue; + + CCustomDetector *detector = smart_cast((*I).m_item); + if (!detector) + continue; + + // evaluating item + ai().ef_storage().non_alife().member_item() = detector; + float current_value = ai().ef_storage().m_pfDetectorType->ffGetValue(); + // choosing the best item + if ((current_value > best_value)) { + best_detector = &*I; + best_value = current_value; + } + } + if (best_detector) + buy_item_virtual (*best_detector); +} + +void CAI_Stalker::choose_equipment () +{ + // stalker cannot change their equipment due to the game design :-((( + return; +} + +void CAI_Stalker::select_items () +{ + if (!m_can_select_items) + return; + + choose_food (); + choose_weapon (ALife::eWeaponPriorityTypeKnife); + choose_weapon (ALife::eWeaponPriorityTypeSecondary); + choose_weapon (ALife::eWeaponPriorityTypePrimary); + choose_weapon (ALife::eWeaponPriorityTypeGrenade); + choose_medikit (); + choose_detector (); + choose_equipment (); +} + +void CAI_Stalker::update_sell_info () +{ + if (m_sell_info_actuality) + return; + + m_sell_info_actuality = true; + + m_temp_items.clear (); + m_current_trader = 0; + m_total_money = get_money(); + u32 money_delta = fill_items(inventory(),this,ALife::_OBJECT_ID(-1)); + m_total_money += money_delta; + std::sort (m_temp_items.begin(),m_temp_items.end()); + select_items (); + + TIItemContainer::iterator I = inventory().m_all.begin(); + TIItemContainer::iterator E = inventory().m_all.end(); + for ( ; I != E; ++I) { + if (!tradable_item(*I,ID())) + m_temp_items.push_back (CTradeItem(*I,ID(),ID())); + } +} + +bool CAI_Stalker::can_sell (CInventoryItem const *item) +{ + update_sell_info (); + xr_vector::const_iterator I = std::find(m_temp_items.begin(),m_temp_items.end(),item->object().ID()); + VERIFY (I != m_temp_items.end()); + return ((*I).m_new_owner_id != ID()); +} + +bool CAI_Stalker::AllowItemToTrade (CInventoryItem const * item, EItemPlace place) const +{ + if (!g_Alive()) + return (trade_parameters().enabled(CTradeParameters::action_show(0),item->object().cNameSect())); + + return (const_cast(this)->can_sell(item)); +} + +bool CAI_Stalker::non_conflicted (const CInventoryItem *item, const CWeapon *new_weapon) const +{ + if (item->object().ID() == new_weapon->ID()) + return (true); + + const CWeapon *weapon = smart_cast(item); + if (!weapon) + return (true); + + switch (weapon->ef_weapon_type()) { + // knives + case 1 : { + if (weapon->ef_weapon_type() != new_weapon->ef_weapon_type()) + return (true); + + break; + } + // pistols + case 5 : { + if (weapon->ef_weapon_type() != new_weapon->ef_weapon_type()) + return (true); + + break; + } + // automatic weapon + case 6 : + // shotguns + case 7 : + // sniper rifles + case 8 : { + if ((new_weapon->ef_weapon_type() < 6) || (new_weapon->ef_weapon_type() > 8)) + return (true); + + break; + } + case 9 : { + if (weapon->ef_weapon_type() != new_weapon->ef_weapon_type()) + return (true); + + break; + } + case 10 : { + if (weapon->ef_weapon_type() != new_weapon->ef_weapon_type()) + return (true); + + break; + } + } + + return (false); +} + +bool CAI_Stalker::enough_ammo (const CWeapon *new_weapon) const +{ + int ammo_box_count = 0; + + TIItemContainer::const_iterator I = inventory().m_all.begin(); + TIItemContainer::const_iterator E = inventory().m_all.end(); + for ( ; I != E; ++I) { + if (std::find(new_weapon->m_ammoTypes.begin(),new_weapon->m_ammoTypes.end(),(*I)->object().cNameSect()) == new_weapon->m_ammoTypes.end()) + continue; + + ++ammo_box_count; + if (ammo_box_count >= enough_ammo_box_count) + return (true); + } + + return (false); +} + +bool CAI_Stalker::conflicted (const CInventoryItem *item, const CWeapon *new_weapon, bool new_wepon_enough_ammo, int new_weapon_rank) const +{ + if (non_conflicted(item,new_weapon)) + return (false); + + const CWeapon *weapon = smart_cast(item); + VERIFY (weapon); + + bool current_wepon_enough_ammo = enough_ammo(weapon); + if (current_wepon_enough_ammo && !new_wepon_enough_ammo) + return (true); + + if (!current_wepon_enough_ammo && new_wepon_enough_ammo) + return (false); + + if (!fsimilar(weapon->GetCondition(),new_weapon->GetCondition(),.05f)) + return (weapon->GetCondition() >= new_weapon->GetCondition()); + + if (weapon->ef_weapon_type() != new_weapon->ef_weapon_type()) + return (weapon->ef_weapon_type() >= new_weapon->ef_weapon_type()); + + u32 weapon_rank = weapon->ai_weapon_rank(); + + if (weapon_rank != (u32)new_weapon_rank) + return (weapon_rank >= (u32)new_weapon_rank); + + return (true); +} + +bool CAI_Stalker::can_take (CInventoryItem const * item) +{ + const CWeapon *new_weapon = smart_cast(item); + if (!new_weapon) + return (false); + + bool new_weapon_enough_ammo = enough_ammo(new_weapon); + u32 new_weapon_rank = new_weapon->ai_weapon_rank(); + + TIItemContainer::iterator I = inventory().m_all.begin(); + TIItemContainer::iterator E = inventory().m_all.end(); + for ( ; I != E; ++I) + if (conflicted(*I,new_weapon,new_weapon_enough_ammo,new_weapon_rank)) + return (false); + + return (true); +} + +void CAI_Stalker::remove_personal_only_ammo (const CInventoryItem *item) +{ + const CWeapon *weapon = smart_cast(item); + VERIFY (weapon); + + xr_vector::const_iterator I = weapon->m_ammoTypes.begin(); + xr_vector::const_iterator E = weapon->m_ammoTypes.end(); + for ( ; I != E; ++I) { + bool found = false; + + TIItemContainer::const_iterator i = inventory().m_all.begin(); + TIItemContainer::const_iterator e = inventory().m_all.end(); + for ( ; i != e; ++i) { + if ((*i)->object().ID() == weapon->ID()) + continue; + + const CWeapon *temp = smart_cast(*i); + if (!temp) + continue; + + if (std::find(temp->m_ammoTypes.begin(),temp->m_ammoTypes.end(),*I) == temp->m_ammoTypes.end()) + continue; + + found = true; + break; + } + + if (found) + continue; + + for (i = inventory().m_all.begin(); i != e; ++i) { + if (xr_strcmp(*I,(*i)->object().cNameSect())) + continue; + + NET_Packet packet; + u_EventGen (packet,GE_DESTROY,(*i)->object().ID()); + u_EventSend (packet); + } + } +} + +void CAI_Stalker::update_conflicted (CInventoryItem *item, const CWeapon *new_weapon) +{ + if (non_conflicted(item,new_weapon)) + return; + + remove_personal_only_ammo (item); + item->SetDropManual (TRUE); +} + +void CAI_Stalker::on_after_take (const CGameObject *object) +{ + if (!g_Alive()) + return; + + if (!READ_IF_EXISTS(pSettings,r_bool,cNameSect(),"use_single_item_rule",true)) + return; + + const CWeapon *new_weapon = smart_cast(object); + if (!new_weapon) + return; + + TIItemContainer::iterator I = inventory().m_all.begin(); + TIItemContainer::iterator E = inventory().m_all.end(); + for ( ; I != E; ++I) + update_conflicted (*I,new_weapon); +} diff --git a/src/xrGameLA/alife_abstract_registry.h b/src/xrGameLA/alife_abstract_registry.h new file mode 100644 index 000000000..3f47cc01f --- /dev/null +++ b/src/xrGameLA/alife_abstract_registry.h @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_abstract_registry.h +// Created : 30.06.2004 +// Modified : 30.06.2004 +// Author : Dmitriy Iassenev +// Description : ALife abstract registry +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "object_interfaces.h" +#include "object_broker.h" + +template +class CALifeAbstractRegistry : public IPureSerializeObject { +public: + typedef xr_map<_index_type,_data_type> OBJECT_REGISTRY; + typedef typename OBJECT_REGISTRY::iterator iterator; + typedef typename OBJECT_REGISTRY::const_iterator const_iterator; + +protected: + OBJECT_REGISTRY m_objects; + +public: + typedef typename _data_type _data; + + IC CALifeAbstractRegistry (); + virtual ~CALifeAbstractRegistry (); + virtual void save (IWriter &memory_stream); + virtual void load (IReader &file_stream); + IC const OBJECT_REGISTRY &objects () const; + IC void add (const _index_type &index, _data_type &data, bool no_assert = false); + IC void remove (const _index_type &index, bool no_assert = false); + IC _data_type *object (const _index_type &index, bool no_assert = false); +}; + +#include "alife_abstract_registry_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_abstract_registry_inline.h b/src/xrGameLA/alife_abstract_registry_inline.h new file mode 100644 index 000000000..086bba6d8 --- /dev/null +++ b/src/xrGameLA/alife_abstract_registry_inline.h @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_abstract_registry_inline.h +// Created : 30.06.2004 +// Modified : 30.06.2004 +// Author : Dmitriy Iassenev +// Description : ALife abstract registry inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#define TEMPLATE_SPECIALIZATION template +#define CSALifeAbstractRegistry CALifeAbstractRegistry<_index_type,_data_type> + +TEMPLATE_SPECIALIZATION +IC CSALifeAbstractRegistry::CALifeAbstractRegistry () +{ +} + +TEMPLATE_SPECIALIZATION +CSALifeAbstractRegistry::~CALifeAbstractRegistry () +{ + delete_data (m_objects); +} + +TEMPLATE_SPECIALIZATION +void CSALifeAbstractRegistry::save (IWriter &memory_stream) +{ + save_data (m_objects,memory_stream); +} + +TEMPLATE_SPECIALIZATION +void CSALifeAbstractRegistry ::load (IReader &file_stream) +{ + load_data (m_objects,file_stream); +} + +TEMPLATE_SPECIALIZATION +IC const typename CSALifeAbstractRegistry::OBJECT_REGISTRY &CSALifeAbstractRegistry::objects () const +{ + return (m_objects); +} + +TEMPLATE_SPECIALIZATION +IC void CSALifeAbstractRegistry::add (const _index_type &index, _data_type &data, bool no_assert) +{ + const_iterator I = objects().find(index); + if (I != objects().end()) { + THROW2 (no_assert,"Specified object has been already found in the specified registry!"); + return; + } + m_objects.insert(std::make_pair(index,data)); +} + +TEMPLATE_SPECIALIZATION +IC void CSALifeAbstractRegistry::remove (const _index_type &index, bool no_assert) +{ + iterator I = m_objects.find(index); + if (I == objects().end()) { + THROW2 (no_assert,"Specified object hasn't been found in the specified registry!"); + return; + } + m_objects.erase (I); +} + +TEMPLATE_SPECIALIZATION +IC _data_type *CSALifeAbstractRegistry::object (const _index_type &index, bool no_assert) +{ + iterator I = m_objects.find(index); + if (I == objects().end()) { + THROW2 (no_assert,"Specified object hasn't been found in the specified registry!"); + return (0); + } + return (&(*I).second); +} + +#undef TEMPLATE_SPECIALIZATION \ No newline at end of file diff --git a/src/xrGameLA/alife_anomalous_zone.cpp b/src/xrGameLA/alife_anomalous_zone.cpp new file mode 100644 index 000000000..1566c3cf1 --- /dev/null +++ b/src/xrGameLA/alife_anomalous_zone.cpp @@ -0,0 +1,158 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_anomalous_zone.cpp +// Created : 27.10.2005 +// Modified : 10.05.2017 tatarinrafa +// Author : Dmitriy Iassenev +// Description : ALife anomalous zone class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_time_manager.h" +#include "alife_spawn_registry.h" +#include "alife_graph_registry.h" + +#pragma warning(push) +#pragma warning(disable:4995) +#include +#pragma warning(pop) + +u32 newgameartscount = 0; + +CSE_ALifeItemWeapon *CSE_ALifeAnomalousZone::tpfGetBestWeapon(ALife::EHitType &tHitType, float &fHitPower) +{ + m_tpCurrentBestWeapon = 0; + m_tTimeID = ai().alife().time_manager().game_time(); + fHitPower = m_maxPower; + tHitType = m_tHitType; + return (m_tpCurrentBestWeapon); +} + +ALife::EMeetActionType CSE_ALifeAnomalousZone::tfGetActionType(CSE_ALifeSchedulable *tpALifeSchedulable, int iGroupIndex, bool bMutualDetection) +{ + return (ALife::eMeetActionTypeAttack); +} + +bool CSE_ALifeAnomalousZone::bfActive() +{ + return (fis_zero(m_maxPower,EPS_L) || !interactive()); +} + +CSE_ALifeDynamicObject *CSE_ALifeAnomalousZone::tpfGetBestDetector() +{ + VERIFY2 (false,"This function shouldn't be called"); + NODEFAULT; +#ifdef DEBUG + return (0); +#endif +} +#pragma todo("need to update that function for ability to create many shapes with different sizes") +void CSE_ALifeAnomalousZone::add_shape_size (float zone_radius) +{ + CShapeData::shape_def _shapes; + _shapes.data.sphere.P.set (0.0f,0.0f,0.0f); + _shapes.data.sphere.R = zone_radius; + _shapes.type = CShapeData::cfSphere; + assign_shapes (&_shapes, u32(1)); +} + +void CSE_ALifeAnomalousZone::spawn_artefacts () +{ +//---- Можно ли спавнить из-за аи сетки? + VERIFY2 (!m_bOnline,"Cannot spawn artefacts in online!"); + + if (this->m_flags.is(flUsedAI_Locations) == FALSE) { + #ifdef _DEBUG + Msg("No AI-MAP for object [%d] in game.spawn", this); + #endif + return; + } +//---- Инит + BOOL m_bSpawnArtefact = !!READ_IF_EXISTS(pSettings, r_bool, name(), "art_onstart_spawn", FALSE); + if (!m_bSpawnArtefact) return; + float m_fArtefactSpawnProbability = READ_IF_EXISTS(pSettings, r_float, name(), "art_onstart_spawn_prob", 0.0); + u8 m_iArtSpawnCicles = READ_IF_EXISTS(pSettings, r_u8, name(), "art_onstart_spawn_cicles", 2); +//---- Инит списка артифактов + struct ARTEFACT_SPAWN + { + shared_str section; + float probability; + }; + DEFINE_VECTOR(ARTEFACT_SPAWN, ARTEFACT_SPAWN_VECTOR, ARTEFACT_SPAWN_IT); + ARTEFACT_SPAWN_VECTOR m_ArtefactSpawn; + + LPCSTR l_caParameters = READ_IF_EXISTS(pSettings, r_string, name(), "artefacts", ""); + u16 m_wItemCount = (u16)_GetItemCount(l_caParameters); +R_ASSERT2(!(m_wItemCount & 1), "Invalid number of parameters in string 'artefacts' in the 'system.ltx'!"); + m_wItemCount >>= 1; + + m_ArtefactSpawn.clear(); + string512 l_caBuffer; + float total_probability = 0.f; + m_ArtefactSpawn.resize(m_wItemCount); + for (u16 i = 0; i %s, total arts = %u", name(), m_ArtefactSpawn[i].section.c_str(), newgameartscount); + Fvector pos = position(); + CSE_Abstract* l_tpSE_Abstract = alife().spawn_item(*m_ArtefactSpawn[i].section, pos, m_tNodeID, m_tGraphID, 0xffff); + +//---- Alife shit + R_ASSERT3(l_tpSE_Abstract, "Can't spawn artefact ", m_ArtefactSpawn[i].section.c_str()); + + CSE_ALifeDynamicObject* oALifeDynamicObject = smart_cast(l_tpSE_Abstract); + + R_ASSERT2(oALifeDynamicObject, "Non-ALife object in the 'game.spawn'"); + + oALifeDynamicObject->m_tSpawnID = m_tSpawnID; + oALifeDynamicObject->m_bALifeControl = true; + ai().alife().spawns().assign_artefact_position(this, oALifeDynamicObject); + + Fvector t = oALifeDynamicObject->o_Position; + u32 p = oALifeDynamicObject->m_tNodeID; + float q = oALifeDynamicObject->m_fDistance; + alife().graph().change(oALifeDynamicObject, m_tGraphID, oALifeDynamicObject->m_tGraphID); + oALifeDynamicObject->o_Position = t; + oALifeDynamicObject->m_tNodeID = p; + oALifeDynamicObject->m_fDistance = q; + + } + } +} + +void CSE_ALifeAnomalousZone::on_spawn () +{ + inherited::on_spawn (); + spawn_artefacts (); +} diff --git a/src/xrGameLA/alife_combat_manager.cpp b/src/xrGameLA/alife_combat_manager.cpp new file mode 100644 index 000000000..6522534e5 --- /dev/null +++ b/src/xrGameLA/alife_combat_manager.cpp @@ -0,0 +1,474 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_combat_manager.h +// Created : 12.08.2003 +// Modified : 14.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife combat manager +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_combat_manager.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "alife_graph_registry.h" +#include "alife_schedule_registry.h" +/** +#include "ef_storage.h" +#include "ef_pattern.h" +#include "alife_object_registry.h" +#include "alife_spawn_registry.h" +#include "alife_time_manager.h" +#include "relation_registry.h" + +using namespace ALife; + +#define NORMALIZE_VARIABLE(a,b,c,d) a = u32(b % c) + d, b /= c; + +void print_time(LPCSTR S, _TIME_ID tTimeID) +{ + u32 Milliseconds,Seconds,Minutes,Hours,Days,Week,Months,Years; + NORMALIZE_VARIABLE(Milliseconds,tTimeID,1000,0); + NORMALIZE_VARIABLE(Seconds, tTimeID, 60,0); + NORMALIZE_VARIABLE(Minutes, tTimeID, 60,0); + NORMALIZE_VARIABLE(Hours, tTimeID, 24,0); + NORMALIZE_VARIABLE(Days, tTimeID, 7,1); + NORMALIZE_VARIABLE(Week, tTimeID, 4,1); + NORMALIZE_VARIABLE(Months, tTimeID, 12,1); + Years = u32(tTimeID) + 1; + Msg("%s year %d month %d week %d day %d time %d:%d:%d.%d",S,Years,Months,Week,Days,Hours,Minutes,Seconds,Milliseconds); +} + +/**/ +CALifeCombatManager::CALifeCombatManager (xrServer *server, LPCSTR section) : + CALifeSimulatorBase (server,section) +{ +/** + seed (u32(CPU::QPC() & 0xffffffff)); + m_dwMaxCombatIterationCount = pSettings->r_u32 (section,"max_combat_iteration_count"); + for (int i=0; i<2; ++i) { + m_tpaCombatGroups[i].clear(); + m_tpaCombatGroups[i].reserve(255); + } +/**/ +} + +/** +CALifeCombatManager::~CALifeCombatManager () +{ +} + +void CALifeCombatManager::vfFillCombatGroup(CSE_ALifeSchedulable *tpALifeSchedulable, int iGroupIndex) +{ + EHitType l_tHitType; + float l_fHitPower; + SCHEDULE_P_VECTOR &tpGroupVector = m_tpaCombatGroups[iGroupIndex]; + CSE_ALifeGroupAbstract *l_tpALifeGroupAbstract = smart_cast(tpALifeSchedulable); + tpGroupVector.clear (); + if (l_tpALifeGroupAbstract) { + OBJECT_IT I = l_tpALifeGroupAbstract->m_tpMembers.begin(); + OBJECT_IT E = l_tpALifeGroupAbstract->m_tpMembers.end(); + for ( ; I != E; ++I) { + CSE_ALifeSchedulable *l_tpALifeSchedulable = smart_cast(objects().object(*I)); + R_ASSERT2 (l_tpALifeSchedulable,"Invalid combat object"); + tpGroupVector.push_back(l_tpALifeSchedulable); + l_tpALifeSchedulable->tpfGetBestWeapon(l_tHitType,l_fHitPower); + } + } + else { + tpGroupVector.push_back(tpALifeSchedulable); + tpALifeSchedulable->tpfGetBestWeapon(l_tHitType,l_fHitPower); + } + m_tpaCombatObjects[iGroupIndex] = tpALifeSchedulable; +} + +ECombatAction CALifeCombatManager::choose_combat_action(int iCombatGroupIndex) +{ + ai().ef_storage().alife_evaluation(true); + if (eCombatTypeMonsterMonster != combat_type()) + if (((eCombatTypeAnomalyMonster == combat_type()) && !iCombatGroupIndex) || ((eCombatTypeMonsterAnomaly == combat_type()) && iCombatGroupIndex)) + return(eCombatActionAttack); + else + return(eCombatActionRetreat); + + SCHEDULE_P_VECTOR &Members = m_tpaCombatGroups[iCombatGroupIndex]; + SCHEDULE_P_VECTOR &Enemies = m_tpaCombatGroups[iCombatGroupIndex ^ 1]; + int i = 0, j = 0, I = (int)Members.size(), J = (int)Enemies.size(); + float fMinProbability; + if (!I) + fMinProbability = 0; + else { + CSE_ALifeMonsterAbstract *l_tpALifeMonsterAbstract = smart_cast(Members[0]); + R_ASSERT2 (l_tpALifeMonsterAbstract,"Invalid combat object"); + fMinProbability = l_tpALifeMonsterAbstract->m_fRetreatThreshold; + } + while ((i < I) && (j < J)) { + ai().ef_storage().alife().member() = smart_cast(Members[i]); + ai().ef_storage().alife().enemy() = smart_cast(Enemies[j]); + float fProbability = ai().ef_storage().m_pfVictoryProbability->ffGetValue()/100.f, fCurrentProbability; + if (fProbability > fMinProbability) { + fCurrentProbability = fProbability; + for (++j; (i < I) && (j < J); ++j) { + ai().ef_storage().alife().enemy() = smart_cast(Enemies[j]); + fProbability = ai().ef_storage().m_pfVictoryProbability->ffGetValue()/100.f; + if (fCurrentProbability*fProbability < fMinProbability) { + ++i; + break; + } + else + fCurrentProbability *= fProbability; + } + } + else { + fCurrentProbability = 1.0f - fProbability; + for (++i; (i < I) && (j < J); ++i) { + ai().ef_storage().alife().member() = smart_cast(Members[i]); + fProbability = 1.0f - ai().ef_storage().m_pfVictoryProbability->ffGetValue()/100.f; + if (fCurrentProbability*fProbability < fMinProbability) { + ++j; + break; + } + else + fCurrentProbability *= fProbability; + } + } + } + return((j >= J) ? eCombatActionAttack : eCombatActionRetreat); +} + +bool CALifeCombatManager::bfCheckObjectDetection(CSE_ALifeSchedulable *tpALifeSchedulable1, CSE_ALifeSchedulable *tpALifeSchedulable2) +{ + ai().ef_storage().alife_evaluation(true); + switch (combat_type()) { + case eCombatTypeMonsterMonster : { + ai().ef_storage().alife().member_item() = smart_cast(tpALifeSchedulable1); + ai().ef_storage().alife().member() = tpALifeSchedulable1; + ai().ef_storage().alife().enemy() = tpALifeSchedulable2; + return (randF(100) < ai().ef_storage().m_pfEnemyDetectProbability->ffGetValue()); + } + case eCombatTypeAnomalyMonster : { + ai().ef_storage().alife().enemy() = tpALifeSchedulable1; + return (randF(100) < ai().ef_storage().m_pfAnomalyInteractProbability->ffGetValue()); + } + case eCombatTypeMonsterAnomaly : { + ai().ef_storage().alife().member_item() = tpALifeSchedulable1->tpfGetBestDetector(); + ai().ef_storage().alife().member() = tpALifeSchedulable1; + ai().ef_storage().alife().enemy() = tpALifeSchedulable2; + return (randF(100) < ai().ef_storage().m_pfAnomalyDetectProbability->ffGetValue()); + } + case eCombatTypeSmartTerrain : { + CSE_ALifeSmartZone *smart_zone = smart_cast(tpALifeSchedulable1); + return (!smart_zone ? false : randF(100) < 100.f*smart_zone->detect_probability()); + } + default : NODEFAULT; + } +#ifdef DEBUG + return (false); +#endif // DEBUG +} + +bool CALifeCombatManager::bfCheckForInteraction(CSE_ALifeSchedulable *tpALifeSchedulable1, CSE_ALifeSchedulable *tpALifeSchedulable2, int &iCombatGroupIndex, bool &bMutualDetection) +{ + if (!tpALifeSchedulable1->bfActive() || !tpALifeSchedulable2->bfActive()) + return(false); + + // determine combat type + CSE_ALifeMonsterAbstract *l_tpALifeMonsterAbstract1 = smart_cast(tpALifeSchedulable1); + CSE_ALifeMonsterAbstract *l_tpALifeMonsterAbstract2 = smart_cast(tpALifeSchedulable2); + if (!l_tpALifeMonsterAbstract1) { + if (!l_tpALifeMonsterAbstract2) + return(false); + else { + CSE_ALifeCustomZone *l_tpALifeSpaceRestrictor = smart_cast(tpALifeSchedulable2); + R_ASSERT2 (l_tpALifeSpaceRestrictor,"Unknown schedulable object class"); + m_combat_type = eCombatTypeAnomalyMonster; + } + } + else { + if (!l_tpALifeMonsterAbstract2) { + CSE_ALifeCustomZone *l_tpALifeSpaceRestrictor = smart_cast(tpALifeSchedulable2); + if (!l_tpALifeSpaceRestrictor) { + CSE_ALifeSmartZone *smart_zone = smart_cast(tpALifeSchedulable2); + if (smart_zone) + m_combat_type = eCombatTypeSmartTerrain; + else + R_ASSERT2 (false,"Unknown schedulable object class"); + } + else + m_combat_type = eCombatTypeMonsterAnomaly; + } + else { + m_combat_type = eCombatTypeMonsterMonster; + if (eRelationTypeFriend == relation_type(l_tpALifeMonsterAbstract1,l_tpALifeMonsterAbstract2)) { + CSE_ALifeHumanAbstract *l_tpALifeHumanAbstract1 = smart_cast(l_tpALifeMonsterAbstract1); + CSE_ALifeHumanAbstract *l_tpALifeHumanAbstract2 = smart_cast(l_tpALifeMonsterAbstract2); + if (l_tpALifeHumanAbstract1 && l_tpALifeHumanAbstract2) { + iCombatGroupIndex = 0; + return(true); + } + else + return(false); + } + } + } + + // perform interaction +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + GameGraph::_GRAPH_ID l_tGraphID = l_tpALifeMonsterAbstract1 ? l_tpALifeMonsterAbstract1->m_tGraphID : l_tpALifeMonsterAbstract2->m_tGraphID; + print_time ("\n[LSS]",time_manager().game_time()); + Msg ("[LSS] %s met %s on the graph point %d (level %s[%d][%d][%d][%d], [%f][%f][%f])", + tpALifeSchedulable1->base()->name_replace(), + tpALifeSchedulable2->base()->name_replace(), + l_tGraphID, + *ai().game_graph().header().levels().find(ai().game_graph().vertex(l_tGraphID)->level_id())->second.name(), + ai().game_graph().vertex(l_tGraphID)->vertex_type()[0], + ai().game_graph().vertex(l_tGraphID)->vertex_type()[1], + ai().game_graph().vertex(l_tGraphID)->vertex_type()[2], + ai().game_graph().vertex(l_tGraphID)->vertex_type()[3], + VPUSH(ai().game_graph().vertex(l_tGraphID)->level_point()) + ); + } +#endif + + bMutualDetection = false; + iCombatGroupIndex = -1; + + if (bfCheckObjectDetection(tpALifeSchedulable1,tpALifeSchedulable2)) { + iCombatGroupIndex = 0; +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s detected %s",tpALifeSchedulable1->base()->name_replace(),tpALifeSchedulable2->base()->name_replace()); + } +#endif + } + else { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s didn't detect %s",tpALifeSchedulable1->base()->name_replace(),tpALifeSchedulable2->base()->name_replace()); + } +#endif + } + + if (eCombatTypeMonsterAnomaly == combat_type()) + m_combat_type = eCombatTypeAnomalyMonster; + else + if (eCombatTypeAnomalyMonster == combat_type()) + m_combat_type = eCombatTypeMonsterAnomaly; + + if (bfCheckObjectDetection(tpALifeSchedulable2,tpALifeSchedulable1)) { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s detected %s",tpALifeSchedulable2->base()->name_replace(),tpALifeSchedulable1->base()->name_replace()); + } +#endif + if (!iCombatGroupIndex) + bMutualDetection = true; + else + iCombatGroupIndex = 1; + } + else { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s didn't detect %s",tpALifeSchedulable2->base()->name_replace(),tpALifeSchedulable1->base()->name_replace()); + } +#endif + } + + if (eCombatTypeMonsterAnomaly == combat_type()) + m_combat_type = eCombatTypeAnomalyMonster; + else + if (eCombatTypeAnomalyMonster == combat_type()) + m_combat_type = eCombatTypeMonsterAnomaly; + + if (iCombatGroupIndex < 0) { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] There is no interaction"); + } +#endif + return (false); + } + else + return (true); +} + +bool CALifeCombatManager::bfCheckIfRetreated(int iCombatGroupIndex) +{ + ai().ef_storage().alife_evaluation(true); + ai().ef_storage().alife().member_item() = (eCombatTypeMonsterMonster == combat_type()) ? smart_cast(m_tpaCombatGroups[iCombatGroupIndex][0]) : m_tpaCombatGroups[iCombatGroupIndex][0]->m_tpBestDetector; + ai().ef_storage().alife().member() = m_tpaCombatGroups[iCombatGroupIndex][0]; + ai().ef_storage().alife().enemy() = m_tpaCombatGroups[iCombatGroupIndex ^ 1][0]; + return (randF(100) < ((eCombatTypeMonsterMonster != combat_type()) ? ai().ef_storage().m_pfAnomalyRetreatProbability->ffGetValue() : ai().ef_storage().m_pfEnemyRetreatProbability->ffGetValue())); +} + +void CALifeCombatManager::vfPerformAttackAction(int iCombatGroupIndex) +{ + ai().ef_storage().alife_evaluation(true); + SCHEDULE_P_VECTOR &l_tCombatGroup = m_tpaCombatGroups[iCombatGroupIndex]; + SCHEDULE_P_IT I = l_tCombatGroup.begin(); + SCHEDULE_P_IT E = l_tCombatGroup.end(); + for ( ; I != E; ++I) { + EHitType l_tHitType = eHitTypeMax; + float l_fHitPower = 0.f; + if (!(*I)->m_tpCurrentBestWeapon) { + CSE_ALifeItemWeapon *l_tpALifeItemWeapon = (*I)->tpfGetBestWeapon(l_tHitType,l_fHitPower); + if (!l_tpALifeItemWeapon && (l_fHitPower <= EPS_L)) + continue; + } + else { + l_tHitType = (*I)->m_tpCurrentBestWeapon->m_tHitType; + l_fHitPower = (*I)->m_tpCurrentBestWeapon->m_fHitPower; + } + + ai().ef_storage().alife().member_item() = smart_cast(*I); + ai().ef_storage().alife().member() = *I; +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s attacks with %s(%d ammo) %d times in a row",(*I)->base()->name_replace(),(*I)->m_tpCurrentBestWeapon ? (*I)->m_tpCurrentBestWeapon->name_replace() : "its natural weapon",(*I)->m_tpCurrentBestWeapon ? (*I)->m_tpCurrentBestWeapon->m_dwAmmoAvailable : 0,iFloor(ai().ef_storage().m_pfWeaponAttackTimes->ffGetValue() + .5f)); + } +#endif + for (int i=0, n=iFloor(ai().ef_storage().m_pfWeaponAttackTimes->ffGetValue() + .5f); iffGetValue()) { + // choose random enemy group member and perform hit with random power + // multiplied by immunity factor + int l_iIndex = randI(m_tpaCombatGroups[iCombatGroupIndex ^ 1].size()); + CSE_ALifeMonsterAbstract *l_tpALifeMonsterAbstract = smart_cast(m_tpaCombatGroups[iCombatGroupIndex ^ 1][l_iIndex]); + R_ASSERT2 (l_tpALifeMonsterAbstract,"Invalid combat object"); + float l_fHit = randF(l_fHitPower*0.5f,l_fHitPower*1.5f); + l_tpALifeMonsterAbstract->fHealth -= l_tpALifeMonsterAbstract->m_fpImmunityFactors[l_tHitType]*l_fHit; +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s %s %s [power %5.2f][damage %5.2f][health %5.2f][creatures left %d]",(*I)->base()->name_replace(),l_tpALifeMonsterAbstract->fHealth <= 0 ? "killed" : "attacked",l_tpALifeMonsterAbstract->name_replace(),l_fHit,l_tpALifeMonsterAbstract->m_fpImmunityFactors[l_tHitType]*l_fHit,_max(l_tpALifeMonsterAbstract->fHealth,0.f),l_tpALifeMonsterAbstract->fHealth >= EPS_L ? m_tpaCombatGroups[iCombatGroupIndex ^ 1].size() : m_tpaCombatGroups[iCombatGroupIndex ^ 1].size() - 1); + } +#endif + // check if victim became dead + if (l_tpALifeMonsterAbstract->fHealth <= 0) { + m_tpaCombatGroups[iCombatGroupIndex ^ 1].erase(m_tpaCombatGroups[iCombatGroupIndex ^ 1].begin() + l_iIndex); + if (m_tpaCombatGroups[iCombatGroupIndex ^ 1].empty()) + return; + } + } + else { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s missed",(*I)->base()->name_replace()); + } +#endif + } + // perform attack (if we use a weapon we should delete ammo we used) + if (!(*I)->bfPerformAttack()) + break; + } + } +} + +void CALifeCombatManager::vfFinishCombat(ECombatResult tCombatResult) +{ + // processing weapons and dead monsters + CSE_ALifeDynamicObject *l_tpALifeDynamicObject = smart_cast(m_tpaCombatObjects[0]); + R_ASSERT2 (l_tpALifeDynamicObject,"Unknown schedulable object class"); + GameGraph::_GRAPH_ID l_tGraphID = l_tpALifeDynamicObject->m_tGraphID; + m_temp_item_vector.clear(); + for (int i=0; i<2; ++i) { + CSE_ALifeGroupAbstract *l_tpALifeGroupAbstract = smart_cast(m_tpaCombatObjects[i]); + if (l_tpALifeGroupAbstract) { + for (int I=0, N=l_tpALifeGroupAbstract->m_tpMembers.size() ; I(objects().object(l_tpALifeGroupAbstract->m_tpMembers[I])); + R_ASSERT2 (l_tpALifeMonsterAbstract,"Invalid group member!"); + l_tpALifeMonsterAbstract->vfUpdateWeaponAmmo (); + if (l_tpALifeMonsterAbstract->fHealth <= EPS_L) { + append_item_vector (l_tpALifeMonsterAbstract->children,m_temp_item_vector); + l_tpALifeMonsterAbstract->m_bDirectControl = true; + l_tpALifeGroupAbstract->m_tpMembers.erase (l_tpALifeGroupAbstract->m_tpMembers.begin() + I); + assign_death_position (l_tpALifeMonsterAbstract, l_tGraphID, m_tpaCombatObjects[i ^ 1]); + l_tpALifeMonsterAbstract->vfDetachAll (); + R_ASSERT (l_tpALifeMonsterAbstract->children.empty()); + register_object (l_tpALifeMonsterAbstract); + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast(l_tpALifeMonsterAbstract); + if (l_tpALifeInventoryItem) + m_temp_item_vector.push_back (l_tpALifeInventoryItem); + --l_tpALifeGroupAbstract->m_wCount; + --I; + --N; + } + } + } + else { + m_tpaCombatObjects[i]->vfUpdateWeaponAmmo (); + CSE_ALifeMonsterAbstract *l_tpALifeMonsterAbstract = smart_cast(m_tpaCombatObjects[i]); + if (l_tpALifeMonsterAbstract && (l_tpALifeMonsterAbstract->fHealth <= EPS_L)) { + kill_entity (l_tpALifeMonsterAbstract,l_tGraphID,m_tpaCombatObjects[i ^ 1]); + } + } + } + + if (m_temp_item_vector.empty() || (eCombatTypeMonsterMonster != combat_type())) { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] There is nothing to take"); + } +#endif + return; + } + + int l_iGroupIndex = -1; + switch (tCombatResult) { + case eCombatResultBothKilled : + case eCombatResultRetreat12 : break; + case eCombatResultRetreat2 : + case eCombatResult1Kill2 : { + l_iGroupIndex = 0; + break; + } + case eCombatResultRetreat1 : + case eCombatResult2Kill1 : { + l_iGroupIndex = 1; + break; + } + default : NODEFAULT; + } + + if (l_iGroupIndex >= 0) { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] Starting taking items [%s][%f]",m_tpaCombatObjects[l_iGroupIndex]->base()->name_replace(),smart_cast(m_tpaCombatObjects[l_iGroupIndex])->fHealth); + } +#endif + m_tpaCombatObjects[l_iGroupIndex]->vfAttachItems(); + } + m_temp_item_vector.clear(); +} + +ALife::ERelationType CALifeCombatManager::relation_type (CSE_ALifeMonsterAbstract *tpALifeMonsterAbstract1, CSE_ALifeMonsterAbstract *tpALifeMonsterAbstract2) const +{ + CSE_ALifeTraderAbstract* human1 = smart_cast(tpALifeMonsterAbstract1); + CSE_ALifeTraderAbstract* human2 = smart_cast(tpALifeMonsterAbstract2); + + if(human1 && human2) + { + ALife::ERelationType rel = RELATION_REGISTRY().GetRelationBetween(human1, human2); + return rel; + } + + if (tpALifeMonsterAbstract1->g_team() != tpALifeMonsterAbstract2->g_team()) + return(ALife::eRelationTypeEnemy); + else + return(ALife::eRelationTypeNeutral); +} +/**/ +void CALifeCombatManager::kill_entity (CSE_ALifeMonsterAbstract *l_tpALifeMonsterAbstract, const GameGraph::_GRAPH_ID &l_tGraphID, CSE_ALifeSchedulable *schedulable) +{ + VERIFY (l_tpALifeMonsterAbstract->g_Alive()); + append_item_vector (l_tpALifeMonsterAbstract->children,m_temp_item_vector); + GameGraph::_GRAPH_ID l_tGraphID1 = l_tpALifeMonsterAbstract->m_tGraphID; + assign_death_position (l_tpALifeMonsterAbstract, l_tGraphID, schedulable); + l_tpALifeMonsterAbstract->vfDetachAll (); + R_ASSERT (l_tpALifeMonsterAbstract->children.empty()); + scheduled().remove (l_tpALifeMonsterAbstract); + if (l_tpALifeMonsterAbstract->m_tGraphID != l_tGraphID1) { + graph().remove (l_tpALifeMonsterAbstract,l_tGraphID1); + graph().add (l_tpALifeMonsterAbstract,l_tpALifeMonsterAbstract->m_tGraphID); + } + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast(l_tpALifeMonsterAbstract); + if (l_tpALifeInventoryItem) + m_temp_item_vector.push_back (l_tpALifeInventoryItem); +} diff --git a/src/xrGameLA/alife_combat_manager.h b/src/xrGameLA/alife_combat_manager.h new file mode 100644 index 000000000..66a9629d6 --- /dev/null +++ b/src/xrGameLA/alife_combat_manager.h @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_combat_manager.h +// Created : 12.08.2003 +// Modified : 14.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife combat manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "alife_simulator_base.h" +//#include "game_graph_space.h" + +class CALifeCombatManager : public virtual CALifeSimulatorBase, CRandom { +/** +protected: + typedef CALifeSimulatorBase inherited; + +protected: + u32 m_dwMaxCombatIterationCount; + ALife::ECombatType m_combat_type; + + // temporary buffers for combats + CSE_ALifeSchedulable *m_tpaCombatObjects[2]; + ALife::D_OBJECT_P_MAP m_tpGraphPointObjects; + +public: + ALife::ITEM_P_VECTOR m_tpTempItemBuffer; + +protected: + void vfFillCombatGroup (CSE_ALifeSchedulable *tpALifeSchedulable, int iGroupIndex); + bool bfCheckObjectDetection (CSE_ALifeSchedulable *tpALifeSchedulable1, CSE_ALifeSchedulable *tpALifeSchedulable2); + bool bfCheckForInteraction (CSE_ALifeSchedulable *tpALifeSchedulable1, CSE_ALifeSchedulable *tpALifeSchedulable2, int &iCombatGroupIndex, bool &bMutualDetection); + void vfPerformAttackAction (int iCombatGroupIndex); + bool bfCheckIfRetreated (int iCombatGroupIndex); + void vfFinishCombat (ALife::ECombatResult tCombatResult); +/**/ +public: + CALifeCombatManager (xrServer *server, LPCSTR section); +/** + virtual ~CALifeCombatManager (); + IC ALife::ECombatType combat_type () const; + ALife::ECombatAction choose_combat_action (int iCombatGroupIndex); + ALife::ERelationType relation_type (CSE_ALifeMonsterAbstract *tpALifeMonsterAbstract1, CSE_ALifeMonsterAbstract*tpALifeMonsterAbstract2) const; +/**/ + void kill_entity (CSE_ALifeMonsterAbstract *l_tpALifeMonsterAbstract, const GameGraph::_GRAPH_ID &l_tGraphID, CSE_ALifeSchedulable *schedulable); +}; + +#include "alife_combat_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_combat_manager_inline.h b/src/xrGameLA/alife_combat_manager_inline.h new file mode 100644 index 000000000..9788be69c --- /dev/null +++ b/src/xrGameLA/alife_combat_manager_inline.h @@ -0,0 +1,16 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_combat_manager_inline.h +// Created : 12.08.2003 +// Modified : 14.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife combat manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +/** +IC ALife::ECombatType CALifeCombatManager::combat_type () const +{ + return (m_combat_type); +} +/**/ diff --git a/src/xrGameLA/alife_communication_manager.cpp b/src/xrGameLA/alife_communication_manager.cpp new file mode 100644 index 000000000..1d099da4d --- /dev/null +++ b/src/xrGameLA/alife_communication_manager.cpp @@ -0,0 +1,905 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_communication_manager.cpp +// Created : 03.09.2003 +// Modified : 14.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife communication manager +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_communication_manager.h" + +/** +#include "alife_communication_space.h" +#include "xrServer_objects_ALife_All.h" +#include "alife_object_registry.h" +#include "alife_graph_registry.h" +#include "ai_debug.h" +#include "alife_human_brain.h" +#include "alife_human_object_handler.h" + +#pragma warning(push) +#pragma warning(disable:4995) +#include +#pragma warning(pop) + +using namespace ALife; +using namespace ALifeCommunication; + +#define KEEP_SORTED + +#define PUSH_STACK(a,b,c,e,f) {\ + (e)[(f)].i1 = (a);\ + (e)[(f)].i2 = (b);\ + (e)[(f)].iCurrentSum = (c);\ + ++(f);\ +} + +#define POP_STACK(a,b,c,e,f) {\ + --(f);\ + (a) = (e)[(f)].i1;\ + (b) = (e)[(f)].i2;\ + (c) = (e)[(f)].iCurrentSum;\ +} + +class CSortItemByValuePredicate { +public: + IC bool operator() (const CSE_ALifeInventoryItem *tpALifeInventoryItem1, const CSE_ALifeInventoryItem *tpALifeInventoryItem2) const + { + return (tpALifeInventoryItem1->m_dwCost < tpALifeInventoryItem2->m_dwCost); + }; +}; + +class CSortByOwnerPredicate { +public: + ALife::_OBJECT_ID m_tParentID; + + CSortByOwnerPredicate (ALife::_OBJECT_ID tParentID) + { + m_tParentID = tParentID; + } + + IC bool operator() (const CSE_ALifeInventoryItem *tpALifeInventoryItem1, const CSE_ALifeInventoryItem *tpALifeInventoryItem2) const + { + if (tpALifeInventoryItem1->m_dwCost == tpALifeInventoryItem2->m_dwCost) + if (tpALifeInventoryItem1->m_tPreviousParentID == m_tParentID) + if (tpALifeInventoryItem2->m_tPreviousParentID == m_tParentID) + return (tpALifeInventoryItem1->base()->ID < tpALifeInventoryItem2->base()->ID); + else + return (true); + else + if (tpALifeInventoryItem2->m_tPreviousParentID == m_tParentID) + return (false); + else + return (tpALifeInventoryItem1->base()->ID < tpALifeInventoryItem2->base()->ID); + else + return (tpALifeInventoryItem1->m_dwCost > tpALifeInventoryItem2->m_dwCost); + } +}; +/**/ + +CALifeCommunicationManager::CALifeCommunicationManager (xrServer *server, LPCSTR section) : + CALifeSimulatorBase (server,section) +{ +// m_tpItems1.reserve (MAX_STACK_DEPTH); +// m_tpItems2.reserve (MAX_STACK_DEPTH); +// m_tpBlockedItems1.reserve (MAX_STACK_DEPTH); +// m_tpBlockedItems2.reserve (MAX_STACK_DEPTH); +// m_tpTrader1.reserve (MAX_STACK_DEPTH); +// m_tpTrader1.reserve (MAX_STACK_DEPTH); +// m_tpSums1.reserve (MAX_STACK_DEPTH); +// m_tpSums2.reserve (MAX_STACK_DEPTH); +} + +/** +CALifeCommunicationManager::~CALifeCommunicationManager () +{ +} + +#ifdef DEBUG +void CALifeCommunicationManager::vfPrintItems(CSE_ALifeHumanAbstract *tpALifeHumanAbstract, ITEM_P_VECTOR &tpItemVector) +{ + Msg ("%s[%d]",tpALifeHumanAbstract->name_replace(),tpALifeHumanAbstract->m_dwMoney); + ITEM_P_IT I = tpItemVector.begin(); + ITEM_P_IT E = tpItemVector.end(); + for ( ; I != E; ++I) + Msg (" %s",(*I)->base()->name_replace()); +} + +void CALifeCommunicationManager::vfPrintItems(CSE_ALifeHumanAbstract *tpALifeHumanAbstract) +{ + Msg ("%s[%d]",tpALifeHumanAbstract->name_replace(),tpALifeHumanAbstract->m_dwMoney); + OBJECT_IT I = tpALifeHumanAbstract->children.begin(); + OBJECT_IT E = tpALifeHumanAbstract->children.end(); + for ( ; I != E; ++I) + Msg (" %s",objects().object(*I)->name_replace()); +} +#endif + +u32 CALifeCommunicationManager::dwfComputeItemCost(ITEM_P_VECTOR &tpItemVector) +{ + u32 l_dwItemCost = 0; + ITEM_P_IT I = tpItemVector.begin(); + ITEM_P_IT E = tpItemVector.end(); + for ( ; I != E; ++I) + l_dwItemCost += (*I)->m_dwCost; + return (l_dwItemCost); +} + +void CALifeCommunicationManager::vfRunFunctionByIndex(CSE_ALifeHumanAbstract *tpALifeHumanAbstract, OBJECT_VECTOR &tpBlockedItems, ITEM_P_VECTOR &tpItems, int i, int &j) +{ + sort (m_temp_item_vector.begin(),m_temp_item_vector.end(),CSortByOwnerPredicate(tpALifeHumanAbstract->ID)); + switch (i) { + case 0 : { + j = tpALifeHumanAbstract->brain().objects().choose_food (&tpBlockedItems); + break; + } + case 1 : { + j = tpALifeHumanAbstract->brain().objects().choose_weapon (eWeaponPriorityTypeKnife,&tpBlockedItems); + break; + } + case 2 : { + j = tpALifeHumanAbstract->brain().objects().choose_weapon (eWeaponPriorityTypeSecondary,&tpBlockedItems); + break; + } + case 3 : { + j = tpALifeHumanAbstract->brain().objects().choose_weapon (eWeaponPriorityTypePrimary,&tpBlockedItems); + break; + } + case 4 : { + j = tpALifeHumanAbstract->brain().objects().choose_weapon (eWeaponPriorityTypeGrenade,&tpBlockedItems); + break; + } + case 5 : { + j = tpALifeHumanAbstract->brain().objects().choose_medikit (&tpBlockedItems); + break; + } + case 6 : { + j = tpALifeHumanAbstract->brain().objects().choose_detector (&tpBlockedItems); + break; + } + case 7 : { + j = tpALifeHumanAbstract->brain().objects().choose_equipment (&tpBlockedItems); + break; + } + default : NODEFAULT; + } +} + +void CALifeCommunicationManager::vfAssignItemParents(CSE_ALifeHumanAbstract *tpALifeHumanAbstract, int iItemCount) +{ + ALife::_OBJECT_ID *temp_children = (ALife::_OBJECT_ID*)alloca(sizeof(ALife::_OBJECT_ID)*iItemCount); + std::copy (tpALifeHumanAbstract->children.begin() + tpALifeHumanAbstract->children.size() - iItemCount,tpALifeHumanAbstract->children.end(),temp_children); + tpALifeHumanAbstract->children.resize(tpALifeHumanAbstract->children.size() - iItemCount); + ALife::_OBJECT_ID *I = temp_children; + ALife::_OBJECT_ID *E = temp_children + iItemCount; + for ( ; I != E; ++I) { + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast(objects().object(*I)); + tpALifeHumanAbstract->attach(l_tpALifeInventoryItem,true,true); + R_ASSERT (tpALifeHumanAbstract->brain().m_dwTotalMoney >= l_tpALifeInventoryItem->m_dwCost); + tpALifeHumanAbstract->brain().m_dwTotalMoney -= l_tpALifeInventoryItem->m_dwCost; + } +} + +void CALifeCommunicationManager::vfAttachOwnerItems(CSE_ALifeHumanAbstract *tpALifeHumanAbstract, ITEM_P_VECTOR &tpItemVector, ITEM_P_VECTOR &tpOwnItems) +{ + ITEM_P_IT I = tpItemVector.begin(); + ITEM_P_IT E = tpItemVector.end(); + for ( ; I != E; ++I) +// if ((std::find(tpOwnItems.begin(),tpOwnItems.end(),*I) != tpOwnItems.end()) && tpALifeHumanAbstract->bfCanGetItem(*I)) + if (std::binary_search(tpOwnItems.begin(),tpOwnItems.end(),*I)) + tpALifeHumanAbstract->attach(*I,true); +} + +int CALifeCommunicationManager::ifComputeBalance(CSE_ALifeHumanAbstract *tpALifeHumanAbstract, ITEM_P_VECTOR &tpItemVector) +{ + int l_iDebt = 0; + OBJECT_VECTOR &l_tpChildren = tpALifeHumanAbstract->children; + ITEM_P_IT I = tpItemVector.begin(); + ITEM_P_IT E = tpItemVector.end(); + for ( ; I != E; ++I) + if (std::find(l_tpChildren.begin(),l_tpChildren.end(),(*I)->base()->ID) != l_tpChildren.end()) + l_iDebt -= (*I)->m_dwCost; + return(l_iDebt); +} + +void CALifeCommunicationManager::vfRestoreItems(CSE_ALifeHumanAbstract *tpALifeHumanAbstract, ITEM_P_VECTOR &tpItemVector) +{ +#ifndef FAST_OWNERSHIP + tpALifeHumanAbstract->vfInitInventory(); +#endif + { + ITEM_P_IT I = tpItemVector.begin(); + ITEM_P_IT E = tpItemVector.end(); +#ifdef FAST_OWNERSHIP + OBJECT_IT i = tpALifeHumanAbstract->children.begin(); +#endif + for ( ; I != E; ++I) { +#ifndef FAST_OWNERSHIP + (*I)->base()->ID_Parent = 0xffff; + graph().attach(*tpALifeHumanAbstract,*I,smart_cast(*I)->m_tGraphID); +#else + *i = (*I)->base()->ID; + ++i; +#endif + } + } +} + +#ifdef FAST_OWNERSHIP +void CALifeCommunicationManager::vfAttachGatheredItems(CSE_ALifeTraderAbstract *tpALifeTraderAbstract1, CSE_ALifeTraderAbstract *tpALifeTraderAbstract2, OBJECT_VECTOR &tpObjectVector) +#else +void CALifeCommunicationManager::vfAttachGatheredItems(CSE_ALifeTraderAbstract *tpALifeTraderAbstract1, OBJECT_VECTOR &tpObjectVector) +#endif +{ + tpObjectVector.clear(); + tpObjectVector.insert(tpObjectVector.end(),tpALifeTraderAbstract1->base()->children.begin(),tpALifeTraderAbstract1->base()->children.end()); + tpALifeTraderAbstract1->base()->children.clear(); + tpALifeTraderAbstract1->vfInitInventory(); + OBJECT_IT I = tpObjectVector.begin(); + OBJECT_IT E = tpObjectVector.end(); + for ( ; I != E; ++I) { +#ifndef FAST_OWNERSHIP + CSE_ALifeDynamicObject *l_tpALifeDynamicObject = objects().object(*I); + l_tpALifeDynamicObject->ID_Parent = 0xffff; + graph().attach (*tpALifeTraderAbstract1->base(),smart_cast(l_tpALifeDynamicObject),l_tpALifeDynamicObject->m_tGraphID); +#else + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast(objects().object(*I)); + if (l_tpALifeInventoryItem->m_tPreviousParentID != tpALifeTraderAbstract1->base()->ID) { + R_ASSERT (l_tpALifeInventoryItem->m_tPreviousParentID == tpALifeTraderAbstract2->base()->ID); +// tpALifeTraderAbstract2->detach (l_tpALifeInventoryItem,0,false); + } + tpALifeTraderAbstract1->attach (l_tpALifeInventoryItem,true); +#endif + } +} + +void CALifeCommunicationManager::vfFillTraderVector(CSE_ALifeHumanAbstract *tpALifeHumanAbstract, int iItemCount, ITEM_P_VECTOR &tpItemVector) +{ + tpItemVector.clear (); + OBJECT_IT I = tpALifeHumanAbstract->children.end() - iItemCount; + OBJECT_IT E = tpALifeHumanAbstract->children.end(); + for ( ; I != E; ++I) + tpItemVector.push_back(smart_cast(objects().object(*I))); +} + +void CALifeCommunicationManager::vfGenerateSums (ITEM_P_VECTOR &tpTrader, INT_VECTOR &tpSums) +{ + int l_iStackPointer = 0; + tpSums.clear (); + tpSums.push_back (0); + for (int i=0, n=tpTrader.size(); i= SUM_COUNT_THRESHOLD) + return; + } +#else + INT_IT I = std::find(tpSums.begin(),tpSums.end(),iCurrentSum); + if (tpSums.end() == I) { + tpSums.push_back(iCurrentSum); + if (tpSums.size() >= SUM_COUNT_THRESHOLD) { + sort (tpSums.begin(),tpSums.end()); + return; + } + } +#endif + continue; + } + + for (int i=i2; i>=0; --i) + PUSH_STACK (i1 - 1, i - 1, iCurrentSum + tpTrader[i]->m_dwCost,m_tpStack1,l_iStackPointer); + } + } +#ifndef KEEP_SORTED + sort (tpSums.begin(),tpSums.end()); +#endif +} + +bool CALifeCommunicationManager::bfGetItemIndexes (ITEM_P_VECTOR &tpTrader, int iSum1, INT_VECTOR &tpIndexes, SSumStackCell *tpStack, int iStartI, int iStackPointer) +{ + for (int j=iStartI, n=tpTrader.size(); j= iSum1) + continue; + + for (int i=i2; i>=0; --i) + PUSH_STACK (i1 - 1, i - 1, iCurrentSum + tpTrader[i]->m_dwCost,tpStack,iStackPointer); + } + } + return (false); +} + +bool CALifeCommunicationManager::bfCheckForInventoryCapacity(CSE_ALifeHumanAbstract *tpALifeHumanAbstract1, ITEM_P_VECTOR &tpTrader1, INT_VECTOR &tpIndexes1, CSE_ALifeHumanAbstract *tpALifeHumanAbstract2, ITEM_P_VECTOR &tpTrader2, INT_VECTOR &tpIndexes2) +{ + { + INT_IT I = tpIndexes2.begin(); + INT_IT E = tpIndexes2.end(); + for ( ; I != E; ++I) { + tpALifeHumanAbstract1->children.push_back(tpTrader2[*I]->base()->ID); + tpALifeHumanAbstract2->children.erase(std::find(tpALifeHumanAbstract2->children.begin(),tpALifeHumanAbstract2->children.end(),tpTrader2[*I]->base()->ID)); + } + } + { + INT_IT I = tpIndexes1.begin(); + INT_IT E = tpIndexes1.end(); + for ( ; I != E; ++I) { + tpALifeHumanAbstract2->children.push_back(tpTrader1[*I]->base()->ID); + tpALifeHumanAbstract1->children.erase(std::find(tpALifeHumanAbstract1->children.begin(),tpALifeHumanAbstract1->children.end(),tpTrader1[*I]->base()->ID)); + } + } + + bool l_bResult = tpALifeHumanAbstract1->brain().objects().can_take_item(0) && tpALifeHumanAbstract2->brain().objects().can_take_item(0); + + if (!l_bResult) { + { + INT_IT I = tpIndexes1.begin(); + INT_IT E = tpIndexes1.end(); + for ( ; I != E; ++I) + tpALifeHumanAbstract1->children.push_back(tpTrader1[*I]->base()->ID); + } + { + INT_IT I = tpIndexes2.begin(); + INT_IT E = tpIndexes2.end(); + for ( ; I != E; ++I) + tpALifeHumanAbstract2->children.push_back(tpTrader2[*I]->base()->ID); + } + return (false); + } + else + return (true); +} + +bool CALifeCommunicationManager::bfCheckForInventoryCapacity(CSE_ALifeHumanAbstract *tpALifeHumanAbstract1, ITEM_P_VECTOR &tpTrader1, int iSum1, int iMoney1, CSE_ALifeHumanAbstract *tpALifeHumanAbstract2, ITEM_P_VECTOR &tpTrader2, int iSum2, int iMoney2, int iBalance) +{ + INT_VECTOR l_tpIndexes1, l_tpIndexes2; + int l_iStartI1 = 0, l_iStackPointer1 = 0, l_iStartI2 = 0, l_iStackPointer2 = 0; + for (;;) { + l_tpIndexes1.clear (); + + if (iSum1) + if (!bfGetItemIndexes(tpTrader1,iSum1,l_tpIndexes1,m_tpStack1,l_iStartI1,l_iStackPointer1)) + return (false); + + for (;;) { + l_tpIndexes2.clear(); + + if (iSum2 && !bfGetItemIndexes(tpTrader2,iSum2,l_tpIndexes2,m_tpStack2,l_iStartI2,l_iStackPointer2)) + return (false); + + if (!bfCheckForInventoryCapacity(tpALifeHumanAbstract1,tpTrader1,l_tpIndexes1,tpALifeHumanAbstract2,tpTrader2,l_tpIndexes2)) + continue; + +#ifdef DEBUG + string4096 S; + char *S1 = S; + if (psAI_Flags.test(aiALife)) { + S1 += xr_sprintf(S1,"%s -> ",tpALifeHumanAbstract1->name_replace()); + + if (iSum1) { + for (int i=0, n=l_tpIndexes1.size(); im_dwMoney) >= iBalance + iSum2 - iSum1); + tpALifeHumanAbstract1->m_dwMoney -= iBalance + iSum2 - iSum1; + tpALifeHumanAbstract2->m_dwMoney += iBalance + iSum2 - iSum1; + } + +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + S1 += xr_sprintf(S1,"\n%s -> ",tpALifeHumanAbstract2->name_replace()); + + if (iSum2) { + for (int i=0, n=l_tpIndexes2.size(); i iBalance + iSum2) { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + S1 += xr_sprintf(S1," + $%d",iSum1 - iBalance - iSum2); + } +#endif + R_ASSERT (int(tpALifeHumanAbstract2->m_dwMoney) >= iSum1 - iBalance - iSum2); + tpALifeHumanAbstract1->m_dwMoney += iSum1 - iBalance - iSum2; + tpALifeHumanAbstract2->m_dwMoney -= iSum1 - iBalance - iSum2; + } + +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("%s\n Can trade!",S); + } +#endif + return (true); + } + } +} + +bool CALifeCommunicationManager::bfCheckForTrade (CSE_ALifeHumanAbstract *tpALifeHumanAbstract1, ITEM_P_VECTOR &tpTrader1, INT_VECTOR &tpSums1, int iMoney1, CSE_ALifeHumanAbstract *tpALifeHumanAbstract2, ITEM_P_VECTOR &tpTrader2, INT_VECTOR &tpSums2, int iMoney2, int iBalance) +{ + INT_IT I = tpSums1.begin(); + INT_IT E = tpSums1.end(); + INT_IT II = tpSums2.begin(); + INT_IT EE = tpSums2.end(); + + for ( ; I != E; ++I) { + if (*I + iMoney1 < iBalance) + continue; + + if (*I < iBalance) { + if (bfCheckForInventoryCapacity(tpALifeHumanAbstract1,tpTrader1,*I,iMoney1,tpALifeHumanAbstract2,tpTrader2,0,iMoney2,iBalance)) + break; + } + else { + bool l_bOk = false; + II = tpSums2.begin(); + for ( ; II != EE; ++II) + if (*I >= *II + iBalance) + if (*I - *II - iBalance <= iMoney2) { + if (bfCheckForInventoryCapacity(tpALifeHumanAbstract1,tpTrader1,*I,iMoney1,tpALifeHumanAbstract2,tpTrader2,*II,iMoney2,iBalance)) { + l_bOk = true; + break; + } + } + else + continue; + else + if (*I + iMoney1 >= *II + iBalance) { + if (bfCheckForInventoryCapacity(tpALifeHumanAbstract1,tpTrader1,*I,iMoney1,tpALifeHumanAbstract2,tpTrader2,*II,iMoney2,iBalance)) { + l_bOk = true; + break; + } + } + else + break; + if (l_bOk) + break; + } + } + + if (I == E) { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("Can't trade!\n"); + } +#endif + return (false); + } + else + return (true); +} + +bool CALifeCommunicationManager::bfCheckIfCanNullTradersBalance(CSE_ALifeHumanAbstract *tpALifeHumanAbstract1, CSE_ALifeHumanAbstract *tpALifeHumanAbstract2, int iItemCount1, int iItemCount2, int iBalance) +{ + if (!iBalance) { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("Balance is null"); + } +#endif + return (true); + } + + if (iBalance < 0) { + if (int(tpALifeHumanAbstract1->m_dwMoney) >= -iBalance) { + tpALifeHumanAbstract1->m_dwMoney += iBalance; + tpALifeHumanAbstract2->m_dwMoney -= iBalance; +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("Balance is covered by money"); + } +#endif + return (true); + } + } + else + if (int(tpALifeHumanAbstract2->m_dwMoney) >= iBalance) { + tpALifeHumanAbstract1->m_dwMoney += iBalance; + tpALifeHumanAbstract2->m_dwMoney -= iBalance; +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("Balance is covered by money"); + } +#endif + return (true); + } + + vfFillTraderVector (tpALifeHumanAbstract1,iItemCount1,m_tpTrader1); + vfFillTraderVector (tpALifeHumanAbstract2,iItemCount2,m_tpTrader2); + + sort (m_tpTrader1.begin(),m_tpTrader1.end(),CSortItemByValuePredicate()); + sort (m_tpTrader2.begin(),m_tpTrader2.end(),CSortItemByValuePredicate()); + +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + { + string4096 S; + char *S1 = S; + S1 += xr_sprintf(S1,"%s [%5d]: ",tpALifeHumanAbstract1->name_replace(),tpALifeHumanAbstract1->m_dwMoney); + for (int i=0, n=m_tpTrader1.size(); im_dwCost); + Msg ("%s",S); + } + { + string4096 S; + char *S1 = S; + S1 += xr_sprintf(S1,"%s [%5d]: ",tpALifeHumanAbstract2->name_replace(),tpALifeHumanAbstract2->m_dwMoney); + for (int i=0, n=m_tpTrader2.size(); im_dwCost); + Msg ("%s",S); + } + Msg ("Balance : %6d",iBalance); + } +#endif + + vfGenerateSums (m_tpTrader1,m_tpSums1); + vfGenerateSums (m_tpTrader2,m_tpSums2); + +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + { + string4096 S; + char *S1 = S; + S1 += xr_sprintf(S1,"%s : ",tpALifeHumanAbstract1->name_replace()); + INT_IT I = m_tpSums1.begin(); + INT_IT E = m_tpSums1.end(); + for ( ; I != E; ++I) + S1 += xr_sprintf(S1,"%6d",*I); + Msg ("%s",S); + } + { + string4096 S; + char *S1 = S; + S1 += xr_sprintf(S1,"%s : ",tpALifeHumanAbstract2->name_replace()); + INT_IT I = m_tpSums2.begin(); + INT_IT E = m_tpSums2.end(); + for ( ; I != E; ++I) + S1 += xr_sprintf(S1,"%6d",*I); + Msg ("%s",S); + } + } +#endif + + if (iBalance < 0) + return (bfCheckForTrade(tpALifeHumanAbstract1, m_tpTrader1, m_tpSums1, tpALifeHumanAbstract1->m_dwMoney, tpALifeHumanAbstract2, m_tpTrader2, m_tpSums2, tpALifeHumanAbstract2->m_dwMoney, _abs(iBalance))); + else + return (bfCheckForTrade(tpALifeHumanAbstract2, m_tpTrader2, m_tpSums2, tpALifeHumanAbstract2->m_dwMoney, tpALifeHumanAbstract1, m_tpTrader1, m_tpSums1, tpALifeHumanAbstract1->m_dwMoney, iBalance)); +} + +void CALifeCommunicationManager::vfAppendBlockedItems(CSE_ALifeHumanAbstract *tpALifeHumanAbstract, OBJECT_VECTOR &tpObjectVector1, OBJECT_VECTOR &tpObjectVector2, int l_iItemCount1) +{ + OBJECT_IT I = tpALifeHumanAbstract->children.end() - l_iItemCount1; + OBJECT_IT E = tpALifeHumanAbstract->children.end(); + for ( ; I != E; ++I) + if (std::find(tpObjectVector2.begin(),tpObjectVector2.end(),*I) == tpObjectVector2.end()) + tpObjectVector1.push_back(*I); +} + +void CALifeCommunicationManager::vfPerformTrading(CSE_ALifeHumanAbstract *tpALifeHumanAbstract1, CSE_ALifeHumanAbstract *tpALifeHumanAbstract2) +{ +// VERIFY (tpALifeHumanAbstract1->check_inventory_consistency()); +// VERIFY (tpALifeHumanAbstract2->check_inventory_consistency()); + + m_tpItems1.clear (); + m_tpItems2.clear (); + + append_item_vector (tpALifeHumanAbstract1->children,m_tpItems1); + append_item_vector (tpALifeHumanAbstract2->children,m_tpItems2); + +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + vfPrintItems (tpALifeHumanAbstract1,m_tpItems1); + vfPrintItems (tpALifeHumanAbstract2,m_tpItems2); + } +#endif + tpALifeHumanAbstract1->brain().m_dwTotalMoney = dwfComputeItemCost(m_tpItems1) + tpALifeHumanAbstract1->m_dwMoney; + tpALifeHumanAbstract2->brain().m_dwTotalMoney = dwfComputeItemCost(m_tpItems2) + tpALifeHumanAbstract2->m_dwMoney; + + if (!(tpALifeHumanAbstract1->brain().m_dwTotalMoney*tpALifeHumanAbstract2->brain().m_dwTotalMoney)) { + tpALifeHumanAbstract1->brain().m_dwTotalMoney = u32(-1); + tpALifeHumanAbstract2->brain().m_dwTotalMoney = u32(-1); +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("There is no money and valuable items to trade"); + } +#endif + return; + } + + m_temp_item_vector = m_tpItems1; + m_temp_item_vector.insert(m_temp_item_vector.end(),m_tpItems2.begin(),m_tpItems2.end()); + + sort (m_tpItems1.begin(),m_tpItems1.end()); + sort (m_tpItems2.begin(),m_tpItems2.end()); + +#ifdef FAST_OWNERSHIP + tpALifeHumanAbstract1->vfDetachAll(true); + tpALifeHumanAbstract2->vfDetachAll(true); +#else + tpALifeHumanAbstract1->vfDetachAll(); + tpALifeHumanAbstract2->vfDetachAll(); +#endif + for (int j=0, k=0; j<8; ++j) { + if (m_temp_item_vector.empty() || !(tpALifeHumanAbstract1->brain().m_dwTotalMoney + tpALifeHumanAbstract2->brain().m_dwTotalMoney)) + break; + int l_iItemCount1 = 0, l_iItemCount2 = 0; + switch (k) { + case 0 : { + vfRunFunctionByIndex(tpALifeHumanAbstract1,m_tpBlockedItems1,m_tpItems1,j,l_iItemCount1); + vfRunFunctionByIndex(tpALifeHumanAbstract2,m_tpBlockedItems2,m_tpItems2,j,l_iItemCount2); + break; + } + case 1 : { + vfRunFunctionByIndex(tpALifeHumanAbstract1,m_tpBlockedItems1,m_tpItems1,j,l_iItemCount1); + break; + } + case 2 : { + vfRunFunctionByIndex(tpALifeHumanAbstract2,m_tpBlockedItems2,m_tpItems2,j,l_iItemCount2); + break; + } + case 3 : { + vfRunFunctionByIndex(tpALifeHumanAbstract1,m_tpBlockedItems1,m_tpItems1,j,l_iItemCount1); + + m_tpBlockedItems2.clear(); + m_tpBlockedItems2.insert(m_tpBlockedItems2.end(),tpALifeHumanAbstract1->children.end() - l_iItemCount1,tpALifeHumanAbstract1->children.end()); + + vfRunFunctionByIndex(tpALifeHumanAbstract2,m_tpBlockedItems2,m_tpItems2,j,l_iItemCount2); + + m_tpBlockedItems1.clear(); + m_tpBlockedItems1.insert(m_tpBlockedItems1.end(),tpALifeHumanAbstract2->children.end() - l_iItemCount2,tpALifeHumanAbstract2->children.end()); + + tpALifeHumanAbstract1->children.resize(tpALifeHumanAbstract1->children.size() - l_iItemCount1); + + vfRunFunctionByIndex(tpALifeHumanAbstract1,m_tpBlockedItems1,m_tpItems1,j,l_iItemCount1); + break; + } + } + + m_tpBlockedItems1.clear(); + m_tpBlockedItems2.clear(); + + if (l_iItemCount1*l_iItemCount2) { + OBJECT_IT I = tpALifeHumanAbstract1->children.end() - l_iItemCount1, J; + OBJECT_IT E = tpALifeHumanAbstract1->children.end(); + for ( ; I != E; ++I) { + J = std::find(tpALifeHumanAbstract2->children.end() - l_iItemCount2,tpALifeHumanAbstract2->children.end(),*I); + if (tpALifeHumanAbstract2->children.end() != J) { + if (std::binary_search(m_tpItems1.begin(),m_tpItems1.end(),smart_cast(objects().object(*I)))) + m_tpBlockedItems2.push_back(*I); + else { + R_ASSERT2(std::binary_search(m_tpItems2.begin(),m_tpItems2.end(),smart_cast(objects().object(*I))),"Unknown item parent"); + m_tpBlockedItems1.push_back(*I); + } + } + } + } + + if (m_tpBlockedItems1.empty() && m_tpBlockedItems2.empty()) { + if (l_iItemCount1) + vfAssignItemParents (tpALifeHumanAbstract1,l_iItemCount1); + if (l_iItemCount2) + vfAssignItemParents (tpALifeHumanAbstract2,l_iItemCount2); + if (l_iItemCount1 + l_iItemCount2) { + ITEM_P_IT I = remove_if(m_temp_item_vector.begin(),m_temp_item_vector.end(),CRemoveAttachedItemsPredicate()); + m_temp_item_vector.erase(I,m_temp_item_vector.end()); + } + k = 0; + } + else { + if (!m_tpBlockedItems1.empty()) + if (!m_tpBlockedItems2.empty()) { + vfAppendBlockedItems(tpALifeHumanAbstract1,m_tpBlockedItems1,m_tpBlockedItems2,l_iItemCount1); + vfAppendBlockedItems(tpALifeHumanAbstract2,m_tpBlockedItems2,m_tpBlockedItems1,l_iItemCount2); + tpALifeHumanAbstract1->children.resize(tpALifeHumanAbstract1->children.size() - l_iItemCount1); + tpALifeHumanAbstract2->children.resize(tpALifeHumanAbstract2->children.size() - l_iItemCount2); + k = 3; + } + else { + tpALifeHumanAbstract1->children.resize(tpALifeHumanAbstract1->children.size() - l_iItemCount1); + vfAssignItemParents (tpALifeHumanAbstract2,l_iItemCount2); + ITEM_P_IT I = remove_if(m_temp_item_vector.begin(),m_temp_item_vector.end(),CRemoveAttachedItemsPredicate()); + m_temp_item_vector.erase(I,m_temp_item_vector.end()); + k = 1; + } + else { + tpALifeHumanAbstract2->children.resize(tpALifeHumanAbstract2->children.size() - l_iItemCount2); + k = 2; + vfAssignItemParents (tpALifeHumanAbstract1,l_iItemCount1); + ITEM_P_IT I = remove_if(m_temp_item_vector.begin(),m_temp_item_vector.end(),CRemoveAttachedItemsPredicate()); + m_temp_item_vector.erase(I,m_temp_item_vector.end()); + } + --j; + } + } + +// VERIFY (tpALifeHumanAbstract1->check_inventory_consistency()); +// VERIFY (tpALifeHumanAbstract2->check_inventory_consistency()); + + int l_iItemCount1 = tpALifeHumanAbstract1->children.size(); + int l_iItemCount2 = tpALifeHumanAbstract2->children.size(); + + vfAttachOwnerItems (tpALifeHumanAbstract1,m_temp_item_vector,m_tpItems1); + vfAttachOwnerItems (tpALifeHumanAbstract2,m_temp_item_vector,m_tpItems2); + + if (!bfCheckIfCanNullTradersBalance(tpALifeHumanAbstract1,tpALifeHumanAbstract2,tpALifeHumanAbstract1->children.size() - l_iItemCount1,tpALifeHumanAbstract2->children.size() - l_iItemCount2,ifComputeBalance(tpALifeHumanAbstract1,m_tpItems2) - ifComputeBalance(tpALifeHumanAbstract2,m_tpItems1))) { + vfRestoreItems (tpALifeHumanAbstract1,m_tpItems1); + vfRestoreItems (tpALifeHumanAbstract2,m_tpItems2); + } + +// VERIFY (tpALifeHumanAbstract1->check_inventory_consistency()); +// VERIFY (tpALifeHumanAbstract2->check_inventory_consistency()); + +#ifdef FAST_OWNERSHIP + vfAttachGatheredItems(tpALifeHumanAbstract1,tpALifeHumanAbstract2,m_tpBlockedItems1); +// VERIFY (tpALifeHumanAbstract1->check_inventory_consistency()); +// VERIFY (tpALifeHumanAbstract2->check_inventory_consistency()); + + vfAttachGatheredItems(tpALifeHumanAbstract2,tpALifeHumanAbstract1,m_tpBlockedItems2); +// VERIFY (tpALifeHumanAbstract1->check_inventory_consistency()); +// VERIFY (tpALifeHumanAbstract2->check_inventory_consistency()); +#else + else { + vfAttachGatheredItems(tpALifeHumanAbstract1,m_tpBlockedItems1); + vfAttachGatheredItems(tpALifeHumanAbstract2,m_tpBlockedItems2); + } +#endif +// VERIFY (tpALifeHumanAbstract1->check_inventory_consistency()); +// VERIFY (tpALifeHumanAbstract2->check_inventory_consistency()); + +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + vfPrintItems (tpALifeHumanAbstract1); + vfPrintItems (tpALifeHumanAbstract2); + } +#endif + + tpALifeHumanAbstract1->brain().m_dwTotalMoney = u32(-1); + tpALifeHumanAbstract2->brain().m_dwTotalMoney = u32(-1); +} + +void CALifeCommunicationManager::vfPerformCommunication() +{ + SCHEDULE_P_IT I = m_tpaCombatGroups[0].begin(); + SCHEDULE_P_IT E = m_tpaCombatGroups[0].end(); + for ( ; I != E; ++I) { + SCHEDULE_P_IT i = m_tpaCombatGroups[1].begin(); + SCHEDULE_P_IT e = m_tpaCombatGroups[1].end(); + for ( ; i != e; ++i) { + if (!(*I)->base()->children.empty() || !(*i)->base()->children.empty()) + vfPerformTrading(smart_cast(*I),smart_cast(*i)); + } + } +} + +void CALifeCommunicationManager::communicate_with_customer(CSE_ALifeHumanAbstract *tpALifeHumanAbstract, CSE_ALifeTrader *tpALifeTrader) +{ + // process group of stalkers + CSE_ALifeGroupAbstract *l_tpALifeAbstractGroup = smart_cast(tpALifeHumanAbstract); + if (l_tpALifeAbstractGroup) { + OBJECT_IT I = l_tpALifeAbstractGroup->m_tpMembers.begin(); + OBJECT_IT E = l_tpALifeAbstractGroup->m_tpMembers.end(); + for ( ; I != E; ++I) + communicate_with_customer (smart_cast(objects().object(*I)),tpALifeTrader); + return; + } + + // trade items +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("Selling all the items to %s",tpALifeTrader->name_replace()); + } +#endif + CSE_ALifeItemPDA *original_pda = 0; + tpALifeHumanAbstract->brain().m_dwTotalMoney = tpALifeHumanAbstract->m_dwMoney; + { + OBJECT_IT I = tpALifeHumanAbstract->children.begin(); + OBJECT_IT E = tpALifeHumanAbstract->children.end(); + for ( ; I != E; ++I) { + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast(objects().object(*I)); + CSE_ALifeItemPDA *pda = smart_cast(l_tpALifeInventoryItem); + if (pda && (pda->m_original_owner == tpALifeHumanAbstract->ID)) { + VERIFY (!original_pda); + original_pda = pda; + } + tpALifeHumanAbstract->detach (l_tpALifeInventoryItem,0,true,false); + tpALifeTrader->attach (l_tpALifeInventoryItem,true); + u32 l_dwItemCost = tpALifeTrader->dwfGetItemCost(l_tpALifeInventoryItem); + tpALifeHumanAbstract->brain().m_dwTotalMoney += l_dwItemCost; + tpALifeTrader->m_dwMoney -= l_dwItemCost; + } + tpALifeHumanAbstract->children.clear(); + } + + sort (tpALifeTrader->children.begin(),tpALifeTrader->children.end()); + + tpALifeHumanAbstract->m_dwMoney = tpALifeHumanAbstract->brain().m_dwTotalMoney; + + m_temp_item_vector.clear (); + append_item_vector (tpALifeTrader->children,m_temp_item_vector); + + m_tpBlockedItems1.clear (); + for (int i=0; i<8; ++i) { + int l_iItemCount = 0; + vfRunFunctionByIndex (tpALifeHumanAbstract,m_tpBlockedItems1,m_temp_item_vector,i,l_iItemCount); + if (l_iItemCount) { + vfAssignItemParents (tpALifeHumanAbstract,l_iItemCount); + ITEM_P_IT I = m_temp_item_vector.begin(); + ITEM_P_IT E = m_temp_item_vector.end(); + for ( ; I != E; ++I) { + if (!(*I)->attached()) + continue; + OBJECT_IT J = std::lower_bound(tpALifeTrader->children.begin(),tpALifeTrader->children.end(),(*I)->base()->ID); + R_ASSERT ((tpALifeTrader->children.end() != J) && (*J == (*I)->base()->ID) && (((J+1) == tpALifeTrader->children.end()) || (*(J + 1) != (*I)->base()->ID))); + tpALifeTrader->children.erase(J); + } + I = remove_if(m_temp_item_vector.begin(),m_temp_item_vector.end(),CRemoveAttachedItemsPredicate()); + m_temp_item_vector.erase (I,m_temp_item_vector.end()); + } + } + + tpALifeTrader->m_dwMoney += tpALifeHumanAbstract->m_dwMoney - tpALifeHumanAbstract->brain().m_dwTotalMoney; + tpALifeHumanAbstract->m_dwMoney = tpALifeHumanAbstract->brain().m_dwTotalMoney; + tpALifeHumanAbstract->brain().m_dwTotalMoney = u32(-1); + + R_ASSERT2 (int(tpALifeTrader->m_dwMoney) >= 0,"Trader must have enough money to pay for the artefacts!"); + +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("Assigning correct parents"); + } +#endif +#ifdef FAST_OWNERSHIP + vfAttachGatheredItems (tpALifeHumanAbstract,tpALifeTrader,m_tpBlockedItems1); + vfAttachGatheredItems (tpALifeTrader,tpALifeHumanAbstract,m_tpBlockedItems2); +#else + vfAttachGatheredItems (tpALifeHumanAbstract,m_tpBlockedItems1); + vfAttachGatheredItems (tpALifeTrader,m_tpBlockedItems2); +#endif + + { + OBJECT_IT I = std::find(tpALifeTrader->children.begin(),tpALifeTrader->children.end(),original_pda->ID); + VERIFY (I != tpALifeTrader->children.end()); + tpALifeTrader->detach (original_pda); + tpALifeHumanAbstract->attach (original_pda,true); + } +} +/**/ \ No newline at end of file diff --git a/src/xrGameLA/alife_communication_manager.h b/src/xrGameLA/alife_communication_manager.h new file mode 100644 index 000000000..4a3e82b29 --- /dev/null +++ b/src/xrGameLA/alife_communication_manager.h @@ -0,0 +1,79 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_communication_manager.h +// Created : 03.09.2003 +// Modified : 14.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife communication manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "alife_simulator_base.h" + +class CSE_ALifeSchedulable; +class CSE_ALifeHumanAbstract; +class CSE_ALifeTrader; +class CSE_ALifeTraderAbstract; + +// #pragma todo("Dima to Dima : Be attentive with this speed optimization - it doesn't suit to the OOP paradigm!") +#define FAST_OWNERSHIP + +class CALifeCommunicationManager : public virtual CALifeSimulatorBase { +/** +protected: + typedef CALifeSimulatorBase inherited; + +private: + enum { + MAX_STACK_DEPTH = u32(128), + SUM_COUNT_THRESHOLD = u32(30), + }; + + // temporary buffers for trading + ALife::ITEM_P_VECTOR m_tpItems1; + ALife::ITEM_P_VECTOR m_tpItems2; + ALife::OBJECT_VECTOR m_tpBlockedItems1; + ALife::OBJECT_VECTOR m_tpBlockedItems2; + + ALife::ITEM_P_VECTOR m_tpTrader1; + ALife::ITEM_P_VECTOR m_tpTrader2; + ALife::INT_VECTOR m_tpSums1; + ALife::INT_VECTOR m_tpSums2; + + ALife::SSumStackCell m_tpStack1[MAX_STACK_DEPTH]; + ALife::SSumStackCell m_tpStack2[MAX_STACK_DEPTH]; +protected: + u32 dwfComputeItemCost (ALife::ITEM_P_VECTOR &tpItemVector); + void vfRunFunctionByIndex (CSE_ALifeHumanAbstract *tpALifeHumanAbstract, ALife::OBJECT_VECTOR &tpBlockedItems, ALife::ITEM_P_VECTOR &tpItems, int i, int &j); + void vfAssignItemParents (CSE_ALifeHumanAbstract *tpALifeHumanAbstract, int iItemCount); + void vfAppendBlockedItems (CSE_ALifeHumanAbstract *tpALifeHumanAbstract, ALife::OBJECT_VECTOR &tpObjectVector1, ALife::OBJECT_VECTOR &tpObjectVector2, int l_iItemCount1); + void vfAttachOwnerItems (CSE_ALifeHumanAbstract *tpALifeHumanAbstract, ALife::ITEM_P_VECTOR &tpItemVector, ALife::ITEM_P_VECTOR &tpOwnItems); + void vfRestoreItems (CSE_ALifeHumanAbstract *tpALifeHumanAbstract, ALife::ITEM_P_VECTOR &tpItemVector); + int ifComputeBalance (CSE_ALifeHumanAbstract *tpALifeHumanAbstract, ALife::ITEM_P_VECTOR &tpItemVector); + void vfFillTraderVector (CSE_ALifeHumanAbstract *tpALifeHumanAbstract, int iItemCount, ALife::ITEM_P_VECTOR &tpItemVector); + void vfGenerateSums (ALife::ITEM_P_VECTOR &tpTrader, ALife::INT_VECTOR &tpSums); + bool bfGetItemIndexes (ALife::ITEM_P_VECTOR &tpTrader, int iSum1, ALife::INT_VECTOR &tpIndexes, ALife::SSumStackCell *tpStack, int iStartI, int iStackPointer); + bool bfCheckForInventoryCapacity (CSE_ALifeHumanAbstract *tpALifeHumanAbstract1, ALife::ITEM_P_VECTOR &tpTrader1, ALife::INT_VECTOR &tpIndexes1, CSE_ALifeHumanAbstract *tpALifeHumanAbstract2, ALife::ITEM_P_VECTOR &tpTrader2, ALife::INT_VECTOR &tpIndexes2); + bool bfCheckForInventoryCapacity (CSE_ALifeHumanAbstract *tpALifeHumanAbstract1, ALife::ITEM_P_VECTOR &tpTrader1, int iSum1, int iMoney1, CSE_ALifeHumanAbstract *tpALifeHumanAbstract2, ALife::ITEM_P_VECTOR &tpTrader2, int iSum2, int iMoney2, int iBalance); + bool bfCheckForTrade (CSE_ALifeHumanAbstract *tpALifeHumanAbstract1, ALife::ITEM_P_VECTOR &tpTrader1, ALife::INT_VECTOR &tpSums1, int iMoney1, CSE_ALifeHumanAbstract *tpALifeHumanAbstract2, ALife::ITEM_P_VECTOR &tpTrader2, ALife::INT_VECTOR &tpSums2, int iMoney2, int iBalance); + bool bfCheckIfCanNullTradersBalance (CSE_ALifeHumanAbstract *tpALifeHumanAbstract1, CSE_ALifeHumanAbstract *tpALifeHumanAbstract2, int iItemCount1, int iItemCount2, int iBalance); + void vfPerformTrading (CSE_ALifeHumanAbstract *tpALifeHumanAbstract1, CSE_ALifeHumanAbstract *tpALifeHumanAbstract2); + void vfPerformCommunication (); + +#ifdef FAST_OWNERSHIP + void vfAttachGatheredItems (CSE_ALifeTraderAbstract *tpALifeTraderAbstract1, CSE_ALifeTraderAbstract *tpALifeTraderAbstract2, ALife::OBJECT_VECTOR &tpObjectVector); +#else + void vfAttachGatheredItems (CSE_ALifeTraderAbstract *tpALifeTraderAbstract1, ALife::OBJECT_VECTOR &tpObjectVector); +#endif +#ifdef DEBUG + void vfPrintItems (CSE_ALifeHumanAbstract *tpALifeHumanAbstract, ALife::ITEM_P_VECTOR &tpItemVector); + void vfPrintItems (CSE_ALifeHumanAbstract *tpALifeHumanAbstract); +#endif +/**/ +public: + CALifeCommunicationManager (xrServer *server, LPCSTR section); +/** + virtual ~CALifeCommunicationManager (); + void communicate_with_customer (CSE_ALifeHumanAbstract *tpALifeHumanAbstract, CSE_ALifeTrader *tpALifeTrader); +/**/ +}; \ No newline at end of file diff --git a/src/xrGameLA/alife_communication_manager_inline.h b/src/xrGameLA/alife_communication_manager_inline.h new file mode 100644 index 000000000..fb6b68bb3 --- /dev/null +++ b/src/xrGameLA/alife_communication_manager_inline.h @@ -0,0 +1,10 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_communication_manager_inline.h +// Created : 03.09.2003 +// Modified : 14.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife communication manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + diff --git a/src/xrGameLA/alife_communication_space.h b/src/xrGameLA/alife_communication_space.h new file mode 100644 index 000000000..778143766 --- /dev/null +++ b/src/xrGameLA/alife_communication_space.h @@ -0,0 +1,23 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_communication_space.h +// Created : 14.05.2004 +// Modified : 14.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife communication space +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +/** +#include "xrServer_Objects_Alife_Items.h" + +namespace ALifeCommunication { + class CRemoveAttachedItemsPredicate { + public: + IC bool operator() (const CSE_ALifeInventoryItem *item) + { + return (item->attached()); + }; + }; +} +/**/ \ No newline at end of file diff --git a/src/xrGameLA/alife_creature_abstract.cpp b/src/xrGameLA/alife_creature_abstract.cpp new file mode 100644 index 000000000..8a18f091e --- /dev/null +++ b/src/xrGameLA/alife_creature_abstract.cpp @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_creature_abstract.cpp +// Created : 27.10.2005 +// Modified : 27.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife creature abstract class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "monster_community.h" +#include "Level.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_time_manager.h" +void CSE_ALifeCreatureAbstract::on_spawn () +{ + inherited::on_spawn (); + + m_dynamic_out_restrictions.clear (); + m_dynamic_in_restrictions.clear (); + + if (smart_cast(this)) + return; + + MONSTER_COMMUNITY monster_community; + monster_community.set (pSettings->r_string(s_name, "species")); + if (monster_community.team() != 255) + s_team = monster_community.team(); + + if (!g_Alive()) + m_game_death_time = 0;//alife().time_manager().game_time(); +} + +void CSE_ALifeCreatureActor::add_online (const bool &update_registries) +{ + CSE_ALifeTraderAbstract::add_online (update_registries); +} + +void CSE_ALifeCreatureActor::add_offline (const xr_vector &saved_children, const bool &update_registries) +{ + CSE_ALifeTraderAbstract::add_offline(saved_children,update_registries); +} diff --git a/src/xrGameLA/alife_dynamic_object.cpp b/src/xrGameLA/alife_dynamic_object.cpp new file mode 100644 index 000000000..54cdd8255 --- /dev/null +++ b/src/xrGameLA/alife_dynamic_object.cpp @@ -0,0 +1,344 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_dynamic_object.cpp +// Created : 27.10.2005 +// Modified : 27.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife dynamic object class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "xrServer_Objects_ALife.h" +#include "alife_simulator.h" +#include "alife_schedule_registry.h" +#include "alife_graph_registry.h" +#include "alife_object_registry.h" +#include "level_graph.h" +#include "game_level_cross_table.h" +#include "game_graph.h" +#include "xrServer.h" + +void CSE_ALifeDynamicObject::on_spawn () +{ +#ifdef DEBUG +// Msg ("[LSS] spawning object [%d][%d][%s][%s]",ID,ID_Parent,name(),name_replace()); +#endif +} + +void CSE_ALifeDynamicObject::on_register () +{ + CSE_ALifeObject *object = this; + while (object->ID_Parent != ALife::_OBJECT_ID(-1)) { + object = ai().alife().objects().object(object->ID_Parent); + VERIFY (object); + } + + if (!alife().graph().level().object(object->ID,true) && !keep_saved_data_anyway()) + client_data.clear (); +} + +void CSE_ALifeDynamicObject::on_before_register () +{ +} + +#include "level.h" +#include "map_manager.h" + +void CSE_ALifeDynamicObject::on_unregister() +{ + Level().MapManager().OnObjectDestroyNotify(ID); +} + +void CSE_ALifeDynamicObject::switch_online () +{ + R_ASSERT (!m_bOnline); + m_bOnline = true; + alife().add_online (this); +} + +void CSE_ALifeDynamicObject::switch_offline () +{ + R_ASSERT (m_bOnline); + m_bOnline = false; + alife().remove_online (this); +#ifdef DEBUG + if (!client_data.empty()) + Msg ("CSE_ALifeDynamicObject::switch_offline: client_data is cleared for [%d][%s]",ID,name_replace()); +#endif // DEBUG + if (!keep_saved_data_anyway()) + client_data.clear (); +} + +void CSE_ALifeDynamicObject::add_online (const bool &update_registries) +{ + if (!update_registries) + return; + + alife().scheduled().remove (this); + alife().graph().remove (this,m_tGraphID,false); +} + +void CSE_ALifeDynamicObject::add_offline (const xr_vector &saved_children, const bool &update_registries) +{ + if (!update_registries) + return; + + alife().scheduled().add (this); + alife().graph().add (this,m_tGraphID,false); +} + +bool CSE_ALifeDynamicObject::synchronize_location () +{ + if (!ai().level_graph().valid_vertex_position(o_Position) || ai().level_graph().inside(ai().level_graph().vertex(m_tNodeID),o_Position)) + return (true); + + m_tNodeID = ai().level_graph().vertex(m_tNodeID,o_Position); + + GameGraph::_GRAPH_ID tGraphID = ai().cross_table().vertex(m_tNodeID).game_vertex_id(); + if (tGraphID != m_tGraphID) { + if (!m_bOnline) { + Fvector position = o_Position; + u32 level_vertex_id = m_tNodeID; + alife().graph().change (this,m_tGraphID,tGraphID); + if (ai().level_graph().inside(ai().level_graph().vertex(level_vertex_id),position)) { + level_vertex_id = m_tNodeID; + o_Position = position; + } + } + else { + VERIFY (ai().game_graph().vertex(tGraphID)->level_id() == alife().graph().level().level_id()); + m_tGraphID = tGraphID; + } + } + + m_fDistance = ai().cross_table().vertex(m_tNodeID).distance(); + + return (true); +} + +void CSE_ALifeDynamicObject::try_switch_online () +{ + CSE_ALifeSchedulable *schedulable = smart_cast(this); + // checking if the abstract monster has just died + if (schedulable) { + if (!schedulable->need_update(this)) { + if (alife().scheduled().object(ID,true)) + alife().scheduled().remove (this); + } + else + if (!alife().scheduled().object(ID,true)) + alife().scheduled().add (this); + } + + if (!can_switch_online()) { +#ifdef DEBUG + if (!client_data.empty()) + //Msg ("CSE_ALifeDynamicObject::try_switch_online: client_data is cleared for [%d][%s]",ID,name_replace()); +#endif // DEBUG + if (!keep_saved_data_anyway()) + client_data.clear (); + return; + } + + if (!can_switch_offline()) { + alife().switch_online (this); + return; + } + + if (alife().graph().actor()->o_Position.distance_to(o_Position) > alife().online_distance()) { +#ifdef DEBUG + if (!client_data.empty()) +// Msg ("CSE_ALifeDynamicObject::try_switch_online2: client_data is cleared for [%d][%s]",ID,name_replace()); +#endif // DEBUG + if (!keep_saved_data_anyway()) + client_data.clear (); + return; + } + + alife().switch_online (this); +} + +void CSE_ALifeDynamicObject::try_switch_offline () +{ + if (!can_switch_offline()) + return; + + if (!can_switch_online()) { + alife().switch_offline (this); + return; + } + + if (alife().graph().actor()->o_Position.distance_to(o_Position) <= alife().offline_distance()) + return; + + alife().switch_offline (this); +} + +bool CSE_ALifeDynamicObject::redundant () const +{ + return (false); +} + +/// ---------------------------- CSE_ALifeInventoryBox --------------------------------------------- + +void CSE_ALifeInventoryBox::add_online (const bool &update_registries) +{ + CSE_ALifeDynamicObjectVisual *object = (this); + + NET_Packet tNetPacket; + ClientID clientID; + clientID.set (object->alife().server().GetServerClient() ? object->alife().server().GetServerClient()->ID.value() : 0); + + ALife::OBJECT_IT I = object->children.begin(); + ALife::OBJECT_IT E = object->children.end(); + for ( ; I != E; ++I) { + CSE_ALifeDynamicObject *l_tpALifeDynamicObject = ai().alife().objects().object(*I); + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast(l_tpALifeDynamicObject); + R_ASSERT2 (l_tpALifeInventoryItem,"Non inventory item object has parent?!"); + l_tpALifeInventoryItem->base()->s_flags.or(M_SPAWN_UPDATE); + CSE_Abstract *l_tpAbstract = smart_cast(l_tpALifeInventoryItem); + object->alife().server().entity_Destroy(l_tpAbstract); + +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { +// Msg ("[LSS] Spawning item [%s][%s][%d]",l_tpALifeInventoryItem->base()->name_replace(),*l_tpALifeInventoryItem->base()->s_name,l_tpALifeDynamicObject->ID); + Msg ( + "[LSS][%d] Going online [%d][%s][%d] with parent [%d][%s] on '%s'", + Device.dwFrame, + Device.dwTimeGlobal, + l_tpALifeInventoryItem->base()->name_replace(), + l_tpALifeInventoryItem->base()->ID, + ID, + name_replace(), + "*SERVER*" + ); + } +#endif + + l_tpALifeDynamicObject->o_Position = object->o_Position; + l_tpALifeDynamicObject->m_tNodeID = object->m_tNodeID; + object->alife().server().Process_spawn (tNetPacket,clientID,FALSE,l_tpALifeInventoryItem->base()); + l_tpALifeDynamicObject->s_flags.and (u16(-1) ^ M_SPAWN_UPDATE); + l_tpALifeDynamicObject->m_bOnline = true; + } + + CSE_ALifeDynamicObjectVisual::add_online(update_registries); +} + +void CSE_ALifeInventoryBox::add_offline (const xr_vector &saved_children, const bool &update_registries) +{ + CSE_ALifeDynamicObjectVisual *object = (this); + + for (u32 i=0, n=saved_children.size(); i(ai().alife().objects().object(saved_children[i],true)); + R_ASSERT2 (child, make_string("parent [%d][%s][%s] has not valid child [%d]", + ID, name(), name_replace(), saved_children[i])); + child->m_bOnline = false; + + CSE_ALifeInventoryItem *inventory_item = smart_cast(child); + VERIFY2 (inventory_item,"Non inventory item object has parent?!"); +#ifdef DEBUG +// if (psAI_Flags.test(aiALife)) +// Msg ("[LSS] Destroying item [%s][%s][%d]",inventory_item->base()->name_replace(),*inventory_item->base()->s_name,inventory_item->base()->ID); + Msg ( + "[LSS][%d] Going offline [%d][%s][%d] with parent [%d][%s] on '%s'", + Device.dwFrame, + Device.dwTimeGlobal, + inventory_item->base()->name_replace(), + inventory_item->base()->ID, + ID, + name_replace(), + "*SERVER*" + ); +#endif + + ALife::_OBJECT_ID item_id = inventory_item->base()->ID; + inventory_item->base()->ID = object->alife().server().PerformIDgen(item_id); + + if (!child->can_save()) { + object->alife().release (child); + --i; + --n; + continue; + } + +#ifdef DEBUG + if (!client_data.empty()) + Msg ("CSE_InventoryBox::add_offline: client_data is cleared for [%d][%s]",ID,name_replace()); +#endif // DEBUG + if (!child->keep_saved_data_anyway()) + child->client_data.clear (); + object->alife().graph().add (child,child->m_tGraphID,false); +// object->alife().graph().attach (*object,inventory_item,child->m_tGraphID,true); + alife().graph().remove (child,child->m_tGraphID); + children.push_back (child->ID); + child->ID_Parent = ID; + } + + + CSE_ALifeDynamicObjectVisual::add_offline(saved_children, update_registries); +} + +/// ---------------------------- CSE_ALifeCar --------------------------------------------- + +void CSE_ALifeCar::add_online (const bool &update_registries) +{ + CSE_ALifeDynamicObjectVisual *object = (this); + + NET_Packet tNetPacket; + ClientID clientID; + clientID.set (object->alife().server().GetServerClient() ? object->alife().server().GetServerClient()->ID.value() : 0); + + ALife::OBJECT_IT I = object->children.begin(); + ALife::OBJECT_IT E = object->children.end(); + for ( ; I != E; ++I) { + CSE_ALifeDynamicObject *l_tpALifeDynamicObject = ai().alife().objects().object(*I); + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast(l_tpALifeDynamicObject); + R_ASSERT2 (l_tpALifeInventoryItem,"Non inventory item object has parent?!"); + l_tpALifeInventoryItem->base()->s_flags.or(M_SPAWN_UPDATE); + CSE_Abstract *l_tpAbstract = smart_cast(l_tpALifeInventoryItem); + object->alife().server().entity_Destroy(l_tpAbstract); + + l_tpALifeDynamicObject->o_Position = object->o_Position; + l_tpALifeDynamicObject->m_tNodeID = object->m_tNodeID; + object->alife().server().Process_spawn (tNetPacket,clientID,FALSE,l_tpALifeInventoryItem->base()); + l_tpALifeDynamicObject->s_flags.and (u16(-1) ^ M_SPAWN_UPDATE); + l_tpALifeDynamicObject->m_bOnline = true; + } + + CSE_ALifeDynamicObjectVisual::add_online(update_registries); +} + +void CSE_ALifeCar::add_offline (const xr_vector &saved_children, const bool &update_registries) +{ + CSE_ALifeDynamicObjectVisual *object = (this); + + for (u32 i=0, n=saved_children.size(); i(ai().alife().objects().object(saved_children[i],true)); + R_ASSERT2 (child, make_string("parent [%d][%s][%s] has not valid child [%d]", + ID, name(), name_replace(), saved_children[i])); + child->m_bOnline = false; + + CSE_ALifeInventoryItem *inventory_item = smart_cast(child); + VERIFY2 (inventory_item,"Non inventory item object has parent?!"); + + ALife::_OBJECT_ID item_id = inventory_item->base()->ID; + inventory_item->base()->ID = object->alife().server().PerformIDgen(item_id); + + if (!child->can_save()) { + object->alife().release (child); + --i; + --n; + continue; + } + + if (!child->keep_saved_data_anyway()) + child->client_data.clear (); + object->alife().graph().add (child,child->m_tGraphID,false); + alife().graph().remove (child,child->m_tGraphID); + children.push_back (child->ID); + child->ID_Parent = ID; + } + + + CSE_ALifeDynamicObjectVisual::add_offline(saved_children, update_registries); +} \ No newline at end of file diff --git a/src/xrGameLA/alife_graph_registry.cpp b/src/xrGameLA/alife_graph_registry.cpp new file mode 100644 index 000000000..0f1115c5f --- /dev/null +++ b/src/xrGameLA/alife_graph_registry.cpp @@ -0,0 +1,187 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_graph_registry.cpp +// Created : 15.01.2003 +// Modified : 12.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife graph registry +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_graph_registry.h" +#include "../x_ray.h" + +using namespace ALife; + +CALifeGraphRegistry::CALifeGraphRegistry () +{ +#ifndef PRIQUEL + on_load (); +#endif // PRIQUEL + + m_level = 0; + m_process_time = 0; + m_actor = 0; +} + +CALifeGraphRegistry::~CALifeGraphRegistry () +{ + xr_delete (m_level); +} + +void CALifeGraphRegistry::on_load () +{ + for (int i=0; ivertex_type()[i]].push_back(j); + } + + m_objects.resize (ai().game_graph().header().vertex_count()); + + { + GRAPH_REGISTRY::iterator I = m_objects.begin(); + GRAPH_REGISTRY::iterator E = m_objects.end(); + for ( ; I != E; ++I) + (*I).objects().clear (); + } +} + +void CALifeGraphRegistry::update (CSE_ALifeDynamicObject *object) +{ + if (!object->m_bDirectControl) + return; + + if (object->s_flags.is(M_SPAWN_OBJECT_ASPLAYER)) { + m_actor = smart_cast(object); + R_ASSERT2 (m_actor,"Invalid flag M_SPAWN_OBJECT_ASPLAYER for non-actor object!"); + } + + if (m_actor && !m_level) + setup_current_level (); + + CSE_ALifeInventoryItem *item = smart_cast(object); + if (!item || !item->attached()) + add (object,object->m_tGraphID); +} + +void CALifeGraphRegistry::setup_current_level () +{ + m_level = new CALifeLevelRegistry(ai().game_graph().vertex(actor()->m_tGraphID)->level_id()); + level().set_process_time (m_process_time); + for (int i=0, n=ai().game_graph().header().vertex_count(); ilevel_id() == level().level_id()) { + D_OBJECT_P_MAP::const_iterator I = m_objects[i].objects().objects().begin(); + D_OBJECT_P_MAP::const_iterator E = m_objects[i].objects().objects().end(); + for ( ; I != E; ++I) + level().add ((*I).second); + } + + { + xr_vector::const_iterator I = m_temp.begin(); + xr_vector::const_iterator E = m_temp.end(); + for ( ; I != E; ++I) + level().add (*I); + + m_temp.clear (); + } + GameGraph::LEVEL_MAP::const_iterator I = ai().game_graph().header().levels().find(ai().game_graph().vertex(actor()->m_tGraphID)->level_id()); + R_ASSERT2 (ai().game_graph().header().levels().end() != I,"Graph point level ID not found!"); + + int id = pApp->Level_ID(*(*I).second.name()); + VERIFY3 (id >= 0,"Level is corrupted or doesn't exist",*(*I).second.name()); + pApp->Level_Set (id); + ai().load (*(*I).second.name()); +} + +void CALifeGraphRegistry::attach (CSE_Abstract &object, CSE_ALifeInventoryItem *item, GameGraph::_GRAPH_ID game_vertex_id, bool alife_query, bool add_children) +{ +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] Attaching item [%s][%d] to [%s][%d]",item->base()->name_replace(),item->base()->ID,object.name_replace(),object.ID); + } +#endif + if (alife_query) + remove (smart_cast(item),game_vertex_id); + else + level().remove (smart_cast(item)); + + CSE_ALifeDynamicObject *dynamic_object = smart_cast(&object); + R_ASSERT2 (!alife_query || dynamic_object,"Cannot attach an item to a non-alife object object"); + + dynamic_object->attach (item,alife_query,add_children); +} + +void CALifeGraphRegistry::detach (CSE_Abstract &object, CSE_ALifeInventoryItem *item, GameGraph::_GRAPH_ID game_vertex_id, bool alife_query, bool remove_children) +{ +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] Detaching item [%s][%d] from [%s][%d]",item->base()->name_replace(),item->base()->ID,object.name_replace(),object.ID); + } +#endif + if (alife_query) + add (smart_cast(item),game_vertex_id); + else { + CSE_ALifeDynamicObject *object = smart_cast(item); + VERIFY (object); + object->m_tGraphID = game_vertex_id; + level().add (object); + } + + CSE_ALifeDynamicObject *dynamic_object = smart_cast(&object); + R_ASSERT2 (!alife_query || dynamic_object,"Cannot detach an item from non-alife object"); + + VERIFY (alife_query || !smart_cast(&object) || (ai().game_graph().vertex(smart_cast(&object)->m_tGraphID)->level_id() == level().level_id())); + + if (dynamic_object) + dynamic_object->detach (item,0,alife_query,remove_children); + else { +#ifdef DEBUG + bool value = std::find(object.children.begin(),object.children.end(),item->base()->ID) != object.children.end(); + if (!value) { + Msg ("! ERROR: can't detach independant object. entity[%s:%d], parent[%s:%d], section[%s]", + item->base()->name_replace(),item->base()->ID,object.name_replace(),object.ID, *item->base()->s_name); + } +#endif // DEBUG +// R_ASSERT2 (value,"Can't detach an item which is not on my own"); + } +} + +void CALifeGraphRegistry::add (CSE_ALifeDynamicObject *object, GameGraph::_GRAPH_ID game_vertex_id, bool update) +{ +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] adding object [%s][%d] to graph point %d",object->name_replace(),object->ID,game_vertex_id); + } +#endif + if (!object->m_bOnline && object->used_ai_locations() /**&& object->interactive()/**/) { + VERIFY (ai().game_graph().valid_vertex_id(game_vertex_id)); + m_objects[game_vertex_id].objects().add(object->ID,object); + object->m_tGraphID = game_vertex_id; + } + else + if (!m_level && update) { + m_temp.push_back (object); + object->m_tGraphID = game_vertex_id; + } + + if (update && m_level && ai().game_graph().valid_vertex_id(game_vertex_id)) + level().add (object); +} + +void CALifeGraphRegistry::remove (CSE_ALifeDynamicObject *object, GameGraph::_GRAPH_ID game_vertex_id, bool update) +{ + if (object->used_ai_locations() /**&& object->interactive()/**/) { + #ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] removing object [%s][%d] from graph point %d",object->name_replace(),object->ID,game_vertex_id); + } + #endif + m_objects[game_vertex_id].objects().remove(object->ID); + } + if (update && m_level) + level().remove (object,ai().game_graph().vertex(game_vertex_id)->level_id() != level().level_id()); +} + diff --git a/src/xrGameLA/alife_graph_registry.h b/src/xrGameLA/alife_graph_registry.h new file mode 100644 index 000000000..ab4673a21 --- /dev/null +++ b/src/xrGameLA/alife_graph_registry.h @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_graph_registry.h +// Created : 15.01.2003 +// Modified : 12.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife graph registry +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "xrServer_Objects_ALife_All.h" +#include "alife_level_registry.h" + +class CSE_ALifeCreatureActor; + +class CALifeGraphRegistry { +public: + typedef CSafeMapIterator< + ALife::_OBJECT_ID, + CSE_ALifeDynamicObject, + std::less< + ALife::_OBJECT_ID + >, + false + > OBJECT_REGISTRY; + +public: + class CGraphPointInfo { + protected: + OBJECT_REGISTRY m_objects; + + public: + IC OBJECT_REGISTRY &objects() + { + return (m_objects); + } + + IC const OBJECT_REGISTRY &objects() const + { + return (m_objects); + } + }; + +public: + typedef xr_vector GRAPH_REGISTRY; + typedef xr_vector TERRAIN_REGISTRY; + +protected: + GRAPH_REGISTRY m_objects; + TERRAIN_REGISTRY m_terrain[GameGraph::LOCATION_TYPE_COUNT][GameGraph::LOCATION_COUNT]; + CALifeLevelRegistry *m_level; + CSE_ALifeCreatureActor *m_actor; + float m_process_time; + xr_vector m_temp; + +protected: + void setup_current_level (); + template + IC void iterate (C &c, const F& f); + +public: + CALifeGraphRegistry (); + virtual ~CALifeGraphRegistry (); + void on_load (); + void update (CSE_ALifeDynamicObject *object); + void attach (CSE_Abstract &object, CSE_ALifeInventoryItem *item, GameGraph::_GRAPH_ID game_vertex_id, bool alife_query = true, bool add_children = true); + void detach (CSE_Abstract &object, CSE_ALifeInventoryItem *item, GameGraph::_GRAPH_ID game_vertex_id, bool alife_query = true, bool remove_children = true); + IC void assign (CSE_ALifeMonsterAbstract *object); + void add (CSE_ALifeDynamicObject *object, GameGraph::_GRAPH_ID game_vertex_id, bool bUpdateSwitchObjects = true); + void remove (CSE_ALifeDynamicObject *object, GameGraph::_GRAPH_ID game_vertex_id, bool bUpdateSwitchObjects = true); + IC void change (CSE_ALifeDynamicObject *object, GameGraph::_GRAPH_ID game_vertex_id, GameGraph::_GRAPH_ID next_game_vertex_id); + IC CALifeLevelRegistry &level () const; + IC void set_process_time (const float &process_time); + IC CSE_ALifeCreatureActor *actor () const; + IC const GRAPH_REGISTRY &objects () const; + template + IC void iterate_objects (GameGraph::_GRAPH_ID game_vertex_id, const F& f); +}; + +#include "alife_graph_registry_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_graph_registry_inline.h b/src/xrGameLA/alife_graph_registry_inline.h new file mode 100644 index 000000000..4d50ae42a --- /dev/null +++ b/src/xrGameLA/alife_graph_registry_inline.h @@ -0,0 +1,67 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_graph_registry_inline.h +// Created : 15.01.2003 +// Modified : 12.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife graph registry inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CALifeLevelRegistry &CALifeGraphRegistry::level () const +{ + VERIFY (m_level); + return (*m_level); +} + +IC void CALifeGraphRegistry::change (CSE_ALifeDynamicObject *object, GameGraph::_GRAPH_ID tGraphPointID, GameGraph::_GRAPH_ID tNextGraphPointID) +{ +// VERIFY3 (object->used_ai_locations()/** && (object->interactive() || object->m_bOnline)/**/,*object->s_name,object->name_replace()); + remove (object,tGraphPointID); + add (object,tNextGraphPointID); + object->m_tGraphID = tNextGraphPointID; + object->o_Position = ai().game_graph().vertex(object->m_tGraphID)->level_point(); + object->m_tNodeID = ai().game_graph().vertex(object->m_tGraphID)->level_vertex_id(); +} + +IC void CALifeGraphRegistry::assign (CSE_ALifeMonsterAbstract *monster) +{ + monster->m_tNextGraphID = monster->m_tPrevGraphID = monster->m_tGraphID; + monster->m_fDistanceToPoint = monster->m_fDistance; + CGameGraph::const_iterator i,e; + ai().game_graph().begin (monster->m_tNextGraphID,i,e); + for ( ; i != e; ++i) + if ((*i).distance() > monster->m_fDistance) { + monster->m_fDistanceFromPoint = (*i).distance() - monster->m_fDistance; + break; + } +} + +IC void CALifeGraphRegistry::set_process_time (const float &process_time) +{ + m_process_time = process_time; + if (m_level) + level().set_process_time(m_process_time); +} + +IC CSE_ALifeCreatureActor *CALifeGraphRegistry::actor () const +{ + return (m_actor); +} + +IC const CALifeGraphRegistry::GRAPH_REGISTRY &CALifeGraphRegistry::objects () const +{ + return (m_objects); +} + +template +IC void CALifeGraphRegistry::iterate_objects (GameGraph::_GRAPH_ID game_vertex_id, const F& f) +{ + iterate (((CGraphPointInfo&)(objects()[game_vertex_id])).objects(),f); +} + +template +IC void CALifeGraphRegistry::iterate (C &c, const F& f) +{ + c.update (f); +} diff --git a/src/xrGameLA/alife_group_abstract.cpp b/src/xrGameLA/alife_group_abstract.cpp new file mode 100644 index 000000000..7a4e67b32 --- /dev/null +++ b/src/xrGameLA/alife_group_abstract.cpp @@ -0,0 +1,201 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_group_abstract.cpp +// Created : 27.10.2005 +// Modified : 27.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife group abstract class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "xrServer_Objects_ALife.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_object_registry.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "alife_schedule_registry.h" +#include "alife_graph_registry.h" +#include "game_level_cross_table.h" +#include "level_graph.h" + +void CSE_ALifeGroupAbstract::switch_online () +{ + CSE_ALifeDynamicObject *object = smart_cast(this); + VERIFY (object); + + R_ASSERT (!object->m_bOnline); + object->m_bOnline = true; + + ALife::OBJECT_IT I = m_tpMembers.begin(), B = I; + ALife::OBJECT_IT E = m_tpMembers.end(); + u32 N = (u32)(E - I); + for ( ; I != E; ++I) { + CSE_ALifeDynamicObject *J = ai().alife().objects().object(*I); + if (m_bCreateSpawnPositions) { + J->o_Position = object->o_Position; + J->m_tNodeID = object->m_tNodeID; + CSE_ALifeMonsterAbstract *l_tpALifeMonsterAbstract = smart_cast(J); + if (l_tpALifeMonsterAbstract) + l_tpALifeMonsterAbstract->o_torso.yaw = angle_normalize_signed((I - B)/N*PI_MUL_2); + } + object->alife().add_online (J, false); + } + m_bCreateSpawnPositions = false; + object->alife().scheduled().remove (object); + object->alife().graph().remove (object,object->m_tGraphID,false); +} + +void CSE_ALifeGroupAbstract::switch_offline () +{ + CSE_ALifeDynamicObject *object = smart_cast(base()); + VERIFY (object); + + R_ASSERT (object->m_bOnline); + object->m_bOnline = false; + + ALife::OBJECT_IT I = m_tpMembers.begin(); + ALife::OBJECT_IT E = m_tpMembers.end(); + if (I != E) { + CSE_ALifeMonsterAbstract *tpGroupMember = smart_cast(ai().alife().objects().object(*I)); + CSE_ALifeMonsterAbstract *tpGroup = smart_cast(this); + if (tpGroupMember && tpGroup) { + tpGroup->m_fCurSpeed = tpGroup->m_fCurrentLevelGoingSpeed; + tpGroup->o_Position = tpGroupMember->o_Position; + u32 dwNodeID = tpGroup->m_tNodeID; + tpGroup->m_tGraphID = ai().cross_table().vertex(dwNodeID).game_vertex_id(); + tpGroup->m_fDistanceToPoint = ai().cross_table().vertex(dwNodeID).distance(); + tpGroup->m_tNextGraphID = tpGroup->m_tGraphID; + u16 wNeighbourCount = ai().game_graph().vertex(tpGroup->m_tGraphID)->edge_count(); + CGameGraph::const_iterator i,e; + ai().game_graph().begin (tpGroup->m_tGraphID,i,e); + tpGroup->m_tPrevGraphID = (*(i + object->randI(0,wNeighbourCount))).vertex_id(); + } + object->alife().remove_online (tpGroupMember,false); + ++I; + } + for ( ; I != E; ++I) + object->alife().remove_online (ai().alife().objects().object(*I),false); + object->alife().scheduled().add (object); + object->alife().graph().add (object,object->m_tGraphID,false); +} + +bool CSE_ALifeGroupAbstract::synchronize_location () +{ + if (m_tpMembers.empty()) + return (true); + + CSE_ALifeDynamicObject *object = smart_cast(base()); + VERIFY (object); + + ALife::OBJECT_VECTOR::iterator I = m_tpMembers.begin(); + ALife::OBJECT_VECTOR::iterator E = m_tpMembers.end(); + for ( ; I != E; ++I) + ai().alife().objects().object(*I)->synchronize_location (); + + CSE_ALifeDynamicObject &member = *ai().alife().objects().object(*I); + object->o_Position = member.o_Position; + object->m_tNodeID = member.m_tNodeID; + + if (object->m_tGraphID != member.m_tGraphID) { + if (!object->m_bOnline) + object->alife().graph().change (object,object->m_tGraphID,member.m_tGraphID); + else + object->m_tGraphID = member.m_tGraphID; + } + + object->m_fDistance = member.m_fDistance; + return (true); +} + +void CSE_ALifeGroupAbstract::try_switch_online () +{ + CSE_ALifeDynamicObject *I = smart_cast(base()); + VERIFY (I); + + // checking if the object is not an empty group of objects + if (m_tpMembers.empty()) + return; + + I->try_switch_online (); +} + +void CSE_ALifeGroupAbstract::try_switch_offline () +{ + // checking if group is not empty + if (m_tpMembers.empty()) + return; + + // so, we have a group of objects + // therefore check all the group members if they are ready to switch offline + + CSE_ALifeDynamicObject *I = smart_cast(base()); + VERIFY (I); + + // iterating on group members + for (u32 i=0, N = (u32)m_tpMembers.size(); i(ai().alife().objects().object(m_tpMembers[i])); + if (!tpGroupMember) + continue; + + // check if monster is not dead + if (tpGroupMember->g_Alive()) { + // so, monster is not dead + // checking if the object is _not_ ready to switch offline + if (!tpGroupMember->can_switch_offline()) + continue; + + if (!tpGroupMember->can_switch_online()) + // so, it is not ready, breaking a cycle, because we can't + // switch group offline since not all the group members are ready + // to switch offline + break; + + if (I->alife().graph().actor()->o_Position.distance_to(tpGroupMember->o_Position) <= I->alife().offline_distance()) + // so, it is not ready, breaking a cycle, because we can't + // switch group offline since not all the group members are ready + // to switch offline + break; + + continue; + } + + // detach object from the group + tpGroupMember->fHealth = 0.f; + tpGroupMember->m_bDirectControl = true; + m_tpMembers.erase (m_tpMembers.begin() + i); + tpGroupMember->m_bOnline = false; + CSE_ALifeInventoryItem *item = smart_cast(tpGroupMember); + if (item && item->attached()) { + CSE_ALifeDynamicObject *object = ai().alife().objects().object(tpGroupMember->ID_Parent,true); + if (object) + object->detach (item); + } + // store the __new separate object into the registries + I->alife().register_object (tpGroupMember); + + // and remove it from the graph point but do not remove it from the current level map + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast(tpGroupMember); + if (!l_tpALifeInventoryItem || !l_tpALifeInventoryItem->attached()) + I->alife().graph().remove (tpGroupMember,tpGroupMember->m_tGraphID,false); + + tpGroupMember->m_bOnline = true; + --m_wCount; + --i; + --N; + } + + // checking if group is not empty + if (m_tpMembers.empty()) + return; + + if (!I->can_switch_offline()) + return; + + if (I->can_switch_online() || (i == N)) + I->alife().switch_offline (I); +} + +bool CSE_ALifeGroupAbstract::redundant () const +{ + return (m_tpMembers.empty()); +} diff --git a/src/xrGameLA/alife_group_registry.cpp b/src/xrGameLA/alife_group_registry.cpp new file mode 100644 index 000000000..d551210e6 --- /dev/null +++ b/src/xrGameLA/alife_group_registry.cpp @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_group_registry.cpp +// Created : 28.10.2005 +// Modified : 28.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife group registry +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_group_registry.h" +#include "xrServer_Objects_ALife_Monsters.h" + +CALifeGroupRegistry::~CALifeGroupRegistry () +{ +} + +void CALifeGroupRegistry::add (CSE_ALifeDynamicObject *object) +{ + CSE_ALifeOnlineOfflineGroup *group = smart_cast(object); + if (!group) + return; + + OBJECTS::const_iterator I = objects().find(group->ID); + VERIFY (I == objects().end()); + m_objects.insert (std::make_pair(group->ID,group)); +} + +void CALifeGroupRegistry::remove (CSE_ALifeDynamicObject *object) +{ + CSE_ALifeOnlineOfflineGroup *group = smart_cast(object); + if (!group) + return; + + OBJECTS::iterator I = m_objects.find(group->ID); + VERIFY (I != m_objects.end()); + m_objects.erase (I); +} + +CALifeGroupRegistry::OBJECT &CALifeGroupRegistry::object (const ALife::_OBJECT_ID &id) const +{ + OBJECTS::const_iterator I = objects().find(id); + VERIFY (I != objects().end()); + return (*(*I).second); +} + +void CALifeGroupRegistry::on_after_game_load () +{ + OBJECTS::iterator I = m_objects.begin(); + OBJECTS::iterator E = m_objects.end(); + for ( ; I != E; ++I) + (*I).second->on_after_game_load (); +} diff --git a/src/xrGameLA/alife_group_registry.h b/src/xrGameLA/alife_group_registry.h new file mode 100644 index 000000000..12b7ee63b --- /dev/null +++ b/src/xrGameLA/alife_group_registry.h @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_group_registry.h +// Created : 28.10.2005 +// Modified : 28.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife group registry +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "alife_space.h" + +class CSE_ALifeOnlineOfflineGroup; +class CSE_ALifeDynamicObject; + +class CALifeGroupRegistry { +public: + typedef CSE_ALifeOnlineOfflineGroup OBJECT; + typedef xr_map OBJECTS; + +protected: + OBJECTS m_objects; + +public: + virtual ~CALifeGroupRegistry (); + void add (CSE_ALifeDynamicObject *object); + void remove (CSE_ALifeDynamicObject *object); + OBJECT &object (const ALife::_OBJECT_ID &id) const; + IC const OBJECTS &objects () const; + void on_after_game_load (); +}; + +#include "alife_group_registry_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_group_registry_inline.h b/src/xrGameLA/alife_group_registry_inline.h new file mode 100644 index 000000000..581f7fb9f --- /dev/null +++ b/src/xrGameLA/alife_group_registry_inline.h @@ -0,0 +1,14 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_group_registry_inline.h +// Created : 28.10.2005 +// Modified : 28.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife group registry inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC const CALifeGroupRegistry::OBJECTS &CALifeGroupRegistry::objects () const +{ + return (m_objects); +} diff --git a/src/xrGameLA/alife_human_abstract.cpp b/src/xrGameLA/alife_human_abstract.cpp new file mode 100644 index 000000000..8bbffcc9e --- /dev/null +++ b/src/xrGameLA/alife_human_abstract.cpp @@ -0,0 +1,97 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_human_abstract.cpp +// Created : 27.10.2005 +// Modified : 27.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife human abstract class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "xrServer_objects_ALife_Monsters.h" +#include "alife_human_brain.h" +#include "alife_human_object_handler.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_group_registry.h" + +void CSE_ALifeHumanAbstract::update () +{ + if (!bfActive()) + return; + + brain().update (); +} + +bool CSE_ALifeHumanAbstract::bfPerformAttack () +{ + return (brain().perform_attack()); +} + +ALife::EMeetActionType CSE_ALifeHumanAbstract::tfGetActionType (CSE_ALifeSchedulable *schedulable, int iGroupIndex, bool bMutualDetection) +{ + return (brain().action_type(schedulable,iGroupIndex,bMutualDetection)); +} + +void CSE_ALifeHumanAbstract::vfDetachAll (bool bFictitious) +{ + brain().objects().detach_all (bFictitious); +} + +void CSE_ALifeHumanAbstract::vfUpdateWeaponAmmo () +{ + brain().objects().update_weapon_ammo (); +} + +void CSE_ALifeHumanAbstract::vfProcessItems () +{ + brain().objects().process_items (); +} + +void CSE_ALifeHumanAbstract::vfAttachItems (ALife::ETakeType tTakeType) +{ + brain().objects().attach_items (); +} + +CSE_ALifeDynamicObject *CSE_ALifeHumanAbstract::tpfGetBestDetector () +{ + return (brain().objects().best_detector()); +} + +CSE_ALifeItemWeapon *CSE_ALifeHumanAbstract::tpfGetBestWeapon (ALife::EHitType &tHitType, float &fHitPower) +{ + return (brain().objects().best_weapon()); +} + +void CSE_ALifeHumanAbstract::on_register () +{ + inherited2::on_register (); + brain().on_register (); + // because we need to load profile to setup graph vertex masks + specific_character (); +} + +void CSE_ALifeHumanAbstract::on_unregister () +{ + brain().on_unregister (); + if (m_group_id != 0xffff) + ai().alife().groups().object(m_group_id).unregister_member (ID); +} + +void CSE_ALifeHumanAbstract::spawn_supplies () +{ + specific_character (); + inherited1::spawn_supplies (); + inherited2::spawn_supplies (); +} + +void CSE_ALifeHumanAbstract::add_online (const bool &update_registries) +{ + CSE_ALifeTraderAbstract::add_online (update_registries); + brain().on_switch_online (); +} + +void CSE_ALifeHumanAbstract::add_offline (const xr_vector &saved_children, const bool &update_registries) +{ + CSE_ALifeTraderAbstract::add_offline (saved_children,update_registries); + brain().on_switch_offline (); +} diff --git a/src/xrGameLA/alife_human_brain.cpp b/src/xrGameLA/alife_human_brain.cpp new file mode 100644 index 000000000..631851187 --- /dev/null +++ b/src/xrGameLA/alife_human_brain.cpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_human_brain.cpp +// Created : 06.10.2005 +// Modified : 06.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife human brain class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_human_brain.h" +#include "object_broker.h" +#include "xrServer_Objects_ALife_Monsters.h" + +#ifdef XRGAME_EXPORTS +# include "alife_human_object_handler.h" +# include "alife_monster_movement_manager.h" +# include "alife_monster_detail_path_manager.h" +# include "alife_monster_patrol_path_manager.h" +# include "ai_space.h" +# include "ef_storage.h" +# include "ef_primary.h" +# include "alife_simulator.h" +# include "alife_graph_registry.h" +# include "movement_manager_space.h" +# include "alife_smart_terrain_registry.h" +# include "alife_time_manager.h" +# include "date_time.h" +# ifdef DEBUG +# include "level.h" +# include "map_location.h" +# include "map_manager.h" +# endif +#endif + +#define MAX_ITEM_FOOD_COUNT 3 +#define MAX_ITEM_MEDIKIT_COUNT 3 +#define MAX_AMMO_ATTACH_COUNT 10 + +CALifeHumanBrain::CALifeHumanBrain (object_type *object) : inherited(object) +{ + VERIFY (object); + m_object = object; + +#ifdef XRGAME_EXPORTS + m_object_handler = new CALifeHumanObjectHandler(object); +#endif + + m_dwTotalMoney = 0; + m_cpEquipmentPreferences.resize (5); + m_cpMainWeaponPreferences.resize(4); + +#ifdef XRGAME_EXPORTS + m_cpEquipmentPreferences.resize (iFloor(ai().ef_storage().m_pfEquipmentType->ffGetMaxResultValue() + .5f)); + m_cpMainWeaponPreferences.resize(iFloor(ai().ef_storage().m_pfMainWeaponType->ffGetMaxResultValue() + .5f)); + R_ASSERT2 ((iFloor(ai().ef_storage().m_pfEquipmentType->ffGetMaxResultValue() + .5f) == 5) && (iFloor(ai().ef_storage().m_pfMainWeaponType->ffGetMaxResultValue() + .5f) == 4),"Recompile Level Editor and xrAI and rebuild file \"game.spawn\"!"); +#endif + + for (int i=0, n=m_cpEquipmentPreferences.size(); i temp; + load_data (temp,packet); + } + } + + if (object().m_wVersion <= 35) + return; + + if (object().m_wVersion < 110) { + shared_str temp; + packet.r_stringZ (temp); + } + + if (object().m_wVersion < 118) { + ALife::OBJECT_VECTOR temp; + load_data (temp,packet); + } + + load_data (m_cpEquipmentPreferences,packet); + load_data (m_cpMainWeaponPreferences,packet); +} diff --git a/src/xrGameLA/alife_human_brain.h b/src/xrGameLA/alife_human_brain.h new file mode 100644 index 000000000..ca9a22aea --- /dev/null +++ b/src/xrGameLA/alife_human_brain.h @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_human_brain.h +// Created : 06.10.2005 +// Modified : 06.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife human brain class +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "alife_monster_brain.h" + +class CALifeHumanObjectHandler; +class CSE_ALifeHumanAbstract; + +class CALifeHumanBrain : public CALifeMonsterBrain { +private: + typedef CALifeMonsterBrain inherited; + +public: + typedef CSE_ALifeHumanAbstract object_type; + typedef CALifeHumanObjectHandler object_handler_type; + +private: + object_type *m_object; + object_handler_type *m_object_handler; + +// old not yet obsolete stuff +public: + svector m_cpEquipmentPreferences; + svector m_cpMainWeaponPreferences; + +// old, to be obsolete +public: + u32 m_dwTotalMoney; + +public: + CALifeHumanBrain (object_type *object); + virtual ~CALifeHumanBrain (); + +public: + void on_state_write (NET_Packet &packet); + void on_state_read (NET_Packet &packet); + +public: + IC object_type &object () const; + IC object_handler_type &objects () const; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CALifeHumanBrain) +#undef script_type_list +#define script_type_list save_type_list(CALifeHumanBrain) + +#include "alife_human_brain_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_human_brain_inline.h b/src/xrGameLA/alife_human_brain_inline.h new file mode 100644 index 000000000..5c4f2aa4b --- /dev/null +++ b/src/xrGameLA/alife_human_brain_inline.h @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_human_brain_inline.h +// Created : 06.10.2005 +// Modified : 06.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife human brain class inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CALifeHumanBrain::object_type &CALifeHumanBrain::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +IC CALifeHumanBrain::object_handler_type &CALifeHumanBrain::objects () const +{ + VERIFY (m_object_handler); + return (*m_object_handler); +} diff --git a/src/xrGameLA/alife_human_brain_save.h b/src/xrGameLA/alife_human_brain_save.h new file mode 100644 index 000000000..eda6e0f01 --- /dev/null +++ b/src/xrGameLA/alife_human_brain_save.h @@ -0,0 +1,79 @@ +/// DO NOT DELETE THIS FILE +/** +bool CSE_ALifeHumanAbstract::bfPerformAttack() +{ + if (!m_tpCurrentBestWeapon) + return(false); + + switch (m_tpCurrentBestWeapon->m_dwSlot) { + case 0 : + return (true); + case 3 : { + bool l_bOk = false; + OBJECT_IT I = children.begin(); + OBJECT_IT E = children.end(); + for ( ; I != E; ++I) + if (*I == m_tpCurrentBestWeapon->ID) { + l_bOk = true; + CSE_ALifeItem *l_tpALifeItem = smart_cast(ai().alife().objects().object(*I)); + alife().release (l_tpALifeItem,true); + break; + } + R_ASSERT2 (l_bOk,"Cannot find specified weapon in the inventory"); + return (false); + } + default : { + R_ASSERT2 (m_tpCurrentBestWeapon->m_dwAmmoAvailable,"No ammo for the selected weapon!"); + if (!m_trader_flags.test(eTraderFlagInfiniteAmmo)) + --(m_tpCurrentBestWeapon->m_dwAmmoAvailable); + if (m_tpCurrentBestWeapon->m_dwAmmoAvailable) + return (true); + + for (int i=0, n=children.size() ; i(ai().alife().objects().object(children[i])); + if (l_tpALifeItemAmmo && strstr(m_tpCurrentBestWeapon->m_caAmmoSections,*l_tpALifeItemAmmo->s_name) && l_tpALifeItemAmmo->a_elapsed) { + alife().release (l_tpALifeItemAmmo,true); + --i; + --n; + } + } + m_tpCurrentBestWeapon = 0; + return (false); + } + } +} + +EMeetActionType CSE_ALifeHumanAbstract::tfGetActionType(CSE_ALifeSchedulable *tpALifeSchedulable, int iGroupIndex, bool bMutualDetection) +{ + if (eCombatTypeMonsterMonster == ai().alife().combat_type()) { + CSE_ALifeMonsterAbstract *l_tpALifeMonsterAbstract = smart_cast(tpALifeSchedulable); + R_ASSERT2 (l_tpALifeMonsterAbstract,"Inconsistent meet action type"); + return (eRelationTypeFriend == ai().alife().relation_type(this,smart_cast(tpALifeSchedulable)) ? eMeetActionTypeInteract : ((bMutualDetection || (eCombatActionAttack == alife().choose_combat_action(iGroupIndex))) ? eMeetActionTypeAttack : eMeetActionTypeIgnore)); + } + else + return(eMeetActionTypeAttack); +} + +void CSE_ALifeHumanAbstract::vfChooseGroup(CSE_ALifeGroupAbstract *tpALifeGroupAbstract) +{ + { + OBJECT_IT I = tpALifeGroupAbstract->m_tpMembers.begin(); + OBJECT_IT E = tpALifeGroupAbstract->m_tpMembers.end(); + for ( ; I != E; ++I) { + CSE_ALifeHumanAbstract *l_tpALifeHumanAbstract = smart_cast(ai().alife().objects().object(*I)); + R_ASSERT2 (l_tpALifeHumanAbstract,"Invalid group member"); + l_tpALifeHumanAbstract->vfAttachItems(eTakeTypeMin); + } + } + + { + OBJECT_IT I = tpALifeGroupAbstract->m_tpMembers.begin(); + OBJECT_IT E = tpALifeGroupAbstract->m_tpMembers.end(); + for ( ; I != E; ++I) { + CSE_ALifeHumanAbstract *l_tpALifeHumanAbstract = smart_cast(ai().alife().objects().object(*I)); + R_ASSERT2 (l_tpALifeHumanAbstract,"Invalid group member"); + l_tpALifeHumanAbstract->vfAttachItems(eTakeTypeRest); + } + } +} +/**/ diff --git a/src/xrGameLA/alife_human_brain_script.cpp b/src/xrGameLA/alife_human_brain_script.cpp new file mode 100644 index 000000000..5dd2eaa3b --- /dev/null +++ b/src/xrGameLA/alife_human_brain_script.cpp @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_human_brain_script.cpp +// Created : 02.11.2005 +// Modified : 02.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife human brain class script export +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "alife_human_brain.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CALifeHumanBrain::script_register (lua_State *L) +{ + module(L) + [ + class_("CALifeHumanBrain") + ]; +} diff --git a/src/xrGameLA/alife_human_object_handler.cpp b/src/xrGameLA/alife_human_object_handler.cpp new file mode 100644 index 000000000..a9237df61 --- /dev/null +++ b/src/xrGameLA/alife_human_object_handler.cpp @@ -0,0 +1,100 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_human_object_handler.cpp +// Created : 07.10.2005 +// Modified : 07.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife human object handler class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_human_object_handler.h" +#include "xrServer_Objects_ALife_Monsters.h" + +u16 CALifeHumanObjectHandler::get_available_ammo_count (const CSE_ALifeItemWeapon *weapon, ALife::OBJECT_VECTOR &objects) +{ + return (0); +} + +u16 CALifeHumanObjectHandler::get_available_ammo_count (const CSE_ALifeItemWeapon *weapon, ALife::ITEM_P_VECTOR &items, ALife::OBJECT_VECTOR *objects) +{ + return (0); +} + +void CALifeHumanObjectHandler::attach_available_ammo (CSE_ALifeItemWeapon *weapon, ALife::ITEM_P_VECTOR &items, ALife::OBJECT_VECTOR *objects) +{ +} + +bool CALifeHumanObjectHandler::can_take_item (CSE_ALifeInventoryItem *inventory_item) +{ + return (false); +} + +void CALifeHumanObjectHandler::collect_ammo_boxes () +{ +} + +int CALifeHumanObjectHandler::choose_equipment (ALife::OBJECT_VECTOR *objects) +{ + return (-1); +} + +int CALifeHumanObjectHandler::choose_weapon (const ALife::EWeaponPriorityType &weapon_priority_type, ALife::OBJECT_VECTOR *objects) +{ + return (-1); +} + +int CALifeHumanObjectHandler::choose_food (ALife::OBJECT_VECTOR *objects) +{ + return (-1); +} + +int CALifeHumanObjectHandler::choose_medikit (ALife::OBJECT_VECTOR *objects) +{ + return (-1); +} + +int CALifeHumanObjectHandler::choose_detector (ALife::OBJECT_VECTOR *objects) +{ + return (-1); +} + +int CALifeHumanObjectHandler::choose_valuables () +{ + return (-1); +} + +bool CALifeHumanObjectHandler::choose_fast () +{ + return (false); +} + +void CALifeHumanObjectHandler::choose_group (CSE_ALifeGroupAbstract *group_abstract) +{ +} + + +void CALifeHumanObjectHandler::detach_all (bool fictitious) +{ +} + +void CALifeHumanObjectHandler::update_weapon_ammo () +{ +} + +void CALifeHumanObjectHandler::process_items () +{ +} + +CSE_ALifeDynamicObject *CALifeHumanObjectHandler::best_detector () +{ + return (0); +} + +CSE_ALifeItemWeapon *CALifeHumanObjectHandler::best_weapon () +{ + return (0); +} + +void CALifeHumanObjectHandler::attach_items () +{ +} diff --git a/src/xrGameLA/alife_human_object_handler.h b/src/xrGameLA/alife_human_object_handler.h new file mode 100644 index 000000000..525f0b6df --- /dev/null +++ b/src/xrGameLA/alife_human_object_handler.h @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_human_object_handler.h +// Created : 07.10.2005 +// Modified : 07.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife human object handler class +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "alife_space.h" + +class CSE_ALifeItemWeapon; +class CSE_ALifeInventoryItem; +class CSE_ALifeGroupAbstract; +class CSE_ALifeHumanAbstract; + +class CALifeHumanObjectHandler { +public: + typedef CSE_ALifeHumanAbstract object_type; + +private: + object_type *m_object; + +public: + IC CALifeHumanObjectHandler (object_type *object); + IC object_type &object () const; + +public: + u16 get_available_ammo_count (const CSE_ALifeItemWeapon *weapon, ALife::OBJECT_VECTOR &objects); + u16 get_available_ammo_count (const CSE_ALifeItemWeapon *weapon, ALife::ITEM_P_VECTOR &items, ALife::OBJECT_VECTOR *objects = 0); + void attach_available_ammo (CSE_ALifeItemWeapon *weapon, ALife::ITEM_P_VECTOR &items, ALife::OBJECT_VECTOR *objects = 0); + bool can_take_item (CSE_ALifeInventoryItem *inventory_item); + void collect_ammo_boxes (); + +public: + void detach_all (bool fictitious); + void update_weapon_ammo (); + void process_items (); + CSE_ALifeDynamicObject *best_detector (); + CSE_ALifeItemWeapon *best_weapon (); + +public: + int choose_equipment (ALife::OBJECT_VECTOR *objects = 0); + int choose_weapon (const ALife::EWeaponPriorityType &weapon_priority_type, ALife::OBJECT_VECTOR *objects = 0); + int choose_food (ALife::OBJECT_VECTOR *objects = 0); + int choose_medikit (ALife::OBJECT_VECTOR *objects = 0); + int choose_detector (ALife::OBJECT_VECTOR *objects = 0); + int choose_valuables (); + bool choose_fast (); + void choose_group (CSE_ALifeGroupAbstract *group_abstract); + void attach_items (); +}; + +#include "alife_human_object_handler_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_human_object_handler_inline.h b/src/xrGameLA/alife_human_object_handler_inline.h new file mode 100644 index 000000000..53ff2e79c --- /dev/null +++ b/src/xrGameLA/alife_human_object_handler_inline.h @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_human_object_handler_inline.h +// Created : 07.10.2005 +// Modified : 07.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife human object handler class inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CALifeHumanObjectHandler::CALifeHumanObjectHandler (object_type *object) +{ + VERIFY (object); + m_object = object; +} + +IC CALifeHumanObjectHandler::object_type &CALifeHumanObjectHandler::object () const +{ + VERIFY (m_object); + return (*m_object); +} diff --git a/src/xrGameLA/alife_human_object_handler_save.h b/src/xrGameLA/alife_human_object_handler_save.h new file mode 100644 index 000000000..5fcbf4603 --- /dev/null +++ b/src/xrGameLA/alife_human_object_handler_save.h @@ -0,0 +1,610 @@ +/// DO NOT DELETE THIS FILE + + +/** +struct CSortItemPredicate { + IC bool operator() (const CSE_ALifeInventoryItem *tpALifeInventoryItem1, const CSE_ALifeInventoryItem *tpALifeInventoryItem2) const + { + return (float(tpALifeInventoryItem1->m_dwCost) > float(tpALifeInventoryItem2->m_dwCost)); + }; +}; + +struct CSortItemVolumePredicate { + IC bool operator() (const CSE_ALifeInventoryItem *tpALifeInventoryItem1, const CSE_ALifeInventoryItem *tpALifeInventoryItem2) const + { + return (float(tpALifeInventoryItem1->m_iGridWidth*tpALifeInventoryItem1->m_iGridHeight) > float(tpALifeInventoryItem2->m_iGridWidth*tpALifeInventoryItem2->m_iGridHeight)); + }; +}; + +struct CRemoveSlotAndCellItemsPredicate { + u32 m_dwMaxCount; + u32 m_dwCurCount; + ALife::WEAPON_P_VECTOR *m_temp_weapons; + + CRemoveSlotAndCellItemsPredicate (ALife::WEAPON_P_VECTOR *tpWeaponVector, u32 dwMaxCount) : m_dwMaxCount(dwMaxCount), m_dwCurCount(0) + { + m_temp_weapons = tpWeaponVector; + } + + IC bool operator() (const CSE_ALifeInventoryItem *tpALifeInventoryItem) + { + const CSE_ALifeItemWeapon *l_tpALifeItemWeapon = smart_cast(tpALifeInventoryItem); + if (l_tpALifeItemWeapon && ((*m_temp_weapons)[l_tpALifeItemWeapon->m_dwSlot] == l_tpALifeItemWeapon)) + return (true); + else + if ((m_dwCurCount < m_dwMaxCount) && (1 == tpALifeInventoryItem->m_iVolume)) { + ++m_dwCurCount; + return (true); + } + return (false); + } +}; + +CSE_ALifeItemWeapon *CSE_ALifeHumanAbstract::tpfGetBestWeapon(EHitType &tHitType, float &fHitPower) +{ + fHitPower = 0.f; + m_tpCurrentBestWeapon = 0; + u32 l_dwBestWeapon = 0; + OBJECT_IT I = children.begin(); + OBJECT_IT E = children.end(); + for ( ; I != E; ++I) { + CSE_ALifeItemWeapon *l_tpALifeItemWeapon = smart_cast(ai().alife().objects().object(*I)); + if (!l_tpALifeItemWeapon) + continue; + + l_tpALifeItemWeapon->m_dwAmmoAvailable = get_available_ammo_count(l_tpALifeItemWeapon,children); + if (l_tpALifeItemWeapon->m_dwAmmoAvailable || (!l_tpALifeItemWeapon->m_dwSlot) || (3 == l_tpALifeItemWeapon->m_dwSlot)) { + u32 l_dwCurrentBestWeapon = l_tpALifeItemWeapon->ef_weapon_type(); + if (l_dwCurrentBestWeapon > l_dwBestWeapon) { + l_dwBestWeapon = l_dwCurrentBestWeapon; + m_tpCurrentBestWeapon = l_tpALifeItemWeapon; + } + } + } + if (m_tpCurrentBestWeapon) { + fHitPower = m_tpCurrentBestWeapon->m_fHitPower; + tHitType = m_tpCurrentBestWeapon->m_tHitType; + return (m_tpCurrentBestWeapon); + } + else + return(inherited2::tpfGetBestWeapon(tHitType,fHitPower)); +} + +void CSE_ALifeHumanAbstract::vfCollectAmmoBoxes() +{ + for (int i=0, n=children.size() ; i(ai().alife().objects().object(children[i])); + if (!l_tpALifeItemAmmo) + continue; + + for (int j=i+1; j(ai().alife().objects().object(children[j])); + if (!l_tpALifeItemAmmo1) { + alife().m_temp_marks[j] = true; + continue; + } + + if (!strstr(*l_tpALifeItemAmmo->s_name,*l_tpALifeItemAmmo1->s_name)) + continue; + + alife().m_temp_marks[j] = true; + + if (l_tpALifeItemAmmo->a_elapsed + l_tpALifeItemAmmo1->a_elapsed > l_tpALifeItemAmmo->m_boxSize) { + l_tpALifeItemAmmo1->a_elapsed = l_tpALifeItemAmmo->a_elapsed + l_tpALifeItemAmmo1->a_elapsed - l_tpALifeItemAmmo->m_boxSize; + l_tpALifeItemAmmo->a_elapsed = l_tpALifeItemAmmo->m_boxSize; + l_tpALifeItemAmmo = l_tpALifeItemAmmo1; + } + else { + l_tpALifeItemAmmo->a_elapsed = l_tpALifeItemAmmo->a_elapsed + l_tpALifeItemAmmo1->a_elapsed; + l_tpALifeItemAmmo1->a_elapsed = 0; + } + } + } + + for (int i=0, j=0; i(ai().alife().objects().object(children[i])); + if (!l_tpALifeItemAmmo || l_tpALifeItemAmmo->a_elapsed) + continue; + + alife().release (l_tpALifeItemAmmo,true); + --i; + --n; + } +} + +void CSE_ALifeHumanAbstract::vfUpdateWeaponAmmo() +{ + if (!m_tpCurrentBestWeapon) + return; + + switch (m_tpCurrentBestWeapon->m_dwSlot) { + case 0 : + case 3 : + break; + default : { + for (int i=0, n=children.size() ; i(ai().alife().objects().object(children[i])); + if (l_tpALifeItemAmmo && strstr(m_tpCurrentBestWeapon->m_caAmmoSections,*l_tpALifeItemAmmo->s_name)) { + if (m_tpCurrentBestWeapon->m_dwAmmoAvailable > l_tpALifeItemAmmo->a_elapsed) { + m_tpCurrentBestWeapon->m_dwAmmoAvailable -= l_tpALifeItemAmmo->a_elapsed; + continue; + } + if (m_tpCurrentBestWeapon->m_dwAmmoAvailable) { + l_tpALifeItemAmmo->a_elapsed = (u16)m_tpCurrentBestWeapon->m_dwAmmoAvailable; + m_tpCurrentBestWeapon->m_dwAmmoAvailable = 0; + continue; + } + alife().release (l_tpALifeItemAmmo,true); + --i; + --n; + } + } + m_tpCurrentBestWeapon = 0; + break; + } + } + vfCollectAmmoBoxes(); +} + +u16 CSE_ALifeHumanAbstract::get_available_ammo_count(const CSE_ALifeItemWeapon *tpALifeItemWeapon, OBJECT_VECTOR &tpObjectVector) +{ + if (!tpALifeItemWeapon->m_caAmmoSections) + return(u16(-1)); + u32 l_dwResult = 0; + OBJECT_IT I = tpObjectVector.begin(); + OBJECT_IT E = tpObjectVector.end(); + for ( ; I != E; ++I) { + CSE_ALifeItemAmmo *l_tpALifeItemAmmo = smart_cast(ai().alife().objects().object(*I)); + if (l_tpALifeItemAmmo && strstr(tpALifeItemWeapon->m_caAmmoSections,*l_tpALifeItemAmmo->s_name)) + l_dwResult += l_tpALifeItemAmmo->a_elapsed; + } + return (u16(l_dwResult)); +} + +u16 CSE_ALifeHumanAbstract::get_available_ammo_count(const CSE_ALifeItemWeapon *tpALifeItemWeapon, ITEM_P_VECTOR &tpItemVector, OBJECT_VECTOR *tpObjectVector) +{ + if (!tpALifeItemWeapon->m_caAmmoSections) + return(u16(-1)); + u32 l_dwResult = 0; + ITEM_P_IT I = tpItemVector.begin(); + ITEM_P_IT E = tpItemVector.end(); + for ( ; I != E; ++I) { + CSE_ALifeItemAmmo *l_tpALifeItemAmmo = smart_cast(*I); + if (l_tpALifeItemAmmo && strstr(tpALifeItemWeapon->m_caAmmoSections,*l_tpALifeItemAmmo->s_name) && (l_tpALifeItemAmmo->m_dwCost <= m_dwTotalMoney) && (!tpObjectVector || (std::find(tpObjectVector->begin(),tpObjectVector->end(),l_tpALifeItemAmmo->ID) == tpObjectVector->end()))) { + l_dwResult += l_tpALifeItemAmmo->a_elapsed; + m_dwTotalMoney -= l_tpALifeItemAmmo->m_dwCost; + } + } + return (u16(l_dwResult)); +} + +void CSE_ALifeHumanAbstract::attach_available_ammo(CSE_ALifeItemWeapon *tpALifeItemWeapon, ITEM_P_VECTOR &tpItemVector, OBJECT_VECTOR *tpObjectVector) +{ + if (!tpALifeItemWeapon || !tpALifeItemWeapon->m_caAmmoSections) + return; + u32 l_dwCount = 0, l_dwSafeMoney = m_dwTotalMoney; + ITEM_P_IT I = tpItemVector.begin(); + ITEM_P_IT E = tpItemVector.end(); + for ( ; I != E; ++I) { + CSE_ALifeItemAmmo *l_tpALifeItemAmmo = smart_cast(*I); + if (l_tpALifeItemAmmo && strstr(tpALifeItemWeapon->m_caAmmoSections,*l_tpALifeItemAmmo->s_name) && (l_tpALifeItemAmmo->m_dwCost <= m_dwTotalMoney) && bfCanGetItem(l_tpALifeItemAmmo) && (!tpObjectVector || (std::find(tpObjectVector->begin(),tpObjectVector->end(),l_tpALifeItemAmmo->ID) == tpObjectVector->end()))) { + if (!tpObjectVector) + alife().graph().attach(*this,l_tpALifeItemAmmo,l_tpALifeItemAmmo->m_tGraphID); + else { + children.push_back(l_tpALifeItemAmmo->ID); + m_dwTotalMoney -= l_tpALifeItemAmmo->m_dwCost; + } + ++l_dwCount; + if (l_dwCount >= MAX_AMMO_ATTACH_COUNT) + break; + } + } + m_dwTotalMoney = l_dwSafeMoney; +} + +void CSE_ALifeHumanAbstract::vfProcessItems() +{ + alife().m_temp_item_vector.clear(); + D_OBJECT_P_MAP::const_iterator I = ai().alife().graph().objects()[m_tGraphID].objects().objects().begin(); + D_OBJECT_P_MAP::const_iterator E = ai().alife().graph().objects()[m_tGraphID].objects().objects().end(); + for ( ; I != E; ++I) { + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast((*I).second); + if (l_tpALifeInventoryItem && l_tpALifeInventoryItem->bfUseful() && !(*I).second->m_bOnline) + if ((randF(1.0f) < m_detect_probability)) { + alife().m_temp_item_vector.push_back(l_tpALifeInventoryItem); +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s detected item %s on the graph point %d (probability %f, speed %f)",name_replace(),l_tpALifeInventoryItem->base()->name_replace(),m_tGraphID,m_detect_probability,m_fCurSpeed); + } +#endif + } + else { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s didn't detect item %s on the graph point %d (probability %f, speed %f)",name_replace(),l_tpALifeInventoryItem->base()->name_replace(),m_tGraphID,m_detect_probability,m_fCurSpeed); + } +#endif + } + } + if (!alife().m_temp_item_vector.empty()) + vfAttachItems(); +} + +void CSE_ALifeHumanAbstract::vfDetachAll(bool bFictitious) +{ + while (!children.empty()) { + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast(ai().alife().objects().object(*children.begin())); + R_ASSERT2 (l_tpALifeInventoryItem,"Invalid inventory object"); + if (!bFictitious) + alife().graph().detach (*this,l_tpALifeInventoryItem,m_tGraphID); + else { + OBJECT_IT I = children.begin(); + detach (l_tpALifeInventoryItem,&I); + } + } + R_ASSERT2 ((m_fCumulativeItemMass < EPS_L) && !m_iCumulativeItemVolume,"Invalid cumulative item mass or volume value"); +} + +CSE_ALifeDynamicObject *CSE_ALifeHumanAbstract::tpfGetBestDetector() +{ + m_tpBestDetector = 0; + CSE_ALifeGroupAbstract *l_tpALifeGroupAbstract = smart_cast(this); + if (l_tpALifeGroupAbstract) { + u32 l_dwBestValue = 0; + if (!l_tpALifeGroupAbstract->m_wCount) + return (0); + OBJECT_IT I = l_tpALifeGroupAbstract->m_tpMembers.begin(); + OBJECT_IT E = l_tpALifeGroupAbstract->m_tpMembers.end(); + for ( ; I != E; ++I) { + CSE_ALifeHumanAbstract *l_tpALifeHumanAbstract = smart_cast(ai().alife().objects().object(l_tpALifeGroupAbstract->m_tpMembers[0])); + R_ASSERT (l_tpALifeHumanAbstract); + ai().ef_storage().alife().member_item() = l_tpALifeHumanAbstract->tpfGetBestDetector(); + u32 l_dwCurrentValue = iFloor(ai().ef_storage().m_pfDetectorType->ffGetValue()+.5f); + if (l_dwCurrentValue > l_dwBestValue) { + l_dwBestValue = l_dwCurrentValue; + m_tpBestDetector = const_cast(smart_cast(ai().ef_storage().alife().member_item())); + } + } + return (m_tpBestDetector); + } + + OBJECT_IT I = children.begin(); + OBJECT_IT E = children.end(); + for ( ; I != E; ++I) { + CSE_ALifeDynamicObject *l_tpALifeDynamicObject = ai().alife().objects().object(*I); + CSE_ALifeInventoryItem *l_tpALifeInventoryItem = smart_cast(l_tpALifeDynamicObject); + R_ASSERT2 (l_tpALifeInventoryItem,"Non-item object in the inventory found"); + switch (l_tpALifeDynamicObject->m_tClassID) { + case CLSID_DETECTOR_SIMPLE : { + m_tpBestDetector = l_tpALifeDynamicObject; + break; + } + case CLSID_DETECTOR_VISUAL : { + m_tpBestDetector = l_tpALifeDynamicObject; + return (m_tpBestDetector); + } + } + } + return (m_tpBestDetector); +} + +bool CSE_ALifeHumanAbstract::bfCanGetItem(CSE_ALifeInventoryItem *tpALifeInventoryItem) +{ + if (tpALifeInventoryItem && ((m_fCumulativeItemMass + tpALifeInventoryItem->m_fMass > m_fMaxItemMass) || (m_iCumulativeItemVolume + tpALifeInventoryItem->m_iVolume > MAX_ITEM_VOLUME))) + return (false); + return (true); +} + +bool CSE_ALifeHumanAbstract::bfChooseFast() +{ + // fast check if I can pick up all the items + u32 l_dwCurrentItemCount = children.size(); + float l_fCumulativeItemMass = m_fCumulativeItemMass; + int l_iCumulativeItemVolume = m_iCumulativeItemVolume; + bool l_bOk = true; + ITEM_P_IT I = alife().m_temp_item_vector.begin(); + ITEM_P_IT E = alife().m_temp_item_vector.end(), J = E - 1; + for ( ; I != E; ++I) + if ((I != J) || bfCanGetItem(*I)) { + m_fCumulativeItemMass += (*I)->m_fMass; + m_iCumulativeItemVolume += (*I)->m_iVolume; + children.push_back ((*I)->base()->ID); + } + else { + l_bOk = false; + break; + } + + m_fCumulativeItemMass = l_fCumulativeItemMass; + m_iCumulativeItemVolume = l_iCumulativeItemVolume; + children.resize (l_dwCurrentItemCount); + + if (l_bOk) { + I = alife().m_temp_item_vector.begin(); + for ( ; I != E; ++I) + alife().graph().attach (*this,*I,smart_cast(*I)->m_tGraphID); + return (true); + } + + return (false); +} + +int CSE_ALifeHumanAbstract::ifChooseEquipment(OBJECT_VECTOR *tpObjectVector) +{ + // stalkers cannot change their equipment due to the game design :-(( + return (0); +// // choosing equipment +// CSE_ALifeInventoryItem *l_tpALifeItemBest = 0; +// float l_fItemBestValue = -1.f; +// ai().ef_storage().alife_evaluation(true); +// ai().ef_storage().alife().member() = this; +// +// ITEM_P_IT I = alife().m_temp_item_vector.begin(), X; +// ITEM_P_IT E = alife().m_temp_item_vector.end(); +// for ( ; I != E; ++I) { +// // checking if it is an equipment item +// ai().ef_storage().alife().member_item() = smart_cast(*I); +// if (ai().ef_storage().m_pfEquipmentType->ffGetValue() > ai().ef_storage().m_pfEquipmentType->ffGetMaxResultValue()) +// continue; +// if (m_dwTotalMoney < (*I)->m_dwCost) +// continue; +// // evaluating item +// float l_fCurrentValue = ai().ef_storage().m_pfEquipmentType->ffGetValue(); +// // choosing the best item +// if ((l_fCurrentValue > l_fItemBestValue) && bfCanGetItem(*I) && (!tpObjectVector || (std::find(tpObjectVector->begin(),tpObjectVector->end(),(*I)->base()->ID) == tpObjectVector->end()))) { +// l_fItemBestValue = l_fCurrentValue; +// l_tpALifeItemBest = *I; +// X = I; +// } +// } +// if (l_tpALifeItemBest) { +// if (!tpObjectVector) { +// alife().graph().attach (*this,l_tpALifeItemBest,smart_cast(l_tpALifeItemBest)->m_tGraphID); +// alife().m_temp_item_vector.erase(X); +// } +// else +// children.push_back (l_tpALifeItemBest->base()->ID); +// return (1); +// } +// return (0); +} + +int CSE_ALifeHumanAbstract::ifChooseWeapon(EWeaponPriorityType tWeaponPriorityType, OBJECT_VECTOR *tpObjectVector) +{ + CSE_ALifeInventoryItem *l_tpALifeItemBest = 0; + float l_fItemBestValue = -1.f; + ai().ef_storage().alife_evaluation (true); + ai().ef_storage().alife().member() = this; + + u32 l_dwSafeMoney = m_dwTotalMoney; + ITEM_P_IT I = alife().m_temp_item_vector.begin(); + ITEM_P_IT E = alife().m_temp_item_vector.end(); + for ( ; I != E; ++I) { + // checking if it is a hand weapon + ai().ef_storage().alife().member_item() = smart_cast(*I); + if (m_dwTotalMoney < (*I)->m_dwCost) + continue; + int j = ai().ef_storage().m_pfPersonalWeaponType->dwfGetWeaponType(); + float l_fCurrentValue = -1.f; + switch (tWeaponPriorityType) { + case eWeaponPriorityTypeKnife : { + if (1 != j) + continue; + l_fCurrentValue = ai().ef_storage().m_pfItemValue->ffGetValue(); + break; + } + case eWeaponPriorityTypeSecondary : { + if (5 != j) + continue; + l_fCurrentValue = ai().ef_storage().m_pfSmallWeaponValue->ffGetValue(); + break; + } + case eWeaponPriorityTypePrimary : { + if ((6 != j) && (8 != j) && (9 != j)) + continue; + l_fCurrentValue = ai().ef_storage().m_pfMainWeaponValue->ffGetValue(); + break; + } + case eWeaponPriorityTypeGrenade : { + if (7 != j) + continue; + l_fCurrentValue = ai().ef_storage().m_pfItemValue->ffGetValue(); + break; + } + default : NODEFAULT; + } + // choosing the best item + if ((l_fCurrentValue > l_fItemBestValue) && bfCanGetItem(*I) && (!tpObjectVector || (std::find(tpObjectVector->begin(),tpObjectVector->end(),(*I)->base()->ID) == tpObjectVector->end()))) { + l_fItemBestValue = l_fCurrentValue; + l_tpALifeItemBest = *I; + } + } + if (l_tpALifeItemBest) { + u32 l_dwCount = children.size(); + + if (!tpObjectVector) + alife().graph().attach (*this,l_tpALifeItemBest,smart_cast(l_tpALifeItemBest)->m_tGraphID); + else + children.push_back (l_tpALifeItemBest->base()->ID); + + m_dwTotalMoney -= l_tpALifeItemBest->m_dwCost; + attach_available_ammo (smart_cast(l_tpALifeItemBest),alife().m_temp_item_vector,tpObjectVector); + m_dwTotalMoney = l_dwSafeMoney; + + if (!tpObjectVector) { + ITEM_P_IT I = remove_if(alife().m_temp_item_vector.begin(),alife().m_temp_item_vector.end(),CRemoveAttachedItemsPredicate()); + alife().m_temp_item_vector.erase(I,alife().m_temp_item_vector.end()); + } + return (children.size() - l_dwCount); + } + return (0); +} + +int CSE_ALifeHumanAbstract::ifChooseFood(OBJECT_VECTOR *tpObjectVector) +{ +#pragma todo("Dima to Dima : Add food and medikit items need count computations") + // choosing food + ai().ef_storage().alife_evaluation(true); + ai().ef_storage().alife().member() = this; + u32 l_dwCount = 0, l_dwSafeMoney = m_dwTotalMoney; + ITEM_P_IT I = alife().m_temp_item_vector.begin(); + ITEM_P_IT E = alife().m_temp_item_vector.end(); + for ( ; I != E; ++I) { + if ((*I)->m_iFoodValue <= 0) + continue; + if (m_dwTotalMoney < (*I)->m_dwCost) + continue; + if (bfCanGetItem(*I) && (!tpObjectVector || (std::find(tpObjectVector->begin(),tpObjectVector->end(),(*I)->base()->ID) == tpObjectVector->end()))) { + m_dwTotalMoney -= (*I)->m_dwCost; + if (!tpObjectVector) + alife().graph().attach (*this,*I,smart_cast(*I)->m_tGraphID); + else { + children.push_back((*I)->base()->ID); + } + ++l_dwCount; + if (l_dwCount >= MAX_ITEM_FOOD_COUNT) + break; + } + } + m_dwTotalMoney = l_dwSafeMoney; + if (l_dwCount) { + if (!tpObjectVector) { + ITEM_P_IT I = remove_if(alife().m_temp_item_vector.begin(),alife().m_temp_item_vector.end(),CRemoveAttachedItemsPredicate()); + alife().m_temp_item_vector.erase(I,alife().m_temp_item_vector.end()); + } + } + return (l_dwCount); +} + +int CSE_ALifeHumanAbstract::ifChooseMedikit(OBJECT_VECTOR *tpObjectVector) +{ + // choosing medikits + u32 l_dwCount = 0, l_dwSafeMoney = m_dwTotalMoney; + ITEM_P_IT I = alife().m_temp_item_vector.begin(); + ITEM_P_IT E = alife().m_temp_item_vector.end(); + for ( ; I != E; ++I) { + if ((*I)->m_iHealthValue <= 0) + continue; + if (m_dwTotalMoney < (*I)->m_dwCost) + continue; + if (bfCanGetItem(*I) && (!tpObjectVector || (std::find(tpObjectVector->begin(),tpObjectVector->end(),(*I)->base()->ID) == tpObjectVector->end()))) { + m_dwTotalMoney -= (*I)->m_dwCost; + if (!tpObjectVector) + alife().graph().attach (*this,*I,smart_cast(*I)->m_tGraphID); + else + children.push_back((*I)->base()->ID); + ++l_dwCount; + if (l_dwCount >= MAX_ITEM_MEDIKIT_COUNT) + break; + } + } + m_dwTotalMoney = l_dwSafeMoney; + if (l_dwCount) { + if (!tpObjectVector) { + ITEM_P_IT I = remove_if(alife().m_temp_item_vector.begin(),alife().m_temp_item_vector.end(),CRemoveAttachedItemsPredicate()); + alife().m_temp_item_vector.erase(I,alife().m_temp_item_vector.end()); + } + } + return (l_dwCount); +} + +int CSE_ALifeHumanAbstract::ifChooseDetector(OBJECT_VECTOR *tpObjectVector) +{ + // choosing detector + CSE_ALifeInventoryItem *l_tpALifeItemBest = 0; + float l_fItemBestValue = -1.f; + ai().ef_storage().alife_evaluation(true); + ai().ef_storage().alife().member() = this; + ITEM_P_IT I = alife().m_temp_item_vector.begin(), X; + ITEM_P_IT E = alife().m_temp_item_vector.end(); + for ( ; I != E; ++I) { + // checking if it is an item + CSE_ALifeItemDetector *l_tpALifeItem = smart_cast(*I); + if (!l_tpALifeItem) + continue; + if (m_dwTotalMoney < l_tpALifeItem->m_dwCost) + continue; + // evaluating item + ai().ef_storage().alife().member_item() = l_tpALifeItem; + float l_fCurrentValue = ai().ef_storage().m_pfEquipmentType->ffGetValue(); + // choosing the best item + if ((l_fCurrentValue > l_fItemBestValue) && bfCanGetItem(l_tpALifeItem) && (!tpObjectVector || (std::find(tpObjectVector->begin(),tpObjectVector->end(),l_tpALifeItem->ID) == tpObjectVector->end()))) { + l_fItemBestValue = l_fCurrentValue; + l_tpALifeItemBest = l_tpALifeItem; + X = I; + } + } + if (l_tpALifeItemBest) { + if (!tpObjectVector) { + alife().graph().attach (*this,l_tpALifeItemBest,smart_cast(l_tpALifeItemBest)->m_tGraphID); + alife().m_temp_item_vector.erase(X); + } + else + children.push_back(l_tpALifeItemBest->base()->ID); + return (1); + } + return (0); +} + +int CSE_ALifeHumanAbstract::ifChooseValuables() +{ + // choosing the rest objects + ITEM_P_IT I = alife().m_temp_item_vector.begin(); + ITEM_P_IT E = alife().m_temp_item_vector.end(); + for ( ; I != E; ++I) + if (bfCanGetItem(*I)) + alife().graph().attach (*this,*I,smart_cast(*I)->m_tGraphID); + + u32 l_dwCount = children.size(); + I = remove_if(alife().m_temp_item_vector.begin(),alife().m_temp_item_vector.end(),CRemoveAttachedItemsPredicate()); + alife().m_temp_item_vector.erase(I,alife().m_temp_item_vector.end()); + + return (children.size() - l_dwCount); +} + +void CSE_ALifeHumanAbstract::vfAttachItems(ETakeType tTakeType) +{ + R_ASSERT2 (fHealth >= EPS_L,"Cannot graph().attach items to dead human"); + + CSE_ALifeGroupAbstract *l_tpALifeGroupAbstract = smart_cast(this); + if (l_tpALifeGroupAbstract) { + vfChooseGroup (l_tpALifeGroupAbstract); + return; + } + else + if (bfChooseFast()) + return; + + if (eTakeTypeAll == tTakeType) { + alife().append_item_vector (children,alife().m_temp_item_vector); + vfDetachAll (); + } + + sort (alife().m_temp_item_vector.begin(),alife().m_temp_item_vector.end(),CSortItemPredicate()); + + if ((eTakeTypeAll == tTakeType) || (eTakeTypeMin == tTakeType)) { + ifChooseFood (); + ifChooseWeapon (eWeaponPriorityTypeKnife); + ifChooseWeapon (eWeaponPriorityTypeSecondary); + ifChooseWeapon (eWeaponPriorityTypePrimary); + ifChooseWeapon (eWeaponPriorityTypeGrenade); + ifChooseMedikit (); + ifChooseDetector (); + ifChooseEquipment (); + } + + if ((eTakeTypeAll == tTakeType) || (eTakeTypeRest == tTakeType)) + ifChooseValuables (); +} +/**/ diff --git a/src/xrGameLA/alife_interaction_manager.cpp b/src/xrGameLA/alife_interaction_manager.cpp new file mode 100644 index 000000000..27e928201 --- /dev/null +++ b/src/xrGameLA/alife_interaction_manager.cpp @@ -0,0 +1,206 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_communication_manager.cpp +// Created : 03.09.2003 +// Modified : 14.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife communication manager +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_interaction_manager.h" +/** +#include "xrServer_Objects_ALife_Monsters.h" +#include "alife_graph_registry.h" +#include "alife_time_manager.h" + +using namespace ALife; + +/**/ +CALifeInteractionManager::CALifeInteractionManager (xrServer*server, LPCSTR section) : + CALifeCombatManager (server,section), + CALifeCommunicationManager (server,section), + CALifeSimulatorBase (server,section) +{ +/** + m_inventory_slot_count = pSettings->r_u32("inventory","slots"); + m_temp_weapons.resize (m_inventory_slot_count); + m_temp_marks.assign (u16(-1),false); +/**/ +} + +/** +CALifeInteractionManager::~CALifeInteractionManager() +{ +} + +void CALifeInteractionManager::check_for_interaction(CSE_ALifeSchedulable *tpALifeSchedulable) +{ + if (!tpALifeSchedulable->bfActive()) + return; + + CSE_ALifeDynamicObject *l_tpALifeDynamicObject = smart_cast(tpALifeSchedulable); + R_ASSERT2 (l_tpALifeDynamicObject,"Unknown schedulable object class"); + GameGraph::_GRAPH_ID l_tGraphID = l_tpALifeDynamicObject->m_tGraphID; + check_for_interaction (tpALifeSchedulable,l_tGraphID); + + CGameGraph::const_iterator I, E; + ai().game_graph().begin (l_tGraphID,I,E); + for ( ; I != E; ++I) + check_for_interaction (tpALifeSchedulable,(*I).vertex_id()); +} + +class CCheckForInteractionPredicate { +public: + CALifeInteractionManager * manager; + mutable CSE_ALifeSchedulable *tpALifeSchedulable; + mutable GameGraph::_GRAPH_ID tGraphID; + mutable int l_iGroupIndex; + mutable bool l_bMutualDetection; + mutable CSE_ALifeHumanAbstract *l_tpALifeHumanAbstract; + mutable CSE_ALifeMonsterAbstract*l_tpALifeMonsterAbstract; + + IC CCheckForInteractionPredicate(CALifeInteractionManager *manager, CSE_ALifeSchedulable *tpALifeSchedulable, GameGraph::_GRAPH_ID tGraphID) : + manager(manager), + tpALifeSchedulable(tpALifeSchedulable), + tGraphID(tGraphID) + { + l_tpALifeHumanAbstract = smart_cast(tpALifeSchedulable); + l_tpALifeMonsterAbstract= smart_cast(tpALifeSchedulable); + manager->vfFillCombatGroup (tpALifeSchedulable,0); + } + + IC bool operator() (CALifeGraphRegistry::OBJECT_REGISTRY::_iterator &I, u64 counter, bool) const + { + if (counter == (*I).second->m_switch_counter) + return (false); + return (!manager->m_tpaCombatGroups[0].empty()); + } + + IC void operator() (CALifeGraphRegistry::OBJECT_REGISTRY::_iterator &I, u64 counter) const + { + (*I).second->m_switch_counter = counter; + + VERIFY (!manager->m_tpaCombatGroups[0].empty()); + + if ((*I).first == tpALifeSchedulable->base()->ID) + return; + + CSE_ALifeSchedulable *l_tpALifeSchedulable = smart_cast((*I).second); + if (!l_tpALifeSchedulable) + return; + + if (!manager->bfCheckForInteraction(tpALifeSchedulable,l_tpALifeSchedulable,l_iGroupIndex,l_bMutualDetection)) + return; + + manager->vfFillCombatGroup (l_tpALifeSchedulable,1); + + switch (manager->m_tpaCombatObjects[l_iGroupIndex]->tfGetActionType(manager->m_tpaCombatObjects[l_iGroupIndex ^ 1],l_iGroupIndex,l_bMutualDetection)) { + case eMeetActionTypeAttack : { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg("[LSS] %s started combat versus %s",manager->m_tpaCombatObjects[l_iGroupIndex]->base()->name_replace(),manager->m_tpaCombatObjects[l_iGroupIndex ^ 1]->base()->name_replace()); + } +#endif + ECombatResult l_tCombatResult = eCombatResultRetreat12; + bool l_bDoNotContinue = false; + for (int i=0; i<2*int(manager->m_dwMaxCombatIterationCount); ++i) { + if (eCombatActionAttack == manager->choose_combat_action(l_iGroupIndex)) { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg("[LSS] %s choosed to attack %s",manager->m_tpaCombatObjects[l_iGroupIndex]->base()->name_replace(),manager->m_tpaCombatObjects[l_iGroupIndex ^ 1]->base()->name_replace()); + } +#endif + manager->vfPerformAttackAction(l_iGroupIndex); + + l_bDoNotContinue = false; + } + else { + if (l_bDoNotContinue) + break; +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg("[LSS] %s choosed to retreat from %s",manager->m_tpaCombatObjects[l_iGroupIndex]->base()->name_replace(),manager->m_tpaCombatObjects[l_iGroupIndex ^ 1]->base()->name_replace()); + } +#endif + if (manager->bfCheckIfRetreated(l_iGroupIndex)) { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg("[LSS] %s did retreat from %s",manager->m_tpaCombatObjects[l_iGroupIndex]->base()->name_replace(),manager->m_tpaCombatObjects[l_iGroupIndex ^ 1]->base()->name_replace()); + } +#endif + l_tCombatResult = l_iGroupIndex ? eCombatResultRetreat2 : eCombatResultRetreat1; + break; + } + l_bDoNotContinue = true; +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg("[LSS] %s didn't retreat from %s",manager->m_tpaCombatObjects[l_iGroupIndex]->base()->name_replace(),manager->m_tpaCombatObjects[l_iGroupIndex ^ 1]->base()->name_replace()); + } +#endif + } + + l_iGroupIndex ^= 1; + + if (manager->m_tpaCombatGroups[l_iGroupIndex].empty()) { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg("[LSS] %s is dead",manager->m_tpaCombatObjects[l_iGroupIndex]->base()->name_replace()); + } +#endif + l_tCombatResult = l_iGroupIndex ? eCombatResult1Kill2 : eCombatResult2Kill1; + break; + } + } +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + if (eCombatResultRetreat12 == l_tCombatResult) + Msg("[LSS] both combat groups decided not to continue combat"); + } +#endif + manager->vfFinishCombat (l_tCombatResult); + break; + } + case eMeetActionTypeInteract : { + R_ASSERT2 (l_tpALifeHumanAbstract,"Non-human objects сannot communicate with each other"); + CSE_ALifeHumanAbstract *l_tpALifeHumanAbstract2 = smart_cast(l_tpALifeSchedulable); + R_ASSERT2 (l_tpALifeHumanAbstract2,"Non-human objects сannot communicate with each other"); +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s interacted with %s",manager->m_tpaCombatObjects[l_iGroupIndex]->base()->name_replace(),manager->m_tpaCombatObjects[l_iGroupIndex ^ 1]->base()->name_replace()); + } +#endif + manager->vfPerformCommunication (); + break; + } + case eMeetActionTypeIgnore : { +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] %s refused from combat",manager->m_tpaCombatObjects[l_iGroupIndex]->base()->name_replace()); + } +#endif + return; + } + case eMeetActionSmartTerrain : { + CSE_ALifeSmartZone *smart_zone = smart_cast(l_tpALifeSchedulable); + VERIFY (smart_zone); + VERIFY (l_tpALifeMonsterAbstract); + smart_zone->smart_touch (l_tpALifeMonsterAbstract); + return; + } + default : NODEFAULT; + } + } +}; + +void CALifeInteractionManager::check_for_interaction(CSE_ALifeSchedulable *tpALifeSchedulable, GameGraph::_GRAPH_ID tGraphID) +{ + graph().iterate_objects( + tGraphID, + CCheckForInteractionPredicate( + this, + tpALifeSchedulable, + tGraphID + ) + ); +} +/**/ diff --git a/src/xrGameLA/alife_interaction_manager.h b/src/xrGameLA/alife_interaction_manager.h new file mode 100644 index 000000000..22901da78 --- /dev/null +++ b/src/xrGameLA/alife_interaction_manager.h @@ -0,0 +1,36 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_communication_manager.h +// Created : 03.09.2003 +// Modified : 14.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife communication manager +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "xrserver_space.h" +#include "alife_combat_manager.h" +#include "alife_communication_manager.h" + +class CALifeInteractionManager : + public CALifeCombatManager, + public CALifeCommunicationManager +{ +/** + friend class CCheckForInteractionPredicate; +protected: + u32 m_inventory_slot_count; + +public: + BOOL_VECTOR m_temp_marks; + ALife::WEAPON_P_VECTOR m_temp_weapons; + +/**/ +public: + CALifeInteractionManager (xrServer *server, LPCSTR section); +/** + virtual ~CALifeInteractionManager (); + void check_for_interaction (CSE_ALifeSchedulable *tpALifeSchedulable); + void check_for_interaction (CSE_ALifeSchedulable *tpALifeSchedulable, GameGraph::_GRAPH_ID tGraphID); +/**/ +}; \ No newline at end of file diff --git a/src/xrGameLA/alife_interaction_manager_inline.h b/src/xrGameLA/alife_interaction_manager_inline.h new file mode 100644 index 000000000..fb6b68bb3 --- /dev/null +++ b/src/xrGameLA/alife_interaction_manager_inline.h @@ -0,0 +1,10 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_communication_manager_inline.h +// Created : 03.09.2003 +// Modified : 14.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife communication manager inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + diff --git a/src/xrGameLA/alife_level_registry.h b/src/xrGameLA/alife_level_registry.h new file mode 100644 index 000000000..b1e751d1f --- /dev/null +++ b/src/xrGameLA/alife_level_registry.h @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_level_registry.h +// Created : 15.01.2003 +// Modified : 12.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife level registry +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "safe_map_iterator.h" +#include "xrServer_Objects_ALife.h" +#include "game_graph.h" +#include "ai_debug.h" + +//#define FULL_LEVEL_UPDATE + +class CSE_ALifeDynamicObject; + +class CALifeLevelRegistry : public CSafeMapIterator { +protected: + typedef CSafeMapIterator inherited; + +protected: + GameGraph::_LEVEL_ID m_level_id; + +public: + IC CALifeLevelRegistry (const GameGraph::_LEVEL_ID &level_id); + IC void add (CSE_ALifeDynamicObject *tpALifeDynamicObject); + IC void remove (CSE_ALifeDynamicObject *tpALifeDynamicObject, bool no_assert = false); + template + IC void update (const _update_predicate &predicate); + IC GameGraph::_LEVEL_ID level_id () const; + IC CSE_ALifeDynamicObject *object (const ALife::_OBJECT_ID &id, bool no_assert = false) const; +}; + +#include "alife_level_registry_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_level_registry_inline.h b/src/xrGameLA/alife_level_registry_inline.h new file mode 100644 index 000000000..a32ede594 --- /dev/null +++ b/src/xrGameLA/alife_level_registry_inline.h @@ -0,0 +1,69 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_level_registry_inline.h +// Created : 15.01.2003 +// Modified : 12.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife level registry inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "ai_space.h" + +IC CALifeLevelRegistry::CALifeLevelRegistry (const GameGraph::_LEVEL_ID &level_id) +{ + m_level_id = level_id; +} + +IC GameGraph::_LEVEL_ID CALifeLevelRegistry::level_id () const +{ + return (m_level_id); +} + +IC void CALifeLevelRegistry::add (CSE_ALifeDynamicObject *object) +{ + if (ai().game_graph().vertex(object->m_tGraphID)->level_id() != level_id()) + return; + +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] adding object [%s][%d] to current level",object->name_replace(),object->ID); + } +#endif + inherited::add (object->ID,object); +} + +IC void CALifeLevelRegistry::remove (CSE_ALifeDynamicObject *object, bool no_assert) +{ +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("[LSS] removing object [%s][%d] from current level",object->name_replace(),object->ID); + } +#endif + inherited::remove (object->ID,no_assert); +} + +template +IC void CALifeLevelRegistry::update (const _update_predicate &predicate) +{ +// u32 object_count = + inherited::update(predicate); +#ifdef FULL_LEVEL_UPDATE + m_first_update = true; +#endif +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { +// Msg ("[LSS][OOS][%d : %d]",object_count, objects().size()); + } +#endif +} + +IC CSE_ALifeDynamicObject *CALifeLevelRegistry::object (const ALife::_OBJECT_ID &id, bool no_assert) const +{ + _REGISTRY::const_iterator I = objects().find(id); + if (I == objects().end()) { + THROW2 (no_assert,"The spesified object hasn't been found in the current level!"); + return (0); + } + return ((*I).second); +} diff --git a/src/xrGameLA/alife_monster_abstract.cpp b/src/xrGameLA/alife_monster_abstract.cpp new file mode 100644 index 000000000..229f1539e --- /dev/null +++ b/src/xrGameLA/alife_monster_abstract.cpp @@ -0,0 +1,228 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_abstract.cpp +// Created : 27.10.2005 +// Modified : 27.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife mnster abstract class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_time_manager.h" +#include "alife_graph_registry.h" +#include "game_graph.h" +#include "alife_object_registry.h" +#include "ef_storage.h" +#include "ef_pattern.h" +#include "alife_monster_brain.h" +#include "alife_monster_movement_manager.h" +#include "alife_monster_detail_path_manager.h" + +void CSE_ALifeMonsterAbstract::add_online (const bool &update_registries) +{ + inherited1::add_online (update_registries); + brain().on_switch_online (); +} + +void CSE_ALifeMonsterAbstract::add_offline (const xr_vector &saved_children, const bool &update_registries) +{ + inherited1::add_offline (saved_children,update_registries); + brain().on_switch_offline (); +} + +void CSE_ALifeMonsterAbstract::update () +{ + if (!bfActive()) + return; + + brain().update (); +/** + GameGraph::_GRAPH_ID start_game_vertex_id = m_tGraphID; + bool bContinue = true; + while (bContinue && bfActive()) { + vfCheckForPopulationChanges(); + bContinue = false; + if (move_offline() && (m_tNextGraphID != m_tGraphID)) { + ALife::_TIME_ID tCurTime = ai().alife().time_manager().game_time(); + m_fDistanceFromPoint += float(tCurTime - m_tTimeID)/1000.f/ai().alife().time_manager().normal_time_factor()*m_fCurSpeed; + if (m_fDistanceToPoint - m_fDistanceFromPoint < EPS_L) { + bContinue = true; + if ((m_fDistanceFromPoint - m_fDistanceToPoint > EPS_L) && (m_fCurSpeed > EPS_L)) + m_tTimeID = tCurTime - ALife::_TIME_ID(iFloor((m_fDistanceFromPoint - m_fDistanceToPoint)*1000.f/m_fCurSpeed)); + m_fDistanceToPoint = m_fDistanceFromPoint = 0.0f; + m_tPrevGraphID = m_tGraphID; + alife().graph().change (this,m_tGraphID,m_tNextGraphID); + CSE_ALifeGroupAbstract *tpALifeGroupAbstract = smart_cast(this); + if (tpALifeGroupAbstract) + tpALifeGroupAbstract->m_bCreateSpawnPositions = true; + } + } + if (move_offline() && (m_tNextGraphID == m_tGraphID)) { + GameGraph::_GRAPH_ID tGraphID = m_tNextGraphID; + CGameGraph::const_iterator i,e; + GameGraph::TERRAIN_VECTOR &tpaTerrain = m_tpaTerrain; + int iPointCount = (int)tpaTerrain.size(); + int iBranches = 0; + ai().game_graph().begin (tGraphID,i,e); + for ( ; i != e; ++i) + if ((*i).vertex_id() != m_tPrevGraphID) + for (int j=0; jvertex_type())) + ++iBranches; + bool bOk = false; + ai().game_graph().begin (tGraphID,i,e); + if (!iBranches) { + for ( ; i != e; ++i) { + for (int j=0; jvertex_type())) { + m_tNextGraphID = (*i).vertex_id(); + m_fDistanceToPoint = (*i).distance(); + bOk = true; + break; + } + if (bOk) + break; + } + } + else { + int iChosenBranch = randI(0,iBranches); + iBranches = 0; + for ( ; i != e; ++i) + if ((*i).vertex_id() != m_tPrevGraphID) { + for (int j=0; jvertex_type()) && ((*i).vertex_id() != m_tPrevGraphID)) { + if (iBranches == iChosenBranch) { + m_tNextGraphID = (*i).vertex_id(); + m_fDistanceToPoint = (*i).distance(); + bOk = true; + break; + } + ++iBranches; + } + if (bOk) + break; + } + } + if (!bOk) { + m_fCurSpeed = 0.0f; + m_fDistanceToPoint = 0.0f; + bContinue = false; + } + else + m_fCurSpeed = m_fGoingSpeed; + } + if (start_game_vertex_id != m_tGraphID) { +#pragma todo("Do not forget to uncomment here!!!") +// alife().check_for_interaction(this); + start_game_vertex_id = m_tGraphID; + } + } + m_tTimeID = ai().alife().time_manager().game_time(); +/**/ +} + +CSE_ALifeItemWeapon *CSE_ALifeMonsterAbstract::tpfGetBestWeapon(ALife::EHitType &tHitType, float &fHitPower) +{ + m_tpCurrentBestWeapon = 0; + fHitPower = m_fHitPower; + tHitType = m_tHitType; + return (m_tpCurrentBestWeapon); +} + +ALife::EMeetActionType CSE_ALifeMonsterAbstract::tfGetActionType(CSE_ALifeSchedulable *tpALifeSchedulable, int iGroupIndex, bool bMutualDetection) +{ + return (ALife::eMeetActionTypeIgnore); + /** + if (ALife::eCombatTypeMonsterMonster == ai().alife().combat_type()) { + CSE_ALifeMonsterAbstract *l_tpALifeMonsterAbstract = smart_cast(tpALifeSchedulable); + R_ASSERT2 (l_tpALifeMonsterAbstract,"Inconsistent meet action type"); + return (ALife::eRelationTypeFriend == ai().alife().relation_type(this,smart_cast(tpALifeSchedulable)) ? ALife::eMeetActionTypeIgnore : ((bMutualDetection || alife().choose_combat_action(iGroupIndex) == ALife::eCombatActionAttack) ? ALife::eMeetActionTypeAttack : ALife::eMeetActionTypeIgnore)); + } + else + if (ALife::eCombatTypeSmartTerrain == ai().alife().combat_type()) { + CSE_ALifeSmartZone *smart_zone = smart_cast(tpALifeSchedulable); + VERIFY (smart_zone); + return (smart_zone->tfGetActionType(this,iGroupIndex ? 0 : 1,bMutualDetection)); + } + else + return (ALife::eMeetActionTypeAttack); + /**/ +} + +bool CSE_ALifeMonsterAbstract::bfActive() +{ + CSE_ALifeGroupAbstract *l_tpALifeGroupAbstract = smart_cast(this); + return (/**/interactive() && /**/((l_tpALifeGroupAbstract && (l_tpALifeGroupAbstract->m_wCount > 0)) || (!l_tpALifeGroupAbstract && (fHealth > EPS_L)))); +} + +CSE_ALifeDynamicObject *CSE_ALifeMonsterAbstract::tpfGetBestDetector() +{ + CSE_ALifeGroupAbstract *l_tpALifeGroupAbstract = smart_cast(this); + if (!l_tpALifeGroupAbstract) + return (this); + else { + if (!l_tpALifeGroupAbstract->m_wCount) + return (0); + else + return (ai().alife().objects().object(l_tpALifeGroupAbstract->m_tpMembers[0])); + } +} + +void CSE_ALifeMonsterAbstract::vfCheckForPopulationChanges () +{ + CSE_ALifeGroupAbstract *l_tpALifeGroupAbstract = smart_cast(this); + if (!l_tpALifeGroupAbstract || !bfActive() || m_bOnline) + return; + + ai().ef_storage().alife_evaluation(true); + ALife::_TIME_ID l_tTimeID = ai().alife().time_manager().game_time(); + if (l_tTimeID >= l_tpALifeGroupAbstract->m_tNextBirthTime) { + ai().ef_storage().alife().member() = this; + l_tpALifeGroupAbstract->m_tNextBirthTime = l_tTimeID + ALife::_TIME_ID(ai().ef_storage().m_pfBirthSpeed->ffGetValue()*24*60*60*1000); + if (randF(100) < ai().ef_storage().m_pfBirthProbability->ffGetValue()) { + u32 l_dwBornCount = iFloor(float(l_tpALifeGroupAbstract->m_wCount)*randF(.5f,1.5f)*ai().ef_storage().m_pfBirthPercentage->ffGetValue()/100.f + .5f); + if (l_dwBornCount) { + l_tpALifeGroupAbstract->m_tpMembers.resize(l_tpALifeGroupAbstract->m_wCount + l_dwBornCount); + ALife::OBJECT_IT I = l_tpALifeGroupAbstract->m_tpMembers.begin() + l_tpALifeGroupAbstract->m_wCount; + ALife::OBJECT_IT E = l_tpALifeGroupAbstract->m_tpMembers.end(); + for ( ; I != E; ++I) { + CSE_Abstract *l_tpAbstract = alife().create (l_tpALifeGroupAbstract,this); + *I = l_tpAbstract->ID; + } + l_tpALifeGroupAbstract->m_wCount = l_tpALifeGroupAbstract->m_wCount + u16(l_dwBornCount); + } + } + } +} + +Fvector CSE_ALifeMonsterAbstract::draw_level_position () const +{ +#if 0 + brain().update (); +#endif + return (brain().movement().detail().draw_level_position()); +} + +bool CSE_ALifeMonsterAbstract::redundant () const +{ + if (g_Alive()) + return (false); + + if (m_bOnline) + return (false); + + if (m_story_id != INVALID_STORY_ID) + return (false); + + if (!m_game_death_time) + return (false); + + ALife::_TIME_ID current_time = alife().time_manager().game_time(); + VERIFY (m_game_death_time <= current_time); + if ((m_game_death_time + m_stay_after_death_time_interval) > current_time) + return (false); + + return (true); +} diff --git a/src/xrGameLA/alife_monster_base.cpp b/src/xrGameLA/alife_monster_base.cpp new file mode 100644 index 000000000..020fa1f56 --- /dev/null +++ b/src/xrGameLA/alife_monster_base.cpp @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_base.cpp +// Created : 07.02.2007 +// Modified : 07.02.2007 +// Author : Dmitriy Iassenev +// Description : ALife mnster base class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "alife_simulator.h" +#include "xrServer.h" +#include "alife_monster_brain.h" + +void CSE_ALifeMonsterBase::on_spawn () +{ + inherited1::on_spawn (); + + if (!pSettings->line_exist(s_name,"Spawn_Inventory_Item_Section")) + return; + + LPCSTR item_section = pSettings->r_string(s_name,"Spawn_Inventory_Item_Section"); + float spawn_probability = pSettings->r_float(s_name,"Spawn_Inventory_Item_Probability"); + float probability = ::Random.randF(); + if ((probability >= spawn_probability) && !fsimilar(spawn_probability,1.f)) + return; + + alife().spawn_item(item_section,o_Position,m_tNodeID,m_tGraphID,ID)->ID_Parent = ID; +} + +extern void add_online_impl (CSE_ALifeDynamicObject *object, const bool &update_registries); + +void CSE_ALifeMonsterBase::add_online (const bool &update_registries) +{ + add_online_impl (this,update_registries); + brain().on_switch_online (); +} + +extern void add_offline_impl (CSE_ALifeDynamicObject *object, const xr_vector &saved_children, const bool &update_registries); + +void CSE_ALifeMonsterBase::add_offline (const xr_vector &saved_children, const bool &update_registries) +{ + add_offline_impl (this,saved_children,update_registries); + brain().on_switch_offline (); +} diff --git a/src/xrGameLA/alife_monster_brain.cpp b/src/xrGameLA/alife_monster_brain.cpp new file mode 100644 index 000000000..85038e03c --- /dev/null +++ b/src/xrGameLA/alife_monster_brain.cpp @@ -0,0 +1,194 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_brain.cpp +// Created : 06.10.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster brain class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_monster_brain.h" +#include "object_broker.h" +#include "xrServer_Objects_ALife_Monsters.h" + +#ifdef XRGAME_EXPORTS +# include "alife_monster_movement_manager.h" +# include "alife_monster_detail_path_manager.h" +# include "alife_monster_patrol_path_manager.h" +# include "ai_space.h" +# include "ef_storage.h" +# include "ef_primary.h" +# include "alife_simulator.h" +# include "alife_graph_registry.h" +# include "movement_manager_space.h" +# include "alife_smart_terrain_registry.h" +# include "alife_time_manager.h" +# include "date_time.h" +# ifdef DEBUG +# include "level.h" +# include "map_location.h" +# include "map_manager.h" +# endif +#endif + +#define MAX_ITEM_FOOD_COUNT 3 +#define MAX_ITEM_MEDIKIT_COUNT 3 +#define MAX_AMMO_ATTACH_COUNT 10 + +CALifeMonsterBrain::CALifeMonsterBrain (object_type *object) +{ + VERIFY (object); + m_object = object; + m_last_search_time = 0; + m_smart_terrain = 0; + +#ifdef XRGAME_EXPORTS + m_movement_manager = new CALifeMonsterMovementManager(object); +#endif + +#ifdef XRGAME_EXPORTS + u32 hours,minutes,seconds; + sscanf (pSettings->r_string(this->object().name(),"smart_terrain_choose_interval"),"%d:%d:%d",&hours,&minutes,&seconds); + m_time_interval = (u32)generate_time(1,1,1,hours,minutes,seconds); +#endif + + m_can_choose_alife_tasks = true; +} + +CALifeMonsterBrain::~CALifeMonsterBrain () +{ +#ifdef XRGAME_EXPORTS + xr_delete (m_movement_manager); +#endif +} + +void CALifeMonsterBrain::on_state_write (NET_Packet &packet) +{ +} + +void CALifeMonsterBrain::on_state_read (NET_Packet &packet) +{ +} + +#ifdef XRGAME_EXPORTS + +bool CALifeMonsterBrain::perform_attack () +{ + return (false); +} + +ALife::EMeetActionType CALifeMonsterBrain::action_type (CSE_ALifeSchedulable *tpALifeSchedulable, const int &iGroupIndex, const bool &bMutualDetection) +{ + return (ALife::eMeetActionTypeIgnore); +} + +void CALifeMonsterBrain::on_register () +{ +} + +void CALifeMonsterBrain::on_unregister () +{ +} + +void CALifeMonsterBrain::on_location_change () +{ +} + +IC CSE_ALifeSmartZone &CALifeMonsterBrain::smart_terrain () +{ + VERIFY2 (object().m_smart_terrain_id != 0xffff, object().name()); + if (m_smart_terrain && (object().m_smart_terrain_id == m_smart_terrain->ID)) + return (*m_smart_terrain); + + m_smart_terrain = ai().alife().smart_terrains().object(object().m_smart_terrain_id); + VERIFY2 (m_smart_terrain, object().name()); + return (*m_smart_terrain); +} + +void CALifeMonsterBrain::process_task () +{ + CALifeSmartTerrainTask *task = smart_terrain().task(&object()); + VERIFY2 (task,make_string("smart terrain [%s] returned nil task, while npc [%s] is registered in it",smart_terrain().name_replace(), object().name())); + movement().path_type (MovementManager::ePathTypeGamePath); + movement().detail().target (*task); +} + +void CALifeMonsterBrain::select_task (bool force) +{ + if (!force) + { + if (object().m_smart_terrain_id != 0xffff) + return; + } + if (!can_choose_alife_tasks()) + return; + + ALife::_TIME_ID current_time = ai().alife().time_manager().game_time(); + + if (!force) + { + if (m_last_search_time + m_time_interval > current_time) + return; + } + m_last_search_time = current_time; + + float best_value = flt_min; + CALifeSmartTerrainRegistry::OBJECTS::const_iterator I = ai().alife().smart_terrains().objects().begin(); + CALifeSmartTerrainRegistry::OBJECTS::const_iterator E = ai().alife().smart_terrains().objects().end(); + for ( ; I != E; ++I) { + if (!(*I).second->enabled(&object())) + continue; + + float value = (*I).second->suitable(&object()); + if (value > best_value) { + best_value = value; + object().m_smart_terrain_id = (*I).second->ID; + } + } + + if (object().m_smart_terrain_id != 0xffff) { + smart_terrain().register_npc (&object()); + m_last_search_time = 0; + } +} + +void CALifeMonsterBrain::update (bool force) +{ +#if 0//def DEBUG + if (!Level().MapManager().HasMapLocation("debug_stalker",object().ID)) { + CMapLocation *map_location = + Level().MapManager().AddMapLocation( + "debug_stalker", + object().ID + ); + + map_location->SetHint (object().name_replace()); + } +#endif + + select_task (force); + + if (object().m_smart_terrain_id != 0xffff) + process_task (); + else + default_behaviour (); + + movement().update (); +} + +void CALifeMonsterBrain::default_behaviour () +{ + movement().path_type (MovementManager::ePathTypeNoPath); +} + +void CALifeMonsterBrain::on_switch_online () +{ + movement().on_switch_online (); +} + +void CALifeMonsterBrain::on_switch_offline () +{ + movement().on_switch_offline (); +} + +#endif // XRGAME_EXPORTS \ No newline at end of file diff --git a/src/xrGameLA/alife_monster_brain.h b/src/xrGameLA/alife_monster_brain.h new file mode 100644 index 000000000..2a6bd8295 --- /dev/null +++ b/src/xrGameLA/alife_monster_brain.h @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_brain.h +// Created : 06.10.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster brain class +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "game_graph_space.h" +#include "xrserver_space.h" +#include "alife_space.h" +#include "script_export_space.h" + +class CSE_ALifeMonsterAbstract; +class CALifeMonsterMovementManager; +class CSE_ALifeSmartZone; +class NET_Packet; + +class CALifeMonsterBrain { +public: + typedef CSE_ALifeMonsterAbstract object_type; + typedef CALifeMonsterMovementManager movement_manager_type; + +private: + object_type *m_object; + movement_manager_type *m_movement_manager; + bool m_can_choose_alife_tasks; + +public: + CSE_ALifeSmartZone *m_smart_terrain; + ALife::_TIME_ID m_last_search_time; + ALife::_TIME_ID m_time_interval; + +// sad, but true +public: + void select_task (bool force = false); + +private: + void process_task (); + void default_behaviour (); + IC bool can_choose_alife_tasks () const; + +public: + CALifeMonsterBrain (object_type *object); + virtual ~CALifeMonsterBrain (); + +public: + void on_state_write (NET_Packet &packet); + void on_state_read (NET_Packet &packet); + void on_register (); + void on_unregister (); + void on_location_change (); + void on_switch_online (); + void on_switch_offline (); + +public: + void update (bool force = false); + bool perform_attack (); + ALife::EMeetActionType action_type (CSE_ALifeSchedulable *tpALifeSchedulable, const int &iGroupIndex, const bool &bMutualDetection); + +public: + IC object_type &object () const; + IC movement_manager_type &movement () const; + IC CSE_ALifeSmartZone &smart_terrain (); + IC void can_choose_alife_tasks (bool value); + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CALifeMonsterBrain) +#undef script_type_list +#define script_type_list save_type_list(CALifeMonsterBrain) + +#include "alife_monster_brain_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_monster_brain_inline.h b/src/xrGameLA/alife_monster_brain_inline.h new file mode 100644 index 000000000..376f4b724 --- /dev/null +++ b/src/xrGameLA/alife_monster_brain_inline.h @@ -0,0 +1,31 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_brain_inline.h +// Created : 06.10.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster brain class inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CALifeMonsterBrain::object_type &CALifeMonsterBrain::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +IC CALifeMonsterBrain::movement_manager_type &CALifeMonsterBrain::movement () const +{ + VERIFY (m_movement_manager); + return (*m_movement_manager); +} + +IC bool CALifeMonsterBrain::can_choose_alife_tasks () const +{ + return (m_can_choose_alife_tasks); +} + +IC void CALifeMonsterBrain::can_choose_alife_tasks (bool value) +{ + m_can_choose_alife_tasks = value; +} diff --git a/src/xrGameLA/alife_monster_brain_script.cpp b/src/xrGameLA/alife_monster_brain_script.cpp new file mode 100644 index 000000000..b1f8689f5 --- /dev/null +++ b/src/xrGameLA/alife_monster_brain_script.cpp @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_brain_script.cpp +// Created : 02.11.2005 +// Modified : 02.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster brain class script export +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "alife_monster_brain.h" +#include "alife_monster_movement_manager.h" + +using namespace luabind; + +CALifeMonsterMovementManager *get_movement (const CALifeMonsterBrain *brain) +{ + return (&brain->movement()); +} + +#pragma optimize("s",on) +void CALifeMonsterBrain::script_register (lua_State *L) +{ + module(L) + [ + class_("CALifeMonsterBrain") + .def("movement", &get_movement) + .def("update", &CALifeMonsterBrain::update) + .def("can_choose_alife_tasks", (void (CALifeMonsterBrain::*)(bool))&CALifeMonsterBrain::can_choose_alife_tasks) + ]; +} diff --git a/src/xrGameLA/alife_monster_detail_path_manager.cpp b/src/xrGameLA/alife_monster_detail_path_manager.cpp new file mode 100644 index 000000000..154da19b4 --- /dev/null +++ b/src/xrGameLA/alife_monster_detail_path_manager.cpp @@ -0,0 +1,274 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_detail_path_manager.cpp +// Created : 01.11.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster detail path manager class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_monster_detail_path_manager.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_time_manager.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "game_graph.h" +#include "level_graph.h" +#include "game_level_cross_table.h" +#include "alife_smart_terrain_task.h" +#include "alife_graph_registry.h" +#include "graph_engine.h" +#include "alife_monster_brain.h" + +CALifeMonsterDetailPathManager::CALifeMonsterDetailPathManager (object_type *object) +{ + VERIFY (object); + m_object = object; + m_last_update_time = 0; + m_destination.m_game_vertex_id = this->object().m_tGraphID; + m_destination.m_level_vertex_id = this->object().m_tNodeID; + m_destination.m_position = this->object().o_Position; + m_walked_distance = 0.f; +} + +void CALifeMonsterDetailPathManager::target (const GameGraph::_GRAPH_ID &game_vertex_id, const u32 &level_vertex_id, const Fvector &position) +{ + VERIFY (ai().game_graph().valid_vertex_id(game_vertex_id)); + VERIFY ((ai().game_graph().vertex(game_vertex_id)->level_id() != ai().alife().graph().level().level_id()) || ai().level_graph().valid_vertex_id(level_vertex_id)); + VERIFY ((ai().game_graph().vertex(game_vertex_id)->level_id() != ai().alife().graph().level().level_id()) || (ai().cross_table().vertex(level_vertex_id).game_vertex_id() == game_vertex_id)); + VERIFY ((ai().game_graph().vertex(game_vertex_id)->level_id() != ai().alife().graph().level().level_id()) || ai().level_graph().inside(level_vertex_id,position)); + + m_destination.m_game_vertex_id = game_vertex_id; + m_destination.m_level_vertex_id = level_vertex_id; + m_destination.m_position = position; + +// Msg ("[%6d][%s][%f][%f][%f]",Device.dwTimeGlobal,object().name_replace(),VPUSH(m_destination.m_position)); +} + +void CALifeMonsterDetailPathManager::target (const GameGraph::_GRAPH_ID &game_vertex_id) +{ + VERIFY (ai().game_graph().valid_vertex_id(game_vertex_id)); + target (game_vertex_id,ai().game_graph().vertex(game_vertex_id)->level_vertex_id(),ai().game_graph().vertex(game_vertex_id)->level_point()); +} + +void CALifeMonsterDetailPathManager::target (const CALifeSmartTerrainTask &task) +{ + target (task.game_vertex_id(),task.level_vertex_id(),task.position()); +} + +void CALifeMonsterDetailPathManager::target (const CALifeSmartTerrainTask *task) +{ +// Msg ("[%6d][%s][%s]",Device.dwTimeGlobal,object().name_replace(),*task->patrol_path_name()); + target (*task); +} + +bool CALifeMonsterDetailPathManager::completed () const +{ + if (m_destination.m_game_vertex_id != object().m_tGraphID) + return (false); + + if (m_destination.m_level_vertex_id != object().m_tNodeID) + return (false); + + return (true); +} + +bool CALifeMonsterDetailPathManager::actual () const +{ + if (failed()) + return (false); + + if (m_destination.m_game_vertex_id != m_path.front()) + return (false); + + return (true); +} + +bool CALifeMonsterDetailPathManager::failed () const +{ + return (m_path.empty()); +} + +void CALifeMonsterDetailPathManager::update () +{ + ALife::_TIME_ID current_time = ai().alife().time_manager().game_time(); + if (current_time <= m_last_update_time) + return; + +// if (ai().game_graph().vertex(object().m_tGraphID)->level_id() == ai().level_graph().level_id()) +// Msg ("[detail::update][%6d][%s]",Device.dwTimeGlobal,object().name_replace()); + + ALife::_TIME_ID time_delta = current_time - m_last_update_time; + update (time_delta); + // we advisedly "lost" time we need to process a query to avoid some undesirable effects + m_last_update_time = ai().alife().time_manager().game_time(); +} + +void CALifeMonsterDetailPathManager::actualize () +{ + m_path.clear (); + + typedef GraphEngineSpace::CGameVertexParams CGameVertexParams; + CGameVertexParams temp = CGameVertexParams(object().m_tpaTerrain); + bool failed = + !ai().graph_engine().search ( + ai().game_graph(), + object().m_tGraphID, + m_destination.m_game_vertex_id, + &m_path, + temp + ); + +#ifdef DEBUG + if (failed) { + Msg ("! %s couldn't build game path from",object().name_replace()); + { + const CGameGraph::CVertex *vertex = ai().game_graph().vertex(object().m_tGraphID); + Msg ( + "! [%d][%s][%f][%f][%f]", + object().m_tGraphID, + *ai().game_graph().header().level( + vertex->level_id() + ).name(), + VPUSH(vertex->level_point()) + ); + } + { + const CGameGraph::CVertex *vertex = ai().game_graph().vertex(m_destination.m_game_vertex_id); + Msg ( + "! [%d][%s][%f][%f][%f]", + m_destination.m_game_vertex_id, + *ai().game_graph().header().level( + vertex->level_id() + ).name(), + VPUSH(vertex->level_point()) + ); + } + } +#endif + if (failed) + return; + + VERIFY (!m_path.empty()); + + if (m_path.size() == 1) { + VERIFY (m_path.back() == object().m_tGraphID); + return; + } + + m_walked_distance = 0.f; + std::reverse (m_path.begin(),m_path.end()); + VERIFY (m_path.back() == object().m_tGraphID); +} + +void CALifeMonsterDetailPathManager::update (const ALife::_TIME_ID &time_delta) +{ + // first update has enormous time delta, therefore just skip it + if (!m_last_update_time) + return; + + if (completed()) + return; + + if (!actual()) { + actualize (); + + if (failed()) + return; + } + + follow_path (time_delta); +} + +void CALifeMonsterDetailPathManager::setup_current_speed () +{ + if (ai().game_graph().vertex(object().m_tGraphID)->level_id() == ai().level_graph().level_id()) + speed (object().m_fCurrentLevelGoingSpeed); + else + speed (object().m_fGoingSpeed); +} + +void CALifeMonsterDetailPathManager::follow_path (const ALife::_TIME_ID &time_delta) +{ + VERIFY (!completed()); + VERIFY (!failed()); + VERIFY (actual()); + VERIFY (!m_path.empty()); + VERIFY (m_path.back() == object().m_tGraphID); + + if (m_path.size() == 1) { + VERIFY (object().m_tGraphID == m_destination.m_game_vertex_id); + m_walked_distance = 0.f; + object().m_tNodeID = m_destination.m_level_vertex_id; + object().o_Position = m_destination.m_position; +#ifdef DEBUG + object().m_fDistanceFromPoint = 0.f; + object().m_fDistanceToPoint = 0.f; + object().m_tNextGraphID = object().m_tGraphID; +#endif + return; + } + + float last_time_delta = float(time_delta)/1000.f; + for ( ; m_path.size() > 1;) { + setup_current_speed (); + float update_distance = (last_time_delta/ai().alife().time_manager().normal_time_factor())*speed(); + + float distance_between = ai().game_graph().distance(object().m_tGraphID,(GameGraph::_GRAPH_ID)m_path[m_path.size() - 2]); + if (distance_between > (update_distance + m_walked_distance)) { + m_walked_distance += update_distance; +#ifdef DEBUG + object().m_fDistanceFromPoint = m_walked_distance; + object().m_fDistanceToPoint = distance_between; + object().m_tNextGraphID = (GameGraph::_GRAPH_ID)m_path[m_path.size() - 2]; +#endif + return; + } + + update_distance += m_walked_distance; + update_distance -= distance_between; + + last_time_delta = update_distance*ai().alife().time_manager().normal_time_factor()/speed(); + + m_walked_distance = 0.f; + m_path.pop_back (); +// Msg ("%6d %s changes graph point from %d to %d",Device.dwTimeGlobal,object().name_replace(),object().m_tGraphID,(GameGraph::_GRAPH_ID)m_path.back()); + object().alife().graph().change (&object(),object().m_tGraphID,(GameGraph::_GRAPH_ID)m_path.back()); + VERIFY (m_path.back() == object().m_tGraphID); + object().brain().on_location_change (); + VERIFY (m_path.back() == object().m_tGraphID); + } +} + +void CALifeMonsterDetailPathManager::on_switch_online () +{ + m_path.clear (); +} + +void CALifeMonsterDetailPathManager::on_switch_offline () +{ + m_path.clear (); +} + +Fvector CALifeMonsterDetailPathManager::draw_level_position () const +{ + if (path().empty()) + return (object().Position()); + + u32 path_size = path().size(); + if (path_size == 1) + return (object().Position()); + + VERIFY (m_path.back() == object().m_tGraphID); + + const GameGraph::CVertex *current = ai().game_graph().vertex(object().m_tGraphID); + const GameGraph::CVertex *next = ai().game_graph().vertex(m_path[path_size - 2]); + if (current->level_id() != next->level_id()) + return (object().Position()); + + Fvector current_vertex = current->level_point(); + Fvector next_vertex = next->level_point(); + Fvector direction = Fvector().sub(next_vertex,current_vertex); + direction.normalize (); + return (current_vertex.mad(direction,walked_distance())); +} diff --git a/src/xrGameLA/alife_monster_detail_path_manager.h b/src/xrGameLA/alife_monster_detail_path_manager.h new file mode 100644 index 000000000..37cfc3c62 --- /dev/null +++ b/src/xrGameLA/alife_monster_detail_path_manager.h @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_detail_path_manager.h +// Created : 01.11.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster detail path manager class +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "game_graph_space.h" +#include "alife_space.h" +#include "script_export_space.h" + +class CSE_ALifeMonsterAbstract; +class CALifeSmartTerrainTask; + +class CALifeMonsterDetailPathManager { +public: + typedef CSE_ALifeMonsterAbstract object_type; + typedef xr_vector PATH; + +private: + struct parameters { + GameGraph::_GRAPH_ID m_game_vertex_id; + u32 m_level_vertex_id; + Fvector m_position; + }; + +private: + object_type *m_object; + ALife::_TIME_ID m_last_update_time; + parameters m_destination; + float m_walked_distance; + float m_speed; + +private: + PATH m_path; + // this is INVERTED path, i.e. + // start vertex is the last one + // destination vertex is the first one. + // this is useful, since iterating back + // on this vector during path following + // we just repeatedly remove the last + // vertex, and this operation is + // efficiently implemented in std::vector + +private: + void actualize (); + void setup_current_speed (); + void follow_path (const ALife::_TIME_ID &time_delta); + void update (const ALife::_TIME_ID &time_delta); + +public: + CALifeMonsterDetailPathManager (object_type *object); + IC object_type &object () const; + +public: + void target (const GameGraph::_GRAPH_ID &game_vertex_id, const u32 &level_vertex_id, const Fvector &position); + void target (const GameGraph::_GRAPH_ID &game_vertex_id); + void target (const CALifeSmartTerrainTask &task); + void target (const CALifeSmartTerrainTask *task); + +public: + void update (); + void on_switch_online (); + void on_switch_offline (); + IC void speed (const float &speed); + +public: + IC const float &speed () const; + bool completed () const; + bool actual () const; + bool failed () const; + IC const PATH &path () const; + IC const float &walked_distance () const; + Fvector draw_level_position () const; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CALifeMonsterDetailPathManager) +#undef script_type_list +#define script_type_list save_type_list(CALifeMonsterDetailPathManager) + +#include "alife_monster_detail_path_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_monster_detail_path_manager_inline.h b/src/xrGameLA/alife_monster_detail_path_manager_inline.h new file mode 100644 index 000000000..695aa51d6 --- /dev/null +++ b/src/xrGameLA/alife_monster_detail_path_manager_inline.h @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_detail_path_manager_inline.h +// Created : 01.11.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster detail path manager class inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CALifeMonsterDetailPathManager::object_type &CALifeMonsterDetailPathManager::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +IC void CALifeMonsterDetailPathManager::speed (const float &speed) +{ + VERIFY (_valid(speed)); + m_speed = speed; +} + +IC const float &CALifeMonsterDetailPathManager::speed () const +{ + VERIFY (_valid(m_speed)); + return (m_speed); +} + +IC const CALifeMonsterDetailPathManager::PATH &CALifeMonsterDetailPathManager::path () const +{ + return (m_path); +} + +IC const float &CALifeMonsterDetailPathManager::walked_distance () const +{ + VERIFY (path().size() > 1); + return (m_walked_distance); +} diff --git a/src/xrGameLA/alife_monster_detail_path_manager_script.cpp b/src/xrGameLA/alife_monster_detail_path_manager_script.cpp new file mode 100644 index 000000000..9afc427b3 --- /dev/null +++ b/src/xrGameLA/alife_monster_detail_path_manager_script.cpp @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_brain_script.cpp +// Created : 02.11.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster detail path manager class script export +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "alife_monster_detail_path_manager.h" +#include "alife_smart_terrain_task.h" + +using namespace luabind; + +#pragma optimize("s",on) +void CALifeMonsterDetailPathManager::script_register (lua_State *L) +{ + module(L) + [ + class_("CALifeMonsterDetailPathManager") + .def("target", (void (CALifeMonsterDetailPathManager::*)(const GameGraph::_GRAPH_ID &, const u32 &, const Fvector &))(&CALifeMonsterDetailPathManager::target)) + .def("target", (void (CALifeMonsterDetailPathManager::*)(const GameGraph::_GRAPH_ID &))(&CALifeMonsterDetailPathManager::target)) + .def("target", (void (CALifeMonsterDetailPathManager::*)(const CALifeSmartTerrainTask *))(&CALifeMonsterDetailPathManager::target)) + .def("speed ", (void (CALifeMonsterDetailPathManager::*)(const float &))(&CALifeMonsterDetailPathManager::speed)) + .def("speed ", (const float &(CALifeMonsterDetailPathManager::*)() const)(&CALifeMonsterDetailPathManager::speed)) + .def("completed", &CALifeMonsterDetailPathManager::completed) + .def("actual", &CALifeMonsterDetailPathManager::actual) + .def("failed", &CALifeMonsterDetailPathManager::failed) + ]; +} diff --git a/src/xrGameLA/alife_monster_movement_manager.cpp b/src/xrGameLA/alife_monster_movement_manager.cpp new file mode 100644 index 000000000..5fd805dc0 --- /dev/null +++ b/src/xrGameLA/alife_monster_movement_manager.cpp @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_movement_manager.cpp +// Created : 31.10.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster movement manager class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_monster_movement_manager.h" +#include "alife_monster_detail_path_manager.h" +#include "alife_monster_patrol_path_manager.h" +#include "object_broker.h" +#include "movement_manager_space.h" + +CALifeMonsterMovementManager::CALifeMonsterMovementManager (object_type *object) +{ + VERIFY (object); + m_object = object; + m_detail = new detail_path_type(object); + m_patrol = new patrol_path_type(object); + m_path_type = MovementManager::ePathTypeNoPath; +} + +CALifeMonsterMovementManager::~CALifeMonsterMovementManager () +{ + delete_data (m_detail); + delete_data (m_patrol); +} + +bool CALifeMonsterMovementManager::completed () const +{ + return (true); +} + +bool CALifeMonsterMovementManager::actual () const +{ + return (true); +} + +void CALifeMonsterMovementManager::update () +{ + switch (path_type()) { + case MovementManager::ePathTypeGamePath : { + detail().update (); + break; + }; + case MovementManager::ePathTypePatrolPath : { + patrol().update (); + + detail().target ( + patrol().target_game_vertex_id(), + patrol().target_level_vertex_id(), + patrol().target_position() + ); + + detail().update (); + + break; + }; + case MovementManager::ePathTypeNoPath : { + break; + }; + default : NODEFAULT; + }; +} + +void CALifeMonsterMovementManager::on_switch_online () +{ + detail().on_switch_online (); +} + +void CALifeMonsterMovementManager::on_switch_offline () +{ + detail().on_switch_offline (); +} diff --git a/src/xrGameLA/alife_monster_movement_manager.h b/src/xrGameLA/alife_monster_movement_manager.h new file mode 100644 index 000000000..f40d63db4 --- /dev/null +++ b/src/xrGameLA/alife_monster_movement_manager.h @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_movement_manager.h +// Created : 31.10.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster movement manager class +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "script_export_space.h" + +class CSE_ALifeMonsterAbstract; +class CALifeMonsterDetailPathManager; +class CALifeMonsterPatrolPathManager; + +namespace MovementManager { + enum EPathType; +}; + +class CALifeMonsterMovementManager { +public: + typedef CSE_ALifeMonsterAbstract object_type; + typedef CALifeMonsterDetailPathManager detail_path_type; + typedef CALifeMonsterPatrolPathManager patrol_path_type; + typedef MovementManager::EPathType EPathType; + +private: + object_type *m_object; + detail_path_type *m_detail; + patrol_path_type *m_patrol; + EPathType m_path_type; + +public: + CALifeMonsterMovementManager (object_type *object); + ~CALifeMonsterMovementManager (); + IC object_type &object () const; + IC detail_path_type &detail () const; + IC patrol_path_type &patrol () const; + IC const EPathType &path_type () const; + +public: + void update (); + void on_switch_online (); + void on_switch_offline (); + IC void path_type (const EPathType &path_type); + +public: + bool completed () const; + bool actual () const; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CALifeMonsterMovementManager) +#undef script_type_list +#define script_type_list save_type_list(CALifeMonsterMovementManager) + +#include "alife_monster_movement_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_monster_movement_manager_inline.h b/src/xrGameLA/alife_monster_movement_manager_inline.h new file mode 100644 index 000000000..30cef006f --- /dev/null +++ b/src/xrGameLA/alife_monster_movement_manager_inline.h @@ -0,0 +1,37 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_movement_manager_inline.h +// Created : 31.10.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster movement manager class inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CALifeMonsterMovementManager::object_type &CALifeMonsterMovementManager::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +IC CALifeMonsterMovementManager::detail_path_type &CALifeMonsterMovementManager::detail () const +{ + VERIFY (m_detail); + return (*m_detail); +} + +IC CALifeMonsterMovementManager::patrol_path_type &CALifeMonsterMovementManager::patrol () const +{ + VERIFY (m_patrol); + return (*m_patrol); +} + +IC const CALifeMonsterMovementManager::EPathType &CALifeMonsterMovementManager::path_type () const +{ + return (m_path_type); +} + +IC void CALifeMonsterMovementManager::path_type (const EPathType &path_type) +{ + m_path_type = path_type; +} diff --git a/src/xrGameLA/alife_monster_movement_manager_script.cpp b/src/xrGameLA/alife_monster_movement_manager_script.cpp new file mode 100644 index 000000000..3ed3045db --- /dev/null +++ b/src/xrGameLA/alife_monster_movement_manager_script.cpp @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_movement_manager_script.cpp +// Created : 02.11.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster movement manager class script export +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "alife_monster_movement_manager.h" +#include "alife_monster_detail_path_manager.h" +#include "alife_monster_patrol_path_manager.h" + +using namespace luabind; + +CALifeMonsterDetailPathManager *get_detail(const CALifeMonsterMovementManager *self) +{ + return (&self->detail()); +} + +CALifeMonsterPatrolPathManager *get_patrol(const CALifeMonsterMovementManager *self) +{ + return (&self->patrol()); +} + +#pragma optimize("s",on) +void CALifeMonsterMovementManager::script_register (lua_State *L) +{ + module(L) + [ + class_("CALifeMonsterMovementManager") + .def("detail", &get_detail) + .def("patrol", &get_patrol) + .def("path_type", (void (CALifeMonsterMovementManager::*)(const EPathType &))(&CALifeMonsterMovementManager::path_type)) + .def("path_type", (const EPathType & (CALifeMonsterMovementManager::*)() const)(&CALifeMonsterMovementManager::path_type)) + .def("actual", &CALifeMonsterMovementManager::actual) + .def("completed", &CALifeMonsterMovementManager::completed) + ]; +} diff --git a/src/xrGameLA/alife_monster_patrol_path_manager.cpp b/src/xrGameLA/alife_monster_patrol_path_manager.cpp new file mode 100644 index 000000000..be503f5ec --- /dev/null +++ b/src/xrGameLA/alife_monster_patrol_path_manager.cpp @@ -0,0 +1,212 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_patrol_path_manager.cpp +// Created : 01.11.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster patrol path manager class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_monster_patrol_path_manager.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "ai_space.h" +#include "patrol_path_storage.h" +#include "patrol_path.h" +#include "patrol_point.h" +#include "patrol_path_manager_space.h" +#include "game_graph.h" + +CALifeMonsterPatrolPathManager::CALifeMonsterPatrolPathManager (object_type *object) +{ + VERIFY (object); + m_object = object; + + m_path = 0; + + m_actual = true; + m_completed = true; + + m_current_vertex_index = u32(-1); + m_previous_vertex_index = u32(-1); + m_start_vertex_index = u32(-1); + + start_type (PatrolPathManager::ePatrolStartTypeNearest); + route_type (PatrolPathManager::ePatrolRouteTypeContinue); + use_randomness (true); +} + +void CALifeMonsterPatrolPathManager::path (const shared_str &path_name) +{ + path (ai().patrol_paths().path(path_name)); +} + +const CALifeMonsterPatrolPathManager::_GRAPH_ID &CALifeMonsterPatrolPathManager::target_game_vertex_id () const +{ + return ( + path().vertex( + m_current_vertex_index + )->data().game_vertex_id() + ); +} + +const u32 &CALifeMonsterPatrolPathManager::target_level_vertex_id () const +{ + return ( + path().vertex( + m_current_vertex_index + )->data().level_vertex_id() + ); +} + +const Fvector &CALifeMonsterPatrolPathManager::target_position () const +{ + return ( + path().vertex( + m_current_vertex_index + )->data().position() + ); +} + +void CALifeMonsterPatrolPathManager::select_nearest () +{ + m_current_vertex_index = u32(-1); + Fvector global_position = ai().game_graph().vertex(object().m_tGraphID)->game_point(); + float best_distance = flt_max; + CPatrolPath::const_vertex_iterator I = path().vertices().begin(); + CPatrolPath::const_vertex_iterator E = path().vertices().end(); + for ( ; I != E; ++I) { + if ((*I).second->data().game_vertex_id() == object().m_tGraphID) { + m_current_vertex_index = (*I).second->vertex_id(); + break; + } + + float distance = + global_position.distance_to( + ai().game_graph().vertex((*I).second->data().game_vertex_id())->game_point() + ); + + if (distance >= best_distance) + continue; + + best_distance = distance; + m_current_vertex_index = (*I).second->vertex_id(); + } + + VERIFY (m_current_vertex_index < path().vertices().size()); +} + +void CALifeMonsterPatrolPathManager::actualize () +{ + m_current_vertex_index = u32(-1); + m_previous_vertex_index = u32(-1); + m_actual = true; + m_completed = false; + + switch (start_type()) { + case PatrolPathManager::ePatrolStartTypeFirst : { + m_current_vertex_index = 0; + break; + } + case PatrolPathManager::ePatrolStartTypeLast : { + m_current_vertex_index = path().vertices().size() - 1; + break; + } + case PatrolPathManager::ePatrolStartTypeNearest : { + select_nearest (); + break; + } + case PatrolPathManager::ePatrolStartTypePoint : { + m_current_vertex_index = m_start_vertex_index; + break; + } + case PatrolPathManager::ePatrolStartTypeNext : + // we advisedly do not process this case since it is far-fetched + default : NODEFAULT; + }; + + VERIFY (path().vertices().size() > m_current_vertex_index); +} + +bool CALifeMonsterPatrolPathManager::location_reached () const +{ + if (object().m_tGraphID != target_game_vertex_id()) + return (false); + + if (object().m_tNodeID != target_level_vertex_id()) + return (false); + + return (true); +} + +void CALifeMonsterPatrolPathManager::navigate () +{ + const CPatrolPath::CVertex &vertex = *path().vertex(m_current_vertex_index); + + typedef CPatrolPath::CVertex::EDGES EDGES; + EDGES::const_iterator I = vertex.edges().begin(), B = I; + EDGES::const_iterator E = vertex.edges().end(); + + u32 branching_factor = 0; + for ( ; I != E; ++I) { + if (*I == m_previous_vertex_index) + continue; + + ++branching_factor; + } + + if (!branching_factor) { + switch (route_type()) { + case PatrolPathManager::ePatrolRouteTypeStop : { + VERIFY (!m_completed); + m_completed = true; + break; + }; + case PatrolPathManager::ePatrolRouteTypeContinue : { + if (vertex.edges().empty()) { + VERIFY (!m_completed); + m_completed = true; + break; + } + + VERIFY (vertex.edges().size() == 1); + VERIFY (vertex.edges().front().vertex_id() == m_previous_vertex_index); + std::swap (m_current_vertex_index,m_previous_vertex_index); + break; + }; + default : NODEFAULT; + }; + } + + u32 chosen = use_randomness() ? object().randI(branching_factor) : 0; + u32 branch = 0; + for (I = B; I != E; ++I) { + if (*I == m_previous_vertex_index) + continue; + + if (chosen == branch) + break; + + ++branch; + } + + VERIFY (I != E); + m_previous_vertex_index = m_current_vertex_index; + m_current_vertex_index = (*I).vertex_id(); +} + +void CALifeMonsterPatrolPathManager::update () +{ + if (!m_path) + return; + + if (completed()) + return; + + if (!actual()) + actualize (); + + if (!location_reached()) + return; + + navigate (); +} diff --git a/src/xrGameLA/alife_monster_patrol_path_manager.h b/src/xrGameLA/alife_monster_patrol_path_manager.h new file mode 100644 index 000000000..745cdf63b --- /dev/null +++ b/src/xrGameLA/alife_monster_patrol_path_manager.h @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_patrol_path_manager.h +// Created : 01.11.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster patrol path manager class +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "game_graph_space.h" +#include "script_export_space.h" + +class CSE_ALifeMonsterAbstract; +class CPatrolPath; + +namespace PatrolPathManager { + enum EPatrolStartType; + enum EPatrolRouteType; +}; + +class CALifeMonsterPatrolPathManager { +public: + typedef CSE_ALifeMonsterAbstract object_type; + typedef PatrolPathManager::EPatrolStartType EPatrolStartType; + typedef PatrolPathManager::EPatrolRouteType EPatrolRouteType; + typedef GameGraph::_GRAPH_ID _GRAPH_ID; + +private: + object_type *m_object; + +private: + const CPatrolPath *m_path; + EPatrolStartType m_start_type; + EPatrolRouteType m_route_type; + bool m_use_randomness; + u32 m_start_vertex_index; + +private: + bool m_actual; + bool m_completed; + u32 m_current_vertex_index; + u32 m_previous_vertex_index; + +private: + void select_nearest (); + void actualize (); + bool location_reached () const; + void navigate (); + +public: + CALifeMonsterPatrolPathManager(object_type *object); + void update (); + void path (const shared_str &path_name); + +public: + IC object_type &object () const; + IC void path (const CPatrolPath *path); + IC void path (LPCSTR path_name); + IC void start_type (const EPatrolStartType &start_type); + IC void route_type (const EPatrolRouteType &route_type); + IC const EPatrolStartType &start_type () const; + IC const EPatrolRouteType &route_type () const; + IC bool actual () const; + IC bool completed () const; + IC const CPatrolPath &path () const; + IC void start_vertex_index (const u32 &start_vertex_index); + IC bool use_randomness () const; + IC void use_randomness (const bool &use_randomness); + const _GRAPH_ID &target_game_vertex_id () const; + const u32 &target_level_vertex_id () const; + const Fvector &target_position () const; + + DECLARE_SCRIPT_REGISTER_FUNCTION +}; +add_to_type_list(CALifeMonsterPatrolPathManager) +#undef script_type_list +#define script_type_list save_type_list(CALifeMonsterPatrolPathManager) + +#include "alife_monster_patrol_path_manager_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_monster_patrol_path_manager_inline.h b/src/xrGameLA/alife_monster_patrol_path_manager_inline.h new file mode 100644 index 000000000..a8682278b --- /dev/null +++ b/src/xrGameLA/alife_monster_patrol_path_manager_inline.h @@ -0,0 +1,77 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_patrol_path_manager_inline.h +// Created : 01.11.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster patrol path manager class inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CALifeMonsterPatrolPathManager::object_type &CALifeMonsterPatrolPathManager::object () const +{ + VERIFY (m_object); + return (*m_object); +} + +IC void CALifeMonsterPatrolPathManager::path (const CPatrolPath *path) +{ + m_actual = m_actual && (m_path == path); + m_path = path; +} + +IC void CALifeMonsterPatrolPathManager::path (LPCSTR path_name) +{ + path (shared_str(path_name)); +} + +IC bool CALifeMonsterPatrolPathManager::actual () const +{ + return (m_actual); +} + +IC bool CALifeMonsterPatrolPathManager::completed () const +{ + return (actual() && m_completed); +} + +IC void CALifeMonsterPatrolPathManager::start_type (const EPatrolStartType &start_type) +{ + m_start_type = start_type; +} + +IC void CALifeMonsterPatrolPathManager::route_type (const EPatrolRouteType &route_type) +{ + m_route_type = route_type; +} + +IC const CALifeMonsterPatrolPathManager::EPatrolStartType &CALifeMonsterPatrolPathManager::start_type () const +{ + return (m_start_type); +} + +IC const CALifeMonsterPatrolPathManager::EPatrolRouteType &CALifeMonsterPatrolPathManager::route_type () const +{ + return (m_route_type); +} + +IC const CPatrolPath &CALifeMonsterPatrolPathManager::path () const +{ + VERIFY (m_path); + return (*m_path); +} + +IC void CALifeMonsterPatrolPathManager::start_vertex_index (const u32 &start_vertex_index) +{ + m_start_vertex_index = start_vertex_index; +} + +IC bool CALifeMonsterPatrolPathManager::use_randomness () const +{ + return (m_use_randomness); +} + +IC void CALifeMonsterPatrolPathManager::use_randomness (const bool &use_randomness) +{ + m_use_randomness = use_randomness; +} diff --git a/src/xrGameLA/alife_monster_patrol_path_manager_script.cpp b/src/xrGameLA/alife_monster_patrol_path_manager_script.cpp new file mode 100644 index 000000000..ea0f80838 --- /dev/null +++ b/src/xrGameLA/alife_monster_patrol_path_manager_script.cpp @@ -0,0 +1,40 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_monster_patrol_path_manager_script.cpp +// Created : 02.11.2005 +// Modified : 22.11.2005 +// Author : Dmitriy Iassenev +// Description : ALife monster patrol path manager class script export +//////////////////////////////////////////////////////////////////////////// + +#include "pch_script.h" +#include "alife_monster_patrol_path_manager.h" + +using namespace luabind; + +Fvector CALifeMonsterPatrolPathManager__target_position (CALifeMonsterPatrolPathManager *self) +{ + THROW (self); + return (self->target_position()); +} + +#pragma optimize("s",on) +void CALifeMonsterPatrolPathManager::script_register (lua_State *L) +{ + module(L) + [ + class_("CALifeMonsterPatrolPathManager") + .def("path", (void (CALifeMonsterPatrolPathManager::*)(LPCSTR))(&CALifeMonsterPatrolPathManager::path)) + .def("start_type", (void (CALifeMonsterPatrolPathManager::*)(const EPatrolStartType &))(&CALifeMonsterPatrolPathManager::start_type)) + .def("start_type", (const EPatrolStartType &(CALifeMonsterPatrolPathManager::*)() const)(&CALifeMonsterPatrolPathManager::start_type)) + .def("route_type", (void (CALifeMonsterPatrolPathManager::*)(const EPatrolRouteType &))(&CALifeMonsterPatrolPathManager::route_type)) + .def("route_type", (const EPatrolRouteType &(CALifeMonsterPatrolPathManager::*)() const)(&CALifeMonsterPatrolPathManager::route_type)) + .def("actual", &CALifeMonsterPatrolPathManager::actual) + .def("completed", &CALifeMonsterPatrolPathManager::completed) + .def("start_vertex_index", &CALifeMonsterPatrolPathManager::start_vertex_index) + .def("use_randomness", (void (CALifeMonsterPatrolPathManager::*)(const bool &))(&CALifeMonsterPatrolPathManager::use_randomness)) + .def("use_randomness", (bool (CALifeMonsterPatrolPathManager::*)() const)(&CALifeMonsterPatrolPathManager::use_randomness)) + .def("target_game_vertex_id", &CALifeMonsterPatrolPathManager::target_game_vertex_id) + .def("target_level_vertex_id", &CALifeMonsterPatrolPathManager::target_level_vertex_id) + .def("target_position", &CALifeMonsterPatrolPathManager__target_position) + ]; +} diff --git a/src/xrGameLA/alife_object.cpp b/src/xrGameLA/alife_object.cpp new file mode 100644 index 000000000..6c323fcde --- /dev/null +++ b/src/xrGameLA/alife_object.cpp @@ -0,0 +1,110 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_object.cpp +// Created : 27.10.2005 +// Modified : 27.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife object class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "xrServer_Objects_ALife.h" +#include "alife_simulator.h" +#include "xrServer_Objects_ALife_Items.h" + +void CSE_ALifeObject::spawn_supplies () +{ + spawn_supplies(*m_ini_string); +} + +void CSE_ALifeObject::spawn_supplies (LPCSTR ini_string) +{ + if (!ini_string) + return; + + if (!xr_strlen(ini_string)) + return; + +#pragma warning(push) +#pragma warning(disable:4238) + CInifile ini( + &IReader ( + (void*)(ini_string), + xr_strlen(ini_string) + ), + FS.get_path("$game_config$")->m_Path + ); +#pragma warning(pop) + + if (ini.section_exist("spawn")) { + LPCSTR N,V; + float p; + for (u32 k = 0, j; ini.r_line("spawn",k,&N,&V); k++) { + VERIFY (xr_strlen(N)); + + float f_cond = 1.0f; + bool bScope = false; + bool bSilencer = false; + bool bLauncher = false; + + j = 1; + p = 1.f; + + if (V && xr_strlen(V)) { + string64 buf; + j = atoi(_GetItem(V, 0, buf)); + if (!j) j = 1; + + bScope = (NULL!=strstr(V,"scope")); + bSilencer = (NULL!=strstr(V,"silencer")); + bLauncher = (NULL!=strstr(V,"launcher")); + //probability + if (NULL != strstr(V,"prob=")) + p = (float)atof(strstr(V,"prob=")+5); + if (fis_zero(p)) p = 1.0f; + if (NULL != strstr(V,"cond=")) + f_cond = (float)atof(strstr(V,"cond=")+5); + } + spawn_item(N, j, p, f_cond, bScope, bLauncher, bSilencer); + } + } +} + +CSE_Abstract* CSE_ALifeObject::spawn_item(LPCSTR section) +{ + return alife().spawn_item(section,o_Position,m_tNodeID,m_tGraphID,ID); +} + +void CSE_ALifeObject::spawn_item(LPCSTR sect, int count, float prob, float cond, bool scope, bool launcher, bool silencer) +{ + for (u32 i = 0; i < count; ++i) + { + if (::Random.randF(1.f) < prob) + { + spawn_item(sect, cond, scope, launcher, silencer); + } + } +} + +CSE_Abstract* CSE_ALifeObject::spawn_item(LPCSTR sect, float cond, bool scope, bool launcher, bool silencer) +{ + CSE_Abstract* E = spawn_item(sect); + //подсоединить аддоны к оружию, если включены соответствующие флажки + CSE_ALifeItemWeapon* W = smart_cast(E); + if (W) { + if (W->m_scope_status == CSE_ALifeItemWeapon::eAddonAttachable) + W->m_addon_flags.set(CSE_ALifeItemWeapon::eWeaponAddonScope, scope); + if (W->m_silencer_status == CSE_ALifeItemWeapon::eAddonAttachable) + W->m_addon_flags.set(CSE_ALifeItemWeapon::eWeaponAddonSilencer, silencer); + if (W->m_grenade_launcher_status == CSE_ALifeItemWeapon::eAddonAttachable) + W->m_addon_flags.set(CSE_ALifeItemWeapon::eWeaponAddonGrenadeLauncher, launcher); + } + CSE_ALifeInventoryItem* IItem = smart_cast(E); + if (IItem) + IItem->m_fCondition = cond; + return E; +} + +bool CSE_ALifeObject::keep_saved_data_anyway() const +{ + return (false); +} diff --git a/src/xrGameLA/alife_object_registry.cpp b/src/xrGameLA/alife_object_registry.cpp new file mode 100644 index 000000000..9abf721e4 --- /dev/null +++ b/src/xrGameLA/alife_object_registry.cpp @@ -0,0 +1,143 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_object_registry.cpp +// Created : 15.01.2003 +// Modified : 12.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife object registry +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_object_registry.h" +#include "../../xrNetServer/net_utils.h" +#include "ai_debug.h" + +CALifeObjectRegistry::CALifeObjectRegistry (LPCSTR section) +{ +} + +CALifeObjectRegistry::~CALifeObjectRegistry () +{ + OBJECT_REGISTRY::iterator I = m_objects.begin(); + OBJECT_REGISTRY::iterator E = m_objects.end(); + for ( ; I != E; ++I) { + // hack, should be revisited in case of not intended behaviour + (*I).second->on_unregister (); + xr_delete ((*I).second); + } +} + +void CALifeObjectRegistry::save (IWriter &memory_stream, CSE_ALifeDynamicObject *object, u32 &object_count) +{ + ++object_count; + + NET_Packet tNetPacket; + // Spawn + object->Spawn_Write (tNetPacket,TRUE); + memory_stream.w_u16 (u16(tNetPacket.B.count)); + memory_stream.w (tNetPacket.B.data,tNetPacket.B.count); + + // Update + tNetPacket.w_begin (M_UPDATE); + object->UPDATE_Write (tNetPacket); + + memory_stream.w_u16 (u16(tNetPacket.B.count)); + memory_stream.w (tNetPacket.B.data,tNetPacket.B.count); + + ALife::OBJECT_VECTOR::const_iterator I = object->children.begin(); + ALife::OBJECT_VECTOR::const_iterator E = object->children.end(); + for ( ; I != E; ++I) { + CSE_ALifeDynamicObject *child = this->object(*I,true); + if (!child) + continue; + + if (!child->can_save()) + continue; + + save (memory_stream,child,object_count); + } +} + +void CALifeObjectRegistry::save (IWriter &memory_stream) +{ + Msg ("* Saving objects..."); + memory_stream.open_chunk (OBJECT_CHUNK_DATA); + + u32 position = memory_stream.tell(); + memory_stream.w_u32 (u32(-1)); + + u32 object_count = 0; + OBJECT_REGISTRY::iterator I = m_objects.begin(); + OBJECT_REGISTRY::iterator E = m_objects.end(); + for ( ; I != E; ++I) { + if (!(*I).second->can_save()) + continue; + + if ((*I).second->ID_Parent != 0xffff) + continue; + + save (memory_stream,(*I).second, object_count); + } + + u32 last_position = memory_stream.tell(); + memory_stream.seek (position); + memory_stream.w_u32 (object_count); + memory_stream.seek (last_position); + + memory_stream.close_chunk (); + + Msg ("* %d objects are successfully saved",object_count); +} + +CSE_ALifeDynamicObject *CALifeObjectRegistry::get_object (IReader &file_stream) +{ + NET_Packet tNetPacket; + u16 u_id; + // Spawn + tNetPacket.B.count = file_stream.r_u16(); + file_stream.r (tNetPacket.B.data,tNetPacket.B.count); + tNetPacket.r_begin (u_id); + R_ASSERT2 (M_SPAWN==u_id,"Invalid packet ID (!= M_SPAWN)"); + + string64 s_name; + tNetPacket.r_stringZ (s_name); +#ifdef DEBUG + if (psAI_Flags.test(aiALife)) { + Msg ("Loading object %s",s_name); + } +#endif + // create entity + CSE_Abstract *tpSE_Abstract = F_entity_Create (s_name); + R_ASSERT2 (tpSE_Abstract,"Can't create entity."); + CSE_ALifeDynamicObject *tpALifeDynamicObject = smart_cast(tpSE_Abstract); + R_ASSERT2 (tpALifeDynamicObject,"Non-ALife object in the saved game!"); + tpALifeDynamicObject->Spawn_Read(tNetPacket); + + // Update + tNetPacket.B.count = file_stream.r_u16(); + file_stream.r (tNetPacket.B.data,tNetPacket.B.count); + tNetPacket.r_begin (u_id); + R_ASSERT2 (M_UPDATE==u_id,"Invalid packet ID (!= M_UPDATE)"); + tpALifeDynamicObject->UPDATE_Read(tNetPacket); + + return (tpALifeDynamicObject); +} + +void CALifeObjectRegistry::load (IReader &file_stream) +{ + Msg ("* Loading objects..."); + R_ASSERT2 (file_stream.find_chunk(OBJECT_CHUNK_DATA),"Can't find chunk OBJECT_CHUNK_DATA!"); + + m_objects.clear (); + + u32 count = file_stream.r_u32(); + CSE_ALifeDynamicObject **objects = (CSE_ALifeDynamicObject**)_alloca(count*sizeof(CSE_ALifeDynamicObject*)); + + CSE_ALifeDynamicObject **I = objects; + CSE_ALifeDynamicObject **E = objects + count; + for ( ; I != E; ++I) { + *I = get_object(file_stream); + add (*I); + } + + Msg ("* %d objects are successfully loaded",count); +} diff --git a/src/xrGameLA/alife_object_registry.h b/src/xrGameLA/alife_object_registry.h new file mode 100644 index 000000000..022203079 --- /dev/null +++ b/src/xrGameLA/alife_object_registry.h @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_object_registry.h +// Created : 15.01.2003 +// Modified : 12.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife object registry +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "xrServer_Objects_ALife.h" +#include "profiler.h" + +#pragma warning(push) +#pragma warning(disable:4995) +#include +#pragma warning(pop) + +class CALifeObjectRegistry { +public: + typedef xr_map OBJECT_REGISTRY; + +protected: + OBJECT_REGISTRY m_objects; + +private: + void save (IWriter &memory_stream, CSE_ALifeDynamicObject *object, u32 &object_count); + +public: + static CSE_ALifeDynamicObject *get_object (IReader &file_stream); + +public: + CALifeObjectRegistry (LPCSTR section); + virtual ~CALifeObjectRegistry (); + virtual void save (IWriter &memory_stream); + void load (IReader &file_stream); + IC void add (CSE_ALifeDynamicObject *object); + IC void remove (const ALife::_OBJECT_ID &id, bool no_assert = false); + IC CSE_ALifeDynamicObject *object (const ALife::_OBJECT_ID &id, bool no_assert = false) const; + IC const OBJECT_REGISTRY &objects () const; + IC OBJECT_REGISTRY &objects (); +}; + +#include "alife_object_registry_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_object_registry_inline.h b/src/xrGameLA/alife_object_registry_inline.h new file mode 100644 index 000000000..9a36872bc --- /dev/null +++ b/src/xrGameLA/alife_object_registry_inline.h @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_object_registry_штдшту.h +// Created : 15.01.2003 +// Modified : 12.05.2004 +// Author : Dmitriy Iassenev +// Description : ALife object registry inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC void CALifeObjectRegistry::add (CSE_ALifeDynamicObject *object) +{ + if (objects().find(object->ID) != objects().end()) { + THROW2 ((*(objects().find(object->ID))).second == object,"The specified object is already presented in the Object Registry!"); + THROW2 ((*(objects().find(object->ID))).second != object,"Object with the specified ID is already presented in the Object Registry!"); + } + + m_objects.insert (std::make_pair(object->ID,object)); +} + +IC void CALifeObjectRegistry::remove (const ALife::_OBJECT_ID &id, bool no_assert) +{ + OBJECT_REGISTRY::iterator I = m_objects.find(id); + if (I == m_objects.end()) { + THROW2 (no_assert,"The specified object hasn't been found in the Object Registry!"); + return; + } + + m_objects.erase (I); +} + +IC CSE_ALifeDynamicObject *CALifeObjectRegistry::object (const ALife::_OBJECT_ID &id, bool no_assert) const +{ + START_PROFILE("ALife/objects::object") + OBJECT_REGISTRY::const_iterator I = objects().find(id); + + if (objects().end() == I) { +#ifdef DEBUG + if (!no_assert) + Msg ("There is no object with id %d!",id); +#endif + THROW2 (no_assert,"Specified object hasn't been found in the object registry!"); + return (0); + } + + return ((*I).second); + STOP_PROFILE +} + +IC const CALifeObjectRegistry::OBJECT_REGISTRY &CALifeObjectRegistry::objects () const +{ + return (m_objects); +} + +IC CALifeObjectRegistry::OBJECT_REGISTRY &CALifeObjectRegistry::objects () +{ + return (m_objects); +} diff --git a/src/xrGameLA/alife_online_offline_group.cpp b/src/xrGameLA/alife_online_offline_group.cpp new file mode 100644 index 000000000..026e8353c --- /dev/null +++ b/src/xrGameLA/alife_online_offline_group.cpp @@ -0,0 +1,281 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_online_offline_group.cpp +// Created : 25.10.2005 +// Modified : 25.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife Online Offline Group class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "xrServer_Objects_ALife_Monsters.h" +#include "ai_space.h" +#include "alife_simulator.h" +#include "alife_object_registry.h" +#include "alife_graph_registry.h" +#include "alife_schedule_registry.h" +#include "game_level_cross_table.h" +#include "level_graph.h" + +#pragma warning(push) +#pragma warning(disable:4995) +#include +#pragma warning(pop) + +CSE_ALifeItemWeapon *CSE_ALifeOnlineOfflineGroup::tpfGetBestWeapon (ALife::EHitType &tHitType, float &fHitPower) +{ + return (0); +} + +ALife::EMeetActionType CSE_ALifeOnlineOfflineGroup::tfGetActionType (CSE_ALifeSchedulable *tpALifeSchedulable, int iGroupIndex, bool bMutualDetection) +{ + return (ALife::eMeetActionTypeIgnore); +} + +bool CSE_ALifeOnlineOfflineGroup::bfActive () +{ + return (false); +} + +CSE_ALifeDynamicObject *CSE_ALifeOnlineOfflineGroup::tpfGetBestDetector () +{ + return (0); +} + +void CSE_ALifeOnlineOfflineGroup::update () +{ + return; +} + +void CSE_ALifeOnlineOfflineGroup::register_member (ALife::_OBJECT_ID member_id) +{ + VERIFY (m_members.find(member_id) == m_members.end()); + CSE_ALifeDynamicObject *object = ai().alife().objects().object(member_id); + CSE_ALifeHumanStalker *stalker = smart_cast(object); + VERIFY (stalker); + VERIFY (stalker->g_Alive()); + + VERIFY ((stalker->m_group_id == 0xffff) || (stalker->m_group_id == ID)); + stalker->m_group_id = ID; + + bool empty = m_members.empty(); + m_members.insert (std::make_pair(member_id,stalker)); + if (empty) { + o_Position = stalker->o_Position; + m_tNodeID = stalker->m_tNodeID; + m_tGraphID = stalker->m_tGraphID; + + m_flags.set (flUsedAI_Locations,TRUE); + + alife().graph().update (this); + } + + if (!object->m_bOnline) { + alife().graph().remove (object,object->m_tGraphID); + alife().scheduled().remove (object); + } + else { + VERIFY (object->ID_Parent == 0xffff); + alife().graph().level().remove (object); + } +} + +void CSE_ALifeOnlineOfflineGroup::unregister_member (ALife::_OBJECT_ID member_id) +{ + CALifeGraphRegistry &graph = alife().graph(); + CALifeLevelRegistry &level = graph.level(); + + MEMBERS::iterator I = m_members.find(member_id); + VERIFY (I != m_members.end()); + VERIFY ((*I).second->m_group_id == ID); + (*I).second->m_group_id = 0xffff; + + graph.update ((*I).second); + alife().scheduled().add ((*I).second); + + m_members.erase (I); + + if (m_members.empty()) { + if (!m_bOnline) { + graph.remove (this,m_tGraphID); + } + else { + if (ID_Parent == 0xffff) + level.remove (this); + } + + m_flags.set (flUsedAI_Locations,FALSE); + } +} + +CSE_ALifeOnlineOfflineGroup::MEMBER *CSE_ALifeOnlineOfflineGroup::member(ALife::_OBJECT_ID member_id, bool no_assert) +{ + MEMBERS::iterator I = m_members.find(member_id); + if (I == m_members.end()) { + if (!no_assert) + Msg ("! There is no member with id %d in the OnlineOfflineGroup id %d",member_id,ID); + VERIFY (no_assert); + return (0); + } + return ((*I).second); +} + +bool CSE_ALifeOnlineOfflineGroup::synchronize_location () +{ + if (m_members.empty()) + return (true); + + MEMBERS::iterator I = m_members.begin(); + MEMBERS::iterator E = m_members.end(); + for ( ; I != E; ++I) + (*I).second->synchronize_location (); + + MEMBER &member = *(*m_members.begin()).second; + o_Position = member.o_Position; + m_tNodeID = member.m_tNodeID; + + if (m_tGraphID != member.m_tGraphID) { + if (!m_bOnline) + alife().graph().change (this,m_tGraphID,member.m_tGraphID); + else + m_tGraphID = member.m_tGraphID; + } + + m_fDistance = member.m_fDistance; + return (true); +} + +void CSE_ALifeOnlineOfflineGroup::try_switch_online () +{ + if (m_members.empty()) + return; + + if (!can_switch_online()) + return; + + if (!can_switch_offline()) { + //. + o_Position = alife().graph().actor()->o_Position; + + inherited1::try_switch_online (); + return; + } + + MEMBERS::iterator I = m_members.begin(); + MEMBERS::iterator E = m_members.end(); + for ( ; I != E; ++I) { + VERIFY3 ((*I).second->g_Alive(),"Incorrect situation : some of the OnlineOffline group members is dead",(*I).second->name_replace()); + VERIFY3 ((*I).second->can_switch_online(),"Incorrect situation : some of the OnlineOffline group members cannot be switched online due to their personal properties",(*I).second->name_replace()); + VERIFY3 ((*I).second->can_switch_offline(),"Incorrect situation : some of the OnlineOffline group members cannot be switched online due to their personal properties",(*I).second->name_replace()); + + if (alife().graph().actor()->o_Position.distance_to((*I).second->o_Position) > alife().offline_distance()) + continue; + + //. + o_Position = (*I).second->o_Position; + + inherited1::try_switch_online (); + return; + } +} + +void CSE_ALifeOnlineOfflineGroup::try_switch_offline () +{ + if (m_members.empty()) + return; + + if (!can_switch_offline()) + return; + + if (!can_switch_online()) { + alife().switch_offline (this); + return; + } + + MEMBERS::iterator I = m_members.begin(); + MEMBERS::iterator E = m_members.end(); + for ( ; I != E; ++I) { + VERIFY3 ((*I).second->g_Alive(),"Incorrect situation : some of the OnlineOffline group members is dead",(*I).second->name_replace()); + VERIFY3 ((*I).second->can_switch_offline(),"Incorrect situation : some of the OnlineOffline group members cannot be switched online due to their personal properties",(*I).second->name_replace()); + VERIFY3 ((*I).second->can_switch_online(),"Incorrect situation : some of the OnlineOffline group members cannot be switched online due to their personal properties",(*I).second->name_replace()); + + if (alife().graph().actor()->o_Position.distance_to((*I).second->o_Position) <= alife().offline_distance()) + return; + } + + alife().switch_offline (this); +} + +void CSE_ALifeOnlineOfflineGroup::switch_online () +{ + R_ASSERT (!m_bOnline); + m_bOnline = true; + + MEMBERS::iterator I = m_members.begin(); + MEMBERS::iterator E = m_members.end(); + for ( ; I != E; ++I) + alife().add_online ((*I).second, false); + + alife().scheduled().remove (this); + alife().graph().remove (this,m_tGraphID,false); +} + +void CSE_ALifeOnlineOfflineGroup::switch_offline () +{ + R_ASSERT (m_bOnline); + m_bOnline = false; + + if (!m_members.empty()) { + MEMBER *member = (*m_members.begin()).second; + o_Position = member->o_Position; + m_tNodeID = member->m_tNodeID; + m_tGraphID = member->m_tGraphID; + m_fDistance = member->m_fDistance; + } + + MEMBERS::iterator I = m_members.begin(); + MEMBERS::iterator E = m_members.end(); + for ( ; I != E; ++I) + alife().remove_online ((*I).second,false); + + alife().scheduled().add (this); + alife().graph().add (this,m_tGraphID,false); +} + +bool CSE_ALifeOnlineOfflineGroup::redundant () const +{ + return (m_members.empty()); +} + +void CSE_ALifeOnlineOfflineGroup::notify_on_member_death(MEMBER *member) +{ + unregister_member (member->ID); +} + +void CSE_ALifeOnlineOfflineGroup::on_before_register () +{ + m_tGraphID = GameGraph::_GRAPH_ID(-1); + m_flags.set (flUsedAI_Locations,FALSE); +} + +void CSE_ALifeOnlineOfflineGroup::on_after_game_load () +{ + if (m_members.empty()) + return; + + ALife::_OBJECT_ID *temp = (ALife::_OBJECT_ID*)_alloca(m_members.size()*sizeof(ALife::_OBJECT_ID)); + ALife::_OBJECT_ID *i = temp, *e = temp + m_members.size(); + + { + MEMBERS::const_iterator I = m_members.begin(); + MEMBERS::const_iterator E = m_members.end(); + for ( ; I != E; ++I, ++i) { + VERIFY (!(*I).second); + *i = (*I).first; + } + } + + m_members.clear (); + + for (i = temp; i != e; ++i) + register_member (*i); +} diff --git a/src/xrGameLA/alife_online_offline_group_brain.cpp b/src/xrGameLA/alife_online_offline_group_brain.cpp new file mode 100644 index 000000000..41cefa339 --- /dev/null +++ b/src/xrGameLA/alife_online_offline_group_brain.cpp @@ -0,0 +1,11 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_online_offline_group_brain.cpp +// Created : 25.10.2005 +// Modified : 25.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife Online Offline Group brain class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_online_offline_group_brain.h" + diff --git a/src/xrGameLA/alife_online_offline_group_brain.h b/src/xrGameLA/alife_online_offline_group_brain.h new file mode 100644 index 000000000..9d265db9a --- /dev/null +++ b/src/xrGameLA/alife_online_offline_group_brain.h @@ -0,0 +1,21 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_online_offline_group_brain.h +// Created : 25.10.2005 +// Modified : 25.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife Online Offline Group brain class +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +class CSE_ALifeOnlineOfflineGroup; + +class CALifeOnlineOfflineGroupBrain { +private: + CSE_ALifeOnlineOfflineGroup *m_object; + +public: + IC CALifeOnlineOfflineGroupBrain (CSE_ALifeOnlineOfflineGroup *object); +}; + +#include "alife_online_offline_group_brain_inline.h" \ No newline at end of file diff --git a/src/xrGameLA/alife_online_offline_group_brain_inline.h b/src/xrGameLA/alife_online_offline_group_brain_inline.h new file mode 100644 index 000000000..6f32ffea3 --- /dev/null +++ b/src/xrGameLA/alife_online_offline_group_brain_inline.h @@ -0,0 +1,14 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_online_offline_group_brain_inline.h +// Created : 25.10.2005 +// Modified : 25.10.2005 +// Author : Dmitriy Iassenev +// Description : ALife Online Offline Group brain class inline functions +//////////////////////////////////////////////////////////////////////////// + +#pragma once + +IC CALifeOnlineOfflineGroupBrain::CALifeOnlineOfflineGroupBrain (CSE_ALifeOnlineOfflineGroup *object) +{ + m_object = object; +} diff --git a/src/xrGameLA/alife_registry_container.cpp b/src/xrGameLA/alife_registry_container.cpp new file mode 100644 index 000000000..86dfb4022 --- /dev/null +++ b/src/xrGameLA/alife_registry_container.cpp @@ -0,0 +1,106 @@ +//////////////////////////////////////////////////////////////////////////// +// Module : alife_registry_container.cpp +// Created : 01.07.2004 +// Modified : 01.07.2004 +// Author : Dmitriy Iassenev +// Description : ALife registry container class +//////////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "alife_registry_container.h" +#include "object_interfaces.h" +#include "alife_space.h" +#include "object_type_traits.h" + +template +struct CRegistryHelperLoad { + typedef typename object_type_traits::remove_reference<_T2>::type T2; + + template + IC static void do_load(T1 *self, T2 &p1) + { + } + + template <> + IC static void do_load(T1 *self, T2 &p1) + { + self->Head::load(p1); + } + + IC static void process(T1 *self, T2 &p1) + { + do_load,Head>::value>(self,p1); + } +}; + +template +struct CRegistryHelperSave { + typedef typename object_type_traits::remove_reference<_T2>::type T2; + + template + IC static void do_save(T1 *self, T2 &p1) + { + } + + template <> + IC static void do_save(T1 *self, T2 &p1) + { + self->Head::save(p1); + } + + IC static void process(T1 *self, T2 &p1) + { + do_save,Head>::value>(self,p1); + } +}; + +template