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).
Code
function TimerAttach takestimer t, real dur, real val, code func returnsnothing call TimerStart(t, val, false, null) call PauseTimer(t) call TimerStart(t, dur, false, func) endfunction
function H2I takeshandle h returnsinteger 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.
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 takesinteger id, integer maxstack returnsnothing call StoreInteger(udg_gc, "CGINVitem"+I2S(id), "maxstack", maxstack) endfunction
// 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 takesnothingreturnsnothing 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 takesitem i returnsinteger return GetStoredInteger(udg_gc, "CGINVitem"+I2S(GetItemTypeId(i)), "maxstack") endfunction // END CONFIG FUNCS ==============================================================================
function CGINV_Setup takesnothingreturnsnothing if udg_gc == nullthen call FlushGameCache(InitGameCache("MapName.w3v")) set udg_gc = InitGameCache("MapName.w3v") endif call CGINV_SetupEx() call DestroyTimer(GetExpiredTimer()) endfunction
function CGINV_InventoryFull takesunit u returnsnothing// 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 takesunit u, item i returnsnothing localinteger max = CGINV_GetMaxStack(i) localinteger li = 0 localinteger id = GetItemTypeId(i) localinteger size = UnitInventorySize(u) localitem temp = null localinteger charges = GetItemCharges(i) localinteger tempCharges
loop set temp = UnitItemInSlot(u, li) if temp != nullthen 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)
function CGINV_Orders_Condition takesnothingreturnsboolean ifnot(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 returnfalse endfunction
function CGINV_Aquired takesnothingreturnsnothing localitem 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 takesunit u, item i, integer size returnsboolean localinteger id = GetItemTypeId(i) localinteger li = 0 localitem temp = null localinteger max = CGINV_GetMaxStack(i) if max <= 1 then returnfalse endif loop set temp = UnitItemInSlot(u, li) if GetItemTypeId(temp) == id and GetItemCharges(temp) < max then set temp = null returntrue endif set li = li + 1 exitwhen li >= size endloop set temp = null returnfalse endfunction
function CGINV_IncCheck takesunit u returnsnothing localstring s = "CGINV"+I2S(H2I(GetTriggerUnit())) localinteger 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 takesnothingreturnsboolean localinteger i = UnitInventorySize(GetTriggerUnit()) localinteger check localstring s if i != 0 then if GetItemTypeId(GetOrderTargetItem()) != 0 and GetIssuedOrderId() == 851971 then// order smart if UnitInventoryCount(GetTriggerUnit()) < i then call CGINV_IncCheck(GetTriggerUnit()) returnfalse elseif CGINV_CanStackItem(GetTriggerUnit(), GetOrderTargetItem(), i)then returntrue endif // fake full inventory here, as you need to disable normal messages call CGINV_InventoryFull(GetTriggerUnit()) endif call CGINV_IncCheck(GetTriggerUnit()) endif returnfalse endfunction
function CGINV_Direct_Ex2 takesnothingreturnsnothing localinteger ti = R2I(TimerGetRemaining(GetExpiredTimer())+0.9) localitem i = udg_CGINV_target[ti] localunit u = udg_CGINV_trigger[ti] localreal x = udg_CGINV_x[ti] localreal y = udg_CGINV_y[ti] localstring hs = "CGINV"+I2S(H2I(u)) localinteger check = udg_CGINV_check[ti]
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")ornot(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 takesnothingreturnsnothing call TriggerExecute(udg_CGINV_trigExecute) endfunction
function CGINV_Direct_Action takesnothingreturnsnothing localitem i = GetOrderTargetItem() localunit u = GetTriggerUnit() localreal x = GetItemX(i) localreal y = GetItemY(i) localitem slot = UnitItemInSlot(u, 0) localstring hs = "CGINV"+I2S(H2I(u)) localinteger check = GetStoredInteger(udg_gc, hs, "check") + 1 // increments the check; if this gets to 2^31, well, the world will end first. localinteger 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
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)
__________________
Quote:
[20-00-13] MasterHaosis: Because I am retarded douchebag [20-15-23] MasterHaosis: I am faggot, retard and such.. [20-21-22] MasterHaosis: because I am chat moderator. And you dont have right to speak against me ~Void~: Knock it off, for fuck's sake. Get that bullshit out of your signature and stop being so immature. Let it die out.
Last edited by Captain Griffen; 07-28-2008 at 10:28 AM..
Reason: Updated.
// - Change the warning sound for inventory full to something that has no sound (I used 'null' for testing)
I could not find 'null'. I tested with SubGroupSelectionChange instead and couldn't hear a thing (though I got some music playing on the background).
The order id of "smart" is 851971. Credits to the ConvOrder tool by Blade.dk @ wc3c.
Imo in CGINV_Direct_Condition you should first check GetItemTypeId(GetOrderTargetItem()) != 0 because that rules out all orders that don't target items, which is the majority of them (bigger than the amount of non-smart orders and orders given to units without inventories, which you check first).
This sys is otherwise nice but it screws up all shift-orders given to a unit that is ordered to pick up an item that can be stacked with something in his inventory when it is full. You give the unit a move order that is the cause of this. The unit will still move to the item even if you don't give that order. The unit wont come in range of 150, but atleast 300, so by increasing that range constant this should work without the move order (I tested this and it worked). There is also some stop order in there that imo you could just get rid of...
// - Change the warning sound for inventory full to something that has no sound (I used 'null' for testing)
I could not find 'null'. I tested with SubGroupSelectionChange instead and couldn't hear a thing (though I got some music playing on the background).
Shift-enter. null isn't there, hence why I used it, as it has no sound.
Quote:
The order id of "smart" is 851971. Credits to the ConvOrder tool by Blade.dk @ wc3c.
Meh, a tiny optimisation given the rest of it.
Quote:
Imo in CGINV_Direct_Condition you should first check GetItemTypeId(GetOrderTargetItem()) != 0 because that rules out all orders that don't target items, which is the majority of them (bigger than the amount of non-smart orders and orders given to units without inventories, which you check first).
Hmm...probably a good idea.
Quote:
This sys is otherwise nice but it screws up all shift-orders given to a unit that is ordered to pick up an item that can be stacked with something in his inventory when it is full. You give the unit a move order that is the cause of this. The unit will still move to the item even if you don't give that order. The unit wont come in range of 150, but atleast 300, so by increasing that range constant this should work without the move order (I tested this and it worked). There is also some stop order in there that imo you could just get rid of...
Oooh, I didn't think of queued orders. Yea, if you set the range a bit higher, then both the move order and the stop order become unnecessary.
__________________
Quote:
[20-00-13] MasterHaosis: Because I am retarded douchebag [20-15-23] MasterHaosis: I am faggot, retard and such.. [20-21-22] MasterHaosis: because I am chat moderator. And you dont have right to speak against me ~Void~: Knock it off, for fuck's sake. Get that bullshit out of your signature and stop being so immature. Let it die out.
Imo in CGINV_Direct_Condition you should first check GetItemTypeId(GetOrderTargetItem()) != 0 because that rules out all orders that don't target items, which is the majority of them (bigger than the amount of non-smart orders and orders given to units without inventories, which you check first).
That has to be checked against all to know if I should set its check to zero (show it has been given another order).
Behaviour's a wee bit different with queued orders to what you'd see with the blizzard version (which runs except when it wouldn't work), but nothing you'd really notice or would have an effect on gameplay - it no longer interrupts them too.
And I might an optimisation short cut for where the item was within range already.
__________________
Quote:
[20-00-13] MasterHaosis: Because I am retarded douchebag [20-15-23] MasterHaosis: I am faggot, retard and such.. [20-21-22] MasterHaosis: because I am chat moderator. And you dont have right to speak against me ~Void~: Knock it off, for fuck's sake. Get that bullshit out of your signature and stop being so immature. Let it die out.
It would make more sense to just flush the integer; also, I'm not sure that StoreInteger with a null value actually clears the memory (though it may very well).
Quote:
// This function can be edited, eg: you might want all of the power up type items to stack up to 3 times, etc. function CGINV_GetMaxStack takesitem i returnsinteger return GetStoredInteger(udg_gc, "CGINVitem"+I2S(GetItemTypeId(i)), "maxstack") endfunction
It would make more sense to advise users not to edit it: maxstack will already store the info they need, and leaving this single-lined allows it to be inlined by Vex's Optimizer.
You could always make udg_CGInv_Loop behave like a stack, to waste needless allocation of arrays.
Quote:
function CGINV_CanStackItem takesunit u, item i, integer size returnsboolean localinteger id = GetItemTypeId(i) localinteger li = 0 localitem temp = null localinteger max = CGINV_GetMaxStack(i) if max <= 1 then returnfalse endif loop set temp = UnitItemInSlot(u, li) if GetItemTypeId(temp) == id and GetItemCharges(temp) < max then set temp = null returntrue endif set li = li + 1 exitwhen li >= size endloop set temp = null returnfalse endfunction
All the mcuking about with temp leaves me inclined to believe it would be better to just call UnitItemInSlot twice. Even if you don't want to, though, I would assuming a bit of manipulation would allow you to remove the last set temp = null, unless WarCraft crashes for invalid slots, which I highly doubt (Though you never know). The loop could be similarly edited in CGINV_ItemToStack.
I can't find too much to be picky about, though I haven't pounded it though testing yet.
Updated. Uses a stack, made the check method non-sucky and non-buggy, and also improved a few things (mainly non-collision with existing gamecaches of the same name). Now has examples on how to use in GUI, with just a CnP import and moving a function to the map script section, and out of the trigger.
__________________
Quote:
[20-00-13] MasterHaosis: Because I am retarded douchebag [20-15-23] MasterHaosis: I am faggot, retard and such.. [20-21-22] MasterHaosis: because I am chat moderator. And you dont have right to speak against me ~Void~: Knock it off, for fuck's sake. Get that bullshit out of your signature and stop being so immature. Let it die out.
Can you include a vjass version pls ?
and does this display Inventory is full and make a sound if inventory is full and item you trying to pickup doesnt stack with anything ?
if not make that depending on a boolean variable which I can set true/false in config
Can you include a vjass version pls ?
and does this display Inventory is full and make a sound if inventory is full and item you trying to pickup doesnt stack with anything ?
if not make that depending on a boolean variable which I can set true/false in config
I could make a vJASS version, but meh, it's easier to support just one version. Just put it in a script and forget about it is the idea, really.
And yes, it has a modified SimError.
__________________
Quote:
[20-00-13] MasterHaosis: Because I am retarded douchebag [20-15-23] MasterHaosis: I am faggot, retard and such.. [20-21-22] MasterHaosis: because I am chat moderator. And you dont have right to speak against me ~Void~: Knock it off, for fuck's sake. Get that bullshit out of your signature and stop being so immature. Let it die out.