object vehicle_violator { void preinit(); void init(); void destroy(); void syncFields(); void vBomberAttack( vector target, vector attackDir, float attackHeight, entity attacker ); void Update(); void CreateFiringMarker(); void ClearFiringMarker(); void BeamSounds(); void Idle(); vector startPos; vector beamDir; float beamStartTime; float beamDuration; float beamStartupDuration; float beamCoreStartupDuration; float beamFizzleDuration; float beamEffectFizzleDuration; float lastGroundEffectsTime; float beamRotationSpeed; entity owner; float movementTableIndex; boolean beamSoundsThread; object beamStartEffect; boolean beamStartEffectPlaying; object beamEffect; boolean beamEffectPlaying; vector targetBase; entity firingMarker; float splashDamage; float beamAreaLength; // marker models void CreateDirectionMarkers( 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; } void vehicle_violator::syncFields() { syncBroadcast( "startPos" ); syncBroadcast( "beamDir" ); syncBroadcast( "beamStartTime" ); syncBroadcast( "owner" ); } void vehicle_violator::preinit() { beamStartTime = -1; lastGroundEffectsTime = -1.f; beamStartupDuration = getFloatKey( "beam_startup_duration" ); beamCoreStartupDuration = getFloatKey( "beam_core_startup_duration" ); beamDuration = getFloatKey( "beam_duration" ); beamFizzleDuration = getFloatKey( "beam_fizzle_duration" ); beamEffectFizzleDuration = getFloatKey( "beam_effect_fizzle_duration" ); beamRotationSpeed = getFloatKey( "beam_rotation_speed" ); splashDamage = GetDamage( "damage_violator_splash" ); beamStartEffectPlaying = false; beamEffectPlaying = false; beamAreaLength = getFloatKey( "beam_area_length" ); movementTableIndex = GetTable( "exp_x2" ); } void vehicle_violator::init() { hide(); setState( "Idle" ); } void vehicle_violator::Idle() { while( true ) { sys.waitFrame(); Update(); } } void vehicle_violator::vBomberAttack( vector target, vector attackDir, float attackHeight, entity attacker ) { float arrivalDelay; float arrivalDelayRandom; vector dir; vector endPos; targetBase = target; targetBase_z = attackHeight; owner = attacker; setGameTeam( attacker.getGameTeam() ); arrivalDelay = getFloatKey( "arrivaldelay" ); arrivalDelayRandom = getFloatKey( "arrivaldelay_random" ); if( arrivalDelayRandom < arrivalDelay ) { arrivalDelayRandom = arrivalDelay; } arrivalDelay = arrivalDelay + sys.random( arrivalDelayRandom ); beamDuration = getFloatKey( "beam_duration" ); dir = sys.vecNormalize( attackDir ); startPos = target - dir * 0.5f * beamAreaLength; sys.tracePoint( target, startPos, MASK_SOLID | MASK_OPAQUE, $null_entity ); startPos = sys.getTraceEndPos(); endPos = target + dir * beamAreaLength; sys.tracePoint( target, endPos, MASK_SOLID | MASK_OPAQUE, $null_entity ); endPos = sys.getTraceEndPos(); // set proper flight height endPos_z = attackHeight + 3072.f; startPos_z = attackHeight + 3072.f; beamDir = endPos - startPos; beamStartTime = sys.getTime() + arrivalDelay + beamStartupDuration; // initalise position setOrigin( startPos ); CreateFiringMarker(); } void vehicle_violator::Update() { float currentTime = sys.getTime(); float frac; vector currentPos; vector angles; object traceObject; vector traceEndPos; vector traceNormal; entity collisionEnt; if ( beamStartTime < 0 || currentTime < beamStartTime - beamStartupDuration ) { return; } if ( currentTime < beamStartTime ) { frac = 0.0f; } else { frac = ( currentTime - beamStartTime ) / beamDuration; if ( !directionMarkerFaded ) { if ( frac > 0.6f ) { directionMarkerFaded = true; FadeDirectionMarkers( 0.5f, 1.0f, 0.0f ); } } if ( frac >= 1.0f ) { if ( currentTime - beamStartTime - beamDuration > 6 ) { // give it some time for the particles to die if ( beamStartEffect != $null_entity ) { beamStartEffect.remove(); beamStartEffect = $null_entity; } if ( beamEffect != $null_entity ) { beamEffect.remove(); beamEffect = $null_entity; } if ( !sys.isClient() ) { remove(); } return; } } } if ( frac < 1.0f ) { CreateDirectionMarkers( startPos, startPos + beamDir ); frac = sys.getTableValueExact( movementTableIndex, frac ); } currentPos = startPos + ( beamDir * frac ); setOrigin( currentPos ); sys.tracePoint( currentPos, currentPos + ( vec3_down * MAX_WORLD_SIZE ), MASK_SHOT_RENDERMODEL, $null_entity ); string surfaceTypeName = sys.getTraceSurfaceType(); traceEndPos = sys.getTraceEndPos(); traceNormal = sys.getTraceNormal(); collisionEnt = sys.getTraceEntity(); if( !beamSoundsThread ) { beamSoundsThread = true; thread BeamSounds(); } // beam visualisation if ( currentTime < beamStartTime ) { // beam start effect if ( beamStartEffect == $null_entity ) { beamStartEffect = spawnClientEffect( "fx_beam_start" ); if ( beamStartEffect != $null_entity ) { beamStartEffectPlaying = true; } } if ( currentTime > ( beamStartTime - beamCoreStartupDuration ) ) { if ( beamEffect == $null_entity ) { beamEffect = spawnClientEffect( "fx_beam" ); if ( beamEffect != $null_entity ) { beamEffectPlaying = true; beamEffect.setEffectLooping( 1 ); } } } } else { if ( currentTime > ( beamStartTime + beamDuration - beamEffectFizzleDuration ) ) { // Xian Hack if ( beamEffectPlaying ) { beamEffect.endEffect( false ); beamEffectPlaying = false; } } else { if ( beamStartEffectPlaying ) { beamStartEffect.endEffect( false ); beamStartEffectPlaying = false; } /* if ( beamEffect == $null_entity ) { beamEffect = spawnClientEffect( "fx_beam" ); beamEffect.setEffectLooping( 1 ); beamEffect.setAngles( sys.vecToAngles( vec3_up ) ); }*/ } } if ( beamStartEffect != $null_entity ) { beamStartEffect.setOrigin( traceEndPos ); float acceleration = beamRotationSpeed / beamStartupDuration; float time = currentTime - ( beamStartTime - beamStartupDuration ); angles_x = -90; angles_y = 0.5 * acceleration * time * time; angles_z = 0; beamStartEffect.setAngles( angles ); } if ( beamEffect != $null_entity ) { beamEffect.setOrigin( traceEndPos ); angles_x = -90; angles_y = beamRotationSpeed * currentTime; angles_z = 0; beamEffect.setAngles( angles ); if ( sys.getTime() >= ( lastGroundEffectsTime + 0.3f ) ) { sys.tracePoint( currentPos, currentPos + ( vec3_down * MAX_WORLD_SIZE ), MASK_SHOT_RENDERMODEL|CONTENTS_WATER, $null_entity ); string fxsurfaceTypeName = sys.getTraceSurfaceType(); vector fxtraceEndPos = sys.getTraceEndPos(); vector fxtraceNormal = sys.getTraceNormal(); float beamTime = beamStartTime + ( beamDuration + 3.5 ) - beamFizzleDuration; // Xian Hack if ( currentTime < beamTime ) { playOriginEffect( "fx_groundeffect", fxsurfaceTypeName, fxtraceEndPos, fxtraceNormal, 0 ); lastGroundEffectsTime = sys.getTime(); } } } if ( !sys.isClient() ) { float damageScale; float fizzleStartTime = beamStartTime + beamDuration - beamFizzleDuration; if ( currentTime < beamStartTime ) { damageScale = 0.0f; } else if ( currentTime > fizzleStartTime ) { damageScale = 1.0f - ( ( currentTime - fizzleStartTime ) / beamFizzleDuration ); } else { damageScale = 1.0f; } if ( damageScale > 0.0f ) { if ( collisionEnt != $null_entity ) { collisionEnt.applyDamage( self, owner, vec3_down, GetDamage( "damage_violator" ), damageScale, traceObject ); } sys.applyRadiusDamage( traceEndPos + ( vec3_up * 64.0f ), self, owner, $null_entity, self, splashDamage, damageScale, 1.f ); } } } void vehicle_violator::destroy() { ClearFiringMarker(); if ( beamStartEffect != $null_entity ) { beamStartEffect.remove(); } if ( beamEffect != $null_entity ) { beamEffect.remove(); } RemoveDirectionMarkers(); } void vehicle_violator::BeamSounds() { boolean playingBeamSound; float beamTime = sys.getTime() + ( beamDuration + 3.5 ) - beamFizzleDuration; // Xian Hack while ( sys.getTime() < beamTime ) { sys.waitFrame(); if( !playingBeamSound ) { playingBeamSound = true; startSound( "snd_beam_start", SND_VEHICLE ); } } startSound( "snd_beam_stop", SND_VEHICLE ); } void vehicle_violator::CreateFiringMarker() { if ( !sys.isClient() ) { firingMarker = G_CreateFiringMarker( self, firingMarker, targetBase ); } } void vehicle_violator::ClearFiringMarker() { if ( firingMarker != $null_entity ) { thread G_DelayRemoveEntity( firingMarker, 5.f ); firingMarker = $null_entity; } } void vehicle_violator::CreateDirectionMarkers( 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; vector basePosition = startPosition + 0.5f * targetDir; 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_violator::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_violator::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_violator::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_violator::DirectionMarkerThread() { while ( true ) { if ( directionMarkerDrawn ) { float currentTime = sys.getTime(); PulseDirectionMarkers( currentTime ); } sys.waitFrame(); } }