| summaryrefslogtreecommitdiff | 
diff options
| author | nsensfel <SpamShield0@noot-noot.org> | 2018-07-11 17:56:00 +0200 | 
|---|---|---|
| committer | nsensfel <SpamShield0@noot-noot.org> | 2018-07-11 17:56:00 +0200 | 
| commit | 93b51e71e7009a286b6cf168bb59bcea1c83bd89 (patch) | |
| tree | fb64151e76c1602e130ffb828f2d480a1a5b444f /src/battle | |
| parent | f974d5b263140d8564d7e36ed8cfd0eac1734e2c (diff) | |
"Battlemap" -> "Battle".
Diffstat (limited to 'src/battle')
100 files changed, 10586 insertions, 0 deletions
| diff --git a/src/battle/Makefile b/src/battle/Makefile new file mode 100644 index 0000000..3b58a08 --- /dev/null +++ b/src/battle/Makefile @@ -0,0 +1,36 @@ +################################################################################ +## CONFIG ###################################################################### +################################################################################ +SRC_DIR ?= src +WWW_DIR ?= www +WWW_SCRIPT_DIR ?= $(WWW_DIR)/script + +ELM_CC ?= elm-make --warn + +MAIN_MODULE ?= $(SRC_DIR)/Main.elm + +################################################################################ +## MAKEFILE MAGIC ############################################################## +################################################################################ +SUB_MODULES = $(shell find $(SRC_DIR) -type f | grep "elm$$") + +################################################################################ +## SANITY CHECKS ############################################################### +################################################################################ + +################################################################################ +## TARGET RULES ################################################################ +################################################################################ +build: $(WWW_SCRIPT_DIR)/main.js + +clean: +	rm -f $(WWW_SCRIPT_DIR)/main.js + +reset: +	rm -rf elm-stuff + +################################################################################ +## INTERNAL RULES ############################################################## +################################################################################ +$(WWW_SCRIPT_DIR)/main.js: $(MAIN_MODULE) $(SUB_MODULES) +	$(ELM_CC) $(MAIN_MODULE) --output $@ diff --git a/src/battle/elm-package.json b/src/battle/elm-package.json new file mode 100644 index 0000000..43957bf --- /dev/null +++ b/src/battle/elm-package.json @@ -0,0 +1,19 @@ +{ +    "version": "1.0.0", +    "summary": "helpful summary of your project, less than 80 characters", +    "repository": "https://github.com/nsensfel/tacticians-client.git", +    "license": "Apache 2.0", +    "source-directories": [ +        "src" +    ], +    "exposed-modules": [], +    "dependencies": { +        "NoRedInk/elm-decode-pipeline": "3.0.0 <= v < 4.0.0", +        "andrewMacmurray/elm-delay": "2.0.3 <= v < 3.0.0", +        "elm-lang/core": "5.1.1 <= v < 6.0.0", +        "elm-lang/dom": "1.1.1 <= v < 2.0.0", +        "elm-lang/html": "2.0.0 <= v < 3.0.0", +        "elm-lang/http": "1.0.0 <= v < 2.0.0" +    }, +    "elm-version": "0.18.0 <= v < 0.19.0" +} diff --git a/src/battle/src/Action/Scroll.elm b/src/battle/src/Action/Scroll.elm new file mode 100644 index 0000000..cf071ae --- /dev/null +++ b/src/battle/src/Action/Scroll.elm @@ -0,0 +1,62 @@ +module Action.Scroll exposing (to) + +-- Elm ------------------------------------------------------------------------- +import Dom +import Dom.Scroll + +import Task + +-- Map ------------------------------------------------------------------- +import Constants.UI + +import Struct.UI +import Struct.Location + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-- FIXME: Scrolling so that the focused element is in the middle, not in the top +-- left corner, would be much better. +tile_to_px : Struct.UI.Type -> Int -> Float +tile_to_px ui t = +   ( +      (toFloat t) +      * (Struct.UI.get_zoom_level ui) +      * (toFloat Constants.UI.tile_size) +   ) + +scroll_to_x : Int -> Struct.UI.Type -> (Task.Task Dom.Error ()) +scroll_to_x x ui = +   (Dom.Scroll.toX +      Constants.UI.viewer_html_id +      ( +         (tile_to_px ui x) +         - Constants.UI.half_viewer_min_width +         -- center on that tile, not its top left corner +         + ((tile_to_px ui 1) / 2.0) +      ) +   ) + +scroll_to_y : Int -> Struct.UI.Type -> (Task.Task Dom.Error ()) +scroll_to_y y ui = +   (Dom.Scroll.toY +      Constants.UI.viewer_html_id +      ( +         (tile_to_px ui y) +         - Constants.UI.half_viewer_min_height +         -- center on that tile, not its top left corner +         + ((tile_to_px ui 1) / 2.0) +      ) +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +to : Struct.Location.Type -> Struct.UI.Type -> (Task.Task Dom.Error (List ())) +to loc ui = +   (Task.sequence +      [ +         (scroll_to_x loc.x ui), +         (scroll_to_y loc.y ui) +      ] +   ) diff --git a/src/battle/src/Comm/AddArmor.elm b/src/battle/src/Comm/AddArmor.elm new file mode 100644 index 0000000..480b823 --- /dev/null +++ b/src/battle/src/Comm/AddArmor.elm @@ -0,0 +1,24 @@ +module Comm.AddArmor exposing (decode) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode + +-- Map ------------------------------------------------------------------- +import Struct.Armor +import Struct.ServerReply + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +internal_decoder : Struct.Armor.Type -> Struct.ServerReply.Type +internal_decoder ar = (Struct.ServerReply.AddArmor ar) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +decode : (Json.Decode.Decoder Struct.ServerReply.Type) +decode = (Json.Decode.map (internal_decoder) (Struct.Armor.decoder)) diff --git a/src/battle/src/Comm/AddChar.elm b/src/battle/src/Comm/AddChar.elm new file mode 100644 index 0000000..32227a8 --- /dev/null +++ b/src/battle/src/Comm/AddChar.elm @@ -0,0 +1,28 @@ +module Comm.AddChar exposing (decode) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode + +-- Map ------------------------------------------------------------------- +import Struct.Character +import Struct.ServerReply + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +internal_decoder : ( +      (Struct.Character.Type, Int, Int, Int) -> +      Struct.ServerReply.Type +   ) +internal_decoder char_and_refs = (Struct.ServerReply.AddCharacter char_and_refs) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +decode : (Json.Decode.Decoder Struct.ServerReply.Type) +decode = (Json.Decode.map (internal_decoder) (Struct.Character.decoder)) diff --git a/src/battle/src/Comm/AddTile.elm b/src/battle/src/Comm/AddTile.elm new file mode 100644 index 0000000..64cf0ea --- /dev/null +++ b/src/battle/src/Comm/AddTile.elm @@ -0,0 +1,24 @@ +module Comm.AddTile exposing (decode) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode + +-- Map ------------------------------------------------------------------- +import Struct.Tile +import Struct.ServerReply + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +internal_decoder : Struct.Tile.Type -> Struct.ServerReply.Type +internal_decoder wp = (Struct.ServerReply.AddTile wp) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +decode : (Json.Decode.Decoder Struct.ServerReply.Type) +decode = (Json.Decode.map (internal_decoder) (Struct.Tile.decoder)) diff --git a/src/battle/src/Comm/AddWeapon.elm b/src/battle/src/Comm/AddWeapon.elm new file mode 100644 index 0000000..7061dea --- /dev/null +++ b/src/battle/src/Comm/AddWeapon.elm @@ -0,0 +1,24 @@ +module Comm.AddWeapon exposing (decode) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode + +-- Map ------------------------------------------------------------------- +import Struct.Weapon +import Struct.ServerReply + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +internal_decoder : Struct.Weapon.Type -> Struct.ServerReply.Type +internal_decoder wp = (Struct.ServerReply.AddWeapon wp) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +decode : (Json.Decode.Decoder Struct.ServerReply.Type) +decode = (Json.Decode.map (internal_decoder) (Struct.Weapon.decoder)) diff --git a/src/battle/src/Comm/CharacterTurn.elm b/src/battle/src/Comm/CharacterTurn.elm new file mode 100644 index 0000000..36dfd96 --- /dev/null +++ b/src/battle/src/Comm/CharacterTurn.elm @@ -0,0 +1,129 @@ +module Comm.CharacterTurn exposing (try) + +-- Elm ------------------------------------------------------------------------- +import Json.Encode + +-- Map ------------------------------------------------------------------- +import Constants.IO + +import Comm.Send + +import Struct.Character +import Struct.CharacterTurn +import Struct.Direction +import Struct.Event +import Struct.Model + +-------------------------------------------------------------------------------- +-- TYPES ------------------------------------------------------------------------ +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +encode_move : Struct.Model.Type -> (Maybe Json.Encode.Value) +encode_move model = +   case (Struct.CharacterTurn.get_path model.char_turn) of +      [] -> Nothing +      path -> +         (Just +            (Json.Encode.object +               [ +                  ("t", (Json.Encode.string "mov")), +                  ( +                     "p", +                     (Json.Encode.list +                        (List.map +                           ( +                              (Json.Encode.string) +                              << +                              (Struct.Direction.to_string) +                           ) +                           (List.reverse path) +                        ) +                     ) +                  ) +               ] +            ) +         ) + +encode_weapon_switch : Struct.Model.Type -> (Maybe Json.Encode.Value) +encode_weapon_switch model = +   if (Struct.CharacterTurn.has_switched_weapons model.char_turn) +   then +      (Just +         (Json.Encode.object +            [ +               ("t", (Json.Encode.string "swp")) +            ] +         ) +      ) +   else +      Nothing + +encode_attack : Struct.Model.Type -> (Maybe Json.Encode.Value) +encode_attack model = +   case (Struct.CharacterTurn.try_getting_target model.char_turn) of +      Nothing -> Nothing + +      (Just ix) -> +         (Just +            (Json.Encode.object +               [ +                  ("t", (Json.Encode.string "atk")), +                  ("tix", (Json.Encode.int ix)) +               ] +            ) +         ) + +encode_actions : Struct.Model.Type -> (List Json.Encode.Value) +encode_actions model = +   case +      ( +         (encode_move model), +         (encode_weapon_switch model), +         (encode_attack model) +      ) +   of +      ((Just move), Nothing, Nothing) -> [move] +      ((Just move), Nothing, (Just attack)) -> [move, attack] +      (Nothing, (Just switch_weapon), Nothing) -> [switch_weapon] +      (Nothing, (Just switch_weapon), (Just attack)) -> [switch_weapon, attack] +      (Nothing, Nothing, (Just attack)) -> [attack] +      _ -> [] + +try_encoding : Struct.Model.Type -> (Maybe Json.Encode.Value) +try_encoding model = +   case (Struct.CharacterTurn.try_getting_active_character model.char_turn) of +      (Just char) -> +         (Just +            (Json.Encode.object +               [ +                  ("stk", (Json.Encode.string model.session_token)), +                  ("pid", (Json.Encode.string model.player_id)), +                  ("bid", (Json.Encode.string model.battle_id)), +                  ( +                     "cix", +                     (Json.Encode.int (Struct.Character.get_index char)) +                  ), +                  ( +                     "act", +                     (Json.Encode.list (encode_actions model)) +                  ) +               ] +            ) +         ) + +      _ -> +         Nothing + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +try : Struct.Model.Type -> (Maybe (Cmd Struct.Event.Type)) +try model = +   (Comm.Send.try_sending +      model +      Constants.IO.character_turn_handler +      try_encoding +   ) diff --git a/src/battle/src/Comm/LoadBattle.elm b/src/battle/src/Comm/LoadBattle.elm new file mode 100644 index 0000000..df4e9dd --- /dev/null +++ b/src/battle/src/Comm/LoadBattle.elm @@ -0,0 +1,42 @@ +module Comm.LoadBattle exposing (try) + +-- Elm ------------------------------------------------------------------------- +import Json.Encode + +-- Map ------------------------------------------------------------------- +import Comm.Send + +import Constants.IO + +import Struct.Event +import Struct.Model + +-------------------------------------------------------------------------------- +-- TYPES ------------------------------------------------------------------------ +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +try_encoding : Struct.Model.Type -> (Maybe Json.Encode.Value) +try_encoding model = +   (Just +      (Json.Encode.object +         [ +            ("stk", (Json.Encode.string model.session_token)), +            ("pid", (Json.Encode.string model.player_id)), +            ("bid", (Json.Encode.string model.battle_id)) +         ] +      ) +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +try : Struct.Model.Type -> (Maybe (Cmd Struct.Event.Type)) +try model = +   (Comm.Send.try_sending +      model +      Constants.IO.map_loading_handler +      try_encoding +   ) diff --git a/src/battle/src/Comm/Send.elm b/src/battle/src/Comm/Send.elm new file mode 100644 index 0000000..98e3ba4 --- /dev/null +++ b/src/battle/src/Comm/Send.elm @@ -0,0 +1,76 @@ +module Comm.Send exposing (try_sending) + +-- Elm ------------------------------------------------------------------------- +import Http + +import Json.Decode +import Json.Encode + +-- Map ------------------------------------------------------------------- +import Comm.AddArmor +import Comm.AddChar +import Comm.AddTile +import Comm.AddWeapon +import Comm.SetMap +import Comm.SetTimeline +import Comm.TurnResults + +import Struct.Event +import Struct.ServerReply +import Struct.Model + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +internal_decoder : String -> (Json.Decode.Decoder Struct.ServerReply.Type) +internal_decoder reply_type = +   case reply_type of +      "add_tile" -> (Comm.AddTile.decode) +      "add_armor" -> (Comm.AddArmor.decode) +      "add_char" -> (Comm.AddChar.decode) +      "add_weapon" -> (Comm.AddWeapon.decode) +      "set_map" -> (Comm.SetMap.decode) +      "turn_results" -> (Comm.TurnResults.decode) +      "set_timeline" -> (Comm.SetTimeline.decode) +      other -> +         (Json.Decode.fail +            ( +               "Unknown server command \"" +               ++ other +               ++ "\"" +            ) +         ) + +decode : (Json.Decode.Decoder Struct.ServerReply.Type) +decode = +   (Json.Decode.field "msg" Json.Decode.string) +   |> (Json.Decode.andThen (internal_decoder)) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +try_sending : ( +      Struct.Model.Type -> +      String -> +      (Struct.Model.Type -> (Maybe Json.Encode.Value)) -> +      (Maybe (Cmd Struct.Event.Type)) +   ) +try_sending model recipient try_encoding_fun = +   case (try_encoding_fun model) of +      (Just serial) -> +         (Just +            (Http.send +               Struct.Event.ServerReplied +               (Http.post +                  recipient +                  (Http.jsonBody serial) +                  (Json.Decode.list (decode)) +               ) +            ) +         ) + +      Nothing -> Nothing diff --git a/src/battle/src/Comm/SetMap.elm b/src/battle/src/Comm/SetMap.elm new file mode 100644 index 0000000..7bfd56f --- /dev/null +++ b/src/battle/src/Comm/SetMap.elm @@ -0,0 +1,62 @@ +module Comm.SetMap exposing (decode) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode + +-- Map ------------------------------------------------------------------- +import Struct.Map +import Struct.ServerReply +import Struct.Tile + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias MapData = +   { +      w : Int, +      h : Int, +      t : (List Int) +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +deserialize_tile_instance : Int -> Int -> Int -> Struct.Tile.Instance +deserialize_tile_instance map_width index id = +   (Struct.Tile.new_instance +      (index % map_width) +      (index // map_width) +      id +      -1 +      -1 +   ) + +internal_decoder : MapData -> Struct.ServerReply.Type +internal_decoder map_data = +   (Struct.ServerReply.SetMap +      (Struct.Map.new +         map_data.w +         map_data.h +         (List.indexedMap +            (deserialize_tile_instance map_data.w) +            map_data.t +         ) +      ) +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +decode : (Json.Decode.Decoder Struct.ServerReply.Type) +decode = +   (Json.Decode.map +      internal_decoder +      (Json.Decode.map3 MapData +         (Json.Decode.field "w" Json.Decode.int) +         (Json.Decode.field "h" Json.Decode.int) +         (Json.Decode.field +            "t" +            (Json.Decode.list Json.Decode.int) +         ) +      ) +   ) diff --git a/src/battle/src/Comm/SetTimeline.elm b/src/battle/src/Comm/SetTimeline.elm new file mode 100644 index 0000000..3956ec3 --- /dev/null +++ b/src/battle/src/Comm/SetTimeline.elm @@ -0,0 +1,27 @@ +module Comm.SetTimeline exposing (decode) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode + +-- Map ------------------------------------------------------------------- +import Struct.ServerReply +import Struct.TurnResult + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +internal_decoder : (List Struct.TurnResult.Type) -> Struct.ServerReply.Type +internal_decoder trl = (Struct.ServerReply.SetTimeline trl) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +decode : (Json.Decode.Decoder Struct.ServerReply.Type) +decode = +   (Json.Decode.map +      (internal_decoder) +      (Json.Decode.field "cnt" (Json.Decode.list Struct.TurnResult.decoder)) +   ) diff --git a/src/battle/src/Comm/TurnResults.elm b/src/battle/src/Comm/TurnResults.elm new file mode 100644 index 0000000..f8727e1 --- /dev/null +++ b/src/battle/src/Comm/TurnResults.elm @@ -0,0 +1,28 @@ +module Comm.TurnResults exposing (decode) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode + +-- Map ------------------------------------------------------------------- +import Struct.ServerReply +import Struct.TurnResult + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +internal_decoder : (List Struct.TurnResult.Type) -> Struct.ServerReply.Type +internal_decoder trl = (Struct.ServerReply.TurnResults trl) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +decode : (Json.Decode.Decoder Struct.ServerReply.Type) +decode = +   (Json.Decode.map +      (internal_decoder) +      (Json.Decode.field "cnt" (Json.Decode.list Struct.TurnResult.decoder)) +   ) diff --git a/src/battle/src/Constants/IO.elm.m4 b/src/battle/src/Constants/IO.elm.m4 new file mode 100644 index 0000000..e91cdc0 --- /dev/null +++ b/src/battle/src/Constants/IO.elm.m4 @@ -0,0 +1,16 @@ +module Constants.IO exposing (..) + +base_url : String +base_url = "__CONF_SERVER_URL" + +map_handler_url : String +map_handler_url = (base_url ++ "/handler/map") + +character_turn_handler : String +character_turn_handler = (map_handler_url ++ "/btl_character_turn") + +map_loading_handler : String +map_loading_handler = (map_handler_url ++ "/btl_load_state") + +tile_assets_url : String +tile_assets_url = (base_url ++ "/asset/svg/tile/") diff --git a/src/battle/src/Constants/Movement.elm b/src/battle/src/Constants/Movement.elm new file mode 100644 index 0000000..a2a5c1e --- /dev/null +++ b/src/battle/src/Constants/Movement.elm @@ -0,0 +1,10 @@ +module Constants.Movement exposing (..) + +max_points : Int +max_points = 200 + +cost_when_occupied_tile : Int +cost_when_occupied_tile = 201 + +cost_when_out_of_bounds : Int +cost_when_out_of_bounds = 255 diff --git a/src/battle/src/Constants/UI.elm b/src/battle/src/Constants/UI.elm new file mode 100644 index 0000000..e56553a --- /dev/null +++ b/src/battle/src/Constants/UI.elm @@ -0,0 +1,16 @@ +module Constants.UI exposing (..) + +tile_size : Int +tile_size = 32 + +variants_per_tile : Int +variants_per_tile = 9 + +viewer_html_id : String +viewer_html_id = "map_viewer" + +half_viewer_min_width : Float +half_viewer_min_width = 109.0 + +half_viewer_min_height : Float +half_viewer_min_height = 180.0 diff --git a/src/battle/src/ElmModule/Init.elm b/src/battle/src/ElmModule/Init.elm new file mode 100644 index 0000000..4fccd82 --- /dev/null +++ b/src/battle/src/ElmModule/Init.elm @@ -0,0 +1,28 @@ +module ElmModule.Init exposing (init) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Comm.LoadBattle + +import Struct.Event +import Struct.Flags +import Struct.Model + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +init : Struct.Flags.Type -> (Struct.Model.Type, (Cmd Struct.Event.Type)) +init flags = +   let model = (Struct.Model.new flags) in +      ( +         model, +         (case (Comm.LoadBattle.try model) of +            (Just cmd) -> cmd +            Nothing -> Cmd.none +         ) +      ) diff --git a/src/battle/src/ElmModule/Subscriptions.elm b/src/battle/src/ElmModule/Subscriptions.elm new file mode 100644 index 0000000..fe276f4 --- /dev/null +++ b/src/battle/src/ElmModule/Subscriptions.elm @@ -0,0 +1,17 @@ +module ElmModule.Subscriptions exposing (..) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.Model +import Struct.Event + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +subscriptions : Struct.Model.Type -> (Sub Struct.Event.Type) +subscriptions model = Sub.none diff --git a/src/battle/src/ElmModule/Update.elm b/src/battle/src/ElmModule/Update.elm new file mode 100644 index 0000000..eafac01 --- /dev/null +++ b/src/battle/src/ElmModule/Update.elm @@ -0,0 +1,106 @@ +module ElmModule.Update exposing (update) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.Model + +import Update.AbortTurn +import Update.AttackWithoutMoving +import Update.ChangeScale +import Update.DisplayCharacterInfo +import Update.EndTurn +import Update.HandleAnimationEnded +import Update.HandleServerReply +import Update.LookForCharacter +import Update.RequestDirection +import Update.SelectCharacter +import Update.SelectCharacterOrTile +import Update.SelectTab +import Update.SelectTile +import Update.SendLoadBattleRequest +import Update.SetRequestedHelp +import Update.SwitchTeam +import Update.SwitchWeapon +import Update.TestAnimation + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +update : ( +      Struct.Event.Type -> +      Struct.Model.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +update event model = +   let +      new_model = (Struct.Model.clear_error model) +   in +   case event of +      Struct.Event.None -> (model, Cmd.none) + +      (Struct.Event.Failed err) -> +         ( +            (Struct.Model.invalidate err new_model), +            Cmd.none +         ) + +      Struct.Event.AttackWithoutMovingRequest -> +         (Update.AttackWithoutMoving.apply_to new_model) + +      Struct.Event.AnimationEnded -> +         (Update.HandleAnimationEnded.apply_to model) + +      (Struct.Event.DirectionRequested d) -> +         (Update.RequestDirection.apply_to new_model d) + +      (Struct.Event.TileSelected loc) -> +         (Update.SelectTile.apply_to new_model loc) + +      (Struct.Event.CharacterOrTileSelected loc) -> +         (Update.SelectCharacterOrTile.apply_to new_model loc) + +      (Struct.Event.CharacterSelected char_id) -> +         (Update.SelectCharacter.apply_to new_model char_id) + +      (Struct.Event.CharacterInfoRequested char_id) -> +         (Update.DisplayCharacterInfo.apply_to new_model char_id) + +      (Struct.Event.LookingForCharacter char_id) -> +         (Update.LookForCharacter.apply_to new_model char_id) + +      Struct.Event.TurnEnded -> +         (Update.EndTurn.apply_to new_model) + +      (Struct.Event.ScaleChangeRequested mod) -> +         (Update.ChangeScale.apply_to new_model mod) + +      (Struct.Event.TabSelected tab) -> +         (Update.SelectTab.apply_to new_model tab) + +      Struct.Event.DebugTeamSwitchRequest -> +         (Update.SwitchTeam.apply_to new_model) + +      Struct.Event.DebugTestAnimation -> +         (Update.TestAnimation.apply_to new_model) + +      (Struct.Event.DebugLoadBattleRequest) -> +         (Update.SendLoadBattleRequest.apply_to new_model) + +      (Struct.Event.ServerReplied result) -> +         (Update.HandleServerReply.apply_to model result) + +      Struct.Event.WeaponSwitchRequest -> +         (Update.SwitchWeapon.apply_to new_model) + +      Struct.Event.AbortTurnRequest -> +         (Update.AbortTurn.apply_to new_model) + +      (Struct.Event.RequestedHelp help_request) -> +         (Update.SetRequestedHelp.apply_to new_model help_request) diff --git a/src/battle/src/ElmModule/View.elm b/src/battle/src/ElmModule/View.elm new file mode 100644 index 0000000..069b0bf --- /dev/null +++ b/src/battle/src/ElmModule/View.elm @@ -0,0 +1,57 @@ +module ElmModule.View exposing (view) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Lazy +import Html.Attributes + +-- Map ------------------------------------------------------------------- +import Constants.UI + +import Struct.Event +import Struct.Model + +import View.Map +import View.Controlled +import View.MessageBoard +import View.MainMenu +import View.SubMenu + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +view : Struct.Model.Type -> (Html.Html Struct.Event.Type) +view model = +   (Html.div +      [ +         (Html.Attributes.class "fullscreen-module") +      ] +      [ +         (View.MainMenu.get_html), +         (Html.Lazy.lazy2 +            (View.Controlled.get_html) +            model.char_turn +            model.player_ix +         ), +         (Html.div +            [ +               (Html.Attributes.class "battle-container-centerer") +            ] +            [ +               (Html.div +                  [ +                     (Html.Attributes.class "battle-container"), +                     (Html.Attributes.id Constants.UI.viewer_html_id) +                  ] +                  [(View.Map.get_html model)] +               ) +            ] +         ), +         (View.SubMenu.get_html model), +         (View.MessageBoard.get_html model) +      ] +   ) diff --git a/src/battle/src/Main.elm b/src/battle/src/Main.elm new file mode 100644 index 0000000..8140041 --- /dev/null +++ b/src/battle/src/Main.elm @@ -0,0 +1,23 @@ +-- Elm ------------------------------------------------------------------------ +import Html + +-- Map ------------------------------------------------------------------- +import Struct.Model +import Struct.Event +import Struct.Flags + +import ElmModule.Init +import ElmModule.Subscriptions +import ElmModule.View +import ElmModule.Update + +main : (Program Struct.Flags.Type Struct.Model.Type Struct.Event.Type) +main = +   (Html.programWithFlags +      { +         init = ElmModule.Init.init, +         view = ElmModule.View.view, +         update = ElmModule.Update.update, +         subscriptions = ElmModule.Subscriptions.subscriptions +      } +   ) diff --git a/src/battle/src/Struct/Armor.elm b/src/battle/src/Struct/Armor.elm new file mode 100644 index 0000000..04a0428 --- /dev/null +++ b/src/battle/src/Struct/Armor.elm @@ -0,0 +1,177 @@ +module Struct.Armor exposing +   ( +      Type, +      Ref, +      Category(..), +      new, +      get_id, +      get_name, +      get_category, +      get_resistance_to, +      get_image_id, +      decoder, +      none, +      apply_to_attributes +   ) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode +import Json.Decode.Pipeline + +-- Map ------------------------------------------------------------------- +import Struct.Attributes +import Struct.Weapon + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias PartiallyDecoded = +   { +      id : Int, +      nam : String, +      ct : String, +      cf : Float +   } + +type alias Type = +   { +      id : Int, +      name : String, +      category : Category, +      coef : Float +   } + +type alias Ref = Int + +type Category = +   Kinetic +   | Leather +   | Chain +   | Plate + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +finish_decoding : PartiallyDecoded -> Type +finish_decoding add_armor = +   { +      id = add_armor.id, +      name = add_armor.nam, +      category = +         ( +            case add_armor.ct of +               "k" -> Kinetic +               "c" -> Chain +               "p" -> Plate +               _   -> Leather +         ), +      coef = add_armor.cf +   } + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +new : Int -> String -> Category -> Float -> Type +new id name category coef = +   { +      id = id, +      name = name, +      category = category, +      coef = coef +   } + +get_id : Type -> Ref +get_id ar = ar.id + +get_name : Type -> String +get_name ar = ar.name + +get_category : Type -> String +get_category ar = ar.name + +get_image_id : Type -> String +get_image_id ar = (toString ar.id) + +get_resistance_to : Struct.Weapon.DamageType -> Type -> Int +get_resistance_to dmg_type ar = +   (ceiling +      ( +         ar.coef +         * +         ( +            case (dmg_type, ar.category) of +               (Struct.Weapon.Slash, Kinetic) -> 0.0 +               (Struct.Weapon.Slash, Leather) -> 20.0 +               (Struct.Weapon.Slash, Chain) -> 30.0 +               (Struct.Weapon.Slash, Plate) -> 30.0 +               (Struct.Weapon.Blunt, Kinetic) -> 30.0 +               (Struct.Weapon.Blunt, Leather) -> 20.0 +               (Struct.Weapon.Blunt, Chain) -> 20.0 +               (Struct.Weapon.Blunt, Plate) -> 20.0 +               (Struct.Weapon.Pierce, Kinetic) -> 20.0 +               (Struct.Weapon.Pierce, Leather) -> 20.0 +               (Struct.Weapon.Pierce, Chain) -> 20.0 +               (Struct.Weapon.Pierce, Plate) -> 30.0 +         ) +      ) +   ) + +apply_to_attributes : Type -> Struct.Attributes.Type -> Struct.Attributes.Type +apply_to_attributes ar atts = +   let +      impact = (-1 * (ceiling (20.0 * ar.coef))) +      half_impact = (-1 * (ceiling (10.0 * ar.coef))) +   in +      case ar.category of +         Kinetic -> (Struct.Attributes.mod_mind impact atts) +         Leather -> +            (Struct.Attributes.mod_constitution +               half_impact +               (Struct.Attributes.mod_dexterity +                  half_impact +                  atts +               ) +            ) + +         Chain -> +            (Struct.Attributes.mod_constitution +               half_impact +               (Struct.Attributes.mod_dexterity +                  half_impact +                  (Struct.Attributes.mod_speed impact atts) +               ) +            ) + +         Plate -> +            (Struct.Attributes.mod_constitution +               half_impact +               (Struct.Attributes.mod_dexterity +                  half_impact +                  (Struct.Attributes.mod_speed +                     impact +                     (Struct.Attributes.mod_strength impact atts) +                  ) +               ) +            ) + +decoder : (Json.Decode.Decoder Type) +decoder = +   (Json.Decode.map +      (finish_decoding) +      (Json.Decode.Pipeline.decode +         PartiallyDecoded +         |> (Json.Decode.Pipeline.required "id" Json.Decode.int) +         |> (Json.Decode.Pipeline.required "nam" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "ct" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "cf" Json.Decode.float) +      ) +   ) + +none : Type +none = +   (new +      0 +      "None" +      Leather +      0.0 +   ) diff --git a/src/battle/src/Struct/Attack.elm b/src/battle/src/Struct/Attack.elm new file mode 100644 index 0000000..377a413 --- /dev/null +++ b/src/battle/src/Struct/Attack.elm @@ -0,0 +1,148 @@ +module Struct.Attack exposing +   ( +      Type, +      Order(..), +      Precision(..), +      apply_to_characters, +      apply_inverse_to_characters, +      decoder +   ) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Json.Decode + +-- Map ------------------------------------------------------------------- +import Struct.Character + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type Order = +   First +   | Counter +   | Second + +type Precision = +   Hit +   | Graze +   | Miss + +type alias Type = +   { +      order : Order, +      precision : Precision, +      critical : Bool, +      parried : Bool, +      damage : Int +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +order_from_string : String -> Order +order_from_string str = +   case str of +      "f" -> First +      "s" -> Second +      _ -> Counter + +precision_from_string : String -> Precision +precision_from_string str = +   case str of +      "h" -> Hit +      "g" -> Graze +      _ -> Miss + +order_decoder : (Json.Decode.Decoder Order) +order_decoder = (Json.Decode.map (order_from_string) (Json.Decode.string)) + +precision_decoder : (Json.Decode.Decoder Precision) +precision_decoder = +   (Json.Decode.map (precision_from_string) (Json.Decode.string)) + +decoder : (Json.Decode.Decoder Type) +decoder = +   (Json.Decode.map5 +      Type +      (Json.Decode.field "ord" (order_decoder)) +      (Json.Decode.field "pre" (precision_decoder)) +      (Json.Decode.field "cri" (Json.Decode.bool)) +      (Json.Decode.field "par" (Json.Decode.bool)) +      (Json.Decode.field "dmg" (Json.Decode.int)) +   ) + +apply_damage_to_character : ( +      Int -> +      Struct.Character.Type -> +      Struct.Character.Type +   ) +apply_damage_to_character damage char = +   (Struct.Character.set_current_health +      ((Struct.Character.get_current_health char) - damage) +      char +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to_characters : ( +      Int -> +      Int -> +      Type -> +      (Array.Array Struct.Character.Type) -> +      (Array.Array Struct.Character.Type) +   ) +apply_to_characters attacker_ix defender_ix attack characters = +   if ((attack.order == Counter) == attack.parried) +   then +      case (Array.get defender_ix characters) of +         (Just char) -> +            (Array.set +               defender_ix +               (apply_damage_to_character attack.damage char) +               characters +            ) + +         Nothing -> characters +   else +      case (Array.get attacker_ix characters) of +         (Just char) -> +            (Array.set +               attacker_ix +               (apply_damage_to_character attack.damage char) +               characters +            ) + +         Nothing -> characters + +apply_inverse_to_characters : ( +      Int -> +      Int -> +      Type -> +      (Array.Array Struct.Character.Type) -> +      (Array.Array Struct.Character.Type) +   ) +apply_inverse_to_characters attacker_ix defender_ix attack characters = +   if ((attack.order == Counter) == attack.parried) +   then +      case (Array.get defender_ix characters) of +         (Just char) -> +            (Array.set +               defender_ix +               (apply_damage_to_character (-1 * attack.damage) char) +               characters +            ) + +         Nothing -> characters +   else +      case (Array.get attacker_ix characters) of +         (Just char) -> +            (Array.set +               attacker_ix +               (apply_damage_to_character (-1 * attack.damage) char) +               characters +            ) + +         Nothing -> characters diff --git a/src/battle/src/Struct/Attributes.elm b/src/battle/src/Struct/Attributes.elm new file mode 100644 index 0000000..50b4ad4 --- /dev/null +++ b/src/battle/src/Struct/Attributes.elm @@ -0,0 +1,134 @@ +module Struct.Attributes exposing +   ( +      Type, +      get_constitution, +      get_dexterity, +      get_intelligence, +      get_mind, +      get_speed, +      get_strength, +      mod_constitution, +      mod_dexterity, +      mod_intelligence, +      mod_mind, +      mod_speed, +      mod_strength, +      new, +      decoder +   ) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode +import Json.Decode.Pipeline + +-- Map ------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Type = +   { +      constitution : Int, +      dexterity : Int, +      intelligence : Int, +      mind : Int, +      speed : Int, +      strength : Int +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_within_range : Int -> Int -> Int -> Int +get_within_range vmin vmax v = (min vmax (max vmin v)) + +get_within_att_range : Int -> Int +get_within_att_range v = (get_within_range 0 100 v) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_constitution : Type -> Int +get_constitution t = t.constitution + +get_dexterity : Type -> Int +get_dexterity t = t.dexterity + +get_intelligence : Type -> Int +get_intelligence t = t.intelligence + +get_mind : Type -> Int +get_mind t = t.mind + +get_speed : Type -> Int +get_speed t = t.speed + +get_strength : Type -> Int +get_strength t = t.strength + +mod_constitution : Int -> Type -> Type +mod_constitution i t = +   {t | +      constitution = (get_within_att_range (i + t.constitution)) +   } + +mod_dexterity : Int -> Type -> Type +mod_dexterity i t = +   {t | +      dexterity = (get_within_att_range (i + t.dexterity)) +   } + +mod_intelligence : Int -> Type -> Type +mod_intelligence i t = +   {t | +      intelligence = (get_within_att_range (i + t.intelligence)) +   } + +mod_mind : Int -> Type -> Type +mod_mind i t = +   {t | +      mind = (get_within_att_range (i + t.mind)) +   } + +mod_speed : Int -> Type -> Type +mod_speed i t = +   {t | +      speed = (get_within_att_range (i + t.speed)) +   } + +mod_strength : Int -> Type -> Type +mod_strength i t = +   {t | +      strength = (get_within_att_range (i + t.strength)) +   } + +new : ( +      Int -> -- constitution +      Int -> -- dexterity +      Int -> -- intelligence +      Int -> -- mind +      Int -> -- speed +      Int -> -- strength +      Type +   ) +new con dex int min spe str = +   { +      constitution = con, +      dexterity = dex, +      intelligence = int, +      mind = min, +      speed = spe, +      strength = str +   } + +decoder : (Json.Decode.Decoder Type) +decoder = +   (Json.Decode.Pipeline.decode +      Type +      |> (Json.Decode.Pipeline.required "con" Json.Decode.int) +      |> (Json.Decode.Pipeline.required "dex" Json.Decode.int) +      |> (Json.Decode.Pipeline.required "int" Json.Decode.int) +      |> (Json.Decode.Pipeline.required "min" Json.Decode.int) +      |> (Json.Decode.Pipeline.required "spe" Json.Decode.int) +      |> (Json.Decode.Pipeline.required "str" Json.Decode.int) +   ) diff --git a/src/battle/src/Struct/Character.elm b/src/battle/src/Struct/Character.elm new file mode 100644 index 0000000..f854b44 --- /dev/null +++ b/src/battle/src/Struct/Character.elm @@ -0,0 +1,238 @@ +module Struct.Character exposing +   ( +      Type, +      Rank(..), +      get_index, +      get_player_ix, +      get_name, +      get_rank, +      get_icon_id, +      get_portrait_id, +      get_armor, +      get_armor_variation, +      get_current_health, +      get_sane_current_health, +      set_current_health, +      get_location, +      set_location, +      get_attributes, +      get_statistics, +      is_enabled, +      is_defeated, +      is_alive, +      set_enabled, +      set_defeated, +      get_weapons, +      set_weapons, +      decoder, +      fill_missing_equipment +   ) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode +import Json.Decode.Pipeline + +-- Map ------------------------------------------------------------------- +import Struct.Armor +import Struct.Attributes +import Struct.Location +import Struct.Statistics +import Struct.Weapon +import Struct.WeaponSet + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias PartiallyDecoded = +   { +      ix : Int, +      nam : String, +      rnk : String, +      ico : String, +      prt : String, +      lc : Struct.Location.Type, +      hea : Int, +      pla : Int, +      ena : Bool, +      dea : Bool, +      att : Struct.Attributes.Type, +      awp : Int, +      swp : Int, +      ar : Int +   } + +type Rank = +   Optional +   | Target +   | Commander + +type alias Type = +   { +      ix : Int, +      name : String, +      rank : Rank, +      icon : String, +      portrait : String, +      location : Struct.Location.Type, +      health : Int, +      player_ix : Int, +      enabled : Bool, +      defeated : Bool, +      attributes : Struct.Attributes.Type, +      statistics : Struct.Statistics.Type, +      weapons : Struct.WeaponSet.Type, +      armor : Struct.Armor.Type +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +str_to_rank : String -> Rank +str_to_rank str = +   case str of +      "t" -> Target +      "c" -> Commander +      _ -> Optional + +finish_decoding : PartiallyDecoded -> (Type, Int, Int, Int) +finish_decoding add_char = +   let +      weapon_set = (Struct.WeaponSet.new Struct.Weapon.none Struct.Weapon.none) +      armor = Struct.Armor.none +      almost_char = +         { +            ix = add_char.ix, +            name = add_char.nam, +            rank = (str_to_rank add_char.rnk), +            icon = add_char.ico, +            portrait = add_char.prt, +            location = add_char.lc, +            health = add_char.hea, +            attributes = add_char.att, +            statistics = (Struct.Statistics.new add_char.att weapon_set armor), +            player_ix = add_char.pla, +            enabled = add_char.ena, +            defeated = add_char.dea, +            weapons = weapon_set, +            armor = armor +         } +   in +      (almost_char, add_char.awp, add_char.swp, add_char.ar) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_index : Type -> Int +get_index c = c.ix + +get_name : Type -> String +get_name c = c.name + +get_rank : Type -> Rank +get_rank c = c.rank + +get_player_ix : Type -> Int +get_player_ix c = c.player_ix + +get_icon_id : Type -> String +get_icon_id c = c.icon + +get_portrait_id : Type -> String +get_portrait_id c = c.portrait + +get_current_health : Type -> Int +get_current_health c = c.health + +get_sane_current_health : Type -> Int +get_sane_current_health c = (max 0 c.health) + +set_current_health : Int -> Type -> Type +set_current_health health c = {c | health = health} + +get_location : Type -> Struct.Location.Type +get_location t = t.location + +set_location : Struct.Location.Type -> Type -> Type +set_location location char = {char | location = location} + +get_attributes : Type -> Struct.Attributes.Type +get_attributes char = char.attributes + +get_statistics : Type -> Struct.Statistics.Type +get_statistics char = char.statistics + +is_alive : Type -> Bool +is_alive char = ((char.health > 0) && (not char.defeated)) + +is_enabled : Type -> Bool +is_enabled char = char.enabled + +is_defeated : Type -> Bool +is_defeated char = char.defeated + +set_enabled : Bool -> Type -> Type +set_enabled enabled char = {char | enabled = enabled} + +set_defeated : Bool -> Type -> Type +set_defeated defeated char = {char | defeated = defeated} + +get_weapons : Type -> Struct.WeaponSet.Type +get_weapons char = char.weapons + +get_armor : Type -> Struct.Armor.Type +get_armor char = char.armor + +get_armor_variation : Type -> String +get_armor_variation char = +   case char.portrait of +      -- Currently hardcoded to match crows from characters.css +      "11" -> "1" +      "4" -> "1" +      _ -> "0" + +set_weapons : Struct.WeaponSet.Type -> Type -> Type +set_weapons weapons char = +   {char | +      weapons = weapons, +      statistics = (Struct.Statistics.new char.attributes weapons char.armor) +   } + +decoder : (Json.Decode.Decoder (Type, Int, Int, Int)) +decoder = +   (Json.Decode.map +      (finish_decoding) +      (Json.Decode.Pipeline.decode +         PartiallyDecoded +         |> (Json.Decode.Pipeline.required "ix" Json.Decode.int) +         |> (Json.Decode.Pipeline.required "nam" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "rnk" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "ico" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "prt" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "lc" (Struct.Location.decoder)) +         |> (Json.Decode.Pipeline.required "hea" Json.Decode.int) +         |> (Json.Decode.Pipeline.required "pla" Json.Decode.int) +         |> (Json.Decode.Pipeline.required "ena" Json.Decode.bool) +         |> (Json.Decode.Pipeline.required "dea" Json.Decode.bool) +         |> (Json.Decode.Pipeline.required "att" (Struct.Attributes.decoder)) +         |> (Json.Decode.Pipeline.required "awp" Json.Decode.int) +         |> (Json.Decode.Pipeline.required "swp" Json.Decode.int) +         |> (Json.Decode.Pipeline.required "ar" Json.Decode.int) +      ) +   ) + +fill_missing_equipment : ( +      Struct.Weapon.Type -> +      Struct.Weapon.Type -> +      Struct.Armor.Type -> +      Type -> +      Type +   ) +fill_missing_equipment awp swp ar char = +   let +      weapon_set = (Struct.WeaponSet.new awp swp) +   in +      {char | +         statistics = (Struct.Statistics.new char.attributes weapon_set ar), +         weapons = weapon_set, +         armor = ar +      } diff --git a/src/battle/src/Struct/CharacterTurn.elm b/src/battle/src/Struct/CharacterTurn.elm new file mode 100644 index 0000000..ddc80d4 --- /dev/null +++ b/src/battle/src/Struct/CharacterTurn.elm @@ -0,0 +1,134 @@ +module Struct.CharacterTurn exposing +   ( +      Type, +      State(..), +      set_target, +      can_select_target, +      set_has_switched_weapons, +      has_switched_weapons, +      get_path, +      get_state, +      try_getting_target, +      lock_path, +      new, +      set_active_character, +      set_navigator, +      try_getting_active_character, +      try_getting_navigator +   ) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.Character +import Struct.Direction +import Struct.Navigator + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type State = +   Default +   | SelectedCharacter +   | MovedCharacter +   | ChoseTarget + +type alias Type = +   { +      state : State, +      active_character : (Maybe Struct.Character.Type), +      path : (List Struct.Direction.Type), +      target : (Maybe Int), +      navigator : (Maybe Struct.Navigator.Type), +      has_switched_weapons : Bool +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +new : Type +new = +   { +      state = Default, +      active_character = Nothing, +      path = [], +      target = Nothing, +      navigator = Nothing, +      has_switched_weapons = False +   } + +try_getting_active_character : Type -> (Maybe Struct.Character.Type) +try_getting_active_character ct = ct.active_character + +can_select_target : Type -> Bool +can_select_target ct = (ct.state == MovedCharacter) + +set_active_character : ( +      Struct.Character.Type -> +      Type -> +      Type +   ) +set_active_character char ct = +   {ct | +      state = SelectedCharacter, +      active_character = (Just char), +      path = [], +      target = Nothing, +      navigator = Nothing, +      has_switched_weapons = False +   } + +get_state : Type -> State +get_state ct = ct.state + +get_path : Type -> (List Struct.Direction.Type) +get_path ct = ct.path + +lock_path : Type -> Type +lock_path ct = +   case ct.navigator of +      (Just old_nav) -> +         {ct | +            state = MovedCharacter, +            path = (Struct.Navigator.get_path old_nav), +            target = Nothing, +            navigator = (Just (Struct.Navigator.lock_path old_nav)) +         } + +      Nothing -> +         ct + +try_getting_navigator : Type -> (Maybe Struct.Navigator.Type) +try_getting_navigator ct = ct.navigator + +set_navigator : Struct.Navigator.Type -> Type -> Type +set_navigator navigator ct = +   {ct | +      state = SelectedCharacter, +      path = [], +      target = Nothing, +      navigator = (Just navigator) +   } + +set_has_switched_weapons : Bool -> Type -> Type +set_has_switched_weapons v ct = +   {ct | +      has_switched_weapons = v +   } + +has_switched_weapons : Type -> Bool +has_switched_weapons ct = ct.has_switched_weapons + +set_target : (Maybe Int) -> Type -> Type +set_target target ct = +   {ct | +      state = ChoseTarget, +      target = target +   } + +try_getting_target : Type -> (Maybe Int) +try_getting_target ct = ct.target diff --git a/src/battle/src/Struct/Direction.elm b/src/battle/src/Struct/Direction.elm new file mode 100644 index 0000000..6fd0a66 --- /dev/null +++ b/src/battle/src/Struct/Direction.elm @@ -0,0 +1,52 @@ +module Struct.Direction exposing (Type(..), opposite_of, to_string, decoder) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode + +-- Map ------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type Type = +   None +   | Left +   | Right +   | Up +   | Down + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +from_string : String -> Type +from_string str = +   case str of +      "R" -> Right +      "L" -> Left +      "U" -> Up +      "D" -> Down +      _ -> None + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +opposite_of : Type -> Type +opposite_of d = +   case d of +      Left -> Right +      Right -> Left +      Up -> Down +      Down -> Up +      None -> None + +to_string : Type -> String +to_string dir = +   case dir of +      Right -> "R" +      Left -> "L" +      Up -> "U" +      Down -> "D" +      None -> "N" + +decoder : (Json.Decode.Decoder Type) +decoder = (Json.Decode.map (from_string) Json.Decode.string) diff --git a/src/battle/src/Struct/Error.elm b/src/battle/src/Struct/Error.elm new file mode 100644 index 0000000..5f40c09 --- /dev/null +++ b/src/battle/src/Struct/Error.elm @@ -0,0 +1,45 @@ +module Struct.Error exposing (Type, Mode(..), new, to_string) + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type Mode = +   IllegalAction +   | Programming +   | Unimplemented +   | Networking +   | Failure + +type alias Type = +   { +      mode: Mode, +      message: String +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +new : Mode -> String -> Type +new mode str = +   { +      mode = mode, +      message = str +   } + +to_string : Type -> String +to_string e = +   ( +      (case e.mode of +         Failure -> "The action failed: " +         IllegalAction -> "Request discarded: " +         Programming -> "Error in the program (please report): " +         Unimplemented -> "Update discarded due to unimplemented feature: " +         Networking -> "Error while conversing with the server: " +      ) +      ++ e.message +   ) + diff --git a/src/battle/src/Struct/Event.elm b/src/battle/src/Struct/Event.elm new file mode 100644 index 0000000..dedb606 --- /dev/null +++ b/src/battle/src/Struct/Event.elm @@ -0,0 +1,44 @@ +module Struct.Event exposing (Type(..), attempted) + +-- Elm ------------------------------------------------------------------------- +import Http + +-- Map ------------------------------------------------------------------- +import Struct.Direction +import Struct.Error +import Struct.Location +import Struct.ServerReply +import Struct.HelpRequest +import Struct.UI + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type Type = +   AbortTurnRequest +   | AnimationEnded +   | AttackWithoutMovingRequest +   | CharacterInfoRequested Int +   | CharacterSelected Int +   | DebugLoadBattleRequest +   | DebugTeamSwitchRequest +   | DebugTestAnimation +   | DirectionRequested Struct.Direction.Type +   | Failed Struct.Error.Type +   | LookingForCharacter Int +   | None +   | ScaleChangeRequested Float +   | ServerReplied (Result Http.Error (List Struct.ServerReply.Type)) +   | TabSelected Struct.UI.Tab +   | TileSelected Struct.Location.Ref +   | CharacterOrTileSelected Struct.Location.Ref +   | TurnEnded +   | RequestedHelp Struct.HelpRequest.Type +   | WeaponSwitchRequest + +attempted : (Result.Result err val) -> Type +attempted act = +   case act of +      (Result.Ok _) -> None +      (Result.Err msg) -> +         (Failed (Struct.Error.new Struct.Error.Failure (toString msg))) diff --git a/src/battle/src/Struct/Flags.elm b/src/battle/src/Struct/Flags.elm new file mode 100644 index 0000000..228d258 --- /dev/null +++ b/src/battle/src/Struct/Flags.elm @@ -0,0 +1,42 @@ +module Struct.Flags exposing +   ( +      Type, +      maybe_get_param +   ) + +-- Elm ------------------------------------------------------------------------- +import List + +-- Map ------------------------------------------------------------------- +import Util.List + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Type = +   { +      user_id : String, +      token : String, +      url_params : (List (List String)) +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +maybe_get_param : String -> Type -> (Maybe String) +maybe_get_param param flags = +   case +      (Util.List.get_first +         (\e -> ((List.head e) == (Just param))) +         flags.url_params +      ) +   of +      Nothing -> Nothing +      (Just a) -> +         case (List.tail a) of +            Nothing -> Nothing +            (Just b) -> (List.head b) diff --git a/src/battle/src/Struct/HelpRequest.elm b/src/battle/src/Struct/HelpRequest.elm new file mode 100644 index 0000000..a0693e2 --- /dev/null +++ b/src/battle/src/Struct/HelpRequest.elm @@ -0,0 +1,13 @@ +module Struct.HelpRequest exposing (Type(..)) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.Character + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type Type = +   None +   | HelpOnRank Struct.Character.Rank diff --git a/src/battle/src/Struct/Location.elm b/src/battle/src/Struct/Location.elm new file mode 100644 index 0000000..26e237c --- /dev/null +++ b/src/battle/src/Struct/Location.elm @@ -0,0 +1,59 @@ +module Struct.Location exposing (..) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode +import Json.Decode.Pipeline + +-- Map ------------------------------------------------------------------- +import Struct.Direction + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Type = +   { +      x : Int, +      y : Int +   } + +type alias Ref = (Int, Int) + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +neighbor : Struct.Direction.Type -> Type -> Type +neighbor dir loc = +   case dir of +      Struct.Direction.Right -> {loc | x = (loc.x + 1)} +      Struct.Direction.Left -> {loc | x = (loc.x - 1)} +      Struct.Direction.Up -> {loc | y = (loc.y - 1)} +      Struct.Direction.Down -> {loc | y = (loc.y + 1)} +      Struct.Direction.None -> loc + +get_ref : Type -> Ref +get_ref l = +   (l.x, l.y) + +from_ref : Ref -> Type +from_ref (x, y) = +   {x = x, y = y} + +dist : Type -> Type -> Int +dist loc_a loc_b = +   ( +      (abs (loc_a.x - loc_b.x)) +      + +      (abs (loc_a.y - loc_b.y)) +   ) + +decoder : (Json.Decode.Decoder Type) +decoder = +   (Json.Decode.Pipeline.decode +      Type +      |> (Json.Decode.Pipeline.required "x" Json.Decode.int) +      |> (Json.Decode.Pipeline.required "y" Json.Decode.int) +   ) diff --git a/src/battle/src/Struct/Map.elm b/src/battle/src/Struct/Map.elm new file mode 100644 index 0000000..9944b72 --- /dev/null +++ b/src/battle/src/Struct/Map.elm @@ -0,0 +1,123 @@ +module Struct.Map exposing +   ( +      Type, +      empty, +      new, +      get_width, +      get_height, +      get_tiles, +      get_movement_cost_function, +      solve_tiles, +      try_getting_tile_at +   ) + +-- Elm ------------------------------------------------------------------------- +import Array + +-- Map ------------------------------------------------------------------- +import Struct.Character +import Struct.Tile +import Struct.Location + +import Constants.Movement + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Type = +   { +      width: Int, +      height: Int, +      content: (Array.Array Struct.Tile.Instance) +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +location_to_index : Struct.Location.Type -> Type -> Int +location_to_index loc bmap = +   ((loc.y * bmap.width) + loc.x) + +has_location : Struct.Location.Type -> Type -> Bool +has_location loc bmap = +   ( +      (loc.x >= 0) +      && (loc.y >= 0) +      && (loc.x < bmap.width) +      && (loc.y < bmap.height) +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_width : Type -> Int +get_width bmap = bmap.width + +get_height : Type -> Int +get_height bmap = bmap.height + +get_tiles : Type -> (Array.Array Struct.Tile.Instance) +get_tiles bmap = bmap.content + +empty : Type +empty = +   { +      width = 0, +      height = 0, +      content = (Array.empty) +   } + +new : Int -> Int -> (List Struct.Tile.Instance) -> Type +new width height tiles = +   { +      width = width, +      height = height, +      content = (Array.fromList tiles) +   } + +try_getting_tile_at : ( +      Struct.Location.Type -> +      Type -> +      (Maybe Struct.Tile.Instance) +   ) +try_getting_tile_at loc bmap = +   (Array.get (location_to_index loc bmap) bmap.content) + +get_movement_cost_function : ( +      Type -> +      Struct.Location.Type -> +      (List Struct.Character.Type) -> +      Struct.Location.Type -> +      Int +   ) +get_movement_cost_function bmap start_loc char_list loc = +   if (has_location loc bmap) +   then +      case (Array.get (location_to_index loc bmap) bmap.content) of +         (Just tile) -> +            if +               (List.any +                  ( +                     \c -> +                        ( +                           ((Struct.Character.get_location c) == loc) +                           && (loc /= start_loc) +                           && (Struct.Character.is_alive c) +                        ) +                  ) +                  char_list +               ) +            then +               Constants.Movement.cost_when_occupied_tile +            else +               (Struct.Tile.get_instance_cost tile) + +         Nothing -> Constants.Movement.cost_when_out_of_bounds +   else +      Constants.Movement.cost_when_out_of_bounds + +solve_tiles : (List Struct.Tile.Type) -> Type -> Type +solve_tiles tiles bmap = +   {bmap | +      content = (Array.map (Struct.Tile.solve_tile_instance tiles) bmap.content) +   } diff --git a/src/battle/src/Struct/Marker.elm b/src/battle/src/Struct/Marker.elm new file mode 100644 index 0000000..e3a051d --- /dev/null +++ b/src/battle/src/Struct/Marker.elm @@ -0,0 +1,10 @@ +module Struct.Marker exposing (Type(..)) + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type Type = +   CanAttackCanDefend +   | CanGoToCanDefend +   | CanAttackCantDefend +   | CanGoToCantDefend diff --git a/src/battle/src/Struct/Model.elm b/src/battle/src/Struct/Model.elm new file mode 100644 index 0000000..aa4ce4a --- /dev/null +++ b/src/battle/src/Struct/Model.elm @@ -0,0 +1,254 @@ +module Struct.Model exposing +   ( +      Type, +      new, +      add_character, +      update_character, +      update_character_fun, +      add_weapon, +      add_armor, +      add_tile, +      invalidate, +      initialize_animator, +      apply_animator_step, +      move_animator_to_next_step, +      reset, +      full_debug_reset, +      clear_error +   ) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Dict + +-- Map ------------------------------------------------------------------- +import Struct.Armor +import Struct.Map +import Struct.Character +import Struct.CharacterTurn +import Struct.Error +import Struct.Flags +import Struct.HelpRequest +import Struct.Tile +import Struct.TurnResult +import Struct.TurnResultAnimator +import Struct.UI +import Struct.Weapon + +import Util.Array + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Type = +   { +      help_request: Struct.HelpRequest.Type, +      animator: (Maybe Struct.TurnResultAnimator.Type), +      map: Struct.Map.Type, +      characters: (Array.Array Struct.Character.Type), +      weapons: (Dict.Dict Struct.Weapon.Ref Struct.Weapon.Type), +      armors: (Dict.Dict Struct.Armor.Ref Struct.Armor.Type), +      tiles: (Dict.Dict Struct.Tile.Ref Struct.Tile.Type), +      error: (Maybe Struct.Error.Type), +      player_id: String, +      battle_id: String, +      session_token: String, +      player_ix: Int, +      ui: Struct.UI.Type, +      char_turn: Struct.CharacterTurn.Type, +      timeline: (Array.Array Struct.TurnResult.Type) +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +new : Struct.Flags.Type -> Type +new flags = +   let +      maybe_battle_id = (Struct.Flags.maybe_get_param "id" flags) +      model = +         { +            help_request = Struct.HelpRequest.None, +            animator = Nothing, +            map = (Struct.Map.empty), +            characters = (Array.empty), +            weapons = (Dict.empty), +            armors = (Dict.empty), +            tiles = (Dict.empty), +            error = Nothing, +            battle_id = "", +            player_id = +               ( +                  if (flags.user_id == "") +                  then "0" +                  else flags.user_id +               ), +            session_token = flags.token, +            player_ix = 0, +            ui = (Struct.UI.default), +            char_turn = (Struct.CharacterTurn.new), +            timeline = (Array.empty) +         } +   in +      case maybe_battle_id of +         Nothing -> +            (invalidate +               (Struct.Error.new +                  Struct.Error.Failure +                  "Could not find battle id." +               ) +               model +            ) + +         (Just id) -> {model | battle_id = id} + +add_character : Struct.Character.Type -> Type -> Type +add_character char model = +   {model | +      characters = +         (Array.push +            char +            model.characters +         ) +   } + +add_weapon : Struct.Weapon.Type -> Type -> Type +add_weapon wp model = +   {model | +      weapons = +         (Dict.insert +            (Struct.Weapon.get_id wp) +            wp +            model.weapons +         ) +   } + +add_armor : Struct.Armor.Type -> Type -> Type +add_armor ar model = +   {model | +      armors = +         (Dict.insert +            (Struct.Armor.get_id ar) +            ar +            model.armors +         ) +   } + +add_tile : Struct.Tile.Type -> Type -> Type +add_tile tl model = +   {model | +      tiles = +         (Dict.insert +            (Struct.Tile.get_id tl) +            tl +            model.tiles +         ) +   } + +reset : Type -> Type +reset model = +   {model | +      help_request = Struct.HelpRequest.None, +      error = Nothing, +      ui = +         (Struct.UI.reset_displayed_nav +            (Struct.UI.set_previous_action Nothing model.ui) +         ), +      char_turn = (Struct.CharacterTurn.new) +   } + +full_debug_reset : Type -> Type +full_debug_reset model = +   {model | +      help_request = Struct.HelpRequest.None, +      animator = Nothing, +      map = (Struct.Map.empty), +      characters = (Array.empty), +      weapons = (Dict.empty), +      armors = (Dict.empty), +      tiles = (Dict.empty), +      error = Nothing, +      ui = (Struct.UI.default), +      char_turn = (Struct.CharacterTurn.new), +      timeline = (Array.empty) +   } + +initialize_animator : Type -> Type +initialize_animator model = +   let +      timeline_list = (Array.toList model.timeline) +   in +      {model | +         animator = +            (Struct.TurnResultAnimator.maybe_new +               (List.reverse timeline_list) +               True +            ), +         ui = (Struct.UI.default), +         characters = +            (List.foldr +               (Struct.TurnResult.apply_inverse_to_characters) +               model.characters +               timeline_list +            ) +      } + +move_animator_to_next_step : Type -> Type +move_animator_to_next_step model = +   case model.animator of +      Nothing -> model +      (Just animator) -> +         {model | +            animator = +               (Struct.TurnResultAnimator.maybe_trigger_next_step animator) +         } + +apply_animator_step : Type -> Type +apply_animator_step model = +   case model.animator of +      Nothing -> model +      (Just animator) -> +         {model | +            characters = +               case +                  (Struct.TurnResultAnimator.get_current_animation animator) +               of +                  (Struct.TurnResultAnimator.TurnResult turn_result) -> +                     (Struct.TurnResult.apply_step_to_characters +                        turn_result +                        model.characters +                     ) + +                  _ -> model.characters +         } + +update_character : Int -> Struct.Character.Type -> Type -> Type +update_character ix new_val model = +   {model | +      characters = (Array.set ix new_val model.characters) +   } + +update_character_fun : ( +      Int -> +      ((Maybe Struct.Character.Type) -> (Maybe Struct.Character.Type)) -> +      Type -> +      Type +   ) +update_character_fun ix fun model = +   {model | +      characters = (Util.Array.update ix (fun) model.characters) +   } + +invalidate : Struct.Error.Type -> Type -> Type +invalidate err model = +   {model | +      error = (Just err) +   } + +clear_error : Type -> Type +clear_error model = {model | error = Nothing} diff --git a/src/battle/src/Struct/Navigator.elm b/src/battle/src/Struct/Navigator.elm new file mode 100644 index 0000000..571115d --- /dev/null +++ b/src/battle/src/Struct/Navigator.elm @@ -0,0 +1,192 @@ +module Struct.Navigator exposing +   ( +      Type, +      Summary, +      new, +      get_current_location, +      get_starting_location, +      get_remaining_points, +      get_range_markers, +      get_path, +      get_summary, +      clear_path, +      lock_path, +      try_adding_step, +      try_getting_path_to +   ) + +-- Elm ------------------------------------------------------------------------- +import Dict + +-- Map ------------------------------------------------------------------- +import Struct.Location +import Struct.Direction +import Struct.Marker +import Struct.Path +import Struct.RangeIndicator + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Type = +   { +      starting_location: Struct.Location.Type, +      movement_dist: Int, +      attack_dist: Int, +      defense_dist: Int, +      path: Struct.Path.Type, +      locked_path: Bool, +      range_indicators: +         (Dict.Dict +            Struct.Location.Ref +            Struct.RangeIndicator.Type +         ), +      cost_fun: (Struct.Location.Type -> Int) +   } + +type alias Summary = +   { +      starting_location: Struct.Location.Type, +      path: (List Struct.Direction.Type), +      markers: (List (Struct.Location.Ref, Struct.Marker.Type)), +      locked_path: Bool +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +new : ( +      Struct.Location.Type -> +      Int -> +      Int -> +      Int -> +      (Struct.Location.Type -> Int) -> +      Type +   ) +new start_loc mov_dist atk_dist def_dist cost_fun = +   { +      starting_location = start_loc, +      movement_dist = mov_dist, +      attack_dist = atk_dist, +      defense_dist = def_dist, +      path = (Struct.Path.new start_loc mov_dist), +      locked_path = False, +      range_indicators = +         (Struct.RangeIndicator.generate +            start_loc +            mov_dist +            atk_dist +            def_dist +            (cost_fun) +         ), +      cost_fun = cost_fun +   } + +get_current_location : Type -> Struct.Location.Type +get_current_location navigator = +   (Struct.Path.get_current_location navigator.path) + +get_starting_location : Type -> Struct.Location.Type +get_starting_location navigator = navigator.starting_location + +get_remaining_points : Type -> Int +get_remaining_points navigator = +    (Struct.Path.get_remaining_points navigator.path) + +get_range_markers : ( +      Type -> +      (List +         (Struct.Location.Ref, Struct.RangeIndicator.Type) +      ) +   ) +get_range_markers navigator = (Dict.toList navigator.range_indicators) + +get_path : Type -> (List Struct.Direction.Type) +get_path navigator = (Struct.Path.get_summary navigator.path) + +get_summary : Type -> Summary +get_summary navigator = +   { +      starting_location = navigator.starting_location, +      path = (Struct.Path.get_summary navigator.path), +      markers = +         (List.map +            (\(loc, range_indicator) -> +               ( +                  loc, +                  (Struct.RangeIndicator.get_marker +                     range_indicator +                  ) +               ) +            ) +            (Dict.toList +               navigator.range_indicators +            ) +         ), +      locked_path = navigator.locked_path +   } + +clear_path : Type -> Type +clear_path navigator = +   if (navigator.locked_path) +   then +      navigator +   else +      {navigator | +         path = +            (Struct.Path.new +               navigator.starting_location +               navigator.movement_dist +            ) +      } + +lock_path : Type -> Type +lock_path navigator = +   {navigator | +      range_indicators = +         (Struct.RangeIndicator.generate +            (Struct.Path.get_current_location navigator.path) +            0 +            navigator.attack_dist +            navigator.defense_dist +            (navigator.cost_fun) +         ), +      locked_path = True +   } + +try_adding_step : ( +      Struct.Direction.Type -> +      Type -> +      (Maybe Type) +   ) +try_adding_step dir navigator = +   if (navigator.locked_path) +   then +      Nothing +   else +      case +         (Struct.Path.try_following_direction +            (navigator.cost_fun) +            (Just navigator.path) +            dir +         ) +      of +         (Just path) -> (Just {navigator | path = path}) +         Nothing -> Nothing + +try_getting_path_to : ( +      Struct.Location.Ref -> +      Type -> +      (Maybe (List Struct.Direction.Type)) +   ) +try_getting_path_to loc_ref navigator = +   case (Dict.get loc_ref navigator.range_indicators) of +      (Just target) -> +         (Just (Struct.RangeIndicator.get_path target)) + +      Nothing -> Nothing + diff --git a/src/battle/src/Struct/Path.elm b/src/battle/src/Struct/Path.elm new file mode 100644 index 0000000..70e6980 --- /dev/null +++ b/src/battle/src/Struct/Path.elm @@ -0,0 +1,175 @@ +module Struct.Path exposing +   ( +      Type, +      new, +      get_current_location, +      get_remaining_points, +      get_summary, +      try_following_direction +   ) + +-- Elm ------------------------------------------------------------------------- +import Set + +-- Map ------------------------------------------------------------------- +import Struct.Direction +import Struct.Location + +import Util.List + +import Constants.Movement + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Type = +   { +      current_location : Struct.Location.Type, +      visited_locations : (Set.Set Struct.Location.Ref), +      previous_directions : (List Struct.Direction.Type), +      previous_points : (List Int), +      remaining_points : Int +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +has_been_to : ( +      Type -> +      Struct.Location.Type -> +      Bool +   ) +has_been_to path location = +   ( +      (path.current_location == location) +      || +      (Set.member +         (Struct.Location.get_ref location) +         path.visited_locations +      ) +   ) + +try_moving_to : ( +      Type -> +      Struct.Direction.Type -> +      Struct.Location.Type -> +      Int -> +      (Maybe Type) +   ) +try_moving_to path dir next_loc cost = +   let +      remaining_points = (path.remaining_points - cost) +   in +      if (remaining_points >= 0) +      then +         (Just +            {path | +               current_location = next_loc, +               visited_locations = +                  (Set.insert +                     (Struct.Location.get_ref path.current_location) +                     path.visited_locations +                  ), +               previous_directions = (dir :: path.previous_directions), +               previous_points = +                  (path.remaining_points :: path.previous_points), +               remaining_points = remaining_points +            } +         ) +      else +         Nothing + +try_backtracking_to : ( +      Type -> +      Struct.Direction.Type -> +      Struct.Location.Type -> +      (Maybe Type) +   ) +try_backtracking_to path dir location = +   case +      ( +         (Util.List.pop path.previous_directions), +         (Util.List.pop path.previous_points) +      ) +   of +      ( +         (Just (prev_dir_head, prev_dir_tail)), +         (Just (prev_pts_head, prev_pts_tail)) +      ) -> +         if (prev_dir_head == (Struct.Direction.opposite_of dir)) +         then +            (Just +               {path | +                  current_location = location, +                  visited_locations = +                     (Set.remove +                        (Struct.Location.get_ref location) +                        path.visited_locations +                     ), +                  previous_directions = prev_dir_tail, +                  previous_points = prev_pts_tail, +                  remaining_points = prev_pts_head +               } +            ) +         else +            Nothing + +      (_, _) -> +         Nothing + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +new : Struct.Location.Type -> Int -> Type +new start points = +   { +      current_location = start, +      visited_locations = Set.empty, +      previous_directions = [], +      previous_points = [], +      remaining_points = points +   } + +get_current_location : Type -> Struct.Location.Type +get_current_location path = path.current_location + +get_remaining_points : Type -> Int +get_remaining_points path = path.remaining_points + +get_summary : Type -> (List Struct.Direction.Type) +get_summary path = path.previous_directions + +try_following_direction : ( +      (Struct.Location.Type -> Int) -> +      (Maybe Type) -> +      Struct.Direction.Type -> +      (Maybe Type) +   ) +try_following_direction cost_fun maybe_path dir = +   case maybe_path of +      (Just path) -> +         let +            next_location = +               (Struct.Location.neighbor +                  dir +                  path.current_location +               ) +            next_location_cost = (cost_fun next_location) +         in +            if (next_location_cost <= Constants.Movement.max_points) +            then +               if (has_been_to path next_location) +               then +                  (try_backtracking_to path dir next_location) +               else +                  (try_moving_to +                     path +                     dir +                     next_location +                     next_location_cost +                  ) +            else +               Nothing + +      Nothing -> Nothing diff --git a/src/battle/src/Struct/RangeIndicator.elm b/src/battle/src/Struct/RangeIndicator.elm new file mode 100644 index 0000000..e78cecb --- /dev/null +++ b/src/battle/src/Struct/RangeIndicator.elm @@ -0,0 +1,344 @@ +module Struct.RangeIndicator exposing +   ( +      Type, +      generate, +      get_marker, +      get_path +   ) + +-- Elm ------------------------------------------------------------------------- +import Dict +import List + +-- Map ------------------------------------------------------------------- +import Struct.Direction +import Struct.Location +import Struct.Marker + +import Constants.Movement + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Type = +   { +      distance: Int, +      true_range: Int, +      atk_range: Int, +      path: (List Struct.Direction.Type), +      marker: Struct.Marker.Type +   } + +type alias SearchParameters = +   { +      maximum_distance: Int, +      maximum_attack_range: Int, +      minimum_defense_range: Int, +      cost_function: (Struct.Location.Type -> Int), +      true_range_fun: (Struct.Location.Type -> Int) +   } + +type alias LocatedIndicator = +   { +      location_ref: Struct.Location.Ref, +      indicator: Type +   } +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_closest : ( +      Int -> +      Struct.Location.Ref -> +      Type -> +      LocatedIndicator -> +      LocatedIndicator +   ) +get_closest max_dist ref indicator current_best = +   if (is_closer max_dist indicator current_best.indicator) +   then +      { +         location_ref = ref, +         indicator = indicator +      } +   else +      current_best + +is_closer : Int -> Type -> Type -> Bool +is_closer max_dist candidate current = +   ( +      -- It's closer when moving +      (candidate.distance < current.distance) +      || +      ( +         -- Or neither are reachable by moving, +         (max_dist <= candidate.distance) +         && (max_dist <= current.distance) +         -- but the new one is closer when attacking. +         && (candidate.atk_range < current.atk_range) +      ) +   ) + +generate_neighbor : ( +      SearchParameters -> +      Struct.Location.Type -> +      Struct.Direction.Type -> +      Type -> +      (Int, Type) +   ) +generate_neighbor search_params neighbor_loc dir src_indicator = +   let +      node_cost = (search_params.cost_function neighbor_loc) +      new_dist = +         if (node_cost == Constants.Movement.cost_when_occupied_tile) +         then +            (search_params.maximum_distance + 1) +         else +            (src_indicator.distance + node_cost) +      new_atk_range = (src_indicator.atk_range + 1) +      new_true_range = (search_params.true_range_fun neighbor_loc) +      can_defend = (new_true_range > search_params.minimum_defense_range) +   in +      if (new_dist > search_params.maximum_distance) +      then +         ( +            node_cost, +            { +               distance = (search_params.maximum_distance + 1), +               atk_range = new_atk_range, +               true_range = new_true_range, +               path = (dir :: src_indicator.path), +               marker = +                  if (can_defend) +                  then +                     Struct.Marker.CanAttackCanDefend +                  else +                     Struct.Marker.CanAttackCantDefend +            } +         ) +      else +         ( +            node_cost, +            { +               distance = new_dist, +               atk_range = 0, +               true_range = new_true_range, +               path = (dir :: src_indicator.path), +               marker = +                  if (can_defend) +                  then +                     Struct.Marker.CanGoToCanDefend +                  else +                     Struct.Marker.CanGoToCantDefend +            } +         ) + +candidate_is_acceptable : (SearchParameters -> Int -> Type -> Bool) +candidate_is_acceptable search_params cost candidate = +   ( +      (cost /= Constants.Movement.cost_when_out_of_bounds) +      && +      ( +         (candidate.distance <= search_params.maximum_distance) +         || (candidate.atk_range <= search_params.maximum_attack_range) +      ) +   ) + +candidate_is_an_improvement : ( +      SearchParameters -> +      Struct.Location.Ref -> +      Type -> +      (Dict.Dict Struct.Location.Ref Type) -> +      Bool +   ) +candidate_is_an_improvement search_params loc_ref candidate alternatives = +   case (Dict.get loc_ref alternatives) of +      (Just alternative) -> +         (is_closer search_params.maximum_distance candidate alternative) + +      Nothing -> +         True + +handle_neighbors : ( +      LocatedIndicator -> +      (Dict.Dict Struct.Location.Ref Type) -> +      SearchParameters -> +      Struct.Direction.Type -> +      (Dict.Dict Struct.Location.Ref Type) -> +      (Dict.Dict Struct.Location.Ref Type) +   ) +handle_neighbors src results search_params dir remaining = +   let +      src_loc = (Struct.Location.from_ref src.location_ref) +      neighbor_loc = (Struct.Location.neighbor dir src_loc) +      neighbor_loc_ref = (Struct.Location.get_ref neighbor_loc) +   in +      case (Dict.get neighbor_loc_ref results) of +         (Just _) -> +            -- A minimal path for this location has already been found +            remaining + +         Nothing -> +            let +               (candidate_cost, candidate) = +                  (generate_neighbor +                     search_params +                     neighbor_loc +                     dir +                     src.indicator +                  ) +            in +               if +               ( +                  (candidate_is_acceptable +                     search_params +                     candidate_cost +                     candidate +                  ) +                  && +                  (candidate_is_an_improvement +                     search_params +                     neighbor_loc_ref +                     candidate +                     remaining +                  ) +               ) +               then +                  (Dict.insert neighbor_loc_ref candidate remaining) +               else +                  remaining + +find_closest_in : ( +      SearchParameters -> +      (Dict.Dict Struct.Location.Ref Type) -> +      LocatedIndicator +   ) +find_closest_in search_params remaining = +   (Dict.foldl +      (get_closest search_params.maximum_distance) +      { +         location_ref = (-1, -1), +         indicator = +            { +               distance = Constants.Movement.cost_when_out_of_bounds, +               path = [], +               atk_range = Constants.Movement.cost_when_out_of_bounds, +               true_range = Constants.Movement.cost_when_out_of_bounds, +               marker = Struct.Marker.CanAttackCanDefend +            } +      } +      remaining +   ) + +resolve_marker_type : SearchParameters -> Type -> Type +resolve_marker_type search_params indicator = +   {indicator | +      marker = +         case +            ( +               (indicator.atk_range > 0), +               (indicator.true_range <= search_params.minimum_defense_range) +            ) +         of +            (True, True) -> Struct.Marker.CanAttackCantDefend +            (True, False) -> Struct.Marker.CanAttackCanDefend +            (False, True) -> Struct.Marker.CanGoToCantDefend +            (False, False) -> Struct.Marker.CanGoToCanDefend +   } + +insert_in_dictionary : ( +      LocatedIndicator -> +      (Dict.Dict Struct.Location.Ref Type) -> +      (Dict.Dict Struct.Location.Ref Type) +   ) +insert_in_dictionary located_indicator dict = +   (Dict.insert +      located_indicator.location_ref +      located_indicator.indicator +      dict +   ) + +search : ( +      (Dict.Dict Struct.Location.Ref Type) -> +      (Dict.Dict Struct.Location.Ref Type) -> +      SearchParameters -> +      (Dict.Dict Struct.Location.Ref Type) +   ) +search result remaining search_params = +   if (Dict.isEmpty remaining) +   then +      result +   else +      let +         closest_located_indicator = (find_closest_in search_params remaining) +         finalized_clos_loc_ind = +            {closest_located_indicator| +               indicator = +                  (resolve_marker_type +                     search_params +                     closest_located_indicator.indicator +                  ) +            } +      in +         (search +            (insert_in_dictionary finalized_clos_loc_ind result) +            (List.foldl +               (handle_neighbors +                  finalized_clos_loc_ind +                  result +                  search_params +               ) +               (Dict.remove finalized_clos_loc_ind.location_ref remaining) +               [ +                  Struct.Direction.Left, +                  Struct.Direction.Right, +                  Struct.Direction.Up, +                  Struct.Direction.Down +               ] +            ) +            search_params +         ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +generate : ( +      Struct.Location.Type -> +      Int -> +      Int -> +      Int -> +      (Struct.Location.Type -> Int) -> +      (Dict.Dict Struct.Location.Ref Type) +   ) +generate location max_dist atk_range def_range cost_fun = +   (search +      Dict.empty +      (Dict.insert +         (Struct.Location.get_ref location) +         { +            distance = 0, +            path = [], +            atk_range = 0, +            true_range = 0, +            marker = +               if (def_range == 0) +               then +                  Struct.Marker.CanGoToCanDefend +               else +                  Struct.Marker.CanGoToCantDefend +         } +         Dict.empty +      ) +      { +         maximum_distance = max_dist, +         maximum_attack_range = atk_range, +         minimum_defense_range = def_range, +         cost_function = (cost_fun), +         true_range_fun = (Struct.Location.dist location) +      } +   ) + +get_marker : Type -> Struct.Marker.Type +get_marker indicator = indicator.marker + +get_path : Type -> (List Struct.Direction.Type) +get_path indicator = indicator.path diff --git a/src/battle/src/Struct/ServerReply.elm b/src/battle/src/Struct/ServerReply.elm new file mode 100644 index 0000000..87325a5 --- /dev/null +++ b/src/battle/src/Struct/ServerReply.elm @@ -0,0 +1,33 @@ +module Struct.ServerReply exposing (Type(..)) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.Armor +import Struct.Map +import Struct.Character +import Struct.Tile +import Struct.TurnResult +import Struct.Weapon + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +type Type = +   Okay +   | AddArmor Struct.Armor.Type +   | AddWeapon Struct.Weapon.Type +   | AddCharacter (Struct.Character.Type, Int, Int, Int) +   | AddTile Struct.Tile.Type +   | SetMap Struct.Map.Type +   | TurnResults (List Struct.TurnResult.Type) +   | SetTimeline (List Struct.TurnResult.Type) + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- diff --git a/src/battle/src/Struct/Statistics.elm b/src/battle/src/Struct/Statistics.elm new file mode 100644 index 0000000..aa3de39 --- /dev/null +++ b/src/battle/src/Struct/Statistics.elm @@ -0,0 +1,176 @@ +module Struct.Statistics exposing +   ( +      Type, +      get_movement_points, +      get_max_health, +      get_dodges, +      get_parries, +      get_damage_min, +      get_damage_max, +      get_accuracy, +      get_double_hits, +      get_critical_hits, +      new +   ) + +-- Elm ------------------------------------------------------------------------- +import List + +-- Map ------------------------------------------------------------------- +import Struct.Attributes +import Struct.Armor +import Struct.Weapon +import Struct.WeaponSet + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Type = +   { +      movement_points : Int, +      max_health : Int, +      dodges : Int, +      parries : Int, +      damage_min : Int, +      damage_max : Int, +      accuracy : Int, +      double_hits : Int, +      critical_hits : Int +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +average : (List Int) -> Float +average l = ((toFloat (List.sum l)) / (toFloat (List.length l))) + +float_to_int : Float -> Int +float_to_int f = +   (ceiling f) + +gentle_squared_growth : Int -> Int +gentle_squared_growth v = (float_to_int (((toFloat v)^1.8)/20.0)) + +gentle_squared_growth_f : Float -> Int +gentle_squared_growth_f v = (float_to_int ((v^1.8)/20.0)) + +sudden_squared_growth : Int -> Int +sudden_squared_growth v = (float_to_int (((toFloat v)^2.5)/1000.0)) + +sudden_squared_growth_f : Float -> Int +sudden_squared_growth_f v = (float_to_int ((v^2.5)/1000.0)) + +sudden_exp_growth : Int -> Int +sudden_exp_growth v = (float_to_int (4.0^((toFloat v)/25.0))) + +sudden_exp_growth_f : Float -> Int +sudden_exp_growth_f f = (float_to_int (4.0^(f/25.0))) + +already_high_slow_growth : Int -> Int +already_high_slow_growth v = +   (float_to_int +      (30.0 * (logBase 2.718281828459 (((toFloat v) + 5.0)/4.0))) +   ) + +damage_base_mod : Float -> Float +damage_base_mod str = (((str^1.8)/2000.0) - 0.75) + +apply_damage_base_mod : Float -> Float -> Int +apply_damage_base_mod bmod dmg = +   (max 0 (float_to_int (dmg + (bmod * dmg)))) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_movement_points : Type -> Int +get_movement_points t = t.movement_points + +get_max_health : Type -> Int +get_max_health t = t.max_health + +get_dodges : Type -> Int +get_dodges t = t.dodges + +get_parries : Type -> Int +get_parries t = t.parries + +get_damage_min : Type -> Int +get_damage_min t = t.damage_min + +get_damage_max : Type -> Int +get_damage_max t = t.damage_max + +get_accuracy : Type -> Int +get_accuracy t = t.accuracy + +get_double_hits : Type -> Int +get_double_hits t = t.double_hits + +get_critical_hits : Type -> Int +get_critical_hits t = t.critical_hits + +new : ( +      Struct.Attributes.Type -> +      Struct.WeaponSet.Type -> +      Struct.Armor.Type -> +      Type +   ) +new att wp_set ar = +   let +      active_weapon = (Struct.WeaponSet.get_active_weapon wp_set) +      actual_att = +         (Struct.Armor.apply_to_attributes +            ar +            (Struct.Weapon.apply_to_attributes active_weapon att) +         ) +      constitution = (Struct.Attributes.get_constitution actual_att) +      dexterity = (Struct.Attributes.get_dexterity actual_att) +      intelligence = (Struct.Attributes.get_intelligence actual_att) +      mind = (Struct.Attributes.get_mind actual_att) +      speed = (Struct.Attributes.get_speed actual_att) +      strength = (Struct.Attributes.get_strength actual_att) +      dmg_bmod = (damage_base_mod (toFloat strength)) +   in +      { +         movement_points = +            (gentle_squared_growth_f +               (average [mind, constitution, constitution, speed, speed, speed]) +            ), +         max_health = +            (gentle_squared_growth_f +               (average [constitution, constitution, constitution, mind]) +            ), +         dodges = +            (clamp +               0 +               100 +               (sudden_exp_growth_f +                  (average +                     [dexterity, mind, speed] +                  ) +               ) +            ), +         parries = +            (clamp +               0 +               75 +               (sudden_exp_growth_f +                  (average [dexterity, intelligence, speed, strength]) +               ) +            ), +         damage_min = +            (apply_damage_base_mod +               dmg_bmod +               (toFloat (Struct.Weapon.get_min_damage active_weapon)) +            ), +         damage_max = +            (apply_damage_base_mod +               dmg_bmod +               (toFloat (Struct.Weapon.get_max_damage active_weapon)) +            ), +         accuracy = (sudden_squared_growth dexterity), +         double_hits = +            (clamp 0 100 (sudden_squared_growth_f (average [mind, speed]))), +         critical_hits = +            (clamp 0 100 (sudden_squared_growth intelligence)) +   } diff --git a/src/battle/src/Struct/Tile.elm b/src/battle/src/Struct/Tile.elm new file mode 100644 index 0000000..550169b --- /dev/null +++ b/src/battle/src/Struct/Tile.elm @@ -0,0 +1,203 @@ +module Struct.Tile exposing +   ( +      Ref, +      Type, +      Instance, +      new, +      new_instance, +      error_tile_instance, +      get_id, +      get_name, +      get_range_minimum, +      get_range_maximum, +      get_cost, +      get_instance_cost, +      get_location, +      get_icon_id, +      get_type_id, +      get_variant_id, +      solve_tile_instance, +      decoder +   ) + +-- Elm ------------------------------------------------------------------------- +import List + +import Json.Decode +import Json.Decode.Pipeline + +-- Map ------------------------------------------------------------------- +import Constants.UI +import Constants.Movement + +import Struct.Location + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Ref = Int + +type alias PartiallyDecoded = +   { +      id : Int, +      nam : String, +      ct : Int, +      rmi : Int, +      rma : Int +   } + +type alias Type = +   { +      id : Int, +      name : String, +      crossing_cost : Int, +      range_minimum : Int, +      range_maximum : Int +   } + +type alias Instance = +   { +      location : Struct.Location.Type, +      icon_id : Int, +      crossing_cost : Int, +      type_id : Int +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +noise_function : Int -> Int -> Int -> Int +noise_function a b c = +   (round (radians (toFloat ((a + 1) * 2 + (b + 1) * 3 + c)))) + +finish_decoding : PartiallyDecoded -> Type +finish_decoding add_tile = +   { +      id = add_tile.id, +      name = add_tile.nam, +      crossing_cost = add_tile.ct, +      range_minimum = add_tile.rmi, +      range_maximum = add_tile.rma +   } + +seek_tile_instance_type : Instance -> Type -> (Maybe Type) -> (Maybe Type) +seek_tile_instance_type instance candidate current_sol = +   if (current_sol == Nothing) +   then +      let +         icon_id = instance.icon_id +      in +         if +         ( +            (icon_id >= candidate.range_minimum) +            && (icon_id <= candidate.range_maximum) +         ) +         then +            (Just candidate) +         else +            current_sol +   else +      current_sol + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +new : Int -> String -> Int -> Int -> Int -> Type +new id name crossing_cost range_minimum range_maximum = +   { +      id = id, +      name = name, +      crossing_cost = crossing_cost, +      range_minimum = range_minimum, +      range_maximum = range_maximum +   } + +new_instance : Int -> Int -> Int -> Int -> Int -> Instance +new_instance x y icon_id crossing_cost type_id = +   { +      location = {x = x, y = y}, +      icon_id = icon_id, +      crossing_cost = crossing_cost, +      type_id = type_id +   } + +error_tile_instance : Int -> Int -> Instance +error_tile_instance x y = +   { +      location = {x = x, y = y}, +      icon_id = -1, +      type_id = -1, +      crossing_cost = Constants.Movement.cost_when_out_of_bounds +   } + + +get_id : Type -> Int +get_id tile = tile.id + +get_cost : Type -> Int +get_cost tile = tile.crossing_cost + +get_instance_cost : Instance -> Int +get_instance_cost tile_inst = tile_inst.crossing_cost + +get_name : Type -> String +get_name tile = tile.name + +get_range_minimum : Type -> Int +get_range_minimum tile = tile.range_minimum + +get_range_maximum : Type -> Int +get_range_maximum tile = tile.range_maximum + +get_location : Instance -> Struct.Location.Type +get_location tile_inst = tile_inst.location + +get_icon_id : Instance -> String +get_icon_id tile_inst = (toString tile_inst.icon_id) + +get_type_id: Instance -> Int +get_type_id tile_inst = tile_inst.type_id + +get_variant_id : Instance -> Int +get_variant_id tile_inst = +   ( +      (noise_function +         tile_inst.location.x +         tile_inst.location.y +         tile_inst.crossing_cost +      ) +      % Constants.UI.variants_per_tile +   ) + +solve_tile_instance : (List Type) -> Instance -> Instance +solve_tile_instance tiles tile_instance = +   let +      maybe_type = +         (List.foldr (seek_tile_instance_type tile_instance) Nothing tiles) +   in +      case maybe_type of +         (Just tile) -> +            {tile_instance | +               type_id = tile.id, +               crossing_cost = tile.crossing_cost +            } + +         Nothing -> +            (error_tile_instance +               tile_instance.location.x +               tile_instance.location.y +            ) + +decoder : (Json.Decode.Decoder Type) +decoder = +   (Json.Decode.map +      (finish_decoding) +      (Json.Decode.Pipeline.decode +         PartiallyDecoded +         |> (Json.Decode.Pipeline.required "id" Json.Decode.int) +         |> (Json.Decode.Pipeline.required "nam" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "ct" Json.Decode.int) +         |> (Json.Decode.Pipeline.required "rmi" Json.Decode.int) +         |> (Json.Decode.Pipeline.required "rma" Json.Decode.int) +      ) +   ) diff --git a/src/battle/src/Struct/TurnResult.elm b/src/battle/src/Struct/TurnResult.elm new file mode 100644 index 0000000..c113994 --- /dev/null +++ b/src/battle/src/Struct/TurnResult.elm @@ -0,0 +1,502 @@ +module Struct.TurnResult exposing +   ( +      Type(..), +      Attack, +      Movement, +      WeaponSwitch, +      PlayerVictory, +      PlayerDefeat, +      PlayerTurnStart, +      get_next_movement_dir, +      get_actor_index, +      get_attack_defender_index, +      maybe_get_attack_next_step, +      apply_to_characters, +      apply_inverse_to_characters, +      apply_step_to_characters, +      maybe_remove_step, +      decoder +   ) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Json.Decode + +-- Map ------------------------------------------------------------------- +import Struct.Attack +import Struct.Character +import Struct.Direction +import Struct.Location +import Struct.WeaponSet + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Movement = +   { +      character_index : Int, +      path : (List Struct.Direction.Type), +      destination : Struct.Location.Type +   } + +type alias Attack = +   { +      attacker_index : Int, +      defender_index : Int, +      sequence : (List Struct.Attack.Type) +   } + +type alias WeaponSwitch = +   { +      character_index : Int +   } + +type alias PlayerVictory = +   { +      player_index : Int +   } + +type alias PlayerDefeat = +   { +      player_index : Int +   } + +type alias PlayerTurnStart = +   { +      player_index : Int +   } + +type Type = +   Moved Movement +   | Attacked Attack +   | SwitchedWeapon WeaponSwitch +   | PlayerWon PlayerVictory +   | PlayerLost PlayerDefeat +   | PlayerTurnStarted PlayerTurnStart + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_movement_to_character : ( +      Movement -> +      Struct.Character.Type -> +      Struct.Character.Type +   ) +apply_movement_to_character movement char = +   (Struct.Character.set_location movement.destination char) + +apply_movement_step_to_character : ( +      Movement -> +      Struct.Character.Type -> +      Struct.Character.Type +   ) +apply_movement_step_to_character movement char = +   case (List.head movement.path) of +      (Just dir) -> +         (Struct.Character.set_location +            (Struct.Location.neighbor dir (Struct.Character.get_location char)) +            char +         ) + +      Nothing -> char + +apply_inverse_movement_to_character : ( +      Movement -> +      Struct.Character.Type -> +      Struct.Character.Type +   ) +apply_inverse_movement_to_character movement char = +   (Struct.Character.set_location +      (List.foldr +         (Struct.Location.neighbor) +         (movement.destination) +         (List.map (Struct.Direction.opposite_of) movement.path) +      ) +      char +   ) + +apply_weapon_switch_to_character : ( +      Struct.Character.Type -> +      Struct.Character.Type +   ) +apply_weapon_switch_to_character char = +   (Struct.Character.set_weapons +      (Struct.WeaponSet.switch_weapons +         (Struct.Character.get_weapons char) +      ) +      char +   ) + +apply_attack_to_characters : ( +      Attack -> +      (Array.Array Struct.Character.Type) -> +      (Array.Array Struct.Character.Type) +   ) +apply_attack_to_characters attack characters = +   (List.foldl +      (Struct.Attack.apply_to_characters +         attack.attacker_index +         attack.defender_index +      ) +      characters +      attack.sequence +   ) + +apply_player_defeat_to_characters : ( +      PlayerDefeat -> +      (Array.Array Struct.Character.Type) -> +      (Array.Array Struct.Character.Type) +   ) +apply_player_defeat_to_characters pdefeat characters = +   (Array.map +      (\c -> +         ( +            if ((Struct.Character.get_player_ix c) == pdefeat.player_index) +            then (Struct.Character.set_defeated True c) +            else c +         ) +      ) +      characters +   ) + +apply_inverse_player_defeat_to_characters : ( +      PlayerDefeat -> +      (Array.Array Struct.Character.Type) -> +      (Array.Array Struct.Character.Type) +   ) +apply_inverse_player_defeat_to_characters pdefeat characters = +   (Array.map +      (\c -> +         ( +            if ((Struct.Character.get_player_ix c) == pdefeat.player_index) +            then (Struct.Character.set_defeated False c) +            else c +         ) +      ) +      characters +   ) + +apply_attack_step_to_characters : ( +      Attack -> +      (Array.Array Struct.Character.Type) -> +      (Array.Array Struct.Character.Type) +   ) +apply_attack_step_to_characters attack characters = +   case (List.head attack.sequence) of +      (Just attack_step) -> +         (Struct.Attack.apply_to_characters +            attack.attacker_index +            attack.defender_index +            attack_step +            characters +         ) + +      Nothing -> characters + +apply_inverse_attack_to_characters : ( +      Attack -> +      (Array.Array Struct.Character.Type) -> +      (Array.Array Struct.Character.Type) +   ) +apply_inverse_attack_to_characters attack characters = +   (List.foldr +      (Struct.Attack.apply_inverse_to_characters +         attack.attacker_index +         attack.defender_index +      ) +      characters +      attack.sequence +   ) + +movement_decoder : (Json.Decode.Decoder Movement) +movement_decoder = +   (Json.Decode.map3 +      Movement +      (Json.Decode.field "ix" Json.Decode.int) +      (Json.Decode.field +         "p" +         (Json.Decode.list (Struct.Direction.decoder)) +      ) +      (Json.Decode.field +         "nlc" +         (Struct.Location.decoder) +      ) +   ) + +attack_decoder : (Json.Decode.Decoder Attack) +attack_decoder = +   (Json.Decode.map3 +      Attack +      (Json.Decode.field "aix" Json.Decode.int) +      (Json.Decode.field "dix" Json.Decode.int) +      (Json.Decode.field +         "seq" +         (Json.Decode.list (Struct.Attack.decoder)) +      ) +   ) + +weapon_switch_decoder : (Json.Decode.Decoder WeaponSwitch) +weapon_switch_decoder = +   (Json.Decode.map +      WeaponSwitch +      (Json.Decode.field "ix" Json.Decode.int) +   ) + +player_won_decoder : (Json.Decode.Decoder PlayerVictory) +player_won_decoder = +   (Json.Decode.map +      PlayerVictory +      (Json.Decode.field "ix" Json.Decode.int) +   ) + +player_lost_decoder : (Json.Decode.Decoder PlayerDefeat) +player_lost_decoder = +   (Json.Decode.map +      PlayerDefeat +      (Json.Decode.field "ix" Json.Decode.int) +   ) + +player_turn_started_decoder : (Json.Decode.Decoder PlayerTurnStart) +player_turn_started_decoder = +   (Json.Decode.map +      PlayerTurnStart +      (Json.Decode.field "ix" Json.Decode.int) +   ) + +internal_decoder : String -> (Json.Decode.Decoder Type) +internal_decoder kind = +   case kind of +      "swp" -> +         (Json.Decode.map +            (\x -> (SwitchedWeapon x)) +            (weapon_switch_decoder) +         ) + +      "mv" -> +         (Json.Decode.map +            (\x -> (Moved x)) +            (movement_decoder) +         ) + +      "atk" -> +         (Json.Decode.map +            (\x -> (Attacked x)) +            (attack_decoder) +         ) + +      "pwo" -> +         (Json.Decode.map +            (\x -> (PlayerWon x)) +            (player_won_decoder) +         ) + +      "plo" -> +         (Json.Decode.map +            (\x -> (PlayerLost x)) +            (player_lost_decoder) +         ) + +      "pts" -> +         (Json.Decode.map +            (\x -> (PlayerTurnStarted x)) +            (player_turn_started_decoder) +         ) + +      other -> +         (Json.Decode.fail +            ( +               "Unknown kind of turn result: \"" +               ++ other +               ++ "\"." +            ) +         ) + +maybe_remove_movement_step : Movement -> (Maybe Type) +maybe_remove_movement_step movement = +   case (List.tail movement.path) of +      Nothing -> Nothing +      (Just path_tail) -> +         (Just +            (Moved +               {movement | +                  path = path_tail +               } +            ) +         ) + +maybe_remove_attack_step : Attack -> (Maybe Type) +maybe_remove_attack_step attack = +   case (List.tail attack.sequence) of +      Nothing -> Nothing +      (Just sequence_tail) -> +         (Just +            (Attacked +               {attack | +                  sequence = sequence_tail +               } +            ) +         ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to_characters : ( +      Type -> +      (Array.Array Struct.Character.Type) -> +      (Array.Array Struct.Character.Type) +   ) +apply_to_characters turn_result characters = +   case turn_result of +      (Moved movement) -> +         case (Array.get movement.character_index characters) of +            (Just char) -> +               (Array.set +                  movement.character_index +                  (apply_movement_to_character movement char) +                  characters +               ) + +            Nothing -> +               characters + +      (SwitchedWeapon weapon_switch) -> +         case (Array.get weapon_switch.character_index characters) of +            (Just char) -> +               (Array.set +                  weapon_switch.character_index +                  (apply_weapon_switch_to_character char) +                  characters +               ) + +            Nothing -> +               characters + +      (Attacked attack) -> +         (apply_attack_to_characters attack characters) + +      (PlayerWon pvict) -> characters + +      (PlayerLost pdefeat) -> +         (apply_player_defeat_to_characters pdefeat characters) + +      (PlayerTurnStarted pturns) -> characters + +apply_step_to_characters : ( +      Type -> +      (Array.Array Struct.Character.Type) -> +      (Array.Array Struct.Character.Type) +   ) +apply_step_to_characters turn_result characters = +   case turn_result of +      (Moved movement) -> +         case (Array.get movement.character_index characters) of +            (Just char) -> +               (Array.set +                  movement.character_index +                  (apply_movement_step_to_character movement char) +                  characters +               ) + +            Nothing -> +               characters + +      (SwitchedWeapon weapon_switch) -> +         case (Array.get weapon_switch.character_index characters) of +            (Just char) -> +               (Array.set +                  weapon_switch.character_index +                  (apply_weapon_switch_to_character char) +                  characters +               ) + +            Nothing -> +               characters + +      (Attacked attack) -> +         (apply_attack_step_to_characters attack characters) + +      (PlayerWon pvict) -> characters + +      (PlayerLost pdefeat) -> +         (apply_player_defeat_to_characters pdefeat characters) + +      (PlayerTurnStarted pturns) -> characters + +apply_inverse_to_characters : ( +      Type -> +      (Array.Array Struct.Character.Type) -> +      (Array.Array Struct.Character.Type) +   ) +apply_inverse_to_characters turn_result characters = +   case turn_result of +      (Moved movement) -> +         case (Array.get movement.character_index characters) of +            (Just char) -> +               (Array.set +                  movement.character_index +                  (apply_inverse_movement_to_character movement char) +                  characters +               ) + +            Nothing -> +               characters + +      (SwitchedWeapon weapon_switch) -> +         case (Array.get weapon_switch.character_index characters) of +            (Just char) -> +               (Array.set +                  weapon_switch.character_index +                  (apply_weapon_switch_to_character char) +                  characters +               ) + +            Nothing -> +               characters + +      (Attacked attack) -> +         (apply_inverse_attack_to_characters attack characters) + +      (PlayerWon pvict) -> characters + +      (PlayerLost pdefeat) -> +         (apply_inverse_player_defeat_to_characters pdefeat characters) + +      (PlayerTurnStarted pturns) -> characters + +decoder : (Json.Decode.Decoder Type) +decoder = +   (Json.Decode.field "t" Json.Decode.string) +   |> (Json.Decode.andThen internal_decoder) + +maybe_remove_step : Type -> (Maybe Type) +maybe_remove_step turn_result = +   case turn_result of +      (Moved movement) -> (maybe_remove_movement_step movement) +      (SwitchedWeapon _) -> Nothing +      (Attacked attack) -> (maybe_remove_attack_step attack) +      (PlayerWon pvict) -> Nothing +      (PlayerLost pdefeat) -> Nothing +      (PlayerTurnStarted pturns) -> Nothing + +get_next_movement_dir : Movement -> Struct.Direction.Type +get_next_movement_dir movement = +   case (List.head movement.path) of +      (Just dir) -> dir +      Nothing -> Struct.Direction.None + +get_attack_defender_index : Attack -> Int +get_attack_defender_index attack = attack.defender_index + +maybe_get_attack_next_step : Attack -> (Maybe Struct.Attack.Type) +maybe_get_attack_next_step attack = (List.head attack.sequence) + +get_actor_index : Type -> Int +get_actor_index turn_result = +   case turn_result of +      (Moved movement) -> movement.character_index +      (SwitchedWeapon weapon_switch) -> weapon_switch.character_index +      (Attacked attack) -> attack.attacker_index +      (PlayerWon pvict) -> pvict.player_index +      (PlayerLost pdefeat) -> pdefeat.player_index +      (PlayerTurnStarted pturns) -> pturns.player_index diff --git a/src/battle/src/Struct/TurnResultAnimator.elm b/src/battle/src/Struct/TurnResultAnimator.elm new file mode 100644 index 0000000..200938e --- /dev/null +++ b/src/battle/src/Struct/TurnResultAnimator.elm @@ -0,0 +1,123 @@ +module Struct.TurnResultAnimator exposing +   ( +      Type, +      Animation(..), +      maybe_new, +      maybe_trigger_next_step, +      waits_for_focus, +      get_current_animation +   ) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.TurnResult + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +type Animation = +   Inactive +   | AttackSetup (Int, Int) +   | Focus Int +   | TurnResult Struct.TurnResult.Type + +type alias Type = +   { +      remaining_animations : (List Animation), +      current_animation : Animation, +      wait_focus : Bool +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +turn_result_to_animations : Struct.TurnResult.Type -> (List Animation) +turn_result_to_animations turn_result = +   case turn_result of +      (Struct.TurnResult.Attacked attack) -> +         let +            attacker_ix = (Struct.TurnResult.get_actor_index turn_result) +            defender_ix = (Struct.TurnResult.get_attack_defender_index attack) +         in +            [ +               (Focus attacker_ix), +               (Focus defender_ix), +               (AttackSetup (attacker_ix, defender_ix)), +               (TurnResult turn_result) +            ] + +      _ -> +         [ +            (Focus (Struct.TurnResult.get_actor_index turn_result)), +            (TurnResult turn_result) +         ] + +turn_result_to_animations_foldl : ( +      Struct.TurnResult.Type -> +      (List Animation) -> +      (List Animation) +   ) +turn_result_to_animations_foldl turn_result current_animations = +   (List.append current_animations (turn_result_to_animations turn_result)) + +maybe_go_to_next_animation : Type -> (Maybe Type) +maybe_go_to_next_animation tra = +   case +   ( +      (List.head tra.remaining_animations), +      (List.tail tra.remaining_animations) +   ) +   of +      ((Just head), (Just tail)) -> +         (Just +            {tra | +               remaining_animations = tail, +               current_animation = head +            } +         ) + +      (_, _) -> Nothing + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +maybe_new : (List Struct.TurnResult.Type) -> Bool -> (Maybe Type) +maybe_new turn_results wait_focus = +   case (List.head turn_results) of +      (Just head) -> +         (Just +            { +               remaining_animations = +                  (List.foldl +                     (turn_result_to_animations_foldl) +                     [] +                     turn_results +                  ), +               current_animation = Inactive, +               wait_focus = wait_focus +            } +         ) + +      _ -> Nothing + +maybe_trigger_next_step : Type -> (Maybe Type) +maybe_trigger_next_step tra = +   case tra.current_animation of +      (TurnResult action) -> +         ( +            case (Struct.TurnResult.maybe_remove_step action) of +               (Just updated_action) -> +                  (Just {tra | current_animation = (TurnResult updated_action)}) + +               Nothing -> (maybe_go_to_next_animation tra) +         ) + +      _ -> (maybe_go_to_next_animation tra) + +get_current_animation : Type -> Animation +get_current_animation tra = tra.current_animation + +waits_for_focus : Type -> Bool +waits_for_focus tra = tra.wait_focus diff --git a/src/battle/src/Struct/UI.elm b/src/battle/src/Struct/UI.elm new file mode 100644 index 0000000..4837434 --- /dev/null +++ b/src/battle/src/Struct/UI.elm @@ -0,0 +1,135 @@ +module Struct.UI exposing +   ( +      Type, +      Tab(..), +      Action(..), +      default, +      -- Zoom +      get_zoom_level, +      reset_zoom_level, +      mod_zoom_level, +      -- Tab +      try_getting_displayed_tab, +      set_displayed_tab, +      reset_displayed_tab, +      to_string, +      get_all_tabs, +      -- Navigator +      try_getting_displayed_nav, +      set_displayed_nav, +      reset_displayed_nav, +      -- Manual Controls +      has_manual_controls_enabled, +      -- Previous Action +      get_previous_action, +      set_previous_action +   ) + +-- Map ------------------------------------------------------------------- +import Struct.Location +import Struct.Navigator + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type Tab = +   StatusTab +   | CharactersTab +   | SettingsTab +   | TimelineTab + +type Action = +   UsedManualControls +   | SelectedLocation Struct.Location.Ref +   | SelectedCharacter Int +   | AttackedCharacter Int + +type alias Type = +   { +      zoom_level : Float, +      show_manual_controls : Bool, +      displayed_tab : (Maybe Tab), +      previous_action : (Maybe Action), +      displayed_nav : (Maybe Struct.Navigator.Type) +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +default : Type +default = +   { +      zoom_level = 1.0, +      show_manual_controls = True, +      displayed_tab = Nothing, +      previous_action = Nothing, +      displayed_nav = Nothing +   } + +-- Zoom ------------------------------------------------------------------------ +get_zoom_level : Type -> Float +get_zoom_level ui = ui.zoom_level + +reset_zoom_level : Type -> Type +reset_zoom_level ui = {ui | zoom_level = 1.0} + +mod_zoom_level : Float -> Type -> Type +mod_zoom_level mod ui = {ui | zoom_level = (mod * ui.zoom_level)} + +-- Tab ------------------------------------------------------------------------- +try_getting_displayed_tab : Type -> (Maybe Tab) +try_getting_displayed_tab ui = ui.displayed_tab + +set_displayed_tab : Tab -> Type -> Type +set_displayed_tab tab ui = {ui | displayed_tab = (Just tab)} + +reset_displayed_tab : Type -> Type +reset_displayed_tab ui = {ui | displayed_tab = Nothing} + +to_string : Tab -> String +to_string tab = +   case tab of +      StatusTab -> "Status" +      CharactersTab -> "Characters" +      SettingsTab -> "Settings" +      TimelineTab -> "Timeline" + +get_all_tabs : (List Tab) +get_all_tabs = +   [StatusTab, CharactersTab, SettingsTab, TimelineTab] + +-- Navigator ------------------------------------------------------------------- +try_getting_displayed_nav : Type -> (Maybe Struct.Navigator.Type) +try_getting_displayed_nav ui = ui.displayed_nav + +set_displayed_nav : Struct.Navigator.Type -> Type -> Type +set_displayed_nav nav ui = {ui | displayed_nav = (Just nav)} + +reset_displayed_nav : Type -> Type +reset_displayed_nav ui = {ui | displayed_nav = Nothing} + +-- ManualControls -------------------------------------------------------------- +has_manual_controls_enabled : Type -> Bool +has_manual_controls_enabled ui = ui.show_manual_controls + +toggle_manual_controls : Type -> Type +toggle_manual_controls ui = +   if (ui.show_manual_controls) +   then +      {ui | show_manual_controls = False} +   else +      {ui | show_manual_controls = True} + +set_enable_manual_controls : Bool -> Type -> Type +set_enable_manual_controls val ui = {ui | show_manual_controls = val} + +-- Previous Action ------------------------------------------------------------- +set_previous_action : (Maybe Action) -> Type -> Type +set_previous_action act ui = {ui | previous_action = act} + +get_previous_action : Type -> (Maybe Action) +get_previous_action ui = ui.previous_action diff --git a/src/battle/src/Struct/Weapon.elm b/src/battle/src/Struct/Weapon.elm new file mode 100644 index 0000000..d572ed6 --- /dev/null +++ b/src/battle/src/Struct/Weapon.elm @@ -0,0 +1,248 @@ +module Struct.Weapon exposing +   ( +      Type, +      Ref, +      RangeType(..), +      RangeModifier(..), +      DamageType(..), +      DamageModifier(..), +      new, +      get_id, +      get_name, +      get_range_type, +      get_range_modifier, +      get_damage_type, +      get_damage_modifier, +      get_attack_range, +      get_defense_range, +      get_max_damage, +      get_min_damage, +      decoder, +      none, +      apply_to_attributes +   ) + +-- Elm ------------------------------------------------------------------------- +import Json.Decode +import Json.Decode.Pipeline + +-- Map ------------------------------------------------------------------- +import Struct.Attributes + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias PartiallyDecoded = +   { +      id : Int, +      nam : String, +      rt : String, +      rm : String, +      dt : String, +      dm : String, +      cf : Float +   } + +type alias Type = +   { +      id : Int, +      name : String, +      coef : Float, +      range_type : RangeType, +      range_mod : RangeModifier, +      dmg_type : DamageType, +      dmg_mod : DamageModifier, +      def_range : Int, +      atk_range : Int, +      dmg_min : Int, +      dmg_max : Int +   } + +type alias Ref = Int + +type RangeType = Ranged | Melee +type RangeModifier = Long | Short +-- Having multiple types at the same time, like Warframe does, would be nice. +type DamageType = Slash | Blunt | Pierce +type DamageModifier = Heavy | Light + +type alias WeaponType = +   { +      range : RangeType, +      range_mod : RangeModifier, +      dmg_type : DamageType +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_ranges : RangeType -> RangeModifier -> (Int, Int) +get_ranges rt rm = +   case (rt, rm) of +      (Ranged, Long) -> (2, 6) +      (Ranged, Short) -> (1, 4) +      (Melee, Long) -> (0, 2) +      (Melee, Short) -> (0, 1) + +get_damages : Float -> RangeType -> DamageModifier -> (Int, Int) +get_damages coef rt dm = +   case (rt, dm) of +      (Ranged, Heavy) -> ((ceiling (15.0 * coef)), (ceiling (30.0 * coef))) +      (Ranged, Light) -> ((ceiling (10.0 * coef)), (ceiling (25.0 * coef))) +      (Melee, Heavy) -> ((ceiling (20.0 * coef)), (ceiling (35.0 * coef))) +      (Melee, Light) -> ((ceiling (15.0 * coef)), (ceiling (30.0 * coef))) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +new : ( +      Int -> +      String -> +      Float -> +      RangeType -> +      RangeModifier -> +      DamageType -> +      DamageModifier -> +      Type +   ) +new +   id name coef +   range_type range_mod +   dmg_type dmg_mod +   = +   let +      (def_range, atk_range) = (get_ranges range_type range_mod) +      (dmg_min, dmg_max) = (get_damages coef range_type dmg_mod) +   in +   { +      id = id, +      name = name, +      coef = coef, +      range_type = range_type, +      range_mod = range_mod, +      dmg_type = dmg_type, +      dmg_mod = dmg_mod, +      def_range = def_range, +      atk_range = atk_range, +      dmg_min = dmg_min, +      dmg_max = dmg_max +   } + +get_id : Type -> Int +get_id wp = wp.id + +get_name : Type -> String +get_name wp = wp.name + +get_range_type : Type -> RangeType +get_range_type wp = wp.range_type + +get_range_modifier : Type -> RangeModifier +get_range_modifier wp = wp.range_mod + +get_damage_type : Type -> DamageType +get_damage_type wp = wp.dmg_type + +get_damage_modifier : Type -> DamageModifier +get_damage_modifier wp = wp.dmg_mod + +get_attack_range : Type -> Int +get_attack_range wp = wp.atk_range + +get_defense_range : Type -> Int +get_defense_range wp = wp.def_range + +get_max_damage : Type -> Int +get_max_damage wp = wp.dmg_max + +get_min_damage : Type -> Int +get_min_damage wp = wp.dmg_min + +apply_to_attributes : Type -> Struct.Attributes.Type -> Struct.Attributes.Type +apply_to_attributes wp atts = +   let +      impact = (20.0 * wp.coef) +      full_impact = (-1 * (ceiling impact)) +      quarter_impact = (-1 * (ceiling (impact / 4.0))) +   in +      case (wp.range_mod, wp.dmg_mod) of +         (Long, Heavy) -> +            (Struct.Attributes.mod_dexterity +               full_impact +               (Struct.Attributes.mod_speed full_impact atts) +            ) + +         (Long, Light) -> +            (Struct.Attributes.mod_dexterity +               full_impact +               (Struct.Attributes.mod_speed quarter_impact atts) +            ) + +         (Short, Heavy) -> +            (Struct.Attributes.mod_dexterity +               quarter_impact +               (Struct.Attributes.mod_speed full_impact atts) +            ) + +         (Short, Light) -> +            (Struct.Attributes.mod_dexterity +               quarter_impact +               (Struct.Attributes.mod_speed quarter_impact atts) +            ) + +finish_decoding : PartiallyDecoded -> Type +finish_decoding add_weapon = +   (new +      add_weapon.id +      add_weapon.nam +      add_weapon.cf +      ( +         case add_weapon.rt of +            "m" -> Melee +            _ -> Ranged +      ) +      ( +         case add_weapon.rm of +            "l" -> Long +            _ -> Short +      ) +      ( +         case add_weapon.dt of +            "s" -> Slash +            "p" -> Pierce +            _ -> Blunt +      ) +      ( +         case add_weapon.dm of +            "l" -> Light +            _ -> Heavy +      ) +   ) + +decoder : (Json.Decode.Decoder Type) +decoder = +   (Json.Decode.map +      (finish_decoding) +      (Json.Decode.Pipeline.decode +         PartiallyDecoded +         |> (Json.Decode.Pipeline.required "id" Json.Decode.int) +         |> (Json.Decode.Pipeline.required "nam" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "rt" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "rm" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "dt" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "dm" Json.Decode.string) +         |> (Json.Decode.Pipeline.required "cf" Json.Decode.float) +      ) +   ) + +none : Type +none = +   (new +      0 +      "None" +      0.0 +      Melee +      Short +      Blunt +      Light +   ) diff --git a/src/battle/src/Struct/WeaponSet.elm b/src/battle/src/Struct/WeaponSet.elm new file mode 100644 index 0000000..de96daf --- /dev/null +++ b/src/battle/src/Struct/WeaponSet.elm @@ -0,0 +1,39 @@ +module Struct.WeaponSet exposing +   ( +      Type, +      new, +      get_active_weapon, +      get_secondary_weapon, +      switch_weapons +   ) + +-- Map ------------------------------------------------------------------- +import Struct.Weapon + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +type alias Type = +   { +      active : Struct.Weapon.Type, +      secondary : Struct.Weapon.Type +   } + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +new : Struct.Weapon.Type -> Struct.Weapon.Type -> Type +new wp0 wp1 = { active = wp0, secondary = wp1 } + +get_active_weapon : Type -> Struct.Weapon.Type +get_active_weapon set = set.active + +get_secondary_weapon : Type -> Struct.Weapon.Type +get_secondary_weapon set = set.secondary + +switch_weapons : Type -> Type +switch_weapons set = {set | active = set.secondary, secondary = set.active} diff --git a/src/battle/src/Update/AbortTurn.elm b/src/battle/src/Update/AbortTurn.elm new file mode 100644 index 0000000..efffcff --- /dev/null +++ b/src/battle/src/Update/AbortTurn.elm @@ -0,0 +1,24 @@ +module Update.AbortTurn exposing (apply_to) + +-- Elm ------------------------------------------------------------------------- + +-- Struct.Map ------------------------------------------------------------------- +import Struct.CharacterTurn +import Struct.Event +import Struct.Model + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : Struct.Model.Type -> (Struct.Model.Type, (Cmd Struct.Event.Type)) +apply_to model = +   ( +      {model | +         char_turn = (Struct.CharacterTurn.new) +      }, +      Cmd.none +   ) diff --git a/src/battle/src/Update/AttackWithoutMoving.elm b/src/battle/src/Update/AttackWithoutMoving.elm new file mode 100644 index 0000000..3d64e57 --- /dev/null +++ b/src/battle/src/Update/AttackWithoutMoving.elm @@ -0,0 +1,45 @@ +module Update.AttackWithoutMoving exposing (apply_to) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.CharacterTurn +import Struct.Error +import Struct.Event +import Struct.Model + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +make_it_so : Struct.Model.Type -> Struct.Model.Type +make_it_so model = +   {model | +      char_turn = (Struct.CharacterTurn.lock_path model.char_turn) +   } + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model = +   case (Struct.CharacterTurn.get_state model.char_turn) of +      Struct.CharacterTurn.SelectedCharacter -> +         ((make_it_so model), Cmd.none) + +      _ -> +         ( +            (Struct.Model.invalidate +               (Struct.Error.new +                  Struct.Error.Programming +                  ( +                     "Attempt to do an attack without moving, despite no" +                     ++ "character being selected." +                  ) +               ) +               model +            ), +            Cmd.none +         ) diff --git a/src/battle/src/Update/ChangeScale.elm b/src/battle/src/Update/ChangeScale.elm new file mode 100644 index 0000000..69e98b4 --- /dev/null +++ b/src/battle/src/Update/ChangeScale.elm @@ -0,0 +1,26 @@ +module Update.ChangeScale exposing (apply_to) +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.Model +import Struct.UI + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      Float -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model mod = +   if (mod == 0.0) +   then +      ({model | ui = (Struct.UI.reset_zoom_level model.ui)}, Cmd.none) +   else +      ({model | ui = (Struct.UI.mod_zoom_level mod model.ui)}, Cmd.none) diff --git a/src/battle/src/Update/DisplayCharacterInfo.elm b/src/battle/src/Update/DisplayCharacterInfo.elm new file mode 100644 index 0000000..e482e2f --- /dev/null +++ b/src/battle/src/Update/DisplayCharacterInfo.elm @@ -0,0 +1,53 @@ +module Update.DisplayCharacterInfo exposing (apply_to) +-- Elm ------------------------------------------------------------------------- +import Array +import Task + +-- Map ------------------------------------------------------------------- +import Action.Scroll + +import Struct.Character +import Struct.Event +import Struct.Model +import Struct.UI + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +scroll_to_char : Struct.Model.Type -> Int -> (Cmd Struct.Event.Type) +scroll_to_char model char_ix = +   case (Array.get char_ix model.characters) of +      (Just char) -> +         (Task.attempt +            (Struct.Event.attempted) +            (Action.Scroll.to +               (Struct.Character.get_location char) +               model.ui +            ) +         ) + +      Nothing -> +         Cmd.none + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      Int -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model target_ref = +   ( +      {model | +         ui = +            (Struct.UI.set_displayed_tab +               Struct.UI.StatusTab +               (Struct.UI.set_previous_action +                  (Just (Struct.UI.SelectedCharacter target_ref)) +                  model.ui +               ) +            ) +      }, +      (scroll_to_char model target_ref) +   ) diff --git a/src/battle/src/Update/EndTurn.elm b/src/battle/src/Update/EndTurn.elm new file mode 100644 index 0000000..cb60b92 --- /dev/null +++ b/src/battle/src/Update/EndTurn.elm @@ -0,0 +1,91 @@ +module Update.EndTurn exposing (apply_to) + +-- Elm ------------------------------------------------------------------------- + +-- Struct.Map ------------------------------------------------------------------- +import Comm.CharacterTurn + +import Struct.Character +import Struct.CharacterTurn +import Struct.Error +import Struct.Event +import Struct.Model +import Struct.Navigator + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +maybe_disable_char : ( +      (Maybe Struct.Character.Type) -> +      (Maybe Struct.Character.Type) +   ) +maybe_disable_char maybe_char = +   case maybe_char of +      (Just char) -> (Just (Struct.Character.set_enabled False char)) +      Nothing -> Nothing + +make_it_so : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      Struct.Navigator.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +make_it_so model char nav = +   case (Comm.CharacterTurn.try model) of +      (Just cmd) -> +         ( +            (Struct.Model.reset +               (Struct.Model.update_character_fun +                  (Struct.Character.get_index char) +                  (maybe_disable_char) +                  model +               ) +            ), +            cmd +         ) + +      Nothing -> +         (model, Cmd.none) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : Struct.Model.Type -> (Struct.Model.Type, (Cmd Struct.Event.Type)) +apply_to model = +   case +      ( +         (Struct.CharacterTurn.get_state model.char_turn), +         (Struct.CharacterTurn.try_getting_active_character +            model.char_turn +         ), +         (Struct.CharacterTurn.try_getting_navigator model.char_turn) +      ) +   of +      ( +         Struct.CharacterTurn.MovedCharacter, +         (Just char), +         (Just nav) +      ) -> +         (make_it_so model char nav) + +      ( +         Struct.CharacterTurn.ChoseTarget, +         (Just char), +         (Just nav) +      ) -> +         (make_it_so model char nav) + +      (Struct.CharacterTurn.SelectedCharacter, (Just char), (Just nav)) -> +         (make_it_so model char nav) + +      (_, _, _) -> +         ( +            (Struct.Model.invalidate +               (Struct.Error.new +                  Struct.Error.Programming +                  "Character turn appears to be in an illegal state." +               ) +               model +            ), +            Cmd.none +         ) diff --git a/src/battle/src/Update/HandleAnimationEnded.elm b/src/battle/src/Update/HandleAnimationEnded.elm new file mode 100644 index 0000000..90782e7 --- /dev/null +++ b/src/battle/src/Update/HandleAnimationEnded.elm @@ -0,0 +1,127 @@ +module Update.HandleAnimationEnded exposing (apply_to) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Delay + +import Time + +import Task + +-- Map ------------------------------------------------------------------- +import Action.Scroll + +import Struct.Character +import Struct.Event +import Struct.Model +import Struct.TurnResult +import Struct.TurnResultAnimator + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +handle_char_focus : ( +      Struct.Model.Type -> +      Struct.TurnResultAnimator.Type -> +      Int -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +handle_char_focus model animator char_index = +   case (Array.get char_index model.characters) of +      (Just char) -> +         if (Struct.TurnResultAnimator.waits_for_focus animator) +         then +            ( +               model, +               (Cmd.batch +                  [ +                     (Task.attempt +                        (Struct.Event.attempted) +                        (Action.Scroll.to +                           (Struct.Character.get_location char) +                           model.ui +                        ) +                     ), +                     (Delay.after 2.0 Time.second Struct.Event.AnimationEnded) +                  ] +               ) +            ) +         else +            ( +               model, +               (Cmd.batch +                  [ +                     (Task.attempt +                        (Struct.Event.attempted) +                        (Action.Scroll.to +                           (Struct.Character.get_location char) +                           model.ui +                        ) +                     ), +                     (Delay.after 0.3 Time.second Struct.Event.AnimationEnded) +                  ] +               ) +            ) + + +      _ -> +         ( +            model, +            (Delay.after 1.0 Time.millisecond Struct.Event.AnimationEnded) +         ) + +prepare_next_animation : ( +      Struct.Model.Type -> +      Struct.TurnResultAnimator.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +prepare_next_animation model animator = +   case (Struct.TurnResultAnimator.get_current_animation animator) of +      (Struct.TurnResultAnimator.Focus char_index) -> +         (handle_char_focus model animator char_index) + +      (Struct.TurnResultAnimator.AttackSetup _) -> +         ( +            model, +            (Delay.after 1.0 Time.second Struct.Event.AnimationEnded) +         ) + +      (Struct.TurnResultAnimator.TurnResult turn_result) -> +         case turn_result of +            (Struct.TurnResult.Attacked _) -> +               ( +                  model, +                  (Delay.after 3.0 Time.second Struct.Event.AnimationEnded) +               ) + +            _ -> +               ( +                  model, +                  (Delay.after 0.1 Time.second Struct.Event.AnimationEnded) +               ) + +      _ -> +         ( +            model, +            (Delay.after 0.3 Time.second Struct.Event.AnimationEnded) +         ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model = +   let +      new_model = +         (Struct.Model.apply_animator_step +            (Struct.Model.move_animator_to_next_step model) +         ) +   in +      case new_model.animator of +         Nothing -> (new_model, Cmd.none) +         (Just animator) -> +            (prepare_next_animation new_model animator) diff --git a/src/battle/src/Update/HandleServerReply.elm b/src/battle/src/Update/HandleServerReply.elm new file mode 100644 index 0000000..2352f67 --- /dev/null +++ b/src/battle/src/Update/HandleServerReply.elm @@ -0,0 +1,230 @@ +module Update.HandleServerReply exposing (apply_to) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Delay + +import Dict + +import Http + +import Time + +-- Map ------------------------------------------------------------------- +import Struct.Armor +import Struct.Map +import Struct.Character +import Struct.Error +import Struct.Event +import Struct.Model +import Struct.ServerReply +import Struct.Tile +import Struct.TurnResult +import Struct.TurnResultAnimator +import Struct.UI +import Struct.Weapon + +-------------------------------------------------------------------------------- +-- TYPES ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +weapon_getter : Struct.Model.Type -> Struct.Weapon.Ref -> Struct.Weapon.Type +weapon_getter model ref = +   case (Dict.get ref model.weapons) of +      (Just w) -> w +      Nothing -> Struct.Weapon.none + +armor_getter : Struct.Model.Type -> Struct.Armor.Ref -> Struct.Armor.Type +armor_getter model ref = +   case (Dict.get ref model.armors) of +      (Just w) -> w +      Nothing -> Struct.Armor.none + +----------- + +add_armor : ( +      Struct.Armor.Type -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) +   ) +add_armor ar current_state = +   case current_state of +      (_, (Just _)) -> current_state +      (model, _) -> ((Struct.Model.add_armor ar model), Nothing) + +add_tile : ( +      Struct.Tile.Type -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) +   ) +add_tile tl current_state = +   case current_state of +      (_, (Just _)) -> current_state +      (model, _) -> ((Struct.Model.add_tile tl model), Nothing) + +add_weapon : ( +      Struct.Weapon.Type -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) +   ) +add_weapon wp current_state = +   case current_state of +      (_, (Just _)) -> current_state +      (model, _) -> ((Struct.Model.add_weapon wp model), Nothing) + +add_character : ( +      (Struct.Character.Type, Int, Int, Int) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) +   ) +add_character char_and_refs current_state = +   case current_state of +      (_, (Just _)) -> current_state +      (model, _) -> +         let +            (char, awp_ref, swp_ref, ar_ref) = char_and_refs +            awp = (weapon_getter model awp_ref) +            swp = (weapon_getter model swp_ref) +            ar = (armor_getter model ar_ref) +         in +            ( +               (Struct.Model.add_character +                  (Struct.Character.fill_missing_equipment awp swp ar char) +                  model +               ), +               Nothing +            ) + +set_map : ( +      Struct.Map.Type -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) +   ) +set_map map current_state = +   case current_state of +      (_, (Just _)) -> current_state +      (model, _) -> +         ( +            {model | +               map = +                  (Struct.Map.solve_tiles (Dict.values model.tiles) map) +            }, +            Nothing +         ) + +add_to_timeline : ( +      (List Struct.TurnResult.Type) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) +   ) +add_to_timeline turn_results current_state = +   case current_state of +      (_, (Just _)) -> current_state + +      (model, _) -> +         ( +            {model | +               animator = +                  (Struct.TurnResultAnimator.maybe_new +                     (List.reverse turn_results) +                     False +                  ), +               timeline = +                  (Array.append +                     (Array.fromList turn_results) +                     model.timeline +                  ), +               ui = +                  (Struct.UI.set_displayed_tab +                     Struct.UI.TimelineTab +                     model.ui +                  ) +            }, +            Nothing +         ) + +set_timeline : ( +      (List Struct.TurnResult.Type) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) +   ) +set_timeline turn_results current_state = +   case current_state of +      (_, (Just _)) -> current_state + +      (model, _) -> +         ( +            {model | timeline = (Array.fromList turn_results)}, +            Nothing +         ) + +apply_command : ( +      Struct.ServerReply.Type -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) -> +      (Struct.Model.Type, (Maybe Struct.Error.Type)) +   ) +apply_command command current_state = +   case command of +      (Struct.ServerReply.AddWeapon wp) -> +         (add_weapon wp current_state) + +      (Struct.ServerReply.AddArmor ar) -> +         (add_armor ar current_state) + +      (Struct.ServerReply.AddTile tl) -> +         (add_tile tl current_state) + +      (Struct.ServerReply.AddCharacter char) -> +         (add_character char current_state) + +      (Struct.ServerReply.SetMap map) -> +         (set_map map current_state) + +      (Struct.ServerReply.TurnResults results) -> +         (add_to_timeline results current_state) + +      (Struct.ServerReply.SetTimeline timeline) -> +         (set_timeline timeline current_state) + +      Struct.ServerReply.Okay -> current_state + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      (Result Http.Error (List Struct.ServerReply.Type)) -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model query_result = +   case query_result of +      (Result.Err error) -> +         ( +            (Struct.Model.invalidate +               (Struct.Error.new Struct.Error.Networking (toString error)) +               model +            ), +            Cmd.none +         ) + +      (Result.Ok commands) -> +         let +            new_model = +               ( +                  case (List.foldl (apply_command) (model, Nothing) commands) of +                     (updated_model, Nothing) -> updated_model +                     (_, (Just error)) -> (Struct.Model.invalidate error model) +               ) +         in +            ( +               new_model, +               if (new_model.animator == Nothing) +               then +                  Cmd.none +               else +                  (Delay.after 1 Time.millisecond Struct.Event.AnimationEnded) +            ) diff --git a/src/battle/src/Update/LookForCharacter.elm b/src/battle/src/Update/LookForCharacter.elm new file mode 100644 index 0000000..7057451 --- /dev/null +++ b/src/battle/src/Update/LookForCharacter.elm @@ -0,0 +1,54 @@ +module Update.LookForCharacter exposing (apply_to) +-- Elm ------------------------------------------------------------------------- +import Array +import Task + +-- Map ------------------------------------------------------------------- +import Action.Scroll + +import Struct.Character +import Struct.Event +import Struct.Model +import Struct.UI + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +scroll_to_char : ( +      Struct.Model.Type -> +      Int -> +      (Cmd Struct.Event.Type) +   ) +scroll_to_char model char_ix = +   case (Array.get char_ix model.characters) of +      (Just char) -> +         (Task.attempt +            (Struct.Event.attempted) +            (Action.Scroll.to +               (Struct.Character.get_location char) +               model.ui +            ) +         ) + +      Nothing -> +         Cmd.none + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      Int -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model target_ix = +   ( +      {model | +         ui = +            (Struct.UI.set_previous_action +               (Just (Struct.UI.SelectedCharacter target_ix)) +               model.ui +            ) +      }, +      (scroll_to_char model target_ix) +   ) diff --git a/src/battle/src/Update/RequestDirection.elm b/src/battle/src/Update/RequestDirection.elm new file mode 100644 index 0000000..b6c8f13 --- /dev/null +++ b/src/battle/src/Update/RequestDirection.elm @@ -0,0 +1,76 @@ +module Update.RequestDirection exposing (apply_to) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.CharacterTurn +import Struct.Direction +import Struct.Error +import Struct.Event +import Struct.Model +import Struct.Navigator +import Struct.UI + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +make_it_so : ( +      Struct.Model.Type -> +      Struct.Navigator.Type -> +      Struct.Direction.Type -> +      Struct.Model.Type +   ) +make_it_so model navigator dir = +   case (Struct.Navigator.try_adding_step dir navigator) of +      (Just new_navigator) -> +         {model | +            char_turn = +               (Struct.CharacterTurn.set_navigator +                  new_navigator +                  model.char_turn +               ), +            ui = +               (Struct.UI.set_previous_action +                  (Just Struct.UI.UsedManualControls) +                  model.ui +               ) +         } + +      Nothing -> +         (Struct.Model.invalidate +            (Struct.Error.new +               Struct.Error.IllegalAction +               "Unreachable/occupied tile." +            ) +            model +         ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      Struct.Direction.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model dir = +   case +      (Struct.CharacterTurn.try_getting_navigator model.char_turn) +   of +      (Just navigator) -> +         ( +            (make_it_so model navigator dir), +            Cmd.none +         ) + +      _ -> +         ( +            (Struct.Model.invalidate +               (Struct.Error.new +                  Struct.Error.IllegalAction +                  "This can only be done while moving a character." +               ) +               model +            ), +            Cmd.none +         ) diff --git a/src/battle/src/Update/SelectCharacter.elm b/src/battle/src/Update/SelectCharacter.elm new file mode 100644 index 0000000..1137435 --- /dev/null +++ b/src/battle/src/Update/SelectCharacter.elm @@ -0,0 +1,298 @@ +module Update.SelectCharacter exposing (apply_to) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Task + +-- Map ------------------------------------------------------------------- +import Action.Scroll + +import Struct.Map +import Struct.Character +import Struct.CharacterTurn +import Struct.Error +import Struct.Event +import Struct.Location +import Struct.Model +import Struct.Navigator +import Struct.Statistics +import Struct.UI +import Struct.Weapon +import Struct.WeaponSet + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_character_navigator : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      Struct.Navigator.Type +   ) +get_character_navigator model char = +   let +      weapon = +         (Struct.WeaponSet.get_active_weapon +            (Struct.Character.get_weapons char) +         ) +   in +      (Struct.Navigator.new +         (Struct.Character.get_location char) +         (Struct.Statistics.get_movement_points +            (Struct.Character.get_statistics char) +         ) +         (Struct.Weapon.get_attack_range weapon) +         (Struct.Weapon.get_defense_range weapon) +         (Struct.Map.get_movement_cost_function +            model.map +            (Struct.Character.get_location char) +            (Array.toList model.characters) +         ) +      ) + +attack_character : ( +      Struct.Model.Type -> +      Int -> +      Struct.Character.Type -> +      Struct.Model.Type +   ) +attack_character model target_char_id target_char = +   {model | +      char_turn = +         (Struct.CharacterTurn.set_target +            (Just target_char_id) +            model.char_turn +         ), +      ui = +         (Struct.UI.reset_displayed_nav +            (Struct.UI.reset_displayed_tab +               (Struct.UI.set_previous_action Nothing model.ui) +            ) +         ) +   } + +ctrl_or_focus_character : ( +      Struct.Model.Type -> +      Int -> +      Struct.Character.Type -> +      Struct.Model.Type +   ) +ctrl_or_focus_character model target_char_id target_char = +   if (Struct.Character.is_enabled target_char) +   then +      let +         nav = +            (case (Struct.UI.try_getting_displayed_nav model.ui) of +               (Just dnav) -> dnav +               Nothing -> +                  (get_character_navigator model target_char) +            ) +      in +         {model | +            char_turn = +               (Struct.CharacterTurn.set_navigator +                  nav +                  (Struct.CharacterTurn.set_active_character +                     target_char +                     model.char_turn +                  ) +               ), +            ui = +               (Struct.UI.reset_displayed_nav +                  (Struct.UI.reset_displayed_tab +                     (Struct.UI.set_previous_action Nothing model.ui) +                  ) +               ) +         } +   else +      {model | +         ui = +            (Struct.UI.set_previous_action +               (Just (Struct.UI.SelectedCharacter target_char_id)) +               (Struct.UI.set_displayed_nav +                  (get_character_navigator model target_char) +                  model.ui +               ) +            ) +      } + +can_target_character : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      Bool +   ) +can_target_character model target = +   ( +      (Struct.CharacterTurn.can_select_target model.char_turn) +      && (Struct.Character.is_alive target) +      && +      ( +         case +            (Struct.CharacterTurn.try_getting_navigator +               model.char_turn +            ) +         of +            (Just nav) -> +               case +                  (Struct.Navigator.try_getting_path_to +                     (Struct.Location.get_ref +                        (Struct.Character.get_location target) +                     ) +                     nav +                  ) +               of +                  (Just _) -> True +                  _ -> False + +            _ -> +               False +      ) +   ) + +second_click_on : ( +      Struct.Model.Type -> +      Int -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +second_click_on model target_char_id = +   case (Array.get target_char_id model.characters) of +      (Just target_char) -> +         case +            ( +               (Struct.CharacterTurn.try_getting_active_character +                  model.char_turn +               ), +               (Struct.CharacterTurn.try_getting_target model.char_turn) +            ) +         of +            ((Just _), (Just char_turn_target_id)) -> +               if (char_turn_target_id == target_char_id) +               then +                  ( +                     model, +                     Cmd.none +                  ) +               else +                  ( +                     (ctrl_or_focus_character model target_char_id target_char), +                     (Task.attempt +                        (Struct.Event.attempted) +                        (Action.Scroll.to +                           (Struct.Character.get_location target_char) +                           model.ui +                        ) +                     ) +                  ) + +            ((Just _), Nothing) -> +               if (can_target_character model target_char) +               then +                  ( +                     (attack_character +                        model +                        target_char_id +                        target_char +                     ), +                     Cmd.none +                  ) +               else +                  ( +                     (ctrl_or_focus_character model target_char_id target_char), +                     (Task.attempt +                        (Struct.Event.attempted) +                        (Action.Scroll.to +                           (Struct.Character.get_location target_char) +                           model.ui +                        ) +                     ) +                  ) + +            (_, _) -> +               ( +                  (ctrl_or_focus_character model target_char_id target_char), +                  (Task.attempt +                     (Struct.Event.attempted) +                     (Action.Scroll.to +                        (Struct.Character.get_location target_char) +                        model.ui +                     ) +                  ) +               ) + +      Nothing -> +         ( +            (Struct.Model.invalidate +               (Struct.Error.new +                  Struct.Error.Programming +                  "SelectCharacter: Unknown char selected." +               ) +               model +            ), +            Cmd.none +         ) + +first_click_on : ( +      Struct.Model.Type -> +      Int -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +first_click_on model target_char_id = +   if +   ( +      (Struct.CharacterTurn.try_getting_target model.char_turn) +      == +      (Just target_char_id) +   ) +   then +      (model, Cmd.none) +   else +      case (Array.get target_char_id model.characters) of +         (Just target_char) -> +            ( +               {model | +                  ui = +                     (Struct.UI.set_previous_action +                        (Just (Struct.UI.SelectedCharacter target_char_id)) +                        (Struct.UI.set_displayed_tab +                           Struct.UI.StatusTab +                           (Struct.UI.set_displayed_nav +                              (get_character_navigator model target_char) +                              model.ui +                           ) +                        ) +                     ) +               }, +               Cmd.none +            ) + +         Nothing -> +            ( +               (Struct.Model.invalidate +                  (Struct.Error.new +                     Struct.Error.Programming +                     "SelectCharacter: Unknown char selected." +                  ) +                  model +               ), +               Cmd.none +            ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      Int -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model target_char_id = +   if +   ( +      (Struct.UI.get_previous_action model.ui) +      == +      (Just (Struct.UI.SelectedCharacter target_char_id)) +   ) +   then +      (second_click_on model target_char_id) +   else +      (first_click_on model target_char_id) diff --git a/src/battle/src/Update/SelectCharacterOrTile.elm b/src/battle/src/Update/SelectCharacterOrTile.elm new file mode 100644 index 0000000..4028d8e --- /dev/null +++ b/src/battle/src/Update/SelectCharacterOrTile.elm @@ -0,0 +1,52 @@ +module Update.SelectCharacterOrTile exposing (apply_to) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.Character +import Struct.Event +import Struct.Location +import Struct.Model + +import Update.SelectCharacter +import Update.SelectTile + +import Util.Array + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      Struct.Location.Ref -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model loc_ref = +   case +      (Util.Array.filter_first +         (\c -> +            ( +               ( +                  (Struct.Character.get_location c) +                  == (Struct.Location.from_ref loc_ref) +               ) +               && +               (Struct.Character.is_alive c) +            ) +         ) +         model.characters +      ) +   of +      (Just char) -> +         (Update.SelectCharacter.apply_to +            model +            (Struct.Character.get_index char) +         ) + +      Nothing -> +         (Update.SelectTile.apply_to model loc_ref) + diff --git a/src/battle/src/Update/SelectTab.elm b/src/battle/src/Update/SelectTab.elm new file mode 100644 index 0000000..d15a463 --- /dev/null +++ b/src/battle/src/Update/SelectTab.elm @@ -0,0 +1,32 @@ +module Update.SelectTab exposing (apply_to) +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.Model +import Struct.Event +import Struct.UI + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      Struct.UI.Tab -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model tab = +   if ((Struct.UI.try_getting_displayed_tab model.ui) == (Just tab)) +   then +      ( +         {model | ui = (Struct.UI.reset_displayed_tab model.ui)}, +         Cmd.none +      ) +   else +      ( +         {model | ui = (Struct.UI.set_displayed_tab tab model.ui)}, +         Cmd.none +      ) diff --git a/src/battle/src/Update/SelectTile.elm b/src/battle/src/Update/SelectTile.elm new file mode 100644 index 0000000..aa9215a --- /dev/null +++ b/src/battle/src/Update/SelectTile.elm @@ -0,0 +1,158 @@ +module Update.SelectTile exposing (apply_to) + +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.CharacterTurn +import Struct.Direction +import Struct.Error +import Struct.Event +import Struct.Location +import Struct.Model +import Struct.Navigator +import Struct.UI + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +try_autopiloting : ( +      Struct.Direction.Type -> +      (Maybe Struct.Navigator.Type) -> +      (Maybe Struct.Navigator.Type) +   ) +try_autopiloting dir maybe_nav = +   case maybe_nav of +      (Just navigator) -> +         (Struct.Navigator.try_adding_step dir navigator) + +      Nothing -> Nothing + +go_to_tile : ( +      Struct.Model.Type -> +      Struct.Navigator.Type -> +      Struct.Location.Ref -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +go_to_tile model navigator loc_ref = +   if +   ( +      loc_ref +      == +      (Struct.Location.get_ref +         (Struct.Navigator.get_current_location navigator) +      ) +   ) +   then +      -- We are already there. +      if +      ( +         (Struct.UI.get_previous_action model.ui) +         == +         (Just (Struct.UI.SelectedLocation loc_ref)) +      ) +      then +         -- And we just clicked on that tile. +         ( +            {model | +               char_turn = +                  (Struct.CharacterTurn.lock_path model.char_turn) +            }, +            Cmd.none +         ) +      else +         -- And we didn't just click on that tile. +         ( +            {model | +               ui = +                  (Struct.UI.reset_displayed_nav +                     (Struct.UI.set_displayed_tab +                        Struct.UI.StatusTab +                        (Struct.UI.set_previous_action +                           (Just (Struct.UI.SelectedLocation loc_ref)) +                           model.ui +                        ) +                     ) +                  ) +            }, +            Cmd.none +         ) +   else +      -- We have to try getting there. +      case +         (Struct.Navigator.try_getting_path_to +            loc_ref +            navigator +         ) +      of +         (Just path) -> +            case +               (List.foldr +                  (try_autopiloting) +                  (Just (Struct.Navigator.clear_path navigator)) +                  path +               ) +            of +               (Just new_navigator) -> +                  ( +                     {model | +                        char_turn = +                           (Struct.CharacterTurn.set_navigator +                              new_navigator +                              model.char_turn +                           ), +                        ui = +                           (Struct.UI.set_displayed_tab +                              Struct.UI.StatusTab +                              (Struct.UI.set_previous_action +                                 (Just (Struct.UI.SelectedLocation loc_ref)) +                                 model.ui +                              ) +                           ) +                     }, +                     Cmd.none +                  ) + +               Nothing -> +                  ( +                     (Struct.Model.invalidate +                        (Struct.Error.new +                           Struct.Error.Programming +                           "SelectTile/Navigator: Could not follow own path." +                        ) +                        model +                     ), +                     Cmd.none +                  ) + +         Nothing -> -- Clicked outside of the range indicator +            ((Struct.Model.reset model), Cmd.none) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      Struct.Location.Ref -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model loc_ref = +   case (Struct.CharacterTurn.try_getting_navigator model.char_turn) of +      (Just navigator) -> +         (go_to_tile model navigator loc_ref) + +      _ -> +         ( +            {model | +               ui = +                  (Struct.UI.reset_displayed_nav +                     (Struct.UI.set_displayed_tab +                        Struct.UI.StatusTab +                        (Struct.UI.set_previous_action +                           (Just (Struct.UI.SelectedLocation loc_ref)) +                           model.ui +                        ) +                     ) +                  ) +            }, +            Cmd.none +         ) diff --git a/src/battle/src/Update/SendLoadBattleRequest.elm b/src/battle/src/Update/SendLoadBattleRequest.elm new file mode 100644 index 0000000..9d8905c --- /dev/null +++ b/src/battle/src/Update/SendLoadBattleRequest.elm @@ -0,0 +1,29 @@ +module Update.SendLoadBattleRequest exposing (apply_to) +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Comm.LoadBattle + +import Struct.Event +import Struct.Model + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model = +   ( +      (Struct.Model.full_debug_reset model), +      (case (Comm.LoadBattle.try model) of +         (Just cmd) -> cmd +         Nothing -> Cmd.none +      ) +   ) + diff --git a/src/battle/src/Update/SetRequestedHelp.elm b/src/battle/src/Update/SetRequestedHelp.elm new file mode 100644 index 0000000..dfc58db --- /dev/null +++ b/src/battle/src/Update/SetRequestedHelp.elm @@ -0,0 +1,22 @@ +module Update.SetRequestedHelp exposing (apply_to) +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.HelpRequest +import Struct.Model + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      Struct.HelpRequest.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model help_request = +   ({model | help_request = help_request}, Cmd.none) diff --git a/src/battle/src/Update/SwitchTeam.elm b/src/battle/src/Update/SwitchTeam.elm new file mode 100644 index 0000000..4e415be --- /dev/null +++ b/src/battle/src/Update/SwitchTeam.elm @@ -0,0 +1,30 @@ +module Update.SwitchTeam exposing (apply_to) +-- Elm ------------------------------------------------------------------------- + +-- Map ------------------------------------------------------------------- +import Struct.Model +import Struct.Event + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model = +   if (model.player_ix == 0) +   then +      ( +         (Struct.Model.reset {model | player_id = "1", player_ix = 1}), +         Cmd.none +      ) +   else +      ( +         (Struct.Model.reset {model | player_id = "0", player_ix = 0}), +         Cmd.none +      ) diff --git a/src/battle/src/Update/SwitchWeapon.elm b/src/battle/src/Update/SwitchWeapon.elm new file mode 100644 index 0000000..d9375dd --- /dev/null +++ b/src/battle/src/Update/SwitchWeapon.elm @@ -0,0 +1,100 @@ +module Update.SwitchWeapon exposing (apply_to) +-- Elm ------------------------------------------------------------------------- +import Array + +-- Map ------------------------------------------------------------------- +import Struct.Map +import Struct.Character +import Struct.CharacterTurn +import Struct.Error +import Struct.Event +import Struct.Model +import Struct.Navigator +import Struct.Statistics +import Struct.Weapon +import Struct.WeaponSet + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +make_it_so : Struct.Model.Type -> Struct.Model.Type +make_it_so model = +   case (Struct.CharacterTurn.try_getting_active_character model.char_turn) of +      (Just char) -> +         let +            new_weapons = +               (Struct.WeaponSet.switch_weapons +                  (Struct.Character.get_weapons char) +               ) +            new_char = (Struct.Character.set_weapons new_weapons char) +         in +         {model | +            char_turn = +               (Struct.CharacterTurn.set_has_switched_weapons +                  True +                  (Struct.CharacterTurn.lock_path +                     (Struct.CharacterTurn.set_navigator +                        (Struct.Navigator.new +                           (Struct.Character.get_location new_char) +                           (Struct.Statistics.get_movement_points +                              (Struct.Character.get_statistics new_char) +                           ) +                           (Struct.Weapon.get_attack_range +                              (Struct.WeaponSet.get_active_weapon new_weapons) +                           ) +                           (Struct.Weapon.get_defense_range +                              (Struct.WeaponSet.get_active_weapon new_weapons) +                           ) +                           (Struct.Map.get_movement_cost_function +                              model.map +                              (Struct.Character.get_location new_char) +                              (Array.toList model.characters) +                           ) +                        ) +                        (Struct.CharacterTurn.set_active_character +                           new_char +                           model.char_turn +                        ) +                     ) +                  ) +               ) +         } + +      _ -> +         (Struct.Model.invalidate +            (Struct.Error.new +               Struct.Error.Programming +               ( +                  "CharacterTurn structure in the 'SelectedCharacter' state" +                  ++ " without character being selected." +               ) +            ) +            model +         ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model = +   case (Struct.CharacterTurn.get_state model.char_turn) of +      Struct.CharacterTurn.SelectedCharacter -> +         ((make_it_so model), Cmd.none) + +      _ -> +         ( +            (Struct.Model.invalidate +               (Struct.Error.new +                  Struct.Error.Programming +                  ( +                     "Attempt to switch weapons as a secondary action or" +                     ++ " without character being selected." +                  ) +               ) +               model +            ), +            Cmd.none +         ) diff --git a/src/battle/src/Update/TestAnimation.elm b/src/battle/src/Update/TestAnimation.elm new file mode 100644 index 0000000..e23d577 --- /dev/null +++ b/src/battle/src/Update/TestAnimation.elm @@ -0,0 +1,27 @@ +module Update.TestAnimation exposing (apply_to) + +-- Elm ------------------------------------------------------------------------- +import Delay + +import Time + +-- Map ------------------------------------------------------------------- +import Struct.Model +import Struct.Event + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +apply_to : ( +      Struct.Model.Type -> +      (Struct.Model.Type, (Cmd Struct.Event.Type)) +   ) +apply_to model = +   ( +      (Struct.Model.initialize_animator model), +      (Delay.after 1 Time.millisecond Struct.Event.AnimationEnded) +   ) diff --git a/src/battle/src/Util/Array.elm b/src/battle/src/Util/Array.elm new file mode 100644 index 0000000..9e57c18 --- /dev/null +++ b/src/battle/src/Util/Array.elm @@ -0,0 +1,34 @@ +module Util.Array exposing +   ( +      update, +      update_unsafe, +      filter_first +   ) + +import Array + +update : ( +      Int -> +      ((Maybe t) -> (Maybe t)) -> +      (Array.Array t) -> +      (Array.Array t) +   ) +update index fun array = +   case (fun (Array.get index array)) of +      Nothing -> array +      (Just e) -> (Array.set index e array) + +update_unsafe : ( +      Int -> +      (t -> t) -> +      (Array.Array t) -> +      (Array.Array t) +   ) +update_unsafe index fun array = +   case (Array.get index array) of +      Nothing -> array +      (Just e) -> (Array.set index (fun e) array) + +filter_first : (t -> Bool) -> (Array.Array t) -> (Maybe t) +filter_first fun array = +   (Array.get 0 (Array.filter fun array)) diff --git a/src/battle/src/Util/Html.elm b/src/battle/src/Util/Html.elm new file mode 100644 index 0000000..42eadba --- /dev/null +++ b/src/battle/src/Util/Html.elm @@ -0,0 +1,6 @@ +module Util.Html exposing (nothing) + +import Html + +nothing : (Html.Html a) +nothing = (Html.text "") diff --git a/src/battle/src/Util/List.elm b/src/battle/src/Util/List.elm new file mode 100644 index 0000000..2bc5217 --- /dev/null +++ b/src/battle/src/Util/List.elm @@ -0,0 +1,16 @@ +module Util.List exposing (..) + +import List + +pop : List a -> (Maybe (a, List a)) +pop l = +   case +      ((List.head l), (List.tail l)) +   of +      (Nothing, _) -> Nothing +      (_ , Nothing) -> Nothing +      ((Just head), (Just tail)) -> (Just (head, tail)) + +get_first : (a -> Bool) -> (List a) -> (Maybe a) +get_first fun list = +   (List.head (List.filter fun list)) diff --git a/src/battle/src/View/Character.elm b/src/battle/src/View/Character.elm new file mode 100644 index 0000000..d33feb1 --- /dev/null +++ b/src/battle/src/View/Character.elm @@ -0,0 +1,230 @@ +module View.Character exposing +   ( +      get_portrait_html, +      get_icon_html +   ) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +import Html.Events + +-- Map  ------------------------------------------------------------------ +import Constants.UI + +import Util.Html + +import Struct.Armor +import Struct.Character +import Struct.CharacterTurn +import Struct.Event +import Struct.Model +import Struct.UI + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_activation_level_class : ( +      Struct.Character.Type -> +      (Html.Attribute Struct.Event.Type) +   ) +get_activation_level_class char = +   if (Struct.Character.is_enabled char) +   then +      (Html.Attributes.class "battle-character-icon-enabled") +   else +      (Html.Attributes.class "battle-character-icon-disabled") + +get_alliance_class : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      (Html.Attribute Struct.Event.Type) +   ) +get_alliance_class model char = +   if ((Struct.Character.get_player_ix char) == model.player_ix) +   then +      (Html.Attributes.class "battle-character-ally") +   else +      (Html.Attributes.class "battle-character-enemy") + +get_position_style : ( +      Struct.Character.Type -> +      (Html.Attribute Struct.Event.Type) +   ) +get_position_style char = +   let char_loc = (Struct.Character.get_location char) in +      (Html.Attributes.style +         [ +            ("top", ((toString (char_loc.y * Constants.UI.tile_size)) ++ "px")), +            ("left", ((toString (char_loc.x * Constants.UI.tile_size)) ++ "px")) +         ] +      ) + +get_focus_class : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      (Html.Attribute Struct.Event.Type) +   ) +get_focus_class model char = +   if +   ( +      (Struct.UI.get_previous_action model.ui) +      == +      (Just (Struct.UI.SelectedCharacter (Struct.Character.get_index char))) +   ) +   then +      (Html.Attributes.class "battle-character-selected") +   else +      if +      ( +         (Struct.CharacterTurn.try_getting_target model.char_turn) +         == +         (Just (Struct.Character.get_index char)) +      ) +      then +         (Html.Attributes.class "battle-character-targeted") +      else +         (Html.Attributes.class "") + +get_icon_body_html : Struct.Character.Type -> (Html.Html Struct.Event.Type) +get_icon_body_html char = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-icon-body"), +         (Html.Attributes.class +            ( +               "asset-character-team-body-" +               ++ (toString (Struct.Character.get_player_ix char)) +            ) +         ) +      ] +      [ +      ] +   ) + +get_icon_head_html : Struct.Character.Type -> (Html.Html Struct.Event.Type) +get_icon_head_html char = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-icon-head"), +         (Html.Attributes.class +            ("asset-character-icon-" ++ (Struct.Character.get_icon_id char)) +         ) +      ] +      [ +      ] +   ) + +get_icon_actual_html : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_icon_actual_html model char = +      (Html.div +         [ +            (Html.Attributes.class "battle-tiled"), +            (Html.Attributes.class "battle-character-icon"), +            (get_activation_level_class char), +            (get_alliance_class model char), +            (get_position_style char), +            (get_focus_class model char), +            (Html.Attributes.class "clickable"), +            (Html.Events.onClick +               (Struct.Event.CharacterSelected (Struct.Character.get_index char)) +            ) +         ] +         [ +            (get_icon_body_html char), +            (get_icon_head_html char) +         ] +      ) + +get_portrait_body_html : Struct.Character.Type -> (Html.Html Struct.Event.Type) +get_portrait_body_html char = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-portrait-body"), +         (Html.Attributes.class +            ( +               "asset-character-portrait-" +               ++ (Struct.Character.get_portrait_id char) +            ) +         ) +      ] +      [ +      ] +   ) + +get_portrait_armor_html : Struct.Character.Type -> (Html.Html Struct.Event.Type) +get_portrait_armor_html char = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-portrait-armor"), +         (Html.Attributes.class +            ( +               "asset-armor-" +               ++ +               (Struct.Armor.get_image_id (Struct.Character.get_armor char)) +            ) +         ), +         (Html.Attributes.class +            ( +               "asset-armor-variation-" +               ++ (Struct.Character.get_armor_variation char) +            ) +         ) +      ] +      [ +      ] +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_portrait_html : ( +      Int -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_portrait_html viewer_ix char = +   (Html.div +      [ +         (Html.Attributes.class +            ( +               if ((Struct.Character.get_player_ix char) == viewer_ix) +               then +                  "battle-character-ally" +               else +                  "battle-character-enemy" +            ) +         ), +         (Html.Attributes.class "battle-character-portrait"), +         (Html.Attributes.class +            ( +               "battle-character-portrait-team-" +               ++ +               (toString (Struct.Character.get_player_ix char)) +            ) +         ), +         (Html.Events.onClick +            (Struct.Event.LookingForCharacter (Struct.Character.get_index char)) +         ) +      ] +      [ +         (get_portrait_body_html char), +         (get_portrait_armor_html char) +      ] +   ) + +get_icon_html : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_icon_html model char = +   if (Struct.Character.is_alive char) +   then +      (get_icon_actual_html model char) +   else +      (Util.Html.nothing) diff --git a/src/battle/src/View/Controlled.elm b/src/battle/src/View/Controlled.elm new file mode 100644 index 0000000..e0e20bf --- /dev/null +++ b/src/battle/src/View/Controlled.elm @@ -0,0 +1,133 @@ +module View.Controlled exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +import Html.Events + +-- Struct.Map ------------------------------------------------------------------- +import Struct.CharacterTurn +import Struct.Event +import Struct.Navigator + +import Util.Html + +import View.Controlled.CharacterCard +import View.Controlled.ManualControls + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +has_a_path : Struct.CharacterTurn.Type -> Bool +has_a_path char_turn = +   case (Struct.CharacterTurn.try_getting_navigator char_turn) of +      (Just nav) -> ((Struct.Navigator.get_path nav) /= []) +      Nothing -> False + + +attack_button : Struct.CharacterTurn.Type -> (Html.Html Struct.Event.Type) +attack_button char_turn = +   (Html.button +      [ (Html.Events.onClick Struct.Event.AttackWithoutMovingRequest) ] +      [ +         (Html.text +            ( +               if (has_a_path char_turn) +               then ("Go & Select Target") +               else ("Select Target") +            ) +         ) +      ] +   ) + +abort_button : (Html.Html Struct.Event.Type) +abort_button = +   (Html.button +      [ (Html.Events.onClick Struct.Event.AbortTurnRequest) ] +      [ (Html.text "Abort") ] +   ) + +end_turn_button : String -> (Html.Html Struct.Event.Type) +end_turn_button suffix = +   (Html.button +      [ +         (Html.Events.onClick Struct.Event.TurnEnded), +         (Html.Attributes.class "battle-end-turn-button") +      ] +      [ (Html.text ("End Turn" ++ suffix)) ] +   ) + +inventory_button : (Html.Html Struct.Event.Type) +inventory_button = +   (Html.button +      [ (Html.Events.onClick Struct.Event.WeaponSwitchRequest) ] +      [ (Html.text "Switch Weapon") ] +   ) + +get_available_actions : ( +      Struct.CharacterTurn.Type -> +      (List (Html.Html Struct.Event.Type)) +   ) +get_available_actions char_turn = +   case (Struct.CharacterTurn.get_state char_turn) of +      Struct.CharacterTurn.SelectedCharacter -> +         [ +            (attack_button char_turn), +            (inventory_button), +            (end_turn_button " Doing Nothing"), +            (abort_button) +         ] + +      Struct.CharacterTurn.MovedCharacter -> +         [ +            (end_turn_button " Without Attacking"), +            (abort_button) +         ] + +      Struct.CharacterTurn.ChoseTarget -> +         [ +            (end_turn_button " By Attacking"), +            (abort_button) +         ] + +      _ -> +         [ +         ] + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : Struct.CharacterTurn.Type -> Int -> (Html.Html Struct.Event.Type) +get_html char_turn player_ix = +   case +      (Struct.CharacterTurn.try_getting_active_character char_turn) +   of +      (Just char) -> +         (Html.div +            [(Html.Attributes.class "battle-controlled")] +            [ +               (View.Controlled.CharacterCard.get_summary_html +                  char_turn +                  player_ix +                  char +               ), +               ( +                  if +                  ( +                     (Struct.CharacterTurn.get_state char_turn) +                     == +                     Struct.CharacterTurn.SelectedCharacter +                  ) +                  then +                     (View.Controlled.ManualControls.get_html) +                  else +                     (Util.Html.nothing) +               ), +               (Html.div +                  [(Html.Attributes.class "battle-controlled-actions")] +                  (get_available_actions char_turn) +               ) +            ] +         ) + +      Nothing -> (Util.Html.nothing) diff --git a/src/battle/src/View/Controlled/CharacterCard.elm b/src/battle/src/View/Controlled/CharacterCard.elm new file mode 100644 index 0000000..b53ae57 --- /dev/null +++ b/src/battle/src/View/Controlled/CharacterCard.elm @@ -0,0 +1,582 @@ +module View.Controlled.CharacterCard exposing +   ( +      get_minimal_html, +      get_summary_html, +      get_full_html +   ) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Armor +import Struct.Attributes +import Struct.Character +import Struct.CharacterTurn +import Struct.Event +import Struct.HelpRequest +import Struct.Navigator +import Struct.Statistics +import Struct.Weapon +import Struct.WeaponSet + +import Util.Html + +import View.Character +import View.Gauge + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_name : ( +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_name char = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-card-name") +      ] +      [ +         (Html.text (Struct.Character.get_name char)) +      ] +   ) + +get_health_bar : ( +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_health_bar char = +   let +      current = (Struct.Character.get_sane_current_health char) +      max = +         (Struct.Statistics.get_max_health +            (Struct.Character.get_statistics char) +         ) +   in +      (View.Gauge.get_html +         ("HP: " ++ (toString current) ++ "/" ++ (toString max)) +         (100.0 * ((toFloat current)/(toFloat max))) +         [(Html.Attributes.class "battle-character-card-health")] +         [] +         [] +      ) + +get_rank_status : ( +      Struct.Character.Rank -> +      (Html.Html Struct.Event.Type) +   ) +get_rank_status rank = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-card-status"), +         (Html.Attributes.class "clickable"), +         (Html.Events.onClick +            (Struct.Event.RequestedHelp (Struct.HelpRequest.HelpOnRank rank)) +         ), +         (Html.Attributes.class +            ( +               case rank of +                  Struct.Character.Commander -> +                     "battle-character-card-commander-status" + +                  Struct.Character.Target -> +                     "battle-character-card-target-status" + +                  Struct.Character.Optional -> "" +            ) +         ) +      ] +      [ +      ] +   ) + +get_statuses : ( +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_statuses char = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-card-statuses") +      ] +      [ +         ( +            case (Struct.Character.get_rank char) of +               Struct.Character.Optional -> (Util.Html.nothing) +               other -> (get_rank_status other) +         ) +      ] +   ) + +get_active_movement_bar : ( +      (Maybe Struct.Navigator.Type) -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_active_movement_bar maybe_navigator char = +   let +      max = +         (Struct.Statistics.get_movement_points +            (Struct.Character.get_statistics char) +         ) +      current = +         case maybe_navigator of +            (Just navigator) -> +               (Struct.Navigator.get_remaining_points navigator) + +            Nothing -> +               max +   in +      (View.Gauge.get_html +         ("MP: " ++ (toString current) ++ "/" ++ (toString max)) +         (100.0 * ((toFloat current)/(toFloat max))) +         [(Html.Attributes.class "battle-character-card-movement")] +         [] +         [] +      ) + +get_inactive_movement_bar : ( +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_inactive_movement_bar char = +   let +      max = +         (Struct.Statistics.get_movement_points +            (Struct.Character.get_statistics char) +         ) +   in +      (View.Gauge.get_html +         ( +            "MP: " +            ++ +            (toString +               (Struct.Statistics.get_movement_points +                  (Struct.Character.get_statistics char) +               ) +            ) +         ) +         100.0 +         [(Html.Attributes.class "battle-character-card-movement")] +         [] +         [] +      ) + +get_movement_bar : ( +      Struct.CharacterTurn.Type -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_movement_bar char_turn char = +   case (Struct.CharacterTurn.try_getting_active_character char_turn) of +      (Just active_char) -> +         if +         ( +            (Struct.Character.get_index active_char) +            == +            (Struct.Character.get_index char) +         ) +         then +            (get_active_movement_bar +               (Struct.CharacterTurn.try_getting_navigator char_turn) +               active_char +            ) +         else +            (get_inactive_movement_bar char) + +      Nothing -> +         (get_inactive_movement_bar char) + +get_weapon_details : ( +      Struct.Statistics.Type -> +      Struct.Weapon.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_weapon_details stats weapon = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-card-weapon") +      ] +      [ +         (Html.div +            [ +               (Html.Attributes.class "battle-character-card-weapon-name") +            ] +            [ +               (Html.text (Struct.Weapon.get_name weapon)) +            ] +         ), +         (Html.div +            [ +               (Html.Attributes.class "battle-character-card-weapon-name") +            ] +            [ +               (Html.text +                  ( +                     "[" +                     ++ (toString (Struct.Statistics.get_damage_min stats)) +                     ++ ", " +                     ++ (toString (Struct.Statistics.get_damage_max stats)) +                     ++ "] " +                     ++ +                     (case (Struct.Weapon.get_damage_type weapon) of +                        Struct.Weapon.Slash -> "slashing " +                        Struct.Weapon.Pierce -> "piercing " +                        Struct.Weapon.Blunt -> "bludgeoning " +                     ) +                     ++ +                     (case (Struct.Weapon.get_range_type weapon) of +                        Struct.Weapon.Ranged -> "ranged" +                        Struct.Weapon.Melee -> "melee" +                     ) +                  ) +               ) +            ] +         ) +      ] +   ) + +get_weapon_summary : ( +      Struct.Weapon.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_weapon_summary weapon = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-card-weapon-summary") +      ] +      [ +         (Html.div +            [ +               (Html.Attributes.class "battle-character-card-weapon-name") +            ] +            [ +               (Html.text (Struct.Weapon.get_name weapon)) +            ] +         ), +         (Html.div +            [ +               (Html.Attributes.class "battle-character-card-weapon-name") +            ] +            [ +               (Html.text +                  ( +                     (case (Struct.Weapon.get_damage_type weapon) of +                        Struct.Weapon.Slash -> "Slashing " +                        Struct.Weapon.Pierce -> "Piercing " +                        Struct.Weapon.Blunt -> "Bludgeoning " +                     ) +                     ++ +                     (case (Struct.Weapon.get_range_type weapon) of +                        Struct.Weapon.Ranged -> "ranged" +                        Struct.Weapon.Melee -> "melee" +                     ) +                  ) +               ) +            ] +         ) +      ] +   ) + +get_armor_details : ( +      Struct.Armor.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_armor_details armor = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-card-armor") +      ] +      [ +         (Html.div +            [ +               (Html.Attributes.class "battle-character-card-armor-name") +            ] +            [ +               (Html.text (Struct.Armor.get_name armor)) +            ] +         ), +         (Html.div +            [ +               (Html.Attributes.class "battle-character-card-armor-stats") +            ] +            [ +               (stat_name "Slash"), +               (stat_val +                  (Struct.Armor.get_resistance_to Struct.Weapon.Slash armor) +                  False +               ), +               (stat_name "Pierc."), +               (stat_val +                  (Struct.Armor.get_resistance_to Struct.Weapon.Pierce armor) +                  False +               ), +               (stat_name "Blund."), +               (stat_val +                  (Struct.Armor.get_resistance_to Struct.Weapon.Blunt armor) +                  False +               ) +            ] +         ) +      ] +   ) + +stat_name  : String -> (Html.Html Struct.Event.Type) +stat_name name = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-card-stat-name") +      ] +      [ +         (Html.text name) +      ] +   ) + +stat_val : Int -> Bool -> (Html.Html Struct.Event.Type) +stat_val val perc = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-card-stat-val") +      ] +      [ +         (Html.text +            ( +               (toString val) +               ++ +               ( +                  if perc +                  then +                     "%" +                  else +                     "" +               ) +            ) +         ) +      ] +   ) + +att_dual_val : Int -> Int -> (Html.Html Struct.Event.Type) +att_dual_val base active = +   let +      diff = (active - base) +   in +      (Html.div +         [ +            (Html.Attributes.class "battle-character-card-att-dual-val") +         ] +         [ +            (Html.text +               ( +                  (toString base) +                  ++ " (" +                  ++ +                  ( +                     if (diff > 0) +                     then +                        ("+" ++ (toString diff)) +                     else +                        if (diff == 0) +                        then +                           "~" +                        else +                           (toString diff) +                  ) +                  ++ ")" +               ) +            ) +         ] +      ) + +get_relevant_stats : ( +      Struct.Character.Type -> +      Struct.Weapon.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_relevant_stats char weapon = +   let +      stats = (Struct.Character.get_statistics char) +   in +      (Html.div +         [ +            (Html.Attributes.class "battle-character-card-stats") +         ] +         [ +            (stat_name "Dodge"), +            (stat_val (Struct.Statistics.get_dodges stats) True), +            (stat_name "Parry"), +            (stat_val +               (case (Struct.Weapon.get_range_type weapon) of +                  Struct.Weapon.Ranged -> 0 +                  Struct.Weapon.Melee -> (Struct.Statistics.get_parries stats) +               ) +               True +            ), +            (stat_name "Accu."), +            (stat_val (Struct.Statistics.get_accuracy stats) False), +            (stat_name "2xHit"), +            (stat_val (Struct.Statistics.get_double_hits stats) True), +            (stat_name "Crit."), +            (stat_val (Struct.Statistics.get_critical_hits stats) True) +         ] +      ) + +get_attributes : ( +      Struct.Character.Type -> +      Struct.Weapon.Type -> +      Struct.Armor.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_attributes char weapon armor = +   let +      base_atts = (Struct.Character.get_attributes char) +      active_atts = +         (Struct.Armor.apply_to_attributes +            armor +            (Struct.Weapon.apply_to_attributes weapon base_atts) +         ) +   in +      (Html.div +         [ +            (Html.Attributes.class "battle-character-card-stats") +         ] +         [ +            (stat_name "Con"), +            (att_dual_val +               (Struct.Attributes.get_constitution base_atts) +               (Struct.Attributes.get_constitution active_atts) +            ), +            (stat_name "Dex"), +            (att_dual_val +               (Struct.Attributes.get_dexterity base_atts) +               (Struct.Attributes.get_dexterity active_atts) +            ), +            (stat_name "Int"), +            (att_dual_val +               (Struct.Attributes.get_intelligence base_atts) +               (Struct.Attributes.get_intelligence active_atts) +            ), +            (stat_name "Min"), +            (att_dual_val +               (Struct.Attributes.get_mind base_atts) +               (Struct.Attributes.get_mind active_atts) +            ), +            (stat_name "Spe"), +            (att_dual_val +               (Struct.Attributes.get_speed base_atts) +               (Struct.Attributes.get_speed active_atts) +            ), +            (stat_name "Str"), +            (att_dual_val +               (Struct.Attributes.get_strength base_atts) +               (Struct.Attributes.get_strength active_atts) +            ) +         ] +      ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_minimal_html : ( +      Int -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_minimal_html player_ix char = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-card"), +         (Html.Attributes.class "battle-character-card-minimal") +      ] +      [ +         (get_name char), +         (Html.div +            [ +               (Html.Attributes.class "battle-character-card-top") +            ] +            [ +               (View.Character.get_portrait_html player_ix char), +               (get_health_bar char), +               (get_inactive_movement_bar char), +               (get_statuses char) +            ] +         ) +      ] +   ) + +get_summary_html : ( +      Struct.CharacterTurn.Type -> +      Int -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_summary_html char_turn player_ix char = +   let +      weapon_set = (Struct.Character.get_weapons char) +      main_weapon = (Struct.WeaponSet.get_active_weapon weapon_set) +      char_statistics = (Struct.Character.get_statistics char) +      secondary_weapon = (Struct.WeaponSet.get_secondary_weapon weapon_set) +   in +      (Html.div +         [ +            (Html.Attributes.class "battle-character-card") +         ] +         [ +            (get_name char), +            (Html.div +               [ +                  (Html.Attributes.class "battle-character-card-top") +               ] +               [ +                  (View.Character.get_portrait_html player_ix char), +                  (get_health_bar char), +                  (get_movement_bar char_turn char), +                  (get_statuses char) +               ] +            ), +            (get_weapon_details char_statistics main_weapon), +            (get_armor_details (Struct.Character.get_armor char)), +            (get_relevant_stats char main_weapon), +            (get_weapon_summary secondary_weapon) +         ] +      ) + +get_full_html : ( +      Int -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_full_html player_ix char = +   let +      weapon_set = (Struct.Character.get_weapons char) +      main_weapon = (Struct.WeaponSet.get_active_weapon weapon_set) +      char_statistics = (Struct.Character.get_statistics char) +      secondary_weapon = (Struct.WeaponSet.get_secondary_weapon weapon_set) +      armor = (Struct.Character.get_armor char) +   in +      (Html.div +         [ +            (Html.Attributes.class "battle-character-card") +         ] +         [ +            (get_name char), +            (Html.div +               [ +                  (Html.Attributes.class "battle-character-card-top") +               ] +               [ +                  (View.Character.get_portrait_html player_ix char), +                  (get_health_bar char), +                  (get_inactive_movement_bar char), +                  (get_statuses char) +               ] +            ), +            (get_weapon_details char_statistics main_weapon), +            (get_armor_details armor), +            (get_relevant_stats char main_weapon), +            (get_weapon_summary secondary_weapon), +            (get_attributes char main_weapon armor) +         ] +      ) diff --git a/src/battle/src/View/Controlled/ManualControls.elm b/src/battle/src/View/Controlled/ManualControls.elm new file mode 100644 index 0000000..1dceafb --- /dev/null +++ b/src/battle/src/View/Controlled/ManualControls.elm @@ -0,0 +1,60 @@ +module View.Controlled.ManualControls exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Direction +import Struct.Event + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +direction_button : ( +      Struct.Direction.Type -> +      String -> +      (Html.Html Struct.Event.Type) +   ) +direction_button dir label = +   (Html.div +      [ +         (Html.Attributes.class ("battle-manual-controls-" ++ label)), +         (Html.Attributes.class "clickable"), +         (Html.Events.onClick +            (Struct.Event.DirectionRequested dir) +         ) +      ] +      [] +   ) + +go_button : (Html.Html Struct.Event.Type) +go_button = +   (Html.button +      [ +         (Html.Attributes.class "battle-manual-controls-go"), +         (Html.Events.onClick Struct.Event.AttackWithoutMovingRequest) +      ] +      [ +         (Html.text "Go") +      ] +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : (Html.Html Struct.Event.Type) +get_html = +   (Html.div +      [ +         (Html.Attributes.class "battle-manual-controls") +      ] +      [ +         (direction_button Struct.Direction.Left "left"), +         (direction_button Struct.Direction.Down "down"), +         (direction_button Struct.Direction.Up "up"), +         (direction_button Struct.Direction.Right "right"), +         (go_button) +      ] +   ) diff --git a/src/battle/src/View/Controlled/Targets.elm b/src/battle/src/View/Controlled/Targets.elm new file mode 100644 index 0000000..eee5a54 --- /dev/null +++ b/src/battle/src/View/Controlled/Targets.elm @@ -0,0 +1,69 @@ +module View.SideBar.Targets exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Dict + +import Html +import Html.Attributes + +-- Map ------------------------------------------------------------------- +import Struct.Character +import Struct.Event +import Struct.Model +import Struct.Statistics + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +get_target_info_html : ( +      Struct.Model.Type -> +      Struct.Character.Ref -> +      (Html.Html Struct.Event.Type) +   ) +get_target_info_html model char_ref = +   case (Dict.get char_ref model.characters) of +      Nothing -> (Html.text "Error: Unknown character selected.") +      (Just char) -> +         (Html.text +            ( +               "Attacking " +               ++ char.name +               ++ " (player " +               ++ (toString (Struct.Character.get_player_ix char)) +               ++ "): " +               ++ +               (toString +                  (Struct.Statistics.get_movement_points +                     (Struct.Character.get_statistics char) +                  ) +               ) +               ++ " movement points; " +               ++ "???" +               ++ " attack range. Health: " +               ++ (toString (Struct.Character.get_sane_current_health char)) +               ++ "/" +               ++ +               (toString +                  (Struct.Statistics.get_max_health +                     (Struct.Character.get_statistics char) +                  ) +               ) +            ) +         ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.Model.Type -> +      Struct.Character.Ref -> +      (Html.Html Struct.Event.Type) +   ) +get_html model target_ref = +   (Html.div +      [ +         (Html.Attributes.class "battle-side-bar-targets") +      ] +      [(get_target_info_html model target_ref)] +   ) diff --git a/src/battle/src/View/Gauge.elm b/src/battle/src/View/Gauge.elm new file mode 100644 index 0000000..cf89f3a --- /dev/null +++ b/src/battle/src/View/Gauge.elm @@ -0,0 +1,76 @@ +module View.Gauge exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes + +-- Map ------------------------------------------------------------------- +import Struct.Event + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_text_div: ( +      String -> +      List (Html.Attribute Struct.Event.Type) -> +      (Html.Html Struct.Event.Type) +   ) +get_text_div text extra_txt_attr = +   (Html.div +      ( +         [(Html.Attributes.class "battle-gauge-text")] +         ++ extra_txt_attr +      ) +      [ +         (Html.text text) +      ] +   ) + +get_bar_div: ( +      Float -> +      List (Html.Attribute Struct.Event.Type) -> +      (Html.Html Struct.Event.Type) +   ) +get_bar_div percent extra_bar_attr = +   (Html.div +      ( +         [ +            (Html.Attributes.style +               [ +                  ("width", ((toString percent) ++ "%")) +               ] +            ), +            (Html.Attributes.class +               "battle-gauge-bar" +            ) +         ] +         ++ +         extra_bar_attr +      ) +      [ +      ] +   ) + + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      String -> +      Float -> +      List (Html.Attribute Struct.Event.Type) -> +      List (Html.Attribute Struct.Event.Type) -> +      List (Html.Attribute Struct.Event.Type) -> +      (Html.Html Struct.Event.Type) +   ) +get_html text percent extra_div_attr extra_bar_attr extra_txt_attr = +   (Html.div +      ( +         [(Html.Attributes.class "battle-gauge")] +         ++ extra_div_attr +      ) +      [ +         (get_text_div text extra_txt_attr), +         (get_bar_div percent extra_bar_attr) +      ] +   ) diff --git a/src/battle/src/View/MainMenu.elm b/src/battle/src/View/MainMenu.elm new file mode 100644 index 0000000..9f3099b --- /dev/null +++ b/src/battle/src/View/MainMenu.elm @@ -0,0 +1,38 @@ +module View.MainMenu exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.UI + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_menu_button_html : ( +      Struct.UI.Tab -> +      (Html.Html Struct.Event.Type) +   ) +get_menu_button_html tab = +   (Html.button +      [ (Html.Events.onClick (Struct.Event.TabSelected tab)) ] +      [ (Html.text (Struct.UI.to_string tab)) ] +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : (Html.Html Struct.Event.Type) +get_html = +   (Html.div +      [ +         (Html.Attributes.class "battle-main-menu") +      ] +      (List.map +         (get_menu_button_html) +         (Struct.UI.get_all_tabs) +      ) +   ) diff --git a/src/battle/src/View/Map.elm b/src/battle/src/View/Map.elm new file mode 100644 index 0000000..ad10695 --- /dev/null +++ b/src/battle/src/View/Map.elm @@ -0,0 +1,162 @@ +module View.Map exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Html +import Html.Attributes +import Html.Lazy + +import List + +-- Map ------------------------------------------------------------------- +import Constants.UI + +import Struct.Map +import Struct.Character +import Struct.Event +import Struct.Model +import Struct.Navigator +import Struct.UI + +import Util.Html + +import View.Map.Character +import View.Map.Navigator +import View.Map.Tile + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_tiles_html : Struct.Map.Type -> (Html.Html Struct.Event.Type) +get_tiles_html map = +   (Html.div +      [ +         (Html.Attributes.class "battle-tiles-layer"), +         (Html.Attributes.style +            [ +               ( +                  "width", +                  ( +                     (toString +                        ( +                           (Struct.Map.get_width map) +                           * Constants.UI.tile_size +                        ) +                     ) +                     ++ "px" +                  ) +               ), +               ( +                  "height", +                  ( +                     (toString +                        ( +                           (Struct.Map.get_height map) +                           * Constants.UI.tile_size +                        ) +                     ) +                     ++ "px" +                  ) +               ) +            ] +         ) +      ] +      (List.map +         (View.Map.Tile.get_html) +         (Array.toList (Struct.Map.get_tiles map)) +      ) +   ) + +maybe_print_navigator : ( +      Bool -> +      (Maybe Struct.Navigator.Type) -> +      (Html.Html Struct.Event.Type) +   ) +maybe_print_navigator interactive maybe_nav = +   let +      name_suffix = +         if (interactive) +         then +            "interactive" +         else +            "non-interactive" +   in +      case maybe_nav of +         (Just nav) -> +            (Html.div +               [ +                  (Html.Attributes.class ("battle-navigator" ++ name_suffix)) +               ] +               (View.Map.Navigator.get_html +                  (Struct.Navigator.get_summary nav) +                  interactive +               ) +            ) + +         Nothing -> +            (Util.Html.nothing) + +get_characters_html : ( +      Struct.Model.Type -> +      (Array.Array Struct.Character.Type) -> +      (Html.Html Struct.Event.Type) +   ) +get_characters_html model characters = +   (Html.div +      [ +         (Html.Attributes.class "battle-characters") +      ] +      (List.map +         (View.Map.Character.get_html model) +         (Array.toList model.characters) +      ) +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.Model.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_html model = +   (Html.div +      [ +         (Html.Attributes.class "battle-actual"), +         (Html.Attributes.style +            ( +               if ((Struct.UI.get_zoom_level model.ui) == 1) +               then [] +               else +                  [ +                     ( +                        "transform", +                        ( +                           "scale(" +                           ++ +                           (toString (Struct.UI.get_zoom_level model.ui)) +                           ++ ")" +                        ) +                     ) +                  ] +            ) +         ) +      ] +      [ +         (Html.Lazy.lazy (get_tiles_html) model.map), +         -- Not in lazy mode, because I can't easily get rid of that 'model' +         -- parameter. +         (get_characters_html model model.characters), +         (Html.Lazy.lazy2 +            (maybe_print_navigator) +            True +            model.char_turn.navigator +         ), +         (Html.Lazy.lazy2 +            (maybe_print_navigator) +            False +            (Struct.UI.try_getting_displayed_nav model.ui) +         ) +      ] +   ) diff --git a/src/battle/src/View/Map/Character.elm b/src/battle/src/View/Map/Character.elm new file mode 100644 index 0000000..aaf7cb2 --- /dev/null +++ b/src/battle/src/View/Map/Character.elm @@ -0,0 +1,218 @@ +module View.Map.Character exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +import Html.Events + +-- Map  ------------------------------------------------------------------ +import Constants.UI + +import Util.Html + +import Struct.Character +import Struct.CharacterTurn +import Struct.Event +import Struct.Model +import Struct.TurnResult +import Struct.TurnResultAnimator +import Struct.UI + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_animation_class : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      (Html.Attribute Struct.Event.Type) +   ) +get_animation_class model char = +   case model.animator of +      Nothing -> (Html.Attributes.class "") +      (Just animator) -> +         case (Struct.TurnResultAnimator.get_current_animation animator) of +            (Struct.TurnResultAnimator.Focus char_index) -> +               if ((Struct.Character.get_index char) /= char_index) +               then +                  (Html.Attributes.class "") +               else +                  (Html.Attributes.class "battle-character-selected") + +            (Struct.TurnResultAnimator.TurnResult current_action) -> +               if +               ( +                  (Struct.TurnResult.get_actor_index current_action) +                  /= +                  (Struct.Character.get_index char) +               ) +               then +                  (Html.Attributes.class "") +               else +                  case current_action of +                     (Struct.TurnResult.Moved _) -> +                        (Html.Attributes.class +                           "battle-animated-character-icon" +                        ) + +                     _ -> (Html.Attributes.class "") +            _ -> (Html.Attributes.class "") + +get_activation_level_class : ( +      Struct.Character.Type -> +      (Html.Attribute Struct.Event.Type) +   ) +get_activation_level_class char = +   if (Struct.Character.is_enabled char) +   then +      (Html.Attributes.class "battle-character-icon-enabled") +   else +      (Html.Attributes.class "battle-character-icon-disabled") + +get_alliance_class : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      (Html.Attribute Struct.Event.Type) +   ) +get_alliance_class model char = +   if ((Struct.Character.get_player_ix char) == model.player_ix) +   then +      (Html.Attributes.class "battle-character-ally") +   else +      (Html.Attributes.class "battle-character-enemy") + +get_position_style : ( +      Struct.Character.Type -> +      (Html.Attribute Struct.Event.Type) +   ) +get_position_style char = +   let char_loc = (Struct.Character.get_location char) in +      (Html.Attributes.style +         [ +            ("top", ((toString (char_loc.y * Constants.UI.tile_size)) ++ "px")), +            ("left", ((toString (char_loc.x * Constants.UI.tile_size)) ++ "px")) +         ] +      ) + +get_focus_class : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      (Html.Attribute Struct.Event.Type) +   ) +get_focus_class model char = +   if +   ( +      (Struct.UI.get_previous_action model.ui) +      == +      (Just (Struct.UI.SelectedCharacter (Struct.Character.get_index char))) +   ) +   then +      (Html.Attributes.class "battle-character-selected") +   else +      if +      ( +         (Struct.CharacterTurn.try_getting_target model.char_turn) +         == +         (Just (Struct.Character.get_index char)) +      ) +      then +         (Html.Attributes.class "battle-character-targeted") +      else +         (Html.Attributes.class "") + +get_body_html : Struct.Character.Type -> (Html.Html Struct.Event.Type) +get_body_html char = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-icon-body"), +         (Html.Attributes.class +            ( +               "asset-character-team-body-" +               ++ (toString (Struct.Character.get_player_ix char)) +            ) +         ) +      ] +      [ +      ] +   ) + +get_head_html : Struct.Character.Type -> (Html.Html Struct.Event.Type) +get_head_html char = +   (Html.div +      [ +         (Html.Attributes.class "battle-character-icon-head"), +         (Html.Attributes.class +            ("asset-character-icon-" ++ (Struct.Character.get_icon_id char)) +         ) +      ] +      [ +      ] +   ) + +get_banner_html : Struct.Character.Type -> (Html.Html Struct.Event.Type) +get_banner_html char = +   case (Struct.Character.get_rank char) of +      Struct.Character.Commander -> +         (Html.div +            [ +               (Html.Attributes.class "battle-character-icon-banner"), +               (Html.Attributes.class "asset-character-icon-commander-banner") +            ] +            [ +            ] +         ) + +      Struct.Character.Target -> +         (Html.div +            [ +               (Html.Attributes.class "battle-character-icon-banner"), +               (Html.Attributes.class "asset-character-icon-target-banner") +            ] +            [ +            ] +         ) + +      _ -> (Util.Html.nothing) + +get_actual_html : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_actual_html model char = +      (Html.div +         [ +            (Html.Attributes.class "battle-tiled"), +            (Html.Attributes.class "battle-character-icon"), +            (get_animation_class model char), +            (get_activation_level_class char), +            (get_alliance_class model char), +            (get_position_style char), +            (get_focus_class model char), +            (Html.Attributes.class "clickable"), +            (Html.Events.onClick +               (Struct.Event.CharacterSelected +                  (Struct.Character.get_index char) +               ) +            ) +         ] +         [ +            (get_body_html char), +            (get_head_html char), +            (get_banner_html char) +         ] +      ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.Model.Type -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_html model char = +   if (Struct.Character.is_alive char) +   then +      (get_actual_html model char) +   else +      (Util.Html.nothing) diff --git a/src/battle/src/View/Map/Navigator.elm b/src/battle/src/View/Map/Navigator.elm new file mode 100644 index 0000000..63c982a --- /dev/null +++ b/src/battle/src/View/Map/Navigator.elm @@ -0,0 +1,245 @@ +module View.Map.Navigator exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +import Html.Events + +import List + +-- Map ------------------------------------------------------------------- +import Constants.UI + +import Struct.Direction +import Struct.Event +import Struct.Location +import Struct.Marker +import Struct.Navigator + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +marker_get_html : ( +      Bool -> +      (Struct.Location.Ref, Struct.Marker.Type) -> +      (Html.Html Struct.Event.Type) +   ) +marker_get_html is_interactive (loc_ref, marker) = +   (Html.div +      ( +         [ +            (Html.Attributes.class "battle-marker-icon"), +            (Html.Attributes.class "battle-tiled"), +            (Html.Attributes.class +               ( +                  "battle-" +                  ++ +                  ( +                     case marker of +                        Struct.Marker.CanGoToCanDefend -> "can-go-to-can-defend" +                        Struct.Marker.CanGoToCantDefend -> +                           "can-go-to-cant-defend" + +                        Struct.Marker.CanAttackCanDefend -> +                           "can-attack-can-defend" + +                        Struct.Marker.CanAttackCantDefend -> +                           "can-attack-cant-defend" +                  ) +                  ++ +                  "-marker" +               ) +            ), +            (Html.Attributes.style +               ( +                  let +                     loc = (Struct.Location.from_ref loc_ref) +                  in +                     [ +                        ( +                           "top", +                           ((toString (loc.y * Constants.UI.tile_size)) ++ "px") +                        ), +                        ( +                           "left", +                           ((toString (loc.x * Constants.UI.tile_size)) ++ "px") +                        ) +                     ] +               ) +            ) +         ] +         ++ +         ( +            if (is_interactive) +            then +               if +               ( +                  (marker == Struct.Marker.CanGoToCanDefend) +                  || (marker == Struct.Marker.CanGoToCantDefend) +               ) +               then +                  [ +                     (Html.Attributes.class "battle-navigator-interactive"), +                     (Html.Attributes.class "clickable"), +                     (Html.Events.onClick +                        (Struct.Event.CharacterOrTileSelected loc_ref) +                     ) +                  ] +               else +                  [ +                     (Html.Attributes.class "battle-navigator-interactive") +                  ] +            else +               [ +                  (Html.Attributes.class "battle-navigator-non-interactive"), +                  (Html.Events.onClick +                     (Struct.Event.CharacterOrTileSelected loc_ref) +                  ) +               ] +         ) +      ) +      [ +      ] +   ) + +path_node_get_html : ( +      Bool -> +      Struct.Direction.Type -> +      ( +         Struct.Location.Type, +         Struct.Direction.Type, +         (List (Html.Html Struct.Event.Type)) +      ) -> +      ( +         Struct.Location.Type, +         Struct.Direction.Type, +         (List (Html.Html Struct.Event.Type)) +      ) +   ) +path_node_get_html is_below_markers next_dir (curr_loc, curr_dir, curr_nodes) = +   ( +      (Struct.Location.neighbor next_dir curr_loc), +      next_dir, +      ( +         (Html.div +            [ +               (Html.Attributes.class "battle-path-icon"), +               (Html.Attributes.class +                  ( +                     if (is_below_markers) +                     then +                        "battle-path-icon-below-markers" +                     else +                        "battle-path-icon-above-markers" +                  ) +               ), +               (Html.Attributes.class "battle-tiled"), +               (Html.Attributes.class +                  ( +                     "battle-path-icon-" +                     ++ +                     (Struct.Direction.to_string curr_dir) +                     ++ +                     (Struct.Direction.to_string next_dir) +                  ) +               ), +               (Html.Events.onClick +                  (Struct.Event.CharacterOrTileSelected +                     (Struct.Location.get_ref curr_loc) +                  ) +               ), +               (Html.Attributes.style +                  [ +                     ( +                        "top", +                        ( +                           (toString (curr_loc.y * Constants.UI.tile_size)) +                           ++ +                           "px" +                        ) +                     ), +                     ( +                        "left", +                        ( +                           (toString (curr_loc.x * Constants.UI.tile_size)) +                           ++ +                           "px" +                        ) +                     ) +                  ] +               ) +            ] +            [ +            ] +         ) +         :: +         curr_nodes +      ) +   ) + +mark_the_spot : ( +      Struct.Location.Type -> +      Struct.Direction.Type -> +      (Html.Html Struct.Event.Type) +   ) +mark_the_spot loc origin_dir = +   (Html.div +      [ +         (Html.Attributes.class "battle-path-icon"), +         (Html.Attributes.class "battle-path-icon-above-markers"), +         (Html.Attributes.class "battle-tiled"), +         (Html.Attributes.class +            ( +               "battle-path-icon-mark" +               ++ +               (Struct.Direction.to_string origin_dir) +            ) +         ), +         (Html.Events.onClick +            (Struct.Event.CharacterOrTileSelected (Struct.Location.get_ref loc)) +         ), +         (Html.Attributes.style +            [ +               ( +                  "top", +                  ((toString (loc.y * Constants.UI.tile_size)) ++ "px") +               ), +               ( +                  "left", +                  ((toString (loc.x * Constants.UI.tile_size)) ++ "px") +               ) +            ] +         ) +      ] +      [ +      ] +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.Navigator.Summary -> +      Bool -> +      (List (Html.Html Struct.Event.Type)) +   ) +get_html nav_summary is_interactive = +   if (is_interactive) +   then +      ( +         (List.map (marker_get_html True) nav_summary.markers) +         ++ +         ( +            let +               (final_loc, final_dir, path_node_htmls) = +                  (List.foldr +                     (path_node_get_html nav_summary.locked_path) +                     (nav_summary.starting_location, Struct.Direction.None, []) +                     nav_summary.path +                  ) +            in +               ((mark_the_spot final_loc final_dir) :: path_node_htmls) +         ) +      ) +   else +      (List.map (marker_get_html False) nav_summary.markers) diff --git a/src/battle/src/View/Map/Tile.elm b/src/battle/src/View/Map/Tile.elm new file mode 100644 index 0000000..600e26d --- /dev/null +++ b/src/battle/src/View/Map/Tile.elm @@ -0,0 +1,69 @@ +module View.Map.Tile exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +import Html.Events + +-- Map ------------------------------------------------------------------- +import Constants.UI +import Constants.IO + +import Struct.Event +import Struct.Location +import Struct.Tile + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.Tile.Instance -> +      (Html.Html Struct.Event.Type) +   ) +get_html tile = +   let +      tile_loc = (Struct.Tile.get_location tile) +   in +      (Html.div +         [ +            (Html.Attributes.class "battle-tile-icon"), +            (Html.Attributes.class "battle-tiled"), +            (Html.Attributes.class +               ( +                  "battle-tile-variant-" +                  ++ (toString (Struct.Tile.get_variant_id tile)) +               ) +            ), +            (Html.Attributes.class "clickable"), +            (Html.Events.onClick +               (Struct.Event.TileSelected (Struct.Location.get_ref tile_loc)) +            ), +            (Html.Attributes.style +               [ +                  ( +                     "top", +                     ((toString (tile_loc.y * Constants.UI.tile_size)) ++ "px") +                  ), +                  ( +                     "left", +                     ((toString (tile_loc.x * Constants.UI.tile_size)) ++ "px") +                  ), +                  ( +                     "background-image", +                     ( +                        "url(" +                        ++ Constants.IO.tile_assets_url +                        ++ (Struct.Tile.get_icon_id tile) +                        ++".svg)" +                     ) +                  ) +               ] +            ) +         ] +         [ +         ] +      ) diff --git a/src/battle/src/View/MessageBoard.elm b/src/battle/src/View/MessageBoard.elm new file mode 100644 index 0000000..736f938 --- /dev/null +++ b/src/battle/src/View/MessageBoard.elm @@ -0,0 +1,30 @@ +module View.MessageBoard exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html + +-- Struct.Map ------------------------------------------------------------------- +import Struct.Event +import Struct.Model + +import View.MessageBoard.Animator +import View.MessageBoard.Error +import View.MessageBoard.Help + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : Struct.Model.Type -> (Html.Html Struct.Event.Type) +get_html model = +   case (model.error) of +      (Just error) -> (View.MessageBoard.Error.get_html model error) +      Nothing -> +         case model.animator of +            (Just animator) -> +               (View.MessageBoard.Animator.get_html model animator) + +            Nothing -> (View.MessageBoard.Help.get_html model) diff --git a/src/battle/src/View/MessageBoard/Animator.elm b/src/battle/src/View/MessageBoard/Animator.elm new file mode 100644 index 0000000..49bb83a --- /dev/null +++ b/src/battle/src/View/MessageBoard/Animator.elm @@ -0,0 +1,57 @@ +module View.MessageBoard.Animator exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.Model +import Struct.TurnResult +import Struct.TurnResultAnimator + +import Util.Html + +import View.MessageBoard.Animator.Attack + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_turn_result_html : ( +      Struct.Model.Type -> +      Struct.TurnResult.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_turn_result_html model turn_result = +   case turn_result of +      (Struct.TurnResult.Attacked attack) -> +         (View.MessageBoard.Animator.Attack.get_html +            model +            (Struct.TurnResult.get_actor_index turn_result) +            (Struct.TurnResult.get_attack_defender_index attack) +            (Struct.TurnResult.maybe_get_attack_next_step attack) +         ) + +      _ -> (Util.Html.nothing) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.Model.Type -> +      Struct.TurnResultAnimator.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_html model animator = +   case (Struct.TurnResultAnimator.get_current_animation animator) of +      (Struct.TurnResultAnimator.TurnResult turn_result) -> +         (get_turn_result_html model turn_result) + +      (Struct.TurnResultAnimator.AttackSetup (attacker_id, defender_id)) -> +         (View.MessageBoard.Animator.Attack.get_html +            model +            attacker_id +            defender_id +            Nothing +         ) + +      _ -> (Util.Html.nothing) diff --git a/src/battle/src/View/MessageBoard/Animator/Attack.elm b/src/battle/src/View/MessageBoard/Animator/Attack.elm new file mode 100644 index 0000000..437a76d --- /dev/null +++ b/src/battle/src/View/MessageBoard/Animator/Attack.elm @@ -0,0 +1,297 @@ +module View.MessageBoard.Animator.Attack exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Html +import Html.Attributes + +-- Map ------------------------------------------------------------------- +import Struct.Attack +import Struct.Character +import Struct.Event +import Struct.Model + +import View.Controlled.CharacterCard +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_effect_text : Struct.Attack.Type -> String +get_effect_text attack = +   ( +      ( +         case attack.precision of +            Struct.Attack.Hit -> " hit for " +            Struct.Attack.Graze -> " grazed for " +            Struct.Attack.Miss -> " missed." +      ) +      ++ +      ( +         if (attack.precision == Struct.Attack.Miss) +         then +            "" +         else +            ( +               ((toString attack.damage) ++ " damage") +               ++ +               ( +                  if (attack.critical) +                  then " (Critical Hit)." +                  else "." +               ) +            ) +      ) +   ) + +get_empty_attack_html : (Html.Html Struct.Event.Type) +get_empty_attack_html = +   (Html.div +      [ +         (Html.Attributes.class "battle-message-attack-text") +      ] +      [] +   ) + +get_attack_html : ( +      Struct.Character.Type -> +      Struct.Character.Type -> +      Struct.Attack.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_attack_html attacker defender attack = +   let +      attacker_name = (Struct.Character.get_name attacker) +      defender_name = (Struct.Character.get_name defender) +   in +      (Html.div +         [ +            (Html.Attributes.class "battle-message-attack-text") +         ] +         [ +            (Html.text +               ( +                  case (attack.order, attack.parried) of +                     (Struct.Attack.Counter, True) -> +                        ( +                           defender_name +                           ++ " attempted to strike back, but " +                           ++ attacker_name +                           ++ " parried, and " +                           ++ (get_effect_text attack) +                        ) + +                     (Struct.Attack.Counter, _) -> +                        ( +                           defender_name +                           ++ " striked back, and " +                           ++ (get_effect_text attack) +                        ) + +                     (_, True) -> +                        ( +                           attacker_name +                           ++ " attempted a hit, but " +                           ++ defender_name +                           ++ " parried, and " +                           ++ (get_effect_text attack) +                        ) + +                     (_, _) -> +                        (attacker_name ++ " " ++ (get_effect_text attack)) +               ) +            ) +         ] +      ) + +get_attack_animation_class : ( +      Struct.Attack.Type -> +      Struct.Character.Type -> +      String +   ) +get_attack_animation_class attack char = +   if (attack.critical) +   then +      "battle-animated-portrait-attack-critical" +   else +      "battle-animated-portrait-attacks" + +get_defense_animation_class : ( +      Struct.Attack.Type -> +      Struct.Character.Type -> +      String +   ) +get_defense_animation_class attack char = +   if (attack.damage == 0) +   then +      if (attack.precision == Struct.Attack.Miss) +      then +         "battle-animated-portrait-dodges" +      else +         "battle-animated-portrait-undamaged" +   else if ((Struct.Character.get_current_health char) > 0) +   then +      if (attack.precision == Struct.Attack.Graze) +      then +         "battle-animated-portrait-grazed-damage" +      else +         "battle-animated-portrait-damaged" +   else +      if (attack.precision == Struct.Attack.Graze) +      then +         "battle-animated-portrait-grazed-death" +      else +         "battle-animated-portrait-dies" + +get_attacker_card : ( +      (Maybe Struct.Attack.Type) -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_attacker_card maybe_attack char = +   (Html.div +      (case maybe_attack of +         Nothing -> +            if ((Struct.Character.get_current_health char) > 0) +            then +               [ +                  (Html.Attributes.class "battle-animated-portrait") +               ] +            else +               [ +                  (Html.Attributes.class "battle-animated-portrait-absent"), +                  (Html.Attributes.class "battle-animated-portrait") +               ] + +         (Just attack) -> +            [ +               (Html.Attributes.class +                  (case (attack.order, attack.parried) of +                     (Struct.Attack.Counter, True) -> +                        (get_attack_animation_class attack char) + +                     (Struct.Attack.Counter, _) -> +                        (get_defense_animation_class attack char) + +                     (_, True) -> +                        (get_defense_animation_class attack char) + +                     (_, _) -> +                        (get_attack_animation_class attack char) +                  ) +               ), +               (Html.Attributes.class "battle-animated-portrait") +            ] +      ) +      [ +         (View.Controlled.CharacterCard.get_minimal_html +            (Struct.Character.get_player_ix char) +            char +         ) +      ] +   ) + +get_defender_card : ( +      (Maybe Struct.Attack.Type) -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_defender_card maybe_attack char = +   (Html.div +      (case maybe_attack of +         Nothing -> +            if ((Struct.Character.get_current_health char) > 0) +            then +               [ +                  (Html.Attributes.class "battle-animated-portrait") +               ] +            else +               [ +                  (Html.Attributes.class "battle-animated-portrait-absent"), +                  (Html.Attributes.class "battle-animated-portrait") +               ] + +         (Just attack) -> +            [ +               (Html.Attributes.class +                  (case (attack.order, attack.parried) of +                     (Struct.Attack.Counter, True) -> +                        (get_defense_animation_class attack char) + +                     (Struct.Attack.Counter, _) -> +                        (get_attack_animation_class attack char) + +                     (_, True) -> +                        (get_attack_animation_class attack char) + +                     (_, _) -> +                        (get_defense_animation_class attack char) +                  ) +               ), +               (Html.Attributes.class "battle-animated-portrait") +            ] +      ) +      [ +         (View.Controlled.CharacterCard.get_minimal_html -1 char) +      ] +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_placeholder_html : ( +      (Array.Array Struct.Character.Type) -> +      Int -> +      Int -> +      (Maybe Struct.Attack.Type) -> +      (Html.Html Struct.Event.Type) +   ) +get_placeholder_html characters attacker_ix defender_ix maybe_attack = +   case +      ( +         (Array.get attacker_ix characters), +         (Array.get defender_ix characters) +      ) +   of +      ((Just atkchar), (Just defchar)) -> +         (Html.div +            [ +               (Html.Attributes.class "battle-message-board"), +               (Html.Attributes.class "battle-message-attack") +            ] +            ( +               [ +                  (get_attacker_card maybe_attack atkchar), +                  ( +                     case maybe_attack of +                        (Just attack) -> +                           (get_attack_html atkchar defchar attack) + +                        Nothing -> +                           (get_empty_attack_html) +                  ), +                  (get_defender_card maybe_attack defchar) +               ] +            ) +         ) + +      _ -> +         (Html.div +            [ +            ] +            [ +               (Html.text "Error: Attack with unknown characters") +            ] +         ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.Model.Type -> +      Int -> +      Int -> +      (Maybe Struct.Attack.Type) -> +      (Html.Html Struct.Event.Type) +   ) +get_html model attacker_ix defender_ix maybe_attack = +   (get_placeholder_html model.characters attacker_ix defender_ix maybe_attack) diff --git a/src/battle/src/View/MessageBoard/Error.elm b/src/battle/src/View/MessageBoard/Error.elm new file mode 100644 index 0000000..797d89f --- /dev/null +++ b/src/battle/src/View/MessageBoard/Error.elm @@ -0,0 +1,33 @@ +module View.MessageBoard.Error exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes + +-- Map ------------------------------------------------------------------- +import Struct.Error +import Struct.Event +import Struct.Model + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.Model.Type -> +      Struct.Error.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_html model error = +   (Html.div +      [ +         (Html.Attributes.class "battle-message-board"), +         (Html.Attributes.class "battle-error") +      ] +      [ +         (Html.text (Struct.Error.to_string error)) +      ] +   ) diff --git a/src/battle/src/View/MessageBoard/Help.elm b/src/battle/src/View/MessageBoard/Help.elm new file mode 100644 index 0000000..6c20bbc --- /dev/null +++ b/src/battle/src/View/MessageBoard/Help.elm @@ -0,0 +1,37 @@ +module View.MessageBoard.Help exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.HelpRequest +import Struct.Model + +import View.MessageBoard.Help.Guide +import View.MessageBoard.Help.Rank + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : Struct.Model.Type -> (Html.Html Struct.Event.Type) +get_html model = +   (Html.div +      [ +         (Html.Attributes.class "battle-message-board"), +         (Html.Attributes.class "battle-message-board-help") +      ] +      ( +         case model.help_request of +            Struct.HelpRequest.None -> +               (View.MessageBoard.Help.Guide.get_html_contents model) + +            (Struct.HelpRequest.HelpOnRank rank) -> +               (View.MessageBoard.Help.Rank.get_html_contents rank) +      ) +   ) diff --git a/src/battle/src/View/MessageBoard/Help/Guide.elm b/src/battle/src/View/MessageBoard/Help/Guide.elm new file mode 100644 index 0000000..0a41e91 --- /dev/null +++ b/src/battle/src/View/MessageBoard/Help/Guide.elm @@ -0,0 +1,100 @@ +module View.MessageBoard.Help.Guide exposing (get_html_contents) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes + +-- Map ------------------------------------------------------------------- +import Struct.CharacterTurn +import Struct.Event +import Struct.Model + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_header_html : (String -> (Html.Html Struct.Event.Type)) +get_header_html title = +   (Html.h1 +      [] +      [ +         (Html.div +            [(Html.Attributes.class "battle-help-guide-icon")] +            [] +         ), +         (Html.text title) +      ] +   ) + +get_selected_character_html_contents : (List (Html.Html Struct.Event.Type)) +get_selected_character_html_contents = +   [ +      (get_header_html "Controlling a Character"), +      (Html.text +         ( +            "Click on a target tile to select a path or use the manual" +            ++ " controls (on the left panel) to make your own. Click on the" +            ++ " destination tile again to confirm (this can be reverted)." +         ) +      ) +   ] + +get_moved_character_html_contents : (List (Html.Html Struct.Event.Type)) +get_moved_character_html_contents = +   [ +      (get_header_html "Selecting a Target"), +      (Html.text +         ( +            "You can now choose a target in range. Dashed tiles indicate" +            ++ " where your character will not be able to defend themselves" +            ++ " against counter attacks." +         ) +      ) +   ] + +get_chose_target_html_contents : (List (Html.Html Struct.Event.Type)) +get_chose_target_html_contents = +   [ +      (get_header_html "Finalizing the Character's Turn"), +      (Html.text +         ( +            "If you are satisfied with your choices, you can end this" +            ++ " character's turn and see the results unfold. Otherwise, click" +            ++ " on the abort button to undo it all." +         ) +      ) +   ] + +get_default_html_contents : (List (Html.Html Struct.Event.Type)) +get_default_html_contents = +   [ +      (get_header_html "Selecting a Character"), +      (Html.text +         ( +            "Click once on a character to focus them. This will show you" +            ++ " their stats, equipment, and other infos. If they are in" +            ++ " your team and active (the pulsating characters)," +            ++ " clicking on them again will let you take control." +         ) +      ) +   ] + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html_contents : ( +      Struct.Model.Type -> +      (List (Html.Html Struct.Event.Type)) +   ) +get_html_contents model = +   case (Struct.CharacterTurn.get_state model.char_turn) of +      Struct.CharacterTurn.SelectedCharacter -> +         (get_selected_character_html_contents) + +      Struct.CharacterTurn.MovedCharacter -> +         (get_moved_character_html_contents) + +      Struct.CharacterTurn.ChoseTarget -> +         (get_chose_target_html_contents) + +      _ -> +         (get_default_html_contents) diff --git a/src/battle/src/View/MessageBoard/Help/Rank.elm b/src/battle/src/View/MessageBoard/Help/Rank.elm new file mode 100644 index 0000000..4a01e75 --- /dev/null +++ b/src/battle/src/View/MessageBoard/Help/Rank.elm @@ -0,0 +1,97 @@ +module View.MessageBoard.Help.Rank exposing (get_html_contents) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes + +-- Map ------------------------------------------------------------------- +import Struct.Character +import Struct.Event + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_guide_icon_html : (Html.Html Struct.Event.Type) +get_guide_icon_html = +   (Html.div +      [(Html.Attributes.class "battle-help-guide-icon")] +      [] +   ) + +get_header_with_icon_html : String -> String -> (Html.Html Struct.Event.Type) +get_header_with_icon_html title rank_name = +   (Html.h1 +      [] +      [ +         (get_guide_icon_html), +         (Html.text (title ++ " - ")), +         (Html.div +            [ +               (Html.Attributes.class +                  "battle-message-board-help-figure" +               ), +               (Html.Attributes.class +                  ("battle-character-card-" ++ rank_name ++ "-status") +               ) +            ] +            [] +         ) +      ] +   ) + +get_target_help_message : (List (Html.Html Struct.Event.Type)) +get_target_help_message = +   [ +      (get_header_with_icon_html "Protected Character" "target"), +      (Html.text +         ( +            "Players that lose all of their Protected Characters are" +            ++ " eliminated." +         ) +      ) +   ] + +get_commander_help_message : (List (Html.Html Struct.Event.Type)) +get_commander_help_message = +   [ +      (get_header_with_icon_html "Critical Character" "commander"), +      (Html.text +         ( +            "Players that lose any of their Critical Characters are" +            ++ " eliminated." +         ) +      ) +   ] + +get_optional_help_message : (List (Html.Html Struct.Event.Type)) +get_optional_help_message = +   [ +      (Html.h1 +         [] +         [ +            (get_guide_icon_html), +            (Html.text "Reinforcement Character") +         ] +      ), +      (Html.text +         ( +            "Unless it is their very last character, losing a" +            ++ " Reinforcement characters never causes a player to be" +            ++ " eliminated." +         ) +      ) +   ] + + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html_contents : ( +      Struct.Character.Rank -> +      (List (Html.Html Struct.Event.Type)) +   ) +get_html_contents rank = +   case rank of +      Struct.Character.Target -> (get_target_help_message) +      Struct.Character.Commander -> (get_commander_help_message) +      Struct.Character.Optional -> (get_optional_help_message) diff --git a/src/battle/src/View/SubMenu.elm b/src/battle/src/View/SubMenu.elm new file mode 100644 index 0000000..e661b9c --- /dev/null +++ b/src/battle/src/View/SubMenu.elm @@ -0,0 +1,85 @@ +module View.SubMenu exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Html +import Html.Attributes +import Html.Lazy + +-- Map ------------------------------------------------------------------- +import Struct.CharacterTurn +import Struct.Event +import Struct.Model +import Struct.UI + +import Util.Html + +import View.Controlled.CharacterCard + +import View.SubMenu.Characters +import View.SubMenu.Settings +import View.SubMenu.Status +import View.SubMenu.Timeline + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_inner_html : ( +      Struct.Model.Type -> +      Struct.UI.Tab -> +      (Html.Html Struct.Event.Type) +   ) +get_inner_html model tab = +   case tab of +      Struct.UI.StatusTab -> +         (View.SubMenu.Status.get_html model) + +      Struct.UI.CharactersTab -> +         (Html.Lazy.lazy2 +            (View.SubMenu.Characters.get_html) +            model.characters +            model.player_ix +         ) + +      Struct.UI.SettingsTab -> +         (View.SubMenu.Settings.get_html model) + +      Struct.UI.TimelineTab -> +         (View.SubMenu.Timeline.get_html model) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : Struct.Model.Type -> (Html.Html Struct.Event.Type) +get_html model = +   case (Struct.UI.try_getting_displayed_tab model.ui) of +      (Just tab) -> +         (Html.div +            [(Html.Attributes.class "battle-sub-menu")] +            [(get_inner_html model tab)] +         ) + +      Nothing -> +         case (Struct.CharacterTurn.try_getting_target model.char_turn) of +            (Just char_ref) -> +               case (Array.get char_ref model.characters) of +                  (Just char) -> +                     (Html.div +                        [(Html.Attributes.class "battle-sub-menu")] +                        [ +                           (Html.text "Targeting:"), +                           (Html.Lazy.lazy3 +                              (View.Controlled.CharacterCard.get_summary_html) +                              model.char_turn +                              model.player_ix +                              char +                           ) +                        ] +                     ) + +                  Nothing -> +                     (Util.Html.nothing) + +            Nothing -> +               (Util.Html.nothing) diff --git a/src/battle/src/View/SubMenu/Characters.elm b/src/battle/src/View/SubMenu/Characters.elm new file mode 100644 index 0000000..396dbee --- /dev/null +++ b/src/battle/src/View/SubMenu/Characters.elm @@ -0,0 +1,69 @@ +module View.SubMenu.Characters exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Array  + +import Html +import Html.Attributes +import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Character +import Struct.Event + +import View.Controlled.CharacterCard + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_character_element_html : ( +      Int -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_character_element_html player_ix char = +   (Html.div +      [ +         (Html.Attributes.class "battle-characters-element"), +         ( +            if (Struct.Character.is_alive char) +            then +               (Html.Attributes.class "clickable") +            else +               (Html.Attributes.class "") +         ), +         (Html.Events.onClick +            (Struct.Event.LookingForCharacter (Struct.Character.get_index char)) +         ), +         ( +            if (Struct.Character.is_enabled char) +            then +               (Html.Attributes.class "battle-characters-element-active") +            else +               (Html.Attributes.class "battle-characters-element-inactive") +         ) +      ] +      [ +         (View.Controlled.CharacterCard.get_minimal_html player_ix char) +      ] +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      (Array.Array Struct.Character.Type) -> +      Int -> +      (Html.Html Struct.Event.Type) +   ) +get_html characters player_ix = +   (Html.div +      [ +         (Html.Attributes.class "battle-tabmenu-content"), +         (Html.Attributes.class "battle-tabmenu-characters-tab") +      ] +      (List.map +         (get_character_element_html player_ix) +         (Array.toList characters) +      ) +   ) diff --git a/src/battle/src/View/SubMenu/Settings.elm b/src/battle/src/View/SubMenu/Settings.elm new file mode 100644 index 0000000..e0ad4d7 --- /dev/null +++ b/src/battle/src/View/SubMenu/Settings.elm @@ -0,0 +1,59 @@ +module View.SubMenu.Settings exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.Model + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +scale_button : Float -> String -> (Html.Html Struct.Event.Type) +scale_button mod label = +   (Html.button +      [ +         (Html.Events.onClick +            (Struct.Event.ScaleChangeRequested mod) +         ) +      ] +      [ (Html.text label) ] +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : Struct.Model.Type -> (Html.Html Struct.Event.Type) +get_html model = +   (Html.div +      [ +         (Html.Attributes.class "battle-tabmenu-content"), +         (Html.Attributes.class "battle-tabmenu-settings-tab") +      ] +      [ +         (scale_button (0.75) "Zoom -"), +         (scale_button 0 "Zoom Reset"), +         (scale_button (1.15) "Zoom +"), +         (Html.button +            [ +               (Html.Events.onClick Struct.Event.DebugTeamSwitchRequest) +            ] +            [ (Html.text "[DEBUG] Switch team") ] +         ), +         (Html.button +            [ +               (Html.Events.onClick Struct.Event.DebugLoadBattleRequest) +            ] +            [ (Html.text "[DEBUG] Load map") ] +         ), +         (Html.button +            [ +               (Html.Events.onClick Struct.Event.DebugTestAnimation) +            ] +            [ (Html.text "[DEBUG] Test animations") ] +         ) +      ] +   ) diff --git a/src/battle/src/View/SubMenu/Status.elm b/src/battle/src/View/SubMenu/Status.elm new file mode 100644 index 0000000..485704e --- /dev/null +++ b/src/battle/src/View/SubMenu/Status.elm @@ -0,0 +1,55 @@ +module View.SubMenu.Status exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Html +import Html.Attributes +import Html.Lazy + +-- Struct.Map ------------------------------------------------------------------- +import Struct.Event +import Struct.Location +import Struct.Model +import Struct.UI + +import View.SubMenu.Status.CharacterInfo +import View.SubMenu.Status.TileInfo +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : Struct.Model.Type -> (Html.Html Struct.Event.Type) +get_html model = +   (Html.div +      [ +         (Html.Attributes.class "battle-footer-tabmenu-content"), +         (Html.Attributes.class "battle-footer-tabmenu-content-status") +      ] +      [ +         (case (Struct.UI.get_previous_action model.ui) of +            (Just (Struct.UI.SelectedLocation loc)) -> +               (View.SubMenu.Status.TileInfo.get_html +                  model +                  (Struct.Location.from_ref loc) +               ) + +            (Just (Struct.UI.SelectedCharacter target_char)) -> +               case (Array.get target_char model.characters) of +                  (Just char) -> +                     (Html.Lazy.lazy2 +                        (View.SubMenu.Status.CharacterInfo.get_html) +                        model.player_ix +                        char +                     ) + +                  _ -> (Html.text "Error: Unknown character selected.") + +            _ -> +               (Html.text "Nothing is being focused.") +         ) +      ] +   ) diff --git a/src/battle/src/View/SubMenu/Status/CharacterInfo.elm b/src/battle/src/View/SubMenu/Status/CharacterInfo.elm new file mode 100644 index 0000000..814ce5f --- /dev/null +++ b/src/battle/src/View/SubMenu/Status/CharacterInfo.elm @@ -0,0 +1,34 @@ +module View.SubMenu.Status.CharacterInfo exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes + +-- Struct.Map ------------------------------------------------------------------- +import Struct.Character +import Struct.Event + +import View.Controlled.CharacterCard + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Int -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_html player_ix char = +   (Html.div +      [ +         (Html.Attributes.class "battle-tabmenu-character-info") +      ] +      [ +         (Html.text ("Focusing:")), +         (View.Controlled.CharacterCard.get_full_html player_ix char) +      ] +   ) diff --git a/src/battle/src/View/SubMenu/Status/TileInfo.elm b/src/battle/src/View/SubMenu/Status/TileInfo.elm new file mode 100644 index 0000000..7448247 --- /dev/null +++ b/src/battle/src/View/SubMenu/Status/TileInfo.elm @@ -0,0 +1,142 @@ +module View.SubMenu.Status.TileInfo exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Dict + +import Html +import Html.Attributes + +-- Struct.Map ------------------------------------------------------------------- +import Constants.IO +import Constants.Movement + +import Struct.Map +import Struct.Event +import Struct.Location +import Struct.Model +import Struct.Tile + +import Util.Html +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_icon : (Struct.Tile.Instance -> (Html.Html Struct.Event.Type)) +get_icon tile = +   (Html.div +      [ +         (Html.Attributes.class "battle-tile-card-icon"), +         (Html.Attributes.class +            ( +               "battle-tile-variant-" +               ++ (toString (Struct.Tile.get_variant_id tile)) +            ) +         ), +         (Html.Attributes.style +            [ +               ( +                  "background-image", +                  ( +                     "url(" +                     ++ Constants.IO.tile_assets_url +                     ++ (Struct.Tile.get_icon_id tile) +                     ++".svg)" +                  ) +               ) +            ] +         ) +      ] +      [ +      ] +   ) + +get_name : ( +      Struct.Model.Type -> +      Struct.Tile.Instance -> +      (Html.Html Struct.Event.Type) +   ) +get_name model tile = +   case (Dict.get (Struct.Tile.get_type_id tile) model.tiles) of +      Nothing -> (Util.Html.nothing) +      (Just tile_type) -> +         (Html.div +            [ +               (Html.Attributes.class "battle-tile-card-name") +            ] +            [ +               (Html.text (Struct.Tile.get_name tile_type)) +            ] +         ) + +get_cost : (Struct.Tile.Instance -> (Html.Html Struct.Event.Type)) +get_cost tile = +   let +      cost = (Struct.Tile.get_instance_cost tile) +      text = +         if (cost > Constants.Movement.max_points) +         then +            "Obstructed" +         else +            ("Cost: " ++ (toString cost)) +   in +      (Html.div +         [ +            (Html.Attributes.class "battle-tile-card-cost") +         ] +         [ +            (Html.text text) +         ] +      ) + +get_location : (Struct.Tile.Instance -> (Html.Html Struct.Event.Type)) +get_location tile = +   let +      tile_location = (Struct.Tile.get_location tile) +   in +      (Html.div +         [ +            (Html.Attributes.class "battle-tile-card-location") +         ] +         [ +            (Html.text +               ( +                  "{x: " +                  ++ (toString tile_location.x) +                  ++ "; y: " +                  ++ (toString tile_location.y) +                  ++ "}" +               ) +            ) +         ] +      ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.Model.Type -> +      Struct.Location.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_html model loc = +   case (Struct.Map.try_getting_tile_at loc model.map) of +      (Just tile) -> +         (Html.div +            [ +               (Html.Attributes.class "battle-tile-card") +            ] +            [ +               (get_name model tile), +               (Html.div +                  [ +                     (Html.Attributes.class "battle-tile-card-top") +                  ] +                  [ +                     (get_icon tile), +                     (get_location tile), +                     (get_cost tile) +                  ] +               ) +            ] +         ) + +      Nothing -> (Html.text "Error: Unknown tile location selected.") diff --git a/src/battle/src/View/SubMenu/Timeline.elm b/src/battle/src/View/SubMenu/Timeline.elm new file mode 100644 index 0000000..7fb1813 --- /dev/null +++ b/src/battle/src/View/SubMenu/Timeline.elm @@ -0,0 +1,95 @@ +module View.SubMenu.Timeline exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Html +import Html.Attributes +--import Html.Events +import Html.Lazy + +-- Map ------------------------------------------------------------------- +import Struct.Character +import Struct.Event +import Struct.TurnResult +import Struct.Model + +import View.SubMenu.Timeline.Attack +import View.SubMenu.Timeline.Movement +import View.SubMenu.Timeline.WeaponSwitch +import View.SubMenu.Timeline.PlayerVictory +import View.SubMenu.Timeline.PlayerDefeat +import View.SubMenu.Timeline.PlayerTurnStart + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_turn_result_html : ( +      (Array.Array Struct.Character.Type) -> +      Int -> +      Struct.TurnResult.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_turn_result_html characters player_ix turn_result = +   case turn_result of +      (Struct.TurnResult.Moved movement) -> +         (View.SubMenu.Timeline.Movement.get_html +            characters +            player_ix +            movement +         ) + +      (Struct.TurnResult.Attacked attack) -> +         (View.SubMenu.Timeline.Attack.get_html +            characters +            player_ix +            attack +         ) + +      (Struct.TurnResult.SwitchedWeapon weapon_switch) -> +         (View.SubMenu.Timeline.WeaponSwitch.get_html +            characters +            player_ix +            weapon_switch +         ) + +      (Struct.TurnResult.PlayerWon pvict) -> +         (View.SubMenu.Timeline.PlayerVictory.get_html pvict) + +      (Struct.TurnResult.PlayerLost pdefeat) -> +         (View.SubMenu.Timeline.PlayerDefeat.get_html pdefeat) + +      (Struct.TurnResult.PlayerTurnStarted pturns) -> +         (View.SubMenu.Timeline.PlayerTurnStart.get_html pturns) + +true_get_html : ( +      (Array.Array Struct.Character.Type) -> +      Int -> +      (Array.Array Struct.TurnResult.Type) -> +      (Html.Html Struct.Event.Type) +   ) +true_get_html characters player_ix turn_results = +   (Html.div +      [ +         (Html.Attributes.class "battle-tabmenu-content"), +         (Html.Attributes.class "battle-tabmenu-timeline-tab") +      ] +      (Array.toList +         (Array.map +            (get_turn_result_html characters player_ix) +            turn_results +         ) +      ) +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : Struct.Model.Type -> (Html.Html Struct.Event.Type) +get_html model = +   (Html.Lazy.lazy3 +      (true_get_html) +      model.characters +      model.player_ix +      model.timeline +   ) diff --git a/src/battle/src/View/SubMenu/Timeline/Attack.elm b/src/battle/src/View/SubMenu/Timeline/Attack.elm new file mode 100644 index 0000000..682540d --- /dev/null +++ b/src/battle/src/View/SubMenu/Timeline/Attack.elm @@ -0,0 +1,164 @@ +module View.SubMenu.Timeline.Attack exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Html +import Html.Attributes +--import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Attack +import Struct.Event +import Struct.TurnResult +import Struct.Character + +import View.Character + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_title_html : ( +      Struct.Character.Type -> +      Struct.Character.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_title_html attacker defender = +   (Html.div +      [ +         (Html.Attributes.class "battle-timeline-attack-title") +      ] +      [ +         (Html.text +            ( +               (Struct.Character.get_name attacker) +               ++ " attacked " +               ++ (Struct.Character.get_name defender) +               ++ "!" +            ) +         ) +      ] +   ) + +get_effect_text : Struct.Attack.Type -> String +get_effect_text attack = +   ( +      ( +         case attack.precision of +            Struct.Attack.Hit -> " hit for " +            Struct.Attack.Graze -> " grazed for " +            Struct.Attack.Miss -> " missed." +      ) +      ++ +      ( +         if (attack.precision == Struct.Attack.Miss) +         then +            "" +         else +            ( +               ((toString attack.damage) ++ " damage") +               ++ +               ( +                  if (attack.critical) +                  then " (Critical Hit)." +                  else "." +               ) +            ) +      ) +   ) + +get_attack_html : ( +      Struct.Character.Type -> +      Struct.Character.Type -> +      Struct.Attack.Type -> +      (Html.Html Struct.Event.Type) +   ) +get_attack_html attacker defender attack = +   let +      attacker_name = (Struct.Character.get_name attacker) +      defender_name = (Struct.Character.get_name defender) +   in +   (Html.div +      [] +      [ +         (Html.text +            ( +               case (attack.order, attack.parried) of +                  (Struct.Attack.Counter, True) -> +                     ( +                        defender_name +                        ++ " attempted to strike back, but " +                        ++ attacker_name +                        ++ " parried, and " +                        ++ (get_effect_text attack) +                     ) + +                  (Struct.Attack.Counter, _) -> +                     ( +                        defender_name +                        ++ " striked back, and " +                        ++ (get_effect_text attack) +                     ) + +                  (_, True) -> +                     ( +                        attacker_name +                        ++ " attempted a hit, but " +                        ++ defender_name +                        ++ " parried, and " +                        ++ (get_effect_text attack) +                     ) + +                  (_, _) -> +                     (attacker_name ++ " " ++ (get_effect_text attack)) +            ) +         ) +      ] +   ) + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      (Array.Array Struct.Character.Type) -> +      Int -> +      Struct.TurnResult.Attack -> +      (Html.Html Struct.Event.Type) +   ) +get_html characters player_ix attack = +   case +      ( +         (Array.get attack.attacker_index characters), +         (Array.get attack.defender_index characters) +      ) +   of +      ((Just atkchar), (Just defchar)) -> +         (Html.div +            [ +               (Html.Attributes.class "battle-timeline-element"), +               (Html.Attributes.class "battle-timeline-attack") +            ] +            ( +               [ +                  (View.Character.get_portrait_html player_ix atkchar), +                  (View.Character.get_portrait_html player_ix defchar), +                  (get_title_html atkchar defchar) +               ] +               ++ +               (List.map +                  (get_attack_html atkchar defchar) +                  attack.sequence +               ) +            ) +         ) + +      _ -> +         (Html.div +            [ +               (Html.Attributes.class "battle-timeline-element"), +               (Html.Attributes.class "battle-timeline-attack") +            ] +            [ +               (Html.text "Error: Attack with unknown characters") +            ] +         ) diff --git a/src/battle/src/View/SubMenu/Timeline/Movement.elm b/src/battle/src/View/SubMenu/Timeline/Movement.elm new file mode 100644 index 0000000..0746f1f --- /dev/null +++ b/src/battle/src/View/SubMenu/Timeline/Movement.elm @@ -0,0 +1,62 @@ +module View.SubMenu.Timeline.Movement exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Html +import Html.Attributes +--import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.TurnResult +import Struct.Character + +import View.Character + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      (Array.Array Struct.Character.Type) -> +      Int -> +      Struct.TurnResult.Movement -> +      (Html.Html Struct.Event.Type) +   ) +get_html characters player_ix movement = +   case (Array.get movement.character_index characters) of +      (Just char) -> +         (Html.div +            [ +               (Html.Attributes.class "battle-timeline-element"), +               (Html.Attributes.class "battle-timeline-movement") +            ] +            [ +               (View.Character.get_portrait_html player_ix char), +               (Html.text +                  ( +                     (Struct.Character.get_name char) +                     ++ " moved to (" +                     ++ (toString movement.destination.x) +                     ++ ", " +                     ++ (toString movement.destination.y) +                     ++ ")." +                  ) +               ) +            ] +         ) + +      _ -> +         (Html.div +            [ +               (Html.Attributes.class "battle-timeline-element"), +               (Html.Attributes.class "battle-timeline-movement") +            ] +            [ +               (Html.text "Error: Moving with unknown character") +            ] +         ) diff --git a/src/battle/src/View/SubMenu/Timeline/PlayerDefeat.elm b/src/battle/src/View/SubMenu/Timeline/PlayerDefeat.elm new file mode 100644 index 0000000..db5e023 --- /dev/null +++ b/src/battle/src/View/SubMenu/Timeline/PlayerDefeat.elm @@ -0,0 +1,38 @@ +module View.SubMenu.Timeline.PlayerDefeat exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +--import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.TurnResult + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.TurnResult.PlayerDefeat -> +      (Html.Html Struct.Event.Type) +   ) +get_html pdefeat = +   (Html.div +      [ +         (Html.Attributes.class "battle-timeline-element"), +         (Html.Attributes.class "battle-timeline-player-defeat") +      ] +      [ +         (Html.text +            ( +               "Player " +               ++ (toString pdefeat.player_index) +               ++ " has been eliminated." +            ) +         ) +      ] +   ) diff --git a/src/battle/src/View/SubMenu/Timeline/PlayerTurnStart.elm b/src/battle/src/View/SubMenu/Timeline/PlayerTurnStart.elm new file mode 100644 index 0000000..a6486fa --- /dev/null +++ b/src/battle/src/View/SubMenu/Timeline/PlayerTurnStart.elm @@ -0,0 +1,38 @@ +module View.SubMenu.Timeline.PlayerTurnStart exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +--import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.TurnResult + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.TurnResult.PlayerTurnStart -> +      (Html.Html Struct.Event.Type) +   ) +get_html pturns = +   (Html.div +      [ +         (Html.Attributes.class "battle-timeline-element"), +         (Html.Attributes.class "battle-timeline-turn-start") +      ] +      [ +         (Html.text +            ( +               "Player " +               ++ (toString pturns.player_index) +               ++ "'s turn has started." +            ) +         ) +      ] +   ) diff --git a/src/battle/src/View/SubMenu/Timeline/PlayerVictory.elm b/src/battle/src/View/SubMenu/Timeline/PlayerVictory.elm new file mode 100644 index 0000000..4d47f62 --- /dev/null +++ b/src/battle/src/View/SubMenu/Timeline/PlayerVictory.elm @@ -0,0 +1,38 @@ +module View.SubMenu.Timeline.PlayerVictory exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Html +import Html.Attributes +--import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.TurnResult + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      Struct.TurnResult.PlayerVictory -> +      (Html.Html Struct.Event.Type) +   ) +get_html pvict = +   (Html.div +      [ +         (Html.Attributes.class "battle-timeline-element"), +         (Html.Attributes.class "battle-timeline-player-victory") +      ] +      [ +         (Html.text +            ( +               "Player " +               ++ (toString pvict.player_index) +               ++ " has won the map." +            ) +         ) +      ] +   ) diff --git a/src/battle/src/View/SubMenu/Timeline/WeaponSwitch.elm b/src/battle/src/View/SubMenu/Timeline/WeaponSwitch.elm new file mode 100644 index 0000000..499e0c3 --- /dev/null +++ b/src/battle/src/View/SubMenu/Timeline/WeaponSwitch.elm @@ -0,0 +1,58 @@ +module View.SubMenu.Timeline.WeaponSwitch exposing (get_html) + +-- Elm ------------------------------------------------------------------------- +import Array + +import Html +import Html.Attributes +--import Html.Events + +-- Map ------------------------------------------------------------------- +import Struct.Event +import Struct.TurnResult +import Struct.Character + +import View.Character + +-------------------------------------------------------------------------------- +-- LOCAL ----------------------------------------------------------------------- +-------------------------------------------------------------------------------- + +-------------------------------------------------------------------------------- +-- EXPORTED -------------------------------------------------------------------- +-------------------------------------------------------------------------------- +get_html : ( +      (Array.Array Struct.Character.Type) -> +      Int -> +      Struct.TurnResult.WeaponSwitch -> +      (Html.Html Struct.Event.Type) +   ) +get_html characters player_ix weapon_switch = +   case (Array.get weapon_switch.character_index characters) of +      (Just char) -> +         (Html.div +            [ +               (Html.Attributes.class "battle-timeline-element"), +               (Html.Attributes.class "battle-timeline-weapon-switch") +            ] +            [ +               (View.Character.get_portrait_html player_ix char), +               (Html.text +                  ( +                     (Struct.Character.get_name char) +                     ++ " switched weapons." +                  ) +               ) +            ] +         ) + +      _ -> +         (Html.div +            [ +               (Html.Attributes.class "battle-timeline-element"), +               (Html.Attributes.class "battle-timeline-weapon-switch") +            ] +            [ +               (Html.text "Error: Unknown character switched weapons") +            ] +         ) diff --git a/src/battle/www/index.html b/src/battle/www/index.html new file mode 100644 index 0000000..16c2e21 --- /dev/null +++ b/src/battle/www/index.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> +   <head> +      <link rel="stylesheet" type="text/css" href="../global/style.css"> +      <link rel="stylesheet" type="text/css" href="../battle/style.css"> +      <link rel="stylesheet" type="text/css" href="../asset/characters.css"> +      <link rel="stylesheet" type="text/css" href="../asset/armors.css"> +      <link rel="icon" type="image/x-icon" href="/favicon.ico"> +   </head> +   <body> +      <script src="script/main.js"></script> +      <script src="../global/script/session.js"></script> +      <script src="../global/script/urlparams.js"></script> +      <script> +         tacticians_online.session.load(); + +         tacticians_online.app = +            Elm.Main.fullscreen +            ( +               { +                  user_id: tacticians_online.session.get_user_id(), +                  token: tacticians_online.session.get_token(), +                  url_params: tacticians_online.urlparams.get_parameters() +               } +            ); + +         tacticians_online.session.attach_to(tacticians_online.app); +      </script> +   </body> +</html> diff --git a/src/battle/www/style.css b/src/battle/www/style.css new file mode 100644 index 0000000..af30c76 --- /dev/null +++ b/src/battle/www/style.css @@ -0,0 +1,1059 @@ +/******************************************************************************/ +/** LAYOUT ********************************************************************/ +/******************************************************************************/ +.fullscreen-module {} + + +.battle-main-menu +{ +   position: absolute; +   top: 0; +   left: 0; +   right: 0; +   height: 3em; + +   display: flex; + +   flex-direction: row; +   flex-wrap: wrap; + +   border: 3px solid #502D16; +   border-top: none; +   border-radius: 0 0 15px 15px; + +   padding: 0.5em; + +   background-color: #917C6F; + +   margin: 0 1em 0 1em; +} + +.battle-message-board +{ +   position: absolute; +   bottom: 0; +   left: 0; +   right: 0; + +   height: 10em; + +   border: 3px solid #502D16; +   border-radius: 15px 15px 0 0; +   border-bottom: none; + +   padding: 0.5em; +   margin: 0 1em 0 1em; + +   background-color: #917C6F; + +   display: flex; +   flex-flow: row; +   justify-content: space-between; +} + +.battle-container-centerer +{ +   position: absolute; +   top: 4em; +   left: 16em; +   right: 21em; +   bottom: 11em; + +   display: flex; +} + +.battle-container +{ +   display: inline-block; +   max-height: 100%; +   max-width: 100%; +   /* +    * 4em: main-menu + margin. +    * 11em: message-board + margin. +    */ +   /*margin: 0 1em 0 1em; */ +   overflow: scroll; + +   margin: auto; +   resize: both; + +   border: 3px solid #502D16; +   border-radius: 15px; +} + +.battle-controlled +{ +   position: absolute; +   left: 0; +   top: 4em; +   width: 15em; +   /* +    * 4em: main-menu + margin. +    * 11em: message-board + margin. +    */ +   height: calc(100% - 11em - 4em); + +   display: flex; +   flex-flow: column; + +   justify-content: space-between; + +   padding: 0.5em; + +   border: 3px solid #502D16; +   border-radius: 0 15px 15px 0; +   border-left: none; + +   background-color: #917C6F; +} + +.battle-sub-menu +{ +   position: absolute; +   right: 0; +   top: 4em; +   width: 20em; +   /* +    * 4em: main-menu + margin. +    * 11em: message-board + margin. +    */ +   height: calc(100% - 11em - 4em); +   padding: 0.5em; +   overflow: auto; + +   border: 3px solid #502D16; +   border-radius: 15px 0 0 15px; +   border-right: none; + +   background-color: #917C6F; +} + +/******************************************************************************/ +/** MESSAGE BOARD *************************************************************/ +/******************************************************************************/ +.battle-error +{ +   background-color: #550000; +} + +.battle-message-board .battle-character-card +{ +   width: 16em; +} + +.battle-message-attack-text +{ +   width: 100%; +   text-align: center; +   font-size: 1.2em; +} + +.battle-message-board-help +{ +   display: block; +} + +.battle-message-board-help h1 +{ +   margin: 0; +   margin-bottom: 0.3em; +   font-size: 1.5em; +   text-align: center; +} + +.battle-message-board-help-figure +{ +   width: 1.5em; +   height: 1.5em; +   background-size: 100%; +   display: inline-block; +   vertical-align: middle; +} + +.battle-help-guide-icon +{ +   margin-right: 0.5em; +   width: 1.5em; +   height: 1.5em; +   background-image: url("/asset/svg/help-icon.svg"); +   background-size: 100%; +   display: inline-block; +   vertical-align: middle; +} + +/******************************************************************************/ +/** CONTROLLED PANEL **********************************************************/ +/******************************************************************************/ +.battle-controlled-actions +{ +   display: flex; +   flex-flow: row wrap; + +   align-items: center; +   justify-content: center; +} + +.battle-end-turn-button +{ +   animation-name: reverse-brown-alarm-bg; +   animation-duration: 2s; +   animation-iteration-count: infinite; +} + +/** Character Card ************************************************************/ +.battle-character-card, +.battle-tile-card +{ +   display: flex; +   flex-flow: column; +} + +.battle-character-card-top, +.battle-tile-card-top +{ +   margin-top: 0.5em; +   position: relative; +} + +.battle-character-portrait +{ +   box-sizing: border-box; +   border-radius: 5px; +   background-size: 100% 100%; +   width: 100px; +   height: 100px; +   overflow: hidden; +} + +.battle-character-portrait +{ +   cursor: pointer; +} + +.battle-tile-card-icon +{ +   box-sizing: border-box; +   border-radius: 5px; +   background-size: 300% 300%; +   width: 80px; +   height: 80px; +} + +.battle-character-portrait * +{ +   box-sizing: border-box; +   background-size: 100% 100%; +   width: inherit; +   height: inherit; +} + +.battle-character-portrait-body +{ +   z-index: 1; +} + +.battle-character-portrait-armor +{ +   position: relative; +   z-index: 1; +   top: -100%; +   background-size: 200% 100%; +} + +.battle-character-card .battle-character-portrait, +.battle-tile-card-icon +{ +   top: 0; +   left: 0; +   margin: 0; +   box-sizing: border-box; +   box-shadow: +      1px 0px 2px #333, +      -1px 0px 2px #333, +      0px 1px 2px #333, +      0px -1px 2px #333; +} + +.battle-character-portrait-team-0 { background-color: rgba(57, 106, 177, 0.3); } +.battle-character-portrait-team-1 { background-color: rgba(204, 37, 41, 0.3); } +.battle-character-portrait-team-2 { background-color: rgba(62, 150, 81, 0.3); } +.battle-character-portrait-team-3 { background-color: rgba(218, 124, 48, 0.3); } +.battle-character-portrait-team-4 { background-color: rgba(83, 81, 84, 0.3); } +.battle-character-portrait-team-5 { background-color: rgba(107, 76, 154, 0.3); } +.battle-character-portrait-team-6 { background-color: rgba(127, 167, 169, 0.3); } +.battle-character-portrait-team-7 { background-color: rgba(231, 167, 169, 0.3); } + +.battle-tile-card-top +{ +   margin:0.3em; +} + +.battle-character-card-name, +.battle-tile-card-name, +.battle-tile-card-cost, +.battle-tile-card-location +{ +   display:flex; +   justify-content:center; +   align-items:center; +   border-radius: 5px; +   background-color: #6C5D53; +   width: 100%; +} + +.battle-gauge +{ +   position: relative; +   border-radius: 5px; +   border: 2px solid #6C5D53; +   text-align: center; +   height: 2em; +} + +.battle-gauge-text +{ +   position: absolute; +   top: 0; +   left: 0; +   height: 100%; +   width: 100%; +   z-index: 1; + +   display: flex; +   flex-direction: column; +   align-items: center; +   justify-content: center; +} + +.battle-gauge-bar +{ +   position: absolute; +   top: 0; +   left: 0; +   height: 100%; +   width: 100%; +   z-index: 0; + +   border-radius: 5px; +   z-index: 0; +   transition: width 3s ease-in-out; +} + +.battle-character-card-health, +.battle-tile-card-cost +{ +   position: absolute; +   left: 100px; +   top: 0; +   margin-left: 0.5em; +   width: calc(100% - 100px - 0.5em); +} + + +.battle-character-card-health > .battle-gauge-bar +{ +   background-color: darkred; +} + +.battle-character-card-movement, +.battle-tile-card-location +{ +   position: absolute; +   left: 100px; +   top: calc(1.5em + 1em); +   margin-left: 0.5em; +   width: calc(100% - 100px - 0.5em); +} + +.battle-character-card-statuses +{ +   position: absolute; +   left: 100px; +   top: calc(2*(1.5em + 0.5em) + 0.7em); +   margin-left: 0.5em; +   width: calc(100% - 100px - 0.5em); +   display: flex; +} + +.battle-character-card-status +{ +   height: 1.5em; +   width: 1.5em; +   background-size: 100%; +} + +.battle-character-card-target-status +{ +   background-image: url("/asset/svg/status/target.svg"); +} + +.battle-character-card-commander-status +{ +   background-image: url("/asset/svg/status/commander.svg"); +} + +.battle-character-card-movement > .battle-gauge-bar +{ +   background-color: darkgrey; +} + +.battle-character-card-weapon, +.battle-character-card-weapon-summary +{ +   display: grid; + +   border-radius: 5px; + +   padding: 0.3em; +   margin-top: 0.3em; +} + +.battle-character-card-weapon +{ +   background-color: #6C5D53; +} + +.battle-character-card-weapon-summary +{ +   background-color: #393939; +} + +.battle-character-card-armor +{ +   display: grid; +   border-radius: 5px; +   background-color: #6C5D53; + +   padding: 0.3em; +   margin-top: 0.3em; +} + +.battle-character-card-armor-stats +{ +   display: grid; +   grid-template-columns: [col] 25% [col] 25% [col] 25% [col] 25%; +} + +.battle-character-card-stats +{ +   display: grid; +   grid-template-columns: [col] 25% [col] 25% [col] 25% [col] 25%; + +   border-radius: 5px; +   background-color: #6C5D53; + +   padding: 0.3em; +   margin-top: 0.3em; +} + +/** Manual Controls ***********************************************************/ +.battle-manual-controls +{ +   width: 96px; +   height: 96px; +   display: grid; +   grid-template: +      '.    top .' +      'left  go  right' +      '.   bottom .'; +   margin: auto; +} + +.battle-manual-controls > div +{ +   width: 32px; +   height: 32px; +   background-image: url(/asset/svg/arrowhead.svg); +   background-size: 100%; +   transition: opacity 0.3s ease-out; +   opacity: 0.5; +} + +.battle-manual-controls > div:hover +{ +   opacity: 1; +} + +.battle-manual-controls-go +{ +   margin: auto; +   width: 28px; +   height: 28px; +   border-radius: 100em; +   grid-area: go; +} + +.battle-manual-controls-up +{ +   transform: rotate(-90deg); +   grid-area: top; +} + +.battle-manual-controls-down +{ +   transform: rotate(90deg); +   grid-area: bottom; +} + +.battle-manual-controls-left +{ +   transform: rotate(180deg); +   grid-area: left; +} + +.battle-manual-controls-right +{ +   grid-area: right; +} + +/******************************************************************************/ +/** MAIN MENU *****************************************************************/ +/******************************************************************************/ +.battle-main-menu +{ +   display: flex; +   flex-direction: row; +   flex-wrap: wrap; +   justify-content: space-between; +} + +.battle-main-menu button +{ +   flex: 1; + +   text-transform: uppercase; +} + +/******************************************************************************/ +/** SUB-MENU ******************************************************************/ +/******************************************************************************/ +.battle-tabmenu-characters-tab +{ +   display: flex; +   flex-grow: 1; +   flex-direction: column; +   flex-wrap: wrap; +} + +.battle-characters-element-active +{ +   animation-name: brown-alarm-bg; +   animation-duration: 5s; +   animation-iteration-count: infinite; +} + + + +.battle-timeline-element, +.battle-characters-element +{ +   flex: 2; +   margin: 0.5em 0.5em 0 0.5em; +   text-align: center; +   border-radius: 5px; +   border: 1px solid #502D16; +   border-bottom-width: 2px; +   padding: 0.5em; +} + +.battle-timeline-element .battle-character-portrait +{ +   display: inline-block; +   vertical-align: middle; +   width: 36px; +   height: 36px; +} + +/******************************************************************************/ +/** Main View Elements ********************************************************/ +/******************************************************************************/ +.battle-actual +{ +   display: inline-block; +   transform-origin: top left; + +   /*** Otherwise, it won't display correctly without 'transform: scale' ***/ +   position: relative; +   background-color: #917C6F; +} + +.battle-tiled +{ +   height: 32px; +   width: 32px; +   /** Fixes odd behavior of table cell being resized. **/ +   /* min-width: 32px; */ +   /* max-width: 32px; */ +} + +.battle-tile-variant-0  {background-position: 0    0;} +.battle-tile-variant-1  {background-position: 100% 0;} +.battle-tile-variant-2  {background-position: 200% 0;} +.battle-tile-variant-3  {background-position: 0    100%;} +.battle-tile-variant-4  {background-position: 100% 100%;} +.battle-tile-variant-5  {background-position: 200% 100%;} +.battle-tile-variant-6  {background-position: 0    200%;} +.battle-tile-variant-7  {background-position: 100% 200%;} +.battle-tile-variant-8  {background-position: 200% 200%;} + +.battle-tile-icon       {z-index: 0; position: absolute; background-size: 300%;} +.battle-path-icon-below-markers {z-index: 1;} +.battle-marker-icon     {z-index: 2;} +.battle-path-icon-above-markers {z-index: 3;} +.battle-character-icon  {z-index: 4;} + +.battle-marker-icon, +.battle-character-icon, +.battle-path-icon +{ +   position: absolute; +} +.battle-tiles-layer +{ +   /*display: table; */ +} + +.battle-tiles-layer-row +{ +  /* display: table-row; */ +} + +.battle-character-icon +{ +   border-radius: 5px; +   height: 38px; +   margin-top: -6px; +} + +.battle-character-icon * +{ +   position: absolute; +   left: 0; +   top: 0; +   background-size: 100% 100%; +   width: inherit; +   height: inherit; +} + +.battle-character-icon-enabled +{ +   animation-name: pulsating; +   animation-duration: 1.5s; +   animation-iteration-count: infinite; +   transform-origin: center; +} + +.battle-character-icon-banner { z-index: 2; } +.battle-character-icon-head { z-index: 1; } +.battle-character-icon-body { z-index: 0; } + +.battle-character-ally +{ +} + +.battle-character-enemy +{ +   transform: scale(-1, 1); +} + +/** Navigator Markers *********************************************************/ +.battle-marker-icon.battle-navigator-non-interactive +{ +   box-sizing: border-box; +   width: 12px; +   height: 12px; +   background-size: 100%; +   margin: 10px; +} + +.battle-marker-icon.battle-navigator-interactive +{ +   box-sizing: border-box; +   width: 24px; +   /*min-width: 24px; +   max-width: 24px; */ +   margin: 4px 0 0 4px; +   height: 24px; +   border-radius: 4px; +   border: none; +   box-shadow: +      1px 0px 2px #333, +      -1px 0px 2px #333, +      0px 1px 2px #333, +      0px -1px 2px #333; +} + +.battle-can-go-to-can-defend-marker.battle-navigator-interactive +{ +   background-color: #FFF; +   opacity: 0.3; +   transition: opacity 0.3s ease-out; +} + +.battle-can-go-to-cant-defend-marker.battle-navigator-interactive +{ +   background: +      repeating-linear-gradient( +        -55deg, +        rgb(255,255,255), +        rgb(255,255,255) 3px, +        rgba(0,0,0,0) 3px, +        rgba(0,0,0,0) 7px +      ); +   opacity: 0.3; +   transition: opacity 0.3s ease-out; +} + +.battle-can-go-to-can-defend-marker.battle-navigator-interactive:hover, +.battle-can-go-to-cant-defend-marker.battle-navigator-interactive:hover +{ +   opacity: 0.9; +} + +.battle-can-attack-can-defend-marker.battle-navigator-interactive +{ +   background-color: #000; +   opacity: 0.7; +   width: 28px; +   /*min-width: 28px; +   max-width: 28px;*/ +   height: 28px; +   margin: 2px 0 0 2px; +   border-radius: 0; +} + +.battle-can-attack-cant-defend-marker.battle-navigator-interactive +{ +   background: +      repeating-linear-gradient( +        -55deg, +        rgba(255,255,255,0), +        rgba(255,255,255,0) 3px, +        rgb(0,0,0) 3px, +        rgb(0,0,0) 7px +      ); +   width: 28px; +   /*min-width: 28px; +   max-width: 28px;*/ +   opacity: 0.7; +   height: 28px; +   margin: 2px 0 0 2px; +   border-radius: 0; +} + +.battle-can-attack-can-defend-marker.battle-navigator-non-interactive +{ +   z-index: 5; +   background-image: url(/asset/svg/marker/blade.svg); +} + +.battle-can-attack-cant-defend-marker.battle-navigator-non-interactive +{ +   z-index: 5; +   background-image: +      url(/asset/svg/marker/blade.svg), +      url(/asset/svg/marker/brokenshield.svg); +} + +.battle-can-go-to-cant-defend-marker.battle-navigator-non-interactive +{ +   background-image: +      url(/asset/svg/marker/pawprint.svg), +      url(/asset/svg/marker/brokenshield.svg); +} + +.battle-can-attack-cant-defend-marker.battle-navigator-non-interactive, +.battle-can-go-to-cant-defend-marker.battle-navigator-non-interactive +{ +   width: 24px; +   height: 24px; +   background-position: top left, bottom right; +   background-size: 50%, 50%; +   background-repeat: no-repeat, no-repeat; +   margin: 2px; +} + +.battle-can-go-to-can-defend-marker.battle-navigator-non-interactive +{ +   background-image: url(/asset/svg/marker/pawprint.svg); +} + +.battle-can-attack-can-defend-marker.battle-navigator-non-interactive, +.battle-can-attack-cant-defend-marker.battle-navigator-non-interactive +{ +   z-index: 5; +} + +.battle-can-go-to-can-defend-marker, +.battle-can-go-to-cant-defend-marker +{ +   z-index: 3; +} + +.battle-cant-defend-marker +{ +   background: +      repeating-linear-gradient( +        -55deg, +        rgba(255,255,255,0.3), +        rgba(255,255,255,0.3) 3px, +        rgba(0,0,0,0.3) 3px, +        rgba(0,0,0,0.3) 7px +      ); +} + +.battle-character-ally.battle-character-icon-disabled +{ +   filter: contrast(35%); +} + + +/**** Selection Classes *******************************************************/ +.battle-character-targeted +{ +   background-color: rgba(255,0,0,0.7); +   animation-name: red-alarm-bg; +   animation-duration: 5s; +   animation-iteration-count: infinite; +} + +.battle-character-selected +{ +   animation-name: strongly-pulsating; +   animation-duration: 1.5s; +   animation-iteration-count: infinite; +} + +/**** Path Icons **************************************************************/ +.battle-path-icon +{ +   background-image: url("/asset/svg/arrows.svg"); +   background-size: 96px 32px; +} + +.battle-path-icon-UR, +.battle-path-icon-LD, +.battle-path-icon-RD, +.battle-path-icon-UL, +.battle-path-icon-LU, +.battle-path-icon-DR, +.battle-path-icon-DL, +.battle-path-icon-RU +{ +   /*** Default is -^ ***/ +   background-position: 64px 0; +} +/*** DL/RU ***/ + +.battle-path-icon-LU, +.battle-path-icon-DR +{ +   transform: rotate(90deg); +} + +.battle-path-icon-RD, +.battle-path-icon-UL +{ +   /***  ***/ +   transform: scale(1, -1); +} + +.battle-path-icon-UR, +.battle-path-icon-LD +{ +   /*** <| ***/ +   transform: rotate(180deg); +} + +.battle-path-icon-RR, +.battle-path-icon-LR, +.battle-path-icon-RL, +.battle-path-icon-LL +{ +   background-position: 0 0; +} + +.battle-path-icon-markN, +.battle-path-icon-NU, +.battle-path-icon-ND, +.battle-path-icon-NL, +.battle-path-icon-NR +{ +   display: none; +} + +.battle-path-icon-UU, +.battle-path-icon-UD, +.battle-path-icon-DU, +.battle-path-icon-DD +{ +   background-position: 0 0; +   transform: rotate(90deg); +} + +.battle-path-icon-markR, +.battle-path-icon-markL, +.battle-path-icon-markU, +.battle-path-icon-markD +{ +   background-position: 32px 0; +} + +.battle-path-icon-markL +{ +   transform: rotate(180deg); +} + +.battle-path-icon-markD +{ +   transform: rotate(90deg); +} + +.battle-path-icon-markU +{ +   transform: rotate(-90deg); +} + +/******************************************************************************/ +/** Animations ****************************************************************/ +/******************************************************************************/ +@keyframes red-alarm-bg { +    0% {background-color: rgba(255,0,0,0.25);} +    25% {background-color: rgba(255,0,0,1);} +    50% {background-color: rgba(255,0,0,1);} +    100% {background-color: rgba(255,0,0,0.25);} +} + +@keyframes pulsating { +    0% { opacity: 1.0; transform: scale(1);} +    25% { opacity: 1.0; transform: scale(1);} +    30% { opacity: 0.8; transform: scale(1.1);} +    50% { opacity: 1.0; transform: scale(1);} +    100% { opacity: 1.0; transform: scale(1);} +} + +@keyframes strongly-pulsating { +    0% { opacity: 1.0; transform: scale(1);} +    50% { opacity: 0.8; transform: scale(1.5);} +    100% { opacity: 1.0; transform: scale(1);} +} + +@keyframes brown-alarm-bg { +    0% {background-color: #917C6F;} +    25% {background-color: #AC9D93} +    50% {background-color: #AC9D93} +    100% {background-color: #917C6F;} +} + +@keyframes reverse-brown-alarm-bg { +    0% {background-color: #917C6F;} +    50% {background-color: #502D16;} +    100% {background-color: #917C6F;} +} + +@keyframes blue-alarm-bg { +    0% {background-color: rgba(0,0,255,0.2);} +    25% {background-color: rgba(0,0,255,0.8);} +    50% {background-color: rgba(0,0,255,0.8);} +    100% {background-color: rgba(0,0,255,0.2);} +} + +@keyframes blinking { +   to { visibility: hidden; } +} + +@keyframes blinking2 { +   from { opacity: 1; } +   to { opacity: 0; } +} + +@keyframes dodges { +   0% { transform: translate(0, 0); } +   50% { transform: translate(-75%, 0); } +   100% { transform: translate(0, 0); } +} + +@keyframes attacks { +   0% { transform: translate(0, 0); } +   25% { transform: translate(25%, 0); } +   100% { transform: translate(0, 0); } +} + +@keyframes parries { +   0% { transform: translate(0, 0); } +   25% { transform: translate(-25%, 0); } +   50% { transform: translate(50%, 20%); } +   100% { transform: translate(0, 0); } +} + +@keyframes dies { +   from { transform: translate(0, 0) rotate(0); } +   to { transform: translate(0, 100%) rotate(25deg); } +} + +@keyframes blue-alarm-bd { +    0% {border-color: rgba(0,0,255,0.25);} +    25% {border-color: rgba(0,0,255,1);} +    100% {border-color: rgba(0,0,255,0.25);} +} + +/******************************************************************************/ +/** Timeline Animations *******************************************************/ +/******************************************************************************/ +/**** Character Icon Animations ***********************************************/ +.battle-animated-character-icon +{ +   transition: top linear 0.3s, left linear 0.3s; +} + +/**** Character Portrait Animations *******************************************/ +.battle-animated-portrait-damaged .battle-character-portrait > * +{ +   animation: blinking 0.2s steps(2, start) 8; +} + +.battle-animated-portrait-absent .battle-character-portrait > * +{ +   visibility: hidden; +} + +.battle-animated-portrait-dodges .battle-character-portrait, +.battle-animated-portrait-dies .battle-character-portrait, +.battle-animated-portrait-parries .battle-character-portrait, +.battle-animated-portrait-attacks .battle-character-portrait +{ +   overflow: hidden; +} + +.battle-animated-portrait +{ +} + +.battle-animated-portrait-dodges .battle-character-portrait > * +{ +   animation-name: dodges; +   animation-duration: 1s; +   animation-iteration-count: 1; +} + +.battle-animated-portrait-attacks .battle-character-portrait > * +{ +   animation-name: attacks; +   animation-duration: 1s; +   animation-iteration-count: 1; +} + +.battle-animated-portrait-dies .battle-character-portrait > * +{ +   animation-name: blinking2, dies; +   animation-duration: 0.15s, 2s; +   animation-delay: 0s, 1s; +   animation-iteration-count: 8, 1; +} + +.battle-animated-portrait-parries .battle-character-portrait > * +{ +   animation-name: parries; +   animation-duration: 1s; +   animation-iteration-count: 1; +} | 


