eater -> predator, because that's a more correct word
[voretournament/voretournament.git] / data / qcsrc / server / vore.qc
1 .float regurgitate_prepare;\r
2 .float system_delay, swallow_delay, digest_button_delay, regurgitate_button_delay;\r
3 .float complain_swallow;\r
4 const float complain_delay = 1;\r
5 const float button_delay = 0.5;\r
6 const float steptime = 0.1;\r
7 const float system_delay_time = 0.1;\r
8 \r
9 .float vore_oldmovetype, vore_oldsolid, vore_oldstomachload, vore_oldview_ofs_z;\r
10 \r
11 entity Swallow_distance_check()\r
12 {\r
13         // check if we can swallow a player instead of firing our weapon\r
14         vector w_shotorg, w_shotdir;\r
15         w_shotorg = self.origin + self.view_ofs;\r
16         w_shotdir = v_forward;\r
17 \r
18         WarpZone_traceline_antilag(self, w_shotorg, w_shotorg + w_shotdir * cvar("g_balance_vore_swallow_range"), FALSE, self, ANTILAG_LATENCY(self));\r
19         if(trace_fraction < 1)\r
20         if(trace_ent.classname == "player")\r
21                 return trace_ent;\r
22         return world;\r
23 }\r
24 \r
25 float Swallow_condition_check(entity prey)\r
26 {\r
27         // checks the necessary conditions for swallowing another player\r
28         if(prey.classname == "player" && prey.predator.classname != "player" && prey.deadflag == DEAD_NO) // we can't swallow someone who's already in someone else's stomach\r
29         if(self.classname == "player" && self.predator.classname != "player" && self.deadflag == DEAD_NO) // we can't swallow players while inside someone's stomach ourselves\r
30         if not(vlen(self.velocity) > cvar("g_balance_vore_regurgitate_velocitylimit"))\r
31         {\r
32                 if(self.stomach_load >= cvar("g_balance_vore_swallow_limit"))\r
33                 {\r
34                         if(time > self.complain_swallow)\r
35                         {\r
36                                 play2(self, "weapons/unavailable.wav");\r
37                                 sprint(self, strcat("You cannot swallow more than ^2", cvar_string("g_balance_vore_swallow_limit"), "^7 players at a time\n"));\r
38                                 self.complain_swallow = time + complain_delay;\r
39                         }\r
40                         return FALSE;\r
41                 }\r
42 \r
43                 if(cvar("g_vore_biggergut"))\r
44                 if(prey.stomach_load > self.stomach_load)\r
45                 {\r
46                         if(time > self.complain_swallow)\r
47                         {\r
48                                 play2(self, "weapons/unavailable.wav");\r
49                                 sprint(self, "You cannot swallow someone with a bigger stomach than yours\n");\r
50                                 self.complain_swallow = time + complain_delay;\r
51                         }\r
52                         return FALSE;\r
53                 }\r
54                 return TRUE;\r
55         }\r
56         return FALSE;\r
57 }\r
58 \r
59 float Vore_PreyCanLeave()\r
60 {\r
61         if(teams_matter && self.team == self.predator.team)\r
62                 return TRUE;\r
63         return FALSE;\r
64 }\r
65 \r
66 // make the camera smoothly lower itself when we get swallowed\r
67 // 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)\r
68 .float cameraeffect_current, cameraeffect_target;\r
69 void Vore_CameraEffect_Set(entity e)\r
70 {\r
71         e.cameraeffect_current = 1;\r
72         e.cameraeffect_target = 2;\r
73 }\r
74 void Vore_CameraEffect_Apply()\r
75 {\r
76         if(self.predator.classname != "player")\r
77                 return;\r
78 \r
79         if(self.cvar_cl_vore_cameraspeed)\r
80         {\r
81                 local float step;\r
82                 step = self.cvar_cl_vore_cameraspeed * frametime;\r
83 \r
84                 // not sure if these maths are good, as the effect should be smoother\r
85                 if(self.cameraeffect_current >= self.cameraeffect_target + step)\r
86                         self.cameraeffect_current -= step;\r
87                 else if(self.cameraeffect_current <= self.cameraeffect_target - step)\r
88                         self.cameraeffect_current += step;\r
89         }\r
90         else\r
91                 self.cameraeffect_current = self.cameraeffect_target;\r
92 \r
93         self.view_ofs_z = self.vore_oldview_ofs_z / self.cameraeffect_current;\r
94 }\r
95 \r
96 void Vore_Weight_apply(entity e)\r
97 {\r
98         // apply stomach weight that makes you heavier the more you eat\r
99         // slowing the player is applied in cl_physics.qc\r
100         if(e.stomach_load != e.vore_oldstomachload)\r
101                 e.gravity += 1 + (e.stomach_load * cvar("g_balance_vore_weight_gravity") - e.vore_oldstomachload);\r
102         if(e.gravity == 0)\r
103                 e.gravity = 0.00001; // 0 becomes 1 for .gravity, so do this to allow 0 gravity\r
104         e.vore_oldstomachload = e.stomach_load;\r
105 }\r
106 \r
107 .entity pusher;\r
108 .float pushltime;\r
109 void Vore_Swallow(entity e)\r
110 {\r
111         // this player is beening swallowed by another player, apply the proper changes\r
112         e.vore_oldmovetype = e.movetype;\r
113         e.vore_oldsolid = e.solid;\r
114         e.vore_oldview_ofs_z = e.view_ofs_z;\r
115 \r
116         e.predator = self;\r
117         setorigin(e, e.predator.origin);\r
118         e.velocity = '0 0 0';\r
119         e.movetype = MOVETYPE_FOLLOW;\r
120         e.solid = SOLID_NOT;\r
121         e.alpha = -1; // best way of hiding / showing the eaten player\r
122         e.aiment = e.predator; // follow the predator. Is automatically unset\r
123 \r
124         // drop keys (KH) and flags (CTF) when we get swallowed\r
125         kh_Key_DropAll(e, FALSE);\r
126         if(e.flagcarried)\r
127                 DropFlag(e.flagcarried, world, e.predator);\r
128 \r
129         Vore_CameraEffect_Set(e);\r
130 \r
131         if(stov(cvar_string("g_vore_regurgitatecolor_released")))\r
132                 e.colormod = stov(cvar_string("g_vore_regurgitatecolor_released"));\r
133 \r
134         if(e.team == e.predator.team && teamplay)\r
135         {\r
136                 centerprint(e, "^3You have been swallowed by a team mate, don't kick!");\r
137                 centerprint(e.predator, "^3You have swallowed a team mate, use caution!");\r
138         }\r
139 \r
140         PlayerSound(e.predator, playersound_swallow, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
141         setanim(e.predator, e.predator.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing / regurgitating\r
142         e.predator.stomach_load += 1;\r
143         e.predator.regurgitate_prepare = 0;\r
144         Vore_Weight_apply(e.predator);\r
145 \r
146         // block firing for a small amount of time when voring, or we'll be firing the next frame after we swallow\r
147         e.predator.weapon_delay = time + button_delay;\r
148         e.predator.swallow_delay = time + cvar("g_balance_vore_swallow_delay");\r
149         e.system_delay = e.predator.system_delay = time + system_delay_time;\r
150 }\r
151 \r
152 void Vore_Regurgitate(entity e)\r
153 {\r
154         // this player is being released from their predator, apply the proper changes\r
155         e.movetype = e.vore_oldmovetype;\r
156         if(e.health > 0) // leave SOLID_NOT for dead bodies\r
157                 e.solid = e.vore_oldsolid;\r
158         e.view_ofs_z = e.vore_oldview_ofs_z;\r
159         e.alpha = default_player_alpha; // best way of hiding / showing the eaten player\r
160 \r
161         // velocities\r
162         local vector oldforward, oldright, oldup;\r
163         oldforward = v_forward;\r
164         oldright = v_right;\r
165         oldup = v_up;\r
166         makevectors(e.predator.v_angle);\r
167         e.velocity = v_forward * cvar("g_balance_vore_regurgitate_force");\r
168         e.predator.velocity += -v_forward * cvar("g_balance_vore_regurgitate_eaterforce");\r
169         v_forward = oldforward;\r
170         v_right = oldright;\r
171         v_up = oldup;\r
172 \r
173         e.pusher = e.predator; // so we can frag players by regurgitating them in deadly pits\r
174         e.pushltime = time + cvar("g_maxpushtime");\r
175 \r
176         PlayerSound(e.predator, playersound_regurgitate, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
177         setanim(e.predator, e.predator.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing / regurgitating\r
178         pointparticles(particleeffectnum("regurgitate"), e.predator.origin, '0 0 0', 1);\r
179         e.predator.stomach_load -= 1;\r
180         e.predator.regurgitate_prepare = 0;\r
181         e.predator.swallow_delay = time + cvar("g_balance_vore_swallow_delay");\r
182         Vore_Weight_apply(e.predator);\r
183 \r
184         // block firing for a small amount of time when getting regurgitated, or we'll be firing the next frame\r
185         e.weapon_delay = time + button_delay;\r
186         e.system_delay = e.predator.system_delay = time + system_delay_time;\r
187         e.predator = world;\r
188 }\r
189 \r
190 void Vore_Gurglesound();\r
191 void Vore_Disconnect()\r
192 {\r
193         // frees prey from their predators when someone disconnects or goes spectating\r
194 \r
195         // prey disconnects or goes spectating while inside someone's belly:\r
196         if(self.predator.classname == "player")\r
197         {\r
198                 self.view_ofs_z = self.vore_oldview_ofs_z;\r
199                 self.predator.stomach_load -= 1;\r
200                 Vore_Weight_apply(self.predator);\r
201                 self.predator = world;\r
202         }\r
203 \r
204         // pred disconnects or goes spectating with players in their belly:\r
205         else if(self.stomach_load > 0)\r
206         {\r
207                 entity head;\r
208                 FOR_EACH_PLAYER(head)\r
209                 {\r
210                         if(head.predator == self)\r
211                                 Vore_Regurgitate(head);\r
212                 }\r
213                 Vore_Gurglesound(); // stop the gurgling sound\r
214         }\r
215 }\r
216 \r
217 .float digestion_step;\r
218 void Vore_Digest()\r
219 {\r
220         // apply digestion to prey\r
221         if(time > self.predator.digestion_step + steptime)\r
222         {\r
223                 Damage(self, self.predator, self.predator, cvar("g_balance_vore_digestion_damage"), DEATH_DIGESTION, self.origin, '0 0 0');\r
224                 if(cvar("g_balance_vore_digestion_vampire") && self.predator.health < cvar("g_balance_vore_digestion_vampire_stable"))\r
225                         self.predator.health += cvar("g_balance_vore_digestion_vampire");\r
226 \r
227                 if (self.predator.digestsound_finished < time)\r
228                 {\r
229                         self.predator.digestsound_finished = time + 0.5;\r
230                         PlayerSound (self.predator, playersound_digest, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);\r
231                 }\r
232                 self.predator.digestion_step = time;\r
233         }\r
234 \r
235         if(self.health <= 0)\r
236         if(stov(cvar_string("g_vore_regurgitatecolor_digested")))\r
237                 self.colormod = stov(cvar_string("g_vore_regurgitatecolor_digested"));\r
238 }\r
239 \r
240 .float teamheal_step;\r
241 void Vore_Teamheal()\r
242 {\r
243         if(cvar("g_balance_vore_teamheal") && self.health < cvar("g_balance_vore_teamheal_stable"))\r
244         if(time > self.teamheal_step + steptime)\r
245         {\r
246                 self.health += cvar("g_balance_vore_teamheal");\r
247                 self.teamheal_step = time;\r
248         }\r
249 }\r
250 \r
251 .float stomachkick_delay;\r
252 void Vore_StomachKick()\r
253 {\r
254         // allows prey to kick the predator's stomach and do some damage or attempt to escape\r
255         if(self.predator.classname != "player")\r
256                 return;\r
257 \r
258         if(time > self.stomachkick_delay)\r
259         {\r
260                 float damage;\r
261                 damage = ceil(random() * (cvar("g_balance_vore_kick_damage_max") - cvar("g_balance_vore_kick_damage_min")) + cvar("g_balance_vore_kick_damage_min"));\r
262                 Damage(self.predator, self, self, damage, DEATH_STOMACHKICK, self.predator.origin, '0 0 0');\r
263                 sound(self.predator, CHAN_PROJECTILE, "weapons/stomachkick.ogg", VOL_BASE, ATTN_NORM);\r
264 \r
265                 if(cvar("g_balance_vore_kick_escapeprobability") >= random())\r
266                         Vore_Regurgitate(self);\r
267 \r
268                 self.stomachkick_delay = time + cvar("g_balance_vore_kick_delay");\r
269         }\r
270 }\r
271 \r
272 void Vore_StomachLeave()\r
273 {\r
274         // allows players to get out of their predator at will in some circumstances, such as team mates\r
275 \r
276         if(Vore_PreyCanLeave())\r
277                 Vore_Regurgitate(self);\r
278         else if(time > self.complain_swallow)\r
279         {\r
280                 play2(self, "weapons/unavailable.wav");\r
281                 sprint(self, strcat("You cannot get out of ", self.predator.netname, "\n"));\r
282                 self.complain_swallow = time + complain_delay;\r
283         }\r
284 }\r
285 \r
286 .float gurglesound_finished, gurglesound_oldstomachload;\r
287 void Vore_Gurglesound()\r
288 {\r
289         if(time > self.gurglesound_finished || self.gurglesound_oldstomachload != self.stomach_load)\r
290         {\r
291                 GlobalSound(self.playersound_gurgle, CHAN_TRIGGER, VOICETYPE_GURGLE);\r
292 \r
293                 self.gurglesound_finished = time + 11; // yes, hard coded sound length. I know it's bad but what can I do?\r
294                 self.gurglesound_oldstomachload = self.stomach_load;\r
295         }\r
296 }\r
297 \r
298 void Vore()\r
299 {\r
300         // if we are free, show our stomach load on the HUD. Otherwise, show the predator's\r
301         if(self.predator.classname == "player")\r
302         {\r
303                 self.stat_stomachload = self.predator.stomach_load;\r
304                 self.stat_digesting = self.predator.digesting;\r
305                 self.stat_eaten = num_for_edict(self.predator);\r
306         }\r
307         else\r
308         {\r
309                 self.stat_stomachload = self.stomach_load;\r
310                 self.stat_digesting = self.digesting;\r
311                 self.stat_eaten = 0;\r
312         }\r
313 \r
314         // skip the vore system under some circumstances\r
315         if(time < game_starttime)\r
316         {\r
317                 Vore_Disconnect();\r
318                 return;\r
319         }\r
320         if(self.spectatee_status)\r
321                 return;\r
322         if(time < self.system_delay)\r
323                 return;\r
324 \r
325 // --------------------------------\r
326 // Code that addresses predators:\r
327 // --------------------------------\r
328 \r
329         entity prey;\r
330         prey = Swallow_distance_check();\r
331 \r
332         // attempt to swallow our new prey if there's any in range\r
333         if(self.BUTTON_ATCK && !self.BUTTON_REGURGITATE && self.swallow_delay < time)\r
334         if(Swallow_condition_check(prey))\r
335                 Vore_Swallow(prey);\r
336 \r
337         // start / stop digestion on command, if the player has someone in their stomach\r
338         if(self.BUTTON_DIGEST)\r
339         {\r
340                 if(self.stomach_load)\r
341                 {\r
342                         if(time > self.digest_button_delay)\r
343                         {\r
344                                 self.digesting = !self.digesting;\r
345                                 self.digest_button_delay = time + button_delay;\r
346                         }\r
347                 }\r
348                 else if(time > self.complain_swallow)\r
349                 {\r
350                         play2(self, "weapons/unavailable.wav");\r
351                         sprint(self, "There is nothing to digest\n");\r
352                         self.complain_swallow = time + complain_delay;\r
353                 }\r
354         }\r
355         if(!self.stomach_load)\r
356                 self.digesting = FALSE;\r
357 \r
358         // release players from this player's stomach on command\r
359         if(self.BUTTON_REGURGITATE)\r
360         {\r
361                 if(self.stomach_load)\r
362                 {\r
363                         if(time > self.regurgitate_button_delay)\r
364                         {\r
365                                 self.regurgitate_prepare = time + cvar("g_balance_vore_regurgitate_delay");\r
366                                 PlayerSound(self, playersound_regurgitate_prepare, CHAN_VOICE, VOICETYPE_PLAYERSOUND);\r
367                                 self.regurgitate_button_delay = time + button_delay;\r
368                         }\r
369                 }\r
370                 else if(time > self.complain_swallow)\r
371                 {\r
372                         play2(self, "weapons/unavailable.wav");\r
373                         sprint(self, "There is nothing to regurgitate\n");\r
374                         self.complain_swallow = time + complain_delay;\r
375                 }\r
376         }\r
377 \r
378         if(cvar("g_vore_gurglesound"))\r
379                 Vore_Gurglesound();\r
380 \r
381 // --------------------------------\r
382 // Code that addresses the prey:\r
383 // --------------------------------\r
384 \r
385         if(self.predator.classname != "player")\r
386                 return;\r
387 \r
388         if(self.predator.deadflag || self.deadflag)\r
389                 Vore_Regurgitate(self);\r
390         else if(self.predator.predator.classname == "player") // don't allow a player inside a player inside another player :)\r
391         {\r
392                 entity target_predator, oldself;\r
393                 target_predator = self.predator.predator;\r
394 \r
395                 Vore_Regurgitate(self);\r
396                 if(random() < cvar("g_vore_stealprey"))\r
397                 if(Swallow_condition_check(self))\r
398                 {\r
399                         oldself = self;\r
400                         self = target_predator;\r
401                         Vore_Swallow(oldself);\r
402                         self = oldself;\r
403                 }\r
404         }\r
405         else if(vlen(self.predator.velocity) > cvar("g_balance_vore_regurgitate_velocitylimit"))\r
406                 Vore_Regurgitate(self);\r
407 \r
408         // apply delayed regurgitating\r
409         if(self.predator.regurgitate_prepare && time > self.predator.regurgitate_prepare)\r
410         {\r
411                 self.predator.regurgitate_prepare = 0;\r
412                 self.predator.complain_swallow = time + complain_delay;\r
413                 Vore_Regurgitate(self);\r
414         }\r
415 \r
416         if(self.predator.digesting == TRUE)\r
417                 Vore_Digest();\r
418         if(teams_matter && self.team == self.predator.team)\r
419                 Vore_Teamheal();\r
420 \r
421         if(self.BUTTON_ATCK)\r
422                 Vore_StomachKick();\r
423         if(self.BUTTON_JUMP)\r
424                 Vore_StomachLeave();\r
425 \r
426         self.stat_canleave = Vore_PreyCanLeave();\r
427 \r
428         Vore_CameraEffect_Apply();\r
429 }