set sv_minigames_pong_ai_tolerance 0.33 "Distance of the ball relative to the paddle size"
-// Snake? Snake! SNAAAAKE!!
-set sv_minigames_snake_wrap 0 "Wrap around the edges of the screen instead of dying on touch"
-set sv_minigames_snake_delay_initial 0.7 "Initial delay between snake movement"
-set sv_minigames_snake_delay_multiplier 50 "Multiplier of incremental of movement speed (player_score / cvar)"
-set sv_minigames_snake_delay_min 0.1 "Minimum delay between snake movement (at fastest rate)"
-set sv_minigames_snake_lives 3
-
-
// Bulldozer
set sv_minigames_bulldozer_startlevel "level1"
" -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
" -teams,lms/deaths +ft,tdm/deaths" \
" -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
-" +teams/teamkills"\
" -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
+" +tdm,ft,dom,ons,as/teamkills"\
" -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
" +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
" +lms/lives +lms/rank" \
void W_Nexball_Attack(entity actor, .entity weaponentity, float t);
void W_Nexball_Attack2(entity actor, .entity weaponentity);
-vector trigger_push_calculatevelocity(vector org, entity tgt, float ht);
+vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity);
METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, .entity weaponentity, int fire))
{
{
entity _ball = actor.ballcarried;
W_SetupShot(actor, weaponentity, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0);
- DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32));
+ DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32, _ball));
setthink(_ball, W_Nexball_Think);
_ball.nextthink = time;
return;
setorigin(wep, ent.origin);
setmodel(wep, MDL_OK_HMG);
wep.ok_item = true;
- wep.noalign = ent.noalign;
+ wep.noalign = Item_ShouldKeepPosition(ent);
wep.cnt = ent.cnt;
wep.team = ent.team;
wep.respawntime = g_pickup_respawntime_superweapon;
setorigin(wep, ent.origin);
setmodel(wep, MDL_OK_RPC);
wep.ok_item = true;
- wep.noalign = ent.noalign;
+ wep.noalign = Item_ShouldKeepPosition(ent);
wep.cnt = ent.cnt;
wep.team = ent.team;
wep.respawntime = g_pickup_respawntime_superweapon;
if (!expr_evaluate(autocvar_g_overkill))
{
new_item = Item_Create(strzone(new_classname), item.origin,
- item.noalign);
+ Item_ShouldKeepPosition(item));
random_items_is_spawning = false;
if (new_item == NULL)
{
new_item = spawn();
new_item.classname = strzone(new_classname);
new_item.spawnfunc_checked = true;
- new_item.noalign = item.noalign;
+ new_item.noalign = Item_ShouldKeepPosition(item);
new_item.ok_item = true;
Item_Initialize(new_item, new_classname);
random_items_is_spawning = false;
return -1;
}
+/// \brief Returns whether team is valid.
+/// \param[in] team_ Team to check.
+/// \return True if team is valid, false otherwise.
+bool Team_IsValidTeam(int team_)
+{
+ switch (team_)
+ {
+ case NUM_TEAM_1:
+ case NUM_TEAM_2:
+ case NUM_TEAM_3:
+ case NUM_TEAM_4:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/// \brief Returns whether team number is valid.
+/// \param[in] number Team number to check.
+/// \return True if team number is valid, false otherwise.
+bool Team_IsValidNumber(int number)
+{
+ switch (number)
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
float Team_NumberToTeam(float number)
{
switch(number)
tgt - target entity (can be either a point or a model entity; if it is
the latter, its midpoint is used)
ht - jump height, measured from the higher one of org and tgt's midpoint
+ pushed_entity - object that is to be pushed
Returns: velocity for the jump
*/
-vector trigger_push_calculatevelocity(vector org, entity tgt, float ht)
+vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity)
{
float grav, sdist, zdist, vs, vz, jumpheight;
vector sdir, torg;
torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5;
- grav = PHYS_GRAVITY(tgt);
- if(PHYS_ENTGRAVITY(tgt))
- grav *= PHYS_ENTGRAVITY(tgt);
+ grav = PHYS_GRAVITY(NULL);
+ if(pushed_entity && PHYS_ENTGRAVITY(pushed_entity))
+ grav *= PHYS_ENTGRAVITY(pushed_entity);
zdist = torg.z - org.z;
sdist = vlen(torg - org - zdist * '0 0 1');
if(this.enemy)
{
- targ.velocity = trigger_push_calculatevelocity(targ.origin, this.enemy, this.height);
+ targ.velocity = trigger_push_calculatevelocity(targ.origin, this.enemy, this.height, targ);
}
else if(this.target && this.target != "")
{
else
RandomSelection_AddEnt(e, 1, 1);
}
- targ.velocity = trigger_push_calculatevelocity(targ.origin, RandomSelection_chosen_ent, this.height);
+ targ.velocity = trigger_push_calculatevelocity(targ.origin, RandomSelection_chosen_ent, this.height, targ);
}
else
{
entity e = spawn();
setsize(e, PL_MIN_CONST, PL_MAX_CONST);
e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
- e.velocity = trigger_push_calculatevelocity(org, t, this.height);
+ e.velocity = trigger_push_calculatevelocity(org, t, this.height, e);
if(item)
{
vector flatdir = normalize(dist - eZ * dist.z);
vector ofs = flatdir * 0.5 * min(fabs(this.absmax.x - this.absmin.x), fabs(this.absmax.y - this.absmin.y));
new_org = org + ofs;
- e.velocity = trigger_push_calculatevelocity(new_org, t, this.height);
+ e.velocity = trigger_push_calculatevelocity(new_org, t, this.height, e);
vel = e.velocity;
if (vdist(vec2(e.velocity), <, autocvar_sv_maxspeed))
e.velocity = autocvar_sv_maxspeed * flatdir;
valid_best_target = true;
}
new_org = org - ofs;
- e.velocity = trigger_push_calculatevelocity(new_org, t, this.height);
+ e.velocity = trigger_push_calculatevelocity(new_org, t, this.height, e);
vel = e.velocity;
if (vdist(vec2(e.velocity), <, autocvar_sv_maxspeed))
e.velocity = autocvar_sv_maxspeed * flatdir;
tgt - target entity (can be either a point or a model entity; if it is
the latter, its midpoint is used)
ht - jump height, measured from the higher one of org and tgt's midpoint
+ pushed_entity - object that is to be pushed
Returns: velocity for the jump
*/
-vector trigger_push_calculatevelocity(vector org, entity tgt, float ht);
+vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity);
void trigger_push_touch(entity this, entity toucher);
{
this.flags = FL_ITEM;
IL_PUSH(g_items, this);
- this.velocity = trigger_push_calculatevelocity(this.origin, this.realowner, 128);
+ this.velocity = trigger_push_calculatevelocity(this.origin, this.realowner, 128, this);
tracetoss(this, this);
if(vdist(trace_endpos - this.realowner.origin, <, 128))
{
else if(this.bot_forced_team==4)
this.team = NUM_TEAM_4;
else
- JoinBestTeam(this, false, true);
+ JoinBestTeam(this, true);
havocbot_setupbot(this);
}
accuracy_resend(this);
if (this.team < 0)
- JoinBestTeam(this, false, true);
+ JoinBestTeam(this, true);
entity spot = SelectSpawnPoint(this, false);
if (!spot) {
{
if(this.killindicator_teamchange == -1)
{
- JoinBestTeam( this, false, true );
+ JoinBestTeam( this, true );
}
else if(this.killindicator_teamchange == -2)
{
{
int id = this.playerid;
this.playerid = 0; // silent
- JoinBestTeam(this, false, false); // if the team number is valid, keep it
+ JoinBestTeam(this, false); // if the team number is valid, keep it
this.playerid = id;
}
if(!this.team_selected)
if(autocvar_g_campaign || autocvar_g_balance_teams)
- JoinBestTeam(this, false, true);
+ JoinBestTeam(this, true);
if(autocvar_g_campaign)
campaign_bots_may_start = true;
item.m_isloot = loot;
}
+bool Item_ShouldKeepPosition(entity item)
+{
+ return item.noalign || (item.spawnflags & 1);
+}
+
bool Item_IsExpiring(entity item)
{
return item.m_isexpiring;
/// \return No return.
void Item_SetLoot(entity item, bool loot);
+/// \brief Returns whether item should keep its position or be dropped to the
+/// ground.
+/// \param[in] item Item to check.
+/// \return True if item should keep its position or false if it should be
+/// dropped to the ground.
+bool Item_ShouldKeepPosition(entity item);
+
/// \brief Returns whether the item is expiring (i.e. its strength, shield and
/// superweapon timers expire while it is on the ground).
/// \param[in] item Item to check.
bool IsTeamSmallerThanTeam(int team_a, int team_b, entity player,
bool use_score)
{
+ if (!Team_IsValidNumber(team_a))
+ {
+ LOG_FATALF("IsTeamSmallerThanTeam: team_a is invalid: %f", team_a);
+ }
+ if (!Team_IsValidNumber(team_b))
+ {
+ LOG_FATALF("IsTeamSmallerThanTeam: team_b is invalid: %f", team_b);
+ }
if (team_a == team_b)
{
return false;
bool IsTeamEqualToTeam(int team_a, int team_b, entity player, bool use_score)
{
+ if (!Team_IsValidNumber(team_a))
+ {
+ LOG_FATALF("IsTeamEqualToTeam: team_a is invalid: %f", team_a);
+ }
+ if (!Team_IsValidNumber(team_b))
+ {
+ LOG_FATALF("IsTeamEqualToTeam: team_b is invalid: %f", team_b);
+ }
if (team_a == team_b)
{
return true;
return RandomSelection_chosen_float;
}
-int JoinBestTeam(entity this, bool only_return_best, bool force_best_team)
+void JoinBestTeam(entity this, bool force_best_team)
{
// don't join a team if we're not playing a team game
if (!teamplay)
{
- return 0;
+ return;
}
// find out what teams are available
CheckAllowedTeams(this);
- // if we don't care what team he ends up on, put him on whatever team he entered as.
- // if he's not on a valid team, then let other code put him on the smallest team
+ // if we don't care what team they end up on, put them on whatever team they entered as.
+ // if they're not on a valid team, then let other code put them on the smallest team
if (!force_best_team)
{
int selected_team;
- if( c1 >= 0 && this.team == NUM_TEAM_1)
+ if ((c1 >= 0) && (this.team == NUM_TEAM_1))
+ {
selected_team = this.team;
- else if(c2 >= 0 && this.team == NUM_TEAM_2)
+ }
+ else if ((c2 >= 0) && (this.team == NUM_TEAM_2))
+ {
selected_team = this.team;
- else if(c3 >= 0 && this.team == NUM_TEAM_3)
+ }
+ else if ((c3 >= 0) && (this.team == NUM_TEAM_3))
+ {
selected_team = this.team;
- else if(c4 >= 0 && this.team == NUM_TEAM_4)
+ }
+ else if ((c4 >= 0) && (this.team == NUM_TEAM_4))
+ {
selected_team = this.team;
+ }
else
+ {
selected_team = -1;
+ }
if (selected_team > 0)
{
- if (!only_return_best)
- {
- SetPlayerTeamSimple(this, selected_team);
-
- // when JoinBestTeam is called by client.qc/ClientKill_Now_TeamChange the players team is -1 and thus skipped
- // when JoinBestTeam is called by client.qc/ClientConnect the player_id is 0 the log attempt is rejected
- LogTeamchange(this.playerid, this.team, 99);
- }
- return selected_team;
+ SetPlayerTeamSimple(this, selected_team);
+ LogTeamchange(this.playerid, this.team, 99);
+ return;
}
- // otherwise end up on the smallest team (handled below)
}
-
- int best_team = FindSmallestTeam(this, true);
- if (only_return_best || this.bot_forced_team)
+ // otherwise end up on the smallest team (handled below)
+ if (this.bot_forced_team)
{
- return best_team;
+ return;
}
+ int best_team = FindSmallestTeam(this, true);
best_team = Team_NumberToTeam(best_team);
if (best_team == -1)
{
TeamchangeFrags(this);
SetPlayerTeamSimple(this, best_team);
LogTeamchange(this.playerid, this.team, 2); // log auto join
- if (!IS_BOT_CLIENT(this))
+ if ((old_team != -1) && !IS_BOT_CLIENT(this))
{
AutoBalanceBots(old_team, Team_TeamToNumber(best_team));
}
KillPlayerForTeamChange(this);
- return best_team;
}
void SV_ChangeTeam(entity this, float _color)
void AutoBalanceBots(int source_team, int destination_team)
{
- if ((source_team == -1) || (destination_team == -1))
+ if (!Team_IsValidNumber(source_team))
+ {
+ LOG_WARNF("AutoBalanceBots: Source team is invalid: %f", source_team);
+ return;
+ }
+ if (!Team_IsValidNumber(destination_team))
{
+ LOG_WARNF("AutoBalanceBots: Destination team is invalid: %f",
+ destination_team);
return;
}
if (!autocvar_g_balance_teams ||
break;
}
}
+ if (num_players_source_team < 0)
+ {
+ return;
+ }
switch (destination_team)
{
case 1:
// NOTE: Assumes CheckAllowedTeams has already been called!
int FindSmallestTeam(entity player, float ignore_player);
-int JoinBestTeam(entity this, bool only_return_best, bool force_best_team);
+void JoinBestTeam(entity this, bool force_best_team);
/// \brief Auto balances bots in teams after the player has changed team.
/// \param[in] source_team Previous team of the player (1, 2, 3, 4).