// Multiplayer Maze FPS - 纯源代码版
// 仅需要 main.cpp 文件,无需项目配置
#include <iostream>
#include <conio.h>
#include <cstdlib>
#include <ctime>
#include <windows.h>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
// 兼容设置
#define getch _getch
#define kbhit _kbhit
const int MAP_WIDTH = 61;
const int MAP_HEIGHT = 21;
const int MAX_PLAYERS = 4;
const int MAX_ENEMIES = 10;
const int MAX_BULLETS = 30;
enum ControlScheme { WASD, TFGH, IJKL, ARROWS };
char map[MAP_HEIGHT][MAP_WIDTH];
struct Player {
int x, y, hp, lives, score;
int ammo[3], weapon, color;
char dir;
ControlScheme controls;
bool active;
};
struct Enemy {
int x, y, hp, type;
bool active;
int aiTimer;
};
struct Boss {
int x, y, hp, maxHp, phase;
bool active;
int attackTimer;
vector<pair<int,int> > minions;
} boss;
struct Bullet {
int x, y, dx, dy, owner, type;
bool active;
};
Player players[MAX_PLAYERS];
Enemy enemies[MAX_ENEMIES];
Bullet bullets[MAX_BULLETS];
int playerCount = 1;
int level = 1;
bool gameRunning = true;
// 生成迷宫
void generateMaze() {
for(int y = 0; y < MAP_HEIGHT; y++)
for(int x = 0; x < MAP_WIDTH; x++)
map[y][x] = '#';
stack<pair<int,int> > stk;
int x = 1, y = 1;
map[y][x] = ' ';
stk.push(make_pair(x, y));
while(!stk.empty()) {
x = stk.top().first;
y = stk.top().second;
vector<int> dirs;
if(y > 2 && map[y-2][x] == '#') dirs.push_back(0);
if(x < MAP_WIDTH-3 && map[y][x+2] == '#') dirs.push_back(1);
if(y < MAP_HEIGHT-3 && map[y+2][x] == '#') dirs.push_back(2);
if(x > 2 && map[y][x-2] == '#') dirs.push_back(3);
if(!dirs.empty()) {
int dir = dirs[rand() % dirs.size()];
switch(dir) {
case 0: map[y-1][x] = ' '; y -= 2; break;
case 1: map[y][x+1] = ' '; x += 2; break;
case 2: map[y+1][x] = ' '; y += 2; break;
case 3: map[y][x-1] = ' '; x -= 2; break;
}
map[y][x] = ' ';
stk.push(make_pair(x, y));
} else {
stk.pop();
}
}
map[1][1] = map[1][MAP_WIDTH-2] = ' ';
map[MAP_HEIGHT-2][1] = map[MAP_HEIGHT-2][MAP_WIDTH-2] = ' ';
}
// 初始化玩家
void initPlayers(int count) {
int colors[] = {14, 10, 13, 11};
int pos[4][2] = {{1,1}, {MAP_WIDTH-2,1},
{1,MAP_HEIGHT-2}, {MAP_WIDTH-2,MAP_HEIGHT-2}};
for(int i = 0; i < count; i++) {
players[i] = {pos[i][0], pos[i][1], 100, 3, 0,
{100,50,30}, 0, colors[i], '^',
(ControlScheme)i, true};
}
}
// 生成敌人
void spawnEnemy() {
for(int i = 0; i < MAX_ENEMIES; i++) {
if(!enemies[i].active) {
int attempts = 0;
do {
enemies[i].x = 1 + rand() % (MAP_WIDTH-2);
enemies[i].y = 1 + rand() % (MAP_HEIGHT-2);
attempts++;
} while(map[enemies[i].y][enemies[i].x] != ' ' && attempts < 100);
if(attempts < 100) {
enemies[i].hp = 2 + level;
enemies[i].type = rand() % 2;
enemies[i].aiTimer = 15 + rand() % 15;
enemies[i].active = true;
}
break;
}
}
}
// 生成BOSS
void spawnBoss() {
boss = {MAP_WIDTH/2, MAP_HEIGHT/2, 50+level*20,
50+level*20, 0, true, 30, {}};
}
// 射击
void shoot(int pid) {
Player& p = players[pid];
for(int i = 0; i < MAX_BULLETS; i++) {
if(!bullets[i].active) {
bullets[i] = {p.x, p.y, 0, 0, pid, p.weapon, true};
switch(p.dir) {
case '^': bullets[i].dy = -1; break;
case 'v': bullets[i].dy = 1; break;
case '<': bullets[i].dx = -1; break;
case '>': bullets[i].dx = 1; break;
}
if(p.weapon > 0) {
if(--p.ammo[p.weapon] <= 0) p.weapon = 0;
}
Beep(1000, 50);
break;
}
}
}
// 更新游戏
void updateGame() {
// 敌人逻辑
for(int i = 0; i < MAX_ENEMIES; i++) {
if(enemies[i].active && --enemies[i].aiTimer <= 0) {
enemies[i].aiTimer = 15 + rand() % 20;
int dir = rand() % 2;
int& coord = dir ? enemies[i].x : enemies[i].y;
int delta = (rand() % 2) ? 1 : -1;
int newPos = coord + delta;
if(newPos > 0 && newPos < (dir ? MAP_WIDTH : MAP_HEIGHT)-1
&& map[dir ? enemies[i].y : newPos][dir ? newPos : enemies[i].x] == ' ') {
coord = newPos;
}
for(int p = 0; p < playerCount; p++) {
if(players[p].active &&
abs(enemies[i].x-players[p].x) <= 1 &&
abs(enemies[i].y-players[p].y) <= 1) {
players[p].hp -= 10;
if(players[p].hp <= 0) {
if(--players[p].lives <= 0) players[p].active = false;
else players[p].hp = 100;
}
}
}
}
}
// BOSS逻辑
if(boss.active && --boss.attackTimer <= 0) {
boss.attackTimer = 30 - boss.phase*5;
if(boss.hp < boss.maxHp/2) boss.phase = 1;
if(boss.hp < boss.maxHp/4) boss.phase = 2;
if(boss.phase == 0 && rand()%3 == 0) {
if(boss.x < players[0].x) boss.x++;
else if(boss.x > players[0].x) boss.x--;
}
if(boss.phase == 1 && rand()%4 == 0) {
int mx = boss.x + rand()%5 - 2;
int my = boss.y + rand()%5 - 2;
if(mx > 0 && my > 0 && mx < MAP_WIDTH-1 && my < MAP_HEIGHT-1
&& map[my][mx] == ' ') {
boss.minions.push_back(make_pair(mx, my));
}
}
}
// 子弹逻辑
for(int i = 0; i < MAX_BULLETS; i++) {
if(bullets[i].active) {
int nx = bullets[i].x + bullets[i].dx;
int ny = bullets[i].y + bullets[i].dy;
if(nx <= 0 || ny <= 0 || nx >= MAP_WIDTH-1 || ny >= MAP_HEIGHT-1
|| map[ny][nx] == '#') {
bullets[i].active = false;
continue;
}
bullets[i].x = nx;
bullets[i].y = ny;
for(int p = 0; p < playerCount; p++) {
if(players[p].active && p != bullets[i].owner
&& nx == players[p].x && ny == players[p].y) {
players[p].hp -= 20;
bullets[i].active = false;
if(players[p].hp <= 0) {
if(--players[p].lives <= 0) players[p].active = false;
else players[p].hp = 100;
}
}
}
if(boss.active && bullets[i].owner >= 0
&& nx == boss.x && ny == boss.y) {
boss.hp -= 15;
bullets[i].active = false;
if(boss.hp <= 0) {
boss.active = false;
for(int p = 0; p < playerCount; p++)
players[p].score += 100;
}
}
}
}
}
// 绘制游戏
void drawGame() {
system("cls");
for(int y = 0; y < MAP_HEIGHT; y++) {
for(int x = 0; x < MAP_WIDTH; x++) {
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
bool drawn = false;
for(int p = 0; p < playerCount; p++) {
if(players[p].active && x == players[p].x && y == players[p].y) {
SetConsoleTextAttribute(h, players[p].color);
cout << players[p].dir;
drawn = true;
break;
}
}
if(!drawn && boss.active && x == boss.x && y == boss.y) {
SetConsoleTextAttribute(h, 12);
cout << 'B';
drawn = true;
}
if(!drawn) {
for(int i = 0; i < MAX_ENEMIES; i++) {
if(enemies[i].active && x == enemies[i].x && y == enemies[i].y) {
SetConsoleTextAttribute(h, 12);
cout << 'E';
drawn = true;
break;
}
}
}
if(!drawn && boss.active) {
for(int i = 0; i < (int)boss.minions.size(); i++) {
if(x == boss.minions[i].first && y == boss.minions[i].second) {
SetConsoleTextAttribute(h, 13);
cout << 'S';
drawn = true;
break;
}
}
}
if(!drawn) {
for(int i = 0; i < MAX_BULLETS; i++) {
if(bullets[i].active && x == bullets[i].x && y == bullets[i].y) {
SetConsoleTextAttribute(h, 14);
cout << '*';
drawn = true;
break;
}
}
}
if(!drawn) {
if(map[y][x] == '#') {
SetConsoleTextAttribute(h, 8);
cout << '#';
} else {
cout << ' ';
}
}
}
cout << endl;
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
for(int p = 0; p < playerCount; p++) {
if(players[p].active) {
cout << "P" << p+1 << ": HP=" << players[p].hp
<< " Lives=" << players[p].lives
<< " Score=" << players[p].score << " ";
}
}
if(boss.active) {
cout << "\nBOSS HP: " << boss.hp << "/" << boss.maxHp
<< " Phase: " << boss.phase+1;
}
cout << "\nControls: 1-WASD 2-TFGH 3-IJKL 4-ARROWS | Space-Shoot | Q-Weapon";
}
// 主函数
int main() {
srand((unsigned)time(NULL));
cout << "Players (1-4): ";
cin >> playerCount;
if(playerCount < 1 || playerCount > 4) playerCount = 1;
initPlayers(playerCount);
generateMaze();
for(int i = 0; i < 3; i++) spawnEnemy();
while(gameRunning) {
if(kbhit()) {
int key = getch();
if(key == 224) { // 方向键
key = getch();
for(int p = 0; p < playerCount; p++) {
if(players[p].controls == ARROWS && players[p].active) {
switch(key) {
case 72: players[p].dir = '^'; break;
case 80: players[p].dir = 'v'; break;
case 75: players[p].dir = '<'; break;
case 77: players[p].dir = '>'; break;
}
}
}
} else {
for(int p = 0; p < playerCount; p++) {
if(!players[p].active) continue;
switch(players[p].controls) {
case WASD:
if(key == 'w') { int ny = players[p].y-1; if(ny > 0 && map[ny][players[p].x]==' ') {players[p].y--; players[p].dir='^';} }
else if(key == 's') { int ny = players[p].y+1; if(ny < MAP_HEIGHT-1 && map[ny][players[p].x]==' ') {players[p].y++; players[p].dir='v';} }
else if(key == 'a') { int nx = players[p].x-1; if(nx > 0 && map[players[p].y][nx]==' ') {players[p].x--; players[p].dir='<';} }
else if(key == 'd') { int nx = players[p].x+1; if(nx < MAP_WIDTH-1 && map[players[p].y][nx]==' ') {players[p].x++; players[p].dir='>';} }
break;
case TFGH:
if(key == 't') { int ny = players[p].y-1; if(ny > 0 && map[ny][players[p].x]==' ') {players[p].y--; players[p].dir='^';} }
else if(key == 'g') { int ny = players[p].y+1; if(ny < MAP_HEIGHT-1 && map[ny][players[p].x]==' ') {players[p].y++; players[p].dir='v';} }
else if(key == 'f') { int nx = players[p].x-1; if(nx > 0 && map[players[p].y][nx]==' ') {players[p].x--; players[p].dir='<';} }
else if(key == 'h') { int nx = players[p].x+1; if(nx < MAP_WIDTH-1 && map[players[p].y][nx]==' ') {players[p].x++; players[p].dir='>';} }
break;
case IJKL:
if(key == 'i') { int ny = players[p].y-1; if(ny > 0 && map[ny][players[p].x]==' ') {players[p].y--; players[p].dir='^';} }
else if(key == 'k') { int ny = players[p].y+1; if(ny < MAP_HEIGHT-1 && map[ny][players[p].x]==' ') {players[p].y++; players[p].dir='v';} }
else if(key == 'j') { int nx = players[p].x-1; if(nx > 0 && map[players[p].y][nx]==' ') {players[p].x--; players[p].dir='<';} }
else if(key == 'l') { int nx = players[p].x+1; if(nx < MAP_WIDTH-1 && map[players[p].y][nx]==' ') {players[p].x++; players[p].dir='>';} }
break;
default: break;
}
}
if(key == ' ') {
for(int p = 0; p < playerCount; p++)
if(players[p].active) shoot(p);
} else if(key == 'q' || key == 'Q') {
for(int p = 0; p < playerCount; p++) {
if(players[p].active) {
do {
players[p].weapon = (players[p].weapon+1) % 3;
} while(players[p].weapon != 0 && players[p].ammo[players[p].weapon] <= 0);
}
}
}
}
}
updateGame();
drawGame();
Sleep(100);
// 随机刷怪
int activeEnemies = 0;
for(int i = 0; i < MAX_ENEMIES; i++)
if(enemies[i].active) activeEnemies++;
if(activeEnemies < 3 && rand() % 30 == 0)
spawnEnemy();
// BOSS触发
if(!boss.active && players[0].score > 50*level*level) {
spawnBoss();
level++;
}
// 胜负判断
bool alive = false;
for(int p = 0; p < playerCount; p++)
if(players[p].active) alive = true;
if(!alive) gameRunning = false;
}
cout << "\nGAME OVER! Scores:\n";
for(int p = 0; p < playerCount; p++)
cout << "P" << p+1 << ": " << players[p].score << endl;
cout << "Press any key...";
getch();
return 0;
}