+#include "gamemode_lms.qh"
#ifndef GAMEMODE_LMS_H
#define GAMEMODE_LMS_H
REGISTER_MUTATOR(lms, false)
{
- SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, -1, -1);
-
MUTATOR_ONADD
{
if (time > 1) // game loads at time 1
error("This is a game type and it cannot be added at runtime.");
lms_Initialize();
+
+ SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, autocvar_timelimit_override, -1);
}
MUTATOR_ONROLLBACK_OR_REMOVE
#ifdef IMPLEMENTATION
-#include "../../campaign.qh"
-#include "../../command/cmd.qh"
+#include <common/mutators/mutator/instagib/items.qc>
+#include <server/campaign.qh>
+#include <server/command/cmd.qh>
int autocvar_g_lms_extra_lives;
bool autocvar_g_lms_join_anytime;
return bound(1, lms_lowest_lives, fl);
}
+void ClearWinners();
+
+// LMS winning condition: game terminates if and only if there's at most one
+// one player who's living lives. Top two scores being equal cancels the time
+// limit.
+int WinningCondition_LMS()
+{
+ entity head, head2;
+ bool have_player = false;
+ bool have_players = false;
+
+ int l = LMS_NewPlayerLives();
+
+ head = find(world, classname, STR_PLAYER);
+ if(head)
+ have_player = true;
+ head2 = find(head, classname, STR_PLAYER);
+ if(head2)
+ have_players = true;
+
+ if(have_player)
+ {
+ // we have at least one player
+ if(have_players)
+ {
+ // two or more active players - continue with the game
+ }
+ else
+ {
+ // exactly one player?
+
+ ClearWinners();
+ SetWinners(winning, 0); // NOTE: exactly one player is still "player", so this works out
+
+ if(l)
+ {
+ // game still running (that is, nobody got removed from the game by a frag yet)? then continue
+ return WINNING_NO;
+ }
+ else
+ {
+ // a winner!
+ // and assign him his first place
+ PlayerScore_Add(head, SP_LMS_RANK, 1);
+ return WINNING_YES;
+ }
+ }
+ }
+ else
+ {
+ // nobody is playing at all...
+ if(l)
+ {
+ // wait for players...
+ }
+ else
+ {
+ // SNAFU (maybe a draw game?)
+ ClearWinners();
+ LOG_TRACE("No players, ending game.\n");
+ return WINNING_YES;
+ }
+ }
+
+ // When we get here, we have at least two players who are actually LIVING,
+ // now check if the top two players have equal score.
+ WinningConditionHelper();
+
+ ClearWinners();
+ if(WinningConditionHelper_winner)
+ WinningConditionHelper_winner.winning = true;
+ if(WinningConditionHelper_topscore == WinningConditionHelper_secondscore)
+ return WINNING_NEVER;
+
+ // Top two have different scores? Way to go for our beloved TIMELIMIT!
+ return WINNING_NO;
+}
+
// mutator hooks
MUTATOR_HOOKFUNCTION(lms, reset_map_global)
{
}
MUTATOR_HOOKFUNCTION(lms, reset_map_players)
-{SELFPARAM();
- entity e;
+{
if(restart_mapalreadyrestarted || (time < game_starttime))
- FOR_EACH_CLIENT(e)
- if(IS_PLAYER(e))
- {
- WITH(entity, self, e, PlayerScore_Add(e, SP_LMS_LIVES, LMS_NewPlayerLives()));
- }
-
+ FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(PlayerScore_Add(it, SP_LMS_LIVES, LMS_NewPlayerLives())));
return false;
}
MUTATOR_HOOKFUNCTION(lms, PutClientInServer)
-{SELFPARAM();
+{
+ entity player = M_ARGV(0, entity);
+
// player is dead and becomes observer
// FIXME fix LMS scoring for new system
- if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0)
+ if(PlayerScore_Add(player, SP_LMS_RANK, 0) > 0)
{
- self.classname = "observer";
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_LMS_NOLIVES);
+ TRANSMUTE(Observer, player);
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_LMS_NOLIVES);
}
-
- return false;
}
MUTATOR_HOOKFUNCTION(lms, PlayerDies)
-{SELFPARAM();
- self.respawn_flags |= RESPAWN_FORCE;
-
+{
+ entity frag_target = M_ARGV(2, entity);
+
+ frag_target.respawn_flags |= RESPAWN_FORCE;
return false;
}
else
player.frags = FRAGS_LMS_LOSER;
- if(player.killcount != -666)
+ if(player.killcount != FRAGS_SPECTATOR)
if(PlayerScore_Add(player, SP_LMS_RANK, 0) > 0 && player.lms_spectate_warning != 2)
Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_NOLIVES, player.netname);
else
}
MUTATOR_HOOKFUNCTION(lms, ClientDisconnect)
-{SELFPARAM();
- lms_RemovePlayer(self);
- return false;
+{
+ entity player = M_ARGV(0, entity);
+
+ lms_RemovePlayer(player);
}
MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver)
-{SELFPARAM();
- lms_RemovePlayer(self);
- return false;
+{
+ entity player = M_ARGV(0, entity);
+
+ lms_RemovePlayer(player);
+ return true; // prevent team reset
}
MUTATOR_HOOKFUNCTION(lms, ClientConnect)
-{SELFPARAM();
- self.classname = "player";
- campaign_bots_may_start = 1;
+{
+ entity player = M_ARGV(0, entity);
- if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
+ TRANSMUTE(Player, player);
+ campaign_bots_may_start = true;
+
+ if(PlayerScore_Add(player, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
{
- PlayerScore_Add(self, SP_LMS_RANK, 666);
- self.frags = FRAGS_SPECTATOR;
+ PlayerScore_Add(player, SP_LMS_RANK, 666);
+ player.frags = FRAGS_SPECTATOR;
}
-
- return false;
}
MUTATOR_HOOKFUNCTION(lms, PlayerPreThink)
{SELFPARAM();
- if(self.deadflag == DEAD_DYING)
- self.deadflag = DEAD_RESPAWNING;
+ if(this.deadflag == DEAD_DYING)
+ this.deadflag = DEAD_RESPAWNING;
return false;
}
MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill)
{
+ entity frag_target = M_ARGV(1, entity);
+
// remove a life
float tl;
tl = PlayerScore_Add(frag_target, SP_LMS_LIVES, -1);
PlayerScore_Add(frag_target, SP_LMS_RANK, lms_next_place); // won't ever spawn again
--lms_next_place;
}
- frag_score = 0;
+ M_ARGV(2, float) = 0;
return true;
}
}
MUTATOR_HOOKFUNCTION(lms, FilterItem)
-{SELFPARAM();
+{
+ entity item = M_ARGV(0, entity);
+
if(autocvar_g_lms_extra_lives)
- if(self.itemdef == ITEM_HealthMega)
- {
- self.max_health = 1;
+ if(item.itemdef == ITEM_ExtraLife)
return false;
- }
+
+ return true;
+}
+
+void lms_extralife(entity this)
+{
+ StartItem(this, ITEM_ExtraLife);
+}
+
+MUTATOR_HOOKFUNCTION(lms, OnEntityPreSpawn)
+{
+ if (!autocvar_g_powerups) return false;
+ if (!autocvar_g_lms_extra_lives) return false;
+
+ entity ent = M_ARGV(0, entity);
+
+ // Can't use .itemdef here
+ if (ent.classname != "item_health_mega") return false;
+
+ entity e = spawn();
+ setthink(e, lms_extralife);
+
+ e.nextthink = time + 0.1;
+ e.spawnflags = ent.spawnflags;
+ e.noalign = ent.noalign;
+ setorigin(e, ent.origin);
return true;
}
MUTATOR_HOOKFUNCTION(lms, ItemTouch)
-{SELFPARAM();
- // give extra lives for mega health
- if (self.items & ITEM_HealthMega.m_itemid)
+{
+ entity item = M_ARGV(0, entity);
+ entity toucher = M_ARGV(1, entity);
+
+ if(item.itemdef == ITEM_ExtraLife)
{
- Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_EXTRALIVES);
- PlayerScore_Add(other, SP_LMS_LIVES, autocvar_g_lms_extra_lives);
+ Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES);
+ PlayerScore_Add(toucher, SP_LMS_LIVES, autocvar_g_lms_extra_lives);
+ return MUT_ITEMTOUCH_PICKUP;
}
return MUT_ITEMTOUCH_CONTINUE;
MUTATOR_HOOKFUNCTION(lms, Bot_FixCount, CBC_ORDER_EXCLUSIVE)
{
- entity head;
- FOR_EACH_REALCLIENT(head)
- {
+ FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
++bot_activerealplayers;
++bot_realplayers;
- }
+ ));
return true;
}
MUTATOR_HOOKFUNCTION(lms, ClientCommand_Spectate)
{
+ SELFPARAM();
if(self.lms_spectate_warning)
{
// for the forfeit message...
MUTATOR_HOOKFUNCTION(lms, CheckRules_World)
{
- ret_float = WinningCondition_LMS();
+ M_ARGV(0, float) = WinningCondition_LMS();
return true;
}
MUTATOR_HOOKFUNCTION(lms, WantWeapon)
{
- want_allguns = true;
- return false;
+ M_ARGV(2, bool) = true; // all weapons
}
MUTATOR_HOOKFUNCTION(lms, GetPlayerStatus)
MUTATOR_HOOKFUNCTION(lms, AddPlayerScore)
{
if(gameover)
- if(score_field == SP_LMS_RANK)
+ if(M_ARGV(0, int) == SP_LMS_RANK) // score field
return true; // allow writing to this field in intermission as it is needed for newly joining players
return false;
}