334 lines
10 KiB
Plaintext
334 lines
10 KiB
Plaintext
/*
|
|
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
|
|
// Also, Nuclide offers Weapons_SetGeomset for calling "setcustomskin" too, but one string
|
|
// at a time. Don't really know if that way or the current way (cumulative string for
|
|
// many commands delimited in one setcustomskin call) is any better, so leaving this as
|
|
// it is for now.
|
|
|
|
|
|
void
|
|
TS_SetViewModelFromStats(void)
|
|
{
|
|
player pl = (player)pSeat->m_ePlayer;
|
|
entity vm = pSeat->m_eViewModel;
|
|
entity mflash = pSeat->m_eMuzzleflash;
|
|
weapondata_basic_t* basicP;
|
|
weapondynamic_t dynaRef;
|
|
|
|
printfline("TS_SetViewModelFromStats: I happen? activeweap:%d", pl.activeweapon);
|
|
|
|
basicP = pl.getEquippedWeaponData();
|
|
dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
|
|
|
//TAGGG NOTE - we're not supporting skins apparently
|
|
//if (autocvar_skins_dir != "") {
|
|
// wm = sprintf("skins/%s/%s", autocvar_skins_dir, sViewModels[ aw - 1 ]);
|
|
//} else {
|
|
// wm = sprintf("models/%s", sViewModels[ aw - 1 ]);
|
|
//}
|
|
//TAGGG NOTE - we're not using that "sViewModels" string either.
|
|
// Grab the currently equipped weapon, its weapon info, and from there pull the viewmodel to use
|
|
weapondata_basic_t* basicPointer = (weapondata_basic_t*) ary_weaponData[dynaRef.weaponID];
|
|
weapondata_basic_t basicRef = *(basicPointer);
|
|
|
|
|
|
|
|
Weapons_SetModel((*basicP).sViewModelPath);
|
|
|
|
string cumulativeCommandString = "";
|
|
|
|
// If this weapon is a Gun (or ironsight-able), check for the presence of attachment-giving
|
|
// buyopts.
|
|
if(basicRef.typeID == WEAPONDATA_TYPEID_GUN || basicRef.typeID == WEAPONDATA_TYPEID_IRONSIGHT){
|
|
weapondata_gun_t gunRef = *((weapondata_gun_t*)basicPointer);
|
|
|
|
if(dynaRef.iBitsUpgrade & BITS_WEAPONOPT_SILENCER){
|
|
// has the silencer? and an attachment?
|
|
if(gunRef.silencer_part != -1){
|
|
cumulativeCommandString = sprintf("%sgeomset %i 2\n", cumulativeCommandString, gunRef.silencer_part );
|
|
}
|
|
}
|
|
if(dynaRef.iBitsUpgrade & BITS_WEAPONOPT_LASERSIGHT){
|
|
// has the silencer? and an attachment?
|
|
if(gunRef.lasersight_part != -1){
|
|
cumulativeCommandString = sprintf("%sgeomset %i 2\n", cumulativeCommandString, gunRef.lasersight_part );
|
|
}
|
|
}
|
|
if(dynaRef.iBitsUpgrade & BITS_WEAPONOPT_FLASHLIGHT){
|
|
// has the silencer? and an attachment?
|
|
if(gunRef.flashlight_part != -1){
|
|
cumulativeCommandString = sprintf("%sgeomset %i 2\n", cumulativeCommandString, gunRef.flashlight_part );
|
|
}
|
|
}
|
|
if(dynaRef.iBitsUpgrade & BITS_WEAPONOPT_SCOPE){
|
|
// has the silencer? and an attachment?
|
|
if(gunRef.scope_part != -1){
|
|
cumulativeCommandString = sprintf("%sgeomset %i 2\n", cumulativeCommandString, gunRef.scope_part );
|
|
}
|
|
}
|
|
}//END OF gun type check (is a gun of some sort)
|
|
|
|
if(dynaRef.forceBodygroup1Submodel > 0){
|
|
cumulativeCommandString = sprintf("%sgeomset 0 %i\n", cumulativeCommandString, dynaRef.forceBodygroup1Submodel );
|
|
}
|
|
// no need to do that geomset with the same value again.
|
|
pl.prev_forceBodygroup1Submodel = dynaRef.forceBodygroup1Submodel;
|
|
|
|
setcustomskin(vm, "", cumulativeCommandString);
|
|
|
|
|
|
// leftovers following a draw call, that likely lead here to begin with.
|
|
SAVE_STATE(pl.w_attack_next);
|
|
SAVE_STATE(pl.w_idle_next);
|
|
SAVE_STATE(pl.viewzoom);
|
|
SAVE_STATE(pl.weapontime);
|
|
|
|
skel_delete( mflash.skeletonindex );
|
|
mflash.skeletonindex = skel_create( vm.modelindex );
|
|
pSeat->m_iVMBones = skel_get_numbones( mflash.skeletonindex ) + 1;
|
|
|
|
// Don't let View_UpdateWeapon do this all over.
|
|
pSeat->m_iLastWeapon = pl.activeweapon;
|
|
|
|
|
|
}//TS_SetViewModelFromStats
|
|
|
|
|
|
// On any frame, see if something about the current viewmodel needs to be changed
|
|
void
|
|
View_RoutineCheck(void)
|
|
{
|
|
|
|
player pl = (player)pSeat->m_ePlayer;
|
|
entity vm = pSeat->m_eViewModel;
|
|
weapondynamic_t dynaRef;
|
|
|
|
// That can happen, apparently.
|
|
if(pl.inventoryEquippedIndex == -1){
|
|
return;
|
|
}
|
|
|
|
dynaRef = pl.ary_myWeapons[pl.inventoryEquippedIndex];
|
|
|
|
if(pl.prev_forceBodygroup1Submodel != dynaRef.forceBodygroup1Submodel){
|
|
|
|
if(dynaRef.forceBodygroup1Submodel > 0){
|
|
string commandString;
|
|
commandString = sprintf("geomset 0 %i\n", dynaRef.forceBodygroup1Submodel );
|
|
setcustomskin(vm, "", commandString);
|
|
}
|
|
pl.prev_forceBodygroup1Submodel = dynaRef.forceBodygroup1Submodel;
|
|
}
|
|
|
|
}//View_RoutineCheck
|
|
|
|
|
|
// vm is pSeat->m_eViewModel passed along from Nuclide's View_DrawViewModel
|
|
void
|
|
View_UpdateWeapon(entity vm, entity mflash)
|
|
{
|
|
player pl = (player)pSeat->m_ePlayer;
|
|
|
|
|
|
if(autocvar_cl_printoutspam == 1){
|
|
printfline("STATUS: %i==%d - %i", pSeat->m_iLastWeapon, pl.activeweapon, pl.inventoryEquippedIndex);
|
|
}
|
|
|
|
|
|
|
|
/* only bother upon change */
|
|
if (pSeat->m_iLastWeapon == pl.activeweapon) {
|
|
|
|
/*
|
|
if(pl.activeweapon == 0 && pl.inventoryEquippedIndex != -1){
|
|
// what??
|
|
pl.setInventoryEquippedIndex(pl.inventoryEquippedIndex);
|
|
}else{
|
|
View_RoutineCheck();
|
|
return;
|
|
}
|
|
*/
|
|
View_RoutineCheck();
|
|
return;
|
|
}
|
|
printfline("View_UpdateWeapon: change detected: %i -> %d", pSeat->m_iLastWeapon, pl.activeweapon);
|
|
printfline("and how about the others %d %i", pl.activeweapon, pl.inventoryEquippedIndex);
|
|
|
|
|
|
pSeat->m_iOldWeapon = pSeat->m_iLastWeapon;
|
|
pSeat->m_iLastWeapon = pl.activeweapon;
|
|
|
|
if (!pl.activeweapon /*|| pl.inventoryEquippedIndex < 0*/) {
|
|
// can't work with this!
|
|
resetViewModel();
|
|
return;
|
|
}
|
|
|
|
//printfline("View_UpdateWeapon: change: %d vs %d", pSeat->m_iLastWeapon, pl.activeweapon);
|
|
|
|
// Call this to do a few other things for any weapon change
|
|
TS_Weapon_Draw_extra();
|
|
|
|
// hack, we changed the wep, move this into Game_Input/PMove
|
|
//... And yes, it seems this bound check is indeed needed, zany sync issues.
|
|
if(pl.inventoryEquippedIndex != -1){
|
|
Weapons_Draw();
|
|
}
|
|
|
|
|
|
|
|
//No longer a var! Oops
|
|
//pSeat->m_iVMEjectBone = pSeat->m_iVMBones + 1;
|
|
|
|
/* we forced a weapon call outside the prediction,
|
|
* thus we need to update all the net variables to
|
|
* make sure these updates are recognized. this is
|
|
* vile but it'll have to do for now */
|
|
|
|
SAVE_STATE(pl.w_attack_next);
|
|
SAVE_STATE(pl.w_idle_next);
|
|
SAVE_STATE(pl.viewzoom);
|
|
SAVE_STATE(pl.weapontime);
|
|
//TAGGG - NEW VAR.
|
|
SAVE_STATE(pl.w_attack_akimbo_next);
|
|
|
|
//TAGGG - also new line. It is a good idea to reset the event
|
|
// on changing models, right?
|
|
pSeat->m_pEventCall = NULL;
|
|
|
|
skel_delete( mflash.skeletonindex );
|
|
mflash.skeletonindex = skel_create( vm.modelindex );
|
|
pSeat->m_iVMBones = skel_get_numbones( mflash.skeletonindex ) + 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
void
|
|
resetViewModel(void)
|
|
{
|
|
printfline("resetViewModel called.");
|
|
|
|
if(pSeat->m_ePlayer == NULL){
|
|
printfline("resetViewModel: early end #1, client ent NULL");
|
|
return;
|
|
}
|
|
|
|
|
|
//was m_eViewModel and m_eMuzzleflash
|
|
entity vm = pSeat->m_eViewModel;
|
|
entity mflash = pSeat->m_eMuzzleflash;
|
|
|
|
setmodel( vm, "" );
|
|
skel_delete( mflash.skeletonindex );
|
|
mflash.skeletonindex = 0; //er wat. would -1 be better or not?
|
|
pSeat->m_iVMBones = 0 + 1;
|
|
//pSeat->m_iVMEjectBone = pSeat->m_iVMBones + 1;
|
|
|
|
/*
|
|
player pl = (player) pSeat->m_ePlayer;
|
|
pl.setInventoryEquippedIndex(-1); //do we have to?
|
|
*/
|
|
|
|
}// resetViewModel
|
|
|
|
|
|
|
|
//TAGGG - loaned from The Wastes.
|
|
void View_HandleZoom(void){
|
|
player pl = (player)pSeat->m_ePlayer;
|
|
|
|
if(pl == NULL){
|
|
return;
|
|
}
|
|
|
|
//TAGGG - any references to STAT_VIEWZOOM are now garbage.
|
|
// Rely on pl.flTargetZoom instead, it's sent to the client every frame
|
|
// and should be handled serverside anyway.
|
|
// flCurrentZoom is actually the "target" zoom we want to reach.
|
|
// flOldZoom is the zoom we started at, at the time of the change.
|
|
// flZoomLerp is how far along we are from oldZoom to currentZoom.
|
|
// flZoomLevel is the actual zoom we are at this very moment.
|
|
|
|
if (pl.flCurrentZoom != pl.flTargetZoom ) {
|
|
pl.flOldZoom = pl.flCurrentZoom;
|
|
pl.flCurrentZoom = pl.flTargetZoom ;
|
|
pl.flZoomLerp = 0.0f;
|
|
}
|
|
|
|
if (pl.flZoomLerp < 1.0f) {
|
|
// Slow this down for debugging purposes, this is meant to be fast.
|
|
// 0.8 can be safe.
|
|
// The Wastes default was 4.
|
|
|
|
pl.flZoomLerp += clframetime * 8;
|
|
|
|
if(pl.flZoomLerp >= 1.0){
|
|
//enforce the cap.
|
|
pl.flZoomLerp = 1.0;
|
|
}
|
|
|
|
//pl.flZoomLevel = getstatf(STAT_VIEWZOOM);
|
|
pl.flZoomLevel = Math_Lerp(pl.flOldZoom, pl.flCurrentZoom, pl.flZoomLerp);
|
|
}
|
|
|
|
// Set this, since Nuclide will read it in and apply instantly.
|
|
// So same effect without having to edit Nuclide, this pipes it over for it
|
|
// to do the setproperty thing below to apply
|
|
pl.viewzoom = pl.flZoomLevel;
|
|
|
|
////setproperty(VF_AFOV, DEFAULT_FOV * pl.flZoomLevel);
|
|
////setsensitivityscaler(pl.flZoomLevel);
|
|
|
|
// here's the way old FreeCS did it
|
|
//setproperty(VF_AFOV, cvar("fov") * pl.viewzoom);
|
|
//setsensitivityscaler(pl.viewzoom);
|
|
}
|
|
|
|
|
|
|
|
|
|
//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)
|
|
{
|
|
// View_SetMuzzleflash
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|