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