//-------------------------------------------------------------------------
/*
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 "build.h"
#include <assert.h>

#include "misc.h"
#include "printf.h"
#include "v_text.h"
#include "seqcb.h"
#include "coreactor.h"
#include "vectors.h"

BEGIN_BLD_NS

void QuitGame(void);

enum
{
	kMagicOwner = 16383,

	kMaxTiles = MAXTILES,
	kMaxStatus = MAXSTATUS,
	kMaxPlayers = 8,

	kTicRate = 120,
	kTicsPerFrame = 4,
	kTicsPerSec = (kTicRate / kTicsPerFrame),

	kExplodeMax = 8,

	kLensSize = 80,
	kViewEffectMax = 20,

	kNoTile = -1,


	//= = = = // = defined = by = NoOne:
	//= = = = // = -------------------------------

	kMaxPAL = 5,
	kUserPLUStart = 15,

	kDmgFall = 0,
	kDmgBurn = 1,
	kDmgBullet = 2,
	kDmgExplode = 3,
	kDmgChoke = 4,
	kDmgSpirit = 5,
	kDmgElectric = 6,
	kDmgMax = 7,
};

// MEDIUM /////////////////////////////////////////////////////
enum {
	kMediumNormal = 0,
	kMediumWater = 1,
	kMediumGoo = 2,
};

// STATNUMS /////////////////////////////////////////////////////
enum {
	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,
	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,
};

enum {
	kExplosionSmall = 0,
	kExplosionStandard = 1,
	kExplosionLarge = 2,
	kExplosionFireball = 3,
	kExplosionSpray = 4,
	kExplosion5 = 5,
	kExplosion6 = 6,
	kExplosionNapalm = 7,
	kExplosionMax = 8
};

// 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,

	// decorations
	kDecorationTorch = 30,
	kDecorationCandle = 32,

	// (weapons)
	kItemWeaponBase = 40,
	kItemWeaponRandom = kItemWeaponBase,
	kItemWeaponSawedoff = 41,
	kItemWeaponTommygun = 42,
	kItemWeaponFlarePistol = 43,
	kItemWeaponVoodooDoll = 44,
	kItemWeaponTeslaCannon = 45,
	kItemWeaponNapalmLauncher = 46,
	kItemWeaponPitchfork = 47,
	kItemWeaponSprayCan = 48,
	kItemWeaponTNT = 49,
	kItemWeaponLifeLeech = 50,
	kItemWeaponMax = 51,

	// items (ammos)
	kItemAmmoBase = 60,
	kItemAmmoSprayCan = kItemAmmoBase,
	kItemAmmoTNTBundle = 62,
	kItemAmmoTNTBox = 63,
	kItemAmmoProxBombBundle = 64,
	kItemAmmoRemoteBombBundle = 65,
	kItemAmmoTrappedSoul = 66,
	kItemAmmoSawedoffFew = 67,
	kItemAmmoSawedoffBox = 68,
	kItemAmmoTommygunFew = 69,
	kItemAmmoVoodooDoll = 70,
	kItemAmmoTommygunDrum = 72,
	kItemAmmoTeslaCharge = 73,
	kItemAmmoFlares = 76,
	kItemAmmoGasolineCan = 79,
	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,

	kItemArmorAsbest = 139,
	kItemArmorBasic = 140,
	kItemArmorBody = 141,
	kItemArmorFire = 142,
	kItemArmorSpirit = 143,
	kItemArmorSuper = 144,

	kItemFlagABase = 145,
	kItemFlagBBase = 146,
	kItemFlagA = 147,
	kItemFlagB = 148,
	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,
	kDudeMax = 256,

	kMissileBase = 300,
	kMissileButcherKnife = kMissileBase,
	kMissileFlareRegular = 301,
	kMissileTeslaAlt = 302,
	kMissileFlareAlt = 303,
	kMissileFlameSpray = 304,
	kMissileFireball = 305,
	kMissileTeslaRegular = 306,
	kMissileEctoSkull = 307,
	kMissileFlameHound = 308,
	kMissilePukeGreen = 309,
	kMissileUnused = 310,
	kMissileArcGargoyle = 311,
	kMissileFireballNapalm = 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,
	kThingPodFireBall = 429,
	kThingPodGreenBall = 430,
	kThingDroppedLifeLeech = 431,
	kThingVoodooHead = 432, // unused
	kThingMax = 436,

	// traps
	kTrapFlame = 452,
	kTrapSawCircular = 454,
	kTrapZapSwitchable = 456,
	kTrapExploder = 459,

	// generators
	kGenTrigger = 700,
	kGenDripWater = 701,
	kGenDripBlood = 702,
	kGenMissileFireball = 703,
	kGenMissileEctoSkull = 704,
	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,
};

// ai state types
enum {
	kAiStateOther = -1,
	kAiStateIdle = 0,
	kAiStateGenIdle = 1,
	kAiStateMove = 2,
	kAiStateSearch = 3,
	kAiStateChase = 4,
	kAiStateRecoil = 5,
	kAiStateAttack = 6,
	kAiStatePatrolBase = 7,
	kAiStatePatrolWaitL = kAiStatePatrolBase,
	kAiStatePatrolWaitC,
	kAiStatePatrolWaitW,
	kAiStatePatrolMoveL,
	kAiStatePatrolMoveC,
	kAiStatePatrolMoveW,
	kAiStatePatrolTurnL,
	kAiStatePatrolTurnC,
	kAiStatePatrolTurnW,
	kAiStatePatrolMax,
};

enum
{
	// sprite attributes
	kHitagAutoAim = 0x0008,
	kHitagRespawn = 0x0010,
	kHitagFree = 0x0020,
	kHitagSmoke = 0x0100,

	//  sprite physics attributes
	kPhysMove = 0x0001, // affected by movement physics
	kPhysGravity = 0x0002, // affected by gravity
	kPhysFalling = 0x0004, // currently in z-motion

	//  sector cstat
	kSecCParallax = 0x01,
	kSecCSloped = 0x02,
	kSecCSwapXY = 0x04,
	kSecCExpand = 0x08,
	kSecCFlipX = 0x10,
	kSecCFlipY = 0x20,
	kSecCFlipMask = 0x34,
	kSecCRelAlign = 0x40,
	kSecCFloorShade = 0x8000,

	kAng5 = 28,
	kAng15 = 85,
	kAng30 = 170,
	kAng45 = 256,
	kAng60 = 341,
	kAng90 = 512,
	kAng120 = 682,
	kAng180 = 1024,
	kAng360 = 2048,
};

// Weapon numbers
enum
{
	kWeapNone = 0,
	kWeapPitchFork = 1,
	kWeapFlareGun = 2,
	kWeapShotgun = 3,
	kWeapTommyGun = 4,
	kWeapNapalm = 5,
	kWeapDynamite = 6,
	kWeapSpraycan = 7,
	kWeapTeslaCannon = 8,
	kWeapLifeLeech = 9,
	kWeapVoodooDoll = 10,
	kWeapProximity = 11,
	kWeapRemote = 12,
	kWeapBeast = 13,
	kWeapMax = 14,
};

// -------------------------------

#pragma pack(push,1)

struct LOCATION {
	int x, y, z;
	int ang;
};

using POINT2D = vec2_t;
using POINT3D = vec3_t;

struct VECTOR2D {
	int dx, dy;
};

#pragma pack(pop)

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 uint8_t Chance(int a1)
{
	return wrand() < (a1 >> 1);
}

// ------------------------------------------------
inline unsigned int Random(int a1)
{
	return MulScale(wrand(), a1, 15);
}

inline double RandomF(int a1, int scale = 16)
{
	return FixedToFloat(Random(a1), scale);
}

inline double RandomD(double val, int scale = 16)
{
	return FixedToFloat(Random(FloatToFixed(val, scale)), scale);
}

inline DAngle RandomAngle(int base = 2048)
{
	return DAngle::fromBuild(MulScale(wrand(), base, 15));
}

// ------------------------------------------------
inline int Random2(int a1)
{
	return MulScale(wrand(), a1, 14) - a1;
}

inline double Random2F(int a1, int scale = 16)
{
	return FixedToFloat(Random2(a1), scale);
}

inline double Random2D(double val, int scale)
{
	return FixedToFloat(Random2(FloatToFixed(val, scale)), scale);
}

inline DAngle Random2A(int a1)
{
	return DAngle::fromBuild(MulScale(wrand(), a1, 14) - a1);
}

// ------------------------------------------------
inline int Random3(int a1)
{
	return MulScale(wrand() + wrand(), a1, 15) - a1;
}

inline double Random3F(int a1, int scale = 16)
{
	return FixedToFloat(Random3(a1), scale);
}


inline unsigned int QRandom(int a1)
{
	return MulScale(qrand(), a1, 15);
}

inline int QRandom2(int a1)
{
	return MulScale(qrand(), a1, 14) - a1;
}

inline double QRandom2F(double a1)
{
	return (qrand() * (1. / 16384.) * a1) - a1;
}

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) // do not delete!
{
	int64_t acc = 1 << (16 - 1);
	acc += ((int64_t)a) * b;
	return (int)(acc >> 16);
}
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;
	}
	bool 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;
	uint8_t* pBuffer;
	BitReader(uint8_t* _pBuffer, int _nSize, int _nBitPos) { pBuffer = _pBuffer; nSize = _nSize; nBitPos = _nBitPos; nSize -= nBitPos >> 3; }
	BitReader(uint8_t* _pBuffer, int _nSize) { pBuffer = _pBuffer; nSize = _nSize; nBitPos = 0; }
	int readBit()
	{
		if (nSize <= 0)
			I_Error("Buffer overflow in BitReader");
		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)
			I_Error("Buffer overflow in BitReader");
	}
	unsigned int readUnsigned(int nBits)
	{
		unsigned int n = 0;
		assert(nBits <= 32);
		for (int i = 0; i < nBits; i++)
			n += readBit() << i;
		return n;
	}
	int readSigned(int nBits)
	{
		assert(nBits <= 32);
		int n = (int)readUnsigned(nBits);
		n <<= 32 - nBits;
		n >>= 32 - nBits;
		return n;
	}
};


END_BLD_NS
