]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/vore.qc
Team healing AI now working as intended, but not ready yet. Also fix another tiny...
[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.eater.team == e.team && teamplay)\r
128                 centerprint(e.eater, "^3You have swallowed a team mate, use caution!");\r
129 \r
130         PlayerSound(e.eater, playersound_swallow, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
131         setanim(e.eater, e.eater.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing / regurgitating\r
132         e.eater.stomach_load += 1;\r
133         e.eater.regurgitate_prepare = 0;\r
134         Vore_Weight_apply(e.eater);\r
135 \r
136         // block firing for a small amount of time when voring, or we'll be firing the next frame after we swallow\r
137         e.eater.weapon_delay = time + button_delay;\r
138         e.eater.swallow_delay = time + cvar("g_balance_vore_swallow_delay");\r
139         e.system_delay = e.eater.system_delay = time + system_delay_time;\r
140 }\r
141 \r
142 void Vore_Regurgitate(entity e)\r
143 {\r
144         // this player is being released from their predator, apply the proper changes\r
145         e.movetype = e.vore_oldmovetype;\r
146         if(e.health > 0) // leave SOLID_NOT for dead bodies\r
147                 e.solid = e.vore_oldsolid;\r
148         e.view_ofs_z = e.vore_oldview_ofs_z;\r
149         e.alpha = default_player_alpha; // best way of hiding / showing the eaten player\r
150 \r
151         // velocities\r
152         local vector oldforward, oldright, oldup;\r
153         oldforward = v_forward;\r
154         oldright = v_right;\r
155         oldup = v_up;\r
156         makevectors(e.eater.v_angle);\r
157         e.velocity = v_forward * cvar("g_balance_vore_regurgitate_force");\r
158         e.eater.velocity += -v_forward * cvar("g_balance_vore_regurgitate_eaterforce");\r
159         v_forward = oldforward;\r
160         v_right = oldright;\r
161         v_up = oldup;\r
162 \r
163         e.pusher = e.eater; // so we can frag players by regurgitating them in deadly pits\r
164         e.pushltime = time + cvar("g_maxpushtime");\r
165 \r
166         PlayerSound(e.eater, playersound_regurgitate, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
167         setanim(e.eater, e.eater.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing / regurgitating\r
168         pointparticles(particleeffectnum("regurgitate"), e.eater.origin, '0 0 0', 1);\r
169         e.eater.stomach_load -= 1;\r
170         e.eater.regurgitate_prepare = 0;\r
171         e.eater.swallow_delay = time + cvar("g_balance_vore_swallow_delay");\r
172         Vore_Weight_apply(e.eater);\r
173 \r
174         e.system_delay = e.eater.system_delay = time + system_delay_time;\r
175         e.eater = world;\r
176 }\r
177 \r
178 void Vore_Gurglesound();\r
179 void Vore_Disconnect()\r
180 {\r
181         // frees prey from their predators when someone disconnects or goes spectating\r
182 \r
183         // prey disconnects or goes spectating while inside someone's belly:\r
184         if(self.eater.classname == "player")\r
185         {\r
186                 self.view_ofs_z = self.vore_oldview_ofs_z;\r
187                 self.eater.stomach_load -= 1;\r
188                 Vore_Weight_apply(self.eater);\r
189                 self.eater = world;\r
190         }\r
191 \r
192         // pred disconnects or goes spectating with players in their belly:\r
193         else if(self.stomach_load > 0)\r
194         {\r
195                 entity head;\r
196                 FOR_EACH_PLAYER(head)\r
197                 {\r
198                         if(head.eater == self)\r
199                                 Vore_Regurgitate(head);\r
200                 }\r
201                 Vore_Gurglesound(); // stop the gurgling sound\r
202         }\r
203 }\r
204 \r
205 .float digestion_step;\r
206 void Vore_Digest()\r
207 {\r
208         // apply digestion to prey\r
209         if(time > self.eater.digestion_step + steptime)\r
210         {\r
211                 Damage(self, self.eater, self.eater, cvar("g_balance_vore_digestion_damage"), DEATH_DIGESTION, self.origin, '0 0 0');\r
212                 if(cvar("g_balance_vore_digestion_vampire") && self.eater.health < cvar("g_balance_vore_digestion_vampire_stable"))\r
213                         self.eater.health += cvar("g_balance_vore_digestion_vampire");\r
214 \r
215                 if (self.eater.digestsound_finished < time)\r
216                 {\r
217                         self.eater.digestsound_finished = time + 0.5;\r
218                         PlayerSound (self.eater, playersound_digest, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);\r
219                 }\r
220                 self.eater.digestion_step = time;\r
221         }\r
222 \r
223         if(self.health <= 0)\r
224         if(stov(cvar_string("g_vore_regurgitatecolor_digested")))\r
225                 self.colormod = stov(cvar_string("g_vore_regurgitatecolor_digested"));\r
226 }\r
227 \r
228 .float teamheal_step;\r
229 void Vore_Teamheal()\r
230 {\r
231         if(cvar("g_balance_vore_teamheal") && self.health < cvar("g_balance_vore_teamheal_stable"))\r
232         if(time > self.teamheal_step + steptime)\r
233         {\r
234                 self.health += cvar("g_balance_vore_teamheal");\r
235                 self.teamheal_step = time;\r
236         }\r
237 }\r
238 \r
239 .float stomachkick_delay;\r
240 void Vore_StomachKick()\r
241 {\r
242         // allows prey to kick the predator's stomach and do some damage or attempt to escape\r
243         if(self.eater.classname != "player")\r
244                 return;\r
245 \r
246         if(time > self.stomachkick_delay)\r
247         {\r
248                 float damage;\r
249                 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
250                 Damage(self.eater, self, self, damage, DEATH_STOMACHKICK, self.eater.origin, '0 0 0');\r
251                 sound(self.eater, CHAN_PROJECTILE, "weapons/stomachkick.ogg", VOL_BASE, ATTN_NORM);\r
252 \r
253                 if(cvar("g_balance_vore_kick_escapeprobability") >= random())\r
254                         Vore_Regurgitate(self);\r
255 \r
256                 self.stomachkick_delay = time + cvar("g_balance_vore_kick_delay");\r
257         }\r
258 }\r
259 \r
260 .float gurglesound_finished, gurglesound_oldstomachload;\r
261 void Vore_Gurglesound()\r
262 {\r
263         if(time > self.gurglesound_finished || self.gurglesound_oldstomachload != self.stomach_load)\r
264         {\r
265                 GlobalSound(self.playersound_gurgle, CHAN_TRIGGER, VOICETYPE_GURGLE);\r
266 \r
267                 self.gurglesound_finished = time + 11; // yes, hard coded sound length. I know it's bad but what can I do?\r
268                 self.gurglesound_oldstomachload = self.stomach_load;\r
269         }\r
270 }\r
271 \r
272 void Vore()\r
273 {\r
274         // if we are free, show our stomach load on the HUD. Otherwise, show the predator's\r
275         if(self.eater.classname == "player")\r
276         {\r
277                 self.stat_stomachload = self.eater.stomach_load;\r
278                 self.stat_digesting = self.eater.digesting;\r
279                 self.stat_eaten = num_for_edict(self.eater);\r
280         }\r
281         else\r
282         {\r
283                 self.stat_stomachload = self.stomach_load;\r
284                 self.stat_digesting = self.digesting;\r
285                 self.stat_eaten = 0;\r
286         }\r
287 \r
288         // skip the vore system under some circumstances\r
289         if(time < game_starttime)\r
290         {\r
291                 Vore_Disconnect();\r
292                 return;\r
293         }\r
294         if(self.spectatee_status)\r
295                 return;\r
296         if(time < self.system_delay)\r
297                 return;\r
298 \r
299 // --------------------------------\r
300 // Code that addresses predators:\r
301 // --------------------------------\r
302 \r
303         entity prey;\r
304         prey = Swallow_distance_check();\r
305 \r
306         // attempt to swallow our new prey if there's any in range\r
307         if(self.BUTTON_ATCK && !self.BUTTON_REGURGITATE && self.swallow_delay < time)\r
308         if(Swallow_condition_check(prey))\r
309                 Vore_Swallow(prey);\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_VOICE, 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         {\r
366                 entity targeteater, oldself;\r
367                 targeteater = self.eater.eater;\r
368 \r
369                 Vore_Regurgitate(self);\r
370                 if(random() < cvar("g_vore_stealprey"))\r
371                 if(Swallow_condition_check(self))\r
372                 {\r
373                         oldself = self;\r
374                         self = targeteater;\r
375                         Vore_Swallow(oldself);\r
376                         self = oldself;\r
377                 }\r
378         }\r
379         else if(vlen(self.eater.velocity) > cvar("g_balance_vore_regurgitate_velocitylimit"))\r
380                 Vore_Regurgitate(self);\r
381 \r
382         // apply delayed regurgitating\r
383         if(self.eater.regurgitate_prepare && time > self.eater.regurgitate_prepare)\r
384         {\r
385                 self.eater.regurgitate_prepare = 0;\r
386                 self.eater.complain_swallow = time + complain_delay;\r
387                 Vore_Regurgitate(self);\r
388         }\r
389 \r
390         if(self.eater.digesting == TRUE)\r
391                 Vore_Digest();\r
392         if(teams_matter && self.team == self.eater.team)\r
393                 Vore_Teamheal();\r
394 \r
395         if(self.BUTTON_ATCK)\r
396                 Vore_StomachKick();\r
397 \r
398         Vore_CameraEffect_Apply();\r
399 }