从自走棋代码分析游戏机制--棋池、回蓝、目标判断、掉落概率与新英雄
前几天接触了一下自走棋,发现这游戏有毒= =。 虽然作为新手菜鸡被虐的不要不要的,但是依然乐此不疲....
可惜的是虽然大概的机制玩过Dota 2的同学都懂,但是某些游戏机制是巨鸟多多独创的,而且并未说明过,除了知道有8位玩家共享棋池,攻击/被攻击都能回蓝,每轮会抽牌,每5轮野怪,大家觉得这似乎是一个纯随机游戏,技能释放完全看脸,回蓝也完全看脸.....
作为非酋的我实在不能忍.....这真的是一个概率游戏?正好逛github的时候发现有人上传了代码,于是去瞅了瞅。
自走棋部分代码(请自己注意日期)
虽然我Lua语法不太熟,但是作者写的注释很全而且意外的好懂.......以至于我这种弱鸡也能从代码发现一点东西,拿来和大家分享一下~
棋池大小与棋子总数
首先,需要明确的是自走棋游戏规则中,每轮抽牌是先根据当前人口的概率分布,先随机选择棋子的稀有度,再从这个稀有度的所有棋子中随机抽出一个棋子。
举个例子,比如在一轮抽牌中我抽到了1个2块钱的月骑,那么实际的过程是:
- 我抽到了一个2块钱的棋子
- 我从所有2块钱棋子组成的棋池里抽到了月骑
彩蛋环节
第一步中决定概率的是你的人口(科技等级),不过在这之前,有两个彩蛋,作者称之以为SSR卡........就是ssr_ck和ssr_nec,似乎是特殊版的混沌骑士和特殊版的死灵法师。
function RandomDrawChessNew(team_id)local h = TeamId2Hero(team_id)local this_chess = nillocal ran = RandomInt(1,100)local chess_level = 1local curr_per = 0local hero_level = h:GetLevel()local table_11chess = {}for _,chess in pairs(GameRules:GetGameModeEntity().mychess[team_id]) doif string.find(chess.chess,'11') thentable.insert(table_11chess,string.sub(chess.chess,1,-3))endendlocal ran1 = RandomInt(1,10000) %随机一个1~10000的数1local ran2 = RandomInt(1,10000) %随机一个1~100000的数2if h:GetLevel() >= 7 and ran1 <= 1 and ran2 <= 1 then %如果人口在7以上,且两个随机数都是1,随机抽SSR卡池中的卡this_chess = GameRules:GetGameModeEntity().chess_list_ssr[RandomInt(1,table.maxn(GameRules:GetGameModeEntity().chess_list_ssr))]elseif GameRules:GetGameModeEntity().chess_gailv[hero_level] ~= nil thenfor per,lv in pairs(GameRules:GetGameModeEntity().chess_gailv[hero_level]) doif ran>per and curr_per<=per thencurr_per = perchess_level = lvendendend-- this_chess = GameRules:GetGameModeEntity().chess_list_by_mana[chess_level][RandomInt(1,table.maxn(GameRules:GetGameModeEntity().chess_list_by_mana[chess_level]))]this_chess = DrawAChessFromChessPool(chess_level, table_11chess)endreturn this_chess
end
我在上述代码中加粗并加了注释来说明这部分,在这里解释一下。
所有的抽卡开始之前,有两个判断逻辑:如果你的人口大于7,且两个1~10000的随机数都是1,那么从SSR棋池中抽卡,这里抽出来的卡目前只有两种,就是上述说的ssr_ck和ssr_nec,两个随机数都是1的概率大概是 。
当前的SSR棋池是这个:
GameRules:GetGameModeEntity().chess_list_ssr = {'chess_nec_ssr','chess_ck_ssr'}
所以这个概率下能抽出来SSR卡的是真·欧洲人,所以想验证自己的rp的各位,请7人口以后疯狂抽卡,8人口提升不了SSR的概率。
PS:这一步会在每轮抽卡的时候进行5次,所以....概率还是有的。
第一步判断棋子等级
在作为RP过滤器的彩蛋环节后,非洲人们就进入了正常抽卡环节。如果我没有理解错的话,这个根据当前人口判断抽卡等级的概率来自这里:
for per,lv in pairs(GameRules:GetGameModeEntity().chess_gailv[hero_level]) doif ran>per and curr_per<=per thencurr_per = perchess_level = lvendend
per,lv两个变量储存在下边这个map里(我也不知道lua里这个数据类型应该叫啥),且随着每个等级不同。
GameRules:GetGameModeEntity().chess_gailv = {[1] = { [101] = 2 },[2] = { [70] = 2 },[3] = { [60] = 2, [95] = 3 },[4] = { [50] = 2, [85] = 3 },[5] = { [40] = 2, [75] = 3, [98] = 4 },[6] = { [33] = 2, [63] = 3, [93] = 4 },[7] = { [30] = 2, [60] = 3, [90] = 4 },[8] = { [24] = 2, [54] = 3, [84] = 4, [99] = 5 },[9] = { [22] = 2, [52] = 3, [77] = 4, [97] = 5 },[10] = { [19] = 2, [44] = 3, [69] = 4, [94] = 5 },}
具体的判断逻辑是这样的:先随机出一个1~100的数,然后和上述map中的数字比大小,比如1级的时候,随机数是无论如何不会大于101的,所以一定是一个1费棋子;2级的时候,有30%可能大于70,获得2费棋子;3级的时候,有40%可能大于60,获得2费棋子,然后重点来了,有5%的可能不仅大于60而且大于95,获得3费棋子,因此3级的概率是 5%的 3费棋子, 35%的2费棋子, 60%的1费棋子。
以此类推得到下图。
棋池有多大
具体抽卡和放回的过程我就不详细描述了,不然太长了。只谈谈大家关心的部分,总体棋池有多大。
文件里负责棋池大小的代码有三部分。
默认棋池参数:
--默认卡池参数GameRules:GetGameModeEntity().CHESS_POOL_SIZE = 5GameRules:GetGameModeEntity().CHESS_INIT_COUNT = {[1] = 9,[2] = 6,[3] = 5,[4] = 3,[5] = 2,}
从服务器端获取的棋池参数:
if t.chess_pool ~= nil thenif t.chess_pool.pool_size ~= nil thenGameRules:GetGameModeEntity().CHESS_POOL_SIZE = t.chess_pool.pool_sizeendif t.chess_pool.chess_init_1 ~= nil thenGameRules:GetGameModeEntity().CHESS_INIT_COUNT[1] = t.chess_pool.chess_init_1endif t.chess_pool.chess_init_2 ~= nil thenGameRules:GetGameModeEntity().CHESS_INIT_COUNT[2] = t.chess_pool.chess_init_2endif t.chess_pool.chess_init_3 ~= nil thenGameRules:GetGameModeEntity().CHESS_INIT_COUNT[3] = t.chess_pool.chess_init_3endif t.chess_pool.chess_init_4 ~= nil thenGameRules:GetGameModeEntity().CHESS_INIT_COUNT[4] = t.chess_pool.chess_init_4endif t.chess_pool.chess_init_5 ~= nil thenGameRules:GetGameModeEntity().CHESS_INIT_COUNT[5] = t.chess_pool.chess_init_5end
end
初始化棋池部分:
function InitChessPool()local chess_pool_times = GameRules:GetGameModeEntity().CHESS_POOL_SIZE or 6for cost,v in pairs(GameRules:GetGameModeEntity().chess_list_by_mana) dofor _,chess in pairs(v) dolocal chess_count = GameRules:GetGameModeEntity().CHESS_INIT_COUNT[cost]*chess_pool_times-- if chess == 'chess_eh' or chess == 'chess_fur' or chess == 'chess_tp' or chess == 'chess_ld' then-- chess_count = math.floor(chess_count*GameRules:GetGameModeEntity().CHESS_INIT_DRUID_PER)-- endfor i=1,chess_count doAddAChessToChessPool(chess)endendendprt('INIT CHESS POOL OK!')
end
请大家注意 CHESS_POOL_SIZE 这个字段 和 CHESS_INIT_COUNT 这个字段,这两个字段分别对应棋池大小倍数和棋池大小基数。
如果采用的是默认棋池的话,根据CHESS_INIT_COUNT,从一费到五费,每局游戏中每种棋子的基数分别为9,6,5,3,2;默认的棋池大小倍数是5,也就是说,如果采用默认棋池,每局能获得的每种一费到五费棋子分别是:45,30,25,15,10。
但是在初始化棋池的时候,可能采取服务器参数,这时候棋池大小的倍数就不定了,
local chess_pool_times = GameRules:GetGameModeEntity().CHESS_POOL_SIZE or 6
这句话的逻辑判断是当CHESS_POOL_SIZE 这个字段 为空时,也就是说,当服务器不传参数时,棋池大小倍数是6。
整理一下,我们从这里得到几个结论:
如果服务器发送棋池参数,那么棋池大小倍数和棋池大小基数都不确定。
如果服务器不发送棋池参数,或者发送参数为空,那么:
- 棋池大小基数是确定的,从一费到五费,每局游戏中每种棋子的基数分别为9,6,5,3,2。
- 棋池大小倍数是5(不发送),或6(发送参数为空)。
从上述结论我们可以得到一些推导:
- 各个职业的棋子数是均衡的,不存在某一盘中单个职业过多,其它职业过少的现象,因为这里边似乎没有涉及职业的参数,玩别人没玩的职业从概率上更容易抽到牌。
- 卡人关键牌是非常非常有效的做法,卡人一时爽,一直卡一直爽,因为单个棋子总数固定,但是不要卡同费用的其它棋子,这样是在帮助别人缩小棋池。
- 如果想抽某一张关键牌,那么可以先拿一些同费用的牌来压缩棋池,这种情况在橙色棋子部分特别有效,这是因为抽棋子是先抽费用,再抽某个种类的棋子。费用的概率始终是不变的,而这个费用的棋池会因为场上存在的其它棋子而缩小。
这部分我可能写的比较绕,来解释一下:当我想抽到一张关键棋子潮汐,那么如果经济允许的情况下,飞机啊lich啊什么的都应该拿在手里,这样棋池中的潮汐概率就会因为棋池的缩小而提升。
(:当然其他人抽到潮汐的概率也会同样增大。
回蓝
游戏中的棋子回蓝分两种,受到伤害回蓝和造成伤害回蓝。
受到伤害回蓝:
--受到伤害回蓝local mana_get = damage/5if mana_get > 50 thenmana_get = 50endmana_get = RandomInt(mana_get/2,mana_get)if caster:FindModifierByName("modifier_item_jixianfaqiu") ~= nil thenmana_get = math.floor(mana_get * 1.25)endif caster:FindModifierByName("modifier_item_yangdao") ~= nil thenmana_get = math.floor(mana_get * 1.5)endcaster:SetMana(caster:GetMana()+mana_get)
上述逻辑是,如果棋子在没有极限法球和羊刀的情况下,回蓝可能有两种:
受到伤害数目/5 和 受到伤害数目/10,两者概率相同。
且单次受到回蓝上限是50。
所以有的时候能看到两个一毛一样的棋子对A,别人的棋子就是比自己的快放出技能,没别的---脸黑而已。
从以上逻辑可以得到一些推论:
- 单次受到伤害250是个门槛,高于250的伤害不会充能
- 如果对方单次伤害没有超过250,不考虑抬手的情况下,纯受伤回蓝,1000血以上的棋子一定能放出来技能,我记得1星潮汐的血量是950,很微妙...一星萨尔是一定需要四兽人BUFF才能稳定抬手放技能的。
- 亡灵猎有四亡灵减甲多出来的20%多额外伤害,对于二星以上火枪,小黑是不回蓝的伤害,这是亡灵猎秒控制类前排的资本,所以各位看到4亡灵,请把潮汐放中后排
造成伤害回蓝:
--造成伤害回蓝if attacker ~= nil thenif attacker:FindAbilityByName('is_mage') or attacker:FindAbilityByName('is_warlock') or attacker:FindAbilityByName('is_shaman') thenmana_get = damage/2.5if mana_get > 20 thenmana_get = 20endelseif mana_get > 10 thenmana_get = 10endend if attacker:FindModifierByName("modifier_item_wangguan") ~= nil or attacker:FindModifierByName("item_hongzhang_1") ~= nil or attacker:FindModifierByName("item_hongzhang_2") ~= nil or attacker:FindModifierByName("item_hongzhang_3") ~= nil or attacker:FindModifierByName("item_hongzhang_4") ~= nil or attacker:FindModifierByName("item_hongzhang_5") ~= nil thenmana_get = math.floor(mana_get * 1.5)endif attacker:FindModifierByName("modifier_item_xuwubaoshi") ~= nil or attacker:FindModifierByName("modifier_item_yangdao") ~= nil or caster:FindModifierByName("modifier_item_shenmifazhang") ~= nil thenmana_get = math.floor(mana_get * 2)endif attacker:FindModifierByName("modifier_item_jianrenqiu") ~= nil thenmana_get = math.floor(mana_get * 2)endif attacker:FindModifierByName("modifier_item_shuaxinqiu") ~= nil thenmana_get = math.floor(mana_get * 3)endattacker:SetMana(attacker:GetMana()+mana_get)endif GameRules:GetGameModeEntity().show_damage == true thenif attacker:GetTeam() == 4 thenAMHC:CreateNumberEffect(caster,damage,2,AMHC.MSG_DAMAGE,"red",9)elseAMHC:CreateNumberEffect(caster,damage,2,AMHC.MSG_DAMAGE,"green",9)endend
对于法师,萨满,术士等单个目标的单次攻击回蓝上限是20,其它职业是10,在没有装备情况下,单次攻击单个目标回蓝数目是造成伤害数值/2.5...这个数字和上限是受到装备修正的,而且修正系数是乘积叠加,比如有1个虚无宝石1个王冠,那么上限就是20*2*1.5。
法术类职业回蓝是厉害的。
技能目标选择
技能目标不是随缘的,有几种类型:
--释放技能:11=新沙王,0=被动技能,1=单位目标,2=无目标,3=点目标,4=自己目标,5=近身单位目标,6=先知在地图边缘招树人,7=随机友军目标(嗜血术),8=随机周围空地目标(炸弹人),9=血量百分比最低的队友,10=等级最高的敌人(末日),11=沙王戳最远的能打到敌人的格子,12=小小投掷身边的敌人到最远的格子
值得谈谈的是末日/lina的大招(还有一个叫bump的技能是啥,据说最新代码里lina的 AI是1,随机单位目标了),选择最高等级目标,这个函数名也挺有意思:
function FindHighLevelUnluckyDog(u)local unluckydog = nillocal max_level = 0local team = u.at_team_id or u.team_idlocal my_pos = XY2Vector(u.x,u.y,team)if RandomInt(1,100)<30 then--30%概率随机找敌人return FindUnluckyDogRandom(u)endfor _,unit in pairs (GameRules:GetGameModeEntity().to_be_destory_list[u.at_team_id or u.team_id]) dolocal lv = unit:GetLevel()if unit:GetMaxMana() <= 0 thenlv = 1endlocal a = GameRules:GetGameModeEntity().chess_ability_list[unit:GetUnitName()]local beh = GameRules:GetGameModeEntity().ability_behavior_list[a]if lv > max_level and unit.team_id ~= u.team_id and unit:FindModifierByName("modifier_doom_bringer_doom") == nil and beh ~= 0 thenunluckydog = unitmax_level = lvendendreturn unluckydog
end
逻辑是先生成1-100的随机数,如果小于30就随机选择一个目标(狗),如果是这个随机数大于等于30,就选择等级最高的敌人释放。
翻译一下就是30%概率随机大,70%概率大最高等级。
然后是沙王,水人:
function FindUnluckyDogFarthest(u)local unluckydog = nillocal length2d = 0for _,unit in pairs (GameRules:GetGameModeEntity().to_be_destory_list[u.at_team_id or u.team_id]) doif (u:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D() > length2d and unit.team_id ~= u.team_id thenunluckydog = unitlength2d = (u:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D() endendreturn unluckydog
end
这个很简单.........选择离沙王/水人最远的敌人穿过去....
掉落
曾经PVP也是能掉落的,后来不知道为啥删掉了...看这里:
if string.find(u:GetUnitName(),'pve') ~= nil then --pve敌人掉宝DropItem(u)-- else-- if u:GetTeam() == 4 and RandomInt(1,100) < 10 then --pvp的敌人也有概率掉宝-- DropItem(u)-- endend
掉落是跟敌人等级有关的,掉落的参数如下:
GameRules:GetGameModeEntity().drop_item_gailv = {[1] = { [80] = 1},[2] = { [60] = 1},[3] = { [50] = 1},[4] = { [40] = 1, [80] = 2},[5] = { [40] = 1, [60] = 2},[6] = { [30] = 1, [60] = 2, [90] = 3},[7] = { [20] = 1, [50] = 2, [80] = 3},[8] = { [0] = 1, [20] = 2, [60] = 3, [90] = 4},[9] = { [0] = 1, [10] = 2, [50] = 3, [80] = 4},}
物品等级是这样的:
[1] = {[1] = 'item_suozijia',[2] = 'item_yuandun',[3] = 'item_zhiliaozhihuan',[4] = 'item_gongjizhizhua',[5] = 'item_kuweishi',[6] = 'item_duangun',[7] = 'item_xixuemianju',[8] = 'item_huifuzhihuan',[9] = 'item_kangmodoupeng',[10] = 'item_xuwubaoshi',[11] = 'item_fashichangpao',[12] = 'item_wangguan',},[2] = {[1] = 'item_banjia', [2] = 'item_huoliqiu',[3] = 'item_kuojian',[4] = 'item_miyinchui',[5] = 'item_biaoqiang',[6] = 'item_molifazhang',[7] = 'item_tiaodao',},[3] = {[1] = 'item_emodaofeng',[2] = 'item_zhenfenbaoshi',[3] = 'item_jixianfaqiu',},[4] = {[1] = 'item_shengzheyiwu',[2] = 'item_dafu',[3] = 'item_shenmifazhang',},
也就是说,一级物品有:
锁子甲、圆盾、治疗指环、攻击之爪、枯萎之石、短棍、吸血面具、回复指环、抗魔头巾、虚无宝石、法师长袍、王冠。
二级物品有:
板甲、活力球、阔剑、秘银锤、标枪、魔力法杖、跳刀
三级物品有:
恶魔刀锋、振奋宝石、极限法球
四级物品有:
圣者遗物、大斧头、神秘法杖
掉落概率根据怪物等级分别是:
1级怪:80%什么都不掉,20%1级物品
2级怪:60%无,40%1级物品
3级怪:50%无,50%1级物品
4级怪:40%无,40%1级物品,20%2级物品
5级怪:40%无。20%1级物品,40%2级物品
6级怪:30%无,30%1级物品,30%2级物品。10%3级物品
7级怪:20%无,30%2级物品,30%2级物品,20%3级物品
8级怪:20%1级物品。40% 2级物品,30%3级物品,10%4级物品
9级怪:10%1级物品,40% 2级物品。30%3级物品,20%4级物品
这个掉落就很随缘了.....
至于野怪是几费的.....我没找到.....可能掉血能看出来?希望评论区有提示= =。
可能的新英雄:
在SSR后发现了三个有意思的英雄:
chess_tiny = 1,
chess_tb = 3,
chess_morph = 2,
chess_nec_ssr = 10,
chess_ck_ssr = 15,
chess_kael = 3,
chess_zeus = 5,
chess_sven = 5,
三费卡尔,5费宙斯和5费斯文。
大概这样了。
谢谢。
完。
从自走棋代码分析游戏机制--棋池、回蓝、目标判断、掉落概率与新英雄相关推荐
- linux的watchdog代码分析,Watchdog机制以及问题分析
目录 1. 概览 Watchdog的中文的"看门狗",有保护的意思.最早引入Watchdog是在单片机系统中,由于单片机的工作环境容易受到外界磁场的干扰,导致程序"跑飞& ...
- 2021SC@SDUSC HBase(六)项目代码分析——Region机制(三)之Region定位
2021SC@SDUSC 目录 一.初步认识region定位 Meta表 Region定位 二.Region定位 总结 一.初步认识region定位 在 HBase 中,表的所有行都是按照 RowKe ...
- 2018-2019-2 网络对抗技术 20165324 Exp4:恶意代码分析
2018-2019-2 网络对抗技术 20165324 网络对抗技术 Exp4:恶意代码分析 课下实验: 实践目标 是监控你自己系统的运行状态,看有没有可疑的程序在运行. 是分析一个恶意软件,就分析E ...
- Exp4 恶意代码分析 20164321 王君陶
Exp4 恶意代码分析 20164321 王君陶 1.实践目标 1.1是监控你自己系统的运行状态,看有没有可疑的程序在运行. 1.2是分析一个恶意软件,就分析Exp2或Exp3中生成后门软件:分析工具 ...
- 【游戏制作】 从零开始的Qt5贪吃蛇代码分析
悲伤的现实 离期末第一场考试之剩下32天了,而oop的期中project还有24天截止.此刻,我们刚刚写完了人生中第一个c++程序,分数类的运算符重载.继承和多态还没学.尽管如此,也是时候作为全裸勇者 ...
- 微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js)
微信小游戏 demo 飞机大战 代码分析(四)(enemy.js, bullet.js, index.js) 微信小游戏 demo 飞机大战 代码分析(一)(main.js) 微信小游戏 demo 飞 ...
- 团队项目代码分析(Android游戏:别踩白块儿)
代码组成部分: 关键代码主要分为三大部分,如下图所示(用思维导图的形式展示): 代码调用关系 通过MainActivity调用其他类❤,具体见核心代码分析! 核心代码分析 public class P ...
- 【SemiDrive源码分析】【X9芯片启动流程】21 - MailBox 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇
[SemiDrive源码分析][X9芯片启动流程]21 - MailBox 核间通信机制介绍(代码分析篇)之 Mailbox for Linux 篇 一.Mailbox for Linux 驱动框架分 ...
- 【SemiDrive源码分析】【X9芯片启动流程】20 - MailBox 核间通信机制介绍(代码分析篇)之 MailBox for RTOS 篇
[SemiDrive源码分析][X9芯片启动流程]20 - MailBox 核间通信机制介绍(代码分析篇)之 MailBox for RTOS 篇 一.Mailbox for RTOS 源码分析 1. ...
- 贪吃蛇小游戏java实现代码分析
贪吃蛇小游戏java实现代码分析 贪吃蛇的小游戏,网上的代码比较多,今天周五,在教研室没啥事做,在电脑中发现了一个贪吃蛇的小游戏,于是就看了下实现的源码,发现别人写的代码确实挺好的,自己也是边加注释边 ...
最新文章
- nefu 118 n!后面有多少个0 算数基本定理,素数分解
- Hyperledger Fabric 1.0 实例简析 第一课 network_setup.sh分析
- 阿里云 mysql 超时_mysql数据库超时
- 苹果电脑怎么删除软件_误格式化,删除文件怎么恢复?3款最好用的数据恢复软件推荐...
- iOS应用横竖屏切换
- Jeecg入门篇,高手掠过
- org.apache.struts.chain.commands.InvalidPathException: No action config found for the specified url.
- 计算机三级嵌入式系统考试之矩阵键盘
- Linux 启动 Apache 时报错:(98)Address already in use: make_sock: could not bind to add
- 机械革命深海泰坦X1(1050T)触控板用不了三指
- lc滤波电路电感电容值选择_模拟电路中,电感的这些知识点你都清楚吗?
- python数据抓取与实战_Python数据抓取技术与实战 pdf
- Mixly米思齐——超声波测距控制LED灯
- Win10安装应用或打开应用时提示“用户账户控制 为了对电脑进行保护,已经阻止此应用”
- java BigDecimal比较大小
- ubuntu下安装lua和luarocks
- JSP中include的两种方法
- 安装libjpeg库后提示libjpeg.so.8不存在(linux环境)
- 中国天气网天气预报API接口城市代码(XML格式,信息全)
- java扫雷程序,Java扫雷程序,初试Java-JSP教程,Java技巧及代码