#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 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; }