物件的身分證字號(回傳錯誤)

回傳錯誤(Return Bug)

function U2I takes unit U returns integer
    return U
    return 0
endfunction

這個函數看起來有些奇怪,對吧?明明要傳回整數,怎麼傳回一個部隊呢?

不過更奇怪的是,如果我把這個函數寫在WE裡面,一向龜毛的WE編譯器居然不會報錯!所以這是一個錯誤,一個Bug。

這個錯誤是由於世界編輯器在檢查函數的回傳時,會從最下面(endfunction那行)往上查,只要查到一個正確的return就停了,不會再管上面有沒有錯誤的return。譬如U2I函數,WE只檢查return 0是正確的就通過,根本不管上面有個錯誤的return U。

讀者可能會問,既然這是一個BUG,為什麼不寫到BUG區呢?那是因為這個BUG是個大福利,它允許我們自由地在物件與整數間進行轉換。 以這個函數為例,把部隊U當成整數傳回後,得到的結果是部隊U在記憶體內部的編號。每個物件在記憶體內的位置是獨一無二的,編號自然也是,所以不同的物件會轉成不同的整數,有如身分證字號一般。

可能因為物件的記憶體編號是整數吧,這個錯誤只適用於將物件和整數互轉,不能把物件轉成布林、實數或字串(反向亦否)。雖然它也能用來做物件和物件之間的互轉,譬如把部隊變成物品、把物品變成觸發、……,不過如果你把部隊當成物品交給電腦去設它的物品數,想也知道十之八九會出問題,輕則沒效,重則當掉,所以實用性 有限。

也許因為這個錯誤意外地好用,B社之前已公開聲明,不會在未來的WE中修正它,所以讀者學了可以放心使用,不必擔心以後無效。

利用回傳錯誤做出的轉換函數

利用相同的方式,我們可以製作出如下的轉換函數:

function H2I takes handle h returns integer
    return h
    return 0
endfunction

function H2S takes handle h returns string
    return I2S(H2I(h))
endfunction

function I2L takes integer i returns location
    return i
    return null
endfunction

function I2U takes integer i returns unit
    return i
    return null
endfunction

function I2T takes integer i returns trigger
    return i
    return null
endfunction

function I2TA takes integer i returns triggeraction
    return i
    return null
endfunction

function I2TI takes integer i returns timer
    return i
    return null
endfunction 

注意,由於所有的物件都屬於handle的子類型,所以我們可以使用一個函數H2I來取代U2I(unit→integer)、L2I(location→integer)等等 。 但是反向就不行,我們不能只用一個I2H代替全部。如果一個函數takes handle,可以傳unit進去;可是一個函數如果takes unit,就不能放handle進去,誰知道你的handle是不是其它類型的東西。 由於我們不能把handle當成unit使用,所以如果你要的是unit,就得 多做一個I2U函數。

回傳錯誤與遊戲快取

講了那麼多理論的東西,總要來點實際的應用吧?

不過回傳錯誤的觀念有些複雜,應用也沒辦法三言兩語說清,讀者以後會看到很多範例,可以從中學習和研究。

回傳錯誤通常會配會遊戲快取,它可以有效做到物件之間的連繫,或者讓你做出無限多的custom value。

假設現在我有個施法者caster、施法的對象target、輔助用的隱藏部隊hiddencaster,臨時創造出來的輔助觸發T1、T2(皆以caster的特定部隊事件觸發),臨時觸發的動作A1、A2:

function GetMyCache takes nothing returns gamecache 
    return InitGameCache("MyCache.che")
endfunction

    call StoreInteger( GetMyCache(), H2S(caster), "target" , target )
    call StoreInteger( GetMyCache(), H2S(caster), "hiddencaster", hiddencaster )
    call StoreInteger( GetMyCache(), H2S(caster), "target" , target )
    call StoreInteger( GetMyCache(), H2S(caster), "T1" , T1 )
    call StoreInteger( GetMyCache(), H2S(caster), "T2" , T2 )
    call StoreInteger( GetMyCache(), H2S(caster), "A1" , A1 )
    call StoreInteger( GetMyCache(), H2S(caster), "A2" , A2 )

利用這些函數,我們就可以把所有的物件和施法者連接。舉例來說,如果T1的內容是「當caster受到傷害,就對target造成50%的傷害」。

一般而言,要為施法者創造新的傷害事件很簡單,但是傷害事件發生後,要怎麼知道target呢?

你說可以用變數記錄,那如果地圖上有很多個能施展此技能的施法者呢?如此我們就必須設計複雜的陣列來記錄每個施法者和它們對應的目標,觸發會變得很複雜。

如果使用此方法呢?

事件發生後,我如果想要取得施法者,只要用GetTriggerUnit()即可(因為T1、T2只有caster的特定部隊事件)。 如果我想取得target呢?很簡單:I2U( GetStoredInteger( GetMyCache(), H2S(caster), "target" ) ) 想要調用另一個觸發T2嗎?沒問題:I2T( GetStoredInteger( GetMyCache(), H2S(caster), "T2" ) ) 不用了,要清空嗎?好:call FlushStoredMission( GetMyCache(), H2S(caster) )

如此一來,只要我們能取得施法者,就能把它施展技能所用到的所有相關物件調閱出來,而且不用創造一堆全域變數去記錄它。

回傳錯誤配合遊戲快取的轉換函數

事實上大多數的情況下,回傳錯誤都會配合遊戲快取使用,目的是把一筆資料附在一個物件上。為了日後方便,我們可以再製作幾個衍生的函數。

記得開一個專供我們使用的全域變數(類型為遊戲快取),假設名為udg_LocalVars:

function H2I takes handle h returns integer
    return h
    return 0
endfunction

function H2S takes handle h returns string
    return I2S(H2I(h))
endfunction
//這裡使用你定義的全域變數。當然,可以換個名稱
function LocalVars takes nothing returns gamecache
    if (udg_LocalVars==null) then
        set udg_LocalVars = InitGameCache("MyCache.che")
    endif
    return udg_LocalVars
endfunction

//=============================================================================
//    在一個物件上附上一筆資料,name是它的名稱,value是它的內容。
//    如果設定的內容是空值(null、0、...),那麼就清空這筆資料。
//=============================================================================
function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),H2S(subject),name)
    else
        call StoreInteger(LocalVars(), H2S(subject), name, H2I(value))
    endif
endfunction

function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),H2S(subject),name)
    else
        call StoreInteger(LocalVars(), H2S(subject), name, value)
    endif
endfunction

function SetHandleBoolean takes handle subject, string name, boolean value returns nothing
    if value==false then
        call FlushStoredBoolean(LocalVars(),H2S(subject),name)
    else
        call StoreBoolean(LocalVars(), H2S(subject), name, value)
    endif
endfunction

function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), H2S(subject), name)
    else
        call StoreReal(LocalVars(), H2S(subject), name, value)
    endif
endfunction

function SetHandleString takes handle subject, string name, string value returns nothing
    if value==null then
        call FlushStoredString(LocalVars(), H2S(subject), name)
    else
        call StoreString(LocalVars(), H2S(subject), name, value)
    endif
endfunction

//=============================================================================
//    將附在某物件上的所有資料刪除
//=============================================================================
function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), H2S(subject) )
endfunction

//=============================================================================
//    取出附在物件上的資料,用資料名稱當作取資料的關鍵字
//=============================================================================
function GetHandleBoolean takes handle subject, string name returns boolean
    return GetStoredBoolean(LocalVars(), H2S(subject), name)
endfunction

function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), H2S(subject), name)
endfunction

function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), H2S(subject), name)
endfunction

function GetHandleString takes handle subject, string name returns string
    return GetStoredString(LocalVars(), H2S(subject), name)
endfunction

//=============================================================================
//    取出附在物件上的物件資料實體,用資料名稱當作取資料的關鍵字
//=============================================================================
function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), H2S(subject), name)
    return null
endfunction

function GetHandleWidget takes handle subject, string name returns widget
    return GetStoredInteger(LocalVars(), H2S(subject), name)
    return null
endfunction

function GetHandleUnit takes handle subject, string name returns unit
    return GetStoredInteger(LocalVars(), H2S(subject), name)
    return null
endfunction

function GetHandleItem takes handle subject, string name returns item
    return GetStoredInteger(LocalVars(), H2S(subject), name)
    return null
endfunction

function GetHandleTimer takes handle subject, string name returns timer
    return GetStoredInteger(LocalVars(), H2S(subject), name)
    return null
endfunction

function GetHandleTrigger takes handle subject, string name returns trigger
    return GetStoredInteger(LocalVars(), H2S(subject), name)
    return null
endfunction

function GetHandleTriggerAction takes handle subject, string name returns triggeraction
    return GetStoredInteger(LocalVars(), H2S(subject), name)
    return null
endfunction

function GetHandleGroup takes handle subject, string name returns group
    return GetStoredInteger(LocalVars(), H2S(subject), name)
    return null
endfunction

function GetHandleEffect takes handle subject, string name returns effect
    return GetStoredInteger(LocalVars(), H2S(subject), name)
    return null
endfunction

function GetHandleLightning takes handle subject, string name returns lightning
    return GetStoredInteger(LocalVars(), H2S(subject), name)
    return null
endfunction

應用上和前面類似,但程式碼更為簡化:

  • 連結資料:

        call SetHandleHandle( caster, "target"      , target )
        call SetHandleHandle( caster, "hiddencaster", hiddencaster )
        call SetHandleHandle( caster, "T1"          , T1 )
        call SetHandleHandle( caster, "T2"          , T2 )
        call SetHandleHandle( caster, "A1"          , A1 )
        call SetHandleHandle( caster, "A2"          , A2 )
    

     

  • 讀取資料:

        //target:
        local unit target = GetHandleUnit( caster, "target" )
        //hiddencaster:
        local unit hiddencaster = GetHandleUnit( caster, "hiddencaster" )
        //T1:
        local trigger T1 = GetHandleTrigger( caster, "T1" ) 
    

     

這幾個函數在國外網站使用率非常非常高,極多的函數都是從這裡衍生,所以就算大家不想用,也最好了解其原理。

return bug是一個被JASS高手廣泛研究和使用的工具,有興趣可多去各大網站爬文研究。

若讀者想看更多return bug的範例,可參考回傳錯誤的應用

綜合教學/jass入門教學/a6.物件身分證.txt · 上一次變更: 2007年11月12日 4:48 am 來自 wasabi
www.chimeric.de Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0