自制游戏Zombie代码

/*
ID: lightmain
TASK: Zombie V4.3
LANG: C++
DATE: 20200920 22:00:02
*///using CRLF, UTF-8

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <windows.h>
#include <ctime>
#include <fstream>
#include <string>
#include <conio.h>
#include <cmath>
#include <winuser.h>
#include <list>
#define pr printf
#define F(i, j, k) for(register int i = j, kkllkl = k; i <= kkllkl; ++i)
#define G(i, j, k) for(register int i = j, kkllkl = k; i >= kkllkl; --i)
#define clr(a, v) memset((a), v, sizeof(a))
#define sqr(x) ((x) * (x))
using namespace std;
typedef long long ll;

#define chkmin(x, y) ((x) = ((x) < (y) ? (x) : (y)))
#define chkmax(x, y) ((x) = ((x) < (y) ? (y) : (x)))

// #define DEBUG

/* --------------------------CSYZ1921------------------------- */
class SCREEN;
class CROOD;
class PLAYER;
class BULLET;
class ENEMY;
class ITEM;
class BIRTH;
class DIE;
class GAME;

/*
 * 屏幕的直接控制。
 * 横纵坐标以命令行坐标为准,与数组不同。
 * 注意:命令行坐标从0开始。 
*/
class SCREEN {
private:
    void HideCursor()//隐藏光标 
    {
        CONSOLE_CURSOR_INFO cursor_info = {1, 0}; 
        SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
    }

public:
    static const int OUTPUT_WIDTH = 80, OUTPUT_HEIGHT = 45;
    static const int CHAR_WIDTH = 7, CHAR_HEIGHT = 16;
    int pressed[130];

    void set_cursor(int x, int y)
    {
        HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
        COORD pos={0,0};
        pos.X=x;
        pos.Y=y;
        SetConsoleCursorPosition(hOut,pos);
    }

    void init() {
        system("cls");
        clr(pressed, 0);
        HideCursor();
    }

    // 用字符c填满一个矩形
    void fill(char c, int x1, int y1, int x2, int y2) {
        F(i, y1, y2) {
            set_cursor(x1,i);
            F(j, x1, x2)
                pr("%c",c);
        }
    }

    // 输出一个二维数组 下标从1开始
    void putarray(char **a, int wid, int hei, int x0, int y0) {
        F(i, 1, hei) {
            set_cursor(x0, y0 + i - 1);
            F(j, 1, wid)
                pr("%c", a[i][j]);
        }
    }

    // 输出一个一维字符串 下标从0开始
    void putarray(char *a, int wid, int hei, int x0, int y0) {
        F(i, 1, hei) {
            set_cursor(x0, y0 + i - 1);
            F(j, 1, wid)
                pr("%c", a[(i - 1) * wid + (j - 1)]);
        }
    }

    // 输出一个一维常量字符串
    void putarray(const char *a, int wid, int hei, int x0, int y0) {
        F(i, 1, hei) {
            set_cursor(x0, y0 + i - 1);
            F(j, 1, wid)
                pr("%c", a[(i - 1) * wid + (j - 1)]);
        }
    }

    // 画一个框框,并把中间清空
    void draw_square(int x1, int y1, int x2, int y2) {
        F(y, y1, y2) {
            set_cursor(x1, y);
            F(x, x1, x2) {
                char c = ' ';
                if(x == x1 || x == x2) c = '|';
                if(y == y1 || y == y2) c = '-';
                pr("%c", c);
            }
        }
    }

    void putc(char c, int x, int y)
    {
        set_cursor(x,y);
        printf("%c",c);
    }

    // 检查按键c是否被按下
    bool check(char c) {
        return (GetKeyState(c) & 0x80) ? true : false;
    }

    bool changed(char c) {
        int x = GetKeyState(c) & 0x01;
        return (x != pressed[c]) ? (pressed[c] = x, true) : false;
    }

    bool click(char c) {
        return changed(c) && check(c);
    }
} screen;

/*
 * 实数坐标,(x, y)意义与数组坐标一致
*/
const double eps = 1e-5;
class crood {
  private:
    double x, y;
  public:
    crood(double x = 0.0, double y = 0.0): x(x), y(y) {}
    crood(int ix, int iy) {
        x = (double)ix; 
        y = (double)iy;
    }

    friend double dis(crood a, crood b);
    double get_len() {
        return sqrt(sqr(x) + sqr(y));
    }
    double get_x() { return x; }
    double get_y() { return y; }

    crood operator + (const crood &b) const {
        return crood(x + b.x, y + b.y);
    }
    crood operator - (const crood &b) const {
        return crood(x - b.x, y - b.y);
    }
    crood operator * (const double &q) const {
        return crood(x * q, y * q);
    }
    crood operator * (const int &q) const {
        return crood(x * q, y * q);
    }
    crood operator += (const crood &b) {
        x += b.x; y += b.y;
        return *this;
    }
    crood operator -= (const crood &b) {
        x -= b.x; y -= b.y;
        return *this;
    }
    crood operator *= (const double &q) {
        x *= q; y *= q;
        return *this;
    }
    void set_len(double length) {
        double old = get_len();
        if(old < eps) return ;
        x *= length / old;
        y *= length / old;
    }
    void set_x(double p) { x = p; }
    void set_y(double p) { y = p; }
    void set_dir(double ang) {
        double l = get_len();
        (*this) = crood(-sin(ang) * l, cos(ang) * l);
    }
    // 顺时针旋转,因为坐标系是左手系
    void turn_dir(double ang) {
        double px = x, py = y;
        x = cos(ang) * px - sin(ang) * py;
        y = cos(ang) * py + sin(ang) * px;
    }
};

/*
 * 公用的工具
*/

const int WIDTH = (SCREEN :: OUTPUT_WIDTH) * (SCREEN :: CHAR_WIDTH), HEIGHT = (SCREEN :: OUTPUT_HEIGHT) * (SCREEN :: CHAR_HEIGHT);
const int LEFT = 0, RIGHT = 1, UP = 2, DOWN = 3, LU = 4, LD = 5, RU = 6, RD = 7, NON = 8;
const int dx[9] = {0, 0, -1, 1, -1, 1, -1, 1, 0}, dy[9] = {-1, 1, 0, 0, -1, -1, 1, 1, 0};
const double PI = acos(-1);
const int INF = 0x1f3f3f3f;
FILE *debug;

// 求实数绝对值
inline double fabs(double x) { return x < 0 ? -x : x; }

// 该坐标是否已出界
int gout(crood v) {
    int ret = 0;
    if(v.get_x() < SCREEN :: CHAR_HEIGHT * 0.6 || v.get_x() > HEIGHT + SCREEN :: CHAR_HEIGHT * 0.4) ret |= 2;
    if(v.get_y() < SCREEN :: CHAR_WIDTH  * 0.6 || v.get_y() > WIDTH  + SCREEN :: CHAR_WIDTH * 0.4)  ret |= 1;
    return ret;
}

// 求向量(x, y)的旋转角
double angle(crood v) {
    double x = v.get_x(), y = v.get_y();
    swap(y, x); y = -y;
    if(fabs(x) < eps) 
        if(fabs(y) > eps) return y > 0 ? PI / 2 : PI * 3 / 2;
        else return 0.0;
    double k = ((double)y) / ((double)x);
    double may = atan(k);
    if(x < 0) may += PI;
    if(may < 0) may += 2.0 * PI;
    // pr("(%.1lf, %.1lf) angle = %.2lf\n", x, y, may / (PI * 2) * 360.0);
    return may;
}

// 求某旋转角对应的单位向量,向量的坐标系是数组坐标系
crood vec(double ang) {
    return crood(-sin(ang), cos(ang));
}

// 求两个点之间的直线距离
double dis(crood a, crood b) {
    return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));
}

// 生成一个大整数,范围为[0, 0x3fffffff]
int bigrand() {
    return (rand() << 15) + rand();
}

// 随机生成一个范围内的整数
int randin(int l, int r) {
    return bigrand() % (r - l + 1) + l;
}

// 生成一个范围内的实数
double drandin(double l, double r) {
    if(fabs(l - r) < eps) return l;
    if(r < l) return 0.0;
    return randin(0, 1000) * 1.0 * (r - l) / 1000.0 + l;
}

// 随机生成一个屏幕范围内的坐标
crood randcrood() {
    double x = drandin(SCREEN :: CHAR_HEIGHT * 0.65, HEIGHT + SCREEN :: CHAR_HEIGHT * 0.35);
    double y = drandin(SCREEN :: CHAR_WIDTH * 0.65, WIDTH + SCREEN :: CHAR_WIDTH * 0.35);
    return crood(x, y);
}

// 四舍五入
void toint(crood v, int &x, int &y) {
    x = ((int)(v.get_x() + 0.5));
    y = ((int)(v.get_y() + 0.5));
}

// 四舍五入
int toint(double x) {
    return (int)(x + 0.5);
}

/*
 * 玩家的数据控制
*/
class PLAYER {
  public:
    static const int BUFF_TYPES = 2;
    enum Skill {
        Grenade_rain, Belief_of_FFF
    };
  private:
    static const double MAXSPEED;
    static const double RADIUS;
    static const int REHEALTH[2], REHEALTH_COOLDOWN;
    static const int HEALTH;
    static const double ACCELERATION, INITIAL_SPEED;
    int HP, dir, lst_move_clock, lst_rehealth_clock, lst_accelerate_clock;
    int buff[BUFF_TYPES]; // 表示buff终结的时间
    crood pos, velo, tarvelo;
    double maxspeed;
  public:
    static const char MRK = '@';
    PLAYER() {}

    void init() {
        HP = HEALTH;
        pos = crood(HEIGHT / 2, WIDTH / 2);
        velo = crood(0, 0);
        maxspeed = MAXSPEED;
        lst_move_clock = lst_rehealth_clock = lst_accelerate_clock = clock();
        clr(buff, 0);
    }

    void move() {
        int c = clock();
        int t = c - lst_move_clock;
        lst_move_clock = c;

        int ret = gout(pos + velo * t);
        crood d = velo * t;
        if(ret & 2) { d.set_x(0.0); velo.set_x(0.0); }
        if(ret & 1) { d.set_y(0.0); velo.set_y(0.0); } 
        pos += d;
    }
    void accelerate() {
        int c = clock();
        crood deltav = tarvelo - velo;
        double deltaspeed = ACCELERATION * (c - lst_accelerate_clock);
        lst_accelerate_clock = c;
        if(deltav.get_len() > deltaspeed) deltav.set_len(deltaspeed);
        velo += deltav;
    }
    void set_dir(int x) {
        dir = x;
        tarvelo = crood(dx[dir], dy[dir]);
        tarvelo.set_len(maxspeed);
    } 

    void set_pos(double px, double py) { pos = crood(px, py); }
    void give_damage(int x, crood knockback) {
        HP = min(max(0, HP - x), HEALTH);
        velo += knockback;
    }
    void rehealth() {
        int c = clock();
        if(c - lst_rehealth_clock < REHEALTH_COOLDOWN) return ;
        lst_rehealth_clock = c;
        HP = min(HEALTH, HP + REHEALTH[dir == NON]); 
    }
    void reclock() {
        lst_move_clock = lst_rehealth_clock = lst_accelerate_clock = clock();
    }
    void give_buff(enum Skill type, int t) {
        buff[type] = clock() + t;
    }

    void frame_operate() {
        rehealth();
        accelerate();
        move();
    }

    double get_radius() { return RADIUS; }
    crood get_pos() { return pos; }
    int get_HP() { return HP; }
    int get_dir() { return dir; }
    crood get_v() { return velo; }
    double get_HPproportion() { return max(0.0, HP * 1.0 / HEALTH); }
    int get_buff(int type) { return buff[type] >= clock(); }
} player;

const double PLAYER :: MAXSPEED          = 0.17;
const double PLAYER :: RADIUS            = 5;
const int    PLAYER :: HEALTH            = 1000;
const int    PLAYER :: REHEALTH[2]       = {5, 20};
const int    PLAYER :: REHEALTH_COOLDOWN = 1700;
const double PLAYER :: ACCELERATION      = 0.0005;
const double PLAYER :: INITIAL_SPEED     = 0;

/*
 * 射弹的数据控制
*/
class BULLET {
  public:
    enum Type {
        normal, shock_wave, RPG, RPGwave, Fire
    };
    static const int TOTAL_TYPES = 5;
    static int lst_birth_clock[TOTAL_TYPES];
    static const int COOLDOWN[TOTAL_TYPES];
    static const double DEFLECTION[TOTAL_TYPES];
    static const double MOVING_DEFLECTION[TOTAL_TYPES];
  private:
    static const int IMMUNE[TOTAL_TYPES];
    static const char MRK[TOTAL_TYPES];
    static const int TIME_LIFE[TOTAL_TYPES];
    static const int PENETRATE[TOTAL_TYPES];
    static const int DEAD_SECONDARY[TOTAL_TYPES];
    static const int DAMAGE[TOTAL_TYPES];
    static const double MAXSPEED[TOTAL_TYPES];
    static const double RADIUS[TOTAL_TYPES];
    static const double ACCELERATION[TOTAL_TYPES];
    static const double INITIAL_SPEED[TOTAL_TYPES];
    static const double KNOCKBACK[TOTAL_TYPES];
    enum Type type;
    int damage, birth_clock, penetrate, lst_move_clock, time_life, lst_accelerate_clock;
    crood pos, velo, tarvelo;
    double radius, maxspeed;
    char mrk;
  public:
    BULLET(int ptype, crood ppos = crood(0, 0), crood towards = crood(1, 0)) {
        type = (enum Type)ptype;
        pos = ppos;
        damage = DAMAGE[type];
        tarvelo = towards;
        tarvelo.set_len(maxspeed = MAXSPEED[type]);
        velo = towards;
        velo.set_len(INITIAL_SPEED[type]);
        radius = RADIUS[type];
        time_life = TIME_LIFE[type] * drandin(0.65, 1.5);
        if(type == Fire && player.get_buff(PLAYER :: Belief_of_FFF)) { time_life = INF; damage *= 10; }
        mrk = MRK[type];
        birth_clock = lst_move_clock = lst_accelerate_clock = clock();
        penetrate = PENETRATE[type];
    }

    void move() {
        int c = clock();
        int t = c - lst_move_clock;
        lst_move_clock = c;
        pos += velo * t;
    }
    void accelerate() {
        int c = clock();
        crood deltav = tarvelo - velo;
        double deltaspeed = ACCELERATION[type] * (c - lst_accelerate_clock);
        lst_accelerate_clock = c;
        if(deltav.get_len() > deltaspeed) deltav.set_len(deltaspeed);
        velo += deltav;
    }
    void reclock() { lst_move_clock = lst_accelerate_clock = clock(); }
    void frame_operate() {
        accelerate();
        move();
    }

    double get_radius() { return radius; }
    crood get_pos() { return pos; }
    int get_damage() { --penetrate; return damage; }
    bool get_die() { return gout(pos) || (clock() - birth_clock > time_life) || (penetrate == 0); }
    char get_mrk() { return mrk; }
    int get_dead_secondary() { return DEAD_SECONDARY[type]; } 
    int get_type() { return type; }
    int get_immune() { return IMMUNE[type]; }
    crood get_knockback() { return tarvelo * KNOCKBACK[type];  }
    static int get_cooldown(int ptype) {
        if(ptype == RPG && player.get_buff(PLAYER :: Grenade_rain)) return COOLDOWN[RPG] / 20;
        return COOLDOWN[ptype]; 
    }
};
list<BULLET> bullets;
typedef list<BULLET> :: iterator Buit;

// normal = 0, shock_wave = 1, RPG = 2, RPGwave = 3, Fire = 4;
const int    BULLET :: DAMAGE[BULLET :: TOTAL_TYPES]            = {400, 15000, 30000, 20, 300};
const double BULLET :: MAXSPEED[BULLET :: TOTAL_TYPES]          = {0.4, 0.3, 0.7, 0.08, 0.4};
const double BULLET :: ACCELERATION[BULLET :: TOTAL_TYPES]      = {0.0, 0.0, 0.0, 0.0, 0.0};
const double BULLET :: INITIAL_SPEED[BULLET :: TOTAL_TYPES]     = {0.4, 0.3, 0.7, 0.08, 0.4};
const double BULLET :: KNOCKBACK[BULLET :: TOTAL_TYPES]         = {0.2, 4.2, 0.0, 0.1, 0};
const double BULLET :: RADIUS[BULLET :: TOTAL_TYPES]            = {3, 6, 5, 10, 10};
int          BULLET :: lst_birth_clock[BULLET :: TOTAL_TYPES]   = {0, 0, 0, 0, 0};
const int    BULLET :: COOLDOWN[BULLET :: TOTAL_TYPES]          = {34, 0, 3000, 0, 9};
const double BULLET :: DEFLECTION[BULLET :: TOTAL_TYPES]        = {PI / 360, 0, PI / 360, 0, PI / 10};
const double BULLET :: MOVING_DEFLECTION[BULLET :: TOTAL_TYPES] = {PI / 72, 0, PI / 360, 0, PI / 4};
const int    BULLET :: DEAD_SECONDARY[BULLET :: TOTAL_TYPES]         = {-1, -1, BULLET :: RPGwave, -1, -1};
const int    BULLET :: TIME_LIFE[BULLET :: TOTAL_TYPES]         = {INF, INF, INF, 400, 500};
const int    BULLET :: PENETRATE[BULLET :: TOTAL_TYPES]         = {1, -1, 1, -1, -1};
const int    BULLET :: IMMUNE[BULLET :: TOTAL_TYPES]            = {0, 10, 0, 0, 10};
const char   BULLET :: MRK[BULLET :: TOTAL_TYPES]               = {'*', 'S', '%', '+', 'F'};

/*
 * 敌怪的数据控制
*/
class ENEMY {
  public:
    enum Type {
        Cole, Bob, Shooter, Shooter_bullet, Artillery, Artillery_bullet, Artillery_dead
    };
    static const int TOTAL_TYPES = 7;
    static const int PROBABILITY[TOTAL_TYPES];
    static const int CAPACITY[TOTAL_TYPES];
    static const int MAX_TYPE_CNT[TOTAL_TYPES];
    static int type_cnt[TOTAL_TYPES];
    enum MOVEAI {
        Warrior, Bullet_enemy, Range
    };
  private:
    static const int MOVEAI[TOTAL_TYPES];
    static const bool MULTIPIXEL[TOTAL_TYPES];
    static const int SECONDARY[TOTAL_TYPES];
    static const int DEAD_SECONDARY[TOTAL_TYPES];
    static const int SECONDARY_COOLDOWN[TOTAL_TYPES];
    static const int SCORE[TOTAL_TYPES];
    static const double MAXSPEED[TOTAL_TYPES];
    static const double RADIUS[TOTAL_TYPES];
    static const int DAMAGE[TOTAL_TYPES];
    static const int HEALTH[TOTAL_TYPES];
    static const char MRK[TOTAL_TYPES];
    static const int COOLDOWN[TOTAL_TYPES];
    static const int TIME_LIFE[TOTAL_TYPES];
    static const int PENETRATE[TOTAL_TYPES];
    static const double ACCELERATION[TOTAL_TYPES];
    static const double INITIAL_SPEED[TOTAL_TYPES];
    static const double KNOCKBACK[TOTAL_TYPES];
    static const double KNOCKBACK_IMMUNITY[TOTAL_TYPES];
    enum Type type;
    int HP, damage, score, randval, timelife;
    int lst_attack_clock, lst_move_clock, birth_clock, lst_secondary_clock, lst_accelerate_clock, lst_knockback_clock;
    int penetrate, immune;
    crood pos, velo, tarvelo;
    double maxspeed;
  public:
    ENEMY(int ptype, crood ppos, double tar = 0) {
        type = (enum Type)ptype;
        HP = HEALTH[type];
        pos = ppos;
        damage = DAMAGE[type];
        maxspeed = MAXSPEED[type];
        score = SCORE[type];
        if(TIME_LIFE[type] < INF) { timelife = TIME_LIFE[type] * drandin(0.65, 1.5); }
        else timelife = INF;
        birth_clock = lst_attack_clock = lst_move_clock = lst_secondary_clock = lst_accelerate_clock = lst_knockback_clock = clock();
        penetrate = PENETRATE[type];
        if(MOVEAI[type] == Bullet_enemy) {
            tarvelo = vec(tar);
            tarvelo.set_len(maxspeed);
            velo = tarvelo;
        }
        velo.set_len(INITIAL_SPEED[type]);
        randval = randin(1, 10000);
        immune = 0;
    }

    void move_enemy() {
        int c = clock();
        int t = c - lst_move_clock;
        lst_move_clock = c;

        int ret = gout(pos + velo * t);
        crood d = velo * t;
        if(ret & 2) { d.set_x(0.0); velo.set_x(0.0); }
        if(ret & 1) { d.set_y(0.0); velo.set_y(0.0); } 
        pos += d;
    }
    
    void move_bullet_enemy() {
        int c = clock();
        int t = c - lst_move_clock;
        lst_move_clock = c;
        pos += velo * t;
    }

    void move() {
        switch(MOVEAI[type]) {
            case Warrior: 
            case Range: { move_enemy(); break; }
            case Bullet_enemy: { move_bullet_enemy(); break; }
        }
    }

    void get_tarvelo_warrior() {
        tarvelo = player.get_pos() - pos;
        tarvelo.set_len(min(maxspeed, dis(player.get_pos(), pos)));
    }

    void get_tarvelo_range() {
        tarvelo = player.get_pos() - pos;
        tarvelo.set_len(maxspeed);
        tarvelo.turn_dir(((randval & 1) ? 1 : -1) * PI / 9 * 4);
    }

    void get_tarvelo() {
        switch(MOVEAI[type]) {
            case Warrior: { get_tarvelo_warrior(); break; }
            case Range: { get_tarvelo_range(); break; }
        }
    }

    void accelerate() {
        get_tarvelo();
        int c = clock();
        crood deltav = tarvelo - velo;
        double deltaspeed = ACCELERATION[type] * (c - lst_accelerate_clock);
        lst_accelerate_clock = c;
        if(deltav.get_len() > deltaspeed) deltav.set_len(deltaspeed);
        velo += deltav;
    }

    void give_damage(int x, int dim, crood knockback) { 
        if(immune != 0) return ;
        immune = dim;
        HP = max(0, HP - x);
        velo += knockback * (1.0 - KNOCKBACK_IMMUNITY[type]);
    }
    void frame_operate() {
        if(immune > 0) --immune;
        accelerate();
        move();
    }
    void reclock() { lst_secondary_clock = lst_move_clock = lst_attack_clock = lst_accelerate_clock = lst_knockback_clock = clock(); }

    int get_HP() { return HP; }
    double get_HPproportion() { return max(0.0, HP * 1.0 / HEALTH[type]); } 
    int get_damage() { 
        score = SCORE[type] * 2;
        int now_clock = clock();
        return (now_clock - lst_attack_clock >= COOLDOWN[type]) ? (lst_attack_clock = now_clock, --penetrate, damage) : 0; 
    }
    crood get_pos() { return pos; }
    double get_radius() { return RADIUS[type]; }
    char get_mrk() { return MRK[type]; }
    int get_score() { return score; }
    bool get_multipixel() { return MULTIPIXEL[type]; }
    int get_type() { return type; }
    bool get_die() { return HP <= 0 || gout(pos) || (clock() - birth_clock > timelife) || (penetrate == 0); }
    int get_secondray() {
        int c = clock();
        return (c - lst_secondary_clock >= SECONDARY_COOLDOWN[type]) ? (lst_secondary_clock = c, SECONDARY[type]) : -1;
    }
    crood get_knockback() {
        int c = clock();
        return (c - lst_attack_clock >= COOLDOWN[type]) ? (lst_attack_clock = c, tarvelo * KNOCKBACK[type]) : crood(0, 0); 
    }
    int get_dead_secondary() { return DEAD_SECONDARY[type]; }
};
list<ENEMY> enemies;
typedef list<ENEMY> :: iterator Enit;

// Cole = 0, Bob = 1, Shooter = 2, Shooter_bullet = 3, Artillery = 4, Artillery_bullet = 5, Artillery_dead = 6;
const int    ENEMY :: HEALTH[ENEMY :: TOTAL_TYPES]              = {1000, 50000, 5000, 1, 350000, INF, INF};
const int    ENEMY :: DAMAGE[ENEMY :: TOTAL_TYPES]              = {150, 50, 50, 200, 0, 500, 0};
const double ENEMY :: MAXSPEED[ENEMY :: TOTAL_TYPES]            = {0.12, 0.04, 0.07, 0.3, 0, 0.35, 0.2};
const double ENEMY :: ACCELERATION[ENEMY :: TOTAL_TYPES]        = {0.0003, 0.0002, 0.0003, 0, 0, 0, 0};
const double ENEMY :: INITIAL_SPEED[ENEMY :: TOTAL_TYPES]       = {0.0, 0.0, 0.0,  0.3, 0, 0.35, 0.2};
const double ENEMY :: KNOCKBACK[ENEMY :: TOTAL_TYPES]           = {0.2, 0.2, 0.2,  0.1, 0, 1.4, 0};
const double ENEMY :: KNOCKBACK_IMMUNITY[ENEMY :: TOTAL_TYPES]  = {0,   0.93, 0.8,  0,   1, 1, 0};
const double ENEMY :: RADIUS[ENEMY :: TOTAL_TYPES]              = {8, 12, 8, 2, 35, 8, -INF}; // 将半径设为-INF可以使之无法被接触
const int    ENEMY :: TIME_LIFE[ENEMY :: TOTAL_TYPES]           = {INF, INF, INF, INF, INF, INF, 800};
const int    ENEMY :: PENETRATE[ENEMY :: TOTAL_TYPES]           = {-1, -1, -1, 1, -1, 1, -1};
const int    ENEMY :: COOLDOWN[ENEMY :: TOTAL_TYPES]            = {300, 300, 300, 0, 0, 0, 0};
const int    ENEMY :: SECONDARY[ENEMY :: TOTAL_TYPES]           = {-1, -1, ENEMY :: Shooter_bullet, -1, ENEMY :: Artillery_bullet, -1, -1};
const int    ENEMY :: DEAD_SECONDARY[ENEMY :: TOTAL_TYPES]      = {-1, -1, -1, -1, ENEMY :: Artillery_dead, -1, -1};
const int    ENEMY :: SECONDARY_COOLDOWN[ENEMY :: TOTAL_TYPES]  = {0, 0, 2000, 0, 3000, 0, 0};
const int    ENEMY :: MOVEAI[ENEMY :: TOTAL_TYPES]              = {ENEMY :: Warrior, ENEMY :: Warrior, ENEMY :: Range, ENEMY :: Bullet_enemy, ENEMY :: Warrior, ENEMY :: Bullet_enemy, ENEMY :: Bullet_enemy};
const int    ENEMY :: CAPACITY[ENEMY :: TOTAL_TYPES]            = {10, 40, 20, 0, 200, 0, 0};
const int    ENEMY :: MAX_TYPE_CNT[ENEMY :: TOTAL_TYPES]        = {INF, INF, INF, INF, 1, INF, INF};
int          ENEMY :: type_cnt[ENEMY :: TOTAL_TYPES]            = {0};
const int    ENEMY :: PROBABILITY[ENEMY :: TOTAL_TYPES]         = {8300, 800, 800, 0, 100, 0, 0}; // 万分数
const char   ENEMY :: MRK[ENEMY :: TOTAL_TYPES]                 = {'X', 'O', 'B', '-', 'H', 'C', '+'};
const bool   ENEMY :: MULTIPIXEL[ENEMY :: TOTAL_TYPES]          = {false, true, false, false, true, true, false};
const int    ENEMY :: SCORE[ENEMY :: TOTAL_TYPES]               = {10, 85, 40, 0, 1000, 0, 0};

/*
 * 物品的数据控制
*/
class ITEM {
  public:
    enum Type {
        Recovery_potion
    };
    static const int TOTAL_TYPES = 2;
    static const int PROBABILITY[TOTAL_TYPES];
    static const int BIRTH_COOLDOWN;
    static int lst_birth_clock;
  private:
    static const char MRK[TOTAL_TYPES];
    static const int TIME_LIFE[TOTAL_TYPES];
    static const int PENETRATE[TOTAL_TYPES];
    static const double RADIUS[TOTAL_TYPES];
    enum Type type;
    int count, birth_clock;
    crood pos;

    void recovery_potion() {
        player.give_damage(-300, crood(0, 0));
    }
  public:
    ITEM(int ptype, crood ppos) {
        type = (enum Type)ptype;
        pos = ppos;
        birth_clock = clock();
        count = 0;
    }

    void operate() {
        switch(type) {
            case Recovery_potion: { recovery_potion(); break; }
        }
        ++count;
    }

    crood get_pos() { return pos; }
    char get_mrk() { return MRK[type]; }
    bool get_die() { return (clock() - birth_clock > TIME_LIFE[type]) || (count >= PENETRATE[type]); }
    double get_radius() { return RADIUS[type]; }
    int get_type() { return type; }
};
list<ITEM> items;
typedef list<ITEM> :: iterator Itit;

const int    ITEM :: PROBABILITY[ITEM :: TOTAL_TYPES] = {10000};
const char   ITEM :: MRK[ITEM :: TOTAL_TYPES]         = {'R'};
const int    ITEM :: TIME_LIFE[ITEM :: TOTAL_TYPES]   = {INF};
const int    ITEM :: PENETRATE[ITEM :: TOTAL_TYPES]   = {1};
const double ITEM :: RADIUS[ITEM :: TOTAL_TYPES]      = {6};
const int    ITEM :: BIRTH_COOLDOWN                   = 60000;
int          ITEM :: lst_birth_clock                  = 0;

/*
 * 生成实体
*/
int enemy_cnt, bullet_cnt, item_cnt, enemy_capacity;

class BIRTH {
  private:
    static const int MAX_ENEMIES, MAX_BULLETS, MAX_ITEMS;
    static const double MIN_BIRTH_ENEMY_DISTANCE;
    static const double MIN_BIRTH_ITEM_DISTANCE;
  public:
    /********************** 射弹生成 **********************/
    // 生成在玩家位置的指定方向的射弹
    void birth_normal_bullet(int type, int dir) {
        #ifdef DEBUG
        fprintf(debug, "Trying to birth a bullet type = %d\n", type);
        fflush(debug);
        #endif
        int now_clock = clock();
        if(now_clock - BULLET :: lst_birth_clock[type] < BULLET :: get_cooldown(type)) return ;
        if(bullet_cnt >= MAX_BULLETS) return ;
        BULLET :: lst_birth_clock[type] = now_clock;

        crood towards(dx[dir], dy[dir]);
        double ang = angle(towards);
        double deflection = (player.get_dir() == NON) ? BULLET :: DEFLECTION[type] : BULLET :: MOVING_DEFLECTION[type];
        ang += drandin(-deflection, deflection);
        towards = vec(ang);

        bullets.push_back(BULLET(type, player.get_pos(), towards));
        ++bullet_cnt;
    }

    // 生成在指定位置的指定方向的射弹
    void birth_pos_bullet(int type, crood pos, double ang) {
        bullets.push_back(BULLET(type, pos, vec(ang)));
        ++bullet_cnt;
    }

    void birth_shock_wave() {
        int now_clock = clock(), type = BULLET :: shock_wave;
        if(now_clock - BULLET :: lst_birth_clock[type] < BULLET :: COOLDOWN[type]) return ;
        BULLET :: lst_birth_clock[type] = now_clock;

        double ang0 = PI / 100, ang = 0;
        F(i, 1, 200) {
            if(bullet_cnt >= MAX_BULLETS) return ;
            bullets.push_back(BULLET(type, player.get_pos(), vec(ang)));
            ang += ang0;
            ++bullet_cnt;
        }
    }

    /********************** 敌怪生成 **********************/
    void birth_normal_enemy() {
        if(enemy_cnt >= MAX_ENEMIES) return ;

        crood pp = player.get_pos(), ep;
        ep = randcrood();
        if(dis(ep, pp) < MIN_BIRTH_ENEMY_DISTANCE) return ;

        int type = 0;
        int randval = randin(1, 10000), randl = 1, randr = 1;
        F(i, 0, ENEMY :: TOTAL_TYPES - 1) {
            randr += ENEMY :: PROBABILITY[i];
            if(randl <= randval && randval < randr)
                { type = i; break; } 
            randl = randr;
        }
        if(ENEMY :: MAX_TYPE_CNT[type] <= ENEMY :: type_cnt[type]) return ;

        ++enemy_cnt;
        ++ENEMY :: type_cnt[type];
        enemy_capacity += ENEMY :: CAPACITY[type];
        enemies.push_back(ENEMY(type, ep));
    }

    void birth_bullet_enemy(int type, crood pos) {
        ++enemy_cnt;
        ++ENEMY :: type_cnt[type];
        double ang = angle(player.get_pos() - pos);
        enemies.push_back(ENEMY(type, pos, ang));
    }

    void birth_dead(int type, crood pos) {
        double ang0 = PI / 45, ang = 0;
        F(i, 1, 90) {
            ++enemy_cnt;
            ++ENEMY :: type_cnt[type];
            enemies.push_back(ENEMY(type, pos, ang));
            ang += ang0;
        }

    }

    void birth_enemy(int type = 0, crood pos = crood(0, 0)) {
        switch(type) {
            case ENEMY :: Cole:
            case ENEMY :: Bob:
            case ENEMY :: Shooter:
            case ENEMY :: Artillery:
                { birth_normal_enemy(); break; }
            case ENEMY :: Shooter_bullet:
            case ENEMY :: Artillery_bullet:
                { birth_bullet_enemy(type, pos); break; }
            case ENEMY :: Artillery_dead:
                { birth_dead(type, pos); break; }
        }
    }

    /********************** 物品生成 **********************/
    void birth_random_item() {
        if(item_cnt >= MAX_ITEMS) return ;
        crood pp = player.get_pos(), ip;
        do {
            ip = randcrood();
        } while(dis(ip, pp) < MIN_BIRTH_ITEM_DISTANCE);

        int randval = randin(1, 10000), type = 0, randl = 1, randr = 1;
        F(i, 0, ITEM :: TOTAL_TYPES - 1) {
            randr += ITEM :: PROBABILITY[i];
            if(randl <= randval && randval < randr)
                { type = i; break; } 
            randl = randr;
        }

        ++item_cnt;
        items.push_back(ITEM(type, ip));
    }

    static const int Random_item = 0;
    void birth_item(int command) {
        if(clock() - ITEM :: lst_birth_clock < ITEM :: BIRTH_COOLDOWN) return ;
        ITEM :: lst_birth_clock = clock();
        switch(command) {
            case Random_item: { birth_random_item(); break; }
        }
    }

    void birth_player() {
        player.init();
    }
} birth;

const int    BIRTH :: MAX_ENEMIES               = 1000;
const int    BIRTH :: MAX_BULLETS               = 1000;
const int    BIRTH :: MAX_ITEMS                 = 1000;
const double BIRTH :: MIN_BIRTH_ENEMY_DISTANCE  = 100.0;
const double BIRTH :: MIN_BIRTH_ITEM_DISTANCE   = 50.0;

/*
 * 消灭实体
*/
class DIE {
  private:
    void die_normal_enemy(Enit it) {
        --enemy_cnt;
        enemy_capacity -= ENEMY :: CAPACITY[it -> get_type()];
        --ENEMY :: type_cnt[it -> get_type()];
        enemies.erase(it);
    }

    void die_artillery(Enit it) {
        --enemy_cnt;
        enemy_capacity -= ENEMY :: CAPACITY[it -> get_type()];
        --ENEMY :: type_cnt[it -> get_type()];
        birth.birth_enemy(it -> get_dead_secondary(), it -> get_pos());
        enemies.erase(it);
    }

    void die_normal_bullet(Buit it) {
        --bullet_cnt;
        bullets.erase(it);
    }

    void die_normal_item(Itit it) {
        --item_cnt;
        items.erase(it);
    }

    void die_RPG(Buit b) {
        --bullet_cnt;
        double ang0 = PI / 20, ang = 0;
        F(i, 1, 40) { 
            birth.birth_pos_bullet(b -> get_dead_secondary(), b -> get_pos(), ang);
            ang += ang0;
        }
        bullets.erase(b);
    }
  public:
    void die_enemy(const Enit &e) {
        switch(e -> get_type()) {
            case ENEMY :: Cole:
            case ENEMY :: Bob:
            case ENEMY :: Shooter:
            case ENEMY :: Shooter_bullet:
            case ENEMY :: Artillery_bullet:
            case ENEMY :: Artillery_dead:
                { die_normal_enemy(e); break; }
            case ENEMY :: Artillery:
                { die_artillery(e); break; }
        }
    }

    void die_bullet(const Buit &b) {
        switch(b -> get_type()) {
            case BULLET :: normal: 
            case BULLET :: shock_wave: 
            case BULLET :: RPGwave:
            case BULLET :: Fire:
                { die_normal_bullet(b); break; }
            case BULLET :: RPG:           
                { die_RPG(b); break; }
        }
    }

    void die_item(const Itit &i) {
        switch(i -> get_type()) {
            case ITEM :: Recovery_potion:
                { die_normal_item(i); break; }
        }
    }
} die;

/*
 * 游戏主体的初始化和运行
*/
class GAME {
private:
    static const int MAX_ENEMY_BIRTH_FREQUENCY, HALF_CAPACITY;
    static const int MIN_FRAME_DELAY;
    static const int ITEM_BIRTH_COOLDOWN;
    static const int WEAPON_CNT = 3;
    static const int WEAPONS[WEAPON_CNT];
    static const int sShock_wave = 0, sRage_potion = 1, sSWZH = 2;
    static const int SKILLS[WEAPON_CNT];
    static const int SKILL_COOLDOWN[WEAPON_CNT];
    static int lst_skill_clock[WEAPON_CNT];
    static const char *WEAPON_NAME[WEAPON_CNT];
    static const char *SKILL_NAME[WEAPON_CNT];
    char field[SCREEN :: OUTPUT_HEIGHT + 1][SCREEN :: OUTPUT_WIDTH + 1];
    char lst_field[SCREEN :: OUTPUT_HEIGHT + 1][SCREEN :: OUTPUT_WIDTH + 1];
    int score, frame_lived, game_start_clock, lst_birth_item_clock;
    double lst_attacked_enemy_HPproportion;
    int hand_weapon, lst_hand_weapon, delta_time, lst_clock;

    /**
     * 在field上画一个实心圆
    ***/
    void draw_circle(const Enit &e) {
        crood center = e -> get_pos();
        double radius = e -> get_radius();
        int l = toint((center.get_y() - radius) / SCREEN :: CHAR_WIDTH);
        int r = toint((center.get_y() + radius) / SCREEN :: CHAR_WIDTH);
        chkmax(l, 1); chkmin(r, SCREEN :: OUTPUT_WIDTH);
        F(i, l, r) {
            double len = sqrt(sqr(radius) - sqr(center.get_y() - i * SCREEN :: CHAR_WIDTH));
            int u = toint((center.get_x() - len) / SCREEN :: CHAR_HEIGHT);
            int d = toint((center.get_x() + len) / SCREEN :: CHAR_HEIGHT);
            chkmax(u, 1); chkmin(d, SCREEN :: OUTPUT_HEIGHT);
            F(j, u, d)
                field[j][i] = e -> get_mrk();
        }
    }

    /**
     * 在field上画一个小的十字
    ***/
    void draw_cross(const Enit &e) {
        int x, y;
        crood p = e -> get_pos();
        p = crood(p.get_x() / SCREEN :: CHAR_HEIGHT, p.get_y() / SCREEN :: CHAR_WIDTH);
        toint(p, x, y);
        field[x][y] = e -> get_mrk();
        if(y < SCREEN :: OUTPUT_WIDTH) field[x][y + 1] = e -> get_mrk();
        if(y > 1) field[x][y - 1] = e -> get_mrk();
        if(x < SCREEN :: OUTPUT_HEIGHT) field[x + 1][y] = e -> get_mrk();
        if(x > 1) field[x - 1][y] = e -> get_mrk();
    }

    /**
     * 更新field
    ***/
    void update_field() {
        F(i, 1, SCREEN :: OUTPUT_HEIGHT) F(j, 1, SCREEN :: OUTPUT_WIDTH) {
            lst_field[i][j] = field[i][j];
            field[i][j] = ' ';
        }
        int x, y;
        crood p;
        for(Buit b = bullets.begin(); b != bullets.end(); ++b) {
            p = b -> get_pos();
            p = crood(p.get_x() / SCREEN :: CHAR_HEIGHT, p.get_y() / SCREEN :: CHAR_WIDTH);
            toint(p, x, y);
            field[x][y] = b -> get_mrk();
        }
        p = player.get_pos();
        p = crood(p.get_x() / SCREEN :: CHAR_HEIGHT, p.get_y() / SCREEN :: CHAR_WIDTH);
        toint(p, x, y);
        field[x][y] = PLAYER :: MRK;
        for(Enit e = enemies.begin(); e != enemies.end(); ++e) {
            if(e -> get_multipixel()) {
                draw_circle(e);
            } else {
                p = e -> get_pos();
                p = crood(p.get_x() / SCREEN :: CHAR_HEIGHT, p.get_y() / SCREEN :: CHAR_WIDTH);
                toint(p, x, y);
                field[x][y] = e -> get_mrk();
            }
        }
        for(Itit i = items.begin(); i != items.end(); ++i) {
            p = i -> get_pos();
            p = crood(p.get_x() / SCREEN :: CHAR_HEIGHT, p.get_y() / SCREEN :: CHAR_WIDTH);
            toint(p, x, y);
            field[x][y] = i -> get_mrk();
        }
    }

    /**
     * 输出field
    ***/
    void print_field() {
        screen.set_cursor(0, 0);
        int now_clock = clock();
        int length = min(player.get_HPproportion() * 50 + 0.5, 50.5);
        pr("Player HP     ["); F(i, 1, length) pr("#"); F(i, 1, 50 - length) pr(" "); pr("] %.0lf%% [%d]   \n", player.get_HPproportion() * 100, player.get_HP());

        int passed = now_clock - BULLET :: lst_birth_clock[WEAPONS[hand_weapon]], total = BULLET :: get_cooldown(WEAPONS[hand_weapon]);
        length = passed * 10 / total + 0.5;
        chkmin(length, 10);
        pr("%s [", WEAPON_NAME[hand_weapon]); F(i, 1, length) pr("#"); F(i, 1, 10 - length) pr(" "); pr("] %.1lf s      \n", max(0.0, (total - passed) / 1000.0));

        passed = now_clock - lst_skill_clock[SKILLS[hand_weapon]], total = SKILL_COOLDOWN[SKILLS[hand_weapon]];
        length = passed * 30 / total + 0.5;
        chkmin(length, 30);
        pr("%s [", SKILL_NAME[hand_weapon]); F(i, 1, length) pr("#"); F(i, 1, 30 - length) pr(" "); pr("] %.1lf s      \n", max(0.0, (total - passed) / 1000.0));

        length = lst_attacked_enemy_HPproportion * 30 + 0.5;
        pr("Enemy HP      ["); F(i, 1, length) pr("#"); F(i, 1, 30 - length) pr(" "); pr("] %.0lf%%      \n", lst_attacked_enemy_HPproportion * 100);

        F(i, 1, SCREEN :: OUTPUT_HEIGHT) F(j, 1, SCREEN :: OUTPUT_WIDTH) {
            if(field[i][j] != lst_field[i][j])
                screen.putc(field[i][j], j - 1, i - 1 + 4);
        }

        screen.set_cursor(0, SCREEN :: OUTPUT_HEIGHT + 4);
        crood v = player.get_pos();
        pr("\nEnemy count [%d] Bullet count [%d] Game time: %.1lf s Score: %d delta_time = %d|||", 
            enemy_cnt, 
            bullet_cnt,
            (now_clock - game_start_clock) / 1000.0,
            score,
            delta_time
        );
    }

    /**
     * 八向输入。
     * 按上下左右的顺序输入四个方向的对应按键符号,输出当前键盘状态所表示的对应八向方向。
    ***/
    int inputdir(char uper, char downer, char lefter, char righter) {
        bool a[4];
        a[0] = screen.check(uper);
        a[1] = screen.check(righter);
        a[2] = screen.check(downer);
        a[3] = screen.check(lefter);
        if(a[0] && a[2]) a[0] = a[2] = false;
        if(a[1] && a[3]) a[1] = a[3] = false;
        if(a[0] && a[1]) return RU; if(a[0] && a[3]) return LU; if(a[2] && a[1]) return RD; if(a[2] && a[3]) return LD;
        if(a[0]) return UP; if(a[1]) return RIGHT; if(a[2]) return DOWN; if(a[3]) return LEFT;
        return NON;
    }

    /**
     * 返回实际的敌人生成概率
    ***/
    int enemy_birth_frequency() {
        return MAX_ENEMY_BIRTH_FREQUENCY * HALF_CAPACITY / (enemy_capacity + HALF_CAPACITY);
    }

    /**
     * 切换武器
    ***/
    void shift_weapon(int tar = -1) {
        if(tar == -1) {
            lst_hand_weapon = hand_weapon++;
            if(hand_weapon >= WEAPON_CNT) hand_weapon = 0;
        } else if(tar == -2) {
            swap(hand_weapon, lst_hand_weapon);
        } else {
            if(lst_hand_weapon != hand_weapon) lst_hand_weapon = hand_weapon;
            hand_weapon = tar;
        }
    }

    /**
     * 暂停
    ***/
    void Pause() {
        screen.putarray("Paused", 6, 1, 38, 26);
        while(!screen.click('P')) { Sleep(10); }
        F(i, 39, 45)
            screen.putc(field[23][i], i - 1, 26);
    }

    /**
     * 在暂停之后调用:重置所有实体的clock。
    ***/
    void reclock() {
        int delta = clock() - lst_clock;
        for(Buit b = bullets.begin(); b != bullets.end(); ++b)
            b -> reclock();
        F(i, 0, BULLET :: TOTAL_TYPES - 1)
            BULLET :: lst_birth_clock[i] += delta;
        F(i, 0, WEAPON_CNT - 1)
            lst_skill_clock[i] += delta;
        game_start_clock += delta;
        for(Enit e = enemies.begin(); e != enemies.end(); ++e)
            e -> reclock();
        player.reclock();
        lst_clock = lst_birth_item_clock = clock();
    }

    /**
     * 释放技能
    ***/
    void release_skill(int type) {
        int c = clock();
        if(c - lst_skill_clock[type] < SKILL_COOLDOWN[type]) return ;
        lst_skill_clock[type] = c;
        switch(type) {
            case sShock_wave:  { birth.birth_shock_wave(); break; }
            case sRage_potion: { player.give_buff(PLAYER :: Grenade_rain, 1600); break; }
            case sSWZH:        { player.give_buff(PLAYER :: Belief_of_FFF, 2500); break; }
        }
    }

    /**
     * 实体更新
    ***/
    bool update_entity() {
        bool died[3] = {false, false, false};
        static const int Bu = 1, En = 0, It = 2;

        #ifdef DEBUG
        fprintf(debug, "Frame: %d\n", frame_lived);
        fflush(debug);
        #endif

        // 游戏状态更新
        if(screen.click('Q')) { shift_weapon(-2); }
        if(screen.click(VK_SHIFT)) { shift_weapon(); }
        F(i, 0, WEAPON_CNT - 1) if(screen.click(i + 1 + '0'))
            { shift_weapon(i); }
        if(screen.click('P')) { Pause(); reclock(); return false; }

        // 实体独立状态更新
        player.set_dir(inputdir('W', 'S', 'A', 'D'));
        player.frame_operate();
        for(Enit e = enemies.begin(); e != enemies.end(); ++e)
            e -> frame_operate();
        for(Buit b = bullets.begin(); b != bullets.end(); ++b)
            b -> frame_operate();
        
        // 生成实体
        F(i, 1, delta_time) {           if(randin(1, 10000) <= enemy_birth_frequency())
                birth.birth_enemy();
        }
        F(i, 1, max(delta_time / 3, 1)) {
            int bullet_dir = inputdir(VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT);
            if(bullet_dir != NON) birth.birth_normal_bullet(WEAPONS[hand_weapon], bullet_dir);
        }
        if(screen.check(VK_SPACE)) release_skill(SKILLS[hand_weapon]);
        birth.birth_item(BIRTH :: Random_item);
        
        for(Enit e = enemies.begin(); e != enemies.end(); ++e) {
            int se = e -> get_secondray();
            if(se >= 0) {
                birth.birth_enemy(se, e -> get_pos());
            }
        }

        #ifdef DEBUG
        fprintf(debug, "Birth finished\n");
        fflush(debug);
        #endif
        
        // 判断碰撞并消灭实体
        // 射弹的独立消灭
        for(Buit b = bullets.begin(); b != bullets.end(); died[Bu] ? (died[Bu] = false) : (++b, false))
            if(b -> get_die()) {
                die.die_bullet(b++);
                died[Bu] = true;
            }
        // 敌怪的独立消灭
        for(Enit e = enemies.begin(); e != enemies.end(); died[En] ? (died[En] = false) : (++e, false))
            if(e -> get_die()) {
                die.die_enemy(e++);
                died[En] = true;
            }
        // 射弹与敌怪
        for(Buit b = bullets.begin(); b != bullets.end(); died[Bu] ? (died[Bu] = false) : (++b, false))
            for(Enit e = enemies.begin(); e != enemies.end(); died[En] ? (died[En] = false) : (++e, false)) {
                crood bp = b -> get_pos();
                crood ep = e -> get_pos();
                double expect_dis = b -> get_radius() + e -> get_radius();

                bool died[2] = {false, false}; // ene, bul
                if(dis(ep, bp) < expect_dis) {
                    e -> give_damage(b -> get_damage(), b -> get_immune(), b -> get_knockback());
                    lst_attacked_enemy_HPproportion = e -> get_HPproportion();

                    if(e -> get_die()) {
                        score += e -> get_score();
                        die.die_enemy(e++);
                        died[En] = true;
                    }
                    if(b -> get_die()) {
                        die.die_bullet(b++);
                        died[Bu] = true;
                    }
                }
            }
        // 物品与玩家
        for(Itit i = items.begin(); i != items.end(); died[It] ? (died[It] = false) : (++i, false)) {
            crood ip = i -> get_pos();
            crood pp = player.get_pos();
            double expect_dis = player.get_radius() + i -> get_radius();
            if(dis(ip, pp) < expect_dis) {
                i -> operate();
                if(i -> get_die()) {
                    die.die_item(i++);
                    died[It] = true;
                }
            }
        }
        // 敌怪与玩家
        bool Gamefail = false;
        for(Enit e = enemies.begin(); e != enemies.end(); died[En] ? (died[En] = false) : (++e, false)) {
            crood ep = e -> get_pos();
            crood pp = player.get_pos();
            double expect_dis = player.get_radius() + e -> get_radius();
            if(dis(ep, pp) < expect_dis) {
                player.give_damage(e -> get_damage(), e -> get_knockback());
                if(player.get_HP() <= 0)
                    Gamefail = true;
                if(e -> get_die()) {
                    score += e -> get_score();
                    die.die_enemy(e++);
                    died[En] = true;
                }
            }
        }
        #ifdef DEBUG            
        fprintf(debug, "Die finished\n");
        fflush(debug);
        #endif

        return Gamefail;
    }

public:
    /**
     * 初始化
    ***/
    void Init() {
        system("cls");
        F(i, 1, SCREEN :: OUTPUT_HEIGHT) F(j, 1, SCREEN :: OUTPUT_WIDTH)
            field[i][j] = lst_field[i][j] = ' ';
        enemy_cnt = bullet_cnt = item_cnt = frame_lived = score = enemy_capacity = 0;
        F(i, 0, ENEMY :: TOTAL_TYPES - 1)
            ENEMY :: type_cnt[i] = 0;
        hand_weapon = lst_hand_weapon = 0;
        lst_attacked_enemy_HPproportion = 0;
        enemies.clear();
        bullets.clear();
        items.clear();
        birth.birth_player();
        print_field();

        game_start_clock = clock();
        F(i, 0, BULLET :: TOTAL_TYPES - 1)
            BULLET :: lst_birth_clock[i] = game_start_clock;
        F(i, 0, WEAPON_CNT - 1)
            lst_skill_clock[i] = game_start_clock;
        lst_birth_item_clock = ITEM :: lst_birth_clock = game_start_clock;
    }

    /**
     * 游戏运行循环
    ***/
    void Rungame() {
        lst_clock = clock();
        while(true) {
            delta_time = clock() - lst_clock;
            if(delta_time < MIN_FRAME_DELAY) {
                Sleep(MIN_FRAME_DELAY - delta_time);
                delta_time = MIN_FRAME_DELAY;
            }
            lst_clock = clock();
            ++frame_lived;
            
            bool Gamefail = update_entity();

            // 输出
            update_field();
            print_field();
            if(Gamefail)
                return ;
        }
    }

    /**
     * 游戏失败
    ***/
    void fail() {
        screen.draw_square(20, 15, 59, 34);
        screen.putarray("You failed!", 11, 1, 35, 18);
        char s[38];
        sprintf(s, "Score: %d", score);
        screen.set_cursor(25, 23); for(int i = 0; s[i] != '\0'; ++i) pr("%c", s[i]);
        int t = (clock() - game_start_clock) / 1000;
        if(t < 60) sprintf(s, "Time survived: %d sec", t);
        else sprintf(s, "Time survived: %d min %d sec", t / 60, t % 60);
        screen.set_cursor(25, 25); for(int i = 0; s[i] != '\0'; ++i) pr("%c", s[i]);
        screen.putarray("Press space bar to restart", 26, 1, 27, 30);

        while(!screen.check(VK_SPACE));
    }
} Game;

const int   GAME :: MIN_FRAME_DELAY                     = 3; // 最小帧间时间
const int   GAME :: MAX_ENEMY_BIRTH_FREQUENCY           = 80; // 每毫秒生成敌人几率的万分比。10除以它,就是期望生成一个敌人的间隔秒数  默认为50 (0.2秒一个敌人)
const int   GAME :: HALF_CAPACITY                       = 120; 
const int   GAME :: WEAPONS[GAME :: WEAPON_CNT]         = {BULLET :: normal, BULLET :: RPG, BULLET :: Fire}; 
const int   GAME :: SKILLS[GAME :: WEAPON_CNT]          = {GAME :: sShock_wave, GAME :: sRage_potion, GAME :: sSWZH}; 
const int   GAME :: SKILL_COOLDOWN[GAME :: WEAPON_CNT]  = {15000, 20000, 25000}; 
int         GAME :: lst_skill_clock[GAME :: WEAPON_CNT] = {0, 0, 0}; 
const char *GAME :: WEAPON_NAME[GAME :: WEAPON_CNT]     = {"Machine Gun  ", "RPG          ", "Flame thrower"};
const char *GAME :: SKILL_NAME[GAME :: WEAPON_CNT]      = {"Shock wave   ", "Grenade rain ", "Belief of FFF"};

/*
 * main函数
*/
int main() {
    srand(time(0));
    screen.init();
    debug = fopen("debug.out", "w");
    while(true) {
        Game.Init();
        Game.Rungame();
        Game.fail();
    }
    return 0;
}
/*

使用说明:(V4.3)
wasd控制玩家,
方向键控制射击。
空格放技能。
shift切换武器。
1,2,3选择武器。
Q键返回上一个拿着的武器。
P暂停。

更新日志:

V1.0 20200913
 - 实现了80 * n控制台显示。
 - 实现了kbhit + getch输入控制。
 - 加入了玩家对象、子弹对象、敌人对象、游戏运行对象。
 - 可以玩了。
 - 加入了失败页面,可以重新开始。

V2.0 [ 操作更新 ] 20200918
 - 实现了GetKeyState和GetAsyncKeyState输入控制。
   - 可以边走边射击。
 - 将整数形式的移动改为实数形式,使得实体运动更加流畅。
   - 设置了圆形碰撞箱。取消了敌人的攻击距离。
 - 改为用方向键控制四向射击。

V2.1 20200918
 - 实现了用四个键进行八向控制,包括玩家移动和射击。
   - 如果同时按了相对的两个键,就会忽略它;如果同时按了三个键,也会忽略那两个相对的,视为只按了中间那个。

V2.2 20200920
 - 将储存实体(entity)的结构从数组改为链表,进行优化。
 - 敌人的运行方向连续化。
 - 常数优化。

V2.3 20200920
 - 改变换帧方式,用clock()求得两次运行时间差,乘以速度进行换帧。
 - 玩家死亡之后,从(y/n)改为按空格重新开始。不想重新开始了就直接把窗口关掉好了。

V2.4 20200920
 - 命令行窗口的每个字符的高宽是不同的,所以“斜上方”并不是45度。通过调整输出把这个问题解决了。
 - 修复了在场地边缘试图斜向行走时不能动的问题。

V3.0 [ 战斗更新 ] 20200920
 - 两种敌怪,会有不同的速度和血量。也就是经典的“坚固笨重+脆弱迅速”组合。
 - 射击和敌怪攻击有了cd。

V3.1 20200920
 - 射弹会随机偏转一个小角度。
 - 射弹会在玩家周围的某个范围内随机生成。范围可能不是以玩家中心对称的,而且方向由射弹的方向决定。
 - 加入了一个按空格发动的技能:向周围发射一圈冲击波。

V3.2 20200920
 - 在上方显示玩家血条,技能加载条,和攻击的最后一个敌怪的血条。同时显示相应数字。

V3.3 20200922
 - 对内核进行了较大改动。优化了对CPU的消耗。

V3.4 20200922
 - 扩大了坚固笨重型敌怪的半径。
 - 在显示时,坚固笨重型敌怪会显示为大于一个字符大小的圆形。
 - 杀敌数统计改为分数统计。不同的敌怪分数不同。
 - 平衡性调整。当敌怪数量过多时会降低生成率。

V3.5 20200924
 - 玩家的血量会缓慢恢复。
 - 低概率随机出现血瓶。

V4.0 [ 射弹更新 ] 20200926
 - 可远程攻击的敌怪。
 - 两种武器。用shift切换。

V4.1 20200927
 - 第三种武器。
 - 机枪的射速降低,伤害提高,精准度提高。
 - 调整了一些其他数据。
 - 一种火力更强、血量更高的炮塔类型敌怪。
 - 各个武器有独立的技能。

V4.2 20200927
 - 加入暂停功能。
 - 改进了换武器的手感。
 - 用数字键切换武器,按q返回上一个拿着的武器。保留shift切换到下一个的功能。
 - 穿透武器会对敌怪造成无敌帧。

V4.3 20201004
 - 改进了代码结构。
 - 攻击有了击退。
 - 有的实体会有一个逐渐加速的过程。
 - 炮台被击破时有特效。
*/
posted @ 2020-10-09 21:11  lightmain  阅读(312)  评论(1编辑  收藏  举报