From: Mario Date: Thu, 28 Jul 2016 15:02:30 +0000 (+0000) Subject: Merge branch 'Mario/teams_bitflag' into 'master' X-Git-Tag: xonotic-v0.8.2~700^2~10 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=87cbf00c7734cf2910502c217b5c5157511ba5ea;hp=-c Merge branch 'Mario/teams_bitflag' into 'master' Merge branch Mario/teams_bitflag (M merge request) Adds support for odd team matches, such as pink vs blue and yellow vs red. See merge request !335 --- 87cbf00c7734cf2910502c217b5c5157511ba5ea diff --combined qcsrc/client/hud/panel/modicons.qc index 18bde3c721,04b97c288e..5901a16326 --- a/qcsrc/client/hud/panel/modicons.qc +++ b/qcsrc/client/hud/panel/modicons.qc @@@ -108,6 -108,7 +108,7 @@@ void HUD_Mod_CTF_Reset( redflag_statuschange_time = blueflag_statuschange_time = yellowflag_statuschange_time = pinkflag_statuschange_time = neutralflag_statuschange_time = 0; } + int autocvar__teams_available; void HUD_Mod_CTF(vector pos, vector mySize) { vector redflag_pos, blueflag_pos, yellowflag_pos, pinkflag_pos, neutralflag_pos; @@@ -117,11 -118,12 +118,13 @@@ int redflag, blueflag, yellowflag, pinkflag, neutralflag; // current status float redflag_statuschange_elapsedtime = 0, blueflag_statuschange_elapsedtime = 0, yellowflag_statuschange_elapsedtime = 0, pinkflag_statuschange_elapsedtime = 0, neutralflag_statuschange_elapsedtime = 0; // time since the status changed bool ctf_oneflag; // one-flag CTF mode enabled/disabled + bool ctf_stalemate; // currently in stalemate int stat_items = STAT(CTF_FLAGSTATUS); float fs, fs2, fs3, size1, size2; vector e1, e2; + int nteams = autocvar__teams_available; + redflag = (stat_items/CTF_RED_FLAG_TAKEN) & 3; blueflag = (stat_items/CTF_BLUE_FLAG_TAKEN) & 3; yellowflag = (stat_items/CTF_YELLOW_FLAG_TAKEN) & 3; @@@ -130,16 -132,14 +133,16 @@@ ctf_oneflag = (stat_items & CTF_FLAG_NEUTRAL); + ctf_stalemate = (stat_items & CTF_STALEMATE); + mod_active = (redflag || blueflag || yellowflag || pinkflag || neutralflag || (stat_items & CTF_SHIELDED)); if (autocvar__hud_configure) { redflag = 1; blueflag = 2; - if (team_count >= 3) + if (nteams & BIT(2)) yellowflag = 2; - if (team_count >= 4) + if (nteams & BIT(3)) pinkflag = 3; ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor? } @@@ -201,18 -201,24 +204,24 @@@ break; \ } \ } MACRO_END - X(red, myteam != NUM_TEAM_1); - X(blue, myteam != NUM_TEAM_2); - X(yellow, myteam != NUM_TEAM_3 && team_count >= 3); - X(pink, myteam != NUM_TEAM_4 && team_count >= 4); + X(red, myteam != NUM_TEAM_1 && (nteams & BIT(0))); + X(blue, myteam != NUM_TEAM_2 && (nteams & BIT(1))); + X(yellow, myteam != NUM_TEAM_3 && (nteams & BIT(2))); + X(pink, myteam != NUM_TEAM_4 && (nteams & BIT(3))); X(neutral, ctf_oneflag); #undef X + int tcount = 2; + if(nteams & BIT(2)) + tcount = 3; + if(nteams & BIT(3)) + tcount = 4; + if (ctf_oneflag) { // hacky, but these aren't needed red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null; fs = fs2 = fs3 = 1; - } else switch (team_count) { + } else switch (tcount) { default: case 2: fs = 0.5; fs2 = 0.5; fs3 = 0.5; break; case 3: fs = 1; fs2 = 0.35; fs3 = 0.35; break; @@@ -267,8 -273,6 +276,8 @@@ #define X(team) MACRO_BEGIN { \ f = bound(0, team##flag_statuschange_elapsedtime * 2, 1); \ + if (team##_icon && ctf_stalemate) \ + drawpic_aspect_skin(team##flag_pos, "flag_stalemate", flag_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); \ if (team##_icon_prevstatus && f < 1) \ drawpic_aspect_skin_expanding(team##flag_pos, team##_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * team##_alpha_prevstatus, DRAWFLAG_NORMAL, f); \ if (team##_icon) \ diff --combined qcsrc/common/gamemodes/gamemode/nexball/nexball.qc index 21d2d858b0,1636965e0b..35fe31c2bd --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc @@@ -72,7 -72,7 +72,7 @@@ float OtherTeam(float t) //works only const float ST_NEXBALL_GOALS = 1; const float SP_NEXBALL_GOALS = 4; const float SP_NEXBALL_FAULTS = 5; - void nb_ScoreRules(float teams) + void nb_ScoreRules(int teams) { ScoreRules_basics(teams, 0, 0, true); ScoreInfo_SetLabel_TeamScore( ST_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); @@@ -178,7 -178,7 +178,7 @@@ void GiveBall(entity plyr, entity ball ball.effects &= ~autocvar_g_nexball_basketball_effects_default; ball.velocity = '0 0 0'; - ball.movetype = MOVETYPE_NONE; + set_movetype(ball, MOVETYPE_NONE); settouch(ball, func_null); ball.effects |= EF_NOSHADOW; ball.scale = 1; // scale down. @@@ -209,7 -209,7 +209,7 @@@ void DropBall(entity ball, vector org, setattachment(ball, NULL, ""); setorigin(ball, org); - ball.movetype = MOVETYPE_BOUNCE; + set_movetype(ball, MOVETYPE_BOUNCE); UNSET_ONGROUND(ball); ball.scale = ball_scale; ball.velocity = vel; @@@ -237,7 -237,7 +237,7 @@@ void InitBall(entity this { if(gameover) return; UNSET_ONGROUND(this); - this.movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); if(this.classname == "nexball_basketball") settouch(this, basketball_touch); else if(this.classname == "nexball_football") @@@ -261,7 -261,7 +261,7 @@@ void ResetBall(entity this bprint("The ", Team_ColoredFullName(this.team), " held the ball for too long.\n"); settouch(this, func_null); - this.movetype = MOVETYPE_NOCLIP; + set_movetype(this, MOVETYPE_NOCLIP); this.velocity = '0 0 0'; // just in case? if(!this.cnt) LogNB("resetidle", NULL); @@@ -283,7 -283,7 +283,7 @@@ vtos(this.origin - this.spawnorigin), " Velocity: ", vtos(this.velocity), "\n"); this.velocity = '0 0 0'; setorigin(this, this.spawnorigin); // make sure it's positioned correctly anyway - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); setthink(this, InitBall); this.nextthink = max(time, game_starttime) + autocvar_g_nexball_delay_start; } @@@ -376,7 -376,7 +376,7 @@@ void GoalTouch(entity this, entity touc EXACTTRIGGER_TOUCH(this, toucher); - if(nb_teams == 2) + if(NumTeams(nb_teams) == 2) otherteam = OtherTeam(ball.team); else otherteam = 0; @@@ -395,7 -395,7 +395,7 @@@ else if(this.team == GOAL_FAULT) { LogNB("fault", ball.pusher); - if(nb_teams == 2) + if(NumTeams(nb_teams) == 2) bprint(Team_ColoredFullName(otherteam), " gets a point due to ", pname, "^7's silliness.\n"); else bprint(Team_ColoredFullName(ball.team), " loses a point due to ", pname, "^7's silliness.\n"); @@@ -421,7 -421,7 +421,7 @@@ if(ball.team && pscore) { - if(nb_teams == 2 && pscore < 0) + if(NumTeams(nb_teams) == 2 && pscore < 0) TeamScore_AddToTeam(otherteam, ST_NEXBALL_GOALS, -pscore); else TeamScore_AddToTeam(ball.team, ST_NEXBALL_GOALS, pscore); @@@ -466,7 -466,7 +466,7 @@@ void nb_spawnteam(string teamname, floa e.netname = teamname; e.cnt = teamcolor; e.team = e.cnt + 1; - nb_teams += 1; + //nb_teams += 1; } void nb_spawnteams() @@@ -481,6 -481,7 +481,7 @@@ if(!t_red) { nb_spawnteam("Red", e.team-1) ; + nb_teams |= BIT(0); t_red = true; } break; @@@ -489,6 -490,7 +490,7 @@@ { nb_spawnteam("Blue", e.team-1) ; t_blue = true; + nb_teams |= BIT(1); } break; case NUM_TEAM_3: @@@ -496,6 -498,7 +498,7 @@@ { nb_spawnteam("Yellow", e.team-1); t_yellow = true; + nb_teams |= BIT(2); } break; case NUM_TEAM_4: @@@ -503,6 -506,7 +506,7 @@@ { nb_spawnteam("Pink", e.team-1) ; t_pink = true; + nb_teams |= BIT(3); } break; } @@@ -549,7 -553,7 +553,7 @@@ void SpawnBall(entity this this.glow_trail = true; } - this.movetype = MOVETYPE_FLY; + set_movetype(this, MOVETYPE_FLY); if(!autocvar_g_nexball_sound_bounce) this.noise = ""; @@@ -818,7 -822,7 +822,7 @@@ void W_Nexball_Attack2(entity actor missile.owner = actor; - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); PROJECTILE_MAKETRIGGER(missile); //setmodel(missile, "models/elaser.mdl"); // precision set below diff --combined qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc index d0c575a48b,6c42f5a4fb..89c5eef51d --- a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc @@@ -211,7 -211,7 +211,7 @@@ void ons_CaptureShield_Spawn(entity gen settouch(shield, ons_CaptureShield_Touch); setcefc(shield, ons_CaptureShield_Customize); shield.effects = EF_ADDITIVE; - shield.movetype = MOVETYPE_NOCLIP; + set_movetype(shield, MOVETYPE_NOCLIP); shield.solid = SOLID_TRIGGER; shield.avelocity = '7 0 11'; shield.scale = 1; @@@ -358,11 -358,13 +358,11 @@@ void onslaught_updatelinks( } ons_ControlPoint_UpdateSprite(l); } - l = findchain(classname, "ons_captureshield"); - while(l) + FOREACH_ENTITY_CLASS("ons_captureshield", true, { - l.team = l.enemy.team; - l.colormap = l.enemy.colormap; - l = l.chain; - } + it.team = it.enemy.team; + it.colormap = it.enemy.colormap; + }); } @@@ -861,7 -863,7 +861,7 @@@ void ons_ControlPoint_Setup(entity cp cp.netname = "Control point"; cp.team = 0; cp.solid = SOLID_BBOX; - cp.movetype = MOVETYPE_NONE; + set_movetype(cp, MOVETYPE_NONE); settouch(cp, ons_ControlPoint_Touch); setthink(cp, ons_ControlPoint_Think); cp.nextthink = time + ONS_CP_THINKRATE; @@@ -880,14 -882,14 +880,14 @@@ if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location { cp.noalign = true; - cp.movetype = MOVETYPE_NONE; + set_movetype(cp, MOVETYPE_NONE); } else // drop to floor, automatically find a platform and set that as spawn origin { setorigin(cp, cp.origin + '0 0 20'); cp.noalign = false; droptofloor(cp); - cp.movetype = MOVETYPE_TOSS; + set_movetype(cp, MOVETYPE_TOSS); } // waypointsprites @@@ -1103,7 -1105,7 +1103,7 @@@ void ons_GeneratorSetup(entity gen) // gen.classname = "onslaught_generator"; gen.solid = SOLID_BBOX; gen.team_saved = teamnumber; - gen.movetype = MOVETYPE_NONE; + set_movetype(gen, MOVETYPE_NONE); gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health; gen.takedamage = DAMAGE_AIM; gen.bot_attack = true; @@@ -1287,6 -1289,8 +1287,6 @@@ void Onslaught_RoundStart( void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector org, float sradius) { - entity head; - float t, c; bool needarmor = false, needweapons = false; // Needs armor/health? @@@ -1294,7 -1298,7 +1294,7 @@@ needarmor = true; // Needs weapons? - c = 0; + int c = 0; FOREACH(Weapons, it != WEP_Null, { if(this.weapons & (it.m_wepset)) if(++c >= 4) @@@ -1311,18 -1315,20 +1311,18 @@@ LOG_DEBUG(strcat(this.netname, " needs armor ", ftos(needarmor) , "\n")); // See what is around - head = findchainfloat(bot_pickup, true); - while (head) + FOREACH_ENTITY_FLOAT(bot_pickup, true, { // gather health and armor only - if (head.solid) - if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) ) - if (vdist(head.origin - org, <, sradius)) + if (it.solid) + if ( ((it.health || it.armorvalue) && needarmor) || (it.weapons && needweapons ) ) + if (vdist(it.origin - org, <, sradius)) { - t = head.bot_pickupevalfunc(this, head); + int t = it.bot_pickupevalfunc(this, it); if (t > 0) - navigation_routerating(this, head, t * ratingscale, 500); + navigation_routerating(this, it, t * ratingscale, 500); } - head = head.chain; - } + }); } void havocbot_role_ons_setrole(entity this, int role) @@@ -1591,22 -1597,26 +1591,22 @@@ void havocbot_ons_reset_role(entity thi */ entity ons_Nearest_ControlPoint(entity this, vector pos, float max_dist) { - entity tmp_entity, closest_target = NULL; - tmp_entity = findchain(classname, "onslaught_controlpoint"); - while(tmp_entity) + entity closest_target = NULL; + FOREACH_ENTITY_CLASS("onslaught_controlpoint", true, { - if(SAME_TEAM(tmp_entity, this)) - if(tmp_entity.iscaptured) - if(max_dist <= 0 || vdist(tmp_entity.origin - pos, <=, max_dist)) - if(vlen2(tmp_entity.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) - closest_target = tmp_entity; - tmp_entity = tmp_entity.chain; - } - tmp_entity = findchain(classname, "onslaught_generator"); - while(tmp_entity) + if(SAME_TEAM(it, this)) + if(it.iscaptured) + if(max_dist <= 0 || vdist(it.origin - pos, <=, max_dist)) + if(vlen2(it.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) + closest_target = it; + }); + FOREACH_ENTITY_CLASS("onslaught_generator", true, { - if(SAME_TEAM(tmp_entity, this)) - if(max_dist <= 0 || vdist(tmp_entity.origin - pos, <, max_dist)) - if(vlen2(tmp_entity.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) - closest_target = tmp_entity; - tmp_entity = tmp_entity.chain; - } + if(SAME_TEAM(it, this)) + if(max_dist <= 0 || vdist(it.origin - pos, <, max_dist)) + if(vlen2(it.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) + closest_target = it; + }); return closest_target; } @@@ -1618,39 -1628,45 +1618,39 @@@ */ entity ons_Nearest_ControlPoint_2D(entity this, vector pos, float max_dist) { - entity tmp_entity, closest_target = NULL; + entity closest_target = NULL; vector delta; float smallest_distance = 0, distance; - tmp_entity = findchain(classname, "onslaught_controlpoint"); - while(tmp_entity) + FOREACH_ENTITY_CLASS("onslaught_controlpoint", true, { - delta = tmp_entity.origin - pos; + delta = it.origin - pos; delta_z = 0; distance = vlen(delta); - if(SAME_TEAM(tmp_entity, this)) - if(tmp_entity.iscaptured) + if(SAME_TEAM(it, this)) + if(it.iscaptured) if(max_dist <= 0 || distance <= max_dist) if(closest_target == NULL || distance <= smallest_distance ) { - closest_target = tmp_entity; + closest_target = it; smallest_distance = distance; } - - tmp_entity = tmp_entity.chain; - } - tmp_entity = findchain(classname, "onslaught_generator"); - while(tmp_entity) + }); + FOREACH_ENTITY_CLASS("onslaught_generator", true, { - delta = tmp_entity.origin - pos; + delta = it.origin - pos; delta_z = 0; distance = vlen(delta); - if(SAME_TEAM(tmp_entity, this)) + if(SAME_TEAM(it, this)) if(max_dist <= 0 || distance <= max_dist) if(closest_target == NULL || distance <= smallest_distance ) { - closest_target = tmp_entity; + closest_target = it; smallest_distance = distance; } - - tmp_entity = tmp_entity.chain; - } + }); return closest_target; } @@@ -1659,18 -1675,23 +1659,18 @@@ */ int ons_Count_SelfControlPoints(entity this) { - entity tmp_entity; - tmp_entity = findchain(classname, "onslaught_controlpoint"); int n = 0; - while(tmp_entity) + FOREACH_ENTITY_CLASS("onslaught_controlpoint", true, { - if(SAME_TEAM(tmp_entity, this)) - if(tmp_entity.iscaptured) + if(SAME_TEAM(it, this)) + if(it.iscaptured) n++; - tmp_entity = tmp_entity.chain; - } - tmp_entity = findchain(classname, "onslaught_generator"); - while(tmp_entity) + }); + FOREACH_ENTITY_CLASS("onslaught_generator", true, { - if(SAME_TEAM(tmp_entity, this)) + if(SAME_TEAM(it, this)) n++; - tmp_entity = tmp_entity.chain; - } + }); return n; } @@@ -2229,7 -2250,12 +2229,12 @@@ spawnfunc(onslaught_generator void ons_ScoreRules() { CheckAllowedTeams(NULL); - ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, true); + int teams = 0; + if(c1 >= 0) teams |= BIT(0); + if(c2 >= 0) teams |= BIT(1); + if(c3 >= 0) teams |= BIT(2); + if(c4 >= 0) teams |= BIT(3); + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY); ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0); diff --combined qcsrc/server/bot/bot.qc index df0f36ab1a,9418074284..308cd95101 --- a/qcsrc/server/bot/bot.qc +++ b/qcsrc/server/bot/bot.qc @@@ -346,28 -346,31 +346,28 @@@ void bot_endgame( void bot_relinkplayerlist() { - entity e; - entity prevbot; player_count = 0; currentbots = 0; - player_list = e = findchainflags(flags, FL_CLIENT); bot_list = NULL; - prevbot = NULL; - while (e) + + entity prevbot = NULL; + FOREACH_CLIENT(true, { - player_count = player_count + 1; - e.nextplayer = e.chain; - if (IS_BOT_CLIENT(e)) + ++player_count; + + if(IS_BOT_CLIENT(it)) { - if (prevbot) - prevbot.nextbot = e; + if(prevbot) + prevbot.nextbot = it; else { - bot_list = e; + bot_list = it; bot_list.nextbot = NULL; } - prevbot = e; - currentbots = currentbots + 1; + prevbot = it; + ++currentbots; } - e = e.chain; - } + }); LOG_TRACE(strcat("relink: ", ftos(currentbots), " bots seen.\n")); bot_strategytoken = bot_list; bot_strategytoken_taken = true; @@@ -424,84 -427,70 +424,84 @@@ void bot_clientconnect(entity this void bot_removefromlargestteam() { - float besttime, bestcount, thiscount; - entity best, head; CheckAllowedTeams(NULL); GetTeamCounts(NULL); - head = findchainfloat(isbot, true); - if (!head) - return; - best = head; - besttime = head.createdtime; - bestcount = 0; - while (head) + + entity best = NULL; + float besttime = 0; + int bestcount = 0; + + int bcount = 0; + FOREACH_ENTITY_FLOAT(isbot, true, { - if(head.team == NUM_TEAM_1) - thiscount = c1; - else if(head.team == NUM_TEAM_2) - thiscount = c2; - else if(head.team == NUM_TEAM_3) - thiscount = c3; - else if(head.team == NUM_TEAM_4) - thiscount = c4; - else - thiscount = 0; - if (thiscount > bestcount) + ++bcount; + + if(!best) + { + best = it; + besttime = it.createdtime; + } + + int thiscount = 0; + + switch(it.team) + { + case NUM_TEAM_1: thiscount = c1; break; + case NUM_TEAM_2: thiscount = c2; break; + case NUM_TEAM_3: thiscount = c3; break; + case NUM_TEAM_4: thiscount = c4; break; + } + + if(thiscount > bestcount) { bestcount = thiscount; - besttime = head.createdtime; - best = head; + besttime = it.createdtime; + best = it; } - else if (thiscount == bestcount && besttime < head.createdtime) + else if(thiscount == bestcount && besttime < it.createdtime) { - besttime = head.createdtime; - best = head; + besttime = it.createdtime; + best = it; } - head = head.chain; - } + }); + if(!bcount) + return; // no bots to remove currentbots = currentbots - 1; dropclient(best); } void bot_removenewest() { - float besttime; - entity best, head; - if(teamplay) { bot_removefromlargestteam(); return; } - head = findchainfloat(isbot, true); - if (!head) - return; - best = head; - besttime = head.createdtime; - while (head) + float besttime = 0; + entity best = NULL; + int bcount = 0; + + FOREACH_ENTITY_FLOAT(isbot, true, { - if (besttime < head.createdtime) + ++bcount; + + if(!best) { - besttime = head.createdtime; - best = head; + best = it; + besttime = it.createdtime; } - head = head.chain; - } + + if(besttime < it.createdtime) + { + besttime = it.createdtime; + best = it; + } + }); + + if(!bcount) + return; // no bots to remove + currentbots = currentbots - 1; dropclient(best); } @@@ -584,7 -573,7 +584,7 @@@ float bot_fixcount( // But don't remove bots immediately on level change, as the real players // usually haven't rejoined yet bots_would_leave = false; - if (teamplay && autocvar_bot_vs_human && (c3==-1 && c4==-1)) + if (teamplay && autocvar_bot_vs_human && AvailableTeams() == 2) bots = min(ceil(fabs(autocvar_bot_vs_human) * activerealplayers), maxclients - realplayers); else if ((realplayers || autocvar_bot_join_empty || (currentbots > 0 && time < 5))) { @@@ -670,11 -659,9 +670,11 @@@ void bot_serverframe( else { // TODO: Make this check cleaner - entity wp = findchain(classname, "waypoint"); - if(time - wp.nextthink > 10) + IL_EACH(g_waypoints, time - it.nextthink > 10, + { waypoint_save_links(); + break; + }); } } else diff --combined qcsrc/server/cl_client.qc index 5487f62622,9468eda99f..fe307fc01a --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@@ -269,7 -269,7 +269,7 @@@ void PutObserverInServer(entity this this.health = FRAGS_SPECTATOR; this.takedamage = DAMAGE_NO; this.solid = SOLID_NOT; - this.movetype = MOVETYPE_FLY_WORLDONLY; // user preference is controlled by playerprethink + set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink this.flags = FL_CLIENT | FL_NOTARGET; this.armorvalue = 666; this.effects = 0; @@@ -481,7 -481,7 +481,7 @@@ void PutClientInServer(entity this this.iscreature = true; this.teleportable = TELEPORT_NORMAL; this.damagedbycontents = true; - this.movetype = MOVETYPE_WALK; + set_movetype(this, MOVETYPE_WALK); this.solid = SOLID_SLIDEBOX; this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID; if (autocvar_g_playerclip_collisions) @@@ -1147,7 -1147,7 +1147,7 @@@ void ClientConnect(entity this if (!sv_foginterval && world.fog != "") stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n")); - if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1))) + if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && AvailableTeams() == 2)) if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts send_CSQC_teamnagger(); @@@ -1289,7 -1289,7 +1289,7 @@@ void respawn(entity this { this.solid = SOLID_NOT; this.takedamage = DAMAGE_NO; - this.movetype = MOVETYPE_FLY; + set_movetype(this, MOVETYPE_FLY); this.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed; this.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3; this.effects |= CSQCMODEL_EF_RESPAWNGHOST; @@@ -1615,7 -1615,7 +1615,7 @@@ void SpectateCopy(entity this, entity s this.angles = spectatee.v_angle; STAT(FROZEN, this) = STAT(FROZEN, spectatee); this.revive_progress = spectatee.revive_progress; - if(!PHYS_INPUT_BUTTON_USE(this)) + if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2) this.fixangle = true; setorigin(this, spectatee.origin); setsize(this, spectatee.mins, spectatee.maxs); @@@ -1675,7 -1675,7 +1675,7 @@@ bool SpectateSet(entity this msg_entity = this; WriteByte(MSG_ONE, SVC_SETVIEW); WriteEntity(MSG_ONE, this.enemy); - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); accuracy_resend(this); if(!SpectateUpdate(this)) @@@ -1925,6 -1925,7 +1925,6 @@@ void ObserverThink(entity this MinigameImpulse(this, this.impulse); this.impulse = 0; } - float prefered_movetype; if (this.flags & FL_JUMPRELEASED) { if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) { this.flags &= ~FL_JUMPRELEASED; @@@ -1935,8 -1936,9 +1935,8 @@@ TRANSMUTE(Spectator, this); } } else { - prefered_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP); - if (this.movetype != prefered_movetype) - this.movetype = prefered_movetype; + int preferred_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP); + set_movetype(this, preferred_movetype); } } else { if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))) { @@@ -1957,13 -1959,6 +1957,13 @@@ void SpectatorThink(entity this { if(MinigameImpulse(this, this.impulse)) this.impulse = 0; + + if (this.impulse == IMP_weapon_drop.impulse) + { + STAT(CAMERA_SPECTATOR, this) = (STAT(CAMERA_SPECTATOR, this) + 1) % 3; + this.impulse = 0; + return; + } } if (this.flags & FL_JUMPRELEASED) { if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) { @@@ -2286,15 -2281,12 +2286,15 @@@ void PlayerPreThink (entity this .entity weaponentity = weaponentities[0]; // TODO: unhardcode if (this.hook.state) { do_crouch = false; + } else if (this.waterlevel >= WATERLEVEL_SWIMMING) { + do_crouch = false; } else if (this.vehicle) { do_crouch = false; } else if (STAT(FROZEN, this)) { do_crouch = false; - } else if ((PS(this).m_weapon == WEP_SHOTGUN || PS(this).m_weapon == WEP_SHOCKWAVE) && this.(weaponentity).wframe == WFRAME_FIRE2 && time < this.(weaponentity).weapon_nextthink) { - // WEAPONTODO: predict + } else if ((PS(this).m_weapon.spawnflags & WEP_TYPE_MELEE_PRI) && this.(weaponentity).wframe == WFRAME_FIRE1 && time < this.(weaponentity).weapon_nextthink) { + do_crouch = false; + } else if ((PS(this).m_weapon.spawnflags & WEP_TYPE_MELEE_SEC) && this.(weaponentity).wframe == WFRAME_FIRE2 && time < this.(weaponentity).weapon_nextthink) { do_crouch = false; } @@@ -2387,13 -2379,13 +2387,13 @@@ entity e = this.teamkill_soundsource; entity oldpusher = e.pusher; e.pusher = this; - PlayerSound(e, playersound_teamshoot, CH_VOICE, VOICETYPE_LASTATTACKER_ONLY); + PlayerSound(e, playersound_teamshoot, CH_VOICE, VOL_BASEVOICE, VOICETYPE_LASTATTACKER_ONLY); e.pusher = oldpusher; } if (this.taunt_soundtime && time > this.taunt_soundtime) { this.taunt_soundtime = 0; - PlayerSound(this, playersound_taunt, CH_VOICE, VOICETYPE_AUTOTAUNT); + PlayerSound(this, playersound_taunt, CH_VOICE, VOL_BASEVOICE, VOICETYPE_AUTOTAUNT); } target_voicescript_next(this); @@@ -2412,7 -2404,7 +2412,7 @@@ void DrownPlayer(entity this if (this.waterlevel != WATERLEVEL_SUBMERGED || this.vehicle) { if(this.air_finished < time) - PlayerSound(this, playersound_gasp, CH_PLAYER, VOICETYPE_PLAYERSOUND); + PlayerSound(this, playersound_gasp, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND); this.air_finished = time + autocvar_g_balance_contents_drowndelay; this.dmg = 2; } @@@ -2426,30 -2418,6 +2426,30 @@@ } } +void Player_Physics(entity this) +{ + this.movetype = ((this.move_qcphysics) ? MOVETYPE_NONE : this.move_movetype); + + if(!this.move_qcphysics) + return; + + int mt = this.move_movetype; + + if(mt == MOVETYPE_PUSH || mt == MOVETYPE_FAKEPUSH || mt == MOVETYPE_PHYSICS) + { + this.move_qcphysics = false; + this.movetype = mt; + return; + } + + if(!frametime && !this.pm_frametime) + return; + + Movetype_Physics_NoMatchTicrate(this, this.pm_frametime, true); + + this.pm_frametime = 0; +} + /* ============= PlayerPostThink @@@ -2460,8 -2428,6 +2460,8 @@@ Called every frame for each client afte .float idlekick_lasttimeleft; void PlayerPostThink (entity this) { + Player_Physics(this); + if (sv_maxidle > 0) if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero). if (IS_REAL_CLIENT(this)) diff --combined qcsrc/server/mutators/mutator/gamemode_assault.qc index 6ce5724377,6c6d74a023..7285d7db36 --- a/qcsrc/server/mutators/mutator/gamemode_assault.qc +++ b/qcsrc/server/mutators/mutator/gamemode_assault.qc @@@ -273,14 -273,10 +273,14 @@@ void assault_new_round(entity this else assault_attacker_team = NUM_TEAM_1; - FOREACH_ENTITY(IS_NOT_A_CLIENT(it), LAMBDA( + FOREACH_ENTITY_FLOAT(pure_data, false, + { + if(IS_CLIENT(it)) + continue; + if (it.team_saved == NUM_TEAM_1) it.team_saved = NUM_TEAM_2; else if (it.team_saved == NUM_TEAM_2) it.team_saved = NUM_TEAM_1; - )); + }); // reset the level with a countdown cvar_set("timelimit", ftos(ceil(time - game_starttime) / 60)); @@@ -429,48 -425,68 +429,48 @@@ spawnfunc(target_assault_roundstart // legacy bot code void havocbot_goalrating_ast_targets(entity this, float ratingscale) { - entity ad, best, wp; - float radius, bestvalue; - bool found; - vector p; - - ad = findchain(classname, "func_assault_destructible"); - - for (; ad; ad = ad.chain) + FOREACH_ENTITY_CLASS("func_assault_destructible", it.bot_attack, { - if (ad.target == "") + if (it.target == "") continue; - if (!ad.bot_attack) - continue; - - found = false; - FOREACH_ENTITY_STRING(targetname, ad.target, + bool found = false; + FOREACH_ENTITY_STRING(targetname, it.target, { - if(it.classname == "target_objective_decrease") + if(it.classname != "target_objective_decrease") + continue; + + if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE) { - if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE) - { - // dprint(etos(ad),"\n"); - found = true; - break; - } + found = true; + break; } }); if(!found) - { - /// dprint("target not found\n"); continue; - } - /// dprint("target #", etos(ad), " found\n"); - - p = 0.5 * (ad.absmin + ad.absmax); - // dprint(vtos(ad.origin), " ", vtos(ad.absmin), " ", vtos(ad.absmax),"\n"); - // te_knightspike(p); - // te_lightning2(NULL, '0 0 0', p); + vector p = 0.5 * (it.absmin + it.absmax); // Find and rate waypoints around it found = false; - best = NULL; - bestvalue = 99999999999; - for(radius=0; radius<1500 && !found; radius+=500) + entity best = NULL; + float bestvalue = 99999999999; + entity des = it; + for(float radius = 0; radius < 1500 && !found; radius += 500) { - for(wp=findradius(p, radius); wp; wp=wp.chain) + FOREACH_ENTITY_RADIUS(p, radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED), { - if(!(wp.wpflags & WAYPOINTFLAG_GENERATED)) - if(wp.classname=="waypoint") - if(checkpvs(wp.origin, ad)) + if(checkpvs(it.origin, des)) { found = true; - if(wp.cnt, autocvar_g_ctf_pass_radius)) || ((trace_fraction < 1) && (trace_ent != this.pass_target)) || (time > this.ctf_droptime + autocvar_g_ctf_pass_timelimit)) { @@@ -1138,7 -1109,6 +1138,7 @@@ void ctf_RespawnFlag(entity flag if((flag.owner) && (flag.owner.flagcarried == flag)) { WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier); + WaypointSprite_Kill(flag.owner.wps_flagreturn); WaypointSprite_Kill(flag.wps_flagcarrier); flag.owner.flagcarried = NULL; @@@ -1157,7 -1127,7 +1157,7 @@@ setattachment(flag, NULL, ""); setorigin(flag, flag.ctf_spawnorigin); - flag.movetype = ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS); + set_movetype(flag, ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS)); flag.takedamage = DAMAGE_NO; flag.health = flag.max_flag_health; flag.solid = SOLID_TRIGGER; @@@ -1186,13 -1156,6 +1186,13 @@@ void ctf_Reset(entity this ctf_RespawnFlag(this); } +bool ctf_FlagBase_Customize(entity this, entity client) +{ + if(client.flagcarried && CTF_SAMETEAM(client, client.flagcarried)) + return false; + return true; +} + void ctf_DelayedFlagSetup(entity this) // called after a flag is placed on a map by ctf_FlagSetup() { // bot waypoints @@@ -1214,7 -1177,6 +1214,7 @@@ entity wp = WaypointSprite_SpawnFixed(basename, this.origin + FLAG_WAYPOINT_OFFSET, this, wps_flagbase, RADARICON_FLAG); wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 1 1'); WaypointSprite_UpdateTeamRadar(this.wps_flagbase, RADARICON_FLAG, ((this.team) ? colormapPaletteColor(this.team - 1, false) : '1 1 1')); + setcefc(wp, ctf_FlagBase_Customize); // captureshield setup ctf_CaptureShield_Spawn(this); @@@ -1318,13 -1280,13 +1318,13 @@@ void ctf_FlagSetup(int teamnumber, enti { flag.dropped_origin = flag.origin; flag.noalign = true; - flag.movetype = MOVETYPE_NONE; + set_movetype(flag, MOVETYPE_NONE); } else // drop to floor, automatically find a platform and set that as spawn origin { flag.noalign = false; droptofloor(flag); - flag.movetype = MOVETYPE_TOSS; + set_movetype(flag, MOVETYPE_NONE); } InitializeEntity(flag, ctf_DelayedFlagSetup, INITPRIO_SETLOCATION); @@@ -1528,19 -1490,23 +1528,19 @@@ void havocbot_goalrating_ctf_droppedfla void havocbot_goalrating_ctf_carrieritems(entity this, float ratingscale, vector org, float sradius) { - entity head; - float t; - head = findchainfloat(bot_pickup, true); - while (head) + FOREACH_ENTITY_FLOAT(bot_pickup, true, { // gather health and armor only - if (head.solid) - if (head.health || head.armorvalue) - if (vdist(head.origin - org, <, sradius)) + if (it.solid) + if (it.health || it.armorvalue) + if (vdist(it.origin - org, <, sradius)) { // get the value of the item - t = head.bot_pickupevalfunc(this, head) * 0.0001; + float t = it.bot_pickupevalfunc(this, it) * 0.0001; if (t > 0) - navigation_routerating(this, head, t * ratingscale, 500); + navigation_routerating(this, it, t * ratingscale, 500); } - head = head.chain; - } + }); } void havocbot_ctf_reset_role(entity this) @@@ -2021,7 -1987,7 +2021,7 @@@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThin | CTF_YELLOW_FLAG_CARRYING | CTF_YELLOW_FLAG_TAKEN | CTF_YELLOW_FLAG_LOST | CTF_PINK_FLAG_CARRYING | CTF_PINK_FLAG_TAKEN | CTF_PINK_FLAG_LOST | CTF_NEUTRAL_FLAG_CARRYING | CTF_NEUTRAL_FLAG_TAKEN | CTF_NEUTRAL_FLAG_LOST - | CTF_FLAG_NEUTRAL | CTF_SHIELDED); + | CTF_FLAG_NEUTRAL | CTF_SHIELDED | CTF_STALEMATE); // scan through all the flags and notify the client about them for(entity flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) @@@ -2055,9 -2021,6 +2055,9 @@@ if(player.ctf_captureshielded) player.ctf_flagstatus |= CTF_SHIELDED; + if(ctf_stalemate) + player.ctf_flagstatus |= CTF_STALEMATE; + // update the health of the flag carrier waypointsprite if(player.wps_flagcarrier) WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); @@@ -2330,7 -2293,7 +2330,7 @@@ MUTATOR_HOOKFUNCTION(ctf, MatchEnd case FLAG_PASSING: { // lock the flag, game is over - flag.movetype = MOVETYPE_NONE; + set_movetype(flag, MOVETYPE_NONE); flag.takedamage = DAMAGE_NO; flag.solid = SOLID_NOT; flag.nextthink = false; // stop thinking @@@ -2418,10 -2381,10 +2418,10 @@@ MUTATOR_HOOKFUNCTION(ctf, SV_ParseClien { switch(argv(1)) { - case "red": _team = NUM_TEAM_1; break; - case "blue": _team = NUM_TEAM_2; break; - case "yellow": if(ctf_teams >= 3) _team = NUM_TEAM_3; break; - case "pink": if(ctf_teams >= 4) _team = NUM_TEAM_4; break; + case "red": if(ctf_teams & BIT(0)) _team = NUM_TEAM_1; break; + case "blue": if(ctf_teams & BIT(1)) _team = NUM_TEAM_2; break; + case "yellow": if(ctf_teams & BIT(2)) _team = NUM_TEAM_3; break; + case "pink": if(ctf_teams & BIT(3)) _team = NUM_TEAM_4; break; } } @@@ -2604,27 -2567,44 +2604,44 @@@ void ctf_SpawnTeam (string teamname, in void ctf_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up. { - ctf_teams = 2; + ctf_teams = 0; entity tmp_entity; for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) { - if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); } - if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); } + //if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); } + //if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); } + + switch(tmp_entity.team) + { + case NUM_TEAM_1: BITSET_ASSIGN(ctf_teams, BIT(0)); break; + case NUM_TEAM_2: BITSET_ASSIGN(ctf_teams, BIT(1)); break; + case NUM_TEAM_3: BITSET_ASSIGN(ctf_teams, BIT(2)); break; + case NUM_TEAM_4: BITSET_ASSIGN(ctf_teams, BIT(3)); break; + } if(tmp_entity.team == 0) { ctf_oneflag = true; } } - ctf_teams = bound(2, ctf_teams, 4); + if(NumTeams(ctf_teams) < 2) // somehow, there's not enough flags! + { + ctf_teams = 0; // so set the default red and blue teams + BITSET_ASSIGN(ctf_teams, BIT(0)); + BITSET_ASSIGN(ctf_teams, BIT(1)); + } + + //ctf_teams = bound(2, ctf_teams, 4); // if no teams are found, spawn defaults if(find(NULL, classname, "ctf_team") == NULL) { LOG_TRACE("No \"ctf_team\" entities found on this map, creating them anyway.\n"); - ctf_SpawnTeam("Red", NUM_TEAM_1); - ctf_SpawnTeam("Blue", NUM_TEAM_2); - if(ctf_teams >= 3) + if(ctf_teams & BIT(0)) + ctf_SpawnTeam("Red", NUM_TEAM_1); + if(ctf_teams & BIT(1)) + ctf_SpawnTeam("Blue", NUM_TEAM_2); + if(ctf_teams & BIT(2)) ctf_SpawnTeam("Yellow", NUM_TEAM_3); - if(ctf_teams >= 4) + if(ctf_teams & BIT(3)) ctf_SpawnTeam("Pink", NUM_TEAM_4); } diff --combined qcsrc/server/mutators/mutator/gamemode_invasion.qc index ffbd29cdfa,de1aa5a2d4..b1a74f2aa9 --- a/qcsrc/server/mutators/mutator/gamemode_invasion.qc +++ b/qcsrc/server/mutators/mutator/gamemode_invasion.qc @@@ -193,11 -193,7 +193,11 @@@ float Invasion_CheckWinner( { if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) { - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(Monster_Remove(it))); + IL_EACH(g_monsters, true, + { + Monster_Remove(it); + }); + IL_CLEAR(g_monsters); Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER); Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER); @@@ -207,21 -203,23 +207,21 @@@ float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0; - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA( - if(it.health > 0) - { - if((get_monsterinfo(it.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) - ++supermonster_count; - ++total_alive_monsters; + IL_EACH(g_monsters, it.health > 0, + { + if((get_monsterinfo(it.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) + ++supermonster_count; + ++total_alive_monsters; - if(teamplay) - switch(it.team) - { - case NUM_TEAM_1: ++red_alive; break; - case NUM_TEAM_2: ++blue_alive; break; - case NUM_TEAM_3: ++yellow_alive; break; - case NUM_TEAM_4: ++pink_alive; break; - } + if(teamplay) + switch(it.team) + { + case NUM_TEAM_1: ++red_alive; break; + case NUM_TEAM_2: ++blue_alive; break; + case NUM_TEAM_3: ++yellow_alive; break; + case NUM_TEAM_4: ++pink_alive; break; } - )); + }); if((total_alive_monsters + inv_numkilled) < inv_maxspawned && inv_maxcurrent < inv_maxspawned) { @@@ -274,11 -272,7 +274,11 @@@ )); } - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(Monster_Remove(it))); + IL_EACH(g_monsters, true, + { + Monster_Remove(it); + }); + IL_CLEAR(g_monsters); if(teamplay) { @@@ -485,7 -479,7 +485,7 @@@ MUTATOR_HOOKFUNCTION(inv, AllowMobButch return true; } - void invasion_ScoreRules(float inv_teams) + void invasion_ScoreRules(int inv_teams) { if(inv_teams) { CheckAllowedTeams(NULL); } ScoreRules_basics(inv_teams, 0, 0, false); @@@ -497,7 -491,16 +497,16 @@@ void invasion_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up. { if(autocvar_g_invasion_teams) + { invasion_teams = bound(2, autocvar_g_invasion_teams, 4); + int teams = 0; + if(invasion_teams >= 1) teams |= BIT(0); + if(invasion_teams >= 2) teams |= BIT(1); + if(invasion_teams >= 3) teams |= BIT(2); + if(invasion_teams >= 4) teams |= BIT(3); + + invasion_teams = teams; // now set it? + } else invasion_teams = 0; diff --combined qcsrc/server/mutators/mutator/gamemode_keyhunt.qc index ab23b686fa,ce7f4a6d90..1ffe6e60c0 --- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc @@@ -144,7 -144,7 +144,7 @@@ const float SP_KH_DESTROYS = 6 const float SP_KH_PICKUPS = 7; const float SP_KH_KCKILLS = 8; const float SP_KH_LOSSES = 9; - void kh_ScoreRules(float teams) + void kh_ScoreRules(int teams) { ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); ScoreInfo_SetLabel_TeamScore( ST_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); @@@ -307,7 -307,7 +307,7 @@@ void kh_Key_Attach(entity key) // run #endif key.flags = 0; key.solid = SOLID_NOT; - key.movetype = MOVETYPE_NONE; + set_movetype(key, MOVETYPE_NONE); key.team = key.owner.team; key.nextthink = time; key.damageforcescale = 0; @@@ -346,7 -346,7 +346,7 @@@ void kh_Key_Detach(entity key) // runs #endif key.flags = FL_ITEM; key.solid = SOLID_TRIGGER; - key.movetype = MOVETYPE_TOSS; + set_movetype(key, MOVETYPE_TOSS); key.pain_finished = time + autocvar_g_balance_keyhunt_delay_return; key.damageforcescale = autocvar_g_balance_keyhunt_damageforcescale; key.takedamage = DAMAGE_YES; @@@ -959,9 -959,15 +959,15 @@@ void kh_WaitForPlayers() // delay star } else { - float missing_teams_mask = boolean(p1) + boolean(p2) * 2; - if(kh_teams >= 3) missing_teams_mask += boolean(p3) * 4; - if(kh_teams >= 4) missing_teams_mask += boolean(p4) * 8; + int missing_teams_mask = 0; + if(kh_teams & BIT(0)) + missing_teams_mask += boolean(p1) * 1; + if(kh_teams & BIT(1)) + missing_teams_mask += boolean(p2) * 2; + if(kh_teams & BIT(2)) + missing_teams_mask += boolean(p3) * 4; + if(kh_teams & BIT(3)) + missing_teams_mask += boolean(p4) * 8; if(prev_missing_teams_mask != missing_teams_mask) { Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); @@@ -1056,6 -1062,14 +1062,14 @@@ void kh_Initialize() // sets up th KH kh_teams = autocvar_g_keyhunt_teams; kh_teams = bound(2, kh_teams, 4); + int teams = 0; + if(kh_teams >= 1) teams |= BIT(0); + if(kh_teams >= 2) teams |= BIT(1); + if(kh_teams >= 3) teams |= BIT(2); + if(kh_teams >= 4) teams |= BIT(3); + + kh_teams = teams; // now set it? + // make a KH entity for controlling the game kh_controller = spawn(); setthink(kh_controller, kh_Controller_Think); diff --combined qcsrc/server/mutators/mutator/gamemode_race.qc index 3043d47145,3db96f7544..63fbd15a27 --- a/qcsrc/server/mutators/mutator/gamemode_race.qc +++ b/qcsrc/server/mutators/mutator/gamemode_race.qc @@@ -159,7 -159,7 +159,7 @@@ MUTATOR_HOOKFUNCTION(rc, PlayerPhysics if(player.race_penalty) { player.velocity = '0 0 0'; - player.movetype = MOVETYPE_NONE; + set_movetype(player, MOVETYPE_NONE); player.disableclientprediction = 2; } } @@@ -463,6 -463,14 +463,14 @@@ void rc_SetLimits( { ActivateTeamplay(); race_teams = bound(2, autocvar_g_race_teams, 4); + int teams = 0; + if(race_teams >= 1) teams |= BIT(0); + if(race_teams >= 2) teams |= BIT(1); + if(race_teams >= 3) teams |= BIT(2); + if(race_teams >= 4) teams |= BIT(3); + + race_teams = teams; // now set it? + have_team_spawns = -1; // request team spawns } else