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