game-source/klik/act/act_player.qc
2003-10-27 22:27:29 +00:00

672 lines
16 KiB
C++

#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);
};