前几天接触了一下自走棋,发现这游戏有毒= =。 虽然作为新手菜鸡被虐的不要不要的,但是依然乐此不疲....
可惜的是虽然大概的机制玩过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 = nil
local ran = RandomInt(1,100)
local chess_level = 1
local curr_per = 0
local hero_level = h:GetLevel()
local table_11chess = {}
for _,chess in pairs(GameRules:GetGameModeEntity().mychess[team_id]) do
if string.find(chess.chess,'11') then
table.insert(table_11chess,string.sub(chess.chess,1,-3))
end
end
local ran1 = RandomInt(1,10000) %随机一个1~10000的数1
local ran2 = RandomInt(1,10000) %随机一个1~100000的数2
if 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))]
else
if GameRules:GetGameModeEntity().chess_gailv[hero_level] ~= nil then
for per,lv in pairs(GameRules:GetGameModeEntity().chess_gailv[hero_level]) do
if ran>per and curr_per<=per then
curr_per = per
chess_level = lv
end
end
end
-- 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)
end
return 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]) do
if ran>per and curr_per<=per then
curr_per = per
chess_level = lv
end
end
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 = 5
GameRules:GetGameModeEntity().CHESS_INIT_COUNT = {
[1] = 9,
[2] = 6,
[3] = 5,
[4] = 3,
[5] = 2,
}
从服务器端获取的棋池参数:
if t.chess_pool ~= nil then
if t.chess_pool.pool_size ~= nil then
GameRules:GetGameModeEntity().CHESS_POOL_SIZE = t.chess_pool.pool_size
end
if t.chess_pool.chess_init_1 ~= nil then
GameRules:GetGameModeEntity().CHESS_INIT_COUNT[1] = t.chess_pool.chess_init_1
end
if t.chess_pool.chess_init_2 ~= nil then
GameRules:GetGameModeEntity().CHESS_INIT_COUNT[2] = t.chess_pool.chess_init_2
end
if t.chess_pool.chess_init_3 ~= nil then
GameRules:GetGameModeEntity().CHESS_INIT_COUNT[3] = t.chess_pool.chess_init_3
end
if t.chess_pool.chess_init_4 ~= nil then
GameRules:GetGameModeEntity().CHESS_INIT_COUNT[4] = t.chess_pool.chess_init_4
end
if t.chess_pool.chess_init_5 ~= nil then
GameRules:GetGameModeEntity().CHESS_INIT_COUNT[5] = t.chess_pool.chess_init_5
end
end
初始化棋池部分:
function InitChessPool()
local chess_pool_times = GameRules:GetGameModeEntity().CHESS_POOL_SIZE or 6
for cost,v in pairs(GameRules:GetGameModeEntity().chess_list_by_mana) do
for _,chess in pairs(v) do
local 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)
-- end
for i=1,chess_count do
AddAChessToChessPool(chess)
end
end
end
prt('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/5
if mana_get > 50 then
mana_get = 50
end
mana_get = RandomInt(mana_get/2,mana_get)
if caster:FindModifierByName("modifier_item_jixianfaqiu") ~= nil then
mana_get = math.floor(mana_get * 1.25)
end
if caster:FindModifierByName("modifier_item_yangdao") ~= nil then
mana_get = math.floor(mana_get * 1.5)
end
caster:SetMana(caster:GetMana()+mana_get)
上述逻辑是,如果棋子在没有极限法球和羊刀的情况下,回蓝可能有两种:
受到伤害数目/5 和 受到伤害数目/10,两者概率相同。
且单次受到回蓝上限是50。
所以有的时候能看到两个一毛一样的棋子对A,别人的棋子就是比自己的快放出技能,没别的---脸黑而已。
从以上逻辑可以得到一些推论:
- 单次受到伤害250是个门槛,高于250的伤害不会充能
- 如果对方单次伤害没有超过250,不考虑抬手的情况下,纯受伤回蓝,1000血以上的棋子一定能放出来技能,我记得1星潮汐的血量是950,很微妙...一星萨尔是一定需要四兽人BUFF才能稳定抬手放技能的。
- 亡灵猎有四亡灵减甲多出来的20%多额外伤害,对于二星以上火枪,小黑是不回蓝的伤害,这是亡灵猎秒控制类前排的资本,所以各位看到4亡灵,请把潮汐放中后排
造成伤害回蓝:
--造成伤害回蓝
if attacker ~= nil then
if attacker:FindAbilityByName('is_mage') or attacker:FindAbilityByName('is_warlock') or attacker:FindAbilityByName('is_shaman') then
mana_get = damage/2.5
if mana_get > 20 then
mana_get = 20
end
else
if mana_get > 10 then
mana_get = 10
end
end
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 then
mana_get = math.floor(mana_get * 1.5)
end
if attacker:FindModifierByName("modifier_item_xuwubaoshi") ~= nil or attacker:FindModifierByName("modifier_item_yangdao") ~= nil or caster:FindModifierByName("modifier_item_shenmifazhang") ~= nil then
mana_get = math.floor(mana_get * 2)
end
if attacker:FindModifierByName("modifier_item_jianrenqiu") ~= nil then
mana_get = math.floor(mana_get * 2)
end
if attacker:FindModifierByName("modifier_item_shuaxinqiu") ~= nil then
mana_get = math.floor(mana_get * 3)
end
attacker:SetMana(attacker:GetMana()+mana_get)
end
if GameRules:GetGameModeEntity().show_damage == true then
if attacker:GetTeam() == 4 then
AMHC:CreateNumberEffect(caster,damage,2,AMHC.MSG_DAMAGE,"red",9)
else
AMHC:CreateNumberEffect(caster,damage,2,AMHC.MSG_DAMAGE,"green",9)
end
end
对于法师,萨满,术士等单个目标的单次攻击回蓝上限是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 = nil
local max_level = 0
local team = u.at_team_id or u.team_id
local my_pos = XY2Vector(u.x,u.y,team)
if RandomInt(1,100)<30 then
--30%概率随机找敌人
return FindUnluckyDogRandom(u)
end
for _,unit in pairs (GameRules:GetGameModeEntity().to_be_destory_list[u.at_team_id or u.team_id]) do
local lv = unit:GetLevel()
if unit:GetMaxMana() <= 0 then
lv = 1
end
local 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 then
unluckydog = unit
max_level = lv
end
end
return unluckydog
end
逻辑是先生成1-100的随机数,如果小于30就随机选择一个目标(狗),如果是这个随机数大于等于30,就选择等级最高的敌人释放。
翻译一下就是30%概率随机大,70%概率大最高等级。
然后是沙王,水人:
function FindUnluckyDogFarthest(u)
local unluckydog = nil
local length2d = 0
for _,unit in pairs (GameRules:GetGameModeEntity().to_be_destory_list[u.at_team_id or u.team_id]) do
if (u:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D() > length2d and unit.team_id ~= u.team_id then
unluckydog = unit
length2d = (u:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D()
end
end
return 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)
-- end
end
掉落是跟敌人等级有关的,掉落的参数如下:
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费斯文。
大概这样了。
谢谢。
完。