This commit is contained in:
2026-05-06 19:28:37 +02:00
parent 01448a33a6
commit c52ce856c3
4 changed files with 59 additions and 90 deletions

View File

@@ -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);
}
}

View File

@@ -90,7 +90,7 @@ initializeGametype(type) // called in vote.gsc after first map
setDvar("speed", 1.5);
setDvar("streaks_online", 1);
setDvar("jump_height", 0.5);
setDvar("amount_weapons", 0); // if 0 uses whole list of possible guns, if set > 0 uses amount larger than 0
setDvar("amount_weapons", 10); // if 0 uses whole list of possible guns, if set > 0 uses amount larger than 0
setDvar("gun_kills", 1);
break;
default: // not required
@@ -343,6 +343,16 @@ getEnemyTeam()
}
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))
{
self thread tryNuke();
@@ -382,10 +392,19 @@ updateWeapon()
else
self setClientDvar("bots_play_knife", 0);
}
// 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()
@@ -624,9 +643,15 @@ lowerMultitext(multiplier)
}
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;
// Set our custom flag and state immediately (atomic — no yield before this).
level.nukeTriggered = true;
level.state = "aftermatch";
if(getDvarInt("scr_nuke_enabled", 1) == 0)
@@ -1150,6 +1175,7 @@ watchDeagleGL()
{
level endon("nuke");
self endon("disconnect");
self endon("death"); // prevent thread accumulation across respawns
while(true)
{
self waittill("weapon_fired", weaponName);
@@ -1173,6 +1199,7 @@ watchHUD()
{
level endon("nuke");
self endon("disconnect");
self endon("death"); // prevent thread accumulation across respawns
while(true)
{
self setClientDvar("ui_drawradar", 1);
@@ -1194,6 +1221,7 @@ watchM40A3()
{
level endon("nuke");
self endon("disconnect");
self endon("death"); // prevent thread accumulation across respawns
while(true)
{
self waittill("weapon_fired", weaponName);

View File

@@ -19,6 +19,10 @@ loadStreaks()
precacheShader("cardicon_cod4");
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.streaks6 = [];
level.streaks9 = [];
@@ -260,6 +264,7 @@ giveStreak(streak)
break;
case "No Reload":
self thread NoReload();
break; // FIX: was missing break, causing fall-through into Radioactive every time
case "Radioactive":
self thread Radioactive();
break;
@@ -337,9 +342,7 @@ Jetpack()
self iPrintlnBold("^3Press ^1F ^7to use ^:Jetpack!");
self.jetpack = 80;
self maps\mp\perks\_perks::givePerk("specialty_falldamage");
self.fx = [];
self.fx[0] = loadfx ("smoke/smoke_trail_white_heli");
self.fx[1] = loadfx( "misc/flares_cobra" );
// Use pre-cached FX handles from loadStreaks() — loadfx() must not be called at runtime.
JETPACKBACK = createPrimaryProgressBar( -275 );
JETPACKBACK.bar.x = 40;
JETPACKBACK.x = 100;
@@ -358,12 +361,10 @@ Jetpack()
{
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 setstance("crouch");
playfx(self.fx[0],self gettagorigin("j_spine4"));
playfx(self.fx[1],self gettagorigin("j_spine4"));
playfx(level._effect["jetpack_smoke"],self gettagorigin("j_spine4"));
playfx(level._effect["jetpack_flare"],self gettagorigin("j_spine4"));
earthquake(.15,.2,self gettagorigin("j_spine4"),50);
self.jetpack--;
if(self getvelocity()[2]<300)
@@ -425,7 +426,11 @@ DeleteIMS(ims)
DeleteIMS2(ims)
{
level waittill("fuckemp");
// FIX: The original waited on "fuckemp" which was never notified anywhere —
// 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();
}
@@ -647,8 +652,11 @@ Juggernaut()
{
level endon("nuke");
self.isJugger = true;
self.maxhealth = 400;
self.health = self.maxhealth;
// FIX: Use the custom health system (actual_maxhealth/actual_health) instead of
// 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 * 2;
self.actual_health = self.actual_maxhealth;
self setMoveSpeedScale(.7);
juggIcon = newHudElem();
@@ -673,6 +681,7 @@ destroyJuggOnNuke(juggIcon)
}
NoReload()
{
level endon("nuke"); // FIX: was missing — thread survived past match end
self endon("death");
self endon("disconnect");
while(true)

View File

@@ -428,8 +428,15 @@ startVote()
level.elems setPoint("CENTER", "CENTER", 20,-75);
level.elems.hideWhenInMenu = true;
//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++)
{
retries++;
if(retries > maxRetries)
break; // pool exhausted: accept fewer options rather than loop forever
valid = true;
map = level.maps[randomInt(level.maps.size-1)];
gamemode = level.gungamemodes[randomInt(level.gungamemodes.size)];