diff --git a/source/games/duke/CMakeLists.txt b/source/games/duke/CMakeLists.txt index a3f5478dd..bfedeea8f 100644 --- a/source/games/duke/CMakeLists.txt +++ b/source/games/duke/CMakeLists.txt @@ -32,6 +32,7 @@ set( PCH_SOURCES src/premap.cpp src/premap_d.cpp src/premap_r.cpp + src/prediction.cpp src/render.cpp src/sbar.cpp src/sbar_d.cpp diff --git a/source/games/duke/src/animatesprites_d.cpp b/source/games/duke/src/animatesprites_d.cpp index fbbfbe869..566352d55 100644 --- a/source/games/duke/src/animatesprites_d.cpp +++ b/source/games/duke/src/animatesprites_d.cpp @@ -36,6 +36,7 @@ source as it is released. #include "ns.h" #include "global.h" #include "game.h" +#include "prediction.h" #include "names_d.h" BEGIN_DUKE_NS diff --git a/source/games/duke/src/animatesprites_r.cpp b/source/games/duke/src/animatesprites_r.cpp index 688c750d8..0dc206c92 100644 --- a/source/games/duke/src/animatesprites_r.cpp +++ b/source/games/duke/src/animatesprites_r.cpp @@ -31,6 +31,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms #include "global.h" #include "game.h" #include "names_r.h" +#include "prediction.h" BEGIN_DUKE_NS diff --git a/source/games/duke/src/gameloop.cpp b/source/games/duke/src/gameloop.cpp index dce639542..e3a14c859 100644 --- a/source/games/duke/src/gameloop.cpp +++ b/source/games/duke/src/gameloop.cpp @@ -50,7 +50,7 @@ void GetNextInput(); static input_t inputfifo[MOVEFIFOSIZ][MAXPLAYERS]; static int movefifoend[MAXPLAYERS]; static int movefifoplc; - +static int bufferjitter; void clearfifo(void) { diff --git a/source/games/duke/src/global.h b/source/games/duke/src/global.h index 6604d9de9..5a808b961 100644 --- a/source/games/duke/src/global.h +++ b/source/games/duke/src/global.h @@ -89,25 +89,6 @@ G_EXTERN int8_t avgextbits; G_EXTERN int32_t movefifosendplc; G_EXTERN int32_t predictfifoplc; -G_EXTERN vec3_t mypos, omypos, myvel; -#define myx mypos.x -#define myy mypos.y -#define myz mypos.z -#define omyx omypos.x -#define omyy omypos.y -#define omyz omypos.z -G_EXTERN fix16_t myhoriz, omyhoriz, myhorizoff, omyhorizoff, myang, omyang; -G_EXTERN int16_t mycursectnum, myjumpingcounter; -G_EXTERN uint8_t myjumpingtoggle, myonground, myhardlanding, myreturntocenter; -G_EXTERN int16_t my_MotoSpeed; -G_EXTERN uint8_t my_NotOnWater, my_MotoOnGround; -G_EXTERN uint8_t my_moto_do_bump, my_moto_bump_fast, my_moto_on_oil, my_moto_on_mud; -G_EXTERN int16_t my_moto_bump, my_moto_bump_target, my_moto_turb; -G_EXTERN int32_t my_stairs; - -G_EXTERN vec3_t myposbak[MOVEFIFOSIZ]; -G_EXTERN fix16_t myhorizbak[MOVEFIFOSIZ], myangbak[MOVEFIFOSIZ]; -G_EXTERN int32_t myminlag[MAXPLAYERS], mymaxlag, otherminlag, bufferjitter; G_EXTERN int32_t g_networkBroadcastMode, g_movesPerPacket; diff --git a/source/games/duke/src/prediction.cpp b/source/games/duke/src/prediction.cpp new file mode 100644 index 000000000..a8149b712 --- /dev/null +++ b/source/games/duke/src/prediction.cpp @@ -0,0 +1,541 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1996, 2003 - 3D Realms Entertainment +Copyright (C) 2020 - Christoph Oelckers + +This file is part of Duke Nukem 3D version 1.5 - Atomic Edition + +Duke Nukem 3D is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +aint with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Original Source: 1996 - Todd Replogle +Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms +Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) +*/ +//------------------------------------------------------------------------- + +#include "ns.h" // Must come before everything else! + +#include "duke3d.h" +#include "sbar.h" +#include "screens.h" +#include "baselayer.h" +#include "m_argv.h" +#include "mapinfo.h" +#include "texturemanager.h" + +BEGIN_DUKE_NS + + +int myx, omyx, myxvel, myy, omyy, myyvel, myz, omyz, myzvel; +short myhoriz, omyhoriz, myhorizoff, omyhorizoff, globalskillsound; +short myang, omyang, mycursectnum, myjumpingcounter; +char myjumpingtoggle, myonground, myhardlanding,myreturntocenter; +int fakemovefifoplc; +int myxbak[MOVEFIFOSIZ], myybak[MOVEFIFOSIZ], myzbak[MOVEFIFOSIZ]; +int myhorizbak[MOVEFIFOSIZ]; +short myangbak[MOVEFIFOSIZ]; + + +void resetmys() +{ + myx = omyx = ps[myconnectindex].posx; + myy = omyy = ps[myconnectindex].posy; + myz = omyz = ps[myconnectindex].posz; + myxvel = myyvel = myzvel = 0; + myang = omyang = ps[myconnectindex].getang(); + myhoriz = omyhoriz = ps[myconnectindex].gethoriz(); + myhorizoff = omyhorizoff = ps[myconnectindex].gethorizof(); + mycursectnum = ps[myconnectindex].cursectnum; + myjumpingcounter = ps[myconnectindex].jumping_counter; + myjumpingtoggle = ps[myconnectindex].jumping_toggle; + myonground = ps[myconnectindex].on_ground; + myhardlanding = ps[myconnectindex].hard_landing; + myreturntocenter = ps[myconnectindex].return_to_center; + } + +#if 0 // todo: fix this when networking works again +void fakedomovethingscorrect(void) +{ + int i; + struct player_struct *p; + + if (numplayers < 2) return; + + i = ((movefifoplc-1)&(MOVEFIFOSIZ-1)); + p = &ps[myconnectindex]; + + if (p->posx == myxbak[i] && p->posy == myybak[i] && p->posz == myzbak[i] + && p->horiz == myhorizbak[i] && p->ang == myangbak[i]) return; + + myx = p->posx; omyx = p->oposx; myxvel = p->posxv; + myy = p->posy; omyy = p->oposy; myyvel = p->posyv; + myz = p->posz; omyz = p->oposz; myzvel = p->poszv; + myang = p->ang; omyang = p->oang; + mycursectnum = p->cursectnum; + myhoriz = p->horiz; omyhoriz = p->ohoriz; + myhorizoff = p->horizoff; omyhorizoff = p->ohorizoff; + myjumpingcounter = p->jumping_counter; + myjumpingtoggle = p->jumping_toggle; + myonground = p->on_ground; + myhardlanding = p->hard_landing; + myreturntocenter = p->return_to_center; + + fakemovefifoplc = movefifoplc; + while (fakemovefifoplc < movefifoend[myconnectindex]) + fakedomovethings(); + +} + +void fakedomovethings(void) +{ + input *syn; + struct player_struct *p; + int i, j, k, doubvel, fz, cz, hz, lz, x, y; + unsigned int sb_snum; + short psect, psectlotag, tempsect, backcstat; + uint8_t shrunk, spritebridge; + + syn = (input *)&inputfifo[fakemovefifoplc&(MOVEFIFOSIZ-1)][myconnectindex]; + + p = &ps[myconnectindex]; + + backcstat = sprite[p->i].cstat; + sprite[p->i].cstat &= ~257; + + sb_snum = syn->bits; + + psect = mycursectnum; + psectlotag = sector[psect].lotag; + spritebridge = 0; + + shrunk = (sprite[p->i].yrepeat < (isRR()? 8 : 32)); + + if( ud.clipping == 0 && ( sector[psect].floorpicnum == MIRROR || psect < 0 || psect >= MAXSECTORS) ) + { + myx = omyx; + myy = omyy; + } + else + { + omyx = myx; + omyy = myy; + } + + omyhoriz = myhoriz; + omyhorizoff = myhorizoff; + omyz = myz; + omyang = myang; + + getzrange(myx,myy,myz,psect,&cz,&hz,&fz,&lz,163L,CLIPMASK0); + + j = getflorzofslope(psect,myx,myy); + + if( (lz&49152) == 16384 && psectlotag == 1 && klabs(myz-j) > PHEIGHT+(16<<8) ) + psectlotag = 0; + + if( p->aim_mode == 0 && myonground && psectlotag != 2 && (sector[psect].floorstat&2) ) + { + x = myx+(sintable[(myang+512)&2047]>>5); + y = myy+(sintable[myang&2047]>>5); + tempsect = psect; + updatesector(x,y,&tempsect); + if (tempsect >= 0) + { + k = getflorzofslope(psect,x,y); + if (psect == tempsect) + myhorizoff += mulscale16(j-k,160); + else if (klabs(getflorzofslope(tempsect,x,y)-k) <= (4<<8)) + myhorizoff += mulscale16(j-k,160); + } + } + if (myhorizoff > 0) myhorizoff -= ((myhorizoff>>3)+1); + else if (myhorizoff < 0) myhorizoff += (((-myhorizoff)>>3)+1); + + if(hz >= 0 && (hz&49152) == 49152) + { + hz &= (MAXSPRITES-1); + if (sprite[hz].statnum == 1 && sprite[hz].extra >= 0) + { + hz = 0; + cz = getceilzofslope(psect,myx,myy); + } + } + + if(lz >= 0 && (lz&49152) == 49152) + { + j = lz&(MAXSPRITES-1); + if ((sprite[j].cstat&33) == 33) + { + psectlotag = 0; + spritebridge = 1; + } + if(badguy(&sprite[j]) && sprite[j].xrepeat > 24 && klabs(sprite[p->i].z-sprite[j].z) < (84<<8) ) + { + j = getangle( sprite[j].x-myx,sprite[j].y-myy); + myxvel -= sintable[(j+512)&2047]<<4; + myyvel -= sintable[j&2047]<<4; + } + } + + if( sprite[p->i].extra <= 0 ) + { + if( psectlotag == 2 ) + { + if(p->on_warping_sector == 0) + { + if( klabs(myz-fz) > (PHEIGHT>>1)) + myz += 348; + } + clipmove(&myx,&myy,&myz,&mycursectnum,0,0,164L,(4L<<8),(4L<<8),CLIPMASK0); + } + + updatesector(myx,myy,&mycursectnum); + pushmove(&myx,&myy,&myz,&mycursectnum,128L,(4L<<8),(20L<<8),CLIPMASK0); + + myhoriz = 100; + myhorizoff = 0; + + goto ENDFAKEPROCESSINPUT; + } + + doubvel = TICSPERFRAME; + + if(p->on_crane >= 0) goto FAKEHORIZONLY; + + if(p->one_eighty_count < 0) myang += 128; + + i = 40; + + if( psectlotag == 2) + { + myjumpingcounter = 0; + + if ( (sb_snum&1) && !(p->OnMotorcycle || p->OnBoat) ) + { + if(myzvel > 0) myzvel = 0; + myzvel -= 348; + if(myzvel < -(256*6)) myzvel = -(256*6); + } + else if ( (sb_snum&(1<<1)) && !(p->OnMotorcycle || p->OnBoat) ) + { + if(myzvel < 0) myzvel = 0; + myzvel += 348; + if(myzvel > (256*6)) myzvel = (256*6); + } + else + { + if(myzvel < 0) + { + myzvel += 256; + if(myzvel > 0) + myzvel = 0; + } + if(myzvel > 0) + { + myzvel -= 256; + if(myzvel < 0) + myzvel = 0; + } + } + + if(myzvel > 2048) myzvel >>= 1; + + myz += myzvel; + + if(myz > (fz-(15<<8)) ) + myz += ((fz-(15<<8))-myz)>>1; + + if(myz < (cz+(4<<8)) ) + { + myz = cz+(4<<8); + myzvel = 0; + } + } + + else if(p->jetpack_on) + { + myonground = 0; + myjumpingcounter = 0; + myhardlanding = 0; + + if(p->jetpack_on < 11) + myz -= (p->jetpack_on<<7); //Goin up + + if(shrunk) j = 512; + else j = 2048; + + if ((sb_snum&1) && !(p->OnMotorcycle || p->OnBoat)) + myz -= j; + if ((sb_snum&(1<<1)) && !(p->OnMotorcycle || p->OnBoat)) + myz += j; + + if(shrunk == 0 && ( psectlotag == 0 || psectlotag == 2 ) ) k = 32; + else k = 16; + + if(myz > (fz-(k<<8)) ) + myz += ((fz-(k<<8))-myz)>>1; + if(myz < (cz+(18<<8)) ) + myz = cz+(18<<8); + } + else if( psectlotag != 2 ) + { + if (psectlotag == 1 && p->spritebridge == 0) + { + if(shrunk == 0) i = 34; + else i = 12; + } + if(myz < (fz-(i<<8)) && (floorspace(psect)|ceilingspace(psect)) == 0 ) //falling + { + if( (sb_snum&3) == 0 && !(p->OnMotorcycle || p->OnBoat) && myonground && (sector[psect].floorstat&2) && myz >= (fz-(i<<8)-(16<<8) ) ) + myz = fz-(i<<8); + else + { + myonground = 0; + + myzvel += (gc+80); + + if(myzvel >= (4096+2048)) myzvel = (4096+2048); + } + } + + else + { + if(psectlotag != 1 && psectlotag != 2 && myonground == 0 && myzvel > (6144>>1)) + myhardlanding = myzvel>>10; + myonground = 1; + + if(i==40) + { + //Smooth on the ground + + k = ((fz-(i<<8))-myz)>>1; + if( klabs(k) < 256 ) k = 0; + myz += k; // ((fz-(i<<8))-myz)>>1; + myzvel -= 768; // 412; + if(myzvel < 0) myzvel = 0; + } + else if(myjumpingcounter == 0) + { + myz += ((fz-(i<<7))-myz)>>1; //Smooth on the water + if(p->on_warping_sector == 0 && myz > fz-(16<<8)) + { + myz = fz-(16<<8); + myzvel >>= 1; + } + } + + if( (sb_snum&2) && !(p->OnMotorcycle || p->OnBoat) ) + myz += (2048+768); + + if( (sb_snum&1) == 0 && !(p->OnMotorcycle || p->OnBoat) && myjumpingtoggle == 1) + myjumpingtoggle = 0; + + else if( (sb_snum&1) && !(p->OnMotorcycle || p->OnBoat) && myjumpingtoggle == 0 ) + { + if( myjumpingcounter == 0 ) + if( (fz-cz) > (56<<8) ) + { + myjumpingcounter = 1; + myjumpingtoggle = 1; + } + } + if(!isRR() && myjumpingcounter && (sb_snum&1) == 0 ) + myjumpingcounter = 0; + } + + if(myjumpingcounter) + { + if( (sb_snum&1) == 0 && !(p->OnMotorcycle || p->OnBoat) && myjumpingtoggle == 1) + myjumpingtoggle = 0; + + if( myjumpingcounter < (1024+256) ) + { + if(psectlotag == 1 && myjumpingcounter > 768) + { + myjumpingcounter = 0; + myzvel = -512; + } + else + { + myzvel -= (sintable[(2048-128+myjumpingcounter)&2047])/12; + myjumpingcounter += 180; + + myonground = 0; + } + } + else + { + myjumpingcounter = 0; + myzvel = 0; + } + } + + myz += myzvel; + + if(myz < (cz+(4<<8)) ) + { + myjumpingcounter = 0; + if(myzvel < 0) myxvel = myyvel = 0; + myzvel = 128; + myz = cz+(4<<8); + } + + } + + if ( p->fist_incs || + p->transporter_hold > 2 || + myhardlanding || + p->access_incs > 0 || + p->knee_incs > 0 || + (p->curr_weapon == TRIPBOMB_WEAPON && + p->kickback_pic > 1 && + p->kickback_pic < 4 ) ) + { + doubvel = 0; + myxvel = 0; + myyvel = 0; + } + else if ( syn->avel ) //p->ang += syncangvel * constant + { //ENGINE calculates angvel for you + int tempang; + + tempang = syn->avel<<1; + + if(psectlotag == 2) + myang += (tempang-(tempang>>3))*sgn(doubvel); + else myang += (tempang)*sgn(doubvel); + myang &= 2047; + } + + if ( myxvel || myyvel || syn->fvel || syn->svel ) + { + if(p->steroids_amount > 0 && p->steroids_amount < 400) + doubvel <<= 1; + + myxvel += ((syn->fvel*doubvel)<<6); + myyvel += ((syn->svel*doubvel)<<6); + + if( ( p->curr_weapon == KNEE_WEAPON && p->kickback_pic > 10 && myonground ) || ( myonground && (sb_snum&2) && !(p->OnMotorcycle || p->OnBoat)) ) + { + myxvel = mulscale16(myxvel,dukefriction-0x2000); + myyvel = mulscale16(myyvel,dukefriction-0x2000); + } + else + { + if(psectlotag == 2) + { + myxvel = mulscale16(myxvel,dukefriction-0x1400); + myyvel = mulscale16(myyvel,dukefriction-0x1400); + } + else + { + myxvel = mulscale16(myxvel,dukefriction); + myyvel = mulscale16(myyvel,dukefriction); + } + } + + if( abs(myxvel) < 2048 && abs(myyvel) < 2048 ) + myxvel = myyvel = 0; + + if( shrunk ) + { + myxvel = + mulscale16(myxvel,(dukefriction)-(dukefriction>>1)+(dukefriction>>2)); + myyvel = + mulscale16(myyvel,(dukefriction)-(dukefriction>>1)+(dukefriction>>2)); + } + } + +FAKEHORIZONLY: + if(psectlotag == 1 || spritebridge == 1) i = (4L<<8); else i = (20L<<8); + + clipmove(&myx,&myy,&myz,&mycursectnum,myxvel,myyvel,164L,4L<<8,i,CLIPMASK0); + pushmove(&myx,&myy,&myz,&mycursectnum,164L,4L<<8,4L<<8,CLIPMASK0); + + if( p->jetpack_on == 0 && psectlotag != 1 && psectlotag != 2 && shrunk) + myz += 30<<8; + + if ((sb_snum&(1<<18)) || myhardlanding) + myreturntocenter = 9; + + if (sb_snum&(1<<13)) + { + myreturntocenter = 9; + if (sb_snum&(1<<5)) myhoriz += 6; + myhoriz += 6; + } + else if (sb_snum&(1<<14)) + { + myreturntocenter = 9; + if (sb_snum&(1<<5)) myhoriz -= 6; + myhoriz -= 6; + } + else if ((sb_snum&(1<<3)) && !(p->OnMotorcycle || p->OnBoat)) + { + if (sb_snum&(1<<5)) myhoriz += 6; + myhoriz += 6; + } + else if ((sb_snum&(1<<4)) && !(p->OnMotorcycle || p->OnBoat)) + { + if (sb_snum&(1<<5)) myhoriz -= 6; + myhoriz -= 6; + } + + if (myreturntocenter > 0) + if ((sb_snum&(1<<13)) == 0 && (sb_snum&(1<<14)) == 0) + { + myreturntocenter--; + myhoriz += 33-(myhoriz/3); + } + + if(p->aim_mode) + myhoriz += syn->horz>>1; + else + { + if( myhoriz > 95 && myhoriz < 105) myhoriz = 100; + if( myhorizoff > -5 && myhorizoff < 5) myhorizoff = 0; + } + + if (myhardlanding > 0) + { + myhardlanding--; + myhoriz -= (myhardlanding<<4); + } + + if (myhoriz > 299) myhoriz = 299; + else if (myhoriz < -99) myhoriz = -99; + + if(p->knee_incs > 0) + { + myhoriz -= 48; + myreturntocenter = 9; + } + + +ENDFAKEPROCESSINPUT: + + myxbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myx; + myybak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myy; + myzbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myz; + myangbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myang; + myhorizbak[fakemovefifoplc&(MOVEFIFOSIZ-1)] = myhoriz; + fakemovefifoplc++; + + sprite[p->i].cstat = backcstat; +} +#endif + +END_DUKE_NS diff --git a/source/games/duke/src/prediction.h b/source/games/duke/src/prediction.h new file mode 100644 index 000000000..09b7b0288 --- /dev/null +++ b/source/games/duke/src/prediction.h @@ -0,0 +1,14 @@ +#pragma once + +BEGIN_DUKE_NS + +extern int myx, omyx, myxvel, myy, omyy, myyvel, myz, omyz, myzvel; +extern short myhoriz, omyhoriz, myhorizoff, omyhorizoff, globalskillsound; +extern short myang, omyang, mycursectnum, myjumpingcounter; +extern char myjumpingtoggle, myonground, myhardlanding,myreturntocenter; +extern int fakemovefifoplc; +extern int myxbak[MOVEFIFOSIZ], myybak[MOVEFIFOSIZ], myzbak[MOVEFIFOSIZ]; +extern int myhorizbak[MOVEFIFOSIZ]; +extern short myangbak[MOVEFIFOSIZ]; + +END_DUKE_NS diff --git a/source/games/duke/src/render.cpp b/source/games/duke/src/render.cpp index f2a7c1658..5b2e7c366 100644 --- a/source/games/duke/src/render.cpp +++ b/source/games/duke/src/render.cpp @@ -29,6 +29,7 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) #include "duke3d.h" #include "build.h" #include "v_video.h" +#include "prediction.h" BEGIN_DUKE_NS @@ -540,8 +541,8 @@ void displayrooms(int snum, int smoothratio) cang = omyang + mulscale16((int)(((myang + 1024 - omyang) & 2047) - 1024), smoothratio); choriz = omyhoriz + omyhorizoff + mulscale16((int)(myhoriz + myhorizoff - omyhoriz - omyhorizoff), smoothratio); #else - cang = myang >> 16; - choriz = (myhoriz + myhorizoff) >> 16; + cang = myang; + choriz = (myhoriz + myhorizoff); #endif sect = mycursectnum; } diff --git a/source/games/duke/src/zz_game.cpp b/source/games/duke/src/zz_game.cpp index 9474375ac..0101f6c1a 100644 --- a/source/games/duke/src/zz_game.cpp +++ b/source/games/duke/src/zz_game.cpp @@ -627,7 +627,7 @@ int GameInterface::app_main() ud.m_monsters_off = userConfig.nomonsters; g_movesPerPacket = 1; - bufferjitter = 1; + //bufferjitter = 1; initsynccrc(); // This needs to happen before G_CheckCommandLine() because G_GameExit() diff --git a/source/games/duke/src/zz_screens.cpp b/source/games/duke/src/zz_screens.cpp index 5912a5181..2bd873458 100644 --- a/source/games/duke/src/zz_screens.cpp +++ b/source/games/duke/src/zz_screens.cpp @@ -32,6 +32,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "mapinfo.h" #include "v_2ddrawer.h" #include "screenjob.h" +#include "prediction.h" BEGIN_DUKE_NS @@ -232,8 +233,8 @@ void G_DisplayRest(int32_t smoothratio) { if (screenpeek == myconnectindex && numplayers > 1) { - cposx = omypos.x + mulscale16(mypos.x-omypos.x, smoothratio); - cposy = omypos.y + mulscale16(mypos.y-omypos.y, smoothratio); + cposx = omyx + mulscale16(myx-omyx, smoothratio); + cposy = omyy + mulscale16(myy-omyy, smoothratio); cang = fix16_to_int(omyang) + mulscale16((fix16_to_int(myang+F16(1024)-omyang)&2047)-1024, smoothratio); } else