diff --git a/Updater.bat b/Updater.bat index 18e18b4..3d577dd 100644 --- a/Updater.bat +++ b/Updater.bat @@ -1,2 +1,2 @@ @echo off -.\plutonium.exe -install-dir "%cd%" -update-only \ No newline at end of file +.\plutonium.exe -install-dir "E:\Games\Plutonium\Plutonium" -update-only \ No newline at end of file diff --git a/bin/VibeCheck.exe b/bin/VibeCheck.exe new file mode 100644 index 0000000..0b6b9ba Binary files /dev/null and b/bin/VibeCheck.exe differ diff --git a/bin/plutonium-bootstrapper-win32.exe b/bin/plutonium-bootstrapper-win32.exe index ba9f473..b40897a 100644 Binary files a/bin/plutonium-bootstrapper-win32.exe and b/bin/plutonium-bootstrapper-win32.exe differ diff --git a/bin/plutonium-launcher-win32.exe b/bin/plutonium-launcher-win32.exe index 371feac..08505ec 100644 Binary files a/bin/plutonium-launcher-win32.exe and b/bin/plutonium-launcher-win32.exe differ diff --git a/bin/steam_api64.dll b/bin/steam_api64.dll new file mode 100644 index 0000000..2b42812 Binary files /dev/null and b/bin/steam_api64.dll differ diff --git a/info.json b/info.json index 0847a03..3f3d998 100644 --- a/info.json +++ b/info.json @@ -1 +1 @@ -{"revision":3641,"launchTarget":"bin/plutonium-launcher-win32.exe"} \ No newline at end of file +{"revision":3755,"launchTarget":"bin/plutonium-launcher-win32.exe"} \ No newline at end of file diff --git a/storage/t6/scripts/mp/ranked.gsc b/storage/t6/scripts/mp/ranked.gsc index 7823e0a..9a7c96c 100644 --- a/storage/t6/scripts/mp/ranked.gsc +++ b/storage/t6/scripts/mp/ranked.gsc @@ -1,96 +1,819 @@ +#include common_scripts\utility; +#include maps\mp\_utility; + main() { - // always allow team change - replaceFunc( maps\mp\gametypes\_serversettings::init, ::serverSettings ); + if ( GetDvarInt( "scr_disablePlutoniumFixes" ) ) + { + return; + } - // never forfeit - replaceFunc( maps\mp\gametypes\_globallogic::updateGameEvents, ::updateGameEvents ); + if ( isDedicated() ) + { + // never forfeit + replaceFunc( GetFunction( "maps/mp/gametypes/_globallogic", "checkforforfeit" ), ::neverForfeit, -1 ); + + // fix team change exploit + replaceFunc( GetFunction( "maps/mp/gametypes/_globallogic_player", "spectate_player_watcher" ), ::spectate_player_watcher_fix, -1 ); + + // fix menuresponse exploits + replaceFunc( GetFunction( "maps/mp/gametypes/_globallogic", "forceend" ), ::noop, -1 ); + replaceFunc( GetFunction( "maps/mp/gametypes/_globallogic", "gamehistoryplayerquit" ), ::noop, -1 ); + replaceFunc( GetFunction( "maps/mp/gametypes/_globallogic", "killserverpc" ), ::noop, -1 ); + + // use item restrictions + if ( getdvarint( "scr_useItemRestrictions" ) ) + { + replaceFunc( GetFunction( "maps/mp/gametypes/_class", "giveloadout" ), ::giveloadout_override, -1 ); + replaceFunc( GetFunction( "maps/mp/gametypes/_class", "getkillstreakindex" ), ::getkillstreakindex_override, -1 ); + } + } } -serverSettings() +init() { - level.hostname = GetDvar( "sv_hostname" ); - if ( level.hostname == "" ) + if ( GetDvarInt( "scr_disablePlutoniumFixes" ) ) { - level.hostname = "CoDHost"; + return; } - SetDvar( "sv_hostname", level.hostname ); - SetDvar( "ui_hostname", level.hostname ); - MakeDvarServerInfo( "ui_hostname", "CoDHost" ); - - level.motd = GetDvar( "scr_motd" ); - if ( level.motd == "" ) + + if ( isDedicated() ) { - level.motd = ""; + // allow team changing on dedis (gts) + level.allow_teamchange = getgametypesetting( "allowInGameTeamChange" ) + ""; + SetDvar( "ui_allow_teamchange", level.allow_teamchange ); + + // readd teambalancing + if ( level.teambased ) + { + level thread updateTeamBalance(); + } } - SetDvar( "scr_motd", level.motd ); - SetDvar( "ui_motd", level.motd ); - MakeDvarServerInfo( "ui_motd", "" ); - - level.allowvote = GetDvar( "g_allowVote" ); - if ( level.allowvote == "" ) + + level thread on_player_connect(); +} + +on_player_connect() +{ + for ( ;; ) { - level.allowvote = "1"; + level waittill( "connected", player ); + player thread player_connected(); } - SetDvar( "g_allowvote", level.allowvote ); - SetDvar( "ui_allowvote", level.allowvote ); - MakeDvarServerInfo( "ui_allowvote", "1" ); - - level.allow_teamchange = "1"; - SetDvar( "ui_allow_teamchange", level.allow_teamchange ); - - level.friendlyfire = getgametypesetting( "friendlyfiretype" ); - SetDvar( "ui_friendlyfire", level.friendlyfire ); - MakeDvarServerInfo( "ui_friendlyfire", "0" ); - - if ( GetDvar( "scr_mapsize" ) == "" ) +} + +player_connected() +{ + self endon( "disconnect" ); + + if ( isDedicated() ) { - SetDvar( "scr_mapsize", "64" ); + // fix max allocation exploit + if ( !self istestclient() ) + { + self thread fix_max_allocation_exploit(); + } } - else if ( GetDvarFloat( "scr_mapsize" ) >= 64 ) + + self thread watch_on_throw_grenade(); +} + +watch_on_throw_grenade() +{ + self endon( "disconnect" ); + + for ( ;; ) { - SetDvar( "scr_mapsize", "64" ); + self waittill( "grenade_fire", grenade, weaponName ); + + // stop grenade team change exploit + if ( isDedicated() ) + { + grenade thread deleteOnOwnerTeamChange( self ); + } } - else if ( GetDvarFloat( "scr_mapsize" ) >= 32 ) +} + +fix_max_allocation_exploit() +{ + self endon( "disconnect" ); + + this_class = ""; + + for ( ;; ) { - SetDvar( "scr_mapsize", "32" ); + wait 0.05; + + if ( !isDefined( self.class ) ) + { + continue; + } + + if ( this_class == self.class ) + { + continue; + } + + this_class = self.class; + + if ( !issubstr( self.class, "CLASS_CUSTOM" ) ) + { + continue; + } + + class_num = int( self.class[self.class.size - 1] ) - 1; + + if ( self GetLoadoutAllocation( class_num ) <= level.maxAllocation ) + { + continue; + } + + self.class = level.defaultclass; + self.pers["class"] = level.defaultclass; + + if ( !isAlive( self ) ) + { + continue; + } + + self suicide(); } - else if ( GetDvarFloat( "scr_mapsize" ) >= 16 ) +} + +deleteOnOwnerTeamChange( owner ) +{ + self endon( "death" ); + + owner waittill_any( "disconnect", "joined_team", "joined_spectators" ); + + self delete (); +} + +updateTeamBalance() +{ + level thread [[ GetFunction( "maps/mp/teams/_teams", "updateteambalancedvar" ) ]](); + + wait .15; + + if ( level.teamBalance && isRoundBased() && level.numlives ) { - SetDvar( "scr_mapsize", "16" ); + if ( isDefined( game["BalanceTeamsNextRound"] ) ) + { + iPrintLnbold( &"MP_AUTOBALANCE_NEXT_ROUND" ); + } + + level waittill( "game_ended" ); + wait 1; + + if ( isDefined( game["BalanceTeamsNextRound"] ) ) + { + level balanceTeams(); + game["BalanceTeamsNextRound"] = undefined; + } + else if ( needsTeamBalance() ) + { + game["BalanceTeamsNextRound"] = true; + } } else { - SetDvar( "scr_mapsize", "8" ); - } - - level.mapsize = GetDvarFloat( "scr_mapsize" ); - maps\mp\gametypes\_serversettings::constraingametype( GetDvar( "g_gametype" ) ); - maps\mp\gametypes\_serversettings::constrainmapsize( level.mapsize ); - - for (;;) - { - maps\mp\gametypes\_serversettings::updateserversettings(); - wait 5; + level endon ( "game_ended" ); + + for ( ;; ) + { + if ( level.teamBalance > 0 ) + { + if ( needsTeamBalance() ) + { + iPrintLnBold( &"MP_AUTOBALANCE_SECONDS", 15 ); + wait 15.0; + + if ( needsTeamBalance() ) + { + level balanceTeams(); + } + } + + wait 59.0; + } + + wait 1.0; + } } } -updateGameEvents() +getBinaryTeamData() { - if ( !level.playerQueuedRespawn && !level.numLives && !level.inOverTime ) - return; - - if ( level.inGracePeriod ) - return; + allies = 0; + axis = 0; - if ( level.playerQueuedRespawn ) + for ( i = 0; i < level.players.size; i++ ) { - maps\mp\gametypes\_globallogic::doSpawnQueueUpdates(); + if ( !isdefined( level.players[i].pers["team"] ) ) + { + continue; + } + + if ( level.players[i].pers["team"] == "allies" ) + { + allies++; + } + else if ( level.players[i].pers["team"] == "axis" ) + { + axis++; + } } - - if ( maps\mp\gametypes\_globallogic::doDeadEventUpdates() ) - return; - - if ( maps\mp\gametypes\_globallogic::doOneLeftEventUpdates() ) - return; -} \ No newline at end of file + + answer = spawnstruct(); + answer.allies = allies; + answer.axis = axis; + + return answer; +} + +getLeastPlayTimePlayerForTeam( team ) +{ + answer = undefined; + + for ( i = 0; i < level.players.size; i++ ) + { + if ( !isdefined( level.players[i].pers["team"] ) || !isdefined( level.players[i].pers["teamTime"] ) ) + { + continue; + } + + if ( level.players[i].pers["team"] != team ) + { + continue; + } + + if ( isDefined( answer ) && level.players[i].pers["teamTime"] < answer.pers["teamTime"] ) + { + continue; + } + + answer = level.players[i]; + } + + return answer; +} + +needsTeamBalance() +{ + if ( level.teamBalance <= 0 ) + { + return false; + } + + teamdata = getBinaryTeamData(); + + if ( abs( teamdata.allies - teamdata.axis ) > level.teamBalance ) + { + return true; + } + + return false; +} + +balanceTeams() +{ + iPrintLnBold( game["strings"]["autobalance"] ); + + while ( needsTeamBalance() ) + { + teamdata = getBinaryTeamData(); + + switchto = "axis"; + + if ( teamdata.axis > teamdata.allies ) + { + switchto = "allies"; + } + + switcher = getLeastPlayTimePlayerForTeam( getotherteam( switchto ) ); + + if ( !isDefined( switcher ) ) + { + break; + } + + switcher changeTeam( switchto ); + } +} + +changeTeam( team ) +{ + self endon( "disconnect" ); + + if ( team != self.pers["team"] ) + { + if ( self.sessionstate == "playing" || self.sessionstate == "dead" ) + { + self.switching_teams = true; + self.joining_team = team; + self.leaving_team = self.pers["team"]; + self suicide(); + } + } + + self.pers["team"] = team; + self.team = team; + self.pers["class"] = undefined; + self.class = undefined; + self.pers["weapon"] = undefined; + self.pers["savedmodel"] = undefined; + + self [[ GetFunction( "maps/mp/gametypes/_globallogic_ui", "updateObjectiveText" ) ]](); + self [[ GetFunction( "maps/mp/gametypes/_spectating", "setspectatepermissions" ) ]](); + + if ( level.teamBased ) + { + self.sessionteam = team; + } + else + { + self.sessionteam = "none"; + self.ffateam = team; + } + + if ( !isAlive( self ) ) + { + self.statusicon = "hud_status_dead"; + } + + self notify( "joined_team" ); + level notify( "joined_team" ); + self setclientscriptmainmenu( game["menu_class"] ); + self openmenu( game["menu_class"] ); + self notify( "end_respawn" ); +} + +noop() +{ +} + +neverForfeit() +{ + return false; +} + +spectate_player_watcher_fix() +{ + self endon( "disconnect" ); + + if ( !level.splitscreen && !level.hardcoremode && getdvarint( "scr_showperksonspawn" ) == 1 && game["state"] != "postgame" && !isdefined( self.perkhudelem ) ) + { + if ( level.perksenabled == 1 ) + { + self [[ GetFunction( "maps/mp/gametypes/_hud_util", "showperks" ) ]](); + } + + self thread [[ GetFunction( "maps/mp/gametypes/_globallogic_ui", "hideloadoutaftertime" ) ]]( 0 ); + } + + self.watchingactiveclient = 1; + self.waitingforplayerstext = undefined; + + while ( true ) + { + if ( self.pers["team"] != "spectator" || level.gameended ) + { + self [[ GetFunction( "maps/mp/gametypes/_hud_message", "clearshoutcasterwaitingmessage" ) ]](); + + if ( !level.inprematchperiod ) + { + self freezecontrols( 0 ); + } + + self.watchingactiveclient = 0; + break; + } + else + { + count = 0; + + for ( i = 0; i < level.players.size; i++ ) + { + if ( level.players[i].team != "spectator" ) + { + count++; + break; + } + } + + if ( count > 0 ) + { + if ( !self.watchingactiveclient ) + { + self [[ GetFunction( "maps/mp/gametypes/_hud_message", "clearshoutcasterwaitingmessage" ) ]](); + self freezecontrols( 0 ); + } + + self.watchingactiveclient = 1; + } + else + { + if ( self.watchingactiveclient ) + { + [[ level.onspawnspectator ]](); + self freezecontrols( 1 ); + self [[ GetFunction( "maps/mp/gametypes/_hud_message", "setshoutcasterwaitingmessage" ) ]](); + } + + self.watchingactiveclient = 0; + } + + wait 0.5; + } + } +} + +restrict_attachments( weapon ) +{ + tokens = strTok( weapon, "+" ); + + if ( tokens.size <= 1 ) + { + return weapon; + } + + new_weapon = tokens[ 0 ]; + + for ( i = 1; i < tokens.size; i++ ) + { + if ( isitemrestricted( tokens[ i ] ) ) + { + continue; + } + + new_weapon += "+" + tokens[ i ]; + } + + return new_weapon; +} + +getkillstreakindex_override( class, killstreaknum ) +{ + killstreaknum++; + killstreakstring = "killstreak" + killstreaknum; + answer = self getloadoutitem( class, killstreakstring ); + + if ( !isDefined( answer ) || answer < 0 ) + { + return undefined; + } + + data = level.tbl_killstreakdata[answer]; + + if ( !isdefined( data ) ) + { + return undefined; + } + + if ( isitemrestricted( data ) ) + { + return undefined; + } + + return answer; +} + +giveloadout_override( team, class ) +{ + pixbeginevent( "giveLoadout" ); + self takeallweapons(); + primaryindex = 0; + self.specialty = []; + self.killstreak = []; + primaryweapon = undefined; + self notify( "give_map" ); + class_num_for_killstreaks = 0; + primaryweaponoptions = 0; + secondaryweaponoptions = 0; + playerrenderoptions = 0; + primarygrenadecount = 0; + iscustomclass = 0; + + if ( issubstr( class, "CLASS_CUSTOM" ) ) + { + pixbeginevent( "custom class" ); + class_num = int( class[class.size - 1] ) - 1; + + if ( -1 == class_num ) + { + class_num = 9; + } + + self.class_num = class_num; + self [[ GetFunction( "maps/mp/gametypes/_class", "reset_specialty_slots" ) ]]( class_num ); + playerrenderoptions = self calcplayeroptions( class_num ); + class_num_for_killstreaks = class_num; + iscustomclass = 1; + pixendevent(); + } + else + { + pixbeginevent( "default class" ); + assert( isdefined( self.pers["class"] ), "Player during spawn and loadout got no class!" ); + class_num = level.classtoclassnum[class]; + self.class_num = class_num; + pixendevent(); + } + + knifeweaponoptions = self calcweaponoptions( class_num, 2 ); + + if ( !isitemrestricted( "knife" ) ) + { + self giveweapon( "knife_mp", 0, knifeweaponoptions ); + } + + self.specialty = self getloadoutperks( class_num ); + + for ( i = 0; i < self.specialty.size; i++ ) + { + if ( isitemrestricted( self.specialty[i] ) ) + { + arrayremoveindex( self.specialty, i ); + i--; + } + } + + self [[ GetFunction( "maps/mp/gametypes/_class", "register_perks" ) ]](); + self setactionslot( 3, "altMode" ); + self setactionslot( 4, "" ); + self [[ GetFunction( "maps/mp/gametypes/_class", "givekillstreaks" ) ]]( class_num_for_killstreaks ); + spawnweapon = ""; + initialweaponcount = 0; + + if ( isdefined( self.pers["weapon"] ) && self.pers["weapon"] != "none" && ![[ GetFunction( "maps/mp/killstreaks/_killstreaks", "iskillstreakweapon" ) ]]( self.pers["weapon"] ) ) + { + weapon = self.pers["weapon"]; + } + else + { + weapon = self getloadoutweapon( class_num, "primary" ); + weapon = [[ GetFunction( "maps/mp/gametypes/_class", "removeduplicateattachments" ) ]]( weapon ); + + if ( [[ GetFunction( "maps/mp/killstreaks/_killstreaks", "iskillstreakweapon" ) ]]( weapon ) ) + { + weapon = "weapon_null_mp"; + } + + if ( isitemrestricted( strTok( weapon, "_" )[0] ) ) + { + weapon = "weapon_null_mp"; + } + } + + sidearm = self getloadoutweapon( class_num, "secondary" ); + sidearm = [[ GetFunction( "maps/mp/gametypes/_class", "removeduplicateattachments" ) ]]( sidearm ); + + if ( [[ GetFunction( "maps/mp/killstreaks/_killstreaks", "iskillstreakweapon" ) ]]( sidearm ) ) + { + sidearm = "weapon_null_mp"; + } + + if ( isitemrestricted( strTok( sidearm, "_" )[0] ) ) + { + sidearm = "weapon_null_mp"; + } + + self.primaryweaponkill = 0; + self.secondaryweaponkill = 0; + + if ( self isbonuscardactive( 2, self.class_num ) ) + { + self.primaryloadoutweapon = weapon; + self.primaryloadoutaltweapon = weaponaltweaponname( weapon ); + self.secondaryloadoutweapon = sidearm; + self.secondaryloadoutaltweapon = weaponaltweaponname( sidearm ); + } + else + { + if ( self isbonuscardactive( 0, self.class_num ) ) + { + self.primaryloadoutweapon = weapon; + } + + if ( self isbonuscardactive( 1, self.class_num ) ) + { + self.secondaryloadoutweapon = sidearm; + } + } + + if ( sidearm != "weapon_null_mp" ) + { + secondaryweaponoptions = self calcweaponoptions( class_num, 1 ); + } + + primaryweapon = weapon; + + if ( primaryweapon != "weapon_null_mp" ) + { + primaryweaponoptions = self calcweaponoptions( class_num, 0 ); + } + + if ( sidearm != "" && sidearm != "weapon_null_mp" && sidearm != "weapon_null" ) + { + sidearm = restrict_attachments( sidearm ); + + self giveweapon( sidearm, 0, secondaryweaponoptions ); + + if ( self hasperk( "specialty_extraammo" ) ) + { + self givemaxammo( sidearm ); + } + + spawnweapon = sidearm; + initialweaponcount++; + } + + primarytokens = strtok( primaryweapon, "_" ); + self.pers["primaryWeapon"] = primarytokens[0]; +/# + println( "^5GiveWeapon( " + weapon + " ) -- weapon" ); +#/ + + if ( primaryweapon != "" && primaryweapon != "weapon_null_mp" && primaryweapon != "weapon_null" ) + { + primaryweapon = restrict_attachments( primaryweapon ); + + self giveweapon( primaryweapon, 0, primaryweaponoptions ); + + if ( self hasperk( "specialty_extraammo" ) ) + { + self givemaxammo( primaryweapon ); + } + + spawnweapon = primaryweapon; + initialweaponcount++; + } + + if ( initialweaponcount < 2 ) + { + knife = "knife_held_mp"; + + if ( isitemrestricted( "knife_held" ) ) + { + knife = "weapon_null_mp"; + } + + if ( knife != "weapon_null_mp" ) + { + self giveweapon( knife, 0, knifeweaponoptions ); + } + + if ( initialweaponcount == 0 ) + { + spawnweapon = knife; + } + } + + if ( !isdefined( self.spawnweapon ) && isdefined( self.pers["spawnWeapon"] ) ) + { + self.spawnweapon = self.pers["spawnWeapon"]; + } + + if ( isdefined( self.spawnweapon ) && doesweaponreplacespawnweapon( self.spawnweapon, spawnweapon ) && !self.pers["changed_class"] ) + { + spawnweapon = self.spawnweapon; + } + + self.pers["changed_class"] = 0; + assert( spawnweapon != "" ); + self.spawnweapon = spawnweapon; + self.pers["spawnWeapon"] = self.spawnweapon; + self setspawnweapon( spawnweapon ); + grenadetypeprimary = self getloadoutitemref( class_num, "primarygrenade" ); + + if ( isitemrestricted( grenadetypeprimary ) ) + { + grenadetypeprimary = ""; + } + + if ( [[ GetFunction( "maps/mp/killstreaks/_killstreaks", "iskillstreakweapon" ) ]]( grenadetypeprimary + "_mp" ) ) + { + grenadetypeprimary = ""; + } + + grenadetypesecondary = self getloadoutitemref( class_num, "specialgrenade" ); + + if ( isitemrestricted( grenadetypesecondary ) ) + { + grenadetypesecondary = ""; + } + + if ( [[ GetFunction( "maps/mp/killstreaks/_killstreaks", "iskillstreakweapon" ) ]]( grenadetypesecondary + "_mp" ) ) + { + grenadetypesecondary = ""; + } + + if ( grenadetypeprimary != "" && grenadetypeprimary != "weapon_null_mp" && [[ GetFunction( "maps/mp/gametypes/_class", "isequipmentallowed" ) ]]( grenadetypeprimary ) ) + { + grenadetypeprimary += "_mp"; + primarygrenadecount = self getloadoutitem( class_num, "primarygrenadecount" ); + } + + if ( grenadetypesecondary != "" && grenadetypesecondary != "weapon_null_mp" && [[ GetFunction( "maps/mp/gametypes/_class", "isequipmentallowed" ) ]]( grenadetypesecondary ) ) + { + grenadetypesecondary += "_mp"; + grenadesecondarycount = self getloadoutitem( class_num, "specialgrenadecount" ); + } + + if ( !( grenadetypeprimary != "" && grenadetypeprimary != "weapon_null_mp" && [[ GetFunction( "maps/mp/gametypes/_class", "isequipmentallowed" ) ]]( grenadetypeprimary ) ) ) + { + if ( grenadetypesecondary != level.weapons["frag"] ) + { + grenadetypeprimary = level.weapons["frag"]; + } + else + { + grenadetypeprimary = level.weapons["flash"]; + } + } + +/# + println( "^5GiveWeapon( " + grenadetypeprimary + " ) -- grenadeTypePrimary" ); +#/ + self giveweapon( grenadetypeprimary ); + self setweaponammoclip( grenadetypeprimary, primarygrenadecount ); + self switchtooffhand( grenadetypeprimary ); + self.grenadetypeprimary = grenadetypeprimary; + self.grenadetypeprimarycount = primarygrenadecount; + + if ( self.grenadetypeprimarycount > 1 ) + { + self dualgrenadesactive(); + } + + if ( grenadetypesecondary != "" && grenadetypesecondary != "weapon_null_mp" && [[ GetFunction( "maps/mp/gametypes/_class", "isequipmentallowed" ) ]]( grenadetypesecondary ) ) + { + self setoffhandsecondaryclass( grenadetypesecondary ); +/# + println( "^5GiveWeapon( " + grenadetypesecondary + " ) -- grenadeTypeSecondary" ); +#/ + self giveweapon( grenadetypesecondary ); + self setweaponammoclip( grenadetypesecondary, grenadesecondarycount ); + self.grenadetypesecondary = grenadetypesecondary; + self.grenadetypesecondarycount = grenadesecondarycount; + } + + self bbclasschoice( class_num, primaryweapon, sidearm ); + + if ( !sessionmodeiszombiesgame() ) + { + for ( i = 0; i < 3; i++ ) + { + if ( level.loadoutkillstreaksenabled && isdefined( self.killstreak[i] ) && isdefined( level.killstreakindices[self.killstreak[i]] ) ) + { + killstreaks[i] = level.killstreakindices[self.killstreak[i]]; + continue; + } + + killstreaks[i] = 0; + } + + self recordloadoutperksandkillstreaks( primaryweapon, sidearm, grenadetypeprimary, grenadetypesecondary, killstreaks[0], killstreaks[1], killstreaks[2] ); + } + + self [[ GetFunction( "maps/mp/teams/_teams", "set_player_model" ) ]]( team, weapon ); + self [[ GetFunction( "maps/mp/gametypes/_class", "initstaticweaponstime" ) ]](); + self thread [[ GetFunction( "maps/mp/gametypes/_class", "initweaponattachments" ) ]]( spawnweapon ); + self setplayerrenderoptions( playerrenderoptions ); + + if ( isdefined( self.movementspeedmodifier ) ) + { + self setmovespeedscale( self.movementspeedmodifier * self getmovespeedscale() ); + } + + if ( isdefined( level.givecustomloadout ) ) + { + spawnweapon = self [[ level.givecustomloadout ]](); + + if ( isdefined( spawnweapon ) ) + { + self thread [[ GetFunction( "maps/mp/gametypes/_class", "initweaponattachments" ) ]]( spawnweapon ); + } + } + + self [[ GetFunction( "maps/mp/gametypes/_class", "cac_selector" ) ]](); + + if ( !isdefined( self.firstspawn ) ) + { + if ( isdefined( spawnweapon ) ) + { + self initialweaponraise( spawnweapon ); + } + else + { + self initialweaponraise( weapon ); + } + } + else + { + self seteverhadweaponall( 1 ); + } + + self.firstspawn = 0; + pixendevent(); +} diff --git a/storage/t6/scripts/zm/ranked.gsc b/storage/t6/scripts/zm/ranked.gsc index d7d43eb..3616a34 100644 --- a/storage/t6/scripts/zm/ranked.gsc +++ b/storage/t6/scripts/zm/ranked.gsc @@ -1,13 +1,138 @@ +#include common_scripts\utility; +#include maps\mp\_utility; +#include maps\mp\zombies\_zm_utility; + +main() +{ + if ( GetDvarInt( "scr_disablePlutoniumFixes" ) ) + { + return; + } + + if ( isDedicated() ) + { + // fix menuresponse exploits + replaceFunc( GetFunction( "maps/mp/gametypes_zm/_zm_gametype", "menu_onmenuresponse" ), ::menu_onmenuresponse_fix, -1 ); + + // fix player spawnpoints + replaceFunc( GetFunction( "maps/mp/zombies/_zm", "getfreespawnpoint" ), ::getFreeSpawnpoint_override, -1 ); + + // use coop revive + level.using_solo_revive = false; + replaceFunc( GetFunction( "maps/mp/zombies/_zm", "check_quickrevive_for_hotjoin" ), ::check_quickrevive_for_hotjoin_fix, -1 ); + + // add a timeout for all_players_connected and kill solo revive + replaceFunc( GetFunction( "maps/mp/zombies/_zm", "onallplayersready" ), ::onallplayersready_override, -1 ); + + // make sure game is coop mode + func = GetFunction( "maps/mp/zm_alcatraz_utility", "check_solo_status" ); + + if ( !isDefined( func ) ) + { + func = GetFunction( "maps/mp/zm_tomb_utility", "check_solo_status" ); + } + + if ( isDefined( func ) ) + { + replaceFunc( func, ::check_solo_status_override, -1 ); + } + + // fix inert zombies spawning + func = GetFunction( "maps/mp/zm_transit_classic", "spawn_inert_zombies" ); + + if ( isDefined( func ) ) + { + replaceFunc( func, ::spawn_inert_zombies_override, -1 ); + } + + // fix tombstone spawning + func = GetFunction( "maps/mp/zm_transit_utility", "solo_tombstone_removal" ); + + if ( isDefined( func ) ) + { + replaceFunc( func, ::noop, -1 ); + } + } + + // replaceFunc( GetFunction( "maps/mp/animscripts/zm_utility", "wait_network_frame" ), ::wait_network_frame_override, -1 ); + // replaceFunc( GetFunction( "maps/mp/zombies/_zm_utility", "wait_network_frame" ), ::wait_network_frame_override, -1 ); +} + init() { + if ( GetDvarInt( "scr_disablePlutoniumFixes" ) ) + { + return; + } + + // enable 8 player zombie games level.player_too_many_players_check = false; if ( isDedicated() ) { + // fix ranking level thread upload_stats_on_round_end(); level thread upload_stats_on_game_end(); level thread upload_stats_on_player_connect(); + + // fix teamchange exploit + level.allow_teamchange = getgametypesetting( "allowInGameTeamChange" ) + ""; + SetDvar( "ui_allow_teamchange", level.allow_teamchange ); } + + level thread watch_all_zombies(); +} + +watch_all_zombies() +{ + can_mantle_over_zambies = ( level.script == "zm_transit" || level.script == "zm_transit_dr" || level.script == "zm_prison" || level.script == "zm_nuked" || level.script == "zm_highrise" ); + + for ( ;; ) + { + zombies = getaiarray( level.zombie_team ); + + foreach ( zombie in zombies ) + { + if ( isDefined( zombie.pluto_audit ) ) + { + continue; + } + + zombie.pluto_audit = true; + + // fix jumping over zombies + if ( isDedicated() && can_mantle_over_zambies ) + { + zombie thread fix_zombie_physparams(); + } + } + + wait 0.05; + } +} + +fix_zombie_physparams() +{ + self endon( "death" ); + + if ( !is_true( self.completed_emerging_into_playable_area ) ) + { + self waittill( "completed_emerging_into_playable_area" ); + } + + animname = self.animname; + + if ( !isDefined( animname ) ) + { + animname = self.targetname; + } + + if ( animname != "zombie" || !self.has_legs ) + { + return; + } + + self setphysparams( 15, 0, 60 ); } upload_stats_on_round_end() @@ -51,3 +176,356 @@ delay_uploadstats( delay ) wait delay; uploadstats(); } + +check_quickrevive_for_hotjoin_fix( disconnecting_player ) +{ + should_update = 0; + solo_mode = 0; + + if ( flag( "solo_game" ) ) + { + should_update = 1; + } + + flag_clear( "solo_game" ); + + level.using_solo_revive = solo_mode; + level.revive_machine_is_solo = solo_mode; + [[ GetFunction( "maps/mp/zombies/_zm", "set_default_laststand_pistol" ) ]]( solo_mode ); + + if ( should_update && isdefined( level.quick_revive_machine ) ) + { + [[ GetFunction( "maps/mp/zombies/_zm", "update_quick_revive" ) ]]( solo_mode ); + } +} + +menu_onmenuresponse_fix() +{ + self endon( "disconnect" ); + + for ( ;; ) + { + self waittill( "menuresponse", menu, response ); + + if ( response == "back" ) + { + self closemenu(); + self closeingamemenu(); + + if ( level.console ) + { + if ( menu == game["menu_changeclass"] || menu == game["menu_changeclass_offline"] || menu == game["menu_team"] || menu == game["menu_controls"] ) + { + if ( self.pers["team"] == "allies" ) + { + self openmenu( game["menu_class"] ); + } + + if ( self.pers["team"] == "axis" ) + { + self openmenu( game["menu_class"] ); + } + } + } + + continue; + } + + if ( response == "changeteam" && level.allow_teamchange == "1" ) + { + self closemenu(); + self closeingamemenu(); + self openmenu( game["menu_team"] ); + } + + if ( response == "changeclass_marines" ) + { + self closemenu(); + self closeingamemenu(); + self openmenu( game["menu_changeclass_allies"] ); + continue; + } + + if ( response == "changeclass_opfor" ) + { + self closemenu(); + self closeingamemenu(); + self openmenu( game["menu_changeclass_axis"] ); + continue; + } + + if ( response == "changeclass_wager" ) + { + self closemenu(); + self closeingamemenu(); + self openmenu( game["menu_changeclass_wager"] ); + continue; + } + + if ( response == "changeclass_custom" ) + { + self closemenu(); + self closeingamemenu(); + self openmenu( game["menu_changeclass_custom"] ); + continue; + } + + if ( response == "changeclass_barebones" ) + { + self closemenu(); + self closeingamemenu(); + self openmenu( game["menu_changeclass_barebones"] ); + continue; + } + + if ( response == "changeclass_marines_splitscreen" ) + { + self openmenu( "changeclass_marines_splitscreen" ); + } + + if ( response == "changeclass_opfor_splitscreen" ) + { + self openmenu( "changeclass_opfor_splitscreen" ); + } + + if ( menu == game["menu_team"] && level.allow_teamchange == "1" ) + { + switch ( response ) + { + case "allies": + self [[ level.allies ]](); + break; + + case "axis": + self [[ level.teammenu ]]( response ); + break; + + case "autoassign": + self [[ level.autoassign ]]( 1 ); + break; + + case "spectator": + self [[ level.spectator ]](); + break; + } + + continue; + } + + if ( menu == game["menu_changeclass"] || menu == game["menu_changeclass_offline"] || menu == game["menu_changeclass_wager"] || menu == game["menu_changeclass_custom"] || menu == game["menu_changeclass_barebones"] ) + { + self closemenu(); + self closeingamemenu(); + + if ( level.rankedmatch && issubstr( response, "custom" ) ) + { + + } + + self.selectedclass = 1; + self [[ level.class ]]( response ); + } + } +} + +check_solo_status_override() +{ + level.is_forever_solo_game = false; +} + +wait_network_frame_override() +{ + wait 0.05; +} + +noop() +{ +} + +getFreeSpawnpoint_override( spawnpoints, player ) +{ + if ( !isdefined( spawnpoints ) ) + { +/# + iprintlnbold( "ZM >> No free spawn points in map" ); +#/ + return undefined; + } + + if ( !isdefined( game["spawns_randomized"] ) ) + { + game["spawns_randomized"] = 1; + spawnpoints = array_randomize( spawnpoints ); + random_chance = randomint( 100 ); + + if ( random_chance > 50 ) + { + set_game_var( "side_selection", 1 ); + } + else + { + set_game_var( "side_selection", 2 ); + } + } + + side_selection = get_game_var( "side_selection" ); + + if ( get_game_var( "switchedsides" ) ) + { + if ( side_selection == 2 ) + { + side_selection = 1; + } + else if ( side_selection == 1 ) + { + side_selection = 2; + } + } + + if ( isdefined( player ) && isdefined( player.team ) ) + { + i = 0; + + while ( isdefined( spawnpoints ) && i < spawnpoints.size ) + { + if ( side_selection == 1 ) + { + if ( player.team != "allies" && ( isdefined( spawnpoints[i].script_int ) && spawnpoints[i].script_int == 1 ) ) + { + arrayremovevalue( spawnpoints, spawnpoints[i] ); + i = 0; + } + else if ( player.team == "allies" && ( isdefined( spawnpoints[i].script_int ) && spawnpoints[i].script_int == 2 ) ) + { + arrayremovevalue( spawnpoints, spawnpoints[i] ); + i = 0; + } + else + { + i++; + } + } + else if ( player.team == "allies" && ( isdefined( spawnpoints[i].script_int ) && spawnpoints[i].script_int == 1 ) ) + { + arrayremovevalue( spawnpoints, spawnpoints[i] ); + i = 0; + } + else if ( player.team != "allies" && ( isdefined( spawnpoints[i].script_int ) && spawnpoints[i].script_int == 2 ) ) + { + arrayremovevalue( spawnpoints, spawnpoints[i] ); + i = 0; + } + else + { + i++; + } + } + } + + max_clients = getdvarint( "sv_maxclients" ); + + if ( !isdefined( self.playernum ) ) + { + self.playernum = self getentitynumber(); + + assert( self.playernum < max_clients ); + } + + for ( j = 0; j < spawnpoints.size; j++ ) + { + if ( !isdefined( spawnpoints[j].en_num ) ) + { + for ( m = 0; m < spawnpoints.size; m++ ) + { + spawnpoints[m].en_num = m % max_clients; + } + } + + if ( spawnpoints[j].en_num == self.playernum ) + { + return spawnpoints[j]; + } + } + +/# + print( "getFreeSpawnpoint: no spawns found, use first spawn" ); +#/ + assert( spawnpoints.size > 0 ); + return spawnpoints[0]; +} + +onallplayersready_override() +{ +/# + println( "ZM >> player_count_expected=" + getnumexpectedplayers() ); +#/ + + player_count_actual = 0; + timeout_started = false; + timeout_point = 0; + + while ( getnumexpectedplayers() == 0 || getnumconnectedplayers() < getnumexpectedplayers() || player_count_actual != getnumexpectedplayers() ) + { + players = get_players(); + player_count_actual = 0; + + for ( i = 0; i < players.size; i++ ) + { + players[i] freezecontrols( 1 ); + + if ( players[i].sessionstate == "playing" ) + { + player_count_actual++; + } + } + + if ( player_count_actual > 0 ) + { + if ( !timeout_started ) + { + timeout_started = true; + timeout_point = ( getDvarFloat( "sv_connecttimeout" ) * 1000 ) + getTime(); + } + + if ( getTime() > timeout_point ) + { + break; + } + } + else + { + timeout_started = false; + } + +/# + println( "ZM >> Num Connected =" + getnumconnectedplayers() + " Expected : " + getnumexpectedplayers() ); +#/ + wait 0.1; + } + + setinitialplayersconnected(); + +/# + println( "ZM >> We have all players - START ZOMBIE LOGIC" ); +#/ + + flag_set( "initial_players_connected" ); + + while ( !aretexturesloaded() ) + { + wait 0.05; + } + + thread [[ GetFunction( "maps/mp/zombies/_zm", "start_zombie_logic_in_x_sec" ) ]]( 3.0 ); + + [[ GetFunction( "maps/mp/zombies/_zm", "fade_out_intro_screen_zm" ) ]]( 5.0, 1.5, 1 ); +} + +spawn_inert_zombies_override() +{ + flag_wait( "initial_players_connected" ); + + func = GetFunction( "maps/mp/zm_transit_classic", "spawn_inert_zombies" ); + disableDetourOnce( func ); + self [[func]](); +}