11 Commits
fungame ... hm

8 changed files with 130 additions and 79 deletions

9
gunfun/README.md Normal file
View File

@@ -0,0 +1,9 @@
# MW2 Fun Gun Mod
> **Note:** This is a modification of Santahunters original Mod, credits go to Santa for the original Mod!
## Server Setup
`set sv_maprotation ""`
Map rotation must be empty for voting to work properly.

View File

@@ -6,24 +6,24 @@
Nevertheless it uses some codesnippets from the old gungame mod.
----- Version 1.5 - Fungame Update -------
----- Version 1.5 - God of Hellfire Update -------
- Added Botwarfare to the Mod
- Added Fungame mode
- 150+ unique weapons progression list
- Classic Progression style from Pistol to Javelin
- Possibility to set it to 2 kills per gun via configuration
- Performance Improvements
- Removed all Server Console Logs
- Optimized mod code
- Removed several unused code snippets
- Bugfixes
- Fixed "Configstring Overflow (511)" crash on asset-heavy maps (mp_nightshift)
- Fixed Classic mode weapon list population for 300+ weapons
- Fixed weapon upgrades not working on first weapon
- Fixed several other minor bugs
- Removed 2.5 second delay on switching weapons on respawn
- Added "Fungame" mode:
-> 150 unique weapons progression list
-> Includes new weapons like Peacekeeper, Ripper, and more
- Weapon Logic Improvements:
-> Reduced Riot Shield requirement to 1 kill (Fungame only)
-> Fixed Riot Shield kills not counting as progression
-> Fixed Javelin win condition (requires 2 kills to finish)
-> Corrected internal weapon names (ACR to masada, Thumper to m79)
- Stability & Optimization:
-> Fixed "Configstring Overflow (511)" crash on asset-heavy maps (mp_nightshift)
-> Removed console debug flooding causing server timeouts
-> Unified game mode configuration in main.gsc
-> Fixed Classic mode weapon list population for 300+ weapons
- Server & Bot Management:
-> Fixed bot persistence across map rotations (bots_manage_fill)
-> Improved server internet visibility (dedicated 2)
----- Version 1.4 -------

View File

@@ -55,6 +55,7 @@ onStartGameType()
setObjectiveHintText( "allies", "Be the first who cycled through all guns to win! \n \n Mod made by ^:Santahunter^7!" );
setObjectiveHintText( "axis", "Be the first who cycled through all guns to win! \n \n Mod made by ^:Santahunter^7!" );
level.killcam = false;
level.spawnMins = ( 0, 0, 0 );
level.spawnMaxs = ( 0, 0, 0 );
maps\mp\gametypes\_spawnlogic::addSpawnPoints( "allies", "mp_dm_spawn" );

View File

@@ -71,6 +71,7 @@ onStartGameType()
setObjectiveHintText( "allies", "Each several kills of your team, your weapon ranks up! \n \n Mod made by ^:Santahunter^7!" );
setObjectiveHintText( "axis", "Each several kills of your team, your weapon ranks up! \n \n Mod made by ^:Santahunter^7!" );
level.killcam = false;
level.spawnMins = ( 0, 0, 0 );
level.spawnMaxs = ( 0, 0, 0 );
maps\mp\gametypes\_spawnlogic::placeSpawnPoints( "mp_tdm_spawn_allies_start" );

View File

@@ -120,6 +120,7 @@ loadSettings()
setDvar("scr_" + (getDvar("g_gametype")) + "_timelimit", 0);
setDvar("scr_" + (getDvar("g_gametype")) + "_scorelimit", 0);
setDvar("ui_allow_teamchange", 0);
setDvar("scr_game_allowkillcam", 0);
setDvar("testClients_watchKillcam", 0);
setDvar("scr_game_hardpoints", 0);
setDvar("scr_game_graceperiod", 0);
@@ -208,7 +209,9 @@ launchGame()
{
player enableWeapons();
player visionSetNakedForPlayer(getDvar("mapname"), 2);
player setMoveSpeedScale(1.25);
// Use the mode-configured speed dvar, not a hardcoded value.
// Fungame = 1.5, Classic = 1.2, etc. — 1.25 was always wrong for Fungame.
player setMoveSpeedScale(getDvarFloat("speed"));
}
setDvar("jump_height", 60);
@@ -253,8 +256,7 @@ firstSpawn()
self setClientDvar("bg_shock_lookControl_mousesensitivityscale", 1);
self setClientDvar("bg_shock_movement", 0);
self setClientDvar("bg_shock_lookControl", 0);
self setClientDvar("ui_drawradar", 0);
self setClientDvar("cg_drawTeamScores", 0);
self setClientDvar("scr_game_allowkillcam", 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 thread onKilling();
@@ -270,7 +272,12 @@ firstSpawn()
self notify("menuresponse", game["menu_team"], team);
wait .1;
self notify("menuresponse", "changeclass", "class1");
wait .1;
wait .1;
// Re-apply perks here — the engine's class-load triggered by changeclass above
// can wipe perk state before loadSetup() gets a chance to set them on first spawn.
self maps\mp\perks\_perks::givePerk("specialty_fastreload"); // Sleight of Hand
self maps\mp\perks\_perks::givePerk("specialty_falldamage");
self maps\mp\perks\_perks::givePerk("specialty_quickdraw");
self.firstSpawn = false;
self thread tryCreateMarkerIcons();
}
@@ -323,19 +330,16 @@ loadSetup()
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_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);
// Static HUD dvars set once per spawn.
// g_hardcore=1 handles minimap/radar/teamscore suppression at engine level.
// We only need to override what we want TO show (ammo, custom health HUD).
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 setClientDvar("ui_hud_hardcore", 0); // show custom HUD elements despite g_hardcore
self thread takeInvalidWeapon();
if(level.state == "prematch")
{
@@ -395,15 +399,22 @@ updateWeapon()
self giveWeapon("onemanarmy_mp");
self takeWeapon(self getCurrentWeapon());
self giveMaxAmmo(level.gungameList[self.current]);
self setWeaponAmmoClip(level.gungameList[self.current], 9999); // force full clip — giveMaxAmmo only fills stock/reserve
waitFrame();
self switchtoweaponimmediate(level.gungameList[self.current]);
waitFrame();
self setWeaponAmmoClip(level.gungameList[self.current], 9999); // re-apply after switch to prevent forced reload
if(level.state == "prematch" || level.state == "ingame")
self show();
if(self.isJugger)
self setMoveSpeedScale(1);
else if(self.speed)
self setMoveSpeedScale(1.6);
// Always restore the correct speed, clamped to the mode's configured base floor.
// getBaseSpeed() ensures no path can silently drop below the dvar-configured minimum.
if(level.state != "prematch")
{
if(self.speed)
self setMoveSpeedScale(1.6); // Speed streak: above base, always fine
else
self setMoveSpeedScale(getBaseSpeed()); // Juggernaut and normal: always >= base speed
}
if(level.state == "prematch")
{
self setMoveSpeedScale(0);
@@ -703,7 +714,6 @@ tryNuke()
level notify("nuke");
foreach(player in level.players)
{
player setClientDvar("ui_hud_hardcore", 0);
player hide();
}
@@ -822,35 +832,39 @@ floatToString(float)
string = getSubStr(string, 0, 5);
return StringToFloat(string);
}
// Returns the mode-configured base speed, clamped so nothing can go below it.
// All setMoveSpeedScale calls for non-prematch state should go through this
// (or compare against it) to honour the 1.5 Fungame / 1.2 Classic floor contract.
getBaseSpeed()
{
base = getDvarFloat("speed");
return base;
}
takeInvalidWeapon()
{
self endon("disconnect");
self endon("death");
level endon("nuke");
counter = 0;
wait 3;
// Skip the first 5 frames so the initial updateWeapon() from loadSetup() has
// time to complete its switchtoweaponimmediate before we start polling.
// This prevents a false-positive correction that was causing the 2.5s delay.
for(i = 0; i < 5; i++)
waitFrame();
while(1)
{
wait 0.1; // was waitFrame() (~60/s) — 10/s is ample for a safety-net poller
wait 0.1; // 10/s is ample for a safety-net poller
if(!isAlive(self))
continue;
if(self isMantling())
continue;
weapon = self getCurrentWeapon();
if(!self hasWeapon(level.gungameList[self.current]))
{
counter++;
if(counter == 2)
{
counter = 0;
level.gungameList[self.current] = mod\weapons::setWeapon("peacekeeper_mp");
}
}
if(weapon != level.gungameList[self.current])
{
self takeAllWeapons();
self thread updateWeapon();
wait 2.5;
// Short cooldown — just enough for switchtoweaponimmediate to settle.
// The old 2.5s wait was causing the visible mid-game-join delay.
wait 0.5;
}
}
}
@@ -1033,7 +1047,8 @@ createRectangle(align, relative, x, y, width, height, color, shader, sort)
hud.HideWhenInMenu = true;
hud setPoint(align,relative,x,y);
return hud;
}
}
upgradeTeamUI(team)
{
fontElem = newTeamHudElem( team );
@@ -1243,9 +1258,7 @@ watchHUD()
self endon("death"); // prevent thread accumulation across respawns
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);
// Re-apply ammo visibility (engine may reset it on class/weapon events).
self setClientDvar("cg_drawAmmo", 1);
wait 5;
}
@@ -1320,11 +1333,22 @@ watchBotRiotShield()
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
);
// The mod pins engine health at 1000 and tracks real HP in actual_health.
// finishPlayerDamageWrapper only hits engine health (which never changes),
// so we apply damage directly to the scripted HP instead.
bashDamage = 100;
player.actual_health -= bashDamage;
player notify("takeDamage"); // reset regen timer
if(player.actual_health <= 0)
{
player.actual_health = 0;
// Drive a lethal hit through the engine so the kill is attributed correctly.
player thread maps\mp\gametypes\_damage::finishPlayerDamageWrapper(
self, self, 1000, 0, "MOD_MELEE", "riotshield_mp",
player getOrigin(), player getOrigin(), "none", 0, 0
);
}
break; // one target per swing
}
}

View File

@@ -236,7 +236,7 @@ giveStreak(streak)
wait 1;
self maps\mp\perks\_perks::givePerk("specialty_lightweight");
self setMoveSpeedScale(1.6);
self.setMoveSpeedScale = 1.6;
self.moveSpeedScaler = 1.6; // keep cached baseline in sync (was: self.setMoveSpeedScale = 1.6 — typo, set entity field not call function)
break;
case "Riotshield":
self AttachShieldModel( "weapon_riot_shield_mp", "tag_shield_back" );
@@ -648,10 +648,11 @@ Juggernaut()
self.isJugger = true;
// 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.
// and regen logic. We multiply 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);
// Speed floor is the mode base (1.5 in Fungame) — no slowdown penalty.
// Juggernaut's balance trade-off is the 5× HP pool only.
juggIcon = newHudElem();
juggIcon.x = self.origin[0];

View File

@@ -60,39 +60,39 @@ loadVote()
maps()
{
level.maps = [];
// MW2 Base Maps
level.maps[level.maps.size] = "mp_rust";
level.maps[level.maps.size] = "mp_afghan";
level.maps[level.maps.size] = "mp_terminal";
level.maps[level.maps.size] = "mp_highrise";
level.maps[level.maps.size] = "mp_derail";
//level.maps[level.maps.size] = "mp_derail"; //fuck derail
level.maps[level.maps.size] = "mp_rundown";
level.maps[level.maps.size] = "mp_brecourt";
level.maps[level.maps.size] = "mp_boneyard";
level.maps[level.maps.size] = "mp_brecourt"; //Wasteland
level.maps[level.maps.size] = "mp_boneyard"; //Scrapyard
level.maps[level.maps.size] = "mp_quarry";
level.maps[level.maps.size] = "mp_nightshift";
level.maps[level.maps.size] = "mp_nightshift"; //Skidrow
level.maps[level.maps.size] = "mp_estate";
level.maps[level.maps.size] = "mp_invasion";
level.maps[level.maps.size] = "mp_checkpoint";
level.maps[level.maps.size] = "mp_checkpoint"; //Karachi
level.maps[level.maps.size] = "mp_subbase";
level.maps[level.maps.size] = "mp_favela";
level.maps[level.maps.size] = "mp_underpass";
level.maps[level.maps.size] = "iw4_credits";
//level.maps[level.maps.size] = "iw4_credits"; // no
// MW2 DLC Maps
level.maps[level.maps.size] = "mp_complex"; //Bailout Stimulus DLC
level.maps[level.maps.size] = "mp_crash"; //Crash
level.maps[level.maps.size] = "mp_overgrown"; //Overgrown
level.maps[level.maps.size] = "mp_compact"; //Salvage Stimulus DLC
level.maps[level.maps.size] = "mp_storm"; //Storm
level.maps[level.maps.size] = "mp_complex";
level.maps[level.maps.size] = "mp_crash";
level.maps[level.maps.size] = "mp_overgrown";
level.maps[level.maps.size] = "mp_compact";
level.maps[level.maps.size] = "mp_storm";
level.maps[level.maps.size] = "mp_abandon"; //Carnival
//level.maps[level.maps.size] = "mp_fuel2"; // hell no
level.maps[level.maps.size] = "mp_strike"; //Strike
level.maps[level.maps.size] = "mp_trailerpark"; //Trailerpark
level.maps[level.maps.size] = "mp_vacant"; //Vacant
level.maps[level.maps.size] = "mp_abandon";
level.maps[level.maps.size] = "mp_fuel2";
level.maps[level.maps.size] = "mp_strike";
level.maps[level.maps.size] = "mp_trailerpark";
level.maps[level.maps.size] = "mp_vacant";
level.maps[level.maps.size] = "mp_nuked";
level.maps[level.maps.size] = "mp_nuked"; //Nuketown
level.maps[level.maps.size] = "mp_cross_fire";
level.maps[level.maps.size] = "mp_bloc";
@@ -126,7 +126,7 @@ maps()
//level.maps[level.maps.size] = "so_forest_contingency";
//level.maps[level.maps.size] = "dcburning";
//level.maps[level.maps.size] = "dcemp";
level.maps[level.maps.size] = "estate";
//level.maps[level.maps.size] = "estate";
//level.maps[level.maps.size] = "favela";
//level.maps[level.maps.size] = "so_showers_gulag";
level.maps[level.maps.size] = "oilrig";

View File

@@ -46,6 +46,7 @@ loadWeapons()
// pistols
weaponList[weaponList.size] = "usp_mp";
weaponList[weaponList.size] = "deserteagle_mp";
weaponList[weaponList.size] = "deserteagle_akimbo_mp"; // GL deagle
weaponList[weaponList.size] = "coltanaconda_mp";
weaponList[weaponList.size] = "beretta_mp";
// semi auto pistols
@@ -118,7 +119,7 @@ loadWeapons()
// Only remove specific weapons if we still have enough unique ones left
if(weaponList.size > 5)
{
if(weaponList[id] == "m79_mp" || weaponList[id] == "at4_mp" || weaponList[id] == "rpg_mp" || weaponList[id] == "riotshield_mp" || weaponList[id] == "dragunov_mp" || weaponList[id] == "m40a3_mp" || weaponList[id] == "peacekeeper_mp")
if(weaponList[id] == "m79_mp" || weaponList[id] == "at4_mp" || weaponList[id] == "rpg_mp" || weaponList[id] == "riotshield_mp" || weaponList[id] == "dragunov_mp" || weaponList[id] == "m40a3_mp" || weaponList[id] == "peacekeeper_mp" || weaponList[id] == "deserteagle_akimbo_mp")
weaponList = removeIDfromArray(id, weaponList);
}
}
@@ -137,6 +138,10 @@ removeIDfromArray(id, weaponList)
}
setWeapon(name)
{
// If the caller already specified a fully-qualified akimbo weapon (e.g.
// "deserteagle_akimbo_mp"), pass it through unchanged — no randomization.
if(isSubstr(name, "_akimbo_"))
return name;
name = strTok(name, "_");
scope = "";
attachments = "";
@@ -518,6 +523,16 @@ loadFungameList()
addFungameWeapon("famas_reflex_mp");
addFungameWeapon("famas_acog_mp");
addFungameWeapon("famas_fmj_mp");
addFungameWeapon("fal_mp");
addFungameWeapon("fal_silencer_mp");
addFungameWeapon("fal_reflex_mp");
addFungameWeapon("fal_acog_mp");
addFungameWeapon("fal_gl_mp");
addFungameWeapon("m16_mp");
addFungameWeapon("m16_silencer_mp");
addFungameWeapon("m16_reflex_mp");
addFungameWeapon("m16_acog_mp");
addFungameWeapon("m16_gl_mp");
addFungameWeapon("ak47classic_mp");
wait .05;