entity tfdetect; .float items_allowed; .string broadcast; entity(float no) FindItem; //#define dprint(x) float(entity trigger, entity blame) triggercantouch = { if (blame.classname != "player") { // dprint("Not a player\n"); return false; } if (trigger.team_no) if (blame.team_no != trigger.team_no) { // dprint("Wrong team\n"); return false; } if (trigger.items_allowed) if ((FindItem(trigger.items_allowed)).owner != blame) { // dprint("missing an item\n"); return false; } return true; }; /* TeamFortress maps will be automatically detected by the TeamFortress patch if you put an entity in the map with a classname of "info_tfdetect". This makes TeamFortress automatically turn on the FORTRESSMAP toggleflag, and turn on teamplay. Also, once the TeamFortress map has been detected, the patch will look for any spawnpoints dedicated to teams (see below). If it finds any, it will look for the highest team number that is used. Once found, it will limit anybody attempting to join a team to that number. At the moment, Maps are limited to a maximum of 4 teams. E.g. If the highest team number used by a Team Spawnpoint is 3, then the patch will only allow players to join teams 1, 2, and 3. The detection entity can also do the following: - specify a version string in the "broadcast" variable. This will be compared with the version string for the TeamFortress patch. If the don't match, a warning will be displayed to the player that their patch and the map are incompatible. The version string is in the format as follows: TeamFortress v2.1 The string is case sensitive, so make sure you've got it right. Don't put a \n on the end of it. - Set the state of the toggleflags when the map starts up. The value of the toggleflags should be in the "impulse" variable. The bits for the toggleflags are: Bit 1 (1) : Off - ClasSkin , On - Multiskin Bit 2 (2) : Off - ClassPersistence Off , On - ClassPersistence On Bit 3 (4) : Off - CheatChecking Off , On - CheatChecking On Bit 4 (8) : Off - FortressMap Off , On - FortressMap On Bit 5 (16) : Off - RespawnDelay Off , On - RespawnDelay (See below) Bit 6 (32) : Off - RespawnDelay Off , On - RespawnDelay (See below) Bit 7 (64) : Off - AutoTeam Off , On - AutoTeam On Bit 8 (128) : Off - Individual Frags , On - Frags = TeamScore N.B. FortressMap will be set On automatically by the the Detection entity anyway, so just ignore that Bit. N.B. The RespawnDelay settings takes 2 bits. The value of both of them determines the level of respawn delay, as follows: Bit 5 Bit 6 Result Off Off No Respawn delays On Off 5 Second respawn delay Off On 10 Second respawn delay On On 20 Second respawn delay - Specify a string which is then "localcmd"ed. This allows you to automatically set gravity, friction, etc. The string should be stored in the "message" variable. - Put a limit on the number of lives each player has. When a player runs out of lives, they are stuck in observer mode for the rest of the level. Lives for each player depend on the setting for the team they belong to. The number of lives players of each team be should be specified in the following variables: Team 1 : "ammo_shells" Team 2 : "ammo_nails" Team 3 : "ammo_rockets" Team 4 : "ammo_cells" If the value of any team is 0, then the players of that team get infinite lives. - Specify any playerclass that is _not_ allowed on this map for each particular team. The bits of the four variables are used for this. The variables are as follows: Team 1 : "maxammo_shells" Team 2 : "maxammo_nails" Team 3 : "maxammo_rockets" Team 4 : "maxammo_cells" Also, the "playerclass" variable can be used to restrict classes for all teams. The bits for all the variables are as follows: Bit 1 (1) : No Scout Bit 2 (2) : No Sniper Bit 3 (4) : No Soldier Bit 4 (8) : No Demolitions Man Bit 5 (16) : No Combat Medic Bit 6 (32) : No Heavy Weapons Guy Bit 7 (64) : No Pyro Bit 8 (128) : No Random PlayerClass Bit 9 (256) : No Spy Bit 10(512) : No Engineer E.g. If the "maxammo_nails" variable is set to 3, then players in Team 2 will be unable to play Scouts or Snipers. Finally, if you want the Team to be a special team for a fancy map, such as the President in President-Quake, then you can restrict the team to only play Civilian Class. Do this by setting the team's variable to "-1". Notes: When using the "message" variable to set do localcmd's, you can issue more than one command by seperating them with \n Make sure you end it with a \n too. E.g. The following changes the gravity and the friction. "message" "sv_gravity 200\nsv_friction .5\n" */ void() info_tfdetect = { tfdetect = self; //broadcast contains the version of tf that it was designed for if (self.broadcast != "") { dprint("map was designed for "); dprint(self.broadcast); dprint("\n"); } //message states a message to localcmd //gravity, sv_friction, etc. localcmd(self.message); localcmd("\n"); //ammo_shells //ammo_nails //ammo_rockets //ammo_cells //these fields specify the number of lives the players of any team have. //maxammo_shells //maxammo_nails //maxammo_rockets //maxammo_cells //these fields specify which teams are allowed which classes //team_broadcast - text for choose-team menu //non_team_broadcast - text for map help //noise1 //noise2 //noise3 //noise4 //these fields are per-team messages for choosing class. }; string nextmap; void() execute_changelevel; .float goal_no; .float group_no; .float goal_results; .float goal_activation; .float owned_by; .string team_broadcast; .string owner_team_broadcast; .string non_team_broadcast; void(float t, float o, entity ignore, string toteam, string toowners, string toenemies) teambroadcast = { local entity p; while(1) { p = find(p, classname, "player"); if (!p) return; if (p != ignore) { if (p.team_no == t) sprint(p, PRINT_HIGH, toteam); else if (p.team_no == o) sprint(p, PRINT_HIGH, toowners); else sprint(p, PRINT_HIGH, toenemies); } } }; entity(float num) FindGoal = { local entity e; do { e = find(e, classname, "info_tfgoal"); if (e.goal_no == num) return e; } while(e); return world; }; entity(float num) FindItem = { local entity e; do { e = find(e, classname, "item_tfgoal"); if (e.goal_no == num) return e; } while(e); return world; }; void(entity goal) goal_remove = { goal.state = -1; }; void(entity goal) goal_restore = { goal.state = 0; }; void(entity goal, entity ap) goal_inactivate = { setmodel(goal, goal.mdl); setorigin(goal, goal.oldorigin); self.solid = SOLID_TRIGGER; goal.movetype = MOVETYPE_TOSS; //dprint("inactivated\n"); goal.owner = world; goal.state = 0; goal.nextthink = 0; } void() goal_timeout = { //dprint("goal_timeout\n"); goal_inactivate(self, world); }; void(entity player, entity goal) TakeGoalFromPlayer; void(entity goal, entity ap) goal_activate; void() goaldropped = { //dprint("goaldropped\n"); TakeGoalFromPlayer(self, self.owner); }; void() GoalTrackCarrier = { if (self.owner.health <= 0) { goal_inactivate(self, self.owner); self.think = goaldropped; self.nextthink = time + 40; } else self.nextthink = time + 0.1; }; void(entity goal, entity player) TakeGoalFromPlayer = { //dprint("TakeGoalFromPlayer\n"); if (goal.owner != player) return; goal_inactivate(goal, goal.owner); }; void(entity goal, entity player) GiveGoalToPlayer = { if (goal.owner != world) { if (goal.owner == player) return; TakeGoalFromPlayer(goal.owner, goal); } goal.owner = player; setmodel(goal, ""); goal.movetype = 12;//MOVETYPE_FOLLOW; //dprint("given "); //dprint(goal.netname); //dprint(" to "); //dprint(player.classname); //dprint("\n"); goal_activate(goal, player); goal.think = GoalTrackCarrier; goal.nextthink = time+0.1; }; void(entity goal, entity ap) goal_activate = { if (goal.state != 0) return; //active already or removed. sprint(ap, PRINT_HIGH, goal.message); teambroadcast(goal.team_no, goal.owned_by, ap, goal.team_broadcast, goal.owner_team_broadcast, goal.non_team_broadcast); sound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM); goal.state = 1; if (goal.goal_results & 4) { nextmap = "$host_mapname"; execute_changelevel(); } //activated goals shouldn't affect players if (!(goal_result&2)) //activated goals don't trigger others if goal_result&8 if (goal.classname == "item_tfgoal") GiveGoalToPlayer(goal, ap); else if (ap != world) { // if (goal.items) // GiveGoalToPlayer(FindItem(goal.items), ap); if (goal.axhitme) TakeGoalFromPlayer(FindItem(goal.axhitme), ap); } /* if (goal.activate_goal_no) goal_activate(FindGoal(goal.activate_goal_no)); if (goal.inactivate_goal_no) goal_inactivate(FindGoal(goal.inactivate_goal_no)); if (goal.remove_goal_no) goal_remove(FindGoal(goal.remove_goal_no)); if (goal.restore_goal_no) goal_restore(FindGoal(goal.restore_goal_no)); */ //goal_results & 16 takes away stealth status. ap.frags = ap.frags + goal.frags; ap.health = ap.health + goal.health; if (goal.wait == -1) goal.nextthink = 0; //permanent else if (goal.wait > 0) { goal.nextthink = time + goal.wait; setmodel(goal, ""); } else //instant. { goal.nextthink = 0; goal_timeout(); return; } goal.think = goal_timeout; if (goal.goal_results & 1) goal_remove(goal); }; void() goal_touch = { local float act; if (self.owner != world) return; if (self.state != 0) return; if (!(other.flags & FL_CLIENT)) return; if (!(self.goal_activation & 1)) //you're only allowed to touch if 1 return; // dprint(self.netname); // dprint(":\n"); act = triggercantouch(self, other); if (self.classname == "item_tfgoal") { if ((self.goal_activation & 64))//64 inverts weather to act act = 1 - act; } else { if ((self.goal_activation & 4)) //4 inverts weather to act act = 1 - act; } if (act) goal_activate(self, other); }; void() StartItem; void() info_tfgoal = { self.oldorigin = self.origin; if (self.mdl != "") { precache_model(self.mdl); setmodel(self, self.mdl); setsize(self, '-16 -16 -24', '16 16 32'); } if (self.noise != "") precache_sound(self.noise); self.touch = goal_touch; StartItem(); }; void() item_tfgoal = { self.wait = -1; info_tfgoal(); }; void() info_tfgoal_timer = { }; void() i_t_g = { self.classname = "info_tfgoal"; info_tfgoal(); }; void() i_t_t = { self.classname = "info_tfgoal_timer"; info_tfgoal_timer(); }; void() info_player_teamspawn = { if (self.team_no == 1) self.classname = "spawn1"; else if (self.team_no == 2) self.classname = "spawn2"; else if (self.team_no == 3) self.classname = "spawn3"; else if (self.team_no == 4) self.classname = "spawn4"; else { dprint("info_player_teamspawn with team_no "); dprint(ftos(self.team_no)); dprint("\n"); self.classname = "info_player_deathmatch"; } }; /* - Since quake has a limit on the size of the entity data in a map, abbreviations for some of the common entity fields were created. The Abbreviations are as follows: */ void() i_p_t = { self.classname = "info_player_teamspawn"; info_player_teamspawn(); }; /* var .float g_a = goal_activation; var .float g_e = goal_effects; var .float h_i_g = has_item_from_group" var .float r_i_g = remove_item_group" var .float a_s = ammo_shells" var .float a_n = ammo_nails" var .float a_r = ammo_rockets" var .float a_c = ammo_cells" var .float rv_s_h = remove_spawngroup" var .float rs_s_h = restore_spawngroup" var .float rv_gr = remove_group_no" var .float rs_gr = restore_group_no" var .float rv_g = remove_goal_no" var .float rs_g = restore_goal_no" var .string t_s_h = team_str_home; var .string t_s_m = team_str_moved; var .string t_s_c = team_str_carried; var .string n_s_h = non_team_str_home; var .string n_s_m = non_team_str_moved; var .string n_s_c = non_team_str_carried; var .string b_b = broadcast; var .string b_t = team_broadcast; var .string b_n = non_team_broadcast; var .string b_o = owners_team_broadcast; var .string n_b = netname_broadcast; var .string n_t = netname_team_broadcast; var .string n_n = netname_non_team_broadcast; var .string n_o = netname_owners_team_broadcast; var .string d_t = team_drop; var .string d_n = non_team_drop; var .string d_n_t = netname_team_drop; var .string d_n_n = netname_non_team_drop; */ //pointless entity .string map_name; .string alias; .string realteam; .float impulse_value; void() map_candidate = { remove(self); };