405 lines
15 KiB
Plaintext
405 lines
15 KiB
Plaintext
#using scripts\codescripts\struct;
|
|
|
|
#using scripts\shared\callbacks_shared;
|
|
#using scripts\shared\entityheadicons_shared;
|
|
#using scripts\shared\system_shared;
|
|
#using scripts\shared\util_shared;
|
|
#using scripts\shared\weapons\_weaponobjects;
|
|
|
|
|
|
|
|
#namespace decoy;
|
|
|
|
function init_shared()
|
|
{
|
|
level.decoyWeapons = [];
|
|
level.decoyWeapons["fullauto"] = [];
|
|
level.decoyWeapons["semiauto"] = [];
|
|
|
|
level.decoyWeapons["fullauto"][level.decoyWeapons["fullauto"].size] = GetWeapon( "ar_accurate" ); // BO2: "uzi"
|
|
|
|
level.decoyWeapons["semiauto"][level.decoyWeapons["semiauto"].size] = GetWeapon( "pistol_standard" ); // BO2: "m1911"
|
|
|
|
callback::add_weapon_watcher( &create_watcher );
|
|
}
|
|
|
|
function create_watcher()
|
|
{
|
|
watcher = self weaponobjects::createUseWeaponObjectWatcher( "nightingale", self.team );
|
|
watcher.onSpawn =&on_spawn;
|
|
watcher.onDetonateCallback =&detonate;
|
|
watcher.deleteOnDifferentObjectSpawn = false;
|
|
watcher.headicon = false;
|
|
}
|
|
|
|
function on_spawn(watcher, owner)
|
|
{
|
|
owner endon("disconnect");
|
|
self endon( "death" );
|
|
|
|
weaponobjects::onSpawnUseWeaponObject(watcher, owner);
|
|
|
|
self.initial_velocity = self GetVelocity();
|
|
delay = 1;
|
|
|
|
wait (delay );
|
|
decoy_time = 30;
|
|
spawn_time = GetTime();
|
|
|
|
owner AddWeaponStat( self.weapon, "used", 1 );
|
|
|
|
self thread simulate_weapon_fire(owner);
|
|
|
|
while( 1 )
|
|
{
|
|
if ( GetTime() > spawn_time + ( decoy_time * 1000 ))
|
|
{
|
|
self destroy(watcher,owner);
|
|
return;
|
|
}
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
function move( owner, count, fire_time, main_dir, max_offset_angle )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "done" );
|
|
|
|
if ( !(self IsOnGround() ) )
|
|
return;
|
|
|
|
min_speed = 100;
|
|
max_speed = 200;
|
|
|
|
min_up_speed = 100;
|
|
max_up_speed = 200;
|
|
|
|
current_main_dir = RandomIntRange(main_dir - max_offset_angle,main_dir + max_offset_angle);
|
|
|
|
avel = ( RandomFloatRange( 800, 1800) * (RandomIntRange( 0, 2 ) * 2 - 1), 0, RandomFloatRange( 580, 940) * (RandomIntRange( 0, 2 ) * 2 - 1));
|
|
|
|
intial_up = RandomFloatRange( min_up_speed, max_up_speed );
|
|
|
|
start_time = GetTime();
|
|
gravity = GetDvarint( "bg_gravity" );
|
|
|
|
// PrintLn( "start time " + start_time );
|
|
for ( i = 0; i < 1; i++ )
|
|
{
|
|
angles = ( 0,RandomIntRange(current_main_dir - max_offset_angle,current_main_dir + max_offset_angle), 0 );
|
|
dir = AnglesToForward( angles );
|
|
|
|
dir = VectorScale( dir, RandomFloatRange( min_speed, max_speed ) );
|
|
|
|
deltaTime = ( GetTime() - start_time ) * 0.001;
|
|
|
|
// must manually manage the gravity because of the way the Launch function and the tr interpolater work
|
|
up = (0,0, (intial_up) - (800 * deltaTime) );
|
|
|
|
self launch( dir + up, avel );
|
|
|
|
wait( fire_time );
|
|
}
|
|
// PrintLn( "end time " + GetTime() );
|
|
}
|
|
|
|
function destroy(watcher,owner)
|
|
{
|
|
self notify( "done" );
|
|
self entityheadicons::setEntityHeadIcon("none");
|
|
|
|
// play deactivated particle effect here
|
|
}
|
|
|
|
function detonate( attacker, weapon, target )
|
|
{
|
|
// NOTE: this isn't being called currently through the watcher system
|
|
self notify( "done" );
|
|
self entityheadicons::setEntityHeadIcon("none");
|
|
}
|
|
|
|
function simulate_weapon_fire( owner )
|
|
{
|
|
owner endon("disconnect");
|
|
self endon( "death" );
|
|
self endon( "done" );
|
|
|
|
weapon = pick_random_weapon();
|
|
|
|
self thread watch_for_explosion( owner, weapon );
|
|
self thread track_main_direction();
|
|
|
|
self.max_offset_angle = 30;
|
|
|
|
weapon_class = util::getWeaponClass( weapon );
|
|
|
|
switch ( weapon_class )
|
|
{
|
|
case "weapon_cqb":
|
|
case "weapon_smg":
|
|
case "weapon_hmg":
|
|
case "weapon_lmg":
|
|
case "weapon_assault":
|
|
simulate_weapon_fire_machine_gun( owner, weapon );
|
|
break;
|
|
case "weapon_sniper":
|
|
simulate_weapon_fire_sniper( owner, weapon );
|
|
break;
|
|
case "weapon_pistol":
|
|
simulate_weapon_fire_pistol( owner, weapon );
|
|
break;
|
|
case "weapon_shotgun":
|
|
simulate_weapon_fire_shotgun( owner, weapon );
|
|
break;
|
|
|
|
// for weapons like rocket launchers
|
|
default:
|
|
simulate_weapon_fire_machine_gun( owner, weapon );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
function simulate_weapon_fire_machine_gun( owner, weapon )
|
|
{
|
|
if ( weapon.isSemiAuto )
|
|
{
|
|
simulate_weapon_fire_machine_gun_semi_auto( owner, weapon );
|
|
}
|
|
else
|
|
{
|
|
simulate_weapon_fire_machine_gun_full_auto( owner, weapon );
|
|
}
|
|
}
|
|
|
|
|
|
function simulate_weapon_fire_machine_gun_semi_auto( owner, weapon )
|
|
{
|
|
clipSize = weapon.clipSize;
|
|
fireTime = weapon.fireTime;
|
|
reloadTime = weapon.reloadTime;
|
|
|
|
burst_spacing_min = 4;
|
|
burst_spacing_max = 10;
|
|
|
|
while( 1 )
|
|
{
|
|
if ( clipSize <= 1 )
|
|
burst_count = 1;
|
|
else
|
|
burst_count = RandomIntRange( 1, clipSize );
|
|
self thread move( owner, burst_count, fireTime, self.main_dir, self.max_offset_angle );
|
|
self fire_burst( owner, weapon, fireTime, burst_count, true );
|
|
finish_while_loop(weapon, reloadTime, burst_spacing_min, burst_spacing_max);
|
|
}
|
|
}
|
|
|
|
function simulate_weapon_fire_pistol( owner, weapon )
|
|
{
|
|
clipSize = weapon.clipSize;
|
|
fireTime = weapon.fireTime;
|
|
reloadTime = weapon.reloadTime;
|
|
|
|
burst_spacing_min = 0.5;
|
|
burst_spacing_max = 4;
|
|
|
|
while( 1 )
|
|
{
|
|
burst_count = RandomIntRange( 1, clipSize );
|
|
self thread move( owner, burst_count, fireTime, self.main_dir, self.max_offset_angle );
|
|
self fire_burst( owner, weapon, fireTime, burst_count, false );
|
|
finish_while_loop(weapon, reloadTime, burst_spacing_min, burst_spacing_max);
|
|
}
|
|
}
|
|
|
|
function simulate_weapon_fire_shotgun( owner, weapon )
|
|
{
|
|
clipSize = weapon.clipSize;
|
|
fireTime = weapon.fireTime;
|
|
reloadTime = weapon.reloadTime;
|
|
|
|
if ( clipSize > 2 )
|
|
clipSize = 2;
|
|
|
|
burst_spacing_min = 0.5;
|
|
burst_spacing_max = 4;
|
|
|
|
while( 1 )
|
|
{
|
|
burst_count = RandomIntRange( 1, clipSize );
|
|
self thread move( owner, burst_count, fireTime, self.main_dir, self.max_offset_angle );
|
|
self fire_burst( owner, weapon, fireTime, burst_count, false );
|
|
finish_while_loop(weapon, reloadTime, burst_spacing_min, burst_spacing_max);
|
|
}
|
|
}
|
|
|
|
function simulate_weapon_fire_machine_gun_full_auto( owner, weapon )
|
|
{
|
|
clipSize = weapon.clipSize;
|
|
fireTime = weapon.fireTime;
|
|
reloadTime = weapon.reloadTime;
|
|
|
|
if ( clipSize > 30 )
|
|
clipSize = 30;
|
|
|
|
burst_spacing_min = 2;
|
|
burst_spacing_max = 6;
|
|
|
|
while( 1 )
|
|
{
|
|
burst_count = RandomIntRange( Int(clipSize * 0.6), clipSize );
|
|
interrupt = false; // RandomIntRange( 0, 2 );
|
|
self thread move( owner, burst_count, fireTime, self.main_dir, self.max_offset_angle );
|
|
self fire_burst( owner, weapon, fireTime, burst_count, interrupt );
|
|
|
|
finish_while_loop(weapon, reloadTime, burst_spacing_min, burst_spacing_max);
|
|
}
|
|
}
|
|
|
|
function simulate_weapon_fire_sniper( owner, weapon )
|
|
{
|
|
clipSize = weapon.clipSize;
|
|
fireTime = weapon.fireTime;
|
|
reloadTime = weapon.reloadTime;
|
|
|
|
if ( clipSize > 2 )
|
|
clipSize = 2;
|
|
|
|
burst_spacing_min = 3;
|
|
burst_spacing_max = 5;
|
|
|
|
while( 1 )
|
|
{
|
|
burst_count = RandomIntRange( 1, clipSize );
|
|
self thread move( owner, burst_count, fireTime, self.main_dir, self.max_offset_angle );
|
|
self fire_burst( owner, weapon, fireTime, burst_count, false );
|
|
finish_while_loop(weapon, reloadTime, burst_spacing_min, burst_spacing_max);
|
|
}
|
|
}
|
|
|
|
function fire_burst( owner, weapon, fireTime, count, interrupt )
|
|
{
|
|
interrupt_shot = count;
|
|
|
|
if ( interrupt )
|
|
{
|
|
interrupt_shot = Int( count * RandomFloatRange( 0.6, 0.8 ) );
|
|
}
|
|
|
|
self FakeFire( owner, self.origin, weapon, interrupt_shot );
|
|
wait ( fireTime * interrupt_shot );
|
|
|
|
if ( interrupt )
|
|
{
|
|
self FakeFire( owner, self.origin, weapon, count - interrupt_shot );
|
|
wait ( fireTime * (count - interrupt_shot) );
|
|
}
|
|
}
|
|
|
|
function finish_while_loop(weapon, reloadTime, burst_spacing_min, burst_spacing_max)
|
|
{
|
|
if ( should_play_reload_sound() )
|
|
{
|
|
play_reload_sounds( weapon, reloadTime );
|
|
}
|
|
else
|
|
{
|
|
wait ( RandomFloatRange(burst_spacing_min, burst_spacing_max) );
|
|
}
|
|
}
|
|
|
|
function play_reload_sounds(weapon, reloadTime)
|
|
{
|
|
divy_it_up = (reloadTime - 0.1) / 2;
|
|
wait (0.1);
|
|
self PlaySound("fly_assault_reload_npc_mag_out");
|
|
wait (divy_it_up);
|
|
self PlaySound("fly_assault_reload_npc_mag_in");
|
|
wait (divy_it_up);
|
|
// self PlaySound("fly_assault_reload_npc_charge");
|
|
// wait (divy_it_up);
|
|
}
|
|
|
|
function watch_for_explosion( owner, weapon )
|
|
{
|
|
self thread watch_for_death_before_explosion();
|
|
|
|
owner endon( "disconnect");
|
|
self endon( "death_before_explode");
|
|
self waittill("explode", pos);
|
|
level thread do_explosion( owner, pos, weapon, RandomIntRange( 5, 10 ) );
|
|
}
|
|
|
|
function watch_for_death_before_explosion( )
|
|
{
|
|
self waittill("death");
|
|
wait(0.1);
|
|
if ( IsDefined(self) )
|
|
{
|
|
self notify("death_before_explode");
|
|
}
|
|
}
|
|
|
|
|
|
function do_explosion( owner, pos, weapon, count )
|
|
{
|
|
min_offset = 100;
|
|
max_offset = 500;
|
|
|
|
for ( i = 0 ; i < count; i++ )
|
|
{
|
|
wait(RandomFloatRange(0.1,0.5));
|
|
offset = ( RandomFloatRange(min_offset,max_offset)* (RandomIntRange( 0, 2 ) * 2 - 1), RandomFloatRange(min_offset,max_offset)* (RandomIntRange( 0, 2 ) * 2 - 1), 0 );
|
|
owner FakeFire( owner, pos+offset, weapon, 1 );
|
|
}
|
|
}
|
|
|
|
function pick_random_weapon()
|
|
{
|
|
type = "fullauto";
|
|
|
|
if ( RandomIntRange( 0, 10 ) < 3 )
|
|
{
|
|
type = "semiauto";
|
|
}
|
|
|
|
randomval = RandomIntRange(0,level.decoyWeapons[type].size);
|
|
|
|
/# PrintLn( "Decoy type: " + type + " weapon: " + level.decoyWeapons[type][randomval].name ); #/
|
|
|
|
return level.decoyWeapons[type][randomval];
|
|
}
|
|
|
|
function should_play_reload_sound()
|
|
{
|
|
if( RandomIntRange(0,5) == 1 )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function track_main_direction()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "done" );
|
|
self.main_dir = Int(VectorToAngles((self.initial_velocity[0], self.initial_velocity[1], 0 ))[1]);
|
|
|
|
up = (0,0,1);
|
|
while( 1 )
|
|
{
|
|
self waittill( "grenade_bounce", pos, normal );
|
|
|
|
dot = VectorDot( normal, up );
|
|
|
|
// something got in the way thats somewhat vertical
|
|
if ( dot < 0.5 && dot > -0.5 )
|
|
{
|
|
self.main_dir = Int(VectorToAngles((normal[0], normal[1], 0 ))[1]);
|
|
}
|
|
}
|
|
}
|