-.vector HookStart;
-.vector HookEnd;
-.float HookKillTime;
-.vector LGBeamStart;
-.vector LGBeamEnd;
-.float LGBeamKillTime;
-.float LGBeamSound;
-.float LGBeamSilent;
-.vector GauntletBeamStart;
-.vector GauntletBeamEnd;
-.float GauntletBeamKillTime;
-.float GauntletBeamSound;
-.float GauntletBeamSilent;
-
+.float HookType; // ENT_CLIENT_*
+.vector origin;
+.vector velocity;
+.float HookSound;
+.float HookSilent;
void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float alpha, float drawflag)
{
void Draw_GrapplingHook()
{
vector a, b;
- string tex;
+ string tex, snd;
vector rgb;
float t;
float s;
vector vs;
- if(time < self.HookKillTime)
- {
- s = cvar("cl_gunalign");
- if(s != 1 && s != 2 && s != 4)
- s = 3; // default value
- --s;
- vs = hook_shotorigin[s];
-
- if(self.sv_entnum == player_localentnum - 1)
- a = view_origin + view_forward * vs_x + view_right * -vs_y + view_up * vs_z;
- else
- a = self.HookStart;
- b = self.HookEnd;
-
- t = GetPlayerColorForce(self.sv_entnum);
-
- if(t == COLOR_TEAM1)
- {
- tex = "particles/hook_red";
- rgb = '1 .3 .3';
- }
- else if(t == COLOR_TEAM2)
- {
- tex = "particles/hook_blue";
- rgb = '.3 .3 1';
- }
- else if(t == COLOR_TEAM3)
- {
- tex = "particles/hook_yellow";
- rgb = '1 1 .3';
- }
- else if(t == COLOR_TEAM4)
- {
- tex = "particles/hook_pink";
- rgb = '1 .3 1';
- }
- else
- {
- tex = "particles/hook_green";
- rgb = '.3 1 .3';
- }
+ InterpolateOrigin_Do();
- Draw_GrapplingHook_trace_callback_tex = tex;
- Draw_GrapplingHook_trace_callback_rnd = random();
- WarpZone_TraceBox_ThroughZone(a, '0 0 0', '0 0 0', b, MOVE_NOMONSTERS, world, world, Draw_GrapplingHook_trace_callback);
- Draw_GrapplingHook_trace_callback_tex = string_null;
+ s = cvar("cl_gunalign");
+ if(s != 1 && s != 2 && s != 4)
+ s = 3; // default value
+ --s;
+ switch(self.HookType)
+ {
+ default:
+ case ENT_CLIENT_HOOK:
+ vs = hook_shotorigin[s];
+ break;
+ case ENT_CLIENT_LGBEAM:
+ vs = electro_shotorigin[s];
+ break;
+ case ENT_CLIENT_GAUNTLET:
+ vs = gauntlet_shotorigin[s];
+ break;
}
- if(time < self.LGBeamKillTime)
+ if((self.owner.sv_entnum == player_localentnum - 1))
{
- s = cvar("cl_gunalign");
- if(s != 1 && s != 2 && s != 4)
- s = 3; // default value
- --s;
- vs = electro_shotorigin[s];
-
- if(self.sv_entnum == player_localentnum - 1)
+ switch(self.HookType)
{
- b = view_origin + view_forward * MAX_SHOT_DISTANCE;
- WarpZone_TraceLine(view_origin, b, MOVE_NORMAL, world);
- a = view_origin + view_forward * vs_x + view_right * -vs_y + view_up * vs_z;
+ default:
+ case ENT_CLIENT_HOOK:
+ a = view_origin + view_forward * vs_x + view_right * -vs_y + view_up * vs_z;
+ b = self.origin;
+ break;
+ case ENT_CLIENT_LGBEAM:
+ case ENT_CLIENT_GAUNTLET:
+ b = view_origin + view_forward * MAX_SHOT_DISTANCE;
+ 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;
}
- else
+ }
+ else
+ {
+ switch(self.HookType)
{
- a = self.LGBeamStart;
- b = self.LGBeamEnd;
+ default:
+ case ENT_CLIENT_HOOK:
+ a = self.velocity;
+ b = self.origin;
+ break;
+ case ENT_CLIENT_LGBEAM:
+ case ENT_CLIENT_GAUNTLET:
+ a = self.origin;
+ b = self.velocity;
+ break;
}
+ }
- tex = "particles/lgbeam";
- rgb = '1 1 1';
-
- Draw_GrapplingHook_trace_callback_tex = tex;
- Draw_GrapplingHook_trace_callback_rnd = random();
- WarpZone_TraceBox_ThroughZone(a, '0 0 0', '0 0 0', b, MOVE_NORMAL, world, world, Draw_GrapplingHook_trace_callback);
- Draw_GrapplingHook_trace_callback_tex = string_null;
+ t = GetPlayerColorForce(self.owner.sv_entnum);
- // helps the sound
- setorigin(self, a);
+ switch(self.HookType)
+ {
+ default:
+ case ENT_CLIENT_HOOK:
+ if(t == COLOR_TEAM1)
+ {
+ tex = "particles/hook_red";
+ rgb = '1 .3 .3';
+ }
+ else if(t == COLOR_TEAM2)
+ {
+ tex = "particles/hook_blue";
+ rgb = '.3 .3 1';
+ }
+ else if(t == COLOR_TEAM3)
+ {
+ tex = "particles/hook_yellow";
+ rgb = '1 1 .3';
+ }
+ else if(t == COLOR_TEAM4)
+ {
+ tex = "particles/hook_pink";
+ rgb = '1 .3 1';
+ }
+ else
+ {
+ tex = "particles/hook_green";
+ rgb = '.3 1 .3';
+ }
+ break;
+ case ENT_CLIENT_LGBEAM:
+ tex = "particles/lgbeam";
+ rgb = '1 1 1';
+ break;
+ case ENT_CLIENT_GAUNTLET:
+ tex = "particles/gauntletbeam";
+ rgb = '1 1 1';
+ break;
}
- if(time < self.GauntletBeamKillTime)
+ Draw_GrapplingHook_trace_callback_tex = tex;
+ Draw_GrapplingHook_trace_callback_rnd = random();
+ WarpZone_TraceBox_ThroughZone(a, '0 0 0', '0 0 0', b, MOVE_NOMONSTERS, world, world, Draw_GrapplingHook_trace_callback);
+ Draw_GrapplingHook_trace_callback_tex = string_null;
+
+ switch(self.HookType)
{
- s = cvar("cl_gunalign");
- if(s != 1 && s != 2 && s != 4)
- s = 3; // default value
- --s;
- vs = gauntlet_shotorigin[s];
+ default:
+ case ENT_CLIENT_HOOK:
+ self.angles = vectoangles(b - a);
+ break;
+ case ENT_CLIENT_LGBEAM:
+ case ENT_CLIENT_GAUNTLET:
+ break;
+ }
+}
- if(self.sv_entnum == player_localentnum - 1)
- {
- b = view_origin + view_forward * MAX_SHOT_DISTANCE;
- WarpZone_TraceLine(view_origin, b, MOVE_NORMAL, world);
- a = view_origin + view_forward * vs_x + view_right * -vs_y + view_up * vs_z;
- }
- else
- {
- a = self.GauntletBeamStart;
- b = self.GauntletBeamEnd;
- }
+void Remove_GrapplingHook()
+{
+ sound (self, CHAN_PROJECTILE, "misc/null.wav", VOL_BASE, ATTN_NORM);
+}
- tex = "particles/gauntletbeam";
- rgb = '1 1 1';
+void Ent_ReadHook(float bIsNew, float type)
+{
+ self.HookType = type;
- Draw_GrapplingHook_trace_callback_tex = tex;
- Draw_GrapplingHook_trace_callback_rnd = random();
- WarpZone_TraceBox_ThroughZone(a, '0 0 0', '0 0 0', b, MOVE_NORMAL, world, world, Draw_GrapplingHook_trace_callback);
- Draw_GrapplingHook_trace_callback_tex = string_null;
+ float sf;
+ sf = ReadByte();
- // helps the sound
- setorigin(self, a);
- }
+ self.HookSilent = (sf & 0x80);
+ self.iflags = IFLAG_VELOCITY;
- if(time < self.LGBeamKillTime && !self.LGBeamSilent)
- {
- if(!self.LGBeamSound)
- {
- sound (self, CHAN_PROJECTILE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTN_NORM);
- self.LGBeamSound = 1;
- }
- }
- else
+ InterpolateOrigin_Undo();
+
+ if(sf & 1)
{
- if(self.LGBeamSound)
- {
- sound (self, CHAN_PROJECTILE, "misc/null.wav", VOL_BASE, ATTN_NORM);
- self.LGBeamSound = 0;
- }
+ self.owner = playerslots[ReadByte() - 1];
}
-
- if(time < self.GauntletBeamKillTime && !self.GauntletBeamSilent)
+ if(sf & 2)
{
- if(!self.GauntletBeamSound)
- {
- sound (self, CHAN_PROJECTILE, "weapons/gauntletbeam_fly.wav", VOL_BASE, ATTN_NORM);
- self.GauntletBeamSound = 1;
- }
+ self.origin_x = ReadCoord();
+ self.origin_y = ReadCoord();
+ self.origin_z = ReadCoord();
+ setorigin(self, self.origin);
}
- else
+ if(sf & 4)
{
- if(self.GauntletBeamSound)
- {
- sound (self, CHAN_PROJECTILE, "misc/null.wav", VOL_BASE, ATTN_NORM);
- self.GauntletBeamSound = 0;
- }
+ self.velocity_x = ReadCoord();
+ self.velocity_y = ReadCoord();
+ self.velocity_z = ReadCoord();
}
-}
-
-void Net_GrapplingHook()
-{
- float i, t;
- vector start, end;
- entity p;
-
- i = ReadByte();
- t = ReadByte();
- end_x = ReadCoord();
- end_y = ReadCoord();
- end_z = ReadCoord();
- start_x = ReadCoord();
- start_y = ReadCoord();
- start_z = ReadCoord();
-
- if(i <= 0 || i >= 256) // not owned by a client
- return;
- --i;
- p = playerslots[i];
- if(!p)
- return;
+ InterpolateOrigin_Note();
- switch(t)
+ if(bIsNew)
{
- case 0: // hook beam
- p.HookKillTime = time + 0.1;
- p.HookStart = start;
- p.HookEnd = end;
- p.draw = Draw_GrapplingHook;
- break;
- case 1: // electro lgbeam
- p.LGBeamKillTime = time + 0.1;
- p.LGBeamStart = start;
- p.LGBeamEnd = end;
- p.LGBeamSilent = 0;
- p.draw = Draw_GrapplingHook;
- break;
- case 2: // silent electro lgbeam
- p.LGBeamKillTime = time + 0.1;
- p.LGBeamStart = start;
- p.LGBeamEnd = end;
- p.LGBeamSilent = 1;
- p.draw = Draw_GrapplingHook;
- break;
- case 3: // gauntlet beam
- p.GauntletBeamKillTime = time + 0.1;
- p.GauntletBeamStart = start;
- p.GauntletBeamEnd = end;
- p.GauntletBeamSilent = 0;
- p.draw = Draw_GrapplingHook;
- break;
+ self.draw = Draw_GrapplingHook;
+ self.entremove = Remove_GrapplingHook;
+
+ switch(self.HookType)
+ {
+ default:
+ case ENT_CLIENT_HOOK:
+ // for the model
+ setmodel(self, "models/hook.md3");
+ self.drawmask = MASK_NORMAL;
+ break;
+ case ENT_CLIENT_LGBEAM:
+ sound (self, CHAN_PROJECTILE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTN_NORM);
+ break;
+ case ENT_CLIENT_GAUNTLET:
+ sound (self, CHAN_PROJECTILE, "weapons/gauntletbeam_fly.wav", VOL_BASE, ATTN_NORM);
+ break;
+ }
}
}
{
precache_sound("weapons/lgbeam_fly.wav");
precache_sound("weapons/gauntletbeam_fly.wav");
+ precache_model("models/hook.md3");
}
+
+// TODO: hook: temporarily transform self.origin for drawing the model along warpzones!
self.hook_length = -1;
}
+.vector hook_start, hook_end;
+float GrapplingHookSend(entity to, float sf)
+{
+ WriteByte(MSG_ENTITY, ENT_CLIENT_HOOK);
+ sf = sf & 0x7F;
+ if(sound_allowed(MSG_BROADCAST, self.owner))
+ sf |= 0x80;
+ WriteByte(MSG_ENTITY, sf);
+ if(sf & 1)
+ {
+ WriteByte(MSG_ENTITY, num_for_edict(self.owner));
+ }
+ if(sf & 2)
+ {
+ WriteCoord(MSG_ENTITY, self.hook_start_x);
+ WriteCoord(MSG_ENTITY, self.hook_start_y);
+ WriteCoord(MSG_ENTITY, self.hook_start_z);
+ }
+ if(sf & 4)
+ {
+ WriteCoord(MSG_ENTITY, self.hook_end_x);
+ WriteCoord(MSG_ENTITY, self.hook_end_y);
+ WriteCoord(MSG_ENTITY, self.hook_end_z);
+ }
+ return TRUE;
+}
+
void GrapplingHookThink()
{
float spd, dist, minlength, pullspeed, ropestretch, ropeairfriction, rubberforce, newlength, rubberforce_overstretch, s;
makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0');
myorg = WarpZone_RefSys_TransformOrigin(self, self.owner, self.origin) + v_forward * (-9);
- // TODO turn into a csqc entity
- WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte(MSG_BROADCAST, TE_CSQC_BEAM);
- WriteByte(MSG_BROADCAST, num_for_edict(self.owner));
- WriteByte(MSG_BROADCAST, 0);
- WriteCoord(MSG_BROADCAST, myorg_x);
- WriteCoord(MSG_BROADCAST, myorg_y);
- WriteCoord(MSG_BROADCAST, myorg_z);
- WriteCoord(MSG_BROADCAST, org_x);
- WriteCoord(MSG_BROADCAST, org_y);
- WriteCoord(MSG_BROADCAST, org_z);
+ if(myorg != self.hook_start)
+ {
+ self.SendFlags |= 2;
+ self.hook_start = myorg;
+ }
+ if(org != self.hook_end)
+ {
+ self.SendFlags |= 4;
+ self.hook_end = org;
+ }
}
void GrapplingHookTouch (void)
missile.movetype = MOVETYPE_FLY;
PROJECTILE_MAKETRIGGER(missile);
- setmodel (missile, "models/hook.md3"); // precision set below
+ //setmodel (missile, "models/hook.md3"); // precision set below
setsize (missile, '-3 -3 -3', '3 3 3');
setorigin (missile, org);
missile.event_damage = GrapplingHook_Damage;
missile.takedamage = DAMAGE_AIM;
missile.damageforcescale = 0;
+
+ missile.hook_start = missile.hook_end = missile.origin;
+
+ Net_LinkEntity(missile, FALSE, 0, GrapplingHookSend);
}
// void GrapplingHookFrame()
CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO, FALSE); // no culling, it has sound
}
+.vector hook_start, hook_end;
+float lgbeam_send(entity to, float sf)
+{
+ WriteByte(MSG_ENTITY, ENT_CLIENT_LGBEAM);
+ sf = sf & 0x7F;
+ if(sound_allowed(MSG_BROADCAST, self.owner))
+ sf |= 0x80;
+ WriteByte(MSG_ENTITY, sf);
+ if(sf & 1)
+ {
+ WriteByte(MSG_ENTITY, num_for_edict(self.owner));
+ }
+ if(sf & 2)
+ {
+ WriteCoord(MSG_ENTITY, self.hook_start_x);
+ WriteCoord(MSG_ENTITY, self.hook_start_y);
+ WriteCoord(MSG_ENTITY, self.hook_start_z);
+ }
+ if(sf & 4)
+ {
+ WriteCoord(MSG_ENTITY, self.hook_end_x);
+ WriteCoord(MSG_ENTITY, self.hook_end_y);
+ WriteCoord(MSG_ENTITY, self.hook_end_z);
+ }
+ return TRUE;
+}
.entity lgbeam;
.float prevlgfire;
void lgbeam_think()
vecs = '0 0 0';
org = self.owner.origin + self.owner.view_ofs + v_forward * vecs_x + v_right * vecs_y + v_up * vecs_z;
- // TODO turn into a csqc entity
- WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte(MSG_BROADCAST, TE_CSQC_BEAM);
- WriteByte(MSG_BROADCAST, num_for_edict(self.owner));
- WriteByte(MSG_BROADCAST, sound_allowed(MSG_BROADCAST, self.owner) ? 1 : 2);
- WriteCoord(MSG_BROADCAST, trace_endpos_x);
- WriteCoord(MSG_BROADCAST, trace_endpos_y);
- WriteCoord(MSG_BROADCAST, trace_endpos_z);
- WriteCoord(MSG_BROADCAST, org_x);
- WriteCoord(MSG_BROADCAST, org_y);
- WriteCoord(MSG_BROADCAST, org_z);
+ if(org != self.hook_start)
+ {
+ self.SendFlags |= 2;
+ self.hook_start = org;
+ }
+ if(trace_endpos != self.hook_end)
+ {
+ self.SendFlags |= 4;
+ self.hook_end = trace_endpos;
+ }
}
// experimental lightning gun
beam.shot_spread = 0;
beam.bot_dodge = TRUE;
beam.bot_dodgerating = cvar("g_balance_electro_primary_damage");
+ Net_LinkEntity(beam, FALSE, 0, lgbeam_send);
oldself = self;
self = beam;
}
}
+.vector hook_start, hook_end;
+float gauntletbeam_send(entity to, float sf)
+{
+ WriteByte(MSG_ENTITY, ENT_CLIENT_GAUNTLET);
+ sf = sf & 0x7F;
+ if(sound_allowed(MSG_BROADCAST, self.owner))
+ sf |= 0x80;
+ WriteByte(MSG_ENTITY, sf);
+ if(sf & 1)
+ {
+ WriteByte(MSG_ENTITY, num_for_edict(self.owner));
+ }
+ if(sf & 2)
+ {
+ WriteCoord(MSG_ENTITY, self.hook_start_x);
+ WriteCoord(MSG_ENTITY, self.hook_start_y);
+ WriteCoord(MSG_ENTITY, self.hook_start_z);
+ }
+ if(sf & 4)
+ {
+ WriteCoord(MSG_ENTITY, self.hook_end_x);
+ WriteCoord(MSG_ENTITY, self.hook_end_y);
+ WriteCoord(MSG_ENTITY, self.hook_end_z);
+ }
+ return TRUE;
+}
.entity gauntletbeam;
.float prevgauntletfire;
void gauntletbeam_think()
vecs = '0 0 0';
org = self.owner.origin + self.owner.view_ofs + v_forward * vecs_x + v_right * vecs_y + v_up * vecs_z;
- // TODO turn into a csqc entity
- WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
- WriteByte(MSG_BROADCAST, TE_CSQC_BEAM);
- WriteByte(MSG_BROADCAST, num_for_edict(self.owner));
- WriteByte(MSG_BROADCAST, 3);
- WriteCoord(MSG_BROADCAST, trace_endpos_x);
- WriteCoord(MSG_BROADCAST, trace_endpos_y);
- WriteCoord(MSG_BROADCAST, trace_endpos_z);
- WriteCoord(MSG_BROADCAST, org_x);
- WriteCoord(MSG_BROADCAST, org_y);
- WriteCoord(MSG_BROADCAST, org_z);
+ if(org != self.hook_start)
+ {
+ self.SendFlags |= 2;
+ self.hook_start = org;
+ }
+ if(trace_endpos != self.hook_end)
+ {
+ self.SendFlags |= 4;
+ self.hook_end = trace_endpos;
+ }
}
// experimental gauntlet
beam.bot_dodge = TRUE;
beam.bot_dodgerating = cvar("g_balance_laser_primary_damage");
beam.cnt = issecondary;
+ Net_LinkEntity(beam, FALSE, 0, gauntletbeam_send);
oldself = self;
self = beam;