fungame #1
@@ -1,75 +0,0 @@
|
|||||||
#include common_scripts\utility;
|
|
||||||
#include maps\mp\_utility;
|
|
||||||
#include maps\mp\gametypes\_hud_util;
|
|
||||||
|
|
||||||
// For sending data, you use - as split character and for response I use | as split character
|
|
||||||
// all-10 => top 10 guids split by |
|
|
||||||
|
|
||||||
Init()
|
|
||||||
{
|
|
||||||
if(true) // unfinished script
|
|
||||||
return;
|
|
||||||
//level.api = "http://178.63.44.165:80831/";
|
|
||||||
level.api = "";
|
|
||||||
level.chatCommands = [];
|
|
||||||
level.chatCommands[level.chatCommands.size] = "register";
|
|
||||||
level.chatCommands[level.chatCommands.size] = "increment";
|
|
||||||
level.chatCommands[level.chatCommands.size] = "all";
|
|
||||||
level.chatCommands[level.chatCommands.size] = "read";
|
|
||||||
thread onSay();
|
|
||||||
thread onPlayerConnect();
|
|
||||||
}
|
|
||||||
onPlayerConnect()
|
|
||||||
{
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
level waittill("connected", player);
|
|
||||||
player thread tryCallApi("register");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onSay()
|
|
||||||
{
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
level waittill("say", string, player);
|
|
||||||
iprintlnBold(string + " " + player.name);
|
|
||||||
player thread checkString(string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkString(string)
|
|
||||||
{
|
|
||||||
if(string[0] != "!" && string[0] != "-")
|
|
||||||
return;
|
|
||||||
string = GetSubStr(string,1); //GetSubStr( <string>, <start index>, <end index> )
|
|
||||||
iPrintln(string);
|
|
||||||
cmd = strTok(string, " ");
|
|
||||||
foreach(chat in level.chatCommands)
|
|
||||||
{
|
|
||||||
if(chat == cmd[0])
|
|
||||||
self callApi(chat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callApi(string)
|
|
||||||
{
|
|
||||||
list = httpGet(level.api + "");
|
|
||||||
list waittill("done", success, data);
|
|
||||||
if(!success)
|
|
||||||
return "error";
|
|
||||||
iPrintln("^2green");
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
tryCallApi(parameter)
|
|
||||||
{
|
|
||||||
data = self callApi("!" + parameter + "-" + self.guid);
|
|
||||||
if(!isDefined(data))
|
|
||||||
self iPrintlnBold(parameter + " was successful");
|
|
||||||
else if(data == "error")
|
|
||||||
self iPrintlnBold("^1error calling the api");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wait 2;
|
|
||||||
ar = strTok(data, "|");
|
|
||||||
foreach(a in ar)
|
|
||||||
self iPrintlnBold(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -89,7 +89,7 @@ initializeGametype(type) // called in vote.gsc after first map
|
|||||||
setDvar("global_health", 60);
|
setDvar("global_health", 60);
|
||||||
setDvar("speed", 1.5);
|
setDvar("speed", 1.5);
|
||||||
setDvar("streaks_online", 1);
|
setDvar("streaks_online", 1);
|
||||||
setDvar("jump_height", 0.5);
|
setDvar("jump_height", 70);
|
||||||
setDvar("amount_weapons", 0); // if 0 uses whole list of possible guns, if set > 0 uses amount larger than 0
|
setDvar("amount_weapons", 0); // if 0 uses whole list of possible guns, if set > 0 uses amount larger than 0
|
||||||
setDvar("gun_kills", 1);
|
setDvar("gun_kills", 1);
|
||||||
break;
|
break;
|
||||||
@@ -147,12 +147,26 @@ loadSettings()
|
|||||||
|
|
||||||
// Bot Management
|
// Bot Management
|
||||||
setDvar("bots_main", 1);
|
setDvar("bots_main", 1);
|
||||||
setDvar("bots_manage_fill", 12);
|
setDvar("bots_manage_fill", 10); // total slots: players + bots = 10
|
||||||
setDvar("bots_skill", 5);
|
setDvar("bots_manage_fill_mode", 0); // mode 0 = count players AND bots
|
||||||
|
setDvar("bots_manage_fill_kick", 1); // kick a bot when a human pushes count over 10
|
||||||
|
// Skill: 2 hard bots (1 per internal team), rest are brain dead
|
||||||
|
setDvar("bots_skill", 8);
|
||||||
|
setDvar("bots_skill_allies_hard", 1);
|
||||||
|
setDvar("bots_skill_allies_med", 0);
|
||||||
|
setDvar("bots_skill_axis_hard", 1);
|
||||||
|
setDvar("bots_skill_axis_med", 0);
|
||||||
setDvar("bots_play_knife", 0);
|
setDvar("bots_play_knife", 0);
|
||||||
setDvar("bots_main_chat", 0);
|
setDvar("bots_main_chat", 0);
|
||||||
|
|
||||||
SetDvarIfUninitialized("scr_nuke_enabled", 1);
|
SetDvarIfUninitialized("scr_nuke_enabled", 1);
|
||||||
|
// Cache mode flags as level vars — avoids repeated getDvar() in hot per-player loops.
|
||||||
|
level.isTeamGame = (getDvar("g_gametype") == "gungame_team");
|
||||||
|
level.isKillConfirmed = (getDvar("gunmode") == "Kill Confirmed");
|
||||||
|
// Suppress engine-level developer prints (e.g. "Replacing perk X in slot Y with Z").
|
||||||
|
// These come from native C code and cannot be silenced any other way.
|
||||||
|
// developer 0 is standard for production servers.
|
||||||
|
setDvar("developer", 0);
|
||||||
}
|
}
|
||||||
deleteSentries()
|
deleteSentries()
|
||||||
{
|
{
|
||||||
@@ -308,8 +322,20 @@ loadSetup()
|
|||||||
self setMoveSpeedScale(getDvarFloat("speed"));
|
self setMoveSpeedScale(getDvarFloat("speed"));
|
||||||
self maps\mp\perks\_perks::givePerk("specialty_fastreload"); // due to icys request :)
|
self maps\mp\perks\_perks::givePerk("specialty_fastreload"); // due to icys request :)
|
||||||
self maps\mp\perks\_perks::givePerk("specialty_falldamage"); // due to icys request :)
|
self maps\mp\perks\_perks::givePerk("specialty_falldamage"); // due to icys request :)
|
||||||
self maps\mp\perks\_perks::givePerk("specialty_quickdraw");
|
self maps\mp\perks\_perks::givePerk("specialty_quickdraw");
|
||||||
|
// Static HUD dvars set once per spawn — moved from watchHUD's 1-second polling loop.
|
||||||
|
// These values never change mid-game so there is no need to re-apply them every second.
|
||||||
|
self setClientDvar("cg_drawRadar", 1);
|
||||||
|
self setClientDvar("cg_drawStance", 0);
|
||||||
|
self setClientDvar("cg_drawTeamScores", 0);
|
||||||
|
self setClientDvar("cg_drawKillfeed", 0);
|
||||||
|
self setClientDvar("cg_drawBreathHint", 0);
|
||||||
|
self setClientDvar("cg_drawMantleHint", 0);
|
||||||
|
self setClientDvar("cg_drawTurretCrosshair", 0);
|
||||||
|
self setClientDvar("cg_cursorHints", 0);
|
||||||
|
// Keep g_hardcore=1 for server-side gameplay rules but show the normal client HUD
|
||||||
|
// so our custom health/weapon overlays render correctly without fighting hardcore suppression.
|
||||||
|
self setClientDvar("ui_hud_hardcore", 0);
|
||||||
self thread takeInvalidWeapon();
|
self thread takeInvalidWeapon();
|
||||||
if(level.state == "prematch")
|
if(level.state == "prematch")
|
||||||
{
|
{
|
||||||
@@ -343,6 +369,16 @@ getEnemyTeam()
|
|||||||
}
|
}
|
||||||
updateWeapon()
|
updateWeapon()
|
||||||
{
|
{
|
||||||
|
// Safety: end this thread if the match is over or the player is gone/dead.
|
||||||
|
level endon("nuke");
|
||||||
|
self endon("disconnect");
|
||||||
|
self endon("death");
|
||||||
|
// Exclusivity guard: notifying "updateWeapon" kills any previously running
|
||||||
|
// instance of this function on this player, then we register to die the same
|
||||||
|
// way when the NEXT call arrives. Only one updateWeapon thread per player.
|
||||||
|
self notify("updateWeapon");
|
||||||
|
self endon("updateWeapon");
|
||||||
|
|
||||||
if(self.current > (level.gungameList.size - 1))
|
if(self.current > (level.gungameList.size - 1))
|
||||||
{
|
{
|
||||||
self thread tryNuke();
|
self thread tryNuke();
|
||||||
@@ -378,14 +414,30 @@ updateWeapon()
|
|||||||
if(isDefined(self.pers["isBot"]) && self.pers["isBot"] && getDvar("gunmode") == "Fungame")
|
if(isDefined(self.pers["isBot"]) && self.pers["isBot"] && getDvar("gunmode") == "Fungame")
|
||||||
{
|
{
|
||||||
if(level.gungameList[self.current] == "riotshield_mp")
|
if(level.gungameList[self.current] == "riotshield_mp")
|
||||||
|
{
|
||||||
self setClientDvar("bots_play_knife", 1);
|
self setClientDvar("bots_play_knife", 1);
|
||||||
|
// Bots can't trigger the riot shield melee button, so we simulate it.
|
||||||
|
self thread watchBotRiotShield();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
self setClientDvar("bots_play_knife", 0);
|
self setClientDvar("bots_play_knife", 0);
|
||||||
|
self notify("botshield"); // shut down any running shield bash thread
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// NOTE: The old recursive self-call "self updateWeapon()" was removed here.
|
||||||
|
// It caused unbounded call-stack growth when getCurrentWeapon() returned "none".
|
||||||
|
// takeInvalidWeapon() polls every frame and will re-issue the thread if needed.
|
||||||
|
|
||||||
if(self getCurrentWeapon() == "none" && !self isMantling() && !self isOnLadder()) // in rare case weapon does not exist
|
// Bounded retry: the engine may need a few frames after switchtoweaponimmediate
|
||||||
|
// to register the active weapon. Loop up to 8 frames re-issuing the switch.
|
||||||
|
// This is the safe replacement for the old unbounded recursive call.
|
||||||
|
retries = 0;
|
||||||
|
while(self getCurrentWeapon() == "none" && !self isMantling() && !self isOnLadder() && retries < 8)
|
||||||
{
|
{
|
||||||
self updateWeapon();
|
self switchtoweaponimmediate(level.gungameList[self.current]);
|
||||||
|
waitFrame();
|
||||||
|
retries++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refillOnFire()
|
refillOnFire()
|
||||||
@@ -399,28 +451,29 @@ refillOnFire()
|
|||||||
self giveMaxAmmo(weapon);
|
self giveMaxAmmo(weapon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onKilling() {
|
onKilling() {
|
||||||
self endon("disconnect");
|
self endon("disconnect");
|
||||||
level endon("nuke");
|
level endon("nuke");
|
||||||
self.multiplier = 0;
|
self.multiplier = 0;
|
||||||
self.amount = 0;
|
self.amount = 0;
|
||||||
//kills = self.pers["kills"];
|
|
||||||
kills = 0;
|
kills = 0;
|
||||||
refreshCounter = 0;
|
refreshCounter = 0;
|
||||||
self.scoretext = self createText("hudbig", 1, "CENTER", "CENTER", 0, 0, .7,"");
|
// Cache settings that never change mid-match — eliminates getDvar()/getDvarInt()
|
||||||
self.scoretext_amount = self createText("hudbig", 1, "CENTER", "CENTER", -10, 20, .7,"");
|
// native calls inside this loop which runs every 0.3s per player.
|
||||||
self.multitext = [];
|
killsPerWeapon = getDvarInt("gun_kills", 1);
|
||||||
for(i=2;i<6;i++)
|
self.scoretext = self createText("hudbig", 1, "CENTER", "CENTER", 0, 0, .7,"");
|
||||||
self.multitext[i] = self createText("hudbig", .6, "CENTER", "CENTER", 50, (i * 20), .7,"");
|
self.scoretext_amount = self createText("hudbig", 1, "CENTER", "CENTER", -10, 20, .7,"");
|
||||||
|
self.multitext = [];
|
||||||
|
for(i=2;i<6;i++)
|
||||||
|
self.multitext[i] = self createText("hudbig", .6, "CENTER", "CENTER", 50, (i * 20), .7,"");
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
wait .3;
|
wait .3;
|
||||||
if(kills > self.gungameKills) // not called on team gungame
|
if(kills > self.gungameKills) // not called on team gungame
|
||||||
{
|
{
|
||||||
self thread scorepopup(-100);
|
self thread scorepopup(-100);
|
||||||
kills--;
|
kills--;
|
||||||
refreshCounter++;
|
refreshCounter++;
|
||||||
killsPerWeapon = getDvarInt("gun_kills", 1);
|
|
||||||
if(killsPerWeapon > 1)
|
if(killsPerWeapon > 1)
|
||||||
{
|
{
|
||||||
killsInGun = (self.gungameKills % killsPerWeapon);
|
killsInGun = (self.gungameKills % killsPerWeapon);
|
||||||
@@ -443,10 +496,9 @@ onKilling() {
|
|||||||
kills++;
|
kills++;
|
||||||
refreshCounter++;
|
refreshCounter++;
|
||||||
self thread scorepopup(100);
|
self thread scorepopup(100);
|
||||||
self.streaking++;
|
self.streaking++;
|
||||||
if(getDvar("g_gametype") != "gungame_team")
|
if(!level.isTeamGame) // cached in loadSettings — avoids getDvar() per kill
|
||||||
{
|
{
|
||||||
killsPerWeapon = getDvarInt("gun_kills", 1);
|
|
||||||
if(killsPerWeapon > 1)
|
if(killsPerWeapon > 1)
|
||||||
{
|
{
|
||||||
if(self.gungameKills % killsPerWeapon == 0)
|
if(self.gungameKills % killsPerWeapon == 0)
|
||||||
@@ -469,7 +521,7 @@ onKilling() {
|
|||||||
self thread initCreateMarkerIcon();
|
self thread initCreateMarkerIcon();
|
||||||
self refreshCounter(refreshCounter);
|
self refreshCounter(refreshCounter);
|
||||||
self updateRatio();
|
self updateRatio();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateRatio()
|
updateRatio()
|
||||||
@@ -567,8 +619,8 @@ scorepopup(amount)
|
|||||||
self.scoretext.color = color;
|
self.scoretext.color = color;
|
||||||
self.scoretext.glowColor = glowColor;
|
self.scoretext.glowColor = glowColor;
|
||||||
self.scoretext SetPulseFX( 40, 2000, 600 );
|
self.scoretext SetPulseFX( 40, 2000, 600 );
|
||||||
if(getDvar("gunmode") == "Kill Confirmed")
|
if(level.isKillConfirmed) // cached in loadSettings — avoids getDvar() per popup
|
||||||
self.scoretext setText("Upgraded!^3");
|
self.scoretext setText("Upgraded!^3");
|
||||||
else
|
else
|
||||||
self.scoretext setText("Killed!^3");
|
self.scoretext setText("Killed!^3");
|
||||||
self.scoretext_amount.color = color;
|
self.scoretext_amount.color = color;
|
||||||
@@ -624,9 +676,15 @@ lowerMultitext(multiplier)
|
|||||||
}
|
}
|
||||||
tryNuke()
|
tryNuke()
|
||||||
{
|
{
|
||||||
if(isDefined(level.nukeIncoming) || level.state == "aftermatch")
|
// Use level.nukeTriggered as OUR re-entry guard.
|
||||||
|
// DO NOT use level.nukeIncoming here — that flag is owned by the engine's
|
||||||
|
// _nuke.gsc::tryUseNuke(). Setting it before calling tryUseNuke causes the
|
||||||
|
// engine to see "nuke already on its way" and abort without firing the nuke.
|
||||||
|
if(isDefined(level.nukeTriggered) || level.state == "aftermatch")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Set our custom flag and state immediately (atomic — no yield before this).
|
||||||
|
level.nukeTriggered = true;
|
||||||
level.state = "aftermatch";
|
level.state = "aftermatch";
|
||||||
|
|
||||||
if(getDvarInt("scr_nuke_enabled", 1) == 0)
|
if(getDvarInt("scr_nuke_enabled", 1) == 0)
|
||||||
@@ -654,9 +712,10 @@ tryNuke()
|
|||||||
}
|
}
|
||||||
createUI()
|
createUI()
|
||||||
{
|
{
|
||||||
self thread createWeaponHud();
|
self thread createWeaponHud();
|
||||||
self thread createKillHud();
|
self thread createKillHud();
|
||||||
self thread Weaponnumber();
|
// Weaponnumber() removed — marked deprecated, waits on "update_weaponNumber"
|
||||||
|
// which is never notified anywhere. Was a zombie thread per player.
|
||||||
self thread createRatioHud();
|
self thread createRatioHud();
|
||||||
}
|
}
|
||||||
createWeaponHud()
|
createWeaponHud()
|
||||||
@@ -770,9 +829,9 @@ takeInvalidWeapon()
|
|||||||
level endon("nuke");
|
level endon("nuke");
|
||||||
counter = 0;
|
counter = 0;
|
||||||
wait 3;
|
wait 3;
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
waitFrame();
|
wait 0.1; // was waitFrame() (~60/s) — 10/s is ample for a safety-net poller
|
||||||
if(!isAlive(self))
|
if(!isAlive(self))
|
||||||
continue;
|
continue;
|
||||||
if(self isMantling())
|
if(self isMantling())
|
||||||
@@ -1113,13 +1172,20 @@ watchHealthHUD()
|
|||||||
self.healthHUD setPoint("BOTTOM RIGHT", "BOTTOM RIGHT", -10, -10);
|
self.healthHUD setPoint("BOTTOM RIGHT", "BOTTOM RIGHT", -10, -10);
|
||||||
self.healthHUD.label = &"HP: ";
|
self.healthHUD.label = &"HP: ";
|
||||||
|
|
||||||
|
lastHealth = -1; // sentinel: forces first-tick update
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
self.healthHUD setValue(self.actual_health);
|
// Only push to the HUD element when the value actually changed.
|
||||||
if(self.actual_health < (self.actual_maxhealth * 0.3))
|
// Eliminates 10 setValue() native calls/sec when player is at full health.
|
||||||
self.healthHUD.color = (1, 0, 0);
|
if(self.actual_health != lastHealth)
|
||||||
else
|
{
|
||||||
self.healthHUD.color = (1, 1, 1);
|
lastHealth = self.actual_health;
|
||||||
|
self.healthHUD setValue(self.actual_health);
|
||||||
|
if(self.actual_health < (self.actual_maxhealth * 0.3))
|
||||||
|
self.healthHUD.color = (1, 0, 0);
|
||||||
|
else
|
||||||
|
self.healthHUD.color = (1, 1, 1);
|
||||||
|
}
|
||||||
wait 0.1;
|
wait 0.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1150,6 +1216,7 @@ watchDeagleGL()
|
|||||||
{
|
{
|
||||||
level endon("nuke");
|
level endon("nuke");
|
||||||
self endon("disconnect");
|
self endon("disconnect");
|
||||||
|
self endon("death"); // prevent thread accumulation across respawns
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
self waittill("weapon_fired", weaponName);
|
self waittill("weapon_fired", weaponName);
|
||||||
@@ -1173,20 +1240,14 @@ watchHUD()
|
|||||||
{
|
{
|
||||||
level endon("nuke");
|
level endon("nuke");
|
||||||
self endon("disconnect");
|
self endon("disconnect");
|
||||||
|
self endon("death"); // prevent thread accumulation across respawns
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
|
// Only re-apply dvars the engine may reset mid-game (radar via UAV, ammo via class events).
|
||||||
|
// ui_hud_hardcore is set to 0 once per spawn in loadSetup() and does not need refreshing.
|
||||||
self setClientDvar("ui_drawradar", 1);
|
self setClientDvar("ui_drawradar", 1);
|
||||||
self setClientDvar("cg_drawRadar", 1);
|
self setClientDvar("cg_drawAmmo", 1);
|
||||||
self setClientDvar("cg_drawAmmo", 1); // Force ammo back on even in hardcore
|
wait 5;
|
||||||
self setClientDvar("cg_drawStance", 0);
|
|
||||||
self setClientDvar("cg_drawTeamScores", 0);
|
|
||||||
self setClientDvar("cg_drawKillfeed", 0);
|
|
||||||
self setClientDvar("ui_hud_hardcore", 1);
|
|
||||||
self setClientDvar("cg_drawBreathHint", 0);
|
|
||||||
self setClientDvar("cg_drawMantleHint", 0);
|
|
||||||
self setClientDvar("cg_drawTurretCrosshair", 0);
|
|
||||||
self setClientDvar("cg_cursorHints", 0);
|
|
||||||
wait 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1194,6 +1255,7 @@ watchM40A3()
|
|||||||
{
|
{
|
||||||
level endon("nuke");
|
level endon("nuke");
|
||||||
self endon("disconnect");
|
self endon("disconnect");
|
||||||
|
self endon("death"); // prevent thread accumulation across respawns
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
self waittill("weapon_fired", weaponName);
|
self waittill("weapon_fired", weaponName);
|
||||||
@@ -1216,3 +1278,55 @@ watchM40A3()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scripted riot shield bash for bots.
|
||||||
|
// Bots cannot press the melee button to bash with the riot shield, so this thread
|
||||||
|
// checks proximity + forward arc every 0.8s and applies damage directly,
|
||||||
|
// matching the real shield bash range (~85 units) and cooldown (~0.8s).
|
||||||
|
watchBotRiotShield()
|
||||||
|
{
|
||||||
|
level endon("nuke");
|
||||||
|
self endon("death");
|
||||||
|
self endon("disconnect");
|
||||||
|
// Exclusivity guard: kill any previous instance when weapon changes re-trigger this.
|
||||||
|
self notify("botshield");
|
||||||
|
self endon("botshield");
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
wait 0.8; // ~length of real shield bash animation / cooldown
|
||||||
|
|
||||||
|
if(self getCurrentWeapon() != "riotshield_mp")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
selfPos = self getOrigin();
|
||||||
|
forward = anglesToForward(self getPlayerAngles());
|
||||||
|
|
||||||
|
foreach(player in level.players)
|
||||||
|
{
|
||||||
|
if(player == self) continue;
|
||||||
|
if(!isAlive(player)) continue;
|
||||||
|
|
||||||
|
toPlayer = player getOrigin() - selfPos;
|
||||||
|
dist = Length(toPlayer);
|
||||||
|
|
||||||
|
if(dist > 85) continue; // riot shield melee range
|
||||||
|
|
||||||
|
// Dot product: check player is within ~75 degree forward arc.
|
||||||
|
nx = toPlayer[0] / dist;
|
||||||
|
ny = toPlayer[1] / dist;
|
||||||
|
nz = toPlayer[2] / dist;
|
||||||
|
dot = (forward[0] * nx) + (forward[1] * ny) + (forward[2] * nz);
|
||||||
|
|
||||||
|
if(dot > 0.25) // 0.25 ~= 75 degree half-angle
|
||||||
|
{
|
||||||
|
// Apply riot shield bash damage (100 = standard MW2 shield bash)
|
||||||
|
player thread maps\mp\gametypes\_damage::finishPlayerDamageWrapper(
|
||||||
|
self, self, 100, 0, "MOD_MELEE", "riotshield_mp",
|
||||||
|
player getOrigin(), player getOrigin(), "none", 0, 0
|
||||||
|
);
|
||||||
|
break; // one target per swing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ loadStreaks()
|
|||||||
precacheShader("cardicon_cod4");
|
precacheShader("cardicon_cod4");
|
||||||
precacheShader("cardicon_loadedfinger");
|
precacheShader("cardicon_loadedfinger");
|
||||||
|
|
||||||
|
// Pre-cache Jetpack FX at level init (loadfx must NOT be called at runtime).
|
||||||
|
level._effect["jetpack_smoke"] = loadfx("smoke/smoke_trail_white_heli");
|
||||||
|
level._effect["jetpack_flare"] = loadfx("misc/flares_cobra");
|
||||||
|
|
||||||
level.streaks3 = [];
|
level.streaks3 = [];
|
||||||
level.streaks6 = [];
|
level.streaks6 = [];
|
||||||
level.streaks9 = [];
|
level.streaks9 = [];
|
||||||
@@ -231,11 +235,9 @@ giveStreak(streak)
|
|||||||
self.speed = true;
|
self.speed = true;
|
||||||
wait 1;
|
wait 1;
|
||||||
self maps\mp\perks\_perks::givePerk("specialty_lightweight");
|
self maps\mp\perks\_perks::givePerk("specialty_lightweight");
|
||||||
self maps\mp\perks\_perks::givePerk("specialty_lightweight");
|
|
||||||
self setMoveSpeedScale(1.6);
|
self setMoveSpeedScale(1.6);
|
||||||
self.setMoveSpeedScale = 1.6;
|
self.setMoveSpeedScale = 1.6;
|
||||||
//self thread spawnFireLoop();
|
break;
|
||||||
break;
|
|
||||||
case "Riotshield":
|
case "Riotshield":
|
||||||
self AttachShieldModel( "weapon_riot_shield_mp", "tag_shield_back" );
|
self AttachShieldModel( "weapon_riot_shield_mp", "tag_shield_back" );
|
||||||
break;
|
break;
|
||||||
@@ -260,6 +262,7 @@ giveStreak(streak)
|
|||||||
break;
|
break;
|
||||||
case "No Reload":
|
case "No Reload":
|
||||||
self thread NoReload();
|
self thread NoReload();
|
||||||
|
break; // FIX: was missing break, causing fall-through into Radioactive every time
|
||||||
case "Radioactive":
|
case "Radioactive":
|
||||||
self thread Radioactive();
|
self thread Radioactive();
|
||||||
break;
|
break;
|
||||||
@@ -273,30 +276,26 @@ Radioactive()
|
|||||||
self endon("disconnect");
|
self endon("disconnect");
|
||||||
self endon("death");
|
self endon("death");
|
||||||
level endon("nuke");
|
level endon("nuke");
|
||||||
playFxOnTag( level.spawnGlow["enemy"], self, "pelvis" );
|
// Cache once \u2014 getDvar() inside the inner foreach would cost ~120 native calls/sec.
|
||||||
|
isTeamGame = (getDvar("g_gametype") == "gungame_team");
|
||||||
|
playFxOnTag( level.spawnGlow["enemy"], self, "pelvis" );
|
||||||
playFxOnTag( level.spawnGlow["friendly"], self, "j_head" );
|
playFxOnTag( level.spawnGlow["friendly"], self, "j_head" );
|
||||||
//self SetPlayerIgnoreRadiusDamage( true );
|
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
wait .1;
|
wait .1;
|
||||||
//self RadiusDamage(self.origin,120,50,40,self,"MOD_Explosive","nuke_mp");
|
|
||||||
//RadiusDamage(self.origin, 50, 30, 20, self );
|
|
||||||
foreach(player in level.players)
|
foreach(player in level.players)
|
||||||
{
|
{
|
||||||
if(player == self)
|
if(player == self)
|
||||||
continue;
|
continue;
|
||||||
if(getDvar("g_gametype") == "gungame_team")
|
if(isTeamGame && player.team == self.team)
|
||||||
{
|
continue;
|
||||||
if(player.team == self.team)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(Distance(player.origin,self.origin) < 120 && isAlive(player))
|
if(Distance(player.origin,self.origin) < 120 && isAlive(player))
|
||||||
{
|
{
|
||||||
player thread maps\mp\gametypes\_damage::finishPlayerDamageWrapper( self, self, 4, 0, "MOD_EXPLOSIVE", "none", player.origin, player.origin, "none", 0, 0 );
|
player thread maps\mp\gametypes\_damage::finishPlayerDamageWrapper( self, self, 4, 0, "MOD_EXPLOSIVE", "none", player.origin, player.origin, "none", 0, 0 );
|
||||||
self thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback("");
|
self thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Suicidebomber()
|
Suicidebomber()
|
||||||
{
|
{
|
||||||
@@ -337,9 +336,7 @@ Jetpack()
|
|||||||
self iPrintlnBold("^3Press ^1F ^7to use ^:Jetpack!");
|
self iPrintlnBold("^3Press ^1F ^7to use ^:Jetpack!");
|
||||||
self.jetpack = 80;
|
self.jetpack = 80;
|
||||||
self maps\mp\perks\_perks::givePerk("specialty_falldamage");
|
self maps\mp\perks\_perks::givePerk("specialty_falldamage");
|
||||||
self.fx = [];
|
// Use pre-cached FX handles from loadStreaks() — loadfx() must not be called at runtime.
|
||||||
self.fx[0] = loadfx ("smoke/smoke_trail_white_heli");
|
|
||||||
self.fx[1] = loadfx( "misc/flares_cobra" );
|
|
||||||
JETPACKBACK = createPrimaryProgressBar( -275 );
|
JETPACKBACK = createPrimaryProgressBar( -275 );
|
||||||
JETPACKBACK.bar.x = 40;
|
JETPACKBACK.bar.x = 40;
|
||||||
JETPACKBACK.x = 100;
|
JETPACKBACK.x = 100;
|
||||||
@@ -358,12 +355,10 @@ Jetpack()
|
|||||||
{
|
{
|
||||||
if(self usebuttonpressed() && self.jetpack>0)
|
if(self usebuttonpressed() && self.jetpack>0)
|
||||||
{
|
{
|
||||||
//self playsound("veh_ac130_sonic_boom");
|
|
||||||
//self playsound("veh_mig29_sonic_boom");
|
|
||||||
self playsound("cobra_helicopter_dying_loop");
|
self playsound("cobra_helicopter_dying_loop");
|
||||||
self setstance("crouch");
|
self setstance("crouch");
|
||||||
playfx(self.fx[0],self gettagorigin("j_spine4"));
|
playfx(level._effect["jetpack_smoke"],self gettagorigin("j_spine4"));
|
||||||
playfx(self.fx[1],self gettagorigin("j_spine4"));
|
playfx(level._effect["jetpack_flare"],self gettagorigin("j_spine4"));
|
||||||
earthquake(.15,.2,self gettagorigin("j_spine4"),50);
|
earthquake(.15,.2,self gettagorigin("j_spine4"),50);
|
||||||
self.jetpack--;
|
self.jetpack--;
|
||||||
if(self getvelocity()[2]<300)
|
if(self getvelocity()[2]<300)
|
||||||
@@ -425,8 +420,12 @@ DeleteIMS(ims)
|
|||||||
|
|
||||||
DeleteIMS2(ims)
|
DeleteIMS2(ims)
|
||||||
{
|
{
|
||||||
level waittill("fuckemp");
|
// FIX: The original waited on "fuckemp" which was never notified anywhere —
|
||||||
ims delete();
|
// this created a permanent zombie thread holding the ims entity reference.
|
||||||
|
// Now we wait for nuke (end of match) and clean up the entity then.
|
||||||
|
level waittill("nuke");
|
||||||
|
if(isDefined(ims))
|
||||||
|
ims delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteIt(block, block2, block3, block4)
|
DeleteIt(block, block2, block3, block4)
|
||||||
@@ -647,8 +646,11 @@ Juggernaut()
|
|||||||
{
|
{
|
||||||
level endon("nuke");
|
level endon("nuke");
|
||||||
self.isJugger = true;
|
self.isJugger = true;
|
||||||
self.maxhealth = 400;
|
// FIX: Use the custom health system (actual_maxhealth/actual_health) instead of
|
||||||
self.health = self.maxhealth;
|
// the raw engine maxhealth. Setting engine maxhealth directly broke the HUD display
|
||||||
|
// and regen logic. We double the effective HP pool through the custom system.
|
||||||
|
self.actual_maxhealth = self.maxhp * 5; // 5× base HP (e.g. 300 in Fungame vs normal 60)
|
||||||
|
self.actual_health = self.actual_maxhealth;
|
||||||
self setMoveSpeedScale(.7);
|
self setMoveSpeedScale(.7);
|
||||||
|
|
||||||
juggIcon = newHudElem();
|
juggIcon = newHudElem();
|
||||||
@@ -673,6 +675,7 @@ destroyJuggOnNuke(juggIcon)
|
|||||||
}
|
}
|
||||||
NoReload()
|
NoReload()
|
||||||
{
|
{
|
||||||
|
level endon("nuke"); // FIX: was missing — thread survived past match end
|
||||||
self endon("death");
|
self endon("death");
|
||||||
self endon("disconnect");
|
self endon("disconnect");
|
||||||
while(true)
|
while(true)
|
||||||
|
|||||||
@@ -428,8 +428,15 @@ startVote()
|
|||||||
level.elems setPoint("CENTER", "CENTER", 20,-75);
|
level.elems setPoint("CENTER", "CENTER", 20,-75);
|
||||||
level.elems.hideWhenInMenu = true;
|
level.elems.hideWhenInMenu = true;
|
||||||
//level.elems = [];
|
//level.elems = [];
|
||||||
|
// FIX: Added maxRetries guard. If the map pool is smaller than maxOptions, the
|
||||||
|
// i-- retry would loop forever (no wait = instant VM instruction-count crash).
|
||||||
|
maxRetries = level.maps.size * 3;
|
||||||
|
retries = 0;
|
||||||
for(i = 0;i < level.maxOptions;i++)
|
for(i = 0;i < level.maxOptions;i++)
|
||||||
{
|
{
|
||||||
|
retries++;
|
||||||
|
if(retries > maxRetries)
|
||||||
|
break; // pool exhausted: accept fewer options rather than loop forever
|
||||||
valid = true;
|
valid = true;
|
||||||
map = level.maps[randomInt(level.maps.size-1)];
|
map = level.maps[randomInt(level.maps.size-1)];
|
||||||
gamemode = level.gungamemodes[randomInt(level.gungamemodes.size)];
|
gamemode = level.gungamemodes[randomInt(level.gungamemodes.size)];
|
||||||
|
|||||||
Reference in New Issue
Block a user