]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/vore.qc
Fix a major issue that was just reported. Right before the 0.3 release :)
[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_speedcap"))\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_release")))\r
132                 e.colormod = stov(cvar_string("g_vore_regurgitatecolor_release"));\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_predatorforce");\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                 Vore_Regurgitate(self);\r
198 \r
199         // pred disconnects or goes spectating with players in their belly:\r
200         else if(self.stomach_load > 0)\r
201         {\r
202                 entity head;\r
203                 FOR_EACH_PLAYER(head)\r
204                 {\r
205                         if(head.predator == self)\r
206                                 Vore_Regurgitate(head);\r
207                 }\r
208                 Vore_Gurglesound(); // stop the gurgling sound\r
209         }\r
210 }\r
211 \r
212 .float digestion_step;\r
213 void Vore_Digest()\r
214 {\r
215         // apply digestion to prey\r
216         if(time > self.predator.digestion_step + steptime)\r
217         {\r
218                 Damage(self, self.predator, self.predator, cvar("g_balance_vore_digestion_damage"), DEATH_DIGESTION, self.origin, '0 0 0');\r
219                 if(cvar("g_balance_vore_digestion_vampire") && self.predator.health < cvar("g_balance_vore_digestion_vampire_stable"))\r
220                         self.predator.health += cvar("g_balance_vore_digestion_vampire");\r
221 \r
222                 if (self.predator.digestsound_finished < time)\r
223                 {\r
224                         self.predator.digestsound_finished = time + 0.5;\r
225                         PlayerSound (self.predator, playersound_digest, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);\r
226                 }\r
227                 self.predator.digestion_step = time;\r
228         }\r
229 \r
230         if(self.health <= 0)\r
231         if(stov(cvar_string("g_vore_regurgitatecolor_digest")))\r
232                 self.colormod = stov(cvar_string("g_vore_regurgitatecolor_digest"));\r
233 }\r
234 \r
235 .float teamheal_step;\r
236 void Vore_Teamheal()\r
237 {\r
238         if(cvar("g_balance_vore_teamheal") && self.health < cvar("g_balance_vore_teamheal_stable"))\r
239         if(time > self.teamheal_step + steptime)\r
240         {\r
241                 self.health += cvar("g_balance_vore_teamheal");\r
242                 self.teamheal_step = time;\r
243         }\r
244 }\r
245 \r
246 .float stomachkick_delay;\r
247 void Vore_StomachKick()\r
248 {\r
249         // allows prey to kick the predator's stomach and do some damage or attempt to escape\r
250         if(self.predator.classname != "player")\r
251                 return;\r
252 \r
253         if(time > self.stomachkick_delay)\r
254         {\r
255                 float damage;\r
256                 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
257                 Damage(self.predator, self, self, damage, DEATH_STOMACHKICK, self.predator.origin, '0 0 0');\r
258                 sound(self.predator, CHAN_PROJECTILE, "weapons/stomachkick.ogg", VOL_BASE, ATTN_NORM);\r
259 \r
260                 if(cvar("g_balance_vore_kick_escapeprobability") >= random())\r
261                         Vore_Regurgitate(self);\r
262 \r
263                 self.stomachkick_delay = time + cvar("g_balance_vore_kick_delay");\r
264         }\r
265 }\r
266 \r
267 void Vore_StomachLeave()\r
268 {\r
269         // allows players to get out of their predator at will in some circumstances, such as team mates\r
270 \r
271         if(Vore_PreyCanLeave())\r
272                 Vore_Regurgitate(self);\r
273         else if(time > self.complain_swallow)\r
274         {\r
275                 play2(self, "weapons/unavailable.wav");\r
276                 sprint(self, strcat("You cannot get out of ", self.predator.netname, "\n"));\r
277                 self.complain_swallow = time + complain_delay;\r
278         }\r
279 }\r
280 \r
281 .float gurglesound_finished, gurglesound_oldstomachload;\r
282 void Vore_Gurglesound()\r
283 {\r
284         if(time > self.gurglesound_finished || self.gurglesound_oldstomachload != self.stomach_load)\r
285         {\r
286                 GlobalSound(self.playersound_gurgle, CHAN_TRIGGER, VOICETYPE_GURGLE);\r
287 \r
288                 self.gurglesound_finished = time + 11; // yes, hard coded sound length. I know it's bad but what can I do?\r
289                 self.gurglesound_oldstomachload = self.stomach_load;\r
290         }\r
291 }\r
292 \r
293 void Vore()\r
294 {\r
295         // if we are free, show our stomach load on the HUD. Otherwise, show the predator's\r
296         if(self.predator.classname == "player")\r
297         {\r
298                 self.stat_stomachload = self.predator.stomach_load;\r
299                 self.stat_digesting = self.predator.digesting;\r
300                 self.stat_eaten = num_for_edict(self.predator);\r
301         }\r
302         else\r
303         {\r
304                 self.stat_stomachload = self.stomach_load;\r
305                 self.stat_digesting = self.digesting;\r
306                 self.stat_eaten = 0;\r
307         }\r
308 \r
309         // skip the vore system under some circumstances\r
310         if(time < game_starttime)\r
311         {\r
312                 Vore_Disconnect();\r
313                 return;\r
314         }\r
315         if(self.spectatee_status)\r
316                 return;\r
317         if(time < self.system_delay)\r
318                 return;\r
319 \r
320 // --------------------------------\r
321 // Code that addresses predators:\r
322 // --------------------------------\r
323 \r
324         entity prey;\r
325         prey = Swallow_distance_check();\r
326 \r
327         // attempt to swallow our new prey if there's any in range\r
328         if(self.BUTTON_ATCK && !self.BUTTON_REGURGITATE && self.swallow_delay < time)\r
329         if(Swallow_condition_check(prey))\r
330                 Vore_Swallow(prey);\r
331 \r
332         // start / stop digestion on command, if the player has someone in their stomach\r
333         if(self.BUTTON_DIGEST)\r
334         {\r
335                 if(self.stomach_load)\r
336                 {\r
337                         if(time > self.digest_button_delay)\r
338                         {\r
339                                 self.digesting = !self.digesting;\r
340                                 self.digest_button_delay = time + button_delay;\r
341                         }\r
342                 }\r
343                 else if(time > self.complain_swallow)\r
344                 {\r
345                         play2(self, "weapons/unavailable.wav");\r
346                         sprint(self, "There is nothing to digest\n");\r
347                         self.complain_swallow = time + complain_delay;\r
348                 }\r
349         }\r
350         if(!self.stomach_load)\r
351                 self.digesting = FALSE;\r
352 \r
353         // release players from this player's stomach on command\r
354         if(self.BUTTON_REGURGITATE)\r
355         {\r
356                 if(self.stomach_load)\r
357                 {\r
358                         if(time > self.regurgitate_button_delay)\r
359                         {\r
360                                 self.regurgitate_prepare = time + cvar("g_balance_vore_regurgitate_delay");\r
361                                 PlayerSound(self, playersound_regurgitate_prepare, CHAN_VOICE, VOICETYPE_PLAYERSOUND);\r
362                                 self.regurgitate_button_delay = time + button_delay;\r
363                         }\r
364                 }\r
365                 else if(time > self.complain_swallow)\r
366                 {\r
367                         play2(self, "weapons/unavailable.wav");\r
368                         sprint(self, "There is nothing to regurgitate\n");\r
369                         self.complain_swallow = time + complain_delay;\r
370                 }\r
371         }\r
372 \r
373         if(cvar("g_vore_gurglesound"))\r
374                 Vore_Gurglesound();\r
375 \r
376 // --------------------------------\r
377 // Code that addresses the prey:\r
378 // --------------------------------\r
379 \r
380         if(self.predator.classname != "player")\r
381                 return;\r
382 \r
383         if(self.predator.deadflag || self.deadflag)\r
384                 Vore_Regurgitate(self);\r
385         else if(self.predator.predator.classname == "player") // don't allow a player inside a player inside another player :)\r
386         {\r
387                 entity target_predator, oldself;\r
388                 target_predator = self.predator.predator;\r
389 \r
390                 Vore_Regurgitate(self);\r
391                 if(random() < cvar("g_vore_stealprey"))\r
392                 if not(teams_matter && self.predator.team == target_predator.team) // don't steal a team mate's prey\r
393                 if(Swallow_condition_check(self))\r
394                 {\r
395                         oldself = self;\r
396                         self = target_predator;\r
397                         Vore_Swallow(oldself);\r
398                         self = oldself;\r
399                 }\r
400         }\r
401         else if(vlen(self.predator.velocity) > cvar("g_balance_vore_regurgitate_speedcap"))\r
402                 Vore_Regurgitate(self);\r
403 \r
404         // apply delayed regurgitating\r
405         if(self.predator.regurgitate_prepare && time > self.predator.regurgitate_prepare)\r
406         {\r
407                 self.predator.regurgitate_prepare = 0;\r
408                 self.predator.complain_swallow = time + complain_delay;\r
409                 Vore_Regurgitate(self);\r
410         }\r
411 \r
412         if(self.predator.digesting == TRUE)\r
413                 Vore_Digest();\r
414         if(teams_matter && self.team == self.predator.team)\r
415                 Vore_Teamheal();\r
416 \r
417         if(self.BUTTON_ATCK)\r
418                 Vore_StomachKick();\r
419         if(self.BUTTON_JUMP)\r
420                 Vore_StomachLeave();\r
421 \r
422         self.stat_canleave = Vore_PreyCanLeave();\r
423 \r
424         Vore_CameraEffect_Apply();\r
425 }