diff --git a/source/ai.qc b/source/ai.qc index cfd8130..fccbb5f 100644 --- a/source/ai.qc +++ b/source/ai.qc @@ -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 { diff --git a/source/boss.qc b/source/boss.qc index ca75292..71f634e 100644 --- a/source/boss.qc +++ b/source/boss.qc @@ -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(); } }; diff --git a/source/client.qc b/source/client.qc index 4f5970c..c3dba8a 100644 --- a/source/client.qc +++ b/source/client.qc @@ -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) && !(temp1flag & SF_CHEATS)) { + if((deathmatch || coop) && !sf_cheats) { return; } @@ -1051,7 +1100,7 @@ void() cheat = { }; void() cheat_quad = { - if((deathmatch || coop) && !(temp1flag & SF_CHEATS)) { + 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(); + } }; diff --git a/source/common.qc b/source/common.qc index 2f55397..09383ed 100644 --- a/source/common.qc +++ b/source/common.qc @@ -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); }; diff --git a/source/defs.qc b/source/defs.qc index f0df59a..edf4244 100644 --- a/source/defs.qc +++ b/source/defs.qc @@ -555,9 +555,9 @@ enum { enum { SF_CHEATS = 1 << 0, - SF_LIVES_BEG = 1 << 1, - SF_LIVES_END = 1 << 3, - SF_LIVES_FLG = 0x0E, + SF_LIVES_BEG = 1, + SF_LIVES_END = 3, + SF_LIVES_MSK = 0x0E, SF_DIST_AMMO = 1 << 4, }; #pragma noref 0 @@ -605,7 +605,12 @@ string nextmap; entity sight_entity; float sight_entity_time; -float temp1flag; +// super co-op additions +float sf_cheats; +float sf_lives; +float sf_dist_ammo; + +float player_respawned; // fields --------------------------------------------------------------------| @@ -723,6 +728,7 @@ float temp1flag; // super co-op additions .float pronoun; +.float lives; // functions -----------------------------------------------------------------| diff --git a/source/world.qc b/source/world.qc index d7bb413..5d9036b 100644 --- a/source/world.qc +++ b/source/world.qc @@ -179,10 +179,16 @@ void() worldspawn = { }; void() StartFrame = { + 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; }; /* diff --git a/todo b/todo index 0e3c77e..179af2e 100644 --- a/todo +++ b/todo @@ -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