• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Charge and Combine System by OgreMagi v3.0

Hi, I'm was build system for my system hero defense:
https://www.hiveworkshop.com/threads/make-hero-defense-map-3.351598/

And this is part of what I will introduce. Below is a video introducing the system.
And if you have trouble using it, try this
Charge System: You can set a limit on items that can be stacked in your inventory by option charge_limit
JASS:
//Learn by: https://www.hiveworkshop.com/threads/item-stacking-system.139336/
struct ItemEvent
    //Setting charge limit below here
    static integer charge_limit = 12
    private static method PickUpEvent takes nothing returns nothing
        local item Item = GetManipulatedItem()
        local integer ItemID = GetItemTypeId(GetManipulatedItem())
        local unit u = GetTriggerUnit()
        local real x = GetUnitX(GetTriggerUnit())
        local real y = GetUnitY(GetTriggerUnit())
        local item ItemSlot = null
        local integer n = 1
        // Stack Charge
        if GetItemType(Item) == ITEM_TYPE_CHARGED then
            set n = 1
            loop
                exitwhen n > UnitInventorySize(u)
                set ItemSlot = UnitItemInSlotBJ(u, n)
                if GetItemTypeId(ItemSlot) == ItemID and GetItemCharges(ItemSlot) <.charge_limit and ItemSlot != Item then
                    call SetItemCharges(ItemSlot, (GetItemCharges(ItemSlot)) + GetItemCharges(Item))
                    call RemoveItem(Item)
                    // //Limit Item
                    if GetItemCharges(ItemSlot) >.charge_limit then
                        set bj_lastCreatedItem = CreateItem(ItemID, x, y)
                        call SetItemCharges(bj_lastCreatedItem, GetItemCharges(ItemSlot) -.charge_limit)
                        call UnitAddItemSwapped(bj_lastCreatedItem, u)
                        call SetItemCharges(ItemSlot,.charge_limit)
                        set bj_lastCreatedItem = null
                    endif
                endif
                set ItemSlot = null
                set n = n + 1
            endloop
        endif
        //End Stack Charge
        set Item = null
    endmethod
    private static method OrderItemEvent takes nothing returns nothing
        local integer OrderID = GetIssuedOrderId()
        local unit OrderUnit = GetOrderedUnit()
        local real x = GetUnitX(GetOrderedUnit())
        local real y = GetUnitY(GetOrderedUnit())
        local item OrderItem = GetOrderTargetItem()
        //Split Charge Feature (Only working with item charge)
        if OrderID >= 852002 and OrderID <= 852007 and OrderItem == UnitItemInSlotBJ(OrderUnit, (OrderID - 852001)) and GetItemCharges(OrderItem) > 1 and GetItemType(OrderItem) == ITEM_TYPE_CHARGED then
            call SetItemCharges(OrderItem, (GetItemCharges(OrderItem) -1))
            set bj_lastCreatedItem = CreateItem(GetItemTypeId(OrderItem), x, y)
            call SetItemCharges(bj_lastCreatedItem, 1)
            set bj_lastCreatedItem = null
        endif
        set OrderUnit = null
        set OrderItem = null
    endmethod
    private static method onInit takes nothing returns nothing
        local trigger PickUp = CreateTrigger()
        local trigger OrderItem = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(PickUp, EVENT_PLAYER_UNIT_PICKUP_ITEM)
        call TriggerAddAction(PickUp, function thistype.PickUpEvent)
        call TriggerRegisterAnyUnitEventBJ(OrderItem, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
        call TriggerAddAction(OrderItem, function thistype.OrderItemEvent)
    endmethod
endstruct

Combine Item: working with charge item. Easy Setup.
JASS:
struct ItemCommbine
    integer CombineID = 0
    integer RecipeID = 0
    integer Charge = 0
    ItemRecipe array Items[6]
    method Match takes nothing returns boolean
        local integer n = 1
        loop
            exitwhen n > bj_MAX_INVENTORY
            if .Items[n].IDReq != 0 and.Items[n].Require == false then
                return false
            endif
            set n = n + 1
        endloop
        return true
    endmethod
    method Setup takes nothing returns nothing
        local integer n = 1
        loop
            exitwhen n > bj_MAX_INVENTORY
            set .Items[n] = ItemRecipe.create()
            set n = n + 1
        endloop
    endmethod
    method Reset takes nothing returns nothing
        local integer n = 1
        loop
            exitwhen n > bj_MAX_INVENTORY
            call .Items[n].Reset()
            set n = n + 1
        endloop
    endmethod
    method GiveCombineItem takes unit u returns nothing
        local integer n = 1
        loop
            exitwhen n > UnitInventorySize(u)
            if .Items[n].IDReq != 0 then
                call .Items[n].Remove(u)
            endif
            set n = n + 1
        endloop
        set bj_lastCreatedItem = CreateItem(CombineID, GetUnitX(u), GetUnitY(u))
        call SetItemCharges(bj_lastCreatedItem, .Charge)
        call UnitAddItemSwapped(bj_lastCreatedItem, u)
        set bj_lastCreatedItem = null
    endmethod
endstruct
struct ItemRecipe
    integer ChargeReq = 0
    integer IDReq = 0
    boolean Require = false
    integer slot = 0
    method Check takes integer ID, integer Charge, integer slot returns boolean
        if ID == .IDReq and Charge >= .ChargeReq then
            set .slot = slot
            set .Require = true
            return true
        endif
        return false
    endmethod
    method Remove takes unit u returns nothing
        local integer charge = 0
        local item Item = null
        set Item = UnitItemInSlotBJ(u, .slot)
        set charge = (GetItemCharges(Item) -.ChargeReq)
        if charge <= 0 then
            call RemoveItem(Item)
        else
            call SetItemCharges(Item, charge)
        endif
    endmethod
    method Reset takes nothing returns nothing
        set .slot = 0
        set .Require = false
    endmethod
endstruct
struct CombineSystem
    static ItemCommbine array Combines
    static integer i = 0
    static integer size = 0
    private static method CheckCombine takes nothing returns boolean
        local item Item = GetManipulatedItem()
        local integer ItemID = GetItemTypeId(GetManipulatedItem())
        local unit u = GetTriggerUnit()
        local integer n = 1
        local integer i = 1
        local ItemCommbine Combine
        local item array Inv
        local boolean array UsedSlot
 
        //Put all recipe in powerup type item, and take it have skill: Check Ability 'C000'
        if GetItemType(Item) == ITEM_TYPE_POWERUP and.Find(ItemID) != 0 then
            // call BJDebugMsg("Check")
            call .Find(ItemID).Reset()
            set Combine =.Find(ItemID)
            //
            set n = 1
            loop
                exitwhen n > UnitInventorySize(u)
                set Inv[n] = UnitItemInSlotBJ(u, n)
                set UsedSlot[n] = false
                set n = n + 1
            endloop
            //
            set n = 1
            loop
                exitwhen n > bj_MAX_INVENTORY
                if Combine.Items[n].IDReq != 0 then
                    set i = 1
                    loop
                        exitwhen i > UnitInventorySize(u)
                        if Inv[i] != null and UsedSlot[i] == false then
                            if Combine.Items[n].Check(GetItemTypeId(Inv[i]), GetItemCharges(Inv[i]), i) then
                                set UsedSlot[i] = true
                            endif
                        endif
                        set i = i + 1
                    endloop
                endif
                set n = n + 1
            endloop
            if Combine.Match() then
                // call BJDebugMsg("Match")
                call Combine.GiveCombineItem(u)
                // You can make call something here if combine is success
            else
                // call BJDebugMsg("Unmatch")
                // You can make call something here if combine is fail
            endif
            set Combine = 0
        endif
        return false
    endmethod
    static method New takes nothing returns nothing
        set .i =.i + 1
        set .Combines[.i] = ItemCommbine.create()
        call .Combines[.i].Setup()
        set .size = 0
    endmethod
    static method CombineResult  takes integer id , integer charge returns nothing
        set .Combines[.i].CombineID = id
        set .Combines[.i].Charge = charge
    endmethod
    static method RecipePowerUp takes integer id returns nothing
        set .Combines[.i].RecipeID = id
    endmethod
    static method ItemRecipe takes integer id , integer charge returns nothing
        set .size = .size + 1
        set .Combines[.i].Items[size].IDReq = id
        set .Combines[.i].Items[size].ChargeReq = charge
    endmethod
    private static method SetupEvent takes nothing returns nothing
        set udg_EventSettingCombine = 1.00
        call DestroyTimer(GetExpiredTimer())
     endmethod
    private static method Find takes integer recipeid returns ItemCommbine
        local ItemCommbine Combine = 0
        local integer n = 1
        loop
            exitwhen n >.i
            if .Combines[n].RecipeID == recipeid then
                return .Combines[n]
            endif
            set n = n + 1
        endloop
        return Combine
    endmethod
    private static method onInit takes nothing returns nothing
        local trigger UseRecipe = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(UseRecipe, EVENT_PLAYER_UNIT_PICKUP_ITEM)
        call TriggerAddAction(UseRecipe, function thistype.CheckCombine)
         call TimerStart(CreateTimer(), 0, false, function thistype.SetupEvent)
    endmethod
endstruct
You can listing variables for item
JASS:
// Put all constant ID Item here, use Ctrl + D in object manager to see it
globals
    constant integer ORB_OF_FROST = 'I002'
    constant integer STEEL_SWORD = 'I001'
    constant integer Frostmune = 'I003'
    //Charge
    constant integer GREATER_HEALING = 'pghe'
    constant integer GREATER_MANA = 'pgma'
    constant integer Replenishment_Potion = 'rej3'
    //Recipe
    constant integer Replenishment_Potion_RECIPE = 'C000'
    constant integer Frostmune_RECIPE = 'C001'
endglobals
If you make a new combine, just create a new sample here
JASS:
struct WeaponCombine
    private static method Update takes nothing returns nothing
       //Add more item type Weapon here
        call CombineSystem.New()
        //Input ItemRecipeID
        call CombineSystem.RecipePowerUp(Frostmune_RECIPE)
        //input ItemID, Charge
        call CombineSystem.ItemRecipe(ORB_OF_FROST,0)  //0 Is Equipment
        call CombineSystem.ItemRecipe(STEEL_SWORD,0)
        //Input ItemID, Charge
        call CombineSystem.CombineResult(Frostmune,0)
        //New Item
        call CombineSystem.New()
        call CombineSystem.RecipePowerUp(Frostmune2_RECIPE)
        call CombineSystem.ItemRecipe(ORB_OF_FROST2,0)  //0 Is Equipment
        call CombineSystem.ItemRecipe(STEEL_SWORD2,0)
        call CombineSystem.CombineResult(Frostmune2,0)
   endmethod
    private static method onInit takes nothing returns nothing
        local trigger Setting = CreateTrigger()
        call TriggerRegisterVariableEvent( Setting, "udg_EventSettingCombine", EQUAL, 1.00 )
        call TriggerAddAction( Setting, function thistype.Update )
        // call TimerStart(CreateTimer(), 0, false, function thistype.SetupEvent)
    endmethod
endstruct
or make new file
JASS:
struct ChargeCombineCombine
    private static method Update takes nothing returns nothing
        call CombineSystem.New()
        call CombineSystem.RecipePowerUp(Replenishment_Potion_RECIPE)
        call CombineSystem.ItemRecipe(GREATER_HEALING,1)  //0 Is Equipment
        call CombineSystem.ItemRecipe(GREATER_MANA,1)
        call CombineSystem.CombineResult(Replenishment_Potion,1)
   endmethod
    private static method onInit takes nothing returns nothing
        local trigger Setting = CreateTrigger()
        call TriggerRegisterVariableEvent( Setting, "udg_EventSettingCombine", EQUAL, 1.00 )
        call TriggerAddAction( Setting, function thistype.Update )
        // call TimerStart(CreateTimer(), 0, false, function thistype.SetupEvent)
    endmethod
endstruct

Recipe use Power Up type item and it's use tome of intelligence with no gain intelligence ability
Contents

Charge and Combine System v3 (Map)

Reviews
Wrda
I suspect you meant ItemCombine and not itemCommbine for the struct name, it's not deep. You could expand the system to use the variable event "udg_EventSettingCombine" to simulate events when a combination was successfully and failed. Could be...
JassHelper is so weird. It won't compile in pJASS nor via vanilla World Editor with them in-place, and this is clearly an unintentional bug. Please refrain from exploiting this bug, as it:

1) is extremely confusing
2) will fail with parses such as pJass or transcompilers such as vJass2Lua
3) is used inconsistently throughout your code
 
Level 6
Joined
Nov 5, 2012
Messages
48
1. im using vscode and my extension vjass auto format this syntax. (it's call pjass right?)
2. Yes, I will find extension format normaly syntax
=> It's ok. I need fix it, but my code working right?

JassHelper is so weird. It won't compile in pJASS nor via vanilla World Editor with them in-place, and this is clearly an unintentional bug. Please refrain from exploiting this bug, as it:

1) is extremely confusing
2) will fail with parses such as pJass or transcompilers such as vJass2Lua
3) is used inconsistently throughout your code
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,889
I think you should follow the JPEG guideline somewhat more. It doesn't need to be strictly followed, but has certain things have strong grounds to be the way they are. For example, StructName and not STRUCTNAME and constants to be always capitalized with underscore (_) being the space.
local integer m = 1 isn't being used in the function. local item I = null better naming, please.
You have unit, item and triggers reference leaks, need to null them at the end of the functions.
This resource contains 2 systems, Item stacking/unstacking and item combine system. Item stack and unstack has been done to death, and Baradé's system supports more configurable parts rather than a globa max stack limit for every item, like this one. Item stack is natively possible recently by turning the following constant to true:
1701216797984.png

I don't remember if it is available on 1.31, but probably is.
I'm not sure why an array with 7 items, if max inventory is 6, from this line:
JASS:
ITEM_RECIPE array Items[7]
Not every unit has 6 inventory slots, use UnitInventorySize (keep in mind you will need to -1 if you're going to iterate with UnitItemInSlot). Consequently, replace UnitItemInSlotBJ with UnitItemInSlot.
You don't need to do this set Combine = 0 nor setting bj_lastCreatedItem to null.

JASS:
    private static method onInit takes nothing returns nothing
        call TimerStart(CreateTimer(), 0.5, false, function thistype.SetupEvent)
    endmethod
I don't understand why you need a timer at all, doing everything on onInit is fine. Even if you keep it this way, you're still leaking the timer.
set.i =.i + 1 set.Combines[.i] = ITEM_COMBINE.create() call.Combines[.i].Setup() if.Combines[n].RecipeID == recipeid and such is extremely bizarre sight to see, I'm surprised how it even works.
Having to scroll all the way down to configure the recipes isn't very acceptable. It would be better if you had a function up top for specifically the user to configure easily, which then you would call on the onInit. I'd say the same thing for the sucess and failure of the combine "events". You also need to put the system in a folder independent of the other scripts of this map, as it's easier for the user to import later.

Even though there has been quite a few item combine systems, this one supports charge based requirements, that's a plus.
 
Level 6
Joined
Nov 5, 2012
Messages
48
I think you should follow the JPEG guideline somewhat more. It doesn't need to be strictly followed, but has certain things have strong grounds to be the way they are. For example, StructName and not STRUCTNAME and constants to be always capitalized with underscore (_) being the space.
local integer m = 1 isn't being used in the function. local item I = null better naming, please.
You have unit, item and triggers reference leaks, need to null them at the end of the functions.
This resource contains 2 systems, Item stacking/unstacking and item combine system. Item stack and unstack has been done to death, and Baradé's system supports more configurable parts rather than a globa max stack limit for every item, like this one. Item stack is natively possible recently by turning the following constant to true:
View attachment 455639
I don't remember if it is available on 1.31, but probably is.
I'm not sure why an array with 7 items, if max inventory is 6, from this line:
JASS:
ITEM_RECIPE array Items[7]
Not every unit has 6 inventory slots, use UnitInventorySize (keep in mind you will need to -1 if you're going to iterate with UnitItemInSlot). Consequently, replace UnitItemInSlotBJ with UnitItemInSlot.
You don't need to do this set Combine = 0 nor setting bj_lastCreatedItem to null.

JASS:
    private static method onInit takes nothing returns nothing
        call TimerStart(CreateTimer(), 0.5, false, function thistype.SetupEvent)
    endmethod
I don't understand why you need a timer at all, doing everything on onInit is fine. Even if you keep it this way, you're still leaking the timer.
set.i =.i + 1 set.Combines[.i] = ITEM_COMBINE.create() call.Combines[.i].Setup() if.Combines[n].RecipeID == recipeid and such is extremely bizarre sight to see, I'm surprised how it even works.
Having to scroll all the way down to configure the recipes isn't very acceptable. It would be better if you had a function up top for specifically the user to configure easily, which then you would call on the onInit. I'd say the same thing for the sucess and failure of the combine "events". You also need to put the system in a folder independent of the other scripts of this map, as it's easier for the user to import later.

Even though there has been quite a few item combine systems, this one supports charge based requirements, that's a plus.
1. Just change struct name , remove variable m, rename I,set null unit for remove leak ! 1.31 charge stack not have option, I added it for the older version as well.
2. Used UnitInventorySize, fix some syntax, Removing timer when using event on timer in this case is not necessary.
3. Makes the code flexible and can be set anywhere
4. Use EventGameVariable for setting everywhere

I have completed the changes accordingly and properly. I feel my skills have improved. I look forward to your review @Wrda !
 

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,889
I suspect you meant ItemCombine and not itemCommbine for the struct name, it's not deep.
You could expand the system to use the variable event "udg_EventSettingCombine" to simulate events when a combination was successfully and failed. Could be something such as udg_EventSettingCombine = 2 for success, udg_EventSettingCombine = 3 for failure, in this part:
JASS:
if Combine.Match() then
                // call BJDebugMsg("Match")
                call Combine.GiveCombineItem(u)
                // You can make call something here if combine is success

            else
                // call BJDebugMsg("Unmatch")
                // You can make call something here if combine is fail
            endif
Note that you have to set it back to 0 after.
Furthermore, you could make event responses, such as GetCombinedItem as the result item, GetCombiningRecipe as the item recipe the player buys.

Overall, the system works fine.

Approved
 

deepstrasz

Map Reviewer
Level 69
Joined
Jun 4, 2009
Messages
18,883
Allows to create recipes that require a certain amount of charges, and the fusion is powerup based, so the combining works even with full inventory.
Thank you. I didn't quite really have proper time understanding the system as it was made like a mini-defense map so I was pretty much losing the game before doing enough combinations to get the gist of it.
Please in the future @SunLord try separating the gameplay from the actual system testing.
 
Level 6
Joined
Nov 5, 2012
Messages
48
Thank you. I didn't quite really have proper time understanding the system as it was made like a mini-defense map so I was pretty much losing the game before doing enough combinations to get the gist of it.
Please in the future @SunLord try separating the gameplay from the actual system testing.

I have edited a version that is only used to test the system!
So, is this system updated?
I would like to use for my trinity orpg map, but I already have some glitches with the save that I didn't figure out, I wouldn't like to have some with the recipes aswell :))
Thank you!
Here is a video on how to create a combine, you can use multiple files to manage the types of items you want.
 
Top