- Joined
- Nov 20, 2005
- Messages
- 1,156
So, stacking inventory, pretty simple, huh? But what if you want it to stack properly even if you have a full inventory? I set out to solve that, and got a big improvement. Not quite totally fixed, due to an insane bit of stupidity on Blizzard's part (basically, if a hero tries to pass something to another hero that has a full inventory, then the system can't kick in as no order is given, but the hero tries to put the object at the other hero's feet; no way to solve that that I can see).
Without further ado, here's the system - it's a wee bit less efficient than it could be, but is pretty efficient. The instructions are in the code. Does NOT require vJASS.
EDIT: Updated. Improved a few things, changed it to use a stack for the indexes, and made the check implementation non-lame (now just cycles up; this is much safer, unless you do 2^31 orders, which'll never happen). Changed config a bit, made it so that the game cache would not conflict if you have another game cache (presuming it is inited on initialization).
Without further ado, here's the system - it's a wee bit less efficient than it could be, but is pretty efficient. The instructions are in the code. Does NOT require vJASS.
EDIT: Updated. Improved a few things, changed it to use a stack for the indexes, and made the check implementation non-lame (now just cycles up; this is much safer, unless you do 2^31 orders, which'll never happen). Changed config a bit, made it so that the game cache would not conflict if you have another game cache (presuming it is inited on initialization).
JASS:
function TimerAttach takes timer t, real dur, real val, code func returns nothing
call TimerStart(t, val, false, null)
call PauseTimer(t)
call TimerStart(t, dur, false, func)
endfunction
function H2I takes handle h returns integer
return h
return 0
endfunction
// In order to install this system, simply:
// - Copy and paste in the code below into a trigger called "CG Inv"
// - Include the H2I and TimerAttach functions above this code (if you already have them, no need to add them again)
// - Change the full inventory message in gameinterface (under advanced) to be " " (nothing won't work; use shift enter)
// - Change the warning sound for inventory full to something that has no sound (I used 'null' for testing; use shift enter)
// - Add in the data into the config funcs below
// - If you have NewGen, that globals block will do you; if not, you have to add them to the variable editor (without the udg_ prefix)
// - Recommended that the pick up distance variable is 60 more than the one in gameplay constants
// - The gamecache will be inited here ONLY if it is null to start with, so no worries if you already init it elsewhere.
// This system very well, and without extra work, at allowing heroes to pick up items and add them to stack, even if
// their inventory is full.
// HOWEVER, while it works fine if heroes give an item to them and they haven't got a full inventory, but if they have,
// then the item will be dropped on the floor, even if it could be stacked in.
// As soon as you try to pass an item to someone with a full inventory, WC3 tells them to put it on the floor.
// These orders, incidently, set off no events. Due to this silliness, this small 'bug' cannot be fixed.
// Picking up items is fine, but if you pick up and stack an item with full inventory, then queued orders will get messed up (it'll still stack the item fine).
// If you are giving items to stack to units at game init / before 0.01 seconds has elapsed, then this system won't have kicked in yet.
// If you know JASS, you know how to fix it, otherwise just use GUI and manually setup the gamecache (see example).
// CREDITS:
// - Vexorian, SimError, adapted for this to a specific ability and sound.
globals
item array udg_CGINV_target
unit array udg_CGINV_trigger
real array udg_CGINV_x
real array udg_CGINV_y
item array udg_CGINV_slot
integer array udg_CGINV_check
integer array udg_CGINV_linkIndex
integer udg_CGINV_nextIndex = 1
trigger udg_CGINV_trigAquire = null
trigger udg_CGINV_trigOrder = null
trigger udg_CGINV_trigExecute = null
real udg_CGINV_PICK_UP_DISTANCE = 210. // NB: This must be slightly (~50+ at least) more than that in gamplay constants
// The actual different will be smaller, as I think that the game's own system uses the edge of the unit, this uses
// the centre of the unit.
sound udg_CGINV_inventoryFull = null
gamecache udg_gc = null
endglobals
//this could be placed in the map script header and then called from elsewhere. Do this if you are going to use the GUI storage method, or access it from elsewhere.
function CGINV_SetItemTypeStack takes integer id, integer maxstack returns nothing
call StoreInteger(udg_gc, "CGINVitem"+I2S(id), "maxstack", maxstack)
endfunction
// CONFIG FUNCS ==================================================================================
// Here you can add items to stack; note that the number of charges MUST BE MORE THAN 0 in the object editor
// Oh, and don't put waits in this function, it'll kill it.
function CGINV_SetupEx takes nothing returns nothing
call CGINV_SetItemTypeStack('ckng', 5)
endfunction
// This function can be edited, eg: you might want all of the power up type items to stack up to 3 times, etc.
// Normally you'll just want to leave this alone unless you know what you are doing.
function CGINV_GetMaxStack takes item i returns integer
return GetStoredInteger(udg_gc, "CGINVitem"+I2S(GetItemTypeId(i)), "maxstack")
endfunction
// END CONFIG FUNCS ==============================================================================
function CGINV_Setup takes nothing returns nothing
if udg_gc == null then
call FlushGameCache(InitGameCache( "MapName.w3v" ))
set udg_gc = InitGameCache( "MapName.w3v" )
endif
call CGINV_SetupEx()
call DestroyTimer(GetExpiredTimer())
endfunction
function CGINV_InventoryFull takes unit u returns nothing // Adapted from - SimError credits to Vexorian.
if GetLocalPlayer() == GetOwningPlayer(u) then
call StartSound(udg_CGINV_inventoryFull)
call DisplayTimedTextToPlayer( GetLocalPlayer(), 0.52, -1.00, 2.00, "|cffffcc00Inventory is full.|r" )
endif
endfunction
function CGINV_ItemToStack takes unit u, item i returns nothing
local integer max = CGINV_GetMaxStack(i)
local integer li = 0
local integer id = GetItemTypeId(i)
local integer size = UnitInventorySize(u)
local item temp = null
local integer charges = GetItemCharges(i)
local integer tempCharges
loop
set temp = UnitItemInSlot(u, li)
if temp != null then
if GetItemTypeId(temp) == id then
set tempCharges = GetItemCharges(temp)
if tempCharges + charges <= max then
call SetItemCharges(temp, tempCharges + charges)
call RemoveItem(i)
set temp = null
return
elseif tempCharges < max then
set charges = charges - max + tempCharges
call SetItemCharges(temp, max)
endif
endif
endif
set li = li + 1
exitwhen li >= size
endloop
call SetItemCharges(i, charges)
set temp = null
call DisableTrigger(udg_CGINV_trigAquire)
if not(UnitAddItem(u, i)) then
call CGINV_InventoryFull(u)
endif
call EnableTrigger(udg_CGINV_trigAquire)
endfunction
function CGINV_Orders_Condition takes nothing returns boolean
if not(UnitInventorySize(GetTriggerUnit()) == 0) then // This may need work to make sure it doesn't fail on stuff like beserk
call FlushStoredInteger(udg_gc, "CGINV"+I2S(H2I(GetTriggerUnit())), "check")
endif
return false
endfunction
function CGINV_Aquired takes nothing returns nothing
local item i = GetManipulatedItem()
if CGINV_GetMaxStack(i) > 1 then
call SetItemPosition(i, 0., 0.)
call CGINV_ItemToStack(GetTriggerUnit(), i)
endif
set i = null
endfunction
function CGINV_CanStackItem takes unit u, item i, integer size returns boolean
local integer id = GetItemTypeId(i)
local integer li = 0
local item temp = null
local integer max = CGINV_GetMaxStack(i)
if max <= 1 then
return false
endif
loop
set temp = UnitItemInSlot(u, li)
if GetItemTypeId(temp) == id and GetItemCharges(temp) < max then
set temp = null
return true
endif
set li = li + 1
exitwhen li >= size
endloop
set temp = null
return false
endfunction
function CGINV_IncCheck takes unit u returns nothing
local string s = "CGINV"+I2S(H2I(GetTriggerUnit()))
local integer check = GetStoredInteger(udg_gc, s, "check")
if check != 0 then // no need to increment it if it has never fired off the action
call StoreInteger(udg_gc, "CGINV"+I2S(H2I(GetTriggerUnit())), "check", check+1)
endif
endfunction
function CGINV_Direct_Condition takes nothing returns boolean
local integer i = UnitInventorySize(GetTriggerUnit())
local integer check
local string s
if i != 0 then
if GetItemTypeId(GetOrderTargetItem()) != 0 and GetIssuedOrderId() == 851971 then // order smart
if UnitInventoryCount(GetTriggerUnit()) < i then
call CGINV_IncCheck(GetTriggerUnit())
return false
elseif CGINV_CanStackItem(GetTriggerUnit(), GetOrderTargetItem(), i) then
return true
endif
// fake full inventory here, as you need to disable normal messages
call CGINV_InventoryFull(GetTriggerUnit())
endif
call CGINV_IncCheck(GetTriggerUnit())
endif
return false
endfunction
function CGINV_Direct_Ex2 takes nothing returns nothing
local integer ti = R2I(TimerGetRemaining(GetExpiredTimer())+0.9)
local item i = udg_CGINV_target[ti]
local unit u = udg_CGINV_trigger[ti]
local real x = udg_CGINV_x[ti]
local real y = udg_CGINV_y[ti]
local string hs = "CGINV"+I2S(H2I(u))
local integer check = udg_CGINV_check[ti]
call DisableTrigger(udg_CGINV_trigAquire)
call UnitAddItem(u, udg_CGINV_slot[ti])
call EnableTrigger(udg_CGINV_trigAquire)
call DestroyTimer(GetExpiredTimer())
set udg_CGINV_slot[ti] = null
set udg_CGINV_trigger[ti] = null
set udg_CGINV_target[ti] = null
set udg_CGINV_linkIndex[ti] = udg_CGINV_nextIndex
set udg_CGINV_nextIndex = ti
loop
exitwhen (x-GetUnitX(u))*(x-GetUnitX(u)) + (y-GetUnitY(u))*(y-GetUnitY(u)) <= udg_CGINV_PICK_UP_DISTANCE*udg_CGINV_PICK_UP_DISTANCE
call TriggerSleepAction(0.)
if check != GetStoredInteger(udg_gc, hs, "check") or not(x==GetItemX(i)) then
set u = null
set i = null
return
endif
endloop
//call IssueImmediateOrder(u, "stop")
call CGINV_ItemToStack(u, i)
set u = null
set i = null
endfunction
function CGINV_Direct_Ex takes nothing returns nothing
call TriggerExecute(udg_CGINV_trigExecute)
endfunction
function CGINV_Direct_Action takes nothing returns nothing
local item i = GetOrderTargetItem()
local unit u = GetTriggerUnit()
local real x = GetItemX(i)
local real y = GetItemY(i)
local item slot = UnitItemInSlot(u, 0)
local string hs = "CGINV"+I2S(H2I(u))
local integer check = GetStoredInteger(udg_gc, hs, "check") + 1 // increments the check; if this gets to 2^31, well, the world will end first.
local integer index = udg_CGINV_nextIndex
if (x-GetUnitX(u))*(x-GetUnitX(u)) + (y-GetUnitY(u))*(y-GetUnitY(u)) <= udg_CGINV_PICK_UP_DISTANCE*udg_CGINV_PICK_UP_DISTANCE then
call CGINV_ItemToStack(u, i)
set i = null
set u = null
set slot = null
return
endif
set udg_CGINV_nextIndex = udg_CGINV_linkIndex[index]
if udg_CGINV_nextIndex == 0 then
set udg_CGINV_nextIndex = index+1
endif
set udg_CGINV_target[index] = i
set udg_CGINV_trigger[index] = u
set udg_CGINV_x[index] = x
set udg_CGINV_y[index] = y
set udg_CGINV_slot[index] = slot
set udg_CGINV_check[index] = check
//call DisableTrigger(udg_CGINV_trigOrder)
//call IssuePointOrder(u, "move", x, y)
//call EnableTrigger(udg_CGINV_trigOrder)
call StoreInteger(udg_gc, hs, "check", check)
call SetItemPosition(slot, 0., 0.)
call TimerAttach(CreateTimer(), 0., index, function CGINV_Direct_Ex)
set u = null
set i = null
set slot = null
endfunction
//===========================================================================
function InitTrig_CG_Inv takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
call TriggerAddCondition( t, Condition( function CGINV_Direct_Condition ) )
call TriggerAddAction( t, function CGINV_Direct_Action )
set udg_CGINV_trigAquire = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( udg_CGINV_trigAquire, EVENT_PLAYER_UNIT_PICKUP_ITEM )
call TriggerAddAction(udg_CGINV_trigAquire, function CGINV_Aquired )
set udg_CGINV_trigOrder = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( udg_CGINV_trigOrder, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
call TriggerRegisterAnyUnitEventBJ( udg_CGINV_trigOrder, EVENT_PLAYER_UNIT_ISSUED_ORDER )
call TriggerAddCondition( udg_CGINV_trigOrder, Condition( function CGINV_Orders_Condition ) )
set udg_CGINV_trigExecute = CreateTrigger()
call TriggerAddAction(udg_CGINV_trigExecute, function CGINV_Direct_Ex2)
set udg_CGINV_inventoryFull = CreateSound("Sound\\Interface\\Warning\\Human\\KnightInventoryFull1.wav", false, false, false, 10, 10, "")
call TimerStart(CreateTimer(), 0.01, false, function CGINV_Setup)
endfunction
-
Example
-
Events
- Map initialization
- Conditions
-
Actions
- Game Cache - Create a game cache from MapName.w3v
- Set gc = (Last created game cache)
- -------- Obviously you need these variables. --------
- Set CGINV_itemType = Health Stone
- Set CGINV_maxStack = 3
- -------- Call this script each time after setting the variables; it'll handle all the work for you. NEEDS THE FUNCTION IN THE SCRIPT AREA, NOT A TRIGGER --------
- Custom script: call CGINV_SetItemTypeStack(udg_CGINV_itemType, udg_CGINV_maxStack)
-
Events
Last edited: