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