return normal_respawntime;
}
- CheckAllowedTeams(NULL);
- GetTeamCounts(NULL);
+ entity balance = TeamBalance_CheckAllowedTeams(NULL);
+ TeamBalance_GetTeamCounts(balance, NULL);
int players = 0;
- if (c1 != -1) players += c1;
- if (c2 != -1) players += c2;
- if (c3 != -1) players += c3;
- if (c4 != -1) players += c4;
-
+ for (int i = 1; i <= NUM_TEAMS; ++i)
+ {
+ if (TeamBalance_IsTeamAllowed(balance, i))
+ {
+ players += TeamBalance_GetNumberOfPlayers(balance, i);
+ }
+ }
+ TeamBalance_Destroy(balance);
+
if (players >= 2) {
return normal_respawntime * (r / (players + o) + l);
} else {
AUTOCVAR(g_pickup_respawntime_initial_random, int, 1,
"For items that don't start spawned: 0: spawn after their normal respawntime; 1: spawn after `random * respawntime` with the *same* random; 2: same as 1 but each item has separate random");
- float shared_random;
- STATIC_INIT(shared_random) { shared_random = random(); }
void Item_ScheduleInitialRespawn(entity e)
{
Item_Show(e, 0);
// range: respawntime .. respawntime + respawntimejitter
spawn_in = e.respawntime + random() * e.respawntimejitter;
}
- else if (autocvar_g_pickup_respawntime_initial_random == 1)
+ else
{
+ float rnd;
+ if (autocvar_g_pickup_respawntime_initial_random == 1)
+ {
+ static float shared_random = 0;
+ // NOTE this code works only if items are scheduled at the same time (normal case)
+ // NOTE2 random() can't return exactly 1 so this check always work as intended
+ if (!shared_random || floor(time) > shared_random)
+ shared_random = floor(time) + random();
+ rnd = shared_random - floor(time);
+ }
+ else
+ rnd = random();
+
// range:
// if respawntime >= ITEM_RESPAWN_TICKS: ITEM_RESPAWN_TICKS .. respawntime + respawntimejitter
// else: 0 .. ITEM_RESPAWN_TICKS
// this is to prevent powerups spawning unexpectedly without waypoints
- spawn_in = ITEM_RESPAWN_TICKS + shared_random * (e.respawntime + e.respawntimejitter - ITEM_RESPAWN_TICKS);
- }
- else
- {
- // range: same as 1
- spawn_in = ITEM_RESPAWN_TICKS + random() * (e.respawntime + e.respawntimejitter - ITEM_RESPAWN_TICKS);
+ spawn_in = ITEM_RESPAWN_TICKS + rnd * (e.respawntime + e.respawntimejitter - ITEM_RESPAWN_TICKS);
}
Item_ScheduleRespawnIn(e, max(0, game_starttime - time) + ((e.respawntimestart) ? e.respawntimestart : spawn_in));
void UpdateFrags(entity player, int f);
.float totalfrags;
-float team1_score, team2_score, team3_score, team4_score;
-
// flag set on worldspawn so that the code knows if it is dedicated or not
float server_is_dedicated;
string gamemode_name;
- float startitem_failed;
-
string W_Apply_Weaponreplace(string in);
void FixIntermissionClient(entity e);
// WEAPONTODO
#define DMG_NOWEP (weaponentities[0])
-float lockteams;
-
float sv_maxidle;
float sv_maxidle_spectatorsareidle;
int sv_maxidle_slots;
.WepSet dual_weapons;
IntrusiveList g_monsters;
- STATIC_INIT(g_monsters) { g_monsters = IL_NEW(); }
-
IntrusiveList g_waypoints;
- STATIC_INIT(g_waypoints) { g_waypoints = IL_NEW(); }
-
IntrusiveList g_vehicles;
- STATIC_INIT(g_vehicles) { g_vehicles = IL_NEW(); }
-
IntrusiveList g_turrets;
- STATIC_INIT(g_turrets) { g_turrets = IL_NEW(); }
-
IntrusiveList g_mines;
- STATIC_INIT(g_mines) { g_mines = IL_NEW(); }
-
IntrusiveList g_projectiles;
- STATIC_INIT(g_projectiles) { g_projectiles = IL_NEW(); }
-
IntrusiveList g_items;
- STATIC_INIT(g_items) { g_items = IL_NEW(); }
-
IntrusiveList g_initforplayer;
- STATIC_INIT(g_initforplayer) { g_initforplayer = IL_NEW(); }
-
IntrusiveList g_clones;
- STATIC_INIT(g_clones) { g_clones = IL_NEW(); }
-
- IntrusiveList g_assault_destructibles;
- STATIC_INIT(g_assault_destructibles) { g_assault_destructibles = IL_NEW(); }
-
- IntrusiveList g_assault_objectivedecreasers;
- STATIC_INIT(g_assault_objectivedecreasers) { g_assault_objectivedecreasers = IL_NEW(); }
-
- IntrusiveList g_assault_objectives;
- STATIC_INIT(g_assault_objectives) { g_assault_objectives = IL_NEW(); }
-
IntrusiveList g_spawnpoints;
- STATIC_INIT(g_spawnpoints) { g_spawnpoints = IL_NEW(); }
-
IntrusiveList g_bot_targets;
- STATIC_INIT(g_bot_targets) { g_bot_targets = IL_NEW(); }
-
IntrusiveList g_bot_dodge;
- STATIC_INIT(g_bot_dodge) { g_bot_dodge = IL_NEW(); }
-
IntrusiveList g_damagedbycontents;
- STATIC_INIT(g_damagedbycontents) { g_damagedbycontents = IL_NEW(); }
-
IntrusiveList g_railgunhit;
- STATIC_INIT(g_railgunhit) { g_railgunhit = IL_NEW(); }
-
IntrusiveList g_ladders;
- STATIC_INIT(g_ladders) { g_ladders = IL_NEW(); }
-
IntrusiveList g_locations;
- STATIC_INIT(g_locations) { g_locations = IL_NEW(); }
-
IntrusiveList g_saved_team;
- STATIC_INIT(g_saved_team) { g_saved_team = IL_NEW(); }
-
IntrusiveList g_monster_targets;
- STATIC_INIT(g_monster_targets) { g_monster_targets = IL_NEW(); }
-
IntrusiveList g_pathlib_nodes;
- STATIC_INIT(g_pathlib_nodes) { g_pathlib_nodes = IL_NEW(); }
+ STATIC_INIT(defs)
+ {
+ g_monsters = IL_NEW();
+ g_waypoints = IL_NEW();
+ g_vehicles = IL_NEW();
+ g_turrets = IL_NEW();
+ g_mines = IL_NEW();
+ g_projectiles = IL_NEW();
+ g_items = IL_NEW();
+ g_initforplayer = IL_NEW();
+ g_clones = IL_NEW();
+ g_spawnpoints = IL_NEW();
+ g_bot_targets = IL_NEW();
+ g_bot_dodge = IL_NEW();
+ g_damagedbycontents = IL_NEW();
+ g_railgunhit = IL_NEW();
+ g_ladders = IL_NEW();
+ g_locations = IL_NEW();
+ g_saved_team = IL_NEW();
+ g_monster_targets = IL_NEW();
+ g_pathlib_nodes = IL_NEW();
+ }
#include <common/mutators/base.qh>
// register all possible hooks here
+
+ // to use a hook, first register your mutator using REGISTER_MUTATOR
+ // then create your function using MUTATOR_HOOKFUNCTION
/** called when a player becomes observer, after shared setup */
#define EV_MakePlayerObserver(i, o) \
/** called when the match ends */
MUTATOR_HOOKABLE(MatchEnd, EV_NO_ARGS);
-/** allows adjusting allowed teams */
-#define EV_CheckAllowedTeams(i, o) \
+/** Allows adjusting allowed teams. Return true to use the bitmask value and set
+ * non-empty string to use team entity name. Both behaviors can be active at the
+ * same time and will stack allowed teams.
+ */
+#define EV_TeamBalance_CheckAllowedTeams(i, o) \
/** mask of teams */ i(float, MUTATOR_ARGV_0_float) \
/**/ o(float, MUTATOR_ARGV_0_float) \
/** team entity name */ i(string, MUTATOR_ARGV_1_string) \
/**/ o(string, MUTATOR_ARGV_1_string) \
/** player checked */ i(entity, MUTATOR_ARGV_2_entity) \
/**/
-MUTATOR_HOOKABLE(CheckAllowedTeams, EV_CheckAllowedTeams);
+MUTATOR_HOOKABLE(TeamBalance_CheckAllowedTeams,
+ EV_TeamBalance_CheckAllowedTeams);
/** return true to manually override team counts */
-MUTATOR_HOOKABLE(GetTeamCounts, EV_NO_ARGS);
+MUTATOR_HOOKABLE(TeamBalance_GetTeamCounts, EV_NO_ARGS);
/** allow overriding of team counts */
-#define EV_GetTeamCount(i, o) \
- /** team to count */ i(float, MUTATOR_ARGV_0_float) \
+#define EV_TeamBalance_GetTeamCount(i, o) \
+ /** team index to count */ i(float, MUTATOR_ARGV_0_float) \
/** player to ignore */ i(entity, MUTATOR_ARGV_1_entity) \
/** number of players in a team */ i(float, MUTATOR_ARGV_2_float) \
/**/ o(float, MUTATOR_ARGV_2_float) \
/** lowest scoring bot in a team */ i(entity, MUTATOR_ARGV_5_entity) \
/**/ o(entity, MUTATOR_ARGV_5_entity) \
/**/
-MUTATOR_HOOKABLE(GetTeamCount, EV_GetTeamCount);
+MUTATOR_HOOKABLE(TeamBalance_GetTeamCount, EV_TeamBalance_GetTeamCount);
-/** allows overriding best teams */
-#define EV_FindBestTeams(i, o) \
+/** allows overriding the teams that will make the game most balanced if the
+ * player joins any of them.
+ */
+#define EV_TeamBalance_FindBestTeams(i, o) \
/** player checked */ i(entity, MUTATOR_ARGV_0_entity) \
/** bitmask of teams */ o(float, MUTATOR_ARGV_1_float) \
/**/
-MUTATOR_HOOKABLE(FindBestTeams, EV_FindBestTeams);
+MUTATOR_HOOKABLE(TeamBalance_FindBestTeams, EV_TeamBalance_FindBestTeams);
/** copies variables for spectating "spectatee" to "this" */
#define EV_SpectateCopy(i, o) \
/**/
MUTATOR_HOOKABLE(CustomizeWaypoint, EV_CustomizeWaypoint);
+ /** Check if items having the given definition are allowed to spawn.
+ * Return true to disallow spawning.
+ */
+ #define EV_FilterItemDefinition(i, o) \
+ /** item */ i(entity, MUTATOR_ARGV_0_entity) \
+ /**/
+ MUTATOR_HOOKABLE(FilterItemDefinition, EV_FilterItemDefinition);
+
/**
* checks if the current item may be spawned (.items and .weapons may be read and written to, as well as the ammo_ fields)
* return error to request removal
* Called before player changes their team. Return true to block team change.
*/
#define EV_Player_ChangeTeam(i, o) \
- /** player */ i(entity, MUTATOR_ARGV_0_entity) \
- /** current team */ i(float, MUTATOR_ARGV_1_float) \
- /** new team */ i(float, MUTATOR_ARGV_2_float) \
+ /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** current team index */ i(float, MUTATOR_ARGV_1_float) \
+ /** new team index */ i(float, MUTATOR_ARGV_2_float) \
/**/
MUTATOR_HOOKABLE(Player_ChangeTeam, EV_Player_ChangeTeam);
* Called after player has changed their team.
*/
#define EV_Player_ChangedTeam(i, o) \
- /** player */ i(entity, MUTATOR_ARGV_0_entity) \
- /** old team */ i(float, MUTATOR_ARGV_1_float) \
- /** current team */ i(float, MUTATOR_ARGV_2_float) \
+ /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** old team index */ i(float, MUTATOR_ARGV_1_float) \
+ /** current team index */ i(float, MUTATOR_ARGV_2_float) \
/**/
MUTATOR_HOOKABLE(Player_ChangedTeam, EV_Player_ChangedTeam);
.entity sprite;
#define AS_ROUND_DELAY 5
+ IntrusiveList g_assault_destructibles;
+ IntrusiveList g_assault_objectivedecreasers;
+ IntrusiveList g_assault_objectives;
+ STATIC_INIT(g_assault)
+ {
+ g_assault_destructibles = IL_NEW();
+ g_assault_objectivedecreasers = IL_NEW();
+ g_assault_objectives = IL_NEW();
+ }
+
// random functions
void assault_objective_use(entity this, entity actor, entity trigger)
{
return (frag_victim.classname == "func_assault_destructible");
}
-MUTATOR_HOOKFUNCTION(as, CheckAllowedTeams)
+MUTATOR_HOOKFUNCTION(as, TeamBalance_CheckAllowedTeams)
{
// assault always has 2 teams
- c1 = c2 = 0;
+ M_ARGV(0, float) = BIT(0) | BIT(1);
return true;
}
#include <server/teamplay.qh>
IntrusiveList g_invasion_roundends;
- STATIC_INIT(g_invasion_roundends) { g_invasion_roundends = IL_NEW(); }
-
IntrusiveList g_invasion_waves;
- STATIC_INIT(g_invasion_waves) { g_invasion_waves = IL_NEW(); }
-
IntrusiveList g_invasion_spawns;
- STATIC_INIT(g_invasion_spawns) { g_invasion_spawns = IL_NEW(); }
+ STATIC_INIT(g_invasion)
+ {
+ g_invasion_roundends = IL_NEW();
+ g_invasion_waves = IL_NEW();
+ g_invasion_spawns = IL_NEW();
+ }
float autocvar_g_invasion_round_timelimit;
float autocvar_g_invasion_spawnpoint_spawn_delay;
return true;
}
-MUTATOR_HOOKFUNCTION(inv, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
+MUTATOR_HOOKFUNCTION(inv, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
{
M_ARGV(0, float) = invasion_teams;
+ return true;
}
MUTATOR_HOOKFUNCTION(inv, AllowMobButcher)
void invasion_ScoreRules(int inv_teams)
{
- if(inv_teams) { CheckAllowedTeams(NULL); }
+ //if(inv_teams) { CheckAllowedTeams(NULL); } // Another bug?
GameRules_score_enabled(false);
GameRules_scoring(inv_teams, 0, 0, {
if (inv_teams) {