]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/w_hook.qc
Merge remote-tracking branch 'origin/master' into samual/weapons
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / weapons / w_hook.qc
1 #ifdef REGISTER_WEAPON
2 REGISTER_WEAPON(
3 /* WEP_##id  */ HOOK,
4 /* function  */ W_Hook,
5 /* ammotype  */ ammo_fuel,
6 /* impulse   */ 0,
7 /* flags     */ WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
8 /* rating    */ 0,
9 /* color     */ '0 0.5 0',
10 /* modelname */ "hookgun",
11 /* simplemdl */ "foobar",
12 /* crosshair */ "gfx/crosshairhook 0.5",
13 /* wepimg    */ "weaponhook",
14 /* refname   */ "hook",
15 /* wepname   */ _("Grappling Hook")
16 );
17
18 #define HOOK_SETTINGS(w_cvar,w_prop) HOOK_SETTINGS_LIST(w_cvar, w_prop, HOOK, hook)
19 #define HOOK_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
20         w_cvar(id, sn, BOTH, animtime) \
21         w_cvar(id, sn, BOTH, refire) \
22         w_cvar(id, sn, PRI,  ammo) \
23         w_cvar(id, sn, PRI,  hooked_ammo) \
24         w_cvar(id, sn, PRI,  hooked_time_free) \
25         w_cvar(id, sn, PRI,  hooked_time_max) \
26         w_cvar(id, sn, SEC,  damage) \
27         w_cvar(id, sn, SEC,  duration) \
28         w_cvar(id, sn, SEC,  edgedamage) \
29         w_cvar(id, sn, SEC,  force) \
30         w_cvar(id, sn, SEC,  gravity) \
31         w_cvar(id, sn, SEC,  lifetime) \
32         w_cvar(id, sn, SEC,  power) \
33         w_cvar(id, sn, SEC,  radius) \
34         w_cvar(id, sn, SEC,  speed) \
35         w_cvar(id, sn, SEC,  health) \
36         w_cvar(id, sn, SEC,  damageforcescale) \
37         w_prop(id, sn, float,  switchdelay_raise, switchdelay_raise) \
38         w_prop(id, sn, float,  switchdelay_drop, switchdelay_drop) \
39         w_prop(id, sn, string, weaponreplace, weaponreplace) \
40         w_prop(id, sn, float,  weaponstart, weaponstart) \
41         w_prop(id, sn, float,  weaponstartoverride, weaponstartoverride)
42
43 #ifdef SVQC
44 HOOK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
45
46 .float dmg;
47 .float dmg_edge;
48 .float dmg_radius;
49 .float dmg_force;
50 .float dmg_power;
51 .float dmg_duration;
52 .float dmg_last;
53 .float hook_refire;
54 .float hook_time_hooked;
55 .float hook_time_fueldecrease;
56 #endif
57 #else
58 #ifdef SVQC
59
60 void spawnfunc_weapon_hook()
61 {
62         if(g_grappling_hook) // offhand hook
63         {
64                 startitem_failed = TRUE;
65                 remove(self);
66                 return;
67         }
68         weapon_defaultspawnfunc(WEP_HOOK);
69 }
70
71 void W_Hook_ExplodeThink (void)
72 {
73         float dt, dmg_remaining_next, f;
74
75         dt = time - self.teleport_time;
76         dmg_remaining_next = pow(bound(0, 1 - dt / self.dmg_duration, 1), self.dmg_power);
77
78         f = self.dmg_last - dmg_remaining_next;
79         self.dmg_last = dmg_remaining_next;
80
81         RadiusDamage (self, self.realowner, self.dmg * f, self.dmg_edge * f, self.dmg_radius, self.realowner, world, self.dmg_force * f, self.projectiledeathtype, world);
82         self.projectiledeathtype |= HITTYPE_BOUNCE;
83         //RadiusDamage (self, world, self.dmg * f, self.dmg_edge * f, self.dmg_radius, world, world, self.dmg_force * f, self.projectiledeathtype, world);
84
85         if(dt < self.dmg_duration)
86                 self.nextthink = time + 0.05; // soon
87         else
88                 remove(self);
89 }
90
91 void W_Hook_Explode2 (void)
92 {
93         self.event_damage = func_null;
94         self.touch = func_null;
95         self.effects |= EF_NODRAW;
96
97         self.think = W_Hook_ExplodeThink;
98         self.nextthink = time;
99         self.dmg = WEP_CVAR_SEC(hook, damage);
100         self.dmg_edge = WEP_CVAR_SEC(hook, edgedamage);
101         self.dmg_radius = WEP_CVAR_SEC(hook, radius);
102         self.dmg_force = WEP_CVAR_SEC(hook, force);
103         self.dmg_power = WEP_CVAR_SEC(hook, power);
104         self.dmg_duration = WEP_CVAR_SEC(hook, duration);
105         self.teleport_time = time;
106         self.dmg_last = 1;
107         self.movetype = MOVETYPE_NONE;
108 }
109
110 void W_Hook_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
111 {
112         if (self.health <= 0)
113                 return;
114                 
115         if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
116                 return; // g_projectiles_damage says to halt    
117         
118         self.health = self.health - damage;
119         
120         if (self.health <= 0)
121                 W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2);
122 }
123
124 void W_Hook_Touch2 (void)
125 {
126         PROJECTILE_TOUCH;
127         self.use();
128 }
129
130 void W_Hook_Attack2()
131 {
132         entity gren;
133
134         //W_DecreaseAmmo(WEP_CVAR_SEC(hook, ammo)); // WEAPONTODO: Figure out how to handle ammo with hook secondary (gravitybomb)
135         W_SetupShot (self, FALSE, 4, "weapons/hookbomb_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hook, damage));
136
137         gren = spawn ();
138         gren.owner = gren.realowner = self;
139         gren.classname = "hookbomb";
140         gren.bot_dodge = TRUE;
141         gren.bot_dodgerating = WEP_CVAR_SEC(hook, damage);
142         gren.movetype = MOVETYPE_TOSS;
143         PROJECTILE_MAKETRIGGER(gren);
144         gren.projectiledeathtype = WEP_HOOK | HITTYPE_SECONDARY;
145         setorigin(gren, w_shotorg);
146         setsize(gren, '0 0 0', '0 0 0');
147
148         gren.nextthink = time + WEP_CVAR_SEC(hook, lifetime);
149         gren.think = adaptor_think2use_hittype_splash;
150         gren.use = W_Hook_Explode2;
151         gren.touch = W_Hook_Touch2;
152         
153         gren.takedamage = DAMAGE_YES;
154         gren.health = WEP_CVAR_SEC(hook, health);
155         gren.damageforcescale = WEP_CVAR_SEC(hook, damageforcescale);
156         gren.event_damage = W_Hook_Damage;
157         gren.damagedbycontents = TRUE;
158         gren.missile_flags = MIF_SPLASH | MIF_ARC;
159
160         gren.velocity = '0 0 1' * WEP_CVAR_SEC(hook, speed);
161         if(autocvar_g_projectiles_newton_style)
162                 gren.velocity = gren.velocity + self.velocity;
163
164         gren.gravity = WEP_CVAR_SEC(hook, gravity);
165         //W_SetupProjVelocity_Basic(gren); // just falling down!
166
167         gren.angles = '0 0 0';
168         gren.flags = FL_PROJECTILE;
169
170         CSQCProjectile(gren, TRUE, PROJECTILE_HOOKBOMB, TRUE);
171
172         other = gren; MUTATOR_CALLHOOK(EditProjectile);
173 }
174
175 float W_Hook(float req)
176 {
177         float hooked_time_max, hooked_fuel;
178                 
179         switch(req)
180         {
181                 case WR_AIM:
182                 {
183                         // no bot AI for hook (yet?)
184                         return TRUE;
185                 }
186                 case WR_THINK:
187                 {
188                         if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
189                         {
190                                 if(!self.hook)
191                                 if(!(self.hook_state & HOOK_WAITING_FOR_RELEASE))
192                                 if(!(self.hook_state & HOOK_FIRING))
193                                 if (time > self.hook_refire)
194                                 if (weapon_prepareattack(0, -1))
195                                 {
196                                         W_DecreaseAmmo(WEP_CVAR_PRI(hook, ammo));
197                                         self.hook_state |= HOOK_FIRING;
198                                         weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready);                             
199                                 }
200                         }
201
202                         if (self.BUTTON_ATCK2)
203                         {
204                                 if (weapon_prepareattack(1, WEP_CVAR_SEC(hook, refire)))
205                                 {
206                                         W_Hook_Attack2();
207                                         weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready);
208                                 }
209                         }
210
211                         if(self.hook)
212                         {
213                                 // if hooked, no bombs, and increase the timer
214                                 self.hook_refire = max(self.hook_refire, time + WEP_CVAR_PRI(hook, refire) * W_WeaponRateFactor());
215
216                                 // hook also inhibits health regeneration, but only for 1 second
217                                 if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
218                                         self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
219                         }
220
221                         if(self.hook && self.hook.state == 1)
222                         {
223                                 hooked_time_max = WEP_CVAR_PRI(hook, hooked_time_max);                  
224                                 if (hooked_time_max > 0)
225                                 {
226                                         if ( time > self.hook_time_hooked + hooked_time_max )
227                                                 self.hook_state |= HOOK_REMOVING;
228                                 }
229                                 
230                                 hooked_fuel = WEP_CVAR_PRI(hook, hooked_ammo);
231                                 if (hooked_fuel > 0)
232                                 {
233                                         if ( time > self.hook_time_fueldecrease )
234                                         {
235                                                 if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
236                                                 {
237                                                         if ( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel )
238                                                         {
239                                                                 W_DecreaseAmmo((time - self.hook_time_fueldecrease) * hooked_fuel);
240                                                                 self.hook_time_fueldecrease = time;
241                                                                 // decrease next frame again
242                                                         }
243                                                         else
244                                                         {
245                                                                 self.ammo_fuel = 0;
246                                                                 self.hook_state |= HOOK_REMOVING;
247                                                                 W_SwitchWeapon_Force(self, w_getbestweapon(self));
248                                                         }
249                                                 }
250                                         }
251                                 }
252                         }
253                         else
254                         {
255                                 self.hook_time_hooked = time;                           
256                                 self.hook_time_fueldecrease = time + WEP_CVAR_PRI(hook, hooked_time_free);
257                         }
258
259                         if (self.BUTTON_CROUCH)
260                         {
261                                 self.hook_state &= ~HOOK_PULLING;
262                                 if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
263                                         self.hook_state &= ~HOOK_RELEASING;
264                                 else
265                                         self.hook_state |= HOOK_RELEASING;
266                         }
267                         else
268                         {
269                                 self.hook_state |= HOOK_PULLING;
270                                 self.hook_state &= ~HOOK_RELEASING;
271
272                                 if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
273                                 {
274                                         // already fired
275                                         if(self.hook)
276                                                 self.hook_state |= HOOK_WAITING_FOR_RELEASE;
277                                 }
278                                 else
279                                 {
280                                         self.hook_state |= HOOK_REMOVING;
281                                         self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
282                                 }
283                         }
284                         
285                         return TRUE;
286                 }
287                 case WR_INIT:
288                 {
289                         precache_model ("models/weapons/g_hookgun.md3");
290                         precache_model ("models/weapons/v_hookgun.md3");
291                         precache_model ("models/weapons/h_hookgun.iqm");
292                         precache_sound ("weapons/hook_impact.wav"); // done by g_hook.qc
293                         precache_sound ("weapons/hook_fire.wav");
294                         precache_sound ("weapons/hookbomb_fire.wav");
295                         HOOK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP)
296                         return TRUE;
297                 }
298                 case WR_SETUP:
299                 {
300                         self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
301                         return TRUE;
302                 }
303                 case WR_CHECKAMMO1:
304                 {
305                         if(self.hook)
306                                 return self.ammo_fuel > 0;
307                         else
308                                 return self.ammo_fuel >= WEP_CVAR_PRI(hook, ammo);
309                 }
310                 case WR_CHECKAMMO2:
311                 {
312                         // infinite ammo for now
313                         return TRUE; // self.ammo_cells >= WEP_CVAR_SEC(hook, ammo); // WEAPONTODO: see above
314                 }
315                 case WR_CONFIG:
316                 {
317                         HOOK_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS)
318                         return TRUE;
319                 }
320                 case WR_RESETPLAYER:
321                 {
322                         self.hook_refire = time;
323                         return TRUE;
324                 }
325                 case WR_SUICIDEMESSAGE:
326                 {
327                         return FALSE;
328                 }
329                 case WR_KILLMESSAGE:
330                 {
331                         return WEAPON_HOOK_MURDER;
332                 }
333         }
334         return TRUE;
335 }
336 #endif
337 #ifdef CSQC
338 float W_Hook(float req)
339 {
340         switch(req)
341         {
342                 case WR_IMPACTEFFECT:
343                 {
344                         vector org2;
345                         org2 = w_org + w_backoff * 2;
346                         pointparticles(particleeffectnum("hookbomb_explode"), org2, '0 0 0', 1);
347                         if(!w_issilent)
348                                 sound(self, CH_SHOTS, "weapons/hookbomb_impact.wav", VOL_BASE, ATTN_NORM);
349                                 
350                         return TRUE;
351                 }
352                 case WR_INIT:
353                 {
354                         precache_sound("weapons/hookbomb_impact.wav");
355                         return TRUE;
356                 }
357                 case WR_ZOOMRETICLE:
358                 {
359                         // no weapon specific image for this weapon
360                         return FALSE;
361                 }
362         }
363         return TRUE;
364 }
365 #endif
366 #endif