| 04-04-2011, 03:46 AM | #1 |
* Modules are superior to text macros in every imaginable way, the syntax is good and readable and there are no name conflict risks. that was said in the introduction to itemdex...a script that Vexorian submitted that i am now going to use in the latest version of my map. i don't need any unit tracking but a minor item tracking system seems like it's efficient and useful...and easy. but i had a major question what did he mean about modules? i've seen them but don't understand them yet...can someone make a quick explanation of how you would convert a textmacro into a module? or is that not really how it works? or what? |
| 04-04-2011, 09:28 AM | #2 |
Modules are textmacros with no parameters but with the following attributes:
|
| 04-04-2011, 06:20 PM | #3 | |
Quote:
let me quote myself like you've heard this one before. can someone make a quick explanation of how you would convert a textmacro into a module? perhaps i should provide the example, huh? i have many textmacro sets like this in libraries: Code:
//! textmacro itemstacking takes itemA, itemB private function f takes nothing returns boolean local unit mu = GetManipulatingUnit() local integer invi = GetInventoryHero(mu, $itemB$) local item itm = null if invi !=6 then set itm=GetItemHero(mu,$itemB$) call SetItemCharges(itm,GetItemCharges(itm)+1) set mu=null return false else call UnitAddItemById(mu,$itemB$) set mu=null return false endif endfunction private function I takes nothing returns nothing call GT_AddItemAcquiredAction(function f, $itemA$) endfunction endscope //! endtextmacro Code:
scope stacka initializer I
//! runtextmacro itemstacking("'desc'","'I007'")
scope stackb initializer I
//! runtextmacro itemstacking("'fgsk'","'I008'") |
| 04-05-2011, 02:38 AM | #4 |
Despite modules being considered a "high-level textmacro", they have different purposes than a textmacro. Textmacros can be used on anything and implemented anywhere. Modules, on the other hand, can only be implemented into structs. The reasons to use a module are pretty much the ones stated by Bribe. If you really want to convert that textmacro into a module, you can always brute force it. JASS:module ItemStacking private static method f takes nothing returns boolean local unit mu = GetManipulatingUnit() local integer invi = GetInventoryHero(mu, thistype.itemB) local item itm if invi != 6 then set itm = GetItemHero(mu, thistype.itemB) call SetItemCharges(itm, GetItemCharges(itm)+1) set mu = null set itm = null return false endif call UnitAddItemById(mu, thistype.itemB) set mu = null return false endmethod static method onInit takes nothing returns nothing call GT_AddItemAcquiredAction(function thistype.f, thistype.itemA) endmethod endmodule That makes the requirement of static integer arrays from the implemented struct. So it would end up looking like this: JASS:struct A static constant integer itemA = 'desc' static constant integer itemB = 'I007' implement ItemStacking endstruct However, that ends up creating problems:
In terms of flexibility, textmacros end up winning the race. However, each has their own benefits, otherwise they would not have been created. ;) |
| 04-05-2011, 09:31 AM | #5 |
Although they are similar in function, textmacros and modules are used for different purposes. I find modules to be more readable and have nicer implementation syntax than textmacros, so I feel that any code that you want users to copy to their libraries should come in modules. For example, grim001 uses modules in his AutoIndex and I use a module for my ABuffStruct. On the other hand, when copying code within a library, textmacros tend to be more useful. When you replicate code within the same library, you usually want each copy to be different in some way and the textmacro parameters allow you to do this easily while modules are not as flexible. For example, I used a textmacro in SpellEvent to replicate event response code for all five of the spell events. However, in general I use a coding style that requires very little use of textmacros. For example, I would write your code in such a way that it wouldn't need to be copied for each stackable item; instead, I would use a hashtable to link source items to results and have one function run whenever any item is acquired, check the hastable to see if that item was registered as a source and if so, create/increase the charges of the other item. |
| 04-05-2011, 08:11 PM | #6 |
oh ya purge... wouldn't the 2 non native functions getitemhero and getinventoryhero need to be present in the module? i didn't post that part of the code? sorry. would i be correct to suppose i should make them methods and put them above the other method there? at any rate, it would appear the point you and previous poster were trying to make was that module was being praised in itemdex for accomplishing something that was quite different from what my textmacro scopes are doing. now anitarf seems to have an idea about hashtables. i would like to see him show me what he means. but maybe i better post the rest of my code...btw, i was noticing cjass was having difficulty compiling my textmacro, so i had to update my textmacro. (quote from DotaMaster666: cJass display syntax error because it process textmacros like #define instruction - you cannot use in textmacros or defines unclossed library or scope (and also closed, but unopened, in cJass you may use private define, so it must process scope and library before define and textmacro).) i will post the textmacro and the entire trigger that i have been working on, even though much of it may be irrelevant. now maybe i can post it and anitarf, perhaps after i do that you could show me what you meant. as it is, i don't know use of hashtables, either. they make no sense to me. bear in mind that some stuff in that trigger might be unused in one map and used in another, since i am currently developing 2+ maps. also, in case you didn't already know it, the stacking is taking advantage of powerups in order to stack items even if the inventory is full. i'm in the process of remaking the code since i got rid of aids and other stuff and so some things might not be optimal right now. Code:
//! runtextmacro itemswitching("'mcri'","'I00B'","a")
//! runtextmacro ammostacking("'I00D'","'mcri'","3","b")
//! runtextmacro ammostacking("'rnec'","'I00E'","4","c")
//! runtextmacro ammostacking("'I00F'","'pgma'","2","d")
//! runtextmacro itemstacking("'desc'","'I007'","a")
//! runtextmacro itemstacking("'fgsk'","'I008'","b")
//! runtextmacro itemstacking("'fgfh'","'I009'","c")
//! runtextmacro itemstacking("'pclr'","'I005'","d")
//! runtextmacro itemstacking("'pman'","'I006'","e")
//! runtextmacro itemstacking("'tgxp'","'I002'","f")
//! runtextmacro itemstacking("'pams'","'I004'","g")
//! runtextmacro itemstacking("'moon'","'I003'","h")
//! runtextmacro itemstacking("'I00C'","'whwd'","i")Code:
//compilation of itemtriggers composed by sankaku
//dubbed item management
scope powerupSENTINEL initializer I
private function F takes nothing returns nothing
if GetWidgetLife(GetManipulatedItem()) == 0 then
call RemoveItem(GetManipulatedItem())
endif
endfunction
private function I takes nothing returns nothing
local trigger T = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( T, EVENT_PLAYER_UNIT_PICKUP_ITEM )
call TriggerAddAction( T, function F)
endfunction
endscope
scope mergeitems initializer I
private function f takes nothing returns boolean
call AddItemCharges(GetManipulatingUnit(),GetManipulatedItem())
return false
endfunction
private function I takes nothing returns nothing
local trigger t = CreateTrigger( )
//! runtextmacro CHASER_PUE("t","EVENT_PLAYER_UNIT_PICKUP_ITEM")
call TriggerAddCondition( t, Condition(function f) )
endfunction
endscope
//=====================================================================================
//HOW TO USE:
//=====================================================================================
//
//=====================================================================================
//MAKE YOUR OWN TRIGGER, CALL IT WHATEVER YOU LIKE...AND MAKE YOUR OWN TEXTMACRO SCOPES
//=====================================================================================
//
//=====================================================================================
//CREDITS
//=====================================================================================
//RDZ's Incredible Item Stacking System of Doom - by Rao Dao Zao
//=====================================================================================
//GTrigger Event System - by Jesus4Lyf
//=====================================================================================
//Itemdex - Vexorian
//=====================================================================================
library StackItems uses GT//and Itemdex
function GetIndexItemCopy takes unit u, integer i, integer in returns integer
local integer index
local item indexItem
set index = 0
loop
set indexItem = UnitItemInSlot(u, index)
if (indexItem != null) and (GetItemTypeId(indexItem) == i) and (GetItemId(indexItem)!=in) then
return index
else
set index=index+1
endif
exitwhen index>5
endloop
return 6
endfunction
function GetInventoryHero takes unit u, integer i returns integer
local integer index
local item indexItem
set index = 0
loop
set indexItem = UnitItemInSlot(u, index)
if (indexItem != null) and (GetItemTypeId(indexItem) == i) then
return index
else
set index=index+1
endif
exitwhen index>5
endloop
return 6
endfunction
function GetItemHero takes unit whichUnit, integer itemId returns item
local integer index = GetInventoryHero(whichUnit, itemId)
if (index == 6) then
return null
else
return UnitItemInSlot(whichUnit, index)
endif
endfunction
function AddItemCharges takes unit x, item y returns boolean
local integer w = GetIndexItemCopy(x, GetItemTypeId(y),GetItemId(y))
local item z = null
if GetItemType(y) !=ConvertItemType(1) then
return false
endif
if w !=6 then
set z=UnitItemInSlot(x,w)
call SetItemCharges(y,GetItemCharges(z)+GetItemCharges(y))
call RemoveItem(z)
set z=null
return false
else
return false
endif
endfunction
//the following is for if you want to add the AUTOMATIC stacking.
//this is useful for if you have units that expect to have full inventories already.
//! textmacro ammostacking takes itemA, itemB, count, type
scope ammo$type$ initializer I
private function f takes nothing returns boolean
local unit mu = GetManipulatingUnit()
local integer invi = GetInventoryHero(mu, $itemB$)
local item itm = null
if invi !=6 then
set itm=GetItemHero(mu,$itemB$)
call SetItemCharges(itm,GetItemCharges(itm)+$count$)
set mu=null
return false
else
call UnitAddItemById(mu,$itemB$)
set mu=null
return false
endif
set mu=null
return false
endfunction
private function I takes nothing returns nothing
call GT_AddItemAcquiredAction(function f, $itemA$)
endfunction
endscope
//! endtextmacro
//this textmacro uses GTrigger by Jesus4Lyf, download it at thehelper.net
//! textmacro itemstacking takes itemA, itemB, type
scope stack$type$ initializer I
private function f takes nothing returns boolean
local unit mu = GetManipulatingUnit()
local integer invi = GetInventoryHero(mu, $itemB$)
local item itm = null
if invi !=6 then
set itm=GetItemHero(mu,$itemB$)
call SetItemCharges(itm,GetItemCharges(itm)+1)
set mu=null
return false
else
call UnitAddItemById(mu,$itemB$)
set mu=null
return false
endif
endfunction
private function I takes nothing returns nothing
call GT_AddItemAcquiredAction(function f, $itemA$)
endfunction
endscope
//! endtextmacro
//remember to make your own textmacro scopes in another trigger, or if you prefer, at the
//bottom of this trigger
//for example:
//! textmacro DisplayToRecipe takes itemA, itemB, type
scope display$type$ initializer I
private function f takes nothing returns boolean
call UnitAddItemById(GetManipulatingUnit(),$itemB$)
return false
endfunction
private function I takes nothing returns nothing
call GT_AddItemAcquiredAction(function f, $itemA$)
endfunction
endscope
//! endtextmacro
/*the following is a textmacro that exchanges one item for another, think of it like
in dota when a hero is ranged or melee, or you have a bear or syllabear item versions...
well it is kinda like that. you can make additional textmacro scopes for different unit
types easily by replacing the UNIT_TYPE_HERO with something else, like in the dota
example of melee or ranged... UNIT_TYPE_MELEE_ATTACKER, in this case, we are checking
to make sure that the heroes get the itemA, but if a different unit picks up the itemA,
that unit gets itemB instead. you can of course name your textmacro whatever you want,
we can of course, make the next one be called itemswitchhero and make a second one
called itemswitchmelee*/
//! textmacro itemswitching takes itemA, itemB, type
scope switch$type$ initializer I
private function f takes nothing returns boolean
local unit mu = GetManipulatingUnit()
if IsUnitType(mu, UNIT_TYPE_HERO) then
set mu=null
return false
else
call UnitRemoveItem(mu,GetManipulatedItem())
call UnitAddItemById(mu,$itemB$)
set mu=null
return false
endif
endfunction
private function I takes nothing returns nothing
call GT_AddItemAcquiredAction(function f, $itemA$)
endfunction
endscope
//! endtextmacro
endlibrary
library Itemdex initializer init
//**************************************************************
//* Itemdex
//* -------
//* I need this for many things, something that indexes items
//* Problems of item indexing:
//* a) Only way to detect item death is with a dynamic trigger
//* b) No known way to detect an item removal event. Besides
//* expecting the user to tell you.
//* c) Runes leave some kind of ghost item with 0.0 life that
//* still got all the data of the item , including its type.
//*
//* ...So, I use a loop to do index garbage collection,
//* see you in hell. Luckily it is even rarer to think of 8190
//* items than to think of 8190 units so it should work ok...
//*
//* ItemUserData Version//comments added for table version creation
//*
//***************************************************************
globals
private constant real RECYCLE_INTERVAL = 45.0//2.5
private constant integer RECYCLE_COUNT= 72//10
// Smaller RECYCLE_INTERVAL / Bigger RECYCLE_COUNT equal faster
// detection of removed items and worse performance.
//
// "Every RECYCLE_INTERVAL seconds RECYCLE_COUNT indexes are
// reviewed looking for removed/dead ones to recycle"
endglobals
private struct index
item attachedto
endstruct
globals
private index array activeIndexes
private integer indexN = 0
private integer DataInteger
endglobals
function GetItemId takes item it returns integer
local index d
//sankaku adds the if then to avoid indexing blank inventory slots...
if it==null then
return 0
endif
//iftablethenset d = index( HandleTable(DataInteger)[it] )
set d = index( GetItemUserData(it) )
if(d==0) then
set d = index.create()
set d.attachedto = it
//iftablethenset HandleTable(DataInteger)[it] = integer(d)
call SetItemUserData(it, integer(d) )
set activeIndexes[indexN] = d
set indexN = indexN + 1
endif
return integer(d)
endfunction
// Requirements to use ItemdexModule:
// : create takes no arguments
// : no custom .destroy method
//
module Itemdex
private static thistype array V
private item attachedto = null
method operator thatItem takes nothing returns item
return this.attachedto
endmethod
private static method get takes nothing returns nothing
//remind me to add .name to structs
endmethod
static method operator [] takes item it returns thistype
local integer d = GetItemId(it)
if( (.V[d]!=0) and (.V[d].attachedto != it) ) then
call .V[d].destroy()
set .V[d] = 0
endif
if ( .V[d] == 0) then
set .V[d] = thistype.create()
set .V[d].attachedto = it
endif
return .V[d]
endmethod
method destroy takes nothing returns nothing
local integer d
if ( this.attachedto != null) then
set d = GetItemId( this.attachedto)
set .V[d] = 0
call deallocate()
set this.attachedto = null
else
call deallocate()
endif
endmethod
method release takes nothing returns nothing
call destroy()
endmethod
endmodule
globals
private integer lastindex = 0
endglobals
private function recycleLoop takes nothing returns nothing
local integer start
local integer i
local integer j
if(indexN>0) then
//set start = lastindex % indexN
set start = lastindex - indexN * (lastindex/indexN)
set i=start
set j=0
loop
exitwhen (j>=RECYCLE_COUNT)
if (GetItemTypeId(activeIndexes[i].attachedto) == 0) or (GetWidgetLife(activeIndexes[i].attachedto) == 0.0) then
set activeIndexes[i].attachedto = null
call activeIndexes[i].destroy()
set indexN = indexN-1
set activeIndexes[i] = activeIndexes[ indexN ]
endif
set i = i +1
exitwhen (indexN==0)
if(i>=indexN) then
set i=0
endif
if(start>=indexN) then
set start = 0
endif
exitwhen(i==start)
set j=j+1
endloop
set lastindex = i
endif
endfunction
private function init takes nothing returns nothing
local timer T= CreateTimer()
//iftablethenset DataInteger = integer(HandleTable.create())//noexclusion
call TimerStart(T, RECYCLE_INTERVAL, true, function recycleLoop)
set T=null
endfunction
endlibrary |
| 04-06-2011, 10:25 AM | #7 | ||
Quote:
I'll disregard that for now, though, and just demonstrate the use of a hashtable; I'll just copy the rest of the code, even if I think it could be done better. Hashtables are data structures that work like two dimensional arrays. They don't have the 8190 size limit of arrays, so we can use very large numbers as indexes; for example, we can use item rawcodes, which allows us to use an item rawcode to store and retrieve another item rawcode. Since I only need a one dimensional array, I'll be using Table which uses a single hashtable to make any number of Tables, which are like vJass dynamic arrays only without a size limit. JASS:globals private Table tab endglobals function NewItemStack takes integer powerup, integer stack returns nothing set tab[powerup] = stack endfunction private function AnyItemAcquired takes nothing returns boolean local integer itemA = GetItemTypeId( GetManipulatedItem() ) local integer itemB = tab[itemA] local unit mu local integer invi local item itm = null if itemB==0 then return false // The acquired item does not have a stackable item linked to it, do nothing. endif set mu = GetManipulatingUnit() set invi = GetInventoryHero(mu, itemB) if invi !=6 then set itm=GetItemHero(mu,itemB) call SetItemCharges(itm,GetItemCharges(itm)+1) set itm=null set mu=null return false else call UnitAddItemById(mu,itemB) set mu=null return false endif endfunction private function I takes nothing returns nothing set tab=Table.create() call GT_AddItemAcquiredAction(function AnyItemAcquired, 0) //I assume using 0 makes this work for all item types, if not then change this. endfunction endscope Quote:
|
| 04-07-2011, 03:01 AM | #8 |
some interesting questions you have there, anitarf...as for dropped items...that's where the ammostacking comes into play. i was originally using a system at thehelper to merge items and then i saw itemdex and thought to use it instead. and yeah...the regular stacking must be charges of 1 only because any higher is irrelevant. that's why i made up the ammostacking idea. which works just fine...but like i say you cannot be picking up the item if your inventory is full. so anyhow the data is attached manually for how much "ammo" is sold... the only thing really different from the itemdex version of my stackitems system and the old version as far as ammo stacking is concerned is that the merged items move around(merged items are different from stacked items, as stacked items require powerups)...but that might be something that i'm too lazy to fix. at any rate, i don't think that part matters much. actually if it's not for that you can't merge the items if you have a full inventory, i would just abolish the powerups altogether. as for the off-topic part...maybe it's a simple side effect of deleting a lot of libraries...that and putting multiple libraries in the same triggers frequently. EDIT: it doesn't appear table is so easily used...i suppose you left out a lot of code. at any rate...i'll just ignore hastables for the moment, i guess. |
| 04-07-2011, 08:28 AM | #9 |
Table is easily used, I included all the code. It is possible you are calling NewItemStack before the Table or Table's hashtable are created (for example, if you are calling NewItemStack from a module initializer). For the first, make sure function I runs before any functions that call NewItemStack, so if you make any such calls from module initializers then you need to also call function I from a module initializer. For the second, you need to fix Table (it's a simple fix, just delete its initialization function and change the global declaration to this: private hashtable ht = InitHashtable()). |
| 04-07-2011, 09:55 PM | #10 | |
Quote:
|
