library DuelSystem initializer Init
//This is my MUI Duel System like the one known from Warhammer Mark of Chaos...
//
//When a hero attacks another one, a circle of flames appears around them.
//The two will fight till one of them is dead.
//They can't leave the circle and other units can't enter it.
//In addition the both fighters can't be attacked or attack a unit outside...
//Targeted spells won't work from outside, too...
//The winner of a duel will be fully healed.
//
//included functions:
// TriggerRegisterDuelEndEvent takes trigger t returns boolean
// will fire the trigger when a duel ends because of the death of a fighter
//
// TriggerRegisterDuelBeginEvent takes trigger t returns boolean
// will fire when a duel is started (use GetAttacker and GetTriggerUnit to refer to the units)
//
// GetWinner takes nothing returns unit
// will return the unit that won the duel when responding to TriggerRegisterDuelEndEvent
//
// GetLoser takes nothing returns unit
// will return the unit that lost the duel when responding to TriggerRegisterDuelEndEvent
//
// ForceDuel takes unit a, unit b, real x, real y returns boolean
// will force a duel between unit a and b with the center of the duel circle at x,y
//
// ForceDuelLoc takes unit a, unit b, location p returns boolean
// same like ForceDuel but with a location instead of coordinates
//
// EnableWinnerHeal takes boolean flag returns nothing
// simply turns on or off the feature that the winner of a duel is fully healed
// The Setup Part
globals
// the effect model which makes the circle
private constant string FLAME_SFX = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
// the effect created when a duel is won
private constant string WIN_SFX = "Abilities\\Spells\\Other\\Awaken\\Awaken.mdl"
// the effect created when a duel is aborted
private constant string ABORT_SFX = "Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl"
// the unit spinning around the circle
private constant integer EFFECT_UNIT = 'h000'
// the radius of the circle
private constant real RADIUS = 400
// the interval of the timer
private constant real INTERVAL = 0.01
// if true the winner of a duel is healed
private boolean heal = true
endglobals
// end of Setup Part (dont change anything below)
//---------------------------------------------
private struct data
unit u1
unit u2
unit d1
unit d2
effect array sfx[37]
real x
real y
real r = 0
integer c = 0
integer s = 0
endstruct
globals
private integer total = 0
private data array DATAS
private group g = CreateGroup()
private group gg = CreateGroup()
private unit winner
private unit loser
private trigger array triggers
private integer total_triggers = 0
private trigger array triggers2
private integer total_triggers2 = 0
private timer tim = CreateTimer()
private hashtable h = InitHashtable()
private data Temp
endglobals
private function DistanceBetweenUnits takes unit a, unit b returns real
local real dx = GetUnitX(b) - GetUnitX(a)
local real dy = GetUnitY(b) - GetUnitY(a)
return SquareRoot(dx * dx + dy * dy)
endfunction
private function AngleBetweenCoords takes real x, real y, real xx, real yy returns real
return Atan2(yy - y, xx - x)
endfunction
private function IsUnitDead takes unit u returns boolean
return (IsUnitType(u,UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0 )
endfunction
function GetDuelWinner takes nothing returns unit
return winner
endfunction
function GetDuelLoser takes nothing returns unit
return loser
endfunction
function EnableWinnerHeal takes boolean flag returns nothing
set heal = flag
endfunction
function TriggerRegisterDuelEndEvent takes trigger t returns boolean
local integer i = 0
if LoadInteger(h, GetHandleId(t),0) == 1 then
return false
endif
set triggers[total_triggers] = t
set total_triggers = total_triggers + 1
call SaveInteger(h, GetHandleId(t),0,1)
return true
endfunction
function TriggerRegisterDuelBeginEvent takes trigger t returns boolean
local integer i = 0
if LoadInteger(h, GetHandleId(t),1) == 1 then
return false
endif
set triggers2[total_triggers2] = t
set total_triggers2 = total_triggers2 + 1
call SaveInteger(h, GetHandleId(t),1,1)
return true
endfunction
function EndDuel takes unit u returns nothing
local integer ii = 0
local integer i = LoadInteger(h, GetHandleId(u),0)
local real x
local real y
local data dat = DATAS[i]
if IsUnitInGroup(u,g) then
call FlushChildHashtable(h,GetHandleId(dat.u1))
call FlushChildHashtable(h,GetHandleId(dat.u2))
loop
exitwhen ii == 36
call DestroyEffect(dat.sfx[ii])
set x = dat.x + RADIUS * Cos(ii*10 * bj_DEGTORAD)
set y = dat.y + RADIUS * Sin(ii*10 * bj_DEGTORAD)
call DestroyEffect(AddSpecialEffect(ABORT_SFX,x,y))
set ii = ii + 1
endloop
call RemoveUnit(dat.d1)
call RemoveUnit(dat.d2)
call GroupRemoveUnit(g,dat.u1)
call GroupRemoveUnit(g,dat.u2)
set total = total - 1
set DATAS[i] = DATAS[total]
call SaveInteger(h, GetHandleId(DATAS[i].u1),0,i)
call SaveInteger(h, GetHandleId(DATAS[i].u2),0,i)
call dat.destroy()
if total == 0 then
call PauseTimer(tim)
endif
endif
endfunction
private function PortAway takes nothing returns nothing
local real x
local real y
local real r
set r = AngleBetweenCoords(Temp.x,Temp.y,GetUnitX(GetEnumUnit()),GetUnitY(GetEnumUnit()))
set x = Temp.x + (RADIUS + 51) * Cos(r)
set y = Temp.y + (RADIUS + 51) * Sin(r)
call SetUnitPosition(GetEnumUnit(),x,y)
call IssueImmediateOrder(GetEnumUnit(),"stop")
endfunction
private function execute takes nothing returns nothing
local data dat
local integer i = 0
local integer ii
local real x
local real y
local real r
local unit u
loop
exitwhen i >= total
set dat = DATAS[i]
//*****circling*****
set x = dat.x + RADIUS * Cos(dat.r * bj_DEGTORAD)
set y = dat.y + RADIUS * Sin(dat.r * bj_DEGTORAD)
call SetUnitPosition(dat.d1,x,y)
set x = dat.x + RADIUS * Cos((dat.r + 180) * bj_DEGTORAD)
set y = dat.y + RADIUS * Sin((dat.r + 180) * bj_DEGTORAD)
call SetUnitPosition(dat.d2,x,y)
set dat.r = dat.r + 1
//*****creating sfx*****
set dat.s = dat.s + 1
if dat.s == 10 and dat.sfx[35] == null then
set dat.s = 0
set dat.sfx[dat.c] = AddSpecialEffect(FLAME_SFX,GetUnitX(dat.d1),GetUnitY(dat.d1))
set dat.sfx[dat.c+1] = AddSpecialEffect(FLAME_SFX,GetUnitX(dat.d2),GetUnitY(dat.d2))
set dat.c = dat.c + 2
endif
//*****port fighters back into circle*****
call GroupEnumUnitsInRange(gg,dat.x,dat.y,RADIUS-50,null)
if not IsUnitInGroup(dat.u1,gg) then
set r = AngleBetweenCoords(dat.x,dat.y,GetUnitX(dat.u1),GetUnitY(dat.u1))
set x = dat.x + (RADIUS - 50) * Cos(r)
set y = dat.y + (RADIUS - 50) * Sin(r)
call SetUnitPosition(dat.u1,x,y)
endif
if not IsUnitInGroup(dat.u2,gg) then
set r = AngleBetweenCoords(dat.x,dat.y,GetUnitX(dat.u2),GetUnitY(dat.u2))
set x = dat.x + (RADIUS - 50) * Cos(r)
set y = dat.y + (RADIUS - 50) * Sin(r)
call SetUnitPosition(dat.u2,x,y)
endif
call GroupClear(gg)
//*****port other units away from the circle*****
call GroupEnumUnitsInRange(gg,dat.x,dat.y,RADIUS + 50,null)
call GroupRemoveUnit(gg,dat.u1)
call GroupRemoveUnit(gg,dat.u2)
set Temp = dat
call ForGroup(gg, function PortAway)
call GroupClear(gg)
//*****Is a fighter dead?*****
if IsUnitDead(dat.u1) or IsUnitDead(dat.u2) then
if IsUnitDead(dat.u1) then
set winner = dat.u2 //if unit 1 is dead unit 2 is the winner
set loser = dat.u1
else
set winner = dat.u1 //else the winner is unit 1
set loser = dat.u2
endif
if heal then
call SetWidgetLife(winner,9999999999)
endif
call SetUnitAnimation(winner,"stand victory")
call EndDuel(dat.u1)
set ii = 0
loop
exitwhen ii >= total_triggers
if triggers[ii] != null and TriggerEvaluate(triggers[ii]) then
call TriggerExecute(triggers[ii])
endif
set ii = ii + 1
endloop
endif
//*****prepare next*****
set i = i + 1
endloop
set u = null
endfunction
function ForceDuel takes unit a, unit b, real x, real y returns boolean
local data dat
local integer i = 0
if IsUnitInGroup(a,g) or IsUnitInGroup(b,g) or IsUnitAlly(a,GetOwningPlayer(b)) then
return false
endif
set dat = data.create()
set dat.u1 = a
set dat.u2 = b
call SaveInteger(h, GetHandleId(a),0,total)
call SaveInteger(h, GetHandleId(b),0,total)
set dat.x = x
set dat.y = y
call GroupAddUnit(g,a)
call GroupAddUnit(g,b)
set dat.d1 = CreateUnit(Player(15),EFFECT_UNIT,0,0,0)
call UnitAddAbility(dat.d1,'Aloc')
call SetUnitPathing(dat.d1,false)
call PauseUnit(dat.d1,true)
set dat.d2 = CreateUnit(Player(15),EFFECT_UNIT,0,0,0)
call UnitAddAbility(dat.d2,'Aloc')
call SetUnitPathing(dat.d2,false)
call PauseUnit(dat.d2,true)
set dat.sfx[35] = null
set DATAS[total] = dat
set total = total + 1
if total == 1 then
call TimerStart(tim, INTERVAL, true, function execute)
endif
loop
exitwhen i >= total_triggers2
if TriggerEvaluate(triggers2[i]) then
call TriggerExecute(triggers2[i])
endif
set i = i + 1
endloop
return true
endfunction
function ForceDuelLoc takes unit a, unit b, location p returns boolean
return ForceDuel(a, b, GetLocationX(p), GetLocationY(p))
endfunction
private function start takes nothing returns boolean
local unit a = GetTriggerUnit()
local unit b = GetAttacker()
local real x = (GetUnitX(b) + GetUnitX(a))/2
local real y = (GetUnitY(b) + GetUnitY(a))/2
if IsUnitType(a,UNIT_TYPE_HERO) and IsUnitType(b,UNIT_TYPE_HERO) then
call ForceDuel(a,b,x,y)
endif
set b = null
set a = null
return false
endfunction
private function PreventAttacking takes nothing returns boolean
if (IsUnitInGroup(GetTriggerUnit(),g) and not IsUnitInGroup(GetAttacker(),g)) or (IsUnitInGroup(GetAttacker(),g) and not IsUnitInGroup(GetTriggerUnit(),g)) then
call IssueImmediateOrder(GetAttacker(),"stop")
endif
return false
endfunction
private function PreventCasting takes nothing returns boolean
if not IsUnitInGroup(GetTriggerUnit(),g) and IsUnitInGroup(GetSpellTargetUnit(),g) then
call IssueImmediateOrder(GetTriggerUnit(),"stop")
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerAddCondition(t,Condition(function start))
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ATTACKED)
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ATTACKED)
call TriggerAddCondition(t,Condition(function PreventAttacking))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerAddCondition(t,Condition(function PreventCasting))
endfunction
endlibrary