Compare commits

...

3 Commits

Author SHA1 Message Date
df8107d247 add lives and spectating 2019-09-19 16:57:27 -04:00
6015681eaa add sf_cheats support 2019-09-19 12:56:39 -04:00
2aa46c72ec add temp1flag 2019-09-19 12:56:17 -04:00
8 changed files with 128 additions and 47 deletions

View File

@ -1,5 +1,9 @@
// ai.qc: monster AI functions
float(entity enemy) enemy_is_gone = {
return enemy.health <= 0 || enemy.flags & FL_NOTARGET;
};
//
// when a monster becomes angry at a player, that monster will be used
// as the sight target the next frame so that monsters near that one
@ -180,14 +184,9 @@ float() FindTarget = {
}
}
if(client == self.enemy) {
return FALSE;
}
if(client.flags & FL_NOTARGET) {
return FALSE;
}
if(client.items & IT_INVISIBILITY) {
if(client == self.enemy ||
client.flags & FL_NOTARGET ||
client.items & IT_INVISIBILITY) {
return FALSE;
}
@ -205,7 +204,7 @@ float() FindTarget = {
return FALSE;
}
} else if(r == RANGE_MID) {
if(/* client.show_hostile < time || */ !infront(client)) {
if(!infront(client)) {
return FALSE;
}
}
@ -468,10 +467,9 @@ void(float dist) ai_run = {
movedist = dist;
// see if the enemy is dead
if(self.enemy.health <= 0) {
if(enemy_is_gone(self.enemy)) {
self.enemy = world;
// FIXME: look all around for other targets
if(self.oldenemy.health > 0) {
if(!enemy_is_gone(self.oldenemy)) {
self.enemy = self.oldenemy;
HuntTarget();
} else {

View File

@ -30,9 +30,8 @@ $frame shockc1 shockc2 shockc3 shockc4 shockc5 shockc6 shockc7 shockc8
$frame shockc9 shockc10
void() boss_face = {
// go for another player if multi player
if(self.enemy.health <= 0 || random() < 0.02) {
if(enemy_is_gone(self.enemy) || random() < 0.02) {
self.enemy = find(self.enemy, classname, "player");
if(!self.enemy) {
self.enemy = find(self.enemy, classname, "player");
@ -211,7 +210,7 @@ void(vector p) boss_missile = {
sound(self, CHAN_WEAPON, "boss1/throw.wav", 1, ATTN_NORM);
// check for dead enemy
if(self.enemy.health <= 0) {
if(enemy_is_gone(self.enemy)) {
boss_idle1();
}
};

View File

@ -16,7 +16,7 @@ void() info_intermission = {
};
void() SetChangeParms = {
if(self.health <= 0) {
if(self.health <= 0 || self.lives == 0) {
SetNewParms();
return;
}
@ -253,7 +253,7 @@ void() changelevel_touch = {
return;
}
if((cvar("noexit") == 1) || ((cvar("noexit") == 2) && (mapname != "start"))) {
if(cvar("noexit") == 1 || (cvar("noexit") == 2 && mapname != "start")) {
T_Damage(other, self, self, 50000);
return;
}
@ -266,7 +266,7 @@ void() changelevel_touch = {
SUB_UseTargets();
if((self.spawnflags & 1) && (deathmatch == 0)) {
if(self.spawnflags & 1 && deathmatch == 0) {
// NO_INTERMISSION
GotoNextMap();
return;
@ -285,7 +285,7 @@ When the player touches this, they get sent to the map listed in the "map" varia
*/
void() trigger_changelevel = {
if(!self.map) {
objerror("chagnelevel trigger doesn't have map");
objerror("changelevel trigger doesn't have map");
}
InitTrigger();
@ -300,7 +300,26 @@ void() trigger_changelevel = {
=============================================================================
*/
// called by ClientKill and DeadThink
void() become_spectator = {
self.health = self.max_health;
self.armortype = 0;
self.armorvalue = 0;
self.items = 0;
self.weapon = 0;
self.ammo_shells = 0;
self.ammo_nails = 0;
self.ammo_rockets = 0;
self.ammo_cells = 0;
self.takedamage = DAMAGE_NO;
self.solid = SOLID_NOT;
self.movetype = MOVETYPE_NOCLIP;
self.flags |= FL_NOTARGET;
self.deadflag = DEAD_DEAD;
self.view_ofs = VEC_ORIGIN;
self.modelindex = modelindex_eyes;
W_SetCurrentAmmo();
};
void() respawn = {
if(coop) {
// make a copy of the dead body for appearances sake
@ -308,6 +327,7 @@ void() respawn = {
// get the spawn parms as they were at level start
setspawnparms(self);
// respawn
player_respawned = TRUE;
PutClientInServer();
} else if(deathmatch) {
// make a copy of the dead body for appearances sake
@ -315,6 +335,7 @@ void() respawn = {
// set default spawn parms
SetNewParms();
// respawn
player_respawned = TRUE;
PutClientInServer();
} else {
// restart the entire server
@ -402,6 +423,31 @@ called each time a player is spawned
*/
void() PutClientInServer = {
entity spot;
float respawned;
respawned = player_respawned;
player_respawned = FALSE;
if(sf_lives) {
if(respawned) {
self.lives--;
bprint(self.netname, " has ");
if(self.lives == 0) {
bprint("run out of lives...\n");
become_spectator();
return;
} else if(self.lives == 1) {
bprint("one life left!\n");
} else {
bprint(ftos(self.lives), " lives left\n");
}
} else {
self.lives = sf_lives;
dprint("lives reset to ", ftos(sf_lives), "\n");
}
} else {
self.lives = 1;
}
spot = SelectSpawnPoint();
@ -583,11 +629,10 @@ void() CheckRules = {
//============================================================================
void() PlayerDeathThink = {
entity old_self;
void() reset_death_vel = {
float forward;
if((self.flags & FL_ONGROUND)) {
if(self.flags & FL_ONGROUND) {
forward = vlen(self.velocity);
forward = forward - 20;
if(forward <= 0) {
@ -596,6 +641,10 @@ void() PlayerDeathThink = {
self.velocity = forward * normalize(self.velocity);
}
}
};
void() PlayerDeathThink = {
reset_death_vel();
// wait for all buttons released
if(self.deadflag == DEAD_DEAD) {
@ -786,7 +835,7 @@ void() PlayerPreThink = {
}
if(self.view_ofs == VEC_ORIGIN) {
return; // intermission or finale
return; // intermission, finale or spectating
}
makevectors(self.v_angle); // is this still used
@ -841,7 +890,7 @@ void() CheckPowerups = {
// sound and screen flash when items starts to run out
if(self.invisible_sound < time) {
sound(self, CHAN_AUTO, "items/inv3.wav", 0.5, ATTN_IDLE);
self.invisible_sound = time + ((random() * 3) + 1);
self.invisible_sound = time + random() * 3 + 1;
}
if(self.invisible_finished < time + 3) {
if(self.invisible_time == 1) {
@ -969,7 +1018,7 @@ void() PlayerPostThink = {
W_WeaponFrame();
// 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) {
if(self.watertype == CONTENT_WATER) {
sound(self, CHAN_BODY, "player/h2ojump.wav", 1, ATTN_NORM);
} else if(self.jump_flag < -650) {
@ -1026,7 +1075,7 @@ void() ClientDisconnect = {
};
void() cheat = {
if(deathmatch || coop) {
if((deathmatch || coop) && !sf_cheats) {
return;
}
@ -1051,7 +1100,7 @@ void() cheat = {
};
void() cheat_quad = {
if(deathmatch || coop) {
if((deathmatch || coop) && !sf_cheats) {
return;
}
@ -1123,13 +1172,13 @@ float(entity targ, entity attacker) obit_teledeath = {
};
float(entity targ, entity attacker) obit_teledeath2 = {
targ.frags = targ.frags - 1;
targ.frags--;
bprint("'s telefrag was deflected by satanic power\n");
return TRUE;
};
float(entity targ, entity attacker) obit_suicide = {
targ.frags = targ.frags - 1;
targ.frags--;
if(targ.weapon == IT_LIGHTNING && targ.waterlevel > 1) {
bprint(" discharges into the water\n");
@ -1150,7 +1199,7 @@ float(entity targ, entity attacker) obit_teamkill = {
float rnum;
rnum = random();
attacker.frags = attacker.frags - 1;
attacker.frags--;
if(rnum < 0.25) {
bprint(" mows down a teammate\n");
@ -1417,7 +1466,7 @@ float(entity targ, entity attacker) obit_trap = {
};
float(entity targ, entity attacker) obit_worldkill = {
targ.frags = targ.frags - 1;
targ.frags--;
if(attacker.flags & FL_MONSTER) {
return obit_monster(targ, attacker);
@ -1482,9 +1531,12 @@ Player entered the suicide command
============
*/
void() ClientKill = {
self.frags = self.frags - 1; // extra penalty
ClientObituary(self, self);
set_suicide_frame();
self.modelindex = modelindex_player;
respawn();
if(self.view_ofs != VEC_ORIGIN) {
reset_death_vel();
self.frags--; // extra penalty
ClientObituary(self, self);
set_suicide_frame();
self.modelindex = modelindex_player;
respawn();
}
};

View File

@ -1,5 +1,18 @@
// common.qc: common functions
const float PO2_LUT[25] = {
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216
};
float(float x, float y) bit_shift_right = {
return rint(x / PO2_LUT[y]);
};
float(float x, float y) bit_shift_left = {
return rint(x * PO2_LUT[y]);
};
float() crandom = {
return 2 * (random() - 0.5);
};

View File

@ -554,10 +554,11 @@ enum {
};
enum {
WEAPON_SHOTGUN = 1,
WEAPON_ROCKET = 2,
WEAPON_SPIKES = 4,
WEAPON_BIG = 8,
SF_CHEATS = 1 << 0,
SF_LIVES_BEG = 1,
SF_LIVES_END = 3,
SF_LIVES_MSK = 0x0E,
SF_DIST_AMMO = 1 << 4,
};
#pragma noref 0
@ -604,6 +605,13 @@ string nextmap;
entity sight_entity;
float sight_entity_time;
// super co-op additions
float sf_cheats;
float sf_lives;
float sf_dist_ammo;
float player_respawned;
// fields --------------------------------------------------------------------|
// world fields
@ -720,6 +728,7 @@ float sight_entity_time;
// super co-op additions
.float pronoun;
.float lives;
// functions -----------------------------------------------------------------|

View File

@ -118,9 +118,8 @@ float(entity e, float healamount, float ignore) T_Heal = {
/*QUAKED item_health(.3 .3 1) (0 0 0) (32 32 32) rotten megahealth
Health box. Normally gives 25 points.
Rotten box heals 5-10 points,
megahealth will add 100 health, then
rot you down to your maximum health limit,
Rotten box heals 15 points,
Megahealth will add 100 health, then rot you down to your maximum health limit,
one point per second.
*/

View File

@ -179,9 +179,16 @@ void() worldspawn = {
};
void() StartFrame = {
teamplay = cvar("teamplay");
skill = cvar("skill");
float temp1flag;
teamplay = cvar("teamplay");
skill = cvar("skill");
temp1flag = cvar("temp1");
framecount = framecount + 1;
sf_cheats = temp1flag & SF_CHEATS;
sf_lives = bit_shift_right(temp1flag & SF_LIVES_MSK, SF_LIVES_BEG);
sf_dist_ammo = temp1flag & SF_DIST_AMMO;
};
/*

6
todo
View File

@ -1,3 +1,7 @@
bugs:
enforcers are broken
refactoring:
add expansion pack entities
@ -6,7 +10,6 @@ rename all functions to be lower_underscore
core features:
distributed ammo
lives counting
useful features:
@ -27,4 +30,5 @@ done:
corpse pickups have keys
custom pronouns
lives counting
no friendly fire