anchor.diff:                  let world = reticles.SetVisible false world
                   let world = previousMenu.SetVisible true world
                   just (previousMenu.SetCenter (ally.GetCenter world) world)
  +
  +        override this.Bindings (_, battle, _) =
  +            battleBindings battle @
  +            allyPartyBindings Constants.Battle.AllyMax
  +
  +        override this.Message (message, model, _, world) =
  +            match message with
  +            | ReadyCharacters ->
  +                let time = World.getTickTime world
  +                let model = updateCharacters (fun character -> { character with CharacterAnimationState = (CharacterAnimationState.setCycle (Some time) ReadyCycle) character.CharacterAnimationState }) model
  +                just model
  +            | PoiseCharacters ->
  +                let time = World.getTickTime world
  +                let model = updateCharacters (fun character -> { character with CharacterAnimationState = (CharacterAnimationState.setCycle (Some time) PoiseCycle) character.CharacterAnimationState }) model
  +                just model
  +            | CelebrateCharacters outcome ->
  +                let time = World.getTickTime world
  +                let model =
  +                    if outcome
  +                    then updateAllies (fun character -> { character with CharacterAnimationState = (CharacterAnimationState.setCycle (Some time) CelebrateCycle) character.CharacterAnimationState }) model
  +                    else updateEnemies (fun character -> { character with CharacterAnimationState = (CharacterAnimationState.setCycle (Some time) CelebrateCycle) character.CharacterAnimationState }) model
  +                just model
  +            | AdvanceCharacters ->
  +                let model = updateCharacters (fun character -> { character with ActionTime = character.ActionTime + Constants.Battle.ActionTimeInc }) model
  +                just model
  +            | AttackCharacter (sourceIndex, targetIndex) ->
  +                let time = World.getTickTime world
  +                let source = getCharacter sourceIndex model
  +                let target = getCharacter targetIndex model
  +                let model = updateCharacter (fun character -> { character with CharacterAnimationState = (CharacterAnimationState.setCycle (Some time) AttackCycle) character.CharacterAnimationState }) sourceIndex model
  +                let model =
  +                    updateCharacter (fun character ->
  +                        let state = character.CharacterState
  +                        let rom = Simulants.Game.GetModel world
  +                        let power = source.CharacterState.ComputePower rom
  +                        let shield = state.ComputeShield rom
  +                        let damage = max 0 (int (Math.Ceiling (double (power - shield))))
  +                        let hitPoints = state.HitPoints
  +                        let hitPoints =  max 0 (hitPoints - damage)
  +                        { character with CharacterState = { state with HitPoints = hitPoints }})
  +                        targetIndex
  +                        model
  +                if target.CharacterState.HitPoints = 0 && target.CharacterState.IsAlly
  +                then withSig model (Message (ResetCharacter targetIndex))
  +                else just model
  +            | DamageCharacter characterIndex ->
  +                let time = World.getTickTime world
  +                let model = updateCharacter (fun character -> { character with CharacterAnimationState = (CharacterAnimationState.setCycle (Some time) DamageCycle) character.CharacterAnimationState }) characterIndex model
  +                just model
  +            | PoiseCharacter characterIndex ->
  +                let time = World.getTickTime world
  +                let model = updateCharacter (fun character -> { character with CharacterAnimationState = (CharacterAnimationState.setCycle (Some time) PoiseCycle) character.CharacterAnimationState }) characterIndex model
  +                just model
  +            | WoundCharacter characterIndex ->
  +                let time = World.getTickTime world
  +                let model = updateCharacter (fun character -> { character with CharacterAnimationState = (CharacterAnimationState.setCycle (Some time) WoundCycle) character.CharacterAnimationState }) characterIndex model
  +                just model
  +            | ResetCharacter characterIndex ->
  +                let model = updateCharacter (fun character -> { character with ActionTime = 0 }) characterIndex model
  +                withCmd model (ResetCharacterCmd characterIndex)
  +            | ReticlesSelect (targetEntity, allyIndex) ->
  +                match model.BattleState with
  +                | BattleRunning ->
  +                    let targetIndex = getCharacterIndex targetEntity
  +                    let command = ActionCommand.make Attack allyIndex (Some targetIndex)
  +                    let model = { model with ActionQueue = Queue.conj command model.ActionQueue }
  +                    withMsg model (ResetCharacter allyIndex)
  +                | _ -> just model
  +            | Tick ->
  +                if World.isTicking world
  +                then tick (World.getTickTime world) model
  +                else just model
  +
  +        override this.Command (command, model, battle, world) =
  +            match command with
  +            | FadeSong ->
  +                let world = World.fadeOutSong Constants.Audio.DefaultTimeToFadeOutSongMs world
  +                just world
  +            | InitializeBattle ->
  +                let world = World.hintRenderPackageUse Assets.BattlePackage world
  +                let world = World.hintAudioPackageUse Assets.BattlePackage world
  +                let world = World.playSong 0 (1.0f * Constants.Audio.MasterSongVolume) Assets.BattleSong world
  +                let world = battle.SetBattleModel { model with BattleState = BattleReady (World.getTickTime world) } world
  +                just world
  +            | FinalizeBattle ->
  +                let world = World.hintRenderPackageDisuse Assets.BattlePackage world
  +                just (World.hintAudioPackageDisuse Assets.BattlePackage world)
  +            | ResetCharacterCmd characterIndex ->
  +                // TODO: P1: find a more Elmish way of expressing this
  +                let character = getCharacter characterIndex model
  +                let world =
  +                    if character.CharacterState.IsAlly then
  +                        let index = character.CharacterState.PartyIndex
  +                        let entities = Simulants.AllInputEntities index
  +                        List.fold (fun world (entity : Entity) -> entity.SetVisible false world) world entities
  +                    else world
  +                just world
  +            | DestroyCharacter characterIndex ->
  +                let character = Simulants.Character characterIndex
  +                let world = World.destroyEntity character world
  +                just world
  +            | IndexedCommand (command, index) ->
  +                indexedCommand command index world
   
           override this.Content (model, _, _) =
               [Content.layer Simulants.Scene.Name []
  
positive.code:                         else just model
                      | WoundCycle ->
                          if CharacterAnimationState.finished time character.CharacterAnimationState
                          then withMsg { model with CurrentCommandOpt = None } (DestroyCharacter characterIndex)
                          else just model
                      | _ -> failwithumf ()
  
          let tickReady time timeStart model =
              let timeLocal = time - timeStart
              match timeLocal with
              | 0L -> withMsg model ReadyCharacters
              | 30L -> withMsg { model with BattleState = BattleRunning } PoiseCharacters
              | _ -> just model
  
          let rec tickCurrentCommand time currentCommand model =
              let timeLocal = time - currentCommand.TimeStart
              match currentCommand.ActionCommand.Action with
              | Attack ->
  
negative.code:                   ActionQueue = Queue.empty }
               model)
  
          static let getAllies model =
              model.BattleCharacters |> Map.toSeq |> Seq.filter (function (AllyIndex _, _) -> true | _ -> false) |> Seq.map snd |> Seq.toList
  
          static let getEnemies model =
              model.BattleCharacters |> Map.toSeq |> Seq.filter (function (EnemyIndex _, _) -> true | _ -> false) |> Seq.map snd |> Seq.toList
  
          static let updateCharacters3 predicate updater model =
              { model with BattleCharacters = Map.map (fun index character -> if predicate index then updater character else character) model.BattleCharacters }
  
          static let updateCharacters updater model =
              updateCharacters3 tautology updater model
  
          static let updateAllies updater model =
              updateCharacters3 (function AllyIndex _ -> true | _ -> false) updater model
  
          static let updateEnemies updater model =
              updateCharacters3 (function EnemyIndex _ -> true | _ -> false) updater model
  
          static let tryGetCharacter characterIndex model =
              Map.tryFind characterIndex model.BattleCharacters
  
          static let getCharacter characterIndex model =
              tryGetCharacter characterIndex model |> Option.get
  
          static let getCharacterIndex (entity : Entity) =
              let strs = entity.Name.Split '+'
              let ctor = if strs.[0] = "Ally" then AllyIndex else EnemyIndex
              ctor (scvalue<int> strs.[1])
  
          static let tryUpdateCharacter updater characterIndex model =
              match tryGetCharacter characterIndex model with
              | Some character ->
                  let character = updater character
                  { model with BattleCharacters = Map.add characterIndex character model.BattleCharacters }
              | None -> model
          do ignore tryUpdateCharacter
  
          static let updateCharacter updater characterIndex model =
              let character = getCharacter characterIndex model
              let character = updater character
              { model with BattleCharacters = Map.add characterIndex character model.BattleCharacters }
  
          static let tickAttack sourceIndex (targetIndexOpt : CharacterIndex option) time timeLocal model =
  
              // if target available, proceed with attack
              let source = getCharacter sourceIndex model
  