summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/battle')
-rw-r--r--src/battle/Makefile36
-rw-r--r--src/battle/elm-package.json19
-rw-r--r--src/battle/src/Action/Scroll.elm62
-rw-r--r--src/battle/src/Comm/AddArmor.elm24
-rw-r--r--src/battle/src/Comm/AddChar.elm28
-rw-r--r--src/battle/src/Comm/AddTile.elm24
-rw-r--r--src/battle/src/Comm/AddWeapon.elm24
-rw-r--r--src/battle/src/Comm/CharacterTurn.elm129
-rw-r--r--src/battle/src/Comm/LoadBattle.elm42
-rw-r--r--src/battle/src/Comm/Send.elm76
-rw-r--r--src/battle/src/Comm/SetMap.elm62
-rw-r--r--src/battle/src/Comm/SetTimeline.elm27
-rw-r--r--src/battle/src/Comm/TurnResults.elm28
-rw-r--r--src/battle/src/Constants/IO.elm.m416
-rw-r--r--src/battle/src/Constants/Movement.elm10
-rw-r--r--src/battle/src/Constants/UI.elm16
-rw-r--r--src/battle/src/ElmModule/Init.elm28
-rw-r--r--src/battle/src/ElmModule/Subscriptions.elm17
-rw-r--r--src/battle/src/ElmModule/Update.elm106
-rw-r--r--src/battle/src/ElmModule/View.elm57
-rw-r--r--src/battle/src/Main.elm23
-rw-r--r--src/battle/src/Struct/Armor.elm177
-rw-r--r--src/battle/src/Struct/Attack.elm148
-rw-r--r--src/battle/src/Struct/Attributes.elm134
-rw-r--r--src/battle/src/Struct/Character.elm238
-rw-r--r--src/battle/src/Struct/CharacterTurn.elm134
-rw-r--r--src/battle/src/Struct/Direction.elm52
-rw-r--r--src/battle/src/Struct/Error.elm45
-rw-r--r--src/battle/src/Struct/Event.elm44
-rw-r--r--src/battle/src/Struct/Flags.elm42
-rw-r--r--src/battle/src/Struct/HelpRequest.elm13
-rw-r--r--src/battle/src/Struct/Location.elm59
-rw-r--r--src/battle/src/Struct/Map.elm123
-rw-r--r--src/battle/src/Struct/Marker.elm10
-rw-r--r--src/battle/src/Struct/Model.elm254
-rw-r--r--src/battle/src/Struct/Navigator.elm192
-rw-r--r--src/battle/src/Struct/Path.elm175
-rw-r--r--src/battle/src/Struct/RangeIndicator.elm344
-rw-r--r--src/battle/src/Struct/ServerReply.elm33
-rw-r--r--src/battle/src/Struct/Statistics.elm176
-rw-r--r--src/battle/src/Struct/Tile.elm203
-rw-r--r--src/battle/src/Struct/TurnResult.elm502
-rw-r--r--src/battle/src/Struct/TurnResultAnimator.elm123
-rw-r--r--src/battle/src/Struct/UI.elm135
-rw-r--r--src/battle/src/Struct/Weapon.elm248
-rw-r--r--src/battle/src/Struct/WeaponSet.elm39
-rw-r--r--src/battle/src/Update/AbortTurn.elm24
-rw-r--r--src/battle/src/Update/AttackWithoutMoving.elm45
-rw-r--r--src/battle/src/Update/ChangeScale.elm26
-rw-r--r--src/battle/src/Update/DisplayCharacterInfo.elm53
-rw-r--r--src/battle/src/Update/EndTurn.elm91
-rw-r--r--src/battle/src/Update/HandleAnimationEnded.elm127
-rw-r--r--src/battle/src/Update/HandleServerReply.elm230
-rw-r--r--src/battle/src/Update/LookForCharacter.elm54
-rw-r--r--src/battle/src/Update/RequestDirection.elm76
-rw-r--r--src/battle/src/Update/SelectCharacter.elm298
-rw-r--r--src/battle/src/Update/SelectCharacterOrTile.elm52
-rw-r--r--src/battle/src/Update/SelectTab.elm32
-rw-r--r--src/battle/src/Update/SelectTile.elm158
-rw-r--r--src/battle/src/Update/SendLoadBattleRequest.elm29
-rw-r--r--src/battle/src/Update/SetRequestedHelp.elm22
-rw-r--r--src/battle/src/Update/SwitchTeam.elm30
-rw-r--r--src/battle/src/Update/SwitchWeapon.elm100
-rw-r--r--src/battle/src/Update/TestAnimation.elm27
-rw-r--r--src/battle/src/Util/Array.elm34
-rw-r--r--src/battle/src/Util/Html.elm6
-rw-r--r--src/battle/src/Util/List.elm16
-rw-r--r--src/battle/src/View/Character.elm230
-rw-r--r--src/battle/src/View/Controlled.elm133
-rw-r--r--src/battle/src/View/Controlled/CharacterCard.elm582
-rw-r--r--src/battle/src/View/Controlled/ManualControls.elm60
-rw-r--r--src/battle/src/View/Controlled/Targets.elm69
-rw-r--r--src/battle/src/View/Gauge.elm76
-rw-r--r--src/battle/src/View/MainMenu.elm38
-rw-r--r--src/battle/src/View/Map.elm162
-rw-r--r--src/battle/src/View/Map/Character.elm218
-rw-r--r--src/battle/src/View/Map/Navigator.elm245
-rw-r--r--src/battle/src/View/Map/Tile.elm69
-rw-r--r--src/battle/src/View/MessageBoard.elm30
-rw-r--r--src/battle/src/View/MessageBoard/Animator.elm57
-rw-r--r--src/battle/src/View/MessageBoard/Animator/Attack.elm297
-rw-r--r--src/battle/src/View/MessageBoard/Error.elm33
-rw-r--r--src/battle/src/View/MessageBoard/Help.elm37
-rw-r--r--src/battle/src/View/MessageBoard/Help/Guide.elm100
-rw-r--r--src/battle/src/View/MessageBoard/Help/Rank.elm97
-rw-r--r--src/battle/src/View/SubMenu.elm85
-rw-r--r--src/battle/src/View/SubMenu/Characters.elm69
-rw-r--r--src/battle/src/View/SubMenu/Settings.elm59
-rw-r--r--src/battle/src/View/SubMenu/Status.elm55
-rw-r--r--src/battle/src/View/SubMenu/Status/CharacterInfo.elm34
-rw-r--r--src/battle/src/View/SubMenu/Status/TileInfo.elm142
-rw-r--r--src/battle/src/View/SubMenu/Timeline.elm95
-rw-r--r--src/battle/src/View/SubMenu/Timeline/Attack.elm164
-rw-r--r--src/battle/src/View/SubMenu/Timeline/Movement.elm62
-rw-r--r--src/battle/src/View/SubMenu/Timeline/PlayerDefeat.elm38
-rw-r--r--src/battle/src/View/SubMenu/Timeline/PlayerTurnStart.elm38
-rw-r--r--src/battle/src/View/SubMenu/Timeline/PlayerVictory.elm38
-rw-r--r--src/battle/src/View/SubMenu/Timeline/WeaponSwitch.elm58
-rw-r--r--src/battle/www/index.html30
-rw-r--r--src/battle/www/style.css1059
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;
+}