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