4 // basically a fusion reactor with a new classname
5 void spawnfunc_healing_tower()
7 self.spawnflags = TSL_NO_RESPAWN; // healing towers don't respawn?
8 self.netname = "Monster Healing Tower"; // not used by waypoints...
9 spawnfunc_turret_fusionreactor();
10 self.classname = "healing_tower";
11 self.target_range = 1000;
15 void rts_waypoint_think()
20 self.nextthink = time + 0.1;
22 for(e = world; (e = findentity(e, goalentity, self)); )
29 WaypointSprite_Kill(self.sprite);
35 void Monster_LevelUp(entity e)
38 return; // max level is 5 for now
41 e.health = e.max_health;
43 WaypointSprite_UpdateHealth(e.sprite, e.health);
46 MUTATOR_HOOKFUNCTION(rts_PlayerSpawn)
48 if(self.rts_viewangle)
49 self.angles_x = self.rts_viewangle;
53 self.effects |= EF_NODRAW;
54 self.oldorigin = self.origin;
55 self.monster_attack = FALSE;
56 self.last_click = time;
57 self.takedamage = DAMAGE_NO;
58 self.flags |= FL_NOTARGET;
59 self.movetype = MOVETYPE_NOCLIP;
60 stuffcmd(self, "cl_cmd settemp cl_prydoncursor 1\n");
64 MUTATOR_HOOKFUNCTION(rts_FilterItem)
70 MUTATOR_HOOKFUNCTION(rts_SetStartItems)
72 WEPSET_COPY_AW(start_weapons, 0);
77 MUTATOR_HOOKFUNCTION(rts_PlayerThink)
79 if(self.classname != "player")
80 return FALSE; // dont do any checks for spectators
87 self.oldorigin_z += 50;
92 self.oldorigin_z -= 50;
95 self.hasweapon_complain_spam = time + 9999999999; // no spam
97 entity head, wp = world;
98 if(!self.cursor_trace_ent && self.BUTTON_ATCK && time >= self.last_click)
100 FOR_EACH_MONSTER(head)
102 if(head.owner != self) continue;
104 head.selected = FALSE;
110 if(self.cursor_trace_ent.flags & FL_MONSTER && self.BUTTON_ATCK && time >= self.last_click)
112 if(self.cursor_trace_ent.owner != self && self.cursor_trace_ent.owner != world)
113 return FALSE; // someone else owns it
114 else if(self.cursor_trace_ent.team != self.team)
115 return FALSE; // not our team
116 else if(self.cursor_trace_ent.selected)
118 self.cursor_trace_ent.selected = FALSE;
119 self.cursor_trace_ent.owner = world;
120 self.last_click = time + 0.5; // prevent spamming
124 self.cursor_trace_ent.owner = self;
125 self.cursor_trace_ent.selected = TRUE;
126 self.last_click = time + 0.5; // prevent spamming
129 if(self.BUTTON_ATCK2)
131 entity e = self.cursor_trace_ent;
136 for(t = world; (t = findflags(t, turrcaps_flags, TFL_TURRCAPS_ISTURRET)); )
138 if(vlen(self.cursor_trace_endpos - t.origin) < 80)
140 if(IsDifferentTeam(e, t))
143 break; // don't bother checking any other turrets
156 wp.classname = "monster_waypoint"; // set so we can kill this later
157 wp.owner = self; // hmm...
158 wp.think = rts_waypoint_think;
160 WaypointSprite_Spawn("Here", 1, 0, wp, '0 0 10', world, self.team, wp, sprite, FALSE, RADARICON_DANGER, ((teamplay) ? TeamColor(self.team) : '1 0 0'));
161 setorigin(wp, self.cursor_trace_endpos);
164 FOR_EACH_MONSTER(head)
166 if(head.owner != self) continue;
167 if not(head.selected) continue;
171 float sheight = ((e.sprite_height) ? e.sprite_height + 20 : 80);
172 if(IsDifferentTeam(e, self))
174 WaypointSprite_Spawn("Attacking", 1, 0, e, '0 0 1' * sheight, world, self.team, self, sprite, FALSE, RADARICON_DANGER, ((teamplay) ? TeamColor(self.team) : '1 0 0'));
175 head.goalentity = world;
178 else if(e.flags & FL_MONSTER)
180 WaypointSprite_Spawn("Following", 1, 0, e, '0 0 1' * sheight, world, self.team, self, sprite, FALSE, RADARICON_DANGER, ((teamplay) ? TeamColor(self.team) : '1 0 0'));
183 else // its not a monster or an enemy, so revert to waypoint
185 head.goalentity = wp;
192 head.goalentity = wp;
201 MUTATOR_HOOKFUNCTION(rts_MonsterSpawn)
204 if not(self.monster_respawned)
210 self.spawnflags = MONSTERFLAG_NORESPAWN;
212 self.goalentity = world;
214 self.moveto = self.origin;
216 self.respawntime = 10; // default to 10 seconds for now
217 self.effects |= EF_SELECTABLE;
218 self.monster_moveflags = MONSTER_MOVE_NOMOVE;
220 WaypointSprite_Kill(self.sprite);
222 self.heal_delay = -1; // this is reset when monster takes damage
227 MUTATOR_HOOKFUNCTION(rts_MonsterThink)
229 vector color = ((self.team) ? TeamColor(self.team) : '1 1 1');
231 if(self.health >= self.max_health)
232 self.heal_delay = -1;
233 else if(time >= self.heal_delay)
235 self.health = min(self.health + 5, self.max_health);
236 WaypointSprite_UpdateHealth(self.sprite, self.health);
237 self.heal_delay = time + 2;
240 monster_speed_run = 150 * self.speed;
241 monster_speed_walk = 150 * self.speed;
243 if(monster_target.classname == "player")
244 monster_target = world;
246 if not(IsDifferentTeam(monster_target, self))
248 // following a fellow teammate, so attack their enemy
249 if(monster_target.deadflag != DEAD_NO || monster_target.health < 1)
250 monster_target = world; // teammate died
252 if(monster_target.enemy)
254 self.enemy = monster_target.enemy;
255 monster_target = world; // don't follow anymore?
260 self.colormod = color * 10;
262 self.colormod = color;
265 self.enemy = world; // don't ignore our owner's commands
269 WaypointSprite_Spawn(self.netname, 0, 0, self, '0 0 1' * self.sprite_height, world, self.team, self, sprite, FALSE, RADARICON_DANGER, ((teamplay) ? TeamColor(self.team) : '1 0 0'));
270 WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
271 WaypointSprite_UpdateHealth(self.sprite, self.health);
275 if not(self.selected)
278 if not(IsDifferentTeam(self, self.enemy))
279 self.enemy = world; // no same team fighting
281 self.last_trace = time; // realtime moving?
286 MUTATOR_HOOKFUNCTION(rts_MonsterDies)
290 if(IsDifferentTeam(frag_attacker, frag_target) && frag_attacker.team)
291 TeamScore_AddToTeam(frag_attacker.team, ST_SCORE, 1);
293 // need to keep the monster selected to get the points... hmm (TODO: realowners?)
294 if(frag_attacker.owner.classname == "player")
296 PlayerScore_Add(frag_attacker.owner, SP_SCORE, 5);
297 PlayerScore_Add(frag_attacker.owner, SP_KILLS, 1);
300 if(frag_attacker.flags & FL_MONSTER)
302 frag_attacker.monster_score += 5;
303 if(frag_attacker.monster_score == 25)
304 Monster_LevelUp(frag_attacker);
307 for(e = world; (e = findentity(e, goalentity, self)); )
309 e.goalentity = world; // fix teammates if they still see us as a valid target
312 self.effects &~= EF_SELECTABLE;
313 self.selected = FALSE;
315 self.goalentity = world;
321 MUTATOR_HOOKFUNCTION(rts_MonsterRespawn)
326 return FALSE; // if no team is set, don't respawn
329 MUTATOR_HOOKFUNCTION(rts_MonsterTarget)
331 // don't search for enemies, they are given to us
335 MUTATOR_HOOKFUNCTION(rts_MonsterBossFlag)
337 // no minibosses in RTS
341 MUTATOR_HOOKFUNCTION(rts_PlayerDamage)
343 if(frag_target.classname == "player")
344 frag_damage = 0; // don't damage the invincible players...
346 if((frag_target.flags & FL_MONSTER) && frag_target.goalentity)
347 frag_target.enemy = world; // don't attack the attacker, we're probably pulling back
349 if((frag_target.flags & FL_MONSTER) && !IsDifferentTeam(frag_target, frag_attacker))
350 frag_damage = 0; // no team damage
352 if((frag_target.flags & FL_MONSTER) && frag_damage > 0)
353 frag_target.heal_delay = time + 2; // reset delay whenever hurt
358 MUTATOR_HOOKFUNCTION(rts_PlayerPhysics)
360 if(self.classname != "player")
363 self.origin_z = self.oldorigin_z;
364 self.stat_sv_maxspeed *= 4; // lol
369 MUTATOR_HOOKFUNCTION(rts_PlayerDies)
371 // prevent changing teams with selected monsters
373 FOR_EACH_MONSTER(head)
375 if(head.owner != self) continue;
376 if not(head.selected) continue;
378 if(IsDifferentTeam(self, head))
380 head.selected = FALSE;
388 void rts_ScoreRules()
390 ScoreRules_basics(2, SFL_SORT_PRIO_PRIMARY, 0, TRUE);
391 ScoreRules_basics_end();
394 void rts_DelayedInit()
399 void rts_Initialize()
401 InitializeEntity(world, rts_DelayedInit, INITPRIO_GAMETYPE);
404 MUTATOR_DEFINITION(gamemode_rts)
406 MUTATOR_HOOK(PlayerPhysics, rts_PlayerPhysics, CBC_ORDER_ANY);
407 MUTATOR_HOOK(PlayerSpawn, rts_PlayerSpawn, CBC_ORDER_ANY);
408 MUTATOR_HOOK(SetStartItems, rts_SetStartItems, CBC_ORDER_ANY);
409 MUTATOR_HOOK(FilterItem, rts_FilterItem, CBC_ORDER_ANY);
410 MUTATOR_HOOK(MonsterSpawn, rts_MonsterSpawn, CBC_ORDER_ANY);
411 MUTATOR_HOOK(PlayerPreThink, rts_PlayerThink, CBC_ORDER_ANY);
412 MUTATOR_HOOK(MonsterMove, rts_MonsterThink, CBC_ORDER_ANY);
413 MUTATOR_HOOK(MonsterFindTarget, rts_MonsterTarget, CBC_ORDER_ANY);
414 MUTATOR_HOOK(MonsterDies, rts_MonsterDies, CBC_ORDER_ANY);
415 MUTATOR_HOOK(MonsterRespawn, rts_MonsterRespawn, CBC_ORDER_ANY);
416 MUTATOR_HOOK(MonsterCheckBossFlag, rts_MonsterBossFlag, CBC_ORDER_ANY);
417 MUTATOR_HOOK(PlayerDamage_Calculate, rts_PlayerDamage, CBC_ORDER_ANY);
418 MUTATOR_HOOK(PlayerDies, rts_PlayerDies, CBC_ORDER_ANY);
422 if(time > 1) // game loads at time 1
423 error("This is a game type and it cannot be added at runtime.");
424 cvar_settemp("g_monsters", "1");
431 error("This is a game type and it cannot be removed at runtime.");