System Duel Engine 1.10

emjlr3

Change can be a good thing
Reaction score
395
Duel Engine 1.10
by: emjlr3

DuelEngine.jpg

  • A dynamic, fully customizable Duel Engine.
  • Supports multiple arenas and includes simple computer acceptance AI.
  • Just type -duel to get the party started!

JASS:
scope DuelEngineConfig initializer Init

// Copy all Duel Engine triggers to your map (except for the Post Duel Sample, unless you want to use it
// Go through this trigger, configure the options, and enjoy!

private function Init takes nothing returns nothing
    // Time in seconds before first duel can occur (>1.)
    set TimeBeforeFirstDuel = 15.
    // Whether a duel is forced after a duration within which there were no duels (True or False)
    set ForcedDuelMatches = true
    // That no duel duration in seconds after which we will force a duel (>30.)
    set ForceDuelTime = 90.
    // Time in seconds after a duel acceptance that it will begin (>1.)
    set DuelStartTime = 10.
    // The maximum time in seconds that a duel will last, after which it is forced to end
    set MaxDuelTime = 120.
    // Time in seconds before a duel demand is automatically canceled
    set AFK_Timeout = 12.
    // If you have computer heroes, this will add duel acceptance AI (True or False)
    // This will accept duels 75% of the time so long as the challenging hero is no more then 2 levels higher then the challengee
    set DuelAcceptanceAI = true
    // This will change the camera bounds for dueling players so they only see the arena (True or False)
    // Remember to change them back once the duel is over
    set ChangeCameraBounds = true
    
    // Duel Arena Setup
    // Each duel arena should have a region over its entire area
    // They should also have two smallers regions, one at each side, for heroes to be placed at the start of a duel
    // This is where we will setup the arenas and each arenas start location
    set Arenas[1] = gg_rct_Duel // This is my arena
    set ArenaSides[1] = GetRectCenter(gg_rct_DuelStart1) // These are the two sides of my arena
    set ArenaSides[2] = GetRectCenter(gg_rct_DuelStart2)
    // If I wanted to add more, it would simply be
    //set Arenas[2] = gg_rct_Name
    //set ArenaSides[3] = GetRectCenter(gg_rct_Name)
    //set ArenaSides[4] = GetRectCenter(gg_rct_Name)
    // It is very important to keep the numbering constant like this, my selection algorithim depends on it!
    
    // This is our total number of arenas available
    set TotalArenas = 1
    
    // When a duel is over, the winning hero is put back at their players starting location, so place them accordingly
    
    // This is the trigger you want ran after a duel has finished
    //set PostDuelTrigger = gg_trg_Name
    // Uncomment the above and write the correct name if you have one
    // In this trigger, the duel winner is stored as 'DuelWinner' and the loser as 'DuelLoser' (Players)
    // However, if the duel was a tie, DuelIsTie will be true (as opposed to false when it is not a tie)
    // Duel playes can then be refered to as DuelPlayers[1] and DuelPlayers[2], respectively
    
    // Needed, don't touch
    call DuelEngineInit_Init.execute()
endfunction

endscope

JASS:
scope DuelEngineDuels initializer Init

globals
    public trigger Trig = CreateTrigger()
    
    private integer I 
    
    private group G1 = CreateGroup()
    private group G2 = CreateGroup()
    
    private timer T
    private timerdialog TD
endglobals

// Needed
private function Filter_Heroes takes nothing returns boolean
    return IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO) == true 
endfunction
private function Hide takes nothing returns nothing
    if GetLocalPlayer()==GetEnumPlayer() then
        call TimerDialogDisplay(TD,false)
    endif
endfunction
private function Show takes nothing returns nothing
    if GetLocalPlayer()==GetEnumPlayer() then
        call TimerDialogDisplay(TD,true)
    endif
endfunction

// Duel Over
private function MoveBack takes nothing returns nothing
    local unit u = GetEnumUnit()
    local player p = GetOwningPlayer(u)
    
    call SetUnitPosition(u,GetPlayerStartLocationX(p),GetPlayerStartLocationY(p))
    if GetLocalPlayer()==p then
        call SelectUnit(u,true)
        call PanCameraTo(GetUnitX(u),GetUnitY(u))
    endif
    
    set u = null
endfunction
private function End takes nothing returns boolean
    local unit u = GetTriggerUnit()
    local group g 
    
    if GetTriggerEventId()==EVENT_PLAYER_UNIT_DEATH then
        if not IsUnitInGroup(u,G1) and not IsUnitInGroup(u,G2) then
            return false
        endif
        
        if GetOwningPlayer(u)==DuelPlayers[1] then
            call GroupRemoveUnit(G1,u)
            if CountUnitsInGroup(G1)==0 then
                set DuelWinner = DuelPlayers[2]
                set DuelLoser = DuelPlayers[1]
                set DuelInProgress = false
            endif
        else
            call GroupRemoveUnit(G2,u)
            if CountUnitsInGroup(G2)==0 then
                set DuelWinner = DuelPlayers[1]
                set DuelLoser = DuelPlayers[2]
                set DuelInProgress = false
            endif
        endif
        if not DuelInProgress then
            set g = CreateGroup()
            call GroupEnumUnitsOfPlayer(g,DuelWinner,Condition(function Filter_Heroes))
            call ForGroup(g,function MoveBack)
            call ForForce(bj_FORCE_ALL_PLAYERS,function Hide)
            call QuestMessageBJ( bj_FORCE_ALL_PLAYERS, bj_QUESTMESSAGE_ALWAYSHINT,GetPlayerName(DuelWinner) + " has defeated " + GetPlayerName(DuelLoser) + " in their duel." )
            if PostDuelTrigger!=null then    
                set DuelIsTie = false
                call TriggerExecute(PostDuelTrigger)
            endif
            call DestroyGroup(g)
            set g = null
            call PauseTimer(T)
            set TimeSinceLastDuel = 0.
            call DisableTrigger(Trig)
        endif
    else
        set DuelInProgress = false
        call QuestMessageBJ( bj_FORCE_ALL_PLAYERS, bj_QUESTMESSAGE_ALWAYSHINT,"The duel between "+GetPlayerName(DuelPlayers[1]) + " and " + GetPlayerName(DuelPlayers[2]) + " has ended in a tie." )
        set g = CreateGroup()
        call GroupEnumUnitsOfPlayer(g,DuelPlayers[1],Condition(function Filter_Heroes))
        call ForGroup(g,function MoveBack)
        call GroupClear(g)
        call GroupEnumUnitsOfPlayer(g,DuelPlayers[2],Condition(function Filter_Heroes))
        call ForGroup(g,function MoveBack)
        call ForForce(bj_FORCE_ALL_PLAYERS,function Hide)
        call DestroyGroup(g)
        set g = null
        if PostDuelTrigger!=null then    
            set DuelIsTie = true
            call TriggerExecute(PostDuelTrigger)
        endif
        set TimeSinceLastDuel = 0.
        call DisableTrigger(Trig)
    endif
    
    return false
endfunction

// Duel Begin
private function Move takes nothing returns nothing
    local unit u = GetEnumUnit()
    
    if GetWidgetLife(u)<.405 then
        call ReviveHeroLoc(u,ArenaSides<i>,true)
    else
        call SetUnitPositionLoc(u,ArenaSides<i>)
    endif
    call SetWidgetLife(u,GetUnitState(u,UNIT_STATE_MAX_LIFE))
    call SetUnitState(u,UNIT_STATE_MANA,GetUnitState(u,UNIT_STATE_MAX_MANA))
    call UnitRemoveBuffs(u,true,true)
    call UnitResetCooldown(u)
    if GetLocalPlayer()==GetOwningPlayer(u) then
        call SelectUnit(u,true)
        call PanCameraTo(GetUnitX(u),GetUnitY(u))
    endif
    
    set u = null
endfunction
private function Start takes nothing returns nothing
    local group g = CreateGroup()
    local integer i = GetRandomInt(1,TotalArenas)
    
    if not DuelInProgress then
        return
    endif
    if GetLocalPlayer()==DuelPlayers[1] or GetLocalPlayer()==DuelPlayers[2] and ChangeCameraBounds then
        call SetCameraBoundsToRect(Arenas<i>)
    endif
    
    call GroupClear(G1)
    call GroupClear(G2)
    
    set I = (i*2)-1
    call GroupEnumUnitsOfPlayer(g,DuelPlayers[1],Condition(function Filter_Heroes))
    call ForGroup(g,function Move)
    call GroupAddGroup(g,G1)
    call GroupClear(g)
    set I = I+1
    call GroupEnumUnitsOfPlayer(g,DuelPlayers[2],Condition(function Filter_Heroes))
    call ForGroup(g,function Move)
    call GroupAddGroup(g,G2)
    
    set I = i
    call TimerDialogSetTitle(TD,&quot;Duel Time&quot;)
    call TimerStart(T,MaxDuelTime,false,function End)
    call EnableTrigger(Trig)
    
    call DestroyGroup(g)
    set g = null
endfunction
public function Go takes nothing returns nothing
    call TimerDialogSetTitle(TD,&quot;Duel In&quot;)
    call TimerStart(T,DuelStartTime,false,function Start)
    call ForForce(bj_FORCE_ALL_PLAYERS,function Show)
endfunction

private function Init takes nothing returns nothing
    call TriggerAddCondition(Trig,Condition(function End))
    call TriggerRegisterAnyUnitEventBJ(Trig,EVENT_PLAYER_UNIT_DEATH)
    call DisableTrigger(Trig)
    
    set T = CreateTimer()
    set TD = CreateTimerDialog(T)
endfunction

endscope</i></i></i>


JASS:
scope DuelEngineForced initializer Init

globals
    public trigger Trig = CreateTrigger()
    real TimeSinceLastDuel = 0.
endglobals

private function Go takes nothing returns nothing
    local integer i = 0
    local player p1
    local player p2
    
    if not DuelInProgress then
        set TimeSinceLastDuel = TimeSinceLastDuel + 5.
    endif
    if TimeSinceLastDuel&lt;ForceDuelTime or DuelInProgress then
        return
    endif
    
    loop
        set p1 = Player(GetRandomInt(0,11))
        exitwhen GetPlayerSlotState(p1)==PLAYER_SLOT_STATE_PLAYING
        set i = i + 1
        exitwhen i&gt;100
    endloop
    if i&gt;100 then
        call DisableTrigger(Trig)
        call BJDebugMsg(&quot;Forced duels is now disabled, there are not enough players left to duel.&quot;)
    endif
    set i = 0
    loop
        set p2 = Player(GetRandomInt(0,11))
        exitwhen GetPlayerSlotState(p2)==PLAYER_SLOT_STATE_PLAYING and IsPlayerEnemy(p1,p2)
        set i = i + 1
        exitwhen i&gt;100
    endloop
    if i&gt;100 then
        call DisableTrigger(Trig)
        call BJDebugMsg(&quot;Forced duels is now disabled, there are not enough players left to duel.&quot;)
    endif
    
    call QuestMessageBJ( bj_FORCE_ALL_PLAYERS, bj_QUESTMESSAGE_UPDATED, &quot;Long time no duel...&quot; )
    set DuelInProgress = true
    call TriggerSleepAction(3.)
    set DuelPlayers[1] = p1
    set DuelPlayers[2] = p2
    call QuestMessageBJ( bj_FORCE_ALL_PLAYERS, bj_QUESTMESSAGE_ALWAYSHINT, &quot;Upcoming duel forced between &quot; + GetPlayerName(DuelPlayers[1]) + &quot; and &quot; + GetPlayerName(DuelPlayers[2]) + &quot;.&quot; )
    call DuelEngineDuels_Go.execute()
endfunction

private function Init takes nothing returns nothing
    call TriggerAddAction(Trig,function Go)
    call TriggerRegisterTimerEvent(Trig,5.,true)
    call DisableTrigger(Trig)
endfunction

endscope


JASS:
scope DuelEngineInit

globals
    real TimeBeforeFirstDuel
    boolean ForcedDuelMatches 
    real ForceDuelTime
    real DuelStartTime
    real MaxDuelTime
    boolean DuelAcceptanceAI
    boolean ChangeCameraBounds
    
    rect array Arenas
    location array ArenaSides
    integer TotalArenas
    
    trigger PostDuelTrigger = null
    player DuelWinner
    player DuelLoser
    boolean DuelIsTie = false
    
    real AFK_Timeout = 10.
    
    // Needed
    boolean DuelInProgress = false
endglobals

private function Setup takes nothing returns nothing
    call TriggerRegisterPlayerChatEvent( DuelEnginePlayers_Trig, GetEnumPlayer(), &quot;-duel&quot;, true )
endfunction
private function Start takes nothing returns nothing
    call QuestMessageBJ( bj_FORCE_ALL_PLAYERS, bj_QUESTMESSAGE_ALWAYSHINT, &quot;|cff00ccffDuels Available:|r
    
You may type |cffffcc00-duel|r now to invite opponents to a 1 on 1 duel.&quot; )
    call ForForce(bj_FORCE_ALL_PLAYERS,function Setup)
    if ForcedDuelMatches then
        call EnableTrigger(DuelEngineForced_Trig)
    endif
    
    call DestroyTrigger(GetTriggeringTrigger())
endfunction
public function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterTimerEvent(t,TimeBeforeFirstDuel,false)
    call TriggerAddAction(t,function Start)
    set t = null
endfunction

endscope


JASS:
scope DuelEnginePlayers initializer Init

globals
    public trigger Trig = CreateTrigger()
    public trigger Offer = CreateTrigger()
    public trigger Take = CreateTrigger()
    dialog DuelDialog
    dialog TakeDialog
    button array DuelButtons
    private integer I
    
    player array DuelPlayers
    
    private timer AFK = CreateTimer()
endglobals

private function Filter_Heroes takes nothing returns boolean
    return IsUnitType(GetFilterUnit(), UNIT_TYPE_HERO) == true
endfunction

// Timeout
private function Timeout takes nothing returns nothing
    local force f = GetForceOfPlayer(DuelPlayers[1])
    
    call QuestMessageBJ( f, bj_QUESTMESSAGE_ALWAYSHINT, &quot;Why go afk right after demanding a duel....?&quot; )
    set DuelInProgress = false
    
    call DestroyForce(f)
    set f = null
endfunction

// Accept/Decline duel
private function Accept takes nothing returns nothing
    call QuestMessageBJ( bj_FORCE_ALL_PLAYERS, bj_QUESTMESSAGE_ALWAYSHINT, &quot;Upcoming duel between &quot; + GetPlayerName(DuelPlayers[1]) + &quot; and &quot; + GetPlayerName(DuelPlayers[2]) + &quot;.&quot; )
    call DuelEngineDuels_Go.execute() 
endfunction
private function Decline takes nothing returns nothing
    local force f = GetForceOfPlayer(DuelPlayers[1])
    
    call QuestMessageBJ( f, bj_QUESTMESSAGE_ALWAYSHINT, &quot;Sorry, &quot; + GetPlayerName(DuelPlayers[2]) + &quot; didn&#039;t want to duel with you at this time.&quot; )
    
    call DestroyForce(f)
    set f = null
    set DuelInProgress = false
endfunction
private function TakeOffer takes nothing returns boolean
    call PauseTimer(AFK)
    if GetClickedButton() == DuelButtons[2] then
        call Decline()
    else
        call Accept()
    endif
    
    return false
endfunction

// Illicit duel offer
private function Offered takes nothing returns boolean
    local integer i = 0
    local unit u
    local group g
    local force f
    local player p = GetTriggerPlayer()
    local player take
    local integer lvl
    
    call PauseTimer(AFK)
    
    loop
        exitwhen i &gt; 15
        exitwhen GetClickedButton() == DuelButtons<i>
        set i = i + 1
    endloop
    if i == 15 then
        set f = GetForceOfPlayer(p)
        call QuestMessageBJ( f, bj_QUESTMESSAGE_ALWAYSHINT, &quot;Maybe next time...&quot; )
        set DuelInProgress = false
        call DestroyForce(f)
        set f = null
        return false
    endif
    set take = Player(i)
    if GetPlayerSlotState(take) != PLAYER_SLOT_STATE_PLAYING then
        set f = GetForceOfPlayer(p)
        call QuestMessageBJ( f, bj_QUESTMESSAGE_ALWAYSHINT, &quot;It would seem that player has just left the game...&quot; )
        set DuelInProgress = false
        call DestroyForce(f)
        set f = null
        return false
    endif
    call DialogClear( TakeDialog )
    set DuelPlayers[2] = take
    set g = GetUnitsOfPlayerMatching(p, Condition(function Filter_Heroes))
    set u = FirstOfGroup(g)
    call DialogSetMessage( TakeDialog, &quot;A duel offer from&quot;+ GetPlayerName(p) + &quot; - &quot; + GetUnitName(u) +  &quot; (&quot; + I2S(GetHeroLevel(u)) + &quot;)&quot;)
    set DuelButtons[1] = DialogAddButton( TakeDialog, &quot;(|cff00ccffY|r) I accept &quot;, &#039;Y&#039;)
    set DuelButtons[2] = DialogAddButton( TakeDialog, &quot;(|cff00ccffN|r) No thanks&quot;, &#039;N&#039;)
    call DialogDisplay( take, TakeDialog, true)
    
    if GetPlayerController(DuelPlayers[2])==MAP_CONTROL_USER then
        call TimerStart(AFK,AFK_Timeout,false,function Decline)
    endif
    
    // Acceptance AI
    if DuelAcceptanceAI and GetPlayerController(DuelPlayers[2])==MAP_CONTROL_COMPUTER then
        set g = GetUnitsOfPlayerMatching(DuelPlayers[1], Condition(function Filter_Heroes))
        set lvl = GetHeroLevel(FirstOfGroup(g))
        call DestroyGroup(g)
        set g = GetUnitsOfPlayerMatching(DuelPlayers[2], Condition(function Filter_Heroes))
        set u = FirstOfGroup(g)
        if lvl&lt;=GetHeroLevel(u)+2 and GetRandomReal(0.,1.)&lt;=.75 then
            call Accept()
        else
            call Decline()
        endif
    endif
    
    call DestroyGroup(g)
    set g = null
    set u = null
    return false
endfunction

// Try to duel someone
private function MakeList takes nothing returns nothing
    local group g
    local unit u
    
    if GetPlayerSlotState(GetEnumPlayer()) == PLAYER_SLOT_STATE_PLAYING and IsPlayerEnemy(GetEnumPlayer(),GetTriggerPlayer()) then
        set g = GetUnitsOfPlayerMatching(GetEnumPlayer(), Condition(function Filter_Heroes))
        set u = FirstOfGroup(g)
        set I = I + 1
        set DuelButtons[GetPlayerId(GetEnumPlayer())] = DialogAddButton( DuelDialog, &quot;(|cff00ccff&quot; + I2S(I - &#039;0&#039;) + &quot;|r) &quot; + GetPlayerName(GetEnumPlayer()) + &quot; - &quot; + GetUnitName(u) + &quot; (&quot; + I2S(GetHeroLevel(u)) + &quot;)&quot;, I)
        call DestroyGroup(g)
        set g = null
        set u = null
    endif
endfunction
private function Go takes nothing returns boolean
    local force f
    local player p = GetTriggerPlayer()
    local integer id = GetPlayerId(p)
    
    if DuelInProgress then
        set f = GetForceOfPlayer(p)
        call QuestMessageBJ( f, bj_QUESTMESSAGE_ALWAYSHINT, &quot;Sorry, a dueling demand is currently in progress.
Please try again later.&quot;)
        call DestroyForce(f)
        set f = null
        return false
    endif
    
    set DuelInProgress = true
    call DialogClear( DuelDialog )
    call DialogSetMessage( DuelDialog, &quot;Which player would you like to duel with?&quot; )
    set I = &#039;0&#039;
    call ForForce(bj_FORCE_ALL_PLAYERS, function MakeList)
    set DuelButtons[15] = DialogAddButton(DuelDialog, &quot;|cff00ccffC|rancel&quot;, &#039;C&#039;)
    call DialogDisplay(p, DuelDialog, true)
    set DuelPlayers[1] = p
    
    call TimerStart(AFK,AFK_Timeout,false,function Timeout)

    return false
endfunction

private function Init takes nothing returns nothing
    call TriggerAddCondition(Trig,Condition(function Go))
    set DuelDialog = DialogCreate()
    set TakeDialog = DialogCreate()
    call TriggerRegisterDialogEvent(Offer,DuelDialog)
    call TriggerAddCondition(Offer,Condition(function Offered))
    call TriggerRegisterDialogEvent(Take,TakeDialog)
    call TriggerAddCondition(Take,Condition(function TakeOffer))
endfunction

endscope</i>


Credits to Acehart for originally writing this system in GUI for Hulks Heroes. I re-wrote it in vJASS, optimized it, and made it configurable.

Broken down into multiple trigger dividers for easier readability.

To come:
  • Team Duels

Changelog:

1.10
  • Added a message when not enough players are around to force a duel.
  • Fixed the map not saving bug
  • Minor optimizations
  • Fixed a bug where it would always update camera bounds even if turned off
  • Fixed afk player bug
  • Altered the duel acceptance AI a bit
 

Attachments

  • Duel Engine 1.10 - emjlr3.w3x
    59.9 KB · Views: 467

Charapanga

New Member
Reaction score
46
Hey! this is awsome! I wanna attach this to my map, but it's a hero defense, with duels between waves, since all of them are allies, how would i make them enemies? for that period of time?
 

emjlr3

Change can be a good thing
Reaction score
395
i assume you trigger wave ending and starting - simply adjust alliances during this time
 

emjlr3

Change can be a good thing
Reaction score
395
exactly as it states...
 

Sim

Forum Administrator
Staff member
Reaction score
534
Reviving this. :)

Using camera bounds seems to glitch the map view a lot. Especially the minimap, which gets all screwed up. :p

Oh and, most importantly, in the newest version of NewGen WE it can't save because of the line

call DuelEngineInit_Init()

within the Config trigger. Not even in the demo map. :p
 

WolfieeifloW

WEHZ Helper
Reaction score
372
Also, I'm not sure if this system does it or not.
But in DotA's old swap system they used dialogs.
Only problem is if someone lags ("Waiting for players" screen) or something while the dialog box is up, the dialog gets taken off the screen.
 

Sim

Forum Administrator
Staff member
Reaction score
534
Well, after extensive (If we could say so...) testing I've come to this conclusion:

When you modify the script within the 4 main triggers, you cannot save unless you close WE (NewGen) and re-open it.

Also, sometimes, modifying the script causes the Post Duel Sample to screw hard. :p In that case, it needs to be deleted and re-inserted as its default was.

Odd. :p Script looks fine.
 

Sim

Forum Administrator
Staff member
Reaction score
534
Very well done though, very fun to play :)

I didn't even know that CameraSmoothingFactor existed too. How does it work? as you increase it, the delay augments?

EDIT: Actually, most of the times when you save the map the "Post Duel Sample" doesn't fire at all.
 

brc

New Member
Reaction score
1
how do i save it?. Its the code call DuelEngineInit_Init(). Also what do you write in set PostDuelTrigger = gg_trg_Name? I dont really get it. Anyways, It looks really good. + Rep
 

Sim

Forum Administrator
Staff member
Reaction score
534
> how do i save it?.

That's the current problem :) Seems like it doesn't recognize this. At least, not always...

> Also what do you write in set PostDuelTrigger = gg_trg_Name?

Replace "Name" with the trigger you want to run at the end of a duel. If the trigger is named, for example, "End Duel", it would become "gg_trg_End_Duel".
 

emjlr3

Change can be a good thing
Reaction score
395
any idea whats the deal with saving?
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top