(原创) 目前正在不断更新!
★ 一款超级有趣的大乱斗游戏,包含多种游戏模式,支持双人联机。 离线情况下也可以与多个(或一群)机器玩家进行疯狂的对战。 直接上图
使用C++ with EGE图形库编写 有一定数量的BUG,请谅解。
[ ↑双人离线的一张截图 ]
操作说明
A,W,S,D | 主玩家移动 1/2/3/4 | 主玩家选择武器或道具 J | 主玩家向面前方向攻击 Backspace | 主玩家删除当前武器或道具(注意:装备是靠刷新更好的,不能删) 鼠标左键 | 向鼠标方向攻击(非常方便),将被识别成八个方向之一(双人离线为了平等不能使用)
↑←↓→ | 双人离线时玩家2的移动 小键盘1/2/3/4 | 双人离线时玩家2选择武器或道具 小键盘5 | 双人离线时玩家2向面前方向攻击 Delete | 双人离线时玩家2删除当前武器或道具
F2 | 立即窗口截图,存入Screenshots文件夹中 Esc | 退出战斗
基本玩法
比如说人机离线对战模式(是可以使用鼠标攻击的), 开局就先随机获得两把武器,两个道具,没有装备。 你可以通过右侧的对方方位与距离锁定敌人的位置进行有效攻击。 机器玩家的算法还说得过去,有时真难弄死它。 [↑ 人机对战的一张截图 ] 普通模式下一般都是300滴血,只有比较容易死的模式会1000血甚至5000血。(自己探索) 有些武器带效果的,要注意防护与使用: 有些剑是带剑气的; 有些弹药是会反弹/爆炸/分裂/追踪(分为3种等级)/穿墙的。 武器千奇百怪,战斗场面琳琅满目: [↑ 混战模式的一张截图 ——那个血量不是正常的血量,请忽视 ] 群殴模式是很难挺过去的,你可以尝试一下!! 群殴模式会给你1000血加上长时间的矫捷效果与永久的穿人特权。就算这样也能死得很惨。 诀窍是不断逃跑,让他们自己误伤。逃跑的时候注意安全。
- 离线团战的话一定要把对方所有人消灭,但自己死了就算失败了。
- 作为红队头领,一定要学会保护自己!
右侧将会有两队活人数数据。
最后关于联机:
-
在线模式下服务端开启后,客户端依次输入服务端IP地址和端口号即可 (若无外网则必须同一局域网下) 配置外网时映射的IP需要是服务端开启后显示的IP(也就是服务端电脑第一个能用的IP), -
端口号一律填8888. 使用外网时,在输入IP地址界面不得输入域名。 客户端输入的端口号是映射后的端口号(一般是五位数而不是8888) 需要使用IP地址。获取其IP地址可以通过ping命令获得: Win+R打开运行,输入cmd -
联机的时候经常会崩溃,应该是这个TCP连接太差劲的缘故,也么有办法,因此我尽量减少机器玩家的数量。凑合着玩吧。不要使用频率极高的武器,因为更容易致使崩溃。
这款游戏已经写了一年左右了,祝愿大家玩得愉快 源码真的很长(快一万行了)真的想要的话(也不给 XD ) 无源码的游戏链接: https://download.csdn.net/download/cjz2005/86287023
★★★
为了造福广大苍生,我决定放一点源码给大家看看! 这是机器玩家的AI算法,原创,还行,有兴趣的同志可以好好研究一下。
#define DIR_NONE 0x00
#define DIR_4 0x01
#define DIR_8 0x02
#define AAM_STILL 0
#define AAM_WANDER 1
#define AAM_CHASE 2
#define AAM_FLEE 3
#define AA_CHANGE_MODE_RATE 25
#define AA_CHANGE_MODE_MIN_T 1000*6
#define AA_CHANGE_WEAPON_RATE 32
#define AA_CHANGE_WEAPON_MIN_T 1000*8
#define AA_LEVEL_UP_T 1000*43
#define AA_DRINK_POTION_RATE 8
#define AA_DODGE_BASE_RATE 13
#define AA_DODGE_RATE_MAX 60
#define AA_DODGE_UP_RATIO 0.5
#define AA_DODGE_T 1000*0.9
#define AA_EXTRA_DODGE_RATE_T 1000*65
#define AA_CHANGE_TARGET_MIN_T 11000
#define AA_CHANGE_TARGET_RATE 8
#define AA_REVENGE_RATE 40
class AIControl {
public:
bool enabled;
int my_index;
int target_index;
int team_index;
clock_t lastModeChanged;
clock_t lastWeaponChanged;
clock_t lastChangeTarget;
clock_t lastUp;
clock_t lastDodge;
clock_t lastExtraRateChanged;
int extraDodgeRate;
int mode;
int xp;
BYTE extraMode;
float adjust;
const vector<int> mode_prob = { 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3 };
const vector<int> curw_prob = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3 };
AIControl()
{
target_index = 0;
team_index = 0;
adjust = 0;
enabled = false;
Init();
}
void Init()
{
my_index = 1;
target_index = 0;
extraDodgeRate = 0;
extraMode = AAE_NONE;
mode = AAM_CHASE;
lastModeChanged = clock() + 5000;
lastWeaponChanged = clock();
lastExtraRateChanged = clock();
lastChangeTarget = clock();
lastUp = clock();
lastDodge = clock();
xp = 0;
adjust = (GetPlayerCount() * 5 + 10101 + RandomRange(0, 100, true, false)) % 100 / 100.0;
}
void Close()
{
enabled = false;
mode = AAM_STILL;
xp = 0;
target_index = 0;
}
void Update()
{
if (!enabled || !IsPlayerAlive(my_index))
return;
CheckLevelUp();
int r;
r = RandomRange(0, 100, true, false);
if (r > 100 - AA_CHANGE_MODE_RATE - adjust*6 && (clock() - lastModeChanged > AA_CHANGE_MODE_MIN_T))
{
mode = mode_prob.at(RandomRange(0, mode_prob.size() - 1, true, true));
lastModeChanged = clock();
}
if (clock() - lastExtraRateChanged > AA_EXTRA_DODGE_RATE_T)
{
extraDodgeRate = Varience(0, 5+adjust*6);
lastExtraRateChanged = clock();
}
Move();
if (clock() - lastDodge > AA_DODGE_T + adjust * 3)
{
TryToDodge();
}
if (g_bIdt != IDT_OFFLINE)
{
if(GetDirMode(GetPlayerCurWeapon(my_index)) == DIR_8)
GetPlayerFace(my_index) = FaceDir8(target_index);
else
GetPlayerFace(my_index) = FaceDir4(target_index);
}
#ifndef AI_CONCENTRATE
if(g_mode != 0 && g_mode != 1 && g_mode != 2
&& g_mode != 3
&& (!IsPlayerAlive(target_index)
|| (
RandomRange(0,100,true,false) < AA_CHANGE_TARGET_RATE + adjust * 9
&& clock() - lastChangeTarget >= AA_CHANGE_TARGET_MIN_T)))
{
int new_target_index = this->target_index;
int min_dist = 9999;
for(int i = 0; i < GetPlayerCount(); ++i)
{
if (!IsPlayerAlive(i) || i == my_index)
continue;
if ((g_mode == 5 || g_mode == 9)
&& GetPlayerTeam(i) == team_index)
{
continue;
}
if ((g_mode == 6 || g_mode == 7) && i != 0 && i != 1)
continue;
auto _dist = Distance(GetX(),GetY(),GetPlayerX(i),GetPlayerY(i));
if(_dist < min_dist)
{
new_target_index = i;
min_dist = _dist;
}
}
this->target_index = new_target_index;
}
#endif
if (RandomRange(0, 100, true, false) < 20 + adjust * 10)
this->mode = AAM_CHASE;
Attack();
}
void CheckLevelUp()
{
if (clock() - lastUp > AA_LEVEL_UP_T)
{
int upv = 1;
if (GetPlayerHp(my_index) > 1 && GetPlayerHp(target_index) > GetPlayerHp(my_index) * 1.8)
upv = 2;
xp += upv;
lastUp = clock();
}
}
void OnHurt(int hurt_by)
{
if(!enabled)
return;
lastUp += 1000 * RandomRange(0,3,true,true);
if(mode == AAM_STILL || mode == AAM_WANDER)
lastModeChanged += 1000 * 4 + adjust * 100;
if (PlayerHasAffect(my_index,3) || PlayerHasAffect(my_index,7) || PlayerHasAffect(my_index,11) || PlayerHasAffect(my_index,12))
{
extraDodgeRate += RandomRange(5, 9 + adjust * 2, true, false);
}
else {
extraDodgeRate += RandomRange(4, 7 + adjust * 2, true, false);
}
if (hurt_by != my_index && hurt_by != target_index
&& (g_mode == 5 || g_mode == 9) || (GetPlayerTeam(hurt_by) != team_index)
&& g_mode != 3
)
{
if (RandomRange(0, 100, true, false) < AA_REVENGE_RATE)
{
target_index = hurt_by;
}
}
}
DIR FaceDir8(int i) const
{
POINT et{GetPlayerX(i),GetPlayerY(i)};
if (IsOuttaField(POINT2(et.x, et.y)))
return 0;
int x = GetX();
int y = GetY();
int ex = GetPlayerX(target_index);
int ey = GetPlayerY(target_index);
if (x < ex)
{
if (y > ey) return RIGHTUP;
else if (y < ey) return RIGHTDOWN;
else return RIGHT;
}
else if (x > ex)
{
if (y > ey) return LEFTUP;
else if (y < ey) return LEFTDOWN;
else return LEFT;
}
else {
if (y > ey) return UP;
else return DOWN;
}
}
DIR FaceDir4(int i) const
{
POINT et{GetPlayerX(i),GetPlayerY(i)};
if (IsOuttaField(POINT2(et.x, et.y)))
return 0;
int x = GetX();
int y = GetY();
int ex = GetPlayerX(target_index);
int ey = GetPlayerY(target_index);
if (ex > x)
{
if (ey > y)
return (abs(ex - x) > abs(ey - y) ? (RandomRange(1, 10) > 7 ? RIGHT : DOWN) : (RandomRange(1, 10) > 7 ? DOWN : RIGHT));
else if (ey < y)
return (abs(ex - x) > abs(ey - y) ? (RandomRange(1, 10) > 7 ? RIGHT : UP) : (RandomRange(1, 10) > 7 ? UP : RIGHT));
else
return RIGHT;
}
else if (ex < x)
{
if (ey > y)
return (abs(ex - x) > abs(ey - y) ? (RandomRange(1, 10) > 7 ? LEFT : DOWN) : (RandomRange(1, 10) > 7 ? DOWN : LEFT));
else if (ey < y)
return (abs(ex - x) > abs(ey - y) ? (RandomRange(1, 10) > 7 ? LEFT : UP) : (RandomRange(1, 10) > 7 ? UP : LEFT));
else
return LEFT;
}
else {
if (ey > y)
return DOWN;
else
return UP;
}
}
UINT GetI() const {
return my_index;
}
UINT GetOppoI() const {
return target_index;
}
int GetX() const
{
return GetPlayerX(GetI());
}
int GetY() const
{
return GetPlayerY(GetI());
}
void _Tag(string text) const
{
setcolor(WHITE);
setbkmode(OPAQUE);
setfont(20, 0, "微软雅黑");
xyprintf(20, 500, "TAG:%s", text.c_str());
}
void Move()
{
EXCEPTION_L
if (mode == AAM_WANDER)
{
if (!PlayerTimeToMove(my_index))
return;
DIR dir;
int x, y;
_retry:
dir = 2 * RandomRange(1, 4, true, true) - 1;
x = GetX();
y = GetY();
if (PlayerHasAffect(GetI(), 12))
dir = OppoDir(dir);
DirOffsetPos(x, y, dir, "AIControl::Move");
if (IsOuttaField(POINT2(x,y)) ||
IsBarrier(bk(x, y, "AIControl::Move")))
{
goto _retry;
}
GetPlayerX(my_index) = x;
GetPlayerY(my_index) = y;
bool sendMsg = (g_bIdt != IDT_OFFLINE);
if (sendMsg)
SendTCP(UM_SETPOS, x, y, GetI());
LastMove(my_index);
}
else if (mode == AAM_CHASE || mode == AAM_FLEE
|| mode == AAM_STILL)
{
if (!PlayerTimeToMove(my_index))
return;
int x = GetX();
int y = GetY();
int ex = GetPlayerX(target_index);
int ey = GetPlayerY(target_index);
DIR xd, yd;
if (ex > x)
xd = RIGHT;
else if (ex < x)
xd = LEFT;
else
xd = 0;
if (ey > y)
yd = DOWN;
else if (ey < y)
yd = UP;
else
yd = 0;
int r = RandomRange(0, 1, true, true);
DIR dir;
if (xd == RIGHT)
{
if (yd == 0)
dir = RIGHT;
else {
dir = (r ? RIGHT : yd);
}
}
else if (xd == LEFT)
{
if (yd == 0)
dir = LEFT;
else {
dir = (r ? LEFT : yd);
}
}
else {
dir = yd;
}
if (mode == AAM_FLEE)
dir = OppoDir(dir);
if (PlayerHasAffect(GetI(), 12))
dir = OppoDir(dir);
DirOffsetPos(x, y, dir,"AIControl::Move");
if (!IsOuttaField(POINT2(x, y)) &&
!IsBarrier(bk(x,y,"AIControl::Move")) &&
!HasPlayerTouch(my_index,dir)
)
{
if (mode != AAM_STILL)
{
bool sendMsg = (g_bIdt != IDT_OFFLINE);
GetPlayerX(my_index) = x;
GetPlayerY(my_index) = y;
if (sendMsg)
SendTCP(UM_SETPOS, x, y, GetI());
}
GetPlayerFace(my_index) = dir;
LastMove(my_index);
}else
LastMove(my_index);
}
else {
}
EXCEPTION_R_TITLED("BOP AIControl::Move EXCEPTION")
}
DIR GetComingDir(const Entity& et) const
{
if (IsOuttaField(POINT2(et.x, et.y)))
return 0;
int x = GetX();
int y = GetY();
int ex = GetPlayerX(target_index);
int ey = GetPlayerY(target_index);
DIR eface = et.face;
if (eface == RIGHT && y == ey && x > ex)
return LEFT;
else if (eface == LEFT && y == ey && x < ex)
return RIGHT;
else if (eface == DOWN && x == ex && y > ey)
return UP;
else if (eface == UP && x == ex && y < ey)
return DOWN;
else if (eface == LEFTDOWN && (x - ex < 0) && (x - ex) / float(y - ey) == -1.0f)
return RIGHTUP;
else if (eface == LEFTUP && (x - ex < 0) && (x - ex) / float(y - ey) == 1.0f)
return RIGHTDOWN;
else if (eface == RIGHTDOWN && (x - ex > 0) && (x - ex) / float(y - ey) == 1.0f)
return LEFTUP;
else if (eface == RIGHTUP && (x - ex > 0) && (x - ex) / float(y - ey) == -1.0f)
return LEFTDOWN;
else
return 0;
}
void TryToDodge()
{
if (entities.empty())
return;
int x = GetX();
int y = GetY();
int dp;
int r;
dp = Varience(AA_DODGE_BASE_RATE,4)
+ xp * AA_DODGE_UP_RATIO
+ adjust * 3
+ extraDodgeRate;
if (PlayerHasAffect(my_index, 6))
dp -= 4;
if (PlayerHasAffect(my_index, 7))
dp -= 3;
if (PlayerHasAffect(my_index, 9))
dp -= 6;
if (PlayerHasAffect(my_index, 11))
dp -= 10;
else if (PlayerHasAffect(my_index, 12))
dp -= 9;
if (PlayerHasAffect(my_index, 1))
dp -= 2;
if (PlayerHasAffect(my_index, 14))
dp -= 8;
ClampA(dp, AA_DODGE_BASE_RATE-5, AA_DODGE_RATE_MAX);
if (PlayerHasAffect(my_index, 4))
dp += 3 + adjust * 1;
for (int i = 0; i < entities.size(); i++)
{
double dist = Distance(x, y, entities.at(i).x, entities.at(i).y);
DIR aimdir=0;
if (aimdir = GetComingDir(entities.at(i)))
{
int dextra_rate = 0;
if (dist < 2.1f)
dextra_rate = 4;
else if (dist < 4.4f)
dextra_rate = 0;
else if (dist < 6.3f)
dextra_rate = -4;
else if (dist < 10.5f)
dextra_rate = -8;
else {
dextra_rate = -16;
}
r = RandomRange(0, 100, true, false);
if (r < dp + dextra_rate)
{
Dodge(aimdir);
}
break;
}
}
}
bool FaceBarrier(int _x, int _y, DIR face)
{
int nx = _x, ny = _y;
DirOffsetPos(nx, ny, face, "FaceBarrier");
if (IsBarrier(bk(nx, ny, string(__func__))) || IsOuttaField(POINT2(nx, ny)))
return true;
return false;
}
bool Dodge(DIR aimdir)
{
if (aimdir == 0)
return false;
if (PlayerHasAffect(my_index, 2) || PlayerHasAffect(my_index, 10))
return false;
const vector<DIR> _choices{1,3,5,7};
vector<DIR> choices{};
DIR oppdir = 0;
for (int i = 0; i < _choices.size(); i++)
{
if (_choices.at(i) != aimdir && _choices.at(i) != OppoDir(aimdir))
{
choices.push_back(_choices.at(i));
}
else if (_choices.at(i) == OppoDir(aimdir))
{
oppdir = _choices.at(i);
}
}
if (oppdir != 0)
{
choices.push_back(oppdir);
}
bool sendMsg = (g_bIdt != IDT_OFFLINE);
for (int k = 0; k < choices.size(); k++)
{
if (!HasPlayerTouch(GetI(),choices.at(k))
&& !FaceBarrier(GetX(),GetY(),choices.at(k))
)
{
int x = GetX(), y = GetY();
DirOffsetPos(x, y, choices.at(k), "Dodge");
SetPlayerPos(GetI(), x, y);
if (sendMsg)
SendTCP(UM_SETPOS, x, y, GetI());
return true;
}
}
return false;
}
#define AA_CANREACH_RANGE_MAX 30
bool CanReach(int x, int y, DIR face, int ex, int ey) const
{
if (face == 0)
return false;
int _x = x, _y = y;
int i = 0;
if (face == RIGHT || face == LEFT)
{
if (y != ey)
return false;
while (!IsOuttaField(POINT2(_x, _y)) && !IsBarrier(bk(_x, _y, "AIControl::CanReach")) && i < AA_CANREACH_RANGE_MAX)
{
if (_x == ex && _y == ey)
return true;
DirOffsetPos(_x, _y, face, "AIControl::CanReach");
i++;
}
return false;
}else if (face == DOWN || face == UP)
{
if (x != ex)
return false;
while (!IsOuttaField(POINT2(_x, _y)) && !IsBarrier(bk(_x, _y, "AIControl::CanReach")) && i < AA_CANREACH_RANGE_MAX)
{
if (_x == ex && _y == ey)
return true;
DirOffsetPos(_x, _y, face, "AIControl::CanReach");
i++;
}
return false;
}
else {
if (x == ex || y == ey)
return false;
while (!IsOuttaField(POINT2(_x, _y)) && !IsBarrier(bk(_x, _y, "AIControl::CanReach")) && i < AA_CANREACH_RANGE_MAX)
{
if (_x == ex && _y == ey)
return true;
DirOffsetPos(_x, _y, face, "AIControl::CanReach");
i++;
}
return false;
}
}
double GetTargetDistance() const
{
return Distance(GetPlayerX(my_index),GetPlayerY(my_index),GetPlayerX(target_index),GetPlayerY(target_index));
}
void Attack()
{
EXCEPTION_L
int r = RandomRange(0, 100, true, false);
if (r > 100 - AA_CHANGE_WEAPON_RATE && (clock() - lastWeaponChanged) > AA_CHANGE_WEAPON_MIN_T)
{
do {
GetPlayerCurWeaponIndex(my_index) = curw_prob.at(RandomRange(0, curw_prob.size() - 1, true, true));
} while (GetPlayerCurWeaponIndex(my_index) == 0);
lastWeaponChanged = clock();
}
double distance = GetTargetDistance();
UINT type = GetItemType(GetPlayerCurWeapon(my_index));
int x = GetX();
int y = GetY();
int ex = GetPlayerX(target_index);
int ey = GetPlayerY(target_index);
DIR face = GetPlayerFace(my_index);
UINT ptype;
ITEM_ID id = GetPlayerCurWeapon(my_index);
if(type == ITT_PROP)
ptype = GetItemPropType(GetPlayerCurWeapon(my_index));
if (type == ITT_BOW || type == ITT_GUN
|| (type == ITT_PROP && ptype == ITPT_THROWABLE))
{
bool reach = CanReach(x, y, face, ex, ey);
r = RandomRange(0, 100, true, false);
int blind_prob = 10;
if (mode == AAM_FLEE)
blind_prob = 2;
else if (type == ITT_PROP && ptype == ITPT_THROWABLE)
blind_prob = 1;
else
blind_prob = 11;
if(reach && r > (mode == AAM_CHASE ? 49 : 62) || r < blind_prob)
PlayerTryToAttack(my_index, (g_bIdt != IDT_OFFLINE));
}
else if (type == ITT_CLOSE_WEAPON)
{
bool reach = distance < 3.3;
r = RandomRange(0, 100, true, false);
if(reach && r > (mode == AAM_CHASE ? 43 : 45) || r > (mode == AAM_FLEE ? 86 : 97))
PlayerTryToAttack(my_index, (g_bIdt != IDT_OFFLINE));
}
else if (type == ITT_PROP)
{
if (ptype == ITPT_PUT)
{
if (id == 33)
{
int prob = 5;
if (distance < 6.0)
{
if (mode == AAM_FLEE)
{
prob = 40;
}
else if (mode == AAM_CHASE)
{
prob = 12;
}
else if (mode == AAM_STILL)
{
bool come = false;
int delta_x, delta_y;
DIR eface = GetPlayerFace(target_index);
delta_x = ex - x;
delta_y = ey - y;
if (delta_x > 0 && eface == LEFT
|| delta_x < 0 && eface == RIGHT
|| delta_y > 0 && eface == UP
|| delta_y < 0 && eface == DOWN)
come = true;
prob = (come?76:33);
}
}
else {
prob = 11;
}
r = RandomRange(0, 100, true, false);
if (r < prob)
{
PlayerTryToAttack(my_index, (g_bIdt != IDT_OFFLINE));
}
}
else {
}
}
else if (ptype == ITPT_POTION)
{
r = RandomRange(0, 100, true, false);
float t = AA_DRINK_POTION_RATE;
if (id >= 71 && id <= 73)
{
if (GetPlayerHp(my_index) < GetPlayerMaxHp(my_index) * 0.10)
t *= 5.0;
else if (GetPlayerHp(my_index) < GetPlayerMaxHp(my_index) * 0.25)
t *= 3.0;
else if (GetPlayerHp(my_index) < GetPlayerMaxHp(my_index) * 0.45)
t *= 1.6;
}
if (r < t)
{
PlayerTryToAttack(my_index, (g_bIdt != IDT_OFFLINE));
}
}
else {
}
}
else {
return;
}
EXCEPTION_R_TITLED("BOP AIControl::Attack EXCEPTION")
}
};
AIControl& GetPlayerAI(int index);
大致就包括自动移动、攻击、躲闪、喝药、埋地雷等,特别是那个躲闪很有意思,经验值越高躲闪越容易成功。 以后打算搞个游击模式算法。。等更新吧
下载: https://download.csdn.net/download/cjz2005/86287023
喜欢的朋友别忘了点赞关注!!
|