//-----------------------------------------------------------------------------
// Purpose: Deal with input
//-----------------------------------------------------------------------------
void C_VGuiScreen::ClientThink( void )
{
int nButtonsChanged = m_nOldButtonState ^ m_nButtonState;
m_nOldButtonState = m_nButtonState;
// Debounced button codes for pressed/released
// UNDONE: Do we need auto-repeat?
m_nButtonPressed = nButtonsChanged & m_nButtonState; // The changed ones still down are "pressed"
m_nButtonReleased = nButtonsChanged & (~m_nButtonState); // The ones not down are "released"
BaseClass::ClientThink();
// FIXME: We should really be taking bob, shake, and roll into account
// but if we did, then all the inputs would be generated multiple times
// if the world was rendered multiple times (for things like water, etc.)
vgui::Panel *pPanel = m_PanelWrapper.GetPanel();
if (!pPanel)
return;
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if (!pLocalPlayer)
return;
// Generate a ray along the view direction
Vector vecEyePosition = pLocalPlayer->EyePosition();
QAngle viewAngles = pLocalPlayer->EyeAngles( );
// Compute cursor position...
Ray_t lookDir;
Vector endPos;
float u, v;
// Viewmodel attached screens that take input need to have a moving cursor
// Do a pick under the cursor as our selection
Vector viewDir;
AngleVectors( viewAngles, &viewDir );
VectorMA( vecEyePosition, 1000.0f, viewDir, endPos );
lookDir.Init( vecEyePosition, endPos );
if (!IntersectWithRay( lookDir, &u, &v, NULL ))
return;
if ( ((u < 0) || (v < 0) || (u > 1) || (v > 1)) && !m_bLoseThinkNextFrame)
return;
// This will cause our panel to grab all input!
g_pClientMode->ActivateInGameVGuiContext( pPanel );
// Convert (u,v) into (px,py)
int px = (int)(u * m_nPixelWidth + 0.5f);
int py = (int)(v * m_nPixelHeight + 0.5f);
// Generate mouse input commands
if ((px != m_nOldPx) || (py != m_nOldPy))
{
g_InputInternal->InternalCursorMoved( px, py );
m_nOldPx = px;
m_nOldPy = py;
}
if (m_nButtonPressed & IN_ATTACK)
{
g_InputInternal->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_PRESSED );
g_InputInternal->InternalMousePressed(MOUSE_LEFT);
}
if (m_nButtonPressed & IN_ATTACK2)
{
g_InputInternal->SetMouseCodeState( MOUSE_RIGHT, vgui::BUTTON_PRESSED );
g_InputInternal->InternalMousePressed( MOUSE_RIGHT );
}
if ( (m_nButtonReleased & IN_ATTACK) || m_bLoseThinkNextFrame) // for a button release on loosing focus
{
g_InputInternal->SetMouseCodeState( MOUSE_LEFT, vgui::BUTTON_RELEASED );
g_InputInternal->InternalMouseReleased( MOUSE_LEFT );
}
if (m_nButtonReleased & IN_ATTACK2)
{
g_InputInternal->SetMouseCodeState( MOUSE_RIGHT, vgui::BUTTON_RELEASED );
g_InputInternal->InternalMouseReleased( MOUSE_RIGHT );
}
if ( m_bLoseThinkNextFrame == true )
{
m_bLoseThinkNextFrame = false;
SetNextClientThink( CLIENT_THINK_NEVER );
}
g_pClientMode->DeactivateInGameVGuiContext( );
}
/*
* Tests whether the player entity ent is visible from the point origin.
*/
qboolean SV_IsPlayerVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, sharedEntity_t *ent, vec3_t diff) {
int i,contents_mask,goal_ent,viewer_clnum,ent_clnum,tries;
trace_t tr;
vec3_t start,end,dir,entangles,angles,temp,forward;
sharedEntity_t *viewer_ent;
client_t *viewer_cl, *ent_cl;
playerState_t *viewer_ps, *ent_ps;
float pitch;
viewer_clnum = frame->ps.clientNum; // get the client number of the viewer
ent_clnum = ent->s.clientNum; // get the client number of the other player
if (viewer_clnum == ent_clnum) { // in case the viewer is the player entity
return qtrue; // we don't need to hide us from ourselves
}
viewer_ps = &frame->ps;
ent_ps = SV_GameClientNum(ent_clnum);
if (viewer_ps->pm_type != PM_NORMAL) { // if the viewer is dead or spectating
return qtrue; // let every entity be visible
}
if (ent_ps->pm_type != PM_NORMAL || (ent->s.weapon == WP_NONE)) { // if the player entity is dead or spectating
return qtrue;
}
viewer_cl = svs.clients+viewer_clnum; // get the client of the viewer
ent_cl = svs.clients+ent_clnum; // get the client of the other player
// if (viewer_clnum > ent_clnum) { // if viewer_clnum > ent_clnum, we have already tested whether ent_clnum is able to see viewer_clnum.
// if (ent_cl->tracetimer[viewer_clnum] > sv.time) return qtrue; // and we could assume symmetry of SV_IsPlayerVisibleFromPoint
// }
if (viewer_cl->tracetimer[ent_clnum] > sv.time+MEMORY+10) { // if the sv.time has been reset
viewer_cl->tracetimer[ent_clnum] = sv.time; // reset the tracetimer
} else if (viewer_cl->tracetimer[ent_clnum] > (sv.time+MEMORY-10)) { // if we have recently seen this entity, we are lazy and assume it is still visible
// Com_Printf(va("client: %i, seen: %i\n", ent_clnum, viewer_cl->tracetimer[ent_clnum]));
return qtrue;
}
goal_ent = SV_NumForGentity(ent); // this might always be the same as ent_clnum
viewer_ent = SV_GentityNum(viewer_clnum);
contents_mask = CONTENTS_SOLID;// |CONTENTS_BODY will work for doors, but also for windows |CONTENTS_PLAYERCLIP|CONTENTS_SOLID|CONTENTS_MOVER|CONTENTS_PLAYERCLIP
// if (seen->v.movetype == MOVETYPE_PUSH ) { //don't cull doors and plats :(
// return false;
// }
// if (sv_antiwallhack.value == 1) //1 only check player models, 2 = check all ents
// if (strcmp(pr_strings + seen->v.classname, "player"))
// return qfalse;
// get camera origin (according to \cg_drawdebug 1)
start[0] = origin[0];
start[1] = origin[1];
start[2] = origin[2]+3.0f;
VectorCopy(viewer_ps->viewangles, angles);
AnglesNormalize180(angles);
pitch = angles[PITCH];
angles[PITCH] = 0;
angles[ROLL] = 0;
AngleVectors(angles, forward, NULL, NULL);
VectorScale(forward, (pitch/3.5f), temp);
VectorAdd( start, temp, start);
// if there is sufficient distance between viewer and player entity, check if player entity is within viewer's field of vision
VectorSubtract(ent->r.currentOrigin, start, dir);
// VectorAdd(ent->r.currentOrigin,dir,diff);// fill diff
VectorCopy(viewer_ent->s.pos.trBase,diff);// fill diff
vectoangles(dir, entangles);
dir[2]=0; // pretend, players are on the same level (the height should no be taken into account)
if (VectorLength(dir) > 1024) {// if it is not within close range (x,y-wise, not z-wise)
if (!InFieldOfVision(viewer_ps->viewangles, 60.f, entangles, ent_clnum)) {// If the player entity is not in the field of vision of the viewer
// Com_Printf( va("behind: %i vorg: %f,%f,%f vang: %f,%f,%f eorg: %f,%f,%f dir: %f,%f,%f eang: %f,%f,%f ent: %i\n", viewer_clnum,origin[0],origin[1],origin[2],viewer_ps->viewangles[0],viewer_ps->viewangles[1],viewer_ps->viewangles[2],ent->r.currentOrigin[0],ent->r.currentOrigin[1],ent->r.currentOrigin[2],dir[0],dir[1],dir[2],entangles[0],entangles[1],entangles[2],ent_clnum));
return qtrue; // if the player entity is behind the viewer, abstain from any computations (and transmit the entity to hear sounds)
// } else {
// Com_Printf( va("front: %i vorg: %f,%f,%f vang: %f,%f,%f eorg: %f,%f,%f dir: %f,%f,%f eang: %f,%f,%f ent: %i\n", viewer_clnum,origin[0],origin[1],origin[2],viewer_ps->viewangles[0],viewer_ps->viewangles[1],viewer_ps->viewangles[2],ent->r.currentOrigin[0],ent->r.currentOrigin[1],ent->r.currentOrigin[2],dir[0],dir[1],dir[2],entangles[0],entangles[1],entangles[2],ent_clnum));
}
}
// aim straight at the head of the entity from our eyes
end[0] = ent->r.currentOrigin[0];
end[1] = ent->r.currentOrigin[1];
end[2] = ent->r.currentOrigin[2]+ent->r.maxs[2];// "+3.0f" doesn't do it. "+ent->r.maxs[2]" is at the top of the BBox
VectorCopy(ent_ps->viewangles, angles);
AnglesNormalize180(angles);
pitch = angles[PITCH];
angles[PITCH] = 0;
angles[ROLL] = 0;
AngleVectors(angles, forward, NULL, NULL);
VectorScale(forward, (pitch/3.5f), temp);
VectorAdd( end, temp, end);
memset (&tr, 0, sizeof(tr));
//.........这里部分代码省略.........
/**
* @param[in] tile Tile to check (normally 0 - except in assembled maps)
* @param[in] traceLine The start and stop vectors of the trace
* @param[in] traceBox The box we shove through the world
* @param[in] headnode if < 0 we are in a leaf node
* @param[in] contentmask content flags the trace should stop at (see MASK_*)
* @param[in] brushrejects brushes the trace should ignore (see MASK_*)
* @param[in] origin center for rotating objects
* @param[in] angles current rotation status (in degrees) for rotating objects
* @param[in] rmaShift how much the object was shifted by the RMA process (needed for doors)
* @param[in] fraction The furthest distance needed to trace before we stop.
* @brief Handles offseting and rotation of the end points for moving and rotating entities
* @sa CM_BoxTrace
*/
trace_t CM_HintedTransformedBoxTrace (MapTile& tile, const Line& traceLine, const AABB& traceBox, const int headnode, const int contentmask, const int brushrejects, const vec3_t origin, const vec3_t angles, const vec3_t rmaShift, const float fraction)
{
vec3_t start_l, end_l;
vec3_t forward, right, up;
vec3_t temp;
bool rotated;
/* subtract origin offset */
VectorSubtract(traceLine.start, origin, start_l);
VectorSubtract(traceLine.stop, origin, end_l);
/* rotate start and end into the models frame of reference */
if (headnode != tile.box_headnode && VectorNotEmpty(angles)) {
rotated = true;
} else {
rotated = false;
}
if (rotated) {
AngleVectors(angles, forward, right, up);
VectorCopy(start_l, temp);
start_l[0] = DotProduct(temp, forward);
start_l[1] = -DotProduct(temp, right);
start_l[2] = DotProduct(temp, up);
VectorCopy(end_l, temp);
end_l[0] = DotProduct(temp, forward);
end_l[1] = -DotProduct(temp, right);
end_l[2] = DotProduct(temp, up);
}
/* When tracing through a model, we want to use the nodes, planes etc. as calculated by ufo2map.
* But nodes and planes have been shifted in case of an RMA. At least for doors we need to undo the shift. */
if (VectorNotEmpty(origin)) { /* only doors seem to have their origin set */
VectorAdd(start_l, rmaShift, start_l); /* undo the shift */
VectorAdd(end_l, rmaShift, end_l);
}
/* sweep the box through the model */
boxtrace_t traceData;
traceData.init(&tile, contentmask, brushrejects, fraction);
traceData.setLineAndBox(Line(start_l, end_l), traceBox);
trace_t trace = TR_BoxTrace(traceData, Line(start_l, end_l), traceBox, headnode, fraction);
trace.mapTile = tile.idx;
if (rotated && trace.fraction != 1.0) {
vec3_t a;
/** @todo figure out how to do this with existing angles */
VectorNegate(angles, a);
AngleVectors(a, forward, right, up);
VectorCopy(trace.plane.normal, temp);
trace.plane.normal[0] = DotProduct(temp, forward);
trace.plane.normal[1] = -DotProduct(temp, right);
trace.plane.normal[2] = DotProduct(temp, up);
}
VectorInterpolation(traceLine.start, traceLine.stop, trace.fraction, trace.endpos);
return trace;
}
//-----------------------------------------------------------------------------
//
// Look for vgui screens, returns true if it found one ...
//
//-----------------------------------------------------------------------------
C_BaseEntity *FindNearbyVguiScreen( const Vector &viewPosition, const QAngle &viewAngle, int nTeam )
{
if ( IsX360() )
{
// X360TBD: Turn this on if feature actually used
return NULL;
}
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
Assert( pLocalPlayer );
if ( !pLocalPlayer )
return NULL;
// Get the view direction...
Vector lookDir;
AngleVectors( viewAngle, &lookDir );
// Create a ray used for raytracing
Vector lookEnd;
VectorMA( viewPosition, 2.0f * VGUI_SCREEN_MODE_RADIUS, lookDir, lookEnd );
Ray_t lookRay;
lookRay.Init( viewPosition, lookEnd );
// Look for vgui screens that are close to the player
CVGuiScreenEnumerator localScreens;
partition->EnumerateElementsInSphere( PARTITION_CLIENT_NON_STATIC_EDICTS, viewPosition, VGUI_SCREEN_MODE_RADIUS, false, &localScreens );
Vector vecOut, vecViewDelta;
float flBestDist = 2.0f;
C_VGuiScreen *pBestScreen = NULL;
for (int i = localScreens.GetScreenCount(); --i >= 0; )
{
C_VGuiScreen *pScreen = localScreens.GetVGuiScreen(i);
if ( pScreen->IsAttachedToViewModel() )
continue;
// Don't bother with screens I'm behind...
// Hax - don't cancel backfacing with viewmodel attached screens.
// we can get prediction bugs that make us backfacing for one frame and
// it resets the mouse position if we lose focus.
if ( pScreen->IsBackfacing(viewPosition) )
continue;
// Don't bother with screens that are turned off
if (!pScreen->IsActive())
continue;
// FIXME: Should this maybe go into a derived class of some sort?
// Don't bother with screens on the wrong team
if (!pScreen->IsVisibleToTeam(nTeam))
continue;
if ( !pScreen->AcceptsInput() )
continue;
if ( pScreen->IsInputOnlyToOwner() && pScreen->GetPlayerOwner() != pLocalPlayer )
continue;
// Test perpendicular distance from the screen...
pScreen->GetVectors( NULL, NULL, &vecOut );
VectorSubtract( viewPosition, pScreen->GetAbsOrigin(), vecViewDelta );
float flPerpDist = DotProduct(vecViewDelta, vecOut);
if ( (flPerpDist < 0) || (flPerpDist > VGUI_SCREEN_MODE_RADIUS) )
continue;
// Perform a raycast to see where in barycentric coordinates the ray hits
// the viewscreen; if it doesn't hit it, you're not in the mode
float u, v, t;
if (!pScreen->IntersectWithRay( lookRay, &u, &v, &t ))
continue;
// Barycentric test
if ((u < 0) || (v < 0) || (u > 1) || (v > 1))
continue;
if ( t < flBestDist )
{
flBestDist = t;
pBestScreen = pScreen;
}
}
return pBestScreen;
}
请发表评论