From d00e3685a8d63b46d9867f9160d75c43832e4865 Mon Sep 17 00:00:00 2001 From: Roman Potashow Date: Tue, 7 May 2019 23:52:10 +0300 Subject: [PATCH] Update elm-tiled to 0.19 --- .travis.yml | 52 +- elm-package.json | 18 - elm.json | 25 + src/Tiled.elm | 84 +++ src/Tiled/Decode.elm | 1103 -------------------------------------- src/Tiled/Layer.elm | 393 ++++++++++++++ src/Tiled/Level.elm | 339 ++++++++++++ src/Tiled/Object.elm | 292 ++++++++++ src/Tiled/Properties.elm | 107 ++++ src/Tiled/Tileset.elm | 478 +++++++++++++++++ tests/Generate.elm | 336 ++++++++++++ tests/Generate/Util.elm | 10 + tests/Layer.elm | 19 + tests/Level.elm | 42 -- tests/Mock.elm | 59 -- tests/Object.elm | 19 + tests/Properties.elm | 19 + tests/Tileset.elm | 59 ++ tests/elm-package.json | 21 - 19 files changed, 2189 insertions(+), 1286 deletions(-) delete mode 100644 elm-package.json create mode 100644 elm.json create mode 100644 src/Tiled.elm delete mode 100644 src/Tiled/Decode.elm create mode 100644 src/Tiled/Layer.elm create mode 100644 src/Tiled/Level.elm create mode 100644 src/Tiled/Object.elm create mode 100644 src/Tiled/Properties.elm create mode 100644 src/Tiled/Tileset.elm create mode 100644 tests/Generate.elm create mode 100644 tests/Generate/Util.elm create mode 100644 tests/Layer.elm delete mode 100644 tests/Level.elm delete mode 100644 tests/Mock.elm create mode 100644 tests/Object.elm create mode 100644 tests/Properties.elm create mode 100644 tests/Tileset.elm delete mode 100644 tests/elm-package.json diff --git a/.travis.yml b/.travis.yml index e337e6c..b29348d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,46 +1,12 @@ -sudo: false -language: bash -cache: - directories: - - test/elm-stuff/build-artifacts - - sysconfcpus - -os: - - osx - - linux +sudo: required -env: - matrix: - - ELM_VERSION=0.18.0 TARGET_NODE_VERSION=node - # - ELM_VERSION=0.18.0 TARGET_NODE_VERSION=4.0 +language: elm +node_js: '10' # latest 10.x -before_install: - - if [ ${TRAVIS_OS_NAME} == "osx" ]; - then brew update; brew install nvm; mkdir ~/.nvm; export NVM_DIR=~/.nvm; source $(brew --prefix nvm)/nvm.sh; - fi - - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - - | # epic build time improvement - see https://github.com/elm-lang/elm-compiler/issues/1473#issuecomment-245704142 - if [ ! -d sysconfcpus/bin ]; - then - git clone https://github.com/obmarg/libsysconfcpus.git; - cd libsysconfcpus; - ./configure --prefix=$TRAVIS_BUILD_DIR/sysconfcpus; - make && make install; - cd ..; - fi +elm-test: 0.19.0-rev6 +elm-format: 0.8.1 -install: - - nvm install $TARGET_NODE_VERSION - - nvm use $TARGET_NODE_VERSION - - node --version - - npm --version - - cd tests - - npm install -g elm@$ELM_VERSION elm-test - - mv $(npm config get prefix)/bin/elm-make $(npm config get prefix)/bin/elm-make-old - - printf '%s\n\n' '#!/bin/bash' 'echo "Running elm-make with sysconfcpus -n 2"' '$TRAVIS_BUILD_DIR/sysconfcpus/bin/sysconfcpus -n 2 elm-make-old "$@"' > $(npm config get prefix)/bin/elm-make - - chmod +x $(npm config get prefix)/bin/elm-make - - elm package install --yes - - cd .. - -script: - - elm-test \ No newline at end of file +cache: + yarn: true + directories: + - node_modules diff --git a/elm-package.json b/elm-package.json deleted file mode 100644 index b24066d..0000000 --- a/elm-package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": "2.1.1", - "summary": "Decoder for Tiled maps (http://www.mapeditor.org/)", - "repository": "https://github.com/justgook/elm-tiled-decode.git", - "license": "BSD3", - "source-directories": [ - "src" - ], - "exposed-modules": [ - "Tiled.Decode" - ], - "dependencies": { - "NoRedInk/elm-decode-pipeline": "3.0.0 <= v < 4.0.0", - "elm-lang/core": "5.1.1 <= v < 6.0.0", - "newlandsvalley/elm-binary-base64": "1.0.1 <= v < 2.0.0" - }, - "elm-version": "0.18.0 <= v < 0.19.0" -} diff --git a/elm.json b/elm.json new file mode 100644 index 0000000..68a2ca0 --- /dev/null +++ b/elm.json @@ -0,0 +1,25 @@ +{ + "type": "package", + "name": "justgook/elm-tiled", + "summary": "A library for building decoders for Tiled levels.", + "license": "BSD-3-Clause", + "version": "1.0.0", + "exposed-modules": [ + "Tiled", + "Tiled.Level", + "Tiled.Layer", + "Tiled.Tileset", + "Tiled.Object", + "Tiled.Properties" + ], + "elm-version": "0.19.0 <= v < 0.20.0", + "dependencies": { + "NoRedInk/elm-json-decode-pipeline": "1.0.0 <= v < 2.0.0", + "elm/core": "1.0.0 <= v < 2.0.0", + "elm/json": "1.1.2 <= v < 2.0.0", + "elm-community/json-extra": "4.0.0 <= v < 5.0.0" + }, + "test-dependencies": { + "elm-explorations/test": "1.2.1 <= v < 2.0.0" + } +} diff --git a/src/Tiled.elm b/src/Tiled.elm new file mode 100644 index 0000000..436d6ed --- /dev/null +++ b/src/Tiled.elm @@ -0,0 +1,84 @@ +module Tiled exposing + ( decode, encode + , GidInfo, gidInfo + ) + +{-| Use the [`decode`](#decode) to get [`Level`](Tiled-Level) + + +# Default Decoding + +@docs decode, encode + +-} + +import Bitwise +import Json.Decode as Json exposing (Decoder) +import Tiled.Level as Level exposing (Level) + + +{-| Alias to [`Level.encode`](Tiled.Level#encode) +-} +encode : Level -> Json.Value +encode = + Level.encode + + +{-| Alias to [`Level.decode`](Tiled.Level#decode) +-} +decode : Decoder Level +decode = + Level.decode + + + +-- http://doc.mapeditor.org/en/latest/reference/tmx-map-format/#tile-flipping + + +type alias GidInfo = + { gid : Int, fh : Bool, fv : Bool, fd : Bool } + + +gidInfo : Int -> GidInfo +gidInfo gid = + { gid = cleanGid gid + , fh = flippedHorizontally gid + , fv = flippedVertically gid + , fd = flippedDiagonally gid + } + + +flippedHorizontally : Int -> Bool +flippedHorizontally globalTileId = + Bitwise.and globalTileId flippedHorizontalFlag /= 0 + + +flippedVertically : Int -> Bool +flippedVertically globalTileId = + Bitwise.and globalTileId flippedVerticalFlag /= 0 + + +flippedDiagonally : Int -> Bool +flippedDiagonally globalTileId = + Bitwise.and globalTileId flippedDiagonalFlag /= 0 + + +cleanGid : Int -> Int +cleanGid globalTileId = + flippedHorizontalFlag + |> Bitwise.or flippedVerticalFlag + |> Bitwise.or flippedDiagonalFlag + |> Bitwise.complement + |> Bitwise.and globalTileId + + +flippedHorizontalFlag = + 0x80000000 + + +flippedVerticalFlag = + 0x40000000 + + +flippedDiagonalFlag = + 0x20000000 diff --git a/src/Tiled/Decode.elm b/src/Tiled/Decode.elm deleted file mode 100644 index 85eab01..0000000 --- a/src/Tiled/Decode.elm +++ /dev/null @@ -1,1103 +0,0 @@ -module Tiled.Decode - exposing - ( CustomProperties - , DrawOrder(..) - , EmbeddedTileData - , ImageCollectionTileData - , ImageCollectionTileDataTile - , ImageLayerData - , Layer(..) - , Level - , Object(..) - , ObjectLayerData - , ObjectPointData - , ObjectPolyPoint - , ObjectPolygonData - , ObjectRectangleData - , ObjectTileData - , Orientation(..) - , Property(..) - , RenderOrder(..) - , SourceTileData - , TileLayerData - , Tileset(..) - , decode - , empty - , decodeTileset - , decodeTiles - , TilesData - ) - -{-| Use the [`decode`](#decode) to get [`Level`](#Level) - - -# Default Decoding - -@docs decode, empty - -##Level - -@docs Level, Orientation - -##Layers - -@docs Layer, ImageLayerData, TileLayerData, ObjectLayerData - -##TileSets -@docs Tileset, SourceTileData, EmbeddedTileData, ImageCollectionTileData, ImageCollectionTileDataTile, TilesData - -##Objects -Objects that is used inside [`ObjectLayerData`](#ObjectLayerData) -@docs Object, ObjectPointData, ObjectRectangleData, ObjectPolygonData, ObjectPolygonData, ObjectTileData, ObjectPolyPoint - - -# Properties - -@docs CustomProperties, Property, RenderOrder, DrawOrder - -##Sub-decoders - -@docs decodeTileset, decodeTiles - --} - -import BinaryBase64 -import Bitwise exposing (or, shiftLeftBy) -import Dict exposing (Dict) -import Json.Decode as Decode exposing (Decoder, field) -import Json.Decode.Pipeline as Pipeline exposing (decode, hardcoded, optional, required) - - --- https://robots.thoughtbot.com/5-common-json-decoders#5---conditional-decoding-based-on-a-field --- http://eeue56.github.io/json-to-elm/ - - -{-| -} -type alias CustomProperties = - Dict String Property - - -{-| Custom properties values --} -type Property - = PropBool Bool - | PropInt Int - | PropFloat Float - | PropString String - | PropColor String - | PropFile String - - -{-| - - - `backgroundcolor` string Hex-formatted color (`#RRGGBB` or `#AARRGGBB`) (default `none`) - - `width` Number of tile columns - - `height` Number of tile rows - - `tilewidth` Map grid width - - `tileheight` Map grid height - - `infinite` Whether the map has infinite dimensions - - `orientation` orthogonal, isometric, staggered or hexagonal - - `renderorder` Rendering direction (orthogonal maps only) - - `layers` List of layers - - `tilesets` List of tilesets - - `properties` A list of properties (name, value, type). - - `version` The JSON format version - - `tiledversion` The Tiled version used to save the file --} -type alias Level = - { backgroundcolor : String - , width : Int - , height : Int - , tilewidth : Int - , tileheight : Int - , infinite : Bool - , orientation : Orientation - , renderorder : RenderOrder - , layers : List Layer - , tilesets : List Tileset - , properties : CustomProperties - , version : Float - , tiledversion : String - } - - -{-| Creates empty [`Level`](#Level) --} -empty : Level -empty = - { backgroundcolor = "" - , height = 0 - , infinite = False - , layers = [] - , orientation = Orthogonal - , renderorder = RightDown - , tiledversion = "" - , tileheight = 0 - , tilesets = [] - , tilewidth = 0 - , version = 0 - , width = 0 - , properties = Dict.empty - } - - -{-| Decodes [Tiled](http://www.mapeditor.org/) map (json encoded) to [`Level`](#Level) data structures - - Http.get "path/to/map.json" Tiled.decode - |> Http.send LevelLoaded - - update msg model = - case msg of - LevelLoaded (Ok level) -> - ( level, Cmd.none ) - LevelLoaded (Err err) -> - Debug.crash "Tiled Map fail to decode" err - -or - - level = - case decodeString Tiled.decode "JSON STRING OF MAP" of - Ok data -> - Debug.log "Tiled Map decoded" data - - Err err -> - Debug.crash "Tiled Map fail to decode" err - --} -decode : Decoder Level -decode = - Pipeline.decode Level - |> optional "backgroundcolor" Decode.string "none" - |> required "width" Decode.int - |> required "height" Decode.int - |> required "tilewidth" Decode.int - |> required "tileheight" Decode.int - |> required "infinite" Decode.bool - |> required "orientation" decodeOrientation - |> required "renderorder" decodeRenderOrder - |> required "layers" (Decode.list decodeLayer) - |> required "tilesets" (Decode.list decodeTileset) - |> Pipeline.custom propertiesDecoder - |> required "version" Decode.float - |> required "tiledversion" Decode.string - - -{-| -} -type RenderOrder - = RightDown - | RightUp - | LeftDown - | LeftUp - - -decodeRenderOrder : Decoder RenderOrder -decodeRenderOrder = - Decode.string - |> Decode.andThen - (\result -> - case result of - "right-down" -> - Decode.succeed RightDown - - "right-up" -> - Decode.succeed RightUp - - "left-down" -> - Decode.succeed LeftDown - - "left-up" -> - Decode.succeed LeftUp - - _ -> - Decode.fail "Unknow render order" - ) - - -{-| -} -type DrawOrder - = TopDown - | Index - - -decodeDrawOrder : Decoder DrawOrder -decodeDrawOrder = - Decode.string - |> Decode.andThen - (\result -> - case result of - "topdown" -> - Decode.succeed TopDown - - "index" -> - Decode.succeed Index - - _ -> - Decode.fail "Unknow draw order" - ) - - -{-| -} -type Orientation - = Orthogonal - | Isometric - | Staggered - | Hexagonal - - -decodeOrientation : Decoder Orientation -decodeOrientation = - Decode.string - |> Decode.andThen - (\result -> - case result of - "orthogonal" -> - Decode.succeed Orthogonal - - "isometric" -> - Decode.succeed Isometric - - "staggered" -> - Decode.succeed Staggered - - "hexagonal" -> - Decode.succeed Hexagonal - - _ -> - Decode.fail "Unknow orientation" - ) - - -{-| Tiles in teleset --} -decodeTiles : Decoder (Dict Int TilesData) -decodeTiles = - let - doNothing a b c = - c - - mergeIfBoth f a b c = - Dict.merge doNothing f doNothing a b c - - oldPropsDecode tileproperties tilepropertytypes = - mergeIfBoth - (\i v1 v2 acc -> - case String.toInt i of - Ok index -> - acc - |> Decode.andThen - (\aAAAA -> - mergeIfBoth - (\a b c -> - Decode.andThen - (\d -> - case Decode.decodeValue (propertyDecoder c) b of - Ok resulValue -> - d |> Dict.insert a resulValue >> Decode.succeed - - Err err -> - Decode.fail err - ) - ) - v1 - v2 - (Decode.succeed Dict.empty) - |> Decode.andThen - (\asd -> - Dict.insert index asd aAAAA |> Decode.succeed - ) - ) - - Err a -> - Decode.fail a - ) - tileproperties - tilepropertytypes - (Decode.succeed Dict.empty) - - oldTilesDecoder = - Decode.map2 (\a b -> { animation = a, objectgroup = b }) - (Decode.maybe (field "animation" (Decode.list decodeSpriteAnimation))) - (Decode.maybe (field "objectgroup" decodeTilesDataObjectgroup)) - |> Decode.keyValuePairs - |> Decode.andThen - (List.foldl - (\( k, v ) acc -> - case String.toInt k of - Ok index -> - acc |> Decode.andThen (Dict.insert index v >> Decode.succeed) - - Err err -> - Decode.fail err - ) - (Decode.succeed Dict.empty) - ) - - appendOldTileDataToProps tiles = - Decode.map - (\props -> - Dict.merge - (\k { objectgroup, animation } acc -> - Dict.insert k { objectgroup = objectgroup, animation = animation, properties = Dict.empty } acc - ) - (\k { objectgroup, animation } properties acc -> - Dict.insert k { objectgroup = objectgroup, animation = animation, properties = properties } acc - ) - (\k v2 acc -> Dict.insert k { objectgroup = Nothing, animation = Nothing, properties = Dict.empty } acc) - tiles - props - Dict.empty - ) - - old = - Decode.map3 - (\tileproperties tilepropertytypes tiles -> - case ( tileproperties, tilepropertytypes ) of - ( Just prop, Just types ) -> - oldPropsDecode prop types - |> appendOldTileDataToProps tiles - - _ -> - Dict.empty |> Decode.succeed |> appendOldTileDataToProps tiles - ) - (Decode.field "tileproperties" (Decode.dict (Decode.dict Decode.value)) |> Decode.maybe) - (Decode.field "tilepropertytypes" (Decode.dict (Decode.dict Decode.string)) |> Decode.maybe) - (Decode.field "tiles" oldTilesDecoder) - |> Decode.andThen identity - - new = - Decode.list decodeTilesDataNew - |> Decode.andThen - (List.foldl - (\{ id, objectgroup, animation, properties } acc -> - acc |> Decode.andThen (Dict.insert id { objectgroup = objectgroup, animation = animation, properties = properties } >> Decode.succeed) - ) - (Decode.succeed Dict.empty) - ) - |> Decode.field "tiles" - in - Decode.oneOf [ new, old ] - |> Decode.maybe - |> Decode.map (Maybe.withDefault Dict.empty) - - -{-| -} -propertyDecoder : String -> Decoder Property -propertyDecoder typeString = - case typeString of - "bool" -> - Decode.map PropBool Decode.bool - - "color" -> - Decode.map PropColor Decode.string - - "float" -> - Decode.map PropFloat Decode.float - - "file" -> - Decode.map PropFile Decode.string - - "int" -> - Decode.map PropInt Decode.int - - "string" -> - Decode.map PropString Decode.string - - _ -> - Decode.fail <| "I can't decode the type " ++ typeString - - -{-| Decoding properties (with predefined field names) as Dict String Poperty --} -propertiesDecoderWith_Old : ( String, String ) -> Decoder (Dict String Property) -propertiesDecoderWith_Old ( properties, propertytypes ) = - let - combine : List (Decoder a) -> Decoder (List a) - combine = - List.foldr (Decode.map2 (::)) (Decode.succeed []) - - decodeProperty : ( String, String ) -> Decoder ( String, Property ) - decodeProperty ( propName, propType ) = - Decode.at [ properties, propName ] - (propertyDecoder propType) - |> Decode.map ((,) propName) - - propertiesDecoder : Decoder (Dict String Property) - propertiesDecoder = - Decode.field propertytypes (Decode.keyValuePairs Decode.string) - |> Decode.map (List.map decodeProperty) - |> Decode.andThen combine - |> Decode.map Dict.fromList - in - propertiesDecoder - - -propertiesDecoder_New : Decoder (Dict String Property) -propertiesDecoder_New = - Decode.field "properties" - (Decode.list - (Pipeline.decode identity - |> required "type" Decode.string - |> Decode.andThen - (\kind -> - Pipeline.decode (,) - |> required "name" Decode.string - |> required "value" (propertyDecoder kind) - ) - ) - |> Decode.map Dict.fromList - ) - - -propertiesDecoderWith : ( String, String ) -> Decoder (Dict String Property) -propertiesDecoderWith ( properties, propertytypes ) = - Decode.oneOf [ propertiesDecoder_New, propertiesDecoderWith_Old ( properties, propertytypes ) ] - |> Decode.maybe - |> Decode.map (Maybe.withDefault Dict.empty) - - -{-| Decoding properties as Dict String Poperty --} -propertiesDecoder : Decoder CustomProperties -propertiesDecoder = - propertiesDecoderWith ( "properties", "propertytypes" ) - - -{-| -} -type Tileset - = TilesetSource SourceTileData - | TilesetEmbedded EmbeddedTileData - | TilesetImageCollection ImageCollectionTileData - - -{-| - - - `columns` The number of tile columns in the tileset - - `firstgid` GID corresponding to the first tile in the set - - `margin` Buffer between image edge and first tile (pixels) - - `name` Name given to this tileset - - `spacing` Spacing between adjacent tiles in image (pixels) - - `tilecount` The number of tiles in this tileset - - `tileheight` Maximum height of tiles in this set - - `tiles` Dict of [`ImageCollectionTileDataTile`](#ImageCollectionTileDataTile) - - `tilewidth` Maximum width of tiles in this set - - `properties` A list of properties (name, value, type). --} -type alias ImageCollectionTileData = - { columns : Int - , firstgid : Int - , margin : Int - , name : String - , spacing : Int - , tilecount : Int - , tileheight : Int - , tiles : Dict Int ImageCollectionTileDataTile - , tilewidth : Int - , properties : CustomProperties - } - - -{-| -} -type alias ImageCollectionTileDataTile = - { image : String - , imageheight : Int - , imagewidth : Int - } - - -{-| -} -decodeImageCollectionTileData : Decode.Decoder Tileset -decodeImageCollectionTileData = - Pipeline.decode ImageCollectionTileData - |> Pipeline.required "columns" Decode.int - |> Pipeline.required "firstgid" Decode.int - |> Pipeline.required "margin" Decode.int - |> Pipeline.required "name" Decode.string - |> Pipeline.required "spacing" Decode.int - |> Pipeline.required "tilecount" Decode.int - |> Pipeline.required "tileheight" Decode.int - |> Pipeline.required "tiles" decodeImageCollectionTileDataTiles - |> Pipeline.required "tilewidth" Decode.int - |> Pipeline.custom propertiesDecoder - |> Decode.map TilesetImageCollection - - -{-| -} -decodeImageCollectionTileDataTiles : Decoder (Dict Int ImageCollectionTileDataTile) -decodeImageCollectionTileDataTiles = - Decode.keyValuePairs decodeImageCollectionTileDataTile - |> Decode.andThen - (List.foldl - (\( i, data ) acc -> - case String.toInt i of - Ok index -> - acc |> Decode.andThen (Dict.insert index data >> Decode.succeed) - - Err a -> - Decode.fail a - ) - (Decode.succeed Dict.empty) - ) - - -{-| -} -decodeImageCollectionTileDataTile : Decode.Decoder ImageCollectionTileDataTile -decodeImageCollectionTileDataTile = - Decode.map3 ImageCollectionTileDataTile - (field "image" Decode.string) - (field "imageheight" Decode.int) - (field "imagewidth" Decode.int) - - -{-| -} -decodeTileset : Decoder Tileset -decodeTileset = - Decode.oneOf [ decodeEmbeddedTileset, decodeSourceTileset, decodeImageCollectionTileData ] - - -{-| -} -type alias SourceTileData = - { firstgid : Int - , source : String - } - - -{-| -} -decodeSourceTileset : Decoder Tileset -decodeSourceTileset = - Pipeline.decode SourceTileData - |> required "firstgid" Decode.int - |> required "source" Decode.string - |> Decode.map TilesetSource - - -{-| - - - `columns` The number of tile columns in the tileset - - `firstgid` GID corresponding to the first tile in the set - - `image` Image used for tiles in this set - - `imagewidth` Width of source image in pixels - - `imageheight` Height of source image in pixels - - `margin` Buffer between image edge and first tile (pixels) - - `name` Name given to this tileset - - `spacing` Spacing between adjacent tiles in image (pixels) - - `tilecount` The number of tiles in this tileset - - `tileheight` Maximum height of tiles in this set - - `tiles` Dict of TilesData [`TilesData`](#TilesData) - - `tilewidth` Maximum width of tiles in this set - - `properties` A list of properties (name, value, type). --} -type alias EmbeddedTileData = - { columns : Int - , firstgid : Int - , image : String - , imageheight : Int - , imagewidth : Int - , margin : Int - , name : String - , spacing : Int - , tilecount : Int - , tileheight : Int - , tilewidth : Int - , transparentcolor : String - , tiles : Dict Int TilesData - , properties : CustomProperties - } - - -{-| -} -decodeEmbeddedTileset : Decoder Tileset -decodeEmbeddedTileset = - Pipeline.decode EmbeddedTileData - |> required "columns" Decode.int - |> required "firstgid" Decode.int - |> required "image" Decode.string - |> required "imageheight" Decode.int - |> required "imagewidth" Decode.int - |> required "margin" Decode.int - |> required "name" Decode.string - |> required "spacing" Decode.int - |> required "tilecount" Decode.int - |> required "tileheight" Decode.int - |> required "tilewidth" Decode.int - |> optional "transparentcolor" Decode.string "none" - |> Pipeline.custom decodeTiles - -- |> optional "tiles" decodeTiles Dict.empty - |> Pipeline.custom propertiesDecoder - |> Decode.map TilesetEmbedded - - -{-| - - - animation : Maybe (List SpriteAnimation) - - objectgroup : Maybe TilesDataObjectgroup - - properties : CustomProperties --} -type alias TilesData = - TilesDataPlain {} - - -{-| -} -type alias TilesDataPlain a = - { a - | animation : Maybe (List SpriteAnimation) - , objectgroup : Maybe TilesDataObjectgroup - , properties : CustomProperties - } - - -{-| -} -type alias TilesDataObjectgroup = - { draworder : DrawOrder - , name : String - , objects : List Object - , opacity : Int - , visible : Bool - , x : Int - , y : Int - } - - -{-| -} -decodeTilesDataNew : Decoder (TilesDataPlain { id : Int }) -decodeTilesDataNew = - Decode.map4 (\a b c d -> { animation = a, objectgroup = b, properties = c, id = d }) - (Decode.maybe (field "animation" (Decode.list decodeSpriteAnimation))) - (Decode.maybe (field "objectgroup" decodeTilesDataObjectgroup)) - (Decode.maybe propertiesDecoder_New |> Decode.map (Maybe.withDefault Dict.empty)) - (field "id" Decode.int) - - -{-| -} -decodeTilesDataObjectgroup : Decoder TilesDataObjectgroup -decodeTilesDataObjectgroup = - Pipeline.decode TilesDataObjectgroup - |> Pipeline.required "draworder" decodeDrawOrder - |> Pipeline.required "name" Decode.string - |> Pipeline.required "objects" (Decode.list decodeObject) - |> Pipeline.required "opacity" Decode.int - |> Pipeline.required "visible" Decode.bool - |> Pipeline.required "x" Decode.int - |> Pipeline.required "y" Decode.int - - -{-| -} -type alias SpriteAnimation = - { duration : Int - , tileid : Int - } - - -{-| -} -decodeSpriteAnimation : Decoder SpriteAnimation -decodeSpriteAnimation = - Decode.map2 SpriteAnimation - (field "duration" Decode.int) - (field "tileid" Decode.int) - - -{-| -} -type Layer - = ImageLayer ImageLayerData - | TileLayer TileLayerData - | ObjectLayer ObjectLayerData - - -{-| - - - `image` - Image used as background - - `name` Name assigned to this layer - - `x` Horizontal layer offset in tiles. Always 0. - - `y` Vertical layer offset in tiles. Always 0. - - `opacity` Value between 0 and 1 - - `properties` A list of properties (name, value, type). - - `visible` Whether layer is shown or hidden in editor --} -type alias ImageLayerData = - { image : String - , name : String - , opacity : Float - , visible : Bool - , x : Float - , y : Float - , transparentcolor : String - , properties : CustomProperties - } - - -{-| - - - `data` List of GIDs. [tilelayer](#TileLayerData) only. - - `name` Name assigned to this layer - - `x` Horizontal layer offset in tiles. Always 0. - - `y` Vertical layer offset in tiles. Always 0. - - `width` Column count. Same as map width for fixed-size maps. - - `height` Row count. Same as map height for fixed-size maps. - - `opacity` Value between 0 and 1 - - `properties` A list of properties (name, value, type). - - `visible` Whether layer is shown or hidden in editor --} -type alias TileLayerData = - { data : List Int - , height : Int - , name : String - , opacity : Float - , visible : Bool - , width : Int - , x : Float - , y : Float - , properties : CustomProperties - } - - -{-| - - - `name` Name assigned to this layer - - `x` Horizontal layer offset in tiles. Always 0. - - `y` Vertical layer offset in tiles. Always 0. - - `draworder` [`TopDown`](#DrawOrder) (default) - - `objects` List of objects. objectgroup only. - - `opacity` Value between 0 and 1 - - `properties` A list of properties (name, value, type). - - `visible` Whether layer is shown or hidden in editor --} -type alias ObjectLayerData = - { draworder : DrawOrder - , name : String - , objects : List Object - , opacity : Float - , visible : Bool - , x : Float - , y : Float - , properties : CustomProperties - } - - -{-| -} -decodeLayer : Decoder Layer -decodeLayer = - field "type" Decode.string - |> Decode.andThen - (\string -> - case string of - "tilelayer" -> - Decode.map TileLayer - decodeTileLayer - - "imagelayer" -> - Decode.map ImageLayer - decodeImageLayer - - "objectgroup" -> - Decode.map ObjectLayer - decodeObjectLayer - - _ -> - Decode.fail ("Invalid layer type: " ++ string) - ) - - -{-| -} -decodeImageLayer : Decoder ImageLayerData -decodeImageLayer = - Pipeline.decode ImageLayerData - |> required "image" Decode.string - |> required "name" Decode.string - |> required "opacity" Decode.float - |> required "visible" Decode.bool - |> required "x" Decode.float - |> required "y" Decode.float - |> optional "transparentcolor" Decode.string "none" - |> Pipeline.custom propertiesDecoder - - -{-| -} -decodeTileLayer : Decoder TileLayerData -decodeTileLayer = - Pipeline.decode (,) - |> optional "encoding" Decode.string "none" - |> optional "compression" Decode.string "none" - |> Decode.andThen - (\( encoding, compression ) -> - Pipeline.decode TileLayerData - |> required "data" (decodeTileLayerData encoding compression) - |> required "height" Decode.int - |> required "name" Decode.string - |> required "opacity" Decode.float - |> required "visible" Decode.bool - |> required "width" Decode.int - |> required "x" Decode.float - |> required "y" Decode.float - |> Pipeline.custom propertiesDecoder - ) - - -decodeTileLayerData : String -> String -> Decoder (List Int) -decodeTileLayerData encoding compression = - if compression /= "none" then - Decode.fail "Tile layer compression not supported yet" - else if encoding == "base64" then - Decode.string - |> Decode.andThen - (\string -> - string - |> BinaryBase64.decode - |> Result.map (\data -> Decode.succeed (convertTilesData data)) - |> resultExtract (\err -> Decode.fail err) - ) - else - Decode.list Decode.int - - -shiftValues : List Int -shiftValues = - [ 0, 8, 16, 24 ] - - -convertTilesData : BinaryBase64.ByteString -> List Int -convertTilesData octets = - let - ( dword, rest ) = - ( List.take 4 octets, List.drop 4 octets ) - - value = - zip shiftValues dword - |> List.foldl - (\( shiftValues, octet ) acc -> - or (octet |> shiftLeftBy shiftValues) acc - ) - 0 - in - if List.isEmpty rest then - [ value ] - else - value :: convertTilesData rest - - -zip : List a -> List b -> List ( a, b ) -zip = - List.map2 (,) - - -resultExtract : (e -> a) -> Result e a -> a -resultExtract f x = - -- http://package.elm-lang.org/packages/elm-community/result-extra/2.2.0/Result-Extra - case x of - Ok a -> - a - - Err e -> - f e - - -{-| -} -decodeObjectLayer : Decoder ObjectLayerData -decodeObjectLayer = - Pipeline.decode ObjectLayerData - |> required "draworder" decodeDrawOrder - |> required "name" Decode.string - |> required "objects" (Decode.list decodeObject) - |> required "opacity" Decode.float - |> required "visible" Decode.bool - |> required "x" Decode.float - |> required "y" Decode.float - |> Pipeline.custom propertiesDecoder - - -{-| -} -type Object - = ObjectPoint ObjectPointData - | ObjectRectangle ObjectRectangleData - | ObjectEllipse ObjectRectangleData - | ObjectPolygon ObjectPolygonData - | ObjectPolyLine ObjectPolygonData - | ObjectTile ObjectTileData - - -when : Decoder a -> (a -> Bool) -> Decoder b -> Decoder b -when checkDecoder expected actualDecoder = - -- http://package.elm-lang.org/packages/zwilias/json-decode-exploration/5.0.0/Json-Decode-Exploration#check - checkDecoder - |> Decode.andThen - (\actual -> - if expected actual then - actualDecoder - else - Decode.fail <| - "Verification failed, expected '" - ++ toString expected - ++ "'." - ) - - -{-| -} -decodeObject : Decoder Object -decodeObject = - let - point = - Decode.map ObjectPoint decodeObjectPoint - |> when (field "point" Decode.bool) ((==) True) - - elipse = - Decode.map ObjectEllipse decodeObjectRectangle - |> when (field "ellipse" Decode.bool) ((==) True) - - polygon = - Decode.map ObjectPolygon (decodeObjectPolygonData "polygon") - - polyline = - Decode.map ObjectPolyLine (decodeObjectPolygonData "polyline") - - tile = - Decode.map ObjectTile decodeObjectTile - |> when (field "gid" Decode.int) ((<) 0) - - rectangle = - Decode.map ObjectRectangle decodeObjectRectangle - in - Decode.oneOf - [ point - , elipse - , tile - , polygon - , polyline - , rectangle - ] - - -{-| -} -type alias ObjectPolygonData = - { height : Float - , id : Int - , name : String - , polygon : List ObjectPolyPoint - , rotation : Float - , kind : String - , visible : Bool - , width : Float - , x : Float - , y : Float - , properties : CustomProperties - } - - -{-| -} -type alias ObjectPolyPoint = - { x : Float - , y : Float - } - - -{-| -} -decodeObjectPolyPoint : Decoder ObjectPolyPoint -decodeObjectPolyPoint = - Decode.map2 ObjectPolyPoint - (field "x" Decode.float) - (field "y" Decode.float) - - -{-| -} -decodeObjectPolygonData : String -> Decoder ObjectPolygonData -decodeObjectPolygonData key = - Pipeline.decode ObjectPolygonData - |> required "height" Decode.float - |> required "id" Decode.int - |> required "name" Decode.string - |> required key (Decode.list decodeObjectPolyPoint) - |> required "rotation" Decode.float - |> required "type" Decode.string - |> required "visible" Decode.bool - |> required "width" Decode.float - |> required "x" Decode.float - |> required "y" Decode.float - |> Pipeline.custom propertiesDecoder - - -{-| -} -type alias ObjectRectangleData = - { height : Float - , id : Int - , name : String - , rotation : Float - , kind : String - , visible : Bool - , width : Float - , x : Float - , y : Float - , properties : CustomProperties - } - - -{-| -} -decodeObjectRectangle : Decoder ObjectRectangleData -decodeObjectRectangle = - Pipeline.decode ObjectRectangleData - |> required "height" Decode.float - |> required "id" Decode.int - |> required "name" Decode.string - |> required "rotation" Decode.float - |> required "type" Decode.string - |> required "visible" Decode.bool - |> required "width" Decode.float - |> required "x" Decode.float - |> required "y" Decode.float - |> Pipeline.custom propertiesDecoder - - -{-| -} -type alias ObjectTileData = - { gid : Int - , height : Float - , id : Int - , name : String - , rotation : Float - , kind : String - , visible : Bool - , width : Float - , x : Float - , y : Float - , properties : CustomProperties - } - - -{-| -} -decodeObjectTile : Decoder ObjectTileData -decodeObjectTile = - Pipeline.decode ObjectTileData - |> required "gid" Decode.int - |> required "height" Decode.float - |> required "id" Decode.int - |> required "name" Decode.string - |> required "rotation" Decode.float - |> required "type" Decode.string - |> required "visible" Decode.bool - |> required "width" Decode.float - |> required "x" Decode.float - |> required "y" Decode.float - |> Pipeline.custom propertiesDecoder - - -{-| -} -type alias ObjectPointData = - { id : Int - , name : String - , rotation : Float - , kind : String - , visible : Bool - , x : Float - , y : Float - , properties : CustomProperties - } - - -{-| -} -decodeObjectPoint : Decoder ObjectPointData -decodeObjectPoint = - Pipeline.decode ObjectPointData - |> required "id" Decode.int - |> required "name" Decode.string - |> required "rotation" Decode.float - |> required "type" Decode.string - |> required "visible" Decode.bool - |> required "x" Decode.float - |> required "y" Decode.float - |> Pipeline.custom propertiesDecoder diff --git a/src/Tiled/Layer.elm b/src/Tiled/Layer.elm new file mode 100644 index 0000000..e26eea9 --- /dev/null +++ b/src/Tiled/Layer.elm @@ -0,0 +1,393 @@ +module Tiled.Layer exposing + ( Layer(..), decode, encode + , TileData, ImageData, ObjectData, DrawOrder(..) + , decodeDraworder, encodeDraworder + , Chunk + ) + +{-| + +@docs Layer, decode, encode +@docs TileData, ImageData, ObjectData, DrawOrder, Chunks + + +# Internal + +@docs decodeDraworder, encodeDraworder + +-} + +import Dict exposing (Dict) +import Json.Decode as Decode exposing (Decoder, list, string) +import Json.Decode.Pipeline exposing (optional, required) +import Json.Encode as Encode +import Tiled.Object as Object exposing (Object) +import Tiled.Properties as Properties exposing (Properties, Property(..)) + + +{-| -} +type Layer + = Image ImageData + | Object ObjectData + | Tile TileData + | InfiniteTile TileChunkedData + + +{-| -} +decode : Decoder Layer +decode = + Decode.field "type" Decode.string + |> Decode.andThen + (\string -> + case string of + "tilelayer" -> + Decode.oneOf + [ Decode.map InfiniteTile decodeTileChunkedData + + -- |> when (Decode.field "chub" Decode.bool) ((==) True) + , Decode.map Tile decodeTile + ] + + "imagelayer" -> + Decode.map Image decodeImage + + "objectgroup" -> + Decode.map Object + decodeObjectLayer + + _ -> + Decode.fail ("Invalid layer type: " ++ string) + ) + + +{-| -} +encode : Layer -> Encode.Value +encode l = + case l of + -- Tile TileData + Image data -> + Encode.object + [ ( "image", Encode.string data.image ) + , ( "name", Encode.string data.name ) + , ( "id", Encode.int data.id ) + , ( "opacity", Encode.float data.opacity ) + , ( "visible", Encode.bool data.visible ) + , ( "x", Encode.float data.x ) + , ( "y", Encode.float data.y ) + , ( "transparentcolor", Encode.string data.transparentcolor ) + , ( "properties", Properties.encode data.properties ) + , ( "type", Encode.string "imagelayer" ) + ] + + Object data -> + let + objects = + Encode.list Object.encode data.objects + in + Encode.object + [ ( "draworder", encodeDraworder data.draworder ) + , ( "name", Encode.string data.name ) + , ( "id", Encode.int data.id ) + , ( "objects", objects ) + , ( "opacity", Encode.float data.opacity ) + , ( "visible", Encode.bool data.visible ) + , ( "x", Encode.float data.x ) + , ( "y", Encode.float data.y ) + , ( "properties", Properties.encode data.properties ) + , ( "type", Encode.string "objectgroup" ) + , ( "color", Encode.string data.color ) + ] + + Tile data -> + Encode.object + [ ( "id", Encode.int data.id ) + , ( "data", Encode.list Encode.int data.data ) + , ( "name", Encode.string data.name ) + , ( "opacity", Encode.float data.opacity ) + , ( "visible", Encode.bool data.visible ) + , ( "width", Encode.int data.width ) + , ( "height", Encode.int data.height ) + , ( "x", Encode.float data.x ) + , ( "y", Encode.float data.y ) + , ( "properties", Properties.encode data.properties ) + , ( "type", Encode.string "tilelayer" ) + ] + + InfiniteTile data -> + Encode.object + [ ( "id", Encode.int data.id ) + , ( "data", Encode.list encodeChunk data.chunks ) + , ( "name", Encode.string data.name ) + , ( "opacity", Encode.float data.opacity ) + , ( "visible", Encode.bool data.visible ) + , ( "width", Encode.int data.width ) + , ( "height", Encode.int data.height ) + , ( "x", Encode.float data.x ) + , ( "y", Encode.float data.y ) + , ( "properties", Properties.encode data.properties ) + , ( "type", Encode.string "tilelayer" ) + ] + + +{-| -} +decodeImage : Decoder ImageData +decodeImage = + Decode.succeed ImageData + |> required "id" Decode.int + |> required "image" Decode.string + |> required "name" Decode.string + |> required "opacity" Decode.float + |> required "visible" Decode.bool + |> required "x" Decode.float + |> required "y" Decode.float + |> optional "transparentcolor" Decode.string "none" + |> optional "properties" Properties.decode Dict.empty + + +{-| -} +decodeObjectLayer : Decoder ObjectData +decodeObjectLayer = + Decode.succeed ObjectData + |> required "id" Decode.int + |> required "draworder" decodeDraworder + |> required "name" Decode.string + |> required "objects" (list Object.decode) + |> required "opacity" Decode.float + |> required "visible" Decode.bool + |> required "x" Decode.float + |> required "y" Decode.float + |> optional "color" Decode.string "none" + |> optional "properties" Properties.decode Dict.empty + + +{-| -} +decodeDraworder : Decoder DrawOrder +decodeDraworder = + Decode.string + |> Decode.andThen + (\result -> + case result of + "topdown" -> + Decode.succeed TopDown + + "index" -> + Decode.succeed Index + + _ -> + Decode.fail "Unknow render order" + ) + + +encodeDraworder do = + (case do of + TopDown -> + "topdown" + + Index -> + "index" + ) + |> Encode.string + + +{-| -} +decodeTile : Decoder TileData +decodeTile = + Decode.succeed Tuple.pair + |> optional "encoding" Decode.string "none" + |> optional "compression" Decode.string "none" + |> Decode.andThen + (\( encoding, compression ) -> + Decode.succeed TileData + |> required "id" Decode.int + |> required "data" (decodeTileData encoding compression) + |> required "name" Decode.string + |> required "opacity" Decode.float + |> required "visible" Decode.bool + |> required "width" Decode.int + |> required "height" Decode.int + |> required "x" Decode.float + |> required "y" Decode.float + |> optional "properties" Properties.decode Dict.empty + ) + + +decodeTileChunkedData : Decoder TileChunkedData +decodeTileChunkedData = + Decode.succeed Tuple.pair + |> optional "encoding" Decode.string "none" + |> optional "compression" Decode.string "none" + |> Decode.andThen + (\( encoding, compression ) -> + Decode.succeed TileChunkedData + |> required "id" Decode.int + |> required "chunks" (Decode.list (decodeChunk encoding compression)) + |> required "name" Decode.string + |> required "opacity" Decode.float + |> required "visible" Decode.bool + |> required "width" Decode.int + |> required "height" Decode.int + |> required "startx" Decode.int + |> required "starty" Decode.int + |> required "x" Decode.float + |> required "y" Decode.float + |> optional "properties" Properties.decode Dict.empty + ) + + +decodeTileData : String -> String -> Decoder (List Int) +decodeTileData encoding compression = + if compression /= "none" then + -- https://package.elm-lang.org/packages/jxxcarlson/elm-tar/latest/Tar + Decode.fail "Tile layer compression not supported yet" + + else if encoding == "base64" then + Decode.fail "Tile layer encoded not supported yet" + -- Decode.string + -- |> Decode.andThen + -- (\string -> + -- string + -- |> BinaryBase64.decode + -- |> Result.map (\data -> Decode.succeed (convertTilesData data)) + -- |> resultExtract (\err -> Decode.fail err) + -- ) + + else + Decode.list Decode.int + + +decodeChunk : String -> String -> Decoder Chunk +decodeChunk encoding compression = + Decode.succeed Chunk + |> required "data" (decodeTileData encoding compression) + |> required "height" Decode.int + |> required "width" Decode.int + |> required "x" Decode.int + |> required "y" Decode.int + + +encodeChunk : Chunk -> Encode.Value +encodeChunk data = + [ ( "data", Encode.list Encode.int data.data ) + , ( "height", Encode.int data.height ) + , ( "width", Encode.int data.width ) + , ( "x", Encode.int data.x ) + , ( "y", Encode.int data.y ) + ] + |> Encode.object + + +{-| + + - `image` - Image used as background + - `name` Name assigned to this layer + - `x` Horizontal layer offset in tiles. Always 0. + - `y` Vertical layer offset in tiles. Always 0. + - `opacity` Value between 0 and 1 + - `properties` A list of properties (name, value, type). + - `visible` Whether layer is shown or hidden in editor + +-} +type alias ImageData = + { id : Int + , image : String + , name : String + , opacity : Float + , visible : Bool + , x : Float + , y : Float + , transparentcolor : String + , properties : Properties + } + + +{-| + + - `name` Name assigned to this layer + - `x` Horizontal layer offset in tiles. Always 0. + - `y` Vertical layer offset in tiles. Always 0. + - `draworder` [`TopDown`](Tiled.Layer#DrawOrder) (default) + - `objects` List of objects. objectgroup only. + - `opacity` Value between 0 and 1 + - `properties` A list of properties (name, value, type). + - `visible` Whether layer is shown or hidden in editor + +-} +type alias ObjectData = + { id : Int + , draworder : DrawOrder + , name : String + , objects : List Object + , opacity : Float + , visible : Bool + , x : Float + , y : Float + , color : String + , properties : Properties + } + + +{-| -} +type DrawOrder + = TopDown + | Index + + +{-| + + - `data` List of GIDs from [`Tileset`](Tiled.Tileset#Tileset). + - `name` Name assigned to this layer + - `x` Horizontal layer offset in tiles. Always 0. + - `y` Vertical layer offset in tiles. Always 0. + - `width` Column count. Same as map width for fixed-size maps. + - `height` Row count. Same as map height for fixed-size maps. + - `opacity` Value between 0 and 1 + - `properties` A list of properties (name, value, type). + - `visible` Whether layer is shown or hidden in editor + +-} +type alias TileData = + { id : Int + , data : List Int + , name : String + , opacity : Float + , visible : Bool + , width : Int + , height : Int + , x : Float + , y : Float + , properties : Properties + } + + +type alias TileChunkedData = + { id : Int + , chunks : List Chunk + , name : String + , opacity : Float + , visible : Bool + , width : Int + , height : Int + , startx : Int + , starty : Int + , x : Float + , y : Float + , properties : Properties + } + + +{-| + + - `data` array or string Array of unsigned int (GIDs) or base64-encoded data + - `height` int Height in tiles + - `width` int Width in tiles + - `x` int X coordinate in tiles + - `y` int Y coordinate in tiles + +-} +type alias Chunk = + { data : List Int + , height : Int + , width : Int + , x : Int + , y : Int + } diff --git a/src/Tiled/Level.elm b/src/Tiled/Level.elm new file mode 100644 index 0000000..6a188a3 --- /dev/null +++ b/src/Tiled/Level.elm @@ -0,0 +1,339 @@ +module Tiled.Level exposing + ( Level(..) + , decode, encode + , LevelData, StaggeredLevelData, ExtensibleLevelData + ) + +{-| + +@docs Level +@docs decode, encode + + +# Level Data + +@docs LevelData, StaggeredLevelData, ExtensibleLevelData + +-} + +import Dict +import Json.Decode as Decode exposing (Decoder) +import Json.Decode.Pipeline exposing (optional, required) +import Json.Encode as Encode +import Tiled.Layer as Layer exposing (Layer) +import Tiled.Properties as Properties exposing (Properties) +import Tiled.Tileset as Tileset exposing (Tileset) + + +{-| Tiled can export maps as JSON files. That is elm representation of that structure. +-} +type Level + = Orthogonal LevelData + | Isometric LevelData + | Staggered StaggeredLevelData + | Hexagonal StaggeredLevelData + + +{-| -} +type alias ExtensibleLevelData addition = + { addition + | backgroundcolor : String + , height : Int + , infinite : Bool + , layers : List Layer + , nextobjectid : Int + , renderorder : RenderOrder + , tiledversion : String + , tileheight : Int + , tilesets : List Tileset + , tilewidth : Int + , version : Float + , width : Int + , properties : Properties + } + + +{-| -} +type alias LevelData = + ExtensibleLevelData {} + + +{-| -} +type alias StaggeredLevelData = + ExtensibleLevelData + { hexsidelength : Int + , staggeraxis : Axis + , staggerindex : OddOrEven + } + + +{-| -} +type RenderOrder + = RightDown + | RightUp + | LeftDown + | LeftUp + + +type Axis + = X + | Y + + +type OddOrEven + = Odd + | Even + + +{-| -} +decode : Decoder Level +decode = + Decode.field "orientation" Decode.string + |> Decode.andThen + (\orientation -> + case orientation of + "orthogonal" -> + decodeLevelData |> Decode.map Orthogonal + + "isometric" -> + decodeLevelData |> Decode.map Isometric + + "staggered" -> + decodeStaggeredlevelData |> Decode.map Staggered + + "hexagonal" -> + decodeStaggeredlevelData |> Decode.map Hexagonal + + _ -> + Decode.fail ("Unknown orientation `" ++ orientation ++ "`") + ) + + +{-| -} +encode : Level -> Encode.Value +encode l = + let + result = + case l of + Orthogonal data -> + encodeLevelData data [ ( "orientation", Encode.string "orthogonal" ) ] + + Isometric data -> + encodeLevelData data [ ( "orientation", Encode.string "isometric" ) ] + + Staggered data -> + encodeStaggered data + ++ [ ( "orientation", Encode.string "staggered" ) ] + |> encodeLevelData data + + Hexagonal data -> + encodeStaggered data + |> (++) [ ( "orientation", Encode.string "hexagonal" ) ] + |> encodeLevelData data + in + result + + +encodeLevelData : ExtensibleLevelData a -> List ( String, Encode.Value ) -> Encode.Value +encodeLevelData record addition = + let + layers = + Encode.list Layer.encode record.layers + in + Encode.object + ([ ( "backgroundcolor", Encode.string <| record.backgroundcolor ) + , ( "height", Encode.int <| record.height ) + , ( "infinite", Encode.bool <| record.infinite ) + , ( "layers", layers ) + , ( "nextlayerid", Encode.int <| List.length record.layers ) + , ( "nextobjectid", Encode.int <| record.nextobjectid ) + , ( "renderorder", encodeRenderOrder <| record.renderorder ) + , ( "tiledversion", Encode.string <| record.tiledversion ) + , ( "tileheight", Encode.int <| record.tileheight ) + , ( "tilesets", Encode.list Tileset.encode record.tilesets ) + , ( "tilewidth", Encode.int <| record.tilewidth ) + , ( "type", Encode.string <| "map" ) + , ( "version", Encode.float <| record.version ) + , ( "width", Encode.int <| record.width ) + ] + ++ addition + ++ (if Dict.isEmpty record.properties then + [] + + else + [ ( "properties", Properties.encode record.properties ) ] + ) + ) + + +encodeStaggered : StaggeredLevelData -> List ( String, Encode.Value ) +encodeStaggered record = + let + staggeraxis = + case record.staggeraxis of + X -> + "x" + + Y -> + "y" + + staggerindex = + case record.staggerindex of + Odd -> + "odd" + + Even -> + "even" + in + [ ( "hexsidelength", Encode.int <| record.hexsidelength ) + , ( "staggeraxis", Encode.string <| staggeraxis ) + , ( "staggerindex", Encode.string <| staggerindex ) + ] + + +encodeRenderOrder ro = + (case ro of + RightDown -> + "right-down" + + RightUp -> + "right-up" + + LeftDown -> + "right-down" + + LeftUp -> + "right-down" + ) + |> Encode.string + + +decodeLevelData : Decoder LevelData +decodeLevelData = + Decode.succeed + (\backgroundcolor height infinite layers nextobjectid renderorder tiledversion tileheight tilesets tilewidth version width props -> + { backgroundcolor = backgroundcolor + , height = height + , infinite = infinite + , layers = layers + , nextobjectid = nextobjectid + , renderorder = renderorder + , tiledversion = tiledversion + , tileheight = tileheight + , tilesets = tilesets + , tilewidth = tilewidth + , version = version + , width = width + , properties = props + } + ) + |> optional "backgroundcolor" Decode.string "" + |> required "height" Decode.int + |> required "infinite" Decode.bool + |> required "layers" (Decode.list Layer.decode) + |> required "nextobjectid" Decode.int + |> required "renderorder" decodeRenderOrder + |> required "tiledversion" Decode.string + |> required "tileheight" Decode.int + |> required "tilesets" (Decode.list Tileset.decode) + |> required "tilewidth" Decode.int + |> required "version" Decode.float + |> required "width" Decode.int + |> optional "properties" Properties.decode Dict.empty + + +decodeStaggeredlevelData : Decoder StaggeredLevelData +decodeStaggeredlevelData = + Decode.succeed + (\backgroundcolor height infinite layers nextobjectid renderorder tiledversion tileheight tilesets tilewidth version width props hexsidelength staggeraxis staggerindex -> + { backgroundcolor = backgroundcolor + , height = height + , hexsidelength = hexsidelength + , infinite = infinite + , layers = layers + , nextobjectid = nextobjectid + , renderorder = renderorder + , staggeraxis = staggeraxis + , staggerindex = staggerindex + , tiledversion = tiledversion + , tileheight = tileheight + , tilesets = tilesets + , tilewidth = tilewidth + , version = version + , width = width + , properties = props + } + ) + |> optional "backgroundcolor" Decode.string "" + |> required "height" Decode.int + |> required "infinite" Decode.bool + |> required "layers" (Decode.list Layer.decode) + |> required "nextobjectid" Decode.int + |> required "renderorder" decodeRenderOrder + |> required "tiledversion" Decode.string + |> required "tileheight" Decode.int + |> required "tilesets" (Decode.list Tileset.decode) + |> required "tilewidth" Decode.int + |> required "version" Decode.float + |> required "width" Decode.int + |> optional "properties" Properties.decode Dict.empty + |> required "hexsidelength" Decode.int + |> required "staggeraxis" decodeAxis + |> required "staggerindex" decodeOddOrEven + + +decodeRenderOrder : Decoder RenderOrder +decodeRenderOrder = + Decode.string + |> Decode.andThen + (\result -> + case result of + "right-down" -> + Decode.succeed RightDown + + "right-up" -> + Decode.succeed RightUp + + "left-down" -> + Decode.succeed LeftDown + + "left-up" -> + Decode.succeed LeftUp + + _ -> + Decode.fail "Unknow render order" + ) + + +decodeAxis : Decoder Axis +decodeAxis = + Decode.string + |> Decode.andThen + (\a -> + case a of + "x" -> + Decode.succeed X + + "y" -> + Decode.succeed Y + + _ -> + Decode.fail <| "Uknown axis `" ++ a ++ "`" + ) + + +decodeOddOrEven : Decoder OddOrEven +decodeOddOrEven = + Decode.string + |> Decode.andThen + (\a -> + case a of + "odd" -> + Decode.succeed Odd + + "even" -> + Decode.succeed Even + + _ -> + Decode.fail <| "Uknown axis `" ++ a ++ "`" + ) diff --git a/src/Tiled/Object.elm b/src/Tiled/Object.elm new file mode 100644 index 0000000..8eed6dc --- /dev/null +++ b/src/Tiled/Object.elm @@ -0,0 +1,292 @@ +module Tiled.Object exposing + ( Object(..), decode, encode + , CommonDimension, CommonDimensionGid, CommonDimensionPolyPoints + , Common, Dimension, Gid, PolyPoints + ) + +{-| + +@docs Object, decode, encode +@docs CommonDimension, CommonDimensionGid, CommonDimensionPolyPoints +@docs Common, Dimension, Gid, PolyPoints + +-} + +import Dict exposing (Dict) +import Json.Decode as Decode exposing (Decoder) +import Json.Decode.Extra exposing (when) +import Json.Decode.Pipeline exposing (optional, required) +import Json.Encode as Encode +import Tiled.Properties as Properties exposing (Properties) + + +{-| -} +type Object + = Point (Common {}) + | Rectangle CommonDimension + | Ellipse CommonDimension + | Polygon CommonDimensionPolyPoints + | PolyLine CommonDimensionPolyPoints + | Tile CommonDimensionGid + + +{-| Do i really need ID here? +-} +type alias Common a = + { a + | id : Int + , name : String + , kind : String + , visible : Bool + , x : Float + , y : Float + , rotation : Float + , properties : Properties + } + + +{-| -} +type alias Dimension a = + { a + | width : Float + , height : Float + } + + +{-| -} +type alias Gid = + Int + + +{-| -} +type alias PolyPoints = + List { x : Float, y : Float } + + +type alias CommonDimension = + Common (Dimension {}) + + +type alias CommonDimensionGid = + Common (Dimension { gid : Gid }) + + +type alias CommonDimensionPolyPoints = + Common (Dimension { points : List { x : Float, y : Float } }) + + +commonDimension : Common a -> Dimension a -> CommonDimension +commonDimension a b = + { id = a.id + , name = a.name + , kind = a.kind + , visible = a.visible + , x = a.x + , y = a.y + , rotation = a.rotation + , properties = a.properties + , width = b.width + , height = b.height + } + + +commonDimensionArgsGid : Common a -> Dimension a -> Gid -> CommonDimensionGid +commonDimensionArgsGid a b c = + { id = a.id + , name = a.name + , kind = a.kind + , visible = a.visible + , x = a.x + , y = a.y + , rotation = a.rotation + , properties = a.properties + , width = b.width + , height = b.height + , gid = c + } + + +commonDimensionPolyPoints : Common a -> Dimension a -> PolyPoints -> CommonDimensionPolyPoints +commonDimensionPolyPoints a b c = + { id = a.id + , name = a.name + , kind = a.kind + , visible = a.visible + , x = a.x + , y = a.y + , rotation = a.rotation + , properties = a.properties + , width = b.width + , height = b.height + , points = c + } + + +encodeProps data = + if Dict.isEmpty data then + [] + + else + [ ( "properties", Properties.encode data ) ] + + +{-| -} +encode : Object -> Encode.Value +encode obj = + let + common data = + [ ( "id", Encode.int data.id ) + , ( "name", Encode.string data.name ) + , ( "type", Encode.string data.kind ) + , ( "visible", Encode.bool data.visible ) + , ( "x", Encode.float data.x ) + , ( "y", Encode.float data.y ) + , ( "rotation", Encode.float data.rotation ) + ] + ++ encodeProps data.properties + + dimension data = + [ ( "width", Encode.float data.width ) + , ( "height", Encode.float data.height ) + ] + + encodePolyPoint { x, y } = + Encode.object + [ ( "x", x |> Encode.float ) + , ( "y", y |> Encode.float ) + ] + in + case obj of + Point data -> + data + |> common + |> (++) [ ( "width", Encode.float 0 ), ( "height", Encode.float 0 ), ( "point", Encode.bool True ) ] + |> Encode.object + + Rectangle data -> + data + |> common + |> (++) (dimension data) + |> List.sortBy Tuple.first + |> Encode.object + + Ellipse data -> + data + |> common + |> (++) (dimension data) + |> (::) ( "ellipse", Encode.bool True ) + |> Encode.object + + Polygon data -> + data + |> common + |> (++) (dimension data) + |> (::) ( "polygon", Encode.list encodePolyPoint data.points ) + |> Encode.object + + PolyLine data -> + data + |> common + |> (++) (dimension data) + |> (::) ( "polyline", Encode.list encodePolyPoint data.points ) + |> Encode.object + + Tile data -> + data + |> common + |> (++) (dimension data) + |> (::) ( "gid", Encode.int data.gid ) + |> Encode.object + + +{-| -} +decode : Decoder Object +decode = + let + point = + Decode.map Point decodeCommon + |> when (Decode.field "point" Decode.bool) ((==) True) + + ellipse = + Decode.map2 commonDimension decodeCommon decodeDimension + |> Decode.map Ellipse + |> when (Decode.field "ellipse" Decode.bool) ((==) True) + + polygon = + Decode.field "polygon" decodePolyPoints + |> Decode.map3 commonDimensionPolyPoints decodeCommon decodeDimension + |> Decode.map Polygon + + polyline = + Decode.field "polyline" decodePolyPoints + |> Decode.map3 commonDimensionPolyPoints decodeCommon decodeDimension + |> Decode.map PolyLine + + tile = + Decode.map3 commonDimensionArgsGid decodeCommon decodeDimension decodeGid + |> Decode.map Tile + |> when (Decode.field "gid" Decode.int) ((<) 0) + + rectangle = + Decode.map2 commonDimension decodeCommon decodeDimension + |> Decode.map Rectangle + in + Decode.oneOf + [ point + , ellipse + , tile + , polygon + , polyline + , rectangle + ] + + +decodeCommon : Decoder (Common {}) +decodeCommon = + let + common id name kind visible x y rotation properties = + { id = id + , name = name + , kind = kind + , visible = visible + , x = x + , y = y + , rotation = rotation + , properties = properties + } + in + Decode.succeed common + |> required "id" Decode.int + |> required "name" Decode.string + |> required "type" Decode.string + |> required "visible" Decode.bool + |> required "x" Decode.float + |> required "y" Decode.float + |> required "rotation" Decode.float + |> optional "properties" Properties.decode Dict.empty + + +decodeDimension : Decoder (Dimension {}) +decodeDimension = + let + dimension width height = + { width = width + , height = height + } + in + Decode.succeed dimension + |> required "width" Decode.float + |> required "height" Decode.float + + +decodePolyPoints : Decoder PolyPoints +decodePolyPoints = + Decode.succeed (\x y -> { x = x, y = y }) + |> required "x" Decode.float + |> required "y" Decode.float + |> Decode.list + + +decodeGid : Decoder Gid +decodeGid = + Decode.field "gid" Decode.int diff --git a/src/Tiled/Properties.elm b/src/Tiled/Properties.elm new file mode 100644 index 0000000..b014ecd --- /dev/null +++ b/src/Tiled/Properties.elm @@ -0,0 +1,107 @@ +module Tiled.Properties exposing + ( Properties, Property(..) + , decode, encode + ) + +{-| + +@docs Properties, Property +@docs decode, encode + +-} + +import Dict exposing (Dict) +import Json.Decode as Decode exposing (Decoder) +import Json.Decode.Pipeline exposing (required) +import Json.Encode as Encode + + +{-| -} +type alias Properties = + Dict String Property + + +{-| Custom properties values +-} +type Property + = PropBool Bool + | PropInt Int + | PropFloat Float + | PropString String + | PropColor String + | PropFile String + + +{-| -} +decode : Decoder Properties +decode = + Decode.list + (Decode.succeed identity + |> required "type" Decode.string + |> Decode.andThen + (\kind -> + Decode.succeed (\a b -> ( a, b )) + |> required "name" Decode.string + |> required "value" (decodeProperty kind) + ) + ) + |> Decode.map Dict.fromList + + +{-| -} +encode : Properties -> Encode.Value +encode props = + props + |> Dict.toList + |> Encode.list + (\( key, value ) -> + Encode.object + ([ ( "name", Encode.string key ) + ] + ++ (case value of + PropBool v -> + [ ( "type", Encode.string "bool" ), ( "value", Encode.bool v ) ] + + PropInt v -> + [ ( "type", Encode.string "int" ), ( "value", Encode.int v ) ] + + PropFloat v -> + [ ( "type", Encode.string "float" ), ( "value", Encode.float v ) ] + + PropString v -> + [ ( "type", Encode.string "string" ), ( "value", Encode.string v ) ] + + PropColor v -> + [ ( "type", Encode.string "color" ), ( "value", Encode.string v ) ] + + PropFile v -> + [ ( "type", Encode.string "file" ), ( "value", Encode.string v ) ] + ) + ) + ) + + +{-| -} +decodeProperty : String -> Decoder Property +decodeProperty typeString = + case typeString of + "bool" -> + Decode.map PropBool Decode.bool + + "color" -> + Decode.map PropColor Decode.string + + "float" -> + Decode.map PropFloat Decode.float + + "file" -> + Decode.map PropFile Decode.string + + "int" -> + Decode.map PropInt Decode.int + + "string" -> + Decode.map PropString Decode.string + + _ -> + Decode.fail <| "I can't decode the type " ++ typeString diff --git a/src/Tiled/Tileset.elm b/src/Tiled/Tileset.elm new file mode 100644 index 0000000..be5e003 --- /dev/null +++ b/src/Tiled/Tileset.elm @@ -0,0 +1,478 @@ +module Tiled.Tileset exposing + ( Tileset(..), decode, encode + , SourceTileData, EmbeddedTileData, ImageCollectionTileData + , TilesData, ImageCollectionTileDataTile, TilesDataObjectgroup, SpriteAnimation + , GridData + , decodeTilesData, encodeTilesData + , decodeFile, decodeTiles + ) + +{-| + +@docs Tileset, decode, encode +@docs SourceTileData, EmbeddedTileData, ImageCollectionTileData +@docs TilesData, ImageCollectionTileDataTile, TilesDataObjectgroup, SpriteAnimation + + +## Internal stuff + +@docs GridData + + +## stuff to delete + +@docs decodeTilesData, encodeTilesData + +-} + +import Dict exposing (Dict) +import Json.Decode as Decode exposing (Decoder) +import Json.Decode.Pipeline exposing (custom, hardcoded, optional, required) +import Json.Encode as Encode +import Tiled.Layer as Layer exposing (DrawOrder) +import Tiled.Object as Object exposing (Object) +import Tiled.Properties as Properties exposing (Properties) + + +{-| -} +type Tileset + = Source SourceTileData + | Embedded EmbeddedTileData + | ImageCollection ImageCollectionTileData + + +{-| -} +decode : Decoder Tileset +decode = + Decode.oneOf + [ decodeEmbeddedTileset (required "firstgid" Decode.int) + , decodeSourceTileset + , decodeImageCollectionTileData (required "firstgid" Decode.int) + ] + + +decodeFile : Int -> Decoder Tileset +decodeFile firstgid = + Decode.oneOf + [ decodeEmbeddedTileset (hardcoded firstgid) + , decodeImageCollectionTileData (hardcoded firstgid) + ] + + +{-| -} +encode : Tileset -> Encode.Value +encode tileset = + let + transparentcolor data = + if data == "none" then + [] + + else + [ ( "transparentcolor", Encode.string data ) ] + + encodeImageCollectionTileDataTile : ( Int, ImageCollectionTileDataTile ) -> Encode.Value + encodeImageCollectionTileDataTile ( id, data ) = + ([ ( "id", Encode.int id ) + , ( "image", Encode.string data.image ) + , ( "imageheight", Encode.int data.imageheight ) + , ( "imagewidth", Encode.int data.imagewidth ) + ] + |> addAnimationIf data.animation + ) + ++ (data.objectgroup |> Maybe.map (\a -> [ ( "objectgroup", encodeTilesDataObjectgroup a ) ]) |> Maybe.withDefault []) + ++ ([] |> encodeProps data.properties) + |> Encode.object + in + case tileset of + Source { firstgid, source } -> + Encode.object [ ( "firstgid", Encode.int firstgid ), ( "source", Encode.string source ) ] + + Embedded data -> + let + encodeTilesIf d = + if Dict.isEmpty d then + identity + + else + (::) ( "tiles", Encode.list encodeTilesData (Dict.toList d) ) + in + (( "columns", Encode.int data.columns ) + :: ( "firstgid", Encode.int data.firstgid ) + :: ( "image", Encode.string data.image ) + :: ( "imageheight", Encode.int data.imageheight ) + :: ( "imagewidth", Encode.int data.imagewidth ) + :: ( "margin", Encode.int data.margin ) + :: ( "name", Encode.string data.name ) + :: ( "spacing", Encode.int data.spacing ) + :: ( "tilecount", Encode.int data.tilecount ) + :: ( "tileheight", Encode.int data.tileheight ) + :: ([] + |> encodeProps data.properties + |> encodeTilesIf data.tiles + ) + ) + ++ [ ( "tilewidth", Encode.int data.tilewidth ) ] + ++ transparentcolor data.transparentcolor + |> Encode.object + + ImageCollection data -> + ([ ( "columns", Encode.int data.columns ) + , ( "firstgid", Encode.int data.firstgid ) + ] + ++ (data.grid |> Maybe.map (encodeGridData >> Tuple.pair "grid" >> List.singleton) |> Maybe.withDefault []) + ++ [ ( "margin", Encode.int data.margin ) + , ( "name", Encode.string data.name ) + , ( "spacing", Encode.int data.spacing ) + , ( "tilecount", Encode.int data.tilecount ) + , ( "tileheight", Encode.int data.tileheight ) + , ( "tiles", Encode.list encodeImageCollectionTileDataTile (Dict.toList data.tiles) ) + , ( "tilewidth", Encode.int data.tilewidth ) + ] + |> encodeProps data.properties + ) + |> Encode.object + + +addAnimationIf l = + if l == [] then + identity + + else + (::) ( "animation", Encode.list encodeSpriteAnimation l ) + + +encodeTilesData ( id, data ) = + [] + |> encodeProps data.properties + |> (++) (data.objectgroup |> Maybe.map (\a -> [ ( "objectgroup", encodeTilesDataObjectgroup a ) ]) |> Maybe.withDefault []) + |> addAnimationIf data.animation + |> (::) ( "id", Encode.int id ) + |> Encode.object + + +encodeProps data = + if Dict.isEmpty data then + identity + + else + (::) ( "properties", Properties.encode data ) + + +encodeTilesDataObjectgroup data = + Encode.object + [ ( "draworder", Layer.encodeDraworder data.draworder ) + , ( "name", Encode.string data.name ) + , ( "objects", Encode.list Object.encode data.objects ) + , ( "opacity", Encode.int data.opacity ) + , ( "type", Encode.string "objectgroup" ) + , ( "visible", Encode.bool data.visible ) + , ( "x", Encode.int data.x ) + , ( "y", Encode.int data.y ) + ] + + +encodeSpriteAnimation data = + Encode.object + [ ( "duration", Encode.int data.duration ) + , ( "tileid", Encode.int data.tileid ) + ] + + +{-| -} + + + +-- decodeEmbeddedTileset : Decoder Tileset + + +decodeEmbeddedTileset firstgid = + Decode.succeed EmbeddedTileData + |> required "columns" Decode.int + -- |> required "firstgid" Decode.int + |> firstgid + |> required "image" Decode.string + |> required "imageheight" Decode.int + |> required "imagewidth" Decode.int + |> required "margin" Decode.int + |> required "name" Decode.string + |> required "spacing" Decode.int + |> required "tilecount" Decode.int + |> required "tileheight" Decode.int + |> required "tilewidth" Decode.int + |> optional "transparentcolor" Decode.string "none" + |> optional "tiles" decodeTiles Dict.empty + |> optional "properties" Properties.decode Dict.empty + |> Decode.map Embedded + + +decodeSourceTileset : Decoder Tileset +decodeSourceTileset = + Decode.succeed SourceTileData + |> required "firstgid" Decode.int + |> required "source" Decode.string + |> Decode.map Source + + +decodeTiles : Decoder (Dict Int TilesData) +decodeTiles = + Decode.list decodeTilesData + |> Decode.andThen + (List.foldl + (\info_ acc -> + case info_ of + Just info -> + acc + |> Decode.andThen + (Dict.insert info.id + { objectgroup = info.objectgroup + , animation = info.animation + , properties = info.properties + } + >> Decode.succeed + ) + + Nothing -> + acc + ) + (Decode.succeed Dict.empty) + ) + + +decodeTilesData : Decoder (Maybe (TilesDataPlain { id : Int })) +decodeTilesData = + Decode.succeed + (\a b c d -> + case ( a, b, Dict.toList c ) of + ( [], Nothing, [] ) -> + Nothing + + _ -> + Just + { animation = a + , objectgroup = b + , properties = c + , id = d + } + ) + |> optional "animation" (Decode.list decodeSpriteAnimation) [] + |> optional "objectgroup" (Decode.maybe decodeTilesDataObjectgroup) Nothing + |> optional "properties" Properties.decode Dict.empty + |> required "id" Decode.int + + +{-| -} +decodeTilesDataObjectgroup : Decoder TilesDataObjectgroup +decodeTilesDataObjectgroup = + Decode.succeed TilesDataObjectgroup + |> required "draworder" Layer.decodeDraworder + |> required "name" Decode.string + |> required "objects" (Decode.list Object.decode) + |> required "opacity" Decode.int + |> required "visible" Decode.bool + |> required "x" Decode.int + |> required "y" Decode.int + + +{-| -} +decodeSpriteAnimation : Decoder SpriteAnimation +decodeSpriteAnimation = + Decode.map2 SpriteAnimation + (Decode.field "duration" Decode.int) + (Decode.field "tileid" Decode.int) + + +{-| -} + + + +-- decodeImageCollectionTileData : Decode.Decoder Tileset + + +decodeImageCollectionTileData firstgid = + Decode.succeed ImageCollectionTileData + |> required "columns" Decode.int + |> firstgid + -- |> required "firstgid" Decode.int + |> required "margin" Decode.int + |> required "name" Decode.string + |> required "spacing" Decode.int + |> required "tilecount" Decode.int + |> required "tilewidth" Decode.int + |> required "tileheight" Decode.int + |> custom decodeImageCollectionTileDataTiles + |> optional "properties" Properties.decode Dict.empty + |> optional "grid" (Decode.map Just decodeGrid) Nothing + |> Decode.map ImageCollection + + +decodeImageCollectionTileDataTiles : Decoder (Dict Int ImageCollectionTileDataTile) +decodeImageCollectionTileDataTiles = + let + decodeImageTile = + Decode.succeed + (\id image imageheight imagewidth animation objectgroup properties -> + ( id + , { image = image + , imageheight = imageheight + , imagewidth = imagewidth + , animation = animation + , objectgroup = objectgroup + , properties = properties + } + ) + ) + |> required "id" Decode.int + |> required "image" Decode.string + |> required "imageheight" Decode.int + |> required "imagewidth" Decode.int + |> optional "animation" (Decode.list decodeSpriteAnimation) [] + |> optional "objectgroup" (Decode.maybe decodeTilesDataObjectgroup) Nothing + |> optional "properties" Properties.decode Dict.empty + + -- (Decode.maybe (Decode.field "objectgroup" decodeTilesDataObjectgroup)) + -- (Decode.maybe Properties.decode |> Decode.map (Maybe.withDefault Dict.empty)) + -- + in + Decode.field "tiles" (Decode.list decodeImageTile) + |> Decode.map (List.foldl (\( i, v ) acc -> Dict.insert i v acc) Dict.empty) + + +type alias GridData = + { height : Int + , orientation : String + , width : Int + } + + +decodeGrid : Decoder GridData +decodeGrid = + Decode.succeed GridData + |> required "height" Decode.int + |> required "orientation" Decode.string + |> required "width" Decode.int + + +encodeGridData : GridData -> Encode.Value +encodeGridData data = + Encode.object + [ ( "height", Encode.int data.height ) + , ( "orientation", Encode.string data.orientation ) + , ( "width", Encode.int data.width ) + ] + + +{-| + + - `columns` The number of tile columns in the tileset + - `firstgid` GID corresponding to the first tile in the set + - `margin` Buffer between image edge and first tile (pixels) + - `name` Name given to this tileset + - `spacing` Spacing between adjacent tiles in image (pixels) + - `tilecount` The number of tiles in this tileset + - `tileheight` Maximum height of tiles in this set + - `tiles` Dict of [`ImageCollectionTileDataTile`](#ImageCollectionTileDataTile) + - `tilewidth` Maximum width of tiles in this set + - `properties` A list of properties (name, value, type). + +-} +type alias ImageCollectionTileData = + { columns : Int + , firstgid : Int + , margin : Int + , name : String + , spacing : Int + , tilecount : Int + , tilewidth : Int + , tileheight : Int + , tiles : Dict Int ImageCollectionTileDataTile + , properties : Properties + , grid : Maybe GridData + } + + +{-| -} +type alias ImageCollectionTileDataTile = + TilesDataPlain + { image : String + , imageheight : Int + , imagewidth : Int + } + + +{-| + + - `firstgid` GID corresponding to the first tile in the set + - `source` url to `EmbeddedTileData` + +-} +type alias SourceTileData = + { firstgid : Int + , source : String + } + + +{-| + + - `columns` The number of tile columns in the tileset + - `firstgid` GID corresponding to the first tile in the set + - `image` Image used for tiles in this set + - `imagewidth` Width of source image in pixels + - `imageheight` Height of source image in pixels + - `margin` Buffer between image edge and first tile (pixels) + - `name` Name given to this tileset + - `spacing` Spacing between adjacent tiles in image (pixels) + - `tilecount` The number of tiles in this tileset + - `tileheight` Maximum height of tiles in this set + - `tiles` Dict of TilesData [`TilesData`](#TilesData) + - `tilewidth` Maximum width of tiles in this set + - `properties` A list of properties (name, value, type). + +-} +type alias EmbeddedTileData = + { columns : Int + , firstgid : Int + , image : String + , imageheight : Int + , imagewidth : Int + , margin : Int + , name : String + , spacing : Int + , tilecount : Int + , tileheight : Int + , tilewidth : Int + , transparentcolor : String + , tiles : Dict Int TilesData + , properties : Properties + } + + +type alias TilesData = + TilesDataPlain {} + + +{-| -} +type alias SpriteAnimation = + { duration : Int + , tileid : Int + } + + +{-| -} +type alias TilesDataObjectgroup = + { draworder : DrawOrder + , name : String + , objects : List Object + , opacity : Int + , visible : Bool + , x : Int + , y : Int + } + + +{-| -} +type alias TilesDataPlain a = + { a + | animation : List SpriteAnimation + , objectgroup : Maybe TilesDataObjectgroup + , properties : Properties + } diff --git a/tests/Generate.elm b/tests/Generate.elm new file mode 100644 index 0000000..a729409 --- /dev/null +++ b/tests/Generate.elm @@ -0,0 +1,336 @@ +module Generate exposing (layer, object, properties, tileset) + +import Dict exposing (Dict) +import Fuzz exposing (Fuzzer) +import Generate.Util exposing (rangeList) +import Tiled.Layer as Layer exposing (Layer(..)) +import Tiled.Object exposing (Common, CommonDimension, CommonDimensionGid, CommonDimensionPolyPoints, Dimension, Gid, Object(..), PolyPoints) +import Tiled.Properties exposing (Properties, Property(..)) +import Tiled.Tileset as Tileset exposing (Tileset(..)) + + +maxInt : Int +maxInt = + 2147483647 + + +fuzzId : Fuzzer Int +fuzzId = + Fuzz.intRange 1 maxInt + + +tileset = + { source = Fuzz.map Tileset.Source source + , embedded = Fuzz.map Tileset.Embedded embedded + , imageCollection = Fuzz.map Tileset.ImageCollection imageCollection + } + + +layer : Fuzzer Layer +layer = + let + imageData = + Fuzz.map Layer.ImageData fuzzId + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap Fuzz.bool + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap properties + + objectData = + Fuzz.map Layer.ObjectData fuzzId + |> Fuzz.andMap draworder + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap (Fuzz.list object) + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap Fuzz.bool + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap properties + + tileData = + Fuzz.map Layer.TileData fuzzId + |> Fuzz.andMap (Fuzz.list fuzzId) + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap Fuzz.bool + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap properties + in + Fuzz.oneOf + [ Fuzz.map Layer.Image imageData + , Fuzz.map Layer.Object objectData + , Fuzz.map Layer.Tile tileData + ] + + +property : Fuzzer Property +property = + Fuzz.oneOf + [ Fuzz.map PropBool Fuzz.bool + , Fuzz.map PropInt Fuzz.int + , Fuzz.map PropFloat Fuzz.float + , Fuzz.map PropString Fuzz.string + , Fuzz.map PropColor Fuzz.string + , Fuzz.map PropFile Fuzz.string + ] + + +properties : Fuzzer Properties +properties = + Fuzz.tuple ( Fuzz.string, property ) + |> Fuzz.list + |> Fuzz.map Dict.fromList + + +propertiesLimit : Int -> Fuzzer Properties +propertiesLimit max = + Fuzz.tuple ( Fuzz.string, property ) + |> rangeList 0 max + |> Fuzz.map Dict.fromList + + +propertiesLimit2 : Int -> Int -> Fuzzer Properties +propertiesLimit2 min max = + Fuzz.tuple ( Fuzz.string, property ) + |> rangeList min max + |> Fuzz.map Dict.fromList + + + +--oneProperty : Fuzzer Properties +--oneProperty = +-- Fuzz.tuple ( Fuzz.string, Fuzz.map PropBool Fuzz.bool ) +-- |> Fuzz.map (List.singleton >> Dict.fromList) + + +object : Fuzzer Object +object = + let + common : Fuzzer (Common {}) + common = + Fuzz.map common_ Fuzz.int + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap Fuzz.bool + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap Fuzz.float + |> Fuzz.andMap (propertiesLimit 5) + + common_ id name kind visible x y rotation properties_ = + { id = id + , name = name + , kind = kind + , visible = visible + , x = x + , y = y + , rotation = rotation + , properties = properties_ + } + + dimension : Fuzzer (Dimension {}) + dimension = + Fuzz.map2 dimension_ Fuzz.float Fuzz.float + + dimension_ width height = + { width = width + , height = height + } + + polyPoints : Fuzzer PolyPoints + polyPoints = + Fuzz.map2 (\p1 p2 -> { x = p1, y = p2 }) Fuzz.float Fuzz.float + |> rangeList 0 1 + in + Fuzz.oneOf + [ Fuzz.map Point common + , Fuzz.map Rectangle (Fuzz.map2 commonDimension common dimension) + , Fuzz.map Ellipse (Fuzz.map2 commonDimension common dimension) + , Fuzz.map Polygon (Fuzz.map3 commonDimensionPolyPoints common dimension polyPoints) + , Fuzz.map PolyLine (Fuzz.map3 commonDimensionPolyPoints common dimension polyPoints) + , Fuzz.map Tiled.Object.Tile (Fuzz.map3 commonDimensionArgsGid common dimension (Fuzz.intRange 1 maxInt)) + ] + + +draworder : Fuzzer Layer.DrawOrder +draworder = + Fuzz.oneOf [ Fuzz.constant Layer.Index, Fuzz.constant Layer.TopDown ] + + +source = + Fuzz.map2 Tileset.SourceTileData Fuzz.int Fuzz.string + + +objectgroup : Fuzzer Tileset.TilesDataObjectgroup +objectgroup = + Fuzz.map Tileset.TilesDataObjectgroup draworder + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap (Fuzz.list object) + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.bool + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + + +animation : Fuzzer Tileset.SpriteAnimation +animation = + Fuzz.map2 Tileset.SpriteAnimation Fuzz.int fuzzId + + +tilesData : Fuzzer Tileset.TilesData +tilesData = + let + build data = + Fuzz.map3 + (\a b c -> + { animation = a + , objectgroup = b + , properties = c + } + ) + data.a + data.b + data.c + + none = + { a = animation |> rangeList 0 5 + , b = Fuzz.maybe objectgroup + , c = propertiesLimit 10 + } + + all = + { a = animation |> rangeList 1 5 + , b = Fuzz.map Just objectgroup + , c = propertiesLimit2 1 10 + } + in + Fuzz.oneOf + [ build all + , build { none | a = all.a } + , build { none | b = all.b } + , build { none | c = all.c } + ] + + + +-- Fuzz.map3 +-- (\a b c -> +-- { animation = a +-- , objectgroup = b +-- , properties = c +-- } +-- ) +-- (animation |> rangeList 0 5) +-- (Fuzz.maybe objectgroup) +-- (propertiesLimit 10) + + +imageCollectionTileDataTile = + Fuzz.map + (\a b c d e f -> + { image = a + , imageheight = b + , imagewidth = c + , animation = d + , objectgroup = e + , properties = f + } + ) + Fuzz.string + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap (animation |> rangeList 0 1) + |> Fuzz.andMap (Fuzz.maybe objectgroup) + |> Fuzz.andMap (propertiesLimit 5) + + +embedded = + Fuzz.map Tileset.EmbeddedTileData Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap (Fuzz.tuple ( fuzzId, tilesData ) |> Fuzz.list |> Fuzz.map Dict.fromList) + |> Fuzz.andMap (propertiesLimit 3) + + +imageCollection = + Fuzz.map Tileset.ImageCollectionTileData Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap Fuzz.int + |> Fuzz.andMap (Fuzz.tuple ( fuzzId, imageCollectionTileDataTile ) |> rangeList 0 1 |> Fuzz.map Dict.fromList) + |> Fuzz.andMap (propertiesLimit 5) + |> Fuzz.andMap (Fuzz.maybe imageCollectionGrid) + + +imageCollectionGrid = + Fuzz.map Tileset.GridData Fuzz.int + |> Fuzz.andMap Fuzz.string + |> Fuzz.andMap Fuzz.int + + +commonDimension : Common a -> Dimension a -> CommonDimension +commonDimension a b = + { id = a.id + , name = a.name + , kind = a.kind + , visible = a.visible + , x = a.x + , y = a.y + , rotation = a.rotation + , properties = a.properties + , width = b.width + , height = b.height + } + + +commonDimensionArgsGid : Common a -> Dimension a -> Gid -> CommonDimensionGid +commonDimensionArgsGid a b c = + { id = a.id + , name = a.name + , kind = a.kind + , visible = a.visible + , x = a.x + , y = a.y + , rotation = a.rotation + , properties = a.properties + , width = b.width + , height = b.height + , gid = c + } + + +commonDimensionPolyPoints : Common a -> Dimension a -> PolyPoints -> CommonDimensionPolyPoints +commonDimensionPolyPoints a b c = + { id = a.id + , name = a.name + , kind = a.kind + , visible = a.visible + , x = a.x + , y = a.y + , rotation = a.rotation + , properties = a.properties + , width = b.width + , height = b.height + , points = c + } diff --git a/tests/Generate/Util.elm b/tests/Generate/Util.elm new file mode 100644 index 0000000..2c1e607 --- /dev/null +++ b/tests/Generate/Util.elm @@ -0,0 +1,10 @@ +module Generate.Util exposing (rangeList) + +import Fuzz exposing (Fuzzer) + + +rangeList : Int -> Int -> Fuzzer a -> Fuzzer (List a) +rangeList lo hi fuzzer = + Fuzz.intRange lo hi + |> Fuzz.map (\i -> List.repeat i "12") + |> Fuzz.map2 (\f l -> List.map (always f) l) fuzzer diff --git a/tests/Layer.elm b/tests/Layer.elm new file mode 100644 index 0000000..be89c7e --- /dev/null +++ b/tests/Layer.elm @@ -0,0 +1,19 @@ +module Layer exposing (suite) + +import Expect +import Generate +import Json.Decode +import Test exposing (..) +import Tiled.Layer exposing (decode, encode) + + +suite : Test +suite = + describe "Tiled.Layer" + [ fuzz Generate.layer "encode / decode" <| + \obj -> + obj + |> encode + |> Json.Decode.decodeValue decode + |> Expect.equal (Ok obj) + ] diff --git a/tests/Level.elm b/tests/Level.elm deleted file mode 100644 index d96ada6..0000000 --- a/tests/Level.elm +++ /dev/null @@ -1,42 +0,0 @@ -module Level exposing (..) - -import Dict -import Expect exposing (Expectation) -import Json.Decode exposing (decodeString) -import Mock -import Test exposing (..) -import Tiled.Decode as Tiled - - -suite : Test -suite = - describe "The Tiled.Decode module" - [ describe "Tiled.Decode.decode" - [ test "decode simple level" <| - \_ -> - case decodeString Tiled.decode Mock.bareMinimum of - Ok data -> - Expect.pass - - Err err -> - Expect.fail err - , test "geting props" <| - \_ -> - case decodeString Tiled.decode Mock.bareMinimum of - Ok { properties } -> - Expect.notEqual properties Dict.empty - - Err err -> - Expect.fail err - ] - , describe "Tiled.Decode.decodeTiles" - [ test "Animations" <| - \_ -> - decodeString Tiled.decodeTiles Mock.tilesDataOldAnimations - |> Expect.equal (decodeString Tiled.decodeTiles Mock.tilesDataNewAnimations) - , test "Properties" <| - \_ -> - decodeString Tiled.decodeTiles Mock.tilesDataOldWithProps - |> Expect.equal (decodeString Tiled.decodeTiles Mock.tilesDataNewWithProps) - ] - ] diff --git a/tests/Mock.elm b/tests/Mock.elm deleted file mode 100644 index 990f299..0000000 --- a/tests/Mock.elm +++ /dev/null @@ -1,59 +0,0 @@ -module Mock exposing (..) - - -bareMinimum : String -bareMinimum = - """{ "height":2, - "infinite":false, - "layers":[ - { - "data":"AAAAAAAAAAAAAAAAAAAAAA==", - "encoding":"base64", - "height":2, - "name":"Tile Layer 1", - "opacity":1, - "type":"tilelayer", - "visible":true, - "width":2, - "x":0, - "y":0 - }], - "nextobjectid":1, - "orientation":"orthogonal", - "properties": - { - "gravity":60 - }, - "propertytypes": - { - "gravity":"float" - }, - "renderorder":"right-down", - "tiledversion":"1.1.3", - "tileheight":16, - "tilesets":[], - "tilewidth":16, - "type":"map", - "version":1, - "width":2 -}""" - - -tilesDataNewWithProps : String -tilesDataNewWithProps = - """{"columns":4,"firstgid":1025,"image":"level2/char1_test.png","imageheight":120,"imagewidth":64,"margin":0,"name":"char","spacing":0,"tilecount":20,"tileheight":24,"tiles":[{"animation":[{"duration":100,"tileid":0}],"id":0,"objectgroup":{"draworder":"index","name":"","objects":[{"height":19,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":3,"y":5}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0},"properties":[{"name":"testFloatProp","type":"float","value":123}]},{"animation":[{"duration":100,"tileid":4},{"duration":100,"tileid":5},{"duration":100,"tileid":6},{"duration":100,"tileid":7}],"id":4,"objectgroup":{"draworder":"index","name":"","objects":[{"height":19,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":1,"y":5}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0},"properties":[{"name":"testIntProp","type":"int","value":12312}]},{"id":5,"objectgroup":{"draworder":"index","name":"","objects":[{"height":18,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":3,"y":6}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},{"id":6,"objectgroup":{"draworder":"index","name":"","objects":[{"height":17,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":4,"y":7}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},{"id":7,"objectgroup":{"draworder":"index","name":"","objects":[{"height":18,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":6,"y":6}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},{"animation":[{"duration":100,"tileid":8},{"duration":100,"tileid":9},{"duration":100,"tileid":10},{"duration":100,"tileid":11}],"id":8,"objectgroup":{"draworder":"index","name":"","objects":[{"height":5,"id":2,"name":"","rotation":0,"type":"","visible":true,"width":16,"x":0,"y":19}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},{"id":9,"objectgroup":{"draworder":"index","name":"","objects":[{"height":9,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":11,"x":3,"y":15}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},{"id":10,"objectgroup":{"draworder":"index","name":"","objects":[{"height":17,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":3,"y":7}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},{"id":11,"objectgroup":{"draworder":"index","name":"","objects":[{"height":19,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":4,"x":6,"y":5}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}}],"tilewidth":16,"transparentcolor":"#ff00ff"}""" - - -tilesDataOldWithProps : String -tilesDataOldWithProps = - """{"columns":4,"firstgid":1025,"image":"level2/char1_test.png","imageheight":120,"imagewidth":64,"margin":0,"name":"char","spacing":0,"tilecount":20,"tileheight":24,"tileproperties":{"0":{"testFloatProp":123},"4":{"testIntProp":12312}},"tilepropertytypes":{"0":{"testFloatProp":"float"},"4":{"testIntProp":"int"}},"tiles":{"0":{"animation":[{"duration":100,"tileid":0}],"objectgroup":{"draworder":"index","name":"","objects":[{"height":19,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":3,"y":5}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},"4":{"animation":[{"duration":100,"tileid":4},{"duration":100,"tileid":5},{"duration":100,"tileid":6},{"duration":100,"tileid":7}],"objectgroup":{"draworder":"index","name":"","objects":[{"height":19,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":1,"y":5}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},"5":{"objectgroup":{"draworder":"index","name":"","objects":[{"height":18,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":3,"y":6}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},"6":{"objectgroup":{"draworder":"index","name":"","objects":[{"height":17,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":4,"y":7}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},"7":{"objectgroup":{"draworder":"index","name":"","objects":[{"height":18,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":6,"y":6}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},"8":{"animation":[{"duration":100,"tileid":8},{"duration":100,"tileid":9},{"duration":100,"tileid":10},{"duration":100,"tileid":11}],"objectgroup":{"draworder":"index","name":"","objects":[{"height":5,"id":2,"name":"","rotation":0,"type":"","visible":true,"width":16,"x":0,"y":19}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},"9":{"objectgroup":{"draworder":"index","name":"","objects":[{"height":9,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":11,"x":3,"y":15}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},"10":{"objectgroup":{"draworder":"index","name":"","objects":[{"height":17,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":10,"x":3,"y":7}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}},"11":{"objectgroup":{"draworder":"index","name":"","objects":[{"height":19,"id":1,"name":"","rotation":0,"type":"","visible":true,"width":4,"x":6,"y":5}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}}},"tilewidth":16,"transparentcolor":"#ff00ff"}""" - - -tilesDataOldAnimations : String -tilesDataOldAnimations = - """{"columns":4,"firstgid":1,"image":"level2/char1_test.png","imageheight":120,"imagewidth":64,"margin":0,"name":"char1_test","spacing":0,"tilecount":20,"tileheight":24,"tiles":{"4":{"animation":[{"duration":100,"tileid":4},{"duration":100,"tileid":5},{"duration":100,"tileid":6},{"duration":100,"tileid":7}]}},"tilewidth":16,"transparentcolor":"#ff00ff"}""" - - -tilesDataNewAnimations : String -tilesDataNewAnimations = - """{"columns":4,"firstgid":1,"image":"level2/char1_test.png","imageheight":120,"imagewidth":64,"margin":0,"name":"char1_test","spacing":0,"tilecount":20,"tileheight":24,"tiles":[{"animation":[{"duration":100,"tileid":4},{"duration":100,"tileid":5},{"duration":100,"tileid":6},{"duration":100,"tileid":7}],"id":4}],"tilewidth":16,"transparentcolor":"#ff00ff"}""" diff --git a/tests/Object.elm b/tests/Object.elm new file mode 100644 index 0000000..9f535fe --- /dev/null +++ b/tests/Object.elm @@ -0,0 +1,19 @@ +module Object exposing (suite) + +import Expect +import Generate +import Json.Decode +import Test exposing (..) +import Tiled.Object exposing (decode, encode) + + +suite : Test +suite = + describe "Tiled.Object" + [ fuzz Generate.object "encode / decode" <| + \obj -> + obj + |> encode + |> Json.Decode.decodeValue decode + |> Expect.equal (Ok obj) + ] diff --git a/tests/Properties.elm b/tests/Properties.elm new file mode 100644 index 0000000..8dc0748 --- /dev/null +++ b/tests/Properties.elm @@ -0,0 +1,19 @@ +module Properties exposing (suite) + +import Expect +import Generate +import Json.Decode +import Test exposing (..) +import Tiled.Properties exposing (decode, encode) + + +suite : Test +suite = + describe "Tiled.Properties" + [ fuzz Generate.properties "encode / decode" <| + \obj -> + obj + |> encode + |> Json.Decode.decodeValue decode + |> Expect.equal (Ok obj) + ] diff --git a/tests/Tileset.elm b/tests/Tileset.elm new file mode 100644 index 0000000..e6baa75 --- /dev/null +++ b/tests/Tileset.elm @@ -0,0 +1,59 @@ +module Tileset exposing (suite) + +import Expect exposing (Expectation) +import Generate +import Json.Decode +import Json.Encode +import Test exposing (..) +import Tiled.Tileset exposing (Tileset(..), decode, encode) + + +suite : Test +suite = + describe "Tiled.Tileset" + [ fuzz Generate.tileset.source "Source encode / decode" <| + \obj -> + obj + |> encode + |> Json.Decode.decodeValue decode + |> Expect.equal (Ok obj) + , fuzz Generate.tileset.embedded "Embedded encode / decode" <| + \income -> + let + obj = + income + in + obj + |> encode + |> Json.Decode.decodeValue decode + |> Expect.equal (Ok obj) + , fuzz Generate.tileset.imageCollection "ImageCollection encode / decode" <| + \obj -> + obj + |> encode + |> Json.Decode.decodeValue decode + |> Expect.equal (Ok obj) + , test "JSON imageCollection1 decode / encode" <| + \_ -> + imageCollection1 + |> Json.Decode.decodeString decode + |> Result.map Tiled.Tileset.encode + |> Result.map (expectEqualsJson imageCollection1) + |> Result.withDefault (Expect.fail "imageCollection sting is not valid json") + ] + + +imageCollection1 = + """{"columns":0,"firstgid":1, "grid":{"height":1,"orientation":"orthogonal","width":1},"margin":0,"name":"123","spacing":0,"tilecount":1,"tileheight":496,"tiles":[{"id":0,"image":"aa.png","imageheight":496,"imagewidth":496}],"tilewidth":496}""" + + +expectEqualsJson : String -> Json.Encode.Value -> Expectation +expectEqualsJson string val = + let + expected = + Json.Decode.decodeString Json.Decode.value string + |> Result.map (Json.Encode.encode 0) + in + Json.Encode.encode 0 val + |> Ok + |> Expect.equal expected diff --git a/tests/elm-package.json b/tests/elm-package.json deleted file mode 100644 index 42ad32a..0000000 --- a/tests/elm-package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": "1.0.0", - "summary": "Test Suites", - "repository": "https://github.com/justgook/elm-tiled-decode.git", - "license": "BSD3", - "source-directories": [ - "../src", - "." - ], - "exposed-modules": [], - "dependencies": { - "NoRedInk/elm-decode-pipeline": "3.0.0 <= v < 4.0.0", - "eeue56/elm-html-test": "5.1.3 <= v < 6.0.0", - "elm-community/elm-test": "4.0.0 <= v < 5.0.0", - "elm-lang/core": "5.1.1 <= v < 6.0.0", - "elm-lang/html": "2.0.0 <= v < 3.0.0", - "elm-lang/http": "1.0.0 <= v < 2.0.0", - "newlandsvalley/elm-binary-base64": "1.0.1 <= v < 2.0.0" - }, - "elm-version": "0.18.0 <= v < 0.19.0" -}