From 0c9b8537e415d68a8ff204ec54aaec42e54b545d Mon Sep 17 00:00:00 2001 From: Chris Dawalt Date: Sun, 15 Aug 2021 06:47:27 -0400 Subject: [PATCH] client effect reorganization and akimbo support (demo in socom), pmove slight reorganization --- src/client/draw.qc | 7 +- src/client/entity/ts_shelleject.h | 6 +- src/client/entity/ts_shelleject.qc | 62 +- src/client/player.h | 3 - src/client/player.qc | 478 --------------- src/client/progs.src | 1 - src/client/view.h | 6 +- src/client/view.qc | 562 ++++++++++++++++-- src/client/viewmodel.qc | 35 +- src/shared/player.h | 25 +- src/shared/player.qc | 12 +- src/shared/pmove.qc | 138 ++++- src/shared/util.h | 4 +- src/shared/weapons.h | 3 + src/shared/weapons.qc | 116 +++- src/shared/weapons/weapon_m61grenade.qc | 8 +- src/shared/weapons/weapon_socommk23_akimbo.qc | 11 +- 17 files changed, 827 insertions(+), 650 deletions(-) delete mode 100644 src/client/player.h diff --git a/src/client/draw.qc b/src/client/draw.qc index 7e0dd5c..0c4236b 100644 --- a/src/client/draw.qc +++ b/src/client/draw.qc @@ -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. // PostDraw further down is too late. // 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_DrawViewModelCustom(pl); + TS_View_DrawCustom(pl); int thirdperson = (autocvar_cl_thirdperson == TRUE || pl.entnum != player_localentnum); - Player_DrawViewModelExtraEffects(pl, thirdperson); + TS_View_DrawExtraEffects(pl, thirdperson); } diff --git a/src/client/entity/ts_shelleject.h b/src/client/entity/ts_shelleject.h index 55b7ff4..217a9a0 100644 --- a/src/client/entity/ts_shelleject.h +++ b/src/client/entity/ts_shelleject.h @@ -10,8 +10,12 @@ class CTSShellEject{ void(void) CTSShellEject; static void(void) precache; + 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; + virtual void(void) touch; }; diff --git a/src/client/entity/ts_shelleject.qc b/src/client/entity/ts_shelleject.qc index 8e489b1..aa2d2ab 100644 --- a/src/client/entity/ts_shelleject.qc +++ b/src/client/entity/ts_shelleject.qc @@ -51,15 +51,21 @@ CTSShellEject::generate(int arg_iShellEjectType){ //setmodel(eShell, "models/shell.mdl"); shellejectdata_t* mySED = ary_shellEjectData[eShell.iShellEjectType]; setmodel(eShell, (*mySED).sModelPath); + //setmodel(eShell, "models/powerup.mdl"); + eShell.solid = SOLID_BBOX; eShell.movetype = MOVETYPE_BOUNCE; eShell.drawmask = MASK_ENGINE; 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; makevectors(pSeat->m_eViewModel.angles); - eShell.velocity += (v_forward * 0); + eShell.velocity += (v_forward * 20); eShell.velocity += (v_right * 80); eShell.velocity += (v_up * 100); @@ -78,8 +84,29 @@ CTSShellEject::generate(int arg_iShellEjectType){ // The practical constructor, manually call this after spawning. // NOTE: assumes pSeat->m_eViewModel is valid for involving in determining // 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::generateForViewmodel(int arg_iShellEjectType) +CTSShellEject::generateForViewModel_attachments(int arg_iShellEjectType, int arg_attachment1, int arg_attachment2) { // give me an origin! CTSShellEject eShell = CTSShellEject::generate(arg_iShellEjectType); @@ -123,8 +150,8 @@ CTSShellEject::generateForViewmodel(int arg_iShellEjectType) vector angGun; - gunpos = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 0i); - gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + 3i); + gunpos = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + arg_attachment1); + gunpos_tempEnd = gettaginfo(pSeat->m_eViewModel, pSeat->m_iVMBones + arg_attachment2); dirGun = normalize(gunpos_tempEnd - gunpos); angGun = vectoangles(dirGun); 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 // weapon, left or right to use then (LEFT always a default, even if non-akimbo is ok too), // 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 += v_forward * -3.5; vOrigin += v_up * -0.02; @@ -144,9 +179,28 @@ CTSShellEject::generateForViewmodel(int arg_iShellEjectType) 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; } + // TODO. For playermodels as seen in thirdperson (local one) or looking at other players // that called for shell ejecting. CTSShellEject diff --git a/src/client/player.h b/src/client/player.h deleted file mode 100644 index 55d8e60..0000000 --- a/src/client/player.h +++ /dev/null @@ -1,3 +0,0 @@ - -void Player_DrawViewModelCustom(player pl); -void Player_DrawViewModelExtraEffects(player pl, int thirdperson); diff --git a/src/client/player.qc b/src/client/player.qc index 0f390be..60487b1 100644 --- a/src/client/player.qc +++ b/src/client/player.qc @@ -164,8 +164,6 @@ Player_DestroyWeaponModel(entity 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 // 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 - -} - - - - - - diff --git a/src/client/progs.src b/src/client/progs.src index c24c352..0e0e49d 100644 --- a/src/client/progs.src +++ b/src/client/progs.src @@ -42,7 +42,6 @@ entity/ts_shelleject.h clientinfo.h seatlocal.h -player.h precache.h ui.h diff --git a/src/client/view.h b/src/client/view.h index 42928d5..5592885 100644 --- a/src/client/view.h +++ b/src/client/view.h @@ -1,12 +1,14 @@ 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_RoutineCheck(void); void TS_View_ChangeViewModelPost(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); diff --git a/src/client/view.qc b/src/client/view.qc index 835b2ea..04e49e0 100644 --- a/src/client/view.qc +++ b/src/client/view.qc @@ -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 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. void TS_View_HandleZoom(void) { diff --git a/src/client/viewmodel.qc b/src/client/viewmodel.qc index c66e359..273cd03 100644 --- a/src/client/viewmodel.qc +++ b/src/client/viewmodel.qc @@ -66,38 +66,13 @@ Viewmodel_CalcBob(void) 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; - - if(pl.waterlevel < 2){ - // Easy, trsy pl.flags having FL_ONGROUND. - 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){ + // 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. + // TODO: may as well turn this into a separate bitflag too then. + if(!pl.bOnGround){ float timeTol; vector vSource = pl.origin; diff --git a/src/shared/player.h b/src/shared/player.h index e7a0350..2eb9cdf 100644 --- a/src/shared/player.h +++ b/src/shared/player.h @@ -157,9 +157,24 @@ class player:base_player // same as below for muzzleflash. int iMuzzleFlashType; + // and akimbo support + int iMuzzleFlashAkimboChoice; + + // 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; + // 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. // 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? float grenadeSpawnTime; 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 ////////////////////////////////////////////////////////////////////////////// - //for pmove to set - BOOL viewAboveWater; + // for custom pmove to set + // 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 // sure what flags are guaranteed open / nonconflicting with anything else. diff --git a/src/shared/player.qc b/src/shared/player.qc index 814eac6..e4b5511 100644 --- a/src/shared/player.qc +++ b/src/shared/player.qc @@ -203,7 +203,7 @@ player::ReceiveEntity(float new, float fl) nextAkimboAttackPreference = readbyte(); akimboDualFireToleranceTime = readfloat(); grenadeFireIndex = readbyte() - 1; - grenadeToss = readbyte(); + bGrenadeToss = readbyte(); armor = readbyte(); @@ -381,7 +381,7 @@ player::PredictPreFrame(void) SAVE_STATE(nextAkimboAttackPreference); SAVE_STATE(akimboDualFireToleranceTime); SAVE_STATE(grenadeFireIndex); - SAVE_STATE(grenadeToss); + SAVE_STATE(bGrenadeToss); SAVE_STATE(shotgunReloadIndex); @@ -480,7 +480,7 @@ player::PredictPostFrame(void) ROLL_BACK(nextAkimboAttackPreference); ROLL_BACK(akimboDualFireToleranceTime); ROLL_BACK(grenadeFireIndex); - ROLL_BACK(grenadeToss); + ROLL_BACK(bGrenadeToss); ROLL_BACK(shotgunReloadIndex); @@ -670,7 +670,7 @@ player::EvaluateEntity(void) SAVE_STATE(nextAkimboAttackPreference); SAVE_STATE(akimboDualFireToleranceTime); SAVE_STATE(grenadeFireIndex); - SAVE_STATE(grenadeToss); + SAVE_STATE(bGrenadeToss); SAVE_STATE(shotgunReloadIndex); @@ -825,7 +825,7 @@ player::SendEntity(entity ePEnt, float fChanged) WriteByte(MSG_ENTITY, nextAkimboAttackPreference); WriteFloat(MSG_ENTITY, akimboDualFireToleranceTime); WriteByte(MSG_ENTITY, grenadeFireIndex + 1); - WriteByte(MSG_ENTITY, grenadeToss); + WriteByte(MSG_ENTITY, bGrenadeToss); WriteByte(MSG_ENTITY, armor ); WriteByte(MSG_ENTITY, iTotalSlots ); @@ -1123,7 +1123,7 @@ player::reset(BOOL resetInventory){ grenadeFireIndex = -1; grenadeHeldDuration = -1; grenadeSpawnTime = -1; - grenadeToss = FALSE; + bGrenadeToss = FALSE; isReloading = FALSE; isChangingIronsight = FALSE; diff --git a/src/shared/pmove.qc b/src/shared/pmove.qc index 2b00810..dccba2b 100644 --- a/src/shared/pmove.qc +++ b/src/shared/pmove.qc @@ -269,6 +269,14 @@ player::Physics_WaterJump(void) float 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(); 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 // 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; } else { targetSpeed = serverkeyfloat("phy_maxspeed") * 1.0; @@ -292,6 +302,10 @@ player::Physics_MaxSpeed(void) if(time <= this.fMoveBlockDelay){ // 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); return targetSpeed; } @@ -314,6 +328,23 @@ player::Physics_InputPreMove(void) { 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 @@ -345,42 +376,87 @@ player::Physics_Run(void) // Copied from PMoveCustom_Categorize / PMove_Categorize, whatever we're calling it, // from Nuclide. // 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'. // 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. - int contents; - - contents = PMoveCustom_Contents(this.origin + this.mins + [0,0,1]); - if (contents & CONTENTBIT_WATER) { - contents = CONTENT_WATER; - } else if (contents & CONTENTBIT_SLIME) { - contents = CONTENT_SLIME; - } else if (contents & CONTENTBIT_LAVA) { - contents = CONTENT_LAVA; - } - - - //player pl = (player)self; - this.viewAboveWater = TRUE; - - // how far underwater are we? - if (contents < CONTENT_SOLID && !(this.flags & FL_ONLADDER)) { - //this.watertype = contents; +// But first some common sense checks with the existing determined pl.waterlevel. +// 0: not touching water at all +// 1: at least the bottom of the bounding box, not past the halfway point up +// 2: between the halfway point up and very close to the top +// 3: close to the very top or completely submerged +//... 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; + }else if (this.waterlevel == 3){ + // submerged so no + this.bViewAboveWater = FALSE; + }else{ + // == 2? Check + int 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. - if(PMoveCustom_Contents(this.origin + this.view_ofs - '0 0 5') & CONTENTBITS_FLUID){ - this.viewAboveWater = FALSE; + contents = PMoveCustom_Contents(this.origin + this.mins + [0,0,1]); + + if (contents & CONTENTBIT_WATER) { + contents = CONTENT_WATER; + } else if (contents & CONTENTBIT_SLIME) { + contents = CONTENT_SLIME; + } else if (contents & CONTENTBIT_LAVA) { + contents = CONTENT_LAVA; } - - } else { - //this.watertype = CONTENT_EMPTY; - ///this.waterlevel = 0; - } - ///////////////////////////////////////////////////////////////////////////// + + this.bViewAboveWater = TRUE; + + // 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 diff --git a/src/shared/util.h b/src/shared/util.h index 5d094e2..25d1446 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -234,8 +234,8 @@ if (pl.inputSecondaryTapFrameCount == 0)\ // OLD UNDERWATER CHECK. Using a new var for more accuracy. //#define WEAPON_UNDERWATER_CHECK pl.waterlevel >= 3 //#define WEAPON_UNDERWATER_CHECK_NOT pl.waterlevel < 3 -#define WEAPON_UNDERWATER_CHECK !pl.viewAboveWater -#define WEAPON_UNDERWATER_CHECK_NOT pl.viewAboveWater +#define WEAPON_UNDERWATER_CHECK !pl.bViewAboveWater +#define WEAPON_UNDERWATER_CHECK_NOT pl.bViewAboveWater diff --git a/src/shared/weapons.h b/src/shared/weapons.h index 605dfa1..5a4b91b 100644 --- a/src/shared/weapons.h +++ b/src/shared/weapons.h @@ -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_ShowMuzzleFlash(int arg_muzzleFlashType); +void weapon_ShowMuzzleFlashAkimbo(int arg_muzzleFlashType, int arg_akimboChoice); 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_ClientEffectsAkimbo(int arg_muzzleFlashType, int arg_shellEjectType, int arg_akimboChoice); void weapon_precache(weapondata_basic_t* basePRef); diff --git a/src/shared/weapons.qc b/src/shared/weapons.qc index 8ca68a0..c4fb3d4 100644 --- a/src/shared/weapons.qc +++ b/src/shared/weapons.qc @@ -9,7 +9,8 @@ weapon_t g_weapons[WEAPON_ID::LAST_ID]; void -weaponconfig_data_init(weaponconfig_data_t* arg_this){ +weaponconfig_data_init(weaponconfig_data_t* arg_this) +{ arg_this->ary_myWeapons_softMax = 0; for(int i = 0; i < AMMO_ID::LAST_ID; i++){ arg_this->ary_ammoTotal[i] = 0; @@ -30,7 +31,8 @@ weaponconfig_data_init(weaponconfig_data_t* arg_this){ void -weapon_base_setWholeAttackDelay(player pl, float amount){ +weapon_base_setWholeAttackDelay(player pl, float amount) +{ pl.w_attack_next = amount; pl.w_attack_akimbo_next = amount; @@ -43,14 +45,16 @@ weapon_base_setWholeAttackDelay(player pl, float amount){ // normal delay only? void -weapon_base_setLeftAttackDelay(player pl, float amount){ +weapon_base_setLeftAttackDelay(player pl, float amount) +{ pl.w_attack_next = amount; //SAVE_STATE(pl.w_attack_next); } // akimbo secondary delay only? void -weapon_base_setRightAttackDelay(player pl, float amount){ +weapon_base_setRightAttackDelay(player pl, float amount) +{ pl.w_attack_akimbo_next = amount; //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 // to set it to 0.3 seconds. void -weapon_base_setLeftAttackDelay_AtLeast(player pl, float amount){ +weapon_base_setLeftAttackDelay_AtLeast(player pl, float amount) +{ if(pl.w_attack_next <= amount){ pl.w_attack_next = amount; //SAVE_STATE(pl.w_attack_next); @@ -70,7 +75,8 @@ weapon_base_setLeftAttackDelay_AtLeast(player pl, float amount){ // akimbo secondary delay only? void -weapon_base_setRightAttackDelay_AtLeast(player pl, float amount){ +weapon_base_setRightAttackDelay_AtLeast(player pl, float amount) +{ if(pl.w_attack_akimbo_next <= amount){ pl.w_attack_akimbo_next = amount; //SAVE_STATE(pl.w_attack_akimbo_next); @@ -272,7 +278,8 @@ weapon_base_onPrimaryAttack_melee_fromCustomDirection( // Apparently serverside-only? ok... 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 @@ -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) // NOTE - checks for being unable to fire yet not included! 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 weapondata_gun_t baseRef = *((weapondata_gun_t*)basePRef); @@ -526,7 +534,8 @@ weapon_shotgun_reload( // (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. 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_shotgun_extra_t* shotgunExtraRef; @@ -1642,7 +1651,8 @@ weapon_gun_onThink_burstFireLogic( 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 weapondata_gun_t baseRef = *basePRef; @@ -1780,7 +1790,8 @@ weapon_gun_onDrawHUD(player pl, weapondata_gun_t* basePRef, weapondynamic_t arg_ 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 weapondata_throwable_t baseRef = *basePRef; @@ -1806,7 +1817,8 @@ weapon_throwable_onDrawHUD(player pl, weapondata_throwable_t* basePRef, weapondy 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 weapondata_melee_t baseRef = *basePRef; @@ -1830,16 +1842,25 @@ weapon_melee_onDrawHUD(player pl, weapondata_melee_t* basePRef, weapondynamic_t }// weapon_throwable_onDrawHUD + 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 if(arg_muzzleFlashType != MUZZLEFLASH_ID::NONE){ - // not yet! - //muzzleflashdata_t* tempRef = ary_muzzleFlashData[arg_muzzleFlashType]; - //View_ShowMuzzleflash((*tempRef).iSpritePrecacheID); + // Set up an event to do this instead so that being called over and over by the client + // with packet delays (realistic network delays in multiplayer) don't cause the flash + // to appear frozen until a server update. player pl = (player)self; pl.iMuzzleFlashType = arg_muzzleFlashType; + pl.iMuzzleFlashAkimboChoice = arg_akimboChoice; View_AddEvent(viewEv_weapon_ShowMuzzleFlash, 0.0f); } #else @@ -1848,18 +1869,25 @@ weapon_ShowMuzzleFlash(int arg_muzzleFlashType){ #endif } - // 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 // for it to see. If shell-ejects are done serverside, that would not be an amazing // idea. Per-player would be better. + void weapon_EjectShell(int arg_shellEjectType) +{ + weapon_EjectShellAkimbo(arg_shellEjectType, BITS_AKIMBOCHOICE_NONE); +} + +void +weapon_EjectShellAkimbo(int arg_shellEjectType, int arg_akimboChoice) { #ifdef CLIENT - player pl = (player)self; if(arg_shellEjectType != SHELLEJECT_ID::NONE){ + player pl = (player)self; pl.iShellEjectType = arg_shellEjectType; + pl.iShellEjectAkimboChoice = arg_akimboChoice; View_AddEvent(viewEv_weapon_EjectShell, 0.0f); } #else @@ -1867,14 +1895,30 @@ weapon_EjectShell(int arg_shellEjectType) #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 // one event that calls both, otherwise, only either can happen (one event allowed; // 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 player pl = (player)self; - pl.iMuzzleFlashType = arg_muzzleFlashType; - pl.iShellEjectType = arg_shellEjectType; + if(arg_shellEjectType != SHELLEJECT_ID::NONE){ + 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); #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? // (compile weapon-specific files first, then weapon.c) void -setupWeaponData(void){ +setupWeaponData(void) +{ // 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; @@ -2156,7 +2201,8 @@ setupWeaponData(void){ weapondata_basic_t* -getWeaponData(int arg_weaponID, BOOL arg_akimbo){ +getWeaponData(int arg_weaponID, BOOL arg_akimbo) +{ weapondata_basic_t* basicP; // 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 // different weapons that use them. // Is HL's "w_shotgun_ejectshell" even any different from this besides shell-model @@ -2262,28 +2306,34 @@ viewEv_playShotgunInsertShellSound(void) 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; - // 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 + pl.bShellEjectScheduled = TRUE; + /* if(pl.iShellEjectType != SHELLEJECT_ID::NONE){ - CTSShellEject::generateForViewmodel(pl.iShellEjectType); + CTSShellEject::generateForViewModelAkimbo(pl.iShellEjectType, pl.iShellEjectAkimboChoice); } + */ } void -viewEv_weapon_ShowMuzzleFlash(void){ +viewEv_weapon_ShowMuzzleFlash(void) +{ player pl = (player)pSeat->m_ePlayer; if(pl.iMuzzleFlashType != MUZZLEFLASH_ID::NONE){ muzzleflashdata_t* tempRef = ary_muzzleFlashData[pl.iMuzzleFlashType]; - View_ShowMuzzleflash((*tempRef).iSpritePrecacheID); + TS_View_ShowMuzzleflash(tempRef->iSpritePrecacheID, pl.iMuzzleFlashAkimboChoice); } } void -viewEv_weapon_ClientEffects(void){ +viewEv_weapon_ClientEffects(void) +{ viewEv_weapon_EjectShell(); viewEv_weapon_ShowMuzzleFlash(); } diff --git a/src/shared/weapons/weapon_m61grenade.qc b/src/shared/weapons/weapon_m61grenade.qc index 27f2267..a81effc 100644 --- a/src/shared/weapons/weapon_m61grenade.qc +++ b/src/shared/weapons/weapon_m61grenade.qc @@ -54,7 +54,7 @@ void weapon_m61grenade_onThink(player pl, weapondynamic_t arg_thisWeapon){ pl.grenadeSpawnTime = -1; #ifdef SERVER - weapon_m61grenade_spawnProjectile(pl, arg_thisWeapon, pl.grenadeToss, pl.grenadeHeldDuration); + weapon_m61grenade_spawnProjectile(pl, arg_thisWeapon, pl.bGrenadeToss, pl.grenadeHeldDuration); #endif } } @@ -90,7 +90,7 @@ void weapon_m61grenade_onThink(player pl, weapondynamic_t arg_thisWeapon){ // should this be joined by else or no? if(pl.grenadeFireIndex == 1){ // 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); }else{ 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. if(pl.grenadeFireIndex == 0){ 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){ - pl.grenadeToss = TRUE; //throw, this was sec. + pl.bGrenadeToss = TRUE; //throw, this was sec. } } diff --git a/src/shared/weapons/weapon_socommk23_akimbo.qc b/src/shared/weapons/weapon_socommk23_akimbo.qc index b7025d1..279fa03 100644 --- a/src/shared/weapons/weapon_socommk23_akimbo.qc +++ b/src/shared/weapons/weapon_socommk23_akimbo.qc @@ -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 // pl.recentAttackHadAmmo == FALSE or finalAkimboChoice is 0 or -1, no need // 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. 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); } - /* - // TODO: akimbo support 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 { - weapon_EjectShell(SHELLEJECT_ID::GENERIC, finalAkimboChoice); + weapon_EjectShellAkimbo(SHELLEJECT_ID::GENERIC, finalAkimboChoice); } - */ + }// weapon_socommk23_akimbo_attack