Skip to content

Instantly share code, notes, and snippets.

@ProdigySim
Created February 4, 2013 12:07
Show Gist options
  • Select an option

  • Save ProdigySim/4706381 to your computer and use it in GitHub Desktop.

Select an option

Save ProdigySim/4706381 to your computer and use it in GitHub Desktop.
Current progress of scoring WIP
#include <sourcemod>
#include <sdktools>
#include <left4downtown>
#include <l4d2_direct>
#define L4D2UTIL_STOCKS_ONLY
#include <l4d2util>
//#include <sendproxy>
public Action:L4D2_OnEndVersusModeRound(bool:countSurvivors)
{
new campaignScores[2];
new chapterScores[2];
new bool:tiebreak = false;
new curTeam = GameRules_GetProp("m_bAreTeamsFlipped", 1);
CTerrorGameRules_CalculateSurvivalMultiplier(countSurvivors);
L4D2_GetVersusCampaignScores(campaignScores);
new directScores0 = L4D2Direct_GetVSCampaignScore(0);
new directScores1 = L4D2Direct_GetVSCampaignScore(1);
PrintToChatAll("EndVersusModeRound invoked. CampaignScores (%d,%d) direct (%d,%d)",
campaignScores[0], campaignScores[1], directScores0, directScores1);
chapterScores[0] = (campaignScores[0] * 2) + 1;
chapterScores[1] = (campaignScores[1] * 2) + 2;
ShowRoundEndScores(campaignScores[0],campaignScores[1],chapterScores[0],chapterScores[1],tiebreak);
L4D2_SetVersusCampaignScores(campaignScores);
L4D2Direct_DirectorEndScenario(5);
SetVersusRoundInProgress(false);
if(InSecondHalfOfRound() && L4D_IsMissionFinalMap())
{
FireMatchEndEvent(CalculateWinner(campaignScores[0], campaignScores[1]));
}
PrintToChatAll("EndVersusModeRound replacement finished. Blocking invocation.");
// Block invocation since we're implementing it ourselves
return Plugin_Handled;
}
// emulate CTerrorGameRules::CalculateSurvivalMultiplier(bool)
stock CTerrorGameRules_CalculateSurvivalMultiplier(bool:countSurvivors)
{
new survivalMultiplier;
if( countSurvivors )
{
if(CDirector_IsFinale())
{
// Count the mystery array in CTerrorGameRules @ 1040
}
else
{
survivalMultiplier = CountSurvivorMutliplier();
}
}
else
{
// SurvivalMultiplier of 0.
}
// There's a PCZ check here that's used to decide whether or not
// to read m_bAreTeamsFlipped. I skip it since that shouldn't really
// ever be the case. However, I suppose you can disable PCZs in
// some mutation. Who knows what will happen then. Probably nothing.
new team = GameRules_GetProp("m_bAreTeamsFlipped", 1);
SetSurvivalMultiplier(survivalMultiplier, team);
}
// emulate statechanged notification checking
stock SetSurvivalMultiplier(multiplier, team)
{
new oldMultiplier = GameRules_GetProp("m_iVersusSurvivalMultiplier", _, team);
new bool:changeState = oldMultiplier != multiplier;
GameRules_SetProp("m_iVersusSurvivalMultiplier", survivalMultiplier, _, element, changeState);
}
// Somewhat emulates ForEachTerrorPlayer<SurvivorMultiplierCounter>();
stock CountSurvivorMutliplier()
{
new count;
for(new client = 1; client < MaxClients+1; client++)
{
if(IsSurvivor(client) && IsPlayerAlive(client))
{
// The actual function here does a bunch of nav area checks--
// to make sure the player is actually inside the saferoom.
// But, the round should be ended by this point--which will
// only happen if all players outside of saferooms are dead.
// The check is volatile (nav area failure == count failure) so
// I skip it.
count++;
}
}
return count;
}
// emulate CDirector::IsFinale()
stock bool:CDirector_IsFinale()
{
return L4D2Direct_GetFinaleType() != FINALETYPE_NONE;
}
stock SetVersusRoundInProgress(bool:inProgress)
{
static Address:pRoundInProgress = Address_Null;
if(pRoundInProgress == Address_Null)
{
new offset = L4D2Direct_GetCDirectorVersusModeOffset("m_bVersusRoundInProgress");
if(offset == -1)
{
SetFailState("Failed to read CDirectorVersusMode::m_bVersusRoundInProgress offset");
}
pRoundInProgress = L4D2Direct_GetCDirectorVersusMode() + Address:offset;
}
StoreToAddress(pRoundInProgress, inProgress ? 1 : 0, NumberType_Int8);
}
// Campaign score 1 (pre),
// Campaign score 2 (pre),
// Chapter score 1,
// Chapter score 2,
// Show tiebreak
ShowRoundEndScores(t1,t2,c1,c2,bool:tiebreak)
{
new iTiebreak = tiebreak ? 1 : 0;
new Handle:scoreKv = CreateKeyValues("scores");
KvSetNum(scoreKv, "t1", t1);
KvSetNum(scoreKv, "t2", t2);
KvSetNum(scoreKv, "c1", c1);
KvSetNum(scoreKv, "c2", c2);
KvSetNum(scoreKv, "tiebreak", iTiebreak);
for(new i = 1; i <= MaxClients; i++)
{
if(IsClientInGame(i) && !IsFakeClient(i))
{
ShowVGUIPanel(i, "fullscreen_vs_scoreboard", scoreKv);
}
}
}
// 0: tie
// 1: team 0/1
// 2: team 1/2
FireMatchEndEvent(winner)
{
new Handle:event = CreateEvent("versus_match_finished", true);
SetEventInt(event, "winners", winner);
FireEvent(event);
}
stock CalculateWinner(score1, score2)
{
// The disasm of valve's version of this calculation is pretty cute.
// Possibly a compiler optimization.
// I try to be a little more explicit but still efficient.
new diff = score1 - score2;
if(diff > 0)
{
// score1 > score2 == winner 1
return 1;
}
else if (diff < 0)
{
// score2 > score 1 == winner 2
return 2;
}
// score2 == score1 === winner 0
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment