Reference
  Util\Color.txt
  Util\Input.txt
  Util\Status.txt
  Util\Party.txt
  Util\CommonDialog.txt
  Util\VisualEffect.txt
  Util\PartyStatusWindow.txt
  Util\WindowFrame.txt
  Util\System.txt
  Util\Audio.txt
End Reference

Import StdUtilLib
  Math = MathUtil
  String = StringUtil
End Import

Import StdWindow
  Wnd = Window
End Import

Constant BattleResult
  NotEnd = 0
  Won = 1
  Lost = 2
  Escaped = 3
End Constant

Constant HeroAnimation
  Standing = 1
  Dead = 2
  Pinch = 3
  Attacked = 4
  Attack1 = 5
  Attack2 = 6
  Attack3 = 7
  Guard = 8
  Invoking = 9
  Reserved10 = 10
  Reserved11 = 11
  Reserved12 = 12
  Reserved13 = 13
  Reserved14 = 14
  Reserved15 = 15
  Reserved16 = 16
End Constant

Dim g_BattleResult = BattleResult.NotEnd
Dim g_We = Nothing
Dim g_They = Nothing
Dim g_InfoWindow = Nothing
Dim g_MsgLines = 0
Dim g_Pics = Nothing
Dim g_Background = Nothing

Procedure OnException(message, call_stack)
  TraceException(message, call_stack)
  DoMsg("戦闘システムで問題が発生しました。", "戦闘は強制終了されます。")
  ClearScriptCache()
  Return BattleResult.NotEnd
End Procedure

Procedure GetAbility(who, what)
  Return who.Ability[what]
End Procedure

Procedure GetAP(who, what)
  Return who.DynAP[what]
End Procedure

Procedure GetDP(who, what)
  Return who.DynDP[what]
End Procedure

Procedure ResetAbility(who, what)
  If what = 0 OrElse what = Ability.Strength Then who.Ability[1] = who.Strength
  If what = 0 OrElse what = Ability.Vitality Then who.Ability[2] = who.Vitality
  If what = 0 OrElse what = Ability.Intelligence Then who.Ability[3] = who.Intelligence
  If what = 0 OrElse what = Ability.Agility Then who.Ability[4] = who.Agility
  If what = 0 OrElse what = Ability.Luck Then who.Ability[5] = who.Luck
End Procedure

Procedure ResetAP(who, what)
  If what = 0 OrElse what = APDP.Phys Then who.DynAP[1] = who.DamageAP[1]
  If what = 0 OrElse what = APDP.Fire Then who.DynAP[2] = who.DamageAP[2]
  If what = 0 OrElse what = APDP.Cold Then who.DynAP[3] = who.DamageAP[3]
  If what = 0 OrElse what = APDP.Elec Then who.DynAP[4] = who.DamageAP[4]
  If what = 0 OrElse what = APDP.Stat Then who.DynAP[5] = 0
End Procedure

Procedure ResetDP(who, what)
  If what = 0 OrElse what = APDP.Phys Then who.DynDP[1] = who.DamageDP[1]
  If what = 0 OrElse what = APDP.Fire Then who.DynDP[2] = who.DamageDP[2]
  If what = 0 OrElse what = APDP.Cold Then who.DynDP[3] = who.DamageDP[3]
  If what = 0 OrElse what = APDP.Elec Then who.DynDP[4] = who.DamageDP[4]
  If what = 0 OrElse what = APDP.Stat Then who.DynDP[5] = who.StatusDP
End Procedure

Procedure GetBattleResult()
  Return g_BattleResult
End Procedure

Procedure SetBattleResult(result)
  g_BattleResult = result
End Procedure

Procedure GetUs()
  Return g_We
End Procedure

Procedure GetLivingUs()
  Dim result = CreateList()
  For i = 1 To g_We.Count
    Dim who = g_We[i]
    If Not TestStatus(who, StatusEffect.Dead) Then
      result.Add(who)
    End If
  Next
  Return result
End Procedure

Procedure GetThem()
  Return g_They
End Procedure

Procedure GetLivingThem()
  Dim result = CreateList()
  For i = 1 To g_They.Count
    Dim who = g_They[i]
    If Not TestStatus(who, StatusEffect.Dead) Then
      result.Add(who)
    End If
  Next
  Return result
End Procedure

Procedure IsHero(who)
  Return g_We.Contains(who)
End Procedure

Procedure IsEnemy(who)
  Return g_They.Contains(who)
End Procedure

Procedure CreateInfoMsgWindow()
  If g_InfoWindow = Nothing Then
    Dim ss = GetScreenSize()
    g_InfoWindow = CreateEmptyInfoWindow((ss.X - 480) / 2, ss.Y - 140, 480, 120, 2, 4)
  End If
End Procedure

Procedure DestroyInfoMsgWindow()
  If g_InfoWindow <> Nothing Then
    DestroyControl(g_InfoWindow)
    g_InfoWindow = Nothing
    g_MsgLines = 0
  End If
End Procedure

Procedure AddInfoMsg(text)
  If g_MsgLines = 4 Then
    For i = 1 To 3
      GetInfoWindowCell(g_InfoWindow, 1, i).Text = _
        GetInfoWindowCell(g_InfoWindow, 1, i + 1).Text
    Next i
    g_MsgLines -= 1
  End If
  g_MsgLines += 1
  GetInfoWindowCell(g_InfoWindow, 1, g_MsgLines).Text = text
End Procedure

Procedure RemoveInfoMsg(left)
  For i = left + 1 To 4
    GetInfoWindowCell(g_InfoWindow, 1, i).Text = ""
  Next i
  g_MsgLines = left
End Procedure

Procedure ClearInfoMsg()
  For i = 1 To 4
    GetInfoWindowCell(g_InfoWindow, 1, i).Text = ""
  Next i
  g_MsgLines = 0
End Procedure

Procedure GetPic(who)
  If IsHero(who) Then
    Return g_Pics[g_We.IndexOf(who)]
  Else
    Return g_Pics[g_We.Count + g_They.IndexOf(who)]
  End If
End Procedure

Procedure UpdatePic(who)
  If IsHero(who) Then
    Dim ss = GetScreenSize()
    Dim pic = GetPic(who)
    pic.Y = ss.Y - 160 - 64
    pic.Children[2].Text = "H:" + who.HP
    pic.Children[3].Text = "M:" + who.MP
    Dim status_text = ""
    If TestStatus(who, StatusEffect.Charm) Then
      status_text = "魅了"
    ElseIf TestStatus(who, StatusEffect.Berserk) Then
      status_text = "狂暴"
    ElseIf TestStatus(who, StatusEffect.Confusion) Then
      status_text = "錯乱"
    ElseIf TestStatus(who, StatusEffect.Panic) Then
      status_text = "混乱"
    ElseIf TestStatus(who, StatusEffect.Paralysis) Then
      status_text = "マヒ"
    ElseIf TestStatus(who, StatusEffect.Blind) Then
      status_text = "暗闇"
    End If
    pic.Children[4].Text = status_text
  End If
End Procedure

Procedure SetPic(who, number)
  Dim pic = GetPic(who)
  If IsHero(who) Then
    If number > 0 Then
      Dim img = pic.Children[1]
      img.SrcX = ((number - 1) % 4) * 64
      img.SrcY = ((number - 1) / 4) * 64
      img.Color = MakeRGB(255, 255, 255)
      UpdatePic(who)
      Dim param_color
      If number = 2 Then
        param_color = Color.White
      ElseIf number = 3 Then
        param_color = Color.Yellow
      ElseIf number = 4 Then
        param_color = Color.Red
      End If
      For i = 2 To 4
        pic.Children[i].Color = param_color
      Next
      pic.Visible = True
    Else
      pic.Visible = False
    End If
  Else
    If number > 0 Then
      Dim img = pic.Children[1]
      img.Color = MakeRGB(255, 255, 255)
      pic.Visible = True
    Else
      pic.Visible = False
    End If
  End If
End Procedure

Procedure RainbowPic(who)
  Dim pic = GetPic(who).Children[1]
  Dim bg = g_Background.Children[1]
  bg.Color = MakeARGB(248, 255, 255, 255)
  pic.Color = MakeRGB(237, 26, 61)
  Sleep(50)
  pic.Color = MakeRGB(255, 183, 76)
  Sleep(50)
  bg.Color = MakeARGB(248, 0, 0, 0)
  pic.Color = MakeRGB(255, 212, 0)
  Sleep(50)
  pic.Color = MakeRGB(0, 128, 0)
  Sleep(50)
  bg.Color = MakeARGB(248, 255, 255, 255)
  pic.Color = MakeRGB(0, 154, 214)
  Sleep(50)
  pic.Color = MakeRGB(35, 71, 148)
  Sleep(50)
  bg.Color = MakeARGB(248, 0, 0, 0)
  pic.Color = MakeRGB(167, 87, 168)
  Sleep(50)
  pic.Color = MakeRGB(255, 255, 255)
End Procedure

Procedure FastBlinkPic(who)
  Dim pic = GetPic(who).Children[1]
  For i = 1 To 1
    pic.Visible = False
    Sleep(50)
    pic.Visible = True
    Sleep(50)
  Next
End Procedure

Procedure BlinkPic(who)
  Dim pic = GetPic(who).Children[1]
  For i = 1 To 3
    pic.Visible = False
    Sleep(100)
    pic.Visible = True
    Sleep(100)
  Next
End Procedure

Procedure IsCommandable(who)
  Return Not (TestStatus(who, StatusEffect.Dead) OrElse TestStatus(who, StatusEffect.Paralysis) OrElse _
    TestStatus(who, StatusEffect.Berserk) OrElse TestStatus(who, StatusEffect.Confusion) OrElse TestStatus(who, StatusEffect.Charm))
End Procedure

Procedure TryRunningAway(times_tried)
  If GetPrimaryActor().Area = [Field] || times_tried >= 4 Then Return True
  Dim we = GetLivingUs()
  Dim they = GetLivingThem()
  Dim who = Nothing
  Dim slowest = 10000
  Dim fastest = 0
  For i = 1 To we.Count
    who = we[i]
    slowest = Math.Min(slowest, GetAbility(who, Ability.Agility) * (who.PPLevel + who.MPLevel))
  Next
  For i = 1 To they.Count
    who = they[i]
    fastest = Math.Max(fastest, GetAbility(who, Ability.Agility) * (who.PPLevel + who.MPLevel))
  Next
  Return Math.GetRandom(0, slowest + fastest) < slowest
End Procedure

Procedure TryRemovingBadStatus(who, fx)
  If TestStatus(who, fx) AndAlso Math.GetRandom(1, 100) <= Math.Max(1, GetAbility(who, Ability.Luck) - 10) * 5 Then
    ResetStatus(who, fx)
    Return True
  Else
    Return False
  End If
End Procedure

Procedure Main(normal_battle, enemies)
  SetExceptionHandler(OnException)
  
  Dim times_tried_runaway = 0
  
  SetGameInfo("Battles", GetGameInfo("Battles") + 1)

  Dim ss = GetScreenSize()

  g_Background = Wnd.CreatePanel(Wnd.CreateFill( _
    0, 0, ss.X, ss.Y, MakeARGB(248, 0, 0, 0)))

  Dim level_counter = CreateList()
  Dim pm = [Party].Member
  g_We = CreateArrayList(pm.Count)
  For i = 1 To pm.Count
    g_We[i] = DuplicateElement(pm[i])
    ResetAbility(g_We[i], 0)
    ResetAP(g_We[i], 0)
    ResetDP(g_We[i], 0)
    level_counter.Add({0, 0})
  Next

  Dim enemy_names = CreateList()
  Dim same_enemy_counter = CreateDictionary()
  g_They = CreateArrayList(enemies.Count)
  For i = 1 To enemies.Count
    g_They[i] = DuplicateElement(enemies[i])
    ResetAbility(g_They[i], 0)
    ResetAP(g_They[i], 0)
    ResetDP(g_They[i], 0)
    If Not same_enemy_counter.Contains(enemies[i]) Then
      same_enemy_counter.Add(enemies[i], {})
      enemy_names.Add(enemies[i].Name)
    End If
    same_enemy_counter[enemies[i]].Add(g_They[i])
  Next
  For i = 1 To same_enemy_counter.Keys.Count
    Dim same_enemies = same_enemy_counter[same_enemy_counter.Keys[i]]
    If same_enemies.Count > 1 Then
      For j = 1 To same_enemies.Count
        same_enemies[j].Name += String.Chr(String.Asc("Ａ") + (j - 1))
      Next
    End If
  Next

  g_Pics = CreateList()

  Dim pic_bottom = ss.Y - 160
  For i = 1 To g_We.Count
    Dim img = Wnd.CreateImage(g_We[i].Texture, 0, 0, 0, 0, 64, 64)
    Dim img_pnl = Wnd.CreatePanel(0, 0, 128, 64)
    img_pnl.Add(img)
    img_pnl.Add(Wnd.CreateLabel(64, 24, 64, [F_SYS].LineHeight, 1, 0, [F_SYS], Color.White, ""))
    img_pnl.Add(Wnd.CreateLabel(64, 44, 64, [F_SYS].LineHeight, 1, 0, [F_SYS], Color.White, ""))
    img_pnl.Add(Wnd.CreateLabel(64,  4, 64, [F_SYS].LineHeight, 1, 0, [F_SYS], Color.White, ""))
    img_pnl.X = ss.X * i / (g_We.Count + 1) - 32
    img_pnl.Y = pic_bottom - 64
    g_Pics.Add(img_pnl)
    SetPic(g_We[i], 2)
    If Not TestStatus(g_We[i], StatusEffect.Dead) Then
      If g_We[i].HP >= g_We[i].MaxHP / 4 Then
        SetPic(g_We[i], 2)
      Else
        SetPic(g_We[i], 3)
      End If
    Else
      SetPic(g_We[i], 4)
    End If
  Next

  pic_bottom -= 64 + 60
  For i = 1 To g_They.Count
    Dim img = Wnd.CreateImage(g_They[i].Texture, 0, 0)
    Dim img_pnl = Wnd.CreatePanel(img)
    img_pnl.X = ss.X * i / (g_They.Count + 1) - img.Width / 2
    img_pnl.Y = pic_bottom - img.Height
    g_Pics.Add(img_pnl)
    SetPic(g_They[i], 1)
  Next

  CreateInfoMsgWindow()
  For i = 1 To enemy_names.Count
    AddInfoMsg(enemy_names[i] + "が現れた！")
    Sleep(1000 / enemy_names.Count)
  Next
  DestroyInfoMsgWindow()

  Dim battle_result = False

  Do 'until end of battle
  
Start_Deciding_Party_Action:
  
    Dim cmds = CreateList()
    
    Dim hero_index = 1
    Do While hero_index <= g_We.Count
      Dim cmd = ActionHero(g_We[hero_index])
      If cmd <> Nothing Then
        If cmd[2] <> 4 Then
          cmds.Add(cmd)
          hero_index += 1
        Else 'runaway
          If normal_battle Then
            CreateInfoMsgWindow()
            If g_We.Count > 1 Then
              AddInfoMsg(g_We[hero_index].Name + "たちは逃げ出した！")
            Else
              AddInfoMsg(g_We[hero_index].Name + "は逃げ出した！")
            End If
            Sleep(500)
            times_tried_runaway += 1
            If TryRunningAway(times_tried_runaway) Then
              DestroyInfoMsgWindow()
              SetBattleResult(BattleResult.Escaped)
              GoTo Exit_From_Battle
            Else
              AddInfoMsg("しかし　逃げ切れなかった！")
              Sleep(500)
              DestroyInfoMsgWindow()
              cmds.Clear()
              For i = 1 To g_We.Count
                cmds.Add({g_We[i], 0, 0, {}})
              Next
              GoTo Decided_Our_Actions
            End If
          Else
            DoMsg("逃げるわけにはいかない！")
            Continue Do
          End If
        End If
      ElseIf hero_index > 1 Then
        Do
          hero_index -= 1
          cmds.RemoveAt(hero_index)
        Loop While hero_index > 1 AndAlso Not IsCommandable(g_We[hero_index])
      End If
      If hero_index = 1 Then GoTo Start_Deciding_Party_Action
    Loop
    
Decided_Our_Actions:
    
    For i = 1 To cmds.Count
      If cmds[i][2] = 1 Then
        level_counter[i][1] += 1
      ElseIf cmds[i][2] = 2 Then
        level_counter[i][2] += 1
      End If
    Next
    
    Dim enemy_index = 1
    Do While enemy_index <= g_They.Count
      cmds.Add(ActionEnemy(g_They[enemy_index]))
      enemy_index += 1
    Loop
    
    Dim all_agilities = CreateList()
    For i = 1 To cmds.Count
      Dim the_agi = Math.Max(1, GetAbility(cmds[i][1], Ability.Agility) - 10) * 5
      Dim cmd_cost = 0
      If cmds[i][2] = 2 Then
        cmd_cost = cmds[i][1].MagicList[cmds[i][3]].Cost
      End If
      all_agilities.Add(Math.Max(1, the_agi - cmd_cost))
    Next
    
    Dim ordered_cmd = CreateList()
    Do While cmds.Count > 0
      Dim cmd_index = 1
      Dim agi_sum = 0
      For i = 1 To cmds.Count
        agi_sum += all_agilities[i]
      Next
      Dim agi_target = Math.GetRandom(0, agi_sum)
      For i = 1 To cmds.Count
        If agi_target <  all_agilities[i] Then
          cmd_index = i
          Exit For
        Else
          agi_target -= all_agilities[i]
        End If
      Next
      ordered_cmd.Add(cmds[cmd_index])
      cmds.RemoveAt(cmd_index)
      all_agilities.RemoveAt(cmd_index)
    Loop
    cmds = ordered_cmd

    CreateInfoMsgWindow()

    Do While cmds.Count > 0

      Dim the_cmd = cmds[1]
      Dim who = the_cmd[1]
      Dim cmd_type = the_cmd[2]
      Dim item_index = the_cmd[3]
      Dim targets = the_cmd[4]
      cmds.RemoveAt(1)
      
      If Not TestStatus(who, StatusEffect.Dead) AndAlso Not TestStatus(who, StatusEffect.Paralysis) AndAlso _
        Not (TestStatus(who, StatusEffect.Panic) AndAlso Math.GetRandom(0,2) = 0) AndAlso _
        Not TestStatus(who, StatusEffect.Protection) Then
        If cmd_type = 1 Then
          ApplyHeroMagic(who, [Attack], targets)
        ElseIf cmd_type = 2 Then
          ApplyHeroMagic(who, who.MagicList[item_index], targets)
        ElseIf cmd_type = 3 Then
          Dim the_tool = Nothing
          If item_index <= who.ToolList.Count Then
            the_tool = who.ToolList[item_index]
          Else
            Dim eq_list = CreateList()
            If who.Weapon <> Nothing Then eq_list.Add(who.Weapon)
            If who.Armor <> Nothing Then eq_list.Add(who.Armor)
            If who.Shield <> Nothing Then eq_list.Add(who.Shield)
            If who.Helmet <> Nothing Then eq_list.Add(who.Helmet)
            the_tool = eq_list[item_index - who.ToolList.Count]
          End If
          ApplyHeroMagic(who, the_tool, targets)
          If Not the_tool.Importance AndAlso the_tool.SingleUse Then
            If item_index <= who.ToolList.Count Then
              who.ToolList.RemoveAt(item_index)
            Else
              If who.Weapon = the_tool Then
                who.Weapon = Nothing
              ElseIf who.Armor = the_tool Then
                who.Armor = Nothing
              ElseIf who.Shield = the_tool Then
                who.Shield = Nothing
              ElseIf who.Helmet = the_tool Then
                who.Helmet = Nothing
              End If
              Equip(who)
            End If
          End If
        ElseIf cmd_type = 6 Then
          ApplyHeroMagic(who, who.CommandList[item_index], targets)
        End If
      End If

      ClearInfoMsg()
      If TestEndOfBattle() Then
        Exit Do
      End If
    Loop

    DestroyInfoMsgWindow()

    If TestEndOfBattle() Then
      Dim result = GetBattleResult()
      If result = BattleResult.Won Then
        If g_They[1].Tag = "E309" Then
          BeatFirst()
        ElseIf g_They[1].Tag = "E310" Then
          BeatSecond()
        ElseIf g_They[1].Tag = "E311" Then
          BeatThird()
          Exit Do
        Else
          SetGameInfo("Victories", GetGameInfo("Victories") + 1)
          ChangeBGM(Nothing)
          Sleep(1000)
          ChangeBGM(Music.Winning)
          Dim exp_sum = 0
          Dim money_sum = 0
          For i = 1 To g_They.Count
            Dim who = g_They[i]
            If TestStatus(who, StatusEffect.Dead) Then
              exp_sum += who.Experience
              money_sum += who.Money
            End If
          Next
          Dim won_msgs = CreateList()
          won_msgs.Add("やった！")
          If money_sum > 0 Then
            ReceiveMoney(money_sum)
            won_msgs.Add(HanToZen(money_sum) + [RVII].MoneyUnit + "　てにいれた")
          End If
          DoMsgV(won_msgs)
          Dim level_up_msgs = CreateList()
          Dim exp_ave = exp_sum / GetLivingUs().Count
          For i = 1 To g_We.Count
            Dim who = g_We[i]
            If Not TestStatus(who, StatusEffect.Dead) Then
              If level_counter[i][1] > 0 OrElse level_counter[i][2] > 0 Then
                who.PPExp += exp_ave * level_counter[i][1] / (level_counter[i][1] + level_counter[i][2])
                who.MPExp += exp_ave * level_counter[i][2] / (level_counter[i][1] + level_counter[i][2])
              Else
                who.PPExp += exp_ave / 2
                who.MPExp += exp_ave / 2
              End If
              who.PPExp = Math.Min(838860, who.PPExp) '0xCCCCC
              who.MPExp = Math.Min(838860, who.MPExp)
              Dim curr_pp_level = who.PPLevel
              For j = curr_pp_level + 1 To 99
                If GetMember([PPExp], Format("L{1:00}", j)) <= who.PPExp Then
                  who.PPLevel += 1
                  who.MaxHP += who.Vitality
                End If
              Next
              Dim curr_mp_level = who.MPLevel
              For j = curr_mp_level + 1 To 99
                If GetMember([MPExp], Format("L{1:00}", j)) <= who.MPExp Then
                  who.MPLevel += 1
                  who.MaxHP += who.Vitality
                  who.MaxMP += who.Intelligence
                End If
              Next
              If curr_pp_level < who.PPLevel Then
                level_up_msgs.Add(who.Name + "の攻撃レベルが上がった！")
              End If
              If curr_mp_level < who.MPLevel Then
                level_up_msgs.Add(who.Name + "の魔法レベルが上がった！")
              End If
            End If
          Next
          If level_up_msgs.Count > 0 Then
            Sleep(500)
            PlayAudio(Music.LevelUp)
            DoMsgV(level_up_msgs)
          End If
          Exit Do
        End If
      Else
        ChangeBGM(Nothing)
        FadeInProc(0, 0, 0, 1000)
        DoMsg("志半ばで　息絶えた")
        Exit Do
      End If
    End If
    
    For i = 1 To g_We.Count
      TryRemovingBadStatus(g_We[i], StatusEffect.Panic)
      TryRemovingBadStatus(g_We[i], StatusEffect.Confusion)
      TryRemovingBadStatus(g_We[i], StatusEffect.Charm)
      TryRemovingBadStatus(g_We[i], StatusEffect.Berserk)
      TryRemovingBadStatus(g_We[i], StatusEffect.Blind)
      TryRemovingBadStatus(g_We[i], StatusEffect.Paralysis)
      UpdatePic(g_We[i])
    Next
    For i = 1 To g_They.Count
      TryRemovingBadStatus(g_They[i], StatusEffect.Panic)
      TryRemovingBadStatus(g_They[i], StatusEffect.Confusion)
      TryRemovingBadStatus(g_They[i], StatusEffect.Charm)
      TryRemovingBadStatus(g_They[i], StatusEffect.Berserk)
      TryRemovingBadStatus(g_They[i], StatusEffect.Blind)
      TryRemovingBadStatus(g_They[i], StatusEffect.Paralysis)
      UpdatePic(g_They[i])
    Next

  Loop

Exit_From_Battle:

  For i = 1 To g_Pics.Count
    g_Pics[i].Dispose()
  Next
  g_Background.Dispose()

  For i = 1 To pm.Count
    Dim org = pm[i]
    Dim cln = g_We[i]
    org.MaxHP = cln.MaxHP
    org.MaxMP = cln.MaxMP
    org.HP = cln.HP
    org.MP = cln.MP
    org.PPExp = cln.PPExp
    org.MPExp = cln.MPExp
    org.PPLevel = cln.PPLevel
    org.MPLevel = cln.MPLevel
    org.Status = cln.Status And StatusEffect.Dead
    org.ToolList = cln.ToolList
    org.Weapon = cln.Weapon
    org.Armor = cln.Armor
    org.Shield = cln.Shield
    org.Helmet = cln.Helmet
  Next i

  Return GetBattleResult()

End Procedure

Procedure SelectRandomTarget(who, magic)

  Dim include_dead = False

  Dim range = magic.TargetRange
  If range = TargetRange.Own Then
    Return {who}
  ElseIf range = TargetRange.AllOfUs Or range = TargetRange.AllOfThem Then
    If Math.GetRandom(0, 2) = 0 Then
      Return GetUs()
    Else
      Return GetThem()
    End If
  ElseIf range = TargetRange.Everyone Then
    If IsHero(who) Then
      Return CombineList(GetUs(), GetThem())
    Else
      Return CombineList(GetThem(), GetUs())
    End If
  End If

  Dim all_items = CombineList(GetUs(), GetThem())
  Dim items = CreateList()
  For i = 1 To all_items.Count
    Dim target = all_items[i]
    If include_dead OrElse Not TestStatus(target, StatusEffect.Dead) Then
      items.Add(target)
    End If
  Next
  If items.Count > 0 Then
    Return {items[Math.GetRandom(0, items.Count) + 1]}
  Else
    Return {}
  End If

End Procedure

Procedure SelectAutoTarget(who, magic)

  Dim include_dead = False
  Dim inEnemies = False

  Dim range = magic.TargetRange
  If Not TestStatus(who, StatusEffect.Charm) Then
    If IsHero(who) Then
      If range = TargetRange.Own Then
        Return {who}
      ElseIf range = TargetRange.OneOfUs Then
        inEnemies = False
      ElseIf range = TargetRange.OneOfThem Then
        inEnemies = True
      ElseIf range = TargetRange.AllOfUs Then
        Return GetUs()
      ElseIf range = TargetRange.AllOfThem Then
        Return GetThem()
      Else 'If range = TargetRange.Everyone Then
        Return CombineList(GetUs(), GetThem())
      End If
    Else
      If range = TargetRange.Own Then
        Return {who}
      ElseIf range = TargetRange.OneOfUs Then
        inEnemies = True
      ElseIf range = TargetRange.OneOfThem Then
        inEnemies = False
      ElseIf range = TargetRange.AllOfUs Then
        Return GetThem()
      ElseIf range = TargetRange.AllOfThem Then
        Return GetUs()
      Else 'If range = TargetRange.Everyone Then
        Return CombineList(GetThem(), GetUs())
      End If
    End If
  Else
    If IsHero(who) Then
      If range = TargetRange.Own Then
        Return {who}
      ElseIf range = TargetRange.OneOfUs Then
        inEnemies = True
      ElseIf range = TargetRange.OneOfThem Then
        inEnemies = False
      ElseIf range = TargetRange.AllOfUs Then
        Return GetThem()
      ElseIf range = TargetRange.AllOfThem Then
        Return GetUs()
      Else 'If range = TargetRange.Everyone Then
        Return CombineList(GetThem(), GetUs())
      End If
    Else
      If range = TargetRange.Own Then
        Return {who}
      ElseIf range = TargetRange.OneOfUs Then
        inEnemies = False
      ElseIf range = TargetRange.OneOfThem Then
        inEnemies = True
      ElseIf range = TargetRange.AllOfUs Then
        Return GetUs()
      ElseIf range = TargetRange.AllOfThem Then
        Return GetThem()
      Else 'If range = TargetRange.Everyone Then
        Return CombineList(GetUs(), GetThem())
      End If
    End If
  End If

  Dim all_items = Nothing
  If inEnemies Then
    all_items = GetThem()
  Else
    all_items = GetUs()
  End If

  Dim items = CreateList()
  For i = 1 To all_items.Count
    Dim target = all_items[i]
    If include_dead OrElse Not TestStatus(target, StatusEffect.Dead) Then
      items.Add(target)
    End If
  Next
  
  If items.Count > 0 Then
    For i = 1 To items.Count
      If Math.GetRandom(0, 2) = 0 Then
        Return {items[i]}
      End If
    Next
    Return {items[Math.GetRandom(0, items.Count) + 1]}
  Else
    Return {}
  End If

End Procedure

Procedure SelectTarget(who, magic)

  Dim inEnemies = False

  Dim range = magic.TargetRange
  If IsHero(who) Then
    If range = TargetRange.Own Then
      Return {who}
    ElseIf range = TargetRange.OneOfUs Then
      inEnemies = False
    ElseIf range = TargetRange.OneOfThem Then
      inEnemies = True
    ElseIf range = TargetRange.AllOfUs Then
      Return GetUs()
    ElseIf range = TargetRange.AllOfThem Then
      Return GetThem()
    Else 'If range = TargetRange.Everyone Then
      Return CombineList(GetUs(), GetThem())
    End If
  Else
    If range = TargetRange.Own Then
      Return {who}
    ElseIf range = TargetRange.OneOfUs Then
      inEnemies = True
    ElseIf range = TargetRange.OneOfThem Then
      inEnemies = False
    ElseIf range = TargetRange.AllOfUs Then
      Return GetThem()
    ElseIf range = TargetRange.AllOfThem Then
      Return GetUs()
    Else 'If range = TargetRange.Everyone Then
      Return CombineList(GetThem(), GetUs())
    End If
  End If

  Dim ss = GetScreenSize()

  Do
    Dim items = Nothing
    If inEnemies Then
      Dim they = GetThem()
      items = CreateList()
      For i = 1 To they.Count
        Dim target = they[i]
        If Not TestStatus(target, StatusEffect.Dead) Then
          items.Add(target)
        End If
      Next
    Else
      items = GetUs()
    End If

    Dim names = CreateArrayList(items.Count + 1)
    For i = 1 To items.Count
      names[i] = items[i].Name
    Next
    If inEnemies Then
      names[names.Count] = "<<"
    Else
      names[names.Count] = ">>"
    End If

    Dim hMenu = CreateMenu(120, ss.Y - 160, 240, 160, _
      Nothing, Nothing, magic.Name, names, Nothing, 1)
    Dim index = ProcessMenu(hMenu)
    DestroyControl(hMenu)

    If index > 0 Then
      If index <= items.Count Then
        Return {items[index]}
      Else
        inEnemies = Not inEnemies
      End If
    Else
      Return Nothing
    End If
  Loop

End Procedure

Procedure CanApplyStatusEffect(magic, target, fx)
  Return Not Math.TestBit(target.Resistance, fx) AndAlso _
    (Math.Max(1, GetAbility(target, Ability.Luck) - 10) * 5 + GetDP(target, APDP.Stat)) < Math.GetRandom(1, 100)
End Procedure

Procedure ApplyHeroMagic(who, magic, targets)

  FastBlinkPic(who)

  Dim usage = magic.Usage
  If usage = 1 Then
    AddInfoMsg(who.Name + "のこうげき！")
    Sleep(500)
    magic = DuplicateElement([Attack])
    magic.Value = {0, 0, 0, 0, 0}
    If Not IsHero(who) OrElse who.Weapon <> Nothing Then
      For i = 1 To magic.Value.Count
        magic.Value[i] = GetAP(who, i)
      Next
    Else 'If IsHero(who) AndAlso who.Weapon = Nothing Then
      magic.Value[1] = 1
    End If
    If TestStatus(who, StatusEffect.Berserk) Then
      magic.Value[1] = magic.Value[1] * 3 / 2
    End If
    magic.StatusFX = who.StatusFX
    If IsHero(who) AndAlso who.Weapon <> Nothing Then
      magic.LevelDep = who.Weapon.LevelDep
      magic.AbilityDep = who.Weapon.AbilityDep
      PlayAudio(Sound.Kitta)
    End If
  ElseIf usage = 2 Then
    AddInfoMsg(who.Name + "は")
    AddInfoMsg(magic.Name + "をとなえた！")
    PlayAudio(Sound.Tonaeta)
    RainbowPic(who)
    Sleep(500)
  ElseIf usage = 3 Then
    AddInfoMsg(who.Name + "は")
    AddInfoMsg(magic.Name + "をつかった！")
    Sleep(500)
  ElseIf usage = 4 Then
    AddInfoMsg(who.Name)
    AddInfoMsg("「" + magic.Name + "！！」")
    If magic = [DragonKilling] OrElse magic = [DragonKilling2] Then
      PlayAudio(Sound.Breath2)
    ElseIf magic = [ColdBreath] OrElse magic = [ColdBreath2] Then
      PlayAudio(Sound.Breath)
    ElseIf magic = [ADFire] OrElse magic = [ADFire2] Then
      PlayAudio(Sound.Tonaeta)
      RainbowPic(who)
    ElseIf magic = [HeatWave] OrElse magic = [HeatWave2] Then
      PlayAudio(Sound.Breath)
    ElseIf magic = [Tricross] OrElse magic = [Tricross2] Then
      PlayAudio(Sound.Tonaeta)
      RainbowPic(who)
    ElseIf magic = [Invalidation] Then
      PlayAudio(Sound.Eraser)
    End If
    Sleep(500)
  End If
  
  If Not magic.HasEffect Then
    AddInfoMsg("しかし　意味がなかった！")
    Sleep(500)
    Return
  End If

  If TestStatus(who, StatusEffect.Berserk) OrElse TestStatus(who, StatusEffect.Charm) Then
    targets = SelectAutoTarget(who, magic)
  ElseIf TestStatus(who, StatusEffect.Confusion) Then
    targets = SelectRandomTarget(who, magic)
  End If

  Dim target_is_in_them = magic.TargetRange = TargetRange.OneOfThem OrElse _
    magic.TargetRange = TargetRange.AllOfThem
  Dim target_is_in_us = Not target_is_in_them AndAlso _
    magic.TargetRange <> TargetRange.Own

  If target_is_in_them AndAlso targets.Count = 1 AndAlso TestStatus(targets[1], StatusEffect.Dead) Then
    targets = SelectAutoTarget(who, magic)
  End If

  Dim level_dep = 0
  Dim level_dep_count = 0
  Dim level_list = {who.PPLevel, who.MPLevel}
  Dim level_dep_list = magic.LevelDep
  For i = 1 To level_list.Count
    If level_dep_list[i] > 0 Then
      If level_dep_list[i] <= level_list[i] Then
        level_dep += level_list[i]
      Else
        level_dep = Math.Max(level_list[i] * level_list[i] / level_dep_list[i], 1)
      End If
      level_dep_count += 1
    End If
  Next
  If level_dep_count > 0 Then
    level_dep /= level_dep_count
  Else
    level_dep = 10
  End If

  Dim ability_dep = 0
  Dim ability_dep_count = 0
  Dim ability_list = who.Ability
  Dim ability_dep_list = magic.AbilityDep
  For j = 1 To ability_list.Count
    If ability_dep_list[j] > 0 Then
      ability_dep += ability_list[j] * ability_dep_list[j]
      ability_dep_count += ability_dep_list[j]
    End If
  Next
  If ability_dep_count > 0 Then
    ability_dep /= ability_dep_count
  Else
    ability_dep = 5
  End If

  If magic.Cost <= who.MP Then
    who.MP -= magic.Cost
    UpdatePic(who)
  Else
    AddInfoMsg("しかしＭＰが足りなかった！")
    Sleep(500)
    targets.Clear()
  End If

  For i = 1 To targets.Count

    Dim msg_list = CreateList()
    Dim target = targets[i]
    
    Dim attack_hit = True
    Dim tar_agi = GetAbility(target, Ability.Agility)
    Dim who_agi = GetAbility(who, Ability.Agility)
    Dim avoiding_ref = 1100 - target.Avoiding
    Dim avoiding = 100 * tar_agi * tar_agi / who_agi / who_agi
    If Not TestStatus(who, StatusEffect.Blind) Then
      attack_hit = avoiding_ref - avoiding >= Math.GetRandom(0, 1000)
    Else
      attack_hit = avoiding_ref / 2 - avoiding >= Math.GetRandom(0, 1000)
    End If

    If magic = [Invalidation] Then
      If Not TestStatus(target, StatusEffect.Dead) Then
        ResetStatus(target, StatusEffect.Blink)
        target.Avoiding = 0
        ResetAbility(target, 0)
        ResetAP(target, 0)
        ResetDP(target, 0)
        msg_list.Add(target.Name + "の魔法効果が消えた！")
      End If
    ElseIf magic = [Spoil] Then
      If Not TestStatus(target, StatusEffect.Dead) AndAlso target.MP > 0 Then
        target.MP = Math.Max(0, target.MP - 50)
        msg_list.Add(target.Name + "のＭＰが減った！")
      End If
    End If

    If Math.TestBit(magic.StatusFX, StatusEffect.Dead) Then
      If target_is_in_us Then
        If TestStatus(target, StatusEffect.Dead) Then
          If magic.Value[1] >= Math.GetRandom(0, 100) Then
            ResetStatus(target, StatusEffect.Dead)
            target.HP = target.MaxHP * magic.Value[2] / 100
            msg_list.Add(target.Name + "は生き返った！")
          Else
            msg_list.Add("しかし" + target.Name + "は生き返らなかった！")
          End If
        End If
      ElseIf target_is_in_them Then
        If Not TestStatus(target, StatusEffect.Dead) Then
          If CanApplyStatusEffect(magic, target, StatusEffect.Dead) Then
            target.HP = 0
            ClearStatus(target)
            SetStatus(target, StatusEffect.Dead)
            If target.Tag <> "E309" AndAlso target.Tag <> "E310" AndAlso target.Tag <> "E311" Then
              msg_list.Add(target.Name + "は死んだ！")
            End If
          End If
        End If
      End If
    End If

    If magic.DamageType <> 0 AndAlso Not TestStatus(target, StatusEffect.Dead) Then
      If target_is_in_us Then
        If magic.Value[1] > 0 Then
          Dim value = ((magic.Value[1] * 3 + (ability_dep - 5) * 8) * level_dep + Math.GetRandom(1, level_dep + 1) * 21) / 34
          target.HP = Math.Min(target.MaxHP, target.HP + value)
          msg_list.Add(target.Name + "のＨＰが" + HanToZen(value) + "回復した！")
        End If
        If magic.Value[2] > 0 Then
          target.MP = Math.Min(target.MaxMP, target.MP + magic.Value[2])
          msg_list.Add(target.Name + "のＭＰが" + HanToZen(magic.Value[2]) + "回復した！")
        End If
      ElseIf target_is_in_them Then
        Dim damage = 0
        Dim damage_type = magic.DamageType
        If damage_type = 1 Then
          For j = 1 To magic.Value.Count
            If magic.Value[j] > 0 Then
              damage = Math.Max(damage, _
                ((Math.Max(0, magic.Value[j] - GetDP(target, j)) * 3 + (ability_dep - 5) * 8) * level_dep + Math.GetRandom(1, level_dep + 1) * 21) / 34)
            End If
          Next
        ElseIf damage_type = 2 Then
          If CanApplyStatusEffect(magic, target, StatusEffect.Protection) Then
            damage = who.MaxHP * GetAbility(who, Ability.Vitality) / 100
            damage += Math.GetRandom(0, damage / 10)
          Else
            damage = 0
          End If
        ElseIf damage_type = 3 Then
          damage = Math.Max(damage, _
            ((Math.Max(0, level_dep * ability_dep / 10 - GetDP(target, 1)) * 3 + (ability_dep - 5) * 8) * level_dep + Math.GetRandom(1, level_dep + 1) * 21) / 34)
        End If
        If TestStatus(target, StatusEffect.Protection) Then damage /= 2
        If attack_hit Then
          PlayAudio(Sound.Kirareta)
          BlinkPic(target)
          If damage > 0 Then
            target.HP = Math.Max(0, target.HP - damage)
            msg_list.Add(target.Name + "に" + HanToZen(damage) + "のダメージ！")
            If target.HP = 0 Then
              ClearStatus(target)
              SetStatus(target, StatusEffect.Dead)
              If target.Tag <> "E309" AndAlso target.Tag <> "E310" AndAlso target.Tag <> "E311" Then
                msg_list.Add(target.Name + "は死んだ！")
              End If
            End If
          Else
            msg_list.Add("しかし　" + target.Name + "には　きかなかった！")
          End If
        Else
          msg_list.Add(target.Name + "は　みをかわした！")
        End If
      End If
    End If
    
    If Not TestStatus(target, StatusEffect.Dead) Then
    
      Dim dpfx_done = False
      If target_is_in_us Then
        For j = 1 To 5
          If magic.DPFX[j] > 0 Then
            target.DynDP[j] = target.DynDP[j] + magic.DPFX[j] * level_dep * ability_dep / 10
            dpfx_done = True
          End If
        Next
        target.DynDP[5] = Math.Min(100, target.DynDP[5])
        If dpfx_done Then msg_list.Add(target.Name + "の防御力が上がった！")
      ElseIf target_is_in_them Then
        For j = 1 To 4
          If magic.DPFX[j] > 0 Then
            target.DynDP[j] = Math.Max(0, target.DamageDP[j] - level_dep * ability_dep / 10)
            dpfx_done = True
          End If
        Next
        If magic.DPFX[5] > 0 Then
          target.DynDP[5] = Math.Max(0, target.StatusDP - level_dep * ability_dep / 10)
          dpfx_done = True
        End If
        If dpfx_done Then msg_list.Add(target.Name + "の防御力が下がった！")
      End If
      
      If target_is_in_us Then
        If magic.AbilityFX[1] > 0 Then
          target.Ability[1] = target.Strength + Math.Min(level_dep * 10 / 100 + 1, 5)
          msg_list.Add(target.Name + "の腕力が上がった！")
        End If
        If magic.AbilityFX[2] > 0 Then
          target.Ability[2] = target.Vitality + Math.Min(level_dep * 10 / 100 + 1, 5)
          msg_list.Add(target.Name + "の体力が上がった！")
        End If
        If magic.AbilityFX[3] > 0 Then
          target.Ability[3] = target.Intelligence + Math.Min(level_dep * 10 / 100 + 1, 5)
          msg_list.Add(target.Name + "のかしこさが上がった！")
        End If
        If magic.AbilityFX[4] > 0 Then
          target.Ability[4] = target.Agility + Math.Min(level_dep * 10 / 100 + 1, 5)
          msg_list.Add(target.Name + "のすばやさが上がった！")
        End If
        If magic.AbilityFX[5] > 0 Then
          target.Ability[5] = target.Luck + Math.Min(level_dep * 10 / 100 + 1, 5)
          msg_list.Add(target.Name + "の運のよさが上がった！")
        End If
      ElseIf target_is_in_them Then
        If magic.AbilityFX[1] > 0 Then
          target.Ability[1] = target.Strength - Math.Min(level_dep * 10 / 100 + 1, 5)
          msg_list.Add(target.Name + "の腕力が下がった！")
        End If
        If magic.AbilityFX[2] > 0 Then
          target.Ability[2] = target.Vitality - Math.Min(level_dep * 10 / 100 + 1, 5)
          msg_list.Add(target.Name + "の体力が下がった！")
        End If
        If magic.AbilityFX[3] > 0 Then
          target.Ability[3] = target.Intelligence - Math.Min(level_dep * 10 / 100 + 1, 5)
          msg_list.Add(target.Name + "のかしこさが下がった！")
        End If
        If magic.AbilityFX[4] > 0 Then
          target.Ability[4] = target.Agility - Math.Min(level_dep * 10 / 100 + 1, 5)
          msg_list.Add(target.Name + "のすばやさが下がった！")
        End If
        If magic.AbilityFX[5] > 0 Then
          target.Ability[5] = target.Luck - Math.Min(level_dep * 10 / 100 + 1, 5)
          msg_list.Add(target.Name + "の運のよさが下がった！")
        End If
      End If

      If Math.TestBit(magic.StatusFX, StatusEffect.Blink) Then
        If target_is_in_us Then
          If Not TestStatus(target, StatusEffect.Blink) Then
            target.Avoiding = level_dep * ability_dep / 4
            SetStatus(target, StatusEffect.Blink)
            msg_list.Add(target.Name + "は体が軽くなった！")
          End If
        End If
      End If
      If Math.TestBit(magic.StatusFX, StatusEffect.Panic) Then
        If target_is_in_us Then
          If TestStatus(target, StatusEffect.Panic) Then
            ResetStatus(target, StatusEffect.Panic)
            msg_list.Add(target.Name + "は軽い混乱から立ち直った！")
          End If
        ElseIf target_is_in_them Then
          If CanApplyStatusEffect(magic, target, StatusEffect.Panic) Then
            SetStatus(target, StatusEffect.Panic)
            msg_list.Add(target.Name + "は軽く混乱している！")
          End If
        End If
      End If
      If Math.TestBit(magic.StatusFX, StatusEffect.Confusion) Then
        If target_is_in_us Then
          If TestStatus(target, StatusEffect.Confusion) Then
            ResetStatus(target, StatusEffect.Confusion)
            SetStatus(target, StatusEffect.Protection)
            msg_list.Add(target.Name + "は混乱から立ち直った！")
          End If
        ElseIf target_is_in_them Then
          If CanApplyStatusEffect(magic, target, StatusEffect.Confusion) Then
            SetStatus(target, StatusEffect.Confusion)
            msg_list.Add(target.Name + "は混乱している！")
          End If
        End If
      End If
      If Math.TestBit(magic.StatusFX, StatusEffect.Charm) Then
        If target_is_in_us Then
          If TestStatus(target, StatusEffect.Charm) Then
            ResetStatus(target, StatusEffect.Charm)
            SetStatus(target, StatusEffect.Protection)
            msg_list.Add(target.Name + "は魅了から覚めた！")
          End If
        ElseIf target_is_in_them Then
          If CanApplyStatusEffect(magic, target, StatusEffect.Charm) Then
            SetStatus(target, StatusEffect.Charm)
            msg_list.Add(target.Name + "は魅了された！")
          End If
        End If
      End If
      If Math.TestBit(magic.StatusFX, StatusEffect.Berserk) Then
        If target_is_in_us Then
          If TestStatus(target, StatusEffect.Berserk) Then
            ResetStatus(target, StatusEffect.Berserk)
            SetStatus(target, StatusEffect.Protection)
            msg_list.Add(target.Name + "は平常心を取り戻した！")
          End If
        ElseIf target_is_in_them Then
          If CanApplyStatusEffect(magic, target, StatusEffect.Berserk) Then
            SetStatus(target, StatusEffect.Berserk)
            msg_list.Add(target.Name + "は狂戦士と化した！")
          End If
        End If
      End If
      If Math.TestBit(magic.StatusFX, StatusEffect.Blind) Then
        If target_is_in_us Then
          If TestStatus(target, StatusEffect.Blind) Then
            ResetStatus(target, StatusEffect.Blind)
            msg_list.Add(target.Name + "は視力を取り戻した！")
          End If
        ElseIf target_is_in_them Then
          If CanApplyStatusEffect(magic, target, StatusEffect.Blind) Then
            SetStatus(target, StatusEffect.Blind)
            msg_list.Add(target.Name + "は視力を封じられた！")
          End If
        End If
      End If
      If Math.TestBit(magic.StatusFX, StatusEffect.Paralysis) Then
        If target_is_in_us Then
          If TestStatus(target, StatusEffect.Paralysis) Then
            ResetStatus(target, StatusEffect.Paralysis)
            msg_list.Add(target.Name + "は動けるようになった！")
          End If
        ElseIf target_is_in_them Then
          If CanApplyStatusEffect(magic, target, StatusEffect.Paralysis) Then
            SetStatus(target, StatusEffect.Paralysis)
            msg_list.Add(target.Name + "は動けなくなった！")
          End If
        End If
      End If
        
    End If

    If IsHero(target) Then
      If target.HP >= target.MaxHP / 4 Then
        SetPic(target, 2)
      Else
        SetPic(target, 3)
      End If
    End If

    Dim special_dead = False
    If msg_list.Count > 0 Then
      For j = 1 To msg_list.Count
        AddInfoMsg(msg_list[j])
        Sleep(500)
      Next
    ElseIf targets.Count = 1 OrElse Not TestStatus(target, StatusEffect.Dead) Then
      AddInfoMsg("しかし　" + target.Name + "には効果がなかった")
      Sleep(500)
    End If

    If TestStatus(target, StatusEffect.Dead) Then
      If IsHero(target) Then
        SetPic(target, 4)
      ElseIf target.Tag <> "E309" AndAlso target.Tag <> "E310" AndAlso target.Tag <> "E311" Then
        SetPic(target, 0)
      End If
    End If

    If usage = 1 Then
      RemoveInfoMsg(1)
    Else
      RemoveInfoMsg(2)
    End If
  Next

End Procedure

Procedure TestEndOfBattle()

  Dim we_lost = True

  'Test all party members are dead.
  Dim we = GetUs()
  For i = 1 To we.Count
    If Not TestStatus(we[i], StatusEffect.Dead) Then
      we_lost = False
      Exit For
    End If
  Next

  If we_lost Then
    SetBattleResult(BattleResult.Lost)
    Return True
  End If

  Dim they = GetThem()
  For i = 1 To they.Count
    If Not TestStatus(they[i], StatusEffect.Dead) Then
      Return False
    End If
  Next

  SetBattleResult(BattleResult.Won)
  Return True

End Procedure

Procedure ActionHero(who)

  ResetStatus(who, StatusEffect.Protection)

  If TestStatus(who, StatusEffect.Dead) OrElse TestStatus(who, StatusEffect.Paralysis) Then
    Return {who, 0, 0, {}}
  ElseIf TestStatus(who, StatusEffect.Berserk) Then
    Return {who, 1, 0, SelectAutoTarget(who, [Attack])}
  ElseIf TestStatus(who, StatusEffect.Confusion) Then
    Dim cfs = Math.GetRandom(0, who.MagicList.Count + who.ToolList.Count + 1)
    If cfs = 0 Then
      Return {who, 1, 0, SelectRandomTarget(who, [Attack])}
    ElseIf cfs <= who.MagicList.Count Then
      Return {who, 2, cfs, SelectRandomTarget(who, who.MagicList[cfs])}
    Else
      Return {who, 3, cfs - who.MagicList.Count, SelectRandomTarget(who, who.ToolList[cfs - who.MagicList.Count])}
    End If
  ElseIf TestStatus(who, StatusEffect.Charm) Then
    Dim cfs = Math.GetRandom(0, who.MagicList.Count + who.ToolList.Count + 1)
    If cfs = 0 Then
      Return {who, 1, 0, SelectAutoTarget(who, [Attack])}
    ElseIf cfs <= who.MagicList.Count Then
      Return {who, 2, cfs, SelectAutoTarget(who, who.MagicList[cfs])}
    Else
      Return {who, 3, cfs - who.MagicList.Count, SelectAutoTarget(who, who.ToolList[cfs - who.MagicList.Count])}
    End If
  End If

  Dim ss = GetScreenSize()

  Dim hMenu = CreateMenu(0, ss.Y - 160, 120, 160, Nothing, Nothing, who.Name, _
    {"たたかう", "まほう", "どうぐ", "にげる", "ぼうぎょ"}, Nothing, 1)
  Dim targets = Nothing
  Dim power = 0
  Do
    Dim index = ProcessMenu(hMenu)
    If index = 1 Then
      targets = SelectTarget(who, [Attack])
      If targets <> Nothing Then
        DestroyControl(hMenu)
        Return {who, index, 0, targets}
      End If
    ElseIf index = 2 Then
      Dim magic_menu = CreateNameListBox(who.MagicList, 120, ss.Y - 160, 240, 160, _
        Nothing, Nothing, "まほう", 1)
      Dim magic_index = ProcessMenu(magic_menu)
      Do While magic_index > 0
        Dim magic = who.MagicList[magic_index]
        If magic.Cost <= who.MP Then
          targets = SelectTarget(who, magic)
          If targets <> Nothing Then
            DestroyControl(magic_menu)
            DestroyControl(hMenu)
            Return {who, index, magic_index, targets}
          End If
        Else
          DoMsg("大変お気の毒ですがＭＰが足りません")
        End If
        magic_index = ProcessMenu(magic_menu)
      Loop
      DestroyControl(magic_menu)
    ElseIf index = 3 Then
      Dim tool_list = CombineList(who.ToolList)
      If who.Weapon <> Nothing Then tool_list.Add(who.Weapon)
      If who.Armor <> Nothing Then tool_list.Add(who.Armor)
      If who.Shield <> Nothing Then tool_list.Add(who.Shield)
      If who.Helmet <> Nothing Then tool_list.Add(who.Helmet)
      Dim tool_menu = CreateNameListBox(tool_list, 120, ss.Y - 160, 240, 160, _
        Nothing, Nothing, "どうぐ", 1)
      Dim tool_index = ProcessMenu(tool_menu)
      Do While tool_index > 0
        Dim tool = tool_list[tool_index]
        targets = SelectTarget(who, tool)
        If targets <> Nothing Then
          DestroyControl(tool_menu)
          DestroyControl(hMenu)
          Return {who, index, tool_index, targets}
        End If
        tool_index = ProcessMenu(tool_menu)
      Loop
      DestroyControl(tool_menu)
    ElseIf index = 4 Then
      DestroyControl(hMenu)
      Return {who, index, 0, {}}
    ElseIf index = 5 Then
      SetStatus(who, StatusEffect.Protection)
      DestroyControl(hMenu)
      Return {who, index, 0, {}}
    ElseIf index = 0 Then
      DestroyControl(hMenu)
      Return Nothing
    End If
  Loop

End Procedure

Procedure ActionEnemy(who)

  If TestStatus(who, StatusEffect.Dead) OrElse TestStatus(who, StatusEffect.Paralysis) Then
    Return {who, 0, 0, {}}
  ElseIf TestStatus(who, StatusEffect.Berserk) Then
    Return {who, 1, 0, SelectAutoTarget(who, [Attack])}
  End If

  If who.CommandList.Count > 0 Then
  
    Dim cmd_ratio_sum = 0
    For i = 1 To who.CommandRatio.Count
      cmd_ratio_sum += who.CommandRatio[i]
    Next
    Dim cmd_index = 1
    Dim cmd_target = Math.GetRandom(0, cmd_ratio_sum)
    cmd_ratio_sum = 0
    For i = 1 To who.CommandRatio.Count
      cmd_ratio_sum += who.CommandRatio[i]
      If cmd_target < cmd_ratio_sum Then
        cmd_index = i
        Exit For
      End If
    Next
    
    Dim cmd = who.CommandList[cmd_index]
    If cmd.Cost <= who.MP Then
      If Not TestStatus(who, StatusEffect.Confusion) Then
        Return {who, 6, cmd_index, SelectAutoTarget(who, cmd)}
      Else
        Return {who, 6, cmd_index, SelectRandomTarget(who, cmd)}
      End If
    Else
      If Not TestStatus(who, StatusEffect.Confusion) Then
        Return {who, 1, 0, SelectAutoTarget(who, [Attack])}
      Else
        Return {who, 1, 0, SelectRandomTarget(who, [Attack])}
      End If
    End If
  Else
    Return {who, 0, 0, {}}
  End If

End Procedure

Procedure BeatFirst()
  Dim p_name = [TheHero].Name + "："
  Dim q_name = "邪神デロク："
  Dim prev_img_pnl = g_Pics[g_Pics.Count]
  ChangeBGM(Nothing)
  Sleep(1000)
  PlayAudio(Sound.Beat1)
  For i = 0 To 255
    prev_img_pnl.Color = SetAlpha(prev_img_pnl.Color, 255 - i)
    Sleep(10)
  Next
  DoMsg(p_name, "（倒した……）")
  Sleep(3000)
  DoMsg(p_name, "（ん？）")
  g_They = {DuplicateElement([E310])}
  ResetAbility(g_They[1], 0)
  ResetAP(g_They[1], 0)
  ResetDP(g_They[1], 0)
  Dim img = Wnd.CreateImage(g_They[1].Texture, 0, 0)
  Dim img_pnl = Wnd.CreatePanel(img)
  img_pnl.X = prev_img_pnl.X + (prev_img_pnl.Width - img.Width) / 2
  img_pnl.Y = prev_img_pnl.Y + prev_img_pnl.Height - img.Height
  img_pnl.Color = SetAlpha(img_pnl.Color, 0)
  prev_img_pnl.Dispose()
  g_Pics[g_Pics.Count] = img_pnl
  SetPic(g_They[1], 1)
  For i = 0 To 255
    img_pnl.Color = SetAlpha(img_pnl.Color, i)
    Sleep(10)
  Next
  DoMsg(q_name, "う………ぬ…………", _
    "き　貴様　エラテの手先か？")
  ChangeBGM(Music.Battle3)
End Procedure

Procedure QuakePartyProc()
  Dim quake_sound = PlayAudio(Sound.Quake)
  Dim we = GetUs()
  Dim x_org = CreateList()
  For i = 1 To we.Count
    x_org.Add(GetPic(we[i]).X)
  Next
  Dim pat = 1
  Do
    For i = 1 To we.Count
      GetPic(we[i]).X = x_org[i] + (pat - 2) * 2 * (pat % 2)
    Next
    If pat < 3 Then pat += 1 Else pat = 0
  Loop While Not WaitForSignal("StopLastBossQuake", 50)
  For i = 1 To we.Count
    GetPic(we[i]).X = x_org[i]
  Next
  quake_sound.Stop(0)
End Procedure

Procedure BeatSecond()
  Dim p_name = [TheHero].Name + "："
  Dim q_name = "邪神デロク："
  Dim prev_img_pnl = g_Pics[g_Pics.Count]
  ChangeBGM(Nothing)
  Sleep(1000)
  PlayAudio(Sound.Beat1)
  For i = 0 To 255
    prev_img_pnl.Color = SetAlpha(prev_img_pnl.Color, 255 - i)
    Sleep(10)
  Next
  DoMsg(p_name, "（今度こそやったか！？）")
  Sleep(1500)
  RunThread(QuakePartyProc)
  Sleep(3000)
  DoMsg(p_name, "（なんだ？……）")
  Sleep(1500)
  g_They = {DuplicateElement([E311])}
  ResetAbility(g_They[1], 0)
  ResetAP(g_They[1], 0)
  ResetDP(g_They[1], 0)
  Dim img = Wnd.CreateImage(g_They[1].Texture, 0, 0)
  Dim img_pnl = Wnd.CreatePanel(img)
  img_pnl.X = prev_img_pnl.X + (prev_img_pnl.Width - img.Width) / 2
  img_pnl.Y = prev_img_pnl.Y + prev_img_pnl.Height - img.Height + 30
  img_pnl.Color = SetAlpha(img_pnl.Color, 0)
  prev_img_pnl.Dispose()
  g_Pics[g_Pics.Count] = img_pnl
  SetPic(g_They[1], 1)
  For i = 0 To 255
    img_pnl.Color = SetAlpha(img_pnl.Color, i)
    Sleep(20)
  Next
  SendSignal("StopLastBossQuake")
  Sleep(2500)
  ChangeBGM(Music.LastBattle)
  Sleep(1500)
  DoMsg(q_name, "神たるこの私に刃向かうとは……", _
    "その罪　重いぞ")
  Sleep(1500)
  DoMsg(q_name, "虚空の塵と化すがいい！")
End Procedure

Procedure BeatThird()
  ChangeBGM(Nothing)
  Sleep(1000)
  FadeIn(0, 0, 0, 3000)
  PlayAudio(Sound.Beat1)
  For i = 0 To 255
    For j = 1 To g_Pics.Count
      g_Pics[j].Color = SetAlpha(g_Pics[j].Color, 255 - i)
    Next
    Sleep(10)
  Next
  Sleep(1000)
End Procedure
