]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/g_grabber.qc
Fix remaining parts of server side code. Reloading will work properly now
[voretournament/voretournament.git] / data / qcsrc / server / g_grabber.qc
1 /*============================================\r
2 \r
3       Wazat's Voretournament Grabber\r
4 \r
5         Contact: Wazat1@gmail.com\r
6 \r
7 \r
8 Installation instructions:\r
9 --------------------------\r
10 \r
11 1. Place grabber.c in your gamec source directory with the other source files.\r
12 \r
13 2. Add this line to the bottom of progs.src:\r
14 \r
15 gamec/grabber.c\r
16 \r
17 3. Open defs.h and add these lines to the very bottom:\r
18 \r
19 // Wazat's grabber\r
20 .entity         grabber;\r
21 voidGrabberFrame();\r
22 void RemoveGrabber(entity pl);\r
23 void SetGrabberBindings();\r
24 // grabber impulses\r
25 float GRABBER_FIRE              = 20;\r
26 float GRABBER_RELEASE           = 21;\r
27 // (note: you can change the grabber impulse #'s to whatever you please)\r
28 \r
29 4. Open client.c and add this to the top of PutClientInServer():\r
30 \r
31         RemoveGrabber(self); // Wazat's Grabber\r
32 \r
33 5. Find ClientConnect() (in client.c) and add these lines to the bottom:\r
34 \r
35         // Wazat's grabber\r
36         SetGrabberBindings();\r
37 \r
38 6. Still in client.c, find PlayerPreThink and add this line just above the call to W_WeaponFrame:\r
39 \r
40         GrabberFrame();\r
41 \r
42 7. Build and test the mod.  You'll want to bind a key to "+grabber" like this:\r
43 bind ctrl "+grabber"\r
44 \r
45 And you should be done!\r
46 \r
47 \r
48 ============================================*/\r
49 \r
50 .string aiment_classname;\r
51 .float aiment_deadflag;\r
52 void SetMovetypeFollow(entity ent, entity e)\r
53 {\r
54         // FIXME this may not be warpzone aware\r
55         ent.movetype = MOVETYPE_FOLLOW; // make the hole follow\r
56         ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported.\r
57         ent.aiment = e; // make the hole follow bmodel\r
58         ent.punchangle = e.angles; // the original angles of bmodel\r
59         ent.view_ofs = ent.origin - e.origin; // relative origin\r
60         ent.v_angle = ent.angles - e.angles; // relative angles\r
61         ent.aiment_classname = strzone(e.classname);\r
62         ent.aiment_deadflag = e.deadflag;\r
63 }\r
64 void UnsetMovetypeFollow(entity ent)\r
65 {\r
66         ent.movetype = MOVETYPE_FLY;\r
67         PROJECTILE_MAKETRIGGER(ent);\r
68         ent.aiment = world;\r
69 }\r
70 float LostMovetypeFollow(entity ent)\r
71 {\r
72 /*\r
73         if(ent.movetype != MOVETYPE_FOLLOW)\r
74                 if(ent.aiment)\r
75                         error("???");\r
76 */\r
77         if(ent.aiment)\r
78         {\r
79                 if(ent.aiment.classname != ent.aiment_classname)\r
80                         return 1;\r
81                 if(ent.aiment.deadflag != ent.aiment_deadflag)\r
82                         return 1;\r
83         }\r
84         return 0;\r
85 }\r
86 \r
87 .float grabber_length;\r
88 .float grabber_switchweapon;\r
89 \r
90 void RemoveGrabber(entity pl)\r
91 {\r
92         if(pl.grabber == world)\r
93                 return;\r
94         remove(pl.grabber);\r
95         pl.grabber = world;\r
96         if(pl.movetype == MOVETYPE_FLY)\r
97                 pl.movetype = MOVETYPE_WALK;\r
98 \r
99         //pl.disableclientprediction = FALSE;\r
100 }\r
101 \r
102 void GrabberThink();\r
103 void Grabber_Stop()\r
104 {\r
105         pointparticles(particleeffectnum("grabber_impact"), self.origin, '0 0 0', 1);\r
106         sound (self, CHAN_PROJECTILE, "weapons/grabber_impact.wav", VOL_BASE, ATTN_NORM);\r
107 \r
108         self.state = 1;\r
109         self.think =GrabberThink;\r
110         self.nextthink = time;\r
111         self.touch = SUB_Null;\r
112         self.velocity = '0 0 0';\r
113         self.movetype = MOVETYPE_NONE;\r
114         self.grabber_length = -1;\r
115 }\r
116 \r
117 void GrabberThink()\r
118 {\r
119         float spd, dist, minlength, pullspeed, ropestretch, ropeairfriction, rubberforce, newlength, rubberforce_overstretch;\r
120         vector dir, org, end, v0, dv, v, myorg;\r
121         if(self.owner.health <= 0 || self.owner.grabber != self)        // how did that happen?\r
122         {                                                                                                               // well, better fix it anyway\r
123                 remove(self);\r
124                 return;\r
125         }\r
126         if(LostMovetypeFollow(self))\r
127         {\r
128                 RemoveGrabber(self.owner);\r
129                 return;\r
130         }\r
131         if(self.aiment)\r
132                 WarpZone_RefSys_AddIncrementally(self, self.aiment);\r
133 \r
134         // prevent the grabber from sticking to a player that has been swallowed\r
135         if(self.aiment.predator.classname == "player")\r
136         {\r
137                 // if the grabber is linked to a player that we swallowed, disconnect it (or the grabber will stick to us / the player inside us)\r
138                 // otherwise, link the grabber to the player who ate our linked player\r
139                 if(self.aiment.predator != self.owner)\r
140                         SetMovetypeFollow(self, self.aiment.predator);\r
141                 else\r
142                         RemoveGrabber(self.owner);\r
143         }\r
144 \r
145         self.nextthink = time;\r
146 \r
147         makevectors(self.owner.v_angle);\r
148         org = self.owner.origin + self.owner.view_ofs + v_forward * grabber_shotorigin_x + v_right * grabber_shotorigin_y + v_up * grabber_shotorigin_z;\r
149         myorg = WarpZone_RefSys_TransformOrigin(self.owner, self, org);\r
150 \r
151         if(self.grabber_length < 0)\r
152                 self.grabber_length = vlen(myorg - self.origin);\r
153 \r
154         if(self.state == 1)\r
155         {\r
156                 pullspeed = cvar("g_balance_grabber_speed_pull");//2000;\r
157                 // speed the rope is pulled with\r
158 \r
159                 rubberforce = cvar("g_balance_grabber_force_rubber");//2000;\r
160                 // force the rope will use if it is stretched\r
161 \r
162                 rubberforce_overstretch = cvar("g_balance_grabber_force_rubber_overstretch");//1000;\r
163                 // force the rope will use if it is stretched\r
164 \r
165                 minlength = cvar("g_balance_grabber_length_min");//100;\r
166                 // minimal rope length\r
167                 // if the rope goes below this length, it isn't pulled any more\r
168 \r
169                 ropestretch = cvar("g_balance_grabber_stretch");//400;\r
170                 // if the rope is stretched by more than this amount, more rope is\r
171                 // given to you again\r
172 \r
173                 ropeairfriction = cvar("g_balance_grabber_airfriction");//0.2\r
174                 // while hanging on the rope, this friction component will help you a\r
175                 // bit to control the rope\r
176 \r
177                 dir = self.origin - myorg;\r
178                 dist = vlen(dir);\r
179                 dir = normalize(dir);\r
180 \r
181                 if(cvar("g_grabber_tarzan"))\r
182                 {\r
183                         v = v0 = WarpZone_RefSys_TransformVelocity(self.owner, self, self.owner.velocity);\r
184 \r
185                         // first pull the rope...\r
186                         if(self.owner.grabber_state & GRABBER_PULLING)\r
187                         {\r
188                                 newlength = self.grabber_length;\r
189                                 newlength = max(newlength - pullspeed * frametime, minlength);\r
190 \r
191                                 if(newlength < dist - ropestretch) // overstretched?\r
192                                 {\r
193                                         newlength = dist - ropestretch;\r
194                                         if(v * dir < 0) // only if not already moving in grabber direction\r
195                                                 v = v + frametime * dir * rubberforce_overstretch;\r
196                                 }\r
197 \r
198                                 self.grabber_length = newlength;\r
199                         }\r
200 \r
201                         if(self.owner.grabber_state & GRABBER_RELEASING)\r
202                         {\r
203                                 newlength = dist;\r
204                                 self.grabber_length = newlength;\r
205                         }\r
206                         else\r
207                         {\r
208                                 // then pull the player\r
209                                 spd = bound(0, (dist - self.grabber_length) / ropestretch, 1);\r
210                                 v = v * (1 - frametime * ropeairfriction);\r
211                                 v = v + frametime * dir * spd * rubberforce;\r
212 \r
213                                 dv = ((v - v0) * dir) * dir;\r
214                                 if(cvar("g_grabber_tarzan") >= 2)\r
215                                 {\r
216                                         if(self.aiment.movetype == MOVETYPE_WALK)\r
217                                         {\r
218                                                 v = v - dv * 0.5;\r
219                                                 self.aiment.velocity = self.aiment.velocity - dv * 0.5;\r
220                                                 self.aiment.flags &~= FL_ONGROUND;\r
221                                                 self.aiment.pusher = self.owner;\r
222                                                 self.aiment.pushltime = time + cvar("g_maxpushtime");\r
223                                         }\r
224                                 }\r
225 \r
226                                 self.owner.flags &~= FL_ONGROUND;\r
227                         }\r
228 \r
229                         self.owner.velocity = WarpZone_RefSys_TransformVelocity(self, self.owner, v);\r
230                 }\r
231                 else\r
232                 {\r
233                         end = self.origin - dir*50;\r
234                         dist = vlen(end - myorg);\r
235                         if(dist < 200)\r
236                                 spd = dist * (pullspeed / 200);\r
237                         else\r
238                                 spd = pullspeed;\r
239                         if(spd < 50)\r
240                                 spd = 0;\r
241                         self.owner.velocity = dir*spd;\r
242                         self.owner.movetype = MOVETYPE_FLY;\r
243 \r
244                         self.owner.flags &~= FL_ONGROUND;\r
245                 }\r
246         }\r
247 \r
248         makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0');\r
249         te_beam(self.owner, WarpZone_RefSys_TransformOrigin(self, self.owner, self.origin) + v_forward * (-9), org);\r
250 }\r
251 \r
252 void GrabberTouch (void)\r
253 {\r
254         if(SUB_OwnerCheck())\r
255                 return;\r
256         if(SUB_NoImpactCheck())\r
257         {\r
258                 RemoveGrabber(self.owner);\r
259                 return;\r
260         }\r
261         PROJECTILE_TOUCH;\r
262 \r
263         Grabber_Stop();\r
264 \r
265         if(other)\r
266                 if(other.movetype != MOVETYPE_NONE)\r
267                 {\r
268                         SetMovetypeFollow(self, other);\r
269                         W_Grabber_UpdateStats(self.owner, FALSE, TRUE); // count this as a hit\r
270                         WarpZone_RefSys_BeginAddingIncrementally(self, self.aiment);\r
271                 }\r
272 \r
273         //self.owner.disableclientprediction = TRUE;\r
274 }\r
275 \r
276 void Grabber_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)\r
277 {\r
278         if(self.health > 0)\r
279         {\r
280                 self.health = self.health - damage;\r
281                 if (self.health <= 0)\r
282                 {\r
283                         if(attacker != self.owner)\r
284                         {\r
285                                 self.owner.pusher = attacker;\r
286                                 self.owner.pushltime = time + cvar("g_maxpushtime");\r
287                         }\r
288                         RemoveGrabber(self.owner);\r
289                 }\r
290         }\r
291 }\r
292 \r
293 void FireGrabber (void)\r
294 {\r
295         local entity missile;\r
296         local vector org;\r
297 \r
298         W_Grabber_UpdateStats(self, TRUE, FALSE);\r
299 \r
300         if((arena_roundbased && time < warmup) || (time < game_starttime))\r
301                 return;\r
302 \r
303         makevectors(self.v_angle);\r
304 \r
305         // UGLY WORKAROUND: play this on CHAN_WEAPON2 so it can't cut off fire sounds\r
306         sound (self, CHAN_WEAPON2, "weapons/grabber_fire.wav", VOL_BASE, ATTN_NORM);\r
307         org = self.origin + self.view_ofs + v_forward * grabber_shotorigin_x + v_right * grabber_shotorigin_y + v_up * grabber_shotorigin_z;\r
308         pointparticles(particleeffectnum("grapple_muzzleflash"), org, '0 0 0', 1);\r
309 \r
310         missile = WarpZone_RefSys_SpawnSameRefSys(self);\r
311         missile.owner = self;\r
312         self.grabber = missile;\r
313         missile.classname = "grabber";\r
314 \r
315         missile.movetype = MOVETYPE_FLY;\r
316         PROJECTILE_MAKETRIGGER(missile);\r
317 \r
318         setmodel (missile, "models/grabber.md3"); // precision set below\r
319         setsize (missile, '-3 -3 -3', '3 3 3');\r
320         setorigin (missile, org);\r
321 \r
322         missile.state = 0; // not latched onto anything\r
323 \r
324         W_SetupProjectileVelocityEx(missile, v_forward, v_up, cvar("g_balance_grabber_speed_fly"), 0, 0, 0);\r
325 \r
326         missile.angles = vectoangles (missile.velocity);\r
327         //missile.glow_color = 250; // 244, 250\r
328         //missile.glow_size = 120;\r
329         missile.touch =GrabberTouch;\r
330         missile.think =GrabberThink;\r
331         missile.nextthink = time + 0.1;\r
332 \r
333         missile.effects = /*EF_FULLBRIGHT | EF_ADDITIVE |*/ EF_LOWPRECISION;\r
334 \r
335         missile.health = cvar("g_balance_grabber_health");//120\r
336         missile.event_damage =Grabber_Damage;\r
337         missile.takedamage = DAMAGE_AIM;\r
338         missile.damageforcescale = 0;\r
339 }\r
340 \r
341 //  voidGrabberFrame()\r
342 //  {\r
343 //         // this function has been modified for Voretournament\r
344 // -       if (self.BUTTON_JETPACK && g_grabber)\r
345 //         {\r
346 // -               if (!self.grabber && self.grabber_time <= time && !self.button6_pressed_before)\r
347 // -                       if (timeoutStatus != 2) //only allow the player to fire the grabber if the game is not paused (timeout)\r
348 // -                               FireGrabber();\r
349 //         }\r
350 // -       else\r
351 //         {\r
352 //                 if (self.grabber)\r
353 //                         RemoveGrabber(self);\r
354 //         }\r
355 // -       self.button6_pressed_before = self.BUTTON_JETPACK;\r
356 //         /*\r
357 //         // if I have no grabber or it's not pulling yet, make sure I'm not flying!\r
358 //         if((self.grabber == world || !self.grabber.state) && self.movetype == MOVETYPE_FLY)\r
359 \r
360 void GrabberFrame()\r
361 {\r
362         if(timeoutStatus != 2 && self.weapon != WEP_GRABBER)\r
363         {\r
364                 // offhand grabber controls\r
365                 if(self.BUTTON_JETPACK)\r
366                 {\r
367                         if not(self.grabber || (self.grabber_state & GRABBER_WAITING_FOR_RELEASE))\r
368                         {\r
369                                 self.grabber_state |= GRABBER_FIRING;\r
370                                 self.grabber_state |= GRABBER_WAITING_FOR_RELEASE;\r
371                         }\r
372                 }\r
373                 else\r
374                 {\r
375                         self.grabber_state |= GRABBER_REMOVING;\r
376                         self.grabber_state &~= GRABBER_WAITING_FOR_RELEASE;\r
377                 }\r
378 \r
379                 self.grabber_state &~= GRABBER_RELEASING;\r
380                 if(self.BUTTON_CROUCH)\r
381                 {\r
382                         self.grabber_state &~= GRABBER_PULLING;\r
383                         //self.grabber_state |= GRABBER_RELEASING;\r
384                 }\r
385                 else\r
386                 {\r
387                         self.grabber_state |= GRABBER_PULLING;\r
388                         //self.grabber_state &~= GRABBER_RELEASING;\r
389                 }\r
390         }\r
391         else if(!(self.items & IT_JETPACK) && self.switchweapon != WEP_GRABBER)\r
392         {\r
393                 if(self.BUTTON_JETPACK && !self.grabber_switchweapon)\r
394                         W_SwitchWeapon(WEP_GRABBER);\r
395         }\r
396         self.grabber_switchweapon = self.BUTTON_JETPACK;\r
397 \r
398         if(self.weapon != WEP_GRABBER)\r
399         {\r
400                 self.grabber_state &~= GRABBER_FIRING;\r
401                 self.grabber_state |= GRABBER_REMOVING;\r
402         }\r
403 \r
404         if (self.grabber_state & GRABBER_FIRING)\r
405         {\r
406                 if (self.grabber)\r
407                         RemoveGrabber(self);\r
408                 FireGrabber();\r
409                 self.grabber_state &~= GRABBER_FIRING;\r
410         }\r
411         else if(self.grabber_state & GRABBER_REMOVING)\r
412         {\r
413                 if (self.grabber)\r
414                         RemoveGrabber(self);\r
415                 self.grabber_state &~= GRABBER_REMOVING;\r
416         }\r
417 \r
418         /*\r
419         // if I have no grabber or it's not pulling yet, make sure I'm not flying!\r
420         if((self.grabber == world || !self.grabber.state) && self.movetype == MOVETYPE_FLY)\r
421         {\r
422                 self.movetype = MOVETYPE_WALK;\r
423         }\r
424         if(self.impulse == GRABBER_FIRE && self.grabber_time <= time)\r
425         {\r
426                 // fire grabber\r
427                 FireGrabber();\r
428                 return;\r
429         }\r
430         else if(self.grabberimpulse == GRABBER_RELEASE)\r
431         {\r
432                 // remove grabber, reset movement type\r
433                 RemoveGrabber(self);\r
434                 return;\r
435         }\r
436         */\r
437         /*else // make sure the player's movetype is correct\r
438         {\r
439                 //if(self.grabber == world && self.movetype == MOVETYPE_FLY)\r
440                 if((self.grabber == world || !self.grabber.state) && self.movetype == MOVETYPE_FLY)\r
441                 {\r
442                         self.movetype = MOVETYPE_WALK;\r
443                 }\r
444         }*/\r
445         // note: The grabber entity does the actual pulling\r
446 }\r
447 \r
448 void GrabberInit()\r
449 {\r
450         grabber_shotorigin = shotorg_adjust('26.2148 9.2059 -15.9772', TRUE, FALSE);\r
451 }\r
452 \r
453 void SetGrabberBindings()\r
454 {\r
455         // this function has been modified for Voretournament\r
456         // don't remove these lines! old server or demos coud overwrite the new aliases\r
457         stuffcmd(self, "alias +grabber +button6\n");\r
458         stuffcmd(self, "alias -grabber -button6\n");\r
459 }\r