5 /* ammotype */ IT_CELLS|IT_FUEL,
7 /* flags */ WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
10 /* shortname */ "hook",
11 /* fullname */ _("Grappling Hook")
23 .float hook_time_hooked;
24 .float hook_time_fueldecrease;
26 void W_Hook_ExplodeThink (void)
28 float dt, dmg_remaining_next, f;
30 dt = time - self.teleport_time;
31 dmg_remaining_next = pow(bound(0, 1 - dt / self.dmg_duration, 1), self.dmg_power);
33 f = self.dmg_last - dmg_remaining_next;
34 self.dmg_last = dmg_remaining_next;
36 RadiusDamage (self, self.realowner, self.dmg * f, self.dmg_edge * f, self.dmg_radius, self.realowner, self.dmg_force * f, self.projectiledeathtype, world);
37 self.projectiledeathtype |= HITTYPE_BOUNCE;
38 //RadiusDamage (self, world, self.dmg * f, self.dmg_edge * f, self.dmg_radius, world, self.dmg_force * f, self.projectiledeathtype, world);
40 if(dt < self.dmg_duration)
41 self.nextthink = time + 0.05; // soon
46 void W_Hook_Explode2 (void)
48 self.event_damage = func_null;
49 self.touch = func_null;
50 self.effects |= EF_NODRAW;
52 self.think = W_Hook_ExplodeThink;
53 self.nextthink = time;
54 self.dmg = autocvar_g_balance_hook_secondary_damage;
55 self.dmg_edge = autocvar_g_balance_hook_secondary_edgedamage;
56 self.dmg_radius = autocvar_g_balance_hook_secondary_radius;
57 self.dmg_force = autocvar_g_balance_hook_secondary_force;
58 self.dmg_power = autocvar_g_balance_hook_secondary_power;
59 self.dmg_duration = autocvar_g_balance_hook_secondary_duration;
60 self.teleport_time = time;
62 self.movetype = MOVETYPE_NONE;
65 void W_Hook_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
70 if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
71 return; // g_projectiles_damage says to halt
73 self.health = self.health - damage;
76 W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2);
79 void W_Hook_Touch2 (void)
89 W_DecreaseAmmo(ammo_cells, autocvar_g_balance_hook_secondary_ammo, FALSE);
90 W_SetupShot (self, FALSE, 4, "weapons/hookbomb_fire.wav", CH_WEAPON_A, autocvar_g_balance_hook_secondary_damage);
93 gren.owner = gren.realowner = self;
94 gren.classname = "hookbomb";
95 gren.bot_dodge = TRUE;
96 gren.bot_dodgerating = autocvar_g_balance_hook_secondary_damage;
97 gren.movetype = MOVETYPE_TOSS;
98 PROJECTILE_MAKETRIGGER(gren);
99 gren.projectiledeathtype = WEP_HOOK | HITTYPE_SECONDARY;
100 setorigin(gren, w_shotorg);
101 setsize(gren, '0 0 0', '0 0 0');
103 gren.nextthink = time + autocvar_g_balance_hook_secondary_lifetime;
104 gren.think = adaptor_think2use_hittype_splash;
105 gren.use = W_Hook_Explode2;
106 gren.touch = W_Hook_Touch2;
108 gren.takedamage = DAMAGE_YES;
109 gren.health = autocvar_g_balance_hook_secondary_health;
110 gren.damageforcescale = autocvar_g_balance_hook_secondary_damageforcescale;
111 gren.event_damage = W_Hook_Damage;
112 gren.damagedbycontents = TRUE;
113 gren.missile_flags = MIF_SPLASH | MIF_ARC;
115 gren.velocity = '0 0 1' * autocvar_g_balance_hook_secondary_speed;
116 if(autocvar_g_projectiles_newton_style)
117 gren.velocity = gren.velocity + self.velocity;
119 gren.gravity = autocvar_g_balance_hook_secondary_gravity;
120 //W_SetupProjectileVelocity(gren); // just falling down!
122 gren.angles = '0 0 0';
123 gren.flags = FL_PROJECTILE;
125 CSQCProjectile(gren, TRUE, PROJECTILE_HOOKBOMB, TRUE);
127 other = gren; MUTATOR_CALLHOOK(EditProjectile);
130 void spawnfunc_weapon_hook (void)
132 if(g_grappling_hook) // offhand hook
134 startitem_failed = TRUE;
138 weapon_defaultspawnfunc(WEP_HOOK);
141 float w_hook(float req)
143 float hooked_time_max, hooked_fuel;
149 else if (req == WR_THINK)
151 if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
154 if not(self.hook_state & HOOK_WAITING_FOR_RELEASE)
155 if not(self.hook_state & HOOK_FIRING)
156 if (time > self.hook_refire)
157 if (weapon_prepareattack(0, -1))
159 W_DecreaseAmmo(ammo_fuel, autocvar_g_balance_hook_primary_fuel, FALSE);
160 self.hook_state |= HOOK_FIRING;
161 weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hook_primary_animtime, w_ready);
165 if (self.BUTTON_ATCK2)
167 if (weapon_prepareattack(1, autocvar_g_balance_hook_secondary_refire))
170 weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hook_secondary_animtime, w_ready);
176 // if hooked, no bombs, and increase the timer
177 self.hook_refire = max(self.hook_refire, time + autocvar_g_balance_hook_primary_refire * W_WeaponRateFactor());
179 // hook also inhibits health regeneration, but only for 1 second
180 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
181 self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
184 if(self.hook && self.hook.state == 1)
186 hooked_time_max = autocvar_g_balance_hook_primary_hooked_time_max;
187 if (hooked_time_max > 0)
189 if ( time > self.hook_time_hooked + hooked_time_max )
190 self.hook_state |= HOOK_REMOVING;
193 hooked_fuel = autocvar_g_balance_hook_primary_hooked_fuel;
196 if ( time > self.hook_time_fueldecrease )
198 if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
200 if ( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel )
202 W_DecreaseAmmo(ammo_fuel, (time - self.hook_time_fueldecrease) * hooked_fuel, FALSE);
203 self.hook_time_fueldecrease = time;
204 // decrease next frame again
209 self.hook_state |= HOOK_REMOVING;
210 W_SwitchWeapon_Force(self, w_getbestweapon(self));
218 self.hook_time_hooked = time;
219 self.hook_time_fueldecrease = time + autocvar_g_balance_hook_primary_hooked_time_free;
222 if (self.BUTTON_CROUCH)
224 self.hook_state &= ~HOOK_PULLING;
225 if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
226 self.hook_state &= ~HOOK_RELEASING;
228 self.hook_state |= HOOK_RELEASING;
232 self.hook_state |= HOOK_PULLING;
233 self.hook_state &= ~HOOK_RELEASING;
235 if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
239 self.hook_state |= HOOK_WAITING_FOR_RELEASE;
243 self.hook_state |= HOOK_REMOVING;
244 self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
248 else if (req == WR_PRECACHE)
250 precache_model ("models/weapons/g_hookgun.md3");
251 precache_model ("models/weapons/v_hookgun.md3");
252 precache_model ("models/weapons/h_hookgun.iqm");
253 precache_sound ("weapons/hook_impact.wav"); // done by g_hook.qc
254 precache_sound ("weapons/hook_fire.wav");
255 precache_sound ("weapons/hookbomb_fire.wav");
257 else if (req == WR_SETUP)
259 weapon_setup(WEP_HOOK);
260 self.current_ammo = ammo_fuel;
261 self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
263 else if (req == WR_CHECKAMMO1)
266 return self.ammo_fuel > 0;
268 return self.ammo_fuel >= autocvar_g_balance_hook_primary_fuel;
270 else if (req == WR_CHECKAMMO2)
272 return self.ammo_cells >= autocvar_g_balance_hook_secondary_ammo;
274 else if (req == WR_RESETPLAYER)
276 self.hook_refire = time;
278 else if (req == WR_SUICIDEMESSAGE)
282 else if (req == WR_KILLMESSAGE)
284 return WEAPON_HOOK_MURDER;
290 float w_hook(float req)
292 if(req == WR_IMPACTEFFECT)
295 org2 = w_org + w_backoff * 2;
296 pointparticles(particleeffectnum("hookbomb_explode"), org2, '0 0 0', 1);
298 sound(self, CH_SHOTS, "weapons/hookbomb_impact.wav", VOL_BASE, ATTEN_NORM);
300 else if(req == WR_PRECACHE)
302 precache_sound("weapons/hookbomb_impact.wav");