JASS綜合應用

這裡收集一些有用的技巧和函數,函數的來源不一,有筆者自己撰寫的,也有網路上抓來的。
撰寫的方式多少有差異,筆者不把它統一,以便讓大家看到多元的JASS撰寫方式。

還有,這裡提供的函數主要是為了表達一個技巧和思路,讀者應當在理解後依照自己的需要去調整,而不是照單全收。

布林轉為字串

非常簡單但蠻有用的轉換函數,尤其在製作報錯功能時很有用。

function B2S takes boolean b returns string
    if b then
        return "true"
    endif
    return "false"
endfunction

布林轉為整數

同上,非常簡單但是往往能幫你省下不少程式碼。blizzard.j中有個類似的函數IntegerTertiaryOp,不過稍嫌冗長,不甚實用。

function B2I takes boolean b returns integer
    if b then
        return 1
    endif
    return 0
endfunction

兩方向角間的夾角

function AngleBetweenAngles takes real angle1, real angle2 returns real
    local real r = ModuloReal( angle1 - angle2, 360.0 )
    if r <= 180.0 then
        return r
    endif
    return 360.0 - r
endfunction

又是一個簡單的函數,不過簡單的函數往往能在需要時發揮很大的效果。以角度問題為例,為了考慮360度同位角的問題,判斷角度往往相當複雜。

例如我想做一個背刺技能,用這個函數就可以輕易做判斷:

    if AngleBetweenAngles( GetUnitFacing(攻擊者), GetUnitFacing(被攻擊者) ) < 50.0 then
        //背刺
    else
        //不可背刺
    endif

如果不這麼寫應該怎麼做呢?讀者可以想想看。

提示錯誤訊息

會產生類似技能無法使用的錯誤訊息,雖然和正牌的有些出入,不過……聊勝於無吧。

function ErrorMsg takes player ForPlayer, string msg returns nothing
    local sound error = CreateSoundFromLabel( "InterfaceError",false,false,false,10,10)
    if (GetLocalPlayer() == ForPlayer) then
        call ClearTextMessages()
        call DisplayTimedTextToPlayer( ForPlayer, 0.52, 0.00, 2.00, "|cffffcc16"+msg+"|r" )
        call StartSound( error )
    endif
    call KillSoundWhenDone(error)
    set error = null
endfunction

取得有生命物件的最大生命值

(widget為unit、item、destructible的父類型)

做法非常簡單,只是傳達一個概念而已。這個方式可以類推到取得技能的最大等級、英雄的最大等級、經驗值的最大值、部隊移動速度上限、……等等。

GetWidgetLife和SetWidgetLife也可以用來讀取部隊和設定的生命值,可以取代較為冗長的GetUnitState(xxx, UNIT_STATE_LIFE)及SetUnitState(xxx, UNIT_STATE_LIFE, ###)。

function GetWidgetMaxLife takes widget whichWidget returns real
    local real a
    local real b = GetWidgetLife(whichWidget)
    call SetWidgetLife(whichWidget, 1000000.00)
    set a = GetWidgetLife(whichWidget)
    call SetWidgetLife(whichWidget, b)
    return a
endfunction

取得部隊受到某種傷害的比例 & 判斷部隊是否無敵

同上,簡單的思路活用。

function GetDamageFactor takes unit u, attacktype a, damagetype d returns real
    local real hp=GetWidgetLife(u)
    local real r=hp
    if (hp<1.0) then
        call SetWidgetLife(u,1.0)
        set r=1
    endif
    call UnitDamageTarget(u,u,0.01,true,false,a,d,null)
    set r=(r-GetWidgetLife(u))*100
    call SetWidgetLife(u,hp)
    return r
endfunction

function IsUnitInvulnerable takes unit u returns boolean
    return (GetDamageFactor(u,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_UNIVERSAL)==0.0)
endfunction

取得部隊群組中生命值最低的部隊

仿照大部分blizzard.j中的group處理函數,那兩個借用的blizzard.j變數本來也是用在類似的用途,為了省事就不把值還原了。

function GroupGetWeakestUnitEnum takes nothing returns nothing
    local real life = GetWidgetLife(GetEnumUnit())
    if life < bj_randomSubGroupChance then
        set bj_randomSubGroupChance = life
        set bj_groupRandomCurrentPick = GetEnumUnit()
    endif
endfunction

function GroupGetWeakestUnit takes group whichGroup returns unit
    // If the user wants the group destroyed, remember that fact and clear
    // the flag, in case it is used again in the callback.
    local boolean wantDestroy = bj_wantDestroyGroup
    set bj_wantDestroyGroup = false

    set bj_randomSubGroupChance = GetWidgetLife(FirstOfGroup(whichGroup))
    set bj_groupRandomCurrentPick = FirstOfGroup(whichGroup)
    call ForGroup(whichGroup, function GroupGetWeakestUnitEnum)

    // If the user wants the group destroyed, do so now.
    if (wantDestroy) then
        call DestroyGroup(whichGroup)
    endif
    return bj_groupRandomCurrentPick
endfunction

取得符合特定條件的最靠近部隊

一個應該蠻有用的函數,可取得符合指定條件且最靠近某處的部隊。

除了GetNearestUnit_Core以外都可以直接使用。

function GetNearestUnit_Reference takes nothing returns unit
    return bj_meleeNearestMine
endfunction

function GetNearestUnit_Core takes real x, real y, real radius, boolexpr filter returns unit
    local real dist
    local real mindist = 9999999.
    local group g = CreateGroup()
    local unit pick
    call GroupEnumUnitsInRange(g, x, y, radius, filter)
    call DestroyBoolExpr(filter)
    if bj_meleeNearestMine != null then
        call GroupRemoveUnit(g,bj_meleeNearestMine)
        set bj_meleeNearestMine = null  
    endif
    loop
        set pick = FirstOfGroup(g)
        exitwhen pick == null
        call GroupRemoveUnit(g, pick)
        set dist = DistanceBetweenPointsXY( GetUnitX(pick), GetUnitY(pick), x, y )
        if dist < mindist then
            set bj_meleeNearestMine = pick
            set mindist = dist
        endif
    endloop
    call DestroyGroup(g)
    set g = null
    return bj_meleeNearestMine
endfunction

//以點(x,y)做為參考點
function GetNearestUnitXY takes real x, real y, real radius, boolexpr filter returns unit
    set bj_meleeNearestMine = null
    return GetNearestUnit_Core( x, y, radius, filter )
endfunction

//取參考點
function GetNearestUnitLoc takes location loc, real radius, boolexpr filter returns unit
    set bj_meleeNearestMine = null
    return GetNearestUnit_Core( GetLocationX(loc), GetLocationY(loc), radius, filter )
endfunction

//取一部隊作為參考點(排除自身,並可使用GetNearestUnit_Reference於filter判斷條件中指代此部隊)
//也可以使用自訂的變數取代bj_meleeNearestMine
function GetNearestUnit takes unit u, real radius, boolexpr filter returns unit
    set bj_meleeNearestMine = u
    return GetNearestUnit_Core( GetUnitX(u), GetUnitY(u), radius, filter )
endfunction

修改傷害的函數

此函數可以把正要受到的傷害(過小及負數傷害不計)修改為你要的數值。

此函數必須在EVENT_UNIT_DAMAGED(部隊遭受傷害)觸發事件下呼叫,否則沒有效果。

範例:

    call DamageModify( 0.0 )
    call DamageModify( GetEventDamage() - 10.0 ) 
    call DamageModify( GetEventDamage() / 2.0 )
constant function DamageModify_DeadFixerAbility takes nothing returns integer
    return 'A000' //一個可以增加大量生命值上限的技能,可用增加生命上限的物品技能修改
endfunction

function DamageModify_Finish takes timer t returns nothing
    local unit u = GetHandleUnit(t,"u")
    if GetHandleBoolean(t,"fix") then
        call UnitRemoveAbility( u, DamageModify_DeadFixerAbility() )
    endif
    call SetUnitState(u ,UNIT_STATE_LIFE, GetHandleReal(t,"finalhp") )
    call SetHandleHandle(u,"#DamageModify:Timer",null)
    call FlushHandleLocals(t)
    call PauseTimer(t)
    call DestroyTimer(t)
    set u = null
endfunction

function DamageModify_Expire takes nothing returns nothing
    call DamageModify_Finish(GetExpiredTimer())
endfunction

// 執行順序:傷害事件→DamageModify→真正受到傷害扣血→DamageModify_Expire→DamageModify_Finish
function DamageModify takes real dmgnew returns nothing
    local unit u = GetTriggerUnit()
    local timer t = GetHandleTimer(u,"#DamageModify:Timer")
    local real life
    local real mlife
    local real dmg
    local real delta
    if (t!=null) then
        //如果此部隊正進行傷害修改,則先完成之前的設定以免造成衝突
        //此舉是為了避免像Bash這種可能同時輸出2個傷害的情形
        //雙傷害的執行順序:傷害事件(1)→DamageModify(1)→真正受到傷害扣血(1)→傷害事件(2)→DamageModify(2)
        //        →DamageModify_Finish(1)→真正受到傷害扣血(2)→DamageModify_Expire(2)→DamageModify_Finish(2)
        call DamageModify_Finish(t)
    endif
    set dmg = GetEventDamage()
    if (dmg < 0.01) then
        //忽略負數和過小的傷害。
        //0傷害通常由buff造成,不是真正的傷害;另外,若是呼叫此函數的時機錯誤(不在傷害時件下呼叫),此值為0
        //如有特殊需要也可把這個判斷刪掉。
        return
    endif
    set life = GetUnitState(u, UNIT_STATE_LIFE)
    set mlife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
    set delta = dmg - dmgnew
    if (life-dmgnew < 0.0) then
        //修改後如果會死,就把生命值降到底限,讓它受到傷害死亡。
        //(0.41接近生命值最低極限,即使只是降到0.40也會使部隊死亡)
        call SetUnitState(u, UNIT_STATE_LIFE, 0.41)
        return
    endif
    if (life+delta > mlife) then
        //這種情況下無法先加血再自然扣,所以只好另外設個計時器,等扣完再調整血量。
        set t = CreateTimer()
        if (dmg >= mlife) then
            //傷害過大,足以致死,必須先調增最大生命值
            call UnitAddAbility( u, DamageModify_DeadFixerAbility() )
            set mlife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
            call SetHandleBoolean(t,"fix",true)
        endif
        call SetUnitState(u ,UNIT_STATE_LIFE, mlife )
        call SetHandleHandle(u,"#DamageModify:Timer",t)
        call SetHandleHandle(t,"u",u)
        call SetHandleReal(t,"finalhp",life-dmgnew)
        call TimerStart(t, 0, false, function DamageModify_Expire)
        set t = null
    else
        //這種情況下可以直接加血讓它自然扣回
        call SetUnitState(u ,UNIT_STATE_LIFE, life+delta )
    endif
endfunction

這個函數邏輯上相當複雜,讀者可以多多思考。學習遇到多種的可能性時,要如何將其條理化和最簡化,兼顧效率和易讀性。

全地圖部隊傷害事件系統

由於event也會佔用記憶體,而且移除event的唯一方法就是移除其所屬的trigger,因此我們不能把所有部隊傷害事件都登錄在同一個trigger,而必須個別為每個部隊創造一個trigger和triggeraction。這個系統會自動把不存在的部隊對應的trigger和triggeraction刪除,不會造成記憶體洩漏。

相對於把所有部隊傷害事件登錄到同一個觸發 (參見FAQ)而言,如果地圖的部隊不多,而且不會創造出太多的部隊,那麼前述的方法會比較適合,因為trigger和triggeraction共用,比較不佔用記憶體。如果隨著遊戲的進行會不斷增加部隊,那麼使用這個系統避免記憶體漏失較佳。

幾個技巧的應用:

  1. 利用GetUnitTypeId(u)==0判斷部隊是否已從遊戲中完全移除。當然還有別的方法,例如:GetUnitState(u,UNIT_STATE_MAX_LIFE)==0

  2. 利用GetTriggerEventId()比對事件,讓一個觸發可以因事件的不同而執行不同的動作

  3. 使用EVENT_UNIT_DAMAGED作為資料庫(unitevent是handle的子類型)

//***************************************************************************
//*
//*  全地圖部隊傷害事件系統 
//*
//*  1.使用TriggerRegisterAnyUnitDamageEvent函數為特定觸發登錄全地圖部隊傷害事件 
//*  2.此系統不支援一個觸發登錄多個AnyUnitDamageEvent(基本上應該很少人會這樣用) 
//*    要改成能支援也行,只是太過麻煩且意義不大。
//*  3.由於此系統使用特殊方式處理,因此須另外注意:
//*    (1)遊戲中若要remove unit,必須先kill unit再remove unit,否則會造成記憶體漏失 。
//*    (2)在給觸發登錄AnyDamageEvent之後,如果要移除該觸發,
//*       須在移除前先呼叫TriggerUnregisterAnyUnitDamageEvent把事件反登錄,否則會造成記憶體漏失。 
//*       (當然,也可以在任何你想反登錄事件的時候做反登錄) 
//*
//***************************************************************************

//===========================================================================
//  傷害事件登入及觸動
//===========================================================================

function TriggerRegisterAnyUnitDamageEvent_Damage takes nothing returns nothing
    local integer i = 1
    local integer max = GetHandleInt( EVENT_UNIT_DAMAGED, "max" )
    loop
        exitwhen i > max
        call ConditionalTriggerExecute(GetHandleTrigger(EVENT_UNIT_DAMAGED, "#Trigger:"+I2S(i)))
        set i = i + 1
    endloop
endfunction

function TriggerRegisterAnyUnitDamageEvent_Register takes trigger t, boolean flag returns nothing
    local integer max = GetHandleInt(EVENT_UNIT_DAMAGED,"max")
    if flag and (GetHandleInt(EVENT_UNIT_DAMAGED,"#IndexOfTrigger:"+H2S(t))==0) then
        set max = max + 1
        call SetHandleInt(EVENT_UNIT_DAMAGED,"max",max)
        call SetHandleInt(EVENT_UNIT_DAMAGED,"#IndexOfTrigger:"+H2S(t),max)
        call SetHandleHandle(EVENT_UNIT_DAMAGED,"#Trigger:"+I2S(max),t)
    else
        call SetHandleHandle(EVENT_UNIT_DAMAGED,"#Trigger:"+ 下行接續
            I2S(GetHandleInt(EVENT_UNIT_DAMAGED,"#IndexOfTrigger:"+H2S(t))),null)
        call SetHandleInt(EVENT_UNIT_DAMAGED,"#IndexOfTrigger:"+H2S(t),0)
        loop
            exitwhen ( GetHandleHandle(EVENT_UNIT_DAMAGED,"#Trigger:"+I2S(max))!=null ) or (max==0)          
            set max = max - 1
        endloop
        call SetHandleInt(EVENT_UNIT_DAMAGED,"max",max)
    endif
endfunction


//===========================================================================
//  動態增減傷害事件
//===========================================================================

function TriggerRegisterAnyUnitDamageEvent_RemoveTrig takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local trigger T1
    local boolean revived = false
    if not IsUnitType(u,UNIT_TYPE_HERO) then
       loop
            set revived = ( GetWidgetLife(u)>0.40 )
            exitwhen revived or ( GetUnitTypeId(u)==0 )
            call TriggerSleepAction(10.0)
       endloop
    endif
    if not revived then
        set T1 = GetHandleTrigger(EVENT_UNIT_DAMAGED,"#T1:"+H2S(u))
        call TriggerRemoveAction(T1,GetHandleTriggerAction(EVENT_UNIT_DAMAGED,"#A1:"+H2S(u)))
        call DestroyTrigger(T1)
        call SetHandleHandle(EVENT_UNIT_DAMAGED,"#T1:"+H2S(u),null)
        call SetHandleHandle(EVENT_UNIT_DAMAGED,"#A1:"+H2S(u),null)
        set T1 = null
    endif
    set u = null
endfunction

function TriggerRegisterAnyUnitDamageEvent_AddTrig takes unit u, trigger T1 returns nothing
    call TriggerRegisterUnitEvent(T1,u,EVENT_UNIT_DAMAGED)
    call SetHandleHandle(EVENT_UNIT_DAMAGED,"#T1:"+H2S(u),T1)
    call SetHandleHandle(EVENT_UNIT_DAMAGED,"#A1:"+H2S(u), 下行接續
        TriggerAddAction(T1,function TriggerRegisterAnyUnitDamageEvent_Damage))
endfunction

function TriggerRegisterAnyUnitDamageEvent_AddTrig1 takes rect r, group g returns nothing
    local unit u
    call GroupEnumUnitsInRect(g,r,null)
    loop
        set u = FirstOfGroup(g)
        exitwhen u == null
        call GroupRemoveUnit(g,u)
        call TriggerRegisterAnyUnitDamageEvent_AddTrig(u,CreateTrigger())
    endloop
    call DestroyGroup(g)
endfunction

function TriggerRegisterAnyUnitDamageEvent_Detector takes nothing returns nothing
    if GetTriggerEventId() == EVENT_PLAYER_UNIT_DEATH then
        call TriggerRegisterAnyUnitDamageEvent_RemoveTrig()
    else
        call TriggerRegisterAnyUnitDamageEvent_AddTrig(GetTriggerUnit(),CreateTrigger())
    endif
endfunction

function TriggerRegisterAnyUnitDamageEvent_Init takes nothing returns nothing
    local trigger TD = CreateTrigger() //Trigger Detector
    local rect r     = GetWorldBounds()
    call TriggerRegisterAnyUnitDamageEvent_AddTrig1(r,CreateGroup())
    call TriggerRegisterEnterRectSimple( TD, r )
    call TriggerRegisterAnyUnitEventBJ( TD, EVENT_PLAYER_HERO_REVIVE_FINISH )
    call TriggerRegisterAnyUnitEventBJ( TD, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddAction( TD, function TriggerRegisterAnyUnitDamageEvent_Detector )
    call RemoveRect(r)
    set TD = null
    set r = null
endfunction


//===========================================================================
//  實用函數 
//===========================================================================

function TriggerUnregisterAnyUnitDamageEvent takes trigger t returns nothing
    call TriggerRegisterAnyUnitDamageEvent_Register(t,false)
endfunction

function TriggerRegisterAnyUnitDamageEvent takes trigger t returns nothing
    if not GetHandleBoolean( EVENT_UNIT_DAMAGED, "init" ) then
        call TriggerRegisterAnyUnitDamageEvent_Init()
        call SetHandleBoolean( EVENT_UNIT_DAMAGED, "init", true )
    endif
    call TriggerRegisterAnyUnitDamageEvent_Register(t,true)
endfunction

取得物品賣價

呼叫此函數時,會自動產生商店進行「試買」,並且利用前後價差取得物品賣價。

如果覺得這樣寫效率不佳,可以自行畫一塊專供商店試買的區域,以及一個專供試買用的商店。這樣就不用每次都創造商店,也不必比對全地圖的物品。

function GetItemGoldCostById_Sub_Enum takes nothing returns nothing
    local real dist = DistanceBetweenPointsXY(GetItemX(GetEnumItem()), GetItemY(GetEnumItem()), 0.0, 0.0 )
    if dist < bj_randomSubGroupChance then
      set bj_itemRandomCurrentPick = GetEnumItem()
      set bj_randomSubGroupChance = dist
    endif
endfunction

//count=-1表示使用預設數量
function GetItemGoldCostById takes integer itemid, integer count returns integer
    local unit U
    local integer cost
    local integer standardcount
    local integer oldgold = GetPlayerState(Player(15),PLAYER_STATE_RESOURCE_GOLD)
    local integer oldwood = GetPlayerState(Player(15),PLAYER_STATE_RESOURCE_LUMBER)
    local real TempR = bj_randomSubGroupChance
    local item TempI = bj_itemRandomCurrentPick
    //設定金錢
    call SetPlayerState( Player(15), PLAYER_STATE_RESOURCE_GOLD, 999999 )
    call SetPlayerState( Player(15), PLAYER_STATE_RESOURCE_LUMBER, 999999 )    
    set cost = GetPlayerState( Player(15), PLAYER_STATE_RESOURCE_GOLD )
    //試買取價差,此時cost為預設價格 
    set U = CreateUnit( Player(15), 'nshe', 0.0, 0.0, 0.0)
    call UnitAddAbility(U, 'Asid')
    call AddItemToStock(U,itemid,1,1)
    call IssueImmediateOrderById(U,itemid)
    set cost = cost - GetPlayerState( Player(15), PLAYER_STATE_RESOURCE_GOLD )
    //取得剛"賣出"的物品
    set bj_randomSubGroupChance = 1000000
    call EnumItemsInRect(bj_mapInitialPlayableArea, null, function GetItemGoldCostById_Sub_Enum)
    //計算單價
    set standardcount = GetItemCharges(bj_itemRandomCurrentPick)
    if (standardcount!=0) and (count!=-1) then
        set cost = R2I( I2R(cost) * I2R(count) / I2R(standardcount) )
    endif
    //善後 
    call RemoveItem(bj_itemRandomCurrentPick)
    call RemoveUnit(U)
    call SetPlayerState(Player(15),PLAYER_STATE_RESOURCE_GOLD,oldgold)
    call SetPlayerState(Player(15),PLAYER_STATE_RESOURCE_LUMBER,oldwood)
    set bj_randomSubGroupChance = TempR
    set bj_itemRandomCurrentPick = TempI
    set U = null
    return cost
endfunction

function GetItemGoldCost takes item I, boolean useCurrentCharge returns integer
    if useCurrentCharge then
        return GetItemGoldCostById( GetItemTypeId(I), GetItemCharges(I) )
    endif
    return GetItemGoldCostById( GetItemTypeId(I), -1 )  
endfunction 

鎖物品系統

讓一個部隊或英雄無法使用物品,但是保有裝備的效果。

function UnitBlockItemUsage_Sub takes nothing returns nothing
    //local string ERROR_MSG = "Unable to use items now!"
    if GetIssuedOrderId() >= 852008 and GetIssuedOrderId() <= 852013 then
        call PauseUnit( GetTriggerUnit(), true )
        call IssueImmediateOrder(GetTriggerUnit(), "stop")
        call PauseUnit( GetTriggerUnit(), false )
        //call ErrorMsg( GetOwningPlayer(GetTriggerUnit()), ERROR_MSG )
    endif
endfunction

//開放使用物品的能力
function UnitUnblockItemUsage takes unit U returns nothing
    local trigger T = GetHandleTrigger(U,"#UnitBlockItemUsage:trig")
    if T != null then
        call TriggerRemoveAction( T, GetHandleTriggerAction(U, "#UnitBlockItemUsage:ta") )
        call DestroyTrigger(T)
        call SetHandleHandle(U,"#UnitBlockItemUsage:trig", null)
        call SetHandleHandle(U,"#UnitBlockItemUsage:ta", null)
        set T = null
    endif
endfunction

//封鎖使用物品的能力
function UnitBlockItemUsage takes unit U returns nothing
    local trigger T
    if GetHandleTrigger(U,"#UnitBlockItemUsage:trig") == null then
        set T = CreateTrigger()
        call SetHandleHandle( U, "#UnitBlockItemUsage:trig", T )
        call SetHandleHandle( U, "#UnitBlockItemUsage:ta" , 下行接續
            TriggerAddAction(T, function UnitBlockItemUsage_Sub) )
        call TriggerRegisterUnitEvent(T, U, EVENT_UNIT_ISSUED_TARGET_ORDER)
        call TriggerRegisterUnitEvent(T, U, EVENT_UNIT_ISSUED_POINT_ORDER)
        call TriggerRegisterUnitEvent(T, U, EVENT_UNIT_ISSUED_ORDER)
        set T = null
    endif
endfunction

置換物品的函數組

模組化函數的範例,主要使用的是ReplaceItem這個函數,然而4個函數皆可使用。

若setcharges為真,則新物品的可用次數會和舊物品一樣。

這裡沒有做相關的檢查,你有可能把非消耗品置換為消耗品,或反向操作,但是這麼做容易造成問題,最好避免。

//置換部隊身上某一格的物品
//如果擁有者死亡,則無法置換,傳回null
function UnitReplaceItemInSlot takes unit owner, integer itemslot, integer newid, boolean setcharges returns item
    local item olditem
    local item newitem
    if GetWidgetLife(owner) <= 0 then //無法對死者置換物品,所以會失敗
        return null
    endif
    set olditem = UnitItemInSlot( owner, itemslot )
    if olditem == null then
        return null
    endif
    call SetItemPosition( olditem, 0.0, 0.0 )
    set newitem = UnitAddItemById(owner, newid)
    call UnitDropItemSlot( owner, newitem, itemslot )
    call SetWidgetLife( newitem, GetWidgetLife(olditem) )
    call SetItemPlayer( newitem, GetItemPlayer(olditem), true )
    if setcharges then
        call SetItemCharges( newitem, GetItemCharges(olditem) )
    endif
    call RemoveItem(olditem)
    return newitem
endfunction

//置換放在地圖上的物品
function ReplaceItemInMap takes item olditem, integer newid, boolean setcharges returns item
    local item newitem
    if (olditem == null) or IsItemOwned( olditem ) then
        return null
    endif
    call SetItemVisible( olditem, false )
    set newitem = CreateItem( newid, GetItemX(olditem), GetItemY(olditem) )
    call SetWidgetLife( newitem, GetWidgetLife(olditem) )
    call SetItemPlayer( newitem, GetItemPlayer(olditem), true )
    if setcharges then
        call SetItemCharges( newitem, GetItemCharges(olditem) )
    endif
    call RemoveItem(olditem)
    return newitem
endfunction

//取得物品的持有者,如果沒有則傳回null
function GetItemOwner takes item whichitem returns unit
    local group g = CreateGroup()
    local unit u
    local integer i = 0
    loop
        call GroupEnumUnitsOfPlayer(g, Player(i), null)
        loop
            set u = FirstOfGroup(g)
            exitwhen u == null
            if UnitHasItem(u, whichitem) then
                call DestroyGroup(g)
                return u
            endif
            call GroupRemoveUnit(g,u)
        endloop
        set i = i + 1
        exitwhen i == bj_MAX_PLAYER_SLOTS
    endloop
    call DestroyGroup(g)
    return null
endfunction

//置換特定物品,無論其是被部隊擁有或是放在地圖上
//如果無法置換則傳回null
function ReplaceItem takes item olditem, integer newid, boolean setcharges returns item
    local item newitem = ReplaceItemInMap( olditem, newid, setcharges )
    local integer itemslot
    local unit owner
    if newitem != null then
        return newitem
    endif
    set owner = GetItemOwner(olditem)
    set itemslot = 0
    loop
        if UnitItemInSlot( owner, itemslot ) == olditem then
            return UnitReplaceItemInSlot( owner, itemslot, newid, setcharges )
        endif
        set itemslot = itemslot + 1
        exitwhen itemslot == UnitInventorySize(owner)
    endloop
    return null
endfunction

自訂的戰敗函數

想像正規遊戲一樣讓戰敗者變成觀察者嗎?用這個函數就行了。

function CustomDefeatDialog takes player whichPlayer, string msg returns nothing
    local trigger t = CreateTrigger()
    local dialog  d = DialogCreate()
    call DialogSetMessage( d, msg )
    //如果地圖設定為「輸家當觀察者」(可用觸發設定),則顯示「繼續當觀察者」選項 
    if IsMapFlagSet(MAP_OBSERVERS_ON_DEATH) then
        call DialogAddButton( d, GetLocalizedString( "GAMEOVER_CONTINUE_OBSERVING" ), 下行接續
            GetLocalizedHotkey("GAMEOVER_CONTINUE_OBSERVING") )
    endif
    //結束遊戲的選項 
    set t = CreateTrigger()
    call TriggerRegisterDialogButtonEvent( t, DialogAddQuitButton( d, true, 下行接續
        GetLocalizedString( "GAMEOVER_QUIT_GAME" ), GetLocalizedHotkey("GAMEOVER_QUIT_GAME") ) )
    call TriggerAddAction( t, function CustomDefeatQuitBJ )
    //顯示對話方塊 
    if (GetLocalPlayer() == whichPlayer) then
        call EnableUserControl( true )
        call EnableUserUI(false)
    endif
    call DialogDisplay( whichPlayer, d, true )
endfunction

function CustomDefeat takes player whichPlayer, string msg returns nothing
    if AllowVictoryDefeat( PLAYER_GAME_RESULT_DEFEAT ) then
        call RemovePlayer( whichPlayer, PLAYER_GAME_RESULT_DEFEAT )
        //如果有很多玩者,則顯示玩者被擊敗的訊息 
        if not bj_isSinglePlayer then
            call DisplayTimedTextFromPlayer(whichPlayer, 0, 0, 60, GetLocalizedString( "PLAYER_DEFEATED" ) )
        endif
        //對話方塊只需顯示給使用者 
        if (GetPlayerController(whichPlayer) == MAP_CONTROL_USER) then
            call CustomDefeatDialog( whichPlayer, msg )
        endif
    endif
endfunction
綜合教學/jass入門教學/e6.jass應用.txt · 上一次變更: 2007年11月11日 3:55 pm 來自 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