chewbranca.com

Changes On Branch lisp-game-jam-2020
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch lisp-game-jam-2020 Excluding Merge-Ins

This is equivalent to a diff from 5a8a5d5d2f to c1b0bf682a

2020-04-20
05:39
Fix build config Leaf check-in: c1b0bf682a user: chewbranca tags: lisp-game-jam-2020
03:23
s/fahombo/worpt/g check-in: 96d35b5c69 user: chewbranca tags: lisp-game-jam-2020
2020-04-10
06:11
Initial Fahombo project and import of https://gitlab.com/alexjgriffith/min-love2d-fennel check-in: 25cf6d1f59 user: chewbranca tags: lisp-game-jam-2020
06:05
Fix user create bug Leaf check-in: 5a8a5d5d2f user: chewbranca tags: trunk
05:22
mkdir src check-in: 49ad980338 user: chewbranca tags: trunk

Added src/worpt/appimage/build.sh.

            1  +#!/bin/bash
            2  +
            3  +set -eo >/dev/null
            4  +
            5  +CURRENT_APPIMAGEKIT_RELEASE=9
            6  +ARCH="$(uname -m)"
            7  +
            8  +if [[ $# -lt 1 ]]; then
            9  +        echo "Usage: $0 <version>"
           10  +        exit 0
           11  +fi
           12  +
           13  +VERSION="$1"
           14  +LOVEFILE="$2"
           15  +
           16  +LOVE_TAR_URL=https://bitbucket.org/rude/love/downloads/love-${VERSION}-linux-${ARCH}.tar.gz
           17  +LOVE_TAR=${HOME}/.cache/love-release/love/love-${VERSION}-${ARCH}.tar.gz
           18  +if ! test -f ${LOVE_TAR}; then
           19  +        echo "No tarball found for $VERSION in $LOVE_TAR"
           20  +        exit 1
           21  +fi
           22  +
           23  +download_if_needed() {
           24  +        if ! test -f "$1"; then
           25  +                if ! curl -L -o "$1" "https://github.com/AppImage/AppImageKit/releases/download/${CURRENT_APPIMAGEKIT_RELEASE}/$1"; then
           26  +                        echo "Failed to download appimagetool"
           27  +                        echo "Please supply it manually"
           28  +                        exit 1
           29  +                fi
           30  +                chmod +x "$1"
           31  +        fi
           32  +}
           33  +
           34  +main() {
           35  +        download_if_needed appimagetool-${ARCH}.AppImage
           36  +        download_if_needed AppRun-${ARCH}
           37  +
           38  +        # Extract the tarball build into a folder
           39  +        rm -rf love-prepared
           40  +        mkdir love-prepared
           41  +        tar xf ${LOVE_TAR} -C love-prepared --strip-components=1
           42  +
           43  +        cd love-prepared
           44  +
           45  +        # Add our small wrapper script (yay, more wrappers), and AppRun
           46  +        cp ../wrapper usr/bin/wrapper-love
           47  +        cp ../AppRun-${ARCH} AppRun
           48  +
           49  +        local desktopfile="love.desktop"
           50  +        local icon="love"
           51  +        local target="love-${VERSION}"
           52  +
           53  +        if test -f ../../game.desktop.in; then
           54  +                desktopfile="game.desktop"
           55  +                cp ../../game.desktop.in .
           56  +        fi
           57  +        if test -f ../../game.svg; then
           58  +                icon="game"
           59  +                cp ../../game.svg .
           60  +        fi
           61  +        if test -f ${LOVEFILE}; then
           62  +                target="game"
           63  +                cat usr/bin/love ${LOVEFILE} > usr/bin/love-fused
           64  +                mv usr/bin/love-fused usr/bin/love
           65  +                chmod +x usr/bin/love
           66  +    else
           67  +                echo "Love file ${LOVEFILE} not found"
           68  +        fi
           69  +
           70  +        # Add our desktop file
           71  +        sed -e 's/%BINARY%/wrapper-love/' -e "s/%ICON%/${icon}/" "${desktopfile}.in" > "$desktopfile"
           72  +        rm "${desktopfile}.in"
           73  +
           74  +        # Add a DirIcon
           75  +        cp "${icon}.svg" .DirIcon
           76  +
           77  +        # Clean up
           78  +        if test -f ../../game.desktop.in; then
           79  +                rm love.desktop.in
           80  +        fi
           81  +        if test -f ../../game.svg; then
           82  +                rm love.svg
           83  +        fi
           84  +
           85  +        # Now build the final AppImage
           86  +        cd ..
           87  +
           88  +        # Work around missing FUSE/docker
           89  +        ./appimagetool-${ARCH}.AppImage --appimage-extract
           90  +        ./squashfs-root/AppRun -n love-prepared "${target}-${ARCH}.AppImage"
           91  +}
           92  +
           93  +main "$@"

Added src/worpt/appimage/squashfs-root/AppRun.

            1  +#!/bin/sh
            2  +HERE="$(dirname "$(readlink -f "${0}")")"
            3  +export PATH="${HERE}"/usr/bin/:"${HERE}"/usr/sbin/:"${HERE}"/usr/games/:"${HERE}"/bin/:"${HERE}"/sbin/:"${PATH}"
            4  +export LD_LIBRARY_PATH="${HERE}"/usr/lib/:"${HERE}"/usr/lib/i386-linux-gnu/:"${HERE}"/usr/lib/x86_64-linux-gnu/:"${HERE}"/usr/lib32/:"${HERE}"/usr/lib64/:"${HERE}"/lib/:"${HERE}"/lib/i386-linux-gnu/:"${HERE}"/lib/x86_64-linux-gnu/:"${HERE}"/lib32/:"${HERE}"/lib64/:"${LD_LIBRARY_PATH}"
            5  +export PYTHONPATH="${HERE}"/usr/share/pyshared/:"${PYTHONPATH}"
            6  +export XDG_DATA_DIRS="${HERE}"/usr/share/:"${XDG_DATA_DIRS}"
            7  +export PERLLIB="${HERE}"/usr/share/perl5/:"${HERE}"/usr/lib/perl5/:"${PERLLIB}"
            8  +export GSETTINGS_SCHEMA_DIR="${HERE}"/usr/share/glib-2.0/schemas/:"${GSETTINGS_SCHEMA_DIR}"
            9  +export QT_PLUGIN_PATH="${HERE}"/usr/lib/qt4/plugins/:"${HERE}"/usr/lib/i386-linux-gnu/qt4/plugins/:"${HERE}"/usr/lib/x86_64-linux-gnu/qt4/plugins/:"${HERE}"/usr/lib32/qt4/plugins/:"${HERE}"/usr/lib64/qt4/plugins/:"${HERE}"/usr/lib/qt5/plugins/:"${HERE}"/usr/lib/i386-linux-gnu/qt5/plugins/:"${HERE}"/usr/lib/x86_64-linux-gnu/qt5/plugins/:"${HERE}"/usr/lib32/qt5/plugins/:"${HERE}"/usr/lib64/qt5/plugins/:"${QT_PLUGIN_PATH}"
           10  +EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2 | cut -d " " -f 1)
           11  +exec "${EXEC}" $@

Added src/worpt/appimage/squashfs-root/appimagetool.desktop.

            1  +[Desktop Entry]
            2  +Type=Application
            3  +Name=appimagetool
            4  +Exec=appimagetool
            5  +Comment=Tool to generate AppImages from AppDirs
            6  +Icon=appimagetool
            7  +Categories=Development;
            8  +Terminal=true
            9  +

Added src/worpt/appimage/squashfs-root/appimagetool.svg.

            1  +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
            2  +<!-- Created with Inkscape (http://www.inkscape.org/) -->
            3  +
            4  +<svg
            5  +   xmlns:dc="http://purl.org/dc/elements/1.1/"
            6  +   xmlns:cc="http://creativecommons.org/ns#"
            7  +   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
            8  +   xmlns:svg="http://www.w3.org/2000/svg"
            9  +   xmlns="http://www.w3.org/2000/svg"
           10  +   xmlns:xlink="http://www.w3.org/1999/xlink"
           11  +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
           12  +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
           13  +   width="48px"
           14  +   height="48px"
           15  +   id="svg3832"
           16  +   version="1.1"
           17  +   inkscape:version="0.47 r22583"
           18  +   sodipodi:docname="appimage-assistant_alt3.svg">
           19  +  <defs
           20  +     id="defs3834">
           21  +    <linearGradient
           22  +       inkscape:collect="always"
           23  +       xlink:href="#linearGradient3308-4-6-931-761-0"
           24  +       id="linearGradient2975"
           25  +       gradientUnits="userSpaceOnUse"
           26  +       x1="24.3125"
           27  +       y1="22.96875"
           28  +       x2="24.3125"
           29  +       y2="41.03125" />
           30  +    <linearGradient
           31  +       id="linearGradient3308-4-6-931-761-0">
           32  +      <stop
           33  +         id="stop2919-2"
           34  +         style="stop-color:#ffffff;stop-opacity:1"
           35  +         offset="0" />
           36  +      <stop
           37  +         id="stop2921-76"
           38  +         style="stop-color:#ffffff;stop-opacity:0"
           39  +         offset="1" />
           40  +    </linearGradient>
           41  +    <linearGradient
           42  +       inkscape:collect="always"
           43  +       xlink:href="#linearGradient4222"
           44  +       id="linearGradient2979"
           45  +       gradientUnits="userSpaceOnUse"
           46  +       gradientTransform="matrix(0,0.3704967,-0.3617496,0,33.508315,6.1670925)"
           47  +       x1="7.6485429"
           48  +       y1="26.437023"
           49  +       x2="41.861729"
           50  +       y2="26.437023" />
           51  +    <linearGradient
           52  +       id="linearGradient4222">
           53  +      <stop
           54  +         id="stop4224"
           55  +         style="stop-color:#ffffff;stop-opacity:1"
           56  +         offset="0" />
           57  +      <stop
           58  +         id="stop4226"
           59  +         style="stop-color:#ffffff;stop-opacity:0"
           60  +         offset="1" />
           61  +    </linearGradient>
           62  +    <linearGradient
           63  +       inkscape:collect="always"
           64  +       xlink:href="#linearGradient3308-4-6-931-761"
           65  +       id="linearGradient2982"
           66  +       gradientUnits="userSpaceOnUse"
           67  +       gradientTransform="translate(0,0.9999987)"
           68  +       x1="23.99999"
           69  +       y1="4.999989"
           70  +       x2="23.99999"
           71  +       y2="43" />
           72  +    <linearGradient
           73  +       id="linearGradient3308-4-6-931-761">
           74  +      <stop
           75  +         id="stop2919"
           76  +         style="stop-color:#ffffff;stop-opacity:1"
           77  +         offset="0" />
           78  +      <stop
           79  +         id="stop2921"
           80  +         style="stop-color:#ffffff;stop-opacity:0"
           81  +         offset="1" />
           82  +    </linearGradient>
           83  +    <radialGradient
           84  +       inkscape:collect="always"
           85  +       xlink:href="#linearGradient3575"
           86  +       id="radialGradient2985"
           87  +       gradientUnits="userSpaceOnUse"
           88  +       gradientTransform="matrix(0,1.0262008,-1.6561124,9.4072203e-4,-56.097482,-45.332325)"
           89  +       cx="48.42384"
           90  +       cy="-48.027504"
           91  +       fx="48.42384"
           92  +       fy="-48.027504"
           93  +       r="38.212933" />
           94  +    <linearGradient
           95  +       id="linearGradient3575">
           96  +      <stop
           97  +         id="stop3577"
           98  +         style="stop-color:#fafafa;stop-opacity:1"
           99  +         offset="0" />
          100  +      <stop
          101  +         id="stop3579"
          102  +         style="stop-color:#e6e6e6;stop-opacity:1"
          103  +         offset="1" />
          104  +    </linearGradient>
          105  +    <radialGradient
          106  +       inkscape:collect="always"
          107  +       xlink:href="#linearGradient3993"
          108  +       id="radialGradient2990"
          109  +       gradientUnits="userSpaceOnUse"
          110  +       gradientTransform="matrix(0,2.0478765,-2.7410544,-8.6412258e-8,47.161382,-8.837436)"
          111  +       cx="9.3330879"
          112  +       cy="8.4497671"
          113  +       fx="9.3330879"
          114  +       fy="8.4497671"
          115  +       r="19.99999" />
          116  +    <linearGradient
          117  +       id="linearGradient3993">
          118  +      <stop
          119  +         offset="0"
          120  +         style="stop-color:#a3c0d0;stop-opacity:1"
          121  +         id="stop3995" />
          122  +      <stop
          123  +         offset="1"
          124  +         style="stop-color:#427da1;stop-opacity:1"
          125  +         id="stop4001" />
          126  +    </linearGradient>
          127  +    <linearGradient
          128  +       inkscape:collect="always"
          129  +       xlink:href="#linearGradient2508"
          130  +       id="linearGradient2992"
          131  +       gradientUnits="userSpaceOnUse"
          132  +       gradientTransform="translate(0,0.9674382)"
          133  +       x1="14.048676"
          134  +       y1="44.137306"
          135  +       x2="14.048676"
          136  +       y2="4.0000005" />
          137  +    <linearGradient
          138  +       id="linearGradient2508">
          139  +      <stop
          140  +         offset="0"
          141  +         style="stop-color:#2e4a5a;stop-opacity:1"
          142  +         id="stop2510" />
          143  +      <stop
          144  +         offset="1"
          145  +         style="stop-color:#6e8796;stop-opacity:1"
          146  +         id="stop2512" />
          147  +    </linearGradient>
          148  +    <radialGradient
          149  +       cx="4.9929786"
          150  +       cy="43.5"
          151  +       r="2.5"
          152  +       fx="4.9929786"
          153  +       fy="43.5"
          154  +       id="radialGradient2873-966-168"
          155  +       xlink:href="#linearGradient3688-166-749"
          156  +       gradientUnits="userSpaceOnUse"
          157  +       gradientTransform="matrix(2.003784,0,0,1.4,27.98813,-17.4)" />
          158  +    <linearGradient
          159  +       id="linearGradient3688-166-749">
          160  +      <stop
          161  +         id="stop2883"
          162  +         style="stop-color:#181818;stop-opacity:1"
          163  +         offset="0" />
          164  +      <stop
          165  +         id="stop2885"
          166  +         style="stop-color:#181818;stop-opacity:0"
          167  +         offset="1" />
          168  +    </linearGradient>
          169  +    <radialGradient
          170  +       cx="4.9929786"
          171  +       cy="43.5"
          172  +       r="2.5"
          173  +       fx="4.9929786"
          174  +       fy="43.5"
          175  +       id="radialGradient2875-742-326"
          176  +       xlink:href="#linearGradient3688-464-309"
          177  +       gradientUnits="userSpaceOnUse"
          178  +       gradientTransform="matrix(2.003784,0,0,1.4,-20.01187,-104.4)" />
          179  +    <linearGradient
          180  +       id="linearGradient3688-464-309">
          181  +      <stop
          182  +         id="stop2889"
          183  +         style="stop-color:#181818;stop-opacity:1"
          184  +         offset="0" />
          185  +      <stop
          186  +         id="stop2891"
          187  +         style="stop-color:#181818;stop-opacity:0"
          188  +         offset="1" />
          189  +    </linearGradient>
          190  +    <linearGradient
          191  +       x1="25.058096"
          192  +       y1="47.027729"
          193  +       x2="25.058096"
          194  +       y2="39.999443"
          195  +       id="linearGradient2877-634-617"
          196  +       xlink:href="#linearGradient3702-501-757"
          197  +       gradientUnits="userSpaceOnUse" />
          198  +    <linearGradient
          199  +       id="linearGradient3702-501-757">
          200  +      <stop
          201  +         id="stop2895"
          202  +         style="stop-color:#181818;stop-opacity:0"
          203  +         offset="0" />
          204  +      <stop
          205  +         id="stop2897"
          206  +         style="stop-color:#181818;stop-opacity:1"
          207  +         offset="0.5" />
          208  +      <stop
          209  +         id="stop2899"
          210  +         style="stop-color:#181818;stop-opacity:0"
          211  +         offset="1" />
          212  +    </linearGradient>
          213  +  </defs>
          214  +  <sodipodi:namedview
          215  +     id="base"
          216  +     pagecolor="#ffffff"
          217  +     bordercolor="#666666"
          218  +     borderopacity="1.0"
          219  +     inkscape:pageopacity="0.0"
          220  +     inkscape:pageshadow="2"
          221  +     inkscape:zoom="7"
          222  +     inkscape:cx="24"
          223  +     inkscape:cy="24"
          224  +     inkscape:current-layer="layer1"
          225  +     showgrid="true"
          226  +     inkscape:grid-bbox="true"
          227  +     inkscape:document-units="px"
          228  +     inkscape:window-width="603"
          229  +     inkscape:window-height="484"
          230  +     inkscape:window-x="417"
          231  +     inkscape:window-y="162"
          232  +     inkscape:window-maximized="0" />
          233  +  <metadata
          234  +     id="metadata3837">
          235  +    <rdf:RDF>
          236  +      <cc:Work
          237  +         rdf:about="">
          238  +        <dc:format>image/svg+xml</dc:format>
          239  +        <dc:type
          240  +           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
          241  +        <dc:title></dc:title>
          242  +      </cc:Work>
          243  +    </rdf:RDF>
          244  +  </metadata>
          245  +  <g
          246  +     id="layer1"
          247  +     inkscape:label="Layer 1"
          248  +     inkscape:groupmode="layer">
          249  +    <g
          250  +       style="display:inline"
          251  +       id="g2036"
          252  +       transform="matrix(1.1,0,0,0.4444449,-2.4000022,25.11107)">
          253  +      <g
          254  +         style="opacity:0.4"
          255  +         id="g3712"
          256  +         transform="matrix(1.052632,0,0,1.285713,-1.263158,-13.42854)">
          257  +        <rect
          258  +           style="fill:url(#radialGradient2873-966-168);fill-opacity:1;stroke:none"
          259  +           id="rect2801"
          260  +           y="40"
          261  +           x="38"
          262  +           height="7"
          263  +           width="5" />
          264  +        <rect
          265  +           style="fill:url(#radialGradient2875-742-326);fill-opacity:1;stroke:none"
          266  +           id="rect3696"
          267  +           transform="scale(-1,-1)"
          268  +           y="-47"
          269  +           x="-10"
          270  +           height="7"
          271  +           width="5" />
          272  +        <rect
          273  +           style="fill:url(#linearGradient2877-634-617);fill-opacity:1;stroke:none"
          274  +           id="rect3700"
          275  +           y="40"
          276  +           x="10"
          277  +           height="7.0000005"
          278  +           width="28" />
          279  +      </g>
          280  +    </g>
          281  +    <rect
          282  +       style="fill:url(#radialGradient2990);fill-opacity:1;stroke:url(#linearGradient2992);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
          283  +       id="rect5505"
          284  +       y="5.4674392"
          285  +       x="4.5"
          286  +       ry="2.2322156"
          287  +       rx="2.2322156"
          288  +       height="39"
          289  +       width="39" />
          290  +    <path
          291  +       style="opacity:0.05;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00178742;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
          292  +       id="path4294-1"
          293  +       d="m 21,6.9687498 a 2.0165107,2.0165107 0 0 0 -2.03125,2.03125 l 0,3.9687502 -1.15625,0 a 2.0165107,2.0165107 0 0 0 -1.5,3.375 l 5.0625,5.75 c -0.06312,0.110777 -0.178724,0.246032 -0.21875,0.34375 -0.195898,0.478256 -0.25,0.83653 -0.25,1.21875 l 0,0.125 L 20.8125,23.6875 C 20.534322,23.409323 20.213169,23.162739 19.71875,22.96875 19.47154,22.87176 19.185456,22.791748 18.75,22.8125 c -0.435456,0.02075 -1.054055,0.210302 -1.46875,0.625 L 15.75,24.96875 c -0.414689,0.414689 -0.604245,1.033294 -0.625,1.46875 -0.02075,0.435456 0.05925,0.721537 0.15625,0.96875 C 15.475241,27.900677 15.721817,28.221821 16,28.5 l 0.09375,0.09375 -0.125,0 c -0.382218,0 -0.740493,0.0541 -1.21875,0.25 -0.239128,0.09795 -0.538285,0.214988 -0.84375,0.53125 -0.305465,0.316262 -0.625,0.914788 -0.625,1.53125 l 0,2.1875 c 0,0.616465 0.319536,1.214989 0.625,1.53125 0.305464,0.316261 0.604622,0.433301 0.84375,0.53125 0.478256,0.195898 0.83653,0.25 1.21875,0.25 l 0.125,0 L 16,35.5 c -0.278175,0.278176 -0.52476,0.599329 -0.71875,1.09375 -0.09699,0.24721 -0.177003,0.533292 -0.15625,0.96875 0.02075,0.435458 0.210304,1.054058 0.625,1.46875 l 1.53125,1.53125 c 0.414691,0.414697 1.033292,0.604245 1.46875,0.625 0.435458,0.02076 0.721537,-0.05926 0.96875,-0.15625 0.494425,-0.19399 0.81557,-0.440568 1.09375,-0.71875 l 0.09375,-0.09375 0,0.125 c 0,0.38222 0.0541,0.740495 0.25,1.21875 0.09795,0.239127 0.214989,0.538285 0.53125,0.84375 0.316261,0.305465 0.914783,0.625 1.53125,0.625 l 2.1875,0 c 0.616466,0 1.214989,-0.319534 1.53125,-0.625 0.316261,-0.305466 0.433302,-0.604622 0.53125,-0.84375 0.195896,-0.478255 0.25,-0.836532 0.25,-1.21875 l 0,-0.125 0.09375,0.09375 c 0.278176,0.278175 0.599329,0.52476 1.09375,0.71875 0.24721,0.09699 0.533292,0.177003 0.96875,0.15625 0.435458,-0.02075 1.054058,-0.210304 1.46875,-0.625 L 32.875,39.03125 C 33.289697,38.616559 33.479245,37.997958 33.5,37.5625 33.52076,37.127042 33.44074,36.840963 33.34375,36.59375 33.14976,36.099325 32.903182,35.77818 32.625,35.5 l -0.09375,-0.09375 0.125,0 c 0.38222,0 0.740494,-0.0541 1.21875,-0.25 0.239128,-0.09795 0.538286,-0.214988 0.84375,-0.53125 0.305464,-0.316262 0.625,-0.914787 0.625,-1.53125 l 0,-2.1875 c 0,-0.61646 -0.319535,-1.214987 -0.625,-1.53125 -0.305465,-0.316263 -0.604621,-0.433301 -0.84375,-0.53125 -0.478257,-0.195898 -0.836532,-0.25 -1.21875,-0.25 l -0.125,0 L 32.625,28.5 c 0.278177,-0.278177 0.52476,-0.599329 0.71875,-1.09375 C 33.44074,27.15904 33.520753,26.872957 33.5,26.4375 33.47925,26.002043 33.289697,25.383443 32.875,24.96875 L 31.34375,23.4375 c -0.414688,-0.414694 -1.03329,-0.604245 -1.46875,-0.625 -0.43546,-0.02076 -0.721537,0.05925 -0.96875,0.15625 -0.494426,0.193991 -0.815572,0.44057 -1.09375,0.71875 l -0.09375,0.09375 0,-0.125 c 0,-0.382218 -0.0541,-0.740493 -0.25,-1.21875 -0.09112,-0.22245 -0.228127,-0.500183 -0.5,-0.78125 l 4.71875,-5.3125 a 2.0165107,2.0165107 0 0 0 -1.5,-3.375 l -1.15625,0 0,-3.9687502 A 2.0165107,2.0165107 0 0 0 27,6.9687498 l -6,0 z M 24.3125,31.25 c 0.427097,0 0.75,0.322904 0.75,0.75 0,0.427096 -0.322903,0.75 -0.75,0.75 -0.427094,0 -0.75,-0.322906 -0.75,-0.75 0,-0.427094 0.322906,-0.75 0.75,-0.75 z" />
          294  +    <path
          295  +       style="opacity:0.05;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00178742;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
          296  +       id="path4294"
          297  +       d="m 20.90625,8.0312498 a 0.96385067,0.96385067 0 0 0 -0.875,0.96875 l 0,5.0312502 -2.21875,0 A 0.96385067,0.96385067 0 0 0 17.09375,15.625 l 5.78125,6.53125 c -0.158814,0.0616 -0.341836,0.0951 -0.4375,0.1875 -0.169161,0.163386 -0.252971,0.323419 -0.3125,0.46875 -0.119058,0.290663 -0.15625,0.566746 -0.15625,0.84375 l 0,1.65625 C 21.718163,25.40233 21.485871,25.509772 21.25,25.625 l -1.1875,-1.1875 c -0.199651,-0.19965 -0.421433,-0.352095 -0.71875,-0.46875 -0.148659,-0.05833 -0.329673,-0.104846 -0.5625,-0.09375 -0.232827,0.0111 -0.53583,0.09833 -0.75,0.3125 L 16.5,25.71875 c -0.214168,0.214168 -0.301403,0.517173 -0.3125,0.75 -0.0111,0.232827 0.03542,0.41384 0.09375,0.5625 0.116655,0.297321 0.269096,0.519099 0.46875,0.71875 l 1.1875,1.1875 c -0.115228,0.235871 -0.222668,0.468163 -0.3125,0.71875 l -1.65625,0 c -0.277003,0 -0.553087,0.03719 -0.84375,0.15625 -0.145332,0.05953 -0.305363,0.143338 -0.46875,0.3125 -0.163387,0.169162 -0.3125,0.46403 -0.3125,0.78125 l 0,2.1875 c 0,0.317221 0.149114,0.612089 0.3125,0.78125 0.163386,0.169161 0.323419,0.252971 0.46875,0.3125 0.290663,0.119058 0.566746,0.15625 0.84375,0.15625 l 1.65625,0 c 0.08983,0.250587 0.197272,0.482879 0.3125,0.71875 L 16.75,36.25 c -0.199649,0.19965 -0.352095,0.421432 -0.46875,0.71875 -0.05833,0.148659 -0.104846,0.329672 -0.09375,0.5625 0.0111,0.232828 0.09833,0.535831 0.3125,0.75 l 1.53125,1.53125 c 0.214168,0.214172 0.517172,0.301403 0.75,0.3125 0.232828,0.0111 0.41384,-0.03542 0.5625,-0.09375 0.29732,-0.116655 0.519098,-0.269096 0.71875,-0.46875 L 21.25,38.375 c 0.235871,0.115228 0.468164,0.222668 0.71875,0.3125 l 0,1.65625 c 0,0.277003 0.03719,0.553087 0.15625,0.84375 0.05953,0.145331 0.143339,0.305364 0.3125,0.46875 0.169161,0.163386 0.464028,0.3125 0.78125,0.3125 l 2.1875,0 c 0.317221,0 0.612089,-0.149113 0.78125,-0.3125 0.169161,-0.163387 0.252971,-0.323419 0.3125,-0.46875 0.119057,-0.290663 0.15625,-0.566748 0.15625,-0.84375 l 0,-1.65625 c 0.250586,-0.08983 0.482879,-0.197272 0.71875,-0.3125 l 1.1875,1.1875 c 0.19965,0.199649 0.421432,0.352095 0.71875,0.46875 0.148659,0.05833 0.329672,0.104846 0.5625,0.09375 0.232828,-0.0111 0.535831,-0.09833 0.75,-0.3125 L 32.125,38.28125 c 0.214172,-0.214168 0.301403,-0.517172 0.3125,-0.75 0.0111,-0.232828 -0.03542,-0.41384 -0.09375,-0.5625 C 32.227095,36.67143 32.074654,36.449652 31.875,36.25 L 30.6875,35.0625 C 30.802728,34.82663 30.910168,34.594337 31,34.34375 l 1.65625,0 c 0.277004,0 0.553087,-0.03719 0.84375,-0.15625 0.145332,-0.05953 0.305364,-0.143339 0.46875,-0.3125 0.163386,-0.169161 0.3125,-0.46403 0.3125,-0.78125 l 0,-2.1875 c 0,-0.317219 -0.149114,-0.612088 -0.3125,-0.78125 C 33.805364,29.955838 33.645332,29.872029 33.5,29.8125 33.209336,29.693442 32.933253,29.65625 32.65625,29.65625 l -1.65625,0 C 30.91017,29.405663 30.802728,29.17337 30.6875,28.9375 L 31.875,27.75 c 0.19965,-0.19965 0.352095,-0.421432 0.46875,-0.71875 0.05833,-0.148659 0.104846,-0.329672 0.09375,-0.5625 -0.0111,-0.232828 -0.09833,-0.535831 -0.3125,-0.75 L 30.59375,24.1875 c -0.214167,-0.21417 -0.517171,-0.301403 -0.75,-0.3125 -0.232829,-0.0111 -0.41384,0.03542 -0.5625,0.09375 -0.29732,0.116656 -0.519099,0.269097 -0.71875,0.46875 L 27.375,25.625 c -0.235871,-0.115228 -0.468163,-0.222668 -0.71875,-0.3125 l 0,-1.65625 c 0,-0.277003 -0.03719,-0.553087 -0.15625,-0.84375 -0.05953,-0.145332 -0.143338,-0.305363 -0.3125,-0.46875 -0.169162,-0.163387 -0.46403,-0.3125 -0.78125,-0.3125 l -0.15625,0 5.65625,-6.40625 A 0.96385067,0.96385067 0 0 0 30.1875,14.03125 l -2.21875,0 0,-5.0312502 A 0.96385067,0.96385067 0 0 0 27,8.0312498 l -6,0 a 0.96385067,0.96385067 0 0 0 -0.09375,0 z M 24.3125,30.1875 c 1.002113,0 1.8125,0.810388 1.8125,1.8125 0,1.002112 -0.810387,1.8125 -1.8125,1.8125 C 23.31039,33.8125 22.5,33.002111 22.5,32 c 0,-1.002111 0.81039,-1.8125 1.8125,-1.8125 z" />
          298  +    <path
          299  +       style="fill:url(#radialGradient2985);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00178742;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
          300  +       id="path2317"
          301  +       d="M 21,8.9999996 21,15 17.8125,15 24,22 30.1875,15 27,15 l 0,-6.0000004 -6,0 z M 23.21875,23 c -0.172892,0 -0.28125,0.294922 -0.28125,0.65625 l 0,2.28125 C 22.24145,26.095996 21.585954,26.379869 21,26.75 l -1.625,-1.625 c -0.255498,-0.255497 -0.533998,-0.372253 -0.65625,-0.25 l -1.53125,1.53125 c -0.122254,0.122254 -0.0055,0.400753 0.25,0.65625 l 1.625,1.625 c -0.37013,0.585953 -0.654003,1.24145 -0.8125,1.9375 l -2.28125,0 c -0.361328,0 -0.65625,0.108357 -0.65625,0.28125 l 0,2.1875 c 0,0.172892 0.294922,0.28125 0.65625,0.28125 l 2.28125,0 c 0.158497,0.69605 0.44237,1.351546 0.8125,1.9375 l -1.625,1.625 c -0.255497,0.255498 -0.372254,0.533997 -0.25,0.65625 l 1.53125,1.53125 c 0.122252,0.122254 0.400752,0.0055 0.65625,-0.25 L 21,37.25 c 0.585954,0.37013 1.24145,0.654002 1.9375,0.8125 l 0,2.28125 C 22.9375,40.705077 23.045858,41 23.21875,41 l 2.1875,0 c 0.172893,0 0.28125,-0.294924 0.28125,-0.65625 l 0,-2.28125 c 0.69605,-0.158498 1.351546,-0.44237 1.9375,-0.8125 l 1.625,1.625 c 0.255498,0.255497 0.533997,0.372254 0.65625,0.25 l 1.53125,-1.53125 c 0.122254,-0.122252 0.0055,-0.400752 -0.25,-0.65625 l -1.625,-1.625 c 0.370129,-0.585954 0.654003,-1.24145 0.8125,-1.9375 l 2.28125,0 c 0.361329,0 0.65625,-0.108358 0.65625,-0.28125 l 0,-2.1875 c 0,-0.172893 -0.294921,-0.28125 -0.65625,-0.28125 l -2.28125,0 c -0.158497,-0.69605 -0.442371,-1.351547 -0.8125,-1.9375 l 1.625,-1.625 c 0.255497,-0.255497 0.372254,-0.533997 0.25,-0.65625 L 29.90625,24.875 C 29.783997,24.752745 29.505498,24.8695 29.25,25.125 l -1.625,1.625 c -0.585954,-0.370131 -1.24145,-0.654004 -1.9375,-0.8125 l 0,-2.28125 C 25.6875,23.294922 25.579143,23 25.40625,23 l -2.1875,0 z m 1.09375,6.21875 c 1.528616,0 2.78125,1.252635 2.78125,2.78125 0,1.528615 -1.252634,2.78125 -2.78125,2.78125 -1.528614,0 -2.78125,-1.252635 -2.78125,-2.78125 0,-1.528615 1.252636,-2.78125 2.78125,-2.78125 z" />
          302  +    <rect
          303  +       style="opacity:0.4;fill:none;stroke:url(#linearGradient2982);stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
          304  +       id="rect6741"
          305  +       y="6.4999886"
          306  +       x="5.4999981"
          307  +       ry="1.365193"
          308  +       rx="1.365193"
          309  +       height="37.000011"
          310  +       width="36.999985" />
          311  +    <path
          312  +       style="fill:none;stroke:url(#linearGradient2979);stroke-width:0.99829447;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
          313  +       id="path2777"
          314  +       d="M 28.926376,15.466668 24,21.177578 18.963089,15.5 21.5,15.5 l 0,-6.0000004 5,0 0,6.0000004 2.426376,-0.03333 z" />
          315  +    <path
          316  +       style="fill:none;stroke:url(#linearGradient2975);stroke-width:1;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
          317  +       id="path4243"
          318  +       d="m 23.4375,23.46875 c -0.01166,0.05381 -0.03125,0.100205 -0.03125,0.1875 l 0,2.28125 a 0.48185467,0.48185467 0 0 1 -0.375,0.46875 c -0.638467,0.145384 -1.238423,0.407111 -1.78125,0.75 a 0.48185467,0.48185467 0 0 1 -0.59375,-0.0625 l -1.625,-1.625 C 18.9779,25.4154 18.9477,25.40242 18.90625,25.375 l -1.21875,1.21875 c 0.02742,0.04145 0.0404,0.07165 0.09375,0.125 l 1.625,1.625 a 0.48185467,0.48185467 0 0 1 0.0625,0.59375 c -0.342888,0.542826 -0.604615,1.142782 -0.75,1.78125 a 0.48185467,0.48185467 0 0 1 -0.46875,0.375 l -2.28125,0 c -0.08729,0 -0.133695,0.01959 -0.1875,0.03125 l 0,1.75 c 0.05381,0.01166 0.100205,0.03125 0.1875,0.03125 l 2.28125,0 a 0.48185467,0.48185467 0 0 1 0.46875,0.375 c 0.145385,0.638468 0.407112,1.238423 0.75,1.78125 a 0.48185467,0.48185467 0 0 1 -0.0625,0.59375 l -1.625,1.625 c -0.05335,0.05335 -0.06633,0.08355 -0.09375,0.125 l 1.21875,1.21875 c 0.04145,-0.02742 0.07165,-0.0404 0.125,-0.09375 l 1.625,-1.625 A 0.48185467,0.48185467 0 0 1 21.25,36.84375 c 0.542827,0.342888 1.142781,0.604614 1.78125,0.75 a 0.48185467,0.48185467 0 0 1 0.375,0.46875 l 0,2.28125 c 0,0.08729 0.01959,0.133695 0.03125,0.1875 l 1.75,0 c 0.01166,-0.0538 0.03125,-0.100206 0.03125,-0.1875 l 0,-2.28125 a 0.48185467,0.48185467 0 0 1 0.375,-0.46875 c 0.638469,-0.145386 1.238423,-0.407112 1.78125,-0.75 a 0.48185467,0.48185467 0 0 1 0.59375,0.0625 l 1.625,1.625 c 0.05335,0.05335 0.08355,0.06633 0.125,0.09375 l 1.21875,-1.21875 c -0.02742,-0.04145 -0.0404,-0.07165 -0.09375,-0.125 l -1.625,-1.625 a 0.48185467,0.48185467 0 0 1 -0.0625,-0.59375 c 0.342888,-0.542828 0.604615,-1.142783 0.75,-1.78125 a 0.48185467,0.48185467 0 0 1 0.46875,-0.375 l 2.28125,0 c 0.08729,0 0.133695,-0.01959 0.1875,-0.03125 l 0,-1.75 c -0.0538,-0.01166 -0.100204,-0.03125 -0.1875,-0.03125 l -2.28125,0 a 0.48185467,0.48185467 0 0 1 -0.46875,-0.375 c -0.145385,-0.638467 -0.407113,-1.238424 -0.75,-1.78125 a 0.48185467,0.48185467 0 0 1 0.0625,-0.59375 l 1.625,-1.625 c 0.05335,-0.05335 0.06633,-0.08355 0.09375,-0.125 L 29.71875,25.375 c -0.04145,0.02742 -0.07165,0.0404 -0.125,0.09375 l -1.625,1.625 a 0.48185467,0.48185467 0 0 1 -0.59375,0.0625 c -0.542827,-0.342889 -1.142783,-0.604616 -1.78125,-0.75 a 0.48185467,0.48185467 0 0 1 -0.375,-0.46875 l 0,-2.28125 c 0,-0.0873 -0.01959,-0.133695 -0.03125,-0.1875 l -1.75,0 z m 0.875,5.28125 c 1.791829,0 3.25,1.458172 3.25,3.25 0,1.791828 -1.458171,3.25 -3.25,3.25 -1.791827,0 -3.25,-1.458172 -3.25,-3.25 0,-1.791828 1.458173,-3.25 3.25,-3.25 z" />
          319  +  </g>
          320  +</svg>

Added src/worpt/appimage/squashfs-root/usr/bin/appimagetool.

cannot compute difference between binary files

Added src/worpt/appimage/squashfs-root/usr/bin/mksquashfs.

cannot compute difference between binary files

Added src/worpt/appimage/squashfs-root/usr/bin/zsyncmake.

cannot compute difference between binary files

Added src/worpt/appimage/wrapper.

            1  +#!/bin/sh
            2  +
            3  +# For some reason this runs from the usr subdirectory
            4  +exec ../love "$@"

Added src/worpt/assets/osmstudios_character_block.png.

cannot compute difference between binary files

Added src/worpt/assets/warped_city/atlas/atlas.json.

            1  +{"frames": [
            2  +
            3  +{
            4  +	"filename": "antenna",
            5  +	"frame": {"x":612,"y":133,"w":21,"h":95},
            6  +	"rotated": false,
            7  +	"trimmed": true,
            8  +	"spriteSourceSize": {"x":1,"y":1,"w":21,"h":95},
            9  +	"sourceSize": {"w":22,"h":96},
           10  +	"pivot": {"x":0.5,"y":0.5}
           11  +},
           12  +{
           13  +	"filename": "back-jump-1",
           14  +	"frame": {"x":575,"y":2,"w":24,"h":27},
           15  +	"rotated": false,
           16  +	"trimmed": true,
           17  +	"spriteSourceSize": {"x":24,"y":40,"w":24,"h":27},
           18  +	"sourceSize": {"w":71,"h":67},
           19  +	"pivot": {"x":0.5,"y":0.5}
           20  +},
           21  +{
           22  +	"filename": "back-jump-2",
           23  +	"frame": {"x":374,"y":55,"w":35,"h":42},
           24  +	"rotated": false,
           25  +	"trimmed": true,
           26  +	"spriteSourceSize": {"x":15,"y":25,"w":35,"h":42},
           27  +	"sourceSize": {"w":71,"h":67},
           28  +	"pivot": {"x":0.5,"y":0.5}
           29  +},
           30  +{
           31  +	"filename": "back-jump-3",
           32  +	"frame": {"x":168,"y":55,"w":35,"h":38},
           33  +	"rotated": false,
           34  +	"trimmed": true,
           35  +	"spriteSourceSize": {"x":15,"y":20,"w":35,"h":38},
           36  +	"sourceSize": {"w":71,"h":67},
           37  +	"pivot": {"x":0.5,"y":0.5}
           38  +},
           39  +{
           40  +	"filename": "back-jump-4",
           41  +	"frame": {"x":773,"y":55,"w":44,"h":40},
           42  +	"rotated": false,
           43  +	"trimmed": true,
           44  +	"spriteSourceSize": {"x":12,"y":16,"w":44,"h":40},
           45  +	"sourceSize": {"w":71,"h":67},
           46  +	"pivot": {"x":0.5,"y":0.5}
           47  +},
           48  +{
           49  +	"filename": "back-jump-5",
           50  +	"frame": {"x":411,"y":55,"w":31,"h":47},
           51  +	"rotated": false,
           52  +	"trimmed": true,
           53  +	"spriteSourceSize": {"x":17,"y":18,"w":31,"h":47},
           54  +	"sourceSize": {"w":71,"h":67},
           55  +	"pivot": {"x":0.5,"y":0.5}
           56  +},
           57  +{
           58  +	"filename": "back-jump-6",
           59  +	"frame": {"x":1124,"y":2,"w":28,"h":39},
           60  +	"rotated": false,
           61  +	"trimmed": true,
           62  +	"spriteSourceSize": {"x":24,"y":28,"w":28,"h":39},
           63  +	"sourceSize": {"w":71,"h":67},
           64  +	"pivot": {"x":0.5,"y":0.5}
           65  +},
           66  +{
           67  +	"filename": "back-jump-7",
           68  +	"frame": {"x":858,"y":2,"w":35,"h":27},
           69  +	"rotated": false,
           70  +	"trimmed": true,
           71  +	"spriteSourceSize": {"x":19,"y":40,"w":35,"h":27},
           72  +	"sourceSize": {"w":71,"h":67},
           73  +	"pivot": {"x":0.5,"y":0.5}
           74  +},
           75  +{
           76  +	"filename": "back-jump-preview",
           77  +	"frame": {"x":601,"y":2,"w":24,"h":27},
           78  +	"rotated": false,
           79  +	"trimmed": true,
           80  +	"spriteSourceSize": {"x":24,"y":40,"w":24,"h":27},
           81  +	"sourceSize": {"w":71,"h":67},
           82  +	"pivot": {"x":0.5,"y":0.5}
           83  +},
           84  +{
           85  +	"filename": "banner-big-1",
           86  +	"frame": {"x":635,"y":133,"w":35,"h":92},
           87  +	"rotated": false,
           88  +	"trimmed": false,
           89  +	"spriteSourceSize": {"x":0,"y":0,"w":35,"h":92},
           90  +	"sourceSize": {"w":35,"h":92},
           91  +	"pivot": {"x":0.5,"y":0.5}
           92  +},
           93  +{
           94  +	"filename": "banner-big-2",
           95  +	"frame": {"x":672,"y":133,"w":35,"h":92},
           96  +	"rotated": false,
           97  +	"trimmed": false,
           98  +	"spriteSourceSize": {"x":0,"y":0,"w":35,"h":92},
           99  +	"sourceSize": {"w":35,"h":92},
          100  +	"pivot": {"x":0.5,"y":0.5}
          101  +},
          102  +{
          103  +	"filename": "banner-big-3",
          104  +	"frame": {"x":746,"y":133,"w":35,"h":92},
          105  +	"rotated": false,
          106  +	"trimmed": false,
          107  +	"spriteSourceSize": {"x":0,"y":0,"w":35,"h":92},
          108  +	"sourceSize": {"w":35,"h":92},
          109  +	"pivot": {"x":0.5,"y":0.5}
          110  +},
          111  +{
          112  +	"filename": "banner-big-4",
          113  +	"frame": {"x":783,"y":133,"w":35,"h":92},
          114  +	"rotated": false,
          115  +	"trimmed": false,
          116  +	"spriteSourceSize": {"x":0,"y":0,"w":35,"h":92},
          117  +	"sourceSize": {"w":35,"h":92},
          118  +	"pivot": {"x":0.5,"y":0.5}
          119  +},
          120  +{
          121  +	"filename": "banner-big-preview",
          122  +	"frame": {"x":709,"y":133,"w":35,"h":92},
          123  +	"rotated": false,
          124  +	"trimmed": false,
          125  +	"spriteSourceSize": {"x":0,"y":0,"w":35,"h":92},
          126  +	"sourceSize": {"w":35,"h":92},
          127  +	"pivot": {"x":0.5,"y":0.5}
          128  +},
          129  +{
          130  +	"filename": "banner-coke-1",
          131  +	"frame": {"x":382,"y":133,"w":27,"h":78},
          132  +	"rotated": false,
          133  +	"trimmed": false,
          134  +	"spriteSourceSize": {"x":0,"y":0,"w":27,"h":78},
          135  +	"sourceSize": {"w":27,"h":78},
          136  +	"pivot": {"x":0.5,"y":0.5}
          137  +},
          138  +{
          139  +	"filename": "banner-coke-2",
          140  +	"frame": {"x":440,"y":133,"w":27,"h":78},
          141  +	"rotated": false,
          142  +	"trimmed": false,
          143  +	"spriteSourceSize": {"x":0,"y":0,"w":27,"h":78},
          144  +	"sourceSize": {"w":27,"h":78},
          145  +	"pivot": {"x":0.5,"y":0.5}
          146  +},
          147  +{
          148  +	"filename": "banner-coke-3",
          149  +	"frame": {"x":411,"y":133,"w":27,"h":78},
          150  +	"rotated": false,
          151  +	"trimmed": false,
          152  +	"spriteSourceSize": {"x":0,"y":0,"w":27,"h":78},
          153  +	"sourceSize": {"w":27,"h":78},
          154  +	"pivot": {"x":0.5,"y":0.5}
          155  +},
          156  +{
          157  +	"filename": "banner-coke-preview",
          158  +	"frame": {"x":469,"y":133,"w":27,"h":78},
          159  +	"rotated": false,
          160  +	"trimmed": false,
          161  +	"spriteSourceSize": {"x":0,"y":0,"w":27,"h":78},
          162  +	"sourceSize": {"w":27,"h":78},
          163  +	"pivot": {"x":0.5,"y":0.5}
          164  +},
          165  +{
          166  +	"filename": "banner-neon-1",
          167  +	"frame": {"x":1215,"y":2,"w":19,"h":48},
          168  +	"rotated": false,
          169  +	"trimmed": false,
          170  +	"spriteSourceSize": {"x":0,"y":0,"w":19,"h":48},
          171  +	"sourceSize": {"w":19,"h":48},
          172  +	"pivot": {"x":0.5,"y":0.5}
          173  +},
          174  +{
          175  +	"filename": "banner-neon-2",
          176  +	"frame": {"x":1194,"y":2,"w":19,"h":48},
          177  +	"rotated": false,
          178  +	"trimmed": false,
          179  +	"spriteSourceSize": {"x":0,"y":0,"w":19,"h":48},
          180  +	"sourceSize": {"w":19,"h":48},
          181  +	"pivot": {"x":0.5,"y":0.5}
          182  +},
          183  +{
          184  +	"filename": "banner-neon-3",
          185  +	"frame": {"x":1103,"y":2,"w":19,"h":48},
          186  +	"rotated": false,
          187  +	"trimmed": false,
          188  +	"spriteSourceSize": {"x":0,"y":0,"w":19,"h":48},
          189  +	"sourceSize": {"w":19,"h":48},
          190  +	"pivot": {"x":0.5,"y":0.5}
          191  +},
          192  +{
          193  +	"filename": "banner-neon-4",
          194  +	"frame": {"x":1236,"y":2,"w":19,"h":48},
          195  +	"rotated": false,
          196  +	"trimmed": false,
          197  +	"spriteSourceSize": {"x":0,"y":0,"w":19,"h":48},
          198  +	"sourceSize": {"w":19,"h":48},
          199  +	"pivot": {"x":0.5,"y":0.5}
          200  +},
          201  +{
          202  +	"filename": "banner-neon-preview",
          203  +	"frame": {"x":1278,"y":2,"w":19,"h":48},
          204  +	"rotated": false,
          205  +	"trimmed": false,
          206  +	"spriteSourceSize": {"x":0,"y":0,"w":19,"h":48},
          207  +	"sourceSize": {"w":19,"h":48},
          208  +	"pivot": {"x":0.5,"y":0.5}
          209  +},
          210  +{
          211  +	"filename": "banner-scroll-1",
          212  +	"frame": {"x":678,"y":2,"w":13,"h":47},
          213  +	"rotated": false,
          214  +	"trimmed": false,
          215  +	"spriteSourceSize": {"x":0,"y":0,"w":13,"h":47},
          216  +	"sourceSize": {"w":13,"h":47},
          217  +	"pivot": {"x":0.5,"y":0.5}
          218  +},
          219  +{
          220  +	"filename": "banner-scroll-2",
          221  +	"frame": {"x":663,"y":2,"w":13,"h":47},
          222  +	"rotated": false,
          223  +	"trimmed": false,
          224  +	"spriteSourceSize": {"x":0,"y":0,"w":13,"h":47},
          225  +	"sourceSize": {"w":13,"h":47},
          226  +	"pivot": {"x":0.5,"y":0.5}
          227  +},
          228  +{
          229  +	"filename": "banner-scroll-3",
          230  +	"frame": {"x":693,"y":2,"w":13,"h":47},
          231  +	"rotated": false,
          232  +	"trimmed": false,
          233  +	"spriteSourceSize": {"x":0,"y":0,"w":13,"h":47},
          234  +	"sourceSize": {"w":13,"h":47},
          235  +	"pivot": {"x":0.5,"y":0.5}
          236  +},
          237  +{
          238  +	"filename": "banner-scroll-4",
          239  +	"frame": {"x":723,"y":2,"w":13,"h":47},
          240  +	"rotated": false,
          241  +	"trimmed": false,
          242  +	"spriteSourceSize": {"x":0,"y":0,"w":13,"h":47},
          243  +	"sourceSize": {"w":13,"h":47},
          244  +	"pivot": {"x":0.5,"y":0.5}
          245  +},
          246  +{
          247  +	"filename": "banner-scroll-preview",
          248  +	"frame": {"x":708,"y":2,"w":13,"h":47},
          249  +	"rotated": false,
          250  +	"trimmed": false,
          251  +	"spriteSourceSize": {"x":0,"y":0,"w":13,"h":47},
          252  +	"sourceSize": {"w":13,"h":47},
          253  +	"pivot": {"x":0.5,"y":0.5}
          254  +},
          255  +{
          256  +	"filename": "banner-side-1",
          257  +	"frame": {"x":1323,"y":55,"w":19,"h":76},
          258  +	"rotated": false,
          259  +	"trimmed": false,
          260  +	"spriteSourceSize": {"x":0,"y":0,"w":19,"h":76},
          261  +	"sourceSize": {"w":19,"h":76},
          262  +	"pivot": {"x":0.5,"y":0.5}
          263  +},
          264  +{
          265  +	"filename": "banner-side-2",
          266  +	"frame": {"x":1302,"y":55,"w":19,"h":76},
          267  +	"rotated": false,
          268  +	"trimmed": false,
          269  +	"spriteSourceSize": {"x":0,"y":0,"w":19,"h":76},
          270  +	"sourceSize": {"w":19,"h":76},
          271  +	"pivot": {"x":0.5,"y":0.5}
          272  +},
          273  +{
          274  +	"filename": "banner-side-3",
          275  +	"frame": {"x":1281,"y":55,"w":19,"h":76},
          276  +	"rotated": false,
          277  +	"trimmed": false,
          278  +	"spriteSourceSize": {"x":0,"y":0,"w":19,"h":76},
          279  +	"sourceSize": {"w":19,"h":76},
          280  +	"pivot": {"x":0.5,"y":0.5}
          281  +},
          282  +{
          283  +	"filename": "banner-side-4",
          284  +	"frame": {"x":1365,"y":55,"w":19,"h":76},
          285  +	"rotated": false,
          286  +	"trimmed": false,
          287  +	"spriteSourceSize": {"x":0,"y":0,"w":19,"h":76},
          288  +	"sourceSize": {"w":19,"h":76},
          289  +	"pivot": {"x":0.5,"y":0.5}
          290  +},
          291  +{
          292  +	"filename": "banner-side-preview",
          293  +	"frame": {"x":1344,"y":55,"w":19,"h":76},
          294  +	"rotated": false,
          295  +	"trimmed": false,
          296  +	"spriteSourceSize": {"x":0,"y":0,"w":19,"h":76},
          297  +	"sourceSize": {"w":19,"h":76},
          298  +	"pivot": {"x":0.5,"y":0.5}
          299  +},
          300  +{
          301  +	"filename": "banner-sushi-1",
          302  +	"frame": {"x":499,"y":2,"w":36,"h":13},
          303  +	"rotated": false,
          304  +	"trimmed": false,
          305  +	"spriteSourceSize": {"x":0,"y":0,"w":36,"h":13},
          306  +	"sourceSize": {"w":36,"h":13},
          307  +	"pivot": {"x":0.5,"y":0.5}
          308  +},
          309  +{
          310  +	"filename": "banner-sushi-2",
          311  +	"frame": {"x":423,"y":2,"w":36,"h":13},
          312  +	"rotated": false,
          313  +	"trimmed": false,
          314  +	"spriteSourceSize": {"x":0,"y":0,"w":36,"h":13},
          315  +	"sourceSize": {"w":36,"h":13},
          316  +	"pivot": {"x":0.5,"y":0.5}
          317  +},
          318  +{
          319  +	"filename": "banner-sushi-3",
          320  +	"frame": {"x":461,"y":2,"w":36,"h":13},
          321  +	"rotated": false,
          322  +	"trimmed": false,
          323  +	"spriteSourceSize": {"x":0,"y":0,"w":36,"h":13},
          324  +	"sourceSize": {"w":36,"h":13},
          325  +	"pivot": {"x":0.5,"y":0.5}
          326  +},
          327  +{
          328  +	"filename": "banner-sushi-preview",
          329  +	"frame": {"x":537,"y":2,"w":36,"h":13},
          330  +	"rotated": false,
          331  +	"trimmed": false,
          332  +	"spriteSourceSize": {"x":0,"y":0,"w":36,"h":13},
          333  +	"sourceSize": {"w":36,"h":13},
          334  +	"pivot": {"x":0.5,"y":0.5}
          335  +},
          336  +{
          337  +	"filename": "climb-1",
          338  +	"frame": {"x":63,"y":55,"w":15,"h":55},
          339  +	"rotated": false,
          340  +	"trimmed": true,
          341  +	"spriteSourceSize": {"x":30,"y":10,"w":15,"h":55},
          342  +	"sourceSize": {"w":71,"h":67},
          343  +	"pivot": {"x":0.5,"y":0.5}
          344  +},
          345  +{
          346  +	"filename": "climb-2",
          347  +	"frame": {"x":969,"y":2,"w":15,"h":49},
          348  +	"rotated": false,
          349  +	"trimmed": true,
          350  +	"spriteSourceSize": {"x":30,"y":16,"w":15,"h":49},
          351  +	"sourceSize": {"w":71,"h":67},
          352  +	"pivot": {"x":0.5,"y":0.5}
          353  +},
          354  +{
          355  +	"filename": "climb-3",
          356  +	"frame": {"x":627,"y":2,"w":16,"h":40},
          357  +	"rotated": false,
          358  +	"trimmed": true,
          359  +	"spriteSourceSize": {"x":30,"y":19,"w":16,"h":40},
          360  +	"sourceSize": {"w":71,"h":67},
          361  +	"pivot": {"x":0.5,"y":0.5}
          362  +},
          363  +{
          364  +	"filename": "climb-4",
          365  +	"frame": {"x":46,"y":55,"w":15,"h":55},
          366  +	"rotated": false,
          367  +	"trimmed": true,
          368  +	"spriteSourceSize": {"x":31,"y":10,"w":15,"h":55},
          369  +	"sourceSize": {"w":71,"h":67},
          370  +	"pivot": {"x":0.5,"y":0.5}
          371  +},
          372  +{
          373  +	"filename": "climb-5",
          374  +	"frame": {"x":952,"y":2,"w":15,"h":49},
          375  +	"rotated": false,
          376  +	"trimmed": true,
          377  +	"spriteSourceSize": {"x":31,"y":16,"w":15,"h":49},
          378  +	"sourceSize": {"w":71,"h":67},
          379  +	"pivot": {"x":0.5,"y":0.5}
          380  +},
          381  +{
          382  +	"filename": "climb-6",
          383  +	"frame": {"x":645,"y":2,"w":16,"h":40},
          384  +	"rotated": false,
          385  +	"trimmed": true,
          386  +	"spriteSourceSize": {"x":30,"y":19,"w":16,"h":40},
          387  +	"sourceSize": {"w":71,"h":67},
          388  +	"pivot": {"x":0.5,"y":0.5}
          389  +},
          390  +{
          391  +	"filename": "climb-preview",
          392  +	"frame": {"x":102,"y":55,"w":15,"h":55},
          393  +	"rotated": false,
          394  +	"trimmed": true,
          395  +	"spriteSourceSize": {"x":30,"y":10,"w":15,"h":55},
          396  +	"sourceSize": {"w":71,"h":67},
          397  +	"pivot": {"x":0.5,"y":0.5}
          398  +},
          399  +{
          400  +	"filename": "control-box-1",
          401  +	"frame": {"x":764,"y":2,"w":32,"h":30},
          402  +	"rotated": false,
          403  +	"trimmed": false,
          404  +	"spriteSourceSize": {"x":0,"y":0,"w":32,"h":30},
          405  +	"sourceSize": {"w":32,"h":30},
          406  +	"pivot": {"x":0.5,"y":0.5}
          407  +},
          408  +{
          409  +	"filename": "control-box-2",
          410  +	"frame": {"x":216,"y":2,"w":16,"h":27},
          411  +	"rotated": false,
          412  +	"trimmed": true,
          413  +	"spriteSourceSize": {"x":0,"y":0,"w":16,"h":27},
          414  +	"sourceSize": {"w":17,"h":27},
          415  +	"pivot": {"x":0.5,"y":0.5}
          416  +},
          417  +{
          418  +	"filename": "control-box-3",
          419  +	"frame": {"x":982,"y":55,"w":62,"h":30},
          420  +	"rotated": false,
          421  +	"trimmed": false,
          422  +	"spriteSourceSize": {"x":0,"y":0,"w":62,"h":30},
          423  +	"sourceSize": {"w":62,"h":30},
          424  +	"pivot": {"x":0.5,"y":0.5}
          425  +},
          426  +{
          427  +	"filename": "crouch",
          428  +	"frame": {"x":1022,"y":2,"w":33,"h":33},
          429  +	"rotated": false,
          430  +	"trimmed": true,
          431  +	"spriteSourceSize": {"x":21,"y":34,"w":33,"h":33},
          432  +	"sourceSize": {"w":71,"h":67},
          433  +	"pivot": {"x":0.5,"y":0.5}
          434  +},
          435  +{
          436  +	"filename": "drone-1",
          437  +	"frame": {"x":546,"y":55,"w":34,"h":48},
          438  +	"rotated": false,
          439  +	"trimmed": true,
          440  +	"spriteSourceSize": {"x":13,"y":4,"w":34,"h":48},
          441  +	"sourceSize": {"w":55,"h":52},
          442  +	"pivot": {"x":0.5,"y":0.5}
          443  +},
          444  +{
          445  +	"filename": "drone-2",
          446  +	"frame": {"x":343,"y":55,"w":29,"h":48},
          447  +	"rotated": false,
          448  +	"trimmed": true,
          449  +	"spriteSourceSize": {"x":11,"y":4,"w":29,"h":48},
          450  +	"sourceSize": {"w":55,"h":52},
          451  +	"pivot": {"x":0.5,"y":0.5}
          452  +},
          453  +{
          454  +	"filename": "drone-3",
          455  +	"frame": {"x":444,"y":55,"w":31,"h":48},
          456  +	"rotated": false,
          457  +	"trimmed": true,
          458  +	"spriteSourceSize": {"x":12,"y":3,"w":31,"h":48},
          459  +	"sourceSize": {"w":55,"h":52},
          460  +	"pivot": {"x":0.5,"y":0.5}
          461  +},
          462  +{
          463  +	"filename": "drone-4",
          464  +	"frame": {"x":312,"y":55,"w":29,"h":48},
          465  +	"rotated": false,
          466  +	"trimmed": true,
          467  +	"spriteSourceSize": {"x":15,"y":4,"w":29,"h":48},
          468  +	"sourceSize": {"w":55,"h":52},
          469  +	"pivot": {"x":0.5,"y":0.5}
          470  +},
          471  +{
          472  +	"filename": "drone_preview",
          473  +	"frame": {"x":510,"y":55,"w":34,"h":48},
          474  +	"rotated": false,
          475  +	"trimmed": true,
          476  +	"spriteSourceSize": {"x":13,"y":4,"w":34,"h":48},
          477  +	"sourceSize": {"w":55,"h":52},
          478  +	"pivot": {"x":0.5,"y":0.5}
          479  +},
          480  +{
          481  +	"filename": "enemy-explosion-1",
          482  +	"frame": {"x":825,"y":2,"w":31,"h":31},
          483  +	"rotated": false,
          484  +	"trimmed": true,
          485  +	"spriteSourceSize": {"x":12,"y":13,"w":31,"h":31},
          486  +	"sourceSize": {"w":55,"h":52},
          487  +	"pivot": {"x":0.5,"y":0.5}
          488  +},
          489  +{
          490  +	"filename": "enemy-explosion-2",
          491  +	"frame": {"x":582,"y":55,"w":42,"h":40},
          492  +	"rotated": false,
          493  +	"trimmed": true,
          494  +	"spriteSourceSize": {"x":5,"y":9,"w":42,"h":40},
          495  +	"sourceSize": {"w":55,"h":52},
          496  +	"pivot": {"x":0.5,"y":0.5}
          497  +},
          498  +{
          499  +	"filename": "enemy-explosion-3",
          500  +	"frame": {"x":1231,"y":55,"w":48,"h":46},
          501  +	"rotated": false,
          502  +	"trimmed": true,
          503  +	"spriteSourceSize": {"x":2,"y":4,"w":48,"h":46},
          504  +	"sourceSize": {"w":55,"h":52},
          505  +	"pivot": {"x":0.5,"y":0.5}
          506  +},
          507  +{
          508  +	"filename": "enemy-explosion-4",
          509  +	"frame": {"x":273,"y":133,"w":52,"h":50},
          510  +	"rotated": false,
          511  +	"trimmed": true,
          512  +	"spriteSourceSize": {"x":1,"y":2,"w":52,"h":50},
          513  +	"sourceSize": {"w":55,"h":52},
          514  +	"pivot": {"x":0.5,"y":0.5}
          515  +},
          516  +{
          517  +	"filename": "enemy-explosion-5",
          518  +	"frame": {"x":555,"y":133,"w":55,"h":52},
          519  +	"rotated": false,
          520  +	"trimmed": false,
          521  +	"spriteSourceSize": {"x":0,"y":0,"w":55,"h":52},
          522  +	"sourceSize": {"w":55,"h":52},
          523  +	"pivot": {"x":0.5,"y":0.5}
          524  +},
          525  +{
          526  +	"filename": "enemy-explosion-6",
          527  +	"frame": {"x":498,"y":133,"w":55,"h":52},
          528  +	"rotated": false,
          529  +	"trimmed": false,
          530  +	"spriteSourceSize": {"x":0,"y":0,"w":55,"h":52},
          531  +	"sourceSize": {"w":55,"h":52},
          532  +	"pivot": {"x":0.5,"y":0.5}
          533  +},
          534  +{
          535  +	"filename": "enemy-explosion-preview",
          536  +	"frame": {"x":662,"y":55,"w":34,"h":48},
          537  +	"rotated": false,
          538  +	"trimmed": true,
          539  +	"spriteSourceSize": {"x":13,"y":4,"w":34,"h":48},
          540  +	"sourceSize": {"w":55,"h":52},
          541  +	"pivot": {"x":0.5,"y":0.5}
          542  +},
          543  +{
          544  +	"filename": "hurt",
          545  +	"frame": {"x":141,"y":55,"w":25,"h":47},
          546  +	"rotated": false,
          547  +	"trimmed": true,
          548  +	"spriteSourceSize": {"x":21,"y":20,"w":25,"h":47},
          549  +	"sourceSize": {"w":71,"h":67},
          550  +	"pivot": {"x":0.5,"y":0.5}
          551  +},
          552  +{
          553  +	"filename": "idle-1",
          554  +	"frame": {"x":1057,"y":2,"w":21,"h":46},
          555  +	"rotated": false,
          556  +	"trimmed": true,
          557  +	"spriteSourceSize": {"x":28,"y":21,"w":21,"h":46},
          558  +	"sourceSize": {"w":71,"h":67},
          559  +	"pivot": {"x":0.5,"y":0.5}
          560  +},
          561  +{
          562  +	"filename": "idle-2",
          563  +	"frame": {"x":24,"y":55,"w":20,"h":50},
          564  +	"rotated": false,
          565  +	"trimmed": true,
          566  +	"spriteSourceSize": {"x":28,"y":17,"w":20,"h":50},
          567  +	"sourceSize": {"w":71,"h":67},
          568  +	"pivot": {"x":0.5,"y":0.5}
          569  +},
          570  +{
          571  +	"filename": "idle-3",
          572  +	"frame": {"x":119,"y":55,"w":20,"h":51},
          573  +	"rotated": false,
          574  +	"trimmed": true,
          575  +	"spriteSourceSize": {"x":28,"y":16,"w":20,"h":51},
          576  +	"sourceSize": {"w":71,"h":67},
          577  +	"pivot": {"x":0.5,"y":0.5}
          578  +},
          579  +{
          580  +	"filename": "idle-4",
          581  +	"frame": {"x":80,"y":55,"w":20,"h":50},
          582  +	"rotated": false,
          583  +	"trimmed": true,
          584  +	"spriteSourceSize": {"x":28,"y":17,"w":20,"h":50},
          585  +	"sourceSize": {"w":71,"h":67},
          586  +	"pivot": {"x":0.5,"y":0.5}
          587  +},
          588  +{
          589  +	"filename": "idle-peview",
          590  +	"frame": {"x":1080,"y":2,"w":21,"h":46},
          591  +	"rotated": false,
          592  +	"trimmed": true,
          593  +	"spriteSourceSize": {"x":28,"y":21,"w":21,"h":46},
          594  +	"sourceSize": {"w":71,"h":67},
          595  +	"pivot": {"x":0.5,"y":0.5}
          596  +},
          597  +{
          598  +	"filename": "jump-1",
          599  +	"frame": {"x":626,"y":55,"w":34,"h":48},
          600  +	"rotated": false,
          601  +	"trimmed": true,
          602  +	"spriteSourceSize": {"x":22,"y":14,"w":34,"h":48},
          603  +	"sourceSize": {"w":71,"h":67},
          604  +	"pivot": {"x":0.5,"y":0.5}
          605  +},
          606  +{
          607  +	"filename": "jump-2",
          608  +	"frame": {"x":269,"y":55,"w":41,"h":35},
          609  +	"rotated": false,
          610  +	"trimmed": true,
          611  +	"spriteSourceSize": {"x":15,"y":15,"w":41,"h":35},
          612  +	"sourceSize": {"w":71,"h":67},
          613  +	"pivot": {"x":0.5,"y":0.5}
          614  +},
          615  +{
          616  +	"filename": "jump-3",
          617  +	"frame": {"x":1342,"y":2,"w":37,"h":32},
          618  +	"rotated": false,
          619  +	"trimmed": true,
          620  +	"spriteSourceSize": {"x":21,"y":18,"w":37,"h":32},
          621  +	"sourceSize": {"w":71,"h":67},
          622  +	"pivot": {"x":0.5,"y":0.5}
          623  +},
          624  +{
          625  +	"filename": "jump-4",
          626  +	"frame": {"x":233,"y":55,"w":34,"h":40},
          627  +	"rotated": false,
          628  +	"trimmed": true,
          629  +	"spriteSourceSize": {"x":23,"y":16,"w":34,"h":40},
          630  +	"sourceSize": {"w":71,"h":67},
          631  +	"pivot": {"x":0.5,"y":0.5}
          632  +},
          633  +{
          634  +	"filename": "jump-preview",
          635  +	"frame": {"x":2,"y":55,"w":20,"h":50},
          636  +	"rotated": false,
          637  +	"trimmed": true,
          638  +	"spriteSourceSize": {"x":28,"y":17,"w":20,"h":50},
          639  +	"sourceSize": {"w":71,"h":67},
          640  +	"pivot": {"x":0.5,"y":0.5}
          641  +},
          642  +{
          643  +	"filename": "monitor-face-1",
          644  +	"frame": {"x":193,"y":2,"w":21,"h":18},
          645  +	"rotated": false,
          646  +	"trimmed": false,
          647  +	"spriteSourceSize": {"x":0,"y":0,"w":21,"h":18},
          648  +	"sourceSize": {"w":21,"h":18},
          649  +	"pivot": {"x":0.5,"y":0.5}
          650  +},
          651  +{
          652  +	"filename": "monitor-face-2",
          653  +	"frame": {"x":101,"y":2,"w":21,"h":18},
          654  +	"rotated": false,
          655  +	"trimmed": false,
          656  +	"spriteSourceSize": {"x":0,"y":0,"w":21,"h":18},
          657  +	"sourceSize": {"w":21,"h":18},
          658  +	"pivot": {"x":0.5,"y":0.5}
          659  +},
          660  +{
          661  +	"filename": "monitor-face-3",
          662  +	"frame": {"x":124,"y":2,"w":21,"h":18},
          663  +	"rotated": false,
          664  +	"trimmed": false,
          665  +	"spriteSourceSize": {"x":0,"y":0,"w":21,"h":18},
          666  +	"sourceSize": {"w":21,"h":18},
          667  +	"pivot": {"x":0.5,"y":0.5}
          668  +},
          669  +{
          670  +	"filename": "monitor-face-4",
          671  +	"frame": {"x":147,"y":2,"w":21,"h":18},
          672  +	"rotated": false,
          673  +	"trimmed": false,
          674  +	"spriteSourceSize": {"x":0,"y":0,"w":21,"h":18},
          675  +	"sourceSize": {"w":21,"h":18},
          676  +	"pivot": {"x":0.5,"y":0.5}
          677  +},
          678  +{
          679  +	"filename": "monitorface_preview",
          680  +	"frame": {"x":170,"y":2,"w":21,"h":18},
          681  +	"rotated": false,
          682  +	"trimmed": false,
          683  +	"spriteSourceSize": {"x":0,"y":0,"w":21,"h":18},
          684  +	"sourceSize": {"w":21,"h":18},
          685  +	"pivot": {"x":0.5,"y":0.5}
          686  +},
          687  +{
          688  +	"filename": "run-1",
          689  +	"frame": {"x":1046,"y":55,"w":41,"h":51},
          690  +	"rotated": false,
          691  +	"trimmed": true,
          692  +	"spriteSourceSize": {"x":10,"y":16,"w":41,"h":51},
          693  +	"sourceSize": {"w":71,"h":67},
          694  +	"pivot": {"x":0.5,"y":0.5}
          695  +},
          696  +{
          697  +	"filename": "run-2",
          698  +	"frame": {"x":1138,"y":55,"w":47,"h":46},
          699  +	"rotated": false,
          700  +	"trimmed": true,
          701  +	"spriteSourceSize": {"x":7,"y":18,"w":47,"h":46},
          702  +	"sourceSize": {"w":71,"h":67},
          703  +	"pivot": {"x":0.5,"y":0.5}
          704  +},
          705  +{
          706  +	"filename": "run-3",
          707  +	"frame": {"x":735,"y":55,"w":36,"h":48},
          708  +	"rotated": false,
          709  +	"trimmed": true,
          710  +	"spriteSourceSize": {"x":16,"y":19,"w":36,"h":48},
          711  +	"sourceSize": {"w":71,"h":67},
          712  +	"pivot": {"x":0.5,"y":0.5}
          713  +},
          714  +{
          715  +	"filename": "run-4",
          716  +	"frame": {"x":205,"y":55,"w":26,"h":48},
          717  +	"rotated": false,
          718  +	"trimmed": true,
          719  +	"spriteSourceSize": {"x":26,"y":19,"w":26,"h":48},
          720  +	"sourceSize": {"w":71,"h":67},
          721  +	"pivot": {"x":0.5,"y":0.5}
          722  +},
          723  +{
          724  +	"filename": "run-5",
          725  +	"frame": {"x":1187,"y":55,"w":42,"h":51},
          726  +	"rotated": false,
          727  +	"trimmed": true,
          728  +	"spriteSourceSize": {"x":10,"y":16,"w":42,"h":51},
          729  +	"sourceSize": {"w":71,"h":67},
          730  +	"pivot": {"x":0.5,"y":0.5}
          731  +},
          732  +{
          733  +	"filename": "run-6",
          734  +	"frame": {"x":1089,"y":55,"w":47,"h":46},
          735  +	"rotated": false,
          736  +	"trimmed": true,
          737  +	"spriteSourceSize": {"x":7,"y":18,"w":47,"h":46},
          738  +	"sourceSize": {"w":71,"h":67},
          739  +	"pivot": {"x":0.5,"y":0.5}
          740  +},
          741  +{
          742  +	"filename": "run-7",
          743  +	"frame": {"x":698,"y":55,"w":35,"h":48},
          744  +	"rotated": false,
          745  +	"trimmed": true,
          746  +	"spriteSourceSize": {"x":16,"y":19,"w":35,"h":48},
          747  +	"sourceSize": {"w":71,"h":67},
          748  +	"pivot": {"x":0.5,"y":0.5}
          749  +},
          750  +{
          751  +	"filename": "run-8",
          752  +	"frame": {"x":477,"y":55,"w":31,"h":48},
          753  +	"rotated": false,
          754  +	"trimmed": true,
          755  +	"spriteSourceSize": {"x":21,"y":19,"w":31,"h":48},
          756  +	"sourceSize": {"w":71,"h":67},
          757  +	"pivot": {"x":0.5,"y":0.5}
          758  +},
          759  +{
          760  +	"filename": "run-preview",
          761  +	"frame": {"x":939,"y":55,"w":41,"h":51},
          762  +	"rotated": false,
          763  +	"trimmed": true,
          764  +	"spriteSourceSize": {"x":10,"y":16,"w":41,"h":51},
          765  +	"sourceSize": {"w":71,"h":67},
          766  +	"pivot": {"x":0.5,"y":0.5}
          767  +},
          768  +{
          769  +	"filename": "run-shoot-1",
          770  +	"frame": {"x":162,"y":133,"w":51,"h":51},
          771  +	"rotated": false,
          772  +	"trimmed": true,
          773  +	"spriteSourceSize": {"x":10,"y":16,"w":51,"h":51},
          774  +	"sourceSize": {"w":71,"h":67},
          775  +	"pivot": {"x":0.5,"y":0.5}
          776  +},
          777  +{
          778  +	"filename": "run-shoot-2",
          779  +	"frame": {"x":52,"y":133,"w":55,"h":46},
          780  +	"rotated": false,
          781  +	"trimmed": true,
          782  +	"spriteSourceSize": {"x":7,"y":18,"w":55,"h":46},
          783  +	"sourceSize": {"w":71,"h":67},
          784  +	"pivot": {"x":0.5,"y":0.5}
          785  +},
          786  +{
          787  +	"filename": "run-shoot-3",
          788  +	"frame": {"x":1386,"y":55,"w":47,"h":48},
          789  +	"rotated": false,
          790  +	"trimmed": true,
          791  +	"spriteSourceSize": {"x":16,"y":19,"w":47,"h":48},
          792  +	"sourceSize": {"w":71,"h":67},
          793  +	"pivot": {"x":0.5,"y":0.5}
          794  +},
          795  +{
          796  +	"filename": "run-shoot-4",
          797  +	"frame": {"x":819,"y":55,"w":37,"h":48},
          798  +	"rotated": false,
          799  +	"trimmed": true,
          800  +	"spriteSourceSize": {"x":26,"y":19,"w":37,"h":48},
          801  +	"sourceSize": {"w":71,"h":67},
          802  +	"pivot": {"x":0.5,"y":0.5}
          803  +},
          804  +{
          805  +	"filename": "run-shoot-5",
          806  +	"frame": {"x":327,"y":133,"w":53,"h":51},
          807  +	"rotated": false,
          808  +	"trimmed": true,
          809  +	"spriteSourceSize": {"x":10,"y":16,"w":53,"h":51},
          810  +	"sourceSize": {"w":71,"h":67},
          811  +	"pivot": {"x":0.5,"y":0.5}
          812  +},
          813  +{
          814  +	"filename": "run-shoot-6",
          815  +	"frame": {"x":215,"y":133,"w":56,"h":46},
          816  +	"rotated": false,
          817  +	"trimmed": true,
          818  +	"spriteSourceSize": {"x":7,"y":18,"w":56,"h":46},
          819  +	"sourceSize": {"w":71,"h":67},
          820  +	"pivot": {"x":0.5,"y":0.5}
          821  +},
          822  +{
          823  +	"filename": "run-shoot-7",
          824  +	"frame": {"x":2,"y":133,"w":48,"h":48},
          825  +	"rotated": false,
          826  +	"trimmed": true,
          827  +	"spriteSourceSize": {"x":16,"y":19,"w":48,"h":48},
          828  +	"sourceSize": {"w":71,"h":67},
          829  +	"pivot": {"x":0.5,"y":0.5}
          830  +},
          831  +{
          832  +	"filename": "run-shoot-8",
          833  +	"frame": {"x":895,"y":55,"w":42,"h":48},
          834  +	"rotated": false,
          835  +	"trimmed": true,
          836  +	"spriteSourceSize": {"x":21,"y":19,"w":42,"h":48},
          837  +	"sourceSize": {"w":71,"h":67},
          838  +	"pivot": {"x":0.5,"y":0.5}
          839  +},
          840  +{
          841  +	"filename": "run-shoot-preview",
          842  +	"frame": {"x":109,"y":133,"w":51,"h":51},
          843  +	"rotated": false,
          844  +	"trimmed": true,
          845  +	"spriteSourceSize": {"x":10,"y":16,"w":51,"h":51},
          846  +	"sourceSize": {"w":71,"h":67},
          847  +	"pivot": {"x":0.5,"y":0.5}
          848  +},
          849  +{
          850  +	"filename": "shoot",
          851  +	"frame": {"x":858,"y":55,"w":35,"h":51},
          852  +	"rotated": false,
          853  +	"trimmed": true,
          854  +	"spriteSourceSize": {"x":20,"y":16,"w":35,"h":51},
          855  +	"sourceSize": {"w":71,"h":67},
          856  +	"pivot": {"x":0.5,"y":0.5}
          857  +},
          858  +{
          859  +	"filename": "shot-1",
          860  +	"frame": {"x":41,"y":2,"w":12,"h":5},
          861  +	"rotated": false,
          862  +	"trimmed": true,
          863  +	"spriteSourceSize": {"x":1,"y":3,"w":12,"h":5},
          864  +	"sourceSize": {"w":15,"h":11},
          865  +	"pivot": {"x":0.5,"y":0.5}
          866  +},
          867  +{
          868  +	"filename": "shot-2",
          869  +	"frame": {"x":2,"y":2,"w":10,"h":7},
          870  +	"rotated": false,
          871  +	"trimmed": true,
          872  +	"spriteSourceSize": {"x":1,"y":2,"w":10,"h":7},
          873  +	"sourceSize": {"w":15,"h":11},
          874  +	"pivot": {"x":0.5,"y":0.5}
          875  +},
          876  +{
          877  +	"filename": "shot-3",
          878  +	"frame": {"x":14,"y":2,"w":11,"h":6},
          879  +	"rotated": false,
          880  +	"trimmed": true,
          881  +	"spriteSourceSize": {"x":1,"y":3,"w":11,"h":6},
          882  +	"sourceSize": {"w":15,"h":11},
          883  +	"pivot": {"x":0.5,"y":0.5}
          884  +},
          885  +{
          886  +	"filename": "shot-hit-1",
          887  +	"frame": {"x":55,"y":2,"w":9,"h":9},
          888  +	"rotated": false,
          889  +	"trimmed": true,
          890  +	"spriteSourceSize": {"x":3,"y":1,"w":9,"h":9},
          891  +	"sourceSize": {"w":15,"h":11},
          892  +	"pivot": {"x":0.5,"y":0.5}
          893  +},
          894  +{
          895  +	"filename": "shot-hit-2",
          896  +	"frame": {"x":66,"y":2,"w":9,"h":9},
          897  +	"rotated": false,
          898  +	"trimmed": true,
          899  +	"spriteSourceSize": {"x":3,"y":1,"w":9,"h":9},
          900  +	"sourceSize": {"w":15,"h":11},
          901  +	"pivot": {"x":0.5,"y":0.5}
          902  +},
          903  +{
          904  +	"filename": "shot-hit-3",
          905  +	"frame": {"x":88,"y":2,"w":11,"h":11},
          906  +	"rotated": false,
          907  +	"trimmed": true,
          908  +	"spriteSourceSize": {"x":2,"y":0,"w":11,"h":11},
          909  +	"sourceSize": {"w":15,"h":11},
          910  +	"pivot": {"x":0.5,"y":0.5}
          911  +},
          912  +{
          913  +	"filename": "shot-hit-preview",
          914  +	"frame": {"x":77,"y":2,"w":9,"h":9},
          915  +	"rotated": false,
          916  +	"trimmed": true,
          917  +	"spriteSourceSize": {"x":3,"y":1,"w":9,"h":9},
          918  +	"sourceSize": {"w":15,"h":11},
          919  +	"pivot": {"x":0.5,"y":0.5}
          920  +},
          921  +{
          922  +	"filename": "shot-preview",
          923  +	"frame": {"x":27,"y":2,"w":12,"h":5},
          924  +	"rotated": false,
          925  +	"trimmed": true,
          926  +	"spriteSourceSize": {"x":1,"y":3,"w":12,"h":5},
          927  +	"sourceSize": {"w":15,"h":11},
          928  +	"pivot": {"x":0.5,"y":0.5}
          929  +},
          930  +{
          931  +	"filename": "turret-1",
          932  +	"frame": {"x":315,"y":2,"w":25,"h":23},
          933  +	"rotated": false,
          934  +	"trimmed": false,
          935  +	"spriteSourceSize": {"x":0,"y":0,"w":25,"h":23},
          936  +	"sourceSize": {"w":25,"h":23},
          937  +	"pivot": {"x":0.5,"y":0.5}
          938  +},
          939  +{
          940  +	"filename": "turret-2",
          941  +	"frame": {"x":342,"y":2,"w":25,"h":23},
          942  +	"rotated": false,
          943  +	"trimmed": false,
          944  +	"spriteSourceSize": {"x":0,"y":0,"w":25,"h":23},
          945  +	"sourceSize": {"w":25,"h":23},
          946  +	"pivot": {"x":0.5,"y":0.5}
          947  +},
          948  +{
          949  +	"filename": "turret-3",
          950  +	"frame": {"x":261,"y":2,"w":25,"h":23},
          951  +	"rotated": false,
          952  +	"trimmed": false,
          953  +	"spriteSourceSize": {"x":0,"y":0,"w":25,"h":23},
          954  +	"sourceSize": {"w":25,"h":23},
          955  +	"pivot": {"x":0.5,"y":0.5}
          956  +},
          957  +{
          958  +	"filename": "turret-4",
          959  +	"frame": {"x":234,"y":2,"w":25,"h":23},
          960  +	"rotated": false,
          961  +	"trimmed": false,
          962  +	"spriteSourceSize": {"x":0,"y":0,"w":25,"h":23},
          963  +	"sourceSize": {"w":25,"h":23},
          964  +	"pivot": {"x":0.5,"y":0.5}
          965  +},
          966  +{
          967  +	"filename": "turret-5",
          968  +	"frame": {"x":288,"y":2,"w":25,"h":23},
          969  +	"rotated": false,
          970  +	"trimmed": false,
          971  +	"spriteSourceSize": {"x":0,"y":0,"w":25,"h":23},
          972  +	"sourceSize": {"w":25,"h":23},
          973  +	"pivot": {"x":0.5,"y":0.5}
          974  +},
          975  +{
          976  +	"filename": "turret-6",
          977  +	"frame": {"x":396,"y":2,"w":25,"h":23},
          978  +	"rotated": false,
          979  +	"trimmed": false,
          980  +	"spriteSourceSize": {"x":0,"y":0,"w":25,"h":23},
          981  +	"sourceSize": {"w":25,"h":23},
          982  +	"pivot": {"x":0.5,"y":0.5}
          983  +},
          984  +{
          985  +	"filename": "turret-preview",
          986  +	"frame": {"x":369,"y":2,"w":25,"h":23},
          987  +	"rotated": false,
          988  +	"trimmed": false,
          989  +	"spriteSourceSize": {"x":0,"y":0,"w":25,"h":23},
          990  +	"sourceSize": {"w":25,"h":23},
          991  +	"pivot": {"x":0.5,"y":0.5}
          992  +},
          993  +{
          994  +	"filename": "v-police",
          995  +	"frame": {"x":1013,"y":133,"w":163,"h":60},
          996  +	"rotated": false,
          997  +	"trimmed": false,
          998  +	"spriteSourceSize": {"x":0,"y":0,"w":163,"h":60},
          999  +	"sourceSize": {"w":163,"h":60},
         1000  +	"pivot": {"x":0.5,"y":0.5}
         1001  +},
         1002  +{
         1003  +	"filename": "v-red",
         1004  +	"frame": {"x":915,"y":133,"w":96,"h":61},
         1005  +	"rotated": false,
         1006  +	"trimmed": false,
         1007  +	"spriteSourceSize": {"x":0,"y":0,"w":96,"h":61},
         1008  +	"sourceSize": {"w":96,"h":61},
         1009  +	"pivot": {"x":0.5,"y":0.5}
         1010  +},
         1011  +{
         1012  +	"filename": "v-truck",
         1013  +	"frame": {"x":1178,"y":133,"w":257,"h":104},
         1014  +	"rotated": false,
         1015  +	"trimmed": false,
         1016  +	"spriteSourceSize": {"x":0,"y":0,"w":257,"h":104},
         1017  +	"sourceSize": {"w":257,"h":104},
         1018  +	"pivot": {"x":0.5,"y":0.5}
         1019  +},
         1020  +{
         1021  +	"filename": "v-yellow",
         1022  +	"frame": {"x":820,"y":133,"w":93,"h":60},
         1023  +	"rotated": false,
         1024  +	"trimmed": false,
         1025  +	"spriteSourceSize": {"x":0,"y":0,"w":93,"h":60},
         1026  +	"sourceSize": {"w":93,"h":60},
         1027  +	"pivot": {"x":0.5,"y":0.5}
         1028  +},
         1029  +{
         1030  +	"filename": "walk-1",
         1031  +	"frame": {"x":1320,"y":2,"w":20,"h":49},
         1032  +	"rotated": false,
         1033  +	"trimmed": true,
         1034  +	"spriteSourceSize": {"x":28,"y":18,"w":20,"h":49},
         1035  +	"sourceSize": {"w":71,"h":67},
         1036  +	"pivot": {"x":0.5,"y":0.5}
         1037  +},
         1038  +{
         1039  +	"filename": "walk-10",
         1040  +	"frame": {"x":1257,"y":2,"w":19,"h":48},
         1041  +	"rotated": false,
         1042  +	"trimmed": true,
         1043  +	"spriteSourceSize": {"x":26,"y":19,"w":19,"h":48},
         1044  +	"sourceSize": {"w":71,"h":67},
         1045  +	"pivot": {"x":0.5,"y":0.5}
         1046  +},
         1047  +{
         1048  +	"filename": "walk-11",
         1049  +	"frame": {"x":986,"y":2,"w":16,"h":49},
         1050  +	"rotated": false,
         1051  +	"trimmed": true,
         1052  +	"spriteSourceSize": {"x":27,"y":18,"w":16,"h":49},
         1053  +	"sourceSize": {"w":71,"h":67},
         1054  +	"pivot": {"x":0.5,"y":0.5}
         1055  +},
         1056  +{
         1057  +	"filename": "walk-12",
         1058  +	"frame": {"x":738,"y":2,"w":11,"h":50},
         1059  +	"rotated": false,
         1060  +	"trimmed": true,
         1061  +	"spriteSourceSize": {"x":30,"y":17,"w":11,"h":50},
         1062  +	"sourceSize": {"w":71,"h":67},
         1063  +	"pivot": {"x":0.5,"y":0.5}
         1064  +},
         1065  +{
         1066  +	"filename": "walk-13",
         1067  +	"frame": {"x":895,"y":2,"w":11,"h":51},
         1068  +	"rotated": false,
         1069  +	"trimmed": true,
         1070  +	"spriteSourceSize": {"x":30,"y":16,"w":11,"h":51},
         1071  +	"sourceSize": {"w":71,"h":67},
         1072  +	"pivot": {"x":0.5,"y":0.5}
         1073  +},
         1074  +{
         1075  +	"filename": "walk-14",
         1076  +	"frame": {"x":937,"y":2,"w":13,"h":51},
         1077  +	"rotated": false,
         1078  +	"trimmed": true,
         1079  +	"spriteSourceSize": {"x":29,"y":16,"w":13,"h":51},
         1080  +	"sourceSize": {"w":71,"h":67},
         1081  +	"pivot": {"x":0.5,"y":0.5}
         1082  +},
         1083  +{
         1084  +	"filename": "walk-15",
         1085  +	"frame": {"x":921,"y":2,"w":14,"h":50},
         1086  +	"rotated": false,
         1087  +	"trimmed": true,
         1088  +	"spriteSourceSize": {"x":28,"y":17,"w":14,"h":50},
         1089  +	"sourceSize": {"w":71,"h":67},
         1090  +	"pivot": {"x":0.5,"y":0.5}
         1091  +},
         1092  +{
         1093  +	"filename": "walk-16",
         1094  +	"frame": {"x":1381,"y":2,"w":19,"h":50},
         1095  +	"rotated": false,
         1096  +	"trimmed": true,
         1097  +	"spriteSourceSize": {"x":27,"y":17,"w":19,"h":50},
         1098  +	"sourceSize": {"w":71,"h":67},
         1099  +	"pivot": {"x":0.5,"y":0.5}
         1100  +},
         1101  +{
         1102  +	"filename": "walk-2",
         1103  +	"frame": {"x":1154,"y":2,"w":19,"h":48},
         1104  +	"rotated": false,
         1105  +	"trimmed": true,
         1106  +	"spriteSourceSize": {"x":26,"y":19,"w":19,"h":48},
         1107  +	"sourceSize": {"w":71,"h":67},
         1108  +	"pivot": {"x":0.5,"y":0.5}
         1109  +},
         1110  +{
         1111  +	"filename": "walk-3",
         1112  +	"frame": {"x":1004,"y":2,"w":16,"h":49},
         1113  +	"rotated": false,
         1114  +	"trimmed": true,
         1115  +	"spriteSourceSize": {"x":27,"y":18,"w":16,"h":49},
         1116  +	"sourceSize": {"w":71,"h":67},
         1117  +	"pivot": {"x":0.5,"y":0.5}
         1118  +},
         1119  +{
         1120  +	"filename": "walk-4",
         1121  +	"frame": {"x":751,"y":2,"w":11,"h":50},
         1122  +	"rotated": false,
         1123  +	"trimmed": true,
         1124  +	"spriteSourceSize": {"x":30,"y":17,"w":11,"h":50},
         1125  +	"sourceSize": {"w":71,"h":67},
         1126  +	"pivot": {"x":0.5,"y":0.5}
         1127  +},
         1128  +{
         1129  +	"filename": "walk-5",
         1130  +	"frame": {"x":908,"y":2,"w":11,"h":51},
         1131  +	"rotated": false,
         1132  +	"trimmed": true,
         1133  +	"spriteSourceSize": {"x":30,"y":16,"w":11,"h":51},
         1134  +	"sourceSize": {"w":71,"h":67},
         1135  +	"pivot": {"x":0.5,"y":0.5}
         1136  +},
         1137  +{
         1138  +	"filename": "walk-6",
         1139  +	"frame": {"x":798,"y":2,"w":11,"h":51},
         1140  +	"rotated": false,
         1141  +	"trimmed": true,
         1142  +	"spriteSourceSize": {"x":30,"y":16,"w":11,"h":51},
         1143  +	"sourceSize": {"w":71,"h":67},
         1144  +	"pivot": {"x":0.5,"y":0.5}
         1145  +},
         1146  +{
         1147  +	"filename": "walk-7",
         1148  +	"frame": {"x":811,"y":2,"w":12,"h":50},
         1149  +	"rotated": false,
         1150  +	"trimmed": true,
         1151  +	"spriteSourceSize": {"x":30,"y":17,"w":12,"h":50},
         1152  +	"sourceSize": {"w":71,"h":67},
         1153  +	"pivot": {"x":0.5,"y":0.5}
         1154  +},
         1155  +{
         1156  +	"filename": "walk-8",
         1157  +	"frame": {"x":1175,"y":2,"w":17,"h":50},
         1158  +	"rotated": false,
         1159  +	"trimmed": true,
         1160  +	"spriteSourceSize": {"x":29,"y":17,"w":17,"h":50},
         1161  +	"sourceSize": {"w":71,"h":67},
         1162  +	"pivot": {"x":0.5,"y":0.5}
         1163  +},
         1164  +{
         1165  +	"filename": "walk-9",
         1166  +	"frame": {"x":1299,"y":2,"w":19,"h":49},
         1167  +	"rotated": false,
         1168  +	"trimmed": true,
         1169  +	"spriteSourceSize": {"x":29,"y":18,"w":19,"h":49},
         1170  +	"sourceSize": {"w":71,"h":67},
         1171  +	"pivot": {"x":0.5,"y":0.5}
         1172  +},
         1173  +{
         1174  +	"filename": "walk-preview",
         1175  +	"frame": {"x":1402,"y":2,"w":20,"h":49},
         1176  +	"rotated": false,
         1177  +	"trimmed": true,
         1178  +	"spriteSourceSize": {"x":28,"y":18,"w":20,"h":49},
         1179  +	"sourceSize": {"w":71,"h":67},
         1180  +	"pivot": {"x":0.5,"y":0.5}
         1181  +}],
         1182  +"meta": {
         1183  +	"app": "http://www.codeandweb.com/texturepacker",
         1184  +	"version": "1.0",
         1185  +	"image": "atlas.png",
         1186  +	"format": "RGBA8888",
         1187  +	"size": {"w":1437,"h":239},
         1188  +	"scale": "1",
         1189  +	"smartupdate": "$TexturePacker:SmartUpdate:133d307a83f4c0480bfd6bd8d553a8ee:a09b2f18f63aa97ec00a8aa0c1bb7edd:cbce6b53f0f49e0bf15173c25c41f876$"
         1190  +}
         1191  +}

Added src/worpt/assets/warped_city/atlas/atlas.lua.

            1  +local frames = {
            2  +    {
            3  +        filename = "antenna",
            4  +        frame = {x=612,y=133,w=21,h=95},
            5  +        rotated = false,
            6  +        trimmed = true,
            7  +        spriteSourceSize = {x=1,y=1,w=21,h=95},
            8  +        sourceSize = {w=22,h=96},
            9  +        pivot = {x=0.5,y=0.5}
           10  +    },
           11  +    {
           12  +        filename = "back-jump-1",
           13  +        frame = {x=575,y=2,w=24,h=27},
           14  +        rotated = false,
           15  +        trimmed = true,
           16  +        spriteSourceSize = {x=24,y=40,w=24,h=27},
           17  +        sourceSize = {w=71,h=67},
           18  +        pivot = {x=0.5,y=0.5}
           19  +    },
           20  +    {
           21  +        filename = "back-jump-2",
           22  +        frame = {x=374,y=55,w=35,h=42},
           23  +        rotated = false,
           24  +        trimmed = true,
           25  +        spriteSourceSize = {x=15,y=25,w=35,h=42},
           26  +        sourceSize = {w=71,h=67},
           27  +        pivot = {x=0.5,y=0.5}
           28  +    },
           29  +    {
           30  +        filename = "back-jump-3",
           31  +        frame = {x=168,y=55,w=35,h=38},
           32  +        rotated = false,
           33  +        trimmed = true,
           34  +        spriteSourceSize = {x=15,y=20,w=35,h=38},
           35  +        sourceSize = {w=71,h=67},
           36  +        pivot = {x=0.5,y=0.5}
           37  +    },
           38  +    {
           39  +        filename = "back-jump-4",
           40  +        frame = {x=773,y=55,w=44,h=40},
           41  +        rotated = false,
           42  +        trimmed = true,
           43  +        spriteSourceSize = {x=12,y=16,w=44,h=40},
           44  +        sourceSize = {w=71,h=67},
           45  +        pivot = {x=0.5,y=0.5}
           46  +    },
           47  +    {
           48  +        filename = "back-jump-5",
           49  +        frame = {x=411,y=55,w=31,h=47},
           50  +        rotated = false,
           51  +        trimmed = true,
           52  +        spriteSourceSize = {x=17,y=18,w=31,h=47},
           53  +        sourceSize = {w=71,h=67},
           54  +        pivot = {x=0.5,y=0.5}
           55  +    },
           56  +    {
           57  +        filename = "back-jump-6",
           58  +        frame = {x=1124,y=2,w=28,h=39},
           59  +        rotated = false,
           60  +        trimmed = true,
           61  +        spriteSourceSize = {x=24,y=28,w=28,h=39},
           62  +        sourceSize = {w=71,h=67},
           63  +        pivot = {x=0.5,y=0.5}
           64  +    },
           65  +    {
           66  +        filename = "back-jump-7",
           67  +        frame = {x=858,y=2,w=35,h=27},
           68  +        rotated = false,
           69  +        trimmed = true,
           70  +        spriteSourceSize = {x=19,y=40,w=35,h=27},
           71  +        sourceSize = {w=71,h=67},
           72  +        pivot = {x=0.5,y=0.5}
           73  +    },
           74  +    {
           75  +        filename = "back-jump-preview",
           76  +        frame = {x=601,y=2,w=24,h=27},
           77  +        rotated = false,
           78  +        trimmed = true,
           79  +        spriteSourceSize = {x=24,y=40,w=24,h=27},
           80  +        sourceSize = {w=71,h=67},
           81  +        pivot = {x=0.5,y=0.5}
           82  +    },
           83  +    {
           84  +        filename = "banner-big-1",
           85  +        frame = {x=635,y=133,w=35,h=92},
           86  +        rotated = false,
           87  +        trimmed = false,
           88  +        spriteSourceSize = {x=0,y=0,w=35,h=92},
           89  +        sourceSize = {w=35,h=92},
           90  +        pivot = {x=0.5,y=0.5}
           91  +    },
           92  +    {
           93  +        filename = "banner-big-2",
           94  +        frame = {x=672,y=133,w=35,h=92},
           95  +        rotated = false,
           96  +        trimmed = false,
           97  +        spriteSourceSize = {x=0,y=0,w=35,h=92},
           98  +        sourceSize = {w=35,h=92},
           99  +        pivot = {x=0.5,y=0.5}
          100  +    },
          101  +    {
          102  +        filename = "banner-big-3",
          103  +        frame = {x=746,y=133,w=35,h=92},
          104  +        rotated = false,
          105  +        trimmed = false,
          106  +        spriteSourceSize = {x=0,y=0,w=35,h=92},
          107  +        sourceSize = {w=35,h=92},
          108  +        pivot = {x=0.5,y=0.5}
          109  +    },
          110  +    {
          111  +        filename = "banner-big-4",
          112  +        frame = {x=783,y=133,w=35,h=92},
          113  +        rotated = false,
          114  +        trimmed = false,
          115  +        spriteSourceSize = {x=0,y=0,w=35,h=92},
          116  +        sourceSize = {w=35,h=92},
          117  +        pivot = {x=0.5,y=0.5}
          118  +    },
          119  +    {
          120  +        filename = "banner-big-preview",
          121  +        frame = {x=709,y=133,w=35,h=92},
          122  +        rotated = false,
          123  +        trimmed = false,
          124  +        spriteSourceSize = {x=0,y=0,w=35,h=92},
          125  +        sourceSize = {w=35,h=92},
          126  +        pivot = {x=0.5,y=0.5}
          127  +    },
          128  +    {
          129  +        filename = "banner-coke-1",
          130  +        frame = {x=382,y=133,w=27,h=78},
          131  +        rotated = false,
          132  +        trimmed = false,
          133  +        spriteSourceSize = {x=0,y=0,w=27,h=78},
          134  +        sourceSize = {w=27,h=78},
          135  +        pivot = {x=0.5,y=0.5}
          136  +    },
          137  +    {
          138  +        filename = "banner-coke-2",
          139  +        frame = {x=440,y=133,w=27,h=78},
          140  +        rotated = false,
          141  +        trimmed = false,
          142  +        spriteSourceSize = {x=0,y=0,w=27,h=78},
          143  +        sourceSize = {w=27,h=78},
          144  +        pivot = {x=0.5,y=0.5}
          145  +    },
          146  +    {
          147  +        filename = "banner-coke-3",
          148  +        frame = {x=411,y=133,w=27,h=78},
          149  +        rotated = false,
          150  +        trimmed = false,
          151  +        spriteSourceSize = {x=0,y=0,w=27,h=78},
          152  +        sourceSize = {w=27,h=78},
          153  +        pivot = {x=0.5,y=0.5}
          154  +    },
          155  +    {
          156  +        filename = "banner-coke-preview",
          157  +        frame = {x=469,y=133,w=27,h=78},
          158  +        rotated = false,
          159  +        trimmed = false,
          160  +        spriteSourceSize = {x=0,y=0,w=27,h=78},
          161  +        sourceSize = {w=27,h=78},
          162  +        pivot = {x=0.5,y=0.5}
          163  +    },
          164  +    {
          165  +        filename = "banner-neon-1",
          166  +        frame = {x=1215,y=2,w=19,h=48},
          167  +        rotated = false,
          168  +        trimmed = false,
          169  +        spriteSourceSize = {x=0,y=0,w=19,h=48},
          170  +        sourceSize = {w=19,h=48},
          171  +        pivot = {x=0.5,y=0.5}
          172  +    },
          173  +    {
          174  +        filename = "banner-neon-2",
          175  +        frame = {x=1194,y=2,w=19,h=48},
          176  +        rotated = false,
          177  +        trimmed = false,
          178  +        spriteSourceSize = {x=0,y=0,w=19,h=48},
          179  +        sourceSize = {w=19,h=48},
          180  +        pivot = {x=0.5,y=0.5}
          181  +    },
          182  +    {
          183  +        filename = "banner-neon-3",
          184  +        frame = {x=1103,y=2,w=19,h=48},
          185  +        rotated = false,
          186  +        trimmed = false,
          187  +        spriteSourceSize = {x=0,y=0,w=19,h=48},
          188  +        sourceSize = {w=19,h=48},
          189  +        pivot = {x=0.5,y=0.5}
          190  +    },
          191  +    {
          192  +        filename = "banner-neon-4",
          193  +        frame = {x=1236,y=2,w=19,h=48},
          194  +        rotated = false,
          195  +        trimmed = false,
          196  +        spriteSourceSize = {x=0,y=0,w=19,h=48},
          197  +        sourceSize = {w=19,h=48},
          198  +        pivot = {x=0.5,y=0.5}
          199  +    },
          200  +    {
          201  +        filename = "banner-neon-preview",
          202  +        frame = {x=1278,y=2,w=19,h=48},
          203  +        rotated = false,
          204  +        trimmed = false,
          205  +        spriteSourceSize = {x=0,y=0,w=19,h=48},
          206  +        sourceSize = {w=19,h=48},
          207  +        pivot = {x=0.5,y=0.5}
          208  +    },
          209  +    {
          210  +        filename = "banner-scroll-1",
          211  +        frame = {x=678,y=2,w=13,h=47},
          212  +        rotated = false,
          213  +        trimmed = false,
          214  +        spriteSourceSize = {x=0,y=0,w=13,h=47},
          215  +        sourceSize = {w=13,h=47},
          216  +        pivot = {x=0.5,y=0.5}
          217  +    },
          218  +    {
          219  +        filename = "banner-scroll-2",
          220  +        frame = {x=663,y=2,w=13,h=47},
          221  +        rotated = false,
          222  +        trimmed = false,
          223  +        spriteSourceSize = {x=0,y=0,w=13,h=47},
          224  +        sourceSize = {w=13,h=47},
          225  +        pivot = {x=0.5,y=0.5}
          226  +    },
          227  +    {
          228  +        filename = "banner-scroll-3",
          229  +        frame = {x=693,y=2,w=13,h=47},
          230  +        rotated = false,
          231  +        trimmed = false,
          232  +        spriteSourceSize = {x=0,y=0,w=13,h=47},
          233  +        sourceSize = {w=13,h=47},
          234  +        pivot = {x=0.5,y=0.5}
          235  +    },
          236  +    {
          237  +        filename = "banner-scroll-4",
          238  +        frame = {x=723,y=2,w=13,h=47},
          239  +        rotated = false,
          240  +        trimmed = false,
          241  +        spriteSourceSize = {x=0,y=0,w=13,h=47},
          242  +        sourceSize = {w=13,h=47},
          243  +        pivot = {x=0.5,y=0.5}
          244  +    },
          245  +    {
          246  +        filename = "banner-scroll-preview",
          247  +        frame = {x=708,y=2,w=13,h=47},
          248  +        rotated = false,
          249  +        trimmed = false,
          250  +        spriteSourceSize = {x=0,y=0,w=13,h=47},
          251  +        sourceSize = {w=13,h=47},
          252  +        pivot = {x=0.5,y=0.5}
          253  +    },
          254  +    {
          255  +        filename = "banner-side-1",
          256  +        frame = {x=1323,y=55,w=19,h=76},
          257  +        rotated = false,
          258  +        trimmed = false,
          259  +        spriteSourceSize = {x=0,y=0,w=19,h=76},
          260  +        sourceSize = {w=19,h=76},
          261  +        pivot = {x=0.5,y=0.5}
          262  +    },
          263  +    {
          264  +        filename = "banner-side-2",
          265  +        frame = {x=1302,y=55,w=19,h=76},
          266  +        rotated = false,
          267  +        trimmed = false,
          268  +        spriteSourceSize = {x=0,y=0,w=19,h=76},
          269  +        sourceSize = {w=19,h=76},
          270  +        pivot = {x=0.5,y=0.5}
          271  +    },
          272  +    {
          273  +        filename = "banner-side-3",
          274  +        frame = {x=1281,y=55,w=19,h=76},
          275  +        rotated = false,
          276  +        trimmed = false,
          277  +        spriteSourceSize = {x=0,y=0,w=19,h=76},
          278  +        sourceSize = {w=19,h=76},
          279  +        pivot = {x=0.5,y=0.5}
          280  +    },
          281  +    {
          282  +        filename = "banner-side-4",
          283  +        frame = {x=1365,y=55,w=19,h=76},
          284  +        rotated = false,
          285  +        trimmed = false,
          286  +        spriteSourceSize = {x=0,y=0,w=19,h=76},
          287  +        sourceSize = {w=19,h=76},
          288  +        pivot = {x=0.5,y=0.5}
          289  +    },
          290  +    {
          291  +        filename = "banner-side-preview",
          292  +        frame = {x=1344,y=55,w=19,h=76},
          293  +        rotated = false,
          294  +        trimmed = false,
          295  +        spriteSourceSize = {x=0,y=0,w=19,h=76},
          296  +        sourceSize = {w=19,h=76},
          297  +        pivot = {x=0.5,y=0.5}
          298  +    },
          299  +    {
          300  +        filename = "banner-sushi-1",
          301  +        frame = {x=499,y=2,w=36,h=13},
          302  +        rotated = false,
          303  +        trimmed = false,
          304  +        spriteSourceSize = {x=0,y=0,w=36,h=13},
          305  +        sourceSize = {w=36,h=13},
          306  +        pivot = {x=0.5,y=0.5}
          307  +    },
          308  +    {
          309  +        filename = "banner-sushi-2",
          310  +        frame = {x=423,y=2,w=36,h=13},
          311  +        rotated = false,
          312  +        trimmed = false,
          313  +        spriteSourceSize = {x=0,y=0,w=36,h=13},
          314  +        sourceSize = {w=36,h=13},
          315  +        pivot = {x=0.5,y=0.5}
          316  +    },
          317  +    {
          318  +        filename = "banner-sushi-3",
          319  +        frame = {x=461,y=2,w=36,h=13},
          320  +        rotated = false,
          321  +        trimmed = false,
          322  +        spriteSourceSize = {x=0,y=0,w=36,h=13},
          323  +        sourceSize = {w=36,h=13},
          324  +        pivot = {x=0.5,y=0.5}
          325  +    },
          326  +    {
          327  +        filename = "banner-sushi-preview",
          328  +        frame = {x=537,y=2,w=36,h=13},
          329  +        rotated = false,
          330  +        trimmed = false,
          331  +        spriteSourceSize = {x=0,y=0,w=36,h=13},
          332  +        sourceSize = {w=36,h=13},
          333  +        pivot = {x=0.5,y=0.5}
          334  +    },
          335  +    {
          336  +        filename = "climb-1",
          337  +        frame = {x=63,y=55,w=15,h=55},
          338  +        rotated = false,
          339  +        trimmed = true,
          340  +        spriteSourceSize = {x=30,y=10,w=15,h=55},
          341  +        sourceSize = {w=71,h=67},
          342  +        pivot = {x=0.5,y=0.5}
          343  +    },
          344  +    {
          345  +        filename = "climb-2",
          346  +        frame = {x=969,y=2,w=15,h=49},
          347  +        rotated = false,
          348  +        trimmed = true,
          349  +        spriteSourceSize = {x=30,y=16,w=15,h=49},
          350  +        sourceSize = {w=71,h=67},
          351  +        pivot = {x=0.5,y=0.5}
          352  +    },
          353  +    {
          354  +        filename = "climb-3",
          355  +        frame = {x=627,y=2,w=16,h=40},
          356  +        rotated = false,
          357  +        trimmed = true,
          358  +        spriteSourceSize = {x=30,y=19,w=16,h=40},
          359  +        sourceSize = {w=71,h=67},
          360  +        pivot = {x=0.5,y=0.5}
          361  +    },
          362  +    {
          363  +        filename = "climb-4",
          364  +        frame = {x=46,y=55,w=15,h=55},
          365  +        rotated = false,
          366  +        trimmed = true,
          367  +        spriteSourceSize = {x=31,y=10,w=15,h=55},
          368  +        sourceSize = {w=71,h=67},
          369  +        pivot = {x=0.5,y=0.5}
          370  +    },
          371  +    {
          372  +        filename = "climb-5",
          373  +        frame = {x=952,y=2,w=15,h=49},
          374  +        rotated = false,
          375  +        trimmed = true,
          376  +        spriteSourceSize = {x=31,y=16,w=15,h=49},
          377  +        sourceSize = {w=71,h=67},
          378  +        pivot = {x=0.5,y=0.5}
          379  +    },
          380  +    {
          381  +        filename = "climb-6",
          382  +        frame = {x=645,y=2,w=16,h=40},
          383  +        rotated = false,
          384  +        trimmed = true,
          385  +        spriteSourceSize = {x=30,y=19,w=16,h=40},
          386  +        sourceSize = {w=71,h=67},
          387  +        pivot = {x=0.5,y=0.5}
          388  +    },
          389  +    {
          390  +        filename = "climb-preview",
          391  +        frame = {x=102,y=55,w=15,h=55},
          392  +        rotated = false,
          393  +        trimmed = true,
          394  +        spriteSourceSize = {x=30,y=10,w=15,h=55},
          395  +        sourceSize = {w=71,h=67},
          396  +        pivot = {x=0.5,y=0.5}
          397  +    },
          398  +    {
          399  +        filename = "control-box-1",
          400  +        frame = {x=764,y=2,w=32,h=30},
          401  +        rotated = false,
          402  +        trimmed = false,
          403  +        spriteSourceSize = {x=0,y=0,w=32,h=30},
          404  +        sourceSize = {w=32,h=30},
          405  +        pivot = {x=0.5,y=0.5}
          406  +    },
          407  +    {
          408  +        filename = "control-box-2",
          409  +        frame = {x=216,y=2,w=16,h=27},
          410  +        rotated = false,
          411  +        trimmed = true,
          412  +        spriteSourceSize = {x=0,y=0,w=16,h=27},
          413  +        sourceSize = {w=17,h=27},
          414  +        pivot = {x=0.5,y=0.5}
          415  +    },
          416  +    {
          417  +        filename = "control-box-3",
          418  +        frame = {x=982,y=55,w=62,h=30},
          419  +        rotated = false,
          420  +        trimmed = false,
          421  +        spriteSourceSize = {x=0,y=0,w=62,h=30},
          422  +        sourceSize = {w=62,h=30},
          423  +        pivot = {x=0.5,y=0.5}
          424  +    },
          425  +    {
          426  +        filename = "crouch",
          427  +        frame = {x=1022,y=2,w=33,h=33},
          428  +        rotated = false,
          429  +        trimmed = true,
          430  +        spriteSourceSize = {x=21,y=34,w=33,h=33},
          431  +        sourceSize = {w=71,h=67},
          432  +        pivot = {x=0.5,y=0.5}
          433  +    },
          434  +    {
          435  +        filename = "drone-1",
          436  +        frame = {x=546,y=55,w=34,h=48},
          437  +        rotated = false,
          438  +        trimmed = true,
          439  +        spriteSourceSize = {x=13,y=4,w=34,h=48},
          440  +        sourceSize = {w=55,h=52},
          441  +        pivot = {x=0.5,y=0.5}
          442  +    },
          443  +    {
          444  +        filename = "drone-2",
          445  +        frame = {x=343,y=55,w=29,h=48},
          446  +        rotated = false,
          447  +        trimmed = true,
          448  +        spriteSourceSize = {x=11,y=4,w=29,h=48},
          449  +        sourceSize = {w=55,h=52},
          450  +        pivot = {x=0.5,y=0.5}
          451  +    },
          452  +    {
          453  +        filename = "drone-3",
          454  +        frame = {x=444,y=55,w=31,h=48},
          455  +        rotated = false,
          456  +        trimmed = true,
          457  +        spriteSourceSize = {x=12,y=3,w=31,h=48},
          458  +        sourceSize = {w=55,h=52},
          459  +        pivot = {x=0.5,y=0.5}
          460  +    },
          461  +    {
          462  +        filename = "drone-4",
          463  +        frame = {x=312,y=55,w=29,h=48},
          464  +        rotated = false,
          465  +        trimmed = true,
          466  +        spriteSourceSize = {x=15,y=4,w=29,h=48},
          467  +        sourceSize = {w=55,h=52},
          468  +        pivot = {x=0.5,y=0.5}
          469  +    },
          470  +    {
          471  +        filename = "drone_preview",
          472  +        frame = {x=510,y=55,w=34,h=48},
          473  +        rotated = false,
          474  +        trimmed = true,
          475  +        spriteSourceSize = {x=13,y=4,w=34,h=48},
          476  +        sourceSize = {w=55,h=52},
          477  +        pivot = {x=0.5,y=0.5}
          478  +    },
          479  +    {
          480  +        filename = "enemy-explosion-1",
          481  +        frame = {x=825,y=2,w=31,h=31},
          482  +        rotated = false,
          483  +        trimmed = true,
          484  +        spriteSourceSize = {x=12,y=13,w=31,h=31},
          485  +        sourceSize = {w=55,h=52},
          486  +        pivot = {x=0.5,y=0.5}
          487  +    },
          488  +    {
          489  +        filename = "enemy-explosion-2",
          490  +        frame = {x=582,y=55,w=42,h=40},
          491  +        rotated = false,
          492  +        trimmed = true,
          493  +        spriteSourceSize = {x=5,y=9,w=42,h=40},
          494  +        sourceSize = {w=55,h=52},
          495  +        pivot = {x=0.5,y=0.5}
          496  +    },
          497  +    {
          498  +        filename = "enemy-explosion-3",
          499  +        frame = {x=1231,y=55,w=48,h=46},
          500  +        rotated = false,
          501  +        trimmed = true,
          502  +        spriteSourceSize = {x=2,y=4,w=48,h=46},
          503  +        sourceSize = {w=55,h=52},
          504  +        pivot = {x=0.5,y=0.5}
          505  +    },
          506  +    {
          507  +        filename = "enemy-explosion-4",
          508  +        frame = {x=273,y=133,w=52,h=50},
          509  +        rotated = false,
          510  +        trimmed = true,
          511  +        spriteSourceSize = {x=1,y=2,w=52,h=50},
          512  +        sourceSize = {w=55,h=52},
          513  +        pivot = {x=0.5,y=0.5}
          514  +    },
          515  +    {
          516  +        filename = "enemy-explosion-5",
          517  +        frame = {x=555,y=133,w=55,h=52},
          518  +        rotated = false,
          519  +        trimmed = false,
          520  +        spriteSourceSize = {x=0,y=0,w=55,h=52},
          521  +        sourceSize = {w=55,h=52},
          522  +        pivot = {x=0.5,y=0.5}
          523  +    },
          524  +    {
          525  +        filename = "enemy-explosion-6",
          526  +        frame = {x=498,y=133,w=55,h=52},
          527  +        rotated = false,
          528  +        trimmed = false,
          529  +        spriteSourceSize = {x=0,y=0,w=55,h=52},
          530  +        sourceSize = {w=55,h=52},
          531  +        pivot = {x=0.5,y=0.5}
          532  +    },
          533  +    {
          534  +        filename = "enemy-explosion-preview",
          535  +        frame = {x=662,y=55,w=34,h=48},
          536  +        rotated = false,
          537  +        trimmed = true,
          538  +        spriteSourceSize = {x=13,y=4,w=34,h=48},
          539  +        sourceSize = {w=55,h=52},
          540  +        pivot = {x=0.5,y=0.5}
          541  +    },
          542  +    {
          543  +        filename = "hurt",
          544  +        frame = {x=141,y=55,w=25,h=47},
          545  +        rotated = false,
          546  +        trimmed = true,
          547  +        spriteSourceSize = {x=21,y=20,w=25,h=47},
          548  +        sourceSize = {w=71,h=67},
          549  +        pivot = {x=0.5,y=0.5}
          550  +    },
          551  +    {
          552  +        filename = "idle-1",
          553  +        frame = {x=1057,y=2,w=21,h=46},
          554  +        rotated = false,
          555  +        trimmed = true,
          556  +        spriteSourceSize = {x=28,y=21,w=21,h=46},
          557  +        sourceSize = {w=71,h=67},
          558  +        pivot = {x=0.5,y=0.5}
          559  +    },
          560  +    {
          561  +        filename = "idle-2",
          562  +        frame = {x=24,y=55,w=20,h=50},
          563  +        rotated = false,
          564  +        trimmed = true,
          565  +        spriteSourceSize = {x=28,y=17,w=20,h=50},
          566  +        sourceSize = {w=71,h=67},
          567  +        pivot = {x=0.5,y=0.5}
          568  +    },
          569  +    {
          570  +        filename = "idle-3",
          571  +        frame = {x=119,y=55,w=20,h=51},
          572  +        rotated = false,
          573  +        trimmed = true,
          574  +        spriteSourceSize = {x=28,y=16,w=20,h=51},
          575  +        sourceSize = {w=71,h=67},
          576  +        pivot = {x=0.5,y=0.5}
          577  +    },
          578  +    {
          579  +        filename = "idle-4",
          580  +        frame = {x=80,y=55,w=20,h=50},
          581  +        rotated = false,
          582  +        trimmed = true,
          583  +        spriteSourceSize = {x=28,y=17,w=20,h=50},
          584  +        sourceSize = {w=71,h=67},
          585  +        pivot = {x=0.5,y=0.5}
          586  +    },
          587  +    {
          588  +        filename = "idle-peview",
          589  +        frame = {x=1080,y=2,w=21,h=46},
          590  +        rotated = false,
          591  +        trimmed = true,
          592  +        spriteSourceSize = {x=28,y=21,w=21,h=46},
          593  +        sourceSize = {w=71,h=67},
          594  +        pivot = {x=0.5,y=0.5}
          595  +    },
          596  +    {
          597  +        filename = "jump-1",
          598  +        frame = {x=626,y=55,w=34,h=48},
          599  +        rotated = false,
          600  +        trimmed = true,
          601  +        spriteSourceSize = {x=22,y=14,w=34,h=48},
          602  +        sourceSize = {w=71,h=67},
          603  +        pivot = {x=0.5,y=0.5}
          604  +    },
          605  +    {
          606  +        filename = "jump-2",
          607  +        frame = {x=269,y=55,w=41,h=35},
          608  +        rotated = false,
          609  +        trimmed = true,
          610  +        spriteSourceSize = {x=15,y=15,w=41,h=35},
          611  +        sourceSize = {w=71,h=67},
          612  +        pivot = {x=0.5,y=0.5}
          613  +    },
          614  +    {
          615  +        filename = "jump-3",
          616  +        frame = {x=1342,y=2,w=37,h=32},
          617  +        rotated = false,
          618  +        trimmed = true,
          619  +        spriteSourceSize = {x=21,y=18,w=37,h=32},
          620  +        sourceSize = {w=71,h=67},
          621  +        pivot = {x=0.5,y=0.5}
          622  +    },
          623  +    {
          624  +        filename = "jump-4",
          625  +        frame = {x=233,y=55,w=34,h=40},
          626  +        rotated = false,
          627  +        trimmed = true,
          628  +        spriteSourceSize = {x=23,y=16,w=34,h=40},
          629  +        sourceSize = {w=71,h=67},
          630  +        pivot = {x=0.5,y=0.5}
          631  +    },
          632  +    {
          633  +        filename = "jump-preview",
          634  +        frame = {x=2,y=55,w=20,h=50},
          635  +        rotated = false,
          636  +        trimmed = true,
          637  +        spriteSourceSize = {x=28,y=17,w=20,h=50},
          638  +        sourceSize = {w=71,h=67},
          639  +        pivot = {x=0.5,y=0.5}
          640  +    },
          641  +    {
          642  +        filename = "monitor-face-1",
          643  +        frame = {x=193,y=2,w=21,h=18},
          644  +        rotated = false,
          645  +        trimmed = false,
          646  +        spriteSourceSize = {x=0,y=0,w=21,h=18},
          647  +        sourceSize = {w=21,h=18},
          648  +        pivot = {x=0.5,y=0.5}
          649  +    },
          650  +    {
          651  +        filename = "monitor-face-2",
          652  +        frame = {x=101,y=2,w=21,h=18},
          653  +        rotated = false,
          654  +        trimmed = false,
          655  +        spriteSourceSize = {x=0,y=0,w=21,h=18},
          656  +        sourceSize = {w=21,h=18},
          657  +        pivot = {x=0.5,y=0.5}
          658  +    },
          659  +    {
          660  +        filename = "monitor-face-3",
          661  +        frame = {x=124,y=2,w=21,h=18},
          662  +        rotated = false,
          663  +        trimmed = false,
          664  +        spriteSourceSize = {x=0,y=0,w=21,h=18},
          665  +        sourceSize = {w=21,h=18},
          666  +        pivot = {x=0.5,y=0.5}
          667  +    },
          668  +    {
          669  +        filename = "monitor-face-4",
          670  +        frame = {x=147,y=2,w=21,h=18},
          671  +        rotated = false,
          672  +        trimmed = false,
          673  +        spriteSourceSize = {x=0,y=0,w=21,h=18},
          674  +        sourceSize = {w=21,h=18},
          675  +        pivot = {x=0.5,y=0.5}
          676  +    },
          677  +    {
          678  +        filename = "monitorface_preview",
          679  +        frame = {x=170,y=2,w=21,h=18},
          680  +        rotated = false,
          681  +        trimmed = false,
          682  +        spriteSourceSize = {x=0,y=0,w=21,h=18},
          683  +        sourceSize = {w=21,h=18},
          684  +        pivot = {x=0.5,y=0.5}
          685  +    },
          686  +    {
          687  +        filename = "run-1",
          688  +        frame = {x=1046,y=55,w=41,h=51},
          689  +        rotated = false,
          690  +        trimmed = true,
          691  +        spriteSourceSize = {x=10,y=16,w=41,h=51},
          692  +        sourceSize = {w=71,h=67},
          693  +        pivot = {x=0.5,y=0.5}
          694  +    },
          695  +    {
          696  +        filename = "run-2",
          697  +        frame = {x=1138,y=55,w=47,h=46},
          698  +        rotated = false,
          699  +        trimmed = true,
          700  +        spriteSourceSize = {x=7,y=18,w=47,h=46},
          701  +        sourceSize = {w=71,h=67},
          702  +        pivot = {x=0.5,y=0.5}
          703  +    },
          704  +    {
          705  +        filename = "run-3",
          706  +        frame = {x=735,y=55,w=36,h=48},
          707  +        rotated = false,
          708  +        trimmed = true,
          709  +        spriteSourceSize = {x=16,y=19,w=36,h=48},
          710  +        sourceSize = {w=71,h=67},
          711  +        pivot = {x=0.5,y=0.5}
          712  +    },
          713  +    {
          714  +        filename = "run-4",
          715  +        frame = {x=205,y=55,w=26,h=48},
          716  +        rotated = false,
          717  +        trimmed = true,
          718  +        spriteSourceSize = {x=26,y=19,w=26,h=48},
          719  +        sourceSize = {w=71,h=67},
          720  +        pivot = {x=0.5,y=0.5}
          721  +    },
          722  +    {
          723  +        filename = "run-5",
          724  +        frame = {x=1187,y=55,w=42,h=51},
          725  +        rotated = false,
          726  +        trimmed = true,
          727  +        spriteSourceSize = {x=10,y=16,w=42,h=51},
          728  +        sourceSize = {w=71,h=67},
          729  +        pivot = {x=0.5,y=0.5}
          730  +    },
          731  +    {
          732  +        filename = "run-6",
          733  +        frame = {x=1089,y=55,w=47,h=46},
          734  +        rotated = false,
          735  +        trimmed = true,
          736  +        spriteSourceSize = {x=7,y=18,w=47,h=46},
          737  +        sourceSize = {w=71,h=67},
          738  +        pivot = {x=0.5,y=0.5}
          739  +    },
          740  +    {
          741  +        filename = "run-7",
          742  +        frame = {x=698,y=55,w=35,h=48},
          743  +        rotated = false,
          744  +        trimmed = true,
          745  +        spriteSourceSize = {x=16,y=19,w=35,h=48},
          746  +        sourceSize = {w=71,h=67},
          747  +        pivot = {x=0.5,y=0.5}
          748  +    },
          749  +    {
          750  +        filename = "run-8",
          751  +        frame = {x=477,y=55,w=31,h=48},
          752  +        rotated = false,
          753  +        trimmed = true,
          754  +        spriteSourceSize = {x=21,y=19,w=31,h=48},
          755  +        sourceSize = {w=71,h=67},
          756  +        pivot = {x=0.5,y=0.5}
          757  +    },
          758  +    {
          759  +        filename = "run-preview",
          760  +        frame = {x=939,y=55,w=41,h=51},
          761  +        rotated = false,
          762  +        trimmed = true,
          763  +        spriteSourceSize = {x=10,y=16,w=41,h=51},
          764  +        sourceSize = {w=71,h=67},
          765  +        pivot = {x=0.5,y=0.5}
          766  +    },
          767  +    {
          768  +        filename = "run-shoot-1",
          769  +        frame = {x=162,y=133,w=51,h=51},
          770  +        rotated = false,
          771  +        trimmed = true,
          772  +        spriteSourceSize = {x=10,y=16,w=51,h=51},
          773  +        sourceSize = {w=71,h=67},
          774  +        pivot = {x=0.5,y=0.5}
          775  +    },
          776  +    {
          777  +        filename = "run-shoot-2",
          778  +        frame = {x=52,y=133,w=55,h=46},
          779  +        rotated = false,
          780  +        trimmed = true,
          781  +        spriteSourceSize = {x=7,y=18,w=55,h=46},
          782  +        sourceSize = {w=71,h=67},
          783  +        pivot = {x=0.5,y=0.5}
          784  +    },
          785  +    {
          786  +        filename = "run-shoot-3",
          787  +        frame = {x=1386,y=55,w=47,h=48},
          788  +        rotated = false,
          789  +        trimmed = true,
          790  +        spriteSourceSize = {x=16,y=19,w=47,h=48},
          791  +        sourceSize = {w=71,h=67},
          792  +        pivot = {x=0.5,y=0.5}
          793  +    },
          794  +    {
          795  +        filename = "run-shoot-4",
          796  +        frame = {x=819,y=55,w=37,h=48},
          797  +        rotated = false,
          798  +        trimmed = true,
          799  +        spriteSourceSize = {x=26,y=19,w=37,h=48},
          800  +        sourceSize = {w=71,h=67},
          801  +        pivot = {x=0.5,y=0.5}
          802  +    },
          803  +    {
          804  +        filename = "run-shoot-5",
          805  +        frame = {x=327,y=133,w=53,h=51},
          806  +        rotated = false,
          807  +        trimmed = true,
          808  +        spriteSourceSize = {x=10,y=16,w=53,h=51},
          809  +        sourceSize = {w=71,h=67},
          810  +        pivot = {x=0.5,y=0.5}
          811  +    },
          812  +    {
          813  +        filename = "run-shoot-6",
          814  +        frame = {x=215,y=133,w=56,h=46},
          815  +        rotated = false,
          816  +        trimmed = true,
          817  +        spriteSourceSize = {x=7,y=18,w=56,h=46},
          818  +        sourceSize = {w=71,h=67},
          819  +        pivot = {x=0.5,y=0.5}
          820  +    },
          821  +    {
          822  +        filename = "run-shoot-7",
          823  +        frame = {x=2,y=133,w=48,h=48},
          824  +        rotated = false,
          825  +        trimmed = true,
          826  +        spriteSourceSize = {x=16,y=19,w=48,h=48},
          827  +        sourceSize = {w=71,h=67},
          828  +        pivot = {x=0.5,y=0.5}
          829  +    },
          830  +    {
          831  +        filename = "run-shoot-8",
          832  +        frame = {x=895,y=55,w=42,h=48},
          833  +        rotated = false,
          834  +        trimmed = true,
          835  +        spriteSourceSize = {x=21,y=19,w=42,h=48},
          836  +        sourceSize = {w=71,h=67},
          837  +        pivot = {x=0.5,y=0.5}
          838  +    },
          839  +    {
          840  +        filename = "run-shoot-preview",
          841  +        frame = {x=109,y=133,w=51,h=51},
          842  +        rotated = false,
          843  +        trimmed = true,
          844  +        spriteSourceSize = {x=10,y=16,w=51,h=51},
          845  +        sourceSize = {w=71,h=67},
          846  +        pivot = {x=0.5,y=0.5}
          847  +    },
          848  +    {
          849  +        filename = "shoot",
          850  +        frame = {x=858,y=55,w=35,h=51},
          851  +        rotated = false,
          852  +        trimmed = true,
          853  +        spriteSourceSize = {x=20,y=16,w=35,h=51},
          854  +        sourceSize = {w=71,h=67},
          855  +        pivot = {x=0.5,y=0.5}
          856  +    },
          857  +    {
          858  +        filename = "shot-1",
          859  +        frame = {x=41,y=2,w=12,h=5},
          860  +        rotated = false,
          861  +        trimmed = true,
          862  +        spriteSourceSize = {x=1,y=3,w=12,h=5},
          863  +        sourceSize = {w=15,h=11},
          864  +        pivot = {x=0.5,y=0.5}
          865  +    },
          866  +    {
          867  +        filename = "shot-2",
          868  +        frame = {x=2,y=2,w=10,h=7},
          869  +        rotated = false,
          870  +        trimmed = true,
          871  +        spriteSourceSize = {x=1,y=2,w=10,h=7},
          872  +        sourceSize = {w=15,h=11},
          873  +        pivot = {x=0.5,y=0.5}
          874  +    },
          875  +    {
          876  +        filename = "shot-3",
          877  +        frame = {x=14,y=2,w=11,h=6},
          878  +        rotated = false,
          879  +        trimmed = true,
          880  +        spriteSourceSize = {x=1,y=3,w=11,h=6},
          881  +        sourceSize = {w=15,h=11},
          882  +        pivot = {x=0.5,y=0.5}
          883  +    },
          884  +    {
          885  +        filename = "shot-hit-1",
          886  +        frame = {x=55,y=2,w=9,h=9},
          887  +        rotated = false,
          888  +        trimmed = true,
          889  +        spriteSourceSize = {x=3,y=1,w=9,h=9},
          890  +        sourceSize = {w=15,h=11},
          891  +        pivot = {x=0.5,y=0.5}
          892  +    },
          893  +    {
          894  +        filename = "shot-hit-2",
          895  +        frame = {x=66,y=2,w=9,h=9},
          896  +        rotated = false,
          897  +        trimmed = true,
          898  +        spriteSourceSize = {x=3,y=1,w=9,h=9},
          899  +        sourceSize = {w=15,h=11},
          900  +        pivot = {x=0.5,y=0.5}
          901  +    },
          902  +    {
          903  +        filename = "shot-hit-3",
          904  +        frame = {x=88,y=2,w=11,h=11},
          905  +        rotated = false,
          906  +        trimmed = true,
          907  +        spriteSourceSize = {x=2,y=0,w=11,h=11},
          908  +        sourceSize = {w=15,h=11},
          909  +        pivot = {x=0.5,y=0.5}
          910  +    },
          911  +    {
          912  +        filename = "shot-hit-preview",
          913  +        frame = {x=77,y=2,w=9,h=9},
          914  +        rotated = false,
          915  +        trimmed = true,
          916  +        spriteSourceSize = {x=3,y=1,w=9,h=9},
          917  +        sourceSize = {w=15,h=11},
          918  +        pivot = {x=0.5,y=0.5}
          919  +    },
          920  +    {
          921  +        filename = "shot-preview",
          922  +        frame = {x=27,y=2,w=12,h=5},
          923  +        rotated = false,
          924  +        trimmed = true,
          925  +        spriteSourceSize = {x=1,y=3,w=12,h=5},
          926  +        sourceSize = {w=15,h=11},
          927  +        pivot = {x=0.5,y=0.5}
          928  +    },
          929  +    {
          930  +        filename = "turret-1",
          931  +        frame = {x=315,y=2,w=25,h=23},
          932  +        rotated = false,
          933  +        trimmed = false,
          934  +        spriteSourceSize = {x=0,y=0,w=25,h=23},
          935  +        sourceSize = {w=25,h=23},
          936  +        pivot = {x=0.5,y=0.5}
          937  +    },
          938  +    {
          939  +        filename = "turret-2",
          940  +        frame = {x=342,y=2,w=25,h=23},
          941  +        rotated = false,
          942  +        trimmed = false,
          943  +        spriteSourceSize = {x=0,y=0,w=25,h=23},
          944  +        sourceSize = {w=25,h=23},
          945  +        pivot = {x=0.5,y=0.5}
          946  +    },
          947  +    {
          948  +        filename = "turret-3",
          949  +        frame = {x=261,y=2,w=25,h=23},
          950  +        rotated = false,
          951  +        trimmed = false,
          952  +        spriteSourceSize = {x=0,y=0,w=25,h=23},
          953  +        sourceSize = {w=25,h=23},
          954  +        pivot = {x=0.5,y=0.5}
          955  +    },
          956  +    {
          957  +        filename = "turret-4",
          958  +        frame = {x=234,y=2,w=25,h=23},
          959  +        rotated = false,
          960  +        trimmed = false,
          961  +        spriteSourceSize = {x=0,y=0,w=25,h=23},
          962  +        sourceSize = {w=25,h=23},
          963  +        pivot = {x=0.5,y=0.5}
          964  +    },
          965  +    {
          966  +        filename = "turret-5",
          967  +        frame = {x=288,y=2,w=25,h=23},
          968  +        rotated = false,
          969  +        trimmed = false,
          970  +        spriteSourceSize = {x=0,y=0,w=25,h=23},
          971  +        sourceSize = {w=25,h=23},
          972  +        pivot = {x=0.5,y=0.5}
          973  +    },
          974  +    {
          975  +        filename = "turret-6",
          976  +        frame = {x=396,y=2,w=25,h=23},
          977  +        rotated = false,
          978  +        trimmed = false,
          979  +        spriteSourceSize = {x=0,y=0,w=25,h=23},
          980  +        sourceSize = {w=25,h=23},
          981  +        pivot = {x=0.5,y=0.5}
          982  +    },
          983  +    {
          984  +        filename = "turret-preview",
          985  +        frame = {x=369,y=2,w=25,h=23},
          986  +        rotated = false,
          987  +        trimmed = false,
          988  +        spriteSourceSize = {x=0,y=0,w=25,h=23},
          989  +        sourceSize = {w=25,h=23},
          990  +        pivot = {x=0.5,y=0.5}
          991  +    },
          992  +    {
          993  +        filename = "v-police",
          994  +        frame = {x=1013,y=133,w=163,h=60},
          995  +        rotated = false,
          996  +        trimmed = false,
          997  +        spriteSourceSize = {x=0,y=0,w=163,h=60},
          998  +        sourceSize = {w=163,h=60},
          999  +        pivot = {x=0.5,y=0.5}
         1000  +    },
         1001  +    {
         1002  +        filename = "v-red",
         1003  +        frame = {x=915,y=133,w=96,h=61},
         1004  +        rotated = false,
         1005  +        trimmed = false,
         1006  +        spriteSourceSize = {x=0,y=0,w=96,h=61},
         1007  +        sourceSize = {w=96,h=61},
         1008  +        pivot = {x=0.5,y=0.5}
         1009  +    },
         1010  +    {
         1011  +        filename = "v-truck",
         1012  +        frame = {x=1178,y=133,w=257,h=104},
         1013  +        rotated = false,
         1014  +        trimmed = false,
         1015  +        spriteSourceSize = {x=0,y=0,w=257,h=104},
         1016  +        sourceSize = {w=257,h=104},
         1017  +        pivot = {x=0.5,y=0.5}
         1018  +    },
         1019  +    {
         1020  +        filename = "v-yellow",
         1021  +        frame = {x=820,y=133,w=93,h=60},
         1022  +        rotated = false,
         1023  +        trimmed = false,
         1024  +        spriteSourceSize = {x=0,y=0,w=93,h=60},
         1025  +        sourceSize = {w=93,h=60},
         1026  +        pivot = {x=0.5,y=0.5}
         1027  +    },
         1028  +    {
         1029  +        filename = "walk-1",
         1030  +        frame = {x=1320,y=2,w=20,h=49},
         1031  +        rotated = false,
         1032  +        trimmed = true,
         1033  +        spriteSourceSize = {x=28,y=18,w=20,h=49},
         1034  +        sourceSize = {w=71,h=67},
         1035  +        pivot = {x=0.5,y=0.5}
         1036  +    },
         1037  +    {
         1038  +        filename = "walk-2",
         1039  +        frame = {x=1154,y=2,w=19,h=48},
         1040  +        rotated = false,
         1041  +        trimmed = true,
         1042  +        spriteSourceSize = {x=26,y=19,w=19,h=48},
         1043  +        sourceSize = {w=71,h=67},
         1044  +        pivot = {x=0.5,y=0.5}
         1045  +    },
         1046  +    {
         1047  +        filename = "walk-3",
         1048  +        frame = {x=1004,y=2,w=16,h=49},
         1049  +        rotated = false,
         1050  +        trimmed = true,
         1051  +        spriteSourceSize = {x=27,y=18,w=16,h=49},
         1052  +        sourceSize = {w=71,h=67},
         1053  +        pivot = {x=0.5,y=0.5}
         1054  +    },
         1055  +    {
         1056  +        filename = "walk-4",
         1057  +        frame = {x=751,y=2,w=11,h=50},
         1058  +        rotated = false,
         1059  +        trimmed = true,
         1060  +        spriteSourceSize = {x=30,y=17,w=11,h=50},
         1061  +        sourceSize = {w=71,h=67},
         1062  +        pivot = {x=0.5,y=0.5}
         1063  +    },
         1064  +    {
         1065  +        filename = "walk-5",
         1066  +        frame = {x=908,y=2,w=11,h=51},
         1067  +        rotated = false,
         1068  +        trimmed = true,
         1069  +        spriteSourceSize = {x=30,y=16,w=11,h=51},
         1070  +        sourceSize = {w=71,h=67},
         1071  +        pivot = {x=0.5,y=0.5}
         1072  +    },
         1073  +    {
         1074  +        filename = "walk-6",
         1075  +        frame = {x=798,y=2,w=11,h=51},
         1076  +        rotated = false,
         1077  +        trimmed = true,
         1078  +        spriteSourceSize = {x=30,y=16,w=11,h=51},
         1079  +        sourceSize = {w=71,h=67},
         1080  +        pivot = {x=0.5,y=0.5}
         1081  +    },
         1082  +    {
         1083  +        filename = "walk-7",
         1084  +        frame = {x=811,y=2,w=12,h=50},
         1085  +        rotated = false,
         1086  +        trimmed = true,
         1087  +        spriteSourceSize = {x=30,y=17,w=12,h=50},
         1088  +        sourceSize = {w=71,h=67},
         1089  +        pivot = {x=0.5,y=0.5}
         1090  +    },
         1091  +    {
         1092  +        filename = "walk-8",
         1093  +        frame = {x=1175,y=2,w=17,h=50},
         1094  +        rotated = false,
         1095  +        trimmed = true,
         1096  +        spriteSourceSize = {x=29,y=17,w=17,h=50},
         1097  +        sourceSize = {w=71,h=67},
         1098  +        pivot = {x=0.5,y=0.5}
         1099  +    },
         1100  +    {
         1101  +        filename = "walk-9",
         1102  +        frame = {x=1299,y=2,w=19,h=49},
         1103  +        rotated = false,
         1104  +        trimmed = true,
         1105  +        spriteSourceSize = {x=29,y=18,w=19,h=49},
         1106  +        sourceSize = {w=71,h=67},
         1107  +        pivot = {x=0.5,y=0.5}
         1108  +    },
         1109  +    {
         1110  +        filename = "walk-10",
         1111  +        frame = {x=1257,y=2,w=19,h=48},
         1112  +        rotated = false,
         1113  +        trimmed = true,
         1114  +        spriteSourceSize = {x=26,y=19,w=19,h=48},
         1115  +        sourceSize = {w=71,h=67},
         1116  +        pivot = {x=0.5,y=0.5}
         1117  +    },
         1118  +    {
         1119  +        filename = "walk-11",
         1120  +        frame = {x=986,y=2,w=16,h=49},
         1121  +        rotated = false,
         1122  +        trimmed = true,
         1123  +        spriteSourceSize = {x=27,y=18,w=16,h=49},
         1124  +        sourceSize = {w=71,h=67},
         1125  +        pivot = {x=0.5,y=0.5}
         1126  +    },
         1127  +    {
         1128  +        filename = "walk-12",
         1129  +        frame = {x=738,y=2,w=11,h=50},
         1130  +        rotated = false,
         1131  +        trimmed = true,
         1132  +        spriteSourceSize = {x=30,y=17,w=11,h=50},
         1133  +        sourceSize = {w=71,h=67},
         1134  +        pivot = {x=0.5,y=0.5}
         1135  +    },
         1136  +    {
         1137  +        filename = "walk-13",
         1138  +        frame = {x=895,y=2,w=11,h=51},
         1139  +        rotated = false,
         1140  +        trimmed = true,
         1141  +        spriteSourceSize = {x=30,y=16,w=11,h=51},
         1142  +        sourceSize = {w=71,h=67},
         1143  +        pivot = {x=0.5,y=0.5}
         1144  +    },
         1145  +    {
         1146  +        filename = "walk-14",
         1147  +        frame = {x=937,y=2,w=13,h=51},
         1148  +        rotated = false,
         1149  +        trimmed = true,
         1150  +        spriteSourceSize = {x=29,y=16,w=13,h=51},
         1151  +        sourceSize = {w=71,h=67},
         1152  +        pivot = {x=0.5,y=0.5}
         1153  +    },
         1154  +    {
         1155  +        filename = "walk-15",
         1156  +        frame = {x=921,y=2,w=14,h=50},
         1157  +        rotated = false,
         1158  +        trimmed = true,
         1159  +        spriteSourceSize = {x=28,y=17,w=14,h=50},
         1160  +        sourceSize = {w=71,h=67},
         1161  +        pivot = {x=0.5,y=0.5}
         1162  +    },
         1163  +    {
         1164  +        filename = "walk-16",
         1165  +        frame = {x=1381,y=2,w=19,h=50},
         1166  +        rotated = false,
         1167  +        trimmed = true,
         1168  +        spriteSourceSize = {x=27,y=17,w=19,h=50},
         1169  +        sourceSize = {w=71,h=67},
         1170  +        pivot = {x=0.5,y=0.5}
         1171  +    },
         1172  +    {
         1173  +        filename = "walk-preview",
         1174  +        frame = {x=1402,y=2,w=20,h=49},
         1175  +        rotated = false,
         1176  +        trimmed = true,
         1177  +        spriteSourceSize = {x=28,y=18,w=20,h=49},
         1178  +        sourceSize = {w=71,h=67},
         1179  +        pivot = {x=0.5,y=0.5}
         1180  +    }
         1181  +}
         1182  +local meta = {
         1183  +	app = "http://www.codeandweb.com/texturepacker",
         1184  +	version = "1.0",
         1185  +	image = "atlas.png",
         1186  +	format = "RGBA8888",
         1187  +	size = {w=1437,h=239},
         1188  +	scale = "1",
         1189  +	smartupdate = "$TexturePacker:SmartUpdate:133d307a83f4c0480bfd6bd8d553a8ee:a09b2f18f63aa97ec00a8aa0c1bb7edd:cbce6b53f0f49e0bf15173c25c41f876$"
         1190  +}
         1191  +return {
         1192  +    frames = frames,
         1193  +    meta = meta
         1194  +}

Added src/worpt/assets/warped_city/atlas/atlas.png.

cannot compute difference between binary files

Added src/worpt/assets/warped_city/atlas/atlas.tps.

            1  +<?xml version="1.0" encoding="UTF-8"?>
            2  +<data version="1.0">
            3  +    <struct type="Settings">
            4  +        <key>fileFormatVersion</key>
            5  +        <int>3</int>
            6  +        <key>texturePackerVersion</key>
            7  +        <string>3.5.2</string>
            8  +        <key>fileName</key>
            9  +        <string>/Users/luiszuno/Documents/GameDev/Phaser/artpacks/2019/warped-city/assets/atlas/atlas.tps</string>
           10  +        <key>autoSDSettings</key>
           11  +        <array>
           12  +            <struct type="AutoSDSettings">
           13  +                <key>scale</key>
           14  +                <double>1</double>
           15  +                <key>extension</key>
           16  +                <string></string>
           17  +                <key>spriteFilter</key>
           18  +                <string></string>
           19  +                <key>acceptFractionalValues</key>
           20  +                <false/>
           21  +                <key>maxTextureSize</key>
           22  +                <QSize>
           23  +                    <key>width</key>
           24  +                    <int>-1</int>
           25  +                    <key>height</key>
           26  +                    <int>-1</int>
           27  +                </QSize>
           28  +            </struct>
           29  +        </array>
           30  +        <key>allowRotation</key>
           31  +        <true/>
           32  +        <key>premultiplyAlpha</key>
           33  +        <false/>
           34  +        <key>shapeDebug</key>
           35  +        <false/>
           36  +        <key>dpi</key>
           37  +        <uint>72</uint>
           38  +        <key>dataFormat</key>
           39  +        <string>json-array</string>
           40  +        <key>textureFileName</key>
           41  +        <filename>atlas.png</filename>
           42  +        <key>flipPVR</key>
           43  +        <false/>
           44  +        <key>pvrCompressionQuality</key>
           45  +        <enum type="SettingsBase::PvrCompressionQuality">PVR_QUALITY_NORMAL</enum>
           46  +        <key>mipMapMinSize</key>
           47  +        <uint>32768</uint>
           48  +        <key>etc1CompressionQuality</key>
           49  +        <enum type="SettingsBase::Etc1CompressionQuality">ETC1_QUALITY_LOW_PERCEPTUAL</enum>
           50  +        <key>dxtCompressionMode</key>
           51  +        <enum type="SettingsBase::DxtCompressionMode">DXT_PERCEPTUAL</enum>
           52  +        <key>jxrColorFormat</key>
           53  +        <enum type="SettingsBase::JpegXrColorMode">JXR_YUV444</enum>
           54  +        <key>jxrTrimFlexBits</key>
           55  +        <uint>0</uint>
           56  +        <key>jxrCompressionLevel</key>
           57  +        <uint>0</uint>
           58  +        <key>ditherType</key>
           59  +        <enum type="SettingsBase::DitherType">NearestNeighbour</enum>
           60  +        <key>backgroundColor</key>
           61  +        <uint>0</uint>
           62  +        <key>libGdx</key>
           63  +        <struct type="LibGDX">
           64  +            <key>filtering</key>
           65  +            <struct type="LibGDXFiltering">
           66  +                <key>x</key>
           67  +                <enum type="LibGDXFiltering::Filtering">Linear</enum>
           68  +                <key>y</key>
           69  +                <enum type="LibGDXFiltering::Filtering">Linear</enum>
           70  +            </struct>
           71  +        </struct>
           72  +        <key>shapePadding</key>
           73  +        <uint>2</uint>
           74  +        <key>jpgQuality</key>
           75  +        <uint>80</uint>
           76  +        <key>pngOptimizationLevel</key>
           77  +        <uint>0</uint>
           78  +        <key>webpQualityLevel</key>
           79  +        <uint>101</uint>
           80  +        <key>textureSubPath</key>
           81  +        <string></string>
           82  +        <key>textureFormat</key>
           83  +        <enum type="SettingsBase::TextureFormat">png</enum>
           84  +        <key>borderPadding</key>
           85  +        <uint>2</uint>
           86  +        <key>maxTextureSize</key>
           87  +        <QSize>
           88  +            <key>width</key>
           89  +            <int>2048</int>
           90  +            <key>height</key>
           91  +            <int>2048</int>
           92  +        </QSize>
           93  +        <key>fixedTextureSize</key>
           94  +        <QSize>
           95  +            <key>width</key>
           96  +            <int>-1</int>
           97  +            <key>height</key>
           98  +            <int>-1</int>
           99  +        </QSize>
          100  +        <key>reduceBorderArtifacts</key>
          101  +        <false/>
          102  +        <key>algorithmSettings</key>
          103  +        <struct type="AlgorithmSettings">
          104  +            <key>algorithm</key>
          105  +            <enum type="AlgorithmSettings::AlgorithmId">Basic</enum>
          106  +            <key>freeSizeMode</key>
          107  +            <enum type="AlgorithmSettings::AlgorithmFreeSizeMode">Best</enum>
          108  +            <key>sizeConstraints</key>
          109  +            <enum type="AlgorithmSettings::SizeConstraints">AnySize</enum>
          110  +            <key>forceSquared</key>
          111  +            <false/>
          112  +            <key>forceWordAligned</key>
          113  +            <false/>
          114  +            <key>maxRects</key>
          115  +            <struct type="AlgorithmMaxRectsSettings">
          116  +                <key>heuristic</key>
          117  +                <enum type="AlgorithmMaxRectsSettings::Heuristic">Best</enum>
          118  +            </struct>
          119  +            <key>basic</key>
          120  +            <struct type="AlgorithmBasicSettings">
          121  +                <key>sortBy</key>
          122  +                <enum type="AlgorithmBasicSettings::SortBy">Best</enum>
          123  +                <key>order</key>
          124  +                <enum type="AlgorithmBasicSettings::Order">Ascending</enum>
          125  +            </struct>
          126  +        </struct>
          127  +        <key>andEngine</key>
          128  +        <struct type="AndEngine">
          129  +            <key>minFilter</key>
          130  +            <enum type="AndEngine::MinFilter">Linear</enum>
          131  +            <key>packageName</key>
          132  +            <string>Texture</string>
          133  +            <key>wrap</key>
          134  +            <struct type="AndEngineWrap">
          135  +                <key>s</key>
          136  +                <enum type="AndEngineWrap::Wrap">Clamp</enum>
          137  +                <key>t</key>
          138  +                <enum type="AndEngineWrap::Wrap">Clamp</enum>
          139  +            </struct>
          140  +            <key>magFilter</key>
          141  +            <enum type="AndEngine::MagFilter">MagLinear</enum>
          142  +        </struct>
          143  +        <key>dataFileNames</key>
          144  +        <map type="GFileNameMap">
          145  +            <key>data</key>
          146  +            <struct type="DataFile">
          147  +                <key>name</key>
          148  +                <filename>atlas.json</filename>
          149  +            </struct>
          150  +        </map>
          151  +        <key>multiPack</key>
          152  +        <false/>
          153  +        <key>forceIdenticalLayout</key>
          154  +        <false/>
          155  +        <key>outputFormat</key>
          156  +        <enum type="SettingsBase::OutputFormat">RGBA8888</enum>
          157  +        <key>contentProtection</key>
          158  +        <struct type="ContentProtection">
          159  +            <key>key</key>
          160  +            <string></string>
          161  +        </struct>
          162  +        <key>autoAliasEnabled</key>
          163  +        <false/>
          164  +        <key>trimSpriteNames</key>
          165  +        <true/>
          166  +        <key>prependSmartFolderName</key>
          167  +        <false/>
          168  +        <key>cleanTransparentPixels</key>
          169  +        <true/>
          170  +        <key>globalSpriteSettings</key>
          171  +        <struct type="SpriteSettings">
          172  +            <key>scale</key>
          173  +            <double>1</double>
          174  +            <key>scaleMode</key>
          175  +            <enum type="ScaleMode">Smooth</enum>
          176  +            <key>innerPadding</key>
          177  +            <uint>0</uint>
          178  +            <key>extrude</key>
          179  +            <uint>0</uint>
          180  +            <key>trimThreshold</key>
          181  +            <uint>1</uint>
          182  +            <key>trimMode</key>
          183  +            <enum type="SpriteSettings::TrimMode">Trim</enum>
          184  +            <key>heuristicMask</key>
          185  +            <false/>
          186  +            <key>pivotPoint</key>
          187  +            <enum type="SpriteSettings::PivotPoint">Center</enum>
          188  +        </struct>
          189  +        <key>fileList</key>
          190  +        <array>
          191  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/player/back-jump</filename>
          192  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/player/climb</filename>
          193  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/player/crouch</filename>
          194  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/player/hurt</filename>
          195  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/player/idle</filename>
          196  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/player/jump</filename>
          197  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/player/run</filename>
          198  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/player/shoot</filename>
          199  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/player/walk</filename>
          200  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/vehicles</filename>
          201  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/antenna.png</filename>
          202  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/control-box-1.png</filename>
          203  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/control-box-2.png</filename>
          204  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/control-box-3.png</filename>
          205  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/banner-big</filename>
          206  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/banner-coke</filename>
          207  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/banner-neon</filename>
          208  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/banner-scroll</filename>
          209  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/banner-side</filename>
          210  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/banner-sushi</filename>
          211  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/ENVIRONMENT/props/monitorface</filename>
          212  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/misc/drone</filename>
          213  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/misc/enemy-explosion</filename>
          214  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/misc/shot</filename>
          215  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/misc/shot-hit</filename>
          216  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/misc/turret</filename>
          217  +            <filename>../../../../../../../ansimuz resources/ART PACKS/2019/warped city/warped city files/SPRITES/player/run-shoot</filename>
          218  +        </array>
          219  +        <key>ignoreFileList</key>
          220  +        <array/>
          221  +        <key>replaceList</key>
          222  +        <array/>
          223  +        <key>ignoredWarnings</key>
          224  +        <array/>
          225  +        <key>commonDivisorX</key>
          226  +        <uint>1</uint>
          227  +        <key>commonDivisorY</key>
          228  +        <uint>1</uint>
          229  +    </struct>
          230  +</data>

Added src/worpt/assets/warped_city/environment/bg-1.png.

cannot compute difference between binary files

Added src/worpt/assets/warped_city/environment/bg-2.png.

cannot compute difference between binary files

Added src/worpt/assets/warped_city/environment/bg-3.png.

cannot compute difference between binary files

Added src/worpt/assets/warped_city/environment/tileset.png.

cannot compute difference between binary files

Added src/worpt/assets/warped_city/maps/map.json.

            1  +{ "backgroundcolor":"#0000ff",
            2  + "height":28,
            3  + "infinite":false,
            4  + "layers":[
            5  +        {
            6  +         "data":[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
            7  +         "height":28,
            8  +         "name":"Collisions Layer",
            9  +         "opacity":1,
           10  +         "type":"tilelayer",
           11  +         "visible":true,
           12  +         "width":120,
           13  +         "x":0,
           14  +         "y":0
           15  +        }, 
           16  +        {
           17  +         "data":[0, 26, 76, 77, 78, 29, 29, 76, 77, 78, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 35, 39, 40, 41, 37, 159, 160, 161, 37, 279, 280, 281, 37, 39, 40, 41, 43, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 76, 77, 78, 245, 29, 76, 77, 78, 80, 0, 50, 100, 101, 102, 29, 29, 100, 101, 102, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 63, 64, 65, 61, 183, 184, 185, 61, 303, 304, 305, 61, 63, 64, 65, 67, 68, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 100, 101, 102, 269, 29, 100, 101, 102, 56, 0, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, 87, 88, 89, 85, 207, 208, 209, 85, 327, 328, 329, 61, 87, 88, 89, 91, 92, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 0, 26, 28, 29, 30, 245, 163, 28, 29, 30, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, 111, 112, 113, 109, 231, 232, 233, 85, 351, 352, 353, 61, 111, 112, 113, 115, 116, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 225, 224, 224, 224, 0, 0, 0, 224, 224, 224, 225, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 28, 29, 30, 228, 229, 28, 29, 30, 32, 0, 50, 52, 53, 54, 269, 187, 52, 53, 54, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 52, 53, 54, 252, 253, 52, 53, 54, 32, 0, 74, 76, 77, 78, 245, 29, 76, 77, 78, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 35, 39, 40, 41, 37, 159, 160, 161, 37, 39, 40, 41, 37, 279, 280, 281, 43, 44, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 35, 29, 29, 29, 29, 165, 147, 29, 228, 229, 43, 44, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 76, 77, 78, 167, 29, 76, 77, 78, 56, 0, 26, 100, 101, 102, 269, 29, 100, 101, 102, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 63, 64, 65, 61, 183, 184, 185, 61, 63, 64, 65, 61, 303, 304, 305, 67, 68, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 189, 189, 29, 29, 29, 147, 29, 252, 253, 67, 68, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 100, 101, 102, 191, 29, 100, 101, 102, 80, 0, 50, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, 87, 88, 89, 109, 207, 208, 209, 85, 87, 88, 89, 61, 327, 328, 329, 91, 92, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, 165, 189, 29, 29, 29, 147, 29, 29, 29, 91, 92, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 273, 272, 272, 272, 272, 272, 272, 272, 272, 272, 0, 74, 28, 29, 30, 228, 229, 28, 29, 30, 32, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, 111, 112, 113, 85, 231, 232, 233, 85, 111, 112, 113, 61, 351, 352, 353, 115, 116, 0, 0, 0, 0, 224, 224, 225, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, 29, 29, 29, 29, 29, 147, 29, 29, 29, 115, 116, 0, 0, 0, 0, 224, 224, 225, 224, 224, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 50, 28, 29, 30, 167, 245, 28, 29, 30, 56, 0, 26, 52, 53, 54, 252, 253, 52, 53, 54, 32, 0, 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 147, 272, 272, 272, 272, 272, 274, 0, 0, 271, 272, 272, 272, 272, 272, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, 155, 156, 157, 29, 29, 147, 29, 189, 165, 67, 68, 0, 0, 0, 271, 272, 272, 272, 272, 272, 274, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 50, 52, 53, 54, 191, 245, 52, 53, 54, 56, 0, 50, 76, 77, 78, 29, 29, 76, 77, 78, 56, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 35, 279, 280, 281, 37, 159, 160, 161, 37, 39, 40, 41, 147, 159, 160, 161, 43, 44, 0, 0, 0, 0, 26, 245, 167, 29, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, 179, 180, 181, 29, 29, 147, 29, 189, 189, 91, 92, 0, 0, 0, 0, 26, 245, 167, 29, 56, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 26, 76, 77, 78, 167, 245, 76, 77, 78, 32, 0, 74, 100, 101, 102, 29, 29, 100, 101, 102, 80, 0, 0, 0, 224, 224, 225, 224, 224, 224, 224, 224, 224, 224, 0, 0, 0, 0, 58, 59, 303, 304, 305, 61, 183, 184, 185, 61, 63, 64, 65, 147, 183, 184, 185, 67, 68, 0, 0, 0, 0, 50, 245, 191, 29, 32, 0, 0, 0, 0, 0, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 147, 272, 272, 272, 272, 272, 274, 0, 0, 0, 50, 245, 191, 29, 32, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 50, 100, 101, 102, 191, 269, 100, 101, 102, 56, 271, 273, 272, 272, 272, 147, 272, 272, 272, 272, 272, 274, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 82, 83, 327, 328, 329, 85, 207, 208, 209, 85, 87, 88, 89, 147, 207, 208, 209, 91, 92, 0, 0, 0, 0, 74, 245, 167, 29, 56, 0, 0, 0, 0, 0, 34, 35, 228, 229, 29, 37, 29, 228, 229, 37, 228, 229, 29, 147, 167, 29, 29, 43, 44, 0, 0, 0, 0, 74, 245, 167, 29, 56, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 0, 50, 28, 29, 30, 147, 245, 28, 29, 30, 56, 0, 0, 0, 26, 228, 229, 245, 149, 29, 163, 228, 229, 32, 0, 0, 0, 0, 106, 107, 351, 352, 353, 85, 231, 232, 233, 85, 111, 112, 113, 147, 231, 232, 233, 115, 116, 0, 0, 0, 0, 26, 245, 191, 29, 56, 0, 0, 0, 0, 0, 58, 59, 252, 253, 29, 61, 29, 252, 253, 61, 252, 253, 29, 147, 191, 29, 29, 67, 68, 0, 0, 0, 0, 26, 245, 191, 29, 56, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 26, 28, 29, 30, 245, 163, 28, 29, 30, 32, 0, 50, 52, 53, 54, 147, 245, 52, 53, 54, 56, 0, 0, 0, 50, 252, 253, 245, 173, 29, 187, 252, 253, 56, 0, 0, 0, 271, 272, 272, 272, 272, 272, 61, 46, 46, 46, 46, 46, 46, 46, 147, 272, 272, 272, 272, 272, 274, 0, 0, 0, 50, 245, 167, 29, 56, 0, 0, 0, 0, 0, 82, 83, 165, 165, 29, 109, 29, 189, 189, 61, 189, 189, 29, 147, 167, 29, 189, 91, 92, 0, 0, 0, 0, 50, 245, 167, 29, 56, 0, 0, 0, 0, 224, 224, 225, 224, 224, 0, 0, 0, 0, 224, 224, 225, 224, 224, 0, 0, 0, 50, 52, 53, 54, 269, 187, 52, 53, 54, 56, 0, 26, 76, 77, 78, 147, 245, 76, 77, 78, 32, 0, 0, 0, 74, 29, 29, 245, 173, 29, 29, 29, 167, 80, 0, 0, 0, 0, 34, 35, 39, 40, 41, 61, 228, 229, 29, 29, 29, 228, 229, 147, 39, 40, 41, 43, 44, 0, 0, 0, 0, 74, 245, 191, 29, 80, 0, 0, 0, 0, 0, 106, 107, 189, 189, 29, 61, 29, 189, 165, 61, 189, 189, 29, 147, 191, 29, 29, 115, 116, 0, 0, 0, 0, 74, 245, 191, 29, 80, 0, 0, 0, 271, 272, 272, 272, 272, 272, 274, 0, 0, 271, 272, 272, 272, 272, 272, 274, 0, 0, 74, 76, 77, 78, 245, 29, 76, 77, 78, 80, 0, 50, 100, 101, 102, 147, 269, 100, 101, 102, 56, 0, 0, 0, 26, 163, 29, 245, 173, 29, 29, 29, 191, 32, 0, 0, 0, 0, 58, 59, 63, 64, 65, 61, 252, 253, 29, 29, 29, 252, 253, 147, 63, 64, 65, 67, 68, 0, 0, 0, 0, 26, 228, 229, 29, 56, 0, 0, 0, 0, 0, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 147, 46, 46, 46, 46, 46, 0, 0, 0, 0, 26, 228, 229, 29, 56, 0, 0, 0, 0, 26, 245, 167, 29, 56, 0, 0, 0, 0, 26, 245, 167, 29, 56, 0, 0, 0, 26, 100, 101, 102, 269, 29, 100, 101, 102, 56, 0, 46, 46, 46, 46, 147, 46, 46, 46, 46, 46, 0, 0, 0, 50, 187, 29, 245, 173, 155, 156, 157, 29, 56, 0, 0, 0, 0, 82, 83, 87, 88, 89, 85, 165, 29, 155, 156, 157, 29, 29, 147, 87, 88, 89, 91, 92, 0, 0, 0, 0, 50, 252, 253, 29, 56, 0, 0, 0, 0, 0, 34, 35, 279, 280, 281, 37, 159, 160, 161, 37, 39, 40, 41, 147, 159, 160, 161, 43, 44, 0, 0, 0, 0, 50, 252, 253, 29, 56, 0, 0, 0, 0, 50, 245, 191, 29, 32, 0, 0, 0, 0, 50, 245, 191, 29, 32, 0, 0, 0, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 0, 26, 28, 29, 30, 147, 29, 28, 29, 30, 32, 0, 0, 0, 74, 29, 189, 189, 173, 179, 180, 181, 29, 80, 0, 0, 0, 0, 106, 107, 111, 112, 113, 109, 189, 29, 179, 180, 181, 29, 29, 171, 111, 112, 113, 115, 116, 0, 0, 0, 0, 74, 29, 167, 29, 80, 0, 0, 0, 0, 0, 58, 59, 303, 304, 305, 61, 183, 184, 185, 61, 63, 64, 65, 147, 183, 184, 185, 67, 68, 0, 0, 0, 0, 74, 29, 167, 29, 80, 0, 0, 0, 0, 74, 245, 167, 29, 56, 0, 0, 0, 0, 74, 245, 167, 29, 56, 0, 0, 0, 74, 28, 29, 30, 228, 229, 28, 29, 30, 32, 0, 50, 52, 53, 54, 147, 29, 52, 53, 54, 56, 0, 0, 0, 26, 29, 163, 163, 173, 228, 229, 167, 29, 32, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 26, 29, 191, 29, 56, 0, 0, 0, 0, 0, 82, 83, 327, 328, 329, 85, 207, 208, 209, 85, 87, 88, 89, 147, 207, 208, 209, 91, 92, 0, 0, 0, 0, 26, 29, 191, 29, 56, 0, 0, 0, 0, 26, 245, 191, 29, 56, 0, 0, 0, 0, 26, 245, 191, 29, 56, 0, 0, 0, 26, 52, 53, 54, 252, 253, 52, 53, 54, 32, 0, 74, 76, 77, 78, 147, 163, 76, 77, 78, 80, 0, 0, 0, 50, 29, 187, 187, 197, 252, 253, 191, 29, 56, 0, 0, 0, 0, 34, 35, 159, 160, 161, 37, 159, 160, 161, 37, 279, 280, 281, 37, 39, 40, 41, 43, 44, 0, 0, 0, 0, 50, 29, 167, 29, 56, 0, 0, 0, 0, 0, 106, 107, 351, 352, 353, 85, 231, 232, 233, 85, 111, 112, 113, 171, 231, 232, 233, 115, 116, 0, 0, 0, 0, 50, 29, 167, 29, 56, 0, 0, 0, 0, 50, 245, 167, 29, 56, 0, 0, 0, 0, 50, 245, 167, 29, 56, 0, 0, 0, 50, 76, 77, 78, 29, 167, 76, 77, 78, 56, 0, 26, 100, 101, 102, 171, 187, 100, 101, 102, 56, 0, 0, 0, 74, 29, 245, 245, 29, 173, 29, 167, 29, 80, 0, 0, 0, 0, 58, 59, 183, 184, 185, 61, 183, 184, 185, 61, 303, 304, 305, 109, 63, 64, 65, 67, 68, 0, 0, 0, 0, 74, 29, 191, 29, 56, 0, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 74, 29, 191, 29, 56, 0, 0, 0, 0, 74, 245, 191, 29, 80, 0, 0, 0, 0, 74, 245, 191, 29, 80, 0, 0, 0, 74, 100, 101, 102, 29, 191, 100, 101, 102, 80, 0, 50, 272, 272, 272, 272, 272, 272, 272, 147, 272, 274, 0, 0, 26, 29, 245, 245, 29, 173, 29, 191, 29, 32, 0, 0, 0, 0, 82, 83, 207, 208, 209, 85, 207, 208, 209, 85, 327, 328, 329, 61, 87, 88, 89, 91, 92, 0, 0, 0, 0, 26, 29, 167, 29, 56, 0, 0, 0, 0, 0, 34, 35, 39, 40, 41, 61, 159, 160, 161, 61, 159, 160, 161, 37, 39, 40, 41, 43, 44, 0, 0, 0, 0, 26, 29, 167, 29, 56, 0, 0, 0, 0, 26, 228, 229, 29, 56, 0, 0, 0, 0, 26, 228, 229, 29, 56, 0, 0, 0, 273, 272, 272, 272, 272, 272, 272, 272, 272, 272, 0, 74, 28, 29, 30, 228, 229, 28, 29, 147, 32, 0, 0, 0, 50, 29, 245, 245, 29, 173, 189, 189, 189, 56, 0, 0, 0, 0, 106, 107, 231, 232, 233, 85, 231, 232, 233, 85, 351, 352, 353, 61, 111, 112, 113, 115, 116, 0, 0, 0, 0, 50, 29, 191, 29, 56, 0, 0, 0, 0, 0, 58, 59, 63, 64, 65, 61, 183, 184, 185, 61, 183, 184, 185, 61, 63, 64, 65, 67, 68, 0, 0, 0, 0, 50, 29, 191, 29, 56, 0, 0, 0, 0, 50, 252, 253, 29, 56, 0, 0, 0, 0, 50, 252, 253, 29, 56, 0, 0, 0, 50, 28, 29, 30, 29, 245, 28, 29, 30, 56, 0, 26, 52, 53, 54, 252, 253, 52, 53, 147, 32, 0, 0, 0, 74, 29, 245, 245, 29, 173, 189, 189, 189, 80, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 74, 29, 167, 29, 56, 0, 0, 0, 0, 0, 82, 83, 87, 88, 89, 85, 207, 208, 209, 61, 207, 208, 209, 109, 87, 88, 89, 91, 92, 0, 0, 0, 0, 74, 29, 167, 29, 56, 0, 0, 0, 0, 74, 29, 167, 29, 80, 0, 0, 0, 0, 74, 29, 167, 29, 80, 0, 0, 0, 50, 52, 53, 54, 29, 245, 52, 53, 54, 56, 0, 50, 76, 77, 78, 29, 29, 76, 77, 147, 56, 0, 0, 0, 26, 29, 197, 245, 29, 173, 29, 29, 29, 32, 0, 0, 0, 0, 34, 35, 39, 40, 41, 37, 159, 160, 161, 37, 39, 40, 41, 37, 39, 40, 41, 43, 44, 0, 0, 0, 0, 50, 29, 191, 29, 56, 0, 0, 0, 0, 0, 106, 107, 111, 112, 113, 109, 231, 232, 233, 61, 231, 232, 233, 61, 111, 112, 113, 115, 116, 0, 0, 0, 0, 50, 29, 191, 29, 56, 0, 0, 0, 0, 26, 29, 191, 29, 56, 0, 0, 0, 0, 26, 29, 191, 29, 56, 0, 0, 0, 26, 76, 77, 78, 29, 245, 76, 77, 78, 32, 0, 74, 100, 101, 102, 29, 29, 100, 101, 171, 80, 0, 0, 0, 50, 155, 156, 157, 29, 173, 29, 228, 229, 56, 0, 0, 0, 0, 58, 59, 63, 64, 65, 61, 183, 184, 185, 61, 63, 64, 65, 61, 63, 64, 65, 67, 68, 0, 0, 0, 0, 50, 155, 156, 157, 80, 0, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 50, 155, 156, 157, 80, 0, 0, 0, 0, 50, 29, 167, 29, 56, 0, 0, 0, 0, 50, 29, 167, 29, 56, 0, 0, 0, 50, 100, 101, 102, 29, 269, 100, 101, 102, 56, 271, 273, 273, 273, 273, 273, 272, 272, 272, 272, 272, 274, 0, 0, 74, 179, 180, 181, 29, 173, 29, 252, 253, 80, 0, 0, 0, 0, 82, 83, 87, 88, 89, 85, 207, 208, 209, 85, 87, 88, 89, 61, 87, 88, 89, 91, 92, 0, 0, 0, 0, 50, 179, 180, 181, 56, 0, 0, 0, 0, 0, 34, 35, 159, 160, 161, 37, 159, 160, 161, 37, 279, 280, 281, 37, 39, 40, 41, 43, 44, 0, 0, 0, 0, 50, 179, 180, 181, 56, 0, 0, 0, 0, 74, 29, 191, 29, 56, 0, 0, 0, 0, 74, 29, 191, 29, 56, 0, 0, 0, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272],
           18  +         "height":28,
           19  +         "name":"Main Layer",
           20  +         "opacity":1,
           21  +         "type":"tilelayer",
           22  +         "visible":true,
           23  +         "width":120,
           24  +         "x":0,
           25  +         "y":0
           26  +        }, 
           27  +        {
           28  +         "draworder":"topdown",
           29  +         "name":"Object Layer",
           30  +         "objects":[
           31  +                {
           32  +                 "gid":5,
           33  +                 "height":16,
           34  +                 "id":12,
           35  +                 "name":"",
           36  +                 "rotation":0,
           37  +                 "type":"",
           38  +                 "visible":true,
           39  +                 "width":16,
           40  +                 "x":294.666666666667,
           41  +                 "y":382.333333333333
           42  +                }, 
           43  +                {
           44  +                 "gid":6,
           45  +                 "height":16,
           46  +                 "id":13,
           47  +                 "name":"",
           48  +                 "rotation":0,
           49  +                 "type":"",
           50  +                 "visible":true,
           51  +                 "width":16,
           52  +                 "x":240.333333333333,
           53  +                 "y":300
           54  +                }, 
           55  +                {
           56  +                 "gid":7,
           57  +                 "height":16,
           58  +                 "id":14,
           59  +                 "name":"",
           60  +                 "rotation":0,
           61  +                 "type":"",
           62  +                 "visible":true,
           63  +                 "width":16,
           64  +                 "x":19.6666666666666,
           65  +                 "y":142.666666666667
           66  +                }, 
           67  +                {
           68  +                 "gid":8,
           69  +                 "height":16,
           70  +                 "id":17,
           71  +                 "name":"",
           72  +                 "rotation":0,
           73  +                 "type":"",
           74  +                 "visible":true,
           75  +                 "width":16,
           76  +                 "x":166,
           77  +                 "y":415.666666666667
           78  +                }, 
           79  +                {
           80  +                 "gid":9,
           81  +                 "height":16,
           82  +                 "id":19,
           83  +                 "name":"",
           84  +                 "rotation":0,
           85  +                 "type":"",
           86  +                 "visible":true,
           87  +                 "width":16,
           88  +                 "x":161,
           89  +                 "y":247.333333333333
           90  +                }, 
           91  +                {
           92  +                 "gid":11,
           93  +                 "height":16,
           94  +                 "id":21,
           95  +                 "name":"",
           96  +                 "rotation":0,
           97  +                 "type":"",
           98  +                 "visible":true,
           99  +                 "width":16,
          100  +                 "x":32.6666666666667,
          101  +                 "y":417.666666666667
          102  +                }, 
          103  +                {
          104  +                 "gid":11,
          105  +                 "height":16,
          106  +                 "id":22,
          107  +                 "name":"",
          108  +                 "rotation":0,
          109  +                 "type":"",
          110  +                 "visible":true,
          111  +                 "width":16,
          112  +                 "x":30.3333333333333,
          113  +                 "y":285
          114  +                }, 
          115  +                {
          116  +                 "gid":10,
          117  +                 "height":16,
          118  +                 "id":23,
          119  +                 "name":"",
          120  +                 "rotation":0,
          121  +                 "type":"",
          122  +                 "visible":true,
          123  +                 "width":16,
          124  +                 "x":248.333333333333,
          125  +                 "y":425
          126  +                }, 
          127  +                {
          128  +                 "gid":6,
          129  +                 "height":16,
          130  +                 "id":29,
          131  +                 "name":"",
          132  +                 "rotation":0,
          133  +                 "type":"",
          134  +                 "visible":true,
          135  +                 "width":16,
          136  +                 "x":737.333333333333,
          137  +                 "y":299.333333333333
          138  +                }, 
          139  +                {
          140  +                 "gid":5,
          141  +                 "height":16,
          142  +                 "id":30,
          143  +                 "name":"",
          144  +                 "rotation":0,
          145  +                 "type":"",
          146  +                 "visible":true,
          147  +                 "width":16,
          148  +                 "x":442,
          149  +                 "y":44.6666666666667
          150  +                }, 
          151  +                {
          152  +                 "gid":9,
          153  +                 "height":16,
          154  +                 "id":31,
          155  +                 "name":"",
          156  +                 "rotation":0,
          157  +                 "type":"",
          158  +                 "visible":true,
          159  +                 "width":16,
          160  +                 "x":811.333333333333,
          161  +                 "y":203.333333333333
          162  +                }, 
          163  +                {
          164  +                 "gid":10,
          165  +                 "height":16,
          166  +                 "id":32,
          167  +                 "name":"",
          168  +                 "rotation":0,
          169  +                 "type":"",
          170  +                 "visible":true,
          171  +                 "width":16,
          172  +                 "x":730,
          173  +                 "y":174
          174  +                }, 
          175  +                {
          176  +                 "gid":5,
          177  +                 "height":16,
          178  +                 "id":33,
          179  +                 "name":"",
          180  +                 "rotation":0,
          181  +                 "type":"",
          182  +                 "visible":true,
          183  +                 "width":16,
          184  +                 "x":1264,
          185  +                 "y":313.333333333333
          186  +                }, 
          187  +                {
          188  +                 "gid":5,
          189  +                 "height":16,
          190  +                 "id":34,
          191  +                 "name":"",
          192  +                 "rotation":0,
          193  +                 "type":"",
          194  +                 "visible":true,
          195  +                 "width":16,
          196  +                 "x":1480.66666666667,
          197  +                 "y":291.333333333333
          198  +                }, 
          199  +                {
          200  +                 "gid":6,
          201  +                 "height":16,
          202  +                 "id":35,
          203  +                 "name":"",
          204  +                 "rotation":0,
          205  +                 "type":"",
          206  +                 "visible":true,
          207  +                 "width":16,
          208  +                 "x":1062.66666666667,
          209  +                 "y":90.6666666666667
          210  +                }, 
          211  +                {
          212  +                 "gid":10,
          213  +                 "height":16,
          214  +                 "id":36,
          215  +                 "name":"",
          216  +                 "rotation":0,
          217  +                 "type":"",
          218  +                 "visible":true,
          219  +                 "width":16,
          220  +                 "x":1646,
          221  +                 "y":268
          222  +                }, 
          223  +                {
          224  +                 "gid":11,
          225  +                 "height":16,
          226  +                 "id":37,
          227  +                 "name":"",
          228  +                 "rotation":0,
          229  +                 "type":"",
          230  +                 "visible":true,
          231  +                 "width":16,
          232  +                 "x":1039.33333333333,
          233  +                 "y":264
          234  +                }, 
          235  +                {
          236  +                 "gid":11,
          237  +                 "height":16,
          238  +                 "id":38,
          239  +                 "name":"",
          240  +                 "rotation":0,
          241  +                 "type":"",
          242  +                 "visible":true,
          243  +                 "width":16,
          244  +                 "x":1370,
          245  +                 "y":222.666666666667
          246  +                }, 
          247  +                {
          248  +                 "gid":11,
          249  +                 "height":16,
          250  +                 "id":39,
          251  +                 "name":"",
          252  +                 "rotation":0,
          253  +                 "type":"",
          254  +                 "visible":true,
          255  +                 "width":16,
          256  +                 "x":1129.33333333333,
          257  +                 "y":264
          258  +                }, 
          259  +                {
          260  +                 "gid":9,
          261  +                 "height":16,
          262  +                 "id":40,
          263  +                 "name":"",
          264  +                 "rotation":0,
          265  +                 "type":"",
          266  +                 "visible":true,
          267  +                 "width":16,
          268  +                 "x":741.333333333333,
          269  +                 "y":390.666666666667
          270  +                }, 
          271  +                {
          272  +                 "gid":9,
          273  +                 "height":16,
          274  +                 "id":41,
          275  +                 "name":"",
          276  +                 "rotation":0,
          277  +                 "type":"",
          278  +                 "visible":true,
          279  +                 "width":16,
          280  +                 "x":323.333333333333,
          281  +                 "y":298.666666666667
          282  +                }, 
          283  +                {
          284  +                 "gid":11,
          285  +                 "height":16,
          286  +                 "id":42,
          287  +                 "name":"",
          288  +                 "rotation":0,
          289  +                 "type":"",
          290  +                 "visible":true,
          291  +                 "width":16,
          292  +                 "x":1798.66666666667,
          293  +                 "y":318
          294  +                }, 
          295  +                {
          296  +                 "gid":11,
          297  +                 "height":16,
          298  +                 "id":43,
          299  +                 "name":"",
          300  +                 "rotation":0,
          301  +                 "type":"",
          302  +                 "visible":true,
          303  +                 "width":16,
          304  +                 "x":1869.33333333333,
          305  +                 "y":104.666666666667
          306  +                }, 
          307  +                {
          308  +                 "gid":9,
          309  +                 "height":16,
          310  +                 "id":44,
          311  +                 "name":"",
          312  +                 "rotation":0,
          313  +                 "type":"",
          314  +                 "visible":true,
          315  +                 "width":16,
          316  +                 "x":1908,
          317  +                 "y":186.666666666667
          318  +                }],
          319  +         "opacity":1,
          320  +         "type":"objectgroup",
          321  +         "visible":true,
          322  +         "x":0,
          323  +         "y":0
          324  +        }],
          325  + "nextobjectid":45,
          326  + "orientation":"orthogonal",
          327  + "renderorder":"right-down",
          328  + "tiledversion":"1.1.4",
          329  + "tileheight":16,
          330  + "tilesets":[
          331  +        {
          332  +         "columns":24,
          333  +         "firstgid":1,
          334  +         "image":"..\/environment\/tileset.png",
          335  +         "imageheight":256,
          336  +         "imagewidth":384,
          337  +         "margin":0,
          338  +         "name":"tileset",
          339  +         "spacing":0,
          340  +         "tilecount":384,
          341  +         "tileheight":16,
          342  +         "tilewidth":16
          343  +        }],
          344  + "tilewidth":16,
          345  + "type":"map",
          346  + "version":1,
          347  + "width":120
          348  +}

Added src/worpt/assets/warped_city/public-license.txt.

            1  +Artwork created by Luis Zuno @ansimuz
            2  +
            3  +License for Everyone. 
            4  +
            5  +Public domain and free to use on whatever you want, personal or commercial. Credit is not required but appreciated.
            6  +
            7  +Feeling Grateful?
            8  +You can support my work at https://www.patreon.com/ansimuz
            9  +
           10  +Get more Free Assetslike these at: http://www.pixelgameart.org
           11  +
           12  +Additional credits
           13  +Demo Music by Pascal Belisle
           14  +
           15  +pacethemusician@hotmail.com
           16  +https://soundcloud.com/pascalbelisle
           17  +

Added src/worpt/conf.lua.

            1  +love.conf = function(t)
            2  +   t.gammacorrect = true
            3  +   t.title, t.identity = "worpt", "worpt"
            4  +   t.modules.joystick = false
            5  +   t.modules.physics = false
            6  +   t.window.width = 768
            7  +   t.window.height = 448
            8  +   t.window.vsync = false
            9  +   t.version = "11.3"
           10  +end

Added src/worpt/lib/anim8.lua.

            1  +local anim8 = {
            2  +  _VERSION     = 'anim8 v2.3.1',
            3  +  _DESCRIPTION = 'An animation library for LÖVE',
            4  +  _URL         = 'https://github.com/kikito/anim8',
            5  +  _LICENSE     = [[
            6  +    MIT LICENSE
            7  +
            8  +    Copyright (c) 2011 Enrique García Cota
            9  +
           10  +    Permission is hereby granted, free of charge, to any person obtaining a
           11  +    copy of this software and associated documentation files (the
           12  +    "Software"), to deal in the Software without restriction, including
           13  +    without limitation the rights to use, copy, modify, merge, publish,
           14  +    distribute, sublicense, and/or sell copies of the Software, and to
           15  +    permit persons to whom the Software is furnished to do so, subject to
           16  +    the following conditions:
           17  +
           18  +    The above copyright notice and this permission notice shall be included
           19  +    in all copies or substantial portions of the Software.
           20  +
           21  +    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
           22  +    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
           23  +    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
           24  +    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
           25  +    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
           26  +    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
           27  +    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
           28  +  ]]
           29  +}
           30  +
           31  +local Grid = {}
           32  +
           33  +local _frames = {}
           34  +
           35  +local function assertPositiveInteger(value, name)
           36  +  if type(value) ~= 'number' then error(("%s should be a number, was %q"):format(name, tostring(value))) end
           37  +  if value < 1 then error(("%s should be a positive number, was %d"):format(name, value)) end
           38  +  if value ~= math.floor(value) then error(("%s should be an integer, was %d"):format(name, value)) end
           39  +end
           40  +
           41  +local function createFrame(self, x, y)
           42  +  local fw, fh = self.frameWidth, self.frameHeight
           43  +  return love.graphics.newQuad(
           44  +    self.left + (x-1) * fw + x * self.border,
           45  +    self.top  + (y-1) * fh + y * self.border,
           46  +    fw,
           47  +    fh,
           48  +    self.imageWidth,
           49  +    self.imageHeight
           50  +  )
           51  +end
           52  +
           53  +local function getGridKey(...)
           54  +  return table.concat( {...} ,'-' )
           55  +end
           56  +
           57  +local function getOrCreateFrame(self, x, y)
           58  +  if x < 1 or x > self.width or y < 1 or y > self.height then
           59  +    error(("There is no frame for x=%d, y=%d"):format(x, y))
           60  +  end
           61  +  local key = self._key
           62  +  _frames[key]       = _frames[key]       or {}
           63  +  _frames[key][x]    = _frames[key][x]    or {}
           64  +  _frames[key][x][y] = _frames[key][x][y] or createFrame(self, x, y)
           65  +  return _frames[key][x][y]
           66  +end
           67  +
           68  +local function parseInterval(str)
           69  +  if type(str) == "number" then return str,str,1 end
           70  +  str = str:gsub('%s', '') -- remove spaces
           71  +  local min, max = str:match("^(%d+)-(%d+)$")
           72  +  assert(min and max, ("Could not parse interval from %q"):format(str))
           73  +  min, max = tonumber(min), tonumber(max)
           74  +  local step = min <= max and 1 or -1
           75  +  return min, max, step
           76  +end
           77  +
           78  +function Grid:getFrames(...)
           79  +  local result, args = {}, {...}
           80  +  local minx, maxx, stepx, miny, maxy, stepy
           81  +
           82  +  for i=1, #args, 2 do
           83  +    minx, maxx, stepx = parseInterval(args[i])
           84  +    miny, maxy, stepy = parseInterval(args[i+1])
           85  +    for y = miny, maxy, stepy do
           86  +      for x = minx, maxx, stepx do
           87  +        result[#result+1] = getOrCreateFrame(self,x,y)
           88  +      end
           89  +    end
           90  +  end
           91  +
           92  +  return result
           93  +end
           94  +
           95  +local Gridmt = {
           96  +  __index = Grid,
           97  +  __call  = Grid.getFrames
           98  +}
           99  +
          100  +local function newGrid(frameWidth, frameHeight, imageWidth, imageHeight, left, top, border)
          101  +  assertPositiveInteger(frameWidth,  "frameWidth")
          102  +  assertPositiveInteger(frameHeight, "frameHeight")
          103  +  assertPositiveInteger(imageWidth,  "imageWidth")
          104  +  assertPositiveInteger(imageHeight, "imageHeight")
          105  +
          106  +  left   = left   or 0
          107  +  top    = top    or 0
          108  +  border = border or 0
          109  +
          110  +  local key  = getGridKey(frameWidth, frameHeight, imageWidth, imageHeight, left, top, border)
          111  +
          112  +  local grid = setmetatable(
          113  +    { frameWidth  = frameWidth,
          114  +      frameHeight = frameHeight,
          115  +      imageWidth  = imageWidth,
          116  +      imageHeight = imageHeight,
          117  +      left        = left,
          118  +      top         = top,
          119  +      border      = border,
          120  +      width       = math.floor(imageWidth/frameWidth),
          121  +      height      = math.floor(imageHeight/frameHeight),
          122  +      _key        = key
          123  +    },
          124  +    Gridmt
          125  +  )
          126  +  return grid
          127  +end
          128  +
          129  +-----------------------------------------------------------
          130  +
          131  +local Animation = {}
          132  +
          133  +local function cloneArray(arr)
          134  +  local result = {}
          135  +  for i=1,#arr do result[i] = arr[i] end
          136  +  return result
          137  +end
          138  +
          139  +local function parseDurations(durations, frameCount)
          140  +  local result = {}
          141  +  if type(durations) == 'number' then
          142  +    for i=1,frameCount do result[i] = durations end
          143  +  else
          144  +    local min, max, step
          145  +    for key,duration in pairs(durations) do
          146  +      assert(type(duration) == 'number', "The value [" .. tostring(duration) .. "] should be a number")
          147  +      min, max, step = parseInterval(key)
          148  +      for i = min,max,step do result[i] = duration end
          149  +    end
          150  +  end
          151  +
          152  +  if #result < frameCount then
          153  +    error("The durations table has length of " .. tostring(#result) .. ", but it should be >= " .. tostring(frameCount))
          154  +  end
          155  +
          156  +  return result
          157  +end
          158  +
          159  +local function parseIntervals(durations)
          160  +  local result, time = {0},0
          161  +  for i=1,#durations do
          162  +    time = time + durations[i]
          163  +    result[i+1] = time
          164  +  end
          165  +  return result, time
          166  +end
          167  +
          168  +local Animationmt = { __index = Animation }
          169  +local nop = function() end
          170  +
          171  +local function newAnimation(frames, durations, onLoop)
          172  +  local td = type(durations);
          173  +  if (td ~= 'number' or durations <= 0) and td ~= 'table' then
          174  +    error("durations must be a positive number. Was " .. tostring(durations) )
          175  +  end
          176  +  onLoop = onLoop or nop
          177  +  durations = parseDurations(durations, #frames)
          178  +  local intervals, totalDuration = parseIntervals(durations)
          179  +  return setmetatable({
          180  +      frames         = cloneArray(frames),
          181  +      durations      = durations,
          182  +      intervals      = intervals,
          183  +      totalDuration  = totalDuration,
          184  +      onLoop         = onLoop,
          185  +      timer          = 0,
          186  +      position       = 1,
          187  +      status         = "playing",
          188  +      flippedH       = false,
          189  +      flippedV       = false
          190  +    },
          191  +    Animationmt
          192  +  )
          193  +end
          194  +
          195  +function Animation:clone()
          196  +  local newAnim = newAnimation(self.frames, self.durations, self.onLoop)
          197  +  newAnim.flippedH, newAnim.flippedV = self.flippedH, self.flippedV
          198  +  return newAnim
          199  +end
          200  +
          201  +function Animation:flipH()
          202  +  self.flippedH = not self.flippedH
          203  +  return self
          204  +end
          205  +
          206  +function Animation:flipV()
          207  +  self.flippedV = not self.flippedV
          208  +  return self
          209  +end
          210  +
          211  +local function seekFrameIndex(intervals, timer)
          212  +  local high, low, i = #intervals-1, 1, 1
          213  +
          214  +  while(low <= high) do
          215  +    i = math.floor((low + high) / 2)
          216  +    if     timer >= intervals[i+1] then low  = i + 1
          217  +    elseif timer <  intervals[i]   then high = i - 1
          218  +    else
          219  +      return i
          220  +    end
          221  +  end
          222  +
          223  +  return i
          224  +end
          225  +
          226  +function Animation:update(dt)
          227  +  if self.status ~= "playing" then return end
          228  +
          229  +  self.timer = self.timer + dt
          230  +  local loops = math.floor(self.timer / self.totalDuration)
          231  +  if loops ~= 0 then
          232  +    self.timer = self.timer - self.totalDuration * loops
          233  +    local f = type(self.onLoop) == 'function' and self.onLoop or self[self.onLoop]
          234  +    f(self, loops)
          235  +  end
          236  +
          237  +  self.position = seekFrameIndex(self.intervals, self.timer)
          238  +end
          239  +
          240  +function Animation:pause()
          241  +  self.status = "paused"
          242  +end
          243  +
          244  +function Animation:gotoFrame(position)
          245  +  self.position = position
          246  +  self.timer = self.intervals[self.position]
          247  +end
          248  +
          249  +function Animation:pauseAtEnd()
          250  +  self.position = #self.frames
          251  +  self.timer = self.totalDuration
          252  +  self:pause()
          253  +end
          254  +
          255  +function Animation:pauseAtStart()
          256  +  self.position = 1
          257  +  self.timer = 0
          258  +  self:pause()
          259  +end
          260  +
          261  +function Animation:resume()
          262  +  self.status = "playing"
          263  +end
          264  +
          265  +function Animation:draw(image, x, y, r, sx, sy, ox, oy, kx, ky)
          266  +  love.graphics.draw(image, self:getFrameInfo(x, y, r, sx, sy, ox, oy, kx, ky))
          267  +end
          268  +
          269  +function Animation:getFrameInfo(x, y, r, sx, sy, ox, oy, kx, ky)
          270  +  local frame = self.frames[self.position]
          271  +  if self.flippedH or self.flippedV then
          272  +    r,sx,sy,ox,oy,kx,ky = r or 0, sx or 1, sy or 1, ox or 0, oy or 0, kx or 0, ky or 0
          273  +    local _,_,w,h = frame:getViewport()
          274  +
          275  +    if self.flippedH then
          276  +      sx = sx * -1
          277  +      ox = w - ox
          278  +      kx = kx * -1
          279  +      ky = ky * -1
          280  +    end
          281  +
          282  +    if self.flippedV then
          283  +      sy = sy * -1
          284  +      oy = h - oy
          285  +      kx = kx * -1
          286  +      ky = ky * -1
          287  +    end
          288  +  end
          289  +  return frame, x, y, r, sx, sy, ox, oy, kx, ky
          290  +end
          291  +
          292  +function Animation:getDimensions()
          293  +  local _,_,w,h = self.frames[self.position]:getViewport()
          294  +  return w,h
          295  +end
          296  +
          297  +-----------------------------------------------------------
          298  +
          299  +anim8.newGrid       = newGrid
          300  +anim8.newAnimation  = newAnimation
          301  +
          302  +return anim8

Added src/worpt/lib/bump.lua.

            1  +local bump = {
            2  +  _VERSION     = 'bump v3.1.7',
            3  +  _URL         = 'https://github.com/kikito/bump.lua',
            4  +  _DESCRIPTION = 'A collision detection library for Lua',
            5  +  _LICENSE     = [[
            6  +    MIT LICENSE
            7  +
            8  +    Copyright (c) 2014 Enrique García Cota
            9  +
           10  +    Permission is hereby granted, free of charge, to any person obtaining a
           11  +    copy of this software and associated documentation files (the
           12  +    "Software"), to deal in the Software without restriction, including
           13  +    without limitation the rights to use, copy, modify, merge, publish,
           14  +    distribute, sublicense, and/or sell copies of the Software, and to
           15  +    permit persons to whom the Software is furnished to do so, subject to
           16  +    the following conditions:
           17  +
           18  +    The above copyright notice and this permission notice shall be included
           19  +    in all copies or substantial portions of the Software.
           20  +
           21  +    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
           22  +    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
           23  +    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
           24  +    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
           25  +    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
           26  +    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
           27  +    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
           28  +  ]]
           29  +}
           30  +
           31  +------------------------------------------
           32  +-- Auxiliary functions
           33  +------------------------------------------
           34  +local DELTA = 1e-10 -- floating-point margin of error
           35  +
           36  +local abs, floor, ceil, min, max = math.abs, math.floor, math.ceil, math.min, math.max
           37  +
           38  +local function sign(x)
           39  +  if x > 0 then return 1 end
           40  +  if x == 0 then return 0 end
           41  +  return -1
           42  +end
           43  +
           44  +local function nearest(x, a, b)
           45  +  if abs(a - x) < abs(b - x) then return a else return b end
           46  +end
           47  +
           48  +local function assertType(desiredType, value, name)
           49  +  if type(value) ~= desiredType then
           50  +    error(name .. ' must be a ' .. desiredType .. ', but was ' .. tostring(value) .. '(a ' .. type(value) .. ')')
           51  +  end
           52  +end
           53  +
           54  +local function assertIsPositiveNumber(value, name)
           55  +  if type(value) ~= 'number' or value <= 0 then
           56  +    error(name .. ' must be a positive integer, but was ' .. tostring(value) .. '(' .. type(value) .. ')')
           57  +  end
           58  +end
           59  +
           60  +local function assertIsRect(x,y,w,h)
           61  +  assertType('number', x, 'x')
           62  +  assertType('number', y, 'y')
           63  +  assertIsPositiveNumber(w, 'w')
           64  +  assertIsPositiveNumber(h, 'h')
           65  +end
           66  +
           67  +local defaultFilter = function()
           68  +  return 'slide'
           69  +end
           70  +
           71  +------------------------------------------
           72  +-- Rectangle functions
           73  +------------------------------------------
           74  +
           75  +local function rect_getNearestCorner(x,y,w,h, px, py)
           76  +  return nearest(px, x, x+w), nearest(py, y, y+h)
           77  +end
           78  +
           79  +-- This is a generalized implementation of the liang-barsky algorithm, which also returns
           80  +-- the normals of the sides where the segment intersects.
           81  +-- Returns nil if the segment never touches the rect
           82  +-- Notice that normals are only guaranteed to be accurate when initially ti1, ti2 == -math.huge, math.huge
           83  +local function rect_getSegmentIntersectionIndices(x,y,w,h, x1,y1,x2,y2, ti1,ti2)
           84  +  ti1, ti2 = ti1 or 0, ti2 or 1
           85  +  local dx, dy = x2-x1, y2-y1
           86  +  local nx, ny
           87  +  local nx1, ny1, nx2, ny2 = 0,0,0,0
           88  +  local p, q, r
           89  +
           90  +  for side = 1,4 do
           91  +    if     side == 1 then nx,ny,p,q = -1,  0, -dx, x1 - x     -- left
           92  +    elseif side == 2 then nx,ny,p,q =  1,  0,  dx, x + w - x1 -- right
           93  +    elseif side == 3 then nx,ny,p,q =  0, -1, -dy, y1 - y     -- top
           94  +    else                  nx,ny,p,q =  0,  1,  dy, y + h - y1 -- bottom
           95  +    end
           96  +
           97  +    if p == 0 then
           98  +      if q <= 0 then return nil end
           99  +    else
          100  +      r = q / p
          101  +      if p < 0 then
          102  +        if     r > ti2 then return nil
          103  +        elseif r > ti1 then ti1,nx1,ny1 = r,nx,ny
          104  +        end
          105  +      else -- p > 0
          106  +        if     r < ti1 then return nil
          107  +        elseif r < ti2 then ti2,nx2,ny2 = r,nx,ny
          108  +        end
          109  +      end
          110  +    end
          111  +  end
          112  +
          113  +  return ti1,ti2, nx1,ny1, nx2,ny2
          114  +end
          115  +
          116  +-- Calculates the minkowsky difference between 2 rects, which is another rect
          117  +local function rect_getDiff(x1,y1,w1,h1, x2,y2,w2,h2)
          118  +  return x2 - x1 - w1,
          119  +         y2 - y1 - h1,
          120  +         w1 + w2,
          121  +         h1 + h2
          122  +end
          123  +
          124  +local function rect_containsPoint(x,y,w,h, px,py)
          125  +  return px - x > DELTA      and py - y > DELTA and
          126  +         x + w - px > DELTA  and y + h - py > DELTA
          127  +end
          128  +
          129  +local function rect_isIntersecting(x1,y1,w1,h1, x2,y2,w2,h2)
          130  +  return x1 < x2+w2 and x2 < x1+w1 and
          131  +         y1 < y2+h2 and y2 < y1+h1
          132  +end
          133  +
          134  +local function rect_getSquareDistance(x1,y1,w1,h1, x2,y2,w2,h2)
          135  +  local dx = x1 - x2 + (w1 - w2)/2
          136  +  local dy = y1 - y2 + (h1 - h2)/2
          137  +  return dx*dx + dy*dy
          138  +end
          139  +
          140  +local function rect_detectCollision(x1,y1,w1,h1, x2,y2,w2,h2, goalX, goalY)
          141  +  goalX = goalX or x1
          142  +  goalY = goalY or y1
          143  +
          144  +  local dx, dy      = goalX - x1, goalY - y1
          145  +  local x,y,w,h     = rect_getDiff(x1,y1,w1,h1, x2,y2,w2,h2)
          146  +
          147  +  local overlaps, ti, nx, ny
          148  +
          149  +  if rect_containsPoint(x,y,w,h, 0,0) then -- item was intersecting other
          150  +    local px, py    = rect_getNearestCorner(x,y,w,h, 0, 0)
          151  +    local wi, hi    = min(w1, abs(px)), min(h1, abs(py)) -- area of intersection
          152  +    ti              = -wi * hi -- ti is the negative area of intersection
          153  +    overlaps = true
          154  +  else
          155  +    local ti1,ti2,nx1,ny1 = rect_getSegmentIntersectionIndices(x,y,w,h, 0,0,dx,dy, -math.huge, math.huge)
          156  +
          157  +    -- item tunnels into other
          158  +    if ti1
          159  +    and ti1 < 1
          160  +    and (abs(ti1 - ti2) >= DELTA) -- special case for rect going through another rect's corner
          161  +    and (0 < ti1 + DELTA
          162  +      or 0 == ti1 and ti2 > 0)
          163  +    then
          164  +      ti, nx, ny = ti1, nx1, ny1
          165  +      overlaps   = false
          166  +    end
          167  +  end
          168  +
          169  +  if not ti then return end
          170  +
          171  +  local tx, ty
          172  +
          173  +  if overlaps then
          174  +    if dx == 0 and dy == 0 then
          175  +      -- intersecting and not moving - use minimum displacement vector
          176  +      local px, py = rect_getNearestCorner(x,y,w,h, 0,0)
          177  +      if abs(px) < abs(py) then py = 0 else px = 0 end
          178  +      nx, ny = sign(px), sign(py)
          179  +      tx, ty = x1 + px, y1 + py
          180  +    else
          181  +      -- intersecting and moving - move in the opposite direction
          182  +      local ti1, _
          183  +      ti1,_,nx,ny = rect_getSegmentIntersectionIndices(x,y,w,h, 0,0,dx,dy, -math.huge, 1)
          184  +      if not ti1 then return end
          185  +      tx, ty = x1 + dx * ti1, y1 + dy * ti1
          186  +    end
          187  +  else -- tunnel
          188  +    tx, ty = x1 + dx * ti, y1 + dy * ti
          189  +  end
          190  +
          191  +  return {
          192  +    overlaps  = overlaps,
          193  +    ti        = ti,
          194  +    move      = {x = dx, y = dy},
          195  +    normal    = {x = nx, y = ny},
          196  +    touch     = {x = tx, y = ty},
          197  +    itemRect  = {x = x1, y = y1, w = w1, h = h1},
          198  +    otherRect = {x = x2, y = y2, w = w2, h = h2}
          199  +  }
          200  +end
          201  +
          202  +------------------------------------------
          203  +-- Grid functions
          204  +------------------------------------------
          205  +
          206  +local function grid_toWorld(cellSize, cx, cy)
          207  +  return (cx - 1)*cellSize, (cy-1)*cellSize
          208  +end
          209  +
          210  +local function grid_toCell(cellSize, x, y)
          211  +  return floor(x / cellSize) + 1, floor(y / cellSize) + 1
          212  +end
          213  +
          214  +-- grid_traverse* functions are based on "A Fast Voxel Traversal Algorithm for Ray Tracing",
          215  +-- by John Amanides and Andrew Woo - http://www.cse.yorku.ca/~amana/research/grid.pdf
          216  +-- It has been modified to include both cells when the ray "touches a grid corner",
          217  +-- and with a different exit condition
          218  +
          219  +local function grid_traverse_initStep(cellSize, ct, t1, t2)
          220  +  local v = t2 - t1
          221  +  if     v > 0 then
          222  +    return  1,  cellSize / v, ((ct + v) * cellSize - t1) / v
          223  +  elseif v < 0 then
          224  +    return -1, -cellSize / v, ((ct + v - 1) * cellSize - t1) / v
          225  +  else
          226  +    return 0, math.huge, math.huge
          227  +  end
          228  +end
          229  +
          230  +local function grid_traverse(cellSize, x1,y1,x2,y2, f)
          231  +  local cx1,cy1        = grid_toCell(cellSize, x1,y1)
          232  +  local cx2,cy2        = grid_toCell(cellSize, x2,y2)
          233  +  local stepX, dx, tx  = grid_traverse_initStep(cellSize, cx1, x1, x2)
          234  +  local stepY, dy, ty  = grid_traverse_initStep(cellSize, cy1, y1, y2)
          235  +  local cx,cy          = cx1,cy1
          236  +
          237  +  f(cx, cy)
          238  +
          239  +  -- The default implementation had an infinite loop problem when
          240  +  -- approaching the last cell in some occassions. We finish iterating
          241  +  -- when we are *next* to the last cell
          242  +  while abs(cx - cx2) + abs(cy - cy2) > 1 do
          243  +    if tx < ty then
          244  +      tx, cx = tx + dx, cx + stepX
          245  +      f(cx, cy)
          246  +    else
          247  +      -- Addition: include both cells when going through corners
          248  +      if tx == ty then f(cx + stepX, cy) end
          249  +      ty, cy = ty + dy, cy + stepY
          250  +      f(cx, cy)
          251  +    end
          252  +  end
          253  +
          254  +  -- If we have not arrived to the last cell, use it
          255  +  if cx ~= cx2 or cy ~= cy2 then f(cx2, cy2) end
          256  +
          257  +end
          258  +
          259  +local function grid_toCellRect(cellSize, x,y,w,h)
          260  +  local cx,cy = grid_toCell(cellSize, x, y)
          261  +  local cr,cb = ceil((x+w) / cellSize), ceil((y+h) / cellSize)
          262  +  return cx, cy, cr - cx + 1, cb - cy + 1
          263  +end
          264  +
          265  +------------------------------------------
          266  +-- Responses
          267  +------------------------------------------
          268  +
          269  +local touch = function(world, col, x,y,w,h, goalX, goalY, filter)
          270  +  return col.touch.x, col.touch.y, {}, 0
          271  +end
          272  +
          273  +local cross = function(world, col, x,y,w,h, goalX, goalY, filter)
          274  +  local cols, len = world:project(col.item, x,y,w,h, goalX, goalY, filter)
          275  +  return goalX, goalY, cols, len
          276  +end
          277  +
          278  +local slide = function(world, col, x,y,w,h, goalX, goalY, filter)
          279  +  goalX = goalX or x
          280  +  goalY = goalY or y
          281  +
          282  +  local tch, move  = col.touch, col.move
          283  +  if move.x ~= 0 or move.y ~= 0 then
          284  +    if col.normal.x ~= 0 then
          285  +      goalX = tch.x
          286  +    else
          287  +      goalY = tch.y
          288  +    end
          289  +  end
          290  +
          291  +  col.slide = {x = goalX, y = goalY}
          292  +
          293  +  x,y = tch.x, tch.y
          294  +  local cols, len  = world:project(col.item, x,y,w,h, goalX, goalY, filter)
          295  +  return goalX, goalY, cols, len
          296  +end
          297  +
          298  +local bounce = function(world, col, x,y,w,h, goalX, goalY, filter)
          299  +  goalX = goalX or x
          300  +  goalY = goalY or y
          301  +
          302  +  local tch, move = col.touch, col.move
          303  +  local tx, ty = tch.x, tch.y
          304  +
          305  +  local bx, by = tx, ty
          306  +
          307  +  if move.x ~= 0 or move.y ~= 0 then
          308  +    local bnx, bny = goalX - tx, goalY - ty
          309  +    if col.normal.x == 0 then bny = -bny else bnx = -bnx end
          310  +    bx, by = tx + bnx, ty + bny
          311  +  end
          312  +
          313  +  col.bounce   = {x = bx,  y = by}
          314  +  x,y          = tch.x, tch.y
          315  +  goalX, goalY = bx, by
          316  +
          317  +  local cols, len    = world:project(col.item, x,y,w,h, goalX, goalY, filter)
          318  +  return goalX, goalY, cols, len
          319  +end
          320  +
          321  +------------------------------------------
          322  +-- World
          323  +------------------------------------------
          324  +
          325  +local World = {}
          326  +local World_mt = {__index = World}
          327  +
          328  +-- Private functions and methods
          329  +
          330  +local function sortByWeight(a,b) return a.weight < b.weight end
          331  +
          332  +local function sortByTiAndDistance(a,b)
          333  +  if a.ti == b.ti then
          334  +    local ir, ar, br = a.itemRect, a.otherRect, b.otherRect
          335  +    local ad = rect_getSquareDistance(ir.x,ir.y,ir.w,ir.h, ar.x,ar.y,ar.w,ar.h)
          336  +    local bd = rect_getSquareDistance(ir.x,ir.y,ir.w,ir.h, br.x,br.y,br.w,br.h)
          337  +    return ad < bd
          338  +  end
          339  +  return a.ti < b.ti
          340  +end
          341  +
          342  +local function addItemToCell(self, item, cx, cy)
          343  +  self.rows[cy] = self.rows[cy] or setmetatable({}, {__mode = 'v'})
          344  +  local row = self.rows[cy]
          345  +  row[cx] = row[cx] or {itemCount = 0, x = cx, y = cy, items = setmetatable({}, {__mode = 'k'})}
          346  +  local cell = row[cx]
          347  +  self.nonEmptyCells[cell] = true
          348  +  if not cell.items[item] then
          349  +    cell.items[item] = true
          350  +    cell.itemCount = cell.itemCount + 1
          351  +  end
          352  +end
          353  +
          354  +local function removeItemFromCell(self, item, cx, cy)
          355  +  local row = self.rows[cy]
          356  +  if not row or not row[cx] or not row[cx].items[item] then return false end
          357  +
          358  +  local cell = row[cx]
          359  +  cell.items[item] = nil
          360  +  cell.itemCount = cell.itemCount - 1
          361  +  if cell.itemCount == 0 then
          362  +    self.nonEmptyCells[cell] = nil
          363  +  end
          364  +  return true
          365  +end
          366  +
          367  +local function getDictItemsInCellRect(self, cl,ct,cw,ch)
          368  +  local items_dict = {}
          369  +  for cy=ct,ct+ch-1 do
          370  +    local row = self.rows[cy]
          371  +    if row then
          372  +      for cx=cl,cl+cw-1 do
          373  +        local cell = row[cx]
          374  +        if cell and cell.itemCount > 0 then -- no cell.itemCount > 1 because tunneling
          375  +          for item,_ in pairs(cell.items) do
          376  +            items_dict[item] = true
          377  +          end
          378  +        end
          379  +      end
          380  +    end
          381  +  end
          382  +
          383  +  return items_dict
          384  +end
          385  +
          386  +local function getCellsTouchedBySegment(self, x1,y1,x2,y2)
          387  +
          388  +  local cells, cellsLen, visited = {}, 0, {}
          389  +
          390  +  grid_traverse(self.cellSize, x1,y1,x2,y2, function(cx, cy)
          391  +    local row  = self.rows[cy]
          392  +    if not row then return end
          393  +    local cell = row[cx]
          394  +    if not cell or visited[cell] then return end
          395  +
          396  +    visited[cell] = true
          397  +    cellsLen = cellsLen + 1
          398  +    cells[cellsLen] = cell
          399  +  end)
          400  +
          401  +  return cells, cellsLen
          402  +end
          403  +
          404  +local function getInfoAboutItemsTouchedBySegment(self, x1,y1, x2,y2, filter)
          405  +  local cells, len = getCellsTouchedBySegment(self, x1,y1,x2,y2)
          406  +  local cell, rect, l,t,w,h, ti1,ti2, tii0,tii1
          407  +  local visited, itemInfo, itemInfoLen = {},{},0
          408  +  for i=1,len do
          409  +    cell = cells[i]
          410  +    for item in pairs(cell.items) do
          411  +      if not visited[item] then
          412  +        visited[item]  = true
          413  +        if (not filter or filter(item)) then
          414  +          rect           = self.rects[item]
          415  +          l,t,w,h        = rect.x,rect.y,rect.w,rect.h
          416  +
          417  +          ti1,ti2 = rect_getSegmentIntersectionIndices(l,t,w,h, x1,y1, x2,y2, 0, 1)
          418  +          if ti1 and ((0 < ti1 and ti1 < 1) or (0 < ti2 and ti2 < 1)) then
          419  +            -- the sorting is according to the t of an infinite line, not the segment
          420  +            tii0,tii1    = rect_getSegmentIntersectionIndices(l,t,w,h, x1,y1, x2,y2, -math.huge, math.huge)
          421  +            itemInfoLen  = itemInfoLen + 1
          422  +            itemInfo[itemInfoLen] = {item = item, ti1 = ti1, ti2 = ti2, weight = min(tii0,tii1)}
          423  +          end
          424  +        end
          425  +      end
          426  +    end
          427  +  end
          428  +  table.sort(itemInfo, sortByWeight)
          429  +  return itemInfo, itemInfoLen
          430  +end
          431  +
          432  +local function getResponseByName(self, name)
          433  +  local response = self.responses[name]
          434  +  if not response then
          435  +    error(('Unknown collision type: %s (%s)'):format(name, type(name)))
          436  +  end
          437  +  return response
          438  +end
          439  +
          440  +
          441  +-- Misc Public Methods
          442  +
          443  +function World:addResponse(name, response)
          444  +  self.responses[name] = response
          445  +end
          446  +
          447  +function World:project(item, x,y,w,h, goalX, goalY, filter)
          448  +  assertIsRect(x,y,w,h)
          449  +
          450  +  goalX = goalX or x
          451  +  goalY = goalY or y
          452  +  filter  = filter  or defaultFilter
          453  +
          454  +  local collisions, len = {}, 0
          455  +
          456  +  local visited = {}
          457  +  if item ~= nil then visited[item] = true end
          458  +
          459  +  -- This could probably be done with less cells using a polygon raster over the cells instead of a
          460  +  -- bounding rect of the whole movement. Conditional to building a queryPolygon method
          461  +  local tl, tt = min(goalX, x),       min(goalY, y)
          462  +  local tr, tb = max(goalX + w, x+w), max(goalY + h, y+h)
          463  +  local tw, th = tr-tl, tb-tt
          464  +
          465  +  local cl,ct,cw,ch = grid_toCellRect(self.cellSize, tl,tt,tw,th)
          466  +
          467  +  local dictItemsInCellRect = getDictItemsInCellRect(self, cl,ct,cw,ch)
          468  +
          469  +  for other,_ in pairs(dictItemsInCellRect) do
          470  +    if not visited[other] then
          471  +      visited[other] = true
          472  +
          473  +      local responseName = filter(item, other)
          474  +      if responseName then
          475  +        local ox,oy,ow,oh   = self:getRect(other)
          476  +        local col           = rect_detectCollision(x,y,w,h, ox,oy,ow,oh, goalX, goalY)
          477  +
          478  +        if col then
          479  +          col.other    = other
          480  +          col.item     = item
          481  +          col.type     = responseName
          482  +
          483  +          len = len + 1
          484  +          collisions[len] = col
          485  +        end
          486  +      end
          487  +    end
          488  +  end
          489  +
          490  +  table.sort(collisions, sortByTiAndDistance)
          491  +
          492  +  return collisions, len
          493  +end
          494  +
          495  +function World:countCells()
          496  +  local count = 0
          497  +  for _,row in pairs(self.rows) do
          498  +    for _,_ in pairs(row) do
          499  +      count = count + 1
          500  +    end
          501  +  end
          502  +  return count
          503  +end
          504  +
          505  +function World:hasItem(item)
          506  +  return not not self.rects[item]
          507  +end
          508  +
          509  +function World:getItems()
          510  +  local items, len = {}, 0
          511  +  for item,_ in pairs(self.rects) do
          512  +    len = len + 1
          513  +    items[len] = item
          514  +  end
          515  +  return items, len
          516  +end
          517  +
          518  +function World:countItems()
          519  +  local len = 0
          520  +  for _ in pairs(self.rects) do len = len + 1 end
          521  +  return len
          522  +end
          523  +
          524  +function World:getRect(item)
          525  +  local rect = self.rects[item]
          526  +  if not rect then
          527  +    error('Item ' .. tostring(item) .. ' must be added to the world before getting its rect. Use world:add(item, x,y,w,h) to add it first.')
          528  +  end
          529  +  return rect.x, rect.y, rect.w, rect.h
          530  +end
          531  +
          532  +function World:toWorld(cx, cy)
          533  +  return grid_toWorld(self.cellSize, cx, cy)
          534  +end
          535  +
          536  +function World:toCell(x,y)
          537  +  return grid_toCell(self.cellSize, x, y)
          538  +end
          539  +
          540  +
          541  +--- Query methods
          542  +
          543  +function World:queryRect(x,y,w,h, filter)
          544  +
          545  +  assertIsRect(x,y,w,h)
          546  +
          547  +  local cl,ct,cw,ch = grid_toCellRect(self.cellSize, x,y,w,h)
          548  +  local dictItemsInCellRect = getDictItemsInCellRect(self, cl,ct,cw,ch)
          549  +
          550  +  local items, len = {}, 0
          551  +
          552  +  local rect
          553  +  for item,_ in pairs(dictItemsInCellRect) do
          554  +    rect = self.rects[item]
          555  +    if (not filter or filter(item))
          556  +    and rect_isIntersecting(x,y,w,h, rect.x, rect.y, rect.w, rect.h)
          557  +    then
          558  +      len = len + 1
          559  +      items[len] = item
          560  +    end
          561  +  end
          562  +
          563  +  return items, len
          564  +end
          565  +
          566  +function World:queryPoint(x,y, filter)
          567  +  local cx,cy = self:toCell(x,y)
          568  +  local dictItemsInCellRect = getDictItemsInCellRect(self, cx,cy,1,1)
          569  +
          570  +  local items, len = {}, 0
          571  +
          572  +  local rect
          573  +  for item,_ in pairs(dictItemsInCellRect) do
          574  +    rect = self.rects[item]
          575  +    if (not filter or filter(item))
          576  +    and rect_containsPoint(rect.x, rect.y, rect.w, rect.h, x, y)
          577  +    then
          578  +      len = len + 1
          579  +      items[len] = item
          580  +    end
          581  +  end
          582  +
          583  +  return items, len
          584  +end
          585  +
          586  +function World:querySegment(x1, y1, x2, y2, filter)
          587  +  local itemInfo, len = getInfoAboutItemsTouchedBySegment(self, x1, y1, x2, y2, filter)
          588  +  local items = {}
          589  +  for i=1, len do
          590  +    items[i] = itemInfo[i].item
          591  +  end
          592  +  return items, len
          593  +end
          594  +
          595  +function World:querySegmentWithCoords(x1, y1, x2, y2, filter)
          596  +  local itemInfo, len = getInfoAboutItemsTouchedBySegment(self, x1, y1, x2, y2, filter)
          597  +  local dx, dy        = x2-x1, y2-y1
          598  +  local info, ti1, ti2
          599  +  for i=1, len do
          600  +    info  = itemInfo[i]
          601  +    ti1   = info.ti1
          602  +    ti2   = info.ti2
          603  +
          604  +    info.weight  = nil
          605  +    info.x1      = x1 + dx * ti1
          606  +    info.y1      = y1 + dy * ti1
          607  +    info.x2      = x1 + dx * ti2
          608  +    info.y2      = y1 + dy * ti2
          609  +  end
          610  +  return itemInfo, len
          611  +end
          612  +
          613  +
          614  +--- Main methods
          615  +
          616  +function World:add(item, x,y,w,h)
          617  +  local rect = self.rects[item]
          618  +  if rect then
          619  +    error('Item ' .. tostring(item) .. ' added to the world twice.')
          620  +  end
          621  +  assertIsRect(x,y,w,h)
          622  +
          623  +  self.rects[item] = {x=x,y=y,w=w,h=h}
          624  +
          625  +  local cl,ct,cw,ch = grid_toCellRect(self.cellSize, x,y,w,h)
          626  +  for cy = ct, ct+ch-1 do
          627  +    for cx = cl, cl+cw-1 do
          628  +      addItemToCell(self, item, cx, cy)
          629  +    end
          630  +  end
          631  +
          632  +  return item
          633  +end
          634  +
          635  +function World:remove(item)
          636  +  local x,y,w,h = self:getRect(item)
          637  +
          638  +  self.rects[item] = nil
          639  +  local cl,ct,cw,ch = grid_toCellRect(self.cellSize, x,y,w,h)
          640  +  for cy = ct, ct+ch-1 do
          641  +    for cx = cl, cl+cw-1 do
          642  +      removeItemFromCell(self, item, cx, cy)
          643  +    end
          644  +  end
          645  +end
          646  +
          647  +function World:update(item, x2,y2,w2,h2)
          648  +  local x1,y1,w1,h1 = self:getRect(item)
          649  +  w2,h2 = w2 or w1, h2 or h1
          650  +  assertIsRect(x2,y2,w2,h2)
          651  +
          652  +  if x1 ~= x2 or y1 ~= y2 or w1 ~= w2 or h1 ~= h2 then
          653  +
          654  +    local cellSize = self.cellSize
          655  +    local cl1,ct1,cw1,ch1 = grid_toCellRect(cellSize, x1,y1,w1,h1)
          656  +    local cl2,ct2,cw2,ch2 = grid_toCellRect(cellSize, x2,y2,w2,h2)
          657  +
          658  +    if cl1 ~= cl2 or ct1 ~= ct2 or cw1 ~= cw2 or ch1 ~= ch2 then
          659  +
          660  +      local cr1, cb1 = cl1+cw1-1, ct1+ch1-1
          661  +      local cr2, cb2 = cl2+cw2-1, ct2+ch2-1
          662  +      local cyOut
          663  +
          664  +      for cy = ct1, cb1 do
          665  +        cyOut = cy < ct2 or cy > cb2
          666  +        for cx = cl1, cr1 do
          667  +          if cyOut or cx < cl2 or cx > cr2 then
          668  +            removeItemFromCell(self, item, cx, cy)
          669  +          end
          670  +        end
          671  +      end
          672  +
          673  +      for cy = ct2, cb2 do
          674  +        cyOut = cy < ct1 or cy > cb1
          675  +        for cx = cl2, cr2 do
          676  +          if cyOut or cx < cl1 or cx > cr1 then
          677  +            addItemToCell(self, item, cx, cy)
          678  +          end
          679  +        end
          680  +      end
          681  +
          682  +    end
          683  +
          684  +    local rect = self.rects[item]
          685  +    rect.x, rect.y, rect.w, rect.h = x2,y2,w2,h2
          686  +
          687  +  end
          688  +end
          689  +
          690  +function World:move(item, goalX, goalY, filter)
          691  +  local actualX, actualY, cols, len = self:check(item, goalX, goalY, filter)
          692  +
          693  +  self:update(item, actualX, actualY)
          694  +
          695  +  return actualX, actualY, cols, len
          696  +end
          697  +
          698  +function World:check(item, goalX, goalY, filter)
          699  +  filter = filter or defaultFilter
          700  +
          701  +  local visited = {[item] = true}
          702  +  local visitedFilter = function(itm, other)
          703  +    if visited[other] then return false end
          704  +    return filter(itm, other)
          705  +  end
          706  +
          707  +  local cols, len = {}, 0
          708  +
          709  +  local x,y,w,h = self:getRect(item)
          710  +
          711  +  local projected_cols, projected_len = self:project(item, x,y,w,h, goalX,goalY, visitedFilter)
          712  +
          713  +  while projected_len > 0 do
          714  +    local col = projected_cols[1]
          715  +    len       = len + 1
          716  +    cols[len] = col
          717  +
          718  +    visited[col.other] = true
          719  +
          720  +    local response = getResponseByName(self, col.type)
          721  +
          722  +    goalX, goalY, projected_cols, projected_len = response(
          723  +      self,
          724  +      col,
          725  +      x, y, w, h,
          726  +      goalX, goalY,
          727  +      visitedFilter
          728  +    )
          729  +  end
          730  +
          731  +  return goalX, goalY, cols, len
          732  +end
          733  +
          734  +
          735  +-- Public library functions
          736  +
          737  +bump.newWorld = function(cellSize)
          738  +  cellSize = cellSize or 64
          739  +  assertIsPositiveNumber(cellSize, 'cellSize')
          740  +  local world = setmetatable({
          741  +    cellSize       = cellSize,
          742  +    rects          = {},
          743  +    rows           = {},
          744  +    nonEmptyCells  = {},
          745  +    responses = {}
          746  +  }, World_mt)
          747  +
          748  +  world:addResponse('touch', touch)
          749  +  world:addResponse('cross', cross)
          750  +  world:addResponse('slide', slide)
          751  +  world:addResponse('bounce', bounce)
          752  +
          753  +  return world
          754  +end
          755  +
          756  +bump.rect = {
          757  +  getNearestCorner              = rect_getNearestCorner,
          758  +  getSegmentIntersectionIndices = rect_getSegmentIntersectionIndices,
          759  +  getDiff                       = rect_getDiff,
          760  +  containsPoint                 = rect_containsPoint,
          761  +  isIntersecting                = rect_isIntersecting,
          762  +  getSquareDistance             = rect_getSquareDistance,
          763  +  detectCollision               = rect_detectCollision
          764  +}
          765  +
          766  +bump.responses = {
          767  +  touch  = touch,
          768  +  cross  = cross,
          769  +  slide  = slide,
          770  +  bounce = bounce
          771  +}
          772  +
          773  +return bump

Added src/worpt/lib/fennel.

            1  +#!/usr/bin/env lua
            2  +
            3  +local fennel_dir = arg[0]:match("(.-)[^\\/]+$")
            4  +package.path = fennel_dir .. "?.lua;" .. package.path
            5  +local fennel = require('fennel')
            6  +local unpack = unpack or table.unpack
            7  +
            8  +local help = [[
            9  +Usage: fennel [FLAG] [FILE]
           10  +
           11  +Run fennel, a lisp programming language for the Lua runtime.
           12  +
           13  +  --repl                  : Command to launch an interactive repl session
           14  +  --compile FILES         : Command to compile files and write Lua to stdout
           15  +  --eval SOURCE (-e)      : Command to evaluate source code and print the result
           16  +
           17  +  --no-searcher           : Skip installing package.searchers entry
           18  +  --indent VAL            : Indent compiler output with VAL
           19  +  --add-package-path PATH : Add PATH to package.path for finding Lua modules
           20  +  --add-fennel-path  PATH : Add PATH to fennel.path for finding Fennel modules
           21  +  --globals G1[,G2...]    : Allow these globals in addition to standard ones
           22  +  --globals-only G1[,G2]  : Same as above, but exclude standard ones
           23  +  --check-unused-locals   : Raise error when compiling code with unused locals
           24  +  --require-as-include    : Inline required modules in the output
           25  +  --metadata              : Enable function metadata, even in compiled output
           26  +  --no-metadata           : Disable function metadata, even in REPL
           27  +  --correlate             : Make Lua output line numbers match Fennel input
           28  +  --load FILE (-l)        : Load the specified FILE before executing the command
           29  +  --no-fennelrc           : Skip loading ~/.fennelrc when launching repl
           30  +
           31  +  --help (-h)             : Display this text
           32  +  --version (-v)          : Show version
           33  +
           34  +  Metadata is typically considered a development feature and is not recommended
           35  +  for production. It is used for docstrings and enabled by default in the REPL.
           36  +
           37  +  When not given a command, runs the file given as the first argument.
           38  +  When given neither command nor file, launches a repl.
           39  +
           40  +  If ~/.fennelrc exists, loads it before launching a repl.]]
           41  +
           42  +local options = {}
           43  +
           44  +local function dosafe(filename, opts, args)
           45  +    local ok, val = xpcall(function()
           46  +        return fennel.dofile(filename, opts, unpack(args))
           47  +    end, fennel.traceback)
           48  +    if not ok then
           49  +        io.stderr:write(val .. "\n")
           50  +        os.exit(1)
           51  +    end
           52  +    return val
           53  +end
           54  +
           55  +local function allowGlobals(globalNames)
           56  +    options.allowedGlobals = {}
           57  +    for g in globalNames:gmatch("([^,]+),?") do
           58  +        table.insert(options.allowedGlobals, g)
           59  +    end
           60  +end
           61  +
           62  +for i=#arg, 1, -1 do
           63  +    if arg[i] == "--no-searcher" then
           64  +        options.no_searcher = true
           65  +        table.remove(arg, i)
           66  +    elseif arg[i] == "--indent" then
           67  +        options.indent = table.remove(arg, i+1)
           68  +        if options.indent == "false" then options.indent = false end
           69  +        table.remove(arg, i)
           70  +    elseif arg[i] == "--add-package-path" then
           71  +        local entry = table.remove(arg, i+1)
           72  +        package.path = entry .. ";" .. package.path
           73  +        table.remove(arg, i)
           74  +    elseif arg[i] == "--add-fennel-path" then
           75  +        local entry = table.remove(arg, i+1)
           76  +        fennel.path = entry .. ";" .. fennel.path
           77  +        table.remove(arg, i)
           78  +    elseif arg[i] == "--load" or arg[i] == "-l" then
           79  +        local file = table.remove(arg, i+1)
           80  +        dosafe(file, options, {})
           81  +        table.remove(arg, i)
           82  +    elseif arg[i] == "--no-fennelrc" then
           83  +        options.fennelrc = false
           84  +        table.remove(arg, i)
           85  +    elseif arg[i] == "--correlate" then
           86  +        options.correlate = true
           87  +        table.remove(arg, i)
           88  +    elseif arg[i] == "--check-unused-locals" then
           89  +        options.checkUnusedLocals = true
           90  +        table.remove(arg, i)
           91  +    elseif arg[i] == "--globals" then
           92  +        allowGlobals(table.remove(arg, i+1))
           93  +        for globalName in pairs(_G) do
           94  +            table.insert(options.allowedGlobals, globalName)
           95  +        end
           96  +        table.remove(arg, i)
           97  +    elseif arg[i] == "--globals-only" then
           98  +        allowGlobals(table.remove(arg, i+1))
           99  +        table.remove(arg, i)
          100  +    elseif arg[i] == "--require-as-include" then
          101  +        options.requireAsInclude = true
          102  +        table.remove(arg, i)
          103  +    elseif arg[i] == "--metadata" then
          104  +        options.useMetadata = true
          105  +        table.remove(arg, i)
          106  +    elseif arg[i] == "--no-metadata" then
          107  +        options.useMetadata = false
          108  +        table.remove(arg, i)
          109  +    end
          110  +end
          111  +
          112  +if not options.no_searcher then
          113  +    local opts = {}
          114  +    for k,v in pairs(options) do opts[k] = v end
          115  +    table.insert((package.loaders or package.searchers), fennel.make_searcher(opts))
          116  +end
          117  +
          118  +-- Try to load readline library
          119  +local function tryReadline(opts)
          120  +    local ok, readline = pcall(require, "readline")
          121  +    if ok then
          122  +        readline.set_options({
          123  +            keeplines = 1000
          124  +        })
          125  +        function opts.readChunk(parserState)
          126  +            local prompt = parserState.stackSize > 0 and '.. ' or '>> '
          127  +            local str = readline.readline(prompt)
          128  +            if str then
          129  +                return str .. "\n"
          130  +            end
          131  +        end
          132  +
          133  +        -- completer is registered by the repl, until then returns empty list
          134  +        local completer
          135  +        function opts.registerCompleter(replCompleter)
          136  +          completer = replCompleter
          137  +        end
          138  +        local function replCompleter(text, from, to)
          139  +          if completer then
          140  +            -- stop completed syms from adding extra space on match
          141  +            readline.set_completion_append_character('')
          142  +            return completer(text:sub(from, to))
          143  +          else
          144  +            return {}
          145  +          end
          146  +        end
          147  +        readline.set_complete_function(replCompleter)
          148  +      end
          149  +end
          150  +
          151  +if arg[1] == "--repl" or #arg == 0 then
          152  +    local ppok, pp = pcall(fennel.dofile, fennel_dir .. "fennelview.fnl", options)
          153  +    if ppok then
          154  +        options.pp = pp
          155  +    else
          156  +        ppok, pp = pcall(require, "fennelview")
          157  +        if ppok then
          158  +            options.pp = pp
          159  +        end
          160  +    end
          161  +
          162  +    tryReadline(options)
          163  +
          164  +    if options.fennelrc ~= false then
          165  +        local home = os.getenv("HOME")
          166  +        local xdg_config_home = os.getenv("XDG_CONFIG_HOME") or home .. "/.config"
          167  +        local xdg_initfile = xdg_config_home .. "/fennel/fennelrc"
          168  +        local home_initfile = home .. "/.fennelrc"
          169  +        local init, initFilename = io.open(xdg_initfile, "rb"), xdg_initfile
          170  +        if not init then
          171  +            init, initFilename = io.open(home_initfile, "rb"), home_initfile
          172  +        end
          173  +
          174  +        if init then
          175  +            init:close()
          176  +            -- pass in options so fennerlrc can make changes to it
          177  +            dosafe(initFilename, options, {options})
          178  +        end
          179  +    end
          180  +
          181  +    print("Welcome to Fennel " .. fennel.version .. "!")
          182  +    if options.useMetadata ~= false then
          183  +        print("Use (doc something) to view documentation.")
          184  +    end
          185  +    fennel.repl(options)
          186  +elseif arg[1] == "--compile" then
          187  +    for i = 2, #arg do
          188  +        local f = arg[i] == "-" and io.stdin or assert(io.open(arg[i], "rb"))
          189  +        options.filename=arg[i]
          190  +        local ok, val = xpcall(function()
          191  +            return fennel.compileString(f:read("*all"), options)
          192  +        end, fennel.traceback)
          193  +        if ok then
          194  +            print(val)
          195  +        else
          196  +            io.stderr:write(val .. "\n")
          197  +            os.exit(1)
          198  +        end
          199  +        f:close()
          200  +    end
          201  +elseif arg[1] == "--eval" or arg[1] == "-e" then
          202  +   if arg[2] and arg[2] ~= "-" then
          203  +      print(fennel.eval(arg[2], options))
          204  +   else
          205  +      local source = io.stdin:read("*a")
          206  +      print(fennel.eval(source, options))
          207  +   end
          208  +elseif arg[1] == "--version" or arg[1] == "-v" then
          209  +    print("Fennel " .. fennel.version)
          210  +elseif #arg >= 1 and arg[1] ~= "--help" and arg[1] ~= "-h" then
          211  +    local filename = table.remove(arg, 1) -- let the script have remaining args
          212  +    if filename == "-" then
          213  +       local source = io.stdin:read("*a")
          214  +       fennel.eval(source, options)
          215  +    else
          216  +       dosafe(filename, options, arg)
          217  +    end
          218  +else
          219  +    print(help)
          220  +end

Added src/worpt/lib/fennel.lua.

            1  +--[[
            2  +Copyright (c) 2016-2019 Calvin Rose and contributors
            3  +Permission is hereby granted, free of charge, to any person obtaining a copy of
            4  +this software and associated documentation files (the "Software"), to deal in
            5  +the Software without restriction, including without limitation the rights to
            6  +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
            7  +the Software, and to permit persons to whom the Software is furnished to do so,
            8  +subject to the following conditions:
            9  +The above copyright notice and this permission notice shall be included in all
           10  +copies or substantial portions of the Software.
           11  +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
           12  +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
           13  +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
           14  +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
           15  +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
           16  +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
           17  +]]
           18  +
           19  +-- Make global variables local.
           20  +local setmetatable = setmetatable
           21  +local getmetatable = getmetatable
           22  +local type = type
           23  +local assert = assert
           24  +local pairs = pairs
           25  +local ipairs = ipairs
           26  +local tostring = tostring
           27  +local unpack = unpack or table.unpack
           28  +
           29  +--
           30  +-- Main Types and support functions
           31  +--
           32  +
           33  +-- Map function f over sequential table t, removing values where f returns nil.
           34  +-- Optionally takes a target table to insert the mapped values into.
           35  +local function map(t, f, out)
           36  +    out = out or {}
           37  +    if type(f) ~= "function" then local s = f f = function(x) return x[s] end end
           38  +    for _,x in ipairs(t) do
           39  +        local v = f(x)
           40  +        if v then table.insert(out, v) end
           41  +    end
           42  +    return out
           43  +end
           44  +
           45  +-- Map function f over key/value table t, similar to above, but it can return a
           46  +-- sequential table if f returns a single value or a k/v table if f returns two.
           47  +-- Optionally takes a target table to insert the mapped values into.
           48  +local function kvmap(t, f, out)
           49  +    out = out or {}
           50  +    if type(f) ~= "function" then local s = f f = function(x) return x[s] end end
           51  +    for k,x in pairs(t) do
           52  +        local korv, v = f(k, x)
           53  +        if korv and not v then table.insert(out, korv) end
           54  +        if korv and v then out[korv] = v end
           55  +    end
           56  +    return out
           57  +end
           58  +
           59  +local function deref(self) return self[1] end
           60  +
           61  +local function listToString(self, tostring2)
           62  +    return '(' .. table.concat(map(self, tostring2 or tostring), ' ', 1, #self) .. ')'
           63  +end
           64  +
           65  +local SYMBOL_MT = { 'SYMBOL', __tostring = deref, __fennelview = deref }
           66  +local EXPR_MT = { 'EXPR', __tostring = deref }
           67  +local VARARG = setmetatable({ '...' }, { 'VARARG', __tostring = deref })
           68  +local LIST_MT = { 'LIST', __tostring = listToString, __fennelview = listToString }
           69  +local SEQUENCE_MT = { 'SEQUENCE' }
           70  +
           71  +-- Load code with an environment in all recent Lua versions
           72  +local function loadCode(code, environment, filename)
           73  +    environment = environment or _ENV or _G
           74  +    if setfenv and loadstring then
           75  +        local f = assert(loadstring(code, filename))
           76  +        setfenv(f, environment)
           77  +        return f
           78  +    else
           79  +        return assert(load(code, filename, "t", environment))
           80  +    end
           81  +end
           82  +
           83  +-- Create a new list. Lists are a compile-time construct in Fennel; they are
           84  +-- represented as tables with a special marker metatable. They only come from
           85  +-- the parser, and they represent code which comes from reading a paren form;
           86  +-- they are specifically not cons cells.
           87  +local function list(...)
           88  +    return setmetatable({...}, LIST_MT)
           89  +end
           90  +
           91  +-- Create a new symbol. Symbols are a compile-time construct in Fennel and are
           92  +-- not exposed outside the compiler. Symbols have metadata describing what file,
           93  +-- line, etc that they came from.
           94  +local function sym(str, scope, meta)
           95  +    local s = {str, scope = scope}
           96  +    for k, v in pairs(meta or {}) do
           97  +        if type(k) == 'string' then s[k] = v end
           98  +    end
           99  +    return setmetatable(s, SYMBOL_MT)
          100  +end
          101  +
          102  +-- Create a new sequence. Sequences are tables that come from the parser when
          103  +-- it encounters a form with square brackets. They are treated as regular tables
          104  +-- except when certain macros need to look for binding forms, etc specifically.
          105  +local function sequence(...)
          106  +   return setmetatable({...}, SEQUENCE_MT)
          107  +end
          108  +
          109  +-- Create a new expr
          110  +-- etype should be one of
          111  +--   "literal", -- literals like numbers, strings, nil, true, false
          112  +--   "expression", -- Complex strings of Lua code, may have side effects, etc, but is an expression
          113  +--   "statement", -- Same as expression, but is also a valid statement (function calls).
          114  +--   "vargs", -- varargs symbol
          115  +--   "sym", -- symbol reference
          116  +local function expr(strcode, etype)
          117  +    return setmetatable({ strcode, type = etype }, EXPR_MT)
          118  +end
          119  +
          120  +local function varg()
          121  +    return VARARG
          122  +end
          123  +
          124  +local function isVarg(x)
          125  +    return x == VARARG and x
          126  +end
          127  +
          128  +-- Checks if an object is a List. Returns the object if is a List.
          129  +local function isList(x)
          130  +    return type(x) == 'table' and getmetatable(x) == LIST_MT and x
          131  +end
          132  +
          133  +-- Checks if an object is a symbol. Returns the object if it is a symbol.
          134  +local function isSym(x)
          135  +    return type(x) == 'table' and getmetatable(x) == SYMBOL_MT and x
          136  +end
          137  +
          138  +-- Checks if an object any kind of table, EXCEPT list or symbol
          139  +local function isTable(x)
          140  +    return type(x) == 'table' and
          141  +        x ~= VARARG and
          142  +        getmetatable(x) ~= LIST_MT and getmetatable(x) ~= SYMBOL_MT and x
          143  +end
          144  +
          145  +-- Checks if an object is a sequence (created with a [] literal)
          146  +local function isSequence(x)
          147  +   return type(x) == 'table' and getmetatable(x) == SEQUENCE_MT and x
          148  +end
          149  +
          150  +-- Returns a shallow copy of its table argument. Returns an empty table on nil.
          151  +local function copy(from)
          152  +   local to = {}
          153  +   for k, v in pairs(from or {}) do to[k] = v end
          154  +   return to
          155  +end
          156  +
          157  +--
          158  +-- Parser
          159  +--
          160  +
          161  +-- Convert a stream of chunks to a stream of bytes.
          162  +-- Also returns a second function to clear the buffer in the byte stream
          163  +local function granulate(getchunk)
          164  +    local c = ''
          165  +    local index = 1
          166  +    local done = false
          167  +    return function (parserState)
          168  +        if done then return nil end
          169  +        if index <= #c then
          170  +            local b = c:byte(index)
          171  +            index = index + 1
          172  +            return b
          173  +        else
          174  +            c = getchunk(parserState)
          175  +            if not c or c == '' then
          176  +                done = true
          177  +                return nil
          178  +            end
          179  +            index = 2
          180  +            return c:byte(1)
          181  +        end
          182  +    end, function ()
          183  +        c = ''
          184  +    end
          185  +end
          186  +
          187  +-- Convert a string into a stream of bytes
          188  +local function stringStream(str)
          189  +    local index = 1
          190  +    return function()
          191  +        local r = str:byte(index)
          192  +        index = index + 1
          193  +        return r
          194  +    end
          195  +end
          196  +
          197  +-- Table of delimiter bytes - (, ), [, ], {, }
          198  +-- Opener keys have closer as the value, and closers keys
          199  +-- have true as their value.
          200  +local delims = {
          201  +    [40] = 41,        -- (
          202  +    [41] = true,      -- )
          203  +    [91] = 93,        -- [
          204  +    [93] = true,      -- ]
          205  +    [123] = 125,      -- {
          206  +    [125] = true      -- }
          207  +}
          208  +
          209  +local function iswhitespace(b)
          210  +    return b == 32 or (b >= 9 and b <= 13)
          211  +end
          212  +
          213  +local function issymbolchar(b)
          214  +    return b > 32 and
          215  +        not delims[b] and
          216  +        b ~= 127 and -- "<BS>"
          217  +        b ~= 34 and -- "\""
          218  +        b ~= 39 and -- "'"
          219  +        b ~= 126 and -- "~"
          220  +        b ~= 59 and -- ";"
          221  +        b ~= 44 and -- ","
          222  +        b ~= 64 and -- "@"
          223  +        b ~= 96 -- "`"
          224  +end
          225  +
          226  +local prefixes = { -- prefix chars substituted while reading
          227  +    [96] = 'quote', -- `
          228  +    [44] = 'unquote', -- ,
          229  +    [39] = 'quote', -- '
          230  +    [35] = 'hashfn' -- #
          231  +}
          232  +
          233  +-- The resetRoot function needs to be called at every exit point of the compiler
          234  +-- including when there's a parse error or compiler error. Introduce it up here
          235  +-- so error functions have access to it, and set it when we have values below.
          236  +local resetRoot = nil
          237  +
          238  +-- Parse one value given a function that
          239  +-- returns sequential bytes. Will throw an error as soon
          240  +-- as possible without getting more bytes on bad input. Returns
          241  +-- if a value was read, and then the value read. Will return nil
          242  +-- when input stream is finished.
          243  +local function parser(getbyte, filename)
          244  +
          245  +    -- Stack of unfinished values
          246  +    local stack = {}
          247  +
          248  +    -- Provide one character buffer and keep
          249  +    -- track of current line and byte index
          250  +    local line = 1
          251  +    local byteindex = 0
          252  +    local lastb
          253  +    local function ungetb(ub)
          254  +        if ub == 10 then line = line - 1 end
          255  +        byteindex = byteindex - 1
          256  +        lastb = ub
          257  +    end
          258  +    local function getb()
          259  +        local r
          260  +        if lastb then
          261  +            r, lastb = lastb, nil
          262  +        else
          263  +            r = getbyte({ stackSize = #stack })
          264  +        end
          265  +        byteindex = byteindex + 1
          266  +        if r == 10 then line = line + 1 end
          267  +        return r
          268  +    end
          269  +    local function parseError(msg)
          270  +        if resetRoot then resetRoot() end
          271  +        return error(msg .. ' in ' .. (filename or 'unknown') .. ':' .. line, 0)
          272  +    end
          273  +
          274  +    -- Parse stream
          275  +    return function()
          276  +
          277  +        -- Dispatch when we complete a value
          278  +        local done, retval
          279  +        local whitespaceSinceDispatch = true
          280  +        local function dispatch(v)
          281  +            if #stack == 0 then
          282  +                retval = v
          283  +                done = true
          284  +            elseif stack[#stack].prefix then
          285  +                local stacktop = stack[#stack]
          286  +                stack[#stack] = nil
          287  +                return dispatch(list(sym(stacktop.prefix), v))
          288  +            else
          289  +                table.insert(stack[#stack], v)
          290  +            end
          291  +            whitespaceSinceDispatch = false
          292  +        end
          293  +
          294  +        -- Throw nice error when we expect more characters
          295  +        -- but reach end of stream.
          296  +        local function badend()
          297  +            local accum = map(stack, "closer")
          298  +            parseError(('expected closing delimiter%s %s'):format(
          299  +                #stack == 1 and "" or "s",
          300  +                string.char(unpack(accum))))
          301  +        end
          302  +
          303  +        -- The main parse loop
          304  +        repeat
          305  +            local b
          306  +
          307  +            -- Skip whitespace
          308  +            repeat
          309  +                b = getb()
          310  +                if b and iswhitespace(b) then
          311  +                    whitespaceSinceDispatch = true
          312  +                end
          313  +            until not b or not iswhitespace(b)
          314  +            if not b then
          315  +                if #stack > 0 then badend() end
          316  +                return nil
          317  +            end
          318  +
          319  +            if b == 59 then -- ; Comment
          320  +                repeat
          321  +                    b = getb()
          322  +                until not b or b == 10 -- newline
          323  +            elseif type(delims[b]) == 'number' then -- Opening delimiter
          324  +                if not whitespaceSinceDispatch then
          325  +                    parseError('expected whitespace before opening delimiter '
          326  +                                   .. string.char(b))
          327  +                end
          328  +                table.insert(stack, setmetatable({
          329  +                    closer = delims[b],
          330  +                    line = line,
          331  +                    filename = filename,
          332  +                    bytestart = byteindex
          333  +                }, LIST_MT))
          334  +            elseif delims[b] then -- Closing delimiter
          335  +                if #stack == 0 then parseError('unexpected closing delimiter '
          336  +                                                   .. string.char(b)) end
          337  +                local last = stack[#stack]
          338  +                local val
          339  +                if last.closer ~= b then
          340  +                    parseError('unexpected delimiter ' .. string.char(b) ..
          341  +                               ', expected ' .. string.char(last.closer))
          342  +                end
          343  +                last.byteend = byteindex -- Set closing byte index
          344  +                if b == 41 then -- ; )
          345  +                    val = last
          346  +                elseif b == 93 then -- ; ]
          347  +                    val = sequence()
          348  +                    for i = 1, #last do
          349  +                        val[i] = last[i]
          350  +                    end
          351  +                else -- ; }
          352  +                    if #last % 2 ~= 0 then
          353  +                        parseError('expected even number of values in table literal')
          354  +                    end
          355  +                    val = {}
          356  +                    for i = 1, #last, 2 do
          357  +                        if(tostring(last[i]) == ":" and isSym(last[i + 1])
          358  +                           and isSym(last[i])) then
          359  +                            last[i] = tostring(last[i + 1])
          360  +                        end
          361  +                        val[last[i]] = last[i + 1]
          362  +                    end
          363  +                end
          364  +                stack[#stack] = nil
          365  +                dispatch(val)
          366  +            elseif b == 34 then -- Quoted string
          367  +                local state = "base"
          368  +                local chars = {34}
          369  +                stack[#stack + 1] = {closer = 34}
          370  +                repeat
          371  +                    b = getb()
          372  +                    chars[#chars + 1] = b
          373  +                    if state == "base" then
          374  +                        if b == 92 then
          375  +                            state = "backslash"
          376  +                        elseif b == 34 then
          377  +                            state = "done"
          378  +                        end
          379  +                    else
          380  +                        -- state == "backslash"
          381  +                        state = "base"
          382  +                    end
          383  +                until not b or (state == "done")
          384  +                if not b then badend() end
          385  +                stack[#stack] = nil
          386  +                local raw = string.char(unpack(chars))
          387  +                local formatted = raw:gsub("[\1-\31]", function (c) return '\\' .. c:byte() end)
          388  +                local loadFn = loadCode(('return %s'):format(formatted), nil, filename)
          389  +                dispatch(loadFn())
          390  +            elseif prefixes[b] then
          391  +                -- expand prefix byte into wrapping form eg. '`a' into '(quote a)'
          392  +                table.insert(stack, {
          393  +                    prefix = prefixes[b]
          394  +                })
          395  +                local nextb = getb()
          396  +                if iswhitespace(nextb) then
          397  +                    if b == 35 then
          398  +                        stack[#stack] = nil
          399  +                        dispatch(sym('#'))
          400  +                    else
          401  +                        parseError('invalid whitespace after quoting prefix')
          402  +                    end
          403  +                end
          404  +                ungetb(nextb)
          405  +            elseif issymbolchar(b) or b == string.byte("~") then -- Try symbol
          406  +                local chars = {}
          407  +                local bytestart = byteindex
          408  +                repeat
          409  +                    chars[#chars + 1] = b
          410  +                    b = getb()
          411  +                until not b or not issymbolchar(b)
          412  +                if b then ungetb(b) end
          413  +                local rawstr = string.char(unpack(chars))
          414  +                if rawstr == 'true' then dispatch(true)
          415  +                elseif rawstr == 'false' then dispatch(false)
          416  +                elseif rawstr == '...' then dispatch(VARARG)
          417  +                elseif rawstr:match('^:.+$') then -- colon style strings
          418  +                    dispatch(rawstr:sub(2))
          419  +                elseif rawstr:match("^~") and rawstr ~= "~=" then
          420  +                    -- for backwards-compatibility, special-case allowance of ~=
          421  +                    -- but all other uses of ~ are disallowed
          422  +                    parseError("illegal character: ~")
          423  +                else
          424  +                    local forceNumber = rawstr:match('^%d')
          425  +                    local numberWithStrippedUnderscores = rawstr:gsub("_", "")
          426  +                    local x
          427  +                    if forceNumber then
          428  +                        x = tonumber(numberWithStrippedUnderscores) or
          429  +                            parseError('could not read token "' .. rawstr .. '"')
          430  +                    else
          431  +                        x = tonumber(numberWithStrippedUnderscores) or
          432  +                            (rawstr:match("%.[0-9]") and
          433  +                                 parseError("can't start multisym segment " ..
          434  +                                                "with digit: ".. rawstr)) or
          435  +                            ((rawstr:match(":%.") or
          436  +                                  rawstr:match("%.:") or
          437  +                                  rawstr:match("::") or
          438  +                                  (rawstr:match("%.%.") and rawstr ~= "..")) and
          439  +                                    parseError("malformed multisym: " .. rawstr)) or
          440  +                            (rawstr:match(":.+:") and
          441  +                                 parseError("method call must be last component " ..
          442  +                                                "of multisym: " .. rawstr)) or
          443  +                            sym(rawstr, nil, { line = line,
          444  +                                               filename = filename,
          445  +                                               bytestart = bytestart,
          446  +                                               byteend = byteindex, })
          447  +                    end
          448  +                    dispatch(x)
          449  +                end
          450  +            else
          451  +                parseError("illegal character: " .. string.char(b))
          452  +            end
          453  +        until done
          454  +        return true, retval
          455  +    end, function ()
          456  +        stack = {}
          457  +    end
          458  +end
          459  +
          460  +--
          461  +-- Compilation
          462  +--
          463  +
          464  +-- Top level compilation bindings.
          465  +local rootChunk, rootScope, rootOptions
          466  +
          467  +local function setResetRoot(oldChunk, oldScope, oldOptions)
          468  +    local oldResetRoot = resetRoot -- this needs to nest!
          469  +    resetRoot = function()
          470  +        rootChunk, rootScope, rootOptions = oldChunk, oldScope, oldOptions
          471  +        resetRoot = oldResetRoot
          472  +    end
          473  +end
          474  +
          475  +local GLOBAL_SCOPE
          476  +
          477  +-- Create a new Scope, optionally under a parent scope. Scopes are compile time
          478  +-- constructs that are responsible for keeping track of local variables, name
          479  +-- mangling, and macros.  They are accessible to user code via the
          480  +-- 'eval-compiler' special form (may change). They use metatables to implement
          481  +-- nesting via metatables.
          482  +local function makeScope(parent)
          483  +    if not parent then parent = GLOBAL_SCOPE end
          484  +    return {
          485  +        unmanglings = setmetatable({}, {
          486  +            __index = parent and parent.unmanglings
          487  +        }),
          488  +        manglings = setmetatable({}, {
          489  +            __index = parent and parent.manglings
          490  +        }),
          491  +        specials = setmetatable({}, {
          492  +            __index = parent and parent.specials
          493  +        }),
          494  +        symmeta = setmetatable({}, {
          495  +            __index = parent and parent.symmeta
          496  +        }),
          497  +        includes = setmetatable({}, {
          498  +            __index = parent and parent.includes
          499  +        }),
          500  +        refedglobals = setmetatable({}, {
          501  +            __index = parent and parent.refedglobals
          502  +        }),
          503  +        autogensyms = {},
          504  +        parent = parent,
          505  +        vararg = parent and parent.vararg,
          506  +        depth = parent and ((parent.depth or 0) + 1) or 0,
          507  +        hashfn = parent and parent.hashfn
          508  +    }
          509  +end
          510  +
          511  +-- Assert a condition and raise a compile error with line numbers. The ast arg
          512  +-- should be unmodified so that its first element is the form being called.
          513  +local function assertCompile(condition, msg, ast)
          514  +    -- if we use regular `assert' we can't provide the `level' argument of zero
          515  +    if not condition then
          516  +        if resetRoot then resetRoot() end
          517  +        error(string.format("Compile error in '%s' %s:%s: %s",
          518  +                            isSym(ast[1]) and ast[1][1] or ast[1] or '()',
          519  +                            ast.filename or "unknown", ast.line or '?', msg), 0)
          520  +    end
          521  +    return condition
          522  +end
          523  +
          524  +GLOBAL_SCOPE = makeScope()
          525  +GLOBAL_SCOPE.vararg = true
          526  +local SPECIALS = GLOBAL_SCOPE.specials
          527  +local COMPILER_SCOPE = makeScope(GLOBAL_SCOPE)
          528  +
          529  +local luaKeywords = {
          530  +    'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function',
          531  +    'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true',
          532  +    'until', 'while'
          533  +}
          534  +
          535  +for i, v in ipairs(luaKeywords) do luaKeywords[v] = i end
          536  +
          537  +local function isValidLuaIdentifier(str)
          538  +    return (str:match('^[%a_][%w_]*$') and not luaKeywords[str])
          539  +end
          540  +
          541  +-- Allow printing a string to Lua, also keep as 1 line.
          542  +local serializeSubst = {
          543  +    ['\a'] = '\\a',
          544  +    ['\b'] = '\\b',
          545  +    ['\f'] = '\\f',
          546  +    ['\n'] = 'n',
          547  +    ['\t'] = '\\t',
          548  +    ['\v'] = '\\v'
          549  +}
          550  +local function serializeString(str)
          551  +    local s = ("%q"):format(str)
          552  +    s = s:gsub('.', serializeSubst):gsub("[\128-\255]", function(c)
          553  +        return "\\" .. c:byte()
          554  +    end)
          555  +    return s
          556  +end
          557  +
          558  +-- A multi symbol is a symbol that is actually composed of
          559  +-- two or more symbols using the dot syntax. The main differences
          560  +-- from normal symbols is that they cannot be declared local, and
          561  +-- they may have side effects on invocation (metatables)
          562  +local function isMultiSym(str)
          563  +    if isSym(str) then
          564  +        return isMultiSym(tostring(str))
          565  +    end
          566  +    if type(str) ~= 'string' then return end
          567  +    local parts = {}
          568  +    for part in str:gmatch('[^%.%:]+[%.%:]?') do
          569  +        local lastChar = part:sub(-1)
          570  +        if lastChar == ":" then
          571  +            parts.multiSymMethodCall = true
          572  +        end
          573  +        if lastChar == ":" or lastChar == "." then
          574  +            parts[#parts + 1] = part:sub(1, -2)
          575  +        else
          576  +            parts[#parts + 1] = part
          577  +        end
          578  +    end
          579  +    return #parts > 0 and
          580  +        (str:match('%.') or str:match(':')) and
          581  +        (not str:match('%.%.')) and
          582  +        str:byte() ~= string.byte '.' and
          583  +        str:byte(-1) ~= string.byte '.' and
          584  +        parts
          585  +end
          586  +
          587  +local function isQuoted(symbol) return symbol.quoted end
          588  +
          589  +-- Mangler for global symbols. Does not protect against collisions,
          590  +-- but makes them unlikely. This is the mangling that is exposed to
          591  +-- to the world.
          592  +local function globalMangling(str)
          593  +    if isValidLuaIdentifier(str) then
          594  +        return str
          595  +    end
          596  +    -- Use underscore as escape character
          597  +    return '__fnl_global__' .. str:gsub('[^%w]', function (c)
          598  +        return ('_%02x'):format(c:byte())
          599  +    end)
          600  +end
          601  +
          602  +-- Reverse a global mangling. Takes a Lua identifier and
          603  +-- returns the fennel symbol string that created it.
          604  +local function globalUnmangling(identifier)
          605  +    local rest = identifier:match('^__fnl_global__(.*)$')
          606  +    if rest then
          607  +        local r = rest:gsub('_[%da-f][%da-f]', function (code)
          608  +            return string.char(tonumber(code:sub(2), 16))
          609  +        end)
          610  +        return r -- don't return multiple values
          611  +    else
          612  +        return identifier
          613  +    end
          614  +end
          615  +
          616  +-- If there's a provided list of allowed globals, don't let references thru that
          617  +-- aren't on the list. This list is set at the compiler entry points of compile
          618  +-- and compileStream.
          619  +local allowedGlobals
          620  +
          621  +local function globalAllowed(name)
          622  +    if not allowedGlobals then return true end
          623  +    for _, g in ipairs(allowedGlobals) do
          624  +        if g == name then return true end
          625  +    end
          626  +end
          627  +
          628  +-- Creates a symbol from a string by mangling it.
          629  +-- ensures that the generated symbol is unique
          630  +-- if the input string is unique in the scope.
          631  +local function localMangling(str, scope, ast, tempManglings)
          632  +    local append = 0
          633  +    local mangling = str
          634  +    assertCompile(not isMultiSym(str), 'did not expect multi symbol ' .. str, ast)
          635  +
          636  +    -- Mapping mangling to a valid Lua identifier
          637  +    if luaKeywords[mangling] or mangling:match('^%d') then
          638  +        mangling = '_' .. mangling
          639  +    end
          640  +    mangling = mangling:gsub('-', '_')
          641  +    mangling = mangling:gsub('[^%w_]', function (c)
          642  +        return ('_%02x'):format(c:byte())
          643  +    end)
          644  +
          645  +    -- Prevent name collisions with existing symbols
          646  +    local raw = mangling
          647  +    while scope.unmanglings[mangling] do
          648  +        mangling = raw .. append
          649  +        append = append + 1
          650  +    end
          651  +
          652  +    scope.unmanglings[mangling] = str
          653  +    local manglings = tempManglings or scope.manglings
          654  +    manglings[str] = mangling
          655  +    return mangling
          656  +end
          657  +
          658  +-- Calling this function will mean that further
          659  +-- compilation in scope will use these new manglings
          660  +-- instead of the current manglings.
          661  +local function applyManglings(scope, newManglings, ast)
          662  +    for raw, mangled in pairs(newManglings) do
          663  +        assertCompile(not scope.refedglobals[mangled],
          664  +        "use of global " .. raw .. " is aliased by a local", ast)
          665  +        scope.manglings[raw] = mangled
          666  +    end
          667  +end
          668  +
          669  +-- Combine parts of a symbol
          670  +local function combineParts(parts, scope)
          671  +    local ret = scope.manglings[parts[1]] or globalMangling(parts[1])
          672  +    for i = 2, #parts do
          673  +        if isValidLuaIdentifier(parts[i]) then
          674  +            if parts.multiSymMethodCall and i == #parts then
          675  +                ret = ret .. ':' .. parts[i]
          676  +            else
          677  +                ret = ret .. '.' .. parts[i]
          678  +            end
          679  +        else
          680  +            ret = ret .. '[' .. serializeString(parts[i]) .. ']'
          681  +        end
          682  +    end
          683  +    return ret
          684  +end
          685  +
          686  +-- Generates a unique symbol in the scope.
          687  +local function gensym(scope, base)
          688  +    local mangling
          689  +    local append = 0
          690  +    repeat
          691  +        mangling = (base or '') .. '_' .. append .. '_'
          692  +        append = append + 1
          693  +    until not scope.unmanglings[mangling]
          694  +    scope.unmanglings[mangling] = true
          695  +    return mangling
          696  +end
          697  +
          698  +-- Generates a unique symbol in the scope based on the base name. Calling
          699  +-- repeatedly with the same base and same scope will return existing symbol
          700  +-- rather than generating new one.
          701  +local function autogensym(base, scope)
          702  +    if scope.autogensyms[base] then return scope.autogensyms[base] end
          703  +    local mangling = gensym(scope, base)
          704  +    scope.autogensyms[base] = mangling
          705  +    return mangling
          706  +end
          707  +
          708  +-- Check if a binding is valid
          709  +local function checkBindingValid(symbol, scope, ast)
          710  +    -- Check if symbol will be over shadowed by special
          711  +    local name = symbol[1]
          712  +    assertCompile(not scope.specials[name],
          713  +    ("symbol %s may be overshadowed by a special form or macro"):format(name), ast)
          714  +    assertCompile(not isQuoted(symbol), 'macro tried to bind ' .. name ..
          715  +                      " without gensym; try ' .. name .. '# instead", ast)
          716  +
          717  +end
          718  +
          719  +-- Declare a local symbol
          720  +local function declareLocal(symbol, meta, scope, ast, tempManglings)
          721  +    checkBindingValid(symbol, scope, ast)
          722  +    local name = symbol[1]
          723  +    assertCompile(not isMultiSym(name), "did not expect mutltisym", ast)
          724  +    local mangling = localMangling(name, scope, ast, tempManglings)
          725  +    scope.symmeta[name] = meta
          726  +    return mangling
          727  +end
          728  +
          729  +-- Convert symbol to Lua code. Will only work for local symbols
          730  +-- if they have already been declared via declareLocal
          731  +local function symbolToExpression(symbol, scope, isReference)
          732  +    local name = symbol[1]
          733  +    local multiSymParts = isMultiSym(name)
          734  +    if scope.hashfn then
          735  +       if name == '$' then name = '$1' end
          736  +       if multiSymParts then
          737  +          if multiSymParts[1] == "$" then
          738  +             multiSymParts[1] = "$1"
          739  +             name = table.concat(multiSymParts, ".")
          740  +          end
          741  +       end
          742  +    end
          743  +    local parts = multiSymParts or {name}
          744  +    local etype = (#parts > 1) and "expression" or "sym"
          745  +    local isLocal = scope.manglings[parts[1]]
          746  +    if isLocal and scope.symmeta[parts[1]] then scope.symmeta[parts[1]].used = true end
          747  +    -- if it's a reference and not a symbol which introduces a new binding
          748  +    -- then we need to check for allowed globals
          749  +    assertCompile(not isReference or isLocal or globalAllowed(parts[1]),
          750  +                  'unknown global in strict mode: ' .. parts[1], symbol)
          751  +    if not isLocal then
          752  +        rootScope.refedglobals[parts[1]] = true
          753  +    end
          754  +    return expr(combineParts(parts, scope), etype)
          755  +end
          756  +
          757  +
          758  +-- Emit Lua code
          759  +local function emit(chunk, out, ast)
          760  +    if type(out) == 'table' then
          761  +        table.insert(chunk, out)
          762  +    else
          763  +        table.insert(chunk, {leaf = out, ast = ast})
          764  +    end
          765  +end
          766  +
          767  +-- Do some peephole optimization.
          768  +local function peephole(chunk)
          769  +    if chunk.leaf then return chunk end
          770  +    -- Optimize do ... end in some cases.
          771  +    if #chunk == 3 and
          772  +        chunk[1].leaf == 'do' and
          773  +        not chunk[2].leaf and
          774  +        chunk[3].leaf == 'end' then
          775  +        return peephole(chunk[2])
          776  +    end
          777  +    -- Recurse
          778  +    return map(chunk, peephole)
          779  +end
          780  +
          781  +-- correlate line numbers in input with line numbers in output
          782  +local function flattenChunkCorrelated(mainChunk)
          783  +    local function flatten(chunk, out, lastLine, file)
          784  +        if chunk.leaf then
          785  +            out[lastLine] = (out[lastLine] or "") .. " " .. chunk.leaf
          786  +        else
          787  +            for _, subchunk in ipairs(chunk) do
          788  +                -- Ignore empty chunks
          789  +                if subchunk.leaf or #subchunk > 0 then
          790  +                    -- don't increase line unless it's from the same file
          791  +                    if subchunk.ast and file == subchunk.ast.file then
          792  +                        lastLine = math.max(lastLine, subchunk.ast.line or 0)
          793  +                    end
          794  +                    lastLine = flatten(subchunk, out, lastLine, file)
          795  +                end
          796  +            end
          797  +        end
          798  +        return lastLine
          799  +    end
          800  +    local out = {}
          801  +    local last = flatten(mainChunk, out, 1, mainChunk.file)
          802  +    for i = 1, last do
          803  +        if out[i] == nil then out[i] = "" end
          804  +    end
          805  +    return table.concat(out, "\n")
          806  +end
          807  +
          808  +-- Flatten a tree of indented Lua source code lines.
          809  +-- Tab is what is used to indent a block.
          810  +local function flattenChunk(sm, chunk, tab, depth)
          811  +    if type(tab) == 'boolean' then tab = tab and '  ' or '' end
          812  +    if chunk.leaf then
          813  +        local code = chunk.leaf
          814  +        local info = chunk.ast
          815  +        -- Just do line info for now to save memory
          816  +        if sm then sm[#sm + 1] = info and info.line or -1 end
          817  +        return code
          818  +    else
          819  +        local parts = map(chunk, function(c)
          820  +            if c.leaf or #c > 0 then -- Ignore empty chunks
          821  +                local sub = flattenChunk(sm, c, tab, depth + 1)
          822  +                if depth > 0 then sub = tab .. sub:gsub('\n', '\n' .. tab) end
          823  +                return sub
          824  +            end
          825  +        end)
          826  +        return table.concat(parts, '\n')
          827  +    end
          828  +end
          829  +
          830  +-- Some global state for all fennel sourcemaps. For the time being,
          831  +-- this seems the easiest way to store the source maps.
          832  +-- Sourcemaps are stored with source being mapped as the key, prepended
          833  +-- with '@' if it is a filename (like debug.getinfo returns for source).
          834  +-- The value is an array of mappings for each line.
          835  +local fennelSourcemap = {}
          836  +-- TODO: loading, unloading, and saving sourcemaps?
          837  +
          838  +local function makeShortSrc(source)
          839  +    source = source:gsub('\n', ' ')
          840  +    if #source <= 49 then
          841  +        return '[fennel "' .. source .. '"]'
          842  +    else
          843  +        return '[fennel "' .. source:sub(1, 46) .. '..."]'
          844  +    end
          845  +end
          846  +
          847  +-- Return Lua source and source map table
          848  +local function flatten(chunk, options)
          849  +    chunk = peephole(chunk)
          850  +    if(options.correlate) then
          851  +        return flattenChunkCorrelated(chunk), {}
          852  +    else
          853  +        local sm = {}
          854  +        local ret = flattenChunk(sm, chunk, options.indent, 0)
          855  +        if sm then
          856  +            local key, short_src
          857  +            if options.filename then
          858  +                short_src = options.filename
          859  +                key = '@' .. short_src
          860  +            else
          861  +                key = ret
          862  +                short_src = makeShortSrc(options.source or ret)
          863  +            end
          864  +            sm.short_src = short_src
          865  +            sm.key = key
          866  +            fennelSourcemap[key] = sm
          867  +        end
          868  +        return ret, sm
          869  +    end
          870  +end
          871  +
          872  +-- module-wide state for metadata
          873  +-- create metadata table with weakly-referenced keys
          874  +local function makeMetadata()
          875  +    return setmetatable({}, {
          876  +        __mode = 'k',
          877  +        __index = {
          878  +            get = function(self, tgt, key)
          879  +                if self[tgt] then return self[tgt][key] end
          880  +            end,
          881  +            set = function(self, tgt, key, value)
          882  +                self[tgt] = self[tgt] or {}
          883  +                self[tgt][key] = value
          884  +                return tgt
          885  +            end,
          886  +            setall = function(self, tgt, ...)
          887  +                local kvLen, kvs = select('#', ...), {...}
          888  +                if kvLen % 2 ~= 0 then
          889  +                    error('metadata:setall() expected even number of k/v pairs')
          890  +                end
          891  +                self[tgt] = self[tgt] or {}
          892  +                for i = 1, kvLen, 2 do self[tgt][kvs[i]] = kvs[i + 1] end
          893  +                return tgt
          894  +            end,
          895  +        }})
          896  +end
          897  +
          898  +local metadata = makeMetadata()
          899  +local doc = function(tgt, name)
          900  +    if(not tgt) then return name .. " not found" end
          901  +    local docstring = (metadata:get(tgt, 'fnl/docstring') or
          902  +                           '#<undocumented>'):gsub('\n$', ''):gsub('\n', '\n  ')
          903  +    if type(tgt) == "function" then
          904  +        local arglist = table.concat(metadata:get(tgt, 'fnl/arglist') or
          905  +                                         {'#<unknown-arguments>'}, ' ')
          906  +        return string.format("(%s%s%s)\n  %s", name, #arglist > 0 and ' ' or '',
          907  +                             arglist, docstring)
          908  +    else
          909  +        return string.format("%s\n  %s", name, docstring)
          910  +    end
          911  +end
          912  +
          913  +local function docSpecial(name, arglist, docstring)
          914  +    metadata[SPECIALS[name]] =
          915  +        { ["fnl/docstring"] = docstring, ["fnl/arglist"] = arglist }
          916  +end
          917  +
          918  +-- Convert expressions to Lua string
          919  +local function exprs1(exprs)
          920  +    return table.concat(map(exprs, 1), ', ')
          921  +end
          922  +
          923  +-- Compile side effects for a chunk
          924  +local function keepSideEffects(exprs, chunk, start, ast)
          925  +    start = start or 1
          926  +    for j = start, #exprs do
          927  +        local se = exprs[j]
          928  +        -- Avoid the rogue 'nil' expression (nil is usually a literal,
          929  +        -- but becomes an expression if a special form
          930  +        -- returns 'nil'.)
          931  +        if se.type == 'expression' and se[1] ~= 'nil' then
          932  +            emit(chunk, ('do local _ = %s end'):format(tostring(se)), ast)
          933  +        elseif se.type == 'statement' then
          934  +            local code = tostring(se)
          935  +            emit(chunk, code:byte() == 40 and ("do end " .. code) or code , ast)
          936  +        end
          937  +    end
          938  +end
          939  +
          940  +-- Does some common handling of returns and register
          941  +-- targets for special forms. Also ensures a list expression
          942  +-- has an acceptable number of expressions if opts contains the
          943  +-- "nval" option.
          944  +local function handleCompileOpts(exprs, parent, opts, ast)
          945  +    if opts.nval then
          946  +        local n = opts.nval
          947  +        if n ~= #exprs then
          948  +            local len = #exprs
          949  +            if len > n then
          950  +                -- Drop extra
          951  +                keepSideEffects(exprs, parent, n + 1, ast)
          952  +                for i = n + 1, len do
          953  +                    exprs[i] = nil
          954  +                end
          955  +            else
          956  +                -- Pad with nils
          957  +                for i = #exprs + 1, n do
          958  +                    exprs[i] = expr('nil', 'literal')
          959  +                end
          960  +            end
          961  +        end
          962  +    end
          963  +    if opts.tail then
          964  +        emit(parent, ('return %s'):format(exprs1(exprs)), ast)
          965  +    end
          966  +    if opts.target then
          967  +        emit(parent, ('%s = %s'):format(opts.target, exprs1(exprs)), ast)
          968  +    end
          969  +    if opts.tail or opts.target then
          970  +        -- Prevent statements and expression from being used twice if they
          971  +        -- have side-effects. Since if the target or tail options are set,
          972  +        -- the expressions are already emitted, we should not return them. This
          973  +        -- is fine, as when these options are set, the caller doesn't need the result
          974  +        -- anyways.
          975  +        exprs = {}
          976  +    end
          977  +    return exprs
          978  +end
          979  +
          980  +-- Compile an AST expression in the scope into parent, a tree
          981  +-- of lines that is eventually compiled into Lua code. Also
          982  +-- returns some information about the evaluation of the compiled expression,
          983  +-- which can be used by the calling function. Macros
          984  +-- are resolved here, as well as special forms in that order.
          985  +-- the 'ast' param is the root AST to compile
          986  +-- the 'scope' param is the scope in which we are compiling
          987  +-- the 'parent' param is the table of lines that we are compiling into.
          988  +-- add lines to parent by appending strings. Add indented blocks by appending
          989  +-- tables of more lines.
          990  +-- the 'opts' param contains info about where the form is being compiled.
          991  +-- Options include:
          992  +--   'target' - mangled name of symbol(s) being compiled to.
          993  +--      Could be one variable, 'a', or a list, like 'a, b, _0_'.
          994  +--   'tail' - boolean indicating tail position if set. If set, form will generate a return
          995  +--   instruction.
          996  +--   'nval' - The number of values to compile to if it is known to be a fixed value.
          997  +
          998  +-- In Lua, an expression can evaluate to 0 or more values via multiple
          999  +-- returns. In many cases, Lua will drop extra values and convert a 0 value
         1000  +-- expression to nil. In other cases, Lua will use all of the values in an
         1001  +-- expression, such as in the last argument of a function call. Nval is an
         1002  +-- option passed to compile1 to say that the resulting expression should have
         1003  +-- at least n values. It lets us generate better code, because if we know we
         1004  +-- are only going to use 1 or 2 values from an expression, we can create 1 or 2
         1005  +-- locals to store intermediate results rather than turn the expression into a
         1006  +-- closure that is called immediately, which we have to do if we don't know.
         1007  +
         1008  +local function compile1(ast, scope, parent, opts)
         1009  +    opts = opts or {}
         1010  +    local exprs = {}
         1011  +
         1012  +    -- Compile the form
         1013  +    if isList(ast) then
         1014  +        -- Function call or special form
         1015  +        local len = #ast
         1016  +        assertCompile(len > 0, "expected a function to call", ast)
         1017  +        -- Test for special form
         1018  +        local first = ast[1]
         1019  +        if isSym(first) then -- Resolve symbol
         1020  +            first = first[1]
         1021  +        end
         1022  +        local multiSymParts = isMultiSym(first)
         1023  +        local special = scope.specials[first]
         1024  +        if special and isSym(ast[1]) then
         1025  +            -- Special form
         1026  +            exprs = special(ast, scope, parent, opts) or expr('nil', 'literal')
         1027  +            -- Be very accepting of strings or expression
         1028  +            -- as well as lists or expressions
         1029  +            if type(exprs) == 'string' then exprs = expr(exprs, 'expression') end
         1030  +            if getmetatable(exprs) == EXPR_MT then exprs = {exprs} end
         1031  +            -- Unless the special form explicitly handles the target, tail, and
         1032  +            -- nval properties, (indicated via the 'returned' flag), handle
         1033  +            -- these options.
         1034  +            if not exprs.returned then
         1035  +                exprs = handleCompileOpts(exprs, parent, opts, ast)
         1036  +            elseif opts.tail or opts.target then
         1037  +                exprs = {}
         1038  +            end
         1039  +            exprs.returned = true
         1040  +            return exprs
         1041  +        elseif multiSymParts and multiSymParts.multiSymMethodCall then
         1042  +            local tableWithMethod = table.concat({
         1043  +                    unpack(multiSymParts, 1, #multiSymParts - 1)
         1044  +                                                 }, '.')
         1045  +            local methodToCall = multiSymParts[#multiSymParts]
         1046  +            local newAST = list(sym(':', scope), sym(tableWithMethod, scope), methodToCall)
         1047  +            for i = 2, len do
         1048  +                newAST[#newAST + 1] = ast[i]
         1049  +            end
         1050  +            local compiled = compile1(newAST, scope, parent, opts)
         1051  +            exprs = compiled
         1052  +        else
         1053  +            -- Function call
         1054  +            local fargs = {}
         1055  +            local fcallee = compile1(ast[1], scope, parent, {
         1056  +                nval = 1
         1057  +            })[1]
         1058  +            assertCompile(fcallee.type ~= 'literal',
         1059  +                          'cannot call literal value', ast)
         1060  +            fcallee = tostring(fcallee)
         1061  +            for i = 2, len do
         1062  +                local subexprs = compile1(ast[i], scope, parent, {
         1063  +                    nval = i ~= len and 1 or nil
         1064  +                })
         1065  +                fargs[#fargs + 1] = subexprs[1] or expr('nil', 'literal')
         1066  +                if i == len then
         1067  +                    -- Add sub expressions to function args
         1068  +                    for j = 2, #subexprs do
         1069  +                        fargs[#fargs + 1] = subexprs[j]
         1070  +                    end
         1071  +                else
         1072  +                    -- Emit sub expression only for side effects
         1073  +                    keepSideEffects(subexprs, parent, 2, ast[i])
         1074  +                end
         1075  +            end
         1076  +            local call = ('%s(%s)'):format(tostring(fcallee), exprs1(fargs))
         1077  +            exprs = handleCompileOpts({expr(call, 'statement')}, parent, opts, ast)
         1078  +        end
         1079  +    elseif isVarg(ast) then
         1080  +        assertCompile(scope.vararg, "unexpected vararg", ast)
         1081  +        exprs = handleCompileOpts({expr('...', 'varg')}, parent, opts, ast)
         1082  +    elseif isSym(ast) then
         1083  +        local e
         1084  +        local multiSymParts = isMultiSym(ast)
         1085  +        assertCompile(not (multiSymParts and multiSymParts.multiSymMethodCall),
         1086  +                      "multisym method calls may only be in call position", ast)
         1087  +        -- Handle nil as special symbol - it resolves to the nil literal rather than
         1088  +        -- being unmangled. Alternatively, we could remove it from the lua keywords table.
         1089  +        if ast[1] == 'nil' then
         1090  +            e = expr('nil', 'literal')
         1091  +        else
         1092  +            e = symbolToExpression(ast, scope, true)
         1093  +        end
         1094  +        exprs = handleCompileOpts({e}, parent, opts, ast)
         1095  +    elseif type(ast) == 'nil' or type(ast) == 'boolean' then
         1096  +        exprs = handleCompileOpts({expr(tostring(ast), 'literal')}, parent, opts)
         1097  +    elseif type(ast) == 'number' then
         1098  +        local n = ('%.17g'):format(ast)
         1099  +        exprs = handleCompileOpts({expr(n, 'literal')}, parent, opts)
         1100  +    elseif type(ast) == 'string' then
         1101  +        local s = serializeString(ast)
         1102  +        exprs = handleCompileOpts({expr(s, 'literal')}, parent, opts)
         1103  +    elseif type(ast) == 'table' then
         1104  +        local buffer = {}
         1105  +        for i = 1, #ast do -- Write numeric keyed values.
         1106  +            local nval = i ~= #ast and 1
         1107  +            buffer[#buffer + 1] = exprs1(compile1(ast[i], scope, parent, {nval = nval}))
         1108  +        end
         1109  +        local function writeOtherValues(k)
         1110  +            if type(k) ~= 'number' or math.floor(k) ~= k or k < 1 or k > #ast then
         1111  +                if type(k) == 'string' and isValidLuaIdentifier(k) then
         1112  +                    return {k, k}
         1113  +                else
         1114  +                    local kstr = '[' .. tostring(compile1(k, scope, parent,
         1115  +                                                          {nval = 1})[1]) .. ']'
         1116  +                    return { kstr, k }
         1117  +                end
         1118  +            end
         1119  +        end
         1120  +        local keys = kvmap(ast, writeOtherValues)
         1121  +        table.sort(keys, function (a, b) return a[1] < b[1] end)
         1122  +        map(keys, function(k)
         1123  +                local v = tostring(compile1(ast[k[2]], scope, parent, {nval = 1})[1])
         1124  +                return ('%s = %s'):format(k[1], v) end,
         1125  +            buffer)
         1126  +        local tbl = '{' .. table.concat(buffer, ', ') ..'}'
         1127  +        exprs = handleCompileOpts({expr(tbl, 'expression')}, parent, opts, ast)
         1128  +    else
         1129  +        assertCompile(false, 'could not compile value of type ' .. type(ast), ast)
         1130  +    end
         1131  +    exprs.returned = true
         1132  +    return exprs
         1133  +end
         1134  +
         1135  +-- SPECIALS --
         1136  +
         1137  +-- For statements and expressions, put the value in a local to avoid
         1138  +-- double-evaluating it.
         1139  +local function once(val, ast, scope, parent)
         1140  +    if val.type == 'statement' or val.type == 'expression' then
         1141  +        local s = gensym(scope)
         1142  +        emit(parent, ('local %s = %s'):format(s, tostring(val)), ast)
         1143  +        return expr(s, 'sym')
         1144  +    else
         1145  +        return val
         1146  +    end
         1147  +end
         1148  +
         1149  +-- Implements destructuring for forms like let, bindings, etc.
         1150  +-- Takes a number of options to control behavior.
         1151  +-- var: Whether or not to mark symbols as mutable
         1152  +-- declaration: begin each assignment with 'local' in output
         1153  +-- nomulti: disallow multisyms in the destructuring. Used for (local) and (global).
         1154  +-- noundef: Don't set undefined bindings. (set)
         1155  +-- forceglobal: Don't allow local bindings
         1156  +local function destructure(to, from, ast, scope, parent, opts)
         1157  +    opts = opts or {}
         1158  +    local isvar = opts.isvar
         1159  +    local declaration = opts.declaration
         1160  +    local nomulti = opts.nomulti
         1161  +    local noundef = opts.noundef
         1162  +    local forceglobal = opts.forceglobal
         1163  +    local forceset = opts.forceset
         1164  +    local setter = declaration and "local %s = %s" or "%s = %s"
         1165  +
         1166  +    local newManglings = {}
         1167  +
         1168  +    -- Get Lua source for symbol, and check for errors
         1169  +    local function getname(symbol, up1)
         1170  +        local raw = symbol[1]
         1171  +        assertCompile(not (nomulti and isMultiSym(raw)),
         1172  +            'did not expect multisym', up1)
         1173  +        if declaration then
         1174  +            return declareLocal(symbol, {var = isvar}, scope, symbol, newManglings)
         1175  +        else
         1176  +            local parts = isMultiSym(raw) or {raw}
         1177  +            local meta = scope.symmeta[parts[1]]
         1178  +            if #parts == 1 and not forceset then
         1179  +                assertCompile(not(forceglobal and meta),
         1180  +                    'expected global, found var', up1)
         1181  +                assertCompile(meta or not noundef,
         1182  +                    'expected local var ' .. parts[1], up1)
         1183  +                assertCompile(not (meta and not meta.var),
         1184  +                    'expected local var', up1)
         1185  +            end
         1186  +            if forceglobal then
         1187  +                assertCompile(not scope.symmeta[scope.unmanglings[raw]],
         1188  +                              "global " .. raw .. " conflicts with local", ast)
         1189  +                scope.manglings[raw] = globalMangling(raw)
         1190  +                scope.unmanglings[globalMangling(raw)] = raw
         1191  +                if allowedGlobals then
         1192  +                    table.insert(allowedGlobals, raw)
         1193  +                end
         1194  +            end
         1195  +
         1196  +            return symbolToExpression(symbol, scope)[1]
         1197  +        end
         1198  +    end
         1199  +
         1200  +    -- Compile the outer most form. We can generate better Lua in this case.
         1201  +    local function compileTopTarget(lvalues)
         1202  +        -- Calculate initial rvalue
         1203  +        local inits = map(lvalues, function(x)
         1204  +                              return scope.manglings[x] and x or 'nil' end)
         1205  +        local init = table.concat(inits, ', ')
         1206  +        local lvalue = table.concat(lvalues, ', ')
         1207  +
         1208  +        local plen = #parent
         1209  +        local ret = compile1(from, scope, parent, {target = lvalue})
         1210  +        if declaration then
         1211  +            if #parent == plen + 1 and parent[#parent].leaf then
         1212  +                -- A single leaf emitted means an simple assignment a = x was emitted
         1213  +                parent[#parent].leaf = 'local ' .. parent[#parent].leaf
         1214  +            else
         1215  +                table.insert(parent, plen + 1, { leaf = 'local ' .. lvalue ..
         1216  +                                                     ' = ' .. init, ast = ast})
         1217  +            end
         1218  +        end
         1219  +        return ret
         1220  +    end
         1221  +
         1222  +    -- Recursive auxiliary function
         1223  +    local function destructure1(left, rightexprs, up1, top)
         1224  +        if isSym(left) and left[1] ~= "nil" then
         1225  +            checkBindingValid(left, scope, left)
         1226  +            local lname = getname(left, up1)
         1227  +            if top then
         1228  +                compileTopTarget({lname})
         1229  +            else
         1230  +                emit(parent, setter:format(lname, exprs1(rightexprs)), left)
         1231  +            end
         1232  +        elseif isTable(left) then -- table destructuring
         1233  +            if top then rightexprs = compile1(from, scope, parent) end
         1234  +            local s = gensym(scope)
         1235  +            emit(parent, ("local %s = %s"):format(s, exprs1(rightexprs)), left)
         1236  +            for k, v in pairs(left) do
         1237  +                if isSym(left[k]) and left[k][1] == "&" then
         1238  +                    assertCompile(type(k) == "number" and not left[k+2],
         1239  +                        "expected rest argument in final position", left)
         1240  +                    local subexpr = expr(('{(table.unpack or unpack)(%s, %s)}'):format(s, k),
         1241  +                        'expression')
         1242  +                    destructure1(left[k+1], {subexpr}, left)
         1243  +                    return
         1244  +                else
         1245  +                    if isSym(k) and tostring(k) == ":" and isSym(v) then k = tostring(v) end
         1246  +                    if type(k) ~= "number" then k = serializeString(k) end
         1247  +                    local subexpr = expr(('%s[%s]'):format(s, k), 'expression')
         1248  +                    destructure1(v, {subexpr}, left)
         1249  +                end
         1250  +            end
         1251  +        elseif isList(left) then -- values destructuring
         1252  +            local leftNames, tables = {}, {}
         1253  +            for i, name in ipairs(left) do
         1254  +                local symname
         1255  +                if isSym(name) then -- binding directly to a name
         1256  +                    symname = getname(name, up1)
         1257  +                else -- further destructuring of tables inside values
         1258  +                    symname = gensym(scope)
         1259  +                    tables[i] = {name, expr(symname, 'sym')}
         1260  +                end
         1261  +                table.insert(leftNames, symname)
         1262  +            end
         1263  +            if top then
         1264  +                compileTopTarget(leftNames)
         1265  +            else
         1266  +                local lvalue = table.concat(leftNames, ', ')
         1267  +                emit(parent, setter:format(lvalue, exprs1(rightexprs)), left)
         1268  +            end
         1269  +            for _, pair in pairs(tables) do -- recurse if left-side tables found
         1270  +                destructure1(pair[1], {pair[2]}, left)
         1271  +            end
         1272  +        else
         1273  +            assertCompile(false, 'unable to bind ' .. tostring(left), up1)
         1274  +        end
         1275  +        if top then return {returned = true} end
         1276  +    end
         1277  +
         1278  +    local ret = destructure1(to, nil, ast, true)
         1279  +    applyManglings(scope, newManglings, ast)
         1280  +    return ret
         1281  +end
         1282  +
         1283  +-- Unlike most expressions and specials, 'values' resolves with multiple
         1284  +-- values, one for each argument, allowing multiple return values. The last
         1285  +-- expression can return multiple arguments as well, allowing for more than the number
         1286  +-- of expected arguments.
         1287  +local function values(ast, scope, parent)
         1288  +    local len = #ast
         1289  +    local exprs = {}
         1290  +    for i = 2, len do
         1291  +        local subexprs = compile1(ast[i], scope, parent, {
         1292  +            nval = (i ~= len) and 1
         1293  +        })
         1294  +        exprs[#exprs + 1] = subexprs[1]
         1295  +        if i == len then
         1296  +            for j = 2, #subexprs do
         1297  +                exprs[#exprs + 1] = subexprs[j]
         1298  +            end
         1299  +        end
         1300  +    end
         1301  +    return exprs
         1302  +end
         1303  +
         1304  +-- Compile a list of forms for side effects
         1305  +local function compileDo(ast, scope, parent, start)
         1306  +    start = start or 2
         1307  +    local len = #ast
         1308  +    local subScope = makeScope(scope)
         1309  +    for i = start, len do
         1310  +        compile1(ast[i], subScope, parent, {
         1311  +            nval = 0
         1312  +        })
         1313  +    end
         1314  +end
         1315  +
         1316  +-- Raises compile error if unused locals are found and we're checking for them.
         1317  +local function checkUnused(scope, ast)
         1318  +    if not rootOptions.checkUnusedLocals then return end
         1319  +    for symName in pairs(scope.symmeta) do
         1320  +        assertCompile(scope.symmeta[symName].used or symName:find("^_"),
         1321  +                      ("unused local %s"):format(symName), ast)
         1322  +    end
         1323  +end
         1324  +
         1325  +-- Implements a do statement, starting at the 'start' element. By default, start is 2.
         1326  +local function doImpl(ast, scope, parent, opts, start, chunk, subScope)
         1327  +    start = start or 2
         1328  +    subScope = subScope or makeScope(scope)
         1329  +    chunk = chunk or {}
         1330  +    local len = #ast
         1331  +    local outerTarget = opts.target
         1332  +    local outerTail = opts.tail
         1333  +    local retexprs = {returned = true}
         1334  +
         1335  +    -- See if we need special handling to get the return values
         1336  +    -- of the do block
         1337  +    if not outerTarget and opts.nval ~= 0 and not outerTail then
         1338  +        if opts.nval then
         1339  +            -- Generate a local target
         1340  +            local syms = {}
         1341  +            for i = 1, opts.nval do
         1342  +                local s = gensym(scope)
         1343  +                syms[i] = s
         1344  +                retexprs[i] = expr(s, 'sym')
         1345  +            end
         1346  +            outerTarget = table.concat(syms, ', ')
         1347  +            emit(parent, ('local %s'):format(outerTarget), ast)
         1348  +            emit(parent, 'do', ast)
         1349  +        else
         1350  +            -- We will use an IIFE for the do
         1351  +            local fname = gensym(scope)
         1352  +            local fargs = scope.vararg and '...' or ''
         1353  +            emit(parent, ('local function %s(%s)'):format(fname, fargs), ast)
         1354  +            retexprs = expr(fname .. '(' .. fargs .. ')', 'statement')
         1355  +            outerTail = true
         1356  +            outerTarget = nil
         1357  +        end
         1358  +    else
         1359  +        emit(parent, 'do', ast)
         1360  +    end
         1361  +    -- Compile the body
         1362  +    if start > len then
         1363  +        -- In the unlikely case we do a do with no arguments.
         1364  +        compile1(nil, subScope, chunk, {
         1365  +            tail = outerTail,
         1366  +            target = outerTarget
         1367  +        })
         1368  +        -- There will be no side effects
         1369  +    else
         1370  +        for i = start, len do
         1371  +            local subopts = {
         1372  +                nval = i ~= len and 0 or opts.nval,
         1373  +                tail = i == len and outerTail or nil,
         1374  +                target = i == len and outerTarget or nil
         1375  +            }
         1376  +            local subexprs = compile1(ast[i], subScope, chunk, subopts)
         1377  +            if i ~= len then
         1378  +                keepSideEffects(subexprs, parent, nil, ast[i])
         1379  +            end
         1380  +        end
         1381  +    end
         1382  +    emit(parent, chunk, ast)
         1383  +    emit(parent, 'end', ast)
         1384  +    checkUnused(subScope, ast)
         1385  +    return retexprs
         1386  +end
         1387  +
         1388  +SPECIALS["do"] = doImpl
         1389  +docSpecial("do", {"..."}, "Evaluate multiple forms; return last value.")
         1390  +
         1391  +SPECIALS["values"] = values
         1392  +docSpecial("values", {"..."},
         1393  +           "Return multiple values from a function.  Must be in tail position.")
         1394  +
         1395  +-- The fn special declares a function. Syntax is similar to other lisps;
         1396  +-- (fn optional-name [arg ...] (body))
         1397  +-- Further decoration such as docstrings, meta info, and multibody functions a possibility.
         1398  +SPECIALS["fn"] = function(ast, scope, parent)
         1399  +    local fScope = makeScope(scope)
         1400  +    local fChunk = {}
         1401  +    local index = 2
         1402  +    local fnName = isSym(ast[index])
         1403  +    local isLocalFn
         1404  +    local docstring
         1405  +    fScope.vararg = false
         1406  +    local multi = fnName and isMultiSym(fnName[1])
         1407  +    assertCompile(not multi or not multi.multiSymMethodCall,
         1408  +                  "malformed function name: " .. tostring(fnName), ast)
         1409  +    if fnName and fnName[1] ~= 'nil' then
         1410  +        isLocalFn = not multi
         1411  +        if isLocalFn then
         1412  +            fnName = declareLocal(fnName, {}, scope, ast)
         1413  +        else
         1414  +            fnName = symbolToExpression(fnName, scope)[1]
         1415  +        end
         1416  +        index = index + 1
         1417  +    else
         1418  +        isLocalFn = true
         1419  +        fnName = gensym(scope)
         1420  +    end
         1421  +    local argList = assertCompile(isTable(ast[index]),
         1422  +                                  "expected vector arg list [a b ...]", ast)
         1423  +    local function getArgName(i, name)
         1424  +        if isVarg(name) then
         1425  +            assertCompile(i == #argList, "expected vararg as last parameter", ast)
         1426  +            fScope.vararg = true
         1427  +            return "..."
         1428  +        elseif(isSym(name) and deref(name) ~= "nil"
         1429  +               and not isMultiSym(deref(name))) then
         1430  +            return declareLocal(name, {}, fScope, ast)
         1431  +        elseif isTable(name) then
         1432  +            local raw = sym(gensym(scope))
         1433  +            local declared = declareLocal(raw, {}, fScope, ast)
         1434  +            destructure(name, raw, ast, fScope, fChunk,
         1435  +                        { declaration = true, nomulti = true })
         1436  +            return declared
         1437  +        else
         1438  +            assertCompile(false, "expected symbol for function parameter", ast)
         1439  +        end
         1440  +    end
         1441  +    local argNameList = kvmap(argList, getArgName)
         1442  +    if type(ast[index + 1]) == 'string' and index + 1 < #ast then
         1443  +        index = index + 1
         1444  +        docstring = ast[index]
         1445  +    end
         1446  +    for i = index + 1, #ast do
         1447  +        compile1(ast[i], fScope, fChunk, {
         1448  +            tail = i == #ast,
         1449  +            nval = i ~= #ast and 0 or nil,
         1450  +        })
         1451  +    end
         1452  +    if isLocalFn then
         1453  +        emit(parent, ('local function %s(%s)')
         1454  +                 :format(fnName, table.concat(argNameList, ', ')), ast)
         1455  +    else
         1456  +        emit(parent, ('%s = function(%s)')
         1457  +                 :format(fnName, table.concat(argNameList, ', ')), ast)
         1458  +    end
         1459  +
         1460  +    emit(parent, fChunk, ast)
         1461  +    emit(parent, 'end', ast)
         1462  +
         1463  +    if rootOptions.useMetadata then
         1464  +        local args = map(argList, function(v)
         1465  +            -- TODO: show destructured args properly instead of replacing
         1466  +            return isTable(v) and '"#<table>"' or string.format('"%s"', tostring(v))
         1467  +        end)
         1468  +
         1469  +        local metaFields = {
         1470  +            '"fnl/arglist"', '{' .. table.concat(args, ', ') .. '}',
         1471  +        }
         1472  +        if docstring then
         1473  +            table.insert(metaFields, '"fnl/docstring"')
         1474  +            table.insert(metaFields, '"' .. docstring:gsub('%s+$', '')
         1475  +                             :gsub('\\', '\\\\'):gsub('\n', '\\n')
         1476  +                             :gsub('"', '\\"') .. '"')
         1477  +        end
         1478  +        local metaStr = ('require("%s").metadata'):format(rootOptions.moduleName or "fennel")
         1479  +        emit(parent, string.format('pcall(function() %s:setall(%s, %s) end)',
         1480  +                                   metaStr, fnName, table.concat(metaFields, ', ')))
         1481  +    end
         1482  +
         1483  +    checkUnused(fScope, ast)
         1484  +    return expr(fnName, 'sym')
         1485  +end
         1486  +docSpecial("fn", {"name?", "args", "docstring?", "..."},
         1487  +           "Function syntax. May optionally include a name and docstring."
         1488  +               .."\nIf a name is provided, the function will be bound in the current scope."
         1489  +               .."\nWhen called with the wrong number of args, excess args will be discarded"
         1490  +               .."\nand lacking args will be nil, use lambda for arity-checked functions.")
         1491  +
         1492  +-- (lua "print('hello!')") -> prints hello, evaluates to nil
         1493  +-- (lua "print 'hello!'" "10") -> prints hello, evaluates to the number 10
         1494  +-- (lua nil "{1,2,3}") -> Evaluates to a table literal
         1495  +SPECIALS['lua'] = function(ast, _, parent)
         1496  +    assertCompile(#ast == 2 or #ast == 3,
         1497  +        "expected 2 or 3 arguments in 'lua' special form", ast)
         1498  +    if ast[2] ~= nil then
         1499  +        table.insert(parent, {leaf = tostring(ast[2]), ast = ast})
         1500  +    end
         1501  +    if #ast == 3 then
         1502  +        return tostring(ast[3])
         1503  +    end
         1504  +end
         1505  +
         1506  +SPECIALS['doc'] = function(ast, scope, parent)
         1507  +    assert(rootOptions.useMetadata, "can't look up doc with metadata disabled.")
         1508  +    assertCompile(#ast == 2, "expected one argument", ast)
         1509  +
         1510  +    local target = deref(ast[2])
         1511  +    local special = scope.specials[target]
         1512  +    if special then
         1513  +        return ("print([[%s]])"):format(doc(special, target))
         1514  +    else
         1515  +        local value = tostring(compile1(ast[2], scope, parent, {nval = 1})[1])
         1516  +        -- need to require here since the metadata is stored in the module
         1517  +        -- and we need to make sure we look it up in the same module it was
         1518  +        -- declared from.
         1519  +        return ("print(require('%s').doc(%s, '%s'))")
         1520  +            :format(rootOptions.moduleName or "fennel", value, tostring(ast[2]))
         1521  +    end
         1522  +end
         1523  +docSpecial("doc", {"x"},
         1524  +           "Print the docstring and arglist for a function, macro, or special form.")
         1525  +
         1526  +-- Table lookup
         1527  +SPECIALS["."] = function(ast, scope, parent)
         1528  +    local len = #ast
         1529  +    assertCompile(len > 1, "expected table argument", ast)
         1530  +    local lhs = compile1(ast[2], scope, parent, {nval = 1})
         1531  +    if len == 2 then
         1532  +        return tostring(lhs[1])
         1533  +    else
         1534  +        local indices = {}
         1535  +        for i = 3, len do
         1536  +            local index = ast[i]
         1537  +            if type(index) == 'string' and isValidLuaIdentifier(index) then
         1538  +                table.insert(indices, '.' .. index)
         1539  +            else
         1540  +                index = compile1(index, scope, parent, {nval = 1})[1]
         1541  +                table.insert(indices, '[' .. tostring(index) .. ']')
         1542  +            end
         1543  +        end
         1544  +        -- extra parens are needed for table literals
         1545  +        if isTable(ast[2]) then
         1546  +            return '(' .. tostring(lhs[1]) .. ')' .. table.concat(indices)
         1547  +        else
         1548  +            return tostring(lhs[1]) .. table.concat(indices)
         1549  +        end
         1550  +    end
         1551  +end
         1552  +docSpecial(".", {"tbl", "key1", "..."},
         1553  +           "Look up key1 in tbl table. If more args are provided, do a nested lookup.")
         1554  +
         1555  +SPECIALS["global"] = function(ast, scope, parent)
         1556  +    assertCompile(#ast == 3, "expected name and value", ast)
         1557  +    destructure(ast[2], ast[3], ast, scope, parent, {
         1558  +        nomulti = true,
         1559  +        forceglobal = true
         1560  +    })
         1561  +end
         1562  +docSpecial("global", {"name", "val"}, "Set name as a global with val.")
         1563  +
         1564  +SPECIALS["set"] = function(ast, scope, parent)
         1565  +    assertCompile(#ast == 3, "expected name and value", ast)
         1566  +    destructure(ast[2], ast[3], ast, scope, parent, {
         1567  +        noundef = true
         1568  +    })
         1569  +end
         1570  +docSpecial("set", {"name", "val"},
         1571  +           "Set a local variable to a new value. Only works on locals using var.")
         1572  +
         1573  +SPECIALS["set-forcibly!"] = function(ast, scope, parent)
         1574  +    assertCompile(#ast == 3, "expected name and value", ast)
         1575  +    destructure(ast[2], ast[3], ast, scope, parent, {
         1576  +        forceset = true
         1577  +    })
         1578  +end
         1579  +
         1580  +SPECIALS["local"] = function(ast, scope, parent)
         1581  +    assertCompile(#ast == 3, "expected name and value", ast)
         1582  +    destructure(ast[2], ast[3], ast, scope, parent, {
         1583  +        declaration = true,
         1584  +        nomulti = true
         1585  +    })
         1586  +end
         1587  +docSpecial("local", {"name", "val"},
         1588  +           "Introduce new top-level immutable local.")
         1589  +
         1590  +SPECIALS["var"] = function(ast, scope, parent)
         1591  +    assertCompile(#ast == 3, "expected name and value", ast)
         1592  +    destructure(ast[2], ast[3], ast, scope, parent, {
         1593  +        declaration = true,
         1594  +        nomulti = true,
         1595  +        isvar = true
         1596  +    })
         1597  +end
         1598  +docSpecial("var", {"name", "val"},
         1599  +           "Introduce new mutable local.")
         1600  +
         1601  +SPECIALS["let"] = function(ast, scope, parent, opts)
         1602  +    local bindings = ast[2]
         1603  +    assertCompile(isList(bindings) or isTable(bindings),
         1604  +                  "expected table for destructuring", ast)
         1605  +    assertCompile(#bindings % 2 == 0,
         1606  +                  "expected even number of name/value bindings", ast)
         1607  +    assertCompile(#ast >= 3, "missing body expression", ast)
         1608  +    local subScope = makeScope(scope)
         1609  +    local subChunk = {}
         1610  +    for i = 1, #bindings, 2 do
         1611  +        destructure(bindings[i], bindings[i + 1], ast, subScope, subChunk, {
         1612  +            declaration = true,
         1613  +            nomulti = true
         1614  +        })
         1615  +    end
         1616  +    return doImpl(ast, scope, parent, opts, 3, subChunk, subScope)
         1617  +end
         1618  +docSpecial("let", {"[name1 val1 ... nameN valN]", "..."},
         1619  +           "Introduces a new scope in which a given set of local bindings are used.")
         1620  +
         1621  +-- For setting items in a table
         1622  +SPECIALS["tset"] = function(ast, scope, parent)
         1623  +    assertCompile(#ast > 3, ("tset form needs table, key, and value"), ast)
         1624  +    local root = compile1(ast[2], scope, parent, {nval = 1})[1]
         1625  +    local keys = {}
         1626  +    for i = 3, #ast - 1 do
         1627  +        local key = compile1(ast[i], scope, parent, {nval = 1})[1]
         1628  +        keys[#keys + 1] = tostring(key)
         1629  +    end
         1630  +    local value = compile1(ast[#ast], scope, parent, {nval = 1})[1]
         1631  +    local rootstr = tostring(root)
         1632  +    -- Prefix 'do end ' so parens are not ambiguous (grouping or function call?)
         1633  +    local fmtstr = (rootstr:match("^{")) and "do end (%s)[%s] = %s" or "%s[%s] = %s"
         1634  +    emit(parent, fmtstr:format(tostring(root),
         1635  +                               table.concat(keys, ']['),
         1636  +                               tostring(value)), ast)
         1637  +end
         1638  +docSpecial("tset", {"tbl", "key1", "...", "keyN", "val"},
         1639  +           "Set the value of a table field. Can take additional keys to set"
         1640  +        .. "nested values,\nbut all parents must contain an existing table.")
         1641  +
         1642  +-- The if special form behaves like the cond form in
         1643  +-- many languages
         1644  +SPECIALS["if"] = function(ast, scope, parent, opts)
         1645  +    local doScope = makeScope(scope)
         1646  +    local branches = {}
         1647  +    local elseBranch = nil
         1648  +
         1649  +    -- Calculate some external stuff. Optimizes for tail calls and what not
         1650  +    local wrapper, innerTail, innerTarget, targetExprs
         1651  +    if opts.tail or opts.target or opts.nval then
         1652  +        if opts.nval and opts.nval ~= 0 and not opts.target then
         1653  +            -- We need to create a target
         1654  +            targetExprs = {}
         1655  +            local accum = {}
         1656  +            for i = 1, opts.nval do
         1657  +                local s = gensym(scope)
         1658  +                accum[i] = s
         1659  +                targetExprs[i] = expr(s, 'sym')
         1660  +            end
         1661  +            wrapper = 'target'
         1662  +            innerTail = opts.tail
         1663  +            innerTarget = table.concat(accum, ', ')
         1664  +        else
         1665  +            wrapper = 'none'
         1666  +            innerTail = opts.tail
         1667  +            innerTarget = opts.target
         1668  +        end
         1669  +    else
         1670  +        wrapper = 'iife'
         1671  +        innerTail = true
         1672  +        innerTarget = nil
         1673  +    end
         1674  +
         1675  +    -- Compile bodies and conditions
         1676  +    local bodyOpts = {
         1677  +        tail = innerTail,
         1678  +        target = innerTarget,
         1679  +        nval = opts.nval
         1680  +    }
         1681  +    local function compileBody(i)
         1682  +        local chunk = {}
         1683  +        local cscope = makeScope(doScope)
         1684  +        keepSideEffects(compile1(ast[i], cscope, chunk, bodyOpts),
         1685  +        chunk, nil, ast[i])
         1686  +        return {
         1687  +            chunk = chunk,
         1688  +            scope = cscope
         1689  +        }
         1690  +    end
         1691  +    for i = 2, #ast - 1, 2 do
         1692  +        local condchunk = {}
         1693  +        local res = compile1(ast[i], doScope, condchunk, {nval = 1})
         1694  +        local cond = res[1]
         1695  +        local branch = compileBody(i + 1)
         1696  +        branch.cond = cond
         1697  +        branch.condchunk = condchunk
         1698  +        branch.nested = i ~= 2 and next(condchunk, nil) == nil
         1699  +        table.insert(branches, branch)
         1700  +    end
         1701  +    local hasElse = #ast > 3 and #ast % 2 == 0
         1702  +    if hasElse then elseBranch = compileBody(#ast) end
         1703  +
         1704  +    -- Emit code
         1705  +    local s = gensym(scope)
         1706  +    local buffer = {}
         1707  +    local lastBuffer = buffer
         1708  +    for i = 1, #branches do
         1709  +        local branch = branches[i]
         1710  +        local fstr = not branch.nested and 'if %s then' or 'elseif %s then'
         1711  +        local cond = tostring(branch.cond)
         1712  +        local condLine = (cond == "true" and branch.nested and i == #branches)
         1713  +            and "else"
         1714  +            or fstr:format(cond)
         1715  +        if branch.nested then
         1716  +            emit(lastBuffer, branch.condchunk, ast)
         1717  +        else
         1718  +            for _, v in ipairs(branch.condchunk) do emit(lastBuffer, v, ast) end
         1719  +        end
         1720  +        emit(lastBuffer, condLine, ast)
         1721  +        emit(lastBuffer, branch.chunk, ast)
         1722  +        if i == #branches then
         1723  +            if hasElse then
         1724  +                emit(lastBuffer, 'else', ast)
         1725  +                emit(lastBuffer, elseBranch.chunk, ast)
         1726  +            -- TODO: Consolidate use of condLine ~= "else" with hasElse
         1727  +            elseif(innerTarget and condLine ~= 'else') then
         1728  +                emit(lastBuffer, 'else', ast)
         1729  +                emit(lastBuffer, ("%s = nil"):format(innerTarget), ast)
         1730  +            end
         1731  +            emit(lastBuffer, 'end', ast)
         1732  +        elseif not branches[i + 1].nested then
         1733  +            emit(lastBuffer, 'else', ast)
         1734  +            local nextBuffer = {}
         1735  +            emit(lastBuffer, nextBuffer, ast)
         1736  +            emit(lastBuffer, 'end', ast)
         1737  +            lastBuffer = nextBuffer
         1738  +        end
         1739  +    end
         1740  +
         1741  +    if wrapper == 'iife' then
         1742  +        local iifeargs = scope.vararg and '...' or ''
         1743  +        emit(parent, ('local function %s(%s)'):format(tostring(s), iifeargs), ast)
         1744  +        emit(parent, buffer, ast)
         1745  +        emit(parent, 'end', ast)
         1746  +        return expr(('%s(%s)'):format(tostring(s), iifeargs), 'statement')
         1747  +    elseif wrapper == 'none' then
         1748  +        -- Splice result right into code
         1749  +        for i = 1, #buffer do
         1750  +            emit(parent, buffer[i], ast)
         1751  +        end
         1752  +        return {returned = true}
         1753  +    else -- wrapper == 'target'
         1754  +        emit(parent, ('local %s'):format(innerTarget), ast)
         1755  +        for i = 1, #buffer do
         1756  +            emit(parent, buffer[i], ast)
         1757  +        end
         1758  +        return targetExprs
         1759  +    end
         1760  +end
         1761  +docSpecial("if", {"cond1", "body1", "...", "condN", "bodyN"},
         1762  +           "Conditional form.\n" ..
         1763  +               "Takes any number of condition/body pairs and evaluates the first body where"
         1764  +               .. "\nthe condition evaluates to truthy. Similar to cond in other lisps.")
         1765  +
         1766  +-- (each [k v (pairs t)] body...) => []
         1767  +SPECIALS["each"] = function(ast, scope, parent)
         1768  +    local binding = assertCompile(isTable(ast[2]), "expected binding table", ast)
         1769  +    local iter = table.remove(binding, #binding) -- last item is iterator call
         1770  +    local destructures = {}
         1771  +    local newManglings = {}
         1772  +    local function destructureBinding(v)
         1773  +        assertCompile(isSym(v) or isTable(v), "expected iterator symbol or table", ast)
         1774  +        if isSym(v) then
         1775  +            return declareLocal(v, {}, scope, ast, newManglings)
         1776  +        else
         1777  +            local raw = sym(gensym(scope))
         1778  +            destructures[raw] = v
         1779  +            return declareLocal(raw, {}, scope, ast)
         1780  +        end
         1781  +    end
         1782  +    local bindVars = map(binding, destructureBinding)
         1783  +    local vals = compile1(iter, scope, parent)
         1784  +    local valNames = map(vals, tostring)
         1785  +
         1786  +    emit(parent, ('for %s in %s do'):format(table.concat(bindVars, ', '),
         1787  +                                            table.concat(valNames, ", ")), ast)
         1788  +    local chunk = {}
         1789  +    for raw, args in pairs(destructures) do
         1790  +        destructure(args, raw, ast, scope, chunk,
         1791  +                    { declaration = true, nomulti = true })
         1792  +    end
         1793  +    applyManglings(scope, newManglings, ast)
         1794  +    compileDo(ast, scope, chunk, 3)
         1795  +    emit(parent, chunk, ast)
         1796  +    emit(parent, 'end', ast)
         1797  +end
         1798  +docSpecial("each", {"[key value (iterator)]", "..."},
         1799  +           "Runs the body once for each set of values provided by the given iterator."
         1800  +           .."\nMost commonly used with ipairs for sequential tables or pairs for"
         1801  +               .." undefined\norder, but can be used with any iterator.")
         1802  +
         1803  +-- (while condition body...) => []
         1804  +SPECIALS["while"] = function(ast, scope, parent)
         1805  +    local len1 = #parent
         1806  +    local condition = compile1(ast[2], scope, parent, {nval = 1})[1]
         1807  +    local len2 = #parent
         1808  +    local subChunk = {}
         1809  +    if len1 ~= len2 then
         1810  +        -- Compound condition
         1811  +        -- Move new compilation to subchunk
         1812  +        for i = len1 + 1, len2 do
         1813  +            subChunk[#subChunk + 1] = parent[i]
         1814  +            parent[i] = nil
         1815  +        end
         1816  +        emit(parent, 'while true do', ast)
         1817  +        emit(subChunk, ('if not %s then break end'):format(condition[1]), ast)
         1818  +    else
         1819  +        -- Simple condition
         1820  +        emit(parent, 'while ' .. tostring(condition) .. ' do', ast)
         1821  +    end
         1822  +    compileDo(ast, makeScope(scope), subChunk, 3)
         1823  +    emit(parent, subChunk, ast)
         1824  +    emit(parent, 'end', ast)
         1825  +end
         1826  +docSpecial("while", {"condition", "..."},
         1827  +           "The classic while loop. Evaluates body until a condition is non-truthy.")
         1828  +
         1829  +SPECIALS["for"] = function(ast, scope, parent)
         1830  +    local ranges = assertCompile(isTable(ast[2]), "expected binding table", ast)
         1831  +    local bindingSym = assertCompile(isSym(table.remove(ast[2], 1)),
         1832  +                                     "expected iterator symbol", ast)
         1833  +    local rangeArgs = {}
         1834  +    for i = 1, math.min(#ranges, 3) do
         1835  +        rangeArgs[i] = tostring(compile1(ranges[i], scope, parent, {nval = 1})[1])
         1836  +    end
         1837  +    emit(parent, ('for %s = %s do'):format(
         1838  +             declareLocal(bindingSym, {}, scope, ast),
         1839  +             table.concat(rangeArgs, ', ')), ast)
         1840  +    local chunk = {}
         1841  +    compileDo(ast, scope, chunk, 3)
         1842  +    emit(parent, chunk, ast)
         1843  +    emit(parent, 'end', ast)
         1844  +end
         1845  +docSpecial("for", {"[index start stop step?]", "..."}, "Numeric loop construct." ..
         1846  +               "\nEvaluates body once for each value between start and stop (inclusive).")
         1847  +
         1848  +SPECIALS[":"] = function(ast, scope, parent)
         1849  +    assertCompile(#ast >= 3, "expected at least 3 arguments", ast)
         1850  +    -- Compile object
         1851  +    local objectexpr = compile1(ast[2], scope, parent, {nval = 1})[1]
         1852  +    -- Compile method selector
         1853  +    local methodstring
         1854  +    local methodident = false
         1855  +    if type(ast[3]) == 'string' and isValidLuaIdentifier(ast[3]) then
         1856  +        methodident = true
         1857  +        methodstring = ast[3]
         1858  +    else
         1859  +        methodstring = tostring(compile1(ast[3], scope, parent, {nval = 1})[1])
         1860  +        objectexpr = once(objectexpr, ast[2], scope, parent)
         1861  +    end
         1862  +    -- Compile arguments
         1863  +    local args = {}
         1864  +    for i = 4, #ast do
         1865  +        local subexprs = compile1(ast[i], scope, parent, {
         1866  +            nval = i ~= #ast and 1 or nil
         1867  +        })
         1868  +        map(subexprs, tostring, args)
         1869  +    end
         1870  +    local fstring
         1871  +    if not methodident then
         1872  +        -- Make object first argument
         1873  +        table.insert(args, 1, tostring(objectexpr))
         1874  +        fstring = objectexpr.type == 'sym'
         1875  +            and '%s[%s](%s)'
         1876  +            or '(%s)[%s](%s)'
         1877  +    elseif(objectexpr.type == 'literal' or objectexpr.type == 'expression') then
         1878  +        fstring = '(%s):%s(%s)'
         1879  +    else
         1880  +        fstring = '%s:%s(%s)'
         1881  +    end
         1882  +    return expr(fstring:format(
         1883  +        tostring(objectexpr),
         1884  +        methodstring,
         1885  +        table.concat(args, ', ')), 'statement')
         1886  +end
         1887  +docSpecial(":", {"tbl", "method-name", "..."},
         1888  +           "Call the named method on tbl with the provided args."..
         1889  +           "\nMethod name doesn\"t have to be known at compile-time; if it is, use"
         1890  +               .."\n(tbl:method-name ...) instead.")
         1891  +
         1892  +SPECIALS["comment"] = function(ast, _, parent)
         1893  +    local els = {}
         1894  +    for i = 2, #ast do
         1895  +        els[#els + 1] = tostring(ast[i]):gsub('\n', ' ')
         1896  +    end
         1897  +    emit(parent, '              -- ' .. table.concat(els, ' '), ast)
         1898  +end
         1899  +docSpecial("comment", {"..."}, "Comment which will be emitted in Lua output.")
         1900  +
         1901  +SPECIALS["hashfn"] = function(ast, scope, parent)
         1902  +    assertCompile(#ast == 2, "expected one argument", ast)
         1903  +    local fScope = makeScope(scope)
         1904  +    local fChunk = {}
         1905  +    local name = gensym(scope)
         1906  +    local symbol = sym(name)
         1907  +    declareLocal(symbol, {}, scope, ast)
         1908  +    fScope.vararg = false
         1909  +    fScope.hashfn = true
         1910  +    local args = {}
         1911  +    for i = 1, 9 do args[i] = declareLocal(sym('$' .. i), {}, fScope, ast) end
         1912  +    -- Compile body
         1913  +    compile1(ast[2], fScope, fChunk, {tail = true})
         1914  +    local maxUsed = 0
         1915  +    for i = 1, 9 do if fScope.symmeta['$' .. i].used then maxUsed = i end end
         1916  +    local argStr = table.concat(args, ', ', 1, maxUsed)
         1917  +    emit(parent, ('local function %s(%s)'):format(name, argStr), ast)
         1918  +    emit(parent, fChunk, ast)
         1919  +    emit(parent, 'end', ast)
         1920  +    return expr(name, 'sym')
         1921  +end
         1922  +docSpecial("hashfn", {"..."}, "Function literal shorthand; args are $1, $2, etc.")
         1923  +
         1924  +local function defineArithmeticSpecial(name, zeroArity, unaryPrefix)
         1925  +    local paddedOp = ' ' .. name .. ' '
         1926  +    SPECIALS[name] = function(ast, scope, parent)
         1927  +        local len = #ast
         1928  +        if len == 1 then
         1929  +            assertCompile(zeroArity ~= nil, 'Expected more than 0 arguments', ast)
         1930  +            return expr(zeroArity, 'literal')
         1931  +        else
         1932  +            local operands = {}
         1933  +            for i = 2, len do
         1934  +                local subexprs = compile1(ast[i], scope, parent, {
         1935  +                    nval = (i == 1 and 1 or nil)
         1936  +                })
         1937  +                map(subexprs, tostring, operands)
         1938  +            end
         1939  +            if #operands == 1 then
         1940  +                if unaryPrefix then
         1941  +                    return '(' .. unaryPrefix .. paddedOp .. operands[1] .. ')'
         1942  +                else
         1943  +                    return operands[1]
         1944  +                end
         1945  +            else
         1946  +                return '(' .. table.concat(operands, paddedOp) .. ')'
         1947  +            end
         1948  +        end
         1949  +    end
         1950  +    docSpecial(name, {"a", "b", "..."},
         1951  +               "Arithmetic operator; works the same as Lua but accepts more arguments.")
         1952  +end
         1953  +
         1954  +defineArithmeticSpecial('+', '0')
         1955  +defineArithmeticSpecial('..', "''")
         1956  +defineArithmeticSpecial('^')
         1957  +defineArithmeticSpecial('-', nil, '')
         1958  +defineArithmeticSpecial('*', '1')
         1959  +defineArithmeticSpecial('%')
         1960  +defineArithmeticSpecial('/', nil, '1')
         1961  +defineArithmeticSpecial('//', nil, '1')
         1962  +defineArithmeticSpecial('or', 'false')
         1963  +defineArithmeticSpecial('and', 'true')
         1964  +
         1965  +docSpecial("and", {"a", "b", "..."},
         1966  +           "Boolean operator; works the same as Lua but accepts more arguments.")
         1967  +docSpecial("or", {"a", "b", "..."},
         1968  +           "Boolean operator; works the same as Lua but accepts more arguments.")
         1969  +docSpecial("..", {"a", "b", "..."},
         1970  +           "String concatenation operator; works the same as Lua but accepts more arguments.")
         1971  +
         1972  +local function defineComparatorSpecial(name, realop, chainOp)
         1973  +    local op = realop or name
         1974  +    SPECIALS[name] = function(ast, scope, parent)
         1975  +        local len = #ast
         1976  +        assertCompile(len > 2, "expected at least two arguments", ast)
         1977  +        local lhs = compile1(ast[2], scope, parent, {nval = 1})[1]
         1978  +        local lastval = compile1(ast[3], scope, parent, {nval = 1})[1]
         1979  +        -- avoid double-eval by introducing locals for possible side-effects
         1980  +        if len > 3 then lastval = once(lastval, ast[3], scope, parent) end
         1981  +        local out = ('(%s %s %s)'):
         1982  +            format(tostring(lhs), op, tostring(lastval))
         1983  +        if len > 3 then
         1984  +            for i = 4, len do -- variadic comparison
         1985  +                local nextval = once(compile1(ast[i], scope, parent, {nval = 1})[1],
         1986  +                                     ast[i], scope, parent)
         1987  +                out = (out .. " %s (%s %s %s)"):
         1988  +                    format(chainOp or 'and', tostring(lastval), op, tostring(nextval))
         1989  +                lastval = nextval
         1990  +            end
         1991  +            out = '(' .. out .. ')'
         1992  +        end
         1993  +        return out
         1994  +    end
         1995  +    docSpecial(name, {"a", "b", "..."},
         1996  +               "Comparison operator; works the same as Lua but accepts more arguments.")
         1997  +end
         1998  +
         1999  +defineComparatorSpecial('>')
         2000  +defineComparatorSpecial('<')
         2001  +defineComparatorSpecial('>=')
         2002  +defineComparatorSpecial('<=')
         2003  +defineComparatorSpecial('=', '==')
         2004  +defineComparatorSpecial('not=', '~=', 'or')
         2005  +SPECIALS["~="] = SPECIALS["not="] -- backwards-compatibility alias
         2006  +
         2007  +local function defineUnarySpecial(op, realop)
         2008  +    SPECIALS[op] = function(ast, scope, parent)
         2009  +        assertCompile(#ast == 2, 'expected one argument', ast)
         2010  +        local tail = compile1(ast[2], scope, parent, {nval = 1})
         2011  +        return (realop or op) .. tostring(tail[1])
         2012  +    end
         2013  +end
         2014  +
         2015  +defineUnarySpecial("not", "not ")
         2016  +docSpecial("not", {"x"}, "Logical operator; works the same as Lua.")
         2017  +
         2018  +defineUnarySpecial("length", "#")
         2019  +docSpecial("length", {"x"}, "Returns the length of a table or string.")
         2020  +SPECIALS["#"] = SPECIALS["length"]
         2021  +
         2022  +-- Save current macro scope
         2023  +local macroCurrentScope = nil
         2024  +
         2025  +-- Covert a macro function to a special form
         2026  +local function macroToSpecial(mac)
         2027  +    local special = function(ast, scope, parent, opts)
         2028  +        local oldScope = macroCurrentScope
         2029  +        macroCurrentScope = scope
         2030  +        local ok, transformed = pcall(mac, unpack(ast, 2))
         2031  +        macroCurrentScope = oldScope
         2032  +        assertCompile(ok, transformed, ast)
         2033  +        local result = compile1(transformed, scope, parent, opts)
         2034  +        return result
         2035  +    end
         2036  +    if metadata[mac] then
         2037  +        -- copy metadata from original function to special form function
         2038  +        metadata[mac], metadata[special] = nil, metadata[mac]
         2039  +    end
         2040  +    return special
         2041  +end
         2042  +
         2043  +local requireSpecial
         2044  +local function compile(ast, options)
         2045  +    local opts = copy(options)
         2046  +    local oldGlobals = allowedGlobals
         2047  +    setResetRoot(rootChunk, rootScope, rootOptions)
         2048  +    allowedGlobals = opts.allowedGlobals
         2049  +    if opts.indent == nil then opts.indent = '  ' end
         2050  +    local chunk = {}
         2051  +    local scope = opts.scope or makeScope(GLOBAL_SCOPE)
         2052  +    rootChunk, rootScope, rootOptions = chunk, scope, opts
         2053  +    if opts.requireAsInclude then scope.specials.require = requireSpecial end
         2054  +    local exprs = compile1(ast, scope, chunk, {tail = true})
         2055  +    keepSideEffects(exprs, chunk, nil, ast)
         2056  +    allowedGlobals = oldGlobals
         2057  +    resetRoot()
         2058  +    return flatten(chunk, opts)
         2059  +end
         2060  +
         2061  +-- make a transformer for key / value table pairs, preserving all numeric keys
         2062  +local function entryTransform(fk,fv)
         2063  +    return function(k, v)
         2064  +        if type(k) == 'number' then
         2065  +            return k,fv(v)
         2066  +        else
         2067  +            return fk(k),fv(v)
         2068  +        end
         2069  +    end
         2070  +end
         2071  +
         2072  +-- consume everything return nothing
         2073  +local function no() end
         2074  +
         2075  +local function mixedConcat(t, joiner)
         2076  +    local ret = ""
         2077  +    local s = ""
         2078  +    local seen = {}
         2079  +    for k,v in ipairs(t) do
         2080  +        table.insert(seen, k)
         2081  +        ret = ret .. s .. v
         2082  +        s = joiner
         2083  +    end
         2084  +    for k,v in pairs(t) do
         2085  +        if not(seen[k]) then
         2086  +            ret = ret .. s .. '[' .. k .. ']' .. '=' .. v
         2087  +            s = joiner
         2088  +        end
         2089  +    end
         2090  +    return ret
         2091  +end
         2092  +
         2093  +-- expand a quoted form into a data literal, evaluating unquote
         2094  +local function doQuote (form, scope, parent, runtime)
         2095  +    local q = function (x) return doQuote(x, scope, parent, runtime) end
         2096  +    -- vararg
         2097  +    if isVarg(form) then
         2098  +        assertCompile(not runtime, "quoted ... may only be used at compile time", form)
         2099  +        return "_VARARG"
         2100  +    -- symbol
         2101  +    elseif isSym(form) then
         2102  +        assertCompile(not runtime, "symbols may only be used at compile time", form)
         2103  +        if deref(form):find("#$") then -- autogensym
         2104  +            return ("sym('%s')"):format(autogensym(deref(form), scope))
         2105  +        else -- prevent non-gensymmed symbols from being bound as an identifier
         2106  +            return ("sym('%s', nil, {quoted=true})"):format(deref(form))
         2107  +        end
         2108  +    -- unquote
         2109  +    elseif isList(form) and isSym(form[1]) and (deref(form[1]) == 'unquote') then
         2110  +        local payload = form[2]
         2111  +        local res = unpack(compile1(payload, scope, parent))
         2112  +        return res[1]
         2113  +    -- list
         2114  +    elseif isList(form) then
         2115  +        assertCompile(not runtime, "lists may only be used at compile time", form)
         2116  +        local mapped = kvmap(form, entryTransform(no, q))
         2117  +        return 'list(' .. mixedConcat(mapped, ", ") .. ')'
         2118  +    -- table
         2119  +    elseif type(form) == 'table' then
         2120  +        local mapped = kvmap(form, entryTransform(q, q))
         2121  +        return '{' .. mixedConcat(mapped, ", ") .. '}'
         2122  +    -- string
         2123  +    elseif type(form) == 'string' then
         2124  +        return serializeString(form)
         2125  +    else
         2126  +        return tostring(form)
         2127  +    end
         2128  +end
         2129  +
         2130  +SPECIALS['quote'] = function(ast, scope, parent)
         2131  +    assertCompile(#ast == 2, "quote only takes a single form")
         2132  +    local runtime, thisScope = true, scope
         2133  +    while thisScope do
         2134  +        thisScope = thisScope.parent
         2135  +        if thisScope == COMPILER_SCOPE then runtime = false end
         2136  +    end
         2137  +    return doQuote(ast[2], scope, parent, runtime)
         2138  +end
         2139  +docSpecial('quote', {'x'}, 'Quasiquote the following form. Only works in macro/compiler scope.')
         2140  +
         2141  +local function compileStream(strm, options)
         2142  +    local opts = copy(options)
         2143  +    local oldGlobals = allowedGlobals
         2144  +    setResetRoot(rootChunk, rootScope, rootOptions)
         2145  +    allowedGlobals = opts.allowedGlobals
         2146  +    if opts.indent == nil then opts.indent = '  ' end
         2147  +    local scope = opts.scope or makeScope(GLOBAL_SCOPE)
         2148  +    if opts.requireAsInclude then scope.specials.require = requireSpecial end
         2149  +    local vals = {}
         2150  +    for ok, val in parser(strm, opts.filename) do
         2151  +        if not ok then break end
         2152  +        vals[#vals + 1] = val
         2153  +    end
         2154  +    local chunk = {}
         2155  +    rootChunk, rootScope, rootOptions = chunk, scope, opts
         2156  +    for i = 1, #vals do
         2157  +        local exprs = compile1(vals[i], scope, chunk, {
         2158  +            tail = i == #vals,
         2159  +        })
         2160  +        keepSideEffects(exprs, chunk, nil, vals[i])
         2161  +    end
         2162  +    allowedGlobals = oldGlobals
         2163  +    resetRoot()
         2164  +    return flatten(chunk, opts)
         2165  +end
         2166  +
         2167  +local function compileString(str, options)
         2168  +    local strm = stringStream(str)
         2169  +    return compileStream(strm, options)
         2170  +end
         2171  +
         2172  +---
         2173  +--- Evaluation
         2174  +---
         2175  +
         2176  +-- Convert a fennel environment table to a Lua environment table.
         2177  +-- This means automatically unmangling globals when getting a value,
         2178  +-- and mangling values when setting a value. This means the original
         2179  +-- env will see its values updated as expected, regardless of mangling rules.
         2180  +local function wrapEnv(env)
         2181  +    return setmetatable({}, {
         2182  +        __index = function(_, key)
         2183  +            if type(key) == 'string' then
         2184  +                key = globalUnmangling(key)
         2185  +            end
         2186  +            return env[key]
         2187  +        end,
         2188  +        __newindex = function(_, key, value)
         2189  +            if type(key) == 'string' then
         2190  +                key = globalMangling(key)
         2191  +            end
         2192  +            env[key] = value
         2193  +        end,
         2194  +        -- checking the __pairs metamethod won't work automatically in Lua 5.1
         2195  +        -- sadly, but it's important for 5.2+ and can be done manually in 5.1
         2196  +        __pairs = function()
         2197  +            local function putenv(k, v)
         2198  +                return type(k) == 'string' and globalUnmangling(k) or k, v
         2199  +            end
         2200  +            local pt = kvmap(env, putenv)
         2201  +            return next, pt, nil
         2202  +        end,
         2203  +    })
         2204  +end
         2205  +
         2206  +-- A custom traceback function for Fennel that looks similar to
         2207  +-- the Lua's debug.traceback.
         2208  +-- Use with xpcall to produce fennel specific stacktraces.
         2209  +local function traceback(msg, start)
         2210  +    local level = start or 2 -- Can be used to skip some frames
         2211  +    local lines = {}
         2212  +    if msg then
         2213  +        if msg:find("^Compile error") then
         2214  +            table.insert(lines, msg)
         2215  +        else
         2216  +            local newmsg = msg:gsub('^[^:]*:%d+:%s+', 'runtime error: ')
         2217  +            table.insert(lines, newmsg)
         2218  +        end
         2219  +    end
         2220  +    table.insert(lines, 'stack traceback:')
         2221  +    while true do
         2222  +        local info = debug.getinfo(level, "Sln")
         2223  +        if not info then break end
         2224  +        local line
         2225  +        if info.what == "C" then
         2226  +            if info.name then
         2227  +                line = ('  [C]: in function \'%s\''):format(info.name)
         2228  +            else
         2229  +                line = '  [C]: in ?'
         2230  +            end
         2231  +        else
         2232  +            local remap = fennelSourcemap[info.source]
         2233  +            if remap and remap[info.currentline] then
         2234  +                -- And some global info
         2235  +                info.short_src = remap.short_src
         2236  +                local mapping = remap[info.currentline]
         2237  +                -- Overwrite info with values from the mapping (mapping is now
         2238  +                -- just integer, but may eventually be a table)
         2239  +                info.currentline = mapping
         2240  +            end
         2241  +            if info.what == 'Lua' then
         2242  +                local n = info.name and ("'" .. info.name .. "'") or '?'
         2243  +                line = ('  %s:%d: in function %s'):format(info.short_src, info.currentline, n)
         2244  +            elseif info.short_src == '(tail call)' then
         2245  +                line = '  (tail call)'
         2246  +            else
         2247  +                line = ('  %s:%d: in main chunk'):format(info.short_src, info.currentline)
         2248  +            end
         2249  +        end
         2250  +        table.insert(lines, line)
         2251  +        level = level + 1
         2252  +    end
         2253  +    return table.concat(lines, '\n')
         2254  +end
         2255  +
         2256  +local function currentGlobalNames(env)
         2257  +    return kvmap(env or _G, globalUnmangling)
         2258  +end
         2259  +
         2260  +local function eval(str, options, ...)
         2261  +    local opts = copy(options)
         2262  +    -- eval and dofile are considered "live" entry points, so we can assume
         2263  +    -- that the globals available at compile time are a reasonable allowed list
         2264  +    -- UNLESS there's a metatable on env, in which case we can't assume that
         2265  +    -- pairs will return all the effective globals; for instance openresty
         2266  +    -- sets up _G in such a way that all the globals are available thru
         2267  +    -- the __index meta method, but as far as pairs is concerned it's empty.
         2268  +    if opts.allowedGlobals == nil and not getmetatable(opts.env) then
         2269  +        opts.allowedGlobals = currentGlobalNames(opts.env)
         2270  +    end
         2271  +    local env = opts.env and wrapEnv(opts.env)
         2272  +    local luaSource = compileString(str, opts)
         2273  +    local loader = loadCode(luaSource, env,
         2274  +                            opts.filename and ('@' .. opts.filename) or str)
         2275  +    opts.filename = nil
         2276  +    return loader(...)
         2277  +end
         2278  +
         2279  +local function dofileFennel(filename, options, ...)
         2280  +    local opts = copy(options)
         2281  +    if opts.allowedGlobals == nil then
         2282  +        opts.allowedGlobals = currentGlobalNames(opts.env)
         2283  +    end
         2284  +    local f = assert(io.open(filename, "rb"))
         2285  +    local source = f:read("*all"):gsub("^#![^\n]*\n", "")
         2286  +    f:close()
         2287  +    opts.filename = filename
         2288  +    return eval(source, opts, ...)
         2289  +end
         2290  +
         2291  +local macroLoaded = {}
         2292  +
         2293  +local pathTable = {"./?.fnl", "./?/init.fnl"}
         2294  +local osPath = os and os.getenv and os.getenv("FENNEL_PATH")
         2295  +if osPath then
         2296  +    table.insert(pathTable, osPath)
         2297  +end
         2298  +
         2299  +local module = {
         2300  +    parser = parser,
         2301  +    granulate = granulate,
         2302  +    stringStream = stringStream,
         2303  +    compile = compile,
         2304  +    compileString = compileString,
         2305  +    compileStream = compileStream,
         2306  +    compile1 = compile1,
         2307  +    loadCode = loadCode,
         2308  +    mangle = globalMangling,
         2309  +    unmangle = globalUnmangling,
         2310  +    list = list,
         2311  +    sym = sym,
         2312  +    varg = varg,
         2313  +    scope = makeScope,
         2314  +    gensym = gensym,
         2315  +    eval = eval,
         2316  +    dofile = dofileFennel,
         2317  +    macroLoaded = macroLoaded,
         2318  +    path = table.concat(pathTable, ";"),
         2319  +    traceback = traceback,
         2320  +    version = "0.4.0-dev",
         2321  +}
         2322  +
         2323  +-- In order to make this more readable, you can switch your editor to treating
         2324  +-- this file as if it were Fennel for the purposes of this section
         2325  +local replsource = [===[(local (fennel internals) ...)
         2326  +
         2327  +(fn default-read-chunk [parser-state]
         2328  +  (io.write (if (< 0 parser-state.stackSize) ".." ">> "))
         2329  +  (io.flush)
         2330  +  (let [input (io.read)]
         2331  +    (and input (.. input "\n"))))
         2332  +
         2333  +(fn default-on-values [xs]
         2334  +  (io.write (table.concat xs "\t"))
         2335  +  (io.write "\n"))
         2336  +
         2337  +(fn default-on-error [errtype err lua-source]
         2338  +  (io.write
         2339  +   (match errtype
         2340  +     "Lua Compile" (.. "Bad code generated - likely a bug with the compiler:\n"
         2341  +                       "--- Generated Lua Start ---\n"
         2342  +                       lua-source
         2343  +                       "--- Generated Lua End ---\n")
         2344  +     "Runtime" (.. (fennel.traceback err 4) "\n")
         2345  +     _ (: "%s error: %s\n" :format errtype (tostring err)))))
         2346  +
         2347  +(local save-source
         2348  +       (table.concat ["local ___i___ = 1"
         2349  +                      "while true do"
         2350  +                      " local name, value = debug.getlocal(1, ___i___)"
         2351  +                      " if(name and name ~= \"___i___\") then"
         2352  +                      " ___replLocals___[name] = value"
         2353  +                      " ___i___ = ___i___ + 1"
         2354  +                      " else break end end"] "\n"))
         2355  +
         2356  +(fn splice-save-locals [env lua-source]
         2357  +  (set env.___replLocals___ (or env.___replLocals___ {}))
         2358  +  (let [spliced-source []
         2359  +        bind "local %s = ___replLocals___['%s']"]
         2360  +    (each [line (lua-source:gmatch "([^\n]+)\n?")]
         2361  +      (table.insert spliced-source line))
         2362  +    (each [name (pairs env.___replLocals___)]
         2363  +      (table.insert spliced-source 1 (bind:format name name)))
         2364  +    (when (and (: (. spliced-source (# spliced-source)) :match "^ *return .*$")
         2365  +               (< 1 (# spliced-source)))
         2366  +      (table.insert spliced-source (# spliced-source) save-source))
         2367  +    (table.concat spliced-source "\n")))
         2368  +
         2369  +(fn completer [env scope text]
         2370  +  (let [matches []
         2371  +        input-fragment (text:gsub ".*[%s)(]+" "")]
         2372  +    (fn add-partials [input tbl prefix] ; add partial key matches in tbl
         2373  +      (each [k (pairs tbl)]
         2374  +        (let [k (if (or (= tbl env) (= tbl env.___replLocals___))
         2375  +                    (. scope.unmanglings k)
         2376  +                    k)]
         2377  +          (when (and (< (# matches) 40)
         2378  +                     (= (type k) "string")
         2379  +                     (= input (k:sub 0 (# input))))
         2380  +            (table.insert matches (.. prefix k))))))
         2381  +    (fn add-matches [input tbl prefix] ; add matches, descending into tbl fields
         2382  +      (let [prefix (if prefix (.. prefix ".") "")]
         2383  +        (if (not (input:find "%.")) ; no more dots, so add matches
         2384  +            (add-partials input tbl prefix)
         2385  +            (let [(head tail) (input:match "^([^.]+)%.(.*)")
         2386  +                  raw-head (if (or (= tbl env) (= tbl env.___replLocals___))
         2387  +                               (. scope.manglings head)
         2388  +                               head)]
         2389  +              (when (= (type (. tbl raw-head)) "table")
         2390  +                (add-matches tail (. tbl raw-head) (.. prefix head)))))))
         2391  +
         2392  +    (add-matches input-fragment (or scope.specials []))
         2393  +    (add-matches input-fragment (or internals.SPECIALS []))
         2394  +    (add-matches input-fragment (or env.___replLocals___ []))
         2395  +    (add-matches input-fragment env)
         2396  +    (add-matches input-fragment (or env._ENV env._G []))
         2397  +    matches))
         2398  +
         2399  +(fn repl [options]
         2400  +  (let [old-root-options internals.rootOptions
         2401  +        env (if options.env
         2402  +                (internals.wrapEnv options.env)
         2403  +                (setmetatable {} {:__index (or _G._ENV _G)}))
         2404  +        save-locals? (and (not= options.saveLocals false)
         2405  +                          env.debug env.debug.getlocal)
         2406  +        opts {}
         2407  +        _ (each [k v (pairs options)] (tset opts k v))
         2408  +        read-chunk (or opts.readChunk default-read-chunk)
         2409  +        on-values (or opts.onValues default-on-values)
         2410  +        on-error (or opts.onError default-on-error)
         2411  +        pp (or opts.pp tostring)
         2412  +        ;; make parser
         2413  +        (byte-stream clear-stream) (fennel.granulate read-chunk)
         2414  +        chars []
         2415  +        (read reset) (fennel.parser (fn [parser-state]
         2416  +                                      (let [c (byte-stream parser-state)]
         2417  +                                        (tset chars (+ (# chars) 1) c)
         2418  +                                        c)))
         2419  +        scope (fennel.scope)]
         2420  +
         2421  +    ;; use metadata unless we've specifically disabled it
         2422  +    (set opts.useMetadata (not= options.useMetadata false))
         2423  +    (when (= opts.allowedGlobals nil)
         2424  +      (set opts.allowedGlobals (internals.currentGlobalNames opts.env)))
         2425  +
         2426  +    (when opts.registerCompleter
         2427  +      (opts.registerCompleter (partial completer env scope)))
         2428  +
         2429  +    (fn loop []
         2430  +      (each [k (pairs chars)] (tset chars k nil))
         2431  +      (let [(ok parse-ok? x) (pcall read)
         2432  +            src-string (string.char ((or _G.unpack table.unpack) chars))]
         2433  +        (internals.setRootOptions opts)
         2434  +        (if (not ok)
         2435  +            (do (on-error "Parse" parse-ok?)
         2436  +                (clear-stream)
         2437  +                (reset)
         2438  +                (loop))
         2439  +            (when parse-ok? ; if this is false, we got eof
         2440  +              (match (pcall fennel.compile x {:correlate opts.correlate
         2441  +                                              :source src-string
         2442  +                                              :scope scope
         2443  +                                              :useMetadata opts.useMetadata
         2444  +                                              :moduleName opts.moduleName})
         2445  +                (false msg) (do (clear-stream)
         2446  +                                (on-error "Compile" msg))
         2447  +                (true source) (let [source (if save-locals?
         2448  +                                               (splice-save-locals env source)
         2449  +                                               source)
         2450  +                                    (lua-ok? loader) (pcall fennel.loadCode
         2451  +                                                            source env)]
         2452  +                                (if (not lua-ok?)
         2453  +                                    (do (clear-stream)
         2454  +                                        (on-error "Lua Compile" loader source))
         2455  +                                    (match (xpcall #[(loader)]
         2456  +                                                   (partial on-error "Runtime"))
         2457  +                                      (true ret)
         2458  +                                      (do (set env._ (. ret 1))
         2459  +                                          (set env.__ ret)
         2460  +                                          (on-values (internals.map ret pp)))))))
         2461  +              (internals.setRootOptions old-root-options)
         2462  +              (loop)))))
         2463  +    (loop)))]===]
         2464  +
         2465  +module.repl = function(options)
         2466  +    -- functionality the repl needs that isn't part of the public API yet
         2467  +    local internals = { rootOptions = rootOptions,
         2468  +                        setRootOptions = function(r) rootOptions = r end,
         2469  +                        currentGlobalNames = currentGlobalNames,
         2470  +                        wrapEnv = wrapEnv,
         2471  +                        SPECIALS = SPECIALS,
         2472  +                        map = map }
         2473  +    return eval(replsource, { correlate = true }, module, internals)(options)
         2474  +end
         2475  +
         2476  +local function searchModule(modulename, pathstring)
         2477  +    modulename = modulename:gsub("%.", "/")
         2478  +    for path in string.gmatch((pathstring or module.path)..";", "([^;]*);") do
         2479  +        local filename = path:gsub("%?", modulename)
         2480  +        local file = io.open(filename, "rb")
         2481  +        if(file) then
         2482  +            file:close()
         2483  +            return filename
         2484  +        end
         2485  +    end
         2486  +end
         2487  +
         2488  +module.makeSearcher = function(options)
         2489  +    return function(modulename)
         2490  +      -- this will propagate options from the repl but not from eval, because
         2491  +      -- eval unsets rootOptions after compiling but before running the actual
         2492  +      -- calls to require.
         2493  +      local opts = copy(rootOptions)
         2494  +      for k,v in pairs(options or {}) do opts[k] = v end
         2495  +      local filename = searchModule(modulename)
         2496  +      if filename then
         2497  +         return function(modname)
         2498  +            return dofileFennel(filename, opts, modname)
         2499  +         end
         2500  +      end
         2501  +   end
         2502  +end
         2503  +
         2504  +-- Add metadata and docstrings to fennel module
         2505  +module.metadata = metadata
         2506  +module.doc = doc
         2507  +
         2508  +-- This will allow regular `require` to work with Fennel:
         2509  +-- table.insert(package.loaders, fennel.searcher)
         2510  +module.searcher = module.makeSearcher()
         2511  +module.make_searcher = module.makeSearcher -- oops backwards compatibility
         2512  +
         2513  +local function makeCompilerEnv(ast, scope, parent)
         2514  +    return setmetatable({
         2515  +        -- State of compiler if needed
         2516  +        _SCOPE = scope,
         2517  +        _CHUNK = parent,
         2518  +        _AST = ast,
         2519  +        _IS_COMPILER = true,
         2520  +        _SPECIALS = SPECIALS,
         2521  +        _VARARG = VARARG,
         2522  +        -- Expose the module in the compiler
         2523  +        fennel = module,
         2524  +        -- Useful for macros and meta programming. All of Fennel can be accessed
         2525  +        -- via fennel.myfun, for example (fennel.eval "(print 1)").
         2526  +        list = list,
         2527  +        sym = sym,
         2528  +        unpack = unpack,
         2529  +        gensym = function() return sym(gensym(macroCurrentScope or scope)) end,
         2530  +        ["list?"] = isList,
         2531  +        ["multi-sym?"] = isMultiSym,
         2532  +        ["sym?"] = isSym,
         2533  +        ["table?"] = isTable,
         2534  +        ["sequence?"] = isSequence,
         2535  +        ["varg?"] = isVarg,
         2536  +        ["get-scope"] = function()
         2537  +            if io and io.stderr then
         2538  +                io.stderr:
         2539  +                 write(("-- %s:%s get-scope is deprecated and will be removed\n"):
         2540  +                 format(ast.filename, ast.line))
         2541  +            end
         2542  +            return macroCurrentScope end,
         2543  +        ["in-scope?"] = function(symbol)
         2544  +            assertCompile(macroCurrentScope, "must call in-scope? from macro", ast)
         2545  +            return macroCurrentScope.manglings[tostring(symbol)]
         2546  +        end
         2547  +    }, { __index = _ENV or _G })
         2548  +end
         2549  +
         2550  +local function macroGlobals(env, globals)
         2551  +    local allowed = currentGlobalNames(env)
         2552  +    for _, k in pairs(globals or {}) do table.insert(allowed, k) end
         2553  +    return allowed
         2554  +end
         2555  +
         2556  +local function addMacros(macros, ast, scope)
         2557  +    assertCompile(isTable(macros), 'expected macros to be table', ast)
         2558  +    kvmap(macros, function(k, v) return k, macroToSpecial(v) end, scope.specials)
         2559  +end
         2560  +
         2561  +local function loadMacros(modname, ast, scope, parent)
         2562  +    local filename = assertCompile(searchModule(modname),
         2563  +                                   modname .. " not found.", ast)
         2564  +    local env = makeCompilerEnv(ast, scope, parent)
         2565  +    local globals = macroGlobals(env, currentGlobalNames())
         2566  +    return dofileFennel(filename, { env = env, allowedGlobals = globals,
         2567  +                                    useMetadata = rootOptions.useMetadata,
         2568  +                                    scope = COMPILER_SCOPE })
         2569  +end
         2570  +
         2571  +SPECIALS['require-macros'] = function(ast, scope, parent)
         2572  +    assertCompile(#ast == 2, "Expected one module name argument", ast)
         2573  +    local modname = ast[2]
         2574  +    if not macroLoaded[modname] then
         2575  +        macroLoaded[modname] = loadMacros(modname, ast, scope, parent)
         2576  +    end
         2577  +    addMacros(macroLoaded[modname], ast, scope, parent)
         2578  +end
         2579  +docSpecial('require-macros', {'macro-module-name'},
         2580  +           'Load given module and use its contents as macro definitions in current scope.'
         2581  +               ..'\nMacro module should return a table of macro functions with string keys.')
         2582  +
         2583  +SPECIALS['include'] = function(ast, scope, parent, opts)
         2584  +    assertCompile(#ast == 2, 'expected one argument', ast)
         2585  +
         2586  +    -- Compile mod argument
         2587  +    local modexpr = compile1(ast[2], scope, parent, {nval = 1})[1]
         2588  +    if modexpr.type ~= 'literal' or modexpr[1]:byte() ~= 34 then
         2589  +        if opts.fallback then
         2590  +            return opts.fallback(modexpr)
         2591  +        else
         2592  +            assertCompile(false, 'module name must resolve to a string literal', ast)
         2593  +        end
         2594  +    end
         2595  +    local code = 'return ' .. modexpr[1]
         2596  +    local mod = loadCode(code)()
         2597  +
         2598  +    -- Check cache
         2599  +    if scope.includes[mod] then return scope.includes[mod] end
         2600  +
         2601  +    -- Find path to source
         2602  +    local path = searchModule(mod)
         2603  +    local isFennel = true
         2604  +    if not path then
         2605  +        isFennel = false
         2606  +        path = searchModule(mod, package.path)
         2607  +        if not path then
         2608  +            if opts.fallback then
         2609  +                return opts.fallback(modexpr)
         2610  +            else
         2611  +                assertCompile(false, 'could not find module ' .. mod, ast)
         2612  +            end
         2613  +        end
         2614  +    end
         2615  +
         2616  +    -- Read source
         2617  +    local f = io.open(path)
         2618  +    local s = f:read('*all')
         2619  +    f:close()
         2620  +
         2621  +    -- splice in source and memoize it in compiler AND package.preload
         2622  +    -- so we can include it again without duplication, even in runtime
         2623  +    local target = 'package.preload["' .. mod .. '"]'
         2624  +    local ret = expr('require("' .. mod .. '")', 'statement')
         2625  +
         2626  +    local subChunk, tempChunk = {}, {}
         2627  +    emit(tempChunk, subChunk, ast)
         2628  +    -- if lua, simply emit the setting of package.preload
         2629  +    if not isFennel then
         2630  +        emit(tempChunk, target .. ' = ' .. target .. ' or function()\n' .. s .. '\nend', ast)
         2631  +    end
         2632  +    -- Splice tempChunk to begining of rootChunk
         2633  +    for i, v in ipairs(tempChunk) do
         2634  +        table.insert(rootChunk, i, v)
         2635  +    end
         2636  +
         2637  +    -- For fnl source, compile subChunk AFTER splicing into start of rootChunk.
         2638  +    if isFennel then
         2639  +        local subopts = { nval = 1, target = target }
         2640  +        local subscope = makeScope(rootScope.parent)
         2641  +        if rootOptions.requireAsInclude then
         2642  +            subscope.specials.require = requireSpecial
         2643  +        end
         2644  +        local targetForm = list(sym('.'), sym('package.preload'), mod)
         2645  +        -- splice "or" statement in so it uses existing package.preload[modname]
         2646  +        -- if it's been set by something else, allowing for overrides
         2647  +        local forms = list(sym('or'), targetForm, list(sym('fn'), sequence()))
         2648  +        local p = parser(stringStream(s), path)
         2649  +        for _, val in p do table.insert(forms[3], val) end
         2650  +        compile1(forms, subscope, subChunk, subopts)
         2651  +    end
         2652  +
         2653  +    -- Put in cache and return
         2654  +    rootScope.includes[mod] = ret
         2655  +    return ret
         2656  +end
         2657  +docSpecial('include', {'module-name-literal'},
         2658  +           'Like require, but load the target module during compilation and embed it in the\n'
         2659  +        .. 'Lua output. The module must be a string literal and resolvable at compile time.')
         2660  +
         2661  +local function requireFallback(e)
         2662  +    local code = ('require(%s)'):format(tostring(e))
         2663  +    return expr(code, 'statement')
         2664  +end
         2665  +
         2666  +requireSpecial = function (ast, scope, parent, opts)
         2667  +    opts.fallback = requireFallback
         2668  +    return SPECIALS['include'](ast, scope, parent, opts)
         2669  +end
         2670  +
         2671  +local function evalCompiler(ast, scope, parent)
         2672  +    local luaSource = compile(ast, { scope = makeScope(COMPILER_SCOPE),
         2673  +                                     useMetadata = rootOptions.useMetadata })
         2674  +    local loader = loadCode(luaSource, wrapEnv(makeCompilerEnv(ast, scope, parent)))
         2675  +    return loader()
         2676  +end
         2677  +
         2678  +SPECIALS['macros'] = function(ast, scope, parent)
         2679  +    assertCompile(#ast == 2, "Expected one table argument", ast)
         2680  +    local macros = evalCompiler(ast[2], scope, parent)
         2681  +    addMacros(macros, ast, scope, parent)
         2682  +end
         2683  +docSpecial('macros', {'{:macro-name-1 (fn [...] ...) ... :macro-name-N macro-body-N}'},
         2684  +           'Define all functions in the given table as macros local to the current scope.')
         2685  +
         2686  +SPECIALS['eval-compiler'] = function(ast, scope, parent)
         2687  +    local oldFirst = ast[1]
         2688  +    ast[1] = sym('do')
         2689  +    local val = evalCompiler(ast, scope, parent)
         2690  +    ast[1] = oldFirst
         2691  +    return val
         2692  +end
         2693  +docSpecial('eval-compiler', {'...'}, 'Evaluate the body at compile-time.'
         2694  +               .. ' Use the macro system instead if possible.')
         2695  +
         2696  +-- Load standard macros
         2697  +local stdmacros = [===[
         2698  +{"->" (fn [val ...]
         2699  +        "Thread-first macro.
         2700  +Take the first value and splice it into the second form as its first argument.
         2701  +The value of the second form is spliced into the first arg of the third, etc."
         2702  +        (var x val)
         2703  +        (each [_ e (ipairs [...])]
         2704  +          (let [elt (if (list? e) e (list e))]
         2705  +            (table.insert elt 2 x)
         2706  +            (set x elt)))
         2707  +        x)
         2708  + "->>" (fn [val ...]
         2709  +         "Thread-last macro.
         2710  +Same as ->, except splices the value into the last position of each form
         2711  +rather than the first."
         2712  +         (var x val)
         2713  +         (each [_ e (pairs [...])]
         2714  +           (let [elt (if (list? e) e (list e))]
         2715  +             (table.insert elt x)
         2716  +             (set x elt)))
         2717  +         x)
         2718  + "-?>" (fn [val ...]
         2719  +         "Nil-safe thread-first macro.
         2720  +Same as -> except will short-circuit with nil when it encounters a nil value."
         2721  +         (if (= 0 (select "#" ...))
         2722  +             val
         2723  +             (let [els [...]
         2724  +                   e (table.remove els 1)
         2725  +                   el (if (list? e) e (list e))
         2726  +                   tmp (gensym)]
         2727  +               (table.insert el 2 tmp)
         2728  +               `(let [,tmp ,val]
         2729  +                  (if ,tmp
         2730  +                      (-?> ,el ,(unpack els))
         2731  +                      ,tmp)))))
         2732  + "-?>>" (fn [val ...]
         2733  +         "Nil-safe thread-last macro.
         2734  +Same as ->> except will short-circuit with nil when it encounters a nil value."
         2735  +          (if (= 0 (select "#" ...))
         2736  +              val
         2737  +              (let [els [...]
         2738  +                    e (table.remove els 1)
         2739  +                    el (if (list? e) e (list e))
         2740  +                    tmp (gensym)]
         2741  +                (table.insert el tmp)
         2742  +                `(let [,tmp ,val]
         2743  +                   (if ,tmp
         2744  +                       (-?>> ,el ,(unpack els))
         2745  +                       ,tmp)))))
         2746  + :doto (fn [val ...]
         2747  +         "Evaluates val and splices it into the first argument of subsequent forms."
         2748  +         (let [name (gensym)
         2749  +               form `(let [,name ,val])]
         2750  +           (each [_ elt (pairs [...])]
         2751  +             (table.insert elt 2 name)
         2752  +             (table.insert form elt))
         2753  +           (table.insert form name)
         2754  +           form))
         2755  + :when (fn [condition body1 ...]
         2756  +         "Evaluate body for side-effects only when condition is truthy."
         2757  +         (assert body1 "expected body")
         2758  +         `(if ,condition
         2759  +              (do ,body1 ,...)))
         2760  + :partial (fn [f ...]
         2761  +            "Returns a function with all arguments partially applied to f."
         2762  +            (let [body (list f ...)]
         2763  +              (table.insert body _VARARG)
         2764  +              `(fn [,_VARARG] ,body)))
         2765  + :lambda (fn [...]
         2766  +           "Function literal with arity checking.
         2767  +Will throw an exception if a declared argument is passed in as nil, unless
         2768  +that argument name begins with ?."
         2769  +           (let [args [...]
         2770  +                 has-internal-name? (sym? (. args 1))
         2771  +                 arglist (if has-internal-name? (. args 2) (. args 1))
         2772  +                 docstring-position (if has-internal-name? 3 2)
         2773  +                 has-docstring? (and (> (# args) docstring-position)
         2774  +                                     (= :string (type (. args docstring-position))))
         2775  +                 arity-check-position (- 4 (if has-internal-name? 0 1) (if has-docstring? 0 1))]
         2776  +             (fn check! [a]
         2777  +               (if (table? a)
         2778  +                   (each [_ a (pairs a)]
         2779  +                     (check! a))
         2780  +                   (and (not (: (tostring a) :match "^?"))
         2781  +                        (not= (tostring a) "&")
         2782  +                        (not= (tostring a) "..."))
         2783  +                   (table.insert args arity-check-position
         2784  +                                 `(assert (not= nil ,a)
         2785  +                                          (: "Missing argument %s on %s:%s"
         2786  +                                             :format ,(tostring a)
         2787  +                                             ,(or a.filename "unknown")
         2788  +                                             ,(or a.line "?"))))))
         2789  +             (assert (> (length args) 1) "missing body expression")
         2790  +             (each [_ a (ipairs arglist)]
         2791  +               (check! a))
         2792  +             `(fn ,(unpack args))))
         2793  + :macro (fn macro [name ...]
         2794  +          "Define a single macro."
         2795  +          (assert (sym? name) "expected symbol for macro name")
         2796  +          (local args [...])
         2797  +          `(macros { ,(tostring name) (fn ,name ,(unpack args))}))
         2798  + :match
         2799  +(fn match [val ...]
         2800  +  "Perform pattern matching on val. See reference for details."
         2801  +  ;; this function takes the AST of values and a single pattern and returns a
         2802  +  ;; condition to determine if it matches as well as a list of bindings to
         2803  +  ;; introduce for the duration of the body if it does match.
         2804  +  (fn match-pattern [vals pattern unifications]
         2805  +    ;; we have to assume we're matching against multiple values here until we
         2806  +    ;; know we're either in a multi-valued clause (in which case we know the #
         2807  +    ;; of vals) or we're not, in which case we only care about the first one.
         2808  +    (let [[val] vals]
         2809  +      (if (or (and (sym? pattern) ; unification with outer locals (or nil)
         2810  +                   (not= :_ (tostring pattern)) ; never unify _
         2811  +                   (or (in-scope? pattern)
         2812  +                       (= :nil (tostring pattern))))
         2813  +              (and (multi-sym? pattern)
         2814  +                   (in-scope? (. (multi-sym? pattern) 1))))
         2815  +          (values `(= ,val ,pattern) [])
         2816  +          ;; unify a local we've seen already
         2817  +          (and (sym? pattern)
         2818  +               (. unifications (tostring pattern)))
         2819  +          (values `(= ,(. unifications (tostring pattern)) ,val) [])
         2820  +          ;; bind a fresh local
         2821  +          (sym? pattern)
         2822  +          (let [wildcard? (= (tostring pattern) "_")]
         2823  +            (if (not wildcard?) (tset unifications (tostring pattern) val))
         2824  +            (values (if (or wildcard? (: (tostring pattern) :find "^?"))
         2825  +                        true `(not= ,(sym :nil) ,val))
         2826  +                    [pattern val]))
         2827  +          ;; guard clause
         2828  +          (and (list? pattern) (sym? (. pattern 2)) (= :? (tostring (. pattern 2))))
         2829  +          (let [(pcondition bindings) (match-pattern vals (. pattern 1)
         2830  +                                                     unifications)
         2831  +                condition `(and ,pcondition)]
         2832  +            (for [i 3 (# pattern)] ; splice in guard clauses
         2833  +              (table.insert condition (. pattern i)))
         2834  +            (values `(let ,bindings ,condition) bindings))
         2835  +
         2836  +          ;; multi-valued patterns (represented as lists)
         2837  +          (list? pattern)
         2838  +          (let [condition `(and)
         2839  +                bindings []]
         2840  +            (each [i pat (ipairs pattern)]
         2841  +              (let [(subcondition subbindings) (match-pattern [(. vals i)] pat
         2842  +                                                              unifications)]
         2843  +                (table.insert condition subcondition)
         2844  +                (each [_ b (ipairs subbindings)]
         2845  +                  (table.insert bindings b))))
         2846  +            (values condition bindings))
         2847  +          ;; table patterns)
         2848  +          (= (type pattern) :table)
         2849  +          (let [condition `(and (= (type ,val) :table))
         2850  +                bindings []]
         2851  +            (each [k pat (pairs pattern)]
         2852  +              (if (and (sym? pat) (= "&" (tostring pat)))
         2853  +                  (do (assert (not (. pattern (+ k 2)))
         2854  +                              "expected rest argument in final position")
         2855  +                      (table.insert bindings (. pattern (+ k 1)))
         2856  +                      (table.insert bindings [`(select ,k ((or _G.unpack table.unpack)
         2857  +                                                           ,val))]))
         2858  +                  (and (= :number (type k))
         2859  +                       (= "&" (tostring (. pattern (- k 1)))))
         2860  +                  nil ; don't process the pattern right after &; already got it
         2861  +                  (let [subval `(. ,val ,k)
         2862  +                        (subcondition subbindings) (match-pattern [subval] pat
         2863  +                                                                  unifications)]
         2864  +                    (table.insert condition subcondition)
         2865  +                    (each [_ b (ipairs subbindings)]
         2866  +                      (table.insert bindings b)))))
         2867  +            (values condition bindings))
         2868  +          ;; literal value
         2869  +          (values `(= ,val ,pattern) []))))
         2870  +  (fn match-condition [vals clauses]
         2871  +    (let [out `(if)]
         2872  +      (for [i 1 (length clauses) 2]
         2873  +        (let [pattern (. clauses i)
         2874  +              body (. clauses (+ i 1))
         2875  +              (condition bindings) (match-pattern vals pattern {})]
         2876  +          (table.insert out condition)
         2877  +          (table.insert out `(let ,bindings ,body))))
         2878  +      out))
         2879  +  ;; how many multi-valued clauses are there? return a list of that many gensyms
         2880  +  (fn val-syms [clauses]
         2881  +    (let [syms (list (gensym))]
         2882  +      (for [i 1 (length clauses) 2]
         2883  +        (if (list? (. clauses i))
         2884  +            (each [valnum (ipairs (. clauses i))]
         2885  +              (if (not (. syms valnum))
         2886  +                  (tset syms valnum (gensym))))))
         2887  +      syms))
         2888  +  ;; wrap it in a way that prevents double-evaluation of the matched value
         2889  +  (let [clauses [...]
         2890  +        vals (val-syms clauses)]
         2891  +    (if (not= 0 (% (length clauses) 2)) ; treat odd final clause as default
         2892  +        (table.insert clauses (length clauses) (sym :_)))
         2893  +    ;; protect against multiple evaluation of the value, bind against as
         2894  +    ;; many values as we ever match against in the clauses.
         2895  +    (list (sym :let) [vals val]
         2896  +          (match-condition vals clauses))))
         2897  + }
         2898  +]===]
         2899  +do
         2900  +    -- docstrings rely on having a place to "put" metadata; we use the module
         2901  +    -- system for that. but if you try to require the module while it's being
         2902  +    -- loaded, you get a stack overflow. so we fake out the module for the
         2903  +    -- purposes of boostrapping the built-in macros here.
         2904  +    local moduleName = "__fennel-bootstrap__"
         2905  +    package.preload[moduleName] = function() return module end
         2906  +    local env = makeCompilerEnv(nil, COMPILER_SCOPE, {})
         2907  +    local macros = eval(stdmacros, {
         2908  +                            env = env,
         2909  +                            scope = makeScope(COMPILER_SCOPE),
         2910  +                            -- assume the code to load globals doesn't have any
         2911  +                            -- mistaken globals, otherwise this can be
         2912  +                            -- problematic when loading fennel in contexts
         2913  +                            -- where _G is an empty table with an __index
         2914  +                            -- metamethod. (openresty)
         2915  +                            allowedGlobals = false,
         2916  +                            useMetadata = true,
         2917  +                            filename = "built-ins",
         2918  +                            moduleName = moduleName })
         2919  +    kvmap(macros,
         2920  +          function(name, fn) return name, macroToSpecial(fn, name) end,
         2921  +          SPECIALS)
         2922  +    package.preload[moduleName] = nil
         2923  +end
         2924  +SPECIALS['λ'] = SPECIALS['lambda']
         2925  +
         2926  +return module

Added src/worpt/lib/fennelview.lua.

            1  +local function view_quote(str)
            2  +  return ("\"" .. str:gsub("\"", "\\\"") .. "\"")
            3  +end
            4  +local short_control_char_escapes = {["\11"] = "\\v", ["\12"] = "\\f", ["\13"] = "\\r", ["\7"] = "\\a", ["\8"] = "\\b", ["\9"] = "\\t", ["\n"] = "\\n"}
            5  +local long_control_char_escapes = nil
            6  +do
            7  +  local long = {}
            8  +  for i = 0, 31 do
            9  +    local ch = string.char(i)
           10  +    if not short_control_char_escapes[ch] then
           11  +      short_control_char_escapes[ch] = ("\\" .. i)
           12  +      long[ch] = ("\\%03d"):format(i)
           13  +    end
           14  +  end
           15  +  long_control_char_escapes = long
           16  +end
           17  +local function escape(str)
           18  +  local str0 = str:gsub("\\", "\\\\")
           19  +  local str1 = str0:gsub("(%c)%f[0-9]", long_control_char_escapes)
           20  +  return str1:gsub("%c", short_control_char_escapes)
           21  +end
           22  +local function sequence_key_3f(k, len)
           23  +  return ((type(k) == "number") and (1 <= k) and (k <= len) and (math.floor(k) == k))
           24  +end
           25  +local type_order = {["function"] = 5, boolean = 2, number = 1, string = 3, table = 4, thread = 7, userdata = 6}
           26  +local function sort_keys(a, b)
           27  +  local ta = type(a)
           28  +  local tb = type(b)
           29  +  if ((ta == tb) and (ta ~= "boolean") and ((ta == "string") or (ta == "number"))) then
           30  +    return (a < b)
           31  +  else
           32  +    local dta = type_order[a]
           33  +    local dtb = type_order[b]
           34  +    if (dta and dtb) then
           35  +      return (dta < dtb)
           36  +    elseif dta then
           37  +      return true
           38  +    elseif dtb then
           39  +      return false
           40  +    elseif "else" then
           41  +      return (ta < tb)
           42  +    end
           43  +  end
           44  +end
           45  +local function get_sequence_length(t)
           46  +  local len = 1
           47  +  for i in ipairs(t) do
           48  +    len = i
           49  +  end
           50  +  return len
           51  +end
           52  +local function get_nonsequential_keys(t)
           53  +  local keys = {}
           54  +  local sequence_length = get_sequence_length(t)
           55  +  for k in pairs(t) do
           56  +    if not sequence_key_3f(k, sequence_length) then
           57  +      table.insert(keys, k)
           58  +    end
           59  +  end
           60  +  table.sort(keys, sort_keys)
           61  +  return keys, sequence_length
           62  +end
           63  +local function count_table_appearances(t, appearances)
           64  +  if (type(t) == "table") then
           65  +    if not appearances[t] then
           66  +      appearances[t] = 1
           67  +      for k, v in pairs(t) do
           68  +        count_table_appearances(k, appearances)
           69  +        count_table_appearances(v, appearances)
           70  +      end
           71  +    end
           72  +  else
           73  +    if (t and (t == t)) then
           74  +      appearances[t] = ((appearances[t] or 0) + 1)
           75  +    end
           76  +  end
           77  +  return appearances
           78  +end
           79  +local put_value = nil
           80  +local function puts(self, ...)
           81  +  for _, v in ipairs({...}) do
           82  +    table.insert(self.buffer, v)
           83  +  end
           84  +  return nil
           85  +end
           86  +local function tabify(self)
           87  +  return puts(self, "\n", (self.indent):rep(self.level))
           88  +end
           89  +local function already_visited_3f(self, v)
           90  +  return (self.ids[v] ~= nil)
           91  +end
           92  +local function get_id(self, v)
           93  +  local id = self.ids[v]
           94  +  if not id then
           95  +    local tv = type(v)
           96  +    id = ((self["max-ids"][tv] or 0) + 1)
           97  +    self["max-ids"][tv] = id
           98  +    self.ids[v] = id
           99  +  end
          100  +  return tostring(id)
          101  +end
          102  +local function put_sequential_table(self, t, len)
          103  +  puts(self, "[")
          104  +  self.level = (self.level + 1)
          105  +  for i = 1, len do
          106  +    local _0_ = (1 + len)
          107  +    if ((1 < i) and (i < _0_)) then
          108  +      puts(self, " ")
          109  +    end
          110  +    put_value(self, t[i])
          111  +  end
          112  +  self.level = (self.level - 1)
          113  +  return puts(self, "]")
          114  +end
          115  +local function put_key(self, k)
          116  +  if ((type(k) == "string") and k:find("^[-%w?\\^_!$%&*+./@:|<=>]+$")) then
          117  +    return puts(self, ":", k)
          118  +  else
          119  +    return put_value(self, k)
          120  +  end
          121  +end
          122  +local function put_kv_table(self, t, ordered_keys)
          123  +  puts(self, "{")
          124  +  self.level = (self.level + 1)
          125  +  for _, k in ipairs(ordered_keys) do
          126  +    tabify(self)
          127  +    put_key(self, k)
          128  +    puts(self, " ")
          129  +    put_value(self, t[k])
          130  +  end
          131  +  for i, v in ipairs(t) do
          132  +    tabify(self)
          133  +    put_key(self, i)
          134  +    puts(self, " ")
          135  +    put_value(self, v)
          136  +  end
          137  +  self.level = (self.level - 1)
          138  +  tabify(self)
          139  +  return puts(self, "}")
          140  +end
          141  +local function put_table(self, t)
          142  +  local metamethod = nil
          143  +  do
          144  +    local _0_0 = t
          145  +    if _0_0 then
          146  +      local _1_0 = getmetatable(_0_0)
          147  +      if _1_0 then
          148  +        metamethod = _1_0.__fennelview
          149  +      else
          150  +        metamethod = _1_0
          151  +      end
          152  +    else
          153  +      metamethod = _0_0
          154  +    end
          155  +  end
          156  +  if (already_visited_3f(self, t) and self["detect-cycles?"]) then
          157  +    return puts(self, "#<table ", get_id(self, t), ">")
          158  +  elseif (self.level >= self.depth) then
          159  +    return puts(self, "{...}")
          160  +  elseif metamethod then
          161  +    return puts(self, metamethod(t, self.fennelview))
          162  +  elseif "else" then
          163  +    local non_seq_keys, len = get_nonsequential_keys(t)
          164  +    local id = get_id(self, t)
          165  +    if ((1 < (self.appearances[t] or 0)) and self["detect-cycles?"]) then
          166  +      return puts(self, "#<table", id, ">")
          167  +    elseif ((#non_seq_keys == 0) and (#t == 0)) then
          168  +      return puts(self, "{}")
          169  +    elseif (#non_seq_keys == 0) then
          170  +      return put_sequential_table(self, t, len)
          171  +    elseif "else" then
          172  +      return put_kv_table(self, t, non_seq_keys)
          173  +    end
          174  +  end
          175  +end
          176  +local function _0_(self, v)
          177  +  local tv = type(v)
          178  +  if (tv == "string") then
          179  +    return puts(self, view_quote(escape(v)))
          180  +  elseif ((tv == "number") or (tv == "boolean") or (tv == "nil")) then
          181  +    return puts(self, tostring(v))
          182  +  elseif (tv == "table") then
          183  +    return put_table(self, v)
          184  +  elseif "else" then
          185  +    return puts(self, "#<", tostring(v), ">")
          186  +  end
          187  +end
          188  +put_value = _0_
          189  +local function one_line(str)
          190  +  local ret = str:gsub("\n", " "):gsub("%[ ", "["):gsub(" %]", "]"):gsub("%{ ", "{"):gsub(" %}", "}"):gsub("%( ", "("):gsub(" %)", ")")
          191  +  return ret
          192  +end
          193  +local function fennelview(x, options)
          194  +  local options0 = (options or {})
          195  +  local inspector = nil
          196  +  local function _1_(_241)
          197  +    return fennelview(_241, options0)
          198  +  end
          199  +  local function _2_()
          200  +    if options0["one-line"] then
          201  +      return ""
          202  +    else
          203  +      return "  "
          204  +    end
          205  +  end
          206  +  inspector = {["detect-cycles?"] = not (false == options0["detect-cycles?"]), ["max-ids"] = {}, appearances = count_table_appearances(x, {}), buffer = {}, depth = (options0.depth or 128), fennelview = _1_, ids = {}, indent = (options0.indent or _2_()), level = 0}
          207  +  put_value(inspector, x)
          208  +  do
          209  +    local str = table.concat(inspector.buffer)
          210  +    if options0["one-line"] then
          211  +      return one_line(str)
          212  +    else
          213  +      return str
          214  +    end
          215  +  end
          216  +end
          217  +return fennelview

Added src/worpt/lib/lume.lua.

            1  +--
            2  +-- lume
            3  +--
            4  +-- Copyright (c) 2018 rxi
            5  +--
            6  +-- Permission is hereby granted, free of charge, to any person obtaining a copy of
            7  +-- this software and associated documentation files (the "Software"), to deal in
            8  +-- the Software without restriction, including without limitation the rights to
            9  +-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
           10  +-- of the Software, and to permit persons to whom the Software is furnished to do
           11  +-- so, subject to the following conditions:
           12  +--
           13  +-- The above copyright notice and this permission notice shall be included in all
           14  +-- copies or substantial portions of the Software.
           15  +--
           16  +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
           17  +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
           18  +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
           19  +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
           20  +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
           21  +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
           22  +-- SOFTWARE.
           23  +--
           24  +
           25  +local lume = { _version = "2.3.0" }
           26  +
           27  +local pairs, ipairs = pairs, ipairs
           28  +local type, assert, unpack = type, assert, unpack or table.unpack
           29  +local tostring, tonumber = tostring, tonumber
           30  +local math_floor = math.floor
           31  +local math_ceil = math.ceil
           32  +local math_atan2 = math.atan2 or math.atan
           33  +local math_sqrt = math.sqrt
           34  +local math_abs = math.abs
           35  +
           36  +local noop = function()
           37  +end
           38  +
           39  +local identity = function(x)
           40  +  return x
           41  +end
           42  +
           43  +local patternescape = function(str)
           44  +  return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1")
           45  +end
           46  +
           47  +local absindex = function(len, i)
           48  +  return i < 0 and (len + i + 1) or i
           49  +end
           50  +
           51  +local iscallable = function(x)
           52  +  if type(x) == "function" then return true end
           53  +  local mt = getmetatable(x)
           54  +  return mt and mt.__call ~= nil
           55  +end
           56  +
           57  +local getiter = function(x)
           58  +  if lume.isarray(x) then
           59  +    return ipairs
           60  +  elseif type(x) == "table" then
           61  +    return pairs
           62  +  end
           63  +  error("expected table", 3)
           64  +end
           65  +
           66  +local iteratee = function(x)
           67  +  if x == nil then return identity end
           68  +  if iscallable(x) then return x end
           69  +  if type(x) == "table" then
           70  +    return function(z)
           71  +      for k, v in pairs(x) do
           72  +        if z[k] ~= v then return false end
           73  +      end
           74  +      return true
           75  +    end
           76  +  end
           77  +  return function(z) return z[x] end
           78  +end
           79  +
           80  +
           81  +
           82  +function lume.clamp(x, min, max)
           83  +  return x < min and min or (x > max and max or x)
           84  +end
           85  +
           86  +
           87  +function lume.round(x, increment)
           88  +  if increment then return lume.round(x / increment) * increment end
           89  +  return x >= 0 and math_floor(x + .5) or math_ceil(x - .5)
           90  +end
           91  +
           92  +
           93  +function lume.sign(x)
           94  +  return x < 0 and -1 or 1
           95  +end
           96  +
           97  +
           98  +function lume.lerp(a, b, amount)
           99  +  return a + (b - a) * lume.clamp(amount, 0, 1)
          100  +end
          101  +
          102  +
          103  +function lume.smooth(a, b, amount)
          104  +  local t = lume.clamp(amount, 0, 1)
          105  +  local m = t * t * (3 - 2 * t)
          106  +  return a + (b - a) * m
          107  +end
          108  +
          109  +
          110  +function lume.pingpong(x)
          111  +  return 1 - math_abs(1 - x % 2)
          112  +end
          113  +
          114  +
          115  +function lume.distance(x1, y1, x2, y2, squared)
          116  +  local dx = x1 - x2
          117  +  local dy = y1 - y2
          118  +  local s = dx * dx + dy * dy
          119  +  return squared and s or math_sqrt(s)
          120  +end
          121  +
          122  +
          123  +function lume.angle(x1, y1, x2, y2)
          124  +  return math_atan2(y2 - y1, x2 - x1)
          125  +end
          126  +
          127  +
          128  +function lume.vector(angle, magnitude)
          129  +  return math.cos(angle) * magnitude, math.sin(angle) * magnitude
          130  +end
          131  +
          132  +
          133  +function lume.random(a, b)
          134  +  if not a then a, b = 0, 1 end
          135  +  if not b then b = 0 end
          136  +  return a + math.random() * (b - a)
          137  +end
          138  +
          139  +
          140  +function lume.randomchoice(t)
          141  +  return t[math.random(#t)]
          142  +end
          143  +
          144  +
          145  +function lume.weightedchoice(t)
          146  +  local sum = 0
          147  +  for _, v in pairs(t) do
          148  +    assert(v >= 0, "weight value less than zero")
          149  +    sum = sum + v
          150  +  end
          151  +  assert(sum ~= 0, "all weights are zero")
          152  +  local rnd = lume.random(sum)
          153  +  for k, v in pairs(t) do
          154  +    if rnd < v then return k end
          155  +    rnd = rnd - v
          156  +  end
          157  +end
          158  +
          159  +
          160  +function lume.isarray(x)
          161  +  return type(x) == "table" and x[1] ~= nil
          162  +end
          163  +
          164  +
          165  +function lume.push(t, ...)
          166  +  local n = select("#", ...)
          167  +  for i = 1, n do
          168  +    t[#t + 1] = select(i, ...)
          169  +  end
          170  +  return ...
          171  +end
          172  +
          173  +
          174  +function lume.remove(t, x)
          175  +  local iter = getiter(t)
          176  +  for i, v in iter(t) do
          177  +    if v == x then
          178  +      if lume.isarray(t) then
          179  +        table.remove(t, i)
          180  +        break
          181  +      else
          182  +        t[i] = nil
          183  +        break
          184  +      end
          185  +    end
          186  +  end
          187  +  return x
          188  +end
          189  +
          190  +
          191  +function lume.clear(t)
          192  +  local iter = getiter(t)
          193  +  for k in iter(t) do
          194  +    t[k] = nil
          195  +  end
          196  +  return t
          197  +end
          198  +
          199  +
          200  +function lume.extend(t, ...)
          201  +  for i = 1, select("#", ...) do
          202  +    local x = select(i, ...)
          203  +    if x then
          204  +      for k, v in pairs(x) do
          205  +        t[k] = v
          206  +      end
          207  +    end
          208  +  end
          209  +  return t
          210  +end
          211  +
          212  +
          213  +function lume.shuffle(t)
          214  +  local rtn = {}
          215  +  for i = 1, #t do
          216  +    local r = math.random(i)
          217  +    if r ~= i then
          218  +      rtn[i] = rtn[r]
          219  +    end
          220  +    rtn[r] = t[i]
          221  +  end
          222  +  return rtn
          223  +end
          224  +
          225  +
          226  +function lume.sort(t, comp)
          227  +  local rtn = lume.clone(t)
          228  +  if comp then
          229  +    if type(comp) == "string" then
          230  +      table.sort(rtn, function(a, b) return a[comp] < b[comp] end)
          231  +    else
          232  +      table.sort(rtn, comp)
          233  +    end
          234  +  else
          235  +    table.sort(rtn)
          236  +  end
          237  +  return rtn
          238  +end
          239  +
          240  +
          241  +function lume.array(...)
          242  +  local t = {}
          243  +  for x in ... do t[#t + 1] = x end
          244  +  return t
          245  +end
          246  +
          247  +
          248  +function lume.each(t, fn, ...)
          249  +  local iter = getiter(t)
          250  +  if type(fn) == "string" then
          251  +    for _, v in iter(t) do v[fn](v, ...) end
          252  +  else
          253  +    for _, v in iter(t) do fn(v, ...) end
          254  +  end
          255  +  return t
          256  +end
          257  +
          258  +
          259  +function lume.map(t, fn)
          260  +  fn = iteratee(fn)
          261  +  local iter = getiter(t)
          262  +  local rtn = {}
          263  +  for k, v in iter(t) do rtn[k] = fn(v) end
          264  +  return rtn
          265  +end
          266  +
          267  +
          268  +function lume.all(t, fn)
          269  +  fn = iteratee(fn)
          270  +  local iter = getiter(t)
          271  +  for _, v in iter(t) do
          272  +    if not fn(v) then return false end
          273  +  end
          274  +  return true
          275  +end
          276  +
          277  +
          278  +function lume.any(t, fn)
          279  +  fn = iteratee(fn)
          280  +  local iter = getiter(t)
          281  +  for _, v in iter(t) do
          282  +    if fn(v) then return true end
          283  +  end
          284  +  return false
          285  +end
          286  +
          287  +
          288  +function lume.reduce(t, fn, first)
          289  +  local acc = first
          290  +  local started = first and true or false
          291  +  local iter = getiter(t)
          292  +  for _, v in iter(t) do
          293  +    if started then
          294  +      acc = fn(acc, v)
          295  +    else
          296  +      acc = v
          297  +      started = true
          298  +    end
          299  +  end
          300  +  assert(started, "reduce of an empty table with no first value")
          301  +  return acc
          302  +end
          303  +
          304  +
          305  +function lume.unique(t)
          306  +  local rtn = {}
          307  +  for k in pairs(lume.invert(t)) do
          308  +    rtn[#rtn + 1] = k
          309  +  end
          310  +  return rtn
          311  +end
          312  +
          313  +
          314  +function lume.filter(t, fn, retainkeys)
          315  +  fn = iteratee(fn)
          316  +  local iter = getiter(t)
          317  +  local rtn = {}
          318  +  if retainkeys then
          319  +    for k, v in iter(t) do
          320  +      if fn(v) then rtn[k] = v end
          321  +    end
          322  +  else
          323  +    for _, v in iter(t) do
          324  +      if fn(v) then rtn[#rtn + 1] = v end
          325  +    end
          326  +  end
          327  +  return rtn
          328  +end
          329  +
          330  +
          331  +function lume.reject(t, fn, retainkeys)
          332  +  fn = iteratee(fn)
          333  +  local iter = getiter(t)
          334  +  local rtn = {}
          335  +  if retainkeys then
          336  +    for k, v in iter(t) do
          337  +      if not fn(v) then rtn[k] = v end
          338  +    end
          339  +  else
          340  +    for _, v in iter(t) do
          341  +      if not fn(v) then rtn[#rtn + 1] = v end
          342  +    end
          343  +  end
          344  +  return rtn
          345  +end
          346  +
          347  +
          348  +function lume.merge(...)
          349  +  local rtn = {}
          350  +  for i = 1, select("#", ...) do
          351  +    local t = select(i, ...)
          352  +    local iter = getiter(t)
          353  +    for k, v in iter(t) do
          354  +      rtn[k] = v
          355  +    end
          356  +  end
          357  +  return rtn
          358  +end
          359  +
          360  +
          361  +function lume.concat(...)
          362  +  local rtn = {}
          363  +  for i = 1, select("#", ...) do
          364  +    local t = select(i, ...)
          365  +    if t ~= nil then
          366  +      local iter = getiter(t)
          367  +      for _, v in iter(t) do
          368  +        rtn[#rtn + 1] = v
          369  +      end
          370  +    end
          371  +  end
          372  +  return rtn
          373  +end
          374  +
          375  +
          376  +function lume.find(t, value)
          377  +  local iter = getiter(t)
          378  +  for k, v in iter(t) do
          379  +    if v == value then return k end
          380  +  end
          381  +  return nil
          382  +end
          383  +
          384  +
          385  +function lume.match(t, fn)
          386  +  fn = iteratee(fn)
          387  +  local iter = getiter(t)
          388  +  for k, v in iter(t) do
          389  +    if fn(v) then return v, k end
          390  +  end
          391  +  return nil
          392  +end
          393  +
          394  +
          395  +function lume.count(t, fn)
          396  +  local count = 0
          397  +  local iter = getiter(t)
          398  +  if fn then
          399  +    fn = iteratee(fn)
          400  +    for _, v in iter(t) do
          401  +      if fn(v) then count = count + 1 end
          402  +    end
          403  +  else
          404  +    if lume.isarray(t) then
          405  +      return #t
          406  +    end
          407  +    for _ in iter(t) do count = count + 1 end
          408  +  end
          409  +  return count
          410  +end
          411  +
          412  +
          413  +function lume.slice(t, i, j)
          414  +  i = i and absindex(#t, i) or 1
          415  +  j = j and absindex(#t, j) or #t
          416  +  local rtn = {}
          417  +  for x = i < 1 and 1 or i, j > #t and #t or j do
          418  +    rtn[#rtn + 1] = t[x]
          419  +  end
          420  +  return rtn
          421  +end
          422  +
          423  +
          424  +function lume.first(t, n)
          425  +  if not n then return t[1] end
          426  +  return lume.slice(t, 1, n)
          427  +end
          428  +
          429  +
          430  +function lume.last(t, n)
          431  +  if not n then return t[#t] end
          432  +  return lume.slice(t, -n, -1)
          433  +end
          434  +
          435  +
          436  +function lume.invert(t)
          437  +  local rtn = {}
          438  +  for k, v in pairs(t) do rtn[v] = k end
          439  +  return rtn
          440  +end
          441  +
          442  +
          443  +function lume.pick(t, ...)
          444  +  local rtn = {}
          445  +  for i = 1, select("#", ...) do
          446  +    local k = select(i, ...)
          447  +    rtn[k] = t[k]
          448  +  end
          449  +  return rtn
          450  +end
          451  +
          452  +
          453  +function lume.keys(t)
          454  +  local rtn = {}
          455  +  local iter = getiter(t)
          456  +  for k in iter(t) do rtn[#rtn + 1] = k end
          457  +  return rtn
          458  +end
          459  +
          460  +
          461  +function lume.clone(t)
          462  +  local rtn = {}
          463  +  for k, v in pairs(t) do rtn[k] = v end
          464  +  return rtn
          465  +end
          466  +
          467  +
          468  +function lume.fn(fn, ...)
          469  +  assert(iscallable(fn), "expected a function as the first argument")
          470  +  local args = { ... }
          471  +  return function(...)
          472  +    local a = lume.concat(args, { ... })
          473  +    return fn(unpack(a))
          474  +  end
          475  +end
          476  +
          477  +
          478  +function lume.once(fn, ...)
          479  +  local f = lume.fn(fn, ...)
          480  +  local done = false
          481  +  return function(...)
          482  +    if done then return end
          483  +    done = true
          484  +    return f(...)
          485  +  end
          486  +end
          487  +
          488  +
          489  +local memoize_fnkey = {}
          490  +local memoize_nil = {}
          491  +
          492  +function lume.memoize(fn)
          493  +  local cache = {}
          494  +  return function(...)
          495  +    local c = cache
          496  +    for i = 1, select("#", ...) do
          497  +      local a = select(i, ...) or memoize_nil
          498  +      c[a] = c[a] or {}
          499  +      c = c[a]
          500  +    end
          501  +    c[memoize_fnkey] = c[memoize_fnkey] or {fn(...)}
          502  +    return unpack(c[memoize_fnkey])
          503  +  end
          504  +end
          505  +
          506  +
          507  +function lume.combine(...)
          508  +  local n = select('#', ...)
          509  +  if n == 0 then return noop end
          510  +  if n == 1 then
          511  +    local fn = select(1, ...)
          512  +    if not fn then return noop end
          513  +    assert(iscallable(fn), "expected a function or nil")
          514  +    return fn
          515  +  end
          516  +  local funcs = {}
          517  +  for i = 1, n do
          518  +    local fn = select(i, ...)
          519  +    if fn ~= nil then
          520  +      assert(iscallable(fn), "expected a function or nil")
          521  +      funcs[#funcs + 1] = fn
          522  +    end
          523  +  end
          524  +  return function(...)
          525  +    for _, f in ipairs(funcs) do f(...) end
          526  +  end
          527  +end
          528  +
          529  +
          530  +function lume.call(fn, ...)
          531  +  if fn then
          532  +    return fn(...)
          533  +  end
          534  +end
          535  +
          536  +
          537  +function lume.time(fn, ...)
          538  +  local start = os.clock()
          539  +  local rtn = {fn(...)}
          540  +  return (os.clock() - start), unpack(rtn)
          541  +end
          542  +
          543  +
          544  +local lambda_cache = {}
          545  +
          546  +function lume.lambda(str)
          547  +  if not lambda_cache[str] then
          548  +    local args, body = str:match([[^([%w,_ ]-)%->(.-)$]])
          549  +    assert(args and body, "bad string lambda")
          550  +    local s = "return function(" .. args .. ")\nreturn " .. body .. "\nend"
          551  +    lambda_cache[str] = lume.dostring(s)
          552  +  end
          553  +  return lambda_cache[str]
          554  +end
          555  +
          556  +
          557  +local serialize
          558  +
          559  +local serialize_map = {
          560  +  [ "boolean" ] = tostring,
          561  +  [ "nil"     ] = tostring,
          562  +  [ "string"  ] = function(v) return string.format("%q", v) end,
          563  +  [ "number"  ] = function(v)
          564  +    if      v ~=  v     then return  "0/0"      --  nan
          565  +    elseif  v ==  1 / 0 then return  "1/0"      --  inf
          566  +    elseif  v == -1 / 0 then return "-1/0" end  -- -inf
          567  +    return tostring(v)
          568  +  end,
          569  +  [ "table"   ] = function(t, stk)
          570  +    stk = stk or {}
          571  +    if stk[t] then error("circular reference") end
          572  +    local rtn = {}
          573  +    stk[t] = true
          574  +    for k, v in pairs(t) do
          575  +      rtn[#rtn + 1] = "[" .. serialize(k, stk) .. "]=" .. serialize(v, stk)
          576  +    end
          577  +    stk[t] = nil
          578  +    return "{" .. table.concat(rtn, ",") .. "}"
          579  +  end
          580  +}
          581  +
          582  +setmetatable(serialize_map, {
          583  +  __index = function(_, k) error("unsupported serialize type: " .. k) end
          584  +})
          585  +
          586  +serialize = function(x, stk)
          587  +  return serialize_map[type(x)](x, stk)
          588  +end
          589  +
          590  +function lume.serialize(x)
          591  +  return serialize(x)
          592  +end
          593  +
          594  +
          595  +function lume.deserialize(str)
          596  +  return lume.dostring("return " .. str)
          597  +end
          598  +
          599  +
          600  +function lume.split(str, sep)
          601  +  if not sep then
          602  +    return lume.array(str:gmatch("([%S]+)"))
          603  +  else
          604  +    assert(sep ~= "", "empty separator")
          605  +    local psep = patternescape(sep)
          606  +    return lume.array((str..sep):gmatch("(.-)("..psep..")"))
          607  +  end
          608  +end
          609  +
          610  +
          611  +function lume.trim(str, chars)
          612  +  if not chars then return str:match("^[%s]*(.-)[%s]*$") end
          613  +  chars = patternescape(chars)
          614  +  return str:match("^[" .. chars .. "]*(.-)[" .. chars .. "]*$")
          615  +end
          616  +
          617  +
          618  +function lume.wordwrap(str, limit)
          619  +  limit = limit or 72
          620  +  local check
          621  +  if type(limit) == "number" then
          622  +    check = function(s) return #s >= limit end
          623  +  else
          624  +    check = limit
          625  +  end
          626  +  local rtn = {}
          627  +  local line = ""
          628  +  for word, spaces in str:gmatch("(%S+)(%s*)") do
          629  +    local s = line .. word
          630  +    if check(s) then
          631  +      table.insert(rtn, line .. "\n")
          632  +      line = word
          633  +    else
          634  +      line = s
          635  +    end
          636  +    for c in spaces:gmatch(".") do
          637  +      if c == "\n" then
          638  +        table.insert(rtn, line .. "\n")
          639  +        line = ""
          640  +      else
          641  +        line = line .. c
          642  +      end
          643  +    end
          644  +  end
          645  +  table.insert(rtn, line)
          646  +  return table.concat(rtn)
          647  +end
          648  +
          649  +
          650  +function lume.format(str, vars)
          651  +  if not vars then return str end
          652  +  local f = function(x)
          653  +    return tostring(vars[x] or vars[tonumber(x)] or "{" .. x .. "}")
          654  +  end
          655  +  return (str:gsub("{(.-)}", f))
          656  +end
          657  +
          658  +
          659  +function lume.trace(...)
          660  +  local info = debug.getinfo(2, "Sl")
          661  +  local t = { info.short_src .. ":" .. info.currentline .. ":" }
          662  +  for i = 1, select("#", ...) do
          663  +    local x = select(i, ...)
          664  +    if type(x) == "number" then
          665  +      x = string.format("%g", lume.round(x, .01))
          666  +    end
          667  +    t[#t + 1] = tostring(x)
          668  +  end
          669  +  print(table.concat(t, " "))
          670  +end
          671  +
          672  +
          673  +function lume.dostring(str)
          674  +  return assert((loadstring or load)(str))()
          675  +end
          676  +
          677  +
          678  +function lume.uuid()
          679  +  local fn = function(x)
          680  +    local r = math.random(16) - 1
          681  +    r = (x == "x") and (r + 1) or (r % 4) + 9
          682  +    return ("0123456789abcdef"):sub(r, r)
          683  +  end
          684  +  return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn))
          685  +end
          686  +
          687  +
          688  +function lume.hotswap(modname)
          689  +  local oldglobal = lume.clone(_G)
          690  +  local updated = {}
          691  +  local function update(old, new)
          692  +    if updated[old] then return end
          693  +    updated[old] = true
          694  +    local oldmt, newmt = getmetatable(old), getmetatable(new)
          695  +    if oldmt and newmt then update(oldmt, newmt) end
          696  +    for k, v in pairs(new) do
          697  +      if type(v) == "table" then update(old[k], v) else old[k] = v end
          698  +    end
          699  +  end
          700  +  local err = nil
          701  +  local function onerror(e)
          702  +    for k in pairs(_G) do _G[k] = oldglobal[k] end
          703  +    err = lume.trim(e)
          704  +  end
          705  +  local ok, oldmod = pcall(require, modname)
          706  +  oldmod = ok and oldmod or nil
          707  +  xpcall(function()
          708  +    package.loaded[modname] = nil
          709  +    local newmod = require(modname)
          710  +    if type(oldmod) == "table" then update(oldmod, newmod) end
          711  +    for k, v in pairs(oldglobal) do
          712  +      if v ~= _G[k] and type(v) == "table" then
          713  +        update(v, _G[k])
          714  +        _G[k] = v
          715  +      end
          716  +    end
          717  +  end, onerror)
          718  +  package.loaded[modname] = oldmod
          719  +  if err then return nil, err end
          720  +  return oldmod
          721  +end
          722  +
          723  +
          724  +local ripairs_iter = function(t, i)
          725  +  i = i - 1
          726  +  local v = t[i]
          727  +  if v ~= nil then 
          728  +    return i, v
          729  +  end
          730  +end
          731  +
          732  +function lume.ripairs(t)
          733  +  return ripairs_iter, t, (#t + 1)
          734  +end
          735  +
          736  +
          737  +function lume.color(str, mul)
          738  +  mul = mul or 1
          739  +  local r, g, b, a
          740  +  r, g, b = str:match("#(%x%x)(%x%x)(%x%x)")
          741  +  if r then
          742  +    r = tonumber(r, 16) / 0xff
          743  +    g = tonumber(g, 16) / 0xff
          744  +    b = tonumber(b, 16) / 0xff
          745  +    a = 1
          746  +  elseif str:match("rgba?%s*%([%d%s%.,]+%)") then
          747  +    local f = str:gmatch("[%d.]+")
          748  +    r = (f() or 0) / 0xff
          749  +    g = (f() or 0) / 0xff
          750  +    b = (f() or 0) / 0xff
          751  +    a = f() or 1
          752  +  else
          753  +    error(("bad color string '%s'"):format(str))
          754  +  end
          755  +  return r * mul, g * mul, b * mul, a * mul
          756  +end
          757  +
          758  +
          759  +local chain_mt = {}
          760  +chain_mt.__index = lume.map(lume.filter(lume, iscallable, true),
          761  +  function(fn)
          762  +    return function(self, ...)
          763  +      self._value = fn(self._value, ...)
          764  +      return self
          765  +    end
          766  +  end)
          767  +chain_mt.__index.result = function(x) return x._value end
          768  +
          769  +function lume.chain(value)
          770  +  return setmetatable({ _value = value }, chain_mt)
          771  +end
          772  +
          773  +setmetatable(lume,  {
          774  +  __call = function(_, ...)
          775  +    return lume.chain(...)
          776  +  end
          777  +})
          778  +
          779  +
          780  +return lume

Added src/worpt/lib/stdio.fnl.

            1  +(require "love.event")
            2  +(local view (require "lib.fennelview"))
            3  +
            4  +;; This module exists in order to expose stdio over a channel so that it
            5  +;; can be used in a non-blocking way from another thread.
            6  +
            7  +(local (event channel) ...)
            8  +
            9  +(when channel
           10  +  (let [prompt (fn [] (io.write "> ") (io.flush) (io.read "*l"))]
           11  +    ((fn looper [input]
           12  +       (when input
           13  +         ;; This is consumed by love.handlers[event]
           14  +         (love.event.push event input)
           15  +         (let [output (: channel :demand)]
           16  +           ;; There is probably a more efficient way of determining an error
           17  +           (if (and (. output 2) (= "Error:" (. output 2)))
           18  +               (print (view output))
           19  +               (each [_ ret (ipairs output)]
           20  +                 (print ret))))
           21  +         (io.flush)
           22  +         (looper (prompt)))) (prompt))))
           23  +
           24  +{:start (fn start-repl []
           25  +
           26  +          (let [code (love.filesystem.read "stdio.fnl")
           27  +                luac (if code
           28  +                         (love.filesystem.newFileData
           29  +                          (fennel.compileString code) "io")
           30  +                         (love.filesystem.read "lib/stdio.lua"))
           31  +                thread (love.thread.newThread luac)
           32  +                io-channel (love.thread.newChannel)
           33  +                coro (coroutine.create fennel.repl)
           34  +                out (fn [val]
           35  +                      (: io-channel :push  val))
           36  +                options {:readChunk coroutine.yield
           37  +                         :onValues out
           38  +                         :onError (fn [kind ...] (out [kind "Error:" ...]))
           39  +                         :pp view
           40  +                         :moduleName "lib.fennel"}]
           41  +            ;; this thread will send "eval" events for us to consume:
           42  +            (coroutine.resume coro options)
           43  +            (: thread :start "eval" io-channel)
           44  +            (set love.handlers.eval
           45  +                 (fn [input]
           46  +                   (coroutine.resume coro  (.. input "\n"))))))}

Added src/worpt/lib/stdio.lua.

            1  + require("love.event")
            2  + local view = require("lib.fennelview") local event, channel = ... local function _0_(...) if channel then
            3  +
            4  +
            5  +
            6  +
            7  +
            8  +
            9  +
           10  + local prompt = nil local function _0_() io.write("> ") io.flush() return io.read("*l") end prompt = _0_
           11  + local function looper(input) if input then
           12  +
           13  +
           14  + love.event.push(event, input)
           15  + do local output = channel:demand()
           16  +
           17  + if (output[2] and ("Error:" == output[2])) then
           18  + print(view(output)) else
           19  + for _, ret in ipairs(output) do
           20  + print(ret) end end end
           21  + io.flush()
           22  + return looper(prompt()) end end return looper(prompt()) end end _0_(...)
           23  +
           24  + local function start_repl()
           25  +
           26  + local code = love.filesystem.read("stdio.fnl") local luac = nil
           27  + if code then
           28  + luac = love.filesystem.newFileData(fennel.compileString(code), "io") else
           29  +
           30  + luac = love.filesystem.read("lib/stdio.lua") end
           31  + local thread = love.thread.newThread(luac)
           32  + local io_channel = love.thread.newChannel()
           33  + local coro = coroutine.create(fennel.repl) local out = nil
           34  + local function _2_(val)
           35  + return io_channel:push(val) end out = _2_ local options = nil
           36  +
           37  +
           38  + local function _3_(kind, ...) return out({kind, "Error:", ...}) end options = {moduleName = "lib.fennel", onError = _3_, onValues = out, pp = view, readChunk = coroutine.yield}
           39  +
           40  +
           41  +
           42  + coroutine.resume(coro, options)
           43  + thread:start("eval", io_channel)
           44  +
           45  + local function _4_(input)
           46  + return coroutine.resume(coro, (input .. "\n")) end love.handlers.eval = _4_ return nil end return {start = start_repl}

Added src/worpt/lib/sti/graphics.lua.

            1  +local lg       = _G.love.graphics
            2  +local graphics = { isCreated = lg and true or false }
            3  +
            4  +function graphics.newSpriteBatch(...)
            5  +	if graphics.isCreated then
            6  +		return lg.newSpriteBatch(...)
            7  +	end
            8  +end
            9  +
           10  +function graphics.newCanvas(...)
           11  +	if graphics.isCreated then
           12  +		return lg.newCanvas(...)
           13  +	end
           14  +end
           15  +
           16  +function graphics.newImage(...)
           17  +	if graphics.isCreated then
           18  +		return lg.newImage(...)
           19  +	end
           20  +end
           21  +
           22  +function graphics.newQuad(...)
           23  +	if graphics.isCreated then
           24  +		return lg.newQuad(...)
           25  +	end
           26  +end
           27  +
           28  +function graphics.getCanvas(...)
           29  +	if graphics.isCreated then
           30  +		return lg.getCanvas(...)
           31  +	end
           32  +end
           33  +
           34  +function graphics.setCanvas(...)
           35  +	if graphics.isCreated then
           36  +		return lg.setCanvas(...)
           37  +	end
           38  +end
           39  +
           40  +function graphics.clear(...)
           41  +	if graphics.isCreated then
           42  +		return lg.clear(...)
           43  +	end
           44  +end
           45  +
           46  +function graphics.push(...)
           47  +	if graphics.isCreated then
           48  +		return lg.push(...)
           49  +	end
           50  +end
           51  +
           52  +function graphics.origin(...)
           53  +	if graphics.isCreated then
           54  +		return lg.origin(...)
           55  +	end
           56  +end
           57  +
           58  +function graphics.scale(...)
           59  +	if graphics.isCreated then
           60  +		return lg.scale(...)
           61  +	end
           62  +end
           63  +
           64  +function graphics.translate(...)
           65  +	if graphics.isCreated then
           66  +		return lg.translate(...)
           67  +	end
           68  +end
           69  +
           70  +function graphics.pop(...)
           71  +	if graphics.isCreated then
           72  +		return lg.pop(...)
           73  +	end
           74  +end
           75  +
           76  +function graphics.draw(...)
           77  +	if graphics.isCreated then
           78  +		return lg.draw(...)
           79  +	end
           80  +end
           81  +
           82  +function graphics.rectangle(...)
           83  +	if graphics.isCreated then
           84  +		return lg.rectangle(...)
           85  +	end
           86  +end
           87  +
           88  +function graphics.getColor(...)
           89  +	if graphics.isCreated then
           90  +		return lg.getColor(...)
           91  +	end
           92  +end
           93  +
           94  +function graphics.setColor(...)
           95  +	if graphics.isCreated then
           96  +		return lg.setColor(...)
           97  +	end
           98  +end
           99  +
          100  +function graphics.line(...)
          101  +	if graphics.isCreated then
          102  +		return lg.line(...)
          103  +	end
          104  +end
          105  +
          106  +function graphics.polygon(...)
          107  +	if graphics.isCreated then
          108  +		return lg.polygon(...)
          109  +	end
          110  +end
          111  +
          112  +function graphics.points(...)
          113  +	if graphics.isCreated then
          114  +		return lg.points(...)
          115  +	end
          116  +end
          117  +
          118  +function graphics.getWidth()
          119  +	if graphics.isCreated then
          120  +		return lg.getWidth()
          121  +	end
          122  +	return 0
          123  +end
          124  +
          125  +function graphics.getHeight()
          126  +	if graphics.isCreated then
          127  +		return lg.getHeight()
          128  +	end
          129  +	return 0
          130  +end
          131  +
          132  +return graphics

Added src/worpt/lib/sti/init.lua.

            1  +--- Simple and fast Tiled map loader and renderer.
            2  +-- @module sti
            3  +-- @author Landon Manning
            4  +-- @copyright 2019
            5  +-- @license MIT/X11
            6  +
            7  +local STI = {
            8  +	_LICENSE     = "MIT/X11",
            9  +	_URL         = "https://github.com/karai17/Simple-Tiled-Implementation",
           10  +	_VERSION     = "1.2.3.0",
           11  +	_DESCRIPTION = "Simple Tiled Implementation is a Tiled Map Editor library designed for the *awesome* LÖVE framework.",
           12  +	cache        = {}
           13  +}
           14  +STI.__index = STI
           15  +
           16  +local love  = _G.love
           17  +local cwd   = (...):gsub('%.init$', '') .. "."
           18  +local utils = require(cwd .. "utils")
           19  +local ceil  = math.ceil
           20  +local floor = math.floor
           21  +local lg    = require(cwd .. "graphics")
           22  +local Map   = {}
           23  +Map.__index = Map
           24  +
           25  +local function new(map, plugins, ox, oy)
           26  +	local dir = ""
           27  +
           28  +	if type(map) == "table" then
           29  +		map = setmetatable(map, Map)
           30  +	else
           31  +		-- Check for valid map type
           32  +		local ext = map:sub(-4, -1)
           33  +		assert(ext == ".lua", string.format(
           34  +			"Invalid file type: %s. File must be of type: lua.",
           35  +			ext
           36  +		))
           37  +
           38  +		-- Get directory of map
           39  +		dir = map:reverse():find("[/\\]") or ""
           40  +		if dir ~= "" then
           41  +			dir = map:sub(1, 1 + (#map - dir))
           42  +		end
           43  +
           44  +		-- Load map
           45  +		map = setmetatable(assert(love.filesystem.load(map))(), Map)
           46  +	end
           47  +
           48  +	map:init(dir, plugins, ox, oy)
           49  +
           50  +	return map
           51  +end
           52  +
           53  +--- Instance a new map.
           54  +-- @param map Path to the map file or the map table itself
           55  +-- @param plugins A list of plugins to load
           56  +-- @param ox Offset of map on the X axis (in pixels)
           57  +-- @param oy Offset of map on the Y axis (in pixels)
           58  +-- @return table The loaded Map
           59  +function STI.__call(_, map, plugins, ox, oy)
           60  +	return new(map, plugins, ox, oy)
           61  +end
           62  +
           63  +--- Flush image cache.
           64  +function STI:flush()
           65  +	self.cache = {}
           66  +end
           67  +
           68  +--- Map object
           69  +
           70  +--- Instance a new map
           71  +-- @param path Path to the map file
           72  +-- @param plugins A list of plugins to load
           73  +-- @param ox Offset of map on the X axis (in pixels)
           74  +-- @param oy Offset of map on the Y axis (in pixels)
           75  +function Map:init(path, plugins, ox, oy)
           76  +	if type(plugins) == "table" then
           77  +		self:loadPlugins(plugins)
           78  +	end
           79  +
           80  +	self:resize()
           81  +	self.objects       = {}
           82  +	self.tiles         = {}
           83  +	self.tileInstances = {}
           84  +	self.drawRange     = {
           85  +		sx = 1,
           86  +		sy = 1,
           87  +		ex = self.width,
           88  +		ey = self.height,
           89  +	}
           90  +	self.offsetx = ox or 0
           91  +	self.offsety = oy or 0
           92  +
           93  +	self.freeBatchSprites = {}
           94  +	setmetatable(self.freeBatchSprites, { __mode = 'k' })
           95  +
           96  +	-- Set tiles, images
           97  +	local gid = 1
           98  +	for i, tileset in ipairs(self.tilesets) do
           99  +		assert(tileset.image, "STI does not support Tile Collections.\nYou need to create a Texture Atlas.")
          100  +
          101  +		-- Cache images
          102  +		if lg.isCreated then
          103  +			local formatted_path = utils.format_path(path .. tileset.image)
          104  +
          105  +			if not STI.cache[formatted_path] then
          106  +				utils.fix_transparent_color(tileset, formatted_path)
          107  +				utils.cache_image(STI, formatted_path, tileset.image)
          108  +			else
          109  +				tileset.image = STI.cache[formatted_path]
          110  +			end
          111  +		end
          112  +
          113  +		gid = self:setTiles(i, tileset, gid)
          114  +	end
          115  +
          116  +	local layers = {}
          117  +	for _, layer in ipairs(self.layers) do
          118  +		self:groupAppendToList(layers, layer)
          119  +	end
          120  +	self.layers = layers
          121  +
          122  +	-- Set layers
          123  +	for _, layer in ipairs(self.layers) do
          124  +		self:setLayer(layer, path)
          125  +	end
          126  +end
          127  +
          128  +--- Layers from the group are added to the list
          129  +-- @param layers List of layers
          130  +-- @param layer Layer data
          131  +function Map:groupAppendToList(layers, layer)
          132  +	if layer.type == "group" then
          133  +		for _, groupLayer in pairs(layer.layers) do
          134  +			groupLayer.name = layer.name .. "." .. groupLayer.name
          135  +			groupLayer.visible = layer.visible
          136  +			groupLayer.opacity = layer.opacity * groupLayer.opacity
          137  +			groupLayer.offsetx = layer.offsetx + groupLayer.offsetx
          138  +			groupLayer.offsety = layer.offsety + groupLayer.offsety
          139  +
          140  +			for key, property in pairs(layer.properties) do
          141  +				if groupLayer.properties[key] == nil then
          142  +					groupLayer.properties[key] = property
          143  +				end
          144  +			end
          145  +
          146  +			self:groupAppendToList(layers, groupLayer)
          147  +		end
          148  +	else
          149  +		table.insert(layers, layer)
          150  +	end
          151  +end
          152  +
          153  +--- Load plugins
          154  +-- @param plugins A list of plugins to load
          155  +function Map:loadPlugins(plugins)
          156  +	for _, plugin in ipairs(plugins) do
          157  +		local pluginModulePath = cwd .. 'plugins.' .. plugin
          158  +		local ok, pluginModule = pcall(require, pluginModulePath)
          159  +		if ok then
          160  +			for k, func in pairs(pluginModule) do
          161  +				if not self[k] then
          162  +					self[k] = func
          163  +				end
          164  +			end
          165  +		end
          166  +	end
          167  +end
          168  +
          169  +--- Create Tiles
          170  +-- @param index Index of the Tileset
          171  +-- @param tileset Tileset data
          172  +-- @param gid First Global ID in Tileset
          173  +-- @return number Next Tileset's first Global ID
          174  +function Map:setTiles(index, tileset, gid)
          175  +	local quad    = lg.newQuad
          176  +	local imageW  = tileset.imagewidth
          177  +	local imageH  = tileset.imageheight
          178  +	local tileW   = tileset.tilewidth
          179  +	local tileH   = tileset.tileheight
          180  +	local margin  = tileset.margin
          181  +	local spacing = tileset.spacing
          182  +	local w       = utils.get_tiles(imageW, tileW, margin, spacing)
          183  +	local h       = utils.get_tiles(imageH, tileH, margin, spacing)
          184  +
          185  +	for y = 1, h do
          186  +		for x = 1, w do
          187  +			local id    = gid - tileset.firstgid
          188  +			local quadX = (x - 1) * tileW + margin + (x - 1) * spacing
          189  +			local quadY = (y - 1) * tileH + margin + (y - 1) * spacing
          190  +			local type = ""
          191  +			local properties, terrain, animation, objectGroup
          192  +
          193  +			for _, tile in pairs(tileset.tiles) do
          194  +				if tile.id == id then
          195  +					properties  = tile.properties
          196  +					animation   = tile.animation
          197  +					objectGroup = tile.objectGroup
          198  +					type        = tile.type
          199  +
          200  +					if tile.terrain then
          201  +						terrain = {}
          202  +
          203  +						for i = 1, #tile.terrain do
          204  +							terrain[i] = tileset.terrains[tile.terrain[i] + 1]
          205  +						end
          206  +					end
          207  +				end
          208  +			end
          209  +
          210  +			local tile = {
          211  +				id          = id,
          212  +				gid         = gid,
          213  +				tileset     = index,
          214  +				type        = type,
          215  +				quad        = quad(
          216  +					quadX,  quadY,
          217  +					tileW,  tileH,
          218  +					imageW, imageH
          219  +				),
          220  +				properties  = properties or {},
          221  +				terrain     = terrain,
          222  +				animation   = animation,
          223  +				objectGroup = objectGroup,
          224  +				frame       = 1,
          225  +				time        = 0,
          226  +				width       = tileW,
          227  +				height      = tileH,
          228  +				sx          = 1,
          229  +				sy          = 1,
          230  +				r           = 0,
          231  +				offset      = tileset.tileoffset,
          232  +			}
          233  +
          234  +			self.tiles[gid] = tile
          235  +			gid             = gid + 1
          236  +		end
          237  +	end
          238  +
          239  +	return gid
          240  +end
          241  +
          242  +--- Create Layers
          243  +-- @param layer Layer data
          244  +-- @param path (Optional) Path to an Image Layer's image
          245  +function Map:setLayer(layer, path)
          246  +	if layer.encoding then
          247  +		if layer.encoding == "base64" then
          248  +			assert(require "ffi", "Compressed maps require LuaJIT FFI.\nPlease Switch your interperator to LuaJIT or your Tile Layer Format to \"CSV\".")
          249  +			local fd = love.data.decode("string", "base64", layer.data)
          250  +
          251  +			if not layer.compression then
          252  +				layer.data = utils.get_decompressed_data(fd)
          253  +			else
          254  +				assert(love.data.decompress, "zlib and gzip compression require LOVE 11.0+.\nPlease set your Tile Layer Format to \"Base64 (uncompressed)\" or \"CSV\".")
          255  +
          256  +				if layer.compression == "zlib" then
          257  +					local data = love.data.decompress("string", "zlib", fd)
          258  +					layer.data = utils.get_decompressed_data(data)
          259  +				end
          260  +
          261  +				if layer.compression == "gzip" then
          262  +					local data = love.data.decompress("string", "gzip", fd)
          263  +					layer.data = utils.get_decompressed_data(data)
          264  +				end
          265  +			end
          266  +		end
          267  +	end
          268  +
          269  +	layer.x      = (layer.x or 0) + layer.offsetx + self.offsetx
          270  +	layer.y      = (layer.y or 0) + layer.offsety + self.offsety
          271  +	layer.update = function() end
          272  +
          273  +	if layer.type == "tilelayer" then
          274  +		self:setTileData(layer)
          275  +		self:setSpriteBatches(layer)
          276  +		layer.draw = function() self:drawTileLayer(layer) end
          277  +	elseif layer.type == "objectgroup" then
          278  +		self:setObjectData(layer)
          279  +		self:setObjectCoordinates(layer)
          280  +		self:setObjectSpriteBatches(layer)
          281  +		layer.draw = function() self:drawObjectLayer(layer) end
          282  +	elseif layer.type == "imagelayer" then
          283  +		layer.draw = function() self:drawImageLayer(layer) end
          284  +
          285  +		if layer.image ~= "" then
          286  +			local formatted_path = utils.format_path(path .. layer.image)
          287  +			if not STI.cache[formatted_path] then
          288  +				utils.cache_image(STI, formatted_path)
          289  +			end
          290  +
          291  +			layer.image  = STI.cache[formatted_path]
          292  +			layer.width  = layer.image:getWidth()
          293  +			layer.height = layer.image:getHeight()
          294  +		end
          295  +	end
          296  +
          297  +	self.layers[layer.name] = layer
          298  +end
          299  +
          300  +--- Add Tiles to Tile Layer
          301  +-- @param layer The Tile Layer
          302  +function Map:setTileData(layer)
          303  +	if layer.chunks then
          304  +		for _, chunk in ipairs(layer.chunks) do
          305  +			self:setTileData(chunk)
          306  +		end
          307  +		return
          308  +	end
          309  +
          310  +	local i   = 1
          311  +	local map = {}
          312  +
          313  +	for y = 1, layer.height do
          314  +		map[y] = {}
          315  +		for x = 1, layer.width do
          316  +			local gid = layer.data[i]
          317  +
          318  +			-- NOTE: Empty tiles have a GID of 0
          319  +			if gid > 0 then
          320  +				map[y][x] = self.tiles[gid] or self:setFlippedGID(gid)
          321  +			end
          322  +
          323  +			i = i + 1
          324  +		end
          325  +	end
          326  +
          327  +	layer.data = map
          328  +end
          329  +
          330  +--- Add Objects to Layer
          331  +-- @param layer The Object Layer
          332  +function Map:setObjectData(layer)
          333  +	for _, object in ipairs(layer.objects) do
          334  +		object.layer            = layer
          335  +		self.objects[object.id] = object
          336  +	end
          337  +end
          338  +
          339  +--- Correct position and orientation of Objects in an Object Layer
          340  +-- @param layer The Object Layer
          341  +function Map:setObjectCoordinates(layer)
          342  +	for _, object in ipairs(layer.objects) do
          343  +		local x   = layer.x + object.x
          344  +		local y   = layer.y + object.y
          345  +		local w   = object.width
          346  +		local h   = object.height
          347  +		local cos = math.cos(math.rad(object.rotation))
          348  +		local sin = math.sin(math.rad(object.rotation))
          349  +
          350  +		if object.shape == "rectangle" and not object.gid then
          351  +			object.rectangle = {}
          352  +
          353  +			local vertices = {
          354  +				{ x=x,     y=y     },
          355  +				{ x=x + w, y=y     },
          356  +				{ x=x + w, y=y + h },
          357  +				{ x=x,     y=y + h },
          358  +			}
          359  +
          360  +			for _, vertex in ipairs(vertices) do
          361  +				vertex.x, vertex.y = utils.rotate_vertex(self, vertex, x, y, cos, sin)
          362  +				table.insert(object.rectangle, { x = vertex.x, y = vertex.y })
          363  +			end
          364  +		elseif object.shape == "ellipse" then
          365  +			object.ellipse = {}
          366  +			local vertices = utils.convert_ellipse_to_polygon(x, y, w, h)
          367  +
          368  +			for _, vertex in ipairs(vertices) do
          369  +				vertex.x, vertex.y = utils.rotate_vertex(self, vertex, x, y, cos, sin)
          370  +				table.insert(object.ellipse, { x = vertex.x, y = vertex.y })
          371  +			end
          372  +		elseif object.shape == "polygon" then
          373  +			for _, vertex in ipairs(object.polygon) do
          374  +				vertex.x           = vertex.x + x
          375  +				vertex.y           = vertex.y + y
          376  +				vertex.x, vertex.y = utils.rotate_vertex(self, vertex, x, y, cos, sin)
          377  +			end
          378  +		elseif object.shape == "polyline" then
          379  +			for _, vertex in ipairs(object.polyline) do
          380  +				vertex.x           = vertex.x + x
          381  +				vertex.y           = vertex.y + y
          382  +				vertex.x, vertex.y = utils.rotate_vertex(self, vertex, x, y, cos, sin)
          383  +			end
          384  +		end
          385  +	end
          386  +end
          387  +
          388  +--- Convert tile location to tile instance location
          389  +-- @param layer Tile layer
          390  +-- @param tile Tile
          391  +-- @param x Tile location on X axis (in tiles)
          392  +-- @param y Tile location on Y axis (in tiles)
          393  +-- @return number Tile instance location on X axis (in pixels)
          394  +-- @return number Tile instance location on Y axis (in pixels)
          395  +function Map:getLayerTilePosition(layer, tile, x, y)
          396  +	local tileW = self.tilewidth
          397  +	local tileH = self.tileheight
          398  +	local tileX, tileY
          399  +
          400  +	if self.orientation == "orthogonal" then
          401  +		local tileset = self.tilesets[tile.tileset]
          402  +		tileX = (x - 1) * tileW + tile.offset.x
          403  +		tileY = (y - 0) * tileH + tile.offset.y - tileset.tileheight
          404  +		tileX, tileY = utils.compensate(tile, tileX, tileY, tileW, tileH)
          405  +	elseif self.orientation == "isometric" then
          406  +		tileX = (x - y) * (tileW / 2) + tile.offset.x + layer.width * tileW / 2 - self.tilewidth / 2
          407  +		tileY = (x + y - 2) * (tileH / 2) + tile.offset.y
          408  +	else
          409  +		local sideLen = self.hexsidelength or 0
          410  +		if self.staggeraxis == "y" then
          411  +			if self.staggerindex == "odd" then
          412  +				if y % 2 == 0 then
          413  +					tileX = (x - 1) * tileW + tileW / 2 + tile.offset.x
          414  +				else
          415  +					tileX = (x - 1) * tileW + tile.offset.x
          416  +				end
          417  +			else
          418  +				if y % 2 == 0 then
          419  +					tileX = (x - 1) * tileW + tile.offset.x
          420  +				else
          421  +					tileX = (x - 1) * tileW + tileW / 2 + tile.offset.x
          422  +				end
          423  +			end
          424  +
          425  +			local rowH = tileH - (tileH - sideLen) / 2
          426  +			tileY = (y - 1) * rowH + tile.offset.y
          427  +		else
          428  +			if self.staggerindex == "odd" then
          429  +				if x % 2 == 0 then
          430  +					tileY = (y - 1) * tileH + tileH / 2 + tile.offset.y
          431  +				else
          432  +					tileY = (y - 1) * tileH + tile.offset.y
          433  +				end
          434  +			else
          435  +				if x % 2 == 0 then
          436  +					tileY = (y - 1) * tileH + tile.offset.y
          437  +				else
          438  +					tileY = (y - 1) * tileH + tileH / 2 + tile.offset.y
          439  +				end
          440  +			end
          441  +
          442  +			local colW = tileW - (tileW - sideLen) / 2
          443  +			tileX = (x - 1) * colW + tile.offset.x
          444  +		end
          445  +	end
          446  +
          447  +	return tileX, tileY
          448  +end
          449  +
          450  +--- Place new tile instance
          451  +-- @param layer Tile layer
          452  +-- @param chunk Layer chunk
          453  +-- @param tile Tile
          454  +-- @param number Tile location on X axis (in tiles)
          455  +-- @param number Tile location on Y axis (in tiles)
          456  +function Map:addNewLayerTile(layer, chunk, tile, x, y)
          457  +	local tileset = tile.tileset
          458  +	local image   = self.tilesets[tile.tileset].image
          459  +	local batches
          460  +	local size
          461  +
          462  +	if chunk then
          463  +		batches = chunk.batches
          464  +		size    = chunk.width * chunk.height
          465  +	else
          466  +		batches = layer.batches
          467  +		size    = layer.width * layer.height
          468  +	end
          469  +
          470  +	batches[tileset] = batches[tileset] or lg.newSpriteBatch(image, size)
          471  +
          472  +	local batch = batches[tileset]
          473  +	local tileX, tileY = self:getLayerTilePosition(layer, tile, x, y)
          474  +
          475  +	local instance = {
          476  +		layer = layer,
          477  +		chunk = chunk,
          478  +		gid   = tile.gid,
          479  +		x     = tileX,
          480  +		y     = tileY,
          481  +		r     = tile.r,
          482  +		oy    = 0
          483  +	}
          484  +
          485  +	-- NOTE: STI can run headless so it is not guaranteed that a batch exists.
          486  +	if batch then
          487  +		instance.batch = batch
          488  +		instance.id = batch:add(tile.quad, tileX, tileY, tile.r, tile.sx, tile.sy)
          489  +	end
          490  +
          491  +	self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {}
          492  +	table.insert(self.tileInstances[tile.gid], instance)
          493  +end
          494  +
          495  +function Map:set_batches(layer, chunk)
          496  +	if chunk then
          497  +		chunk.batches = {}
          498  +	else
          499  +		layer.batches = {}
          500  +	end
          501  +
          502  +	if self.orientation == "orthogonal" or self.orientation == "isometric" then
          503  +		local offsetX = chunk and chunk.x or 0
          504  +		local offsetY = chunk and chunk.y or 0
          505  +
          506  +		local startX     = 1
          507  +		local startY     = 1
          508  +		local endX       = chunk and chunk.width  or layer.width
          509  +		local endY       = chunk and chunk.height or layer.height
          510  +		local incrementX = 1
          511  +		local incrementY = 1
          512  +
          513  +		-- Determine order to add tiles to sprite batch
          514  +		-- Defaults to right-down
          515  +		if self.renderorder == "right-up" then
          516  +			startY, endY, incrementY = endY, startY, -1
          517  +		elseif self.renderorder == "left-down" then
          518  +			startX, endX, incrementX = endX, startX, -1
          519  +		elseif self.renderorder == "left-up" then
          520  +			startX, endX, incrementX = endX, startX, -1
          521  +			startY, endY, incrementY = endY, startY, -1
          522  +		end
          523  +
          524  +		for y = startY, endY, incrementY do
          525  +			for x = startX, endX, incrementX do
          526  +				-- NOTE: Cannot short circuit this since it is valid for tile to be assigned nil
          527  +				local tile
          528  +				if chunk then
          529  +					tile = chunk.data[y][x]
          530  +				else
          531  +					tile = layer.data[y][x]
          532  +				end
          533  +
          534  +				if tile then
          535  +					self:addNewLayerTile(layer, chunk, tile, x + offsetX, y + offsetY)
          536  +				end
          537  +			end
          538  +		end
          539  +	else
          540  +		if self.staggeraxis == "y" then
          541  +			for y = 1, (chunk and chunk.height or layer.height) do
          542  +				for x = 1, (chunk and chunk.width or layer.width) do
          543  +					-- NOTE: Cannot short circuit this since it is valid for tile to be assigned nil
          544  +					local tile
          545  +					if chunk then
          546  +						tile = chunk.data[y][x]
          547  +					else
          548  +						tile = layer.data[y][x]
          549  +					end
          550  +
          551  +					if tile then
          552  +						self:addNewLayerTile(layer, chunk, tile, x, y)
          553  +					end
          554  +				end
          555  +			end
          556  +		else
          557  +			local i = 0
          558  +			local _x
          559  +
          560  +			if self.staggerindex == "odd" then
          561  +				_x = 1
          562  +			else
          563  +				_x = 2
          564  +			end
          565  +
          566  +			while i < (chunk and chunk.width * chunk.height or layer.width * layer.height) do
          567  +				for _y = 1, (chunk and chunk.height or layer.height) + 0.5, 0.5 do
          568  +					local y = floor(_y)
          569  +
          570  +					for x = _x, (chunk and chunk.width or layer.width), 2 do
          571  +						i = i + 1
          572  +
          573  +						-- NOTE: Cannot short circuit this since it is valid for tile to be assigned nil
          574  +						local tile
          575  +						if chunk then
          576  +							tile = chunk.data[y][x]
          577  +						else
          578  +							tile = layer.data[y][x]
          579  +						end
          580  +
          581  +						if tile then
          582  +							self:addNewLayerTile(layer, chunk, tile, x, y)
          583  +						end
          584  +					end
          585  +
          586  +					if _x == 1 then
          587  +						_x = 2
          588  +					else
          589  +						_x = 1
          590  +					end
          591  +				end
          592  +			end
          593  +		end
          594  +	end
          595  +end
          596  +
          597  +--- Batch Tiles in Tile Layer for improved draw speed
          598  +-- @param layer The Tile Layer
          599  +function Map:setSpriteBatches(layer)
          600  +	if layer.chunks then
          601  +		for _, chunk in ipairs(layer.chunks) do
          602  +			self:set_batches(layer, chunk)
          603  +		end
          604  +		return
          605  +	end
          606  +
          607  +	self:set_batches(layer)
          608  +end
          609  +
          610  +--- Batch Tiles in Object Layer for improved draw speed
          611  +-- @param layer The Object Layer
          612  +function Map:setObjectSpriteBatches(layer)
          613  +	local newBatch = lg.newSpriteBatch
          614  +	local batches  = {}
          615  +
          616  +	if layer.draworder == "topdown" then
          617  +		table.sort(layer.objects, function(a, b)
          618  +			return a.y + a.height < b.y + b.height
          619  +		end)
          620  +	end
          621  +
          622  +	for _, object in ipairs(layer.objects) do
          623  +		if object.visible ~= false and object.gid then
          624  +			local tile    = self.tiles[object.gid] or self:setFlippedGID(object.gid)
          625  +			local tileset = tile.tileset
          626  +			local image   = self.tilesets[tileset].image
          627  +
          628  +			batches[tileset] = batches[tileset] or newBatch(image)
          629  +
          630  +			local sx = object.width  / tile.width
          631  +			local sy = object.height / tile.height
          632  +
          633  +			-- Tiled rotates around bottom left corner, where love2D rotates around top left corner
          634  +			local ox = 0
          635  +			local oy = tile.height
          636  +
          637  +			local batch = batches[tileset]
          638  +			local tileX = object.x + tile.offset.x
          639  +			local tileY = object.y + tile.offset.y
          640  +			local tileR = math.rad(object.rotation)
          641  +
          642  +			-- Compensation for scale/rotation shift
          643  +			if tile.sx == -1 then
          644  +				tileX = tileX + object.width
          645  +
          646  +				if tileR ~= 0 then
          647  +					tileX = tileX - object.width
          648  +					ox = ox + tile.width
          649  +				end
          650  +			end
          651  +
          652  +			if tile.sy == -1 then
          653  +				tileY = tileY - object.height
          654  +
          655  +				if tileR ~= 0 then
          656  +					tileY = tileY + object.width
          657  +					oy = oy - tile.width
          658  +				end
          659  +			end
          660  +
          661  +			local instance = {
          662  +				id    = batch:add(tile.quad, tileX, tileY, tileR, tile.sx * sx, tile.sy * sy, ox, oy),
          663  +				batch = batch,
          664  +				layer = layer,
          665  +				gid   = tile.gid,
          666  +				x     = tileX,
          667  +				y     = tileY - oy,
          668  +				r     = tileR,
          669  +				oy    = oy
          670  +			}
          671  +
          672  +			self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {}
          673  +			table.insert(self.tileInstances[tile.gid], instance)
          674  +		end
          675  +	end
          676  +
          677  +	layer.batches = batches
          678  +end
          679  +
          680  +--- Create a Custom Layer to place userdata in (such as player sprites)
          681  +-- @param name Name of Custom Layer
          682  +-- @param index Draw order within Layer stack
          683  +-- @return table Custom Layer
          684  +function Map:addCustomLayer(name, index)
          685  +	index = index or #self.layers + 1
          686  +	local layer = {
          687  +      type       = "customlayer",
          688  +      name       = name,
          689  +      visible    = true,
          690  +      opacity    = 1,
          691  +      properties = {},
          692  +    }
          693  +
          694  +	function layer.draw() end
          695  +	function layer.update() end
          696  +
          697  +	table.insert(self.layers, index, layer)
          698  +	self.layers[name] = self.layers[index]
          699  +
          700  +	return layer
          701  +end
          702  +
          703  +--- Convert another Layer into a Custom Layer
          704  +-- @param index Index or name of Layer to convert
          705  +-- @return table Custom Layer
          706  +function Map:convertToCustomLayer(index)
          707  +	local layer = assert(self.layers[index], "Layer not found: " .. index)
          708  +
          709  +	layer.type     = "customlayer"
          710  +	layer.x        = nil
          711  +	layer.y        = nil
          712  +	layer.width    = nil
          713  +	layer.height   = nil
          714  +	layer.encoding = nil
          715  +	layer.data     = nil
          716  +	layer.chunks   = nil
          717  +	layer.objects  = nil
          718  +	layer.image    = nil
          719  +
          720  +	function layer.draw() end
          721  +	function layer.update() end
          722  +
          723  +	return layer
          724  +end
          725  +
          726  +--- Remove a Layer from the Layer stack
          727  +-- @param index Index or name of Layer to remove
          728  +function Map:removeLayer(index)
          729  +	local layer = assert(self.layers[index], "Layer not found: " .. index)
          730  +
          731  +	if type(index) == "string" then
          732  +		for i, l in ipairs(self.layers) do
          733  +			if l.name == index then
          734  +				table.remove(self.layers, i)
          735  +				self.layers[index] = nil
          736  +				break
          737  +			end
          738  +		end
          739  +	else
          740  +		local name = self.layers[index].name
          741  +		table.remove(self.layers, index)
          742  +		self.layers[name] = nil
          743  +	end
          744  +
          745  +	-- Remove layer batches
          746  +	if layer.batches then
          747  +		for _, batch in pairs(layer.batches) do
          748  +			self.freeBatchSprites[batch] = nil
          749  +		end
          750  +	end
          751  +
          752  +	-- Remove chunk batches
          753  +	if layer.chunks then
          754  +		for _, chunk in ipairs(layer.chunks) do
          755  +			for _, batch in pairs(chunk.batches) do
          756  +				self.freeBatchSprites[batch] = nil
          757  +			end
          758  +		end
          759  +	end
          760  +
          761  +	-- Remove tile instances
          762  +	if layer.type == "tilelayer" then
          763  +		for _, tiles in pairs(self.tileInstances) do
          764  +			for i = #tiles, 1, -1 do
          765  +				local tile = tiles[i]
          766  +				if tile.layer == layer then
          767  +					table.remove(tiles, i)
          768  +				end
          769  +			end
          770  +		end
          771  +	end
          772  +
          773  +	-- Remove objects
          774  +	if layer.objects then
          775  +		for i, object in pairs(self.objects) do
          776  +			if object.layer == layer then
          777  +				self.objects[i] = nil
          778  +			end
          779  +		end
          780  +	end
          781  +end
          782  +
          783  +--- Animate Tiles and update every Layer
          784  +-- @param dt Delta Time
          785  +function Map:update(dt)
          786  +	for _, tile in pairs(self.tiles) do
          787  +		local update = false
          788  +
          789  +		if tile.animation then
          790  +			tile.time = tile.time + dt * 1000
          791  +
          792  +			while tile.time > tonumber(tile.animation[tile.frame].duration) do
          793  +				update     = true
          794  +				tile.time  = tile.time  - tonumber(tile.animation[tile.frame].duration)
          795  +				tile.frame = tile.frame + 1
          796  +
          797  +				if tile.frame > #tile.animation then tile.frame = 1 end
          798  +			end
          799  +
          800  +			if update and self.tileInstances[tile.gid] then
          801  +				for _, j in pairs(self.tileInstances[tile.gid]) do
          802  +					local t = self.tiles[tonumber(tile.animation[tile.frame].tileid) + self.tilesets[tile.tileset].firstgid]
          803  +					j.batch:set(j.id, t.quad, j.x, j.y, j.r, tile.sx, tile.sy, 0, j.oy)
          804  +				end
          805  +			end
          806  +		end
          807  +	end
          808  +
          809  +	for _, layer in ipairs(self.layers) do
          810  +		layer:update(dt)
          811  +	end
          812  +end
          813  +
          814  +--- Draw every Layer
          815  +-- @param tx Translate on X
          816  +-- @param ty Translate on Y
          817  +-- @param sx Scale on X
          818  +-- @param sy Scale on Y
          819  +function Map:draw(tx, ty, sx, sy)
          820  +	local current_canvas = lg.getCanvas()
          821  +	lg.setCanvas(self.canvas)
          822  +	lg.clear()
          823  +
          824  +	-- Scale map to 1.0 to draw onto canvas, this fixes tearing issues
          825  +	-- Map is translated to correct position so the right section is drawn
          826  +	lg.push()
          827  +	lg.origin()
          828  +	lg.translate(math.floor(tx or 0), math.floor(ty or 0))
          829  +
          830  +	for _, layer in ipairs(self.layers) do
          831  +		if layer.visible and layer.opacity > 0 then
          832  +			self:drawLayer(layer)
          833  +		end
          834  +	end
          835  +
          836  +	lg.pop()
          837  +
          838  +	-- Draw canvas at 0,0; this fixes scissoring issues
          839  +	-- Map is scaled to correct scale so the right section is shown
          840  +	lg.push()
          841  +	lg.origin()
          842  +	lg.scale(sx or 1, sy or sx or 1)
          843  +
          844  +	lg.setCanvas(current_canvas)
          845  +	lg.draw(self.canvas)
          846  +
          847  +	lg.pop()
          848  +end
          849  +
          850  +--- Draw an individual Layer
          851  +-- @param layer The Layer to draw
          852  +function Map.drawLayer(_, layer)
          853  +	local r,g,b,a = lg.getColor()
          854  +	lg.setColor(r, g, b, a * layer.opacity)
          855  +	layer:draw()
          856  +	lg.setColor(r,g,b,a)
          857  +end
          858  +
          859  +--- Default draw function for Tile Layers
          860  +-- @param layer The Tile Layer to draw
          861  +function Map:drawTileLayer(layer)
          862  +	if type(layer) == "string" or type(layer) == "number" then
          863  +		layer = self.layers[layer]
          864  +	end
          865  +
          866  +	assert(layer.type == "tilelayer", "Invalid layer type: " .. layer.type .. ". Layer must be of type: tilelayer")
          867  +
          868  +	-- NOTE: This does not take into account any sort of draw range clipping and will always draw every chunk
          869  +	if layer.chunks then
          870  +		for _, chunk in ipairs(layer.chunks) do
          871  +			for _, batch in pairs(chunk.batches) do
          872  +				lg.draw(batch, 0, 0)
          873  +			end
          874  +		end
          875  +
          876  +		return
          877  +	end
          878  +
          879  +	for _, batch in pairs(layer.batches) do
          880  +		lg.draw(batch, floor(layer.x), floor(layer.y))
          881  +	end
          882  +end
          883  +
          884  +--- Default draw function for Object Layers
          885  +-- @param layer The Object Layer to draw
          886  +function Map:drawObjectLayer(layer)
          887  +	if type(layer) == "string" or type(layer) == "number" then
          888  +		layer = self.layers[layer]
          889  +	end
          890  +
          891  +	assert(layer.type == "objectgroup", "Invalid layer type: " .. layer.type .. ". Layer must be of type: objectgroup")
          892  +
          893  +	local line  = { 160, 160, 160, 255 * layer.opacity       }
          894  +	local fill  = { 160, 160, 160, 255 * layer.opacity * 0.5 }
          895  +	local r,g,b,a = lg.getColor()
          896  +	local reset = {   r,   g,   b,   a * layer.opacity       }
          897  +
          898  +	local function sortVertices(obj)
          899  +		local vertex = {}
          900  +
          901  +		for _, v in ipairs(obj) do
          902  +			table.insert(vertex, v.x)
          903  +			table.insert(vertex, v.y)
          904  +		end
          905  +
          906  +		return vertex
          907  +	end
          908  +
          909  +	local function drawShape(obj, shape)
          910  +		local vertex = sortVertices(obj)
          911  +
          912  +		if shape == "polyline" then
          913  +			lg.setColor(line)
          914  +			lg.line(vertex)
          915  +			return
          916  +		elseif shape == "polygon" then
          917  +			lg.setColor(fill)
          918  +			if not love.math.isConvex(vertex) then
          919  +				local triangles = love.math.triangulate(vertex)
          920  +				for _, triangle in ipairs(triangles) do
          921  +					lg.polygon("fill", triangle)
          922  +				end
          923  +			else
          924  +				lg.polygon("fill", vertex)
          925  +			end
          926  +		else
          927  +			lg.setColor(fill)
          928  +			lg.polygon("fill", vertex)
          929  +		end
          930  +
          931  +		lg.setColor(line)
          932  +		lg.polygon("line", vertex)
          933  +	end
          934  +
          935  +	for _, object in ipairs(layer.objects) do
          936  +        if object.visible ~= false then
          937  +            if object.shape == "rectangle" and not object.gid then
          938  +                drawShape(object.rectangle, "rectangle")
          939  +            elseif object.shape == "ellipse" then
          940  +                drawShape(object.ellipse, "ellipse")
          941  +            elseif object.shape == "polygon" then
          942  +                drawShape(object.polygon, "polygon")
          943  +            elseif object.shape == "polyline" then
          944  +                drawShape(object.polyline, "polyline")
          945  +            elseif object.shape == "point" then
          946  +                lg.points(object.x, object.y)
          947  +            end
          948  +        end
          949  +	end
          950  +
          951  +	lg.setColor(reset)
          952  +	for _, batch in pairs(layer.batches) do
          953  +		lg.draw(batch, 0, 0)
          954  +	end
          955  +	lg.setColor(r,g,b,a)
          956  +end
          957  +
          958  +--- Default draw function for Image Layers
          959  +-- @param layer The Image Layer to draw
          960  +function Map:drawImageLayer(layer)
          961  +	if type(layer) == "string" or type(layer) == "number" then
          962  +		layer = self.layers[layer]
          963  +	end
          964  +
          965  +	assert(layer.type == "imagelayer", "Invalid layer type: " .. layer.type .. ". Layer must be of type: imagelayer")
          966  +
          967  +	if layer.image ~= "" then
          968  +		lg.draw(layer.image, layer.x, layer.y)
          969  +	end
          970  +end
          971  +
          972  +--- Resize the drawable area of the Map
          973  +-- @param w The new width of the drawable area (in pixels)
          974  +-- @param h The new Height of the drawable area (in pixels)
          975  +function Map:resize(w, h)
          976  +	if lg.isCreated then
          977  +		w = w or lg.getWidth()
          978  +		h = h or lg.getHeight()
          979  +
          980  +		self.canvas = lg.newCanvas(w, h)
          981  +		self.canvas:setFilter("nearest", "nearest")
          982  +	end
          983  +end
          984  +
          985  +--- Create flipped or rotated Tiles based on bitop flags
          986  +-- @param gid The flagged Global ID
          987  +-- @return table Flipped Tile
          988  +function Map:setFlippedGID(gid)
          989  +	local bit31   = 2147483648
          990  +	local bit30   = 1073741824
          991  +	local bit29   = 536870912
          992  +	local flipX   = false
          993  +	local flipY   = false
          994  +	local flipD   = false
          995  +	local realgid = gid
          996  +
          997  +	if realgid >= bit31 then
          998  +		realgid = realgid - bit31
          999  +		flipX   = not flipX
         1000  +	end
         1001  +
         1002  +	if realgid >= bit30 then
         1003  +		realgid = realgid - bit30
         1004  +		flipY   = not flipY
         1005  +	end
         1006  +
         1007  +	if realgid >= bit29 then
         1008  +		realgid = realgid - bit29
         1009  +		flipD   = not flipD
         1010  +	end
         1011  +
         1012  +	local tile = self.tiles[realgid]
         1013  +	local data = {
         1014  +		id         = tile.id,
         1015  +		gid        = gid,
         1016  +		tileset    = tile.tileset,
         1017  +		frame      = tile.frame,
         1018  +		time       = tile.time,
         1019  +		width      = tile.width,
         1020  +		height     = tile.height,
         1021  +		offset     = tile.offset,
         1022  +		quad       = tile.quad,
         1023  +		properties = tile.properties,
         1024  +		terrain    = tile.terrain,
         1025  +		animation  = tile.animation,
         1026  +		sx         = tile.sx,
         1027  +		sy         = tile.sy,
         1028  +		r          = tile.r,
         1029  +	}
         1030  +
         1031  +	if flipX then
         1032  +		if flipY and flipD then
         1033  +			data.r  = math.rad(-90)
         1034  +			data.sy = -1
         1035  +		elseif flipY then
         1036  +			data.sx = -1
         1037  +			data.sy = -1
         1038  +		elseif flipD then
         1039  +			data.r = math.rad(90)
         1040  +		else
         1041  +			data.sx = -1
         1042  +		end
         1043  +	elseif flipY then
         1044  +		if flipD then
         1045  +			data.r = math.rad(-90)
         1046  +		else
         1047  +			data.sy = -1
         1048  +		end
         1049  +	elseif flipD then
         1050  +		data.r  = math.rad(90)
         1051  +		data.sy = -1
         1052  +	end
         1053  +
         1054  +	self.tiles[gid] = data
         1055  +
         1056  +	return self.tiles[gid]
         1057  +end
         1058  +
         1059  +--- Get custom properties from Layer
         1060  +-- @param layer The Layer
         1061  +-- @return table List of properties
         1062  +function Map:getLayerProperties(layer)
         1063  +	local l = self.layers[layer]
         1064  +
         1065  +	if not l then
         1066  +		return {}
         1067  +	end
         1068  +
         1069  +	return l.properties
         1070  +end
         1071  +
         1072  +--- Get custom properties from Tile
         1073  +-- @param layer The Layer that the Tile belongs to
         1074  +-- @param x The X axis location of the Tile (in tiles)
         1075  +-- @param y The Y axis location of the Tile (in tiles)
         1076  +-- @return table List of properties
         1077  +function Map:getTileProperties(layer, x, y)
         1078  +	local tile = self.layers[layer].data[y][x]
         1079  +
         1080  +	if not tile then
         1081  +		return {}
         1082  +	end
         1083  +
         1084  +	return tile.properties
         1085  +end
         1086  +
         1087  +--- Get custom properties from Object
         1088  +-- @param layer The Layer that the Object belongs to
         1089  +-- @param object The index or name of the Object
         1090  +-- @return table List of properties
         1091  +function Map:getObjectProperties(layer, object)
         1092  +	local o = self.layers[layer].objects
         1093  +
         1094  +	if type(object) == "number" then
         1095  +		o = o[object]
         1096  +	else
         1097  +		for _, v in ipairs(o) do
         1098  +			if v.name == object then
         1099  +				o = v
         1100  +				break
         1101  +			end
         1102  +		end
         1103  +	end
         1104  +
         1105  +	if not o then
         1106  +		return {}
         1107  +	end
         1108  +
         1109  +	return o.properties
         1110  +end
         1111  +
         1112  +--- Change a tile in a layer to another tile
         1113  +-- @param layer The Layer that the Tile belongs to
         1114  +-- @param x The X axis location of the Tile (in tiles)
         1115  +-- @param y The Y axis location of the Tile (in tiles)
         1116  +-- @param gid The gid of the new tile
         1117  +function Map:setLayerTile(layer, x, y, gid)
         1118  +	layer = self.layers[layer]
         1119  +
         1120  +	layer.data[y] = layer.data[y] or {}
         1121  +	local tile = layer.data[y][x]
         1122  +	local instance
         1123  +	if tile then
         1124  +		local tileX, tileY = self:getLayerTilePosition(layer, tile, x, y)
         1125  +		for _, inst in pairs(self.tileInstances[tile.gid]) do
         1126  +			if inst.x == tileX and inst.y == tileY then
         1127  +				instance = inst
         1128  +				break
         1129  +			end
         1130  +		end
         1131  +	end
         1132  +
         1133  +	if tile == self.tiles[gid] then
         1134  +		return
         1135  +	end
         1136  +
         1137  +	tile = self.tiles[gid]
         1138  +
         1139  +	if instance then
         1140  +		self:swapTile(instance, tile)
         1141  +	else
         1142  +		self:addNewLayerTile(layer, tile, x, y)
         1143  +	end
         1144  +	layer.data[y][x] = tile
         1145  +end
         1146  +
         1147  +--- Swap a tile in a spritebatch
         1148  +-- @param instance The current Instance object we want to replace
         1149  +-- @param tile The Tile object we want to use
         1150  +-- @return none
         1151  +function Map:swapTile(instance, tile)
         1152  +	-- Update sprite batch
         1153  +	if instance.batch then
         1154  +		if tile then
         1155  +			instance.batch:set(
         1156  +				instance.id,
         1157  +				tile.quad,
         1158  +				instance.x,
         1159  +				instance.y,
         1160  +				tile.r,
         1161  +				tile.sx,
         1162  +				tile.sy
         1163  +			)
         1164  +		else
         1165  +			instance.batch:set(
         1166  +				instance.id,
         1167  +				instance.x,
         1168  +				instance.y,
         1169  +				0,
         1170  +				0)
         1171  +
         1172  +			self.freeBatchSprites[instance.batch] = self.freeBatchSprites[instance.batch] or {}
         1173  +			table.insert(self.freeBatchSprites[instance.batch], instance)
         1174  +		end
         1175  +	end
         1176  +
         1177  +	-- Remove old tile instance
         1178  +	for i, ins in ipairs(self.tileInstances[instance.gid]) do
         1179  +		if ins.batch == instance.batch and ins.id == instance.id then
         1180  +			table.remove(self.tileInstances[instance.gid], i)
         1181  +			break
         1182  +		end
         1183  +	end
         1184  +
         1185  +	-- Add new tile instance
         1186  +	if tile then
         1187  +		self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {}
         1188  +
         1189  +		local freeBatchSprites = self.freeBatchSprites[instance.batch]
         1190  +		local newInstance
         1191  +		if freeBatchSprites and #freeBatchSprites > 0 then
         1192  +			newInstance = freeBatchSprites[#freeBatchSprites]
         1193  +			freeBatchSprites[#freeBatchSprites] = nil
         1194  +		else
         1195  +			newInstance = {}
         1196  +		end
         1197  +
         1198  +		newInstance.layer = instance.layer
         1199  +		newInstance.batch = instance.batch
         1200  +		newInstance.id    = instance.id
         1201  +		newInstance.gid   = tile.gid or 0
         1202  +		newInstance.x     = instance.x
         1203  +		newInstance.y     = instance.y
         1204  +		newInstance.r     = tile.r or 0
         1205  +		newInstance.oy    = tile.r ~= 0 and tile.height or 0
         1206  +		table.insert(self.tileInstances[tile.gid], newInstance)
         1207  +	end
         1208  +end
         1209  +
         1210  +--- Convert tile location to pixel location
         1211  +-- @param x The X axis location of the point (in tiles)
         1212  +-- @param y The Y axis location of the point (in tiles)
         1213  +-- @return number The X axis location of the point (in pixels)
         1214  +-- @return number The Y axis location of the point (in pixels)
         1215  +function Map:convertTileToPixel(x,y)
         1216  +	if self.orientation == "orthogonal" then
         1217  +		local tileW = self.tilewidth
         1218  +		local tileH = self.tileheight
         1219  +		return
         1220  +			x * tileW,
         1221  +			y * tileH
         1222  +	elseif self.orientation == "isometric" then
         1223  +		local mapH    = self.height
         1224  +		local tileW   = self.tilewidth
         1225  +		local tileH   = self.tileheight
         1226  +		local offsetX = mapH * tileW / 2
         1227  +		return
         1228  +			(x - y) * tileW / 2 + offsetX,
         1229  +			(x + y) * tileH / 2
         1230  +	elseif self.orientation == "staggered" or
         1231  +		self.orientation     == "hexagonal" then
         1232  +		local tileW   = self.tilewidth
         1233  +		local tileH   = self.tileheight
         1234  +		local sideLen = self.hexsidelength or 0
         1235  +
         1236  +		if self.staggeraxis == "x" then
         1237  +			return
         1238  +				x * tileW,
         1239  +				ceil(y) * (tileH + sideLen) + (ceil(y) % 2 == 0 and tileH or 0)
         1240  +		else
         1241  +			return
         1242  +				ceil(x) * (tileW + sideLen) + (ceil(x) % 2 == 0 and tileW or 0),
         1243  +				y * tileH
         1244  +		end
         1245  +	end
         1246  +end
         1247  +
         1248  +--- Convert pixel location to tile location
         1249  +-- @param x The X axis location of the point (in pixels)
         1250  +-- @param y The Y axis location of the point (in pixels)
         1251  +-- @return number The X axis location of the point (in tiles)
         1252  +-- @return number The Y axis location of the point (in tiles)
         1253  +function Map:convertPixelToTile(x, y)
         1254  +	if self.orientation == "orthogonal" then
         1255  +		local tileW = self.tilewidth
         1256  +		local tileH = self.tileheight
         1257  +		return
         1258  +			x / tileW,
         1259  +			y / tileH
         1260  +	elseif self.orientation == "isometric" then
         1261  +		local mapH    = self.height
         1262  +		local tileW   = self.tilewidth
         1263  +		local tileH   = self.tileheight
         1264  +		local offsetX = mapH * tileW / 2
         1265  +		return
         1266  +			y / tileH + (x - offsetX) / tileW,
         1267  +			y / tileH - (x - offsetX) / tileW
         1268  +	elseif self.orientation == "staggered" then
         1269  +		local staggerX = self.staggeraxis  == "x"
         1270  +		local even     = self.staggerindex == "even"
         1271  +
         1272  +		local function topLeft(x, y)
         1273  +			if staggerX then
         1274  +				if ceil(x) % 2 == 1 and even then
         1275  +					return x - 1, y
         1276  +				else
         1277  +					return x - 1, y - 1
         1278  +				end
         1279  +			else
         1280  +				if ceil(y) % 2 == 1 and even then
         1281  +					return x, y - 1
         1282  +				else
         1283  +					return x - 1, y - 1
         1284  +				end
         1285  +			end
         1286  +		end
         1287  +
         1288  +		local function topRight(x, y)
         1289  +			if staggerX then
         1290  +				if ceil(x) % 2 == 1 and even then
         1291  +					return x + 1, y
         1292  +				else
         1293  +					return x + 1, y - 1
         1294  +				end
         1295  +			else
         1296  +				if ceil(y) % 2 == 1 and even then
         1297  +					return x + 1, y - 1
         1298  +				else
         1299  +					return x, y - 1
         1300  +				end
         1301  +			end
         1302  +		end
         1303  +
         1304  +		local function bottomLeft(x, y)
         1305  +			if staggerX then
         1306  +				if ceil(x) % 2 == 1 and even then
         1307  +					return x - 1, y + 1
         1308  +				else
         1309  +					return x - 1, y
         1310  +				end
         1311  +			else
         1312  +				if ceil(y) % 2 == 1 and even then
         1313  +					return x, y + 1
         1314  +				else
         1315  +					return x - 1, y + 1
         1316  +				end
         1317  +			end
         1318  +		end
         1319  +
         1320  +		local function bottomRight(x, y)
         1321  +			if staggerX then
         1322  +				if ceil(x) % 2 == 1 and even then
         1323  +					return x + 1, y + 1
         1324  +				else
         1325  +					return x + 1, y
         1326  +				end
         1327  +			else
         1328  +				if ceil(y) % 2 == 1 and even then
         1329  +					return x + 1, y + 1
         1330  +				else
         1331  +					return x, y + 1
         1332  +				end
         1333  +			end
         1334  +		end
         1335  +
         1336  +		local tileW = self.tilewidth
         1337  +		local tileH = self.tileheight
         1338  +
         1339  +		if staggerX then
         1340  +			x = x - (even and tileW / 2 or 0)
         1341  +		else
         1342  +			y = y - (even and tileH / 2 or 0)
         1343  +		end
         1344  +
         1345  +		local halfH      = tileH / 2
         1346  +		local ratio      = tileH / tileW
         1347  +		local referenceX = ceil(x / tileW)
         1348  +		local referenceY = ceil(y / tileH)
         1349  +		local relativeX  = x - referenceX * tileW
         1350  +		local relativeY  = y - referenceY * tileH
         1351  +
         1352  +		if (halfH - relativeX * ratio > relativeY) then
         1353  +			return topLeft(referenceX, referenceY)
         1354  +		elseif (-halfH + relativeX * ratio > relativeY) then
         1355  +			return topRight(referenceX, referenceY)
         1356  +		elseif (halfH + relativeX * ratio < relativeY) then
         1357  +			return bottomLeft(referenceX, referenceY)
         1358  +		elseif (halfH * 3 - relativeX * ratio < relativeY) then
         1359  +			return bottomRight(referenceX, referenceY)
         1360  +		end
         1361  +
         1362  +		return referenceX, referenceY
         1363  +	elseif self.orientation == "hexagonal" then
         1364  +		local staggerX  = self.staggeraxis  == "x"
         1365  +		local even      = self.staggerindex == "even"
         1366  +		local tileW     = self.tilewidth
         1367  +		local tileH     = self.tileheight
         1368  +		local sideLenX  = 0
         1369  +		local sideLenY  = 0
         1370  +
         1371  +		local colW       = tileW / 2
         1372  +		local rowH       = tileH / 2
         1373  +		if staggerX then
         1374  +			sideLenX = self.hexsidelength
         1375  +			x = x - (even and tileW or (tileW - sideLenX) / 2)
         1376  +			colW = colW - (colW  - sideLenX / 2) / 2
         1377  +		else
         1378  +			sideLenY = self.hexsidelength
         1379  +			y = y - (even and tileH or (tileH - sideLenY) / 2)
         1380  +			rowH = rowH - (rowH  - sideLenY / 2) / 2
         1381  +		end
         1382  +
         1383  +		local referenceX = ceil(x) / (colW * 2)
         1384  +		local referenceY = ceil(y) / (rowH * 2)
         1385  +
         1386  +    -- If in staggered line, then shift reference by 0.5 of other axes
         1387  +		if staggerX then
         1388  +			if (floor(referenceX) % 2 == 0) == even then
         1389  +				referenceY = referenceY - 0.5
         1390  +			end
         1391  +		else
         1392  +			if (floor(referenceY) % 2 == 0) == even then
         1393  +				referenceX = referenceX - 0.5
         1394  +			end
         1395  +		end
         1396  +
         1397  +		local relativeX  = x - referenceX * colW * 2
         1398  +		local relativeY  = y - referenceY * rowH * 2
         1399  +		local centers
         1400  +
         1401  +		if staggerX then
         1402  +			local left    = sideLenX / 2
         1403  +			local centerX = left + colW
         1404  +			local centerY = tileH / 2
         1405  +
         1406  +			centers = {
         1407  +				{ x = left,           y = centerY        },
         1408  +				{ x = centerX,        y = centerY - rowH },
         1409  +				{ x = centerX,        y = centerY + rowH },
         1410  +				{ x = centerX + colW, y = centerY        },
         1411  +			}
         1412  +		else
         1413  +			local top     = sideLenY / 2
         1414  +			local centerX = tileW / 2
         1415  +			local centerY = top + rowH
         1416  +
         1417  +			centers = {
         1418  +				{ x = centerX,        y = top },
         1419  +				{ x = centerX - colW, y = centerY },
         1420  +				{ x = centerX + colW, y = centerY },
         1421  +				{ x = centerX,        y = centerY + rowH }
         1422  +			}
         1423  +		end
         1424  +
         1425  +		local nearest = 0
         1426  +		local minDist = math.huge
         1427  +
         1428  +		local function len2(ax, ay)
         1429  +			return ax * ax + ay * ay
         1430  +		end
         1431  +
         1432  +		for i = 1, 4 do
         1433  +			local dc = len2(centers[i].x - relativeX, centers[i].y - relativeY)
         1434  +
         1435  +			if dc < minDist then
         1436  +				minDist = dc
         1437  +				nearest = i
         1438  +			end
         1439  +		end
         1440  +
         1441  +		local offsetsStaggerX = {
         1442  +			{ x = 1, y =  1 },
         1443  +			{ x = 2, y =  0 },
         1444  +			{ x = 2, y =  1 },
         1445  +			{ x = 3, y =  1 },
         1446  +		}
         1447  +
         1448  +		local offsetsStaggerY = {
         1449  +			{ x =  1, y = 1 },
         1450  +			{ x =  0, y = 2 },
         1451  +			{ x =  1, y = 2 },
         1452  +			{ x =  1, y = 3 },
         1453  +		}
         1454  +
         1455  +		local offsets = staggerX and offsetsStaggerX or offsetsStaggerY
         1456  +
         1457  +		return
         1458  +			referenceX + offsets[nearest].x,
         1459  +			referenceY + offsets[nearest].y
         1460  +	end
         1461  +end
         1462  +
         1463  +--- A list of individual layers indexed both by draw order and name
         1464  +-- @table Map.layers
         1465  +-- @see TileLayer
         1466  +-- @see ObjectLayer
         1467  +-- @see ImageLayer
         1468  +-- @see CustomLayer
         1469  +
         1470  +--- A list of individual tiles indexed by Global ID
         1471  +-- @table Map.tiles
         1472  +-- @see Tile
         1473  +-- @see Map.tileInstances
         1474  +
         1475  +--- A list of tile instances indexed by Global ID
         1476  +-- @table Map.tileInstances
         1477  +-- @see TileInstance
         1478  +-- @see Tile
         1479  +-- @see Map.tiles
         1480  +
         1481  +--- A list of no-longer-used batch sprites, indexed by batch
         1482  +--@table Map.freeBatchSprites
         1483  +
         1484  +--- A list of individual objects indexed by Global ID
         1485  +-- @table Map.objects
         1486  +-- @see Object
         1487  +
         1488  +--- @table TileLayer
         1489  +-- @field name The name of the layer
         1490  +-- @field x Position on the X axis (in pixels)
         1491  +-- @field y Position on the Y axis (in pixels)
         1492  +-- @field width Width of layer (in tiles)
         1493  +-- @field height Height of layer (in tiles)
         1494  +-- @field visible Toggle if layer is visible or hidden
         1495  +-- @field opacity Opacity of layer
         1496  +-- @field properties Custom properties
         1497  +-- @field data A tileWo dimensional table filled with individual tiles indexed by [y][x] (in tiles)
         1498  +-- @field update Update function
         1499  +-- @field draw Draw function
         1500  +-- @see Map.layers
         1501  +-- @see Tile
         1502  +
         1503  +--- @table ObjectLayer
         1504  +-- @field name The name of the layer
         1505  +-- @field x Position on the X axis (in pixels)
         1506  +-- @field y Position on the Y axis (in pixels)
         1507  +-- @field visible Toggle if layer is visible or hidden
         1508  +-- @field opacity Opacity of layer
         1509  +-- @field properties Custom properties
         1510  +-- @field objects List of objects indexed by draw order
         1511  +-- @field update Update function
         1512  +-- @field draw Draw function
         1513  +-- @see Map.layers
         1514  +-- @see Object
         1515  +
         1516  +--- @table ImageLayer
         1517  +-- @field name The name of the layer
         1518  +-- @field x Position on the X axis (in pixels)
         1519  +-- @field y Position on the Y axis (in pixels)
         1520  +-- @field visible Toggle if layer is visible or hidden
         1521  +-- @field opacity Opacity of layer
         1522  +-- @field properties Custom properties
         1523  +-- @field image Image to be drawn
         1524  +-- @field update Update function
         1525  +-- @field draw Draw function
         1526  +-- @see Map.layers
         1527  +
         1528  +--- Custom Layers are used to place userdata such as sprites within the draw order of the map.
         1529  +-- @table CustomLayer
         1530  +-- @field name The name of the layer
         1531  +-- @field x Position on the X axis (in pixels)
         1532  +-- @field y Position on the Y axis (in pixels)
         1533  +-- @field visible Toggle if layer is visible or hidden
         1534  +-- @field opacity Opacity of layer
         1535  +-- @field properties Custom properties
         1536  +-- @field update Update function
         1537  +-- @field draw Draw function
         1538  +-- @see Map.layers
         1539  +-- @usage
         1540  +--	-- Create a Custom Layer
         1541  +--	local spriteLayer = map:addCustomLayer("Sprite Layer", 3)
         1542  +--
         1543  +--	-- Add data to Custom Layer
         1544  +--	spriteLayer.sprites = {
         1545  +--		player = {
         1546  +--			image = lg.newImage("assets/sprites/player.png"),
         1547  +--			x = 64,
         1548  +--			y = 64,
         1549  +--			r = 0,
         1550  +--		}
         1551  +--	}
         1552  +--
         1553  +--	-- Update callback for Custom Layer
         1554  +--	function spriteLayer:update(dt)
         1555  +--		for _, sprite in pairs(self.sprites) do
         1556  +--			sprite.r = sprite.r + math.rad(90 * dt)
         1557  +--		end
         1558  +--	end
         1559  +--
         1560  +--	-- Draw callback for Custom Layer
         1561  +--	function spriteLayer:draw()
         1562  +--		for _, sprite in pairs(self.sprites) do
         1563  +--			local x = math.floor(sprite.x)
         1564  +--			local y = math.floor(sprite.y)
         1565  +--			local r = sprite.r
         1566  +--			lg.draw(sprite.image, x, y, r)
         1567  +--		end
         1568  +--	end
         1569  +
         1570  +--- @table Tile
         1571  +-- @field id Local ID within Tileset
         1572  +-- @field gid Global ID
         1573  +-- @field tileset Tileset ID
         1574  +-- @field quad Quad object
         1575  +-- @field properties Custom properties
         1576  +-- @field terrain Terrain data
         1577  +-- @field animation Animation data
         1578  +-- @field frame Current animation frame
         1579  +-- @field time Time spent on current animation frame
         1580  +-- @field width Width of tile
         1581  +-- @field height Height of tile
         1582  +-- @field sx Scale value on the X axis
         1583  +-- @field sy Scale value on the Y axis
         1584  +-- @field r Rotation of tile (in radians)
         1585  +-- @field offset Offset drawing position
         1586  +-- @field offset.x Offset value on the X axis
         1587  +-- @field offset.y Offset value on the Y axis
         1588  +-- @see Map.tiles
         1589  +
         1590  +--- @table TileInstance
         1591  +-- @field batch Spritebatch the Tile Instance belongs to
         1592  +-- @field id ID within the spritebatch
         1593  +-- @field gid Global ID
         1594  +-- @field x Position on the X axis (in pixels)
         1595  +-- @field y Position on the Y axis (in pixels)
         1596  +-- @see Map.tileInstances
         1597  +-- @see Tile
         1598  +
         1599  +--- @table Object
         1600  +-- @field id Global ID
         1601  +-- @field name Name of object (non-unique)
         1602  +-- @field shape Shape of object
         1603  +-- @field x Position of object on X axis (in pixels)
         1604  +-- @field y Position of object on Y axis (in pixels)
         1605  +-- @field width Width of object (in pixels)
         1606  +-- @field height Heigh tof object (in pixels)
         1607  +-- @field rotation Rotation of object (in radians)
         1608  +-- @field visible Toggle if object is visible or hidden
         1609  +-- @field properties Custom properties
         1610  +-- @field ellipse List of verticies of specific shape
         1611  +-- @field rectangle List of verticies of specific shape
         1612  +-- @field polygon List of verticies of specific shape
         1613  +-- @field polyline List of verticies of specific shape
         1614  +-- @see Map.objects
         1615  +
         1616  +return setmetatable({}, STI)

Added src/worpt/lib/sti/plugins/box2d.lua.

            1  +--- Box2D plugin for STI
            2  +-- @module box2d
            3  +-- @author Landon Manning
            4  +-- @copyright 2019
            5  +-- @license MIT/X11
            6  +
            7  +local love  = _G.love
            8  +local utils = require((...):gsub('plugins.box2d', 'utils'))
            9  +local lg    = require((...):gsub('plugins.box2d', 'graphics'))
           10  +
           11  +return {
           12  +	box2d_LICENSE     = "MIT/X11",
           13  +	box2d_URL         = "https://github.com/karai17/Simple-Tiled-Implementation",
           14  +	box2d_VERSION     = "2.3.2.7",
           15  +	box2d_DESCRIPTION = "Box2D hooks for STI.",
           16  +
           17  +	--- Initialize Box2D physics world.
           18  +	-- @param world The Box2D world to add objects to.
           19  +	box2d_init = function(map, world)
           20  +		assert(love.physics, "To use the Box2D plugin, please enable the love.physics module.")
           21  +
           22  +		local body      = love.physics.newBody(world, map.offsetx, map.offsety)
           23  +		local collision = {
           24  +			body = body,
           25  +		}
           26  +
           27  +		local function addObjectToWorld(objshape, vertices, userdata, object)
           28  +			local shape
           29  +
           30  +			if objshape == "polyline" then
           31  +				if #vertices == 4 then
           32  +					shape = love.physics.newEdgeShape(unpack(vertices))
           33  +				else
           34  +					shape = love.physics.newChainShape(false, unpack(vertices))
           35  +				end
           36  +			else
           37  +				shape = love.physics.newPolygonShape(unpack(vertices))
           38  +			end
           39  +
           40  +			local currentBody = body
           41  +
           42  +			if userdata.properties.dynamic == true then
           43  +				currentBody = love.physics.newBody(world, map.offsetx, map.offsety, 'dynamic')
           44  +			end
           45  +
           46  +			local fixture = love.physics.newFixture(currentBody, shape)
           47  +			fixture:setUserData(userdata)
           48  +
           49  +			-- Set some custom properties from userdata (or use default set by box2d)
           50  +			fixture:setFriction(userdata.properties.friction       or 0.2)
           51  +			fixture:setRestitution(userdata.properties.restitution or 0.0)
           52  +			fixture:setSensor(userdata.properties.sensor           or false)
           53  +			fixture:setFilterData(
           54  +				userdata.properties.categories or 1,
           55  +				userdata.properties.mask       or 65535,
           56  +				userdata.properties.group      or 0
           57  +			)
           58  +
           59  +			local obj = {
           60  +				object  = object,
           61  +				body    = currentBody,
           62  +				shape   = shape,
           63  +				fixture = fixture,
           64  +			}
           65  +
           66  +			table.insert(collision, obj)
           67  +		end
           68  +
           69  +		local function getPolygonVertices(object)
           70  +			local vertices = {}
           71  +			for _, vertex in ipairs(object.polygon) do
           72  +				table.insert(vertices, vertex.x)
           73  +				table.insert(vertices, vertex.y)
           74  +			end
           75  +
           76  +			return vertices
           77  +		end
           78  +
           79  +		local function calculateObjectPosition(object, tile)
           80  +			local o = {
           81  +				shape   = object.shape,
           82  +				x       = (object.dx or object.x) + map.offsetx,
           83  +				y       = (object.dy or object.y) + map.offsety,
           84  +				w       = object.width,
           85  +				h       = object.height,
           86  +				polygon = object.polygon or object.polyline or object.ellipse or object.rectangle
           87  +			}
           88  +
           89  +			local userdata = {
           90  +				object     = o,
           91  +				properties = object.properties
           92  +			}
           93  +
           94  +			o.r = object.rotation or 0
           95  +			if o.shape == "rectangle" then
           96  +				local cos = math.cos(math.rad(o.r))
           97  +				local sin = math.sin(math.rad(o.r))
           98  +				local oy  = 0
           99  +
          100  +				if object.gid then
          101  +					local tileset = map.tilesets[map.tiles[object.gid].tileset]
          102  +					local lid     = object.gid - tileset.firstgid
          103  +					local t       = {}
          104  +
          105  +					-- This fixes a height issue
          106  +					 o.y = o.y + map.tiles[object.gid].offset.y
          107  +					 oy  = o.h
          108  +
          109  +					for _, tt in ipairs(tileset.tiles) do
          110  +						if tt.id == lid then
          111  +							t = tt
          112  +							break
          113  +						end
          114  +					end
          115  +
          116  +					if t.objectGroup then
          117  +						for _, obj in ipairs(t.objectGroup.objects) do
          118  +							-- Every object in the tile
          119  +							calculateObjectPosition(obj, object)
          120  +						end
          121  +
          122  +						return
          123  +					else
          124  +						o.w = map.tiles[object.gid].width
          125  +						o.h = map.tiles[object.gid].height
          126  +					end
          127  +				end
          128  +
          129  +				o.polygon = {
          130  +					{ x=o.x+0,   y=o.y+0   },
          131  +					{ x=o.x+o.w, y=o.y+0   },
          132  +					{ x=o.x+o.w, y=o.y+o.h },
          133  +					{ x=o.x+0,   y=o.y+o.h }
          134  +				}
          135  +
          136  +				for _, vertex in ipairs(o.polygon) do
          137  +					vertex.x, vertex.y = utils.rotate_vertex(map, vertex, o.x, o.y, cos, sin, oy)
          138  +				end
          139  +
          140  +				local vertices = getPolygonVertices(o)
          141  +				addObjectToWorld(o.shape, vertices, userdata, tile or object)
          142  +			elseif o.shape == "ellipse" then
          143  +				if not o.polygon then
          144  +					o.polygon = utils.convert_ellipse_to_polygon(o.x, o.y, o.w, o.h)
          145  +				end
          146  +				local vertices  = getPolygonVertices(o)
          147  +				local triangles = love.math.triangulate(vertices)
          148  +
          149  +				for _, triangle in ipairs(triangles) do
          150  +					addObjectToWorld(o.shape, triangle, userdata, tile or object)
          151  +				end
          152  +			elseif o.shape == "polygon" then
          153  +				-- Recalculate collision polygons inside tiles
          154  +				if tile then
          155  +					local cos = math.cos(math.rad(o.r))
          156  +					local sin = math.sin(math.rad(o.r))
          157  +					for _, vertex in ipairs(o.polygon) do
          158  +						vertex.x = vertex.x + o.x
          159  +						vertex.y = vertex.y + o.y
          160  +						vertex.x, vertex.y = utils.rotate_vertex(map, vertex, o.x, o.y, cos, sin)
          161  +					end
          162  +				end
          163  +
          164  +				local vertices  = getPolygonVertices(o)
          165  +				local triangles = love.math.triangulate(vertices)
          166  +
          167  +				for _, triangle in ipairs(triangles) do
          168  +					addObjectToWorld(o.shape, triangle, userdata, tile or object)
          169  +				end
          170  +			elseif o.shape == "polyline" then
          171  +				local vertices = getPolygonVertices(o)
          172  +				addObjectToWorld(o.shape, vertices, userdata, tile or object)
          173  +			end
          174  +		end
          175  +
          176  +		for _, tile in pairs(map.tiles) do
          177  +			if map.tileInstances[tile.gid] then
          178  +				for _, instance in ipairs(map.tileInstances[tile.gid]) do
          179  +					-- Every object in every instance of a tile
          180  +					if tile.objectGroup then
          181  +						for _, object in ipairs(tile.objectGroup.objects) do
          182  +							if object.properties.collidable == true then
          183  +								object = utils.deepCopy(object)
          184  +								object.dx = instance.x + object.x
          185  +								object.dy = instance.y + object.y
          186  +								calculateObjectPosition(object, instance)
          187  +							end
          188  +						end
          189  +					end
          190  +
          191  +					-- Every instance of a tile
          192  +					if tile.properties.collidable == true then
          193  +						local object = {
          194  +							shape      = "rectangle",
          195  +							x          = instance.x,
          196  +							y          = instance.y,
          197  +							width      = map.tilewidth,
          198  +							height     = map.tileheight,
          199  +							properties = tile.properties
          200  +						}
          201  +
          202  +						calculateObjectPosition(object, instance)
          203  +					end
          204  +				end
          205  +			end
          206  +		end
          207  +
          208  +		for _, layer in ipairs(map.layers) do
          209  +			-- Entire layer
          210  +			if layer.properties.collidable == true then
          211  +				if layer.type == "tilelayer" then
          212  +					for gid, tiles in pairs(map.tileInstances) do
          213  +						local tile = map.tiles[gid]
          214  +						local tileset = map.tilesets[tile.tileset]
          215  +
          216  +						for _, instance in ipairs(tiles) do
          217  +							if instance.layer == layer then
          218  +								local object = {
          219  +									shape      = "rectangle",
          220  +									x          = instance.x,
          221  +									y          = instance.y,
          222  +									width      = tileset.tilewidth,
          223  +									height     = tileset.tileheight,
          224  +									properties = tile.properties
          225  +								}
          226  +
          227  +								calculateObjectPosition(object, instance)
          228  +							end
          229  +						end
          230  +					end
          231  +				elseif layer.type == "objectgroup" then
          232  +					for _, object in ipairs(layer.objects) do
          233  +						calculateObjectPosition(object)
          234  +					end
          235  +				elseif layer.type == "imagelayer" then
          236  +					local object = {
          237  +						shape      = "rectangle",
          238  +						x          = layer.x or 0,
          239  +						y          = layer.y or 0,
          240  +						width      = layer.width,
          241  +						height     = layer.height,
          242  +						properties = layer.properties
          243  +					}
          244  +
          245  +					calculateObjectPosition(object)
          246  +				end
          247  +			end
          248  +
          249  +			-- Individual objects
          250  +			if layer.type == "objectgroup" then
          251  +				for _, object in ipairs(layer.objects) do
          252  +					if object.properties.collidable == true then
          253  +						calculateObjectPosition(object)
          254  +					end
          255  +				end
          256  +			end
          257  +		end
          258  +
          259  +		map.box2d_collision = collision
          260  +	end,
          261  +
          262  +	--- Remove Box2D fixtures and shapes from world.
          263  +	-- @param index The index or name of the layer being removed
          264  +	box2d_removeLayer = function(map, index)
          265  +		local layer = assert(map.layers[index], "Layer not found: " .. index)
          266  +		local collision = map.box2d_collision
          267  +
          268  +		-- Remove collision objects
          269  +		for i = #collision, 1, -1 do
          270  +			local obj = collision[i]
          271  +
          272  +			if obj.object.layer == layer then
          273  +				obj.fixture:destroy()
          274  +				table.remove(collision, i)
          275  +			end
          276  +		end
          277  +	end,
          278  +
          279  +	--- Draw Box2D physics world.
          280  +	-- @param tx Translate on X
          281  +	-- @param ty Translate on Y
          282  +	-- @param sx Scale on X
          283  +	-- @param sy Scale on Y
          284  +	box2d_draw = function(map, tx, ty, sx, sy)
          285  +		local collision = map.box2d_collision
          286  +
          287  +		lg.push()
          288  +		lg.scale(sx or 1, sy or sx or 1)
          289  +		lg.translate(math.floor(tx or 0), math.floor(ty or 0))
          290  +
          291  +		for _, obj in ipairs(collision) do
          292  +			local points = {obj.body:getWorldPoints(obj.shape:getPoints())}
          293  +			local shape_type = obj.shape:getType()
          294  +
          295  +			if shape_type == "edge" or shape_type == "chain" then
          296  +				love.graphics.line(points)
          297  +			elseif shape_type == "polygon" then
          298  +				love.graphics.polygon("line", points)
          299  +			else
          300  +				error("sti box2d plugin does not support "..shape_type.." shapes")
          301  +			end
          302  +		end
          303  +
          304  +		lg.pop()
          305  +	end
          306  +}
          307  +
          308  +--- Custom Properties in Tiled are used to tell this plugin what to do.
          309  +-- @table Properties
          310  +-- @field collidable set to true, can be used on any Layer, Tile, or Object
          311  +-- @field sensor set to true, can be used on any Tile or Object that is also collidable
          312  +-- @field dynamic set to true, can be used on any Tile or Object
          313  +-- @field friction can be used to define the friction of any Object
          314  +-- @field restitution can be used to define the restitution of any Object
          315  +-- @field categories can be used to set the filter Category of any Object
          316  +-- @field mask can be used to set the filter Mask of any Object
          317  +-- @field group can be used to set the filter Group of any Object

Added src/worpt/lib/sti/plugins/bump.lua.

            1  +--- Bump.lua plugin for STI
            2  +-- @module bump.lua
            3  +-- @author David Serrano (BobbyJones|FrenchFryLord)
            4  +-- @copyright 2019
            5  +-- @license MIT/X11
            6  +
            7  +local lg = require((...):gsub('plugins.bump', 'graphics'))
            8  +
            9  +return {
           10  +	bump_LICENSE        = "MIT/X11",
           11  +	bump_URL            = "https://github.com/karai17/Simple-Tiled-Implementation",
           12  +	bump_VERSION        = "3.1.7.0",
           13  +	bump_DESCRIPTION    = "Bump hooks for STI.",
           14  +
           15  +	--- Adds each collidable tile to the Bump world.
           16  +	-- @param world The Bump world to add objects to.
           17  +	-- @return collidables table containing the handles to the objects in the Bump world.
           18  +	bump_init = function(map, world)
           19  +		local collidables = {}
           20  +
           21  +		for _, tileset in ipairs(map.tilesets) do
           22  +			for _, tile in ipairs(tileset.tiles) do
           23  +				local gid = tileset.firstgid + tile.id
           24  +
           25  +				if map.tileInstances[gid] then
           26  +					for _, instance in ipairs(map.tileInstances[gid]) do
           27  +						-- Every object in every instance of a tile
           28  +						if tile.objectGroup then
           29  +							for _, object in ipairs(tile.objectGroup.objects) do
           30  +								if object.properties.collidable == true then
           31  +									local t = {
           32  +										name       = object.name,
           33  +										type       = object.type,
           34  +										x          = instance.x + map.offsetx + object.x,
           35  +										y          = instance.y + map.offsety + object.y,
           36  +										width      = object.width,
           37  +										height     = object.height,
           38  +										layer      = instance.layer,
           39  +										properties = object.properties
           40  +
           41  +									}
           42  +
           43  +									world:add(t, t.x, t.y, t.width, t.height)
           44  +									table.insert(collidables, t)
           45  +								end
           46  +							end
           47  +						end
           48  +
           49  +						-- Every instance of a tile
           50  +						if tile.properties and tile.properties.collidable == true then
           51  +							local t = {
           52  +								x          = instance.x + map.offsetx,
           53  +								y          = instance.y + map.offsety,
           54  +								width      = map.tilewidth,
           55  +								height     = map.tileheight,
           56  +								layer      = instance.layer,
           57  +								properties = tile.properties
           58  +							}
           59  +
           60  +							world:add(t, t.x, t.y, t.width, t.height)
           61  +							table.insert(collidables, t)
           62  +						end
           63  +					end
           64  +				end
           65  +			end
           66  +		end
           67  +
           68  +		for _, layer in ipairs(map.layers) do
           69  +			-- Entire layer
           70  +			if layer.properties.collidable == true then
           71  +				if layer.type == "tilelayer" then
           72  +					for y, tiles in ipairs(layer.data) do
           73  +						for x, tile in pairs(tiles) do
           74  +
           75  +							if tile.objectGroup then
           76  +								for _, object in ipairs(tile.objectGroup.objects) do
           77  +									if object.properties.collidable == true then
           78  +										local t = {
           79  +											name       = object.name,
           80  +											type       = object.type,
           81  +											x          = ((x-1) * map.tilewidth  + tile.offset.x + map.offsetx) + object.x,
           82  +											y          = ((y-1) * map.tileheight + tile.offset.y + map.offsety) + object.y,
           83  +											width      = object.width,
           84  +											height     = object.height,
           85  +											layer      = layer,
           86  +											properties = object.properties
           87  +										}
           88  +
           89  +										world:add(t, t.x, t.y, t.width, t.height)
           90  +										table.insert(collidables, t)
           91  +									end
           92  +								end
           93  +							end
           94  +
           95  +
           96  +							local t = {
           97  +								x          = (x-1) * map.tilewidth  + tile.offset.x + map.offsetx,
           98  +								y          = (y-1) * map.tileheight + tile.offset.y + map.offsety,
           99  +								width      = tile.width,
          100  +								height     = tile.height,
          101  +								layer      = layer,
          102  +								properties = tile.properties
          103  +							}
          104  +
          105  +							world:add(t, t.x, t.y, t.width, t.height)
          106  +							table.insert(collidables, t)
          107  +						end
          108  +					end
          109  +				elseif layer.type == "imagelayer" then
          110  +					world:add(layer, layer.x, layer.y, layer.width, layer.height)
          111  +					table.insert(collidables, layer)
          112  +				end
          113  +		  end
          114  +
          115  +			-- individual collidable objects in a layer that is not "collidable"
          116  +			-- or whole collidable objects layer
          117  +		  if layer.type == "objectgroup" then
          118  +				for _, obj in ipairs(layer.objects) do
          119  +					if layer.properties.collidable == true or obj.properties.collidable == true then
          120  +						if obj.shape == "rectangle" then
          121  +							local t = {
          122  +								name       = obj.name,
          123  +								type       = obj.type,
          124  +								x          = obj.x + map.offsetx,
          125  +								y          = obj.y + map.offsety,
          126  +								width      = obj.width,
          127  +								height     = obj.height,
          128  +								layer      = layer,
          129  +								properties = obj.properties
          130  +							}
          131  +
          132  +							if obj.gid then
          133  +								t.y = t.y - obj.height
          134  +							end
          135  +
          136  +							world:add(t, t.x, t.y, t.width, t.height)
          137  +							table.insert(collidables, t)
          138  +						end -- TODO implement other object shapes?
          139  +					end
          140  +				end
          141  +			end
          142  +
          143  +		end
          144  +		map.bump_collidables = collidables
          145  +	end,
          146  +
          147  +	--- Remove layer
          148  +	-- @param index to layer to be removed
          149  +	-- @param world bump world the holds the tiles
          150  +	-- @param tx Translate on X
          151  +-- @param ty Translate on Y
          152  +-- @param sx Scale on X
          153  +-- @param sy Scale on Y
          154  +	bump_removeLayer = function(map, index, world)
          155  +		local layer = assert(map.layers[index], "Layer not found: " .. index)
          156  +		local collidables = map.bump_collidables
          157  +
          158  +		-- Remove collision objects
          159  +		for i = #collidables, 1, -1 do
          160  +			local obj = collidables[i]
          161  +
          162  +			if obj.layer == layer
          163  +			and (
          164  +				layer.properties.collidable == true
          165  +				or obj.properties.collidable == true
          166  +			) then
          167  +				world:remove(obj)
          168  +				table.remove(collidables, i)
          169  +			end
          170  +		end
          171  +	end,
          172  +
          173  +	--- Draw bump collisions world.
          174  +	-- @param world bump world holding the tiles geometry
          175  +	-- @param tx Translate on X
          176  +	-- @param ty Translate on Y
          177  +	-- @param sx Scale on X
          178  +	-- @param sy Scale on Y
          179  +	bump_draw = function(map, world, tx, ty, sx, sy)
          180  +		lg.push()
          181  +		lg.scale(sx or 1, sy or sx or 1)
          182  +		lg.translate(math.floor(tx or 0), math.floor(ty or 0))
          183  +
          184  +		for _, collidable in pairs(map.bump_collidables) do
          185  +			lg.rectangle("line", world:getRect(collidable))
          186  +		end
          187  +
          188  +		lg.pop()
          189  +	end
          190  +}
          191  +
          192  +--- Custom Properties in Tiled are used to tell this plugin what to do.
          193  +-- @table Properties
          194  +-- @field collidable set to true, can be used on any Layer, Tile, or Object

Added src/worpt/lib/sti/utils.lua.

            1  +-- Some utility functions that shouldn't be exposed.
            2  +local utils = {}
            3  +
            4  +-- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
            5  +function utils.format_path(path)
            6  +	local np_gen1,np_gen2  = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
            7  +	local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
            8  +	local k
            9  +
           10  +	repeat -- /./ -> /
           11  +		path,k = path:gsub(np_pat2,'/',1)
           12  +	until k == 0
           13  +
           14  +	repeat -- A/../ -> (empty)
           15  +		path,k = path:gsub(np_pat1,'',1)
           16  +	until k == 0
           17  +
           18  +	if path == '' then path = '.' end
           19  +
           20  +	return path
           21  +end
           22  +
           23  +-- Compensation for scale/rotation shift
           24  +function utils.compensate(tile, tileX, tileY, tileW, tileH)
           25  +	local compx = 0
           26  +	local compy = 0
           27  +
           28  +	if tile.sx < 0 then compx = tileW end
           29  +	if tile.sy < 0 then compy = tileH end
           30  +
           31  +	if tile.r > 0 then
           32  +		tileX = tileX + tileH - compy
           33  +		tileY = tileY + tileH + compx - tileW
           34  +	elseif tile.r < 0 then
           35  +		tileX = tileX + compy
           36  +		tileY = tileY - compx + tileH
           37  +	else
           38  +		tileX = tileX + compx
           39  +		tileY = tileY + compy
           40  +	end
           41  +
           42  +	return tileX, tileY
           43  +end
           44  +
           45  +-- Cache images in main STI module
           46  +function utils.cache_image(sti, path, image)
           47  +	image = image or love.graphics.newImage(path)
           48  +	image:setFilter("nearest", "nearest")
           49  +	sti.cache[path] = image
           50  +end
           51  +
           52  +-- We just don't know.
           53  +function utils.get_tiles(imageW, tileW, margin, spacing)
           54  +	imageW  = imageW - margin
           55  +	local n = 0
           56  +
           57  +	while imageW >= tileW do
           58  +		imageW = imageW - tileW
           59  +		if n ~= 0 then imageW = imageW - spacing end
           60  +		if imageW >= 0 then n  = n + 1 end
           61  +	end
           62  +
           63  +	return n
           64  +end
           65  +
           66  +-- Decompress tile layer data
           67  +function utils.get_decompressed_data(data)
           68  +	local ffi     = require "ffi"
           69  +	local d       = {}
           70  +	local decoded = ffi.cast("uint32_t*", data)
           71  +
           72  +	for i = 0, data:len() / ffi.sizeof("uint32_t") do
           73  +		table.insert(d, tonumber(decoded[i]))
           74  +	end
           75  +
           76  +	return d
           77  +end
           78  +
           79  +-- Convert a Tiled ellipse object to a LOVE polygon
           80  +function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
           81  +	local ceil = math.ceil
           82  +	local cos  = math.cos
           83  +	local sin  = math.sin
           84  +
           85  +	local function calc_segments(segments)
           86  +		local function vdist(a, b)
           87  +			local c = {
           88  +				x = a.x - b.x,
           89  +				y = a.y - b.y,
           90  +			}
           91  +
           92  +			return c.x * c.x + c.y * c.y
           93  +		end
           94  +
           95  +		segments = segments or 64
           96  +		local vertices = {}
           97  +
           98  +		local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) }
           99  +
          100  +		local m
          101  +		if love and love.physics then
          102  +			m = love.physics.getMeter()
          103  +		else
          104  +			m = 32
          105  +		end
          106  +
          107  +		for _, i in ipairs(v) do
          108  +			local angle = (i / segments) * math.pi * 2
          109  +			local px    = x + w / 2 + cos(angle) * w / 2
          110  +			local py    = y + h / 2 + sin(angle) * h / 2
          111  +
          112  +			table.insert(vertices, { x = px / m, y = py / m })
          113  +		end
          114  +
          115  +		local dist1 = vdist(vertices[1], vertices[2])
          116  +		local dist2 = vdist(vertices[3], vertices[4])
          117  +
          118  +		-- Box2D threshold
          119  +		if dist1 < 0.0025 or dist2 < 0.0025 then
          120  +			return calc_segments(segments-2)
          121  +		end
          122  +
          123  +		return segments
          124  +	end
          125  +
          126  +	local segments = calc_segments(max_segments)
          127  +	local vertices = {}
          128  +
          129  +	table.insert(vertices, { x = x + w / 2, y = y + h / 2 })
          130  +
          131  +	for i = 0, segments do
          132  +		local angle = (i / segments) * math.pi * 2
          133  +		local px    = x + w / 2 + cos(angle) * w / 2
          134  +		local py    = y + h / 2 + sin(angle) * h / 2
          135  +
          136  +		table.insert(vertices, { x = px, y = py })
          137  +	end
          138  +
          139  +	return vertices
          140  +end
          141  +
          142  +function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
          143  +	if map.orientation == "isometric" then
          144  +		x, y               = utils.convert_isometric_to_screen(map, x, y)
          145  +		vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y)
          146  +	end
          147  +
          148  +	vertex.x = vertex.x - x
          149  +	vertex.y = vertex.y - y
          150  +
          151  +	return
          152  +		x + cos * vertex.x - sin * vertex.y,
          153  +		y + sin * vertex.x + cos * vertex.y - (oy or 0)
          154  +end
          155  +
          156  +--- Project isometric position to cartesian position
          157  +function utils.convert_isometric_to_screen(map, x, y)
          158  +	local mapW    = map.width
          159  +	local tileW   = map.tilewidth
          160  +	local tileH   = map.tileheight
          161  +	local tileX   = x / tileH
          162  +	local tileY   = y / tileH
          163  +	local offsetX = mapW * tileW / 2
          164  +
          165  +	return
          166  +		(tileX - tileY) * tileW / 2 + offsetX,
          167  +		(tileX + tileY) * tileH / 2
          168  +end
          169  +
          170  +function utils.hex_to_color(hex)
          171  +	if hex:sub(1, 1) == "#" then
          172  +		hex = hex:sub(2)
          173  +	end
          174  +
          175  +	return {
          176  +		r = tonumber(hex:sub(1, 2), 16) / 255,
          177  +		g = tonumber(hex:sub(3, 4), 16) / 255,
          178  +		b = tonumber(hex:sub(5, 6), 16) / 255
          179  +	}
          180  +end
          181  +
          182  +function utils.pixel_function(_, _, r, g, b, a)
          183  +	local mask = utils._TC
          184  +
          185  +	if r == mask.r and
          186  +		g == mask.g and
          187  +		b == mask.b then
          188  +		return r, g, b, 0
          189  +	end
          190  +
          191  +	return r, g, b, a
          192  +end
          193  +
          194  +function utils.fix_transparent_color(tileset, path)
          195  +	local image_data = love.image.newImageData(path)
          196  +	tileset.image = love.graphics.newImage(image_data)
          197  +
          198  +	if tileset.transparentcolor then
          199  +		utils._TC = utils.hex_to_color(tileset.transparentcolor)
          200  +
          201  +		image_data:mapPixel(utils.pixel_function)
          202  +		tileset.image = love.graphics.newImage(image_data)
          203  +	end
          204  +end
          205  +
          206  +function utils.deepCopy(t)
          207  +	local copy = {}
          208  +	for k,v in pairs(t) do
          209  +		if type(v) == "table" then
          210  +			v = utils.deepCopy(v)
          211  +		end
          212  +		copy[k] = v
          213  +	end
          214  +	return copy
          215  +end
          216  +
          217  +return utils

Added src/worpt/license.txt.

            1  +                    GNU GENERAL PUBLIC LICENSE
            2  +                       Version 3, 29 June 2007
            3  +
            4  + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
            5  + Everyone is permitted to copy and distribute verbatim copies
            6  + of this license document, but changing it is not allowed.
            7  +
            8  +                            Preamble
            9  +
           10  +  The GNU General Public License is a free, copyleft license for
           11  +software and other kinds of works.
           12  +
           13  +  The licenses for most software and other practical works are designed
           14  +to take away your freedom to share and change the works.  By contrast,
           15  +the GNU General Public License is intended to guarantee your freedom to
           16  +share and change all versions of a program--to make sure it remains free
           17  +software for all its users.  We, the Free Software Foundation, use the
           18  +GNU General Public License for most of our software; it applies also to
           19  +any other work released this way by its authors.  You can apply it to
           20  +your programs, too.
           21  +
           22  +  When we speak of free software, we are referring to freedom, not
           23  +price.  Our General Public Licenses are designed to make sure that you
           24  +have the freedom to distribute copies of free software (and charge for
           25  +them if you wish), that you receive source code or can get it if you
           26  +want it, that you can change the software or use pieces of it in new
           27  +free programs, and that you know you can do these things.
           28  +
           29  +  To protect your rights, we need to prevent others from denying you
           30  +these rights or asking you to surrender the rights.  Therefore, you have
           31  +certain responsibilities if you distribute copies of the software, or if
           32  +you modify it: responsibilities to respect the freedom of others.
           33  +
           34  +  For example, if you distribute copies of such a program, whether
           35  +gratis or for a fee, you must pass on to the recipients the same
           36  +freedoms that you received.  You must make sure that they, too, receive
           37  +or can get the source code.  And you must show them these terms so they
           38  +know their rights.
           39  +
           40  +  Developers that use the GNU GPL protect your rights with two steps:
           41  +(1) assert copyright on the software, and (2) offer you this License
           42  +giving you legal permission to copy, distribute and/or modify it.
           43  +
           44  +  For the developers' and authors' protection, the GPL clearly explains
           45  +that there is no warranty for this free software.  For both users' and
           46  +authors' sake, the GPL requires that modified versions be marked as
           47  +changed, so that their problems will not be attributed erroneously to
           48  +authors of previous versions.
           49  +
           50  +  Some devices are designed to deny users access to install or run
           51  +modified versions of the software inside them, although the manufacturer
           52  +can do so.  This is fundamentally incompatible with the aim of
           53  +protecting users' freedom to change the software.  The systematic
           54  +pattern of such abuse occurs in the area of products for individuals to
           55  +use, which is precisely where it is most unacceptable.  Therefore, we
           56  +have designed this version of the GPL to prohibit the practice for those
           57  +products.  If such problems arise substantially in other domains, we
           58  +stand ready to extend this provision to those domains in future versions
           59  +of the GPL, as needed to protect the freedom of users.
           60  +
           61  +  Finally, every program is threatened constantly by software patents.
           62  +States should not allow patents to restrict development and use of
           63  +software on general-purpose computers, but in those that do, we wish to
           64  +avoid the special danger that patents applied to a free program could
           65  +make it effectively proprietary.  To prevent this, the GPL assures that
           66  +patents cannot be used to render the program non-free.
           67  +
           68  +  The precise terms and conditions for copying, distribution and
           69  +modification follow.
           70  +
           71  +                       TERMS AND CONDITIONS
           72  +
           73  +  0. Definitions.
           74  +
           75  +  "This License" refers to version 3 of the GNU General Public License.
           76  +
           77  +  "Copyright" also means copyright-like laws that apply to other kinds of
           78  +works, such as semiconductor masks.
           79  +
           80  +  "The Program" refers to any copyrightable work licensed under this
           81  +License.  Each licensee is addressed as "you".  "Licensees" and
           82  +"recipients" may be individuals or organizations.
           83  +
           84  +  To "modify" a work means to copy from or adapt all or part of the work
           85  +in a fashion requiring copyright permission, other than the making of an
           86  +exact copy.  The resulting work is called a "modified version" of the
           87  +earlier work or a work "based on" the earlier work.
           88  +
           89  +  A "covered work" means either the unmodified Program or a work based
           90  +on the Program.
           91  +
           92  +  To "propagate" a work means to do anything with it that, without
           93  +permission, would make you directly or secondarily liable for
           94  +infringement under applicable copyright law, except executing it on a
           95  +computer or modifying a private copy.  Propagation includes copying,
           96  +distribution (with or without modification), making available to the
           97  +public, and in some countries other activities as well.
           98  +
           99  +  To "convey" a work means any kind of propagation that enables other
          100  +parties to make or receive copies.  Mere interaction with a user through
          101  +a computer network, with no transfer of a copy, is not conveying.
          102  +
          103  +  An interactive user interface displays "Appropriate Legal Notices"
          104  +to the extent that it includes a convenient and prominently visible
          105  +feature that (1) displays an appropriate copyright notice, and (2)
          106  +tells the user that there is no warranty for the work (except to the
          107  +extent that warranties are provided), that licensees may convey the
          108  +work under this License, and how to view a copy of this License.  If
          109  +the interface presents a list of user commands or options, such as a
          110  +menu, a prominent item in the list meets this criterion.
          111  +
          112  +  1. Source Code.
          113  +
          114  +  The "source code" for a work means the preferred form of the work
          115  +for making modifications to it.  "Object code" means any non-source
          116  +form of a work.
          117  +
          118  +  A "Standard Interface" means an interface that either is an official
          119  +standard defined by a recognized standards body, or, in the case of
          120  +interfaces specified for a particular programming language, one that
          121  +is widely used among developers working in that language.
          122  +
          123  +  The "System Libraries" of an executable work include anything, other
          124  +than the work as a whole, that (a) is included in the normal form of
          125  +packaging a Major Component, but which is not part of that Major
          126  +Component, and (b) serves only to enable use of the work with that
          127  +Major Component, or to implement a Standard Interface for which an
          128  +implementation is available to the public in source code form.  A
          129  +"Major Component", in this context, means a major essential component
          130  +(kernel, window system, and so on) of the specific operating system
          131  +(if any) on which the executable work runs, or a compiler used to
          132  +produce the work, or an object code interpreter used to run it.
          133  +
          134  +  The "Corresponding Source" for a work in object code form means all
          135  +the source code needed to generate, install, and (for an executable
          136  +work) run the object code and to modify the work, including scripts to
          137  +control those activities.  However, it does not include the work's
          138  +System Libraries, or general-purpose tools or generally available free
          139  +programs which are used unmodified in performing those activities but
          140  +which are not part of the work.  For example, Corresponding Source
          141  +includes interface definition files associated with source files for
          142  +the work, and the source code for shared libraries and dynamically
          143  +linked subprograms that the work is specifically designed to require,
          144  +such as by intimate data communication or control flow between those
          145  +subprograms and other parts of the work.
          146  +
          147  +  The Corresponding Source need not include anything that users
          148  +can regenerate automatically from other parts of the Corresponding
          149  +Source.
          150  +
          151  +  The Corresponding Source for a work in source code form is that
          152  +same work.
          153  +
          154  +  2. Basic Permissions.
          155  +
          156  +  All rights granted under this License are granted for the term of
          157  +copyright on the Program, and are irrevocable provided the stated
          158  +conditions are met.  This License explicitly affirms your unlimited
          159  +permission to run the unmodified Program.  The output from running a
          160  +covered work is covered by this License only if the output, given its
          161  +content, constitutes a covered work.  This License acknowledges your
          162  +rights of fair use or other equivalent, as provided by copyright law.
          163  +
          164  +  You may make, run and propagate covered works that you do not
          165  +convey, without conditions so long as your license otherwise remains
          166  +in force.  You may convey covered works to others for the sole purpose
          167  +of having them make modifications exclusively for you, or provide you
          168  +with facilities for running those works, provided that you comply with
          169  +the terms of this License in conveying all material for which you do
          170  +not control copyright.  Those thus making or running the covered works
          171  +for you must do so exclusively on your behalf, under your direction
          172  +and control, on terms that prohibit them from making any copies of
          173  +your copyrighted material outside their relationship with you.
          174  +
          175  +  Conveying under any other circumstances is permitted solely under
          176  +the conditions stated below.  Sublicensing is not allowed; section 10
          177  +makes it unnecessary.
          178  +
          179  +  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
          180  +
          181  +  No covered work shall be deemed part of an effective technological
          182  +measure under any applicable law fulfilling obligations under article
          183  +11 of the WIPO copyright treaty adopted on 20 December 1996, or
          184  +similar laws prohibiting or restricting circumvention of such
          185  +measures.
          186  +
          187  +  When you convey a covered work, you waive any legal power to forbid
          188  +circumvention of technological measures to the extent such circumvention
          189  +is effected by exercising rights under this License with respect to
          190  +the covered work, and you disclaim any intention to limit operation or
          191  +modification of the work as a means of enforcing, against the work's
          192  +users, your or third parties' legal rights to forbid circumvention of
          193  +technological measures.
          194  +
          195  +  4. Conveying Verbatim Copies.
          196  +
          197  +  You may convey verbatim copies of the Program's source code as you
          198  +receive it, in any medium, provided that you conspicuously and
          199  +appropriately publish on each copy an appropriate copyright notice;
          200  +keep intact all notices stating that this License and any
          201  +non-permissive terms added in accord with section 7 apply to the code;
          202  +keep intact all notices of the absence of any warranty; and give all
          203  +recipients a copy of this License along with the Program.
          204  +
          205  +  You may charge any price or no price for each copy that you convey,
          206  +and you may offer support or warranty protection for a fee.
          207  +
          208  +  5. Conveying Modified Source Versions.
          209  +
          210  +  You may convey a work based on the Program, or the modifications to
          211  +produce it from the Program, in the form of source code under the
          212  +terms of section 4, provided that you also meet all of these conditions:
          213  +
          214  +    a) The work must carry prominent notices stating that you modified
          215  +    it, and giving a relevant date.
          216  +
          217  +    b) The work must carry prominent notices stating that it is
          218  +    released under this License and any conditions added under section
          219  +    7.  This requirement modifies the requirement in section 4 to
          220  +    "keep intact all notices".
          221  +
          222  +    c) You must license the entire work, as a whole, under this
          223  +    License to anyone who comes into possession of a copy.  This
          224  +    License will therefore apply, along with any applicable section 7
          225  +    additional terms, to the whole of the work, and all its parts,
          226  +    regardless of how they are packaged.  This License gives no
          227  +    permission to license the work in any other way, but it does not
          228  +    invalidate such permission if you have separately received it.
          229  +
          230  +    d) If the work has interactive user interfaces, each must display
          231  +    Appropriate Legal Notices; however, if the Program has interactive
          232  +    interfaces that do not display Appropriate Legal Notices, your
          233  +    work need not make them do so.
          234  +
          235  +  A compilation of a covered work with other separate and independent
          236  +works, which are not by their nature extensions of the covered work,
          237  +and which are not combined with it such as to form a larger program,
          238  +in or on a volume of a storage or distribution medium, is called an
          239  +"aggregate" if the compilation and its resulting copyright are not
          240  +used to limit the access or legal rights of the compilation's users
          241  +beyond what the individual works permit.  Inclusion of a covered work
          242  +in an aggregate does not cause this License to apply to the other
          243  +parts of the aggregate.
          244  +
          245  +  6. Conveying Non-Source Forms.
          246  +
          247  +  You may convey a covered work in object code form under the terms
          248  +of sections 4 and 5, provided that you also convey the
          249  +machine-readable Corresponding Source under the terms of this License,
          250  +in one of these ways:
          251  +
          252  +    a) Convey the object code in, or embodied in, a physical product
          253  +    (including a physical distribution medium), accompanied by the
          254  +    Corresponding Source fixed on a durable physical medium
          255  +    customarily used for software interchange.
          256  +
          257  +    b) Convey the object code in, or embodied in, a physical product
          258  +    (including a physical distribution medium), accompanied by a
          259  +    written offer, valid for at least three years and valid for as
          260  +    long as you offer spare parts or customer support for that product
          261  +    model, to give anyone who possesses the object code either (1) a
          262  +    copy of the Corresponding Source for all the software in the
          263  +    product that is covered by this License, on a durable physical
          264  +    medium customarily used for software interchange, for a price no
          265  +    more than your reasonable cost of physically performing this
          266  +    conveying of source, or (2) access to copy the
          267  +    Corresponding Source from a network server at no charge.
          268  +
          269  +    c) Convey individual copies of the object code with a copy of the
          270  +    written offer to provide the Corresponding Source.  This
          271  +    alternative is allowed only occasionally and noncommercially, and
          272  +    only if you received the object code with such an offer, in accord
          273  +    with subsection 6b.
          274  +
          275  +    d) Convey the object code by offering access from a designated
          276  +    place (gratis or for a charge), and offer equivalent access to the
          277  +    Corresponding Source in the same way through the same place at no
          278  +    further charge.  You need not require recipients to copy the
          279  +    Corresponding Source along with the object code.  If the place to
          280  +    copy the object code is a network server, the Corresponding Source
          281  +    may be on a different server (operated by you or a third party)
          282  +    that supports equivalent copying facilities, provided you maintain
          283  +    clear directions next to the object code saying where to find the
          284  +    Corresponding Source.  Regardless of what server hosts the
          285  +    Corresponding Source, you remain obligated to ensure that it is
          286  +    available for as long as needed to satisfy these requirements.
          287  +
          288  +    e) Convey the object code using peer-to-peer transmission, provided
          289  +    you inform other peers where the object code and Corresponding
          290  +    Source of the work are being offered to the general public at no
          291  +    charge under subsection 6d.
          292  +
          293  +  A separable portion of the object code, whose source code is excluded
          294  +from the Corresponding Source as a System Library, need not be
          295  +included in conveying the object code work.
          296  +
          297  +  A "User Product" is either (1) a "consumer product", which means any
          298  +tangible personal property which is normally used for personal, family,
          299  +or household purposes, or (2) anything designed or sold for incorporation
          300  +into a dwelling.  In determining whether a product is a consumer product,
          301  +doubtful cases shall be resolved in favor of coverage.  For a particular
          302  +product received by a particular user, "normally used" refers to a
          303  +typical or common use of that class of product, regardless of the status
          304  +of the particular user or of the way in which the particular user
          305  +actually uses, or expects or is expected to use, the product.  A product
          306  +is a consumer product regardless of whether the product has substantial
          307  +commercial, industrial or non-consumer uses, unless such uses represent
          308  +the only significant mode of use of the product.
          309  +
          310  +  "Installation Information" for a User Product means any methods,
          311  +procedures, authorization keys, or other information required to install
          312  +and execute modified versions of a covered work in that User Product from
          313  +a modified version of its Corresponding Source.  The information must
          314  +suffice to ensure that the continued functioning of the modified object
          315  +code is in no case prevented or interfered with solely because
          316  +modification has been made.
          317  +
          318  +  If you convey an object code work under this section in, or with, or
          319  +specifically for use in, a User Product, and the conveying occurs as
          320  +part of a transaction in which the right of possession and use of the
          321  +User Product is transferred to the recipient in perpetuity or for a
          322  +fixed term (regardless of how the transaction is characterized), the
          323  +Corresponding Source conveyed under this section must be accompanied
          324  +by the Installation Information.  But this requirement does not apply
          325  +if neither you nor any third party retains the ability to install
          326  +modified object code on the User Product (for example, the work has
          327  +been installed in ROM).
          328  +
          329  +  The requirement to provide Installation Information does not include a
          330  +requirement to continue to provide support service, warranty, or updates
          331  +for a work that has been modified or installed by the recipient, or for
          332  +the User Product in which it has been modified or installed.  Access to a
          333  +network may be denied when the modification itself materially and
          334  +adversely affects the operation of the network or violates the rules and
          335  +protocols for communication across the network.
          336  +
          337  +  Corresponding Source conveyed, and Installation Information provided,
          338  +in accord with this section must be in a format that is publicly
          339  +documented (and with an implementation available to the public in
          340  +source code form), and must require no special password or key for
          341  +unpacking, reading or copying.
          342  +
          343  +  7. Additional Terms.
          344  +
          345  +  "Additional permissions" are terms that supplement the terms of this
          346  +License by making exceptions from one or more of its conditions.
          347  +Additional permissions that are applicable to the entire Program shall
          348  +be treated as though they were included in this License, to the extent
          349  +that they are valid under applicable law.  If additional permissions
          350  +apply only to part of the Program, that part may be used separately
          351  +under those permissions, but the entire Program remains governed by
          352  +this License without regard to the additional permissions.
          353  +
          354  +  When you convey a copy of a covered work, you may at your option
          355  +remove any additional permissions from that copy, or from any part of
          356  +it.  (Additional permissions may be written to require their own
          357  +removal in certain cases when you modify the work.)  You may place
          358  +additional permissions on material, added by you to a covered work,
          359  +for which you have or can give appropriate copyright permission.
          360  +
          361  +  Notwithstanding any other provision of this License, for material you
          362  +add to a covered work, you may (if authorized by the copyright holders of
          363  +that material) supplement the terms of this License with terms:
          364  +
          365  +    a) Disclaiming warranty or limiting liability differently from the
          366  +    terms of sections 15 and 16 of this License; or
          367  +
          368  +    b) Requiring preservation of specified reasonable legal notices or
          369  +    author attributions in that material or in the Appropriate Legal
          370  +    Notices displayed by works containing it; or
          371  +
          372  +    c) Prohibiting misrepresentation of the origin of that material, or
          373  +    requiring that modified versions of such material be marked in
          374  +    reasonable ways as different from the original version; or
          375  +
          376  +    d) Limiting the use for publicity purposes of names of licensors or
          377  +    authors of the material; or
          378  +
          379  +    e) Declining to grant rights under trademark law for use of some
          380  +    trade names, trademarks, or service marks; or
          381  +
          382  +    f) Requiring indemnification of licensors and authors of that
          383  +    material by anyone who conveys the material (or modified versions of
          384  +    it) with contractual assumptions of liability to the recipient, for
          385  +    any liability that these contractual assumptions directly impose on
          386  +    those licensors and authors.
          387  +
          388  +  All other non-permissive additional terms are considered "further
          389  +restrictions" within the meaning of section 10.  If the Program as you
          390  +received it, or any part of it, contains a notice stating that it is
          391  +governed by this License along with a term that is a further
          392  +restriction, you may remove that term.  If a license document contains
          393  +a further restriction but permits relicensing or conveying under this
          394  +License, you may add to a covered work material governed by the terms
          395  +of that license document, provided that the further restriction does
          396  +not survive such relicensing or conveying.
          397  +
          398  +  If you add terms to a covered work in accord with this section, you
          399  +must place, in the relevant source files, a statement of the
          400  +additional terms that apply to those files, or a notice indicating
          401  +where to find the applicable terms.
          402  +
          403  +  Additional terms, permissive or non-permissive, may be stated in the
          404  +form of a separately written license, or stated as exceptions;
          405  +the above requirements apply either way.
          406  +
          407  +  8. Termination.
          408  +
          409  +  You may not propagate or modify a covered work except as expressly
          410  +provided under this License.  Any attempt otherwise to propagate or
          411  +modify it is void, and will automatically terminate your rights under
          412  +this License (including any patent licenses granted under the third
          413  +paragraph of section 11).
          414  +
          415  +  However, if you cease all violation of this License, then your
          416  +license from a particular copyright holder is reinstated (a)
          417  +provisionally, unless and until the copyright holder explicitly and
          418  +finally terminates your license, and (b) permanently, if the copyright
          419  +holder fails to notify you of the violation by some reasonable means
          420  +prior to 60 days after the cessation.
          421  +
          422  +  Moreover, your license from a particular copyright holder is
          423  +reinstated permanently if the copyright holder notifies you of the
          424  +violation by some reasonable means, this is the first time you have
          425  +received notice of violation of this License (for any work) from that
          426  +copyright holder, and you cure the violation prior to 30 days after
          427  +your receipt of the notice.
          428  +
          429  +  Termination of your rights under this section does not terminate the
          430  +licenses of parties who have received copies or rights from you under
          431  +this License.  If your rights have been terminated and not permanently
          432  +reinstated, you do not qualify to receive new licenses for the same
          433  +material under section 10.
          434  +
          435  +  9. Acceptance Not Required for Having Copies.
          436  +
          437  +  You are not required to accept this License in order to receive or
          438  +run a copy of the Program.  Ancillary propagation of a covered work
          439  +occurring solely as a consequence of using peer-to-peer transmission
          440  +to receive a copy likewise does not require acceptance.  However,
          441  +nothing other than this License grants you permission to propagate or
          442  +modify any covered work.  These actions infringe copyright if you do
          443  +not accept this License.  Therefore, by modifying or propagating a
          444  +covered work, you indicate your acceptance of this License to do so.
          445  +
          446  +  10. Automatic Licensing of Downstream Recipients.
          447  +
          448  +  Each time you convey a covered work, the recipient automatically
          449  +receives a license from the original licensors, to run, modify and
          450  +propagate that work, subject to this License.  You are not responsible
          451  +for enforcing compliance by third parties with this License.
          452  +
          453  +  An "entity transaction" is a transaction transferring control of an
          454  +organization, or substantially all assets of one, or subdividing an
          455  +organization, or merging organizations.  If propagation of a covered
          456  +work results from an entity transaction, each party to that
          457  +transaction who receives a copy of the work also receives whatever
          458  +licenses to the work the party's predecessor in interest had or could
          459  +give under the previous paragraph, plus a right to possession of the
          460  +Corresponding Source of the work from the predecessor in interest, if
          461  +the predecessor has it or can get it with reasonable efforts.
          462  +
          463  +  You may not impose any further restrictions on the exercise of the
          464  +rights granted or affirmed under this License.  For example, you may
          465  +not impose a license fee, royalty, or other charge for exercise of
          466  +rights granted under this License, and you may not initiate litigation
          467  +(including a cross-claim or counterclaim in a lawsuit) alleging that
          468  +any patent claim is infringed by making, using, selling, offering for
          469  +sale, or importing the Program or any portion of it.
          470  +
          471  +  11. Patents.
          472  +
          473  +  A "contributor" is a copyright holder who authorizes use under this
          474  +License of the Program or a work on which the Program is based.  The
          475  +work thus licensed is called the contributor's "contributor version".
          476  +
          477  +  A contributor's "essential patent claims" are all patent claims
          478  +owned or controlled by the contributor, whether already acquired or
          479  +hereafter acquired, that would be infringed by some manner, permitted
          480  +by this License, of making, using, or selling its contributor version,
          481  +but do not include claims that would be infringed only as a
          482  +consequence of further modification of the contributor version.  For
          483  +purposes of this definition, "control" includes the right to grant
          484  +patent sublicenses in a manner consistent with the requirements of
          485  +this License.
          486  +
          487  +  Each contributor grants you a non-exclusive, worldwide, royalty-free
          488  +patent license under the contributor's essential patent claims, to
          489  +make, use, sell, offer for sale, import and otherwise run, modify and
          490  +propagate the contents of its contributor version.
          491  +
          492  +  In the following three paragraphs, a "patent license" is any express
          493  +agreement or commitment, however denominated, not to enforce a patent
          494  +(such as an express permission to practice a patent or covenant not to
          495  +sue for patent infringement).  To "grant" such a patent license to a
          496  +party means to make such an agreement or commitment not to enforce a
          497  +patent against the party.
          498  +
          499  +  If you convey a covered work, knowingly relying on a patent license,
          500  +and the Corresponding Source of the work is not available for anyone
          501  +to copy, free of charge and under the terms of this License, through a
          502  +publicly available network server or other readily accessible means,
          503  +then you must either (1) cause the Corresponding Source to be so
          504  +available, or (2) arrange to deprive yourself of the benefit of the
          505  +patent license for this particular work, or (3) arrange, in a manner
          506  +consistent with the requirements of this License, to extend the patent
          507  +license to downstream recipients.  "Knowingly relying" means you have
          508  +actual knowledge that, but for the patent license, your conveying the
          509  +covered work in a country, or your recipient's use of the covered work
          510  +in a country, would infringe one or more identifiable patents in that
          511  +country that you have reason to believe are valid.
          512  +
          513  +  If, pursuant to or in connection with a single transaction or
          514  +arrangement, you convey, or propagate by procuring conveyance of, a
          515  +covered work, and grant a patent license to some of the parties
          516  +receiving the covered work authorizing them to use, propagate, modify
          517  +or convey a specific copy of the covered work, then the patent license
          518  +you grant is automatically extended to all recipients of the covered
          519  +work and works based on it.
          520  +
          521  +  A patent license is "discriminatory" if it does not include within
          522  +the scope of its coverage, prohibits the exercise of, or is
          523  +conditioned on the non-exercise of one or more of the rights that are
          524  +specifically granted under this License.  You may not convey a covered
          525  +work if you are a party to an arrangement with a third party that is
          526  +in the business of distributing software, under which you make payment
          527  +to the third party based on the extent of your activity of conveying
          528  +the work, and under which the third party grants, to any of the
          529  +parties who would receive the covered work from you, a discriminatory
          530  +patent license (a) in connection with copies of the covered work
          531  +conveyed by you (or copies made from those copies), or (b) primarily
          532  +for and in connection with specific products or compilations that
          533  +contain the covered work, unless you entered into that arrangement,
          534  +or that patent license was granted, prior to 28 March 2007.
          535  +
          536  +  Nothing in this License shall be construed as excluding or limiting
          537  +any implied license or other defenses to infringement that may
          538  +otherwise be available to you under applicable patent law.
          539  +
          540  +  12. No Surrender of Others' Freedom.
          541  +
          542  +  If conditions are imposed on you (whether by court order, agreement or
          543  +otherwise) that contradict the conditions of this License, they do not
          544  +excuse you from the conditions of this License.  If you cannot convey a
          545  +covered work so as to satisfy simultaneously your obligations under this
          546  +License and any other pertinent obligations, then as a consequence you may
          547  +not convey it at all.  For example, if you agree to terms that obligate you
          548  +to collect a royalty for further conveying from those to whom you convey
          549  +the Program, the only way you could satisfy both those terms and this
          550  +License would be to refrain entirely from conveying the Program.
          551  +
          552  +  13. Use with the GNU Affero General Public License.
          553  +
          554  +  Notwithstanding any other provision of this License, you have
          555  +permission to link or combine any covered work with a work licensed
          556  +under version 3 of the GNU Affero General Public License into a single
          557  +combined work, and to convey the resulting work.  The terms of this
          558  +License will continue to apply to the part which is the covered work,
          559  +but the special requirements of the GNU Affero General Public License,
          560  +section 13, concerning interaction through a network will apply to the
          561  +combination as such.
          562  +
          563  +  14. Revised Versions of this License.
          564  +
          565  +  The Free Software Foundation may publish revised and/or new versions of
          566  +the GNU General Public License from time to time.  Such new versions will
          567  +be similar in spirit to the present version, but may differ in detail to
          568  +address new problems or concerns.
          569  +
          570  +  Each version is given a distinguishing version number.  If the
          571  +Program specifies that a certain numbered version of the GNU General
          572  +Public License "or any later version" applies to it, you have the
          573  +option of following the terms and conditions either of that numbered
          574  +version or of any later version published by the Free Software
          575  +Foundation.  If the Program does not specify a version number of the
          576  +GNU General Public License, you may choose any version ever published
          577  +by the Free Software Foundation.
          578  +
          579  +  If the Program specifies that a proxy can decide which future
          580  +versions of the GNU General Public License can be used, that proxy's
          581  +public statement of acceptance of a version permanently authorizes you
          582  +to choose that version for the Program.
          583  +
          584  +  Later license versions may give you additional or different
          585  +permissions.  However, no additional obligations are imposed on any
          586  +author or copyright holder as a result of your choosing to follow a
          587  +later version.
          588  +
          589  +  15. Disclaimer of Warranty.
          590  +
          591  +  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
          592  +APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
          593  +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
          594  +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
          595  +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
          596  +PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
          597  +IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
          598  +ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
          599  +
          600  +  16. Limitation of Liability.
          601  +
          602  +  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
          603  +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
          604  +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
          605  +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
          606  +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
          607  +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
          608  +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
          609  +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
          610  +SUCH DAMAGES.
          611  +
          612  +  17. Interpretation of Sections 15 and 16.
          613  +
          614  +  If the disclaimer of warranty and limitation of liability provided
          615  +above cannot be given local legal effect according to their terms,
          616  +reviewing courts shall apply local law that most closely approximates
          617  +an absolute waiver of all civil liability in connection with the
          618  +Program, unless a warranty or assumption of liability accompanies a
          619  +copy of the Program in return for a fee.
          620  +
          621  +                     END OF TERMS AND CONDITIONS
          622  +
          623  +            How to Apply These Terms to Your New Programs
          624  +
          625  +  If you develop a new program, and you want it to be of the greatest
          626  +possible use to the public, the best way to achieve this is to make it
          627  +free software which everyone can redistribute and change under these terms.
          628  +
          629  +  To do so, attach the following notices to the program.  It is safest
          630  +to attach them to the start of each source file to most effectively
          631  +state the exclusion of warranty; and each file should have at least
          632  +the "copyright" line and a pointer to where the full notice is found.
          633  +
          634  +    <one line to give the program's name and a brief idea of what it does.>
          635  +    Copyright (C) <year>  <name of author>
          636  +
          637  +    This program is free software: you can redistribute it and/or modify
          638  +    it under the terms of the GNU General Public License as published by
          639  +    the Free Software Foundation, either version 3 of the License, or
          640  +    (at your option) any later version.
          641  +
          642  +    This program is distributed in the hope that it will be useful,
          643  +    but WITHOUT ANY WARRANTY; without even the implied warranty of
          644  +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
          645  +    GNU General Public License for more details.
          646  +
          647  +    You should have received a copy of the GNU General Public License
          648  +    along with this program.  If not, see <https://www.gnu.org/licenses/>.
          649  +
          650  +Also add information on how to contact you by electronic and paper mail.
          651  +
          652  +  If the program does terminal interaction, make it output a short
          653  +notice like this when it starts in an interactive mode:
          654  +
          655  +    <program>  Copyright (C) <year>  <name of author>
          656  +    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
          657  +    This is free software, and you are welcome to redistribute it
          658  +    under certain conditions; type `show c' for details.
          659  +
          660  +The hypothetical commands `show w' and `show c' should show the appropriate
          661  +parts of the General Public License.  Of course, your program's commands
          662  +might be different; for a GUI interface, you would use an "about box".
          663  +
          664  +  You should also get your employer (if you work as a programmer) or school,
          665  +if any, to sign a "copyright disclaimer" for the program, if necessary.
          666  +For more information on this, and how to apply and follow the GNU GPL, see
          667  +<https://www.gnu.org/licenses/>.
          668  +
          669  +  The GNU General Public License does not permit incorporating your program
          670  +into proprietary programs.  If your program is a subroutine library, you
          671  +may consider it more useful to permit linking proprietary applications with
          672  +the library.  If this is what you want to do, use the GNU Lesser General
          673  +Public License instead of this License.  But first, please read
          674  +<https://www.gnu.org/philosophy/why-not-lgpl.html>.

Added src/worpt/love-release.sh.

            1  +#!/usr/bin/env bash
            2  +
            3  +## For users on macos
            4  +## #!/usr/local/Cellar/bash/4.4.23/bin/bash
            5  +
            6  +# LÖVE version
            7  +LOVE_DEF_VERSION=0.11.3
            8  +
            9  +
           10  +
           11  +# Helper functions
           12  +
           13  +# Dependencies check
           14  +check_deps () {
           15  +    command -v curl  > /dev/null 2>&1 || {
           16  +        >&2 echo "curl is not installed. Aborting."
           17  +        local EXIT=true
           18  +    }
           19  +    command -v zip   > /dev/null 2>&1 || {
           20  +        >&2 echo "zip is not installed. Aborting."
           21  +        local EXIT=true
           22  +    }
           23  +    command -v unzip > /dev/null 2>&1 || {
           24  +        >&2 echo "unzip is not installed. Aborting."
           25  +        local EXIT=true
           26  +    }
           27  +    command -v getopt > /dev/null 2>&1 || {
           28  +        local opt=false
           29  +    } && {
           30  +        unset GETOPT_COMPATIBLE
           31  +        local out=$(getopt -T)
           32  +        if (( $? != 4 )) && [[ -n $out ]]; then
           33  +            local opt=false
           34  +        fi
           35  +    }
           36  +    if [[ $opt == false ]]; then
           37  +        >&2 echo "GNU getopt is not installed. Aborting."
           38  +        local EXIT=true
           39  +    fi
           40  +    if ! command -v readlink > /dev/null 2>&1 || ! readlink -m / > /dev/null 2>&1; then
           41  +        command -v greadlink > /dev/null 2>&1 || {
           42  +            >&2 echo "GNU readlink is not installed. Aborting."
           43  +            local EXIT=true
           44  +        } && {
           45  +            readlink () {
           46  +                greadlink "$@"
           47  +            }
           48  +        }
           49  +    fi
           50  +
           51  +    command -v lua   > /dev/null 2>&1 || {
           52  +        echo "lua is not installed. Install it to ease your releases."
           53  +    } && {
           54  +        LUA=true
           55  +    }
           56  +    if [[ $EXIT == true ]]; then
           57  +        exit_module "deps"
           58  +    fi
           59  +}
           60  +
           61  +# Get user confirmation, simple Yes/No question
           62  +## $1: message, usually just a question
           63  +## $2: default choice, 0 - yes; 1 - no, default - yes
           64  +## return: 0 - yes, 1 - no
           65  +get_user_confirmation () {
           66  +    if [[ -z $2 || $2 == "0" ]]; then
           67  +        read -n 1 -p "$1 [Y/n]: " yn
           68  +        local default=0
           69  +    else
           70  +        read -n 1 -p "$1 [y/N]: " yn
           71  +        local default=1
           72  +    fi
           73  +    case $yn in
           74  +        [Yy]* )
           75  +            echo; return 0;;
           76  +        [Nn]* )
           77  +            echo; return 1;;
           78  +        "" )
           79  +            return $default;;
           80  +        * )
           81  +            echo; return $default;;
           82  +    esac
           83  +}
           84  +
           85  +
           86  +# Generate LÖVE version variables
           87  +## $1: LÖVE version string
           88  +## return: 0 - string matched, 1 - else
           89  +gen_version () {
           90  +    if [[ $1 =~ ^([0-9]+)\.([0-9]+)(\.([0-9]+))?$ ]]; then
           91  +        LOVE_VERSION=$1
           92  +        LOVE_VERSION_MAJOR=${BASH_REMATCH[1]}
           93  +        LOVE_VERSION_MINOR=${BASH_REMATCH[2]}
           94  +        LOVE_VERSION_REVISION=${BASH_REMATCH[4]}
           95  +        return 0
           96  +    fi
           97  +    return 1
           98  +}
           99  +
          100  +
          101  +# Compare two LÖVE versions
          102  +## $1: First LÖVE version
          103  +## $2: comparison operator
          104  +##     "ge", "le", "gt" "lt"
          105  +##     ">=", "<=", ">", "<"
          106  +## $3: Second LÖVE version
          107  +## return: 0 - true, 1 - false
          108  +compare_version () {
          109  +    if [[ $1 =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
          110  +        local v1_maj=${BASH_REMATCH[1]}
          111  +        local v1_min=${BASH_REMATCH[2]}
          112  +        local v1_rev=${BASH_REMATCH[3]}
          113  +    else
          114  +        return 1
          115  +    fi
          116  +    if [[ $3 =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
          117  +        local v2_maj=${BASH_REMATCH[1]}
          118  +        local v2_min=${BASH_REMATCH[2]}
          119  +        local v2_rev=${BASH_REMATCH[3]}
          120  +    else
          121  +        return 1
          122  +    fi
          123  +
          124  +    case $2 in
          125  +        ge|\>= )
          126  +            if (( $v1_maj >= $v2_maj && $v1_min >= $v2_min && $v1_rev >= $v2_rev )); then
          127  +                return 0
          128  +            else
          129  +                return 1
          130  +            fi
          131  +            ;;
          132  +        le|\<= )
          133  +            if (( $v1_maj <= $v2_maj && $v1_min <= $v2_min && $v1_rev <= $v2_rev )); then
          134  +                return 0
          135  +            else
          136  +                return 1
          137  +            fi
          138  +            ;;
          139  +        gt|\> )
          140  +            if (( $v1_maj > $v2_maj || ( $v1_max == $v2_max && $v1_min > $v2_min ) ||
          141  +                ( $v1_max == $v2_max && $v1_min == $v2_min && $v1_rev > $v2_rev ) )); then
          142  +                return 0
          143  +            else
          144  +                return 1
          145  +            fi
          146  +            ;;
          147  +        lt|\< )
          148  +            if (( $v1_maj < $v2_maj || ( $v1_max == $v2_max && $v1_min < $v2_min ) ||
          149  +                ( $v1_max == $v2_max && $v1_min == $v2_min && $v1_rev < $v2_rev ) )); then
          150  +                return 0
          151  +            else
          152  +                return 1
          153  +            fi
          154  +            ;;
          155  +    esac
          156  +}
          157  +
          158  +
          159  +# Read configuration
          160  +## $1: system name
          161  +read_config () {
          162  +    if [[ $LUA == true ]] && [[ -f "conf.lua" ]]; then
          163  +        local var=$(lua - <<EOF
          164  +f = loadfile("conf.lua")
          165  +t, love = {window = {}, modules = {}, screen = {}}, {}
          166  +f()
          167  +love.conf(t)
          168  +
          169  +-- "love", "windows", "osx", "debian" or "android"
          170  +os = "$1"
          171  +
          172  +fields = {
          173  +    "identity", "version", "game_version", "icon", "exclude",
          174  +    "title", "author", "email", "url", "description", }
          175  +
          176  +for _, f in ipairs(fields) do
          177  +    t[f] = t[f] or ""
          178  +end
          179  +
          180  +t.os = t.os or {}
          181  +for _, v in ipairs(t.os) do
          182  +    t.os[v] = {}
          183  +end
          184  +
          185  +if os == "default" then
          186  +    t.os.default = {}
          187  +end
          188  +
          189  +if t.os[os] then
          190  +    print(os:upper()..'=true')
          191  +    for _, f in ipairs(fields) do
          192  +        t.os[os][f] = t.os[os][f] or t[f]
          193  +        if type(t.os[os][f]) == "table" then
          194  +            str = f:upper()..'=('
          195  +            for _, v in ipairs(t.os[os][f]) do
          196  +                str = str..' "'..v..'"'
          197  +            end
          198  +            str = str..' )'
          199  +            print(str)
          200  +        else
          201  +            print(f:upper()..'="'..t.os[os][f]..'"')
          202  +        end
          203  +    end
          204  +else
          205  +    print(os:upper()..'=false')
          206  +end
          207  +
          208  +if t.os.windows and os == "windows" then
          209  +    t.os.windows.x32 = t.os.windows.x32 and true or false
          210  +    t.os.windows.x64 = t.os.windows.x64 and true or false
          211  +    t.os.windows.installer = t.os.windows.installer and true or false
          212  +    t.os.windows.appid = t.os.windows.appid or ""
          213  +    print("X32="..tostring(t.os.windows.x32))
          214  +    print("X64="..tostring(t.os.windows.x64))
          215  +    print("INSTALLER="..tostring(t.os.windows.installer))
          216  +    print("APPID="..t.os.windows.appid)
          217  +end
          218  +EOF
          219  +)
          220  +        eval "$var"
          221  +    fi
          222  +}
          223  +
          224  +
          225  +# Read script options
          226  +## $1: options prefix
          227  +read_options () {
          228  +    local pre="$1"
          229  +    eval set -- "$ARGS"
          230  +    while true; do
          231  +        case "$1" in
          232  +            -a|--${pre}author )       AUTHOR="$2"; shift 2 ;;
          233  +            --${pre}clean )           rm -rf "$CACHE_DIR"; shift ;;
          234  +            -d|--${pre}description )  DESCRIPTION="$2"; shift 2 ;;
          235  +            -e|--${pre}email )        EMAIL="$2"; shift 2 ;;
          236  +            -h|--${pre}help )         short_help; exit 0 ;;
          237  +            -i|--${pre}icon )
          238  +                ICON="$2"
          239  +                if [[ -d $ICON ]]; then
          240  +                    local icon="$(readlink -m "$ICON")"
          241  +                    local wd="$(readlink -m "$PWD")"
          242  +                    EXCLUDE+=( "${icon//$wd\/}/*" )
          243  +                elif [[ -f $ICON ]]; then
          244  +                    EXCLUDE+=( "$ICON" )
          245  +                fi
          246  +                shift 2 ;;
          247  +            -l|--${pre}love )         if ! gen_version "$2"; then exit_module "version"; fi; shift 2 ;;
          248  +            -f|--${pre}lovefile )     LOVEFILE="$2"; shift 2 ;;
          249  +            -p|--${pre}pkg )          IDENTITY="$2"; shift 2 ;;
          250  +            -r|--${pre}release )      RELEASE_DIR="$2"; shift 2 ;;
          251  +            -t|--${pre}title )        TITLE="$2"; shift 2 ;;
          252  +            -u|--${pre}url )          URL="$2"; shift 2 ;;
          253  +            -v|--${pre}version )      GAME_VERSION="$2"; shift 2 ;;
          254  +            -x|--${pre}exclude )      EXCLUDE+=( "$2" ); shift 2 ;;
          255  +            -- ) shift; break ;;
          256  +            * ) shift ;;
          257  +        esac
          258  +    done
          259  +}
          260  +
          261  +
          262  +# Test if default module should be executed
          263  +default_module () {
          264  +    if [[ $? -ne 2 ]]; then
          265  +        DEFAULT_MODULE=false
          266  +    fi
          267  +}
          268  +
          269  +# Print short help
          270  +short_help () {
          271  +    cat <<EndOfSHelp
          272  +Usage: love-release.sh [options...] [files...]
          273  +Options:
          274  + -h           Print short help
          275  + -t <title>   Set the project's title
          276  + -r <release> Set the release directory
          277  + -v <version> Set the LÖVE version
          278  +Modules:
          279  + -L    LÖVE
          280  +\ -A    Android\
          281  +\ -D    Debian\
          282  +\ -M    Mac OS X\
          283  +\ -W    Windows
          284  +EndOfSHelp
          285  +}
          286  +
          287  +dump_var () {
          288  +    echo "LOVE_VERSION=$LOVE_VERSION"
          289  +    echo "LOVE_DEF_VERSION=$LOVE_DEF_VERSION"
          290  +    echo "LOVE_WEB_VERSION=$LOVE_WEB_VERSION"
          291  +    echo
          292  +    echo "RELEASE_DIR=$RELEASE_DIR"
          293  +    echo "CACHE_DIR=$CACHE_DIR"
          294  +    echo
          295  +    echo "IDENTITY=$IDENTITY"
          296  +    echo "GAME_VERSION=$GAME_VERSION"
          297  +    echo "ICON=$ICON"
          298  +    echo
          299  +    echo "TITLE=$TITLE"
          300  +    echo "AUTHOR=$AUTHOR"
          301  +    echo "EMAIL=$EMAIL"
          302  +    echo "URL=$URL"
          303  +    echo "DESCRIPTION=$DESCRIPTION"
          304  +    echo
          305  +    echo "${FILES[@]}"
          306  +}
          307  +
          308  +
          309  +# Modules functions
          310  +
          311  +# Init module
          312  +## $1: Pretty module name
          313  +## $2: Configuration module name
          314  +## $3: Module option
          315  +## return: 0 - if module should be executed, else exit 2
          316  +init_module () {
          317  +    (
          318  +        opt="$3"
          319  +        if (( ${#opt} == 1 )); then opt="-$opt"
          320  +        elif (( ${#opt} >= 2 )); then opt="--$opt"; fi
          321  +        eval set -- "$ARGS"
          322  +        while true; do
          323  +            case "$1" in
          324  +                $opt ) exit 0 ;;
          325  +                -- )   exit 1 ;;
          326  +                * )    shift ;;
          327  +            esac
          328  +        done
          329  +    )
          330  +    local opt=$?
          331  +    local module="$2"
          332  +    read_config "$module"
          333  +    module=${module^^}
          334  +    if (( $opt == 0 )); then
          335  +        if [[ ${!module} == false ]]; then
          336  +            read_config "default"
          337  +        fi
          338  +    else
          339  +        if [[ ${!module} == false ]]; then
          340  +            exit_module "execute"
          341  +        fi
          342  +    fi
          343  +    gen_version $VERSION
          344  +    unset VERSION
          345  +    MODULE="$1"
          346  +    CACHE_DIR="$CACHE_DIR/$2"
          347  +    read_options "$3"
          348  +    LOVE_FILE="${TITLE}.love"
          349  +    mkdir -p "$RELEASE_DIR" "$CACHE_DIR"
          350  +    echo "Generating $TITLE with LÖVE $LOVE_VERSION for ${MODULE}..."
          351  +    return 0
          352  +}
          353  +
          354  +# Create the LÖVE file
          355  +## $1: Compression level 0-9
          356  +create_love_file () {
          357  +    if [[ -r $LOVEFILE ]]; then
          358  +        cp "$LOVEFILE" $RELEASE_DIR/$LOVE_FILE
          359  +    else
          360  +        local dotfiles=()
          361  +        for file in .*; do
          362  +            if [[ $file == '.' || $file == '..' ]]; then continue; fi
          363  +            if [[ -d $file ]]; then file="$file/*"; fi
          364  +            dotfiles+=( "$file" )
          365  +        done
          366  +        local release_dir="$(readlink -m "$RELEASE_DIR")"
          367  +        local wd="$(readlink -m "$PWD")"
          368  +        zip -FS -$1 -r "$RELEASE_DIR/$LOVE_FILE" \
          369  +            -x "$0" "${release_dir//$wd\/}/*" "${dotfiles[@]}" "${EXCLUDE[@]}" @ \
          370  +            "${FILES[@]}"
          371  +    fi
          372  +}
          373  +
          374  +# Exit module
          375  +## $1: optional error identifier
          376  +## $2: optional error message
          377  +exit_module () {
          378  +    if [[ -z $1 ]]; then
          379  +        echo "Done !"
          380  +        exit 0
          381  +    fi
          382  +    if [[ -n $2 ]]; then
          383  +        >&2 echo -e "$2"
          384  +    fi
          385  +    case $1 in
          386  +        execute )
          387  +            exit 2 ;;
          388  +        binary )
          389  +            >&2 echo "LÖVE $LOVE_VERSION could not be found or downloaded."
          390  +            exit 3 ;;
          391  +        options )
          392  +            exit 4 ;;
          393  +        version )
          394  +            >&2 echo "LÖVE version string is invalid."
          395  +            exit 5 ;;
          396  +        deps )
          397  +            exit 6 ;;
          398  +        undef|* )
          399  +            exit 1 ;;
          400  +    esac
          401  +}
          402  +
          403  +
          404  +
          405  +# Main
          406  +
          407  +check_deps
          408  +
          409  +# Get latest LÖVE version number
          410  +gen_version $LOVE_DEF_VERSION
          411  +LOVE_WEB_VERSION=$(curl -s https://love2d.org/releases.xml | grep -m 2 "<title>" | tail -n 1 | grep -Eo "[0-9]+.[0-9]+.[0-9]+")
          412  +gen_version $LOVE_WEB_VERSION
          413  +
          414  +INSTALLED=false
          415  +EMBEDDED=true
          416  +
          417  +DEFAULT_MODULE=true
          418  +
          419  +TITLE="$(basename $(pwd))"
          420  +PROJECT_DIR="$PWD"
          421  +RELEASE_DIR=releases
          422  +CACHE_DIR=~/.cache/love-release
          423  +FILES=()
          424  +EXCLUDE=()
          425  +
          426  +OPTIONS="W::MDALa:d:e:hi:l:p:r:t:u:v:x:"
          427  +LONG_OPTIONS="Wauthor:,Wclean,Wdescription:,Wemail:,Wexclude:,Whelp,Wicon:,Wlove:,Wlovefile:,Wpkg:,Wrelease:,Wtitle:,Wurl:,Wversion:,Wappid:,Winstaller,Mauthor:,Mclean,Mdescription:,Memail:,Mexclude:,Mhelp,Micon:,Mlove:,Mlovefile:,Mpkg:,Mrelease:,Mtitle:,Murl:,Mversion:,Dauthor:,Dclean,Ddescription:,Demail:,Dexclude:,Dhelp,Dicon:,Dlove:,Dlovefile:,Dpkg:,Drelease:,Dtitle:,Durl:,Dversion:,Aauthor:,Aclean,Adescription:,Aemail:,Aexclude:,Ahelp,Aicon:,Alove:,Alovefile:,Apkg:,Arelease:,Atitle:,Aurl:,Aversion:,Aactivity:,Aupdate,author:,clean,description:,email:,exclude:,help,icon:,love:,lovefile:,pkg:,release:,title:,url:,version:"
          428  +ARGS=$(getopt -o "$OPTIONS" -l "$LONG_OPTIONS" -n 'love-release' -- "$@")
          429  +if (( $? != 0 )); then short_help; exit_module "options"; fi
          430  +eval set -- "$ARGS"
          431  +read_options
          432  +while [[ $1 != '--' ]]; do shift; done; shift
          433  +for arg do
          434  +    FILES+=( "$arg" )
          435  +done
          436  +if (( ${#FILES} == 0 )); then FILES+=( "." ); fi
          437  +eval set -- "$ARGS"
          438  +
          439  +if [[ $INSTALLED == false && $EMBEDDED == false ]]; then
          440  +    exit_module "undef" "love-release has not been installed, and is not embedded into one script."
          441  +fi
          442  +
          443  +if [[ ! -f "main.lua" ]]; then
          444  +    >&2 echo "No main.lua file was found."
          445  +    exit_module 1
          446  +fi
          447  +
          448  +if [[ $EMBEDDED == true ]]; then
          449  +    : # include_scripts_here
          450  +(source <(cat <<\EndOfModule
          451  +# Android debug package
          452  +init_module "Android" "android" "A"
          453  +OPTIONS="A"
          454  +LONG_OPTIONS="activity:,update"
          455  +
          456  +
          457  +IDENTITY=$(echo $TITLE | sed -e 's/[^-a-zA-Z0-9_]/-/g' | tr '[:upper:]' '[:lower:]')
          458  +ACTIVITY=$(echo $TITLE | sed -e 's/[^a-zA-Z0-9_]/_/g')
          459  +
          460  +
          461  +# Options
          462  +while true; do
          463  +    case "$1" in
          464  +        --Aactivity ) ACTIVITY="$2"; shift 2 ;;
          465  +        --Aupdate )   UPDATE_ANDROID=true; shift ;;
          466  +        -- ) break ;;
          467  +        * ) shift ;;
          468  +    esac
          469  +done
          470  +
          471  +
          472  +# Android
          473  +missing_info=false
          474  +missing_deps=false
          475  +error_msg="Could not build Android package."
          476  +if ! command -v git > /dev/null 2>&1; then
          477  +    missing_deps=true
          478  +    error_msg="$error_msg\ngit was not found."
          479  +fi
          480  +if ! command -v ndk-build > /dev/null 2>&1; then
          481  +    missing_deps=true
          482  +    error_msg="$error_msg\nndk-build was not found."
          483  +fi
          484  +if ! command -v ant > /dev/null 2>&1; then
          485  +    missing_deps=true
          486  +    error_msg="$error_msg\nant was not found."
          487  +fi
          488  +if [[ $missing_deps == true  ]]; then
          489  +    exit_module "deps" "$error_msg"
          490  +fi
          491  +
          492  +if [[ -z $GAME_VERSION ]]; then
          493  +    missing_info=true
          494  +    error_msg="$error_msg\nMissing project's version. Use -v or --Aversion."
          495  +fi
          496  +if [[ -z $AUTHOR ]]; then
          497  +    missing_info=true
          498  +    error_msg="$error_msg\nMissing maintainer's name. Use -a or --Aauthor."
          499  +fi
          500  +if [[ $missing_info == true  ]]; then
          501  +    exit_module "options" "$error_msg"
          502  +fi
          503  +
          504  +
          505  +create_love_file 0
          506  +
          507  +
          508  +LOVE_ANDROID_DIR="$CACHE_DIR/love-android-sdl2"
          509  +if [[ -d $LOVE_ANDROID_DIR ]]; then
          510  +    cd "$LOVE_ANDROID_DIR"
          511  +    git checkout -- .
          512  +    rm -rf src/com bin gen
          513  +    if [[ $UPDATE_ANDROID = true ]]; then
          514  +        LOCAL=$(git rev-parse @)
          515  +        REMOTE=$(git rev-parse @{u})
          516  +        BASE=$(git merge-base @ @{u})
          517  +        if [[ $LOCAL == $REMOTE ]]; then
          518  +            echo "love-android-sdl2 is already up-to-date."
          519  +        elif [[ $LOCAL == $BASE ]]; then
          520  +            git pull
          521  +            ndk-build --jobs $(( $(nproc) + 1))
          522  +        fi
          523  +    fi
          524  +else
          525  +    cd "$CACHE_DIR"
          526  +    git clone https://bitbucket.org/MartinFelis/love-android-sdl2.git
          527  +    cd "$LOVE_ANDROID_DIR"
          528  +    ndk-build --jobs $(( $(nproc) + 1))
          529  +fi
          530  +
          531  +ANDROID_VERSION=$(grep -Eo -m 1 "[0-9]+.[0-9]+.[0-9]+[a-z]*" "$LOVE_ANDROID_DIR"/AndroidManifest.xml)
          532  +ANDROID_LOVE_VERSION=$(echo "$ANDROID_VERSION" | grep -Eo "[0-9]+.[0-9]+.[0-9]+")
          533  +
          534  +if [[ "$LOVE_VERSION" != "$ANDROID_LOVE_VERSION" ]]; then
          535  +    exit_module 1 "Love version ($LOVE_VERSION) differs from love-android-sdl2 version ($ANDROID_LOVE_VERSION). Could not create package."
          536  +fi
          537  +
          538  +mkdir -p assets
          539  +cd "$PROJECT_DIR"
          540  +cd "$RELEASE_DIR"
          541  +cp "$LOVE_FILE" "$LOVE_ANDROID_DIR/assets/game.love"
          542  +cd "$LOVE_ANDROID_DIR"
          543  +
          544  +sed -i.bak -e "s/org.love2d.android/com.${AUTHOR}.${IDENTITY}/" \
          545  +    -e "s/$ANDROID_VERSION/${ANDROID_VERSION}-${IDENTITY}-v${GAME_VERSION}/" \
          546  +    -e "0,/LÖVE for Android/s//$TITLE $GAME_VERSION/" \
          547  +    -e "s/LÖVE for Android/$TITLE/" \
          548  +    -e "s/GameActivity/$ACTIVITY/" \
          549  +    AndroidManifest.xml
          550  +
          551  +mkdir -p "src/com/$AUTHOR/$IDENTITY"
          552  +cat > "src/com/$AUTHOR/$IDENTITY/${ACTIVITY}.java" <<EOF
          553  +package com.${AUTHOR}.${IDENTITY};
          554  +import org.love2d.android.GameActivity;
          555  +
          556  +public class $ACTIVITY extends GameActivity {}
          557  +EOF
          558  +
          559  +if [[ -d "$ICON" ]]; then
          560  +    cd "$PROJECT_DIR"
          561  +    cd "$ICON"
          562  +
          563  +    for icon in *; do
          564  +        RES=$(echo "$icon" | grep -Eo "[0-9]+x[0-9]+")
          565  +        EXT=$(echo "$icon" | sed -e 's/.*\.//g')
          566  +        if [[ $RES == "42x42" ]]; then
          567  +            cp "$icon" "$LOVE_ANDROID_DIR/res/drawable-mdpi/ic_launcher.png"
          568  +        elif [[ $RES == "72x72" ]]; then
          569  +            cp "$icon" "$LOVE_ANDROID_DIR/res/drawable-hdpi/ic_launcher.png"
          570  +        elif [[ $RES == "96x96" ]]; then
          571  +            cp "$icon" "$LOVE_ANDROID_DIR/res/drawable-xhdpi/ic_launcher.png"
          572  +        elif [[ $RES == "144x144" ]]; then
          573  +            cp "$icon" "$LOVE_ANDROID_DIR/res/drawable-xxhdpi/ic_launcher.png"
          574  +        elif [[ "$RES" == "732x412" ]]; then
          575  +            cp "$icon" "$LOVE_ANDROID_DIR/res/drawable-xhdpi/ouya_icon.png"
          576  +        fi
          577  +    done
          578  +    if [[ -f "drawable-mdpi/ic_launcher.png" ]]; then
          579  +        cp "drawable-mdpi/ic_launcher.png" "$LOVE_ANDROID_DIR/res/drawable-mdpi/ic_launcher.png"
          580  +    fi
          581  +    if [[ -f "drawable-hdpi/ic_launcher.png" ]]; then
          582  +        cp "drawable-hdpi/ic_launcher.png" "$LOVE_ANDROID_DIR/res/drawable-hdpi/ic_launcher.png"
          583  +    fi
          584  +    if [[ -f "drawable-xhdpi/ic_launcher.png" ]]; then
          585  +        cp "drawable-xhdpi/ic_launcher.png" "$LOVE_ANDROID_DIR/res/drawable-xhdpi/ic_launcher.png"
          586  +    fi
          587  +    if [[ -f "drawable-xxhdpi/ic_launcher.png" ]]; then
          588  +        cp "drawable-xxhdpi/ic_launcher.png" "$LOVE_ANDROID_DIR/res/drawable-xxhdpi/ic_launcher.png"
          589  +    fi
          590  +    if [[ -f "drawable-xhdpi/ouya_icon.png" ]]; then
          591  +        cp "drawable-xhdpi/ouya_icon.png" "$LOVE_ANDROID_DIR/res/drawable-xhdpi/ouya_icon.png"
          592  +    fi
          593  +
          594  +    cd "$LOVE_ANDROID_DIR"
          595  +fi
          596  +
          597  +
          598  +ant debug
          599  +cd "$PROJECT_DIR"
          600  +cp "$LOVE_ANDROID_DIR/bin/love_android_sdl2-debug.apk" "$RELEASE_DIR"
          601  +git checkout -- .
          602  +rm -rf src/com bin gen
          603  +
          604  +
          605  +exit_module
          606  +EndOfModule
          607  +))
          608  +default_module
          609  +
          610  +
          611  +(source <(cat <<\EndOfModule
          612  +# Debian package
          613  +init_module "Debian" "debian" "D"
          614  +OPTIONS="D"
          615  +LONG_OPTIONS=""
          616  +
          617  +
          618  +IDENTITY=$(echo $TITLE | sed -e 's/[^-a-zA-Z0-9_]/-/g' | tr '[:upper:]' '[:lower:]')
          619  +
          620  +# Debian
          621  +missing_info=false
          622  +error_msg="Could not build Debian package."
          623  +if [[ -z $GAME_VERSION ]]; then
          624  +    missing_info=true
          625  +    error_msg="$error_msg\nMissing project's version. Use -v or --Dversion."
          626  +fi
          627  +if [[ -z $URL ]]; then
          628  +    missing_info=true
          629  +    error_msg="$error_msg\nMissing project's homepage. Use -u or -Durl."
          630  +fi
          631  +if [[ -z $DESCRIPTION ]]; then
          632  +    missing_info=true
          633  +    error_msg="$error_msg\nMissing project's description. Use -d or --Ddescription."
          634  +fi
          635  +if [[ -z $AUTHOR ]]; then
          636  +    missing_info=true
          637  +    error_msg="$error_msg\nMissing maintainer's name. Use -a or --Dauthor."
          638  +fi
          639  +if [[ -z $EMAIL ]]; then
          640  +    missing_info=true
          641  +    error_msg="$error_msg\nMissing maintainer's email. Use -e or --Demail."
          642  +fi
          643  +if [[ $missing_info == true  ]]; then
          644  +    exit_module "options" "$error_msg"
          645  +fi
          646  +
          647  +
          648  +create_love_file 9
          649  +cd "$RELEASE_DIR"
          650  +
          651  +
          652  +TEMP="$(mktemp -d)"
          653  +umask 0022
          654  +
          655  +mkdir -p "$TEMP/DEBIAN"
          656  +cat > "$TEMP/DEBIAN/control" <<EOF
          657  +Package: $IDENTITY
          658  +Version: $GAME_VERSION
          659  +Architecture: all
          660  +Maintainer: $AUTHOR <$EMAIL>
          661  +Installed-Size: $(( $(stat -c %s "$LOVE_FILE") / 1024 ))
          662  +Depends: love (>= $LOVE_VERSION)
          663  +Priority: extra
          664  +Homepage: $URL
          665  +Description: $DESCRIPTION
          666  +EOF
          667  +
          668  +mkdir -p "$TEMP/usr/share/applications"
          669  +cat > "$TEMP/usr/share/applications/${IDENTITY}.desktop" <<EOF
          670  +[Desktop Entry]
          671  +Name=$TITLE
          672  +Comment=$DESCRIPTION
          673  +Exec=$IDENTITY
          674  +Type=Application
          675  +Categories=Game;
          676  +EOF
          677  +
          678  +mkdir -p "$TEMP/usr/bin"
          679  +cat <(echo -ne '#!/usr/bin/env love\n') "$LOVE_FILE" > "$TEMP/usr/bin/$IDENTITY"
          680  +chmod +x "$TEMP/usr/bin/$IDENTITY"
          681  +
          682  +if [[ -d $ICON ]]; then
          683  +    ICON_LOC=$TEMP/usr/share/icons/hicolor
          684  +    mkdir -p $ICON_LOC
          685  +    echo "Icon=$IDENTITY" >> "$TEMP/usr/share/applications/${IDENTITY}.desktop"
          686  +
          687  +    cd "$ICON"
          688  +    for file in *; do
          689  +        RES=$(echo "$file" | grep -Eo "[0-9]+x[0-9]+")
          690  +        EXT=$(echo "$file" | sed -e 's/.*\.//g')
          691  +        if [[ $EXT == "svg" ]]; then
          692  +            mkdir -p "$ICON_LOC/scalable/apps"
          693  +            cp "$file" "$ICON_LOC/scalable/apps/${IDENTITY}.svg"
          694  +            chmod 0644 "$ICON_LOC/scalable/apps/${IDENTITY}.svg"
          695  +        elif [[ -n $RES ]]; then
          696  +            mkdir -p "$ICON_LOC/$RES/apps"
          697  +            cp "$file" "$ICON_LOC/$RES/apps/${IDENTITY}.$EXT"
          698  +            chmod 0644 "$ICON_LOC/$RES/apps/${IDENTITY}.$EXT"
          699  +        fi
          700  +    done
          701  +else
          702  +    echo "Icon=love" >> "$TEMP/usr/share/applications/${IDENTITY}.desktop"
          703  +fi
          704  +
          705  +cd "$TEMP"
          706  +find "usr" -type f -exec md5sum {} \; | sed -E "s/^([0-9a-f]{32}  )/\1\//g" > "$TEMP/DEBIAN/md5sums"
          707  +cd "$PROJECT_DIR"
          708  +
          709  +fakeroot dpkg-deb -b "$TEMP" "$RELEASE_DIR/$IDENTITY-${GAME_VERSION}_all.deb"
          710  +rm -rf "$TEMP"
          711  +
          712  +
          713  +exit_module
          714  +EndOfModule
          715  +))
          716  +default_module
          717  +
          718  +
          719  +(source <(cat <<\EndOfModule
          720  +# Mac OS X
          721  +init_module "Mac OS X" "osx" "M"
          722  +OPTIONS="M"
          723  +LONG_OPTIONS=""
          724  +
          725  +
          726  +IDENTITY=$(echo $TITLE | sed -e 's/[^-a-zA-Z0-9_]/-/g' | tr '[:upper:]' '[:lower:]')
          727  +
          728  +if [[ -z $AUTHOR ]]; then
          729  +    exit_module "options" "Missing maintainer's name. Use -a or --Mauthor."
          730  +fi
          731  +if [[ -z $GAME_VERSION ]]; then
          732  +    GAME_VERSION="$LOVE_VERSION"
          733  +fi
          734  +
          735  +if [[ -n $ICON ]]; then
          736  +    if [[ -d $ICON ]]; then
          737  +        for file in $ICON/*.icns; do
          738  +            if [[ -f $file ]]; then
          739  +                ICON="$file"
          740  +                break
          741  +            else
          742  +                found=false
          743  +            fi
          744  +        done
          745  +    fi
          746  +    if [[ $found == false || ! -f $ICON ]]; then
          747  +        >&2 echo "OS X icon was not found in ${ICON}."
          748  +        icon=Love.icns
          749  +        ICON=
          750  +    else
          751  +        icon="${IDENTITY}.icns"
          752  +    fi
          753  +fi
          754  +
          755  +
          756  +create_love_file 9
          757  +cd "$RELEASE_DIR"
          758  +
          759  +
          760  +## MacOS ##
          761  +if [[ ! -f "$CACHE_DIR/love-$LOVE_VERSION-macos.zip" ]]; then
          762  +    curl -L -C - -o $CACHE_DIR/love-$LOVE_VERSION-macos.zip https://bitbucket.org/rude/love/downloads/love-$LOVE_VERSION-macos.zip
          763  +fi
          764  +unzip -qq "$CACHE_DIR/love-$LOVE_VERSION-macos.zip"
          765  +
          766  +rm -rf "$TITLE-macos.zip" 2> /dev/null
          767  +mv love.app "${TITLE}.app"
          768  +cp "$LOVE_FILE" "${TITLE}.app/Contents/Resources"
          769  +if [[ -n $ICON ]]; then
          770  +    cd "$PROJECT_DIR"
          771  +    cp "$ICON" "$RELEASE_DIR/$icon"
          772  +    cd "$RELEASE_DIR"
          773  +    mv "$icon" "${TITLE}.app/Contents/Resources"
          774  +fi
          775  +
          776  +sed -i.bak -e '/<key>UTExportedTypeDeclarations<\/key>/,/^\t<\/array>/d' \
          777  +    -e "s/>org.love2d.love</>org.${AUTHOR}.$IDENTITY</" \
          778  +    -e "s/$LOVE_VERSION/$GAME_VERSION/" \
          779  +    -e "s/Love.icns/$icon/" \
          780  +    -e "s/>LÖVE</>$TITLE</" \
          781  +    "${TITLE}.app/Contents/Info.plist"
          782  +rm "${TITLE}.app/Contents/Info.plist.bak"
          783  +
          784  +zip -9 -qyr "${TITLE}-macos.zip" "${TITLE}.app"
          785  +rm -rf love-$LOVE_VERSION-macos.zip "${TITLE}.app" __MACOSX
          786  +
          787  +exit_module
          788  +EndOfModule
          789  +))
          790  +default_module
          791  +
          792  +
          793  +(source <(cat <<\EndOfModule
          794  +# Windows
          795  +init_module "Windows" "windows" "W"
          796  +OPTIONS="W::"
          797  +LONG_OPTIONS="appid:,installer"
          798  +
          799  +if [[ -z $IDENTITY ]]; then
          800  +    IDENTITY=$(echo $IDENTITY | sed -e 's/[^-a-zA-Z0-9_]/-/g' | tr '[:upper:]' '[:lower:]')
          801  +fi
          802  +
          803  +while true; do
          804  +    case "$1" in
          805  +        --Wappid )     APPID="$2"; shift 2 ;;
          806  +        --Winstaller ) INSTALLER=true; shift ;;
          807  +        -W )           if [[ -z "$2" ]]; then X32=true; X64=true;
          808  +                       elif (( "$2" == 32 )); then X32=true;
          809  +                       elif (( "$2" == 64 )); then X64=true;
          810  +                       fi; shift ;;
          811  +        -- ) break ;;
          812  +        * ) shift ;;
          813  +    esac
          814  +done
          815  +
          816  +
          817  +FOUND_WINE=true
          818  +command -v wine >/dev/null 2>&1 || { FOUND_WINE=false; } && { WINEPREFIX="$CACHE_DIR/wine"; }
          819  +
          820  +
          821  +if [[ -n $ICON ]]; then
          822  +    if [[ $FOUND_WINE == true ]]; then
          823  +        if [[ -d $ICON ]]; then
          824  +            for file in $ICON/*.ico; do
          825  +                if [[ -f $file ]]; then
          826  +                    ICON="$file"
          827  +                    break
          828  +                else
          829  +                    found=false
          830  +                fi
          831  +            done
          832  +        fi
          833  +        if [[ $found == false || ! -f $ICON ]]; then
          834  +            >&2 echo "Windows icon was not found in ${ICON}."
          835  +            ICON=
          836  +        else
          837  +            RESHACKER="$WINEPREFIX/drive_c/Program Files (x86)/Resource Hacker/ResourceHacker.exe"
          838  +            if [[ ! -f $RESHACKER ]]; then
          839  +                curl -L -C - -o "$WINEPREFIX/drive_c/reshacker_setup.exe" "http://www.angusj.com/resourcehacker/reshacker_setup.exe"
          840  +                WINEPREFIX="$WINEPREFIX" wine "$WINEPREFIX/drive_c/reshacker_setup.exe" 2>&1 /dev/null
          841  +            fi
          842  +        fi
          843  +    else
          844  +        >&2 echo "Can not set Windows icon without Wine."
          845  +    fi
          846  +fi
          847  +
          848  +
          849  +if [[ $INSTALLER == true ]]; then
          850  +    missing_opt=false
          851  +    error_msg="Could not build Windows installer."
          852  +    if [[ $FOUND_WINE == false ]]; then
          853  +        >&2 echo "Can not build Windows installer without Wine."
          854  +        exit_module "deps"
          855  +    fi
          856  +    if [[ -z $AUTHOR ]]; then
          857  +        missing_opt=true
          858  +        error_msg="$error_msg\nMissing project author. Use -a or --Wauthor."
          859  +    fi
          860  +    if [[ -z $URL ]]; then
          861  +        missing_opt=true
          862  +        error_msg="$error_msg\nMissing project url. Use -u or --Wurl."
          863  +    fi
          864  +    if [[ -z $GAME_VERSION ]]; then
          865  +        missing_opt=true
          866  +        error_msg="$error_msg\nMissing project version. Use -v or --Wversion."
          867  +    fi
          868  +    if [[ -z $APPID ]]; then
          869  +        missing_opt=true
          870  +        error_msg="$error_msg\nMissing application GUID. Use --Wappid."
          871  +    fi
          872  +    if [[ $missing_opt == true ]]; then
          873  +        exit_module "options" "$error_msg"
          874  +    fi
          875  +
          876  +    INNOSETUP="$WINEPREFIX/drive_c/Program Files (x86)/Inno Setup 5/ISCC.exe"
          877  +    if [[ ! -f $INNOSETUP ]]; then
          878  +        curl -L -C - -o "$WINEPREFIX/drive_c/is-unicode.exe" "http://www.jrsoftware.org/download.php/is-unicode.exe"
          879  +        WINEPREFIX="$WINEPREFIX" wine "$WINEPREFIX/drive_c/is-unicode.exe" 2>&1 /dev/null
          880  +    fi
          881  +
          882  +# Inno Setup
          883  +# $1: Path to game exe directory
          884  +# $2: true if 64 bits release
          885  +create_installer () {
          886  +    ln -s "$1" "$WINEPREFIX/drive_c/game"
          887  +    if [[ -n $ICON ]]; then
          888  +        cd "$PROJECT_DIR"
          889  +        ln -s "$ICON" "$WINEPREFIX/drive_c/game.ico"
          890  +        cd "$RELEASE_DIR"
          891  +    else
          892  +        ln -s "$1/game.ico" "$WINEPREFIX/drive_c/game.ico"
          893  +    fi
          894  +
          895  +    cat > "$WINEPREFIX/drive_c/innosetup.iss" <<EOF
          896  +#define MyAppName "$TITLE"
          897  +#define MyAppVersion "$GAME_VERSION"
          898  +#define MyAppPublisher "$AUTHOR"
          899  +#define MyAppURL "$URL"
          900  +#define MyAppExeName "${TITLE}.exe"
          901  +
          902  +[Setup]
          903  +;ArchitecturesInstallIn64BitMode=x64 ia64
          904  +AppId={{$APPID}
          905  +AppName={#MyAppName}
          906  +AppVersion={#MyAppVersion}
          907  +;AppVerName={#MyAppName} {#MyAppVersion}
          908  +AppPublisher={#MyAppPublisher}
          909  +AppPublisherURL={#MyAppURL}
          910  +AppSupportURL={#MyAppURL}
          911  +AppUpdatesURL={#MyAppURL}
          912  +DefaultDirName={pf}\{#MyAppName}
          913  +DefaultGroupName={#MyAppName}
          914  +AllowNoIcons=yes
          915  +OutputBaseFilename=${IDENTITY}-setup
          916  +SetupIconFile=C:\\game.ico
          917  +Compression=lzma
          918  +SolidCompression=yes
          919  +
          920  +[Languages]
          921  +Name: "english"; MessagesFile: "compiler:Default.isl"
          922  +Name: "french"; MessagesFile: "compiler:Languages\French.isl"
          923  +Name: "german"; MessagesFile: "compiler:Languages\German.isl"
          924  +
          925  +[Tasks]
          926  +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
          927  +
          928  +[Icons]
          929  +Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
          930  +Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
          931  +Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
          932  +
          933  +[Run]
          934  +Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
          935  +
          936  +[Files]
          937  +EOF
          938  +    if [[ $2 == true ]]; then
          939  +        sed -i 's/;ArchitecturesInstallIn64BitMode/ArchitecturesInstallIn64BitMode/' "$WINEPREFIX/drive_c/innosetup.iss"
          940  +    fi
          941  +
          942  +    for file in $1; do
          943  +        echo "Source: \"C:\\game\\$file\"; DestDir: \"{app}\"; Flags: ignoreversion" \
          944  +            >> "$WINEPREFIX"/drive_c/innosetup.iss
          945  +    done
          946  +
          947  +    WINEPREFIX="$WINEPREFIX" wine "$INNOSETUP" /Q 'c:\innosetup.iss'
          948  +    mv "$WINEPREFIX/drive_c/Output/$IDENTITY-setup.exe" .
          949  +    rm -rf "$WINEPREFIX/drive_c/{game,game.ico,innosetup.iss,Output}"
          950  +}
          951  +
          952  +fi
          953  +
          954  +${X32:=false}
          955  +${X64:=false}
          956  +if [[ $X32 == false && $X64 == false ]]; then
          957  +    X32=true
          958  +    X64=true
          959  +fi
          960  +
          961  +
          962  +create_love_file 9
          963  +cd "$RELEASE_DIR"
          964  +
          965  +
          966  +# Windows 32-bits
          967  +if [[ $X32 == true ]]; then
          968  +
          969  +    if [[ ! -f "$CACHE_DIR/love-$LOVE_VERSION-win32.zip" ]]; then
          970  +        curl -L -C - -o "$CACHE_DIR/love-$LOVE_VERSION-win32.zip" "https://bitbucket.org/rude/love/downloads/love-$LOVE_VERSION-win32.zip"
          971  +    fi
          972  +
          973  +    unzip -qq "$CACHE_DIR/love-$LOVE_VERSION-win32.zip"
          974  +
          975  +    if [[ -n $ICON ]]; then
          976  +        WINEPREFIX="$WINEPREFIX" wine "$RESHACKER" \
          977  +            -addoverwrite "love-$LOVE_VERSION-win32/love.exe,love-$LOVE_VERSION-win32/love.exe,$ICON,ICONGROUP,MAINICON,0" 2>&1 /dev/null
          978  +    fi
          979  +
          980  +    # version number is incorrect inside zip file; oops
          981  +    LOVE_VERSION="$LOVE_VERSION".0
          982  +    cat love-$LOVE_VERSION-win32/love.exe "$LOVE_FILE" > "love-$LOVE_VERSION-win32/${TITLE}.exe"
          983  +    rm love-$LOVE_VERSION-win32/love.exe
          984  +    mv love-$LOVE_VERSION-win32 "$TITLE"-win32
          985  +    if [[ $INSTALLER == true ]]; then
          986  +        rm -rf "$IDENTITY-setup-win32.exe" 2> /dev/null
          987  +        create_installer "$TITLE-win32"
          988  +        mv "$IDENTITY-setup.exe" "$IDENTITY-setup-win32.exe"
          989  +    else
          990  +        zip -FS -9 -qr "$TITLE-win32.zip" "$TITLE-win32"
          991  +    fi
          992  +    rm -rf "$TITLE-win32"
          993  +fi
          994  +
          995  +## Windows 64-bits ##
          996  +if [[ $X64 == true ]] && compare_version "$LOVE_VERSION" '>=' '0.8.0'; then
          997  +
          998  +    if [[ ! -f "$CACHE_DIR/love-$LOVE_VERSION-win64.zip" ]]; then
          999  +        if compare_version "$LOVE_VERSION" '>=' '0.9.0'; then
         1000  +            curl -L -C - -o "$CACHE_DIR/love-$LOVE_VERSION-win64.zip" "https://bitbucket.org/rude/love/downloads/love-$LOVE_VERSION-win64.zip"
         1001  +        else
         1002  +            curl -L -C - -o "$CACHE_DIR/love-$LOVE_VERSION-win-x64.zip" "https://bitbucket.org/rude/love/downloads/love-$LOVE_VERSION-win-x64.zip"
         1003  +        fi
         1004  +    fi
         1005  +
         1006  +    unzip -qq "$CACHE_DIR/love-$LOVE_VERSION-win64.zip"
         1007  +
         1008  +    if [[ -n $ICON ]]; then
         1009  +        WINEPREFIX="$WINEPREFIX" wine "$RESHACKER" \
         1010  +            -addoverwrite "love-$LOVE_VERSION-win64/love.exe,love-$LOVE_VERSION-win64/love.exe,$ICON,ICONGROUP,MAINICON,0" 2>&1 /dev/null
         1011  +    fi
         1012  +
         1013  +    cat love-$LOVE_VERSION-win64/love.exe "$LOVE_FILE" > "love-$LOVE_VERSION-win64/${TITLE}.exe"
         1014  +    rm love-$LOVE_VERSION-win64/love.exe
         1015  +    mv love-$LOVE_VERSION-win64 "$TITLE-win64"
         1016  +    if [[ $INSTALLER == true ]]; then
         1017  +        rm -rf "$IDENTITY-setup-win64.exe" 2> /dev/null
         1018  +        create_installer "$TITLE-win64" "true"
         1019  +        mv "$IDENTITY-setup.exe" "$IDENTITY-setup-win64.exe"
         1020  +    else
         1021  +        zip -FS -9 -qr "$TITLE-win64.zip" "$TITLE-win64"
         1022  +    fi
         1023  +    rm -rf "$TITLE-win64"
         1024  +fi
         1025  +
         1026  +
         1027  +exit_module
         1028  +EndOfModule
         1029  +))
         1030  +default_module
         1031  +
         1032  +
         1033  +elif [[ $INSTALLED == true ]]; then
         1034  +    SCRIPTS_DIR="scripts"
         1035  +    for file in "$SCRIPTS_DIR"/*.sh; do
         1036  +        (source "$file")
         1037  +        default_module
         1038  +    done
         1039  +fi
         1040  +
         1041  +
         1042  +(
         1043  +    init_module "LÖVE" "love" "L"
         1044  +    create_love_file 9
         1045  +    exit_module
         1046  +)
         1047  +if [[ $? -ne 0 && $DEFAULT_MODULE == true ]]; then
         1048  +(
         1049  +    init_module "LÖVE" "default"
         1050  +    create_love_file 9
         1051  +    exit_module
         1052  +)
         1053  +fi
         1054  +
         1055  +exit 0

Added src/worpt/main.lua.

            1  +-- bootstrap the compiler
            2  +fennel = require("lib.fennel")
            3  +table.insert(package.loaders, fennel.make_searcher({correlate=true}))
            4  +pp = function(x) print(require("lib.fennelview")(x)) end
            5  +pps = function(x) return require("lib.fennelview")(x) end
            6  +lume = require("lib.lume")
            7  +log = function(...) print(string.format(...)) end
            8  +
            9  +require("wrap")

Added src/worpt/makefile.

            1  +VERSION=0.1.3
            2  +LOVE_VERSION=11.3
            3  +NAME=worpt
            4  +ITCH_ACCOUNT=chewbranca
            5  +URL='https://chewbranca.com/dir?ci=6f88a252fe2ba468&name=src/worpt'
            6  +AUTHOR=Russell Branca {@chewbranca} <chewbranca@gmail.com>
            7  +DESCRIPTION=Worpt is a prototype platformer using the awesome _Warped City_ art assets from https://ansimuz.itch.io/warped-city . Currently this is an engine capable of running the map.json file from the _Warped City_ demo game. The goal was for this to be the intro level and then to create new levels, and the progress is that intro level is playable and nearly complete.
            8  +LIBS := $(wildcard lib/*)
            9  +LUA := $(wildcard *.lua)
           10  +SRC := $(wildcard *.fnl)
           11  +OUT := $(patsubst %.fnl,%.lua,$(SRC))
           12  +
           13  +
           14  +run: $(OUT) ; love .
           15  +
           16  +count: ; cloc *.fnl --force-lang=clojure
           17  +
           18  +clean: ; rm -rf releases/* $(OUT)
           19  +
           20  +cleansrc: ; rm -rf $(OUT)
           21  +
           22  +%.lua: %.fnl; lua lib/fennel --compile --correlate $< > $@
           23  +
           24  +LOVEFILE=releases/$(NAME)-$(VERSION).love
           25  +
           26  +$(LOVEFILE): $(LUA) $(OUT) $(LIBS) assets #text
           27  +	mkdir -p releases/
           28  +	find $^ -type f | LC_ALL=C sort | env TZ=UTC zip -r -q -9 -X $@ -@
           29  +
           30  +love: $(LOVEFILE)
           31  +
           32  +# platform-specific distributables
           33  +
           34  +REL=$(PWD)/love-release.sh # https://p.hagelb.org/love-release.sh
           35  +FLAGS=-a "$(AUTHOR)" --description $(DESCRIPTION) \
           36  +	--love $(LOVE_VERSION) --url $(URL) --version $(VERSION) --lovefile $(LOVEFILE)
           37  +
           38  +releases/$(NAME)-$(VERSION)-x86_64.AppImage: $(LOVEFILE)
           39  +	cd appimage && ./build.sh $(LOVE_VERSION) $(PWD)/$(LOVEFILE)
           40  +	mv appimage/game-x86_64.AppImage $@
           41  +
           42  +releases/$(NAME)-$(VERSION)-macos.zip: $(LOVEFILE)
           43  +	$(REL) $(FLAGS) -M
           44  +	mv releases/$(NAME)-macos.zip $@
           45  +
           46  +releases/$(NAME)-$(VERSION)-win.zip: $(LOVEFILE)
           47  +	$(REL) $(FLAGS) -W32
           48  +	mv releases/$(NAME)-win32.zip $@
           49  +
           50  +linux: releases/$(NAME)-$(VERSION)-x86_64.AppImage
           51  +mac: releases/$(NAME)-$(VERSION)-macos.zip
           52  +windows: releases/$(NAME)-$(VERSION)-win.zip
           53  +
           54  +# If you release on itch.io, you should install butler:
           55  +# https://itch.io/docs/butler/installing.html
           56  +
           57  +uploadlinux: releases/$(NAME)-$(VERSION)-x86_64.AppImage
           58  +	butler push $^ $(ITCH_ACCOUNT)/$(NAME):linux --userversion $(VERSION)
           59  +uploadmac: releases/$(NAME)-$(VERSION)-macos.zip
           60  +	butler push $^ $(ITCH_ACCOUNT)/$(NAME):mac --userversion $(VERSION)
           61  +uploadwindows: releases/$(NAME)-$(VERSION)-win.zip
           62  +	butler push $^ $(ITCH_ACCOUNT)/$(NAME):windows --userversion $(VERSION)
           63  +
           64  +upload: uploadlinux uploadmac uploadwindows
           65  +
           66  +release: linux mac windows upload cleansrc

Added src/worpt/map.lua.

            1  +-- based on assets/warped_city/maps/map.json
            2  +return {
            3  +  version = "1.2",
            4  +  luaversion = "5.1",
            5  +  tiledversion = "1.3.3",
            6  +  orientation = "orthogonal",
            7  +  renderorder = "right-down",
            8  +  width = 120,
            9  +  height = 28,
           10  +  tilewidth = 16,
           11  +  tileheight = 16,
           12  +  nextlayerid = 4,
           13  +  nextobjectid = 45,
           14  +  backgroundcolor = { 0, 0, 255 },
           15  +  properties = {},
           16  +  tilesets = {
           17  +    {
           18  +      name = "tileset",
           19  +      firstgid = 1,
           20  +      tilewidth = 16,
           21  +      tileheight = 16,
           22  +      spacing = 0,
           23  +      margin = 0,
           24  +      columns = 24,
           25  +      image = "assets/warped_city/environment/tileset.png",
           26  +      imagewidth = 384,
           27  +      imageheight = 256,
           28  +      tileoffset = {
           29  +        x = 0,
           30  +        y = 0
           31  +      },
           32  +      grid = {
           33  +        orientation = "orthogonal",
           34  +        width = 16,
           35  +        height = 16
           36  +      },
           37  +      properties = {},
           38  +      terrains = {},
           39  +      tilecount = 384,
           40  +      tiles = {}
           41  +    }
           42  +  },
           43  +  layers = {
           44  +    {
           45  +      type = "tilelayer",
           46  +      id = 1,
           47  +      name = "Collisions Layer",
           48  +      x = 0,
           49  +      y = 0,
           50  +      width = 120,
           51  +      height = 28,
           52  +      visible = true,
           53  +      opacity = 1,
           54  +      offsetx = 0,
           55  +      offsety = 0,
           56  +      properties = {collidable = true},
           57  +      encoding = "lua",
           58  +      data = {
           59  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           60  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           61  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
           62  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           63  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           64  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           65  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           66  +        1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
           67  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           68  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           69  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           70  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           71  +        1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           72  +        1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           73  +        1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           74  +        1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           75  +        1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           76  +        1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
           77  +        1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           78  +        1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           79  +        1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           80  +        1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           81  +        1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
           82  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           83  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           84  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           85  +        1, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
           86  +        1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
           87  +      }
           88  +    },
           89  +    {
           90  +      type = "tilelayer",
           91  +      id = 2,
           92  +      name = "Main Layer",
           93  +      x = 0,
           94  +      y = 0,
           95  +      width = 120,
           96  +      height = 28,
           97  +      visible = true,
           98  +      opacity = 1,
           99  +      offsetx = 0,
          100  +      offsety = 0,
          101  +      properties = {},
          102  +      encoding = "lua",
          103  +      data = {
          104  +        0, 26, 76, 77, 78, 29, 29, 76, 77, 78, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 35, 39, 40, 41, 37, 159, 160, 161, 37, 279, 280, 281, 37, 39, 40, 41, 43, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 76, 77, 78, 245, 29, 76, 77, 78, 80,
          105  +        0, 50, 100, 101, 102, 29, 29, 100, 101, 102, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 63, 64, 65, 61, 183, 184, 185, 61, 303, 304, 305, 61, 63, 64, 65, 67, 68, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 100, 101, 102, 269, 29, 100, 101, 102, 56,
          106  +        0, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, 87, 88, 89, 85, 207, 208, 209, 85, 327, 328, 329, 61, 87, 88, 89, 91, 92, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
          107  +        0, 26, 28, 29, 30, 245, 163, 28, 29, 30, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, 111, 112, 113, 109, 231, 232, 233, 85, 351, 352, 353, 61, 111, 112, 113, 115, 116, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 225, 224, 224, 224, 0, 0, 0, 224, 224, 224, 225, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 28, 29, 30, 228, 229, 28, 29, 30, 32,
          108  +        0, 50, 52, 53, 54, 269, 187, 52, 53, 54, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 52, 53, 54, 252, 253, 52, 53, 54, 32,
          109  +        0, 74, 76, 77, 78, 245, 29, 76, 77, 78, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 35, 39, 40, 41, 37, 159, 160, 161, 37, 39, 40, 41, 37, 279, 280, 281, 43, 44, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 35, 29, 29, 29, 29, 165, 147, 29, 228, 229, 43, 44, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 76, 77, 78, 167, 29, 76, 77, 78, 56,
          110  +        0, 26, 100, 101, 102, 269, 29, 100, 101, 102, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 63, 64, 65, 61, 183, 184, 185, 61, 63, 64, 65, 61, 303, 304, 305, 67, 68, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 189, 189, 29, 29, 29, 147, 29, 252, 253, 67, 68, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 100, 101, 102, 191, 29, 100, 101, 102, 80,
          111  +        0, 50, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, 87, 88, 89, 109, 207, 208, 209, 85, 87, 88, 89, 61, 327, 328, 329, 91, 92, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, 165, 189, 29, 29, 29, 147, 29, 29, 29, 91, 92, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 273, 272, 272, 272, 272, 272, 272, 272, 272, 272,
          112  +        0, 74, 28, 29, 30, 228, 229, 28, 29, 30, 32, 0, 0, 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, 111, 112, 113, 85, 231, 232, 233, 85, 111, 112, 113, 61, 351, 352, 353, 115, 116, 0, 0, 0, 0, 224, 224, 225, 224, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, 29, 29, 29, 29, 29, 147, 29, 29, 29, 115, 116, 0, 0, 0, 0, 224, 224, 225, 224, 224, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 50, 28, 29, 30, 167, 245, 28, 29, 30, 56,
          113  +        0, 26, 52, 53, 54, 252, 253, 52, 53, 54, 32, 0, 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 147, 272, 272, 272, 272, 272, 274, 0, 0, 271, 272, 272, 272, 272, 272, 274, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 83, 155, 156, 157, 29, 29, 147, 29, 189, 165, 67, 68, 0, 0, 0, 271, 272, 272, 272, 272, 272, 274, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 50, 52, 53, 54, 191, 245, 52, 53, 54, 56,
          114  +        0, 50, 76, 77, 78, 29, 29, 76, 77, 78, 56, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 35, 279, 280, 281, 37, 159, 160, 161, 37, 39, 40, 41, 147, 159, 160, 161, 43, 44, 0, 0, 0, 0, 26, 245, 167, 29, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 107, 179, 180, 181, 29, 29, 147, 29, 189, 189, 91, 92, 0, 0, 0, 0, 26, 245, 167, 29, 56, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 26, 76, 77, 78, 167, 245, 76, 77, 78, 32,
          115  +        0, 74, 100, 101, 102, 29, 29, 100, 101, 102, 80, 0, 0, 0, 224, 224, 225, 224, 224, 224, 224, 224, 224, 224, 0, 0, 0, 0, 58, 59, 303, 304, 305, 61, 183, 184, 185, 61, 63, 64, 65, 147, 183, 184, 185, 67, 68, 0, 0, 0, 0, 50, 245, 191, 29, 32, 0, 0, 0, 0, 0, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 147, 272, 272, 272, 272, 272, 274, 0, 0, 0, 50, 245, 191, 29, 32, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 50, 100, 101, 102, 191, 269, 100, 101, 102, 56,
          116  +        271, 273, 272, 272, 272, 147, 272, 272, 272, 272, 272, 274, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 82, 83, 327, 328, 329, 85, 207, 208, 209, 85, 87, 88, 89, 147, 207, 208, 209, 91, 92, 0, 0, 0, 0, 74, 245, 167, 29, 56, 0, 0, 0, 0, 0, 34, 35, 228, 229, 29, 37, 29, 228, 229, 37, 228, 229, 29, 147, 167, 29, 29, 43, 44, 0, 0, 0, 0, 74, 245, 167, 29, 56, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
          117  +        0, 50, 28, 29, 30, 147, 245, 28, 29, 30, 56, 0, 0, 0, 26, 228, 229, 245, 149, 29, 163, 228, 229, 32, 0, 0, 0, 0, 106, 107, 351, 352, 353, 85, 231, 232, 233, 85, 111, 112, 113, 147, 231, 232, 233, 115, 116, 0, 0, 0, 0, 26, 245, 191, 29, 56, 0, 0, 0, 0, 0, 58, 59, 252, 253, 29, 61, 29, 252, 253, 61, 252, 253, 29, 147, 191, 29, 29, 67, 68, 0, 0, 0, 0, 26, 245, 191, 29, 56, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, 0, 0, 0, 0, 26, 28, 29, 30, 245, 163, 28, 29, 30, 32,
          118  +        0, 50, 52, 53, 54, 147, 245, 52, 53, 54, 56, 0, 0, 0, 50, 252, 253, 245, 173, 29, 187, 252, 253, 56, 0, 0, 0, 271, 272, 272, 272, 272, 272, 61, 46, 46, 46, 46, 46, 46, 46, 147, 272, 272, 272, 272, 272, 274, 0, 0, 0, 50, 245, 167, 29, 56, 0, 0, 0, 0, 0, 82, 83, 165, 165, 29, 109, 29, 189, 189, 61, 189, 189, 29, 147, 167, 29, 189, 91, 92, 0, 0, 0, 0, 50, 245, 167, 29, 56, 0, 0, 0, 0, 224, 224, 225, 224, 224, 0, 0, 0, 0, 224, 224, 225, 224, 224, 0, 0, 0, 50, 52, 53, 54, 269, 187, 52, 53, 54, 56,
          119  +        0, 26, 76, 77, 78, 147, 245, 76, 77, 78, 32, 0, 0, 0, 74, 29, 29, 245, 173, 29, 29, 29, 167, 80, 0, 0, 0, 0, 34, 35, 39, 40, 41, 61, 228, 229, 29, 29, 29, 228, 229, 147, 39, 40, 41, 43, 44, 0, 0, 0, 0, 74, 245, 191, 29, 80, 0, 0, 0, 0, 0, 106, 107, 189, 189, 29, 61, 29, 189, 165, 61, 189, 189, 29, 147, 191, 29, 29, 115, 116, 0, 0, 0, 0, 74, 245, 191, 29, 80, 0, 0, 0, 271, 272, 272, 272, 272, 272, 274, 0, 0, 271, 272, 272, 272, 272, 272, 274, 0, 0, 74, 76, 77, 78, 245, 29, 76, 77, 78, 80,
          120  +        0, 50, 100, 101, 102, 147, 269, 100, 101, 102, 56, 0, 0, 0, 26, 163, 29, 245, 173, 29, 29, 29, 191, 32, 0, 0, 0, 0, 58, 59, 63, 64, 65, 61, 252, 253, 29, 29, 29, 252, 253, 147, 63, 64, 65, 67, 68, 0, 0, 0, 0, 26, 228, 229, 29, 56, 0, 0, 0, 0, 0, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 147, 46, 46, 46, 46, 46, 0, 0, 0, 0, 26, 228, 229, 29, 56, 0, 0, 0, 0, 26, 245, 167, 29, 56, 0, 0, 0, 0, 26, 245, 167, 29, 56, 0, 0, 0, 26, 100, 101, 102, 269, 29, 100, 101, 102, 56,
          121  +        0, 46, 46, 46, 46, 147, 46, 46, 46, 46, 46, 0, 0, 0, 50, 187, 29, 245, 173, 155, 156, 157, 29, 56, 0, 0, 0, 0, 82, 83, 87, 88, 89, 85, 165, 29, 155, 156, 157, 29, 29, 147, 87, 88, 89, 91, 92, 0, 0, 0, 0, 50, 252, 253, 29, 56, 0, 0, 0, 0, 0, 34, 35, 279, 280, 281, 37, 159, 160, 161, 37, 39, 40, 41, 147, 159, 160, 161, 43, 44, 0, 0, 0, 0, 50, 252, 253, 29, 56, 0, 0, 0, 0, 50, 245, 191, 29, 32, 0, 0, 0, 0, 50, 245, 191, 29, 32, 0, 0, 0, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
          122  +        0, 26, 28, 29, 30, 147, 29, 28, 29, 30, 32, 0, 0, 0, 74, 29, 189, 189, 173, 179, 180, 181, 29, 80, 0, 0, 0, 0, 106, 107, 111, 112, 113, 109, 189, 29, 179, 180, 181, 29, 29, 171, 111, 112, 113, 115, 116, 0, 0, 0, 0, 74, 29, 167, 29, 80, 0, 0, 0, 0, 0, 58, 59, 303, 304, 305, 61, 183, 184, 185, 61, 63, 64, 65, 147, 183, 184, 185, 67, 68, 0, 0, 0, 0, 74, 29, 167, 29, 80, 0, 0, 0, 0, 74, 245, 167, 29, 56, 0, 0, 0, 0, 74, 245, 167, 29, 56, 0, 0, 0, 74, 28, 29, 30, 228, 229, 28, 29, 30, 32,
          123  +        0, 50, 52, 53, 54, 147, 29, 52, 53, 54, 56, 0, 0, 0, 26, 29, 163, 163, 173, 228, 229, 167, 29, 32, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 26, 29, 191, 29, 56, 0, 0, 0, 0, 0, 82, 83, 327, 328, 329, 85, 207, 208, 209, 85, 87, 88, 89, 147, 207, 208, 209, 91, 92, 0, 0, 0, 0, 26, 29, 191, 29, 56, 0, 0, 0, 0, 26, 245, 191, 29, 56, 0, 0, 0, 0, 26, 245, 191, 29, 56, 0, 0, 0, 26, 52, 53, 54, 252, 253, 52, 53, 54, 32,
          124  +        0, 74, 76, 77, 78, 147, 163, 76, 77, 78, 80, 0, 0, 0, 50, 29, 187, 187, 197, 252, 253, 191, 29, 56, 0, 0, 0, 0, 34, 35, 159, 160, 161, 37, 159, 160, 161, 37, 279, 280, 281, 37, 39, 40, 41, 43, 44, 0, 0, 0, 0, 50, 29, 167, 29, 56, 0, 0, 0, 0, 0, 106, 107, 351, 352, 353, 85, 231, 232, 233, 85, 111, 112, 113, 171, 231, 232, 233, 115, 116, 0, 0, 0, 0, 50, 29, 167, 29, 56, 0, 0, 0, 0, 50, 245, 167, 29, 56, 0, 0, 0, 0, 50, 245, 167, 29, 56, 0, 0, 0, 50, 76, 77, 78, 29, 167, 76, 77, 78, 56,
          125  +        0, 26, 100, 101, 102, 171, 187, 100, 101, 102, 56, 0, 0, 0, 74, 29, 245, 245, 29, 173, 29, 167, 29, 80, 0, 0, 0, 0, 58, 59, 183, 184, 185, 61, 183, 184, 185, 61, 303, 304, 305, 109, 63, 64, 65, 67, 68, 0, 0, 0, 0, 74, 29, 191, 29, 56, 0, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 74, 29, 191, 29, 56, 0, 0, 0, 0, 74, 245, 191, 29, 80, 0, 0, 0, 0, 74, 245, 191, 29, 80, 0, 0, 0, 74, 100, 101, 102, 29, 191, 100, 101, 102, 80,
          126  +        0, 50, 272, 272, 272, 272, 272, 272, 272, 147, 272, 274, 0, 0, 26, 29, 245, 245, 29, 173, 29, 191, 29, 32, 0, 0, 0, 0, 82, 83, 207, 208, 209, 85, 207, 208, 209, 85, 327, 328, 329, 61, 87, 88, 89, 91, 92, 0, 0, 0, 0, 26, 29, 167, 29, 56, 0, 0, 0, 0, 0, 34, 35, 39, 40, 41, 61, 159, 160, 161, 61, 159, 160, 161, 37, 39, 40, 41, 43, 44, 0, 0, 0, 0, 26, 29, 167, 29, 56, 0, 0, 0, 0, 26, 228, 229, 29, 56, 0, 0, 0, 0, 26, 228, 229, 29, 56, 0, 0, 0, 273, 272, 272, 272, 272, 272, 272, 272, 272, 272,
          127  +        0, 74, 28, 29, 30, 228, 229, 28, 29, 147, 32, 0, 0, 0, 50, 29, 245, 245, 29, 173, 189, 189, 189, 56, 0, 0, 0, 0, 106, 107, 231, 232, 233, 85, 231, 232, 233, 85, 351, 352, 353, 61, 111, 112, 113, 115, 116, 0, 0, 0, 0, 50, 29, 191, 29, 56, 0, 0, 0, 0, 0, 58, 59, 63, 64, 65, 61, 183, 184, 185, 61, 183, 184, 185, 61, 63, 64, 65, 67, 68, 0, 0, 0, 0, 50, 29, 191, 29, 56, 0, 0, 0, 0, 50, 252, 253, 29, 56, 0, 0, 0, 0, 50, 252, 253, 29, 56, 0, 0, 0, 50, 28, 29, 30, 29, 245, 28, 29, 30, 56,
          128  +        0, 26, 52, 53, 54, 252, 253, 52, 53, 147, 32, 0, 0, 0, 74, 29, 245, 245, 29, 173, 189, 189, 189, 80, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 74, 29, 167, 29, 56, 0, 0, 0, 0, 0, 82, 83, 87, 88, 89, 85, 207, 208, 209, 61, 207, 208, 209, 109, 87, 88, 89, 91, 92, 0, 0, 0, 0, 74, 29, 167, 29, 56, 0, 0, 0, 0, 74, 29, 167, 29, 80, 0, 0, 0, 0, 74, 29, 167, 29, 80, 0, 0, 0, 50, 52, 53, 54, 29, 245, 52, 53, 54, 56,
          129  +        0, 50, 76, 77, 78, 29, 29, 76, 77, 147, 56, 0, 0, 0, 26, 29, 197, 245, 29, 173, 29, 29, 29, 32, 0, 0, 0, 0, 34, 35, 39, 40, 41, 37, 159, 160, 161, 37, 39, 40, 41, 37, 39, 40, 41, 43, 44, 0, 0, 0, 0, 50, 29, 191, 29, 56, 0, 0, 0, 0, 0, 106, 107, 111, 112, 113, 109, 231, 232, 233, 61, 231, 232, 233, 61, 111, 112, 113, 115, 116, 0, 0, 0, 0, 50, 29, 191, 29, 56, 0, 0, 0, 0, 26, 29, 191, 29, 56, 0, 0, 0, 0, 26, 29, 191, 29, 56, 0, 0, 0, 26, 76, 77, 78, 29, 245, 76, 77, 78, 32,
          130  +        0, 74, 100, 101, 102, 29, 29, 100, 101, 171, 80, 0, 0, 0, 50, 155, 156, 157, 29, 173, 29, 228, 229, 56, 0, 0, 0, 0, 58, 59, 63, 64, 65, 61, 183, 184, 185, 61, 63, 64, 65, 61, 63, 64, 65, 67, 68, 0, 0, 0, 0, 50, 155, 156, 157, 80, 0, 0, 0, 0, 271, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 274, 0, 0, 0, 50, 155, 156, 157, 80, 0, 0, 0, 0, 50, 29, 167, 29, 56, 0, 0, 0, 0, 50, 29, 167, 29, 56, 0, 0, 0, 50, 100, 101, 102, 29, 269, 100, 101, 102, 56,
          131  +        271, 273, 273, 273, 273, 273, 272, 272, 272, 272, 272, 274, 0, 0, 74, 179, 180, 181, 29, 173, 29, 252, 253, 80, 0, 0, 0, 0, 82, 83, 87, 88, 89, 85, 207, 208, 209, 85, 87, 88, 89, 61, 87, 88, 89, 91, 92, 0, 0, 0, 0, 50, 179, 180, 181, 56, 0, 0, 0, 0, 0, 34, 35, 159, 160, 161, 37, 159, 160, 161, 37, 279, 280, 281, 37, 39, 40, 41, 43, 44, 0, 0, 0, 0, 50, 179, 180, 181, 56, 0, 0, 0, 0, 74, 29, 191, 29, 56, 0, 0, 0, 0, 74, 29, 191, 29, 56, 0, 0, 0, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272
          132  +      }
          133  +    },
          134  +    {
          135  +      type = "objectgroup",
          136  +      id = 3,
          137  +      name = "Object Layer",
          138  +      visible = true,
          139  +      opacity = 1,
          140  +      offsetx = 0,
          141  +      offsety = 0,
          142  +      draworder = "topdown",
          143  +      properties = {},
          144  +      objects = {
          145  +        {
          146  +          id = 12,
          147  +          name = "",
          148  +          type = "",
          149  +          shape = "rectangle",
          150  +          x = 294.667,
          151  +          y = 382.333,
          152  +          width = 16,
          153  +          height = 16,
          154  +          rotation = 0,
          155  +          gid = 5,
          156  +          visible = false,
          157  +          properties = {}
          158  +        },
          159  +        {
          160  +          id = 13,
          161  +          name = "",
          162  +          type = "",
          163  +          shape = "rectangle",
          164  +          x = 240.333,
          165  +          y = 300,
          166  +          width = 16,
          167  +          height = 16,
          168  +          rotation = 0,
          169  +          gid = 6,
          170  +          visible = false,
          171  +          properties = {}
          172  +        },
          173  +        {
          174  +          id = 14,
          175  +          name = "",
          176  +          type = "",
          177  +          shape = "rectangle",
          178  +          x = 19.6667,
          179  +          y = 142.667,
          180  +          width = 16,
          181  +          height = 16,
          182  +          rotation = 0,
          183  +          gid = 7,
          184  +          visible = false,
          185  +          properties = {}
          186  +        },
          187  +        {
          188  +          id = 17,
          189  +          name = "",
          190  +          type = "",
          191  +          shape = "rectangle",
          192  +          x = 166,
          193  +          y = 415.667,
          194  +          width = 16,
          195  +          height = 16,
          196  +          rotation = 0,
          197  +          gid = 8,
          198  +          visible = false,
          199  +          properties = {}
          200  +        },
          201  +        {
          202  +          id = 19,
          203  +          name = "",
          204  +          type = "",
          205  +          shape = "rectangle",
          206  +          x = 161,
          207  +          y = 247.333,
          208  +          width = 16,
          209  +          height = 16,
          210  +          rotation = 0,
          211  +          gid = 9,
          212  +          visible = false,
          213  +          properties = {}
          214  +        },
          215  +        {
          216  +          id = 21,
          217  +          name = "",
          218  +          type = "",
          219  +          shape = "rectangle",
          220  +          x = 32.6667,
          221  +          y = 417.667,
          222  +          width = 16,
          223  +          height = 16,
          224  +          rotation = 0,
          225  +          gid = 11,
          226  +          visible = false,
          227  +          properties = {}
          228  +        },
          229  +        {
          230  +          id = 22,
          231  +          name = "",
          232  +          type = "",
          233  +          shape = "rectangle",
          234  +          x = 30.3333,
          235  +          y = 285,
          236  +          width = 16,
          237  +          height = 16,
          238  +          rotation = 0,
          239  +          gid = 11,
          240  +          visible = false,
          241  +          properties = {}
          242  +        },
          243  +        {
          244  +          id = 23,
          245  +          name = "",
          246  +          type = "",
          247  +          shape = "rectangle",
          248  +          x = 248.333,
          249  +          y = 425,
          250  +          width = 16,
          251  +          height = 16,
          252  +          rotation = 0,
          253  +          gid = 10,
          254  +          visible = false,
          255  +          properties = {}
          256  +        },
          257  +        {
          258  +          id = 29,
          259  +          name = "",
          260  +          type = "",
          261  +          shape = "rectangle",
          262  +          x = 737.333,
          263  +          y = 299.333,
          264  +          width = 16,
          265  +          height = 16,
          266  +          rotation = 0,
          267  +          gid = 6,
          268  +          visible = false,
          269  +          properties = {}
          270  +        },
          271  +        {
          272  +          id = 30,
          273  +          name = "",
          274  +          type = "",
          275  +          shape = "rectangle",
          276  +          x = 442,
          277  +          y = 44.6667,
          278  +          width = 16,
          279  +          height = 16,
          280  +          rotation = 0,
          281  +          gid = 5,
          282  +          visible = false,
          283  +          properties = {}
          284  +        },
          285  +        {
          286  +          id = 31,
          287  +          name = "",
          288  +          type = "",
          289  +          shape = "rectangle",
          290  +          x = 811.333,
          291  +          y = 203.333,
          292  +          width = 16,
          293  +          height = 16,
          294  +          rotation = 0,
          295  +          gid = 9,
          296  +          visible = false,
          297  +          properties = {}
          298  +        },
          299  +        {
          300  +          id = 32,
          301  +          name = "",
          302  +          type = "",
          303  +          shape = "rectangle",
          304  +          x = 730,
          305  +          y = 174,
          306  +          width = 16,
          307  +          height = 16,
          308  +          rotation = 0,
          309  +          gid = 10,
          310  +          visible = false,
          311  +          properties = {}
          312  +        },
          313  +        {
          314  +          id = 33,
          315  +          name = "",
          316  +          type = "",
          317  +          shape = "rectangle",
          318  +          x = 1264,
          319  +          y = 313.333,
          320  +          width = 16,
          321  +          height = 16,
          322  +          rotation = 0,
          323  +          gid = 5,
          324  +          visible = false,
          325  +          properties = {}
          326  +        },
          327  +        {
          328  +          id = 34,
          329  +          name = "",
          330  +          type = "",
          331  +          shape = "rectangle",
          332  +          x = 1480.67,
          333  +          y = 291.333,
          334  +          width = 16,
          335  +          height = 16,
          336  +          rotation = 0,
          337  +          gid = 5,
          338  +          visible = false,
          339  +          properties = {}
          340  +        },
          341  +        {
          342  +          id = 35,
          343  +          name = "",
          344  +          type = "",
          345  +          shape = "rectangle",
          346  +          x = 1062.67,
          347  +          y = 90.6667,
          348  +          width = 16,
          349  +          height = 16,
          350  +          rotation = 0,
          351  +          gid = 6,
          352  +          visible = false,
          353  +          properties = {}
          354  +        },
          355  +        {
          356  +          id = 36,
          357  +          name = "",
          358  +          type = "",
          359  +          shape = "rectangle",
          360  +          x = 1646,
          361  +          y = 268,
          362  +          width = 16,
          363  +          height = 16,
          364  +          rotation = 0,
          365  +          gid = 10,
          366  +          visible = false,
          367  +          properties = {}
          368  +        },
          369  +        {
          370  +          id = 37,
          371  +          name = "",
          372  +          type = "",
          373  +          shape = "rectangle",
          374  +          x = 1039.33,
          375  +          y = 264,
          376  +          width = 16,
          377  +          height = 16,
          378  +          rotation = 0,
          379  +          gid = 11,
          380  +          visible = false,
          381  +          properties = {}
          382  +        },
          383  +        {
          384  +          id = 38,
          385  +          name = "",
          386  +          type = "",
          387  +          shape = "rectangle",
          388  +          x = 1370,
          389  +          y = 222.667,
          390  +          width = 16,
          391  +          height = 16,
          392  +          rotation = 0,
          393  +          gid = 11,
          394  +          visible = false,
          395  +          properties = {}
          396  +        },
          397  +        {
          398  +          id = 39,
          399  +          name = "",
          400  +          type = "",
          401  +          shape = "rectangle",
          402  +          x = 1129.33,
          403  +          y = 264,
          404  +          width = 16,
          405  +          height = 16,
          406  +          rotation = 0,
          407  +          gid = 11,
          408  +          visible = false,
          409  +          properties = {}
          410  +        },
          411  +        {
          412  +          id = 40,
          413  +          name = "",
          414  +          type = "",
          415  +          shape = "rectangle",
          416  +          x = 741.333,
          417  +          y = 390.667,
          418  +          width = 16,
          419  +          height = 16,
          420  +          rotation = 0,
          421  +          gid = 9,
          422  +          visible = false,
          423  +          properties = {}
          424  +        },
          425  +        {
          426  +          id = 41,
          427  +          name = "",
          428  +          type = "",
          429  +          shape = "rectangle",
          430  +          x = 323.333,
          431  +          y = 298.667,
          432  +          width = 16,
          433  +          height = 16,
          434  +          rotation = 0,
          435  +          gid = 9,
          436  +          visible = false,
          437  +          properties = {}
          438  +        },
          439  +        {
          440  +          id = 42,
          441  +          name = "",
          442  +          type = "",
          443  +          shape = "rectangle",
          444  +          x = 1798.67,
          445  +          y = 318,
          446  +          width = 16,
          447  +          height = 16,
          448  +          rotation = 0,
          449  +          gid = 11,
          450  +          visible = false,
          451  +          properties = {}
          452  +        },
          453  +        {
          454  +          id = 43,
          455  +          name = "",
          456  +          type = "",
          457  +          shape = "rectangle",
          458  +          x = 1869.33,
          459  +          y = 104.667,
          460  +          width = 16,
          461  +          height = 16,
          462  +          rotation = 0,
          463  +          gid = 11,
          464  +          visible = false,
          465  +          properties = {}
          466  +        },
          467  +        {
          468  +          id = 44,
          469  +          name = "",
          470  +          type = "",
          471  +          shape = "rectangle",
          472  +          x = 1908,
          473  +          y = 186.667,
          474  +          width = 16,
          475  +          height = 16,
          476  +          rotation = 0,
          477  +          gid = 9,
          478  +          visible = false,
          479  +          properties = {}
          480  +        }
          481  +      }
          482  +    }
          483  +  }
          484  +}

Added src/worpt/mode-intro.fnl.

            1  +(var counter 0)
            2  +(var time 0)
            3  +
            4  +(local dev-mode? true)
            5  +(local play-game :play)
            6  +;;(local play-game :play-osms-part1)
            7  +
            8  +{:draw (fn draw [message]
            9  +         (love.graphics.print (: "This window should close in %0.2f seconds"
           10  +                                 :format (- 3 time)) 32 16))
           11  + :update (fn update [dt set-mode]
           12  +             (if (< counter 65535)
           13  +                 (set counter (+ counter 1))
           14  +                 (set counter 0))
           15  +             (set time (+ time dt))
           16  +             (when (or (> time 3) dev-mode?)
           17  +               (set-mode play-game)))
           18  + :keypressed (fn keypressed [key set-mode]
           19  +                 (love.event.quit))}

Added src/worpt/play-osms-part1.fnl.

            1  +;; This is a Fennel implementation of http://www.osmstudios.com/tutorials/love2d-platformer-tutorial-part-1-the-basics
            2  +
            3  +(local bump (require "lib.bump"))
            4  +(local player-img (love.graphics.newImage "assets/osmstudios_character_block.png"))
            5  +
            6  +(local player {
            7  +  :x 16
            8  +  :y 16
            9  +  ;; The first set of values are for our rudimentary physics system
           10  +  :xVelocity 0 ;; current velocity on x, y axes
           11  +  :yVelocity 0
           12  +  :acc 100 ;; the acceleration of our player
           13  +  :maxSpeed 600 ;; the top speed
           14  +  :friction 20 ;; slow our player down - we could toggle this situationally to create icy or slick platforms
           15  +  :gravity 80 ;; we will accelerate towards the bottom
           16  +
           17  +  ;; These are values applying specifically to jumping
           18  +  :isJumping false ;; are we in the process of jumping?
           19  +  :isGrounded false ;; are we on the ground?
           20  +  :hasReachedMax false ;; is this as high as we can go?
           21  +  :jumpAcc 500 ;; how fast do we accelerate towards the top
           22  +  :jumpMaxSpeed 9.5 ;; our speed limit while jumping
           23  +
           24  +  ;; Here are some incidental storage areas
           25  +  :img player-img}) ;; store the sprite we'll be drawing
           26  +
           27  +(local world (bump.newWorld 16))
           28  +(local ground-0 {})
           29  +(local ground-1 {})
           30  +
           31  +(: world :add player player.x player.y (: player.img :getWidth) (: player.img :getHeight))
           32  +;;(: world :add ground-0 20 360 640 16) ; blog version
           33  +(: world :add ground-0 120 360 640 16) ; github version
           34  +(: world :add ground-1 0 448 640 32)
           35  +
           36  +(set player.filter (fn [item other]
           37  +                     (let [(x y w h) (: world :getRect other)
           38  +                           (px py pw ph) (: world :getRect item)
           39  +                           player-bottom (+ py ph)
           40  +                           other-bottom (+ y h)]
           41  +                       (if (<= player-bottom y)
           42  +                         :slide))))
           43  +
           44  +(fn update [dt set-mode]
           45  +  (let [goal-x (+ player.x player.xVelocity)
           46  +        goal-y (+ player.y player.yVelocity)
           47  +        (x y cols) (: world :move player goal-x goal-y player.filter)]
           48  +    (set player.x (lume.clamp x 5 1000))
           49  +    (set player.y y)
           50  +    (each [i col (ipairs cols)]
           51  +      (if
           52  +        (> col.touch.y goal-y)
           53  +        (do
           54  +          (set player.hasReachedMax true)
           55  +          (set player.isGrounded false))
           56  +        (< col.normal.y 0)
           57  +        (do
           58  +          (set player.hasReachedMax false)
           59  +          (set player.isGrounded true)))))
           60  +
           61  +  ;; apply friction
           62  +  (let [df (- 1 (math.min 1 (* dt player.friction)))]
           63  +    (set player.xVelocity (* player.xVelocity df))
           64  +    (set player.yVelocity (* player.yVelocity df)))
           65  +
           66  +  ;; apply gravity
           67  +  (set player.yVelocity (+ player.yVelocity (* player.gravity dt)))
           68  +
           69  +  ;; handle horizontal movement
           70  +  (if
           71  +    (and (love.keyboard.isDown "left" "a") (> player.xVelocity (- player.maxSpeed)))
           72  +    (set player.xVelocity (- player.xVelocity (* player.acc dt)))
           73  +    (and (love.keyboard.isDown "right" "d") (< player.xVelocity player.maxSpeed))
           74  +    (set player.xVelocity (+ player.xVelocity (* player.acc dt))))
           75  +
           76  +  ;; jump up jump up and get down
           77  +  (if (love.keyboard.isDown "up" "w")
           78  +    (do
           79  +      (if
           80  +        (and (< (- player.yVelocity) player.jumpMaxSpeed) (not player.hasReachedMax))
           81  +        (do
           82  +          ;; (log "{%s} Setting player velocity{%s} to {%s} using a={%s}" dt player.yVelocity (- player.yVelocity (* player.jumpAcc dt)) player.jumpAcc)
           83  +          (set player.yVelocity (- player.yVelocity (* player.jumpAcc dt))))
           84  +        ;; (log "Approaching terminal velocity %s ----> %s" (math.abs player.yVelocity) player.jumpMaxSpeed)
           85  +        (> (math.abs player.yVelocity) player.jumpMaxSpeed)
           86  +        (set player.hasReachedMax true))
           87  +
           88  +      (set player.isGrounded false))))
           89  +
           90  +(fn keypressed [key set-mode])
           91  +
           92  +(fn draw [player world]
           93  +  (love.graphics.draw player.img player.x player.y)
           94  +  (let [(x0 y0 w0 h0) (: world :getRect ground-0)
           95  +        (x1 y1 w1 h1) (: world :getRect ground-1)]
           96  +    (love.graphics.rectangle "fill" x0 y0 w0 h0)
           97  +    (love.graphics.rectangle "fill" x1 y1 w1 h1)))
           98  +
           99  +{:draw (partial draw player world)
          100  + :update update
          101  + :keypressed keypressed}

Added src/worpt/play.fnl.

            1  +;; This is a Fennel implementation of http://www.osmstudios.com/tutorials/love2d-platformer-tutorial-part-1-the-basics
            2  +;; combined with the awesome art assets and the demo game from
            3  +;; https://ansimuz.itch.io/warped-city
            4  +
            5  +;; TODO
            6  +;; - [ ] swap animation directions for -x movement
            7  +;;   # the moonwalking is pretty cool though...
            8  +
            9  +(local debug? false)
           10  +
           11  +(local anim8 (require "lib.anim8"))
           12  +(local bump (require "lib.bump"))
           13  +(local sti (require "lib.sti"))
           14  +
           15  +(local game-width 384)
           16  +(local game-height 224)
           17  +
           18  +(local idle-speed 0.001)
           19  +
           20  +(local bg-1 (love.graphics.newImage "assets/warped_city/environment/bg-1.png"))
           21  +(local bg-2 (love.graphics.newImage "assets/warped_city/environment/bg-2.png"))
           22  +(local bg-3 (love.graphics.newImage "assets/warped_city/environment/bg-3.png"))
           23  +
           24  +(local player-img (love.graphics.newImage "assets/osmstudios_character_block.png"))
           25  +(local player {
           26  +  :x 16
           27  +  :y 330
           28  +  ;; The first set of values are for our rudimentary physics system
           29  +  :xVelocity 0 ;; current velocity on x, y axes
           30  +  :yVelocity 0
           31  +  :acc 10 ;; the acceleration of our player
           32  +  :maxSpeed 600 ;; the top speed
           33  +  :friction 20 ;; slow our player down - we could toggle this situationally to create icy or slick platforms
           34  +  :gravity 30 ;; we will accelerate towards the bottom
           35  +
           36  +  ;; These are values applying specifically to jumping
           37  +  :isJumping false ;; are we in the process of jumping?
           38  +  :isGrounded false ;; are we on the ground?
           39  +  :hasReachedMax false ;; is this as high as we can go?
           40  +  :jumpAcc 40 ;; how fast do we accelerate towards the top
           41  +  :jumpMaxSpeed 9.5 ;; our speed limit while jumping
           42  +
           43  +  ;; Here are some incidental storage areas
           44  +  :state :idle ;; player animation
           45  +  :img player-img}) ;; store the sprite we'll be drawing
           46  +
           47  +(local atlas-data (require "assets/warped_city/atlas/atlas"))
           48  +
           49  +(local atlas-tile (love.graphics.newImage "assets/warped_city/atlas/atlas.png"))
           50  +;; apparently this helps for scaling
           51  +(: atlas-tile :setFilter "nearest" "linear")
           52  +
           53  +;;> Created animations: ["enemy-explosion" "control-box" "climb" "shot" "back-jump" "banner-coke" "shot-hit" "banner-scroll" "banner-sushi" "monitor-face" "run" "drone" "banner-side" "walk" "turret" "banner-big" "run-shoot" "idle" "banner-neon" "jump"]
           54  +
           55  +(local gid->am {
           56  +                5  :banner-coke
           57  +                6  :banner-big
           58  +                7  :banner-neon
           59  +                8  :banner-scroll
           60  +                9  :banner-side
           61  +                10 :banner-sushi
           62  +                11 :monitor-face})
           63  +
           64  +(local am->gid {})
           65  +(each [gid am (pairs gid->am)] (tset am->gid am gid))
           66  +
           67  +(local atlas-quads {})
           68  +(local atlas-imgs {})
           69  +(local atlas-ams {})
           70  +(local atlas-ams-imgs {})
           71  +
           72  +;; process atlas definition
           73  +(let [(tw th) (: atlas-tile :getDimensions)]
           74  +  (each [i frame (ipairs atlas-data.frames)]
           75  +    (let [name frame.filename
           76  +          x frame.frame.x
           77  +          y frame.frame.y
           78  +          w frame.frame.w
           79  +          h frame.frame.h
           80  +          quad (love.graphics.newQuad x y w h tw th)]
           81  +      (if
           82  +        ;; annimation images end in the frame number
           83  +        (string.match name "^(.*)(-%d+)$")
           84  +        (let [(group scount) (string.match name "^(.*)-(%d+)$")
           85  +              count (tonumber scount)]
           86  +          (when (not (. atlas-ams-imgs group)) (tset atlas-ams-imgs group []))
           87  +          (table.insert (. atlas-ams-imgs group) quad)
           88  +          (assert (= (length (. atlas-ams-imgs group)) count)))
           89  +        ;; other images
           90  +        (string.match name "[a-z]$")
           91  +        (tset atlas-imgs name quad)))))
           92  +
           93  +;; create animations
           94  +(each [name quads (pairs atlas-ams-imgs)]
           95  +  (let [duration (if (. am->gid name) 0.5 0.15)
           96  +        am (anim8.newAnimation quads duration)]
           97  +    (tset atlas-ams name am)))
           98  +
           99  +(log "Created animations: %s" (pps (lume.keys atlas-ams)))
          100  +
          101  +(local map (sti "map.lua" ["bump"])) ;; TODO: add lint wrapper
          102  +(local world (bump.newWorld 16))
          103  +
          104  +(: map :bump_init world)
          105  +(: world :add player player.x player.y (: player.img :getWidth) (: player.img :getHeight))
          106  +
          107  +;;(log "MAP PROPERTIES: %s" (pps (lume.keys map.layers)))
          108  +
          109  +(local entity-layer (: map :addCustomLayer "Entities" (+ 1 (length map.layers))))
          110  +(set entity-layer.entities {})
          111  +(fn entity-layer.update [self dt]
          112  +  (each [_i ent (ipairs self.entities)]
          113  +    (when ent.update (ent.update ent dt))))
          114  +(fn entity-layer.draw [self dt]
          115  +  (each [_i ent (ipairs self.entities)]
          116  +    (when ent.draw (ent.draw ent))))
          117  +
          118  +(fn player.draw [self]
          119  +  (let [am (. atlas-ams player.state)
          120  +        x (+ player.x 0) y (+ player.y 14)]
          121  +    ;; TODO: switch to drawing with a spritebatch
          122  +    (: am :draw atlas-tile x y)))
          123  +
          124  +(local ground-0 {})
          125  +(local ground-1 {})
          126  +
          127  +(fn draw-object [obj]
          128  +  (let [x obj.x y obj.y am obj.am]
          129  +    (: am :draw atlas-tile x y)))
          130  +
          131  +(fn update-object [obj dt]
          132  +  (let [am obj.am]
          133  +    (: am :update dt)))
          134  +
          135  +(each [_i obj (pairs map.objects)]
          136  +  (let [id obj.id
          137  +        gid obj.gid
          138  +        am-name (. gid->am gid)
          139  +        am (. atlas-ams am-name)
          140  +        o-x obj.x o-y obj.y o-w obj.width o-h obj.height
          141  +        (a-w a-h) (: am :getDimensions)
          142  +        x (+ o-x o-w) y (- o-y o-h a-h)
          143  +        am-obj {:x x :y y :am am :draw draw-object :update update-object}]
          144  +    ;;(log "Adding animation object %s-%s %s" id gid am-name)
          145  +    (table.insert entity-layer.entities am-obj)))
          146  +
          147  +;; Insert player last so it's rendered on top; who needs z-index'ing amirite?
          148  +(table.insert entity-layer.entities player)
          149  +
          150  +(set player.filter (fn [item other]
          151  +                     ;;(log "ITEM{%s} COLLIDED WITH OTHER{%s}" (pps item) (pps (lume.keys other.properties)))
          152  +                     (let [(x y w h) (: world :getRect other)
          153  +                           (px py pw ph) (: world :getRect item)
          154  +                           player-bottom (+ py ph)
          155  +                           other-bottom (+ y h)]
          156  +                       (if (<= player-bottom y)
          157  +                         :slide))))
          158  +
          159  +(fn update [map world player dt set-mode]
          160  +  (let [goal-x (+ player.x player.xVelocity)
          161  +        goal-y (+ player.y player.yVelocity)
          162  +        (x y cols) (: world :move player goal-x goal-y player.filter)
          163  +        am (. atlas-ams player.state)]
          164  +    (: am :update dt)
          165  +    (: map :update dt)
          166  +    (set player.x (lume.clamp x 0 (* game-width 16)))
          167  +    (set player.y y)
          168  +    (each [i col (ipairs cols)]
          169  +      (if
          170  +        (> col.touch.y goal-y)
          171  +        (do
          172  +          (set player.hasReachedMax true)
          173  +          (set player.isGrounded false)
          174  +          (set player.state :jump))
          175  +        (< col.normal.y 0)
          176  +        (do
          177  +          (set player.hasReachedMax false)
          178  +          (set player.isGrounded true)
          179  +          (set player.state :walk)))) ;; TODO: don't just default to :walk
          180  +
          181  +  ;; apply friction
          182  +  (let [df (- 1 (math.min 1 (* dt player.friction)))]
          183  +    (set player.xVelocity (* player.xVelocity df))
          184  +    (set player.yVelocity (* player.yVelocity df)))
          185  +
          186  +  ;; apply gravity
          187  +  (set player.yVelocity (+ player.yVelocity (* player.gravity dt)))
          188  +
          189  +  ;; handle horizontal movement
          190  +  (if
          191  +    (and (love.keyboard.isDown "left" "a") (> player.xVelocity (- player.maxSpeed)))
          192  +    (set player.xVelocity (- player.xVelocity (* player.acc dt)))
          193  +    (and (love.keyboard.isDown "right" "d") (< player.xVelocity player.maxSpeed))
          194  +    (set player.xVelocity (+ player.xVelocity (* player.acc dt))))
          195  +
          196  +  (if
          197  +    (< (math.abs player.xVelocity) idle-speed)
          198  +    (when (not (= player.state :idle)) (set player.state :idle))
          199  +    (match player.state
          200  +      :idle    (set player.state :walk)))
          201  +
          202  +  ;; jump up jump up and get down
          203  +  (if (love.keyboard.isDown "up" "w")
          204  +    (do
          205  +      (if
          206  +        (and (< (- player.yVelocity) player.jumpMaxSpeed) (not player.hasReachedMax))
          207  +        (do
          208  +          (set player.yVelocity (- player.yVelocity (* player.jumpAcc dt))))
          209  +        (>= (math.abs player.yVelocity) player.jumpMaxSpeed)
          210  +        (set player.hasReachedMax true))
          211  +
          212  +      (set player.isGrounded false)
          213  +      (set player.state :jump)))))
          214  +
          215  +(fn keypressed [key set-mode])
          216  +
          217  +(fn draw [map world player]
          218  +  (let [w (love.graphics.getWidth)
          219  +        h (love.graphics.getHeight)
          220  +        iw (: bg-1 :getWidth)
          221  +        ih (: bg-1 :getHeight)
          222  +        sw (/ w iw)
          223  +        sh (/ h ih)
          224  +        tx (math.max 0 (math.floor (- player.x (/ w 4))))
          225  +        ty (math.max 0 (math.floor (- player.y (/ h 4))))
          226  +        tdx 1]
          227  +    (love.graphics.draw bg-1 0 0 0 sw sh)
          228  +    (love.graphics.draw bg-2 0 0 0 sw sh)
          229  +    (love.graphics.draw bg-3 0 0 0 sw sh)
          230  +
          231  +    (: map :draw (- tx) (- ty))))
          232  +
          233  +(fn draw-debug []
          234  +  (when debug?
          235  +    (let [msg-fps (string.format "Current FPS: %s" (love.timer.getFPS))
          236  +          msg-state (string.format "STATE: J?|G?|M? -- %s|%s|%s" player.isJumping player.isGrounded player.hasReachedMax)
          237  +          msg-xy (string.format "{X,Y}: {%.2f, %.2f} || {dx: %.2f, dy: %.2f}" player.x player.y player.xVelocity player.yVelocity)]
          238  +      (love.graphics.print msg-fps 10 10)
          239  +      (love.graphics.print msg-xy 10 30)
          240  +      (love.graphics.print msg-state 10 50))))
          241  +
          242  +{:draw (partial draw map world player)
          243  + :draw-debug (partial draw-debug map world player)
          244  + :update (partial update map world player)
          245  + :keypressed keypressed}

Added src/worpt/readme.md.

            1  +# Chewbranca's Worpt
            2  +
            3  +Worpt is a prototype platformer using the awesome _Warped City_ art assets from https://ansimuz.itch.io/warped-city and the platforming physics from http://www.osmstudios.com/tutorials/love2d-platformer-tutorial-part-1-the-basics . Currently this is an engine capable of running the map.json file from the _Warped City_ demo game. The goal was for this to be the intro level and then to create new levels, and the progress is that intro level is playable and nearly complete.
            4  +
            5  +## This Project is Based On:
            6  +
            7  +### Project Structure
            8  +
            9  +  - technomancy: https://gitlab.com/technomancy/exo-encounter-667
           10  +  - alexjgriffith: https://gitlab.com/alexjgriffith/min-love2d-fennel
           11  +
           12  +## Platforming Physics
           13  +
           14  +  - OSM Studios Tutorial: http://www.osmstudios.com/tutorials/love2d-platformer-tutorial-part-1-the-basics
           15  +
           16  +## Game Art Assets, Map File, and Sample Game
           17  +
           18  +  The wonderful game assets from:
           19  +
           20  +  - Warped City: https://ansimuz.itch.io/warped-city
           21  +
           22  +## Original readme.{org,md} follows:
           23  +
           24  +-----
           25  +
           26  +# Minimal Fennel Love2D Setup
           27  +
           28  +In a lead up to the annual [Autumn Lisp Game Jam](https://itch.io/jam/autumn-lisp-game-jam-2018) I thought I'd look into Phil Hegelberg's approach to last Aprils Jam, using [love2d](https://love2d.org/) in concert with [fennel](https://fennel-lang.org/). Phil outlines his approach on his [blog](https://love2d.org/).
           29  +
           30  +The first step is to see how fennel works as a standalone execution environment, then slowly integrate some of the love2d API.
           31  +
           32  +This repo contains the minimal viable setup to get started with Phil Hegelberg's game design process, plus some additional libraries.
           33  +
           34  +```
           35  +PROJECT=project-name
           36  +git clone https://gitlab.com/alexjgriffith/min-love2d-fennel.git $PROJECT
           37  +cd $PROJECT
           38  +rm -rf .git
           39  +```
           40  +
           41  +# Phil's Modal Callbacks (PMC)
           42  +Phil Hegelberg's [exo-encounter-667](https://gitlab.com/technomancy/exo-encounter-667/) is structured using a modal callback system. Each game state has a mode and each mode has a series of specific callbacks.
           43  +
           44  +If you design your game as a series of states in a very simple state machine, for example *start-screen*, *play* and *end*, with unidirectional progression, you can easily separate the logic for each state into state/mode specific callbacks. As an example, in order to have state dependant rendering that differs between start-screen,play and end you could provide a *draw* callback for each of those states. Similarly if we need state dependent logic and keyboard input we could provide *update* and *keyboard* callbacks. As you iterate you can add and remove callbacks and states/modes as needed with very little friction.
           45  +
           46  +
           47  +# Emacs Setup
           48  +Once you install the latest version of [fennel-mode](https://gitlab.com/technomancy/fennel-mode), you can run
           49  +`C-u M-x fennel-repl` followed by `love .` to launch a repl.

Added src/worpt/wrap.fnl.

            1  +(local repl (require "lib.stdio"))
            2  +(local canvas (let [(w h) (love.window.getMode)]
            3  +                (love.graphics.newCanvas w h)))
            4  +
            5  +(var scale 2)
            6  +
            7  +;; set the first mode
            8  +(var mode (require "mode-intro"))
            9  +
           10  +(fn set-mode [mode-name ...]
           11  +  (set mode (require mode-name))
           12  +  (when mode.activate
           13  +    (mode.activate ...)))
           14  +
           15  +(fn love.load []
           16  +  (canvas:setFilter "nearest" "nearest")
           17  +  (repl.start))
           18  +
           19  +(fn love.draw []
           20  +  ;; the canvas allows you to get sharp pixel-art style scaling; if you
           21  +  ;; don't want that, just skip that and call mode.draw directly.
           22  +  (love.graphics.setCanvas canvas)
           23  +  (love.graphics.clear)
           24  +  (love.graphics.setColor 1 1 1)
           25  +  (mode.draw)
           26  +  (love.graphics.setCanvas)
           27  +  (love.graphics.setColor 1 1 1)
           28  +  (love.graphics.draw canvas 0 0 0 scale scale)
           29  +  (if mode.draw-debug (mode.draw-debug)))
           30  +
           31  +(fn love.update [dt]
           32  +  (mode.update dt set-mode))
           33  +
           34  +(fn love.keypressed [key]
           35  +  (if (and (love.keyboard.isDown "lctrl" "rctrl" "capslock") (= key "q"))
           36  +      (love.event.quit)
           37  +      ;; add what each keypress should do in each mode
           38  +      (mode.keypressed key set-mode)))