Compare commits

...

4 次程式碼提交

作者 SHA1 備註 提交日期
6bca60e74a implement ammo distribution flag 2019-09-19 19:41:14 -04:00
71ac1c45a1 various cleanup 2019-09-19 19:39:59 -04:00
c817ef05df fix team damage mitigating self damage 2019-09-19 19:37:40 -04:00
ae5c37af74 fix eyes giving invalid frames 2019-09-19 19:37:07 -04:00
共有 12 個文件被更改,包括 211 次插入207 次删除

查看文件

@ -316,6 +316,7 @@ void() become_spectator = {
self.flags |= FL_NOTARGET; self.flags |= FL_NOTARGET;
self.deadflag = DEAD_DEAD; self.deadflag = DEAD_DEAD;
self.view_ofs = VEC_ORIGIN; self.view_ofs = VEC_ORIGIN;
self.frame = 0;
self.modelindex = modelindex_eyes; self.modelindex = modelindex_eyes;
W_SetCurrentAmmo(); W_SetCurrentAmmo();
}; };
@ -1079,10 +1080,10 @@ void() cheat = {
return; return;
} }
self.ammo_rockets = 100; self.ammo_cells = AMMAX_CELLS;
self.ammo_nails = 200; self.ammo_nails = AMMAX_NAILS;
self.ammo_shells = 100; self.ammo_rockets = AMMAX_ROCKETS;
self.ammo_cells = 200; self.ammo_shells = AMMAX_SHELLS;
self.items |= IT_AXE | self.items |= IT_AXE |
IT_SHOTGUN | IT_SHOTGUN |
@ -1393,7 +1394,7 @@ float(entity targ, entity attacker) obit_monster = {
float(entity targ, entity attacker) obit_falling = { float(entity targ, entity attacker) obit_falling = {
if(targ.deathtype == "falling") { if(targ.deathtype == "falling") {
targ.deathtype = ""; targ.deathtype = string_null;
if(targ.pronoun == PRO_NONE) { if(targ.pronoun == PRO_NONE) {
bprint(" hit the ground too hard\n"); bprint(" hit the ground too hard\n");
} else { } else {

查看文件

@ -1,11 +1,15 @@
// combat.qc: entity-entity damage functions // combat.qc: entity-entity damage functions
float(entity targ, entity attacker) SameTeam = { float(entity targ, entity attacker) SameTeam = {
return targ.team > 0 && targ.team == attacker.team; return targ != attacker &&
targ.team > 0 &&
targ.team == attacker.team;
}; };
float(entity targ, entity attacker) BothPlayers = { float(entity targ, entity attacker) BothPlayers = {
return targ.classname == "player" && attacker.classname == "player"; return targ != attacker &&
targ.classname == "player" &&
attacker.classname == "player";
}; };
/* /*

查看文件

@ -275,6 +275,11 @@ const vector VEC_ORIGIN = '0 0 0';
const float BODYQUE_MAX = 16; const float BODYQUE_MAX = 16;
const float AMMAX_CELLS = 100;
const float AMMAX_NAILS = 200;
const float AMMAX_ROCKETS = 100;
const float AMMAX_SHELLS = 100;
const string WEPNAME_AXE = "Axe"; const string WEPNAME_AXE = "Axe";
const string WEPNAME_SHOTGUN = "Shotgun"; const string WEPNAME_SHOTGUN = "Shotgun";
const string WEPNAME_SUPER_SHOTGUN = "Double-barrelled Shotgun"; const string WEPNAME_SUPER_SHOTGUN = "Double-barrelled Shotgun";
@ -554,11 +559,25 @@ enum {
}; };
enum { enum {
SF_CHEATS = 1 << 0, AMTYPE_SHELLS = 1,
AMTYPE_NAILS,
AMTYPE_ROCKETS,
AMTYPE_CELLS,
};
enum {
SIGIL_1 = 1,
SIGIL_2 = 2,
SIGIL_3 = 4,
SIGIL_4 = 8,
};
enum {
SF_CHEATS = 1,
SF_LIVES_BEG = 1, SF_LIVES_BEG = 1,
SF_LIVES_END = 3, SF_LIVES_END = 3,
SF_LIVES_MSK = 0x0E, SF_LIVES_MSK = 14,
SF_DIST_AMMO = 1 << 4, SF_DIST_AMMO = 16,
}; };
#pragma noref 0 #pragma noref 0

查看文件

@ -128,9 +128,9 @@ void() door_fire = {
void() door_use = { void() door_use = {
entity oself; entity oself;
self.message = ""; // door message are for touch only self.message = string_null; // door message are for touch only
self.owner.message = ""; self.owner.message = string_null;
self.enemy.message = ""; self.enemy.message = string_null;
oself = self; oself = self;
self = self.owner; self = self.owner;
door_fire(); door_fire();
@ -181,7 +181,7 @@ void() door_touch = {
self.owner.attack_finished = time + 2; self.owner.attack_finished = time + 2;
if(self.owner.message != "") { if(self.owner.message != string_null) {
centerprint(other, self.owner.message); centerprint(other, self.owner.message);
sound(other, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM); sound(other, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM);
} }
@ -287,7 +287,7 @@ void() LinkDoors = {
if(self.targetname) { if(self.targetname) {
starte.targetname = self.targetname; starte.targetname = self.targetname;
} }
if(self.message != "") { if(self.message != string_null) {
starte.message = self.message; starte.message = self.message;
} }
t = find(t, classname, self.classname); t = find(t, classname, self.classname);

查看文件

@ -97,16 +97,13 @@ HEALTH BOX
// "ignore" will ignore max_health limit // "ignore" will ignore max_health limit
// //
float(entity e, float healamount, float ignore) T_Heal = { float(entity e, float healamount, float ignore) T_Heal = {
if(e.health <= 0) { if(e.health <= 0 || (!ignore && e.health >= other.max_health)) {
return 0;
}
if((!ignore) && (e.health >= other.max_health)) {
return 0; return 0;
} }
healamount = ceil(healamount); healamount = ceil(healamount);
e.health = e.health + healamount; e.health = e.health + healamount;
if((!ignore) && (e.health >= other.max_health)) { if(!ignore && e.health >= other.max_health) {
e.health = other.max_health; e.health = other.max_health;
} }
@ -161,17 +158,12 @@ void() health_touch = {
} }
if(self.healtype == 2) { // Megahealth? Ignore max_health... if(self.healtype == 2) { // Megahealth? Ignore max_health...
if(other.health >= 250) { if(other.health >= 250 || !T_Heal(other, self.healamount, TRUE)) {
return; return;
} }
if(!T_Heal(other, self.healamount, 1)) { } else if(!T_Heal(other, self.healamount, FALSE)) {
return; return;
} }
} else {
if(!T_Heal(other, self.healamount, 0)) {
return;
}
}
sprint(other, "You receive ", ftos(self.healamount), " health\n"); sprint(other, "You receive ", ftos(self.healamount), " health\n");
@ -232,28 +224,28 @@ ARMOR
void() armor_touch = { void() armor_touch = {
float type, value, bit; float type, value, bit;
if(other.health <= 0) { if(other.health <= 0 || other.classname != "player") {
return;
}
if(other.classname != "player") {
return; return;
} }
if(self.classname == "item_armor1") { switch(self.classname) {
case "item_armor1":
type = 0.3; type = 0.3;
value = 100; value = 100;
bit = IT_ARMOR1; bit = IT_ARMOR1;
} break;
if(self.classname == "item_armor2") { case "item_armor2":
type = 0.6; type = 0.6;
value = 150; value = 150;
bit = IT_ARMOR2; bit = IT_ARMOR2;
} break;
if(self.classname == "item_armorInv") { case "item_armorInv":
type = 0.8; type = 0.8;
value = 200; value = 200;
bit = IT_ARMOR3; bit = IT_ARMOR3;
break;
} }
if(other.armortype * other.armorvalue >= type * value) { if(other.armortype * other.armorvalue >= type * value) {
return; return;
} }
@ -323,42 +315,32 @@ WEAPONS
=============================================================================== ===============================================================================
*/ */
void() bound_other_ammo = { void(entity e) bound_ammo = {
if(other.ammo_shells > 100) { if(e.ammo_shells > AMMAX_SHELLS) {
other.ammo_shells = 100; e.ammo_shells = AMMAX_SHELLS;
} }
if(other.ammo_nails > 200) { if(e.ammo_nails > AMMAX_NAILS) {
other.ammo_nails = 200; e.ammo_nails = AMMAX_NAILS;
} }
if(other.ammo_rockets > 100) { if(e.ammo_rockets > AMMAX_ROCKETS) {
other.ammo_rockets = 100; e.ammo_rockets = AMMAX_ROCKETS;
} }
if(other.ammo_cells > 100) { if(e.ammo_cells > AMMAX_CELLS) {
other.ammo_cells = 100; e.ammo_cells = AMMAX_CELLS;
} }
}; };
float(float w) RankForWeapon = { float(float w) RankForWeapon = {
if(w == IT_LIGHTNING) { switch(w) {
return 1; case IT_LIGHTNING: return 1;
case IT_ROCKET_LAUNCHER: return 2;
case IT_SUPER_NAILGUN: return 3;
case IT_GRENADE_LAUNCHER: return 4;
case IT_SUPER_SHOTGUN: return 5;
case IT_NAILGUN: return 6;
default: return 7;
} }
if(w == IT_ROCKET_LAUNCHER) {
return 2;
}
if(w == IT_SUPER_NAILGUN) {
return 3;
}
if(w == IT_GRENADE_LAUNCHER) {
return 4;
}
if(w == IT_SUPER_SHOTGUN) {
return 5;
}
if(w == IT_NAILGUN) {
return 6;
}
return 7;
}; };
/* /*
@ -393,61 +375,65 @@ void() weapon_touch = {
return; return;
} }
// if the player was using their best weapon, change up to the new one if better // if the player was using their best weapon, change up to the new one if
// better
stemp = self; stemp = self;
self = other; self = other;
best = W_BestWeapon(); best = W_BestWeapon();
self = stemp; self = stemp;
if(deathmatch == 2 || coop) { leave = (deathmatch == 2 || coop);
leave = 1;
} else {
leave = 0;
}
if(self.classname == "weapon_nailgun") { switch(self.classname) {
if(leave && (other.items & IT_NAILGUN)) { case "weapon_nailgun":
if(leave && other.items & IT_NAILGUN) {
return; return;
} }
hadammo = other.ammo_nails; hadammo = other.ammo_nails;
new = IT_NAILGUN; new = IT_NAILGUN;
other.ammo_nails = other.ammo_nails + 30; other.ammo_nails = other.ammo_nails + 30;
} else if(self.classname == "weapon_supernailgun") { break;
if(leave && (other.items & IT_SUPER_NAILGUN)) { case "weapon_supernailgun":
if(leave && other.items & IT_SUPER_NAILGUN) {
return; return;
} }
hadammo = other.ammo_rockets; hadammo = other.ammo_rockets;
new = IT_SUPER_NAILGUN; new = IT_SUPER_NAILGUN;
other.ammo_nails = other.ammo_nails + 30; other.ammo_nails = other.ammo_nails + 30;
} else if(self.classname == "weapon_supershotgun") { break;
if(leave && (other.items & IT_SUPER_SHOTGUN)) { case "weapon_supershotgun":
if(leave && other.items & IT_SUPER_SHOTGUN) {
return; return;
} }
hadammo = other.ammo_rockets; hadammo = other.ammo_rockets;
new = IT_SUPER_SHOTGUN; new = IT_SUPER_SHOTGUN;
other.ammo_shells = other.ammo_shells + 5; other.ammo_shells = other.ammo_shells + 5;
} else if(self.classname == "weapon_rocketlauncher") { break;
if(leave && (other.items & IT_ROCKET_LAUNCHER)) { case "weapon_rocketlauncher":
if(leave && other.items & IT_ROCKET_LAUNCHER) {
return; return;
} }
hadammo = other.ammo_rockets; hadammo = other.ammo_rockets;
new = IT_ROCKET_LAUNCHER; new = IT_ROCKET_LAUNCHER;
other.ammo_rockets = other.ammo_rockets + 5; other.ammo_rockets = other.ammo_rockets + 5;
} else if(self.classname == "weapon_grenadelauncher") { break;
if(leave && (other.items & IT_GRENADE_LAUNCHER)) { case "weapon_grenadelauncher":
if(leave && other.items & IT_GRENADE_LAUNCHER) {
return; return;
} }
hadammo = other.ammo_rockets; hadammo = other.ammo_rockets;
new = IT_GRENADE_LAUNCHER; new = IT_GRENADE_LAUNCHER;
other.ammo_rockets = other.ammo_rockets + 5; other.ammo_rockets = other.ammo_rockets + 5;
} else if(self.classname == "weapon_lightning") { break;
if(leave && (other.items & IT_LIGHTNING)) { case "weapon_lightning":
if(leave && other.items & IT_LIGHTNING) {
return; return;
} }
hadammo = other.ammo_rockets; hadammo = other.ammo_rockets;
new = IT_LIGHTNING; new = IT_LIGHTNING;
other.ammo_cells = other.ammo_cells + 15; other.ammo_cells = other.ammo_cells + 15;
} else { break;
default:
objerror("weapon_touch: unknown classname"); objerror("weapon_touch: unknown classname");
} }
@ -456,7 +442,7 @@ void() weapon_touch = {
sound(other, CHAN_ITEM, "weapons/pkup.wav", 1, ATTN_NORM); sound(other, CHAN_ITEM, "weapons/pkup.wav", 1, ATTN_NORM);
stuffcmd(other, "bf\n"); stuffcmd(other, "bf\n");
bound_other_ammo(); bound_ammo(other);
// change to the weapon // change to the weapon
old = other.items; old = other.items;
@ -580,65 +566,66 @@ AMMO
=============================================================================== ===============================================================================
*/ */
void(.float ammo, float max) distribute_ammo = {
entity pl, stemp;
if(sf_dist_ammo) {
pl = find(world, classname, "player");
while(pl != world) {
pl.ammo += self.aflag;
bound_ammo(pl);
stemp = self;
self = pl;
W_SetCurrentAmmo();
self = stemp;
pl = find(pl, classname, "player");
}
} else {
if(other.ammo >= max) {
return;
}
other.ammo += self.aflag;
bound_ammo(other);
}
};
void() ammo_touch = { void() ammo_touch = {
entity stemp; entity stemp;
float best; float best;
if(other.classname != "player") { if(other.classname != "player" || other.health <= 0) {
return;
}
if(other.health <= 0) {
return; return;
} }
// if the player was using their best weapon, change up to the new one if better // if the player was using their best weapon, change up to the new one if
// better
stemp = self; stemp = self;
self = other; self = other;
best = W_BestWeapon(); best = W_BestWeapon();
self = stemp; self = stemp;
switch(self.weapon) {
// shotgun case AMTYPE_SHELLS:
if(self.weapon == 1) { distribute_ammo(ammo_shells, AMMAX_SHELLS);
if(other.ammo_shells >= 100) { break;
return; case AMTYPE_NAILS:
distribute_ammo(ammo_nails, AMMAX_NAILS);
break;
case AMTYPE_ROCKETS:
distribute_ammo(ammo_rockets, AMMAX_ROCKETS);
break;
case AMTYPE_CELLS:
distribute_ammo(ammo_cells, AMMAX_CELLS);
break;
} }
other.ammo_shells = other.ammo_shells + self.aflag;
}
// spikes
if(self.weapon == 2) {
if(other.ammo_nails >= 200) {
return;
}
other.ammo_nails = other.ammo_nails + self.aflag;
}
// rockets
if(self.weapon == 3) {
if(other.ammo_rockets >= 100) {
return;
}
other.ammo_rockets = other.ammo_rockets + self.aflag;
}
// cells
if(self.weapon == 4) {
if(other.ammo_cells >= 100) {
return;
}
other.ammo_cells = other.ammo_cells + self.aflag;
}
bound_other_ammo();
sprint(other, "You got the ", self.netname, "\n"); sprint(other, "You got the ", self.netname, "\n");
// ammo touch sound // ammo touch sound
sound(other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM); sound(other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM);
stuffcmd(other, "bf\n"); stuffcmd(other, "bf\n");
// change to a better weapon if appropriate // change to a better weapon if appropriate
if(other.weapon == best) { if(other.weapon == best) {
stemp = self; stemp = self;
self = other; self = other;
@ -759,13 +746,9 @@ void() key_touch = {
entity stemp; entity stemp;
float best; float best;
if(other.classname != "player") { if(other.classname != "player" ||
return; other.health <= 0 ||
} other.items & self.items) {
if(other.health <= 0) {
return;
}
if(other.items & self.items) {
return; return;
} }
@ -884,10 +867,7 @@ void() sigil_touch = {
entity stemp; entity stemp;
float best; float best;
if(other.classname != "player") { if(other.classname != "player" || other.health <= 0) {
return;
}
if(other.health <= 0) {
return; return;
} }
@ -898,7 +878,7 @@ void() sigil_touch = {
self.solid = SOLID_NOT; self.solid = SOLID_NOT;
self.model = string_null; self.model = string_null;
serverflags = serverflags | (self.spawnflags & 15); serverflags = serverflags | (self.spawnflags & 15);
self.classname = ""; // so rune doors won't find it self.classname = string_null; // so rune doors won't find it
activator = other; activator = other;
SUB_UseTargets(); // fire all targets / killtargets SUB_UseTargets(); // fire all targets / killtargets
@ -917,19 +897,19 @@ void() item_sigil = {
precache_sound("misc/runekey.wav"); precache_sound("misc/runekey.wav");
self.noise = "misc/runekey.wav"; self.noise = "misc/runekey.wav";
if(self.spawnflags & 1) { if(self.spawnflags & SIGIL_1) {
precache_model("progs/end1.mdl"); precache_model("progs/end1.mdl");
setmodel(self, "progs/end1.mdl"); setmodel(self, "progs/end1.mdl");
} }
if(self.spawnflags & 2) { if(self.spawnflags & SIGIL_2) {
precache_model2("progs/end2.mdl"); precache_model2("progs/end2.mdl");
setmodel(self, "progs/end2.mdl"); setmodel(self, "progs/end2.mdl");
} }
if(self.spawnflags & 4) { if(self.spawnflags & SIGIL_3) {
precache_model2("progs/end3.mdl"); precache_model2("progs/end3.mdl");
setmodel(self, "progs/end3.mdl"); setmodel(self, "progs/end3.mdl");
} }
if(self.spawnflags & 8) { if(self.spawnflags & SIGIL_4) {
precache_model2("progs/end4.mdl"); precache_model2("progs/end4.mdl");
setmodel(self, "progs/end4.mdl"); setmodel(self, "progs/end4.mdl");
} }
@ -951,10 +931,7 @@ void() powerup_touch = {
entity stemp; entity stemp;
float best; float best;
if(other.classname != "player") { if(other.classname != "player" || other.health <= 0) {
return;
}
if(other.health <= 0) {
return; return;
} }
@ -962,8 +939,8 @@ void() powerup_touch = {
if(deathmatch) { if(deathmatch) {
self.mdl = self.model; self.mdl = self.model;
if((self.classname == "item_artifact_invulnerability") || if(self.classname == "item_artifact_invulnerability" ||
(self.classname == "item_artifact_invisibility")) { self.classname == "item_artifact_invisibility") {
self.nextthink = time + 60 * 5; self.nextthink = time + 60 * 5;
} else { } else {
self.nextthink = time + 60; self.nextthink = time + 60;
@ -1104,7 +1081,8 @@ void() BackpackTouch = {
sprint(other, "the ", self.netname); sprint(other, "the ", self.netname);
} }
// if the player was using their best weapon, change up to the new one if better // if the player was using their best weapon, change up to the new one if
// better
stemp = self; stemp = self;
self = other; self = other;
best = W_BestWeapon(); best = W_BestWeapon();
@ -1123,7 +1101,7 @@ void() BackpackTouch = {
old = other.items; old = other.items;
other.items = other.items | new; other.items = other.items | new;
bound_other_ammo(); bound_ammo(other);
if(self.ammo_shells) { if(self.ammo_shells) {
if(acount) { if(acount) {
@ -1226,7 +1204,7 @@ entity() DropBackpack = {
item.netname = WEPNAME_LIGHTNING; item.netname = WEPNAME_LIGHTNING;
break; break;
default: default:
item.netname = ""; item.netname = string_null;
break; break;
} }

查看文件

@ -112,7 +112,7 @@ void() finale_1 = {
remove(pl); remove(pl);
WriteByte(MSG_ALL, SVC_FINALE); WriteByte(MSG_ALL, SVC_FINALE);
WriteString(MSG_ALL, ""); WriteString(MSG_ALL, string_null);
pl = find(world, classname, "player"); pl = find(world, classname, "player");
while(pl != world) { while(pl != world) {

查看文件

@ -284,7 +284,7 @@ void() PainSound = {
rs = rint((random() * 5) + 1); rs = rint((random() * 5) + 1);
self.noise = ""; self.noise = string_null;
if(rs == 1) { if(rs == 1) {
self.noise = "player/pain1.wav"; self.noise = "player/pain1.wav";
} else if(rs == 2) { } else if(rs == 2) {
@ -504,7 +504,7 @@ void() PlayerDie = {
DropBackpack(); DropBackpack();
} }
self.weaponmodel = ""; self.weaponmodel = string_null;
self.view_ofs = '0 0 -8'; self.view_ofs = '0 0 -8';
self.deadflag = DEAD_DYING; self.deadflag = DEAD_DYING;
self.solid = SOLID_NOT; self.solid = SOLID_NOT;

查看文件

@ -36,7 +36,7 @@ void() InitTrigger = {
setmodel(self, self.model); // set size and link into world setmodel(self, self.model); // set size and link into world
self.movetype = MOVETYPE_NONE; self.movetype = MOVETYPE_NONE;
self.modelindex = 0; self.modelindex = 0;
self.model = ""; self.model = string_null;
}; };
/* /*
@ -219,7 +219,7 @@ void() SUB_UseTargets = {
// //
// print the message // print the message
// //
if(activator.classname == "player" && self.message != "") { if(activator.classname == "player" && self.message != string_null) {
centerprint(activator, self.message); centerprint(activator, self.message);
if(!self.noise) { if(!self.noise) {
sound(activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM); sound(activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM);

查看文件

@ -384,7 +384,7 @@ void() info_teleport_destination = {
// this does nothing, just serves as a target spot // this does nothing, just serves as a target spot
self.mangle = self.angles; self.mangle = self.angles;
self.angles = VEC_ORIGIN; self.angles = VEC_ORIGIN;
self.model = ""; self.model = string_null;
self.origin = self.origin + '0 0 27'; self.origin = self.origin + '0 0 27';
if(!self.targetname) { if(!self.targetname) {
objerror("no targetname"); objerror("no targetname");
@ -463,11 +463,11 @@ void() trigger_onlyregistered_touch = {
self.attack_finished = time + 2; self.attack_finished = time + 2;
if(cvar("registered")) { if(cvar("registered")) {
self.message = ""; self.message = string_null;
SUB_UseTargets(); SUB_UseTargets();
remove(self); remove(self);
} else { } else {
if(self.message != "") { if(self.message != string_null) {
centerprint(other, self.message); centerprint(other, self.message);
sound(other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM); sound(other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM);
} }

查看文件

@ -731,7 +731,7 @@ void() W_SetCurrentAmmo = {
break; break;
default: default:
self.currentammo = 0; self.currentammo = 0;
self.weaponmodel = ""; self.weaponmodel = string_null;
self.weaponframe = 0; self.weaponframe = 0;
break; break;
} }

查看文件

@ -186,9 +186,9 @@ void() StartFrame = {
temp1flag = cvar("temp1"); temp1flag = cvar("temp1");
framecount = framecount + 1; framecount = framecount + 1;
sf_cheats = temp1flag & SF_CHEATS; sf_cheats = (temp1flag & SF_CHEATS) != 0;
sf_lives = bit_shift_right(temp1flag & SF_LIVES_MSK, SF_LIVES_BEG); sf_lives = bit_shift_right(temp1flag & SF_LIVES_MSK, SF_LIVES_BEG);
sf_dist_ammo = temp1flag & SF_DIST_AMMO; sf_dist_ammo = (temp1flag & SF_DIST_AMMO) != 0;
}; };
/* /*

6
todo
查看文件

@ -9,7 +9,8 @@ rename all functions to be lower_underscore
core features: core features:
distributed ammo add registercvar support
restart map after 10 seconds when everyone is dead
useful features: useful features:
@ -21,7 +22,7 @@ extraneous features:
emotes emotes
player sound effect option player sound effect option
players shoot through eachother players shoot through eachother (teamplay 4?)
selectable player models and skins selectable player models and skins
sound clips sound clips
third person player weapon models third person player weapon models
@ -30,5 +31,6 @@ done:
corpse pickups have keys corpse pickups have keys
custom pronouns custom pronouns
distributed ammo
lives counting lives counting
no friendly fire no friendly fire