#include "common.qh" #include "damage.qh" #include "mdl.qh" #include "client.qh" #include "server.qh" #include "misc.qh" #include "items.qh" #include "weapon.qh" #include "equip.qh" #include "act.qh" #include "act_player.qh" #include "override.qh" void() act_player_init = { precache_sound("player/plyrjmp8.wav"); precache_sound("player/h2ojump.wav"); precache_sound("player/land.wav"); precache_sound("player/land2.wav"); precache_sound("player/inh2o.wav"); precache_sound("player/slimbrn2.wav"); precache_sound("player/inlava.wav"); precache_sound("misc/water1.wav"); precache_sound("misc/water2.wav"); precache_sound("player/drown1.wav"); precache_sound("player/drown2.wav"); precache_sound("player/lburn1.wav"); precache_sound("player/lburn2.wav"); precache_sound("player/gasp1.wav"); precache_sound("player/gasp2.wav"); precache_sound("misc/outwater.wav"); precache_sound("player/pain1.wav"); precache_sound("player/pain2.wav"); precache_sound("player/pain3.wav"); precache_sound("player/pain4.wav"); precache_sound("player/pain5.wav"); precache_sound("player/pain6.wav"); precache_sound("player/death1.wav"); precache_sound("player/death2.wav"); precache_sound("player/death3.wav"); precache_sound("player/death4.wav"); precache_sound("player/death5.wav"); precache_sound("player/gib.wav"); precache_sound("player/udeath.wav"); precache_sound("player/h2odeath.wav"); precache_model("progs/backpack.mdl"); precache_sound("weapons/lock4.wav"); precache_sound("items/itembk2.wav"); }; // ======================================================================== void(float scl) player_throw_ammo = { local entity player; player = self; self = spawn("BACKPACK"); if (scl > 1) { /* FIXME: This is actually a hack to avoid powerups */ #define qg(eid) \ if (equip_query(player, eid)) \ equip_grant(self, eid) \ qg(EQUIPID_SUPER_SHOTGUN); qg(EQUIPID_NAILGUN); qg(EQUIPID_SUPER_NAILGUN); qg(EQUIPID_GRENADE_LAUNCHER); qg(EQUIPID_ROCKET_LAUNCHER); qg(EQUIPID_LIGHTNING_GUN); #undef qg player.itemfield_1 = 0; player.itemfield_2 = 0; player.itemfield_3 = 0; player.itemfield_4 = 0; scl = 1; } self.ammo_shells = player.ammo_shells * scl; player.ammo_shells -= self.ammo_shells; self.ammo_nails = player.ammo_nails * scl; player.ammo_nails -= self.ammo_nails; self.ammo_rockets = player.ammo_rockets * scl; player.ammo_rockets -= self.ammo_rockets; self.ammo_cells = player.ammo_cells * scl; player.ammo_cells -= self.ammo_cells; if (!self.ammo_shells && !self.ammo_nails && !self.ammo_rockets && !self.ammo_cells && !self.itemfield_1 && !self.itemfield_2 && !self.itemfield_3 && !self.itemfield_4) { remove(self); self = player; return; } self.origin = center(player); self.movetype = MOVETYPE_TOSS; self.velocity_x = crandom()*100; self.velocity_y = crandom()*100; self.velocity_z = 200; self.mins = '-16 -16 0'; self.maxs = '16 16 56'; self.model = "progs/backpack.mdl"; self.noise2 = "weapons/lock4.wav"; self.noise7 = "items/itembk2.wav"; self.wait = -1; self.think = SUB_remove; self.nextthink = time + 20 + random()*20; item_generic(); self = player; }; void() player_die = { local float r; if (self.health > CONFIG_GIB_HEALTH) { if (self.waterlevel >= 3) { self.noise = "player/h2odeath.wav"; } else { r = random()*5; if (r < 1) self.noise = "player/death1.wav"; else if (r < 2) self.noise = "player/death2.wav"; else if (r < 3) self.noise = "player/death3.wav"; else if (r < 4) self.noise = "player/death4.wav"; else self.noise = "player/death5.wav"; } sound(self, CHAN_VOICE, self.noise, 1, ATTN_NONE); player_throw_ammo(2); mdl_bodyque_and_func(MDL_FUNC_DIE, floor(random()*5)); } else { r = random()*2; if (r < 1) self.noise = "player/gib.wav"; else self.noise = "player/udeath.wav"; sound(self, CHAN_VOICE, self.noise, 1, ATTN_NONE); mdl_bodyque_and_func(MDL_FUNC_GIB, 0); } makevectors(self.angles); /* Not v_angle! */ self.velocity = (v_forward*-200) + (v_up*100); if (self.v_angle_x < 75 && self.v_angle_x > -75) { self.angles = self.v_angle; self.angles_x += 20; self.fixangle = TRUE; } /* FIXME: Hack. */ setorigin(self, vieworigin(self)); RemoveClientFromGame(); }; // ======================================================================== float(float d) _player_takedamage_core = { self.health -= d; self.dmg_take += d; if (floor(self.health) <= 0) { deathmsg_display(); player_die(); } else { deathmsg_nodisplay(); self.mdl_func(MDL_FUNC_PAIN, d); } return TRUE; }; float(float d) player_takedamage = { local float r, taked; damage_push(d); taked = damage_armor(d); self.dmg_save = self.dmg_save + (d - taked); if (taked <= 0) return FALSE; if (self.pain_finished < time) { if ((self.mdl_mod & MDL_MOD_SWIM_IN) && self.air_finished < (time + random()*9)) { r = random()*2; if (r < 1) self.noise = "player/drown2.wav"; else self.noise = "player/drown1.wav"; } else { r = random()*5; if (r < 1) self.noise = "player/pain1.wav"; else if (r < 2) self.noise = "player/pain2.wav"; else if (r < 3) self.noise = "player/pain3.wav"; else if (r < 4) self.noise = "player/pain4.wav"; else if (r < 5) self.noise = "player/pain5.wav"; else self.noise = "player/pain6.wav"; } sound(self, CHAN_VOICE, self.noise, 1, ATTN_NORM); self.pain_finished = time + 1; } return _player_takedamage_core(taked); }; // ======================================================================== float(float d) _player_takedamage_drown = { local float r; if (d <= 0) return FALSE; if (self.pain_finished < time) { r = random()*2; if (r < 1) self.noise = self.noise3; else self.noise = self.noise4; sound(self, CHAN_VOICE, self.noise, 1, ATTN_NORM); self.pain_finished = time + 1; } return _player_takedamage_core(d); }; float(float d) _player_takedamage_melt = { local float r, taked; taked = damage_armor(d); self.dmg_save += d - taked; if (taked <= 0) return FALSE; if (self.pain_finished < time) { r = random()*2; if (r < 1) self.noise = self.noise3; else self.noise = self.noise4; sound(self, CHAN_VOICE, self.noise, 1, ATTN_NORM); self.pain_finished = time + 1; } return _player_takedamage_core(taked); }; float(float d, float(float d) damg_func, void() deathmessage) _player_damage_custom = { local float ret; override_set_th_takedamage(self, damg_func); ret = damage(self, self, world, d, deathmessage); override_set_th_takedamage(self, player_takedamage); return ret; }; // ======================================================================== void() _deathmsg_player_liquid = { local string def_nname; local float r; def_nname = name(self); if (self.watertype == CONTENT_WATER) { r = random()*2; if (r < 1) bprint(PRINT_DEATH, def_nname, " sleeps with the fishes.\n"); else bprint(PRINT_DEATH, def_nname, " hunts for air.\n"); } else if (self.watertype == CONTENT_SLIME) { r = random()*2; if (r < 1) bprint(PRINT_DEATH, def_nname, " can't exist on slime alone.\n"); else bprint(PRINT_DEATH, def_nname, " floats in slime.\n"); } else if (self.watertype == CONTENT_LAVA) { r = random()*3; if (r < 1) bprint(PRINT_DEATH, def_nname, " swam in a volcano.\n"); else if (r < 2) bprint(PRINT_DEATH, def_nname, " turned into hot slag.\n"); else bprint(PRINT_DEATH, def_nname, " parties with Chthon.\n"); } else { bprint(PRINT_DEATH, def_nname, " gulps it down.\n"); } }; void() _deathmsg_player_fall = { local string def_name; local float r; def_name = name(self); r = random()*3; if (r < 1) bprint(PRINT_DEATH, def_name, " landed head-first.\n"); else if (r < 2) bprint(PRINT_DEATH, def_name, " cratered.\n"); else bprint(PRINT_DEATH, def_name, " took a nose dive into the ground.\n"); }; // ======================================================================== /* This is so stupid. */ float() _player_jump = { if (!self.button2) { self.flags |= FL_JUMPRELEASED; return FALSE; } if (!(self.flags & FL_ONGROUND)) return FALSE; if (!(self.flags & FL_JUMPRELEASED)) return FALSE; self.mdl_func(MDL_FUNC_JUMP, 0); self.flags &= ~FL_JUMPRELEASED; self.button2 = 0; return TRUE; }; void() _player_water_jump = { if (self.waterlevel <= 1) { if (_player_jump()) sound(self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM); return; } if (self.waterlevel <= 2) { if (_player_jump()) sound(self, CHAN_BODY, "misc/water2.wav", 1, ATTN_NORM); } /* Yeah. The engine is a piece of crap. */ if (self.button2) { if (self.flags & FL_ONGROUND) { self.flags = self.flags - FL_ONGROUND; self.origin_z = self.origin_z + 1; } } }; /* This is called every frame... */ void() player_prethink = { /* Gasp for air if we weren't swimming.. /* Checking here rather than later, with mdl_mod is a HACK */ /* It avoids the gasp when the player hasn't communicated with the server in a while (it happens..) */ if (self.waterlevel < 3) { if (self.mdl_mod&MDL_MOD_SWIM_IN) { if (self.air_finished < time) sound(self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM); else if (self.air_finished < (time + 9)) sound(self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM); } self.air_finished = time + 12; } /* Let the MDL (and us) know we're swimming */ self.mdl_mod &= ~MDL_MOD_SWIM; if (self.waterlevel >= 3) self.mdl_mod |= MDL_MOD_SWIM_IN; else if (self.waterlevel >= 1) self.mdl_mod |= MDL_MOD_SWIM_OVER; /* Handle environmental damage */ if (self.watertype == CONTENT_SLIME) { self.noise3 = "player/lburn1.wav"; self.noise4 = "player/lburn2.wav"; _player_damage_custom(4*self.waterlevel*frametime, _player_takedamage_melt, _deathmsg_player_liquid); } else if (self.watertype == CONTENT_LAVA) { self.noise3 = "player/lburn1.wav"; self.noise4 = "player/lburn2.wav"; _player_damage_custom(50*self.waterlevel*frametime, _player_takedamage_melt, _deathmsg_player_liquid); } /* Try to breathe :) */ if (self.air_finished < time) { local float damg; damg = (time - self.air_finished) * 0.2; if (damg > 1.5) damg = 1.5; self.noise3 = "player/drown1.wav"; self.noise4 = "player/drown2.wav"; _player_damage_custom(damg, _player_takedamage_drown, _deathmsg_player_liquid); } /* Enter/exit water, swim sound */ if (!self.waterlevel) { if (self.flags & FL_INWATER) { sound(self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM); self.flags = self.flags - FL_INWATER; } if (_player_jump()) sound(self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM); } else { if (!(self.flags & FL_INWATER)) { if (self.watertype == CONTENT_WATER) self.noise = "player/inh2o.wav"; else if (self.watertype == CONTENT_SLIME) self.noise = "player/slimbrn2.wav"; else if (self.watertype == CONTENT_LAVA) self.noise = "player/inlava.wav"; sound(self, CHAN_BODY, self.noise, 1, ATTN_NORM); self.flags = (self.flags & ~FL_JUMPRELEASED) | FL_INWATER; self.water_finished = 0; } if (self.mdl_mod&MDL_MOD_SWIM) { local float increment, mxspeed; increment = vlen(self.origin - self.pos1); mxspeed = calc_max_speed(self); if (increment > mxspeed) increment = mxspeed; self.water_finished += increment; if (self.water_finished >= mxspeed) { self.water_finished = 0; if (random() < 0.5) self.noise = "misc/water1.wav"; else self.noise = "misc/water2.wav"; sound(self, CHAN_BODY, self.noise, 1, ATTN_NORM); } } _player_water_jump(); } self.pos1 = self.origin; #if DEBUG debug_impulse(); #endif weapon_player_impulse(); }; /* This is not called every frame */ void() player_think = { /* The player does not think. */ }; /* FIXME: We should check to see if we actually collided. I don't know how to at the moment... */ void() player_velocity_damage = { local vector vel, dir; local float v1, v2; vel = self.movedir; self.movedir = self.velocity; /* Did we just teleport? */ if (self.teleport_time) return; /* Did we slow down a lot? */ v1 = vlen(vel); v2 = vlen(self.velocity); if (v2 >= v1) return; vel = vel - self.velocity; vel_z = vel_z * 2; v1 = vlen(vel); /* Find the largest magnitude dir */ vel_x = fabs(vel_x); vel_y = fabs(vel_y); vel_z = fabs(vel_z); dir = '0 0 1'; if (vel_y > (vel*dir)) dir = '0 1 0'; if (vel_x > (vel*dir)) dir = '1 0 0'; /* Play sounds, apply damage */ if (v1 > CONFIG_LAND_SOUND) { if (dir_z && self.watertype == CONTENT_WATER) { self.noise = "player/h2ojump.wav"; } else if (v1 > CONFIG_LAND_HURT) { self.noise = "player/land2.wav"; self.pain_finished = time + 1; damage(self, self, world, 5, _deathmsg_player_fall); } else { self.noise = "player/land.wav"; } sound(self, CHAN_VOICE, self.noise, 1, ATTN_NORM); } }; /* This is also called every frame... */ void() player_postthink = { player_velocity_damage(); self.items &= ~(IT_ARMOR1|IT_ARMOR2|IT_ARMOR3); if (self.armortype >= 0.8) self.items |= IT_ARMOR3; else if (self.armortype >= 0.6) self.items |= IT_ARMOR2; else if (self.armortype >= 0.3) self.items |= IT_ARMOR1; update_weapon_flags(); weapon_set_ammo(); }; // ======================================================================== void(float nitem, string str1, string str2) _item_xprint_strs = { if (!is_cl(self)) return; if (!str1) return; if (nitem == 1) { stuffcmd(self, "bf\n"); xprint_start(self, PRINT_LOW); xprint_str("You get "); } else if (nitem > 1) xprint_str(", "); xprint_str(str1); if (str2) xprint_str(str2); }; void(float nitem, string str1, string str2) _item_xprint_strs_last = { if (!is_cl(self)) return; if (!str1) return; if (nitem == 1) { stuffcmd(self, "bf\n"); xprint_start(self, PRINT_LOW); xprint_str("You get "); } if (nitem == 2) xprint_str(" and "); else if (nitem > 2) xprint_str(", and "); xprint_str(str1); if (str2) xprint_str(str2); xprint_str("\n"); }; float nitem; string str1, str2; void(.float fld, float max, string sing, string plur) _player_takefield = { local float new, diff; new = increase_bound(self.fld, other.fld, max); if (new > 999) new = 999; if (new > self.fld) { diff = floor(new) - floor(self.fld); if (!diff) diff = new - self.fld; _item_xprint_strs(nitem, str1, str2); str1 = ftos(diff); if (diff == 1) str2 = sing; else str2 = plur; nitem++; self.fld = new; } }; float() player_takeitem = { local float eid; nitem = 0; str1 = NIL; str2 = NIL; while ((eid = equip_iter(other)) != -1) { if (equip_query(self, eid)) continue; equip_grant(self, eid); _item_xprint_strs(nitem, str1, str2); str1 = "the "; str2 = equip_id_to_string(eid); nitem++; } _player_takefield(ammo_shells, self.max_ammo_shells, " shell", " shells"); _player_takefield(ammo_nails, self.max_ammo_nails, " nail", " nails"); _player_takefield(ammo_rockets, self.max_ammo_rockets, " rocket", " rockets"); _player_takefield(ammo_cells, self.max_ammo_cells, " cell", " cells"); if (other.armorvalue <= self.max_armor) { if ((other.armorvalue*other.armortype) > (self.armorvalue*self.armortype)) { self.armorvalue = other.armorvalue; self.armortype = other.armortype; _item_xprint_strs(nitem, str1, str2); str2 = NIL; if (self.armortype >= 0.8) str1 = "strong armor"; else if (self.armortype >= 0.6) str1 = "medium armor"; else if (self.armortype >= 0.3) str1 = "light armor"; else str1 = "really crappy armor"; nitem++; } } _player_takefield(health, self.max_health, " health", " health"); _item_xprint_strs_last(nitem, str1, str2); if (nitem) return TRUE; return FALSE; }; // ===================================================================== // void() act_setup_player = { act_setup(); self.movedir = '0 0 0'; self.solid = SOLID_SLIDEBOX; self.takedamage = DAMAGE_AIM; self.movetype = MOVETYPE_WALK; self.deadflag = DEAD_NO; self.gravity = 1; self.maxspeed = sv_maxspeed; self.mass = 1000; self.air_finished = time + 12; self.pain_finished = 0; self.water_finished = 0; override_set_th_takeitem(self, player_takeitem); override_set_th_takedamage(self, player_takedamage); override_set_prethink(self, player_prethink); override_set_actthink(self, player_think); override_set_postthink(self, player_postthink); };