1 void dom_EventLog(string mode, float team_before, entity actor) // use an alias for easy changing and quick editing later
3 if(autocvar_sv_eventlog)
4 GameLogEcho(strcat(":dom:", mode, ":", ftos(team_before), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
7 void set_dom_state(entity e)
9 e.dom_total_pps = total_pps;
10 e.dom_pps_red = pps_red;
11 e.dom_pps_blue = pps_blue;
12 if(domination_teams >= 3)
13 e.dom_pps_yellow = pps_yellow;
14 if(domination_teams >= 4)
15 e.dom_pps_pink = pps_pink;
18 void dompoint_captured ()
21 float old_delay, old_team, real_team;
22 string msg = "dom-neut";
24 // now that the delay has expired, switch to the latest team to lay claim to this point
30 dom_EventLog("taken", self.team, self.dmg_inflictor);
31 self.dmg_inflictor = world;
33 self.goalentity = head;
34 self.model = head.mdl;
35 self.modelindex = head.dmg;
36 self.skin = head.skin;
38 float points, wait_time;
39 if (autocvar_g_domination_point_amt)
40 points = autocvar_g_domination_point_amt;
43 if (autocvar_g_domination_point_rate)
44 wait_time = autocvar_g_domination_point_rate;
46 wait_time = self.wait;
48 if(domination_roundbased)
49 bprint(sprintf("^3%s^3%s\n", head.netname, self.message));
51 Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_DOMINATION_CAPTURE_TIME, head.netname, self.message, points, wait_time);
53 if(self.enemy.playerid == self.enemy_playerid)
54 PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
60 sound(self.enemy, CH_TRIGGER, head.noise, VOL_BASE, ATTEN_NORM);
62 sound(self, CH_TRIGGER, head.noise, VOL_BASE, ATTEN_NORM);
63 if (head.noise1 != "")
64 play2all(head.noise1);
66 self.delay = time + wait_time;
69 old_delay = self.delay;
71 self.team = real_team;
75 self.delay = old_delay;
80 case NUM_TEAM_1: msg = "dom-red"; break;
81 case NUM_TEAM_2: msg = "dom-blue"; break;
82 case NUM_TEAM_3: msg = "dom-yellow"; break;
83 case NUM_TEAM_4: msg = "dom-pink"; break;
86 WaypointSprite_UpdateSprites(self.sprite, msg, "", "");
88 total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
89 for(head = world; (head = find(head, classname, "dom_controlpoint")) != world; )
91 if (autocvar_g_domination_point_amt)
92 points = autocvar_g_domination_point_amt;
95 if (autocvar_g_domination_point_rate)
96 wait_time = autocvar_g_domination_point_rate;
98 wait_time = head.wait;
99 switch(head.goalentity.team)
102 pps_red += points/wait_time;
105 pps_blue += points/wait_time;
108 pps_yellow += points/wait_time;
111 pps_pink += points/wait_time;
114 total_pps += points/wait_time;
117 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
118 WaypointSprite_Ping(self.sprite);
122 FOR_EACH_REALCLIENT(head)
126 void AnimateDomPoint()
128 if(self.pain_finished > time)
130 self.pain_finished = time + self.t_width;
131 if(self.nextthink > self.pain_finished)
132 self.nextthink = self.pain_finished;
134 self.frame = self.frame + 1;
135 if(self.frame > self.t_length)
143 self.nextthink = time + 0.1;
145 //self.frame = self.frame + 1;
146 //if(self.frame > 119)
152 if (gameover || self.delay > time || time < game_starttime) // game has ended, don't keep giving points
155 if(autocvar_g_domination_point_rate)
156 self.delay = time + autocvar_g_domination_point_rate;
158 self.delay = time + self.wait;
160 // give credit to the team
161 // NOTE: this defaults to 0
162 if (!domination_roundbased)
163 if (self.goalentity.netname != "")
165 if(autocvar_g_domination_point_amt)
166 fragamt = autocvar_g_domination_point_amt;
168 fragamt = self.frags;
169 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
170 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
172 // give credit to the individual player, if he is still there
173 if (self.enemy.playerid == self.enemy_playerid)
175 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
176 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
186 if (!IS_PLAYER(other))
188 if (other.health < 1)
191 if(round_handler_IsActive() && !round_handler_IsRoundStarted())
194 if(time < self.captime + 0.3)
197 // only valid teams can claim it
198 head = find(world, classname, "dom_team");
199 while (head && head.team != other.team)
200 head = find(head, classname, "dom_team");
201 if (!head || head.netname == "" || head == self.goalentity)
206 self.team = self.goalentity.team; // this stores the PREVIOUS team!
208 self.cnt = other.team;
209 self.owner = head; // team to switch to after the delay
210 self.dmg_inflictor = other;
213 // self.delay = time + cvar("g_domination_point_capturetime");
214 //self.nextthink = time + cvar("g_domination_point_capturetime");
215 //self.think = dompoint_captured;
217 // go to neutral team in the mean time
218 head = find(world, classname, "dom_team");
219 while (head && head.netname != "")
220 head = find(head, classname, "dom_team");
224 WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
225 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
226 WaypointSprite_Ping(self.sprite);
228 self.goalentity = head;
229 self.model = head.mdl;
230 self.modelindex = head.dmg;
231 self.skin = head.skin;
233 self.enemy = other; // individual player scoring
234 self.enemy_playerid = other.playerid;
238 void dom_controlpoint_setup()
241 // find the spawnfunc_dom_team representing unclaimed points
242 head = find(world, classname, "dom_team");
243 while(head && head.netname != "")
244 head = find(head, classname, "dom_team");
246 objerror("no spawnfunc_dom_team with netname \"\" found\n");
248 // copy important properties from spawnfunc_dom_team entity
249 self.goalentity = head;
250 setmodel(self, head.mdl); // precision already set
251 self.skin = head.skin;
255 if(self.message == "")
256 self.message = " has captured a control point";
263 float points, waittime;
264 if (autocvar_g_domination_point_amt)
265 points = autocvar_g_domination_point_amt;
268 if (autocvar_g_domination_point_rate)
269 waittime = autocvar_g_domination_point_rate;
271 waittime = self.wait;
273 total_pps += points/waittime;
276 self.t_width = 0.02; // frame animation rate
278 self.t_length = 239; // maximum frame
280 self.think = dompointthink;
281 self.nextthink = time;
282 self.touch = dompointtouch;
283 self.solid = SOLID_TRIGGER;
284 self.flags = FL_ITEM;
285 setsize(self, '-32 -32 -32', '32 32 32');
286 setorigin(self, self.origin + '0 0 20');
289 waypoint_spawnforitem(self);
290 WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite, RADARICON_DOMPOINT, '0 1 1');
293 float total_controlpoints, redowned, blueowned, yellowowned, pinkowned;
294 void Domination_count_controlpoints()
297 total_controlpoints = redowned = blueowned = yellowowned = pinkowned = 0;
298 for(e = world; (e = find(e, classname, "dom_controlpoint")) != world; )
300 ++total_controlpoints;
301 redowned += (e.goalentity.team == NUM_TEAM_1);
302 blueowned += (e.goalentity.team == NUM_TEAM_2);
303 yellowowned += (e.goalentity.team == NUM_TEAM_3);
304 pinkowned += (e.goalentity.team == NUM_TEAM_4);
308 float Domination_GetWinnerTeam()
310 float winner_team = 0;
311 if(redowned == total_controlpoints)
312 winner_team = NUM_TEAM_1;
313 if(blueowned == total_controlpoints)
315 if(winner_team) return 0;
316 winner_team = NUM_TEAM_2;
318 if(yellowowned == total_controlpoints)
320 if(winner_team) return 0;
321 winner_team = NUM_TEAM_3;
323 if(pinkowned == total_controlpoints)
325 if(winner_team) return 0;
326 winner_team = NUM_TEAM_4;
330 return -1; // no control points left?
333 #define DOM_OWNED_CONTROLPOINTS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0))
334 #define DOM_OWNED_CONTROLPOINTS_OK() (DOM_OWNED_CONTROLPOINTS() < total_controlpoints)
335 float Domination_CheckWinner()
337 if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
339 Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
340 Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
341 round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit);
345 Domination_count_controlpoints();
347 float winner_team = Domination_GetWinnerTeam();
349 if(winner_team == -1)
354 Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_));
355 Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_));
356 TeamScore_AddToTeam(winner_team, ST_DOM_CAPS, +1);
358 else if(winner_team == -1)
360 Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
361 Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
364 round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit);
369 float Domination_CheckPlayers()
374 void Domination_RoundStart()
378 e.player_blocked = 0;
381 //go to best items, or control points you don't own
382 void havocbot_role_dom()
384 if(self.deadflag != DEAD_NO)
387 if (self.bot_strategytime < time)
389 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
390 navigation_goalrating_start();
391 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
392 havocbot_goalrating_items(8000, self.origin, 8000);
393 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
394 //havocbot_goalrating_waypoints(1, self.origin, 1000);
395 navigation_goalrating_end();
399 MUTATOR_HOOKFUNCTION(dom_GetTeamCount)
401 ret_float = domination_teams;
405 MUTATOR_HOOKFUNCTION(dom_ResetMap)
407 total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
408 FOR_EACH_PLAYER(self)
411 self.player_blocked = 1;
412 if(IS_REAL_CLIENT(self))
418 MUTATOR_HOOKFUNCTION(dom_PlayerSpawn)
420 if(domination_roundbased)
421 if(!round_handler_IsRoundStarted())
422 self.player_blocked = 1;
424 self.player_blocked = 0;
428 MUTATOR_HOOKFUNCTION(dom_ClientConnect)
434 MUTATOR_HOOKFUNCTION(dom_BotRoles)
436 self.havocbot_role = havocbot_role_dom;
440 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
441 Control point for Domination gameplay.
443 void spawnfunc_dom_controlpoint()
450 self.think = dom_controlpoint_setup;
451 self.nextthink = time + 0.1;
452 self.reset = dom_controlpoint_setup;
457 self.effects = self.effects | EF_LOWPRECISION;
458 if (autocvar_g_domination_point_fullbright)
459 self.effects |= EF_FULLBRIGHT;
462 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
463 Team declaration for Domination gameplay, this allows you to decide what team
464 names and control point models are used in your map.
466 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
467 can have netname set! The nameless team owns all control points at start.
471 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
473 Scoreboard color of the team (for example 4 is red and 13 is blue)
475 Model to use for control points owned by this team (for example
476 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
479 Skin of the model to use (for team skins on a single model)
481 Sound to play when this team captures a point.
482 (this is a localized sound, like a small alarm or other effect)
484 Narrator speech to play when this team captures a point.
485 (this is a global sound, like "Red team has captured a control point")
488 void spawnfunc_dom_team()
490 if(!g_domination || autocvar_g_domination_teams_override >= 2)
495 precache_model(self.model);
496 if (self.noise != "")
497 precache_sound(self.noise);
498 if (self.noise1 != "")
499 precache_sound(self.noise1);
500 self.classname = "dom_team";
501 setmodel(self, self.model); // precision not needed
502 self.mdl = self.model;
503 self.dmg = self.modelindex;
506 // this would have to be changed if used in quakeworld
508 self.team = self.cnt + 1; // WHY are these different anyway?
512 void ScoreRules_dom(float teams)
514 if(domination_roundbased)
516 ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true);
517 ScoreInfo_SetLabel_TeamScore (ST_DOM_CAPS, "caps", SFL_SORT_PRIO_PRIMARY);
518 ScoreInfo_SetLabel_PlayerScore(SP_DOM_TAKES, "takes", 0);
519 ScoreRules_basics_end();
523 float sp_domticks, sp_score;
524 sp_score = sp_domticks = 0;
525 if(autocvar_g_domination_disable_frags)
526 sp_domticks = SFL_SORT_PRIO_PRIMARY;
528 sp_score = SFL_SORT_PRIO_PRIMARY;
529 ScoreRules_basics(teams, sp_score, sp_score, true);
530 ScoreInfo_SetLabel_TeamScore (ST_DOM_TICKS, "ticks", sp_domticks);
531 ScoreInfo_SetLabel_PlayerScore(SP_DOM_TICKS, "ticks", sp_domticks);
532 ScoreInfo_SetLabel_PlayerScore(SP_DOM_TAKES, "takes", 0);
533 ScoreRules_basics_end();
537 // code from here on is just to support maps that don't have control point and team entities
538 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
543 self.classname = "dom_team";
544 self.netname = teamname;
545 self.cnt = teamcolor;
546 self.model = pointmodel;
547 self.skin = pointskin;
548 self.noise = capsound;
549 self.noise1 = capnarration;
550 self.message = capmessage;
552 // this code is identical to spawnfunc_dom_team
553 setmodel(self, self.model); // precision not needed
554 self.mdl = self.model;
555 self.dmg = self.modelindex;
558 // this would have to be changed if used in quakeworld
559 self.team = self.cnt + 1;
565 void dom_spawnpoint(vector org)
570 self.classname = "dom_controlpoint";
571 self.think = spawnfunc_dom_controlpoint;
572 self.nextthink = time;
573 setorigin(self, org);
574 spawnfunc_dom_controlpoint();
578 // spawn some default teams if the map is not set up for domination
579 void dom_spawnteams(float teams)
581 dom_spawnteam("Red", NUM_TEAM_1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
582 dom_spawnteam("Blue", NUM_TEAM_2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
584 dom_spawnteam("Yellow", NUM_TEAM_3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
586 dom_spawnteam("Pink", NUM_TEAM_4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
587 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
590 void dom_DelayedInit() // Do this check with a delay so we can wait for teams to be set up.
592 // if no teams are found, spawn defaults
593 if(find(world, classname, "dom_team") == world || autocvar_g_domination_teams_override >= 2)
595 print("No ""dom_team"" entities found on this map, creating them anyway.\n");
596 domination_teams = bound(2, ((autocvar_g_domination_teams_override < 2) ? autocvar_g_domination_default_teams : autocvar_g_domination_teams_override), 4);
597 dom_spawnteams(domination_teams);
600 CheckAllowedTeams(world);
601 domination_teams = ((c4>=0) ? 4 : (c3>=0) ? 3 : 2);
603 addstat(STAT_DOM_TOTAL_PPS, AS_FLOAT, dom_total_pps);
604 addstat(STAT_DOM_PPS_RED, AS_FLOAT, dom_pps_red);
605 addstat(STAT_DOM_PPS_BLUE, AS_FLOAT, dom_pps_blue);
606 if(domination_teams >= 3) addstat(STAT_DOM_PPS_YELLOW, AS_FLOAT, dom_pps_yellow);
607 if(domination_teams >= 4) addstat(STAT_DOM_PPS_PINK, AS_FLOAT, dom_pps_pink);
609 domination_roundbased = autocvar_g_domination_roundbased;
611 ScoreRules_dom(domination_teams);
613 if(domination_roundbased)
615 round_handler_Spawn(Domination_CheckPlayers, Domination_CheckWinner, Domination_RoundStart);
616 round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit);
620 void dom_Initialize()
622 precache_model("models/domination/dom_red.md3");
623 precache_model("models/domination/dom_blue.md3");
624 precache_model("models/domination/dom_yellow.md3");
625 precache_model("models/domination/dom_pink.md3");
626 precache_model("models/domination/dom_unclaimed.md3");
627 precache_sound("domination/claim.wav");
629 InitializeEntity(world, dom_DelayedInit, INITPRIO_GAMETYPE);
633 MUTATOR_DEFINITION(gamemode_domination)
635 MUTATOR_HOOK(GetTeamCount, dom_GetTeamCount, CBC_ORDER_ANY);
636 MUTATOR_HOOK(reset_map_players, dom_ResetMap, CBC_ORDER_ANY);
637 MUTATOR_HOOK(PlayerSpawn, dom_PlayerSpawn, CBC_ORDER_ANY);
638 MUTATOR_HOOK(ClientConnect, dom_ClientConnect, CBC_ORDER_ANY);
639 MUTATOR_HOOK(HavocBot_ChooseRole, dom_BotRoles, CBC_ORDER_ANY);
643 if(time > 1) // game loads at time 1
644 error("This is a game type and it cannot be added at runtime.");
650 print("This is a game type and it cannot be removed at runtime.");