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;
13 e.dom_pps_yellow = pps_yellow;
15 e.dom_pps_pink = pps_pink;
18 void dompoint_captured ()
21 float old_delay, old_team, real_team;
23 // now that the delay has expired, switch to the latest team to lay claim to this point
29 dom_EventLog("taken", self.team, self.dmg_inflictor);
30 self.dmg_inflictor = world;
32 self.goalentity = head;
33 self.model = head.mdl;
34 self.modelindex = head.dmg;
35 self.skin = head.skin;
37 float points, wait_time;
38 if (autocvar_g_domination_point_amt)
39 points = autocvar_g_domination_point_amt;
42 if (autocvar_g_domination_point_rate)
43 wait_time = autocvar_g_domination_point_rate;
45 wait_time = self.wait;
47 bprint("^3", head.netname, "^3", self.message);
49 bprint(" ^7(", ftos(points), " points every ", ftos(wait_time), " seconds)\n");
51 bprint(" ^7(", ftos(points), " point every ", ftos(wait_time), " seconds)\n");
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, ATTN_NORM);
62 sound(self, CH_TRIGGER, head.noise, VOL_BASE, ATTN_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;
78 switch(self.goalentity.team)
81 WaypointSprite_UpdateSprites(self.sprite, "dom-red", "", "");
84 WaypointSprite_UpdateSprites(self.sprite, "dom-blue", "", "");
87 WaypointSprite_UpdateSprites(self.sprite, "dom-yellow", "", "");
90 WaypointSprite_UpdateSprites(self.sprite, "dom-pink", "", "");
93 total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
94 for(head = world; (head = find(head, classname, "dom_controlpoint")) != world; )
96 if (autocvar_g_domination_point_amt)
97 points = autocvar_g_domination_point_amt;
100 if (autocvar_g_domination_point_rate)
101 wait_time = autocvar_g_domination_point_rate;
103 wait_time = head.wait;
104 switch(head.goalentity.team)
107 pps_red += points/wait_time;
110 pps_blue += points/wait_time;
113 pps_yellow += points/wait_time;
116 pps_pink += points/wait_time;
118 total_pps += points/wait_time;
121 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
122 WaypointSprite_Ping(self.sprite);
126 FOR_EACH_REALCLIENT(head)
130 void AnimateDomPoint()
132 if(self.pain_finished > time)
134 self.pain_finished = time + self.t_width;
135 if(self.nextthink > self.pain_finished)
136 self.nextthink = self.pain_finished;
138 self.frame = self.frame + 1;
139 if(self.frame > self.t_length)
147 self.nextthink = time + 0.1;
149 //self.frame = self.frame + 1;
150 //if(self.frame > 119)
156 if (gameover || self.delay > time || time < game_starttime) // game has ended, don't keep giving points
159 if(autocvar_g_domination_point_rate)
160 self.delay = time + autocvar_g_domination_point_rate;
162 self.delay = time + self.wait;
164 // give credit to the team
165 // NOTE: this defaults to 0
166 if (self.goalentity.netname != "")
168 if(autocvar_g_domination_point_amt)
169 fragamt = autocvar_g_domination_point_amt;
171 fragamt = self.frags;
172 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
173 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
175 // give credit to the individual player, if he is still there
176 if (self.enemy.playerid == self.enemy_playerid)
178 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
179 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
189 if (other.classname != "player")
191 if (other.health < 1)
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 //go to best items, or control points you don't own
294 void havocbot_role_dom()
296 if(self.deadflag != DEAD_NO)
299 if (self.bot_strategytime < time)
301 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
302 navigation_goalrating_start();
303 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
304 havocbot_goalrating_items(8000, self.origin, 8000);
305 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
306 //havocbot_goalrating_waypoints(1, self.origin, 1000);
307 navigation_goalrating_end();
311 MUTATOR_HOOKFUNCTION(dom_ClientConnect)
317 MUTATOR_HOOKFUNCTION(dom_BotRoles)
319 self.havocbot_role = havocbot_role_dom;
323 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
324 Control point for Domination gameplay.
326 void spawnfunc_dom_controlpoint()
333 self.think = dom_controlpoint_setup;
334 self.nextthink = time + 0.1;
335 self.reset = dom_controlpoint_setup;
340 self.effects = self.effects | EF_LOWPRECISION;
341 if (autocvar_g_domination_point_fullbright)
342 self.effects |= EF_FULLBRIGHT;
345 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
346 Team declaration for Domination gameplay, this allows you to decide what team
347 names and control point models are used in your map.
349 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
350 can have netname set! The nameless team owns all control points at start.
354 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
356 Scoreboard color of the team (for example 4 is red and 13 is blue)
358 Model to use for control points owned by this team (for example
359 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
362 Skin of the model to use (for team skins on a single model)
364 Sound to play when this team captures a point.
365 (this is a localized sound, like a small alarm or other effect)
367 Narrator speech to play when this team captures a point.
368 (this is a global sound, like "Red team has captured a control point")
371 void spawnfunc_dom_team()
373 if(!g_domination || autocvar_g_domination_teams_override >= 2)
378 precache_model(self.model);
379 if (self.noise != "")
380 precache_sound(self.noise);
381 if (self.noise1 != "")
382 precache_sound(self.noise1);
383 self.classname = "dom_team";
384 setmodel(self, self.model); // precision not needed
385 self.mdl = self.model;
386 self.dmg = self.modelindex;
389 // this would have to be changed if used in quakeworld
391 self.team = self.cnt + 1; // WHY are these different anyway?
395 void ScoreRules_dom()
397 float sp_domticks, sp_score;
398 sp_score = sp_domticks = 0;
399 if(autocvar_g_domination_disable_frags)
400 sp_domticks = SFL_SORT_PRIO_PRIMARY;
402 sp_score = SFL_SORT_PRIO_PRIMARY;
403 CheckAllowedTeams(world);
404 ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), sp_score, sp_score, TRUE);
405 ScoreInfo_SetLabel_TeamScore (ST_DOM_TICKS, "ticks", sp_domticks);
406 ScoreInfo_SetLabel_PlayerScore(SP_DOM_TICKS, "ticks", sp_domticks);
407 ScoreInfo_SetLabel_PlayerScore(SP_DOM_TAKES, "takes", 0);
408 ScoreRules_basics_end();
411 // code from here on is just to support maps that don't have control point and team entities
412 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
417 self.classname = "dom_team";
418 self.netname = teamname;
419 self.cnt = teamcolor;
420 self.model = pointmodel;
421 self.skin = pointskin;
422 self.noise = capsound;
423 self.noise1 = capnarration;
424 self.message = capmessage;
426 // this code is identical to spawnfunc_dom_team
427 setmodel(self, self.model); // precision not needed
428 self.mdl = self.model;
429 self.dmg = self.modelindex;
432 // this would have to be changed if used in quakeworld
433 self.team = self.cnt + 1;
439 void dom_spawnpoint(vector org)
444 self.classname = "dom_controlpoint";
445 self.think = spawnfunc_dom_controlpoint;
446 self.nextthink = time;
447 setorigin(self, org);
448 spawnfunc_dom_controlpoint();
452 // spawn some default teams if the map is not set up for domination
453 void dom_spawnteams()
455 float numteams = ((autocvar_g_domination_teams_override < 2) ? autocvar_g_domination_default_teams : autocvar_g_domination_teams_override);
457 dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
458 dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
460 dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
462 dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
463 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
466 void dom_DelayedInit() // Do this check with a delay so we can wait for teams to be set up.
468 // if no teams are found, spawn defaults
469 if(find(world, classname, "dom_team") == world || autocvar_g_domination_teams_override >= 2)
471 print("No ""dom_team"" entities found on this map, creating them anyway.\n");
478 void dom_Initialize()
480 precache_model("models/domination/dom_red.md3");
481 precache_model("models/domination/dom_blue.md3");
482 precache_model("models/domination/dom_yellow.md3");
483 precache_model("models/domination/dom_pink.md3");
484 precache_model("models/domination/dom_unclaimed.md3");
485 precache_sound("domination/claim.wav");
487 addstat(STAT_DOM_TOTAL_PPS, AS_FLOAT, dom_total_pps);
488 addstat(STAT_DOM_PPS_RED, AS_FLOAT, dom_pps_red);
489 addstat(STAT_DOM_PPS_BLUE, AS_FLOAT, dom_pps_blue);
490 if(c3 >= 0) addstat(STAT_DOM_PPS_YELLOW, AS_FLOAT, dom_pps_yellow);
491 if(c4 >= 0) addstat(STAT_DOM_PPS_PINK, AS_FLOAT, dom_pps_pink);
493 InitializeEntity(world, dom_DelayedInit, INITPRIO_GAMETYPE);
497 MUTATOR_DEFINITION(gamemode_domination)
499 MUTATOR_HOOK(ClientConnect, dom_ClientConnect, CBC_ORDER_ANY);
500 MUTATOR_HOOK(HavocBot_ChooseRule, dom_BotRoles, CBC_ORDER_ANY);
504 if(time > 1) // game loads at time 1
505 error("This is a game type and it cannot be added at runtime.");
511 error("This is a game type and it cannot be removed at runtime.");