/*-- Temple Pushing --*/
#strict 2
static const MODE_Classic = 0, MODE_Magic = 1, MODE_Festive = 2, MODE_Apocalyptic = 3, MODE_Knightly = 4;
static const SBRD_RelaunchesCol = 0;
static const MaxTeamCount = 2;
static SBRD_ScoreCol;
static section, mode;
static numRelaunches;
static deathmatchEnabled, deathmatchWinScore;
static playerDeaths, playerScore, teamScore;
static ambienceEnabled;
static suddendeathEnabled;
static loadingSection;
static gameStartMessage, countdown, gameStarted, gameOver;
func Initialize()
{
ShowLobby();
// Create Thrones for recreational purposes
CreateObject(THRN, 590, 420, NO_OWNER);
CreateObject(THRN, 410, 210, NO_OWNER);
// Initial values
deathmatchWinScore = 20;
playerDeaths = CreateArray();
playerScore = CreateArray();
teamScore = CreateArray();
countdown = 3;
// Create setup menu
var menu = CreateObject(SPMU, 0, 0, NO_OWNER);
menu->LocalN("extinguisherEnabled") = true;
menu->LocalN("rotateInJumpEnabled") = true;
menu->LocalN("numRelaunches") = 10;
menu->LocalN("deathmatchWinScore") = 20;
menu->LocalN("ambienceEnabled") = true;
ScriptGo(true);
}
/*func TeamRow(int team) { return 1 + team * 1000; }
func PlayerRow(int player) { return TeamRow(GetPlayerTeam(player)) + GetPlayerID(player); }*/
func TeamRow(int team) { return team; }
func PlayerRow(int player) { return MaxTeamCount + 1 + GetPlayerID(player); }
func InitializePlayer(int player)
{
SetFoW(false, player);
if (LobbyActive()) return;
playerDeaths[GetPlayerID(player)] = 0;
if (deathmatchEnabled)
{
var team = GetPlayerTeam(player);
if (team)
{
SetScoreboardData(TeamRow(team), SBRD_Caption, Format("Team %s", GetTeamColor(team), GetTeamName(team)), 0);
}
}
SetScoreboardData(PlayerRow(player), SBRD_Caption, GetTaggedPlayerName(player), 1);
UpdateScoreboard(player);
LaunchClonk(player, GetCrew(player));
}
func GetRelaunchesLeft(int player) { return numRelaunches - playerDeaths[GetPlayerID(player)]; }
func RemovePlayer(int player)
{
// Leaver?
if (!LobbyActive() && (!deathmatchEnabled && GetRelaunchesLeft(player) >= 0 || deathmatchEnabled && !gameOver))
{
UpdateScoreboard(player, true);
}
}
func UpdateScoreboard(int player, bool leaver)
{
var playerID = GetPlayerID(player);
var text;
if (!deathmatchEnabled) SBRD_ScoreCol = 1;
if (deathmatchEnabled) SBRD_ScoreCol = 0;
// Ignore invalid player numbers
if (!playerID) return;
// Deathmatch only: Team rows
if (deathmatchEnabled)
{
var team = GetPlayerTeam(player);
if (team)
{
if (teamScore[team] == deathmatchWinScore)
text = "$Win$";
else
text = Format("%d", GetTeamColor(team), teamScore[team]);
SetScoreboardData(TeamRow(team), SBRD_ScoreCol, text, teamScore[team]);
}
}
// Normal mode only: Show remaining relaunches
if (!deathmatchEnabled)
{
var relaunchesLeft = numRelaunches - playerDeaths[playerID];
if (relaunchesLeft < 0)
text = "$Death$";
else if (leaver)
text = "{{SLVR}}";
else
text = Format("%d", relaunchesLeft);
SetScoreboardData(PlayerRow(player), SBRD_RelaunchesCol, text, relaunchesLeft);
}
// Normal and deathmatch mode: Show score
if (leaver)
text = "{{SLVR}}";
else
text = Format("%d", playerScore[playerID]);
SetScoreboardData(PlayerRow(player), SBRD_ScoreCol, text, playerScore[playerID]);
// Sort rows
if (!deathmatchEnabled)
{
SortScoreboard(SBRD_RelaunchesCol, true);
SortScoreboard(SBRD_ScoreCol, true);
}
else SortScoreboard(SBRD_ScoreCol, true);
SortScoreboard(SBRD_Caption);
}
func ShowLobby()
{
LoadScenarioSection("Lobby");
}
func StartGame(object menu, string message)
{
// Copy settings from menu
var sectionID = menu->LocalN("section");
mode = menu->LocalN("mode");
var extinguisherEnabled = menu->LocalN("extinguisherEnabled");
var suddendeathEnabled = menu->LocalN("suddendeathEnabled");
var rotateInJumpEnabled = menu->LocalN("rotateInJumpEnabled");
ambienceEnabled = menu->LocalN("ambienceEnabled");
numRelaunches = menu->LocalN("numRelaunches");
deathmatchEnabled = menu->LocalN("deathmatchEnabled");
deathmatchWinScore = menu->LocalN("deathmatchWinScore");
menu->RemoveObject();
// Change scenario section
loadingSection = true;
Log("Before LoadScenarioSection()");
LoadScenarioSection(DefinitionCall(sectionID, "SectionName"));
Log("After LoadScenarioSection()");
loadingSection = false;
section = CreateObject(sectionID, 0, 0, NO_OWNER);
// Create spawnpoints
if (mode != MODE_Apocalyptic)
{
Log("Creating spawnpoints");
var spawnPointSpawner = CreateObject(SPSR, 0, 0, NO_OWNER);
spawnPointSpawner->SetLocations(section->SpawnpointLocations());
if (mode == MODE_Classic)
{
spawnPointSpawner->SetDefinitions([[ROCK, 7], [SCRL, 5], [FLNT, 7], [SFLN, 9], [STFN, 3], [EFLN, 5], [FBMP, 4], [ARWP, 4]]);
spawnPointSpawner->SetSpawnInterval(900);
}
if (mode == MODE_Festive)
{
spawnPointSpawner->SetDefinitions([[TSWB, 10], [SCRL, 5]]);
spawnPointSpawner->SetSpawnInterval(600);
}
if (mode == MODE_Knightly)
{
spawnPointSpawner->SetDefinitions([[SFLN, 3], [EFLN,3], [SWOR, 5], [AXE1, 5], [SPER, 2], [ARWP, 5], [SCRL, 5], [FARP, 3]]);
spawnPointSpawner->SetSpawnInterval(1000);
}
if (mode == MODE_Magic)
{
spawnPointSpawner->SetDefinitions([[SCRL, 14], [GBLT, 1]]);
spawnPointSpawner->SetSpawnInterval(750);
spawnPointSpawner->SetSpawnpointGamma(RGB(5, 5, 10), RGB(80, 80, 150), RGB(200, 200, 255));
}
spawnPointSpawner->CreateSpawnPoints();
}
// Effects for apocalypse mode
if (mode == MODE_Apocalyptic)
{
AddEffect("CreateTeraFlints", 0, 20, 90);
AddEffect("ShakeScreen", 0, 20, 50);
AddEffect("Bottom", 0, 20, 2);
AddEffect("SkyAdjust", 0, 20, 1);
}
// Effects for festive mode
if (mode == MODE_Festive)
{
if (ambienceEnabled) CreateObject(SNOR, 0, 0, NO_OWNER);
SetSkyAdjust(RGB(189, 189, 255));
SetGamma(RGB(0, 0, 50), RGB(100, 100, 128), RGB(200, 200, 255));
}
// Create melee goal
Log("Creating melee goal");
CreateObject(MELE, 0, 0, NO_OWNER);
// Create rules
Log("Creating rules");
if (extinguisherEnabled) CreateObject(_ETG, 0, 0, NO_OWNER);
if (suddendeathEnabled) CreateObject(SDDT, 0, 0, NO_OWNER);
if (rotateInJumpEnabled) CreateObject(RIJP, 0, 0, NO_OWNER);
CreateObject(OFDR, 0, 0, NO_OWNER);
// Create mode objects
if (mode == MODE_Magic)
{
CreateObject(MLPG, 0, 0, NO_OWNER);
}
if (mode == MODE_Festive)
{
CreateObject(FSTV, 0, 0, NO_OWNER);
}
if (mode == MODE_Knightly)
{
CreateObject(MKNI, 0, 0, NO_OWNER);
}
if (mode == MODE_Apocalyptic)
{
CreateObject(APCE, 0, 0, NO_OWNER);
}
// Deathmatch?
if (deathmatchEnabled)
{
Log("Creating deathmatch rule");
CreateObject(DTHM, 0, 0, NO_OWNER);
SetScoreboardData(SBRD_Caption, 0, "{{SKIL}}");
SetScoreboardData(0, SBRD_Caption, "$WinScore$");
SetScoreboardData(0, 0, Format("%d $Kills$", deathmatchWinScore));
}
else
{
SetScoreboardData(SBRD_Caption, 0, "{{SREL}}");
SetScoreboardData(SBRD_Caption, 1, "{{SKIL}}");
}
// Initialize players
Log("Initializing players");
for (var i = 0; i < GetPlayerCount(); ++i)
{
InitializePlayer(GetPlayerByIndex(i));
}
Log("Starting countdown");
gameStartMessage = message;
ShowCountdown();
Log("StartGame() done");
}
func ShowCountdown()
{
if (countdown == 0)
{
gameStartMessage = 0;
Message("");
Schedule("SetMaxPlayer(0)", 60 * 38);
// Reenable crew
for (var i = 0; i < GetPlayerCount(); ++i)
{
var player = GetPlayerByIndex(i);
var clonk = GetCrew(player);
if (!clonk) continue; // Happens if player still in team choice menu
// Relaunch if clonk does not stand.
if (clonk->GetAction() != "Walk")
{
clonk->Kill();
clonk = GetCrew(player);
}
clonk->SetCrewEnabled(true);
SelectCrew(player, clonk, true);
// Make sure all clonks start with equal health
clonk->Extinguish();
ResetHealth(clonk);
}
gameStarted = true;
}
else
{
Message("@%d|%s", 0, countdown, gameStartMessage);
Schedule("GameCall(\"ShowCountdown\")", 38);
}
--countdown;
}
func LobbyActive() { return !section; }
func HandleKill(int killed, int killer)
{
// Assume suicide if killer cannot be determined
if (killer == NO_OWNER) killer = killed;
var killedEliminated = false;
var killedID = GetPlayerID(killed), killerID = GetPlayerID(killer);
var killedTeam = GetPlayerTeam(killed), killerTeam = GetPlayerTeam(killer);
var teamKill = killedTeam != 0 && killedTeam == killerTeam;
++playerDeaths[killedID];
// Update kill score
if (!teamKill)
{
++playerScore[killerID];
if (killerTeam) ++teamScore[killerTeam];
}
else if (teamKill && killed != killer) // Decrement score if it's a team kill but not a suicide
{
if (playerScore[killerID] > 0) // Prevent negative scores
{
--playerScore[killerID];
if (killerTeam) --teamScore[killerTeam];
}
}
// Show relaunch message or eliminate player if no relaunches left
var relaunchesLeft;
if (!deathmatchEnabled)
{
relaunchesLeft = numRelaunches - playerDeaths[killedID];
if (relaunchesLeft < 0)
{
EliminatePlayer(killed);
killedEliminated = true;
}
else if (relaunchesLeft == 0)
{
PlayerMessage(killed, "$MsgLastRelaunch$");
}
else if (relaunchesLeft == 1)
{
PlayerMessage(killed, "$MsgOneRelaunch$");
}
else // More than one relaunch left
{
PlayerMessage(killed, "$MsgRelaunch$", 0, relaunchesLeft);
}
}
// Check for deathmatch winner
if (deathmatchEnabled)
{
// Teams enabled?
if (killerTeam)
{
// Winner?
if (teamScore[killerTeam] >= deathmatchWinScore)
{
// Eliminate all players of losing teams
for (var player in GetPlayers())
{
if (GetPlayerTeam(player) != killerTeam) EliminatePlayer(player);
}
Message(Format("$MsgDeathmatchWin$", GetTeamColor(killerTeam), GetTeamName(killerTeam)));
gameOver = true;
killedEliminated = true;
}
}
else // No teams?
{
// Winner?
if (playerScore[killerID] >= deathmatchWinScore)
{
// Eliminate all losing players
for (var player in GetPlayers())
{
if (player != killer) EliminatePlayer(player);
}
gameOver = true;
killedEliminated = true;
}
}
}
UpdateScoreboard(killed);
UpdateScoreboard(killer);
return killedEliminated;
}
func GetPlayers()
{
var players = CreateArray(GetPlayerCount());
for (var i = 0; i < GetPlayerCount(); ++i)
{
players[i] = GetPlayerByIndex(i);
}
return players;
}
func OnClonkDeath(object clonk, int killedBy)
{
if (loadingSection) return;
var player = clonk->GetOwner();
// Do nothing if Clonk does not belong to a player
if (GetPlayerID(player) == 0) return;
// Handle kill and return if player got eliminated
if (gameStarted && HandleKill(player, killedBy)) return;
// Relaunch
if (GetPlayerID(player)) LaunchClonk(player, clonk, true);
}
func LaunchClonk(int player, object clonk, bool relaunch)
{
if (!clonk || relaunch)
{
var newClonk = CreateObject(CLNK, 0, 0, player);
if (clonk)
{
newClonk->GrabObjectInfo(clonk);
}
else
{
MakeCrewMember(newClonk, player);
}
clonk = newClonk;
}
ResetHealth(clonk);
SelectCrew(player, clonk, true);
// Move clonk to random position
var NumCheckPos = 10;
var positions = CreateArray(NumCheckPos);
for (var i = 0; i < NumCheckPos; ++i)
{
var wipf = PlaceAnimal(WIPF);
positions[i] = IIf(wipf, [wipf->GetX(), wipf->GetY()], [LandscapeWidth() / 2, LandscapeHeight() / 2]);
if (wipf) wipf->RemoveObject();
}
// Prevent spawning too close to an enemy
var bestPosition = positions[0], bestDistance = 0;
for (var pos in positions)
{
var closestHostileClonk = FindObject2(Find_OCF(OCF_CrewMember | OCF_Alive), Find_Hostile(player), Sort_Distance(pos[0], pos[1]));
if (!closestHostileClonk) break;
var distance = Distance(pos[0], pos[1], closestHostileClonk->GetX(), closestHostileClonk->GetY());
if (distance > bestDistance)
{
bestPosition = pos;
bestDistance = distance;
}
}
clonk->SetPosition(bestPosition[0], bestPosition[1]);
// Still in countdown?
if (!LobbyActive() && !gameStarted)
{
clonk->SetCrewEnabled(false);
}
// No corpses in apocalopyse mode
if (mode == MODE_Apocalyptic) clonk->LocalN("removeOnDeath") = true;
// Enable rotation in jump for the clonk if allowed by rule
if (FindObject(RIJP)) clonk->LocalN("rotateInJump") = true;
// Do not immediately pass player control to the new clonk to prevent accidental jumps
if (relaunch)
{
clonk->SetCrewEnabled(false);
clonk->SetCrewEnabled(true);
clonk->Schedule("SelectCrew(GetOwner(), this, true)", 20);
}
PlayerMessage(player, "{{SREL}}", clonk);
DrawParticleLine("PSpark", GetX(clonk), 0, GetX(clonk), GetY(clonk), 10, 150, RGBa(Random(255), Random(255), Random(255),50), RGBa(Random(255), Random(255), Random(255),50), 0);
DrawParticleLine("PSpark", GetX(clonk) + RandomX(5, 60), 0, GetX(clonk), GetY(clonk), 10, 150, RGBa(Random(255), Random(255), Random(255),50), RGBa(Random(255), Random(255), Random(255),50), 0);
DrawParticleLine("PSpark", GetX(clonk) - RandomX(5, 60), 0, GetX(clonk), GetY(clonk), 10, 150, RGBa(Random(255), Random(255), Random(255),50), RGBa(Random(255), Random(255), Random(255),50), 0);
Sound("PlayerJoin", 0, clonk, 100);
CastParticles("MSpark", 50, 20, GetX(clonk), GetY(clonk), 50, 75, RGBa(128,128,255,0), RGBa(255,255,255,127));
SetPlrView(player, clonk);
ResetHealth(clonk);
}
func ResetHealth(object clonk)
{
if (suddendeathEnabled)
{
clonk->DoEnergy(1 - clonk->GetEnergy());
}
else
{
clonk->DoEnergy(100);
}
}
func CheckDeathmatchKillCount()
{
if (GetTeamCount() == 0)
{
var winner = NO_OWNER;
// Check if a player has reached the kill limit
for (var i = 0; i < GetPlayerCount(); ++i)
{
var plr = GetPlayerByIndex(i);
if (playerScore[plr] >= deathmatchWinScore)
{
winner = plr;
break;
}
}
// Eliminate other players if there is a winner
for (var i = 0; i < GetPlayerCount(); ++i)
{
var plr = GetPlayerByIndex(i);
if (plr != winner) EliminatePlayer(plr);
}
}
else
{
var winnerTeam = 0;
// Clear team score array
teamScore = CreateArray(GetTeamCount());
// Calculate team score and check if kill limit is reached
for (var i = 0; i < GetPlayerCount(); ++i)
{
var plr = GetPlayerByIndex(i), team = GetPlayerTeam(plr);
teamScore[team] += playerScore[plr];
if (teamScore >= deathmatchWinScore)
{
winnerTeam = team;
break;
}
}
// Do we have a winner?
if (winnerTeam != 0)
{
for (var i = 0; i < GetPlayerCount(); ++i)
{
var plr = GetPlayerByIndex(i);
if (GetPlayerTeam(plr) != winnerTeam) EliminatePlayer(plr);
}
}
}
}