#include "gamemode_assault.qh"
-#ifndef GAMEMODE_ASSAULT_H
-#define GAMEMODE_ASSAULT_H
-void assault_ScoreRules();
-void ActivateTeamplay();
-
-REGISTER_MUTATOR(as, false)
-{
- ActivateTeamplay();
- have_team_spawns = -1; // request team spawns
-
- MUTATOR_ONADD
- {
- if (time > 1) // game loads at time 1
- error("This is a game type and it cannot be added at runtime.");
- assault_ScoreRules();
- }
-
- MUTATOR_ONROLLBACK_OR_REMOVE
- {
- // we actually cannot roll back assault_Initialize here
- // BUT: we don't need to! If this gets called, adding always
- // succeeds.
- }
-
- MUTATOR_ONREMOVE
- {
- LOG_INFO("This is a game type and it cannot be removed at runtime.");
- return -1;
- }
-
- return 0;
-}
-
-// sprites
-.entity assault_decreaser;
-.entity assault_sprite;
-
-// legacy bot defs
-const int HAVOCBOT_AST_ROLE_NONE = 0;
-const int HAVOCBOT_AST_ROLE_DEFENSE = 2;
-const int HAVOCBOT_AST_ROLE_OFFENSE = 4;
-
-.int havocbot_role_flags;
-.float havocbot_attack_time;
-
-.void(entity this) havocbot_role;
-.void(entity this) havocbot_previous_role;
-
-void(entity this) havocbot_role_ast_defense;
-void(entity this) havocbot_role_ast_offense;
-.entity havocbot_ast_target;
-
-void(entity bot) havocbot_ast_reset_role;
-
-void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items;
-void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
-
-// scoreboard stuff
-const float ST_ASSAULT_OBJECTIVES = 1;
-const float SP_ASSAULT_OBJECTIVES = 4;
-
-// predefined spawnfuncs
-void target_objective_decrease_activate();
-#endif
-
-#ifdef IMPLEMENTATION
.entity sprite;
// random functions
-void assault_objective_use()
-{SELFPARAM();
+void assault_objective_use(entity this, entity actor, entity trigger)
+{
// activate objective
- self.health = 100;
- //print("^2Activated objective ", self.targetname, "=", etos(self), "\n");
- //print("Activator is ", activator.classname, "\n");
+ this.health = 100;
+ //print("^2Activated objective ", this.targetname, "=", etos(this), "\n");
+ //print("Activator is ", actor.classname, "\n");
- for (entity e = world; (e = find(e, target, this.targetname)); )
+ IL_EACH(g_assault_objectivedecreasers, it.target == this.targetname,
{
- if (e.classname == "target_objective_decrease")
- {
- WITHSELF(e, target_objective_decrease_activate());
- }
- }
-
- setself(this);
+ target_objective_decrease_activate(it);
+ });
}
-vector target_objective_spawn_evalfunc(entity player, entity spot, vector current)
-{SELFPARAM();
- if(self.health < 0 || self.health >= ASSAULT_VALUE_INACTIVE)
+vector target_objective_spawn_evalfunc(entity this, entity player, entity spot, vector current)
+{
+ if(this.health < 0 || this.health >= ASSAULT_VALUE_INACTIVE)
return '-1 0 0';
return current;
}
}
// decrease the health of targeted objectives
-void assault_objective_decrease_use()
-{SELFPARAM();
- if(activator.team != assault_attacker_team)
+void assault_objective_decrease_use(entity this, entity actor, entity trigger)
+{
+ if(actor.team != assault_attacker_team)
{
// wrong team triggered decrease
return;
}
- if(other.assault_sprite)
+ if(trigger.assault_sprite)
{
- WaypointSprite_Disown(other.assault_sprite, waypointsprite_deadlifetime);
- if(other.classname == "func_assault_destructible")
- other.sprite = world;
+ WaypointSprite_Disown(trigger.assault_sprite, waypointsprite_deadlifetime);
+ if(trigger.classname == "func_assault_destructible")
+ trigger.sprite = NULL; // TODO: just unsetting it?!
}
else
return; // already activated! cannot activate again!
- if(self.enemy.health < ASSAULT_VALUE_INACTIVE)
+ if(this.enemy.health < ASSAULT_VALUE_INACTIVE)
{
- if(self.enemy.health - self.dmg > 0.5)
+ if(this.enemy.health - this.dmg > 0.5)
{
- PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.dmg);
- self.enemy.health = self.enemy.health - self.dmg;
+ PlayerTeamScore_Add(actor, SP_SCORE, ST_SCORE, this.dmg);
+ this.enemy.health = this.enemy.health - this.dmg;
}
else
{
- PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.enemy.health);
- PlayerTeamScore_Add(activator, SP_ASSAULT_OBJECTIVES, ST_ASSAULT_OBJECTIVES, 1);
- self.enemy.health = -1;
+ PlayerTeamScore_Add(actor, SP_SCORE, ST_SCORE, this.enemy.health);
+ PlayerTeamScore_Add(actor, SP_ASSAULT_OBJECTIVES, ST_ASSAULT_OBJECTIVES, 1);
+ this.enemy.health = -1;
- entity oldactivator;
+ if(this.enemy.message)
+ FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(centerprint(it, this.enemy.message)));
- setself(this.enemy);
- if(self.message)
- FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(centerprint(it, self.message)));
-
- oldactivator = activator;
- activator = this;
- SUB_UseTargets();
- activator = oldactivator;
- setself(this);
+ SUB_UseTargets(this.enemy, this, trigger);
}
}
}
void assault_setenemytoobjective(entity this)
{
- entity objective;
- for(objective = world; (objective = find(objective, targetname, self.target)); )
+ IL_EACH(g_assault_objectives, it.targetname == this.target,
{
- if(objective.classname == "target_objective")
- {
- if(self.enemy == world)
- self.enemy = objective;
- else
- objerror("more than one objective as target - fix the map!");
- break;
- }
- }
+ if(this.enemy == NULL)
+ this.enemy = it;
+ else
+ objerror(this, "more than one objective as target - fix the map!");
+ break;
+ });
- if(self.enemy == world)
- objerror("no objective as target - fix the map!");
+ if(this.enemy == NULL)
+ objerror(this, "no objective as target - fix the map!");
}
-float assault_decreaser_sprite_visible(entity e)
-{SELFPARAM();
- entity decreaser;
-
- decreaser = self.assault_decreaser;
-
- if(decreaser.enemy.health >= ASSAULT_VALUE_INACTIVE)
+bool assault_decreaser_sprite_visible(entity this, entity player, entity view)
+{
+ if(this.assault_decreaser.enemy.health >= ASSAULT_VALUE_INACTIVE)
return false;
return true;
}
-void target_objective_decrease_activate()
-{SELFPARAM();
- entity ent, spr;
- self.owner = world;
- for(ent = world; (ent = find(ent, target, self.targetname)); )
+void target_objective_decrease_activate(entity this)
+{
+ entity spr;
+ this.owner = NULL;
+ FOREACH_ENTITY_STRING(target, this.targetname,
{
- if(ent.assault_sprite != world)
+ if(it.assault_sprite != NULL)
{
- WaypointSprite_Disown(ent.assault_sprite, waypointsprite_deadlifetime);
- if(ent.classname == "func_assault_destructible")
- ent.sprite = world;
+ WaypointSprite_Disown(it.assault_sprite, waypointsprite_deadlifetime);
+ if(it.classname == "func_assault_destructible")
+ it.sprite = NULL; // TODO: just unsetting it?!
}
- spr = WaypointSprite_SpawnFixed(WP_Assault, 0.5 * (ent.absmin + ent.absmax), ent, assault_sprite, RADARICON_OBJECTIVE);
- spr.assault_decreaser = self;
+ spr = WaypointSprite_SpawnFixed(WP_Assault, 0.5 * (it.absmin + it.absmax), it, assault_sprite, RADARICON_OBJECTIVE);
+ spr.assault_decreaser = this;
spr.waypointsprite_visible_for_player = assault_decreaser_sprite_visible;
spr.classname = "sprite_waypoint";
WaypointSprite_UpdateRule(spr, assault_attacker_team, SPRITERULE_TEAMPLAY);
- if(ent.classname == "func_assault_destructible")
+ if(it.classname == "func_assault_destructible")
{
WaypointSprite_UpdateSprites(spr, WP_AssaultDefend, WP_AssaultDestroy, WP_AssaultDestroy);
- WaypointSprite_UpdateMaxHealth(spr, ent.max_health);
- WaypointSprite_UpdateHealth(spr, ent.health);
- ent.sprite = spr;
+ WaypointSprite_UpdateMaxHealth(spr, it.max_health);
+ WaypointSprite_UpdateHealth(spr, it.health);
+ it.sprite = spr;
}
else
WaypointSprite_UpdateSprites(spr, WP_AssaultDefend, WP_AssaultPush, WP_AssaultPush);
- }
+ });
}
void target_objective_decrease_findtarget(entity this)
this.winning = false; // up round
}
-void target_assault_roundend_use()
-{SELFPARAM();
- self.winning = 1; // round has been won by attackers
+void target_assault_roundend_use(entity this, entity actor, entity trigger)
+{
+ this.winning = 1; // round has been won by attackers
}
-void assault_roundstart_use(entity this)
+void assault_roundstart_use(entity this, entity actor, entity trigger)
{
- activator = self;
- SUB_UseTargets();
+ SUB_UseTargets(this, this, trigger);
//(Re)spawn all turrets
- FOREACH_ENTITY_CLASS("turret_main", true, LAMBDA(
+ IL_EACH(g_turrets, true,
+ {
// Swap turret teams
if(it.team == NUM_TEAM_1)
it.team = NUM_TEAM_2;
else
it.team = NUM_TEAM_1;
- // Dubbles as teamchange
- WITHSELF(it, turret_respawn());
- ));
+ // Doubles as teamchange
+ turret_respawn(it);
+ });
}
-void assault_roundstart_use_self()
+void assault_roundstart_use_this(entity this)
{
- SELFPARAM();
- assault_roundstart_use(this);
+ assault_roundstart_use(this, NULL, NULL);
}
-void assault_wall_think()
-{SELFPARAM();
- if(self.enemy.health < 0)
+void assault_wall_think(entity this)
+{
+ if(this.enemy.health < 0)
{
- self.model = "";
- self.solid = SOLID_NOT;
+ this.model = "";
+ this.solid = SOLID_NOT;
}
else
{
- self.model = self.mdl;
- self.solid = SOLID_BSP;
+ this.model = this.mdl;
+ this.solid = SOLID_BSP;
}
- self.nextthink = time + 0.2;
+ this.nextthink = time + 0.2;
}
// trigger new round
// reset objectives, toggle spawnpoints, reset triggers, ...
void vehicles_clearreturn(entity veh);
-void vehicles_spawn();
-void assault_new_round()
-{SELFPARAM();
+void vehicles_spawn(entity this);
+void assault_new_round(entity this)
+{
//bprint("ASSAULT: new round\n");
// Eject players from vehicles
- FOREACH_CLIENT(IS_PLAYER(it) && it.vehicle, WITHSELF(it, vehicles_exit(VHEF_RELEASE)));
-
- FOREACH_ENTITY_FLAGS(vehicle_flags, VHF_ISVEHICLE, LAMBDA(
- setself(it);
- vehicles_clearreturn(self);
- vehicles_spawn();
- ));
+ FOREACH_CLIENT(IS_PLAYER(it) && it.vehicle, vehicles_exit(it.vehicle, VHEF_RELEASE));
- setself(this);
+ IL_EACH(g_vehicles, true,
+ {
+ vehicles_clearreturn(it);
+ vehicles_spawn(it);
+ });
// up round counter
- self.winning = self.winning + 1;
+ this.winning = this.winning + 1;
// swap attacker/defender roles
if(assault_attacker_team == NUM_TEAM_1)
else
assault_attacker_team = NUM_TEAM_1;
- FOREACH_ENTITY(IS_NOT_A_CLIENT(it), LAMBDA(
+ FOREACH_ENTITY_FLOAT(pure_data, false,
+ {
+ if(IS_CLIENT(it))
+ continue;
+
if (it.team_saved == NUM_TEAM_1) it.team_saved = NUM_TEAM_2;
else if (it.team_saved == NUM_TEAM_2) it.team_saved = NUM_TEAM_1;
- ));
+ });
// reset the level with a countdown
cvar_set("timelimit", ftos(ceil(time - game_starttime) / 60));
// they win. Otherwise the defending team wins once the timelimit passes.
int WinningCondition_Assault()
{
- SELFPARAM();
- WinningConditionHelper(); // set worldstatus
+ WinningConditionHelper(NULL); // set worldstatus
int status = WINNING_NO;
// as the timelimit has not yet passed just assume the defending team will win
}
entity ent;
- ent = find(world, classname, "target_assault_roundend");
+ ent = find(NULL, classname, "target_assault_roundend");
if(ent)
{
if(ent.winning) // round end has been triggered by attacking team
}
else
{
- WITHSELF(ent, assault_new_round());
+ assault_new_round(ent);
}
}
}
// spawnfuncs
spawnfunc(info_player_attacker)
{
- if (!g_assault) { remove(self); return; }
+ if (!g_assault) { delete(this); return; }
- self.team = NUM_TEAM_1; // red, gets swapped every round
+ this.team = NUM_TEAM_1; // red, gets swapped every round
spawnfunc_info_player_deathmatch(this);
}
spawnfunc(info_player_defender)
{
- if (!g_assault) { remove(self); return; }
+ if (!g_assault) { delete(this); return; }
- self.team = NUM_TEAM_2; // blue, gets swapped every round
+ this.team = NUM_TEAM_2; // blue, gets swapped every round
spawnfunc_info_player_deathmatch(this);
}
spawnfunc(target_objective)
{
- if (!g_assault) { remove(this); return; }
+ if (!g_assault) { delete(this); return; }
this.classname = "target_objective";
+ IL_PUSH(g_assault_objectives, this);
this.use = assault_objective_use;
this.reset = assault_objective_reset;
this.reset(this);
spawnfunc(target_objective_decrease)
{
- if (!g_assault) { remove(self); return; }
+ if (!g_assault) { delete(this); return; }
- self.classname = "target_objective_decrease";
+ this.classname = "target_objective_decrease";
+ IL_PUSH(g_assault_objectivedecreasers, this);
- if(!self.dmg)
- self.dmg = 101;
+ if(!this.dmg)
+ this.dmg = 101;
- self.use = assault_objective_decrease_use;
- self.health = ASSAULT_VALUE_INACTIVE;
- self.max_health = ASSAULT_VALUE_INACTIVE;
- self.enemy = world;
+ this.use = assault_objective_decrease_use;
+ this.health = ASSAULT_VALUE_INACTIVE;
+ this.max_health = ASSAULT_VALUE_INACTIVE;
+ this.enemy = NULL;
- InitializeEntity(self, target_objective_decrease_findtarget, INITPRIO_FINDTARGET);
+ InitializeEntity(this, target_objective_decrease_findtarget, INITPRIO_FINDTARGET);
}
// destructible walls that can be used to trigger target_objective_decrease
spawnfunc(func_breakable);
spawnfunc(func_assault_destructible)
{
- if (!g_assault) { remove(self); return; }
+ if (!g_assault) { delete(this); return; }
- self.spawnflags = 3;
- self.classname = "func_assault_destructible";
+ this.spawnflags = 3;
+ this.classname = "func_assault_destructible";
+ IL_PUSH(g_assault_destructibles, this);
if(assault_attacker_team == NUM_TEAM_1)
- self.team = NUM_TEAM_2;
+ this.team = NUM_TEAM_2;
else
- self.team = NUM_TEAM_1;
+ this.team = NUM_TEAM_1;
spawnfunc_func_breakable(this);
}
spawnfunc(func_assault_wall)
{
- if (!g_assault) { remove(self); return; }
+ if (!g_assault) { delete(this); return; }
- 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;
- InitializeEntity(self, assault_setenemytoobjective, INITPRIO_FINDTARGET);
+ this.classname = "func_assault_wall";
+ this.mdl = this.model;
+ _setmodel(this, this.mdl);
+ this.solid = SOLID_BSP;
+ setthink(this, assault_wall_think);
+ this.nextthink = time;
+ InitializeEntity(this, assault_setenemytoobjective, INITPRIO_FINDTARGET);
}
spawnfunc(target_assault_roundend)
{
- if (!g_assault) { remove(self); return; }
+ if (!g_assault) { delete(this); return; }
- self.winning = 0; // round not yet won by attackers
- self.classname = "target_assault_roundend";
- self.use = target_assault_roundend_use;
- self.cnt = 0; // first round
- self.reset = target_assault_roundend_reset;
+ this.winning = 0; // round not yet won by attackers
+ this.classname = "target_assault_roundend";
+ this.use = target_assault_roundend_use;
+ this.cnt = 0; // first round
+ this.reset = target_assault_roundend_reset;
}
spawnfunc(target_assault_roundstart)
{
- if (!g_assault) { remove(self); return; }
+ if (!g_assault) { delete(this); return; }
assault_attacker_team = NUM_TEAM_1;
- self.classname = "target_assault_roundstart";
- self.use = assault_roundstart_use_self;
- self.reset2 = assault_roundstart_use_self;
- InitializeEntity(self, assault_roundstart_use, INITPRIO_FINDTARGET);
+ this.classname = "target_assault_roundstart";
+ this.use = assault_roundstart_use;
+ this.reset2 = assault_roundstart_use_this;
+ InitializeEntity(this, assault_roundstart_use_this, INITPRIO_FINDTARGET);
}
// legacy bot code
void havocbot_goalrating_ast_targets(entity this, float ratingscale)
{
- entity ad, best, wp, tod;
- float radius, found, bestvalue;
- vector p;
-
- ad = findchain(classname, "func_assault_destructible");
-
- for (; ad; ad = ad.chain)
+ IL_EACH(g_assault_destructibles, it.bot_attack,
{
- if (ad.target == "")
+ if (it.target == "")
continue;
- if (!ad.bot_attack)
- continue;
-
- found = false;
- for(tod = world; (tod = find(tod, targetname, ad.target)); )
+ bool found = false;
+ entity destr = it;
+ IL_EACH(g_assault_objectivedecreasers, it.targetname == destr.target,
{
- if(tod.classname == "target_objective_decrease")
+ if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE)
{
- if(tod.enemy.health > 0 && tod.enemy.health < ASSAULT_VALUE_INACTIVE)
- {
- // dprint(etos(ad),"\n");
- found = true;
- break;
- }
+ found = true;
+ break;
}
- }
+ });
if(!found)
- {
- /// dprint("target not found\n");
continue;
- }
- /// dprint("target #", etos(ad), " found\n");
-
- p = 0.5 * (ad.absmin + ad.absmax);
- // dprint(vtos(ad.origin), " ", vtos(ad.absmin), " ", vtos(ad.absmax),"\n");
- // te_knightspike(p);
- // te_lightning2(world, '0 0 0', p);
+ vector p = 0.5 * (it.absmin + it.absmax);
// Find and rate waypoints around it
found = false;
- best = world;
- bestvalue = 99999999999;
- for(radius=0; radius<1500 && !found; radius+=500)
+ entity best = NULL;
+ float bestvalue = 99999999999;
+ entity des = it;
+ for(float radius = 0; radius < 1500 && !found; radius += 500)
{
- for(wp=findradius(p, radius); wp; wp=wp.chain)
+ FOREACH_ENTITY_RADIUS(p, radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED),
{
- if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
- if(wp.classname=="waypoint")
- if(checkpvs(wp.origin, ad))
+ if(checkpvs(it.origin, des))
{
found = true;
- if(wp.cnt<bestvalue)
+ if(it.cnt < bestvalue)
{
- best = wp;
- bestvalue = wp.cnt;
+ best = it;
+ bestvalue = it.cnt;
}
}
- }
+ });
}
if(best)
{
/// dprint("waypoints around target were found\n");
- // te_lightning2(world, '0 0 0', best.origin);
+ // te_lightning2(NULL, '0 0 0', best.origin);
// te_knightspike(best.origin);
navigation_routerating(this, best, ratingscale, 4000);
this.havocbot_attack_time = 0;
- if(checkpvs(this.view_ofs,ad))
+ if(checkpvs(this.view_ofs,it))
if(checkpvs(this.view_ofs,best))
{
// dprint("increasing attack time for this target\n");
this.havocbot_attack_time = time + 2;
}
}
- }
+ });
}
void havocbot_role_ast_offense(entity this)
// mutator hooks
MUTATOR_HOOKFUNCTION(as, PlayerSpawn)
-{SELFPARAM();
- if(self.team == assault_attacker_team)
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_ASSAULT_ATTACKING);
- else
- Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_ASSAULT_DEFENDING);
+{
+ entity player = M_ARGV(0, entity);
- return false;
+ if(player.team == assault_attacker_team)
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_ASSAULT_ATTACKING);
+ else
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_ASSAULT_DEFENDING);
}
MUTATOR_HOOKFUNCTION(as, TurretSpawn)
-{SELFPARAM();
- if(!self.team || self.team == MAX_SHOT_DISTANCE)
- self.team = 5; // this gets reversed when match starts?
+{
+ entity turret = M_ARGV(0, entity);
- return false;
+ if(!turret.team || turret.team == MAX_SHOT_DISTANCE)
+ turret.team = 5; // this gets reversed when match starts?
}
MUTATOR_HOOKFUNCTION(as, VehicleSpawn)
-{SELFPARAM();
- self.nextthink = time + 0.5;
+{
+ entity veh = M_ARGV(0, entity);
- return false;
+ veh.nextthink = time + 0.5;
}
MUTATOR_HOOKFUNCTION(as, HavocBot_ChooseRole)
-{SELFPARAM();
- havocbot_ast_reset_role(self);
+{
+ entity bot = M_ARGV(0, entity);
+
+ havocbot_ast_reset_role(bot);
return true;
}
MUTATOR_HOOKFUNCTION(as, PlayHitsound)
{
+ entity frag_victim = M_ARGV(0, entity);
+
return (frag_victim.classname == "func_assault_destructible");
}
MUTATOR_HOOKFUNCTION(as, CheckRules_World)
{
- ret_float = WinningCondition_Assault();
+ M_ARGV(0, float) = WinningCondition_Assault();
return true;
}
{
// no assault warmups
warmup_stage = 0;
- return false;
}
MUTATOR_HOOKFUNCTION(as, OnEntityPreSpawn)
{
- SELFPARAM();
- switch(self.classname)
+ entity ent = M_ARGV(0, entity);
+
+ switch(ent.classname)
{
case "info_player_team1":
case "info_player_team2":
case "info_player_team4":
return true;
}
-
- return false;
}
// scoreboard setup
void assault_ScoreRules()
{
- ScoreRules_basics(2, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, true);
+ int teams = 0;
+ teams |= BIT(0);
+ teams |= BIT(1); // always red vs blue
+
+ ScoreRules_basics(teams, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, true);
ScoreInfo_SetLabel_TeamScore( ST_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY);
ScoreInfo_SetLabel_PlayerScore(SP_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY);
ScoreRules_basics_end();
}
-
-#endif