Script:Files:script/vehicles/vampire.script
From Mod Wiki
#define VAMPIRE_FLY_HEIGHT 2048.f #define VAMPIRE_BOMB_GRAVITY -6000.f object projectile_vampire : projectile_missile { void init() { if ( sys.isClient() ) { hide(); } } void Idle() { if ( sys.isClient() ) { hide(); } } void SetupContents() { SetupContents_Base(); setContents( 0 ); } } object projectile_vampire_client { float expireTime; void init() { setState( "Idle" ); } void Idle() { while ( true ) { if ( sys.getTime() > expireTime ) { remove(); } sys.waitFrame(); } } } object vehicle_vampire { void preinit(); void init(); void destroy(); void syncFields(); void vBomberAttack( vector target, vector attackDir, float attackHeight, entity attacker ); void Update(); void CreateFiringMarker(); void ClearFiringMarker(); float OnUpdateCrosshairInfo( entity p ); void Idle(); vector lastVelocity; vector GetLastVelocity() { return lastVelocity; } void SetupCommandMapIcons(); void FreeCommandMapIcons(); // flight information float flightStartTime; vector runStart; vector runEnd; vector targetBase; float flightTime; vector bombHitStart; // state entity firingMarker; entity owner; boolean hasPath; // tuning properties float bomberSpeed; // sound float flyOverSoundRange; boolean playedFlyBySound; boolean playingFlySound; // bombing float bombCount; float bombDelay; float bombSpread; float bombUpto; boolean hasBombInfo; float bombPosition; vector bombAcceleration; vector bombStart; vector bombStartDir; float bombFallTime; // marker models void CreateDirectionMarkers( vector basePosition, vector startPosition, vector endPosition ); void FadeDirectionMarkers( float duration, float start, float end ); void RemoveDirectionMarkers(); void PulseDirectionMarkers( float currentTime ); void DirectionMarkerThread(); boolean directionMarkerDrawn; boolean directionMarkerFaded; float directionMarkerStartTime; direction_marker directionMarker0; direction_marker directionMarker1; direction_marker directionMarker2; direction_marker directionMarker3; direction_marker directionMarker4; direction_marker directionMarker5; direction_marker directionMarker6; direction_marker directionMarker7; direction_marker directionMarker8; direction_marker directionMarker9; direction_marker directionMarker10; direction_marker directionMarker11; direction_marker directionMarker12; direction_marker directionMarker13; direction_marker directionMarker14; direction_marker directionMarker15; direction_marker directionMarker16; direction_marker directionMarker17; direction_marker directionMarker18; direction_marker directionMarker19; float cmIcon; }; void vehicle_vampire::syncFields() { syncBroadcast( "owner" ); syncBroadcast( "flightStartTime" ); syncBroadcast( "runStart" ); syncBroadcast( "runEnd" ); syncBroadcast( "targetBase" ); syncBroadcast( "flightTime" ); syncBroadcast( "bombHitStart" ); } void vehicle_vampire::preinit() { flightStartTime = -1; playedFlyBySound = false; playingFlySound = false; flyOverSoundRange = getFloatKey( "flyover_sndrange" ); flyOverSoundRange = flyOverSoundRange * flyOverSoundRange; bombCount = getFloatKey( "bomb_count" ); if ( bombCount > 10 ) { sys.error( "too many bombs!" ); } bombDelay = getFloatKey( "bomb_delay" ); bombSpread = getFloatKey( "bomb_spread" ); bomberSpeed = getFloatKey( "speed" ); cmIcon = -1; setCoverage( 0.f ); hide(); SetupCommandMapIcons(); } void vehicle_vampire::init() { setContents( 0 ); setState( "Idle" ); } void vehicle_vampire::Idle() { while( true ) { sys.waitFrame(); Update(); } } void vehicle_vampire::vBomberAttack( vector target, vector attackDir, float attackHeight, entity attacker ) { targetBase = target; owner = attacker; setGameTeam( attacker.getGameTeam() ); float arrivalDelay = getFloatKey( "arrivaldelay" ); float arrivalDelayRandom = getFloatKey( "arrivaldelay_random" ); if( arrivalDelayRandom < arrivalDelay ) { arrivalDelayRandom = arrivalDelay; } arrivalDelay = arrivalDelay + sys.random( arrivalDelayRandom ); // offset so center of bombs arrive at target point float targetPosOffset = ( 0.5f * bombCount ) * ( bombDelay * bomberSpeed ); runStart = targetBase + attackDir * targetPosOffset; runEnd = targetBase - attackDir * targetPosOffset; runStart_z = pathFindVampire( runStart, runEnd, VAMPIRE_FLY_HEIGHT ); runEnd_z = runStart_z; flightStartTime = sys.getTime() + arrivalDelay; flightTime = pathGetLength() / bomberSpeed; bombHitStart = runStart; bombHitStart_z = targetBase_z; float frac = sys.heightMapTrace( bombHitStart, bombHitStart - '0 0 20000' ); bombHitStart_z = bombHitStart_z - 20000 * frac; targetBase_z = attackHeight; CreateFiringMarker(); } void vehicle_vampire::Update() { float currentTime = sys.getTime(); if ( flightStartTime < 0 || currentTime < flightStartTime ) { return; } vector origin; vector angles; float curFlightTime = sys.getTime() - flightStartTime; float position = curFlightTime * bomberSpeed; if ( !hasPath && sys.isClient() ) { // got through to here so the necessary stuff for the path must be in pathFindVampire( runStart, runEnd, VAMPIRE_FLY_HEIGHT ); hasPath = true; } show(); if( !playingFlySound ) { playingFlySound = true; fadeSound( SND_VEHICLE_IDLE, -60.f, 0.f ); startSound( "snd_flyby_far", SND_VEHICLE_IDLE ); fadeSound( SND_VEHICLE_IDLE, 0.f, 2.f ); } float frac = ( curFlightTime ) / flightTime; setCoverage( 1.f ); if ( curFlightTime < 0.15f ) { setCoverage( curFlightTime / 0.15f ); } if ( curFlightTime > ( flightTime - 0.15f ) ) { setCoverage( (flightTime - curFlightTime) / 0.15f ); } if ( frac >= 1.0 ) { if( !sys.isClient() ) { remove(); } return; } CreateDirectionMarkers( targetBase, runStart, runEnd ); if ( !directionMarkerFaded ) { if ( frac > 0.6f ) { directionMarkerFaded = true; FadeDirectionMarkers( 0.5f, 1.0f, 0.0f ); } } origin = pathGetPosition( position ); angles = pathGetAngles( position ); // limit the roll rate so it doesn't look too unnatural vector oldAngles = getAngles(); float rollDiff = angles_z - oldAngles_z; rollDiff = sys.angleNormalize180( rollDiff ); float frameTime = sys.getFrameTime(); float rotationSpeed = rollDiff / frameTime; if ( rotationSpeed > 20.0f ) { rotationSpeed = 20.0f; } if ( rotationSpeed < -20.0f ) { rotationSpeed = -20.0f; } angles_z = oldAngles_z + rotationSpeed * frameTime; vector oldOrigin = getWorldOrigin(); setOrigin( origin ); setAngles( angles ); vector dir = getWorldAxis( 0 ); // check that any bombs that need to be dropped have been while ( bombUpto < bombCount ) { if ( !hasBombInfo ) { vector targetDir = runEnd - runStart; targetDir_z = 0.0f; targetDir = sys.vecNormalize( targetDir ); vector bombHitDelta = targetDir * ( bombDelay * bomberSpeed ); vector targetPos = bombHitStart + bombHitDelta * bombUpto; float pathLength = flightTime * bomberSpeed; targetPos_z -= 512.0f; vector traceStart = targetPos; traceStart_z += VAMPIRE_FLY_HEIGHT; sys.tracePoint( traceStart, targetPos, MASK_SHOT_BOUNDINGBOX, $null_entity ); targetPos = sys.getTraceEndPos(); bombPosition = getVampireBombPosition( targetPos, VAMPIRE_BOMB_GRAVITY, bomberSpeed, bomberSpeed / 30.0f, pathLength ); bombAcceleration = getVampireBombAcceleration(); bombStart = pathGetPosition( bombPosition ); bombStartDir = pathGetDirection( bombPosition ); bombFallTime = getVampireBombFallTime(); hasBombInfo = true; } if ( position < bombPosition ) { // no need to drop this bomb yet break; } // time to drop this bomb // how much time has passed since it was meant to be dropped? float timeSinceDrop = ( position - bombPosition ) / bomberSpeed; // adjust the start position & velocity to suit vector bombVelocity = bombStartDir * bomberSpeed; bombStart = bombStart + ( bombVelocity*timeSinceDrop ) + ( bombAcceleration*( 0.5f*timeSinceDrop*timeSinceDrop ) ); bombVelocity = bombVelocity + ( bombAcceleration*timeSinceDrop ); entity projectile; if ( !sys.isClient() ) { projectile = sys.spawn( getKey( "def_bomb_type" ) ); } else { projectile = sys.spawnClient( getKey( "def_bomb_type_client" ) ); projectile_vampire_client clientProj = projectile; if ( clientProj != $null_entity ) { clientProj.expireTime = sys.getTime() - timeSinceDrop + bombFallTime; } } projectile.setOwner( owner ); projectile.addOwner( self ); projectile.setGameTeam( getGameTeam() ); projectile.setOrigin( bombStart ); projectile.setAngles( sys.vecToAngles( bombStartDir ) ); projectile.setGravity( bombAcceleration ); projectile.launch( bombVelocity ); bombUpto = bombUpto + 1; hasBombInfo = false; } // check distance to local player if ( !playedFlyBySound ) { entity p = sys.getLocalPlayer(); if ( p != $null_entity ) { vector org = p.getWorldOrigin(); org -= getWorldOrigin(); org_z = 0.f; if ( sys.vecLengthSquared( org ) < flyOverSoundRange ) { sys.startSoundDirect( getKey( "snd_flyby" ), SND_VEHICLE_DRIVE ); playedFlyBySound = true; } } } } float vehicle_vampire::OnUpdateCrosshairInfo( entity p ) { if ( sys.getLocalPlayer() == $null_entity ) { return 1.f; } float allegiance = getEntityAllegiance( p ); vector color = GetAllegianceColor( allegiance ); string title; float range = InchesToMetres( sys.vecLength( getWorldOrigin() - p.getWorldOrigin() ) ); chSetNumLines( 2 ); if( allegiance == TA_FRIEND ) { title = "Friendly "; } else if( allegiance == TA_ENEMY ) { title = "Enemy "; } else { title = "Neutral "; } chSetLineTextIndex( 0, g_locStr_Airstrike ); chSetLineColor( 0, color, 1.f ); chSetLineType( 0, CI_TEXT ); chSetLineSize( 0, 0, 0 ); chSetLineColor( 1, color, 0.5f ); chSetLineType( 1, CI_BAR ); chSetLineFraction( 1, getHealth() / getMaxHealth() ); chSetLineSize( 1, 150, CROSSHAIR_INFO_BAR_HEIGHT ); return 1.f; } void vehicle_vampire::destroy() { ClearFiringMarker(); FreeCommandMapIcons(); RemoveDirectionMarkers(); } void vehicle_vampire::CreateFiringMarker() { if ( !sys.isClient() ) { firingMarker = G_CreateFiringMarker( self, firingMarker, targetBase ); } } void vehicle_vampire::ClearFiringMarker() { if ( firingMarker != $null_entity ) { thread G_DelayRemoveEntity( firingMarker, 5.f ); firingMarker = $null_entity; } } void vehicle_vampire::SetupCommandMapIcons() { FreeCommandMapIcons(); cmIcon = sys.allocCMIcon( self, getFloatKey( "mtr_commandmap_sort" ) ); sys.setCMIconMaterial( cmIcon, GetMaterial( getKey( "mtr_commandmap" ) ) ); sys.setCMIconUnknownMaterial( cmIcon, GetMaterial( getKey( "mtr_commandmap_unknown" ) ) ); sys.setCMIconDrawMode( cmIcon, DM_ROTATED_MATERIAL ); sys.setCMIconSize( cmIcon, 16.0f ); sys.setCMIconUnknownSize( cmIcon, 12 ); sys.setCMIconFlag( cmIcon, CMF_FOLLOWROTATION ); sys.setCMIconColorMode( cmIcon, CM_ALLEGIANCE ); } void vehicle_vampire::FreeCommandMapIcons() { if ( cmIcon != -1 ) { sys.freeCMIcon( self, cmIcon ); cmIcon = -1; } } void vehicle_vampire::CreateDirectionMarkers( vector basePosition, vector startPosition, vector endPosition ) { if ( directionMarkerDrawn ) { return; } directionMarkerDrawn = true; directionMarkerStartTime = sys.getTime(); entity p = sys.getLocalViewPlayer(); if ( p == $null_entity ) { return; } if ( getEntityAllegiance( p ) != TA_FRIEND ) { return; } string entityDef = getKey( "def_directionmarker" ); if ( entityDef == "" ) { return; } vector targetDir = endPosition - startPosition; targetDir = sys.vecNormalize( targetDir ); vector half; float halfLength; basePosition_z = startPosition_z; // calculate marker start position half = basePosition - startPosition; halfLength = min( sys.vecLength( half ), 2048.0f ); vector markerStart = basePosition - halfLength * targetDir; // calculate marker end position half = endPosition - basePosition; halfLength = min( sys.vecLength( half ), 2048.0f ); vector markerEnd = basePosition + halfLength * targetDir; float markerLength = sys.vecLength( markerEnd - markerStart ); float numMarkers = rint( markerLength / 256.0f ) + 1.0f; vector targetStep = targetDir * ( markerLength / numMarkers ); vector targetPos = markerStart; // spawn direction markers if ( numMarkers > 0.0f ) { directionMarker0 = new direction_marker; directionMarker0.Create( 0, entityDef, targetPos, targetDir ); } #define CREATE_DIRECTION_MARKER( index ) \ if ( numMarkers > (index) ) { \ targetPos += targetStep; \ directionMarker##index = new direction_marker; \ directionMarker##index.Create( (index), entityDef, targetPos, targetDir ); \ } CREATE_DIRECTION_MARKER( 1 ) CREATE_DIRECTION_MARKER( 2 ) CREATE_DIRECTION_MARKER( 3 ) CREATE_DIRECTION_MARKER( 4 ) CREATE_DIRECTION_MARKER( 5 ) CREATE_DIRECTION_MARKER( 6 ) CREATE_DIRECTION_MARKER( 7 ) CREATE_DIRECTION_MARKER( 8 ) CREATE_DIRECTION_MARKER( 9 ) CREATE_DIRECTION_MARKER( 10 ) CREATE_DIRECTION_MARKER( 11 ) CREATE_DIRECTION_MARKER( 12 ) CREATE_DIRECTION_MARKER( 13 ) CREATE_DIRECTION_MARKER( 14 ) CREATE_DIRECTION_MARKER( 15 ) CREATE_DIRECTION_MARKER( 16 ) CREATE_DIRECTION_MARKER( 17 ) CREATE_DIRECTION_MARKER( 18 ) CREATE_DIRECTION_MARKER( 19 ) #undef CREATE_DIRECTION_MARKER FadeDirectionMarkers( 0.3f, 0.0f, 1.0f ); PulseDirectionMarkers( 0.0f ); // force an update thread DirectionMarkerThread(); } void vehicle_vampire::FadeDirectionMarkers( float duration, float start, float end ) { if ( !directionMarkerDrawn ) { return; } float currentTime = sys.getTime() - directionMarkerStartTime; #define FADE_DIRECTION_MARKER( index ) \ if ( directionMarker##index != $null ) { \ directionMarker##index.Fade( currentTime, duration, start, end ); \ } FADE_DIRECTION_MARKER( 0 ) FADE_DIRECTION_MARKER( 1 ) FADE_DIRECTION_MARKER( 2 ) FADE_DIRECTION_MARKER( 3 ) FADE_DIRECTION_MARKER( 4 ) FADE_DIRECTION_MARKER( 5 ) FADE_DIRECTION_MARKER( 6 ) FADE_DIRECTION_MARKER( 7 ) FADE_DIRECTION_MARKER( 8 ) FADE_DIRECTION_MARKER( 9 ) FADE_DIRECTION_MARKER( 10 ) FADE_DIRECTION_MARKER( 11 ) FADE_DIRECTION_MARKER( 12 ) FADE_DIRECTION_MARKER( 13 ) FADE_DIRECTION_MARKER( 14 ) FADE_DIRECTION_MARKER( 15 ) FADE_DIRECTION_MARKER( 16 ) FADE_DIRECTION_MARKER( 17 ) FADE_DIRECTION_MARKER( 18 ) FADE_DIRECTION_MARKER( 19 ) #undef FADE_DIRECTION_MARKER } void vehicle_vampire::RemoveDirectionMarkers() { sys.killThread( "DirectionMarkerThread_" + getName() ); directionMarkerDrawn = false; #define DELETE_DIRECTION_MARKER( index ) \ if ( directionMarker##index != $null ) { \ delete directionMarker##index; \ directionMarker##index = $null; \ } DELETE_DIRECTION_MARKER( 0 ) DELETE_DIRECTION_MARKER( 1 ) DELETE_DIRECTION_MARKER( 2 ) DELETE_DIRECTION_MARKER( 3 ) DELETE_DIRECTION_MARKER( 4 ) DELETE_DIRECTION_MARKER( 5 ) DELETE_DIRECTION_MARKER( 6 ) DELETE_DIRECTION_MARKER( 7 ) DELETE_DIRECTION_MARKER( 8 ) DELETE_DIRECTION_MARKER( 9 ) DELETE_DIRECTION_MARKER( 10 ) DELETE_DIRECTION_MARKER( 11 ) DELETE_DIRECTION_MARKER( 12 ) DELETE_DIRECTION_MARKER( 13 ) DELETE_DIRECTION_MARKER( 14 ) DELETE_DIRECTION_MARKER( 15 ) DELETE_DIRECTION_MARKER( 16 ) DELETE_DIRECTION_MARKER( 17 ) DELETE_DIRECTION_MARKER( 18 ) DELETE_DIRECTION_MARKER( 19 ) #undef DELETE_DIRECTION_MARKER } void vehicle_vampire::PulseDirectionMarkers( float currentTime ) { if ( !directionMarkerDrawn ) { return; } float pulseTime = currentTime - directionMarkerStartTime; #define UPDATE_DIRECTION_MARKER( index ) \ if ( directionMarker##index != $null ) { \ directionMarker##index.Update( pulseTime ); \ } UPDATE_DIRECTION_MARKER( 0 ) UPDATE_DIRECTION_MARKER( 1 ) UPDATE_DIRECTION_MARKER( 2 ) UPDATE_DIRECTION_MARKER( 3 ) UPDATE_DIRECTION_MARKER( 4 ) UPDATE_DIRECTION_MARKER( 5 ) UPDATE_DIRECTION_MARKER( 6 ) UPDATE_DIRECTION_MARKER( 7 ) UPDATE_DIRECTION_MARKER( 8 ) UPDATE_DIRECTION_MARKER( 9 ) UPDATE_DIRECTION_MARKER( 10 ) UPDATE_DIRECTION_MARKER( 11 ) UPDATE_DIRECTION_MARKER( 12 ) UPDATE_DIRECTION_MARKER( 13 ) UPDATE_DIRECTION_MARKER( 14 ) UPDATE_DIRECTION_MARKER( 15 ) UPDATE_DIRECTION_MARKER( 16 ) UPDATE_DIRECTION_MARKER( 17 ) UPDATE_DIRECTION_MARKER( 18 ) UPDATE_DIRECTION_MARKER( 19 ) #undef UPDATE_DIRECTION_MARKER } void vehicle_vampire::DirectionMarkerThread() { while ( true ) { if ( directionMarkerDrawn ) { float currentTime = sys.getTime(); PulseDirectionMarkers( currentTime ); } sys.waitFrame(); } }