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;
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 bprint("^3", head.netname, "^3", self.message);
50 bprint(" ^7(", ftos(points), " points every ", ftos(wait_time), " seconds)\n");
52 bprint(" ^7(", ftos(points), " point every ", ftos(wait_time), " seconds)\n");
54 if(self.enemy.playerid == self.enemy_playerid)
55 PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
61 sound(self.enemy, CH_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
63 sound(self, CH_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
64 if (head.noise1 != "")
65 play2all(head.noise1);
67 self.delay = time + wait_time;
70 old_delay = self.delay;
72 self.team = real_team;
76 self.delay = old_delay;
81 case NUM_TEAM_1: msg = "dom-red"; break;
82 case NUM_TEAM_2: msg = "dom-blue"; break;
83 case NUM_TEAM_3: msg = "dom-yellow"; break;
84 case NUM_TEAM_4: msg = "dom-pink"; break;
87 WaypointSprite_UpdateSprites(self.sprite, msg, "", "");
89 total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
90 for(head = world; (head = find(head, classname, "dom_controlpoint")) != world; )
92 if (autocvar_g_domination_point_amt)
93 points = autocvar_g_domination_point_amt;
96 if (autocvar_g_domination_point_rate)
97 wait_time = autocvar_g_domination_point_rate;
99 wait_time = head.wait;
100 switch(head.goalentity.team)
103 pps_red += points/wait_time;
106 pps_blue += points/wait_time;
109 pps_yellow += points/wait_time;
112 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 (self.goalentity.netname != "")
164 if(autocvar_g_domination_point_amt)
165 fragamt = autocvar_g_domination_point_amt;
167 fragamt = self.frags;
168 TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
169 TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
171 // give credit to the individual player, if he is still there
172 if (self.enemy.playerid == self.enemy_playerid)
174 PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
175 PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
185 if (other.classname != "player")
187 if (other.health < 1)
190 if(time < self.captime + 0.3)
193 // only valid teams can claim it
194 head = find(world, classname, "dom_team");
195 while (head && head.team != other.team)
196 head = find(head, classname, "dom_team");
197 if (!head || head.netname == "" || head == self.goalentity)
202 self.team = self.goalentity.team; // this stores the PREVIOUS team!
204 self.cnt = other.team;
205 self.owner = head; // team to switch to after the delay
206 self.dmg_inflictor = other;
209 // self.delay = time + cvar("g_domination_point_capturetime");
210 //self.nextthink = time + cvar("g_domination_point_capturetime");
211 //self.think = dompoint_captured;
213 // go to neutral team in the mean time
214 head = find(world, classname, "dom_team");
215 while (head && head.netname != "")
216 head = find(head, classname, "dom_team");
220 WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
221 WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
222 WaypointSprite_Ping(self.sprite);
224 self.goalentity = head;
225 self.model = head.mdl;
226 self.modelindex = head.dmg;
227 self.skin = head.skin;
229 self.enemy = other; // individual player scoring
230 self.enemy_playerid = other.playerid;
234 void dom_controlpoint_setup()
237 // find the spawnfunc_dom_team representing unclaimed points
238 head = find(world, classname, "dom_team");
239 while(head && head.netname != "")
240 head = find(head, classname, "dom_team");
242 objerror("no spawnfunc_dom_team with netname \"\" found\n");
244 // copy important properties from spawnfunc_dom_team entity
245 self.goalentity = head;
246 setmodel(self, head.mdl); // precision already set
247 self.skin = head.skin;
251 if(self.message == "")
252 self.message = " has captured a control point";
259 float points, waittime;
260 if (autocvar_g_domination_point_amt)
261 points = autocvar_g_domination_point_amt;
264 if (autocvar_g_domination_point_rate)
265 waittime = autocvar_g_domination_point_rate;
267 waittime = self.wait;
269 total_pps += points/waittime;
272 self.t_width = 0.02; // frame animation rate
274 self.t_length = 239; // maximum frame
276 self.think = dompointthink;
277 self.nextthink = time;
278 self.touch = dompointtouch;
279 self.solid = SOLID_TRIGGER;
280 self.flags = FL_ITEM;
281 setsize(self, '-32 -32 -32', '32 32 32');
282 setorigin(self, self.origin + '0 0 20');
285 waypoint_spawnforitem(self);
286 WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite, RADARICON_DOMPOINT, '0 1 1');
289 //go to best items, or control points you don't own
290 void havocbot_role_dom()
292 if(self.deadflag != DEAD_NO)
295 if (self.bot_strategytime < time)
297 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
298 navigation_goalrating_start();
299 havocbot_goalrating_controlpoints(10000, self.origin, 15000);
300 havocbot_goalrating_items(8000, self.origin, 8000);
301 //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
302 //havocbot_goalrating_waypoints(1, self.origin, 1000);
303 navigation_goalrating_end();
307 MUTATOR_HOOKFUNCTION(dom_ClientConnect)
313 MUTATOR_HOOKFUNCTION(dom_BotRoles)
315 self.havocbot_role = havocbot_role_dom;
319 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
320 Control point for Domination gameplay.
322 void spawnfunc_dom_controlpoint()
329 self.think = dom_controlpoint_setup;
330 self.nextthink = time + 0.1;
331 self.reset = dom_controlpoint_setup;
336 self.effects = self.effects | EF_LOWPRECISION;
337 if (autocvar_g_domination_point_fullbright)
338 self.effects |= EF_FULLBRIGHT;
341 /*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
342 Team declaration for Domination gameplay, this allows you to decide what team
343 names and control point models are used in your map.
345 Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
346 can have netname set! The nameless team owns all control points at start.
350 Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
352 Scoreboard color of the team (for example 4 is red and 13 is blue)
354 Model to use for control points owned by this team (for example
355 "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
358 Skin of the model to use (for team skins on a single model)
360 Sound to play when this team captures a point.
361 (this is a localized sound, like a small alarm or other effect)
363 Narrator speech to play when this team captures a point.
364 (this is a global sound, like "Red team has captured a control point")
367 void spawnfunc_dom_team()
369 if(!g_domination || autocvar_g_domination_teams_override >= 2)
374 precache_model(self.model);
375 if (self.noise != "")
376 precache_sound(self.noise);
377 if (self.noise1 != "")
378 precache_sound(self.noise1);
379 self.classname = "dom_team";
380 setmodel(self, self.model); // precision not needed
381 self.mdl = self.model;
382 self.dmg = self.modelindex;
385 // this would have to be changed if used in quakeworld
387 self.team = self.cnt + 1; // WHY are these different anyway?
391 void ScoreRules_dom()
393 float sp_domticks, sp_score;
394 sp_score = sp_domticks = 0;
395 if(autocvar_g_domination_disable_frags)
396 sp_domticks = SFL_SORT_PRIO_PRIMARY;
398 sp_score = SFL_SORT_PRIO_PRIMARY;
399 CheckAllowedTeams(world);
400 ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), sp_score, sp_score, TRUE);
401 ScoreInfo_SetLabel_TeamScore (ST_DOM_TICKS, "ticks", sp_domticks);
402 ScoreInfo_SetLabel_PlayerScore(SP_DOM_TICKS, "ticks", sp_domticks);
403 ScoreInfo_SetLabel_PlayerScore(SP_DOM_TAKES, "takes", 0);
404 ScoreRules_basics_end();
407 // code from here on is just to support maps that don't have control point and team entities
408 void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
413 self.classname = "dom_team";
414 self.netname = teamname;
415 self.cnt = teamcolor;
416 self.model = pointmodel;
417 self.skin = pointskin;
418 self.noise = capsound;
419 self.noise1 = capnarration;
420 self.message = capmessage;
422 // this code is identical to spawnfunc_dom_team
423 setmodel(self, self.model); // precision not needed
424 self.mdl = self.model;
425 self.dmg = self.modelindex;
428 // this would have to be changed if used in quakeworld
429 self.team = self.cnt + 1;
435 void dom_spawnpoint(vector org)
440 self.classname = "dom_controlpoint";
441 self.think = spawnfunc_dom_controlpoint;
442 self.nextthink = time;
443 setorigin(self, org);
444 spawnfunc_dom_controlpoint();
448 // spawn some default teams if the map is not set up for domination
449 void dom_spawnteams()
451 float numteams = ((autocvar_g_domination_teams_override < 2) ? autocvar_g_domination_default_teams : autocvar_g_domination_teams_override);
453 dom_spawnteam("Red", NUM_TEAM_1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
454 dom_spawnteam("Blue", NUM_TEAM_2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
456 dom_spawnteam("Yellow", NUM_TEAM_3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
458 dom_spawnteam("Pink", NUM_TEAM_4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
459 dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
462 void dom_DelayedInit() // Do this check with a delay so we can wait for teams to be set up.
464 // if no teams are found, spawn defaults
465 if(find(world, classname, "dom_team") == world || autocvar_g_domination_teams_override >= 2)
467 print("No ""dom_team"" entities found on this map, creating them anyway.\n");
474 void dom_Initialize()
476 precache_model("models/domination/dom_red.md3");
477 precache_model("models/domination/dom_blue.md3");
478 precache_model("models/domination/dom_yellow.md3");
479 precache_model("models/domination/dom_pink.md3");
480 precache_model("models/domination/dom_unclaimed.md3");
481 precache_sound("domination/claim.wav");
483 addstat(STAT_DOM_TOTAL_PPS, AS_FLOAT, dom_total_pps);
484 addstat(STAT_DOM_PPS_RED, AS_FLOAT, dom_pps_red);
485 addstat(STAT_DOM_PPS_BLUE, AS_FLOAT, dom_pps_blue);
486 if(c3 >= 0) addstat(STAT_DOM_PPS_YELLOW, AS_FLOAT, dom_pps_yellow);
487 if(c4 >= 0) addstat(STAT_DOM_PPS_PINK, AS_FLOAT, dom_pps_pink);
489 InitializeEntity(world, dom_DelayedInit, INITPRIO_GAMETYPE);
493 MUTATOR_DEFINITION(gamemode_domination)
495 MUTATOR_HOOK(ClientConnect, dom_ClientConnect, CBC_ORDER_ANY);
496 MUTATOR_HOOK(HavocBot_ChooseRule, dom_BotRoles, CBC_ORDER_ANY);
500 if(time > 1) // game loads at time 1
501 error("This is a game type and it cannot be added at runtime.");
507 error("This is a game type and it cannot be removed at runtime.");