#ifndef IMPLEMENTATION
-REGISTER_WEAPON(
-/* WEP_##id */ HOOK,
-/* function */ W_Hook,
-/* ammotype */ ammo_fuel,
-/* impulse */ 0,
-/* flags */ WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
-/* rating */ 0,
-/* color */ '0 0.5 0',
-/* modelname */ "hookgun",
-/* simplemdl */ "foobar",
-/* crosshair */ "gfx/crosshairhook 0.5",
-/* wepimg */ "weaponhook",
-/* refname */ "hook",
-/* wepname */ _("Grappling Hook")
-);
-
-#define HOOK_SETTINGS(w_cvar,w_prop) HOOK_SETTINGS_LIST(w_cvar, w_prop, HOOK, hook)
-#define HOOK_SETTINGS_LIST(w_cvar,w_prop,id,sn) \
- w_cvar(id, sn, BOTH, animtime) \
- w_cvar(id, sn, BOTH, refire) \
- w_cvar(id, sn, PRI, ammo) \
- w_cvar(id, sn, PRI, hooked_ammo) \
- w_cvar(id, sn, PRI, hooked_time_free) \
- w_cvar(id, sn, PRI, hooked_time_max) \
- w_cvar(id, sn, SEC, damage) \
- w_cvar(id, sn, SEC, duration) \
- w_cvar(id, sn, SEC, edgedamage) \
- w_cvar(id, sn, SEC, force) \
- w_cvar(id, sn, SEC, gravity) \
- w_cvar(id, sn, SEC, lifetime) \
- w_cvar(id, sn, SEC, power) \
- w_cvar(id, sn, SEC, radius) \
- w_cvar(id, sn, SEC, speed) \
- w_cvar(id, sn, SEC, health) \
- w_cvar(id, sn, SEC, damageforcescale) \
- w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \
- w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \
- w_prop(id, sn, string, weaponreplace, weaponreplace) \
- w_prop(id, sn, float, weaponstart, weaponstart) \
- w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \
- w_prop(id, sn, float, weaponthrowable, weaponthrowable)
+CLASS(Hook, Weapon)
+/* ammotype */ ATTRIB(Hook, ammo_field, .int, ammo_fuel)
+/* impulse */ ATTRIB(Hook, impulse, int, 0)
+/* flags */ ATTRIB(Hook, spawnflags, int, WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
+/* rating */ ATTRIB(Hook, bot_pickupbasevalue, float, 0);
+/* color */ ATTRIB(Hook, wpcolor, vector, '0 0.5 0');
+/* modelname */ ATTRIB(Hook, mdl, string, "hookgun");
+#ifndef MENUQC
+/* model */ ATTRIB(Hook, m_model, Model, MDL_HOOK_ITEM);
+#endif
+/* crosshair */ ATTRIB(Hook, w_crosshair, string, "gfx/crosshairhook");
+/* crosshair */ ATTRIB(Hook, w_crosshair_size, float, 0.5);
+/* wepimg */ ATTRIB(Hook, model2, string, "weaponhook");
+/* refname */ ATTRIB(Hook, netname, string, "hook");
+/* wepname */ ATTRIB(Hook, m_name, string, _("Grappling Hook"));
+ ATTRIB(Hook, ammo_factor, float, 1)
+
+#define X(BEGIN, P, END, class, prefix) \
+ BEGIN(class) \
+ P(class, prefix, ammo, float, PRI) \
+ P(class, prefix, animtime, float, BOTH) \
+ P(class, prefix, damageforcescale, float, SEC) \
+ P(class, prefix, damage, float, SEC) \
+ P(class, prefix, duration, float, SEC) \
+ P(class, prefix, edgedamage, float, SEC) \
+ P(class, prefix, force, float, SEC) \
+ P(class, prefix, gravity, float, SEC) \
+ P(class, prefix, health, float, SEC) \
+ P(class, prefix, hooked_ammo, float, PRI) \
+ P(class, prefix, hooked_time_free, float, PRI) \
+ P(class, prefix, hooked_time_max, float, PRI) \
+ P(class, prefix, lifetime, float, SEC) \
+ P(class, prefix, power, float, SEC) \
+ P(class, prefix, radius, float, SEC) \
+ P(class, prefix, refire, float, BOTH) \
+ P(class, prefix, speed, float, SEC) \
+ P(class, prefix, switchdelay_drop, float, NONE) \
+ P(class, prefix, switchdelay_raise, float, NONE) \
+ P(class, prefix, weaponreplace, string, NONE) \
+ P(class, prefix, weaponstartoverride, float, NONE) \
+ P(class, prefix, weaponstart, float, NONE) \
+ P(class, prefix, weaponthrowable, float, NONE) \
+ END()
+ W_PROPS(X, Hook, hook)
+#undef X
+
+ENDCLASS(Hook)
+REGISTER_WEAPON(HOOK, hook, NEW(Hook));
+
+CLASS(OffhandHook, OffhandWeapon)
+#ifdef SVQC
+ METHOD(OffhandHook, offhand_think, void(OffhandHook this, entity actor, bool key_pressed))
+ {
+ Weapon wep = WEP_HOOK;
+ .entity weaponentity = weaponentities[1];
+ wep.wr_think(wep, actor, weaponentity, key_pressed ? 1 : 0);
+ }
+#endif
+ENDCLASS(OffhandHook)
+OffhandHook OFFHAND_HOOK; STATIC_INIT(OFFHAND_HOOK) { OFFHAND_HOOK = NEW(OffhandHook); }
#ifdef SVQC
-HOOK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP)
.float dmg;
.float dmg_edge;
#ifdef IMPLEMENTATION
#ifdef SVQC
-spawnfunc(weapon_hook)
-{
- if(g_grappling_hook) // offhand hook
- {
- startitem_failed = true;
- remove(self);
- return;
- }
- weapon_defaultspawnfunc(WEP_HOOK.m_id);
-}
+spawnfunc(weapon_hook) { weapon_defaultspawnfunc(this, WEP_HOOK); }
-void W_Hook_ExplodeThink(void)
+void W_Hook_ExplodeThink()
{SELFPARAM();
float dt, dmg_remaining_next, f;
remove(self);
}
-void W_Hook_Explode2(void)
+void W_Hook_Explode2()
{SELFPARAM();
self.event_damage = func_null;
self.touch = func_null;
self.movetype = MOVETYPE_NONE;
}
-void W_Hook_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
-{SELFPARAM();
- if(self.health <= 0)
+void W_Hook_Explode2_use(entity this, entity actor, entity trigger)
+{
+ WITHSELF(this, W_Hook_Explode2());
+}
+
+void W_Hook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{
+ if(this.health <= 0)
return;
- if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
+ if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
return; // g_projectiles_damage says to halt
- self.health = self.health - damage;
+ this.health = this.health - damage;
- if(self.health <= 0)
- W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2);
+ if(this.health <= 0)
+ W_PrepareExplosionByDamage(this, this.realowner, W_Hook_Explode2);
}
-void W_Hook_Touch2(void)
+void W_Hook_Touch2()
{SELFPARAM();
PROJECTILE_TOUCH;
- self.use();
+ this.use(this, NULL, NULL);
}
-void W_Hook_Attack2(void)
-{SELFPARAM();
- entity gren;
-
- //W_DecreaseAmmo(WEP_CVAR_SEC(hook, ammo)); // WEAPONTODO: Figure out how to handle ammo with hook secondary (gravitybomb)
- W_SetupShot(self, false, 4, SND(HOOKBOMB_FIRE), CH_WEAPON_A, WEP_CVAR_SEC(hook, damage));
+void W_Hook_Attack2(Weapon thiswep, entity actor)
+{
+ //W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hook, ammo)); // WEAPONTODO: Figure out how to handle ammo with hook secondary (gravitybomb)
+ W_SetupShot(actor, false, 4, SND_HOOKBOMB_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hook, damage));
- gren = spawn();
- gren.owner = gren.realowner = self;
- gren.classname = "hookbomb";
+ entity gren = new(hookbomb);
+ gren.owner = gren.realowner = actor;
gren.bot_dodge = true;
gren.bot_dodgerating = WEP_CVAR_SEC(hook, damage);
gren.movetype = MOVETYPE_TOSS;
gren.nextthink = time + WEP_CVAR_SEC(hook, lifetime);
gren.think = adaptor_think2use_hittype_splash;
- gren.use = W_Hook_Explode2;
+ gren.use = W_Hook_Explode2_use;
gren.touch = W_Hook_Touch2;
gren.takedamage = DAMAGE_YES;
gren.missile_flags = MIF_SPLASH | MIF_ARC;
gren.velocity = '0 0 1' * WEP_CVAR_SEC(hook, speed);
- if(autocvar_g_projectiles_newton_style)
- gren.velocity = gren.velocity + self.velocity;
+ if (autocvar_g_projectiles_newton_style)
+ gren.velocity = gren.velocity + actor.velocity;
gren.gravity = WEP_CVAR_SEC(hook, gravity);
//W_SetupProjVelocity_Basic(gren); // just falling down!
CSQCProjectile(gren, true, PROJECTILE_HOOKBOMB, true);
- MUTATOR_CALLHOOK(EditProjectile, self, gren);
+ MUTATOR_CALLHOOK(EditProjectile, actor, gren);
}
-bool W_Hook(int req)
-{SELFPARAM();
- float hooked_time_max, hooked_fuel;
+METHOD(Hook, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
+{
+ SELFPARAM();
+ if (fire & 1) {
+ if(!actor.hook)
+ if(!(actor.hook_state & HOOK_WAITING_FOR_RELEASE))
+ if(time > actor.hook_refire)
+ if(weapon_prepareattack(thiswep, actor, weaponentity, false, -1))
+ {
+ W_DecreaseAmmo(thiswep, actor, thiswep.ammo_factor * WEP_CVAR_PRI(hook, ammo));
+ actor.hook_state |= HOOK_FIRING;
+ actor.hook_state |= HOOK_WAITING_FOR_RELEASE;
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready);
+ }
+ } else {
+ actor.hook_state |= HOOK_REMOVING;
+ actor.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
+ }
+
+ if(fire & 2)
+ {
+ if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hook, refire)))
+ {
+ W_Hook_Attack2(thiswep, actor);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready);
+ }
+ }
+
+ if(actor.hook)
+ {
+ // if hooked, no bombs, and increase the timer
+ actor.hook_refire = max(actor.hook_refire, time + WEP_CVAR_PRI(hook, refire) * W_WeaponRateFactor());
+
+ // hook also inhibits health regeneration, but only for 1 second
+ if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
+ actor.pauseregen_finished = max(actor.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
+ }
+
+ if(actor.hook && actor.hook.state == 1)
+ {
+ float hooked_time_max = WEP_CVAR_PRI(hook, hooked_time_max);
+ if(hooked_time_max > 0)
+ {
+ if( time > actor.hook_time_hooked + hooked_time_max )
+ actor.hook_state |= HOOK_REMOVING;
+ }
+
+ float hooked_fuel = thiswep.ammo_factor * WEP_CVAR_PRI(hook, hooked_ammo);
+ if(hooked_fuel > 0)
+ {
+ if( time > actor.hook_time_fueldecrease )
+ {
+ if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
+ {
+ if( actor.ammo_fuel >= (time - actor.hook_time_fueldecrease) * hooked_fuel )
+ {
+ W_DecreaseAmmo(thiswep, actor, (time - actor.hook_time_fueldecrease) * hooked_fuel);
+ actor.hook_time_fueldecrease = time;
+ // decrease next frame again
+ }
+ else
+ {
+ actor.ammo_fuel = 0;
+ actor.hook_state |= HOOK_REMOVING;
+ W_SwitchWeapon_Force(actor, w_getbestweapon(actor));
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ actor.hook_time_hooked = time;
+ actor.hook_time_fueldecrease = time + WEP_CVAR_PRI(hook, hooked_time_free);
+ }
+
+ actor.hook_state = BITSET(actor.hook_state, HOOK_PULLING, (!PHYS_INPUT_BUTTON_CROUCH(actor) || !autocvar_g_balance_grapplehook_crouchslide));
+
+ if (actor.hook_state & HOOK_FIRING)
+ {
+ if (actor.hook)
+ RemoveGrapplingHook(actor);
+ WITHSELF(actor, FireGrapplingHook());
+ actor.hook_state &= ~HOOK_FIRING;
+ actor.hook_refire = max(actor.hook_refire, time + autocvar_g_balance_grapplehook_refire * W_WeaponRateFactor());
+ }
+ else if (actor.hook_state & HOOK_REMOVING)
+ {
+ if (actor.hook)
+ RemoveGrapplingHook(actor);
+ actor.hook_state &= ~HOOK_REMOVING;
+ }
+}
+METHOD(Hook, wr_setup, void(entity thiswep))
+{
+ SELFPARAM();
+ self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
+}
+METHOD(Hook, wr_checkammo1, bool(Hook thiswep))
+{
+ SELFPARAM();
+ if (!thiswep.ammo_factor) return true;
+ if(self.hook)
+ return self.ammo_fuel > 0;
+ else
+ return self.ammo_fuel >= WEP_CVAR_PRI(hook, ammo);
+}
+METHOD(Hook, wr_checkammo2, bool(Hook thiswep))
+{
+ // infinite ammo for now
+ return true; // self.ammo_cells >= WEP_CVAR_SEC(hook, ammo); // WEAPONTODO: see above
+}
+METHOD(Hook, wr_resetplayer, void(entity thiswep))
+{
+ SELFPARAM();
+ RemoveGrapplingHook(self);
+ self.hook_time = 0;
+ self.hook_refire = time;
+}
+METHOD(Hook, wr_killmessage, Notification(entity thiswep))
+{
+ return WEAPON_HOOK_MURDER;
+}
+
+#endif
+#ifdef CSQC
+
+METHOD(Hook, wr_impacteffect, void(entity thiswep))
+{
+ SELFPARAM();
+ vector org2;
+ org2 = w_org + w_backoff * 2;
+ pointparticles(EFFECT_HOOK_EXPLODE, org2, '0 0 0', 1);
+ if(!w_issilent)
+ sound(self, CH_SHOTS, SND_HOOKBOMB_IMPACT, VOL_BASE, ATTN_NORM);
+}
+
+#endif
+
+#ifdef CSQC
+#include <lib/csqcmodel/interpolate.qh>
+#include <lib/warpzone/common.qh>
+
+float autocvar_cl_grapplehook_alpha = 1;
+
+void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float theAlpha, float drawflag, vector vieworg);
+
+entityclass(Hook);
+class(Hook) .entity HookType; // ENT_CLIENT_*
+class(Hook) .vector origin;
+class(Hook) .vector velocity;
+class(Hook) .float HookSilent;
+class(Hook) .float HookRange;
- switch(req)
+string Draw_GrapplingHook_trace_callback_tex;
+float Draw_GrapplingHook_trace_callback_rnd;
+vector Draw_GrapplingHook_trace_callback_rgb;
+float Draw_GrapplingHook_trace_callback_a;
+void Draw_GrapplingHook_trace_callback(vector start, vector hit, vector end)
+{
+ float i;
+ vector vorg;
+ vorg = WarpZone_TransformOrigin(WarpZone_trace_transform, view_origin);
+ for(i = 0; i < Draw_GrapplingHook_trace_callback_a; ++i)
+ Draw_CylindricLine(hit, start, 8, Draw_GrapplingHook_trace_callback_tex, 0.25, Draw_GrapplingHook_trace_callback_rnd, Draw_GrapplingHook_trace_callback_rgb, min(1, Draw_GrapplingHook_trace_callback_a - i), DRAWFLAG_NORMAL, vorg);
+ Draw_GrapplingHook_trace_callback_rnd += 0.25 * vlen(hit - start) / 8;
+}
+
+class(Hook) .float teleport_time;
+void Draw_GrapplingHook(entity this)
+{
+ vector a, b, atrans;
+ string tex;
+ vector rgb;
+ float t;
+ vector vs;
+ float intensity, offset;
+
+ if(this.teleport_time)
+ if(time > this.teleport_time)
{
- case WR_AIM:
+ sound (this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); // safeguard
+ this.teleport_time = 0;
+ }
+
+ InterpolateOrigin_Do(this);
+
+ int s = W_GetGunAlignment(world);
+
+ switch(this.HookType)
+ {
+ default:
+ case NET_ENT_CLIENT_HOOK:
+ vs = hook_shotorigin[s];
+ break;
+ case NET_ENT_CLIENT_ARC_BEAM:
+ vs = lightning_shotorigin[s];
+ break;
+ }
+
+ if((this.owner.sv_entnum == player_localentnum - 1) && autocvar_chase_active <= 0)
+ {
+ switch(this.HookType)
{
- // no bot AI for hook (yet?)
- return true;
+ default:
+ case NET_ENT_CLIENT_HOOK:
+ a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z;
+ b = this.origin;
+ break;
+ case NET_ENT_CLIENT_ARC_BEAM:
+ if(this.HookRange)
+ b = view_origin + view_forward * this.HookRange;
+ else
+ b = view_origin + view_forward * vlen(this.velocity - this.origin); // honor original length of beam!
+ WarpZone_TraceLine(view_origin, b, MOVE_NORMAL, world);
+ b = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
+ a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z;
+ break;
}
- case WR_THINK:
+ }
+ else
+ {
+ switch(this.HookType)
{
- if(self.BUTTON_ATCK || self.BUTTON_HOOK)
- {
- if(!self.hook)
- if(!(self.hook_state & HOOK_WAITING_FOR_RELEASE))
- if(!(self.hook_state & HOOK_FIRING))
- if(time > self.hook_refire)
- if(weapon_prepareattack(0, -1))
- {
- W_DecreaseAmmo(WEP_CVAR_PRI(hook, ammo));
- self.hook_state |= HOOK_FIRING;
- weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready);
- }
- }
+ default:
+ case NET_ENT_CLIENT_HOOK:
+ a = this.velocity;
+ b = this.origin;
+ break;
+ case NET_ENT_CLIENT_ARC_BEAM:
+ a = this.origin;
+ b = this.velocity;
+ break;
+ }
+ }
+
+ t = entcs_GetTeamColor(this.owner.sv_entnum);
- if(self.BUTTON_ATCK2)
+ switch(this.HookType)
+ {
+ default:
+ case NET_ENT_CLIENT_HOOK:
+ intensity = autocvar_cl_grapplehook_alpha;
+ offset = 0;
+ switch(t)
{
- if(weapon_prepareattack(1, WEP_CVAR_SEC(hook, refire)))
- {
- W_Hook_Attack2();
- weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready);
- }
+ case NUM_TEAM_1: tex = "particles/hook_red"; rgb = '1 0.3 0.3'; break;
+ case NUM_TEAM_2: tex = "particles/hook_blue"; rgb = '0.3 0.3 1'; break;
+ case NUM_TEAM_3: tex = "particles/hook_yellow"; rgb = '1 1 0.3'; break;
+ case NUM_TEAM_4: tex = "particles/hook_pink"; rgb = '1 0.3 1'; break;
+ default: tex = "particles/hook_white"; rgb = entcs_GetColor(this.sv_entnum - 1); break;
}
+ break;
+ case NET_ENT_CLIENT_ARC_BEAM: // todo
+ intensity = bound(0.2, 1 + Noise_Pink(this, frametime) * 1 + Noise_Burst(this, frametime, 0.03) * 0.3, 2);
+ offset = Noise_Brown(this, frametime) * 10;
+ tex = "particles/lgbeam";
+ rgb = '1 1 1';
+ break;
+ }
- if(self.hook)
- {
- // if hooked, no bombs, and increase the timer
- self.hook_refire = max(self.hook_refire, time + WEP_CVAR_PRI(hook, refire) * W_WeaponRateFactor());
+ Draw_GrapplingHook_trace_callback_tex = tex;
+ Draw_GrapplingHook_trace_callback_rnd = offset;
+ Draw_GrapplingHook_trace_callback_rgb = rgb;
+ Draw_GrapplingHook_trace_callback_a = intensity;
+ WarpZone_TraceBox_ThroughZone(a, '0 0 0', '0 0 0', b, ((this.HookType == NET_ENT_CLIENT_HOOK) ? MOVE_NOTHING : MOVE_NORMAL), world, world, Draw_GrapplingHook_trace_callback);
+ Draw_GrapplingHook_trace_callback_tex = string_null;
- // hook also inhibits health regeneration, but only for 1 second
- if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
- self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
- }
+ atrans = WarpZone_TransformOrigin(WarpZone_trace_transform, a);
- if(self.hook && self.hook.state == 1)
+ switch(this.HookType)
+ {
+ default:
+ case NET_ENT_CLIENT_HOOK:
+ if(vdist(trace_endpos - atrans, >, 0.5))
{
- hooked_time_max = WEP_CVAR_PRI(hook, hooked_time_max);
- if(hooked_time_max > 0)
- {
- if( time > self.hook_time_hooked + hooked_time_max )
- self.hook_state |= HOOK_REMOVING;
- }
-
- hooked_fuel = WEP_CVAR_PRI(hook, hooked_ammo);
- if(hooked_fuel > 0)
- {
- if( time > self.hook_time_fueldecrease )
- {
- if(!(self.items & IT_UNLIMITED_WEAPON_AMMO))
- {
- if( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel )
- {
- W_DecreaseAmmo((time - self.hook_time_fueldecrease) * hooked_fuel);
- self.hook_time_fueldecrease = time;
- // decrease next frame again
- }
- else
- {
- self.ammo_fuel = 0;
- self.hook_state |= HOOK_REMOVING;
- W_SwitchWeapon_Force(self, w_getbestweapon(self));
- }
- }
- }
- }
+ setorigin(this, trace_endpos); // hook endpoint!
+ this.angles = vectoangles(trace_endpos - atrans);
+ this.drawmask = MASK_NORMAL;
}
else
{
- self.hook_time_hooked = time;
- self.hook_time_fueldecrease = time + WEP_CVAR_PRI(hook, hooked_time_free);
+ this.drawmask = 0;
}
+ break;
+ case NET_ENT_CLIENT_ARC_BEAM:
+ setorigin(this, a); // beam origin!
+ break;
+ }
- if(self.BUTTON_CROUCH)
- {
- self.hook_state &= ~HOOK_PULLING;
- if(self.BUTTON_ATCK || self.BUTTON_HOOK)
- self.hook_state &= ~HOOK_RELEASING;
- else
- self.hook_state |= HOOK_RELEASING;
- }
- else
- {
- self.hook_state |= HOOK_PULLING;
- self.hook_state &= ~HOOK_RELEASING;
-
- if(self.BUTTON_ATCK || self.BUTTON_HOOK)
- {
- // already fired
- if(self.hook)
- self.hook_state |= HOOK_WAITING_FOR_RELEASE;
- }
- else
- {
- self.hook_state |= HOOK_REMOVING;
- self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
- }
- }
+ switch(this.HookType)
+ {
+ default:
+ case NET_ENT_CLIENT_HOOK:
+ break;
+ case NET_ENT_CLIENT_ARC_BEAM:
+ pointparticles(EFFECT_ARC_LIGHTNING2, trace_endpos, normalize(atrans - trace_endpos), frametime * intensity); // todo: new effect
+ break;
+ }
+}
- return true;
- }
- case WR_INIT:
- {
- HOOK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP);
- return true;
- }
- case WR_SETUP:
- {
- self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
- return true;
- }
- case WR_CHECKAMMO1:
- {
- if(self.hook)
- return self.ammo_fuel > 0;
- else
- return self.ammo_fuel >= WEP_CVAR_PRI(hook, ammo);
- }
- case WR_CHECKAMMO2:
- {
- // infinite ammo for now
- return true; // self.ammo_cells >= WEP_CVAR_SEC(hook, ammo); // WEAPONTODO: see above
- }
- case WR_CONFIG:
- {
- HOOK_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS);
- return true;
- }
- case WR_RESETPLAYER:
- {
- self.hook_refire = time;
- return true;
- }
- case WR_SUICIDEMESSAGE:
- {
- return false;
- }
- case WR_KILLMESSAGE:
+void Remove_GrapplingHook(entity this)
+{
+ sound (this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM);
+
+ if(csqcplayer && csqcplayer.hook == this)
+ csqcplayer.hook = NULL;
+}
+
+NET_HANDLE(ENT_CLIENT_HOOK, bool bIsNew)
+{
+ self.HookType = NET_ENT_CLIENT_HOOK;
+
+ int sf = ReadByte();
+
+ self.HookSilent = (sf & 0x80);
+ self.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN;
+
+ InterpolateOrigin_Undo(self);
+
+ if(sf & 1)
+ {
+ int myowner = ReadByte();
+ self.owner = playerslots[myowner - 1];
+ self.sv_entnum = myowner;
+ if(csqcplayer && myowner == player_localentnum)
+ csqcplayer.hook = self;
+ switch(self.HookType)
{
- return WEAPON_HOOK_MURDER;
+ default:
+ case NET_ENT_CLIENT_HOOK:
+ self.HookRange = 0;
+ break;
+ case NET_ENT_CLIENT_ARC_BEAM:
+ self.HookRange = ReadCoord();
+ break;
}
}
- return false;
-}
-#endif
-#ifdef CSQC
-bool W_Hook(int req)
-{SELFPARAM();
- switch(req)
+ if(sf & 2)
{
- case WR_IMPACTEFFECT:
- {
- vector org2;
- org2 = w_org + w_backoff * 2;
- pointparticles(particleeffectnum(EFFECT_HOOK_EXPLODE), org2, '0 0 0', 1);
- if(!w_issilent)
- sound(self, CH_SHOTS, SND_HOOKBOMB_IMPACT, VOL_BASE, ATTN_NORM);
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+ setorigin(self, self.origin);
+ }
+ if(sf & 4)
+ {
+ self.velocity_x = ReadCoord();
+ self.velocity_y = ReadCoord();
+ self.velocity_z = ReadCoord();
+ }
- return true;
- }
- case WR_INIT:
- {
- return true;
- }
- case WR_ZOOMRETICLE:
+ InterpolateOrigin_Note(this);
+
+ if(bIsNew || !self.teleport_time)
+ {
+ self.draw = Draw_GrapplingHook;
+ self.entremove = Remove_GrapplingHook;
+
+ switch(self.HookType)
{
- // no weapon specific image for this weapon
- return false;
+ default:
+ case NET_ENT_CLIENT_HOOK:
+ // for the model
+ setmodel(self, MDL_HOOK);
+ self.drawmask = MASK_NORMAL;
+ break;
+ case NET_ENT_CLIENT_ARC_BEAM:
+ sound (self, CH_SHOTS_SINGLE, SND_LGBEAM_FLY, VOL_BASE, ATTEN_NORM);
+ break;
}
}
- return false;
+
+ self.teleport_time = time + 10;
+ return true;
}
+
+// TODO: hook: temporarily transform self.origin for drawing the model along warpzones!
#endif
+
#endif