If you have comments or questions concerning this source file, discuss them in the forum.
/*
Copyright (c) 2002 Nicolai Haehnle

See the license.txt for details. If that file was not included in the
source distributions, please email <prefect@rtts.org>
*/
// game.h -- included by actual in-game stuff

#include "rtts.h"
#include "gui_panel.h"


#define TOTALFADETIME       800
#define TOTALPREWINTIME     1500

#define SHADOWFRACT         (0.80)

#define SHADOWX(x)          (320 + (((x) - 320) * SHADOWFRACT))
#define SHADOWY(y)          (400 + (((y) - 400) * SHADOWFRACT))

// solves SHADOWY(y)+m=0
#define FIRSTSHADOWY(m)     (400 - ((400+(m)) / SHADOWFRACT))

class AirPlayer;

typedef struct {
    int     money;      // money earned
    int     moneymax;   // total money in level
    float   shieldslost;
} eplrstats_t;

typedef struct {
    player_t        data;
    plrcontrol_t    ctrl;
    AirPlayer       *ship;
    eplrstats_t     stats;
} eplayer_t;

/*
=============================================================================

AIR UNITS

=============================================================================
*/

#define LYR_BOMB        0
#define LYR_UNIT        1
#define LYR_MISS        2
#define LYR_SPRITE      3

#define NUM_LAYERS      4

#define AIRMOVE_DUMB        0
#define AIRMOVE_CHASE       1


#define MAX_ATTACKS         32

#define EW_SHOT             0
#define EW_HOMINGSHOT       1
#define EW_LEFTSHOT         2
#define EW_RIGHTSHOT        3
#define EW_MISSILE          4
#define EW_FIREBALL         5

typedef struct au_attack_s {
    int         attach;
    int         type;
    float       wait;
    int         repeat;
    float       delay;
} au_attack_t;

typedef struct airunit_type_s {
    struct airunit_type_s **pprev, *next;
    int             used;
    char            name[64];

    sprite_t        *sprite;
    bool            bBoss;
    float           flSpeed;
    int             iMovement;
    float           flMParm1;   // meaning depends on iMovement
    float           flMParm2;
    float           flHealth;

    int             iMoney;

    float           flAttackCycle; // time between attack sequences
    int             numattacks;
    au_attack_t     attacks[MAX_ATTACKS];

    int             iTrailAttach;
    char            szTrailSprite[64];
    int             iTrailTime;
} airunit_type_t;

//
// ge_air.cpp
//
airunit_type_t *Air_GetType(const char *name);
void Air_FreeType(airunit_type_t *type);


class AirBase {
public:
    AirBase     **m_ppPrev;
    AirBase     *m_pNext;

    bool        m_bRemove;
    int         m_iLayer;
    sprite_t    *m_pSprite;
    int         m_iAnimTime;
    float       m_x, m_y;
    float       m_lastx, m_lasty;
    float       m_vel[2];

public:
    AirBase(int iLayer, const char *pszSprite, float x, float y);
    AirBase(int iLayer, sprite_t *pSprite, float x, float y);
    virtual ~AirBase();

    virtual void Think();
    virtual void Draw();
    virtual bool IsGoodGuy() { return false; }
    virtual bool IsTangible() { return false; }
    virtual bool IsGhost() { return false; }
    virtual void Collide(AirBase *pOther) { }
    virtual void Damage(AirBase *pInflictor, float flAmt) { }

    bool IsInScreen();

    void Move(float dx, float dy);

private:
    void Init();
};

class AirUnit : public AirBase {
public:
    float       m_flHealth;
    float       m_flLowHealth;
    int         m_iDeathTime;
    float       m_flDmgExplosions;  // explosions per second
    float       m_flSpeed;

public:
    AirUnit(const char *pszSprite, float flSpeed, float x, float y, float health);
    AirUnit(sprite_t *sprite, float flSpeed, float x, float y, float health);

    virtual void Think();
    virtual void Damage(AirBase *pInflictor, float flAmt);
    virtual bool IsTangible() { return true; }

    virtual void Run() { }
    virtual void FinalDeath() { }

    virtual void DamageNotice(float amt) { }

public:
    int         m_iDmgExplosionTimer;
    float       m_flDeathMoveAngle;
    float       m_flDeathAngleSpeed;
};

class AirPlayer : public AirUnit {
public:
    eplayer_t   *m_pPlayer;

    int         m_iCurWeapon;
    int         m_iFireMGDelay;
    int         m_iMGCycle;
    int         m_iFirePrimaryDelay;
    int         m_iPrimaryCycle;
    bool        m_bInFire;

public:
    AirPlayer(eplayer_t *pPlayer, float x, float y);
    virtual ~AirPlayer();

    virtual void Run();
    virtual void Draw();
    virtual bool IsGoodGuy() { return true; }
    virtual void Collide(AirBase *pOther);

    bool Input(int code, int c, bool down);

    void RunWeapons();
    void RunMovement(byte *keystate);
    void SelectBestWeapon();
    void NextWeapon();

    virtual void DamageNotice(float amt);
};


/*
=============================================================================

LEVEL

=============================================================================
*/

#define MAPWIDTH        20 // 640 / 32

struct tiletype_t {
    HPicture    *pic;
};

class ETileTypes {
public:
    unsigned    m_iNumTT;
    tiletype_t  *m_pTT;

public:
    ETileTypes();
    ~ETileTypes();

    unsigned Load(const char *name);

    inline bool ValidId(unsigned id) { return (id < m_iNumTT); }
};

typedef struct {
    unsigned    gfx;    // ~0 is a NUL tile
} tile_t;

struct jobnode_t {
    float   dx;
    float   dy;
};

struct airjob_t {
    float           x;
    float           y;      // in pixels, 0 = bottom of level
    airunit_type_t  *type;
    byte            level;  // 0-2

    std::vector<jobnode_t> nodes;

public:
    airjob_t();
    airjob_t(airunit_type_t *ntype);
    airjob_t(const airjob_t &copy);
    ~airjob_t();

    airjob_t &operator=(const airjob_t &copy);
};

struct airsched_t {
    float       line;   // automatically calculated based on shadow
    airjob_t    *job;
};

typedef std::vector<airjob_t>::iterator airjob_it;
typedef std::vector<airjob_t>::const_iterator airjob_cit;
typedef std::vector<airjob_t*>::iterator airjobp_it;
typedef std::vector<airjob_t*>::const_iterator airjobp_cit;

typedef std::vector<airsched_t>::iterator airsched_it;
typedef std::vector<airsched_t>::const_iterator airsched_cit;

typedef std::vector<jobnode_t>::iterator jobnode_it;
typedef std::vector<jobnode_t>::const_iterator jobnode_cit;

class ELevel {
public:
    bool        m_bEdit;

    int         m_iMapLength;       // in tiles
    ETileTypes  *m_pTileTypes;
    tile_t      *m_pTiles;

    float       m_flDistance;       // scrolling, in tiles
    float       m_flLastDistance;

public:
    int         m_iBaseLevel;
    char        m_szName[32];
    char        m_szAuthor[32];

    int         m_iNumBosses;       // total # of bosses in the level

public: // air-related stuff
    std::vector<airjob_t> m_AirJobs; // list of all airjobs
    std::vector<airsched_t> m_AirSched; // list of jobs to dispatch; this is kept sorted backwards

public:
    ELevel(ETileTypes *pTileTypes);
    ~ELevel();

    bool Load(const char *fname);
    bool Save();
    void Clear();

    void BeginPlay(int level);
    void EndPlay();
    void Logic();
    void Draw();

    void SetEditMode(bool bEdit);
    void SetDistance(float dist);

    void ScreenToLevel(float sx, float sy, float *plx, float *ply);
    void LevelToScreen(float lx, float ly, float *psx, float *psy);

private:
    bool SavePrepare();
    void LoadAirjobs(LFileRead *fr, unsigned features);
    bool LoadTiles(LFileRead *fr);

public: // Tile editing
    void TileAtPoint(float x, float y, int *ptx, int *pty);
    void PointForTile(int tx, int ty, float *px, float *py);

    void Resize(bool start, int tiles);
    void SetTile(int x, int y, unsigned id);
    unsigned GetTile(int x, int y);

public: // Airjob editing
    airjob_t *AddAirJob(airunit_type_t *type, float x, float y);
    void AddAirjobs(const std::vector<airjob_t> &jobs, float x, float y);
    void DelAirJob(airjob_t *job);
    void DelAirjobs(const std::vector<airjob_t*> &jobs);
    airjob_t *AirJobAtPoint(float x, float y);
    void SetAirjobPos(airjob_t *aj, float x, float y);
    bool FindAirJobs(float x1, float y1, float x2, float y2, std::vector<airjob_t*> *list);

    airjob_t *NodeAtPoint(float x, float y, int *pidx);
    airjob_t *NodeAtPoint(const std::vector<airjob_t*> &list, float x, float y, int *pidx);
};

/*
=============================================================================

GAME ENGINE

=============================================================================
*/

enum {
    ge_none,
    ge_running,
    ge_prewin,  // boss destroyed, wait some seconds
    ge_abort,
    ge_win,
    ge_loose
};

#define ENGINE_ABORT        0
#define ENGINE_LOOSE        1
#define ENGINE_WIN          2

class EEngine : public GPanel {
public:
    int             m_iState;

    ELevel          *m_pLevel;
    eplayer_t       m_player;

    bool            m_bPaused;
    int             m_iLevel;
    int             m_iLevelAdjust;

    bool            m_bEndGame;

public: // ingame variables (valid after Start())
    int             gametime;
    int             curframe;
    float           oldfract, curfract;     // interpolation

    int             m_iBossDestroyed;   // # destroyed bosses
    int             m_iPrewinTime;      // win countdown

    int             m_iFadeoutTime;

public: // difficulty adjustors
    float           m_flHealthFactor;
    int             m_iFrameTime;   // logic steps (in easy, this is == LOGICTIME)

public:
    EEngine();
    ~EEngine();

    const char *MouseCursor();

    // setup and post-processing
    void SetLevel(ELevel *level, bool edit);
    void SetLevelAdjust(int lvl, int adjust);

    void PokePlayer(const player_t *player, const LConfig *cfg);
    void PeekPlayer(player_t *player);

    // runtime
    void Begin();
    void End();

    void Logic();
    void Draw();

    bool Key(int code, char c, bool down);

    void SetPaused(bool paused) { m_bPaused = paused; }

public: // integrated air unit subsystem
    AirBase     *units_air[NUM_LAYERS];

    void Air_Init();
    void Air_Shutdown();
    void Air_Logic();
    void Air_Draw();
    void Air_Spawn(airjob_t *aj);

    AirBase *Air_FindNearest(float x, float y, bool goodguy, float xdir, float ydir);

public: // integrated HUD subsystem
    HPicture *hud_pHealth;
    HFont *hud_pFont;

    void Hud_Load();
    void Hud_Unload();
    void Hud_Draw();
};

extern EEngine *ge;

#define LERP(old,cur) ((old)*ge->oldfract + (cur)*ge->curfract)