pacov

Untitled

Jun 19th, 2011
191
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # [MOD] Increased item comparison for selling to fixed amount to prevent sell loops.
  2. # [MOD] Announce Citadel Upgrades
  3. # [MOD] TE All WaitTicks set to 10 = 1 second
  4.  
  5. local ValidateAbility = import('/lua/common/ValidateAbility.lua')
  6. local ValidateInventory = import('/lua/common/ValidateInventory.lua')
  7. local ValidateShop = import('/lua/common/ValidateShop.lua')
  8. local CanPickItem = import('/lua/common/ValidateShop.lua').CanPickItem
  9. local AIChatGlobals = import('/lua/sim/AI/AIChatGlobals.lua').AIChat
  10. local Buff = import('/lua/sim/Buff.lua')
  11. local Common = import('/lua/common/CommonUtils.lua')
  12. local ValidateUpgrade = import('/lua/common/ValidateUpgrade.lua')
  13. local Upgrades = import('/lua/common/CitadelUpgrades.lua').Upgrades
  14.  
  15. local AIUtils = import('/lua/sim/ai/aiutilities.lua')
  16.  
  17. local AIGlobals = import('/lua/sim/ai/AIGlobals.lua')
  18.  
  19. local ITEM_RESELL_MULTIPLIER = import('/lua/game.lua').GameData.SellMult
  20. local ShopDistance = import('/lua/game.lua').GameData.ShopDistance
  21.  
  22.  
  23. # ===== Shop functions ===== #
  24. function ShopCleanup(unit, action)
  25. if unit.Sync.ShopId then
  26. # Leave the shop
  27. commandData = {
  28. TaskName = 'EndShopTask',
  29. }
  30. IssueScript( {unit}, commandData )
  31.  
  32. WaitTicks(6)
  33. if unit:IsDead() then
  34. return false
  35. end
  36. end
  37.  
  38. return true
  39. end
  40.  
  41. function MoveToShop( unit, shop, aiBrain )
  42. local path = AIUtils.GetSafePathBetweenPoints(aiBrain, unit.Position, shop.Position)
  43. local cmd = false
  44. if not path then
  45. return IssueMove( {unit}, shop.Position )
  46. end
  47.  
  48. for k,v in path do
  49. cmd = IssueMove( {unit}, v )
  50. end
  51. return cmd
  52. end
  53.  
  54. # Set up the sell routine
  55. function SellItemAction( unit, action )
  56. local actionBp = HeroAIActionTemplates[action.ActionName]
  57. local item, itemPri = FindLowestInventoryItem( unit, action, actionBp.InventoryType )
  58.  
  59. if not item then
  60. return false
  61. end
  62.  
  63. local itemType = item.Sync.Name
  64.  
  65. local shopPos = false
  66. if unit.ShopInformation.BuyItem['Purchase Base Item - Use Sell Refund'] and unit.ShopInformation.BuyItem['Purchase Base Item - Use Sell Refund'].PurchaseShopPosition then
  67. shopPos = unit.ShopInformation.BuyItem['Purchase Base Item - Use Sell Refund'].PurchaseShopPosition
  68. else
  69. shopPos = unit.Position
  70. end
  71. local shop = unit:GetAIBrain().GoalPlanner:GetClosestFriendlyShop(shopPos)
  72.  
  73. if SellItem( unit, itemType, shop ) == 'Stuck' then
  74. action:LockAction( 2 )
  75. end
  76.  
  77. #if unit:GetArmy() == 1 then
  78. # WARN('*AI SHOP DEBUG: Brain= ' .. unit:GetArmy() .. ' - Selling item= ' .. itemType .. ' - Priority= ' .. itemPri)
  79. #end
  80.  
  81. ShopCleanup(unit, action)
  82. end
  83.  
  84. # If we have an item in the slot, we can sell it
  85. function SellItemStatus( unit, action )
  86. local aiBrain = unit:GetAIBrain()
  87.  
  88. if not unit.ShopInformation.SellItem[action.ActionName] then
  89. unit.ShopInformation.SellItem[action.ActionName] = {}
  90. end
  91. local actionInformation = unit.ShopInformation.SellItem[action.ActionName]
  92. actionInformation.SellItem = false
  93. actionInformation.SellItemPriority = false
  94. actionInformation.SellItemRefund = false
  95. actionInformation.SellItemInventoryType = false
  96.  
  97. local actionBp = HeroAIActionTemplates[action.ActionName]
  98. local item,priority,shopType = FindLowestInventoryItem( unit, action, actionBp.InventoryType )
  99. if not item or not priority or not shopType then
  100. return false
  101. end
  102.  
  103. local nearby = aiBrain.GoalPlanner:GetFriendlyShop(shopType, unit:GetPosition())
  104. if not nearby then
  105. return false
  106. end
  107.  
  108. actionInformation.SellItemRefund = math.floor(item.Sync.PurchasePrice * ITEM_RESELL_MULTIPLIER )
  109. actionInformation.SellItem = item.Sync.Name
  110. actionInformation.SellItemPriority = priority
  111. actionInformation.SellItemInventoryType = actionBp.InventoryType
  112.  
  113. #if (unit:GetArmy() == 1) then
  114. # LOG('*AI SHOP DEBUG: SellItemStatus - Item= ' .. item.Sync.Name .. ' - ItemPriority = ' .. priority)
  115. #end
  116.  
  117. return true
  118. end
  119.  
  120. # Get weight of selling this item
  121. function SellItemWeights(action, aiBrain, agent, initialAgent)
  122. if not agent.WorldStateData.CanMove then
  123. return false
  124. end
  125.  
  126. local actionInformation = initialAgent.ShopInformation.SellItem[action.ActionName]
  127. if not actionInformation or not actionInformation.SellItem then
  128. return false
  129. end
  130.  
  131. agent.WorldStateData.ItemSold = true
  132. agent.WorldStateData.ItemSoldPriority = actionInformation.SellItemPriority
  133. agent.WorldStateConsistent = false
  134.  
  135. agent.Gold = agent.Gold + actionInformation.SellItemRefund
  136. agent.InventoryData[actionInformation.SellItemInventoryType] = agent.InventoryData[actionInformation.SellItemInventoryType] + 1
  137.  
  138. return { PurchaseItems = ( actionInformation.SellItemPriority ), }, 0
  139. end
  140.  
  141. # Moves to shop, sells item, closes shop
  142. function SellItem( unit, item, shop )
  143. if unit.Sync.ShopId and not ShopCleanup(unit) then
  144. return false
  145. end
  146.  
  147. local aiBrain = unit:GetAIBrain()
  148. if VDist3XZSq( unit.Position, shop.Position ) > 200 then
  149. local cmd = MoveToShop( unit, shop, aiBrain )
  150. while VDist3XZSq( unit.Position, shop.Position ) > 200 do
  151. WaitTicks(6)
  152.  
  153. if unit:IsDead() or shop:IsDead() then
  154. return false
  155. end
  156. end
  157. end
  158.  
  159. # Go to the shop
  160. local commandData = {
  161. TaskName = 'BeginShopTask',
  162. TargetId = shop:GetEntityId(),
  163. }
  164. local cmd = IssueScript( {unit}, commandData )
  165.  
  166. local stuckCount = 0
  167. local oldPos = table.copy(unit.Position)
  168. if unit:IsDead() or shop:IsDead() then
  169. return
  170. end
  171.  
  172. while shop and not shop:IsDead() and shop.CheckShopper and not shop:CheckShopper(unit) do
  173. WaitTicks(6)
  174.  
  175. if unit:IsDead() or shop:IsDead() then
  176. return
  177. end
  178.  
  179. local newPos = unit:GetPosition()
  180. if newPos[1] == oldPos[1] and newPos[2] == oldPos[2] and newPos[3] == oldPos[3] then
  181. stuckCount = stuckCount + 1
  182. else
  183. stuckCount = 0
  184. end
  185.  
  186. if stuckCount >= 10 then
  187. LOG('*AI DEBUG: Unit Stuck getting to shop - Sell Item')
  188. return 'Stuck'
  189. end
  190.  
  191. oldPos = table.copy(newPos)
  192. end
  193.  
  194. if unit:IsDead() or shop:IsDead() or not shop.CheckShopper or not shop:CheckShopper(unit) then
  195. return false
  196. end
  197.  
  198. local itemEntity = unit.Inventory[ Items[item].InventoryType ]:FindItem( item )
  199.  
  200. if not itemEntity then
  201. return false
  202. end
  203.  
  204. # Sell the item
  205. CODE_SellItem(unit, itemEntity:GetEntityId())
  206. WaitTicks(6)
  207. #WARN( LOC(unit:GetAIBrain().Nickname) .. ' Sold: ' .. LOC(Items[item].DisplayName))
  208.  
  209.  
  210. return true
  211. end
  212.  
  213.  
  214. # ==== Item purchasing functions ==== #
  215.  
  216. # == Action CalculateWeights Function == #
  217. function PurchaseItemCalculateWeights( action, aiBrain, agent, initialAgent )
  218.  
  219.  
  220.  
  221.  
  222. if not agent.WorldStateData.CanMove then
  223. return false
  224. end
  225.  
  226. local actionInformation = initialAgent.ShopInformation.BuyItem[action.ActionName]
  227.  
  228. if agent.InventoryData[actionInformation.PurchaseItemInventoryType] <= 0 then
  229. return false
  230. end
  231.  
  232. local actionBp = HeroAIActionTemplates[action.ActionName]
  233. if actionBp.UseSellRefund and not agent.WorldStateData.ItemSold then
  234. return false
  235. end
  236.  
  237. local actionBp = HeroAIActionTemplates[action.ActionName]
  238. if not actionBp.UseSellRefund and agent.WorldStateData.ItemSold then
  239. return false
  240. end
  241.  
  242. if not actionInformation or not actionInformation.PurchaseItem then
  243. return false
  244. end
  245.  
  246. if actionInformation.PurchaseItemCost > agent.Gold then
  247. return false
  248. end
  249.  
  250. #if actionBp.UseSellRefund then
  251. # if agent.WorldStateData.ItemSoldPriority >= actionInformation.PurchaseItemPriority then
  252. # return false
  253. # end
  254. #end
  255.  
  256. local shopPos = actionInformation.PurchaseShopPosition
  257. if agent.Gold - actionInformation.PurchaseItemCost < 0 then
  258. return false
  259. end
  260. agent.Gold = agent.Gold - actionInformation.PurchaseItemCost
  261.  
  262. agent.InventoryData[actionInformation.PurchaseItemInventoryType] = agent.InventoryData[actionInformation.PurchaseItemInventoryType] - 1
  263.  
  264. if not agent.AgentHasMoved then
  265. distance = initialAgent.GOAP.BrainAsset:GetDistance( actionInformation.PurchaseShopType, 'Ally' )
  266. else
  267. distance = VDist3XZ( agent.Position, shopPos )
  268. end
  269.  
  270. if not distance then
  271. return false
  272. end
  273.  
  274. agent:SetPosition( shopPos )
  275.  
  276. return { PurchaseItems = ( actionInformation.PurchaseItemPriority * -1 ), }, math.max( distance / agent.Speed, 2 )
  277. end
  278.  
  279. # == StatusTrigger for buying items == #
  280. function PurchaseItemStatus(unit, action)
  281. local aiBrain = unit:GetAIBrain()
  282. local actionBp = HeroAIActionTemplates[action.ActionName]
  283. unit.ShopInformation.CanBuyItem[action.ActionName] = false
  284.  
  285. if not unit.ShopInformation.BuyItem[action.ActionName] then
  286. unit.ShopInformation.BuyItem[action.ActionName] = false
  287. end
  288. local actionInformation = {}
  289.  
  290. actionInformation.PurchaseItem = false
  291. actionInformation.PurchaseItemPriority = false
  292. actionInformation.PurchaseShopPosition = false
  293. actionInformation.PurchaseItemCost = false
  294. actionInformation.PurchaseItemInventoryType = false
  295. actionInformation.PurchaseItemBaseShopType = false
  296.  
  297. unit.ShopInformation.BuyItem[action.ActionName] = actionInformation
  298.  
  299. local sellItemData = {}
  300. if actionBp.UseSellRefund then
  301. for k,v in unit.ShopInformation.SellItem do
  302. if not v.SellItem then
  303. continue
  304. end
  305.  
  306. sellItemData[v.SellItemInventoryType] = v
  307. end
  308. end
  309.  
  310.  
  311.  
  312. if actionBp.UseSellRefund then
  313. if table.empty(sellItemData) then
  314. return false
  315. end
  316. end
  317.  
  318. local bestItem = FindBestBaseItem( unit, aiBrain, action, sellItemData )
  319. if not bestItem then
  320. return false
  321. end
  322. # [MOD] TE Citadel Check
  323.  
  324. local bestCitadel = FindBestCitadelUpgrade( unit, aiBrain, action)
  325. if bestCitadel and bestCitadel.ItemPriority > bestItem.ItemPriority then
  326. return false
  327. end
  328.  
  329. local nearby = bestItem.Shop
  330. if not nearby then
  331. return false
  332. end
  333.  
  334.  
  335.  
  336.  
  337. # TODO: VALIDATE IF WE CAN BUY MORE OF A STACKED ITEM HERE
  338. if ValidateInventory.NumFreeSlots( unit.Inventory[bestItem.InventoryType] ) <= 0 and not actionBp.UseSellRefund then
  339. return false
  340. end
  341.  
  342. actionInformation.PurchaseItem = bestItem.ItemName
  343. actionInformation.PurchaseItemPriority = bestItem.ItemPriority
  344. actionInformation.PurchaseShopType = bestItem.ShopType
  345. actionInformation.PurchaseItemBaseShopType = bestItem.BaseShopType
  346. actionInformation.PurchaseShopPosition = bestItem.Shop:GetPosition()
  347. actionInformation.PurchaseItemCost = bestItem.ItemCost
  348. actionInformation.PurchaseItemInventoryType = bestItem.InventoryType
  349.  
  350. unit.ShopInformation.BuyItem[action.ActionName] = actionInformation
  351. unit.ShopInformation.CanBuyItem[action.ActionName] = true
  352.  
  353. #if (unit:GetArmy() == 1) then
  354. # LOG('*AI SHOP DEBUG: ' .. action.ActionName .. 'Status - Item= ' .. bestItem.ItemName
  355. # .. ' - ItemPriority = ' .. bestItem.ItemPriority)
  356. #end
  357.  
  358.  
  359.  
  360. return true
  361. end
  362.  
  363. # == Action ActionFunction == #
  364. function PurchaseItemAction( unit, action )
  365. local aiBrain = unit:GetAIBrain()
  366.  
  367. local bestItem = FindBestBaseItem( unit, aiBrain, action )
  368.  
  369. if not bestItem then
  370. return
  371. end
  372.  
  373. #if (unit:GetArmy() == 1) then
  374. # WARN('*AI SHOP DEBUG: Army= ' .. unit:GetArmy() .. ' - Purchasing item= ' .. bestItem.ItemName
  375. # .. ' - Priority= ' .. bestItem.ItemPriority
  376. # .. ' - Quantity= ' .. bestItem.NumPurchase .. '\n\n' )
  377. #end
  378.  
  379. #WARN(aiBrain.Nickname.. ' Buying ' .. bestItem.ItemName)
  380. if PurchaseItem( unit, bestItem.ItemName, bestItem.Shop, bestItem.NumPurchase, bestItem.BaseShopType ) == 'Stuck' then
  381. action:LockAction( 2 )
  382. end
  383.  
  384. ShopCleanup(unit, action)
  385.  
  386. unit.GOAP:ForcePurchaseUpdate()
  387. end
  388.  
  389. # Purchase item - moves unit to shop, buys item, closes shop
  390. function PurchaseItem( unit, itemName, shop, quantity, baseShopType )
  391. local shopItemId = false
  392. local aiBrain = unit:GetAIBrain()
  393. local shopBp = false
  394. if baseShopType then
  395. shopBp = GetUnitBlueprintByName(baseShopType)
  396. shopItemId = FindShopItemId( itemName, nil, shopBp )
  397. else
  398. shopBp = shop:GetBlueprint()
  399. shopItemId = FindShopItemId( itemName, shop )
  400. end
  401.  
  402. quantity = quantity or 1
  403.  
  404. if not shopItemId then
  405. return false
  406. end
  407.  
  408.  
  409. # [MOD] TE Check idol priority against current idol
  410. local sellIdol = false
  411. local syncData = Common.GetSyncData(unit)
  412. local itemEntity
  413. local sellItem
  414. if syncData.Inventory.Generals then
  415. for slot, invData in syncData.Inventory.Generals.Slots do
  416. local invItemData = EntityData[invData[1]].Data # [MOD] TE Refrence .Data so item def can get blueprint.
  417. local invItemDef = Items[invItemData.BlueprintId]
  418. if invItemDef then
  419. local idolInSlot = string.sub(invItemDef.Name, 1, -5)
  420. local idolInShop = string.sub(shopItemId, 1, -5)
  421. if idolInSlot == idolInShop then
  422. sellIdol = true
  423. sellItem = invItemDef
  424. itemEntity = unit.Inventory[ Items[invItemDef.Name].InventoryType ]:FindItem( invItemDef.Name )
  425. #WARN(aiBrain.Nickname .. ' Sell Idol ' .. invItemDef.Name.. ' Type ' .. Items[invItemDef.Name].InventoryType .. ' , Entity - '.. tostring(itemEntity) )
  426. end
  427. end
  428. end
  429. end
  430.  
  431.  
  432. if not CanPickItem( unit, shopBp, shopItemId ) then
  433. return false
  434. end
  435.  
  436. if unit.Sync.ShopId and not ShopCleanup(unit) then
  437. return false
  438. end
  439.  
  440.  
  441. if VDist3XZSq( unit.Position, shop.Position ) > 200 then
  442. local cmd = MoveToShop( unit, shop, aiBrain )
  443. while VDist3XZSq( unit.Position, shop.Position ) > 200 do
  444. WaitTicks(6)
  445.  
  446. if unit:IsDead() or shop:IsDead() then
  447. return false
  448. end
  449. end
  450. end
  451.  
  452. # Go to the shop
  453. local commandData = {
  454. TaskName = 'BeginShopTask',
  455. TargetId = shop:GetEntityId(),
  456. }
  457. local cmd = IssueScript( {unit}, commandData )
  458.  
  459. local oldPos = table.copy( unit:GetPosition() )
  460. local stuckCount = 0
  461.  
  462. # Wait until we are in the shop
  463. while not shop:CheckShopper(unit) do
  464. WaitTicks(6)
  465.  
  466. if unit:IsDead() or shop:IsDead() then
  467. return
  468. end
  469.  
  470. local newPos = unit:GetPosition()
  471. if newPos[1] == oldPos[1] and newPos[2] == oldPos[2] and newPos[3] == oldPos[3] then
  472. stuckCount = stuckCount + 1
  473. else
  474. stuckCount = 0
  475. end
  476.  
  477. if stuckCount >= 10 then
  478. LOG('*AI DEBUG: Unit Stuck getting to shop - Sell Item')
  479. return 'Stuck'
  480. end
  481.  
  482. oldPos = table.copy(newPos)
  483. end
  484.  
  485.  
  486. if sellIdol then
  487. #WARN('Entity - ' .. tostring(itemEntity) )
  488. #WARN( LOC(unit:GetAIBrain().Nickname) .. ' Idol Sold: ' .. LOC(sellItem.DisplayName) )
  489. CODE_SellItem(unit, itemEntity:GetEntityId())
  490. WaitTicks(6)
  491. end
  492.  
  493. for i=1,quantity do
  494. # Purchase the item
  495. CODE_PurchaseItem(unit, shopItemId, shopBp.BlueprintId)
  496.  
  497. WaitTicks(6)
  498. end
  499.  
  500.  
  501. if shopBp.BlueprintId == 'ugbshop05' then
  502. local def = shopBp.Shop.Tree[shopItemId]
  503. if Items[def.ItemBP].DisplayName then
  504. WaitTicks(6)
  505. local announcement = "Artifact Purchased: "..LOC(Items[def.ItemBP].DisplayName)
  506. AIUtils.AIChat(unit, announcement)
  507.  
  508. end
  509. end
  510.  
  511. if shopBp.BlueprintId == 'ugbshop09' then
  512. local def = shopBp.Shop.Tree[shopItemId]
  513. if Items[def.ItemBP].DisplayName then
  514. WaitTicks(6)
  515. # -- local announcement = "Idol Purchased: "..LOC(Items[def.ItemBP].DisplayName)
  516. # -- AIUtils.AIChat(unit, announcement)
  517. #WARN( LOC(aiBrain.Nickname) .. ' Idol Purchased: ' .. LOC(Items[def.ItemBP].DisplayName))
  518. end
  519. end
  520.  
  521. return true
  522. end
  523.  
  524.  
  525.  
  526. # ==== Helper functions ==== #
  527.  
  528. # iterates through the shop's items and sees if there is an item found
  529. function FindShopItemId( itemName, shop, shopBp )
  530. if shop then
  531. shopBp = shop:GetBlueprint()
  532. end
  533.  
  534. if not shopBp then
  535. WARN('AI ERROR: Could not find a shop bp')
  536. return false
  537. end
  538.  
  539. for shopItemId,item in shopBp.Shop.Tree do
  540. if item.ItemBP == itemName then
  541. return shopItemId
  542. end
  543. end
  544.  
  545. return false
  546. end
  547.  
  548. function GetItemCount(unit, itemName)
  549. local numItems = 0
  550. for _,inv in unit.Inventory do
  551. local temp = inv:GetCount( itemName )
  552. if table.empty(temp) then
  553. continue
  554. end
  555.  
  556. for k,v in temp do
  557. numItems = numItems + v.Count
  558. end
  559. end
  560.  
  561. return numItems
  562. end
  563.  
  564. # Check if the unit already has the item or not
  565. function UnitHasItem( unit, itemName, baseShopType )
  566. local shopBp = GetUnitBlueprintByName(baseShopType)
  567.  
  568. local itemTree = Common.GetShopTreeByBlueprint(shopBp)
  569.  
  570. local shopItemId = FindShopItemId( itemName, nil, shopBp )
  571.  
  572. local itemData = itemTree[shopItemId]
  573.  
  574. if not itemData then
  575. WARN('*AI ERROR: No item data found for item - ' .. itemName )
  576. return false
  577. end
  578.  
  579. local syncData = Common.GetSyncData(unit)
  580. local counts = ValidateInventory.GetCount(syncData.Inventory[ Items[itemName].InventoryType ], itemData.ItemBP)
  581.  
  582. # We have none return out
  583. if table.empty(counts) then
  584. return false, 0
  585. end
  586.  
  587. # Find out how many we have
  588. local numHeld = 0
  589. for k,v in counts do
  590. numHeld = numHeld + v.Count
  591. end
  592.  
  593. if numHeld > 0 then
  594. return true, numHeld
  595. end
  596.  
  597. return false, 0
  598. end
  599.  
  600.  
  601. # Returns the best item that can be purchased by the agent
  602. # Returned Table = { ItemName, Shop, ItemCost, ItemPriority }
  603. function FindBestBaseItem( unit, aiBrain, action, sellItemData )
  604. local bestValue = 0
  605. local bestItems = {}
  606. local shops = {}
  607.  
  608. local asset = action.StrategicAsset
  609. if not asset or not asset.ItemPriorities then
  610. return false
  611. end
  612.  
  613. local inventoryOpen = {}
  614. for invName,inv in unit.Inventory do
  615. inventoryOpen[invName] = ValidateInventory.NumFreeSlots( inv )
  616. end
  617.  
  618. local highestPriority = false
  619.  
  620. local maxCost = false
  621.  
  622.  
  623. # -- if ScenarioInfo.Options.TournamentMode then
  624. # -- # LOG('*AI DEBUG: Setting max cost for tournament')
  625. # -- if aiBrain.Difficulty == 1 then
  626. # -- maxCost = 0
  627.  
  628. # -- elseif aiBrain.Difficulty == 2 then
  629. # -- maxCost = 2500
  630. # -- end
  631. # -- end
  632.  
  633. local shouldSave = AIGlobals.ShouldSave(unit, 'item')
  634.  
  635. local syncData = Common.GetSyncData(unit)
  636.  
  637. for _,itemData in asset.ItemPriorities do
  638.  
  639. # this number can increase for stackable items
  640. local purchaseQuantity = 1
  641. local currentPriority = 0
  642.  
  643. if itemData.Priority <= 0 then
  644. continue
  645. end
  646.  
  647.  
  648.  
  649. # If we have a highest priority and this next item won't be higher; break the loop
  650. #if highestPriority and (itemData.Priority * itemData.ItemTable.StacksPerSlot) < highestPriority then
  651. # continue
  652. #end
  653. if highestPriority and itemData.Priority < highestPriority then
  654. continue
  655. end
  656.  
  657. # Store out each shop only once; this way we don't have to find shops more like crazy
  658. if shops[itemData.ItemTable.ShopType] == nil then
  659. local shop = aiBrain.GoalPlanner:GetFriendlyShop(itemData.ItemTable.ShopType, unit:GetPosition())
  660. if shop then
  661. shops[itemData.ItemTable.ShopType] = shop
  662. else
  663. shops[itemData.ItemTable.ShopType] = false
  664. end
  665.  
  666. # all shops of this time seem dead, continue to next item
  667. if not shop then
  668. continue
  669. end
  670. end
  671.  
  672.  
  673.  
  674. local nearby = shops[itemData.ItemTable.ShopType]
  675. if not nearby then
  676. continue
  677. end
  678.  
  679.  
  680. # [MOD] TE Amount to subtract from AI Item shopping budget (to save for critical upgrades
  681. local gold = GetArmyBrain(unit:GetArmy()).mGold - shouldSave
  682.  
  683.  
  684.  
  685.  
  686. local hasItem, numHeld = UnitHasItem( unit, itemData.ItemTable.ItemId, itemData.ItemTable.BaseShopType )
  687. # Figure out if we can carry this item; We have two checks - one for non-stacked items and one for stacks
  688. if itemData.ItemTable.StacksPerSlot > 1 then
  689. if hasItem and numHeld >= itemData.ItemTable.StacksPerSlot then
  690. continue
  691. end
  692.  
  693. local maxPurchasable = itemData.ItemTable.StacksPerSlot - numHeld
  694. # Already at max; this item is bankrupt
  695. if maxPurchasable <= 0 then
  696. continue
  697. end
  698.  
  699. maxPurchasable = math.min( maxPurchasable, AIGlobals.ItemWeights[itemData.ItemTable.ItemId].MaxPurchase or 1 )
  700.  
  701. local maxAffordable = math.floor( gold / itemData.ItemTable.ItemCost )
  702. if maxAffordable <= 0 then
  703. continue
  704. end
  705.  
  706. currentPriority = itemData.Priority
  707.  
  708. #if maxAffordable > maxPurchasable then
  709. # maxAffordable = maxPurchasable
  710. #end
  711.  
  712.  
  713. # Make sure the maximum number of items we can buy is over the threshold for the best priority currently
  714. #currentPriority = maxAffordable * itemData.Priority
  715. #if currentPriority < highestPriority then
  716. # continue
  717. #end
  718.  
  719. # Make sure the new item is better than the old item
  720. if sellItemData[itemData.ItemTable.InventoryType] then
  721. # [MOD] Fixed amount added to sell priority to ensure new item is greater than 5 priority better. Prevents buy/sell loops
  722. if sellItemData[itemData.ItemTable.InventoryType].SellItemPriority + 5 > itemData.Priority then
  723. continue
  724. end
  725. end
  726.  
  727. #purchaseQuantity = maxAffordable
  728.  
  729. else
  730. # Handle non-stacked items here
  731. if hasItem then
  732. continue
  733. end
  734.  
  735. # 0.26.40 - removed the code that placed a limitation on what items could be purchased at the start of the game
  736. --[[ # [MOD] TE Buy more cheaper items at start
  737. if GetGameTimeSeconds() < 30 and gold < 5000 and itemData.ItemTable.ItemCost >= 1750 then
  738. continue
  739. elseif GetGameTimeSeconds() < 30 and gold < 35000 and itemData.ItemTable.ItemCost > 10000 then
  740. continue
  741. end
  742. --]]
  743.  
  744. # Get our sell refund and make sure the new item is better than the old item
  745. local addAmount = 0
  746. if sellItemData[itemData.ItemTable.InventoryType] then
  747. addAmount = sellItemData[itemData.ItemTable.InventoryType].SellItemRefund
  748.  
  749.  
  750. # [MOD] Fixed amount added to sell priority to ensure new item is greater than 5 priority better. Prevents buy/sell loops
  751. if sellItemData[itemData.ItemTable.InventoryType].SellItemPriority + 5 > itemData.Priority then
  752. continue
  753. end
  754. end
  755.  
  756. if gold + addAmount < itemData.ItemTable.ItemCost then
  757. continue
  758. end
  759.  
  760.  
  761. # [MOD] TE Check idol priority against current idol
  762. if syncData.Inventory.Generals then
  763. local buyItem = true
  764. for slot, invData in syncData.Inventory.Generals.Slots do
  765. local invItemData = EntityData[invData[1]].Data # [MOD] TE Refrence .Data so item def can get blueprint.
  766. local invItemDef = Items[invItemData.BlueprintId]
  767. if invItemDef then
  768. local idolInSlot = string.sub(invItemDef.Name, 1, -5)
  769. local idolInShop = string.sub(itemData.ItemTable.ItemId, 1, -5)
  770. if idolInSlot == idolInShop then
  771. local invPriority = 0
  772. for k,v in asset.ItemPriorities do
  773. if v.ItemTable.ItemId == invItemDef.Name then
  774. invPriority = v.Priority
  775. break
  776. end
  777.  
  778. end
  779.  
  780.  
  781. if invPriority + 5 > itemData.Priority then
  782. #WARN( LOC(aiBrain.Nickname) ..' Current: '..invItemDef.Name ..' - ' .. invPriority + 5 .. ' | vs | ' .. itemData.ItemTable.ItemId .. ' - ' .. itemData.Priority )
  783. buyItem = false
  784. else
  785. buyItem = true
  786. #WARN( LOC(aiBrain.Nickname) ..' Sell: '..invItemDef.Name ..' - ' .. invPriority .. ' | Buy: ' .. itemData.ItemTable.ItemId .. ' - ' .. itemData.Priority )
  787. end
  788. end
  789. end
  790. end
  791. if not buyItem then
  792. continue
  793. end
  794. end
  795.  
  796.  
  797. currentPriority = itemData.Priority
  798. end
  799.  
  800. # Make sure we'll have room for the item
  801. if inventoryOpen[itemData.ItemTable.InventoryType] <= 0 and not sellItemData[itemData.ItemTable.InventoryType] then
  802. continue
  803. end
  804.  
  805. #WARN('Items - ' .. itemData.ItemTable.ItemId .. ' - Inventory Type - ' .. inventoryOpen[itemData.ItemTable.InventoryType] )
  806.  
  807.  
  808. # Do NOT allow rebuying of the same item
  809. if sellItemData[itemData.ItemTable.InventoryType].SellItem == itemData.ItemTable.ItemId then
  810. continue
  811. end
  812.  
  813. highestPriority = currentPriority
  814.  
  815. #if (unit:GetArmy() == 1) then
  816. # WARN('*AI SHOP DEBUG: Find Best Item Army= ' .. unit:GetArmy() .. ' - Purchasing item= ' .. itemData.ItemTable.ItemId
  817. # .. ' - Priority= ' .. highestPriority
  818. # .. ' - Quantity= ' .. purchaseQuantity )
  819. #end
  820.  
  821. table.insert( bestItems, { ItemName = itemData.ItemTable.ItemId, ShopType = itemData.ItemTable.ShopType,
  822. Shop = nearby, ItemCost = itemData.ItemTable.ItemCost, ItemPriority = highestPriority, NumPurchase = purchaseQuantity,
  823. InventoryType = itemData.ItemTable.InventoryType, BaseShopType = itemData.ItemTable.BaseShopType } )
  824. end
  825.  
  826. if table.getn( bestItems ) > 0 then
  827. return bestItems[Random( 1, table.getn(bestItems) )]
  828. end
  829.  
  830. return false
  831. end
  832.  
  833. function FindLowestInventoryItem( unit, action, inventoryType )
  834. local lowestPriority = false
  835. local lowestItem = false
  836. local shopType = false
  837. local aiBrain = unit:GetAIBrain()
  838.  
  839. local asset = action.StrategicAsset
  840.  
  841. if not asset.ItemPriorities then
  842. return false
  843. end
  844.  
  845. # [MOD] Block selling Generals item (sale of old is handled on idol purchase)
  846. if inventoryType == 'Generals' then
  847. #WARN('Generals Item')
  848. return false
  849. end
  850.  
  851. if ValidateInventory.NumFreeSlots(unit.Inventory[inventoryType]) > 0 then
  852. return false
  853. end
  854.  
  855. local inv = unit.Inventory[inventoryType]
  856. for i=1,8 do
  857. local itemId = inv:GetItemFromSlot( i )
  858. if not itemId then
  859. continue
  860. end
  861.  
  862. local item = GetEntityById( itemId )
  863. if not item then
  864. continue
  865. end
  866.  
  867.  
  868.  
  869.  
  870.  
  871.  
  872. # [MOD] TE Don't Sell Portals and capture locks
  873. if item.Sync.Name == 'Item_Consumable_010' or item.Sync.Name == 'Item_Consumable_030' then
  874. #WARN( LOC(aiBrain.Nickname) .. 'Skipped TP/Capture Lock ' .. item.Sync.Name )
  875. continue
  876. end
  877.  
  878. local numItems = 0
  879. for k,v in inv:GetCount( item.Sync.Name ) do
  880. numItems = numItems + v.Count
  881. end
  882.  
  883. # Do not sell a stack of items
  884. if numItems > 1 then
  885. continue
  886. end
  887.  
  888.  
  889. local itemType = item.Sync.Name
  890. local tempPriority, tempShop
  891. for k,v in asset.ItemPriorities do
  892. if v.ItemTable.ItemId == itemType then
  893. if v.SellPriority > 0 then
  894. tempPriority = v.SellPriority
  895. tempShop = v.ItemTable.ShopType
  896. LOG('*AI DEBUG: Using SellPriority for item - ' .. v.ItemTable.ItemId)
  897. elseif v.Priority > 0 then
  898. tempPriority = v.Priority
  899. tempShop = v.ItemTable.ShopType
  900. end
  901. break
  902. end
  903. end
  904.  
  905.  
  906.  
  907. #WARN('Inventory Priority - ' .. tostring(item.Blueprint.SubInventoryType) )
  908.  
  909. if tempPriority and ( not lowestPriority or tempPriority < lowestPriority ) then # [MOD] TE Don't sell idols
  910. lowestPriority = tempPriority
  911. lowestItem = item
  912. shopType = tempShop
  913. end
  914. end
  915.  
  916. # -- local myString = ' '
  917. # -- for kk, vv in lowestItem.Blueprint do
  918. # -- myString = myString .. 'column: ' .. kk .. ' value: ' .. tostring(vv) .. ' || '
  919. # -- end
  920. # -- WARN('Inventory Priority - ' .. myString )
  921. return lowestItem, lowestPriority, shopType
  922. end
  923.  
  924. function GetItemDesires(itemId)
  925. if not ScenarioInfo.AIItemsList then
  926. return false
  927. end
  928.  
  929. for k,itemTable in ScenarioInfo.AIItemsList do
  930. if itemTable.ItemId != itemId then
  931. continue
  932. end
  933.  
  934. return {
  935. HealthDesire = itemTable.ItemWeights.HealthDesire,
  936. ManaDesire = itemTable.ItemWeights.ManaDesire,
  937. PrimaryWeaponDesire = itemTable.ItemWeights.PrimaryWeaponDesire,
  938. MinionDesire = itemTable.ItemWeights.MinionDesire,
  939. SpeedDesire = itemTable.ItemWeights.SpeedDesire,
  940. }
  941. end
  942. end
  943.  
  944. function GetPriorityFromCost(cost)
  945. if cost < 1250 then
  946. return 5
  947. elseif cost < 2000 then
  948. return 15
  949. elseif cost < 4000 then
  950. return 30
  951. elseif cost < 7500 then
  952. return 50
  953. elseif cost < 11000 then
  954. return 100
  955. elseif cost < 17500 then
  956. return 150
  957. else
  958. return 200
  959. end
  960. end
  961.  
  962. function GetItemsList()
  963. # We've already built the list once; return the list - it should NOT change mid game
  964. if ScenarioInfo.AIItemsList then
  965. return ScenarioInfo.AIItemsList
  966. end
  967.  
  968. ScenarioInfo.AIItemsList = {}
  969.  
  970. # Get all the shops
  971. local shops = {}
  972. for _,brain in ArmyBrains do
  973. shops = table.append( shops, brain:GetListOfUnits( categories.SHOP, false, false ) )
  974. end
  975. local shopIds = {
  976. Boots = 'ugbshop10', # [MOD] TE Fixed to correct boot shop ugbshop01 -> ugbshop10
  977. Breastplates = 'ugbshop02',
  978. Gloves = 'ugbshop03',
  979. Helms = 'ugbshop04',
  980. Artifacts = 'ugbshop05',
  981. Rings = 'ugbshop06',
  982. Consumables = 'ugbshop07',
  983. Generals = 'ugbshop09',
  984. }
  985.  
  986. # Iterate through the shops
  987. for shopType,shopId in shopIds do
  988.  
  989. local shopBp = __blueprints[shopId]
  990.  
  991. # Get the shop tree which has the costs and ItemBPs for all the items
  992. local shopTree = Common.GetShopTreeByBlueprint(shopBp)
  993.  
  994. # Go through all the items in the shop tree
  995. for shopItemId,itemData in shopTree do
  996. if not Items[itemData.ItemBP] then
  997. continue
  998. end
  999.  
  1000. # Blank template used by all items
  1001. local itemTable = {
  1002. ItemWeights = {
  1003. HealthDesire = 0,
  1004. ManaDesire = 0,
  1005. PrimaryWeaponDesire = 0,
  1006. MinionDesire = 0,
  1007. SpeedDesire = 0,
  1008.  
  1009. Priority = 0,
  1010. SellPriority = -1,
  1011. },
  1012. }
  1013. if not AIGlobals.ItemWeights[itemData.ItemBP].Priority and not AIGlobals.ItemWeights[itemData.ItemBP].PriorityFunction then
  1014. WARN('*AI DEBUG: No priority set for item = ' .. itemData.ItemBP)
  1015. WARN('*AI DEBUG: Item Cost = ' .. itemData.Cost)
  1016. WARN('*AI DEBUG: ItemData = ' .. repr(Items[itemData.ItemBP]) )
  1017. LOG('*AI DEBUG: ======================')
  1018. end
  1019.  
  1020. if AIGlobals.ItemWeights[itemData.ItemBP].Priority == -1 then
  1021. itemTable.ItemWeights.Priority = GetPriorityFromCost(itemData.Cost)
  1022. end
  1023.  
  1024. if AIGlobals.ItemWeights[itemData.ItemBp].SellPriority then
  1025. itemTable.ItemWeights.SellPriority = AIGlobals.ItemWeights[itemData.ItemBp].SellPriority
  1026. end
  1027.  
  1028. # Store out some basic info we'll use when trying to shop
  1029. itemTable.ShopItemId = shopItemId
  1030.  
  1031. if (shopId == 'ugbshop05') then
  1032. itemTable.ShopType = shopId
  1033. else
  1034. itemTable.ShopType = 'ugbshop01'
  1035. end
  1036. itemTable.BaseShopType = shopId
  1037. itemTable.ItemCost = itemData.Cost
  1038. itemTable.ItemId = itemData.ItemBP
  1039.  
  1040. # Figure out the weight for each item sold at this shop
  1041. local itemBp = Items[itemData.ItemBP]
  1042. itemTable.SlotLimit = itemBp.SlotLimit
  1043. itemTable.StacksPerSlot = itemBp.StacksPerSlot or 1
  1044. itemTable.InventoryType = itemBp.InventoryType
  1045. itemTable.GeneralItem = AIGlobals.ItemWeights[itemTable.ItemId]['GeneralItem'] or false
  1046. itemTable.AssassinItem = AIGlobals.ItemWeights[itemTable.ItemId]['AssassinItem'] or false
  1047.  
  1048.  
  1049. # Go through all the abilities on the item and figure out what the ability does for demigods
  1050. for _,abilityName in itemBp.Abilities do
  1051. if not Ability[abilityName] then
  1052. ERROR('*AI ERROR: Could not find ability information for Ability - ' .. abilityName)
  1053. end
  1054.  
  1055. # make sure we are taking into account the item even if the ability is a clicky
  1056. #if Ability[abilityName].AbilityType != 'Quiet' and not AIGlobals.ItemWeights[itemTable.ItemId] then
  1057. #LOG('*AI DEBUG: Click Ability = ' .. abilityName .. ' - Data = ' .. repr(Ability[abilityName]) )
  1058. #end
  1059.  
  1060. # TODO: Intelligently balance desire for procs & crits
  1061. if Ability[abilityName].AbilityType == 'WeaponProc' then
  1062. itemTable.ItemWeights.PrimaryWeaponDesire = itemTable.ItemWeights.PrimaryWeaponDesire + 0.1
  1063. elseif Ability[abilityName].AbilityType == 'ArmorProc' then
  1064. itemTable.ItemWeights.HealthDesire = itemTable.ItemWeights.HealthDesire + 0.1
  1065. elseif Ability[abilityName].AbilityType == 'WeaponCrit' then
  1066. itemTable.ItemWeights.PrimaryWeaponDesire = itemTable.ItemWeights.PrimaryWeaponDesire + 0.1
  1067. end
  1068.  
  1069. if Ability[abilityName].Buffs then
  1070. for _,buffName in Ability[abilityName].Buffs do
  1071. local buffWeights = GetBuffWeights( buffName )
  1072.  
  1073. for k,v in buffWeights do
  1074. itemTable.ItemWeights[k] = v + itemTable.ItemWeights[k]
  1075. end
  1076. end
  1077. end
  1078. end
  1079.  
  1080.  
  1081. # Get item default weighting
  1082. if AIGlobals.ItemWeights[itemTable.ItemId] then
  1083. for weightName,weightValue in AIGlobals.ItemWeights[itemTable.ItemId] do
  1084. if weightName == 'MaxPurchase' or weightName == 'GeneralItem' or weightName == 'AssassinItem' then
  1085. continue
  1086. end
  1087.  
  1088. if (weightName == 'Priority' and weightValue == -1) or weightName == 'PriorityFunction' or weightName == 'SellPriority' then
  1089. continue
  1090. end
  1091.  
  1092. itemTable.ItemWeights[weightName] = itemTable.ItemWeights[weightName] + weightValue
  1093. end
  1094. end
  1095.  
  1096. # Uncomment to see the default weights for items
  1097. #LOG('*AI DEBUG: Item = ' .. itemTable.ItemId .. ' - WeightsTable = ' .. repr(itemTable.ItemWeights) )
  1098.  
  1099. # Store the cost and weight for this item in the global items list
  1100. table.insert( ScenarioInfo.AIItemsList, itemTable )
  1101. end
  1102. end
  1103.  
  1104. # return the global items list
  1105. return ScenarioInfo.AIItemsList
  1106. end
  1107.  
  1108. function GetBuffWeights(buffName)
  1109. local buffData = Buffs[buffName]
  1110.  
  1111. # We currently are NOT dealing with items that have variable durations
  1112. if buffData.Duration > -1 then
  1113. return {}
  1114. end
  1115.  
  1116. local returnTable = {
  1117. HealthDesire = 0,
  1118. ManaDesire = 0,
  1119. PrimaryWeaponDesire = 0,
  1120. MinionDesire = 0,
  1121. SpeedDesire = 0,
  1122. }
  1123.  
  1124. if not buffData.Affects then
  1125. WARN('*AI WARNING: Item with buff - ' .. buffName .. ' - Does not have an Affects block')
  1126. return returnTable
  1127. end
  1128.  
  1129. for aName,aData in buffData.Affects do
  1130. # LOG('*AI DEBUG: BuffAffects = ' .. repr(buffData.Affects))
  1131.  
  1132. # Damage Rating
  1133. if aName == 'DamageRating' then
  1134. local addAmount = aData.Add / 100
  1135. returnTable.PrimaryWeaponDesire = returnTable.PrimaryWeaponDesire + addAmount
  1136.  
  1137. # Health
  1138. elseif aName == 'Regen' then
  1139. local addAmount = ((aData.Add or 0) / 75 ) + ((aData.Mult or 0) / 5)
  1140. returnTable.HealthDesire = returnTable.HealthDesire + addAmount
  1141.  
  1142. elseif aName == 'MaxHealth' then
  1143. local addAmount = aData.Add / 4000
  1144. returnTable.HealthDesire = returnTable.HealthDesire + addAmount
  1145.  
  1146. # Energy
  1147. elseif aName == 'MaxEnergy' then
  1148. local addAmount = ( aData.Add / 6000 )
  1149. returnTable.ManaDesire = returnTable.ManaDesire + addAmount
  1150.  
  1151. elseif aName == 'EnergyRegen' then
  1152. local addAmount = ( (aData.Add or 0) / 25 ) + ((aData.Mult or 0) )
  1153. returnTable.ManaDesire = returnTable.ManaDesire + addAmount
  1154.  
  1155. # Armor
  1156. elseif aName == 'Armor' then
  1157. local addAmount = ( aData.Add / 4000 )
  1158. returnTable.HealthDesire = returnTable.HealthDesire + addAmount
  1159.  
  1160. # Move Speed
  1161. elseif aName == 'MoveMult' then
  1162. local addAmount = ( (aData.Mult or 0) )
  1163. returnTable.SpeedDesire = returnTable.SpeedDesire + addAmount
  1164.  
  1165. # Lifesteal
  1166. elseif aName == 'LifeSteal' then
  1167. local addAmount = aData.Add
  1168. returnTable.PrimaryWeaponDesire = returnTable.PrimaryWeaponDesire + addAmount
  1169. returnTable.HealthDesire = returnTable.HealthDesire + ( addAmount / 2 )
  1170.  
  1171. # Damage return
  1172. elseif aName == 'DamageReturn' then
  1173. local addAmount = ( aData.Add / 100 )
  1174. returnTable.HealthDesire = returnTable.HealthDesire + addAmount
  1175.  
  1176. # Rate of Fire
  1177. elseif aName == 'RateOfFire' then
  1178. local addAmount = ((aData.Mult or 0) * 2)
  1179. returnTable.PrimaryWeaponDesire = returnTable.PrimaryWeaponDesire + addAmount
  1180.  
  1181. # Cooldown
  1182. elseif aName == 'Cooldown' then
  1183. local addAmount = aData.Mult * -1
  1184. returnTable.ManaDesire = returnTable.ManaDesire + addAmount
  1185.  
  1186. # Evasion
  1187. elseif aName == 'Evasion' then
  1188. local addAmount = ( aData.Add / 50 )
  1189. returnTable.HealthDesire = returnTable.HealthDesire + addAmount
  1190.  
  1191. # Damage Taken
  1192. elseif aName == 'DamageTakenMult' then
  1193. local addAmount = ( aData.Add * -1.5 )
  1194. returnTable.HealthDesire = returnTable.HealthDesire + addAmount
  1195.  
  1196. # Item cost reduction
  1197. elseif aName == 'ItemCost' then
  1198. local addAmount = ( aData.Add * -2 )
  1199. returnTable.SpeedDesire = returnTable.SpeedDesire + addAmount
  1200.  
  1201. # Item cost reduction
  1202. elseif aName == 'Experience' then
  1203. local addAmount = aData.Add
  1204. returnTable.SpeedDesire = returnTable.SpeedDesire + addAmount
  1205.  
  1206. elseif aName == 'Dummy' then
  1207. # Valid name for dummy buffs used for persistent effects on actors
  1208. else
  1209. # Find any we don't handle and report
  1210. LOG('*AI DEBUG: Unhandled buff data = ' .. aName .. ' - ' .. repr(buffData))
  1211. end
  1212. end
  1213.  
  1214. return returnTable
  1215. end
  1216.  
  1217. # ------------------------------------------------------------------------------
  1218. # Purchase Achievement Items
  1219. # ------------------------------------------------------------------------------
  1220. function PurchaseAchievementItem(unit)
  1221. local result = false
  1222. local bestItem = FindBestAchievementItem(unit)
  1223.  
  1224. if(bestItem) then
  1225. local itemName = bestItem.ItemName
  1226. local baseShopType = bestItem.BaseShopType
  1227. local shopItemId = false
  1228.  
  1229. local shopBp = false
  1230. if(baseShopType) then
  1231. shopBp = GetUnitBlueprintByName(baseShopType)
  1232. shopItemId = FindShopItemId(itemName, nil, shopBp)
  1233. end
  1234.  
  1235. if(shopItemId and CanPickItem(unit, shopBp, shopItemId, true)) then
  1236. # Purchase the item
  1237. CODE_PurchaseItem(unit, shopItemId, shopBp.BlueprintId)
  1238. result = true
  1239. #LOG('*AI DEBUG: Army ' .. unit.Army .. ' buying achievement item ' .. shopItemId)
  1240. end
  1241. end
  1242.  
  1243. return result
  1244. end
  1245.  
  1246. # Returns the best item that can be purchased
  1247. function FindBestAchievementItem(unit)
  1248. local result = false
  1249. local bestItem = {}
  1250.  
  1251. if(ValidateInventory.NumFreeSlots(unit.Inventory.Achievement) > 0) then
  1252. -- local difficulty = ScenarioInfo.ArmySetup[unit:GetAIBrain().Name].Difficulty
  1253. -- local maxPoints = 1125 #GameData.DifficultyHandicaps[difficulty].MaxPoints
  1254. -- local minPoints = 1125 #GameData.DifficultyHandicaps[difficulty].MinPoints
  1255.  
  1256. local isGeneral = EntityCategoryContains( categories.GENERAL, unit )
  1257. local isAssassin = EntityCategoryContains( categories.ASSASSIN, unit )
  1258.  
  1259. local itemList = GetAchievementItemsList()
  1260. local achievementItems = {}
  1261.  
  1262.  
  1263.  
  1264. # [MOD] Favor items added as list of items ot select from in each demigod build
  1265. local unitId = unit:GetUnitId()#..'peppe'
  1266.  
  1267. if(UnitAITemplates[unitId].SkillBuilds) then
  1268. local brain = unit:GetAIBrain()
  1269. if(not brain.UseSkillWeights) then
  1270. local build = nil
  1271. build = UnitAITemplates[unitId].SkillBuilds[brain.SkillBuild]
  1272.  
  1273. if(build.FavorItems) then
  1274. local myFavorItems = {}
  1275. for k, v in build.FavorItems do
  1276. table.insert(myFavorItems, k)
  1277. end
  1278. local num = math.random(table.getn(myFavorItems))
  1279.  
  1280. for k, v in itemList do
  1281.  
  1282. if(v.BaseShopType == 'ugbshop08_general' and not isGeneral) then
  1283. continue
  1284. elseif(v.BaseShopType == 'ugbshop08_assassin' and not isAssassin) then
  1285. continue
  1286. end
  1287.  
  1288. if v.ItemId == build.FavorItems[num] then
  1289. table.insert(achievementItems, {ItemTable = v})
  1290. end
  1291. end
  1292.  
  1293. end
  1294. end
  1295. end
  1296.  
  1297. local numItems = table.getn(achievementItems)
  1298. if(numItems == 0) then
  1299. local myFavorItems = {'AchievementHealth','AchievementRunSpeed'} #'AchievementTeleport',
  1300. local num = math.random(table.getn(myFavorItems))
  1301. for k, v in itemList do
  1302. if(v.BaseShopType == 'ugbshop08_general' and not isGeneral) then
  1303. continue
  1304. elseif(v.BaseShopType == 'ugbshop08_assassin' and not isAssassin) then
  1305. continue
  1306. end
  1307.  
  1308. if v.ItemId == myFavorItems[num] then
  1309. table.insert(achievementItems, {ItemTable = v})
  1310. end
  1311. end
  1312. numItems = table.getn(achievementItems)
  1313. end
  1314. if(numItems > 0) then
  1315. result = achievementItems[Random(1, numItems)]
  1316. table.insert(bestItem, {ItemName = result.ItemTable.ItemId, ItemCost = result.ItemTable.ItemCost, BaseShopType = result.ItemTable.BaseShopType})
  1317. end
  1318. end
  1319.  
  1320. if(result) then
  1321. return bestItem[1]
  1322. else
  1323. return result
  1324. end
  1325. end
  1326.  
  1327. function GetAchievementItemsList()
  1328. # We've already built the list once; return the list - it should NOT change mid game
  1329. if ScenarioInfo.AIAchievementItemsList then
  1330. return ScenarioInfo.AIAchievementItemsList
  1331. end
  1332.  
  1333. ScenarioInfo.AIAchievementItemsList = {}
  1334.  
  1335. local shopIds = {
  1336. Generic = 'ugbshop08',
  1337. General = 'ugbshop08_general',
  1338. Assassin = 'ugbshop08_assassin',
  1339. }
  1340.  
  1341. for shopType,shopId in shopIds do
  1342. local shopBp = __blueprints[shopId]
  1343.  
  1344.  
  1345. # Get the shop tree which has the costs and ItemBPs for all the items
  1346. local shopTree = Common.GetShopTreeByBlueprint(shopBp)
  1347.  
  1348. # Go through all the items in the shop tree
  1349. for shopItemId,itemData in shopTree do
  1350. if not Items[itemData.ItemBP] then
  1351. continue
  1352. end
  1353.  
  1354. local itemTable = {}
  1355. itemTable.BaseShopType = shopId
  1356. itemTable.ItemCost = itemData.Cost
  1357. itemTable.ItemId = itemData.ItemBP
  1358.  
  1359. table.insert(ScenarioInfo.AIAchievementItemsList, itemTable)
  1360. end
  1361. end
  1362.  
  1363. # return the global items list
  1364. return ScenarioInfo.AIAchievementItemsList
  1365. end
  1366.  
  1367. # ------------------------------------------------------------------------------
  1368. # Purchase Citadel Upgrades
  1369. # ------------------------------------------------------------------------------
  1370. function GetUpgradesList()
  1371. # We've already built the list once; return the list - it should NOT change mid game
  1372. if ScenarioInfo.AIUpgradesList then
  1373. return ScenarioInfo.AIUpgradesList
  1374. end
  1375.  
  1376. ScenarioInfo.AIUpgradesList = {}
  1377.  
  1378. # Get the shop tree which has the costs and BPs for all the upgrades
  1379. local shopTree = Upgrades.Tree
  1380.  
  1381. # Go through all the items in the shop tree
  1382. for upgradeId, upgradeData in shopTree do
  1383.  
  1384.  
  1385.  
  1386. # [MOD] TE Ignore buff constraint to allow all upgrades to be purchased.
  1387. # -- if not Buffs[upgradeId] then
  1388. # -- continue
  1389.  
  1390. # -- end
  1391.  
  1392. # Blank template used by all upgrades
  1393. local itemTable = {
  1394. ItemWeights = {
  1395. HealthDesire = 0,
  1396. ManaDesire = 0,
  1397. PrimaryWeaponDesire = 0,
  1398. MinionDesire = 0,
  1399. SpeedDesire = 0,
  1400.  
  1401. Priority = 0,
  1402. },
  1403. }
  1404.  
  1405.  
  1406.  
  1407. # Store out some basic info we'll use when trying to shop
  1408. itemTable.ShopItemId = upgradeId
  1409.  
  1410. itemTable.ShopType = 'ugbshop01'
  1411. itemTable.BaseShopType = 'stronghold01'
  1412. itemTable.ItemCost = upgradeData.Cost
  1413. itemTable.ItemId = upgradeId
  1414. itemTable.WarRank = upgradeData.Level
  1415.  
  1416. # Get item default weighting
  1417.  
  1418. # [MOD] Set priority if it is available otherwise leave it to FriendlyAsset to process a PriorityFunction.
  1419. if(AIGlobals.CitadelUpgradeWeights[upgradeId] and AIGlobals.CitadelUpgradeWeights[upgradeId].Priority != -1 and AIGlobals.CitadelUpgradeWeights[upgradeId].Priority != nil) then
  1420. itemTable.ItemWeights.Priority = AIGlobals.CitadelUpgradeWeights[upgradeId].Priority
  1421. end
  1422.  
  1423. # [MOD] If prioty is set to -1 (auto) convert it to a priority.
  1424. if (AIGlobals.CitadelUpgradeWeights[upgradeId].Priority == -1) then
  1425. itemTable.ItemWeights.Priority = GetPriorityFromCost(itemTable.ItemCost)
  1426. end
  1427.  
  1428. # Uncomment to see the default weights for items
  1429. #LOG('*AI DEBUG: Item = ' .. itemTable.ItemId .. ' - WeightsTable = ' .. repr(itemTable.ItemWeights) )
  1430.  
  1431. # Store the cost and weight for this upgrade in the global upgrades list
  1432. table.insert( ScenarioInfo.AIUpgradesList, itemTable )
  1433. end
  1434.  
  1435.  
  1436. # return the global upgrades list
  1437. return ScenarioInfo.AIUpgradesList
  1438. end
  1439.  
  1440. function PurchaseCitadelUpgradeCalculateWeights( action, aiBrain, agent, initialAgent )
  1441. if not agent.WorldStateData.CanMove then
  1442. return false
  1443. end
  1444.  
  1445.  
  1446.  
  1447.  
  1448.  
  1449. local actionInformation = initialAgent.ShopInformation.BuyItem[action.ActionName]
  1450. if not actionInformation or not actionInformation.PurchaseItem then
  1451. return false
  1452. end
  1453.  
  1454. if actionInformation.PurchaseItemCost > agent.Gold then
  1455. return false
  1456. end
  1457.  
  1458. local shopPos = actionInformation.PurchaseShopPosition
  1459. if agent.Gold - actionInformation.PurchaseItemCost < 0 then
  1460. return false
  1461. end
  1462. agent.Gold = agent.Gold - actionInformation.PurchaseItemCost
  1463.  
  1464. if not agent.AgentHasMoved then
  1465. distance = initialAgent.GOAP.BrainAsset:GetDistance( 'STRONGHOLD', 'Ally' )
  1466. else
  1467. distance = VDist3XZ( agent.Position, shopPos )
  1468. end
  1469.  
  1470. if not distance then
  1471. return false
  1472. end
  1473.  
  1474. agent:SetPosition( shopPos )
  1475.  
  1476. return { PurchaseItems = ( actionInformation.PurchaseItemPriority * -1 ), }, math.max( distance / agent.Speed, 2 )
  1477. end
  1478.  
  1479. function PurchaseCitadelUpgradeStatus(unit, action)
  1480. local aiBrain = unit:GetAIBrain()
  1481. local actionBp = HeroAIActionTemplates[action.ActionName]
  1482.  
  1483. unit.ShopInformation.CanBuyItem[action.ActionName] = false
  1484.  
  1485. if(not unit.ShopInformation.BuyItem[action.ActionName]) then
  1486. unit.ShopInformation.BuyItem[action.ActionName] = false
  1487. end
  1488.  
  1489. local actionInformation = {}
  1490. actionInformation.PurchaseItem = false
  1491. actionInformation.PurchaseItemPriority = false
  1492. actionInformation.PurchaseShopPosition = false
  1493. actionInformation.PurchaseItemCost = false
  1494.  
  1495. unit.ShopInformation.BuyItem[action.ActionName] = actionInformation
  1496.  
  1497. local bestItem = FindBestCitadelUpgrade(unit, aiBrain, action)
  1498. if not bestItem then
  1499. return false
  1500. end
  1501.  
  1502. actionInformation.PurchaseItem = bestItem.ItemName
  1503. actionInformation.PurchaseItemPriority = bestItem.ItemPriority
  1504. actionInformation.PurchaseShopType = bestItem.ShopType
  1505. actionInformation.PurchaseShopPosition = bestItem.Shop:GetPosition()
  1506. actionInformation.PurchaseItemCost = bestItem.ItemCost
  1507.  
  1508. unit.ShopInformation.BuyItem[action.ActionName] = actionInformation
  1509. unit.ShopInformation.CanBuyItem[action.ActionName] = true
  1510.  
  1511. #if (unit:GetArmy() == 1) then
  1512. # LOG('*AI SHOP DEBUG: ' .. action.ActionName .. 'Status - Upgrade = ' .. bestItem.ItemName
  1513. # .. ' - ItemPriority = ' .. bestItem.ItemPriority)
  1514. #end
  1515.  
  1516. return true
  1517. end
  1518.  
  1519. function PurchaseCitadelUpgradeAction(unit, action)
  1520. local aiBrain = unit:GetAIBrain()
  1521.  
  1522. local bestItem = FindBestCitadelUpgrade( unit, aiBrain, action )
  1523. if not bestItem then
  1524. return
  1525. end
  1526.  
  1527. #if (unit:GetArmy() == 1) then
  1528. # WARN('*AI SHOP DEBUG: Army= ' .. unit:GetArmy() .. ' - Purchasing item= ' .. bestItem.ItemName
  1529. # .. ' - Priority= ' .. bestItem.ItemPriority
  1530. # .. ' - Quantity= ' .. bestItem.NumPurchase .. '\n\n' )
  1531. #end
  1532.  
  1533. if(PurchaseCitadelUpgrade(unit, bestItem.ItemName) == 'Stuck') then
  1534. action:LockAction(2)
  1535. end
  1536.  
  1537. ShopCleanup(unit, action)
  1538.  
  1539. unit.GOAP:ForcePurchaseUpdate()
  1540. end
  1541.  
  1542. function FindBestCitadelUpgrade(unit, aiBrain, action)
  1543. local bestValue = 0
  1544. local bestItems = {}
  1545. local shop = aiBrain:GetStronghold()
  1546.  
  1547. local asset = action.StrategicAsset
  1548. if not asset or not asset.CitadelUpgradePriorities or not shop then
  1549. return false
  1550. end
  1551.  
  1552. local highestPriority = false
  1553.  
  1554. for _,itemData in asset.CitadelUpgradePriorities do
  1555. local purchaseQuantity = 1
  1556. local currentPriority = 0
  1557.  
  1558. if itemData.Priority <= 0 then
  1559. continue
  1560. end
  1561.  
  1562. # If we have a highest priority and this next item won't be higher; break the loop
  1563. if highestPriority and (itemData.Priority) < highestPriority then
  1564. break
  1565. end
  1566.  
  1567. if GetArmyBrain(unit:GetArmy()).mGold - AIGlobals.ShouldSave(unit, itemData.ItemTable.ItemId) < itemData.ItemTable.ItemCost then
  1568. continue
  1569. end
  1570.  
  1571. if ValidateUpgrade.CanPickUpgrade(Upgrades.Tree, unit, aiBrain.Score.WarRank, itemData.ItemTable.ItemId) != true then
  1572. continue
  1573. end
  1574.  
  1575. highestPriority = itemData.Priority
  1576.  
  1577. table.insert( bestItems, { ItemName = itemData.ItemTable.ItemId, ShopType = itemData.ItemTable.ShopType,
  1578. Shop = shop, ItemCost = itemData.ItemTable.ItemCost, ItemPriority = highestPriority } )
  1579. end
  1580.  
  1581. if table.getn( bestItems ) > 0 then
  1582. return bestItems[ Random( 1, table.getn(bestItems) ) ]
  1583. end
  1584.  
  1585. return false
  1586. end
  1587.  
  1588. function PurchaseCitadelUpgrade(unit, upgradeName)
  1589. if not ValidateUpgrade.CanPickUpgrade(Upgrades.Tree, unit, unit:GetAIBrain().Score.WarRank, upgradeName) then
  1590. return false
  1591. end
  1592.  
  1593. if unit.Sync.ShopId and not ShopCleanup(unit) then
  1594. return false
  1595. end
  1596.  
  1597. local aiBrain = unit:GetAIBrain()
  1598. local shop = aiBrain:GetStronghold()
  1599. if VDist3XZSq( unit.Position, shop.Position ) > 200 then
  1600. local cmd = MoveToShop( unit, shop, aiBrain )
  1601. while VDist3XZSq( unit.Position, shop.Position ) > 200 do
  1602. WaitTicks(6)
  1603.  
  1604. if unit:IsDead() or shop:IsDead() then
  1605. return false
  1606. end
  1607. end
  1608. end
  1609.  
  1610. # Go to the shop
  1611. local commandData = {
  1612. TaskName = 'BeginShopTask',
  1613. TargetId = shop:GetEntityId(),
  1614. }
  1615. local cmd = IssueScript( {unit}, commandData )
  1616.  
  1617. local oldPos = table.copy( unit:GetPosition() )
  1618. local stuckCount = 0
  1619.  
  1620. # Wait until we are in the shop
  1621. while not shop:CheckShopper(unit) do
  1622. WaitTicks(6)
  1623.  
  1624. if unit:IsDead() or shop:IsDead() then
  1625. return
  1626. end
  1627.  
  1628. local newPos = unit:GetPosition()
  1629. if newPos[1] == oldPos[1] and newPos[2] == oldPos[2] and newPos[3] == oldPos[3] then
  1630. stuckCount = stuckCount + 1
  1631. else
  1632. stuckCount = 0
  1633. end
  1634.  
  1635. if stuckCount >= 10 then
  1636. LOG('*AI DEBUG: Unit Stuck getting to shop - PurchaseCitadelUpgrade')
  1637. return 'Stuck'
  1638. end
  1639.  
  1640. oldPos = table.copy(newPos)
  1641. end
  1642.  
  1643. # Purchase the upgrade
  1644.  
  1645. local result = import('/lua/sim/upgradesys.lua').HandleUpgrade(unit, upgradeName)
  1646. WaitTicks(6)
  1647.  
  1648.  
  1649. if(result) then
  1650. local announcement = LOCF(AIChatGlobals.Named.PurchasingUpgrade, ArmyBonuses[upgradeName].DisplayName)
  1651. AIUtils.AIChat(unit, announcement)
  1652. end
  1653.  
  1654. return result
  1655. end
RAW Paste Data