]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/weapons/w_hook.qc
Working on cleaning up weapon spawn functions
[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  */ IT_CELLS|IT_FUEL,
6 /* impulse   */ 0,
7 /* flags     */ WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
8 /* rating    */ 0,
9 /* model     */ "hookgun",
10 /* shortname */ "hook",
11 /* fullname  */ _("Grappling Hook")
12 );
13 #else
14 #ifdef SVQC
15 void spawnfunc_weapon_hook()
16 {
17         if(g_grappling_hook) // offhand hook
18         {
19                 startitem_failed = TRUE;
20                 remove(self);
21                 return;
22         }
23         weapon_defaultspawnfunc(WEP_HOOK);
24 }
25
26 .float dmg;
27 .float dmg_edge;
28 .float dmg_radius;
29 .float dmg_force;
30 .float dmg_power;
31 .float dmg_duration;
32 .float dmg_last;
33 .float hook_refire;
34 .float hook_time_hooked;
35 .float hook_time_fueldecrease;
36
37 void W_Hook_ExplodeThink (void)
38 {
39         float dt, dmg_remaining_next, f;
40
41         dt = time - self.teleport_time;
42         dmg_remaining_next = pow(bound(0, 1 - dt / self.dmg_duration, 1), self.dmg_power);
43
44         f = self.dmg_last - dmg_remaining_next;
45         self.dmg_last = dmg_remaining_next;
46
47         RadiusDamage (self, self.realowner, self.dmg * f, self.dmg_edge * f, self.dmg_radius, self.realowner, world, self.dmg_force * f, self.projectiledeathtype, world);
48         self.projectiledeathtype |= HITTYPE_BOUNCE;
49         //RadiusDamage (self, world, self.dmg * f, self.dmg_edge * f, self.dmg_radius, world, world, self.dmg_force * f, self.projectiledeathtype, world);
50
51         if(dt < self.dmg_duration)
52                 self.nextthink = time + 0.05; // soon
53         else
54                 remove(self);
55 }
56
57 void W_Hook_Explode2 (void)
58 {
59         self.event_damage = func_null;
60         self.touch = func_null;
61         self.effects |= EF_NODRAW;
62
63         self.think = W_Hook_ExplodeThink;
64         self.nextthink = time;
65         self.dmg = autocvar_g_balance_hook_secondary_damage;
66         self.dmg_edge = autocvar_g_balance_hook_secondary_edgedamage;
67         self.dmg_radius = autocvar_g_balance_hook_secondary_radius;
68         self.dmg_force = autocvar_g_balance_hook_secondary_force;
69         self.dmg_power = autocvar_g_balance_hook_secondary_power;
70         self.dmg_duration = autocvar_g_balance_hook_secondary_duration;
71         self.teleport_time = time;
72         self.dmg_last = 1;
73         self.movetype = MOVETYPE_NONE;
74 }
75
76 void W_Hook_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
77 {
78         if (self.health <= 0)
79                 return;
80                 
81         if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
82                 return; // g_projectiles_damage says to halt    
83         
84         self.health = self.health - damage;
85         
86         if (self.health <= 0)
87                 W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2);
88 }
89
90 void W_Hook_Touch2 (void)
91 {
92         PROJECTILE_TOUCH;
93         self.use();
94 }
95
96 void W_Hook_Attack2()
97 {
98         entity gren;
99
100         W_DecreaseAmmo(ammo_cells, autocvar_g_balance_hook_secondary_ammo, FALSE);
101         W_SetupShot (self, FALSE, 4, "weapons/hookbomb_fire.wav", CH_WEAPON_A, autocvar_g_balance_hook_secondary_damage);
102
103         gren = spawn ();
104         gren.owner = gren.realowner = self;
105         gren.classname = "hookbomb";
106         gren.bot_dodge = TRUE;
107         gren.bot_dodgerating = autocvar_g_balance_hook_secondary_damage;
108         gren.movetype = MOVETYPE_TOSS;
109         PROJECTILE_MAKETRIGGER(gren);
110         gren.projectiledeathtype = WEP_HOOK | HITTYPE_SECONDARY;
111         setorigin(gren, w_shotorg);
112         setsize(gren, '0 0 0', '0 0 0');
113
114         gren.nextthink = time + autocvar_g_balance_hook_secondary_lifetime;
115         gren.think = adaptor_think2use_hittype_splash;
116         gren.use = W_Hook_Explode2;
117         gren.touch = W_Hook_Touch2;
118         
119         gren.takedamage = DAMAGE_YES;
120         gren.health = autocvar_g_balance_hook_secondary_health;
121         gren.damageforcescale = autocvar_g_balance_hook_secondary_damageforcescale;
122         gren.event_damage = W_Hook_Damage;
123         gren.damagedbycontents = TRUE;
124         gren.missile_flags = MIF_SPLASH | MIF_ARC;
125
126         gren.velocity = '0 0 1' * autocvar_g_balance_hook_secondary_speed;
127         if(autocvar_g_projectiles_newton_style)
128                 gren.velocity = gren.velocity + self.velocity;
129
130         gren.gravity = autocvar_g_balance_hook_secondary_gravity;
131         //W_SetupProjectileVelocity(gren); // just falling down!
132
133         gren.angles = '0 0 0';
134         gren.flags = FL_PROJECTILE;
135
136         CSQCProjectile(gren, TRUE, PROJECTILE_HOOKBOMB, TRUE);
137
138         other = gren; MUTATOR_CALLHOOK(EditProjectile);
139 }
140
141 float w_hook(float req)
142 {
143         float hooked_time_max, hooked_fuel;
144                 
145         switch(req)
146         {
147                 case WR_AIM:
148                 {
149                         // no bot AI for hook (yet?)
150                         return TRUE;
151                 }
152                 case WR_THINK:
153                 {
154                         if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
155                         {
156                                 if(!self.hook)
157                                 if not(self.hook_state & HOOK_WAITING_FOR_RELEASE)
158                                 if not(self.hook_state & HOOK_FIRING)
159                                 if (time > self.hook_refire)
160                                 if (weapon_prepareattack(0, -1))
161                                 {
162                                         W_DecreaseAmmo(ammo_fuel, autocvar_g_balance_hook_primary_fuel, FALSE);
163                                         self.hook_state |= HOOK_FIRING;
164                                         weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hook_primary_animtime, w_ready);                         
165                                 }
166                         }
167
168                         if (self.BUTTON_ATCK2)
169                         {
170                                 if (weapon_prepareattack(1, autocvar_g_balance_hook_secondary_refire))
171                                 {
172                                         W_Hook_Attack2();
173                                         weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hook_secondary_animtime, w_ready);
174                                 }
175                         }
176
177                         if(self.hook)
178                         {
179                                 // if hooked, no bombs, and increase the timer
180                                 self.hook_refire = max(self.hook_refire, time + autocvar_g_balance_hook_primary_refire * W_WeaponRateFactor());
181
182                                 // hook also inhibits health regeneration, but only for 1 second
183                                 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
184                                         self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
185                         }
186
187                         if(self.hook && self.hook.state == 1)
188                         {
189                                 hooked_time_max = autocvar_g_balance_hook_primary_hooked_time_max;                      
190                                 if (hooked_time_max > 0)
191                                 {
192                                         if ( time > self.hook_time_hooked + hooked_time_max )
193                                                 self.hook_state |= HOOK_REMOVING;
194                                 }
195                                 
196                                 hooked_fuel = autocvar_g_balance_hook_primary_hooked_fuel;
197                                 if (hooked_fuel > 0)
198                                 {
199                                         if ( time > self.hook_time_fueldecrease )
200                                         {
201                                                 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
202                                                 {
203                                                         if ( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel )
204                                                         {
205                                                                 W_DecreaseAmmo(ammo_fuel, (time - self.hook_time_fueldecrease) * hooked_fuel, FALSE);
206                                                                 self.hook_time_fueldecrease = time;
207                                                                 // decrease next frame again
208                                                         }
209                                                         else
210                                                         {
211                                                                 self.ammo_fuel = 0;
212                                                                 self.hook_state |= HOOK_REMOVING;
213                                                                 W_SwitchWeapon_Force(self, w_getbestweapon(self));
214                                                         }
215                                                 }
216                                         }
217                                 }
218                         }
219                         else
220                         {
221                                 self.hook_time_hooked = time;                           
222                                 self.hook_time_fueldecrease = time + autocvar_g_balance_hook_primary_hooked_time_free;
223                         }
224
225                         if (self.BUTTON_CROUCH)
226                         {
227                                 self.hook_state &~= HOOK_PULLING;
228                                 if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
229                                         self.hook_state &~= HOOK_RELEASING;
230                                 else
231                                         self.hook_state |= HOOK_RELEASING;
232                         }
233                         else
234                         {
235                                 self.hook_state |= HOOK_PULLING;
236                                 self.hook_state &~= HOOK_RELEASING;
237
238                                 if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
239                                 {
240                                         // already fired
241                                         if(self.hook)
242                                                 self.hook_state |= HOOK_WAITING_FOR_RELEASE;
243                                 }
244                                 else
245                                 {
246                                         self.hook_state |= HOOK_REMOVING;
247                                         self.hook_state &~= HOOK_WAITING_FOR_RELEASE;
248                                 }
249                         }
250                         
251                         return TRUE;
252                 }
253                 case WR_PRECACHE:
254                 {
255                         precache_model ("models/weapons/g_hookgun.md3");
256                         precache_model ("models/weapons/v_hookgun.md3");
257                         precache_model ("models/weapons/h_hookgun.iqm");
258                         precache_sound ("weapons/hook_impact.wav"); // done by g_hook.qc
259                         precache_sound ("weapons/hook_fire.wav");
260                         precache_sound ("weapons/hookbomb_fire.wav");
261                         return TRUE;
262                 }
263                 case WR_SETUP:
264                 {
265                         weapon_setup(WEP_HOOK);
266                         self.current_ammo = ammo_fuel;
267                         self.hook_state &~= HOOK_WAITING_FOR_RELEASE;
268                         return TRUE;
269                 }
270                 case WR_CHECKAMMO1:
271                 {
272                         if(self.hook)
273                                 return self.ammo_fuel > 0;
274                         else
275                                 return self.ammo_fuel >= autocvar_g_balance_hook_primary_fuel;
276                 }
277                 case WR_CHECKAMMO2:
278                 {
279                         return self.ammo_cells >= autocvar_g_balance_hook_secondary_ammo;
280                 }
281                 case WR_RESETPLAYER:
282                 {
283                         self.hook_refire = time;
284                         return TRUE;
285                 }
286                 case WR_SUICIDEMESSAGE:
287                 {
288                         return FALSE;
289                 }
290                 case WR_KILLMESSAGE:
291                 {
292                         return WEAPON_HOOK_MURDER;
293                 }
294         }
295         return TRUE;
296 }
297 #endif
298 #ifdef CSQC
299 float w_hook(float req)
300 {
301         switch(req)
302         {
303                 case WR_IMPACTEFFECT:
304                 {
305                         vector org2;
306                         org2 = w_org + w_backoff * 2;
307                         pointparticles(particleeffectnum("hookbomb_explode"), org2, '0 0 0', 1);
308                         if(!w_issilent)
309                                 sound(self, CH_SHOTS, "weapons/hookbomb_impact.wav", VOL_BASE, ATTN_NORM);
310                                 
311                         return TRUE;
312                 }
313                 case WR_PRECACHE:
314                 {
315                         precache_sound("weapons/hookbomb_impact.wav");
316                         return TRUE;
317                 }
318         }
319         return TRUE;
320 }
321 #endif
322 #endif