Extract Goal_MultiRoundMelee from HotIce

There are currently multiple multi-round scenarios in development that
all copy most of HotIce's >500-line scenario script for the multi-round
logic. This commit isolates that logic in a goal with an easy-to-use
interface.
master
Lukas Werling 2018-03-31 15:59:19 +02:00
parent 07e66279be
commit 1382478774
12 changed files with 459 additions and 349 deletions

View File

@ -7,7 +7,7 @@ Icon=21
[Game]
Mode=Melee
Goals=Goal_Melee=1;
Goals=Goal_MultiRoundMelee=1;
Rules=Rule_KillLogs=1;Rule_Gravestones=1;
[Landscape]

View File

@ -1,66 +1,7 @@
/* Hot ice */
static g_remaining_rounds, g_winners, g_check_victory_effect;
static g_gameover;
func Initialize()
func InitializeRound() // called by Goal_MultiRoundMelee
{
g_remaining_rounds = SCENPAR_Rounds;
g_winners = [];
InitializeRound();
Scoreboard->Init([
// Invisible team column for sorting players under their teams.
{key = "team", title = "", sorted = true, desc = false, default = "", priority = 90},
{key = "wins", title = "Wins", sorted = true, desc = true, default = 0, priority = 100},
{key = "death", title = "", sorted = false, default = "", priority = 0},
]);
}
// Resets the scenario, redrawing the map.
func ResetRound()
{
// Retrieve all Clonks.
var clonks = [];
for (var clonk in FindObjects(Find_OCF(OCF_CrewMember)))
{
var container = clonk->Contained();
if (container)
{
clonk->Exit();
container->RemoveObject();
}
else
{
// Players not waiting for a relaunch get a new Clonk to prevent
// status effects from carrying over to the next round.
var new_clonk = CreateObject(clonk->GetID(), 0, 0, clonk->GetOwner());
new_clonk->GrabObjectInfo(clonk);
clonk = new_clonk;
}
PushBack(clonks, clonk);
clonk->SetObjectStatus(C4OS_INACTIVE);
}
// Clear and redraw the map.
LoadScenarioSection("main");
InitializeRound();
AssignHandicaps();
// Re-enable the players.
for (var clonk in clonks)
{
clonk->SetObjectStatus(C4OS_NORMAL);
SetCursor(clonk->GetOwner(), clonk);
// Select the first item. This fixes item ordering.
clonk->SetHandItemPos(0, 0);
InitPlayerRound(clonk->GetOwner());
}
}
func InitializeRound()
{
// Checking for victory: Only active after a Clonk dies.
g_check_victory_effect = AddEffect("CheckVictory", nil, 1, 0);
g_player_spawn_index = 0;
if (GetType(g_player_spawn_positions) == C4V_Array)
ShuffleArray(g_player_spawn_positions);
@ -105,55 +46,20 @@ func InitializeRound()
if (IsFirestoneSpot(pos.x,pos.y))
CreateObjectAbove([Firestone,IronBomb][Random(Random(3))],pos.x,pos.y-1);
// The game starts after a delay to ensure that everyone is ready.
GUI_Clock->CreateCountdown(3);
SetSky(g_theme.Sky);
g_theme->InitializeRound();
g_theme->InitializeMusic();
return true;
}
static g_player_spawn_positions, g_map_width, g_player_spawn_index;
global func ScoreboardTeam(int team) { return team * 100; }
func InitializePlayer(int plr)
func InitPlayerRound(int plr, object crew) // called by Goal_MultiRoundMelee
{
// Add the player and their team to the scoreboard.
Scoreboard->NewPlayerEntry(plr);
Scoreboard->SetPlayerData(plr, "wins", "");
var team = GetPlayerTeam(plr);
Scoreboard->NewEntry(ScoreboardTeam(team), GetTeamName(team));
Scoreboard->SetData(ScoreboardTeam(team), "team", "", ScoreboardTeam(team));
Scoreboard->SetPlayerData(plr, "team", "", ScoreboardTeam(team) + 1);
// Players joining at runtime will participate in the following round.
// Should only happen if it's not game start, else Clonks would start stuck in a RelaunchContainer.
if (FrameCounter() > 1) PutInRelaunchContainer(GetCrew(plr));
}
func InitializePlayers()
{
AssignHandicaps();
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
InitPlayerRound(plr);
}
}
func InitPlayerRound(int plr)
{
// Unmark death on scoreboard.
Scoreboard->SetPlayerData(plr, "death", "");
// everything visible
SetFoW(false, plr);
SetPlayerViewLock(plr, true);
// Player positioning.
var ls_wdt = LandscapeWidth(), ls_hgt = LandscapeHeight();
var crew = GetCrew(plr), start_pos;
var start_pos;
// Position by map type?
if (SCENPAR_SpawnType == 0)
{
@ -202,7 +108,7 @@ func InitPlayerRound(int plr)
var ammo = launcher->CreateContents(IronBomb);
launcher->AddTimer(Scenario.ReplenishLauncherAmmo, 10);
// Start reloading the launcher during the countdown.
if (!IsHandicapped(plr))
if (!Goal_MultiRoundMelee->IsHandicapped(plr))
{
crew->SetHandItemPos(0, crew->GetItemPos(launcher));
// This doesn't play the animation properly - simulate a click instead.
@ -214,14 +120,16 @@ func InitPlayerRound(int plr)
}
crew.MaxEnergy = 100000;
crew->DoEnergy(1000);
// Disable the Clonk during the countdown.
crew->SetCrewEnabled(false);
crew->SetComDir(COMD_Stop);
if (SCENPAR_SpawnType == 1 && balloon)
balloon->CreateEffect(IntNoGravity, 1, 1);
}
return true;
func StartRound() // called by Goal_MultiRoundMelee
{
for (var clonk in FindObjects(Find_OCF(OCF_CrewMember)))
if (SCENPAR_SpawnType == 1 && clonk->GetActionTarget())
RemoveEffect("IntNoGravity", clonk->GetActionTarget());
}
local IntNoGravity = new Effect {
@ -230,219 +138,6 @@ local IntNoGravity = new Effect {
}
};
// Called by the round start countdown.
func OnCountdownFinished()
{
// Re-enable all Clonks.
for (var clonk in FindObjects(Find_OCF(OCF_CrewMember)))
{
clonk->SetCrewEnabled(true);
SetCursor(clonk->GetOwner(), clonk);
if (SCENPAR_SpawnType == 1 && clonk->GetActionTarget())
RemoveEffect("IntNoGravity", clonk->GetActionTarget());
}
}
func PutInRelaunchContainer(object clonk)
{
var plr = clonk->GetOwner();
var relaunch = CreateObject(RelaunchContainer, LandscapeWidth() / 2, LandscapeHeight() / 2, plr);
// We just use the relaunch object as a dumb container.
clonk->Enter(relaunch);
// Allow scrolling around the landscape.
SetPlayerViewLock(plr, false);
}
func OnClonkDeath(object clonk)
{
var plr = clonk->GetOwner();
// Mark death on scoreboard.
Scoreboard->SetPlayerData(plr, "death", "{{Scoreboard_Death}}");
// Skip eliminated players, NO_OWNER, etc.
if (GetPlayerName(plr))
{
var crew = CreateObject(Clonk, 0, 0, plr);
crew->MakeCrewMember(plr);
PutInRelaunchContainer(crew);
}
// Check for victory after three seconds to allow stalemates.
if (!g_gameover)
g_check_victory_effect.Interval = g_check_victory_effect.Time + 36 * 3;
}
// Returns an array of team -> number of players in team.
func GetTeamPlayers()
{
var result = CreateArray(GetTeamCount() + 1);
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i), team = GetPlayerTeam(plr);
SetLength(result, Max(team + 1, GetLength(result)));
result[team] = result[team] ?? [];
PushBack(result[team], plr);
}
return result;
}
static g_handicapped_players;
func _MinSize(int a, array b) { if (b == nil) return a; else return Min(a, GetLength(b)); }
// Assigns handicaps so that the number of not-handicapped players is the same for all teams.
func AssignHandicaps()
{
g_handicapped_players = CreateArray(GetPlayerCount());
var teams = GetTeamPlayers();
var smallest_size = Reduce(teams, Scenario._MinSize, ~(1<<31));
for (var team in teams) if (team != nil)
{
var to_handicap = GetLength(team) - smallest_size;
while (GetLength(team) > to_handicap)
RemoveArrayIndexUnstable(team, Random(GetLength(team)));
for (var plr in team)
{
SetLength(g_handicapped_players, Max(plr + 1, GetLength(g_handicapped_players)));
g_handicapped_players[plr] = true;
}
}
}
func IsHandicapped(int plr)
{
return !!g_handicapped_players[plr];
}
// Returns a list of colored player names, for example "Sven2, Maikel, Luchs"
global func GetTeamPlayerNames(int team)
{
var str = "";
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
if (GetPlayerTeam(plr) == team)
{
var comma = "";
if (str != "") comma = ", ";
str = Format("%s%s<c %x>%s</c>", str, comma, GetPlayerColor(plr), GetPlayerName(plr));
}
}
return str;
}
global func FxCheckVictoryTimer(_, proplist effect)
{
var find_living = Find_And(Find_OCF(OCF_CrewMember), Find_NoContainer());
var clonk = FindObject(find_living);
var msg;
if (!clonk)
{
// Stalemate!
msg = "$Stalemate$";
Log(msg);
GameCall("ResetRound");
}
else if (!FindObject(find_living, Find_Hostile(clonk->GetOwner())))
{
// We have a winner!
var team = GetPlayerTeam(clonk->GetOwner());
PushBack(g_winners, team);
// Announce the winning team.
msg = Format("$WinningTeam$", GetTeamPlayerNames(team));
Log(msg);
// Update the scoreboard.
UpdateScoreboardWins(team);
// The leading team has to win the last round.
if (--g_remaining_rounds > 0 || GetLeadingTeam() != team)
{
var msg2 = CurrentRoundStr();
Log(msg2);
msg = Format("%s|%s", msg, msg2);
GameCall("ResetRound");
}
else
{
GameCall("EliminateLosers");
}
}
// Switching scenario sections makes the Log() messages hard to see, so announce them using a message as well.
CustomMessage(msg);
// Go to sleep again.
effect.Interval = 0;
return FX_OK;
}
global func CurrentRoundStr()
{
if (g_remaining_rounds == 1)
return "$LastRound$";
else if (g_remaining_rounds > 1)
return Format("$RemainingRounds$", g_remaining_rounds);
else if (GetLeadingTeam() == nil)
return "$Tiebreak$";
else
return "$BonusRound$";
}
global func UpdateScoreboardWins(int team)
{
var wins = GetTeamWins(team);
Scoreboard->SetData(ScoreboardTeam(team), "wins", wins, wins);
// We have to update each player as well to make the sorting work.
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
if (GetPlayerTeam(plr) == team)
{
Scoreboard->SetPlayerData(plr, "wins", "", wins);
}
}
}
global func GetTeamWins(int team)
{
var wins = 0;
for (var w in g_winners)
if (w == team)
wins++;
return wins;
}
// Returns the team which won the most rounds, or nil if there is a tie.
global func GetLeadingTeam()
{
var teams = [], winning_team = g_winners[0];
for (var w in g_winners)
{
teams[w] += 1;
if (teams[w] > teams[winning_team])
winning_team = w;
}
// Detect a tie.
for (var i = 0; i < GetLength(teams); i++)
{
if (i != winning_team && teams[i] == teams[winning_team])
return nil;
}
return winning_team;
}
func EliminateLosers()
{
g_gameover = true;
// Determine the winning team.
var winning_team = GetLeadingTeam();
// Eliminate everybody who isn't on the winning team.
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
if (GetPlayerTeam(plr) != winning_team)
EliminatePlayer(plr);
}
// The scenario goal will end the scenario.
}
/* Called periodically in grenade launcher */
func ReplenishLauncherAmmo()

View File

@ -32,9 +32,3 @@ DescBalloonSpawn=Die Clonks fallen mit Ballons vom Himmel.
Rounds=Rundenzahl
DescRounds=Mehrere Runden spielen
Stalemate=Unentschieden!
WinningTeam=Gewinner: %s
RemainingRounds=Noch %d Runden.
LastRound=Letzte Runde!
Tiebreak=Entscheidende Runde!
BonusRound=Bonusrunde!

View File

@ -32,9 +32,3 @@ DescBalloonSpawn=The clonks will drop with balloons from the sky.
Rounds=Number of rounds
DescRounds=Play for multiple rounds.
Stalemate=Stalemate!
WinningTeam=Winning team: %s
RemainingRounds=%d rounds remaining.
LastRound=Last round!
Tiebreak=Tiebreak!
BonusRound=Bonus round!

View File

@ -1,17 +0,0 @@
#appendto Goal_Melee
public func GetDescription(int plr)
{
// Count active enemy clonks.
var hostile_count = ObjectCount(Find_OCF(OCF_CrewMember), Find_NoContainer(), Find_Hostile(plr));
var message;
if (!hostile_count)
message = "$MsgGoalFulfilled$";
else
message = Format("$MsgGoalUnfulfilled$", hostile_count);
// Also report the remaining rounds.
message = Format("%s|%s", message, CurrentRoundStr());
return message;
}

View File

@ -1,2 +0,0 @@
MsgGoalFulfilled=Eure Gegner sind eliminiert.
MsgGoalUnfulfilled=Es sind noch %d Gegner im Spiel.

View File

@ -1,2 +0,0 @@
MsgGoalFulfilled=All opponents eliminated.
MsgGoalUnfulfilled=There are still %d opponents in the game.

View File

@ -0,0 +1,7 @@
[DefCore]
id=Goal_MultiRoundMelee
Version=8,0
Category=C4D_StaticBack|C4D_Goal
Width=64
Height=64
Offset=-32,-32

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,419 @@
/*-- Multi-Round Melee --*/
/* Originally part of HotIce, but now also used in other scenarios.
Usage:
In the scenario script, implement the following functions:
InitializeRound():
InitializeRound should create scenario objects for each round.
InitPlayerRound(int plr, object crew):
InitPlayerRound is called every round for each player and should equip
and position their Clonks. Note that the players won't be able to
control their Clonks until the round start countdown finishes.
Check Goal_MultiRoundMelee->IsHandicapped(plr) to improve balance
with unequal team sizes (see documentation below).
StartRound():
Called after the round start countdown finishes and the players can
control their Clonks.
@author Luchs
*/
#include Goal_Melee
/* Public Interface */
// SetRounds changes the number of remaining rounds. The number of rounds
// defaults to the `Rounds` scenario parameter if available.
public func SetRounds(int rounds)
{
if (this == Goal_MultiRoundMelee) return FindObject(Find_ID(Goal_MultiRoundMelee))->SetRounds(rounds);
remaining_rounds = rounds;
}
// IsHandicapped indicates whether the given player should receive a handicap.
// When playing with inbalanced teams, the goal randomly selects players to be
// handicapped so that the number of non-handicapped players is tha same for
// all teams.
public func IsHandicapped(int plr)
{
if (this == Goal_MultiRoundMelee) return FindObject(Find_ID(Goal_MultiRoundMelee))->IsHandicapped(plr);
return !!handicapped_players[plr];
}
/* Implementation */
local remaining_rounds, winners, check_victory_effect, gameover;
local handicapped_players;
protected func Initialize()
{
// Don't allow creating the goal at runtime. This is important as the
// engine will recreate goals during section changes, but we need to retain
// all data.
if (FrameCounter() > 1) return RemoveObject();
remaining_rounds = SCENPAR.Rounds ?? 1;
winners = [];
InitializeRound();
Scoreboard->Init([
// Invisible team column for sorting players under their teams.
{key = "team", title = "", sorted = true, desc = false, default = "", priority = 90},
{key = "wins", title = "Wins", sorted = true, desc = true, default = 0, priority = 100},
{key = "death", title = "", sorted = false, default = "", priority = 0},
]);
return inherited(...);
}
protected func InitializePlayer(int plr, int x, int y, object base, int team)
{
// Add the player and their team to the scoreboard.
Scoreboard->NewPlayerEntry(plr);
Scoreboard->SetPlayerData(plr, "wins", "");
Scoreboard->NewEntry(ScoreboardTeam(team), GetTeamName(team));
Scoreboard->SetData(ScoreboardTeam(team), "team", "", ScoreboardTeam(team));
Scoreboard->SetPlayerData(plr, "team", "", ScoreboardTeam(team) + 1);
// Players joining at runtime will participate in the following round.
// Should only happen if it's not game start, else Clonks would start stuck in a RelaunchContainer.
if (FrameCounter() > 1) PutInRelaunchContainer(GetCrew(plr));
return inherited(plr, x, y, base, team, ...);
}
protected func InitializePlayers()
{
AssignHandicaps();
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
InitPlayerRound(plr);
}
}
// InitPlayerRound initializes the round for the given player.
private func InitPlayerRound(int plr)
{
// Unmark death on scoreboard.
Scoreboard->SetPlayerData(plr, "death", "");
// Players can scroll freely while waiting for the next round. Disable this now.
SetPlayerViewLock(plr, true);
// Disable the Clonk during the countdown.
var crew = GetCrew(plr);
crew->SetCrewEnabled(false);
crew->SetComDir(COMD_Stop);
// Let the scenario do its thing.
Scenario->~InitPlayerRound(plr, crew);
}
// ResetRound resets the scenario, redrawing the map.
private func ResetRound()
{
// Retrieve all Clonks.
var clonks = [];
for (var clonk in FindObjects(Find_OCF(OCF_CrewMember)))
{
var container = clonk->Contained();
if (container)
{
clonk->Exit();
container->RemoveObject();
}
else
{
// Players not waiting for a relaunch get a new Clonk to prevent
// status effects from carrying over to the next round.
var new_clonk = CreateObject(clonk->GetID(), 0, 0, clonk->GetOwner());
new_clonk->GrabObjectInfo(clonk);
clonk = new_clonk;
}
PushBack(clonks, clonk);
clonk->SetObjectStatus(C4OS_INACTIVE);
}
// Clear and redraw the map while retaining the goal.
SetObjectStatus(C4OS_INACTIVE);
LoadScenarioSection("main");
SetObjectStatus(C4OS_NORMAL);
InitializeRound();
AssignHandicaps();
// Re-enable the players.
for (var clonk in clonks)
{
clonk->SetObjectStatus(C4OS_NORMAL);
SetCursor(clonk->GetOwner(), clonk);
// Select the first item. This fixes item ordering.
clonk->SetHandItemPos(0, 0);
InitPlayerRound(clonk->GetOwner());
}
// Fix goal icon in the HUD.
NotifyHUD();
}
local CheckVictory = new Effect
{
goal = nil,
Construction = func(object g)
{
goal = g;
},
Timer = func()
{
var find_living = Find_And(Find_OCF(OCF_CrewMember), Find_NoContainer());
var clonk = FindObject(find_living);
var msg;
if (!clonk)
{
// Stalemate!
msg = "$Stalemate$";
Log(msg);
goal->ResetRound();
}
else if (!FindObject(find_living, Find_Hostile(clonk->GetOwner())))
{
// We have a winner!
var team = GetPlayerTeam(clonk->GetOwner());
PushBack(goal.winners, team);
// Announce the winning team.
msg = Format("$WinningTeam$", GetTeamPlayerNames(team));
Log(msg);
// Update the scoreboard.
goal->UpdateScoreboardWins(team);
// The leading team has to win the last round.
if (--goal.remaining_rounds > 0 || goal->GetLeadingTeam() != team)
{
var msg2 = goal->CurrentRoundStr();
Log(msg2);
msg = Format("%s|%s", msg, msg2);
goal->ResetRound();
}
else
{
goal->EliminateLosers();
}
}
// Switching scenario sections makes the Log() messages hard to see, so announce them using a message as well.
CustomMessage(msg);
// Go to sleep again.
this.Interval = 0;
return FX_OK;
},
// GetTeamPlayerNames returns a list of colored player names, for example
// "Sven2, Maikel, Luchs"
GetTeamPlayerNames = func(int team)
{
var str = "";
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
if (GetPlayerTeam(plr) == team)
{
var comma = "";
if (str != "") comma = ", ";
str = Format("%s%s<c %x>%s</c>", str, comma, GetPlayerColor(plr), GetPlayerName(plr));
}
}
return str;
},
};
private func CurrentRoundStr()
{
if (remaining_rounds == 1)
return "$LastRound$";
else if (remaining_rounds > 1)
return Format("$RemainingRounds$", remaining_rounds);
else if (GetLeadingTeam() == nil)
return "$Tiebreak$";
else
return "$BonusRound$";
}
private func InitializeRound()
{
// Checking for victory: Only active after a Clonk dies.
if (!check_victory_effect)
check_victory_effect = CreateEffect(CheckVictory, 1, 0, this);
// Now let the scenario do its thing.
Scenario->~InitializeRound();
// The game starts after a delay to ensure that everyone is ready.
GUI_Clock->CreateCountdown(3);
return true;
}
protected func OnCountdownFinished() // called by the round start countdown
{
// Re-enable all Clonks.
for (var clonk in FindObjects(Find_OCF(OCF_CrewMember)))
{
clonk->SetCrewEnabled(true);
SetCursor(clonk->GetOwner(), clonk);
}
Scenario->~StartRound();
}
private func PutInRelaunchContainer(object clonk)
{
var plr = clonk->GetOwner();
var relaunch = CreateObject(RelaunchContainer, LandscapeWidth() / 2, LandscapeHeight() / 2, plr);
// We just use the relaunch object as a dumb container.
clonk->Enter(relaunch);
// Allow scrolling around the landscape.
SetPlayerViewLock(plr, false);
}
protected func OnClonkDeath(object clonk)
{
var plr = clonk->GetOwner();
// Mark death on scoreboard.
Scoreboard->SetPlayerData(plr, "death", "{{Scoreboard_Death}}");
// Skip eliminated players, NO_OWNER, etc.
if (GetPlayerName(plr))
{
var crew = CreateObject(Clonk, 0, 0, plr);
crew->MakeCrewMember(plr);
PutInRelaunchContainer(crew);
}
// Check for victory after three seconds to allow stalemates.
if (!gameover)
check_victory_effect.Interval = check_victory_effect.Time + 36 * 3;
}
// GetTeamPlayers returns an array of team -> number of players in team.
private func GetTeamPlayers()
{
var result = CreateArray(GetTeamCount() + 1);
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i), team = GetPlayerTeam(plr);
SetLength(result, Max(team + 1, GetLength(result)));
result[team] = result[team] ?? [];
PushBack(result[team], plr);
}
return result;
}
func _MinSize(int a, array b) { if (b == nil) return a; else return Min(a, GetLength(b)); }
// Assigns handicaps so that the number of not-handicapped players is the same for all teams.
func AssignHandicaps()
{
handicapped_players = CreateArray(GetPlayerCount());
var teams = GetTeamPlayers();
var smallest_size = Reduce(teams, this._MinSize, ~(1<<31));
for (var team in teams) if (team != nil)
{
var to_handicap = GetLength(team) - smallest_size;
while (GetLength(team) > to_handicap)
RemoveArrayIndexUnstable(team, Random(GetLength(team)));
for (var plr in team)
{
SetLength(handicapped_players, Max(plr + 1, GetLength(handicapped_players)));
handicapped_players[plr] = true;
}
}
}
// GetLeadingTeam returns the team which won the most rounds, or nil if there is a tie.
private func GetLeadingTeam()
{
var teams = [], winning_team = winners[0];
for (var w in winners)
{
teams[w] += 1;
if (teams[w] > teams[winning_team])
winning_team = w;
}
// Detect a tie.
for (var i = 0; i < GetLength(teams); i++)
{
if (i != winning_team && teams[i] == teams[winning_team])
return nil;
}
return winning_team;
}
private func EliminateLosers()
{
gameover = true;
// Determine the winning team.
var winning_team = GetLeadingTeam();
// Eliminate everybody who isn't on the winning team.
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
if (GetPlayerTeam(plr) != winning_team)
EliminatePlayer(plr);
}
// The included melee goal will end the scenario.
}
/* Scoreboard */
private func ScoreboardTeam(int team) { return team * 100; }
// GetTeamWins returns how many rounds that team has won.
private func GetTeamWins(int team)
{
var wins = 0;
for (var w in winners)
if (w == team)
wins++;
return wins;
}
private func UpdateScoreboardWins(int team)
{
var wins = GetTeamWins(team);
Scoreboard->SetData(ScoreboardTeam(team), "wins", wins, wins);
// We have to update each player as well to make the sorting work.
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
if (GetPlayerTeam(plr) == team)
{
Scoreboard->SetPlayerData(plr, "wins", "", wins);
}
}
}
/* Goal interface */
public func GetDescription(int plr)
{
// Count active enemy clonks.
var hostile_count = ObjectCount(Find_OCF(OCF_CrewMember), Find_NoContainer(), Find_Hostile(plr));
var message;
if (!hostile_count)
message = "$MsgGoalFulfilled$";
else
message = Format("$MsgGoalUnfulfilled$", hostile_count);
// Also report the remaining rounds.
message = Format("%s|%s", message, CurrentRoundStr());
return message;
}
public func GetShortDescription(int plr)
{
return CurrentRoundStr();
}
local Name = "$Name$";

View File

@ -0,0 +1,11 @@
Name=Mehrrundiges Melee
MsgGoalFulfilled=Eure Gegner sind eliminiert.
MsgGoalUnfulfilled=Es sind noch %d Gegner in der aktuellen Runde.
Stalemate=Unentschieden!
WinningTeam=Gewinner: %s
RemainingRounds=Noch %d Runden.
LastRound=Letzte Runde!
Tiebreak=Entscheidende Runde!
BonusRound=Bonusrunde!

View File

@ -0,0 +1,11 @@
Name=Multi-Round Melee
MsgGoalFulfilled=All opponents eliminated.
MsgGoalUnfulfilled=There are still %d opponents in the current round.
Stalemate=Stalemate!
WinningTeam=Winning team: %s
RemainingRounds=%d rounds remaining.
LastRound=Last round!
Tiebreak=Tiebreak!
BonusRound=Bonus round!