Files
StellaSora_DataLua/lua/game/adventure/startower/startowerautodecision.lua
T
SL1900 08b515afd9 Major Update - 2.0.0.143
EN: 2.0.0.143
CN: 2.0.0.142
JP: 2.0.0.145
KR: 2.0.0.150
2026-10-20 13:00:00 +09:00

970 lines
34 KiB
Lua

local StarTowerAutoDecision = {}
StarTowerAutoDecision.ActionType = {
None = 0,
SelectPotential = 1,
PotentialReroll = 2,
OpenShop = 3,
StrengthMachine = 4,
ShopBuy = 5,
ShopReroll = 6,
EventSelect = 7
}
StarTowerAutoDecision.FailReason = {
None = 0,
Disabled = 1,
InvalidParam = 2,
NoOption = 3,
NoMatchedTarget = 4,
TokenNotEnough = 5,
CoinNotEnough = 6,
RerollUnavailable = 7
}
StarTowerAutoDecision.PotentialRule = {
MainChar = 1,
AssistChar1 = 2,
AssistChar2 = 3,
BookLocked = 4,
PresetNotOwned = 5,
LevelAdd2 = 6,
LevelAdd3 = 7,
RarePotential = 8
}
StarTowerAutoDecision.ShopMode = {BuyFirst = 1, UpgradeFirst = 2}
StarTowerAutoDecision.ShopRule = {
NeedNote = 1,
DiscountNote = 2,
DiscountMainPotential = 3,
DiscountRandomPotential = 4,
MainPotentialNoDiscount = 5,
RandomPotentialNoDiscount = 6
}
StarTowerAutoDecision.EventTendency = {
Stable = 1,
Standard = 2,
Aggressive = 3
}
StarTowerAutoDecision.EventRule = {
MainPotential = 1,
AssistPotential = 2,
RarePotential = 3,
NormalPotential = 4,
NeedNote = 5,
RandomNote = 6,
Coin = 7
}
StarTowerAutoDecision.OptionTag = {
Stable = 1,
Standard = 2,
Aggressive = 3,
MainPotential = 4,
AssistPotential = 5,
Coin = 6,
RarePotential = 7,
NormalPotential = 8,
RandomPotential = 9,
RandomNote = 10,
NeedNote = 11
}
local DEFAULT_DELAY = 2
local DEFAULT_TOKEN_COST = 1
local FALLBACK_SCORE = 1
local PRIORITY_BASE_SCORE = 10000
local PRESET_SCORE = 100000
local TENDENCY_SCORE = 50000
local CORRECT_EVENT_OPTION_SCORE = 1000000
local _IsTable = function(value)
return type(value) == "table"
end
local _ToNumber = function(value, nDefault)
local nValue = tonumber(value)
if nValue == nil then
return nDefault or 0
end
return nValue
end
local _IsArrayEmpty = function(tbList)
return not _IsTable(tbList) or #tbList <= 0
end
local _ListContains = function(tbList, value)
if not _IsTable(tbList) then
return false
end
for _, v in ipairs(tbList) do
if v == value then
return true
end
end
return false
end
local _BuildSet = function(tbList, bZeroBasedIndex)
local mapSet = {}
if not _IsTable(tbList) then
return mapSet
end
for _, value in ipairs(tbList) do
mapSet[value] = true
if bZeroBasedIndex and type(value) == "number" then
mapSet[value + 1] = true
end
end
return mapSet
end
local _CopyArray = function(tbList)
local tbRet = {}
if not _IsTable(tbList) then
return tbRet
end
for _, v in ipairs(tbList) do
table.insert(tbRet, v)
end
return tbRet
end
local _AddReason = function(mapCandidate, sReason)
if mapCandidate.tbReasons == nil then
mapCandidate.tbReasons = {}
end
table.insert(mapCandidate.tbReasons, sReason)
end
local _GetPlan = function(mapContext)
if _IsTable(mapContext) and _IsTable(mapContext.mapPlan) then
return mapContext.mapPlan
end
return {}
end
local _GetTokenCost = function(mapContext, nActionType)
if _IsTable(mapContext) and _IsTable(mapContext.mapTokenCostByAction) then
local nCost = mapContext.mapTokenCostByAction[nActionType]
if nCost ~= nil then
return nCost
end
end
if _IsTable(mapContext) and mapContext.nTokenCost ~= nil then
return _ToNumber(mapContext.nTokenCost, DEFAULT_TOKEN_COST)
end
return DEFAULT_TOKEN_COST
end
local _MakeFail = function(nFailReason, sReason, tbDebug)
return {
bSuccess = false,
nActionType = StarTowerAutoDecision.ActionType.None,
nFailReason = nFailReason or StarTowerAutoDecision.FailReason.None,
sReason = sReason or "",
tbDebug = tbDebug or {}
}
end
local _MakeSuccess = function(nActionType, mapData)
mapData = mapData or {}
mapData.bSuccess = true
mapData.nActionType = nActionType
mapData.nFailReason = StarTowerAutoDecision.FailReason.None
if mapData.nDelay == nil then
mapData.nDelay = DEFAULT_DELAY
end
if mapData.bNeedToken == nil then
mapData.bNeedToken = false
end
return mapData
end
local _AttachTokenCheck = function(mapContext, mapDecision)
if not _IsTable(mapDecision) or not mapDecision.bSuccess then
return mapDecision
end
if mapDecision.bNeedToken ~= true then
return mapDecision
end
local nCost = mapDecision.nTokenCost or _GetTokenCost(mapContext, mapDecision.nActionType)
mapDecision.nTokenCost = nCost
if _IsTable(mapContext) and mapContext.nToken ~= nil and nCost > _ToNumber(mapContext.nToken, 0) then
return _MakeFail(StarTowerAutoDecision.FailReason.TokenNotEnough, "TokenNotEnough", {
nActionType = mapDecision.nActionType,
nToken = mapContext.nToken,
nTokenCost = nCost
})
end
return mapDecision
end
local _BuildPriorityMap = function(tbPriority, bMapMode)
local mapPriority = {}
if not _IsTable(tbPriority) then
return mapPriority
end
if bMapMode then
for nRule, nPriority in pairs(tbPriority) do
if type(nRule) == "number" and type(nPriority) == "number" and 0 < nPriority then
mapPriority[nRule] = nPriority
end
end
return mapPriority
end
for nPriority, nRule in ipairs(tbPriority) do
if type(nRule) == "number" and 0 < nPriority then
mapPriority[nRule] = nPriority
end
end
return mapPriority
end
local _GetPriorityScore = function(mapPriority, nRule)
local nPriority = mapPriority[nRule]
if nPriority == nil or nPriority <= 0 then
return 0
end
return PRIORITY_BASE_SCORE - nPriority
end
local _RefreshCandidateScore = function(mapCandidate)
mapCandidate.nScore = _ToNumber(mapCandidate.nBaseScore, 0) + _ToNumber(mapCandidate.nPriorityScore, 0)
end
local _AddCandidateBaseScore = function(mapCandidate, nScore, sReason)
mapCandidate.nBaseScore = _ToNumber(mapCandidate.nBaseScore, 0) + nScore
_RefreshCandidateScore(mapCandidate)
if sReason ~= nil then
_AddReason(mapCandidate, sReason)
end
end
local _AddCandidatePriorityScore = function(mapCandidate, mapPriority, nRule, sReason)
local nScore = _GetPriorityScore(mapPriority, nRule)
if nScore <= 0 then
return false
end
mapCandidate.mapMatchedPriority = mapCandidate.mapMatchedPriority or {}
mapCandidate.mapMatchedPriority[mapPriority[nRule]] = true
if nScore > _ToNumber(mapCandidate.nPriorityScore, 0) then
mapCandidate.nPriorityScore = nScore
_RefreshCandidateScore(mapCandidate)
end
_AddReason(mapCandidate, sReason)
return true
end
local _GetPotentialCfg = function(nPotentialId)
if ConfigTable == nil then
return nil
end
return ConfigTable.GetData("Potential", nPotentialId)
end
local _GetItemCfg = function(nItemId)
if ConfigTable == nil then
return nil
end
if ConfigTable.GetData_Item ~= nil then
local mapItem = ConfigTable.GetData_Item(nItemId)
if mapItem ~= nil then
return mapItem
end
end
return ConfigTable.GetData("Item", nItemId)
end
local _GetEventOptionCfg = function(nOptionId)
if ConfigTable == nil then
return nil
end
return ConfigTable.GetData("EventOptions", nOptionId)
end
local _GetPotentialCurLevel = function(mapContext, nPotentialId, nCharId, mapPotential)
if _IsTable(mapPotential) and mapPotential[nPotentialId] ~= nil then
return _ToNumber(mapPotential[nPotentialId], 0)
end
if _IsTable(mapContext) and _IsTable(mapContext.mapPotential) then
if nCharId ~= nil and _IsTable(mapContext.mapPotential[nCharId]) and mapContext.mapPotential[nCharId][nPotentialId] ~= nil then
return _ToNumber(mapContext.mapPotential[nCharId][nPotentialId], 0)
end
if mapContext.mapPotential[nPotentialId] ~= nil then
return _ToNumber(mapContext.mapPotential[nPotentialId], 0)
end
end
return 0
end
local _GetMainCharId = function(mapContext)
if not _IsTable(mapContext) then
return 0
end
if mapContext.nMainCharId ~= nil then
return mapContext.nMainCharId
end
if _IsTable(mapContext.tbTeam) then
return mapContext.tbTeam[1] or 0
end
return 0
end
local _GetAssistCharId = function(mapContext, nIndex)
if not _IsTable(mapContext) then
return 0
end
if _IsTable(mapContext.tbAssistCharIds) then
return mapContext.tbAssistCharIds[nIndex] or 0
end
if _IsTable(mapContext.tbTeam) then
return mapContext.tbTeam[nIndex + 1] or 0
end
return 0
end
local _IsRarePotential = function(mapContext, nPotentialId, mapItemCfg, bSpecial)
if bSpecial == true then
return true
end
if _IsTable(mapContext) and _IsTable(mapContext.mapRarePotentialIds) and mapContext.mapRarePotentialIds[nPotentialId] == true then
return true
end
if mapItemCfg == nil then
mapItemCfg = _GetItemCfg(nPotentialId)
end
if mapItemCfg == nil then
return false
end
if GameEnum ~= nil and GameEnum.itemStype ~= nil and mapItemCfg.Stype == GameEnum.itemStype.SpecificPotential then
return true
end
local nRareRarity = GameEnum ~= nil and GameEnum.itemRarity ~= nil and GameEnum.itemRarity.SSR or 1
if _IsTable(mapContext) and _GetPlan(mapContext).nRarePotentialRarity ~= nil then
nRareRarity = _GetPlan(mapContext).nRarePotentialRarity
end
return mapItemCfg.Rarity ~= nil and mapItemCfg.Rarity > 0 and nRareRarity >= mapItemCfg.Rarity
end
local _NormalizeRecommend = function(tbRecommend)
local mapRecommend = {}
if not _IsTable(tbRecommend) then
return mapRecommend
end
for _, mapData in ipairs(tbRecommend) do
local nId = mapData.nId or mapData.Id
if nId ~= nil then
mapRecommend[nId] = mapData
end
end
return mapRecommend
end
local _NormalizePotentialOptions = function(tbPotential)
local tbCandidates = {}
if not _IsTable(tbPotential) then
return tbCandidates
end
for nIndex, mapData in ipairs(tbPotential) do
local nPotentialId = mapData.Id or mapData.nId or mapData.Tid
if nPotentialId ~= nil then
table.insert(tbCandidates, {
nIndex = nIndex,
nTargetId = nPotentialId,
nOptionLevel = _ToNumber(mapData.Count or mapData.Level or mapData.nLevel, 0),
mapRawData = mapData,
nBaseScore = 0,
nPriorityScore = 0,
nScore = 0,
tbReasons = {}
})
end
end
return tbCandidates
end
local _CompareCandidatePriority = function(mapCandidateA, mapCandidateB)
if mapCandidateA.nScore ~= mapCandidateB.nScore then
return mapCandidateA.nScore > mapCandidateB.nScore
end
local mapPriorityA = mapCandidateA.mapMatchedPriority or {}
local mapPriorityB = mapCandidateB.mapMatchedPriority or {}
local nFirstDifferentPriority = math.huge
for nPriority in pairs(mapPriorityA) do
if not mapPriorityB[nPriority] and nPriority < nFirstDifferentPriority then
nFirstDifferentPriority = nPriority
end
end
for nPriority in pairs(mapPriorityB) do
if not mapPriorityA[nPriority] and nPriority < nFirstDifferentPriority then
nFirstDifferentPriority = nPriority
end
end
if nFirstDifferentPriority < math.huge then
return mapPriorityA[nFirstDifferentPriority] == true
end
return nil
end
local _SortCandidates = function(tbCandidates)
table.sort(tbCandidates, function(mapCandidateA, mapCandidateB)
local bHigherPriority = _CompareCandidatePriority(mapCandidateA, mapCandidateB)
if bHigherPriority ~= nil then
return bHigherPriority
end
return mapCandidateA.nIndex < mapCandidateB.nIndex
end)
end
local _SortShopCandidates = function(tbCandidates)
local mapFirstIndexByGroup = {}
for _, mapCandidate in ipairs(tbCandidates) do
local sGroup = mapCandidate.sDiscountGroup
local nFirstIndex = mapFirstIndexByGroup[sGroup]
if nFirstIndex == nil or nFirstIndex > mapCandidate.nIndex then
mapFirstIndexByGroup[sGroup] = mapCandidate.nIndex
end
end
table.sort(tbCandidates, function(mapCandidateA, mapCandidateB)
local bHigherPriority = _CompareCandidatePriority(mapCandidateA, mapCandidateB)
if bHigherPriority ~= nil then
return bHigherPriority
end
local nGroupIndexA = mapFirstIndexByGroup[mapCandidateA.sDiscountGroup]
local nGroupIndexB = mapFirstIndexByGroup[mapCandidateB.sDiscountGroup]
if nGroupIndexA ~= nGroupIndexB then
return nGroupIndexA < nGroupIndexB
end
if mapCandidateA.nDiscountRate ~= mapCandidateB.nDiscountRate then
return mapCandidateA.nDiscountRate < mapCandidateB.nDiscountRate
end
return mapCandidateA.nIndex < mapCandidateB.nIndex
end)
end
function StarTowerAutoDecision:IsEnabled(mapContext)
if not _IsTable(mapContext) then
return false
end
if mapContext.bAutoEnabled == false then
return false
end
local mapPlan = _GetPlan(mapContext)
if mapPlan.bEnabled == false then
return false
end
return true
end
function StarTowerAutoDecision:BuildContext(objLevelData, mapPlan, mapExtra)
local mapContext = mapExtra or {}
mapContext.mapPlan = mapPlan or mapContext.mapPlan or {}
if objLevelData == nil then
return mapContext
end
mapContext.nTowerId = mapContext.nTowerId or objLevelData.nTowerId
mapContext.nCurFloor = mapContext.nCurFloor or objLevelData.nCurLevel
mapContext.tbTeam = mapContext.tbTeam or _CopyArray(objLevelData.tbTeam)
mapContext.mapNote = mapContext.mapNote or objLevelData._mapNote
mapContext.mapPotential = mapContext.mapPotential or objLevelData._mapPotential
mapContext.mapItem = mapContext.mapItem or objLevelData._mapItem
mapContext.mapFateCard = mapContext.mapFateCard or objLevelData._mapFateCard
mapContext.mapPreselectionData = mapContext.mapPreselectionData or objLevelData.mapPreselectionData
if mapContext.nCoin == nil and _IsTable(objLevelData._mapItem) and AllEnum ~= nil and AllEnum.CoinItemId ~= nil then
mapContext.nCoin = objLevelData._mapItem[AllEnum.CoinItemId.FixedRogCurrency] or 0
end
if mapContext.bFinalFloor == nil and _IsTable(objLevelData.tbStarTowerAllLevel) and objLevelData.nCurLevel ~= nil then
mapContext.bFinalFloor = objLevelData.nCurLevel == #objLevelData.tbStarTowerAllLevel
end
return mapContext
end
function StarTowerAutoDecision:DecidePotential(mapContext, tbPotential, mapPotential, mapRoll, tbNewIds, tbLuckyIds, tbRecommend)
if not self:IsEnabled(mapContext) then
return _MakeFail(StarTowerAutoDecision.FailReason.Disabled, "AutoDisabled")
end
local tbCandidates = _NormalizePotentialOptions(tbPotential)
if _IsArrayEmpty(tbCandidates) then
return _MakeFail(StarTowerAutoDecision.FailReason.NoOption, "NoPotentialOption")
end
local mapPlan = _GetPlan(mapContext)
local mapPriority = _BuildPriorityMap(mapPlan.tbPotentialRulePriority, false)
if next(mapPriority) == nil then
mapPriority = _BuildPriorityMap(mapPlan.mapPotentialRulePriority, true)
end
local bHasPreset = _IsTable(mapContext.mapPreselectionData)
local mapRecommend = bHasPreset and _NormalizeRecommend(tbRecommend) or {}
local mapNewIds = _BuildSet(tbNewIds)
local mapLuckyIds = _BuildSet(tbLuckyIds)
local nMainCharId = _GetMainCharId(mapContext)
local nAssistCharId1 = _GetAssistCharId(mapContext, 1)
local nAssistCharId2 = _GetAssistCharId(mapContext, 2)
local bPreferPreset = bHasPreset and mapPlan.bPreferPresetPotential ~= false
local bPresetOnly = bPreferPreset and mapPlan.bPresetOnly == true
local bHasSpecialPotential = false
for _, mapCandidate in ipairs(tbCandidates) do
local nPotentialId = mapCandidate.nTargetId
local mapPotentialCfg = _GetPotentialCfg(nPotentialId)
local mapItemCfg = _GetItemCfg(nPotentialId)
local nCharId = mapPotentialCfg and mapPotentialCfg.CharId or 0
local nCurLevel = _GetPotentialCurLevel(mapContext, nPotentialId, nCharId, mapPotential)
local mapRecommendData = mapRecommend[nPotentialId]
local nRecommendLevel = mapRecommendData and _ToNumber(mapRecommendData.nLevel or mapRecommendData.Level, 0) or 0
local bSpecial = mapCandidate.mapRawData.bSpecial == true or mapItemCfg ~= nil and GameEnum ~= nil and GameEnum.itemStype ~= nil and mapItemCfg.Stype == GameEnum.itemStype.SpecificPotential
bHasSpecialPotential = bHasSpecialPotential or bSpecial
mapCandidate.bPresetEligible = mapRecommendData ~= nil and nCurLevel < nRecommendLevel
mapCandidate.bPresetSpecial = bSpecial and mapCandidate.bPresetEligible
if mapCandidate.bPresetSpecial then
_AddCandidateBaseScore(mapCandidate, PRESET_SCORE, "PresetSpecialPotential")
elseif bPreferPreset and mapCandidate.bPresetEligible then
_AddCandidateBaseScore(mapCandidate, PRESET_SCORE, "PreferPresetPotential")
end
if nCharId == nMainCharId then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.PotentialRule.MainChar, "MainChar")
end
if nCharId == nAssistCharId1 then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.PotentialRule.AssistChar1, "AssistChar1")
end
if nCharId == nAssistCharId2 then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.PotentialRule.AssistChar2, "AssistChar2")
end
if mapNewIds[nPotentialId] then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.PotentialRule.BookLocked, "BookLocked")
end
if mapRecommendData ~= nil and nCurLevel <= 0 then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.PotentialRule.PresetNotOwned, "PresetNotOwned")
end
if mapCandidate.nOptionLevel == 2 then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.PotentialRule.LevelAdd2, "LevelAdd2")
end
if mapCandidate.nOptionLevel == 3 then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.PotentialRule.LevelAdd3, "LevelAdd3")
end
if _IsRarePotential(mapContext, nPotentialId, mapItemCfg, mapCandidate.mapRawData.bSpecial) then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.PotentialRule.RarePotential, "RarePotential")
end
if mapNewIds[nPotentialId] then
_AddReason(mapCandidate, "NewId")
end
if mapLuckyIds[nPotentialId] then
_AddReason(mapCandidate, "LuckyId")
end
end
_SortCandidates(tbCandidates)
local mapBest = tbCandidates[1]
local mapPresetSpecial
for _, mapCandidate in ipairs(tbCandidates) do
if mapCandidate.bPresetSpecial then
mapPresetSpecial = mapCandidate
break
end
end
if mapPresetSpecial ~= nil then
mapBest = mapPresetSpecial
elseif bPresetOnly then
mapBest = nil
for _, mapCandidate in ipairs(tbCandidates) do
if mapCandidate.bPresetEligible then
mapBest = mapCandidate
break
end
end
end
if mapBest == nil or 0 >= mapBest.nScore then
local nCoin = _ToNumber(mapContext.nCoin, 0)
local bCanReroll = not bHasSpecialPotential and _IsTable(mapRoll) and mapRoll.CanReRoll == true
local nRerollPrice = _ToNumber(_IsTable(mapRoll) and mapRoll.ReRollPrice or 0, 0)
local nRerollCoinMin = _ToNumber(mapPlan.nPotentialRerollCoinMin, 0)
if mapPlan.bPotentialReroll == true and bCanReroll and nCoin >= nRerollPrice and nCoin > nRerollCoinMin then
return _AttachTokenCheck(mapContext, _MakeSuccess(StarTowerAutoDecision.ActionType.PotentialReroll, {
bReRoll = true,
bNeedToken = false,
nTargetIndex = 0,
nTargetId = 0,
sReason = "PotentialReroll",
tbDebug = {nCoin = nCoin, nRerollPrice = nRerollPrice}
}))
end
mapBest = tbCandidates[1]
if 0 >= mapBest.nScore then
mapBest.nScore = FALLBACK_SCORE
_AddReason(mapBest, "FallbackFirst")
end
end
return _AttachTokenCheck(mapContext, _MakeSuccess(StarTowerAutoDecision.ActionType.SelectPotential, {
nTargetIndex = mapBest.nIndex,
nTargetId = mapBest.nTargetId,
bNeedToken = true,
sReason = mapBest.tbReasons[1] or "SelectPotential",
tbDebug = {
nScore = mapBest.nScore,
tbReasons = mapBest.tbReasons,
mapRawData = mapBest.mapRawData
}
}))
end
local _DecideUpgradeBeforeShopSpendAll = function(mapContext, mapShopOption, nCoin)
local mapPlan = _GetPlan(mapContext)
if mapContext.bFinalFloor ~= true or mapPlan.bShopSpendAllWhenFinalNoTarget ~= true or (mapPlan.nShopMode or StarTowerAutoDecision.ShopMode.BuyFirst) ~= StarTowerAutoDecision.ShopMode.BuyFirst or mapShopOption.bMachine ~= true then
return nil
end
local nMachineCost = _ToNumber(mapShopOption.nMachineCost, 0)
local nMaxUpgradePrice = _ToNumber(mapPlan.nShopUpgradeMaxPrice, 180)
if nMachineCost > nMaxUpgradePrice or nCoin < nMachineCost then
return nil
end
return _AttachTokenCheck(mapContext, _MakeSuccess(StarTowerAutoDecision.ActionType.StrengthMachine, {
bNeedToken = true,
sReason = "UpgradeBeforeShopSpendAll",
tbDebug = {nMachineCost = nMachineCost, nCoin = nCoin}
}))
end
function StarTowerAutoDecision:DecideShopEntry(mapContext, mapShopOption)
if not self:IsEnabled(mapContext) then
return _MakeFail(StarTowerAutoDecision.FailReason.Disabled, "AutoDisabled")
end
if not _IsTable(mapShopOption) then
return _MakeFail(StarTowerAutoDecision.FailReason.InvalidParam, "InvalidShopOption")
end
local mapPlan = _GetPlan(mapContext)
local nMode = mapPlan.nShopMode or StarTowerAutoDecision.ShopMode.BuyFirst
local nCoin = _ToNumber(mapShopOption.nCoin or mapContext.nCoin, 0)
local bShop = mapShopOption.bShop == true
local bMachine = mapShopOption.bMachine == true
local nMachineCost = _ToNumber(mapShopOption.nMachineCost, 0)
local nMaxUpgradePrice = _ToNumber(mapPlan.nShopUpgradeMaxPrice, 180)
if bShop and mapShopOption.bUpgradeBeforeSpendAll == true then
local mapDecision = _DecideUpgradeBeforeShopSpendAll(mapContext, mapShopOption, nCoin)
if mapDecision ~= nil then
return mapDecision
end
end
local bSpendAllOnUpgrade = mapContext.bFinalFloor == true and mapPlan.bShopSpendAllWhenFinalNoTarget == true and mapShopOption.bShopExhausted == true and not bShop
local bMachineAvailable = bMachine and nCoin >= nMachineCost and (bSpendAllOnUpgrade or nMachineCost <= nMaxUpgradePrice)
if nMode == StarTowerAutoDecision.ShopMode.UpgradeFirst and bMachineAvailable then
return _AttachTokenCheck(mapContext, _MakeSuccess(StarTowerAutoDecision.ActionType.StrengthMachine, {
bNeedToken = true,
sReason = "UpgradeFirst",
tbDebug = {nMachineCost = nMachineCost, nCoin = nCoin}
}))
end
if bShop then
return _MakeSuccess(StarTowerAutoDecision.ActionType.OpenShop, {bNeedToken = false, sReason = "OpenShop"})
end
if bMachineAvailable then
return _AttachTokenCheck(mapContext, _MakeSuccess(StarTowerAutoDecision.ActionType.StrengthMachine, {
bNeedToken = true,
sReason = "StrengthMachine",
tbDebug = {nMachineCost = nMachineCost, nCoin = nCoin}
}))
end
return _MakeFail(StarTowerAutoDecision.FailReason.NoMatchedTarget, "NoShopEntryTarget", {
bShop = bShop,
bMachine = bMachine,
nMachineCost = nMachineCost,
nCoin = nCoin
})
end
local _IsDiscountGood = function(mapGood)
return 0 < _ToNumber(mapGood.nDiscount, 0)
end
local _GetGoodPrice = function(mapGood)
if 0 < _ToNumber(mapGood.nDiscount, 0) then
return _ToNumber(mapGood.nDiscount, 0)
end
return _ToNumber(mapGood.Price, 0)
end
local _GetGoodDiscountRate = function(mapGood)
local nOriginalPrice = _ToNumber(mapGood.Price, 0)
local nDiscountPrice = _ToNumber(mapGood.nDiscount, 0)
if nOriginalPrice <= 0 or nDiscountPrice <= 0 then
return 1
end
return nDiscountPrice / nOriginalPrice
end
local _GetShopGoodDiscountGroup = function(mapGood, bDiscount, bMainPotential, bRandomPotential)
local nType = _ToNumber(mapGood.nType, 0)
local sSubType = "Other"
if nType == 1 then
if bMainPotential then
sSubType = "MainPotential"
elseif bRandomPotential then
sSubType = "RandomPotential"
else
sSubType = "Potential"
end
elseif nType == 2 then
sSubType = "Note"
end
return tostring(nType) .. ":" .. sSubType .. ":" .. (bDiscount and "Discount" or "Regular")
end
local _IsNoteGood = function(mapGood, mapItemCfg)
if _ToNumber(mapGood.nType, 0) ~= 2 then
return false
end
if mapItemCfg == nil then
mapItemCfg = _GetItemCfg(mapGood.nGoodsId)
end
return mapItemCfg ~= nil and GameEnum ~= nil and GameEnum.itemStype ~= nil and mapItemCfg.Stype == GameEnum.itemStype.SubNoteSkill
end
local _IsPotentialGood = function(mapGood)
return _ToNumber(mapGood.nType, 0) == 1
end
local _GetPotentialGoodCharId = function(mapGood)
return _ToNumber(mapGood.nCharId, 0)
end
local _IsMainPotentialGood = function(mapContext, mapGood)
if not _IsPotentialGood(mapGood) then
return false
end
local nMainCharId = _GetMainCharId(mapContext)
local nGoodCharId = _GetPotentialGoodCharId(mapGood)
return 0 < nMainCharId and 0 < nGoodCharId and nGoodCharId == nMainCharId
end
local _IsRandomPotentialGood = function(mapGood)
if not _IsPotentialGood(mapGood) then
return false
end
return _GetPotentialGoodCharId(mapGood) <= 0
end
local _HasShopReroll = function(mapRoll)
return _IsTable(mapRoll) and mapRoll.CanReRoll == true and 0 < _ToNumber(mapRoll.ReRollTimes, 0)
end
local _CanShopReroll = function(mapPlan, mapContext, mapRoll, nCoin)
local bFinalFloor = mapContext.bFinalFloor == true
local bAllowReroll = bFinalFloor and mapPlan.bShopRerollWhenFinalNoTarget == true or not bFinalFloor and mapPlan.bShopRerollWhenNoTarget == true
local bCanReroll = _HasShopReroll(mapRoll)
local nRerollPrice = _ToNumber(_IsTable(mapRoll) and mapRoll.ReRollPrice or 0, 0)
local nRerollCoinMin = _ToNumber(mapPlan.nShopRerollCoinMin, 500)
return bAllowReroll and bCanReroll and nCoin >= nRerollPrice and (bFinalFloor or nCoin > nRerollCoinMin), nRerollPrice
end
function StarTowerAutoDecision:DecideShop(mapContext, tbShopData, mapRoll, nCoin)
if not self:IsEnabled(mapContext) then
return _MakeFail(StarTowerAutoDecision.FailReason.Disabled, "AutoDisabled")
end
if _IsArrayEmpty(tbShopData) then
local mapDecision = _MakeFail(StarTowerAutoDecision.FailReason.NoOption, "NoShopGoods")
mapDecision.bShopExhausted = not _HasShopReroll(mapRoll)
return mapDecision
end
local mapPlan = _GetPlan(mapContext)
local mapPriority = _BuildPriorityMap(mapPlan.tbShopRulePriority, false)
if next(mapPriority) == nil then
mapPriority = _BuildPriorityMap(mapPlan.mapShopRulePriority, true)
end
local nCurCoin = _ToNumber(nCoin or mapContext.nCoin, 0)
local tbCandidates = {}
for nIndex, mapGood in ipairs(tbShopData) do
local nPrice = _GetGoodPrice(mapGood)
if mapGood.bSoldOut ~= true and nCurCoin >= nPrice then
local mapItemCfg = _ToNumber(mapGood.nType, 0) == 2 and _GetItemCfg(mapGood.nGoodsId) or nil
local bDiscount = _IsDiscountGood(mapGood)
local bNote = _IsNoteGood(mapGood, mapItemCfg)
local bNeedNote = bNote
if _IsTable(mapContext.mapNeedNote) then
bNeedNote = bNote and mapContext.mapNeedNote[mapGood.nGoodsId] == true
end
local bMainPotential = _IsMainPotentialGood(mapContext, mapGood)
local bRandomPotential = _IsRandomPotentialGood(mapGood)
local mapCandidate = {
nIndex = nIndex,
nTargetIndex = mapGood.Idx or nIndex,
nTargetId = mapGood.nSid,
nGoodsId = mapGood.nGoodsId,
nPrice = nPrice,
nDiscountRate = _GetGoodDiscountRate(mapGood),
sDiscountGroup = _GetShopGoodDiscountGroup(mapGood, bDiscount, bMainPotential, bRandomPotential),
nBaseScore = 0,
nPriorityScore = 0,
nScore = 0,
tbReasons = {},
mapRawData = mapGood
}
if bNeedNote then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.ShopRule.NeedNote, "NeedNote")
end
if bMainPotential and not bDiscount then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.ShopRule.MainPotentialNoDiscount, "MainPotentialNoDiscount")
end
if bRandomPotential and not bDiscount then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.ShopRule.RandomPotentialNoDiscount, "RandomPotentialNoDiscount")
end
if bNote and bDiscount then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.ShopRule.DiscountNote, "DiscountNote")
end
if bMainPotential and bDiscount then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.ShopRule.DiscountMainPotential, "DiscountMainPotential")
end
if bRandomPotential and bDiscount then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.ShopRule.DiscountRandomPotential, "DiscountRandomPotential")
end
table.insert(tbCandidates, mapCandidate)
end
end
if _IsArrayEmpty(tbCandidates) then
local bCanReroll, nRerollPrice = _CanShopReroll(mapPlan, mapContext, mapRoll, nCurCoin)
if bCanReroll then
return _MakeSuccess(StarTowerAutoDecision.ActionType.ShopReroll, {
bReRoll = true,
bNeedToken = false,
sReason = "ShopRerollNoAffordableGoods",
tbDebug = {nCoin = nCurCoin, nRerollPrice = nRerollPrice}
})
end
local mapDecision = _MakeFail(StarTowerAutoDecision.FailReason.CoinNotEnough, "NoAffordableGoods", {nCoin = nCurCoin})
mapDecision.bShopExhausted = not _HasShopReroll(mapRoll)
return mapDecision
end
_SortShopCandidates(tbCandidates)
local mapBest = tbCandidates[1]
if 0 >= mapBest.nScore then
local bFinalFloor = mapContext.bFinalFloor == true
local bCanReroll, nRerollPrice = _CanShopReroll(mapPlan, mapContext, mapRoll, nCurCoin)
if bCanReroll then
return _MakeSuccess(StarTowerAutoDecision.ActionType.ShopReroll, {
bReRoll = true,
bNeedToken = false,
sReason = "ShopReroll",
tbDebug = {nCoin = nCurCoin, nRerollPrice = nRerollPrice}
})
end
if bFinalFloor and mapPlan.bShopSpendAllWhenFinalNoTarget == true then
local mapDecision = _DecideUpgradeBeforeShopSpendAll(mapContext, mapContext, nCurCoin)
if mapDecision ~= nil then
return mapDecision
end
mapBest.nScore = FALLBACK_SCORE
_AddReason(mapBest, "FinalFloorSpendAll")
else
return _MakeFail(StarTowerAutoDecision.FailReason.NoOption, "NoMatchedShopGoods", {bFinalFloor = bFinalFloor, nCoin = nCurCoin})
end
end
return _AttachTokenCheck(mapContext, _MakeSuccess(StarTowerAutoDecision.ActionType.ShopBuy, {
nTargetIndex = mapBest.nTargetIndex,
nTargetId = mapBest.nTargetId,
nGoodsId = mapBest.nGoodsId,
bNeedToken = true,
sReason = mapBest.tbReasons[1] or "ShopBuy",
tbDebug = {
nScore = mapBest.nScore,
nPrice = mapBest.nPrice,
tbReasons = mapBest.tbReasons,
mapRawData = mapBest.mapRawData
}
}))
end
local _BuildOptionTags = function(mapContext, nOptionId, mapOptionCfg)
local mapTags = {}
if _IsTable(mapContext.mapEventOptionTags) and _IsTable(mapContext.mapEventOptionTags[nOptionId]) then
for key, value in pairs(mapContext.mapEventOptionTags[nOptionId]) do
if value == true then
mapTags[key] = true
else
mapTags[value] = true
end
end
return mapTags
end
if mapOptionCfg == nil then
mapOptionCfg = _GetEventOptionCfg(nOptionId)
end
if mapOptionCfg == nil then
return mapTags
end
local tbTags = mapOptionCfg.OptionTag or mapOptionCfg.OptionTags or mapOptionCfg.EventOptionTag
if type(tbTags) == "string" then
local tbJsonTags = decodeJson(tbTags)
if tbJsonTags ~= nil then
tbTags = tbJsonTags
end
end
if type(tbTags) == "number" or type(tbTags) == "string" then
mapTags[tbTags] = true
elseif _IsTable(tbTags) then
for _, tag in ipairs(tbTags) do
mapTags[tag] = true
end
end
return mapTags
end
local _HasTag = function(mapTags, tag)
return _IsTable(mapTags) and mapTags[tag] == true
end
local _GetEventOptionTag = function(sTagName)
if GameEnum == nil or GameEnum.EventOptionTag == nil then
return nil
end
return GameEnum.EventOptionTag[sTagName]
end
local _HasEventOptionTag = function(mapTags, sTagName)
local nTag = _GetEventOptionTag(sTagName)
return nTag ~= nil and _HasTag(mapTags, nTag)
end
local _HasNeededNoteTag = function(mapContext, mapTags)
if not _IsTable(mapContext.mapNeedNote) then
return false
end
for nNoteId, bNeed in pairs(mapContext.mapNeedNote) do
if bNeed == true and _HasEventOptionTag(mapTags, "Note" .. tostring(nNoteId)) then
return true
end
end
return false
end
function StarTowerAutoDecision:DecideEvent(mapContext, tbOption, tbUnabledOption, nEventId)
if not self:IsEnabled(mapContext) then
return _MakeFail(StarTowerAutoDecision.FailReason.Disabled, "AutoDisabled")
end
if _IsArrayEmpty(tbOption) then
return _MakeFail(StarTowerAutoDecision.FailReason.NoOption, "NoEventOption")
end
local mapPlan = _GetPlan(mapContext)
local mapPriority = _BuildPriorityMap(mapPlan.tbEventRulePriority, false)
if next(mapPriority) == nil then
mapPriority = _BuildPriorityMap(mapPlan.mapEventRulePriority, true)
end
local mapDisabled = _BuildSet(tbUnabledOption, false)
local nTendency = mapPlan.nEventTendency or StarTowerAutoDecision.EventTendency.Standard
local mapTendencyTagName = {
[StarTowerAutoDecision.EventTendency.Stable] = "Preservative",
[StarTowerAutoDecision.EventTendency.Standard] = "Standard",
[StarTowerAutoDecision.EventTendency.Aggressive] = "Aggresive"
}
local tbCandidates = {}
for nIndex, nOptionId in ipairs(tbOption) do
if mapDisabled[nIndex - 1] ~= true then
local mapOptionCfg = _GetEventOptionCfg(nOptionId)
local mapTags = _BuildOptionTags(mapContext, nOptionId, mapOptionCfg)
local mapCandidate = {
nIndex = nIndex,
nTargetIndex = nIndex,
nTargetId = nOptionId,
nBaseScore = 0,
nPriorityScore = 0,
nScore = 0,
tbReasons = {},
mapTags = mapTags,
bNeedToken = true
}
if _HasEventOptionTag(mapTags, "Correct") then
_AddCandidateBaseScore(mapCandidate, CORRECT_EVENT_OPTION_SCORE, "CorrectAnswer")
end
local sTendencyTagName = mapTendencyTagName[nTendency]
if sTendencyTagName ~= nil and _HasEventOptionTag(mapTags, sTendencyTagName) then
_AddCandidateBaseScore(mapCandidate, TENDENCY_SCORE, "EventTendency")
end
local tbRuleToTagName = {
[StarTowerAutoDecision.EventRule.MainPotential] = "PotentialMain",
[StarTowerAutoDecision.EventRule.AssistPotential] = "PotentialAssist",
[StarTowerAutoDecision.EventRule.Coin] = "Coin",
[StarTowerAutoDecision.EventRule.RarePotential] = "PotentialRare",
[StarTowerAutoDecision.EventRule.NormalPotential] = "PotentialCommon",
[StarTowerAutoDecision.EventRule.RandomNote] = "NoteRandom"
}
if _HasNeededNoteTag(mapContext, mapTags) then
_AddCandidatePriorityScore(mapCandidate, mapPriority, StarTowerAutoDecision.EventRule.NeedNote, "NeedNote")
end
for nRule, sTagName in pairs(tbRuleToTagName) do
if _HasEventOptionTag(mapTags, sTagName) then
_AddCandidatePriorityScore(mapCandidate, mapPriority, nRule, "EventRule" .. tostring(nRule))
end
end
table.insert(tbCandidates, mapCandidate)
end
end
if _IsArrayEmpty(tbCandidates) then
return _MakeFail(StarTowerAutoDecision.FailReason.NoMatchedTarget, "NoEnabledEventOption", {nEventId = nEventId})
end
_SortCandidates(tbCandidates)
local mapBest = tbCandidates[1]
if 0 >= mapBest.nScore then
mapBest.nScore = FALLBACK_SCORE
_AddReason(mapBest, "FallbackFirst")
end
return _AttachTokenCheck(mapContext, _MakeSuccess(StarTowerAutoDecision.ActionType.EventSelect, {
nTargetIndex = mapBest.nTargetIndex,
nTargetId = mapBest.nTargetId,
bNeedToken = mapBest.bNeedToken,
sReason = mapBest.tbReasons[1] or "EventSelect",
tbDebug = {
nEventId = nEventId,
nScore = mapBest.nScore,
tbReasons = mapBest.tbReasons,
mapTags = mapBest.mapTags
}
}))
end
function StarTowerAutoDecision:CanConsumeToken(mapContext, nActionType)
local nCost = _GetTokenCost(mapContext, nActionType)
if not _IsTable(mapContext) or mapContext.nToken == nil then
return true, nCost
end
return nCost <= _ToNumber(mapContext.nToken, 0), nCost
end
return StarTowerAutoDecision