diff options
| author | Felix Morgner <felix.morgner@gmail.com> | 2026-08-24 11:16:07 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@gmail.com> | 2026-08-24 11:16:07 +0200 |
| commit | c068f22329d5cc722622a2183bbb22eef2093df7 (patch) | |
| tree | 12d56c1aede67988a55e241364606bfbb4dba933 /src/parsec_server/e_colldet_sv.cpp | |
| download | openparsec-main.tar.xz openparsec-main.zip | |
Diffstat (limited to 'src/parsec_server/e_colldet_sv.cpp')
| -rwxr-xr-x | src/parsec_server/e_colldet_sv.cpp | 1251 |
1 files changed, 1251 insertions, 0 deletions
diff --git a/src/parsec_server/e_colldet_sv.cpp b/src/parsec_server/e_colldet_sv.cpp new file mode 100755 index 0000000..cd58817 --- /dev/null +++ b/src/parsec_server/e_colldet_sv.cpp @@ -0,0 +1,1251 @@ +/* + * PARSEC - Game collision detection - SERVER + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:44 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +// C library +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// subsystem & headers +#include "net_defs.h" +#include "e_defs.h" + +// mathematics header +#include "utl_math.h" + +// local module header +#include "e_colldet.h" + +// proprietary module headers +#include "g_extra.h" +#include "net_game_sv.h" +#include "g_main_sv.h" +#include "e_simplayerinfo.h" +#include "e_connmanager.h" +#include "e_simnetoutput.h" +#include "e_simulator.h" +#include "parttype.h" + +#define SET_SHIELD_TESTING() { test_shield = TRUE; } +//#define SET_SHIELD_TESTING() { test_shield = FALSE; } + +#define CONSERVATIVE_POINTSAMPLE_EXPANSION 16 + +// collision detection helper function ---------------------------------------- +// +int G_CollDet::_CheckShipProjectileDisjoint() +{ + // extend sphere for point sampling + geomv_t hugesphere = bd_sphere * CONSERVATIVE_POINTSAMPLE_EXPANSION; + + // vector to center + Vector3 vectest; + vectest.X = obj_pos.X - test_pos.X; + vectest.Y = obj_pos.Y - test_pos.Y; + vectest.Z = obj_pos.Z - test_pos.Z; + + // check enlarged vicinity for early-out + if ( vectest.X >= hugesphere ) + return TRUE; + if ( vectest.Y >= hugesphere ) + return TRUE; + if ( vectest.Z >= hugesphere ) + return TRUE; + + hugesphere = -hugesphere; + if ( vectest.X <= hugesphere ) + return TRUE; + if ( vectest.Y <= hugesphere ) + return TRUE; + if ( vectest.Z <= hugesphere ) + return TRUE; + + // test shield (bounding sphere) + if ( test_shield ) { + + // squared distance to current position + geomv_t vectestl2 = DOT_PRODUCT( &vectest, &vectest ); + + // trivial intersect + if ( vectestl2 < bd_sphere2 ) + return FALSE; + + // squared distance to previous position + Vector3 vecprev; + vecprev.X = obj_pos.X - prev_pos.X; + vecprev.Y = obj_pos.Y - prev_pos.Y; + vecprev.Z = obj_pos.Z - prev_pos.Z; + + geomv_t vecprevl2 = DOT_PRODUCT( &vecprev, &vecprev ); + + // trivial intersect + if ( vecprevl2 < bd_sphere2 ) + return FALSE; + + // calc nearest point on line to center + Vector3 vecline; + vecline.X = test_pos.X - prev_pos.X; + vecline.Y = test_pos.Y - prev_pos.Y; + vecline.Z = test_pos.Z - prev_pos.Z; + + geomv_t scaledt = DOT_PRODUCT( &vecline, &vecprev ); + + // ray pointing away from sphere? + if ( GEOMV_NEGATIVE( scaledt ) ) + return TRUE; + + // ray not yet at the sphere? + geomv_t veclinel2 = DOT_PRODUCT( &vecline, &vecline ); + if ( scaledt >= veclinel2 ) + return TRUE; + ASSERT( veclinel2 > GEOMV_VANISHING ); + + // calc squared distance of nearest point + geomv_t dot2 = GEOMV_MUL( scaledt, scaledt ); + geomv_t tlen2 = GEOMV_DIV( dot2, veclinel2 ); + + //NOTE: + // the actual point of impact could now be + // easily calculated without a sqrt(). + + // collision if nearest point inside + return ( ( vecprevl2 - tlen2 ) >= bd_sphere2 ); + + } else { + + ASSERT( FALSE ); +/* + // shield is down: test actual object + +#ifdef CHECK_BOX_BEFORE_BSP_COLLISION + + // clip lineseg into box + + geomv_t isect; + geomv_t seglen; + geomv_t vtx0t = GEOMV_0; + geomv_t vtx1t = GEOMV_1; + + int collside = -1; + + static geomv_t vanish = FLOAT_TO_GEOMV( 0.00001 ); + + // cull/clip against +Z + geomv_t curax = obj_pos.Z + bd_sphere; + geomv_t dist0 = prev_pos.Z - curax; + geomv_t dist1 = test_pos.Z - curax; + dword outcode = ( ( DW32( dist0 ) & 0x80000000 ) >> 1 ) | + ( DW32( dist1 ) & 0x80000000 ); + // both outside? + if ( outcode == 0x00000000 ) + return TRUE; + // at least one outside? + if ( outcode != 0xc0000000 ) { + if ( outcode == 0x40000000 ) { + // v0 inside, v1 outside + ABS_GEOMV( dist0 ); + seglen = dist0 + dist1; + if ( seglen <= vanish ) + return TRUE; + vtx1t = GEOMV_DIV( dist0, seglen ); + } else { + // v0 outside, v1 inside + ABS_GEOMV( dist1 ); + seglen = dist0 + dist1; + if ( seglen <= vanish ) + return TRUE; + vtx0t = GEOMV_DIV( dist0, seglen ); + collside = 0; + } + } + +#define AXIAL_CLIP_T(s) { \ +\ + outcode = ( ( DW32( dist0 ) & 0x80000000 ) >> 1 ) | \ + ( DW32( dist1 ) & 0x80000000 ); \ + if ( outcode == 0x00000000 ) \ + return TRUE; \ + if ( outcode != 0xc0000000 ) { \ + if ( outcode == 0x40000000 ) { \ + ABS_GEOMV( dist0 ); \ + seglen = dist0 + dist1; \ + if ( seglen <= vanish ) \ + return TRUE; \ + isect = GEOMV_DIV( dist0, seglen ); \ + if ( isect <= vtx0t ) \ + return TRUE; \ + if ( isect < vtx1t ) \ + vtx1t = isect; \ + } else { \ + ABS_GEOMV( dist1 ); \ + seglen = dist0 + dist1; \ + if ( seglen <= vanish ) \ + return TRUE; \ + isect = GEOMV_DIV( dist0, seglen ); \ + if ( isect >= vtx1t ) \ + return TRUE; \ + if ( isect > vtx0t ) { \ + vtx0t = isect; \ + collside = (s); \ + } \ + } \ + } \ +} + // cull/clip against -Z + curax = obj_pos.Z - bd_sphere; + dist0 = curax - prev_pos.Z; + dist1 = curax - test_pos.Z; + AXIAL_CLIP_T( 1 ); + + // cull/clip against +X + curax = obj_pos.X + bd_sphere; + dist0 = prev_pos.X - curax; + dist1 = test_pos.X - curax; + AXIAL_CLIP_T( 2 ); + + // cull/clip against -X + curax = obj_pos.X - bd_sphere; + dist0 = curax - prev_pos.X; + dist1 = curax - test_pos.X; + AXIAL_CLIP_T( 3 ); + + // cull/clip against +Y + curax = obj_pos.Y + bd_sphere; + dist0 = prev_pos.Y - curax; + dist1 = test_pos.Y - curax; + AXIAL_CLIP_T( 4 ); + + // cull/clip against -Y + curax = obj_pos.Y - bd_sphere; + dist0 = curax - prev_pos.Y; + dist1 = curax - test_pos.Y; + AXIAL_CLIP_T( 5 ); + + //NOTE: + // collside now contains the code of the collider side + // (-1 means the lineseg starts in the bounding box) + + //TODO: + // use already clipped segment in BSP test? + +#endif // CHECK_BOX_BEFORE_BSP_COLLISION + + // test actual object (polygon accurate) + + // transform line vertices into object-space + CalcOrthoInverse( cur_ship->ObjPosition, DestXmatrx ); + Vertex3 v0; + MtxVctMUL( DestXmatrx, &prev_pos, &v0 ); + Vertex3 v1; + MtxVctMUL( DestXmatrx, &test_pos, &v1 ); + + // assume collision if no bsp tree + CullBSPNode *node = cur_ship->AuxBSPTree; + if ( node == NULL ) + return FALSE; + + dword nodeid = 0; + geomv_t impact_t; + int collided = BSP_FindColliderLine( node, &v0, &v1, &nodeid, &impact_t ); + + // no collision? + if ( !collided ) + return TRUE; + + // react only if not started in solid leaf + if ( nodeid > 0 ) { + + // fetch collider + node = &cur_ship->AuxBSPTree[ nodeid ]; + + // calc collision position (object-space) + v1.X -= v0.X; + v1.Y -= v0.Y; + v1.Z -= v0.Z; + v1.X = v0.X + GEOMV_MUL( v1.X, impact_t ); + v1.Y = v0.Y + GEOMV_MUL( v1.Y, impact_t ); + v1.Z = v0.Z + GEOMV_MUL( v1.Z, impact_t ); + + // create hull impact particles + SFX_HullImpact( cur_ship, &v1, &node->plane ); + } + + // disable shield + test_shield = FALSE; +*/ + } + + return FALSE; +} + +//Check if any ship is hit by missile +void G_CollDet::_CheckShipMissileCollision() +{ + // check whether any other ship object is hit by missile + for ( cur_ship = TheWorld->FetchFirstShip(); cur_ship != NULL; ) { + + ShipObject *nextship = (ShipObject*) cur_ship->NextObj; + + obj_pos.X = cur_ship->ObjPosition[ 0 ][ 3 ]; + obj_pos.Y = cur_ship->ObjPosition[ 1 ][ 3 ]; + obj_pos.Z = cur_ship->ObjPosition[ 2 ][ 3 ]; + bd_sphere = cur_ship->BoundingSphere; + bd_sphere2 = cur_ship->BoundingSphere2; + + + int cur_ship_owner = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // walk all missiles + MissileObject *walkmissiles = TheWorld->FetchFirstMissile(); + while ( walkmissiles != NULL ) { + + MissileObject *curmissile = walkmissiles; + walkmissiles = (MissileObject *) walkmissiles->NextObj; + + // owner can't be hit by own missile + if ( curmissile->Owner == cur_ship_owner ) { + continue; + } + + test_pos.X = curmissile->ObjPosition[ 0 ][ 3 ]; + test_pos.Y = curmissile->ObjPosition[ 1 ][ 3 ]; + test_pos.Z = curmissile->ObjPosition[ 2 ][ 3 ]; + + prev_pos.X = curmissile->PrevPosition.X; + prev_pos.Y = curmissile->PrevPosition.Y; + prev_pos.Z = curmissile->PrevPosition.Z; + + // shield or actual object? + SET_SHIELD_TESTING(); + + // actual collision detection + if ( _CheckShipProjectileDisjoint() ) { + continue; + } + + // collision response + _CollisionResponse_MissileShip( curmissile ); + } + + cur_ship = nextship; + } + +} + +// check whether any ship is hit by laser beam -------------------------------- +// +void G_CollDet::_CheckShipLaserCollision() +{ +#ifdef PARSEC_CLIENT + // check whether local player is hit by laser + if ( NetConnected ) { + + cur_ship = MyShip; + obj_pos.X = MyShip->ObjPosition[ 0 ][ 3 ]; + obj_pos.Y = MyShip->ObjPosition[ 1 ][ 3 ]; + obj_pos.Z = MyShip->ObjPosition[ 2 ][ 3 ]; + bd_sphere = MyShip->BoundingSphere; + bd_sphere2 = MyShip->BoundingSphere2; + + // walk all laser projectiles + LaserObject *walklasers = TheWorld->FetchFirstLaser(); + while ( walklasers != NULL ) { + + LaserObject *curlaser = walklasers; + walklasers = (LaserObject *) walklasers->NextObj; + + // can't be hit by own laser projectile + if ( curlaser->Owner == OWNER_LOCAL_PLAYER ) { + continue; + } + + test_pos.X = curlaser->ObjPosition[ 0 ][ 3 ]; + test_pos.Y = curlaser->ObjPosition[ 1 ][ 3 ]; + test_pos.Z = curlaser->ObjPosition[ 2 ][ 3 ]; + + prev_pos.X = curlaser->PrevPosition.X; + prev_pos.Y = curlaser->PrevPosition.Y; + prev_pos.Z = curlaser->PrevPosition.Z; + + // shield or actual object? + SET_SHIELD_TESTING(); + + // actual collision detection + if ( CheckShipProjectileDisjoint() ) { + continue; + } + + // collision response + CollisionLaserMyShip( curlaser ); + } + } +#endif // PARSEC_CLIENT + + // check whether any (other) shipobject is hit by laser + for ( cur_ship = TheWorld->FetchFirstShip(); cur_ship != NULL; ) { + + // get pointer to next ship in list, as the current might get removed upon collision + ShipObject* nextship = (ShipObject *) cur_ship->NextObj; + + obj_pos.X = cur_ship->ObjPosition[ 0 ][ 3 ]; + obj_pos.Y = cur_ship->ObjPosition[ 1 ][ 3 ]; + obj_pos.Z = cur_ship->ObjPosition[ 2 ][ 3 ]; + bd_sphere = cur_ship->BoundingSphere; + bd_sphere2 = cur_ship->BoundingSphere2; + + int cur_ship_owner = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // walk all laser projectiles + for ( LaserObject *walklasers = TheWorld->FetchFirstLaser(); walklasers != NULL; ) { + + LaserObject* curlaser = walklasers; + walklasers = (LaserObject *) walklasers->NextObj; + + // owner can't be hit by own laser projectile + if ( curlaser->Owner == cur_ship_owner ) { + continue; + } + + test_pos.X = curlaser->ObjPosition[ 0 ][ 3 ]; + test_pos.Y = curlaser->ObjPosition[ 1 ][ 3 ]; + test_pos.Z = curlaser->ObjPosition[ 2 ][ 3 ]; + + prev_pos.X = curlaser->PrevPosition.X; + prev_pos.Y = curlaser->PrevPosition.Y; + prev_pos.Z = curlaser->PrevPosition.Z; + + // shield or actual object? + SET_SHIELD_TESTING(); + + // actual collision detection + if ( _CheckShipProjectileDisjoint() ) { + continue; + } + + // collision response + _CollisionResponse_LaserShip( curlaser ); + } + + cur_ship = nextship; + } +} + +// emp collision detection ---------------------------------------------------- +// +void G_CollDet::_CheckShipEmpCollision() +{ + // walk the list of EMP objects + ASSERT( TheWorld->m_CustmObjects != NULL ); + + CustomObject *precnode = TheWorld->m_CustmObjects; + CustomObject *walkobjs = (CustomObject *)TheWorld->m_CustmObjects->NextObj; + + // walk all lasers + while ( walkobjs != NULL ) { + if ((walkobjs->ObjectType == emp_type_id[ 0 ]) || + (walkobjs->ObjectType == emp_type_id[ 1 ]) || + (walkobjs->ObjectType == emp_type_id[ 2 ])) { + + Emp *tmpemp = (Emp *)walkobjs; + // got an emp object, now let's walk the ship objects + // and check for collisions. + + for ( cur_ship = TheWorld->FetchFirstShip(); cur_ship != NULL; ) { + // current ship's owner + int cur_ship_owner = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // get pointer to next ship in list, as the current might get removed upon collision + ShipObject* nextship = (ShipObject *) cur_ship->NextObj; + + // cant collide with the owner + if(tmpemp->Owner != cur_ship_owner){ + // do the actual check + if(EmpShipCollision(tmpemp, cur_ship)){ + // HIT! Record damage and send out packets. + _CollisionResponse_EmpShip(tmpemp); + } + } + cur_ship = nextship; + } + } + + precnode = walkobjs; + walkobjs = (CustomObject *)walkobjs->NextObj; + } + + +} +// ship collided with EMP --------------------------------------------------- +// +void G_CollDet::_CollisionResponse_EmpShip( Emp *curemp ) +{ + ASSERT( curemp != NULL ); + ASSERT( cur_ship != NULL ); + + int hitpoints = curemp->damage; + + if ( ( hitpoints -= cur_ship->MegaShieldAbsorption ) < 0 ) { + hitpoints = 0; + } + + // apply damage + if ( cur_ship->CurDamage <= cur_ship->MaxDamage ) { + MSGOUT("Hitpoints deduction is %d", hitpoints); + MSGOUT("Damage Calc Ref Frames is %d",TheSimulator->GetThisFrameRefFrames()*2); + cur_ship->CurDamageFrac += (TheSimulator->GetThisFrameRefFrames()*2) * hitpoints; + MSGOUT("Damage Fractional is %d",cur_ship->CurDamageFrac); + cur_ship->CurDamage += cur_ship->CurDamageFrac >> 16; + MSGOUT("Doing %d damage to ship", cur_ship->CurDamageFrac >> 16); + cur_ship->CurDamageFrac &= 0xffff; + + + // new less confusing shit... + //cur_ship->CurDamage += hitpoints; + } + + // check whether the ship should explode + if ( cur_ship->CurDamage > cur_ship->MaxDamage ) { + int nClientID_Attacker = curemp->Owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( cur_ship ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + + //FIXME: + //cur_ship->pDist->UpdateMODE_REFRESH(); + + //FIXME: this should be called GAME_LOGOUT or so to indicate that these messages + // are the most important ones for processing user stats + MSGOUT( "%s was shot down by %s's EMP", TheConnManager->GetClientName( nClientID_Downed ), TheConnManager->GetClientName( nClientID_Attacker ) ); + + } +} + + +// ship collided with Missile --------------------------------------------------- +// +void G_CollDet::_CollisionResponse_MissileShip( MissileObject *curmissile ) +{ + ASSERT( curmissile != NULL ); + ASSERT( cur_ship != NULL ); + + int hitpoints = curmissile->HitPoints; + if ( ( hitpoints -= cur_ship->MegaShieldAbsorption ) < 0 ) { + hitpoints = 0; + } + + // apply damage + if ( cur_ship->CurDamage <= cur_ship->MaxDamage ) { + cur_ship->CurDamage += hitpoints; + } + + // check whehter the ship should explode + if ( cur_ship->CurDamage > cur_ship->MaxDamage ) { + + + int nClientID_Attacker = curmissile->Owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( cur_ship ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + + //FIXME: + //cur_ship->pDist->UpdateMODE_REFRESH(); + + //FIXME: this should be called GAME_LOGOUT or so to indicate that these messages + // are the most important ones for processing user stats + MSGOUT( "%s was shot down by %s's Missile", TheConnManager->GetClientName( nClientID_Downed ), TheConnManager->GetClientName( nClientID_Attacker ) ); + } + + // release the E_Distributable ( distribute removal ) + //MSGOUT( "G_CollDet::_CollisionResponse_MissileShip() calls ReleaseDistributable()" ); + TheSimNetOutput->ReleaseDistributable( curmissile->pDist ); + + TheWorld->KillSpecificObject( curmissile->ObjectNumber, TheWorld->m_MisslObjects ); + +} + +// ship collided with laser --------------------------------------------------- +// +void G_CollDet::_CollisionResponse_LaserShip( LaserObject *curlaser ) +{ + ASSERT( curlaser != NULL ); + ASSERT( cur_ship != NULL ); + + int hitpoints = curlaser->HitPoints; + if ( ( hitpoints -= cur_ship->MegaShieldAbsorption ) < 0 ) { + hitpoints = 0; + } + + // apply damage + if ( cur_ship->CurDamage <= cur_ship->MaxDamage ) { + cur_ship->CurDamage += hitpoints; + } + + // check whehter the ship should explode + if ( cur_ship->CurDamage > cur_ship->MaxDamage ) { + +#ifdef PARSEC_CLIENT + KillDurationWeapons( MyShip ); + OBJ_CreateShipExtras( MyShip ); + NET_SetPlayerKillStat( curlaser->Owner, 1 ); + strcpy( paste_str, killed_by_str ); + strcat( paste_str, NET_FetchPlayerName( curlaser->Owner ) ); + ShowMessage( paste_str ); +#else // !PARSEC_CLIENT + + int nClientID_Attacker = curlaser->Owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( cur_ship ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + + //FIXME: + //cur_ship->pDist->UpdateMODE_REFRESH(); + + //FIXME: this should be called GAME_LOGOUT or so to indicate that these messages + // are the most important ones for processing user stats + MSGOUT( "%s was shot down by %s's laser", TheConnManager->GetClientName( nClientID_Downed ), + TheConnManager->GetClientName( nClientID_Attacker ) ); + +#endif // !PARSEC_CLIENT + } + + +#ifdef PARSEC_CLIENT + + //if ( cur_ship->CurDamage <= cur_ship->MaxDamage ) { + // OBJ_EventShipImpact( cur_ship, test_shield ); + //} + + NET_RmEvKillObject( curlaser->HostObjNumber, LASER_LIST ); + +#else // !PARSEC_CLIENT + + // release the E_Distributable ( distribute removal ) + //MSGOUT( "G_CollDet::_CollisionResponse_LaserShip() calls ReleaseDistributable()" ); + TheSimNetOutput->ReleaseDistributable( curlaser->pDist ); +#endif // !PARSEC_CLIENT + + TheWorld->KillSpecificObject( curlaser->ObjectNumber, TheWorld->m_LaserObjects ); + +/* if ( cur_ship->ExplosionCount == 0 ) { + +#ifdef PARSEC_CLIENT + + //FIXME: pure client gamecode + OBJ_EventShipImpact( cur_ship, test_shield ); + +#endif // PARSEC_CLIENT + + if ( !NetConnected ) { + cur_ship->CurDamage += hitpoints; + if ( cur_ship->CurDamage > cur_ship->MaxDamage ) { + LetShipExplode( cur_ship ); + ShowMessage( laser_downed_ship_str ); + AUD_ShipDestroyed( cur_ship ); + AUD_JimCommenting( JIM_COMMENT_EATTHIS ); + } + } + } +*/ +} +// local ship collided with proximity mine ------------------------------------ +// +void G_CollDet::_CollisionResponse_MineShip( Mine1Obj *curmine ) +{ + ASSERT( curmine != NULL ); + + int cur_ship_owner = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + + int hitpoints = curmine->HitPoints; + if ( ( hitpoints -= cur_ship->MegaShieldAbsorption ) < 0 ) { + hitpoints = 0; + } + + if ( cur_ship->CurDamage <= cur_ship->MaxDamage ) { + + cur_ship->CurDamage += hitpoints; + if ( cur_ship->CurDamage > cur_ship->MaxDamage ) { + int nClientID_Attacker = curmine->Owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( cur_ship ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + if(cur_ship_owner != curmine->Owner) { + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + } + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + + //FIXME: + //cur_ship->pDist->UpdateMODE_REFRESH(); + + //FIXME: this should be called GAME_LOGOUT or so to indicate that these messages + // are the most important ones for processing user stats + MSGOUT( "%s was destroyed by %s's mine", TheConnManager->GetClientName( nClientID_Downed ), + TheConnManager->GetClientName( nClientID_Attacker ) ); + + } + } + // release the E_Distributable ( distribute removal ) + // MSGOUT( "G_CollDet::_CollisionResponse_MineShip() calls ReleaseDistributable()" ); + curmine->pDist->WillBeSentToOwner(); + TheSimNetOutput->ReleaseDistributable( curmine->pDist ); + TheWorld->KillSpecificObject( curmine->ObjectNumber, TheWorld->m_ExtraObjects ); + + return; +} + +void G_CollDet::OBJ_ShipHelixDamage( ShipObject *shippo, int owner ) +{ + ASSERT( shippo != NULL ); + + //NOTE: + // this function is used by + // PART_ANI::LinearParticleCollision(). + +#define HELIX_HITPOINTS_PER_PARTICLE 1 + + // no damage at all if megashield active + if ( shippo->MegaShieldAbsorption > 0 ) + return; + + if (shippo->ExplosionCount == 0 ) { + + shippo->CurDamage += HELIX_HITPOINTS_PER_PARTICLE; + if ( shippo->CurDamage > shippo->MaxDamage ) { + // needed for explosions caused by particles + shippo->DelayExplosion = 1; + + int nClientID_Attacker = owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( shippo->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( shippo ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + MSGOUT( "%s was shot down by %s's Helix Cannon", TheConnManager->GetClientName( nClientID_Downed ),TheConnManager->GetClientName( nClientID_Attacker ) ); + } + } +} + +// inflict damage upon ship hit by swarm missiles ----------------------------- +// +void G_CollDet::OBJ_ShipSwarmDamage( ShipObject *shippo, int owner ) +{ + ASSERT( shippo != NULL ); + + //NOTE: + // this function is used by + // S_SWARM::SWARM_Animate(). + +#define SWARM_HITPOINTS_PER_PARTICLE 1 + + // no damage at all if megashield active + if ( shippo->MegaShieldAbsorption > 0 ) + return; + + if ( shippo->ExplosionCount == 0 ) { + + shippo->CurDamage += SWARM_HITPOINTS_PER_PARTICLE; + if ( shippo->CurDamage > shippo->MaxDamage ) { + + // schedule explosion + + // needed for explosions caused by particles + shippo->DelayExplosion = 1; + + int nClientID_Attacker = owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( shippo->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( shippo ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + MSGOUT( "%s was destroyed by %s's Swarm missiles", TheConnManager->GetClientName( nClientID_Downed ), TheConnManager->GetClientName( nClientID_Attacker ) ); + } + } +} + +// inflict damage upon ship hit by lightning beam ----------------------------- +// +void G_CollDet::OBJ_ShipLightningDamage( ShipObject *shippo, int owner ) +{ + ASSERT( shippo != NULL ); + + //NOTE: + // this function is used by + // PART_ANI::CheckLightningParticleShipCollision(). + +#define LIGHTNING_HITPOINTS_PER_REFFRAME 6000 + + // no damage at all if megashield active + if ( shippo->MegaShieldAbsorption > 0 ) + return; + + if ( shippo->ExplosionCount == 0 ) { + + shippo->CurDamageFrac += TheSimulator->GetThisFrameRefFrames() * LIGHTNING_HITPOINTS_PER_REFFRAME; + shippo->CurDamage += shippo->CurDamageFrac >> 16; + shippo->CurDamageFrac &= 0xffff; + + if ( shippo->CurDamage > shippo->MaxDamage ) { + // needed for explosions caused by particles + shippo->DelayExplosion = 1; + + int nClientID_Attacker = owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( shippo->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( shippo ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + MSGOUT( "%s was cooked %s's Lightning Cannon", TheConnManager->GetClientName( nClientID_Downed ),TheConnManager->GetClientName( nClientID_Attacker ) ); + } + } +} + +// check if ship collected extra ---------------------------------------------- +// +void G_CollDet::_CheckShipExtraCollision() +{ + // check whether any (other) shipobject is hit by laser + for ( cur_ship = TheWorld->FetchFirstShip(); cur_ship != NULL; ) { + + // fetch location of local ship + geomv_t objX = cur_ship->ObjPosition[ 0 ][ 3 ]; + geomv_t objY = cur_ship->ObjPosition[ 1 ][ 3 ]; + geomv_t objZ = cur_ship->ObjPosition[ 2 ][ 3 ]; + + //MSGOUT( "G_CollDet::_CheckShipExtraCollision(): ship %d is at %.2f/%.2f/%.2f", cur_ship->HostObjNumber, objX, objY, objZ ); + + // fetch radius of bounding sphere of local ship + geomv_t bsphere = cur_ship->BoundingSphere; + + ASSERT( TheWorld->m_ExtraObjects != NULL ); + + // walk list of extras + ExtraObject *precnode = TheWorld->m_ExtraObjects; + while ( precnode->NextObj != NULL ) { + + ASSERT( OBJECT_TYPE_EXTRA( precnode->NextObj ) ); + + // get pointer to current extra + ExtraObject *curextra = (ExtraObject *) precnode->NextObj; + ASSERT( curextra != NULL ); + + // fetch position of extra + geomv_t extraX = curextra->ObjPosition[ 0 ][ 3 ]; + geomv_t extraY = curextra->ObjPosition[ 1 ][ 3 ]; + geomv_t extraZ = curextra->ObjPosition[ 2 ][ 3 ]; + + // do simple collision test (bounding box) + if ( extraX < ( objX - bsphere ) ) { precnode = curextra; continue; } + if ( extraX > ( objX + bsphere ) ) { precnode = curextra; continue; } + if ( extraY < ( objY - bsphere ) ) { precnode = curextra; continue; } + if ( extraY > ( objY + bsphere ) ) { precnode = curextra; continue; } + if ( extraZ < ( objZ - bsphere ) ) { precnode = curextra; continue; } + if ( extraZ > ( objZ + bsphere ) ) { precnode = curextra; continue; } + + // do actual bounding sphere collision test + Vector3 vecdist; + vecdist.X = extraX - objX; + vecdist.Y = extraY - objY; + vecdist.Z = extraZ - objZ; + + geomv_t dist2 = DOT_PRODUCT( &vecdist, &vecdist ); + if ( dist2 > cur_ship->BoundingSphere2 ) { + precnode = curextra; + continue; + } + + // perform action according to extra type and determine message to show + char *text = TheGameExtraManager->CollectExtra( cur_ship, curextra ); + + // show message saying what type of extra has been collected + if (( text != NULL ) || ( curextra->ObjectType == MINE1TYPE )) { + + //FIXME: implement server->client reliable "got ..." message + //fgfg + + } else { + + MSGOUT( "CheckShipExtraCollision(): invalid extra type: %0x.", curextra->ObjectType ); + } + + // remove extra object + if( curextra->ObjectType == MINE1TYPE ) //Mine collision + _CollisionResponse_MineShip((Mine1Obj *) curextra); + else + TheGameExtraManager->OBJ_KillExtra( precnode, TRUE ); + } + + cur_ship = (ShipObject*)cur_ship->NextObj; + } +} + +// check if point is contained in ship's bounding sphere ---------------------- +// +int G_CollDet::PRT_ParticleInBoundingSphere( ShipObject *shippo, Vertex3& point ) +{ + ASSERT( shippo != NULL ); + + geomv_t objX = shippo->ObjPosition[ 0 ][ 3 ]; + geomv_t objY = shippo->ObjPosition[ 1 ][ 3 ]; + geomv_t objZ = shippo->ObjPosition[ 2 ][ 3 ]; + + geomv_t bdsphere = shippo->BoundingSphere; + geomv_t bdsphere2 = shippo->BoundingSphere2; + + if ( point.X < ( objX - bdsphere ) ) return FALSE; + if ( point.X > ( objX + bdsphere ) ) return FALSE; + if ( point.Y < ( objY - bdsphere ) ) return FALSE; + if ( point.Y > ( objY + bdsphere ) ) return FALSE; + if ( point.Z < ( objZ - bdsphere ) ) return FALSE; + if ( point.Z > ( objZ + bdsphere ) ) return FALSE; + + Vector3 dvec; + dvec.X = point.X - objX; + dvec.Y = point.Y - objY; + dvec.Z = point.Z - objZ; + + return ( DOT_PRODUCT( &dvec, &dvec ) < bdsphere2 ); +} + +// brute force collision detection of linear particle with all ships ---------- +// +void G_CollDet::LinearParticleCollision( linear_pcluster_s *cluster, int pid ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( pid >= 0 ) && ( pid < cluster->numel ) ); + ASSERT( cluster->rep[ pid ].flags & PARTICLE_COLLISION ); + + //NOTE: + // this is set as callback by PART_WFX::MaintainHelix() + // to detect collisions of helix particles. + + int owner = cluster->rep[ pid ].owner; + + // check shiplist + ShipObject *walkships = TheWorld->FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject*) walkships->NextObj ) { + + // prevent collision with owner of particle + if ( ( GetObjectOwner( walkships ) == (dword)owner ) ) + continue; + + if ( !PRT_ParticleInBoundingSphere( walkships, cluster->rep[ pid ].position ) ) + continue; + + // disable particle + cluster->rep[ pid ].flags &= ~PARTICLE_ACTIVE; + + switch ( cluster->rep[ pid ].flags & PARTICLE_IS_MASK ) { + + case PARTICLE_IS_HELIX : + // MSGOUT("G_CollDet::LinearParticleCollision(): Helix particle collision with player %d",GetObjectOwner( walkships )); + OBJ_ShipHelixDamage( walkships, owner ); + break; + case PARTICLE_IS_PHOTON : + OBJ_ShipPhotonDamage( walkships, owner ); + break; + default: + break; + } + } +} + +// inflict damage upon ship hit by photon weapon ------------------------------ +// +void G_CollDet::OBJ_ShipPhotonDamage( ShipObject *shippo, int owner ) +{ + ASSERT( shippo != NULL ); + + //NOTE: + // this function is used by + // PART_ANI::LinearParticleCollision(). + +#define PHOTON_HITPOINTS_PER_PARTICLE 3 + + // no damage at all if megashield active + if ( shippo->MegaShieldAbsorption > 0 ) + return; + if (shippo->ExplosionCount == 0 ) { + + shippo->CurDamage += PHOTON_HITPOINTS_PER_PARTICLE; + if ( shippo->CurDamage > shippo->MaxDamage ) { + // needed for explosions caused by particles + shippo->DelayExplosion = 1; + + int nClientID_Attacker = owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( shippo->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( shippo ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + MSGOUT( "%s was mowed down by %s's Photon Cannon", TheConnManager->GetClientName( nClientID_Downed ),TheConnManager->GetClientName( nClientID_Attacker ) ); + } + } +} + + +// check if ship entered energy field ----------------------------------------- +// +void G_CollDet::CheckEnergyField( pcluster_s *cluster ) +{ + if ( ( cluster->type & CT_TYPEMASK ) == CT_PARTICLE_SPHERE ) { + sphereobj_pcluster_s *objcluster = (sphereobj_pcluster_s *) cluster; + if ( objcluster->animtype == SAT_ENERGYFIELD_SPHERE ) { + // check shiplist + ShipObject *walkships = TheWorld->FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject*) walkships->NextObj ) { + if ( PRT_ParticleInBoundingSphere( walkships, objcluster->origin ) ) { + TheGameExtraManager->_CollectBoostEnergyField(walkships, TheSimulator->GetThisFrameRefFrames() * 2 ); + } + } + } + } +} + +int G_CollDet::CheckLightningParticleShipCollision( Vertex3& particlepos, int owner ) +{ + // check shiplist + ShipObject *walkships = TheWorld->FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject*) walkships->NextObj ) { + + // prevent collision with owner of particle + if ( ( GetObjectOwner( walkships ) == (dword)owner ) ) + continue; + + if ( !PRT_ParticleInBoundingSphere( walkships, particlepos ) ) + continue; + + OBJ_ShipLightningDamage( walkships, owner ); + + return TRUE; + } + + return FALSE; +} + +// perform collision detection tests ------------------------------------------ +// +void G_CollDet::OBJ_CheckCollisions() +{ + //NOTE: + // this function gets called once per frame by the + // game loop (G_MAIN.C). + + // check if laser beam hits ship + _CheckShipLaserCollision(); + + // check if missile hits ship + _CheckShipMissileCollision(); + + // check if two ships collide + //CheckShipShipCollision(); + + // check if ship collected extra + _CheckShipExtraCollision(); + + // check collisions for custom objects + //CheckCustomCollisions(); + + // check for emp collisions + _CheckShipEmpCollision(); +} + |
