/*
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
- 改进了代码结构。
- 攻击有了击退。
- 有的实体会有一个逐渐加速的过程。
- 炮台被击破时有特效。
*/