Advertisement
Guest User

Untitled

a guest
Oct 20th, 2019
125
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 95.12 KB | None | 0 0
  1. CONTAINERS_VERSION = "0.80"
  2.  
  3. require('libraries/timers')
  4. require('libraries/playertables')
  5.  
  6. local ID_BASE = "cont_"
  7. FORCE_NIL = false
  8.  
  9.  
  10. CONTAINERS_DEBUG = IsInToolsMode() -- Should we print debugging prints for containers
  11.  
  12. --[[
  13.  
  14. Containers API Calls
  15. Containers:AddItemToUnit(unit, item)
  16. Containers:CreateContainer(cont)
  17. Containers:CreateShop(cont)
  18. Containers:DeleteContainer(c, deleteContents)
  19. Containers:DisplayError(pid, message)
  20. Containers:EmitSoundOnClient(pid, sound)
  21. Containers:GetDefaultInventory(unit)
  22. Containers:GetEntityContainers(entity)
  23. Containers:SetDefaultInventory(unit, container)
  24. Containers:SetItemLimit(limit)
  25. Containers:SetRangeAction(unit, tab)
  26. Containers:UsePanoramaInventory(useInventory)
  27.  
  28. Containers:OnButtonPressed(playerID, container, unit, buttonNumber, buttonName)
  29. Containers:OnCloseClicked(playerID, container, unit)
  30. Containers:OnDragFrom(playerID, container, unit, item, fromSlot, toContainer, toSlot)
  31. Containers:OnDragTo(playerID, container, unit, item, fromSlot, toContainer, toSlot)
  32. Containers:OnDragWithin(playerID, container, unit, item, fromSlot, toSlot)
  33. Containers:OnDragWorld(playerID, container, unit, item, slot, position, entity)
  34. Containers:OnLeftClick(playerID, container, unit, item, slot)
  35. Containers:OnRightClick(playerID, container, unit, item, slot)
  36.  
  37.  
  38. Container Creation:
  39. Containers:CreateContainer({
  40. layout = {3,2,2},
  41. skins = {"Skin1", "Another"}, --{}
  42. buttons = {}, -- {"Take All"}
  43. headerText = "#lootbox",
  44. draggable = true,
  45. position = "200px 200px 0px", -- "30% 40%" -- "mouse" -- "entity"
  46. equipment = true,
  47. range = 250,
  48. closeOnOrder = true,
  49. forceOwner = false,
  50. forcePurchaser = false,
  51. entity = PlayerResource:GetSelectedHeroEntity(0),
  52.  
  53. pids = {0}, -- {[2]=true, [4]=true}
  54. items = {}, -- {CreateItem(...), CreateItem(...)} -- {[3]=CreateItem(...), [5]=CreateItem(...):GetEntityIndex()}
  55.  
  56. cantDragFrom = {}, -- {3,5}
  57. cantDragTo = {},
  58.  
  59. layoutFile = "file://{resources}/layout/custom_game/containers/alt_container_example.xml", nil->default
  60.  
  61. OnLeftClick = function(playerID, container, unit, item, slot) ... end, -- nil->default, false->do nothing
  62. OnRightClick = function(playerID, container, unit, item, slot) ... end, -- nil->default, false->do nothing
  63. OnDragFrom = function(playerID, container, unit, item, fromSlot, toContainer, toSlot) ... end, -- nil->default, false->do nothing
  64. OnDragTo = function(playerID, container, unit, item, fromSlot, toContainer, toSlot) ... end, -- nil->default, false->do nothing
  65. OnDragWithin = function(playerID, container, unit, item, fromSlot, toSlot) ... end, -- nil->default, false->do nothing
  66. OnDragWorld = function(playerID, container, unit, item, slot, position, entity) ... end, -- nil->default, false->do nothing
  67. OnCloseClicked = function(playerID, container, unit) ... end, -- nil->default, false->do nothing
  68. OnButtonPressed = function(playerID, container, unit, buttonNumber, buttonName) ... end, -- nil->default, false->do nothing
  69. OnEntityOrder = function(playerID, container, unit, target), -- nil->do nothing
  70. OnEntityDrag = function(playerID, container, unit, target, fromContainer, item) ... end, -- nil->do nothing
  71. OnClose = function(playerID, container) ... end, -- nil->default
  72. OnOpen = function(playerID, container) ... end, -- nil->default,
  73. OnSelect = function(playerID, container, selectedEntity) ... end, -- nil->default,
  74. OnDeselect = function(playerID, container, deselectedEntity) ... end, -- nil->default,,
  75.  
  76. -- return true to allow the item add event. slot is -1 if no slot is specified.
  77. AddItemFilter = function(container, item, slot), -- nil->no filter
  78.  
  79.  
  80. -- See containers/container_events.js for javascript callback registration and handling
  81. -- nil means to use the default
  82. OnLeftClickJS = "ExampleLeftClick",
  83. OnRightClickJS = "ExampleRightClick",
  84. OnDoubleClickJS = "ExampleDoubleClick",
  85. OnMouseOutJS = "ExampleMouseOut",
  86. OnMouseOverJS = "ExampleMouseOver",
  87. OnButtonPressedJS = "ExampleButtonPressed",
  88. OnCloseClickedJS = "ExampleCloseClicked",
  89. )}
  90.  
  91. Container Functions:
  92. c:ActivateItem(unit, item, playerID)
  93. c:AddItem(item, slot, column, bypassFilter)
  94. c:AddSkin(skin)
  95. c:AddSubscription(pid)
  96. c:CanDragFrom(pid)
  97. c:CanDragTo(pid)
  98. c:CanDragWithin(pid)
  99. c:ClearSlot(slot)
  100. c:Close(pid)
  101. c:ContainsItem(item)
  102. c:Delete(deleteContents)
  103. c:GetAllItems()
  104. c:GetAllOpen()
  105. c:GetButtonName(number)
  106. c:GetButtons()
  107. c:GetCanDragFromPlayers()
  108. c:GetCanDragToPlayers()
  109. c:GetCanDragWithinPlayers()
  110. c:GetContainerIndex()
  111. c:GetEntity()
  112. c:GetForceOwner()
  113. c:GetForcePurchaser()
  114. c:GetHeaderText()
  115. c:GetItemInRowColumn(row, column)
  116. c:GetItemInSlot(slot)
  117. c:GetItemsByName(name)
  118. c:GetLayout()
  119. c:GetNumItems()
  120. c:GetRange()
  121. c:GetRowColumnForItem(item)
  122. c:GetSize()
  123. c:GetSkins()
  124. c:GetSlotForItem(item)
  125. c:GetSubscriptions()
  126. c:HasSkin(skin)
  127. c:IsCloseOnOrder()
  128. c:IsDraggable()
  129. c:IsEquipment()
  130. c:IsInventory()
  131. c:IsOpen(pid)
  132. c:IsSubscribed(pid)
  133. c:OnButtonPressed(fun)
  134. c:OnButtonPressedJS(jsCallback)
  135. c:OnClose(fun)
  136. c:OnCloseClicked(fun)
  137. c:OnCloseClickedJS(jsCallback)
  138. c:OnDeselect(fun)
  139. c:OnDoubleClickJS(jsCallback)
  140. c:OnDragFrom(fun)
  141. c:OnDragTo(fun)
  142. c:OnDragWithin(fun)
  143. c:OnDragWorld(fun)
  144. c:OnEntityDrag(fun)
  145. c:OnEntityOrder(fun)
  146. c:OnLeftClick(fun)
  147. c:OnLeftClickJS(jsCallback)
  148. c:OnMouseOutJS(jsCallback)
  149. c:OnMouseOverJS(jsCallback)
  150. c:OnOpen(fun)
  151. c:OnRightClick(fun)
  152. c:OnRightClickJS(jsCallback)
  153. c:OnSelect(fun)
  154. c:Open(pid)
  155. c:RemoveButton(number)
  156. c:RemoveItem(item)
  157. c:RemoveSkin(skin)
  158. c:RemoveSubscription(pid)
  159. c:SetButton(number, name)
  160. c:SetCanDragFrom(pid, canDrag)
  161. c:SetCanDragTo(pid, canDrag)
  162. c:SetCanDragWithin(pid, canDrag)
  163. c:SetCloseOnOrder(close)
  164. c:SetDraggable(drag)
  165. c:SetEntity(entity)
  166. c:SetEquipment(equip)
  167. c:SetForceOwner(owner)
  168. c:SetForcePurchaser(purchaser)
  169. c:SetHeaderText(header)
  170. c:SetLayout(layout, removeOnContract)
  171. c:SetRange(range)
  172. c:SwapItems(item1, item2, allowCombine)
  173. c:SwapSlots(slot1, slot2, allowCombine)
  174.  
  175.  
  176.  
  177.  
  178. Shop Creation:
  179. Containers:CreateShop({
  180. -- Same as CreateContainer but with the additions of
  181. items = {}, -- {CreateItem(...), CreateItem(...)} -- {[3]=CreateItem(...), [5]=CreateItem(...):GetEntityIndex()}
  182. prices = {}, -- {500, 800} -- {[3]=500, [5]=800}
  183. stocks = {}, -- {[5]=10}
  184. }
  185.  
  186. Shop Functions:
  187. -- Same as a Container but with the additions of
  188. shop:BuyItem(playerID, unit, item)
  189. shop:GetPrice(item)
  190. shop:GetStock(item)
  191. shop:SetPrice(item, price)
  192. shop:SetStock(item, stock)
  193.  
  194. ]]
  195.  
  196.  
  197. LinkLuaModifier( "modifier_shopkeeper", "libraries/modifiers/modifier_shopkeeper.lua", LUA_MODIFIER_MOTION_NONE )
  198.  
  199. --"dota_hud_error_not_enough_gold" "Not Enough Gold"
  200. --"dota_hud_error_item_out_of_stock" "Item is out of Stock"
  201. --"dota_hud_error_cant_pick_up_item" "Inventory Full"
  202. --"dota_hud_error_cant_sell_shop_not_in_range" "No Shop In Range"
  203. --"dota_hud_error_target_out_of_range" "Target Out Of Range"
  204. --"dota_hud_error_unit_command_restricted" "Can't Act"
  205. --"DOTA_FantasyTeamCreate_Error_Header" "Error"
  206. --"DOTA_Trading_Response_UnknownError" "Unknown Error"
  207.  
  208.  
  209. -- mango can't be activated from outside inventory if it ever touched it
  210. -- dust crash for same reason
  211. -- dagon no particles/effects for same reason
  212. -- soul ring crash for same reason
  213. -- bottle also doesn't activate for same reason
  214.  
  215. -- travels don't work, TP either probably
  216. -- armlet doesn't activate at all
  217.  
  218. -- euls has targeting issues
  219. -- aghs probably not for sure
  220. -- treads don't work in equipment
  221.  
  222.  
  223. local ApplyPassives = nil
  224. ApplyPassives = function(container, item, entOverride)
  225. local ent = entOverride or container:GetEntity()
  226. if not ent or not ent.AddNewModifier then return end
  227. if container.appliedPassives[item:GetEntityIndex()] then return end
  228.  
  229. local passives = Containers.itemPassives[item:GetAbilityName()]
  230. if passives then
  231. for _,passive in ipairs(passives) do
  232. -- check for previous buffs from this exact item
  233. local buffs = ent:FindAllModifiersByName(passive)
  234. for _, buff in ipairs(buffs) do
  235. if buff:GetAbility() == item then
  236. Timers:CreateTimer(function()
  237. --print("FOUND, rerunning until removed")
  238. ApplyPassives(container, item, entOverride)
  239. end)
  240. return
  241. end
  242. end
  243. end
  244.  
  245. container.appliedPassives[item:GetEntityIndex()] = {}
  246. for _,passive in ipairs(passives) do
  247. item:ApplyDataDrivenModifier(ent, ent, passive, {})
  248. buffs = ent:FindAllModifiersByName(passive)
  249. for _, buff in ipairs(buffs) do
  250. if buff:GetAbility() == item then
  251. table.insert(container.appliedPassives[item:GetEntityIndex()], buff)
  252. break
  253. end
  254. end
  255. end
  256. else
  257. passives = item:GetIntrinsicModifierName()
  258. if passives then
  259. local buff = ent:AddNewModifier(ent, item, passives, {})
  260. container.appliedPassives[item:GetEntityIndex()] = {buff}
  261. end
  262. end
  263. end
  264.  
  265. local GetItem = function(item)
  266. if type(item) == "number" then
  267. return EntIndexToHScript(item)
  268. elseif item and IsValidEntity(item) and item.IsItem and item:IsItem() then
  269. return item
  270. end
  271. end
  272.  
  273.  
  274.  
  275. local unitInventory = {unit = nil, range = 150, allowStash = false, canDragFrom = {}, canDragTo = {}, forceOwner = nil, forcePurchaser = nil}
  276.  
  277. function unitInventory:AddItem(item, slot)
  278. item = GetItem(item)
  279. local unit = self.unit
  280.  
  281. local findStack = slot == nil
  282. slot = slot or 0
  283.  
  284. local size = self:GetSize()
  285.  
  286. if slot > size then
  287. print("[containers.lua] Request to add item in slot " .. slot .. " -- exceeding dota inventory size " .. size)
  288. return false
  289. end
  290.  
  291. local full = true
  292. local stack = false
  293. for i=0,size do
  294. local sItem = unit:GetItemInSlot(i)
  295. if not sItem then
  296. full = false
  297. elseif sItem:GetAbilityName() == item:GetAbilityName() and item:IsStackable() then
  298. stack = true
  299. end
  300. end
  301.  
  302. if full and not stack then return false end
  303. unit:AddItem(item)
  304.  
  305. Timers:CreateTimer(function()
  306. if not IsValidEntity(item) then return end
  307.  
  308. unit:DropItemAtPositionImmediate(item, unit:GetAbsOrigin())
  309. local drop = nil
  310. for i=GameRules:NumDroppedItems()-1,0,-1 do
  311. drop = GameRules:GetDroppedItem(i)
  312. if drop:GetContainedItem() == item then
  313. drop:RemoveSelf()
  314. break
  315. end
  316. end
  317.  
  318. unit:AddItem(item)
  319.  
  320. if not findStack then
  321. for i=0,5 do
  322. if unit:GetItemInSlot(i) == item then
  323. unit:SwapItems(i,slot)
  324.  
  325. if self.forceOwner then
  326. item:SetOwner(self.forceOwner)
  327. elseif self.forceOwner == false then
  328. item:SetOwner(nil)
  329. end
  330. if self.forcePurchaser then
  331. item:SetPurchaser(self.forcePurchaser)
  332. elseif self.forceOwner == false then
  333. item:SetPurchaser(nil)
  334. end
  335. end
  336. end
  337. end
  338.  
  339. end)
  340.  
  341. if not findStack then
  342. for i=0,5 do
  343. if unit:GetItemInSlot(i) == item then
  344. unit:SwapItems(i,slot)
  345.  
  346. if self.forceOwner then
  347. item:SetOwner(self.forceOwner)
  348. elseif self.forceOwner == false then
  349. item:SetOwner(nil)
  350. end
  351. if self.forcePurchaser then
  352. item:SetPurchaser(self.forcePurchaser)
  353. elseif self.forceOwner == false then
  354. item:SetPurchaser(nil)
  355. end
  356. return true
  357. end
  358. end
  359. end
  360.  
  361.  
  362.  
  363. return true
  364. end
  365.  
  366. function unitInventory:ClearSlot(slot)
  367. local item = self.unit:GetItemInSlot(slot)
  368. if item then
  369. self:RemoveItem(item)
  370. end
  371. end
  372.  
  373. function unitInventory:RemoveItem(item)
  374. item = GetItem(item)
  375. local unit = self.unit
  376.  
  377. if self:ContainsItem(item) then
  378. unit:DropItemAtPositionImmediate(item, unit:GetAbsOrigin())
  379. local drop = nil
  380. for i=GameRules:NumDroppedItems()-1,0,-1 do
  381. drop = GameRules:GetDroppedItem(i)
  382. if drop:GetContainedItem() == item then
  383. drop:RemoveSelf()
  384. break
  385. end
  386. end
  387. end
  388. end
  389.  
  390.  
  391. function unitInventory:ContainsItem(item)
  392. item = GetItem(item)
  393. if not item then return false end
  394.  
  395. local unit = self.unit
  396. for i=0,11 do
  397. if item == unit:GetItemInSlot(i) then
  398. return true
  399. end
  400. end
  401.  
  402. return false
  403. end
  404.  
  405. function unitInventory:GetSize()
  406. if self.allowStash then
  407. return 11
  408. else
  409. return 5
  410. end
  411. end
  412.  
  413. function unitInventory:GetItemInSlot(slot)
  414. return self.unit:GetItemInSlot(slot)
  415. end
  416.  
  417. function unitInventory:GetRange()
  418. return self.range
  419. end
  420.  
  421. function unitInventory:GetEntity()
  422. return self.unit
  423. end
  424.  
  425. function unitInventory:IsInventory()
  426. return true
  427. end
  428.  
  429.  
  430.  
  431.  
  432.  
  433. if not Containers then
  434. Containers = class({})
  435. end
  436.  
  437. function Containers:start()
  438. if not __ACTIVATE_HOOK then
  439. __ACTIVATE_HOOK = {funcs={}}
  440. setmetatable(__ACTIVATE_HOOK, {
  441. __call = function(t, func)
  442. table.insert(t.funcs, func)
  443. end
  444. })
  445.  
  446. debug.sethook(function(...)
  447. local info = debug.getinfo(2)
  448. local src = tostring(info.short_src)
  449. local name = tostring(info.name)
  450. if name ~= "__index" then
  451. if string.find(src, "addon_game_mode") then
  452. if GameRules:GetGameModeEntity() then
  453. for _, func in ipairs(__ACTIVATE_HOOK.funcs) do
  454. local status, err = pcall(func)
  455. if not status then
  456. print("__ACTIVATE_HOOK callback error: " .. err)
  457. end
  458. end
  459.  
  460. debug.sethook(nil, "c")
  461. end
  462. end
  463. end
  464. end, "c")
  465. end
  466.  
  467. __ACTIVATE_HOOK(function()
  468. local mode = GameRules:GetGameModeEntity()
  469. mode:SetExecuteOrderFilter(Dynamic_Wrap(Containers, 'OrderFilter'), Containers)
  470. Containers.oldFilter = mode.SetExecuteOrderFilter
  471. mode.SetExecuteOrderFilter = function(mode, fun, context)
  472. --print('SetExecuteOrderFilter', fun, context)
  473. Containers.nextFilter = fun
  474. Containers.nextContext = context
  475. end
  476. Containers.initialized = true
  477. end)
  478.  
  479.  
  480. self.initialized = false
  481. self.containers = {}
  482. self.nextID = 0
  483. self.closeOnOrders = {}
  484. for i=0,DOTA_MAX_TEAM_PLAYERS do
  485. self.closeOnOrders[i] = {}
  486. end
  487.  
  488. CustomNetTables:SetTableValue("containers_lua", "use_panorama_inventory", {value=false})
  489.  
  490. self.nextFilter = nil
  491. self.nextContext = nil
  492. self.disableItemLimit = false
  493.  
  494. self.itemKV = LoadKeyValues("scripts/npc/items.txt")
  495. self.itemIDs = {}
  496.  
  497. self.entityShops = {}
  498. self.defaultInventories = {}
  499.  
  500. self.rangeActions = {}
  501. self.previousSelection = {}
  502. self.entityContainers = {}
  503.  
  504. for k,v in pairs(LoadKeyValues("scripts/npc/npc_abilities_override.txt")) do
  505. if self.itemKV[k] then
  506. self.itemKV[k] = v
  507. end
  508. end
  509.  
  510. for k,v in pairs(LoadKeyValues("scripts/npc/npc_items_custom.txt")) do
  511. if not self.itemKV[k] then
  512. self.itemKV[k] = v
  513. end
  514. end
  515.  
  516. for k,v in pairs(self.itemKV) do
  517. if type(v) == "table" and v.ID then
  518. self.itemIDs[v.ID] = k
  519. end
  520. end
  521.  
  522. self.itemPassives = {}
  523.  
  524. for id,itemName in pairs(Containers.itemIDs) do
  525. local kv = Containers.itemKV[itemName]
  526. if kv.BaseClass == "item_datadriven" then
  527. self.itemPassives[itemName] = {}
  528. if kv.Modifiers then
  529. local mods = kv.Modifiers
  530. for modname, mod in pairs(mods) do
  531. if mod.Passive == 1 then
  532. table.insert(self.itemPassives[itemName], modname)
  533. end
  534. end
  535. end
  536. end
  537. end
  538.  
  539. self.oldUniversal = GameRules.SetUseUniversalShopMode
  540. self.universalShopMode = false
  541. GameRules.SetUseUniversalShopMode = function(gamerules, universal)
  542. Containers.universalShopMode = universal
  543. Containers.oldUniversal(gamerules, universal)
  544. end
  545. if GameRules.FDesc then
  546. GameRules.FDesc["SetUseUniversalShopMode"] = nil
  547. end
  548.  
  549. CustomGameEventManager:RegisterListener("Containers_EntityShopRange", Dynamic_Wrap(Containers, "Containers_EntityShopRange"))
  550. CustomGameEventManager:RegisterListener("Containers_Select", Dynamic_Wrap(Containers, "Containers_Select"))
  551. CustomGameEventManager:RegisterListener("Containers_HideProxy", Dynamic_Wrap(Containers, "Containers_HideProxy"))
  552.  
  553. CustomGameEventManager:RegisterListener("Containers_OnLeftClick", Dynamic_Wrap(Containers, "Containers_OnLeftClick"))
  554. CustomGameEventManager:RegisterListener("Containers_OnRightClick", Dynamic_Wrap(Containers, "Containers_OnRightClick"))
  555. CustomGameEventManager:RegisterListener("Containers_OnDragFrom", Dynamic_Wrap(Containers, "Containers_OnDragFrom"))
  556. CustomGameEventManager:RegisterListener("Containers_OnDragWorld", Dynamic_Wrap(Containers, "Containers_OnDragWorld"))
  557. CustomGameEventManager:RegisterListener("Containers_OnCloseClicked", Dynamic_Wrap(Containers, "Containers_OnCloseClicked"))
  558. CustomGameEventManager:RegisterListener("Containers_OnButtonPressed", Dynamic_Wrap(Containers, "Containers_OnButtonPressed"))
  559.  
  560. CustomGameEventManager:RegisterListener("Containers_OnSell", Dynamic_Wrap(Containers, "Containers_OnSell"))
  561.  
  562.  
  563.  
  564. Timers:CreateTimer(function()
  565. for id,action in pairs(Containers.rangeActions) do
  566. local unit = action.unit
  567. if unit then
  568. if action.entity and not IsValidEntity(action.entity) then
  569. Containers.rangeActions[id] = nil
  570. else
  571. local range = action.range
  572. if not range and action.container then
  573. range = action.container:GetRange()
  574. end
  575. if not range then range = 150 end
  576. local range2 = range * range
  577. local pos = action.position or action.entity:GetAbsOrigin()
  578. local dist = unit:GetAbsOrigin() - pos
  579.  
  580. if (dist.x * dist.x + dist.y * dist.y) <= range2 then
  581. local status, err = pcall(action.action, action.playerID, action.container, unit, action.entity or action.position, action.fromContainer or action.orderType, action.item)
  582. if not status then print('[containers.lua] RangeAction failure:' .. err) end
  583. Containers.rangeActions[id] = nil
  584. end
  585. end
  586. else
  587. Containers.rangeActions[id] = nil
  588. end
  589. end
  590. return .01
  591. end)
  592. end
  593.  
  594.  
  595. local closeOnOrderSkip = {
  596. [DOTA_UNIT_ORDER_PURCHASE_ITEM] = true,
  597. [DOTA_UNIT_ORDER_GLYPH] = true,
  598. [DOTA_UNIT_ORDER_HOLD_POSITION] = true,
  599. [DOTA_UNIT_ORDER_STOP] = true,
  600. [DOTA_UNIT_ORDER_EJECT_ITEM_FROM_STASH] = true,
  601. [DOTA_UNIT_ORDER_DISASSEMBLE_ITEM] = true,
  602. [DOTA_UNIT_ORDER_PING_ABILITY] = true,
  603. [DOTA_UNIT_ORDER_TRAIN_ABILITY] = true,
  604. [DOTA_UNIT_ORDER_CAST_NO_TARGET] = true,
  605. [DOTA_UNIT_ORDER_CAST_TOGGLE] = true,
  606. }
  607.  
  608. function Containers:AddItemToUnit(unit, item)
  609. if item and unit then
  610. local defInventory = Containers:GetDefaultInventory(unit)
  611. if defInventory then
  612. if not defInventory:AddItem(item) then
  613. CreateItemOnPositionSync(unit:GetAbsOrigin() + RandomVector(10), item)
  614. end
  615. else
  616. local iname = item:GetAbilityName()
  617. local exists = false
  618. local full = true
  619. for i=0,5 do
  620. local it = unit:GetItemInSlot(i)
  621. if not it then
  622. full = false
  623. elseif it:GetAbilityName() == iname then
  624. exists = true
  625. end
  626. end
  627.  
  628. if not full or (full and item:IsStackable() and exists) then
  629. unit:AddItem(item)
  630. else
  631. CreateItemOnPositionSync(unit:GetAbsOrigin() + RandomVector(10), item)
  632. end
  633. end
  634. end
  635. end
  636.  
  637. function Containers:SetItemLimit(limit)
  638. SendToServerConsole("dota_max_physical_items_purchase_limit " .. limit)
  639. end
  640.  
  641. function Containers:SetDisableItemLimit(disable)
  642. if not self.initialized then
  643. print('[containers.lua] FATAL: Containers:Init() has not been initialized!')
  644. return
  645. end
  646. --self.disableItemLimit = disable
  647. end
  648.  
  649. function Containers:UsePanoramaInventory(useInventory)
  650. CustomNetTables:SetTableValue("containers_lua", "use_panorama_inventory", {value=useInventory})
  651. CustomGameEventManager:Send_ServerToAllClients("cont_use_panorama_inventory", {use=useInventory})
  652. end
  653.  
  654. function Containers:DisplayError(pid, message)
  655. local player = PlayerResource:GetPlayer(pid)
  656. if player then
  657. CustomGameEventManager:Send_ServerToPlayer(player, "cont_create_error_message", {message=message})
  658. end
  659. end
  660.  
  661. function Containers:EmitSoundOnClient(pid, sound)
  662. local player = PlayerResource:GetPlayer(pid)
  663. if player then
  664. CustomGameEventManager:Send_ServerToPlayer(player, "cont_emit_client_sound", {sound=sound})
  665. end
  666. end
  667.  
  668. function Containers:OrderFilter(order)
  669. --print('Containers:OrderFilter')
  670. --PrintTable(order)
  671.  
  672. local ret = true
  673.  
  674. if Containers.nextFilter then
  675. ret = Containers.nextFilter(Containers.nextContext, order)
  676. end
  677.  
  678. if not ret then
  679. return false
  680. end
  681.  
  682. local issuerID = order.issuer_player_id_const
  683. local queue = order.queue == 1
  684.  
  685. if issuerID == -1 then return true end
  686.  
  687. -- close on order
  688. if not closeOnOrderSkip[order.order_type] then
  689. local oConts = Containers.closeOnOrders[issuerID]
  690. for id,cont in pairs(oConts) do
  691. if IsValidContainer(cont) then
  692. cont:Close(issuerID)
  693. else
  694. oConts[id] = nil
  695. end
  696. end
  697. end
  698.  
  699. if not queue and order.units["0"] then
  700. Containers.rangeActions[order.units["0"]] = nil
  701. end
  702.  
  703. local conts = Containers:GetEntityContainers(order.entindex_target)
  704. if order.units["0"] and #conts > 0 then
  705. local container = nil
  706. for _,cont in ipairs(conts) do
  707. if cont._OnEntityOrder then
  708. container = cont
  709. break
  710. end
  711. end
  712.  
  713. if container then
  714. local unit = EntIndexToHScript(order.units["0"])
  715. local target = EntIndexToHScript(order.entindex_target)
  716. local range = container:GetRange() or 150
  717. local unitpos = unit:GetAbsOrigin()
  718. local diff = unitpos - target:GetAbsOrigin()
  719. local dist = diff:Length2D()
  720. local pos = unitpos
  721. if dist > range * .9 then
  722. pos = target:GetAbsOrigin() + diff:Normalized() * range * .9
  723. end
  724.  
  725. local origOrder = order.order_type
  726.  
  727. if target.GetContainedItem then
  728. order.order_type = DOTA_UNIT_ORDER_MOVE_TO_POSITION
  729. order.position_x = pos.x
  730. order.position_y = pos.y
  731. order.position_z = pos.z
  732. else
  733. order.order_type = DOTA_UNIT_ORDER_MOVE_TO_TARGET
  734. end
  735.  
  736. Containers.rangeActions[order.units["0"]] = {
  737. unit = unit,
  738. entity = target,
  739. range = range,
  740. playerID = issuerID,
  741. container = container,
  742. orderType = origOrder,
  743. action = container._OnEntityOrder,
  744. }
  745. end
  746. end
  747.  
  748. --DOTA_UNIT_ORDER_GIVE_ITEM
  749. if order.units["0"] and order.order_type == DOTA_UNIT_ORDER_GIVE_ITEM then
  750. local unit = EntIndexToHScript(order.units["0"])
  751. local item = EntIndexToHScript(order.entindex_ability)
  752. local target = EntIndexToHScript(order.entindex_target)
  753.  
  754. local defInventory = Containers:GetDefaultInventory(target)
  755. if defInventory then
  756. order.order_type = DOTA_UNIT_ORDER_MOVE_TO_TARGET
  757.  
  758. Containers.rangeActions[order.units["0"]] = {
  759. unit = unit,
  760. entity = target,
  761. range = 180,
  762. playerID = issuerID,
  763. action = function(playerID, container, unit, target)
  764. if IsValidEntity(target) and target:IsAlive() then
  765. unit:DropItemAtPositionImmediate(item, unit:GetAbsOrigin())
  766. local drop = nil
  767. for i=GameRules:NumDroppedItems()-1,0,-1 do
  768. drop = GameRules:GetDroppedItem(i)
  769. if drop:GetContainedItem() == item then
  770. Containers:AddItemToUnit(target, item)
  771. drop:RemoveSelf()
  772. break
  773. end
  774. end
  775. end
  776.  
  777. unit:Stop()
  778. end,
  779. }
  780. else
  781. return true
  782. end
  783. end
  784.  
  785. if order.units["0"] and order.order_type == DOTA_UNIT_ORDER_PICKUP_ITEM then
  786. local unit = EntIndexToHScript(order.units["0"])
  787. local physItem = EntIndexToHScript(order.entindex_target)
  788. local unitpos = unit:GetAbsOrigin()
  789. if not physItem then return false end
  790. local diff = unitpos - physItem:GetAbsOrigin()
  791. local dist = diff:Length2D()
  792. local pos = unitpos
  793. if dist > 90 then
  794. pos = physItem:GetAbsOrigin() + diff:Normalized() * 90
  795. end
  796.  
  797. local defInventory = Containers:GetDefaultInventory(unit)
  798. if defInventory then
  799. order.order_type = DOTA_UNIT_ORDER_MOVE_TO_POSITION
  800. order.position_x = pos.x
  801. order.position_y = pos.y
  802. order.position_z = pos.z
  803.  
  804. Containers.rangeActions[order.units["0"]] = {
  805. unit = unit,
  806. position = physItem:GetAbsOrigin(),
  807. range = 100,
  808. playerID = issuerID,
  809. action = function(playerID, container, unit, target)
  810. if IsValidEntity(physItem) then
  811. local item = physItem:GetContainedItem()
  812. if item and defInventory:AddItem(item) then
  813. physItem:RemoveSelf()
  814. end
  815. end
  816. end,
  817. }
  818. end
  819. end
  820.  
  821. if order.units["0"] and order.order_type == DOTA_UNIT_ORDER_PURCHASE_ITEM then
  822. local unit = EntIndexToHScript(order.units["0"])
  823. local itemID = order.entindex_ability
  824. local itemName = Containers.itemIDs[itemID]
  825. local player = PlayerResource:GetPlayer(issuerID)
  826. local ownerID = issuerID --unit:GetMainControllingPlayer()
  827. local owner = PlayerResource:GetSelectedHeroEntity(ownerID)
  828.  
  829. local defInventory = Containers:GetDefaultInventory(unit)
  830.  
  831. if defInventory then
  832. local shops = Containers.entityShops[unit:GetEntityIndex()] or {home=false, side=false, secret=false}
  833. if not unit:IsAlive() then
  834. shops = {home=true, side=false, secret=false}
  835. end
  836.  
  837. if not shops.home and not shops.side and not shops.secret then
  838. CustomGameEventManager:Send_ServerToPlayer(player, "cont_create_error_message", {reason=67})
  839. return false
  840. end
  841.  
  842. local item = CreateItem(itemName, owner, owner)
  843. local cost = item:GetCost()
  844. if not defInventory:AddItem(item) then
  845. CreateItemOnPositionSync(unit:GetAbsOrigin() + RandomVector(10), item)
  846. end
  847.  
  848. PlayerResource:SpendGold(ownerID, cost, DOTA_ModifyGold_PurchaseItem)
  849. return false
  850. elseif Containers.disableItemLimit then
  851. if not unit:HasInventory() then
  852. unit = owner
  853. if unit == nil then return false end
  854. end
  855.  
  856. local itemDefinition = Containers.itemKV[itemName]
  857. local itemSide = itemDefinition["SideShop"] == 1
  858. local itemSecret = itemDefinition["SecretShop"] == 1
  859. if itemDefinition["ItemPurchasable"] == 0 then return false end
  860.  
  861. local toStash = true
  862. local full = true
  863. for i=0,5 do
  864. if unit:GetItemInSlot(i) == nil then
  865. full = false
  866. break
  867. end
  868. end
  869.  
  870. local shops = Containers.entityShops[unit:GetEntityIndex()] or {home=false, side=false, secret=false}
  871. if not unit:IsAlive() then
  872. shops = {home=true, side=false, secret=false}
  873. end
  874. local stashPurchasingDisabled = GameRules:GetGameModeEntity():GetStashPurchasingDisabled()
  875. local universalShopMode = Containers.universalShopMode
  876.  
  877. if universalShopMode then
  878. if not shops.home and not shops.side and not shops.secret then
  879. if stashPurchasingDisabled then
  880. CustomGameEventManager:Send_ServerToPlayer(player, "cont_create_error_message", {reason=67})
  881. return false
  882. end
  883.  
  884. toStash = true
  885. end
  886. else
  887. if not shops.home and not shops.side and not shops.secret then
  888. -- not in range of any shops
  889. if stashPurchasingDisabled then
  890. CustomGameEventManager:Send_ServerToPlayer(player, "cont_create_error_message", {reason=67})
  891. return false
  892. end
  893. elseif itemSecret and shops.secret then
  894. toStash = false
  895. elseif itemSide and shops.side then
  896. toStash = false
  897. elseif shops.home and not full then
  898. toStash = false
  899. end
  900. end
  901.  
  902. local item = CreateItem(itemName, owner, owner)
  903. local fullyShareStacking = Containers.itemKV[itemName]["ItemShareability"] == "ITEM_FULLY_SHAREABLE_STACKING"
  904. local dropped = {}
  905. local cost = item:GetCost()
  906.  
  907. if toStash then
  908. local restore = {}
  909. local stashSlot = 11
  910. for i=6,11 do
  911. local slot = owner:GetItemInSlot(i)
  912. if not slot then
  913. stashSlot = i
  914. break
  915. end
  916. end
  917.  
  918. for i=0,5 do
  919. local slot = owner:GetItemInSlot(i)
  920. if slot and (fullyShareStacking and slot:GetAbilityName() == itemName
  921. or ((slot:GetAbilityName() == "item_ward_dispenser" or slot:GetAbilityName() == "item_ward_observer" or slot:GetAbilityName() == "item_ward_sentry")) and
  922. (itemName == "item_ward_observer" or itemName == "item_ward_sentry")) then
  923.  
  924. owner:DropItemAtPositionImmediate(slot, owner:GetAbsOrigin())
  925. for i=GameRules:NumDroppedItems()-1,0,-1 do
  926. local drop = GameRules:GetDroppedItem(i)
  927. if drop:GetContainedItem() == slot then
  928. table.insert(dropped, drop)
  929. break
  930. end
  931. end
  932.  
  933. elseif slot and (slot:GetOwner() == owner or slot:GetPurchaser() == owner) then
  934. restore[slot:GetEntityIndex()] = {owner=slot:GetOwner(), purchaser=slot:GetPurchaser()}
  935. slot:SetPurchaser(nil)
  936. slot:SetOwner(nil)
  937. end
  938. end
  939.  
  940. if owner:GetNumItemsInStash() == 6 then
  941. if not unit.HasAnyAvailableInventorySpace and shops.home then
  942. CreateItemOnPositionSync(unit:GetAbsOrigin() + RandomVector(20), item)
  943. else
  944. local slot = DOTA_STASH_SLOT_6
  945. local slotItem = owner:GetItemInSlot(slot)
  946. if slotItem and slotItem:GetAbilityName() == item:GetAbilityName() then
  947. slot = DOTA_STASH_SLOT_5
  948. end
  949. owner:SwapItems(slot,14)
  950. owner:AddItem(item)
  951. for i=0,11 do
  952. if owner:GetItemInSlot(i) == item then
  953. owner:SwapItems(slot,i)
  954. owner:EjectItemFromStash(item)
  955. end
  956. end
  957. owner:SwapItems(14,slot)
  958. end
  959. else
  960. owner:AddItem(item)
  961. for i=0,11 do
  962. if owner:GetItemInSlot(i) == item then
  963. owner:SwapItems(stashSlot,i)
  964. end
  965. end
  966. end
  967.  
  968. for i=0,5 do
  969. local item = owner:GetItemInSlot(i)
  970. if item and restore[item:GetEntityIndex()] ~= nil then
  971. item:SetPurchaser(restore[item:GetEntityIndex()].purchaser)
  972. item:SetOwner(restore[item:GetEntityIndex()].owner)
  973. end
  974. end
  975. else
  976. if full then
  977. local physItem = CreateItemOnPositionSync(unit:GetAbsOrigin() + RandomVector(5), item)
  978. unit:PickupDroppedItem(physItem)
  979. else
  980. for i=6,11 do
  981. local item = unit:GetItemInSlot(i)
  982. if not shops.home and item and item:GetPurchaser() == owner then
  983. item:SetPurchaser(nil)
  984. end
  985. end
  986.  
  987. unit:AddItem(item)
  988.  
  989. for i=6,11 do
  990. local item = unit:GetItemInSlot(i)
  991. if not shops.home and item and item:GetPurchaser() == nil then
  992. item:SetPurchaser(owner)
  993. end
  994. end
  995. end
  996. end
  997.  
  998. local queue = false
  999. for _,drop in ipairs(dropped) do
  1000. ExecuteOrderFromTable({
  1001. UnitIndex = owner:GetEntityIndex(),
  1002. TargetIndex = drop:GetEntityIndex(),
  1003. OrderType = DOTA_UNIT_ORDER_PICKUP_ITEM,
  1004. Queue = queue,
  1005. })
  1006. queue = true
  1007. --owner:AddItem(drop:GetContainedItem())
  1008. --drop:RemoveSelf()
  1009. end
  1010.  
  1011. PlayerResource:SpendGold(ownerID, cost, DOTA_ModifyGold_PurchaseItem)
  1012.  
  1013. return false
  1014. end
  1015. end
  1016.  
  1017. return ret
  1018. end
  1019.  
  1020. function Containers:Containers_EntityShopRange(args)
  1021. local unit = args.unit
  1022. local shop = args.shop
  1023.  
  1024. local cs = Containers.entityShops
  1025. if not cs[unit] then cs[unit] = {home=false, side=false, secret=false} end
  1026.  
  1027. cs[unit].home = bit.band(shop, 1) ~= 0
  1028. cs[unit].side = bit.band(shop, 2) ~= 0
  1029. cs[unit].secret = bit.band(shop, 4) ~= 0
  1030. end
  1031.  
  1032. function Containers:Containers_Select(args)
  1033. local playerID = args.PlayerID
  1034. local prev = Containers.previousSelection[playerID]
  1035. local new = args.entity
  1036. local newEnt = EntIndexToHScript(new)
  1037.  
  1038. local prevConts = Containers:GetEntityContainers(prev)
  1039. for _, c in ipairs(prevConts) do
  1040. if c._OnDeselect then
  1041. local res, err = pcall(c._OnDeselect, playerID, c, prev)
  1042. if err then
  1043. print('[containers.lua] Error in OnDeselect: ' .. err)
  1044. end
  1045. end
  1046. end
  1047.  
  1048. Containers.previousSelection[playerID] = newEnt
  1049.  
  1050. local conts = Containers:GetEntityContainers(new)
  1051. for _, c in ipairs(conts) do
  1052. if c._OnSelect then
  1053. local res, err = pcall(c._OnSelect, playerID, c, newEnt)
  1054. if err then
  1055. print('[containers.lua] Error in OnSelect: ' .. err)
  1056. end
  1057. end
  1058. end
  1059. end
  1060.  
  1061. function Containers:Containers_HideProxy(args)
  1062. local abil = EntIndexToHScript(args.abilID)
  1063. if abil and abil.GetAbilityName and (abil:GetAbilityName() == "containers_lua_targeting" or abil:GetAbilityName() == "containers_lua_targeting_tree" )
  1064. and abil:GetOwner():GetPlayerOwnerID() == args.PlayerID then
  1065. abil:SetHidden(true)
  1066. end
  1067. end
  1068.  
  1069. function Containers:Containers_OnSell(args)
  1070. Containers:print('Containers_OnSell')
  1071. Containers:PrintTable(args)
  1072.  
  1073. local playerID = args.PlayerID
  1074. local unit = args.unit == nil and nil or EntIndexToHScript(args.unit)
  1075. local contID = args.contID
  1076. local itemID = args.itemID
  1077. local slot = args.slot
  1078.  
  1079. if not playerID then return end
  1080. --if unit and unit:GetMainControllingPlayer() ~= playerID then return end
  1081.  
  1082. local container = Containers.containers[contID]
  1083. if not container then return end
  1084.  
  1085. local item = EntIndexToHScript(args.itemID)
  1086. if not (item and IsValidEntity(item) and item.IsItem and item:IsItem()) then return end
  1087.  
  1088. if item:GetOwner() ~= unit or item:GetPurchaser() ~= unit then return end
  1089.  
  1090. local itemInSlot = container:GetItemInSlot(slot)
  1091. if itemInSlot ~= item then return end
  1092.  
  1093. local range = container:GetRange()
  1094. local ent = container:GetEntity()
  1095. if range == nil and ent and unit ~= ent then return end
  1096. if range and ent and unit and (ent:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D() >= range then
  1097. Containers:DisplayError(playerID,"#dota_hud_error_target_out_of_range")
  1098. return
  1099. end
  1100.  
  1101. local shops = Containers.entityShops[unit:GetEntityIndex()] or {home=false, side=false, secret=false}
  1102. local player = PlayerResource:GetPlayer(playerID)
  1103.  
  1104. if not shops.home and not shops.side and not shops.secret then
  1105. if player then
  1106. CustomGameEventManager:Send_ServerToPlayer(player, "cont_create_error_message", {reason=67})
  1107. end
  1108. return
  1109. end
  1110.  
  1111. local cost = item:GetCost()
  1112. if GameRules:GetGameTime() - item:GetPurchaseTime() > 10 then
  1113. cost = cost /2
  1114. end
  1115.  
  1116. container:RemoveItem(item)
  1117. item:RemoveSelf()
  1118. PlayerResource:ModifyGold(playerID, cost, false, DOTA_ModifyGold_SellItem)
  1119.  
  1120. if player then
  1121. SendOverheadEventMessage(player, OVERHEAD_ALERT_GOLD, unit, cost, player)
  1122. EmitSoundOnClient("General.Sell", player)
  1123. end
  1124. end
  1125.  
  1126. function Containers:Containers_OnLeftClick(args)
  1127. Containers:print('Containers_OnLeftClick')
  1128. Containers:PrintTable(args)
  1129.  
  1130. local playerID = args.PlayerID
  1131. local unit = args.unit == nil and nil or EntIndexToHScript(args.unit)
  1132. local contID = args.contID
  1133. local itemID = args.itemID
  1134. local slot = args.slot
  1135.  
  1136. if not playerID then return end
  1137. --if unit and unit:GetMainControllingPlayer() ~= playerID then return end
  1138.  
  1139. local container = Containers.containers[contID]
  1140. if not container then return end
  1141.  
  1142. local fun = container._OnLeftClick
  1143. if fun == false then return end
  1144.  
  1145. local item = EntIndexToHScript(args.itemID)
  1146. if not (item and IsValidEntity(item) and item.IsItem and item:IsItem()) then return end
  1147.  
  1148. local itemInSlot = container:GetItemInSlot(slot)
  1149. if itemInSlot ~= item then return end
  1150.  
  1151. local range = container:GetRange()
  1152. local ent = container:GetEntity()
  1153. if range == nil and ent and unit ~= ent then return end
  1154. if range and ent and unit and (ent:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D() >= range then
  1155. Containers:DisplayError(playerID,"#dota_hud_error_target_out_of_range")
  1156. return
  1157. end
  1158.  
  1159. if type(fun) == "function" then
  1160. fun(playerID, container, unit, item, slot)
  1161. else
  1162. Containers:OnLeftClick(playerID, container, unit, item, slot)
  1163. end
  1164. end
  1165.  
  1166. function Containers:Containers_OnRightClick(args)
  1167. Containers:print('Containers_OnRightClick')
  1168. Containers:PrintTable(args)
  1169.  
  1170. local playerID = args.PlayerID
  1171. local unit = args.unit == nil and nil or EntIndexToHScript(args.unit)
  1172. local contID = args.contID
  1173. local itemID = args.itemID
  1174. local slot = args.slot
  1175.  
  1176. if not playerID then return end
  1177. --if unit and unit:GetMainControllingPlayer() ~= playerID then return end
  1178.  
  1179. local container = Containers.containers[contID]
  1180. if not container then return end
  1181.  
  1182. local fun = container._OnRightClick
  1183. if fun == false then return end
  1184.  
  1185. local item = EntIndexToHScript(args.itemID)
  1186. if not (item and IsValidEntity(item) and item.IsItem and item:IsItem()) then return end
  1187.  
  1188. local itemInSlot = container:GetItemInSlot(slot)
  1189. if itemInSlot ~= item then return end
  1190.  
  1191. local range = container:GetRange()
  1192. local ent = container:GetEntity()
  1193. if range == nil and ent and unit ~= ent then return end
  1194. if range and ent and unit and (ent:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D() >= range then
  1195. Containers:DisplayError(playerID,"#dota_hud_error_target_out_of_range")
  1196. return
  1197. end
  1198.  
  1199. if type(fun) == "function" then
  1200. fun(playerID, container, unit, item, slot)
  1201. else
  1202. Containers:OnRightClick(playerID, container, unit, item, slot)
  1203. end
  1204. end
  1205.  
  1206. function Containers:Containers_OnDragFrom(args)
  1207. Containers:print('Containers_OnDragFrom')
  1208. Containers:PrintTable(args)
  1209.  
  1210. local playerID = args.PlayerID
  1211. local unit = args.unit == nil and nil or EntIndexToHScript(args.unit)
  1212. local contID = args.contID
  1213. local itemID = args.itemID
  1214. local fromSlot = args.fromSlot
  1215. local toContID = args.toContID
  1216. local toSlot = args.toSlot
  1217.  
  1218. if not playerID then return end
  1219. --if unit and unit:GetMainControllingPlayer() ~= playerID then return end
  1220.  
  1221. local container = nil
  1222. if contID == -1 then
  1223. container = unitInventory
  1224. container.unit = unit
  1225. container.range = 150
  1226. if fromSlot > 5 then return end
  1227. else
  1228. container = Containers.containers[contID]
  1229. end
  1230. if not container then return end
  1231.  
  1232. local toContainer = nil
  1233. if toContID == -1 then
  1234. toContainer = unitInventory
  1235. toContainer.unit = unit
  1236. toContainer.range = 150
  1237. if toSlot > 5 then return end
  1238. else
  1239. toContainer = Containers.containers[toContID]
  1240. end
  1241. if not toContainer then return end
  1242.  
  1243. local item = EntIndexToHScript(args.itemID)
  1244. if not (item and IsValidEntity(item) and item.IsItem and item:IsItem()) then return end
  1245.  
  1246. local itemInSlot = container:GetItemInSlot(fromSlot)
  1247. if itemInSlot ~= item then return end
  1248.  
  1249. if toSlot > toContainer:GetSize() then return end
  1250.  
  1251. local range = container:GetRange()
  1252. local ent = container:GetEntity()
  1253. if range == nil and ent and unit ~= ent then return end
  1254. if range and ent and unit and (ent:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D() >= range then
  1255. Containers:DisplayError(playerID,"#dota_hud_error_target_out_of_range")
  1256. return
  1257. end
  1258.  
  1259. if container == toContainer then
  1260. if container.canDragWithin[playerID] == false then return end
  1261.  
  1262. local fun = container._OnDragWithin
  1263. if fun == false then return end
  1264.  
  1265. if type(fun) == "function" then
  1266. fun(playerID, container, unit, item, fromSlot, toSlot)
  1267. else
  1268. Containers:OnDragWithin(playerID, container, unit, item, fromSlot, toSlot)
  1269. end
  1270. else
  1271. if container.canDragFrom[playerID] == false or toContainer.canDragTo[playerID] == false then return end
  1272.  
  1273. local range = toContainer:GetRange()
  1274. local ent = toContainer:GetEntity()
  1275. if range and ent and unit and (ent:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D() >= range then
  1276. Containers:DisplayError(playerID,"#dota_hud_error_target_out_of_range")
  1277. return
  1278. end
  1279.  
  1280. local fun = container._OnDragFrom
  1281. if fun == false then return end
  1282.  
  1283. if type(fun) == "function" then
  1284. fun(playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1285. else
  1286. Containers:OnDragFrom(playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1287. end
  1288. end
  1289. end
  1290.  
  1291. function Containers:Containers_OnDragWorld(args)
  1292. Containers:print('Containers_OnDragWorld')
  1293. Containers:PrintTable(args)
  1294.  
  1295. local playerID = args.PlayerID
  1296. local unit = args.unit == nil and nil or EntIndexToHScript(args.unit)
  1297. local contID = args.contID
  1298. local itemID = args.itemID
  1299. local slot = args.slot
  1300. local position = args.position
  1301. local entity = nil
  1302. if type(args.entity) == "number" then entity = EntIndexToHScript(args.entity) end
  1303.  
  1304. if not playerID then return end
  1305. --if unit and unit:GetMainControllingPlayer() ~= playerID then return end
  1306.  
  1307. local container = nil
  1308. if contID == -1 then
  1309. container = unitInventory
  1310. container.unit = unit
  1311. container.range = nil
  1312. else
  1313. container = Containers.containers[contID]
  1314. end
  1315. if not container then return end
  1316.  
  1317. local fun = container._OnDragWorld
  1318. if fun == false then return end
  1319.  
  1320. local item = EntIndexToHScript(args.itemID)
  1321. if not (item and IsValidEntity(item) and item.IsItem and item:IsItem()) then return end
  1322.  
  1323. local itemInSlot = container:GetItemInSlot(slot)
  1324. if itemInSlot ~= item then return end
  1325.  
  1326. if not position["0"] or not position["1"] or not position["2"] then return end
  1327. position = Vector(position["0"], position["1"], position["2"])
  1328.  
  1329. if container.canDragFrom[playerID] == false then return end
  1330.  
  1331. if not item:IsDroppable() then
  1332. Containers:DisplayError(playerID,"#dota_hud_error_item_cant_be_dropped")
  1333. return
  1334. end
  1335.  
  1336. local range = container:GetRange()
  1337. local ent = container:GetEntity()
  1338. if range == nil and ent and unit ~= ent then return end
  1339. if range and ent and unit and (ent:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D() >= range then
  1340. Containers:DisplayError(playerID,"#dota_hud_error_target_out_of_range")
  1341. return
  1342. end
  1343.  
  1344. if type(fun) == "function" then
  1345. fun(playerID, container, unit, item, slot, position, entity)
  1346. else
  1347. Containers:OnDragWorld(playerID, container, unit, item, slot, position, entity)
  1348. end
  1349. end
  1350.  
  1351. function Containers:Containers_OnCloseClicked(args)
  1352. Containers:print('Containers_OnCloseClicked')
  1353. Containers:PrintTable(args)
  1354.  
  1355. local playerID = args.PlayerID
  1356. local unit = args.unit == nil and nil or EntIndexToHScript(args.unit)
  1357. local contID = args.contID
  1358.  
  1359. if not playerID then return end
  1360.  
  1361. local container = Containers.containers[contID]
  1362. if not container then return end
  1363.  
  1364. local fun = container._OnCloseClicked
  1365. if fun == false then return end
  1366.  
  1367. if type(fun) == "function" then
  1368. fun(playerID, container, unit)
  1369. else
  1370. Containers:OnCloseClicked(playerID, container, unit)
  1371. end
  1372. end
  1373.  
  1374. function Containers:Containers_OnButtonPressed(args)
  1375. Containers:print('Containers_OnButtonPressed')
  1376. Containers:PrintTable(args)
  1377.  
  1378. local playerID = args.PlayerID
  1379. local unit = args.unit == nil and nil or EntIndexToHScript(args.unit)
  1380. local contID = args.contID
  1381. local buttonNumber = args.button
  1382.  
  1383. if not playerID then return end
  1384. --if unit and unit:GetMainControllingPlayer() ~= playerID then return end
  1385.  
  1386. local container = Containers.containers[contID]
  1387. if not container then return end
  1388.  
  1389. local fun = container._OnButtonPressed
  1390. if fun == false then return end
  1391.  
  1392. if buttonNumber < 1 then return end
  1393. local buttonName = container:GetButtonName(buttonNumber)
  1394.  
  1395. if not buttonName then return end
  1396.  
  1397. local range = container:GetRange()
  1398. local ent = container:GetEntity()
  1399. if range == nil and ent and unit ~= ent then return end
  1400. if range and ent and unit and (ent:GetAbsOrigin() - unit:GetAbsOrigin()):Length2D() >= range then
  1401. Containers:DisplayError(playerID,"#dota_hud_error_target_out_of_range")
  1402. return
  1403. end
  1404.  
  1405. if type(fun) == "function" then
  1406. fun(playerID, container, unit, buttonNumber, buttonName)
  1407. else
  1408. Containers:OnButtonPressed(playerID, container, unit, buttonNumber, buttonName)
  1409. end
  1410. end
  1411.  
  1412.  
  1413. function Containers:OnLeftClick(playerID, container, unit, item, slot)
  1414. Containers:print("Containers:OnLeftClick", playerID, container, unit, item:GetEntityIndex(), slot)
  1415.  
  1416. local hero = PlayerResource:GetSelectedHeroEntity(playerID)
  1417. container:ActivateItem(hero, item, playerID)
  1418. end
  1419.  
  1420. function Containers:OnRightClick(playerID, container, unit, item, slot)
  1421. Containers:print("Containers:OnRightClick", playerID, container, unit, item:GetEntityIndex(), slot)
  1422. end
  1423.  
  1424. function Containers:OnDragWithin(playerID, container, unit, item, fromSlot, toSlot)
  1425. Containers:print('Containers:OnDragWithin', playerID, container, unit, item, fromSlot, toSlot)
  1426.  
  1427. container:SwapSlots(fromSlot, toSlot, true)
  1428. end
  1429.  
  1430. function Containers:OnDragFrom(playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1431. Containers:print('Containers:OnDragFrom', playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1432.  
  1433. local canChange = Containers.itemKV[item:GetAbilityName()].ItemCanChangeContainer
  1434. if toContainer._OnDragTo == false or canChange == 0 then return end
  1435.  
  1436. local fun = nil
  1437. if type(toContainer._OnDragTo) == "function" then
  1438. fun = toContainer._OnDragTo
  1439. end
  1440.  
  1441. if fun then
  1442. fun(playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1443. else
  1444. Containers:OnDragTo(playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1445. end
  1446.  
  1447. end
  1448.  
  1449. function Containers:OnDragTo(playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1450. Containers:print('Containers:OnDragTo', playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1451.  
  1452. local item2 = toContainer:GetItemInSlot(toSlot)
  1453. local addItem = nil
  1454. if item2 and IsValidEntity(item2) and (item2:GetAbilityName() ~= item:GetAbilityName() or not item2:IsStackable() or not item:IsStackable()) then
  1455. if Containers.itemKV[item2:GetAbilityName()].ItemCanChangeContainer == 0 then
  1456. return false
  1457. end
  1458. toContainer:RemoveItem(item2)
  1459. addItem = item2
  1460. end
  1461.  
  1462. if toContainer:AddItem(item, toSlot) then
  1463. container:ClearSlot(fromSlot)
  1464. if addItem then
  1465. if container:AddItem(addItem, fromSlot) then
  1466. return true
  1467. else
  1468. toContainer:RemoveItem(item)
  1469. toContainer:AddItem(item2, toSlot, nil, true)
  1470. container:AddItem(item, fromSlot, nil, true)
  1471. return false
  1472. end
  1473. end
  1474. return true
  1475. elseif addItem then
  1476. toContainer:AddItem(item2, toSlot, nil, true)
  1477. end
  1478.  
  1479. return false
  1480. end
  1481.  
  1482. function Containers:OnDragWorld(playerID, container, unit, item, slot, position, entity)
  1483. Containers:print('Containers:OnDragWorld', playerID, container, unit, item, slot, position, entity)
  1484.  
  1485. local unitpos = unit:GetAbsOrigin()
  1486. local diff = unitpos - position
  1487. local dist = diff:Length2D()
  1488.  
  1489. local conts = {}
  1490. if IsValidEntity(entity) then
  1491. conts = Containers:GetEntityContainers(entity:GetEntityIndex())
  1492. end
  1493.  
  1494. local toCont = nil
  1495. for _,cont in ipairs(conts) do
  1496. if cont._OnEntityDrag then
  1497. toCont = cont
  1498. break
  1499. end
  1500. end
  1501.  
  1502. if IsValidEntity(entity) and entity.GetContainedItem and toCont then
  1503. local range = toCont:GetRange() or 150
  1504.  
  1505. Containers:SetRangeAction(unit, {
  1506. unit = unit,
  1507. entity = entity,
  1508. range = range,
  1509. playerID = playerID,
  1510. container = toCont,
  1511. fromContainer = container,
  1512. item = item,
  1513. action = toCont._OnEntityDrag,
  1514. })
  1515. elseif IsValidEntity(entity) and entity:GetTeam() == unit:GetTeam() and entity.HasInventory and entity:HasInventory() and entity:IsAlive() then
  1516. ExecuteOrderFromTable({
  1517. UnitIndex= unit:GetEntityIndex(),
  1518. OrderType= DOTA_UNIT_ORDER_MOVE_TO_TARGET,
  1519. TargetIndex= entity:GetEntityIndex(),
  1520. })
  1521.  
  1522. Containers.rangeActions[unit:GetEntityIndex()] = {
  1523. unit = unit,
  1524. entity = entity,
  1525. range = 180,
  1526. container = container,
  1527. playerID = playerID,
  1528. action = function(playerID, container, unit, target)
  1529. if IsValidEntity(target) and target:IsAlive() and container:ContainsItem(item) then
  1530. container:RemoveItem(item)
  1531. Containers:AddItemToUnit(target, item)
  1532. end
  1533.  
  1534. unit:Stop()
  1535. end,
  1536. }
  1537. elseif IsValidEntity(entity) and entity:GetClassname() == "ent_dota_shop" and item:IsSellable() then
  1538. ExecuteOrderFromTable({
  1539. UnitIndex= unit:GetEntityIndex(),
  1540. OrderType= DOTA_UNIT_ORDER_MOVE_TO_TARGET,
  1541. TargetIndex= entity:GetEntityIndex(),
  1542. })
  1543.  
  1544. Containers.rangeActions[unit:GetEntityIndex()] = {
  1545. unit = unit,
  1546. entity = entity,
  1547. range = 425,
  1548. container = container,
  1549. playerID = playerID,
  1550. action = function(playerID, container, unit, target)
  1551. if IsValidEntity(target) and container:ContainsItem(item) then
  1552. local cost = item:GetCost()
  1553. if GameRules:GetGameTime() - item:GetPurchaseTime() > 10 then
  1554. cost = cost /2
  1555. end
  1556.  
  1557. container:RemoveItem(item)
  1558. item:RemoveSelf()
  1559. PlayerResource:ModifyGold(playerID, cost, false, DOTA_ModifyGold_SellItem)
  1560.  
  1561. local player = PlayerResource:GetPlayer(playerID)
  1562.  
  1563. if player then
  1564. SendOverheadEventMessage(player, OVERHEAD_ALERT_GOLD, unit, cost, player)
  1565. EmitSoundOnClient("General.Sell", player)
  1566. end
  1567. end
  1568.  
  1569. unit:Stop()
  1570. end,
  1571. }
  1572. else
  1573. local pos = unitpos
  1574. if dist > 150 *.9 then
  1575. pos = position + diff:Normalized() * 150 * .9
  1576. end
  1577.  
  1578. --DebugDrawCircle(pos, Vector(255,0,0), 1, 50.0, true, 1)
  1579.  
  1580. --unit:MoveToPosition(pos)
  1581. ExecuteOrderFromTable({
  1582. UnitIndex= unit:GetEntityIndex(),
  1583. OrderType= DOTA_UNIT_ORDER_MOVE_TO_POSITION,
  1584. Position= pos,
  1585. })
  1586.  
  1587. Containers.rangeActions[unit:GetEntityIndex()] = {
  1588. unit = unit,
  1589. --entity = target,
  1590. position = position,
  1591. range = 150,
  1592. container = container,
  1593. playerID = playerID,
  1594. action = function(playerID, container, unit, target)
  1595. if container:ContainsItem(item) then
  1596. container:RemoveItem(item)
  1597. CreateItemOnPositionSync(position, item)
  1598. end
  1599. end,
  1600. }
  1601. end
  1602. end
  1603.  
  1604.  
  1605. function Containers:OnCloseClicked(playerID, container, unit)
  1606. Containers:print('Containers:OnCloseClicked', playerID, container, unit)
  1607. container:Close(playerID)
  1608. end
  1609.  
  1610. function Containers:OnButtonPressed(playerID, container, unit, buttonNumber, buttonName)
  1611. print('Button ' .. buttonNumber .. ':\'' .. buttonName .. '\' Pressed by player:' .. playerID .. ' for container ' .. container.id .. '. No OnButtonPressed handler.')
  1612. end
  1613.  
  1614.  
  1615.  
  1616. function Containers:GetEntityContainers(entity)
  1617. if entity and type(entity) ~= "number" and entity.GetEntityIndex and not entity:IsNull() then
  1618. entity = entity:GetEntityIndex()
  1619. end
  1620.  
  1621. local tab = {}
  1622. for id,cont in pairs(Containers.entityContainers[entity] or {}) do
  1623. table.insert(tab, cont)
  1624. end
  1625. return tab
  1626. end
  1627.  
  1628. function Containers:SetRangeAction(unit, tab)
  1629. if not IsValidEntity(unit) then
  1630. return
  1631. end
  1632.  
  1633. local range = tab.range or 150
  1634. if tab.container then range = (tab.container:GetRange() or 150) end
  1635. local tpos = tab.position or tab.entity:GetAbsOrigin()
  1636. local unitpos = unit:GetAbsOrigin()
  1637. local diff = unitpos - tpos
  1638. local dist = diff:Length2D()
  1639. local pos = unitpos
  1640. if dist > range * .9 then
  1641. pos = tpos + diff:Normalized() * range * .9
  1642. end
  1643.  
  1644. tab.unit = unit
  1645.  
  1646. if tab.entity and not tab.entity.GetContainedItem then
  1647. ExecuteOrderFromTable({
  1648. UnitIndex= unit:GetEntityIndex(),
  1649. OrderType= DOTA_UNIT_ORDER_MOVE_TO_TARGET,
  1650. TargetIndex= tab.entity:GetEntityIndex(),
  1651. })
  1652. else
  1653. ExecuteOrderFromTable({
  1654. UnitIndex= unit:GetEntityIndex(),
  1655. OrderType= DOTA_UNIT_ORDER_MOVE_TO_POSITION,
  1656. Position = pos,
  1657. })
  1658. end
  1659.  
  1660. Containers.rangeActions[unit:GetEntityIndex()] = tab
  1661. end
  1662.  
  1663. function Containers:SetDefaultInventory(unit, container)
  1664. if not self.initialized then
  1665. print('[containers.lua] FATAL: Containers:Init() has not been called in the Activate() function chain!')
  1666. return
  1667. end
  1668. self.defaultInventories[unit:GetEntityIndex()] = container
  1669. end
  1670.  
  1671. function Containers:GetDefaultInventory(unit)
  1672. local di = self.defaultInventories[unit:GetEntityIndex()]
  1673. if IsValidContainer(di) then
  1674. return di
  1675. else
  1676. self.defaultInventories[unit:GetEntityIndex()] = nil
  1677. return nil
  1678. end
  1679. end
  1680.  
  1681. function Containers:CreateShop(cont)
  1682. local shop = self:CreateContainer(cont)
  1683.  
  1684. local ptID = shop.ptID
  1685. local pt = {shop = 1,
  1686. }
  1687.  
  1688. if cont.prices then
  1689. for k,v in pairs(cont.prices) do
  1690. local item = k
  1691. if type(k) ~= "number" then
  1692. item = k:GetEntityIndex()
  1693. end
  1694.  
  1695. pt['price' .. k] = v
  1696. end
  1697. end
  1698.  
  1699. if cont.stocks then
  1700. for k,v in pairs(cont.stocks) do
  1701. local item = k
  1702. if type(k) ~= "number" then
  1703. item = k:GetEntityIndex()
  1704. end
  1705.  
  1706. pt['stock' .. k] = v
  1707. end
  1708. end
  1709.  
  1710. PlayerTables:SetTableValues(ptID, pt)
  1711.  
  1712. function shop:BuyItem(playerID, unit, item)
  1713. local cost = self:GetPrice(item)
  1714. local stock = self:GetStock(item)
  1715. local owner = PlayerResource:GetSelectedHeroEntity(playerID)
  1716. local gold = PlayerResource:GetGold(playerID)
  1717. if gold >= cost and (stock == nil or stock > 0) then
  1718. local newItem = CreateItem(item:GetAbilityName(), owner, owner)
  1719. newItem:SetLevel(item:GetLevel())
  1720. newItem:SetCurrentCharges(item:GetCurrentCharges())
  1721.  
  1722. PlayerResource:SpendGold(playerID, cost, DOTA_ModifyGold_PurchaseItem)
  1723.  
  1724. if stock then
  1725. self:SetStock(item, stock-1)
  1726. end
  1727.  
  1728. Containers:EmitSoundOnClient(playerID, "General.Buy")
  1729.  
  1730. return newItem
  1731. elseif stock ~= nil and stock <= 0 then
  1732. Containers:DisplayError(playerID, "#dota_hud_error_item_out_of_stock")
  1733. elseif gold < cost then
  1734. Containers:DisplayError(playerID, "#dota_hud_error_not_enough_gold")
  1735. end
  1736. end
  1737.  
  1738. function shop:SellItem()
  1739.  
  1740. end
  1741.  
  1742. function shop:GetPrice(item)
  1743. item = GetItem(item)
  1744. return PlayerTables:GetTableValue(ptID, "price" .. item:GetEntityIndex()) or item:GetCost()
  1745. end
  1746. function shop:SetPrice(item, price)
  1747. item = GetItem(item)
  1748. if price then
  1749. PlayerTables:SetTableValue(ptID, "price" .. item:GetEntityIndex(), price)
  1750. else
  1751. PlayerTables:DeleteTableKey(ptID, "price" .. item:GetEntityIndex())
  1752. end
  1753. end
  1754.  
  1755. function shop:GetStock(item)
  1756. item = GetItem(item)
  1757. return PlayerTables:GetTableValue(ptID, "stock" .. item:GetEntityIndex())
  1758. end
  1759. function shop:SetStock(item, stock)
  1760. item = GetItem(item)
  1761. if stock then
  1762. PlayerTables:SetTableValue(ptID, "stock" .. item:GetEntityIndex(), stock)
  1763. else
  1764. PlayerTables:DeleteTableKey(ptID, "stock" .. item:GetEntityIndex())
  1765. end
  1766. end
  1767.  
  1768. if not cont.canDragWithin then shop.canDragWithin = {} end
  1769. shop:AddSkin("ContainerShop")
  1770. if not cont.OnDragWorld then shop:OnDragWorld(false) end
  1771. if not cont.OnDragWithin then shop:OnDragWithin(false) end
  1772. if not cont.OnLeftClick then shop:OnLeftClick(false) end
  1773. if not cont.OnDragTo then
  1774. shop:OnDragTo(function(playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1775. Containers:print('Shop:OnDragTo', playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1776. end)
  1777. end
  1778. if not cont.OnDragFrom then
  1779. shop:OnDragFrom(function(playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1780. Containers:print('Shop:OnDragFrom', playerID, container, unit, item, fromSlot, toContainer, toSlot)
  1781. end)
  1782. end
  1783. --[[shop:OnLeftClick(function(playerID, container, unit, item, slot)
  1784. print("Shop:OnLeftClick", playerID, container, unit, item:GetEntityIndex(), slot)
  1785. end)]]
  1786. if not cont.OnRightClick then
  1787. shop:OnRightClick(function(playerID, container, unit, item, slot)
  1788. Containers:print("Shop:OnRightClick", playerID, container, unit, item:GetEntityIndex(), slot)
  1789.  
  1790. local defInventory = Containers:GetDefaultInventory(unit)
  1791. if not defInventory and not unit:HasInventory() then return end
  1792.  
  1793. local item = container:BuyItem(playerID, unit, item)
  1794. Containers:AddItemToUnit(unit, item)
  1795. end)
  1796. end
  1797.  
  1798.  
  1799. return shop
  1800. end
  1801.  
  1802. function Containers:CreateContainer(cont)
  1803. if not self.initialized then
  1804. print('[containers.lua] FATAL: Containers:Init() has not been called in the Activate() function chain!')
  1805. return
  1806. end
  1807. local pt =
  1808. {id = self.nextID,
  1809. ptID = ID_BASE .. self.nextID,
  1810. --unit = cont.unit
  1811.  
  1812. layout = cont.layout or {2,2},
  1813. size = 0, -- calculated below
  1814. rowStarts = {}, -- calculated below
  1815. --slot1 = 1111, -- set up below
  1816. --slot2 = 1122, -- set up below
  1817. skins = {},
  1818. buttons = cont.buttons or {},
  1819. headerText = cont.headerText or "Container",
  1820. draggable = cont.draggable or true,
  1821. position = cont.position or "100px 200px 0px",
  1822. equipment = cont.equipment,
  1823.  
  1824. layoutFile = cont.layoutFile,
  1825.  
  1826. OnLeftClick = type(cont.OnLeftClick) == "function" and true or cont.OnLeftClick,
  1827. OnRightClick = type(cont.OnRightClick) == "function" and true or cont.OnRightClick,
  1828. OnDragFrom = type(cont.OnDragFrom) == "function" and true or cont.OnDragFrom,
  1829. OnDragWorld = false,
  1830. OnCloseClicked = type(cont.OnCloseClicked) == "function" and true or cont.OnCloseClicked,
  1831. OnButtonPressed = type(cont.OnButtonPressed) == "function" and true or cont.OnButtonPressed,
  1832.  
  1833. OnLeftClickJS = cont.OnLeftClickJS,
  1834. OnRightClickJS = cont.OnRightClickJS,
  1835. OnDoubleClickJS = cont.OnDoubleClickJS,
  1836. OnMouseOutJS = cont.OnMouseOutJS,
  1837. OnMouseOverJS = cont.OnMouseOverJS,
  1838. OnButtonPressedJS=cont.OnButtonPressedJS,
  1839. OnCloseClickedJS =cont.OnCloseClickedJS,
  1840. }
  1841.  
  1842. local c = {id = pt.id,
  1843. ptID = pt.ptID,
  1844. items = {},
  1845. itemNames = {},
  1846. subs = {},
  1847. opens = {},
  1848. range = cont.range,
  1849. closeOnOrder = cont.closeOnOrder == nil and false or cont.closeOnOrder,
  1850.  
  1851. canDragFrom = {},
  1852. canDragTo = {},
  1853. canDragWithin = {},
  1854. appliedPassives = {},
  1855. cleanupTimer = nil,
  1856.  
  1857. forceOwner = cont.forceOwner,
  1858. forcePurchaser = cont.forcePurchaser,
  1859. --entity = nil,
  1860.  
  1861. _OnLeftClick = cont.OnLeftClick,
  1862. _OnRightClick = cont.OnRightClick,
  1863. _OnDragTo = cont.OnDragTo,
  1864. _OnDragFrom = cont.OnDragFrom,
  1865. _OnDragWithin = cont.OnDragWithin,
  1866. _OnDragWorld = cont.OnDragWorld,
  1867. _OnCloseClicked = cont.OnCloseClicked,
  1868. _OnButtonPressed = cont.OnButtonPressed,
  1869. _OnEntityOrder = cont.OnEntityOrder,
  1870. _OnEntityDrag = cont.OnEntityDrag,
  1871. _OnClose = cont.OnClose,
  1872. _OnOpen = cont.OnOpen,
  1873. _OnSelect = cont.OnSelect,
  1874. _OnDeselect = cont.OnDeselect,
  1875. AddItemFilter = cont.AddItemFilter,
  1876. }
  1877.  
  1878. if cont.OnDragWorld ~= nil and (type(cont.OnDragWorld) == "function" or cont.OnDragWorld == true) then
  1879. pt.OnDragWorld = true
  1880. end
  1881.  
  1882. --if cont.setOwner then c.setOwner = cont.setOwner end
  1883. --if cont.setPurchaser then c.setOwner = cont.setPurchaser end
  1884.  
  1885. if cont.entity and type(cont.entity) == "number" then
  1886. pt.entity = cont.entity
  1887. elseif cont.entity and cont.entity.GetEntityIndex then
  1888. pt.entity = cont.entity:GetEntityIndex()
  1889. end
  1890.  
  1891. if pt.entity then
  1892. Containers.entityContainers[pt.entity] = Containers.entityContainers[pt.entity] or {}
  1893. Containers.entityContainers[pt.entity][c.id] = c
  1894. end
  1895.  
  1896. for i,row in ipairs(pt.layout) do
  1897. table.insert(pt.rowStarts, pt.size+1)
  1898. pt.size = pt.size + row
  1899. end
  1900.  
  1901. if cont.skins then
  1902. for k,v in pairs(cont.skins) do
  1903. if type(v) == "string" then
  1904. pt.skins[v] = true
  1905. end
  1906. end
  1907. end
  1908.  
  1909. if cont.items then
  1910. for k,v in pairs(cont.items) do
  1911. if type(k) == "number" then
  1912. local item = v
  1913. if type(v) == "number" then
  1914. item = EntIndexToHScript(item)
  1915. end
  1916.  
  1917. if item and IsValidEntity(item) and item.IsItem and item:IsItem() then
  1918. local itemid = item:GetEntityIndex()
  1919. local itemname = item:GetAbilityName()
  1920. pt['slot' .. k] = itemid
  1921. c.items[itemid] = k
  1922. c.itemNames[itemname] = c.itemNames[itemname] or {}
  1923. c.itemNames[itemname][itemid] = k
  1924.  
  1925. if cont.equipment and pt.entity then
  1926. ApplyPassives(c, item, EntIndexToHScript(pt.entity))
  1927. end
  1928. end
  1929. end
  1930. end
  1931. end
  1932.  
  1933. if cont.equipment then
  1934. c.cleanupTimer = Timers:CreateTimer(1, function()
  1935. for itemID, mods in pairs(c.appliedPassives) do
  1936. if not IsValidEntity(EntIndexToHScript(itemID)) or not c:ContainsItem(itemID) then
  1937. for _, mod in ipairs(mods) do
  1938. mod:Destroy()
  1939. end
  1940. c.appliedPassives[itemID] = nil
  1941. end
  1942. end
  1943. return 1
  1944. end)
  1945. end
  1946.  
  1947. if cont.cantDragFrom then
  1948. for _,pid in ipairs(cont.cantDragFrom) do
  1949. if type(pid) == "number" then
  1950. c.canDragFrom[pid] = false
  1951. end
  1952. end
  1953. end
  1954.  
  1955. if cont.cantDragTo then
  1956. for _,pid in ipairs(cont.cantDragTo) do
  1957. if type(pid) == "number" then
  1958. c.canDragTo[pid] = false
  1959. end
  1960. end
  1961. end
  1962.  
  1963. if cont.pids then
  1964. for _,pid in ipairs(cont.pids) do
  1965. c.subs[pid] = true
  1966. end
  1967. end
  1968.  
  1969. PlayerTables:CreateTable(c.ptID, pt, c.subs)
  1970.  
  1971. function c:ActivateItem(unit, item, playerID)
  1972. if item:GetOwner() ~= unit or not item:IsFullyCastable() then
  1973. Containers:EmitSoundOnClient(playerID, "General.Cancel")
  1974. return
  1975. end
  1976.  
  1977. local playerID = playerID or unit:GetPlayerOwnerID()
  1978.  
  1979. local behavior = item:GetBehavior()
  1980. local targetType = item:GetAbilityTargetType()
  1981. local toggle = bit.band(behavior, DOTA_ABILITY_BEHAVIOR_TOGGLE) ~= 0
  1982. local unrestricted = bit.band(behavior, DOTA_ABILITY_BEHAVIOR_UNRESTRICTED) ~= 0
  1983. local rootDisables = bit.band(behavior, DOTA_ABILITY_BEHAVIOR_ROOT_DISABLES) ~= 0
  1984. local channelled = bit.band(behavior, DOTA_ABILITY_BEHAVIOR_CHANNELLED) ~= 0
  1985. local noTarget = bit.band(behavior, DOTA_ABILITY_BEHAVIOR_NO_TARGET) ~= 0
  1986. local treeTarget = bit.band(targetType, DOTA_UNIT_TARGET_TREE) ~= 0
  1987.  
  1988. if unit:IsStunned() and not unrestricted then
  1989. Containers:DisplayError(playerID, "#dota_hud_error_unit_command_restricted")
  1990. return
  1991. end
  1992. if unit:IsRooted() and rootDisables then
  1993. Containers:DisplayError(playerID, "#dota_hud_error_ability_disabled_by_root")
  1994. return
  1995. end
  1996. if noTarget and not channelled then
  1997. item:PayGoldCost()
  1998. item:PayManaCost()
  1999. item:StartCooldown(item:GetCooldown(item:GetLevel()))
  2000. if toggle then
  2001. item:OnToggle()
  2002. else
  2003. item:OnSpellStart()
  2004. end
  2005. else
  2006. local abil = unit:FindAbilityByName("containers_lua_targeting")
  2007. if treeTarget then
  2008. if unit:HasAbility("containers_lua_targeting") then unit:RemoveAbility("containers_lua_targeting") end
  2009. abil = unit:FindAbilityByName("containers_lua_targeting_tree")
  2010. elseif unit:HasAbility("containers_lua_targeting_tree") then
  2011. unit:RemoveAbility("containers_lua_targeting_tree")
  2012. end
  2013. if not abil then
  2014. -- no ability proxy found, add
  2015. local abilSlot = -1
  2016. for i=15,0,-1 do
  2017. local ab = unit:GetAbilityByIndex(i)
  2018. if not ab then
  2019. abilSlot = i
  2020. break
  2021. end
  2022. end
  2023.  
  2024. if abilSlot == -1 then
  2025. print("[containers.lua] ERROR: 'containers_lua-targeting' ability not found for unit '" .. unit:GetUnitName() .. '" and all ability slots are full.')
  2026. return
  2027. end
  2028.  
  2029. if treeTarget then
  2030. abil = unit:AddAbility("containers_lua_targeting_tree")
  2031. --abil = unit:FindAbilityByName("containers_lua_targeting_tree")
  2032. else
  2033. abil = unit:AddAbility("containers_lua_targeting")
  2034. --abil = unit:FindAbilityByName("containers_lua_targeting")
  2035. end
  2036. abil:SetLevel(1)
  2037. end
  2038.  
  2039. abil:SetHidden(false)
  2040. abil.proxyItem = item
  2041.  
  2042. local aoe = nil
  2043. local iname = item:GetAbilityName()
  2044. if iname == "item_veil_of_discord" then
  2045. aoe = 600
  2046. elseif Containers.itemKV[iname] then
  2047. aoe = Containers.itemKV[iname].AOERadius
  2048. end
  2049.  
  2050. CustomNetTables:SetTableValue("containers_lua", tostring(abil:GetEntityIndex()), {behavior=behavior, aoe=aoe, range=item:GetCastRange(),
  2051. targetType=targetType, targetTeam=item:GetAbilityTargetTeam(), targetFlags=item:GetAbilityTargetFlags(),
  2052. channelTime=item:GetChannelTime(), channelCost=item:GetChannelledManaCostPerSecond(item:GetLevel()),
  2053. proxyItem=item:GetEntityIndex()})
  2054.  
  2055. local player = PlayerResource:GetPlayer(playerID)
  2056. if player then
  2057. CustomGameEventManager:Send_ServerToPlayer(player, "cont_execute_proxy", {unit=unit:GetEntityIndex()})
  2058. end
  2059. end
  2060. end
  2061.  
  2062. function c:GetAllOpen()
  2063. return self.opens
  2064. end
  2065.  
  2066. function c:IsOpen(pid)
  2067. return self.opens[pid] ~= nil
  2068. end
  2069.  
  2070. function c:Open(pid)
  2071. self.opens[pid] = true
  2072. PlayerTables:AddPlayerSubscription(self.ptID, pid)
  2073.  
  2074. if self:IsCloseOnOrder() then
  2075. Containers.closeOnOrders[pid][self.id] = self
  2076. end
  2077.  
  2078. local player = PlayerResource:GetPlayer(pid)
  2079. if player then
  2080. CustomGameEventManager:Send_ServerToPlayer(player, "cont_open_container", {id=self.id} )
  2081. end
  2082.  
  2083. if self._OnOpen then
  2084. self._OnOpen(pid, self)
  2085. end
  2086. end
  2087.  
  2088. function c:Close(pid)
  2089. if self.opens[pid] == nil then return end
  2090.  
  2091. self.opens[pid] = nil
  2092. if not self.subs[pid] then
  2093. PlayerTables:RemovePlayerSubscription(self.ptID, pid)
  2094. end
  2095.  
  2096. if self:IsCloseOnOrder() then
  2097. Containers.closeOnOrders[pid][self.id] = nil
  2098. end
  2099.  
  2100. local player = PlayerResource:GetPlayer(pid)
  2101. if player then
  2102. CustomGameEventManager:Send_ServerToPlayer(player, "cont_close_container", {id=self.id} )
  2103. end
  2104.  
  2105. if self._OnClose then
  2106. self._OnClose(pid, self)
  2107. end
  2108. end
  2109.  
  2110. function c:Delete(deleteContents)
  2111. Containers:DeleteContainer(self, deleteContents)
  2112. end
  2113.  
  2114. function c:AddSubscription(pid)
  2115. self.subs[pid] = true
  2116. PlayerTables:AddPlayerSubscription(self.ptID, pid)
  2117. end
  2118.  
  2119. function c:RemoveSubscription(pid)
  2120. self.subs[pid] = nil
  2121. if not self.opens[pid] then
  2122. PlayerTables:RemovePlayerSubscription(self.ptID, pid)
  2123. end
  2124. end
  2125.  
  2126. function c:GetSubscriptions()
  2127. return self.subs
  2128. end
  2129.  
  2130. function c:IsSubscribed(pid)
  2131. return self.subs[pid] ~= nil
  2132. end
  2133.  
  2134. function c:SwapSlots(slot1, slot2, allowCombine)
  2135. local item1 = self:GetItemInSlot(slot1)
  2136. local item2 = self:GetItemInSlot(slot2)
  2137.  
  2138. if item1 and item2 then
  2139. return self:SwapItems(item1, item2, allowCombine)
  2140. elseif item1 then
  2141. local itemid = item1:GetEntityIndex()
  2142. local itemname = item1:GetAbilityName()
  2143. self.items[itemid] = slot2
  2144. self.itemNames[itemname][itemid] = slot2
  2145.  
  2146. PlayerTables:SetTableValue(self.ptID, "slot"..slot2, itemid)
  2147. PlayerTables:DeleteTableKey(self.ptID, "slot"..slot1)
  2148. return true
  2149. elseif item2 then
  2150. local itemid = item2:GetEntityIndex()
  2151. local itemname = item2:GetAbilityName()
  2152. self.items[itemid] = slot1
  2153. self.itemNames[itemname][itemid] = slot1
  2154.  
  2155. PlayerTables:SetTableValue(self.ptID, "slot"..slot1, itemid)
  2156. PlayerTables:DeleteTableKey(self.ptID, "slot"..slot2)
  2157. return true
  2158. end
  2159. return false
  2160. end
  2161.  
  2162. function c:SwapItems(item1, item2, allowCombine)
  2163. item1 = GetItem(item1)
  2164. item2 = GetItem(item2)
  2165.  
  2166. local i1id = item1:GetEntityIndex()
  2167. local i1name = item1:GetAbilityName()
  2168. local i2id = item2:GetEntityIndex()
  2169. local i2name = item2:GetAbilityName()
  2170.  
  2171. local i1 = self.items[i1id]
  2172. local i2 = self.items[i2id]
  2173.  
  2174. if i1 and i2 then
  2175. if allowCombine and i1name == i2name and item1:IsStackable() and item2:IsStackable() then
  2176. self:RemoveItem(item1)
  2177. item2:SetCurrentCharges(item2:GetCurrentCharges() + item1:GetCurrentCharges())
  2178. item1:RemoveSelf()
  2179. return true
  2180. end
  2181. self.items[i1id] = i2
  2182. self.items[i2id] = i1
  2183. self.itemNames[i1name][i1id] = i2
  2184. self.itemNames[i2name][i2id] = i1
  2185.  
  2186. PlayerTables:SetTableValues(self.ptID, {["slot"..i1]=i2id, ["slot"..i2]=i1id})
  2187. return true
  2188. end
  2189.  
  2190. return false
  2191. end
  2192.  
  2193. function c:ContainsItem(item)
  2194. item = GetItem(item)
  2195. if not item then return false end
  2196. return self.items[item:GetEntityIndex()] ~= nil
  2197. end
  2198.  
  2199. function c:GetSlotForItem(item)
  2200. item = GetItem(item)
  2201. return self.items[item:GetEntityIndex()]
  2202. end
  2203.  
  2204. function c:GetRowColumnForItem(item)
  2205. item = GetItem(item)
  2206.  
  2207. local itemid = item:GetEntityIndex()
  2208. local slot = self.items[itemid]
  2209. if not slot then
  2210. return nil, nil
  2211. end
  2212.  
  2213. local size = self:GetSize()
  2214. if slot > size then
  2215. return nil, nil
  2216. end
  2217. local rowStarts = PlayerTables:GetTableValue(self.ptID, "rowStarts")
  2218. for row,start in ipairs(rowStarts) do
  2219. if start > slot then
  2220. local prev = row-1
  2221. return prev, (slot - rowStarts[prev] + 1)
  2222. end
  2223. end
  2224.  
  2225. local row = #rowStarts
  2226.  
  2227. return row, (slot - rowStarts[row] + 1)
  2228. end
  2229.  
  2230. function c:GetAllItems()
  2231. local items = {}
  2232. for slot=1,self:GetSize() do
  2233. local item = self:GetItemInSlot(slot)
  2234. if item then
  2235. table.insert(items, item)
  2236. end
  2237. end
  2238.  
  2239. return items
  2240. end
  2241.  
  2242. function c:GetItemsByName(name)
  2243. local nameTable = self.itemNames[name]
  2244. local items = {}
  2245. if not nameTable then
  2246. return items
  2247. end
  2248.  
  2249. for id,slot in pairs(nameTable) do
  2250. local item = GetItem(id)
  2251. if item then
  2252. table.insert(items, item)
  2253. else
  2254. nameTable[id] = nil
  2255. end
  2256. end
  2257. return items
  2258. end
  2259.  
  2260. function c:GetItemInSlot(slot)
  2261. local item = PlayerTables:GetTableValue(self.ptID, "slot" .. slot)
  2262. if item then
  2263. item = EntIndexToHScript(item)
  2264. if item and not IsValidEntity(item) then
  2265. PlayerTables:DeleteTableKey(self.ptID, "slot" .. slot)
  2266. return nil
  2267. elseif item and IsValidEntity(item) and item.IsItem and item:IsItem() then
  2268. return item
  2269. end
  2270. end
  2271.  
  2272. return nil
  2273. end
  2274.  
  2275. function c:GetItemInRowColumn(row, column)
  2276. local rowStarts = PlayerTables:GetTableValue(self.ptID, "rowStarts")
  2277. if not rowStarts[row] then
  2278. return nil
  2279. end
  2280.  
  2281. local nextRowStart = rowStarts[row+1] or self:GetSize() + 1
  2282. local slot = rowStarts[row] + column - 1
  2283.  
  2284. if slot >= nextRowStart then
  2285. return nil
  2286. end
  2287.  
  2288. return self:GetItemInSlot(slot)
  2289. end
  2290.  
  2291. function c:AddItem(item, slot, column, bypassFilter)
  2292. item = GetItem(item)
  2293. if slot and type(slot) == "number" and column and type(column) == "number" then
  2294. local rowStarts = PlayerTables:GetTableValue(self.ptID, "rowStarts")
  2295. if not rowStarts[slot] then
  2296. print("[containers.lua] Request to add item in row " .. slot .. " -- row not found. ")
  2297. return false
  2298. end
  2299.  
  2300. local nextRowStart = rowStarts[slot+1] or self:GetSize() + 1
  2301. local newslot = rowStarts[slot] + column - 1
  2302.  
  2303. if newslot >= nextRowStart then
  2304. print("[containers.lua] Request to add item in row " .. slot .. ", column " .. column .. " -- column exceeds row length. ")
  2305. return false
  2306. end
  2307.  
  2308. slot = newslot
  2309. end
  2310.  
  2311. local findStack = slot == nil
  2312. slot = slot or 1
  2313.  
  2314. local size = self:GetSize()
  2315. if slot > size then
  2316. print("[containers.lua] Request to add item in slot " .. slot .. " -- exceeding container size " .. size)
  2317. return false
  2318. end
  2319.  
  2320. local func = c.AddItemFilter
  2321. if not bypassFilter and c.AddItemFilter then
  2322. local status, result = pcall(c.AddItemFilter, c, item, findstack and -1 or slot)
  2323. if not status then
  2324. print("[containers.lua] AddItemFilter callback error: " .. result)
  2325. return false
  2326. end
  2327.  
  2328. if not result then
  2329. return false
  2330. end
  2331. end
  2332.  
  2333. local itemid = item:GetEntityIndex()
  2334. local itemname = item:GetAbilityName()
  2335.  
  2336. if findStack and item:IsStackable() then
  2337. local nameTable = self.itemNames[itemname]
  2338. if nameTable then
  2339. local lowestSlot = size+1
  2340. local lowestItem = nil
  2341. for itemid, nameslot in pairs(nameTable) do
  2342. if nameslot < lowestSlot then
  2343. local item = self:GetItemInSlot(nameslot)
  2344. if item then
  2345. lowestSlot = nameslot
  2346. lowestItem = item
  2347. else
  2348. nameTable[itemid] = nil
  2349. end
  2350. end
  2351. end
  2352.  
  2353. if lowestItem and lowestItem:IsStackable() then
  2354. lowestItem:SetCurrentCharges(lowestItem:GetCurrentCharges() + item:GetCurrentCharges())
  2355. item:RemoveSelf()
  2356. return true
  2357. end
  2358. end
  2359. end
  2360.  
  2361. -- check if the slot specified is stackable
  2362. if not findStack and item:IsStackable() then
  2363. local slotitem = self:GetItemInSlot(slot)
  2364.  
  2365. if slotitem and itemname == slotitem:GetAbilityName() and slotitem:IsStackable() then
  2366. slotitem:SetCurrentCharges(slotitem:GetCurrentCharges() + item:GetCurrentCharges())
  2367. item:RemoveSelf()
  2368. return true
  2369. end
  2370. end
  2371.  
  2372. for i=slot,size do
  2373. local slotitem = self:GetItemInSlot(i)
  2374. if not slotitem then
  2375.  
  2376. self.items[itemid] = i
  2377. self.itemNames[itemname] = self.itemNames[itemname] or {}
  2378. self.itemNames[itemname][itemid] = i
  2379.  
  2380. if self.forceOwner then
  2381. item:SetOwner(self.forceOwner)
  2382. elseif self.forceOwner == false then
  2383. item:SetOwner(nil)
  2384. end
  2385. if self.forcePurchaser then
  2386. item:SetPurchaser(self.forcePurchaser)
  2387. elseif self.forceOwner == false then
  2388. item:SetPurchaser(nil)
  2389. end
  2390.  
  2391. if self:IsEquipment() then
  2392. ApplyPassives(self, item)
  2393. end
  2394.  
  2395. PlayerTables:SetTableValue(self.ptID, "slot" .. i, itemid)
  2396. return true
  2397. end
  2398. end
  2399.  
  2400. return false
  2401. end
  2402.  
  2403. function c:AddItemBush(item, slot, column, bypassFilter)
  2404. item = GetItem(item)
  2405. if slot and type(slot) == "number" and column and type(column) == "number" then
  2406. local rowStarts = PlayerTables:GetTableValue(self.ptID, "rowStarts")
  2407. if not rowStarts[slot] then
  2408. print("[containers.lua] Request to add item in row " .. slot .. " -- row not found. ")
  2409. return false
  2410. end
  2411.  
  2412. local nextRowStart = rowStarts[slot+1] or self:GetSize() + 1
  2413. local newslot = rowStarts[slot] + column - 1
  2414.  
  2415. if newslot >= nextRowStart then
  2416. print("[containers.lua] Request to add item in row " .. slot .. ", column " .. column .. " -- column exceeds row length. ")
  2417. return false
  2418. end
  2419.  
  2420. slot = newslot
  2421. end
  2422.  
  2423. local findStack = slot == nil
  2424. slot = slot or 1
  2425.  
  2426. local size = self:GetSize()
  2427. if slot > size then
  2428. print("[containers.lua] Request to add item in slot " .. slot .. " -- exceeding container size " .. size)
  2429. return false
  2430. end
  2431.  
  2432. local func = c.AddItemFilter
  2433. if not bypassFilter and c.AddItemFilter then
  2434. local status, result = pcall(c.AddItemFilter, c, item, findstack and -1 or slot)
  2435. if not status then
  2436. print("[containers.lua] AddItemFilter callback error: " .. result)
  2437. return false
  2438. end
  2439.  
  2440. if not result then
  2441. return false
  2442. end
  2443. end
  2444.  
  2445. local itemid = item:GetEntityIndex()
  2446. local itemname = item:GetAbilityName()
  2447. local maxStacks = GameRules.ItemKV[itemname]["MaxStacks"]
  2448.  
  2449. if findStack and maxStacks then
  2450. print("adding to container stackable")
  2451. local nameTable = self.itemNames[itemname]
  2452. if nameTable then
  2453. local lowestSlot = size+1
  2454. local lowestItem = nil
  2455. for itemid, nameslot in pairs(nameTable) do
  2456. if nameslot < lowestSlot then
  2457. local item = self:GetItemInSlot(nameslot)
  2458. if item then -- This for loop was kinda meaningless before...
  2459. if item:GetCurrentCharges() < maxStacks then -- It always went through the slots and returned the first one
  2460. lowestSlot = nameslot -- Now it only returns a slot that doesn't have maxStacks yet.
  2461. lowestItem = item -- Not sure if Line 2345 should get the same treatment.
  2462. end
  2463. else
  2464. nameTable[itemid] = nil
  2465. end
  2466. end
  2467. end
  2468.  
  2469. if lowestItem and GameRules.ItemKV[lowestItem:GetAbilityName()]["MaxStacks"] then
  2470. local chargesToAdd = item:GetCurrentCharges()
  2471. if chargesToAdd + lowestItem:GetCurrentCharges() > maxStacks then
  2472. chargesToAdd = maxStacks - lowestItem:GetCurrentCharges()
  2473. end
  2474. lowestItem:SetCurrentCharges(lowestItem:GetCurrentCharges() + chargesToAdd)
  2475. if item:GetCurrentCharges() - chargesToAdd <= 0 then
  2476. --print("only adding charges " .. lowestItem:GetCurrentCharges())
  2477. item:RemoveSelf()
  2478. else
  2479. item:SetCurrentCharges(item:GetCurrentCharges() - chargesToAdd)
  2480. for i=slot,size do
  2481. --print("looping to find open slot " .. slot .. " size " .. size)
  2482. local slotitem = self:GetItemInSlot(i)
  2483. if not slotitem then
  2484.  
  2485. self.items[itemid] = i
  2486. self.itemNames[itemname] = self.itemNames[itemname] or {}
  2487. self.itemNames[itemname][itemid] = i
  2488.  
  2489. if self.forceOwner then
  2490. item:SetOwner(self.forceOwner)
  2491. elseif self.forceOwner == false then
  2492. item:SetOwner(nil)
  2493. end
  2494. if self.forcePurchaser then
  2495. item:SetPurchaser(self.forcePurchaser)
  2496. elseif self.forceOwner == false then
  2497. item:SetPurchaser(nil)
  2498. end
  2499.  
  2500. if self:IsEquipment() then
  2501. ApplyPassives(self, item)
  2502. end
  2503.  
  2504. PlayerTables:SetTableValue(self.ptID, "slot" .. i, itemid)
  2505. print("added item and charges")
  2506. return true
  2507. end
  2508. end
  2509. print("all slots full in container")
  2510. return false
  2511. end
  2512. return true
  2513. end
  2514. end
  2515. end
  2516.  
  2517. -- check if the slot specified is stackable
  2518. if not findStack and item:IsStackable() then
  2519. local slotitem = self:GetItemInSlot(slot)
  2520.  
  2521. if slotitem and itemname == slotitem:GetAbilityName() and slotitem:IsStackable() then
  2522. slotitem:SetCurrentCharges(slotitem:GetCurrentCharges() + item:GetCurrentCharges())
  2523. item:RemoveSelf()
  2524. return true
  2525. end
  2526. end
  2527.  
  2528. for i=slot,size do
  2529. local slotitem = self:GetItemInSlot(i)
  2530. if not slotitem then
  2531.  
  2532. self.items[itemid] = i
  2533. self.itemNames[itemname] = self.itemNames[itemname] or {}
  2534. self.itemNames[itemname][itemid] = i
  2535.  
  2536. if self.forceOwner then
  2537. item:SetOwner(self.forceOwner)
  2538. elseif self.forceOwner == false then
  2539. item:SetOwner(nil)
  2540. end
  2541. if self.forcePurchaser then
  2542. item:SetPurchaser(self.forcePurchaser)
  2543. elseif self.forceOwner == false then
  2544. item:SetPurchaser(nil)
  2545. end
  2546.  
  2547. if self:IsEquipment() then
  2548. ApplyPassives(self, item)
  2549. end
  2550.  
  2551. PlayerTables:SetTableValue(self.ptID, "slot" .. i, itemid)
  2552. return true
  2553. end
  2554. end
  2555.  
  2556. return false
  2557. end
  2558.  
  2559. function c:RemoveItem(item)
  2560. item = GetItem(item)
  2561. local slot = self.items[item:GetEntityIndex()]
  2562. local nameTable = self.itemNames[item:GetAbilityName()]
  2563. local itemid = item:GetEntityIndex()
  2564.  
  2565. self.items[itemid] = nil
  2566. nameTable[itemid] = nil
  2567.  
  2568. if self:IsEquipment() then
  2569. local mods = self.appliedPassives[itemid]
  2570. if mods then
  2571. for _, mod in ipairs(mods) do
  2572. mod:Destroy()
  2573. end
  2574. end
  2575. self.appliedPassives[itemid] = nil
  2576. end
  2577.  
  2578. PlayerTables:DeleteTableKey(self.ptID, "slot" .. slot)
  2579. end
  2580.  
  2581. function c:ClearSlot(slot)
  2582. local item = self:GetItemInSlot(slot)
  2583. if IsValidEntity(item) then
  2584. self:RemoveItem(item)
  2585. else
  2586. PlayerTables:DeleteTableKey(self.ptID, "slot" .. slot)
  2587. end
  2588. end
  2589.  
  2590. function c:GetContainerIndex()
  2591. return self.id
  2592. end
  2593.  
  2594. function c:GetHeaderText()
  2595. local headerText = PlayerTables:GetTableValue(self.ptID, "headerText")
  2596. return headerText
  2597. end
  2598.  
  2599. function c:SetHeaderText(header)
  2600. PlayerTables:SetTableValue(self.ptID, "headerText", header)
  2601. end
  2602.  
  2603. function c:GetSize()
  2604. local size = PlayerTables:GetTableValue(self.ptID, "size")
  2605. return size
  2606. end
  2607.  
  2608. function c:GetNumItems()
  2609. return #c:GetAllItems()
  2610. end
  2611.  
  2612. function c:GetLayout()
  2613. local layout = PlayerTables:GetTableValue(self.ptID, "layout")
  2614. return layout
  2615. end
  2616.  
  2617. function c:SetLayout(layout, removeOnContract)
  2618. local size = 0
  2619. local rowStarts = {}
  2620.  
  2621. for i,row in ipairs(layout) do
  2622. table.insert(rowStarts, size+1)
  2623. size = size + row
  2624. end
  2625.  
  2626. local oldSize = self:GetSize()
  2627. local changes = {}
  2628.  
  2629. if removeOnContract and size < oldSize then
  2630. local deletions = {}
  2631. for i=size+1,oldSize do
  2632. local item = self:GetItemInSlot(i)
  2633. if item then
  2634. local itemid = item:GetEntityIndex()
  2635. local itemname = item:GetAbilityName()
  2636. local nameTable = self.itemNames[item:GetAbilityName()]
  2637.  
  2638. self.items[item:GetEntityIndex()] = nil
  2639. nameTable[item:GetEntityIndex()] = nil
  2640.  
  2641. if self:IsEquipment() then
  2642. local mods = self.appliedPassives[itemid]
  2643. if mods then
  2644. for _, mod in ipairs(mods) do
  2645. mod:Destroy()
  2646. end
  2647. end
  2648. self.appliedPassives[itemid] = nil
  2649. end
  2650.  
  2651. table.insert(deletions, "slot"..i)
  2652. end
  2653. end
  2654.  
  2655. PlayerTables:DeleteTableKeys(self.ptID, deletions)
  2656. end
  2657.  
  2658. changes.layout = layout
  2659. changes.size = size
  2660. changes.rowStarts = rowStarts
  2661.  
  2662. PlayerTables:SetTableValues(self.ptID, changes)
  2663. end
  2664.  
  2665. function c:GetRange()
  2666. return self.range
  2667. end
  2668.  
  2669. function c:SetRange(range)
  2670. self.range = range
  2671. --[[if range then
  2672. PlayerTables:SetTableValue(self.ptID, "range", range)
  2673. else
  2674. PlayerTables:DeleteTableKey(self.ptID, "range")
  2675. end]]
  2676. end
  2677.  
  2678. function c:AddSkin(skin)
  2679. local skins = PlayerTables:GetTableValue(self.ptID, "skins")
  2680. skins[skin] = true
  2681.  
  2682. PlayerTables:SetTableValue(self.ptID, "skins", skins)
  2683. end
  2684.  
  2685. function c:RemoveSkin(skin)
  2686. local skins = PlayerTables:GetTableValue(self.ptID, "skins")
  2687. skins[skin] = nil
  2688.  
  2689. PlayerTables:SetTableValue(self.ptID, "skins", skins)
  2690. end
  2691.  
  2692. function c:GetSkins()
  2693. local skins = PlayerTables:GetTableValue(self.ptID, "skins")
  2694. return skins
  2695. end
  2696.  
  2697. function c:HasSkin(skin)
  2698. local skins = PlayerTables:GetTableValue(self.ptID, "skins")
  2699. return skins[skin] ~= nil
  2700. end
  2701.  
  2702. function c:SetButton(number, name)
  2703. local buttons = PlayerTables:GetTableValue(self.ptID, "buttons")
  2704. buttons[number] = name
  2705.  
  2706. PlayerTables:SetTableValue(self.ptID, "buttons", buttons)
  2707. end
  2708.  
  2709. function c:RemoveButton(number)
  2710. local buttons = PlayerTables:GetTableValue(self.ptID, "buttons")
  2711. buttons[number] = nil
  2712.  
  2713. PlayerTables:SetTableValue(self.ptID, "buttons", buttons)
  2714. end
  2715.  
  2716. function c:GetButtons()
  2717. local buttons = PlayerTables:GetTableValue(self.ptID, "buttons")
  2718. return buttons
  2719. end
  2720.  
  2721. function c:GetButtonName(number)
  2722. local buttons = PlayerTables:GetTableValue(self.ptID, "buttons")
  2723. return buttons[number]
  2724. end
  2725.  
  2726. function c:GetEntity()
  2727. local entity = PlayerTables:GetTableValue(self.ptID, "entity")
  2728. if entity then
  2729. return EntIndexToHScript(entity)
  2730. end
  2731. return nil
  2732. end
  2733.  
  2734. function c:SetEntity(entity)
  2735. local old = c:GetEntity()
  2736. local num = entity
  2737. if entity and type(entity) == "number" then
  2738. entity = EntIndexToHScript(entity)
  2739. elseif entity and entity.GetEntityIndex then
  2740. num = entity:GetEntityIndex()
  2741. end
  2742.  
  2743. if entity then
  2744. PlayerTables:SetTableValue(self.ptID, "entity", num)
  2745. Containers.entityContainers[num] = Containers.entityContainers[num] or {}
  2746. Containers.entityContainers[num][self.id] = self
  2747. elseif old ~= nil then
  2748. PlayerTables:DeleteTableKey(self.ptID, "entity")
  2749. Containers.entityContainers[old:GetEntityIndex()] = Containers.entityContainers[old:GetEntityIndex()] or {}
  2750. Containers.entityContainers[old:GetEntityIndex()][self.id] = nil
  2751. end
  2752.  
  2753. if self:IsEquipment() and old ~= entity then
  2754. for itemID, mods in pairs(self.appliedPassives) do
  2755. for _, mod in ipairs(mods) do
  2756. mod:Destroy()
  2757. end
  2758. end
  2759. self.appliedPassives = {}
  2760.  
  2761. local items = self:GetAllItems()
  2762. for _, item in ipairs(items) do
  2763. ApplyPassives(self, item)
  2764. end
  2765. end
  2766. end
  2767.  
  2768. function c:GetCanDragFromPlayers()
  2769. return self.canDragFrom
  2770. end
  2771.  
  2772. function c:CanDragFrom(pid)
  2773. return self.canDragFrom[pid] ~= false
  2774. end
  2775.  
  2776. function c:SetCanDragFrom(pid, canDrag)
  2777. self.canDragFrom[pid] = canDrag
  2778. end
  2779.  
  2780. function c:GetCanDragToPlayers()
  2781. return self.canDragTo
  2782. end
  2783.  
  2784. function c:CanDragTo(pid)
  2785. return self.canDragTo[pid] ~= false
  2786. end
  2787.  
  2788. function c:SetCanDragTo(pid, canDrag)
  2789. self.canDragTo[pid] = canDrag
  2790. end
  2791.  
  2792. function c:GetCanDragWithinPlayers()
  2793. return self.canDragWithin
  2794. end
  2795.  
  2796. function c:CanDragWithin(pid)
  2797. return self.canDragWithin[pid] ~= false
  2798. end
  2799.  
  2800. function c:SetCanDragWithin(pid, canDrag)
  2801. self.canDragWithin[pid] = canDrag
  2802. end
  2803.  
  2804. function c:IsDraggable()
  2805. return PlayerTables:GetTableValue(self.ptID, "draggable")
  2806. end
  2807.  
  2808. function c:SetDraggable(drag)
  2809. PlayerTables:SetTableValue(self.ptID, "draggable", drag)
  2810. end
  2811.  
  2812. function c:IsEquipment()
  2813. local eq = PlayerTables:GetTableValue(self.ptID, "equipment")
  2814. if eq then
  2815. return true
  2816. else
  2817. return false
  2818. end
  2819. end
  2820.  
  2821. function c:SetEquipment(equip)
  2822. local isEq = self:IsEquipment()
  2823. if equip and not isEq then
  2824. local items = self:GetAllItems()
  2825. for _, item in ipairs(items) do
  2826. ApplyPassives(self, item)
  2827. end
  2828.  
  2829. local c = self
  2830.  
  2831. self.cleanupTimer = Timers:CreateTimer(1, function()
  2832. for itemID, mods in pairs(c.appliedPassives) do
  2833. if not IsValidEntity(EntIndexToHScript(itemID)) or not c:ContainsItem(itemID) then
  2834. for _, mod in ipairs(mods) do
  2835. mod:Destroy()
  2836. end
  2837. c.appliedPassives[itemID] = nil
  2838. end
  2839. end
  2840. return 1
  2841. end)
  2842. elseif not equip and isEq then
  2843. local items = self:GetAllItems()
  2844. for itemID,mods in pairs(self.appliedPassives) do
  2845. for _, mod in ipairs(mods) do
  2846. mod:Destroy()
  2847. end
  2848. end
  2849. self.appliedPassives = {}
  2850.  
  2851. Timers:RemoveTimer(self.cleanupTimer)
  2852. end
  2853. PlayerTables:SetTableValue(self.ptID, "equipment", equip)
  2854. end
  2855.  
  2856. function c:GetForceOwner()
  2857. return self.forceOwner
  2858. end
  2859.  
  2860. function c:GetForcePurchaser()
  2861. return self.ForcePurchaser
  2862. end
  2863.  
  2864. function c:SetForceOwner(owner)
  2865. self.forceOwner = owner
  2866. end
  2867.  
  2868. function c:SetForcePurchaser(purchaser)
  2869. self.ForcePurchaser = purchaser
  2870. end
  2871.  
  2872. function c:IsCloseOnOrder()
  2873. return self.closeOnOrder
  2874. end
  2875.  
  2876. function c:SetCloseOnOrder(close)
  2877. if close then
  2878. for pid, _ in pairs(self.opens) do
  2879. Containers.closeOnOrders[pid][self.id] = self
  2880. end
  2881. else
  2882. for pid,v in pairs(Containers.closeOnOrders) do
  2883. v[self.id] = nil
  2884. end
  2885. end
  2886. self.closeOnOrder = close
  2887. end
  2888.  
  2889. function c:OnLeftClick(fun)
  2890. if fun == nil then
  2891. PlayerTables:DeleteTableKey(self.ptID, "OnLeftClick")
  2892. elseif type(fun) == "function" then
  2893. PlayerTables:SetTableValue(self.ptID, "OnLeftClick", true)
  2894. else
  2895. PlayerTables:SetTableValue(self.ptID, "OnLeftClick", fun)
  2896. end
  2897.  
  2898. self._OnLeftClick = fun
  2899. end
  2900.  
  2901. function c:OnRightClick(fun)
  2902. if fun == nil then
  2903. PlayerTables:DeleteTableKey(self.ptID, "OnRightClick")
  2904. elseif type(fun) == "function" then
  2905. PlayerTables:SetTableValue(self.ptID, "OnRightClick", true)
  2906. else
  2907. PlayerTables:SetTableValue(self.ptID, "OnRightClick", fun)
  2908. end
  2909.  
  2910. self._OnRightClick = fun
  2911. end
  2912.  
  2913. function c:OnDragTo(fun)
  2914. self._OnDragTo = fun
  2915. end
  2916.  
  2917. function c:OnDragWithin(fun)
  2918. self._OnDragWithin = fun
  2919. end
  2920.  
  2921. function c:OnDragFrom(fun)
  2922. if fun == nil then
  2923. PlayerTables:DeleteTableKey(self.ptID, "OnDragFrom")
  2924. elseif type(fun) == "function" then
  2925. PlayerTables:SetTableValue(self.ptID, "OnDragFrom", true)
  2926. else
  2927. PlayerTables:SetTableValue(self.ptID, "OnDragFrom", fun)
  2928. end
  2929.  
  2930. self._OnDragFrom = fun
  2931. end
  2932.  
  2933. function c:OnDragWorld(fun)
  2934. if fun == nil then
  2935. PlayerTables:DeleteTableKey(self.ptID, "OnDragWorld")
  2936. elseif type(fun) == "function" then
  2937. PlayerTables:SetTableValue(self.ptID, "OnDragWorld", true)
  2938. else
  2939. PlayerTables:SetTableValue(self.ptID, "OnDragWorld", fun)
  2940. end
  2941.  
  2942. self._OnDragWorld = fun
  2943. end
  2944.  
  2945. function c:OnCloseClicked(fun)
  2946. if fun == nil then
  2947. PlayerTables:DeleteTableKey(self.ptID, "OnCloseClicked")
  2948. elseif type(fun) == "function" then
  2949. PlayerTables:SetTableValue(self.ptID, "OnCloseClicked", true)
  2950. else
  2951. PlayerTables:SetTableValue(self.ptID, "OnCloseClicked", fun)
  2952. end
  2953.  
  2954. self._OnCloseClicked = fun
  2955. end
  2956.  
  2957. function c:OnButtonPressed(fun)
  2958. if fun == nil then
  2959. PlayerTables:DeleteTableKey(self.ptID, "OnButtonPressed")
  2960. elseif type(fun) == "function" then
  2961. PlayerTables:SetTableValue(self.ptID, "OnButtonPressed", true)
  2962. else
  2963. PlayerTables:SetTableValue(self.ptID, "OnButtonPressed", fun)
  2964. end
  2965.  
  2966. self._OnButtonPressed = fun
  2967. end
  2968.  
  2969. function c:OnEntityOrder(fun)
  2970. self._OnEntityOrder = fun
  2971. end
  2972.  
  2973. function c:OnEntityDrag(fun)
  2974. self._OnEntityDrag = fun
  2975. end
  2976.  
  2977. function c:OnClose(fun)
  2978. self._OnClose = fun
  2979. end
  2980.  
  2981. function c:OnOpen(fun)
  2982. self._OnOpen = fun
  2983. end
  2984.  
  2985. function c:OnSelect(fun)
  2986. self._OnSelect = fun
  2987. end
  2988.  
  2989. function c:OnDeselect(fun)
  2990. self._OnDeselect = fun
  2991. end
  2992.  
  2993. function c:OnLeftClickJS(jsCallback)
  2994. if jsCallback == nil then
  2995. PlayerTables:DeleteTableKey(self.ptID, "OnLeftClickJS")
  2996. else
  2997. PlayerTables:SetTableValue(self.ptID, "OnLeftClickJS", jsCallback)
  2998. end
  2999. end
  3000.  
  3001. function c:OnRightClickJS(jsCallback)
  3002. if jsCallback == nil then
  3003. PlayerTables:DeleteTableKey(self.ptID, "OnRightClickJS")
  3004. else
  3005. PlayerTables:SetTableValue(self.ptID, "OnRightClickJS", jsCallback)
  3006. end
  3007. end
  3008.  
  3009. function c:OnDoubleClickJS(jsCallback)
  3010. if jsCallback == nil then
  3011. PlayerTables:DeleteTableKey(self.ptID, "OnDoubleClickJS")
  3012. else
  3013. PlayerTables:SetTableValue(self.ptID, "OnDoubleClickJS", jsCallback)
  3014. end
  3015. end
  3016.  
  3017. function c:OnMouseOutJS(jsCallback)
  3018. if jsCallback == nil then
  3019. PlayerTables:DeleteTableKey(self.ptID, "OnMouseOutJS")
  3020. else
  3021. PlayerTables:SetTableValue(self.ptID, "OnMouseOutJS", jsCallback)
  3022. end
  3023. end
  3024.  
  3025. function c:OnMouseOverJS(jsCallback)
  3026. if jsCallback == nil then
  3027. PlayerTables:DeleteTableKey(self.ptID, "OnMouseOverJS")
  3028. else
  3029. PlayerTables:SetTableValue(self.ptID, "OnMouseOverJS", jsCallback)
  3030. end
  3031. end
  3032.  
  3033. function c:OnButtonPressedJS(jsCallback)
  3034. if jsCallback == nil then
  3035. PlayerTables:DeleteTableKey(self.ptID, "OnButtonPressedJS")
  3036. else
  3037. PlayerTables:SetTableValue(self.ptID, "OnButtonPressedJS", jsCallback)
  3038. end
  3039. end
  3040.  
  3041. function c:OnCloseClickedJS(jsCallback)
  3042. if jsCallback == nil then
  3043. PlayerTables:DeleteTableKey(self.ptID, "OnCloseClickedJS")
  3044. else
  3045. PlayerTables:SetTableValue(self.ptID, "OnCloseClickedJS", jsCallback)
  3046. end
  3047. end
  3048.  
  3049. function c:IsInventory()
  3050. return false
  3051. end
  3052.  
  3053.  
  3054. self.containers[self.nextID] = c
  3055. self.nextID = self.nextID + 1
  3056. return c
  3057. end
  3058.  
  3059. function Containers:DeleteContainer(c, deleteContents)
  3060. if deleteContents ~= false or c:IsEquipment() then
  3061. local items = c:GetAllItems()
  3062. for _, item in ipairs(items) do
  3063. if c:IsEquipment() then
  3064. local mods = c.appliedPassives[item:GetEntityIndex()]
  3065. Timers:RemoveTimer(c.cleanupTimer)
  3066. if mods then
  3067. for _, mod in ipairs(mods) do
  3068. mod:Destroy()
  3069. end
  3070. end
  3071. end
  3072. if deleteContents then
  3073. item:RemoveSelf()
  3074. end
  3075. end
  3076. end
  3077.  
  3078. PlayerTables:DeleteTable(c.ptID)
  3079. self.containers[c.id] = nil
  3080.  
  3081. CustomGameEventManager:Send_ServerToAllClients("cont_delete_container", {id=c.id} )
  3082.  
  3083. for k,v in pairs(c) do
  3084. c[k] = nil
  3085. end
  3086. end
  3087.  
  3088. function Containers:print(...)
  3089. if CONTAINERS_DEBUG then
  3090. print(unpack({...}))
  3091. end
  3092. end
  3093.  
  3094. function Containers:PrintTable(t)
  3095. if CONTAINERS_DEBUG then
  3096. PrintTable(t)
  3097. end
  3098. end
  3099.  
  3100. function IsValidContainer(c)
  3101. if c and c.GetAllOpen then
  3102. return true
  3103. else
  3104. return false
  3105. end
  3106. end
  3107.  
  3108. if not Containers.containers then Containers:start() end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement