//============================================================================= /*QUAKED info_player_attacker (1 0 0) (-16 -16 -24) (16 16 45) INITIAL Normal attacker spawning location for Nexuiz Asssault -------- KEYS -------- angle : direction in which player will look when spawning in the game. Does not apply to bots. target : this should point to a target_objective to decide when this spawning point is active. nobots : when set to 1, bots will never use this spawn point to respawn in the game. nohumans : when set to 1, human players will never use this spawn point to respawn in the game. notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notsingle : when set to 1, entity will not spawn in Single Player mode (bot play mode). -------- SPAWNFLAGS -------- INITIAL : makes the spawnpoint the initial place for the player to spawn at the beginning of the game.*/ void info_player_attacker() { info_player_deathmatch(); self.team = COLOR_TEAM1; // red, gets swapped every round } //============================================================================= /*QUAKED info_player_defender (0 1 0) (-16 -16 -24) (16 16 45) INITIAL Normal defender spawning location for Nexuiz Asssault -------- KEYS -------- angle : direction in which player will look when spawning in the game. Does not apply to bots. target : this should point to a target_objective to decide when this spawning point is active. nobots : when set to 1, bots will never use this spawn point to respawn in the game. nohumans : when set to 1, human players will never use this spawn point to respawn in the game. notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. notsingle : when set to 1, entity will not spawn in Single Player mode (bot play mode). -------- SPAWNFLAGS -------- INITIAL : makes the spawnpoint the initial place for the player to spawn at the beginning of the game.*/ void info_player_defender() { info_player_deathmatch(); self.team = COLOR_TEAM2; // blue, gets swapped every round } // reset this objective. Used when spawning an objective // and when a new round starts void assault_objective_reset() { self.health = ASSAULT_VALUE_INACTIVE; } void assault_objective_use() { // activate objective self.health = 100; self.nextthink = time + 0.1; } void assault_objective_think() { local entity oldself; if(self.health < 0) { //self.effects = 0; local entity ent; ent = find(world, targetname, self.target); while(ent) { oldself = self; self = ent; self.use(); self = oldself; ent = find(ent, targetname, self.target); } } else { //self.effects = EF_STARDUST; self.nextthink = time + 0.1; } } //============================================================================= /*QUAKED target_objective (0 .5 0) (-8 -8 -8) (8 8 8) Objective controller for Nexuiz Assault. When active it has 100 health. If it falls below 0 then it'll trigger the next targeted entity (usually the next objective or target_assault_roundend etc.) -------- KEYS -------- targetname : point to e.g. next objective*/ void target_objective() { self.classname = "target_objective"; self.think = assault_objective_think; self.use = assault_objective_use; assault_objective_reset(); } float assault_objective_decrease_customizeforclient() { if(!self.spawnflags) return FALSE; if(self.cnt == 0) { if(other.team == assault_attacker_team) if(self.spawnflags == 1) setmodel(self, "models/sprites/push.sp2"); else setmodel(self, "models/sprites/destroy.sp2"); else setmodel(self, "models/sprites/defend.sp2"); } else { return FALSE; } return TRUE; } void assault_objective_decrease_think() { local entity objective; local float found; found = 0; objective = find(world, targetname, self.target); while(objective && found == 0) { if(objective.classname == "target_objective") { found = 1; if(objective.health < ASSAULT_VALUE_INACTIVE) { // targeted objective is active if(self.cnt == 1 && self.max_health >= ASSAULT_VALUE_INACTIVE) { // decrease was fired already, but objective did recover (round reset) self.cnt = 0; } } else { // objective isn't active self.cnt = 1; } self.max_health = objective.health; // save current objective status for next think } } if(!self.spawnflags) { local entity ent; ent = find(world, target, self.targetname); if(ent) { if(ent.classname == "func_assault_destructible") self.spawnflags = 2; else self.spawnflags = 1; } } self.nextthink = time + 0.2; } // decrease the health of targeted objectives void assault_objective_decrease_use() { if(self.cnt > 0) return; if(activator.team != assault_attacker_team) return; local entity ent; ent = find(world, targetname, self.target); while(ent) { if(ent.health > 0 && ent.health < ASSAULT_VALUE_INACTIVE) ent.health = ent.health - self.dmg; ent = find(ent, targetname, self.target); } self.cnt = 1; } //============================================================================= /*QUAKED target_objective_decrease (0 .5 0) (-8 -8 -8) (8 8 8) When triggered decreases health of the targeted target_objective. -------- KEYS -------- targetname : point to a target_objective entity*/ void target_objective_decrease() { self.classname = "target_objective_decrease"; precache_model("models/sprites/defend.sp2"); precache_model("models/sprites/destroy.sp2"); precache_model("models/sprites/push.sp2"); if(!self.dmg) { self.dmg = 101; } self.cnt = 0; // not used yet self.use = assault_objective_decrease_use; self.mdl = "models/sprites/here.sp2"; self.effects = EF_NODEPTHTEST; self.health = ASSAULT_VALUE_INACTIVE; self.max_health = ASSAULT_VALUE_INACTIVE; self.think = assault_objective_decrease_think; self.customizeentityforclient = assault_objective_decrease_customizeforclient; self.nextthink = time; } void assault_destructible_reset() { self.health = self.max_health; self.model = self.mdl; self.solid = SOLID_BSP; self.colormod = '1 1 1'; self.cnt = 0; // not active if(self.spawnflags) self.use(); } void assault_destructible_use() { self.cnt = 1; // mark active self.takedamage = DAMAGE_YES; } void assault_destructible_destroy() { local entity oldself; self.model = ""; self.takedamage = DAMAGE_NO; self.solid = SOLID_NOT; local entity ent; ent = find(world, targetname, self.target); while(ent) { oldself = self; self = ent; self.use(); self = oldself; ent = find(ent, targetname, self.target); } } void assault_destructible_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) { if(self.cnt > 0 && assault_attacker_team == attacker.team) { self.health = self.health - damage; if(self.health / self.max_health < 0.25) self.colormod = '1 0 0'; else if(self.health / self.max_health < 0.375) self.colormod = '1 0.25 0'; else if(self.health / self.max_health < 0.50) self.colormod = '1 0.5 0'; else if(self.health / self.max_health < 0.625) self.colormod = '1 0.75 0'; else if(self.health / self.max_health < 0.75) self.colormod = '1 1 0'; else self.colormod = '1 1 1'; } if(self.health < 0) { activator = attacker; assault_destructible_destroy(); } } // destructible walls that can be used to trigger target_objective_decrease void func_assault_destructible() { if(!self.health) self.health = 100; self.max_health = self.health; self.cnt = 0; // not yet activated self.classname = "func_assault_destructible"; self.mdl = self.model; setmodel(self, self.mdl); self.solid = SOLID_BSP; self.use = assault_destructible_use; self.event_damage = assault_destructible_damage; } void assault_wall_think() { local entity ent; local float notvisible; notvisible = 0; ent = find(world, targetname, self.target); while(ent) { if(ent.classname == "target_objective" && ent.health < 0) notvisible = 1; ent = find(ent, targetname, self.target); } if(notvisible) { self.model = ""; self.solid = SOLID_NOT; } else { self.model = self.mdl; self.solid = SOLID_BSP; } self.nextthink = time + 0.2; } void func_assault_wall() { self.classname = "func_assault_wall"; self.mdl = self.model; setmodel(self, self.mdl); self.solid = SOLID_BSP; self.think = assault_wall_think; self.nextthink = time; } // trigger new round // reset objectives, toggle spawnpoints, reset triggers, ... void assault_new_round() { // up round counter self.winning = self.winning + 1; // set end time for next round self.cnt = time + self.health; // swap spawn point teams local entity ent; local entity oldself; // reward attackers for winning the round ent = find(world, classname, "player"); while(ent) { if(ent.team == assault_attacker_team) { UpdateFrags(ent, 10); } ent = find(ent, classname, "player"); } // swap attacker/defender roles if(assault_attacker_team == COLOR_TEAM1) { assault_attacker_team = COLOR_TEAM2; } else { assault_attacker_team = COLOR_TEAM1; } ent = find(world, classname, "info_player_deathmatch"); while (ent) { oldself = self; self = ent; if(self.team == COLOR_TEAM1) { self.team = COLOR_TEAM2; } else { self.team = COLOR_TEAM1; } self = oldself; ent = find(ent, classname, "info_player_deathmatch"); } // reset all objectives ent = find(world, classname, "target_objective"); while (ent) { oldself = self; self = ent; assault_objective_reset(); self = oldself; ent = find(ent, classname, "target_objective"); } // reset all target_object_decrease ent = find(world, classname, "target_objective_decrease"); while (ent) { ent.cnt = 0; ent = find(ent, classname, "target_objective_decrease"); } // reset all func_assault_destructible ent = find(world, classname, "func_assault_destructible"); while (ent) { oldself = self; self = ent; assault_destructible_reset(); self = oldself; ent = find(ent, classname, "func_assault_destructible"); } ent = find(world, classname, "target_assault_roundstart"); while (ent) { oldself = self; self = ent; self.use(); self = oldself; ent = find(ent, classname, "target_assault_roundstart"); } // actually restart round... how to do that? ent = find(world, classname, "player"); while(ent) { oldself = self; self = ent; PutClientInServer(); self = oldself; ent = find(ent, classname, "player"); } } void assault_print_time_warning(string s) { local entity ent; ent = find(world, classname, "player"); while(ent) { centerprint(ent, s); ent = find(ent, classname, "player"); } } void assault_roundend_think() { if(time > self.cnt) assault_new_round(); local float timeleft; timeleft = self.cnt - time; timeleft = ceil(timeleft); // reset time notification if the values don't make sense if(timeleft > self.nextstep) self.nextstep = 9999999; if(timeleft <= 300 && self.nextstep > 300) { assault_print_time_warning("5 minutes left"); self.nextstep = 300; } if(timeleft <= 240 && self.nextstep > 240) { assault_print_time_warning("4 minutes left"); self.nextstep = 240; } if(timeleft <= 180 && self.nextstep > 180) { assault_print_time_warning("3 minutes left"); self.nextstep = 180; } if(timeleft <= 120 && self.nextstep > 120) { assault_print_time_warning("2 minutes left"); self.nextstep = 120; } if(timeleft <= 60 && self.nextstep > 60) { assault_print_time_warning("one minute left"); self.nextstep = 60; } if(timeleft <= 10) { assault_print_time_warning(ftos(timeleft)); } self.nextthink = time + 0.25; } void target_assault_roundend() { if(!self.health) self.health = 300; // 5 minutes self.winning = 0; // round counter self.cnt = time + self.max_health; // time when this round ends self.classname = "target_assault_roundend"; self.nextstep = 9999999; // used to store what time warning was last issued; self.use = assault_new_round; self.think = assault_roundend_think; self.nextthink = time; } void assault_roundstart_use() { local entity ent; local entity oldself; ent = find(world, targetname, self.target); while(ent) { oldself = self; self = ent; self.use(); self = oldself; ent = find(ent, targetname, self.target); } } void target_assault_roundstart() { assault_attacker_team = COLOR_TEAM1; self.classname = "target_assault_roundstart"; self.use = assault_roundstart_use; self.think = assault_roundstart_use; self.nextthink = time + 0.1; }