1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566 |
- #ifdef PRECOMPILEDHEADERS
- #include "TileEngine All.h"
- #else
- #include "physics.h"
- #include "wcheck.h"
- #include "timer control.h"
- #include "isometric utils.h"
- #include "los.h"
- #include "worldman.h"
- #include "event pump.h"
- #include "Sound Control.h"
- #include "soldier control.h"
- #include "interface.h"
- #include "interface items.h"
- #include "weapons.h"
- #include "explosion control.h"
- #include "Debug Control.h"
- #include "tile animation.h"
- #include "message.h"
- #include "weapons.h"
- #include "structure wrap.h"
- #include "physics.h"
- #include "overhead.h"
- #include "animation control.h"
- #include "text.h"
- #include "Random.h"
- #include "lighteffects.h"
- #include "opplist.h"
- #include "World Items.h"
- #include "environment.h"
- #endif
- #include "Campaign.h"
- #include "SkillCheck.h"
- #define NO_TEST_OBJECT 0
- #define TEST_OBJECT_NO_COLLISIONS 1
- #define TEST_OBJECT_ANY_COLLISION 2
- #define TEST_OBJECT_NOTWALLROOF_COLLISIONS 3
- #define OUTDOORS_START_ANGLE (FLOAT)( PI/4 )
- #define INDOORS_START_ANGLE (FLOAT)( PI/30 )
- //#define INDOORS_START_ANGLE (FLOAT)( 0 )
- #define GLAUNCHER_START_ANGLE (FLOAT)( PI/8 )
- #define GLAUNCHER_HIGHER_LEVEL_START_ANGLE (FLOAT)( PI/6 )
- #define GET_THROW_HEIGHT( l ) (INT16)( ( l * 256 ) )
- #define GET_SOLDIER_THROW_HEIGHT( l ) (INT16)( ( l * 256 ) + STANDING_HEIGHT )
- #define GET_OBJECT_LEVEL( z ) ( (INT8)( ( z + 10 ) / HEIGHT_UNITS ) )
- #define OBJECT_DETONATE_ON_IMPACT( o ) ( ( o->Obj.usItem == MORTAR_SHELL ) ) // && ( o->ubActionCode == THROW_ARM_ITEM || pObject->fTestObject ) )
-
- #define MAX_INTEGRATIONS 8
- #define TIME_MULTI 1.8
- //#define TIME_MULTI 2.2
- #define DELTA_T ( 1.0 * TIME_MULTI )
- #define GRAVITY ( 9.8 * 2.5 )
- //#define GRAVITY ( 9.8 * 2.8 )
- REAL_OBJECT ObjectSlots[ NUM_OBJECT_SLOTS ];
- UINT32 guiNumObjectSlots = 0;
- BOOLEAN fDampingActive = FALSE;
- //real Kdl = (float)0.5; // LINEAR DAMPENING ( WIND RESISTANCE )
- real Kdl = (float)( 0.1 * TIME_MULTI ); // LINEAR DAMPENING ( WIND RESISTANCE )
- #define EPSILONV 0.5
- #define EPSILONP (real)0.01
- #define EPSILONPZ 3
- #define CALCULATE_OBJECT_MASS( m ) ( (float)( m * 2 ) )
- #define SCALE_VERT_VAL_TO_HORZ( f ) ( ( f / HEIGHT_UNITS ) * CELL_X_SIZE )
- #define SCALE_HORZ_VAL_TO_VERT( f ) ( ( f / CELL_X_SIZE ) * HEIGHT_UNITS )
- void SimulateObject( REAL_OBJECT *pObject, real deltaT );
- void CheckForObjectHittingMerc( REAL_OBJECT *pObject, UINT16 usStructureID );
- extern void DoGenericHit( SOLDIERTYPE *pSoldier, UINT8 ubSpecial, INT16 bDirection );
- BOOLEAN PhysicsUpdateLife( REAL_OBJECT *pObject, real DeltaTime );
- BOOLEAN PhysicsComputeForces( REAL_OBJECT *pObject );
- BOOLEAN PhysicsIntegrate( REAL_OBJECT *pObject, real DeltaTime );
- BOOLEAN PhysicsMoveObject( REAL_OBJECT *pObject );
- BOOLEAN PhysicsCheckForCollisions( REAL_OBJECT *pObject, INT32 *piCollisionID );
- void PhysicsResolveCollision( REAL_OBJECT *pObject, vector_3 *pVelocity, vector_3 *pNormal, real CoefficientOfRestitution );
- void PhysicsDeleteObject( REAL_OBJECT *pObject );
- BOOLEAN PhysicsHandleCollisions( REAL_OBJECT *pObject, INT32 *piCollisionID, real DeltaTime );
- FLOAT CalculateForceFromRange( INT16 sRange, FLOAT dDegrees );
- UINT16 RandomGridFromRadius( INT16 sSweetGridNo, INT8 ubMinRadius, INT8 ubMaxRadius );
- void HandleArmedObjectImpact( REAL_OBJECT *pObject );
- void ObjectHitWindow( INT16 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, BOOLEAN fLargeForce );
- FLOAT CalculateObjectTrajectory( INT16 sTargetZ, OBJECTTYPE *pItem, vector_3 *vPosition, vector_3 *vForce, INT16 *psFinalGridNo );
- vector_3 FindBestForceForTrajectory( INT16 sSrcGridNo, INT16 sGridNo,INT16 sStartZ, INT16 sEndZ, real dzDegrees, OBJECTTYPE *pItem, INT16 *psGridNo, FLOAT *pzMagForce );
- INT32 ChanceToGetThroughObjectTrajectory( INT16 sTargetZ, OBJECTTYPE *pItem, vector_3 *vPosition, vector_3 *vForce, INT16 *psFinalGridNo, INT8 *pbLevel, BOOLEAN fFromUI );
- FLOAT CalculateSoldierMaxForce( SOLDIERTYPE *pSoldier, FLOAT dDegrees, OBJECTTYPE *pObject, BOOLEAN fArmed );
- BOOLEAN AttemptToCatchObject( REAL_OBJECT *pObject );
- BOOLEAN CheckForCatchObject( REAL_OBJECT *pObject );
- BOOLEAN DoCatchObject( REAL_OBJECT *pObject );
- BOOLEAN CheckForCatcher( REAL_OBJECT *pObject, UINT16 usStructureID );
- /// OBJECT POOL FUNCTIONS
- INT32 GetFreeObjectSlot(void)
- {
- UINT32 uiCount;
- for(uiCount=0; uiCount < guiNumObjectSlots; uiCount++)
- {
- if(( ObjectSlots[uiCount].fAllocated == FALSE ) )
- return((INT32)uiCount);
- }
- if(guiNumObjectSlots < NUM_OBJECT_SLOTS )
- return((INT32)guiNumObjectSlots++);
- return(-1);
- }
- void RecountObjectSlots(void)
- {
- INT32 uiCount;
- for(uiCount=guiNumObjectSlots-1; (uiCount >=0) ; uiCount--)
- {
- if( ( ObjectSlots[uiCount].fAllocated ) )
- {
- guiNumObjectSlots=(UINT32)(uiCount+1);
- return;
- }
- }
- guiNumObjectSlots = 0;
- }
- INT32 CreatePhysicalObject( OBJECTTYPE *pGameObj, real dLifeLength, real xPos, real yPos, real zPos, real xForce, real yForce, real zForce, UINT8 ubOwner, UINT8 ubActionCode, UINT32 uiActionData )
- {
- INT32 iObjectIndex;
- FLOAT mass;
- REAL_OBJECT *pObject;
- if( ( iObjectIndex = GetFreeObjectSlot() )==(-1) )
- return(-1);
- pObject = &( ObjectSlots[ iObjectIndex ] );
- memset( pObject, 0, sizeof( REAL_OBJECT ) );
- // OK, GET OBJECT DATA AND COPY
- memcpy( &(pObject->Obj), pGameObj, sizeof( OBJECTTYPE ) );
- // Get mass
- mass = CALCULATE_OBJECT_MASS( Item[pGameObj->usItem ].ubWeight );
- // If mass is z, make it something!
- if ( mass == 0 )
- {
- mass = 10;
- }
- // OK, mass determines the smoothness of the physics integration
- // For gameplay, we will use mass for maybe max throw distance
- mass = 60;
- // Set lifelength
- pObject->dLifeLength = dLifeLength;
- pObject->fAllocated = TRUE;
- pObject->fAlive = TRUE;
- pObject->fApplyFriction = FALSE;
- pObject->iSoundID = NO_SAMPLE;
- // Set values
- pObject->OneOverMass = 1 / mass;
- pObject->Position.x = xPos;
- pObject->Position.y = yPos;
- pObject->Position.z = zPos;
- pObject->fVisible = TRUE;
- pObject->ubOwner = ubOwner;
- pObject->ubActionCode = ubActionCode;
- pObject->uiActionData = uiActionData;
- pObject->fDropItem = TRUE;
- pObject->ubLastTargetTakenDamage = NOBODY;
- pObject->fFirstTimeMoved = TRUE;
- pObject->InitialForce.x = SCALE_VERT_VAL_TO_HORZ( xForce );
- pObject->InitialForce.y = SCALE_VERT_VAL_TO_HORZ( yForce );
- pObject->InitialForce.z = zForce ;
- pObject->InitialForce = VDivScalar( &(pObject->InitialForce), (float)TIME_MULTI );
- pObject->InitialForce = VMultScalar( &(pObject->InitialForce), 1.5 );
-
- // Calculate gridNo
- pObject->sGridNo = MAPROWCOLTOPOS( ( (INT16)yPos / CELL_Y_SIZE ), ( (INT16)xPos / CELL_X_SIZE ) );
- pObject->iID = iObjectIndex;
- pObject->pNode = NULL;
- pObject->pShadow = NULL;
- // If gridno not equal to NOWHERE, use sHeight of alnd....
- if ( pObject->sGridNo != NOWHERE )
- {
- pObject->Position.z += CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ pObject->sGridNo ].sHeight );
- pObject->EndedWithCollisionPosition.z += CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ pObject->sGridNo ].sHeight );
- }
- PhysicsDebugMsg( String( "NewPhysics Object") );
- return( iObjectIndex );
- }
- BOOLEAN RemoveObjectSlot( INT32 iObject )
- {
- CHECKF( iObject < NUM_OBJECT_SLOTS );
- ObjectSlots[iObject].fAllocated=FALSE;
- RecountObjectSlots();
- return( TRUE );
- }
- void SimulateWorld( )
- {
- UINT32 cnt;
- REAL_OBJECT *pObject;
- if ( COUNTERDONE( PHYSICSUPDATE ) )
- {
- RESETCOUNTER( PHYSICSUPDATE );
- for( cnt = 0;cnt < guiNumObjectSlots; cnt++)
- {
- // CHECK FOR ALLOCATED
- if( ObjectSlots[ cnt ].fAllocated )
- {
- // Get object
- pObject = &( ObjectSlots[ cnt ] );
- SimulateObject( pObject, (real)DELTA_T );
- }
- }
- }
- }
- void RemoveAllPhysicsObjects( )
- {
- UINT32 cnt;
- for( cnt = 0;cnt < guiNumObjectSlots; cnt++)
- {
- // CHECK FOR ALLOCATED
- if( ObjectSlots[ cnt ].fAllocated )
- {
- PhysicsDeleteObject( &(ObjectSlots[ cnt ]) );
- }
- }
- }
- void SimulateObject( REAL_OBJECT *pObject, real deltaT )
- {
- real DeltaTime = 0;
- real CurrentTime = 0;
- real TargetTime = DeltaTime;
- INT32 iCollisionID;
- BOOLEAN fEndThisObject = FALSE;
- if ( !PhysicsUpdateLife( pObject, (float)deltaT ) )
- {
- return;
- }
- if ( pObject->fAlive )
- {
- CurrentTime = 0;
- TargetTime = (float)deltaT;
- // Do subtime here....
- DeltaTime = (float)deltaT / (float)10;
- if ( !PhysicsComputeForces( pObject ) )
- {
- return;
- }
- while( CurrentTime < TargetTime )
- {
- if ( !PhysicsIntegrate( pObject, DeltaTime ) )
- {
- fEndThisObject = TRUE;
- break;
- }
- if ( !PhysicsHandleCollisions( pObject, &iCollisionID, DeltaTime ) )
- {
- fEndThisObject = TRUE;
- break;
- }
- if ( iCollisionID != COLLISION_NONE )
- {
- break;
- }
- CurrentTime += DeltaTime;
- }
- if ( fEndThisObject )
- {
- return;
- }
- if ( !PhysicsMoveObject( pObject ) )
- {
- return;
- }
- }
- }
- BOOLEAN PhysicsComputeForces( REAL_OBJECT *pObject )
- {
- vector_3 vTemp;
- // Calculate forces
- pObject->Force = VSetEqual( &(pObject->InitialForce ) );
- // Note: Only apply gravity if we are not resting on some structure surface
- if ( !pObject->fZOnRest )
- {
- pObject->Force.z -= (real)GRAVITY;
- }
- // Set intial force to zero
- pObject->InitialForce = VMultScalar( &(pObject->InitialForce ), 0 );
- if ( pObject->fApplyFriction )
- {
- vTemp = VMultScalar( &(pObject->Velocity), -pObject->AppliedMu );
- pObject->Force = VAdd( &(vTemp), &(pObject->Force) );
- pObject->fApplyFriction = FALSE;
- }
- if( fDampingActive )
- {
- vTemp = VMultScalar( &(pObject->Velocity), -Kdl );
- pObject->Force = VAdd( &(vTemp), &(pObject->Force) );
- }
- return( TRUE );
- }
- BOOLEAN PhysicsUpdateLife( REAL_OBJECT *pObject, real DeltaTime )
- {
- UINT8 bLevel = 0;
- pObject->dLifeSpan += DeltaTime;
- // End life if time has ran out or we are stationary
- if ( pObject->dLifeLength != -1 )
- {
- if ( pObject->dLifeSpan > pObject->dLifeLength )
- {
- pObject->fAlive = FALSE;
- }
- }
- // End life if we are out of bounds....
- if ( !GridNoOnVisibleWorldTile( pObject->sGridNo ) )
- {
- pObject->fAlive = FALSE;
- }
- if ( !pObject->fAlive )
- {
- pObject->fAlive = FALSE;
- if ( !pObject->fTestObject )
- {
- if ( pObject->iSoundID != NO_SAMPLE )
- {
- SoundStop( pObject->iSoundID );
- }
- if ( pObject->ubActionCode == THROW_ARM_ITEM && !pObject->fInWater )
- {
- HandleArmedObjectImpact( pObject );
- }
- else
- {
- // If we are in water, and we are a sinkable item...
- if ( !pObject->fInWater || !( Item[ pObject->Obj.usItem ].fFlags & ITEM_SINKS ) )
- {
- if ( pObject->fDropItem )
- {
- // ATE: If we have collided with roof last...
- if ( pObject->iOldCollisionCode == COLLISION_ROOF )
- {
- bLevel = 1;
- }
- // ATE; If an armed object, don't add....
- if ( pObject->ubActionCode != THROW_ARM_ITEM )
- {
- AddItemToPool( pObject->sGridNo, &( pObject->Obj ), 1, bLevel, 0, -1 );
- }
- }
- }
- }
- // Make impact noise....
- if ( pObject->Obj.usItem == ROCK || pObject->Obj.usItem == ROCK2 )
- {
- MakeNoise( pObject->ubOwner, pObject->sGridNo, 0, gpWorldLevelData[ pObject->sGridNo ].ubTerrainID, (UINT8) (9 + PreRandom( 9 ) ), NOISE_ROCK_IMPACT );
- }
- else if ( Item[ pObject->Obj.usItem ].usItemClass & IC_GRENADE )
- {
- MakeNoise( pObject->ubOwner, pObject->sGridNo, 0, gpWorldLevelData[ pObject->sGridNo ].ubTerrainID, (UINT8) (9 + PreRandom( 9 ) ), NOISE_GRENADE_IMPACT );
- }
- if ( !pObject->fTestObject && pObject->iOldCollisionCode == COLLISION_GROUND )
- {
- PlayJA2Sample( THROW_IMPACT_2, RATE_11025, SoundVolume( MIDVOLUME, pObject->sGridNo ), 1, SoundDir( pObject->sGridNo ) );
- }
- DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., PHYSICS OBJECT DONE effect gone off") );
- ReduceAttackBusyCount( pObject->ubOwner, FALSE );
- // ATE: Handle end of animation...
- if ( pObject->fCatchAnimOn )
- {
- SOLDIERTYPE *pSoldier;
- pObject->fCatchAnimOn = FALSE;
- // Get intended target
- pSoldier = MercPtrs[ pObject->uiActionData ];
- // Catch anim.....
- switch( gAnimControl[ pSoldier->usAnimState ].ubHeight )
- {
- case ANIM_STAND:
- pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
- EVENT_InitNewSoldierAnim( pSoldier, END_CATCH, 0 , FALSE );
- break;
- case ANIM_CROUCH:
- pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
- EVENT_InitNewSoldierAnim( pSoldier, END_CROUCH_CATCH, 0 , FALSE );
- break;
- }
- PlayJA2Sample( CATCH_OBJECT, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
- }
- }
- PhysicsDeleteObject( pObject );
- return( FALSE );
- }
- return( TRUE );
- }
- BOOLEAN PhysicsIntegrate( REAL_OBJECT *pObject, real DeltaTime )
- {
- vector_3 vTemp;
- // Save old position
- pObject->OldPosition = VSetEqual( &(pObject->Position) );
- pObject->OldVelocity = VSetEqual( &(pObject->Velocity) );
- vTemp = VMultScalar( &(pObject->Velocity), DeltaTime );
- pObject->Position = VAdd( &(pObject->Position), &vTemp );
- // Save test TargetPosition
- if ( pObject->fTestPositionNotSet )
- {
- pObject->TestTargetPosition = VSetEqual( &(pObject->Position) );
- }
- vTemp = VMultScalar( &(pObject->Force), ( DeltaTime * pObject->OneOverMass ) );
- pObject->Velocity = VAdd( &(pObject->Velocity), &vTemp );
- if ( pObject->fPotentialForDebug )
- {
- PhysicsDebugMsg( String( "Object %d: Force %f %f %f", pObject->iID, pObject->Force.x, pObject->Force.y, pObject->Force.z ) );
- PhysicsDebugMsg( String( "Object %d: Velocity %f %f %f", pObject->iID, pObject->Velocity.x, pObject->Velocity.y, pObject->Velocity.z ) );
- PhysicsDebugMsg( String( "Object %d: Position %f %f %f", pObject->iID, pObject->Position.x, pObject->Position.y, pObject->Position.z ) );
- PhysicsDebugMsg( String( "Object %d: Delta Pos %f %f %f", pObject->iID, (pObject->OldPosition.x - pObject->Position.x ), (pObject->OldPosition.y - pObject->Position.y ), ( pObject->OldPosition.z - pObject->Position.z ) ) );
- }
- if ( pObject->Obj.usItem == MORTAR_SHELL && !pObject->fTestObject && pObject->ubActionCode == THROW_ARM_ITEM )
- {
- // Start soud if we have reached our max height
- if ( pObject->OldVelocity.z >= 0 && pObject->Velocity.z < 0 )
- {
- if ( pObject->iSoundID == NO_SAMPLE )
- {
- pObject->iSoundID = PlayJA2Sample( MORTAR_WHISTLE, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
- }
- }
- }
- return( TRUE );
- }
- BOOLEAN PhysicsHandleCollisions( REAL_OBJECT *pObject, INT32 *piCollisionID, real DeltaTime )
- {
- FLOAT dDeltaX, dDeltaY, dDeltaZ;
- if ( PhysicsCheckForCollisions( pObject, piCollisionID ) )
- {
- dDeltaX = pObject->Position.x - pObject->OldPosition.x;
- dDeltaY = pObject->Position.y - pObject->OldPosition.y;
- dDeltaZ = pObject->Position.z - pObject->OldPosition.z;
- if ( dDeltaX <= EPSILONV && dDeltaX >= -EPSILONV &&
- dDeltaY <= EPSILONV && dDeltaY >= -EPSILONV )
- {
- pObject->sConsecutiveZeroVelocityCollisions++;
- }
- if ( pObject->sConsecutiveZeroVelocityCollisions > 3 )
- {
- // We will continue with our Z velocity
- pObject->Velocity.x = 0;
- pObject->Velocity.y = 0;
- // Check that we are not colliding with structure z
- //if ( *piCollisionID == COLLISION_STRUCTURE_Z || *piCollisionID == COLLISION_ROOF )
- if ( *piCollisionID == COLLISION_STRUCTURE_Z || *piCollisionID == COLLISION_ROOF || *piCollisionID == COLLISION_GROUND )
- {
- pObject->Velocity.z = 0;
- // Set us not alive!
- pObject->fAlive = FALSE;
- }
- *piCollisionID = COLLISION_NONE;
- }
- else
- {
- // Set position back to before collision
- pObject->Position = VSetEqual( &(pObject->OldPosition) );
- // Set old position!
- pObject->OldPosition.x = pObject->Position.y - dDeltaX;
- pObject->OldPosition.y = pObject->Position.x - dDeltaY;
- pObject->OldPosition.z = pObject->Position.z - dDeltaZ;
- PhysicsResolveCollision( pObject, &(pObject->CollisionVelocity), &(pObject->CollisionNormal), pObject->CollisionElasticity );
- }
- if ( pObject->Position.z < 0 )
- {
- pObject->Position.z = 0;
- }
- //otherwise, continue falling downwards!
- // TO STOP?
-
- // Check for delta position values
- if ( dDeltaZ <= EPSILONP && dDeltaZ >= -EPSILONP &&
- dDeltaY <= EPSILONP && dDeltaY >= -EPSILONP &&
- dDeltaX <= EPSILONP && dDeltaX >= -EPSILONP )
- {
- //pObject->fAlive = FALSE;
- //return( FALSE );
- }
- // Check for repeated collisions...
- //if ( pObject->iOldCollisionCode == COLLISION_ROOF || pObject->iOldCollisionCode == COLLISION_GROUND || pObject->iOldCollisionCode == COLLISION_WATER )
- {
- // ATE: This is a safeguard
- if ( pObject->sConsecutiveCollisions > 30 )
- {
- pObject->fAlive = FALSE;
- return( FALSE );
- }
- }
- // Check for -ve velocity still...
- //if ( pObject->Velocity.z <= EPSILONV && pObject->Velocity.z >= -EPSILONV &&
- // pObject->Velocity.y <= EPSILONV && pObject->Velocity.y >= -EPSILONV &&
- // pObject->Velocity.x <= EPSILONV && pObject->Velocity.x >= -EPSILONV )
- //{
- //PhysicsDeleteObject( pObject );
- // pObject->fAlive = FALSE;
- // return( FALSE );
- //}
- }
- return( TRUE );
- }
- void PhysicsDeleteObject( REAL_OBJECT *pObject )
- {
- if ( pObject->fAllocated )
- {
- if ( pObject->pNode != NULL )
- {
- RemoveStructFromLevelNode( pObject->sLevelNodeGridNo, pObject->pNode );
- }
- if ( pObject->pShadow != NULL )
- {
- RemoveShadowFromLevelNode( pObject->sLevelNodeGridNo, pObject->pShadow );
- }
- RemoveObjectSlot( pObject->iID );
- }
- }
- BOOLEAN PhysicsCheckForCollisions( REAL_OBJECT *pObject, INT32 *piCollisionID )
- {
- vector_3 vTemp;
- FLOAT dDeltaX, dDeltaY, dDeltaZ, dX, dY, dZ;
- INT32 iCollisionCode = COLLISION_NONE;
- BOOLEAN fDoCollision = FALSE;
- FLOAT dElasity = 1;
- UINT16 usStructureID;
- FLOAT dNormalX, dNormalY, dNormalZ;
- INT16 sGridNo;
- // Checkf for collisions
- dX = pObject->Position.x;
- dY = pObject->Position.y;
- dZ = pObject->Position.z;
- vTemp.x = 0;
- vTemp.y = 0;
- vTemp.z = 0;
- dDeltaX = dX - pObject->OldPosition.x;
- dDeltaY = dY - pObject->OldPosition.y;
- dDeltaZ = dZ - pObject->OldPosition.z;
- //Round delta pos to nearest 0.01
- //dDeltaX = (float)( (int)dDeltaX * 100 ) / 100;
- //dDeltaY = (float)( (int)dDeltaY * 100 ) / 100;
- //dDeltaZ = (float)( (int)dDeltaZ * 100 ) / 100;
- // SKIP FIRST GRIDNO, WE'LL COLLIDE WITH OURSELVES....
- if ( pObject->fTestObject != TEST_OBJECT_NO_COLLISIONS )
- {
- iCollisionCode = CheckForCollision( dX, dY, dZ, dDeltaX, dDeltaY, dDeltaZ, &usStructureID, &dNormalX, &dNormalY, &dNormalZ );
- }
- else if ( pObject->fTestObject == TEST_OBJECT_NO_COLLISIONS )
- {
- iCollisionCode = COLLISION_NONE;
- // Are we on a downward slope?
- if ( dZ < pObject->TestZTarget && dDeltaZ < 0 )
- {
- if (pObject->fTestPositionNotSet )
- {
- if ( pObject->TestZTarget > 32 )
- {
- pObject->fTestPositionNotSet = FALSE;
- pObject->TestZTarget = 0;
- }
- else
- {
- iCollisionCode = COLLISION_GROUND;
- }
- }
- else
- {
- iCollisionCode = COLLISION_GROUND;
- }
- }
- }
- // If a test object and we have collided with something ( should only be ground ( or roof? ) )
- // Or destination?
- if ( pObject->fTestObject == TEST_OBJECT_ANY_COLLISION )
- {
- if ( iCollisionCode != COLLISION_GROUND && iCollisionCode != COLLISION_ROOF && iCollisionCode != COLLISION_WATER && iCollisionCode != COLLISION_NONE )
- {
- pObject->fTestEndedWithCollision = TRUE;
- pObject->fAlive = FALSE;
- return( FALSE );
- }
- }
- if ( pObject->fTestObject == TEST_OBJECT_NOTWALLROOF_COLLISIONS )
- {
- // So we don't collide with ourselves.....
- if ( iCollisionCode != COLLISION_WATER && iCollisionCode != COLLISION_GROUND && iCollisionCode != COLLISION_NONE &&
- iCollisionCode != COLLISION_ROOF && iCollisionCode != COLLISION_INTERIOR_ROOF &&
- iCollisionCode != COLLISION_WALL_SOUTHEAST && iCollisionCode != COLLISION_WALL_SOUTHWEST &&
- iCollisionCode != COLLISION_WALL_NORTHEAST && iCollisionCode != COLLISION_WALL_NORTHWEST )
- {
- if ( pObject->fFirstTimeMoved || pObject->sFirstGridNo == pObject->sGridNo )
- {
- iCollisionCode = COLLISION_NONE;
- }
- // If we are NOT a wall or window, ignore....
- if ( pObject->uiNumTilesMoved < 4 )
- {
- switch( iCollisionCode )
- {
- case COLLISION_MERC:
- case COLLISION_STRUCTURE:
- case COLLISION_STRUCTURE_Z:
- // Set to no collision ( we shot past )
- iCollisionCode = COLLISION_NONE;
- break;
- }
- }
- }
- switch( iCollisionCode )
- {
- // End test with any collision NOT a wall, roof...
- case COLLISION_STRUCTURE:
- case COLLISION_STRUCTURE_Z:
- // OK, if it's mercs... don't stop
- if ( usStructureID >= INVALID_STRUCTURE_ID )
- {
- pObject->fTestEndedWithCollision = TRUE;
- if ( !pObject->fEndedWithCollisionPositionSet )
- {
- pObject->fEndedWithCollisionPositionSet = TRUE;
- pObject->EndedWithCollisionPosition = VSetEqual( &(pObject->Position) );
- }
- iCollisionCode = COLLISION_NONE;
- }
- else
- {
- if ( !pObject->fEndedWithCollisionPositionSet )
- {
- pObject->fEndedWithCollisionPositionSet = TRUE;
- pObject->EndedWithCollisionPosition = VSetEqual( &(pObject->Position) );
- }
- }
- break;
- case COLLISION_ROOF:
- if ( !pObject->fEndedWithCollisionPositionSet )
- {
- pObject->fEndedWithCollisionPositionSet = TRUE;
- pObject->EndedWithCollisionPosition = VSetEqual( &(pObject->Position) );
- }
- break;
- case COLLISION_WATER:
- case COLLISION_GROUND:
- case COLLISION_MERC:
- case COLLISION_INTERIOR_ROOF:
- case COLLISION_NONE:
- case COLLISION_WINDOW_SOUTHEAST:
- case COLLISION_WINDOW_SOUTHWEST:
- case COLLISION_WINDOW_NORTHEAST:
- case COLLISION_WINDOW_NORTHWEST:
- // Here we just keep going..
- break;
- default:
-
- // THis is for walls, windows, etc
- // here, we set test ended with collision, but keep going...
- pObject->fTestEndedWithCollision = TRUE;
- break;
- }
- }
- if ( pObject->fTestObject != TEST_OBJECT_NOTWALLROOF_COLLISIONS )
- {
- if ( iCollisionCode != COLLISION_WATER && iCollisionCode != COLLISION_GROUND && iCollisionCode != COLLISION_NONE &&
- iCollisionCode != COLLISION_ROOF && iCollisionCode != COLLISION_INTERIOR_ROOF &&
- iCollisionCode != COLLISION_WALL_SOUTHEAST && iCollisionCode != COLLISION_WALL_SOUTHWEST &&
- iCollisionCode != COLLISION_WALL_NORTHEAST && iCollisionCode != COLLISION_WALL_NORTHWEST )
- {
- // So we don't collide with ourselves.....
- if ( pObject->fFirstTimeMoved || pObject->sFirstGridNo == pObject->sGridNo )
- {
- iCollisionCode = COLLISION_NONE;
- }
- // If we are NOT a wall or window, ignore....
- if ( pObject->uiNumTilesMoved < 4 )
- {
- switch( iCollisionCode )
- {
- case COLLISION_MERC:
- case COLLISION_STRUCTURE:
- case COLLISION_STRUCTURE_Z:
- // Set to no collision ( we shot past )
- iCollisionCode = COLLISION_NONE;
- break;
- }
- }
- }
- }
- *piCollisionID = iCollisionCode;
- // If We hit the ground
- if ( iCollisionCode > COLLISION_NONE )
- {
- if ( pObject->iOldCollisionCode == iCollisionCode )
- {
- pObject->sConsecutiveCollisions++;
- }
- else
- {
- pObject->sConsecutiveCollisions = 1;
- }
- if ( iCollisionCode == COLLISION_WINDOW_NORTHWEST || iCollisionCode == COLLISION_WINDOW_NORTHEAST || iCollisionCode == COLLISION_WINDOW_SOUTHWEST || iCollisionCode == COLLISION_WINDOW_SOUTHEAST )
- {
- if ( !pObject->fTestObject )
- {
- // Break window!
- PhysicsDebugMsg( String( "Object %d: Collision Window", pObject->iID ) );
- sGridNo = MAPROWCOLTOPOS( ( (INT16)pObject->Position.y / CELL_Y_SIZE ), ( (INT16)pObject->Position.x / CELL_X_SIZE ) );
- ObjectHitWindow( sGridNo, usStructureID, FALSE, TRUE );
- }
- *piCollisionID = COLLISION_NONE;
- return( FALSE );
- }
- // ATE: IF detonate on impact, stop now!
- if ( OBJECT_DETONATE_ON_IMPACT( pObject ) )
- {
- pObject->fAlive = FALSE;
- return( TRUE );
- }
- if ( iCollisionCode == COLLISION_GROUND )
- {
- vTemp.x = 0;
- vTemp.y = 0;
- vTemp.z = -1;
- pObject->fApplyFriction = TRUE;
- //pObject->AppliedMu = (float)(0.54 * TIME_MULTI );
- pObject->AppliedMu = (float)(0.34 * TIME_MULTI );
- //dElasity = (float)1.5;
- dElasity = (float)1.3;
- fDoCollision = TRUE;
- if ( !pObject->fTestObject && !pObject->fHaveHitGround )
- {
- PlayJA2Sample( THROW_IMPACT_2, RATE_11025, SoundVolume( MIDVOLUME, pObject->sGridNo ), 1, SoundDir( pObject->sGridNo ) );
- }
- pObject->fHaveHitGround = TRUE;
- }
- else if ( iCollisionCode == COLLISION_WATER )
- {
- ANITILE_PARAMS AniParams;
- ANITILE *pNode;
- // Continue going...
- pObject->fApplyFriction = TRUE;
- pObject->AppliedMu = (float)(1.54 * TIME_MULTI );
- sGridNo = MAPROWCOLTOPOS( ( (INT16)pObject->Position.y / CELL_Y_SIZE ), ( (INT16)pObject->Position.x / CELL_X_SIZE ) );
- // Make thing unalive...
- pObject->fAlive = FALSE;
- // If first time...
- if ( pObject->fVisible )
- {
- if ( pObject->fTestObject == NO_TEST_OBJECT )
- {
- // Make invisible
- pObject->fVisible = FALSE;
- // JA25 CJC Oct 13 1999 - if node pointer is null don't try to set flags inside it!
- if( pObject->pNode )
- {
- pObject->pNode->uiFlags |= LEVELNODE_HIDDEN;
- }
- pObject->fInWater = TRUE;
- // Make ripple
- memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
- AniParams.sGridNo = sGridNo;
- AniParams.ubLevelID = ANI_STRUCT_LEVEL;
- AniParams.usTileType = THIRDMISS;
- AniParams.usTileIndex = THIRDMISS1;
- AniParams.sDelay = 50;
- AniParams.sStartFrame = 0;
- AniParams.uiFlags = ANITILE_FORWARD;
- if ( pObject->ubActionCode == THROW_ARM_ITEM )
- {
- AniParams.ubKeyFrame1 = 11;
- AniParams.uiKeyFrame1Code = ANI_KEYFRAME_CHAIN_WATER_EXPLOSION;
- AniParams.uiUserData = pObject->Obj.usItem;
- AniParams.ubUserData2 = pObject->ubOwner;
- }
- pNode = CreateAnimationTile( &AniParams );
- // Adjust for absolute positioning
- pNode->pLevelNode->uiFlags |= LEVELNODE_USEABSOLUTEPOS;
- pNode->pLevelNode->sRelativeX = (INT16)pObject->Position.x;
- pNode->pLevelNode->sRelativeY = (INT16)pObject->Position.y;
- pNode->pLevelNode->sRelativeZ = (INT16)CONVERT_HEIGHTUNITS_TO_PIXELS( (INT16)pObject->Position.z );
- }
- }
- }
- else if ( iCollisionCode == COLLISION_ROOF || iCollisionCode == COLLISION_INTERIOR_ROOF )
- {
- vTemp.x = 0;
- vTemp.y = 0;
- vTemp.z = -1;
- pObject->fApplyFriction = TRUE;
- pObject->AppliedMu = (float)(0.54 * TIME_MULTI );
- dElasity = (float)1.4;
- fDoCollision = TRUE;
- }
- //else if ( iCollisionCode == COLLISION_INTERIOR_ROOF )
- //{
- // vTemp.x = 0;
- // vTemp.y = 0;
- // vTemp.z = 1;
- // pObject->fApplyFriction = TRUE;
- // pObject->AppliedMu = (float)(0.54 * TIME_MULTI );
- // dElasity = (float)1.4;
- // fDoCollision = TRUE;
- //}
- else if ( iCollisionCode == COLLISION_STRUCTURE_Z )
- {
- if ( CheckForCatcher( pObject, usStructureID ) )
- {
- return( FALSE );
- }
- CheckForObjectHittingMerc( pObject, usStructureID );
- vTemp.x = 0;
- vTemp.y = 0;
- vTemp.z = -1;
- pObject->fApplyFriction = TRUE;
- pObject->AppliedMu = (float)(0.54 * TIME_MULTI );
- dElasity = (float)1.2;
- fDoCollision = TRUE;
- }
- else if ( iCollisionCode == COLLISION_WALL_SOUTHEAST || iCollisionCode == COLLISION_WALL_SOUTHWEST ||
- iCollisionCode == COLLISION_WALL_NORTHEAST || iCollisionCode == COLLISION_WALL_NORTHWEST )
- {
- // A wall, do stuff
- vTemp.x = dNormalX;
- vTemp.y = dNormalY;
- vTemp.z = dNormalZ;
- fDoCollision = TRUE;
- dElasity = (float)1.1;
- }
- else
- {
- vector_3 vIncident;
- if ( CheckForCatcher( pObject, usStructureID ) )
- {
- return( FALSE );
- }
- CheckForObjectHittingMerc( pObject, usStructureID );
- vIncident.x = dDeltaX;
- vIncident.y = dDeltaY;
- vIncident.z = 0;
- // Nomralize
-
- vIncident = VGetNormal( &vIncident );
- //vTemp.x = -1;
- //vTemp.y = 0;
- //vTemp.z = 0;
- vTemp.x = -1 * vIncident.x;
- vTemp.y = -1 * vIncident.y;
- vTemp.z = 0;
- fDoCollision = TRUE;
- dElasity = (float)1.1;
- }
- if ( fDoCollision )
- {
- pObject->CollisionNormal.x = vTemp.x;
- pObject->CollisionNormal.y = vTemp.y;
- pObject->CollisionNormal.z = vTemp.z;
- pObject->CollisionElasticity = dElasity;
- pObject->iOldCollisionCode = iCollisionCode;
- // Save collision velocity
- pObject->CollisionVelocity = VSetEqual( &(pObject->OldVelocity) );
- if ( pObject->fPotentialForDebug )
- {
- PhysicsDebugMsg( String( "Object %d: Collision %d", pObject->iID, iCollisionCode ) );
- PhysicsDebugMsg( String( "Object %d: Collision Normal %f %f %f", pObject->iID, vTemp.x, vTemp.y, vTemp.z ) );
- PhysicsDebugMsg( String( "Object %d: Collision OldPos %f %f %f", pObject->iID, pObject->Position.x, pObject->Position.y, pObject->Position.z ) );
- PhysicsDebugMsg( String( "Object %d: Collision Velocity %f %f %f", pObject->iID, pObject->CollisionVelocity.x, pObject->CollisionVelocity.y, pObject->CollisionVelocity.z ) );
- }
- pObject->fColliding = TRUE;
- }
- else
- {
- pObject->fColliding = FALSE;
- pObject->sConsecutiveCollisions = 0;
- pObject->sConsecutiveZeroVelocityCollisions = 0;
- pObject->fHaveHitGround = FALSE;
- }
- }
- return( fDoCollision );
- }
- void PhysicsResolveCollision( REAL_OBJECT *pObject, vector_3 *pVelocity, vector_3 *pNormal, real CoefficientOfRestitution )
- {
- real ImpulseNumerator, Impulse;
- vector_3 vTemp;
-
- ImpulseNumerator = -1 * CoefficientOfRestitution * VDotProduct( pVelocity , pNormal );
- Impulse = ImpulseNumerator;
- vTemp = VMultScalar( pNormal, Impulse );
- pObject->Velocity = VAdd( &(pObject->Velocity), &vTemp );
- }
- BOOLEAN PhysicsMoveObject( REAL_OBJECT *pObject )
- {
- LEVELNODE *pNode;
- INT16 sNewGridNo, sTileIndex;
- ETRLEObject *pTrav;
- HVOBJECT hVObject;
-
- //Determine new gridno
- sNewGridNo = MAPROWCOLTOPOS( ( (INT16)pObject->Position.y / CELL_Y_SIZE ), ( (INT16)pObject->Position.x / CELL_X_SIZE ) );
- if ( pObject->fFirstTimeMoved )
- {
- pObject->fFirstTimeMoved = FALSE;
- pObject->sFirstGridNo = sNewGridNo;
- }
- // CHECK FOR RANGE< IF INVALID, REMOVE!
- if ( sNewGridNo == -1 )
- {
- PhysicsDeleteObject( pObject );
- return( FALSE );
- }
- // Look at old gridno
- if ( sNewGridNo != pObject->sGridNo || pObject->pNode == NULL )
- {
- if ( pObject->fVisible )
- {
- if ( CheckForCatchObject( pObject ) )
- {
- pObject->fVisible = FALSE;
- }
- }
- if ( pObject->fVisible )
- {
- // Add smoke trails...
- if ( pObject->Obj.usItem == MORTAR_SHELL && pObject->uiNumTilesMoved > 2 && pObject->ubActionCode == THROW_ARM_ITEM )
- {
- if ( sNewGridNo != pObject->sGridNo )
- {
- ANITILE_PARAMS AniParams;
- AniParams.sGridNo = (INT16)sNewGridNo;
- AniParams.ubLevelID = ANI_STRUCT_LEVEL;
- AniParams.sDelay = (INT16)( 100 + PreRandom( 100 ) );
- AniParams.sStartFrame = 0;
- AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_ALWAYS_TRANSLUCENT;
- AniParams.sX = (INT16)pObject->Position.x;
- AniParams.sY = (INT16)pObject->Position.y;
- AniParams.sZ = (INT16)CONVERT_HEIGHTUNITS_TO_PIXELS( (INT16)pObject->Position.z );
- strcpy( AniParams.zCachedFile, "TILECACHE\\MSLE_SMK.STI" );
- CreateAnimationTile( &AniParams );
- }
- }
- else if ( pObject->uiNumTilesMoved > 0 )
- {
- if ( sNewGridNo != pObject->sGridNo )
- {
- // We're at a new gridno!
- if ( pObject->pNode != NULL )
- {
- RemoveStructFromLevelNode( pObject->sLevelNodeGridNo, pObject->pNode );
- }
- // We're at a new gridno!
- if ( pObject->pShadow != NULL )
- {
- RemoveShadowFromLevelNode( pObject->sLevelNodeGridNo, pObject->pShadow );
- }
- // Now get graphic index
- sTileIndex = GetTileGraphicForItem( &(Item[ pObject->Obj.usItem ] ) );
- //sTileIndex = BULLETTILE1;
- // Set new gridno, add
- pNode = AddStructToTail( sNewGridNo, sTileIndex );
- pNode->ubShadeLevel=DEFAULT_SHADE_LEVEL;
- pNode->ubNaturalShadeLevel=DEFAULT_SHADE_LEVEL;
- pNode->uiFlags |= ( LEVELNODE_USEABSOLUTEPOS | LEVELNODE_IGNOREHEIGHT | LEVELNODE_PHYSICSOBJECT | LEVELNODE_DYNAMIC );
- // Set levelnode
- pObject->pNode = pNode;
- // Add shadow
- AddShadowToHead( sNewGridNo, sTileIndex );
- pNode = gpWorldLevelData[ sNewGridNo ].pShadowHead;
- pNode->ubShadeLevel=DEFAULT_SHADE_LEVEL;
- pNode->ubNaturalShadeLevel=DEFAULT_SHADE_LEVEL;
- pNode->uiFlags |= ( LEVELNODE_USEABSOLUTEPOS | LEVELNODE_IGNOREHEIGHT | LEVELNODE_PHYSICSOBJECT | LEVELNODE_DYNAMIC );
- // Set levelnode
- pObject->pShadow = pNode;
- pObject->sLevelNodeGridNo = sNewGridNo;
- }
- }
- }
- else
- {
- // Remove!
- if ( pObject->pNode != NULL )
- {
- RemoveStructFromLevelNode( pObject->sLevelNodeGridNo, pObject->pNode );
- }
- // We're at a new gridno!
- if ( pObject->pShadow != NULL )
- {
- RemoveShadowFromLevelNode( pObject->sLevelNodeGridNo, pObject->pShadow );
- }
- pObject->pNode = NULL;
- pObject->pShadow = NULL;
- }
- if ( sNewGridNo != pObject->sGridNo )
- {
- pObject->uiNumTilesMoved++;
- }
- pObject->sGridNo = sNewGridNo;
- if ( pObject->fPotentialForDebug )
- {
- PhysicsDebugMsg( String( "Object %d: uiNumTilesMoved: %d", pObject->iID, pObject->uiNumTilesMoved ) );
- }
- }
- if ( pObject->fVisible )
- {
- if ( pObject->Obj.usItem != MORTAR_SHELL || pObject->ubActionCode != THROW_ARM_ITEM )
- {
- if ( pObject->pNode != NULL )
- {
- // OK, get offsets
- hVObject = gTileDatabase[ pObject->pNode->usIndex ].hTileSurface;
- pTrav = &(hVObject->pETRLEObject[ gTileDatabase[ pObject->pNode->usIndex ].usRegionIndex ] );
- // Add new object / update position
- // Update position data
- pObject->pNode->sRelativeX = (INT16)pObject->Position.x; // + pTrav->sOffsetX;
- pObject->pNode->sRelativeY = (INT16)pObject->Position.y; // + pTrav->sOffsetY;
- pObject->pNode->sRelativeZ = (INT16)CONVERT_HEIGHTUNITS_TO_PIXELS( (INT16)pObject->Position.z );
- // Update position data
- pObject->pShadow->sRelativeX = (INT16)pObject->Position.x; // + pTrav->sOffsetX;
- pObject->pShadow->sRelativeY = (INT16)pObject->Position.y; // + pTrav->sOffsetY;
- pObject->pShadow->sRelativeZ = (INT16)gpWorldLevelData[ pObject->sGridNo ].sHeight;
- }
- }
- }
- return( TRUE );
- }
- #if 0
- {
- LEVELNODE *pNode;
- INT16 sNewGridNo;
-
- //Determine new gridno
- sNewGridNo = MAPROWCOLTOPOS( ( pObject->Position.y / CELL_Y_SIZE ), ( pObject->Position.x / CELL_X_SIZE ) );
- // Look at old gridno
- if ( sNewGridNo != pObject->sGridNo )
- {
- // We're at a new gridno!
- // First find levelnode of our object and delete
- pNode = gpWorldLevelData[ pObject->sGridNo ].pStructHead;
- // LOOP THORUGH OBJECT LAYER
- while( pNode != NULL )
- {
- if ( pNode->uiFlags & LEVELNODE_PHYSICSOBJECT )
- {
- if ( pNode->iPhysicsObjectID == pObject->iID )
- {
- RemoveStruct( pObject->sGridNo, pNode->usIndex );
- break;
- }
- }
- pNode = ppNode->pNext;
- }
- // Set new gridno, add
- }
- // Add new object / update position
- }
- #endif
- void ObjectHitWindow( INT16 sGridNo, UINT16 usStructureID, BOOLEAN fBlowWindowSouth, BOOLEAN fLargeForce )
- {
- EV_S_WINDOWHIT SWindowHit;
- SWindowHit.sGridNo = sGridNo;
- SWindowHit.usStructureID = usStructureID;
- SWindowHit.fBlowWindowSouth = fBlowWindowSouth;
- SWindowHit.fLargeForce = fLargeForce;
- //AddGameEvent( S_WINDOWHIT, 0, &SWindowHit );
- WindowHit( sGridNo, usStructureID, fBlowWindowSouth, fLargeForce );
- }
- vector_3 FindBestForceForTrajectory( INT16 sSrcGridNo, INT16 sGridNo,INT16 sStartZ, INT16 sEndZ, real dzDegrees, OBJECTTYPE *pItem, INT16 *psGridNo, real *pdMagForce )
- {
- vector_3 vDirNormal, vPosition, vForce;
- INT16 sDestX, sDestY, sSrcX, sSrcY;
- real dForce = 20;
- real dRange;
- real dPercentDiff = 0;
- real dTestRange, dTestDiff;
- INT32 iNumChecks = 0;
- // Get XY from gridno
- ConvertGridNoToCenterCellXY( sGridNo, &sDestX, &sDestY );
- ConvertGridNoToCenterCellXY( sSrcGridNo, &sSrcX, &sSrcY );
- // Set position
- vPosition.x = sSrcX;
- vPosition.y = sSrcY;
- vPosition.z = sStartZ;
- // OK, get direction normal
- vDirNormal.x = (float)(sDestX - sSrcX);
- vDirNormal.y = (float)(sDestY - sSrcY);
- vDirNormal.z = 0;
- // NOmralize
- vDirNormal = VGetNormal( &vDirNormal );
- // From degrees, calculate Z portion of normal
- vDirNormal.z = (float)sin( dzDegrees );
- // Get range
- dRange = (float)GetRangeInCellCoordsFromGridNoDiff( sGridNo, sSrcGridNo );
- //calculate force needed
- {
- dForce = (float)( 12 * ( sqrt( ( GRAVITY * dRange ) / sin( 2 * dzDegrees ) ) ) );
- }
- do
- {
- // This first force is just an estimate...
- // now di a binary search to find best value....
- iNumChecks++;
- // Now use a force
- vForce.x = dForce * vDirNormal.x;
- vForce.y = dForce * vDirNormal.y;
- vForce.z = dForce * vDirNormal.z;
- dTestRange = CalculateObjectTrajectory( sEndZ, pItem, &vPosition, &vForce, psGridNo );
- // What's the diff?
- dTestDiff = dTestRange - dRange;
-
- // How have we done?
- // < 5% off...
- if ( fabs( ( dTestDiff / dRange ) ) < .01 )
- {
- break;
- }
- if ( iNumChecks > MAX_INTEGRATIONS )
- {
- break;
- }
- // What is the Percentage difference?
- dPercentDiff = dForce * ( dTestDiff / dRange );
- // Adjust force accordingly
- dForce = dForce - ( ( dPercentDiff ) / 2 );
- } while( TRUE );
- // OK, we have our force, calculate change to get through without collide
- //if ( ChanceToGetThroughObjectTrajectory( sEndZ, pItem, &vPosition, &vForce, NULL ) == 0 )
- {
- //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Chance to get through throw is 0." );
- }
- if ( pdMagForce )
- {
- (*pdMagForce) = dForce;
- }
- #ifdef JA2TESTVERSION
- //ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Number of integration: %d", iNumChecks );
- #endif
- return( vForce );
- }
- INT16 FindFinalGridNoGivenDirectionGridNoForceAngle( INT16 sSrcGridNo, INT16 sGridNo, INT16 sStartZ, INT16 sEndZ, real dForce, real dzDegrees, OBJECTTYPE *pItem )
- {
- vector_3 vDirNormal, vPosition, vForce;
- INT16 sDestX, sDestY, sSrcX, sSrcY;
- real dRange;
- INT16 sEndGridNo;
- // Get XY from gridno
- ConvertGridNoToCenterCellXY( sGridNo, &sDestX, &sDestY );
- ConvertGridNoToCenterCellXY( sSrcGridNo, &sSrcX, &sSrcY );
- // Set position
- vPosition.x = sSrcX;
- vPosition.y = sSrcY;
- vPosition.z = sStartZ;
- // OK, get direction normal
- vDirNormal.x = (float)(sDestX - sSrcX);
- vDirNormal.y = (float)(sDestY - sSrcY);
- vDirNormal.z = 0;
- // NOmralize
- vDirNormal = VGetNormal( &vDirNormal );
- // From degrees, calculate Z portion of normal
- vDirNormal.z = (float)sin( dzDegrees );
- // Get range
- dRange = (float)GetRangeInCellCoordsFromGridNoDiff( sGridNo, sSrcGridNo );
- // Now use a force
- vForce.x = dForce * vDirNormal.x;
- vForce.y = dForce * vDirNormal.y;
- vForce.z = dForce * vDirNormal.z;
- CalculateObjectTrajectory( sEndZ, pItem, &vPosition, &vForce, &sEndGridNo );
- return( sEndGridNo );
- }
- real FindBestAngleForTrajectory( INT16 sSrcGridNo, INT16 sGridNo,INT16 sStartZ, INT16 sEndZ, real dForce, OBJECTTYPE *pItem, INT16 *psGridNo )
- {
- vector_3 vDirNormal, vPosition, vForce;
- INT16 sDestX, sDestY, sSrcX, sSrcY;
- real dRange;
- real dzDegrees = ( (float)PI/8 );
- real dPercentDiff = 0;
- real dTestRange, dTestDiff;
- INT32 iNumChecks = 0;
- // Get XY from gridno
- ConvertGridNoToCenterCellXY( sGridNo, &sDestX, &sDestY );
- ConvertGridNoToCenterCellXY( sSrcGridNo, &sSrcX, &sSrcY );
- // Set position
- vPosition.x = sSrcX;
- vPosition.y = sSrcY;
- vPosition.z = sStartZ;
- // OK, get direction normal
- vDirNormal.x = (float)(sDestX - sSrcX);
- vDirNormal.y = (float)(sDestY - sSrcY);
- vDirNormal.z = 0;
- // NOmralize
- vDirNormal = VGetNormal( &vDirNormal );
- // From degrees, calculate Z portion of normal
- vDirNormal.z = (float)sin( dzDegrees );
- // Get range
- dRange = (float)GetRangeInCellCoordsFromGridNoDiff( sGridNo, sSrcGridNo );
- do
- {
- // This first direction is just an estimate...
- // now do a binary search to find best value....
- iNumChecks++;
- // Now use a force
- vForce.x = dForce * vDirNormal.x;
- vForce.y = dForce * vDirNormal.y;
- vForce.z = dForce * vDirNormal.z;
- dTestRange = CalculateObjectTrajectory( sEndZ, pItem, &vPosition, &vForce, psGridNo );
- // What's the diff?
- dTestDiff = dTestRange - dRange;
-
- // How have we done?
- // < 5% off...
- if ( fabs( (FLOAT)( dTestDiff / dRange ) ) < .05 )
- {
- break;
- }
- if ( iNumChecks > MAX_INTEGRATIONS )
- {
- break;
- }
- // What is the Percentage difference?
- dPercentDiff = dzDegrees * ( dTestDiff / dRange );
- // Adjust degrees accordingly
- dzDegrees = dzDegrees - ( dPercentDiff / 2 );
- // OK, If our angle is too far either way, giveup!
- if ( fabs( dzDegrees ) >= ( PI / 2 ) || fabs( dzDegrees ) <= 0.005 )
- {
- // Use 0.....
- dzDegrees = 0;
- // From degrees, calculate Z portion of normal
- vDirNormal.z = (float)sin( dzDegrees );
- // Now use a force
- vForce.x = dForce * vDirNormal.x;
- vForce.y = dForce * vDirNormal.y;
- vForce.z = dForce * vDirNormal.z;
- dTestRange = CalculateObjectTrajectory( sEndZ, pItem, &vPosition, &vForce, psGridNo );
- return( (FLOAT)( dzDegrees ) );
- }
- // From degrees, calculate Z portion of normal
- vDirNormal.z = (float)sin( dzDegrees );
- } while( TRUE );
- // OK, we have our force, calculate change to get through without collide
- //if ( ChanceToGetThroughObjectTrajectory( sEndZ, pItem, &vPosition, &vForce ) == 0 )
- //{
- // ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Chance to get through throw is 0." );
- //}
- return( dzDegrees );
- }
- void FindTrajectory( INT16 sSrcGridNo, INT16 sGridNo, INT16 sStartZ, INT16 sEndZ, real dForce, real dzDegrees, OBJECTTYPE *pItem, INT16 *psGridNo )
- {
- vector_3 vDirNormal, vPosition, vForce;
- INT16 sDestX, sDestY, sSrcX, sSrcY;
- // Get XY from gridno
- ConvertGridNoToCenterCellXY( sGridNo, &sDestX, &sDestY );
- ConvertGridNoToCenterCellXY( sSrcGridNo, &sSrcX, &sSrcY );
- // Set position
- vPosition.x = sSrcX;
- vPosition.y = sSrcY;
- vPosition.z = sStartZ;
- // OK, get direction normal
- vDirNormal.x = (float)(sDestX - sSrcX);
- vDirNormal.y = (float)(sDestY - sSrcY);
- vDirNormal.z = 0;
- // NOmralize
- vDirNormal = VGetNormal( &vDirNormal );
- // From degrees, calculate Z portion of normal
- vDirNormal.z = (float)sin( dzDegrees );
- // Now use a force
- vForce.x = dForce * vDirNormal.x;
- vForce.y = dForce * vDirNormal.y;
- vForce.z = dForce * vDirNormal.z;
- CalculateObjectTrajectory( sEndZ, pItem, &vPosition, &vForce, psGridNo );
- }
- // OK, this will, given a target Z, INVTYPE, source, target gridnos, initial force vector, will
- // return range
- FLOAT CalculateObjectTrajectory( INT16 sTargetZ, OBJECTTYPE *pItem, vector_3 *vPosition, vector_3 *vForce, INT16 *psFinalGridNo )
- {
- INT32 iID;
- REAL_OBJECT *pObject;
- FLOAT dDiffX, dDiffY;
- INT16 sGridNo;
- if ( psFinalGridNo )
- {
- (*psFinalGridNo) = NOWHERE;
- }
- // OK, create a physics object....
- iID = CreatePhysicalObject( pItem, -1, vPosition->x, vPosition->y, vPosition->z, vForce->x, vForce->y, vForce->z, NOBODY, NO_THROW_ACTION, 0 );
- if ( iID == -1 )
- {
- return( -1 );
- }
- pObject = &( ObjectSlots[ iID ] );
- // Set some special values...
- pObject->fTestObject = TEST_OBJECT_NO_COLLISIONS;
- pObject->TestZTarget = sTargetZ;
- pObject->fTestPositionNotSet = TRUE;
- pObject->fVisible = FALSE;
- // Alrighty, move this beast until it dies....
- while( pObject->fAlive )
- {
- SimulateObject( pObject, (float)DELTA_T );
- }
- // Calculate gridno from last position
- sGridNo = MAPROWCOLTOPOS( ( (INT16)pObject->Position.y / CELL_Y_SIZE ), ( (INT16)pObject->Position.x / CELL_X_SIZE ) );
- PhysicsDeleteObject( pObject );
- // get new x, y, z values
- dDiffX = ( pObject->TestTargetPosition.x - vPosition->x );
- dDiffY = ( pObject->TestTargetPosition.y - vPosition->y );
- if ( psFinalGridNo )
- {
- (*psFinalGridNo) = sGridNo;
- }
- return( (FLOAT)sqrt( ( dDiffX * dDiffX ) + ( dDiffY * dDiffY ) ) );
-
- }
- INT32 ChanceToGetThroughObjectTrajectory( INT16 sTargetZ, OBJECTTYPE *pItem, vector_3 *vPosition, vector_3 *vForce, INT16 *psNewGridNo, INT8 *pbLevel, BOOLEAN fFromUI )
- {
- INT32 iID;
- REAL_OBJECT *pObject;
- // OK, create a physics object....
- iID = CreatePhysicalObject( pItem, -1, vPosition->x, vPosition->y, vPosition->z, vForce->x, vForce->y, vForce->z, NOBODY, NO_THROW_ACTION, 0 );
- if ( iID == -1 )
- {
- return( -1 );
- }
- pObject = &( ObjectSlots[ iID ] );
- // Set some special values...
- pObject->fTestObject = TEST_OBJECT_NOTWALLROOF_COLLISIONS;
- pObject->fTestPositionNotSet = TRUE;
- pObject->TestZTarget = sTargetZ;
- pObject->fVisible = FALSE;
- //pObject->fPotentialForDebug = TRUE;
- // Alrighty, move this beast until it dies....
- while( pObject->fAlive )
- {
- SimulateObject( pObject, (float)DELTA_T );
- }
- if ( psNewGridNo != NULL )
- {
- // Calculate gridno from last position
- // If NOT from UI, use exact collision position
- if ( fFromUI )
- {
- (*psNewGridNo) = MAPROWCOLTOPOS( ( (INT16)pObject->Position.y / CELL_Y_SIZE ), ( (INT16)pObject->Position.x / CELL_X_SIZE ) );
- }
- else
- {
- (*psNewGridNo) = MAPROWCOLTOPOS( ( (INT16)pObject->EndedWithCollisionPosition.y / CELL_Y_SIZE ), ( (INT16)pObject->EndedWithCollisionPosition.x / CELL_X_SIZE ) );
- }
- (*pbLevel) = GET_OBJECT_LEVEL( pObject->EndedWithCollisionPosition.z - CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ (*psNewGridNo) ].sHeight ) );
- }
- PhysicsDeleteObject( pObject );
- // See If we collided
- if ( pObject->fTestEndedWithCollision )
- {
- return( 0 );
- }
- return( 100 );
-
- }
- FLOAT CalculateLaunchItemAngle( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubHeight, real dForce, OBJECTTYPE *pItem, INT16 *psGridNo )
- {
- real dAngle;
- INT16 sSrcX, sSrcY;
- ConvertGridNoToCenterCellXY( pSoldier->sGridNo, &sSrcX, &sSrcY );
- dAngle = FindBestAngleForTrajectory( pSoldier->sGridNo, sGridNo, GET_SOLDIER_THROW_HEIGHT( pSoldier->bLevel ), ubHeight, dForce, pItem, psGridNo );
- // new we have defaut angle value...
- return( dAngle );
- }
- void CalculateLaunchItemBasicParams( SOLDIERTYPE *pSoldier, OBJECTTYPE *pItem, INT16 sGridNo, UINT8 ubLevel, INT16 sEndZ, FLOAT *pdMagForce, FLOAT *pdDegrees, INT16 *psFinalGridNo, BOOLEAN fArmed )
- {
- INT16 sInterGridNo;
- INT16 sStartZ;
- FLOAT dMagForce, dMaxForce, dMinForce;
- FLOAT dDegrees, dNewDegrees;
- BOOLEAN fThroughIntermediateGridNo = FALSE;
- UINT16 usLauncher;
- BOOLEAN fIndoors = FALSE;
- BOOLEAN fLauncher = FALSE;
- BOOLEAN fMortar = FALSE;
- BOOLEAN fGLauncher = FALSE;
- INT16 sMinRange = 0;
- // Start with default degrees/ force
- dDegrees = OUTDOORS_START_ANGLE;
- sStartZ = GET_SOLDIER_THROW_HEIGHT( pSoldier->bLevel );
- // Are we armed, and are we throwing a LAUNCHABLE?
- usLauncher = GetLauncherFromLaunchable( pItem->usItem );
-
- if ( fArmed && ( usLauncher == MORTAR || pItem->usItem == MORTAR ) )
- {
- // Start at 0....
- sStartZ = ( pSoldier->bLevel * 256 );
- fMortar = TRUE;
- sMinRange = MIN_MORTAR_RANGE;
- //fLauncher = TRUE;
- }
-
- if ( fArmed && ( usLauncher == GLAUNCHER || usLauncher == UNDER_GLAUNCHER || pItem->usItem == GLAUNCHER || pItem->usItem == UNDER_GLAUNCHER ) )
- {
- // OK, look at target level and decide angle to use...
- if ( ubLevel == 1 )
- {
- //dDegrees = GLAUNCHER_START_ANGLE;
- dDegrees = GLAUNCHER_HIGHER_LEVEL_START_ANGLE;
- }
- else
- {
- dDegrees = GLAUNCHER_START_ANGLE;
- }
- fGLauncher = TRUE;
- sMinRange = MIN_MORTAR_RANGE;
- //fLauncher = TRUE;
- }
- // CHANGE DEGREE VALUES BASED ON IF WE ARE INSIDE, ETC
- // ARE WE INSIDE?
- if ( gfCaves || gfBasement )
- {
- // Adjust angle....
- dDegrees = INDOORS_START_ANGLE;
- fIndoors = TRUE;
- }
- if ( ( IsRoofPresentAtGridno( pSoldier->sGridNo ) ) && pSoldier->bLevel == 0 )
- {
- // Adjust angle....
- dDegrees = INDOORS_START_ANGLE;
- fIndoors = TRUE;
- }
- // IS OUR TARGET INSIDE?
- if ( IsRoofPresentAtGridno( sGridNo ) && ubLevel == 0 )
- {
- // Adjust angle....
- dDegrees = INDOORS_START_ANGLE;
- fIndoors = TRUE;
- }
- // OK, look if we can go through a windows here...
- if ( ubLevel == 0 )
- {
- sInterGridNo = SoldierToLocationWindowTest( pSoldier, sGridNo );
- }
- else
- {
- sInterGridNo = NOWHERE;
- }
- if ( sInterGridNo != NOWHERE )
- {
- // IF so, adjust target height, gridno....
- ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Through a window!" );
- fThroughIntermediateGridNo = TRUE;
- }
- if ( !fLauncher )
- {
- // Find force for basic
- FindBestForceForTrajectory( pSoldier->sGridNo, sGridNo, sStartZ, sEndZ, dDegrees, pItem, psFinalGridNo, &dMagForce );
- // Adjust due to max range....
- dMaxForce = CalculateSoldierMaxForce( pSoldier, dDegrees, pItem, fArmed );
- if ( fIndoors )
- {
- dMaxForce = dMaxForce * 2;
- }
- if ( dMagForce > dMaxForce )
- {
- dMagForce = dMaxForce;
- }
- // ATE: If we are a mortar, make sure we are at min.
- if ( fMortar || fGLauncher )
- {
- // find min force
- dMinForce = CalculateForceFromRange( (INT16)( sMinRange / 10 ), (FLOAT)( PI / 4 ) );
- if ( dMagForce < dMinForce )
- {
- dMagForce = dMinForce;
- }
- }
- if ( fThroughIntermediateGridNo )
- {
- // Given this power, now try and go through this window....
- dDegrees = FindBestAngleForTrajectory( pSoldier->sGridNo, sInterGridNo, GET_SOLDIER_THROW_HEIGHT( pSoldier->bLevel ), 150, dMagForce, pItem, psFinalGridNo );
- }
- }
- else
- {
- // Use MAX force, vary angle....
- dMagForce = CalculateSoldierMaxForce( pSoldier, dDegrees, pItem, fArmed );
- if ( ubLevel == 0 )
- {
- dMagForce = (float)( dMagForce * 1.25 );
- }
-
- FindTrajectory( pSoldier->sGridNo, sGridNo, sStartZ, sEndZ, dMagForce, dDegrees, pItem, psFinalGridNo );
- if ( ubLevel == 1 && !fThroughIntermediateGridNo )
- {
- // Is there a guy here...?
- if ( WhoIsThere2( sGridNo, ubLevel ) != NO_SOLDIER )
- {
- dMagForce = (float)( dMagForce * 0.85 );
- // Yep, try to get angle...
- dNewDegrees = FindBestAngleForTrajectory( pSoldier->sGridNo, sGridNo, GET_SOLDIER_THROW_HEIGHT( pSoldier->bLevel ), 150, dMagForce, pItem, psFinalGridNo );
- if ( dNewDegrees != 0 )
- {
- dDegrees = dNewDegrees;
- }
- }
- }
- if ( fThroughIntermediateGridNo )
- {
- dDegrees = FindBestAngleForTrajectory( pSoldier->sGridNo, sInterGridNo, GET_SOLDIER_THROW_HEIGHT( pSoldier->bLevel ), 150, dMagForce, pItem, psFinalGridNo );
- }
- }
- (*pdMagForce) = dMagForce;
- (*pdDegrees ) = dDegrees;
- }
- BOOLEAN CalculateLaunchItemChanceToGetThrough( SOLDIERTYPE *pSoldier, OBJECTTYPE *pItem, INT16 sGridNo, UINT8 ubLevel, INT16 sEndZ, INT16 *psFinalGridNo, BOOLEAN fArmed, INT8 *pbLevel, BOOLEAN fFromUI )
- {
- FLOAT dForce, dDegrees;
- INT16 sDestX, sDestY, sSrcX, sSrcY;
- vector_3 vForce, vPosition, vDirNormal;
- // Ge7t basic launch params...
- CalculateLaunchItemBasicParams( pSoldier, pItem, sGridNo, ubLevel, sEndZ, &dForce, &dDegrees, psFinalGridNo, fArmed );
- // Get XY from gridno
- ConvertGridNoToCenterCellXY( sGridNo, &sDestX, &sDestY );
- ConvertGridNoToCenterCellXY( pSoldier->sGridNo, &sSrcX, &sSrcY );
- // Set position
- vPosition.x = sSrcX;
- vPosition.y = sSrcY;
- vPosition.z = GET_SOLDIER_THROW_HEIGHT( pSoldier->bLevel );
- // OK, get direction normal
- vDirNormal.x = (float)(sDestX - sSrcX);
- vDirNormal.y = (float)(sDestY - sSrcY);
- vDirNormal.z = 0;
- // NOmralize
- vDirNormal = VGetNormal( &vDirNormal );
- // From degrees, calculate Z portion of normal
- vDirNormal.z = (float)sin( dDegrees );
- // Do force....
- vForce.x = dForce * vDirNormal.x;
- vForce.y = dForce * vDirNormal.y;
- vForce.z = dForce * vDirNormal.z;
- // OK, we have our force, calculate change to get through without collide
- if ( ChanceToGetThroughObjectTrajectory( sEndZ, pItem, &vPosition, &vForce, psFinalGridNo, pbLevel, fFromUI ) == 0 )
- {
- return( FALSE );
- }
- if ( (*pbLevel) != ubLevel )
- {
- return( FALSE );
- }
- if ( !fFromUI && (*psFinalGridNo) != sGridNo )
- {
- return( FALSE );
- }
- return( TRUE );
- }
- FLOAT CalculateForceFromRange( INT16 sRange, FLOAT dDegrees )
- {
- FLOAT dMagForce;
- INT16 sSrcGridNo, sDestGridNo;
- OBJECTTYPE Object;
- INT16 sFinalGridNo;
- // OK, use a fake gridno, find the new gridno based on range, use height of merc, end height of ground,
- // 45 degrees
- sSrcGridNo = 4408;
- sDestGridNo = 4408 + ( sRange * WORLD_COLS );
- // Use a grenade objecttype
- CreateItem( HAND_GRENADE, 100, &Object );
- FindBestForceForTrajectory( sSrcGridNo, sDestGridNo, GET_SOLDIER_THROW_HEIGHT( 0 ), 0, dDegrees, &Object, &sFinalGridNo, &dMagForce );
- return( dMagForce );
- }
- FLOAT CalculateSoldierMaxForce( SOLDIERTYPE *pSoldier, FLOAT dDegrees , OBJECTTYPE *pItem , BOOLEAN fArmed )
- {
- INT32 uiMaxRange;
- FLOAT dMagForce;
-
- dDegrees = (FLOAT)( PI/4 );
- uiMaxRange = CalcMaxTossRange( pSoldier, pItem->usItem, fArmed );
- dMagForce = CalculateForceFromRange( (INT16) uiMaxRange, dDegrees );
- return( dMagForce );
- }
- #define MAX_MISS_BY 30
- #define MIN_MISS_BY 1
- #define MAX_MISS_RADIUS 5
- void CalculateLaunchItemParamsForThrow( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubLevel, INT16 sEndZ, OBJECTTYPE *pItem, INT8 bMissBy, UINT8 ubActionCode, UINT32 uiActionData )
- {
- FLOAT dForce, dDegrees;
- INT16 sDestX, sDestY, sSrcX, sSrcY;
- vector_3 vForce, vDirNormal;
- INT16 sFinalGridNo;
- BOOLEAN fArmed = FALSE;
- UINT16 usLauncher;
- INT16 sStartZ;
- INT8 bMinMissRadius, bMaxMissRadius, bMaxRadius;
- FLOAT fScale;
- // Set target ID if anyone
- pSoldier->ubTargetID = WhoIsThere2( sGridNo, ubLevel );
- if ( ubActionCode == THROW_ARM_ITEM )
- {
- fArmed = TRUE;
- }
- if ( bMissBy < 0 )
- {
- // then we hit!
- bMissBy = 0;
- }
- //if ( 0 )
- if ( bMissBy > 0 )
- {
- // Max the miss variance
- if ( bMissBy > MAX_MISS_BY )
- {
- bMissBy = MAX_MISS_BY;
- }
- // Min the miss varience...
- if ( bMissBy < MIN_MISS_BY )
- {
- bMissBy = MIN_MISS_BY;
- }
- // Adjust position, force, angle
- #ifdef JA2TESTVERSION
- ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Throw miss by: %d", bMissBy );
- #endif
- // Default to max radius...
- bMaxRadius = 5;
- // scale if pyth spaces away is too far
- if ( PythSpacesAway( sGridNo, pSoldier->sGridNo ) < ( (float)bMaxRadius / (float)1.5 ) )
- {
- bMaxRadius = PythSpacesAway( sGridNo, pSoldier->sGridNo ) / 2;
- }
- // Get radius
- fScale = ( (float)bMissBy / (float) MAX_MISS_BY );
- bMaxMissRadius = (INT8)( bMaxRadius * fScale );
- // Limit max radius...
- if ( bMaxMissRadius > 4 )
- {
- bMaxMissRadius = 4;
- }
- bMinMissRadius = bMaxMissRadius - 1;
- if ( bMinMissRadius < 2 )
- {
- bMinMissRadius = 2;
- }
- if ( bMaxMissRadius < bMinMissRadius )
- {
- bMaxMissRadius = bMinMissRadius;
- }
- sGridNo = RandomGridFromRadius( sGridNo, bMinMissRadius, bMaxMissRadius );
- }
-
- // Get basic launch params...
- CalculateLaunchItemBasicParams( pSoldier, pItem, sGridNo, ubLevel, sEndZ, &dForce, &dDegrees, &sFinalGridNo, fArmed );
- // Get XY from gridno
- ConvertGridNoToCenterCellXY( sGridNo, &sDestX, &sDestY );
- ConvertGridNoToCenterCellXY( pSoldier->sGridNo, &sSrcX, &sSrcY );
- // OK, get direction normal
- vDirNormal.x = (float)(sDestX - sSrcX);
- vDirNormal.y = (float)(sDestY - sSrcY);
- vDirNormal.z = 0;
- // NOmralize
- vDirNormal = VGetNormal( &vDirNormal );
- // From degrees, calculate Z portion of normal
- vDirNormal.z = (float)sin( dDegrees );
- // Do force....
- vForce.x = dForce * vDirNormal.x;
- vForce.y = dForce * vDirNormal.y;
- vForce.z = dForce * vDirNormal.z;
- // Allocate Throw Parameters
- pSoldier->pThrowParams = MemAlloc( sizeof( THROW_PARAMS ) );
- memset( pSoldier->pThrowParams, 0, sizeof( THROW_PARAMS ) );
- pSoldier->pTempObject = MemAlloc( sizeof( OBJECTTYPE ) );
- memcpy( pSoldier->pTempObject, pItem, sizeof( OBJECTTYPE ) );
- pSoldier->pThrowParams->dX = (float)sSrcX;
- pSoldier->pThrowParams->dY = (float)sSrcY;
- sStartZ = GET_SOLDIER_THROW_HEIGHT( pSoldier->bLevel );
- usLauncher = GetLauncherFromLaunchable( pItem->usItem );
- if ( fArmed && usLauncher == MORTAR )
- {
- // Start at 0....
- sStartZ = ( pSoldier->bLevel * 256 ) + 50;
- }
- pSoldier->pThrowParams->dZ = (float)sStartZ;
- pSoldier->pThrowParams->dForceX = vForce.x;
- pSoldier->pThrowParams->dForceY = vForce.y;
- pSoldier->pThrowParams->dForceZ = vForce.z;
- pSoldier->pThrowParams->dLifeSpan = -1;
- pSoldier->pThrowParams->ubActionCode = ubActionCode;
- pSoldier->pThrowParams->uiActionData = uiActionData;
- // Dirty interface
- DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
- }
- BOOLEAN CheckForCatcher( REAL_OBJECT *pObject, UINT16 usStructureID )
- {
- // Do we want to catch?
- if ( pObject->fTestObject == NO_TEST_OBJECT )
- {
- if ( pObject->ubActionCode == THROW_TARGET_MERC_CATCH )
- {
- // Is it a guy?
- if ( usStructureID < INVALID_STRUCTURE_ID )
- {
- //Is it the same guy?
- if ( usStructureID == pObject->uiActionData )
- {
- if ( DoCatchObject( pObject ) )
- {
- pObject->fAlive = FALSE;
- return( TRUE );
- }
- }
- }
- }
- }
- return( FALSE );
- }
- void CheckForObjectHittingMerc( REAL_OBJECT *pObject, UINT16 usStructureID )
- {
- SOLDIERTYPE *pSoldier;
- INT16 sDamage, sBreath;
- // Do we want to catch?
- if ( pObject->fTestObject == NO_TEST_OBJECT )
- {
- // Is it a guy?
- if ( usStructureID < INVALID_STRUCTURE_ID )
- {
- if ( pObject->ubLastTargetTakenDamage != (UINT8)usStructureID )
- {
- pSoldier = MercPtrs[ usStructureID ];
- sDamage = 1;
- sBreath = 0;
- EVENT_SoldierGotHit( pSoldier, NOTHING, sDamage, sBreath, pSoldier->bDirection, 0, pObject->ubOwner, FIRE_WEAPON_TOSSED_OBJECT_SPECIAL, 0, 0, NOWHERE );
- pObject->ubLastTargetTakenDamage = (UINT8)( usStructureID );
- }
- }
- }
- }
- BOOLEAN CheckForCatchObject( REAL_OBJECT *pObject )
- {
- SOLDIERTYPE *pSoldier;
- UINT32 uiSpacesAway;
- // Do we want to catch?
- if ( pObject->fTestObject == NO_TEST_OBJECT )
- {
- if ( pObject->ubActionCode == THROW_TARGET_MERC_CATCH )
- {
- pSoldier = MercPtrs[ pObject->uiActionData ];
- // Is it a guy?
- // Are we close to this guy?
- uiSpacesAway = PythSpacesAway( pObject->sGridNo, pSoldier->sGridNo );
- if ( uiSpacesAway < 4 && !pObject->fAttemptedCatch )
- {
- if ( pSoldier->usAnimState != CATCH_STANDING &&
- pSoldier->usAnimState != CATCH_CROUCHED &&
- pSoldier->usAnimState != LOWER_RIFLE )
- {
- if ( gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND )
- {
- EVENT_InitNewSoldierAnim( pSoldier, CATCH_STANDING, 0 , FALSE );
- }
- else if ( gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_CROUCH )
- {
- EVENT_InitNewSoldierAnim( pSoldier, CATCH_CROUCHED, 0 , FALSE );
- }
- pObject->fCatchAnimOn = TRUE;
- }
- }
- pObject->fAttemptedCatch = TRUE;
- if ( uiSpacesAway <= 1 && !pObject->fCatchCheckDone )
- {
- if ( AttemptToCatchObject( pObject ) )
- {
- return( TRUE );
- }
- }
- }
- }
- return( FALSE );
- }
- BOOLEAN AttemptToCatchObject( REAL_OBJECT *pObject )
- {
- SOLDIERTYPE *pSoldier;
- UINT8 ubChanceToCatch;
- // Get intended target
- pSoldier = MercPtrs[ pObject->uiActionData ];
- // OK, get chance to catch
- // base it on...? CC? Dexterity?
- ubChanceToCatch = 50 + EffectiveDexterity( pSoldier ) / 2;
- #ifdef JA2TESTVERSION
- ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Chance To Catch: %d", ubChanceToCatch );
- #endif
- pObject->fCatchCheckDone = TRUE;
- if ( PreRandom( 100 ) > ubChanceToCatch )
- {
- return( FALSE );
- }
- pObject->fCatchGood = TRUE;
- return( TRUE );
- }
- BOOLEAN DoCatchObject( REAL_OBJECT *pObject )
- {
- SOLDIERTYPE *pSoldier;
- BOOLEAN fGoodCatch = FALSE;
- UINT16 usItem;
- // Get intended target
- pSoldier = MercPtrs[ pObject->uiActionData ];
- // Catch anim.....
- switch( gAnimControl[ pSoldier->usAnimState ].ubHeight )
- {
- case ANIM_STAND:
- pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
- EVENT_InitNewSoldierAnim( pSoldier, END_CATCH, 0 , FALSE );
- break;
- case ANIM_CROUCH:
- pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
- EVENT_InitNewSoldierAnim( pSoldier, END_CROUCH_CATCH, 0 , FALSE );
- break;
- }
- PlayJA2Sample( CATCH_OBJECT, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
- pObject->fCatchAnimOn = FALSE;
- if ( !pObject->fCatchGood )
- {
- return( FALSE );
- }
- // Get item
- usItem = pObject->Obj.usItem;
- // Transfer object
- fGoodCatch = AutoPlaceObject( pSoldier, &(pObject->Obj), TRUE );
- // Report success....
- if ( fGoodCatch )
- {
- pObject->fDropItem = FALSE;
- ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, pMessageStrings[ MSG_MERC_CAUGHT_ITEM ], pSoldier->name, ShortItemNames[ usItem ] );
- }
- return( TRUE );
- }
- //#define TESTDUDEXPLOSIVES
- void HandleArmedObjectImpact( REAL_OBJECT *pObject )
- {
- INT16 sZ;
- BOOLEAN fDoImpact = FALSE;
- BOOLEAN fCheckForDuds = FALSE;
- OBJECTTYPE *pObj;
- INT32 iTrapped = 0;
- UINT16 usFlags = 0;
- INT8 bLevel = 0;
- // Calculate pixel position of z
- sZ = (INT16)CONVERT_HEIGHTUNITS_TO_PIXELS( (INT16)( pObject->Position.z ) ) - gpWorldLevelData[ pObject->sGridNo ].sHeight;
- // get OBJECTTYPE
- pObj = &(pObject->Obj);
- // ATE: Make sure number of objects is 1...
- pObj->ubNumberOfObjects = 1;
- if ( Item[ pObj->usItem ].usItemClass & IC_GRENADE )
- {
- fCheckForDuds = TRUE;
- }
- if ( pObj->usItem == MORTAR_SHELL )
- {
- fCheckForDuds = TRUE;
- }
- if ( Item[ pObj->usItem ].usItemClass & IC_THROWN )
- {
- AddItemToPool( pObject->sGridNo, pObj, INVISIBLE, bLevel, usFlags, 0 );
- }
- if ( fCheckForDuds )
- {
- // If we landed on anything other than the floor, always! go off...
- #ifdef TESTDUDEXPLOSIVES
- if ( sZ != 0 || pObject->fInWater )
- #else
- if ( sZ != 0 || pObject->fInWater || ( pObj->bStatus[0] >= USABLE && ( PreRandom( 100 ) < (UINT32) pObj->bStatus[0] + PreRandom( 50 ) ) ) )
- #endif
- {
- fDoImpact = TRUE;
- }
- else // didn't go off!
- {
- #ifdef TESTDUDEXPLOSIVES
- if ( 1 )
- #else
- if ( pObj->bStatus[0] >= USABLE && PreRandom(100) < (UINT32) pObj->bStatus[0] + PreRandom( 50 ) )
- #endif
- {
- iTrapped = PreRandom( 4 ) + 2;
- }
- if ( iTrapped )
- {
- // Start timed bomb...
- usFlags |= WORLD_ITEM_ARMED_BOMB;
- pObj->bDetonatorType = BOMB_TIMED;
- pObj->bDelay = (INT8)( 1 + PreRandom( 2 ) );
- }
- // ATE: If we have collided with roof last...
- if ( pObject->iOldCollisionCode == COLLISION_ROOF )
- {
- bLevel = 1;
- }
- // Add item to pool....
- AddItemToPool( pObject->sGridNo, pObj, INVISIBLE, bLevel, usFlags, 0 );
- // All teams lok for this...
- NotifySoldiersToLookforItems( );
- if ( pObject->ubOwner != NOBODY )
- {
- DoMercBattleSound( MercPtrs[ pObject->ubOwner ], (INT8)( BATTLE_SOUND_CURSE1 ) );
- }
- }
- }
- else
- {
- fDoImpact = TRUE;
- }
- if ( fDoImpact )
- {
- if ( pObject->Obj.usItem == BREAK_LIGHT )
- {
- // Add a light effect...
- NewLightEffect( pObject->sGridNo, LIGHT_FLARE_MARK_1 );
- }
- else if ( Item[ pObject->Obj.usItem ].usItemClass & IC_GRENADE )
- {
- /* ARM: Removed. Rewards even missed throws, and pulling a pin doesn't really teach anything about explosives
- if ( MercPtrs[ pObject->ubOwner ]->bTeam == gbPlayerNum && gTacticalStatus.uiFlags & INCOMBAT )
- {
- // tossed grenade, not a dud, so grant xp
- // EXPLOSIVES GAIN (10): Tossing grenade
- if ( pObject->ubOwner != NOBODY )
- {
- StatChange( MercPtrs[ pObject->ubOwner ], EXPLODEAMT, 10, FALSE );
- }
- }
- */
- IgniteExplosion( pObject->ubOwner, (INT16)pObject->Position.x, (INT16)pObject->Position.y, sZ, pObject->sGridNo, pObject->Obj.usItem, GET_OBJECT_LEVEL( pObject->Position.z - CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ pObject->sGridNo ].sHeight ) ) );
- }
- else if ( pObject->Obj.usItem == MORTAR_SHELL )
- {
- sZ = (INT16)CONVERT_HEIGHTUNITS_TO_PIXELS( (INT16)pObject->Position.z );
- IgniteExplosion( pObject->ubOwner, (INT16)pObject->Position.x, (INT16)pObject->Position.y, sZ, pObject->sGridNo, pObject->Obj.usItem, GET_OBJECT_LEVEL( pObject->Position.z - CONVERT_PIXELS_TO_HEIGHTUNITS( gpWorldLevelData[ pObject->sGridNo ].sHeight ) ) );
- }
- }
- }
- BOOLEAN SavePhysicsTableToSaveGameFile( HWFILE hFile )
- {
- UINT32 uiNumBytesWritten=0;
- UINT16 usCnt=0;
- UINT32 usPhysicsCount=0;
- for( usCnt=0; usCnt<NUM_OBJECT_SLOTS; usCnt++ )
- {
- //if the REAL_OBJECT is active, save it
- if( ObjectSlots[ usCnt ].fAllocated )
- {
- usPhysicsCount++;
- }
- }
- //Save the number of REAL_OBJECTs in the array
- FileWrite( hFile, &usPhysicsCount, sizeof( UINT32 ), &uiNumBytesWritten );
- if( uiNumBytesWritten != sizeof( UINT32 ) )
- {
- return( FALSE );
- }
- if( usPhysicsCount != 0 )
- {
- for( usCnt=0; usCnt<NUM_OBJECT_SLOTS; usCnt++ )
- {
- //if the REAL_OBJECT is active, save it
- if( ObjectSlots[ usCnt ].fAllocated )
- {
- //Save the the REAL_OBJECT structure
- FileWrite( hFile, &ObjectSlots[usCnt], sizeof( REAL_OBJECT ), &uiNumBytesWritten );
- if( uiNumBytesWritten != sizeof( REAL_OBJECT ) )
- {
- return( FALSE );
- }
- }
- }
- }
- return( TRUE );
- }
- BOOLEAN LoadPhysicsTableFromSavedGameFile( HWFILE hFile )
- {
- UINT32 uiNumBytesRead=0;
- UINT16 usCnt=0;
- //make sure the objects are not allocated
- memset( ObjectSlots, 0, NUM_OBJECT_SLOTS * sizeof( REAL_OBJECT ) );
- //Load the number of REAL_OBJECTs in the array
- FileRead( hFile, &guiNumObjectSlots, sizeof( UINT32 ), &uiNumBytesRead );
- if( uiNumBytesRead != sizeof( UINT32 ) )
- {
- return( FALSE );
- }
- //loop through and add the objects
- for( usCnt=0; usCnt<guiNumObjectSlots; usCnt++ )
- {
- //Load the the REAL_OBJECT structure
- FileRead( hFile, &ObjectSlots[usCnt], sizeof( REAL_OBJECT ), &uiNumBytesRead );
- if( uiNumBytesRead != sizeof( REAL_OBJECT ) )
- {
- return( FALSE );
- }
- ObjectSlots[usCnt].pNode = NULL;
- ObjectSlots[usCnt].pShadow = NULL;
- ObjectSlots[usCnt].iID = usCnt;
- }
- return( TRUE );
- }
- UINT16 RandomGridFromRadius( INT16 sSweetGridNo, INT8 ubMinRadius, INT8 ubMaxRadius )
- {
- INT16 sX, sY;
- INT16 sGridNo;
- INT32 leftmost;
- BOOLEAN fFound = FALSE;
- UINT32 cnt = 0;
- if ( ubMaxRadius == 0 || ubMinRadius == 0 )
- {
- return( sSweetGridNo );
- }
- do
- {
- sX = (UINT16)PreRandom( ubMaxRadius );
- sY = (UINT16)PreRandom( ubMaxRadius );
- if ( ( sX < ubMinRadius || sY < ubMinRadius ) && ubMaxRadius != ubMinRadius )
- {
- continue;
- }
- if ( PreRandom( 2 ) == 0 )
- {
- sX = sX * -1;
- }
- if ( PreRandom( 2 ) == 0 )
- {
- sY = sY * -1;
- }
- leftmost = ( ( sSweetGridNo + ( WORLD_COLS * sY ) )/ WORLD_COLS ) * WORLD_COLS;
- sGridNo = sSweetGridNo + ( WORLD_COLS * sY ) + sX;
- if ( sGridNo == sSweetGridNo )
- {
- continue;
- }
- if ( sGridNo >=0 && sGridNo < WORLD_MAX &&
- sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) )
- {
- fFound = TRUE;
- }
- cnt++;
- if ( cnt > 50 )
- {
- return( NOWHERE );
- }
- } while( !fFound );
- return( sGridNo );
- }
|