Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
* Introduction
* Code
* Screenshots
* How to Import
* Changelog
* Credits
Introduction
This is a simple system with the following features:
Compatibility with the default inventory
Drag and drop items
- Move items from custom inventory to default one and vice versa
- Drop items to the ground from custom inventory
- Sell items from the custom inventory to shop
- Give items from custom inventory to another unit
Code
Wurstscript
vJASS
Wurst:
package Inventory
import FramehandleNames
import ClosureFrames
import ClosureTimers
import ClosureEvents
import LinkedList
import HashMap
import SyncSimple
import MapBounds
import UI
import InventoryEvent
import HoverOriginButton
@configurable constant SLOTS_PER_PAGE = 64
@configurable constant SLOTS_PER_ROW = 8
@configurable constant SLOT_SIZE = 0.02
@configurable constant SPACE_BETWEEN_SLOTS = 0.008
@configurable constant EMPTY_SLOT_TEXTURE = "ui\\widgets\\escmenu\\human\\quest-completed-background.blp"
@configurable constant SELECT_SLOT_MODEL = UI.uI_ModalButtonOn
@configurable constant BACKDROP_BORDER_SIZE = 0.029
@configurable constant BACKDROP_POSITION = vec2(0.35, 0.35)
var itemSlot = -1
var itemSlotEx = -1
//let mapItem = new HashMap<framehandle, item> // Needs to be player based
let mapInventory = new HashMap<player, Inventory>
let mapUnitItem = new HashMap<unit, item>
let mapSelectedFrame = new HashMap<player, framehandle>
let mapSlot = new HashMap<framehandle, Slot>
let mapFlag = new HashMap<item, bool>
let mapUnit = new HashMap<player, unit>
framehandle dummyFrame
framehandle dummyFrameEx
framehandle dummyFrameIcon
framehandle dummyTooltip
framehandle dummyTooltipBox
framehandle dummyTooltipText
trigger dropTrigger
// Determines whether the player is currently an item or not
function isInTargetingMode() returns boolean
var index = 0
for i = 0 to 11 // when 12 is reached no button is visible
if BlzFrameIsVisible(BlzGetOriginFrame(ORIGIN_FRAME_COMMAND_BUTTON, i))
break
index++
return index == 11 //when the loop broke in index == 11 its targeting mode
function filter() returns bool
return GetFilterUnit().isType(UNIT_TYPE_HERO)
class Slot
framehandle buttonFrame
framehandle iconFrame
framehandle tooltipFrame
framehandle tooltipText
framehandle tooltipBox
framehandle indicator
framehandle chargeBackdrop
framehandle chargeText
construct(framehandle parent, int id, int index, real x, real y, Inventory inv)
buttonFrame = createFrame(FramehandleNames.scriptDialogButton, parent, 0, id*SLOTS_PER_PAGE + index)
..setSize(SLOT_SIZE, SLOT_SIZE)
..setPoint(FRAMEPOINT_TOPLEFT, parent, FRAMEPOINT_TOPLEFT, x, y)
// Link the button frame to this slot instance
mapSlot.put(buttonFrame, this)
iconFrame = createFrame(FramehandleTypeNames.backdrop, "InventorySlotIcon", buttonFrame, "", id*SLOTS_PER_PAGE + index)
..setAllPoints(buttonFrame)
..setTexture(EMPTY_SLOT_TEXTURE, 0, true)
tooltipFrame = createFrame("FRAME", "InventorySlotTooltipParent", buttonFrame, "", id*SLOTS_PER_PAGE + index)
..disable()
tooltipBox = createFrame("TasToolTipBox", tooltipFrame, 0, id*SLOTS_PER_PAGE + index)
..setVisible(false)
tooltipText = createFrame("TasTooltipText", tooltipBox, 0, id*SLOTS_PER_PAGE + index)
..setPoint(FRAMEPOINT_TOPLEFT, buttonFrame, FRAMEPOINT_TOPRIGHT, 0.01, 0.01)
..setSize(0.2, 0)
tooltipBox..setPoint(FRAMEPOINT_TOPLEFT, tooltipText, FRAMEPOINT_TOPLEFT, -0.007, 0.007)
..setPoint(FRAMEPOINT_BOTTOMRIGHT, tooltipText, FRAMEPOINT_BOTTOMRIGHT, 0.007, -0.007)
indicator = createFrame("SPRITE", "MySlotIndicator", buttonFrame, "", id*SLOTS_PER_PAGE + index)
..setAllPoints(buttonFrame)
..setScale(SLOT_SIZE/0.036)
..setVisible(false)
..setModel(SELECT_SLOT_MODEL, 0)
chargeBackdrop = createFrame(FramehandleTypeNames.backdrop, "", buttonFrame, "", id*SLOTS_PER_PAGE + index)
..setSize(0.015, 0.015)
..setPoint(FRAMEPOINT_BOTTOMRIGHT, buttonFrame, FRAMEPOINT_BOTTOMRIGHT)
..setTexture("ui\\widgets\\console\\human\\commandbutton\\human-button-lvls-overlay", 0, true)
..setVisible(false)
chargeText = createFrame(FramehandleTypeNames.text, "", chargeBackdrop, "", id*SLOTS_PER_PAGE + index)
..setAllPoints(chargeBackdrop)
..setTextAlignment(TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
..setFont("MasterFont", 0.01, 0)
..setText("1")
buttonFrame..setTooltip(tooltipFrame)
..onClick() ->
let frame = EventData.getTriggerFrame()
let p = EventData.getTriggerPlayer()
let u = ENUM_GROUP..enumUnitsSelected(p, Filter(function filter)).getRandom()
let mapItemFrame = inv.mapItem.get(p)
if u != null
frame.unfocus(p)
// The custom inventory slot clicked does not contain an item
if not mapItemFrame.has(frame)
itemSlot.sync(p) (int data) ->
// The player currently is dragging an item
if data != -1/* and isInTargetingMode() */
isInTargetingMode().sync(p) (boolean dataBool) ->
if dataBool
// The player selected a custom inventory slot with an item beforehand
if mapSelectedFrame.has(p)
// Empty out the custom inventory slot
let selectedFrame = mapSelectedFrame.get(p)
let itemInSlot = mapItemFrame.get(selectedFrame)
mapSelectedFrame.remove(p)
mapItemFrame.remove(selectedFrame)
mapItemFrame.put(frame, itemInSlot)
// Update the visuals
if localPlayer == p
let slot = mapSlot.get(selectedFrame)
slot.indicator.setVisible(p, false)
slot.iconFrame.setTexture(EMPTY_SLOT_TEXTURE, 0, true)
slot.tooltipBox.setVisible(p, false)
slot.chargeBackdrop.setVisible(false)
// Move manipulated item to clicked custom inventory slot
let itm = u.itemInSlot(data)
if itm != null
let charges = itm.getCharges()
if localPlayer == p
iconFrame.setTexture(itm.getIconPath(), 0, true)
tooltipBox..setVisible(true)
tooltipText..setText(GetLocalizedString("|cffffcc00" + itm.getName() + "|r|n" + itm.getExtendedTooltip()))
if charges > 0
chargeBackdrop.setVisible(true)
chargeText.setText(charges.toString())
mapItemFrame.put(frame, itm)
itm..setPos(boundRect.getLeftTop())..setVisible(false)
// Move back the origin item button frame back to its original location
if localPlayer == p
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(dummyFrame)
// Return the original item in first slot to default unit inventory
if mapUnitItem.has(u)
mapUnitItem.get(u).setVisible(true)
u.addItemHandle(mapUnitItem.get(u))
mapUnitItem.remove(u)
if localPlayer == p
dummyFrameIcon.setVisible(false)
dummyTooltipBox.setVisible(false)
else // The slot clicked contains an item
dummyFrameEx.enable()
// The player did not select a slot with an item beforehand
if not mapSelectedFrame.has(p)
isInTargetingMode().sync(p) (boolean dataBool) ->
// The player is not currently dragging an item
if not dataBool
let itm = u.itemInSlot(0)
if itm != null
itm
..setPos(boundRect.getLeftTop())..setVisible(false)
mapUnitItem.put(u, itm)
u.addItemHandle(mapItemFrame.get(frame))
mapSelectedFrame.put(p, frame)
// Update the visuals
if localPlayer == p
indicator
..setVisible(true)
tooltipBox..setVisible(false)
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(frame)
if mapUnitItem.has(u)
dummyFrameIcon..setVisible(true)
..setTexture(mapUnitItem.get(u).getIconPath(), 0, true)
dummyTooltipBox.setVisible(true)
dummyTooltipText.setText(GetLocalizedString("|cffffcc00" + itm.getName() + "|r|n" + itm.getExtendedTooltip()))
else // The player currently is dragging an item
itemSlot.sync(p) (int data) ->
if data != -1
let itm = mapItemFrame.get(frame)
let itemInSlot = u.itemInSlot(data)
if itemInSlot != null
// Flag the items to be ignored by item events
mapFlag.put(itm, true)
mapFlag.put(itemInSlot, true)
itemInSlot..setPos(boundRect.getLeftTop())..setVisible(false)
u.addItemHandle(itm)
mapFlag.put(itm, true)
u.dropItemSlot(itm, data)
// Swap the item that the player is dragging with the item in slot
mapItemFrame.remove(frame)
mapItemFrame.put(frame, itemInSlot)
// Update the visuals
let charges = itemInSlot.getCharges()
if localPlayer == p
iconFrame
..setTexture(itemInSlot.getIconPath(), 0, true)
tooltipBox..setVisible(true)
tooltipText..setText(GetLocalizedString("|cffffcc00" + itemInSlot.getName() + "|r|n" + itemInSlot.getExtendedTooltip()))
if charges > 0
chargeBackdrop.setVisible(true)
chargeText.setText(charges.toString())
else // The player did select a slot with an item beforehand
isInTargetingMode().sync(p) (boolean dataBool) ->
if dataBool
itemSlot.sync(p) (int data) ->
if data == 0
let selectedFrame = mapSelectedFrame.get(p)
let slot = mapSlot.get(selectedFrame)
let clickedSlot = mapSlot.get(frame)
let clickedItem = mapItemFrame.get(frame)
let selectedItem = mapItemFrame.get(selectedFrame)
let clickedItemCharges = clickedItem.getCharges()
let selectedItemCharges = selectedItem.getCharges()
mapSelectedFrame.remove(p)
mapItemFrame.put(selectedFrame, clickedItem)
mapItemFrame.put(frame, selectedItem)
dropTrigger.disable()
selectedItem..setPos(boundRect.getLeftTop())..setVisible(false)
dropTrigger.enable()
// Update the visuals
if localPlayer == p
// Swap the two items' position
slot.indicator.setVisible(false)
slot.iconFrame.setTexture(clickedItem.getIconPath(), 0, true)
slot.tooltipBox.setVisible(true)
slot.tooltipText.setText(GetLocalizedString("|cffffcc00" + clickedItem.getName() + "|r|n" + clickedItem.getExtendedTooltip()))
if clickedItemCharges > 0
slot.chargeBackdrop.setVisible(true)
slot.chargeText.setText(clickedItemCharges.toString())
clickedSlot.iconFrame.setTexture(selectedItem.getIconPath(), 0, true)
clickedSlot.tooltipBox.setVisible(true)
clickedSlot.tooltipText.setText(GetLocalizedString("|cffffcc00" + selectedItem.getName() + "|r|n" + selectedItem.getExtendedTooltip()))
if selectedItemCharges > 0
clickedSlot.chargeBackdrop.setVisible(true)
clickedSlot.chargeText.setText(selectedItemCharges.toString())
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(dummyFrame)
// Return the original item in first slot to default unit inventory
if mapUnitItem.has(u)
mapUnitItem.get(u).setVisible(true)
u.addItemHandle(mapUnitItem.get(u))
mapUnitItem.remove(u)
if localPlayer == p
dummyFrameIcon.setVisible(false)
dummyTooltipBox.setVisible(false)
else // The player is not currently dragging an item
let selectedFrame = mapSelectedFrame.get(p)
let slot = mapSlot.get(selectedFrame)
let clickedSlot = mapSlot.get(frame)
let clickedItem = mapItemFrame.get(frame)
mapSelectedFrame.remove(p)
dropTrigger.disable()
u.itemInSlot(0)..setPos(boundRect.getLeftTop())..setVisible(false)
dropTrigger.enable()
u.addItemHandle(clickedItem)
if localPlayer == p
slot.indicator.setVisible(false)
clickedSlot.indicator.setVisible(true)
slot.tooltipBox.setVisible(true)
clickedSlot.tooltipBox.setVisible(true)
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(frame)
mapSelectedFrame.put(p, frame)
public class Inventory
private let slots = new LinkedList<Slot>
protected let mapItem = new HashMap<player, HashMap<framehandle, item>>
private framehandle parent
private framehandle next
private framehandle prev
framehandle open
construct()
let id = this castTo int
let titleSize = 0.015
for i = 0 to bj_MAX_PLAYER_SLOTS - 1
if players[i].isIngame()
let map = new HashMap<framehandle, item>
mapItem.put(players[i], map)
// Create the inventory backdrop
let rowCount = SLOTS_PER_PAGE/SLOTS_PER_ROW
parent = createFrame(FramehandleNames.questButtonBaseTemplate, GAME_UI, 0, 0)
..setAbsPoint(FRAMEPOINT_CENTER, BACKDROP_POSITION)
..setSize(BACKDROP_BORDER_SIZE*2 + SLOT_SIZE*SLOTS_PER_ROW + SPACE_BETWEEN_SLOTS*(SLOTS_PER_ROW - 1), BACKDROP_BORDER_SIZE*2 + SLOT_SIZE*rowCount + SPACE_BETWEEN_SLOTS*(rowCount - 1) + titleSize*2 + 0.0145)
// Create the inventory title
let title = createFrame("HeroSelectorTitle", parent, 0, 0)
..setTextAlignment(TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
..setPoint(FRAMEPOINT_TOP, parent, FRAMEPOINT_TOP, 0, -BACKDROP_BORDER_SIZE*0.6)
..setText(GetLocalizedString("Inventory"))
..setSize(parent.getWidth() - BACKDROP_BORDER_SIZE*2, 0.03)
/* // Create page number
createFrame("HeroSelectorTitle", parent, 0, 0)
..setTextAlignment(TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
..setPoint(FRAMEPOINT_BOTTOM, parent, FRAMEPOINT_BOTTOM, 0, 0)
..setSize(parent.getWidth() - BACKDROP_BORDER_SIZE*2, 0.03)
..setText("1") */
// Create close button
createFrame(FramehandleNames.scriptDialogButton, parent, 0, 0)
..setSize(0.03, 0.03)
..setPoint(FRAMEPOINT_TOPRIGHT, parent, FRAMEPOINT_TOPRIGHT, 0, 0)
..setText("X")
..setTextAlignment(TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
..onClick() ->
EventData.getTriggerFrame().unfocus(EventData.getTriggerPlayer())
parent.setVisible(EventData.getTriggerPlayer(), false)
let u = ENUM_GROUP..enumUnitsSelected(EventData.getTriggerPlayer(), Filter(function filter)).getRandom()
let selectedFrame = mapSelectedFrame.get(EventData.getTriggerPlayer())
let mapItemFrame = mapItem.get(EventData.getTriggerPlayer())
let itm = mapItemFrame.get(selectedFrame)
mapSelectedFrame.remove(EventData.getTriggerPlayer())
itm..setPos(boundRect.getLeftTop())..setVisible(false)
let slot = mapSlot.get(selectedFrame)
if slot != null
if localPlayer == EventData.getTriggerPlayer()
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(dummyFrame)
dummyFrameIcon.setVisible(false)
dummyTooltipBox.setVisible(false)
slot.indicator.setVisible(false)
// Return the original item in first slot to default unit inventory
if mapUnitItem.has(u)
let item0 = mapUnitItem.get(u)
mapFlag.put(item0, true)
item0.setVisible(true)
u.addItemHandle(item0)
mapUnitItem.remove(u)
mapInventory.remove(EventData.getTriggerPlayer())
// Create the open button
let inv = this
open = createFrame(FramehandleNames.scriptDialogButton, GAME_UI, 0, 0)
..setSize(0.2, 0.05)
..setTextAlignment(TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
..setAbsPoint(FRAMEPOINT_CENTER, 0.007, 0.175)
..setText(GetLocalizedString("Open Inventory"))
..setScale(0.5)
..onClick() ->
EventData.getTriggerFrame().unfocus(EventData.getTriggerPlayer())
parent.setVisible(EventData.getTriggerPlayer(), true)
mapInventory.put(EventData.getTriggerPlayer(), inv)
if mapSelectedFrame.has(EventData.getTriggerPlayer())
let selectedFrame = mapSelectedFrame.get(EventData.getTriggerPlayer())
let slot = mapSlot.get(selectedFrame)
if slot != null
if localPlayer == EventData.getTriggerPlayer()
slot.indicator.setVisible(false)
// Create the next and prev buttons
/* next = createFrame(FramehandleNames.scriptDialogButton, parent, 0, 0)
..setSize(0.03, 0.03)
..setPoint(FRAMEPOINT_BOTTOMRIGHT, parent, FRAMEPOINT_BOTTOMRIGHT, 0, 0)
..setText(">")
prev = createFrame(FramehandleNames.scriptDialogButton, parent, 0, 0)
..setSize(0.03, 0.03)
..setPoint(FRAMEPOINT_BOTTOMLEFT, parent, FRAMEPOINT_BOTTOMLEFT, 0, 0)
..setVisible(false)
..setText("<")
*/
// Create the inventory slots
var x = BACKDROP_BORDER_SIZE
var y = -BACKDROP_BORDER_SIZE - title.getHeight()/* - 0.0125 */
var j = 0
for i = 0 to (SLOTS_PER_PAGE - 1)
slots.add(new Slot(parent, id, i, x, y, this))
x += SLOT_SIZE + SPACE_BETWEEN_SLOTS
j++
if j == SLOTS_PER_ROW
j = 0
y -= SLOT_SIZE + SPACE_BETWEEN_SLOTS
x = BACKDROP_BORDER_SIZE
// Hide inventory by default
parent.setVisible(false)
function show(bool flag)
parent.setVisible(flag)
init
// Detect player entering the origin item button frames
hoverOriginButtonAdd(false) ->
if not isInTargetingMode()
itemSlot = currentSelectedButtonIndex - itemButtonOffset
if (currentSelectedButtonIndex - itemButtonOffset) != 0
itemSlotEx = currentSelectedButtonIndex - itemButtonOffset
dummyFrameEx.enable()
// Detect player leaving the origin item button frames
hoverOriginButtonAddClose() ->
if not isInTargetingMode()
itemSlot = -1
itemSlotEx = -1
// Detect unit selection
EventListener.add(EVENT_PLAYER_UNIT_SELECTED) ->
let p = EventData.getTriggerPlayer()
let u = EventData.getTriggerUnit()
if mapInventory.has(p)
let inv = mapInventory.get(p)
let mapItemFrame = inv.mapItem.get(p)
// Check if player has an item on custom inventory selected
if mapSelectedFrame.has(p)
// Did the player select an unit before?
if mapUnit.has(p)
let prev = mapUnit.get(p)
let itm = prev.itemInSlot(0)
// Check if this unit had an item in the first slot
if mapUnitItem.has(prev)
let item0 = mapUnitItem.get(prev)
mapFlag.put(item0, true)
prev.addItemHandle(item0)
mapFlag.put(itm, true)
if itm != null
itm..setPos(boundRect.getLeftTop())..setVisible(false)
mapUnitItem.remove(prev)
nullTimer() ->
mapFlag.put(item0, true)
prev.dropItemSlot(item0, 0)
if u.inventorySize() > 0
let itemInFirstSlot = u.itemInSlot(0)
if itemInFirstSlot != null
mapUnitItem.put(u, itemInFirstSlot)
mapFlag.put(itemInFirstSlot, true)
itemInFirstSlot..setPos(boundRect.getLeftTop())..setVisible(false)
if localPlayer == p
dummyFrameIcon.setVisible(true)
dummyFrameIcon.setTexture(itemInFirstSlot.getIconPath(), 0, true)
else
if localPlayer == p
dummyFrameIcon.setVisible(false)
let selectedItem = mapItemFrame.get(mapSelectedFrame.get(p))
mapFlag.put(selectedItem, true)
u.addItemHandle(selectedItem)
nullTimer() ->
mapFlag.put(selectedItem, true)
u.dropItemSlot(selectedItem, 0)
else
let slot = mapSlot.get(mapSelectedFrame.get(p))
if localPlayer == p
dummyFrameIcon.setVisible(false)
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(dummyFrame)
dummyFrameEx.disable()
slot.indicator.setVisible(false)
slot.tooltipBox.setVisible(true)
mapSelectedFrame.remove(p)
mapUnit.put(p, u)
// Detect Mouse Click
EventListener.add(EVENT_PLAYER_MOUSE_UP) ->
let p = EventData.getTriggerPlayer()
if mapInventory.has(p)
let inv = mapInventory.get(p)
let mapItemFrame = inv.mapItem.get(p)
itemSlotEx.sync(p) (int data) ->
if data == 0
if mapSelectedFrame.has(p)
let selectedFrame = mapSelectedFrame.get(p)
let u = ENUM_GROUP..enumUnitsSelected(p, Filter(function filter)).getRandom()
let slot = mapSlot.get(selectedFrame)
mapItemFrame.remove(selectedFrame)
let itm = mapUnitItem.get(u)
mapSelectedFrame.remove(p)
if localPlayer == p
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(dummyFrame)
dummyFrameIcon.setVisible(false)
dummyTooltipBox.setVisible(false)
itemSlot = 0
slot.indicator.setVisible(false)
if itm == null
slot.iconFrame.setTexture(EMPTY_SLOT_TEXTURE, 0, true)
slot.chargeBackdrop.setVisible(false)
slot.tooltipBox.setVisible(false)
else
slot.iconFrame.setTexture(itm.getIconPath(), 0, true)
slot.tooltipBox.setVisible(true)
slot.tooltipText.setText(GetLocalizedString("|cffffcc00" + itm.getName() + "|r|n" + itm.getExtendedTooltip()))
itemSlotEx = -1
dummyFrameEx.disable()
ForceUICancel()
if itm != null
mapItemFrame.put(selectedFrame, itm)
mapUnitItem.remove(u)
// Detect player dropping an item from custom inventory
dropTrigger = CreateTrigger()..addAction() ->
let p = EventData.getTriggerPlayer()
if mapInventory.has(p)
let inv = mapInventory.get(p)
let mapItemFrame = inv.mapItem.get(p)
let itm = EventData.getManipulatedItem()
let u = EventData.getTriggerUnit()
if not mapFlag.has(itm)
if mapSelectedFrame.has(p) and u.getItemSlot(itm) == 0
// Update the visuals
let slot = mapSlot.get(mapSelectedFrame.get(p))
if localPlayer == p
dummyFrameIcon.setVisible(false)
dummyTooltipBox.setVisible(false)
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(dummyFrame)
slot.iconFrame.setTexture(EMPTY_SLOT_TEXTURE, 0, true)
slot.tooltipBox.setVisible(false)
slot.indicator.setVisible(p, false)
slot.chargeBackdrop.setVisible(false)
// Return the original item in first slot to default unit inventory
if mapUnitItem.has(u)
let item0 = mapUnitItem.get(u)
item0.setVisible(true)
mapFlag.put(item0, true)
u.addItemHandle(item0)
nullTimer() ->
mapFlag.put(item0, true)
u.dropItemSlot(item0, 0)
mapUnitItem.remove(u)
// Remove the link between item and custom inventory slot selected
mapItemFrame.remove(mapSelectedFrame.get(p))
mapSelectedFrame.remove(p)
else
mapFlag.remove(itm)
TriggerRegisterAnyUnitEventBJ(dropTrigger, EVENT_PLAYER_UNIT_DROP_ITEM)
// Detect player moving items in default unit inventory
registerInventoryEvent(EVENT_ITEM_INVENTORY.MOVE) ->
let u = getInventoryManipulatingUnit()
let p = u.getOwner()
if mapInventory.has(p)
let inv = mapInventory.get(p)
let mapItemFrame = inv.mapItem.get(p)
let itm = getInventoryManipulatedItem()
let slotFrom = getInventorySlotFrom()
let slotTo = getInventorySlotTo()
if localPlayer == p
itemSlot = slotTo
if not mapFlag.has(itm)
let swappedItem = getInventorySwappedItem()
if slotFrom == 0
if swappedItem == null
// The player selected a custom inventory slot with an item beforehand
if mapSelectedFrame.has(p)
let selectedFrame = mapSelectedFrame.get(p)
let slot = mapSlot.get(selectedFrame)
if slotFrom != slotTo
// Empty out the selected custom inventory slot
mapItemFrame.remove(selectedFrame)
if localPlayer == p
slot.iconFrame.setTexture(EMPTY_SLOT_TEXTURE, 0, true)
slot.tooltipBox.setVisible(false)
slot.chargeBackdrop.setVisible(false)
else
itm..setPos(boundRect.getLeftTop())..setVisible(false)
// Update the visuals
if localPlayer == p
dummyFrameIcon.setVisible(false)
dummyTooltipBox.setVisible(false)
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(dummyFrame)
slot.indicator.setVisible(p, false)
// Return the original item in first slot to default unit inventory
if mapUnitItem.has(u)
let item0 = mapUnitItem.get(u)
item0.setVisible(true)
mapFlag.put(item0, true)
u.addItemHandle(item0)
mapUnitItem.remove(u)
nullTimer() ->
mapFlag.put(item0, true)
u.dropItemSlot(item0, 0)
mapSelectedFrame.remove(p)
else if swappedItem != itm
// The player selected a custom inventory slot with an item beforehand
if mapSelectedFrame.has(p)
let selectedFrame = mapSelectedFrame.get(p)
let slot = mapSlot.get(selectedFrame)
mapItemFrame.remove(selectedFrame)
mapItemFrame.put(selectedFrame, swappedItem)
swappedItem..setPos(boundRect.getLeftTop())..setVisible(false)
// Update the visuals
let charges = swappedItem.getCharges()
if localPlayer == p
dummyFrameIcon.setVisible(false)
dummyTooltipBox.setVisible(false)
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(dummyFrame)
slot.iconFrame.setTexture(swappedItem.getIconPath(), 0, true)
slot.tooltipBox.setVisible(true)
slot.tooltipText
..setText(GetLocalizedString("|cffffcc00" + swappedItem.getName() + "|r|n" + swappedItem.getExtendedTooltip()))
slot.indicator.setVisible(p, false)
if charges > 0
slot.chargeBackdrop.setVisible(true)
slot.chargeText.setText(charges.toString())
// Return the original item in first slot to default unit inventory
if mapUnitItem.has(u)
mapUnitItem.get(u).setVisible(true)
u.addItemHandle(mapUnitItem.get(u))
mapUnitItem.remove(u)
mapSelectedFrame.remove(p)
else // Item being manipulated was not in first slot of default unit inventory
if slotTo == 0
// The player selected a slot in custom inventory with an item beforehand
if mapSelectedFrame.has(p)
let selectedFrame = mapSelectedFrame.get(p)
let slot = mapSlot.get(selectedFrame)
// Place the item being manipulated in the custom inventory slot
mapItemFrame.remove(selectedFrame)
mapItemFrame.put(selectedFrame, itm)
mapFlag.put(itm, true)
mapFlag.put(swappedItem, true)
itm..setPos(boundRect.getLeftTop())..setVisible(false)
u.dropItemSlot(swappedItem, slotFrom)
// Update the visuals
let charges = itm.getCharges()
if localPlayer == p
dummyFrameIcon.setVisible(false)
dummyTooltipBox.setVisible(false)
getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
..setAllPoints(dummyFrame)
slot.iconFrame.setTexture(itm.getIconPath(), 0, true)
slot.tooltipBox.setVisible(true)
slot.tooltipText
..setText(GetLocalizedString("|cffffcc00" + itm.getName() + "|r|n" + itm.getExtendedTooltip()))
slot.indicator.setVisible(p, false)
if charges > 0
slot.chargeBackdrop.setVisible(true)
slot.chargeText.setText(charges.toString())
// Return the original item in first slot to default unit inventory
if mapUnitItem.has(u)
mapUnitItem.get(u).setVisible(true)
u.addItemHandle(mapUnitItem.get(u))
mapUnitItem.remove(u)
mapSelectedFrame.remove(p)
else
mapFlag.remove(itm)
// Create a dummy frame for the first slot of default unit inventory
dummyFrame = createFrame(FramehandleTypeNames.gluetextbutton, "DummyFrame", GAME_UI, "", -1)
..setSize(0.030, 0.030)
..setAbsPoint(FRAMEPOINT_CENTER, 0.531 + 0.0008, 0.101 - 0.004)
// Create the icon texture frame for the dummy frame
dummyFrameIcon = createFrame(FramehandleTypeNames.backdrop, "DummyFrameIcon", dummyFrame, "", -1)
..setAllPoints(dummyFrame)
..setVisible(false)
// Create a dummy button to detect if player entered the first item slot when the ORIGIN_ITEM_BUTTON is moved
dummyFrameEx = createFrame(FramehandleTypeNames.gluetextbutton, "DummyFrameEx", dummyFrame, "", -1)
..setSize(0.04, 0.04)
..setAbsPoint(FRAMEPOINT_CENTER, 0.531 + 0.0008, 0.101 - 0.004)
..onMouseEnter() ->
EventData.getTriggerFrame()..unfocus(EventData.getTriggerPlayer())
..disable()
if isInTargetingMode()
itemSlotEx = 0
// dummyTooltip = createFrame("FRAME", "DummyTooltipParent", dummyFrameEx, "", 0)
// ..disable()
// dummyTooltipBox = createFrame("TasToolTipBox", dummyTooltip, 0, 0)
// dummyTooltipText = createFrame("TasTooltipText", dummyTooltipBox, 0, 0)
// ..setPoint(FRAMEPOINT_TOPLEFT, dummyFrame, FRAMEPOINT_TOPRIGHT, 0.01, 0.01)
// ..setSize(0.2, 0)
// dummyTooltipBox..setPoint(FRAMEPOINT_TOPLEFT, dummyTooltipText, FRAMEPOINT_TOPLEFT, -0.007, 0.007)
// ..setPoint(FRAMEPOINT_BOTTOMRIGHT, dummyTooltipText, FRAMEPOINT_BOTTOMRIGHT, 0.007, -0.007)
// dummyFrameEx.setTooltip(dummyTooltip)
init
loadTOCFile("war3mapImported\\TasInventoryEx.toc")
Other than importing the main package and the standard library, the following two packages are also required to use Inventory:
Wurst:
package HoverOriginButton
// Original by Tasyen
import FramehandleNames
import ClosureTimers
@configurable constant INTERVAL = 1/60
public let itemButtonOffset = 30
framehandle array frames
public var currentSelectedButtonIndex = -1
trigger actionsCommand = CreateTrigger()
trigger actionsClose = CreateTrigger()
trigger actionsItem = CreateTrigger()
public function hoverOriginButtonAdd(bool forCommandButton, code action) returns triggercondition
if forCommandButton
return TriggerAddCondition(actionsCommand, Filter(action))
else
return TriggerAddCondition(actionsItem, Filter(action))
public function hoverOriginButtonAddClose(code action) returns triggercondition
return TriggerAddCondition(actionsClose, Filter(action))
public function hoverOriginButtonRemove(triggercondition action)
actionsClose.removeCondition(action)
actionsCommand.removeCondition(action)
actionsItem.removeCondition(action)
init
nullTimer() ->
// Create one tooltip frame for each command button
for i = 0 to 11
let buttonFrame = getOriginFrame(ORIGIN_FRAME_COMMAND_BUTTON, i)
let frame = createFrame(FramehandleTypeNames.simpleframe, "", buttonFrame, "", 0)
..setVisible(false)
buttonFrame..setTooltip(frame)
frames[i] = frame
// Create one tooltip frame for each item button
for i = 0 to 5
let buttonFrame = getOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, i)
let frame = createFrame(FramehandleTypeNames.simpleframe, "", buttonFrame, "", 0)
..setVisible(false)
buttonFrame..setTooltip(frame)
frames[i + itemButtonOffset] = frame
doPeriodically(INTERVAL) (CallbackPeriodic cb) ->
var selectedAnything = false
for i = 0 to 11
if frames[i].isVisible()
selectedAnything = true
if currentSelectedButtonIndex != i
currentSelectedButtonIndex = i
actionsCommand.evaluate()
if i < 6
let tableIndex = i + itemButtonOffset
if frames[tableIndex].isVisible()
selectedAnything = true
if currentSelectedButtonIndex != tableIndex
currentSelectedButtonIndex = tableIndex
actionsItem.evaluate()
if not selectedAnything and currentSelectedButtonIndex != -1
actionsClose.evaluate()
currentSelectedButtonIndex = -1
Wurst:
/*
* InventoryEvent v1.0.1.5
* by Bannar
*
* For intuitive inventory event handling.
*/
package InventoryEvent
import RegisterEvents
import Orders
tuple eventInfo(unit u, item itm, int slotFrom, int slotTo)
var eventState = eventInfo(null, null, -1, -1)
constant eventMovedTrigger = CreateTrigger()
constant eventUsedTrigger = CreateTrigger()
public enum EVENT_ITEM_INVENTORY
MOVE
USE
/** Returns unit which manipulated event item. */
public function getInventoryManipulatingUnit() returns unit
return eventState.u
/** Returns manupilated event item. */
public function getInventoryManipulatedItem() returns item
return eventState.itm
/** Returns slot index of manipulated item from which it was moved or used. */
public function getInventorySlotFrom() returns int
return eventState.slotFrom
/** Returns slot index of manipulated item to which it was moved. */
public function getInventorySlotTo() returns int
return eventState.slotTo
/** Returns item which manipulated item switched position with if any. */
public function getInventorySwappedItem() returns item
return eventState.u.itemInSlot(eventState.slotTo)
public function getInventoryEventTrigger(EVENT_ITEM_INVENTORY whichEvent) returns trigger
trigger result
switch whichEvent
case EVENT_ITEM_INVENTORY.MOVE
result = eventMovedTrigger
case EVENT_ITEM_INVENTORY.USE
result = eventUsedTrigger
return result
public function registerInventoryEvent(EVENT_ITEM_INVENTORY whichEvent, code func)
switch whichEvent
case EVENT_ITEM_INVENTORY.MOVE
eventMovedTrigger.addCondition(Condition(func))
case EVENT_ITEM_INVENTORY.USE
eventUsedTrigger.addCondition(Condition(func))
function fireEvent(trigger evt, eventInfo currState)
let prevState = eventState
eventState = currState
evt.evaluate()
eventState = prevState
function onItemOrder()
let order = GetIssuedOrderId()
let u = GetTriggerUnit()
if order >= SpecialOrders.itemdrag00 and order <= SpecialOrders.itemdrag05
let itm = GetOrderTargetItem()
let slotFrom = u.getItemSlot(itm)
let slotTo = order - SpecialOrders.itemdrag00
fireEvent(eventMovedTrigger, eventInfo(u, itm, slotFrom, slotTo))
else
let slotFrom = order - SpecialOrders.itemuse00
let itm = u.itemInSlot(slotFrom)
fireEvent(eventUsedTrigger, eventInfo(u, itm, slotFrom, -1))
function onAnyOrder()
let order = GetIssuedOrderId()
if order >= SpecialOrders.itemdrag00 and order <= SpecialOrders.itemuse05
onItemOrder()
init
// MOVED is order of type TARGET_ORDER yet USED can be anyone of them
registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, () -> onAnyOrder())
registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, () -> onAnyOrder())
registerPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, () -> onAnyOrder())
JASS:
/*****************************************************************************
*
* Inventory v1.0.8
* by SmitingDevil
*
* Custom inventory that is compatible with the vanilla one
*
******************************************************************************
*
* Requirements:
*
* Table by Bribe
* https://www.hiveworkshop.com/threads/lua-vjass-new-table.188084/
*
* Alloc - choose whatever you like
* e.g.: by AGD https://www.hiveworkshop.com/threads/global-alloc.324937/
*
* HoverOriginButton by Tasyen
* https://www.hiveworkshop.com/threads/hoveroriginbutton.337965/
*
* GetMainSelectedUnit (vJASS) by Tasyen
* https://www.hiveworkshop.com/threads/getmainselectedunit.325337/
*
* Sync by TriggerHappy
* https://www.hiveworkshop.com/threads/sync-game-cache.279148/
*
* List<T> by Bannar
* https://www.hiveworkshop.com/threads/containers-list-t.249011/
*
* InventoryEvent by Bannar
* https://www.hiveworkshop.com/threads/snippet-inventoryevent.287084/
*
* StringIterator by edo494
* https://www.hiveworkshop.com/threads/snippet-stringiterator.246143/
*
* WorldBounds by Nestharus
* https://raw.githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
*
* TimerUtils by Magtheridon96/Vexorian/Bribe
* https://www.hiveworkshop.com/threads/system-timerutilsex.204500/#post-2019478
*
******************************************************************************
*
* Inventory Struct API:
*
* General:
*
* | static method create takes nothing returns thistype
* | Default constructor.
* |
* | static method createEx takes integer slotCount, integer cols, real buttonSize, real spaceGap, real borderSize, real titleSize, real posX, real posY returns thistype
* | Constructor that builds the inventory in one go.
* |
* | method destroy takes nothing returns nothing
* | Default destructor.
*
*
* Access:
*
* | readonly emptySlotTexture
* | readonly slotIndicatorModel
* | readonly backdrop
* | readonly buttonSize
*
*
* Modifiers:
*
* [Design]
*
* | method setSlotCount takes integer count returns thistype
* | Sets the number of inventory slots.
* |
* | method setSlotEmptyTexture takes string texture returns thistype
* | Sets the default texture for slots.
* |
* | method setSlotIndicatorModel takes string str returns thistype
* | Sets the model that shows a slot is selected.
* |
* | method setButtonSize takes real size returns thistype
* | Sets the size of the inventory slots and other buttons.
* |
* | method setButtonSpaceGap takes real size returns thistype
* | Sets the size of the space between inventory slots.
* |
* | method setColumnCount takes integer cols returns thistype
* | Sets the number of slots in a row.
* |
* | method setTitle takes string str returns thistype
* | Sets the name of the inventory.
* |
* | method setTitleSize takes real size returns thistype
* | Sets the size of the title's margins.
* |
* | method setBorderSize takes real size returns thistype
* | Sets the size of the inventory's margins.
|
* | method setOpenButtonPosition takes real x, real y returns thistype
* | Sets the position of the open button icon.
* |
* | method setOpenButtonTexture takes string str returns thistype
* | Sets the texture for open button icon.
* |
* | method build takes nothing returns nothing
* | Creates the inventory and all its elements.
* | All methods for design must be called before running this method.
* | They cannot be modified after build.
* | Only title name and methods for the open button are the only exceptions.
* | They can be modified before and after build and are optional.
*
* [Technical]
*
* | method setPosition takes real x, real y returns thistype
* | Moves the inventory to given point on the screen
* |
* | method show takes boolean flag returns nothing
* | Hides or displays the inventory
* |
* | method showEx takes player p, boolean flag returns nothing
* | Hides or displays the inventory for specific player only
* | Links the inventory to player as the one being displayed
*
*
*****************************************************************************/
library Inventory initializer Init requires TimerUtilsEx, WorldBounds, ListT, StringIterator, Sync, InventoryEvent, HoverOriginButton, GetMainSelectedUnit
globals
private framehandle dummyFrame
private framehandle dummyFrameEx
private framehandle dummyIcon
private framehandle array playerFrame
private integer originItemSlot = -1
private integer originItemSlotEx = -1
private Table player2Inventory
//private Table item2Unit
player LOCAL_PLAYER
framehandle GAME_UI
framehandle ORIGIN_ITEM_BUTTON_ONE
//private integer count = 0
endglobals
function IsFrameEnabled takes framehandle frame, player p returns boolean
if GetLocalPlayer() == p or p == null then
return BlzFrameGetEnable(frame)
else
return false
endif
endfunction
function UnfocusFrame takes framehandle frame, player p returns nothing
if IsFrameEnabled(frame, p) then
call BlzFrameSetEnable(frame, false)
call BlzFrameSetEnable(frame, true)
else
call BlzFrameSetEnable(frame, true)
call BlzFrameSetEnable(frame, false)
endif
endfunction
function print takes string msg returns nothing
call DisplayTimedTextToPlayer(LOCAL_PLAYER, 0, 0, 3, msg)
endfunction
private function IsMouseDraggingItem takes nothing returns boolean
local integer i = 0
loop
if BlzFrameIsVisible(BlzGetOriginFrame(ORIGIN_FRAME_COMMAND_BUTTON, i)) then
exitwhen true
endif
set i = i + 1
exitwhen i == 12
endloop
return i == 11
endfunction
private module MyModule
private static method onInit takes nothing returns nothing
//this is run on map init
call init()
endmethod
endmodule
private struct Frame extends array
implement Alloc
framehandle handle
static method create takes framehandle frame returns thistype
local thistype this = allocate()
set handle = frame
return this
endmethod
method destroy takes nothing returns nothing
set handle = null
call deallocate()
endmethod
endstruct
private struct UnitItem extends array
implement Alloc
item itemHandle
unit unitHandle
static method create takes unit u, item it returns thistype
local thistype this = allocate()
set unitHandle = u
set itemHandle = it
return this
endmethod
method destroy takes nothing returns nothing
set unitHandle = null
set itemHandle = null
call deallocate()
endmethod
endstruct
private struct Slot extends array
implement Alloc
private framehandle actionButton
private framehandle icon
private framehandle tooltipParent
private framehandle tooltipText
private framehandle tooltipBackdrop
readonly framehandle indicator
readonly Inventory parent
readonly static Table frame2Slot
readonly static Table frame2Item
readonly static Table unit2Item
readonly static Table player2Unit
readonly static Table data2Frame
private static trigger actionTrigger
private static trigger enterTrigger
//private static framehandle clickedFrame
static method create takes real size, real x, real y, Inventory parent returns thistype
local thistype this = allocate()
set actionButton = BlzCreateFrame("ScriptDialogButton", parent.backdrop, 0, 0)
call BlzFrameSetSize(actionButton, size, size)
call BlzFrameSetPoint(actionButton, FRAMEPOINT_TOPLEFT, parent.backdrop, FRAMEPOINT_TOPLEFT, x, y)
set frame2Slot[GetHandleId(actionButton)] = this
set frame2Item[GetHandleId(actionButton)] = Table.create()
set icon = BlzCreateFrameByType("BACKDROP", "SlotIcon", actionButton, "", 0)
call BlzFrameSetAllPoints(icon, actionButton)
call BlzFrameSetTexture(icon, parent.emptySlotTexture, 0, true)
set tooltipParent = BlzCreateFrameByType("FRAME", "SlotTooltipParent", actionButton, "", 0)
call BlzFrameSetTooltip(actionButton, tooltipParent)
set tooltipBackdrop = BlzCreateFrameByType("BACKDROP", "SlotTooltipBackdrop", tooltipParent, "", 0)
call BlzFrameSetVisible(tooltipBackdrop, false)
set tooltipText = BlzCreateFrameByType("TEXT", "SlotTooltipText", tooltipBackdrop, "", 0)
call BlzFrameSetSize(tooltipText, 0.2, 0)
call BlzFrameSetPoint(tooltipText, FRAMEPOINT_TOPLEFT, actionButton, FRAMEPOINT_TOPRIGHT, 0, 0)
call BlzFrameSetPoint(tooltipBackdrop, FRAMEPOINT_TOPLEFT, tooltipText, FRAMEPOINT_TOPLEFT, -0.007, 0.007)
call BlzFrameSetPoint(tooltipBackdrop, FRAMEPOINT_BOTTOMRIGHT, tooltipText, FRAMEPOINT_BOTTOMRIGHT, 0.007, -0.007)
call BlzFrameSetTexture(tooltipBackdrop, "UI\\Widgets\\ToolTips\\Human\\human-tooltip-background.blp", 0, true)
set indicator = BlzCreateFrameByType("SPRITE", "SlotIndicator", actionButton, "", 0)
call BlzFrameSetAllPoints(indicator, actionButton)
call BlzFrameSetScale(indicator, parent.buttonSize/0.036)
call BlzFrameSetVisible(indicator, false)
call BlzFrameSetModel(indicator, parent.slotIndicatorModel, 0)
call BlzTriggerRegisterFrameEvent(actionTrigger, actionButton, FRAMEEVENT_CONTROL_CLICK)
set .parent = parent
return this
endmethod
private method update takes item itm returns nothing
if itm != null then
call BlzFrameSetTexture(icon, BlzGetItemIconPath(itm), 0, true)
call BlzFrameSetVisible(tooltipBackdrop, true)
call BlzFrameSetText(tooltipText, GetLocalizedString("|cffffcc00" + GetItemName(itm) + "|r\n\n" + BlzGetAbilityExtendedTooltip(GetItemTypeId(itm), 0)))
else
call BlzFrameSetTexture(icon, parent.emptySlotTexture, 0, true)
call BlzFrameSetVisible(tooltipBackdrop, false)
endif
endmethod
private static method onExpired takes nothing returns nothing
local UnitItem data = ReleaseTimer(GetExpiredTimer())
call SetItemVisible(data.itemHandle, true)
call UnitAddItem(data.unitHandle, data.itemHandle)
call UnitDropItemSlot(data.unitHandle, data.itemHandle, 0)
call data.destroy()
endmethod
private static method clickAction takes nothing returns nothing
local SyncData d = GetSyncedData()
local StringIterator iter = StringIterator.create(d.readString(0))
local Frame clickedFrame = data2Frame[d]
local integer originSlot
local integer pid
local integer selectedIndex = S2I(iter.read())
local unit mainSelectedUnit = GetMainSelectedUnit(selectedIndex)
local framehandle frame
local framehandle pFrame
local boolean itemOnMouse
local player syncer
local item itemInOriginSlot
local item slotItem
local item item0
local thistype slot
local thistype pSlot
local Table table
local Table tb
call data2Frame.remove(d)
//call print("Click Action: " + GetUnitName(mainSelectedUnit))
//call ClearTextMessages()
//call print("------------------------------------------------------")
if mainSelectedUnit != null then
set frame = clickedFrame.handle
set syncer = d.from
set pid = GetPlayerId(syncer)
set table = frame2Item[GetHandleId(frame)]
set slot = frame2Slot[GetHandleId(frame)]
set originSlot = S2I(iter.read())
if iter.read() == "true" then
set itemOnMouse = true
else
set itemOnMouse = false
endif
if itemOnMouse then
set itemInOriginSlot = UnitItemInSlot(mainSelectedUnit, originSlot)
if itemInOriginSlot != null then
set pFrame = playerFrame[pid]
set playerFrame[pid] = null
call SetItemPosition(itemInOriginSlot, WorldBounds.minX, WorldBounds.minY)
call SetItemVisible(itemInOriginSlot, false)
if LOCAL_PLAYER == syncer then
call slot.update(itemInOriginSlot)
set originItemSlot = -1
set originItemSlotEx = -1
endif
set slotItem = table.item[GetHandleId(mainSelectedUnit)]
set table.item[GetHandleId(mainSelectedUnit)] = itemInOriginSlot
set playerFrame[pid] = pFrame
if pFrame == null then
if slotItem != null then
//call print("Click Slot - itemOnMouse - no pFrame: " + GetItemName(slotItem))
call UnitAddItem(mainSelectedUnit, slotItem)
call UnitDropItemSlot(mainSelectedUnit, slotItem, originSlot)
endif
else
if originSlot == 0 then
set playerFrame[pid] = null
set slot = frame2Slot[GetHandleId(pFrame)]
set tb = frame2Item[GetHandleId(pFrame)]
if slotItem == null then
call tb.item.remove(GetHandleId(mainSelectedUnit))
else
set tb.item[GetHandleId(mainSelectedUnit)] = slotItem
endif
//call print("Click Slot - itemOnMouse - pFrame - originSlot 0: " + GetItemName(slotItem))
if LOCAL_PLAYER == syncer then
call slot.update(slotItem)
call BlzFrameSetVisible(dummyIcon, false)
call BlzFrameSetAllPoints(ORIGIN_ITEM_BUTTON_ONE, dummyFrame)
call BlzFrameSetVisible(slot.indicator, false)
endif
if unit2Item.item.has(GetHandleId(mainSelectedUnit)) then
set item0 = unit2Item.item[GetHandleId(mainSelectedUnit)]
call TimerStart(NewTimerEx(UnitItem.create(mainSelectedUnit, item0)), 0, false, function thistype.onExpired)
//call UnitAddItem(mainSelectedUnit, item0)
//call UnitDropItemSlot(mainSelectedUnit, item0, 0)
call unit2Item.item.remove(GetHandleId(mainSelectedUnit))
endif
else
set playerFrame[pid] = null
if slotItem != null then
call UnitAddItem(mainSelectedUnit, slotItem)
call UnitDropItemSlot(mainSelectedUnit, slotItem, originSlot)
endif
set playerFrame[pid] = pFrame
endif
set pFrame = null
endif
endif
set itemInOriginSlot = null
else
if table.item.has(GetHandleId(mainSelectedUnit)) then
if playerFrame[GetPlayerId(syncer)] != null then
set pFrame = playerFrame[GetPlayerId(syncer)]
set playerFrame[GetPlayerId(syncer)] = null
set pSlot = frame2Slot[GetHandleId(pFrame)]
set tb = frame2Item[GetHandleId(pFrame)]
set slotItem = tb.item[GetHandleId(mainSelectedUnit)]
call SetItemPosition(slotItem, WorldBounds.minX, WorldBounds.minY)
call SetItemVisible(slotItem, false)
if LOCAL_PLAYER == syncer then
call BlzFrameSetVisible(pSlot.indicator, false)
endif
if unit2Item.item.has(GetHandleId(mainSelectedUnit)) then
set item0 = unit2Item.item[GetHandleId(mainSelectedUnit)]
call UnitAddItem(mainSelectedUnit, item0)
call UnitDropItemSlot(mainSelectedUnit, item0, 0)
call unit2Item.item.remove(GetHandleId(mainSelectedUnit))
endif
endif
if LOCAL_PLAYER == syncer then
call BlzFrameSetVisible(slot.indicator, true)
call BlzFrameSetAllPoints(ORIGIN_ITEM_BUTTON_ONE, frame)
set originItemSlot = -1
set originItemSlotEx = -1
call BlzFrameSetEnable(dummyFrameEx, true)
endif
set slotItem = UnitItemInSlot(mainSelectedUnit, 0)
if slotItem != null then
set unit2Item.item[GetHandleId(mainSelectedUnit)] = slotItem
//set item2Unit.unit[GetHandleId(slotItem)] = mainSelectedUnit
//call print("Clicked slot no item on mouse: " + GetItemName(slotItem))
if LOCAL_PLAYER == syncer then
call BlzFrameSetTexture(dummyIcon, BlzGetAbilityIcon(GetItemTypeId(slotItem)), 0, true)
call BlzFrameSetVisible(dummyIcon, true)
endif
call SetItemPosition(slotItem, WorldBounds.minX, WorldBounds.minY)
call SetItemVisible(slotItem, false)
set slotItem = null
endif
set slotItem = table.item[GetHandleId(mainSelectedUnit)]
call UnitAddItem(mainSelectedUnit, slotItem)
call UnitDropItemSlot(mainSelectedUnit, slotItem, 0)
set slotItem = null
set playerFrame[GetPlayerId(syncer)] = frame
endif
endif
set frame = null
set mainSelectedUnit = null
set syncer = null
endif
call d.destroy()
call clickedFrame.destroy()
endmethod
private static method onClick takes nothing returns nothing
local SyncData req = SyncData.create(GetTriggerPlayer())
local string str
set data2Frame[req] = Frame.create(BlzGetTriggerFrame())
call UnfocusFrame(BlzGetTriggerFrame(), GetTriggerPlayer())
if frame2Slot.has(GetHandleId(BlzGetTriggerFrame())) then
set str = ""
if LOCAL_PLAYER == GetTriggerPlayer() then
set str = I2S(GetSelectedUnitIndex()) + " "
set str = str + I2S(originItemSlot) + " "
if IsMouseDraggingItem() then
set str = str + "true"
else
set str = str + "false"
endif
endif
set req.onComplete = Filter(function thistype.clickAction)
call req.syncString(str, StringLength(str))
//call SyncStr(str, GetTriggerPlayer(), function thistype.clickAction)
endif
endmethod
private static method onEnter takes nothing returns nothing
call UnfocusFrame(BlzGetTriggerFrame(), GetTriggerPlayer())
call BlzFrameSetEnable(BlzGetTriggerFrame(), false)
if BlzGetTriggerFrame() == dummyFrameEx then
if IsMouseDraggingItem() then
set originItemSlotEx = 0
endif
endif
endmethod
private static method onItemMove takes nothing returns nothing
local item item0
local unit u = GetInventoryManipulatingUnit()
local item itm
local item swapped
local integer slotFrom
local integer slotTo = GetInventorySlotTo()
local player p = GetOwningPlayer(u)
local integer pid = GetPlayerId(p)
local thistype slot
local framehandle pFrame
local Table tb
if LOCAL_PLAYER == p then
set originItemSlot = slotTo
endif
set pFrame = playerFrame[pid]
set playerFrame[pid] = null
if pFrame != null then
//call print("Item Move: " + GetItemName(GetInventoryManipulatedItem()))
set itm = GetInventoryManipulatedItem()
set swapped = GetInventorySwappedItem()
set slot = frame2Slot[GetHandleId(pFrame)]
set slotFrom = GetInventorySlotFrom()
if slotTo == slotFrom then
else
call BlzFrameSetEnable(dummyFrame, true)
set tb = frame2Item[GetHandleId(pFrame)]
if slotFrom == 0 then
if swapped != null then
set tb.item[GetHandleId(u)] = swapped
call SetItemPosition(swapped, WorldBounds.minX, WorldBounds.minY)
call SetItemVisible(swapped, false)
if LOCAL_PLAYER == p then
call slot.update(swapped)
call BlzFrameSetVisible(slot.indicator, false)
call BlzFrameSetVisible(dummyIcon, false)
call BlzFrameSetAllPoints(ORIGIN_ITEM_BUTTON_ONE, dummyFrame)
//set originItemSlot = -1
set originItemSlotEx = -1
endif
else
call tb.item.remove(GetHandleId(u))
if LOCAL_PLAYER == p then
call slot.update(null)
call BlzFrameSetVisible(slot.indicator, false)
call BlzFrameSetVisible(dummyIcon, false)
call BlzFrameSetAllPoints(ORIGIN_ITEM_BUTTON_ONE, dummyFrame)
//set originItemSlot = -1
set originItemSlotEx = -1
endif
endif
elseif slotTo == 0 then
set tb.item[GetHandleId(u)] = itm
call print("sloTo 0 - swapped item: " + GetItemName(itm))
call SetItemPosition(itm, WorldBounds.minX, WorldBounds.minY)
call SetItemVisible(itm, false)
if LOCAL_PLAYER == p then
call slot.update(itm)
call BlzFrameSetVisible(slot.indicator, false)
call BlzFrameSetVisible(dummyIcon, false)
call BlzFrameSetAllPoints(ORIGIN_ITEM_BUTTON_ONE, dummyFrame)
//set originItemSlot = -1
set originItemSlotEx = -1
endif
call UnitDropItemSlot(u, swapped, slotFrom)
endif
if slotFrom == 0 or slotTo == 0 then
set pFrame = null
set item0 = unit2Item.item[GetHandleId(u)]
//call print("sloTo 0 - item0: " + GetItemName(item0))
if item0 != null then
call unit2Item.item.remove(GetHandleId(u))
call SetItemVisible(item0, true)
call UnitAddItem(u, item0)
call UnitDropItemSlot(u, item0, 0)
set item0 = null
endif
endif
endif
endif
set u = null
set itm = null
set swapped = null
set playerFrame[pid] = pFrame
set pFrame = null
endmethod
private static method onItemDrop takes nothing returns nothing
local item itm = GetManipulatedItem()
local item item0
local unit u = GetTriggerUnit()
local integer pid = GetPlayerId(GetTriggerPlayer())
local thistype slot
local framehandle pFrame = playerFrame[pid]
local Table tb
if pFrame != null and UnitItemInSlot(u, 0) == itm then
set playerFrame[pid] = null
//call print("Item Drop: " + GetItemName(itm))
call BlzFrameSetEnable(dummyFrame, true)
set slot = frame2Slot[GetHandleId(pFrame)]
set tb = frame2Item[GetHandleId(pFrame)]
call tb.item.remove(GetHandleId(u))
if LOCAL_PLAYER == GetTriggerPlayer() then
call slot.update(null)
call BlzFrameSetVisible(slot.indicator, false)
call BlzFrameSetVisible(dummyIcon, false)
call BlzFrameSetAllPoints(ORIGIN_ITEM_BUTTON_ONE, dummyFrame)
set originItemSlot = -1
set originItemSlotEx = -1
endif
set item0 = unit2Item.item[GetHandleId(u)]
if item0 != null then
//call print("Item Drop - item0: " + GetItemName(item0))
call unit2Item.item.remove(GetHandleId(u))
call TimerStart(NewTimerEx(UnitItem.create(u, item0)), 0, false, function thistype.onExpired)
set item0 = null
endif
endif
endmethod
private static method mouseAction takes nothing returns nothing
local SyncData d = GetSyncedData()
local StringIterator iter = StringIterator.create(d.readString(0))
local IntegerListItem node
local Inventory inv
local Table tb
local player p = d.from
local integer pid = GetPlayerId(p)
local integer selectedIndex
local integer originSlot
local thistype slot
local boolean itemOnMouse
local item item0
local item itm
local unit u
local unit mainUnit
local string str = iter.read()
local framehandle pFrame
call d.destroy()
if not player2Inventory.has(pid) then
return
endif
if str == "true" then
set itemOnMouse = true
else
set itemOnMouse = false
endif
set originSlot = S2I(iter.read())
set selectedIndex = S2I(iter.read())
//set count = count + 1
if itemOnMouse then
//call print("Main Selected Unit Index: " + I2S(selectedIndex))
if playerFrame[pid] != null and originSlot == 0 then
set u = GetMainSelectedUnit(selectedIndex)
set slot = frame2Slot[GetHandleId(playerFrame[pid])]
set item0 = unit2Item.item[GetHandleId(u)]
set tb = frame2Item[GetHandleId(playerFrame[pid])]
if item0 == null then
call tb.item.remove(GetHandleId(u))
else
call unit2Item.item.remove(GetHandleId(u))
set tb.item[GetHandleId(u)] = item0
endif
if LOCAL_PLAYER == p then
call slot.update(item0)
call BlzFrameSetVisible(slot.indicator, false)
call BlzFrameSetVisible(dummyIcon, false)
call BlzFrameSetAllPoints(ORIGIN_ITEM_BUTTON_ONE, dummyFrame)
set originItemSlot = -1
set originItemSlotEx = -1
call ForceUICancel()
call BlzFrameSetEnable(dummyFrameEx, true)
endif
set item0 = null
set u = null
set playerFrame[pid] = null
endif
else
set pFrame = playerFrame[pid]
set playerFrame[pid] = null
set mainUnit = GetMainSelectedUnit(selectedIndex)
set u = player2Unit.unit[pid]
set player2Unit.unit[pid] = mainUnit
if mainUnit != u and u != null then
if unit2Item.item.has(GetHandleId(u)) then
set item0 = unit2Item.item[GetHandleId(u)]
set itm = UnitItemInSlot(u, 0)
call SetItemPosition(itm, WorldBounds.minX, WorldBounds.minY)
call SetItemVisible(itm, false)
call UnitAddItem(u, item0)
call UnitDropItemSlot(u, item0, 0)
call unit2Item.item.remove(GetHandleId(u))
set itm = null
set item0 = null
endif
set inv = player2Inventory[pid]
set node = inv.slots.first
loop
exitwhen node == 0
set slot = node.data
set tb = frame2Item[GetHandleId(slot.actionButton)]
set itm = tb.item[GetHandleId(mainUnit)]
if itm != null then
call SetItemPosition(itm, WorldBounds.minX, WorldBounds.minY)
call SetItemVisible(itm, false)
endif
if LOCAL_PLAYER == p then
call slot.update(itm)
endif
set itm = null
set node = node.next
endloop
if unit2Item.item.has(GetHandleId(mainUnit)) then
set item0 = unit2Item.item[GetHandleId(mainUnit)]
//call print("mainUnit - item0: " + GetItemName(item0))
call UnitAddItem(mainUnit, item0)
call UnitDropItemSlot(mainUnit, item0, 0)
call unit2Item.item.remove(GetHandleId(mainUnit))
set item0 = null
endif
if pFrame != null then
set slot = frame2Slot[GetHandleId(pFrame)]
set pFrame = null
if LOCAL_PLAYER == p then
call BlzFrameSetVisible(slot.indicator, false)
call BlzFrameSetVisible(dummyIcon, false)
call BlzFrameSetAllPoints(ORIGIN_ITEM_BUTTON_ONE, dummyFrame)
set originItemSlot = -1
set originItemSlotEx = -1
call BlzFrameSetEnable(dummyFrameEx, true)
endif
endif
endif
set playerFrame[pid] = pFrame
endif
endmethod
private static method onMouseClick takes nothing returns nothing
local SyncData req = SyncData.create(GetTriggerPlayer())
local string str = ""
if LOCAL_PLAYER == GetTriggerPlayer() then
if IsMouseDraggingItem() then
set str = "true "
else
set str = "false "
endif
set str = str + I2S(originItemSlotEx) + " "
set str = str + I2S(GetSelectedUnitIndex())
endif
set req.onComplete = Filter(function thistype.mouseAction)
call req.syncString(str, StringLength(str))
//call print(str + ": " + I2S(StringLength(str)) + " characters")
//call SyncStr(str, GetTriggerPlayer(), function thistype.mouseAction)
endmethod
private static method onInit takes nothing returns nothing
set frame2Slot = Table.create()
set frame2Item = Table.create()
set unit2Item = Table.create()
set player2Unit = Table.create()
set data2Frame = Table.create()
set actionTrigger = CreateTrigger()
set enterTrigger = CreateTrigger()
call TriggerAddCondition(actionTrigger, function thistype.onClick)
call TriggerAddCondition(enterTrigger, function thistype.onEnter)
set ORIGIN_ITEM_BUTTON_ONE = BlzGetOriginFrame(ORIGIN_FRAME_ITEM_BUTTON, 0)
set dummyFrame = BlzCreateFrameByType("GLUETEXTBUTTON", "DummyFrame", GAME_UI, "", 0)
call BlzFrameSetSize(dummyFrame, 0.030, 0.031)
call BlzFrameSetAbsPoint(dummyFrame, FRAMEPOINT_CENTER, 0.5318, 0.097)
set dummyIcon = BlzCreateFrameByType("BACKDROP", "DummyIcon", dummyFrame, "", 0)
call BlzFrameSetAllPoints(dummyIcon, dummyFrame)
call BlzFrameSetVisible(dummyIcon, false)
set dummyFrameEx = BlzCreateFrameByType("GLUETEXTBUTTON", "DummyFrame", dummyFrame, "", 0)
call BlzFrameSetSize(dummyFrameEx, 0.04, 0.04)
call BlzFrameSetAbsPoint(dummyFrameEx, FRAMEPOINT_CENTER, 0.5318, 0.097)
call BlzTriggerRegisterFrameEvent(enterTrigger, dummyFrameEx, FRAMEEVENT_MOUSE_ENTER)
call RegisterNativeEvent(EVENT_ITEM_INVENTORY_MOVE, function thistype.onItemMove)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function thistype.onItemDrop)
call RegisterAnyPlayerEvent(EVENT_PLAYER_MOUSE_UP, function thistype.onMouseClick)
endmethod
endstruct
struct Inventory extends array
implement Alloc
private static trigger closeTrigger
private static trigger openTrigger
private static Table frame2Inventory
private framehandle title
private framehandle closeButton
private framehandle openIcon
private framehandle nextButton
private framehandle prevButton
private framehandle pageNumber
private integer slotCount
private integer columns
private real borderSize
private real titleSize
private real spaceGap
readonly string emptySlotTexture
readonly string slotIndicatorModel
readonly framehandle backdrop
readonly real buttonSize
framehandle openButton
IntegerList slots
method setSlotCount takes integer count returns thistype
set slotCount = count
return this
endmethod
method setColumnCount takes integer cols returns thistype
set columns = cols
return this
endmethod
method setTitle takes string str returns thistype
call BlzFrameSetText(title, GetLocalizedString(str))
return this
endmethod
method setTitleSize takes real size returns thistype
set titleSize = size
return this
endmethod
method setBorderSize takes real size returns thistype
set borderSize = size
return this
endmethod
method setButtonSize takes real size returns thistype
set buttonSize = size
return this
endmethod
method setButtonSpaceGap takes real size returns thistype
set spaceGap = size
return this
endmethod
method setSlotEmptyTexture takes string texture returns thistype
set emptySlotTexture = texture
return this
endmethod
method setPosition takes real x, real y returns thistype
call BlzFrameSetAbsPoint(backdrop, FRAMEPOINT_CENTER, x, y)
return this
endmethod
method setOpenButtonPosition takes real x, real y returns thistype
call BlzFrameSetAbsPoint(openButton, FRAMEPOINT_CENTER, x, y)
return this
endmethod
method setOpenButtonTexture takes string str returns thistype
call BlzFrameSetTexture(openIcon, str, 0, true)
return this
endmethod
method setSlotIndicatorModel takes string str returns thistype
set slotIndicatorModel = str
return this
endmethod
method show takes boolean flag returns nothing
call BlzFrameSetVisible(backdrop, flag)
endmethod
method showEx takes player p, boolean flag returns nothing
if LOCAL_PLAYER == p then
call BlzFrameSetVisible(backdrop, flag)
endif
if flag then
set player2Inventory[GetPlayerId(p)] = this
else
call player2Inventory.remove(GetPlayerId(p))
endif
endmethod
method build takes nothing returns nothing
local integer rows = slotCount/columns
local integer i = 0
local integer j = 0
local real x = borderSize
local real y = -borderSize - titleSize
call BlzFrameSetSize(backdrop, columns*buttonSize + (columns - 1)*spaceGap + 2*borderSize, rows*buttonSize + (rows - 1)*spaceGap + 2*borderSize + titleSize)
loop
exitwhen i == slotCount
call slots.push(Slot.create(buttonSize, x, y, this))
set x = x + spaceGap + buttonSize
set j = j + 1
if j == columns then
set j = 0
set x = borderSize
set y = y - spaceGap - buttonSize
endif
set i = i + 1
endloop
call BlzFrameSetSize(openButton, buttonSize, buttonSize)
call BlzFrameSetSize(title, 0, titleSize)
call BlzFrameSetPoint(title, FRAMEPOINT_CENTER, backdrop, FRAMEPOINT_TOP, 0, -borderSize*.75)
//call BlzFrameSetSize(pageNumber, 0, titleSize)
//call BlzFrameSetPoint(pageNumber, FRAMEPOINT_CENTER, backdrop, FRAMEPOINT_BOTTOM, 0, borderSize)
call BlzFrameSetSize(openButton, buttonSize, buttonSize)
call BlzTriggerRegisterFrameEvent(openTrigger, openButton, FRAMEEVENT_CONTROL_CLICK)
call BlzFrameSetPoint(closeButton, FRAMEPOINT_TOPRIGHT, backdrop, FRAMEPOINT_TOPRIGHT, 0, 0)
call BlzFrameSetSize(closeButton, buttonSize, buttonSize)
call BlzFrameSetText(closeButton, "X")
call BlzTriggerRegisterFrameEvent(closeTrigger, closeButton, FRAMEEVENT_CONTROL_CLICK)
// call BlzFrameSetPoint(nextButton, FRAMEPOINT_BOTTOMRIGHT, backdrop, FRAMEPOINT_BOTTOMRIGHT, 0, 0)
// call BlzFrameSetSize(nextButton, buttonSize, buttonSize)
// call BlzFrameSetText(nextButton, ">")
//
// call BlzFrameSetPoint(prevButton, FRAMEPOINT_BOTTOMLEFT, backdrop, FRAMEPOINT_BOTTOMLEFT, 0, 0)
// call BlzFrameSetSize(prevButton, buttonSize, buttonSize)
// call BlzFrameSetText(prevButton, "<")
endmethod
private static method closeAction takes nothing returns nothing
local SyncData d = GetSyncedData()
local integer pid = GetPlayerId(d.from)
local thistype this = player2Inventory[pid]
local unit selectedUnit = GetMainSelectedUnit(d.readInt(0))
local item item0
local item itemSlot
local framehandle pFrame
local Slot slot
local Table tb
if LOCAL_PLAYER == GetTriggerPlayer() then
call BlzFrameSetVisible(this.backdrop, false)
endif
call player2Inventory.remove(pid)
set pFrame = playerFrame[pid]
set playerFrame[pid] = null
if pFrame != null then
set slot = Slot.frame2Slot[GetHandleId(pFrame)]
set tb = Slot.frame2Item[GetHandleId(pFrame)]
set itemSlot = tb.item[GetHandleId(selectedUnit)]
call SetItemPosition(itemSlot, WorldBounds.minX, WorldBounds.minY)
call SetItemVisible(itemSlot, false)
set itemSlot = null
set pFrame = null
if LOCAL_PLAYER == GetTriggerPlayer() then
call BlzFrameSetVisible(slot.indicator, false)
call BlzFrameSetVisible(dummyIcon, false)
call BlzFrameSetAllPoints(ORIGIN_ITEM_BUTTON_ONE, dummyFrame)
call BlzFrameSetEnable(dummyFrameEx, true)
endif
set item0 = Slot.unit2Item.item[GetHandleId(selectedUnit)]
if item0 != null then
call UnitAddItem(selectedUnit, item0)
call UnitDropItemSlot(selectedUnit, item0, 0)
call Slot.unit2Item.item.remove(GetHandleId(selectedUnit))
set item0 = null
endif
endif
set selectedUnit = null
call d.destroy()
endmethod
private static method close takes nothing returns nothing
local SyncData req = SyncData.create(GetTriggerPlayer())
call UnfocusFrame(BlzGetTriggerFrame(), GetTriggerPlayer())
set req.onComplete = Filter(function thistype.closeAction)
call req.syncInt(GetSelectedUnitIndex())
endmethod
private static method open takes nothing returns nothing
local thistype this = frame2Inventory[GetHandleId(BlzGetTriggerFrame())]
call UnfocusFrame(BlzGetTriggerFrame(), GetTriggerPlayer())
if LOCAL_PLAYER == GetTriggerPlayer() then
call BlzFrameSetVisible(.backdrop, true)
endif
set player2Inventory[GetPlayerId(GetTriggerPlayer())] = this
endmethod
static method createEx takes integer slotCount, integer cols, real buttonSize, real spaceGap, real borderSize, real titleSize, real posX, real posY returns thistype
local thistype this = allocate()
local integer rows = slotCount/columns
local integer i = 0
local integer j = 0
local real x = borderSize
local real y = -borderSize - titleSize
set .slotCount = slotCount
set .columns = cols
set .buttonSize = buttonSize
set .spaceGap = spaceGap
set .buttonSize = buttonSize
set .borderSize = borderSize
set .titleSize = titleSize
set backdrop = BlzCreateFrame("QuestButtonBackdropTemplate", GAME_UI, 0, this)
call BlzFrameSetSize(backdrop, columns*buttonSize + (columns - 1)*spaceGap + 2*borderSize, rows*buttonSize + (rows - 1)*spaceGap + 2*borderSize + titleSize)
call BlzFrameSetAbsPoint(backdrop, FRAMEPOINT_CENTER, posX, posY)
set title = BlzCreateFrameByType("TEXT", "InventoryTitle", backdrop, "", this)
call BlzFrameSetSize(title, 0, titleSize)
call BlzFrameSetPoint(title, FRAMEPOINT_CENTER, backdrop, FRAMEPOINT_TOP, 0, -borderSize*.75)
call BlzFrameSetTextAlignment(title, TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_TOP)
call BlzFrameSetScale(title, 1.2)
// set pageNumber = BlzCreateFrameByType("TEXT", "InventoryPage", backdrop, "", this)
// call BlzFrameSetSize(pageNumber, 0, titleSize)
// call BlzFrameSetPoint(pageNumber, FRAMEPOINT_CENTER, backdrop, FRAMEPOINT_BOTTOM, 0, borderSize)
// call BlzFrameSetTextAlignment(pageNumber, TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
// call BlzFrameSetText(pageNumber, "1")
set openButton = BlzCreateFrameByType("GLUETEXTBUTTON", "InventoryOpen", GAME_UI, "", this)
set openIcon = BlzCreateFrameByType("BACKDROP", "InventoryOpenIcon", openButton, "", this)
call BlzFrameSetAllPoints(openIcon, openButton)
set closeButton = BlzCreateFrame("ScriptDialogButton", backdrop, 0, this)
call BlzFrameSetPoint(closeButton, FRAMEPOINT_TOPRIGHT, backdrop, FRAMEPOINT_TOPRIGHT, 0, 0)
call BlzFrameSetSize(closeButton, buttonSize, buttonSize)
call BlzFrameSetText(closeButton, "X")
call BlzTriggerRegisterFrameEvent(closeTrigger, closeButton, FRAMEEVENT_CONTROL_CLICK)
// set nextButton = BlzCreateFrame("ScriptDialogButton", backdrop, 0, this)
// call BlzFrameSetPoint(nextButton, FRAMEPOINT_BOTTOMRIGHT, backdrop, FRAMEPOINT_BOTTOMRIGHT, 0, 0)
// call BlzFrameSetSize(nextButton, buttonSize, buttonSize)
// call BlzFrameSetText(nextButton, "→")
//
// set prevButton = BlzCreateFrame("ScriptDialogButton", backdrop, 0, this)
// call BlzFrameSetPoint(prevButton, FRAMEPOINT_BOTTOMLEFT, backdrop, FRAMEPOINT_BOTTOMLEFT, 0, 0)
// call BlzFrameSetSize(prevButton, buttonSize, buttonSize)
// call BlzFrameSetText(prevButton, "←")
// call BlzFrameSetVisible(prevButton, false)
set slots = IntegerList.create()
loop
exitwhen i == slotCount
call slots.push(Slot.create(buttonSize, x, y, this))
set x = x + spaceGap + buttonSize
set j = j + 1
if j == columns then
set j = 0
set x = borderSize
set y = y - spaceGap - buttonSize
endif
set i = i + 1
endloop
call BlzFrameSetVisible(backdrop, false)
return this
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set backdrop = BlzCreateFrame("QuestButtonBackdropTemplate", GAME_UI, 0, this)
set title = BlzCreateFrameByType("TEXT", "InventoryTitle", backdrop, "", this)
call BlzFrameSetTextAlignment(title, TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_TOP)
call BlzFrameSetScale(title, 1.2)
// set pageNumber = BlzCreateFrameByType("TEXT", "InventoryPage", backdrop, "", this)
// call BlzFrameSetTextAlignment(pageNumber, TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
// call BlzFrameSetText(pageNumber, "1")
set openButton = BlzCreateFrameByType("GLUETEXTBUTTON", "InventoryOpen", GAME_UI, "", this)
set openIcon = BlzCreateFrameByType("BACKDROP", "InventoryOpenIcon", openButton, "", this)
call BlzFrameSetAllPoints(openIcon, openButton)
set frame2Inventory[GetHandleId(openButton)] = this
set closeButton = BlzCreateFrame("ScriptDialogButton", backdrop, 0, this)
//set nextButton = BlzCreateFrame("ScriptDialogButton", backdrop, 0, this)
//set prevButton = BlzCreateFrame("ScriptDialogButton", backdrop, 0, this)
set slots = IntegerList.create()
call BlzFrameSetVisible(backdrop, false)
return this
endmethod
private static method init takes nothing returns nothing
set GAME_UI = BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0)
set LOCAL_PLAYER = GetLocalPlayer()
set player2Inventory = Table.create()
set closeTrigger = CreateTrigger()
call TriggerAddCondition(closeTrigger, Filter(function thistype.close))
set openTrigger = CreateTrigger()
call TriggerAddCondition(openTrigger, Filter(function thistype.open))
set frame2Inventory = Table.create()
endmethod
implement MyModule
endstruct
private function MouseOnItem takes nothing returns nothing
local integer index = HoverOriginButton_CurrentSelectedButtonIndex - HoverOriginButton_ItemButtonOffset
if not IsMouseDraggingItem() then
set originItemSlot = index
endif
if index > 0 then
call BlzFrameSetEnable(dummyFrameEx, true)
set originItemSlotEx = index
endif
endfunction
private function MouseLeftItem takes nothing returns nothing
if not IsMouseDraggingItem() then
set originItemSlot = -1
endif
set originItemSlotEx = -1
endfunction
private function Init takes nothing returns nothing
call HoverOriginButtonAdd(false, function MouseOnItem)
call HoverOriginButtonAddClose(function MouseLeftItem)
//set item2Unit = Table.create()
endfunction
endlibrary
Screenshots
How to Import
Wurstscript
vJASS
Import the .toc and .fdf files from test map to your own
Import the code to your project either directly or as a dependency via the Wurstsetup app or git
Import the entire Inventory folder to your map's trigger editor.
In the Trigger Editor, under the JassHelper submenu, make sure Enable JassHelper and Enable vJass are both checked.
Changelog
Wurstscript
vJASS
1.0.0 7 February 2023:
First Release
1.0.1 7 February 2023:
Fixes for potential desyncs.
1.0.2 7 February 2023:
Fixed the MPI aspect of the system - visuals and item handling
Items are now correctly stored in custom inventory per player
1.0.7 25 February 2023:
First Release
1.0.8 25 February 2023:
Fixed bugs with item selection with closing the inventory and reselection (Thanks to Izzetin)
I had to convert the script manually. However, the Wurst compiler does run JASSHelper beforehand, so you could actually use functions from compiled vJASS code in the same map. The compiler doesn't work with maps that have Lua as the main scripting language.
All the code has been compiled into that one war3map.lua file. You can grab the original code from the map description. To use this system, however, you will need to set up a Wurst project. I am planning to make a GUI-Friendly version with Lua/vJASS.
I think theres a bug in the 1.0.7 JASS version. if you try to place an Item from 1 free Bag slot to another (With Button Effect) the Item-Icon isnt moveable anymore and is stuck on the screen (I can provide screenshots if needed)
I think theres a bug in the 1.0.7 JASS version. if you try to place an Item from 1 free Bag slot to another (With Button Effect) the Item-Icon isnt moveable anymore and is stuck on the screen (I can provide screenshots if needed)
I just downloaded the version from this thread and tested it just now.
This is what I am seeing on my end. After swapping item positions, I can still move the items to another slot.
@Izzetin Is this not what is happening for you? Or did you meant something else?
Does the big inventory window act as active inventory slots (allowing heroes to receive bonus/abilities from more than 6 items allowed) or as a backpack (items put there do not provide any bonus/abilities, only the default 6 slots are active)
At the moment, the inventory is just a storage device. However, such is not off the table. I might abuse the inventory ability to do so or figure out something else. Perhaps, I would provide custom events for when an item is stored in or removed from the inventory. From there, you can add any abilities you like given the item concerned.
At the moment, the inventory is just a storage device. However, such is not off the table. I might abuse the inventory ability to do so or figure out something else. Perhaps, I would provide custom events for when an item is stored in or removed from the inventory. From there, you can add any abilities you like given the item concerned.
The Wurst version is player-owned, but the vJASS version is unit-owned. I am going to introduce a lock mechanism, so you can lock the inventory to a unit. From there, you can lock the inventory to a dummy unit. Then all units of a player can share an inventory. Or I might just make another version that is player based. It will definitely be easier to code than the unit-based one.
If the system is capable of holding tens of slots, it would make sense for the inventory to be player-owned, as a common stash for units owned by a player.
You can have 6 Active Items. With this Inventory you can ''Click'' 1 Item in the Custom Inventory and the Unit gets the Ability/Stats of the clicked item. so you can have 7 Active Items at once if you leave the Inventory open. is this intended ?
If the system is capable of holding tens of slots, it would make sense for the inventory to be player-owned, as a common stash for units owned by a player.
With the way I coded the system, the number of slots you can have all together in one map is the array limit in theory. You are also limited by the operation limit. So, if you have one inventory instance, that one inventory could have 32768 slots. Each inventory instance will divide up the number of slots. I will soon change this.
Some users may want the system to be player-based, while others may want it to be unit-based. I am going to cater to both sides since that would give the system greater potential for branching. I just haven't decided yet how to approach it. A two in one system or two separate systems?
You can have 6 Active Items. With this Inventory you can ''Click'' 1 Item in the Custom Inventory and the Unit gets the Ability/Stats of the clicked item. so you can have 7 Active Items at once if you leave the Inventory open. is this intended ?
It wasn't intended but it is a consequence of the method that I used to enable drag and drop. To remedy this, I could use dummy items or disable abilities. I will look into this. Besides, this is only when an item is selected. Other times, all is well.
That system is exactly what i was looking for !!! Thnx !!!
Allthought i have no knowledge of vJASS or Wrust and in the how to import tab about vJASS you say just copy the folder ...
I did but i get like 2228 compile errors , can you please help me to fix this .
The system is very good, but I tested it in multiplayer and it doesn't work correctly, when two players try to open the bag at the same time it crashes. I did the test in the jass version.
While I'm not qualified to review the Wurst version, since I don't really know it, I still can say this is a pretty good system. Perhaps you could add the clicking sound when one clicks on the button to open the inventory, and highlighting, to show it's interactable and not simply a frame with no events for decoration. I only took a glimpse on the VJASS version. I'm not sure if it's actually possible for one reviewer to review part of a resource, and another one review the other part. The only current Wurst reviewer I know is Cokemonkey11.
The vJass system crashed when I was putting an item to the inventory AND I had tens of thousands of destructibles / doodads. I spent a day to troubleshoot it :')
I selected a bunch of doodads and clicked Edit / Select All Special then delete.
Turns out that the root cause of the issue was Blizzard's Volcano doodad.
I don't know why, but it crashed the game.
Another issue to report:
System crashes map when loading saves.
The root cause is Blizzard's bug with the frames being tripped by the vJass HoverOriginButton library
To reproduce:
(I'm using the latest version of Warcraft III.)
Take a completely empty map.
Inject vJass HoverOriginButton system into the triggers of the map.
Start the map.
Do a save.
Load the save.
After it loads and you press the ANY key, Warcraft III will crash.
library FrameLoader initializer init_function
// in 1.31 and upto 1.32.9 PTR (when I wrote this). Frames are not correctly saved and loaded, breaking the game.
// This library runs all functions added to it with a 0s delay after the game was loaded.
// function FrameLoaderAdd takes code func returns nothing
// func runs when the game is loaded.
globals
private trigger eventTrigger = CreateTrigger()
private trigger actionTrigger = CreateTrigger()
private timer t = CreateTimer()
endglobals
function FrameLoaderAdd takes code func returns nothing
call TriggerAddAction(actionTrigger, func)
endfunction
private function timerAction takes nothing returns nothing
call TriggerExecute(actionTrigger)
endfunction
private function eventAction takes nothing returns nothing
call TimerStart(t, 0, false, function timerAction)
endfunction
private function init_function takes nothing returns nothing
call TriggerRegisterGameEvent(eventTrigger, EVENT_GAME_LOADED)
call TriggerAddAction(eventTrigger, function eventAction)
endfunction
endlibrary
And then find all the code in the Inventory library and some rewrite it to work around the bug :')
Codeless Save and Load allows you to save Integers. Integers can represent anything and everything. So you can save anything you want.
However, glancing at the Jass API, this system doesn't seem to have a simple AddItemToInventorySlot() function. Instead, everything seems to be hardcoded based on Mouse events (dragging and dropping). You'll have to request that the Author adds some kind of functionality to build an Inventory with an array of predetermined items. Or add it yourself if you understand the code.
Codeless Save and Load allows you to save Integers. Integers can represent anything and everything. So you can save anything you want.
However, glancing at the Jass API, this system doesn't seem to have a simple AddItemToInventorySlot() function. Instead, everything seems to be hardcoded based on Mouse events (dragging and dropping). You'll have to request that the Author adds some kind of functionality to build an Inventory with an array of predetermined items. Or add it yourself if you understand the code.
I don't have any experience with vJASS. What do I need to do to get the Inventory Button to show up in my map?
Furthermore do I need to keep the following code in order to make it work?
I don't have any experience with vJASS. What do I need to do to get the Inventory Button to show up in my map?
Furthermore do I need to keep the following code in order to make it work?
That Test function creates a new inventory and shows it to Player 1. I'm not sure if each Player needs to create their own inventory but I'd assume so.
Anyway, here's some code that you can put in your map that will automatically create an Inventory for each Player and position the Inventory Button to a convenient location near the standard Inventory:
vJASS:
scope SetupInventories initializer Init
private function CreateInv takes player p returns nothing
local Inventory inv = Inventory.create()/*
*/.setTitle("|cffffcc00Inventory|r") /*
*/.setTitleSize(0.015) /*
*/.setBorderSize(0.029) /*
*/.setButtonSize(0.03) /*
*/.setSlotCount(12) /*
*/.setColumnCount(4) /*
*/.setPosition(0.45, 0.35) /*
*/.setButtonSpaceGap(0.01) /*
*/.setSlotIndicatorModel("UI\\Feedback\\Autocast\\UI-ModalButtonOn.mdx") /*
*/.setSlotEmptyTexture("ui\\widgets\\escmenu\\human\\quest-completed-background.blp") /*
*/.setOpenButtonTexture("ReplaceableTextures/CommandButtons/BTNDustOfAppearance")
call inv.build()
call inv.showEx(p, true)
call BlzFrameSetAbsPoint(inv.openButton, FRAMEPOINT_CENTER, 0.6, 0.15)
//call BlzFrameSetPoint(inv.openButton, FRAMEPOINT_TOPLEFT, BlzGetFrameByName("TipsTextArea", 0), FRAMEPOINT_TOPRIGHT, 0, 0)
endfunction
private function SetupInv takes nothing returns nothing
local integer i = 0
local player p
loop
set p = Player(i)
if GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
call CreateInv(p)
endif
set i = i + 1
exitwhen i > 23
endloop
call DestroyTimer(GetExpiredTimer())
set p = null
endfunction
private function Init takes nothing returns nothing
call TimerStart(CreateTimer(), 0.01, false, function SetupInv)
endfunction
endscope
You can adjust the position of the Inventory Button here:
0.6 = X position (left and right).
0.15 = Y position (up and down).
To put these values into perspective, that Inventory Button is about X: 0.03, Y: 0.03 units in size. So adjusting the x/y values by increments of 0.01 should be good for precise positioning. Increasing 0.6 to 0.63 for example would move the Button "one button" over to the right.
That Test function creates a new inventory and shows it to Player 1. I'm not sure if each Player needs to create their own inventory but I'd assume so.
Anyway, here's some code that you can put in your map that will automatically create an Inventory for each Player and position the Inventory Button to a convenient location near the standard Inventory:
vJASS:
scope SetupInventories initializer Init
private function CreateInv takes player p returns nothing
local Inventory inv = Inventory.create()/*
*/.setTitle("|cffffcc00Inventory|r") /*
*/.setTitleSize(0.015) /*
*/.setBorderSize(0.029) /*
*/.setButtonSize(0.03) /*
*/.setSlotCount(12) /*
*/.setColumnCount(4) /*
*/.setPosition(0.45, 0.35) /*
*/.setButtonSpaceGap(0.01) /*
*/.setSlotIndicatorModel("UI\\Feedback\\Autocast\\UI-ModalButtonOn.mdx") /*
*/.setSlotEmptyTexture("ui\\widgets\\escmenu\\human\\quest-completed-background.blp") /*
*/.setOpenButtonTexture("ReplaceableTextures/CommandButtons/BTNDustOfAppearance")
call inv.build()
call inv.showEx(p, true)
call BlzFrameSetAbsPoint(inv.openButton, FRAMEPOINT_CENTER, 0.6, 0.15)
//call BlzFrameSetPoint(inv.openButton, FRAMEPOINT_TOPLEFT, BlzGetFrameByName("TipsTextArea", 0), FRAMEPOINT_TOPRIGHT, 0, 0)
endfunction
private function SetupInv takes nothing returns nothing
local integer i = 0
local player p
loop
set p = Player(i)
if GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
call CreateInv(p)
endif
set i = i + 1
exitwhen i > 23
endloop
call DestroyTimer(GetExpiredTimer())
set p = null
endfunction
private function Init takes nothing returns nothing
call TimerStart(CreateTimer(), 0.01, false, function SetupInv)
endfunction
endscope
You can adjust the position of the Inventory Button here:
0.6 = X position (left and right).
0.15 = Y position (up and down).
To put these values into perspective, that Inventory Button is about X: 0.03, Y: 0.03 units in size. So adjusting the x/y values by increments of 0.01 should be good for precise positioning. Increasing 0.6 to 0.63 for example would move the Button "one button" over to the right.
That Test function creates a new inventory and shows it to Player 1. I'm not sure if each Player needs to create their own inventory but I'd assume so.
Anyway, here's some code that you can put in your map that will automatically create an Inventory for each Player and position the Inventory Button to a convenient location near the standard Inventory:
vJASS:
scope SetupInventories initializer Init
private function CreateInv takes player p returns nothing
local Inventory inv = Inventory.create()/*
*/.setTitle("|cffffcc00Inventory|r") /*
*/.setTitleSize(0.015) /*
*/.setBorderSize(0.029) /*
*/.setButtonSize(0.03) /*
*/.setSlotCount(12) /*
*/.setColumnCount(4) /*
*/.setPosition(0.45, 0.35) /*
*/.setButtonSpaceGap(0.01) /*
*/.setSlotIndicatorModel("UI\\Feedback\\Autocast\\UI-ModalButtonOn.mdx") /*
*/.setSlotEmptyTexture("ui\\widgets\\escmenu\\human\\quest-completed-background.blp") /*
*/.setOpenButtonTexture("ReplaceableTextures/CommandButtons/BTNDustOfAppearance")
call inv.build()
call inv.showEx(p, true)
call BlzFrameSetAbsPoint(inv.openButton, FRAMEPOINT_CENTER, 0.6, 0.15)
//call BlzFrameSetPoint(inv.openButton, FRAMEPOINT_TOPLEFT, BlzGetFrameByName("TipsTextArea", 0), FRAMEPOINT_TOPRIGHT, 0, 0)
endfunction
private function SetupInv takes nothing returns nothing
local integer i = 0
local player p
loop
set p = Player(i)
if GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
call CreateInv(p)
endif
set i = i + 1
exitwhen i > 23
endloop
call DestroyTimer(GetExpiredTimer())
set p = null
endfunction
private function Init takes nothing returns nothing
call TimerStart(CreateTimer(), 0.01, false, function SetupInv)
endfunction
endscope
You can adjust the position of the Inventory Button here:
0.6 = X position (left and right).
0.15 = Y position (up and down).
To put these values into perspective, that Inventory Button is about X: 0.03, Y: 0.03 units in size. So adjusting the x/y values by increments of 0.01 should be good for precise positioning. Increasing 0.6 to 0.63 for example would move the Button "one button" over to the right.
Thanks for the quick reply! I tested it and it works until I tried to put an item into the inventory. Then the game crashed, same thing that happened to
emperor_d3stbefore. It was indeed the Volcano doodad that caused the crash after testing it a second time.
Edit: I want to open the Inventory with the ESC button
Show Inventory
Events
Player - Player 1 (Red) skips a cinematic sequence
I would like to know if there is a way that when an Item is moved to the Inventory system, a copy of it can be created in one or more dummy units or if this is already done?
Because then I can save this unit in variables and I can save its inventory in "Codeless Save and Load System"
I would like to know if there is a way that when an Item is moved to the Inventory system, a copy of it can be created in one or more dummy units or if this is already done?
Because then I can save this unit in variables and I can save its inventory in "Codeless Save and Load System"
You can use a Hashtable to store the Items when they're acquired/lost. Then load these Items when it comes time to Save your data using a For Loop.
The basic idea when acquiring an item:
Events
Unit - A unit Acquires an item
Conditions
Actions
Set Variable CV = (Custom value of (Triggering unit))
Set Variable Hero_Item_Count[CV] = (Hero_Item_Count[CV] + 1)
-------- Get Item_Index using a For Loop or some other method --------
Hashtable - Save Item_Type[Item_Index] as Hero_Item_Count[CV] of CV in Item_Hashtable
Then when losing an item:
Events
Unit - A unit Loses an item
Conditions
((Item being manipulated) is Hidden) Equal to False
Actions
Set Variable CV = (Custom value of (Triggering unit))
Set Variable Hero_Item_Count[CV] = (Hero_Item_Count[CV] - 1)
-------- Manage the Hashtable data to remove the now lost item (you could sort everything down an index) --------
Remember that this Inventory system will fire the "A unit Loses an item" Event when you transfer an Item into the custom Inventory UI. This is because the Item is moved out of the hero's standard inventory and made Hidden. That's why we check to see if it's Hidden in the Conditions as to not count it as "lost" when this transfer occurs. Note that the CV (custom value) stuff will only work if your units have a unique custom value.
I can't say for certain that this will work, but I don't see why it wouldn't.
You can use a Hashtable to store the Items when they're acquired/lost. Then load these Items when it comes time to Save your data using a For Loop.
The basic idea when acquiring an item:
Events
Unit - A unit Acquires an item
Conditions
Actions
Set Variable CV = (Custom value of (Triggering unit))
Set Variable Hero_Item_Count[CV] = (Hero_Item_Count[CV] + 1)
-------- Get Item_Index using a For Loop or some other method --------
Hashtable - Save Item_Type[Item_Index] as Hero_Item_Count[CV] of CV in Item_Hashtable
Then when losing an item:
Events
Unit - A unit Loses an item
Conditions
((Item being manipulated) is Hidden) Equal to False
Actions
Set Variable CV = (Custom value of (Triggering unit))
Set Variable Hero_Item_Count[CV] = (Hero_Item_Count[CV] - 1)
-------- Manage the Hashtable data to remove the now lost item (you could sort everything down an index) --------
Remember that this Inventory system will fire the "A unit Loses an item" Event when you transfer an Item into the custom Inventory UI. This is because the Item is moved out of the hero's standard inventory and made Hidden. That's why we check to see if it's Hidden in the Conditions as to not count it as "lost" when this transfer occurs. Note that the CV (custom value) stuff will only work if your units have a unique custom value.
I can't say for certain that this will work, but I don't see why it wouldn't.
Save is easy part.. harder part is how you load, the hardest part is how you put all the items into the inventory at once after load.. my stomach is hurt just think about it..
Save is easy part.. harder part is how you load, the hardest part is how you put all the items into the inventory at once after load.. my stomach is hurt just think about it..
Hey folks, I've been working on this for a few weeks now in my scarce insomniac free time. Currently the code is a mess (had to rewrite everything a couple of times as I learned about new bugs and shortcomings of Warcraft). There are probably leaks. There are probably possibilities for desync...
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.