]> de.git.xonotic.org Git - voretournament/voretournament.git/blobdiff - data/qcsrc/server/vore.qc
Goodbye old vixen model. We now have a brand new fox player mesh of much higher quali...
[voretournament/voretournament.git] / data / qcsrc / server / vore.qc
index b53642b1461c761396c642e33e8416ae93e1a5d5..146a763a3a19c5ac73b175b7571ca4e5fc06be5d 100644 (file)
@@ -1,4 +1,3 @@
-.float regurgitate_prepare;\r
 .float stomachkick_delay, system_delay, action_delay, digest_button_delay_time, regurgitate_button_delay_time;\r
 .float complain_vore;\r
 .float vore_oldmovetype, vore_oldsolid;\r
@@ -6,7 +5,6 @@
 const float system_delay_time = 0.1;\r
 const float complain_delay_time = 1;\r
 const float button_delay_time = 0.5;\r
-const float steptime = 0.1;\r
 \r
 entity Swallow_player_check()\r
 {\r
@@ -89,6 +87,18 @@ float Stomach_TeamMates_check(entity pred)
        return FALSE;\r
 }\r
 \r
+float Stomach_HasLivePrey(entity pred)\r
+{\r
+       entity head;\r
+       FOR_EACH_PLAYER(head)\r
+       {\r
+               if(head.predator == pred)\r
+               if(head.deadflag == DEAD_NO)\r
+                       return TRUE;\r
+       }\r
+       return FALSE;\r
+}\r
+\r
 float Vore_CanLeave()\r
 {\r
        if(self.stat_eaten)\r
@@ -109,7 +119,6 @@ void Vore_SetPreyPositions(entity pred)
        // pred is the predator and head is the prey\r
 \r
        local entity head;\r
-       local vector origin_apply;\r
 \r
        // In order to allow prey to see each other in the stomach, we must position each occupant differently,\r
        // else all players overlap in the center. To do this, we use a random origin on all players in the same stomach.\r
@@ -117,18 +126,10 @@ void Vore_SetPreyPositions(entity pred)
        {\r
                if(head.predator == pred)\r
                {\r
-                       origin_apply_x = PL_PREY_VIEW_OFS_x + crandom() * cvar("g_vore_neighborprey_distance");\r
-                       origin_apply_y = PL_PREY_VIEW_OFS_y + crandom() * cvar("g_vore_neighborprey_distance");\r
-                       origin_apply_z = PL_PREY_VIEW_OFS_z;\r
-\r
                        // since prey have their predators set as an aiment, view_ofs will specify the real origin of prey, not just the view offset\r
-                       head.view_ofs = origin_apply;\r
-                       head.view_ofs_z *= pred.scale; // stomach center depends on predator scale\r
-\r
-                       // change prey height based on scale\r
-                       float prey_height;\r
-                               prey_height = (head.scale - pred.scale) * cvar("g_healthsize_vore_pos");\r
-                       head.view_ofs_z += prey_height;\r
+                       head.view_ofs_x = PL_PREY_VIEW_OFS_x + crandom() * cvar("g_vore_neighborprey_distance");\r
+                       head.view_ofs_y = PL_PREY_VIEW_OFS_y + crandom() * cvar("g_vore_neighborprey_distance");\r
+                       head.view_ofs_z = PL_PREY_VIEW_OFS_z;\r
                }\r
        }\r
 }\r
@@ -136,30 +137,54 @@ void Vore_SetPreyPositions(entity pred)
 .float gurgle_oldstomachload;\r
 void Vore_GurgleSound()\r
 {\r
-       if(time > self.gurglesound_finished || self.gurgle_oldstomachload != self.stomach_load)\r
+       if(time > self.gurglesound_finished || (self.gurgle_oldstomachload != self.stomach_load && !self.digesting))\r
        {\r
-               GlobalSound(self.playersound_gurgle, CHAN_TRIGGER, VOICETYPE_GURGLE);\r
+               GlobalSound(self.playersound_gurgle, CHAN_TRIGGER, VOICETYPE_GURGLE, VOL_BASE);\r
+\r
+               // yes, hard coded sound length. I know it's bad but what can I do?\r
+               if(cvar("g_healthsize") && cvar("g_healthsize_pitch"))\r
+                       self.gurglesound_finished = time + 11 * pow(self.scale, -cvar("g_healthsize_pitch")); // modified sound pitch, based on player scale\r
+               else\r
+                       self.gurglesound_finished = time + 11; // yes, hard coded sound length. I know it's bad but what can I do?\r
 \r
-               self.gurglesound_finished = time + 11; // yes, hard coded sound length. I know it's bad but what can I do?\r
                self.gurgle_oldstomachload = self.stomach_load;\r
        }\r
 }\r
 \r
+void Vore_AutoDigest(entity e)\r
+{\r
+       // if the predator has the autodigest preference enabled, begin digesting new prey automatically\r
+\r
+       if(!cvar("g_vore_digestion") || e.digesting)\r
+               return;\r
+       if(g_rpg)\r
+               return; // RPG is choice based, so don't do things automatically there\r
+       if(e.stomach_load)\r
+               return; // don't start digestion if we already ate someone, as that means we manually disabled it after the first prey and want it off\r
+       if(clienttype(e) != CLIENTTYPE_REAL)\r
+               return; // this feature is only for players, not bots\r
+       if(Stomach_TeamMates_check(e))\r
+               return; // never begin automatic digestion if we've swallowed a team mate\r
+\r
+       e.digesting = TRUE;\r
+}\r
+\r
 void Vore_StomachLoad_Apply()\r
 {\r
        // apply stomach weight that makes you heavier and larger the more you eat\r
        // slowing the player is done in cl_physics.qc\r
+       if(self.classname != "player")\r
+               return;\r
 \r
        entity e;\r
-       float prey_mass;\r
+       float prey_mass, final_load;\r
 \r
        // apply the stomach capacity of the predator\r
        self.stomach_maxload = cvar("g_balance_vore_load_pred_capacity");\r
        if(cvar("g_healthsize"))\r
                self.stomach_maxload *= self.scale;\r
-       self.stomach_maxload = floor(self.stomach_maxload);\r
+       self.stomach_maxload = ceil(self.stomach_maxload);\r
 \r
-       self.stomach_load = 0; // start from zero\r
        FOR_EACH_PLAYER(e)\r
        {\r
                if(e.predator == self)\r
@@ -167,51 +192,47 @@ void Vore_StomachLoad_Apply()
                        prey_mass = cvar("g_balance_vore_load_prey_mass");\r
                        if(cvar("g_healthsize"))\r
                                prey_mass *= e.scale;\r
-                       self.stomach_load += floor(prey_mass);\r
+                       final_load += ceil(prey_mass);\r
+                       if(self.cvar_cl_vore_autodigest > 1)\r
+                               Vore_AutoDigest(self);\r
+               }\r
+       }\r
+       for(e = world; (e = find(e, classname, "consumable")); )\r
+       {\r
+               if(e.predator == self)\r
+               {\r
+                       final_load += ceil(e.dmg);\r
+                       if(self.cvar_cl_vore_autodigest > 0)\r
+                               Vore_AutoDigest(self);\r
                }\r
        }\r
+       self.stomach_load = final_load;\r
 \r
        // apply weight\r
-       self.gravity = 1 + (self.stomach_load / self.stomach_maxload) * cvar("g_balance_vore_load_pred_weight");\r
+       self.gravity = 1 * (cvar("g_healthsize") ? pow(self.scale, cvar("g_healthsize_weight")) : 1) + (self.stomach_load / self.stomach_maxload) * cvar("g_balance_vore_load_pred_weight");\r
        if(!self.gravity && self.stomach_load)\r
                self.gravity = 0.00001; // 0 becomes 1 for gravity, so do this to allow 0 gravity\r
 }\r
 \r
-void Vore_AutoDigest(entity e)\r
-{\r
-       // if the predator has the autodigest preference enabled, begin digesting new prey automatically\r
-\r
-       if(!cvar("g_vore_digestion") || e.digesting)\r
-               return;\r
-       if(!e.cvar_cl_vore_autodigest || clienttype(e) != CLIENTTYPE_REAL)\r
-               return; // this feature is only for players, not bots\r
-       if(e.stomach_load > 1)\r
-               return; // don't start digestion if we already ate someone, as that means we manually disabled it after the first prey and want it off\r
-       if(Stomach_TeamMates_check(e))\r
-               return; // never begin automatic digestion if we've swallowed a team mate\r
-\r
-       e.digesting = TRUE;\r
-}\r
-\r
 .entity swallow_model;\r
-float Vore_SwallowModel_CustomizeEntityForClient()\r
+float Vore_GulletModel_CustomizeEntityForClient()\r
 {\r
        // use the same system as the weapon model\r
 \r
        self.viewmodelforclient = self.owner;\r
-       self.alpha = self.owner.cvar_cl_vore_swallowmodel;\r
+       self.alpha = self.owner.cvar_cl_vore_gulletmodel;\r
 \r
        if(other.classname == "spectator")\r
        if(other.enemy == self.owner)\r
        {\r
                self.viewmodelforclient = other;\r
-               self.alpha = other.cvar_cl_vore_swallowmodel;\r
+               self.alpha = other.cvar_cl_vore_gulletmodel;\r
        }\r
 \r
        return TRUE;\r
 }\r
 \r
-void Vore_SwallowModel_Think()\r
+void Vore_GulletModel_Think()\r
 {\r
        // update the position of the swallow model to match our swallow progress\r
        float dist;\r
@@ -232,7 +253,7 @@ void Vore_SwallowModel_Think()
        self.nextthink = time;\r
 }\r
 \r
-void Vore_SwallowModel_Update(entity prey, entity pred)\r
+void Vore_GulletModel_Update(entity prey, entity pred)\r
 {\r
        // if we don't have a swallow model already, spawn one\r
        if(!prey.swallow_model)\r
@@ -243,16 +264,16 @@ void Vore_SwallowModel_Update(entity prey, entity pred)
                //prey.swallow_model.effects |= EF_NOGUNBOB; // let it bob\r
                prey.swallow_model.effects |= EF_NODEPTHTEST; // don't hide behind walls\r
                prey.swallow_model.owner = prey;\r
-               prey.swallow_model.customizeentityforclient = Vore_SwallowModel_CustomizeEntityForClient;\r
-               prey.swallow_model.think = Vore_SwallowModel_Think;\r
+               prey.swallow_model.customizeentityforclient = Vore_GulletModel_CustomizeEntityForClient;\r
+               prey.swallow_model.think = Vore_GulletModel_Think;\r
                prey.swallow_model.nextthink = time;\r
        }\r
 \r
        // properties that should update whenever possible, but when the predator is available\r
-       string player_swallowmodel;\r
-       player_swallowmodel = strcat(substring(pred.playermodel, 0, strlen(pred.playermodel) - 4), "_swallow.md3"); // 4 is the extension length\r
-       if(prey.swallow_model.model != player_swallowmodel) // player model can be changed while the predator is active\r
-               setmodel(prey.swallow_model, player_swallowmodel);\r
+       string player_gulletmodel;\r
+       player_gulletmodel = strcat(substring(pred.playermodel, 0, strlen(pred.playermodel) - 4), "_gullet.iqm"); // 4 is the extension length\r
+       if(prey.swallow_model.model != player_gulletmodel) // player model can be changed while the predator is active\r
+               setmodel(prey.swallow_model, player_gulletmodel);\r
        if(prey.swallow_model.skin != pred.skin) // player skin can be changed while the predator is active\r
                prey.swallow_model.skin = pred.skin;\r
        if(cvar("g_healthsize"))\r
@@ -273,6 +294,7 @@ void Vore_Swallow(entity e)
 \r
        e.vore_oldmovetype = e.movetype;\r
        e.vore_oldsolid = e.solid;\r
+       e.punchvector_z = cvar("g_balance_vore_swallow_prey_punchvector");\r
 \r
        e.predator = self;\r
        setorigin(e, e.predator.origin);\r
@@ -281,6 +303,9 @@ void Vore_Swallow(entity e)
        e.solid = SOLID_NOT;\r
        e.aiment = e.predator; // follow the predator, automatically unset when regurgitated\r
 \r
+       float scalediff;\r
+       scalediff = cvar("g_healthsize") ? e.scale / e.predator.scale : 1; // the tighter the gut, the greater the effect\r
+\r
        // drop keys (KH) and flags (CTF) when we get swallowed\r
        kh_Key_DropAll(e, FALSE);\r
        if(e.flagcarried)\r
@@ -299,11 +324,12 @@ void Vore_Swallow(entity e)
 \r
        PlayerSound(e.predator, playersound_swallow, CHAN_VOICE, VOICETYPE_PLAYERSOUND);\r
        setanim(e.predator, e.predator.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing / regurgitating\r
-       e.predator.punchangle_x -= cvar("g_balance_vore_swallow_punchangle");\r
+       e.predator.punchangle_x = crandom() * cvar("g_balance_vore_swallow_predator_punchangle") * scalediff;\r
+       e.predator.punchangle_y = crandom() * cvar("g_balance_vore_swallow_predator_punchangle") * scalediff;\r
+       e.predator.punchangle_z = crandom() * cvar("g_balance_vore_swallow_predator_punchangle") * scalediff;\r
        e.predator.regurgitate_prepare = 0;\r
        e.predator.spawnshieldtime = 0; // lose spawn shield when we vore\r
        e.predator.hitsound += 1; // play this for team mates too, as we could be swallowing them to heal them\r
-       Vore_AutoDigest(e.predator);\r
        Vore_SetPreyPositions(e.predator);\r
 \r
        // block firing for a small amount of time, or we'll be firing the next frame after we swallow\r
@@ -315,23 +341,38 @@ void Vore_Swallow(entity e)
 \r
 void Vore_SwallowStep(entity e)\r
 {\r
-       if(!cvar("g_balance_vore_swallow_speed_fill"))\r
+       if(!cvar("g_balance_vore_swallow_speed_fill_player"))\r
        {\r
                Vore_Swallow(e);\r
                return;\r
        }\r
 \r
-       Vore_SwallowModel_Update(e, self);\r
+       // when the predator starts swallowing, play his grab sound\r
+       if(!self.swallow_progress_pred)\r
+               PlayerSound(self, playersound_grab, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
+\r
+       Vore_GulletModel_Update(e, self);\r
 \r
        // increase the progress value until it reaches 1, then swallow the player\r
        if(e.swallow_progress_prey < 1)\r
        {\r
                float fill;\r
-               fill = cvar("g_balance_vore_swallow_speed_fill") * frametime;\r
-               if(cvar("g_healthsize") && cvar("g_balance_vore_swallow_speed_fill_scalediff")) // fill rate depends on predator size compared to prey size\r
-                       fill *= pow(self.scale / e.scale, cvar("g_balance_vore_swallow_speed_fill_scalediff"));\r
+               fill = cvar("g_balance_vore_swallow_speed_fill_player") * frametime;\r
+               if(cvar("g_healthsize") && cvar("g_balance_vore_swallow_speed_fill_scalediff_player")) // fill rate depends on predator size compared to prey size\r
+                       fill *= pow(self.scale / e.scale, cvar("g_balance_vore_swallow_speed_fill_scalediff_player"));\r
                if(cvar("g_balance_vore_swallow_speed_fill_stomachload") && e.stomach_load) // fill rate is influenced by the prey's stomach load\r
-                       fill /= e.stomach_load;\r
+                       fill *= (1 - ((e.stomach_load / e.stomach_maxload) * bound(0, cvar("g_balance_vore_swallow_speed_fill_stomachload"), 1)));\r
+\r
+               // skill-based speed offset for bots\r
+               if(skill && cvar("skill_offset"))\r
+               {\r
+                       float ofs;\r
+                       ofs = pow(skill / cvar("skill_offset_center"), cvar("skill_offset"));\r
+                       if(clienttype(self) == CLIENTTYPE_BOT)\r
+                               fill *= ofs;\r
+                       if(clienttype(e) == CLIENTTYPE_BOT)\r
+                               fill /= ofs;\r
+               }\r
 \r
                e.swallow_progress_prey += fill;\r
        }\r
@@ -353,27 +394,36 @@ void Vore_Regurgitate(entity e)
        if(e.health > 0) // leave SOLID_NOT for dead bodies\r
                e.solid = e.vore_oldsolid;\r
        e.view_ofs_z = PL_VIEW_OFS_z;\r
+       e.punchvector_z = -cvar("g_balance_vore_regurgitate_prey_punchvector");\r
+\r
+       // if the prey has been fully digested, silently detach them\r
+       if(cvar("g_balance_vore_regurgitate_death_silent"))\r
+       if(e.deadflag != DEAD_NO && e.health <= cvar("g_balance_vore_digestion_limit"))\r
+       {\r
+               e.predator = world;\r
+               e.modelindex = 0; // hide the dead body\r
+               return;\r
+       }\r
+\r
+       float scalediff;\r
+       scalediff = cvar("g_healthsize") ? e.scale / e.predator.scale : 1; // the tighter the gut, the greater the effect\r
 \r
        // apply velocities\r
-       local vector oldforward, oldright, oldup;\r
-       oldforward = v_forward;\r
-       oldright = v_right;\r
-       oldup = v_up;\r
        makevectors(e.predator.v_angle);\r
-       e.velocity = v_forward * cvar("g_balance_vore_regurgitate_force");\r
-       e.predator.velocity += -v_forward * cvar("g_balance_vore_regurgitate_predatorforce");\r
-       v_forward = oldforward;\r
-       v_right = oldright;\r
-       v_up = oldup;\r
-\r
+       e.velocity = v_forward * cvar("g_balance_vore_regurgitate_force") * scalediff;\r
+       e.predator.velocity += -v_forward * cvar("g_balance_vore_regurgitate_predatorforce") * scalediff;\r
        e.pusher = e.predator; // allows us to frag players by regurgitating them in deadly pits\r
        e.pushltime = time + cvar("g_maxpushtime");\r
 \r
+       // if the dead body of the prey is below gibbing health, gib it\r
+       e.stat_eaten = 0; // necessary for gibs to show\r
+       PlayerGib(e, e.predator);\r
+\r
        // regurgitated prey is given this amount of swallow progress, to simulate being more vulnerable\r
-       if(cvar("g_balance_vore_swallow_speed_fill") && cvar("g_balance_vore_regurgitate_swallowprogress"))\r
+       if(cvar("g_balance_vore_swallow_speed_fill_player") && cvar("g_balance_vore_regurgitate_swallowprogress"))\r
        {\r
                e.swallow_progress_prey = cvar("g_balance_vore_regurgitate_swallowprogress");\r
-               Vore_SwallowModel_Update(e, e.predator);\r
+               Vore_GulletModel_Update(e, e.predator);\r
        }\r
 \r
        // apply regurgitation damage to the predator\r
@@ -383,13 +433,15 @@ void Vore_Regurgitate(entity e)
                regurgitate_dmg = cvar("g_balance_vore_regurgitate_damage");\r
                if(cvar("g_healthsize"))\r
                        regurgitate_dmg *= e.scale / e.predator.scale;\r
-               Damage(e.predator, e.predator, e.predator, regurgitate_dmg, DEATH_REGURGITATION, e.predator.origin, '0 0 0');\r
+               Damage(e.predator, e, e, regurgitate_dmg, DEATH_REGURGITATION, e.predator.origin, '0 0 0');\r
        }\r
 \r
        PlayerSound(e.predator, playersound_regurgitate, CHAN_VOICE, VOICETYPE_PLAYERSOUND);\r
        setanim(e.predator, e.predator.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing / regurgitating\r
-       pointparticles(particleeffectnum("vore_regurgitate"), e.predator.origin, '0 0 0', 1);\r
-       e.predator.punchangle_x += cvar("g_balance_vore_regurgitate_punchangle");\r
+       pointparticles(particleeffectnum("vore_regurgitate"), e.predator.origin, '0 0 0', floor(scalediff * PARTICLE_MULTIPLIER));\r
+       e.predator.punchangle_x = crandom() * cvar("g_balance_vore_regurgitate_predator_punchangle") * scalediff;\r
+       e.predator.punchangle_y = crandom() * cvar("g_balance_vore_regurgitate_predator_punchangle") * scalediff;\r
+       e.predator.punchangle_z = crandom() * cvar("g_balance_vore_regurgitate_predator_punchangle") * scalediff;\r
        e.predator.regurgitate_prepare = 0;\r
        e.predator.action_delay = time + cvar("g_balance_vore_action_delay");\r
        Vore_SetPreyPositions(e.predator);\r
@@ -400,9 +452,10 @@ void Vore_Regurgitate(entity e)
        e.predator = world;\r
 }\r
 \r
-void Vore_Disconnect()\r
+void Vore_Disconnect(float consumables)\r
 {\r
        // frees prey from their predators when someone disconnects or goes spectating, or in other circumstances\r
+       entity e;\r
 \r
        // prey disconnects or goes spectating while inside someone's belly\r
        if(self.stat_eaten)\r
@@ -415,9 +468,18 @@ void Vore_Disconnect()
                if(head.predator == self)\r
                        Vore_Regurgitate(head);\r
        }\r
-       Vore_GurgleSound(); // stop the gurgling sound\r
+       // remove consumable items when we disconnect\r
+       if(consumables)\r
+       {\r
+               for(e = world; (e = find(e, classname, "consumable")); )\r
+               {\r
+                       if(e.predator == self)\r
+                               Item_Consumable_Remove(e, TRUE);\r
+               }\r
+       }\r
 \r
        self.stomach_load = self.gravity = 0; // prevents a bug\r
+       Vore_GurgleSound(); // stop the gurgling sound\r
 }\r
 \r
 .float digestion_step;\r
@@ -425,30 +487,38 @@ void Vore_Digest()
 {\r
        // apply digestion to prey\r
 \r
+       if(self.predator.deadflag != DEAD_NO) // dead predators don't digest\r
+       {\r
+               self.predator.digesting = FALSE;\r
+               return;\r
+       }\r
+       if(self.health <= cvar("g_balance_vore_digestion_limit")) // don't digest below this amount of health\r
+               return;\r
+\r
        if(time > self.digestion_step)\r
        {\r
                // if distributed digestion is enabled, reduce digestion strength by the amount of prey in our stomach\r
-               float vore_offset;\r
-               vore_offset = 1;\r
+               float damage, damage_offset;\r
+\r
+               damage_offset = 1;\r
                if(cvar("g_balance_vore_digestion_distribute")) // apply distributed digestion damage\r
-                       vore_offset *= self.predator.stomach_load / self.predator.stomach_maxload;\r
+                       damage_offset /= (1 - (self.predator.stomach_load / self.predator.stomach_maxload) * bound(0, cvar("g_balance_vore_digestion_distribute"), 1));\r
                if(cvar("g_healthsize") && cvar("g_balance_vore_digestion_scalediff")) // apply player scale to digestion damage\r
-                       vore_offset *= pow(self.scale / self.predator.scale, cvar("g_balance_vore_digestion_scalediff"));\r
-               vore_offset = ceil(vore_offset);\r
+                       damage_offset *= pow(self.scale / self.predator.scale, cvar("g_balance_vore_digestion_scalediff"));\r
 \r
-               float damage;\r
-               damage = cvar("g_balance_vore_digestion_damage") / vore_offset;\r
+               if(self.deadflag != DEAD_NO)\r
+                       damage = cvar("g_balance_vore_digestion_damage_death") / damage_offset;\r
+               else\r
+                       damage = cvar("g_balance_vore_digestion_damage") / damage_offset;\r
 \r
                Damage(self, self.predator, self.predator, damage, DEATH_DIGESTION, self.origin, '0 0 0');\r
                if(cvar("g_balance_vore_digestion_vampire") && self.predator.health < cvar("g_balance_vore_digestion_vampire_stable"))\r
-                       self.predator.health += cvar("g_balance_vore_digestion_vampire") / vore_offset;\r
-\r
-               if (self.predator.digestsound_finished < time)\r
                {\r
-                       PlayerSound (self.predator, playersound_digest, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);\r
-                       self.predator.digestsound_finished = time + 0.5;\r
+                       self.predator.health += damage * cvar("g_balance_vore_digestion_vampire");\r
+                       self.predator.pauserothealth_finished = max(self.predator.pauserothealth_finished, time + cvar("g_balance_pause_health_rot"));\r
                }\r
-               self.digestion_step = time + steptime;\r
+\r
+               self.digestion_step = time + vore_steptime;\r
        }\r
 \r
        if(stov(cvar_string("g_vore_regurgitatecolor_color_digest")))\r
@@ -460,11 +530,14 @@ void Vore_Teamheal()
 {\r
        // apply teamheal\r
 \r
+       if(self.deadflag != DEAD_NO)\r
+               return;\r
+\r
        if(cvar("g_balance_vore_teamheal") && self.health < cvar("g_balance_vore_teamheal_stable"))\r
        if(time > self.teamheal_step)\r
        {\r
                self.health += cvar("g_balance_vore_teamheal");\r
-               self.teamheal_step = time + steptime;\r
+               self.teamheal_step = time + vore_steptime;\r
 \r
                // play beep sound when a team mate was healed to the maximum amount, to both the prey and the predator\r
                if(self.health >= cvar("g_balance_vore_teamheal_stable"))\r
@@ -480,27 +553,41 @@ void Vore_StomachKick()
 {\r
        // allows prey to kick the predator's stomach and do some damage or attempt to escape\r
 \r
+       if(self.deadflag != DEAD_NO)\r
+               return;\r
+\r
+       float scalediff;\r
+       scalediff = pow(self.scale / self.predator.scale, cvar("g_balance_vore_kick_scalediff"));\r
+\r
        if(time > self.stomachkick_delay && !self.kick_pressed)\r
        {\r
-               float damage, vol;\r
+               float damage, vol, pitch;\r
                vector force;\r
                damage = cvar("g_balance_vore_kick_damage");\r
                force = v_forward * cvar("g_balance_vore_kick_force");\r
                vol = VOL_BASE;\r
 \r
+               // modified sound pitch, based on player scale\r
+               if(cvar("g_healthsize") && cvar("g_healthsize_pitch"))\r
+                       pitch = pow(self.predator.scale, -cvar("g_healthsize_pitch"));\r
+\r
                // apply player scale to the damage / force of the kick\r
                if(cvar("g_healthsize") && cvar("g_balance_vore_kick_scalediff"))\r
                {\r
-                       damage *= pow(self.scale / self.predator.scale, cvar("g_balance_vore_kick_scalediff"));\r
-                       force *= pow(self.scale / self.predator.scale, cvar("g_balance_vore_kick_scalediff"));\r
-                       vol *= pow(self.scale / self.predator.scale, cvar("g_balance_vore_kick_scalediff")); // kick sound volume based on the same scale\r
+                       damage *= scalediff;\r
+                       force *= scalediff;\r
+                       vol *= scalediff; // kick sound volume based on the same scale\r
                }\r
                vol = bound(0, vol, 1);\r
 \r
                Damage(self.predator, self, self, damage, DEATH_STOMACHKICK, self.predator.origin, force);\r
-               sound(self.predator, CHAN_PROJECTILE, strcat("weapons/hit", ftos(floor(random() * 8)), ".wav"), vol, ATTN_NORM);\r
-               self.predator.punchangle_x -= cvar("g_balance_vore_kick_predator_punchangle");\r
-               self.punchangle_x += cvar("g_balance_vore_kick_prey_punchangle");\r
+               sound7(self.predator, CHAN_PROJECTILE, strcat("weapons/hit", ftos(floor(random() * 8)), ".wav"), vol, ATTN_NORM, 100 * pitch, 0);\r
+               self.predator.punchangle_x = crandom() * cvar("g_balance_vore_kick_predator_punchangle") * scalediff;\r
+               self.predator.punchangle_y = crandom() * cvar("g_balance_vore_kick_predator_punchangle") * scalediff;\r
+               self.predator.punchangle_z = crandom() * cvar("g_balance_vore_kick_predator_punchangle") * scalediff;\r
+               self.punchangle_x = crandom() * cvar("g_balance_vore_kick_prey_punchangle") * scalediff;\r
+               self.punchangle_y = crandom() * cvar("g_balance_vore_kick_prey_punchangle") * scalediff;\r
+               self.punchangle_z = crandom() * cvar("g_balance_vore_kick_prey_punchangle") * scalediff;\r
 \r
                // abort the predator's scheduled regurgitation\r
                if(random() < cvar("g_balance_vore_kick_cutregurgitate"))\r
@@ -517,6 +604,9 @@ void Vore_StomachLeave()
 {\r
        // allows players to get out of their predator at will in some circumstances, such as team mates\r
 \r
+       if(self.deadflag != DEAD_NO)\r
+               return;\r
+\r
        if(Vore_CanLeave())\r
                Vore_Regurgitate(self);\r
        else if(time > self.complain_vore)\r
@@ -534,7 +624,7 @@ void Vore_AutoTaunt()
        float taunt_time;\r
 \r
        // predator taunts\r
-       if(self.stomach_load && !Stomach_TeamMates_check(self))\r
+       if((self.swallow_progress_pred || (self.stomach_load && Stomach_HasLivePrey(self))) && !Stomach_TeamMates_check(self))\r
        {\r
                if(!self.taunt_soundtime) // taunt_soundtime becomes 0 once the taunt has played\r
                {\r
@@ -549,7 +639,7 @@ void Vore_AutoTaunt()
        }\r
 \r
        // prey taunts\r
-       if(self.stat_eaten && !(teams_matter && self.team == self.predator.team))\r
+       if((self.swallow_progress_prey || (self.stat_eaten && !(teams_matter && self.team == self.predator.team))) && self.deadflag == DEAD_NO)\r
        {\r
                if(!self.taunt_soundtime) // taunt_soundtime becomes 0 once the taunt has played\r
                {\r
@@ -639,7 +729,7 @@ void Vore()
                if(cvar("g_vore_regurgitatecolor_particles"))\r
                if(self.regurgitatecolor_particles_tick < time)\r
                {\r
-                       pointparticles(particleeffectnum("vore_regurgitate_constant"), self.origin, '0 0 0', 1);\r
+                       pointparticles(particleeffectnum("vore_regurgitate_constant"), self.origin, '0 0 0', floor((cvar("g_healthsize") ? self.scale : 1) * PARTICLE_MULTIPLIER));\r
                        self.regurgitatecolor_particles_tick = time + cvar("g_vore_regurgitatecolor_particles") * vlen(self.colormod); // particle time depends on how dirty the player is\r
                }\r
        }\r
@@ -666,41 +756,15 @@ void Vore()
        // prevent this by checking if such has happened, and taking the proper measures\r
        // this code has a high priority and must not be stopped by any delay, so run it here\r
        if(self.predator.stat_eaten)\r
-       {\r
-               entity target_predator, target_predator_predator, oldself;\r
-               target_predator = self.predator;\r
-               target_predator_predator = self.predator.predator;\r
-\r
                Vore_Regurgitate(self);\r
 \r
-               // now steal our prey's prey if this probability applies\r
-               if(random() < cvar("g_balance_vore_swallow_stealprey"))\r
-               {\r
-                       oldself = self;\r
-                       self = target_predator_predator;\r
-                       if(Swallow_condition_check(oldself))\r
-                       if not(teams_matter && self.team == target_predator.team) // don't steal a team mate's prey\r
-                       if not(teams_matter && self.team == oldself.team) // if the prey we would be stealing is a team mate, don't do it\r
-                               Vore_Swallow(oldself);\r
-                       self = oldself;\r
-               }\r
-       }\r
-\r
        // the swallow progress of prey and preds idly decrease by this amount\r
        if(cvar("g_balance_vore_swallow_speed_decrease"))\r
        {\r
                if(self.swallow_progress_pred)\r
-               {\r
-                       self.swallow_progress_pred -= cvar("g_balance_vore_swallow_speed_decrease") * frametime;\r
-                       if(self.swallow_progress_pred < 0)\r
-                               self.swallow_progress_pred = 0;\r
-               }\r
+                       self.swallow_progress_pred = max(0, self.swallow_progress_pred - cvar("g_balance_vore_swallow_speed_decrease") * frametime);\r
                if(self.swallow_progress_prey)\r
-               {\r
-                       self.swallow_progress_prey -= cvar("g_balance_vore_swallow_speed_decrease") * frametime;\r
-                       if(self.swallow_progress_prey < 0)\r
-                               self.swallow_progress_prey = 0;\r
-               }\r
+                       self.swallow_progress_prey = max(0, self.swallow_progress_prey - cvar("g_balance_vore_swallow_speed_decrease") * frametime);\r
        }\r
 \r
        // set the predator's stomach load and capacity\r
@@ -709,12 +773,12 @@ void Vore()
        // apply delays and skip the vore system under some circumstances\r
        if(!cvar("g_vore")) // the vore system is disabled\r
        {\r
-               Vore_Disconnect();\r
+               Vore_Disconnect(TRUE);\r
                return;\r
        }\r
        if(time < game_starttime || (time < warmup && !inWarmupStage)) // don't allow vore before a round begins\r
        {\r
-               Vore_Disconnect();\r
+               Vore_Disconnect(FALSE);\r
                return;\r
        }\r
        if(self.spectatee_status)\r
@@ -768,6 +832,7 @@ void Vore()
 \r
        // predator wishes to regurgitate his prey\r
        if(self.BUTTON_REGURGITATE && time > self.action_delay)\r
+       if(!self.regurgitate_prepare)\r
        {\r
                if(self.stomach_load)\r
                {\r
@@ -787,6 +852,23 @@ void Vore()
                }\r
        }\r
 \r
+       if(self.stomach_load > self.stomach_maxload) // the predator got beyond his capacity after eating, so some prey must pop out\r
+       {\r
+               self.regurgitate_prepare = -1;\r
+               return;\r
+       }\r
+       if(cvar("g_balance_vore_load_pred_speedcap") && self.stomach_load)\r
+       if(vlen(self.velocity) >= cvar("g_balance_vore_load_pred_speedcap") / (self.stomach_load / self.stomach_maxload)) // predator's going too fast, gets sick and throws up\r
+       {\r
+               self.regurgitate_prepare = -1;\r
+               return;\r
+       }\r
+\r
+       if (self.digesting && self.digestsound_finished < time)\r
+       {\r
+               PlayerSound (self, playersound_digest, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);\r
+               self.digestsound_finished = time + 0.5;\r
+       }\r
        if(cvar("g_vore_gurglesound"))\r
                Vore_GurgleSound();\r
 \r
@@ -797,34 +879,34 @@ void Vore()
        if(!self.stat_eaten)\r
                return;\r
 \r
-       /*if(self.deadflag != DEAD_NO) // we're dead, do what we must\r
-       {\r
-               Vore_Regurgitate(self);\r
-               return;\r
-       }*/\r
+       if(self.deadflag != DEAD_NO && self.health < cvar("g_balance_vore_digestion_limit")) // make sure health doesn't go below the limit\r
+               self.health = cvar("g_balance_vore_digestion_limit");\r
 \r
-       if(self.predator.deadflag != DEAD_NO) // do we want to be in a dead furry x_x\r
-       {\r
-               Vore_Regurgitate(self);\r
-               return;\r
-       }\r
-       if(self.predator.stomach_load > self.predator.stomach_maxload) // the predator got beyond his capacity after eating, so some prey must pop out\r
+       // automatically regurgitate prey that has reached their digestion limit\r
+       if(cvar("g_balance_vore_digestion_limit_regurgitate"))\r
+       if(self.health <= cvar("g_balance_vore_digestion_limit"))\r
        {\r
                Vore_Regurgitate(self);\r
                return;\r
        }\r
-       if(cvar("g_balance_vore_load_pred_speedcap") && vlen(self.predator.velocity) >= cvar("g_balance_vore_load_pred_speedcap") / (self.predator.stomach_load / self.predator.stomach_maxload)) // predator's going too fast, gets sick and throws up\r
+\r
+       // do we stick around inside dead furries? x_x\r
+       if(self.predator.deadflag != DEAD_NO)\r
        {\r
-               Vore_Regurgitate(self);\r
-               return;\r
+               if(!cvar("g_balance_vore_deadpredator") || !self.predator.modelindex) // if the predator is gibbed, we are out\r
+               {\r
+                       Vore_Regurgitate(self);\r
+                       return;\r
+               }\r
+               if(self.predator.regurgitate_prepare) // abort scheduled regurgitation\r
+                       self.predator.regurgitate_prepare = 0;\r
        }\r
 \r
        // apply delayed regurgitating if it was scheduled\r
        if(self.predator.regurgitate_prepare && time > self.predator.regurgitate_prepare)\r
        {\r
-               self.predator.regurgitate_prepare = 0;\r
-               self.predator.complain_vore = time + complain_delay_time; // prevent complaining the next frame if this empties our stomach\r
                Vore_Regurgitate(self);\r
+               self.predator.complain_vore = time + complain_delay_time; // prevent complaining the next frame if this empties our stomach\r
        }\r
 \r
        // execute digesting and team healing\r
@@ -848,4 +930,7 @@ void Vore()
        // Ugly workaround for a Keyhunt issue. Your team's key can still be given to you while in the stomach\r
        // (at round start), which is pretty ugly and wrong. So attempt to drop keys each frame for prey\r
        kh_Key_DropAll(self, FALSE);\r
-}
\ No newline at end of file
+\r
+       // always position camera at the center of the stomach, to reduce probability of the view poking out\r
+       self.view_ofs_z = PL_PREY_VIEW_OFS_z * self.predator.scale;\r
+}\r