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