Fix comments and move check
[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\r
231         if(self.eater.classname != "player")\r
232                 return;\r
233 \r
234         if(time > self.stomachkick_delay)\r
235         {\r
236                 float damage;\r
237                 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
238                 Damage(self.eater, self, self, damage, DEATH_STOMACHKICK, self.eater.origin, '0 0 0');\r
239                 sound(self.eater, CHAN_PROJECTILE, "weapons/stomachkick.ogg", VOL_BASE, ATTN_NORM);\r
240 \r
241                 if(cvar("g_balance_vore_kick_escapeprobability") >= random())\r
242                         Vore_Regurgitate(self);\r
243 \r
244                 self.stomachkick_delay = time + cvar("g_balance_vore_kick_delay");\r
245         }\r
246 }\r
247 \r
248 .float gurglesound_finished, gurglesound_oldstomachload;\r
249 void Vore_Gurglesound()\r
250 {\r
251         if(time > self.gurglesound_finished || self.gurglesound_oldstomachload != self.stomach_load)\r
252         {\r
253                 GlobalSound(self.playersound_gurgle, CHAN_TRIGGER, VOICETYPE_GURGLE);\r
254 \r
255                 self.gurglesound_finished = time + 11; // yes, hard coded sound length. I know it's bad but what can I do?\r
256                 self.gurglesound_oldstomachload = self.stomach_load;\r
257         }\r
258 }\r
259 \r
260 void Vore()\r
261 {\r
262         // if we are free, show our stomach load on the HUD. Otherwise, show the predator's\r
263         if(self.eater.classname == "player")\r
264         {\r
265                 self.stat_stomachload = self.eater.stomach_load;\r
266                 self.stat_digesting = self.eater.digesting;\r
267                 self.stat_eaten = num_for_edict(self.eater);\r
268         }\r
269         else\r
270         {\r
271                 self.stat_stomachload = self.stomach_load;\r
272                 self.stat_digesting = self.digesting;\r
273                 self.stat_eaten = 0;\r
274         }\r
275 \r
276         // skip the vore system under some circumstances\r
277         if(time < game_starttime)\r
278         {\r
279                 Vore_Disconnect();\r
280                 return;\r
281         }\r
282         if(self.spectatee_status)\r
283                 return;\r
284         if(time < self.system_delay)\r
285                 return;\r
286 \r
287 // --------------------------------\r
288 // Code that addresses predators:\r
289 // --------------------------------\r
290 \r
291         entity prey;\r
292         prey = Swallow_distance_check();\r
293 \r
294         // attempt to swallow our new prey if there's any in range\r
295         if(self.BUTTON_ATCK && !self.BUTTON_REGURGITATE && self.swallow_delay < time)\r
296         if(Swallow_condition_check(prey))\r
297         {\r
298                 prey.eater = self;\r
299                 Vore_Swallow(prey);\r
300                 self.swallow_delay = time + cvar("g_balance_vore_swallow_delay");\r
301 \r
302                 if(self.team == prey.team && teamplay)\r
303                         centerprint(self, "You have swallowed a team mate, use caution!");\r
304 \r
305                 // block firing for a small amount of time when voring, or we'll be firing the next frame after we swallow\r
306                 self.weapon_delay = time + 0.25;\r
307         }\r
308 \r
309         // start / stop digestion on command, if the player has someone in their stomach\r
310         if(self.BUTTON_DIGEST)\r
311         {\r
312                 if(self.stomach_load)\r
313                 {\r
314                         if(time > self.digest_button_delay)\r
315                         {\r
316                                 self.digesting = !self.digesting;\r
317                                 self.digest_button_delay = time + button_delay;\r
318                         }\r
319                 }\r
320                 else if(time > self.complain_swallow)\r
321                 {\r
322                         play2(self, "weapons/unavailable.wav");\r
323                         sprint(self, "There is nothing to digest\n");\r
324                         self.complain_swallow = time + complain_delay;\r
325                 }\r
326         }\r
327         if(!self.stomach_load)\r
328                 self.digesting = FALSE;\r
329 \r
330         // release players from this player's stomach on command\r
331         if(self.BUTTON_REGURGITATE)\r
332         {\r
333                 if(self.stomach_load)\r
334                 {\r
335                         if(time > self.regurgitate_button_delay)\r
336                         {\r
337                                 self.regurgitate_prepare = time + cvar("g_balance_vore_regurgitate_delay");\r
338                                 PlayerSound(self, playersound_regurgitate_prepare, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
339                                 self.regurgitate_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 regurgitate\n");\r
346                         self.complain_swallow = time + complain_delay;\r
347                 }\r
348         }\r
349 \r
350         if(cvar("g_vore_gurglesound"))\r
351                 Vore_Gurglesound();\r
352 \r
353 // --------------------------------\r
354 // Code that addresses the prey:\r
355 // --------------------------------\r
356 \r
357         if(self.eater.classname != "player")\r
358                 return;\r
359 \r
360         if(self.eater.deadflag || self.deadflag)\r
361                 Vore_Regurgitate(self);\r
362         else if(self.eater.eater.classname == "player") // don't allow a player inside a player inside another player :)\r
363                 Vore_Regurgitate(self);\r
364         else if(vlen(self.eater.velocity) > cvar("g_balance_vore_regurgitate_velocitylimit"))\r
365                 Vore_Regurgitate(self);\r
366 \r
367         // apply delayed regurgitating\r
368         if(self.eater.regurgitate_prepare && time > self.eater.regurgitate_prepare)\r
369         {\r
370                 self.eater.regurgitate_prepare = 0;\r
371                 self.eater.complain_swallow = time + complain_delay;\r
372                 Vore_Regurgitate(self);\r
373         }\r
374 \r
375         if(self.eater.digesting == TRUE)\r
376                 Vore_Digest();\r
377         if(teams_matter && self.team == self.eater.team)\r
378                 Vore_Teamheal();\r
379 \r
380         if(self.BUTTON_ATCK)\r
381                 Vore_StomachKick();\r
382 \r
383         Vore_CameraEffect_Apply();\r
384 }