You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1391 lines
41 KiB
1391 lines
41 KiB
|
5 years ago
|
local _, T = ...
|
||
|
|
local SpellInfo = T.KnownSpells
|
||
|
|
|
||
|
|
local band, bor, floor = bit.band, bit.bor, math.floor
|
||
|
|
local f32_ne, f32_perc, f32_pim, f32_fpim do
|
||
|
|
local frexp, lt = math.frexp, {
|
||
|
|
[-80]=-0xcccccd*2^-24,
|
||
|
|
[-60]=-0x99999a*2^-24,
|
||
|
|
[-40]=-0xcccccd*2^-25,
|
||
|
|
[-30]=-0x99999a*2^-25,
|
||
|
|
[-20]=-0xcccccd*2^-26,
|
||
|
|
[-10]=-0xcccccd*2^-27,
|
||
|
|
[1]=0xa3d70a*2^-30,
|
||
|
|
[3]=0xf5c28f*2^-29,
|
||
|
|
[5]=0xcccccd*2^-28,
|
||
|
|
[10]=0xcccccd*2^-27,
|
||
|
|
[15]=0x99999a*2^-26,
|
||
|
|
[20]=0xcccccd*2^-26,
|
||
|
|
[30]=0x99999a*2^-25,
|
||
|
|
[33]=0xa8f5c3*2^-25,
|
||
|
|
[40]=0xcccccd*2^-25,
|
||
|
|
[45]=0xe66666*2^-25,
|
||
|
|
[60]=0x99999a*2^-24,
|
||
|
|
[65]=0xa66666*2^-24,
|
||
|
|
[70]=0xb33333*2^-24,
|
||
|
|
[80]=0xcccccd*2^-24,
|
||
|
|
[90]=0xe66666*2^-24,
|
||
|
|
[120]=0x99999a*2^-23,
|
||
|
|
[140]=0xb33333*2^-23,
|
||
|
|
[160]=0xcccccd*2^-23,
|
||
|
|
[4520]=0xb4cccd*2^-18,
|
||
|
|
[9040]=0xb4cccd*2^-17,
|
||
|
|
}
|
||
|
|
function f32_perc(p)
|
||
|
|
return lt[p] or f32_ne(p/100)
|
||
|
|
end
|
||
|
|
function f32_ne(f)
|
||
|
|
local neg, s, e = f < 0, frexp(f)
|
||
|
|
s = neg and -s or s
|
||
|
|
local lo = s % 2^-24
|
||
|
|
local a = lo >= 2^-25 and (lo > 2^-25 or s % 2^-23 >= 2^-24) and 2^-24 or 0
|
||
|
|
local rv = (s - lo + a) * 2^e
|
||
|
|
return neg and -rv or rv
|
||
|
|
end
|
||
|
|
function f32_pim(p, i)
|
||
|
|
i = f32_ne(i * (lt[p] or f32_ne(p/100)))
|
||
|
|
return i - i%1
|
||
|
|
end
|
||
|
|
function f32_fpim(p, i)
|
||
|
|
return f32_ne(i * (lt[p] or f32_ne(p/100)))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local f32_sr do
|
||
|
|
local ah, al = {}, {}
|
||
|
|
local ev, minv, maxv = {}, {}, {}
|
||
|
|
function f32_sr(s)
|
||
|
|
local c, mc = s.c, #s
|
||
|
|
if mc == 1 then
|
||
|
|
local m, r = f32_perc(s[1]), 1
|
||
|
|
for i=1, c[s[1]] do
|
||
|
|
r = f32_ne(r+m)
|
||
|
|
end
|
||
|
|
return r, r
|
||
|
|
end
|
||
|
|
table.sort(s)
|
||
|
|
local sk, ec = "", 0
|
||
|
|
for i=1,mc do
|
||
|
|
local m = s[i]
|
||
|
|
ec = ec + c[m]
|
||
|
|
sk = sk .. m .. ":" .. ec .. ":"
|
||
|
|
end
|
||
|
|
local vl, vh = al[sk], ah[sk]
|
||
|
|
if vl then
|
||
|
|
return vl, vh
|
||
|
|
end
|
||
|
|
local sv, cv = 0, 1
|
||
|
|
for i=1,mc do
|
||
|
|
local m = s[i]
|
||
|
|
local c, smul = c[m], 2
|
||
|
|
while smul <= c do
|
||
|
|
smul = smul + smul
|
||
|
|
end
|
||
|
|
ev[i], sv, minv[i], maxv[i], cv = f32_perc(m), sv+cv*c, cv, cv*smul, cv*smul
|
||
|
|
end
|
||
|
|
local hi, lo = {[sv]=1}, {[sv]=1}
|
||
|
|
for i=1,ec do
|
||
|
|
local hi2, lo2 = {}, {}
|
||
|
|
for k, v in pairs(hi) do
|
||
|
|
for j=1,mc do
|
||
|
|
local f, mv = k % maxv[j], minv[j]
|
||
|
|
if f >= mv then
|
||
|
|
local nk = k - mv
|
||
|
|
local vj = ev[j]
|
||
|
|
local hv = f32_ne(v+vj)
|
||
|
|
local lv = f32_ne(lo[k]+vj)
|
||
|
|
local oh, ol = hi2[nk], lo2[nk]
|
||
|
|
if oh == nil or hv > oh then
|
||
|
|
hi2[nk] = hv
|
||
|
|
end
|
||
|
|
if ol == nil or lv < ol then
|
||
|
|
lo2[nk] = lv
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
hi, lo = hi2, lo2
|
||
|
|
end
|
||
|
|
vl, vh = lo[0], hi[0]
|
||
|
|
al[sk], ah[sk] = vl, vh
|
||
|
|
return vl, vh
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local function icast(f)
|
||
|
|
local m = f % 1
|
||
|
|
return f - m + (m > f and m > 0 and 1 or 0)
|
||
|
|
end
|
||
|
|
|
||
|
|
local VS, VSI = {}, {}
|
||
|
|
local VSIm = {__index=VSI}
|
||
|
|
|
||
|
|
local slotHex = {[-1]="w"}
|
||
|
|
for i=0,15 do
|
||
|
|
slotHex[i] = ("%x"):format(i)
|
||
|
|
end
|
||
|
|
|
||
|
|
local forkTargets = {["random-enemy"]="all-enemies", ["random-ally"]="all-allies", ["random-all"]="all"}
|
||
|
|
local forkTargetBits= {["all-enemies"]=1, ["all-allies"]=2, ["all"]=4}
|
||
|
|
do -- targets
|
||
|
|
local overrideAA = {[57]=0, [181]=0, [209]=0, [341]=0, [409]=1, [777]=0, [1213]=0, [69424]=0, [69426]=0, [69432]=0, [69434]=0, [69518]=0, [69522]=0, [69524]=0, [69530]=0, [69646]=0, [69648]=0, [69650]=0, [69652]=0, [70286]=0, [70288]=0, [70290]=0, [70292]=0, [70456]=0, [70478]=0, [70550]=0, [70556]=0, [70584]=0, [70586]=0, [70638]=0, [70640]=0, [70642]=0, [70644]=0, [70678]=0, [70682]=0, [70684]=0, [70702]=0, [70704]=0, [70706]=0, [70708]=0, [70714]=0, [70806]=0, [70808]=0, [70812]=0, [70832]=0, [70862]=0, [70868]=0, [70874]=0, [70908]=0, [71194]=0, [71606]=0, [71612]=0, [71640]=0, [71670]=0, [71672]=0, [71674]=0, [71676]=0, [71736]=0, [71800]=0, [71802]=0, [72086]=0, [72088]=0, [72090]=0, [72092]=0, [72310]=0, [72314]=0, [72336]=0, [72338]=0, [72942]=0, [72944]=0, [72946]=0, [72948]=0, [72954]=0, [73210]=0, [73398]=0, [73404]=0, [73558]=0, [73560]=0, [73564]=0}
|
||
|
|
local targetLists do
|
||
|
|
targetLists = {
|
||
|
|
[0]={
|
||
|
|
[0]="56a79b8c", "67b8ac59", "569a7b8c", "675a9b8c", "786bac59",
|
||
|
|
"20314", "23014", "34201", "43120",
|
||
|
|
"23014", "23401", "23401", "34201"
|
||
|
|
},
|
||
|
|
[1]={
|
||
|
|
[0]="c89ba576", "95acb687", "c8b79a56", "9c58ab67", "95a6bc78",
|
||
|
|
"41302", "41023", "20134", "20314",
|
||
|
|
"41032", "10423", "01234", "20134"
|
||
|
|
},
|
||
|
|
[3]={
|
||
|
|
[0]="23104", "03421", "03214", "20143", "31204",
|
||
|
|
"56a79b8c", "5a7b69c8", "6bac8759", "768bc95a",
|
||
|
|
"5a6978bc", "96b57a8c", "a78c69b5", "56a79b8c"
|
||
|
|
},
|
||
|
|
[4]={
|
||
|
|
[0]="0", "1", "2", "3", "4",
|
||
|
|
"5","6","7","8",
|
||
|
|
"9","a","b","c",
|
||
|
|
},
|
||
|
|
}
|
||
|
|
for _, m in pairs(targetLists) do
|
||
|
|
for o, t in pairs(m) do
|
||
|
|
local r = {}
|
||
|
|
for i=1,#t do
|
||
|
|
r[i] = tonumber(t:sub(i,i), 16)
|
||
|
|
end
|
||
|
|
m[o] = r
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local adjCleave = {
|
||
|
|
[0x50]=3, [0x83]=1,
|
||
|
|
[0x62]=3, [0x63]=2, [0xa2]=3, [0xa3]=2,
|
||
|
|
[0x73]=4, [0x74]=3, [0xb3]=4, [0xb4]=3,
|
||
|
|
[0x51]=4, [0x70]=1, [0x82]=0,
|
||
|
|
}
|
||
|
|
local adjCleaveN = {
|
||
|
|
[0]={5,6,32,7,9,10,11,32,8,12},
|
||
|
|
{6,7,32,8,10,11,12,32,5,9},
|
||
|
|
{5,6,32,9,10,32,7,11,32,8,12},
|
||
|
|
{6,7,32,5,9,10,11,32,8,12},
|
||
|
|
{7,8,32,6,10,11,12,32,5,9},
|
||
|
|
[7]={3,4,32,0,1,2},
|
||
|
|
}
|
||
|
|
local adjCleaveT = {
|
||
|
|
[6]={6,21,9,10},
|
||
|
|
}
|
||
|
|
local coneCleave = {
|
||
|
|
[0x20]=1, [0x30]=1, [0x31]=1, [0x41]=1,
|
||
|
|
[0x59]=1, [0x5a]=1,
|
||
|
|
[0x69]=1, [0x6a]=1, [0x6b]=1,
|
||
|
|
[0x7a]=1, [0x7b]=1, [0x7c]=1,
|
||
|
|
[0x8b]=1, [0x8c]=1,
|
||
|
|
}
|
||
|
|
local friendSurround = {
|
||
|
|
[0x01]=1, [0x02]=1, [0x03]=1,
|
||
|
|
[0x10]=1, [0x13]=1, [0x14]=1,
|
||
|
|
[0x20]=1, [0x23]=1,
|
||
|
|
[0x30]=1, [0x31]=1, [0x32]=1, [0x34]=1,
|
||
|
|
[0x41]=1, [0x43]=1,
|
||
|
|
}
|
||
|
|
local stt = {}
|
||
|
|
local function GetTargets(source, tt, board)
|
||
|
|
local ni, su, tl, lo, taunt = 1, board[source], targetLists[tt], source < 5 and source >= 0
|
||
|
|
taunt, tl = su and su.taunt, tl and tl[source]
|
||
|
|
if tl then
|
||
|
|
for i=1,#tl do
|
||
|
|
local t = tl[i]
|
||
|
|
local tu = board[t]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[ni], ni = source < 5 and t > 4 and taunt or t, ni + 1
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif tt == "all-enemies" then
|
||
|
|
for i=lo and 5 or 0, lo and 12 or 4 do
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif tt == "all-other-allies" then
|
||
|
|
for i=lo and 0 or 5, lo and 4 or 12 do
|
||
|
|
local tu = board[i]
|
||
|
|
if i ~= source and tu and tu.curHP > 0 then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif tt == "all-allies" then
|
||
|
|
for i=lo and 0 or 5, lo and 4 or 12 do
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif tt == "all" then
|
||
|
|
for i=0,12 do
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif tt == "friend-surround" then
|
||
|
|
local f = source*16
|
||
|
|
for i=0,12 do
|
||
|
|
if friendSurround[f+i] then
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if ni == 1 then
|
||
|
|
if source == 0 then
|
||
|
|
stt[ni], ni = source, ni + 1
|
||
|
|
else
|
||
|
|
return GetTargets(source, "all-allies", board)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif tt == "cone" then
|
||
|
|
local ot
|
||
|
|
tl = targetLists[0][source]
|
||
|
|
for i=1,#tl do
|
||
|
|
i = tl[i]
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[1], ot, ni = taunt or i, i, 2
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if taunt == 6 and source == 4 and ot ~= taunt then
|
||
|
|
local tu = board[0]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[1], ni, ot = 0, 2
|
||
|
|
else
|
||
|
|
local t2 = board[2]
|
||
|
|
if t2 and t2.curHP > 0 and not t2.shroud then
|
||
|
|
stt[1], ni, ot = 2, 2
|
||
|
|
for i=5,6 do
|
||
|
|
tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if ot then
|
||
|
|
local f = stt[1]*16
|
||
|
|
for i=lo and 5 or 0, lo and 12 or 4 do
|
||
|
|
if coneCleave[f+i] then
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif tt == "cleave" then
|
||
|
|
local coa, cot = adjCleaveN[source], adjCleaveT[taunt]
|
||
|
|
if cot then
|
||
|
|
for i=1,#cot do
|
||
|
|
i = cot[i]
|
||
|
|
if i <= 12 then
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
elseif i >= 16 then
|
||
|
|
local tu = board[i-16]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif coa then
|
||
|
|
for i=1,#coa do
|
||
|
|
i = coa[i]
|
||
|
|
if i <= 12 then
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
elseif i == 32 and ni > 1 then
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if taunt and (ni < 2 or stt[1] ~= taunt) then
|
||
|
|
stt[1], ni = taunt, 2
|
||
|
|
elseif taunt and ni > 3 then
|
||
|
|
ni = 3
|
||
|
|
end
|
||
|
|
elseif taunt then
|
||
|
|
stt[1], ni = taunt, 2
|
||
|
|
else
|
||
|
|
GetTargets(source, 0, board)
|
||
|
|
if #stt > 0 then
|
||
|
|
local s1 = stt[1]
|
||
|
|
local t = adjCleave[source*16+s1]
|
||
|
|
local tu = board[t]
|
||
|
|
local s2 = tu and tu.curHP > 0 and not tu.shroud and t or nil
|
||
|
|
stt[2], ni = s2, s2 and 3 or 2
|
||
|
|
if s2 and s2 < s1 then
|
||
|
|
stt[1], stt[2] = s2, s1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif tt == "col" then
|
||
|
|
GetTargets(source, 0, board)
|
||
|
|
ni = #stt + 1
|
||
|
|
local ex = lo and ni == 2 and (stt[1]+4) or nil
|
||
|
|
local exu = board[ex]
|
||
|
|
if exu and exu.curHP > 0 and not exu.shroud then
|
||
|
|
stt[2], ni = ex, ni + 1
|
||
|
|
end
|
||
|
|
elseif tt == "enemy-front" then
|
||
|
|
local br = lo and 8 or 2
|
||
|
|
for i=lo and (taunt and taunt > 8 and 9 or 5) or 4, lo and 12 or 0, lo and 1 or -1 do
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
if i == br and ni > 1 then
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
for i=1,ni > 2 and not lo and ni/2 or 0 do
|
||
|
|
stt[ni-i], stt[i] = stt[i], stt[ni-i]
|
||
|
|
end
|
||
|
|
elseif tt == "enemy-back" then
|
||
|
|
local br = lo and 9 or 1
|
||
|
|
for i=lo and (taunt and taunt < 9 and 8 or 12) or 0, lo and 5 or 4, lo and -1 or 1 do
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and tu.curHP > 0 and not tu.shroud then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
if i == br and ni > 1 then
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
for i=1,ni > 2 and lo and ni/2 or 0 do
|
||
|
|
stt[ni-i], stt[i] = stt[i], stt[ni-i]
|
||
|
|
end
|
||
|
|
elseif tt == "friend-back" or tt == "friend-back-hard" or tt == "friend-back-soft" then
|
||
|
|
local br, selfOK = lo and 1 or 9, tt == "friend-back"
|
||
|
|
for i=lo and 0 or 12, lo and 4 or 5, lo and 1 or -1 do
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and (i ~= source or selfOK) and tu.curHP > 0 then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
if i == br and ni > 1 then
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if ni == 1 and tt == "friend-back-soft" then
|
||
|
|
stt[ni], ni = source, ni + 1
|
||
|
|
end
|
||
|
|
elseif tt == "friend-front" or tt == "friend-front-hard" or tt == "friend-front-soft" then
|
||
|
|
local br, selfOK = lo and 2 or 8, tt == "friend-front"
|
||
|
|
for i=lo and 4 or 5, lo and 0 or 12, lo and -1 or 1 do
|
||
|
|
local tu = board[i]
|
||
|
|
if tu and (i ~= source or selfOK) and tu.curHP > 0 then
|
||
|
|
stt[ni], ni = i, ni + 1
|
||
|
|
end
|
||
|
|
if i == br and ni > 1 then
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if ni == 1 and tt == "friend-front-soft" then
|
||
|
|
stt[ni], ni = source, ni + 1
|
||
|
|
end
|
||
|
|
elseif tt == "self" then
|
||
|
|
stt[ni], ni = source, ni + 1
|
||
|
|
end
|
||
|
|
for i=#stt, ni, -1 do
|
||
|
|
stt[i] = nil
|
||
|
|
end
|
||
|
|
return stt
|
||
|
|
end
|
||
|
|
function VS:GetAutoAttack(role, slot, mid, firstSpell)
|
||
|
|
local a1 = slot and mid and overrideAA[4+2*slot+32*mid]
|
||
|
|
local a2 = (slot or 0) < 5 and firstSpell and overrideAA[1+4*firstSpell]
|
||
|
|
return (a1 or a2 or (role == 1 or role == 5) and 0 or 1) == 0 and 11 or 15
|
||
|
|
end
|
||
|
|
VS.GetTargets = GetTargets
|
||
|
|
VS.targetLists = targetLists
|
||
|
|
VS.forkTargetMap = forkTargets
|
||
|
|
end
|
||
|
|
|
||
|
|
local function enq(qs, k, e)
|
||
|
|
local q = qs[k]
|
||
|
|
if q == nil then
|
||
|
|
q = {}
|
||
|
|
qs[k] = q
|
||
|
|
end
|
||
|
|
q[#q+1] = e
|
||
|
|
end
|
||
|
|
local function enqc(q, k, e)
|
||
|
|
e[5] = e
|
||
|
|
return enq(q, k, e)
|
||
|
|
end
|
||
|
|
|
||
|
|
local mu = {}
|
||
|
|
function mu:stackf32(_sourceIndex, targetIndex, stackName, magh, _sid)
|
||
|
|
local b = self.board[targetIndex]
|
||
|
|
local s = b.stacks[stackName]
|
||
|
|
if s == nil then
|
||
|
|
b.stacks[stackName] = {magh, c={[magh]=1}}
|
||
|
|
else
|
||
|
|
local c = s.c
|
||
|
|
local oc = c[magh]
|
||
|
|
if oc then
|
||
|
|
c[magh] = oc + 1
|
||
|
|
else
|
||
|
|
s[#s+1], c[magh] = magh, 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
b[stackName], b[stackName .. "H"] = nil
|
||
|
|
end
|
||
|
|
function mu:unstackf32(_sourceIndex, targetIndex, stackName, magh)
|
||
|
|
local b = self.board[targetIndex]
|
||
|
|
local s = b.stacks[stackName]
|
||
|
|
local c = s.c
|
||
|
|
local oc = c[magh]
|
||
|
|
if oc > 1 then
|
||
|
|
c[magh] = oc - 1
|
||
|
|
elseif #s == 1 then
|
||
|
|
b[stackName], b[stackName .. "H"], b.stacks[stackName] = 1,1, nil
|
||
|
|
return
|
||
|
|
else
|
||
|
|
c[magh] = nil
|
||
|
|
for i=1, #s-1 do
|
||
|
|
if s[i] == magh then
|
||
|
|
s[i] = s[#s]
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
s[#s] = nil
|
||
|
|
end
|
||
|
|
b[stackName], b[stackName .. "H"] = nil
|
||
|
|
end
|
||
|
|
function mu:modDamageDealt(sourceIndex, targetIndex, mod, sid)
|
||
|
|
mu.stackf32(self, sourceIndex, targetIndex, "modDamageDealt", mod, sid)
|
||
|
|
end
|
||
|
|
function mu:modDamageTaken(sourceIndex, targetIndex, mod, sid)
|
||
|
|
mu.stackf32(self, sourceIndex, targetIndex, "modDamageTaken", mod, sid)
|
||
|
|
end
|
||
|
|
function mu:damage(sourceIndex, targetIndex, baseDamage, causeTag, causeSID, eDNE)
|
||
|
|
local board = self.board
|
||
|
|
local tu, su = board[targetIndex], board[sourceIndex]
|
||
|
|
local tHP, rHP = tu.curHP, tu.hpR
|
||
|
|
if tHP <= 0 then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
local su_mdd, tu_mdt, su_hmdd, tu_hmdt = su.modDamageDealt, tu.modDamageTaken
|
||
|
|
if su_mdd then
|
||
|
|
su_hmdd = su.modDamageDealtH
|
||
|
|
else
|
||
|
|
su_mdd, su_hmdd = f32_sr(su.stacks.modDamageDealt)
|
||
|
|
su.modDamageDealt, su.modDamageDealtH = su_mdd, su_hmdd
|
||
|
|
end
|
||
|
|
if tu_mdt then
|
||
|
|
tu_hmdt = tu.modDamageTakenH
|
||
|
|
else
|
||
|
|
tu_mdt, tu_hmdt = f32_sr(tu.stacks.modDamageTaken)
|
||
|
|
tu.modDamageTaken, tu.modDamageTakenH = tu_mdt, tu_hmdt
|
||
|
|
end
|
||
|
|
if (su_mdd < 0) ~= (tu_mdt < 0) then
|
||
|
|
tu_mdt, tu_hmdt = tu_hmdt, tu_mdt
|
||
|
|
end
|
||
|
|
local bp, pdd, pdt = floor(baseDamage), su.plusDamageDealt, tu.plusDamageTaken
|
||
|
|
bp = pdd == 0 and bp or f32_ne(bp + pdd)
|
||
|
|
local points = icast(su_mdd == 1 and bp or f32_ne(bp * su_mdd))
|
||
|
|
points = pdt == 0 and points or f32_ne(points + pdt)
|
||
|
|
points = icast(tu_mdt == 1 and points or f32_ne(points * tu_mdt))
|
||
|
|
local pointsR, points2 = 0, points
|
||
|
|
if su_mdd ~= su_hmdd or tu_mdt ~= tu_hmdt then
|
||
|
|
points2 = icast(su_hmdd == 1 and bp or f32_ne(bp * su_hmdd))
|
||
|
|
points2 = pdt == 0 and points2 or f32_ne(points2 + pdt)
|
||
|
|
points2 = icast(tu_hmdt == 1 and points2 or f32_ne(points2 * tu_hmdt))
|
||
|
|
if points > points2 then
|
||
|
|
points, points2, pointsR = points2, points, points-points2
|
||
|
|
elseif points < points2 then
|
||
|
|
pointsR = points2-points
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if causeTag ~= "Thorn" and points <= 0 and points2 >= 0 then
|
||
|
|
points, pointsR = 0, points2
|
||
|
|
end
|
||
|
|
if points2 > 0 or (causeTag == "Thorn" and (points ~= 0 or points2 ~= 0)) then
|
||
|
|
local tracer = self.trace
|
||
|
|
if tracer then
|
||
|
|
tracer(self, "HIT", sourceIndex, targetIndex, points, tHP, causeTag, causeSID, pointsR)
|
||
|
|
end
|
||
|
|
if points < 0 then
|
||
|
|
local nHP, maxHP, nrHP = tHP-points2, tu.maxHP, rHP + pointsR
|
||
|
|
if nHP > maxHP then
|
||
|
|
tu.curHP = maxHP
|
||
|
|
local nr = nrHP - nHP + maxHP
|
||
|
|
tu.rHP = nr > 0 and nr or 0
|
||
|
|
else
|
||
|
|
tu.curHP, tu.hpR = nHP, nrHP
|
||
|
|
end
|
||
|
|
elseif tHP > points then
|
||
|
|
tu.curHP, tu.hpR = tHP - points, rHP + pointsR
|
||
|
|
else
|
||
|
|
tu.curHP, tu.hpR = 0, 0
|
||
|
|
mu.die(self, sourceIndex, targetIndex, causeTag, eDNE)
|
||
|
|
end
|
||
|
|
if tu.curHP > 0 and tu.curHP <= tu.hpR then
|
||
|
|
local oracle = self.finalHitOracle
|
||
|
|
local survived = oracle and oracle(self.turn, sourceIndex, targetIndex, causeSID, tHP, rHP)
|
||
|
|
if survived == nil then
|
||
|
|
survived = math.random() >= 0.5
|
||
|
|
local f = self:Clone()
|
||
|
|
if f then
|
||
|
|
local tuf = f.board[targetIndex]
|
||
|
|
if survived then
|
||
|
|
tuf.curHP, tuf.hpR = 0,0
|
||
|
|
mu.die(f, sourceIndex, targetIndex, causeTag, eDNE)
|
||
|
|
else
|
||
|
|
tuf.hpR = tuf.curHP-1
|
||
|
|
local thorns = tu.thornsDamage
|
||
|
|
if thorns > 0 and causeTag ~= "Tick" and causeTag ~= "Thorn" and causeTag ~= "EEcho" then
|
||
|
|
local fqh = f.sqh-1
|
||
|
|
f.sqh, f.sq[fqh] = fqh, {"damage", targetIndex, sourceIndex, thorns, "Thorn", tu.thornsSID}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if survived then
|
||
|
|
tu.hpR = tu.curHP-1
|
||
|
|
else
|
||
|
|
tu.curHP, tu.hpR = 0, 0
|
||
|
|
mu.die(self, sourceIndex, targetIndex, causeTag, eDNE)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local thorns = tu.thornsDamage
|
||
|
|
if thorns > 0 and causeTag ~= "Tick" and causeTag ~= "Thorn" and causeTag ~= "EEcho" then
|
||
|
|
mu.damage(self, targetIndex, sourceIndex, thorns, "Thorn", tu.thornsSID)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:dtick(sourceIndex, targetIndex, esid, eeid, causeTag, causeSID)
|
||
|
|
local board = self.board
|
||
|
|
local tu, su = board[targetIndex], board[sourceIndex]
|
||
|
|
if tu.curHP > 0 then
|
||
|
|
local effect = eeid ~= 0 and SpellInfo[esid][eeid] or SpellInfo[esid]
|
||
|
|
local datk, dperc = effect.damageATK, effect.damagePerc
|
||
|
|
local points = (datk and f32_pim(datk,su.atk) or 0) + (dperc and floor(dperc*tu.maxHP/100) or 0)
|
||
|
|
if points > 0 then
|
||
|
|
mu.damage(self, sourceIndex, targetIndex, points, "Tick", causeSID, effect.dne)
|
||
|
|
end
|
||
|
|
local datk, dperc = effect.healATK, effect.healPerc
|
||
|
|
local points = (datk and f32_pim(datk,su.atk) or 0) + (dperc and floor(dperc*tu.maxHP/100) or 0)
|
||
|
|
if points > 0 then
|
||
|
|
mu.mend(self, sourceIndex, targetIndex, points, causeTag, causeSID)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:mend(sourceIndex, targetIndex, halfPoints, causeTag, causeSID)
|
||
|
|
local board = self.board
|
||
|
|
local tu = board[targetIndex]
|
||
|
|
local cHP = tu.curHP
|
||
|
|
if cHP > 0 then
|
||
|
|
local points = floor(halfPoints)
|
||
|
|
if points > 0 then
|
||
|
|
local nhp, max, rHP = cHP + points, tu.maxHP, tu.hpR
|
||
|
|
if nhp > max then
|
||
|
|
local nr = rHP - nhp + max
|
||
|
|
tu.curHP, tu.hpR = max, nr > 0 and nr or 0
|
||
|
|
else
|
||
|
|
tu.curHP = nhp
|
||
|
|
end
|
||
|
|
local tracer = self.trace
|
||
|
|
if tracer then
|
||
|
|
tracer(self, "HEAL", sourceIndex, targetIndex, points, cHP, causeTag, causeSID)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:unshroud(_sourceIndex, targetIndex)
|
||
|
|
local tu = self.board[targetIndex]
|
||
|
|
if tu then
|
||
|
|
local ns = (tu.shroud or 0) - 1
|
||
|
|
tu.shroud, self.ftc = ns > 0 and ns or nil, nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:untaunt(source, target, _sid)
|
||
|
|
local tu = self.board[target]
|
||
|
|
if tu and tu.taunt == source then
|
||
|
|
tu.taunt = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:statDelta(_sourceIndex, targetIndex, statName, delta)
|
||
|
|
local tu = self.board[targetIndex]
|
||
|
|
if tu then
|
||
|
|
local nv = tu[statName] + delta
|
||
|
|
tu[statName] = nv
|
||
|
|
if statName == "maxHP" and tu.curHP > nv then
|
||
|
|
tu.curHP = nv
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:die(sourceIndex, deadIndex, causeTag, eDNE)
|
||
|
|
local k, board, wasOver = deadIndex < 5 and "liveFriends" or "liveEnemies", self.board, self.over
|
||
|
|
self[k], self.ftc = self[k] - 1, nil
|
||
|
|
if self[k] == 0 and not self.over then
|
||
|
|
self.over, self.dne = true, eDNE or nil
|
||
|
|
if causeTag ~= "Thorn" and sourceIndex ~= deadIndex and self.won == nil then
|
||
|
|
self.won = deadIndex > 4
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local ds = 0
|
||
|
|
for i=0,12 do
|
||
|
|
local tu = board[i]
|
||
|
|
if tu then
|
||
|
|
if tu.taunt == deadIndex then
|
||
|
|
tu.taunt = nil
|
||
|
|
end
|
||
|
|
local tds = (tu.deathSeq or 0)
|
||
|
|
if tds > ds then
|
||
|
|
ds = tds
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local du = board[deadIndex]
|
||
|
|
du.deathSeq = ds + 1
|
||
|
|
if (causeTag == "Thorn" or deadIndex == sourceIndex) and self.over and not wasOver then
|
||
|
|
self.overnext, self.over = self.turn, nil
|
||
|
|
end
|
||
|
|
if causeTag ~= "Thorn" then
|
||
|
|
local duw = du.deathUnwind
|
||
|
|
for i=1, duw and #duw or 0 do
|
||
|
|
local q = duw[i]
|
||
|
|
mu[q[1]](self, unpack(q,2))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:passive(source, sid)
|
||
|
|
local board = self.board
|
||
|
|
local si, su = SpellInfo[sid], board[source]
|
||
|
|
local onDeath = su.deathUnwind or {}
|
||
|
|
local mdd, mdt, tatk = si.modDamageDealt, si.modDamageTaken, si.thornsATK
|
||
|
|
local td = tatk and f32_pim(tatk, su.atk)
|
||
|
|
local tt = VS.GetTargets(source, si.target, board)
|
||
|
|
for i=1,#tt do
|
||
|
|
i = tt[i]
|
||
|
|
local tu = board[i]
|
||
|
|
if mdd then
|
||
|
|
mu.modDamageDealt(self, source, i, mdd, sid)
|
||
|
|
onDeath[#onDeath+1] = {"unstackf32", source, i, "modDamageDealt", mdd}
|
||
|
|
end
|
||
|
|
if mdt then
|
||
|
|
mu.modDamageTaken(self, source, i, mdt, sid)
|
||
|
|
onDeath[#onDeath+1] = {"unstackf32", source, i, "modDamageTaken", mdt}
|
||
|
|
end
|
||
|
|
if td then
|
||
|
|
tu.thornsDamage = tu.thornsDamage + td
|
||
|
|
tu.thornsSID = tu.thornsSID or sid
|
||
|
|
onDeath[#onDeath+1] = {"statDelta", source, i, "thornsDamage", -td}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
su.deathUnwind = onDeath
|
||
|
|
end
|
||
|
|
function mu:aura0(sourceIndex, targetIndex, _targetSeq, _ord, si, sid, _eid)
|
||
|
|
local board = self.board
|
||
|
|
local su = board[sourceIndex]
|
||
|
|
local firstTick = not si.period and not si.noFirstTick
|
||
|
|
local d1, d2, h2 = si.damageATK1, firstTick and si.damageATK, firstTick and si.healATK
|
||
|
|
local ec = (d1 and 1 or 0) + (d2 and 1 or 0) + (h2 and 1 or 0)
|
||
|
|
local sATK = su.atk
|
||
|
|
if ec == 1 then
|
||
|
|
if d1 then
|
||
|
|
mu.damage(self, sourceIndex, targetIndex, f32_pim(d1, sATK), "EFirst", sid)
|
||
|
|
elseif d2 then
|
||
|
|
mu.damage(self, sourceIndex, targetIndex, f32_pim(d2, sATK), si.nore and "Tick" or "EFront", sid)
|
||
|
|
else
|
||
|
|
mu.mend(self, sourceIndex, targetIndex, f32_pim(h2, sATK), "EFront", sid)
|
||
|
|
end
|
||
|
|
elseif ec > 0 then
|
||
|
|
local sq, sqh = self.sq, self.sqh-1
|
||
|
|
if h2 then
|
||
|
|
sqh, sq[sqh] = sqh-1, {"mend", sourceIndex, targetIndex, f32_pim(h2, sATK), "EFront", sid}
|
||
|
|
end
|
||
|
|
if d2 then
|
||
|
|
sqh, sq[sqh] = sqh-1, {"damage", sourceIndex, targetIndex, f32_pim(d2, sATK), (si.nore or d1) and "Tick" or "EFront", sid}
|
||
|
|
end
|
||
|
|
if d1 then
|
||
|
|
sqh, sq[sqh] = sqh-1, {"damage", sourceIndex, targetIndex, f32_pim(d1, sATK), "EFirst", sid}
|
||
|
|
end
|
||
|
|
self.sqh = sqh+1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:aura(sourceIndex, targetIndex, targetSeq, ord, si, sid, eid)
|
||
|
|
local board = self.board
|
||
|
|
local su, tu = board[sourceIndex], board[targetIndex]
|
||
|
|
if tu.curHP <= 0 then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
local period, duration = si.period, si.duration
|
||
|
|
local mdd, mdt = si.modDamageDealt, si.modDamageTaken
|
||
|
|
local pdda, pdta, thornsa = si.plusDamageDealtATK, si.plusDamageTakenATK, si.thornsATK
|
||
|
|
local thornsp = thornsa and f32_pim(thornsa, tu.atk)
|
||
|
|
local hasDamage, hasHeal = si.damageATK or si.damagePerc, si.healATK or si.healPerc
|
||
|
|
local modHPP, modHPA = si.modMaxHP, si.modMaxHPATK
|
||
|
|
local pdd, pdt = pdda and f32_fpim(pdda, su.atk) or si.plusDamageDealtA, pdta and f32_fpim(pdta, su.atk) or si.plusDamageTakenA
|
||
|
|
local ordt, ordf, fadeTurn = ord-1e6+targetSeq, ord-8e5, self.turn+duration
|
||
|
|
if mdd then
|
||
|
|
mu.modDamageDealt(self, sourceIndex, targetIndex, mdd, sid)
|
||
|
|
enq(self.queue, fadeTurn, {"unstackf32", sourceIndex, targetIndex, "modDamageDealt", mdd, ord=ordf})
|
||
|
|
end
|
||
|
|
if mdt then
|
||
|
|
mu.modDamageTaken(self, sourceIndex, targetIndex, mdt, sid)
|
||
|
|
enq(self.queue, fadeTurn, {"unstackf32", sourceIndex, targetIndex, "modDamageTaken", mdt, ord=ordf})
|
||
|
|
end
|
||
|
|
if pdd then
|
||
|
|
tu.plusDamageDealt = tu.plusDamageDealt + pdd
|
||
|
|
enq(self.queue, fadeTurn, {"statDelta", sourceIndex, targetIndex, "plusDamageDealt", -pdd, ord=ordf})
|
||
|
|
end
|
||
|
|
if pdt then
|
||
|
|
tu.plusDamageTaken = tu.plusDamageTaken + pdt
|
||
|
|
enq(self.queue, fadeTurn, {"statDelta", sourceIndex, targetIndex, "plusDamageTaken", -pdt, ord=ordf})
|
||
|
|
end
|
||
|
|
if modHPP or modHPA then
|
||
|
|
local d = (modHPP and f32_pim(modHPP, tu.maxHP) or 0) + (modHPA and f32_pim(modHPA, su.atk) or 0)
|
||
|
|
tu.curHP, tu.maxHP = tu.curHP+d, tu.maxHP+d
|
||
|
|
enq(self.queue, fadeTurn, {"statDelta", sourceIndex, targetIndex, "maxHP", -d, ord=ordf})
|
||
|
|
end
|
||
|
|
if thornsp then
|
||
|
|
tu.thornsDamage = tu.thornsDamage + thornsp
|
||
|
|
tu.thornsSID = tu.thornsSID or sid
|
||
|
|
enq(self.queue, fadeTurn, {"statDelta", sourceIndex, targetIndex, "thornsDamage", -thornsp, ord=ordf})
|
||
|
|
end
|
||
|
|
if hasDamage or hasHeal then
|
||
|
|
local tb = fadeTurn-duration-1
|
||
|
|
if period == 2 then
|
||
|
|
for j=3,duration+1,2 do
|
||
|
|
enq(self.queue, tb+j, {"dtick", sourceIndex, targetIndex, sid, eid, "Effect", sid, ord=ordt})
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for j=2,duration do
|
||
|
|
enq(self.queue, tb+j, {"dtick", sourceIndex, targetIndex, sid, eid, "Effect", sid, ord=ordt})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if si.echo then
|
||
|
|
enq(self.queue, self.turn+si.echo, {"damage", sourceIndex, targetIndex, f32_pim(si.damageATK, su.atk), "EEcho", sid, ord=ordt})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:nuke(sourceIndex, targetIndex, targetSeq, ord, si, sid, _eid)
|
||
|
|
local board = self.board
|
||
|
|
local su, tu = board[sourceIndex], board[targetIndex]
|
||
|
|
local sATK, datk, dperc, echo = su.atk, si.damageATK, si.damagePerc, si.echo
|
||
|
|
local points = (datk and f32_pim(datk, sATK) or 0) + (dperc and floor(dperc*tu.maxHP/100) or 0)
|
||
|
|
local causeTag = si.nore and "Tick" or "Spell"
|
||
|
|
mu.damage(self, sourceIndex, targetIndex, points, causeTag, sid)
|
||
|
|
if tu.curHP > 0 and echo then
|
||
|
|
enq(self.queue, self.turn+echo, {"damage", sourceIndex, targetIndex, points, "Tick", sid, ord=ord-1e6+targetSeq})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:nukem(sourceIndex, targetIndex, _targetSeq, _ord, si, sid, _eid)
|
||
|
|
local su = self.board[sourceIndex]
|
||
|
|
local sATK, d = su.atk, si.damageATK
|
||
|
|
local sq, sqh = self.sq, self.sqh-1
|
||
|
|
for j=#d, 1, -1 do
|
||
|
|
sq[sqh], sqh = {"damage", sourceIndex, targetIndex, f32_pim(d[j], sATK), "Spell", sid}, sqh - 1
|
||
|
|
end
|
||
|
|
self.sqh = sqh+1
|
||
|
|
end
|
||
|
|
function mu:heal(sourceIndex, targetIndex, _targetSeq, ord, si, sid, _eid)
|
||
|
|
local board = self.board
|
||
|
|
local su, tu = board[sourceIndex], board[targetIndex]
|
||
|
|
local hPerc, hatk = si.healPercent, si.healATK
|
||
|
|
local points = (hatk and f32_pim(hatk, su.atk) or 0) + (hPerc and floor(hPerc*tu.maxHP/100) or 0)
|
||
|
|
mu.mend(self, sourceIndex, targetIndex, points, "Spell", sid)
|
||
|
|
if si.shroudTurns then
|
||
|
|
tu.shroud, self.ftc = (tu.shroud or 0) + 1, nil
|
||
|
|
enq(self.queue, self.turn+si.shroudTurns, {"unshroud", sourceIndex, targetIndex, ord=ord-80})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function mu:taunt(sourceIndex, targetIndex, _targetSeq, ord, si, sid, _eid)
|
||
|
|
local tu = self.board[targetIndex]
|
||
|
|
tu.taunt = sourceIndex
|
||
|
|
enq(self.queue, self.turn+si.duration, {"untaunt", sourceIndex, targetIndex, sid, ord=ord-8e5})
|
||
|
|
end
|
||
|
|
function mu:cast(sourceIndex, sid, recast, qe)
|
||
|
|
local board = self.board
|
||
|
|
local su = board[sourceIndex]
|
||
|
|
if su.curHP <= 0 and sourceIndex >= 0 then
|
||
|
|
return
|
||
|
|
elseif self.overnext then
|
||
|
|
self.over, self.overnext = true
|
||
|
|
return
|
||
|
|
end
|
||
|
|
local si, ord = SpellInfo[sid], (qe.ord or 0)+recast*40
|
||
|
|
if si.type == "passive" then
|
||
|
|
return mu.passive(self, sourceIndex, sid)
|
||
|
|
else
|
||
|
|
enqc(self.queue, self.turn+recast, {"cast", sourceIndex, sid, recast, ord=ord, ord0=qe.ord0})
|
||
|
|
end
|
||
|
|
return mu.qcast(self, sourceIndex, sid, si[1] and 1 or 0, ord-1)
|
||
|
|
end
|
||
|
|
function mu:qcast(sourceIndex, sid, eid, ord1, forkTarget)
|
||
|
|
local si, board = SpellInfo[sid], self.board
|
||
|
|
local ne = #si
|
||
|
|
for i=eid, ne do
|
||
|
|
local si = si[i] or si
|
||
|
|
local sitt, tt = si.target
|
||
|
|
local ft = forkTargets[sitt]
|
||
|
|
if ft and forkTarget then
|
||
|
|
tt = VS.GetTargets(forkTarget, "self", board)
|
||
|
|
elseif ft then
|
||
|
|
local pileOn = band(self.ftc or 0, forkTargetBits[ft]) > 0 and self["ft-" .. ft]
|
||
|
|
pileOn = pileOn and VS.GetTargets(pileOn, "self", board)
|
||
|
|
if pileOn and pileOn[1] then
|
||
|
|
tt = pileOn
|
||
|
|
else
|
||
|
|
tt = VS.GetTargets(sourceIndex, ft, board)
|
||
|
|
if #tt > 1 then
|
||
|
|
local oracle = self.forkOracle
|
||
|
|
local ownTarget = oracle and oracle(self.turn, sourceIndex, sid) or tt[math.random(#tt)]
|
||
|
|
for i=1,#tt do
|
||
|
|
if tt[i] == ownTarget then
|
||
|
|
tt[1], tt[i] = tt[i], tt[1]
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
for j=2,#tt do
|
||
|
|
local f = self:Clone()
|
||
|
|
if not f then
|
||
|
|
break
|
||
|
|
end
|
||
|
|
local sqh = f.sqh-1
|
||
|
|
f.sq[sqh], f.sqh = {"qcast", sourceIndex, sid, i, ord1, tt[j]}, sqh
|
||
|
|
end
|
||
|
|
end
|
||
|
|
tt = tt[1] and VS.GetTargets(tt[1], "self", board)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
tt = VS.GetTargets(sourceIndex, sitt, board)
|
||
|
|
end
|
||
|
|
if ft then
|
||
|
|
self.ftc, self["ft-"..ft] = bor(self.ftc or 0, tt and tt[1] and forkTargetBits[ft] or 0), tt and tt[1] or nil
|
||
|
|
end
|
||
|
|
|
||
|
|
local et, sq, sqt, ordi = si.type, self.sq, self.sqt, ord1+i
|
||
|
|
for ti=1,tt and #tt or 0 do
|
||
|
|
local targetIndex = tt[ti]
|
||
|
|
if et == "aura" then
|
||
|
|
sqt = sqt + 1
|
||
|
|
sq[sqt] = {"aura0", sourceIndex, targetIndex, ti, ordi, si, sid, i}
|
||
|
|
end
|
||
|
|
sqt = sqt + 1
|
||
|
|
sq[sqt] = {et, sourceIndex, targetIndex, ti, ordi, si, sid, i}
|
||
|
|
end
|
||
|
|
if et == "nuke" and si.selfhealATK then
|
||
|
|
sqt = sqt + 1
|
||
|
|
sq[sqt] = {"mend", sourceIndex, sourceIndex, f32_pim(si.selfhealATK, board[sourceIndex].atk), "DHeal", sid}
|
||
|
|
end
|
||
|
|
self.sqt = sqt
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function resolveRange(bFirst, b, f, bh, bl, fh, fl)
|
||
|
|
if bFirst then
|
||
|
|
if bh < fh then
|
||
|
|
f.curHP, f.hpR = bh, bh-fl
|
||
|
|
end
|
||
|
|
if fl > bl then
|
||
|
|
b.hpR = bh-fl
|
||
|
|
end
|
||
|
|
else
|
||
|
|
if fh <= bh then
|
||
|
|
b.curHP, b.hpR = fh - 1, fh - 1 - bl
|
||
|
|
end
|
||
|
|
if fl <= bl then
|
||
|
|
f.hpR = fh - bl - 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local function resolveDeath(board, a, b, inOrder)
|
||
|
|
if not inOrder then
|
||
|
|
a, b = b, a
|
||
|
|
end
|
||
|
|
local abit, bbit = 2^a, 2^b
|
||
|
|
local lo, au, bu = a < 5, board[a], board[b]
|
||
|
|
au.drB, au.drC = bor(au.drB, bbit, bu.drC), bor(au.drC, bbit, bu.drC)
|
||
|
|
bu.drB, bu.drA = bor(bu.drB, abit, au.drA), bor(bu.drA, abit, au.drA)
|
||
|
|
local aA, bC, aC, maxAA = au.drA, bu.drC, au.drC, 0
|
||
|
|
for i=lo and 0 or 5, lo and 4 or 12 do
|
||
|
|
local iu, ibit = board[i], 2^i
|
||
|
|
if iu and iu.curHP == 0 then
|
||
|
|
if band(aA, ibit) then
|
||
|
|
iu.drB, iu.drC = bor(iu.drB, bC), bor(iu.drC, bC)
|
||
|
|
maxAA = iu.deathSeq > maxAA and iu.deathSeq or maxAA
|
||
|
|
elseif band(bC, ibit) then
|
||
|
|
iu.drB, iu.drA = bor(iu.drB, aA), bor(iu.drA, aA)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
maxAA = maxAA+1
|
||
|
|
au.deathSeq = maxAA
|
||
|
|
for i=lo and 0 or 5, lo and 4 or 12 do
|
||
|
|
local iu, ibit = board[i], 2^i
|
||
|
|
if i ~= a and iu and iu.curHP == 0 and (iu.deathSeq >= maxAA or band(aC, ibit) > 0) then
|
||
|
|
iu.deathSeq = maxAA + iu.deathSeq
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local function prepareDeath(self, turn, du, deadIndex)
|
||
|
|
local dside, masks, horizon = deadIndex < 5
|
||
|
|
for k,v in pairs(self.queue) do
|
||
|
|
if k >= turn then
|
||
|
|
local fim, fom, dtm = 0,0,0
|
||
|
|
for i=1,#v do
|
||
|
|
local vi = v[i]
|
||
|
|
if vi[2] == deadIndex then
|
||
|
|
local mt, tar, ex = vi[1], vi[3], vi[4]
|
||
|
|
local tside = tar < 5
|
||
|
|
if tside == dside and (mt == "unstackf32" and ex == "modDamageDealt" or mt == "statDelta" and ex == "plusDamageDealt") then
|
||
|
|
fom = bor(fom, 2^tar)
|
||
|
|
elseif tside ~= dside and (mt == "unstackf32" and ex == "modDamageTaken" or mt == "statDelta" and ex == "plusDamageTaken") then
|
||
|
|
fim = bor(fim, 2^tar)
|
||
|
|
elseif (mt == "damage" or mt == "dtick") then
|
||
|
|
dtm = bor(dtm, 2^tar)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if fim > 0 or fom > 0 or dtm > 0 then
|
||
|
|
masks = masks or {}
|
||
|
|
masks[3*k] = fim > 0 and fim or nil
|
||
|
|
masks[3*k+1] = fom > 0 and fom or nil
|
||
|
|
masks[3*k+2] = dtm > 0 and dtm or nil
|
||
|
|
horizon = horizon and horizon > k and horizon or k
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
du.dmasks, du.dhorizon, du.drA, du.drB, du.drC = masks, horizon, 0, 0, 0
|
||
|
|
end
|
||
|
|
local function prepareTurn(self)
|
||
|
|
local board, turn = self.board, self.turn
|
||
|
|
|
||
|
|
local mlive = 0
|
||
|
|
for b=0,12 do
|
||
|
|
local e = board[b]
|
||
|
|
if e and e.curHP > 0 then
|
||
|
|
mlive = mlive + 2^b
|
||
|
|
elseif e and not e.drA then
|
||
|
|
prepareDeath(self, turn, e, b)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local t3, pm, oracle, skip = turn*3, band(self.pmask, mlive), self.firstHitOracle, self.saoSkip or 0
|
||
|
|
for b=0, 12 do
|
||
|
|
local e = board[b]
|
||
|
|
local bh = e and e.curHP
|
||
|
|
local bl = bh and (bh-e.hpR)
|
||
|
|
local bd = bh == 0 and (e.dhorizon or 0) >= turn
|
||
|
|
for i=b+1,bh and (b<5 and 4 or 12) or 0 do
|
||
|
|
local f = board[i]
|
||
|
|
local fh = f and f.curHP
|
||
|
|
local fl = fh and (fh-f.hpR)
|
||
|
|
local fd = fh == 0 and (f.dhorizon or 0) >= turn
|
||
|
|
if skip >= (16*b+i) then
|
||
|
|
elseif fh and fh > 0 and bh > 0 and not (bl >= fh or fl > bh) then
|
||
|
|
local bFirst = oracle and oracle(turn, b, i)
|
||
|
|
if bFirst == nil and oracle then
|
||
|
|
else
|
||
|
|
if bFirst == nil then
|
||
|
|
bFirst = math.random(2) == 1
|
||
|
|
end
|
||
|
|
local fc, fb = self:Clone()
|
||
|
|
if fc then
|
||
|
|
fc.turn, fb, fc.saoSkip = turn-1, fc.board, b*16+i
|
||
|
|
resolveRange(not bFirst, fb[b], fb[i], bh, bl, fh, fl)
|
||
|
|
end
|
||
|
|
resolveRange(bFirst, e, f, bh, bl, fh, fl)
|
||
|
|
end
|
||
|
|
elseif bd and fd and band(e.drB, 2^i) == 0 then
|
||
|
|
local bm, fm, bbit, fbit = e.dmasks, f.dmasks, 2^b, 2^i
|
||
|
|
local bfi, bfo, bdt = bm[t3], bm[t3+1], bm[t3+2]
|
||
|
|
local ffi, ffo, fdt = fm[t3], fm[t3+1], fm[t3+2]
|
||
|
|
bdt, fdt = bdt and band(bdt, mlive) or 0, fdt and band(fdt, mlive) or 0
|
||
|
|
if bdt > 0 and (ffi and band(ffi, bdt) > 0 or ffo and band(ffo, bbit) > 0) or
|
||
|
|
fdt > 0 and (bfi and band(bfi, fdt) > 0 or bfo and band(bfo, fbit) > 0) or
|
||
|
|
bdt > 0 and fdt > 0 and pm > 0 and (band(bdt, pm) > 0 or band(fdt, pm) > 0)
|
||
|
|
then
|
||
|
|
local bFirst = oracle and oracle(turn, b, i)
|
||
|
|
if bFirst == nil then
|
||
|
|
bFirst = math.random(2) == 1
|
||
|
|
end
|
||
|
|
local fc = self:Clone()
|
||
|
|
if fc then
|
||
|
|
fc.turn, fc.saoSkip = turn-1, b*16+i
|
||
|
|
resolveDeath(fc.board, b, i, not bFirst)
|
||
|
|
end
|
||
|
|
resolveDeath(board, b, i, bFirst)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
self.saoSkip = nil
|
||
|
|
end
|
||
|
|
local function sortAttackOrder(self, q)
|
||
|
|
local board, bo, bom = self.board, self.boardOrder, self.bom
|
||
|
|
for b=0,12 do
|
||
|
|
local e = board[b]
|
||
|
|
if e then
|
||
|
|
bom[b] = (b < 5 and 1e9 or 2e9) - e.curHP * 1e3 + b + 20*(e.deathSeq or 0)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
table.sort(bo, function(a,b)
|
||
|
|
return bom[a] < bom[b]
|
||
|
|
end)
|
||
|
|
for i=1,#bo do
|
||
|
|
bom[bo[i]] = i
|
||
|
|
end
|
||
|
|
table.sort(q, function(a, b)
|
||
|
|
local ac, bc = a.ord0 or bom[a[2]], b.ord0 or bom[b[2]]
|
||
|
|
if ac == bc then
|
||
|
|
ac, bc = a.ord or 0, b.ord or 0
|
||
|
|
end
|
||
|
|
return ac > bc
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
local function registerTraceResult(self, stopCB)
|
||
|
|
local prime = self.prime or self
|
||
|
|
local ch = self.checkpoints
|
||
|
|
if ch[#ch] == ch[#ch-1] then
|
||
|
|
ch[#ch] = nil
|
||
|
|
end
|
||
|
|
for _, a in pairs(self.queue) do
|
||
|
|
for _, qi in pairs(a) do
|
||
|
|
if qi[1] == "statDelta" then
|
||
|
|
mu[qi[1]](self, unpack(qi, 2))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
self.over, self.queue, self.sq, self.sqh, self.sqt = "r", nil
|
||
|
|
if self.won == nil then
|
||
|
|
self.won = self.liveFriends > 0
|
||
|
|
end
|
||
|
|
local tHP1, tHP2, ns, res, inf = 0, 0, 0, prime.res, math.huge
|
||
|
|
local wHP1, wHP2, wmask = 0,0, self.wmask or 31
|
||
|
|
res[self.won and "hadWins" or "hadLosses"] = true
|
||
|
|
res.n = res.n + 1
|
||
|
|
res.isFinished = res.n > #(prime.forks or "")
|
||
|
|
for i=0,12 do
|
||
|
|
local e = self.board[i]
|
||
|
|
if e then
|
||
|
|
local hp1, hp2 = e.curHP-e.hpR, e.curHP
|
||
|
|
tHP1, tHP2, ns = tHP1 + hp1, tHP2 + hp2, ns + (e.curHP > 0 and 1 or 0)
|
||
|
|
if wmask and band(wmask, 2^i) > 0 then
|
||
|
|
wHP1, wHP2 = wHP1 + hp1, wHP2 + hp2
|
||
|
|
end
|
||
|
|
res.min[i] = math.min(res.min[i] or inf, hp1)
|
||
|
|
res.max[i] = math.max(res.max[i] or 0, hp2)
|
||
|
|
end
|
||
|
|
if i == 4 or i == 12 then
|
||
|
|
local o = i == 4 and 12 or 14
|
||
|
|
res.min[o+1], res.max[o+1] = math.min(tHP1, res.min[o+1] or inf), math.max(tHP2, res.max[o+1] or 0)
|
||
|
|
res.min[o+2], res.max[o+2] = math.min(ns, res.min[o+2] or inf), math.max(ns, res.max[o+2] or 0)
|
||
|
|
tHP1, tHP2, ns = 0, 0, 0
|
||
|
|
end
|
||
|
|
end
|
||
|
|
res.min[17] = math.min(res.min[17] or inf, self.turn)
|
||
|
|
res.max[17] = math.max(res.max[17] or 0, self.turn)
|
||
|
|
res.min[18] = math.min(res.min[18] or inf, wHP1)
|
||
|
|
res.max[18] = math.max(res.max[18] or 0, wHP2)
|
||
|
|
if self.forkID and self.dropForks then
|
||
|
|
self.forks[self.forkID] = not not self.won
|
||
|
|
end
|
||
|
|
if stopCB and self.forks and #self.forks >= res.n and stopCB(prime, res.n, 1+#self.forks, self) then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local function storeShallowCopy(r, s)
|
||
|
|
local d = {}
|
||
|
|
for k,v in pairs(s) do
|
||
|
|
d[k] = v
|
||
|
|
end
|
||
|
|
r[s] = d
|
||
|
|
end
|
||
|
|
|
||
|
|
function VSI:Turn()
|
||
|
|
local sq, sqh, q, turn = self.sq, self.sqh
|
||
|
|
if self.unfinishedTurn then
|
||
|
|
turn = self.turn
|
||
|
|
q = self.queue[turn]
|
||
|
|
while sqh <= self.sqt do
|
||
|
|
local e = sq[sqh]
|
||
|
|
self.sqh, sq[sqh] = sqh + 1
|
||
|
|
mu[e[1]](self, unpack(e, 2))
|
||
|
|
sqh = self.sqh
|
||
|
|
end
|
||
|
|
else
|
||
|
|
turn = self.turn + 1
|
||
|
|
q, self.turn = self.queue[turn], turn
|
||
|
|
prepareTurn(self)
|
||
|
|
sortAttackOrder(self, q)
|
||
|
|
self.unfinishedTurn = true
|
||
|
|
end
|
||
|
|
local qi, at
|
||
|
|
for i=#q, 1, -1 do
|
||
|
|
qi, q[i] = q[i], nil
|
||
|
|
at = qi[1]
|
||
|
|
mu[at](self, unpack(qi, 2))
|
||
|
|
sqh = self.sqh
|
||
|
|
while sqh <= self.sqt do
|
||
|
|
local e = sq[sqh]
|
||
|
|
self.sqh, sq[sqh] = sqh + 1
|
||
|
|
mu[e[1]](self, unpack(e, 2))
|
||
|
|
sqh = self.sqh
|
||
|
|
end
|
||
|
|
if self.over then
|
||
|
|
if self.dne then
|
||
|
|
self.dne = nil
|
||
|
|
else
|
||
|
|
for j=i-1,1,-1 do
|
||
|
|
if q[j][2] == qi[2] and q[j][1] == "statDelta" then
|
||
|
|
qi, q[j] = q[j], nil
|
||
|
|
mu[qi[1]](self, unpack(qi, 2))
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if self.overnext and self.overnext < turn then
|
||
|
|
self.over, self.overnext = true
|
||
|
|
end
|
||
|
|
self.checkpoints[turn] = self:CheckpointBoard()
|
||
|
|
self.queue[turn], self.unfinishedTurn = next(q) and q, nil
|
||
|
|
end
|
||
|
|
function VSI:Run(stopCB)
|
||
|
|
if self.over ~= "r" then
|
||
|
|
if self.unfinishedTurn then
|
||
|
|
self:Turn()
|
||
|
|
end
|
||
|
|
while not self.over do
|
||
|
|
self:Turn()
|
||
|
|
if stopCB and self.turn % 100 == 0 and not self.over and stopCB(self.prime or self) then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if registerTraceResult(self, stopCB) then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if self.forks and not self.prime then
|
||
|
|
local i, forks = self.res.n, self.forks
|
||
|
|
while i <= #forks do
|
||
|
|
if forks[i]:Run(stopCB) then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
i = i + 1
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function VSI:CheckpointBoard()
|
||
|
|
local board = self.board
|
||
|
|
local c = ""
|
||
|
|
for i=0,12 do
|
||
|
|
local b = board[i]
|
||
|
|
if b and b.curHP > 0 then
|
||
|
|
local r = b.hpR
|
||
|
|
c = (c ~= "" and c .. "_" or "") .. slotHex[i] .. ":" .. b.curHP .. (r > 0 and "-" .. r or "")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return c
|
||
|
|
end
|
||
|
|
function VSI:Clone()
|
||
|
|
local lim, forks = self.forkLimit, self.forks
|
||
|
|
if lim and lim <= (forks and #forks or 0) then
|
||
|
|
self.res.hadDrops = true
|
||
|
|
return
|
||
|
|
elseif forks == nil then
|
||
|
|
forks = {[0]=self}
|
||
|
|
end
|
||
|
|
local n = setmetatable({}, VSIm)
|
||
|
|
local q, r, s, d = {}, {[self]=n}, self, n
|
||
|
|
self.forks, forks[#forks+1] = forks, n
|
||
|
|
r[self.prime or 0], r[self.res or 0], r[forks] = self.prime, self.res, forks
|
||
|
|
r[0] = nil
|
||
|
|
if s.sq then
|
||
|
|
storeShallowCopy(r, s.sq)
|
||
|
|
end
|
||
|
|
if s.queue then
|
||
|
|
for _, v in pairs(s.queue) do
|
||
|
|
storeShallowCopy(r, v)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
while s do
|
||
|
|
q[s] = nil
|
||
|
|
for k,v in pairs(s) do
|
||
|
|
if r[k] then
|
||
|
|
k = r[k]
|
||
|
|
elseif type(k) == "table" then
|
||
|
|
r[k] = {}
|
||
|
|
q[k], k = r[k], r[k]
|
||
|
|
end
|
||
|
|
if r[v] then
|
||
|
|
v = r[v]
|
||
|
|
elseif type(v) == "table" then
|
||
|
|
r[v] = {}
|
||
|
|
q[v], v = r[v], r[v]
|
||
|
|
end
|
||
|
|
d[k] = v
|
||
|
|
end
|
||
|
|
q[s] = nil
|
||
|
|
s, d = next(q)
|
||
|
|
end
|
||
|
|
n.prime, n.forkID, n.forkOracle = self.prime or self, #forks
|
||
|
|
return n
|
||
|
|
end
|
||
|
|
function VSI:AddFightLogOracles(log)
|
||
|
|
function self.forkOracle(turn, source, spell)
|
||
|
|
local la = log[turn]
|
||
|
|
la = la and la.events
|
||
|
|
for i=1,la and #la or 0 do
|
||
|
|
local li = la[i]
|
||
|
|
if li.spellID == spell and li.casterBoardIndex == source and (li.type < 5 or li.type == 7) and li.targetInfo[1] then
|
||
|
|
return li.targetInfo[1].boardIndex
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
function self.finalHitOracle(turn, source, target, sid, _oldHP, _oldRange)
|
||
|
|
local la = log[turn]
|
||
|
|
la = la and la.events
|
||
|
|
for i=1,la and #la or 0 do
|
||
|
|
local li = la[i]
|
||
|
|
if li.type == 9 and li.casterBoardIndex == source and li.spellID == sid then
|
||
|
|
local tt = li.targetInfo
|
||
|
|
for j=1,tt and #tt or 0 do
|
||
|
|
if tt[j].boardIndex == target then
|
||
|
|
return false
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
function self.firstHitOracle(turn, a, b)
|
||
|
|
local la = log[turn]
|
||
|
|
la = la and la.events
|
||
|
|
for i=1,la and #la or 0 do
|
||
|
|
local li = la[i]
|
||
|
|
local c, t = li.casterBoardIndex, li.type
|
||
|
|
if t == 9 then
|
||
|
|
local tt = li.targetInfo
|
||
|
|
for i=1,tt and #tt or 0 do
|
||
|
|
local di = tt[i].boardIndex
|
||
|
|
if di == a or di == b then
|
||
|
|
return nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif (c == a or c == b) then
|
||
|
|
local si = SpellInfo[li.spellID]
|
||
|
|
if (t == 7 or not (si and si.thornsATK)) and (t ~= 8 or not (si and si.type == "passive")) then
|
||
|
|
return c == a
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function addActorProps(a)
|
||
|
|
a.modDamageTaken = 1
|
||
|
|
a.modDamageDealt = 1
|
||
|
|
a.modDamageTakenH = 1
|
||
|
|
a.modDamageDealtH = 1
|
||
|
|
a.plusDamageTaken = 0
|
||
|
|
a.plusDamageDealt = 0
|
||
|
|
a.thornsDamage = 0
|
||
|
|
a.hpR = 0
|
||
|
|
a.stacks = {}
|
||
|
|
return a
|
||
|
|
end
|
||
|
|
local function addCasts(q, slot, spells, aa, missingSpells, pmask)
|
||
|
|
for i=1,#spells do
|
||
|
|
local s = spells[i]
|
||
|
|
local sid = s.autoCombatSpellID
|
||
|
|
local si = SpellInfo[sid]
|
||
|
|
if not si then
|
||
|
|
missingSpells = missingSpells or {}
|
||
|
|
missingSpells[sid] = 1
|
||
|
|
elseif si.type ~= "nop" then
|
||
|
|
local qe = {"cast", slot, sid, 1+s.cooldown}
|
||
|
|
if si.type == "passive" then
|
||
|
|
qe.ord0, qe.ord = 0, slot*1e3 + i*10
|
||
|
|
pmask = si.modDamageTaken and bor(pmask, 2^slot) or pmask
|
||
|
|
else
|
||
|
|
qe.ord = (1+slot)*1e7 + 5e6 + i*1e5
|
||
|
|
end
|
||
|
|
enqc(q, si.firstTurn or 1, qe)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
enqc(q, 1, {"cast", slot, aa, 1, ord=(1+slot)*1e7 + 5e6})
|
||
|
|
return missingSpells, pmask
|
||
|
|
end
|
||
|
|
function VS:New(team, encounters, envSpell, mid, mscalar, forkLimit)
|
||
|
|
local q, board, nf, pmask, missingSpells = {}, {}, 0, 0
|
||
|
|
for slot, f in pairs(team) do
|
||
|
|
if f.stats then
|
||
|
|
f.attack, f.health, f.maxHealth = f.stats.attack, f.stats.currentHealth, f.stats.maxHealth
|
||
|
|
end
|
||
|
|
local rf, sa = {maxHP=f.maxHealth, curHP=math.max(1,f.health), atk=f.attack, slot=f.boardIndex or slot, name=f.name}, f.spells
|
||
|
|
local aa = VS:GetAutoAttack(f.role, nil, nil, sa and sa[1] and sa[1].autoCombatSpellID)
|
||
|
|
missingSpells, pmask = addCasts(q, rf.slot, sa, aa, missingSpells, pmask)
|
||
|
|
board[rf.slot], nf = addActorProps(rf), nf + 1
|
||
|
|
end
|
||
|
|
for i=1,#encounters do
|
||
|
|
local e = encounters[i]
|
||
|
|
local rf, sa = {maxHP=e.maxHealth, curHP=e.maxHealth, atk=e.attack, slot=e.boardIndex}, e.autoCombatSpells
|
||
|
|
local aa = VS:GetAutoAttack(e.role, rf.slot, mid, sa and sa[1] and sa[1].autoCombatSpellID)
|
||
|
|
missingSpells, pmask = addCasts(q, rf.slot, sa, aa, missingSpells, pmask)
|
||
|
|
board[e.boardIndex] = addActorProps(rf)
|
||
|
|
end
|
||
|
|
|
||
|
|
local environmentSID = envSpell and envSpell.autoCombatSpellID
|
||
|
|
local esi = SpellInfo[environmentSID]
|
||
|
|
if environmentSID and not esi then
|
||
|
|
missingSpells = missingSpells or {}
|
||
|
|
missingSpells[environmentSID] = 2
|
||
|
|
elseif esi and esi.type ~= "nop" then
|
||
|
|
-- There's no way making the environment killable is going to cause problems later. Nope. No way at all.
|
||
|
|
board[-1] = addActorProps({atk=(esi.cATKa or 0) + (esi.cATKb or 0)*mscalar, curHP=1e9, maxHP=1e9, slot=-1})
|
||
|
|
enqc(q, esi.firstTurn or 1, {"cast", -1, environmentSID, 1+envSpell.cooldown, ord=0})
|
||
|
|
end
|
||
|
|
|
||
|
|
local boardOrder = {}
|
||
|
|
for b=0,12 do
|
||
|
|
local e = board[b]
|
||
|
|
if e then
|
||
|
|
boardOrder[1+#boardOrder] = b
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local ii = setmetatable({
|
||
|
|
board=board, turn=0, queue=q, sq={}, sqh=1, sqt=0,
|
||
|
|
liveFriends=nf, liveEnemies=#encounters, over=nf == 0,
|
||
|
|
checkpoints={}, boardOrder=boardOrder, bom={[-1]=14},
|
||
|
|
res={min={}, max={}, hadWins=false, hadLosses=false, hadDrops=false, isFinished=false, n=0},
|
||
|
|
pmask=pmask,
|
||
|
|
forkLimit=forkLimit,
|
||
|
|
}, VSIm)
|
||
|
|
ii.checkpoints[0] = ii:CheckpointBoard()
|
||
|
|
if ii.over then
|
||
|
|
registerTraceResult(ii)
|
||
|
|
end
|
||
|
|
return ii, missingSpells
|
||
|
|
end
|
||
|
|
function VS:SetSpellInfo(t)
|
||
|
|
SpellInfo = t
|
||
|
|
end
|
||
|
|
|
||
|
|
T.VSim, VS.VSI, VS.mu = VS, VSI, mu
|