2023-04-13 17:30:38 +02:00

1950 lines
51 KiB
Plaintext

#using scripts\codescripts\struct;
#using scripts\shared\array_shared;
#using scripts\shared\bb_shared;
#using scripts\shared\callbacks_shared;
#using scripts\shared\challenges_shared;
#using scripts\shared\gameobjects_shared;
#using scripts\shared\killstreaks_shared;
#using scripts\shared\loadout_shared;
#using scripts\shared\scoreevents_shared;
#using scripts\shared\system_shared;
#using scripts\shared\util_shared;
#using scripts\shared\weapons_shared;
#using scripts\shared\weapons\_hive_gun;
#using scripts\shared\weapons\_empgrenade;
#using scripts\shared\weapons\_flashgrenades;
#using scripts\shared\weapons\_hacker_tool;
#using scripts\shared\weapons\_proximity_grenade;
#using scripts\shared\weapons\_sticky_grenade;
#using scripts\shared\weapons\_riotshield;
#using scripts\shared\weapons\_tabun;
#using scripts\shared\weapons\_trophy_system;
#using scripts\shared\weapons\_weapon_utils;
#using scripts\shared\weapons\_weaponobjects;
#precache( "material", "hud_scavenger_pickup" );
#namespace weapons;
function init_shared()
{
level.weaponNone = GetWeapon( "none" );
level.weaponNull = GetWeapon( "weapon_null" );
level.weaponBaseMelee = GetWeapon( "knife" );
level.weaponBaseMeleeHeld = GetWeapon( "knife_held" );
level.weaponBallisticKnife = GetWeapon( "knife_ballistic" );
level.weaponRiotshield = GetWeapon( "riotshield" );
level.weaponFlashGrenade = GetWeapon( "flash_grenade" );
level.weaponSatchelCharge = GetWeapon( "satchel_charge" );
if ( !IsDefined(level.trackWeaponStats) )
{
level.trackWeaponStats = true;
}
level._effect["flashNineBang"] = "_t6/misc/fx_equip_tac_insert_exp";
callback::on_start_gametype( &init );
}
function init()
{
// assigns weapons with stat numbers from 0-99
// attachments are now shown here, they are per weapon settings instead
//TODO - add to statstable so its a regular weapon?
level.missileEntities = [];
level.hackerToolTargets = [];
level.missileDudDeleteDelay = GetDvarInt( "scr_missileDudDeleteDelay", 3 ); //Seconds
if ( !isdefined(level.roundStartExplosiveDelay) )
level.roundStartExplosiveDelay = 0;
callback::on_connect( &on_player_connect );
callback::on_spawned( &on_player_spawned );
}
function on_player_connect()
{
self.usedWeapons = false;
self.lastFireTime = 0;
self.hits = 0;
self scavenger_hud_create();
}
function on_player_spawned()
{
self.concussionEndTime = 0;
self.scavenged = false;
self.hasDoneCombat = false;
self.shieldDamageBlocked = 0;
self thread watch_usage();
self thread watch_grenade_usage();
self thread watch_missile_usage();
self thread watch_weapon_change();
if ( level.trackWeaponStats )
{
self thread track();
}
self.droppedDeathWeapon = undefined;
self.tookWeaponFrom = [];
self.pickedUpWeaponKills = [];
self thread update_stowed_weapon();
}
function watch_weapon_change()
{
self endon("death");
self endon("disconnect");
self.lastDroppableWeapon = self GetCurrentWeapon();
self.lastWeaponChange = 0;
while(1)
{
previous_weapon = self GetCurrentWeapon();
self waittill( "weapon_change", newWeapon );
if ( may_drop( newWeapon ) )
{
self.lastDroppableWeapon = newWeapon;
self.lastWeaponChange = getTime();
}
if ( DoesWeaponReplaceSpawnWeapon( self.spawnWeapon, newWeapon ) )
{
self.spawnWeapon = newWeapon;
self.pers["spawnWeapon"] = newWeapon;
}
}
}
function update_last_held_weapon_timings( newTime )
{
if ( isdefined( self.currentWeapon ) && isdefined( self.currentWeaponStartTime ) )
{
totalTime = int ( ( newTime - self.currentWeaponStartTime ) / 1000 );
if ( totalTime > 0 )
{
weaponPickedUp = false;
if( isdefined( self.pickedUpWeapons ) && isdefined( self.pickedUpWeapons[self.currentWeapon] ) )
{
weaponPickedUp = true;
}
if ( isdefined( self.class_num ) )
{
self AddWeaponStat( self.currentWeapon, "timeUsed", totalTime, self.class_num, weaponPickedUp );
self.currentWeaponStartTime = newTime;
}
}
}
}
function update_timings( newTime )
{
if ( self util::is_bot() )
{
return;
}
update_last_held_weapon_timings( newTime );
if ( !isdefined( self.staticWeaponsStartTime ) )
{
return;
}
totalTime = int ( ( newTime - self.staticWeaponsStartTime ) / 1000 );
if ( totalTime < 0 )
{
return;
}
self.staticWeaponsStartTime = newTime;
// Record grenades and equipment
if( isdefined( self.weapon_array_grenade ) )
{
for( i=0; i<self.weapon_array_grenade.size; i++ )
{
self AddWeaponStat( self.weapon_array_grenade[i], "timeUsed", totalTime, self.class_num );
}
}
if( isdefined( self.weapon_array_inventory ) )
{
for( i=0; i<self.weapon_array_inventory.size; i++ )
{
self AddWeaponStat( self.weapon_array_inventory[i], "timeUsed", totalTime, self.class_num );
}
}
// Record killstreaks
if( isdefined( self.killstreak ) )
{
for( i=0; i<self.killstreak.size; i++ )
{
killstreakType = level.menuReferenceForKillStreak[ self.killstreak[i] ];
if ( isdefined( killstreakType ) )
{
killstreakWeapon = killstreaks::get_killstreak_weapon( killstreakType );
self AddWeaponStat( killstreakWeapon, "timeUsed", totalTime, self.class_num );
}
}
}
// Record all of the equipped perks
if ( level.rankedmatch && level.perksEnabled )
{
perksIndexArray = [];
specialtys = self.specialty;
if ( !isdefined( specialtys ) )
{
return;
}
if ( !isdefined( self.curClass ) )
{
return;
}
if ( isdefined( self.class_num ) )
{
for ( numSpecialties = 0; numSpecialties < level.maxSpecialties; numSpecialties++ )
{
perk = self GetLoadoutItem( self.class_num, "specialty" + ( numSpecialties + 1 ) );
if ( perk != 0 )
{
perksIndexArray[ perk ] = true;
}
}
perkIndexArrayKeys = getarraykeys( perksIndexArray );
for ( i = 0; i < perkIndexArrayKeys.size; i ++ )
{
if ( perksIndexArray[ perkIndexArrayKeys[i] ] == true )
{
self AddDStat( "itemStats", perkIndexArrayKeys[i], "stats", "timeUsed", "statValue", totalTime );
}
}
}
}
}
function track()
{
currentWeapon = self getCurrentWeapon();
currentTime = getTime();
spawnid = getplayerspawnid( self );
while(1)
{
event = self util::waittill_any_return( "weapon_change", "death", "disconnect" );
newTime = getTime();
if( event == "weapon_change" )
{
self bb::commit_weapon_data( spawnid, currentWeapon, currentTime );
newWeapon = self getCurrentWeapon();
if( newWeapon != level.weaponNone && newWeapon != currentWeapon )
{
update_last_held_weapon_timings( newTime );
self loadout::initWeaponAttachments( newWeapon );
currentWeapon = newWeapon;
currentTime = newTime;
}
}
else
{
if( event != "disconnect" && isdefined( self ) )
{
self bb::commit_weapon_data( spawnid, currentWeapon, currentTime );
update_timings( newTime );
}
return;
}
}
}
function may_drop( weapon )
{
if ( level.disableWeaponDrop == 1 )
return false;
if ( weapon == level.weaponNone )
return false;
if ( killstreaks::is_killstreak_weapon( weapon ) )
return false;
if ( weapon.isGameplayWeapon )
return false;
if ( !weapon.isPrimary )
return false;
if ( IsDefined( level.mayDropWeapon ) && ![[level.mayDropWeapon]]( weapon ) )
return false;
return true;
}
function drop_for_death( attacker, sWeapon, sMeansOfDeath )
{
if ( level.disableWeaponDrop == 1 )
return;
weapon = self.lastDroppableWeapon;
if ( isdefined( self.droppedDeathWeapon ) )
return;
if ( !isdefined( weapon ) )
{
/#
if ( GetDvarString( "scr_dropdebug") == "1" )
println( "didn't drop weapon: not defined" );
#/
return;
}
if ( weapon == level.weaponNone )
{
/#
if ( GetDvarString( "scr_dropdebug") == "1" )
println( "didn't drop weapon: weapon == none" );
#/
return;
}
if ( !self hasWeapon( weapon ) )
{
/#
if ( GetDvarString( "scr_dropdebug") == "1" )
println( "didn't drop weapon: don't have it anymore (" + weapon.name + ")" );
#/
return;
}
if ( !(self AnyAmmoForWeaponModes( weapon )) )
{
/#
if ( GetDvarString( "scr_dropdebug") == "1" )
println( "didn't drop weapon: no ammo for weapon modes" );
#/
return;
}
if ( !should_drop_limited_weapon( weapon, self ) )
{
return;
}
if( weapon.isCarriedKillstreak )
return;
clipAmmo = self GetWeaponAmmoClip( weapon );
stockAmmo = self GetWeaponAmmoStock( weapon );
clip_and_stock_ammo = clipAmmo + stockAmmo;
//Check if the weapon has ammo
if( !clip_and_stock_ammo && !( isdefined( weapon.unlimitedammo ) && weapon.unlimitedammo ) )
{
/#
if ( GetDvarString( "scr_dropdebug") == "1" )
println( "didn't drop weapon: no ammo" );
#/
return;
}
if( ( isdefined( weapon.isnotdroppable ) && weapon.isnotdroppable ) )
{
return;
}
stockMax = weapon.maxAmmo;
if ( stockAmmo > stockMax )
stockAmmo = stockMax;
item = self dropItem( weapon );
if ( !isdefined( item ) )
{
/# iprintlnbold( "dropItem: was not able to drop weapon " + weapon.name ); #/
return;
}
/#
if ( GetDvarString( "scr_dropdebug") == "1" )
println( "dropped weapon: " + weapon.name );
#/
drop_limited_weapon( weapon, self, item );
self.droppedDeathWeapon = true;
item ItemWeaponSetAmmo( clipAmmo, stockAmmo );
if ( isdefined( level.gameModeWeaponDropped ) )
self [[ level.gameModeWeaponDropped ]]( item );
item.owner = self;
item.ownersattacker = attacker;
item.sWeapon = sWeapon;
item.sMeansOfDeath = sMeansOfDeath;
item thread watch_pickup();
item thread delete_pickup_after_aWhile();
}
function delete_pickup_after_aWhile()
{
self endon("death");
wait 60;
if ( !isdefined( self ) )
return;
self delete();
}
function watch_pickup()
{
self endon("death");
weapon = self.item;
self waittill( "trigger", player, droppedItem, pickedUpOnTouch );
// As far as the mp_match_record weapon stats are concerned, any weapon you pick up
// is a picked-up weapon, even if you were the previous owner. This way we can still
// map the weapons of your *current* loadout directly to loadoutWeaponStats slots.
// Also, in contrast to the player.tookWeaponFrom array, you don't have to be the one who
// killed the previous owner.
if( 1 ) // isdefined(self.owner) && self.owner != player ) <-- intentionally not checking this
{
if( isdefined( player ) && isPlayer( player ) )
{
if( isdefined( player.weaponPickupsCount ) )
{
player.weaponPickupsCount++;
}
else
{
player.weaponPickupsCount = 1;
}
player incrementSpecificWeaponPickedUpCount( weapon );
if( !isdefined( player.pickedUpWeapons) )
{
player.pickedUpWeapons = []; // this array will be reset on each death
}
player.pickedUpWeapons[weapon] = 1; // value not important, just that the entry exists
}
}
/#
if ( GetDvarString( "scr_dropdebug") == "1" )
println( "picked up weapon: " + weapon.name + ", " + isdefined( self.ownersattacker ) );
#/
assert( isdefined( player.tookWeaponFrom ) );
assert( isdefined( player.pickedUpWeaponKills ) );
if ( isdefined( droppedItem ) )
{
for ( i = 0; i < droppedItem.size; i++ )
{
if ( !IsDefined( droppedItem[i] ) )
{
continue;
}
// make sure the owner information on the dropped item is preserved
droppedWeapon = droppedItem[i].item;
if ( isdefined( player.tookWeaponFrom[ droppedWeapon ] ) )
{
droppedItem[i].owner = player.tookWeaponFrom[ droppedWeapon ];
droppedItem[i].ownersattacker = player;
player.tookWeaponFrom[ droppedWeapon ] = undefined;
}
droppedItem[i] thread watch_pickup();
}
}
// take owner information from self and put it onto player
if ( !isdefined( pickedUpOnTouch ) || !pickedUpOnTouch )
{
if ( isdefined( self.ownersattacker ) && self.ownersattacker == player )
{
player.tookWeaponFrom[ weapon ] = SpawnStruct();
player.tookWeaponFrom[ weapon ].previousOwner = self.owner;
player.tookWeaponFrom[ weapon ].sWeapon = self.sWeapon;
player.tookWeaponFrom[ weapon ].sMeansOfDeath = self.sMeansOfDeath;
player.pickedUpWeaponKills[ weapon ] = 0;
}
else
{
player.tookWeaponFrom[ weapon ] = undefined;
player.pickedUpWeaponKills[ weapon ] = undefined;
}
}
}
function watch_usage()
{
self endon( "death" );
self endon( "disconnect" );
level endon ( "game_ended" );
for ( ;; )
{
self waittill ( "weapon_fired", curWeapon );
self.lastFireTime = GetTime();
self.hasDoneCombat = true;
switch ( curWeapon.weapClass )
{
case "rifle":
case "pistol":
case "pistol spread":
case "mg":
case "smg":
case "spread":
self track_fire( curWeapon );
level.globalShotsFired++;
break;
case "rocketlauncher":
case "grenade":
self AddWeaponStat( curWeapon, "shots", 1, self.class_num, false );
break;
default:
break;
}
if( isDefined(curWeapon.gadget_type) && curWeapon.gadget_type == 14 )
{
if( isDefined(self.heroweaponShots) )
{
self.heroweaponShots++;
}
}
if( curWeapon.isCarriedKillstreak )
{
if ( IsDefined( self.pers["held_killstreak_ammo_count"][curWeapon] ) )
{
self.pers["held_killstreak_ammo_count"][curWeapon]--;
}
}
}
}
function track_fire( curWeapon )
{
if ( ( isdefined( level.disableWeaponAccuracyTracking ) && level.disableWeaponAccuracyTracking ) )
{
return;
}
pixbeginevent("trackWeaponFire");
weaponPickedUp = false;
if( isdefined( self.pickedUpWeapons ) && isdefined( self.pickedUpWeapons[curWeapon] ) )
{
weaponPickedUp = true;
}
self TrackWeaponFireNative( curWeapon, 1, self.hits, 1, self.class_num, weaponPickedUp, self.primaryLoadoutGunSmithVariantIndex, self.secondaryLoadoutGunSmithVariantIndex );
// old:
//self AddWeaponStat( curWeapon, "shots", TRACK_WEAPON_SHOT_FIRED );
//self AddWeaponStat( curWeapon, "hits", self.hits );
//self AddPlayerStat( "total_shots", TRACK_WEAPON_SHOT_FIRED );
//self AddPlayerStat( "hits", self.hits );
//self AddPlayerStat( "misses", int( max( 0, TRACK_WEAPON_SHOT_FIRED - self.hits ) ) );
if ( isdefined( self.totalMatchShots ) )
self.totalMatchShots++;
self bb::add_to_stat( "shots", 1 );
self bb::add_to_stat( "hits", self.hits );
if( level.mpCustomMatch === true )
{
self.pers["shotsfired"]++;
self.shotsfired = self.pers["shotsfired"];
self.pers["shotshit"] += self.hits;
self.shotshit = self.pers["shotshit"];
self.pers["shotsmissed"] = self.shotsfired - self.shotshit;
self.shotsmissed = self.pers["shotsmissed"];
}
self.hits = 0;
pixendevent();
}
function watch_grenade_usage()
{
self endon( "death" );
self endon( "disconnect" );
self.throwingGrenade = false;
self.gotPullbackNotify = false;
self thread begin_other_grenade_tracking();
self thread watch_for_throwbacks();
self thread watch_for_grenade_duds();
self thread watch_for_grenade_launcher_duds();
for ( ;; )
{
self waittill ( "grenade_pullback", weapon );
self AddWeaponStat( weapon, "shots", 1, self.class_num );
self.hasDoneCombat = true;
self.throwingGrenade = true;
self.gotPullbackNotify = true;
if ( weapon.drawOffhandModelInHand )
{
self SetOffhandVisible( true );
self thread watch_offhand_end();
}
self thread begin_grenade_tracking();
}
}
function watch_missile_usage()
{
self endon( "death" );
self endon( "disconnect" );
level endon ( "game_ended" );
for ( ;; )
{
self waittill ( "missile_fire", missile, weapon );
self.hasDoneCombat = true;
/#assert( isdefined( missile ));#/
level.missileEntities[ level.missileEntities.size ] = missile;
missile.weapon = weapon;
missile thread watch_missile_death();
}
}
function watch_missile_death() // self == missile
{
self waittill( "death" );
ArrayRemoveValue( level.missileEntities, self );
}
function drop_all_to_ground( origin, radius )
{
weapons = GetDroppedWeapons();
for ( i = 0 ; i < weapons.size ; i++ )
{
if ( DistanceSquared( origin, weapons[i].origin ) < radius * radius )
{
trace = bullettrace( weapons[i].origin, weapons[i].origin + (0,0,-2000), false, weapons[i] );
weapons[i].origin = trace["position"];
}
}
}
function drop_grenades_to_ground( origin, radius )
{
grenades = getentarray( "grenade", "classname" );
for( i = 0 ; i < grenades.size ; i++ )
{
if( DistanceSquared( origin, grenades[i].origin )< radius * radius )
{
grenades[i] launch( (5,5,5) );
}
}
}
function watch_grenade_cancel()
{
self endon ( "death" );
self endon ( "disconnect" );
self endon ( "grenade_fire" );
waittillframeend;
weapon = level.weaponNone;
while( self IsThrowingGrenade() && weapon == level.weaponNone )
{
self waittill( "weapon_change", weapon );
}
self.throwingGrenade = false;
self.gotPullbackNotify = false;
self notify( "grenade_throw_cancelled" );
}
function watch_offhand_end() // self == player
{
self notify( "watchOffhandEnd" );
self endon( "watchOffhandEnd" );
while ( self is_using_offhand_equipment() )
{
msg = self util::waittill_any_return( "death", "disconnect", "grenade_fire", "weapon_change", "watchOffhandEnd" );
if (( msg == "death" ) || ( msg == "disconnect" ))
{
break;
}
}
if ( isdefined( self ) )
{
self SetOffhandVisible( false );
}
}
function is_using_offhand_equipment() // self == player
{
if ( self IsUsingOffhand() )
{
weapon = self GetCurrentOffhand();
if ( weapon.isEquipment )
{
return true;
}
}
return false;
}
function begin_grenade_tracking()
{
self endon ( "death" );
self endon ( "disconnect" );
self endon ( "grenade_throw_cancelled" );
startTime = getTime();
self thread watch_grenade_cancel();
self waittill ( "grenade_fire", grenade, weapon, cookTime );
/#assert( isdefined( grenade ));#/
level.missileEntities[ level.missileEntities.size ] = grenade;
grenade.weapon = weapon;
grenade thread watch_missile_death();
if( ( SessionModeIsCampaignZombiesGame() ) || ( isdefined( level.projectiles_should_ignore_world_pause ) && level.projectiles_should_ignore_world_pause ) )
{
grenade SetIgnorePauseWorld( true );
}
if ( grenade util::isHacked() )
{
return;
}
blackBoxEventName = "mpequipmentuses";
if ( SessionModeIsCampaignGame() )
{
blackBoxEventName = "cpequipmentuses";
}
else if ( SessionModeIsZombiesGame() )
{
blackBoxEventName = "zmequipmentuses";
}
bbPrint( blackBoxEventName, "gametime %d spawnid %d weaponname %s", gettime(), getplayerspawnid( self ), weapon.name );
cookedTime = getTime() - startTime;
if ( cookedTime > 1000 )
{
grenade.isCooked = true;
}
if (isDefined(self.grenadesUsed))
{
self.grenadesUsed++;
}
switch ( weapon.rootWeapon.name )
{
case "frag_grenade":
level.globalFragGrenadesFired++;
// fall through on purpose
case "sticky_grenade":
self AddWeaponStat( weapon, "used", 1 );
grenade SetTeam( self.pers["team"] );
grenade SetOwner( self );
// fall through on purpose
case "explosive_bolt":
grenade.originalOwner = self;
break;
case "satchel_charge":
level.globalSatchelChargeFired++;
break;
case "concussion_grenade":
case "flash_grenade":
self AddWeaponStat( weapon, "used", 1 );
break;
}
self.throwingGrenade = false;
if ( weapon.cookOffHoldTime > 0 )
{
grenade thread track_cooked_detonation( self, weapon, cookTime );
}
else if ( weapon.multiDetonation > 0 )
{
grenade thread track_multi_detonation( self, weapon, cookTime );
}
}
function begin_other_grenade_tracking()
{
self notify( "otherGrenadeTrackingStart" );
self endon( "otherGrenadeTrackingStart" );
self endon( "disconnect" );
for (;;)
{
self waittill ( "grenade_fire", grenade, weapon );
if ( grenade util::isHacked() )
{
continue;
}
switch( weapon.rootweapon.name )
{
case "tabun_gas":
grenade thread tabun::watchTabunGrenadeDetonation( self );
break;
case "sticky_grenade":
grenade thread check_stuck_to_player( true, true, weapon );
grenade thread riotshield::check_stuck_to_shield();
break;
case "satchel_charge":
case "c4":
grenade thread check_stuck_to_player( true, false, weapon );
break;
case "hatchet":
grenade.lastWeaponBeforeToss = self util::getLastWeapon();
grenade thread check_hatchet_bounce();
grenade thread check_stuck_to_player( false, false, weapon );
self AddWeaponStat( weapon, "used", 1 );
break;
default:
break;
}
}
}
function check_stuck_to_player( deleteOnTeamChange, awardScoreEvent, weapon )
{
self endon( "death" );
self waittill( "stuck_to_player", player );
if ( isdefined ( player ) )
{
if ( deleteOnTeamChange )
self thread stuck_to_player_team_change( player );
if ( awardScoreEvent && isdefined ( self.originalOwner ) )
{
if ( self.originalOwner util::IsEnemyPlayer( player ) )
{
scoreevents::processScoreEvent( "stick_explosive_kill", self.originalOwner, player, weapon );
}
}
self.stuckToPlayer = player;
}
}
function check_hatchet_bounce()
{
self endon( "stuck_to_player" );
self endon( "death");
self waittill( "grenade_bounce" );
self.bounced = true;
}
function stuck_to_player_team_change( player )
{
self endon("death");
player endon("disconnect");
originalTeam = player.pers["team"];
while(1)
{
player waittill("joined_team");
if ( player.pers["team"] != originalTeam )
{
self detonate();
return;
}
}
}
function watch_for_throwbacks()
{
self endon ( "death" );
self endon ( "disconnect" );
for ( ;; )
{
self waittill ( "grenade_fire", grenade, weapon );
if ( self.gotPullbackNotify )
{
self.gotPullbackNotify = false;
continue;
}
if ( !isSubStr( weapon.name, "frag_" ) )
continue;
// no grenade_pullback notify! we must have picked it up off the ground.
grenade.threwBack = true;
grenade.originalOwner = self;
}
}
function wait_and_delete_dud( waitTime )
{
self endon( "death" );
wait( waitTime );
if( isDefined( self ) )
self delete();
}
function getTimeFromLevelStart()
{
if ( !isdefined( level.startTime ) )
return 0;
return (gettime() - level.startTime);
}
function turn_grenade_into_a_dud( weapon, isThrownGrenade, player )
{
time = (getTimeFromLevelStart() / 1000);
if ( level.roundStartExplosiveDelay >= time )
{
if ( weapon.disallowatmatchstart || WeaponHasAttachment( weapon, "gl" ) )
{
timeLeft = Int( level.roundStartExplosiveDelay - time );
if ( !timeLeft )
timeLeft = 1;
// these prints need to be changed to the correct location and they should include the weapon name
if ( isThrownGrenade )
player iPrintLnBold( &"MP_GRENADE_UNAVAILABLE_FOR_N", " " + timeLeft + " ", &"EXE_SECONDS" );
else
player iPrintLnBold( &"MP_LAUNCHER_UNAVAILABLE_FOR_N", " " + timeLeft + " ", &"EXE_SECONDS" );
self makeGrenadeDud();
}
}
}
function watch_for_grenade_duds()
{
self endon( "spawned_player" );
self endon( "disconnect" );
while ( 1 )
{
self waittill( "grenade_fire", grenade, weapon );
grenade turn_grenade_into_a_dud( weapon, true, self );
}
}
function watch_for_grenade_launcher_duds()
{
self endon( "spawned_player" );
self endon( "disconnect" );
while ( 1 )
{
self waittill( "grenade_launcher_fire", grenade, weapon );
grenade turn_grenade_into_a_dud( weapon, false, self );
/#assert( isDefined( grenade ));#/
level.missileEntities[ level.missileEntities.size ] = grenade;
grenade.weapon = weapon;
grenade thread watch_missile_death();
}
}
// these functions are used with scripted weapons (like satchels, shoeboxs, artillery)
// returns an array of objects representing damageable entities (including players) within a given sphere.
// each object has the property damageCenter, which represents its center (the location from which it can be damaged).
// each object also has the property entity, which contains the entity that it represents.
// to damage it, call damageEnt() on it.
function get_damageable_ents(pos, radius, doLOS, startRadius)
{
ents = [];
if (!isdefined(doLOS))
doLOS = false;
if ( !isdefined( startRadius ) )
startRadius = 0;
// players
players = level.players;
for (i = 0; i < players.size; i++)
{
if (!isalive(players[i]) || players[i].sessionstate != "playing")
continue;
playerpos = players[i].origin + (0,0,32);
distsq = distancesquared(pos, playerpos);
if (distsq < radius*radius && (!doLOS || damage_trace_passed(pos, playerpos, startRadius, undefined)))
{
newent = spawnstruct();
newent.isPlayer = true;
newent.isADestructable = false;
newent.isADestructible = false;
newent.isActor = false;
newent.entity = players[i];
newent.damageCenter = playerpos;
ents[ents.size] = newent;
}
}
// grenades
grenades = getentarray("grenade", "classname");
for (i = 0; i < grenades.size; i++)
{
entpos = grenades[i].origin;
distsq = distancesquared(pos, entpos);
if (distsq < radius*radius && (!doLOS || damage_trace_passed(pos, entpos, startRadius, grenades[i])))
{
newent = spawnstruct();
newent.isPlayer = false;
newent.isADestructable = false;
newent.isADestructible = false;
newent.isActor = false;
newent.entity = grenades[i];
newent.damageCenter = entpos;
ents[ents.size] = newent;
}
}
// THIS IS NOT THE SAME AS THE destruct-A-bles BELOW
destructibles = getentarray("destructible", "targetname");
for (i = 0; i < destructibles.size; i++)
{
entpos = destructibles[i].origin;
distsq = distancesquared(pos, entpos);
if (distsq < radius*radius && (!doLOS || damage_trace_passed(pos, entpos, startRadius, destructibles[i])))
{
newent = spawnstruct();
newent.isPlayer = false;
newent.isADestructable = false;
newent.isADestructible = true;
newent.isActor = false;
newent.entity = destructibles[i];
newent.damageCenter = entpos;
ents[ents.size] = newent;
}
}
// THIS IS NOT THE SAME AS THE destruct-I-bles ABOVE
destructables = getentarray("destructable", "targetname");
for (i = 0; i < destructables.size; i++)
{
entpos = destructables[i].origin;
distsq = distancesquared(pos, entpos);
if (distsq < radius*radius && (!doLOS || damage_trace_passed(pos, entpos, startRadius, destructables[i])))
{
newent = spawnstruct();
newent.isPlayer = false;
newent.isADestructable = true;
newent.isADestructible = false;
newent.isActor = false;
newent.entity = destructables[i];
newent.damageCenter = entpos;
ents[ents.size] = newent;
}
}
dogs = [[level.dogManagerOnGetDogs]]();
if ( isdefined( dogs ) )
{
foreach( dog in dogs )
{
if ( !IsAlive( dog ) )
{
continue;
}
entpos = dog.origin;
distsq = distancesquared(pos, entpos);
if (distsq < radius*radius && (!doLOS || damage_trace_passed(pos, entpos, startRadius, dog)))
{
newent = spawnstruct();
newent.isPlayer = false;
newent.isADestructable = false;
newent.isADestructible = false;
newent.isActor = true;
newent.entity = dog;
newent.damageCenter = entpos;
ents[ents.size] = newent;
}
}
}
return ents;
}
function damage_trace_passed(from, to, startRadius, ignore)
{
trace = damage_trace(from, to, startRadius, ignore);
return (trace["fraction"] == 1);
}
function damage_trace(from, to, startRadius, ignore)
{
midpos = undefined;
diff = to - from;
if ( lengthsquared( diff ) < startRadius*startRadius )
midpos = to;
dir = vectornormalize( diff );
midpos = from + (dir[0]*startRadius, dir[1]*startRadius, dir[2]*startRadius);
trace = bullettrace(midpos, to, false, ignore);
if ( GetDvarint( "scr_damage_debug") != 0 )
{
if (trace["fraction"] == 1)
{
thread debugline(midpos, to, (1,1,1));
}
else
{
thread debugline(midpos, trace["position"], (1,.9,.8));
thread debugline(trace["position"], to, (1,.4,.3));
}
}
return trace;
}
// eInflictor = the entity that causes the damage (e.g. a shoebox)
// eAttacker = the player that is attacking
// iDamage = the amount of damage to do
// sMeansOfDeath = string specifying the method of death (e.g. "MOD_PROJECTILE_SPLASH")
// weapon = the weapon used
// damagepos = the position damage is coming from
// damagedir = the direction damage is moving in
function damage_ent(eInflictor, eAttacker, iDamage, sMeansOfDeath, weapon, damagepos, damagedir)
{
if (self.isPlayer)
{
self.damageOrigin = damagepos;
self.entity thread [[level.callbackPlayerDamage]](
eInflictor, // eInflictor The entity that causes the damage.(e.g. a turret)
eAttacker, // eAttacker The entity that is attacking.
iDamage, // iDamage Integer specifying the amount of damage done
0, // iDFlags Integer specifying flags that are to be applied to the damage
sMeansOfDeath, // sMeansOfDeath Integer specifying the method of death
weapon, // weapon The weapon used to inflict the damage
damagepos, // vPoint The point the damage is from?
damagedir, // vDir The direction of the damage
"none", // sHitLoc The location of the hit
damagepos, // sDamageOrigin
0, // psOffsetTime The time offset for the damage
0, // boneIndex
undefined // surfaceNormal
);
}
else if (self.isactor)
{
self.damageOrigin = damagepos;
self.entity thread [[level.callbackActorDamage]](
eInflictor, // eInflictor The entity that causes the damage.(e.g. a turret)
eAttacker, // eAttacker The entity that is attacking.
iDamage, // iDamage Integer specifying the amount of damage done
0, // iDFlags Integer specifying flags that are to be applied to the damage
sMeansOfDeath, // sMeansOfDeath Integer specifying the method of death
weapon, // weapon The weapon used to inflict the damage
damagepos, // vPoint The point the damage is from?
damagedir, // vDir The direction of the damage
"none", // sHitLoc The location of the hit
damagepos, // vDamageOrigin the point the damage originates
0, // psOffsetTime The time offset for the damage
0, // boneIndex
0, // modelIndex
0, // surfaceType
(1.0, 0, 0) // surfaceNormal
);
}
else if (self.isADestructible)
{
self.damageOrigin = damagepos;
self.entity DoDamage(
iDamage, // iDamage Integer specifying the amount of damage done
damagepos, // vPoint The point the damage is from?
eAttacker, // eAttacker The entity that is attacking.
eInflictor, // eInflictor The entity that causes the damage.(e.g. a turret)
0,
sMeansOfDeath, // sMeansOfDeath Integer specifying the method of death
0, // iDFlags Integer specifying flags that are to be applied to the damage
weapon // weapon The weapon used to inflict the damage
);
}
else
{
self.entity util::damage_notify_wrapper( iDamage, eAttacker, (0,0,0), (0,0,0), "mod_explosive", "", "" );
}
}
function debugline(a, b, color)
{
/#
for (i = 0; i < 30*20; i++)
{
line(a,b, color);
wait .05;
}
#/
}
function on_damage( eAttacker, eInflictor, weapon, meansOfDeath, damage )
{
self endon ( "death" );
self endon ( "disconnect" );
if ( isdefined( level._custom_weapon_damage_func ) )
{
is_weapon_registered = self [[level._custom_weapon_damage_func]]( eAttacker, eInflictor, weapon, meansOfDeath, damage );
if( is_weapon_registered )
{
return;
}
}
switch ( weapon.rootWeapon.name )
{
case "concussion_grenade":
if ( ( isdefined( self.concussionImmune ) && self.concussionImmune ) )
return;
radius = weapon.explosionRadius;
if (self == eAttacker) // TFLAME 8/1/12 - reduce effects on attacker
radius *= 0.5;
scale = 1 - (distance( self.origin, eInflictor.origin ) / radius);
if ( scale < 0 )
scale = 0;
time = 0.25 + (4 * scale);
{wait(.05);};
if ( meansOfDeath != "MOD_IMPACT" )
{
if ( self HasPerk ( "specialty_stunprotection" ) )
{
time *= 0.1;
}
else
{
if ( self util::mayApplyScreenEffect() )
{
self shellShock( "concussion_grenade_mp", time, false );
}
}
self thread play_concussion_sound( time );
self.concussionEndTime = getTime() + (time * 1000);
self.lastConcussedBy = eAttacker;
}
break;
default:
// shellshock will only be done if meansofdeath is an appropriate type and if there is enough damage.
if ( isdefined( level.shellshockOnPlayerDamage ) )
{
[[level.shellshockOnPlayerDamage]]( meansOfDeath, damage, weapon );
}
break;
}
}
function play_concussion_sound( duration )
{
self endon( "death" );
self endon( "disconnect" );
concussionSound = spawn ("script_origin",(0,0,1));
concussionSound.origin = self.origin;
concussionSound linkTo( self );
concussionSound thread delete_ent_on_owner_death( self );
concussionSound playsound( "" );
concussionSound playLoopSound ( "" );
if ( duration > 0.5 )
wait( duration - 0.5 );
concussionSound playsound( "" );
concussionSound StopLoopSound( .5);
wait(0.5);
concussionSound notify ( "delete" );
concussionSound delete();
}
function delete_ent_on_owner_death( owner )
{
self endon( "delete" );
owner waittill( "death" );
self delete();
}
// weapon stowing logic ===================================================================
// thread loop life = player's life
function update_stowed_weapon()
{
self endon( "spawned" );
self endon( "killed_player" );
self endon( "disconnect" );
//weapons::detach_all_weapons();
self.tag_stowed_back = undefined;
self.tag_stowed_hip = undefined;
team = self.pers["team"];
playerclass = self.pers["class"];
while ( true )
{
self waittill( "weapon_change", newWeapon );
if ( self IsMantling() )
{
continue;
}
currentStowed = self GetStowedWeapon();
hasStowed = false;
// weapon array reset, might have swapped weapons off the ground
self.weapon_array_primary =[];
self.weapon_array_sidearm = [];
self.weapon_array_grenade = [];
self.weapon_array_inventory =[];
// populate player's weapon stock arrays
weaponsList = self GetWeaponsList();
for( idx = 0; idx < weaponsList.size; idx++ )
{
// we don't want these in the primary list
switch( weaponsList[idx].name )
{
case "minigun": // death machine
case "m32": // grenade launcher
continue;
default:
break;
}
if ( !hasStowed || currentStowed == weaponsList[idx] )
{
currentStowed = weaponsList[idx];
hasStowed = true;
}
if ( weapons::is_primary_weapon( weaponsList[idx] ) )
self.weapon_array_primary[self.weapon_array_primary.size] = weaponsList[idx];
else if ( weapons::is_side_arm( weaponsList[idx] ) )
self.weapon_array_sidearm[self.weapon_array_sidearm.size] = weaponsList[idx];
else if ( weapons::is_grenade( weaponsList[idx] ) )
self.weapon_array_grenade[self.weapon_array_grenade.size] = weaponsList[idx];
else if ( weapons::is_inventory( weaponsList[idx] ) )
self.weapon_array_inventory[self.weapon_array_inventory.size] = weaponsList[idx];
else if ( weaponsList[idx].isPrimary )
self.weapon_array_primary[self.weapon_array_primary.size] = weaponsList[idx];
}
if ( newWeapon != level.weaponNone || !hasStowed )
{
weapons::detach_all_weapons();
weapons::stow_on_back();
weapons::stow_on_hip();
}
}
}
function loadout_get_offhand_weapon( stat )
{
if ( isdefined( level.giveCustomLoadout ) )
{
return level.weaponNone;
}
assert( isdefined( self.class_num ) );
if ( isdefined( self.class_num ) )
{
index = self loadout::getLoadoutItemFromDDLStats( self.class_num, stat );
if ( isdefined( level.tbl_weaponIDs[index] ) && isdefined( level.tbl_weaponIDs[index]["reference"] ) )
{
return GetWeapon( level.tbl_weaponIDs[index]["reference"] );
}
}
return level.weaponNone;
}
function loadout_get_offhand_count( stat )
{
count = 0;
if ( isdefined( level.giveCustomLoadout ) )
{
return 0;
}
assert( isdefined( self.class_num ) );
if ( isdefined( self.class_num ) )
{
count = self loadout::getLoadoutItemFromDDLStats( self.class_num, stat );
}
return count;
}
// Self == player
function flash_scavenger_icon()
{
self.scavenger_icon.alpha = 1;
self.scavenger_icon fadeOverTime( 1.0 );
self.scavenger_icon.alpha = 0;
}
function scavenger_think()
{
self endon( "death" );
self waittill( "scavenger", player );
primary_weapons = player GetWeaponsListPrimaries();
offhand_weapons_and_alts = array::exclude( player GetWeaponsList( true ), primary_weapons );
ArrayRemoveValue( offhand_weapons_and_alts, level.weaponBaseMelee );
offhand_weapons_and_alts = array::reverse( offhand_weapons_and_alts ); // Prioritize tacticals over lethals
player playsound( "wpn_ammo_pickup" );
player playlocalsound( "wpn_ammo_pickup" );
player flash_scavenger_icon();
for ( i = 0; i < offhand_weapons_and_alts.size; i++ )
{
weapon = offhand_weapons_and_alts[i];
if ( !weapon.isScavengable || killstreaks::is_killstreak_weapon( weapon ) )
{
continue;
}
maxAmmo = 0;
if ( weapon == player.grenadeTypePrimary && isdefined( player.grenadeTypePrimaryCount ) && player.grenadeTypePrimaryCount > 0 )
{
maxAmmo = player.grenadeTypePrimaryCount;
}
else if ( weapon == player.grenadeTypeSecondary && isdefined( player.grenadeTypeSecondaryCount ) && player.grenadeTypeSecondaryCount > 0 )
{
maxAmmo = player.grenadeTypeSecondaryCount;
}
if ( isdefined( level.playerGetWeaponScavengeAmmo ) )
{
maxAmmo = player [[level.playerGetWeaponScavengeAmmo]]( weapon, maxAmmo );
}
if ( maxAmmo == 0 )
{
continue;
}
if ( weapon.rootWeapon == level.weaponSatchelCharge )
{
if ( player weaponobjects::anyObjectsInWorld( weapon.rootWeapon ) )
{
continue;
}
}
stock = player GetWeaponAmmoStock( weapon );
if ( stock < maxAmmo )
{
// just give 1 for each scavenger pick up
ammo = stock + 1;
if ( ammo > maxAmmo )
{
ammo = maxAmmo;
}
player SetWeaponAmmoStock( weapon, ammo );
player.scavenged = true;
player thread challenges::scavengedGrenade();
}
else
{
if ( weapon.rootWeapon == GetWeapon( "trophy_system" ) )
{
player trophy_system::ammo_scavenger( weapon );
}
}
}
for ( i = 0; i < primary_weapons.size; i++ )
{
weapon = primary_weapons[i];
if ( !weapon.isScavengable || killstreaks::is_killstreak_weapon( weapon ) )
{
continue;
}
stock = player GetWeaponAmmoStock( weapon );
start = player GetFractionStartAmmo( weapon );
clip = weapon.clipSize;
clip *= GetDvarFloat( "scavenger_clip_multiplier", 1 );
clip = Int( clip );
if ( isdefined( level.weaponLauncherEx41 ) && ( weapon.statIndex == level.weaponLauncherEx41.statIndex ) )
clip = 1;
maxAmmo = weapon.maxAmmo;
if ( stock < maxAmmo - clip )
{
ammo = stock + clip;
player SetWeaponAmmoStock( weapon, ammo );
player.scavenged = true;
}
else
{
player SetWeaponAmmoStock( weapon, maxAmmo );
player.scavenged = true;
}
}
}
function scavenger_hud_destroyOnDisconnect()
{
self waittill("disconnect");
if(isDefined(self.scavenger_icon))
self.scavenger_icon destroy();
}
function scavenger_hud_create()
{
if( level.wagerMatch )
return;
if( ( isdefined( level.noScavenger ) && level.noScavenger ))
return;
self.scavenger_icon = NewClientHudElem( self );
if(isDefined(self.scavenger_icon))
{
self thread scavenger_hud_destroyOnDisconnect();
self.scavenger_icon.horzAlign = "center";
self.scavenger_icon.vertAlign = "middle";
self.scavenger_icon.alpha = 0;
width = 64;
height = 64;
if ( level.splitscreen )
{
width = Int( width * 0.5 );
height = Int( height * 0.5 );
}
self.scavenger_icon.x = -width/2;
self.scavenger_icon.y = 16;
self.scavenger_icon setShader( "hud_scavenger_pickup", width, height );
}
}
function drop_scavenger_for_death( attacker )
{
if ( level.wagerMatch )
return;
if ( !isdefined( attacker ) )
return;
if ( attacker == self )
return;
if ( level.gameType == "hack" )
{
item = self dropScavengerItem( GetWeapon( "scavenger_item_hack" ) );
}
else if ( isplayer( Attacker ) )
{
item = self dropScavengerItem( GetWeapon( "scavenger_item" ) );
}
else
{
return;
}
item thread scavenger_think();
}
// if we need to store multiple drop limited weapons, we'll need to store an array on the player entity
function add_limited_weapon( weapon, owner, num_drops )
{
limited_info = SpawnStruct();
limited_info.weapon = weapon;
limited_info.drops = num_drops;
owner.limited_info = limited_info;
}
function should_drop_limited_weapon( weapon, owner )
{
limited_info = owner.limited_info;
if ( !isdefined( limited_info ) )
{
return true;
}
if ( limited_info.weapon != weapon )
{
return true;
}
if ( limited_info.drops <= 0 )
{
return false;
}
return true;
}
function drop_limited_weapon( weapon, owner, item )
{
limited_info = owner.limited_info;
if ( !isdefined( limited_info ) )
{
return;
}
if ( limited_info.weapon != weapon )
{
return;
}
limited_info.drops = limited_info.drops - 1;
owner.limited_info = undefined;
item thread limited_pickup( limited_info );
}
function limited_pickup( limited_info )
{
self endon( "death" );
self waittill( "trigger", player, item );
if ( !isdefined( item ) )
{
return;
}
player.limited_info = limited_info;
}
// self is the grenade
function track_cooked_detonation( attacker, weapon, cookTime )
{
self endon( "trophy_destroyed" );
self waittill( "explode", origin, surface );
if ( weapon.rootWeapon == level.weaponFlashGrenade )
{
level thread nineBang_doNineBang( attacker, weapon, origin, cookTime );
}
}
function nineBang_doNineBang( attacker, weapon, pos, cookTime )
{
level endon( "game_ended" );
maxStages = 4;
maxRadius = 20;
minDelay = 0.15;
maxdelay = 0.3;
explosionRadiusSq = weapon.explosionRadius * weapon.explosionRadius;
explosionRadiusMinSq = weapon.explosionInnerRadius * weapon.explosionInnerRadius;
cookStages = cookTime / weapon.cookOffHoldTime * maxStages + 1;
detonations = 0;
if ( cookStages < 2 )
{
return;
}
else if ( cookStages < 3 )
{
detonations = 3;
}
else if ( cookStages < 4 )
{
detonations = 6;
}
else // max
{
detonations = 9;
//nineBang_DoEmpDamage( attacker, weapon, pos );
}
wait( RandomFloatRange( minDelay, maxDelay ) );
// the nine bang will go off detonations-1 times as it already exploded once, and flash everything in the vacinity
for( i = 1; i < detonations; i++ )
{
newPos = level nineBang_getSubExplosionPos( pos, maxRadius );
PlaySoundAtPosition( "wpn_flash_grenade_explode", newPos );
PlayFX( level._effect["flashNineBang"], newPos );
closestPlayers = ArraySort( level.players, newPos, true );
// get players within the radius
foreach( player in closestPlayers )
{
if ( !isdefined( player ) || !IsAlive( player ) )
{
continue;
}
if ( player.sessionstate != "playing" )
{
continue;
}
viewOrigin = player GetEye();
// first make sure they are within distance
dist = DistanceSquared( pos, viewOrigin );
if( dist > explosionRadiusSq )
{
break;
}
// now make sure they can be hit by it
if( !BulletTracePassed( pos, viewOrigin, false, player ) )
continue;
if ( dist <= explosionRadiusMinSq )
percent_distance = 1.0;
else
percent_distance = 1.0 - ( dist - explosionRadiusMinSq ) / ( explosionRadiusSq - explosionRadiusMinSq );
forward = AnglesToForward( player GetPlayerAngles() );
toBlast = pos - viewOrigin;
toBlast = VectorNormalize( toBlast );
percent_angle = 0.5 * ( 1.0 + VectorDot( forward, toBlast ) );
player notify( "flashbang", percent_distance, percent_angle, attacker );
}
wait( RandomFloatRange( minDelay, maxDelay ) );
}
}
function nineBang_getSubExplosionPos( startPos, range )
{
offset = ( RandomFloatRange( -1.0 * range, range ), RandomFloatRange( -1.0 * range, range ), 0 );
newPos = startPos + offset;
// make sure we don't spawn through walls
if ( BulletTracePassed( startPos, newPos, false, undefined ) )
{
return newPos;
}
return startPos;
}
function nineBang_DoEmpDamage( player, weapon, position )
{
kNineBangEmpRadius = 512;
radiusSq = kNineBangEmpRadius * kNineBangEmpRadius;
PlaySoundAtPosition( "wpn_emp_explode", position );
level empgrenade::empExplosionDamageEnts( player, weapon, position, kNineBangEmpRadius, false );
foreach ( targetEnt in level.players )
{
if ( nineBang_empCanDamage( targetEnt, position, radiusSq, false, 0 ) )
{
targetEnt notify( "emp_grenaded", player );
}
}
}
function nineBang_empCanDamage( ent, pos, radiusSq, doLOS, startRadius )
{
entpos = ent.origin;
distSq = DistanceSquared( pos, entpos );
return ( distSq < radiusSq
&& ( !doLOS || weapons::weaponDamageTracePassed( pos, entpos, startRadius, ent ) ) );
}
function track_multi_detonation( ownerEnt, weapon, cookTime ) // self is the grenade
{
self endon( "trophy_destroyed" );
self waittill( "explode", origin, surface );
if ( weapon.rootWeapon == GetWeapon( "frag_grenade_grenade" ) )
{
for ( i = 0; i < weapon.multiDetonation; i++ )
{
// spawn a magic grenade
if ( !isdefined( ownerEnt ) )
{
return;
}
multiblastWeapon = GetWeapon( "frag_multi_blast" );
// get a velocity
dir = level multi_detonation_get_cluster_launch_dir( i, weapon.multiDetonation );
vel = dir * multiblastWeapon.multiDetonationFragmentSpeed;
fuseTime = multiblastWeapon.fusetime/1000;
grenade = ownerEnt MagicGrenadeType( multiblastWeapon, origin, vel, fuseTime );
util::wait_network_frame();
}
}
}
function multi_detonation_get_cluster_launch_dir( index, multiVal )
{
pitch = 45;
yaw = -180 + ( 360 / multiVal ) * index;
angles = ( pitch, yaw, 45 );
dir = AnglesToForward( angles );
return dir;
}
function should_suppress_damage( weapon, inflictor )
{
if ( !isdefined( weapon ) )
return false;
if ( !isdefined( self ) )
return false;
if ( isdefined( level.weaponSpecialDiscGun ) && weapon.statIndex == level.weaponSpecialDiscGun.statIndex )
{
if ( isdefined( inflictor ) )
{
if(!isdefined(inflictor.hit_info))inflictor.hit_info=[];
victimEntNum = self GetEntityNumber();
if ( isdefined( inflictor.hit_info[ victimEntNum ] ) )
return true;
inflictor.hit_info[ victimEntNum ] = true;
}
}
return false;
}