├── public
├── .gitkeep
├── tiles.png
├── coinGold.png
├── player.png
├── screenshot.png
├── license.txt
├── player.json
├── map.tmx
└── map.json
├── .husky
└── pre-commit
├── src
├── vite-env.d.ts
├── main.ts
└── scenes
│ ├── kibibit-logo.scene.ts
│ └── profile.scene.ts
├── .eslintignore
├── vite.config.ts
├── .gitignore
├── tsconfig.json
├── scss
└── style.scss
├── .github
└── workflows
│ └── github-pages.yml
├── index.html
├── package.json
├── .stylelintrc.cjs
├── readme.md
├── plugins
└── stylelint-selector-disallowed-custom.mjs
└── .eslintrc.cjs
/public/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | npx lint-staged
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/public/tiles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kibibit/phaser-seed/main/public/tiles.png
--------------------------------------------------------------------------------
/public/coinGold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kibibit/phaser-seed/main/public/coinGold.png
--------------------------------------------------------------------------------
/public/player.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kibibit/phaser-seed/main/public/player.png
--------------------------------------------------------------------------------
/public/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kibibit/phaser-seed/main/public/screenshot.png
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # don't ever lint node_modules
2 | node_modules
3 | # don't lint build output (make sure it's set to your correct build folder name)
4 | dist
5 | # .eslintrc.cjs
--------------------------------------------------------------------------------
/public/license.txt:
--------------------------------------------------------------------------------
1 | Assets copyright
2 | The art assets used in this tutorial are created by Kenney Vleugels and can be found at www.kenney.nl.
3 | All assets used are public domain CC0 licensed.
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 |
3 | export default defineConfig({
4 | base: '',
5 | plugins: [],
6 | server: { host: '0.0.0.0', port: 8000 },
7 | clearScreen: false
8 | });
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | css/
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "lib": ["ESNext", "DOM"],
7 | "moduleResolution": "Node",
8 | "strict": true,
9 | "sourceMap": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 |
3 | import KibibitLogoScene from './scenes/kibibit-logo.scene';
4 | import ProfileScene from './scenes/profile.scene';
5 |
6 | // select scene based on url params
7 | const urlParams = new URLSearchParams(window.location.search);
8 | const scene = urlParams.get('scene');
9 |
10 | const config: Phaser.Types.Core.GameConfig = {
11 | type: Phaser.AUTO,
12 | parent: 'app',
13 | width: document.body.clientWidth,
14 | height: (document.body.clientHeight || 700) / 2,
15 | physics: {
16 | default: 'arcade',
17 | arcade: {
18 | gravity: { y: 500 }
19 | }
20 | },
21 | // scene: [ KibibitLogoScene ]
22 | scene: [
23 | scene === 'profile' ? ProfileScene : KibibitLogoScene
24 | ]
25 | };
26 |
27 | export default new Phaser.Game(config);
28 |
--------------------------------------------------------------------------------
/src/scenes/kibibit-logo.scene.ts:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 |
3 | export default class KibibitLogoScene extends Phaser.Scene {
4 | constructor() {
5 | super('kibibit-logo');
6 | }
7 |
8 | preload() {
9 | this.load.setBaseURL('https://kibibit.io/kibibit-assets');
10 |
11 | this.load.image('sky', '4HPEcJ8.jpg');
12 | this.load.image('logo', 'long-white-small.png');
13 | this.load.image('red', 'https://labs.phaser.io/assets/particles/red.png' );
14 | }
15 |
16 | create() {
17 | this.add.image(400, 300, 'sky');
18 |
19 | // const particles = this.add.particles('red')
20 |
21 | // const emitter = particles.createEmitter({
22 | // speed: 100,
23 | // scale: { start: 1, end: 0 },
24 | // blendMode: 'ADD',
25 | // });
26 |
27 | const logo = this.physics.add.image(400, 100, 'logo');
28 |
29 | logo.setVelocity(100, 200);
30 | logo.setBounce(1, 1);
31 | logo.setCollideWorldBounds(true);
32 |
33 | // emitter.startFollow(logo);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/scss/style.scss:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | --avatar-size: 150px;
4 | display: flex;
5 | flex-direction: column;
6 |
7 | // justify-content: center;
8 |
9 | align-items: center;
10 |
11 | width: 100dvw;
12 | height: 100dvh;
13 | margin: 0;
14 | padding: 0;
15 |
16 | font-size: 16px;
17 | font-family: Roboto, sans-serif;
18 | line-height: 1.5;
19 |
20 | background-color: #212121;
21 | }
22 |
23 | #app {
24 | position: relative;
25 | }
26 |
27 | canvas {
28 | box-shadow: rgb(0 0 0 / 50%) 0 5px 10px;
29 | }
30 |
31 | .content {
32 | margin-block-start: calc(var(--avatar-size) / 2);
33 | padding: 0 20px;
34 |
35 | color: rgb(131 130 130);
36 | }
37 |
38 | .user-avatar {
39 | position: absolute;
40 | inset-block-start: calc(100dvh / 2 - var(--avatar-size) / 2);
41 | inset-inline-start: calc(50% - var(--avatar-size) / 2);
42 |
43 | width: 150px;
44 | height: 150px;
45 |
46 | border-radius: 50%;
47 |
48 | background-color: pink;
49 |
50 | box-shadow: rgb(0 0 0 / 50%) 0 5px 10px;
51 |
52 | overflow: hidden;
53 |
54 | img {
55 | width: 100%;
56 | height: 100%;
57 | }
58 | }
--------------------------------------------------------------------------------
/.github/workflows/github-pages.yml:
--------------------------------------------------------------------------------
1 | name: 🗒️ GitHub Pages
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | workflow_dispatch:
7 |
8 | env:
9 | NODE_VERSION: '20.x'
10 |
11 | permissions:
12 | contents: read
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v4
19 |
20 | - name: Set up Node.js
21 | uses: actions/setup-node@v4
22 | with:
23 | node-version: ${{ env.NODE_VERSION }}
24 | cache: 'npm'
25 |
26 | - name: npm install, build, and test
27 | run: |
28 | npm install
29 | npm run build --if-present
30 | npm run test --if-present
31 |
32 | - name: Upload GitHub Pages artifact
33 | uses: actions/upload-pages-artifact@v3.0.1
34 | with:
35 | name: github-pages
36 | path: ./dist
37 |
38 | deploy:
39 | needs: build
40 | permissions:
41 | pages: write
42 | id-token: write
43 | environment:
44 | name: github-pages
45 | url: ${{ steps.deployment.outputs.page_url }}
46 | runs-on: ubuntu-latest
47 | steps:
48 | - name: Deploy to GitHub Pages
49 | id: deployment
50 | uses: actions/deploy-pages@v4
51 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Profile Platformer Test
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | This demo showcases the Achievibit profile cover image in action, brought to life using Phaser. Instead of a simple static image, the cover transforms into a fun, interactive platformer where users can explore their personalized "layer." The layer acts as a home base, reflecting the user’s faction and decorated with trophies and characters based on their achievements.
27 |
28 |
29 | Navigate through your custom environment, uncover hidden surprises, and get a taste of how your accomplishments come to life in this dynamic cover experience. This demo offers a glimpse into how profile covers can be more than just visuals—they can be adventures!
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phaser3-typescript-vite-template",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "lint": "eslint .",
8 | "lint:fix": "eslint --fix .",
9 | "lint:styles": "stylelint \"**/*.scss\" --config .stylelintrc.cjs",
10 | "lint:styles:fix": "stylelint \"**/*.scss\" --config .stylelintrc.cjs --fix",
11 | "start": "vite",
12 | "build": "tsc && npm run scss && vite build",
13 | "preview": "vite preview",
14 | "prepare": "husky",
15 | "scss": "sass scss/style.scss css/style.css",
16 | "scss:watch": "npm run scss -- --watch",
17 | "scss:minify": "npm run scss -- --style compressed"
18 | },
19 | "devDependencies": {
20 | "@eslint/js": "^9.9.1",
21 | "@html-eslint/eslint-plugin": "^0.26.0",
22 | "@html-eslint/parser": "^0.26.0",
23 | "@stylistic/eslint-plugin-js": "^2.7.2",
24 | "@typescript-eslint/eslint-plugin": "^5.40.0",
25 | "@typescript-eslint/parser": "^5.40.0",
26 | "eslint": "^8.57.0",
27 | "eslint-plugin-import": "^2.30.0",
28 | "eslint-plugin-simple-import-sort": "^12.1.1",
29 | "eslint-plugin-unused-imports": "^4.1.3",
30 | "husky": "^9.1.5",
31 | "lint-staged": "^15.2.10",
32 | "postcss-selector-parser": "^6.1.2",
33 | "sass": "^1.78.0",
34 | "stylelint": "^16.9.0",
35 | "stylelint-config-rational-order": "^0.1.2",
36 | "stylelint-config-recess-order": "^5.1.0",
37 | "stylelint-config-standard": "^36.0.1",
38 | "stylelint-config-standard-scss": "^13.1.0",
39 | "stylelint-order": "^6.0.4",
40 | "stylelint-semantic-groups": "^1.2.1",
41 | "stylelint-use-logical": "^2.1.2",
42 | "typescript": "^5.5.4",
43 | "typescript-eslint": "^8.4.0",
44 | "vite": "^3.1.0"
45 | },
46 | "dependencies": {
47 | "@kibibit/configit": "^2.11.1",
48 | "phaser": "^3.55.2"
49 | },
50 | "lint-staged": {
51 | "*.ts": "npm run lint:fix",
52 | "*.js": "npm run lint:fix",
53 | "*.scss": "npm run lint:styles:fix"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/.stylelintrc.cjs:
--------------------------------------------------------------------------------
1 | const { propertyOrdering, selectorOrdering } = require('stylelint-semantic-groups');
2 |
3 | module.exports = {
4 | overrides: [
5 | {
6 | files: [ '**/*.scss' ],
7 | customSyntax: 'postcss-scss'
8 | }
9 | ],
10 | extends: [
11 | 'stylelint-config-standard-scss'
12 | ],
13 | ignoreFiles: [
14 | "css/**/*.css"
15 | ],
16 | plugins: [
17 | 'stylelint-order',
18 | 'stylelint-config-rational-order/plugin',
19 | 'stylelint-use-logical',
20 | './plugins/stylelint-selector-disallowed-custom.mjs'
21 | ],
22 | rules: {
23 | 'order/order': selectorOrdering,
24 | 'order/properties-order': propertyOrdering,
25 | 'color-no-invalid-hex': true,
26 | 'declaration-empty-line-before': [ null, { 'severity': 'warning' } ],
27 | // 'string-quotes': 'single',
28 | 'selector-class-pattern': [ null, { 'severity': 'warning' } ],
29 | 'selector-pseudo-element-no-unknown': [true, { 'ignorePseudoElements': ['ng-deep'] } ],
30 | 'color-hex-length': 'long',
31 | 'csstools/use-logical': [
32 | true,
33 | {
34 | 'except': [
35 | 'width', // not important for RTL support
36 | 'height', // not important for RTL support
37 | 'min-width', // not important for RTL support
38 | 'min-height', // not important for RTL support
39 | 'max-width', // not important for RTL support
40 | 'max-height', // not important for RTL support
41 | /**
42 | * might need to eventually add this as well, since it's used in a lot of places and
43 | * would not affect RTL support anyway. This goes for both top and bottom.
44 | **/
45 | // 'top',
46 | // 'bottom'
47 | ]
48 | }
49 | ],
50 | "plugin/selector-disallowed-custom": [[
51 | ["/^&[a-zA-Z0-9-_]/", "Avoid concatenating strings with '&' (e.g., '<%= selector %>'). This reduces readability and clarity. Use explicit selectors instead." ],
52 | ["/__[a-zA-Z0-9]+(--[a-zA-Z0-9]+)?$/", "\"<%= selector %>\" is not allowed. Simplify your class naming strategy and avoid using BEM naming conventions to prevent bloated class names." ]
53 | ]],
54 | "selector-disallowed-list": null
55 | // "selector-disallowed-list": [
56 | // // don't allow concatenation of strings in selectors
57 | // "/^&[\\w-_]/"
58 | // ]
59 | }
60 | };
--------------------------------------------------------------------------------
/public/player.json:
--------------------------------------------------------------------------------
1 | {"frames":[{"filename":"p1_walk01","frame":{"x":73,"y":0,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_walk02","frame":{"x":219,"y":98,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_walk03","frame":{"x":0,"y":98,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_walk04","frame":{"x":73,"y":98,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_walk05","frame":{"x":146,"y":0,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_walk06","frame":{"x":146,"y":98,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_walk07","frame":{"x":219,"y":0,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_walk08","frame":{"x":0,"y":0,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_walk09","frame":{"x":0,"y":196,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_walk10","frame":{"x":73,"y":196,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_walk11","frame":{"x":146,"y":196,"w":72,"h":97},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":72,"h":97},"sourceSize":{"w":72,"h":97}},{"filename":"p1_jump","frame":{"x":219,"y":196,"w":67,"h":94},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":67,"h":94},"sourceSize":{"w":67,"h":94}},{"filename":"p1_stand","frame":{"x":292,"y":0,"w":66,"h":92},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":66,"h":92},"sourceSize":{"w":66,"h":92}},{"filename":"p1_front","frame":{"x":292,"y":93,"w":66,"h":92},"rotated":false,"trimmed":false,"spriteSourceSize":{"x":0,"y":0,"w":66,"h":92},"sourceSize":{"w":66,"h":92}}],"meta":{"app":"https://www.leshylabs.com/apps/sstool/","version":"Leshy SpriteSheet Tool v0.8.4","image":"player.png","size":{"w":358,"h":293},"scale":1}}
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Phaser 3 + TypeScript + Vite.js Template
2 | > Make Phaser 3 games with TypeScript and modern frontend tooling.
3 |
4 | 
5 |
6 | This is a TypeScript specific fork of [phaser3-vite-template](https://github.com/ourcade/phaser3-vite-template).
7 |
8 | ## Prerequisites
9 |
10 | You'll need [Node.js](https://nodejs.org/en/) and [npm](https://www.npmjs.com/) installed.
11 |
12 | It is highly recommended to use [Node Version Manager](https://github.com/nvm-sh/nvm) (nvm) to install Node.js and npm.
13 |
14 | For Windows users there is [Node Version Manager for Windows](https://github.com/coreybutler/nvm-windows).
15 |
16 | Install Node.js and `npm` with `nvm`:
17 |
18 | ```bash
19 | nvm install node
20 |
21 | nvm use node
22 | ```
23 |
24 | Replace 'node' with 'latest' for `nvm-windows`.
25 |
26 | ## Getting Started
27 |
28 | You can clone this repository or use [degit](https://github.com/Rich-Harris/degit) to scaffold the project like this:
29 |
30 | ```bash
31 | npx degit https://github.com/ourcade/phaser3-typescript-vite-template my-folder-name
32 | cd my-folder-name
33 |
34 | npm install
35 | ```
36 |
37 | Start development server:
38 |
39 | ```
40 | npm run start
41 | ```
42 |
43 | To create a production build:
44 |
45 | ```
46 | npm run build
47 | ```
48 |
49 | Production files will be placed in the `dist` folder. Then upload those files to a web server. 🎉
50 |
51 | ## Project Structure
52 |
53 | ```
54 | .
55 | ├── dist
56 | ├── node_modules
57 | ├── public
58 | ├── src
59 | │ ├── HelloWorldScene.ts
60 | │ ├── main.ts
61 | ├── index.html
62 | ├── package.json
63 | ```
64 |
65 | TypeScript files are intended for the `src` folder. `main.ts` is the entry point referenced by `index.html`.
66 |
67 | Other than that there is no opinion on how you should structure your project.
68 |
69 | There is an example `HelloWorldScene.ts` file that can be placed inside a `scenes` folder to organize by type or elsewhere to organize by function. For example, you can keep all files specific to the HelloWorld scene in a `hello-world` folder.
70 |
71 | It is all up to you!
72 |
73 | ## Static Assets
74 |
75 | Any static assets like images or audio files should be placed in the `public` folder. It'll then be served from the root. For example: http://localhost:8000/images/my-image.png
76 |
77 | Example `public` structure:
78 |
79 | ```
80 | public
81 | ├── images
82 | │ ├── my-image.png
83 | ├── music
84 | │ ├── ...
85 | ├── sfx
86 | │ ├── ...
87 | ```
88 |
89 | They can then be loaded by Phaser with `this.image.load('my-image', 'images/my-image.png')`.
90 |
91 | # TypeScript ESLint
92 |
93 | This template uses a basic `typescript-eslint` set up for code linting.
94 |
95 | It does not aim to be opinionated.
96 |
97 | [See here for rules to turn on or off](https://eslint.org/docs/rules/).
98 |
99 | ## Dev Server Port
100 |
101 | You can change the dev server's port number by modifying the `vite.config.ts` file. Look for the `server` section:
102 |
103 | ```js
104 | {
105 | // ...
106 | server: { host: '0.0.0.0', port: 8000 },
107 | }
108 | ```
109 |
110 | Change 8000 to whatever you want.
111 |
112 | ## License
113 |
114 | [MIT License](https://github.com/ourcade/phaser3-vite-template/blob/master/LICENSE)
115 |
--------------------------------------------------------------------------------
/plugins/stylelint-selector-disallowed-custom.mjs:
--------------------------------------------------------------------------------
1 | import stylelint from 'stylelint';
2 | import selectorParser from 'postcss-selector-parser';
3 | import _ from 'lodash';
4 |
5 | const ruleName = 'plugin/selector-disallowed-custom';
6 |
7 | // Helper to resolve the full selector when `&` is used
8 | const resolveFullSelector = (rule) => {
9 | let fullSelector = rule.selector;
10 | let parent = rule.parent;
11 |
12 | // Traverse upwards to resolve `&` in nested selectors
13 | while (parent && parent.type !== 'root') {
14 | if (parent.selector) {
15 | fullSelector = fullSelector.replace(/&/g, parent.selector);
16 | }
17 | parent = parent.parent;
18 | }
19 |
20 | return fullSelector;
21 | };
22 |
23 | const pluginRule = (primaryOption) => {
24 | return (root, result) => {
25 | primaryOption.forEach(([ pattern, errorMessage ]) => {
26 | const regexPattern = (typeof pattern === 'string' && pattern.startsWith('/') && pattern.endsWith('/'))
27 | ? new RegExp(pattern.slice(1, -1))
28 | : pattern;
29 |
30 | root.walkRules((rule) => {
31 | const fullSelector = resolveFullSelector(rule);
32 |
33 | selectorParser((selectors) => {
34 | selectors.walk((selectorNode) => {
35 | if (selectorNode.type === 'selector') {
36 | const partialSelector = selectorNode.toString();
37 |
38 | const selectorIndex = rule.raws.selector && rule.raws.selector.raw
39 | ? rule.toString().indexOf(rule.raws.selector.raw)
40 | : rule.toString().indexOf(rule.selector);
41 |
42 | // Check full selector (after resolving `&`)
43 | const isFullSelectorMatch = typeof regexPattern === 'string' && fullSelector === regexPattern;
44 | const isFullSelectorRegexMatch =
45 | regexPattern instanceof RegExp &&
46 | regexPattern.test(fullSelector);
47 | if (isFullSelectorMatch || isFullSelectorRegexMatch) {
48 | const template = _.template(errorMessage);
49 | const message = template({ selector: fullSelector });
50 | stylelint.utils.report({
51 | message: message,
52 | node: rule,
53 | result: result,
54 | ruleName: ruleName,
55 | index: selectorIndex,
56 | endIndex: selectorIndex + rule.selector.length
57 | });
58 |
59 | return;
60 | }
61 |
62 | // Check partial selector (without resolving `&`)
63 | const isPartialSelectorMatch = typeof regexPattern === 'string' && partialSelector === regexPattern;
64 | const isPartialSelectorRegexMatch =
65 | regexPattern instanceof RegExp &&
66 | regexPattern.test(partialSelector);
67 | if (isPartialSelectorMatch || isPartialSelectorRegexMatch) {
68 | const template = _.template(errorMessage || 'Selector "<%= selector %>" is disallowed');
69 | const message = template({ selector: partialSelector });
70 | stylelint.utils.report({
71 | message: message,
72 | node: rule,
73 | result: result,
74 | ruleName: ruleName,
75 | index: selectorIndex,
76 | endIndex: selectorIndex + rule.selector.length
77 | });
78 | }
79 | }
80 | });
81 | }).processSync(rule.selector);
82 | });
83 | });
84 | };
85 | };
86 |
87 | export default stylelint.createPlugin(ruleName, pluginRule);
88 |
--------------------------------------------------------------------------------
/src/scenes/profile.scene.ts:
--------------------------------------------------------------------------------
1 | import Phaser from 'phaser';
2 |
3 | export default class ProfileScene extends Phaser.Scene {
4 | map!: Phaser.Tilemaps.Tilemap;
5 | player!: Phaser.Physics.Arcade.Sprite;
6 | cursors!: Phaser.Types.Input.Keyboard.CursorKeys;
7 | groundLayer!: Phaser.Tilemaps.TilemapLayer;
8 |
9 | constructor() {
10 | super('kibibit-logo');
11 | }
12 |
13 | preload() {
14 | // map made with Tiled in JSON format
15 | this.load.tilemapTiledJSON('map', 'map.json');
16 | // tiles in spritesheet
17 | this.load.spritesheet('tiles', 'tiles.png', { frameWidth: 70, frameHeight: 70 });
18 | // simple coin image
19 | this.load.image('coin', 'coinGold.png');
20 | // player animations
21 | this.load.atlas('player', 'player.png', 'player.json');
22 | }
23 |
24 | create() {
25 | // load the map
26 | this.map = this.make.tilemap({ key: 'map' });
27 |
28 | // tiles for the ground layer
29 | const groundTiles = this.map.addTilesetImage('tiles');
30 | // create the ground layer
31 | this.groundLayer = this.map.createLayer('World', groundTiles, 0, 0);
32 | // the player will collide with this layer
33 | this.groundLayer.setCollisionByExclusion([ -1 ]);
34 | // set the boundaries of our game world
35 | this.physics.world.bounds.width = this.groundLayer.width;
36 | this.physics.world.bounds.height = this.groundLayer.height;
37 |
38 | // create the player sprite
39 | this.player = this.physics.add.sprite(200, 200, 'player');
40 | this.player.setBounce(0.2); // our player will bounce from items
41 | this.player.setCollideWorldBounds(true); // don't go out of the map
42 | this.physics.add.collider(this.groundLayer, this.player);
43 | this.cursors = this.input.keyboard.createCursorKeys();
44 |
45 | // set bounds so the camera won't go outside the game world
46 | this.cameras.main.setBounds(
47 | 0,
48 | 0,
49 | this.map.widthInPixels,
50 | this.map.heightInPixels
51 | );
52 | // make the camera follow the player
53 | this.cameras.main.startFollow(this.player);
54 |
55 | // set background color, so the sky is not black
56 | this.cameras.main.setBackgroundColor('#ccccff');
57 |
58 | this.anims.create({
59 | key: 'walk',
60 | frames: this.anims.generateFrameNames('player', { prefix: 'p1_walk', start: 1, end: 11, zeroPad: 2 }),
61 | frameRate: 10,
62 | repeat: -1
63 | });
64 |
65 | this.anims.create({
66 | key: 'idle',
67 | frames: [ { key: 'player', frame: 'p1_stand' } ],
68 | frameRate: 10
69 | });
70 | }
71 |
72 | update(): void {
73 | // if (this.cursors.left.isDown) {
74 | // this.player.setVelocityX(-200); // move left
75 | // this.player.anims.play('walk', true);
76 | // this.player.flipX= true; // flip the sprite to the left
77 | // }
78 | // else if (this.cursors.right.isDown) {
79 | // this.player.setVelocityX(200); // move right
80 | // this.player.anims.play('walk', true); // play walk animation
81 | // this.player.flipX = false;
82 | // }
83 |
84 | if (this.cursors.left.isDown)
85 | {
86 | this.player.setVelocityX(-200); // move left
87 | this.player.anims.play('walk', true); // play walk animation
88 | this.player.flipX= true; // flip the sprite to the left
89 | }
90 | else if (this.cursors.right.isDown)
91 | {
92 | this.player.setVelocityX(200); // move right
93 | this.player.anims.play('walk', true); // play walk animation
94 | this.player.flipX = false; // use the original sprite looking to the right
95 | } else {
96 | this.player.setVelocityX(0);
97 | this.player.anims.play('idle', true);
98 | }
99 |
100 | if (
101 | (this.cursors.space.isDown || this.cursors.up.isDown)
102 | // this.player.
103 | ) {
104 | this.player.setVelocityY(-500); // jump up
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-useless-escape */
2 | /* eslint-disable @typescript-eslint/no-var-requires */
3 | module.exports = {
4 | env: {
5 | browser: true,
6 | es2021: true,
7 | node: true
8 | },
9 | extends: [
10 | 'eslint:recommended',
11 | 'plugin:@typescript-eslint/recommended'
12 | ],
13 | overrides: [
14 | {
15 | files: [
16 | 'src/**/*.ts',
17 | 'src/**/*.tsx',
18 | 'src/**/*.js',
19 | 'src/**/*.jsx',
20 | 'src/**/*.cjs',
21 | 'src/**/*.mjs'
22 | ],
23 | rules: {
24 | '@typescript-eslint/naming-convention': [
25 | 'error',
26 | { selector: 'default', format: [ 'camelCase' ] },
27 |
28 | { selector: 'variableLike', format: [ 'camelCase' ] },
29 | { selector: 'variable', format: [ 'camelCase', 'UPPER_CASE' ] },
30 | { selector: 'parameter', format: [ 'camelCase' ], leadingUnderscore: 'allow' },
31 |
32 | { selector: 'memberLike', format: [ 'camelCase' ] },
33 | { selector: 'memberLike', modifiers: [ 'private' ], format: [ 'camelCase' ], leadingUnderscore: 'require' },
34 |
35 | { selector: 'typeLike', format: [ 'PascalCase' ] },
36 | { selector: 'typeParameter', format: [ 'PascalCase' ], prefix: [ 'T' ] },
37 |
38 | { selector: 'interface', format: [ 'PascalCase' ], custom: { regex: '^I[A-Z]', match: true } }
39 | ],
40 | 'simple-import-sort/imports': [
41 | 'error',
42 | {
43 | groups: [
44 | // 1. built-in node.js modules
45 | [ `^(${ require('module').builtinModules.join('|') })(/|$)` ],
46 | // 2.1. package that start without @
47 | // 2.2. package that start with @
48 | [ '^\\w', '^@\\w' ],
49 | // 3. @nestjs packages
50 | [ '^@nestjs\/' ],
51 | // 4. @kibibit packages
52 | [ '^@kibibit\/' ],
53 | // 5. Internal kibibit packages (inside this project)
54 | [ '^@kb-' ],
55 | // 6. Parent imports. Put `..` last.
56 | [ '^\\.\\.(?!/?$)', '^\\.\\./?$', '^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$' ],
57 | // 7. Side effect imports.
58 | [ '^\\u0000' ]
59 | ]
60 | }
61 | ],
62 | 'import/first': 'error',
63 | 'import/newline-after-import': 'error',
64 | 'import/no-duplicates': 'error',
65 | 'unused-imports/no-unused-imports': 'error'
66 | }
67 | },
68 | {
69 | files: [
70 | '*.ts',
71 | '*.tsx',
72 | '*.js',
73 | '*.jsx',
74 | '*.cjs',
75 | '*.mjs'
76 | ],
77 | rules: {
78 | '@typescript-eslint/explicit-function-return-type': 0,
79 | '@typescript-eslint/ban-ts-ignore': 0,
80 | '@typescript-eslint/member-delimiter-style': 0,
81 | '@typescript-eslint/no-explicit-any': 0,
82 | '@stylistic/js/semi': [ 'error', 'always' ],
83 | '@stylistic/js/quotes': [ 'error', 'single', { 'avoidEscape': true } ],
84 | '@stylistic/js/comma-dangle': [ 'error', 'never' ],
85 | '@stylistic/js/object-curly-spacing': [ 'error', 'always' ],
86 | '@stylistic/js/no-multi-spaces': 'error',
87 | '@stylistic/js/no-multiple-empty-lines': 'error',
88 | '@stylistic/js/array-bracket-spacing': [ 'error', 'always' ],
89 | '@stylistic/js/arrow-spacing': 'error',
90 | '@stylistic/js/block-spacing': 'error',
91 | '@stylistic/js/semi-spacing': 'error',
92 | '@stylistic/js/indent': [ 'error', 2 ],
93 | '@stylistic/js/template-curly-spacing': [ 'error', 'always' ],
94 | '@stylistic/js/max-len': [
95 | 'error', {
96 | 'code': 80,
97 | 'ignoreStrings': true,
98 | 'ignoreTemplateLiterals': true
99 | }
100 | ]
101 | }
102 | },
103 | {
104 | files: [ '*.html' ],
105 | parser: '@html-eslint/parser',
106 | extends: [ 'plugin:@html-eslint/recommended' ],
107 | rules: {
108 | '@html-eslint/attrs-newline': 'error',
109 | '@html-eslint/no-duplicate-attrs': 'error',
110 | '@html-eslint/indent': [ 'error', 2 ],
111 | '@html-eslint/lowercase': 'error',
112 | '@html-eslint/sort-attrs': [ 'error', {
113 | 'priority': [ 'id', 'name', 'content', 'type', 'class' ]
114 | } ],
115 | '@html-eslint/require-button-type': 'error',
116 | '@html-eslint/no-obsolete-tags': 'error',
117 | '@html-eslint/no-duplicate-id': 'error',
118 | '@html-eslint/no-positive-tabindex': 'error',
119 | '@html-eslint/require-open-graph-protocol': 'error'
120 | }
121 | }
122 | ],
123 | parser: '@typescript-eslint/parser',
124 | parserOptions: {
125 | ecmaVersion: 'latest',
126 | sourceType: 'module'
127 | },
128 | plugins: [
129 | '@typescript-eslint',
130 | '@stylistic/js',
131 | 'unused-imports',
132 | 'simple-import-sort',
133 | 'import',
134 | '@html-eslint'
135 | ]
136 | };
137 |
--------------------------------------------------------------------------------
/public/map.tmx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAAAAAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAA
77 |
78 |
79 |
80 |
81 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAAAAAAAAAAAAAAAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAAAAAAAAAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/public/map.json:
--------------------------------------------------------------------------------
1 | { "backgroundcolor":"#000000",
2 | "height":10,
3 | "infinite":false,
4 | "layers":[
5 | {
6 | "data":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAAAAAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAAAAAAAAAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAAAwAAAAMAAAADAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAABQAAAAUAAAAFAAAA",
7 | "encoding":"base64",
8 | "height":10,
9 | "name":"World",
10 | "opacity":1,
11 | "type":"tilelayer",
12 | "visible":true,
13 | "width":30,
14 | "x":0,
15 | "y":0
16 | },
17 | {
18 | "data":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAAAAAAAAAAAAAAAAABEAAAARAAAAEQAAABEAAAARAAAAEQAAAAAAAAAAAAAAEQAAABEAAAARAAAAEQAAABEAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
19 | "encoding":"base64",
20 | "height":10,
21 | "name":"Coins",
22 | "opacity":1,
23 | "type":"tilelayer",
24 | "visible":true,
25 | "width":30,
26 | "x":0,
27 | "y":0
28 | }],
29 | "nextobjectid":31,
30 | "orientation":"orthogonal",
31 | "renderorder":"right-down",
32 | "tiledversion":"2018.03.21",
33 | "tileheight":70,
34 | "tilesets":[
35 | {
36 | "columns":4,
37 | "firstgid":1,
38 | "image":"tiles.png",
39 | "imageheight":280,
40 | "imagewidth":280,
41 | "margin":0,
42 | "name":"tiles",
43 | "spacing":0,
44 | "tilecount":16,
45 | "tileheight":70,
46 | "tiles":[
47 | {
48 | "id":0,
49 | "properties":[
50 | {
51 | "name":"collides",
52 | "type":"bool",
53 | "value":true
54 | }]
55 | },
56 | {
57 | "id":1,
58 | "properties":[
59 | {
60 | "name":"collides",
61 | "type":"bool",
62 | "value":true
63 | }]
64 | },
65 | {
66 | "id":2,
67 | "properties":[
68 | {
69 | "name":"collides",
70 | "type":"bool",
71 | "value":true
72 | }]
73 | },
74 | {
75 | "id":3,
76 | "properties":[
77 | {
78 | "name":"collides",
79 | "type":"bool",
80 | "value":true
81 | }]
82 | },
83 | {
84 | "id":4,
85 | "properties":[
86 | {
87 | "name":"collides",
88 | "type":"bool",
89 | "value":true
90 | }]
91 | },
92 | {
93 | "id":5,
94 | "properties":[
95 | {
96 | "name":"collides",
97 | "type":"bool",
98 | "value":true
99 | }]
100 | },
101 | {
102 | "id":6,
103 | "properties":[
104 | {
105 | "name":"collides",
106 | "type":"bool",
107 | "value":true
108 | }]
109 | },
110 | {
111 | "id":8,
112 | "properties":[
113 | {
114 | "name":"collides",
115 | "type":"bool",
116 | "value":true
117 | }]
118 | },
119 | {
120 | "id":9,
121 | "properties":[
122 | {
123 | "name":"collides",
124 | "type":"bool",
125 | "value":true
126 | }]
127 | },
128 | {
129 | "id":10,
130 | "properties":[
131 | {
132 | "name":"collides",
133 | "type":"bool",
134 | "value":true
135 | }]
136 | },
137 | {
138 | "id":12,
139 | "properties":[
140 | {
141 | "name":"collides",
142 | "type":"bool",
143 | "value":true
144 | }]
145 | },
146 | {
147 | "id":13,
148 | "properties":[
149 | {
150 | "name":"collides",
151 | "type":"bool",
152 | "value":true
153 | }]
154 | },
155 | {
156 | "id":14,
157 | "properties":[
158 | {
159 | "name":"collides",
160 | "type":"bool",
161 | "value":true
162 | }]
163 | }],
164 | "tilewidth":70
165 | },
166 | {
167 | "columns":1,
168 | "firstgid":17,
169 | "image":"coinGold.png",
170 | "imageheight":70,
171 | "imagewidth":70,
172 | "margin":0,
173 | "name":"coin",
174 | "spacing":0,
175 | "tilecount":1,
176 | "tileheight":70,
177 | "tilewidth":70
178 | }],
179 | "tilewidth":70,
180 | "type":"map",
181 | "version":1.2,
182 | "width":30
183 | }
--------------------------------------------------------------------------------