testing to fix memleak and disabling voting requires maprotation

This commit is contained in:
2026-05-10 04:27:29 +02:00
parent 33165763de
commit ab5ea78957
6 changed files with 98 additions and 48 deletions

View File

@@ -94,6 +94,9 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
return; return;
} }
} }
// Guard: eAttacker is undefined for world/self damage (falls, barrels, etc.)
if(isPlayer(eAttacker) && eAttacker != self)
{
damagehud = eAttacker createDamageHud(); damagehud = eAttacker createDamageHud();
damageHud setValue(iDamage); damageHud setValue(iDamage);
damageHud moveOverTime(1); damageHud moveOverTime(1);
@@ -102,6 +105,7 @@ CodeCallback_PlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath
damageHud fadeOverTime(1); damageHud fadeOverTime(1);
damageHud.alpha = 0; damageHud.alpha = 0;
eAttacker thread destroyDamageHud(damageHud); eAttacker thread destroyDamageHud(damageHud);
}
self thread updateDamageHud(); self thread updateDamageHud();
} }
if(level.state != "ingame") if(level.state != "ingame")

View File

@@ -2106,14 +2106,21 @@ endGame( winner, endReasonText, nukeDetonated )
{ {
player closepopupMenu(); player closepopupMenu();
player closeInGameMenu(); player closeInGameMenu();
//player notify ( "reset_outcome" ); // opens da scoreboard
//player thread maps\mp\gametypes\_playerlogic::spawnIntermission();
} }
if(level.players.size > 0) if(level.players.size > 0 && getDvarInt("vote_enabled", 1))
{ {
level notify("spawnVote"); level notify("spawnVote");
level waittill("endVote"); level waittill("endVote");
} }
else
{
// No vote — show the normal scoreboard and leaderboard
foreach ( player in level.players )
{
player notify ( "reset_outcome" );
player thread maps\mp\gametypes\_playerlogic::spawnIntermission();
}
}
// End of Round // End of Round

View File

@@ -166,6 +166,9 @@ loadSettings()
setDvar("bots_main_chat", 0); setDvar("bots_main_chat", 0);
SetDvarIfUninitialized("scr_nuke_enabled", 1); SetDvarIfUninitialized("scr_nuke_enabled", 1);
// Map voting: set to 1 to show custom vote screen at end of game,
// 0 to use the normal leaderboard + server map rotation instead.
SetDvarIfUninitialized("vote_enabled", 0);
// Cache mode flags as level vars — avoids repeated getDvar() in hot per-player loops. // Cache mode flags as level vars — avoids repeated getDvar() in hot per-player loops.
level.isTeamGame = (getDvar("g_gametype") == "gungame_team"); level.isTeamGame = (getDvar("g_gametype") == "gungame_team");
level.isKillConfirmed = (getDvar("gunmode") == "Kill Confirmed"); level.isKillConfirmed = (getDvar("gunmode") == "Kill Confirmed");
@@ -182,6 +185,10 @@ deleteSentries()
} }
launchGame() launchGame()
{ {
// Wait for gametype to initialize _hud_util globals (level.fontHeight, level.uiParent).
// launchGame runs from Init() which fires before the gametype callback.
while(!isDefined(level.fontHeight) || !isDefined(level.uiParent))
waitFrame();
hud = createServerFontString("hudbig", 1); hud = createServerFontString("hudbig", 1);
hud setPoint("CENTER", "CENTER", 0,-80); hud setPoint("CENTER", "CENTER", 0,-80);
hud.color = (1,1,0); hud.color = (1,1,0);
@@ -253,6 +260,7 @@ firstSpawn()
self.firstSpawn = true; self.firstSpawn = true;
self.knifeKills = 0; self.knifeKills = 0;
self.gungameKills = 0; self.gungameKills = 0;
self.isJugger = false;
self.streaks = []; self.streaks = [];
self setClientDvar("cg_drawSplatter", 0); self setClientDvar("cg_drawSplatter", 0);
self setClientDvar("cg_drawDamageFlash", 0); self setClientDvar("cg_drawDamageFlash", 0);
@@ -262,6 +270,7 @@ firstSpawn()
self setClientDvar("bg_shock_movement", 0); self setClientDvar("bg_shock_movement", 0);
self setClientDvar("bg_shock_lookControl", 0); self setClientDvar("bg_shock_lookControl", 0);
self setClientDvar("scr_game_allowkillcam", 0); self setClientDvar("scr_game_allowkillcam", 0);
if(isDefined(self.hud_damagefeedback))
self.hud_damagefeedback.color = (1,0,0); self.hud_damagefeedback.color = (1,0,0);
self.line = self createRectangle("CENTER", "LEFT", 0,-90,300,5,(1,1,0),"line_horizontal",1); self.line = self createRectangle("CENTER", "LEFT", 0,-90,300,5,(1,1,0),"line_horizontal",1);
// Subtle crosshair dot — light blue, low alpha // Subtle crosshair dot — light blue, low alpha
@@ -409,27 +418,47 @@ updateWeapon()
self notify("updateWeapon"); self notify("updateWeapon");
self endon("updateWeapon"); self endon("updateWeapon");
// Don't proceed until the weapon list is fully built
if(!isDefined(level.weaponsLoaded) || !level.weaponsLoaded)
return;
if(self.current > (level.gungameList.size - 1)) if(self.current > (level.gungameList.size - 1))
{ {
self thread tryNuke(); self thread tryNuke();
return; return;
} }
//self iPrintlnBold(level.gungameList[self.current]); //self iPrintlnBold(level.gungameList[self.current]);
weaponName = level.gungameList[self.current];
// Guard: skip if the weapon entry is missing, empty, or "none".
if(!isDefined(weaponName) || weaponName == "none" || weaponName == "")
return;
variant = randomInt(9); variant = randomInt(9);
if(getDvar("gunmode") == "Fungame") if(getDvar("gunmode") == "Fungame")
variant = 0; variant = 0;
if (isSubstr(level.gungameList[self.current], "_akimbo")) if (isSubstr(weaponName, "_akimbo"))
self giveWeapon(level.gungameList[self.current], variant, true); self giveWeapon(weaponName, variant, true);
else else
self giveWeapon(level.gungameList[self.current], variant, false); self giveWeapon(weaponName, variant, false);
// Verify the weapon was actually loaded — giveWeapon silently fails if
// the weapon asset doesn't exist in the engine.
if(!self hasWeapon(weaponName))
{
self.current++;
return;
}
self giveWeapon("onemanarmy_mp"); self giveWeapon("onemanarmy_mp");
self takeWeapon(self getCurrentWeapon()); self takeWeapon("onemanarmy_mp");
self giveMaxAmmo(level.gungameList[self.current]); // Skip ammo operations for weapons with no ammo pool (riotshield, defaultweapon, etc.)
self setWeaponAmmoClip(level.gungameList[self.current], 9999); // force full clip — giveMaxAmmo only fills stock/reserve // giveMaxAmmo on these causes "Weapon name none" engine errors.
if(weaponName != "riotshield_mp" && weaponName != "defaultweapon_mp")
{
self giveMaxAmmo(weaponName);
self setWeaponAmmoClip(weaponName, 9999);
}
waitFrame(); waitFrame();
self switchtoweaponimmediate(level.gungameList[self.current]); self switchtoweaponimmediate(weaponName);
waitFrame(); waitFrame();
self setWeaponAmmoClip(level.gungameList[self.current], 9999); // re-apply after switch to prevent forced reload if(weaponName != "riotshield_mp" && weaponName != "defaultweapon_mp")
self setWeaponAmmoClip(weaponName, 9999);
if(level.state == "prematch" || level.state == "ingame") if(level.state == "prematch" || level.state == "ingame")
self show(); self show();
// Always restore the correct speed, clamped to the mode's configured base floor. // Always restore the correct speed, clamped to the mode's configured base floor.
@@ -485,6 +514,7 @@ refillOnFire()
{ {
self waittill("weapon_fired"); self waittill("weapon_fired");
weapon = self getCurrentWeapon(); weapon = self getCurrentWeapon();
if(weapon != "none" && weapon != "")
self giveMaxAmmo(weapon); self giveMaxAmmo(weapon);
} }
} }
@@ -615,7 +645,7 @@ clearWarning(hud)
} }
createText(font,fontScale,align,relative,x,y,alpha,input,color,gcolor,galpha) createText(font,fontScale,align,relative,x,y,alpha,input,color,gcolor,galpha)
{ {
text = self createFontString(font,fontScale,self); text = self createFontString(font,fontScale);
text setPoint(align,relative,x,y); text setPoint(align,relative,x,y);
text.sort = 999; text.sort = 999;
text.alpha = alpha; text.alpha = alpha;
@@ -675,28 +705,28 @@ scorepopup(amount)
// Each unique amount string burned a permanent configstring slot. // Each unique amount string burned a permanent configstring slot.
self.scoretext_amount.label = &"+"; self.scoretext_amount.label = &"+";
self.scoretext_amount setValue(self.amount); self.scoretext_amount setValue(self.amount);
if(self.multiplier > 1) // Clamp multitext index to the valid range [2..5] to prevent undefined access
mtIdx = self.multiplier;
if(mtIdx > 5) mtIdx = 5;
if(self.multiplier > 1 && isDefined(self.multitext[mtIdx]))
{ {
self.multitext[self.multiplier].alpha = 1; self.multitext[mtIdx].alpha = 1;
self.multitext[self.multiplier] fadeOverTime(0.1); self.multitext[mtIdx] fadeOverTime(0.1);
self.multitext[self.multiplier].color = color; self.multitext[mtIdx].color = color;
self.multitext[self.multiplier].glowColor = glowColor; self.multitext[mtIdx].glowColor = glowColor;
} }
if(self.multiplier == 2) if(self.multiplier == 2)
{ {
self thread lowerMultitext(self.multiplier); self thread lowerMultitext(self.multiplier);
self.multitext[self.multiplier] setText("Double Kill!"); self.multitext[mtIdx] setText("Double Kill!");
} }
else if(self.multiplier == 3) else if(self.multiplier == 3)
self.multitext[self.multiplier] setText("Triple Kill!"); self.multitext[mtIdx] setText("Triple Kill!");
else if(self.multiplier == 4) else if(self.multiplier == 4)
self.multitext[self.multiplier] setText("Multi Kill!"); self.multitext[mtIdx] setText("Multi Kill!");
else if(self.multiplier > 4) else if(self.multiplier > 4)
{ {
difference = self.multiplier - 4; difference = self.multiplier - 4;
// FIX: Use a fixed string instead of randomized color codes.
// The old rainbow text generated a unique configstring for every kill spree
// (random colors = unique string every time = configstring leak).
// FIX: Use .label + setValue() to avoid configstring leak per spree level. // FIX: Use .label + setValue() to avoid configstring leak per spree level.
self.multitext[5].label = &"^1K^2i^3l^4l^5i^6n^7g ^1S^2p^3r^4e^5e ^3x"; self.multitext[5].label = &"^1K^2i^3l^4l^5i^6n^7g ^1S^2p^3r^4e^5e ^3x";
self.multitext[5] setValue(difference); self.multitext[5] setValue(difference);
@@ -717,9 +747,12 @@ lowerMultitext(multiplier)
{ {
self waittill_any("start_lowering", "disconnect"); self waittill_any("start_lowering", "disconnect");
for(i=2;i<6;i++) for(i=2;i<6;i++)
{
if(isDefined(self.multitext[i]))
{ {
self.multitext[i] fadeOverTime(3); self.multitext[i] fadeOverTime(3);
self.multitext[i].alpha = 0; self.multitext[i].alpha = 0;
}
wait .75; wait .75;
} }
wait 4; wait 4;
@@ -910,13 +943,18 @@ takeInvalidWeapon()
continue; continue;
if(self isMantling()) if(self isMantling())
continue; continue;
// Don't poll until weapons are fully loaded
if(!isDefined(level.weaponsLoaded) || !level.weaponsLoaded)
continue;
weapon = self getCurrentWeapon(); weapon = self getCurrentWeapon();
// Skip if player is in a weapon transition ("none" is normal during switches)
if(weapon == "none" || weapon == "")
continue;
if(weapon != level.gungameList[self.current]) if(weapon != level.gungameList[self.current])
{ {
self takeAllWeapons(); self takeAllWeapons();
self thread updateWeapon(); self thread updateWeapon();
// Short cooldown — just enough for switchtoweaponimmediate to settle. // Short cooldown — just enough for switchtoweaponimmediate to settle.
// The old 2.5s wait was causing the visible mid-game-join delay.
wait 0.5; wait 0.5;
} }
} }
@@ -1084,11 +1122,9 @@ tryCreateMarkerIcons()
createRectangle(align, relative, x, y, width, height, color, shader, sort) createRectangle(align, relative, x, y, width, height, color, shader, sort)
{ {
hud = newClientHudElem(self); hud = newClientHudElem(self);
hud.sTexType = "bar"; hud.elemType = "icon";
hud.width = width; hud.width = width;
hud.height = height; hud.height = height;
hud.align = align;
hud.relative = relative;
hud.xOffset = 0; hud.xOffset = 0;
hud.yOffset = 0; hud.yOffset = 0;
hud.children = []; hud.children = [];

View File

@@ -22,6 +22,8 @@ loadStreaks()
// Pre-cache Jetpack FX at level init (loadfx must NOT be called at runtime). // 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_smoke"] = loadfx("smoke/smoke_trail_white_heli");
level._effect["jetpack_flare"] = loadfx("misc/flares_cobra"); level._effect["jetpack_flare"] = loadfx("misc/flares_cobra");
// Small bullet-impact explosion for the Explosive Bullets streak.
level._effect["bullet_explode"] = loadfx("explosions/grenadeExp_default");
level.streaks3 = []; level.streaks3 = [];
level.streaks6 = []; level.streaks6 = [];
@@ -115,7 +117,7 @@ setStreaks()
} }
drawStreaks() drawStreaks()
{ {
if(isDefined(self.streakIcons[0])) if(isDefined(self.streakIcons) && isDefined(self.streakIcons[0]))
{ {
for(i=0;i<3;i++) for(i=0;i<3;i++)
{ {
@@ -321,7 +323,7 @@ Explosive()
forward = self getTagOrigin("j_head"); forward = self getTagOrigin("j_head");
end = self thread vector_scal(anglestoforward(self getPlayerAngles()),1000000); end = self thread vector_scal(anglestoforward(self getPlayerAngles()),1000000);
Location = BulletTrace( forward, end, 0, self )[ "position" ]; Location = BulletTrace( forward, end, 0, self )[ "position" ];
playFx(level._effect["claymore_explode"], Location); playFx(level._effect["bullet_explode"], Location);
RadiusDamage(Location, 50, 30, 20, self ); RadiusDamage(Location, 50, 30, 20, self );
//self RadiusDamage(Location,250,self.explodmg,self.explomindmg,self,"MOD_Explosive","barrel_mp"); //self RadiusDamage(Location,250,self.explodmg,self.explomindmg,self,"MOD_Explosive","barrel_mp");
wait self.explotime; wait self.explotime;
@@ -355,7 +357,7 @@ Jetpack()
{ {
if(self usebuttonpressed() && self.jetpack>0) if(self usebuttonpressed() && self.jetpack>0)
{ {
self playsound("cobra_helicopter_dying_loop"); self playloopsound("cobra_helicopter_dying_loop");
self setstance("crouch"); self setstance("crouch");
playfx(level._effect["jetpack_smoke"],self gettagorigin("j_spine4")); playfx(level._effect["jetpack_smoke"],self gettagorigin("j_spine4"));
playfx(level._effect["jetpack_flare"],self gettagorigin("j_spine4")); playfx(level._effect["jetpack_flare"],self gettagorigin("j_spine4"));
@@ -364,6 +366,8 @@ Jetpack()
if(self getvelocity()[2]<300) if(self getvelocity()[2]<300)
self setvelocity(self getvelocity()+(0,0,60)); self setvelocity(self getvelocity()+(0,0,60));
} }
else
self stoploopsound("cobra_helicopter_dying_loop");
if(self.jetpack < 80 &&!self usebuttonpressed()) if(self.jetpack < 80 &&!self usebuttonpressed())
self.jetpack++; self.jetpack++;
JETPACKBACK updateBar(self.jetpack/80); JETPACKBACK updateBar(self.jetpack/80);
@@ -694,6 +698,7 @@ NoReload()
if(self AttackButtonPressed()) if(self AttackButtonPressed())
{ {
current = self getCurrentWeapon(); current = self getCurrentWeapon();
if(current == "none" || current == "") { waitFrame(); continue; }
clip = self GetWeaponAmmoClip(current); clip = self GetWeaponAmmoClip(current);
stock = self GetWeaponAmmoStock(current); stock = self GetWeaponAmmoStock(current);
if(stock > 0) if(stock > 0)

View File

@@ -296,11 +296,9 @@ getDisplayName(map)
createRectangle(shader,align, relative, x, y, width, height) createRectangle(shader,align, relative, x, y, width, height)
{ {
hud = newHudElem(); hud = newHudElem();
hud.sTexType = "bar"; hud.elemType = "icon";
hud.width = width; hud.width = width;
hud.height = height; hud.height = height;
hud.align = align;
hud.relative = relative;
hud.alpha = 1; hud.alpha = 1;
hud.xOffset = 0; hud.xOffset = 0;
hud.yOffset = 0; hud.yOffset = 0;
@@ -316,11 +314,9 @@ createRectangle(shader,align, relative, x, y, width, height)
createPlayerRectangle(shader,align, relative, x, y, width, height) createPlayerRectangle(shader,align, relative, x, y, width, height)
{ {
hud = newClientHudElem(self); hud = newClientHudElem(self);
hud.sTexType = "bar"; hud.elemType = "icon";
hud.width = width; hud.width = width;
hud.height = height; hud.height = height;
hud.align = align;
hud.relative = relative;
hud.alpha = 1; hud.alpha = 1;
hud.xOffset = 0; hud.xOffset = 0;
hud.yOffset = 0; hud.yOffset = 0;

View File

@@ -125,6 +125,7 @@ loadWeapons()
} }
//level.gungameList[1] = setWeapon("rpg_mp"); //level.gungameList[1] = setWeapon("rpg_mp");
//level.gungameList[1] = "rpg_mp"; //level.gungameList[1] = "rpg_mp";
level.weaponsLoaded = true;
} }
removeIDfromArray(id, weaponList) removeIDfromArray(id, weaponList)
{ {
@@ -594,6 +595,7 @@ loadFungameList()
level.gungameList[level.gungameList.size] = level.fungameWeapons[i]; level.gungameList[level.gungameList.size] = level.fungameWeapons[i];
} }
} }
level.weaponsLoaded = true; // signal that the list is ready for use
} }
addFungameWeapon(weapon) addFungameWeapon(weapon)