Code will post later, let me explain it first
Start at 2009
I saw the plugin “Advanced Weapon Tracers” by ConnorMcLeod about 4 years ago,
http://forums.alliedmods.net/showthread.php?t=62224
It is a good one, however, Attacker can't see his own tracer, Hence, I was planning to create tracer for first person view.
Trace line is drawn by TE_USERTRACER, which require the start point coordinate, so ask for help to get that point,
http://forums.alliedmods.net/showthread.php?t=100920 (including the picture of gunpoint)
Thanks for Arkshine’s help, I tried function “EngFunc_GetAttachment“. In order get attachment from view model (e.g. v_usp.mdl), I have to create an entity with view model, because it don’t exist at server side. But the result coordinate was incorrect.
Progress at 2013
Re-test the code recently, I found “EngFunc_GetAttachment“ only return correct coordinate when entity angle is 0,0,0, and dias’s post had the same conclusion,
http://forums.alliedmods.net/showthread.php?t=183288
Finally, I change my approach:
1. Get attachment coordinate offset with EngFunc_GetAttachment, when angle is 0,0,0
2. Covert the coordinate offset into distance and angle offset (pitch, yaw)
3. Add angle offset to Attacker view angle to get the angle of the gunpoint
4. Calculate the actual coordinate offset with angle and distance of gunpoint
It is tested and working fine. You may argue the yaw angle is an approximate result in my algorithm, I think that is close enough.
PHP Code:
//first person tracer
#include <amxmodx>
#include <cstrike>
#include <fakemeta_stocks>
#include <engine>
#include <hamsandwich>
#include <fakemeta_util>
#include <maths>
#define VERSION "1.0"
#define write_coord_f(%1) engfunc(EngFunc_WriteCoord,%1)
enum _:Coord_e { Float:x, Float:y, Float:z };
#define VectorAdd(%0,%1,%2) ( %2[ x ] = %0[ x ] + %1[ x ], %2[ y ] = %0[ y ] + %1[ y ], %2[ z ] = %0[ z ] + %1[ z ] )
#define VectorScale(%0,%1,%2) ( %2[ x ] = %1 * %0[ x ], %2[ y ] = %1 * %0[ y ], %2[ z ] = %1 * %0[ z ] )
#define max_weapon CSW_P90
#define min_weapon CSW_P228
#define weapon_size max_weapon-min_weapon+1
#define Degree2Radian 3.1415926/180.0
new Trie:g_tClassNames
new dummy_ent
new Float:offset_distance[weapon_size]
new Float:offset_pitch[weapon_size]
new Float:offset_yaw[weapon_size]
public plugin_precache()
{
for(new i=0;i<weapon_size;i++) offset_distance[i]=0;
dummy_ent=create_entity("worldspawn");
set_entity_visibility(dummy_ent,0);
g_tClassNames = TrieCreate()
RegisterHam(Ham_TraceAttack, "worldspawn", "TraceAttack", 1)
TrieSetCell(g_tClassNames, "worldspawn", 1)
RegisterHam(Ham_TraceAttack, "player", "TraceAttack", 1)
TrieSetCell(g_tClassNames, "player", 1)
register_forward(FM_Spawn, "Spawn", 1)
}
public plugin_init()
{
register_plugin("First Person Tracers", VERSION, "akn")
}
public plugin_end()
{
TrieDestroy(g_tClassNames)
}
public Spawn( iEnt )
{
if( pev_valid(iEnt) )
{
static szClassName[32]
pev(iEnt, pev_classname, szClassName, charsmax(szClassName))
if( !TrieKeyExists(g_tClassNames, szClassName) )
{
RegisterHam(Ham_TraceAttack, szClassName, "TraceAttack", 1)
TrieSetCell(g_tClassNames, szClassName, 1)
}
}
}
public TraceAttack(iEnt, iAttacker, Float:flDamage, Float:fDir[3], ptr, iDamageType)
{
new wid = get_user_weapon(iAttacker);
if( wid==CSW_KNIFE || wid==CSW_HEGRENADE || wid == CSW_SMOKEGRENADE || wid == CSW_FLASHBANG || wid == CSW_C4 ) return;
new Float:end[3]
get_tr2(ptr, TR_vecEndPos, end)
Draw_from_gunpoint(iAttacker,wid,end);
}
public Draw_from_gunpoint(id, wid, const Float:end[3])
{
new Float:p_ori[3];
new Float:p_offset[3];
new Float:p_angle[3];
new Float:w_attach[3];
new Float:d_vector[3];
new Float:xy;
new str[64];
entity_get_vector(id,EV_VEC_origin,p_ori);
if(get_distance_f(p_ori, end) < 50.0) return;
pev(id,pev_view_ofs,p_offset);
p_ori[2]+=p_offset[2] //p_ori now is player view point
wid=wid-min_weapon;
if(offset_distance[wid]==0)
{
Get_current_weapon_offset(wid); //calculate offset for each weapon
}
entity_get_vector(1,EV_VEC_v_angle,p_angle);
p_angle[0]=-p_angle[0];
p_angle[0]=floatmul(p_angle[0],Degree2Radian)
p_angle[1]=floatmul(p_angle[1],Degree2Radian)
p_angle[0]=floatadd(p_angle[0],offset_pitch[wid])
p_angle[1]=floatadd(p_angle[1],offset_yaw[wid]) //now p_angle is angle of the gunpoint from player view,
//for righthand tracer, do floatsub for offset_yaw
w_attach[2]=sin(p_angle[0])*offset_distance[wid];
xy=cos(p_angle[0])*offset_distance[wid];
w_attach[1]=sin(p_angle[1])*xy;
w_attach[0]=cos(p_angle[1])*xy; //now w_attach is coordinate offset from player view
p_ori[0]=floatadd(w_attach[0],p_ori[0]);
p_ori[1]=floatadd(w_attach[1],p_ori[1]);
p_ori[2]=floatadd(w_attach[2],p_ori[2]); //now p_ori is coordinate of gunpoint
d_vector[0]=end[0]-p_ori[0];
d_vector[1]=end[1]-p_ori[1];
d_vector[2]=p_ori[2]-end[2];
vector_to_angle(d_vector,p_angle);
angle_vector(p_angle, ANGLEVECTOR_FORWARD, d_vector);
VectorScale(d_vector, 3000 , d_vector);
Draw_tracer(p_ori,d_vector);
}
Get_current_weapon_offset(wid)
{
new classname[32];
pev(1, pev_viewmodel2, classname, sizeof(classname)-1);
entity_set_model(dummy_ent,classname); //set current view model
new Float:empty[3]={0.0, 0.0, 0.0}
new Float:w_attach[3]
new Float:w_angle[3]
entity_set_vector(dummy_ent,EV_VEC_origin,empty);
set_pev(dummy_ent, pev_angles, empty);
engfunc(EngFunc_GetAttachment, dummy_ent, 0, w_attach, w_angle); //get gunpoint coordinate offset when angle is 0,0,0, origin is 0,0,0
w_attach[2]-=1.0 //view model is 1.0 lower than player view point
offset_distance[wid]=get_distance_f(w_attach, empty); //distance offset from origin to gunpoint
offset_pitch[wid]=atan(w_attach[2]/floatsqroot(w_attach[0]*w_attach[0]+w_attach[1]*w_attach[1])) //pitch offset
offset_yaw[wid]=atan(w_attach[1]/w_attach[0]) //yaw offset
}
Draw_tracer(const Float:Source[3], const Float:Velocity[3])
{
message_begin( MSG_BROADCAST, SVC_TEMPENTITY );
write_byte( TE_USERTRACER );
write_coord_f( Source[0] );
write_coord_f( Source[1] );
write_coord_f( Source[2] );
write_coord_f( Velocity[0] );
write_coord_f( Velocity[1] );
write_coord_f( Velocity[2] );
write_byte( 5 ); //life
write_byte( 0 ); //color
write_byte( 3 ); //length
message_end();
}