找回密码
 点一下
查看: 32720|回复: 60

Hashtable教程~

[复制链接]
发表于 2009-10-30 21:30:17 | 显示全部楼层 |阅读模式
…………让GameCache+ReturnBug消失…………
…………让GameCache+ReturnBug消失…………
…………让GameCache+ReturnBug消失…………

首先,把自己的War3升级到1.24b。
这个1.24b补丁在哪里都有,自己随便找。
打上补丁之后,把目录下的因为安了第三方WE而产生的Units和UI这两个文件夹改个名字。这样hashtable的UI就不会被覆盖了。
如果你还想用以前的UI,那么不改也没关系。
因为我们是用J写的。
1.24b目前看来好像只能使用原版WE。

对于想快速将GameCache替换成Hashtable的人来说,以下部分就足够了:
用记事本的替换:
把"gamecache"换成"hashtable"。
把StoredInteger( XXX, XXX, XXX, H2I( XXXX ) )
换成SaveXXXX( XXX, XXX, XXX )
I2X( GetStoredInteger( XXXX, I2S(H2I(XXXX)), XXXX ) )
换成LoadXXXX( XXXX, H2I(XXXX), StringHash( XXXX ) )即可。
[jass]
function H2I takes handle h returns integer
return GetHandleId( h )
endfunction
[/jass]
H2I这样写。
把I2X全删了。

对于新手:
首先,你要把以前所有教程中关于使用Gamecache+ReturnBug的东西全部忘掉。那些东西早就不管用了。
然后,在最开始我们看一下所有关于hashtable的函数:
[jass]
type hashtable extends agent
native InitHashtable takes nothing returns hashtable

native SaveInteger takes hashtable table, integer parentKey, integer childKey, integer value returns nothing
native SaveReal takes hashtable table, integer parentKey, integer childKey, real value returns nothing
native SaveBoolean takes hashtable table, integer parentKey, integer childKey, boolean value returns nothing
native SaveStr takes hashtable table, integer parentKey, integer childKey, string value returns boolean
native SavePlayerHandle takes hashtable table, integer parentKey, integer childKey, player whichPlayer returns boolean
native SaveWidgetHandle takes hashtable table, integer parentKey, integer childKey, widget whichWidget returns boolean
native SaveDestructableHandle takes hashtable table, integer parentKey, integer childKey, destructable whichDestructable returns boolean
native SaveItemHandle takes hashtable table, integer parentKey, integer childKey, item whichItem returns boolean
native SaveUnitHandle takes hashtable table, integer parentKey, integer childKey, unit whichUnit returns boolean
native SaveAbilityHandle takes hashtable table, integer parentKey, integer childKey, ability whichAbility returns boolean
native SaveTimerHandle takes hashtable table, integer parentKey, integer childKey, timer whichTimer returns boolean
native SaveTriggerHandle takes hashtable table, integer parentKey, integer childKey, trigger whichTrigger returns boolean
native SaveTriggerConditionHandle takes hashtable table, integer parentKey, integer childKey, triggercondition whichTriggercondition returns boolean
native SaveTriggerActionHandle takes hashtable table, integer parentKey, integer childKey, triggeraction whichTriggeraction returns boolean
native SaveTriggerEventHandle takes hashtable table, integer parentKey, integer childKey, event whichEvent returns boolean
native SaveForceHandle takes hashtable table, integer parentKey, integer childKey, force whichForce returns boolean
native SaveGroupHandle takes hashtable table, integer parentKey, integer childKey, group whichGroup returns boolean
native SaveLocationHandle takes hashtable table, integer parentKey, integer childKey, location whichLocation returns boolean
native SaveRectHandle takes hashtable table, integer parentKey, integer childKey, rect whichRect returns boolean
native SaveBooleanExprHandle takes hashtable table, integer parentKey, integer childKey, boolexpr whichBoolexpr returns boolean
native SaveSoundHandle takes hashtable table, integer parentKey, integer childKey, sound whichSound returns boolean
native SaveEffectHandle takes hashtable table, integer parentKey, integer childKey, effect whichEffect returns boolean
native SaveUnitPoolHandle takes hashtable table, integer parentKey, integer childKey, unitpool whichUnitpool returns boolean
native SaveItemPoolHandle takes hashtable table, integer parentKey, integer childKey, itempool whichItempool returns boolean
native SaveQuestHandle takes hashtable table, integer parentKey, integer childKey, quest whichQuest returns boolean
native SaveQuestItemHandle takes hashtable table, integer parentKey, integer childKey, questitem whichQuestitem returns boolean
native SaveDefeatConditionHandle takes hashtable table, integer parentKey, integer childKey, defeatcondition whichDefeatcondition returns boolean
native SaveTimerDialogHandle takes hashtable table, integer parentKey, integer childKey, timerdialog whichTimerdialog returns boolean
native SaveLeaderboardHandle takes hashtable table, integer parentKey, integer childKey, leaderboard whichLeaderboard returns boolean
native SaveMultiboardHandle takes hashtable table, integer parentKey, integer childKey, multiboard whichMultiboard returns boolean
native SaveMultiboardItemHandle takes hashtable table, integer parentKey, integer childKey, multiboarditem whichMultiboarditem returns boolean
native SaveTrackableHandle takes hashtable table, integer parentKey, integer childKey, trackable whichTrackable returns boolean
native SaveDialogHandle takes hashtable table, integer parentKey, integer childKey, dialog whichDialog returns boolean
native SaveButtonHandle takes hashtable table, integer parentKey, integer childKey, button whichButton returns boolean
native SaveTextTagHandle takes hashtable table, integer parentKey, integer childKey, texttag whichTexttag returns boolean
native SaveLightningHandle takes hashtable table, integer parentKey, integer childKey, lightning whichLightning returns boolean
native SaveImageHandle takes hashtable table, integer parentKey, integer childKey, image whichImage returns boolean
native SaveUbersplatHandle takes hashtable table, integer parentKey, integer childKey, ubersplat whichUbersplat returns boolean
native SaveRegionHandle takes hashtable table, integer parentKey, integer childKey, region whichRegion returns boolean
native SaveFogStateHandle takes hashtable table, integer parentKey, integer childKey, fogstate whichFogState returns boolean
native SaveFogModifierHandle takes hashtable table, integer parentKey, integer childKey, fogmodifier whichFogModifier returns boolean
native SaveAgentHandle takes hashtable table, integer parentKey, integer childKey, agent whichAgent returns boolean
native SaveHashtableHandle takes hashtable table, integer parentKey, integer childKey, hashtable whichHashtable returns boolean


native LoadInteger takes hashtable table, integer parentKey, integer childKey returns integer
native LoadReal takes hashtable table, integer parentKey, integer childKey returns real
native LoadBoolean takes hashtable table, integer parentKey, integer childKey returns boolean
native LoadStr takes hashtable table, integer parentKey, integer childKey returns string
native LoadPlayerHandle takes hashtable table, integer parentKey, integer childKey returns player
native LoadWidgetHandle takes hashtable table, integer parentKey, integer childKey returns widget
native LoadDestructableHandle takes hashtable table, integer parentKey, integer childKey returns destructable
native LoadItemHandle takes hashtable table, integer parentKey, integer childKey returns item
native LoadUnitHandle takes hashtable table, integer parentKey, integer childKey returns unit
native LoadAbilityHandle takes hashtable table, integer parentKey, integer childKey returns ability
native LoadTimerHandle takes hashtable table, integer parentKey, integer childKey returns timer
native LoadTriggerHandle takes hashtable table, integer parentKey, integer childKey returns trigger
native LoadTriggerConditionHandle takes hashtable table, integer parentKey, integer childKey returns triggercondition
native LoadTriggerActionHandle takes hashtable table, integer parentKey, integer childKey returns triggeraction
native LoadTriggerEventHandle takes hashtable table, integer parentKey, integer childKey returns event
native LoadForceHandle takes hashtable table, integer parentKey, integer childKey returns force
native LoadGroupHandle takes hashtable table, integer parentKey, integer childKey returns group
native LoadLocationHandle takes hashtable table, integer parentKey, integer childKey returns location
native LoadRectHandle takes hashtable table, integer parentKey, integer childKey returns rect
native LoadBooleanExprHandle takes hashtable table, integer parentKey, integer childKey returns boolexpr
native LoadSoundHandle takes hashtable table, integer parentKey, integer childKey returns sound
native LoadEffectHandle takes hashtable table, integer parentKey, integer childKey returns effect
native LoadUnitPoolHandle takes hashtable table, integer parentKey, integer childKey returns unitpool
native LoadItemPoolHandle takes hashtable table, integer parentKey, integer childKey returns itempool
native LoadQuestHandle takes hashtable table, integer parentKey, integer childKey returns quest
native LoadQuestItemHandle takes hashtable table, integer parentKey, integer childKey returns questitem
native LoadDefeatConditionHandle takes hashtable table, integer parentKey, integer childKey returns defeatcondition
native LoadTimerDialogHandle takes hashtable table, integer parentKey, integer childKey returns timerdialog
native LoadLeaderboardHandle takes hashtable table, integer parentKey, integer childKey returns leaderboard
native LoadMultiboardHandle takes hashtable table, integer parentKey, integer childKey returns multiboard
native LoadMultiboardItemHandle takes hashtable table, integer parentKey, integer childKey returns multiboarditem
native LoadTrackableHandle takes hashtable table, integer parentKey, integer childKey returns trackable
native LoadDialogHandle takes hashtable table, integer parentKey, integer childKey returns dialog
native LoadButtonHandle takes hashtable table, integer parentKey, integer childKey returns button
native LoadTextTagHandle takes hashtable table, integer parentKey, integer childKey returns texttag
native LoadLightningHandle takes hashtable table, integer parentKey, integer childKey returns lightning
native LoadImageHandle takes hashtable table, integer parentKey, integer childKey returns image
native LoadUbersplatHandle takes hashtable table, integer parentKey, integer childKey returns ubersplat
native LoadRegionHandle takes hashtable table, integer parentKey, integer childKey returns region
native LoadFogStateHandle takes hashtable table, integer parentKey, integer childKey returns fogstate
native LoadFogModifierHandle takes hashtable table, integer parentKey, integer childKey returns fogmodifier
native LoadHashtableHandle takes hashtable table, integer parentKey, integer childKey returns hashtable

native HaveSavedInteger takes hashtable table, integer parentKey, integer childKey returns boolean
native HaveSavedReal takes hashtable table, integer parentKey, integer childKey returns boolean
native HaveSavedBoolean takes hashtable table, integer parentKey, integer childKey returns boolean
native HaveSavedString takes hashtable table, integer parentKey, integer childKey returns boolean
native HaveSavedHandle takes hashtable table, integer parentKey, integer childKey returns boolean

native RemoveSavedInteger takes hashtable table, integer parentKey, integer childKey returns nothing
native RemoveSavedReal takes hashtable table, integer parentKey, integer childKey returns nothing
native RemoveSavedBoolean takes hashtable table, integer parentKey, integer childKey returns nothing
native RemoveSavedString takes hashtable table, integer parentKey, integer childKey returns nothing
native RemoveSavedHandle takes hashtable table, integer parentKey, integer childKey returns nothing

native FlushParentHashtable takes hashtable table returns nothing
native FlushChildHashtable takes hashtable table, integer parentKey returns nothing
[/jass]
hashtable就是一个你用来存储数据的地方,基本上不管是什么都可以存进去。
InitHashtable是创建一个hashtable,可以用来存储数据
Save和Load可以看成同一种类型。
Have是判断。
Remove是清除。
Flush是大范围清除。
那么,你们都看到了,参数上基本都是上前面都是这个样子:
[jass]hashtable table, integer parentKey, integer childKey[/jass]
那么他们的意思是什么呢?
就是在这个table中,parentKey的项目里的childKey的子项目的某些数据。
比如SaveInteger( HT, GetHandleId( t ), Const_ChildKey_Timer_Index, index )
就是在hashtable HT,的timer t的HandleId的项目下,的integer Const_ChildKey_Timer_Index的子项目中存储一个integer index的数据~
举一个形象的例子,把hashtable看成一个非常巨大的图书馆。
图书馆里有很多个横向排列的书架,这些书架都有自己的独一无二的编号,这些编号就是parentKey。
每个书架都有竖向排列的长方体的书柜,这些书柜在每个书架中都有自己独一无二的编号,这些编号就是childKey。
而每个书柜都有5层,每层都有一个格子,上面贴有标签"integer"、"real"、"boolean"、"string"、"handle"。分别表示这个格子存放的书籍中的内容是什么。
InitHashtable就可以理解为在空地上创建一所巨大的图书馆。
Save的话,就相当于,从图书馆大门走进去,首先找到编号为parentKey(图中为1000872)的书架:
pic1.jpg
然后我们放大这个图像,往上走,找到在这个1000872的书架中的编号为childKey(图中为10)的书柜:
pic2.jpg
(画工不好,书就不画了)
然后呢,如果是:
[jass]
native SaveInteger takes hashtable table, integer parentKey, integer childKey, integer value returns nothing
native SaveReal takes hashtable table, integer parentKey, integer childKey, real value returns nothing
native SaveBoolean takes hashtable table, integer parentKey, integer childKey, boolean value returns nothing
native SaveStr takes hashtable table, integer parentKey, integer childKey, string value returns boolean
[/jass]
就是直接在这个书柜中的对应格子(integer,real,boolean,string)中的书上写上你要存储的数据。
比如
[jass]
call SaveInteger( HT, 1000872, 10, 4567 )
[/jass]
就是在刚才的书柜中的integer格子中的书上写上4567的意思~
但是大家注意了~因为这些书都薄的惊人,所以为了能写下以后的数据,我们在写新的数据的时候,都会把原来的字擦掉~~
这个就像对一个变量赋值一样。
[jass]
local integer An_Int = 882211
set An_Int = 4567
[/jass]
新的4567会覆盖原先的882211的值。
real、boolean和string都是类似的~
当然啦~计算机可比我们的速度快很多~可以近似认为它有全部图书馆内范围的0CD0耗魔闪烁技能~可以从大门闪烁到对应书架再闪烁到对应的书柜~
所以即使整个图书馆里有无数的书架和书柜~它也可以很快达到目的地~

但是~~
[jass]native SaveXXXXHandle takes hashtable table, integer parentKey, integer childKey, XXXX whichXXXX returns boolean[/jass]
像这样的都注意啦~
这些数据都是存储一个对于whichXXXX的指针。并不是像StoreUnit+RestoreUnit那样可以创造出一个新的单位那样的。
具体待会再说。
这些函数的格子在哪里呢?
注意到了?后缀都是Handle,所以他们所有的数据都是直接存储在handle这种格子里哦~
同类的player或者unit都是像integer那些一样的“后来者居上”的覆盖原则(这个应该可以简单推理得到的吧),但是如果不同类的怎么办?
[jass]
call SavePlayerHandle( HT, 1000872, 10, Player( 0 ) )
call SaveUnitHandle( HT, 1000872, 10, u )
[/jass]
那么那个handle的格子里面的书上(数据)到底写了什么呢?
答案是unit u~
所以不同类别的Handle存储在一起也是后来者居上的原则,u会把之前写的player Player(0)覆盖掉~
非常遗憾他不能存储更加细分的Handle类别~不过这其实也足够了(对我来说一个integer就完全足够了)
既然是图书馆,那么就必然会有人查找资料~~
这时候Load就可以出现啦~
Load是用来查询对应的书架(parentKey)的书柜(childKey)对应的格子的数据的函数~
比如
[jass]
set An_Int = LoadInteger( HT, 1000872, 10 )
[/jass]
这时An_Int其实就是在编号1000872的书架的编号为10的书柜的integer的格子里的书上写的数字啦~
刚刚我们存了一个4567进去,那么现在An_Int就是4567啦~
但是如果格子中的书上什么也没有写怎么办?
那么
[jass]
native LoadInteger takes hashtable table, integer parentKey, integer childKey returns integer
native LoadReal takes hashtable table, integer parentKey, integer childKey returns real
native LoadBoolean takes hashtable table, integer parentKey, integer childKey returns boolean
native LoadStr takes hashtable table, integer parentKey, integer childKey returns string
[/jass]
这四个分别会返回:
[jass]
0
0.0
false
null
[/jass]
其他的后缀为Handle的函数,返回的也都是null
但是如果书上写了integer 0,那么又如何区别写了字的书和没有写字的书呢?
这样就需要用到这些Have判断函数了~
[jass]
native HaveSavedInteger takes hashtable table, integer parentKey, integer childKey returns boolean
native HaveSavedReal takes hashtable table, integer parentKey, integer childKey returns boolean
native HaveSavedBoolean takes hashtable table, integer parentKey, integer childKey returns boolean
native HaveSavedString takes hashtable table, integer parentKey, integer childKey returns boolean
native HaveSavedHandle takes hashtable table, integer parentKey, integer childKey returns boolean
[/jass]
parentKey和childKey我就不多说了~
如果这个对应格子的书上什么也没有写的话,这个函数就会返回false。
但是如果写了字的话,那么就会返回true。
刚才那个情况,一本写了“0”的书,和一本什么也没有写的书。
用LoadInteger,返回的全部是0
用HaveSavedInteger,第一本书返回true,第二本书返回false~
那么~如果我们想把一本书上的内容清空怎么办?
比如我们发现有些书上的内容已经不需要了~
我们想把这些书上的字都擦掉~
可以用
[jass]
native RemoveSavedInteger takes hashtable table, integer parentKey, integer childKey returns nothing
native RemoveSavedReal takes hashtable table, integer parentKey, integer childKey returns nothing
native RemoveSavedBoolean takes hashtable table, integer parentKey, integer childKey returns nothing
native RemoveSavedString takes hashtable table, integer parentKey, integer childKey returns nothing
native RemoveSavedHandle takes hashtable table, integer parentKey, integer childKey returns nothing
[/jass]
这些Remove函数啦~
这种函数不管那本书上存储的什么内容~都会清扫干净哦~

我们为什么要清除这些东西呢?
如果就这么放着不也是不太影响吗?
这可就大错特错了哦~
因为hashtable原理的原因,如果是一本空书的话,就不会再额外占地方(使用内存少)。
写了字的书会额外的占地方(使用内存多),我们不清除数据的话。
每一次使用完毕的数据都会在这个巨大的图书馆中沉淀下来~
虽然说是“巨大”的图书馆,但是如果长年累月这么一直把数据放在里面不整理的话,不仅地方不够大了~而且下一次读取数据的时候,很可能就会读取到以前遗留下来的数据~但是我们认为那应该会读取对应的空值(0,0.0,false,null)~引起的数据混乱可是不堪设想哦~
PS:我在这里小小的解释一下为什么“地方不够大了”的原因:
虽然头目说过貌似10W还是100W的childKey还是parentKey都还是有效的。
第一这一定会使用某些空间来存储数据(比如内存),内存就算是2G,4G……的那也总得有个头吧(虽然依靠Save很难达到这种目标)。
第二,根据头目的描述,我们可以简单判断war3的hashtable是一个开链表HashTable,这种Hashtable理论上childKey和parentKey都可以无限增加(当然,是空间无限的条件下),可悲的是所有的HashTable的效率都会受到这个HashTable里面存储的Key的数量的影响,Key越多,HashTable的效率就会越差。如果不清除Key,那么HashTable的效率会越来越低,真的比GameCache的效率(这玩意效率我记得应该很平稳,但是极端低下)都低了的时候,就变成了一个茶table~

啊啦拉…………一不小心就把后面的Flush函数的东西提出来了呢~
[jass]
native FlushParentHashtable takes hashtable table returns nothing
native FlushChildHashtable takes hashtable table, integer parentKey returns nothing
[/jass]
这两个威力都非常大的函数~
FlushParentHashtable就是直接摧毁(destroy)这个巨大的图书馆的意思啦~
所有的数据都灰飞烟灭了哦~
FlushChildHashtable的威力虽然没有Parent大,但是也算是很强力的喽~
可以直接把一个书架的所有书柜的所有格子里的书的内容都擦掉哦!
也就是说重新初始化(init)一个书架~
里面的数据也都消失了~
这个就是用来清除Key的函数啦~
现在还不知道:
[jass]
call SaveInteger( HT, 1000872, 10, 4567 )
call RemoveSavedInteger( HT, 1000872, 10 )
[/jass]
这样清除一个parentKey书柜里的所有书的内容是不是就真的可以清除掉这个parentKey。
所以保险起见,我们还是直接用FlushChildHashtable来清除整个parentKey(1000872)的数据~

PS:这两个函数的名称就暴露了hashtable其实是嵌套hashtable的事实~

既然我已经让大家熟悉了整个hashtable的所有操作函数。
那么我们现在要开始理解一些有关hashtable的概念了~
1.指针:
这个概念可能理解起来比较困难(对于第一次接触这个概念的人来说的确很难)。
简单来说,就是一个指向目标的数据。但是它又不是目标本身。
类似于购买物品时,悬在英雄头上的小箭头…………
通过这个指针,我们可以访问目标本身。
就拿unit变量来说。
实际上unit(local unit XXX的那个unit)并不是真正的地图中的单位,只不过是从这个unit我们可以访问并且操作真正的单位。
指针就是类似于这样的东西………………
2.hashtable:
先说一下,下文的数组只是表示一种紧密有序排列的一维元素集合,只不过写成数组方便而已。
hashtable是一种根据关键字(Key)计算出存储数据的数组的下标(索引,index)再存储数据的存储结构(table)。
当关键字(Key)的范围远远大于真正Key的数量时,由于不能直接使用Key当数组下标来存储
hashtable会让关键字压缩成一个hashtable预先声明的数组的尺寸允许以内的范围,再找到对应的数组元素(书架或者书柜)进行存储。
比如parentKey是1000872,实际上可能存储在1000872%1000 = 872这个下标的数组元素(书架)上。
至于怎么压缩呢?一般来说都是使用求模(mod)法,把一开始的Key对内部数组的尺寸允许的一个数进行求模,得到的结果就是对应的数组元素了(书架或者书柜)。
这样缩小了需要处理的下标范围。
这样的用来缩小下标范围的函数一律统称为散列函数(Hash)
其实有各种各样的Hash(),但是这里我们只介绍这种通过对Key求模压缩下标的Hash()。
而不幸的是,这样压缩的索引,虽然本身是独一无二的,但是因为对Key进行了压缩,所以:两个关键字(Key)可能会映射到同一个数组元素上去
这种情况我们称之为发生了碰撞,也就是两个关键字冲突了。
幸运的是,我们还有很有效的方法解决这种情况。
由于War3使用的解决方法是链接法(应该是),所以我们也只讨论这种情况,其余的请大家自己阅读《数据结构》。
这种方法让hashtable的数组元素都是一个链表或者为空。
先补充链表的概念:
一个线性有序非紧密排列的节点(node,包括数据域和指针域)的集合。
每个节点至少有一个统一方向的指针指向后一个节点。
这样串联起来的节点,从头到尾称作一个链表。
当一个Key进入hashtable的时候,就在对应的数组元素(链表)里添加一个关键字为Key的节点。
每一次读取和加入Key,都会从这个链表中找到关键字为Key的节点,在单独对这个节点的数据进行操作。
因为Key不相同,所以结点互相就不会冲突,而且还保留了高效的特点,最重要的这样是一个动态的存储结构。没有Key数量的上限。
当然,这样的话,因为Key的数量的增多,每个链表的节点就增多,那必然在链表中搜索(search)指定节点的平均效率就会降低~整个hashtable的平均效率也会下降。所以我们需要对hashtable进行清理工作~
总的来说,这种hashtable的查找效率是极端高效的~
当然和array比起来还是有比较大的差距…………
比Gamecache那种鬼东西好的多。

在hashtable中,除了integer,real,boolean,string是直接把“本体”存储在hashtable中以外,剩下的handle都是只存储一个指向这个handle的指针。

实在理解不能的话……你可以把这个hashtable想成一个二维数组,也就是矩阵…………
parentKey为X坐标,childKey为Y坐标………………
每个格子有五个数据,分别是integer,real,boolean,string,handle…………

实际上,hashtable实际上可能只有100000个横向的书架的位置。
当新存储一个未使用过的书架(parentKey),Hash(Key)得到的索引的书架,其实是在那个地方建造一个新的书架。
如果有前面的旧的书架(即发生碰撞),就直接建造在就书架的上面。书柜(childKey)也是一样。
SaveXXXX的时候,会先检测parentKey对应的书架存不存在,如果不存在则创建新书架,然后检测childKey对应的书柜存不存在,如果不存在就创建新书柜。
再存储数据。
所以对不用的书柜(childKey)/书架(parentKey)进行销毁工作也是十分有必要的。不仅影响hashtable近乎所有函数的效率,而且占空间也是很让人讨厌的一件事情。

概念都理解完了没有~如果没有理解也不着急~我们开始讲它的实际意义~及其应用~
War3的hashtable的实际意义就在于,给指定的integer Key来绑定一些数据。
说得再直白点,就是给handle绑定数据。
比如我想在一个timer身上绑定一个unit source,一个unit target,一个real damage。
在timer到期的时候,令source对target造成damage点伤害~
首先,我们需要对一个timer绑定数据,这可怎么办?
timer可不是integer~
我们没必要把timer转换成integer,其实只需要一个专门对应这个timer的一个独一无二的Id即可~
我们可以通过:
[jass]native GetHandleId takes handle h returns integer[/jass]
来获得一个专门对应handle h的独一无二的值,称作HandleId~
所以我们可以把timer转换成一个Key了~为了清除时的方便,一般来说,HandleId都会作为parentKey。
那另外一个childKey怎么办?
既然我们想存储一个source,一个target,那如果存储在同一个childKey的格子里,就会发生覆盖这样的恶性事件~
所以我们分别存储在childKey分别为1,2,3中。
代码就是下面的:
对了~实际使用hashtable之前,需要把它初始化哦~
[jass]
globals
hashtable HT = null
endglobals

function Init takes nothing returns nothing
call FlushParentHashtable( HT )
set HT = InitHashtable()
endfunction

function TimeDamage_Act takes nothing returns nothing
local integer parentKey = GetHandleId( GetExpiredTimer() )
local unit source = LoadUnitHandle( HT, parentKey, 1 )
local unit target = LoadUnitHandle( HT, parentKey, 2 )
local real damage = LoadReal( HT, parentKey, 3 )
call UnitDamageTargetBJ( source, target, damage, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE )
call FlushChildHashtable( HT, parentKey )
call DestroyTimer( GetExpiredTimer() )
set source = null
set target = null
endfunction

function TimeDamage takes unit source, unit target, real time, real damage returns nothing
local timer t = CreateTimer()
local integer parentKey = GetHandleId( t )
call SaveUnitHandle( HT, parentKey, 1, source )
call SaveUnitHandle( HT, parentKey, 2, target )
call SaveReal( HT, parentKey, 3, damage )
call TimerStart( t, time, false, function TimeDamage_Act )
set t = null
endfunction
[/jass]
看起来1,2,3似乎有些太随便了,而且也不方便我们记忆。
所以我们可以设定常量表明我们的用意:
[jass]
globals
constant integer Hash_Timer_UnitSource_Key = 1
constant integer Hash_Timer_UnitTarget_Key = 2
constant integer Hash_Timer_RealDamage_Key = 3
endglobals

local unit source = LoadUnitHandle( HT, parentKey, Hash_Timer_UnitSource_Key )
local unit target = LoadUnitHandle( HT, parentKey, Hash_Timer_UnitTarget_Key )
local real damage = LoadReal( HT, parentKey, Hash_Timer_RealDamage_Key )

call SaveUnitHandle( HT, parentKey, Hash_Timer_UnitSource_Key, source )
call SaveUnitHandle( HT, parentKey, Hash_Timer_UnitTarget_Key, target )
call SaveReal( HT, parentKey, Hash_Timer_RealDamage_Key, damage )
[/jass]
这样看起来意思简单明了,也知道是这个ChildKey是什么意思。
还有一种比较偷懒的方法,就是利用
[jass]native StringHash takes string s returns integer[/jass]
这个函数。
StringHash和GetHandleId是一样的作用。只不过它是可以把一个字符串转换成一个独一无二的整数Key。
但是和ReturnBug(当然1.24禁止了)的那种转换还不一样。ReturnBug在不同地图中,对于同一字符串转换可能会出现不同结果。
但是StringHash就不一样,在任何地图中都可以转换为同一个Key。
刚才的代码我们可以写成
[jass]
local unit source = LoadUnitHandle( HT, parentKey, StringHash( "source" ) )
local unit target = LoadUnitHandle( HT, parentKey, StringHash( "target" )
local real damage = LoadReal( HT, parentKey, StringHash( "damage" )

call SaveUnitHandle( HT, parentKey, StringHash( "source", source )
call SaveUnitHandle( HT, parentKey, StringHash( "target", target )
call SaveReal( HT, parentKey, StringHash( "damage", damage )
[/jass]
这样可以同样达到不覆盖的效果。
但是相对于StringHash,我更喜欢定义常量的方式。
因为StringHash毕竟是对字符串本身的每个字符进行运算,随着字符串的长度的增长,它的开销应该会增大。这让人比较不爽。
当然,用string意思非常明确,也不需要在地图头文件和触发器里来回换(当然,真要这样的话,直接用空的blizzard.j来声明这些变量其实更好呢~)
新手可以考虑这种东西。
但是因为缺乏统一性,在更换关键字或者更换关键字形式的时候,就比较麻烦。
小药好像在综合区发了一个blizzard.j的东西,里面有专门1.24b的给handle绑定各种数据的函数(Save,Load)。
如果觉得自己写比较麻烦,可以直接用那个(我比较推荐)。



说到应用,就是举实例了~
首先先从比较简单的功能做起吧~
比如我们要做一个持续性的嘲讽技能,我们可以0.1秒对受影响的敌军发布一次smart命令,目标是释放嘲讽技能的单位(这样是不会打断单位的攻击的哦~)
那么我们就要对一个timer绑定一些数据:一个单位组,一个unit target。
timer每0.1秒触发一次,让单位组内的所有单位攻击目标。
[jass]
globals
hashtable HT = null
endglobals

function Init takes nothing returns nothing
call FlushParentHashtable( HT )
set HT = InitHashtable()
endfunction

function TauntUnits_Act takes nothing returns nothing
local integer parentKey = GetHandleId( GetExpiredTimer() )
local unit target = LoadUnitHandle( HT, parentKey, 1 )
local group units = LoadGroupHandle( HT, parentKey, 2 )
local real time = LoadReal( HT, parentKey, 3 )
local real elapsed = LoadReal( HT, parentKey, 4 )
if elapsed < time then
call ForGroup( units, function TauntUnits_Attack )
call SaveReal( HT, parentKey, 4, elapsed + 0.1 )
else
call DestroyGroup( units )
call FlushChildHashtable( HT, parentKey )
call DestroyTimer( GetExpiredTimer() )
endif
set target = null
set units = null
endfunction

function TauntUnits takes unit target, group units, real time returns nothing
local timer t = CreateTimer()
local integer parentKey = GetHandleId( t )
call SaveUnitHandle( HT, parentKey, 1, target )
call SaveGroupHandle( HT, parentKey, 2, units )
call SaveReal( HT, parentKey, 3, time )
call TimerStart( t, 0.1, true, function TauntUnits_Act )
set t = null
endfunction
[/jass]
(为了方便,把麻烦的命令单位组攻击的函数省略不写了)
通过SaveUnitHandle和SaveGroupHandle,我们把unit和group都绑定在了timer上。
SaveReal绑定总时间和已流逝的时间。
在地图初始化的时候call Init(),否则HT为null,无法存储数据!

从中得知:我们只要GetHandleId( h ),把这个Id作为parentKey,那么我们随时随地都可以给handle绑定各种数据。
确定childKey和数据对应的含义,整个hashtable就变成了handle的数据库。
比如我们想给宠物绑定一个“主人”的unit数据:
[jass]call SaveUnitHandle( HT, GetHandleId( pet ), StringHash( "Master" ), master )[/jass]
或者想给单位绑定一个攻击力数据:
[jass]call SaveInteger( HT, GetHandleId( u ), StringHash( "Attack" ), attack )[/jass]
当然,hashtable的功用不仅仅在于只给handle绑定数据
给【物品类型】绑定技能数据:
[jass]call SaveInteger( HT, 'I000', StringHash( "Ability" ), ab )[/jass]
也就是说,不管是什么对象,只要能转换成integer,就可以为其绑定数据。

那么,是第一次小试身手的时候了:
制作一个函数,参数是目标unit target,时间real time。
首先暂时移除target的攻击,在time到期后,返还原来的攻击。
提示:
[jass]
call UnitAddAbility( u, 'Abun' )//移除攻击
call UnitRemoveAbility( u, 'Abun' )//返还攻击
[/jass]
要求:利用hashtable给timer绑定参数。

请大家自己编写代码~~

==========================分割线===========================




















==========================分割线===========================

答案:
[jass]
globals
hashtable HT = null
endglobals

function Init takes nothing returns nothing
call FlushParentHashtable( HT )
set HT = InitHashtable()
endfunction

function Attack_Act takes nothing returns nothing
local integer parentKey = GetHandleId( GetExpiredTimer() )
local unit target = LoadUnitHandle( HT, parentKey, 1 )
call UnitRemoveAbility( target, 'Abun' )
call FlushChildHashtable( HT, parentKey )
call DestroyTimer( GetExpiredTimer() )
set target = null
endfunction

function Attack takes unit target, real time returns nothing
local timer t = CreateTimer()
local integer parentKey = GetHandleId( t )
call UnitAddAbility( target, 'Abun' )
call SaveUnitHandle( HT, parentKey, 1, target )
call TimerStart( t, time, false, function Attack_Act )
set t = null
endfunction
[/jass]
是不是很简单呢?

再来一道题:
这道题就比较难了~
要求是:
takes unit source, unit target, real damage
当target受到来自source的伤害(此伤害必须大于20.0)时,source对target额外造成此次伤害40%的伤害
伤害函数:
[jass]call UnitDamageTargetBJ( source, target, damage, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_MAGIC )[/jass]
此效果从函数被调用开始,target受到5次符合要求的伤害之后,效果消失。
要求:利用hashtable给trigger绑定参数。并且要求对trigger进行排泄。

请大家自己编写代码~~

==========================分割线===========================




















==========================分割线===========================

答案:
[jass]
globals
hashtable HT = null
endglobals

function Init takes nothing returns nothing
call FlushParentHashtable( HT )
set HT = InitHashtable()
endfunction

function Damage_Act takes nothing returns boolean
local integer parentKey = GetHandleId( GetTriggeringTrigger() )
local unit source = LoadUnitHandle( HT, parentKey, 1 )
local unit target = LoadUnitHandle( HT, parentKey, 2 )
call FlushChildHashtable( HT, parentKey )
call TriggerRemoveCondition( GetTriggeringTrigger(), LoadTriggerConditionHandle( HT, parentKey, 3 ) )
call DestroyTrigger( GetTriggeringTrigger() )
set source = null
set target = null
return false
endfunction

function Damage takes unit source, unit target returns nothing
local trigger t = CreateTrigger()
local integer parentKey = GetHandleId( t )
local boolexpr cond = Condition( function Damage_Act )
call SaveUnitHandle( HT, parentKey, 1, source )
call SaveUnitHandle( HT, parentKey, 2, target )
call TriggerRegisterUnitEvent( t, target, EVENT_UNIT_DAMAGED )
call SaveTriggerConditionHandle( HT, parentKey, 3, TriggerAddCondition( t, cond ) )
set t = null
set cond = null
endfunction
[/jass]
(不知道为什么我现在很不注意局部变量清空排泄了……貌似是VJstruct全局变量数组使多了的缘故)


不知道大家经过上面的练习,有没有熟练使用hashtable来为一个integer(Key、handle)绑定数据呢?
接下来我要说一些使用hashtable中需要注意的地方:
1.hashtable用全局变量来存储。
2.在地图初始化或者游戏开始0.0秒时(其实是在你使用hashtable之前),必须要InitHashtable()。这可不要忘记了。
3.使用后一定记得FlushChildHashtable,这点影响整个hashtable的效率
4.childKey尽量使用常量或者StringHash,使用1,2,3这样的childKey意义非常不明确,如果忘记了具体含义,想要修改代码的话,就很麻烦了。
5.parentKey尽量使用有代表性或者特殊含义的integer,比如handle的id,而不是1,2,3这样的。




现在就要学习hashtable的高级技巧了~
1.与数组array(vj-struct)混用,大幅提高效率
hashtable的实际存储效率是比不过array的,但是array却不能直接绑定在过大的integer上(数组最大尺寸8192)。
在这里就不累述那些使用array来存储数据的系统了。只是讲一下如何利用hashtable来间接得在array上绑定数据。
我们可以通过hashtable给handle绑定一个integer数据,而这个数据就是这个handle数据索引。
使用这个数据索引就可以在array中找到数据。
但是这个数据索引必须是唯一的,找这样一个数据hashtable就不能帮我们办到了。
我的方法是vj-struct的allocate()和destroy(integer this)。
代码如下:
[jass]
globals
integer Array_Max = 1
integer Array_Top = 0
integer array Array_Next
endglobals

function Array_allocate takes nothing returns integer
local integer this = Array_Top
if this == 0 then
set this = Array_Max
if this >= 8190 then
return 0
endif
set Array_Max = Array_Max + 1
else
set Array_Top = Array_Next[Array_Top]
endif
return this
endfunction

function Array_destroy takes integer this returns nothing
set Array_Next[this] = Array_Top
set Array_Top = this
endfunction
[/jass]
allocate()可以分配给你一个在数组中的独一无二的索引。
destroy(integer this)可以将this索引回收,以便下次利用。
(说下,这两个函数对于每一种要绑定数据integer,比如trigger,effect等,是应该为私有的函数的
也就是说,你给马甲绑定数据,那么所有的这种马甲就应该使用一套allocate()、destroy()以及Top、Max、Next)
利用这两个函数我们就可以通过hashtable作为接口,实际以高效的array为handle绑定数据了。
而且,使用array存储数据,实际上能比hashtable存储更多类型的数据,比如weathereffect,这个hashtable就根本存储不了。

2.尽量不使用StringHash()和整数,利用常量来做childKey,区分数据
大家肯定都觉得StringHash()写起来非常方便,中途也无需声明新的全局变量。但是这并不表示StringHash就一好到底了。
如果你在编写代码时,发现区分两种基础数据的string发生了冲突,那么你为了区分这两种数据不一样但是标识string一样,就需要把所有的两种数据的标识string全部换一边。
这实在是有点太麻烦了。
而如果使用无意义的整数,第一你自己很快就会忘记这些整数存储的数据代表了什么。
第二和StringHash()一样,维护的时候会很麻烦。
所以我建议大家全部使用常量定义childKey。
使用常量,在发生冲突的时候,只需要去globals里改一个数就可以避免冲突。
而且StringHash()需要对string进行运算得出效果,明显不如直接常量传递数据来得快。
效率比用常量低了很多。

3.利用hashtable达到N维数组的效果
hashtable的parentKey和childKey双层integer下标,实际上可以近似看成一个矩阵,也就是2维的数组。
我曾经发过一个帖子,给出了利用运算下标的方式将array(1维数组)转换为N维数组的算法。很麻烦。
但是有了hashtable我们就很好弄了。
注意这个函数:
[jass]
native SaveHashtableHandle takes hashtable table, integer parentKey, integer childKey, hashtable whichHashtable returns boolean
[/jass]
利用这个函数,我们可以嵌套hashtable来达到N维数组的效果。
我这里只举一个3维数组的例子:
[jass]
globals
hashtable HT = null
endglobals

function Init takes nothing returns nothing
call FlushParentHashtable( HT )
set HT = InitHashtable()
endfunction

function CreateArray3 takes integer index, integer size1, integer size2, integer size3 returns nothing
local integer i = 0
local integer j = 0
loop
exitwhen i >= size1
call SaveHashtableHandle( HT, index, i, InitHashtable() )
set i = i + 1
endloop
endfunction

function SaveArray3 takes integer index, integer index1, integer index2, integer index3, integer value returns nothing
local hashtable child = LoadHashtableHandle( HT, index, index1 )
call SaveInteger( child, index2, index3, value )
set child = null
endfunction

function LoadArray3 takes integer index, integer index1, integer index2, integer index3 returns integer
local hashtable child = LoadHashtableHandle( HT, index, index1 )
local integer value = LoadInteger( child, index2, index3 )
set child = null
return value
endfunction

function DestroyArray3 takes integer index, integer size1, integer size2, integer size3 returns nothing
local integer i = 0
local integer j = 0
loop
exitwhen i >= size1
call FlushParentHashtable( LoadHashtableHandle( HT, index, i ) )
set i = i + 1
endloop
call FlushChildHashtable( HT, index )
endfunction
[/jass]
说实话,2维数组用的人就很少了…………

4.利用hashtable来达到I2H的效果
嗯……实际上有了hashtable来存储数据,是应该不需要I2H的函数的。不太推荐。
不过代码如下(相对于ReturnBug和UnionBug,这东西效率简直低下到一定程度):
[jass]
function RegisterUnit takes unit u returns nothing
call SaveUnitHandle( HT, GetHandleId( u ), Const_Unit, u )
endfunction

function I2U takes integer id returns unit
return LoadUnitHandle( HT, id, Const_Unit )
endfunction
[/jass]
使用前,必须用RegisterUnit注册单位,才能I2U,否则得到null。

以下内容为疯人&衰人友情提供~
5.利用hashtable在函数中传递数组
我们可以利用hashtable来在jass中干一件以前很麻烦的事情~
就是在函数中传递数组array~
在parentKey中分配一个独一无二的索引
在特定的childKey中存储这个数组的尺寸size
其他的childKey中存储数据。
传递数组的时候,只要传递独一无二的索引即可。
然后再在对应的parentKey下的childKey里读取数据。
代码如下(childKey=0存储size):
[jass]
globals
hashtable HT = null
constant integer HT_Max_Key = -1
constant integer HT_Top_Key = -2
constant integer HT_Next_Key = -1
constant integer HT_Size_Key = -2
endglobals

function Init takes nothing returns nothing
call FlushParentHashtable( HT )
set HT = InitHashtable()
call SaveInteger( HT, HT_Max_Key, 0, 1 )
endfunction

function CreateArray takes integer size returns integer
local integer this = LoadInteger( HT, HT_Top_Key, 0 )
if this == 0 then
set this = LoadInteger( HT, HT_Max_Key, 0 )
call SaveInteger( HT, HT_Max_Key, 0, this + 1 )
else
call SaveInteger( HT, HT_Top_Key, 0, LoadInteger( HT, this, HT_Next_Key ) )
endif
call SaveInteger( HT, this, HT_Size_Key, size )
return this
endfunction

function GetArraySize takes integer this returns integer
return LoadInteger( HT, this, HT_Size_Key )
endfunction

function DestroyArray takes integer this returns nothing
call FlushChildHashtable( HT, this )
call SaveInteger( HT, this, HT_Next_Key, LoadInteger( HT, HT_Top_Key, 0 ) )
call SaveInteger( HT, HT_Top_Key, 0, this )
endfunction

function SetArrayInt takes integer this, integer index, integer value returns nothing
call SaveInteger( HT, this, index, value )
endfunction

function GetArrayInt takes integer this, integer index returns integer
return LoadInteger( HT, this, index )
endfunction
[/jass]
其实用hashtable达到的数组,不管是数组的个数,还是数组的尺寸理论上都是无限的,所以弄个size反而画蛇添足……
不过考虑到确实很有用,所以加上了。
你可以和数组版的allocate()和destroy(integer this)对比一下~

6.替换hashtable来达到替换所有操作对象的目的
这个…………
这玩意原理很简单,通过更换全局变量hashtable HT的值,来让更改所有函数的操作对象。
虽然有些非常规,但是就某些封装性来说,还是很有效的。
这东西是疯人用来写完全随机地图用的……估计推广很难。

暂时以上内容就是整个hashtable教程的内容了,因为我本身并没有太熟练地使用hashtable,所以仅仅是这样的一个教程~
感谢疯人的大力支持~
相信hashtable可能还有一些要注意的地方或者很棒的用法。我不能在此全部发掘出来~这个只是用来让大家过渡的东西~
应该就是这样。
话说这次我应该写的还可以了吧……疯人哥哥都说还可以了…………
中文字数6600+~
 楼主| 发表于 2009-10-30 21:32:25 | 显示全部楼层
论坛代码原因前面的4个空格的缩进消失了。
于是放上txt版。

HashTable教程.txt

43 KB, 下载次数: 152

回复

使用道具 举报

 楼主| 发表于 2009-10-30 21:34:33 | 显示全部楼层
两个附带的图(txt版中upload=1和upload=2的地方])
pic1.jpg
pic2.jpg
回复

使用道具 举报

发表于 2009-10-30 21:45:59 | 显示全部楼层
以下是也许不会完成的兼容修改教程……
很可能不会继续写了,也放这里吧。


好吧,鉴于许多人需要,大约写个简单的教程。因为本人没有出过J版的图,所以对于制图的很多细节都不甚清楚,加之对于1.24b的了解程度也不甚精通,所以因此产生的错误或不足先请大家原谅。

一、
修改兼容性,那么首先就要知道不同版本之间的差别。玻璃渣是从1.23b开始修改的。所以这里只列出1.22到1.24b之间关于函数变化的说明。
--------------------------------------------------------------------------
《魔兽争霸III:冰封王座》版本历史\\
--------------------------------------------------------------------------

--------------------------------------------------------------------------
- 补丁 1.24b
--------------------------------------------------------------------------

PC世界编辑器更改

- 地图文件的大小上限已从4MB提高到8MB。
- 添加了可在哈希表(hashtable)中储存哈希表句柄(hashtable handles)的功能。
- 添加了GetSpellTargetX以及GetSpellTargetY的本地函数。
- 添加了一个新的"Agent"类型的基础句柄,现在许多类型均由该类型扩展得来。
- 添加了一个SaveAgentHandle的本地函数,该函数可用来保存大多数的句柄类型。
- 添加了一个处理全局变量变更事件的JASS优化。

修正

- 修正由“Return bug"修正而导致的一些误判。
- 修正了因受哈希表(hashtable)引用统计影响而发生的程序崩溃错误。
- 本地变量不再能够“映射”全局变量。
- 修正了一种用于运算符的类型转换(如,使用一个整数创建一个句柄)。

--------------------------------------------------------------------------
- 补丁 1.24
--------------------------------------------------------------------------

PC世界编辑器更改

- 增加了新的JASS哈希(Hash)表函数,用以替换在修正不安全类型的强制转换中所失去的功能。
   - 哈希表 - 保存物品句柄
   - 哈希表 - 保存单位句柄
   - ...
   - 哈希表 - 读取物品句柄
   - 哈希表 - 读取单位句柄
   - ...
   - 哈希表 - 获取句柄ID

修正

- 修正了一处关于不安全类型的强制转换的漏洞,该漏洞可以允许用户在地图中执行任意代码。
- 修正了JASS不安全类型的强制转换的漏洞("return bug")。
- 修正了“世界编辑器”若干种程序崩溃的错误。

这是玻璃渣1.24b官方补丁中的升级说明。因为1.23的说明只是关于战网、互通漏洞和mac系统下安装的问题,这里就不列出。

实际上在1.23b出来时,暴雪就有个大段的说明v1.23b Map Maker Transition Guide

详细的内容翻译似乎没有,这里给个用网站翻译的地址吧:v1.23b Map Maker Transition Guide翻译
1.23b补丁变化说明,给看不明白的同学
http://forums.battle.net/thread.html?topicId=17730193178

上面的东西没什么不好理解的。

请把StringHash、GetHandleId这两个函数跟那个Hashtable孤立看待。

StringHash就是获得字符串的的唯一标识,GetHandleId就是获得handle对象的唯一标识

然后其实他这个地方写错了,S2I这个函数其实是把字符串转换为整数的函数……也就是说把字符串“A124”转换成124~~这跟“获得该字符串的唯一Integer标识”可是两回事情。

然后,你只要知道StringHash和GetHandleId返回的都是指定对象的游戏内唯一标识就可以了(你可以把它当作直接取对象handle值的函数,虽然这里的唯一标识不一定得跟handle值一样)之所以把String和Handle分开纯粹是因为本来它们就位于互相独立的两表里。这点war3里一直是如此。


然后那个新对象Hashtable,它和handle表是一点关系都没有的,请把它当作新表来理解。你是无法通过操作这个hashtable来访问handle表的。比如如果我事先知道某单位的handle值是12345,那么在新系统中,你是无法用12345来获得那个单位的。除非你把这个单位放进hashtable中,然后用12345来做他的key。这样才可以用LoadUnitHanlde 参数12345来取出那个单位。Hashtable不唯一,可以创建多个,你可以把它简单地理解为效率更高更安全的GameCache。

而且这个hashtable是类型安全的,比如12345表位如果其实存的是个单位,那么你用LoadPlayerHandle12345取出来的是null,而不像以前的Return Bug一样可以不分青红皂白地取。

至于变量连接数问题,这又是比gamecache高级的地方。(gamecache这玩意儿话说这年头还真有人用阿?效率又慢又bug百出的。)就是你在hashtable里存取东西的时候会影响变量连接数。避免出现gamecache里那种指向混乱。

以上是 头目的解读

实际上你不必记住那么多内容。你只要知道1.24b中returnbug和Unionbug都被干掉了。然后新增了一种叫希哈表的东西来代替GC,然后给了一个官方的H2I函数GetHandleId来替代returnbug。

而I2H一类的函数已经不存在了,我们使用从希哈表中读取来代替I2H,不过在此修改之前,我们也需要在H2I中做些修改,比如GetHandleId以及向希哈表中存入数据。

二、
[jass]
InitHashtable //新建哈希表
GetHandleId //获取对象的h2i值
StringHash //获取字符串的哈希值

SaveInteger //保存整数
SaveReal //保存实数
SaveBoolean //保存布尔值
SaveStr //保存字符串
SavePlayerHandle //保存玩家
SaveWidgetHandle //保存有生命的物体
SaveDestructableHandle //保存可破坏物
SaveItemHandle //保存物品
SaveUnitHandle //保存单位
SaveAbilityHandle //保存技能
SaveTimerHandle //保存计时器
SaveTriggerHandle //保存触发器
SaveTriggerConditionHandle //保存触发条件
SaveTriggerActionHandle //保存触发动作
SaveTriggerEventHandle //保存触发事件
SaveForceHandle //保存玩家组
SaveGroupHandle//保存单位组
SaveLocationHandle //保存点
SaveRectHandle //保存区域(矩型)
SaveBooleanExprHandle //保存布尔表达式
SaveSoundHandle //保存音效
SaveEffectHandle //保存特效
SaveUnitPoolHandle //保存单位池
SaveItemPoolHandle //保存物品池
SaveQuestHandle //保存任务
SaveQuestItemHandle //保存任务要求
SaveDefeatConditionHandle //保存失败条件
SaveTimerDialogHandle //保存计时器窗口
SaveLeaderboardHandle //保存排行榜
SaveMultiboardHandle //保存多面板
SaveMultiboardItemHandle //保存多面板项目
SaveTrackableHandle //保存可追踪对象
SaveDialogHandle //保存对话框
SaveButtonHandle //保存对话框按钮
SaveTextTagHandle //保存漂浮文字
SaveLightningHandle //保存闪电效果
SaveImageHandle //保存图像
SaveUbersplatHandle //保存地面纹理变化
SaveRegionHandle //保存区域(不规则)
SaveFogStateHandle //保存迷雾状态
SaveFogModifierHandle //保存可见度修正器
SaveHashtableHandle//保存哈希表

LoadInteger //从哈希表提取整数
LoadReal //从哈希表提取实数
LoadBoolean //从哈希表提取布尔值
LoadStr //从哈希表提取字符串
LoadPlayerHandle //从哈希表提取玩家
LoadWidgetHandle //从哈希表提取有生命的物体
LoadDestructableHandle //从哈希表提取可破坏物
LoadItemHandle //从哈希表提取物品
LoadUnitHandle //从哈希表提取单位
LoadAbilityHandle //从哈希表提取技能
LoadTimerHandle //从哈希表提取计时器
LoadTriggerHandle //从哈希表提取触发器
LoadTriggerConditionHandle //从哈希表提取触发条件
LoadTriggerActionHandle //从哈希表提取触发动作
LoadTriggerEventHandle //从哈希表提取触发事件
LoadForceHandle //从哈希表提取玩家组
LoadGroupHandle //从哈希表提取单位组
LoadLocationHandle //从哈希表提取点
LoadRectHandle //从哈希表提取区域(矩型)
LoadBooleanExprHandle //从哈希表提取布尔表达式
LoadSoundHandle //从哈希表提取音效
LoadEffectHandle //从哈希表提取特效
LoadUnitPoolHandle //从哈希表提取单位池
LoadItemPoolHandle //从哈希表提取物品池
LoadQuestHandle //从哈希表提取任务
LoadQuestItemHandle //从哈希表提取任务要求
LoadDefeatConditionHandle //从哈希表提取失败条件
LoadTimerDialogHandle //从哈希表提取计时器窗口
LoadLeaderboardHandle //从哈希表提取排行榜
LoadMultiboardHandle //从哈希表提取多面板
LoadMultiboardItemHandle //从哈希表提取多面板项目
LoadTrackableHandle //从哈希表提取可追踪对象
LoadDialogHandle //从哈希表提取对话框
LoadButtonHandle //从哈希表提取对话框按钮
LoadTextTagHandle //从哈希表提取漂浮文字
LoadLightningHandle //从哈希表提取闪电效果
LoadImageHandle //从哈希表提取图象
LoadUbersplatHandle //从哈希表提取地面纹理变化
LoadRegionHandle //从哈希表提取区域(不规则)
LoadFogStateHandle //从哈希表提取迷雾状态
LoadFogModifierHandle //从哈希表提取可见度修正器
LoadHashtableHandle// 从哈希表提取哈希表

HaveSavedInteger //希哈表中是否存储整数
HaveSavedReal //希哈表中是否存储实数
HaveSavedBoolean //希哈表中是否存储布尔值
HaveSavedString //希哈表中是否存储字符串
HaveSavedHandle //希哈表中是否存储Handle
HaveSavedValue//希哈表中是否存储以上的值(BJ)

RemoveSavedInteger //清除整数
RemoveSavedReal //清除实数
RemoveSavedBoolean //清除布尔值
RemoveSavedString //清除字符串
RemoveSavedHandle //清除Handle


FlushParentHashtable //清空哈希表
FlushChildHashtable //清空哈希表主索引
[/jass]

以上是新函数的大体翻译,如果想使用汉化的WE包括UI那么请使用 夜天新手助推器(1.24汉化UI)这个,这个UI大体上支持使用1.24b的函数。

建议各位使用官方的升级补丁,而不是剑心等傻瓜补丁。否则可能出现各种问题。
回复

使用道具 举报

发表于 2009-10-30 23:26:48 | 显示全部楼层
好吧,乃们真得搞出来了,辛苦了……

话说乃们用的是哪个支持1.24的pjass啊,咱的war3刷成1.24就成渣了,以前封装的library和struct都报废了……
回复

使用道具 举报

发表于 2009-10-30 23:30:29 | 显示全部楼层
小血大人真厉害呀,如此深奥的内容竟然连我都能看得懂。
回复

使用道具 举报

发表于 2009-10-30 23:38:24 | 显示全部楼层
不过还有一个小小的疑问,不同数据类型的数据是混淆的吗?比如HT的1号parentkey的2号childkey已经存了并且只存了一个整型数据6,现在来个LoadReal(HT,1,2)得到的是6还是0.0?或者来个SaveReal(HT,1,2,7.0)会不会把6覆盖掉?
回复

使用道具 举报

发表于 2009-10-30 23:42:00 | 显示全部楼层
什么时候一起升级到124吧. 还好我没有用到哪些J的部分.
回复

使用道具 举报

发表于 2009-10-30 23:46:36 | 显示全部楼层
引用第6楼0123456789于2009-10-30 23:38发表的  :
不过还有一个小小的疑问,不同数据类型的数据是混淆的吗?比如HT的1号parentkey的2号childkey已经存了并且只存了一个整型数据6,现在来个LoadReal(HT,1,2)得到的是6还是0.0?或者来个SaveReal(HT,1,2,7.0)会不会把6覆盖掉?
会得到0,save会覆盖
引用第4楼louter于2009-10-30 23:26发表的  :
好吧,乃们真得搞出来了,辛苦了……

话说乃们用的是哪个支持1.24的pjass啊,咱的war3刷成1.24就成渣了,以前封装的library和struct都报废了……
没用pj,用的原版……
回复

使用道具 举报

发表于 2009-10-30 23:51:18 | 显示全部楼层
呀,那样的话岂不是可以进行数据类型转换了吗?
回复

使用道具 举报

发表于 2009-10-30 23:53:14 | 显示全部楼层
我去转到wow8骗点三围去……
好歹跟剑魔橙子他们都很熟……
某人没少转小血的东西呢
回复

使用道具 举报

发表于 2009-10-31 00:07:32 | 显示全部楼层
引用第9楼0123456789于2009-10-30 23:51发表的  :
呀,那样的话岂不是可以进行数据类型转换了吗?
怎么转,hashtable是强类型限制的,读取错误的类型返回为空
回复

使用道具 举报

 楼主| 发表于 2009-10-31 06:49:30 | 显示全部楼层
很明显疯人是错误的。
integer real boolean string handle。
是每个childKey的5个互不干扰的数据域。
LoadSave每个数据域都不会影响到其他数据域的数据。
测试触发:
[trigger]
对战初始化
    事件
        Map initialization
    环境
    动作
        哈希表 - Create a hashtable
        Set HT = (Last created hashtable)
        哈希表 - Save 123.00 as 0 of 0 in (Last created hashtable)
        哈希表 - Save 456 as 0 of 0 in (Last created hashtable)
        游戏 - Display to (All players) the text: (String((Load 0 of 0 from (Last created hashtable))))
        游戏 - Display to (All players) the text: (String((Load 0 of 0 from (Last created hashtable))))
[/trigger]
测试结果:
456
123.000
回复

使用道具 举报

发表于 2009-10-31 07:55:17 | 显示全部楼层
引用第12楼血戮魔动冰于2009-10-31 06:49发表的  :
很明显疯人是错误的。
integer real boolean string handle。
是每个childKey的5个互不干扰的数据域。
LoadSave每个数据域都不会影响到其他数据域的数据。
测试触发:
.......

明明更新说明说过会干扰的...

话说创建多个hashtable会不会拖速度?
回复

使用道具 举报

 楼主| 发表于 2009-10-31 07:59:12 | 显示全部楼层
LS请问是哪个更新说明?
至少我测试的是不会干扰的。
创建多个hashtable不会干扰速度。
回复

使用道具 举报

发表于 2009-10-31 08:02:16 | 显示全部楼层
我去转到U9骗点三围。。
原本以为HS充其量就是GC的双胞胎弟弟。。
回复

使用道具 举报

发表于 2009-10-31 08:02:27 | 显示全部楼层
果然是对头目的说明理解错误
回复

使用道具 举报

发表于 2009-10-31 08:07:18 | 显示全部楼层
引用第15楼蟋有的蟀于2009-10-31 08:02发表的  :
我去转到U9骗点三围。。
原本以为HS充其量就是GC的双胞胎弟弟。。
wow8已经骗完
回复

使用道具 举报

 楼主| 发表于 2009-10-31 08:13:44 | 显示全部楼层
引用第15楼蟋有的蟀于2009-10-31 08:02发表的  :
我去转到U9骗点三围。。
原本以为HS充其量就是GC的双胞胎弟弟。。

………………正是因为有你们这种人存在我才需要写这教程…………
话说……HT要真是GC双胞胎弟弟那BLZ弄他干嘛……也不好好想想。
回复

使用道具 举报

 楼主| 发表于 2009-10-31 08:15:42 | 显示全部楼层
引用第13楼zjwzero于2009-10-31 07:55发表的  :


明明更新说明说过会干扰的...

话说创建多个hashtable会不会拖速度?

真正覆盖的是handle域的问题。
比如SaveUnitHandle。
然后SaveTriggerHandle
LoadUnitHandle得到null
LoadTriggerHandle得到刚才的trigger
handle域只有一个。
unit,trigger....那些全部都只能放进这个域里。
所以会产生覆盖问题。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 点一下

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2024-12-4 01:20 , Processed in 0.267784 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表