mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-22 12:01:25 +00:00
f49d3e2cee
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1876 fc73d0e0-1445-4013-8a0c-d673dee63da5
550 lines
13 KiB
C++
550 lines
13 KiB
C++
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);
|
|
};
|
|
|
|
|
|
|
|
|