]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/vore.qc
Attempt to implement smooth camera lowering when we get swallowed (so we see our...
[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         local float step;\r
73         step = 2 * frametime; // CVAR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\r
74 \r
75         if(self.cameraeffect_current >= self.cameraeffect_target + step)\r
76                 self.cameraeffect_current -= step;\r
77         else if(self.cameraeffect_current <= self.cameraeffect_target - step)\r
78                 self.cameraeffect_current += step;\r
79 \r
80 bprint(strcat(ftos(self.cameraeffect_current), "<<-----------\n"));\r
81         self.view_ofs_z = self.vore_oldview_ofs_z / self.cameraeffect_current;\r
82 }\r
83 \r
84 void Vore_Weight_apply(entity e)\r
85 {\r
86         // apply stomach weight that makes you heavier the more you eat\r
87         // slowing the player is applied in cl_physics.qc\r
88         if(e.stomach_load != e.vore_oldstomachload)\r
89                 e.gravity += 1 + (e.stomach_load * cvar("g_balance_vore_weight_gravity") - e.vore_oldstomachload);\r
90         if(e.gravity == 0)\r
91                 e.gravity = 0.00001; // 0 becomes 1 for .gravity, so do this to allow 0 gravity\r
92         e.vore_oldstomachload = e.stomach_load;\r
93 }\r
94 \r
95 void Vore_Swallow(entity e)\r
96 {\r
97         // this player is beening swallowed by another player, apply the proper changes\r
98         e.vore_oldmovetype = e.movetype;\r
99         e.vore_oldsolid = e.solid;\r
100         e.vore_oldview_ofs_z = e.view_ofs_z;\r
101 \r
102         setorigin(e, e.eater.origin);\r
103         e.velocity = '0 0 0';\r
104         e.movetype = MOVETYPE_FOLLOW;\r
105         e.solid = SOLID_NOT;\r
106         e.alpha = -1; // best way of hiding / showing the eaten player\r
107         e.aiment = e.eater; // follow the predator. Is automatically unset\r
108 \r
109         /*e.cameraeffect_current = e.view_ofs_z * 2;\r
110         e.cameraeffect_target = e.view_ofs_z / 2; // best positioning for the stomach model*/\r
111 \r
112         Vore_CameraEffect_Set(e);\r
113 \r
114         // drop keys (KH) and flags (CTF) when we get swallowed\r
115         kh_Key_DropAll(e, FALSE);\r
116         if(e.flagcarried)\r
117                 DropFlag(e.flagcarried, world, e.eater);\r
118 \r
119         if(stov(cvar_string("g_vore_regurgitatecolor_released")))\r
120                 e.colormod = stov(cvar_string("g_vore_regurgitatecolor_released"));\r
121 \r
122         PlayerSound(e.eater, playersound_swallow, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
123         setanim(e.eater, e.eater.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing \ regurgitating\r
124         e.eater.stomach_load += 1;\r
125         e.eater.regurgitate_prepare = 0;\r
126         Vore_Weight_apply(e.eater);\r
127 \r
128         e.system_delay = e.eater.system_delay = time + system_delay_time;\r
129 }\r
130 \r
131 void Vore_Regurgitate(entity e)\r
132 {\r
133         // this player is being released from their predator, apply the proper changes\r
134         e.movetype = e.vore_oldmovetype;\r
135         if(e.health > 0) // leave SOLID_NOT for dead bodies\r
136                 e.solid = e.vore_oldsolid;\r
137         e.view_ofs_z = e.vore_oldview_ofs_z;\r
138         e.alpha = default_player_alpha; // best way of hiding / showing the eaten player\r
139 \r
140         //e.view_ofs_z *= 2; // best positioning for the stomach model\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 += 25;\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         // start the predator's digestion\r
250         if(self.BUTTON_ATCK2)\r
251         {\r
252                 centerprint(self.eater, strcat(self.netname, " has triggered your digestion"));\r
253                 self.eater.digesting = TRUE;\r
254         }\r
255 }\r
256 \r
257 .float gurglesound_finished, gurglesound_oldstomachload;\r
258 void Vore_Gurglesound()\r
259 {\r
260         if(time > self.gurglesound_finished || self.gurglesound_oldstomachload != self.stomach_load)\r
261         {\r
262                 GlobalSound(self.playersound_gurgle, CHAN_TRIGGER, VOICETYPE_GURGLE);\r
263 \r
264                 self.gurglesound_finished = time + 11; // yes, hard coded sound length. I know it's bad but what can I do?\r
265                 self.gurglesound_oldstomachload = self.stomach_load;\r
266         }\r
267 }\r
268 \r
269 void Vore()\r
270 {\r
271         // if we are free, show our stomach load on the HUD. Otherwise, show the predator's\r
272         if(self.eater.classname == "player")\r
273         {\r
274                 self.stat_stomachload = self.eater.stomach_load;\r
275                 self.stat_digesting = self.eater.digesting;\r
276                 self.stat_eaten = num_for_edict(self.eater);\r
277         }\r
278         else\r
279         {\r
280                 self.stat_stomachload = self.stomach_load;\r
281                 self.stat_digesting = self.digesting;\r
282                 self.stat_eaten = 0;\r
283         }\r
284 \r
285         // skip the vore system under some circumstances\r
286         if(time < game_starttime)\r
287         {\r
288                 Vore_Disconnect();\r
289                 return;\r
290         }\r
291         if(self.spectatee_status)\r
292                 return;\r
293         if(time < self.system_delay)\r
294                 return;\r
295 \r
296 // --------------------------------\r
297 // Code that addresses predators:\r
298 // --------------------------------\r
299 \r
300         entity prey;\r
301         prey = Swallow_distance_check();\r
302 \r
303         // attempt to swallow our new prey if there's any in range\r
304         if(self.BUTTON_ATCK && !self.BUTTON_REGURGITATE && self.swallow_delay < time)\r
305         if(Swallow_condition_check(prey))\r
306         {\r
307                 prey.eater = self;\r
308                 Vore_Swallow(prey);\r
309                 self.swallow_delay = time + cvar("g_balance_vore_swallow_delay");\r
310 \r
311                 if(self.team == prey.team && teamplay)\r
312                         centerprint(self, "You have swallowed a team mate, use caution!");\r
313 \r
314                 // block firing for a small amount of time when voring, or we'll be firing the next frame after we swallow\r
315                 self.weapon_delay = time + 0.25;\r
316         }\r
317 \r
318         // start / stop digestion on command, if the player has someone in their stomach\r
319         if(self.BUTTON_DIGEST)\r
320         {\r
321                 if(self.stomach_load)\r
322                 {\r
323                         if(time > self.digest_button_delay)\r
324                         {\r
325                                 self.digesting = !self.digesting;\r
326                                 self.digest_button_delay = time + button_delay;\r
327                         }\r
328                 }\r
329                 else if(time > self.complain_swallow)\r
330                 {\r
331                         play2(self, "weapons/unavailable.wav");\r
332                         sprint(self, "There is nothing to digest\n");\r
333                         self.complain_swallow = time + complain_delay;\r
334                 }\r
335         }\r
336         if(!self.stomach_load)\r
337                 self.digesting = FALSE;\r
338 \r
339         // release players from this player's stomach on command\r
340         if(self.BUTTON_REGURGITATE)\r
341         {\r
342                 if(self.stomach_load)\r
343                 {\r
344                         if(time > self.regurgitate_button_delay)\r
345                         {\r
346                                 self.regurgitate_prepare = time + cvar("g_balance_vore_regurgitate_delay");\r
347                                 PlayerSound(self, playersound_regurgitate_prepare, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
348                                 self.regurgitate_button_delay = time + button_delay;\r
349                         }\r
350                 }\r
351                 else if(time > self.complain_swallow)\r
352                 {\r
353                         play2(self, "weapons/unavailable.wav");\r
354                         sprint(self, "There is nothing to regurgitate\n");\r
355                         self.complain_swallow = time + complain_delay;\r
356                 }\r
357         }\r
358 \r
359         if(cvar("g_vore_gurglesound"))\r
360                 Vore_Gurglesound();\r
361 \r
362 // --------------------------------\r
363 // Code that addresses the prey:\r
364 // --------------------------------\r
365 \r
366         if(self.eater.classname != "player")\r
367                 return;\r
368 \r
369         if(self.eater.deadflag || self.deadflag)\r
370                 Vore_Regurgitate(self);\r
371         else if(self.eater.eater.classname == "player") // don't allow a player inside a player inside another player :)\r
372                 Vore_Regurgitate(self);\r
373         else if(vlen(self.eater.velocity) > cvar("g_balance_vore_regurgitate_velocitylimit"))\r
374                 Vore_Regurgitate(self);\r
375 \r
376         // apply delayed regurgitating\r
377         if(self.eater.regurgitate_prepare && time > self.eater.regurgitate_prepare)\r
378         {\r
379                 self.eater.regurgitate_prepare = 0;\r
380                 self.eater.complain_swallow = time + complain_delay;\r
381                 Vore_Regurgitate(self);\r
382         }\r
383 \r
384         if(self.eater.digesting == TRUE)\r
385                 Vore_Digest();\r
386         if(teams_matter && self.team == self.eater.team)\r
387                 Vore_Teamheal();\r
388 \r
389         Vore_StomachKick();\r
390 \r
391         Vore_CameraEffect_Apply();\r
392 }