123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637 |
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- #include "Game_local.h"
- #define WAKEUPTIME 1100 //activation time.
- #define MAXVOLLEY 5 //how many shots in one volley.
- #define WARMUPTIME 1900 //how long the pre-volley warning warmup is.
- #define VOLLEYDELAY 250 //ms between each shot in a volley.
- #define IDLESOUNDDELAY 2000
- #define IDLESOUNDRAND 2000
- #define MUZZLEFLASHTIME 0.05
- #define BEAMWIDTH 4.0f
- #define PITCHADJUSTMENT -8 //aim a little below the eyes.
- #define THROWTIME_THRESHOLD 1000 //thrown objects are valid targets for X milliseconds after being thrown.
- const idEventDef EV_Turret_activate( "turretactivate", "d" );
- const idEventDef EV_Turret_isactive( "turretisactive", NULL, 'd' );
- const idEventDef EV_Turret_muzzleflashoff( "turretmuzzleoff", NULL );
- CLASS_DECLARATION( idAnimatedEntity, idTurret )
- EVENT( EV_Turret_activate, idTurret::Event_activate)
- EVENT( EV_Turret_isactive, idTurret::Event_isactive)
- EVENT( EV_Turret_muzzleflashoff, idTurret::MuzzleflashOff)
- END_CLASS
- void idTurret::Save( idSaveGame *savefile ) const
- {
- savefile->WriteInt(state);
- savefile->WriteInt(attackState);
- savefile->WriteInt(volleyCount);
- savefile->WriteInt(nextIdleSound);
- savefile->WriteMat3(bodyAxis);
- savefile->WriteMat3(turretAxis);
- savefile->WriteObject(beamStart);
- savefile->WriteObject(beamEnd);
- savefile->WriteObject(laserdot);
- //BC 7-29-2016
- savefile->WriteInt(nextStateTime);
- targetEnt.Save(savefile);
- }
- void idTurret::Restore( idRestoreGame *savefile )
- {
- savefile->ReadInt(state);
- savefile->ReadInt(attackState);
- savefile->ReadInt(volleyCount);
- savefile->ReadInt(nextIdleSound);
- savefile->ReadMat3(bodyAxis);
- savefile->ReadMat3(turretAxis);
- savefile->ReadObject(reinterpret_cast<idClass *&>(beamStart));
- savefile->ReadObject(reinterpret_cast<idClass *&>(beamEnd));
- savefile->ReadObject(reinterpret_cast<idClass *&>(laserdot));
- //BC 7-29-2016
- savefile->ReadInt(nextStateTime);
- targetEnt.Restore(savefile);
-
- }
- void idTurret::Spawn( void )
- {
- jointHandle_t bodyJoint, turretJoint;
- idVec3 origin;
- idDict args;
- idVec3 turretPos;
- this->state = OFF;
- this->attackState = IDLE;
- this->nextStateTime = 0;
- this->volleyCount = 0;
- this->nextIdleSound = 0;
- targetEnt = NULL;
- BecomeActive( TH_THINK );
-
- bodyJoint = animator.GetJointHandle( "body" );
- animator.GetJointTransform( bodyJoint, gameLocal.time, origin, this->bodyAxis );
- turretJoint = animator.GetJointHandle( "turret" );
- animator.GetJointTransform( turretJoint, gameLocal.time, turretPos, this->turretAxis );
- turretPos = this->GetPhysics()->GetOrigin() + turretPos * this->GetPhysics()->GetAxis();
- SetSkin(declManager->FindSkin( "skins/turret/skin" ));
- //spawn beam end.
- args.SetVector( "origin", vec3_origin );
- beamEnd = ( idBeam * )gameLocal.SpawnEntityType( idBeam::Type, &args );
-
-
-
- //spawn beam start.
- args.Clear();
- args.Set( "target", beamEnd->name.c_str() );
- args.SetVector( "origin", turretPos );
- args.SetBool( "start_off", true );
- args.Set( "skin", "skins/beam_turret" );
- args.SetFloat( "width", BEAMWIDTH );
-
- beamStart = ( idBeam * )gameLocal.SpawnEntityType( idBeam::Type, &args );
- beamStart->BindToJoint(this, turretJoint, false);
- beamStart->BecomeActive(TH_PHYSICS);
- beamStart->GetRenderEntity()->shaderParms[ SHADERPARM_RED ] = spawnArgs.GetVector("_color").x;
- beamStart->GetRenderEntity()->shaderParms[ SHADERPARM_GREEN ] = spawnArgs.GetVector("_color").y;
- beamStart->GetRenderEntity()->shaderParms[ SHADERPARM_BLUE ] = spawnArgs.GetVector("_color").z;
- beamStart->Hide();
- args.Clear();
- args.SetVector( "origin", vec3_origin );
- args.Set( "model", "models/lasersight/tris.ase" );
- args.SetInt( "solid", 0 );
- laserdot = gameLocal.SpawnEntityType( idStaticEntity::Type, &args );
- laserdot->GetRenderEntity()->shaderParms[ SHADERPARM_RED ] = spawnArgs.GetVector("_color").x;
- laserdot->GetRenderEntity()->shaderParms[ SHADERPARM_GREEN ] = spawnArgs.GetVector("_color").y;
- laserdot->GetRenderEntity()->shaderParms[ SHADERPARM_BLUE ] = spawnArgs.GetVector("_color").z;
- laserdot->Hide();
- laserdot->SetOrigin(beamEnd->GetPhysics()->GetOrigin());
- laserdot->Bind(beamEnd, false);
- if (spawnArgs.GetBool( "start_on", "0" ))
- {
- Event_activate( 1 );
- }
- //precache the projectile.
- }
- void idTurret::Event_isactive()
- {
- idThread::ReturnInt( this->state );
- }
- void idTurret::Event_activate( int value )
- {
- if (value == 1)
- {
- if (state == ON)
- {
- return;
- }
- //open.
- Event_PlayAnim("opening", 4);
- this->state = ON;
- StartSound( "snd_opening", SND_CHANNEL_ANY, 0, false, NULL );
- this->nextStateTime = gameLocal.time + WAKEUPTIME;
- this->nextIdleSound = gameLocal.time + 2000;
- }
- else
- {
- if (state == OFF)
- {
- return;
- }
- //close up.
- int doneTime = Event_PlayAnim("closing", 4);
- this->state = CLOSING;
- this->nextStateTime = doneTime;
- StartSound( "snd_closing", SND_CHANNEL_ANY, 0, false, NULL );
- this->beamStart->Hide();
- this->laserdot->Hide();
- }
- }
- void idTurret::MuzzleflashOff( void )
- {
- SetSkin(declManager->FindSkin( "skins/turret/skin" ));
- }
- void idTurret::GotoWarmupState( void )
- {
- this->attackState = WARMUP;
- StartSound( "snd_warmup", SND_CHANNEL_ANY, 0, false, NULL );
- this->volleyCount = 0;
- this->nextStateTime = gameLocal.time + WARMUPTIME;
- }
- bool idTurret::CheckTargetValidity(idEntity *ent)
- {
- if (CheckTargetLOS(ent, vec3_zero))
- {
- return true;
- }
-
- //first check failed. so now do the fallback offset check.
- idBounds bounds = ent->GetPhysics()->GetBounds();
- idVec3 tmax;
- tmax[2] = bounds[1][2] ;
- tmax[2]--; //shrink it a little.
- if (CheckTargetLOS(ent, idVec3(0,0,tmax[2])))
- {
- return true;
- }
- //both checks have failed. abort.
- return false;
- }
- bool idTurret::CheckTargetLOS(idEntity *ent, idVec3 offset)
- {
- //check distance.
- idVec3 selfPos = GetPhysics()->GetOrigin() + idVec3(0,0,-12);
- float distanceToTarget = ( ent->GetPhysics()->GetOrigin() - selfPos ).Normalize();
- float maxDist = spawnArgs.GetInt("maxrange", "1024");
- if (maxDist < 0)
- maxDist = 1024;
- if (distanceToTarget >= maxDist)
- {
- //too far. Out of range.
- return false;
- }
- idVec3 forward, right, up;
- ent->GetPhysics()->GetAxis().ToAngles().ToVectors( &forward, &right, &up );
- idVec3 offsetPosition = vec3_zero;
- offsetPosition += (forward * offset.x) + (right * offset.y) + (up * offset.z);
- //check LOS.
- trace_t tr;
- gameLocal.clip.TracePoint( tr, selfPos, ent->GetPhysics()->GetOrigin() + offsetPosition, CONTENTS_OPAQUE, this );
- if (tr.fraction < 0)
- {
- //blocked.
- return false;
- }
- idVec3 movedir = (ent->GetPhysics()->GetOrigin() + offsetPosition) - selfPos;
- movedir.Normalize();
-
- int i;
- idVec3 hitpoint = selfPos;
- int maxGlassPanes = 32;
- for (i = 0; i < maxGlassPanes; i++)
- {
- trace_t paneTr;
- idVec3 startTrace = hitpoint + (movedir * 1.0f);
- gameLocal.clip.TracePoint( paneTr, startTrace, startTrace + (movedir * 4096), MASK_SOLID | MASK_SHOT_RENDERMODEL | MASK_SHOT_BOUNDINGBOX, this );
- //gameRenderWorld->DebugArrow(idVec4(1,1,0,1), startTrace, paneTr.endpos, 16, 5000);
-
- if ( paneTr.c.material != NULL )
- {
- surfTypes_t materialType = paneTr.c.material->GetSurfaceType();
- if (materialType == SURFTYPE_GLASS)
- {
- hitpoint = paneTr.endpos;
- continue;
- }
- }
- if (paneTr.c.entityNum == ent->entityNumber)
- {
- break;
- }
- //if (gameLocal.entities[paneTr.c.entityNum]->IsType(idDoor::Type) || gameLocal.entities[paneTr.c.entityNum]->IsType( idWorldspawn::Type ))
- {
- return false;
- }
- }
-
- //turret has a valid target. Start the gunfire sequence.
- if ( developer.GetBool() )
- {
- gameRenderWorld->DebugArrow(idVec4(1,0,0,1), selfPos, ent->GetPhysics()->GetOrigin() + offsetPosition, 16, 5000);
- }
- return true;
- }
- void idTurret::UpdateStates( void )
- {
- if (this->state == CLOSING)
- {
- idAnimatedEntity::Think();
- if (gameLocal.time > this->nextStateTime)
- {
- this->state = OFF;
- }
- return;
- }
- else if (this->state == ON)
- {
- jointHandle_t bodyJoint, turretJoint;
- idRotation bodyRotation, turretRotation;
- idVec3 playerPos;
- idVec3 turretPos;
- idMat3 turretAxis;
- bodyJoint = animator.GetJointHandle( "body" );
- turretJoint = animator.GetJointHandle( "turret" );
-
-
-
- //get yaw.
- if (targetEnt.IsValid())
- {
- playerPos = targetEnt.GetEntity()->GetPhysics()->GetOrigin();
- }
- else
- {
- playerPos = gameLocal.GetLocalPlayer()->GetEyePosition();
- playerPos[2] += PITCHADJUSTMENT; //aim a little below the eyes
- }
- idVec3 diff = playerPos - this->GetPhysics()->GetOrigin();
- float yaw = diff.ToYaw();
-
- //BC 9-14-2015 fix bad facing.
- yaw -= this->GetPhysics()->GetAxis().ToAngles().yaw;
- //get pitch.
- animator.GetJointTransform(turretJoint, gameLocal.time, turretPos, turretAxis);
- turretPos = this->GetPhysics()->GetOrigin() + turretPos * this->GetPhysics()->GetAxis();
- //turretPos = turretPos + this->GetPhysics()->GetOrigin();
- idVec3 pitchDiff = playerPos - turretPos;
- float pitch = pitchDiff.ToPitch();
- if (gameLocal.GetLocalPlayer()->health <= 0
- || gameLocal.GetLocalPlayer()->fl.notarget
- || gameLocal.GetLocalPlayer()->noclip
- || g_skill.GetInteger() <= 0)
- {
- }
- else
- {
- //rotate body.
- bodyRotation.SetVec( bodyAxis[1] );
- bodyRotation.SetAngle( yaw );
- animator.SetJointAxis( bodyJoint, JOINTMOD_WORLD, bodyRotation.ToMat3() );
- }
- if (this->attackState == IDLE)
- {
- //make beam idle around.
- idVec3 forward, up;
- idVec3 bodyPos;
- idVec3 facingAngle;
- idVec3 idlePos;
- bodyPos = this->GetPhysics()->GetOrigin();
- bodyPos.z = turretPos.z;
- facingAngle = turretPos - bodyPos;
- facingAngle.Normalize();
- facingAngle.ToAngles().ToVectors(&forward, NULL, &up);
- trace_t idleTr;
- idlePos = turretPos + (forward * 2048) + (up * -512);
- idlePos.x += idMath::Sin(gameLocal.time * 0.001f) * 16.0f;
- idlePos.z += idMath::Sin(gameLocal.time * 0.0005f) * 16.0f;
- gameLocal.clip.TracePoint( idleTr, turretPos, idlePos, CONTENTS_SOLID, this );
- this->beamEnd->SetOrigin(idleTr.endpos);
- if (this->nextStateTime > gameLocal.time)
- {
- return;
- }
- else
- {
- //show laser.
- this->beamStart->Show();
- this->laserdot->Show();
- //how often to search for targets.
- this->nextStateTime = gameLocal.time + 300;
- }
- if (this->nextIdleSound < gameLocal.time)
- {
- int rand = gameLocal.random.RandomInt(3);
- StartSound( "snd_idle", SND_CHANNEL_ANY, 0, false, NULL );
- if (rand == 0)
- {
- Event_PlayAnim("idle", 4);
- }
- else if (rand == 1)
- {
- Event_PlayAnim("idle2", 4);
- }
- else
- {
- Event_PlayAnim("idle3", 4);
- }
- this->nextIdleSound = gameLocal.time + IDLESOUNDDELAY + (gameLocal.random.RandomFloat() * IDLESOUNDRAND);
- }
- if (gameLocal.GetLocalPlayer()->health <= 0
- || gameLocal.GetLocalPlayer()->fl.notarget
- || gameLocal.GetLocalPlayer()->noclip
- || g_skill.GetInteger() <= 0)
- {
- return;
- }
- //search for things to shoot.
- //check maximum range.
- //player target check gets priority.
- if (CheckTargetValidity(gameLocal.GetLocalPlayer()))
- {
- targetEnt = NULL;
- GotoWarmupState();
- return;
- }
- //if no player
- //check if player threw an object.
- float maxDist = spawnArgs.GetInt("maxrange", "1024");
- int listedEntities;
- idEntity *entityList[ MAX_GENTITIES ];
- listedEntities = gameLocal.EntitiesWithinRadius( GetPhysics()->GetOrigin(), maxDist, entityList, MAX_GENTITIES );
- for (int i = 0; i < listedEntities; i++)
- {
- idEntity *ent = entityList[ i ];
- if (!ent)
- {
- continue;
- }
- if ( ent->IsType( idMoveableItem::Type ) || ent->IsType( idMoveable::Type ) )
- {
- if (ent->lastThrowTime + THROWTIME_THRESHOLD > gameLocal.time)
- {
- if (CheckTargetValidity(ent))
- {
- targetEnt = ent;
- GotoWarmupState();
- return;
- }
- }
- }
- if (ent->IsType(idWeevil::Type))
- {
- if (static_cast<idWeevil *>( ent)->isJumping())
- {
- targetEnt = ent;
- GotoWarmupState();
- return;
- }
- }
- }
- //no target found. oh well.
- }
- else if (this->attackState == WARMUP)
- {
- //update laser.
- trace_t tr;
- gameLocal.clip.TracePoint( tr, beamStart->GetPhysics()->GetOrigin(), playerPos, CONTENTS_SOLID, this );
- this->beamEnd->SetOrigin(tr.endpos);
- if (gameLocal.time > nextStateTime)
- {
- this->attackState = VOLLEYING;
- this->nextStateTime = 0;
- }
- }
- else if (this->attackState == VOLLEYING)
- {
- //update laser.
- trace_t tr;
- gameLocal.clip.TracePoint( tr, beamStart->GetPhysics()->GetOrigin(), playerPos, CONTENTS_SOLID, this );
- this->beamEnd->SetOrigin(tr.endpos);
- if (this->volleyCount >= MAXVOLLEY)
- {
- this->attackState = IDLE;
- this->nextStateTime = 0;
- return;
- }
- if (this->nextStateTime < gameLocal.time)
- {
- const idDict * projectileDef;
- idEntity * ent;
- idVec3 bulletVelocity;
- pitchDiff.Normalize();
- //FIRE.
- idProjectile *bullet;
- projectileDef = gameLocal.FindEntityDefDict( spawnArgs.GetString("projectile", "projectile_turretbullet"), false );
- bulletVelocity = projectileDef->GetVector("velocity");
- gameLocal.SpawnEntityDef( *projectileDef, &ent, false );
- bullet = ( idProjectile * )ent;
- bullet->Create( this, turretPos, pitchDiff );
- bullet->Launch( turretPos, pitchDiff, pitchDiff * bulletVelocity.x );
- //muzzle flash skin.
- SetSkin(declManager->FindSkin( "skins/turret/firing" ));
- PostEventSec( &EV_Turret_muzzleflashoff, MUZZLEFLASHTIME);
- //play animation.
- Event_PlayAnim("fire", 4);
- //play sound.
- StartSound( "snd_fire", SND_CHANNEL_ANY, 0, false, NULL );
- //keep track of fire count.
- this->volleyCount++;
- this->nextStateTime = gameLocal.time + VOLLEYDELAY;
- return;
- }
- }
- }
- }
- void idTurret::Think( void )
- {
- this->RunPhysics();
- UpdateStates();
-
- idAnimatedEntity::Think();
- Present();
-
- }
- /*
- ================
- idSecurityCamera::Present
- Present is called to allow entities to generate refEntities, lights, etc for the renderer.
- ================
- */
- void idTurret::Present( void )
- {
- // don't present to the renderer if the entity hasn't changed
- if ( !( thinkFlags & TH_UPDATEVISUALS ) )
- {
- return;
- }
- BecomeInactive( TH_UPDATEVISUALS );
-
- // if set to invisible, skip
- if ( !renderEntity.hModel || IsHidden() )
- {
- return;
- }
- // add to refresh list
- if ( modelDefHandle == -1 )
- {
- modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
- }
- else
- {
- gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );
- }
- }
|