| 02-26-2010, 10:05 PM | #1 |
I've searched around for hours.. I still cant find a solution to this. Im trying to save a unit group to a hashtable, but it just wont save. Ive tested it with debug messages to prove this. Code:
call DisplayTimedTextToPlayer(Player(0),0,0,60,"Debug Msg, Group g: " + I2S(CountUnitsInGroup(g)))
call DisplayTimedTextToPlayer(Player(0),0,0,60,"Debug Msg, Group n: " + I2S(CountUnitsInGroup(n)))
call SaveGroupHandleBJ( n, 0, 1, udg_ht )
set n1 = LoadGroupHandleBJ( 0, 1, udg_ht )
call DisplayTimedTextToPlayer(Player(0),0,0,60,"Debug Msg n1: " + I2S(CountUnitsInGroup(n1)))The outputs to the debug messages are 2 2 0 Does anyone have any idea at all how to fix this? Or if there are any other alternatives i might be able to use? =( |
| 02-26-2010, 11:35 PM | #2 |
I have a feeling you made the same mistake I did. I too looked around for a while when I was dealing with hashtables, then I realized I didn't do: JASS:set udg_ht = InitHashtable() Hopefully that was the problem. Otherwise it should work, just tested it. |
| 02-27-2010, 04:11 AM | #3 | ||||
Quote:
Hm.. that would work. From what i understand The prefix "udg_" basically makes the hashtable global right? So it can be shared between functions. Declaring otherwise would only allow it to work within a single function. I did have it declared by the way, except i used the variable editor. I'm worried that because i did so, my spell wouldn't be multi instancable as it is shared. After digging around i was fortunate enough to come across this: http://www.wc3c.net/showthread.php?t=106334 Apparently I misunderstood the function of missionKey and integerKey. Quote:
Heres the corrected version of the code: Quote:
That out of the way, any idea how i clean up the hashtable after im done with it? Wouldnt want any leaks. My guess is: ht = null destroy.ht() ? Edit: Okay.. this is getting really frustrating.. This hashtable group save and load is intended to store a group of units affected by a spell. For the first cast instance of the spell, it is able to callback the group, after that it keeps returning an empty group.. Edit #2: Added set udg_ut = InitHashTable() Group saving now works properly within function #1 with multiple instances. For some very annoyingly transparent reason, my second function is unable to call the values from a freaking GLOBAL hashtable variable. Im really stumped here. Any help would be greatly appreciated. Heres the algorithm.. Quote:
|
| 02-27-2010, 06:39 AM | #4 |
Just putting udg_ in front of a variable declaration doesn't make it global. What matters is where it is declared. Since you're using vJASS you can declare a globals block wherever you like: JASS:globals hashtable ht = InitHashtable() endglobals You should never have to destroy the hashtable after it's created, they are designed to be permanent. You can use natives to flush hashtables of data though. Show us your actual, full code in [jass] tags. |
| 02-27-2010, 06:58 AM | #5 |
Here it is, Its actually modded off a tutorial by Blade.dk. Unfortunately the tutorial was made before 1.23b where the return bug was used. Im trying to do a conversion so it works with hashtable. But im having terrible luck. If it would make it easier, I wouldn't mind sharing the map through PM. Just say the word and ill send it over. Thanks for responding, your feedback is greatly appreciated. FYI: Function #2 Is Stomp_Move, Function #1 is Stomp_Action Forgive me if there are silly mistakes, im not very experienced with vJASS yet. JASS:// Stomp spell by Blade.dk // Stomp spell by Blade.dk // Visit [url]http://www.wc3campaigns.net[/url] // globals hashtable ht = InitHashtable() endglobals constant function Stomp_SpellId takes nothing returns integer return 'A003' endfunction function Stomp_Conditions takes nothing returns boolean return GetSpellAbilityId() == Stomp_SpellId() endfunction function Stomp_Filter takes nothing returns boolean return IsPlayerEnemy(GetOwningPlayer(GetTriggerUnit()), GetOwningPlayer(GetFilterUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405 and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) endfunction function Stomp_CopyGroup takes group g returns group set bj_groupAddGroupDest = CreateGroup() call ForGroup(g, function GroupAddGroupEnum) return bj_groupAddGroupDest endfunction function Stomp_Move takes nothing returns nothing //Moves units //Initialize local vars, pulls stuff from gamecache //local string s = I2S(H2I(GetExpiredTimer())) //local gamecache gc = udg_AbilityCache //local hashtable ht = ht local integer j = Stomp_SpellId()+GetConvertedPlayerId(GetOwningPlayer(GetTriggerUnit())) local real x = LoadRealBJ(j+1000*1, 2, ht) local real y = LoadRealBJ(j+1000*2, 2, ht) local integer i = LoadIntegerBJ(j, 1, ht) local group g = LoadGroupHandleBJ(j, 4, ht) local real dur = LoadRealBJ(j+1000*4, 2, ht)+0.05 //dur local real ux local real uy local real a local unit f local real p = LoadRealBJ(j+1000*3, 2, ht)-0.5/(1+0.5*i) //speed local real fx = LoadRealBJ(j+1000*5, 2, ht)+0.05 //set ht = InitHashtable() //call DisplayTimedTextToPlayer(Player(0),0,0,60,"Spell Level: " + I2S(i)) call DisplayTimedTextToPlayer(Player(0),0,0,60,"xpos: " + R2S(x)) //call DisplayTimedTextToPlayer(Player(0),0,0,60,I2S(CountUnitsInGroup(g))) //call DisplayTimedTextToPlayer(Player(0),0,0,60,"Debug Msg ,j = " + I2S(j)) //Pushback effect if dur < 1+0.5*i then loop set f = FirstOfGroup(g) exitwhen f == null set ux = GetUnitX(f) set uy = GetUnitY(f) set a = Atan2(uy-y, ux-x) call SetUnitPosition(f, ux+p*Cos(a), uy+p*Sin(a)) if fx >= 0.5 then call DestroyEffect(AddSpecialEffectTarget(GetAbilityEffectById(Stomp_SpellId(), EFFECT_TYPE_MISSILE, 1), f, GetAbilityEffectById(Stomp_SpellId(), EFFECT_TYPE_MISSILE, 2))) endif call GroupRemoveUnit(g, f) endloop call SaveRealBJ(dur, j+1000*4, 2, ht) //dur call SaveRealBJ(p, j+1000*3, 2, ht) //p, speed call SaveRealBJ(fx, j+1000*5, 2, ht) //fx //Reset fx to 0, put here to prevent resetting during the loop. if fx >= 0.5 then call SaveRealBJ(fx, j+1000*5, 2, ht) endif else call DestroyGroup(LoadGroupHandleBJ(j, 4, ht)) //call FlushStoredMission(gc, s) call DestroyTimer(GetExpiredTimer()) endif //Cleanup //set gc = null call DestroyGroup(g) call FlushParentHashtable(ht) set g = null set f = null endfunction function Stomp_Actions takes nothing returns nothing //Initialize Variables local unit c = GetTriggerUnit() local real x = GetUnitX(c) local real y = GetUnitY(c) local integer i = GetUnitAbilityLevel(c, Stomp_SpellId()) local integer j = Stomp_SpellId()+GetConvertedPlayerId(GetOwningPlayer(GetTriggerUnit())) //Player Number of Owner of triggering unit local boolexpr b = Condition(function Stomp_Filter) local group g = CreateGroup() local group n local group n1 local unit f //local gamecache gc = udg_AbilityCache local timer t = CreateTimer() //local string s = I2S(H2I(t)) //set ht = InitHashtable() //Functions //call DisplayTextToForce( GetPlayersAll(), GetAbilityEffectById(Stomp_SpellId(), EFFECT_TYPE_MISSILE, 0) ) //Debug //call DisplayTextToForce( GetPlayersAll(), GetAbilityEffectById(Stomp_SpellId(), EFFECT_TYPE_MISSILE, 1) ) //Debug call DestroyEffect(AddSpecialEffect(GetAbilityEffectById(Stomp_SpellId(), EFFECT_TYPE_MISSILE, 0), x, y)) call GroupEnumUnitsInRange(g, x, y, 200+50*i, b) set n = Stomp_CopyGroup(g) call DisplayTimedTextToPlayer(Player(0),0,0,60,"Debug Msg, Group g: " + I2S(CountUnitsInGroup(g))) call DisplayTimedTextToPlayer(Player(0),0,0,60,"Debug Msg, Group n: " + I2S(CountUnitsInGroup(n))) call SaveGroupHandleBJ( g, j, 4, ht ) set n1 = LoadGroupHandleBJ( j, 4, ht ) call DisplayTimedTextToPlayer(Player(0),0,0,60,"Debug Msg n1: " + I2S(CountUnitsInGroup(n1))) loop set f = FirstOfGroup(n) call DisplayTimedTextToPlayer(Player(0),0,0,60,GetUnitName(f)) exitwhen f == null call UnitDamageTarget(c, f, 50*i, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null) call GroupRemoveUnit(n, f) endloop //Store stuff into gamecache // call StoreInteger(gc, s, "level", i) // call StoreInteger(gc, s, "group", H2I(g)) // call StoreReal(gc, s, "x", x) // call StoreReal(gc, s, "y", y) // call StoreReal(gc, s, "speed", 10) //Store stuff into Hashtable call SaveIntegerBJ( i, j+1000*1, 1, ht ) //call SaveIntegerBJ( j, Stomp_SpellId()+j+1000*2, 1, ht ) //call SaveGroupHandleBJ( g, 0 , 4, ht ) set n1 = LoadGroupHandleBJ( j, 4, ht ) call DisplayTimedTextToPlayer(Player(0),0,0,60,"Debug Msg n1: " + I2S(CountUnitsInGroup(n1)) + " ,j = " + I2S(j)) call SaveRealBJ( x, j+1000*1, 2, ht ) call SaveRealBJ( y, j+1000*2, 2, ht ) call SaveRealBJ( 10, j+1000*3, 2, ht ) //Expiring timer that runs Stomp_Move call TimerStart(t, 1, true, function Stomp_Move) //Destroy variables after use. set c = null call DestroyBoolExpr(b) set b = null set g = null call DestroyGroup(n) call DestroyGroup(n1) set n = null set f = null // set gc = null call FlushParentHashtable(ht) set ht = null //This may cause a bug set t = null endfunction //=========================================================================== function InitTrig_Stomp takes nothing returns nothing set gg_trg_Stomp = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( gg_trg_Stomp, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition( gg_trg_Stomp, Condition( function Stomp_Conditions ) ) call TriggerAddAction( gg_trg_Stomp, function Stomp_Actions ) call Preload(GetAbilityEffectById(Stomp_SpellId(), EFFECT_TYPE_MISSILE, 0)) call Preload(GetAbilityEffectById(Stomp_SpellId(), EFFECT_TYPE_MISSILE, 1)) endfunction Edit: Problem solved, Caused by careless destruction of unit group before its intended use finished. Also caused by premature flushing of the parent hashtable. |
| 02-28-2010, 03:53 PM | #6 |
Did you create group for n1 variable? |
| 03-02-2010, 04:36 AM | #7 | |
Quote:
Yea i did. But it was unused in the end. Please note the block of code up above is riddled with bugs =/ If you'd like a copy of the fixed code i could post it here once i'm able. Its been heavily modified since then. Im now using structs attached to a timer to pass my variables instead of a global hashtable. Eliminates the risk of accidentaly overwriting an important value stored in hashtable as my spells are intended to be multiinstancable. That actually brings up the question.. if i create a global hashtable, is it shared between all players in multiplayer? |
| 03-02-2010, 06:15 AM | #8 |
Hashtables are global, so yes. Usually when writing spells, we "attach" the spell data to the timer controlling it. You can do this by using one of the keys for the spcific timer ID (or handle ID). You can get a Handle ID by calling GetHandleId() and it will return a unique integer for any handle. Therefore, you could then store the spell struct in a hashtable based on this ID. If you use TimerUtils (which I recommend) then this will do it all automatically for you, with SetTimerData(timer, data); where data is the struct (or integer) you wish to attach. You can then call GetTimerData(timer, data) in each function that is executed from the timer to get your struct back. But it sounds like you already know this. |
| 03-02-2010, 11:58 AM | #9 |
Hmm.. That sounds useful. I was actually using Vexorians CSData library, which i'm not sure whether or not has the limitation of passing structs between function calls. I searched for GetHandleID() couldnt find it in my VJass function list though. =/ |
| 03-02-2010, 03:02 PM | #10 |
native GetHandleId takes handle h returns integer I think you may need a more recent version of the trigger GUI thing (name is slipping my mind) to see newer natives. |
| 03-02-2010, 04:08 PM | #11 | |
Quote:
I havnt actually had a chance to try yet, but assuming my version of Jasshelper recognizes the function, does GetHandleId work with timers? eg. GetHandleId(timer) |
| 03-02-2010, 05:45 PM | #12 |
yes, anything that extends a handle (unit, item, timers, triggers, etc) You can actually get a full list of all the type and which extend handles by looking at the common.j file found either within patch.mpq or within your jasshelper folder. Watch our for multiple extends (aka agent and widget). |
