Compare commits

...

4 Commits

Author SHA1 Message Date
an 10d98c67c0 add automatic pronoun detection for FTE_STRINGS engines 2019-09-20 19:56:41 -04:00
an af7d5bd846 clean up 2019-09-20 19:54:53 -04:00
an bcac79a048 add impulse command for spectating 2019-09-20 19:52:15 -04:00
an d3372f846a fix spectating edge cases 2019-09-20 11:38:29 -04:00
7 changed files with 241 additions and 195 deletions

View File

@ -8,6 +8,8 @@ alias pronoun_xey "impulse 26"
alias pronoun_ze_hir "impulse 27" alias pronoun_ze_hir "impulse 27"
alias pronoun_ze_zir "impulse 28" alias pronoun_ze_zir "impulse 28"
alias spectate "impulse 13"
set sc_cheats 0 set sc_cheats 0
set sc_lives 0 set sc_lives 0
set sc_dist_ammo 0 set sc_dist_ammo 0

View File

@ -1,5 +1,35 @@
// client.qc: player-adjacent functions // client.qc: player-adjacent functions
string(float pro) pronoun_subject = {
switch(pro) {
case PRO_NONE: return "none";
case PRO_FAE: return "fae";
case PRO_HE: return "he";
case PRO_IT: return "it";
case PRO_SHE: return "she";
case PRO_THEY: return "they";
case PRO_XEY: return "xey";
case PRO_ZE_H: return "ze";
case PRO_ZE_Z: return "ze";
default: return "unknown";
}
};
string(float pro) pronoun_possessive = {
switch(pro) {
case PRO_NONE: return "none";
case PRO_FAE: return "faer";
case PRO_HE: return "his";
case PRO_IT: return "its";
case PRO_SHE: return "her";
case PRO_THEY: return "their";
case PRO_XEY: return "xyr";
case PRO_ZE_H: return "hir";
case PRO_ZE_Z: return "zir";
default: return "unknown";
}
};
/* /*
============================================================================= =============================================================================
@ -22,28 +52,21 @@ void() SetChangeParms = {
} }
// remove items // remove items
self.items &= ~(IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD); self.items &= ~(IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY |
IT_SUIT | IT_QUAD);
// cap super health // cap super health
if(self.health > 100) { self.health = minmax(self.health, 50, 100);
self.health = 100;
} parm1 = self.items;
if(self.health < 50) { parm2 = self.health;
self.health = 50; parm3 = self.armorvalue;
} parm4 = (self.ammo_shells < 25 ? 25 : self.ammo_shells);
parm1 = self.items; parm5 = self.ammo_nails;
parm2 = self.health; parm6 = self.ammo_rockets;
parm3 = self.armorvalue; parm7 = self.ammo_cells;
if(self.ammo_shells < 25) { parm8 = self.weapon;
parm4 = 25; parm9 = self.armortype * 100;
} else {
parm4 = self.ammo_shells;
}
parm5 = self.ammo_nails;
parm6 = self.ammo_rockets;
parm7 = self.ammo_cells;
parm8 = self.weapon;
parm9 = self.armortype * 100;
parm10 = self.pronoun; parm10 = self.pronoun;
}; };
@ -60,22 +83,37 @@ void() SetNewParms = {
}; };
void() DecodeLevelParms = { void() DecodeLevelParms = {
float ofs, ofs2, pro;
if(serverflags) { if(serverflags) {
if(world.model == "maps/start.bsp") { if(world.model == "maps/start.bsp") {
SetNewParms(); // take away all stuff on starting new episode SetNewParms(); // take away all stuff on starting new episode
} }
} }
self.items = parm1; self.items = parm1;
self.health = parm2; self.health = parm2;
self.armorvalue = parm3; self.armorvalue = parm3;
self.ammo_shells = parm4; self.ammo_shells = parm4;
self.ammo_nails = parm5; self.ammo_nails = parm5;
self.ammo_rockets = parm6; self.ammo_rockets = parm6;
self.ammo_cells = parm7; self.ammo_cells = parm7;
self.weapon = parm8; self.weapon = parm8;
self.armortype = parm9 * 0.01; self.armortype = parm9 * 0.01;
self.pronoun = parm10; self.pronoun = parm10;
if(ext_strings) {
ofs = strstrofs(self.netname, "(", 0);
if(ofs != -1) {
for(pro = PRO_NONE; pro < PRO_MAX; pro++) {
ofs2 = strstrofs(self.netname, pronoun_possessive(pro), ofs);
if(ofs2 == ofs + 1 && strstrofs(self.netname, ")", ofs) != -1) {
self.pronoun = pro;
break;
}
}
}
}
}; };
/* /*
@ -231,14 +269,15 @@ void() execute_changelevel = {
other = find(world, classname, "player"); other = find(world, classname, "player");
while(other != world) { while(other != world) {
other.view_ofs = VEC_ORIGIN; other.view_ofs = VEC_ORIGIN;
other.angles = other.v_angle = pos.mangle; other.angles = other.v_angle = pos.mangle;
other.fixangle = TRUE; // turn this way immediately other.fixangle = TRUE; // turn this way immediately
other.nextthink = time + 0.5; other.nextthink = time + 0.5;
other.takedamage = DAMAGE_NO; other.takedamage = DAMAGE_NO;
other.solid = SOLID_NOT; other.solid = SOLID_NOT;
other.movetype = MOVETYPE_NONE; other.movetype = MOVETYPE_NONE;
other.modelindex = 0; other.modelindex = 0;
other.spectating = SPECTATING_INTERMISSION;
setorigin(other, pos.origin); setorigin(other, pos.origin);
other = find(other, classname, "player"); other = find(other, classname, "player");
} }
@ -295,7 +334,7 @@ void() trigger_changelevel = {
/* /*
============================================================================= =============================================================================
PLAYER GAME EDGE FUNCTIONS PLAYER GAME EDGE FUNCTIONS
============================================================================= =============================================================================
*/ */
@ -304,6 +343,7 @@ void() become_spectator = {
float not_dead; float not_dead;
entity pl; entity pl;
self.spectating = (self.lives ? SPECTATING_SPECTATING : SPECTATING_DEAD);
self.health = self.max_health; self.health = self.max_health;
self.armortype = 0; self.armortype = 0;
self.armorvalue = 0; self.armorvalue = 0;
@ -455,7 +495,7 @@ void() PutClientInServer = {
} else { } else {
bprint(ftos(self.lives), " lives left\n"); bprint(ftos(self.lives), " lives left\n");
} }
} else { } else if(self.lives == 0) {
self.lives = sf_lives; self.lives = sf_lives;
dprint("lives reset to ", ftos(sf_lives), "\n"); dprint("lives reset to ", ftos(sf_lives), "\n");
} }
@ -465,22 +505,23 @@ void() PutClientInServer = {
spot = SelectSpawnPoint(); spot = SelectSpawnPoint();
self.classname = "player"; self.classname = "player";
self.health = 100; self.health = 100;
self.takedamage = DAMAGE_AIM; self.takedamage = DAMAGE_AIM;
self.solid = SOLID_SLIDEBOX; self.solid = SOLID_SLIDEBOX;
self.movetype = MOVETYPE_WALK; self.movetype = MOVETYPE_WALK;
self.show_hostile = 0; self.show_hostile = 0;
self.max_health = 100; self.max_health = 100;
self.flags = FL_CLIENT; self.flags = FL_CLIENT;
self.air_finished = time + 12; self.air_finished = time + 12;
self.dmg = 2; // initial water damage self.dmg = 2; // initial water damage
self.super_damage_finished = 0; self.super_damage_finished = 0;
self.radsuit_finished = 0; self.radsuit_finished = 0;
self.invisible_finished = 0; self.invisible_finished = 0;
self.invincible_finished = 0; self.invincible_finished = 0;
self.effects = 0; self.effects = 0;
self.invincible_time = 0; self.invincible_time = 0;
self.spectating = SPECTATING_NOT;
DecodeLevelParms(); DecodeLevelParms();
@ -491,13 +532,12 @@ void() PutClientInServer = {
self.th_die = PlayerDie; self.th_die = PlayerDie;
self.deadflag = DEAD_NO; self.deadflag = DEAD_NO;
// paustime is set by teleporters to keep the player from moving a while
// pausetime is set by teleporters to keep the player from moving for a bit
self.pausetime = 0; self.pausetime = 0;
// spot = SelectSpawnPoint(); self.origin = spot.origin + '0 0 1';
self.angles = spot.angles;
self.origin = spot.origin + '0 0 1';
self.angles = spot.angles;
self.fixangle = TRUE; // turn this way immediately self.fixangle = TRUE; // turn this way immediately
// oh, this is a hack! // oh, this is a hack!
@ -524,7 +564,7 @@ void() PutClientInServer = {
/* /*
============================================================================= =============================================================================
QUAKED FUNCTIONS QUAKED FUNCTIONS
============================================================================= =============================================================================
*/ */
@ -620,8 +660,7 @@ Exit deathmatch games upon conditions
============ ============
*/ */
void() CheckRules = { void() CheckRules = {
float timelimit; float timelimit, fraglimit;
float fraglimit;
if(gameover) { // someone else quit the game already if(gameover) { // someone else quit the game already
return; return;
@ -630,12 +669,8 @@ void() CheckRules = {
timelimit = cvar("timelimit") * 60; timelimit = cvar("timelimit") * 60;
fraglimit = cvar("fraglimit"); fraglimit = cvar("fraglimit");
if(timelimit && time >= timelimit) { if((timelimit && time >= timelimit) ||
NextLevel(); (fraglimit && self.frags >= fraglimit)) {
return;
}
if(fraglimit && self.frags >= fraglimit) {
NextLevel(); NextLevel();
return; return;
} }
@ -707,17 +742,12 @@ void() PlayerJump = {
return; return;
} }
if(!(self.flags & FL_ONGROUND)) { if(!(self.flags & FL_ONGROUND) || !(self.flags & FL_JUMPRELEASED)) {
return;
}
if(!(self.flags & FL_JUMPRELEASED)) {
return; // don't pogo stick return; // don't pogo stick
} }
self.flags = self.flags - (self.flags & FL_JUMPRELEASED); self.flags -= (self.flags & FL_JUMPRELEASED);
self.flags -= FL_ONGROUND; // don't stairwalk
self.flags = self.flags - FL_ONGROUND; // don't stairwalk
self.button2 = 0; self.button2 = 0;
// player jumping sound // player jumping sound
@ -732,11 +762,8 @@ WaterMove
============ ============
*/ */
void() WaterMove = { void() WaterMove = {
//dprint(ftos(self.waterlevel)); string water_snd;
if(self.movetype == MOVETYPE_NOCLIP) { if(self.movetype == MOVETYPE_NOCLIP || self.health < 0) {
return;
}
if(self.health < 0) {
return; return;
} }
@ -769,41 +796,39 @@ void() WaterMove = {
return; return;
} }
if(self.watertype == CONTENT_LAVA) { switch(self.watertype) {
// do damage case CONTENT_LAVA:
if(self.dmgtime < time) { if(self.dmgtime < time) {
if(self.radsuit_finished > time) { if(self.radsuit_finished > time) {
self.dmgtime = time + 1; self.dmgtime = time + 1;
} else { } else {
self.dmgtime = time + 0.2; self.dmgtime = time + 0.2;
}
T_Damage(self, world, world, 10 * self.waterlevel);
} }
T_Damage(self, world, world, 10 * self.waterlevel); break;
} case CONTENT_SLIME:
} else if(self.watertype == CONTENT_SLIME) { if(self.dmgtime < time && self.radsuit_finished < time) {
// do damage self.dmgtime = time + 1;
if(self.dmgtime < time && self.radsuit_finished < time) { T_Damage(self, world, world, 4 * self.waterlevel);
self.dmgtime = time + 1; }
T_Damage(self, world, world, 4 * self.waterlevel); break;
}
} }
if(!(self.flags & FL_INWATER)) { if(!(self.flags & FL_INWATER)) {
// player enter water sound // player enter water sound
if(self.watertype == CONTENT_LAVA) { switch(self.watertype) {
sound(self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM); case CONTENT_LAVA: water_snd = "player/inlava.wav"; break;
} case CONTENT_WATER: water_snd = "player/inh2o.wav"; break;
if(self.watertype == CONTENT_WATER) { case CONTENT_SLIME: water_snd = "player/slimbrn2.wav"; break;
sound(self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM);
}
if(self.watertype == CONTENT_SLIME) {
sound(self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM);
} }
sound(self, CHAN_BODY, water_snd, 1, ATTN_NORM);
self.flags = self.flags + FL_INWATER; self.flags = self.flags + FL_INWATER;
self.dmgtime = 0; self.dmgtime = 0;
} }
if(!(self.flags & FL_WATERJUMP)) { if(!(self.flags & FL_WATERJUMP)) {
self.velocity = self.velocity - 0.8 * self.waterlevel * frametime * self.velocity; self.velocity -= 0.8 * self.waterlevel * frametime * self.velocity;
} }
}; };
@ -843,17 +868,14 @@ Called every frame before physics are run
================ ================
*/ */
void() PlayerPreThink = { void() PlayerPreThink = {
// otherwise a button could be missed between the think tics
if(intermission_running) { if(intermission_running) {
IntermissionThink(); // otherwise a button could be missed between IntermissionThink();
return; // the think tics return;
} else if(self.spectating) {
return;
} }
if(self.view_ofs == VEC_ORIGIN) {
return; // intermission, finale or spectating
}
makevectors(self.v_angle); // is this still used
CheckRules(); CheckRules();
WaterMove(); WaterMove();
@ -895,7 +917,7 @@ Check for turning off powerups
================ ================
*/ */
void() CheckPowerups = { void() CheckPowerups = {
if(self.health <= 0) { if(self.health <= 0 || self.spectating) {
return; return;
} }
@ -1020,16 +1042,18 @@ Called every frame after physics are run
================ ================
*/ */
void() PlayerPostThink = { void() PlayerPostThink = {
if(self.view_ofs == VEC_ORIGIN) { if(time >= self.attack_finished) {
return; // intermission, finale or spectating ImpulseCommands();
} }
if(self.deadflag) {
if(self.spectating || self.deadflag) {
return; return;
} }
// do weapon stuff // do weapon stuff
if(time >= self.attack_finished) {
W_WeaponFrame(); W_WeaponFrame();
}
// check to see if player landed and play landing sound // check to see if player landed and play landing sound
if(self.jump_flag < -300 && self.flags & FL_ONGROUND && self.health > 0) { if(self.jump_flag < -300 && self.flags & FL_ONGROUND && self.health > 0) {
@ -1076,20 +1100,23 @@ called when a player disconnects from a server
============ ============
*/ */
void() ClientDisconnect = { void() ClientDisconnect = {
// if the level end trigger has been activated, just return
// since they aren't *really* leaving
if(gameover) { if(gameover) {
return; return;
} }
// if the level end trigger has been activated, just return
// since they aren't *really* leaving
// let everyone else know // let everyone else know
bprint(self.netname, " left the game with ", ftos(self.frags), " frags\n"); bprint(self.netname, " left the game with ", ftos(self.frags), " frags\n");
sound(self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE);
set_suicide_frame(); if(!self.spectating) {
sound(self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE);
set_suicide_frame();
}
}; };
void() cheat = { void() cheat = {
if((deathmatch || coop) && !sf_cheats) { if(((deathmatch || coop) && !sf_cheats) || self.spectating) {
return; return;
} }
@ -1114,43 +1141,13 @@ void() cheat = {
}; };
void() cheat_quad = { void() cheat_quad = {
if((deathmatch || coop) && !sf_cheats) { if(((deathmatch || coop) && !sf_cheats) || self.spectating) {
return; return;
} }
self.super_time = 1; self.super_time = 1;
self.super_damage_finished = time + 30; self.super_damage_finished = time + 30;
self.items = self.items | IT_QUAD; self.items = self.items | IT_QUAD;
};
string(float pro) pronoun_subject = {
switch(pro) {
case PRO_NONE: return "none";
case PRO_FAE: return "fae";
case PRO_HE: return "he";
case PRO_IT: return "it";
case PRO_SHE: return "she";
case PRO_THEY: return "they";
case PRO_XEY: return "xey";
case PRO_ZE_H: return "ze";
case PRO_ZE_Z: return "ze";
default: return "unknown";
}
};
string(float pro) pronoun_possessive = {
switch(pro) {
case PRO_NONE: return "none";
case PRO_FAE: return "faer";
case PRO_HE: return "his";
case PRO_IT: return "its";
case PRO_SHE: return "her";
case PRO_THEY: return "their";
case PRO_XEY: return "xyr";
case PRO_ZE_H: return "hir";
case PRO_ZE_Z: return "zir";
default: return "unknown";
}
}; };
void(float pro) change_pronoun = { void(float pro) change_pronoun = {
@ -1162,6 +1159,34 @@ void(float pro) change_pronoun = {
pronoun_possessive(pro), "\n"); pronoun_possessive(pro), "\n");
}; };
void() spectate = {
switch(self.spectating) {
case SPECTATING_NOT:
bprint(self.netname, " has become a spectator\n");
become_spectator();
break;
case SPECTATING_SPECTATING:
bprint(self.netname, " has returned from spectating\n");
setspawnparms(self);
PutClientInServer();
break;
case SPECTATING_DEAD:
centerprint(self,
"You have no life force left\n"
"and cannot return to\n"
"the mortal world yet");
break;
case SPECTATING_INTERMISSION:
case SPECTATING_FINALE:
centerprint(self,
"You are incorporeal as you wait\n"
"for the next cycle\n"
"and cannot return to\n"
"the mortal world yet");
break;
}
};
void() ImpulseCommands = { void() ImpulseCommands = {
if(self.impulse >= 1 && self.impulse <= 8) { if(self.impulse >= 1 && self.impulse <= 8) {
W_ChangeWeapon(self.impulse); W_ChangeWeapon(self.impulse);
@ -1173,6 +1198,7 @@ void() ImpulseCommands = {
case 10: W_CycleWeapon(); break; case 10: W_CycleWeapon(); break;
case 11: cheat_quad(); break; case 11: cheat_quad(); break;
case 12: W_CycleWeaponReverse(); break; case 12: W_CycleWeaponReverse(); break;
case 13: spectate(); break;
} }
} }
@ -1545,7 +1571,7 @@ Player entered the suicide command
============ ============
*/ */
void() ClientKill = { void() ClientKill = {
if(self.view_ofs != VEC_ORIGIN) { if(!self.spectating) {
reset_death_vel(); reset_death_vel();
self.frags--; // extra penalty self.frags--; // extra penalty
ClientObituary(self, self); ClientObituary(self, self);

View File

@ -52,15 +52,15 @@ entity msg_entity; // destination of single entity writes
// required prog functions // required prog functions
void() main; // only for testing void() main; // only for testing
void() StartFrame; void() StartFrame; // called every frame
void() PlayerPreThink; void() PlayerPreThink; // called each frame on each client before physics
void() PlayerPostThink; void() PlayerPostThink; // called each frame on each client after physics
void() ClientKill; void() ClientKill; // called when the "kill" cmd is entered
void() ClientConnect; void() ClientConnect; // called upon client "connect"
void() PutClientInServer; // call after setting the parm1... parms void() PutClientInServer; // call after setting the parm1... parms
void() ClientDisconnect; void() ClientDisconnect; // called upon client "disconnect"
/* called when a client first connects to a server. sets parms so they can be /* called when a client first connects to a server. sets parms so they can be
* saved off for restarts * saved off for restarts
@ -271,6 +271,17 @@ void(entity e) setspawnparms = #78;
float(string s) checkextension = #99; float(string s) checkextension = #99;
float(string str, string sub, float startpos) strstrofs = #221;
float(string str, float ofs) str2chr = #222;
string(float... c) chr2str = #223;
string(float ccase, float calpha, float cnum, string... s) strconv = #224;
string(float chars, string... s) strpad = #225;
string(string info, string key, string... value) infoadd = #226;
string(string info, string key) infoget = #227;
float(string s1, string s2, float len) strncmp = #228;
float(string s1, string s2) strcasecmp = #229;
float(string s1, string s2, float len) strncasecmp = #230;
// constants -----------------------------------------------------------------| // constants -----------------------------------------------------------------|
const vector VEC_ORIGIN = '0 0 0'; const vector VEC_ORIGIN = '0 0 0';
@ -485,22 +496,6 @@ enum {
WORLD_BASE, WORLD_BASE,
}; };
enum {
PRO_NONE,
// alphabetically sorted, based on pronoun.is and what i've seen used
PRO_FAE,
PRO_HE,
PRO_IT,
PRO_SHE,
PRO_THEY,
PRO_XEY,
PRO_ZE_H,
PRO_ZE_Z,
PRO_MAX,
};
enum { enum {
DOOR_START_OPEN = 1, DOOR_START_OPEN = 1,
DOOR_DONT_LINK = 4, DOOR_DONT_LINK = 4,
@ -574,6 +569,23 @@ enum {
SIGIL_4 = 8, SIGIL_4 = 8,
}; };
// super co-op additions
enum {
PRO_NONE,
// alphabetically sorted, based on pronoun.is and what i've seen used
PRO_FAE,
PRO_HE,
PRO_IT,
PRO_SHE,
PRO_THEY,
PRO_XEY,
PRO_ZE_H,
PRO_ZE_Z,
PRO_MAX,
};
enum { enum {
SF_CHEATS = 1, SF_CHEATS = 1,
SF_LIVES_BEG = 1, SF_LIVES_BEG = 1,
@ -581,6 +593,14 @@ enum {
SF_LIVES_MSK = 14, SF_LIVES_MSK = 14,
SF_DIST_AMMO = 16, SF_DIST_AMMO = 16,
}; };
enum {
SPECTATING_NOT,
SPECTATING_DEAD,
SPECTATING_SPECTATING,
SPECTATING_INTERMISSION,
SPECTATING_FINALE,
};
#pragma noref 0 #pragma noref 0
// globals -------------------------------------------------------------------| // globals -------------------------------------------------------------------|
@ -635,6 +655,7 @@ float player_respawned;
float all_players_are_dead; float all_players_are_dead;
float ext_con_set; float ext_con_set;
float ext_strings;
// fields --------------------------------------------------------------------| // fields --------------------------------------------------------------------|
@ -753,6 +774,7 @@ float ext_con_set;
// super co-op additions // super co-op additions
.float pronoun; .float pronoun;
.float lives; .float lives;
.float spectating;
// functions -----------------------------------------------------------------| // functions -----------------------------------------------------------------|
@ -921,4 +943,6 @@ void() func_train_find;
void(vector p) boss_missile; void(vector p) boss_missile;
void() ImpulseCommands;
// EOF // EOF

View File

@ -116,15 +116,16 @@ void() finale_1 = {
pl = find(world, classname, "player"); pl = find(world, classname, "player");
while(pl != world) { while(pl != world) {
pl.view_ofs = VEC_ORIGIN; pl.view_ofs = VEC_ORIGIN;
pl.angles = other.v_angle = pos.mangle; pl.angles = other.v_angle = pos.mangle;
pl.fixangle = TRUE; // turn this way immediately pl.fixangle = TRUE; // turn this way immediately
pl.map = self.map; pl.map = self.map;
pl.nextthink = time + 0.5; pl.nextthink = time + 0.5;
pl.takedamage = DAMAGE_NO; pl.takedamage = DAMAGE_NO;
pl.solid = SOLID_NOT; pl.solid = SOLID_NOT;
pl.movetype = MOVETYPE_NONE; pl.movetype = MOVETYPE_NONE;
pl.modelindex = 0; pl.modelindex = 0;
pl.spectating = SPECTATING_FINALE;
setorigin(pl, pos.origin); setorigin(pl, pos.origin);
pl = find(pl, classname, "player"); pl = find(pl, classname, "player");
} }

View File

@ -1051,12 +1051,6 @@ Called every frame so impulse events can be handled as well as possible
============ ============
*/ */
void() W_WeaponFrame = { void() W_WeaponFrame = {
if(time < self.attack_finished) {
return;
}
ImpulseCommands();
// check for attack // check for attack
if(self.button0) { if(self.button0) {
SuperDamageSound(); SuperDamageSound();

View File

@ -18,9 +18,8 @@ void() worldspawn = {
InitBodyQue(); InitBodyQue();
if(cvar("pr_checkextension")) { if(cvar("pr_checkextension")) {
if(checkextension("DP_CON_SET")) { ext_con_set = checkextension("DP_CON_SET");
ext_con_set = TRUE; ext_strings = checkextension("FTE_STRINGS");
}
} }
// custom map attributes // custom map attributes

2
todo
View File

@ -14,7 +14,6 @@ all done
useful features: useful features:
configurable enemy stats configurable enemy stats
impulse command for spectating
indicators for where other players are indicators for where other players are
users can cancel map ends ("<name> initiated travel to <mapname>") users can cancel map ends ("<name> initiated travel to <mapname>")
@ -34,6 +33,7 @@ corpse pickups have keys
custom pronouns custom pronouns
distributed ammo distributed ammo
enforcers are broken enforcers are broken
impulse command for spectating
lives counting lives counting
no friendly fire no friendly fire
restart map after 10 seconds when everyone is dead restart map after 10 seconds when everyone is dead