The "start the predator's digestion" feature doesn't make too much sense now. We...
[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 void Vore_Swallow(entity e)\r
101 {\r
102         // this player is beening swallowed by another player, apply the proper changes\r
103         e.vore_oldmovetype = e.movetype;\r
104         e.vore_oldsolid = e.solid;\r
105         e.vore_oldview_ofs_z = e.view_ofs_z;\r
106 \r
107         setorigin(e, e.eater.origin);\r
108         e.velocity = '0 0 0';\r
109         e.movetype = MOVETYPE_FOLLOW;\r
110         e.solid = SOLID_NOT;\r
111         e.alpha = -1; // best way of hiding / showing the eaten player\r
112         e.aiment = e.eater; // follow the predator. Is automatically unset\r
113 \r
114         Vore_CameraEffect_Set(e);\r
115 \r
116         // drop keys (KH) and flags (CTF) when we get swallowed\r
117         kh_Key_DropAll(e, FALSE);\r
118         if(e.flagcarried)\r
119                 DropFlag(e.flagcarried, world, e.eater);\r
120 \r
121         if(stov(cvar_string("g_vore_regurgitatecolor_released")))\r
122                 e.colormod = stov(cvar_string("g_vore_regurgitatecolor_released"));\r
123 \r
124         PlayerSound(e.eater, playersound_swallow, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
125         setanim(e.eater, e.eater.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing \ regurgitating\r
126         e.eater.stomach_load += 1;\r
127         e.eater.regurgitate_prepare = 0;\r
128         Vore_Weight_apply(e.eater);\r
129 \r
130         e.system_delay = e.eater.system_delay = time + system_delay_time;\r
131 }\r
132 \r
133 void Vore_Regurgitate(entity e)\r
134 {\r
135         // this player is being released from their predator, apply the proper changes\r
136         e.movetype = e.vore_oldmovetype;\r
137         if(e.health > 0) // leave SOLID_NOT for dead bodies\r
138                 e.solid = e.vore_oldsolid;\r
139         e.view_ofs_z = e.vore_oldview_ofs_z;\r
140         e.alpha = default_player_alpha; // best way of hiding / showing the eaten player\r
141 \r
142         // velocities\r
143         local vector oldforward, oldright, oldup;\r
144         oldforward = v_forward;\r
145         oldright = v_right;\r
146         oldup = v_up;\r
147         makevectors(e.eater.v_angle);\r
148         e.velocity = v_forward * cvar("g_balance_vore_regurgitate_force");\r
149         e.eater.velocity += -v_forward * cvar("g_balance_vore_regurgitate_eaterforce");\r
150         v_forward = oldforward;\r
151         v_right = oldright;\r
152         v_up = oldup;\r
153 \r
154         PlayerSound(e.eater, playersound_regurgitate, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
155         setanim(e.eater, e.eater.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing \ regurgitating\r
156         pointparticles(particleeffectnum("regurgitate"), e.eater.origin, '0 0 0', 1);\r
157         e.eater.stomach_load -= 1;\r
158         e.eater.regurgitate_prepare = 0;\r
159         e.eater.swallow_delay = time + cvar("g_balance_vore_swallow_delay");\r
160         Vore_Weight_apply(e.eater);\r
161 \r
162         e.system_delay = e.eater.system_delay = time + system_delay_time;\r
163         e.eater = world;\r
164 }\r
165 \r
166 void Vore_Gurglesound();\r
167 void Vore_Disconnect()\r
168 {\r
169         // frees prey from their predators when someone disconnects or goes spectating\r
170 \r
171         // prey disconnects or goes spectating while inside someone's belly:\r
172         if(self.eater.classname == "player")\r
173         {\r
174                 self.view_ofs_z = self.vore_oldview_ofs_z;\r
175                 self.eater.stomach_load -= 1;\r
176                 Vore_Weight_apply(self.eater);\r
177                 self.eater = world;\r
178         }\r
179 \r
180         // pred disconnects or goes spectating with players in their belly:\r
181         else if(self.stomach_load > 0)\r
182         {\r
183                 entity head;\r
184                 FOR_EACH_PLAYER(head)\r
185                 {\r
186                         if(head.eater == self)\r
187                                 Vore_Regurgitate(head);\r
188                 }\r
189                 Vore_Gurglesound(); // stop the gurgling sound\r
190         }\r
191 }\r
192 \r
193 .float digestion_step;\r
194 void Vore_Digest()\r
195 {\r
196         // apply digestion to prey\r
197         if(time > self.eater.digestion_step + steptime)\r
198         {\r
199                 Damage(self, self.eater, self.eater, cvar("g_balance_vore_digestion_damage"), DEATH_DIGESTION, self.origin, '0 0 0');\r
200                 if(cvar("g_balance_vore_digestion_vampire") && self.eater.health < cvar("g_balance_vore_digestion_vampire_stable"))\r
201                         self.eater.health += cvar("g_balance_vore_digestion_vampire");\r
202 \r
203                 if (self.eater.digestsound_finished < time)\r
204                 {\r
205                         self.eater.digestsound_finished = time + 0.5;\r
206                         PlayerSound (self.eater, playersound_digest, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);\r
207                 }\r
208                 self.eater.digestion_step = time;\r
209         }\r
210 \r
211         if(self.health <= 0)\r
212         if(stov(cvar_string("g_vore_regurgitatecolor_digested")))\r
213                 self.colormod = stov(cvar_string("g_vore_regurgitatecolor_digested"));\r
214 }\r
215 \r
216 .float teamheal_step;\r
217 void Vore_Teamheal()\r
218 {\r
219         if(cvar("g_balance_vore_teamheal") && self.health < cvar("g_balance_vore_teamheal_stable"))\r
220         if(time > self.teamheal_step + steptime)\r
221         {\r
222                 self.health += cvar("g_balance_vore_teamheal");\r
223                 self.teamheal_step = time;\r
224         }\r
225 }\r
226 \r
227 .float stomachkick_delay;\r
228 void Vore_StomachKick()\r
229 {\r
230         // allows prey to kick the predator's stomach and do some damage / attempt to escape, or bring the predator's digestion upon their self when there's no other option\r
231         if(self.eater.classname != "player")\r
232                 return;\r
233 \r
234         // kick the predator's stomach and do damage, or escape if we are lucky\r
235         if(self.BUTTON_ATCK)\r
236         if(time > self.stomachkick_delay)\r
237         {\r
238                 float damage;\r
239                 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
240                 Damage(self.eater, self, self, damage, DEATH_STOMACHKICK, self.eater.origin, '0 0 0');\r
241                 sound(self.eater, CHAN_PROJECTILE, "weapons/stomachkick.ogg", VOL_BASE, ATTN_NORM);\r
242 \r
243                 if(cvar("g_balance_vore_kick_escapeprobability") >= random())\r
244                         Vore_Regurgitate(self);\r
245 \r
246                 self.stomachkick_delay = time + cvar("g_balance_vore_kick_delay");\r
247         }\r
248 }\r
249 \r
250 .float gurglesound_finished, gurglesound_oldstomachload;\r
251 void Vore_Gurglesound()\r
252 {\r
253         if(time > self.gurglesound_finished || self.gurglesound_oldstomachload != self.stomach_load)\r
254         {\r
255                 GlobalSound(self.playersound_gurgle, CHAN_TRIGGER, VOICETYPE_GURGLE);\r
256 \r
257                 self.gurglesound_finished = time + 11; // yes, hard coded sound length. I know it's bad but what can I do?\r
258                 self.gurglesound_oldstomachload = self.stomach_load;\r
259         }\r
260 }\r
261 \r
262 void Vore()\r
263 {\r
264         // if we are free, show our stomach load on the HUD. Otherwise, show the predator's\r
265         if(self.eater.classname == "player")\r
266         {\r
267                 self.stat_stomachload = self.eater.stomach_load;\r
268                 self.stat_digesting = self.eater.digesting;\r
269                 self.stat_eaten = num_for_edict(self.eater);\r
270         }\r
271         else\r
272         {\r
273                 self.stat_stomachload = self.stomach_load;\r
274                 self.stat_digesting = self.digesting;\r
275                 self.stat_eaten = 0;\r
276         }\r
277 \r
278         // skip the vore system under some circumstances\r
279         if(time < game_starttime)\r
280         {\r
281                 Vore_Disconnect();\r
282                 return;\r
283         }\r
284         if(self.spectatee_status)\r
285                 return;\r
286         if(time < self.system_delay)\r
287                 return;\r
288 \r
289 // --------------------------------\r
290 // Code that addresses predators:\r
291 // --------------------------------\r
292 \r
293         entity prey;\r
294         prey = Swallow_distance_check();\r
295 \r
296         // attempt to swallow our new prey if there's any in range\r
297         if(self.BUTTON_ATCK && !self.BUTTON_REGURGITATE && self.swallow_delay < time)\r
298         if(Swallow_condition_check(prey))\r
299         {\r
300                 prey.eater = self;\r
301                 Vore_Swallow(prey);\r
302                 self.swallow_delay = time + cvar("g_balance_vore_swallow_delay");\r
303 \r
304                 if(self.team == prey.team && teamplay)\r
305                         centerprint(self, "You have swallowed a team mate, use caution!");\r
306 \r
307                 // block firing for a small amount of time when voring, or we'll be firing the next frame after we swallow\r
308                 self.weapon_delay = time + 0.25;\r
309         }\r
310 \r
311         // start / stop digestion on command, if the player has someone in their stomach\r
312         if(self.BUTTON_DIGEST)\r
313         {\r
314                 if(self.stomach_load)\r
315                 {\r
316                         if(time > self.digest_button_delay)\r
317                         {\r
318                                 self.digesting = !self.digesting;\r
319                                 self.digest_button_delay = time + button_delay;\r
320                         }\r
321                 }\r
322                 else if(time > self.complain_swallow)\r
323                 {\r
324                         play2(self, "weapons/unavailable.wav");\r
325                         sprint(self, "There is nothing to digest\n");\r
326                         self.complain_swallow = time + complain_delay;\r
327                 }\r
328         }\r
329         if(!self.stomach_load)\r
330                 self.digesting = FALSE;\r
331 \r
332         // release players from this player's stomach on command\r
333         if(self.BUTTON_REGURGITATE)\r
334         {\r
335                 if(self.stomach_load)\r
336                 {\r
337                         if(time > self.regurgitate_button_delay)\r
338                         {\r
339                                 self.regurgitate_prepare = time + cvar("g_balance_vore_regurgitate_delay");\r
340                                 PlayerSound(self, playersound_regurgitate_prepare, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
341                                 self.regurgitate_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 regurgitate\n");\r
348                         self.complain_swallow = time + complain_delay;\r
349                 }\r
350         }\r
351 \r
352         if(cvar("g_vore_gurglesound"))\r
353                 Vore_Gurglesound();\r
354 \r
355 // --------------------------------\r
356 // Code that addresses the prey:\r
357 // --------------------------------\r
358 \r
359         if(self.eater.classname != "player")\r
360                 return;\r
361 \r
362         if(self.eater.deadflag || self.deadflag)\r
363                 Vore_Regurgitate(self);\r
364         else if(self.eater.eater.classname == "player") // don't allow a player inside a player inside another player :)\r
365                 Vore_Regurgitate(self);\r
366         else if(vlen(self.eater.velocity) > cvar("g_balance_vore_regurgitate_velocitylimit"))\r
367                 Vore_Regurgitate(self);\r
368 \r
369         // apply delayed regurgitating\r
370         if(self.eater.regurgitate_prepare && time > self.eater.regurgitate_prepare)\r
371         {\r
372                 self.eater.regurgitate_prepare = 0;\r
373                 self.eater.complain_swallow = time + complain_delay;\r
374                 Vore_Regurgitate(self);\r
375         }\r
376 \r
377         if(self.eater.digesting == TRUE)\r
378                 Vore_Digest();\r
379         if(teams_matter && self.team == self.eater.team)\r
380                 Vore_Teamheal();\r
381 \r
382         Vore_StomachKick();\r
383 \r
384         Vore_CameraEffect_Apply();\r
385 }