├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── audio │ ├── bump.ogg │ ├── campfire.ogg │ ├── fountain.ogg │ ├── grass_step.ogg │ ├── ground_step.ogg │ ├── gs_sound_data.0017dd40.mid │ ├── gs_sound_data.0017dda0.mid │ ├── jump.ogg │ ├── jump2.ogg │ ├── new-bark-town.ogg │ ├── notice.ogg │ ├── run_step.ogg │ └── sea.ogg ├── fonts │ ├── advocut-webfont.eot │ ├── advocut-webfont.ttf │ └── advocut-webfont.woff ├── i18n │ ├── br.json │ ├── cn.json │ ├── da.json │ ├── de.json │ ├── en.json │ ├── fr.json │ └── nl.json └── img │ ├── 0.png │ ├── 0_normal.png │ ├── 1.png │ ├── 136.png │ ├── 151.png │ ├── 200.png │ ├── 3.png │ ├── 4.png │ ├── 4_normal.png │ ├── 85.png │ ├── 85_normal.png │ ├── chat │ ├── bleft.png │ ├── bright.png │ ├── fill.png │ ├── point.png │ ├── tleft.png │ └── tright.png │ ├── icons.png │ ├── light.png │ └── tilesets │ └── hgss_by_epicday.png ├── index.html ├── main.css ├── package.json ├── server ├── config.js ├── package.json └── src │ ├── GameServer.js │ ├── Instance.js │ ├── index.js │ └── polyfill.js ├── src ├── Engine │ ├── Audio │ │ └── index.js │ ├── Camera │ │ └── index.js │ ├── Commander │ │ └── index.js │ ├── Connection │ │ └── index.js │ ├── Controller │ │ ├── actions.js │ │ └── index.js │ ├── DisplayObject │ │ └── index.js │ ├── Editor │ │ ├── commands.js │ │ ├── index.js │ │ ├── render.js │ │ └── tileset.js │ ├── Entity │ │ ├── functions.js │ │ └── index.js │ ├── Environment │ │ ├── Evaluator │ │ │ └── index.js │ │ ├── Parser │ │ │ ├── NodeList.js │ │ │ ├── expression.js │ │ │ ├── index.js │ │ │ ├── parse.js │ │ │ └── precedence.js │ │ ├── Tester │ │ │ ├── index.js │ │ │ └── tests.js │ │ ├── Tokenizer │ │ │ ├── index.js │ │ │ └── tokens.js │ │ └── index.js │ ├── Input │ │ ├── Keyboard.js │ │ ├── Mouse.js │ │ └── index.js │ ├── Language │ │ └── index.js │ ├── Map │ │ ├── MapEntity.js │ │ ├── events.js │ │ ├── functions.js │ │ └── index.js │ ├── MiniMap │ │ └── index.js │ ├── Notification │ │ └── index.js │ ├── Path │ │ └── index.js │ ├── Renderer │ │ ├── debug.js │ │ ├── grid.js │ │ ├── index.js │ │ ├── render.js │ │ └── webgl │ │ │ ├── index.js │ │ │ └── shaders.js │ ├── Shadow │ │ └── index.js │ ├── Texture │ │ ├── effects.js │ │ └── index.js │ ├── index.js │ ├── logic.js │ ├── sound.js │ └── utils │ │ └── index.js ├── Game │ ├── entities │ │ ├── Light │ │ │ └── index.js │ │ ├── Monster.js │ │ ├── Player │ │ │ ├── face.js │ │ │ ├── follow.js │ │ │ ├── index.js │ │ │ ├── jump.js │ │ │ ├── new │ │ │ │ ├── face.js │ │ │ │ ├── index.js │ │ │ │ ├── jump.js │ │ │ │ ├── sound.js │ │ │ │ └── walk.js │ │ │ ├── sound.js │ │ │ └── walk.js │ │ └── index.js │ ├── index.js │ └── input.js ├── Math.js ├── Packets │ ├── Facing.js │ ├── Jumping.js │ ├── Position.js │ ├── Velocity.js │ └── index.js ├── cfg.js ├── libs │ ├── Howler.js │ ├── astar.js │ ├── seed.js │ └── wheel.js └── polyfill.js ├── stats.js ├── webpack.config.js └── worlds └── kanto ├── i18n ├── de.json └── en.json ├── index.js └── town ├── objects ├── building1.json ├── building1.png ├── building1_normal.png ├── campfire.json ├── campfire.png ├── campfire_normal.png ├── charizard.json ├── charizard.png ├── charizard_normal.png ├── door1.json ├── door1.png ├── flower.json ├── flower.png ├── flower_normal.png ├── grass.json ├── grass.png ├── lantern.json ├── lantern.png ├── lantern_normal.png ├── ping.json ├── ping.png ├── raindrop.json ├── raindrop.png ├── sign.json ├── sign.png ├── sign_normal.png ├── table.json ├── table.png ├── table_normal.png ├── tree.json ├── tree.png ├── tree_normal.png ├── water1.json └── water1.png ├── town.js └── town.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Line endings: enforce LF in GitHub, convert to native on checkout. 2 | 3 | * text=auto 4 | *.js text 5 | 6 | # Make GitHub ignore vendor libraries while computing language stats. 7 | # See https://github.com/github/linguist#overrides. 8 | 9 | /assets/* linguist-vendored=true 10 | /worlds/* linguist-vendored=true 11 | 12 | # Explicitly specify language for non-standard extensions used under 13 | # ide/web/lib/templates to make GitHub correctly count their language stats. 14 | # 15 | *.js_ linguist-language=JavaScript -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Thumbs.db 2 | Desktop.ini 3 | $RECYCLE.BIN/ 4 | .DS_Store 5 | *.bat 6 | *.sh 7 | 8 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | PokeMMO 2 | 2016-05-05 3 | Copyright (c) 2015 Felix Maier @github.com/maierfelix 4 | All rights reserved. 5 | 6 | Repository: https://github.com/maierfelix/PokeMMO 7 | 8 | The above notice and the following licenses applies to all parts of this software. 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | * The Software shall be used for Good, not Evil. 13 | 14 | * Redistributions of source code must retain the above copyright notice, this 15 | list of conditions and the following disclaimer. 16 | 17 | * Redistributions in binary form must reproduce the above copyright notice, 18 | this list of conditions and the following disclaimer in the documentation 19 | and/or other materials provided with the distribution. 20 | 21 | * You may not remove, edit or change any copyright notices, inside the source. 22 | Those conditions also applies to any established copyright notices inside the production build. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Pokémon Engine 2 | 3 | [![Join the chat at https://gitter.im/maierfelix/PokeMMO](https://badges.gitter.im/maierfelix/PokeMMO.svg)](https://gitter.im/maierfelix/PokeMMO?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | [Demo](http://maierfelix.github.io/PokeMMO)
6 | Runs best on Chrome.
7 | Uses the 2D canvas to render the editor mode and WebGL for the gameplay (unfinished).
8 | Graphics used in the demo are created by [EpicDay](http://epicday.deviantart.com/) and [Red_Ex](http://the-red-ex.deviantart.com/).
9 | Sounds are taken from [SoundBible](http://soundbible.com/). 10 | 11 | - Z: Action 12 | - X: Run 13 | - C: Jump 14 | - F1: Switch renderer (webgl/canvas) 15 | - F2: Edit mode 16 | - F3: Free camera mode (Press right mouse key to drag around) 17 | - F4: God mode 18 | 19 | Setup: 20 | ```` 21 | Client: 22 | npm install 23 | npm run watch 24 | 25 | Server: 26 | cd ./server 27 | npm install 28 | npm run start 29 | ```` 30 | 31 | - [ ] Engine 32 | - [x] Collisions 33 | - [x] Camera 34 | - [x] 3D Audio implementation 35 | - [x] Grid based path finding 36 | - [x] Maps 37 | - [ ] Map connections 38 | - [x] Dynamic multi-lingual support 39 | - [x] Mini map 40 | - [x] Pings 41 | - [ ] Notifications (Map name, dialog boxes etc) 42 | - [ ] Record/Replay Mode 43 | - [ ] Seed based animations 44 | - [x] Canvas renderer 45 | - [x] WebGL renderer 46 | - [ ] Normal map based lighting 47 | 48 | - [ ] Editor 49 | - [x] Undo & Redo 50 | - [x] Select, Copy, Paste, Cut, Delete (unstable) 51 | - [ ] Range map entity selections 52 | - [x] Map entities 53 | - [ ] Map entities settings 54 | - [x] Map entity add & edit support 55 | - [ ] Map entity collison box editor 56 | - [x] Draggable map entities 57 | - [ ] UI 58 | - [ ] Background map tile drawing 59 | - [ ] Background map collision tile drawing 60 | - [ ] Map connections 61 | - [ ] Map entity event code editor 62 | 63 | - [ ] Interpreter 64 | - [x] Syntax 65 | - [x] Global flags 66 | - [x] Expressions 67 | - [ ] Game api 68 | - [x] Frame based step script execution 69 | -------------------------------------------------------------------------------- /assets/audio/bump.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/bump.ogg -------------------------------------------------------------------------------- /assets/audio/campfire.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/campfire.ogg -------------------------------------------------------------------------------- /assets/audio/fountain.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/fountain.ogg -------------------------------------------------------------------------------- /assets/audio/grass_step.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/grass_step.ogg -------------------------------------------------------------------------------- /assets/audio/ground_step.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/ground_step.ogg -------------------------------------------------------------------------------- /assets/audio/gs_sound_data.0017dd40.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/gs_sound_data.0017dd40.mid -------------------------------------------------------------------------------- /assets/audio/gs_sound_data.0017dda0.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/gs_sound_data.0017dda0.mid -------------------------------------------------------------------------------- /assets/audio/jump.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/jump.ogg -------------------------------------------------------------------------------- /assets/audio/jump2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/jump2.ogg -------------------------------------------------------------------------------- /assets/audio/new-bark-town.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/new-bark-town.ogg -------------------------------------------------------------------------------- /assets/audio/notice.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/notice.ogg -------------------------------------------------------------------------------- /assets/audio/run_step.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/run_step.ogg -------------------------------------------------------------------------------- /assets/audio/sea.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/audio/sea.ogg -------------------------------------------------------------------------------- /assets/fonts/advocut-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/fonts/advocut-webfont.eot -------------------------------------------------------------------------------- /assets/fonts/advocut-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/fonts/advocut-webfont.ttf -------------------------------------------------------------------------------- /assets/fonts/advocut-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/fonts/advocut-webfont.woff -------------------------------------------------------------------------------- /assets/i18n/br.json: -------------------------------------------------------------------------------- 1 | { 2 | "s_BrowserNotSupported": "Navegador não suportado.", 3 | "s_days": [ 4 | "Domingo", 5 | "Segunda", 6 | "Terça", 7 | "Quarta", 8 | "Quinta", 9 | "Sexta", 10 | "Sábado" 11 | ], 12 | "s_months": [ 13 | "Janeiro", 14 | "Fevereiro", 15 | "Março", 16 | "Abril", 17 | "Maio", 18 | "Junho", 19 | "Julho", 20 | "Agosto", 21 | "Setembro", 22 | "Outubro", 23 | "Novembro", 24 | "Dezembro" 25 | ], 26 | "s_German": "Alemão", 27 | "s_English": "Inglês", 28 | "s_Settings": "Configurações", 29 | "s_Objects": "Objetos", 30 | "s_On": "Ligado", 31 | "s_Off": "Desligado", 32 | "s_ChangeLanguage": "Mudar linguagem" 33 | } 34 | -------------------------------------------------------------------------------- /assets/i18n/cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "s_BrowserNotSupported": "浏览器不支持.", 3 | "s_days": [ 4 | "星期天", 5 | "星期一", 6 | "星期二", 7 | "星期三", 8 | "星期四", 9 | "星期五", 10 | "星期六" 11 | ], 12 | "s_months": [ 13 | "一月", 14 | "二月", 15 | "三月", 16 | "四月", 17 | "五月", 18 | "六月", 19 | "七月", 20 | "八月", 21 | "九月", 22 | "十月", 23 | "十一月", 24 | "十二月" 25 | ], 26 | "s_German": "德语", 27 | "s_English": "英语", 28 | "s_Settings": "设置", 29 | "s_Objects": "物体", 30 | "s_On": "开", 31 | "s_Off": "关", 32 | "s_ChangeLanguage": "更换语言" 33 | } -------------------------------------------------------------------------------- /assets/i18n/da.json: -------------------------------------------------------------------------------- 1 | { 2 | "s_BrowserNotSupported": "Browser understøttes ikke.", 3 | "s_days": [ 4 | "Søndag", 5 | "Mandag", 6 | "Tirsdag", 7 | "Onsdag", 8 | "Torsdag", 9 | "Fredag", 10 | "Lørdag" 11 | ], 12 | "s_months": [ 13 | "Januar", 14 | "Februar", 15 | "Marts", 16 | "April", 17 | "Maj", 18 | "Juni", 19 | "Juli", 20 | "August", 21 | "September", 22 | "Oktober", 23 | "November", 24 | "December" 25 | ], 26 | "s_German": "Tysk", 27 | "s_English": "Engelsk", 28 | "s_Settings": "Indstillinger", 29 | "s_Objects": "Objekter", 30 | "s_On": "Tænd", 31 | "s_Off": "Sluk", 32 | "s_ChangeLanguage": "Skift sprog", 33 | } -------------------------------------------------------------------------------- /assets/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "s_BrowserNotSupported": "Browser nicht unterstützt.", 3 | "s_Days": [ 4 | "Sonntag", 5 | "Montag", 6 | "Dienstag", 7 | "Mittwoch", 8 | "Donnerstag", 9 | "Freitag", 10 | "Samstag" 11 | ], 12 | "s_Months": [ 13 | "Januar", 14 | "Februar", 15 | "März", 16 | "April", 17 | "Mai", 18 | "Juni", 19 | "Juli", 20 | "August", 21 | "September", 22 | "Oktober", 23 | "November", 24 | "Dezember" 25 | ], 26 | "s_German": "Deutsch", 27 | "s_English": "Englisch", 28 | "s_Settings": "Einstellungen", 29 | "s_Objects": "Objekte", 30 | "s_On": "An", 31 | "s_Off": "Aus", 32 | "s_ChangeLanguage": "Sprache wechseln", 33 | "s_Width": "Breite", 34 | "s_Height": "Höhe", 35 | "s_Dimension": "Dimension", 36 | "s_MS": "ms", 37 | "s_C": "C", 38 | "s_V": "V", 39 | "s_X": "X", 40 | "s_Y": "Y", 41 | "s_Z": "Z", 42 | "s_Delta": "Delta", 43 | "s_Scale": "Skalierung", 44 | "s_Entity": "Entität", 45 | "s_Entities": "Entitäten", 46 | "s_EntitiesInView": "Sichtbare Entitäten", 47 | "s_Textures": "Texturen", 48 | "s_Local": "Lokal", 49 | "s_CommandStack": "Kommandos", 50 | "s_GodMode": "Gott Modus", 51 | "s_EditMode": "Editier Modus", 52 | "s_DebugMode": "Debug Modus", 53 | "s_FreeCamera": "Freie Kamera", 54 | "s_Enabled": "Aktiviert", 55 | "s_Disabled": "Deaktiviert", 56 | "s_Jump": "Hüpfen", 57 | "s_Run": "Rennen", 58 | "s_Action": "Aktion", 59 | "s_CTRL": "STRG", 60 | "s_Undo": "Rückgängig", 61 | "s_Redo": "Wiederherstellen", 62 | "s_Copy": "Kopieren", 63 | "s_Paste": "Einfügen", 64 | "s_Cut": "Ausschneiden", 65 | "s_Space": "Leertaste", 66 | "s_FocusPlayer": "Spieler fokussieren", 67 | "s_FocusEntity": "Entität fokussieren", 68 | "s_Wheel": "Mausrad", 69 | "s_Zoom": "Vergrößern", 70 | "s_RightMouse": "Rechte Maus", 71 | "s_DoubleClick": "Doppelklick" 72 | } -------------------------------------------------------------------------------- /assets/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "s_BrowserNotSupported": "Browser not supported.", 3 | "s_days": [ 4 | "Sunday", 5 | "Monday", 6 | "Tuesday", 7 | "Wednesday", 8 | "Thursday", 9 | "Friday", 10 | "Saturday" 11 | ], 12 | "s_months": [ 13 | "January", 14 | "February", 15 | "March", 16 | "April", 17 | "May", 18 | "June", 19 | "July", 20 | "August", 21 | "September", 22 | "October", 23 | "November", 24 | "December" 25 | ], 26 | "s_German": "German", 27 | "s_English": "English", 28 | "s_Settings": "Settings", 29 | "s_Objects": "Objects", 30 | "s_On": "On", 31 | "s_Off": "Off", 32 | "s_ChangeLanguage": "Change language", 33 | "s_Width": "Width", 34 | "s_Height": "Height", 35 | "s_Dimension": "Dimension", 36 | "s_MS": "ms", 37 | "s_C": "C", 38 | "s_V": "V", 39 | "s_X": "X", 40 | "s_Y": "Y", 41 | "s_Z": "Z", 42 | "s_Delta": "Delta", 43 | "s_Scale": "Scale", 44 | "s_Entity": "Entity", 45 | "s_Entities": "Entities", 46 | "s_EntitiesInView": "Entities In View", 47 | "s_Textures": "Textures", 48 | "s_Local": "Local", 49 | "s_CommandStack": "Command Stack", 50 | "s_GodMode": "God Mode", 51 | "s_EditMode": "Edit Mode", 52 | "s_DebugMode": "Debug Mode", 53 | "s_FreeCamera": "Free Camera", 54 | "s_Enabled": "Enabled", 55 | "s_Disabled": "Disabled", 56 | "s_Jump": "Jump", 57 | "s_Run": "Run", 58 | "s_Action": "Action", 59 | "s_CTRL": "CTRL", 60 | "s_Undo": "Undo", 61 | "s_Redo": "Redo", 62 | "s_Copy": "Copy", 63 | "s_Paste": "Paste", 64 | "s_Cut": "Cut", 65 | "s_Space": "Space", 66 | "s_FocusPlayer": "Focus Player", 67 | "s_FocusEntity": "Focus Entity", 68 | "s_Wheel": "Wheel", 69 | "s_Zoom": "Zoom", 70 | "s_RightMouse": "RMouse", 71 | "s_DoubleClick": "Doubleclick" 72 | } -------------------------------------------------------------------------------- /assets/i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "s_BrowserNotSupported": "Navigateur non supporté", 3 | "s_days": [ 4 | "Dimanche", 5 | "Lundi", 6 | "Mardi", 7 | "Mercredi", 8 | "Jeudi", 9 | "Vendredi", 10 | "Samedi" 11 | ], 12 | "s_months": [ 13 | "Janvier", 14 | "Février", 15 | "Mars", 16 | "Avril", 17 | "Mai", 18 | "Juin", 19 | "Juillet", 20 | "Août", 21 | "Septembre", 22 | "Octobre", 23 | "Novembre", 24 | "Décembre" 25 | ], 26 | "s_German": "Allemand", 27 | "s_English": "Anglais", 28 | "s_Settings": "Paramètres", 29 | "s_Objects": "Objets", 30 | "s_On": "Activé", 31 | "s_Off": "Désactivé", 32 | "s_ChangeLanguage": "Changer la langue" 33 | } 34 | -------------------------------------------------------------------------------- /assets/i18n/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "s_BrowserNotSupported": "Uw browser word niet ondersteund.", 3 | "s_Days": [ 4 | "Zondag", 5 | "Maandag", 6 | "Dinsdag", 7 | "Woensdag", 8 | "Donderdag", 9 | "Vrijdag", 10 | "Zaterdag" 11 | ], 12 | "s_Months": [ 13 | "Januari", 14 | "Februari", 15 | "Maart", 16 | "April", 17 | "Mei", 18 | "Juni", 19 | "Juli", 20 | "Augustus", 21 | "September", 22 | "Oktober", 23 | "November", 24 | "December" 25 | ], 26 | "s_German": "Duits", 27 | "s_English": "Engels", 28 | "s_Settings": "Instellingen", 29 | "s_Objects": "Objecten", 30 | "s_On": "Aan", 31 | "s_Off": "Uit", 32 | "s_ChangeLanguage": "Taal Veranderen", 33 | "s_Width": "Breedte", 34 | "s_Height": "Hoogte", 35 | "s_Dimension": "Dimensie", 36 | "s_MS": "ms", 37 | "s_C": "C", 38 | "s_V": "V", 39 | "s_X": "X", 40 | "s_Y": "Y", 41 | "s_Z": "Z", 42 | "s_Delta": "Delta", 43 | "s_Scale": "Schaal", 44 | "s_Entity": "Entiteit", 45 | "s_Entities": "Entiteiten", 46 | "s_EntitiesInView": "Entiteiten op het scherm", 47 | "s_Textures": "Texturen", 48 | "s_Local": "Lokaal", 49 | "s_CommandStack": "Commandostapel", 50 | "s_GodMode": "God Mode", 51 | "s_EditMode": "Bewerkmodus", 52 | "s_DebugMode": "Foutopsporingsmodus", 53 | "s_FreeCamera": "Vrije Camera", 54 | "s_Enabled": "Ingeschakeld", 55 | "s_Disabled": "Uitgeschakeld", 56 | "s_Jump": "Springen", 57 | "s_Run": "Rennen", 58 | "s_Action": "Actie", 59 | "s_CTRL": "CTRL", 60 | "s_Undo": "Ongedaan maken" 61 | } -------------------------------------------------------------------------------- /assets/img/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/0.png -------------------------------------------------------------------------------- /assets/img/0_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/0_normal.png -------------------------------------------------------------------------------- /assets/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/1.png -------------------------------------------------------------------------------- /assets/img/136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/136.png -------------------------------------------------------------------------------- /assets/img/151.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/151.png -------------------------------------------------------------------------------- /assets/img/200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/200.png -------------------------------------------------------------------------------- /assets/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/3.png -------------------------------------------------------------------------------- /assets/img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/4.png -------------------------------------------------------------------------------- /assets/img/4_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/4_normal.png -------------------------------------------------------------------------------- /assets/img/85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/85.png -------------------------------------------------------------------------------- /assets/img/85_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/85_normal.png -------------------------------------------------------------------------------- /assets/img/chat/bleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/chat/bleft.png -------------------------------------------------------------------------------- /assets/img/chat/bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/chat/bright.png -------------------------------------------------------------------------------- /assets/img/chat/fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/chat/fill.png -------------------------------------------------------------------------------- /assets/img/chat/point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/chat/point.png -------------------------------------------------------------------------------- /assets/img/chat/tleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/chat/tleft.png -------------------------------------------------------------------------------- /assets/img/chat/tright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/chat/tright.png -------------------------------------------------------------------------------- /assets/img/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/icons.png -------------------------------------------------------------------------------- /assets/img/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/light.png -------------------------------------------------------------------------------- /assets/img/tilesets/hgss_by_epicday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/assets/img/tilesets/hgss_by_epicday.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 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 | Pokémon Engine 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /main.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | @font-face { 4 | font-family: 'AdvoCut'; 5 | src: url(./assets/fonts/advocut-webfont.eot); 6 | src: url(./assets/fonts/advocut-webfont.eot?#iefix) format('embedded-opentype'), 7 | url(./assets/fonts/advocut-webfont.woff) format('woff'), 8 | url(./assets/fonts/advocut-webfont.ttf) format('truetype'), 9 | url(./assets/fonts/advocut-webfont.svg#AndinaRegular) format('svg'); 10 | font-weight: normal; 11 | font-style: normal; 12 | } 13 | 14 | @-ms-viewport { 15 | width: device-width; 16 | } 17 | 18 | html { 19 | min-height:100%; 20 | background: #1f1f1f; 21 | } 22 | 23 | body { 24 | overflow:hidden; 25 | -webkit-user-select: none; 26 | -moz-user-select: none; 27 | margin:0; 28 | color: white; 29 | font-family: 'AdvoCut', sans-serif; 30 | text-align: center; 31 | cursor: default; 32 | } 33 | 34 | *:focus { 35 | outline:none; 36 | } 37 | 38 | *,*::before,*::after { 39 | -moz-box-sizing:border-box; 40 | box-sizing:border-box; 41 | } 42 | 43 | ::-webkit-scrollbar { 44 | width: 8px; 45 | height: 8px; 46 | background:transparent; 47 | } 48 | ::-webkit-scrollbar-thumb { 49 | background: rgba(255,255,255,0.12); 50 | border-radius: 14px; 51 | } 52 | ::-webkit-scrollbar-corner { 53 | background:transparent; 54 | } 55 | ::selection{ 56 | color:#EEE; 57 | background:#141414; 58 | } 59 | 60 | button, a { 61 | padding: 0; 62 | border: 1px solid transparent; 63 | background: none; 64 | outline: none; 65 | text-decoration: none; 66 | } 67 | 68 | #ui { 69 | z-index: 2; 70 | } 71 | 72 | #canvas { 73 | z-index: 1; 74 | } 75 | 76 | #webgl { 77 | z-index: 1; 78 | } 79 | 80 | canvas, img { 81 | position: absolute; 82 | z-index: 1; 83 | left: 0px; 84 | top: 0px; 85 | image-rendering: optimizeSpeed; 86 | image-rendering: -o-crisp-edges; 87 | image-rendering: -moz-crisp-edges; 88 | image-rendering: optimize-contrast; 89 | image-rendering: -webkit-optimize-contrast; 90 | image-rendering: pixelated; 91 | -ms-interpolation-mode: nearest-neighbor; 92 | -ms-touch-action: manipulation; 93 | touch-action: manipulation; 94 | transform-origin: 0% 0%; 95 | -ms-transform-origin: 0% 0%; 96 | -webkit-transform-origin: 0% 0%; 97 | -o-transform-origin: 0% 0%; 98 | -moz-transform-origin: 0% 0%; 99 | transform: scale(1, 1); 100 | -ms-transform: scale(1, 1); 101 | -webkit-transform: scale(1, 1); 102 | -o-transform: scale(1, 1); 103 | -moz-transform: scale(1, 1); 104 | -webkit-backface-visibility: hidden; 105 | -webkit-perspective: 1000; 106 | cursor: -moz-grab; 107 | cursor: -webkit-grab; 108 | cursor: grab; 109 | -webkit-touch-callout: none; 110 | -webkit-user-select: none; 111 | -khtml-user-select: none; 112 | -moz-user-select: none; 113 | -ms-user-select: none; 114 | user-select: none; 115 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pokiengine", 3 | "version": "0.0.2", 4 | "description": "Pokémon Engine", 5 | "scripts": { 6 | "watch": "webpack --watch" 7 | }, 8 | "devDependencies": { 9 | "babel-core": "^6.0.20", 10 | "babel-loader": "^6.0.1", 11 | "babel-plugin-transform-runtime": "^6.4.3", 12 | "babel-preset-es2015": "^6.24.1", 13 | "babel-preset-stage-0": "^6.3.13", 14 | "node-libs-browser": "^0.5.3", 15 | "webpack": "^1.12.2" 16 | }, 17 | "dependencies": { 18 | "babel-runtime": "^6.3.19", 19 | "json-stringify-safe": "^5.0.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Window title 3 | * @type {String} 4 | */ 5 | export let TITLE = "Server"; 6 | 7 | /** 8 | * Maximum connections 9 | * @type {Number} 10 | */ 11 | export let MAX_CONNECTIONS = 64; 12 | 13 | /** 14 | * Connection port 15 | * @constant 16 | * @type {Number} 17 | */ 18 | export const PORT = 449; 19 | 20 | /** 21 | * Server mode 22 | * @constant 23 | * @type {Number} 24 | */ 25 | export const MODE = 0; 26 | 27 | /** 28 | * Log level 29 | * @constant 30 | * @type {Number} 31 | */ 32 | export const LOG_LEVEL = 1; -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pokiengine", 3 | "version": "0.0.1", 4 | "description": "Pokémon Engine Server", 5 | "main": "src/index.js", 6 | "dependencies": { 7 | "fast-json-patch": "Starcounter-Jack/JSON-Patch", 8 | "msgpack-lite": "^0.1.13", 9 | "shortid": "^2.2.4", 10 | "es6-module-loader": "^0.17.11", 11 | "ws": "latest", 12 | "babel-runtime": "^6.3.19" 13 | }, 14 | "devDependencies": { 15 | "assert": "^1.3.0", 16 | "babel": "^6.5.2", 17 | "babel-cli": "^6.6.5", 18 | "babel-core": "^6.0.20", 19 | "babel-loader": "^6.0.1", 20 | "babel-plugin-transform-runtime": "^6.4.3", 21 | "babel-preset-stage-0": "^6.5.0", 22 | "benchmark": "^1.0.0", 23 | "express": "^4.13.3", 24 | "immutable": "^3.7.6", 25 | "immutable-diff": "^0.1.1", 26 | "mocha": "^2.3.4", 27 | "node-libs-browser": "^0.5.3", 28 | "nodemon": "^1.7.1" 29 | }, 30 | "scripts": { 31 | "start": "nodemon src/index.js --exec babel-node --presets es2015,stage-0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/src/GameServer.js: -------------------------------------------------------------------------------- 1 | import WebSocket from "ws"; 2 | import http from "http"; 3 | import fs from "fs"; 4 | 5 | import "./polyfill"; 6 | 7 | import Packet from "../../src/packets"; 8 | 9 | import { 10 | PORT 11 | } from "../config"; 12 | 13 | import { uHash } from "../../src/Engine/utils"; 14 | 15 | import Entity from "../../src/Engine/Entity"; 16 | 17 | import Instance from "./Instance"; 18 | 19 | import * as game_cfg from "../../src/cfg"; 20 | 21 | /** 22 | * GameServer 23 | * @class GameServer 24 | * @export 25 | */ 26 | export default class GameServer { 27 | 28 | /** 29 | * @constructor 30 | */ 31 | constructor() { 32 | 33 | game_cfg.IS_CLIENT = false; 34 | 35 | /** 36 | * Websocket instance 37 | * @type {Object} 38 | */ 39 | this.ws = null; 40 | 41 | /** 42 | * One frame tick 43 | * @type {Number} 44 | */ 45 | this.frame = 1000.0 / 60.0; 46 | 47 | /** 48 | * Now timestamp 49 | * @type {Number} 50 | */ 51 | this.now = 0; 52 | 53 | /** 54 | * Tick timer 55 | * @type {Number} 56 | */ 57 | this.tick = 0; 58 | 59 | /** 60 | * Now timestamp 61 | * @type {Number} 62 | */ 63 | this.then = 0; 64 | 65 | /** 66 | * Running state 67 | * @type {Boolean} 68 | */ 69 | this.running = false; 70 | 71 | /** 72 | * Users 73 | * @type {Array} 74 | */ 75 | this.users = []; 76 | 77 | /** 78 | * Interval instance 79 | * @type {Object} 80 | */ 81 | this.interval = null; 82 | 83 | this.init(); 84 | 85 | } 86 | 87 | /** 88 | * Intitialse a ws server 89 | */ 90 | init() { 91 | 92 | let options = { 93 | port: PORT, 94 | perMessageDeflate: false 95 | }; 96 | 97 | this.ws = new WebSocket.Server(options, this::this.onStart); 98 | 99 | this.ws.on('connection', this::this.onConnection); 100 | this.ws.on('error', this::this.onError); 101 | 102 | } 103 | 104 | /** 105 | * Start main loop 106 | */ 107 | startLoop() { 108 | clearInterval(this.interval); 109 | this.interval = setInterval(this::this.loop, this.frame); 110 | } 111 | 112 | /** 113 | * Stop main loop 114 | */ 115 | stopLoop() { 116 | clearInterval(this.interval); 117 | } 118 | 119 | /** 120 | * Start event 121 | * @param {Object} e 122 | */ 123 | onStart(e) { 124 | this.startLoop(); 125 | this.running = true; 126 | } 127 | 128 | /** 129 | * New client connected 130 | * @param {Object} socket 131 | */ 132 | onConnection(socket, req) { 133 | 134 | let ip = req.connection.remoteAddress; 135 | 136 | console.log(`${ip} joined!`); 137 | 138 | this.addUser(socket); 139 | 140 | } 141 | 142 | addUser(socket) { 143 | 144 | let entity = new Entity({}); 145 | let instance = new Instance(this, entity); 146 | 147 | entity.id = uHash(); 148 | entity.socket = socket; 149 | entity.instance = instance; 150 | 151 | var self = this; 152 | 153 | socket.on('message', instance::instance.onMessage); 154 | socket.on('close', function() { 155 | self.onClose(entity.id); 156 | }); 157 | 158 | this.users.push(entity); 159 | 160 | } 161 | 162 | /** 163 | * Message event 164 | * @param {Object} msg 165 | */ 166 | onMessage(msg) { 167 | //console.log(msg); 168 | } 169 | 170 | /** 171 | * Close event 172 | * @param {Number} id 173 | */ 174 | onClose(id) { 175 | 176 | let ii = 0; 177 | let length = this.users.length; 178 | 179 | for (; ii < length; ++ii) { 180 | if (this.users[ii].id === id) { 181 | this.users[ii].instance.kill(); 182 | this.users.splice(ii, 1); 183 | break; 184 | } 185 | }; 186 | 187 | } 188 | 189 | /** 190 | * Error event 191 | * @param {Object} e 192 | */ 193 | onError(e) { 194 | switch (e.code) { 195 | case "EADDRINUSE": 196 | console.log("[Error] Server could not bind to port! Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); 197 | break; 198 | case "EACCES": 199 | console.log("[Error] Please make sure you are running Ogar with root privileges."); 200 | break; 201 | default: 202 | console.log("[Error] Unhandled error code: " + e.code); 203 | break; 204 | }; 205 | process.exit(1); 206 | } 207 | 208 | /** 209 | * Share a message 210 | * @param {Object} msg 211 | * @param {String} name 212 | */ 213 | broadcastMessage(msg, name) { 214 | 215 | let ii = 0; 216 | let length = this.users.length; 217 | 218 | for (; ii < length; ++ii) { 219 | if (this.users[ii].name === name) continue; 220 | this.users[ii].socket.sendPacket(msg); 221 | }; 222 | 223 | } 224 | 225 | /** 226 | * Send message to single client 227 | * @param {Object} msg 228 | * @param {String} name 229 | */ 230 | sendMessageTo(msg, name) { 231 | 232 | let ii = 0; 233 | let length = this.users.length; 234 | 235 | for (; ii < length; ++ii) { 236 | if (this.users[ii].name !== name) continue; 237 | this.users[ii].socket.sendPacket(msg); 238 | }; 239 | 240 | } 241 | 242 | /** 243 | * Update timers 244 | */ 245 | update() { 246 | this.now = Date.now(); 247 | this.tick += (this.now - this.then); 248 | this.then = this.now; 249 | return void 0; 250 | } 251 | 252 | /** 253 | * Main loop 254 | */ 255 | loop() { 256 | 257 | this.update(); 258 | 259 | if (this.running === false) return void 0; 260 | 261 | if (this.tick < 25) return void 0; 262 | 263 | this.thread(this.moveTick); 264 | 265 | this.tick = 0; 266 | 267 | /* 268 | this.thread(this.spawnTick); 269 | this.thread(this.gamemodeTick); 270 | this.thread(this.cellUpdateTick); 271 | */ 272 | 273 | return void 0; 274 | 275 | } 276 | 277 | animateNPC() { 278 | 279 | let cfg = game_cfg; 280 | 281 | let entity = "Joy"; 282 | let move = [cfg.LEFT, cfg.RIGHT, cfg.UP, cfg.DOWN][(Math.random() * 3) << 0]; 283 | 284 | let ii = 0; 285 | let length = this.users.length; 286 | 287 | for (; ii < length; ++ii) { 288 | //this.users[ii].packetHandler.socket.sendPacket(new Packet.Position(1337, move)); 289 | }; 290 | 291 | } 292 | 293 | moveTick() { 294 | 295 | } 296 | 297 | /** 298 | * Thread based function execution 299 | * @param {Function} func 300 | */ 301 | thread(func) { 302 | setTimeout(this::func, 0); 303 | return void 0; 304 | } 305 | 306 | } -------------------------------------------------------------------------------- /server/src/Instance.js: -------------------------------------------------------------------------------- 1 | import Packet from "../../src/packets"; 2 | 3 | /** 4 | * Instance 5 | * @class Instance 6 | * @export 7 | */ 8 | export default class Instance { 9 | 10 | /** 11 | * @constructor 12 | * @param {Object} instance 13 | * @param {Object} entity 14 | */ 15 | constructor(instance, entity) { 16 | 17 | /** 18 | * Instance ref 19 | * @type {Object} 20 | */ 21 | this.instance = instance; 22 | 23 | /** 24 | * Entity ref 25 | * @type {Object} 26 | */ 27 | this.entity = entity; 28 | 29 | /** 30 | * Protocol 31 | * @type {Number} 32 | */ 33 | this.protocol = 0; 34 | 35 | } 36 | 37 | /** 38 | * Sto buffer 39 | * @param {Array} message 40 | * @return {Object} 41 | */ 42 | stobuf(buffer) { 43 | 44 | let ii = 0; 45 | let length = buffer.length; 46 | let arrayBuffer = new ArrayBuffer(length); 47 | let view = new Uint8Array(arrayBuffer); 48 | 49 | for (; ii < length; ++ii) { 50 | view[ii] = buffer[ii]; 51 | }; 52 | 53 | return (view.buffer); 54 | } 55 | 56 | invalidMessage(msg) { 57 | return ( 58 | msg !== void 0 && 59 | typeof msg === "string" || 60 | msg.length === 0 61 | ); 62 | } 63 | 64 | /** 65 | * Kill myself 66 | */ 67 | kill() { 68 | let data = this.getSTR(34, JSON.stringify({name: this.entity.name})); 69 | this.instance.broadcastMessage(data, this.entity.name); 70 | return void 0; 71 | } 72 | 73 | /** 74 | * Handle a message 75 | * @param {Array} msg 76 | */ 77 | onMessage(msg) { 78 | 79 | if (this.invalidMessage(msg) === true) return void 0; 80 | 81 | let buffer = this.stobuf(msg); 82 | let view = new DataView(buffer); 83 | let packetId = view.getUint8(0, true); 84 | 85 | /** Username */ 86 | if (packetId === 0) { 87 | let name = this.getString(view); 88 | this.entity.name = name; 89 | this.instance.broadcastMessage(this.buildEntityData(name, 160, 144, false), name); 90 | this.instance.sendMessageTo(this.buildEntityData(name, 160, 144, true), name); 91 | return void 0; 92 | } 93 | 94 | /** Jumping */ 95 | if (packetId === 30) { 96 | let id = view.getUint16(1, true); 97 | let data = this.getSTR(packetId, JSON.stringify({name: this.entity.name})); 98 | this.instance.broadcastMessage(data, this.entity.name); 99 | return void 0; 100 | } 101 | 102 | /** Facing */ 103 | if (packetId === 31) { 104 | let id = view.getUint16(1, true); 105 | let dir = view.getUint16(3, true); 106 | this.entity.facing = dir << 0; 107 | let data = this.getSTR(packetId, JSON.stringify({ name: this.entity.name, dir: dir })); 108 | this.instance.broadcastMessage(data, this.entity.name); 109 | return void 0; 110 | } 111 | 112 | /** Movement */ 113 | if (packetId === 32) { 114 | let id = view.getUint16(1, true); 115 | let dir = view.getUint16(3, true); 116 | let x = view.getUint16(5, true); 117 | let y = view.getUint16(7, true); 118 | this.entity.position.x = x << 0; 119 | this.entity.position.y = y << 0; 120 | let data = this.getSTR(packetId, JSON.stringify({ name: this.entity.name, dir: dir, x: x, y: y })); 121 | this.instance.broadcastMessage(data, this.entity.name); 122 | return void 0; 123 | } 124 | 125 | /** Velocity */ 126 | if (packetId === 33) { 127 | let id = view.getUint16(1, true); 128 | let velocity = view.getUint16(3, true); 129 | this.entity.velocity = Number(velocity); 130 | let data = this.getSTR(packetId, JSON.stringify({ name: this.entity.name, velocity: velocity })); 131 | this.instance.broadcastMessage(data, this.entity.name); 132 | return void 0; 133 | } 134 | 135 | } 136 | 137 | /** 138 | * Build entity data 139 | * @param {String} name 140 | * @param {Number} x 141 | * @param {Number} y 142 | * @param {Boolean} local 143 | * @return {Object} 144 | */ 145 | buildEntityData(name, x, y, local) { 146 | 147 | var options = { 148 | name: name, 149 | map: "Town", 150 | x: x, 151 | y: y, 152 | width: 16, 153 | height: 16, 154 | isLocalPlayer: local, 155 | sprite: "assets/img/0.png" 156 | }; 157 | 158 | let data = JSON.stringify(options); 159 | 160 | return (this.getSTR(22, data)); 161 | 162 | } 163 | 164 | getString(view) { 165 | 166 | if ((view.byteLength + 1) % 2 === 1) { 167 | return void 0; 168 | } 169 | 170 | var txt = ""; 171 | var maxLen = 32 * 2; 172 | for (var i = 1; i < view.byteLength && i <= maxLen; i += 2) { 173 | var charCode = view.getUint16(i, true); 174 | if (charCode == 0) { 175 | return void 0; 176 | } 177 | txt += String.fromCharCode(charCode); 178 | } 179 | return (txt); 180 | } 181 | 182 | getSTR(id, str) { 183 | 184 | var lb = [str]; 185 | var bufferSize = 5; 186 | var validElements = 0; 187 | 188 | // Get size of packet 189 | for (var i = 0; i < lb.length; i++) { 190 | if (typeof lb[i] == "undefined") { 191 | continue; 192 | } 193 | 194 | var item = lb[i]; 195 | bufferSize += 4; // Empty ID 196 | bufferSize += item.length * 2; // String length 197 | bufferSize += 2; // Name terminator 198 | 199 | validElements++; 200 | } 201 | 202 | var buf = new ArrayBuffer(bufferSize); 203 | var view = new DataView(buf); 204 | 205 | // Set packet data 206 | view.setUint8(0, id, true); // Packet ID 207 | view.setUint32(1, validElements, true); // Number of elements 208 | var offset = 5; 209 | 210 | // Loop through strings 211 | for (var i = 0; i < lb.length; i++) { 212 | if (typeof lb[i] == "undefined") { 213 | continue; 214 | } 215 | 216 | var item = lb[i]; 217 | 218 | view.setUint32(offset, 0, true); 219 | offset += 4; 220 | 221 | for (var j = 0; j < item.length; j++) { 222 | view.setUint16(offset, item.charCodeAt(j), true); 223 | offset += 2; 224 | } 225 | 226 | view.setUint16(offset, 0, true); 227 | offset += 2; 228 | } 229 | 230 | return buf; 231 | 232 | } 233 | 234 | } -------------------------------------------------------------------------------- /server/src/index.js: -------------------------------------------------------------------------------- 1 | import * as cfg from "../config"; 2 | 3 | import GameServer from "./GameServer"; 4 | 5 | let server = new GameServer(); 6 | 7 | process.title = cfg.TITLE; 8 | 9 | console.log("\x1b[32;1mStarting new server...\x1b[0m"); -------------------------------------------------------------------------------- /server/src/polyfill.js: -------------------------------------------------------------------------------- 1 | import WebSocket from "ws"; 2 | 3 | /** 4 | * Get buffer 5 | * @param {Object} packet 6 | */ 7 | WebSocket.prototype.getBuffer = function(data) { 8 | 9 | let ii = 0; 10 | let length = data.byteLength || data.length; 11 | 12 | let array = new Uint8Array(data.buffer || data); 13 | let offset = data.byteOffset || 0; 14 | let buffer = new Buffer(length); 15 | 16 | for (; ii < length; ++ii) { 17 | buffer[ii] = array[offset + ii]; 18 | }; 19 | 20 | return (buffer); 21 | 22 | }; 23 | 24 | /** 25 | * Send a packet object 26 | * @param {Object} packet 27 | */ 28 | WebSocket.prototype.sendPacket = function(packet) { 29 | 30 | if (this.readyState === WebSocket.OPEN && packet.build !== void 0) { 31 | this.send(this.getBuffer(packet.build()), { 32 | binary: true 33 | }); 34 | } else if (packet.build === void 0) { 35 | this.send(packet, { 36 | binary: true 37 | }); 38 | } 39 | 40 | return void 0; 41 | 42 | }; -------------------------------------------------------------------------------- /src/Engine/Audio/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Audio 3 | * @class Audio 4 | * @export 5 | */ 6 | class Audio { 7 | 8 | /** 9 | * @constructor 10 | */ 11 | constructor() { 12 | 13 | /** 14 | * Audio res path 15 | * @type {String} 16 | */ 17 | this.path = "assets/audio/"; 18 | 19 | /** 20 | * Noises 21 | * @type {Array} 22 | */ 23 | this.noises = []; 24 | 25 | } 26 | 27 | /** 28 | * Play a sound 29 | * @param {String} name 30 | * @param {Number} vol 31 | * @param {Number} x 32 | * @param {Number} y 33 | */ 34 | playSound(name, vol, x, y) { 35 | let path = this.path + `${name}.ogg`; 36 | var sound = new Howl({ 37 | urls: [path], 38 | autoplay: true, 39 | loop: false, 40 | pos3d: [x, y, vol / 1e3] 41 | }); 42 | } 43 | 44 | /** 45 | * Play a song 46 | * @param {String} name 47 | * @param {Number} vol 48 | * @param {Boolean} fadeIn 49 | */ 50 | playSong(name, vol, fadeIn) { 51 | vol = vol / 1e2; 52 | let path = this.path + `${name}.ogg`; 53 | var song = new Howl({ 54 | urls: [path], 55 | autoplay: true, 56 | loop: true, 57 | volume: fadeIn ? 0 : vol 58 | }); 59 | if (fadeIn) { 60 | song.fadeIn(vol, 2e3); 61 | } 62 | } 63 | 64 | /** 65 | * Play a noise 66 | * @param {String} name 67 | * @param {Number} vol 68 | * @param {Number} x 69 | * @param {Number} y 70 | * @return {Object} 71 | */ 72 | playNoise(name, vol, x, y) { 73 | let path = this.path + `${name}.ogg`; 74 | var noise = new Howl({ 75 | urls: [path], 76 | autoplay: true, 77 | loop: true, 78 | volume: vol / 1e2, 79 | pos3d: [x, y, vol / 1e3] 80 | }); 81 | this.noises.push(noise); 82 | /** This is for smooth out/in fading noise range area */ 83 | noise.isInView = true; 84 | noise.fadingIn = false; 85 | noise.fadingOut = false; 86 | return (noise); 87 | } 88 | 89 | } 90 | 91 | export default Audio = new Audio(); -------------------------------------------------------------------------------- /src/Engine/Commander/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Commander 3 | * @class Commander 4 | * @export 5 | */ 6 | export default class Commander { 7 | 8 | /** 9 | * @constructor 10 | */ 11 | constructor() { 12 | 13 | /** 14 | * Stack position 15 | * @type {Number} 16 | */ 17 | this.position = -1; 18 | 19 | /** 20 | * Command templates 21 | * @type {Object} 22 | */ 23 | this.commands = {}; 24 | 25 | /** 26 | * Command stack 27 | * @type {Array} 28 | */ 29 | this.stack = []; 30 | 31 | } 32 | 33 | /** 34 | * Register a new command 35 | * @param {Object} cmd 36 | */ 37 | newCommand(cmd) { 38 | this.commands[cmd.action] = cmd; 39 | cmd = null; 40 | } 41 | 42 | /** 43 | * Push a command 44 | * @param {String} action 45 | * @param {Object} scope 46 | * @param {Array} data 47 | */ 48 | push(action, scope, data) { 49 | 50 | let cmd = { 51 | action: action, 52 | data: data, 53 | scope: scope 54 | }; 55 | 56 | this.stack.splice(this.position + 1, this.stack.length); 57 | 58 | this.stack.push(cmd); 59 | 60 | this.redo(); 61 | this.undo(); 62 | this.redo(); 63 | this.undo(); 64 | this.redo(); 65 | 66 | } 67 | 68 | /** 69 | * Fire command 70 | * @param {Object} cmd 71 | * @param {String} action 72 | */ 73 | fire(cmd, action) { 74 | let template = this.commands[cmd.action][action]; 75 | template.bind(cmd.scope).apply(template, cmd.data); 76 | } 77 | 78 | /** 79 | * Get cmd from current stack index 80 | * @return {Object} 81 | */ 82 | getCurrentCmd() { 83 | return (this.stack[this.position]); 84 | } 85 | 86 | /** 87 | * Undo 88 | */ 89 | undo() { 90 | 91 | if (this.position >= 0) { 92 | this.fire(this.getCurrentCmd(), "onUndo"); 93 | this.position--; 94 | } 95 | 96 | } 97 | 98 | /** 99 | * Redo 100 | */ 101 | redo() { 102 | 103 | if (this.position < this.stack.length - 1) { 104 | this.position++; 105 | this.fire(this.getCurrentCmd(), "onRedo"); 106 | } 107 | 108 | } 109 | 110 | } -------------------------------------------------------------------------------- /src/Engine/Controller/index.js: -------------------------------------------------------------------------------- 1 | import { DIMENSION } from "../../cfg"; 2 | 3 | import * as actions from "./actions"; 4 | 5 | /** 6 | * Controller 7 | * @class Controller 8 | * @export 9 | */ 10 | export default class Controller { 11 | 12 | /** 13 | * @constructor 14 | * @param {Object} instance 15 | */ 16 | constructor(instance) { 17 | 18 | /** 19 | * Instance 20 | * @type {Object} 21 | */ 22 | this.instance = instance; 23 | 24 | /** 25 | * Engine ref 26 | * @type {Object} 27 | */ 28 | this.engine = this.instance; 29 | 30 | /** 31 | * Actions ref 32 | * @type {Object} 33 | */ 34 | this.actions = actions.actions; 35 | 36 | /** 37 | * Log array 38 | * @type {Array} 39 | */ 40 | this.logs = []; 41 | 42 | } 43 | 44 | /** 45 | * Execute a action 46 | * @param {String} name 47 | * @param {Array} args 48 | */ 49 | action(name, args = args || []) { 50 | 51 | let cmd = this.actions[name]; 52 | let rule = this.engine.instance::cmd.rule() === true; 53 | 54 | if (rule === true) { 55 | cmd.action.bind(this.engine.instance).apply(this, args); 56 | } 57 | 58 | if (cmd.log !== false) this.log(name, rule); 59 | 60 | return void 0; 61 | 62 | } 63 | 64 | /** 65 | * Log a action 66 | * @param {String} name 67 | */ 68 | log(name, failed) { 69 | 70 | this.logs.push({ 71 | name: name, 72 | success: failed, 73 | timestamp: this.instance.renderer.now 74 | }); 75 | 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /src/Engine/DisplayObject/index.js: -------------------------------------------------------------------------------- 1 | import { uHash } from "../utils"; 2 | import math from "../../Math"; 3 | 4 | /** 5 | * Display object 6 | * @class DisplayObject 7 | * @export 8 | */ 9 | export default class DisplayObject { 10 | 11 | /** 12 | * @constructor 13 | * @param {Number} width 14 | * @param {Number} height 15 | */ 16 | constructor(x, y, width, height) { 17 | 18 | /** 19 | * Unique id 20 | * @type {Number} 21 | */ 22 | this.id = uHash(); 23 | 24 | /** 25 | * Position 26 | * @type {Object} 27 | */ 28 | this.position = new math.Point( 29 | x !== void 0 ? x : 0, 30 | y !== void 0 ? y : 0 31 | ); 32 | 33 | /** 34 | * Size 35 | * @type {Object} 36 | */ 37 | this.size = new math.Point( 38 | width !== void 0 ? width : 0, 39 | height !== void 0 ? height : 0 40 | ); 41 | 42 | /** 43 | * Scale factor 44 | * @type {Object} 45 | */ 46 | this.scale = new math.Point(1, 1); 47 | 48 | /** 49 | * X 50 | * @type {Number} 51 | * @getter 52 | * @setter 53 | */ 54 | Object.defineProperty(this, "x", { 55 | get: function() { 56 | return (this.position.x); 57 | }, 58 | set: function(value) { 59 | this.position.x = value; 60 | }, 61 | configurable: true, 62 | enumerable: true 63 | }); 64 | 65 | /** 66 | * Y 67 | * @type {Number} 68 | * @getter 69 | * @setter 70 | */ 71 | Object.defineProperty(this, "y", { 72 | get: function() { 73 | return (this.position.y); 74 | }, 75 | set: function(value) { 76 | this.position.y = value; 77 | }, 78 | configurable: true, 79 | enumerable: true 80 | }); 81 | 82 | /** 83 | * Width 84 | * @type {Number} 85 | * @getter 86 | * @setter 87 | */ 88 | Object.defineProperty(this, "width", { 89 | get: function() { 90 | return (this.size.x); 91 | }, 92 | set: function(value) { 93 | this.size.x = value; 94 | }, 95 | configurable: true, 96 | enumerable: true 97 | }); 98 | 99 | /** 100 | * Height 101 | * @type {Number} 102 | * @getter 103 | * @setter 104 | */ 105 | Object.defineProperty(this, "height", { 106 | get: function() { 107 | return (this.size.y); 108 | }, 109 | set: function(value) { 110 | this.size.y = value; 111 | }, 112 | configurable: true, 113 | enumerable: true 114 | }); 115 | 116 | } 117 | 118 | } -------------------------------------------------------------------------------- /src/Engine/Editor/commands.js: -------------------------------------------------------------------------------- 1 | import { 2 | Y_DEPTH_HACK, 3 | DIMENSION 4 | } from "../../cfg"; 5 | 6 | export let commands = [ 7 | /** Select command */ 8 | { 9 | action: "select", 10 | onUndo: function(entity, selection) { 11 | this.entitySelection = null; 12 | this.entitySelection = selection; 13 | }, 14 | onRedo: function(entity, selection) { 15 | this.entitySelection = null; 16 | this.entitySelection = entity; 17 | } 18 | }, 19 | /** Drag command */ 20 | { 21 | action: "drag", 22 | onUndo: function(x, y) { 23 | this.x -= x; 24 | this.y -= y; 25 | this.y <<= 0; 26 | this.y += Y_DEPTH_HACK; 27 | this.last.x = this.x; 28 | this.last.y = this.y; 29 | }, 30 | onRedo: function(x, y) { 31 | this.x += x; 32 | this.y += y; 33 | this.y <<= 0; 34 | this.y += Y_DEPTH_HACK; 35 | this.last.x = this.x; 36 | this.last.y = this.y; 37 | } 38 | }, 39 | /** Delete command */ 40 | { 41 | action: "delete", 42 | onUndo: function(entity) { 43 | this.instance.addEntity(entity); 44 | this.entitySelection = entity; 45 | }, 46 | onRedo: function(entity) { 47 | this.instance.removeEntity(entity); 48 | this.entitySelection = null; 49 | } 50 | }, 51 | /** Cut command */ 52 | { 53 | action: "cut", 54 | onUndo: function(entity) { 55 | this.instance.editor.pasteEntity(); 56 | }, 57 | onRedo: function(entity) { 58 | this.instance.editor.copyEntity(); 59 | this.instance.editor.deleteEntity(); 60 | } 61 | }, 62 | /** Copy command */ 63 | { 64 | action: "copy", 65 | onUndo: function(entity, copy) { 66 | this.entityCopy = copy; 67 | this.entitySelection = copy; 68 | }, 69 | onRedo: function(entity, copy) { 70 | this.entityCopy = entity; 71 | this.entitySelection = entity; 72 | } 73 | }, 74 | /** Paste command */ 75 | { 76 | action: "paste", 77 | onUndo: function (entity, paste) { 78 | this.instance.removeEntity(paste); 79 | }, 80 | onRedo: function(entity, paste) { 81 | 82 | let map = this.map; 83 | 84 | if (paste !== null && paste !== void 0) { 85 | map.instance.addEntity(paste); 86 | return void 0; 87 | } 88 | 89 | let clone = this.instance.cloneEntity(entity); 90 | 91 | /** Fuck that */ 92 | this.instance.editor.commander.stack[this.instance.editor.commander.position].data[1] = clone; 93 | 94 | map.instance.addEntity(clone); 95 | 96 | } 97 | } 98 | ]; -------------------------------------------------------------------------------- /src/Engine/Editor/render.js: -------------------------------------------------------------------------------- 1 | import { 2 | DIMENSION 3 | } from "../../cfg"; 4 | 5 | import math from "../../Math"; 6 | 7 | /** 8 | * Edit mode 9 | */ 10 | export function renderEditorMode() { 11 | 12 | this.renderSelection(); 13 | 14 | if (this.instance.editor.STATES.SELECTING === true) { 15 | //this.renderSelectedEntities(); 16 | } 17 | 18 | this.renderEntitySelection(); 19 | 20 | } 21 | 22 | /** 23 | * Render selected entities 24 | */ 25 | export function renderSelectedEntities() { 26 | 27 | let ii = 0; 28 | let length = 0; 29 | 30 | let entity = null; 31 | let entities = this.instance.editor.selectedEntities; 32 | 33 | length = entities.length; 34 | 35 | let resolution = 0; 36 | 37 | let x = 0; 38 | let y = 0; 39 | 40 | let width = 0; 41 | let height = 0; 42 | 43 | for (; ii < length; ++ii) { 44 | 45 | entity = entities[ii]; 46 | 47 | resolution = this.camera.resolution; 48 | 49 | x = (this.camera.x + (entity.position.x + entity.xMargin) * resolution) << 0; 50 | y = (this.camera.y + (entity.position.y + entity.yMargin + entity.z) * resolution) << 0; 51 | 52 | width = (entity.size.x * resolution) << 0; 53 | height = (entity.size.y * resolution) << 0; 54 | 55 | this.context.beginPath(); 56 | 57 | this.context.strokeStyle = "red"; 58 | this.context.lineWidth = (resolution / 2) << 0; 59 | this.context.strokeRect( 60 | x, y, 61 | width, height 62 | ); 63 | this.context.stroke(); 64 | 65 | this.context.closePath(); 66 | 67 | }; 68 | 69 | } 70 | 71 | /** 72 | * Render selection 73 | */ 74 | export function renderSelection() { 75 | 76 | if (this.instance.editor.STATES.SELECTING === false) return void 0; 77 | 78 | let selection = this.instance.editor.selection; 79 | 80 | let resolution = this.camera.resolution; 81 | 82 | let x = (this.camera.x + selection.x1 * resolution) << 0; 83 | let y = (this.camera.y + selection.y1 * resolution) << 0; 84 | 85 | let width = ((selection.x2 - selection.x1) * resolution) << 0; 86 | let height = ((selection.y2 - selection.y1) * resolution) << 0; 87 | 88 | this.context.beginPath(); 89 | 90 | this.context.strokeStyle = "red"; 91 | this.context.lineWidth = (resolution / 2) << 0; 92 | this.context.strokeRect( 93 | x, y, 94 | width, height 95 | ); 96 | this.context.stroke(); 97 | 98 | this.context.closePath(); 99 | 100 | return void 0; 101 | 102 | } 103 | 104 | /** 105 | * Render entity selection 106 | */ 107 | export function renderEntitySelection() { 108 | 109 | let entity = this.instance.editor.entitySelection; 110 | 111 | if (entity === null) return void 0; 112 | 113 | if (this.camera.isInView( 114 | entity.position.x, entity.position.y, 115 | entity.size.x * entity.scale, entity.size.y * entity.scale 116 | ) === false) return void 0; 117 | if (entity.opacity === .0) return void 0; 118 | if (entity.texture === null) return void 0; 119 | 120 | let resolution = this.camera.resolution; 121 | 122 | let x = (this.camera.x + (entity.position.x + entity.xMargin) * resolution) << 0; 123 | let y = (this.camera.y + (entity.position.y + entity.yMargin + entity.z) * resolution) << 0; 124 | 125 | let width = ((entity.size.x * entity.scale) * resolution) << 0; 126 | let height = ((entity.size.y * entity.scale) * resolution) << 0; 127 | 128 | if (entity.noise !== null) { 129 | this.renderEntityNoise(entity, x, y, width, height); 130 | } 131 | 132 | this.context.beginPath(); 133 | 134 | this.context.strokeStyle = "red"; 135 | this.context.lineWidth = (resolution / 2) << 0; 136 | this.context.strokeRect( 137 | x, y, 138 | width, height 139 | ); 140 | this.context.stroke(); 141 | 142 | this.context.closePath(); 143 | 144 | this.renderSelectionText(entity, x, y); 145 | 146 | this.context.globalAlpha = .25; 147 | 148 | if (entity.collidable === true) { 149 | if (entity.collisionBox.length > 0) { 150 | this.renderEntityCollisionBox(entity, x, y); 151 | } else { 152 | this.context.fillStyle = "red"; 153 | this.context.fillRect( 154 | x, y, 155 | width, height 156 | ); 157 | this.context.fill(); 158 | } 159 | } 160 | 161 | this.context.globalAlpha = 1.0; 162 | 163 | return void 0; 164 | 165 | } 166 | 167 | /** 168 | * Render entity noise radius 169 | * @param {Object} entity 170 | * @param {Number} x 171 | * @param {Number} y 172 | * @param {Number} width 173 | * @param {Number} height 174 | */ 175 | export function renderEntityNoise(entity, x, y, width, height) { 176 | 177 | let resolution = this.camera.resolution; 178 | 179 | let radius = ((entity.noiseRadius - DIMENSION) || DIMENSION) * resolution; 180 | 181 | x += width / 2; 182 | y += height / 2; 183 | 184 | this.context.globalAlpha = .2; 185 | 186 | this.context.beginPath(); 187 | 188 | this.context.fillStyle = "green"; 189 | this.context.lineWidth = (resolution / 2) << 0; 190 | this.context.arc(x, y, radius, 0, 2 * Math.PI, false); 191 | this.context.fill(); 192 | 193 | this.context.closePath(); 194 | 195 | this.context.globalAlpha = 1.0; 196 | 197 | return void 0; 198 | 199 | } 200 | 201 | /** 202 | * Render entity collision box 203 | * @param {Object} entity 204 | * @param {Number} x 205 | * @param {Number} y 206 | */ 207 | export function renderEntityCollisionBox(entity, x, y) { 208 | 209 | let collision = entity.collisionBox; 210 | 211 | let resolution = this.camera.resolution; 212 | 213 | let tile = 0; 214 | 215 | let ii = 0; 216 | 217 | let xx = 0; 218 | let yy = 0; 219 | 220 | let dim = DIMENSION * entity.scale * resolution; 221 | 222 | let width = (entity.width) / DIMENSION; 223 | let height = (entity.height) / DIMENSION; 224 | 225 | let length = width * height; 226 | 227 | for (; ii < length; ++ii) { 228 | tile = collision[yy + xx]; 229 | if (tile === 1) { 230 | this.context.fillStyle = "red"; 231 | this.context.fillRect( 232 | x + (xx * dim), 233 | y + ((yy / width) * dim), 234 | dim, dim 235 | ); 236 | this.context.fill(); 237 | } 238 | ++xx; 239 | if (xx >= width) { 240 | yy += width; 241 | xx = 0; 242 | } 243 | }; 244 | 245 | return void 0; 246 | 247 | } 248 | 249 | /** 250 | * Render entity selection text 251 | * @param {Object} entity 252 | * @param {Number} x 253 | * @param {Number} y 254 | */ 255 | export function renderSelectionText(entity, x, y) { 256 | 257 | let resolution = this.camera.resolution; 258 | 259 | let color = "red"; 260 | 261 | let ln = .5 * resolution; 262 | let size = 2.5 * resolution; 263 | 264 | let xx = x; 265 | let yy = y - (ln * 1.25) - size; 266 | 267 | let decimals = 1; 268 | 269 | let txtX = `X: ${entity.position.x.toFixed(decimals)}`; 270 | let txtY = `Y: ${entity.position.y.toFixed(decimals)}`; 271 | 272 | this.instance.renderer.drawPixelText( 273 | txtX, 274 | xx, yy, 275 | size, ln, 276 | color 277 | ); 278 | 279 | this.instance.renderer.drawPixelText( 280 | txtY, 281 | xx, yy += size, 282 | size, ln, 283 | color 284 | ); 285 | 286 | return void 0; 287 | 288 | } -------------------------------------------------------------------------------- /src/Engine/Editor/tileset.js: -------------------------------------------------------------------------------- 1 | import { 2 | FREE_CAMERA, 3 | Y_DEPTH_HACK, 4 | DIMENSION, 5 | MIN_SCALE, MAX_SCALE, 6 | PIXEL_SCALE 7 | } from "../../cfg"; 8 | 9 | import { tileContainsImageData } from "../utils"; 10 | 11 | export function updateTilesetPosition() { 12 | if (this.instance.currentMap !== null) { 13 | let width = this.instance.currentMap.texture.width; 14 | let height = this.instance.currentMap.texture.height; 15 | this.tileset.x = DIMENSION + this.instance.width - width; 16 | this.tileset.y = 0; 17 | } 18 | } 19 | 20 | /** 21 | * @param {Number} x 22 | * @param {Number} y 23 | */ 24 | export function clickedInsideTileset(x, y) { 25 | let tileX = this.instance.editor.tileset.x; 26 | let tileY = this.instance.editor.tileset.y; 27 | let tileWidth = this.instance.currentMap.texture.width; 28 | let tileHeight = this.instance.currentMap.texture.height; 29 | return ( 30 | x >= tileX && x <= tileX + tileWidth && 31 | y >= tileY && y <= tileY + tileHeight 32 | ); 33 | } 34 | 35 | /** 36 | * @param {Number} x 37 | * @param {Number} y 38 | */ 39 | export function selectTile(x, y) { 40 | 41 | console.log(x, y); 42 | 43 | } -------------------------------------------------------------------------------- /src/Engine/Entity/functions.js: -------------------------------------------------------------------------------- 1 | import { 2 | VOLUME 3 | } from "../../cfg"; 4 | 5 | import MapEntity from "../Map/MapEntity"; 6 | 7 | import Entity from "./index"; 8 | 9 | /** 10 | * Add a new entity 11 | * @param {Object} entity 12 | * @export 13 | */ 14 | export function addEntity(entity) { 15 | 16 | if (entity.isLocalPlayer) { 17 | entity.instance = this.instance; 18 | this.localEntity = entity; 19 | } 20 | 21 | if (entity.customOpacity() === false) { 22 | entity.fadeIn(1); 23 | } 24 | 25 | if (entity.noise) { 26 | if (entity.noise._audioNode !== void 0) { 27 | entity.noise._audioNode[0].gain.linearRampToValueAtTime(VOLUME.ENTITY_NOISE / 1e2, entity.noise._audioNode[0].context.currentTime); 28 | } 29 | } 30 | 31 | this.currentMap.entities.push(entity); 32 | 33 | } 34 | 35 | /** 36 | * Clone a entity 37 | * @param {Object} entity 38 | * @return {Object} 39 | */ 40 | export function cloneEntity(entity) { 41 | 42 | let entities = this.instance.entities; 43 | 44 | let map = this.currentMap; 45 | 46 | let clone = null; 47 | let tmp = null; 48 | 49 | if (entity instanceof entities.Player) { 50 | tmp = new entities.Player({ 51 | name: "undefined", 52 | map: entity.map, 53 | x: entity.x, y: entity.y, 54 | zIndex: entity.zIndex, 55 | sprite: entity.sprite, 56 | width: entity.width, height: entity.height, 57 | isLocalPlayer: false, 58 | collidable: entity.collidable, 59 | shadow: entity.hasShadow 60 | }); 61 | if (entity.instance) { 62 | tmp.instance = entity.instance; 63 | } 64 | if (tmp.hasShadow) { 65 | tmp.shadow.x = entity.shadow.x; 66 | tmp.shadow.y = entity.shadow.y; 67 | } 68 | } 69 | else if (entity instanceof MapEntity) { 70 | tmp = map.objectTemplates[entity.name.toLowerCase()]; 71 | } else { 72 | return void 0; 73 | } 74 | 75 | tmp.x = entity.x; 76 | tmp.y = entity.y; 77 | tmp.z = entity.z; 78 | 79 | if (entity instanceof MapEntity) { 80 | clone = new MapEntity(entity); 81 | if (entity.noise) { 82 | clone.noiseSrcPath = entity.noiseSrcPath; 83 | clone.noise = clone.noiseSrcPath; 84 | } 85 | if (entity.normal) { 86 | clone.normal = entity.normal; 87 | } 88 | } else { 89 | clone = tmp; 90 | } 91 | 92 | return (clone); 93 | 94 | } 95 | 96 | /** 97 | * Get a entity by its 98 | * matching property 99 | * @param {*} key 100 | * @param {String} prop 101 | * @return {Number} 102 | */ 103 | export function getEntityByProperty(key, prop) { 104 | 105 | let ii = 0; 106 | let length = 0; 107 | 108 | length = this.currentMap.entities.length; 109 | 110 | for (; ii < length; ++ii) { 111 | if (this.currentMap.entities[ii][prop] === key) { 112 | return (this.currentMap.entities[ii]); 113 | } 114 | }; 115 | 116 | return (-1); 117 | 118 | } 119 | 120 | /** 121 | * Remove a entity 122 | * @param {Object} entity 123 | */ 124 | export function removeEntity(entity) { 125 | 126 | let noiseEntity = null; 127 | 128 | /** Clear entity selection */ 129 | if ( 130 | this.editor.entitySelection !== null && 131 | entity.id === this.editor.entitySelection.id 132 | ) { 133 | this.editor.entitySelection = null; 134 | } 135 | 136 | if (entity.noise) { 137 | if (entity.noise._audioNode !== void 0) { 138 | entity.noise._audioNode[0].gain.linearRampToValueAtTime(.0, entity.noise._audioNode[0].context.currentTime); 139 | } 140 | } 141 | 142 | this.removeEntityFromArray(entity, this.currentMap.entities); 143 | 144 | } 145 | 146 | /** 147 | * Remove a entity from an array 148 | * @param {Object} entity 149 | * @param {Array} entities 150 | * @return {Object} 151 | */ 152 | export function removeEntityFromArray(entity, array) { 153 | 154 | let ii = 0; 155 | let length = 0; 156 | 157 | let id = entity.id; 158 | 159 | let cache = null; 160 | 161 | length = array.length; 162 | 163 | for (; ii < length; ++ii) { 164 | if (array[ii].id === id) { 165 | cache = array[ii]; 166 | array[ii] = null; 167 | array.splice(ii, 1); 168 | return (cache); 169 | } 170 | }; 171 | 172 | return void 0; 173 | 174 | } 175 | 176 | /** 177 | * Get a entity 178 | * @param {Number} id 179 | * @return {Number} 180 | */ 181 | export function getEntityById(id) { 182 | 183 | let property = "id"; 184 | 185 | let index = 0; 186 | 187 | return (this.getEntityByProperty(id, property)); 188 | 189 | } 190 | 191 | /** 192 | * Remove a entity by its id 193 | * @param {Number} id 194 | */ 195 | export function removeEntityById(id) { 196 | 197 | let entity = null; 198 | 199 | if ((entity = this.getEntityByProperty(id, property)) === void 0) return void 0; 200 | 201 | this.removeEntity(entity); 202 | 203 | } -------------------------------------------------------------------------------- /src/Engine/Environment/Parser/NodeList.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Numeric node types 3 | * @type {Object} 4 | */ 5 | export let NODE_TYPES = { 6 | Program: 1, 7 | BlockStatement: 2, 8 | ReturnStatement: 3, 9 | Literal: 4, 10 | Identifier: 5, 11 | IfStatement: 6, 12 | BinaryExpression: 7, 13 | UnaryExpression: 8, 14 | AsyncStatement: 9, 15 | MemberExpression: 10, 16 | CallExpression: 11, 17 | AssignmentExpression: 12 18 | }; 19 | 20 | /** 21 | * NODE_LIST 22 | * @class NODE_LIST 23 | * @export 24 | */ 25 | export default class NODE_LIST { 26 | 27 | constructor() {} 28 | 29 | static get Program() { 30 | return ( 31 | class Program { 32 | constructor() { 33 | this.type = NODE_TYPES.Program; 34 | this.body = []; 35 | } 36 | } 37 | ); 38 | } 39 | 40 | static get BlockStatement() { 41 | return ( 42 | class BlockStatement { 43 | constructor() { 44 | this.type = NODE_TYPES.BlockStatement; 45 | this.body = []; 46 | } 47 | } 48 | ); 49 | } 50 | 51 | static get ReturnStatement() { 52 | return ( 53 | class ReturnStatement { 54 | constructor() { 55 | this.type = NODE_TYPES.ReturnStatement; 56 | this.value = null; 57 | } 58 | } 59 | ); 60 | } 61 | 62 | static get Literal() { 63 | return ( 64 | class Literal { 65 | constructor() { 66 | this.type = NODE_TYPES.Literal; 67 | this.name = null; 68 | this.value = null; 69 | } 70 | } 71 | ); 72 | } 73 | 74 | static get Identifier() { 75 | return ( 76 | class Identifier { 77 | constructor() { 78 | this.type = NODE_TYPES.Identifier; 79 | this.name = null; 80 | } 81 | } 82 | ); 83 | } 84 | 85 | static get IfStatement() { 86 | return ( 87 | class IfStatement { 88 | constructor() { 89 | this.type = NODE_TYPES.IfStatement; 90 | this.condition = null; 91 | this.consequent = null; 92 | this.alternate = null; 93 | } 94 | } 95 | ); 96 | } 97 | 98 | static get BinaryExpression() { 99 | return ( 100 | class BinaryExpression { 101 | constructor() { 102 | this.type = NODE_TYPES.BinaryExpression; 103 | this.operator = null; 104 | this.left = null; 105 | this.right = null; 106 | } 107 | } 108 | ); 109 | } 110 | 111 | static get UnaryExpression() { 112 | return ( 113 | class UnaryExpression { 114 | constructor() { 115 | this.type = NODE_TYPES.UnaryExpression; 116 | this.operator = null; 117 | this.init = null; 118 | } 119 | } 120 | ); 121 | } 122 | 123 | static get AsyncStatement() { 124 | return ( 125 | class AsyncStatement { 126 | constructor() { 127 | this.type = NODE_TYPES.AsyncStatement; 128 | this.init = null; 129 | } 130 | } 131 | ); 132 | } 133 | 134 | static get MemberExpression() { 135 | return ( 136 | class MemberExpression { 137 | constructor() { 138 | this.type = NODE_TYPES.MemberExpression; 139 | this.object = null; 140 | this.property = null; 141 | } 142 | } 143 | ); 144 | } 145 | 146 | static get CallExpression() { 147 | return ( 148 | class CallExpression { 149 | constructor() { 150 | this.type = NODE_TYPES.CallExpression; 151 | this.callee = null; 152 | this.arguments = []; 153 | } 154 | } 155 | ); 156 | } 157 | 158 | static get AssignmentExpression() { 159 | return ( 160 | class AssignmentExpression { 161 | constructor() { 162 | this.type = NODE_TYPES.AssignmentExpression; 163 | this.operator = null; 164 | this.left = null; 165 | this.right = null; 166 | } 167 | } 168 | ); 169 | } 170 | 171 | } -------------------------------------------------------------------------------- /src/Engine/Environment/Parser/expression.js: -------------------------------------------------------------------------------- 1 | import NODE_LIST from "./NodeList"; 2 | 3 | /** 4 | * Recursive object member parsing 5 | * Identifier *. *Identifier 6 | * @return {Object} 7 | */ 8 | export function parseMemberExpression() { 9 | 10 | let ast = null; 11 | let tmp = null; 12 | let parent = null; 13 | 14 | ast = this.parseUnary(); 15 | 16 | for (;this.accept("PERIOD") === true;) { 17 | parent = new NODE_LIST.MemberExpression(); 18 | parent.object = ast; 19 | this.next(); 20 | tmp = this.parseMemberExpression(); 21 | parent.property = tmp; 22 | ast = parent; 23 | }; 24 | 25 | return (ast); 26 | 27 | } 28 | 29 | /** 30 | * Recursive operator precedence based 31 | * binary expression parsing 32 | * @param {Number} id 33 | * @return {Object} 34 | */ 35 | export function parseExpression(id) { 36 | 37 | let state = null; 38 | let ast = null; 39 | let parent = null; 40 | let tmp = null; 41 | 42 | state = this.precedence[id]; 43 | 44 | ast = state === void 0 ? this.parseUnary() : this.parseExpression(id + 1); 45 | 46 | for (;this.acceptPrecedenceState(state) === true;) { 47 | parent = new NODE_LIST.BinaryExpression(); 48 | parent.operator = this.node.name; 49 | parent.left = ast; 50 | this.next(); 51 | tmp = (state === void 0 ? this.parseUnary() : this.parseExpression(id + 1)); 52 | if (tmp === null) return (null); 53 | parent.right = tmp; 54 | ast = parent; 55 | if (this.accept("SEMICOLON") === true) { 56 | this.next(); 57 | } 58 | }; 59 | 60 | return (ast); 61 | 62 | } 63 | 64 | /** 65 | * Parse unary 66 | * @return {Object} 67 | */ 68 | export function parseUnary() { 69 | 70 | let ast = null; 71 | let tmp = null; 72 | 73 | if (this.accept("SUB") === true) { 74 | ast = new NODE_LIST.BinaryExpression(); 75 | ast.operator = this.node.name; 76 | tmp = new NODE_LIST.Literal(); 77 | tmp.name = "NUMBER"; 78 | tmp.value = 0; 79 | ast.right = tmp; 80 | this.next(); 81 | if ((tmp = this.parseBase()) === null) return (null); 82 | ast.left = tmp; 83 | } 84 | else if (this.accept("NOT") === true) { 85 | ast = new NODE_LIST.UnaryExpression(); 86 | ast.operator = this.node.name; 87 | this.next(); 88 | ast.init = this.parseExpression(0); 89 | } 90 | else { 91 | if (this.accept("ADD") === true) { 92 | this.next(); 93 | } 94 | if (!(ast = this.parseBase())) return (null); 95 | } 96 | 97 | return (ast); 98 | 99 | } 100 | 101 | /** 102 | * Parse base 103 | * @return {Object} 104 | */ 105 | export function parseBase() { 106 | 107 | let ast = null; 108 | 109 | if ( 110 | this.accept("TRUE") === true || 111 | this.accept("FALSE") === true 112 | ) { 113 | ast = new NODE_LIST.Identifier(); 114 | ast.name = this.node.value; 115 | this.next(); 116 | return (ast); 117 | } 118 | 119 | if (this.accept("NUMBER") === true) { 120 | ast = new NODE_LIST.Literal(); 121 | ast.name = this.node.name; 122 | ast.value = Number(this.node.value); 123 | this.next(); 124 | return (ast); 125 | } 126 | 127 | if (this.accept("STRING") === true) { 128 | ast = new NODE_LIST.Literal(); 129 | ast.name = this.node.name; 130 | ast.value = this.node.value; 131 | this.next(); 132 | return (ast); 133 | } 134 | 135 | if (this.accept("LPAREN") === true) { 136 | this.next(); 137 | ast = this.parseExpression(0); 138 | this.next(); 139 | return (ast); 140 | } 141 | 142 | if (this.accept("IDENTIFIER") === true) { 143 | ast = new NODE_LIST.Identifier(); 144 | ast.name = this.node.value; 145 | if (this.tokens[this.index + 1].name === "PERIOD") { 146 | this.next(); 147 | let exp = this.parseMemberExpression(); 148 | exp.object = ast; 149 | return (exp); 150 | } 151 | this.next(); 152 | return (ast); 153 | } 154 | 155 | return (ast); 156 | 157 | } -------------------------------------------------------------------------------- /src/Engine/Environment/Parser/index.js: -------------------------------------------------------------------------------- 1 | import NODE_LIST from "./NodeList"; 2 | 3 | import * as pr from "./precedence"; 4 | 5 | import * as parse from "./parse"; 6 | import * as expression from "./expression"; 7 | 8 | import { inherit } from "../../utils"; 9 | 10 | /** 11 | * Parser 12 | * @class Parser 13 | * @export 14 | */ 15 | export default class Parser { 16 | 17 | /** 18 | * @constructor 19 | */ 20 | constructor() { 21 | 22 | /** 23 | * Token input 24 | * @type {Array} 25 | */ 26 | this.tokens = null; 27 | 28 | /** 29 | * Token index 30 | * @type {Number} 31 | */ 32 | this.index = 0; 33 | 34 | /** 35 | * Operator precedences 36 | * @type {Array} 37 | */ 38 | this.precedence = pr.precedence; 39 | 40 | /** 41 | * node 42 | * @type {Object} 43 | * @getter 44 | */ 45 | Object.defineProperty(this, "node", { 46 | get: function() { 47 | return (this.tokens[this.index]); 48 | } 49 | }); 50 | 51 | } 52 | 53 | /** 54 | * Parse 55 | * @param {Array} tokens 56 | * @return {Object} 57 | */ 58 | parse(tokens) { 59 | 60 | this.tokens = tokens; 61 | 62 | this.index = 0; 63 | 64 | let ast = new NODE_LIST.Program(); 65 | 66 | let length = this.tokens.length; 67 | 68 | let block = null; 69 | 70 | for (;;) { 71 | if (this.index >= length) break; 72 | if ((block = this.parseBlock()) === null) continue; 73 | ast.body.push(block); 74 | }; 75 | 76 | return (ast); 77 | 78 | } 79 | 80 | /** 81 | * Increase token index 82 | */ 83 | next() { 84 | this.index++; 85 | } 86 | 87 | /** 88 | * Node type acception 89 | * @param {String} type 90 | * @return {Boolean} 91 | */ 92 | accept(type) { 93 | if (this.node === void 0) return (false); 94 | if (this.node.name === type) { 95 | return (true); 96 | } 97 | return (false); 98 | } 99 | 100 | /** 101 | * Node type expection 102 | * @param {String} name 103 | */ 104 | expect(name) { 105 | for (;true;) { 106 | if (this.node.name === name) { 107 | this.next(); 108 | break; 109 | } 110 | this.next(); 111 | } 112 | return void 0; 113 | } 114 | 115 | /** 116 | * Accept precedence state 117 | * @param {String} state 118 | * @return {Boolean} 119 | */ 120 | acceptPrecedenceState(state) { 121 | return ( 122 | state !== void 0 && 123 | this.node !== void 0 && 124 | state.indexOf(this.node.name) > -1 125 | ); 126 | } 127 | 128 | } 129 | 130 | inherit(Parser, parse); 131 | inherit(Parser, expression); -------------------------------------------------------------------------------- /src/Engine/Environment/Parser/parse.js: -------------------------------------------------------------------------------- 1 | import NODE_LIST from "./NodeList"; 2 | 3 | /** 4 | * Parse a block 5 | * @return {Object} 6 | */ 7 | export function parseBlock() { 8 | 9 | if (this.accept("IF") === true) { 10 | return (this.parseIfStatement()); 11 | } 12 | 13 | if (this.accept("RETURN") === true) { 14 | return (this.parseReturnStatement()); 15 | } 16 | 17 | if (this.accept("ATSIGN") === true) { 18 | return (this.parseAsyncStatement()); 19 | } 20 | 21 | if (this.accept("IDENTIFIER") === true) { 22 | return (this.parseIdentifierRoute()); 23 | } 24 | 25 | return (this.parseExpression(0)); 26 | 27 | } 28 | 29 | /** 30 | * Parse async statement 31 | * Identifier () | = | ; 32 | * @return {Object} 33 | */ 34 | export function parseAsyncStatement() { 35 | 36 | let ast = null; 37 | 38 | this.next(); 39 | ast = new NODE_LIST.AsyncStatement(); 40 | ast.init = this.parseBlock(); 41 | 42 | return (ast); 43 | 44 | } 45 | 46 | /** 47 | * Is assignment or assignset 48 | * @return {Boolean} 49 | */ 50 | export function isSet() { 51 | return ( 52 | this.accept("ASSIGN") === true || 53 | this.accept("ADDSET") === true || 54 | this.accept("SUBSET") === true || 55 | this.accept("MULSET") === true || 56 | this.accept("DIVSET") === true || 57 | this.accept("MODSET") === true 58 | ); 59 | } 60 | 61 | /** 62 | * Parse identifier route 63 | * Identifier () | = | . | ; 64 | * @return {Object} 65 | */ 66 | export function parseIdentifierRoute() { 67 | 68 | let ast = null; 69 | 70 | let tmp = this.parseExpression(0); 71 | 72 | /** Call expression */ 73 | if (this.accept("LPAREN") === true) { 74 | ast = this.parseCallExpression(); 75 | ast.callee = tmp; 76 | } 77 | 78 | /** Assignment expression */ 79 | if (this.isSet() === true) { 80 | ast = this.parseAssignmentExpression(); 81 | ast.left = tmp; 82 | } 83 | 84 | if (ast === null) { 85 | return (tmp); 86 | } 87 | 88 | return (ast); 89 | 90 | } 91 | 92 | /** 93 | * Parse call expression 94 | * MemberExpression () ; 95 | * @return {Object} 96 | */ 97 | export function parseCallExpression() { 98 | 99 | let ast = null; 100 | 101 | ast = new NODE_LIST.CallExpression(); 102 | ast.arguments = this.parseArguments(); 103 | 104 | this.next(); 105 | 106 | return (ast); 107 | 108 | } 109 | 110 | /** 111 | * Parse assignment expression 112 | * Expression = Expression 113 | * @return {Object} 114 | */ 115 | export function parseAssignmentExpression() { 116 | 117 | let ast = null; 118 | 119 | ast = new NODE_LIST.AssignmentExpression(); 120 | ast.left = this.parseExpression(0); 121 | ast.operator = this.node.name; 122 | this.next(); 123 | ast.right = this.parseExpression(0); 124 | 125 | if (this.accept("SEMICOLON") === true) { 126 | this.next(); 127 | } 128 | 129 | return (ast); 130 | 131 | } 132 | 133 | /** 134 | * Parse if statement 135 | * if ( Expression ) { Body } | { Body } 136 | * @return {Object} 137 | */ 138 | export function parseIfStatement() { 139 | 140 | let ast = null; 141 | 142 | this.next(); 143 | 144 | ast = new NODE_LIST.IfStatement(); 145 | ast.condition = this.parseParentheseExpression(); 146 | ast.consequent = this.parseBraceBody(); 147 | 148 | if (this.accept("LBRACE") === true) { 149 | ast.alternate = this.parseBraceBody(); 150 | } 151 | 152 | return (ast); 153 | 154 | } 155 | 156 | /** 157 | * Parse return statement 158 | * return ( Expression ) 159 | * @return {Object} 160 | */ 161 | export function parseReturnStatement() { 162 | 163 | let ast = null; 164 | 165 | this.next(); 166 | ast = new NODE_LIST.ReturnStatement(); 167 | ast.value = this.parseParentheseExpression(); 168 | this.next(); 169 | 170 | return (ast); 171 | 172 | } 173 | 174 | /** 175 | * Parse brace body 176 | * { Body } 177 | * @return {Object} 178 | */ 179 | export function parseBraceBody() { 180 | 181 | let ast = null; 182 | 183 | this.expect("LBRACE"); 184 | ast = this.parseBlockStatement(); 185 | this.expect("RBRACE"); 186 | 187 | return (ast); 188 | 189 | } 190 | 191 | /** 192 | * Parse block statement 193 | * @return {Object} 194 | */ 195 | export function parseBlockStatement() { 196 | 197 | let ast = new NODE_LIST.BlockStatement(); 198 | 199 | for (;true;) { 200 | if (this.accept("RBRACE") === true) break; 201 | ast.body.push(this.parseBlock()); 202 | }; 203 | 204 | return (ast); 205 | 206 | } 207 | 208 | /** 209 | * Parse arguments 210 | * [ , ] 211 | * @return {Array} 212 | */ 213 | export function parseArguments() { 214 | 215 | let args = []; 216 | 217 | let tmp = null; 218 | 219 | this.expect("LPAREN"); 220 | 221 | tmp = this.parseBlock(); 222 | 223 | if (tmp !== null) { 224 | args.push(tmp); 225 | } 226 | 227 | for (;this.accept("COMMA") === true;) { 228 | this.next(); 229 | if (this.accept("LPAREN") === true) { 230 | this.next(); 231 | tmp = this.parseCallExpression(); 232 | if (tmp !== null) { 233 | args.push(tmp); 234 | } 235 | } else { 236 | tmp = this.parseBlock(); 237 | if (tmp !== null) { 238 | args.push(tmp); 239 | } 240 | } 241 | if (this.accept("RPAREN") === true) { 242 | this.next(); 243 | break; 244 | } 245 | }; 246 | 247 | if (args.length <= 1 && this.accept("RPAREN") === true) { 248 | this.next(); 249 | } 250 | 251 | return (args); 252 | 253 | } 254 | 255 | /** 256 | * Parse parenthese expression 257 | * ( Expression ) 258 | */ 259 | export function parseParentheseExpression() { 260 | 261 | let ast = null; 262 | 263 | this.expect("LPAREN"); 264 | ast = this.parseExpression(0); 265 | this.expect("RPAREN"); 266 | 267 | return (ast); 268 | 269 | } -------------------------------------------------------------------------------- /src/Engine/Environment/Parser/precedence.js: -------------------------------------------------------------------------------- 1 | export let precedence = [ 2 | ["OR"], 3 | ["AND"], 4 | ["EQ", "NEQ"], 5 | ["LE", "LT", "GE", "GT"], 6 | ["ADD", "SUB"], 7 | ["MUL", "DIV", "MOD"] 8 | ]; -------------------------------------------------------------------------------- /src/Engine/Environment/Tester/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | NODE_LIST, NODE_TYPES 3 | } from "../Parser/NodeList"; 4 | 5 | import * as tests from "./tests"; 6 | 7 | import { inherit } from "../../utils"; 8 | 9 | /** 10 | * Tester 11 | * @class Tester 12 | * @export 13 | */ 14 | export default class Tester { 15 | 16 | /** 17 | * @constructor 18 | * @param {Object} tokenizer 19 | * @param {Object} parser 20 | * @param {Object} evaluator 21 | */ 22 | constructor(tokenizer, parser, evaluator) { 23 | 24 | /** 25 | * Tokenizer instance ref 26 | * @type {Object} 27 | */ 28 | this.tokenizer = tokenizer; 29 | 30 | /** 31 | * Parser instance ref 32 | * @type {Object} 33 | */ 34 | this.parser = parser; 35 | 36 | /** 37 | * Evaluator instance ref 38 | * @type {Object} 39 | */ 40 | this.evaluator = evaluator; 41 | 42 | this.setup(); 43 | 44 | } 45 | 46 | /** 47 | * Test 48 | * @param {Object} key 49 | * @param {Function} resolve 50 | */ 51 | test(key, resolve) { 52 | 53 | let ast = this.parser.parse(this.tokenizer.scan(key.expression)); 54 | 55 | this.evaluator.evaluate(ast, this::function(result) { 56 | if (result !== key.expect) { 57 | console.log(this.parser.parse(this.tokenizer.scan(key.expression))); 58 | console.info(`%c ☓ ${key.name} :: ${key.expression} = ${result}`, "color: darkred;"); 59 | resolve(false); 60 | } 61 | resolve(true); 62 | }); 63 | 64 | return void 0; 65 | 66 | } 67 | 68 | /** 69 | * Setup 70 | */ 71 | setup() { 72 | 73 | let failures = 0; 74 | 75 | for (let type in tests) { 76 | for (let key of tests[type]) { 77 | this.test(key, (result) => result === false && ++failures); 78 | }; 79 | if (failures <= 0) { 80 | console.info(`%c ✓ ${type}`, "color: darkgreen;"); 81 | } 82 | }; 83 | 84 | if (failures) { 85 | console.error(`${failures} ${failures > 1 || failures === 0 ? "tests" : "test"} failed!`); 86 | } else { 87 | console.info("%cAll tests passed successfully!", "color: green;"); 88 | } 89 | 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /src/Engine/Environment/Tester/tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mathematical tests 3 | * @type {Array} 4 | */ 5 | export let Mathematical = [ 6 | /** Logical */ 7 | { 8 | name: "Not", 9 | expression: "!true", 10 | expect: false 11 | }, 12 | { 13 | name: "Not", 14 | expression: "!!true", 15 | expect: true 16 | }, 17 | { 18 | name: "Not", 19 | expression: "!!!false", 20 | expect: true 21 | }, 22 | { 23 | name: "Logical And", 24 | expression: "1 && 2", 25 | expect: 2 26 | }, 27 | { 28 | name: "Logical And", 29 | expression: "1 == 1 && 2 >= 2", 30 | expect: true 31 | }, 32 | { 33 | name: "Logical And", 34 | expression: "1 != 1 && 2 < 2", 35 | expect: false 36 | }, 37 | { 38 | name: "Logical Or", 39 | expression: "0 || 2", 40 | expect: 2 41 | }, 42 | { 43 | name: "Logical Or", 44 | expression: "2 || 0", 45 | expect: 2 46 | }, 47 | { 48 | name: "Logical Or", 49 | expression: "10 <= 2 || 5 < 2 || 50 / 2 == 25", 50 | expect: true 51 | }, 52 | { 53 | name: "Logical And Or", 54 | expression: "5 == 1 || 1 == 2 || 5 == 5", 55 | expect: true 56 | }, 57 | /** Comparisions */ 58 | { 59 | name: "Equality", 60 | expression: "1 == 5", 61 | expect: false 62 | }, 63 | { 64 | name: "Equality", 65 | expression: "5 == 5", 66 | expect: true 67 | }, 68 | { 69 | name: "Inequality", 70 | expression: "1 != 5", 71 | expect: true 72 | }, 73 | { 74 | name: "Inequality", 75 | expression: "5 != 5", 76 | expect: false 77 | }, 78 | { 79 | name: "LW", 80 | expression: "5 < 10", 81 | expect: true 82 | }, 83 | { 84 | name: "LW", 85 | expression: "10 < 5", 86 | expect: false 87 | }, 88 | { 89 | name: "LW", 90 | expression: "5 < 5", 91 | expect: false 92 | }, 93 | { 94 | name: "LE", 95 | expression: "5 <= 10", 96 | expect: true 97 | }, 98 | { 99 | name: "LE", 100 | expression: "10 <= 5", 101 | expect: false 102 | }, 103 | { 104 | name: "LE", 105 | expression: "5 <= 5", 106 | expect: true 107 | }, 108 | { 109 | name: "GT", 110 | expression: "5 > 10", 111 | expect: false 112 | }, 113 | { 114 | name: "GT", 115 | expression: "10 > 5", 116 | expect: true 117 | }, 118 | { 119 | name: "GT", 120 | expression: "5 > 5", 121 | expect: false 122 | }, 123 | { 124 | name: "GE", 125 | expression: "5 >= 10", 126 | expect: false 127 | }, 128 | { 129 | name: "GE", 130 | expression: "10 >= 5", 131 | expect: true 132 | }, 133 | { 134 | name: "GE", 135 | expression: "5 >= 5", 136 | expect: true 137 | }, 138 | /** Binary operators */ 139 | { 140 | name: "Add operator", 141 | expression: "5.5 + 2.5 + 7", 142 | expect: 15 143 | }, 144 | { 145 | name: "Minus operator", 146 | expression: "5.5 - 2.5 - 7", 147 | expect: -4 148 | }, 149 | { 150 | name: "Mul operator", 151 | expression: "5.5 * 2.5 * 7", 152 | expect: 96.25 153 | }, 154 | { 155 | name: "Div operator", 156 | expression: "25 / 5 / 2.5", 157 | expect: 2 158 | }, 159 | { 160 | name: "Mod operator", 161 | expression: "32 % 6", 162 | expect: 2 163 | }, 164 | { 165 | name: "Complex", 166 | expression: "5 + 1 / 2 * 2 - ((2.5 * (6 + 4 * 2) / 5) * 5) - 1.5", 167 | expect: -30.5 168 | }, 169 | /** Numbers */ 170 | { 171 | name: "Negative integer", 172 | expression: "-77.5", 173 | expect: -77.5 174 | }, 175 | /** Booleans */ 176 | { 177 | name: "True bool", 178 | expression: "true", 179 | expect: true 180 | }, 181 | { 182 | name: "False bool", 183 | expression: "false", 184 | expect: false 185 | } 186 | ]; -------------------------------------------------------------------------------- /src/Engine/Environment/Tokenizer/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tokenizer 3 | * @class Tokenizer 4 | * @export 5 | */ 6 | export default class Tokenizer { 7 | 8 | /** 9 | * @constructor 10 | * @param {Object} tokens 11 | * @param {Array} ignore 12 | */ 13 | constructor(tokens, ignore) { 14 | 15 | /** 16 | * Operand lookup map 17 | * @type {Object} 18 | */ 19 | this.TOKEN_LIST = tokens || {}; 20 | 21 | /** 22 | * Ignore token list 23 | * @type {Array} 24 | */ 25 | this.IGNORE_LIST = ignore || []; 26 | 27 | /** 28 | * Stream buffer 29 | * @type {String} 30 | */ 31 | this.buffer = null; 32 | 33 | /** 34 | * Stream index 35 | * @type {Number} 36 | */ 37 | this.index = 0; 38 | 39 | } 40 | 41 | /** 42 | * Is digit 43 | * @param {Number} c 44 | * @return {Boolean} 45 | */ 46 | isDigit(c) { 47 | return ( 48 | c >= 48 && c <= 57 49 | ); 50 | } 51 | 52 | /** 53 | * Is alpha 54 | * @param {Number} c 55 | * @return {Boolean} 56 | */ 57 | isAlpha(c) { 58 | return ( 59 | c > 64 && c < 91 || 60 | c > 96 && c < 123 61 | ); 62 | } 63 | 64 | /** 65 | * Is alpha digit 66 | * @param {Number} c 67 | * @return {Boolean} 68 | */ 69 | isAlphaDigit(c) { 70 | return ( 71 | c > 47 && c < 58 || 72 | c > 64 && c < 91 || 73 | c > 96 && c < 123 || 74 | c === 95 75 | ); 76 | } 77 | 78 | /** 79 | * Is string 80 | * @param {Number} c 81 | * @return {Boolean} 82 | */ 83 | isString(c) { 84 | return ( 85 | c === 34 || c === 39 86 | ); 87 | } 88 | 89 | /** 90 | * Token validation 91 | * @param {Object} token 92 | * @return {Boolean} 93 | */ 94 | isValidToken(token) { 95 | return ( 96 | token.name !== void 0 && 97 | this.IGNORE_LIST.indexOf(token.name) <= -1 98 | ); 99 | } 100 | 101 | /** 102 | * Token name validation 103 | * @param {String} name 104 | * @return {Boolean} 105 | */ 106 | isIgnoredName(name) { 107 | return ( 108 | this.IGNORE_LIST.indexOf(name) <= -1 109 | ); 110 | } 111 | 112 | /** 113 | * Creates number token 114 | * @return {Object} 115 | */ 116 | readNumber() { 117 | 118 | let end = this.index + 1; 119 | 120 | let c = null; 121 | 122 | for (; end < this.length; ++end) { 123 | c = this.buffer.charAt(end).charCodeAt(0); 124 | /** Also check for floating numbers */ 125 | if (c !== 46 && this.isDigit(c) === false) break; 126 | }; 127 | 128 | let value = this.buffer.slice(this.index, end); 129 | 130 | this.index = end; 131 | 132 | return ({ 133 | name: "NUMBER", 134 | value: value 135 | }); 136 | 137 | } 138 | 139 | /** 140 | * Creates identifier or keyword token 141 | * @return {Object} 142 | */ 143 | readIdentifier() { 144 | 145 | let end = this.index + 1; 146 | 147 | for (; end < this.length && this.isAlphaDigit(this.buffer.charAt(end).charCodeAt(0)) === true; ++end) {}; 148 | 149 | let value = this.buffer.slice(this.index, end); 150 | 151 | this.index = end; 152 | 153 | /** Keyword */ 154 | if (this.TOKEN_LIST[value] !== void 0) { 155 | return ({ 156 | name: this.TOKEN_LIST[value], 157 | value: value 158 | }); 159 | /** Identifier */ 160 | } else { 161 | return ({ 162 | name: "IDENTIFIER", 163 | value: value 164 | }); 165 | } 166 | 167 | } 168 | 169 | /** 170 | * Creates string token 171 | * @return {Object} 172 | */ 173 | readString() { 174 | 175 | let end = this.buffer.indexOf("'", this.index + 1); 176 | 177 | if (end === -1) { 178 | end = this.buffer.indexOf('"', this.index + 1); 179 | if (end === -1) throw new Error(`Unexpected quote at ${ this.index }!`); 180 | } 181 | 182 | let token = { 183 | name: "STRING", 184 | value: this.buffer.slice(this.index, end + 1) 185 | }; 186 | 187 | this.index = end + 1; 188 | 189 | return (token); 190 | 191 | } 192 | 193 | readNegativeNumber() { 194 | 195 | let node = null; 196 | 197 | node = this.readNumber(); 198 | 199 | node.value = "-" + node.value; 200 | 201 | return (node); 202 | 203 | } 204 | 205 | /** 206 | * Read sign 207 | * @return {Object} 208 | */ 209 | readSign() { 210 | 211 | let c = null; 212 | 213 | let code = 0; 214 | 215 | let name = null; 216 | 217 | let value = ""; 218 | 219 | for (;;) { 220 | c = this.buffer.charAt(this.index); 221 | code = c.charCodeAt(0); 222 | if (this.isDigit(code) === true) { 223 | if (value === "-") { 224 | return (this.readNegativeNumber()); 225 | 226 | } 227 | } 228 | value += c; 229 | if (this.TOKEN_LIST[value] === void 0) break; 230 | this.index++; 231 | name = this.TOKEN_LIST[value]; 232 | if (this.index > this.length) break; 233 | }; 234 | 235 | return ({ 236 | name: name, 237 | value: value 238 | }); 239 | 240 | } 241 | 242 | /** 243 | * Lexical analysis 244 | * @param {String} stream 245 | * @return {Array} 246 | */ 247 | scan(stream) { 248 | 249 | this.index = 0; 250 | this.vIndex = 0; 251 | this.buffer = stream; 252 | this.length = this.buffer.length; 253 | 254 | let c = null; 255 | let op = null; 256 | let cCode = 0; 257 | let token = null; 258 | 259 | let tokens = []; 260 | 261 | for (;;) { 262 | 263 | if (!(c = this.buffer.charAt(this.index)) || this.index >= this.length) break; 264 | 265 | cCode = c.charCodeAt(0); 266 | 267 | if ((op = this.TOKEN_LIST[c]) !== void 0) { 268 | token = this.readSign(); 269 | if (this.isValidToken(token) === true) tokens.push(token); 270 | } 271 | if (this.isDigit(cCode) === true) { 272 | token = this.readNumber(); 273 | if (this.isValidToken(token) === true) tokens.push(token); 274 | } 275 | if (this.isAlpha(cCode) === true) { 276 | token = this.readIdentifier(); 277 | if (this.isValidToken(token) === true) tokens.push(token); 278 | } 279 | if (this.isString(cCode) === true) { 280 | token = this.readString(); 281 | if (this.isValidToken(token) === true) tokens.push(token); 282 | } 283 | 284 | }; 285 | 286 | return (tokens); 287 | 288 | } 289 | 290 | } -------------------------------------------------------------------------------- /src/Engine/Environment/Tokenizer/tokens.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tokens to match 3 | * @type {Object} 4 | */ 5 | export const TOKENS = { 6 | /** Punctuators */ 7 | "(": "LPAREN", 8 | ")": "RPAREN", 9 | "[": "LBRACK", 10 | "]": "RBRACK", 11 | "{": "LBRACE", 12 | "}": "RBRACE", 13 | ":": "COLON", 14 | ";": "SEMICOLON", 15 | ".": "PERIOD", 16 | "?": "CONDITIONAL", 17 | "$": "DOLLAR", 18 | "@": "ATSIGN", 19 | /** Logical operators */ 20 | "!": "NOT", 21 | "||": "OR", 22 | "&&": "AND", 23 | /** Binary operators */ 24 | ",": "COMMA", 25 | "+": "ADD", 26 | "-": "SUB", 27 | "*": "MUL", 28 | "/": "DIV", 29 | "%": "MOD", 30 | "^": "POW", 31 | "+=": "ADDSET", 32 | "-=": "SUBSET", 33 | "*=": "MULSET", 34 | "/=": "DIVSET", 35 | "%=": "MODSET", 36 | /** Compare operators */ 37 | "<": "LT", 38 | "<=": "LE", 39 | ">": "GT", 40 | ">=": "GE", 41 | "==": "EQ", 42 | "!=": "NEQ", 43 | /** Assignment operators */ 44 | "=": "ASSIGN", 45 | /** Bitwise operators */ 46 | "~": "BIT_NOT", 47 | "|": "BIT_OR", 48 | "&": "BIT_AND", 49 | /** Literals */ 50 | "null": "NULL", 51 | "true": "TRUE", 52 | "false": "FALSE", 53 | /** Keywords */ 54 | "if": "IF", 55 | "else": "ELSE", 56 | "while": "WHILE", 57 | "do": "DO", 58 | "for": "FOR", 59 | "function": "FUNCTION", 60 | "var": "VAR", 61 | "const": "CONST", 62 | "return": "RETURN", 63 | " ": "BLANK", 64 | "\t": "TAB", 65 | "\n": "NL", 66 | "\r": "X", 67 | "\f": "X1", 68 | "\v": "X2" 69 | }; 70 | 71 | /** 72 | * Tokens to ignore 73 | * @type {Array} 74 | */ 75 | export let IGNORE = [ 76 | "BLANK", "TAB", "NL", "X", "X1", "X2" 77 | ]; -------------------------------------------------------------------------------- /src/Engine/Environment/index.js: -------------------------------------------------------------------------------- 1 | import * as tokens from "./Tokenizer/tokens"; 2 | 3 | import Tester from "./Tester/"; 4 | import Tokenizer from "./Tokenizer/"; 5 | import Parser from "./Parser/"; 6 | import Evaluator from "./Evaluator/"; 7 | 8 | /** 9 | * Environment 10 | * @class Environment 11 | * @export 12 | */ 13 | export default class Environment { 14 | 15 | /** 16 | * @constructor 17 | * @param {Object} instance 18 | */ 19 | constructor(instance) { 20 | 21 | /** 22 | * Game instance ref 23 | * @type {Object} 24 | */ 25 | this.instance = instance; 26 | 27 | /** 28 | * Global flags 29 | * @type {Object} 30 | */ 31 | this.FLAGS = { 32 | GOT_STARTER_PKMN: false, 33 | COUNTER: 0 34 | }; 35 | 36 | /** 37 | * Tokenizer instance 38 | * @type {Object} 39 | */ 40 | this.tokenizer = new Tokenizer(tokens.TOKENS, tokens.IGNORE); 41 | 42 | /** 43 | * Parser instance 44 | * @type {Object} 45 | */ 46 | this.parser = new Parser(); 47 | 48 | /** 49 | * Evaluator instance 50 | * @type {Object} 51 | */ 52 | this.evaluator = new Evaluator(this); 53 | 54 | /** 55 | * Tester instance 56 | * @type {Object} 57 | */ 58 | this.tester = new Tester( 59 | this.tokenizer, 60 | this.parser, 61 | this.evaluator 62 | ); 63 | 64 | console.log(this.FLAGS); 65 | 66 | } 67 | 68 | /** 69 | * Run a stream 70 | * @param {Object} local 71 | * @param {Object} trigger 72 | * @param {String} stream 73 | */ 74 | run(local, trigger, stream) { 75 | 76 | this.evaluator.setTriggerScope(local); 77 | this.evaluator.setGlobalScope(trigger); 78 | 79 | let tokens = this.tokenizer.scan(stream); 80 | 81 | let ast = this.parser.parse(tokens); 82 | 83 | this.evaluator.evaluate(ast, function(result) { 84 | console.log("Ok!", result); 85 | }); 86 | 87 | } 88 | 89 | } -------------------------------------------------------------------------------- /src/Engine/Input/Mouse.js: -------------------------------------------------------------------------------- 1 | import { 2 | uHash 3 | } from "../utils"; 4 | 5 | /** 6 | * Mouse 7 | * @class Mouse 8 | * @export 9 | */ 10 | export default class Mouse { 11 | 12 | /** 13 | * @constructor 14 | * @param {Array} events 15 | */ 16 | constructor(events) { 17 | 18 | return (this); 19 | 20 | } 21 | 22 | /** 23 | * Register a mouse event 24 | * @param {Object} event 25 | */ 26 | registerEvent(event, root) { 27 | 28 | let fire = null; 29 | 30 | let events = event.name.split("|"); 31 | 32 | for (let ev of events) { 33 | if (ev === "mousewheel") { 34 | window.addWheelListener(document.body, e => root::event.fire(e)); 35 | } else { 36 | window.addEventListener(ev, e => root::event.fire(e), false); 37 | } 38 | }; 39 | 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/Engine/Input/index.js: -------------------------------------------------------------------------------- 1 | import keyboard from "./Keyboard"; 2 | import mouse from "./Mouse"; 3 | 4 | /** 5 | * Input 6 | * @class Input 7 | * @export 8 | */ 9 | export default class Input { 10 | 11 | /** 12 | * @constructor 13 | */ 14 | constructor(events, instance) { 15 | 16 | this.instance = instance; 17 | 18 | this.events = events; 19 | 20 | this.KeyBoard = new keyboard(); 21 | this.Mouse = new mouse(); 22 | 23 | this.registerKeys(); 24 | this.registerMouse(); 25 | this.registerGlobal(); 26 | 27 | } 28 | 29 | /** 30 | * Register keys 31 | */ 32 | registerKeys() { 33 | 34 | if (this.events.keys === void 0) return void 0; 35 | 36 | for (let key of this.events.keys) { 37 | this.KeyBoard.registerKey( 38 | key, 39 | this.instance.engine.controller::key.fire, 40 | key.leave instanceof Function ? this.instance.engine.controller::key.leave : void 0 41 | ); 42 | }; 43 | 44 | } 45 | 46 | /** 47 | * Register mouse 48 | */ 49 | registerMouse() { 50 | 51 | if (this.events.mouse === void 0) return void 0; 52 | 53 | for (let ev of this.events.mouse) { 54 | this.Mouse.registerEvent(ev, this.instance.engine.controller); 55 | }; 56 | 57 | } 58 | 59 | /** 60 | * Register globals 61 | */ 62 | registerGlobal() { 63 | 64 | if (this.events.global === void 0) return void 0; 65 | 66 | for (let ev of this.events.global) { 67 | this.registerGlobalEvent(ev, this.instance.engine.controller); 68 | }; 69 | 70 | } 71 | 72 | /** 73 | * Register event 74 | * @param {Object} event 75 | */ 76 | registerGlobalEvent(event) { 77 | 78 | window.addEventListener(event.name, this.instance.engine.controller::event.fire, false); 79 | 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /src/Engine/Language/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | DEFAULT_LANG 3 | } from "../../cfg"; 4 | 5 | import { 6 | ajax as $GET 7 | } from "../utils"; 8 | 9 | /** 10 | * Language 11 | * @class Language 12 | * @export 13 | */ 14 | export default class Language { 15 | 16 | /** 17 | * @param {Function} resolve 18 | * @constructor 19 | */ 20 | constructor(resolve) { 21 | 22 | /** 23 | * Language packets 24 | * @type {Object} 25 | */ 26 | this.packets = {}; 27 | 28 | /** 29 | * String base 30 | * @type {String} 31 | */ 32 | this.strBase = "s_"; 33 | 34 | /** 35 | * Active packet ref 36 | * @type {Object} 37 | */ 38 | this.activePacket = null; 39 | 40 | /** 41 | * Default language 42 | * @type {String} 43 | */ 44 | this.defaultLanguage = DEFAULT_LANG; 45 | 46 | /** 47 | * Download default lang packet 48 | * Download navigators lang packet 49 | * Auto switch to navigators lang 50 | */ 51 | this.downloadPacket(this.defaultLanguage, () => { 52 | this.switch(this.getNavigatorLanguage(), resolve); 53 | }); 54 | 55 | } 56 | 57 | /** 58 | * Get navigators language 59 | * @return {String} 60 | */ 61 | getNavigatorLanguage() { 62 | 63 | let lang = null; 64 | 65 | if (navigator.languages) { 66 | lang = navigator.languages[0]; 67 | } else if (navigator.userLanguage) { 68 | lang = navigator.userLanguage; 69 | } else { 70 | lang = navigator.language; 71 | } 72 | 73 | return (lang.split("-")[0]); 74 | 75 | } 76 | 77 | /** 78 | * Get language dependant string 79 | * @param {String} key 80 | * @return {String} 81 | */ 82 | get(key) { 83 | return ( 84 | this.activePacket[key] !== void 0 ? this.activePacket[key] : 85 | this.packets[this.defaultLanguage][key] !== void 0 ? this.packets[this.defaultLanguage][key] : 86 | "undefined" 87 | ); 88 | } 89 | 90 | /** 91 | * Download language packet 92 | * @param {String} name 93 | * @param {Function} resolve 94 | */ 95 | downloadPacket(name, resolve) { 96 | 97 | if (this.packets[name] !== void 0) { 98 | return resolve(); 99 | } 100 | 101 | let path = "assets/i18n/"; 102 | 103 | try { 104 | $GET(`${path + name}.json`).then( 105 | JSON.parse 106 | ).then(this::function(data) { 107 | this.packets[name] = data; 108 | resolve(); 109 | }); 110 | } catch(e) { 111 | console.error(`${name} is a invalid language packet!`); 112 | resolve(); 113 | } 114 | 115 | return void 0; 116 | 117 | } 118 | 119 | /** 120 | * Switch to another language packet 121 | * @param {String} name 122 | * @param {Function} resolve 123 | */ 124 | switch(name, resolve) { 125 | 126 | if (this.packets[name] !== void 0) { 127 | this.activePacket = this.packets[name]; 128 | return (resolve && resolve()); 129 | } 130 | 131 | this.downloadPacket(name, () => { 132 | if (this.packets[name] !== void 0) { 133 | this.activePacket = this.packets[name]; 134 | } else { 135 | this.activePacket = this.packets[this.defaultLanguage]; 136 | } 137 | 138 | return (resolve && resolve()); 139 | }); 140 | 141 | return void 0; 142 | 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/Engine/Map/MapEntity.js: -------------------------------------------------------------------------------- 1 | import { DIMENSION } from "../../cfg"; 2 | 3 | import Entity from "../Entity"; 4 | 5 | /** 6 | * MapEntity 7 | * @class MapEntity 8 | * @export 9 | */ 10 | export default class MapEntity extends Entity { 11 | 12 | /** 13 | * @param {Object} obj 14 | * @constructor 15 | */ 16 | constructor(obj) { 17 | 18 | super(obj); 19 | 20 | this.zIndex = obj.zIndex === void 0 ? 1 : obj.zIndex; 21 | 22 | this.frames = [0, 0]; 23 | 24 | this.facing = 0; 25 | 26 | this.opacity = obj.opacity === void 0 ? 1.0 : obj.opacity; 27 | 28 | this.scale = obj.scale === void 0 ? 1.0 : obj.scale; 29 | 30 | this.frame = obj.frame === void 0 ? 1 : obj.frame; 31 | 32 | this.reversed = [0, 0]; 33 | 34 | this.reverseShadow = [0, 0]; 35 | 36 | if (obj.collisionBox !== void 0) { 37 | this.collisionBox = obj.collisionBox; 38 | } 39 | 40 | return (this); 41 | 42 | } 43 | 44 | /** 45 | * Get frame index 46 | * @return {Number} 47 | */ 48 | getFrameIndex() { 49 | return (0); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/Engine/Map/events.js: -------------------------------------------------------------------------------- 1 | import { DIMENSION } from "../../cfg"; 2 | import math from "../../Math"; 3 | 4 | /** 5 | * Trigger events 6 | * @param {Object} entity 7 | * @param {Object} parent 8 | * @param {String} event 9 | */ 10 | export function triggerEvent(entity, parent, event) { 11 | 12 | let cmd = entity[event]; 13 | 14 | /** Collide event */ 15 | if (cmd !== null) { 16 | /** JavaScript API */ 17 | if (cmd.JavaScript !== void 0) { 18 | cmd.JavaScript.bind(entity)(parent, this); 19 | } 20 | /** EngelScript API */ 21 | if (cmd.EngelScript !== void 0) { 22 | this.instance.environment.run( 23 | parent, entity, 24 | cmd.EngelScript 25 | ); 26 | } else { 27 | if (cmd.JavaScript === void 0) { 28 | if (cmd instanceof Function) { 29 | cmd.bind(entity)(parent, this); 30 | } 31 | } 32 | } 33 | } 34 | 35 | return void 0; 36 | 37 | } 38 | 39 | /** 40 | * Action trigger 41 | * @param {Object} position 42 | * @param {Object} entity 43 | */ 44 | export function actionTrigger(position, entity) { 45 | 46 | let entities = this.entities; 47 | 48 | let ii = 0; 49 | let length = entities.length; 50 | 51 | let event = null; 52 | 53 | let id = entity.id; 54 | 55 | let x = position.x << 0; 56 | let y = position.y << 0; 57 | 58 | for (; ii < length; ++ii) { 59 | event = entities[ii]; 60 | if (event.id === id) continue; 61 | if (event.x << 0 === x && event.y << 0 === y) { 62 | this.triggerEvent(event, entity, "onAction"); 63 | } 64 | }; 65 | 66 | return void 0; 67 | 68 | } 69 | 70 | /** 71 | * Obstacle check 72 | * @param {Object} entity 73 | * @param {Number} dir 74 | * @return {Boolean} 75 | */ 76 | export function isObstacle(entity, dir) { 77 | 78 | let position = math.getTilePosition(entity.x << 0, entity.y << 0, dir << 0); 79 | 80 | return ( 81 | this.collisionLayer.data[(position.y << 0) / DIMENSION][(position.x << 0) / DIMENSION] === 0 || 82 | this.isEntityCollidable(entity, position.x, position.y) === true 83 | ); 84 | 85 | } 86 | 87 | /** 88 | * Entity collidable check 89 | * @param {Object} entity 90 | * @param {Number} x 91 | * @param {Number} y 92 | * @return {Boolean} 93 | */ 94 | export function isEntityCollidable(entity, x, y) { 95 | 96 | let entities = this.entities; 97 | 98 | let ii = 0; 99 | let length = entities.length; 100 | 101 | let intersection = false; 102 | 103 | let collide = false; 104 | 105 | let id = entity.id; 106 | 107 | let event = null; 108 | 109 | for (; ii < length; ++ii) { 110 | event = entities[ii]; 111 | if (event.id === id) continue; 112 | intersection = math.linearIntersect( 113 | event.position.x << 0, event.position.y << 0, 114 | ((math.roundTo(event.size.x, DIMENSION) * event.scale) + event.xMargin) - DIMENSION, 115 | ((math.roundTo(event.size.y, DIMENSION) * event.scale) + event.yMargin) - DIMENSION, 116 | x, y, 117 | 1 118 | ); 119 | /** Entity is a collidable */ 120 | if (event.collidable === true) { 121 | /** Collision box */ 122 | if (event.collisionBox.length > 0) { 123 | if (this.collidesWithCollisionBox(event, x, y) === true) { 124 | this.triggerEvent(event, entity, "onCollide"); 125 | collide = true; 126 | } 127 | /** Cubic based collision */ 128 | } else { 129 | if (intersection === true) { 130 | this.triggerEvent(event, entity, "onCollide"); 131 | collide = true; 132 | } 133 | } 134 | } else { 135 | if ( 136 | math.linearIntersect( 137 | event.position.x << 0, event.position.y << 0, 138 | ((math.roundTo(event.size.x, DIMENSION) * event.scale) + event.xMargin) - DIMENSION, 139 | ((math.roundTo(event.size.y, DIMENSION) * event.scale) + event.yMargin) - DIMENSION, 140 | entity.position.x << 0, entity.position.y << 0, 141 | 1 142 | ) === true 143 | ) { 144 | this.triggerEvent(event, entity, "onLeave"); 145 | } 146 | if (intersection === true) { 147 | this.triggerEvent(event, entity, "onEnter"); 148 | } 149 | } 150 | }; 151 | 152 | return (collide); 153 | 154 | } 155 | 156 | /** 157 | * Collides with a entity collision box 158 | * @param {Number} entity 159 | * @param {Number} x 160 | * @param {Number} y 161 | * @return {Boolean} 162 | */ 163 | export function collidesWithCollisionBox(entity, x, y) { 164 | 165 | let tile = 0; 166 | 167 | let ii = 0; 168 | 169 | let xx = 0; 170 | let yy = 0; 171 | 172 | let dim = DIMENSION * entity.scale; 173 | 174 | let width = (math.roundTo(entity.size.x, DIMENSION) + entity.xMargin) / DIMENSION; 175 | let height = (math.roundTo(entity.size.y, DIMENSION) + entity.yMargin) / DIMENSION; 176 | 177 | let length = width * height; 178 | 179 | let eX = entity.position.x << 0; 180 | let eY = entity.position.y << 0; 181 | 182 | for (; ii < length; ++ii) { 183 | tile = entity.collisionBox[yy + xx]; 184 | if (tile === 1) { 185 | if ( 186 | eX + (xx * dim) === x && 187 | eY + ((yy / width) * dim) === y 188 | ) { 189 | return (true); 190 | } 191 | } 192 | ++xx; 193 | if (xx >= width) { 194 | yy += width; 195 | xx = 0; 196 | } 197 | }; 198 | 199 | return (false); 200 | 201 | } -------------------------------------------------------------------------------- /src/Engine/Map/functions.js: -------------------------------------------------------------------------------- 1 | import { 2 | ajax as $GET, 3 | getPath as getPath 4 | } from "../utils"; 5 | 6 | import math from "../../Math"; 7 | 8 | import Audio from "../Audio"; 9 | 10 | import { 11 | DIMENSION, 12 | BGM, 13 | VOLUME 14 | } from "../../cfg"; 15 | 16 | import Map from "../Map"; 17 | 18 | /** 19 | * Add a new map 20 | * @param {String} path 21 | * @param {Function} resolve 22 | */ 23 | export function addMap(path, resolve) { 24 | 25 | $GET(path).then( 26 | JSON.parse 27 | ).then(this::function(data) { 28 | data.path = getPath(path); 29 | let map = new Map(this, data, this::function() { 30 | map.instance = this; 31 | this.maps[map.name] = map; 32 | this.currentMap = this.maps[map.name]; 33 | if (this.editor !== null) { 34 | this.editor.map = this.currentMap; 35 | } 36 | if (map.settings.music && BGM) { 37 | Audio.playSong(map.settings.music, VOLUME.MUSIC, true); 38 | } 39 | /** Map name notification */ 40 | //this.notify(map, map.name + map.name + map.name + map.name, "MapMessage"); 41 | return (resolve()); 42 | }); 43 | }); 44 | 45 | } 46 | 47 | /** 48 | * Measure distance between entity and camera 49 | * @param {Object} entity 50 | * @param {Object} camera 51 | * @return {Object} 52 | */ 53 | export function distance(entity, camera) { 54 | 55 | let distance = math.distance( 56 | entity.position.x + (entity.size.x / 2) + entity.xMargin, 57 | entity.position.y + (entity.size.y / 2) + entity.position.z + entity.yMargin, 58 | (((camera.size.x / 2) - camera.position.x) / camera.resolution), 59 | (((camera.size.y / 2) - camera.position.y) / camera.resolution) 60 | ); 61 | 62 | distance.x /= 1e2; 63 | distance.y /= 1e2; 64 | 65 | return (distance); 66 | 67 | } -------------------------------------------------------------------------------- /src/Engine/Path/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | DIMENSION 3 | } from "../../cfg"; 4 | 5 | import path from "../../libs/astar"; 6 | 7 | /** 8 | * Path 9 | * @class Path 10 | * @export 11 | */ 12 | export default class Path { 13 | 14 | /** 15 | * @param {Object} instance 16 | * @constructor 17 | */ 18 | constructor(instance) { 19 | 20 | /** 21 | * Instance ref 22 | * @type {Object} 23 | */ 24 | this.instance = instance; 25 | 26 | /** 27 | * Grid 28 | * @type {Object} 29 | */ 30 | this.grid = new path.Graph(this.instance.collisionLayer.data); 31 | 32 | } 33 | 34 | /** 35 | * Get shortest path 36 | * @param {Number} x1 37 | * @param {Number} y1 38 | * @param {Number} x2 39 | * @param {Number} y2 40 | * @return {Array} 41 | */ 42 | getShortestPath(x1, y1, x2, y2) { 43 | 44 | return ( 45 | path.astar.search( 46 | this.grid, 47 | this.grid.grid[(x1 << 0) / DIMENSION][(y1 << 0) / DIMENSION], 48 | this.grid.grid[(x2 << 0) / DIMENSION][(y2 << 0) / DIMENSION] 49 | ) 50 | ); 51 | 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/Engine/Renderer/debug.js: -------------------------------------------------------------------------------- 1 | import { TextureCache } from "../utils"; 2 | import { 3 | DIMENSION, 4 | GOD_MODE, EDIT_MODE, 5 | FREE_CAMERA, 6 | SHADOW_Y 7 | } from "../../cfg"; 8 | 9 | /** 10 | * Render debug scene 11 | */ 12 | export function renderDebugScene() { 13 | 14 | let color = '#313131'; 15 | 16 | let get = (str) => this.instance.getUpperCaseString(str); 17 | 18 | this.drawPixelText( 19 | `${get("Width")}: ${this.width} ${get("Height")}: ${this.height}`, 20 | 15, 30, 21 | 20, 1.5, 22 | color 23 | ); 24 | 25 | this.drawPixelText( 26 | `${get("Dimension")}: ${DIMENSION}`, 27 | 15, 60, 28 | 20, 1.5, 29 | color 30 | ); 31 | 32 | this.drawPixelText( 33 | `${get("X")}: ${this.camera.x.toFixed(2)} ${get("Y")}: ${this.camera.y.toFixed(2)}`, 34 | 15, 90, 35 | 20, 1.5, 36 | color 37 | ); 38 | 39 | this.drawPixelText( 40 | `${get("Delta")}: ${this.delta * 1E3} ${get("MS")}`, 41 | 15, 120, 42 | 20, 1.5, 43 | color 44 | ); 45 | 46 | this.drawPixelText( 47 | `${get("Scale")}: ${this.camera.resolution.toFixed(6)}`, 48 | 15, 150, 49 | 20, 1.5, 50 | color 51 | ); 52 | 53 | if (this.instance.currentMap !== null) { 54 | this.drawPixelText( 55 | `${get("Entities")}: ${this.instance.currentMap.entities.length}`, 56 | 15, 180, 57 | 20, 1.5, 58 | color 59 | ); 60 | } 61 | 62 | let ii = 0; 63 | let kk = 0; 64 | 65 | let length = 0; 66 | 67 | if (this.instance.currentMap !== null) { 68 | 69 | let entities = this.instance.currentMap.entities; 70 | 71 | length = entities.length; 72 | 73 | for (; ii < length; ++ii) { 74 | if (this.instance.camera.isInView( 75 | entities[ii].position.x, entities[ii].position.y, 76 | entities[ii].size.x, (entities[ii].size.y * 2) + entities[ii].shadowY 77 | ) && ++kk) {} 78 | }; 79 | 80 | } 81 | 82 | this.drawPixelText( 83 | `${get("EntitiesInView")}: ${kk}`, 84 | 15, 210, 85 | 20, 1.5, 86 | color 87 | ); 88 | 89 | this.drawPixelText( 90 | `${get("Textures")}: ${Object.keys(TextureCache).length}`, 91 | 15, 240, 92 | 20, 1.5 93 | ); 94 | 95 | if (this.instance.localEntity !== null) { 96 | this.drawPixelText( 97 | `${get("Local")} ${get("X")}: ${this.instance.localEntity.x} ${get("Y")}: ${this.instance.localEntity.y.toFixed(2)} ${get("Local")} Z: ${-this.instance.localEntity.z.toFixed(4)}`, 98 | 15, 270, 99 | 20, 1.5, 100 | color 101 | ); 102 | } 103 | 104 | this.drawPixelText( 105 | `${get("CommandStack")}: ${this.instance.editor.commander.position + 1} | ${this.instance.editor.commander.stack.length}`, 106 | 15, 300, 107 | 20, 1.5, 108 | color 109 | ); 110 | 111 | this.drawPixelText( 112 | `${get("GodMode")}: ${GOD_MODE === true ? get("Enabled") : get("Disabled")}`, 113 | 15, 330, 114 | 20, 1.5, 115 | color 116 | ); 117 | 118 | this.drawPixelText( 119 | `${get("FreeCamera")}: ${FREE_CAMERA === true ? get("Enabled") : get("Disabled")}`, 120 | 15, 360, 121 | 20, 1.5, 122 | color 123 | ); 124 | 125 | this.drawPixelText( 126 | `${get("EditMode")}: ${EDIT_MODE === true ? get("Enabled") : get("Disabled")}`, 127 | 15, 390, 128 | 20, 1.5, 129 | color 130 | ); 131 | 132 | } -------------------------------------------------------------------------------- /src/Engine/Renderer/grid.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Draw a grid 3 | * @param {Object} ctx 4 | * @param {Number} x 5 | * @param {Number} y 6 | * @param {Number} width 7 | * @param {Number} height 8 | * @param {Number} dim 9 | * @param {Number} scale 10 | * @param {Number} ln 11 | * @param {String} color 12 | */ 13 | export function drawGrid(ctx, x, y, width, height, dim, scale, ln, color, xPad, yPad) { 14 | 15 | let ww = dim * scale; 16 | let hh = dim * scale; 17 | 18 | let xx = x % ww; 19 | let yy = y % hh; 20 | 21 | ctx.beginPath(); 22 | 23 | for (; xx < width; xx += ww) { 24 | ctx.moveTo((xx - ln) + xPad, yPad); 25 | ctx.lineTo((xx - ln) + xPad, height + yPad); 26 | }; 27 | 28 | for (; yy < height; yy += hh) { 29 | ctx.moveTo(xPad, (yy + ln) + yPad); 30 | ctx.lineTo(width + xPad, (yy + ln) + yPad); 31 | }; 32 | 33 | ctx.strokeStyle = color; 34 | ctx.lineWidth = ln; 35 | 36 | ctx.stroke(); 37 | 38 | ctx.closePath(); 39 | 40 | return void 0; 41 | 42 | } -------------------------------------------------------------------------------- /src/Engine/Renderer/index.js: -------------------------------------------------------------------------------- 1 | import "../../polyfill"; 2 | import math from "../../Math"; 3 | 4 | import * as cfg from "../../cfg"; 5 | 6 | import { inherit } from "../utils"; 7 | 8 | import * as entity from "../Entity/functions"; 9 | import * as render from "./render"; 10 | import * as debug from "./debug"; 11 | 12 | import WGL_Renderer from "./webgl"; 13 | 14 | /** 15 | * Renderer 16 | * @class Renderer 17 | * @export 18 | */ 19 | export default class Renderer { 20 | 21 | /** 22 | * @param {Object} instance 23 | * @constructor 24 | */ 25 | constructor(instance) { 26 | 27 | /** 28 | * Instance ref 29 | * @type {Object} 30 | */ 31 | this.instance = instance; 32 | 33 | /** 34 | * WebGL renderer 35 | * @type {Object} 36 | */ 37 | this.glRenderer = null; 38 | 39 | /** 40 | * Size 41 | * @type {Object} 42 | */ 43 | this.size = instance.size; 44 | 45 | /** 46 | * Layers ref 47 | * @type {Object} 48 | */ 49 | this.layers = instance.layers; 50 | 51 | /** 52 | * Node ref 53 | * @type {Object} 54 | */ 55 | this.node = instance.node; 56 | 57 | /** 58 | * WebGL node ref 59 | * @type {Object} 60 | */ 61 | this.glNode = instance.glNode; 62 | 63 | /** 64 | * Context ref 65 | * @type {Object} 66 | */ 67 | this.context = instance.context; 68 | 69 | /** 70 | * Gl context ref 71 | * @type {Object} 72 | */ 73 | this.gl = instance.glContext; 74 | 75 | /** 76 | * Image smoothing 77 | * @type {Boolean} 78 | */ 79 | this.imageSmoothing = false; 80 | 81 | /** 82 | * Dimension 83 | * @type {Number} 84 | */ 85 | this.dimension = cfg.DIMENSION; 86 | 87 | /** 88 | * Delta timer 89 | * @type {Number} 90 | */ 91 | this.delta = 0; 92 | 93 | /** 94 | * Now timestamp 95 | * @type {Number} 96 | */ 97 | this.now = 0; 98 | 99 | /** 100 | * Then timestamp 101 | * @type {Number} 102 | */ 103 | this.then = 0; 104 | 105 | /** 106 | * Width 107 | * @type {Number} 108 | */ 109 | this.width = 0; 110 | 111 | /** 112 | * Height 113 | * @type {Number} 114 | */ 115 | this.height = 0; 116 | 117 | /** 118 | * Camera ref 119 | * @type {Object} 120 | */ 121 | this.camera = instance.camera; 122 | 123 | if (cfg.WGL_SUPPORT) { 124 | this.glRenderer = new WGL_Renderer(this); 125 | this.glRenderer.init(); 126 | } 127 | 128 | /** 129 | * Auto switch to current game mode dependant rendering 130 | */ 131 | this.switchRenderingMode(cfg.DEBUG_MODE ? 0 : 1); 132 | 133 | this.resize(false); 134 | 135 | } 136 | 137 | /** 138 | * Switch rendering mode 139 | * @param {Number} mode 140 | */ 141 | switchRenderingMode(mode) { 142 | 143 | if (mode === cfg.WGL) { 144 | if (cfg.WGL_SUPPORT) { 145 | this.node.style.display = "none"; 146 | this.glNode.style.display = "block"; 147 | cfg.RENDER_MODE = mode; 148 | } else { 149 | mode = cfg.CANVAS; 150 | } 151 | } 152 | 153 | if (mode === cfg.CANVAS) { 154 | this.node.style.display = "block"; 155 | this.glNode.style.display = "none"; 156 | cfg.RENDER_MODE = mode; 157 | } 158 | 159 | } 160 | 161 | /** 162 | * @param {Boolean} value 163 | * @setter 164 | */ 165 | set imageSmoothingEnabled(value) { 166 | 167 | value = value ? true : false; 168 | 169 | this.imageSmoothing = value; 170 | 171 | this.context.setImageSmoothing(value); 172 | 173 | } 174 | 175 | /** 176 | * Update 177 | */ 178 | update() { 179 | 180 | this.updateTimers(); 181 | 182 | if (this.camera.objectFocus !== null) { 183 | this.camera.animate(this.camera.objectFocus); 184 | } 185 | 186 | return void 0; 187 | 188 | } 189 | 190 | /** 191 | * Update timers 192 | */ 193 | updateTimers() { 194 | this.now = Date.now(); 195 | this.delta = (this.now - this.then) / 1e3; 196 | this.then = this.now; 197 | return void 0; 198 | } 199 | 200 | /** 201 | * Resize 202 | * @param {Boolean} redraw 203 | */ 204 | resize(redraw) { 205 | this.width = window.innerWidth; 206 | this.height = window.innerHeight; 207 | this.camera.width = this.width; 208 | this.camera.height = this.height; 209 | this.instance.width = this.width; 210 | this.instance.height = this.height; 211 | if (cfg.RENDER_MODE === cfg.WGL) { 212 | this.glNode.width = this.width; 213 | this.glNode.height = this.height; 214 | this.glRenderer.resize(this.width, this.height); 215 | } else { 216 | this.node.width = this.width; 217 | this.node.height = this.height; 218 | } 219 | this.clear(); 220 | this.imageSmoothingEnabled = this.imageSmoothing; 221 | this.instance.mini.resize(); 222 | this.instance.editor.updateTilesetPosition(); 223 | this.draw(); 224 | } 225 | 226 | } 227 | 228 | inherit(Renderer, debug); 229 | inherit(Renderer, render); 230 | inherit(Renderer, entity); 231 | inherit(Renderer, webgl); -------------------------------------------------------------------------------- /src/Engine/Renderer/webgl/shaders.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Thanks to mattdesl for the 3 | * normal mappping thingys 4 | */ 5 | export const spritevs = ` 6 | 7 | precision lowp float; 8 | 9 | uniform vec2 uScale; 10 | uniform vec2 uEntityScale; 11 | attribute vec2 aObjCen; 12 | attribute float aObjRot; 13 | attribute float aIdx; 14 | varying vec2 uv; 15 | 16 | void main(void) { 17 | if (aIdx == 0.0) { 18 | uv = vec2(0.0,0.0); 19 | } else if (aIdx == 1.0) { 20 | uv = vec2(1.0,0.0); 21 | } else if (aIdx == 2.0) { 22 | uv = vec2(0.0,1.0); 23 | } else { 24 | uv = vec2(1.0,1.0); 25 | } 26 | vec2 pos = vec2( 27 | aObjCen.x + sin(aObjRot)*uEntityScale.y*(-0.5 + uv.y) 28 | + cos(aObjRot)*uEntityScale.x*(-0.5 + uv.x), 29 | aObjCen.y + cos(aObjRot)*uEntityScale.y*(-0.5 + uv.y) 30 | - sin(aObjRot)*uEntityScale.x*(-0.5 + uv.x) 31 | ); 32 | gl_Position = vec4( 33 | -1.0 + 2.0*pos.x/uScale.x, 34 | 1.0 - 2.0*pos.y/uScale.y, 35 | 0.0, 1.0 36 | ); 37 | } 38 | 39 | `; 40 | 41 | export const spritefs = ` 42 | 43 | precision lowp float; 44 | 45 | #define STEP_A 0.4 46 | #define STEP_B 0.6 47 | #define STEP_C 0.8 48 | #define STEP_D 1.0 49 | 50 | uniform sampler2D u_texture0; 51 | uniform sampler2D u_normals; 52 | 53 | varying vec2 uv; 54 | 55 | uniform vec4 AmbientColor; 56 | uniform vec2 Resolution; 57 | 58 | uniform float Opacity; 59 | 60 | uniform float LightSize; 61 | uniform bool SoftLight; 62 | uniform vec3 LightPos; 63 | uniform vec4 LightColor; 64 | uniform vec3 Falloff; 65 | 66 | void main() { 67 | 68 | vec3 Sum = vec3(0.0); 69 | 70 | vec4 DiffuseColor = texture2D(u_texture0, uv); 71 | 72 | vec3 NormalMap = texture2D(u_normals, uv).rgb; 73 | 74 | vec3 LightDir = vec3(LightPos.xy - (gl_FragCoord.xy / Resolution.xy), LightPos.z); 75 | 76 | LightDir.x /= (LightSize / Resolution.x); 77 | LightDir.y /= (LightSize / Resolution.y); 78 | 79 | float D = length(LightDir); 80 | 81 | vec3 N = normalize(NormalMap * 2.0 - 1.0); 82 | vec3 L = normalize(LightDir); 83 | 84 | N = mix(N, vec3(0), 0.5); 85 | 86 | float df = max(dot(N, L), 0.0); 87 | 88 | vec3 Diffuse = (LightColor.rgb * LightColor.a) * df; 89 | 90 | vec3 Ambient = AmbientColor.rgb * AmbientColor.a; 91 | 92 | float Attenuation = 1.0 / ( Falloff.x + (Falloff.y*D) + (Falloff.z*D*D) ); 93 | 94 | if (SoftLight == false) { 95 | if (Attenuation < STEP_A) Attenuation = 0.0; 96 | else if (Attenuation < STEP_B) Attenuation = STEP_B; 97 | else if (Attenuation < STEP_C) Attenuation = STEP_C; 98 | else Attenuation = STEP_D; 99 | } 100 | 101 | vec3 Intensity = Ambient + Diffuse * Attenuation; 102 | vec3 FinalColor = DiffuseColor.rgb * Intensity; 103 | 104 | Sum += FinalColor; 105 | 106 | if (SoftLight == false) { 107 | gl_FragColor = vec4(Sum, DiffuseColor.a * Opacity); 108 | } else { 109 | gl_FragColor = vec4(FinalColor, DiffuseColor.a * Opacity); 110 | } 111 | 112 | if (gl_FragColor.a < 0.1) discard; 113 | 114 | } 115 | 116 | `; 117 | 118 | export const outlinefs = ` 119 | 120 | precision lowp float; 121 | 122 | #define PI 3.14159265359 123 | #define WIDTH 10.0 124 | #define COLOR vec4(0.0,0.0,0.0,1.0) 125 | #define NUM_FRAMES 6.0 126 | 127 | uniform sampler2D u_texture0; 128 | 129 | varying vec2 uv; 130 | uniform vec2 uScale; 131 | uniform vec2 uEntityScale; 132 | 133 | void main() { 134 | 135 | vec2 point = vec2( (WIDTH/uEntityScale.x)*cos(PI), (WIDTH/uEntityScale.y)*sin(PI)); 136 | point = clamp(uv + point, vec2(0.0), vec4(uEntityScale.xy, uEntityScale.xy).zw ); 137 | float sampledAlpha = texture2D(u_texture0, point).a; 138 | float outlineAlpha = max(0.0, sampledAlpha); 139 | 140 | gl_FragColor = mix(vec4(0.0), COLOR, outlineAlpha); 141 | 142 | vec4 tex0 = texture2D(u_texture0, uv); 143 | gl_FragColor = mix(gl_FragColor, tex0, tex0.a); 144 | 145 | if (gl_FragColor.a < 0.5) discard; 146 | 147 | } 148 | 149 | `; -------------------------------------------------------------------------------- /src/Engine/Shadow/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | DIMENSION, 3 | LEFT, RIGHT, UP, DOWN, 4 | SHADOW_ALPHA 5 | } from "../../cfg"; 6 | 7 | import math from "../../Math"; 8 | 9 | import DisplayObject from "../DisplayObject"; 10 | 11 | import { TextureCache, uHash, createCanvasBuffer } from "../utils"; 12 | import { colorizePixels } from "../Texture/effects"; 13 | 14 | /** 15 | * Shadow 16 | * @class Shadow 17 | * @export 18 | */ 19 | export default class Shadow extends DisplayObject { 20 | 21 | /** 22 | * @param {Object} parent 23 | * @constructor 24 | */ 25 | constructor(parent) { 26 | 27 | super(null); 28 | 29 | /** 30 | * Parent ref 31 | * @type {Object} 32 | */ 33 | this.parent = parent; 34 | 35 | /** 36 | * Texture 37 | * @type {Object} 38 | */ 39 | this.texture = null; 40 | 41 | /** 42 | * GL texture 43 | * @type {Object} 44 | */ 45 | this.glTexture = null; 46 | 47 | /** 48 | * Splitted sprites 49 | * @type {Array} 50 | */ 51 | this.sprites = []; 52 | 53 | this.scale.set(0, 0); 54 | 55 | this.init(); 56 | 57 | } 58 | 59 | /** 60 | * Initialise 61 | * Build shadow 62 | */ 63 | init() { 64 | 65 | this.texture = this.buildShadow(); 66 | 67 | } 68 | 69 | /** 70 | * Build shadow 71 | * @return {Object} 72 | */ 73 | buildShadow() { 74 | 75 | let ii = 0; 76 | let length = 0; 77 | 78 | let buffer = null; 79 | 80 | let parent = this.parent.texture; 81 | 82 | let width = 0; 83 | let height = 0; 84 | 85 | length = parent.sprites.length; 86 | 87 | for (; ii < length; ++ii) { 88 | width = parent.sprites[ii].canvas.width; 89 | height = parent.sprites[ii].canvas.height; 90 | buffer = createCanvasBuffer(width, height); 91 | buffer.translate(0, height); 92 | buffer.scale(1, -1); 93 | this.drawShadow( 94 | parent.sprites[ii], 95 | buffer, 96 | width, height 97 | ); 98 | buffer.setTransform(1, 0, 0, 1, 0, 0); 99 | this.sprites[ii] = buffer; 100 | buffer = null; 101 | }; 102 | 103 | return (this); 104 | 105 | } 106 | 107 | /** 108 | * Create shadow of a sprite 109 | * @param {Object} buffer 110 | * @param {Object} ctx 111 | * @param {Number} width 112 | * @param {Number} height 113 | */ 114 | drawShadow(buffer, ctx, width, height) { 115 | 116 | ctx.clear(); 117 | 118 | ctx.drawImage( 119 | buffer.canvas, 120 | 0, 0, 121 | width, height 122 | ); 123 | 124 | this.drawTint( 125 | ctx, 126 | 0, 0, 127 | width, height, 128 | SHADOW_ALPHA * 1e2 129 | ); 130 | 131 | } 132 | 133 | /** 134 | * Tint a canvas 135 | * @param {Object} ctx 136 | * @param {Number} x 137 | * @param {Number} y 138 | * @param {Number} width 139 | * @param {Number} height 140 | * @param {Number} value 141 | */ 142 | drawTint(ctx, x, y, width, height, value) { 143 | 144 | let imgData = ctx.getImageData( 145 | x, y, 146 | width, height 147 | ); 148 | 149 | colorizePixels( 150 | imgData, 151 | 0, 152 | 0, 153 | value, 154 | true 155 | ); 156 | 157 | ctx.putImageData( 158 | imgData, 159 | x, y 160 | ); 161 | 162 | } 163 | 164 | } -------------------------------------------------------------------------------- /src/Engine/Texture/effects.js: -------------------------------------------------------------------------------- 1 | import { 2 | getTime 3 | } from "../utils"; 4 | 5 | /** 6 | * Draw time based lightning 7 | * @param {Object} buffer 8 | * @param {Object} ctx 9 | * @param {Number} x 10 | * @param {Number} y 11 | * @param {Number} width 12 | * @param {Number} height 13 | * @param {Array} colors 14 | */ 15 | export function drawTimeLightning(buffer, ctx, x, y, width, height, colors) { 16 | 17 | let hour = 18/*getTime().hours*/; 18 | 19 | let imgData = buffer.getImageData( 20 | x, y, 21 | width, height 22 | ); 23 | 24 | this.colorizePixels( 25 | imgData, 26 | colors[hour][0] / 100, 27 | colors[hour][1] / 100, 28 | colors[hour][2] / 100, 29 | false 30 | ); 31 | 32 | ctx.putImageData( 33 | imgData, 34 | x, y 35 | ); 36 | 37 | return void 0; 38 | 39 | }; 40 | 41 | /** 42 | * Colorize pixels 43 | * @param {Object} imgData 44 | * @param {Number} r 45 | * @param {Number} g 46 | * @param {Number} b 47 | * @param {Boolean} strict 48 | */ 49 | export function colorizePixels(imgData, r, g, b, strict) { 50 | 51 | let ii = 0; 52 | let length = 0; 53 | 54 | let pixels = imgData.data; 55 | 56 | length = pixels.length; 57 | 58 | if (strict) { 59 | for (; ii < length / 4; ++ii) { 60 | if (pixels[ii * 4] > 0) { 61 | pixels[ii * 4] = g; 62 | } 63 | if (pixels[ii * 4 + 1] > 0) { 64 | pixels[ii * 4 + 1] = r; 65 | } 66 | if (pixels[ii * 4 + 2] > 0) { 67 | pixels[ii * 4 + 2] = g; 68 | } 69 | if (pixels[ii * 4 + 3] > 2) { 70 | pixels[ii * 4 + 3] = b; 71 | } 72 | }; 73 | } else { 74 | for (; ii < length / 4; ++ii) { 75 | pixels[ii * 4 + 1] = pixels[ii * 4 + 1] / r; 76 | pixels[ii * 4 + 2] = pixels[ii * 4 + 2] / g; 77 | pixels[ii * 4 + 3] = pixels[ii * 4 + 3] * b; 78 | }; 79 | } 80 | 81 | return void 0; 82 | 83 | } -------------------------------------------------------------------------------- /src/Engine/Texture/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | DEV_MODE, 3 | ColorPalette 4 | } from "../../cfg"; 5 | 6 | import { 7 | inherit, 8 | TextureCache, 9 | createCanvasBuffer, 10 | imageToCanvas, 11 | antiCache 12 | } from "../utils"; 13 | 14 | import * as effect from "./effects"; 15 | 16 | /** 17 | * Texture 18 | * @class Texture 19 | * @export 20 | */ 21 | export default class Texture { 22 | 23 | /** 24 | * @param {String} url 25 | * @param {Number} width 26 | * @param {Number} height 27 | * @param {Function} resolve 28 | * @constructor 29 | */ 30 | constructor(url, width, height, resolve) { 31 | 32 | /** 33 | * Texture 34 | * @type {Object} 35 | */ 36 | this.texture = null; 37 | 38 | /** 39 | * Texture effect 40 | * @type {Object} 41 | */ 42 | this.texture_effect = null; 43 | 44 | /** 45 | * Effect texture 46 | * @type {Object} 47 | */ 48 | this.effect_sprites = []; 49 | 50 | /** 51 | * Splitted sprites 52 | * @type {Array} 53 | */ 54 | this.sprites = []; 55 | 56 | /** 57 | * Image url 58 | * @type {String} 59 | */ 60 | this.imgUrl = url; 61 | 62 | /** 63 | * Width 64 | * @type {Number} 65 | */ 66 | this.width = 0; 67 | 68 | /** 69 | * Height 70 | * @type {Number} 71 | */ 72 | this.height = 0; 73 | 74 | /** 75 | * Sprite width 76 | * @type {Number} 77 | */ 78 | this.sWidth = width; 79 | 80 | /** 81 | * Sprite height 82 | * @type {Number} 83 | */ 84 | this.sHeight = height; 85 | 86 | /** 87 | * X multiplicator 88 | * @type {Number} 89 | */ 90 | this.xMul = 0; 91 | 92 | /** 93 | * Y multiplicator 94 | * @type {Number} 95 | */ 96 | this.yMul = 0; 97 | 98 | /** 99 | * Loading state 100 | * @type {Boolean} 101 | */ 102 | this.hasLoaded = false; 103 | 104 | this.fromImage(this.imgUrl, this::function() { 105 | resolve(this); 106 | }); 107 | 108 | } 109 | 110 | /** 111 | * @param {String} url 112 | * @param {Function} resolve 113 | */ 114 | fromImage(url, resolve) { 115 | 116 | let img = null; 117 | 118 | let texture = TextureCache[url]; 119 | 120 | if ( 121 | texture !== void 0 && 122 | texture instanceof Texture 123 | ) { 124 | this.hasLoaded = true; 125 | return (TextureCache[url]); 126 | } 127 | 128 | img = new Image(); 129 | 130 | img.addEventListener('load', this::function() { 131 | this.width = img.width; 132 | this.height = img.height; 133 | this.hasLoaded = true; 134 | this.texture = imageToCanvas(img); 135 | this.splitTexture(); 136 | TextureCache[url] = this; 137 | this.renderEffects(); 138 | resolve(); 139 | }); 140 | 141 | if (DEV_MODE === true) { 142 | img.src = url + antiCache(); 143 | } else { 144 | img.src = url; 145 | } 146 | 147 | return void 0; 148 | 149 | } 150 | 151 | /** 152 | * Split texture into seperate sprites 153 | */ 154 | splitTexture() { 155 | 156 | if (this.sWidth === -1 && this.sHeight === -1) { 157 | this.sWidth = this.width / 2; 158 | this.sHeight = this.height / 2; 159 | } 160 | 161 | this.xMul = this.height / (this.sWidth * 2); 162 | this.yMul = this.width / (this.sHeight * 2); 163 | 164 | let buffer = null; 165 | 166 | let ii = 0; 167 | 168 | let xx = 0; 169 | let yy = 0; 170 | 171 | let width = this.width / (this.sWidth * 2); 172 | let height = this.height / (this.sHeight * 2); 173 | 174 | for (; yy < height;) { 175 | for (xx = 0; xx < width; ++xx) { 176 | if (xx === 0) ++yy; 177 | buffer = createCanvasBuffer(this.sWidth * 2, this.sHeight * 2); 178 | buffer.drawImage( 179 | this.texture.canvas, 180 | (this.sWidth * 2) * xx, (this.sHeight * 2) * (yy - 1), 181 | this.width, this.height, 182 | 0, 0, 183 | this.width, this.height 184 | ); 185 | this.sprites.push(buffer); 186 | buffer = null; 187 | }; 188 | }; 189 | 190 | } 191 | 192 | /** 193 | * Render texture effects 194 | */ 195 | renderEffects() { 196 | this.buildTimeLightning(); 197 | } 198 | 199 | /** 200 | * Build texture time lightning 201 | */ 202 | buildTimeLightning() { 203 | 204 | let ii = 0; 205 | let length = 0; 206 | 207 | let buffer = null; 208 | 209 | let width = 0; 210 | let height = 0; 211 | 212 | length = this.sprites.length; 213 | 214 | for (; ii < length; ++ii) { 215 | width = this.sprites[ii].canvas.width; 216 | height = this.sprites[ii].canvas.height; 217 | buffer = createCanvasBuffer(width, height); 218 | buffer.translate(0, height); 219 | buffer.scale(1, -1); 220 | this.drawTimeLightning( 221 | this.sprites[ii], 222 | buffer, 223 | 0, 0, 224 | width, height, 225 | ColorPalette 226 | ); 227 | buffer.setTransform(1, 0, 0, 1, 0, 0); 228 | this.effect_sprites[ii] = buffer; 229 | buffer = null; 230 | }; 231 | 232 | } 233 | 234 | } 235 | 236 | inherit(Texture, effect); -------------------------------------------------------------------------------- /src/Engine/logic.js: -------------------------------------------------------------------------------- 1 | import { 2 | DIMENSION, 3 | GRAVITY, 4 | TYPES 5 | } from "../cfg"; 6 | 7 | /** 8 | * Logic loop 9 | */ 10 | export function logic() { 11 | 12 | if (this.currentMap === null) return void 0; 13 | 14 | /** Depth sort entities */ 15 | this.sort(); 16 | 17 | let ii = 0; 18 | let length = 0; 19 | 20 | let entity = null; 21 | let entities = this.currentMap.entities; 22 | 23 | length = entities.length; 24 | 25 | for (; ii < length; ++ii) { 26 | entity = entities[ii]; 27 | entity.idleTime++; 28 | entity.renderable = this.updateEntity(entity); 29 | if (entity.type === TYPES.Notification) { 30 | this.updateNotification(entity); 31 | } 32 | if (entity.opacity < 0) { 33 | this.removeEntity(entity); 34 | --length; 35 | --ii; 36 | continue; 37 | } 38 | if (entity.noise === null) continue; 39 | this.updateEntityNoise(entity, this.currentMap.distance(entity, this.camera)); 40 | }; 41 | 42 | return void 0; 43 | 44 | } 45 | 46 | /** 47 | * Update notification 48 | * @param {Object} entity 49 | */ 50 | export function updateNotification(entity) { 51 | 52 | entity.xPadding = ( 53 | ( 54 | (Math.max(entity.follow.size.x, entity.size.x / 2) / 2) - entity.follow.size.x / 2 55 | ) 56 | - entity.follow.xMargin - (entity.size.x === entity.follow.size.x ? DIMENSION : 0) 57 | ); 58 | 59 | entity.yPadding = entity.size.y; 60 | 61 | } 62 | 63 | /** 64 | * Orbit animation 65 | * @param {Object} entity 66 | */ 67 | export function orbit(entity) { 68 | 69 | entity.orbitAngle += (entity.velocity * 2) * Math.PI / 180; 70 | 71 | let target = entity.orbitTarget; 72 | 73 | let radius = ((target.size.x * target.scale + target.size.y * target.scale) / DIMENSION) * 2; 74 | 75 | let xPadding = radius - (DIMENSION / 2); 76 | let yPadding = radius - (DIMENSION / 2); 77 | 78 | xPadding += target.xMargin; 79 | yPadding += target.yMargin / 2; 80 | 81 | entity.x = (target.position.x + xPadding) + radius * Math.cos(entity.orbitAngle); 82 | entity.y = (target.position.y + yPadding) + radius * Math.sin(entity.orbitAngle); 83 | 84 | /** Stop the orbit on a dimension friendly position */ 85 | if ( 86 | entity.stopOrbit === true && 87 | (entity.x << 0) % 8 === 0 && 88 | (entity.y << 0) % 8 === 0 89 | ) { 90 | entity.x = math.roundTo(entity.x, DIMENSION); 91 | entity.y = math.roundTo(entity.y, DIMENSION); 92 | entity.orbitAround(null); 93 | entity.stopOrbit = false; 94 | } 95 | 96 | /*if (entity.orbitAngle > 360) { 97 | entity.orbitAngle = 0; 98 | }*/ 99 | 100 | return void 0; 101 | 102 | } 103 | 104 | /** 105 | * Floating animation 106 | * TODO: ENTITY CAN ONLY WALK IF ON FLOOR (Z =^ 0) 107 | * @param {Object} entity 108 | */ 109 | export function float(entity) { 110 | 111 | entity.z += entity.gravity / 5; 112 | 113 | if (entity.z < 0) { 114 | entity.gravity += (.1 / 5); 115 | } else { 116 | entity.gravity = GRAVITY; 117 | entity.z = 0; 118 | entity.updateShadow(); 119 | entity.refreshState(); 120 | } 121 | 122 | entity.updateShadow(); 123 | 124 | return void 0; 125 | 126 | } 127 | 128 | /** 129 | * Update entity 130 | * @param {Object} entity 131 | * @return {Boolean} renderable 132 | */ 133 | export function updateEntity(entity) { 134 | 135 | if (entity.lifeTime > 0) { 136 | if (this.renderer.now >= entity.lifeTime) { 137 | entity.lifeTime = 0; 138 | entity.fadeOut(1, true); 139 | } 140 | } 141 | 142 | entity.scaling = entity.scale + (-entity.position.z / this.camera.resolution) / ((entity.size.x + entity.size.y) / 2); 143 | 144 | entity.animate(); 145 | 146 | if (entity.orbit === true) { 147 | this.orbit(entity); 148 | } 149 | 150 | if (entity.floating === true) { 151 | this.float(entity); 152 | } 153 | 154 | if (entity.z !== 0) { 155 | entity.updateShadow(); 156 | } 157 | 158 | if (entity.absolute === true) { 159 | return ( 160 | this.isRenderable(entity) === true 161 | ); 162 | } 163 | 164 | if (this.camera.isInView( 165 | entity.position.x + entity.xMargin, entity.position.y + entity.yMargin, 166 | entity.size.x * entity.scale, ((entity.size.y * 2) * entity.scale) + entity.shadowY 167 | ) === false) { 168 | return (false); 169 | } 170 | 171 | if (this.isRenderable(entity) === false) { 172 | return (false); 173 | } 174 | 175 | this.renderer.updateEntitySpriteFrame(entity); 176 | 177 | return (true); 178 | 179 | } 180 | 181 | /** 182 | * Entity is renderable 183 | * @param {Object} entity 184 | * @return {Boolean} renderable 185 | */ 186 | export function isRenderable(entity) { 187 | return ( 188 | entity.texture !== null && 189 | entity.opacity !== .0 190 | ); 191 | } -------------------------------------------------------------------------------- /src/Engine/sound.js: -------------------------------------------------------------------------------- 1 | import { 2 | DIMENSION, 3 | VOLUME, 4 | BGS 5 | } from "../cfg"; 6 | 7 | import math from "../Math"; 8 | 9 | import Audio from "./Audio"; 10 | 11 | /** 12 | * Update entity noise 13 | * @param {Object} entity 14 | * @param {Object} distance 15 | */ 16 | export function updateEntityNoise(entity, dist) { 17 | 18 | let radius = 0; 19 | 20 | let cx = 0; 21 | let cy = 0; 22 | let dx = 0; 23 | let dy = 0; 24 | 25 | if (BGS === false) { 26 | dist.x = 99999; 27 | dist.y = 99999; 28 | radius = 0; 29 | if (entity.noise._audioNode !== void 0) { 30 | entity.noise._audioNode[0].gain.value = 0; 31 | } 32 | } 33 | 34 | radius = (entity.noiseRadius - DIMENSION) || DIMENSION; 35 | cx = radius / 2; 36 | cy = radius / 2; 37 | dx = Math.floor(dist.x * 1e2) + cx; 38 | dy = Math.floor(dist.y * 1e2) + cy; 39 | 40 | if (entity.STATES.NOISE === false) { 41 | entity.noiseSrcPath = entity.noise; 42 | entity.noise = Audio.playNoise(entity.noise, VOLUME.ENTITY_NOISE, dist.x, dist.y); 43 | entity.STATES.NOISE = true; 44 | } 45 | 46 | if (math.pointIntersectsCircle(dx, dy, cx, cy, radius) === true) { 47 | if (entity.noise.isInView === false) { 48 | let gainNode = entity.noise._audioNode[0]; 49 | entity.noise.fadingIn = true; 50 | let start = gainNode.context.currentTime; 51 | let end = start + 1; 52 | gainNode.gain.linearRampToValueAtTime(gainNode.gain.value, start); 53 | gainNode.gain.linearRampToValueAtTime(VOLUME.ENTITY_NOISE / 1e2, end); 54 | entity.noise.isInView = true; 55 | } 56 | } else { 57 | if (entity.noise.isInView === true) { 58 | let gainNode = entity.noise._audioNode[0]; 59 | entity.noise.fadingOut = true; 60 | let start = gainNode.context.currentTime; 61 | let end = start + 1; 62 | gainNode.gain.linearRampToValueAtTime(gainNode.gain.value, start); 63 | gainNode.gain.linearRampToValueAtTime(.0, end); 64 | entity.noise.isInView = false; 65 | } 66 | } 67 | 68 | entity.noise.pos3d(dist.x, dist.y, 0); 69 | 70 | return void 0; 71 | 72 | } -------------------------------------------------------------------------------- /src/Engine/utils/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | DEV_MODE 3 | } from "../../cfg"; 4 | 5 | import Texture from "../Texture"; 6 | 7 | let rx = { 8 | path: /[^\\/]+$/ 9 | }; 10 | 11 | /** 12 | * Cached textures 13 | * @type {Object} 14 | */ 15 | export let TextureCache = {}; 16 | 17 | let hashIndex = -1; 18 | let hashes = []; 19 | 20 | /** 21 | * Parsed maps 22 | * @type {Object} 23 | */ 24 | export let Maps = {}; 25 | 26 | /** 27 | * Check if webgl is supported 28 | * @return {Boolean} 29 | */ 30 | export function supportWGL() { 31 | 32 | let canvas = null; 33 | 34 | try { 35 | canvas = document.createElement("canvas"); 36 | if (WebGLRenderingContext !== void 0) { 37 | return (!!getWGLContext(canvas)); 38 | } 39 | } catch(e) { 40 | return (false); 41 | }; 42 | 43 | return (false); 44 | 45 | } 46 | 47 | /** 48 | * Converts a string into corresponding type 49 | * @param {String} value 50 | * @return {*} 51 | */ 52 | export function parseString(value) { 53 | 54 | let isNumber = Number(value) >= 0 || Number(value) < 0; 55 | let isBoolean = value === "true" || value === "false"; 56 | let isString = !isNumber && !isBoolean; 57 | 58 | return ( 59 | isNumber ? Number(value) : 60 | isBoolean ? value === "true" : 61 | isString ? value : 62 | null 63 | ); 64 | 65 | } 66 | 67 | /** 68 | * Get local host 69 | * @return {String} 70 | */ 71 | export function getLocalHost() { 72 | if (typeof document === "undefined") return void 0; 73 | return ( 74 | document.location.host.replace(/:.*/, "") 75 | ); 76 | } 77 | 78 | /** 79 | * Get wgl context of a canvas 80 | * @return {Object} 81 | */ 82 | export function getWGLContext(canvas) { 83 | let options = { 84 | alpha: false, 85 | antialias: false, 86 | premultipliedAlpha: false, 87 | stencil: false, 88 | preserveDrawingBuffer: false 89 | }; 90 | return ( 91 | canvas.getContext("webgl", options) || 92 | canvas.getContext("experimental-webgl", options) 93 | ); 94 | } 95 | 96 | /** 97 | * Get a sprite 98 | * @param {String} sprite 99 | * @param {Number} width 100 | * @param {Number} height 101 | * @param {Function} resolve 102 | */ 103 | export function getSprite(sprite, width, height, resolve) { 104 | 105 | if (TextureCache[sprite]) { 106 | resolve(TextureCache[sprite]); 107 | return void 0; 108 | } 109 | 110 | new Texture(sprite, width, height, function(instance) { 111 | resolve(TextureCache[sprite] = instance); 112 | }); 113 | 114 | return void 0; 115 | 116 | } 117 | 118 | /** 119 | * Generate a unique hash 120 | * @export 121 | */ 122 | export function uHash() { 123 | 124 | let index = ++hashIndex; 125 | 126 | if (hashes.indexOf(index) > -1) return (this.uHash()); 127 | 128 | hashes.push(index); 129 | 130 | return (index); 131 | 132 | } 133 | 134 | /** 135 | * Get path without file ext 136 | * @param {String} path 137 | * @return {String} 138 | */ 139 | export function getPath(path) { 140 | return ( 141 | path.replace(rx.path.exec(path)[0], "") 142 | ); 143 | } 144 | 145 | /** 146 | * @param {Object} cls 147 | * @param {Object} prot 148 | * @export 149 | */ 150 | export function inherit(cls, prot) { 151 | 152 | let key = null; 153 | 154 | for (key in prot) { 155 | if (prot[key] instanceof Function) { 156 | cls.prototype[key] = prot[key]; 157 | } 158 | }; 159 | 160 | } 161 | 162 | /** 163 | * @param {Number} width 164 | * @param {Number} height 165 | */ 166 | export function createCanvasBuffer(width, height) { 167 | 168 | let canvas = document.createElement("canvas"); 169 | let ctx = canvas.getContext("2d"); 170 | 171 | ctx.setImageSmoothing(false); 172 | 173 | canvas.width = width; 174 | canvas.height = height; 175 | 176 | return (ctx); 177 | 178 | } 179 | 180 | /** 181 | * @param {Object} img 182 | * @return {Object} 183 | */ 184 | export function imageToCanvas(img) { 185 | 186 | let ctx = createCanvasBuffer( 187 | img.width, img.height 188 | ); 189 | 190 | ctx.drawImage( 191 | img, 192 | 0, 0, 193 | img.width, img.height 194 | ); 195 | 196 | return (ctx); 197 | 198 | } 199 | 200 | /** 201 | * @param {Object} canvas 202 | * @return {Object} 203 | */ 204 | export function canvasToImage(canvas) { 205 | 206 | let image = new Image(); 207 | 208 | image.src = canvas.toDataURL("image/png"); 209 | 210 | return (image); 211 | 212 | } 213 | 214 | /** 215 | * Check if a tile contains any image data 216 | * @param {Object} ctx 217 | * @param {Number} x 218 | * @param {Number} y 219 | * @param {Number} width 220 | * @param {Number} height 221 | * @return {Boolean} 222 | */ 223 | export function tileContainsImageData(ctx, x, y, width, height) { 224 | 225 | let ii = 0; 226 | let length = 0; 227 | 228 | let data = ctx.getImageData(x * 2, y * 2, width * 2, height * 2).data; 229 | 230 | length = data.length; 231 | 232 | for (; ii < length; ii += 4) { 233 | if (data[ii] > 0) return (true); 234 | if (data[ii + 1] > 0) return (true); 235 | if (data[ii + 2] > 0) return (true); 236 | if (data[ii + 3] > 0) return (true); 237 | }; 238 | 239 | return (false); 240 | 241 | } 242 | 243 | /** 244 | * Get current time 245 | * @return {Object} 246 | */ 247 | export function getTime() { 248 | 249 | let date = new Date(); 250 | 251 | return ({ 252 | hours: date.getHours(), 253 | minutes: date.getMinutes(), 254 | seconds: date.getSeconds() 255 | }); 256 | 257 | } 258 | 259 | /** 260 | * Anti cache 261 | * @return {String} 262 | */ 263 | export function antiCache() { 264 | return ( 265 | `?${+(new Date())}` 266 | ); 267 | } 268 | 269 | /** 270 | * Ajax 271 | * @param {String} url 272 | */ 273 | export function ajax(url) { 274 | if (DEV_MODE === true) { 275 | url = url + antiCache(); 276 | } 277 | return new Promise(function(resolve, reject) { 278 | let req = new XMLHttpRequest(); 279 | req.open("GET", url); 280 | req.onload = function() { 281 | if (req.status === 200) { 282 | resolve(req.response); 283 | } else { 284 | reject(new Error(req.statusText)); 285 | } 286 | }; 287 | req.onerror = function() { 288 | reject(new Error("Network error")); 289 | }; 290 | req.send(); 291 | }); 292 | } -------------------------------------------------------------------------------- /src/Game/entities/Light/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | LEFT, RIGHT, UP, DOWN, 3 | OFFLINE_MODE, 4 | DIMENSION, GRAVITY, 5 | TYPES 6 | } from "../../../cfg"; 7 | 8 | import math from "../../../Math"; 9 | 10 | import { 11 | Maps 12 | } from "../../../Engine/utils"; 13 | 14 | import MapEntity from "../../../Engine/Map/MapEntity"; 15 | 16 | export class Light extends MapEntity { 17 | 18 | /** 19 | * @constructor 20 | * @param {Object} obj 21 | */ 22 | constructor(obj) { 23 | 24 | super(obj); 25 | 26 | this.scale = .25; 27 | 28 | this.name = "light" + this.id; 29 | 30 | this.zIndex = 999999; 31 | 32 | this.isLight = true; 33 | 34 | this.lightSize = obj.lightSize === void 0 ? 512 : obj.lightSize; 35 | 36 | this.jumpable = false; 37 | 38 | this.hasShadow = false; 39 | 40 | this.lightSize = obj.lightSize === void 0 ? 256 : obj.lightSize; 41 | 42 | this.soft = obj.soft === void 0 ? true : obj.soft; 43 | 44 | this.color = this.processColor(obj.color); 45 | 46 | this.type = TYPES.Light; 47 | 48 | } 49 | 50 | /** 51 | * Process hex color 52 | * @param {String} color 53 | * @return {Array} 54 | */ 55 | processColor(color) { 56 | let cString = color[0] === "#" ? color.substr(1) : color; 57 | return ( 58 | math.hexToRGB(cString) 59 | ); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /src/Game/entities/Monster.js: -------------------------------------------------------------------------------- 1 | import Entity from "../../Engine/Entity"; 2 | 3 | export class Monster extends Entity { 4 | 5 | /** 6 | * @constructor 7 | * @param {Object} obj 8 | */ 9 | constructor(obj) { 10 | super(obj); 11 | this.init(obj) 12 | } 13 | 14 | init() { console.log(this); } 15 | 16 | } -------------------------------------------------------------------------------- /src/Game/entities/Player/face.js: -------------------------------------------------------------------------------- 1 | import { 2 | OFFLINE_MODE 3 | } from "../../../cfg"; 4 | 5 | /** 6 | * Change facing 7 | * @param {Number} dir 8 | */ 9 | export function changeFacing(dir) { 10 | 11 | if (this.STATES.LOCK === true || this.moving === true) return void 0; 12 | 13 | this.idleTime = 0; 14 | 15 | if ( 16 | this.moving === false && 17 | this.STATES.BUMPING === false 18 | ) { 19 | this.lastFacing = this.facing; 20 | this.facing = dir; 21 | if (this.isLocalPlayer === true && OFFLINE_MODE === false) { 22 | this.instance.engine.connection.sendData( 23 | "Facing", 24 | [this.id, this.facing] 25 | ); 26 | } 27 | this.frame = (this.frame + 3 + this.getFrameIndex()) % 4; 28 | } 29 | 30 | /** TODO: Avoid settimeout */ 31 | setTimeout(this::function() { 32 | if ( 33 | this.moving === false && 34 | this.STATES.BUMPING === false && 35 | this.STATES.JUMPING === false 36 | ) { 37 | this.resetFrame(); 38 | } 39 | }, 30); 40 | 41 | } -------------------------------------------------------------------------------- /src/Game/entities/Player/follow.js: -------------------------------------------------------------------------------- 1 | import { 2 | DIMENSION 3 | } from "../../../cfg"; 4 | 5 | /** 6 | * Follow a entity 7 | * @param {Number} x 8 | * @param {Number} y 9 | * @param {Boolean} obstacle 10 | */ 11 | export function follow(x, y, obstacle) { 12 | 13 | let leader = this.leader; 14 | 15 | if (obstacle === false) { 16 | if ( 17 | leader.x << 0 === this.followTarget.x << 0 && 18 | leader.y << 0 === this.followTarget.y << 0 19 | ) { 20 | leader.walkTo( 21 | x << 0, 22 | y << 0 23 | ); 24 | this.followTarget.x = x << 0; 25 | this.followTarget.y = y << 0; 26 | /** Target has moved to new position */ 27 | } 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/Game/entities/Player/jump.js: -------------------------------------------------------------------------------- 1 | import { 2 | OFFLINE_MODE, 3 | GRAVITY 4 | } from "../../../cfg"; 5 | 6 | import { 7 | Maps 8 | } from "../../../Engine/utils"; 9 | 10 | /** 11 | * Jump 12 | */ 13 | export function jump() { 14 | 15 | if (this.jumpable === false) return void 0; 16 | 17 | this.refreshState(); 18 | 19 | if ( 20 | this.STATES.JUMPING === true || 21 | this.STATES.LOCK === true 22 | ) return void 0; 23 | 24 | this.STATES.JUMPING = true; 25 | 26 | if (this.onJump !== null) { 27 | Maps[this.map].triggerEvent(this, this, "onJump"); 28 | } 29 | 30 | this.jumping(); 31 | 32 | if (this.isLocalPlayer === true && OFFLINE_MODE === false) { 33 | this.instance.engine.connection.sendData( 34 | "Jumping", 35 | [this.id] 36 | ); 37 | } 38 | 39 | this.idleTime = 0; 40 | 41 | } 42 | 43 | /** 44 | * Jumping 45 | */ 46 | export function jumping() { 47 | 48 | this.frame = 3; 49 | 50 | if (this.z === 0) { 51 | this.playStateSound(); 52 | } 53 | 54 | this.z += this.gravity; 55 | 56 | this.refreshState(); 57 | 58 | if (this.z < 0) { 59 | this.gravity += .1; 60 | this.shadow.position.set(-(this.z / 2), this.shadowY - (this.z)); 61 | this.shadow.scale.set(this.z, this.z); 62 | } else { 63 | this.gravity = GRAVITY; 64 | this.z = 0; 65 | this.resetFrame(); 66 | this.refreshState(); 67 | this.shadow.position.set(this.shadowX, this.shadowY); 68 | this.shadow.scale.set(0, 0); 69 | 70 | if (this.isLocalPlayer === true) { 71 | this.instance.engine.everythingJump(); 72 | this.instance.engine.everythingRotate(1); 73 | } 74 | 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /src/Game/entities/Player/new/face.js: -------------------------------------------------------------------------------- 1 | import { 2 | OFFLINE_MODE 3 | } from "../../../cfg"; 4 | 5 | /** 6 | * Change facing 7 | * @param {Number} dir 8 | */ 9 | export function changeFacing(dir) { 10 | 11 | if (this.STATES.LOCK === true || this.moving === true) return void 0; 12 | 13 | this.idleTime = 0; 14 | 15 | if ( 16 | this.moving === false && 17 | this.STATES.BUMPING === false 18 | ) { 19 | this.lastFacing = this.facing; 20 | this.facing = dir; 21 | if (this.isLocalPlayer === true && OFFLINE_MODE === false) { 22 | this.instance.engine.connection.sendData( 23 | "Facing", 24 | [this.id, this.facing] 25 | ); 26 | } 27 | this.frame = (this.frame + 3 + this.getFrameIndex()) % 4; 28 | } 29 | 30 | /** TODO: Avoid settimeout */ 31 | setTimeout(this::function() { 32 | if ( 33 | this.moving === false && 34 | this.STATES.BUMPING === false && 35 | this.STATES.JUMPING === false 36 | ) { 37 | this.resetFrame(); 38 | } 39 | }, 30); 40 | 41 | } -------------------------------------------------------------------------------- /src/Game/entities/Player/new/index.js: -------------------------------------------------------------------------------- 1 | import math from "../../../Math"; 2 | import { 3 | OFFLINE_MODE, 4 | DIMENSION, GRAVITY 5 | } from "../../../cfg"; 6 | 7 | import { 8 | Maps 9 | } from "../../../Engine/utils"; 10 | 11 | import { inherit } from "../../../Engine/utils"; 12 | 13 | import Entity from "../../../Engine/Entity"; 14 | 15 | import * as jump from "./jump"; 16 | import * as walk from "./walk"; 17 | import * as face from "./face"; 18 | import * as sound from "./sound"; 19 | 20 | export class Player extends Entity { 21 | 22 | /** 23 | * @constructor 24 | * @param {Object} obj 25 | */ 26 | constructor(obj) { 27 | 28 | super(obj); 29 | 30 | /** 31 | * Local entity requires instance ref 32 | * @type {Object} 33 | */ 34 | this.instance = null; 35 | 36 | /** 37 | * Gravity 38 | * @type {Number} 39 | */ 40 | this.gravity = GRAVITY; 41 | 42 | /** 43 | * Player facing 44 | * @type {Number} 45 | */ 46 | this.facing = 0; 47 | 48 | /** 49 | * Idle state 50 | * @type {Number} 51 | */ 52 | this.idle = 0; 53 | 54 | /** 55 | * States 56 | * @type {Object} 57 | */ 58 | this.STATES["WALKING"] = false; 59 | this.STATES["RUNNING"] = false; 60 | this.STATES["BUMPING"] = false; 61 | this.STATES["WALKING"] = false; 62 | this.STATES["FACING"] = false; 63 | 64 | /** 65 | * Shadow offsets 66 | * @type {Number} 67 | */ 68 | this.shadowX = obj.shadowX === void 0 ? 0 : obj.shadowX; 69 | this.shadowY = obj.shadowY === void 0 ? -1.75 : obj.shadowY; 70 | 71 | /** 72 | * Local player check 73 | * @type {Boolean} 74 | */ 75 | this.isLocalPlayer = false; 76 | 77 | /** 78 | * NPC check 79 | * @type {Boolean} 80 | */ 81 | this.isNPC = false; 82 | 83 | /** 84 | * Network player check 85 | * @type {Boolean} 86 | */ 87 | this.isNetworkPlayer = false; 88 | 89 | /** 90 | * Animation frames 91 | * @type {Array} 92 | */ 93 | this.frames = [0, 1, 0, 2, 3, 4]; 94 | 95 | /** 96 | * Reset frame 97 | * @type {Array} 98 | */ 99 | this.frameReset = [0, 2, 2, 0]; 100 | 101 | /** 102 | * Last facing 103 | * @type {Number} 104 | */ 105 | this.lastFacing = 0; 106 | 107 | /** 108 | * Step count 109 | * @type {Number} 110 | */ 111 | this.stepCount = 0; 112 | 113 | /** 114 | * Face count 115 | * @type {Number} 116 | */ 117 | this.faceCount = 0; 118 | 119 | /** 120 | * Latency 121 | * @type {Number} 122 | */ 123 | this.latency = .5; 124 | 125 | /** 126 | * Map the player is on 127 | * @type {String} 128 | */ 129 | this.map = obj.map; 130 | 131 | /** 132 | * Step sound 133 | * @type {Number} 134 | */ 135 | this.soundSteps = DIMENSION * 2; 136 | 137 | this.xMargin = -(DIMENSION / 2); 138 | this.yMargin = -DIMENSION; 139 | 140 | if ( 141 | obj.x !== void 0 && 142 | obj.y !== void 0 143 | ) { 144 | this.x = obj.x; 145 | this.y = obj.y; 146 | } 147 | 148 | this.init(obj); 149 | 150 | } 151 | 152 | /** 153 | * @getter 154 | * @return {Number} 155 | */ 156 | get velocity() { 157 | return (this.latency); 158 | } 159 | 160 | /** 161 | * @param {Number} value 162 | * @setter 163 | */ 164 | set velocity(value) { 165 | this.latency = value / 2; 166 | if (this.isLocalPlayer === true && OFFLINE_MODE === false) { 167 | this.instance.engine.connection.sendData( 168 | "Velocity", 169 | [this.id, value] 170 | ); 171 | } 172 | this.refreshState(); 173 | } 174 | 175 | /** 176 | * Player is moving 177 | * @return {Boolean} 178 | * @getter 179 | */ 180 | get moving() { 181 | return ( 182 | this.STATES.WALKING === true || 183 | this.STATES.RUNNING === true 184 | ); 185 | } 186 | 187 | /** 188 | * Player is moving 189 | * @param {Boolean} value 190 | * @setter 191 | */ 192 | set moving(value) { 193 | this.STATES.WALKING = value; 194 | this.STATES.RUNNING = value; 195 | } 196 | 197 | /** 198 | * Initialise 199 | * @param {Object} obj 200 | */ 201 | init(obj) { 202 | this.setPlayerType(obj); 203 | } 204 | 205 | /** 206 | * Set player entity type 207 | * @param {Object} obj 208 | */ 209 | setPlayerType(obj) { 210 | 211 | if (obj.isLocalPlayer === true) { 212 | this.isLocalPlayer = true; 213 | this.isNPC = false; 214 | this.isNetworkPlayer = false; 215 | } 216 | else if (obj.isNPC === true) { 217 | this.isLocalPlayer = false; 218 | this.isNPC = true; 219 | this.isNetworkPlayer = false; 220 | } 221 | else if (obj.isNetworkPlayer === true) { 222 | this.isLocalPlayer = false; 223 | this.isNPC = false; 224 | this.isNetworkPlayer = true; 225 | } 226 | /** Default is npc */ 227 | else { 228 | this.isLocalPlayer = false; 229 | this.isNPC = true; 230 | this.isNetworkPlayer = false; 231 | } 232 | 233 | } 234 | 235 | /** 236 | * Get frame index 237 | * @return {Number} 238 | */ 239 | getFrameIndex() { 240 | return ( 241 | this.STATES.RUNNING === true ? 2 : 0 242 | ); 243 | } 244 | 245 | /** Reset sprite frame */ 246 | resetFrame() { 247 | this.frame = this.frameReset[this.frame] + this.getFrameIndex(); 248 | } 249 | 250 | /** Refresh entity states */ 251 | refreshState() { 252 | this.STATES.RUNNING = this.velocity === .5 ? false : this.velocity === 1 && this.STATES.WALKING === true ? true : false; 253 | this.STATES.JUMPING = this.z !== 0; 254 | } 255 | 256 | /** Trigger faced tile */ 257 | action() { 258 | let position = math.getTilePosition(this.x << 0, this.y << 0, this.facing); 259 | Maps[this.map].actionTrigger(position, this); 260 | } 261 | 262 | /** 263 | * Face a entity 264 | * @param {Object} entity 265 | */ 266 | faceEntity(entity) { 267 | if (entity === null) return void 0; 268 | let facing = this.oppositFacing(entity.facing); 269 | if (this.facing !== facing) { 270 | this.changeFacing(facing); 271 | } 272 | } 273 | 274 | /** Animate */ 275 | animate() { 276 | 277 | if (this.animations.length <= 0) return void 0; 278 | 279 | if (this.animations[0] !== void 0 && this.animations[0].type === "fade") { 280 | this.fade(this.animations[0]); 281 | } 282 | 283 | if (this.animations[0] !== void 0 && this.animations[0].type === "jump") { 284 | this.jumpAnimation(); 285 | } 286 | 287 | if (this.animations[0] !== void 0 && this.animations[0].type === "move") { 288 | this.moveAnimation(this.animations[0]); 289 | } 290 | 291 | if (this.animations[0] !== void 0 && this.animations[0].type === "bump") { 292 | this.bumpAnimation(this.animations[0]); 293 | } 294 | 295 | } 296 | 297 | } 298 | 299 | inherit(Player, jump); 300 | inherit(Player, walk); 301 | inherit(Player, face); 302 | inherit(Player, sound); -------------------------------------------------------------------------------- /src/Game/entities/Player/new/jump.js: -------------------------------------------------------------------------------- 1 | import { 2 | OFFLINE_MODE, 3 | GRAVITY 4 | } from "../../../cfg"; 5 | 6 | import { 7 | Maps 8 | } from "../../../Engine/utils"; 9 | 10 | export function jump() { 11 | 12 | if ( 13 | this.STATES.JUMPING === true || 14 | this.STATES.LOCK === true 15 | ) return void 0; 16 | 17 | this.animations.push({ 18 | type: "jump" 19 | }); 20 | 21 | } 22 | 23 | /** 24 | * Jump 25 | */ 26 | export function jumpAnimation() { 27 | 28 | if (this.jumpable === false) return void 0; 29 | 30 | this.refreshState(); 31 | 32 | if (this.onJump !== null) { 33 | Maps[this.map].triggerEvent(this, this, "onJump"); 34 | } 35 | 36 | if (this.isLocalPlayer === true && OFFLINE_MODE === false) { 37 | this.instance.engine.connection.sendData( 38 | "Jumping", 39 | [this.id] 40 | ); 41 | } 42 | 43 | this.idleTime = 0; 44 | 45 | this.jumping(); 46 | 47 | } 48 | 49 | /** 50 | * Jumping 51 | */ 52 | export function jumping() { 53 | 54 | if (this.frame === 0 && this.z === 0) { 55 | this.playStateSound(); 56 | } 57 | 58 | this.frame = 3; 59 | 60 | this.z += this.gravity; 61 | 62 | this.refreshState(); 63 | 64 | if (this.z < 0) { 65 | this.gravity += .1; 66 | this.shadow.position.set(-(this.z / 2), this.shadowY - (this.z)); 67 | this.shadow.scale.set(this.z, this.z); 68 | } else { 69 | this.gravity = GRAVITY; 70 | this.z = 0; 71 | this.resetFrame(); 72 | this.refreshState(); 73 | this.shadow.position.set(this.shadowX, this.shadowY); 74 | this.shadow.scale.set(0, 0); 75 | this.animations.shift(); 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /src/Game/entities/Player/new/sound.js: -------------------------------------------------------------------------------- 1 | import { 2 | DIMENSION, 3 | BGS, VOLUME 4 | } from "../../../cfg"; 5 | 6 | import { 7 | Maps 8 | } from "../../../Engine/utils"; 9 | 10 | import Audio from "../../../Engine/Audio"; 11 | 12 | /** 13 | * Play sound 14 | */ 15 | export function playStateSound() { 16 | 17 | if (BGS !== true) return void 0; 18 | 19 | let volume = this.isLocalPlayer === true ? VOLUME.NETWORK_PLAYER : VOLUME.LOCAL_PLAYER; 20 | 21 | let dist = Maps[this.map].distance(this, game.engine.camera); 22 | 23 | if (this.STATES.JUMPING === true && this.z === 0) { 24 | Audio.playSound("jump", volume, dist.x, dist.y); 25 | } 26 | 27 | /** Player is bumping */ 28 | if (this.STATES.BUMPING === true) { 29 | Audio.playSound("bump", volume, dist.x, dist.y); 30 | /** Player is walking */ 31 | } else { 32 | if (this.moving === true) { 33 | if (this.soundSteps >= DIMENSION * 2) { 34 | this.soundSteps = 0; 35 | if (this.STATES.RUNNING === true) { 36 | Audio.playSound("run_step", volume, dist.x, dist.y); 37 | } else { 38 | Audio.playSound("ground_step", volume, dist.x, dist.y); 39 | } 40 | } 41 | } 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/Game/entities/Player/new/walk.js: -------------------------------------------------------------------------------- 1 | import { 2 | OFFLINE_MODE, 3 | Y_DEPTH_HACK, 4 | DIMENSION, 5 | LEFT, RIGHT, UP, DOWN, 6 | GOD_MODE 7 | } from "../../../cfg"; 8 | 9 | import { 10 | Maps 11 | } from "../../../Engine/utils"; 12 | 13 | import math from "../../../Math"; 14 | 15 | export function move(dir, resolve) { 16 | 17 | if ( 18 | this.STATES.LOCK === true || 19 | this.STATES.EDITING === true || 20 | this.moving === true 21 | ) return void 0; 22 | 23 | let x = this.x; 24 | let y = this.y; 25 | 26 | let position = math.getTilePosition(x, y, dir); 27 | let obstacle = Maps[this.map].isObstacle(this, dir); 28 | 29 | if (this.isLocalPlayer === true && GOD_MODE === true) { 30 | obstacle = false; 31 | } 32 | 33 | if (this.isLocalPlayer === true && OFFLINE_MODE === false) { 34 | this.instance.engine.connection.sendData( 35 | "Position", 36 | [this.id, dir, x, y] 37 | ); 38 | } 39 | 40 | /** Blocked, bump so */ 41 | if (obstacle === true) { 42 | this.animations.push({ 43 | type: "bump", 44 | x: x, 45 | y: y 46 | }); 47 | this.STATES.BUMPING = true; 48 | /** Crossable */ 49 | } else { 50 | this.animations.push({ 51 | type: "move", 52 | obstacle: obstacle, 53 | x: position.x, 54 | y: position.y, 55 | oX: x, 56 | oY: y 57 | }); 58 | this.moving = true; 59 | } 60 | 61 | this.idleTime = 0; 62 | 63 | } 64 | 65 | /** 66 | * Do halfstep 67 | */ 68 | export function halfStep() { 69 | 70 | let half = Math.ceil(Math.ceil(DIMENSION / (this.velocity * 2)) / 2); 71 | 72 | if (this.stepCount === half) { 73 | this.frame = (this.frame + 1 + this.getFrameIndex()) % 4; 74 | } 75 | 76 | } 77 | 78 | /** 79 | * Bump 80 | * @param {Object} animation 81 | */ 82 | export function bumpAnimation(animation) { 83 | 84 | if (this.stepCount <= 0) { 85 | this.playStateSound(); 86 | } 87 | 88 | this.stepCount += .5; 89 | 90 | if (this.STATES.JUMPING === false) { 91 | this.halfStep(); 92 | } 93 | 94 | if (this.stepCount >= DIMENSION * 2) { 95 | if (this.STATES.JUMPING === false) { 96 | this.halfStep(); 97 | this.resetFrame(); 98 | } 99 | this.stepCount = 0; 100 | this.animations.shift(); 101 | this.STATES.BUMPING = false; 102 | this.refreshState(); 103 | } 104 | 105 | } 106 | 107 | /** 108 | * Walk 109 | * @param {Object} animation 110 | */ 111 | export function moveAnimation(animation) { 112 | 113 | if (this.stepCount <= 0) { 114 | if (this.STATES.JUMPING === false) { 115 | this.resetFrame(); 116 | } 117 | if (animation.obstacle === false) { 118 | /** onEnter event => animation.x, animation.y */ 119 | } 120 | } 121 | 122 | this.playStateSound(); 123 | 124 | if (animation.obstacle === false) { 125 | if (this.STATES.JUMPING === false) { 126 | this.halfStep(); 127 | } 128 | if (this.x > animation.x) { 129 | this.x -= this.velocity; 130 | } 131 | else if (this.x < animation.x) { 132 | this.x += this.velocity; 133 | } 134 | else if (this.y > animation.y) { 135 | this.y -= this.velocity; 136 | } 137 | else if (this.y < animation.y) { 138 | this.y += this.velocity; 139 | } 140 | this.stepCount += this.velocity; 141 | } else { 142 | animation.x = this.x; 143 | animation.y = this.y; 144 | this.stopMoving(animation); 145 | } 146 | 147 | if (this.stepCount >= DIMENSION) { 148 | this.lastFacing = this.facing; 149 | this.stopMoving(animation); 150 | } 151 | 152 | this.soundSteps += this.velocity; 153 | 154 | } 155 | 156 | /** 157 | * Stop moving 158 | * @param {Object} animation 159 | */ 160 | export function stopMoving(animation) { 161 | 162 | this.x = animation.x; 163 | this.y = animation.y + Y_DEPTH_HACK; 164 | 165 | this.last.x = animation.oX; 166 | this.last.y = animation.oY; 167 | 168 | this.moving = false; 169 | 170 | this.stepCount = 0; 171 | 172 | setTimeout(this::function() { 173 | if ( 174 | this.moving === false && 175 | this.STATES.BUMPING === false && 176 | this.STATES.JUMPING === false 177 | ) { 178 | this.resetFrame(); 179 | } 180 | }, 100); 181 | 182 | this.animations.shift(); 183 | 184 | this.refreshState(); 185 | 186 | /** Continue moving */ 187 | if (this.isLocalPlayer === true) { 188 | if (this.instance.input.KeyBoard.isKeyPressed(this.facingToKey(LEFT)) === true) { 189 | this.move(LEFT); 190 | } 191 | else if (this.instance.input.KeyBoard.isKeyPressed(this.facingToKey(UP)) === true) { 192 | this.move(UP); 193 | } 194 | else if (this.instance.input.KeyBoard.isKeyPressed(this.facingToKey(RIGHT)) === true) { 195 | this.move(RIGHT); 196 | } 197 | else if (this.instance.input.KeyBoard.isKeyPressed(this.facingToKey(DOWN)) === true) { 198 | this.move(DOWN); 199 | } else { 200 | this.soundSteps = DIMENSION; 201 | } 202 | } else { 203 | this.soundSteps = DIMENSION; 204 | } 205 | 206 | if (this.moveCB) { 207 | this.moveCB(); 208 | } 209 | 210 | } -------------------------------------------------------------------------------- /src/Game/entities/Player/sound.js: -------------------------------------------------------------------------------- 1 | import { 2 | DIMENSION, 3 | BGS, VOLUME 4 | } from "../../../cfg"; 5 | 6 | import { 7 | Maps 8 | } from "../../../Engine/utils"; 9 | 10 | import Audio from "../../../Engine/Audio"; 11 | 12 | /** 13 | * Play sound 14 | */ 15 | export function playStateSound() { 16 | 17 | if (BGS !== true) return void 0; 18 | 19 | let volume = this.isLocalPlayer === true ? VOLUME.NETWORK_PLAYER : VOLUME.LOCAL_PLAYER; 20 | 21 | let dist = Maps[this.map].distance(this, game.engine.camera); 22 | 23 | if (Math.abs(dist.x) + Math.abs(dist.y) >= 1.0) { 24 | dist.x *= 4; 25 | dist.y *= 4; 26 | } 27 | 28 | if (this.STATES.JUMPING === true && this.z === 0) { 29 | Audio.playSound("jump", volume, dist.x, dist.y); 30 | } 31 | 32 | /** Player is bumping */ 33 | if (this.STATES.BUMPING === true) { 34 | Audio.playSound("bump", volume, dist.x, dist.y); 35 | /** Player is walking */ 36 | } else { 37 | if (this.moving === true) { 38 | if (this.soundSteps >= DIMENSION * 2) { 39 | this.soundSteps = 0; 40 | if (this.STATES.RUNNING === true) { 41 | Audio.playSound("run_step", volume, dist.x, dist.y); 42 | } else { 43 | Audio.playSound("ground_step", volume, dist.x, dist.y); 44 | } 45 | } 46 | } 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/Game/entities/index.js: -------------------------------------------------------------------------------- 1 | export { Light } from "./Light"; 2 | export { Player } from "./Player"; 3 | export { Monster } from "./Monster"; -------------------------------------------------------------------------------- /src/Game/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | OFFLINE_MODE, 3 | LEFT, RIGHT, UP, DOWN, 4 | CONNECTION_URL, CONNECTION_PORT 5 | } from "../cfg"; 6 | 7 | import Engine from "../Engine"; 8 | import Renderer from "../Engine/Renderer"; 9 | import Input from "../Engine/Input"; 10 | import Connection from "../Engine/Connection"; 11 | 12 | import * as Events from "./input.js"; 13 | import * as entities from "./entities"; 14 | 15 | /** 16 | * Game 17 | * @class Game 18 | * @export 19 | */ 20 | export default class Game { 21 | 22 | /** 23 | * @constructor 24 | */ 25 | constructor() { 26 | 27 | this.glNode = document.querySelector("#webgl"); 28 | this.canvasNode = document.querySelector("#canvas"); 29 | 30 | this.entities = entities; 31 | 32 | this.engine = new Engine(this, () => this.setup()); 33 | 34 | } 35 | 36 | /** 37 | * Setup 38 | * @param {Number} stage 39 | */ 40 | setup(stage = stage === void 0 ? 0 : stage) { 41 | 42 | switch (++stage) { 43 | case 1: 44 | this.engine.renderer = new Renderer(this.engine); 45 | window.rAF(() => this.engine.renderer.render()); 46 | this.setup(stage); 47 | return void 0; 48 | case 2: 49 | this.addWorld(() => this.setup(stage)); 50 | return void 0; 51 | case 3: 52 | this.addMap(() => this.setup(stage)); 53 | return void 0; 54 | case 4: 55 | this.addEntities(() => this.setup(stage)); 56 | return void 0; 57 | case 5: 58 | this.animateNPC(); 59 | this.setup(stage); 60 | return void 0; 61 | case 6: 62 | /** Instant focus local player */ 63 | this.engine.camera.focus(this.engine.getEntityByProperty("Felix", "name"), true); 64 | this.setup(stage); 65 | return void 0; 66 | case 7: 67 | this.input = new Input(Events, this); 68 | this.setup(stage); 69 | return void 0; 70 | case 8: 71 | if (!OFFLINE_MODE) { 72 | this.engine.connection = new Connection( 73 | this, 74 | `${CONNECTION_URL}:${CONNECTION_PORT}` 75 | ); 76 | } 77 | this.setup(stage); 78 | return void 0; 79 | }; 80 | 81 | return void 0; 82 | 83 | } 84 | 85 | animateNPC() { 86 | setTimeout(this::function() { 87 | let entity = this.engine.getEntityByProperty("Joy", "name"); 88 | let move = [LEFT, RIGHT, UP, DOWN][(Math.random() * 3) << 0]; 89 | entity.move(move); 90 | this.animateNPC(); 91 | }, 2e3); 92 | } 93 | 94 | /** 95 | * Add world 96 | * @param {Function} resolve 97 | */ 98 | addWorld(resolve) { 99 | this.engine.addWorld("worlds/kanto/index.js", resolve); 100 | } 101 | 102 | /** 103 | * Add map 104 | * @param {Function} resolve 105 | */ 106 | addMap(resolve) { 107 | this.engine.addMap("worlds/kanto/town/town.json", resolve); 108 | } 109 | 110 | /** 111 | * Add entities 112 | * @param {Function} resolve 113 | */ 114 | addEntities(resolve) { 115 | 116 | let player = this.entities.Player; 117 | 118 | this.engine.addEntity(new entities.Light({ 119 | sprite: "assets/img/light.png", 120 | map: "Town", 121 | x: 168, y: 96, 122 | width: 32, height: 32, 123 | soft: false, 124 | color: "#E6E6E6" 125 | })); 126 | 127 | this.engine.addEntity(new player({ name: "Joy", map: "Town", x: 96, y: 144, sprite: "assets/img/200.png", width: 16, height: 16, collidable: true, 128 | facing: 1, 129 | onCollide: { 130 | JavaScript: function(entity, engine) { 131 | this.faceEntity(entity); 132 | engine.instance.notify(this, "Stop!"); 133 | } 134 | } 135 | })); 136 | 137 | this.engine.addEntity(new player({ name: "Merlin", map: "Town", x: 160, y: 144, sprite: "assets/img/85.png", width: 16, height: 16, collidable: true, shadowY: -3, normal: true, 138 | onAction: { 139 | EngelScript: ` 140 | if (trigger.facing == 2 || trigger.facing == 3) { 141 | FLAGS.COUNTER += 1; 142 | } { 143 | FLAGS.COUNTER -= 1; 144 | } 145 | kernel.notify(this, '+' + FLAGS.COUNTER + " "); 146 | this.faceEntity(trigger); 147 | ` 148 | } 149 | })); 150 | 151 | this.engine.addEntity(new player({ name: "Merlin2", map: "Town", x: 136, y: 120, sprite: "assets/img/85.png", width: 16, height: 16, collidable: true, shadowY: -3, normal: true, 152 | onCollide: { 153 | EngelScript: ` 154 | kernel.notify(this, trigger.name); 155 | ` 156 | } 157 | })); 158 | 159 | this.engine.addEntity(new player({ 160 | name: "Mew", map: "Town", 161 | sprite: "assets/img/151.png", 162 | width: 16, height: 16, 163 | collidable: false, 164 | following: "Joy" 165 | })); 166 | 167 | this.engine.addEntity(new player({ 168 | name: "Charizard", map: "Town", 169 | sprite: "assets/img/4.png", 170 | width: 16, height: 16, 171 | collidable: false, 172 | following: "Felix", 173 | onAction: { 174 | EngelScript: ` 175 | kernel.notify(this, ":p"); 176 | trigger.leader.faceEntity(trigger); 177 | ` 178 | }, 179 | normal: true 180 | })); 181 | 182 | this.engine.addEntity(new player({ 183 | name: "Flareon", map: "Town", 184 | sprite: "assets/img/136.png", 185 | width: 16, height: 16, 186 | collidable: false, 187 | following: "Mew" 188 | })); 189 | 190 | if (OFFLINE_MODE) { 191 | this.engine.addEntity(new player({ 192 | name: "Felix", map: "Town", x: 144, y: 152, sprite: "assets/img/0.png", width: 16, height: 16, isLocalPlayer: true, collidable: true, normal: true, 193 | onJump: (entity, map) => { 194 | if (entity.leader) { 195 | setTimeout(() => map.instance.notify(entity.leader, " :3 "), 250); 196 | setTimeout(() => entity.leader.jump(), 500); 197 | } 198 | } 199 | })); 200 | } 201 | 202 | return (resolve()); 203 | 204 | } 205 | 206 | } 207 | 208 | window.game = new Game(); -------------------------------------------------------------------------------- /src/Game/input.js: -------------------------------------------------------------------------------- 1 | export const keys = [ 2 | { 3 | name: "SHIFT", 4 | fire: function() {}, 5 | leave: function() { 6 | this.action("SHIFT"); 7 | } 8 | }, 9 | { 10 | name: "CTRL+Z", 11 | simultaneous: false, 12 | fire: function() { 13 | this.action("CTRL+Z"); 14 | } 15 | }, 16 | { 17 | name: "CTRL+Y", 18 | simultaneous: false, 19 | fire: function() { 20 | this.action("CTRL+Y"); 21 | } 22 | }, 23 | { 24 | name: "CTRL+C", 25 | spam: false, 26 | simultaneous: false, 27 | fire: function() { 28 | this.action("CTRL+C"); 29 | } 30 | }, 31 | { 32 | name: "CTRL+V", 33 | spam: false, 34 | simultaneous: false, 35 | fire: function() { 36 | this.action("CTRL+V"); 37 | } 38 | }, 39 | { 40 | name: "CTRL+X", 41 | spam: false, 42 | simultaneous: false, 43 | fire: function() { 44 | this.action("CTRL+X"); 45 | } 46 | }, 47 | { 48 | name: "DELETE", 49 | fire: function() { 50 | this.action("DELETE"); 51 | } 52 | }, 53 | { 54 | name: "F1", 55 | spam: false, 56 | fire: function() { 57 | this.action("F1"); 58 | } 59 | }, 60 | { 61 | name: "F2", 62 | spam: false, 63 | fire: function() { 64 | this.action("F2"); 65 | } 66 | }, 67 | { 68 | name: "F3", 69 | spam: false, 70 | fire: function() { 71 | this.action("F3"); 72 | } 73 | }, 74 | { 75 | name: "F4", 76 | spam: false, 77 | fire: function() { 78 | this.action("F4"); 79 | } 80 | }, 81 | { 82 | name: "F6", 83 | spam: false, 84 | fire: function() { 85 | this.action("F6"); 86 | } 87 | }, 88 | /** BUGGY, KEY COMBOS DONT WORK WITHOUT THIS */ 89 | { 90 | name: "Y", 91 | fire: function() {} 92 | }, 93 | /** BUGGY, KEY COMBOS DONT WORK WITHOUT THIS */ 94 | { 95 | name: "G", 96 | fire: function() {} 97 | }, 98 | /** BUGGY, KEY COMBOS DONT WORK WITHOUT THIS */ 99 | { 100 | name: "V", 101 | fire: function() {} 102 | }, 103 | /** BUGGY, KEY COMBOS DONT WORK WITHOUT THIS */ 104 | { 105 | name: "CTRL", 106 | fire: function() {} 107 | }, 108 | { 109 | name: "ESCAPE", 110 | spam: false, 111 | fire: function() { 112 | this.action("ESCAPE"); 113 | } 114 | }, 115 | { 116 | name: "B", 117 | spam: false, 118 | fire: function() { 119 | this.action("B"); 120 | } 121 | }, 122 | { 123 | name: "Z", 124 | spam: false, 125 | fire: function() { 126 | this.action("Z"); 127 | } 128 | }, 129 | { 130 | name: "X", 131 | spam: false, 132 | fire: function() { 133 | this.action("X_FIRE"); 134 | }, 135 | leave: function() { 136 | this.action("X_LEAVE"); 137 | } 138 | }, 139 | { 140 | name: "C", 141 | spam: false, 142 | fire: function() { 143 | this.action("C"); 144 | } 145 | }, 146 | { 147 | name: "←", 148 | fire: function() { 149 | this.action("←"); 150 | } 151 | }, 152 | { 153 | name: "→", 154 | fire: function() { 155 | this.action("→"); 156 | } 157 | }, 158 | { 159 | name: "↑", 160 | fire: function() { 161 | this.action("↑"); 162 | } 163 | }, 164 | { 165 | name: "↓", 166 | fire: function() { 167 | this.action("↓"); 168 | } 169 | }, 170 | { 171 | name: "SPACE", 172 | fire: function() { 173 | this.action("SPACE"); 174 | } 175 | } 176 | ]; 177 | 178 | export const mouse = [ 179 | { 180 | name: "dblclick", 181 | fire: function(e) { 182 | this.action("DBLCLICK", [e]); 183 | } 184 | }, 185 | { 186 | name: "mousedown|touchstart", 187 | fire: function(e) { 188 | this.action("LEFTCLICK", [e]); 189 | } 190 | }, 191 | { 192 | name: "contextmenu", 193 | fire: function(e) { 194 | this.action("RIGHTCLICK", [e]); 195 | } 196 | }, 197 | { 198 | name: "mouseup|touchend", 199 | fire: function(e) { 200 | this.action("MOUSEUP", [e]); 201 | } 202 | }, 203 | { 204 | name: "mousemove|touchmove", 205 | fire: function(e) { 206 | this.action("MOUSEMOVE", [e]); 207 | } 208 | }, 209 | { 210 | name: "mousewheel", 211 | fire: function(e) { 212 | this.action("MOUSEWHEEL", [e]); 213 | } 214 | } 215 | ]; 216 | 217 | export const global = [ 218 | { 219 | name: "resize", 220 | fire: function(e) { 221 | this.action("RESIZE"); 222 | } 223 | } 224 | ]; -------------------------------------------------------------------------------- /src/Packets/Facing.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Facing packet 3 | * @class Facing 4 | * @export 5 | */ 6 | export class Facing { 7 | 8 | /** 9 | * @param {Number} id 10 | * @param {Number} dir 11 | * @return {Object} 12 | * @constructor 13 | */ 14 | constructor(id, dir) { 15 | 16 | /** 17 | * Entity id 18 | * @type {Number} 19 | */ 20 | this.id = id; 21 | 22 | /** 23 | * Direction 24 | * @type {Number} 25 | */ 26 | this.dir = dir; 27 | 28 | return (this.encode()); 29 | 30 | } 31 | 32 | /** 33 | * Encode process 34 | * @return {Object} 35 | */ 36 | encode() { 37 | 38 | let buffer = new ArrayBuffer(5); 39 | let view = new DataView(buffer); 40 | 41 | view.setUint8(0, 31, true); 42 | view.setUint16(1, this.id, true); 43 | view.setUint16(3, this.dir, true); 44 | 45 | return (view); 46 | 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/Packets/Jumping.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Jumping packet 3 | * @class Jumping 4 | * @export 5 | */ 6 | export class Jumping { 7 | 8 | /** 9 | * @param {Number} id 10 | * @return {Object} 11 | * @constructor 12 | */ 13 | constructor(id) { 14 | 15 | /** 16 | * Entity id 17 | * @type {Number} 18 | */ 19 | this.id = id; 20 | 21 | return (this.encode()); 22 | 23 | } 24 | 25 | /** 26 | * Encode process 27 | * @return {Object} 28 | */ 29 | encode() { 30 | 31 | let buffer = new ArrayBuffer(3); 32 | let view = new DataView(buffer); 33 | 34 | view.setUint8(0, 30, true); 35 | view.setUint16(1, this.id, true); 36 | 37 | return (view); 38 | 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /src/Packets/Position.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Position packet 3 | * @class Position 4 | * @export 5 | */ 6 | export class Position { 7 | 8 | /** 9 | * @param {Number} id 10 | * @param {Number} dir 11 | * @param {Number} x 12 | * @param {Number} y 13 | * @return {Object} 14 | * @constructor 15 | */ 16 | constructor(id, dir, x, y) { 17 | 18 | /** 19 | * Entity id 20 | * @type {Number} 21 | */ 22 | this.id = id; 23 | 24 | /** 25 | * Direction 26 | * @type {Number} 27 | */ 28 | this.dir = dir; 29 | 30 | /** 31 | * X 32 | * @type {Number} 33 | */ 34 | this.x = x; 35 | 36 | /** 37 | * Y 38 | * @type {Number} 39 | */ 40 | this.y = y; 41 | 42 | return (this.encode()); 43 | 44 | } 45 | 46 | /** 47 | * Encode process 48 | * @return {Object} 49 | */ 50 | encode() { 51 | 52 | let buffer = new ArrayBuffer(9); 53 | let view = new DataView(buffer); 54 | 55 | view.setUint8(0, 32, true); 56 | view.setUint16(1, this.id, true); 57 | view.setUint16(3, this.dir, true); 58 | view.setUint16(5, this.x, true); 59 | view.setUint16(7, this.y, true); 60 | 61 | return (view); 62 | 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /src/Packets/Velocity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Velocity packet 3 | * @class Velocity 4 | * @export 5 | */ 6 | export class Velocity { 7 | 8 | /** 9 | * @param {Number} id 10 | * @param {Number} velocity 11 | * @return {Object} 12 | * @constructor 13 | */ 14 | constructor(id, velocity) { 15 | 16 | /** 17 | * Entity id 18 | * @type {Number} 19 | */ 20 | this.id = id; 21 | 22 | /** 23 | * Velocity 24 | * @type {Number} 25 | */ 26 | this.velocity = velocity; 27 | 28 | return (this.encode()); 29 | 30 | } 31 | 32 | /** 33 | * Encode process 34 | * @return {Object} 35 | */ 36 | encode() { 37 | 38 | let buffer = new ArrayBuffer(5); 39 | let view = new DataView(buffer); 40 | 41 | view.setUint8(0, 33, true); 42 | view.setUint16(1, this.id, true); 43 | view.setUint16(3, this.velocity, true); 44 | 45 | return (view); 46 | 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/Packets/index.js: -------------------------------------------------------------------------------- 1 | export { Facing } from "./Facing"; 2 | export { Jumping } from "./Jumping"; 3 | export { Position } from "./Position"; 4 | export { Velocity } from "./Velocity"; -------------------------------------------------------------------------------- /src/cfg.js: -------------------------------------------------------------------------------- 1 | import { supportWGL, getLocalHost } from "./Engine/utils"; 2 | 3 | /** 4 | * Canvas rendering mode 5 | * @constant 6 | * @type {Number} 7 | */ 8 | export const CANVAS = 0; 9 | 10 | /** 11 | * WebGL rendering mode 12 | * @constant 13 | * @type {Number} 14 | */ 15 | export const WGL = 1; 16 | 17 | /** 18 | * Game rendering mode 19 | * @type {Number} 20 | */ 21 | export let RENDER_MODE = -1; 22 | 23 | /** 24 | * Grid width 25 | * @constant 26 | * @type {Number} 27 | */ 28 | export const GRID_WIDTH = 1; 29 | 30 | /** 31 | * Local player name 32 | * @type {String} 33 | */ 34 | export let LOCAL_PLAYER = null; 35 | 36 | /** 37 | * Connection url 38 | * @constant 39 | * @type {String} 40 | */ 41 | export const CONNECTION_URL = getLocalHost(); 42 | 43 | /** 44 | * Connection port 45 | * @constant 46 | * @type {String} 47 | */ 48 | export const CONNECTION_PORT = 449; 49 | 50 | /** 51 | * @constant 52 | * @type {String} 53 | */ 54 | export const __dirname = "./src/"; 55 | 56 | /** 57 | * Version 58 | * @constant 59 | * @type {String} 60 | */ 61 | export const VERSION = "0.1.0"; 62 | 63 | /** 64 | * WebGL support 65 | * @constant 66 | * @type {Boolean} 67 | */ 68 | export const WGL_SUPPORT = supportWGL(); 69 | 70 | /** 71 | * Walk by keyboard 72 | * @constant 73 | * @type {Boolean} 74 | */ 75 | export const WALK_BY_KEYBOARD = true; 76 | 77 | /** 78 | * Free camera 79 | * @type {Boolean} 80 | */ 81 | export let FREE_CAMERA = false; 82 | 83 | /** 84 | * Easing camera 85 | * @type {Boolean} 86 | */ 87 | export let EASING_CAMERA = false; 88 | 89 | /** 90 | * Developer mode 91 | * @type {Boolean} 92 | */ 93 | export let DEV_MODE = true; 94 | 95 | /** 96 | * Debug mode 97 | * @type {Boolean} 98 | */ 99 | export let DEBUG_MODE = false; 100 | 101 | /** 102 | * Offline mode 103 | * @constant 104 | * @type {Boolean} 105 | */ 106 | export let OFFLINE_MODE = true; 107 | 108 | /** 109 | * Record mode 110 | * @type {Boolean} 111 | */ 112 | export let RECORD_MODE = true; 113 | 114 | /** 115 | * Edit mode 116 | * @type {Boolean} 117 | */ 118 | export let EDIT_MODE = true; 119 | 120 | /** 121 | * God mode 122 | * @type {Boolean} 123 | */ 124 | export let GOD_MODE = false; 125 | 126 | /** 127 | * Tileset drawind mode 128 | * @type {Boolean} 129 | */ 130 | export let TILESET_MODE = true; 131 | 132 | /** 133 | * Debug mode 134 | * @type {Boolean} 135 | */ 136 | export let MINI_MAP = true; 137 | 138 | /** 139 | * Debug fps 140 | * @constant 141 | * @type {Number} 142 | */ 143 | export const DEBUG_FPS = 60; 144 | 145 | /** 146 | * Vertical depth sorting hack 147 | * @constant 148 | * @type {Number} 149 | */ 150 | export const Y_DEPTH_HACK = .0001; 151 | 152 | /** 153 | * @constant 154 | * @type {Number} 155 | */ 156 | export const DIMENSION = 8; 157 | 158 | /** 159 | * PP rounding 160 | * @constant 161 | * @type {Number} 162 | */ 163 | export const PIXEL_SCALE = .125; 164 | 165 | /** 166 | * @constant 167 | * @type {Number} 168 | */ 169 | export const MIN_SCALE = 3.0; 170 | 171 | /** 172 | * @constant 173 | * @type {Number} 174 | */ 175 | export const MAX_SCALE = 12.5; 176 | 177 | /** 178 | * Display shadows 179 | * @constant 180 | * @type {Boolean} 181 | */ 182 | export const DISPLAY_SHADOWS = true; 183 | 184 | /** 185 | * Shadow x scale 186 | * @constant 187 | * @type {Number} 188 | */ 189 | export const SHADOW_X = 1.0; 190 | 191 | /** 192 | * Shadow y scale 193 | * @constant 194 | * @type {Number} 195 | */ 196 | export const SHADOW_Y = 1.45; 197 | 198 | /** 199 | * Shadow alpha 200 | * @type {Number} 201 | */ 202 | export let SHADOW_ALPHA = .85; 203 | 204 | /** 205 | * Direction 206 | * @constant 207 | * @type {Number} 208 | */ 209 | export const LEFT = 3; 210 | 211 | /** 212 | * Direction 213 | * @constant 214 | * @type {Number} 215 | */ 216 | export const UP = 1; 217 | 218 | /** 219 | * Direction 220 | * @constant 221 | * @type {Number} 222 | */ 223 | export const RIGHT = 2; 224 | 225 | /** 226 | * Direction 227 | * @constant 228 | * @type {Number} 229 | */ 230 | export const DOWN = 0; 231 | 232 | /** 233 | * Gravity 234 | * @constant 235 | * @type {Number} 236 | */ 237 | export const GRAVITY = -1; 238 | 239 | /** 240 | * Play bgm 241 | * @constant 242 | * @type {Number} 243 | */ 244 | export const BGM = true; 245 | 246 | /** 247 | * Play bgs 248 | * @constant 249 | * @type {Number} 250 | */ 251 | export const BGS = true; 252 | 253 | /** 254 | * @constant 255 | * @type {Object} 256 | */ 257 | export const VOLUME = { 258 | LOCAL_PLAYER: 100, 259 | NETWORK_PLAYER: 10, 260 | MUSIC: 30, 261 | ENTITY_NOISE: 30 262 | }; 263 | 264 | /** 265 | * @constant 266 | * @type {Object} 267 | */ 268 | export const TYPES = { 269 | Notification: 0, 270 | MapEntity: 1, 271 | Player: 2, 272 | Ping: 3, 273 | Light: 4 274 | }; 275 | 276 | /** 277 | * Which browser we 278 | * are running on 279 | * @type {Object} 280 | */ 281 | export let BROWSERS = { 282 | IE: false, 283 | iOS: false, 284 | Chrome: false, 285 | Firefox: false, 286 | Vivaldi: false, 287 | Electron: false 288 | }; 289 | 290 | BROWSERS::function() { 291 | 292 | if (typeof window === "undefined") return void 0; 293 | 294 | let isChrome = !!(navigator.userAgent.match(/Chrome/i)); 295 | let isVivaldi = !!(navigator.userAgent.match(/Vivaldi/i)); 296 | let isElectron = !!(typeof window !== "undefined" && window.process && window.process.type === "renderer"); 297 | 298 | this.IE = !!(typeof window !== "undefined" && window.ActiveXObject !== void 0); 299 | this.iOS = !!(navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i)); 300 | this.Firefox = !!(navigator.userAgent.match(/Firefox/i)); 301 | 302 | this.Chrome = isChrome && !isVivaldi; 303 | this.Vivaldi = !this.Chrome; 304 | 305 | this.Electron = !this.Chrome && this.Vivaldi; 306 | 307 | }(); 308 | 309 | /** 310 | * Default language packet 311 | * to auto load and use, 312 | * if no language file for 313 | * the navigator was found 314 | * @type {String} 315 | */ 316 | export const DEFAULT_LANG = "en"; 317 | 318 | /** 319 | * Is client 320 | * @type {Boolean} 321 | */ 322 | export let IS_CLIENT = true; 323 | 324 | /** 325 | * @constant 326 | * @type {Array} 327 | */ 328 | export const ColorPalette = [ 329 | [135, 100, 100], 330 | [135, 105, 100], 331 | [140, 110, 100], 332 | [150, 115, 100], 333 | [155, 125, 100], 334 | [150, 135, 100], 335 | [135, 135, 100], 336 | [135, 125, 100], 337 | [130, 125, 100], 338 | /** Morning */ 339 | [130, 120, 100], 340 | [135, 120, 100], 341 | [145, 130, 100], 342 | [150, 145, 100], 343 | /** Day */ 344 | [135, 145, 100], 345 | [145, 150, 100], 346 | [150, 125, 100], 347 | [145, 130, 100], 348 | [135, 130, 100], 349 | /** Early night */ 350 | [125, 135, 100], 351 | [135, 130, 100], 352 | [135, 135, 100], 353 | [135, 100, 100], 354 | [135, 105, 100], 355 | [140, 110, 100], 356 | [150, 115, 100] 357 | ]; -------------------------------------------------------------------------------- /src/libs/wheel.js: -------------------------------------------------------------------------------- 1 | // from: https://developer.mozilla.org/de/docs/Web/Events/wheel 2 | // creates a global "addWheelListener" method 3 | // example: addWheelListener( elem, function( e ) { console.log( e.deltaY ); e.preventDefault(); } ); 4 | (function(window,document) { 5 | 6 | var prefix = "", _addEventListener, support; 7 | 8 | // detect event model 9 | if ( window.addEventListener ) { 10 | _addEventListener = "addEventListener"; 11 | } else { 12 | _addEventListener = "attachEvent"; 13 | prefix = "on"; 14 | } 15 | 16 | // detect available wheel event 17 | support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel" 18 | document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel" 19 | "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox 20 | 21 | window.addWheelListener = function( elem, callback, useCapture ) { 22 | _addWheelListener( elem, support, callback, useCapture ); 23 | 24 | // handle MozMousePixelScroll in older Firefox 25 | if( support == "DOMMouseScroll" ) { 26 | _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture ); 27 | } 28 | }; 29 | 30 | function _addWheelListener( elem, eventName, callback, useCapture ) { 31 | elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) { 32 | !originalEvent && ( originalEvent = window.event ); 33 | 34 | // create a normalized event object 35 | var event = { 36 | // keep a ref to the original event object 37 | originalEvent: originalEvent, 38 | target: originalEvent.target || originalEvent.srcElement, 39 | type: "wheel", 40 | deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1, 41 | deltaX: 0, 42 | deltaZ: 0, 43 | preventDefault: function() { 44 | originalEvent.preventDefault ? 45 | originalEvent.preventDefault() : 46 | originalEvent.returnValue = false; 47 | } 48 | }; 49 | 50 | // calculate deltaY (and deltaX) according to the event 51 | if ( support == "mousewheel" ) { 52 | event.deltaY = - 1/40 * originalEvent.wheelDelta; 53 | // Webkit also support wheelDeltaX 54 | originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX ); 55 | } else { 56 | event.deltaY = originalEvent.detail; 57 | } 58 | 59 | // it's time to fire the callback 60 | return callback( event ); 61 | 62 | }, useCapture || false ); 63 | } 64 | 65 | })(window,document); -------------------------------------------------------------------------------- /src/polyfill.js: -------------------------------------------------------------------------------- 1 | import { Howler } from "./libs/Howler"; 2 | 3 | window.rAF = (function() { 4 | return ( 5 | window.requestAnimationFrame || 6 | window.webkitRequestAnimationFrame || 7 | window.mozRequestAnimationFrame || 8 | window.oRequestAnimationFrame || 9 | window.msRequestAnimationFrame 10 | ); 11 | })(); 12 | 13 | /** 14 | * @param {Boolean} value 15 | */ 16 | CanvasRenderingContext2D.prototype.setImageSmoothing = function(value) { 17 | 18 | this.imageSmoothingEnabled = value; 19 | this.oImageSmoothingEnabled = value; 20 | this.msImageSmoothingEnabled = value; 21 | this.mozImageSmoothingEnabled = value; 22 | this.webkitImageSmoothingEnabled = value; 23 | 24 | return void 0; 25 | 26 | }; 27 | 28 | /** 29 | * Clear a context 30 | * @param {String} color Clear by color 31 | */ 32 | CanvasRenderingContext2D.prototype.clear = function (color) { 33 | 34 | if (color) { 35 | let original = this.fillStyle; 36 | this.fillStyle = color; 37 | this.fillRect(0, 0, this.canvas.width, this.canvas.height); 38 | this.fillStyle = original; 39 | } else { 40 | this.clearRect(0, 0, this.canvas.width, this.canvas.height); 41 | } 42 | 43 | return void 0; 44 | 45 | }; 46 | 47 | // from: https://developer.mozilla.org/de/docs/Web/Events/wheel 48 | // creates a global "addWheelListener" method 49 | // example: addWheelListener( elem, function( e ) { console.log( e.deltaY ); e.preventDefault(); } ); 50 | (function(window, document) { 51 | 52 | var prefix = "", 53 | _addEventListener, support; 54 | 55 | // detect event model 56 | if (window.addEventListener) { 57 | _addEventListener = "addEventListener"; 58 | } else { 59 | _addEventListener = "attachEvent"; 60 | prefix = "on"; 61 | } 62 | 63 | // detect available wheel event 64 | support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel" 65 | document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel" 66 | "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox 67 | 68 | window.addWheelListener = function(elem, callback, useCapture) { 69 | _addWheelListener(elem, support, callback, useCapture); 70 | 71 | // handle MozMousePixelScroll in older Firefox 72 | if (support == "DOMMouseScroll") { 73 | _addWheelListener(elem, "MozMousePixelScroll", callback, useCapture); 74 | } 75 | }; 76 | 77 | function _addWheelListener(elem, eventName, callback, useCapture) { 78 | elem[_addEventListener](prefix + eventName, support == "wheel" ? callback : function(originalEvent) { 79 | !originalEvent && (originalEvent = window.event); 80 | 81 | // create a normalized event object 82 | var event = { 83 | // keep a ref to the original event object 84 | originalEvent: originalEvent, 85 | target: originalEvent.target || originalEvent.srcElement, 86 | type: "wheel", 87 | deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1, 88 | deltaX: 0, 89 | deltaZ: 0, 90 | preventDefault: function() { 91 | originalEvent.preventDefault ? 92 | originalEvent.preventDefault() : 93 | originalEvent.returnValue = false; 94 | } 95 | }; 96 | 97 | // calculate deltaY (and deltaX) according to the event 98 | if (support == "mousewheel") { 99 | event.deltaY = -1 / 40 * originalEvent.wheelDelta; 100 | // Webkit also support wheelDeltaX 101 | originalEvent.wheelDeltaX && (event.deltaX = -1 / 40 * originalEvent.wheelDeltaX); 102 | } else { 103 | event.deltaY = originalEvent.detail; 104 | } 105 | 106 | // it's time to fire the callback 107 | return callback(event); 108 | 109 | }, useCapture || false); 110 | } 111 | 112 | })(window, document); -------------------------------------------------------------------------------- /stats.js: -------------------------------------------------------------------------------- 1 | // stats.js - http://github.com/mrdoob/stats.js 2 | var Stats=function(){function f(a,e,b){a=document.createElement(a);a.id=e;a.style.cssText=b;return a}function l(a,e,b){var c=f("div",a,"padding:0 0 3px 3px;text-align:left;background:"+b),d=f("div",a+"Text","font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px;color:"+e);d.innerHTML=a.toUpperCase();c.appendChild(d);a=f("div",a+"Graph","width:74px;height:30px;background:"+e);c.appendChild(a);for(e=0;74>e;e++)a.appendChild(f("span","","width:1px;height:30px;float:left;opacity:0.9;background:"+ 3 | b));return c}function m(a){for(var b=c.children,d=0;dr+1E3&&(d=Math.round(1E3* 5 | t/(a-r)),u=Math.min(u,d),v=Math.max(v,d),A.textContent=d+" FPS ("+u+"-"+v+")",p(B,d/100),r=a,t=0,void 0!==h)){var b=performance.memory.usedJSHeapSize,c=performance.memory.jsHeapSizeLimit;h=Math.round(9.54E-7*b);y=Math.min(y,h);z=Math.max(z,h);E.textContent=h+" MB ("+y+"-"+z+")";p(F,b/c)}return a},update:function(){k=this.end()}}};"object"===typeof module&&(module.exports=Stats); 6 | 7 | var stats = new Stats(); 8 | stats.setMode( 0 ); // 0: fps, 1: ms, 2: mb 9 | 10 | // align top-left 11 | stats.domElement.style.position = 'absolute'; 12 | stats.domElement.style.left = '0px'; 13 | stats.domElement.style.top = '0px'; 14 | stats.domElement.style.zIndex = 999999; 15 | stats.domElement.style.opacity = 1.0; 16 | 17 | document.body.appendChild( stats.domElement ); 18 | 19 | var update = function () { 20 | 21 | stats.begin(); 22 | 23 | // monitored code goes here 24 | 25 | stats.end(); 26 | 27 | requestAnimationFrame( update ); 28 | 29 | }; 30 | 31 | requestAnimationFrame( update ); -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | entry: './src/Game/index.js', 6 | output: { 7 | path: __dirname, 8 | filename: 'bundle.js' 9 | }, 10 | module: { 11 | loaders: [{ 12 | loader: 'babel-loader', 13 | test: path.join(__dirname, 'src'), 14 | query: { 15 | plugins: ['transform-runtime'], 16 | presets: ['es2015', 'stage-0'], 17 | }, 18 | exclude: /node_modules/ 19 | }] 20 | }, 21 | plugins: [ 22 | // Avoid publishing files when compilation fails 23 | new webpack.NoErrorsPlugin()/*, 24 | new webpack.optimize.UglifyJsPlugin({ 25 | sourceMap: false, 26 | mangle: true, 27 | compress: true 28 | })*/ 29 | ], 30 | stats: { 31 | // Nice colored output 32 | colors: true 33 | } 34 | }; -------------------------------------------------------------------------------- /worlds/kanto/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "s_Name": "Kanto", 3 | "s_Settings": "Willkommen" 4 | } -------------------------------------------------------------------------------- /worlds/kanto/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "s_Name": "Kanto", 3 | "s_Settings": "Welcome" 4 | } -------------------------------------------------------------------------------- /worlds/kanto/index.js: -------------------------------------------------------------------------------- 1 | return (function() { 2 | 3 | "use strict"; 4 | 5 | var WORLD = { 6 | GLOBAL_FLAGS: { 7 | ADMIN_ONLINE: false, 8 | HEAL_DISABLED: false 9 | }, 10 | USER_FLAGS: { 11 | GOT_STARTER_PKMN: false, 12 | GOT_POKEDEX: false, 13 | BILLS_PC_ACCOUNT: false 14 | }, 15 | SETTINGS: { 16 | DAY_NIGHT_CYCLE: true 17 | } 18 | }; 19 | 20 | return (WORLD); 21 | 22 | })(); -------------------------------------------------------------------------------- /worlds/kanto/town/objects/building1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Building1", 3 | "sprite" : "building1.png", 4 | "normal": true, 5 | "width" : 32, 6 | "height" : 40, 7 | "scale" : 1, 8 | "shadow" : true, 9 | "collidable": true, 10 | "collisionBox": [ 11 | 0, 0, 0, 0, 12 | 1, 1, 1, 1, 13 | 1, 1, 1, 1, 14 | 1, 0, 1, 1, 15 | 1, 0, 1, 1 16 | ] 17 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/building1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/building1.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/building1_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/building1_normal.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/campfire.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "CampFire", 3 | "sprite" : "campfire.png", 4 | "normal": true, 5 | "width" : 8, 6 | "height" : 8, 7 | "scale" : 1, 8 | "shadow" : true, 9 | "shadowY": -2, 10 | "collidable": true, 11 | "animation": true, 12 | "animationFrames": 4, 13 | "animationSpeed": 300, 14 | "loop": true, 15 | "noise": "campfire", 16 | "noiseRadius": 32 17 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/campfire.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/campfire.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/campfire_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/campfire_normal.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/charizard.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Charizard", 3 | "sprite" : "charizard.png", 4 | "normal": true, 5 | "width" : 32, 6 | "height" : 40, 7 | "scale" : 1, 8 | "shadow" : true, 9 | "collidable": true, 10 | "shadowY": -14, 11 | "collisionBox": [ 12 | 0, 0, 0, 0, 13 | 0, 0, 0, 0, 14 | 1, 1, 1, 1, 15 | 1, 1, 1, 1, 16 | 1, 1, 1, 1 17 | ] 18 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/charizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/charizard.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/charizard_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/charizard_normal.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/door1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Door1", 3 | "sprite" : "door1.png", 4 | "width" : 10, 5 | "xMargin": -1, 6 | "height" : 7, 7 | "shadow" : false, 8 | "collidable": true, 9 | "animation": true, 10 | "animationFrames": 4, 11 | "animationSpeed": 500, 12 | "loop": false 13 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/door1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/door1.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/flower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Flower", 3 | "sprite" : "flower.png", 4 | "normal": true, 5 | "width" : 8, 6 | "height" : 8, 7 | "scale" : 1, 8 | "shadow" : true, 9 | "shadowY": -4, 10 | "collidable": false, 11 | "animation": true, 12 | "animationFrames": 4, 13 | "animationSpeed": 500, 14 | "loop": true 15 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/flower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/flower.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/flower_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/flower_normal.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/grass.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Grass", 3 | "sprite" : "grass.png", 4 | "width" : 8, 5 | "height" : 8, 6 | "scale" : 1, 7 | "shadow" : true, 8 | "shadowY": -4, 9 | "collidable": false, 10 | "animation": true, 11 | "animationFrames": 4, 12 | "animationSpeed": 175, 13 | "loop": true 14 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/grass.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/lantern.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Lantern", 3 | "sprite" : "lantern.png", 4 | "normal" : true, 5 | "width" : 8, 6 | "height" : 24, 7 | "scale" : 1, 8 | "shadow" : true, 9 | "shadowY": -5, 10 | "collidable": true, 11 | "collisionBox": [ 12 | 0, 13 | 0, 14 | 1 15 | ] 16 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/lantern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/lantern.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/lantern_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/lantern_normal.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/ping.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Ping", 3 | "sprite" : "ping.png", 4 | "width" : 8, 5 | "height" : 8, 6 | "scale" : 1, 7 | "zIndex" : 9999999, 8 | "shadow" : true, 9 | "shadowY": 0, 10 | "collidable": false 11 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/ping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/ping.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/raindrop.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "RainDrop", 3 | "sprite" : "raindrop.png", 4 | "width" : 15, 5 | "height" : 11, 6 | "scale" : 0.25, 7 | "shadow" : true, 8 | "shadowY": -2, 9 | "collidable": false, 10 | "animation": true, 11 | "animationFrames": 4, 12 | "animationSpeed": 100, 13 | "loop": true, 14 | "xMargin": 2, 15 | "yMargin": 2 16 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/raindrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/raindrop.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/sign.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Sign", 3 | "sprite" : "sign.png", 4 | "normal": true, 5 | "width" : 8, 6 | "height" : 9, 7 | "scale" : 1, 8 | "shadow" : true, 9 | "shadowY": -2, 10 | "collidable": true 11 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/sign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/sign.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/sign_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/sign_normal.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/table.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Table", 3 | "sprite" : "table.png", 4 | "normal": true, 5 | "width" : 16, 6 | "height" : 16, 7 | "scale" : 1, 8 | "shadow" : true, 9 | "collidable": true, 10 | "collisionBox": [ 11 | 1, 1, 12 | 1, 1 13 | ] 14 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/table.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/table_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/table_normal.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Tree", 3 | "sprite" : "tree.png", 4 | "normal" : true, 5 | "width" : 32, 6 | "height" : 32, 7 | "scale" : 1, 8 | "shadow" : true, 9 | "collidable": true, 10 | "collisionBox": [ 11 | 0, 0, 0, 0, 12 | 0, 0, 0, 0, 13 | 0, 1, 1, 0, 14 | 0, 1, 1, 0 15 | ] 16 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/tree.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/tree_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/tree_normal.png -------------------------------------------------------------------------------- /worlds/kanto/town/objects/water1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Water1", 3 | "sprite" : "water1.png", 4 | "width" : 16, 5 | "height" : 16, 6 | "scale" : 1, 7 | "zIndex" : 0, 8 | "shadow" : false, 9 | "animation": true, 10 | "animationFrames": 32, 11 | "loop": true, 12 | "jumpable": false, 13 | "collidable": true 14 | } -------------------------------------------------------------------------------- /worlds/kanto/town/objects/water1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maierfelix/PokeMMO/4ac97013664a1bae640f44ecc35f887f69bea67a/worlds/kanto/town/objects/water1.png --------------------------------------------------------------------------------