Show the predator's name on the stomach board.
[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;\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 not(vlen(self.velocity) > cvar("g_balance_vore_regurgitate_velocitylimit"))\r
30         if(self.eater.classname != "player") // we can't swallow players while inside someone's stomach ourselves\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 void Vore_Weight_apply(entity e)\r
60 {\r
61         // apply stomach weight that makes you heavier the more you eat\r
62         // slowing the player is applied in cl_physics.qc\r
63         if(e.stomach_load != e.vore_oldstomachload)\r
64                 e.gravity += 1 + (e.stomach_load * cvar("g_balance_vore_weight_gravity") - e.vore_oldstomachload);\r
65         if(e.gravity == 0)\r
66                 e.gravity = 0.00001; // 0 becomes 1 for .gravity, so do this to allow 0 gravity\r
67         e.vore_oldstomachload = e.stomach_load;\r
68 }\r
69 \r
70 void Vore_Swallow(entity e)\r
71 {\r
72         // this player is beening swallowed by another player, apply the proper changes\r
73         e.vore_oldmovetype = e.movetype;\r
74         e.vore_oldsolid = e.solid;\r
75 \r
76         setorigin(e, e.eater.origin);\r
77         e.velocity = '0 0 0';\r
78         e.movetype = MOVETYPE_FOLLOW;\r
79         e.solid = SOLID_NOT;\r
80         e.alpha = -1; // best way of hiding / showing the eaten player\r
81         e.aiment = e.eater; // follow the predator. Is automatically unset\r
82         e.view_ofs_z /= 2; // best positioning for the stomach model\r
83 \r
84         // drop keys (KH) and flags (CTF) when we get swallowed\r
85         kh_Key_DropAll(e, FALSE);\r
86         if(e.flagcarried)\r
87                 DropFlag(e.flagcarried, world, e.eater);\r
88 \r
89         if(stov(cvar_string("g_vore_regurgitatecolor_released")))\r
90                 e.colormod = stov(cvar_string("g_vore_regurgitatecolor_released"));\r
91 \r
92         PlayerSound(e.eater, playersound_swallow, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
93         setanim(e.eater, e.eater.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing \ regurgitating\r
94         e.eater.stomach_load += 1;\r
95         e.eater.regurgitate_prepare = 0;\r
96         Vore_Weight_apply(e.eater);\r
97 \r
98         e.system_delay = e.eater.system_delay = time + system_delay_time;\r
99 }\r
100 \r
101 void Vore_Regurgitate(entity e)\r
102 {\r
103         // this player is being released from their predator, apply the proper changes\r
104         e.movetype = e.vore_oldmovetype;\r
105         if(e.health > 0) // leave SOLID_NOT for dead bodies\r
106                 e.solid = e.vore_oldsolid;\r
107         e.alpha = default_player_alpha; // best way of hiding / showing the eaten player\r
108         e.view_ofs_z *= 2; // best positioning for the stomach model\r
109 \r
110         // velocities\r
111         local vector oldforward, oldright, oldup;\r
112         oldforward = v_forward;\r
113         oldright = v_right;\r
114         oldup = v_up;\r
115         makevectors(e.eater.v_angle);\r
116         e.velocity = v_forward * cvar("g_balance_vore_regurgitate_force");\r
117         e.eater.velocity += -v_forward * cvar("g_balance_vore_regurgitate_eaterforce");\r
118         v_forward = oldforward;\r
119         v_right = oldright;\r
120         v_up = oldup;\r
121 \r
122         PlayerSound(e.eater, playersound_regurgitate, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
123         setanim(e.eater, e.eater.anim_pain1, FALSE, TRUE, TRUE); // looks good for swallowing \ regurgitating\r
124         pointparticles(particleeffectnum("regurgitate"), e.eater.origin, '0 0 0', 1);\r
125         e.eater.stomach_load -= 1;\r
126         e.eater.regurgitate_prepare = 0;\r
127         e.eater.swallow_delay = time + cvar("g_balance_vore_swallow_delay");\r
128         Vore_Weight_apply(e.eater);\r
129 \r
130         e.system_delay = e.eater.system_delay = time + system_delay_time;\r
131         e.eater = world;\r
132 }\r
133 \r
134 void Vore_Disconnect()\r
135 {\r
136         // frees prey from their predators when someone disconnects or goes spectating\r
137 \r
138         // prey disconnects or goes spectating while inside someone's belly:\r
139         if(self.eater.classname == "player")\r
140         {\r
141                 self.view_ofs_z += 25;\r
142                 self.eater.stomach_load -= 1;\r
143                 Vore_Weight_apply(self.eater);\r
144                 self.eater = world;\r
145         }\r
146 \r
147         // pred disconnects or goes spectating with players in their belly:\r
148         else if(self.stomach_load > 0)\r
149         {\r
150                 entity head;\r
151                 FOR_EACH_PLAYER(head)\r
152                 {\r
153                         if(head.eater == self)\r
154                                 Vore_Regurgitate(head);\r
155                 }\r
156         }\r
157 }\r
158 \r
159 .float digestion_step;\r
160 void Vore_Digest()\r
161 {\r
162         // apply digestion to prey\r
163         if(time > self.eater.digestion_step + steptime)\r
164         {\r
165                 Damage(self, self.eater, self.eater, cvar("g_balance_vore_digestion_damage"), DEATH_DIGESTION, self.origin, '0 0 0');\r
166                 if(cvar("g_balance_vore_digestion_vampire") && self.eater.health < cvar("g_balance_vore_digestion_vampire_stable"))\r
167                         self.eater.health += cvar("g_balance_vore_digestion_vampire");\r
168 \r
169                 if (self.eater.digestsound_finished < time)\r
170                 {\r
171                         self.eater.digestsound_finished = time + 0.5;\r
172                         PlayerSound (self.eater, playersound_digest, CHAN_PLAYER, VOICETYPE_PLAYERSOUND);\r
173                 }\r
174                 self.eater.digestion_step = time;\r
175         }\r
176 \r
177         if(self.health <= 0)\r
178         if(stov(cvar_string("g_vore_regurgitatecolor_digested")))\r
179                 self.colormod = stov(cvar_string("g_vore_regurgitatecolor_digested"));\r
180 }\r
181 \r
182 .float teamheal_step;\r
183 void Vore_Teamheal()\r
184 {\r
185         if(cvar("g_balance_vore_teamheal") && self.health < cvar("g_balance_vore_teamheal_stable"))\r
186         if(time > self.teamheal_step + steptime)\r
187         {\r
188                 self.health += cvar("g_balance_vore_teamheal");\r
189                 self.teamheal_step = time;\r
190         }\r
191 }\r
192 \r
193 .float stomachkick_delay;\r
194 void Vore_StomachKick()\r
195 {\r
196         // 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
197         if(self.eater.classname != "player")\r
198                 return;\r
199 \r
200         // kick the predator's stomach and do damage, or escape if we are lucky\r
201         if(self.BUTTON_ATCK)\r
202         if(time > self.stomachkick_delay)\r
203         {\r
204                 float damage;\r
205                 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
206                 Damage(self.eater, self, self, damage, DEATH_STOMACHKICK, self.eater.origin, '0 0 0');\r
207                 sound(self.eater, CHAN_PROJECTILE, "weapons/stomachkick.ogg", VOL_BASE, ATTN_NORM);\r
208 \r
209                 if(cvar("g_balance_vore_kick_escapeprobability") >= random())\r
210                         Vore_Regurgitate(self);\r
211 \r
212                 self.stomachkick_delay = time + cvar("g_balance_vore_kick_delay");\r
213         }\r
214 \r
215         // start the predator's digestion\r
216         if(self.BUTTON_ATCK2)\r
217         {\r
218                 centerprint(self.eater, strcat(self.netname, " has triggered your digestion"));\r
219                 self.eater.digesting = TRUE;\r
220         }\r
221 }\r
222 \r
223 .float gurglesound_finished, gurglesound_oldstomachload;\r
224 void Vore_Gurglesound()\r
225 {\r
226         if(time > self.gurglesound_finished || self.gurglesound_oldstomachload != self.stomach_load)\r
227         {\r
228                 GlobalSound(self.playersound_gurgle, CHAN_TRIGGER, VOICETYPE_GURGLE);\r
229 \r
230                 self.gurglesound_finished = time + 11; // yes, hard coded sound length. I know it's bad but what can I do?\r
231                 self.gurglesound_oldstomachload = self.stomach_load;\r
232         }\r
233 }\r
234 \r
235 void Vore()\r
236 {\r
237         // if we are free, show our stomach load on the HUD. Otherwise, show the predator's\r
238         string x;\r
239         if(self.eater != self && self.eater.classname == "player")\r
240         {\r
241                 self.stat_stomachload = self.eater.stomach_load;\r
242                 self.stat_eaten = num_for_edict(self.eater);\r
243                 x = playername(self.eater);\r
244                 //bprint(strcat(ftos(self.stat_eaten), "<<--------------------------\n"));\r
245         }\r
246         else\r
247         {\r
248                 self.stat_stomachload = self.stomach_load;\r
249                 self.stat_eaten = 0;\r
250                 x = "NONE!";\r
251         }\r
252         //bprint(strcat(ftos(self.stat_eaten), "<<--------------------------\n"));\r
253         //bprint(strcat(playername(self), "<<--------------------------\n"));\r
254 \r
255         // skip the vore system under some circumstances\r
256         if(time < game_starttime)\r
257         {\r
258                 Vore_Disconnect();\r
259                 return;\r
260         }\r
261         if(self.spectatee_status)\r
262                 return;\r
263         if(time < self.system_delay)\r
264                 return;\r
265 \r
266 // --------------------------------\r
267 // Code that addresses predators:\r
268 // --------------------------------\r
269 \r
270         entity prey;\r
271         prey = Swallow_distance_check();\r
272 \r
273         // attempt to swallow our new prey if there's any in range\r
274         if(self.BUTTON_ATCK && !self.BUTTON_REGURGITATE && self.swallow_delay < time)\r
275         if(Swallow_condition_check(prey))\r
276         {\r
277                 prey.eater = self;\r
278                 Vore_Swallow(prey);\r
279                 self.swallow_delay = time + cvar("g_balance_vore_swallow_delay");\r
280 \r
281                 if(self.team == prey.team && teamplay)\r
282                         centerprint(self, "You have swallowed a team mate, use caution!");\r
283 \r
284                 // block firing for a small amount of time when voring, or we'll be firing the next frame after we swallow\r
285                 self.weapon_delay = time + 0.25;\r
286         }\r
287 \r
288         // start / stop digestion on command, if the player has someone in their stomach\r
289         if(self.BUTTON_DIGEST)\r
290         {\r
291                 if(self.stomach_load)\r
292                 {\r
293                         if(time > self.digest_button_delay)\r
294                         {\r
295                                 self.digesting = !self.digesting;\r
296                                 self.digest_button_delay = time + button_delay;\r
297                         }\r
298                 }\r
299                 else if(time > self.complain_swallow)\r
300                 {\r
301                         play2(self, "weapons/unavailable.wav");\r
302                         sprint(self, "There is nothing to digest\n");\r
303                         self.complain_swallow = time + complain_delay;\r
304                 }\r
305         }\r
306         if(!self.stomach_load)\r
307                 self.digesting = FALSE;\r
308 \r
309         // release players from this player's stomach on command\r
310         if(self.BUTTON_REGURGITATE)\r
311         {\r
312                 if(self.stomach_load)\r
313                 {\r
314                         if(time > self.regurgitate_button_delay)\r
315                         {\r
316                                 self.regurgitate_prepare = time + cvar("g_balance_vore_regurgitate_delay");\r
317                                 PlayerSound(self, playersound_regurgitate_prepare, CHAN_PAIN, VOICETYPE_PLAYERSOUND);\r
318                                 self.regurgitate_button_delay = time + button_delay;\r
319                         }\r
320                 }\r
321                 else if(time > self.complain_swallow)\r
322                 {\r
323                         play2(self, "weapons/unavailable.wav");\r
324                         sprint(self, "There is nothing to regurgitate\n");\r
325                         self.complain_swallow = time + complain_delay;\r
326                 }\r
327         }\r
328 \r
329         if(cvar("g_vore_gurglesound"))\r
330                 Vore_Gurglesound();\r
331 \r
332 // --------------------------------\r
333 // Code that addresses the prey:\r
334 // --------------------------------\r
335 \r
336         if(self.eater.classname != "player")\r
337                 return;\r
338 \r
339         if(self.eater.deadflag || self.deadflag)\r
340                 Vore_Regurgitate(self);\r
341         else if(self.eater.eater.classname == "player") // don't allow a player inside a player inside another player :)\r
342                 Vore_Regurgitate(self);\r
343         else if(vlen(self.eater.velocity) > cvar("g_balance_vore_regurgitate_velocitylimit"))\r
344                 Vore_Regurgitate(self);\r
345 \r
346         // apply delayed regurgitating\r
347         if(self.eater.regurgitate_prepare && time > self.eater.regurgitate_prepare)\r
348         {\r
349                 self.eater.regurgitate_prepare = 0;\r
350                 self.eater.complain_swallow = time + complain_delay;\r
351                 Vore_Regurgitate(self);\r
352         }\r
353 \r
354         if(self.eater.digesting == TRUE)\r
355                 Vore_Digest();\r
356         if(teams_matter && self.team == self.eater.team)\r
357                 Vore_Teamheal();\r
358 \r
359         Vore_StomachKick();\r
360 }