/*============================================================================= Name : Gun.c Purpose : Handles gun firing Created 6/30/1997 by gshaw Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #include #include #include #include #include "glinc.h" #include "types.h" #include "fastmath.h" #include "debug.h" #include "linkedlist.h" #include "memory.h" #include "spaceobj.h" #include "physics.h" #include "vector.h" #include "matrix.h" #include "universe.h" #include "soundevent.h" #include "univupdate.h" #include "prim3d.h" #include "render.h" #include "tweak.h" #include "aiship.h" #include "gun.h" #include "ships.h" #include "collision.h" #include "tactics.h" #include "madlinkindefs.h" #include "madlinkin.h" #include "randy.h" #include "etg.h" #include "mainrgn.h" #include "mex.h" #include "nis.h" #define ENABLE_SHIPRECOIL 0 #define GUNBURST_STATE_READY 0 #define GUNBURST_STATE_BURST 1 #define GUNBURST_STATE_COOLOFF 2 /*============================================================================= Data: =============================================================================*/ real32 gunRecoilTable[GUN_RecoilTableLength + 1]; /*============================================================================= Functions: =============================================================================*/ /*----------------------------------------------------------------------------- Name : gunCanShoot Description : returns TRUE if gun can shoot Inputs : gun Outputs : Return : returns TRUE if gun can shoot ----------------------------------------------------------------------------*/ bool gunCanShoot(Ship *ship, Gun *gun) { GunStatic *gunstatic = gun->gunstatic; CommandToDo *command = getShipAndItsCommand(&universe.mainCommandLayer,ship); if(ship->staticinfo->maxfuel != 0.0f) { //ship has fuel///lkets check it if(ship->fuel <= 0.0f) { //ship is out of fuel if(ship->shiptype != MinelayerCorvette) { //out of fuel and not minelayer //so shouldn't shoot guns...out of energy return FALSE; } } } if (gunstatic->burstFireTime == 0.0f) // this gun doesn't fire in bursts { if(command != NULL) { if(command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING && !ship->staticinfo->passiveAttackPenaltyExempt) { //ship is passive attacking, so lets limit its gun fire time if(command->ordertype.attributes & COMMAND_IS_PROTECTING && command->ordertype.attributes & COMMAND_IS_FORMATION && command->formation.formationtype == SPHERE_FORMATION) { goto dontdospecialpassivefix; } { real32 firetime = gunstatic->firetime+tacticsInfo.PassiveRetaliationFireTimeModifierLow; real32 compare = (universe.totaltimeelapsed - gun->lasttimefired); if(compare > firetime) { //pass minimum requirement firetime += frandombetween(tacticsInfo.PassiveRetaliationFireTimeModifierLow,tacticsInfo.PassiveRetaliationFireTimeModifierHi); if(compare > firetime) { //gun can now fire return TRUE; } } //it isn't ready to fire return FALSE; } } } dontdospecialpassivefix: return ((universe.totaltimeelapsed - gun->lasttimefired) > gunstatic->firetime); } else // this gun fires in bursts { switch (gun->burstState) { case GUNBURST_STATE_READY: readystate: gun->burstStartTime = universe.totaltimeelapsed; gun->burstState = GUNBURST_STATE_BURST; gun->useBurstFireTime = gunstatic->burstFireTime * tacticsInfo.tacticsBurstFireTimeModifier[ship->tacticstype]; gun->useBurstWaitTime = gunstatic->burstWaitTime * tacticsInfo.tacticsBurstWaitTimeModifier[ship->tacticstype]; if (ship->formationcommand && ship->formationcommand->formation.formationtype == SPHERE_FORMATION && ship->tacticstype != Evasive && ship->formationcommand->ordertype.order == COMMAND_ATTACK) { gun->useBurstFireTime *= tacticsInfo.tacticsBurstFireTimeSphereModifier; gun->useBurstWaitTime *= tacticsInfo.tacticsBurstWaitTimeSphereModifier; } else if (ship->attackvars.myLeaderIs) { gun->useBurstFireTime *= tacticsInfo.tacticsBurstFireTimeWingmanModifier; gun->useBurstWaitTime *= tacticsInfo.tacticsBurstWaitTimeWingmanModifier; } // soundEventBurstFire(ship, gun); break; case GUNBURST_STATE_BURST: if ((universe.totaltimeelapsed - gun->burstStartTime) > gun->useBurstFireTime) { gun->burstState = GUNBURST_STATE_COOLOFF; gun->burstStartTime = universe.totaltimeelapsed; // fall through to GUNBURST_STATE_COOLOFF soundEventBurstStop(ship, gun); } else { break; } case GUNBURST_STATE_COOLOFF: if ((universe.totaltimeelapsed - gun->burstStartTime) > gun->useBurstWaitTime) { gun->burstState = GUNBURST_STATE_READY; goto readystate; } else { return FALSE; } } if(command != NULL) { if(command->ordertype.attributes & COMMAND_IS_PASSIVEATTACKING && !ship->staticinfo->passiveAttackPenaltyExempt) { //ship is passive attacking, so lets limit its gun fire time if(command->ordertype.attributes & COMMAND_IS_PROTECTING && command->ordertype.attributes & COMMAND_IS_FORMATION && command->formation.formationtype == SPHERE_FORMATION) { goto dontdospecialpassivefix2; } { real32 firetime = gunstatic->firetime+tacticsInfo.PassiveRetaliationFireTimeModifierLow; real32 compare = (universe.totaltimeelapsed - gun->lasttimefired); if(compare > firetime) { //pass minimum requirement firetime += frandombetween(tacticsInfo.PassiveRetaliationFireTimeModifierLow,tacticsInfo.PassiveRetaliationFireTimeModifierHi); if(compare > firetime) { //gun can now fire return TRUE; } } //it isn't ready to fire return FALSE; } } } dontdospecialpassivefix2: return ((universe.totaltimeelapsed - gun->lasttimefired) > gunstatic->firetime); } } /*----------------------------------------------------------------------------- Name : gunNewGimbleUpdateCoordSys Description : update a New_Gimble gun's coordinate system to reflect gun->angle, gun->declination Inputs : gun, gunstatic Outputs : Return : ----------------------------------------------------------------------------*/ void gunNewGimbleUpdateCoordSys(Gun *gun,GunStatic *gunstatic) { matrix newmat,rotxmatrix,rotymatrix; matMakeRotAboutY(&rotymatrix,(real32)cos(gun->declination),(real32)sin(gun->declination)); matMakeRotAboutX(&rotxmatrix,(real32)cos(gun->angle),(real32)sin(gun->angle)); matMultiplyMatByMat(&newmat,&gunstatic->defaultgunorientation,&rotxmatrix); matMultiplyMatByMat(&gun->curgunorientation,&newmat,&rotymatrix); if (bitTest(gunstatic->flags, GF_MultiLevelMatrix)) { matMultiplyMatByMat(&newmat,&gunstatic->defaultGunOrientationNonConcat,&rotymatrix); matMultiplyMatByMat(&gun->curGunOrientationNonConcat,&newmat,&rotxmatrix); } matGetVectFromMatrixCol3(gun->gunheading,gun->curgunorientation); } /*----------------------------------------------------------------------------- Name : gunUpdateSlave Description : Update a slave gun's status from it's slave driver Inputs : gun - slave gun guns - list of gunst iSlaveDriver - index of slaveDriver gun Outputs : Return : ----------------------------------------------------------------------------*/ void gunUpdateSlave(Gun *gun, GunInfo *guns, sdword iSlaveDriver) { Gun *slaveDriver; dbgAssert(iSlaveDriver >= 0 && iSlaveDriver < guns->numGuns); slaveDriver = &guns->guns[iSlaveDriver]; gun->gunheading = slaveDriver->gunheading; //copy the essentials from the slave driver gun->curgunorientation = slaveDriver->curgunorientation; gun->declination = slaveDriver->declination; gun->angle = slaveDriver->angle; } /*----------------------------------------------------------------------------- Name : gunOrientGimbleGun Description : if possible, orients the gimble gun to point at target Inputs : gun,target Outputs : Return : TRUE if gun oriented as desired ----------------------------------------------------------------------------*/ bool gunOrientGimbleGun(Ship *ship,Gun *gun,SpaceObjRotImpTarg *target) { GunStatic *gunstatic = gun->gunstatic; vector targetInWorldCoordSys; vector targetInShipCoordSys; vector targetInGunCoordSys; real32 dotprod,bulletspeed; real32 triggerHappy, maxAngle, maxAngleSpeed; real32 tryangle; real32 turndeclination; real32 desireddeclinationspeed; real32 turnangle; real32 desiredanglespeed; bool updateguncoordsys; if (bitTest(ship->flags, SOF_NISShip)) { triggerHappy = NIS_TriggerHappyAngle; maxAngle = NIS_TriggerHappyAngle; maxAngleSpeed = gunstatic->maxanglespeed; } else { triggerHappy = gunstatic->triggerHappy; maxAngle = gunstatic->cosmaxAngleFromNorm; maxAngleSpeed = gunstatic->maxanglespeed; if ((gunstatic->bulletType == BULLET_Beam) && (target->objtype == OBJ_ShipType)) { ShipClass sclass = ((Ship *)target)->staticinfo->shipclass; if (sclass == CLASS_Fighter) { maxAngle = IONCANNON_TARGETS_FIGHTER_ANGLE; maxAngleSpeed = IONCANNON_TARGETS_FIGHTER_MAXANGLESPEED; triggerHappy = IONCANNON_TARGETS_FIGHTER_TRIGGERHAPPY; } else if (sclass == CLASS_Corvette) { maxAngle = IONCANNON_TARGETS_CORVETTE_ANGLE; maxAngleSpeed = IONCANNON_TARGETS_CORVETTE_MAXANGLESPEED; triggerHappy = IONCANNON_TARGETS_CORVETTE_TRIGGERHAPPY; } } } if(ship->staticinfo->shipclass == CLASS_Fighter) { bulletspeed = gunstatic->bulletspeed*tacticsInfo.BulletSpeedBonus[Tactics_Fighter][ship->tacticstype]; } else if(ship->staticinfo->shipclass == CLASS_Corvette) { bulletspeed = gunstatic->bulletspeed*tacticsInfo.BulletSpeedBonus[Tactics_Corvette][ship->tacticstype]; } else { bulletspeed = gunstatic->bulletspeed; } if (gunstatic->guntype == GUN_Gimble) { aishipGetTrajectoryWithVelPrediction(ship,target,bulletspeed,&targetInWorldCoordSys); // vecSub(targetInWorldCoordSys,target->posinfo.position,ship->posinfo.position); matMultiplyVecByMat(&targetInShipCoordSys,&targetInWorldCoordSys,&ship->rotinfo.coordsys); vecSubFrom(targetInShipCoordSys,gunstatic->position); vecNormalize(&targetInShipCoordSys); dotprod = vecDotProduct(targetInShipCoordSys,gunstatic->gunnormal); if (dotprod > 1.0f) { dotprod = 1.0f; // do this because vecNormalize does not normalize perfectly } if (isBetweenInclusive(dotprod,maxAngle,gunstatic->cosminAngleFromNorm)) { gun->gunheading = targetInShipCoordSys; soundEventStop(gun->gimblehandle); gun->gimblehandle = -1; return TRUE; } if (gun->gimblehandle < 0) { gun->gimblehandle = soundEventPlay(ship, Gun_WeaponMove, gun); } return FALSE; } else if (gunstatic->guntype == GUN_NewGimble) { if (gunstatic->slaveDriver >= 0) { //if this gun has a slavedriver gunUpdateSlave(gun, ship->gunInfo, gunstatic->slaveDriver); } aishipGetTrajectoryWithVelPrediction(ship,target,bulletspeed,&targetInWorldCoordSys); // vecSub(targetInWorldCoordSys,target->posinfo.position,ship->posinfo.position); matMultiplyVecByMat(&targetInShipCoordSys,&targetInWorldCoordSys,&ship->rotinfo.coordsys); // we have targetInShipCoordSys. Now change to gun's coordinate system vecSubFrom(targetInShipCoordSys,gunstatic->position); vecNormalize(&targetInShipCoordSys); matMultiplyVecByMat(&targetInGunCoordSys,&targetInShipCoordSys,&gun->curgunorientation); dotprod = targetInGunCoordSys.z; // (0,0,1) dot (x,y,z) == z gun->anglespeed = 0.0f; gun->declinationspeed = 0.0f; if ((dotprod >= triggerHappy) && (gunstatic->bulletType != BULLET_Beam)) { goto onTargetExitEarly; } updateguncoordsys = FALSE; if (gunstatic->slaveDriver < 0) { //only do updates for free guns // always want target to be on positive z axis in gun coordinate system (i.e. gun barrel on z should point to target) if (targetInGunCoordSys.y != 0) { desiredanglespeed = -targetInGunCoordSys.y * gunstatic->angletracking; if (desiredanglespeed > maxAngleSpeed) { desiredanglespeed = maxAngleSpeed; } else if (desiredanglespeed < -maxAngleSpeed) { desiredanglespeed = -maxAngleSpeed; } turnangle = desiredanglespeed * universe.phystimeelapsed; tryangle = turnangle + gun->angle; if ((tryangle < gunstatic->maxturnangle) && (tryangle > gunstatic->minturnangle)) { gun->angle = tryangle; gun->anglespeed = desiredanglespeed; updateguncoordsys = TRUE; } } if (targetInGunCoordSys.x != 0) { desireddeclinationspeed = targetInGunCoordSys.x * gunstatic->declinationtracking; if (desireddeclinationspeed > maxAngleSpeed) { desireddeclinationspeed = maxAngleSpeed; } else if (desireddeclinationspeed < -maxAngleSpeed) { desireddeclinationspeed = -maxAngleSpeed; } turndeclination = desireddeclinationspeed * universe.phystimeelapsed; tryangle = turndeclination + gun->declination; if ((tryangle < gunstatic->maxdeclination) && (tryangle > gunstatic->mindeclination)) { gun->declination = tryangle; gun->declinationspeed = desireddeclinationspeed; updateguncoordsys = TRUE; } } if (updateguncoordsys) { gunNewGimbleUpdateCoordSys(gun,gunstatic); } } if (dotprod >= triggerHappy) { onTargetExitEarly: gun->gunheading = targetInShipCoordSys; soundEventStop(gun->gimblehandle); gun->gimblehandle = -1; return TRUE; } else { if (gun->gimblehandle < 0) { gun->gimblehandle = soundEventPlay(ship, Gun_WeaponMove, gun); } return FALSE; } } else { dbgAssert(FALSE); return FALSE; } return(FALSE); } void gunGetGunPositionInWorld(vector *positionInWorldCoordSys,matrix *coordsys,Gun *gun) { GunStatic *gunstatic = gun->gunstatic; matMultiplyMatByVec(positionInWorldCoordSys,coordsys,&gunstatic->position); } /*----------------------------------------------------------------------------- Name : missileShoot Description : shoots a missile Inputs : ship which owns gun, gun to fire missile Outputs : Return : ----------------------------------------------------------------------------*/ void missileShoot(Ship *ship,Gun *gun,SpaceObjRotImpTarg *target) { Missile *missile; GunStatic *gunstatic = gun->gunstatic; ShipStaticInfo *shipstatic = (ShipStaticInfo *)ship->staticinfo; etgeffectstatic *stat; etglod *etgLOD; sdword LOD,i; real32 floatDamage; udword intDamage; ShipRace shiprace = ship->shiprace; matrix newCoordsys; MinelayerCorvetteSpec *spec; vector gunheadingInWorldCoordSys; vector positionInWorldCoordSys,mineup,mineright,minehead; #if ENABLE_SHIPRECOIL vector recoil; real32 massovertime; #endif dbgAssert(gunstatic->guntype == GUN_MissileLauncher || gunstatic->guntype == GUN_MineLauncher); dbgAssert(gunHasMissiles(gun)); //line-of-sight check if (target != NULL && gunstatic->guntype == GUN_MissileLauncher) { vector gunDirection, gunPosition; matMultiplyMatByVec(&gunDirection, &ship->rotinfo.coordsys, &gun->gunheading); matMultiplyMatByVec(&gunPosition, &ship->rotinfo.coordsys, &gunstatic->position); vecAddTo(gunPosition, ship->posinfo.position); if (!collCheckLineOfSight(ship, (Ship*)target, &gunPosition, &gunDirection)) { #if GUN_VERBOSE_LEVEL > 1 dbgMessage("\nline-of-sight obscured, not shooting"); #endif return; } } gun->numMissiles--; gun->lasttimefired = universe.totaltimeelapsed; missile = memAlloc(sizeof(Missile),"Missile",0); memset(missile,0,sizeof(Missile)); missile->objtype = OBJ_MissileType; missile->flags = SOF_Rotatable + SOF_Impactable + SOF_NotBumpable + SOF_Targetable; missile->enginePosition.x = 0.0f; missile->enginePosition.y = 0.0f; missile->enginePosition.z = 0.0f; missile->trail = NULL; if(gunstatic->guntype == GUN_MissileLauncher) { //specific definitions for MISSILES missile->staticinfo = &missileStaticInfos[shiprace]; missile->maxtraveldist = missile->staticinfo->staticheader.maxvelocity * universe.phystimeelapsed; missile->missileType = MISSILE_Regular; missile->FORCE_DROPPED = FALSE; missile->formationinfo = NULL; missile->trail = mistrailNew(missile->staticinfo->trailStatic, missile); } //specific definitions for MINES else { //must be a mine launcher missile->staticinfo = &mineStaticInfos[shiprace]; dbgAssert(ship->shiptype == MinelayerCorvette); spec = (MinelayerCorvetteSpec *)ship->ShipSpecifics; missile->missileType = MISSILE_Mine; missile->mineAIState = spec->mineaistate; //initialize the Mines AI state if(spec->mineaistate == MINE_DROP_FORMATION) { listAddNode(&spec->mineforminfo->MineList,&(missile->formationLink),missile); missile->formationinfo = spec->mineforminfo; missile->formation_position = spec->formation_position; //only way to communicate this info missile->formation_number_X = spec->formation_number_X; missile->formation_number_Y = spec->formation_number_Y; missile->maxtraveldist = ((MissileStaticInfo *)missile->staticinfo)->maxvelocity_FORCED * universe.phystimeelapsed; missile->FORCE_DROPPED = TRUE; //set flag } else { missile->FORCE_DROPPED = FALSE; //set flag missile->formationinfo = NULL; missile->maxtraveldist = missile->staticinfo->staticheader.maxvelocity * universe.phystimeelapsed; } } missile->haveCheckedForLoss = 0; //init target loss variable missile->colorScheme = ship->colorScheme; missile->health = ((MissileStaticInfo *)missile->staticinfo)->maxhealth; ClearNode(missile->renderlink); missile->currentLOD = ship->currentLOD; missile->collMyBlob = NULL; missile->cameraDistanceSquared = ship->cameraDistanceSquared; missile->flashtimer = 0; missile->lasttimecollided = 0; missile->attributes = 0; missile->attributesParam = 0; vecZeroVector(missile->posinfo.force); vecZeroVector(missile->rotinfo.torque); for (i=0;inonstatvars.thruststrength[i] = missile->staticinfo->thruststrengthstat[i]; } for(i=0;i< NUM_ROT_DEGOFFREEDOM;i++) { missile->nonstatvars.rotstrength[i] = missile->staticinfo->rotstrengthstat[i]; } for(i=0;i< NUM_TURN_TYPES;i++) { missile->nonstatvars.turnspeed[i] = missile->staticinfo->turnspeedstat[i]; } missile->soundType = gunstatic->gunsoundtype; missile->race = shiprace; missile->timelived = 0.0f; missile->playerowner = ship->playerowner; missile->owner = ship; missile->target = target; if(missile->FORCE_DROPPED == TRUE) { //force dropped mine only! MinelayerCorvetteStatics *staticinfo = (MinelayerCorvetteStatics *) ship->staticinfo->custstatinfo; missile->damage = (frandombetween(staticinfo->forced_drop_damage_lo,staticinfo->forced_drop_damage_hi)); missile->totallifetime = staticinfo->forced_drop_lifetime; } else { //normal missile->damage = (frandombetween(gunstatic->gunDamageLo[ship->tacticstype],gunstatic->gunDamageHi[ship->tacticstype])); missile->totallifetime = gunstatic->bulletlifetime; } //test to see if needs damage modifier if(bitTest(ship->specialFlags,SPECIAL_KasSpecialDamageModifier)) { //ship is being 'beefed' up by KAS...add value to damagemultiplier missile->damage*=ship->damageModifier; } missile->missileID.missileNumber = universe.missileNumber; if (multiPlayerGame) IDToPtrTableAdd(&MissileIDToPtr,universe.missileNumber,(SpaceObj *)missile); universe.missileNumber++; // now fill in missile->posinfo // heading matMultiplyMatByVec(&gunheadingInWorldCoordSys,&ship->rotinfo.coordsys,&gun->gunheading); // position matMultiplyMatByVec(&positionInWorldCoordSys,&ship->rotinfo.coordsys,&gunstatic->position); vecAdd(missile->posinfo.position,positionInWorldCoordSys,ship->posinfo.position); missile->posinfo.position.x += gunheadingInWorldCoordSys.x * gunstatic->barrelLength; missile->posinfo.position.y += gunheadingInWorldCoordSys.y * gunstatic->barrelLength; missile->posinfo.position.z += gunheadingInWorldCoordSys.z * gunstatic->barrelLength; missile->posinfo.isMoving = TRUE; missile->posinfo.haventCalculatedDist = TRUE; vecScalarMultiply(missile->posinfo.velocity,gunheadingInWorldCoordSys,gunstatic->bulletspeed); // now fill in missile->rotinfo if (gunstatic->guntype == GUN_MineLauncher) { //mine launcher so fix mine rotation depending on gun launched from...and race? //hard coded fix to a special exception :) if(ship->gunInfo->numGuns > 1) { //race 1 ship..do rotation..later make global LOD on this..since its a waste of time..really //build mine coordinate system from the ships...BIG savings... if(((Gun *) &(ship->gunInfo->guns[0])) == gun) { matGetVectFromMatrixCol1(mineright,ship->rotinfo.coordsys); matGetVectFromMatrixCol2(mineup,ship->rotinfo.coordsys); matGetVectFromMatrixCol3(minehead,ship->rotinfo.coordsys); vecScalarMultiply(mineup,mineup,-1.0f); } else { matGetVectFromMatrixCol1(mineright,ship->rotinfo.coordsys); matGetVectFromMatrixCol2(mineup,ship->rotinfo.coordsys); matGetVectFromMatrixCol3(minehead,ship->rotinfo.coordsys); vecScalarMultiply(mineright,mineright, -1.0f); } matPutVectIntoMatrixCol1(mineup,missile->rotinfo.coordsys); matPutVectIntoMatrixCol2(mineright,missile->rotinfo.coordsys); matPutVectIntoMatrixCol3(minehead,missile->rotinfo.coordsys); } else { // LM: what is this stranded code doing? matGetVectFromMatrixCol1(mineup,ship->rotinfo.coordsys); matGetVectFromMatrixCol2(mineright,ship->rotinfo.coordsys); matGetVectFromMatrixCol3(minehead,ship->rotinfo.coordsys); matPutVectIntoMatrixCol1(mineup,missile->rotinfo.coordsys); matPutVectIntoMatrixCol2(mineright,missile->rotinfo.coordsys); matPutVectIntoMatrixCol3(minehead,missile->rotinfo.coordsys); // matCreateCoordSysFromHeading(&missile->rotinfo.coordsys,&gunheadingInWorldCoordSys); } } else { matCreateCoordSysFromHeading(&missile->rotinfo.coordsys,&gunheadingInWorldCoordSys); } univUpdateObjRotInfo((SpaceObjRot *)missile); if ((target != NULL) && (target->collMyBlob != NULL)) { collAddSpaceObjToSpecificBlob(target->collMyBlob,(SpaceObj *)missile); } else { collAddSpaceObjToCollBlobs((SpaceObj *)missile); } listAddNode(&universe.SpaceObjList,&(missile->objlink),missile); listAddNode(&universe.ImpactableList,&(missile->impactablelink),missile); listAddNode(&universe.MissileList,&(missile->missilelink),missile); univAddObjToRenderListIf((SpaceObj *)missile,(SpaceObj *)ship); // add missile to render list if parent ship is in render list // if (gunstatic->burstFireTime == 0.0f) { soundEventPlay(ship, Gun_WeaponShot, gun); } //muzle flash effect trigger and set collision event missile->hitEffect = etgGunEventTable[shipstatic->shiprace][gunstatic->gunsoundtype][EGT_GunHit];//get pointer to bullet effect // missile->hitEffect = etgGunEventTable[shiprace][gunstatic->gunsoundtype][EGT_GunHit];//just get pointer from parent gun etgLOD = etgGunEventTable[shipstatic->shiprace][gunstatic->gunsoundtype][EGT_GunFire];//get pointer to bullet effect if (etgLOD != NULL) { LOD = ship->currentLOD; if (LOD >= etgLOD->nLevels) { stat = NULL; } else { stat = etgLOD->level[LOD]; } } else { stat = NULL; } // stat = etgGunEventTable[shiprace][gunstatic->gunsoundtype][EGT_GunFire];//get gun effect if (univSpaceObjInRenderList((SpaceObj *)ship)) { #if ETG_DISABLEABLE if (stat != NULL && etgFireEffectsEnabled && etgEffectsEnabled && !etgFrequencyExceeded(stat)) #else if (stat != NULL && etgFireEffectsEnabled && !etgFrequencyExceeded(stat)) #endif { //if there is a gun fire effect matCreateCoordSysFromHeading(&newCoordsys,&gunheadingInWorldCoordSys); floatDamage = (real32)missile->damage; if (RGLtype == SWtype) { //smaller muzzle flashes in software floatDamage *= etgSoftwareScalarFire; } floatDamage *= ship->magnitudeSquared; intDamage = TreatAsUdword(floatDamage); etgEffectCreate(stat, ship, &missile->posinfo.position, NULL, &newCoordsys, 1.0f, EAF_Velocity | EAF_NLips, 1, intDamage); } } if(bitTest(ship->flags,SOF_CloakGenField)) { //dang decloakage... bitSet(ship->flags,SOF_DeCloaking); if(bitTest(ship->flags,SOF_Cloaked)) { bitClear(ship->flags,SOF_Cloaked); ship->shipDeCloakTime = universe.totaltimeelapsed; } bitClear(ship->flags,SOF_Cloaking); } if (target != NULL && target->objtype == OBJ_ShipType) { //if we succeeded in shooting at enemy ((Ship *)target)->firingAtUs = ship; ((Ship *)target)->recentlyFiredUpon = RECENT_ATTACK_DURATION; } } /*----------------------------------------------------------------------------- Name : gunRecoilTableCompute Description : Computes and fills in the gun recoil motion table Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void gunRecoilTableCompute(void) { sdword index; real32 timeIndex, value; for (index = 0, timeIndex = 0.0f; timeIndex < GUN_RecoilTime; index++, timeIndex += 1.0f / (real32)GUN_RecoilTableLength) { //y = 1 - ((x - .05) * 20)^2 value = (timeIndex - GUN_RecoilTime) * GUN_OneOverTime; gunRecoilTable[index] = 1.0f - (value * value); } for (; index < GUN_RecoilTableLength; index++, timeIndex += 1.0f / (real32)GUN_RecoilTableLength) { //y = ((-atan((x-.4)*8)/PI+.5-.06538)/.825433 gunRecoilTable[index] = (((real32)-atan((timeIndex-0.4f)*8.0f)/PI+0.5f)-0.06538f)/0.825433f; } gunRecoilTable[GUN_RecoilTableLength] = 0.0f; } /*----------------------------------------------------------------------------- Name : gunRecoilVectorCompute Description : Compute a vector representing a gun's recoil as a function of time between shots. Inputs : dest - heading of gun mesh recoilLength - length of the gun's recoil, in meters lastFired - absolute time of the last gun shot rateOfFire - time between shots Outputs : dest - recoil vector to be added to position Return : ----------------------------------------------------------------------------*/ void gunRecoilVectorCompute(vector *dest, real32 recoilLength, real32 lastFired, real32 rateOfFire) { real32 timeElapsed, offset; sdword index; timeElapsed = universe.totaltimeelapsed - lastFired; if (timeElapsed > rateOfFire) { //if the gun is sitting idle dest->x = dest->y = dest->z = 0.0f; //no recoil at all return; } timeElapsed /= rateOfFire; //normalize time elapsed from 0 to 1 index = (sdword)(timeElapsed * (real32)GUN_RecoilTableLength); offset = gunRecoilTable[index]; //table-lookup for gun recoil curve offset *= -recoilLength; dest->x *= offset; dest->y *= offset; dest->z *= offset; } /*----------------------------------------------------------------------------- Name : gunShoot Description : shoots a given gun of the given ship Inputs : ship - that which owns gun gun - gun to fire target - object being shot at or NULL Outputs : Return : ----------------------------------------------------------------------------*/ void gunShoot(Ship *ship, Gun *gun, SpaceObjRotImpTarg *target) { Bullet *bullet; GunStatic *gunstatic = gun->gunstatic; ShipStaticInfo *shipstatic; etgeffectstatic *stat; etglod *etgLOD; sdword LOD; real32 floatDamage,bulletspeed,damagemult,healthFactor; udword intDamage, intVelocity, intLength; vector offset0, offset1; CommandToDo *shipFormationCommand; vector gunheadingInWorldCoordSys; vector positionInWorldCoordSys; vector gunDirection, gunPosition; #if ENABLE_SHIPRECOIL vector recoil; real32 massovertime; #endif dbgAssert(gunstatic->guntype != GUN_MissileLauncher); //line-of-sight check if (target != NULL) { switch (((ShipStaticInfo*)ship->staticinfo)->shipclass) { case CLASS_Mothership: case CLASS_HeavyCruiser: case CLASS_Destroyer: case CLASS_Carrier: case CLASS_Frigate: matMultiplyMatByVec(&gunDirection, &ship->rotinfo.coordsys, &gun->gunheading); matMultiplyMatByVec(&gunPosition, &ship->rotinfo.coordsys, &gunstatic->position); vecAddTo(gunPosition, ship->posinfo.position); if (!collCheckLineOfSight(ship, (Ship*)target, &gunPosition, &gunDirection)) { #if GUN_VERBOSE_LEVEL > 1 dbgMessage("\nline-of-sight obscured, not shooting"); #endif return; } } } //if this is the first gunshot, set the shipisattacking variable //for the ship and his wingman as well (if the wingman exists) if (!ship->shipisattacking) { ship->shipisattacking = TRUE; //dbgMessagef("\nShip %i is attacking (gun)", ship->shipID.shipNumber); } if ((ship->attackvars.myWingmanIs)&&(!ship->attackvars.myWingmanIs->shipisattacking)) { ship->attackvars.myWingmanIs->shipisattacking = TRUE; //dbgMessagef("\nShip %i is attacking (gun - wingman)", ship->attackvars.myWingmanIs->shipID.shipNumber); } if(ship->specialFlags2 & SPECIAL_2_ShipInGravwell) { //ship is in gravwell return; //don't let it fire. } shipstatic = (ShipStaticInfo *)ship->staticinfo; gun->lasttimefired = universe.totaltimeelapsed; bullet = memAlloc(sizeof(Bullet),"Bu(Bullet)",Pyrophoric); bullet->objtype = OBJ_BulletType; bullet->flags = 0; bullet->staticinfo = NULL; ClearNode(bullet->renderlink); bullet->currentLOD = ship->currentLOD; bullet->collMyBlob = NULL; bullet->cameraDistanceSquared = ship->cameraDistanceSquared; //calculate a damage multipler based on several variables //including strike craft being in formation, and their damage switch(ship->staticinfo->shipclass) { case CLASS_Fighter: case CLASS_Corvette: //optimize 'healthfactor' for all game operations //by calculating it once a frame? healthFactor = ship->health*ship->staticinfo->oneOverMaxHealth; if (healthFactor < selShipHealthYellowFactor) { //in the red damagemult = tacticsInfo.tacticsDamageModifierRed; } else if (healthFactor < selShipHealthGreenFactor ) { //in the yellow damagemult = tacticsInfo.tacticsDamageModifierYellow; } else { //in the green damagemult = 1.0f; } shipFormationCommand = ship->formationcommand; if(shipFormationCommand != NULL) { //in formation..concatenate the damage multipler from above with this new one damagemult *= (shipFormationCommand->formation.formationtype == SPHERE_FORMATION) ? tacticsInfo.tacticsDamageMultiplierInSphereFormation : tacticsInfo.tacticsDamageMultiplierInFormation; break; } break; default: damagemult = 1.0f; break; } if(bitTest(ship->specialFlags,SPECIAL_KasSpecialDamageModifier)) { //ship is being 'beefed' up by KAS...add value to damagemultiplier damagemult*=ship->damageModifier; } //set gun damage based on ships tactical state. if(bitTest(ship->specialFlags,SPECIAL_BurstFiring)) { bullet->damageFull = bullet->damage = frandombetween(burstDamageLo[ship->shiprace],burstDamageHi[ship->shiprace])*damagemult; bullet->bulletType = BULLET_SpecialBurst; } else { bullet->damageFull = bullet->damage = frandombetween(gunstatic->gunDamageLo[ship->tacticstype],gunstatic->gunDamageHi[ship->tacticstype])*damagemult; bullet->bulletType = gunstatic->bulletType; } bullet->soundType = gunstatic->gunsoundtype; bullet->owner = ship; bullet->playerowner = ship->playerowner; bullet->gunowner = gun; if ((target != NULL))// && (target->objtype != OBJ_MissileType)) { if(bitTest(ship->specialFlags,SPECIAL_BurstFiring)) bullet->target = NULL; else bullet->target = target; } else { bullet->target = NULL; } bullet->bulletColor = etgBulletColor[shipstatic->shiprace][bullet->soundType]; bullet->bulletmass = gunstatic->bulletmass; bullet->lengthmag = gunstatic->bulletlength; bullet->timelived = 0.0f; if(bitTest(ship->specialFlags,SPECIAL_BurstFiring)) { HeavyCorvetteSpec *spec = (HeavyCorvetteSpec *)ship->ShipSpecifics; bullet->totallifetime = spec->bulletLifeTime; } else { //set bullet life time based on range (and hence add tactics bonuses) if(ship->staticinfo->shipclass == CLASS_Fighter) { bullet->totallifetime = gunstatic->bulletlifetime*tacticsInfo.BulletRangeBonus[Tactics_Fighter][ship->tacticstype]; } else if(ship->staticinfo->shipclass == CLASS_Corvette) { bullet->totallifetime = gunstatic->bulletlifetime*tacticsInfo.BulletRangeBonus[Tactics_Corvette][ship->tacticstype]; } else { bullet->totallifetime = gunstatic->bulletlifetime; } } bullet->SpecialEffectFlag = 0; bullet->DFGFieldEntryTime = 0.0f; // now fill in bullet->posinfo memset(&(bullet->posinfo),0,sizeof(bullet->posinfo)); // heading matMultiplyMatByVec(&gunheadingInWorldCoordSys,&ship->rotinfo.coordsys,&gun->gunheading); bullet->bulletheading = gunheadingInWorldCoordSys; // length of bullet vecScalarMultiply(bullet->lengthvec,gunheadingInWorldCoordSys,gunstatic->bulletlength); matCreateCoordSysFromHeading(&bullet->rotinfo.coordsys,&gunheadingInWorldCoordSys); switch (bullet->bulletType) { case BULLET_Beam: vecZeroVector(bullet->posinfo.velocity); bullet->traveldist = gunstatic->bulletlength; bullet->beamtraveldist = gunstatic->bulletlength; break; default: // velocity if(bitTest(ship->specialFlags,SPECIAL_BurstFiring)) { //fix later bulletspeed = burstSpeed; } else { if(ship->staticinfo->shipclass == CLASS_Fighter) { bulletspeed = gunstatic->bulletspeed*tacticsInfo.BulletSpeedBonus[Tactics_Fighter][ship->tacticstype]; } else if(ship->staticinfo->shipclass == CLASS_Corvette) { bulletspeed = gunstatic->bulletspeed*tacticsInfo.BulletSpeedBonus[Tactics_Corvette][ship->tacticstype]; } else { bulletspeed = gunstatic->bulletspeed; } } vecScalarMultiply(bullet->posinfo.velocity,gunheadingInWorldCoordSys,bulletspeed); // bullettravel bullet->traveldist = bulletspeed * universe.phystimeelapsed; break; } #if ENABLE_SHIPRECOIL if (!shipstatic->staticheader.immobile) // if ship isn't immobile, apply recoil (opposite force) { massovertime = -bullet->bulletmass / universe.phystimeelapsed; recoil = bullet->posinfo.velocity; vecMultiplyByScalar(recoil,massovertime); physApplyForceVectorToObj(ship,recoil); } #endif // position matMultiplyMatByVec(&positionInWorldCoordSys,&ship->rotinfo.coordsys,&gunstatic->position); //add barrel length if (gunstatic->offset.x != 0.0f) { //if there is an offset vector matMultiplyMatByVec(&offset0, &gun->curgunorientation, &gunstatic->offset);//offset in gun space matMultiplyMatByVec(&offset1, &ship->rotinfo.coordsys, &offset0);//offset in world space vecAdd(positionInWorldCoordSys, positionInWorldCoordSys, offset1);//add to position } vecAdd(bullet->posinfo.position,positionInWorldCoordSys,ship->posinfo.position); bullet->posinfo.position.x += gunheadingInWorldCoordSys.x * gunstatic->barrelLength; bullet->posinfo.position.y += gunheadingInWorldCoordSys.y * gunstatic->barrelLength; bullet->posinfo.position.z += gunheadingInWorldCoordSys.z * gunstatic->barrelLength; bullet->posinfo.isMoving = TRUE; bullet->posinfo.haventCalculatedDist = TRUE; if ((target != NULL) && (target->collMyBlob != NULL)) { collAddSpaceObjToSpecificBlob(target->collMyBlob,(SpaceObj *)bullet); } else { collAddSpaceObjToCollBlobs((SpaceObj *)bullet); } listAddNode(&universe.SpaceObjList,&(bullet->objlink),bullet); listAddNode(&universe.BulletList,&(bullet->bulletlink),bullet); univAddObjToRenderListIf((SpaceObj *)bullet,(SpaceObj *)ship); // add to render list if parent ship is in render list // if (gunstatic->burstFireTime == 0.0f) { if (bullet->bulletType == BULLET_Beam) { soundEventPlay(ship, Gun_WeaponFireLooped, gun); } else { soundEventPlay(ship, Gun_WeaponShot, gun); } } //figure out some parameters for the effects we are about to spawn floatDamage = (real32)bullet->damage; floatDamage *= ship->magnitudeSquared; intDamage = TreatAsUdword(floatDamage); intVelocity = TreatAsUdword(bulletspeed); intLength = TreatAsUdword(gunstatic->bulletlength); //create an effect for bullet, if applicable if(bitTest(ship->specialFlags,SPECIAL_BurstFiring)) { etgLOD = etgSpecialPurposeEffectTable[EGT_BURST_BULLET_EFFECT]; } else { etgLOD = etgGunEventTable[shipstatic->shiprace][gunstatic->gunsoundtype][EGT_GunBullet];//get pointer to bullet effect } if (etgLOD != NULL) { if (target != NULL) { LOD = min(ship->currentLOD, target->currentLOD); } else { LOD = ship->currentLOD; } if (LOD >= etgLOD->nLevels) { stat = NULL; } else { stat = etgLOD->level[LOD]; } } else { stat = NULL; } if (univSpaceObjInRenderList((SpaceObj *)ship)) { #if ETG_DISABLEABLE if (stat != NULL && etgBulletEffectsEnabled && etgEffectsEnabled && !etgFrequencyExceeded(stat)) #else if (stat != NULL && etgBulletEffectsEnabled && !etgFrequencyExceeded(stat)) #endif { bullet->effect = etgEffectCreate(stat, (Ship *)bullet, NULL, &ship->posinfo.velocity, NULL, 1.0f, EAF_AllButNLips, 3, intDamage, intVelocity, intLength); } else { bullet->effect = NULL; //play no effect for this bullet } } else { bullet->effect = NULL; //play no effect for this bullet } //fire effect trigger and set collision event bullet->hitEffect = etgGunEventTable[shipstatic->shiprace][gunstatic->gunsoundtype][EGT_GunHit];//get pointer to bullet effect // bullet->hitEffect = etgGunEventTable[ship->shiprace][gunstatic->gunsoundtype][EGT_GunHit];//just get pointer from parent gun etgLOD = etgGunEventTable[shipstatic->shiprace][gunstatic->gunsoundtype][EGT_GunFire];//get pointer to bullet effect if (etgLOD != NULL) { LOD = ship->currentLOD; if (LOD >= etgLOD->nLevels) { stat = NULL; } else { stat = etgLOD->level[LOD]; } } else { stat = NULL; } // stat = etgGunEventTable[ship->shiprace][gunstatic->gunsoundtype][EGT_GunFire];//get gun effect if (univSpaceObjInRenderList((SpaceObj *)ship)) { #if ETG_DISABLEABLE if (stat != NULL && etgFireEffectsEnabled && etgEffectsEnabled && !etgFrequencyExceeded(stat)) #else if (stat != NULL && etgFireEffectsEnabled && !etgFrequencyExceeded(stat)) #endif { //if there is a gun fire effect if (RGLtype == SWtype) { //smaller muzzle flashes in software floatDamage *= etgSoftwareScalarFire; } etgEffectCreate(stat, ship, &bullet->posinfo.position, &ship->posinfo.velocity, &bullet->rotinfo.coordsys, 1.0f, EAF_Velocity, 1, intDamage); } } if(bitTest(ship->flags,SOF_CloakGenField)) { bitSet(ship->flags,SOF_DeCloaking); if(bitTest(ship->flags,SOF_Cloaked)) { bitClear(ship->flags,SOF_Cloaked); ship->shipDeCloakTime = universe.totaltimeelapsed; } bitClear(ship->flags,SOF_Cloaking); } if (target != NULL && target->objtype == OBJ_ShipType) { //if we succeeded in shooting at enemy ((Ship *)target)->firingAtUs = ship; ((Ship *)target)->recentlyFiredUpon = RECENT_ATTACK_DURATION; } } /*----------------------------------------------------------------------------- Name : gunShootGunsAtTarget Description : shoots a ship's guns at target Inputs : ship, target, range, [trajectory] (trajectory required for ships with fixed guns) Outputs : Return : Note : trajectory can be passed in as NULL for ships that don't have any fixed guns. trajectory is required only for ships that have fixed guns. ----------------------------------------------------------------------------*/ bool gunShootGunsAtTarget(Ship *ship,SpaceObjRotImpTarg *target,real32 range,vector *trajectory) { GunInfo *gunInfo = ship->gunInfo; sdword numGuns; Gun *gun; sdword i; vector shipheading; bool shotguns = FALSE; GunStatic *gunstatic; real32 dotprod; real32 bonus; real32 triggerHappy; if (gunInfo == NULL) { // dbgMessagef("\nWARNING: no guns on ship %s %s, tried to shoot",ShipRaceToStr(ship->shiprace),ShipTypeToStr(ship->shiptype)); return shotguns; } numGuns = gunInfo->numGuns; if(ship->staticinfo->shipclass == CLASS_Fighter) { bonus = tacticsInfo.BulletRangeBonus[Tactics_Fighter][ship->tacticstype]; } else if (ship->staticinfo->shipclass == CLASS_Corvette) { bonus = tacticsInfo.BulletRangeBonus[Tactics_Fighter][ship->tacticstype]; } else { bonus = 1.0f; } for (i=0;iguns[i]; gunstatic = gun->gunstatic; if (bitTest(ship->flags, SOF_NISShip)) { triggerHappy = NIS_TriggerHappyAngle; } else { triggerHappy = gunstatic->triggerHappy; } if(ship->staticinfo->madStatic != NULL) { //ship has meshanimations if(ship->staticinfo->madStatic->numGunOpenIndexes > 0) { //has gun opening animations if(ship->madGunStatus != MAD_STATUS_GUNS_OPEN && ship->madGunStatus != MAD_STATUS_GUNS_OPENING) { madOpenGunsShip(ship); return shotguns; } else if(ship->madGunStatus != MAD_STATUS_GUNS_OPEN) { return shotguns; } //otherwise, we're open..try to fire away! } } if (range < gunstatic->bulletrange*bonus) { if (gunCanShoot(ship, gun)) { switch (gunstatic->guntype) { case GUN_MissileLauncher: if (gunHasMissiles(gun)) { matGetVectFromMatrixCol3(shipheading,ship->rotinfo.coordsys); dotprod = vecDotProduct(*trajectory,shipheading); if (dotprod >= triggerHappy) { missileShoot(ship,gun,target); shotguns = TRUE; } } break; case GUN_MineLauncher: if (gunHasMissiles(gun)) { matGetVectFromMatrixCol3(shipheading,ship->rotinfo.coordsys); dotprod = vecDotProduct(*trajectory,shipheading); if (dotprod >= triggerHappy) { missileShoot(ship,gun,target); shotguns = TRUE; } } break; case GUN_Fixed: matGetVectFromMatrixCol3(shipheading,ship->rotinfo.coordsys); dotprod = vecDotProduct(*trajectory,shipheading); if (dotprod >= triggerHappy) { gunShoot(ship,gun,target); shotguns = TRUE; } break; case GUN_Gimble: case GUN_NewGimble: if (gunOrientGimbleGun(ship,gun,target)) { gunShoot(ship,gun,target); shotguns = TRUE; } break; default: dbgAssert(FALSE); break; } } } } /* if (shotguns && target->objtype == OBJ_ShipType) { //if we succeeded in shooting at enemy ((Ship *)target)->firingAtUs = ship; ((Ship *)target)->recentlyFiredUpon = RECENT_ATTACK_DURATION; } */ return shotguns; } /*----------------------------------------------------------------------------- Name : gunShootGunsAtMultipleTargets Description : shoots a ship's guns at multiple targets Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ bool gunShootGunsAtMultipleTargets(Ship *ship) { GunInfo *gunInfo = ship->gunInfo; sdword numGuns = gunInfo->numGuns; Gun *gun; sdword i; vector shipheading; bool shotguns = FALSE; GunStatic *gunstatic; real32 dotprod; AttackTargets *attackTargets = ship->attackvars.multipleAttackTargets; SpaceObjRotImpTarg *target; real32 dist; vector trajectory; real32 temp; real32 triggerHappy; dbgAssert(attackTargets->numAttackTargets == numGuns); // one attack target for each gun for (i=0;iguns[i]; gunstatic = gun->gunstatic; if (bitTest(ship->flags, SOF_NISShip)) { triggerHappy = NIS_TriggerHappyAngle; } else { triggerHappy = gunstatic->triggerHappy; } if(ship->staticinfo->madStatic != NULL) { //ship has meshanimations if(ship->staticinfo->madStatic->numGunOpenIndexes > 0) { //has gun opening animations if(ship->madGunStatus != MAD_STATUS_GUNS_OPEN && ship->madGunStatus != MAD_STATUS_GUNS_OPENING) { madOpenGunsShip(ship); return shotguns; } else if(ship->madGunStatus != MAD_STATUS_GUNS_OPEN) { return shotguns; } //otherwise, we're open..try to fire away! } } if (gunCanShoot(ship, gun)) { target = attackTargets->TargetPtr[i]; if (target != NULL) { switch (gunstatic->guntype) { case GUN_MissileLauncher: if (gunHasMissiles(gun)) { missileShoot(ship,gun,target); shotguns = TRUE; } break; case GUN_MineLauncher: if (gunHasMissiles(gun)) { missileShoot(ship,gun,target); shotguns = TRUE; } break; case GUN_Fixed: aishipGetTrajectory(ship,target,&trajectory); dist = fsqrt(vecMagnitudeSquared(trajectory)); vecDivideByScalar(trajectory,dist,temp); matGetVectFromMatrixCol3(shipheading,ship->rotinfo.coordsys); dotprod = vecDotProduct(trajectory,shipheading); if (dotprod >= triggerHappy) { gunShoot(ship,gun,target); shotguns = TRUE; } break; case GUN_Gimble: case GUN_NewGimble: if (gunOrientGimbleGun(ship,gun,target)) { gunShoot(ship,gun,target); shotguns = TRUE; } break; default: dbgAssert(FALSE); break; } } } else if (gunstatic->slaveDriver >= 0) { gunUpdateSlave(gun, gunInfo, gunstatic->slaveDriver); } } /* if (shotguns && target->objtype == OBJ_ShipType) { //if we succeeded in shooting at enemy ((Ship *)target)->firingAtUs = ship; ((Ship *)target)->recentlyFiredUpon = RECENT_ATTACK_DURATION; } */ return shotguns; } /*----------------------------------------------------------------------------- Name : gunMatrixUpdate Description : Binding matrix callback for updating gun mesh positions. Inputs : flags - flags indicating certain attributes (see mesh.h) startMatrix - starting matrix (from .geo file) matrix - matrix to store result in. data - pointer to Gun structure ID - pointer to ship structure Outputs : computes and fills in the gun matrix. Return : true = matrix updated ----------------------------------------------------------------------------*/ bool gunMatrixUpdate(udword flags, hmatrix *startMatrix, hmatrix *matrix, void *data, sdword ID) { Ship *ship; Gun *gun; GunStatic *gunstatic; bool bRecoil; vector recoilVector; ship = (Ship *)ID; dbgAssert(ship->objtype == OBJ_ShipType); gun = (Gun *)data; gunstatic = gun->gunstatic; bRecoil = bitTest(flags, HBF_Recoil); flags &= (HBF_Declination|HBF_Azimuth); switch (gunstatic->guntype) { case GUN_NewGimble: if (flags == (HBF_Declination|HBF_Azimuth)) { if (bitTest(gunstatic->flags, GF_MultiLevelMatrix)) { hmatMakeHMatFromMat(matrix,&gun->curGunOrientationNonConcat); } else { hmatMakeHMatFromMat(matrix,&gun->curgunorientation); } matrix->m14 = startMatrix->m14; matrix->m24 = startMatrix->m24; matrix->m34 = startMatrix->m34; break; } else if (flags == HBF_Azimuth) { hmatrix rotmatrix; real32 angle = gun->angle; hmatMakeRotAboutX(&rotmatrix,(real32)cos(angle),(real32)sin(angle)); hmatMultiplyHMatByHMat(matrix, startMatrix, &rotmatrix); break; } else if (flags == HBF_Declination) { hmatrix rotmatrix; real32 declination = gun->declination; hmatMakeRotAboutY(&rotmatrix,(real32)cos(declination),(real32)sin(declination)); hmatMultiplyHMatByHMat(matrix, startMatrix, &rotmatrix); break; } else { // dbgAssert(FALSE); // return FALSE; *matrix = IdentityHMatrix; matrix->m14 = startMatrix->m14; matrix->m24 = startMatrix->m24; matrix->m34 = startMatrix->m34; break; } default: dbgAssert(FALSE); return FALSE; } //add recoil, if any if (bRecoil) { hmatGetVectFromHMatrixCol3(recoilVector, *matrix); gunRecoilVectorCompute(&recoilVector, gunstatic->recoilLength, gun->lasttimefired, gunstatic->firetime); // vecAddTo(bullet->posinfo.position, offset0); matrix->m14 += recoilVector.x; matrix->m24 += recoilVector.y; matrix->m34 += recoilVector.z; } return(TRUE); } /*----------------------------------------------------------------------------- Name : gunDrawGunInfo Description : draws gun debug information Inputs : ship Outputs : Return : ----------------------------------------------------------------------------*/ #if RND_VISUALIZATION void gunDrawGunInfo(Ship *ship) { sdword i; GunInfo *gunInfo = ship->gunInfo; sdword numGuns; Gun *gun; GunStatic *gunstatic; vector to, from, offset0; if (gunInfo == NULL) { return; } numGuns = gunInfo->numGuns; rndLightingEnable(FALSE); rndTextureEnable(FALSE); for (i=0,gun=&gunInfo->guns[0];igunstatic; from = gun->gunstatic->position; vecScalarMultiply(to, gun->gunheading,500.0f); vecAddTo(to, from); // heading //gun offset if (gunstatic->offset.x != 0.0f) { //if there is an offset vector matMultiplyMatByVec(&offset0, &gun->curgunorientation, &gunstatic->offset);//offset in gun space vecAddTo(from, offset0); //add to position vecAddTo(to, offset0); //add to position } //add barrel length from.x += gun->gunheading.x * gunstatic->barrelLength; from.y += gun->gunheading.y * gunstatic->barrelLength; from.z += gun->gunheading.z * gunstatic->barrelLength; to.x += gun->gunheading.x * gunstatic->barrelLength; to.y += gun->gunheading.y * gunstatic->barrelLength; to.z += gun->gunheading.z * gunstatic->barrelLength; primLine3(&from, &to, colFuscia/*colRGB(10, 255, 100)*/); } rndLightingEnable(TRUE); } #endif /*----------------------------------------------------------------------------- Name : gunFirePower Description : Compute the firepower of a gun for a given tactics setting. Inputs : gunStatic - static info of the gun tactic - tactics setting to determine damage for Outputs : fireTime - the computed rate of fire. Not set for guns of type BULLET_Beam. Return : ----------------------------------------------------------------------------*/ real32 gunFirePower(GunStatic *gunStatic, TacticsType tactics, real32 *fireTime) { dbgAssert(tactics == Evasive || tactics == Neutral || tactics == Aggressive); if (gunStatic->firetime == 0.0f) { *fireTime = UNIVERSE_UPDATE_PERIOD; } else { *fireTime = gunStatic->firetime; } if (gunStatic->bulletType == BULLET_Beam) { return((gunStatic->gunDamageLo[tactics] + gunStatic->gunDamageHi[tactics]) / 2.0f * (real32)UNIVERSE_UPDATE_RATE * gunStatic->bulletlifetime / *fireTime); } else if(gunStatic->burstFireTime != 0.0f) { //burst fire gun..add aditional factor of: //burstfiretime/(burstfiretime_burstwaittime) return(((gunStatic->gunDamageLo[tactics] + gunStatic->gunDamageHi[tactics]) / 2.0f / *fireTime)*(gunStatic->burstFireTime/(gunStatic->burstFireTime+gunStatic->burstWaitTime))); } return((gunStatic->gunDamageLo[tactics] + gunStatic->gunDamageHi[tactics]) / 2.0f / *fireTime); } /*----------------------------------------------------------------------------- Name : gunShipFirePower Description : Compute the firepower for a given ship at some tactics setting Inputs : info - staticinfo of ship tactics - what tactic to compute damage for Outputs : Return : total damage of the ship ----------------------------------------------------------------------------*/ #define GUN_NumberInterceptorTypes 7 ubyte gunInterceptorTypes[GUN_NumberInterceptorTypes] = {LightInterceptor, HeavyInterceptor, CloakedFighter, P1Fighter, P2Swarmer, P2AdvanceSwarmer, TargetDrone}; real32 gunShipFirePower(ShipStaticInfo *info, TacticsType tactics) { sdword index; real32 power = 0.0f, powerThisGun, totalFireRate, fireRate; GenericInterceptorStatics *interceptorstat; if (memchr(gunInterceptorTypes, info->shiptype, GUN_NumberInterceptorTypes)) { //interceptors have a different rate of fire computation interceptorstat = (GenericInterceptorStatics *)info->custstatinfo; totalFireRate = interceptorstat->firetime; if (info->gunStaticInfo != NULL) { for (index = 0; index < info->gunStaticInfo->numGuns; index++) { powerThisGun = gunFirePower(&info->gunStaticInfo->gunstatics[index], tactics, &fireRate); powerThisGun *= fireRate / (totalFireRate * (real32)info->gunStaticInfo->numGuns); power += powerThisGun; } } } else { if (info->gunStaticInfo != NULL) { for (index = 0; index < info->gunStaticInfo->numGuns; index++) { power += gunFirePower(&info->gunStaticInfo->gunstatics[index], tactics, &fireRate); } } } return(power); } /*----------------------------------------------------------------------------- Name : gunStartup Description : Start the gun module Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void gunStartup(void) { gunRecoilTableCompute(); } /*----------------------------------------------------------------------------- Name : gunShutdown Description : Shut down the gun module Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void gunShutdown(void) { ; } #if GUN_TUNE_MODE bool gunTuningMode = FALSE; sdword tuningGun = 0; void gunTuneGun(Ship *ship) { ShipStaticInfo *shipstatic = ship->staticinfo; GunInfo *gunInfo = ship->gunInfo; Gun *gun; GunStatic *gunstatic; bool updateguncoordsys = FALSE; if (gunInfo == NULL) { return; } if (gunInfo->numGuns == 0) { return; } if (tuningGun >= gunInfo->numGuns) { tuningGun = 0; } gun = &gunInfo->guns[tuningGun]; gunstatic = gun->gunstatic; switch (gunstatic->guntype) { case GUN_NewGimble: if (keyIsHit(ARRLEFT)) { gun->angle -= GUNTUNE_SPEED; updateguncoordsys = TRUE; } if (keyIsHit(ARRRIGHT)) { gun->angle += GUNTUNE_SPEED; updateguncoordsys = TRUE; } if (keyIsHit(ARRUP)) { gun->declination += GUNTUNE_SPEED; updateguncoordsys = TRUE; } if (keyIsHit(ARRDOWN)) { gun->declination -= GUNTUNE_SPEED; updateguncoordsys = TRUE; } if (keyIsHit(NUMPAD0)) { gun->angle = 0.0f; gun->declination = 0.0f; updateguncoordsys = TRUE; } if (updateguncoordsys) { if (gun->angle < gunstatic->minturnangle) { gun->angle = gunstatic->minturnangle; } if (gun->angle > gunstatic->maxturnangle) { gun->angle = gunstatic->maxturnangle; } if (gun->declination < gunstatic->mindeclination) { gun->declination = gunstatic->mindeclination; } if (gun->declination > gunstatic->maxdeclination) { gun->declination = gunstatic->maxdeclination; } gunNewGimbleUpdateCoordSys(gun,gunstatic); } { char buffer[300]; sprintf(buffer,"Gun %d %s Angle:%4.2f Declin:%4.2f Heading:%2.3f %2.3f %2.3f %d",tuningGun,GunTypeToStr(gunstatic->guntype), RAD_TO_DEG(gun->angle),RAD_TO_DEG(gun->declination),gun->gunheading.x,gun->gunheading.y,gun->gunheading.z,universe.univUpdateCounter); strcat(buffer,"\n"); fontPrint(0, 400, colWhite, buffer); //dbgMessage(buffer); } break; default: fontPrintf(0, 400, colWhite, "Gun %d %s",tuningGun,GunTypeToStr(gunstatic->guntype)); break; } } #endif