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_ResetMap)
401 FOR_EACH_PLAYER(self)
404 self.player_blocked = 1;
409 MUTATOR_HOOKFUNCTION(dom_PlayerSpawn)
411 if(domination_roundbased)
412 if(!round_handler_IsRoundStarted())
413 self.player_blocked = 1;
415 self.player_blocked = 0;
419 MUTATOR_HOOKFUNCTION(dom_ClientConnect)
425 MUTATOR_HOOKFUNCTION(dom_BotRoles)
427 self.havocbot_role = havocbot_role_dom;
431 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
432 Control point for Domination gameplay.
434 void spawnfunc_dom_controlpoint()
441 self.think = dom_controlpoint_setup;
442 self.nextthink = time + 0.1;
443 self.reset = dom_controlpoint_setup;
448 self.effects = self.effects | EF_LOWPRECISION;
449 if (autocvar_g_domination_point_fullbright)
450 self.effects |= EF_FULLBRIGHT;
453 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
454 Team declaration for Domination gameplay, this allows you to decide what team
455 names and control point models are used in your map.
457 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
458 can have netname set! The nameless team owns all control points at start.
462 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
464 Scoreboard color of the team (for example 4 is red and 13 is blue)
466 Model to use for control points owned by this team (for example
467 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
470 Skin of the model to use (for team skins on a single model)
472 Sound to play when this team captures a point.
473 (this is a localized sound, like a small alarm or other effect)
475 Narrator speech to play when this team captures a point.
476 (this is a global sound, like "Red team has captured a control point")
479 void spawnfunc_dom_team()
481 if(!g_domination || autocvar_g_domination_teams_override >= 2)
486 precache_model(self.model);
487 if (self.noise != "")
488 precache_sound(self.noise);
489 if (self.noise1 != "")
490 precache_sound(self.noise1);
491 self.classname = "dom_team";
492 setmodel(self, self.model); // precision not needed
493 self.mdl = self.model;
494 self.dmg = self.modelindex;
497 // this would have to be changed if used in quakeworld
499 self.team = self.cnt + 1; // WHY are these different anyway?
503 void ScoreRules_dom()
505 if(domination_roundbased)
507 ScoreRules_basics(domination_teams, SFL_SORT_PRIO_PRIMARY, 0, TRUE);
508 ScoreInfo_SetLabel_TeamScore (ST_DOM_CAPS, "caps", SFL_SORT_PRIO_PRIMARY);
509 ScoreInfo_SetLabel_PlayerScore(SP_DOM_TAKES, "takes", 0);
510 ScoreRules_basics_end();
514 float sp_domticks, sp_score;
515 sp_score = sp_domticks = 0;
516 if(autocvar_g_domination_disable_frags)
517 sp_domticks = SFL_SORT_PRIO_PRIMARY;
519 sp_score = SFL_SORT_PRIO_PRIMARY;
520 CheckAllowedTeams(world);
521 ScoreRules_basics(domination_teams, sp_score, sp_score, TRUE);
522 ScoreInfo_SetLabel_TeamScore (ST_DOM_TICKS, "ticks", sp_domticks);
523 ScoreInfo_SetLabel_PlayerScore(SP_DOM_TICKS, "ticks", sp_domticks);
524 ScoreInfo_SetLabel_PlayerScore(SP_DOM_TAKES, "takes", 0);
525 ScoreRules_basics_end();
529 // code from here on is just to support maps that don't have control point and team entities
530 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
535 self.classname = "dom_team";
536 self.netname = teamname;
537 self.cnt = teamcolor;
538 self.model = pointmodel;
539 self.skin = pointskin;
540 self.noise = capsound;
541 self.noise1 = capnarration;
542 self.message = capmessage;
544 // this code is identical to spawnfunc_dom_team
545 setmodel(self, self.model); // precision not needed
546 self.mdl = self.model;
547 self.dmg = self.modelindex;
550 // this would have to be changed if used in quakeworld
551 self.team = self.cnt + 1;
557 void dom_spawnpoint(vector org)
562 self.classname = "dom_controlpoint";
563 self.think = spawnfunc_dom_controlpoint;
564 self.nextthink = time;
565 setorigin(self, org);
566 spawnfunc_dom_controlpoint();
570 // spawn some default teams if the map is not set up for domination
571 void dom_spawnteams()
573 dom_spawnteam("Red", NUM_TEAM_1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
574 dom_spawnteam("Blue", NUM_TEAM_2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
575 if(domination_teams >= 3)
576 dom_spawnteam("Yellow", NUM_TEAM_3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
577 if(domination_teams >= 4)
578 dom_spawnteam("Pink", NUM_TEAM_4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
579 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
582 void dom_DelayedInit() // Do this check with a delay so we can wait for teams to be set up.
584 // if no teams are found, spawn defaults
585 if(find(world, classname, "dom_team") == world || autocvar_g_domination_teams_override >= 2)
587 print("No ""dom_team"" entities found on this map, creating them anyway.\n");
591 domination_roundbased = autocvar_g_domination_roundbased;
595 if(domination_roundbased)
597 round_handler_Spawn(Domination_CheckPlayers, Domination_CheckWinner, Domination_RoundStart);
598 round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit);
602 void dom_Initialize()
604 precache_model("models/domination/dom_red.md3");
605 precache_model("models/domination/dom_blue.md3");
606 precache_model("models/domination/dom_yellow.md3");
607 precache_model("models/domination/dom_pink.md3");
608 precache_model("models/domination/dom_unclaimed.md3");
609 precache_sound("domination/claim.wav");
611 domination_teams = ((autocvar_g_domination_teams_override < 2) ? autocvar_g_domination_default_teams : autocvar_g_domination_teams_override);
612 domination_teams = ((c4>=0) ? 4 : (c3>=0) ? 3 : 2);
614 addstat(STAT_DOM_TOTAL_PPS, AS_FLOAT, dom_total_pps);
615 addstat(STAT_DOM_PPS_RED, AS_FLOAT, dom_pps_red);
616 addstat(STAT_DOM_PPS_BLUE, AS_FLOAT, dom_pps_blue);
617 if(domination_teams >= 3) addstat(STAT_DOM_PPS_YELLOW, AS_FLOAT, dom_pps_yellow);
618 if(domination_teams >= 4) addstat(STAT_DOM_PPS_PINK, AS_FLOAT, dom_pps_pink);
620 InitializeEntity(world, dom_DelayedInit, INITPRIO_GAMETYPE);
624 MUTATOR_DEFINITION(gamemode_domination)
626 MUTATOR_HOOK(reset_map_players, dom_ResetMap, CBC_ORDER_ANY);
627 MUTATOR_HOOK(PlayerSpawn, dom_PlayerSpawn, CBC_ORDER_ANY);
628 MUTATOR_HOOK(ClientConnect, dom_ClientConnect, CBC_ORDER_ANY);
629 MUTATOR_HOOK(HavocBot_ChooseRule, dom_BotRoles, CBC_ORDER_ANY);
633 if(time > 1) // game loads at time 1
634 error("This is a game type and it cannot be added at runtime.");
640 error("This is a game type and it cannot be removed at runtime.");