.float regurgitate_prepare; .float system_delay, swallow_delay, digest_button_delay, regurgitate_button_delay; .float complain_swallow; const float complain_delay = 1; const float button_delay = 0.5; const float steptime = 0.1; const float system_delay_time = 0.1; .float vore_oldmovetype, vore_oldsolid, vore_oldstomachload, vore_oldview_ofs_z; entity Swallow_distance_check() { // check if we can swallow a player instead of firing our weapon vector w_shotorg, w_shotdir; w_shotorg = self.origin + self.view_ofs; w_shotdir = v_forward; WarpZone_traceline_antilag(self, w_shotorg, w_shotorg + w_shotdir * cvar("g_balance_vore_swallow_range"), FALSE, self, ANTILAG_LATENCY(self)); if(trace_fraction < 1) if(trace_ent.classname == "player") return trace_ent; return world; } float Swallow_condition_check(entity prey) { // checks the necessary conditions for swallowing another player if(prey.classname == "player" && prey.eater.classname != "player" && prey.deadflag == DEAD_NO) // we can't swallow someone who's already in someone else's stomach if(self.classname == "player" && self.eater.classname != "player" && self.deadflag == DEAD_NO) // we can't swallow players while inside someone's stomach ourselves if not(vlen(self.velocity) > cvar("g_balance_vore_regurgitate_velocitylimit")) { if(self.stomach_load >= cvar("g_balance_vore_swallow_limit")) { if(time > self.complain_swallow) { play2(self, "weapons/unavailable.wav"); sprint(self, strcat("You cannot swallow more than ^2", cvar_string("g_balance_vore_swallow_limit"), "^7 players at a time\n")); self.complain_swallow = time + complain_delay; } return FALSE; } if(cvar("g_vore_biggergut")) if(prey.stomach_load > self.stomach_load) { if(time > self.complain_swallow) { play2(self, "weapons/unavailable.wav"); sprint(self, "You cannot swallow someone with a bigger stomach than yours\n"); self.complain_swallow = time + complain_delay; } return FALSE; } return TRUE; } return FALSE; } // make the camera smoothly lower itself when we get swallowed // the target we are going for is from normal view offset to half of the view offset (because half is the best positioning of the view for the stomach model) .float cameraeffect_current, cameraeffect_target; void Vore_CameraEffect_Set(entity e) { e.cameraeffect_current = 1; e.cameraeffect_target = 2; } void Vore_CameraEffect_Apply() { if(self.eater.classname != "player") return; if(self.cvar_cl_vore_cameraspeed) { local float step; step = self.cvar_cl_vore_cameraspeed * frametime; // not sure if these maths are good, as the effect should be smoother if(self.cameraeffect_current >= self.cameraeffect_target + step) self.cameraeffect_current -= step; else if(self.cameraeffect_current <= self.cameraeffect_target - step) self.cameraeffect_current += step; } else self.cameraeffect_current = self.cameraeffect_target; self.view_ofs_z = self.vore_oldview_ofs_z / self.cameraeffect_current; } void Vore_Weight_apply(entity e) { // apply stomach weight that makes you heavier the more you eat // slowing the player is applied in cl_physics.qc if(e.stomach_load != e.vore_oldstomachload) e.gravity += 1 + (e.stomach_load * cvar("g_balance_vore_weight_gravity") - e.vore_oldstomachload); if(e.gravity == 0) e.gravity = 0.00001; // 0 becomes 1 for .gravity, so do this to allow 0 gravity e.vore_oldstomachload = e.stomach_load; } .entity pusher; .float pushltime; void Vore_Swallow(entity e) { // this player is beening swallowed by another player, apply the proper changes e.vore_oldmovetype = e.movetype; e.vore_oldsolid = e.solid; e.vore_oldview_ofs_z = e.view_ofs_z; e.eater = self; setorigin(e, e.eater.origin); e.velocity = '0 0 0'; e.movetype = MOVETYPE_FOLLOW; e.solid = SOLID_NOT; e.alpha = -1; // best way of hiding / showing the eaten player e.aiment = e.eater; // follow the predator. Is automatically unset // drop keys (KH) and flags (CTF) when we get swallowed kh_Key_DropAll(e, FALSE); if(e.flagcarried) DropFlag(e.flagcarried, world, e.eater); Vore_CameraEffect_Set(e); if(stov(cvar_string("g_vore_regurgitatecolor_released"))) e.colormod = stov(cvar_string("g_vore_regurgitatecolor_released")); if(e.team == e.eater.team && teamplay) { centerprint(e, "^3You have been swallowed by a team mate, don't kick!"); centerprint(e.eater, "^3You have swallowed a team mate, use caution!"); } PlayerSound(e.eater, playersound_swallow, CHAN_PAIN, VOICETYPE_PLAYERSOUND); setanim(e.eater, e.eater.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing / regurgitating e.eater.stomach_load += 1; e.eater.regurgitate_prepare = 0; Vore_Weight_apply(e.eater); // block firing for a small amount of time when voring, or we'll be firing the next frame after we swallow e.eater.weapon_delay = time + button_delay; e.eater.swallow_delay = time + cvar("g_balance_vore_swallow_delay"); e.system_delay = e.eater.system_delay = time + system_delay_time; } void Vore_Regurgitate(entity e) { // this player is being released from their predator, apply the proper changes e.movetype = e.vore_oldmovetype; if(e.health > 0) // leave SOLID_NOT for dead bodies e.solid = e.vore_oldsolid; e.view_ofs_z = e.vore_oldview_ofs_z; e.alpha = default_player_alpha; // best way of hiding / showing the eaten player // velocities local vector oldforward, oldright, oldup; oldforward = v_forward; oldright = v_right; oldup = v_up; makevectors(e.eater.v_angle); e.velocity = v_forward * cvar("g_balance_vore_regurgitate_force"); e.eater.velocity += -v_forward * cvar("g_balance_vore_regurgitate_eaterforce"); v_forward = oldforward; v_right = oldright; v_up = oldup; e.pusher = e.eater; // so we can frag players by regurgitating them in deadly pits e.pushltime = time + cvar("g_maxpushtime"); PlayerSound(e.eater, playersound_regurgitate, CHAN_PAIN, VOICETYPE_PLAYERSOUND); setanim(e.eater, e.eater.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing / regurgitating pointparticles(particleeffectnum("regurgitate"), e.eater.origin, '0 0 0', 1); e.eater.stomach_load -= 1; e.eater.regurgitate_prepare = 0; e.eater.swallow_delay = time + cvar("g_balance_vore_swallow_delay"); Vore_Weight_apply(e.eater); e.system_delay = e.eater.system_delay = time + system_delay_time; e.eater = world; } void Vore_Gurglesound(); void Vore_Disconnect() { // frees prey from their predators when someone disconnects or goes spectating // prey disconnects or goes spectating while inside someone's belly: if(self.eater.classname == "player") { self.view_ofs_z = self.vore_oldview_ofs_z; self.eater.stomach_load -= 1; Vore_Weight_apply(self.eater); self.eater = world; } // pred disconnects or goes spectating with players in their belly: else if(self.stomach_load > 0) { entity head; FOR_EACH_PLAYER(head) { if(head.eater == self) Vore_Regurgitate(head); } Vore_Gurglesound(); // stop the gurgling sound } } .float digestion_step; void Vore_Digest() { // apply digestion to prey if(time > self.eater.digestion_step + steptime) { Damage(self, self.eater, self.eater, cvar("g_balance_vore_digestion_damage"), DEATH_DIGESTION, self.origin, '0 0 0'); if(cvar("g_balance_vore_digestion_vampire") && self.eater.health < cvar("g_balance_vore_digestion_vampire_stable")) self.eater.health += cvar("g_balance_vore_digestion_vampire"); if (self.eater.digestsound_finished < time) { self.eater.digestsound_finished = time + 0.5; PlayerSound (self.eater, playersound_digest, CHAN_PLAYER, VOICETYPE_PLAYERSOUND); } self.eater.digestion_step = time; } if(self.health <= 0) if(stov(cvar_string("g_vore_regurgitatecolor_digested"))) self.colormod = stov(cvar_string("g_vore_regurgitatecolor_digested")); } .float teamheal_step; void Vore_Teamheal() { if(cvar("g_balance_vore_teamheal") && self.health < cvar("g_balance_vore_teamheal_stable")) if(time > self.teamheal_step + steptime) { self.health += cvar("g_balance_vore_teamheal"); self.teamheal_step = time; } } .float stomachkick_delay; void Vore_StomachKick() { // allows prey to kick the predator's stomach and do some damage or attempt to escape if(self.eater.classname != "player") return; if(time > self.stomachkick_delay) { float damage; damage = ceil(random() * (cvar("g_balance_vore_kick_damage_max") - cvar("g_balance_vore_kick_damage_min")) + cvar("g_balance_vore_kick_damage_min")); Damage(self.eater, self, self, damage, DEATH_STOMACHKICK, self.eater.origin, '0 0 0'); sound(self.eater, CHAN_PROJECTILE, "weapons/stomachkick.ogg", VOL_BASE, ATTN_NORM); if(cvar("g_balance_vore_kick_escapeprobability") >= random()) Vore_Regurgitate(self); self.stomachkick_delay = time + cvar("g_balance_vore_kick_delay"); } } void Vore_StomachLeave() { // allows players to get out of their predator at will in some circumstances, such as team mates float canleave; canleave = (teams_matter && self.team == self.eater.team); // currently, the only circumstance where you can use this if for team mates if(canleave) Vore_Regurgitate(self); else if(time > self.complain_swallow) { play2(self, "weapons/unavailable.wav"); sprint(self, strcat("You can't willingly get out of ", self.eater.netname, "\n")); self.complain_swallow = time + complain_delay; } } .float gurglesound_finished, gurglesound_oldstomachload; void Vore_Gurglesound() { if(time > self.gurglesound_finished || self.gurglesound_oldstomachload != self.stomach_load) { GlobalSound(self.playersound_gurgle, CHAN_TRIGGER, VOICETYPE_GURGLE); self.gurglesound_finished = time + 11; // yes, hard coded sound length. I know it's bad but what can I do? self.gurglesound_oldstomachload = self.stomach_load; } } void Vore() { // if we are free, show our stomach load on the HUD. Otherwise, show the predator's if(self.eater.classname == "player") { self.stat_stomachload = self.eater.stomach_load; self.stat_digesting = self.eater.digesting; self.stat_eaten = num_for_edict(self.eater); } else { self.stat_stomachload = self.stomach_load; self.stat_digesting = self.digesting; self.stat_eaten = 0; } // skip the vore system under some circumstances if(time < game_starttime) { Vore_Disconnect(); return; } if(self.spectatee_status) return; if(time < self.system_delay) return; // -------------------------------- // Code that addresses predators: // -------------------------------- entity prey; prey = Swallow_distance_check(); // attempt to swallow our new prey if there's any in range if(self.BUTTON_ATCK && !self.BUTTON_REGURGITATE && self.swallow_delay < time) if(Swallow_condition_check(prey)) Vore_Swallow(prey); // start / stop digestion on command, if the player has someone in their stomach if(self.BUTTON_DIGEST) { if(self.stomach_load) { if(time > self.digest_button_delay) { self.digesting = !self.digesting; self.digest_button_delay = time + button_delay; } } else if(time > self.complain_swallow) { play2(self, "weapons/unavailable.wav"); sprint(self, "There is nothing to digest\n"); self.complain_swallow = time + complain_delay; } } if(!self.stomach_load) self.digesting = FALSE; // release players from this player's stomach on command if(self.BUTTON_REGURGITATE) { if(self.stomach_load) { if(time > self.regurgitate_button_delay) { self.regurgitate_prepare = time + cvar("g_balance_vore_regurgitate_delay"); PlayerSound(self, playersound_regurgitate_prepare, CHAN_VOICE, VOICETYPE_PLAYERSOUND); self.regurgitate_button_delay = time + button_delay; } } else if(time > self.complain_swallow) { play2(self, "weapons/unavailable.wav"); sprint(self, "There is nothing to regurgitate\n"); self.complain_swallow = time + complain_delay; } } if(cvar("g_vore_gurglesound")) Vore_Gurglesound(); // -------------------------------- // Code that addresses the prey: // -------------------------------- if(self.eater.classname != "player") return; if(self.eater.deadflag || self.deadflag) Vore_Regurgitate(self); else if(self.eater.eater.classname == "player") // don't allow a player inside a player inside another player :) { entity targeteater, oldself; targeteater = self.eater.eater; Vore_Regurgitate(self); if(random() < cvar("g_vore_stealprey")) if(Swallow_condition_check(self)) { oldself = self; self = targeteater; Vore_Swallow(oldself); self = oldself; } } else if(vlen(self.eater.velocity) > cvar("g_balance_vore_regurgitate_velocitylimit")) Vore_Regurgitate(self); // apply delayed regurgitating if(self.eater.regurgitate_prepare && time > self.eater.regurgitate_prepare) { self.eater.regurgitate_prepare = 0; self.eater.complain_swallow = time + complain_delay; Vore_Regurgitate(self); } if(self.eater.digesting == TRUE) Vore_Digest(); if(teams_matter && self.team == self.eater.team) Vore_Teamheal(); if(self.BUTTON_ATCK) Vore_StomachKick(); else if(self.BUTTON_ATCK2) Vore_StomachLeave(); Vore_CameraEffect_Apply(); }