// ===================
set g_lms 0 "Last Man Standing: everyone starts with a certain amount of lives, and the survivor wins"
set g_lms_lives_override -1
+set g_lms_extra_lives 0
set g_lms_regenerate 0
set g_lms_campcheck_interval 10
set g_lms_campcheck_damage 100
MSG_CENTER_NOTIF(1, CENTER_DEATH_TEAMKILL_FRAG, 1, 0, "s1", NO_CPID, "0 0", _("^K1Moron! You fragged ^BG%s^K1, a team mate!"), _("^K1Moron! You went against ^BG%s^K1, a team mate!")) \
MSG_CENTER_NOTIF(1, CENTER_DEATH_TEAMKILL_FRAGGED, 1, 0, "s1", NO_CPID, "0 0", _("^K1You were fragged by ^BG%s^K1, a team mate"), _("^K1You were scored against by ^BG%s^K1, a team mate")) \
MSG_CENTER_NOTIF(1, CENTER_DISCONNECT_IDLING, 0, 1, "", CPID_IDLING, "1 f1", _("^K1Stop idling!\n^BGDisconnecting in ^COUNT..."), "") \
+ MSG_CENTER_NOTIF(1, CENTER_EXTRALIVES, 0, 0, "", NO_CPID, "0 0", _("^F2You picked up some extra lives"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FREEZE, 1, 0, "s1", NO_CPID, "0 0", _("^K3You froze ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FROZEN, 1, 0, "s1", NO_CPID, "0 0", _("^K1You were frozen by ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE, 1, 0, "s1", NO_CPID, "0 0", _("^K3You revived ^BG%s"), "") \
float autocvar_g_keyhunt_teams_override;
float autocvar_g_lms_campcheck_damage;
float autocvar_g_lms_campcheck_distance;
+float autocvar_g_lms_extra_lives;
float autocvar_g_lms_campcheck_interval;
float autocvar_g_lms_join_anytime;
float autocvar_g_lms_last_join;
if not(g_ca) // don't reset teams when moving a ca player to the spectators
self.team = -1; // move this as it is needed to log the player spectating in eventlog
- if(self.killcount != -666) {
- if(g_lms) {
- if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0 && self.lms_spectate_warning != 2)
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_NOLIVES, self.netname);
- else
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_FORFEIT, self.netname);
- } else { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, self.netname); }
+ if(self.killcount != -666)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_QUIT_SPECTATE, self.netname);
if(self.just_joined == FALSE) {
LogTeamchange(self.playerid, -1, 4);
JoinBestTeam(self, FALSE, FALSE); // if the team number is valid, keep it
- if((autocvar_sv_spectate == 1 && !g_lms) || autocvar_g_campaign || self.team_forced < 0) {
+ if((autocvar_sv_spectate == 1) || autocvar_g_campaign || self.team_forced < 0) {
self.classname = "observer";
} else {
if(teamplay)
stuffcmd(self, "cl_cmd settemp chase_active 1\n");
}
- if(g_lms)
- {
- if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
- {
- PlayerScore_Add(self, SP_LMS_RANK, 666);
- self.frags = FRAGS_SPECTATOR;
- }
- }
-
if(!sv_foginterval && world.fog != "")
stuffcmd(self, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
if(frametime)
player_anim();
button_pressed = (self.BUTTON_ATCK || self.BUTTON_JUMP || self.BUTTON_ATCK2 || self.BUTTON_HOOK || self.BUTTON_USE);
-
+
if (self.deadflag == DEAD_DYING)
{
if(self.respawn_flags & RESPAWN_FORCE)
return;
}
- if(g_lms && !self.deadflag && autocvar_g_lms_campcheck_interval)
- {
- vector dist;
-
- // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
- dist = self.prevorigin - self.origin;
- dist_z = 0;
- self.lms_traveled_distance += fabs(vlen(dist));
-
- if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime))
- {
- self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval*2;
- self.lms_traveled_distance = 0;
- }
-
- if(time > self.lms_nextcheck)
- {
- //sprint(self, "distance: ", ftos(self.lms_traveled_distance), "\n");
- if(self.lms_traveled_distance < autocvar_g_lms_campcheck_distance)
- {
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_LMS_CAMPCHECK);
- // FIXME KadaverJack: gibbing player here causes playermodel to bounce around, instead of eye.md3
- // I wasn't able to find out WHY that happens, so I put a workaround in place that shall prevent players from being gibbed :(
- if(self.vehicle)
- Damage(self.vehicle, self, self, autocvar_g_lms_campcheck_damage * 2, DEATH_CAMP, self.vehicle.origin, '0 0 0');
- else
- Damage(self, self, self, bound(0, autocvar_g_lms_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP, self.origin, '0 0 0');
- }
- self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval;
- self.lms_traveled_distance = 0;
- }
- }
-
self.prevorigin = self.origin;
float do_crouch = self.BUTTON_CROUCH;
return 0;
if (g_weaponarena)
return 0;
- if (g_lms)
- return 0;
if (g_cts)
return 0;
if (g_nexball && w == WEP_GRENADE_LAUNCHER)
float player_count;
float currentbots;
float bots_would_leave;
-float lms_lowest_lives;
-float lms_next_place;
-float LMS_NewPlayerLives();
void UpdateFrags(entity player, float f);
.float totalfrags;
else
{
self = oldself;
- if(g_lms)
- {
- // remove a life
- float tl;
- tl = PlayerScore_Add(targ, SP_LMS_LIVES, -1);
- if(tl < lms_lowest_lives)
- lms_lowest_lives = tl;
- if(tl <= 0)
- {
- if(!lms_next_place)
- lms_next_place = player_count;
- else
- lms_next_place = min(lms_next_place, player_count);
- PlayerScore_Add(targ, SP_LMS_RANK, lms_next_place); // won't ever spawn again
- --lms_next_place;
- }
- f = 0;
- }
}
attacker.totalfrags += f;
}
}
- if(targ.classname == "player")
- if(attacker.classname == "player")
- if(attacker != targ)
- {
- targ.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
- attacker.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
- }
-
if(targ.classname == "player")
if (g_minstagib)
{
return WINNING_NO;
}
-float LMS_NewPlayerLives()
-{
- float fl;
- fl = autocvar_fraglimit;
- if(fl == 0)
- fl = 999;
-
- // first player has left the game for dying too much? Nobody else can get in.
- if(lms_lowest_lives < 1)
- return 0;
-
- if(!autocvar_g_lms_join_anytime)
- if(lms_lowest_lives < fl - autocvar_g_lms_last_join)
- return 0;
-
- return bound(1, lms_lowest_lives, fl);
-}
-
// Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives)
// they win. Otherwise the defending team wins once the timelimit passes.
void assault_new_round();
s = cvar_string("g_weaponarena");
if (s == "0" || s == "")
{
- if(g_lms || g_ca)
+ if(g_ca)
s = "most";
}
g_pinata = 0; // incompatible
g_weapon_stay = 0; // incompatible
WEPSET_COPY_AA(start_weapons, g_weaponarena_weapons);
- if(!(g_lms || g_ca))
+ if(!g_ca)
start_items |= IT_UNLIMITED_AMMO;
}
else if (g_minstagib)
}
else
{
- if(g_lms || g_ca)
+ if(g_ca)
{
start_ammo_shells = cvar("g_lms_start_ammo_shells");
start_ammo_nails = cvar("g_lms_start_ammo_nails");
}
}
- if (g_lms || g_ca)
+ if (g_ca)
{
start_health = cvar("g_lms_start_health");
start_armorvalue = cvar("g_lms_start_armor");
--- /dev/null
+// main functions
+float LMS_NewPlayerLives()
+{
+ float fl;
+ fl = autocvar_fraglimit;
+ if(fl == 0)
+ fl = 999;
+
+ // first player has left the game for dying too much? Nobody else can get in.
+ if(lms_lowest_lives < 1)
+ return 0;
+
+ if(!autocvar_g_lms_join_anytime)
+ if(lms_lowest_lives < fl - autocvar_g_lms_last_join)
+ return 0;
+
+ return bound(1, lms_lowest_lives, fl);
+}
+
+// mutator hooks
+MUTATOR_HOOKFUNCTION(lms_PlayerSpawn)
+{
+ if(IS_PLAYER(self))
+ if(restart_mapalreadyrestarted || (time < game_starttime))
+ PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives());
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_RemovePlayer)
+{
+ // Only if the player cannot play at all
+ if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666)
+ self.frags = FRAGS_SPECTATOR;
+ else
+ self.frags = FRAGS_LMS_LOSER;
+
+ if(self.killcount != -666)
+ if(PlayerScore_Add(self, SP_LMS_RANK, 0) > 0 && self.lms_spectate_warning != 2)
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_NOLIVES, self.netname);
+ else
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_LMS_FORFEIT, self.netname);
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_ClientConnect)
+{
+ self.classname = "player";
+ campaign_bots_may_start = 1;
+
+ if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
+ {
+ PlayerScore_Add(self, SP_LMS_RANK, 666);
+ self.frags = FRAGS_SPECTATOR;
+ }
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_PlayerThink)
+{
+ if(self.deadflag == DEAD_DYING)
+ self.deadflag = DEAD_RESPAWNING;
+
+ if not(self.deadflag)
+ if(autocvar_g_lms_campcheck_interval)
+ {
+ vector dist;
+
+ // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement)
+ dist = self.prevorigin - self.origin;
+ dist_z = 0;
+ self.lms_traveled_distance += fabs(vlen(dist));
+
+ if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime))
+ {
+ self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval*2;
+ self.lms_traveled_distance = 0;
+ }
+
+ if(time > self.lms_nextcheck)
+ {
+ if(self.lms_traveled_distance < autocvar_g_lms_campcheck_distance)
+ {
+ Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_LMS_CAMPCHECK);
+ if(self.vehicle)
+ Damage(self.vehicle, self, self, autocvar_g_lms_campcheck_damage * 2, DEATH_CAMP, self.vehicle.origin, '0 0 0');
+ else
+ Damage(self, self, self, bound(0, autocvar_g_lms_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP, self.origin, '0 0 0');
+ }
+ self.lms_nextcheck = time + autocvar_g_lms_campcheck_interval;
+ self.lms_traveled_distance = 0;
+ }
+ }
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_PlayerDamage)
+{
+ if(IS_PLAYER(frag_target))
+ if(IS_PLAYER(frag_attacker))
+ if(frag_attacker != frag_target)
+ {
+ frag_target.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
+ frag_attacker.lms_traveled_distance = autocvar_g_lms_campcheck_distance;
+ }
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_ForbidThrowing)
+{
+ // forbode!
+ return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_GiveFragsForKill)
+{
+ // remove a life
+ float tl;
+ tl = PlayerScore_Add(frag_target, SP_LMS_LIVES, -1);
+ if(tl < lms_lowest_lives)
+ lms_lowest_lives = tl;
+ if(tl <= 0)
+ {
+ if(!lms_next_place)
+ lms_next_place = player_count;
+ else
+ lms_next_place = min(lms_next_place, player_count);
+ PlayerScore_Add(frag_target, SP_LMS_RANK, lms_next_place); // won't ever spawn again
+ --lms_next_place;
+ }
+ frag_score = 0;
+
+ return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_SetStartItems)
+{
+ start_items &~= IT_UNLIMITED_AMMO;
+ start_ammo_shells = cvar("g_lms_start_ammo_shells");
+ start_ammo_nails = cvar("g_lms_start_ammo_nails");
+ start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
+ start_ammo_cells = cvar("g_lms_start_ammo_cells");
+ start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
+ start_health = cvar("g_lms_start_health");
+ start_armorvalue = cvar("g_lms_start_armor");
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_KeepScore)
+{
+ // don't clear player score
+ return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_FilterItem)
+{
+ if(autocvar_g_lms_extra_lives)
+ if(self.classname == "item_health_mega")
+ {
+ self.max_health = 1;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_ItemTouch)
+{
+ // give extra lives for mega health
+ if(self.items & IT_HEALTH)
+ {
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_EXTRALIVES);
+ PlayerScore_Add(other, SP_LMS_LIVES, autocvar_g_lms_extra_lives);
+ }
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(lms_BotSpawn)
+{
+ // temporary hack to give bots lives
+ if(PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0)
+ {
+ PlayerScore_Add(self, SP_LMS_RANK, 666);
+ self.frags = FRAGS_SPECTATOR;
+ }
+
+ return FALSE;
+}
+
+// scoreboard stuff
+void lms_ScoreRules()
+{
+ ScoreRules_basics(0, 0, 0, FALSE);
+ ScoreInfo_SetLabel_PlayerScore(SP_LMS_LIVES, "lives", SFL_SORT_PRIO_SECONDARY);
+ ScoreInfo_SetLabel_PlayerScore(SP_LMS_RANK, "rank", SFL_LOWER_IS_BETTER | SFL_RANK | SFL_SORT_PRIO_PRIMARY | SFL_ALLOW_HIDE);
+ ScoreRules_basics_end();
+}
+
+void lms_Initialize()
+{
+ lms_lowest_lives = 9999;
+ lms_next_place = 0;
+
+ lms_ScoreRules();
+}
+
+MUTATOR_DEFINITION(gamemode_lms)
+{
+ MUTATOR_HOOK(PlayerSpawn, lms_PlayerSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MakePlayerObserver, lms_RemovePlayer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ClientConnect, lms_ClientConnect, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPreThink, lms_PlayerThink, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDamage_Calculate, lms_PlayerDamage, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ForbidThrowCurrentWeapon, lms_ForbidThrowing, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GiveFragsForKill, lms_GiveFragsForKill, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SetStartItems, lms_SetStartItems, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ForbidPlayerScore_Clear, lms_KeepScore, CBC_ORDER_ANY);
+ MUTATOR_HOOK(FilterItem, lms_FilterItem, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ItemTouch, lms_ItemTouch, CBC_ORDER_ANY);
+ MUTATOR_HOOK(HavocBot_ChooseRule, lms_BotSpawn, CBC_ORDER_ANY);
+
+ 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();
+ }
+
+ MUTATOR_ONROLLBACK_OR_REMOVE
+ {
+ // we actually cannot roll back lms_Initialize here
+ // BUT: we don't need to! If this gets called, adding always
+ // succeeds.
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ print("This is a game type and it cannot be removed at runtime.");
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+// scoreboard stuff
+#define SP_LMS_LIVES 4
+#define SP_LMS_RANK 5
+
+// lives related defs
+float lms_lowest_lives;
+float lms_next_place;
+float LMS_NewPlayerLives();
\ No newline at end of file
MUTATOR_DECLARATION(gamemode_nexball);
MUTATOR_DECLARATION(gamemode_onslaught);
MUTATOR_DECLARATION(gamemode_domination);
+MUTATOR_DECLARATION(gamemode_lms);
MUTATOR_DECLARATION(mutator_dodging);
MUTATOR_DECLARATION(mutator_invincibleprojectiles);
mutators/gamemode_keyhunt.qh // TODO fix this
mutators/gamemode_keepaway.qh
mutators/gamemode_nexball.qh
+mutators/gamemode_lms.qh
mutators/mutator_dodging.qh
//// tZork Turrets ////
mutators/gamemode_keepaway.qc
mutators/gamemode_nexball.qc
mutators/gamemode_onslaught.qc
+mutators/gamemode_lms.qc
mutators/mutator_invincibleproj.qc
mutators/mutator_new_toys.qc
mutators/mutator_nix.qc
return 0;
if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear)) return 0;
- if(g_lms) return 0;
+
if(g_cts) return 0; // in CTS, you don't lose score by observing
if(g_race && g_race_qualifying) return 0; // in qualifying, you don't lose score by observing
ScoreRules_basics_end();
}
-// LMS stuff
-#define SP_LMS_LIVES 4
-#define SP_LMS_RANK 5
-void ScoreRules_lms()
-{
- ScoreRules_basics(0, 0, 0, FALSE);
- ScoreInfo_SetLabel_PlayerScore(SP_LMS_LIVES, "lives", SFL_SORT_PRIO_SECONDARY);
- ScoreInfo_SetLabel_PlayerScore(SP_LMS_RANK, "rank", SFL_LOWER_IS_BETTER | SFL_RANK | SFL_SORT_PRIO_PRIMARY | SFL_ALLOW_HIDE);
- ScoreRules_basics_end();
-}
-
// Key hunt stuff
#define ST_KH_CAPS 1
#define SP_KH_CAPS 4
return TRUE;
if(autocvar_g_powerups == 0)
return FALSE;
- if(g_lms)
- return FALSE;
if(g_ca)
return FALSE;
if(g_arena)
return TRUE;
if(autocvar_g_pickup_items == 0)
return FALSE;
- if(g_lms)
- return FALSE;
if(g_ca)
return FALSE;
if(g_weaponarena)
// sound not available
// AnnounceTo(player, "_lives");
player.armorvalue = bound(player.armorvalue, 999, player.armorvalue + autocvar_g_minstagib_extralives);
- sprint(player, "^3You picked up some extra lives\n");
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_EXTRALIVES);
}
// invis powerup
leadlimit_override = 0; // not supported by LMS
if(fraglimit_override == 0)
fraglimit_override = -1;
- lms_lowest_lives = 9999;
- lms_next_place = 0;
- ScoreRules_lms();
+ MUTATOR_ADD(gamemode_lms);
}
if(g_arena)