client effect reorganization and akimbo support (demo in socom), pmove slight reorganization

This commit is contained in:
Chris Dawalt 2021-08-15 06:47:27 -04:00
parent 6ba566a616
commit 0c9b8537e4
17 changed files with 827 additions and 650 deletions

View file

@ -42,9 +42,6 @@ ClientGame_PreDraw(void)
} }
void
Player_HandleWeaponModel(base_player pp, float thirdperson);
// Happens right before the CSQC_RenderScene call seen in Nuclide's client/draw.qc. // Happens right before the CSQC_RenderScene call seen in Nuclide's client/draw.qc.
// PostDraw further down is too late. // PostDraw further down is too late.
// NOT A NUCLIDE EVENT - called by the end of ViewModel_ApplyBob (viewmodel.qc) so that // NOT A NUCLIDE EVENT - called by the end of ViewModel_ApplyBob (viewmodel.qc) so that
@ -54,10 +51,10 @@ Custom_LatePreDraw(void)
{ {
player pl = (player)pSeat->m_ePlayer; player pl = (player)pSeat->m_ePlayer;
Player_DrawViewModelCustom(pl); TS_View_DrawCustom(pl);
int thirdperson = (autocvar_cl_thirdperson == TRUE || pl.entnum != player_localentnum); int thirdperson = (autocvar_cl_thirdperson == TRUE || pl.entnum != player_localentnum);
Player_DrawViewModelExtraEffects(pl, thirdperson); TS_View_DrawExtraEffects(pl, thirdperson);
} }

View file

@ -10,8 +10,12 @@ class CTSShellEject{
void(void) CTSShellEject; void(void) CTSShellEject;
static void(void) precache; static void(void) precache;
static CTSShellEject(int arg_iShellEjectType) generate; static CTSShellEject(int arg_iShellEjectType) generate;
static CTSShellEject(int arg_iShellEjectType) generateForViewmodel; static void(int arg_iShellEjectType, int arg_akimboChoice) generateForViewModelAkimbo;
static CTSShellEject(int arg_iShellEjectType, int arg_attachment1, int arg_attachment2) generateForViewModel_attachments;
static CTSShellEject(int arg_iShellEjectType, vector arg_vOrigin, vector arg_vDir) generate2; static CTSShellEject(int arg_iShellEjectType, vector arg_vOrigin, vector arg_vDir) generate2;
virtual void(void) touch; virtual void(void) touch;
}; };

View file

@ -51,15 +51,21 @@ CTSShellEject::generate(int arg_iShellEjectType){
//setmodel(eShell, "models/shell.mdl"); //setmodel(eShell, "models/shell.mdl");
shellejectdata_t* mySED = ary_shellEjectData[eShell.iShellEjectType]; shellejectdata_t* mySED = ary_shellEjectData[eShell.iShellEjectType];
setmodel(eShell, (*mySED).sModelPath); setmodel(eShell, (*mySED).sModelPath);
//setmodel(eShell, "models/powerup.mdl");
eShell.solid = SOLID_BBOX; eShell.solid = SOLID_BBOX;
eShell.movetype = MOVETYPE_BOUNCE; eShell.movetype = MOVETYPE_BOUNCE;
eShell.drawmask = MASK_ENGINE; eShell.drawmask = MASK_ENGINE;
eShell.angles = [pSeat->m_eViewModel.angles[0], pSeat->m_eViewModel.angles[1], 0]; eShell.angles = [pSeat->m_eViewModel.angles[0], pSeat->m_eViewModel.angles[1], 0];
//player pl = (player) pSeat->m_ePlayer;
//printfline("What?? (%.2f %.2f %.2f), (%.2f %.2f %.2f)", pSeat->m_vecPredictedVelocity.x, pSeat->m_vecPredictedVelocity.y, pSeat->m_vecPredictedVelocity.z, pl.velocity.x, pl.velocity.y, pl.velocity.z);
eShell.velocity = pSeat->m_vecPredictedVelocity; eShell.velocity = pSeat->m_vecPredictedVelocity;
makevectors(pSeat->m_eViewModel.angles); makevectors(pSeat->m_eViewModel.angles);
eShell.velocity += (v_forward * 0); eShell.velocity += (v_forward * 20);
eShell.velocity += (v_right * 80); eShell.velocity += (v_right * 80);
eShell.velocity += (v_up * 100); eShell.velocity += (v_up * 100);
@ -78,8 +84,29 @@ CTSShellEject::generate(int arg_iShellEjectType){
// The practical constructor, manually call this after spawning. // The practical constructor, manually call this after spawning.
// NOTE: assumes pSeat->m_eViewModel is valid for involving in determining // NOTE: assumes pSeat->m_eViewModel is valid for involving in determining
// origin, angles, velocity // origin, angles, velocity
void
CTSShellEject::generateForViewModelAkimbo(int arg_iShellEjectType, int arg_akimboChoice)
{
// this means, a singular weapon, not akimbo. Important distinction for
// attachment points to pick
if(arg_akimboChoice == BITS_AKIMBOCHOICE_NONE){
CTSShellEject::generateForViewModel_attachments(arg_iShellEjectType, 0, 3);
}
if(arg_akimboChoice & BITS_AKIMBOCHOICE_LEFT){
CTSShellEject::generateForViewModel_attachments(arg_iShellEjectType, 2, 3);
}
if(arg_akimboChoice & BITS_AKIMBOCHOICE_RIGHT){
CTSShellEject::generateForViewModel_attachments(arg_iShellEjectType, 0, 1);
}
}
// with the attachments to be used to help get a better idea of forwards/backwards/sideways
// for the current weapon, create a shell.
CTSShellEject CTSShellEject
CTSShellEject::generateForViewmodel(int arg_iShellEjectType) CTSShellEject::generateForViewModel_attachments(int arg_iShellEjectType, int arg_attachment1, int arg_attachment2)
{ {
// give me an origin! // give me an origin!
CTSShellEject eShell = CTSShellEject::generate(arg_iShellEjectType); CTSShellEject eShell = CTSShellEject::generate(arg_iShellEjectType);
@ -123,8 +150,8 @@ CTSShellEject::generateForViewmodel(int arg_iShellEjectType)
vector angGun; vector angGun;
gunpos = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 0i); gunpos = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + arg_attachment1);
gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 3i); gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + arg_attachment2);
dirGun = normalize(gunpos_tempEnd - gunpos); dirGun = normalize(gunpos_tempEnd - gunpos);
angGun = vectoangles(dirGun); angGun = vectoangles(dirGun);
makevectors(angGun); makevectors(angGun);
@ -136,6 +163,14 @@ CTSShellEject::generateForViewmodel(int arg_iShellEjectType)
// ALSO, TODO: Check for being akimbo and per weapon. oh dear... we need a paramter for what // ALSO, TODO: Check for being akimbo and per weapon. oh dear... we need a paramter for what
// weapon, left or right to use then (LEFT always a default, even if non-akimbo is ok too), // weapon, left or right to use then (LEFT always a default, even if non-akimbo is ok too),
// see weapons for that AKIMBOCHOICE thing // see weapons for that AKIMBOCHOICE thing
/*
player pl = (player) pSeat->m_ePlayer;
if(arg_iShellEjectType == SHELLEJECT_ID::SHOTGUN_BLUE){
vOrigin = gunpos + pl.velocity*input_timelength;
}else{
vOrigin = gunpos;
}
*/
vOrigin = gunpos; vOrigin = gunpos;
vOrigin += v_forward * -3.5; vOrigin += v_forward * -3.5;
vOrigin += v_up * -0.02; vOrigin += v_up * -0.02;
@ -144,9 +179,28 @@ CTSShellEject::generateForViewmodel(int arg_iShellEjectType)
setorigin(eShell, vOrigin); setorigin(eShell, vOrigin);
/*
// TEST. Any difference between the vecPredictedOrigin and current origin at this point of the call?
player pl = (player) pSeat->m_ePlayer;
vector vDelta;
vDelta = pSeat->m_vecPredictedOrigin - pl.origin;
if(vlen(vDelta) > 0.001){
printfline("Difference? %.2f %.2f %.2f", vDelta.x, vDelta.y, vDelta.z);
}
*/
/*
player pl = (player) pSeat->m_ePlayer;
float vel = vlen(pl.velocity);
float vel2 = vlen(pl.basevelocity);
if(vel > 0.001 || vel2 > 0.001){
printfline("vel: %.2f basevel: %.2f", vel, vel2);
}
*/
return eShell; return eShell;
} }
// TODO. For playermodels as seen in thirdperson (local one) or looking at other players // TODO. For playermodels as seen in thirdperson (local one) or looking at other players
// that called for shell ejecting. // that called for shell ejecting.
CTSShellEject CTSShellEject

View file

@ -1,3 +0,0 @@
void Player_DrawViewModelCustom(player pl);
void Player_DrawViewModelExtraEffects(player pl, int thirdperson);

View file

@ -164,8 +164,6 @@ Player_DestroyWeaponModel(entity pp)
} }
//TAGGG - NOTE! In case it ever matters, parameter "base_player pl" renamed to "base_player pp". //TAGGG - NOTE! In case it ever matters, parameter "base_player pl" renamed to "base_player pp".
// Doubt it ever should, no idea if FTE would complain about a discrepency between prototype and // Doubt it ever should, no idea if FTE would complain about a discrepency between prototype and
// implementation parameter names if that ever happened. // implementation parameter names if that ever happened.
@ -184,479 +182,3 @@ Player_PreDraw(base_player pp, int thirdperson)
} }
// NEW, helper method, not called by Nuclide
// Does the same Nuclide script to draw the normal viewmodel at viewzooms between 0.5
// and 1.0, Nuclide skips drawing it on any zoom below 1.0 unlike original TS.
// Also, handles drawing the akimbo muzzle flash.
void
Player_DrawViewModelCustom(player pl){
// Same forbidding conditions from Nuclide's src/client/view.qc
if (pl.health <= 0) {
return;
}
if (cvar("r_drawviewmodel") == 0 || autocvar_cl_thirdperson == TRUE) {
return;
}
// Nuclide does not draw the viewmodel at any zoom other than 1.0 (unmodified).
// So, this draws the viewmodel the othertimes TS did, in lower zoom choices.
// At viewzoom 0.5 and above, the scope graphic is not drawn so it is safe to
// do this.
if(pl.viewzoom >= 0.5){
entity m_eViewModel = pSeat->m_eViewModel;
entity m_eMuzzleflash = pSeat->m_eMuzzleflash;
entity m_eMuzzleflashAkimbo = pSeatLocal->m_eMuzzleflashAkimbo;
// (only re-do the default Nuclide viewmodel + normal muzzleflash script if we have reason
// to believe Nuclide didn't, i.e., a zoom under 1.0)
if(pl.viewzoom < 1.0){
// CLONE OF NUCLIDE SCRIPT
/////////////////////////////////////////////////////////////////////////////
if (m_eMuzzleflash.alpha > 0.0f) {
makevectors(getproperty(VF_ANGLES));
m_eMuzzleflash.origin = gettaginfo(m_eViewModel, m_eMuzzleflash.skin);
m_eMuzzleflash.angles = m_eViewModel.angles;
m_eMuzzleflash.angles[2] += (random() * 10) - 5;
/*dynamiclight_add(pSeat->m_vecPredictedOrigin + (v_forward * 32), 400 * m_eMuzzleflash.alpha, [1,0.45,0]);*/
setorigin(m_eMuzzleflash, m_eMuzzleflash.origin);
addentity(m_eMuzzleflash);
}
setorigin(m_eViewModel, m_eViewModel.origin);
addentity(m_eViewModel);
/////////////////////////////////////////////////////////////////////////////
}
// Regardless of being 1.0 or not (Nuclide certainly isn't doing this),
// handle the other muzzleflash too
if (m_eMuzzleflashAkimbo.alpha > 0.0f) {
makevectors(getproperty(VF_ANGLES));
m_eMuzzleflashAkimbo.origin = gettaginfo(m_eViewModel, m_eMuzzleflashAkimbo.skin);
m_eMuzzleflashAkimbo.angles = m_eViewModel.angles;
m_eMuzzleflashAkimbo.angles[2] += (random() * 10) - 5;
/*dynamiclight_add(pSeat->m_vecPredictedOrigin + (v_forward * 32), 400 * m_eMuzzleflashAkimbo.alpha, [1,0.45,0]);*/
setorigin(m_eMuzzleflashAkimbo, m_eMuzzleflashAkimbo.origin);
addentity(m_eMuzzleflashAkimbo);
}
}
}
// another helper method
// Draw the lasersight, and flashlight effects
void
Player_DrawViewModelExtraEffects(player pl, int thirdperson)
{
//int thirdperson = (autocvar_cl_thirdperson == TRUE || this.entnum != player_localentnum);
//base_player pp = (base_player)this;
BOOL canRenderFlashlight = FALSE;
BOOL canRenderLaserSight = FALSE;
//player pl = (player)pp;
//we're going to use the buyopts of our current weapon + the one actually turned on, yah?
// DEBUG: printouts about the other player.
// Start a server with over 1 max players allowed in one window,
// connect to it in another window. Boom, read printouts.
/*
if(entnum != player_localentnum){
// so other player's "pl.weaponEquippedID" are not sent over to our clientside copies of them... I guess?
// At least that's not confusing.
printfline("It is I, the other player! What do I have? %i weapon count: %i", pl.weaponEquippedID, pl.ary_myWeapons_softMax);
}
*/
if(pl.inventoryEquippedIndex != -1){
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
if(dynaRef.weaponTypeID == WEAPONDATA_TYPEID_GUN || dynaRef.weaponTypeID == WEAPONDATA_TYPEID_IRONSIGHT){
weapondata_basic_t* basePRef = pl.getEquippedWeaponData();
weapondata_basic_t baseRef = *basePRef;
// We must have the flashlight bit on, AND support it on our weapon, AND it be a possibility according to weapon data.
int legalBuyOpts_on = (dynaRef.iBitsUpgrade_on & (dynaRef.iBitsUpgrade & baseRef.iBitsUpgrade));
if(legalBuyOpts_on & BITS_WEAPONOPT_FLASHLIGHT){
canRenderFlashlight = TRUE;
}
if(legalBuyOpts_on & BITS_WEAPONOPT_LASERSIGHT){
canRenderLaserSight = TRUE;
}
}///END OF _GUN or _IRONSIGHT type checks
}//END OF weaponEquippedID check
vector posView;
vector angView;
//for glock it is 40.
//vector gunpos = gettaginfo(pSeat->eViewModel, 33);
float daDrawAlphahz = 1.0;
const vector lasColor = [1.0, 0, 0];
const vector fsize = [2,2];
const vector fsizeDot = [18,18];
const vector fsizeFlashlightMuzzleGlow = [3, 3];
vector flashPos;
vector gunpos;
vector gunpos2 = [0,0,0];
vector gunpos_tempEnd;
vector dirGun = [0,0,0];
vector dirGun2 = [0,0,0];
vector angGun = [0,0,0];
vector angGun2 = [0,0,0];
//TAGGG - IMPORTANT NOTE!!!
// BEWARE "view_angles", it is a client global (what a confusing term), meaning it pertains only to THIS
// client (local player), no matter what player is being rendered by this call.
// wait... shouldn't we do the third-person check for the flash-light check above too?
BOOL canDrawAkimboLaser = FALSE;
pl.recentLaserHitPosSet = TRUE;
// TAGGG - QUESTION: Is it better to use the bool "thirdperson"
// (see check below involving cl_thirdperson)
// OR a raw "pl.entnum != player_localentnum" check?
// The former is a more concrete check for, "Am I the local player
// looking in 1st person, yes or no?".
// The latter will still treat the player being the local player the
// same as firstperson, even if the local player is forcing themselves
// to be viewed in third person.
// I am inclined to think the former is the better choice, but
// valve/src/client/flashlight.qc uses the latter way. Why?
// thirdperson
// True IF (autocvar_cl_thirdperson == TRUE || this.entnum != player_localentnum)
// False IF (autocvar_cl_thirdperson == FALSE && this.entnum == player_localentnum)
if(!thirdperson){
//TAGGG - Old way!
//posView = getproperty(VF_ORIGIN) + [0,0,-8];
//angView = getproperty(VF_CL_VIEWANGLES);
posView = pSeat->m_vecPredictedOrigin + [0,0,-8];
angView = view_angles;
// CHECK: is "getproperty(VF_CL_VIEWANGLES)" always the same as "view_angles"?
if(!pl.weaponEquippedAkimbo){
// first-person and this is the local player?
// We can get more specific info from the visible viewmodel, do so!
gunpos = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 0i);
gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 3i);
// should shell casings come from a bit behind the firing point here when needed?
//gunpos += v_right * 0.8; //why is this 'up'??
// not this one maybe... what the hell is this direction at all.
//gunpos += v_forward * -1.8;
dirGun = normalize(gunpos_tempEnd - gunpos);
angGun = vectoangles(dirGun);
}else{
canDrawAkimboLaser = TRUE;
gunpos = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 0i);
gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 1i);
dirGun = normalize(gunpos_tempEnd - gunpos);
angGun = vectoangles(dirGun);
gunpos2 = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 2i);
gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 3i);
dirGun2 = normalize(gunpos_tempEnd - gunpos2);
angGun2 = vectoangles(dirGun2);
}
}else{
posView = pl.origin + pl.view_ofs;
angView = [pl.pitch, pl.angles[1], pl.angles[2]];
gunpos = posView;
angGun = angView;
}
if(canRenderLaserSight && pl.entnum == player_localentnum){
// The lasersight displays a number of distance to show how long the laser goes.
//makevectors(view_angles);
makevectors(angView);
traceline(posView, posView + v_forward * 2048*4, MOVE_HITMODEL, pl);
pl.recentLaserDistanceDisplay = (int)(vlen(trace_endpos - posView) / 40 );
}
// DEBUG!!!
//canRenderLaserSight = TRUE;
//canRenderFlashlight = TRUE;
if(canRenderFlashlight){
//TAGGG - FLASHLIGHT STUFF HERE..
// oh wait a comment above already said that
// HOWEVER... in TS flashlights have a range limit. Up to so far they have max brightness,
// then it lowers with a bit of range, then it's nothing.
////////////////////////////////////////////////////////////////////////////////////
int flashlightRangeMax = 1070;
float flashlightBrightnessFactor = 1.0;
float rangeDimPriorStart = 170; //rangeMax minus this is where I start dimming.
makevectors(angView);
traceline(posView, posView + (v_forward * flashlightRangeMax), MOVE_NORMAL, pl);
int traceDist = trace_fraction * flashlightRangeMax;
//printfline("%.2f %d",, trace_fraction, trace_inopen);
//TODO - not here but elsewhere, draw the muzzle flashlight effect, some sprite on that gun attachment where am uzzleflash would go should do it.
// ALSO, go ahead and use this line trace to tell the lasersight how far the laser went.
// And just draw the lasersight (and dot if necessary), IF this render is for the local player.
// If not the local player, a slightly larger red dot (actual sprite) goes at the point the
// player is looking, likely not influenced by animations / view-model stuff.
if(trace_fraction == 1.0){
//uh-oh.
flashlightBrightnessFactor = 0;
}if(traceDist >= flashlightRangeMax - rangeDimPriorStart){
//the flashlight gets dimmer the further it is at this point.
// rangeDimPriorStart from the end: max bright still.
// very end: 0% bright.
flashlightBrightnessFactor = (-flashlightRangeMax * (trace_fraction + -1)) / rangeDimPriorStart;
}
if(flashlightBrightnessFactor > 0){
if (serverkeyfloat("*bspversion") == BSPVER_HL) {
dynamiclight_add(trace_endpos + (v_forward * -2), 128 * flashlightBrightnessFactor, [1,1,1]);
} else {
float p = dynamiclight_add(posView, 512 * flashlightBrightnessFactor, [1,1,1], 0, "textures/flashlight");
dynamiclight_set(p, LFIELD_ANGLES, angView);
dynamiclight_set(p, LFIELD_FLAGS, 3);
}
}//END OF brightness check
}//END OF "flashlight is on" criteria
if(canRenderLaserSight || canRenderFlashlight){
// TRY IT SOMEHOW? RF_DEPTHHACK
//pSeat->m_eViewModel.renderflags = RF_DEPTHHACK;
//if (alpha <= 0.0f) {
// return;
//}
makevectors(angGun);
//rotatevectorsbyangle( [-0.42, 0.75, 0] );
//rotatevectorsbyangle( [-0.52, 0.85, 0] );
rotatevectorsbyangle( [-0.45, 0.27, 0] );
flashPos = gunpos + v_up * -0.08 + v_right * 0.06;
vector shortForwardEnd = gunpos;
shortForwardEnd += v_forward * -1;
shortForwardEnd += v_up * (0.22); //why is this 'up'??
shortForwardEnd += v_right * (0.35); //why is this 'up'??
//makevectors(m_vecAngle); really now
//makevectors(input_angles); //maybe this if we need to do this.
// v_up * 2. or.. 1.6, for size [3,3] at least.
// for size [5,5], we need v_up*3, v_right*2. I DONT KNOW.
// for size [2, 2], we want v_up*5, v_right*5. Go figure that one out.
// Keep in mind, the global vectors (v_up, etc.) are set to the orientation of the recently received
// viewmodel attachment (gettaginfo above). Don't 'makevectors' at all to simply rely on that.
// ...unfortunately the orientation we get back is not great either. oh well.
// NEW TEST. Can we even get a straight line from the player's center to the gunpos?
traceline(posView, shortForwardEnd, FALSE, pl);
if(trace_fraction >= 1.0){
//woohoo!
traceline(shortForwardEnd, shortForwardEnd + v_forward * 1024, MOVE_HITMODEL, pl);
// other places care about this.
if (pl.entnum == player_localentnum) {
pl.recentLaserHitPos = trace_endpos;
}
if(canRenderLaserSight){
// In original TS, the 'laster' does not render for the local player if they are
// in thirdperson to see their own playermodel. I... don't really understand that,
// feels like a mistake. The lasers from other players are visible.
// TAGGG - TODO - SUPER LOW PRIORITY
// Lasers only for not in 3rd person and local player.
// Could work for the third-person model or other players if there were a way to determine
// the muzzle-end point for player models. Unsure if that is possible.
// To see it in third-person anyway, just change the "!thirdperson" condition below
// to "TRUE"; always do it. There is no separate place that does only first-person lasers
// to worry about being redundant with.
// Note the laser will try to face the direction the player model is, which may not
// necessarily be where the player is looking, although that same issue would come up
// with firing anyway; you would look like you're firing sideways anyway, this would be
// just as "off".
if (!thirdperson) {
//makevectors(view_angles); //??? it seems we do not need this perhaps...
// IN SHORT, we're riding the prior "makevectors(angGun);".
R_BeginPolygon("sprites/laserbeam.spr_0.tga", 1, 0);
R_PolygonVertex(gunpos + v_right * fsize[0] - v_up * fsize[1], [1,1], lasColor, daDrawAlphahz);
R_PolygonVertex(gunpos - v_right * fsize[0] - v_up * fsize[1], [0,1], lasColor, daDrawAlphahz);
R_PolygonVertex(trace_endpos - v_right * fsize[0] + v_up * fsize[1], [0,0], lasColor, daDrawAlphahz);
R_PolygonVertex(trace_endpos + v_right * fsize[0] + v_up * fsize[1], [1,0], lasColor, daDrawAlphahz);
R_EndPolygon();
}
// Draw the laser sprite effect at where the laser is hitting, but ONLY for every other player
// except this one. That's because, for the local player, we already are drawing the laserdot
// projected onto the screen in the HUD logic.
if (pl.entnum != player_localentnum) {
makevectors(angView);
trace_endpos += trace_plane_normal * fsizeDot[0]/6;
R_BeginPolygon("sprites/laserdot.spr_0.tga", 1, 0);
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] - v_up * fsizeDot[1], [1,1], lasColor, 0.80f);
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] - v_up * fsizeDot[1], [0,1], lasColor, 0.80f);
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] + v_up * fsizeDot[1], [0,0], lasColor, 0.80f);
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] + v_up * fsizeDot[1], [1,0], lasColor, 0.80f);
R_EndPolygon();
}
}//END OF canRenderLaserSight
// glow effect on top of the gun muzzle while the flashlight is on?
if(canRenderFlashlight){
if(!thirdperson){
makevectors(angView);
//trace_endpos += trace_plane_normal * fsizeDot[0]/6;
R_BeginPolygon("sprites/glow02.spr_0.tga", 1, 0);
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [1,1], [1,1,1], 0.45f);
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [0,1], [1,1,1], 0.45f);
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [0,0], [1,1,1], 0.45f);
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [1,0], [1,1,1], 0.45f);
R_EndPolygon();
}
}
}//END OF trace pre-check
// This requires the current weapon to be akimbo, player is in first person,
// and the local player is being rendered.
if(canDrawAkimboLaser){
// test for the 2nd gun's laser too then.
// makevectors(theDir);
makevectors(angGun2);
rotatevectorsbyangle( [-0.45, 0.27, 0] );
//gunpos2 += v_forward * -18;
flashPos = gunpos2 + v_up * -0.08 + v_right * 0.06;
shortForwardEnd = gunpos2;
shortForwardEnd += v_forward * -1;
shortForwardEnd += v_up * (0.22); //why is this 'up'??
shortForwardEnd += -v_right * (0.35); //why is this 'up'??
traceline(posView, shortForwardEnd, FALSE, pl);
if(trace_fraction >= 1.0){
traceline(shortForwardEnd, shortForwardEnd + v_forward * 1024, MOVE_HITMODEL, pl);
// other places care about this.
if (pl.entnum == player_localentnum) {
pl.recentLaserHitPos2 = trace_endpos;
}
if(canRenderLaserSight){
if (!thirdperson) {
// ONLY render the polygon
// makevectors(view_angles); //??? it seems we do not need this perhaps...
// IN SHORT, we're riding the prior "makevectors(angGun2);".
R_BeginPolygon("sprites/laserbeam.spr_0.tga", 1, 0);
R_PolygonVertex(gunpos2 + v_right * fsize[0] - v_up * fsize[1], [1,1], lasColor, daDrawAlphahz);
R_PolygonVertex(gunpos2 - v_right * fsize[0] - v_up * fsize[1], [0,1], lasColor, daDrawAlphahz);
R_PolygonVertex(trace_endpos - v_right * fsize[0] + v_up * fsize[1], [0,0], lasColor, daDrawAlphahz);
R_PolygonVertex(trace_endpos + v_right * fsize[0] + v_up * fsize[1], [1,0], lasColor, daDrawAlphahz);
R_EndPolygon();
}
if (pl.entnum != player_localentnum) {
//makevectors(view_angles);
makevectors(angView);
trace_endpos += trace_plane_normal * fsizeDot[0]/6;
R_BeginPolygon("sprites/laserdot.spr_0.tga", 1, 0);
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] - v_up * fsizeDot[1], [1,1], lasColor, 0.80f);
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] - v_up * fsizeDot[1], [0,1], lasColor, 0.80f);
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] + v_up * fsizeDot[1], [0,0], lasColor, 0.80f);
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] + v_up * fsizeDot[1], [1,0], lasColor, 0.80f);
R_EndPolygon();
}
}
if(canRenderFlashlight){
if(!thirdperson){
//makevectors(view_angles);
makevectors(angView);
//trace_endpos += trace_plane_normal * fsizeDot[0]/6;
R_BeginPolygon("sprites/glow02.spr_0.tga", 1, 0);
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [1,1], [1,1,1], 0.45f);
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [0,1], [1,1,1], 0.45f);
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [0,0], [1,1,1], 0.45f);
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [1,0], [1,1,1], 0.45f);
R_EndPolygon();
}
}
}//END OF trace pre-check
}//END OF akimbo check
/*
if (m_iBeams == 0) {
alpha -= clframetime * 3;
return;
}
*/
}else{
pl.recentLaserHitPosSet = FALSE;
}//END OF canRenderLaserSight || canRenderFlashlight
}

View file

@ -42,7 +42,6 @@ entity/ts_shelleject.h
clientinfo.h clientinfo.h
seatlocal.h seatlocal.h
player.h
precache.h precache.h
ui.h ui.h

View file

@ -1,12 +1,14 @@
void View_UpdateWeapon(entity vm, entity mflash); void View_UpdateWeapon(entity vm, entity mflash);
void View_ShowMuzzleflash(int index);
void View_ShowMuzzleflashAkimbo(int index);
void TS_View_SetViewModelFromStats(void); void TS_View_SetViewModelFromStats(void);
void TS_View_RoutineCheck(void); void TS_View_RoutineCheck(void);
void TS_View_ChangeViewModelPost(void); void TS_View_ChangeViewModelPost(void);
void TS_View_ResetViewModel(void); void TS_View_ResetViewModel(void);
void TS_View_DrawCustom(player pl);
void TS_View_DrawExtraEffects(player pl, int thirdperson);
void TS_View_ShowMuzzleflash(int index, int akimboChoice);
void TS_View_HandleZoom(void); void TS_View_HandleZoom(void);

View file

@ -74,44 +74,6 @@ View_UpdateWeapon(entity vm, entity mflash)
} }
//TAGGG - NEW. Similar to Nuclide's provided "View_SetMuzzleflash", but also acts
// as though the first frame were an event by doing the same lines as a 5000-ish
// event. This is because TS weapons don't have events for the muzzle flash
// unlike HL ones, must be hardcoded to show up.
// Figuring out what muzzle flash for what weapon will come another time.
// For now using the same HL set, but TS comes with some muzzle-flash looking sprites.
// ALSO - for now, always assuming attachment #0.
// For the model event in case that changes, see Nuclide's src/client/modelevent.qc,
// Event_ProcessModel.
void
View_ShowMuzzleflash(int index)
{
pSeat->m_eMuzzleflash.modelindex = (float)index;
// Event_ProcessModel: force it.
pSeat->m_eMuzzleflash.alpha = 1.0f;
pSeat->m_eMuzzleflash.scale = 0.25;
// attachment #0 is m_iVMBones + 0. Add for #1 to #3, I think.
// No idea if any weapons play with attachments, #0 should always
// be the end of the weapon for flashes
pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones + 0;
}
void
View_ShowMuzzleflashAkimbo(int index)
{
pSeatLocal->m_eMuzzleflashAkimbo.modelindex = (float)index;
// Event_ProcessModel: force it.
pSeatLocal->m_eMuzzleflashAkimbo.alpha = 1.0f;
pSeatLocal->m_eMuzzleflashAkimbo.scale = 0.25;
// akimbo attachments are in pairs:
// 0 on one weapon, 1 further out along that weapon's direction,
// 2 on the other weapon, 3 further along the other weapon's direction.
// ALSO: on akimbo weapons, attachments 0 and 1 are for the right weapon,
// 1 and 2 for the left.
pSeatLocal->m_eMuzzleflashAkimbo.skin = pSeat->m_iVMBones + 2;
}
void void
TS_View_SetViewModelFromStats(void) TS_View_SetViewModelFromStats(void)
@ -299,6 +261,530 @@ TS_View_ResetViewModel(void)
// NEW, helper method, not called by Nuclide
// Does the same Nuclide script to draw the normal viewmodel at viewzooms between 0.5
// and 1.0, Nuclide skips drawing it on any zoom below 1.0 unlike original TS.
// Also, handles drawing the akimbo muzzle flash.
// Do not call from View_UpdateWeapon. Calling from LatePreDraw (new TS pseudo-event)
// is best.
// Also this is "Draw" as in render, not equip a weapon
void
TS_View_DrawCustom(player pl){
// Same forbidding conditions from Nuclide's src/client/view.qc
if (pl.health <= 0) {
return;
}
if (cvar("r_drawviewmodel") == 0 || autocvar_cl_thirdperson == TRUE) {
return;
}
// Nuclide does not draw the viewmodel at any zoom other than 1.0 (unmodified).
// So, this draws the viewmodel the othertimes TS did, in lower zoom choices.
// At viewzoom 0.5 and above, the scope graphic is not drawn so it is safe to
// do this.
if(pl.viewzoom >= 0.5){
entity m_eViewModel = pSeat->m_eViewModel;
entity m_eMuzzleflash = pSeat->m_eMuzzleflash;
entity m_eMuzzleflashAkimbo = pSeatLocal->m_eMuzzleflashAkimbo;
// (only re-do the default Nuclide viewmodel + normal muzzleflash script if we have reason
// to believe Nuclide didn't, i.e., a zoom under 1.0)
if(pl.viewzoom < 1.0){
// CLONE OF NUCLIDE SCRIPT
/////////////////////////////////////////////////////////////////////////////
if (m_eMuzzleflash.alpha > 0.0f) {
makevectors(getproperty(VF_ANGLES));
m_eMuzzleflash.origin = gettaginfo(m_eViewModel, m_eMuzzleflash.skin);
m_eMuzzleflash.angles = m_eViewModel.angles;
m_eMuzzleflash.angles[2] += (random() * 10) - 5;
/*dynamiclight_add(pSeat->m_vecPredictedOrigin + (v_forward * 32), 400 * m_eMuzzleflash.alpha, [1,0.45,0]);*/
setorigin(m_eMuzzleflash, m_eMuzzleflash.origin);
addentity(m_eMuzzleflash);
}
setorigin(m_eViewModel, m_eViewModel.origin);
addentity(m_eViewModel);
/////////////////////////////////////////////////////////////////////////////
}
// Regardless of being 1.0 or not (Nuclide certainly isn't doing this),
// handle the other muzzleflash too
if (m_eMuzzleflashAkimbo.alpha > 0.0f) {
makevectors(getproperty(VF_ANGLES));
m_eMuzzleflashAkimbo.origin = gettaginfo(m_eViewModel, m_eMuzzleflashAkimbo.skin);
m_eMuzzleflashAkimbo.angles = m_eViewModel.angles;
m_eMuzzleflashAkimbo.angles[2] += (random() * 10) - 5;
/*dynamiclight_add(pSeat->m_vecPredictedOrigin + (v_forward * 32), 400 * m_eMuzzleflashAkimbo.alpha, [1,0.45,0]);*/
setorigin(m_eMuzzleflashAkimbo, m_eMuzzleflashAkimbo.origin);
addentity(m_eMuzzleflashAkimbo);
}
}
if(pl.bShellEjectScheduled){
pl.bShellEjectScheduled = FALSE;
// TODO - how about a first-person check? Doesn't make sense if in thirdperson.
// Although that might've been needed earlier unless this event-thing works fine for being
// a playermodel too. Players other than the local one being rendered and needing to drop
// shells, that sounds like a whole other story
if(pl.iShellEjectType != SHELLEJECT_ID::NONE){
CTSShellEject::generateForViewModelAkimbo(pl.iShellEjectType, pl.iShellEjectAkimboChoice);
}
}
}
// another helper method
// Draw the lasersight, and flashlight effects
void
TS_View_DrawExtraEffects(player pl, int thirdperson)
{
//int thirdperson = (autocvar_cl_thirdperson == TRUE || this.entnum != player_localentnum);
//base_player pp = (base_player)this;
BOOL canRenderFlashlight = FALSE;
BOOL canRenderLaserSight = FALSE;
//player pl = (player)pp;
//we're going to use the buyopts of our current weapon + the one actually turned on, yah?
// DEBUG: printouts about the other player.
// Start a server with over 1 max players allowed in one window,
// connect to it in another window. Boom, read printouts.
/*
if(entnum != player_localentnum){
// so other player's "pl.weaponEquippedID" are not sent over to our clientside copies of them... I guess?
// At least that's not confusing.
printfline("It is I, the other player! What do I have? %i weapon count: %i", pl.weaponEquippedID, pl.ary_myWeapons_softMax);
}
*/
if(pl.inventoryEquippedIndex != -1){
weapondynamic_t dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
if(dynaRef.weaponTypeID == WEAPONDATA_TYPEID_GUN || dynaRef.weaponTypeID == WEAPONDATA_TYPEID_IRONSIGHT){
weapondata_basic_t* basePRef = pl.getEquippedWeaponData();
weapondata_basic_t baseRef = *basePRef;
// We must have the flashlight bit on, AND support it on our weapon, AND it be a possibility according to weapon data.
int legalBuyOpts_on = (dynaRef.iBitsUpgrade_on & (dynaRef.iBitsUpgrade & baseRef.iBitsUpgrade));
if(legalBuyOpts_on & BITS_WEAPONOPT_FLASHLIGHT){
canRenderFlashlight = TRUE;
}
if(legalBuyOpts_on & BITS_WEAPONOPT_LASERSIGHT){
canRenderLaserSight = TRUE;
}
}///END OF _GUN or _IRONSIGHT type checks
}//END OF weaponEquippedID check
vector posView;
vector angView;
//for glock it is 40.
//vector gunpos = gettaginfo(pSeat->eViewModel, 33);
float daDrawAlphahz = 1.0;
const vector lasColor = [1.0, 0, 0];
const vector fsize = [2,2];
const vector fsizeDot = [18,18];
const vector fsizeFlashlightMuzzleGlow = [3, 3];
vector flashPos;
vector gunpos;
vector gunpos2 = [0,0,0];
vector gunpos_tempEnd;
vector dirGun = [0,0,0];
vector dirGun2 = [0,0,0];
vector angGun = [0,0,0];
vector angGun2 = [0,0,0];
//TAGGG - IMPORTANT NOTE!!!
// BEWARE "view_angles", it is a client global (what a confusing term), meaning it pertains only to THIS
// client (local player), no matter what player is being rendered by this call.
// wait... shouldn't we do the third-person check for the flash-light check above too?
BOOL canDrawAkimboLaser = FALSE;
pl.recentLaserHitPosSet = TRUE;
// TAGGG - QUESTION: Is it better to use the bool "thirdperson"
// (see check below involving cl_thirdperson)
// OR a raw "pl.entnum != player_localentnum" check?
// The former is a more concrete check for, "Am I the local player
// looking in 1st person, yes or no?".
// The latter will still treat the player being the local player the
// same as firstperson, even if the local player is forcing themselves
// to be viewed in third person.
// I am inclined to think the former is the better choice, but
// valve/src/client/flashlight.qc uses the latter way. Why?
// thirdperson
// True IF (autocvar_cl_thirdperson == TRUE || this.entnum != player_localentnum)
// False IF (autocvar_cl_thirdperson == FALSE && this.entnum == player_localentnum)
if(!thirdperson){
//TAGGG - Old way!
//posView = getproperty(VF_ORIGIN) + [0,0,-8];
//angView = getproperty(VF_CL_VIEWANGLES);
posView = pSeat->m_vecPredictedOrigin + [0,0,-8];
angView = view_angles;
// CHECK: is "getproperty(VF_CL_VIEWANGLES)" always the same as "view_angles"?
if(!pl.weaponEquippedAkimbo){
// first-person and this is the local player?
// We can get more specific info from the visible viewmodel, do so!
gunpos = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 0i);
gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 3i);
// should shell casings come from a bit behind the firing point here when needed?
//gunpos += v_right * 0.8; //why is this 'up'??
// not this one maybe... what the hell is this direction at all.
//gunpos += v_forward * -1.8;
dirGun = normalize(gunpos_tempEnd - gunpos);
angGun = vectoangles(dirGun);
}else{
canDrawAkimboLaser = TRUE;
gunpos = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 0i);
gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 1i);
dirGun = normalize(gunpos_tempEnd - gunpos);
angGun = vectoangles(dirGun);
gunpos2 = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 2i);
gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 3i);
dirGun2 = normalize(gunpos_tempEnd - gunpos2);
angGun2 = vectoangles(dirGun2);
}
}else{
posView = pl.origin + pl.view_ofs;
angView = [pl.pitch, pl.angles[1], pl.angles[2]];
gunpos = posView;
angGun = angView;
}
if(canRenderLaserSight && pl.entnum == player_localentnum){
// The lasersight displays a number of distance to show how long the laser goes.
//makevectors(view_angles);
makevectors(angView);
traceline(posView, posView + v_forward * 2048*4, MOVE_HITMODEL, pl);
pl.recentLaserDistanceDisplay = (int)(vlen(trace_endpos - posView) / 40 );
}
// DEBUG!!!
//canRenderLaserSight = TRUE;
//canRenderFlashlight = TRUE;
if(canRenderFlashlight){
//TAGGG - FLASHLIGHT STUFF HERE..
// oh wait a comment above already said that
// HOWEVER... in TS flashlights have a range limit. Up to so far they have max brightness,
// then it lowers with a bit of range, then it's nothing.
////////////////////////////////////////////////////////////////////////////////////
int flashlightRangeMax = 1070;
float flashlightBrightnessFactor = 1.0;
float rangeDimPriorStart = 170; //rangeMax minus this is where I start dimming.
makevectors(angView);
traceline(posView, posView + (v_forward * flashlightRangeMax), MOVE_NORMAL, pl);
int traceDist = trace_fraction * flashlightRangeMax;
//printfline("%.2f %d",, trace_fraction, trace_inopen);
//TODO - not here but elsewhere, draw the muzzle flashlight effect, some sprite on that gun attachment where am uzzleflash would go should do it.
// ALSO, go ahead and use this line trace to tell the lasersight how far the laser went.
// And just draw the lasersight (and dot if necessary), IF this render is for the local player.
// If not the local player, a slightly larger red dot (actual sprite) goes at the point the
// player is looking, likely not influenced by animations / view-model stuff.
if(trace_fraction == 1.0){
//uh-oh.
flashlightBrightnessFactor = 0;
}if(traceDist >= flashlightRangeMax - rangeDimPriorStart){
//the flashlight gets dimmer the further it is at this point.
// rangeDimPriorStart from the end: max bright still.
// very end: 0% bright.
flashlightBrightnessFactor = (-flashlightRangeMax * (trace_fraction + -1)) / rangeDimPriorStart;
}
if(flashlightBrightnessFactor > 0){
if (serverkeyfloat("*bspversion") == BSPVER_HL) {
dynamiclight_add(trace_endpos + (v_forward * -2), 128 * flashlightBrightnessFactor, [1,1,1]);
} else {
float p = dynamiclight_add(posView, 512 * flashlightBrightnessFactor, [1,1,1], 0, "textures/flashlight");
dynamiclight_set(p, LFIELD_ANGLES, angView);
dynamiclight_set(p, LFIELD_FLAGS, 3);
}
}//END OF brightness check
}//END OF "flashlight is on" criteria
if(canRenderLaserSight || canRenderFlashlight){
// TRY IT SOMEHOW? RF_DEPTHHACK
//pSeat->m_eViewModel.renderflags = RF_DEPTHHACK;
//if (alpha <= 0.0f) {
// return;
//}
makevectors(angGun);
//rotatevectorsbyangle( [-0.42, 0.75, 0] );
//rotatevectorsbyangle( [-0.52, 0.85, 0] );
rotatevectorsbyangle( [-0.45, 0.27, 0] );
flashPos = gunpos + v_up * -0.08 + v_right * 0.06;
vector shortForwardEnd = gunpos;
shortForwardEnd += v_forward * -1;
shortForwardEnd += v_up * (0.22); //why is this 'up'??
shortForwardEnd += v_right * (0.35); //why is this 'up'??
//makevectors(m_vecAngle); really now
//makevectors(input_angles); //maybe this if we need to do this.
// v_up * 2. or.. 1.6, for size [3,3] at least.
// for size [5,5], we need v_up*3, v_right*2. I DONT KNOW.
// for size [2, 2], we want v_up*5, v_right*5. Go figure that one out.
// Keep in mind, the global vectors (v_up, etc.) are set to the orientation of the recently received
// viewmodel attachment (gettaginfo above). Don't 'makevectors' at all to simply rely on that.
// ...unfortunately the orientation we get back is not great either. oh well.
// NEW TEST. Can we even get a straight line from the player's center to the gunpos?
traceline(posView, shortForwardEnd, FALSE, pl);
if(trace_fraction >= 1.0){
//woohoo!
traceline(shortForwardEnd, shortForwardEnd + v_forward * 1024, MOVE_HITMODEL, pl);
// other places care about this.
if (pl.entnum == player_localentnum) {
pl.recentLaserHitPos = trace_endpos;
}
if(canRenderLaserSight){
// In original TS, the 'laster' does not render for the local player if they are
// in thirdperson to see their own playermodel. I... don't really understand that,
// feels like a mistake. The lasers from other players are visible.
// TAGGG - TODO - SUPER LOW PRIORITY
// Lasers only for not in 3rd person and local player.
// Could work for the third-person model or other players if there were a way to determine
// the muzzle-end point for player models. Unsure if that is possible.
// To see it in third-person anyway, just change the "!thirdperson" condition below
// to "TRUE"; always do it. There is no separate place that does only first-person lasers
// to worry about being redundant with.
// Note the laser will try to face the direction the player model is, which may not
// necessarily be where the player is looking, although that same issue would come up
// with firing anyway; you would look like you're firing sideways anyway, this would be
// just as "off".
if (!thirdperson) {
//makevectors(view_angles); //??? it seems we do not need this perhaps...
// IN SHORT, we're riding the prior "makevectors(angGun);".
R_BeginPolygon("sprites/laserbeam.spr_0.tga", 1, 0);
R_PolygonVertex(gunpos + v_right * fsize[0] - v_up * fsize[1], [1,1], lasColor, daDrawAlphahz);
R_PolygonVertex(gunpos - v_right * fsize[0] - v_up * fsize[1], [0,1], lasColor, daDrawAlphahz);
R_PolygonVertex(trace_endpos - v_right * fsize[0] + v_up * fsize[1], [0,0], lasColor, daDrawAlphahz);
R_PolygonVertex(trace_endpos + v_right * fsize[0] + v_up * fsize[1], [1,0], lasColor, daDrawAlphahz);
R_EndPolygon();
}
// Draw the laser sprite effect at where the laser is hitting, but ONLY for every other player
// except this one. That's because, for the local player, we already are drawing the laserdot
// projected onto the screen in the HUD logic.
if (pl.entnum != player_localentnum) {
makevectors(angView);
trace_endpos += trace_plane_normal * fsizeDot[0]/6;
R_BeginPolygon("sprites/laserdot.spr_0.tga", 1, 0);
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] - v_up * fsizeDot[1], [1,1], lasColor, 0.80f);
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] - v_up * fsizeDot[1], [0,1], lasColor, 0.80f);
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] + v_up * fsizeDot[1], [0,0], lasColor, 0.80f);
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] + v_up * fsizeDot[1], [1,0], lasColor, 0.80f);
R_EndPolygon();
}
}//END OF canRenderLaserSight
// glow effect on top of the gun muzzle while the flashlight is on?
if(canRenderFlashlight){
if(!thirdperson){
makevectors(angView);
//trace_endpos += trace_plane_normal * fsizeDot[0]/6;
R_BeginPolygon("sprites/glow02.spr_0.tga", 1, 0);
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [1,1], [1,1,1], 0.45f);
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [0,1], [1,1,1], 0.45f);
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [0,0], [1,1,1], 0.45f);
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [1,0], [1,1,1], 0.45f);
R_EndPolygon();
}
}
}//END OF trace pre-check
// This requires the current weapon to be akimbo, player is in first person,
// and the local player is being rendered.
if(canDrawAkimboLaser){
// test for the 2nd gun's laser too then.
// makevectors(theDir);
makevectors(angGun2);
rotatevectorsbyangle( [-0.45, 0.27, 0] );
//gunpos2 += v_forward * -18;
flashPos = gunpos2 + v_up * -0.08 + v_right * 0.06;
shortForwardEnd = gunpos2;
shortForwardEnd += v_forward * -1;
shortForwardEnd += v_up * (0.22); //why is this 'up'??
shortForwardEnd += -v_right * (0.35); //why is this 'up'??
traceline(posView, shortForwardEnd, FALSE, pl);
if(trace_fraction >= 1.0){
traceline(shortForwardEnd, shortForwardEnd + v_forward * 1024, MOVE_HITMODEL, pl);
// other places care about this.
if (pl.entnum == player_localentnum) {
pl.recentLaserHitPos2 = trace_endpos;
}
if(canRenderLaserSight){
if (!thirdperson) {
// ONLY render the polygon
// makevectors(view_angles); //??? it seems we do not need this perhaps...
// IN SHORT, we're riding the prior "makevectors(angGun2);".
R_BeginPolygon("sprites/laserbeam.spr_0.tga", 1, 0);
R_PolygonVertex(gunpos2 + v_right * fsize[0] - v_up * fsize[1], [1,1], lasColor, daDrawAlphahz);
R_PolygonVertex(gunpos2 - v_right * fsize[0] - v_up * fsize[1], [0,1], lasColor, daDrawAlphahz);
R_PolygonVertex(trace_endpos - v_right * fsize[0] + v_up * fsize[1], [0,0], lasColor, daDrawAlphahz);
R_PolygonVertex(trace_endpos + v_right * fsize[0] + v_up * fsize[1], [1,0], lasColor, daDrawAlphahz);
R_EndPolygon();
}
if (pl.entnum != player_localentnum) {
//makevectors(view_angles);
makevectors(angView);
trace_endpos += trace_plane_normal * fsizeDot[0]/6;
R_BeginPolygon("sprites/laserdot.spr_0.tga", 1, 0);
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] - v_up * fsizeDot[1], [1,1], lasColor, 0.80f);
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] - v_up * fsizeDot[1], [0,1], lasColor, 0.80f);
R_PolygonVertex(trace_endpos - v_right * fsizeDot[0] + v_up * fsizeDot[1], [0,0], lasColor, 0.80f);
R_PolygonVertex(trace_endpos + v_right * fsizeDot[0] + v_up * fsizeDot[1], [1,0], lasColor, 0.80f);
R_EndPolygon();
}
}
if(canRenderFlashlight){
if(!thirdperson){
//makevectors(view_angles);
makevectors(angView);
//trace_endpos += trace_plane_normal * fsizeDot[0]/6;
R_BeginPolygon("sprites/glow02.spr_0.tga", 1, 0);
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [1,1], [1,1,1], 0.45f);
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] - v_up * fsizeFlashlightMuzzleGlow[1], [0,1], [1,1,1], 0.45f);
R_PolygonVertex(flashPos - v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [0,0], [1,1,1], 0.45f);
R_PolygonVertex(flashPos + v_right * fsizeFlashlightMuzzleGlow[0] + v_up * fsizeFlashlightMuzzleGlow[1], [1,0], [1,1,1], 0.45f);
R_EndPolygon();
}
}
}//END OF trace pre-check
}//END OF akimbo check
/*
if (m_iBeams == 0) {
alpha -= clframetime * 3;
return;
}
*/
}else{
pl.recentLaserHitPosSet = FALSE;
}//END OF canRenderLaserSight || canRenderFlashlight
}
//TAGGG - NEW. Similar to Nuclide's provided "View_SetMuzzleflash", but also acts
// as though the first frame were an event by doing the same lines as a 5000-ish
// event. This is because TS weapons don't have events for the muzzle flash
// unlike HL ones, must be hardcoded to show up.
// Figuring out what muzzle flash for what weapon will come another time.
// For now using the same HL set, but TS comes with some muzzle-flash looking sprites.
// ALSO - for now, always assuming attachment #0.
// For the model event in case that changes, see Nuclide's src/client/modelevent.qc,
// Event_ProcessModel.
void
TS_View_ShowMuzzleflash(int index, int akimboChoice)
{
// use the akimbo choice to tell which one to show, or both
// (NONE means this is a singular weapon)
if(akimboChoice == BITS_AKIMBOCHOICE_NONE || (akimboChoice & BITS_AKIMBOCHOICE_RIGHT)){
pSeat->m_eMuzzleflash.modelindex = (float)index;
// Event_ProcessModel: force it.
pSeat->m_eMuzzleflash.alpha = 1.0f;
pSeat->m_eMuzzleflash.scale = 0.25;
// attachment #0 is m_iVMBones + 0. Add for #1 to #3, I think.
// No idea if any weapons play with attachments, #0 should always
// be the end of the weapon for flashes
pSeat->m_eMuzzleflash.skin = pSeat->m_iVMBones + 0;
}
if(akimboChoice & BITS_AKIMBOCHOICE_LEFT){
pSeatLocal->m_eMuzzleflashAkimbo.modelindex = (float)index;
// Event_ProcessModel: force it.
pSeatLocal->m_eMuzzleflashAkimbo.alpha = 1.0f;
pSeatLocal->m_eMuzzleflashAkimbo.scale = 0.25;
// akimbo attachments are in pairs:
// 0 on one weapon, 1 further out along that weapon's direction,
// 2 on the other weapon, 3 further along the other weapon's direction.
// ALSO: on akimbo weapons, attachments 0 and 1 are for the right weapon,
// 1 and 2 for the left.
pSeatLocal->m_eMuzzleflashAkimbo.skin = pSeat->m_iVMBones + 2;
}
}
//TAGGG - loaned from The Wastes. //TAGGG - loaned from The Wastes.
void TS_View_HandleZoom(void) void TS_View_HandleZoom(void)
{ {

View file

@ -66,38 +66,13 @@ Viewmodel_CalcBob(void)
int inputLen = vlen(input_movevalues); int inputLen = vlen(input_movevalues);
// do we care about "self.maxspeed" ?? // do we care about "self.maxspeed" ?
// Instead of some new bTouchingGround, using the presence of flag FL_ONGROUND.
// If the player waterlevel >= 2, the "FL_ONGROUND" flag never occurs,
// regardless of touching the floor in the area.
// In original TS, being on the floor underwater DOES still do the camera/viewbob.
// No idea how to get around this, beyond traces against the ground like pmove
// does.
BOOL bOnGround = FALSE; // could check for FL_ONGROUND, but it is never set when the player is touching the ground
// while underwater, even though orginal TS viewbob counts that too.
if(pl.waterlevel < 2){ // TODO: may as well turn this into a separate bitflag too then.
// Easy, trsy pl.flags having FL_ONGROUND. if(!pl.bOnGround){
bOnGround = ((pl.flags & FL_ONGROUND) != 0);
}else{
// Underwater enough? Need to do a re-trace, FL_ONGROUND is forced off by
// pmove.
// Open for better ideas, or maybe this is a massive "who cares" situation.
tracebox(pl.origin, pl.mins, pl.maxs, pl.origin - [0,0,0.25], FALSE, pl);
if (!trace_startsolid) {
if ((trace_fraction < 1) && (trace_plane_normal[2] > 0.7)) {
bOnGround = TRUE;
} else {
}
}
}
if(!bOnGround){
float timeTol; float timeTol;
vector vSource = pl.origin; vector vSource = pl.origin;

View file

@ -157,9 +157,24 @@ class player:base_player
// same as below for muzzleflash. // same as below for muzzleflash.
int iMuzzleFlashType; int iMuzzleFlashType;
// and akimbo support
int iMuzzleFlashAkimboChoice;
// During a shell-eject event, what member of aryShellEjectData do I use for the model // During a shell-eject event, what member of aryShellEjectData do I use for the model
// and hitsound script file to use? Set on setting the event // and hitsound script file to use? Set on setting up the event so that it is only
// called once, clientside weapons can be strange.
// Must be stored as a var to be preserved for the event (which does not take any params,
// and is called very shortly after being set in a separate stream of execution).
int iShellEjectType; int iShellEjectType;
// And, for which weapon? Default is LEFT (attachment #0)
int iShellEjectAkimboChoice;
// Not involved in setting up the shell-eject event. Rather, it is set BY the shell-eject
// event to ensure this only happens once per fire animation.
// When seen TRUE in TS_View_DrawCustom, the shell ejection actually happens for the
// most accurate position.
BOOL bShellEjectScheduled;
// WEAPON KICKBACK STUFF. // WEAPON KICKBACK STUFF.
// Way it works is a little different from the average (Half-Life and Counterstrike). // Way it works is a little different from the average (Half-Life and Counterstrike).
@ -350,14 +365,16 @@ class player:base_player
// at what time do I throw the grenade after starting the throw/toss anim? // at what time do I throw the grenade after starting the throw/toss anim?
float grenadeSpawnTime; float grenadeSpawnTime;
float grenadeHeldDuration; float grenadeHeldDuration;
PREDICTED_FLOAT(grenadeToss); //set to FALSE if it's a typical throw instead. PREDICTED_FLOAT(bGrenadeToss); //set to FALSE if it's a typical throw instead.
// shared // shared
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
//for pmove to set // for custom pmove to set
BOOL viewAboveWater; // TODO: these should probably be gflags constants as bitflags instead
BOOL bOnGround;
BOOL bViewAboveWater;
//TODO - could this just be an entity flag just as well? Unfortunately I don't know for //TODO - could this just be an entity flag just as well? Unfortunately I don't know for
// sure what flags are guaranteed open / nonconflicting with anything else. // sure what flags are guaranteed open / nonconflicting with anything else.

View file

@ -203,7 +203,7 @@ player::ReceiveEntity(float new, float fl)
nextAkimboAttackPreference = readbyte(); nextAkimboAttackPreference = readbyte();
akimboDualFireToleranceTime = readfloat(); akimboDualFireToleranceTime = readfloat();
grenadeFireIndex = readbyte() - 1; grenadeFireIndex = readbyte() - 1;
grenadeToss = readbyte(); bGrenadeToss = readbyte();
armor = readbyte(); armor = readbyte();
@ -381,7 +381,7 @@ player::PredictPreFrame(void)
SAVE_STATE(nextAkimboAttackPreference); SAVE_STATE(nextAkimboAttackPreference);
SAVE_STATE(akimboDualFireToleranceTime); SAVE_STATE(akimboDualFireToleranceTime);
SAVE_STATE(grenadeFireIndex); SAVE_STATE(grenadeFireIndex);
SAVE_STATE(grenadeToss); SAVE_STATE(bGrenadeToss);
SAVE_STATE(shotgunReloadIndex); SAVE_STATE(shotgunReloadIndex);
@ -480,7 +480,7 @@ player::PredictPostFrame(void)
ROLL_BACK(nextAkimboAttackPreference); ROLL_BACK(nextAkimboAttackPreference);
ROLL_BACK(akimboDualFireToleranceTime); ROLL_BACK(akimboDualFireToleranceTime);
ROLL_BACK(grenadeFireIndex); ROLL_BACK(grenadeFireIndex);
ROLL_BACK(grenadeToss); ROLL_BACK(bGrenadeToss);
ROLL_BACK(shotgunReloadIndex); ROLL_BACK(shotgunReloadIndex);
@ -670,7 +670,7 @@ player::EvaluateEntity(void)
SAVE_STATE(nextAkimboAttackPreference); SAVE_STATE(nextAkimboAttackPreference);
SAVE_STATE(akimboDualFireToleranceTime); SAVE_STATE(akimboDualFireToleranceTime);
SAVE_STATE(grenadeFireIndex); SAVE_STATE(grenadeFireIndex);
SAVE_STATE(grenadeToss); SAVE_STATE(bGrenadeToss);
SAVE_STATE(shotgunReloadIndex); SAVE_STATE(shotgunReloadIndex);
@ -825,7 +825,7 @@ player::SendEntity(entity ePEnt, float fChanged)
WriteByte(MSG_ENTITY, nextAkimboAttackPreference); WriteByte(MSG_ENTITY, nextAkimboAttackPreference);
WriteFloat(MSG_ENTITY, akimboDualFireToleranceTime); WriteFloat(MSG_ENTITY, akimboDualFireToleranceTime);
WriteByte(MSG_ENTITY, grenadeFireIndex + 1); WriteByte(MSG_ENTITY, grenadeFireIndex + 1);
WriteByte(MSG_ENTITY, grenadeToss); WriteByte(MSG_ENTITY, bGrenadeToss);
WriteByte(MSG_ENTITY, armor ); WriteByte(MSG_ENTITY, armor );
WriteByte(MSG_ENTITY, iTotalSlots ); WriteByte(MSG_ENTITY, iTotalSlots );
@ -1123,7 +1123,7 @@ player::reset(BOOL resetInventory){
grenadeFireIndex = -1; grenadeFireIndex = -1;
grenadeHeldDuration = -1; grenadeHeldDuration = -1;
grenadeSpawnTime = -1; grenadeSpawnTime = -1;
grenadeToss = FALSE; bGrenadeToss = FALSE;
isReloading = FALSE; isReloading = FALSE;
isChangingIronsight = FALSE; isChangingIronsight = FALSE;

View file

@ -269,6 +269,14 @@ player::Physics_WaterJump(void)
float float
player::Physics_MaxSpeed(void) player::Physics_MaxSpeed(void)
{ {
// Consider noclip. Otherwise, moving downward is considerably slower for no
// apparent reason (FL_CROUCHING input detected further down).
if(this.movetype == MOVETYPE_NOCLIP){
return serverkeyfloat("phy_maxspeed");
}
/* /*
float parentResult = base_player::Physics_MaxSpeed(); float parentResult = base_player::Physics_MaxSpeed();
return parentResult; return parentResult;
@ -283,7 +291,9 @@ player::Physics_MaxSpeed(void)
//TAGGG - CRITICAL TODO. Check for prone, that gets set to 30% of normal speed and only //TAGGG - CRITICAL TODO. Check for prone, that gets set to 30% of normal speed and only
// allows for sideways movement, ignore input_movevalues[#] whatever is the forward one. probably 0. // allows for sideways movement, ignore input_movevalues[#] whatever is the forward one. probably 0.
if ( this.flags & FL_CROUCHING ) { // TODO: does this slow down lateral movement while trying to go downard underwater? Does it even
// signal for that at all? This might be a non-issue.
if((this.flags & FL_CROUCHING) ){
targetSpeed = serverkeyfloat("phy_maxspeed") * 1.0 * 0.33; targetSpeed = serverkeyfloat("phy_maxspeed") * 1.0 * 0.33;
} else { } else {
targetSpeed = serverkeyfloat("phy_maxspeed") * 1.0; targetSpeed = serverkeyfloat("phy_maxspeed") * 1.0;
@ -292,6 +302,10 @@ player::Physics_MaxSpeed(void)
if(time <= this.fMoveBlockDelay){ if(time <= this.fMoveBlockDelay){
// don't move much until this has passed. Further slowdowns need not apply. // don't move much until this has passed. Further slowdowns need not apply.
// !
// "fMoveBlockCapSpeed" must be set by a weapon before it is involved.
// Is that the best way to handle this? Being a field in all weapon data would
// not make sense though, not much uses this
targetSpeed = min(targetSpeed, this.fMoveBlockCapSpeed); targetSpeed = min(targetSpeed, this.fMoveBlockCapSpeed);
return targetSpeed; return targetSpeed;
} }
@ -314,6 +328,23 @@ player::Physics_InputPreMove(void)
{ {
base_player::Physics_InputPreMove(); base_player::Physics_InputPreMove();
// IDEA: is changing the speed by inputmove value adjusting better than changing
// the max speed for prediction wonkiness (the camera looks to jump around on slowing down
// since a recent karate attack)?
// from Nuclide's player_move.qc (holding use-key slowdown):
//if (input_buttons & INPUT_BUTTON5) {
// input_movevalues *= 0.25;
//}
// also, reduce the up/down noclip movement a bit, no need to be that quite that fast in
// most cases
if (input_buttons & INPUT_BUTTON2) {
input_movevalues[2] *= 0.7;
}
if (input_buttons & INPUT_BUTTON8) {
input_movevalues[2] *= 0.7;
}
} }
void void
@ -345,42 +376,87 @@ player::Physics_Run(void)
// Copied from PMoveCustom_Categorize / PMove_Categorize, whatever we're calling it, // Copied from PMoveCustom_Categorize / PMove_Categorize, whatever we're calling it,
// from Nuclide. // from Nuclide.
// Any changes to what PMoveCustom_Categorize already changes would be redundant, // Any changes to what PMoveCustom_Categorize already changes would be redundant,
// this is only to do a finer check for a new var, 'viewAboveWater', a more accurate // this is only to do a finer check for a new var, 'bViewAboveWater', a more accurate
// check than 'pl.waterlevel < 3'. // check than 'pl.waterlevel < 3'.
// Or maybe this will be deemed pointless and scrapped anyway, that isn't too bad and // Or maybe this will be deemed pointless and scrapped anyway, that isn't too bad and
// it's what nearly any goldsource mod did, probably quake-based in general. // it's what nearly any goldsource mod did, probably quake-based in general.
int contents;
contents = PMoveCustom_Contents(this.origin + this.mins + [0,0,1]);
if (contents & CONTENTBIT_WATER) { // But first some common sense checks with the existing determined pl.waterlevel.
contents = CONTENT_WATER; // 0: not touching water at all
} else if (contents & CONTENTBIT_SLIME) { // 1: at least the bottom of the bounding box, not past the halfway point up
contents = CONTENT_SLIME; // 2: between the halfway point up and very close to the top
} else if (contents & CONTENTBIT_LAVA) { // 3: close to the very top or completely submerged
contents = CONTENT_LAVA; //... so we only need to be more specifc with the trace below if pl.waterlevel is 2
} if(this.waterlevel < 2){
// definitely so (not in water at all also counts)
this.bViewAboveWater = TRUE;
//player pl = (player)self; }else if (this.waterlevel == 3){
this.viewAboveWater = TRUE; // submerged so no
this.bViewAboveWater = FALSE;
// how far underwater are we? }else{
if (contents < CONTENT_SOLID && !(this.flags & FL_ONLADDER)) { // == 2? Check
//this.watertype = contents; int contents;
// Let's do this check sperately instead of below. contents = PMoveCustom_Contents(this.origin + this.mins + [0,0,1]);
// Little margin of error (-5 in .z) to see if the viewmodel is probably above/below water.
if(PMoveCustom_Contents(this.origin + this.view_ofs - '0 0 5') & CONTENTBITS_FLUID){ if (contents & CONTENTBIT_WATER) {
this.viewAboveWater = FALSE; contents = CONTENT_WATER;
} else if (contents & CONTENTBIT_SLIME) {
contents = CONTENT_SLIME;
} else if (contents & CONTENTBIT_LAVA) {
contents = CONTENT_LAVA;
} }
} else { this.bViewAboveWater = TRUE;
//this.watertype = CONTENT_EMPTY;
///this.waterlevel = 0; // how far underwater are we?
} if (contents < CONTENT_SOLID && !(this.flags & FL_ONLADDER)) {
///////////////////////////////////////////////////////////////////////////// //this.watertype = contents;
// Let's do this check sperately instead of below.
// Little margin of error (-5 in .z) to see if the viewmodel is probably above/below water.
vector vTest = this.origin + this.view_ofs - [0, 0, 5];
if(PMoveCustom_Contents(vTest) & CONTENTBITS_FLUID){
this.bViewAboveWater = FALSE;
}
} else {
//this.watertype = CONTENT_EMPTY;
///this.waterlevel = 0;
}
/////////////////////////////////////////////////////////////////////////////
}// bViewAboveWater check
}
// If the player waterlevel >= 2, the "FL_ONGROUND" flag never occurs,
// regardless of touching the floor while underwater (as seen in Nuclide pmove).
// In original TS, being on the floor underwater DOES still do the camera/viewbob.
// No idea how to get around this, beyond traces against the ground like pmove
// does.
// Open for better ideas, or maybe this is a massive "who cares" situation.
if(this.waterlevel < 2){
// Easy, trsy pl.flags having FL_ONGROUND.
this.bOnGround = ((this.flags & FL_ONGROUND) != 0);
}else{
// Underwater enough? Need to do a re-trace, FL_ONGROUND is forced off by
// pmove.
// until proven otherwise
this.bOnGround = FALSE;
vector vTest2 = this.origin - [0,0,0.25];
tracebox(this.origin, this.mins, this.maxs, vTest2, FALSE, this);
if (!trace_startsolid) {
if ((trace_fraction < 1) && (trace_plane_normal[2] > 0.7)) {
this.bOnGround = TRUE;
} else {
}
}
}
}// Physics_Run

View file

@ -234,8 +234,8 @@ if (pl.inputSecondaryTapFrameCount == 0)\
// OLD UNDERWATER CHECK. Using a new var for more accuracy. // OLD UNDERWATER CHECK. Using a new var for more accuracy.
//#define WEAPON_UNDERWATER_CHECK pl.waterlevel >= 3 //#define WEAPON_UNDERWATER_CHECK pl.waterlevel >= 3
//#define WEAPON_UNDERWATER_CHECK_NOT pl.waterlevel < 3 //#define WEAPON_UNDERWATER_CHECK_NOT pl.waterlevel < 3
#define WEAPON_UNDERWATER_CHECK !pl.viewAboveWater #define WEAPON_UNDERWATER_CHECK !pl.bViewAboveWater
#define WEAPON_UNDERWATER_CHECK_NOT pl.viewAboveWater #define WEAPON_UNDERWATER_CHECK_NOT pl.bViewAboveWater

View file

@ -860,8 +860,11 @@ void weapon_throwable_onDrawHUD(player pl, weapondata_throwable_t* basePRef, wea
void weapon_melee_onDrawHUD(player pl, weapondata_melee_t* basePRef, weapondynamic_t arg_thisWeapon); void weapon_melee_onDrawHUD(player pl, weapondata_melee_t* basePRef, weapondynamic_t arg_thisWeapon);
void weapon_ShowMuzzleFlash(int arg_muzzleFlashType); void weapon_ShowMuzzleFlash(int arg_muzzleFlashType);
void weapon_ShowMuzzleFlashAkimbo(int arg_muzzleFlashType, int arg_akimboChoice);
void weapon_EjectShell(int arg_shellEjectType); void weapon_EjectShell(int arg_shellEjectType);
void weapon_EjectShellAkimbo(int arg_shellEjectType, int arg_akimboChoice);
void weapon_ClientEffects(int arg_muzzleFlashType, int arg_shellEjectType); void weapon_ClientEffects(int arg_muzzleFlashType, int arg_shellEjectType);
void weapon_ClientEffectsAkimbo(int arg_muzzleFlashType, int arg_shellEjectType, int arg_akimboChoice);
void weapon_precache(weapondata_basic_t* basePRef); void weapon_precache(weapondata_basic_t* basePRef);

View file

@ -9,7 +9,8 @@ weapon_t g_weapons[WEAPON_ID::LAST_ID];
void void
weaponconfig_data_init(weaponconfig_data_t* arg_this){ weaponconfig_data_init(weaponconfig_data_t* arg_this)
{
arg_this->ary_myWeapons_softMax = 0; arg_this->ary_myWeapons_softMax = 0;
for(int i = 0; i < AMMO_ID::LAST_ID; i++){ for(int i = 0; i < AMMO_ID::LAST_ID; i++){
arg_this->ary_ammoTotal[i] = 0; arg_this->ary_ammoTotal[i] = 0;
@ -30,7 +31,8 @@ weaponconfig_data_init(weaponconfig_data_t* arg_this){
void void
weapon_base_setWholeAttackDelay(player pl, float amount){ weapon_base_setWholeAttackDelay(player pl, float amount)
{
pl.w_attack_next = amount; pl.w_attack_next = amount;
pl.w_attack_akimbo_next = amount; pl.w_attack_akimbo_next = amount;
@ -43,14 +45,16 @@ weapon_base_setWholeAttackDelay(player pl, float amount){
// normal delay only? // normal delay only?
void void
weapon_base_setLeftAttackDelay(player pl, float amount){ weapon_base_setLeftAttackDelay(player pl, float amount)
{
pl.w_attack_next = amount; pl.w_attack_next = amount;
//SAVE_STATE(pl.w_attack_next); //SAVE_STATE(pl.w_attack_next);
} }
// akimbo secondary delay only? // akimbo secondary delay only?
void void
weapon_base_setRightAttackDelay(player pl, float amount){ weapon_base_setRightAttackDelay(player pl, float amount)
{
pl.w_attack_akimbo_next = amount; pl.w_attack_akimbo_next = amount;
//SAVE_STATE(pl.w_attack_akimbo_next); //SAVE_STATE(pl.w_attack_akimbo_next);
} }
@ -61,7 +65,8 @@ weapon_base_setRightAttackDelay(player pl, float amount){
// If there are 2 seconds left until firing is allowed, ignore a request // If there are 2 seconds left until firing is allowed, ignore a request
// to set it to 0.3 seconds. // to set it to 0.3 seconds.
void void
weapon_base_setLeftAttackDelay_AtLeast(player pl, float amount){ weapon_base_setLeftAttackDelay_AtLeast(player pl, float amount)
{
if(pl.w_attack_next <= amount){ if(pl.w_attack_next <= amount){
pl.w_attack_next = amount; pl.w_attack_next = amount;
//SAVE_STATE(pl.w_attack_next); //SAVE_STATE(pl.w_attack_next);
@ -70,7 +75,8 @@ weapon_base_setLeftAttackDelay_AtLeast(player pl, float amount){
// akimbo secondary delay only? // akimbo secondary delay only?
void void
weapon_base_setRightAttackDelay_AtLeast(player pl, float amount){ weapon_base_setRightAttackDelay_AtLeast(player pl, float amount)
{
if(pl.w_attack_akimbo_next <= amount){ if(pl.w_attack_akimbo_next <= amount){
pl.w_attack_akimbo_next = amount; pl.w_attack_akimbo_next = amount;
//SAVE_STATE(pl.w_attack_akimbo_next); //SAVE_STATE(pl.w_attack_akimbo_next);
@ -272,7 +278,8 @@ weapon_base_onPrimaryAttack_melee_fromCustomDirection(
// Apparently serverside-only? ok... // Apparently serverside-only? ok...
void void
weapon_base_onAttack(player pl, weapondata_basic_t* basePRef, weapondynamic_t arg_thisWeapon, int attackTypeUsed){ weapon_base_onAttack(player pl, weapondata_basic_t* basePRef, weapondynamic_t arg_thisWeapon, int attackTypeUsed)
{
weapon_base_onAttack_multi(pl, basePRef, arg_thisWeapon, 1, attackTypeUsed); weapon_base_onAttack_multi(pl, basePRef, arg_thisWeapon, 1, attackTypeUsed);
}// weapon_base_onAttack }// weapon_base_onAttack
@ -287,7 +294,8 @@ weapon_base_onAttack(player pl, weapondata_basic_t* basePRef, weapondynamic_t ar
// (effectively becomes 'max number of penetrations allowed', which is still something only for guns) // (effectively becomes 'max number of penetrations allowed', which is still something only for guns)
// NOTE - checks for being unable to fire yet not included! // NOTE - checks for being unable to fire yet not included!
void void
weapon_base_onAttack_multi(player pl, weapondata_basic_t* basePRef, weapondynamic_t arg_thisWeapon, int shellCount, int attackTypeUsed){ weapon_base_onAttack_multi(player pl, weapondata_basic_t* basePRef, weapondynamic_t arg_thisWeapon, int shellCount, int attackTypeUsed)
{
#ifdef SERVER #ifdef SERVER
weapondata_gun_t baseRef = *((weapondata_gun_t*)basePRef); weapondata_gun_t baseRef = *((weapondata_gun_t*)basePRef);
@ -526,7 +534,8 @@ weapon_shotgun_reload(
// (don't do anything if not of course) // (don't do anything if not of course)
// Also, this is meant to completely replace the weapon_gun_onThink call that most would have used. // Also, this is meant to completely replace the weapon_gun_onThink call that most would have used.
void void
weapon_shotgun_onThink_reloadLogic(player pl, weapondata_gun_t* basePRef, weapondynamic_t arg_thisWeapon){ weapon_shotgun_onThink_reloadLogic(player pl, weapondata_gun_t* basePRef, weapondynamic_t arg_thisWeapon)
{
weapondata_gun_t baseRef = *basePRef; weapondata_gun_t baseRef = *basePRef;
weapondata_shotgun_extra_t* shotgunExtraRef; weapondata_shotgun_extra_t* shotgunExtraRef;
@ -1642,7 +1651,8 @@ weapon_gun_onThink_burstFireLogic(
void void
weapon_gun_onDrawHUD(player pl, weapondata_gun_t* basePRef, weapondynamic_t arg_thisWeapon){ weapon_gun_onDrawHUD(player pl, weapondata_gun_t* basePRef, weapondynamic_t arg_thisWeapon)
{
#ifdef CLIENT #ifdef CLIENT
weapondata_gun_t baseRef = *basePRef; weapondata_gun_t baseRef = *basePRef;
@ -1780,7 +1790,8 @@ weapon_gun_onDrawHUD(player pl, weapondata_gun_t* basePRef, weapondynamic_t arg_
void void
weapon_throwable_onDrawHUD(player pl, weapondata_throwable_t* basePRef, weapondynamic_t arg_thisWeapon){ weapon_throwable_onDrawHUD(player pl, weapondata_throwable_t* basePRef, weapondynamic_t arg_thisWeapon)
{
#ifdef CLIENT #ifdef CLIENT
weapondata_throwable_t baseRef = *basePRef; weapondata_throwable_t baseRef = *basePRef;
@ -1806,7 +1817,8 @@ weapon_throwable_onDrawHUD(player pl, weapondata_throwable_t* basePRef, weapondy
void void
weapon_melee_onDrawHUD(player pl, weapondata_melee_t* basePRef, weapondynamic_t arg_thisWeapon){ weapon_melee_onDrawHUD(player pl, weapondata_melee_t* basePRef, weapondynamic_t arg_thisWeapon)
{
#ifdef CLIENT #ifdef CLIENT
weapondata_melee_t baseRef = *basePRef; weapondata_melee_t baseRef = *basePRef;
@ -1830,16 +1842,25 @@ weapon_melee_onDrawHUD(player pl, weapondata_melee_t* basePRef, weapondynamic_t
}// weapon_throwable_onDrawHUD }// weapon_throwable_onDrawHUD
void void
weapon_ShowMuzzleFlash(int arg_muzzleFlashType){ weapon_ShowMuzzleFlash(int arg_muzzleFlashType)
{
weapon_ShowMuzzleFlashAkimbo(arg_muzzleFlashType, BITS_AKIMBOCHOICE_NONE);
}
void
weapon_ShowMuzzleFlashAkimbo(int arg_muzzleFlashType, int arg_akimboChoice)
{
#ifdef CLIENT #ifdef CLIENT
if(arg_muzzleFlashType != MUZZLEFLASH_ID::NONE){ if(arg_muzzleFlashType != MUZZLEFLASH_ID::NONE){
// not yet! // Set up an event to do this instead so that being called over and over by the client
//muzzleflashdata_t* tempRef = ary_muzzleFlashData[arg_muzzleFlashType]; // with packet delays (realistic network delays in multiplayer) don't cause the flash
//View_ShowMuzzleflash((*tempRef).iSpritePrecacheID); // to appear frozen until a server update.
player pl = (player)self; player pl = (player)self;
pl.iMuzzleFlashType = arg_muzzleFlashType; pl.iMuzzleFlashType = arg_muzzleFlashType;
pl.iMuzzleFlashAkimboChoice = arg_akimboChoice;
View_AddEvent(viewEv_weapon_ShowMuzzleFlash, 0.0f); View_AddEvent(viewEv_weapon_ShowMuzzleFlash, 0.0f);
} }
#else #else
@ -1848,18 +1869,25 @@ weapon_ShowMuzzleFlash(int arg_muzzleFlashType){
#endif #endif
} }
// Not to be confused with the clientside-only event "viewEv_weapon_EjectShell". // Not to be confused with the clientside-only event "viewEv_weapon_EjectShell".
// This is called by weapons to add the event, handles setting the shell-type global // This is called by weapons to add the event, handles setting the shell-type global
// for it to see. If shell-ejects are done serverside, that would not be an amazing // for it to see. If shell-ejects are done serverside, that would not be an amazing
// idea. Per-player would be better. // idea. Per-player would be better.
void void
weapon_EjectShell(int arg_shellEjectType) weapon_EjectShell(int arg_shellEjectType)
{
weapon_EjectShellAkimbo(arg_shellEjectType, BITS_AKIMBOCHOICE_NONE);
}
void
weapon_EjectShellAkimbo(int arg_shellEjectType, int arg_akimboChoice)
{ {
#ifdef CLIENT #ifdef CLIENT
player pl = (player)self;
if(arg_shellEjectType != SHELLEJECT_ID::NONE){ if(arg_shellEjectType != SHELLEJECT_ID::NONE){
player pl = (player)self;
pl.iShellEjectType = arg_shellEjectType; pl.iShellEjectType = arg_shellEjectType;
pl.iShellEjectAkimboChoice = arg_akimboChoice;
View_AddEvent(viewEv_weapon_EjectShell, 0.0f); View_AddEvent(viewEv_weapon_EjectShell, 0.0f);
} }
#else #else
@ -1867,14 +1895,30 @@ weapon_EjectShell(int arg_shellEjectType)
#endif #endif
} }
void
weapon_ClientEffects(int arg_muzzleFlashType, int arg_shellEjectType)
{
weapon_ClientEffectsAkimbo(arg_muzzleFlashType, arg_shellEjectType, BITS_AKIMBOCHOICE_NONE);
}
// Both weapon_ShowMuzzleFlash and weapon_EjectShell in one call for scheduling // Both weapon_ShowMuzzleFlash and weapon_EjectShell in one call for scheduling
// one event that calls both, otherwise, only either can happen (one event allowed; // one event that calls both, otherwise, only either can happen (one event allowed;
// any more and they just overwrite each other on being set) // any more and they just overwrite each other on being set)
void weapon_ClientEffects(int arg_muzzleFlashType, int arg_shellEjectType){ void
weapon_ClientEffectsAkimbo(int arg_muzzleFlashType, int arg_shellEjectType, int arg_akimboChoice)
{
#ifdef CLIENT #ifdef CLIENT
player pl = (player)self; player pl = (player)self;
pl.iMuzzleFlashType = arg_muzzleFlashType; if(arg_shellEjectType != SHELLEJECT_ID::NONE){
pl.iShellEjectType = arg_shellEjectType; pl.iShellEjectType = arg_shellEjectType;
pl.iShellEjectAkimboChoice = arg_akimboChoice;
}
if(arg_muzzleFlashType != MUZZLEFLASH_ID::NONE){
pl.iMuzzleFlashType = arg_muzzleFlashType;
pl.iMuzzleFlashAkimboChoice = arg_akimboChoice;
}
View_AddEvent(viewEv_weapon_ClientEffects, 0.0f); View_AddEvent(viewEv_weapon_ClientEffects, 0.0f);
#else #else
// ? // ?
@ -2015,7 +2059,8 @@ weapondata_basic_t weapon_none_akimbo =
//Or would it make sense to extern the structs here and keep this method in the same file? //Or would it make sense to extern the structs here and keep this method in the same file?
// (compile weapon-specific files first, then weapon.c) // (compile weapon-specific files first, then weapon.c)
void void
setupWeaponData(void){ setupWeaponData(void)
{
// for reference, old way. REDO THAT MACRO to involve g_weapons, described more below // for reference, old way. REDO THAT MACRO to involve g_weapons, described more below
//#define ASSIGN_WEAPONDATA(arg_constName) ary_weaponData[WEAPON_ID::##arg_constName] = (weapondata_basic_t*) &weapon_##arg_constName; //#define ASSIGN_WEAPONDATA(arg_constName) ary_weaponData[WEAPON_ID::##arg_constName] = (weapondata_basic_t*) &weapon_##arg_constName;
@ -2156,7 +2201,8 @@ setupWeaponData(void){
weapondata_basic_t* weapondata_basic_t*
getWeaponData(int arg_weaponID, BOOL arg_akimbo){ getWeaponData(int arg_weaponID, BOOL arg_akimbo)
{
weapondata_basic_t* basicP; weapondata_basic_t* basicP;
// no cap check for now // no cap check for now
@ -2235,8 +2281,6 @@ getAmmoTypeOfWeapon(int arg_weaponID)
// This is based off what the HL glock does, adjust with custom shell-choices per // This is based off what the HL glock does, adjust with custom shell-choices per
// different weapons that use them. // different weapons that use them.
// Is HL's "w_shotgun_ejectshell" even any different from this besides shell-model // Is HL's "w_shotgun_ejectshell" even any different from this besides shell-model
@ -2262,28 +2306,34 @@ viewEv_playShotgunInsertShellSound(void)
void void
viewEv_weapon_EjectShell(void) viewEv_weapon_EjectShell(void)
{ {
// Actually, queue a shell eject during the next LatePreDraw call
// (TS_View_DrawCustom to be more specific). This makes the generated shell
// position more accurate. It will use the player's tempvars to set up
// the shell for the akimbo choice like here would have.
player pl = (player)pSeat->m_ePlayer; player pl = (player)pSeat->m_ePlayer;
// TODO - how about a first-person check? Doesn't make sense if in thirdperson. pl.bShellEjectScheduled = TRUE;
// Although that might've been needed earlier unless this event-thing works fine for being /*
// a playermodel too. Players other than the local one being rendered and needing to drop
// shells, that sounds like a whole other story
if(pl.iShellEjectType != SHELLEJECT_ID::NONE){ if(pl.iShellEjectType != SHELLEJECT_ID::NONE){
CTSShellEject::generateForViewmodel(pl.iShellEjectType); CTSShellEject::generateForViewModelAkimbo(pl.iShellEjectType, pl.iShellEjectAkimboChoice);
} }
*/
} }
void void
viewEv_weapon_ShowMuzzleFlash(void){ viewEv_weapon_ShowMuzzleFlash(void)
{
player pl = (player)pSeat->m_ePlayer; player pl = (player)pSeat->m_ePlayer;
if(pl.iMuzzleFlashType != MUZZLEFLASH_ID::NONE){ if(pl.iMuzzleFlashType != MUZZLEFLASH_ID::NONE){
muzzleflashdata_t* tempRef = ary_muzzleFlashData[pl.iMuzzleFlashType]; muzzleflashdata_t* tempRef = ary_muzzleFlashData[pl.iMuzzleFlashType];
View_ShowMuzzleflash((*tempRef).iSpritePrecacheID); TS_View_ShowMuzzleflash(tempRef->iSpritePrecacheID, pl.iMuzzleFlashAkimboChoice);
} }
} }
void void
viewEv_weapon_ClientEffects(void){ viewEv_weapon_ClientEffects(void)
{
viewEv_weapon_EjectShell(); viewEv_weapon_EjectShell();
viewEv_weapon_ShowMuzzleFlash(); viewEv_weapon_ShowMuzzleFlash();
} }

View file

@ -54,7 +54,7 @@ void weapon_m61grenade_onThink(player pl, weapondynamic_t arg_thisWeapon){
pl.grenadeSpawnTime = -1; pl.grenadeSpawnTime = -1;
#ifdef SERVER #ifdef SERVER
weapon_m61grenade_spawnProjectile(pl, arg_thisWeapon, pl.grenadeToss, pl.grenadeHeldDuration); weapon_m61grenade_spawnProjectile(pl, arg_thisWeapon, pl.bGrenadeToss, pl.grenadeHeldDuration);
#endif #endif
} }
} }
@ -90,7 +90,7 @@ void weapon_m61grenade_onThink(player pl, weapondynamic_t arg_thisWeapon){
// should this be joined by else or no? // should this be joined by else or no?
if(pl.grenadeFireIndex == 1){ if(pl.grenadeFireIndex == 1){
// Release detected, do the throw anim + spawn the grenade soon // Release detected, do the throw anim + spawn the grenade soon
if(!pl.grenadeToss){ if(!pl.bGrenadeToss){
TS_Weapons_ViewAnimation(weaponseq_m61grenade::throw, 11.0f/30.0f); TS_Weapons_ViewAnimation(weaponseq_m61grenade::throw, 11.0f/30.0f);
}else{ }else{
TS_Weapons_ViewAnimation(weaponseq_m61grenade::throw_slide, 11.0f/30.0f); TS_Weapons_ViewAnimation(weaponseq_m61grenade::throw_slide, 11.0f/30.0f);
@ -196,9 +196,9 @@ void weapon_grenade_onInputPress(player pl, weapondynamic_t arg_thisWeapon, int
// Once already started, this can affect being a normal throw or slide-throw. // Once already started, this can affect being a normal throw or slide-throw.
if(pl.grenadeFireIndex == 0){ if(pl.grenadeFireIndex == 0){
if(attackTypeUsed & BITS_AKIMBOCHOICE_LEFT){ if(attackTypeUsed & BITS_AKIMBOCHOICE_LEFT){
pl.grenadeToss = FALSE; //throw, this was primary. pl.bGrenadeToss = FALSE; //throw, this was primary.
}else if(attackTypeUsed & BITS_AKIMBOCHOICE_RIGHT){ }else if(attackTypeUsed & BITS_AKIMBOCHOICE_RIGHT){
pl.grenadeToss = TRUE; //throw, this was sec. pl.bGrenadeToss = TRUE; //throw, this was sec.
} }
} }

View file

@ -161,9 +161,6 @@ void weapon_socommk23_akimbo_attack(player pl, weapondynamic_t arg_thisWeapon, i
// NOTE! weapon_akimbo_semiAttackChoice already handles telling to skip firing if // NOTE! weapon_akimbo_semiAttackChoice already handles telling to skip firing if
// pl.recentAttackHadAmmo == FALSE or finalAkimboChoice is 0 or -1, no need // pl.recentAttackHadAmmo == FALSE or finalAkimboChoice is 0 or -1, no need
// to keep track of w_attack_next per variant. // to keep track of w_attack_next per variant.
// Also,
//TODO - CRITICAL!
// Muzzle flashes and shell ejections for akimbo pending!
// because I am not copy/pasting this monstrosity 5 times. // because I am not copy/pasting this monstrosity 5 times.
finalAkimboChoice = weapon_akimbo_semiAttackChoice(pl, ary_weaponData[WEAPON_ID::SOCOMMK23_AKIMBO], arg_thisWeapon, attackTypeUsed); finalAkimboChoice = weapon_akimbo_semiAttackChoice(pl, ary_weaponData[WEAPON_ID::SOCOMMK23_AKIMBO], arg_thisWeapon, attackTypeUsed);
@ -315,14 +312,12 @@ void weapon_socommk23_akimbo_attack(player pl, weapondynamic_t arg_thisWeapon, i
weapon_base_setWholeAttackDelay(pl, (*ary_weaponData[pl.activeweapon]).fAttackDelay * 1); weapon_base_setWholeAttackDelay(pl, (*ary_weaponData[pl.activeweapon]).fAttackDelay * 1);
} }
/*
// TODO: akimbo support
if(!(arg_thisWeapon.iBitsUpgrade & BITS_WEAPONOPT_SILENCER) ){ if(!(arg_thisWeapon.iBitsUpgrade & BITS_WEAPONOPT_SILENCER) ){
weapon_ClientEffects(MUZZLEFLASH_ID::SMALL, SHELLEJECT_ID::GENERIC, finalAkimboChoice); weapon_ClientEffectsAkimbo(MUZZLEFLASH_ID::SMALL, SHELLEJECT_ID::GENERIC, finalAkimboChoice);
} else { } else {
weapon_EjectShell(SHELLEJECT_ID::GENERIC, finalAkimboChoice); weapon_EjectShellAkimbo(SHELLEJECT_ID::GENERIC, finalAkimboChoice);
} }
*/
}// weapon_socommk23_akimbo_attack }// weapon_socommk23_akimbo_attack