1. Are you planning to upload your awesome map to Hive? Please review the rules here.
    Dismiss Notice
  2. Head to the 33rd Modeling Contest Poll and drink to your heart's desire.
    Dismiss Notice
  3. Choose your means of doom in the 17th Mini Mapping Contest Poll.
    Dismiss Notice
  4. A slave to two rhythms, the 22nd Terraining Contest is here.
    Dismiss Notice
  5. The heavens smile on the old faithful. The 16th Techtree Contest has begun.
    Dismiss Notice
  6. The die is cast - the 6th Melee Mapping Contest results have been announced. Onward to the Hive Cup!
    Dismiss Notice
  7. The glory of the 20th Icon Contest is yours for the taking!
    Dismiss Notice
  8. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Trigger Viewer

Fighting for Azeroth 3D v110.w3x
Variables
Libs
Lib AI
Lib Camera
Lib Char
Lib Controls
Lib Fight
Lib Hints
Lib Menu
Lib Modes
Lib Movelist
Lib Multiboard
Lib Shade
Lib Sound
Lib Terrain hide
AddTerr other
Character data
common frames
---- specific ----
Archer
Blademaster
Butcher
Deathp
Demoness
Felg
Illidan
Kael
Maiev
Naga
Panda
Shaman
Skele
Sorceress
Tauren
Templar
Titan
Trog
Vamp
Villager
Misc
Initialization
peasant anims
color cheat
Settings, Main, Misc
//TESH.scrollpos=109
//TESH.alwaysfold=0
include "cj_types.j"
library LibSettings
    // hashtable keys
    constant int HASH_CHAR_ID = 100
    constant int HASH_CHAR_NAME = 101
    
    // primary
    constant int PLAYERS = 6
    
    // round and camera stuff
    constant int INTRO_TIME = 90
    constant real MIN_CAM_DIST = 140
    constant int ROUND_END_TIME = 64 // frames
    constant int DEF_ROUNDTIME = 120// 60
    
    // Life and mana
    constant int MAX_LIFE = 1000
    constant int MAX_MANA = 1000
    constant int START_MANA = 333
    constant int MANA_FLASH = 10
    constant int GREAT_LIFE = MAX_LIFE / 10
    
    // Slow motion and game speed
    constant real SLOW_MOTION_RANGE = 200
    constant int SLOW_MOTION_HP = MAX_LIFE/2
    constant int SLOW_MOTION_SPEED = 24
    constant int DEFAULT_SPEED = 3
    constant real frame = 0.01 * DEFAULT_SPEED
    
    // states common for all characters
    constant int STATE_NEUTRAL = 0
    constant int STATE_BACK_STEP = 1
    constant int STATE_FORTH_STEP = 2
    constant int STATE_UP_STEP = 3
    constant int STATE_DOWN_STEP = 4
    constant int STATE_JUMP_START = 5
    constant int STATE_RUNNING = 6
    constant int STATE_BLOCK = 7
    constant int STATE_BLOCK_RECOVERY = 8
    constant int STATE_HURT=9
    constant int STATE_STAND_UP = 10
    constant int STATE_ROLL = 11
    constant int STATE_SIDEROLL = 12
    constant int STATE_HIT_THROW = 13
    constant int STATE_BREAK_THROW = 14
    constant int STATE_DEAD = 15
    constant int STATE_VICTORY = 16
    constant int STATE_FALLING = 17 // knockdown
    constant int STATE_INTRO = 18
    constant int STATE_FROZEN = 20
    constant int STATE_RAGEART = 21
    constant int STATE_WHILE_STANDING = 22
    
    constant int THROW_REACTION_TIME = 20
    
    // Rage
    constant int RAGE_TRESHOLD = MAX_LIFE / 5
    constant string RAGE_EFFECT = "Abilities\\Spells\\Orc\\Bloodlust\\BloodlustTarget.mdl"
    
    // Blood and screams
    constant int BLOOD_TIME = 26
    constant int BLOOD_THRESHOLD = MAX_LIFE/10
    constant int PAIN_THRESHOLD = MAX_LIFE/7
    constant real HURT_TIME = .6 // red bar
    
    // Juggle reduction and other balance stuff
    constant real JUGGLE_FACTOR = 0.12
    constant int MAX_JUGGLE = 6
    constant real COUNTERHIT_DAMAGE = 1.25
    constant real BLOCKED_KNOCKBACK = .7
    constant int ARMOR_PAUSE = 12
    
    // physics
    constant real BACKSTAB_ANGLE = 90
    constant real BUMP_RANGE = 65
    constant real BOUNCE_MULTIPLIER = .4
    constant real FRICTION = .95
    constant real G_FORCE = -frame*1750
    constant real G_FORCE_JUGGLE = G_FORCE * .9
    constant real BOUNCE_TRESHOLD = -320 // the falling speed to bounce off the floor
    
    // special effects
    constant real EXPLOD_TICK = 0.05
    constant int BLOCK_EXPLOD_TIME = 30
    
endlibrary

library MAIN initializer Init uses LibSettings
    hashtable hash = InitHashtable()
    bool array here
    bool array ai
    int TEMP_ARENA
    multiboard array mb
    bool array Key[PLAYERS][4] // arrow is pressed
    
    real AngleDif (real a1, real a2) // thanks internet
    {
        //return 180 - RAbsBJ(RAbsBJ(a1 - a2) - 180); 
        real d=a1-a2
        loop; exitwhen(d>=-180); d+=360; endloop
        loop; exitwhen(d<=180); d-=360; endloop
        return d
    }
    
    bool AngleBetween(real n, real a, real b) { // thanks internet
        n = ModuloReal((360 + (ModuloReal(n, 360))), 360)
        a = ModuloReal((3600000 + a), 360)
        b = ModuloReal((3600000 + b), 360)

        if (a < b)
            {return a <= n && n <= b}
        return a <= n || n <= b
    }
    
    void InitBoard (int i) // mb uses fight, and i need this in libfight
    {
            if true//here[i] && !ai[i]
            {
                if GetHandleId(mb[i])>0 {
                    MultiboardClear(mb[i]); DestroyMultiboard(mb[i])
                }
                mb[i] = CreateMultiboardBJ(3, 3, "")
                MultiboardSetItemWidthBJ(mb[i],0,0,36)
                MultiboardSetItemWidthBJ(mb[i],2,0,7)
                MultiboardSetItemsStyle(mb[i], true, false)
                MultiboardDisplay(mb[i], false)
            }
    }
    
    int GetCharIndex(int rawcode) {return LoadInteger(hash,rawcode,HASH_CHAR_ID)}

    void RefreshPlayers()
    {
        int i; for(i=0;i<PLAYERS;i++)
            {
                here[i] = GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING
                ai[i] = GetPlayerController(Player(i)) == MAP_CONTROL_COMPUTER
            }
    }
    
    private void Init()
    {
        //hash = InitHashtable()
        string s=""
        s = " "
    }
endlibrary

library LibMisc initializer Init uses MAIN
    constant int CHARS_X = 8
    constant int CHARS_Z = 4
    private location temp_zloc
    string array pcolor
    
    effect array expl
    real array extime
    int array exta
    int excount
    
    lightning array lightn
    real array ligtime
    int array liga
    real array ligdh1
    real array ligdh2
    unit array ligt1
    unit array ligt2
    int ligcount
    
    int array nameindex // char.selection opponent; if less than 0, hasn't selected a hero yet
    
    define GetPlayerName(p) = {pcolor[GetPlayerId(p)]+GetPla##yerName(p)+"|r"}
    
    void Explod(int a, unit u, string path, string att, int time)
    {
        expl[excount] = AddSpecialEffectTarget(path, u, att)
        extime[excount] = time*frame
        exta[excount] = a
        excount++
    }
    
    void ExplodPt(int a, real x, real y, string path, int time)
    {
        expl[excount] = AddSpecialEffect(path, x, y)
        extime[excount] = time*frame
        exta[excount] = a
        excount++
    }
    
    void Lightning(int a, unit u1, unit u2, real dh1, real dh2, string path, int time)
    {
        ligdh1[ligcount] = dh1; ligt1[ligcount] = u1
        ligdh2[ligcount] = dh2; ligt2[ligcount] = u2
        lightn[ligcount] = AddLightningEx(path, true, GetUnitX(u1), GetUnitY(u1), GetUnitFlyHeight(u1)+dh1, GetUnitX(u2), GetUnitY(u2), GetUnitFlyHeight(u2)+dh2)
        ligtime[ligcount] = time*frame
        liga[ligcount] = a
        ligcount++
    }
    
    tCharData array CData[CHARS_X][CHARS_Z]
    
    real NormAngle(real a) {if a>180 {return a-360} else {return a}}
    
    real GetZ(real x, real y)
    {
        MoveLocation(temp_zloc,x,y)
        return GetLocationZ(temp_zloc)
    }
    
    void onLeave()
    {int p=GetPlayerId(GetTriggerPlayer())
        BJDebugMsg(GetPlayerName(GetTriggerPlayer()) + " left the game!")
        if View[p].battle_index > 0
        {
                Arena[View[p].battle_index].EndFight(1., GetPlayerName(Player(p))+" gave up!")
        }
    }
    
    void InitPlayerColor(int i)
    {
        playercolor pc = GetPlayerColor(Player(i))
        if pc==ConvertPlayerColor(PLAYER_NEUTRAL_AGGRESSIVE)
                {pcolor[i] = "|cff666666"}
            elseif pc==ConvertPlayerColor(0) || pc==ConvertPlayerColor(12)
                {pcolor[i] = "|cffff2222"}
            elseif pc==ConvertPlayerColor(1) || pc==ConvertPlayerColor(13)
                {pcolor[i] = "|cff4040ff"}
            elseif pc==ConvertPlayerColor(2) || pc==ConvertPlayerColor(14)
                {pcolor[i] = "|cff44dddd"}
            elseif pc==ConvertPlayerColor(3) || pc==ConvertPlayerColor(15)
                {pcolor[i] = "|cffbb00bb"}
            elseif pc==ConvertPlayerColor(4) || pc==ConvertPlayerColor(16)
                {pcolor[i] = "|cffffff00"}
            elseif pc==ConvertPlayerColor(5)
                {pcolor[i] = "|cffff8000"}
            elseif pc==ConvertPlayerColor(6) || pc==ConvertPlayerColor(18)
                {pcolor[i] = "|cff19ee19"}
            elseif pc==ConvertPlayerColor(7) || pc==ConvertPlayerColor(17) || pc==ConvertPlayerColor(19)
                {pcolor[i] = "|cffff99ff"}
            elseif pc==ConvertPlayerColor(9)
                {pcolor[i] = "|cff8888ff"}
            elseif pc==ConvertPlayerColor(10) || pc==ConvertPlayerColor(22)
                {pcolor[i] = "|cff008040"}
            elseif pc==ConvertPlayerColor(11) || pc==ConvertPlayerColor(23)
                {pcolor[i] = "|cff666600"}
            else {pcolor[i] = "|cffdddddd"}
    }
    
    void InitPlayerColors()
    {
        int i; for(i=0; i<PLAYERS; i++)
        {
            InitPlayerColor(i)
        }
    }
    
    private void Init()
    {int i
        temp_zloc = Location(0,0)
        excount=0; ligcount=0
        trigger t = CreateTrigger(); TriggerAddAction(t, function onLeave)
        for(i=0; i<PLAYERS; i++) {TriggerRegisterPlayerEvent(t, Player(i), EVENT_PLAYER_LEAVE)}
        pcolor[0] = "|cffff0000"
        pcolor[1] = "|cff2222ff"
        pcolor[2] = "|cff00ffff"
        pcolor[3] = "|cffcc22cc"
        pcolor[4] = "|cffeeee00"
        pcolor[5] = "|cffffaa00"
        pcolor[6] = "|cff00ff00"
        pcolor[7] = "|cffff77ff"
        pcolor[PLAYER_NEUTRAL_AGGRESSIVE] = "|cff777777"
        pcolor[PLAYER_NEUTRAL_PASSIVE] = "|cff777777"
        InitPlayerColors()
    }
    
    int array random_sort
    int random_count
    
    int RandomChar()
    {int i,j
        random_count=0
        for(i=0; i<CHARS_X; i++)
            {
            for(j=0; j<CHARS_Z; j++)
            {
                if CData[i][j].id>0
                {
                    random_sort[random_count++] = CData[i][j].id
                }
            }
        }
        return random_sort[GetRandomInt(0,random_count-1)]
    }
endlibrary
Name Type Is Array Initial Value
terrain group No
u unit No
The AI opponents.
//TESH.scrollpos=30
//TESH.alwaysfold=0
library LibAI initializer Init uses LibControls
    constant bool AI_PEACE = false
    //constant bool AI_PEACE = true

    constant real AI_TICK = 0.12
    private constant int ACTION_NONE = 0
    private constant int ACTION_DUCK = 1
    private constant int ACTION_BLOCK = 2
    private constant int ACTION_BACKDASH = 3
    private constant int ACTION_JUMP = 4
    private constant int ACTION_DASH = 5
    private constant int ACTION_STEP_LEFT = 6
    private constant int ACTION_STEP_RIGHT = 7
   
    private int array DCD
    private int array DTM // fix that pesky crouching bug
   
    private real array time
    private int array action
    private void FullReset(int i) {
        if here[i] && !ai[i] {return}
        ResetCmds(i)
        Key[i][KEY_DOWN] = false; nKey[i][KEY_DOWN] = 0
        Key[i][KEY_UP] = false; nKey[i][KEY_UP] = 0
        Key[i][KEY_RIGHT] = false; nKey[i][KEY_RIGHT] = 0
        Key[i][KEY_LEFT] = false; nKey[i][KEY_LEFT] = 0
        time[i]=0
    }
    private void Duck(int i) {action[i] = ACTION_DUCK}
    private void Block(int i) {action[i] = ACTION_BLOCK}
    private void Backdash(int i) {action[i] = ACTION_BACKDASH}
    private void StepLeft(int i) {action[i] = ACTION_STEP_LEFT}
    private void StepRight(int i) {action[i] = ACTION_STEP_RIGHT}
    private void Dash(int i) {action[i] = ACTION_DASH}
    private void Jump(int i) {action[i] = ACTION_JUMP; time[i]=11*frame}
    void AI_Straight(tArena a, real angle, int i, int t, real range, string height, string tp, int diff) // random action against a straight attack
    {
        if a.practise {return}
       
        FullReset(i)
       
        //  More challenge; without overriding this function's arguments everywhere, where diff is usually 40
        diff = 22
       
        if GetRandomInt(0,100) < diff || a.CharDist() > range {return}
        int j=1; if a.pl[j].owner != i {j=0}
       
        time[i] = t*frame
       
        if a.pl[1].Backstab(angle) {Backdash(i)}
       
        elseif height=="h"
        {
            if GetRandomInt(0,100) > 50 {Duck(i)} elseif tp!="l" && tp!="!" {Block(i)} else {Backdash(i)}
        }
        elseif height=="m" || height=="s"
        {
            if GetRandomInt(0,100) > 50 && tp!="h" && tp!="!" {Duck(i)} elseif tp!="l" && tp!="!" {Block(i)} else {Backdash(i)}
        }
        elseif height=="l"
        {
            if GetRandomInt(0,100) > 50 && t>=11 {Jump(i)} elseif GetRandomInt(0,100) > 50 && tp!="h" && tp!="!" {Duck(i)} elseif tp!="l" && tp!="!" {Block(i)} else {Backdash(i)}
        }
       
        if GetRandomInt(0,100) < 10 {StepLeft(i)}
        elseif GetRandomInt(0,100) < 11 {StepRight(i)}
    }
    private void Tick()
    {
        RefreshPlayers()
        int i; for(i=0;i<PLAYERS; i++)
        {
            if (ai[i] || !here[i]) && View[i].battle_index!=0
            {
                tArena a; if View[i].battle_index!=0 {a=Arena[View[i].battle_index]}
               
                    if time[i]>0 {time[i] -= AI_TICK}
                    else {FullReset(i); action[i]=ACTION_NONE}
                   
                    if Key[i][KEY_DOWN] {
                        DTM[i]++; if DTM[i]>=12 {DTM[i]=0; DCD[i]=15}
                    }
                   
                    //FullReset(i) //wtf mate stahp
                   
                    real dist = Arena[View[i].battle_index].CharDist()
                    tChar pt = Arena[View[i].battle_index].pl[1]
                    if pt.owner != i {pt = Arena[View[i].battle_index].pl[0]}
                   
                    if pt.state==STATE_NEUTRAL
                    {
                        if pt.statetype=="ld"
                        {
                            //Command[i][CMD_W] = CMD_TIME
                            //Key[i][KEY_DOWN] = true; nKey[i][KEY_DOWN] = 1.
                            if GetRandomInt(0,100) < 20 {Key[i][KEY_LEFT] = true}
                            elseif GetRandomInt(0,100) < 20 {Key[i][KEY_DOWN] = true}
                            elseif GetRandomInt(0,100) < 20 {Key[i][KEY_UP] = true}
                            elseif GetRandomInt(0,100) < 20 {Key[i][KEY_RIGHT] = true}
                            else {Command[i][CMD_W] = CMD_TIME}
                            time[i] = 1.
                        }
                        elseif time[i]>0
                        {
                            int ac=action[i]
                            if ac==ACTION_NONE {FullReset(i)}
                            elseif ac==ACTION_DUCK {Key[i][KEY_DOWN] = true}
                            elseif ac==ACTION_BLOCK {Key[i][KEY_LEFT] = true}
                            elseif ac==ACTION_BACKDASH {nKey[i][KEY_LEFT] = 1}
                            elseif ac==ACTION_DASH {nKey[i][KEY_RIGHT] = 1; time[i] = 0.5}
                            elseif ac==ACTION_JUMP {Key[i][KEY_UP] = true}
                            elseif ac==ACTION_STEP_RIGHT {nKey[i][KEY_DOWN] = 1}
                            elseif ac==ACTION_STEP_LEFT {nKey[i][KEY_UP] = 1}
                        }
                        elseif /*GetRandomInt(0,100) < 67 && */!AI_PEACE && !a.practise
                        {
                            FullReset(i)
                           
                            if dist > 200 && GetRandomInt(0,100) < 30 {Dash(i); time[i] = 1.}
                           
                            if GetRandomInt(0,100) < 20 {nKey[i][KEY_DOWN] = .5; nKey[i][KEY_RIGHT] = 1}
                            if GetRandomInt(0,100) < 17 {Command[i][CMD_A] = CMD_TIME; time[i] = 1.}
                            if GetRandomInt(0,100) < 17 {Command[i][CMD_S] = CMD_TIME; time[i] = 1.}
                            if GetRandomInt(0,100) < 17 {Command[i][CMD_D] = CMD_TIME; time[i] = 1.}
                            if GetRandomInt(0,100) < 17 {Command[i][CMD_W] = CMD_TIME; time[i] = 1.}
                           
                            Key[i][KEY_DOWN] = GetRandomInt(0,100) < 35
                            if GetRandomInt(0,100) < 10 {StepLeft(i); time[i] = 1.}
                            elseif GetRandomInt(0,100) < 11 {StepRight(i); time[i] = 1.}
                        }
                    }
                    elseif pt.state==STATE_HIT_THROW && !a.practise
                    {
                        if GetRandomInt(0,100) < 30 {Command[i][CMD_W] = CMD_TIME; time[i] = 1.} else {Command[i][CMD_A] = CMD_TIME; time[i] = 1.} // fail is inputing the wrong command
                    }
                   
                    if DCD[i] > 0
                    {
                        DCD[i]--; Key[i][KEY_DOWN]=false
                    }
            }
            else {action[i]=ACTION_NONE; if (ai[i] || !here[i]) {FullReset(i)}}
        }
    }
    private void Init()
    {int i
        for(i=0;i<PLAYERS; i++)
        {
            time[i]=0; action[i]=ACTION_NONE; DCD[i]=0; DTM[i]=0
        }
        TimerStart(CreateTimer(),AI_TICK,true,function Tick)
    }
endlibrary

function InitTrig_Lib_AI takes nothing returns nothing
endfunction
Each player has own camera settings. Also the Flashy text
//TESH.scrollpos=0
//TESH.alwaysfold=0
library LibCamera initializer Init uses MAIN, LibMisc

    struct tPlayerView
        int index
        int battle_index // 0 - out, >0- in combat, <0 - watching
        unit dummy
        // d - delta per second, p - previous (smooth movement)
        real Range, dRange, pRange
        real Angle, dAngle, pAngle
        real Facing, dFacing, pFacing
        real X, Y, Z, pX, pY, pZ, dX, dY, dZ
        int time
       
        void Rotate(real r, real a, real f, real length)
        {
            if length < 1 {length=1}
            dRange = r/length;            dAngle = a/length;            dFacing = f/length
            time=R2I(.1+length)
        }
       
        void Reset(real r, real a, real f, bool instantly)
        {
            Range = r;             Angle = a;             Facing = f
            dRange = 0;            dAngle = 0;            dFacing = 0
            dX=0; dY=0; dZ=0
            if instantly {pRange = r; pAngle = a; pFacing = f}
            time=0
        }
        void Move(real x, real y, real z, bool instantly)
        {
            X=x; Y=y; Z=z; dX=0; dY=0; dZ=0
            if instantly {pX=x; pY=y; pZ=z}
        }
        void Velocity(real x, real y, real z)
        {
            dX=x; dY=y; dZ=z
        }
        void ATick()
        {  
                pX += frame*dX
                pY += frame*dY
                pZ += frame*dZ          
            if time>0
            {
                time--
                pRange += dRange
                pAngle += dAngle
                pFacing += dFacing
            }
            else
            {
                loop //eh
                    exitwhen (RAbsBJ(Facing -pFacing) <= 180)
                    if pFacing -Facing>180 {pFacing-=360}
                    if Facing -pFacing>180 {Facing-=360}
                endloop
                pRange=0.95*pRange + 0.05*Range
                pAngle=0.95*pAngle + 0.05*Angle
                pFacing=0.94*pFacing + 0.06*Facing
                pX=0.87*pX + 0.13*X
                pY=0.87*pY + 0.13*Y
                pZ=0.87*pZ + 0.13*Z
                //BJDebugMsg(I2S(R2I(pRange))+" "+I2S(R2I(pAngle))+" "+I2S(R2I(pFacing)))
            }
           
            SetUnitPosition(dummy,pX,pY)
        }
        //
        static thistype Create(int i)
        {
            thistype this = thistype.allocate()
            this.index = i
            this.battle_index = 0
            this.Reset(0,0,0,true)
            this.Move(0,0,0,true)
            this.dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),'h000',this.pX,this.pY,0)
            if GetLocalPlayer()==Player(i) {SetCameraTargetController(this.dummy,0,0,false)}
            return this
        }
    endstruct
   
    int LocalArena() {return IAbsBJ(View[GetPlayerId(GetLocalPlayer())].battle_index)}
   
    void SwayEnd()
    {
        timer tm=GetExpiredTimer()
        if LocalArena() == LoadInteger(hash,GetHandleId(tm),0)
        {
            CameraSetSourceNoise(0, 0)
            CameraSetTargetNoise(0, 0)
        }
        FlushChildHashtable(hash,GetHandleId(tm))
        DestroyTimer(tm)
        tm=null
    }
   
    tPlayerView array View
    private void Tick()
    {
        int i; for(i=0;i<PLAYERS;i++)
        {
            if here[i] && !ai[i]
            {
                View[i].ATick()
            }
        }
        int j=GetPlayerId(GetLocalPlayer())
        SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK,    View[j].pAngle,0)
        SetCameraField(CAMERA_FIELD_ZOFFSET,            View[j].pZ,0)
        SetCameraField(CAMERA_FIELD_TARGET_DISTANCE,    View[j].pRange,0)
        SetCameraField(CAMERA_FIELD_ROTATION,           View[j].pFacing,0)
    }
   
        void FlashyTextTick ()
        {
            timer tm=GetExpiredTimer(); int h=GetHandleId(tm)
            texttag tt = LoadTextTagHandle(hash,h,0)
            real maxtime = LoadReal(hash,h,1)
            real time = LoadReal(hash,h,2)
            unit u=LoadUnitHandle(hash,h,4)
            real perc = time/maxtime
            real z=LoadReal(hash,h,5); SaveReal(hash,h,5,z+1)
            if perc < .25 || perc > .75 {perc = .5}
            elseif perc < .5 {perc = perc * 2}
            else {perc = 2*(1. - perc)}
            SetTextTagText(tt,LoadStr(hash,h,3), 20 * perc * 0.023 / 10)
            SetTextTagPos(tt,GetUnitX(u),GetUnitY(u),z)
            SaveReal(hash,h,2,time+.04)
            if time > maxtime
            {
                FlushChildHashtable(hash,h)
                DestroyTimer(tm); DestroyTextTag(tt)
            }
            tt=null; tm=null; u=null
        }
       
        void FlashyText(int a, unit mdl, real x, real y, real z, string text, real time)
        {
            texttag tt = CreateTextTag()
            timer tm = CreateTimer(); int h=GetHandleId(tm)
            if IAbsBJ(View[GetPlayerId(GetLocalPlayer())].battle_index) != a {text=""}
            SaveTextTagHandle(hash,h,0,tt)
            SaveReal(hash,h,1,time)
            SaveReal(hash,h,2,0)
            SaveStr(hash,h,3,text)
            SaveUnitHandle(hash,h,4,mdl)
            SaveReal(hash,h,5,z)
            SetTextTagText(tt, text, 0)
            SetTextTagPos(tt, x, y, z)
            SetTextTagVisibility(tt, true)
            SetTextTagVelocity(tt, 0.0, 0.0)
            SetTextTagFadepoint(tt, 2.5)
            SetTextTagLifespan(tt, 4.0)
            SetTextTagPermanent(tt, false)
            TimerStart(tm,0.04,true, function FlashyTextTick)
            tt=null; tm=null
        }
       
    private void DelayedInit()
    {
        RefreshPlayers()
       
        int i; for(i=0;i<PLAYERS;i++)
        {
            if true//here[i] && !ai[i]
            {
                View[i] = tPlayerView.Create(i)
            }
        }
    }
   
    private void Init()
    {
        TimerStart(CreateTimer(), frame, true, function Tick)
        TimerStart(CreateTimer(), 0, false, function DelayedInit)
    }
endlibrary

//===========================================================================
function InitTrig_Lib_Camera takes nothing returns nothing
endfunction
The Character class
//TESH.scrollpos=231
//TESH.alwaysfold=0
library LibChar uses MAIN, LibMisc, LibCamera, LibSound, LibMovelist    
    tChar PT
    struct tChar
        unit mdl
        // physics
        real x,y,z,face     // position
        real sx,sy,sz       // speed
        // states and special properties
        string name         // used to call functions, like framedata
        int state, fr, slowtime, falldmg, juggles, poison, wallsplat
        int kndbounce
        string statetype, lasthit
        bool noclip, counterhit, unbreakable, parry, didhit, immortal
        real targetx, targety
        int armor_pause
        // stats
        int life, mana, manaflash, hurt
        real hurt_time
        // RAGE
        effect rageart
        bool rage_used, rage_active
        // important stuff
        int wins, owner, id, index, arena
        // animations and such
        sound speech
        real timescale
        string anim
        real speed_factor // game speed for animations
       
        // Scales rageart damage depending on hp
        int RageDmg(int dmg)
        {
            real d = 1. - 0.67 * life/RAGE_TRESHOLD
            if d>1 {d=1} elseif d<0.35 {d=0.35}
            return R2I(dmg*d)
        }
       
        // Character height for collisions, depending on state type
        real GetMaxZ()
        {
            if statetype=="c" {return z+45} elseif statetype=="ld" {return z+20}
            return z+90
        }
       
        // Moves slightly towards that point
        void SmoothShift (real nx, real ny, real nz)
        {
            x=0.86*x + nx*0.14
            y=0.86*y + ny*0.14
            z=0.86*z + nz*0.14
        }
       
        // Freezes the character in a block of ice. This state stops knockback and is unbreakable.
        void Freeze(int time)
        {
            if statetype=="a" {return}
           
            state = STATE_FROZEN
            fr = time
            unbreakable = true
            SetUnitVertexColor(mdl, 50, 200, 255, 200)
            Explod(arena, mdl, "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathTargetArt.mdl", "origin", time)
        }
       
        // Checks if there is enough mana, and then spends it.
        bool UseBars(int val)
        {
            val*=MANA_BAR_LENGTH
            if mana >= val {
                manaflash = MANA_FLASH
                mana -= val
                return true
            }
            return false
        }
       
        // Checks if the attack is coming at the character from the back.
        bool Backstab(real angle)
        {
            return RAbsBJ(AngleDif(face, angle+180)) >= BACKSTAB_ANGLE
            //return RAbsBJ(NormAngle(angle+180) - NormAngle(face)) >= BACKSTAB_ANGLE
        }
       
        // Changes animation speed
        void Scale(real scale) {SetUnitTimeScale(mdl, scale*speed_factor); timescale = scale}
       
        // Sets current animation and it's speed
        void Anim(string a, real scale) {SetUnitAnimation(mdl, a); anim = a; Scale(scale)}
       
        // Adds or reduces mana
        void PowerAdd(int val) {mana += val; if mana>MAX_MANA {mana=MAX_MANA} elseif mana<0 {mana=0}}
       
        // Makes the character only visible to the owner
        void Invisible()
        {
            int a = 0; if GetLocalPlayer() == Player(owner) {a=120}
            SetUnitVertexColor(mdl, 255, 255, 255, a)
        }
       
        // Makes the character visible to everyone
        void Visible() {SetUnitVertexColor(mdl, 255, 255, 255, 255)}
       
        // Puts the character into the basic state of the current state type
        void Neutral()
        {
            state=STATE_NEUTRAL; fr=0; sx=0; sy=0; sz=0
            wallsplat = 0
            SetUnitVertexColor(mdl, 255, 255, 255, 255)
            falldmg=0; armor_pause=0
            if statetype!="ld" {noclip = false}
           
            if statetype=="s" {Anim("Stand Ready", 1.); juggles=0; z=0}
            elseif statetype=="c" {Anim("Stand Alternate", 1.); juggles=0; z=0}
            elseif statetype=="a" {Anim("Stand Swim", 1.)}
            elseif statetype=="ld" {Anim("Decay", 1.); z=0}
           
            counterhit=false
            unbreakable=false
            didhit=false
            parry=false
            kndbounce=0
           
            if life<1
            {
                Anim("Death", 1.); state=STATE_DEAD; fr=999
            }
        }
       
        // Checks if the character lies dead on the floor
        bool Dead() {if state==STATE_DEAD {return true}; return life<=0 && z<1 && (sz>-1 && sz<1)}
       
        // Starts the winning animation
        bool WinPosture()
        {
            bool val = anim!="Stand Victory"
            sx=0; sy=0; sz=0
            if statetype=="s" || statetype=="c" || statetype=="ld"
            {
                sx=0; sy=0
                if state < 100 && state!=STATE_VICTORY
                {
                    statetype="s"
                    state=STATE_VICTORY
                    Anim("Stand Victory", 1.)
                    WinSounds(arena, mdl)
                }
            }
            return val
        }
       
        // launches the character up in the air
        void Launch(real velz) {if unbreakable {return}; if z<20 {z=20}; statetype="a"; state=STATE_HURT; sz = velz}
       
        // checks if the character is lying on the ground
        bool Ground() {return statetype=="ld" || state==STATE_FALLING}
       
        // restores some hp
        void Heal(int amount) {life += amount; if life > MAX_LIFE {life=MAX_LIFE}; hurt -= amount; if hurt < 0 {hurt=0; hurt_time=0}}
       
        // Speech, interrupted by getting hit
        void PlaySound (string soundName)
        {
            /*speech = CreateSound(soundName, false, false, true, 12700, 12700, "")
            StartSound(speech)
            KillSoundWhenDone(speech)
            AttachSoundToUnit(speech, mdl)
            if LocalArena() != arena
            {
                SetSoundVolume(speech, 0)
            }*/

            PlaySoundOnUnit(arena, soundName, mdl)
        }
       
        // reduces hp, without knockback or state changes
        void Hurt(int amount)
        {
            if amount > life {amount=life}; hurt+=amount; life -= amount; hurt_time = HURT_TIME
            if immortal && life < 1 {life=1} // zero deaths
            StopSound(speech, false, false) // stop talking!
            // blood and guts
            if amount >= BLOOD_THRESHOLD
            {
                string path="", point="origin"
               
                  if IsUnitType(mdl, UNIT_TYPE_UNDEAD) {
                    if GetRandomInt(0,100)>50
                        {path="Objects\\Spawnmodels\\Undead\\UndeadBlood\\UndeadBloodCryptFiend.mdl"}
                        else{path="Objects\\Spawnmodels\\Naga\\NagaBlood\\NagaBloodWindserpent.mdl"; point="chest"}
                    }
                  elseif IsUnitType(mdl, UNIT_TYPE_MECHANICAL) {path="Abilities\\Weapons\\GyroCopter\\GyroCopterImpact.mdl"; point="chest"}
                  else
                  {
                    if GetRandomInt(0,100)>50 {path="Objects\\Spawnmodels\\Human\\HumanBlood\\HeroBloodElfBlood.mdl"}
                    elseif GetRandomInt(0,100)>50 {path="Objects\\Spawnmodels\\Human\\HumanBlood\\HumanBloodMortarTeam.mdl"}
                    else {path="Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"}
                  }
                 
                Explod(arena, mdl, path, point, BLOOD_TIME)
            }
            // RAGE
            bool enrage=false
            if life <= RAGE_TRESHOLD && !rage_used && !rage_active
            {
                rage_active = true; enrage=true
                rageart = AddSpecialEffectTarget(RAGE_EFFECT, mdl, "head")
            }
            // sounds on hit
            if amount > 1
            {
                if amount >= PAIN_THRESHOLD || life <= 0 || enrage {PainScream(arena, mdl)}
                else {HitSound(arena, mdl, amount)}
            }
        }
       
        // checks if the character has RAGE, and spends it
        bool UseRage()
        {
            if rage_active
            {
                DestroyEffect(rageart)
                rage_active = false
                rage_used = true
                return true
            }
            return false
        }
       
        // main on-hit function
        bool Damage(real angle, int amount, real velx, real vely, real velz, int pause, int blocktime, string tp)
        {   bool blk=false, specthrow=false
            if counterhit {counterhit=false; amount=R2I(amount * COUNTERHIT_DAMAGE)}
           
            // a fix added later because of how some code below works
            if tp=="t" && state==STATE_FALLING {state=STATE_HURT}
           
            // while in rage, all damage taken is halved; throws don't interrupt it unlike other unbreakables
            if state == STATE_RAGEART
            {
                amount /= 2
            }
            else
            {
                // throws counter unbreakables
                if tp=="t" {unbreakable=false; if parry {parry=false; specthrow=true}}
                elseif unbreakable {armor_pause = ARMOR_PAUSE; amount = (3*amount)/4; SetUnitTimeScale(mdl, 0); SetUnitVertexColor(mdl, 100,100,100,255)}
               
                // check for blocks
                if state==STATE_BLOCK && statetype=="s" && tp != "!" && tp != "l" {blk=true}
                elseif state==STATE_NEUTRAL && statetype=="c" && tp != "!" && tp != "h" {blk=true}
               
                // but no blocks against throws or attacks from the back
                if Backstab(angle) || tp=="t" {blk=false; parry=false}
               
                // on-block effects
                if blk
                {
                    fr = blocktime; state = STATE_BLOCK_RECOVERY
                    sx = velx*BLOCKED_KNOCKBACK
                    sy = vely*BLOCKED_KNOCKBACK
                    if statetype=="c" {Anim("Stand Defend Alternate", 1.3)}
                    string att="sprite second"; if tp=="h" {att="sprite third"} elseif tp=="l" {att="sprite first"}
                    Explod(arena, mdl, "DefendCaster.mdx", att, BLOCK_EXPLOD_TIME)
                }
            }
           
            // Throw hit indication
            if tp=="t" {Explod(arena, mdl, "Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl", "origin", 32)}
           
            // juggle damage reduction
            int dmg = R2I(amount * (1 - juggles*JUGGLE_FACTOR))
           
            // Attack type indication
            string msg = ""; real hz=0
            if lasthit==" " {msg=" "; hz=25}
            elseif tp=="t" {msg="|cff0000ffThrow|r"; hz = 75}
            elseif tp=="!" {msg="|cffff00ffUnblock!|r"; hz = 50}
            elseif tp=="l" {msg="|cff00ff00Low|r"; hz = 25}
            elseif tp=="m" {msg="|cff00ffffAny|r"; hz = 50}
            elseif tp=="h" && lasthit!="h" {msg="|cffffff00Mid|r"; hz = 50}
            elseif tp=="h" {msg="|cffff0000High|r"; hz = 90}
           
            if !(blk || parry) { msg += (" "+I2S(dmg)+"!") }
            FlashyText(arena, mdl, x, y, hz, msg, 1.11)
           
            // that's it if it was blocked
            if blk {return false}
           
            // now deal damage
            if !parry { // parry is a move that catches an enemy attack and then does something
                Hurt(dmg)
                if juggles < MAX_JUGGLE {juggles++}
            } else {parry=false}
           
            // on-hit state change, knockback and launching
            if !unbreakable || (life<=0 && state!=STATE_RAGEART)
            {
                // juggling and knockdown
                if velz < 5 && statetype == "a" && sz < 100 {sz = 120}
                elseif velz > 0.1 {Launch(velz)} elseif velz < -0.1
                {
                    statetype="c"; Anim("Death", 1.); state=STATE_FALLING
                } elseif statetype!="a" {sz=0}
               
                // put the character in the Hurt state, except knockdown has it's own state
                fr=pause;
                if state!=STATE_FALLING
                {
                    if specthrow {parry=true}
                        elseif tp=="t" {state=STATE_HIT_THROW; fr++}
                            else {state=STATE_HURT}
                   
                    if statetype=="s" {
                        if tp=="l" {Anim("Stand Hit Lumber", 2.)}
                        else {Anim("Stand Hit", 1.5)}
                    }
                    elseif statetype=="a" {Anim("Death", 1.)}
                    elseif statetype=="c" {Anim("Stand Hit Alternate", 1.5)}
                }
               
                sx = velx * (1 + juggles*JUGGLE_FACTOR)
                sy = vely * (1 + juggles*JUGGLE_FACTOR)
            }
           
            // if the attack did hurt and changed state, return true
            return !unbreakable
        }
       
        // Some moves cause daze, it has a special animation and everything
        void Stun(int pause) {state = STATE_HURT; fr = pause; Anim("Stand Hit Slam", 1.)
            Explod(arena, mdl, "Abilities\\Spells\\NightElf\\FaerieFire\\FaerieFireTarget.mdl", "head", pause)}
       
        // physics are done 100/sec. regardless of game speed, otherwise it looks like crap during slowmo
        void PhysTick(real spd)
        {
            SetUnitPosition(mdl, x, y)
            SetUnitFacing(mdl, face)
            SetUnitFlyHeight(mdl, z, 0)
            real m = 1; if slowtime > 0 {m=.5}
           
            // GROUND SPEED
            x += sx*frame*spd*m; y += sy*frame*spd*m
           
            // FLIGHT
            if statetype=="a" {z += sz*frame*spd}
        }
       
        // Starts framedata
        void Tick()
        {
            PT = this
            if index==0 || armor_pause<=0 {ExecuteFunc(name+"Frames")}
        }
    endstruct
endlibrary

function InitTrig_Lib_Char takes nothing returns nothing
endfunction
All about detecting keys & combinations
//TESH.scrollpos=103
//TESH.alwaysfold=0
library LibControls initializer Init uses MAIN, LibCamera, LibMenu, LibModes
    constant int KEY_LEFT = 0, KEY_RIGHT = 1, KEY_DOWN = 2, KEY_UP = 3
    constant int CMD_A = 0, CMD_S = 1, CMD_D = 2, CMD_W = 3
    constant real KEY_TIME = 1.1 // arrows
    constant real CMD_TIME = 0.9 // wasd
    constant real Ctrl_Tick = .01
    constant real ESCAPE_TIME = 1.4 // cooldown to stop spamming pause
   
    trigger tEsc, tSkill, tLD, tLU, tRD, tRU, tDD, tDU, tUD, tUU
   
    real array Command[PLAYERS][4] // WASD time
    real array nKey[PLAYERS][4] // last press timer
    real array pKey[PLAYERS][4] // previous press timer
   
    void ResetCmds(int p)
    {
        Command[p][CMD_A] = 0
        Command[p][CMD_S] = 0
        Command[p][CMD_D] = 0
        Command[p][CMD_W] = 0
    }
   
    public void ArrowKey(int p, int k, bool press)
    {
        Key[p][k] = press
        if press
        {
            pKey[p][k] = nKey[p][k]
            nKey[p][k] = KEY_TIME
           
            if View[p].battle_index == 0 && duel_sent[p]<0
            {
                MenuKey(p,k)
            }
        }
    }
   
    private unit array order_dummy
    private unit array order_dummy2
   
    private void onOrder()
    {
        unit u=GetTriggerUnit()
        int p = GetPlayerId(GetOwningPlayer(u))
        string s = OrderId2String(GetIssuedOrderId())
        int id=GetIssuedOrderId()
       
        if s=="stop" || s=="holdposition" {return}
            //BJDebugMsg(I2S(GetIssuedOrderId()))
            //BJDebugMsg(s)
       
        if u == order_dummy[p] {SelectUnitForPlayerSingle(order_dummy2[p], Player(p))}
            else {SelectUnitForPlayerSingle(order_dummy[p], Player(p))}
       
        if View[p].battle_index == 0
        {
            MenuOrder(p)
        }
        else
        {
            if s=="windwalk" {Command[p][CMD_A] = CMD_TIME}
            if s=="divineshield" {Command[p][CMD_D] = CMD_TIME}
            if s=="berserk" {Command[p][CMD_S] = CMD_TIME}
            if s=="immolation" || s=="unimmolation" || id==1747988790 {Command[p][CMD_W] = CMD_TIME}
        }
        u = null
    }
   
    int array escape_count
    real array escape_reset
    public void onEscape()
    {
        int p=GetPlayerId(GetTriggerPlayer())
        escape_count[p]++
        tArena a = Arena[IAbsBJ(View[p].battle_index)]
        if View[p].battle_index != 0 && a.intro < 0
        {
            if View[p].battle_index<0 {/*View[p].battle_index=0;*/ ShowMenu(p)}
            elseif escape_count[p] >= 3
            {
                // Quit battle
                if View[p].battle_index > 0
                {
                    if !Arena[View[p].battle_index].finish {
                        if Arena[View[p].battle_index].survival
                        {
                            Arena[View[p].battle_index].EndFight(1., GetPlayerName(Player(p))+" quit survival mode")
                        }
                        else
                        {
                            Arena[View[p].battle_index].EndFight(1., GetPlayerName(Player(p))+" gave up!")
                        }
                    }
                }
                else
                {
                    ShowMenu(p)
                }
            }
            elseif View[p].battle_index > 0 && (escape_reset[p] < 0.1 || Arena[View[p].battle_index].practise) && (Arena[View[p].battle_index].movelister < 0 || Arena[View[p].battle_index].movelister == p)
            {
                // pause the game and show Movelist, or unpause
                Arena[View[p].battle_index].movelist = !Arena[View[p].battle_index].movelist
                if Arena[View[p].battle_index].movelist
                {
                        int p2 = Arena[View[p].battle_index].pl[1].owner
                        Arena[View[p].battle_index].SetSpeed(10000)
                        int id = GetUnitTypeId(Arena[View[p].battle_index].pl[0].mdl)
                        int id2 = GetUnitTypeId(Arena[View[p].battle_index].pl[1].mdl)
                        if Arena[View[p].battle_index].pl[0].owner != p
                        {
                            p2 = Arena[View[p].battle_index].pl[0].owner
                            id = GetUnitTypeId(Arena[View[p].battle_index].pl[1].mdl)
                            id2 = GetUnitTypeId(Arena[View[p].battle_index].pl[0].mdl)
                        }
                        PrintMovelist(p, id)
                        PrintMovelist(p2, id2)
                        Arena[View[p].battle_index].movelister = p
                }
                else
                {
                        ClearTextMessages()
                        HideAllMovelistsFor(p)
                        InitBoard(p)
                        Arena[View[p].battle_index].ResetSpeed()
                        Arena[View[p].battle_index].movelister = -1
                }
            }
        }
        else
        {
            if duel_sent[p]>=0 {CancelDuel(p)} elseif nameindex[p] >= 0 {MenuBack(p)} else {ModeDialog(p)}
        }
        if escape_reset[p] < 0.15 {escape_reset[p]=ESCAPE_TIME}
    }
   
    private void Tick()
    {
        int i; for(i=0;i<PLAYERS;i++)
        {
            if escape_reset[i] < ESCAPE_TIME - 1. {escape_count[i]=0}
            if escape_reset[i]>0 {escape_reset[i] -= Ctrl_Tick}
           
            int k; for(k=0;k<4;k++)
            {
                if nKey[i][k]>0 {nKey[i][k] -= Ctrl_Tick}
                if pKey[i][k]>0 {pKey[i][k] -= Ctrl_Tick}
                if Command[i][k]>0 {Command[i][k] -= Ctrl_Tick}
            }
           
            if !IsUnitSelected(order_dummy[i], Player(i)) {SelectUnitForPlayerSingle(order_dummy[i], Player(i))}
        }
    }
   
    private void onArrow()
    {
        int h=GetHandleId(GetTriggeringTrigger())
        ArrowKey(GetPlayerId(GetTriggerPlayer()), LoadInteger(hash,h,0), LoadBoolean(hash,h,1))
    }

    private void DelayedInit()
    {
        RefreshPlayers()
        tEsc=CreateTrigger()
        tSkill=CreateTrigger()
        tLD=CreateTrigger(); tLU=CreateTrigger()
        tRD=CreateTrigger(); tRU=CreateTrigger()
        tDD=CreateTrigger(); tDU=CreateTrigger()
        tUD=CreateTrigger(); tUU=CreateTrigger()
       
        SaveInteger(hash,GetHandleId(tLD),0,KEY_LEFT); SaveBoolean(hash,GetHandleId(tLD),1,true)
        SaveInteger(hash,GetHandleId(tLU),0,KEY_LEFT); SaveBoolean(hash,GetHandleId(tLU),1,false)
        SaveInteger(hash,GetHandleId(tRD),0,KEY_RIGHT); SaveBoolean(hash,GetHandleId(tRD),1,true)
        SaveInteger(hash,GetHandleId(tRU),0,KEY_RIGHT); SaveBoolean(hash,GetHandleId(tRU),1,false)
        SaveInteger(hash,GetHandleId(tUD),0,KEY_UP); SaveBoolean(hash,GetHandleId(tUD),1,true)
        SaveInteger(hash,GetHandleId(tUU),0,KEY_UP); SaveBoolean(hash,GetHandleId(tUU),1,false)
        SaveInteger(hash,GetHandleId(tDD),0,KEY_DOWN); SaveBoolean(hash,GetHandleId(tDD),1,true)
        SaveInteger(hash,GetHandleId(tDU),0,KEY_DOWN); SaveBoolean(hash,GetHandleId(tDU),1,false)
       
        TriggerAddAction(tLD, function onArrow); TriggerAddAction(tLU, function onArrow)
        TriggerAddAction(tRD, function onArrow); TriggerAddAction(tRU, function onArrow)
        TriggerAddAction(tUD, function onArrow); TriggerAddAction(tUU, function onArrow)
        TriggerAddAction(tDD, function onArrow); TriggerAddAction(tDU, function onArrow)
        TriggerAddAction(tSkill, function onOrder)
        TriggerAddAction(tEsc, function onEscape)
       
        int i; for(i=0;i<PLAYERS;i++)
        {
            if here[i] && !ai[i]
            {
                order_dummy[i] = CreateUnit(Player(i),'h001',0,0,0)
                order_dummy2[i] = CreateUnit(Player(i),'h001',0,0,0)
                TriggerRegisterPlayerUnitEvent(tSkill,Player(i),EVENT_PLAYER_UNIT_ISSUED_ORDER,null)
                TriggerRegisterPlayerEvent(tEsc,Player(i),EVENT_PLAYER_END_CINEMATIC)
                TriggerRegisterPlayerEvent(tLD,Player(i),EVENT_PLAYER_ARROW_LEFT_DOWN)
                TriggerRegisterPlayerEvent(tLU,Player(i),EVENT_PLAYER_ARROW_LEFT_UP)
                TriggerRegisterPlayerEvent(tRD,Player(i),EVENT_PLAYER_ARROW_RIGHT_DOWN)
                TriggerRegisterPlayerEvent(tRU,Player(i),EVENT_PLAYER_ARROW_RIGHT_UP)
                TriggerRegisterPlayerEvent(tDD,Player(i),EVENT_PLAYER_ARROW_DOWN_DOWN)
                TriggerRegisterPlayerEvent(tDU,Player(i),EVENT_PLAYER_ARROW_DOWN_UP)
                TriggerRegisterPlayerEvent(tUD,Player(i),EVENT_PLAYER_ARROW_UP_DOWN)
                TriggerRegisterPlayerEvent(tUU,Player(i),EVENT_PLAYER_ARROW_UP_UP)
            }
        }
       
        TimerStart(CreateTimer(),Ctrl_Tick,true,function Tick)
    }
   
    private void Init()
    {
        TimerStart(CreateTimer(), 0, false, function DelayedInit)
    }
endlibrary

//===========================================================================
function InitTrig_Lib_Controls takes nothing returns nothing
endfunction
General combat stuff, aside from most states, which are to be found in "common frames" and other character data.
Arenas, timers, win/loss, hit detection (not in the Character class, in case I add helpers).
Shared camera (for every player watching the same arena).
Screen splats like "Round 1". Fireballs.
//TESH.scrollpos=491
//TESH.alwaysfold=0
library LibFight initializer Init uses MAIN, LibMisc, LibCamera, LibSound, LibMovelist, LibChar
    struct tFireball
        int owner // character index on arena, not player
        int arena
        real lifetime
        unit mdl
        real x,y,z
        real dx,dy,dz // fix model's center
        real sx,sy,sz,az
        real radius, face
        int prio
        string onhit
        string ondeath
        bool no_death, nowall
        //
        void Shift(real shiftx, real shifty, real shiftz) {x+=shiftx; y+=shifty; z+=shiftz; SetUnitPosition(mdl,x,y); SetUnitFlyHeight(mdl,z,0)}
    endstruct
    tFireball TriggeringFireball
    tFireball array Fireballs
    int fireball_count
   
    constant int ARENA_TOWN = 5
    constant int ARENA_LAVA = 4
    constant int ARENA_WOOD = 1
    constant int ARENA_ROCK = 2
    constant int ARENA_SNOW = 3
           
    public int count
       
    struct tArena // each arena supports one fight at a time so all main fight variables are there
        string name
        // coordinates
        real cx, cy, radius
        bool taken
        int index
       
        // settings
        real roundtime, defroundtime
        bool practise, survival
       
        // time
        int speed // 0.01 seconds per frame; default is frame*100
        int speed_count
        int round, maxround, maxwin
        bool final_round
        bool paused, finish, movelist; int movelister
        real superpause
        int roundend
       
        // camera
        int follow
        bool default_camera
       
        // chars
        tChar array pl[2]
       
        tFireball NewFireball(int p, int id, real speed, string onhit, real lifetime)
        {
            tFireball ball = tFireball.create()
            ball.prio = 1
            ball.face = pl[p].face
            ball.arena = index
            ball.owner = p
            ball.onhit = onhit
            ball.ondeath = " "
            ball.x = pl[p].x;   ball.y = pl[p].y;   ball.z = pl[p].z + 80
            ball.sx = CosBJ(pl[p].face)*speed;    ball.sy = SinBJ(pl[p].face)*speed;    ball.sz = 0;    ball.az = 0;
            ball.dx=0;  ball.dy=0;  ball.dz=0;  ball.radius = 45
            ball.lifetime = lifetime
            ball.no_death = false; ball.nowall=false
            ball.mdl = CreateUnit(Player(pl[p].owner), id, ball.x, ball.y, pl[p].face)
            Fireballs[fireball_count++] = ball
            return ball
        }
       
        void Msg(string what)
        {
            int i; for(i=0; i<PLAYERS; i++)
            {
                if here[i] && !ai[i] && IAbsBJ(View[i].battle_index) == index
                {
                    DisplayTextToPlayer(Player(i),0,0,what)
                }
            }
        }
       
        void TimedSway(real t) // shakes camera
        {
            timer tm=CreateTimer(); int h=GetHandleId(tm)
            SaveInteger(hash,h,0, index)
            if LocalArena()==index
            {
                CameraSetTargetNoiseEx(7.0, Pow(10,3.5),true)
                CameraSetSourceNoiseEx(7.0, Pow(10,3.5),true)
            }
            TimerStart(tm, t, false, function SwayEnd)
            tm=null
        }
       
        void Splat(string what, real time)
        {
            if LocalArena() == index
            {
                CinematicFadeCommonBJ(100, 100, 100, time /** 0.5*/, what, 100, 0)
            }
            FinishCinematicFadeAfterBJ(time)
        }
       
        void SlowMotionFlash()
        {
            if LocalArena() == index
            {
                CinematicFadeCommonBJ(33, 67, 67, 0.5 * 0.5, "ReplaceableTextures\\CameraMasks\\White_mask.blp", 75, 75)
            }
            FinishCinematicFadeAfterBJ(0.5)
        }
       
        void RageConflict()
        {
            tChar pt = pl[0], op = pl[1]
            pt.fr=20; pt.state=STATE_HURT; pt.sx=-600*CosBJ(pt.face); pt.sy=-600*SinBJ(pt.face)
            op.fr=20; op.state=STATE_HURT; op.sx=-600*CosBJ(op.face); op.sy=-600*SinBJ(op.face)
        }
       
        trigger roundsplattrigger
        void RoundSplat() {
        //TriggerExecute(roundsplattrigger)
            EnableTrigger(roundsplattrigger)
        }
       
        bool Paused()
        {
            //if paused {BJDebugMsg("paused")}
            //if movelist {BJDebugMsg("movelist")}
            //if superpause>0 {BJDebugMsg("superpause>0")}
            return paused || movelist || superpause>0
        }
       
        real CharDist() {
            real dx=pl[0].x-pl[1].x
            real dy=pl[0].y-pl[1].y
            return SquareRoot(dx*dx+dy*dy)
        }
       
        void SetSpeed(int what)
        {
            speed = what
            speed_count=0
            real factor = I2R(DEFAULT_SPEED)/what
            pl[0].speed_factor = factor
            pl[1].speed_factor = factor
            SetUnitTimeScale(pl[0].mdl, pl[0].timescale*factor)
            SetUnitTimeScale(pl[1].mdl, pl[1].timescale*factor)
            // change critter animation speed
            group g = CreateGroup(); unit u
            GroupEnumUnitsOfPlayer(g, Player(PLAYER_NEUTRAL_PASSIVE), null)
            loop
                u = FirstOfGroup(g); exitwhen (u == null); GroupRemoveUnit (g,u)
                if RAbsBJ(GetUnitX(u) - cx) < radius + 500 && RAbsBJ(GetUnitY(u) - cy) < radius + 500
                {
                    SetUnitTimeScale(u, factor)
                }
            endloop
            DestroyGroup(g)
            int i=0
            loop
                exitwhen(i>=fireball_count)
                if Fireballs[i].arena == index
                {
                    SetUnitTimeScale(Fireballs[i].mdl, factor)
                }
                i++
            endloop
            g=null; u=null
        }
        void Superpause(real t)
        {
            superpause=t
            SetSpeed(300)
        }
        void ResetSpeed() { SetSpeed(DEFAULT_SPEED) }
       
        void SetCamera(real x, real y, real z, real d, real a, real f, bool instantly)
        {int i
            default_camera = false
            for(i=0;i<PLAYERS;i++)
            {
                if (here[i] and !ai[i]) and index==IAbsBJ(View[i].battle_index)
                {
                    if i==pl[1].owner {View[i].Reset(d,a,f+180,instantly)}
                    else {View[i].Reset(d,a,f,instantly)}
                    View[i].Move(x,y,z,instantly)
                }
            }
        }
       
        void MoveCamera(real x, real y, real z, bool instant)
        {int i
            default_camera = false
            for(i=0;i<PLAYERS;i++)
            {
                if (here[i] and !ai[i]) and index==IAbsBJ(View[i].battle_index)
                {
                    if instant {
                        View[i].pX = x
                        View[i].pY = y
                        View[i].pZ = z
                    } else
                    {
                        View[i].X = x
                        View[i].Y = y
                        View[i].Z = z
                    }
                }
            }
        }
       
        bool HitEx(int i, real x, real y, real a1, real a2, real range, string height)
        {
           
            real dx=pl[1-i].x - x, dy = pl[1-i].y - y
            real an = /*NormAngle*/(Atan2BJ(dy, dx))
            if AngleBetween(an,a1,a2) //(a1 > an && a2 < an) || (a2 > an && a1 < an)
            {
                if dx*dx+dy*dy < range*range
                {
                    string st=pl[1-i].statetype
                    if height=="h" && ((st=="a" && pl[1-i].z<140) || st=="s")
                    {
                        pl[1-i].lasthit = "h"
                        return true
                    }
                    if height=="m" && (st=="c" || st=="s")
                    {
                        pl[1-i].lasthit = "m"
                        return true
                    }
                    if height=="l" && (st=="c" || st=="s" || st=="ld")
                    {
                        pl[1-i].lasthit = "l"
                        return true
                    }
                    if height=="s" && (pl[1-i].z<100) && st!="ld"
                    {
                        pl[1-i].lasthit = "s"
                        return true
                    }
                }
            }
            return false
        }
       
        bool Hit(int i, real a1, real a2, real range, string height) // attacks directly from character[i], not fireballs
        {
            return HitEx(i, pl[i].x, pl[i].y, pl[i].face+a1, pl[i].face+a2, range, height)
        }
       
        void EndFightFin()
        {int i
            MultiboardDisplay(mb[pl[0].owner], false)
            MultiboardDisplay(mb[pl[1].owner], false)
            RemoveUnit(pl[0].mdl)
            RemoveUnit(pl[1].mdl)
            taken=false
            ChangeMusic(index)
            for(i=0;i<PLAYERS;i++)
            {
                if (here[i] && !ai[i]) && IAbsBJ(View[i].battle_index) == index
                {
                    FINP=i; ExecuteFunc("LibMenu_ShowMenuExe")
                }
            }
        }
             
        void StopMovement()
        {
            int i; for(i=0; i<2; i++)
            {
                if pl[i].statetype!="a" && pl[i].state != STATE_HURT {pl[i].sx=0; pl[i].sy=0}
            }
        }
       
        void EndFight(real t, string msg)
        {
            superpause = t
            finish = true
            //Msg(msg)
           
            MultiboardDisplay(mb[pl[0].owner], false)
            MultiboardDisplay(mb[pl[1].owner], false)
            StopMovement()
           
            Splat("ReplaceableTextures\\CameraMasks\\Black_mask.blp", t)
            string s = GetPlayerName(Player(pl[0].owner))+" vs "+GetPlayerName(Player(pl[1].owner)) + ": " + msg
            if survival {s="Survival: " + msg}
           
            if !practise
                {BJDebugMsg(s)}
        }
       
        void DefaultCamera()
        {
            real dist = CharDist() + (pl[0].z + pl[1].z)/2
            real dx=pl[1].x - pl[0].x
            real dy=pl[1].y - pl[0].y
            real an = 90+Atan2BJ(dy,dx)
            real zd = 60+RMaxBJ(pl[0].z, pl[1].z)*0.65
            real x = (pl[0].x + pl[1].x)/2
            real y = (pl[0].y + pl[1].y)/2
            real z = GetZ(x,y)
           
            real cd = (x-cx)*(x-cx)+(y-cy)*(y-cy)
            if index == ARENA_LAVA && cd > 150*150 {zd += 50}
           
            //BJDebugMsg(R2S(z))
           
            if (cx-x)*(cx-x) + (cy-y)*(cy-y) > (radius-150)*(radius-150) {z += 35} // this shit is so annoying. why, blizzard?
            if dist < 240 {SetCamera(x+CosBJ(an+180)*40, y+SinBJ(an+180)*40, zd - z, dist+MIN_CAM_DIST, 340, an, false)}
                else {SetCamera(x, y, zd - z, dist+MIN_CAM_DIST, 350, an, false)}
            default_camera = true
            follow = -1
        }
       
        int custom_camera
        void SpinCamera(real dx, real dy, real dz, real dd, real da, real df, int time)
        {int i
            default_camera = false
            custom_camera = time
            for(i=0;i<PLAYERS;i++)
            {
                if (here[i] and !ai[i]) and index==IAbsBJ(View[i].battle_index)
                {
                    View[i].Rotate(dd,da,df, time)
                    View[i].Velocity(dx,dy,dz)
                }
            }
        }
       
        int intro // <0 - no, 0 - p1, 1 - p2
        int intro_time
        void LaunchIntro(int p)
        {
            intro = p
            intro_time = INTRO_TIME
            SetCamera(pl[p].x, pl[p].y, 0, 320, 350, 90, true)
            SpinCamera(0,0,16, 220, -10, 360*(1-2*p), INTRO_TIME)
            pl[p].Anim("Stand Gold", 1.)
            pl[p].speech = IntroSounds(index, pl[p].mdl)
        }
       
        static thistype Create(real x, real y, real r, int i, string n)
        {
            thistype this = thistype.allocate()
            taken = false
            this.cx=x; this.cy=y; this.radius=r
            this.name = n
            this.pl[0] = tChar.create()
            this.pl[1] = tChar.create()
            this.pl[0].arena = i
            this.pl[1].arena = i
            this.pl[0].rage_active = false
            this.pl[1].rage_active = false
            this.index = i
            return this
        }
       
        void Reset()
        {            
            paused=false; finish=false; movelist=false; movelister=-1; superpause=0; custom_camera=0; roundend = 0
            TEMP_ARENA=index
            ExecuteFunc(ClearShades.name)
            roundtime = defroundtime
            follow=-1
            ChangeMusic(index)
           
            if survival {
                pl[1].id = RandomChar()
                pl[1].name = LoadStr(hash, pl[1].id, HASH_CHAR_NAME)
            }
           
            int i; for(i=0;i<2;i++)
            {
                DestroyMultiboard(mb[pl[i].owner])
                InitBoard(pl[i].owner)
                pl[i].Neutral()
                pl[i].armor_pause = 0
                pl[i].poison = 0
                pl[i].face = 180*i
                pl[i].x = cx - (1-2*i) * radius/3
                pl[i].y = cy
                pl[i].z = 0
                pl[i].manaflash = 0
                pl[i].life = MAX_LIFE
                pl[i].mana = START_MANA
                pl[i].hurt = 0
                pl[i].statetype = "s"
                pl[i].hurt_time = 0
                pl[i].rage_used = false; pl[i].rage_active = false
                if pl[i].rage_active {DestroyEffect(pl[i].rageart)}
                pl[i].Neutral()
               
                if GetUnitTypeId(pl[i].mdl)!=0 {RemoveUnit(pl[i].mdl)}
                pl[i].mdl = CreateUnit(Player(pl[i].owner), pl[i].id, pl[i].x, pl[i].y, pl[i].face)
                SetUnitColor(pl[i].mdl, GetPlayerColor(Player(pl[i].owner)))
            }
           
            SetCamera(cx,cy,0,700,345,90, true)
            ResetSpeed()
            StopMovement()
        }
       
        bool CanFinish()
        {
            if pl[0].z > 5 || pl[1].z > 5 {return false}
            if pl[0].state > 0 && pl[0].state != STATE_DEAD && pl[0].state != STATE_VICTORY {return false}
            if pl[1].state > 0 && pl[1].state != STATE_DEAD && pl[1].state != STATE_VICTORY {return false}
           
            return true
        }
       
        void EndRound(int winner)
        {
            //if pl[0].z > 6 || pl[1].z > 6 {return} // no more characters stuck in air
       
            if winner==0 {pl[0].wins++}
            elseif winner==1 {pl[1].wins++}
            round++
            int i=-1
           
            string s="Tied! "+I2S(round)+" rounds."
            if pl[0].wins > pl[1].wins {s=GetPlayerName(Player(pl[0].owner))+" won!"; i=0}
            elseif pl[0].wins < pl[1].wins {s=GetPlayerName(Player(pl[1].owner))+" won!"; i=1}
           
            bool just_dont = false
            if survival && winner==1 {maxround=0; s=GetPlayerName(Player(pl[0].owner))+" lost, "+I2S(pl[0].wins)+" rounds"; i=1}
                elseif survival {just_dont=true; maxround = 99999; s+=GetPlayerName(Player(pl[0].owner))+" won, "+I2S(pl[0].wins)+" rounds"; i=0}
           
            if (round>maxround || pl[0].wins>=maxwin || pl[1].wins>=maxwin) && !just_dont
            {
                if i>=0
                {
                    SetCamera(pl[i].x, pl[i].y, 0, 400, 335, pl[i].face + 180, false)
                    custom_camera = 200
                    EndFight(4., s)
                }
                else
                {
                    EndFight(5., s)
                }
                return
            }
           
            Reset()
            RoundSplat()
        }
    endstruct
   
    int array PreferredArena
    tArena array Arena
   
        void RoundSplatAction()
        {
            trigger t = GetTriggeringTrigger()
            DisableTrigger(t)
            tArena a = LoadInteger(hash, GetHandleId(t), 0)
            string path
            if (a.pl[0].wins+1 >= a.maxwin && a.pl[1].wins+1 >= a.maxwin) || a.round >= a.maxround
            {
                path = "Splats\\finals.tga"; a.final_round=true
                a.pl[0].PlaySound("announcer\\FRound.wav")
                a.superpause = 1.25
                a.Splat(path, a.superpause)
            }
            else
            {
                if a.practise || a.survival
                {
                    a.superpause = 0.43
                }
                else
                {
                    path = "Splats\\round"+I2S(a.round)+".tga"
                    a.pl[0].PlaySound("announcer\\Round.wav")
                    a.superpause = 1.25
                    a.Splat(path, a.superpause)
                    TriggerSleepAction(1.)
                    a.pl[0].PlaySound("announcer\\"+I2S(a.round)+".wav")
                }
            }
            t=null
        }
   
    void ExplodTick()
    {
        int i=0
        loop
            exitwhen(i >= excount)
            if extime[i] > 0 { if !Arena[exta[i]].Paused() {extime[i] -= EXPLOD_TICK*I2R(DEFAULT_SPEED)/Arena[exta[i]].speed}; i++ }
            else
            {
                DestroyEffect(expl[i]); excount--; expl[i] = expl[excount]; exta[i]=exta[excount]
            }
        endloop
        i=0
        loop
            exitwhen(i >= ligcount)
            //BJDebugMsg("i = "+I2S(i)+"/"+I2S(ligcount))
            if ligtime[i] <= 0 || (GetUnitTypeId(ligt1[i])*GetUnitTypeId(ligt2[i]) == 0) || !(IsUnitAliveBJ(ligt1[i]) && IsUnitAliveBJ(ligt2[i]))
            {
                //BJDebugMsg("destroy "+I2S(i)+" out of "+I2S(ligcount))
                ligcount--
                liga[i]=liga[ligcount]
                ligdh1[i] = ligdh1[ligcount]; ligdh2[i] = ligdh2[ligcount]
                ligt1[i] = ligt1[ligcount]; ligt2[i] = ligt2[ligcount]
                ligtime[i] = ligtime[ligcount]
                DestroyLightning(lightn[i]); lightn[i] = lightn[ligcount]
            }
            else
            {
                if !Arena[liga[i]].Paused() {ligtime[i] -= EXPLOD_TICK*I2R(DEFAULT_SPEED)/Arena[liga[i]].speed}
                MoveLightningEx(lightn[i], true, GetUnitX(ligt1[i]), GetUnitY(ligt1[i]), GetUnitFlyHeight(ligt1[i])+ligdh1[i], GetUnitX(ligt2[i]), GetUnitY(ligt2[i]), GetUnitFlyHeight(ligt2[i])+ligdh2[i])
                i++
            }
        endloop
    }
   
    private int PickArena(int p1, int p2)
    {
        int pp1 = PreferredArena[p1]; if Arena[pp1].taken {pp1=0}
        int pp2 = PreferredArena[p2]; if Arena[pp2].taken {pp2=0}
        if pp1>0 && pp2>0 {if GetRandomInt(0,100)>50 {return pp1} else {return pp2}}
        elseif pp1>0 {return pp1}
        elseif pp2>0 {return pp2}
       
        //return ARENA_LAVA
        random_count=0
        int i; for(i=1;i<count;i++)
        {
            if !false {random_sort[random_count++] = i}
        }
       
        //return 0
        //return count-1 // test the last one
        return random_sort[GetRandomInt(0,random_count-1)]
    }
   
    void ShowBoard(int p)
    {
        MultiboardDisplay(mb[p], GetLocalPlayer() == Player(p))
        MultiboardMinimize(mb[p], true)
        MultiboardMinimize(mb[p], false)
    }
   
    void HideBoard(int p) {MultiboardDisplay(mb[p], false)}
   
    tArena PrepareFight(int p1, int p2, int id1, int id2, int a)
    {    
        //BJDebugMsg(I2S(p1)+" vs "+I2S(p2)); return 0
       
        if id1<=0 {id1=RandomChar()}
        if id2<=0 {id2=RandomChar()}
        escape_reset[p1]=0
        escape_reset[p2]=0
   
        if ( a < 1 || a >= count ) || Arena[a].taken {a=PickArena(p1,p2)}
       
        Arena[a].taken = true
        Arena[a].practise = false
        Arena[a].survival = false
        Arena[a].pl[0].owner = p1; Arena[a].pl[0].id = id1; View[p1].battle_index = a
        Arena[a].pl[1].owner = p2; Arena[a].pl[1].id = id2; View[p2].battle_index = a
        Arena[a].pl[0].immortal = false
        Arena[a].pl[1].immortal = false
       
        int i; for (i=0; i<2; i++)
        {
            Arena[a].pl[i].index = i
            Arena[a].pl[i].name = LoadStr(hash, Arena[a].pl[i].id, HASH_CHAR_NAME)
        }
       
        Arena[a].defroundtime=DEF_ROUNDTIME
       
        Arena[a].Reset()
        Arena[a].LaunchIntro(0)        
        Arena[a].round = 1
        Arena[a].maxround = 5
        Arena[a].maxwin = 3
        Arena[a].pl[0].wins = 0
        Arena[a].pl[1].wins = 0
        Arena[a].final_round = false
       
        return Arena[a]
    }
   
    void FireballTick()
    {
        int i=0
        loop
            exitwhen(i >= fireball_count)
            tFireball ball = Fireballs[i]
            tArena a = Arena[ball.arena]
            real cf = CosBJ(ball.face), sf = SinBJ(ball.face)
            if !a.Paused()
            {
                real f = /*frame**/I2R(DEFAULT_SPEED)/a.speed // game speed factor
                tChar pt = a.pl[ball.owner]
               
                // hitting opponent
                bool hit_op = false
                string height = "s"; if ball.z > 65 {height = "h"} elseif ball.z < 40 {height = "l"}
                if ball.z > a.pl[1-ball.owner].z + 120 {
                    // too high
                }
                elseif ball.radius > 0 && a.HitEx(ball.owner, ball.x - cf*ball.radius/2, ball.y - sf*ball.radius/2, ball.face-30, ball.face+30, ball.radius, height)
                {
                    hit_op = true
                }
               
                // hitting opponent's fireballs
                int j=i+1
                    real dx,dy,dz,rr
                loop
                    exitwhen(j >= fireball_count)
                    tFireball ball2 = Fireballs[j]
                    if ball2.arena == ball.arena && ball2.owner != ball.owner
                    {
                        dx = ball.x - ball2.x
                        dy = ball.y - ball2.y
                        dz = ball.z - ball2.z
                        rr = ball.radius + ball2.radius
                        if dx*dx + dy*dy + dz*dz < rr*rr
                        {
                            if ball.prio <= ball2.prio {ball.lifetime = 0}
                            if ball2.prio <= ball.prio {ball2.lifetime = 0}
                        }
                    }
                    j++
                endloop
               
                // movement
                ball.x += ball.sx * .01 *f
                ball.y += ball.sy * .01 *f
                ball.z += ball.sz * .01 *f
                ball.sz += ball.az * .01 *f
                SetUnitPosition(ball.mdl, ball.x+ball.dx, ball.y+ball.dy)
                SetUnitFlyHeight(ball.mdl, ball.z+ball.dz, 0)
               
                // wall collision
                dx=a.cx - ball.x
                dy=a.cy - ball.y
                if dx*dx+dy*dy > a.radius*a.radius && !ball.nowall {if ball.radius<1 {ball.sx=0; ball.sy=0; ball.sz=0} else {ball.lifetime=0}}
               
                // and that's it
                ball.lifetime -= 0.01 * f
                if ball.lifetime < 0
                {
                    if ball.no_death {RemoveUnit(ball.mdl)} else {KillUnit(ball.mdl)}
                    ExplodPt(a.index, ball.x, ball.y, ball.ondeath, 33)
                    Fireballs[i].destroy()
                    fireball_count--
                    Fireballs[i] = Fireballs[fireball_count]
                }
                else {
                    i++
                    if hit_op {
                        TriggeringFireball = ball
                        ExecuteFunc(ball.onhit)
                    }
                }
            } else {i++}
        endloop
    }
   
    int AT
    private void ArenaTick()
    {tArena a=Arena[AT]
        if a.intro>=0 {
            a.intro_time--
            if a.practise {a.intro_time = 0}
            //
            if a.intro_time==0
            {
                if a.intro==1
                {
                    a.intro = -1
                    a.DefaultCamera()
                    // start the fight
                    a.pl[0].Neutral()
                    a.pl[1].Neutral()
                    ShowBoard(a.pl[0].owner)
                    ShowBoard(a.pl[1].owner)
                    a.RoundSplat()
                }
                else {
                    a.LaunchIntro(1)
                    a.pl[0].state=STATE_INTRO; a.pl[0].fr = 40
                    a.pl[1].state=STATE_INTRO; a.pl[1].fr = 40
                }
            }
        } else {
            bool d1 = a.pl[0].Dead(), d2 = a.pl[1].Dead()
            bool endit = false
           
            if true//a.CanFinish() // floating in the air bug
            {
                if a.finish { if a.superpause < 0.1 {a.EndFightFin()}; return}
                elseif (d1 || d2) && a.roundend < ROUND_END_TIME
                {
                    a.roundend++
                    a.StopMovement()
                    if d1 {if a.pl[1].WinPosture() {endit = true}}
                    if d2 {if a.pl[0].WinPosture() {endit = true}}
                }
                elseif d1 && a.CanFinish() {a.EndRound(1)}
                elseif d2 && a.CanFinish() {a.EndRound(0)}
            }
           
            endit = endit && (d1 || d2)
           
            if endit && (a.pl[0].life >= MAX_LIFE || a.pl[1].life >= MAX_LIFE) {a.Splat("Splats\\PERFECT.tga", 1.6)}
            elseif endit && a.pl[0].life <= GREAT_LIFE && a.pl[1].life <= GREAT_LIFE {a.Splat("Splats\\great.tga", 1.6)}
           
            if !a.Paused() {
                if a.pl[0].state != STATE_RAGEART && a.pl[1].state != STATE_RAGEART
                {
                    if a.roundend<=0
                    {
                        if !(a.practise || a.survival) {if a.roundtime > 0 {a.roundtime -= frame}}
                    }
                   
                    if a.roundtime<0 && a.CanFinish() { // Time up!
                        a.roundtime=0
                        if a.roundend==0
                        {
                            if a.pl[0].life == a.pl[1].life {a.pl[0].life ++}
                            a.Splat("Splats\\timeup.tga", 1.1)
                        }
                        a.roundend++
                        if a.roundend < ROUND_END_TIME
                        {
                            if a.pl[0].life>=a.pl[1].life {a.pl[0].WinPosture()} else {a.pl[0].state=STATE_DEAD}
                            if a.pl[0].life<=a.pl[1].life {a.pl[1].WinPosture()} else {a.pl[1].state=STATE_DEAD}
                        }
                        elseif a.roundend >= ROUND_END_TIME
                        {
                            if a.pl[0].life == a.pl[1].life {a.pl[0].life ++}
                            if a.pl[0].life<a.pl[1].life {a.EndRound(1)}
                            elseif a.pl[0].life>a.pl[1].life {a.EndRound(0)}
                            else {a.EndRound(-1)}
                        }
                    } elseif a.pl[0].counterhit && a.pl[1].counterhit && a.speed == DEFAULT_SPEED
                    {
                        if a.pl[0].life <= SLOW_MOTION_HP && a.pl[1].life <= SLOW_MOTION_HP && a.CharDist() <= SLOW_MOTION_RANGE
                        {
                            if a.speed != SLOW_MOTION_SPEED {a.SlowMotionFlash()}
                            a.SetSpeed(SLOW_MOTION_SPEED)
                        }
                    }
                    elseif a.speed == SLOW_MOTION_SPEED && !(a.pl[0].counterhit && a.pl[1].counterhit) {a.ResetSpeed()}
                }
                a.pl[0].lasthit=" "
                a.pl[1].lasthit=" "
                //
                if a.pl[1].armor_pause > 0 {a.pl[1].armor_pause--; if a.pl[1].armor_pause==0 {SetUnitTimeScale(a.pl[1].mdl, a.pl[1].timescale); SetUnitVertexColor(a.pl[1].mdl, 255,255,255,255)}}
                if a.pl[0].armor_pause > 0 {a.pl[0].armor_pause--; if a.pl[0].armor_pause==0 {SetUnitTimeScale(a.pl[0].mdl, a.pl[0].timescale); SetUnitVertexColor(a.pl[0].mdl, 255,255,255,255)}
                    a.pl[1].Tick()
                }
                else
                {
                    a.pl[0].Tick()
                }
            }
        }
       
        if a.custom_camera>0 {
            a.custom_camera--
            if a.follow>=0
            {
                a.MoveCamera(a.pl[a.follow].x, a.pl[a.follow].y, (a.pl[0].z + a.pl[1].z)/2 + 40, true)
            }
        } else {a.default_camera = true}
        if a.default_camera {a.DefaultCamera()}
    }
   
    private void PTick(tArena a, tChar pt, tChar op)
    {
        real prex = pt.x, prey = pt.y
        pt.PhysTick(1./a.speed)
        // OPPONENT COLLISION
        if a.CharDist() < BUMP_RANGE && !(pt.noclip || op.noclip || (pt.z > op.GetMaxZ()) || (pt.GetMaxZ() < op.z))
        {
            real dx=prex-op.x
            real dy=prey-op.y
            real sq=SquareRoot(dx*dx+dy*dy)
            pt.x = prex+1.3*dx/sq
            pt.y = prey+1.3*dy/sq
        }
    }
   
    private void Tick()
    {
        ExecuteFunc(FireballTick.name)
        int i; for(i=1;i<count;i++)
        {
            tArena a = Arena[i]
            if a.taken
            {
                if a.superpause > 0 {a.superpause -= .01; if a.superpause <= 0 {a.ResetSpeed()}}
               
                a.speed_count++
               
                if !a.Paused()
                {
                    PTick(a, a.pl[1], a.pl[0])
                    PTick(a, a.pl[0], a.pl[1])
                }
               
                if Arena[i].speed_count >= Arena[i].speed
                {
                    Arena[i].speed_count = 0
                    AT = i
                    //ExecuteFunc(ArenaTick.name)
                    ArenaTick()
                }
            }
        }
    }
   
    tArena InitArena(real x, real y, real r, int i, string name) // because RoundSplatAction is below the struct
    {
        tArena a = tArena.Create(x,y,r,i,name)
        a.roundsplattrigger = CreateTrigger()
        TriggerAddAction(a.roundsplattrigger, function RoundSplatAction)
        TriggerRegisterTimerEvent(a.roundsplattrigger,.01,true)
        DisableTrigger(a.roundsplattrigger)
        SaveInteger(hash, GetHandleId(a.roundsplattrigger), 0, a)
        return a
    }
   
    private void DelayedInit()
    {
        count=1
        fireball_count=0
        RefreshPlayers()
        Arena[ARENA_WOOD] = InitArena(-1750,3430,775,   ARENA_WOOD, "Wood"); count++
        Arena[ARENA_ROCK] = InitArena(1800,3351,605,    ARENA_ROCK, "Rock"); count++
        Arena[ARENA_SNOW] = InitArena(-2170,-4190,670,  ARENA_SNOW, "Snow"); count++
        Arena[ARENA_LAVA] = InitArena(-1800,-610,650,   ARENA_LAVA, "Lava"); count++
        Arena[ARENA_TOWN] = InitArena(2194,-274,740,    ARENA_TOWN, "Town"); count++
        TimerStart(CreateTimer(),0.01,true,function Tick)
        TimerStart(CreateTimer(),EXPLOD_TICK,true, function ExplodTick)
        PreloadSound("announcer\\Round.wav")
        PreloadSound("announcer\\FRound.wav")
        PreloadSound("announcer\\1.wav")
        PreloadSound("announcer\\2.wav")
        PreloadSound("announcer\\3.wav")
        PreloadSound("announcer\\4.wav")
    }
   
    private void Init()
    {
        TimerStart(CreateTimer(), 0, false, function DelayedInit)
    }
endlibrary

function InitTrig_Lib_Fight takes nothing returns nothing
endfunction
hints displayed during the game
//TESH.scrollpos=0
//TESH.alwaysfold=0
library LibHints initializer Init uses LibSound
    constant real HintTick = 20.
    constant real HintDur = 10.
   
    string array hint_text
    bool array hints_on
    private int count
    private int display_index
   
    private void Tick()
    {
        bool b=false
        int i; for(i=0; i<PLAYERS; i++)
        {
            if here[i] && hints_on[i] && !ai[i]
            {
                DisplayTimedTextToPlayer(Player(i), 0,0, HintDur, "|cff22eeeeHINT:|r " + hint_text[display_index])
                if GetLocalPlayer() == Player(i) {b=true}
            }
        }
        HintSound(b, "Sound\\Interface\\Hint.wav")
        display_index++; if display_index >= count {display_index = 0}
    }
   
    private void onChatHints ()
    {
        int i = GetPlayerId(GetTriggerPlayer())
        hints_on[i] = !hints_on[i]
        if hints_on[i]
        {
            DisplayTimedTextToPlayer(Player(i), 0,0, 3., "|cff33ee33Hints turned on.")
        }
        else
        {
            DisplayTimedTextToPlayer(Player(i), 0,0, 3., "|cffee3333Hints turned off.")
        }
    }
   
    private void Init()
    {
        trigger t=CreateTrigger(); TriggerAddAction(t, function onChatHints)
        int i; for(i=0; i<PLAYERS; i++) {hints_on[i] = true; TriggerRegisterPlayerChatEvent(t, Player(i), "-hints", true)}
        TimerStart(CreateTimer(), HintTick, true, function Tick)
        count=0; display_index=0
        hint_text[count++] = "Tap any arrow key to make a step in that direction."
        hint_text[count++] = "You can |cffffcc00Block|r by holding the |cffffcc00Left|r arrow key."
        hint_text[count++] = "|cff22aa22Low|r attacks may not be blocked while standing."
        hint_text[count++] = "You can |cff22aa22Crouch|r by holding the |cff22aa22Down|r arrow key, to block |cff22aa22Low|r and avoid |cffee2222High|r attacks."
        hint_text[count++] = "|cffffcc00Mid|r attacks may not be blocked while crouching."
        hint_text[count++] = "When knocked down, hit W to get up, or hold any arrow key to roll."
        hint_text[count++] = "When hit with a |cff0000ffThrow|r, quickly hit W to break free."
        hint_text[count++] = "Hit |cffffcc00Escape|r to pause the fight and show/hide the character's move list."
    }
endlibrary

function InitTrig_Lib_Hints takes nothing returns nothing
endfunction
hero selection, opponent, duels
//TESH.scrollpos=294
//TESH.alwaysfold=0
library LibMenu initializer Init uses LibCamera, LibFight, LibMisc, LibSound
    struct tCharData
        int index,x,z,id,icon
        real X,Y,Z
        destructable btn
        string name
    endstruct
    private int count
    private int array select_a
    private constant real HOLD_PERIOD = 0.3, HOLD_INITIAL = 1.
    private real array key_hold[PLAYERS][4]
    private unit array select
    int array selx
    int array selz
    private     dialog array d_dial
    private     button array d_conf
    private     button array d_decl
   
    constant real preview_x1 = -5005
    constant real preview_y1 = 4175
    constant real preview_x2 = -4331
    constant real preview_y2 = 4301
   
    void InitChar(int x, int z, int id, int icon, string name) // name is used for ai, movelist and framedata function names
    {
        if id==0 && icon != 'B00M' {icon = 'B000'} // locked characters have id of 0
        tCharData t = tCharData.create()
        t.index = count++; CData[x][z] = t
        t.x = x; t.z = z; t.id = id; t.icon = icon; t.name = name
        t.X = -4903 + 64*x - 64
        t.Y = 4604
        t.Z = 64 + 64*z
        SaveStr(hash,id,HASH_CHAR_NAME,name)
        SaveInteger(hash,id,HASH_CHAR_ID,t.index)
        t.btn = CreateDestructableZ(icon,t.X,t.Y,t.Z,270,1,0)
    }
   
    private unit array preview
    private unit ai_preview
   
    private unit array preview2
    int array duel_sent
    private int array duel_received
    private int array ai_pickx
    private int array ai_pickz
   
    private texttag array pretag
    private texttag array nametag
   
    void CancelDuel(int p)
    {
        int i=duel_sent[p]
        DialogDisplay(Player(i), d_dial[i], false)
        duel_received[i] = -1; duel_sent[p]=-1
    }
   
    void HideTexts(int p)
    {
        SetTextTagVisibility(pretag[p], false)
        SetTextTagVisibility(nametag[p], false)
    }
   
    void Select(int p, int x, int z)
    {
        if x>8 {x=0} elseif x<0 {x=8}
        if z>3 {z=0} elseif z<0 {z=3}
        int id=CData[x][z].id
        if id==0 { id='h004' }
        if preview2[p]!=null {RemoveUnit(preview2[p])}
       
        real X,Y; int a
        if ai_pickx[p] < 0
        {
            selx[p]=x; selz[p]=z
            X=preview_x1; Y=preview_y1
            if preview[p]!=null {RemoveUnit(preview[p])}
            preview[p] = CreateUnit(Player(p),id,X,Y,270)
            SetUnitColor(preview[p], GetPlayerColor(Player(p)))
            a=255; if GetLocalPlayer()!=Player(p) {a=0}
            SetUnitVertexColor(preview[p],255,255,255,a)
            SetTextTagTextBJ(pretag[p], GetUnitName(preview[p]), 11)
            SetTextTagPos(pretag[p],X-32, Y, 150)
        }
        else
        {
            ai_pickx[p]=x; ai_pickz[p]=z
            X=preview_x2; Y=preview_y2
            //if preview2[p]!=null {RemoveUnit(preview2[p])}
            preview2[p] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),id,X,Y,270)
            a=255; if GetLocalPlayer()!=Player(p) {a=0}
            SetUnitVertexColor(preview[p],255,255,255,a)
            SetTextTagTextBJ(pretag[p], GetUnitName(preview2[p]), 11)
            SetTextTagPos(pretag[p],X-32, Y, 200)
        }
       
        SetUnitPosition(select[p],CData[x][z].X,CData[x][z].Y - 6)
        SetUnitFlyHeight(select[p],CData[x][z].Z + 6,0)
       
        SetTextTagVisibility(pretag[p], a>0)
    }
   
    void MenuBack(int p)
    {
        if nameindex[p] >= 0
        {
            nameindex[p] = -1
            SetTextTagVisibility(nametag[p], false)
            duel_sent[p] = -1
            ai_pickx[p] = -1
            ai_pickz[p] = -1
            //if preview2[p]!=null {RemoveUnit(preview2[p])}
            Select(p,4,1)
        }
    }
   
    void MenuKey(int p, int k)
    {
        if nameindex[p] < 0 || ai_pickx[p]>=0
        {
            int x=selx[p], z=selz[p]
            if ai_pickx[p]>=0 {x=ai_pickx[p]; z=ai_pickz[p]}
            if k==KEY_LEFT {Select(p,x-1,z)}
            if k==KEY_RIGHT {Select(p,x+1,z)}
            if k==KEY_UP {Select(p,x,z+1)}
            if k==KEY_DOWN {Select(p,x,z-1)}
            key_hold[p][k] = HOLD_INITIAL
        }
        elseif k==KEY_LEFT || k==KEY_RIGHT
        {
            if k==KEY_LEFT {nameindex[p]--}
            if k==KEY_RIGHT {nameindex[p]++}
           
            if nameindex[p] >= PLAYERS {if p==0 {nameindex[p]=1} else {nameindex[p]=0}}
            elseif nameindex[p] == p {if k==KEY_LEFT {nameindex[p]--} else {nameindex[p]++}}
           
            if nameindex[p] < 0 {nameindex[p] = PLAYERS - 1}
        }
       
        PlayerSound(p, "Sound\\Interface\\MouseClick1.wav")
    }
   
    void MenuOrder(int p)
    {
        if !here[p] {return}
       
        if nameindex[p] < 0 && View[p].battle_index==0
        {
            //ExecuteFunc(CData[selx[p]][selz[p]].name+"Sounds")
            if p>0 {nameindex[p] = 0} else {nameindex[p] = 1}
            duel_sent[p] = -1
            ai_pickx[p] = -1
            ai_pickz[p] = -1
            if GetLocalPlayer()==Player(p)
            {
                SetTextTagVisibility(nametag[p], true)
            }
        }
        elseif ai_pickx[p]>=0 && View[p].battle_index==0 // ai opponent's character picked, start fight
        {
            HideTexts(p)
            PrepareFight(p, nameindex[p], CData[selx[p]][selz[p]].id, CData[ai_pickx[p]][ai_pickz[p]].id, -1)
        }
        elseif duel_sent[p] < 0 && View[p].battle_index==0 // start picking ai or send a duel request
        {
            int i=nameindex[p]
            //
            if ai[i] || !here[i]
            {
                ai_pickx[p]=3; ai_pickz[p]=1
                Select(p,4,1)
            }
            elseif View[i].battle_index != 0
            {
                // spectate
                HideTexts(p)
                View[p].battle_index = -IAbsBJ(View[i].battle_index)
            }
            elseif duel_received[i]<0
            {
                duel_received[i] = p
                duel_sent[p] = i
                DialogSetMessage(d_dial[i], GetPlayerName(Player(p))+" requests a duel")
                DialogDisplay(Player(i), d_dial[i], true)
                DisplayTextToPlayer(Player(p), 0,0, "Sent a duel request to "+GetPlayerName(Player(i)))
            }
        }
    }
   
    void ShowMenu(int p)
    {
        View[p].battle_index = 0
        View[p].Reset(1250,340,90,true)
        View[p].Move(-4652,4601,0,true)
        select_a[p] = 0
        nameindex[p] = -1
        duel_sent[p] = -1
        duel_received[p] = -1
            ai_pickx[p] = -1
            ai_pickz[p] = -1
        Select(p,4,1)
        ChangeMusicForPlayer(p)
    }
   
    int FINP
    public void ShowMenuExe() {ShowMenu(FINP)}
   
    private void Tick()
    {
        RefreshPlayers()
        int p; for(p=0;p<PLAYERS;p++)
        {
            if here[p] && !ai[p]
            {
                select_a[p]+=40
                if select_a[p] >= 255 {select_a[p]=-255}
               
                int a = select_a[p]
                if GetLocalPlayer()!=Player(p) {a=0}
                //BJDebugMsg(I2S(a))
                SetUnitVertexColor(select[p],255,255,255,IAbsBJ(a))
               
                if nameindex[p] >= 0
                {
                    int i=nameindex[p]
                    string what=""
                    if ai[i] || !here[i] {what="VS: AI "+I2S(i)}
                    elseif View[i].battle_index == 0 {what="VS: "+GetPlayerName(Player(nameindex[p]))}
                    else {what="WATCH: "+GetPlayerName(Player(nameindex[p]))}
                    SetTextTagTextBJ(nametag[p],"< "+what+" >",12)
                }
               
                int k=0
                loop
                {
                    int x=selx[p], z=selz[p]
                    if (Key[p][k]) && (key_hold[p][k] > 0)
                    {
                        key_hold[p][k] -= 0.1
                        if key_hold[p][k] <= 0
                        {
                            key_hold[p][k] = HOLD_PERIOD
                            if ai_pickx[p]>=0 {x=ai_pickx[p]; z=ai_pickz[p]}
                            if k==KEY_LEFT {Select(p,x-1,z)}
                            if k==KEY_RIGHT {Select(p,x+1,z)}
                            if k==KEY_UP {Select(p,x,z+1)}
                            if k==KEY_DOWN {Select(p,x,z-1)}
                        }
                    }
                    else
                    {
                        key_hold[p][k] = 0
                    }
                    k++; exitwhen(k>3)
                }
            }
        }
    }
   
    void onDuelAccept()
    {
        int i = LoadInteger(hash,GetHandleId(GetTriggeringTrigger()),0)
        int p=duel_received[i]
            HideTexts(i)
            HideTexts(p)
        PrepareFight(p, i, CData[selx[p]][selz[p]].id, CData[selx[i]][selz[i]].id, -1)
    }
   
    void onDuelDecline()
    {
        int i = LoadInteger(hash,GetHandleId(GetTriggeringTrigger()),0)
        int p = duel_received[i]
        duel_sent[p] = -1
        duel_received[i] = -1
        DisplayTextToPlayer(Player(p),0,0,GetPlayerName(Player(i))+" declined your duel request.")
    }
   
    private void DelayedInit()
    {
        count=0
        RefreshPlayers()
        InitChar(0,0, 0,'B000',"footman")
        InitChar(1,0, 'h017','B00I',"demoness")
        InitChar(2,0, 'h00Q','B00F',"felg")
        InitChar(3,0, 'h00L','B00C',"titan")
        InitChar(4,0, 'h00E','B008',"templar")
        InitChar(5,0, 'h00Z','B00H',"naga")
        InitChar(6,0, 'h00P','B00E',"skele")
        InitChar(7,0, 'h00R','B00G',"shaman")
        InitChar(8,0, 0,'B000',"footman")
        InitChar(0,1, 0,'B000',"footman")
        InitChar(1,1, 'h00F','B009',"maiev")
        InitChar(2,1, 'h00K','B00B',"vamp")
        InitChar(3,1, 'h006' ,'B003',"blademaster")
        InitChar(4,1, 0,'B00M',"footman")
        InitChar(5,1, 'h002',   'B002', "villager")
        InitChar(6,1, 'h00B' ,'B006',"deathp")
        InitChar(7,1, 'h00G','B00A',"panda")
        InitChar(8,1, 0,'B000',"footman")
        InitChar(0,2, 0,'B000',"footman")
        InitChar(1,2, 'h01T','B00K',"tauren")
        InitChar(2,2, 'h007' ,'B004',"butcher")
        InitChar(3,2, 'h008' ,'B005',"sorceress")
        InitChar(4,2, 'h01W','B00L',"sylvanus")
        InitChar(5,2, 'h00M','B00D',"kael")
        InitChar(6,2, 'h00D','B007',"illidan")
        InitChar(7,2, 'h019','B00J',"troll")
        InitChar(8,2, 0,'B000',"footman")
        InitChar(0,3, 0,'B000',"footman")
        InitChar(1,3, 0,'B000',"footman")
        InitChar(2,3, 0,'B000',"footman")
        InitChar(3,3, 0,'B000',"footman")
        InitChar(4,3, 0,'B000',"footman")
        InitChar(5,3, 0,'B000',"footman")
        InitChar(6,3, 0,'B000',"footman")
        InitChar(7,3, 0,'B000',"footman")
        InitChar(8,3, 0,'B000',"footman")
               
        int i; for(i=0;i<PLAYERS;i++)
        {
            key_hold[i][KEY_LEFT] = 0
            key_hold[i][KEY_RIGHT] = 0
            key_hold[i][KEY_DOWN] = 0
            key_hold[i][KEY_UP] = 0
            if here[i] && !ai[i]
            {
                trigger dta=CreateTrigger(); TriggerAddAction(dta, function onDuelAccept)
                trigger dtc=CreateTrigger(); TriggerAddAction(dtc, function onDuelDecline)
                select[i] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),'h003',0,0,265)
                pretag[i] = CreateTextTagUnitBJ("",select[i],0,10,100,70,0,0)
                nametag[i] = CreateTextTagUnitBJ("",select[i],0,10,100,70,0,0)
                SetTextTagPos(nametag[i],-4619,4295,0)
                SetTextTagPermanent(pretag[i], true)
                SetTextTagPermanent(nametag[i], true)
                SetTextTagVisibility(pretag[i], false)
                SetTextTagVisibility(nametag[i], false)
                d_dial[i] = DialogCreate()
                d_conf[i] = DialogAddButton(d_dial[i],"Accept",0)
                d_decl[i] = DialogAddButton(d_dial[i],"Decline",0)
                SaveInteger(hash, GetHandleId(dta), 0, i)
                SaveInteger(hash, GetHandleId(dtc), 0, i)
                TriggerRegisterDialogButtonEvent(dta, d_conf[i])
                TriggerRegisterDialogButtonEvent(dtc, d_decl[i])
                ShowMenu(i)
            }
        }
       
        SetUnitColor(CreateUnit(Player(0), 'ncop', preview_x1, preview_y1, 270), GetPlayerColor(GetLocalPlayer()))
        CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE), 'ncop', preview_x2, preview_y2, 0) // AI
       
        TimerStart(CreateTimer(),.1,true,function Tick)
    }
   
    private void Init()
    {
        TimerStart(CreateTimer(), 0, false, function DelayedInit)
    }
endlibrary

function InitTrig_Lib_Menu takes nothing returns nothing
endfunction
The mode menu, summoned by pressing Escape while in the main menu.
//TESH.scrollpos=95
//TESH.alwaysfold=0
library LibModes initializer Init uses LibFight, LibMenu
    private dialog array modedialog
    private int array modecount
   
    private button array modebutton
    private string array buttonexec
   
    private int array ArenaButton
   
    private constant int BUTTONS = 10 // per dialog
   
    define GetButton(p, n) = {modebutton[n + BUTTONS*p]}
    define GetBtnExec(p, n) = {buttonexec[n + BUTTONS*p]}
   
    private int AddButton(int p, string text, string exec)
    {
        int i = modecount[p]
        GetBtnExec(p, i) = exec
        GetButton(p, i) = DialogAddButton(modedialog[p], text, 0)
        modecount[p] ++
        return i + BUTTONS*p
    }
   
    private void ResetD(int p, string text)
    {
        modecount[p] = 0
        DialogClear(modedialog[p])
        DialogSetMessage(modedialog[p], "Extra modes:")
    }
   
    private int GetAnAIPlayer()
    {
        random_count = 0
        int i; for(i=0; i<PLAYERS; i++)
        {
            if (ai[i] || !here[i]) && View[i].battle_index==0 {random_sort[random_count++] = i}
        }
       
        if random_count < 0 {return -1}
            else {return random_sort[GetRandomInt(0,random_count-1)]}
    }
   
    private int clicking_player
   
    private void PracticeExe()
    {
        int opp = GetAnAIPlayer()
        int p = clicking_player
        if opp < 0 {DisplayTextToPlayer(Player(p), 0,0, "|cffff2200No AI opponents found!|r")}
        else
        {
            HideTexts(p)
            tArena a = PrepareFight(p, opp, CData[selx[p]][selz[p]].id, 'h00L', -1)
            a.practise = true
            a.pl[0].immortal = true
            a.pl[1].immortal = true
        }
    }
   
    private void SurvivalExe()
    {
        int opp = GetAnAIPlayer()
        int p = clicking_player
        if opp < 0 {DisplayTextToPlayer(Player(p), 0,0, "|cffff2200No AI opponents found!|r")}
        else
        {
            HideTexts(p)
            tArena a = PrepareFight(p, opp, CData[selx[p]][selz[p]].id, 0, -1)
            a.survival = true
        }
    }
   
    private void onClick()
    {
        dialog dial=GetClickedDialog()
        button btn=GetClickedButton()
        int p; for(p=0; p<PLAYERS; p++) {exitwhen (modedialog[p] == dial)}
        int i; for(i=0; i<modecount[p]; i++) {exitwhen(GetButton(p, i) == btn)}
       
        string path = GetBtnExec(p, i)
        if path!="Back"
        {
            clicking_player = p
            ExecuteFunc(path)
        }
       
        dial=null; btn=null
    }
   
    private void ArenaClickExe() {} // WTF
   
    private void DefaultDialog(int i)
    {
        ResetD(i, "Extra modes:")
        AddButton(i, "Practice Mode", PracticeExe.name)
        AddButton(i, "Survival Mode", SurvivalExe.name)
        string s="Random"
        if PreferredArena[i] > 0 {s = Arena[PreferredArena[i]].name}
        ArenaButton[i] = AddButton(i, "Arena: "+s, ArenaClickExe.name+"2")
        AddButton(i, "Back", "Back")
    }
   
    private void ArenaClickExe2()
    {
        int p = clicking_player
        PreferredArena[p]++; if PreferredArena[p] >= LibFight_count {PreferredArena[p]=0}
        DefaultDialog(p)
        DialogDisplay(Player(p), modedialog[p], true)
    }
   
    private void DelayedInit()
    {
        int i; for(i=0; i<PLAYERS; i++)
        {
            modedialog[i] = DialogCreate()
            PreferredArena[i] = 0
            trigger t = CreateTrigger()
            TriggerAddAction(t, function onClick)
            TriggerRegisterDialogEvent(t, modedialog[i])
            //
            DefaultDialog(i)
        }
    }
   
    void ModeDialog(int p) // Esc
    {
        if View[p].battle_index != 0 {return}
       
        DialogDisplay(Player(p), modedialog[p], true)
    }
   
    private void Init()
    {
        TimerStart(CreateTimer(), 1., false, function DelayedInit)
    }
endlibrary

function InitTrig_Lib_Modes takes nothing returns nothing
endfunction
Commands info board shown on Escape; the particular movelists are in the characters' triggers
//TESH.scrollpos=247
//TESH.alwaysfold=0
library LibMovelist initializer Init uses LibMisc, MAIN, LibCamera
    private constant int CHCOUNT = 30, TXCOUNT = 45
    constant string Uses_rage = " |cffdd5511RAGEART"
    constant string Uses_1bar = " [|cff22aaeeBar|r]"
    constant string Uses_2bars = " [|cff22aaeeBar|r][|cff22aaeeBar|r]"
    constant string Uses_3bars = " [|cff22aaeeBar|r][|cff22aaeeBar|r][|cff22aaeeBar|r]"
   
    private constant int MB_COLUMNS = 20
    private constant int MB_ROWS = 20
   
    private constant string arrow_icon_up = "icons\\up.blp"
    private constant string arrow_icon_down= "icons\\dwn.blp"
    private constant string arrow_icon_left = "icons\\lft.blp"
    private constant string arrow_icon_right = "icons\\rgt.blp"
   
    /*
    private constant string arrow_icon_up = "war3mapimported\\up.blp"
    private constant string arrow_icon_down= "war3mapimported\\dwn.blp"
    private constant string arrow_icon_left = "war3mapimported\\lft.blp"
    private constant string arrow_icon_right = "war3mapimported\\rgt.blp"
    */

    private constant int MAX_COL = 1
    private constant real ml_width = .3//.45
   
    private string array mv[CHCOUNT][TXCOUNT]
    private texttag array tt[TXCOUNT]
    private int array count
   
    multiboard array movelist_board
    private int init_counter
    void InitMoveBoard(int id) { // id is array index of the character
        multiboard m = CreateMultiboard(); movelist_board[id] = m
        MultiboardDisplay(m, false); MultiboardSetTitleText(m, "MOVELIST")
        //
        int i, j; int x=0, y=0, z=0 // z is 0 or 1 column, x is for subcolumns
        multiboarditem mbi
        MultiboardSetColumnCount(m, MB_COLUMNS)
        MultiboardSetRowCount(m, MB_ROWS)
        MultiboardSetItemsWidth(m, 0)
        int last_col_count=0
       
        for(i=0; i<count[id]; i++)
        {
            //BJDebugMsg(I2S(i))
            x = 0
            string s = mv[id][i]; int len = StringLength(s); string txt=""
            real total_width = 0
            bool flag = false
                // find arrows
                for(j=0; j<len; j++)
                {
                    string c = SubString(s,j,j+1)
                   
                    if flag
                    {
                        mbi = MultiboardGetItem(m,y,x + z*last_col_count)
                        MultiboardSetItemStyle(mbi,false,true)
                        if c=="d" {MultiboardSetItemIcon(mbi, arrow_icon_down)}
                        if c=="u" {MultiboardSetItemIcon(mbi, arrow_icon_up)}
                        if c=="b" {MultiboardSetItemIcon(mbi, arrow_icon_left)}
                        if c=="f" {MultiboardSetItemIcon(mbi, arrow_icon_right)}
                        MultiboardSetItemWidth(mbi, .01)
                        MultiboardReleaseItem(mbi)
                        x++; total_width += .01
                        flag = false
                    }
                    else
                    {
                        if c=="[" && SubString(s,j+1,j+2)!="|"  // next is arrow
                        {
                            flag = true
                            if x>0 {x++} elseif x==0 && txt!="" {x++} // the bug with text being replaced by icon
                            txt=""
                        }
                        else {
                            txt += c
                            mbi = MultiboardGetItem(m,y,x + z*last_col_count)
                            MultiboardSetItemStyle(mbi,true,false)
                            MultiboardSetItemWidth(mbi, .02*StringLength(txt)/3)
                            total_width += .02/3
                            MultiboardSetItemValue(mbi, txt)
                            MultiboardReleaseItem(mbi)
                        }
                    }
                }
           
            // next row in the list
            if z<MAX_COL {
                last_col_count = x+2
                mbi = MultiboardGetItem(m,y,x+1)
                MultiboardSetItemWidth(mbi, ml_width - total_width)
                MultiboardSetItemStyle(mbi,true,false)
                MultiboardSetItemValue(mbi, " ")
                MultiboardReleaseItem(mbi)
            } else {
                mbi = MultiboardGetItem(m,y,x+1+last_col_count)
                MultiboardSetItemWidth(mbi, ml_width - (total_width + last_col_count * 0.02/3))
                MultiboardSetItemStyle(mbi,true,false)
                MultiboardSetItemValue(mbi, " ")
                MultiboardReleaseItem(mbi)
                last_col_count = 0
            }
            z++; if z>MAX_COL {z=0; y++}
           
        }
        //
        m=null; mbi=null
    }
    void InitMoveBoard_tick() {
        init_counter++
        if init_counter >= CHCOUNT {DestroyTimer(GetExpiredTimer())}
        elseif init_counter >= 0 {
            InitMoveBoard(init_counter)
        }
    }
    void InitMoveBoard_start() {
        init_counter = -6
        TimerStart(CreateTimer(),0.1,true,function InitMoveBoard_tick)
    }
   
    // old
    /*
    void BoardMovelist(int p, int id)
    {
        //BJDebugMsg("Print movelist for " + GetPlayerName(Player(p)))
        ClearTextMessages()
        id = LoadInteger(hash,id,HASH_CHAR_ID)
       
        MultiboardClear(mb[p])
        DestroyMultiboard(mb[p]); mb[p] = CreateMultiboard(); MultiboardSetTitleText(mb[p], "MOVELIST")
        MultiboardMinimize(mb[p],true)
       
        multiboard m = mb[p]; int i, j; int x=0, y=0, z=0 // z is 0 or 1 column, x is for subcolumns
        multiboarditem mbi
        MultiboardSetColumnCount(m, MB_COLUMNS)
        MultiboardSetRowCount(m, MB_ROWS)
        MultiboardSetItemsWidth(mb[p],0)
        MultiboardDisplay(m, false)
       
        int last_col_count=0
       
        for(i=0; i<count[id]; i++)
        {
            //BJDebugMsg(I2S(i))
            x = 0
            string s = mv[id][i]; int len = StringLength(s); string txt=""
            real total_width = 0
            bool flag = false
           
                // find arrows
                for(j=0; j<len; j++)
                {
                    string c = SubString(s,j,j+1)
                   
                    if flag
                    {
                        mbi = MultiboardGetItem(m,y,x + z*last_col_count)
                        MultiboardSetItemStyle(mbi,false,true)
                        if c=="d" {MultiboardSetItemIcon(mbi, arrow_icon_down)}
                        if c=="u" {MultiboardSetItemIcon(mbi, arrow_icon_up)}
                        if c=="b" {MultiboardSetItemIcon(mbi, arrow_icon_left)}
                        if c=="f" {MultiboardSetItemIcon(mbi, arrow_icon_right)}
                        MultiboardSetItemWidth(mbi, .01)
                        MultiboardReleaseItem(mbi)
                        x++; total_width += .01
                        flag = false
                    }
                    else
                    {
                        if c=="[" && SubString(s,j+1,j+2)!="|"  // next is arrow
                        {
                            flag = true
                            if x>0 {x++} elseif x==0 && txt!="" {x++} // the bug with text being replaced by icon
                            txt=""
                        }
                        else {
                            txt += c
                            mbi = MultiboardGetItem(m,y,x + z*last_col_count)
                            MultiboardSetItemStyle(mbi,true,false)
                            MultiboardSetItemWidth(mbi, .02*StringLength(txt)/3)
                            total_width += .02/3
                            MultiboardSetItemValue(mbi, txt)
                            MultiboardReleaseItem(mbi)
                        }
                    }
                }
           
            // next row in the list
            if z<MAX_COL {
                last_col_count = x+2
                mbi = MultiboardGetItem(m,y,x+1)
                MultiboardSetItemWidth(mbi, ml_width - total_width)
                MultiboardSetItemStyle(mbi,true,false)
                MultiboardSetItemValue(mbi, " ")
                MultiboardReleaseItem(mbi)
            } else {
                mbi = MultiboardGetItem(m,y,x+1+last_col_count)
                MultiboardSetItemWidth(mbi, ml_width - (total_width + last_col_count * 0.02/3))
                MultiboardSetItemStyle(mbi,true,false)
                MultiboardSetItemValue(mbi, " ")
                MultiboardReleaseItem(mbi)
                last_col_count = 0
            }
            z++; if z>MAX_COL {z=0; y++}
           
        }
       
        MultiboardDisplay(m, GetPlayerId(GetLocalPlayer()) == p)
        MultiboardMinimize(m,false)
        m = null
    }*/

   
    private int tempp
   
    /*void ShowThemDamnBoards()
    {
        int i
        for(i=0; i<6; i++)
        {
            if here[i] && !ai[i] && View[i].battle_index > 0 && GetLocalPlayer()==Player(i)
            {
                MultiboardDisplay(mb[i], true)
            }
        }
    }*/

   
    void PrintSecondList()
    {
        int p = tempp, id
        tArena a = Arena[IAbsBJ(View[p].battle_index)]
        int i=a.pl[0].owner; id = GetUnitTypeId(a.pl[0].mdl); if i==p {i=a.pl[1].owner; id = GetUnitTypeId(a.pl[1].mdl)}
        /*BJDebugMsg("second list")
        BJDebugMsg("i = "+I2S(i))
        if here[i] {BJDebugMsg("here")}
        if !ai[i] {BJDebugMsg("!ai")}*/

        //if here[i] && !ai[i] {BoardMovelist(i, id)}
        //
        //ShowThemDamnBoards()
    }
   
    void HideAllMovelistsFor(int p) {
        int i
        if GetLocalPlayer()==Player(p) {
            for(i=0; i<CHCOUNT; i++) {
                MultiboardDisplay(movelist_board[i], false)
            }
        }
    }
   
    void PrintMovelist(int p, int id)
    {
        tempp=p; //ExecuteFunc(PrintSecondList.name)
        //BoardMovelist(p, id)
        int index = LoadInteger(hash,id,HASH_CHAR_ID)
        if GetLocalPlayer()==Player(p) {
            MultiboardDisplay(mb[p], false)
            MultiboardDisplay(movelist_board[index], true)
        }
        /*
        ClearTextMessages()
        int i;
        id = LoadInteger(hash,id,HASH_CHAR_ID)
        for(i=0; i<count[id]; i++)
        {
            DisplayTimedTextToPlayer(Player(p), 0,0, 30, mv[id][i])
        }
        */

    }
   
    void CMove(int id, string s)
    {
        id = GetCharIndex(id)
        mv[id][count[id]++] = s
    }
   
    private void Init()
    {
        ExecuteFunc(InitMoveBoard_start.name)
        int i; for(i=0; i<CHCOUNT; i++)
        {
            count[i] = 0
        }
        //
        for(i=0; i<TXCOUNT; i++)
        {
            tt[i] = CreateTextTag()
            SetTextTagPermanent(tt[i], true)
            SetTextTagVisibility(tt[i], false)
        }
    }
endlibrary

function InitTrig_Lib_Movelist takes nothing returns nothing
endfunction
Combat info board with life bars etc.
//TESH.scrollpos=146
//TESH.alwaysfold=0
library LibMultiboard initializer Init uses LibFight
    //constant string LIFE_COLOR = "|cff22bb00"
    constant string HURT_COLOR = "|cffcc1100"
    constant string EMPTY_COLOR = "|cff777777"
    constant string MANA_COLOR = "|cff5533cc"
    constant string WHITE_COLOR = "|cffffffff"
    constant string WON_COLOR = "|cffffcc00"
    constant string POISON_COLOR = "|cffff00ff"
    constant string ROUND_CHAR = "*"
    constant int BAR_LENGTH = 85
    constant int MANA_BAR_LENGTH = 333
    constant real BOARD_TICK = .13
    constant string s100 = "llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll"
    define Bar(n) = {SubString(s100,0,n)}
    private int ManaGlow
    private string array MANA_COLOR_LIGHT
    private string array mana_flash_color
   
    string LifeColor(int perc)
    {
        if perc > 75 {return "|cff00ff00"}
        elseif perc > 66 {return "|cffaaee00"}
        elseif perc > 50 {return "|cffbbcc00"}
        elseif perc > 33 {return "|cffccaa00"}
        elseif perc > 25 {return "|cffdd8800"}
        return "|cffee7700"
    }
   
    string ManaBarLeft(int bn, int mp, int max) //123 mp/max
    {
        if mp >= bn*MANA_BAR_LENGTH
        {
            return WHITE_COLOR+"["+MANA_COLOR_LIGHT[ManaGlow+bn*3]+Bar(MANA_BAR_LENGTH*BAR_LENGTH/MAX_MANA -1)+WHITE_COLOR+"]"
        }
        elseif mp <= (bn-1)*MANA_BAR_LENGTH
        {
            return EMPTY_COLOR + Bar(BAR_LENGTH*MANA_BAR_LENGTH/MAX_MANA)
        }
        return MANA_COLOR + Bar(R2I(.97 + (mp - MANA_BAR_LENGTH*(bn-1))*BAR_LENGTH/MAX_MANA)) + EMPTY_COLOR + Bar(R2I((bn*MANA_BAR_LENGTH - mp)*BAR_LENGTH/MAX_MANA + .97))
    }
   
    string ManaBarRight(int bn, int mp, int max) //123 mp/max
    {
        if mp >= bn*MANA_BAR_LENGTH
        {
            return WHITE_COLOR+"["+MANA_COLOR_LIGHT[ManaGlow+bn*3]+Bar(MANA_BAR_LENGTH*BAR_LENGTH/MAX_MANA -1)+WHITE_COLOR+"]"
        }
        elseif mp <= (bn-1)*MANA_BAR_LENGTH
        {
            return EMPTY_COLOR + Bar(BAR_LENGTH*MANA_BAR_LENGTH/MAX_MANA)
        }
        return EMPTY_COLOR + Bar(R2I((bn*MANA_BAR_LENGTH - mp)*BAR_LENGTH/MAX_MANA +.97)) + MANA_COLOR + Bar(R2I(.97 + (mp - MANA_BAR_LENGTH*(bn-1))*BAR_LENGTH/MAX_MANA))
    }
   
    void SetBars(tArena a)
    {
        multiboarditem m
        int i; for(i=0; i<2; i++)
        {
            int lpc,mpc,hbc,lrc,mrc,ppc
            tChar pt = a.pl[i]
            tChar op = a.pl[1-i]
            int p = pt.owner
            int psn = IMinBJ(pt.poison, pt.life)
           
            lpc = (BAR_LENGTH*(pt.life - psn))/MAX_LIFE
            mpc = (BAR_LENGTH*pt.mana)/MAX_MANA
            hbc = (BAR_LENGTH*pt.hurt)/MAX_LIFE
            ppc = (BAR_LENGTH*psn)/MAX_LIFE
            lrc = BAR_LENGTH - (lpc+hbc+ppc)
            mrc = BAR_LENGTH - (mpc)
           
            if GetLocalPlayer()==Player(p) {
                MultiboardMinimize(mb[p], true)
                MultiboardDisplay(mb[p], true)
            }
           
            m=MultiboardGetItem(mb[p], 0, 0)
            MultiboardSetItemValue(m, LifeColor(100*pt.life/MAX_LIFE)+Bar(lpc)+POISON_COLOR+Bar(ppc)+HURT_COLOR+Bar(hbc)+EMPTY_COLOR+Bar(lrc)+"|r")
            MultiboardReleaseItem(m)
           
            m=MultiboardGetItem(mb[p], 1, 0)
            if pt.manaflash>0
            {
                pt.manaflash--
                MultiboardSetItemValue(m, mana_flash_color[ModuloInteger(pt.manaflash,7)]+Bar(BAR_LENGTH)+"|r")
            }
            else
            {
                MultiboardSetItemValue(m, ManaBarLeft(1,pt.mana,MAX_MANA)+ManaBarLeft(2,pt.mana,MAX_MANA)+ManaBarLeft(3,pt.mana,MAX_MANA)+"|r")
            }
            MultiboardReleaseItem(m)
            psn = IMinBJ(op.poison, op.life)
            lpc = (BAR_LENGTH*(op.life-psn))/MAX_LIFE
            mpc = (BAR_LENGTH*op.mana)/MAX_MANA
            hbc = (BAR_LENGTH*op.hurt)/MAX_LIFE
            ppc = (BAR_LENGTH*psn)/MAX_LIFE
           
            lrc = BAR_LENGTH - (lpc+hbc+ppc)
            mrc = BAR_LENGTH - (mpc)
           
            m=MultiboardGetItem(mb[p], 0, 2)
            MultiboardSetItemValue(m, EMPTY_COLOR+Bar(lrc)+HURT_COLOR+Bar(hbc)+POISON_COLOR+Bar(ppc)+LifeColor(100*op.life/MAX_LIFE)+Bar(lpc)+"|r")
            MultiboardReleaseItem(m)
           
            m=MultiboardGetItem(mb[p], 1, 2)
            if op.manaflash>0
            {
                op.manaflash--
                MultiboardSetItemValue(m, mana_flash_color[ModuloInteger(op.manaflash,7)]+Bar(BAR_LENGTH)+"|r")
            }
            else
            {
                MultiboardSetItemValue(m, ManaBarRight(3,op.mana,MAX_MANA)+ManaBarRight(2,op.mana,MAX_MANA)+ManaBarRight(1,op.mana,MAX_MANA)+"|r")
            }
            MultiboardReleaseItem(m)
           
            string wins1=WON_COLOR, wins2=EMPTY_COLOR; int w1 = a.pl[i].wins, w2 = a.pl[1-i].wins; int n=0
            loop
                if n==w1 {wins1+=EMPTY_COLOR}
                n++; wins1 += ROUND_CHAR; exitwhen(n>=a.maxwin)
            endloop
            loop
                if n==w2 {wins2+=WON_COLOR}
                n--; wins2 += ROUND_CHAR; exitwhen(n<=0)
            endloop
           
            m=MultiboardGetItem(mb[p], 1, 1)
            if a.practise { MultiboardSetItemValue(m, "PRACTISE") }
            elseif a.survival { MultiboardSetItemValue(m, "Survived "+I2S(a.pl[0].wins)) }
                else { MultiboardSetItemValue(m, wins1+" |r"+I2S(R2I(a.roundtime + .5))+" "+wins2) }
            MultiboardReleaseItem(m)
           
            m=MultiboardGetItem(mb[p], 2, 0)
            MultiboardSetItemValue(m, GetPlayerName(Player(a.pl[i].owner)) + " ("+GetUnitName(a.pl[i].mdl)+")")
            MultiboardReleaseItem(m)
           
            m=MultiboardGetItem(mb[p], 2, 2)
            MultiboardSetItemValue(m, "("+GetUnitName(a.pl[1-i].mdl)+") " + GetPlayerName(Player(a.pl[1-i].owner)))
            MultiboardReleaseItem(m)
           
            MultiboardMinimize(mb[p], false)
           
            if !a.Paused()
                {
                if pt.hurt_time > 0 {pt.hurt_time -= BOARD_TICK}
                else
                    {
                        if pt.hurt>0 {pt.hurt-=10}
                        if pt.hurt<0 {pt.hurt=0}
                    }
            }
        }
       
        int p1 = a.pl[0].owner
        int p2 = a.pl[1].owner
       
        if IsMultiboardDisplayed(mb[p1]) {MultiboardDisplay(mb[p1], false)}
        if IsMultiboardDisplayed(mb[p1]) {MultiboardDisplay(mb[p2], false)}
       
        if GetLocalPlayer() == Player(p2) {MultiboardDisplay(mb[p2], true)}
           elseif LocalArena() == a.index && GetLocalPlayer() == Player(p1) {MultiboardDisplay(mb[p1], true)}
       
        m=null
    }
   
    private int cycle
    private void Tick()
    {
        cycle++; if cycle>=LibFight_count
        {
            cycle=0
            ManaGlow++; if ManaGlow>15 {ManaGlow=0}
        }
        if Arena[cycle].taken && Arena[cycle].speed < 1000 && Arena[cycle].intro < 0 && !Arena[cycle].movelist {SetBars(Arena[cycle])}
    }
   
    private void TrueInit()
    {
        ManaGlow=0
        int g=0
        MANA_COLOR_LIGHT[g] = "|cff00ffff"; g++
        MANA_COLOR_LIGHT[g] = "|cff22ddff"; g++
        MANA_COLOR_LIGHT[g] = "|cff44bbff"; g++
        MANA_COLOR_LIGHT[g] = "|cff6699ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff8877ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffaa55ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffcc33ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffee11ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffff00ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffdd22ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffbb44ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff9966ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff7788ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff55aaff"; g++
        MANA_COLOR_LIGHT[g] = "|cff33ccff"; g++
        MANA_COLOR_LIGHT[g] = "|cff11eeff"; g++
        MANA_COLOR_LIGHT[g] = "|cff00ffff"; g++
        MANA_COLOR_LIGHT[g] = "|cff22ddff"; g++
        MANA_COLOR_LIGHT[g] = "|cff44bbff"; g++
        MANA_COLOR_LIGHT[g] = "|cff6699ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff8877ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffaa55ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffcc33ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffee11ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffff00ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffdd22ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffbb44ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff9966ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff7788ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff55aaff"; g++
        MANA_COLOR_LIGHT[g] = "|cff33ccff"; g++
        MANA_COLOR_LIGHT[g] = "|cff11eeff"; g++
        MANA_COLOR_LIGHT[g] = "|cff00ffff"; g++
        MANA_COLOR_LIGHT[g] = "|cff22ddff"; g++
        MANA_COLOR_LIGHT[g] = "|cff44bbff"; g++
        MANA_COLOR_LIGHT[g] = "|cff6699ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff8877ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffaa55ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffcc33ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffee11ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffff00ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffdd22ff"; g++
        MANA_COLOR_LIGHT[g] = "|cffbb44ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff9966ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff7788ff"; g++
        MANA_COLOR_LIGHT[g] = "|cff55aaff"; g++
        MANA_COLOR_LIGHT[g] = "|cff33ccff"; g++
        MANA_COLOR_LIGHT[g] = "|cff11eeff"; g++
        g=0
        mana_flash_color[g++] = "|cffaaaaff"
        mana_flash_color[g++] = "|cffbbbbee"
        mana_flash_color[g++] = "|cffccccdd"
        mana_flash_color[g++] = "|cffdddddd"
        mana_flash_color[g++] = "|cffeeeeee"
        mana_flash_color[g++] = "|cffffffff"
        mana_flash_color[g++] = "|cffeeeeee"
        mana_flash_color[g++] = "|cffdddddd"
        mana_flash_color[g++] = "|cffccccdd"
        mana_flash_color[g++] = "|cffbbbbee"
        mana_flash_color[g++] = "|cffaaaaff"
        int i; for(i=0; i<PLAYERS; i++)
        {
            InitBoard(i)
        }
        TimerStart(CreateTimer(),BOARD_TICK/LibFight_count,true,function Tick)
        cycle=0
    }
    private void Init() { TimerStart(CreateTimer(),0.56,false,function TrueInit) }
endlibrary

function InitTrig_Lib_Multiboard takes nothing returns nothing
endfunction
Hero leaves trail of mirror images
//TESH.scrollpos=24
//TESH.alwaysfold=0
library LibShade initializer Init uses LibFight, LibMisc, MAIN
    private timer ShadeTimer
    struct tShade
        unit mdl
        int arena
        real lifetime, maxtime
        real x,y,z,face
        real sx, sy, sz
    endstruct
   
    tShade array Shade
    private int count
    void NewShade(int arena, unit what, int fr, real sx, real sy, real sz, real time, string anim)
    {
        tShade sh = tShade.create()
        sh.arena = arena
        sh.face=GetUnitFacing(what)
        sh.maxtime = time; sh.lifetime = sh.maxtime
        sh.sx = sx;     sh.sy = sy;     sh.sz = sz
        sh.x = GetUnitX(what); sh.y=GetUnitY(what); sh.z=GetUnitFlyHeight(what)
        sh.mdl = CreateUnit(GetOwningPlayer(what), GetUnitTypeId(what), sh.x, sh.y, sh.face)
        SetUnitFlyHeight(sh.mdl, sh.z, 0)
        real tl = TimerGetRemaining(ShadeTimer)
        SetUnitTimeScale(sh.mdl, (fr*frame - .01)/tl)
        SetUnitAnimation(sh.mdl, anim)
        Shade[count++] = sh
    }
   
    void ClearShades()
    {
        int a=TEMP_ARENA
        int i=0
        loop
            exitwhen(i >= count)
            tShade sh = Shade[i]
            if sh.arena == a
            {
                RemoveUnit(sh.mdl); sh.destroy(); count--; Shade[i] = Shade[count]
            }
            else {i++}
        endloop
    }
   
    private void Tick()
    {
        int i=0
        loop
            exitwhen(i >= count)
            tShade sh = Shade[i]
            if !Arena[sh.arena].Paused()
            {
                if sh.lifetime > 0
                {
                    real t = frame
                    sh.lifetime -= t
                    sh.x += sh.sx*t; sh.y += sh.sy*t; sh.z += sh.sz*t
                    SetUnitPosition(sh.mdl, sh.x, sh.y)
                    SetUnitFlyHeight(sh.mdl, sh.z, 0)
                    SetUnitVertexColor(sh.mdl, 255,255,255, R2I(255*sh.lifetime/sh.maxtime))
                    SetUnitTimeScale(sh.mdl, 0.)
                    i++
                }
                else
                {
                    RemoveUnit(sh.mdl); sh.destroy(); count--; Shade[i] = Shade[count]
                }
            } else {i++}
        endloop
    }
   
    private void Init()
    {
        count=0
        ShadeTimer = CreateTimer()
        TimerStart(ShadeTimer, frame, true, function Tick)
    }
endlibrary

function InitTrig_Lib_Shade takes nothing returns nothing
endfunction
Changing music, preloading sounds
//TESH.scrollpos=147
//TESH.alwaysfold=0
library LibSound initializer Init uses MAIN, LibCamera
    constant int HASH_INTRO_1 = 201
    constant int HASH_INTRO_2 = 202
    constant int HASH_INTRO_3 = 203
    constant int HASH_VICTORY_1 = 301
    constant int HASH_VICTORY_2 = 302
    constant int HASH_VICTORY_3 = 303
    constant int HASH_SCREAMOFPAIN = 401
   
    private string array Music
    private int count
    private string array pr_path
    private int pr_count, mus_count
   
    private void KillSoundDone()
    {
        timer tm=GetExpiredTimer(); int h=GetHandleId(tm)
        KillSoundWhenDone(LoadSoundHandle(hash,h,0))
        FlushChildHashtable(hash,h); DestroyTimer(tm)
        tm=null
    }
   
    private sound sh
    sound PlaySoundOnUnit(int arena, string s, unit u)
    {
       
        if GetSoundFileDuration(s)<0.01 {return null}
       
        sh=CreateSound(s, false, false, true, 12700, 12700, "")
        PlaySoundOnUnitBJ(sh,100,u)
        timer tm=CreateTimer()
        SaveSoundHandle(hash,GetHandleId(tm),0,sh)
        TimerStart(tm, GetSoundDuration(sh), false, function KillSoundDone)
        if LocalArena() != arena
        {
            SetSoundVolume(sh, 0)
        }
        tm=null
        return sh
    }
   
    void HitSound(int arena, unit mdl, int dmg)
    {
        string path=""
        int a=GetRandomInt(0,99)
        if IsUnitType(mdl, UNIT_TYPE_MECHANICAL)
        {
           
                if a<20 {path="Sound\\Units\\Combat\\MetalMediumBashMetal1.wav"}
                elseif a<40 {path="Sound\\Units\\Combat\\MetalMediumBashMetal2.wav"}
                elseif a<60 {path="Sound\\Units\\Combat\\MetalMediumBashMetal3.wav"}
                elseif a<80 {path="Sound\\Units\\Combat\\MetalMediumChopMetal1.wav"}
                else {path="Sound\\Units\\Combat\\MetalHeavyChopMetal2.wav"}
        }
        elseif dmg>=100
        {
            if a<20 {path = "Abilities\\Weapons\\Arrow\\ArrowImpact.wav"}
            elseif a<40 {path = "Abilities\\Weapons\\GargoyleMissile\\GargoyleMissileHit1.wav"}
            elseif a<60 {path = "Abilities\\Weapons\\GuardTowerMissile\\GuardTowerMissileHit1.wav"}
            elseif a<80 {path = "Abilities\\Weapons\\huntermissile\\HeadHunterMissileHit1.wav"}
            else {path = "Abilities\\Weapons\\NecromancerMissile\\NecromancerMissileHit1.wav"}
        }
        else
        {
            if a<33 {path = "Abilities\\Weapons\\HarpyMissile\\HarpyMissileHit1.wav"}
            elseif a<66 {path = "Abilities\\Weapons\\LavaSpawnMissile\\LavaSpawnMissileDeath1.wav"}
            else {path="Abilities\\Weapons\\PriestMissile\\PriestMissileHit2.wav"}
        }
        if path!=null
        {
            PlaySoundOnUnit(arena, path, mdl)
        }
    }
   
    void ChangeMusic(int index)
    {
        int r = GetRandomInt(0, mus_count - 1)
       
        if index == 0 || LocalArena() == index
        {
            PlayMusic(Music[r])
        }
    }
   
    void ChangeMusicForPlayer(int index)
    {
        int r = GetRandomInt(0, mus_count - 1)
       
        if GetPlayerId(GetLocalPlayer()) == index
        {
            PlayMusic(Music[r])
        }
    }
   
    void PainScream(int arena, unit mdl)
    {
        int id=GetUnitTypeId(mdl)
        PlaySoundOnUnit(arena, LoadStr(hash,id,HASH_SCREAMOFPAIN), mdl)
    }
   
    void HintSound(bool b, string what)
    {
        local sound soundHandle = CreateSound(what, false, false, true, 12700, 12700, "")
        call StartSound(soundHandle)
        //call KillSoundWhenDone(soundHandle)
        if !b
        {
            SetSoundVolume(soundHandle, 0)
        }
        timer tm=CreateTimer()
        SaveSoundHandle(hash,GetHandleId(tm),0,soundHandle)
        TimerStart(tm, GetSoundDuration(soundHandle), false, function KillSoundDone)
        soundHandle = null; tm=null
    }
   
    void PlayerSound(int p, string what)
    {
        local sound soundHandle = CreateSound(what, false, false, true, 12700, 12700, "")
        call StartSound(soundHandle)
        //call KillSoundWhenDone(soundHandle)
        if p != GetPlayerId(GetLocalPlayer())
        {
            SetSoundVolume(soundHandle, 0)
        }
        timer tm=CreateTimer()
        SaveSoundHandle(hash,GetHandleId(tm),0,soundHandle)
        TimerStart(tm, GetSoundDuration(soundHandle), false, function KillSoundDone)
        soundHandle = null; tm=null
    }
   
    private sound temp_snd
    sound IntroSounds(int arena, unit mdl)
    {
        int id=GetUnitTypeId(mdl)
        int r=GetRandomInt(0,2)
        string what=LoadStr(hash,id,HASH_INTRO_1+r)
        return PlaySoundOnUnit(arena, what, mdl)
        /*temp_snd = CreateSound(what, false, false, true, 12700, 12700, "")
        call StartSound(temp_snd)
        call KillSoundWhenDone(temp_snd)
        if LocalArena() != arena
        {
            SetSoundVolume(temp_snd, 0)
        }
        return temp_snd*/

    }
   
    void WinSounds(int arena, unit mdl)
    {
        int id=GetUnitTypeId(mdl)
        int r=GetRandomInt(0,2)
        string what=LoadStr(hash,id,HASH_VICTORY_1+r)
        PlaySoundOnUnit(arena, what, mdl)
        /*local sound soundHandle = CreateSound(what, false, false, true, 12700, 12700, "")
        call StartSound(soundHandle)
        call KillSoundWhenDone(soundHandle)
        if LocalArena() != arena
        {
            SetSoundVolume(soundHandle, 0)
        }
        soundHandle = null*/

    }
   
    private timer pr_timer
   
    /*private void PreloadSoundFin()
    {
        int i=0
        if mus_count > 0 {PlayMusic(Music[--mus_count])}
       
        loop
            i++; exitwhen(i > 2)
            if pr_count < 1 {DestroyTimer(pr_timer); return}
            string path = pr_path[--pr_count]
            local sound soundHandle = CreateSound(path, false, false, true, 12700, 12700, "")
            //BJDebugMsg(path)
            call SetSoundVolume(soundHandle, 0)
            call StartSound(soundHandle)
            call KillSoundWhenDone(soundHandle)
            soundHandle = null
        endloop
    }*/

   
        /*void TimedSoundExp ()
        {
            timer tm=GetExpiredTimer()
            int h = GetHandleId(tm)
            int a = LoadInteger(hash,h,0)
           
            sound soundHandle = CreateSound(LoadStr(hash,h,1), false, false, true, 12700, 12700, "")
            StartSound(soundHandle)
            KillSoundWhenDone(soundHandle)
           
            if LocalArena() != a
            {
                SetSoundVolume(soundHandle, 0)
            }
           
            FlushChildHashtable(hash,h)
            DestroyTimer(tm)
           
            tm=null
            soundHandle=null
        }*/

       
        void TimedSound (real t, int index, string path)
        {
                /*timer tm=CreateTimer(); int h = GetHandleId(tm)
                SaveInteger(hash,h,0,index)
                SaveStr(hash,h,1,path)
                TimerStart(tm, t, false, function TimedSoundExp)
                tm=null*/

            sh = CreateSound(path, false, false, true, 12700, 12700, "")
            StartSound(sh)
            if LocalArena() != index
            {
                SetSoundVolume(sh, 0)
            }
            timer tm=CreateTimer()
            SaveSoundHandle(hash,GetHandleId(tm),0,sh)
            TimerStart(tm, GetSoundDuration(sh), false, function KillSoundDone)
        }
   
    string PreloadSound(string path)
    {
        pr_path[pr_count++] = path
        //Preload(path)
        return path
    }
   
    private void Init()
    {
        mus_count=0; pr_count=0
       
        Music[mus_count++] = "Sound\\Music\\mp3Music\\Doom.mp3"
        Music[mus_count++] = "Sound\\Music\\mp3Music\\HeroicVictory.mp3"
        Music[mus_count++] = "Sound\\Music\\mp3Music\\LichKingTheme.mp3"
        Music[mus_count++] = "Sound\\Music\\mp3Music\\Orc1.mp3"
        Music[mus_count++] = "Sound\\Music\\mp3Music\\NightElf3.mp3"
        Music[mus_count++] = "Sound\\Music\\mp3Music\\IllidansTheme.mp3"
        Music[mus_count++] = "Sound\\Music\\mp3Music\\DarkAgents.mp3"
       
        PreloadSound("Sound\\Interface\\Hint.wav")
       
        /*int i; for(i=0; i<count; i++)
        {
            PlayMusic(Music[i])
        }*/

        pr_timer = CreateTimer()
        //TimerStart(pr_timer, 0.1, true, function PreloadSoundFin)
    }
endlibrary

function InitTrig_Lib_Sound takes nothing returns nothing
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
library HideTerrain initializer Init uses LibCamera, LibFight
    private int count
    private unit array terr
   
    void AddTerr(unit u)
    {
        terr[count++] = u
    }
       
    private void Tick()
    {   int i; unit d
        tPlayerView tv = View[GetPlayerId(GetLocalPlayer())]
        int index = tv.battle_index; if index < 0 {index = -index}
        real cx = tv.X, cy = tv.Y
        real cangle = 360+tv.Facing
        for(i=0; i<count; i++)
        {
            d = terr[i]
            real x = GetUnitX(d), y = GetUnitY(d)
            real angle = 360+Atan2BJ(cy - y, cx - x)
            if AngleBetween(angle, cangle -55, cangle +55)
            {
                SetUnitVertexColor(d,255,255,255,80)
            }
            else
            {
                SetUnitVertexColor(d,255,255,255,255)
            }
        }
        d = null
    }
   
    private void ReplaceDestEnum()
    {
        destructable d = GetEnumDestructable()
        int id = GetDestructableTypeId(d), r
        if id == 'LTlt'
        {
            r = GetRandomInt(0,100)
            if r < 33 {id = 'h01D'}
            elseif r < 67 {id = 'h01E'}
            else {id= 'h01C'}
        }
        elseif id == 'FTtw'
        {
            r = GetRandomInt(0,100)
            if r < 33 {id = 'h01F'}
            elseif r < 67 {id = 'h01G'}
            else {id= 'h01H'}
        }
        elseif id == 'WTst'
        {
            r = GetRandomInt(0,100)
            if r < 33 {id = 'h01I'}
            elseif r < 67 {id = 'h01J'}
            else {id= 'h01K'}
        }
        elseif id == 'KTtw'
        {
            r = GetRandomInt(0,100)
            if r < 25 {id = 'h01L'}
            elseif r < 50 {id = 'h01M'}
            elseif r < 75 {id = 'h01N'}
            else {id= 'h01O'}
        }
        else
        {
            d = null; return
        }
        real x = GetDestructableX(d), y = GetDestructableY(d), face = GetRandomReal(0,360)
        RemoveDestructable(d)
        unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), id, x, y, face)
        real scale = GetRandomReal(0.9, 1.5); SetUnitScale(u, scale, scale, scale)
        //SetUnitVertexColor(u, 255,255,255,150)
        terr[count++] = u
        d = null; u=null
    }
   
    private void DelayedInit()
    {
        count = 0
        EnumDestructablesInRect(GetPlayableMapRect(), null, function ReplaceDestEnum)
    }
   
    private void Init()
    {
        TimerStart(CreateTimer(), 0.1, true, function Tick)
        TimerStart(CreateTimer(), 0.01, false, function DelayedInit)
    }
endlibrary

function InitTrig_Lib_Terrain_hide takes nothing returns nothing
endfunction
AddTerr other
  Events
    Time - Elapsed game time is 0.11 seconds
  Conditions
  Actions
    Set VariableSet u = Green house (small) 0014 <gen>
    Custom script: AddTerr(udg_u)
    Set VariableSet u = Red house (small) 0030 <gen>
    Custom script: AddTerr(udg_u)
    Set VariableSet u = Green house (small) 0028 <gen>
    Custom script: AddTerr(udg_u)
    Set VariableSet u = Green house (small) 0029 <gen>
    Custom script: AddTerr(udg_u)
    Set VariableSet u = Green house (large) 0031 <gen>
    Custom script: AddTerr(udg_u)
    Set VariableSet u = Spider statue 0032 <gen>
    Custom script: AddTerr(udg_u)
294/480 moves in all movelists, 61% done
This trigger contains states common for all chars, like blocking or jumping, as well as some useful functions used in character move code.
//TESH.scrollpos=81
//TESH.alwaysfold=0
library Common uses MAIN, LibFight, LibCamera, LibControls
    bool CmdBack(int p) { return Key[p][KEY_LEFT] }
    bool CmdForth(int p) { return Key[p][KEY_RIGHT] }
    bool CmdUp(int p) { return Key[p][KEY_UP] }
    bool CmdDown(int p) { return Key[p][KEY_DOWN] }
    bool CmdBackRelease(int p) { return nKey[p][KEY_LEFT] > .65 && !Key[p][KEY_LEFT] }
    bool CmdForthRelease(int p) { return nKey[p][KEY_RIGHT] > .65 && !Key[p][KEY_RIGHT] }
    bool CmdUpRelease(int p) { return nKey[p][KEY_UP] > .65 && !Key[p][KEY_UP] }
    bool CmdDownRelease(int p) { return nKey[p][KEY_DOWN] > .65 && !Key[p][KEY_DOWN] }
    bool FF(int p) {return pKey[p][KEY_RIGHT] > 0 && pKey[p][KEY_RIGHT] < nKey[p][KEY_RIGHT]}
    bool FFState(int s) {return s==STATE_NEUTRAL || s==STATE_RUNNING || s==STATE_FORTH_STEP}
    bool BB(int p) {return pKey[p][KEY_LEFT] > 0 && pKey[p][KEY_LEFT] < nKey[p][KEY_LEFT]}
    bool BBState(int s) {return s==STATE_NEUTRAL || s==STATE_BLOCK || s==STATE_BACK_STEP}
    bool Cmd(int p, int what) { return Command[p][what] > 0}
    bool DoubleCmd(tChar ch, int c1, int c2, int s1, int s2)
    {
        int p=ch.owner
        if ch.state==STATE_NEUTRAL && Cmd(p,c1) && Cmd(p,c2) {return true}
        if ch.state==s1 && Cmd(p,c2) && ch.fr<4 {return true}
        if ch.state==s2 && Cmd(p,c1) && ch.fr<4 {return true}
        return false
    }
    bool QCF(int p)
    {
        //BJDebugMsg(R2S(nKey[p][KEY_DOWN])+" "+R2S(nKey[p][KEY_RIGHT]))
        return nKey[p][KEY_DOWN] <= nKey[p][KEY_RIGHT] && nKey[p][KEY_DOWN] > KEY_TIME - 0.5 && !Key[p][KEY_DOWN]
    }
    bool QcfState(int val) {return (val==STATE_NEUTRAL || val==STATE_FORTH_STEP || val==STATE_RUNNING)}
   
    void StartMove(tArena a, int j) // slow motion on near-simultaneous moves
    {
        ResetCmds(a.pl[j].owner)
        a.pl[j].counterhit = true
    }
   
    void FaceForward(tChar pt, tChar op)
    {
        real dx = op.x-pt.x
        real dy = op.y-pt.y
        pt.face = Atan2BJ(dy,dx)
    }
   
    void BounceLaunch(tArena a, int i, int t)
    {
        tChar p = a.pl[1-i]
        FaceForward(p, a.pl[i])
        //p.Anim("Death Spin")
        if (p.state==STATE_HURT || p.state==STATE_FALLING) && p.statetype!="ld"
        {
            p.Scale(1.3); p.kndbounce = t
        }
    }
   
    void CommonFrames(tChar pt)
    {
        tArena a = Arena[pt.arena]
        int j = pt.index
        tChar op = a.pl[1-j]
        int p = pt.owner
       
        // Regen
        if pt.state < 100 {pt.PowerAdd(1)}
        if pt.poison > 0 {pt.poison--; pt.Hurt(1)}
        if pt.immortal && pt.hurt < 1 {pt.Heal(5); pt.PowerAdd(1)}
       
        // PHYSICS
        real dx, dy, angle
        //...
        if pt.statetype!="a" {pt.sx *= FRICTION; pt.sy *= FRICTION}
        elseif pt.z > 0 {
            if pt.state == STATE_HURT {pt.sz += G_FORCE_JUGGLE}
            else {pt.sz += G_FORCE}
        }
        //...
        // WALL
        dx=pt.x-a.cx
        dy=pt.y-a.cy
        real ddist = SquareRoot(dx*dx+dy*dy)
        if dx*dx + dy*dy >= a.radius*a.radius
        {/*
            angle = Atan2(dy,dx)
            pt.x = Cos(angle)*a.radius + a.cx
            pt.y = Sin(angle)*a.radius + a.cy*/

           
            pt.x = dx/ddist * (a.radius-5) + a.cx
            pt.y = dy/ddist * (a.radius-5) + a.cy
       
            // Wallsplat
            if (pt.state==STATE_HURT || pt.state==STATE_BLOCK_RECOVERY || pt.state==STATE_FALLING) && pt.wallsplat > 0
            {
                FlashyText(pt.arena, pt.mdl, pt.x, pt.y, 33, I2S(pt.wallsplat)+"!", 1.11)
                pt.Hurt(pt.wallsplat); pt.wallsplat=0
                real kbspeed = SquareRoot(pt.sx*pt.sx+pt.sy*pt.sy)
                pt.sx = -0.6 * kbspeed * dx/ddist
                pt.sy = -0.6 * kbspeed * dy/ddist
            }
        }
       
        // COLD
        if pt.state == STATE_FROZEN
        {
            if pt.timescale > 0 {pt.Scale(RMaxBJ(0, pt.timescale - .07))}
            if pt.z < 1 {pt.sx=0; pt.sy=0}
        }
       
        // throw facing
        if pt.state == STATE_HIT_THROW {FaceForward(pt, op)}
       
        // slow spell
        if pt.slowtime > 0 {pt.slowtime--}
       
        // no rage at high health
        if pt.life > RAGE_TRESHOLD && pt.rage_active {DestroyEffect(pt.rageart); pt.rage_active=false}
       
        // BOUNCE
        if pt.z<=1 && pt.statetype=="a" {
            pt.z=0
            if pt.state == STATE_NEUTRAL
            {
                pt.statetype="s"
                pt.Neutral()
                FaceForward(pt, op)
            }
            elseif (pt.sz < BOUNCE_TRESHOLD || pt.falldmg>0) && pt.state==STATE_HURT {
                pt.sz = -pt.sz * BOUNCE_MULTIPLIER; pt.sx *= BOUNCE_MULTIPLIER; pt.sy *= BOUNCE_MULTIPLIER
                if pt.falldmg > 0
                {
                    FlashyText(pt.arena, pt.mdl, pt.x, pt.y, 33, I2S(pt.falldmg)+"!", 1.11)
                    pt.Hurt(pt.falldmg)
                    pt.falldmg = 0
                } else {HitSound(pt.arena, pt.mdl, 0)}
            }
            elseif pt.state==STATE_HURT {pt.statetype="ld"; pt.Anim("Decay", 1.); HitSound(pt.arena, pt.mdl, 0)}
        }
       
        // DEAD
        if pt.Dead() {pt.state=STATE_DEAD; if pt.statetype!="ld" {pt.Anim("Death", 1.); pt.statetype="ld"}}
       
        // States with custom finish time, expiring on 0 into Neutral
        if pt.state==STATE_WHILE_STANDING || pt.state==STATE_BLOCK_RECOVERY || pt.state==STATE_BREAK_THROW || pt.state== STATE_INTRO || ((pt.state==STATE_HURT || pt.state==STATE_FROZEN) && pt.statetype!="a")
            {pt.fr--; if pt.fr<=0 {pt.Neutral()}}
        elseif pt.state==STATE_HIT_THROW
        {
            pt.fr--
            if !pt.parry && Cmd(pt.owner, CMD_W) && !Cmd(pt.owner, CMD_A) && !Cmd(pt.owner, CMD_S) && !Cmd(pt.owner, CMD_D) {pt.state=STATE_BREAK_THROW; ResetCmds(pt.owner)}
            elseif pt.fr==0 {pt.Neutral()}
        }
       
        if pt.statetype=="s"
        {
            if pt.state==STATE_NEUTRAL
            {
                if CmdDown(p)
                {
                    pt.statetype="c"
                    FaceForward(pt, op)
                    pt.Neutral()
                }
                elseif CmdUp(p)
                {
                    pt.state=STATE_JUMP_START
                    pt.fr=0
                    pt.Anim("Morph", 1.)
                }
                elseif CmdBack(p)
                {
                    pt.state=STATE_BLOCK
                    FaceForward(pt, op)
                    pt.fr=0
                    pt.Anim("Stand Defend", 1.)
                }
                elseif CmdBackRelease(p)
                {
                    pt.Anim("Stand Fast Slam", 2.)
                    FaceForward(pt, op)
                    pt.sx = -540*CosBJ(pt.face)
                    pt.sy = -540*SinBJ(pt.face)
                    pt.state = STATE_BACK_STEP
                    pt.fr=17
                }
                elseif CmdForth(p) || CmdForthRelease(p)
                {
                    pt.Anim("Stand Fast", 1)
                    FaceForward(pt, op)
                    pt.sx = 720*CosBJ(pt.face)
                    pt.sy = 720*SinBJ(pt.face)
                    pt.state = STATE_FORTH_STEP
                    pt.fr=17
                }
                elseif CmdDownRelease(p)
                {
                    pt.Anim("Stand Fast Gold", 1.2)
                    FaceForward(pt, op)
                    pt.sx = 720*CosBJ(pt.face-72)
                    pt.sy = 720*SinBJ(pt.face-72)
                    pt.state = STATE_DOWN_STEP
                    pt.fr=15
                }
                elseif CmdUpRelease(p)
                {
                    pt.Anim("Stand Fast Lumber", 1.2)
                    FaceForward(pt, op)
                    pt.sx = 720*CosBJ(pt.face+72)
                    pt.sy = 720*SinBJ(pt.face+72)
                    pt.state = STATE_UP_STEP
                    pt.fr=15
                }
            }
            elseif pt.state==STATE_BLOCK
            {
                pt.fr++
                if pt.fr>=9 && !CmdBack(p) {pt.Neutral()}
            }
            elseif pt.state==STATE_BACK_STEP || pt.state==STATE_UP_STEP || pt.state==STATE_DOWN_STEP
            {
                if pt.fr==9 {pt.sx=0; pt.sy=0; FaceForward(pt, op); pt.Anim("Stand Ready", 1.)}
                pt.fr--; if pt.fr==0 {pt.Neutral()}
            }
            elseif pt.state==STATE_FORTH_STEP
            {
                if pt.fr==7 {pt.sx=0; pt.sy=0; FaceForward(pt, op); pt.Anim("Stand Ready", 1.)}
                pt.fr--; if pt.fr==0 {pt.Neutral()}
               
                if pt.fr==10 && CmdForth(p)
                {
                    pt.state=STATE_RUNNING
                }
            }
            elseif pt.state==STATE_RUNNING
            {
                    //FaceForward(pt, op)
                    pt.sx = 540*CosBJ(pt.face)
                    pt.sy = 540*SinBJ(pt.face)
                   
                    if op.statetype=="ld" && a.CharDist() < 50 {op.Hurt(1)}
                   
                    if !CmdForth(p)
                    {
                        pt.state=STATE_FORTH_STEP
                        pt.fr=9
                    }
                    elseif CmdUp(p)
                    {
                        pt.statetype="a"
                        pt.Neutral()
                        pt.sz = 420
                        if CmdForth(p) {
                            pt.sx = 320*CosBJ(pt.face)
                            pt.sy = 320*SinBJ(pt.face)
                        }
                    }
            }
            elseif pt.state==STATE_JUMP_START
            {
                pt.fr++
                if pt.fr==9
                {
                    if !CmdUp(p) {
                        pt.Neutral()
                        pt.Anim("Stand Fast", 1)
                        FaceForward(pt, op)
                        pt.sx = 720*CosBJ(pt.face+72)
                        pt.sy = 720*SinBJ(pt.face+72)
                        pt.state = STATE_UP_STEP
                        pt.fr=15
                    }
                    else
                    {
                        pt.statetype="a"
                        pt.Neutral()
                        pt.sz = 420
                        if CmdForth(p) {
                            pt.sx = 320*CosBJ(pt.face)
                            pt.sy = 320*SinBJ(pt.face)
                        }
                        elseif CmdBack(p) {
                            pt.sx = -300*CosBJ(pt.face)
                            pt.sy = -300*SinBJ(pt.face)
                        }
                    }
                }
            }
        }
        elseif pt.statetype=="c"
        {
            if pt.state==STATE_NEUTRAL
            {
                if !CmdDown(p)
                {
                    pt.statetype="s"
                    pt.state=STATE_WHILE_STANDING
                    pt.fr=8
                    pt.Anim("Stand Slam", 1.)
                    //pt.Neutral()
                }
            }
            elseif pt.state==STATE_FALLING
            {
                //BJDebugMsg(I2S(j)+" is falling: "+I2S(pt.fr))
                pt.fr--; if pt.fr<=0 {pt.statetype="ld"; pt.Neutral()}
                pt.kndbounce--; if pt.kndbounce==0
                {
                    pt.statetype="a"; pt.z=10; pt.sz=420; pt.state=STATE_HURT
                }
            }
            elseif pt.state==STATE_STAND_UP
            {
                pt.fr++; if pt.fr == 15 {ResetCmds(p); pt.Neutral()}
            }
        }
        elseif pt.statetype=="ld"
        {
            pt.noclip = true
            if pt.state==STATE_NEUTRAL && pt.life>0
            {
                if Cmd(p, CMD_W) {pt.statetype="c"; pt.noclip=false; pt.state=STATE_STAND_UP; pt.fr=0; pt.Anim("Dissipate", 1.2)}
                elseif CmdBack(p) {pt.state=STATE_ROLL; pt.fr=0; pt.Anim("Dissipate Spin", 1.); FaceForward(pt, op)
                        pt.sx = -640*CosBJ(pt.face); pt.sy = -640*SinBJ(pt.face)}
                elseif CmdForth(p) {pt.state=STATE_ROLL; pt.fr=0; pt.Anim("Dissipate Spin", 1.); FaceForward(pt, op)
                        pt.sx = 540*CosBJ(pt.face); pt.sy = 540*SinBJ(pt.face)}
                elseif CmdDown(p) {pt.state=STATE_SIDEROLL; pt.fr=0; pt.Anim("Dissipate Spin", 1.); FaceForward(pt, op)
                        pt.sx = 540*CosBJ(pt.face-72); pt.sy = 540*SinBJ(pt.face-72)}
                elseif CmdUp(p) {pt.state=STATE_SIDEROLL; pt.fr=0; pt.Anim("Dissipate Spin", 1.); FaceForward(pt, op)
                        pt.sx = 540*CosBJ(pt.face+72); pt.sy = 540*SinBJ(pt.face+72)}
            }
            elseif pt.state==STATE_ROLL || pt.state==STATE_SIDEROLL
            {
                pt.fr++; if pt.fr==16 {pt.statetype="c"; pt.noclip=false; pt.state=STATE_STAND_UP; pt.fr=0; pt.Anim("Dissipate", 1.2)}
            }
        }
    }
endlibrary

function InitTrig_common_frames takes nothing returns nothing
endfunction
//TESH.scrollpos=18
//TESH.alwaysfold=0
scope SylvanusDATA initializer Init
    private constant int rawcode='h01W'
   
    private bool firstpick=true
   
    private constant int STATE_PUNCH = 101
    private constant int STATE_UPPERCUT = 102
    private constant int STATE_KICK = 103
    private constant int STATE_SPINKICK = 104
    private constant int STATE_BACKFLIP = 105
    private constant int STATE_FIREBALL = 106
    private constant int STATE_THROW = 107
    private constant int STATE_SLASH = 108
    private constant int STATE_SCATTERSHOT = 109
    private constant int STATE_ARROWRAIN = 110
   
    private void DelayedInit()
    {
        CMove(rawcode, "#                                                                  NORMALS"); CMove(rawcode, " ")
        CMove(rawcode, "W: Punch");    CMove(rawcode, "[dW: Uppercut")
        CMove(rawcode, "A: Whack");    CMove(rawcode, "[dA: Backflip")
        CMove(rawcode, "S: Kick");    CMove(rawcode, "[dS: Mid kick")
        CMove(rawcode, "D: Spin kick");    CMove(rawcode, "[dD: Sweep")
        CMove(rawcode, " ");    CMove(rawcode, "(ground) W: get up")
        CMove(rawcode, "[uW: Air whack");    CMove(rawcode, "[uA: Air whack")
        CMove(rawcode, "[uS: Air kick");    CMove(rawcode, "[uD: Air spin kick")
        CMove(rawcode, "#                                                                  SPECIALS"); CMove(rawcode, " ")
        CMove(rawcode, "A+D: Arrow rain"+Uses_2bars); CMove(rawcode, " ")
        CMove(rawcode, "[d,[f,A: Arrow shot"+Uses_1bar); CMove(rawcode, "W+D: Throw")
        CMove(rawcode, "[d,[f,A+S: Scatter shot"+Uses_2bars); CMove(rawcode, " ")
        CMove(rawcode, "#                                                                  ULTIMATE"); CMove(rawcode, " ")
        CMove(rawcode, "A+W"+Uses_rage)
        CMove(rawcode, " ")
        CMove(rawcode, "#                                                                  COMBO"); CMove(rawcode, " ")
        CMove(rawcode, "A,[bA: Hit and run");    CMove(rawcode, " ")
       
        if firstpick
        {
            firstpick=false
           
            SaveStr(hash,rawcode,HASH_INTRO_1,PreloadSound("Units\\Human\\Jaina\\JainaWarcry1.wav"))
            SaveStr(hash,rawcode,HASH_INTRO_2,PreloadSound("Units\\Human\\Jaina\\JainaWhat3.wav"))
            SaveStr(hash,rawcode,HASH_INTRO_3,PreloadSound("Units\\Human\\Jaina\\JainaYesAttack1.wav"))
           
            SaveStr(hash,rawcode,HASH_VICTORY_1,PreloadSound("Units\\Human\\Jaina\\JainaYesAttack1.wav"))
            SaveStr(hash,rawcode,HASH_VICTORY_2,PreloadSound("Units\\Human\\Jaina\\JainaYesAttack2.wav"))
            SaveStr(hash,rawcode,HASH_VICTORY_3,PreloadSound("Units\\Human\\Jaina\\JainaYesAttack1.wav"))
           
            SaveStr(hash,rawcode,HASH_SCREAMOFPAIN,PreloadSound("Units\\Human\\Jaina\\JainaOnFootDeath1.wav"))
            PreloadSound("Abilities\\Spells\\Human\\Blizzard\\BlizzardTarget3.wav")
        }
    }
   
    private void ScattershotOnHit()
    {
        tFireball ball = TriggeringFireball
        tArena a = Arena[ball.arena]
        tChar op = a.pl[1 - ball.owner]
        ball.lifetime = 0
        if op.state == STATE_HURT || op.state == STATE_FALLING {
            if op.Damage(ball.face, 15, ball.sx*.4, ball.sy*.4, 0, 10, 10, "h")
                {op.poison += 20}
        } else {
            if op.Damage(ball.face, 30, ball.sx*.4, ball.sy*.4, 0, 10, 10, "h")
                {op.poison += 100}
        }
    }
   
    private void ArrowOnHit()
    {
        tFireball ball = TriggeringFireball
        tArena a = Arena[ball.arena]
        tChar op = a.pl[1 - ball.owner]
        ball.lifetime = 0
        op.Damage(ball.face, 80, ball.sx*.4, ball.sy*.4, 0, 10, 10, "h")
    }
   
    private void RainOnHit()
    {
        tFireball ball = TriggeringFireball
        tArena a = Arena[ball.arena]
        tChar op = a.pl[1 - ball.owner]
        ball.lifetime = 0
        if op.Damage(ball.face, 5, 0, 0, 450, 30, 10, "h")
            {op.poison = 140}
    }
   
    void sylvanusFrames()
    {
        tChar pt = PT
        real cf=CosBJ(pt.face)
        real sf=SinBJ(pt.face)
        int j=pt.index
        int p = pt.owner
        tArena a = Arena[pt.arena]
        tChar op = a.pl[1-j]
        CommonFrames(pt)
        // start
        if (pt.statetype=="c" || pt.statetype=="a") && pt.state == STATE_BACKFLIP
            {
                pt.fr++
                if pt.fr == 42 {pt.z=0; pt.Neutral()}
                elseif pt.fr==10
                {
                    pt.counterhit = false
                    pt.statetype = "a"; pt.z=10; pt.sz = 360
                    pt.sx = -360*CosBJ(pt.face)
                    pt.sy = -360*SinBJ(pt.face)
                    if a.Hit(j, -30, 30, 100, "s")
                    {
                        op.Damage(pt.face, 60, cf*150, sf*150, 360, 30, 5, "m")
                    }
                }
            }
           
        if pt.statetype=="s"
        {
            // Rageart
            if pt.state == STATE_NEUTRAL && pt.fr==0 && Cmd(p,CMD_A) && Cmd(p,CMD_W) && pt.UseRage()
            {
                StartMove(a,j)
                pt.state = STATE_RAGEART
                pt.fr = 0
                pt.unbreakable = true
                a.Superpause(1.)
                pt.Anim("Spell", 1.); SetUnitTimeScale(pt.mdl, 1.)
                pt.PlaySound("Units\\NightElf\\Maiev\\MaievWhat2.wav")
                a.SetCamera(pt.x, pt.y, 50 - GetZ(pt.x,pt.y), 200, 350, pt.face + 180, false)
                a.custom_camera=32
            }
           
            elseif pt.state==STATE_RAGEART
            {
                pt.fr++
                if pt.fr==65 {pt.Neutral()}
                elseif pt.fr==47 {pt.unbreakable=false}
                elseif pt.fr==16 || pt.fr==30 || pt.fr==44
                {
                    pt.sx=cf*240; pt.sy=sf*240; pt.counterhit = false
                    if a.Hit(j, -30, 30, 135, "s")
                    {
                        if op.Ground() {op.Launch(420); op.falldmg = 32}
                        real ragesz=0; if pt.fr==44 {ragesz=360; op.falldmg += 90}
                        if !op.Damage(pt.face, pt.RageDmg(120), cf*240, sf*240, ragesz, 20, 7, "h") {pt.fr=45}
                    }
                    if pt.fr==44 {pt.sx = -cf*240; pt.sy = -sf*240}
                }
                elseif pt.fr==6 || pt.fr==20 || pt.fr==34
                {
                    //pt.unbreakable = false
                    a.custom_camera=0
                    if pt.fr==34 {pt.Anim("Attack Alternate Gold", 1.2); Explod(a.index, pt.mdl, "Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", "left foot", 10)}
                    if pt.fr==20 {pt.Anim("Attack Alternate", 1.2); Explod(a.index, pt.mdl, "Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", "right hand", 10)}
                    if pt.fr==6 {pt.Anim("Attack", 1.2); Explod(a.index, pt.mdl, "Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", "weapon", 10)}
                    if op.state==STATE_RAGEART
                    {
                        a.RageConflict()
                    }
                }
            }
           
            // Scatter shot [d,f+AS]
            if QCF(p) && QcfState(pt.state) && Cmd(p, CMD_A) && Cmd(p, CMD_S) && pt.UseBars(2)
            {
                StartMove(a,j)
                pt.state = STATE_SCATTERSHOT; pt.fr=0
                pt.Anim("Spell Throw", 1.)
                Explod(a.index, pt.mdl, "Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", "left hand", 10)
                FaceForward(pt, op)
                pt.sx=0; pt.sy=0
            }
            elseif pt.state==STATE_SCATTERSHOT
            {
                pt.fr++
                if pt.fr==32 {pt.Neutral()}
                elseif pt.fr==12
                {
                    int c
                    for(c=0; c<5; c++) {
                        tFireball ball = a.NewFireball(j, 'h01Y', 700, ScattershotOnHit.name, 2.)
                        ball.face = GetUnitFacing(pt.mdl) + (c-2)*15
                        ball.sx = CosBJ(ball.face)*700
                        ball.sy = SinBJ(ball.face)*700
                    }
                }
            }
           
            // Arrow shot [d,f+A]
            if QCF(p) && QcfState(pt.state) && Cmd(p, CMD_A) && pt.UseBars(1)
            {
                StartMove(a,j)
                pt.state = STATE_FIREBALL; pt.fr=0
                pt.Anim("Spell Throw", 1.)
                FaceForward(pt, op)
                pt.sx=0; pt.sy=0
            }
            elseif pt.state==STATE_FIREBALL
            {
                pt.fr++
                if pt.fr==32 {pt.Neutral()}
                elseif pt.fr==12
                {
                    a.NewFireball(j, 'h01X', 1000, ArrowOnHit.name, 2.)
                }
            }
           
            // arrow rain
            if DoubleCmd(pt, CMD_A, CMD_D, STATE_SLASH, STATE_SPINKICK) && pt.UseBars(2)
            {
                StartMove(a,j)
                AI_Straight(a, pt.face, op.owner, 9, 200, "h", "h", 30)
                pt.state = STATE_ARROWRAIN; pt.fr = 0
                pt.Anim("Spell Slam", 1.4)
                    FaceForward(pt, op)
            }
            elseif pt.state==STATE_ARROWRAIN {
                pt.fr++
                if pt.fr == 150 {pt.Neutral()}
                elseif pt.fr == 72 {pt.Anim("Spell Slam", 1.4)}
                elseif pt.fr==10 || pt.fr==30 || pt.fr==50 || pt.fr==70 || pt.fr==90 || pt.fr==110 {
                    Explod(a.index, pt.mdl, "Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", "weapon right", 10)                    
                }
                elseif pt.fr==15 || pt.fr==35 || pt.fr==55 || pt.fr==75 || pt.fr==95 || pt.fr==115 {
                    tFireball ball = a.NewFireball(j, 'h01Z', 700, RainOnHit.name, 2.)
                    ball.x = GetUnitX(op.mdl); ball.y = GetUnitY(op.mdl)
                    ball.sx = 0
                    ball.sy = 0
                    ball.z = 1000; ball.sz = -1200
                    ball.az = 0
                    ball.dz = 0
                }
            }
           
            // throw
            if DoubleCmd(pt, CMD_W, CMD_D, STATE_PUNCH, STATE_SPINKICK)
            {
                StartMove(a,j)
                AI_Straight(a, pt.face, op.owner, 9, 200, "h", "h", 30)
                pt.state = STATE_THROW; pt.fr = 0
                pt.Anim("Attack throw", 0.7)
                    FaceForward(pt, op)
            }
            elseif pt.state == STATE_THROW
            {
                pt.fr++
                if pt.fr == 64 {pt.Neutral()}
                elseif pt.fr == 40 {FaceForward(pt, op)}
                elseif pt.fr==9 + THROW_REACTION_TIME
                {
                    if op.state==STATE_HIT_THROW
                    {
                        if op.Damage(pt.face, 40, cf*220, sf*220, 540, 26, 14, "!")
                        {
                            //Explod(a.index, op.mdl, "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl", "chest", 32)
                            op.Anim("Death Throw", 1.)
                            op.noclip = true
                            //
                            //pt.face += 180
                            pt.sx = -120*CosBJ(pt.face)
                            pt.sy = -120*SinBJ(pt.face)
                            //
                            //a.SpinCamera(-cf*320, -sf*320, 0, 480, -15, 180, 30)
                        }
                    }
                    elseif op.state==STATE_BREAK_THROW
                    {
                        op.Anim("Stand Defend", 1.)
                        FaceForward(op, pt)
                        op.sx = -720*CosBJ(op.face)
                        op.sy = -720*SinBJ(op.face)
                        op.fr = 14
                    }
                }
                elseif pt.fr==9
                {
                    pt.sx=0; pt.sy=0; pt.counterhit = false
                    if a.Hit(j, -30, 30, 100, "h")
                    {
                        op.Damage(pt.face, 0, -cf*200, -sf*200, 0, THROW_REACTION_TIME, 20, "t")
                        pt.Scale(1.)
                    } else {pt.Scale(-.3)}
                }
                elseif pt.fr==2
                {
                    pt.sx = 540*CosBJ(pt.face)
                    pt.sy = 540*SinBJ(pt.face)
                    //Explod(a.index, pt.mdl, "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl", "right hand", 19)
                }
            }
           
            // Straight jab [W]
            if pt.state==STATE_NEUTRAL && Cmd(p, CMD_W)
            {
                StartMove(a,j)
                AI_Straight(a, pt.face, op.owner, 9, 200, "h", "h", 40)
                pt.state = STATE_PUNCH; pt.fr = 0
                pt.Anim("Attack Fast", 1.1)
                    FaceForward(pt, op)
            }
            elseif pt.state == STATE_PUNCH
            {
                pt.fr++
                if pt.fr == 20 {pt.Neutral()}
                elseif pt.fr==8
                {
                    pt.sx=0; pt.sy=0; pt.counterhit = false
                    if a.Hit(j, -30, 30, 90, "h")
                    {
                        op.Damage(pt.face, 60, cf*200, sf*200, 0, 9, 3, "h")
                    }
                }
                elseif pt.fr==4
                {
                    pt.sx = 540*CosBJ(pt.face)
                    pt.sy = 540*SinBJ(pt.face)
                }
            }
           
            // Bow slash [A]
            if pt.state==STATE_NEUTRAL && Cmd(p, CMD_A)
            {
                StartMove(