self.nextthink = time + self.respawntime;
setorigin(self, self.pos1);
self.angles = self.pos2;
- self.health = 0;
+ self.health = self.max_health; // TODO: check if resetting to max_health is wise here
return;
}
self.think = SUB_Remove;
if(self.enemy.health <= 0 || (!autocvar_g_monsters_typefrag && self.enemy.BUTTON_CHAT))
self.enemy = world;
+ if not(self.enemy.takedamage)
+ self.enemy = world;
+
if not(self.enemy)
self.enemy = FindTarget(self);
self.target2 = "";
SUB_UseTargets();
- self.monster_die();
+ self.monster_die();
+
+ frag_attacker = attacker;
+ frag_target = self;
+ MUTATOR_CALLHOOK(MonsterDies);
}
}
self.realowner.monstercount -= 1;
totalspawned -= 1;
-
- MUTATOR_CALLHOOK(MonsterDies);
}
// used to hook into monster post spawn functions without a mutator
void monster_hook_spawn()
{
- self.health *= monster_skill; // skill based monster health?
self.max_health = self.health;
if(teamplay && autocvar_g_monsters_teams)
return FALSE;
// support for quake style removing monsters based on skill
- if(autocvar_skill <= autocvar_g_monsters_skill_easy && (self.spawnflags & MONSTERSKILL_NOTEASY)) { return FALSE; }
- else if(autocvar_skill == autocvar_g_monsters_skill_normal && (self.spawnflags & MONSTERSKILL_NOTMEDIUM)) { return FALSE; }
- else if(autocvar_skill == autocvar_g_monsters_skill_hard && (self.spawnflags & MONSTERSKILL_NOTHARD)) { return FALSE; }
- else if(autocvar_skill == autocvar_g_monsters_skill_insane && (self.spawnflags & MONSTERSKILL_NOTINSANE)) { return FALSE; }
- else if(autocvar_skill >= autocvar_g_monsters_skill_nightmare && (self.spawnflags & MONSTERSKILL_NOTNIGHTMARE)) { return FALSE; }
+ if(monster_skill <= autocvar_g_monsters_skill_easy && (self.spawnflags & MONSTERSKILL_NOTEASY)) { return FALSE; }
+ if(monster_skill == autocvar_g_monsters_skill_normal && (self.spawnflags & MONSTERSKILL_NOTMEDIUM)) { return FALSE; }
+ if(monster_skill == autocvar_g_monsters_skill_hard && (self.spawnflags & MONSTERSKILL_NOTHARD)) { return FALSE; }
+ if(monster_skill == autocvar_g_monsters_skill_insane && (self.spawnflags & MONSTERSKILL_NOTINSANE)) { return FALSE; }
+ if(monster_skill >= autocvar_g_monsters_skill_nightmare && (self.spawnflags & MONSTERSKILL_NOTNIGHTMARE)) { return FALSE; }
if(self.model == "")
if(bodymodel == "")
spawnfunc_turret_fusionreactor();
self.classname = "healing_tower";
self.target_range = 1000;
- self.shot_dmg = 20;
+ self.shot_dmg = 30;
+}
+
+void rts_waypoint_think()
+{
+ float goalcount = 0;
+ entity e;
+
+ self.nextthink = time + 0.1;
+
+ for(e = world; (e = findentity(e, goalentity, self)); )
+ {
+ ++goalcount;
+ }
+
+ if(goalcount < 1)
+ {
+ WaypointSprite_Kill(self.sprite);
+ remove(self);
+ return;
+ }
+}
+
+void Monster_LevelUp(entity e)
+{
+ if(self.level >= 5)
+ return; // max level is 5 for now
+ e.speed += 0.25;
+ e.max_health += 20;
+ e.health = e.max_health;
+ e.level += 1;
+ WaypointSprite_UpdateHealth(e.sprite, e.health);
}
MUTATOR_HOOKFUNCTION(rts_PlayerSpawn)
{
+ if(self.rts_viewangle)
+ self.angles_x = self.rts_viewangle;
+ else
+ self.angles_x = 30;
+
self.effects |= EF_NODRAW;
self.oldorigin = self.origin;
self.monster_attack = FALSE;
self.takedamage = DAMAGE_NO;
self.flags |= FL_NOTARGET;
self.movetype = MOVETYPE_NOCLIP;
- self.angles_x = 30;
stuffcmd(self, "cl_cmd settemp cl_prydoncursor 1\n");
return FALSE;
}
self.oldorigin_z -= 50;
break;
}
- self.hasweapon_complain_spam = time + 99999; // no spam
+ self.hasweapon_complain_spam = time + 9999999999; // no spam
- entity head, w, wp = world;
+ entity head, wp = world;
if(!self.cursor_trace_ent && self.BUTTON_ATCK && time >= self.last_click)
{
FOR_EACH_MONSTER(head)
{
- if(head.goalentity) head.goalentity.selected = TRUE; // do this to make sure we're not removing any owned waypoints below
-
if(head.owner != self) continue;
-
+
head.selected = FALSE;
if(!self.enemy)
head.owner = world;
}
-
- for(w = world; (w = find(w, classname, "monster_waypoint")); )
- {
- if not(w.selected)
- remove(w);
- }
}
if(self.cursor_trace_ent.flags & FL_MONSTER && self.BUTTON_ATCK && time >= self.last_click)
{
}
if(self.BUTTON_ATCK2)
{
- entity e = world;
- if(self.cursor_trace_ent) e = self.cursor_trace_ent;
- else e = findradius(self.cursor_trace_endpos, 50);
-
- if not(IsDifferentTeam(e, self))
- e = world; // same team
+ entity e = self.cursor_trace_ent;
- if not(e.takedamage)
- e = world; // can't hurt this enemy
+ if(e)
+ if not(IsDifferentTeam(e, self) || e.takedamage)
+ e = world;
- if(e == world)
+ if not(e)
{
wp = spawn();
wp.classname = "monster_waypoint"; // set so we can kill this later
+ wp.owner = self; // hmm...
+ wp.think = rts_waypoint_think;
+ wp.nextthink = time;
+ WaypointSprite_Spawn("Waypoint", 0, 0, wp, '0 0 10', world, 0, wp, sprite, FALSE, RADARICON_DANGER, ((teamplay) ? TeamColor(self.team) : '1 0 0'));
setorigin(wp, self.cursor_trace_endpos);
}
if(e)
{
- remove(head.goalentity);
head.goalentity = world;
head.enemy = e;
}
- else if(wp)
+ else
{
head.goalentity = wp;
head.enemy = world;
MUTATOR_HOOKFUNCTION(rts_MonsterSpawn)
{
- self.respawntime = 10; // default to 5 seconds for now
+ // new monster
+ if not(self.monster_respawned)
+ {
+ self.level = 0;
+ self.speed = 1;
+ }
+
+ self.spawnflags = MONSTERFLAG_NORESPAWN;
+
+ self.goalentity = world;
+ self.enemy = world;
+ self.moveto = self.origin;
+
+ self.respawntime = 10; // default to 10 seconds for now
self.effects |= EF_SELECTABLE;
self.monster_moveflags = MONSTER_MOVE_NOMOVE;
self.heal_delay = time + 2;
}
- monster_speed_run = 150;
- monster_speed_walk = 150;
+ monster_speed_run = 150 * self.speed;
+ monster_speed_walk = 150 * self.speed;
+
+ if(monster_target.classname == "player")
+ monster_target = world;
if(self.selected)
self.colormod = color * 4;
else
self.colormod = color;
-
- if(self.goalentity)
+
+ if(monster_target)
self.enemy = world; // don't ignore our owner's commands
- if(!self.sprite)
+ if not(self.sprite)
{
- 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'));
+ 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'));
WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
WaypointSprite_UpdateHealth(self.sprite, self.health);
}
- if(!self.selected)
if(self.owner)
+ if not(self.selected)
self.owner = world;
- if(!IsDifferentTeam(self, self.enemy))
+ if not(IsDifferentTeam(self, self.enemy))
self.enemy = world; // no same team fighting
-
- if(!self.goalentity && !self.enemy && vlen(self.velocity) > 64) // wtf
- self.moveto = self.origin;
self.last_trace = time; // realtime moving?
MUTATOR_HOOKFUNCTION(rts_MonsterDies)
{
- float otherteam = ((self.team == COLOR_TEAM1) ? COLOR_TEAM2 : COLOR_TEAM1);
+ if(IsDifferentTeam(frag_attacker, frag_target) && frag_attacker.team)
+ TeamScore_AddToTeam(frag_attacker.team, ST_SCORE, 1);
- TeamScore_AddToTeam(otherteam, ST_SCORE, 1);
+ // need to keep the monster selected to get the points... hmm (TODO: realowners?)
+ if(frag_attacker.owner.classname == "player")
+ {
+ PlayerScore_Add(frag_attacker.owner, SP_SCORE, 5);
+ PlayerScore_Add(frag_attacker.owner, SP_KILLS, 1);
+ }
+
+ if(frag_attacker.flags & FL_MONSTER)
+ {
+ frag_attacker.monster_score += 5;
+ if(frag_attacker.monster_score == 25)
+ Monster_LevelUp(frag_attacker);
+ }
self.effects &~= EF_SELECTABLE;
self.selected = FALSE;
- if(self.goalentity)
- {
- remove(self.goalentity);
- self.goalentity = world;
- }
+
+ self.goalentity = world;
+ self.enemy = world;
return FALSE;
}
FOR_EACH_MONSTER(head)
{
if(head.owner != self) continue;
- if(!head.selected) continue;
+ if not(head.selected) continue;
if(IsDifferentTeam(self, head))
{