raze-gles/source/blood/src/common_game.h
2019-11-05 20:16:53 +01:00

940 lines
22 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
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
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#pragma once
#include "baselayer.h"
#include "build.h"
#include "cache1d.h"
#include "common.h"
#include "pragmas.h"
#include "misc.h"
#include "network.h"
#include "printf.h"
BEGIN_BLD_NS
#define BLOODWIDESCREENDEF "blood_widescreen.def"
#define BYTEVERSION 102
#define EXEVERSION 101
void _SetErrorLoc(const char *pzFile, int nLine);
void __dassert(const char *pzExpr, const char *pzFile, int nLine);
void QuitGame(void);
void _consoleSysMsg(const char* pMessage, ...);
#define ThrowError(...) \
{ \
_SetErrorLoc(__FILE__,__LINE__); \
I_Error(__VA_ARGS__); \
}
// print error to console only
#define consoleSysMsg(...) \
{ \
_SetErrorLoc(__FILE__,__LINE__); \
_consoleSysMsg(__VA_ARGS__); \
}
#define dassert(x) assert(x)
#define kMaxSectors MAXSECTORS
#define kMaxWalls MAXWALLS
#define kMaxSprites MAXSPRITES
#define kMaxTiles MAXTILES
#define kMaxStatus MAXSTATUS
#define kMaxPlayers 8
#define kMaxViewSprites maxspritesonscreen
#define kMaxVoxels MAXVOXELS
#define kTicRate 120
#define kTicsPerFrame 4
#define kTicsPerSec (kTicRate/kTicsPerFrame)
#define TILTBUFFER 4078
#define kExplodeMax 8
#define kLensSize 80
#define kViewEffectMax 19
#define kNoTile -1
// defined by NoOne:
// -------------------------------
#define kMaxPAL 5
#define kFreeQAVEntry 108
// MEDIUM /////////////////////////////////////////////////////
enum {
kMediumNormal = 0,
kMediumWater = 1,
kMediumGoo = 2,
};
// STATNUMS /////////////////////////////////////////////////////
enum {
kStatNothing = -1,
kStatDecoration = 0,
kStatFX = 1,
kStatExplosion = 2,
kStatItem = 3,
kStatThing = 4,
kStatProjectile = 5,
kStatDude = 6,
kStatInactive = 7, // inactive (ambush) dudes
kStatRespawn = 8,
kStatPurge = 9,
kStatMarker = 10,
kStatTraps = 11,
kStatAmbience = 12,
kStatSpares = 13,
kStatFlare = 14,
kStatDebris = 15,
kStatPathMarker = 16,
kStatModernDudeTargetChanger = 20, // gModernMap only
kStatFree = 1024,
};
// POWERUPS /////////////////////////////////////////////////////
enum {
kPwUpFeatherFall = 12,
kPwUpShadowCloak = 13,
kPwUpDeathMask = 14,
kPwUpJumpBoots = 15,
kPwUpTwoGuns = 17,
kPwUpDivingSuit = 18,
kPwUpGasMask = 19,
kPwUpCrystalBall = 21,
kPwUpDoppleganger = 23,
kPwUpReflectShots = 24,
kPwUpBeastVision = 25,
kPwUpShadowCloakUseless = 26,
kPwUpDeliriumShroom = 28,
kPwUpGrowShroom = 29,
kPwUpShrinkShroom = 30,
kPwUpDeathMaskUseless = 31,
kPwUpAsbestArmor = 39,
kMaxPowerUps = 51,
};
// SPRITE TYPES /////////////////////////////////////////////////
enum {
kSpriteDecoration = 0,
// markers
kMarkerSPStart = 1,
kMarkerMPStart = 2,
kMarkerOff = 3,
kMarkerOn = 4,
kMarkerAxis = 5,
kMarkerLowLink = 6,
kMarkerUpLink = 7,
kMarkerWarpDest = 8,
kMarkerUpWater = 9,
kMarkerLowWater = 10,
kMarkerUpStack = 11,
kMarkerLowStack = 12,
kMarkerUpGoo = 13,
kMarkerLowGoo = 14,
kMarkerPath = 15,
kMarkerDudeSpawn = 18,
kMarkerEarthQuake = 19,
// switches
kSwitchBase = 20,
kSwitchToggle = 20,
kSwitchOneWay = 21,
kSwitchCombo = 22,
kSwitchPadlock = 23,
kSwitchMax = 24,
// modern types (gModernMap only)
kModernCustomDudeSpawn = 24,
kModernRandomTX = 25,
kModernSequentialTX = 26,
kModernSeqSpawner = 27,
kModernObjPropertiesChanger = 28,
kModernObjPicnumChanger = 29,
kModernObjSizeChanger = 31,
kModernDudeTargetChanger = 33,
kModernSectorFXChanger = 34,
kModernObjDataChanger = 35,
kModernSpriteDamager = 36,
kModernObjDataAccumulator = 37,
kModernEffectSpawner = 38,
kModernWindGenerator = 39,
kModernConcussSprite = 712, /// WIP
kModernPlayerControl = 500, /// WIP
// decorations
kDecorationTorch = 30,
kDecorationCandle = 32,
// (weapons)
kItemWeaponBase = 40,
kModernRandom = 40, // gModernMap only
kItemWeaponSawedoff = 41,
kItemWeaponTommygun = 42,
kItemWeaponVoodooDoll = 44,
kItemWeaponLifeLeech = 50,
kItemWeaponMax = 51,
// items (ammos)
kItemAmmoBase = 60,
kItemAmmoSawedoffFew = 67,
kItemAmmoTommygunFew = 69,
kAmmoItemVoodooDoll = 70,
kModernRandom2 = 80, // gModernMap Only
kItemAmmoMax = 81,
kItemBase = 100,
// items (keys)
kItemKeyBase = kItemBase,
kItemKeySkull = kItemKeyBase,
kItemKeyEye = 101,
kItemKeyFire = 102,
kItemKeyDagger = 103,
kItemKeySpider = 104,
kItemKeyMoon = 105,
kItemKeyKey7 = 106,
kItemKeyMax = 107,
// items (health)
kItemHealthDoctorBag = 107,
kItemHealthMedPouch = 108,
kItemHealthLifeEssense = 109,
kItemHealthLifeSeed = 110,
kItemHealthRedPotion = 111,
// items (misc)
kItemFeatherFall = 112,
kItemShadowCloak = 113, // ltdInvisibility
kItemDeathMask = 114, // invulnerability
kItemJumpBoots = 115,
kItemTwoGuns = 117,
kItemDivingSuit = 118,
kItemGasMask = 119,
kItemCrystalBall = 121,
kItemReflectShots = 124,
kItemBeastVision = 125,
kItemShroomDelirium = 128,
kItemShroomGrow = 129, // gModernMap only
kItemShroomShrink = 130, // gModernMap only
kItemArmorAsbest = 139,
kItemArmorBasic = 140,
kItemArmorBody = 141,
kItemArmorFire = 142,
kItemArmorSpirit = 143,
kItemArmorSuper = 144,
kItemFlagABase = 145,
kItemFlagBBase = 146,
kItemFlagA = 147,
kItemFlagB = 148,
kItemModernMapLevel = 150, // once picked up, draws whole minimap
kItemMax = 151,
// dudes
kDudeBase = 200,
kDudeCultistTommy = 201,
kDudeCultistShotgun = 202,
kDudeZombieAxeNormal = 203,
kDudeZombieButcher = 204,
kDudeZombieAxeBuried = 205,
kDudeGargoyleFlesh = 206,
kDudeGargoyleStone = 207,
kDudeGargoyleStatueFlesh = 208,
kDudeGargoyleStatueStone = 209,
kDudePhantasm = 210,
kDudeHellHound = 211,
kDudeHand = 212,
kDudeSpiderBrown = 213,
kDudeSpiderRed = 214,
kDudeSpiderBlack = 215,
kDudeSpiderMother = 216,
kDudeGillBeast = 217,
kDudeBoneEel = 218,
kDudeBat = 219,
kDudeRat = 220,
kDudePodGreen = 221,
kDudeTentacleGreen = 222,
kDudePodFire = 223,
kDudeTentacleFire = 224,
kDudePodMother = 225,
kDudeTentacleMother = 226,
kDudeCerberusTwoHead = 227,
kDudeCerberusOneHead = 228,
kDudeTchernobog = 229,
kDudeCultistTommyProne = 230,
kDudePlayer1 = 231,
kDudePlayer2 = 232,
kDudePlayer3 = 233,
kDudePlayer4 = 234,
kDudePlayer5 = 235,
kDudePlayer6 = 236,
kDudePlayer7 = 237,
kDudePlayer8 = 238,
kDudeBurningInnocent = 239,
kDudeBurningCultist = 240,
kDudeBurningZombieAxe = 241,
kDudeBurningZombieButcher = 242,
kDudeCultistReserved = 243, // unused
kDudeZombieAxeLaying = 244,
kDudeInnocent = 245,
kDudeCultistShotgunProne = 246,
kDudeCultistTesla = 247,
kDudeCultistTNT = 248,
kDudeCultistBeast = 249,
kDudeTinyCaleb = 250,
kDudeBeast = 251,
kDudeBurningTinyCaleb = 252,
kDudeBurningBeast = 253,
kDudeVanillaMax = 254,
kDudeModernCustom = kDudeVanillaMax, // gModern map only
kDudeModernCustomBurning = 255, // gModern map only
kDudeMax = 256,
kMissileBase = 300,
kMissileButcherKnife = kMissileBase,
kMissileFlareRegular = 301,
kMissileTeslaAlt = 302,
kMissileFlareAlt = 303,
kMissileFlameSpray = 304,
kMissileFireball = 305,
kMissileTeslaRegular = 306,
kMissileEctoSkull = 307,
kMissileFlameHound = 308,
kMissilePukeGreen = 309,
kMissileArcGargoyle = 311,
kMissileFireballNapam = 312,
kMissileFireballCerberus = 313,
kMissileFireballTchernobog = 314,
kMissileLifeLeechRegular = 315,
kMissileLifeLeechAltNormal = 316,
kMissileLifeLeechAltSmall = 317,
kMissileMax = 318,
// things
kThingBase = 400,
kThingTNTBarrel = 400,
kThingArmedProxBomb = 401,
kThingArmedRemoteBomb = 402,
kThingCrateFace = 405,
kThingGlassWindow = 406,
kThingFluorescent = 407,
kThingWallCrack = 408,
kThingSpiderWeb = 410,
kThingMetalGrate = 411,
kThingFlammableTree = 412,
kTrapMachinegun = 413, // not really a thing, should be in traps instead
kThingFallingRock = 414,
kThingKickablePail = 415,
kThingObjectGib = 416,
kThingObjectExplode = 417,
kThingArmedTNTStick = 418,
kThingArmedTNTBundle = 419,
kThingArmedSpray = 420,
kThingBone = 421,
kThingDripWater = 423,
kThingDripBlood = 424,
kThingBloodBits = 425,
kThingBloodChunks = 426,
kThingZombieHead = 427,
kThingNapalmBall = 428,
kThingPodGreenBall = 429,
kThingPodFireBall = 430,
kThingDroppedLifeLeech = 431,
kThingVoodooHead = 432, // unused
kModernThingTNTProx = 433, // gModernMap only - detects only players
kModernThingThrowableRock = 434, // gModernMap only - does small damage if hits target
kModernThingEnemyLifeLeech = 435, // gModernMap only - the same as normal, except it aims in specified target only
kThingMax = 436,
// traps
kTrapFlame = 452,
kTrapSawCircular = 454,
kTrapZapSwitchable = 456,
kTrapExploder = 459,
// generators
kGenTrigger = 700,
kGenDripWater = 701,
kGenDripBlood = 702,
kGenMissileFireball = 703,
kGenMissileEctoSkull = 704, // does not work in vanilla
kGenModernMissileUniversal = 704, // gModernMap only
kGenDart = 705,
kGenBubble = 706,
kGenBubbleMulti = 707,
// sound sprites
kGenSound = 708,
kSoundSector = 709,
kSoundPlayer = 711,
};
// WALL TYPES /////////////////////////////////////////////////
enum {
kWallBase = 500,
kWallStack = 501,
kWallGib = 511,
kWallMax = 512,
};
// SECTOR TYPES /////////////////////////////////////////////////
enum {
kSectorBase = 600,
kSectorZMotion = 600,
kSectorZMotionSprite = 602,
kSectorTeleport = 604,
kSectorPath = 612,
kSectorRotateStep = 613,
kSectorSlideMarked = 614,
kSectorRotateMarked = 615,
kSectorSlide = 616,
kSectorRotate = 617,
kSectorDamage = 618,
kSectorCounter = 619,
kSectorMax = 620,
};
// sprite attributes
#define kHitagAutoAim 0x0008
#define kHitagRespawn 0x0010
#define kHitagFree 0x0020
#define kHitagSmoke 0x0100
// sprite physics attributes
#define kPhysMove 0x0001 // affected by movement physics
#define kPhysGravity 0x0002 // affected by gravity
#define kPhysFalling 0x0004 // currently in z-motion
// additional physics attributes for debris sprites
#define kPhysDebrisFly 0x0008 // *debris* affected by negative gravity (fly instead of falling, DO NOT mess with kHitagAutoAim)
#define kPhysDebrisSwim 0x0016 // *debris* can swim underwater (instead of drowning)
#define kPhysDebrisVector 0x0400 // *debris* can be affected by vector weapons
#define kPhysDebrisExplode 0x0800 // *debris* can be affected by explosions
// *modern types only hitag*
#define kModernTypeFlag0 0x0
#define kModernTypeFlag1 0x1
#define kModernTypeFlag2 0x2
#define kModernTypeFlag3 0x3
// sector cstat
#define kSecCParallax 0x01
#define kSecCSloped 0x02
#define kSecCSwapXY 0x04
#define kSecCExpand 0x08
#define kSecCFlipX 0x10
#define kSecCFlipY 0x20
#define kSecCFlipMask 0x34
#define kSecCRelAlign 0x40
#define kSecCFloorShade 0x8000
// ai state types
#define kAiStateOther -1
#define kAiStateIdle 0
#define kAiStateGenIdle 1
#define kAiStateMove 2
#define kAiStateSearch 3
#define kAiStateChase 4
#define kAiStateRecoil 5
#define kAng5 28
#define kAng15 85
#define kAng30 170
#define kAng45 256
#define kAng60 341
#define kAng90 512
#define kAng120 682
#define kAng180 1024
#define kAng360 2048
// -------------------------------
// NUKE-TODO:
#include "v_text.h"
enum BLOOD_GLOBALFLAGS {
BLOOD_FORCE_WIDELOADSCREEN = 1<<0,
};
enum searchpathtypes_t {
SEARCHPATH_REMOVE = 1<<0,
};
#define NEG_ALPHA_TO_BLEND(alpha, blend, orientation) do { \
if (alpha < 0) { blend = -alpha; alpha = 0; orientation |= RS_TRANS1; } \
} while (0)
extern int loaddefinitions_game(const char *fn, int32_t preload);
extern void G_ExtInit(void);
extern void G_SetupGlobalPsky(void);
static inline int gameHandleEvents(void)
{
netGetPackets();
return handleevents();
}
#pragma pack(push,1)
#if 0
struct sectortype
{
short wallptr, wallnum;
int ceilingz, floorz;
unsigned short ceilingstat, floorstat;
short ceilingpicnum, ceilingheinum;
signed char ceilingshade;
char ceilingpal, ceilingxpanning, ceilingypanning;
short floorpicnum, floorheinum;
signed char floorshade;
char floorpal, floorxpanning, floorypanning;
char visibility, filler;
unsigned short lotag;
short hitag, extra;
};
struct walltype
{
int x, y;
short point2, nextwall, nextsector;
unsigned short cstat;
short picnum, overpicnum;
signed char shade;
char pal, xrepeat, yrepeat, xpanning, ypanning;
short lotag, hitag, extra;
};
struct spritetype
{
int x, y, z;
short cstat, picnum;
signed char shade;
char pal, clipdist, filler;
unsigned char xrepeat, yrepeat;
signed char xoffset, yoffset;
short sectnum, statnum;
short ang, owner, index, yvel, zvel;
short type, hitag, extra;
};
struct PICANM {
unsigned int animframes : 5;
unsigned int at0_5 : 1;
unsigned int animtype : 2;
signed int xoffset : 8;
signed int yoffset : 8;
unsigned int animspeed : 4;
unsigned int at3_4 : 3; // type
unsigned int at3_7 : 1; // filler
};
#endif
struct LOCATION {
int x, y, z;
int ang;
};
struct POINT2D {
int x, y;
};
struct POINT3D {
int x, y, z;
};
struct VECTOR2D {
int dx, dy;
};
struct Aim {
int dx, dy, dz;
};
#pragma pack(pop)
inline int ksgnf(float f)
{
if (f < 0)
return -1;
if (f > 0)
return 1;
return 0;
}
inline int IncBy(int a, int b)
{
a += b;
int q = a % b;
a -= q;
if (q < 0)
a -= b;
return a;
}
inline int DecBy(int a, int b)
{
a--;
int q = a % b;
a -= q;
if (q < 0)
a -= b;
return a;
}
#if 0
inline float IncByF(float a, float b)
{
a += b;
float q = fmod(a, b);
a -= q;
if (q < 0)
a -= b;
return a;
}
inline float DecByF(float a, float b)
{
//a--;
a -= fabs(b)*0.001;
float q = fmod(a, b);
a -= q;
if (q < 0)
a -= b;
return a;
}
#endif
inline int ClipLow(int a, int b)
{
if (a < b)
return b;
return a;
}
inline int ClipHigh(int a, int b)
{
if (a >= b)
return b;
return a;
}
inline int ClipRange(int a, int b, int c)
{
if (a < b)
return b;
if (a > c)
return c;
return a;
}
inline float ClipRangeF(float a, float b, float c)
{
if (a < b)
return b;
if (a > c)
return c;
return a;
}
inline int interpolate(int a, int b, int c)
{
return a+mulscale16(b-a,c);
}
inline int interpolateang(int a, int b, int c)
{
return a+mulscale16(((b-a+1024)&2047)-1024, c);
}
inline fix16_t interpolateangfix16(fix16_t a, fix16_t b, int c)
{
return a+mulscale16(((b-a+0x4000000)&0x7ffffff)-0x4000000, c);
}
inline char Chance(int a1)
{
return wrand() < (a1>>1);
}
inline unsigned int Random(int a1)
{
return mulscale(wrand(), a1, 15);
}
inline int Random2(int a1)
{
return mulscale(wrand(), a1, 14)-a1;
}
inline int Random3(int a1)
{
return mulscale(wrand()+wrand(), a1, 15) - a1;
}
inline unsigned int QRandom(int a1)
{
return mulscale(qrand(), a1, 15);
}
inline int QRandom2(int a1)
{
return mulscale(qrand(), a1, 14)-a1;
}
template<class T>
inline void SetBitString(T *pArray, int nIndex)
{
static_assert(sizeof(T) == 1, "Bit array element too large");
pArray[nIndex>>3] |= 1<<(nIndex&7);
}
template<class T>
inline void ClearBitString(T *pArray, int nIndex)
{
static_assert(sizeof(T) == 1, "Bit array element too large");
pArray[nIndex >> 3] &= ~(1 << (nIndex & 7));
}
template<class T>
inline char TestBitString(T *pArray, int nIndex)
{
static_assert(sizeof(T) == 1, "Bit array element too large");
return pArray[nIndex>>3] & (1<<(nIndex&7));
}
// This is to override the namepace prioritization without altering the actual calls.
inline int scale(int a, int b, int c)
{
return ::scale(a, b, c);
}
inline int scale(int a1, int a2, int a3, int a4, int a5)
{
return a4 + (a5-a4) * (a1-a2) / (a3-a2);
}
inline int mulscale16r(int a, int b)
{
int64_t acc = 1<<(16-1);
acc += ((int64_t)a) * b;
return (int)(acc>>16);
}
inline int mulscale30r(int a, int b)
{
int64_t acc = 1<<(30-1);
acc += ((int64_t)a) * b;
return (int)(acc>>30);
}
inline int dmulscale30r(int a, int b, int c, int d)
{
int64_t acc = 1<<(30-1);
acc += ((int64_t)a) * b;
acc += ((int64_t)c) * d;
return (int)(acc>>30);
}
inline int approxDist(int dx, int dy)
{
dx = klabs(dx);
dy = klabs(dy);
if (dx > dy)
dy = (3*dy)>>3;
else
dx = (3*dx)>>3;
return dx+dy;
}
// the point of this is to prevent re-running a function or calculation passed to potentialValue
// without making a new variable under each individual circumstance
inline void SetIfGreater(int32_t* variable, int32_t potentialValue)
{
if (potentialValue > * variable)
* variable = potentialValue;
}
class Rect {
public:
int x0, y0, x1, y1;
Rect(int _x0, int _y0, int _x1, int _y1)
{
x0 = _x0; y0 = _y0; x1 = _x1; y1 = _y1;
}
bool isValid(void) const
{
return x0 < x1 && y0 < y1;
}
char isEmpty(void) const
{
return !isValid();
}
bool operator!(void) const
{
return isEmpty();
}
Rect & operator&=(Rect &pOther)
{
x0 = ClipLow(x0, pOther.x0);
y0 = ClipLow(y0, pOther.y0);
x1 = ClipHigh(x1, pOther.x1);
y1 = ClipHigh(y1, pOther.y1);
return *this;
}
void offset(int dx, int dy)
{
x0 += dx;
y0 += dy;
x1 += dx;
y1 += dy;
}
int height()
{
return y1 - y0;
}
int width()
{
return x1 - x0;
}
bool inside(Rect& other)
{
return (x0 <= other.x0 && x1 >= other.x1 && y0 <= other.y0 && y1 >= other.y1);
}
bool inside(int x, int y)
{
return (x0 <= x && x1 > x && y0 <= y && y1 > y);
}
};
class BitReader {
public:
int nBitPos;
int nSize;
char *pBuffer;
BitReader(char *_pBuffer, int _nSize, int _nBitPos) { pBuffer = _pBuffer; nSize = _nSize; nBitPos = _nBitPos; nSize -= nBitPos>>3; }
BitReader(char *_pBuffer, int _nSize) { pBuffer = _pBuffer; nSize = _nSize; nBitPos = 0; }
int readBit()
{
if (nSize <= 0)
ThrowError("Buffer overflow");
int bit = ((*pBuffer)>>nBitPos)&1;
if (++nBitPos >= 8)
{
nBitPos = 0;
pBuffer++;
nSize--;
}
return bit;
}
void skipBits(int nBits)
{
nBitPos += nBits;
pBuffer += nBitPos>>3;
nSize -= nBitPos>>3;
nBitPos &= 7;
if ((nSize == 0 && nBitPos > 0) || nSize < 0)
ThrowError("Buffer overflow");
}
unsigned int readUnsigned(int nBits)
{
unsigned int n = 0;
dassert(nBits <= 32);
for (int i = 0; i < nBits; i++)
n += readBit()<<i;
return n;
}
int readSigned(int nBits)
{
dassert(nBits <= 32);
int n = (int)readUnsigned(nBits);
n <<= 32-nBits;
n >>= 32-nBits;
return n;
}
};
class BitWriter {
public:
int nBitPos;
int nSize;
char *pBuffer;
BitWriter(char *_pBuffer, int _nSize, int _nBitPos) { pBuffer = _pBuffer; nSize = _nSize; nBitPos = _nBitPos; memset(pBuffer, 0, nSize); nSize -= nBitPos>>3; }
BitWriter(char *_pBuffer, int _nSize) { pBuffer = _pBuffer; nSize = _nSize; nBitPos = 0; memset(pBuffer, 0, nSize); }
void writeBit(int bit)
{
if (nSize <= 0)
ThrowError("Buffer overflow");
*pBuffer |= bit<<nBitPos;
if (++nBitPos >= 8)
{
nBitPos = 0;
pBuffer++;
nSize--;
}
}
void skipBits(int nBits)
{
nBitPos += nBits;
pBuffer += nBitPos>>3;
nSize -= nBitPos>>3;
nBitPos &= 7;
if ((nSize == 0 && nBitPos > 0) || nSize < 0)
ThrowError("Buffer overflow");
}
void write(int nValue, int nBits)
{
dassert(nBits <= 32);
for (int i = 0; i < nBits; i++)
writeBit((nValue>>i)&1);
}
};
END_BLD_NS