initial commit
@@ -0,0 +1 @@
|
|||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
Copyright (c) 2013, 2014, 2015, 2016 Jake Gordon and contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Javascript Tiny-Platformer
|
||||||
|
|
||||||
|
A very minimal javascript platform game
|
||||||
|
|
||||||
|
- [play the game](http://codeincomplete.com/projects/tiny-platformer/index.html)
|
||||||
|
- read the [original article](http://codeincomplete.com/posts/2013/5/27/tiny_platformer/)
|
||||||
|
- read a [follow up article](http://codeincomplete.com/posts/2013/6/2/tiny_platformer_revisited/) about adding monsters and treasure
|
||||||
|
- view the [source](https://github.com/jakesgordon/javascript-tiny-platformer)
|
||||||
|
|
||||||
|
Just a sinple example of how to have a tiny rectangle run around some rectangle platforms,
|
||||||
|
collecting rectangular gold and avoiding rectangular monsters.
|
||||||
|
|
||||||
|
# SUPPORTED BROWSERS
|
||||||
|
|
||||||
|
Should work in any modern browser with canvas support
|
||||||
|
|
||||||
|
# DEVELOPMENT
|
||||||
|
|
||||||
|
The game is 100% client side javascript, html and css. It should run when served up by any web server.
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
[MIT](http://en.wikipedia.org/wiki/MIT_License) license.
|
||||||
|
|
||||||
|
---
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# Readme
|
||||||
|
|
||||||
|
Simple JS platformer to modify and extend.
|
||||||
|
|
||||||
|
## Welche VS Code Extensions sollten installiert sein
|
||||||
|
|
||||||
|
- Better Comments
|
||||||
|
- Color Highlight
|
||||||
|
- Atom One Dark Theme
|
||||||
|
- Material Icon Theme
|
||||||
|
|
||||||
|
## Grundideen
|
||||||
|
|
||||||
|
- Camera Setup kann ich erstmal auslassen. Dann mache ich stattdessen single rooms (vgl. Lykia).
|
||||||
|
- Overworld und Levels?
|
||||||
|
- Character selection?
|
||||||
|
- Inventory System mit durchsuchbaren Kisten, Content bleibt erhalten.
|
||||||
|
- Auflösung von Gameboy, GBC, Game Gear ist 160 × 144 pixel.
|
||||||
|
- Sprite Animation kann ich genauso machen wie bei Pico-8 (<https://nerdyteachers.com/Explain/AnimateSprite/>, <https://www.youtube.com/watch?v=rtWGZ1uiBLY>)!
|
||||||
|
- Setting ist ein Space Adventure. Mit einem Raumschiff kann man viele verschiedene Umgebungen erreichen (Dschungel, Techno, Unterwasser, Höhlen,...)
|
||||||
|
- Commander Keen trifft Metroidvania (mit ein bisschen Subnautica?)
|
||||||
|
|
||||||
|
## Todo
|
||||||
|
|
||||||
|
- [x] Refactor code. Alles sortieren (Helper, Utils, Functions, Main Function, Objects usw.)
|
||||||
|
- [x] Robuste collision checks und collision maps bauen
|
||||||
|
- [x] Irgendeine Art Dev Function bauen, die mir übersichtliche console logs ausgibt.
|
||||||
|
- [x] "Doors" System entwickeln.
|
||||||
|
- [x] Die Camera funktioniert in Level2 noch nicht mit Begrenzung nach Rechts und Unten. Liegt das am Level selbst?
|
||||||
|
- [x] Pause Function
|
||||||
|
- [x] Sprite Animation (<https://codehs.com/tutorial/andy/Programming_Sprites_in_JavaScript>)
|
||||||
|
- [x] Maps umrüsten auf 16x16 px tiles. Ist in den Variablen schon vorbereitet, muss nur die Maps noch mal aus Tiled exportieren
|
||||||
|
- [x] Aus Tiled werden die Custom Properties nicht richtig exportiert
|
||||||
|
- [x] "type" muss ich ändern in "class" in der setup Funktion
|
||||||
|
- [x] Die properties werden als Array exportiert. Da brauche ich in der setup Funktion noch eine Function um die zu Konviertieren.
|
||||||
|
- [x] Globales Objekt bauen mit einem einzigen Wert ("life = 3" z.B.), den ich immer Anzeige und der über die Level hinaus bestehen bleibt.
|
||||||
|
- [ ] Gravity Variable für verschiedene Umgebungen (Land, Wasser, Space)
|
||||||
|
- [ ] Verschiedene Biomes
|
||||||
|
- [ ] Ice World
|
||||||
|
- [ ] Desert World
|
||||||
|
- [ ] Lava World
|
||||||
|
- [ ] Underwater
|
||||||
|
- [ ] Forest World
|
||||||
|
- [ ] Techno World
|
||||||
|
- [ ] Fix Scaling bei verschiedenen Level-Größen (protrait ist broken)
|
||||||
|
|
||||||
|
### Welche Assets nutze ich
|
||||||
|
|
||||||
|
- "Grotto Escape Pack" by Ansimuz
|
||||||
|
- "8-Bit Cave Loop" by Wolfgang
|
||||||
|
|
||||||
|
## Welche Tools nutze ich
|
||||||
|
|
||||||
|
- Beepbox für Musik <https://www.beepbox.co/>
|
||||||
|
- Tiled für Sprites und Maps <https://www.mapeditor.org/>
|
||||||
|
|
||||||
|
### Tiled
|
||||||
|
|
||||||
|
Map Setup:
|
||||||
|
|
||||||
|
- Orthogonal
|
||||||
|
- CSV
|
||||||
|
- Left Down (! das ist wichtig)
|
||||||
|
|
||||||
|
Layer in folgender Reihenfolge:
|
||||||
|
|
||||||
|
- foreground
|
||||||
|
- Object Layer 1
|
||||||
|
- tileLayer
|
||||||
|
- background
|
||||||
|
|
||||||
|
Export als ".tmj"
|
||||||
|
|
||||||
|
## Feature Roadmap
|
||||||
|
|
||||||
|
- [x] zweite Map
|
||||||
|
- [x] Variablen für alle Props, die die Map betreffen (MAP.tw/.th, Scaling, HUD-Scaling, TileAtlas)
|
||||||
|
- [x] Alphabet TileMap (oder font!)
|
||||||
|
- [x] Map Layers (CollisionLayer, Background, Foreground, Object-Layer) inkl. Anleitung für Tiled
|
||||||
|
- [x] Map Transitions (mit einem Doors Object)
|
||||||
|
- [x] Map Layers
|
||||||
|
- [x] Global Player Object
|
||||||
|
- [x] Shooting
|
||||||
|
- [x] Swimming
|
||||||
|
- [x] Ammo
|
||||||
|
- [ ] mehr Weapons
|
||||||
|
- [ ] animated Tiles
|
||||||
|
- [ ] Destructibles (haben eigenes "destructibles = {}" Objekt wie monsters oder treasure und haben hitpoints, eigener Layer oder gehören die zu Objects?)
|
||||||
|
- [ ] Upgrades/Powerups
|
||||||
|
- [ ] Ladders/Poles
|
||||||
|
- [ ] mehr Enemy Types (flying, wallcrawler, shooting)
|
||||||
|
- [ ] Game Start/Game Over Screen
|
||||||
|
- [ ] Inventory System
|
||||||
|
- [ ] CameraBox
|
||||||
|
- [ ] Sprites
|
||||||
|
- [ ] Player
|
||||||
|
- [ ] Enemies
|
||||||
|
- [ ] Items
|
||||||
|
- [ ] Map
|
||||||
|
|
||||||
|
## Possible Story
|
||||||
|
|
||||||
|
Biff Bolton, sidekick to intergalactic space adventurer Ace Hunter, is stranded on a strange planet after several parts of his ship fail and he has to make an emergency landing. To repair his ship he must explore the planet and delve deep into its mysterious caves and catacombs.
|
||||||
@@ -0,0 +1,683 @@
|
|||||||
|
{
|
||||||
|
"format": "BeepBox",
|
||||||
|
"version": 9,
|
||||||
|
"scale": "easy :)",
|
||||||
|
"key": "C",
|
||||||
|
"introBars": 0,
|
||||||
|
"loopBars": 4,
|
||||||
|
"beatsPerBar": 8,
|
||||||
|
"ticksPerBeat": 4,
|
||||||
|
"beatsPerMinute": 150,
|
||||||
|
"layeredInstruments": false,
|
||||||
|
"patternInstruments": false,
|
||||||
|
"channels": [
|
||||||
|
{
|
||||||
|
"type": "pitch",
|
||||||
|
"instruments": [
|
||||||
|
{
|
||||||
|
"type": "FM",
|
||||||
|
"volume": 80,
|
||||||
|
"eqFilter": [],
|
||||||
|
"preset": 325,
|
||||||
|
"effects": [
|
||||||
|
"chord type",
|
||||||
|
"note filter",
|
||||||
|
"reverb"
|
||||||
|
],
|
||||||
|
"chord": "strum",
|
||||||
|
"noteFilter": [
|
||||||
|
{
|
||||||
|
"type": "low-pass",
|
||||||
|
"cutoffHz": 19027.31,
|
||||||
|
"linearGain": 0.3536
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reverb": 33,
|
||||||
|
"fadeInSeconds": 0,
|
||||||
|
"fadeOutTicks": -3,
|
||||||
|
"algorithm": "1←2←3←4",
|
||||||
|
"feedbackType": "3⟲",
|
||||||
|
"feedbackAmplitude": 4,
|
||||||
|
"operators": [
|
||||||
|
{
|
||||||
|
"frequency": "3×",
|
||||||
|
"amplitude": 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"frequency": "1×",
|
||||||
|
"amplitude": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"frequency": "13×",
|
||||||
|
"amplitude": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"frequency": "1×",
|
||||||
|
"amplitude": 11
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"envelopes": [
|
||||||
|
{
|
||||||
|
"target": "noteFilterAllFreqs",
|
||||||
|
"envelope": "twang 1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
57
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 4,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 6,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sequence": [
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"octaveScrollBar": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "pitch",
|
||||||
|
"instruments": [
|
||||||
|
{
|
||||||
|
"type": "FM",
|
||||||
|
"volume": 80,
|
||||||
|
"eqFilter": [],
|
||||||
|
"preset": 649,
|
||||||
|
"effects": [
|
||||||
|
"transition type",
|
||||||
|
"vibrato",
|
||||||
|
"reverb"
|
||||||
|
],
|
||||||
|
"transition": "slide",
|
||||||
|
"vibrato": "delayed",
|
||||||
|
"reverb": 33,
|
||||||
|
"fadeInSeconds": 0.0263,
|
||||||
|
"fadeOutTicks": -3,
|
||||||
|
"algorithm": "1←(2 3 4)",
|
||||||
|
"feedbackType": "1⟲",
|
||||||
|
"feedbackAmplitude": 2,
|
||||||
|
"operators": [
|
||||||
|
{
|
||||||
|
"frequency": "2×",
|
||||||
|
"amplitude": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"frequency": "1×",
|
||||||
|
"amplitude": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"frequency": "~1×",
|
||||||
|
"amplitude": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"frequency": "1×",
|
||||||
|
"amplitude": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"envelopes": [
|
||||||
|
{
|
||||||
|
"target": "operatorAmplitude",
|
||||||
|
"envelope": "punch",
|
||||||
|
"index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "feedbackAmplitude",
|
||||||
|
"envelope": "twang 3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
38
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 4,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 6,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sequence": [
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"octaveScrollBar": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "pitch",
|
||||||
|
"instruments": [
|
||||||
|
{
|
||||||
|
"type": "FM",
|
||||||
|
"volume": 80,
|
||||||
|
"eqFilter": [],
|
||||||
|
"preset": 322,
|
||||||
|
"effects": [
|
||||||
|
"chord type",
|
||||||
|
"note filter",
|
||||||
|
"reverb"
|
||||||
|
],
|
||||||
|
"chord": "strum",
|
||||||
|
"noteFilter": [
|
||||||
|
{
|
||||||
|
"type": "low-pass",
|
||||||
|
"cutoffHz": 13454.34,
|
||||||
|
"linearGain": 0.25
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"reverb": 33,
|
||||||
|
"fadeInSeconds": 0,
|
||||||
|
"fadeOutTicks": 48,
|
||||||
|
"algorithm": "1←(2 3←4)",
|
||||||
|
"feedbackType": "3⟲",
|
||||||
|
"feedbackAmplitude": 4,
|
||||||
|
"operators": [
|
||||||
|
{
|
||||||
|
"frequency": "1×",
|
||||||
|
"amplitude": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"frequency": "1×",
|
||||||
|
"amplitude": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"frequency": "11×",
|
||||||
|
"amplitude": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"frequency": "1×",
|
||||||
|
"amplitude": 9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"envelopes": [
|
||||||
|
{
|
||||||
|
"target": "noteFilterAllFreqs",
|
||||||
|
"envelope": "twang 1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "operatorAmplitude",
|
||||||
|
"envelope": "twang 3",
|
||||||
|
"index": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "feedbackAmplitude",
|
||||||
|
"envelope": "twang 1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
19
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 4,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 6,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
19
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 8,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 10,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
28
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 12,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 14,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
19
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 20,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 22,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
19
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 24,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 26,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
28
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 28,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 30,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sequence": [
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"octaveScrollBar": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "drum",
|
||||||
|
"instruments": [
|
||||||
|
{
|
||||||
|
"type": "noise",
|
||||||
|
"volume": 80,
|
||||||
|
"eqFilter": [
|
||||||
|
{
|
||||||
|
"type": "low-pass",
|
||||||
|
"cutoffHz": 16000,
|
||||||
|
"linearGain": 0.3536
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preset": 69,
|
||||||
|
"effects": [
|
||||||
|
"chord type"
|
||||||
|
],
|
||||||
|
"chord": "arpeggio",
|
||||||
|
"fadeInSeconds": 0,
|
||||||
|
"fadeOutTicks": -3,
|
||||||
|
"wave": "retro",
|
||||||
|
"envelopes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"notes": [
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 0,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 2,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"continuesLastPattern": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
6
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 4,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 6,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
6
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 6,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 8,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 8,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 10,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 10,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 12,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 12,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 14,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 16,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 18,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 20,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 23,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pitches": [
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"points": [
|
||||||
|
{
|
||||||
|
"tick": 24,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tick": 26,
|
||||||
|
"pitchBend": 0,
|
||||||
|
"volume": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sequence": [
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/*! FPSMeter 0.3.1 - 9th May 2013 | https://github.com/Darsain/fpsmeter */
|
||||||
|
(function(m,j){function s(a,e){for(var g in e)try{a.style[g]=e[g]}catch(j){}return a}function H(a){return null==a?String(a):"object"===typeof a||"function"===typeof a?Object.prototype.toString.call(a).match(/\s([a-z]+)/i)[1].toLowerCase()||"object":typeof a}function R(a,e){if("array"!==H(e))return-1;if(e.indexOf)return e.indexOf(a);for(var g=0,j=e.length;g<j;g++)if(e[g]===a)return g;return-1}function I(){var a=arguments,e;for(e in a[1])if(a[1].hasOwnProperty(e))switch(H(a[1][e])){case "object":a[0][e]=
|
||||||
|
I({},a[0][e],a[1][e]);break;case "array":a[0][e]=a[1][e].slice(0);break;default:a[0][e]=a[1][e]}return 2<a.length?I.apply(null,[a[0]].concat(Array.prototype.slice.call(a,2))):a[0]}function N(a){a=Math.round(255*a).toString(16);return 1===a.length?"0"+a:a}function S(a,e,g,j){if(a.addEventListener)a[j?"removeEventListener":"addEventListener"](e,g,!1);else if(a.attachEvent)a[j?"detachEvent":"attachEvent"]("on"+e,g)}function D(a,e){function g(a,b,d,c){return y[0|a][Math.round(Math.min((b-d)/(c-d)*J,J))]}
|
||||||
|
function r(){f.legend.fps!==q&&(f.legend.fps=q,f.legend[T]=q?"FPS":"ms");K=q?b.fps:b.duration;f.count[T]=999<K?"999+":K.toFixed(99<K?0:d.decimals)}function m(){z=A();L<z-d.threshold&&(b.fps-=b.fps/Math.max(1,60*d.smoothing/d.interval),b.duration=1E3/b.fps);for(c=d.history;c--;)E[c]=0===c?b.fps:E[c-1],F[c]=0===c?b.duration:F[c-1];r();if(d.heat){if(w.length)for(c=w.length;c--;)w[c].el.style[h[w[c].name].heatOn]=q?g(h[w[c].name].heatmap,b.fps,0,d.maxFps):g(h[w[c].name].heatmap,b.duration,d.threshold,
|
||||||
|
0);if(f.graph&&h.column.heatOn)for(c=u.length;c--;)u[c].style[h.column.heatOn]=q?g(h.column.heatmap,E[c],0,d.maxFps):g(h.column.heatmap,F[c],d.threshold,0)}if(f.graph)for(p=0;p<d.history;p++)u[p].style.height=(q?E[p]?Math.round(O/d.maxFps*Math.min(E[p],d.maxFps)):0:F[p]?Math.round(O/d.threshold*Math.min(F[p],d.threshold)):0)+"px"}function k(){20>d.interval?(x=M(k),m()):(x=setTimeout(k,d.interval),P=M(m))}function G(a){a=a||window.event;a.preventDefault?(a.preventDefault(),a.stopPropagation()):(a.returnValue=
|
||||||
|
!1,a.cancelBubble=!0);b.toggle()}function U(){d.toggleOn&&S(f.container,d.toggleOn,G,1);a.removeChild(f.container)}function V(){f.container&&U();h=D.theme[d.theme];y=h.compiledHeatmaps||[];if(!y.length&&h.heatmaps.length){for(p=0;p<h.heatmaps.length;p++){y[p]=[];for(c=0;c<=J;c++){var b=y[p],e=c,g;g=0.33/J*c;var j=h.heatmaps[p].saturation,m=h.heatmaps[p].lightness,n=void 0,k=void 0,l=void 0,t=l=void 0,v=n=k=void 0,v=void 0,l=0.5>=m?m*(1+j):m+j-m*j;0===l?g="#000":(t=2*m-l,k=(l-t)/l,g*=6,n=Math.floor(g),
|
||||||
|
v=g-n,v*=l*k,0===n||6===n?(n=l,k=t+v,l=t):1===n?(n=l-v,k=l,l=t):2===n?(n=t,k=l,l=t+v):3===n?(n=t,k=l-v):4===n?(n=t+v,k=t):(n=l,k=t,l-=v),g="#"+N(n)+N(k)+N(l));b[e]=g}}h.compiledHeatmaps=y}f.container=s(document.createElement("div"),h.container);f.count=f.container.appendChild(s(document.createElement("div"),h.count));f.legend=f.container.appendChild(s(document.createElement("div"),h.legend));f.graph=d.graph?f.container.appendChild(s(document.createElement("div"),h.graph)):0;w.length=0;for(var q in f)f[q]&&
|
||||||
|
h[q].heatOn&&w.push({name:q,el:f[q]});u.length=0;if(f.graph){f.graph.style.width=d.history*h.column.width+(d.history-1)*h.column.spacing+"px";for(c=0;c<d.history;c++)u[c]=f.graph.appendChild(s(document.createElement("div"),h.column)),u[c].style.position="absolute",u[c].style.bottom=0,u[c].style.right=c*h.column.width+c*h.column.spacing+"px",u[c].style.width=h.column.width+"px",u[c].style.height="0px"}s(f.container,d);r();a.appendChild(f.container);f.graph&&(O=f.graph.clientHeight);d.toggleOn&&("click"===
|
||||||
|
d.toggleOn&&(f.container.style.cursor="pointer"),S(f.container,d.toggleOn,G))}"object"===H(a)&&a.nodeType===j&&(e=a,a=document.body);a||(a=document.body);var b=this,d=I({},D.defaults,e||{}),f={},u=[],h,y,J=100,w=[],W=0,B=d.threshold,Q=0,L=A()-B,z,E=[],F=[],x,P,q="fps"===d.show,O,K,c,p;b.options=d;b.fps=0;b.duration=0;b.isPaused=0;b.tickStart=function(){Q=A()};b.tick=function(){z=A();W=z-L;B+=(W-B)/d.smoothing;b.fps=1E3/B;b.duration=Q<L?B:z-Q;L=z};b.pause=function(){x&&(b.isPaused=1,clearTimeout(x),
|
||||||
|
C(x),C(P),x=P=0);return b};b.resume=function(){x||(b.isPaused=0,k());return b};b.set=function(a,c){d[a]=c;q="fps"===d.show;-1!==R(a,X)&&V();-1!==R(a,Y)&&s(f.container,d);return b};b.showDuration=function(){b.set("show","ms");return b};b.showFps=function(){b.set("show","fps");return b};b.toggle=function(){b.set("show",q?"ms":"fps");return b};b.hide=function(){b.pause();f.container.style.display="none";return b};b.show=function(){b.resume();f.container.style.display="block";return b};b.destroy=function(){b.pause();
|
||||||
|
U();b.tick=b.tickStart=function(){}};V();k()}var A,r=m.performance;A=r&&(r.now||r.webkitNow)?r[r.now?"now":"webkitNow"].bind(r):function(){return+new Date};for(var C=m.cancelAnimationFrame||m.cancelRequestAnimationFrame,M=m.requestAnimationFrame,r=["moz","webkit","o"],G=0,k=0,Z=r.length;k<Z&&!C;++k)M=(C=m[r[k]+"CancelAnimationFrame"]||m[r[k]+"CancelRequestAnimationFrame"])&&m[r[k]+"RequestAnimationFrame"];C||(M=function(a){var e=A(),g=Math.max(0,16-(e-G));G=e+g;return m.setTimeout(function(){a(e+
|
||||||
|
g)},g)},C=function(a){clearTimeout(a)});var T="string"===H(document.createElement("div").textContent)?"textContent":"innerText";D.extend=I;window.FPSMeter=D;D.defaults={interval:100,smoothing:10,show:"fps",toggleOn:"click",decimals:1,maxFps:60,threshold:100,position:"absolute",zIndex:10,left:"5px",top:"5px",right:"auto",bottom:"auto",margin:"0 0 0 0",theme:"dark",heat:0,graph:0,history:20};var X=["toggleOn","theme","heat","graph","history"],Y="position zIndex left top right bottom margin".split(" ")})(window);(function(m,j){j.theme={};var s=j.theme.base={heatmaps:[],container:{heatOn:null,heatmap:null,padding:"5px",minWidth:"95px",height:"30px",lineHeight:"30px",textAlign:"right",textShadow:"none"},count:{heatOn:null,heatmap:null,position:"absolute",top:0,right:0,padding:"5px 10px",height:"30px",fontSize:"24px",fontFamily:"Consolas, Andale Mono, monospace",zIndex:2},legend:{heatOn:null,heatmap:null,position:"absolute",top:0,left:0,padding:"5px 10px",height:"30px",fontSize:"12px",lineHeight:"32px",fontFamily:"sans-serif",
|
||||||
|
textAlign:"left",zIndex:2},graph:{heatOn:null,heatmap:null,position:"relative",boxSizing:"padding-box",MozBoxSizing:"padding-box",height:"100%",zIndex:1},column:{width:4,spacing:1,heatOn:null,heatmap:null}};j.theme.dark=j.extend({},s,{heatmaps:[{saturation:0.8,lightness:0.8}],container:{background:"#222",color:"#fff",border:"1px solid #1a1a1a",textShadow:"1px 1px 0 #222"},count:{heatOn:"color"},column:{background:"#3f3f3f"}});j.theme.light=j.extend({},s,{heatmaps:[{saturation:0.5,lightness:0.5}],
|
||||||
|
container:{color:"#666",background:"#fff",textShadow:"1px 1px 0 rgba(255,255,255,.5), -1px -1px 0 rgba(255,255,255,.5)",boxShadow:"0 0 0 1px rgba(0,0,0,.1)"},count:{heatOn:"color"},column:{background:"#eaeaea"}});j.theme.colorful=j.extend({},s,{heatmaps:[{saturation:0.5,lightness:0.6}],container:{heatOn:"backgroundColor",background:"#888",color:"#fff",textShadow:"1px 1px 0 rgba(0,0,0,.2)",boxShadow:"0 0 0 1px rgba(0,0,0,.1)"},column:{background:"#777",backgroundColor:"rgba(0,0,0,.2)"}});j.theme.transparent=
|
||||||
|
j.extend({},s,{heatmaps:[{saturation:0.8,lightness:0.5}],container:{padding:0,color:"#fff",textShadow:"1px 1px 0 rgba(0,0,0,.5)"},count:{padding:"0 5px",height:"40px",lineHeight:"40px"},legend:{padding:"0 5px",height:"40px",lineHeight:"42px"},graph:{height:"40px"},column:{width:5,background:"#999",heatOn:"backgroundColor",opacity:0.5}})})(window,FPSMeter);
|
||||||
|
After Width: | Height: | Size: 273 B |
|
After Width: | Height: | Size: 228 B |
|
After Width: | Height: | Size: 230 B |
|
After Width: | Height: | Size: 250 B |
|
After Width: | Height: | Size: 255 B |
|
After Width: | Height: | Size: 236 B |
|
After Width: | Height: | Size: 350 B |
|
After Width: | Height: | Size: 364 B |
|
After Width: | Height: | Size: 353 B |
|
After Width: | Height: | Size: 374 B |
|
After Width: | Height: | Size: 373 B |
|
After Width: | Height: | Size: 398 B |
|
After Width: | Height: | Size: 361 B |
|
After Width: | Height: | Size: 335 B |
|
After Width: | Height: | Size: 366 B |
|
After Width: | Height: | Size: 268 B |
|
After Width: | Height: | Size: 238 B |
|
After Width: | Height: | Size: 258 B |
|
After Width: | Height: | Size: 416 B |
|
After Width: | Height: | Size: 417 B |
|
After Width: | Height: | Size: 634 B |
|
After Width: | Height: | Size: 548 B |
|
After Width: | Height: | Size: 607 B |
|
After Width: | Height: | Size: 420 B |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 246 B |
|
After Width: | Height: | Size: 262 B |
|
After Width: | Height: | Size: 454 B |
|
After Width: | Height: | Size: 329 B |
|
After Width: | Height: | Size: 409 B |
|
After Width: | Height: | Size: 260 B |
@@ -0,0 +1,10 @@
|
|||||||
|
Artwork created by Luis Zuno (@ansimuz)
|
||||||
|
|
||||||
|
License for Everyone. This piece is under a CC-BY-3.0 License http://creativecommons.org/licenses/by/3.0/
|
||||||
|
|
||||||
|
You can copy, modify, distribute and perform the work, even for commercial purposes, as long as you give appropiate credit.
|
||||||
|
|
||||||
|
You are not require to credit this work if you are or had been a Patreon Supporter at https://www.patreon.com/ansimuz
|
||||||
|
|
||||||
|
Thanks get more like these at: https://www.patreon.com/ansimuz
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 524 B |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 492 B |
|
After Width: | Height: | Size: 358 B |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 508 B |
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,66 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||||
|
<title>Javascript Platformer</title>
|
||||||
|
<link rel="stylesheet" href="style.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="platformer">
|
||||||
|
<canvas id="canvas">
|
||||||
|
<div class="unsupported">
|
||||||
|
Sorry, this example cannot be run because your browser does not
|
||||||
|
support the <canvas> element
|
||||||
|
</div>
|
||||||
|
</canvas>
|
||||||
|
|
||||||
|
<div id="instructions">
|
||||||
|
<b>LEFT/RIGHT</b> to move, <b>SPACE</b> to jump, collect gold and jump
|
||||||
|
on monsters.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="controls-wrapper">
|
||||||
|
<div class="dpad-wrapper">
|
||||||
|
<div class="dpad-top-bottom">
|
||||||
|
<button class="dpad-button up"></button>
|
||||||
|
</div>
|
||||||
|
<div class="dpad-middle">
|
||||||
|
<button class="dpad-button left"></button>
|
||||||
|
<button class="dpad-button right"></button>
|
||||||
|
</div>
|
||||||
|
<div class="dpad-top-bottom">
|
||||||
|
<button class="dpad-button down"></button>
|
||||||
|
</div>
|
||||||
|
</div
|
||||||
|
|
||||||
|
|
||||||
|
<div id="buttons">
|
||||||
|
<div class="buttons-wrapper">
|
||||||
|
<button class="button">A</button>
|
||||||
|
<button class="button">B</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="fpsmeter.min.js"></script>
|
||||||
|
|
||||||
|
<script src="js/utils.js"></script>
|
||||||
|
<script src="levels/levelObjects.js"></script>
|
||||||
|
<script src="levels/level1.js"></script>
|
||||||
|
<script src="levels/level2.js"></script>
|
||||||
|
<script src="levels/level3.js"></script>
|
||||||
|
<script src="js/variables.js"></script>
|
||||||
|
|
||||||
|
<script src="js/input.js"></script>
|
||||||
|
<script src="js/render.js"></script>
|
||||||
|
<script src="js/update.js"></script>
|
||||||
|
<script src="js/camera.js"></script>
|
||||||
|
<script src="js/setup.js"></script>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// 3.2 Simple CAMERA
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function renderCamera() {
|
||||||
|
// ! Ich muss diese Funktion gegen eine mit CameraBox austauschen. Diese kann ich aber für TopDown/Overworld Level benutzen.
|
||||||
|
|
||||||
|
const cameraWidth = mapWidth * TILE
|
||||||
|
const cameraHeight = mapHeight * TILE
|
||||||
|
|
||||||
|
const camera = {
|
||||||
|
get x() {
|
||||||
|
if (player.x < scaledCanvas.width) return 0
|
||||||
|
if (player.x > cameraWidth - scaledCanvas.width)
|
||||||
|
return -(cameraWidth - scaledCanvas.width * 2)
|
||||||
|
else return -(player.x - scaledCanvas.width)
|
||||||
|
},
|
||||||
|
get y() {
|
||||||
|
if (player.y < scaledCanvas.height) return 0
|
||||||
|
if (player.y > cameraHeight - scaledCanvas.height)
|
||||||
|
return -(cameraHeight - scaledCanvas.height * 2)
|
||||||
|
else return -(player.y - scaledCanvas.height)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.translate(camera.x, camera.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Box CAMERA
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
let cameraWithBox = {
|
||||||
|
position: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCameraWithBox() {
|
||||||
|
//console.log('log renderCameraWithBox position.x: ', cameraWithBox.position.x)
|
||||||
|
ctx.translate(cameraWithBox.position.x, cameraWithBox.position.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cameraBox = {
|
||||||
|
// ! Die Werte stimme nur ungefähr, geben mir aber eine grobe CameraBox zurück.
|
||||||
|
get x() {
|
||||||
|
return -(canvas.width / 2 / 2 - 100 - player.x)
|
||||||
|
},
|
||||||
|
get y() {
|
||||||
|
return -(canvas.height / 2 / 2 - 150 - player.y)
|
||||||
|
},
|
||||||
|
width: canvas.width / 2 - 200,
|
||||||
|
height: canvas.height / 2 - 300,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ! "camera" muss ich dann in der renderCamera() in der render Funktion anpassen.
|
||||||
|
// ? Woher kommt this.velocity ?
|
||||||
|
|
||||||
|
function shouldPanCameraToTheLeft({ canvas, camera }) {
|
||||||
|
const cameraboxRightSide = cameraBox.x + cameraBox.width
|
||||||
|
const scaledDownCanvasWidth = canvas.width / 2
|
||||||
|
|
||||||
|
// console.log('log cameraboxRightSide: ', cameraboxRightSide)
|
||||||
|
//console.log('log cameraboxRightSide >= 2048: ', cameraboxRightSide >= 2048)
|
||||||
|
// ! Woher kommt der Wert hier?
|
||||||
|
if (cameraboxRightSide >= 2048) return
|
||||||
|
|
||||||
|
//console.log('log player.x: ', player.x)
|
||||||
|
|
||||||
|
//console.log('log scaledDownCanvasWidth: ', scaledDownCanvasWidth)
|
||||||
|
|
||||||
|
/* console.log(
|
||||||
|
'log shouldPanCameraToTheLeft: ',
|
||||||
|
cameraboxRightSide >= scaledDownCanvasWidth
|
||||||
|
) */
|
||||||
|
|
||||||
|
if (
|
||||||
|
cameraboxRightSide >=
|
||||||
|
scaledDownCanvasWidth + Math.abs(cameraWithBox.position.x)
|
||||||
|
) {
|
||||||
|
cameraWithBox.position.x -= player.x - canvas.width / 2 / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldPanCameraToTheRight({ canvas, cameraprop }) {
|
||||||
|
//console.log('log shouldPanCameraToTheRight')
|
||||||
|
//console.log('log entity x update: ', (player.x + step * player.dx).toFixed(2))
|
||||||
|
//console.log('log cameraBox.x <= 0: ', cameraBox.x <= 0)
|
||||||
|
if (cameraBox.x <= 0) return
|
||||||
|
|
||||||
|
if (cameraBox.x <= Math.abs(cameraWithBox.position.x)) {
|
||||||
|
cameraWithBox.position.x = cameraWithBox.position.x--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldPanCameraDown({ canvas, camera }) {
|
||||||
|
//console.log('log shouldPanCameraDown')
|
||||||
|
/* if (cameraBox.position.y + this.velocity.y <= 0) return
|
||||||
|
|
||||||
|
if (cameraBox.position.y <= Math.abs(camera.position.y)) {
|
||||||
|
camera.position.y -= this.velocity.y
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldPanCameraUp({ canvas, camera }) {
|
||||||
|
// console.log('log shouldPanCameraUp')
|
||||||
|
/* if (cameraBox.position.y + cameraBox.height + this.velocity.y >= 432) return
|
||||||
|
|
||||||
|
const scaledCanvasHeight = canvas.height / 4
|
||||||
|
|
||||||
|
if (
|
||||||
|
cameraBox.position.y + cameraBox.height >=
|
||||||
|
Math.abs(camera.position.y) + scaledCanvasHeight
|
||||||
|
) {
|
||||||
|
camera.position.y -= this.velocity.y
|
||||||
|
} */
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// 3.1 INPUT
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function onkey(ev, key, down) {
|
||||||
|
switch (key) {
|
||||||
|
case KEY.LEFT:
|
||||||
|
player.left = down
|
||||||
|
shouldPanCameraToTheRight({ canvas, cameraWithBox })
|
||||||
|
ev.preventDefault()
|
||||||
|
return false
|
||||||
|
case KEY.RIGHT:
|
||||||
|
player.right = down
|
||||||
|
shouldPanCameraToTheLeft({ canvas, cameraWithBox })
|
||||||
|
ev.preventDefault()
|
||||||
|
return false
|
||||||
|
case KEY.X:
|
||||||
|
player.jump = down
|
||||||
|
// console.log('log Key X')
|
||||||
|
shouldPanCameraDown({ canvas, cameraWithBox })
|
||||||
|
ev.preventDefault()
|
||||||
|
return false
|
||||||
|
case KEY.Y:
|
||||||
|
console.log('log shoot')
|
||||||
|
// player.shooting = down
|
||||||
|
shoot(player)
|
||||||
|
ev.preventDefault()
|
||||||
|
return false
|
||||||
|
case KEY.UP:
|
||||||
|
console.log('log Key Up')
|
||||||
|
player.up = down
|
||||||
|
shouldPanCameraDown({ canvas, cameraWithBox })
|
||||||
|
ev.preventDefault()
|
||||||
|
return false
|
||||||
|
case KEY.DOWN:
|
||||||
|
console.log('log Key Down')
|
||||||
|
player.down = down
|
||||||
|
ev.preventDefault()
|
||||||
|
return false
|
||||||
|
case KEY.ENTER:
|
||||||
|
console.log('log Key Enter')
|
||||||
|
player.interact = down
|
||||||
|
ev.preventDefault()
|
||||||
|
return false
|
||||||
|
case KEY.SPACE:
|
||||||
|
console.log('log Key Space')
|
||||||
|
ev.preventDefault()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
function main() {
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// 6. THE GAME LOOP
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var counter = 0,
|
||||||
|
dt = 0,
|
||||||
|
now,
|
||||||
|
last = timestamp(),
|
||||||
|
fpsmeter = new FPSMeter({
|
||||||
|
decimals: 0,
|
||||||
|
graph: true,
|
||||||
|
theme: 'dark',
|
||||||
|
left: '5px',
|
||||||
|
})
|
||||||
|
|
||||||
|
function frame() {
|
||||||
|
fpsmeter.tickStart()
|
||||||
|
now = timestamp()
|
||||||
|
dt = dt + Math.min(1, (now - last) / 1000)
|
||||||
|
while (dt > step) {
|
||||||
|
dt = dt - step
|
||||||
|
|
||||||
|
// * Update wird pausiert bei Toggle 'p'.
|
||||||
|
if (!paused) {
|
||||||
|
update(step)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(ctx, counter, dt, currentLevel)
|
||||||
|
|
||||||
|
last = now
|
||||||
|
counter++
|
||||||
|
fpsmeter.tick()
|
||||||
|
requestAnimationFrame(frame, canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener(
|
||||||
|
'keydown',
|
||||||
|
function (ev) {
|
||||||
|
return onkey(ev, ev.key, true)
|
||||||
|
},
|
||||||
|
false
|
||||||
|
)
|
||||||
|
document.addEventListener(
|
||||||
|
'keyup',
|
||||||
|
function (ev) {
|
||||||
|
return onkey(ev, ev.key, false)
|
||||||
|
},
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
// * EventListener für Pause-Taste
|
||||||
|
window.addEventListener('keydown', function (e) {
|
||||||
|
var key = e.key
|
||||||
|
if (key === 'p') {
|
||||||
|
// p key
|
||||||
|
togglePause()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// * EventListener für DevInfos Toggle
|
||||||
|
window.addEventListener('keydown', function (e) {
|
||||||
|
var key = e.key
|
||||||
|
if (key === 'o') {
|
||||||
|
// p key
|
||||||
|
toggleDevInfos()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// ! Vielleicht kann ich statt des setup() an dieser Stelle auch eine gameStart() Funktion erstellen, die beim erstmaligen Start alles erledigt. Später wird dann ja immer die _initLevel() aufgerufen.
|
||||||
|
setup(currentLevel.levelData) // ! Was brauche ich hier, damit das variabel alle Level laden kann?
|
||||||
|
|
||||||
|
console.log('log monsters ', monsters)
|
||||||
|
|
||||||
|
frame()
|
||||||
|
}
|
||||||
|
|
||||||
|
// * window onload function
|
||||||
|
;(function () {
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// POLYFILLS
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if (!window.requestAnimationFrame) {
|
||||||
|
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
|
||||||
|
window.requestAnimationFrame =
|
||||||
|
window.webkitRequestAnimationFrame ||
|
||||||
|
window.mozRequestAnimationFrame ||
|
||||||
|
window.oRequestAnimationFrame ||
|
||||||
|
window.msRequestAnimationFrame ||
|
||||||
|
function (callback, element) {
|
||||||
|
window.setTimeout(callback, 1000 / 60)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
})()
|
||||||
@@ -0,0 +1,375 @@
|
|||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// 4. RENDERING
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function render(ctx, frame, dt, thiscurrentLevel) {
|
||||||
|
// ? Kann ich mich eigentlich mit scale und translate noch weiter an Pico-8 orientieren und in den variables sowas wie scale = ctx.scale oder so erstellen?
|
||||||
|
// * Disable Image Smoothing
|
||||||
|
ctx.mozImageSmoothingEnabled = false
|
||||||
|
ctx.webkitImageSmoothingEnabled = false
|
||||||
|
ctx.msImageSmoothingEnabled = false
|
||||||
|
ctx.imageSmoothingEnabled = false
|
||||||
|
|
||||||
|
ctx.save()
|
||||||
|
ctx.scale(
|
||||||
|
Math.round(thiscurrentLevel.scalingFactor / 2),
|
||||||
|
Math.round(thiscurrentLevel.scalingFactor / 2)
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, width, height)
|
||||||
|
renderCamera()
|
||||||
|
//renderCameraWithBox()
|
||||||
|
renderBackground(ctx, thiscurrentLevel.backgroundAtlas, bgCells)
|
||||||
|
renderMap(ctx, thiscurrentLevel.levelAtlas, cells)
|
||||||
|
renderPlayer(ctx, playerAtlas, dt, frame)
|
||||||
|
renderBullets(ctx, itemsAtlas, dt, frame)
|
||||||
|
renderMonsters(ctx, enemyAtlas, dt, frame)
|
||||||
|
renderTreasure(ctx, itemsAtlas, dt, frame)
|
||||||
|
renderPickups(ctx, itemsAtlas, dt, frame)
|
||||||
|
renderForeground(ctx, thiscurrentLevel.foregroundAtlas, fgCells)
|
||||||
|
renderLiquids(ctx, thiscurrentLevel.foregroundAtlas, fgCells)
|
||||||
|
|
||||||
|
// * Draw CameraBox
|
||||||
|
//renderCameraBox()
|
||||||
|
ctx.restore()
|
||||||
|
renderPause()
|
||||||
|
renderHud(ctx, meterAtlas, dt, frame)
|
||||||
|
renderHudSprites()
|
||||||
|
//fadeScreen(frame, dt)
|
||||||
|
renderDevInfos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map Limit Variables
|
||||||
|
|
||||||
|
// * Das hier ist die allgemeine Funktion um Tiles aus dem TileAtlas auf den Canvas zu malen.
|
||||||
|
function drawTile(levelAtlas, cell, dx, dy, opacity) {
|
||||||
|
levelAtlas.sourceY =
|
||||||
|
Math.floor(cell / levelAtlas.atlasCol) * levelAtlas.tileSize
|
||||||
|
levelAtlas.sourceX = (cell % levelAtlas.atlasCol) * levelAtlas.tileSize
|
||||||
|
// ? Wofür stehen die Werte sWidth, sHeight, dWidth, dHeight
|
||||||
|
|
||||||
|
ctx.globalAlpha = opacity
|
||||||
|
// ! Kann ich die ganzen festen Zahlen als Variablen aus der Map holen?
|
||||||
|
ctx.drawImage(
|
||||||
|
levelAtlas.tileAtlas,
|
||||||
|
levelAtlas.sourceX,
|
||||||
|
levelAtlas.sourceY,
|
||||||
|
16,
|
||||||
|
16,
|
||||||
|
dx * 16,
|
||||||
|
dy * 16,
|
||||||
|
16,
|
||||||
|
16
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBackground(ctx, levelAtlas) {
|
||||||
|
var x, y, cell
|
||||||
|
for (y = 0; y < mapHeight; y++) {
|
||||||
|
for (x = 0; x < mapWidth; x++) {
|
||||||
|
cell = bgTcell(x, y, mapWidth)
|
||||||
|
if (cell) {
|
||||||
|
drawTile(levelAtlas, cell - levelAtlas.offset, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderForeground(ctx, levelAtlas) {
|
||||||
|
var x, y, cell
|
||||||
|
for (y = 0; y < mapHeight; y++) {
|
||||||
|
for (x = 0; x < mapWidth; x++) {
|
||||||
|
cell = fgTcell(x, y, mapWidth)
|
||||||
|
if (cell) {
|
||||||
|
drawTile(levelAtlas, cell - levelAtlas.offset, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLiquids(ctx, levelAtlas) {
|
||||||
|
var x, y, cell
|
||||||
|
for (y = 0; y < mapHeight; y++) {
|
||||||
|
for (x = 0; x < mapWidth; x++) {
|
||||||
|
cell = lqTcell(x, y, mapWidth)
|
||||||
|
if (cell) {
|
||||||
|
drawTile(levelAtlas, cell - levelAtlas.offset, x, y, 0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMap(ctx, levelAtlas) {
|
||||||
|
var x, y, cell
|
||||||
|
for (y = 0; y < mapHeight; y++) {
|
||||||
|
for (x = 0; x < mapWidth; x++) {
|
||||||
|
cell = tcell(x, y, mapWidth)
|
||||||
|
if (cell) {
|
||||||
|
drawTile(levelAtlas, cell - levelAtlas.offset, x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Sprite Animation
|
||||||
|
function drawSprite(entity, spriteAtlas, dt, frame) {
|
||||||
|
/* entity.start.x === 48 &&
|
||||||
|
entity.start.y === 656 &&
|
||||||
|
console.log('log frame ', entity.sprites.idle)
|
||||||
|
*/
|
||||||
|
|
||||||
|
let sprites = entity.sprites
|
||||||
|
|
||||||
|
let isJumping = entity.jumping || entity.falling
|
||||||
|
|
||||||
|
let isRunning =
|
||||||
|
(entity.right === true && !isJumping) ||
|
||||||
|
(entity.left === true && !isJumping)
|
||||||
|
|
||||||
|
let isShooting = entity.shooting || false
|
||||||
|
|
||||||
|
let sprite =
|
||||||
|
!!sprites.run && isRunning
|
||||||
|
? sprites.run
|
||||||
|
: !!sprites.jump && isJumping
|
||||||
|
? sprites.jump
|
||||||
|
: !!sprites.shoot && isShooting
|
||||||
|
? sprites.shoot
|
||||||
|
: sprites.idle
|
||||||
|
|
||||||
|
const x = entity.bullet ? entity.x : entity.x + entity.dx * dt
|
||||||
|
const y = entity.bullet
|
||||||
|
? entity.y - entity.height * 2
|
||||||
|
: entity.y + entity.dy * dt
|
||||||
|
const flipped = entity.flipped
|
||||||
|
const width = entity.bullet ? entity.width * 4 : entity.width
|
||||||
|
const height = entity.bullet ? entity.height * 4 : entity.height
|
||||||
|
|
||||||
|
let spriteTile = sprite.tiles[sprite.currentFrame]
|
||||||
|
|
||||||
|
/* entity.pickups &&
|
||||||
|
console.log(
|
||||||
|
'log drawSprite currentFrame ',
|
||||||
|
sprite.tiles[sprite.currentFrame]
|
||||||
|
) */
|
||||||
|
|
||||||
|
// make an image position using the
|
||||||
|
// current row and colum
|
||||||
|
spriteAtlas.sourceY =
|
||||||
|
Math.floor(spriteTile / spriteAtlas.atlasCol) * spriteAtlas.tileSize
|
||||||
|
spriteAtlas.sourceX =
|
||||||
|
(spriteTile % spriteAtlas.atlasCol) * spriteAtlas.tileSize
|
||||||
|
|
||||||
|
ctx.save()
|
||||||
|
// * Flip Sprite
|
||||||
|
ctx.scale(flipped ? -1 : 1, 1)
|
||||||
|
let dx = flipped ? -x - width : x
|
||||||
|
|
||||||
|
ctx.drawImage(
|
||||||
|
spriteAtlas.tileAtlas,
|
||||||
|
spriteAtlas.sourceX,
|
||||||
|
spriteAtlas.sourceY,
|
||||||
|
16, // * source Höhe
|
||||||
|
16, // * source Breite
|
||||||
|
dx,
|
||||||
|
y,
|
||||||
|
width, // * player Höhe
|
||||||
|
height // * player Breite
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.restore()
|
||||||
|
updateFrames(frame, sprite)
|
||||||
|
|
||||||
|
// entity.treasure && console.log('log drawSprite ', entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFrames(frame, sprite) {
|
||||||
|
/* entity.start.x === 48 &&
|
||||||
|
entity.start.y === 656 &&
|
||||||
|
console.log('log frame ', entity) */
|
||||||
|
|
||||||
|
if (frame % sprite.framebuffer === 0) {
|
||||||
|
if (sprite.currentFrame + 1 < sprite.tiles.length) sprite.currentFrame++
|
||||||
|
else if (sprite.loop) sprite.currentFrame = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// * Render Player
|
||||||
|
function renderPlayer(ctx, spriteAtlas, dt, frame) {
|
||||||
|
if (player.vul === false) {
|
||||||
|
ctx.globalAlpha = 0.25 + tweenTreasure(frame * 3, 60)
|
||||||
|
}
|
||||||
|
drawSprite(player, spriteAtlas, dt, frame)
|
||||||
|
ctx.globalAlpha = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderHud(ctx, spriteAtlas, dt, frame) {
|
||||||
|
var n, max
|
||||||
|
const hudScaling = scalingFactor / 2
|
||||||
|
|
||||||
|
const hitpoints = globalObject.hitpoints
|
||||||
|
const killed = globalObject.killed
|
||||||
|
const collected = globalObject.collected
|
||||||
|
|
||||||
|
ctx.fillStyle = COLOR.GOLD
|
||||||
|
for (n = 0, max = collected; n < max; n++)
|
||||||
|
ctx.fillRect(
|
||||||
|
t2p(1 + n) * hudScaling,
|
||||||
|
t2p(1) * hudScaling,
|
||||||
|
(TILE / 2) * hudScaling,
|
||||||
|
(TILE / 2) * hudScaling
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.fillStyle = COLOR.SLATE
|
||||||
|
for (n = 0, max = killed; n < max; n++)
|
||||||
|
ctx.fillRect(
|
||||||
|
t2p(1 + n) * hudScaling,
|
||||||
|
t2p(2) * hudScaling,
|
||||||
|
(TILE / 2) * hudScaling,
|
||||||
|
(TILE / 2) * hudScaling
|
||||||
|
)
|
||||||
|
|
||||||
|
// ! provisorische Hitpoint-Anzeige
|
||||||
|
ctx.fillStyle = 'red'
|
||||||
|
if (hitpoints === 1) {
|
||||||
|
ctx.globalAlpha = 0.25 + tweenTreasure(frame, 60)
|
||||||
|
}
|
||||||
|
for (n = 0, max = hitpoints; n < max; n++)
|
||||||
|
ctx.fillRect(
|
||||||
|
t2p(1 + n) * hudScaling,
|
||||||
|
t2p(3) * hudScaling,
|
||||||
|
(TILE / 2) * hudScaling,
|
||||||
|
(TILE / 2) * hudScaling
|
||||||
|
)
|
||||||
|
// ! Der Atlas funktioniert so nicht weil die Tiles nicht quadratisch sind.
|
||||||
|
// drawSprite(t, spriteAtlas, dt, frame)
|
||||||
|
ctx.globalAlpha = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function fadeScreen(frame, dt) {
|
||||||
|
ctx.fillStyle = 'black'
|
||||||
|
ctx.globalAlpha = 0
|
||||||
|
let duration = 100
|
||||||
|
|
||||||
|
pulse = frame % duration
|
||||||
|
|
||||||
|
if (pulse < duration) {
|
||||||
|
opacity = pulse / duration
|
||||||
|
} else {
|
||||||
|
opacity = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillRect(0, 0, width, height)
|
||||||
|
ctx.globalAlpha = opacity
|
||||||
|
|
||||||
|
//setInterval(show(), 800)
|
||||||
|
// console.log('log opacity ', opacity)
|
||||||
|
// console.log('log fade frame ', frame)
|
||||||
|
// console.log('log fade pulse ', pulse)
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Render Pause Function
|
||||||
|
function renderPause() {
|
||||||
|
if (paused) {
|
||||||
|
ctx.save()
|
||||||
|
ctx.scale(scalingFactor / 2, scalingFactor / 2)
|
||||||
|
|
||||||
|
ctx.font = '12px C64 TrueType'
|
||||||
|
ctx.fillStyle = 'white'
|
||||||
|
ctx.textAlign = 'center'
|
||||||
|
ctx.fillText('- pause -', scaledCanvas.width, scaledCanvas.height)
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCameraBox() {
|
||||||
|
ctx.fillStyle = 'rgba(0, 0, 255, 0.2)'
|
||||||
|
ctx.fillRect(cameraBox.x, cameraBox.y, cameraBox.width, cameraBox.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMonsters(ctx, spriteAtlas, dt, frame) {
|
||||||
|
ctx.fillStyle = COLOR.SLATE
|
||||||
|
var n, max, monster
|
||||||
|
for (n = 0, max = monsters.length; n < max; n++) {
|
||||||
|
monster = monsters[n]
|
||||||
|
|
||||||
|
if (monster.sprites === undefined && !monster.dead)
|
||||||
|
ctx.fillRect(
|
||||||
|
monster.x + monster.dx * dt,
|
||||||
|
monster.y + monster.dy * dt,
|
||||||
|
TILE,
|
||||||
|
TILE
|
||||||
|
)
|
||||||
|
if (monster.sprites !== undefined && !monster.dead) {
|
||||||
|
drawSprite(monster, spriteAtlas, dt, frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPickups(ctx, spriteAtlas, dt, frame) {
|
||||||
|
//ctx.globalAlpha = 0.25 + tweenTreasure(frame, 60)
|
||||||
|
var n, max, p
|
||||||
|
for (n = 0, max = pickups.length; n < max; n++) {
|
||||||
|
p = pickups[n]
|
||||||
|
|
||||||
|
//console.log('log p.sprites.currentsprite ', p.sprites.currentFrame)
|
||||||
|
|
||||||
|
if (p.sprites !== undefined && !p.collected) {
|
||||||
|
drawSprite(p, spriteAtlas, dt, frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//ctx.globalAlpha = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTreasure(ctx, spriteAtlas, dt, frame) {
|
||||||
|
ctx.fillStyle = COLOR.GOLD
|
||||||
|
ctx.globalAlpha = 0.25 + tweenTreasure(frame, 60)
|
||||||
|
var n, max, t
|
||||||
|
for (n = 0, max = treasure.length; n < max; n++) {
|
||||||
|
t = treasure[n]
|
||||||
|
//if (!t.collected) ctx.fillRect(t.x, t.y + TILE / 3, TILE, (TILE * 2) / 3)
|
||||||
|
|
||||||
|
if (t.sprites !== undefined && !t.collected) {
|
||||||
|
// console.log('log treasure entity ', t)
|
||||||
|
drawSprite(t, spriteAtlas, dt, frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.globalAlpha = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function tweenTreasure(frame, duration) {
|
||||||
|
const half = duration / 2
|
||||||
|
pulse = frame % duration
|
||||||
|
return pulse < half ? pulse / half : 1 - (pulse - half) / half
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBullets(ctx, spriteAtlas, dt, frame) {
|
||||||
|
ctx.fillStyle = COLOR.GOLD
|
||||||
|
|
||||||
|
var n, max, bullet
|
||||||
|
|
||||||
|
for (n = 0, max = bullets.length; n < max; n++) {
|
||||||
|
bullet = bullets[n]
|
||||||
|
|
||||||
|
switch (bullet.ammoType) {
|
||||||
|
case 'simpleGun':
|
||||||
|
bullet.sprites = simpleBullet.sprites
|
||||||
|
break
|
||||||
|
case 'advancedGun':
|
||||||
|
bullet.sprites = simpleBullet.sprites
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
//console.log('log bullet ', bullet.sprites)
|
||||||
|
//console.log('log bullet spriteAtlas ', spriteAtlas)
|
||||||
|
|
||||||
|
if (bullet !== undefined) {
|
||||||
|
if (bullet.sprites === undefined) {
|
||||||
|
ctx.fillRect(bullet.x, bullet.y, ammoType.width, ammoType.height)
|
||||||
|
}
|
||||||
|
if (bullet.sprites !== undefined) {
|
||||||
|
console.log('log bullet entity ', bullet)
|
||||||
|
drawSprite(bullet, spriteAtlas, dt, frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// 5. LOAD THE MAP
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ! Wenn ich die Setup-Funktion zum Levelwechsel aufrufe, dann werden die Variablen nicht upgedated!
|
||||||
|
|
||||||
|
// * Die setup Funktion finde ich gut, weil sie alle Infos aus einer map mit mehreren Layern erhalten kann.
|
||||||
|
function setup(map) {
|
||||||
|
var background = map.layers[0]?.data,
|
||||||
|
data = map.layers[1].data,
|
||||||
|
objects = map.layers[2].objects,
|
||||||
|
foreground = map.layers[3]?.data,
|
||||||
|
liquidTiles = map.layers[4]?.data || null,
|
||||||
|
n,
|
||||||
|
obj,
|
||||||
|
entity
|
||||||
|
|
||||||
|
for (n = 0; n < objects.length; n++) {
|
||||||
|
obj = objects[n]
|
||||||
|
|
||||||
|
const entityType = obj.type || obj.class
|
||||||
|
const entityName = obj.name
|
||||||
|
const entityClass = obj.class
|
||||||
|
|
||||||
|
const entityProperties = Array.isArray(obj.properties)
|
||||||
|
? Object.fromEntries(
|
||||||
|
!!obj.properties &&
|
||||||
|
obj.properties?.map((obj) => [obj.name, obj.value])
|
||||||
|
)
|
||||||
|
: !!obj.properties
|
||||||
|
? obj.properties
|
||||||
|
: {}
|
||||||
|
|
||||||
|
// * Hier werden alle actors/entities in die entsprechenden Ojekte gepushed.
|
||||||
|
entity = setupEntity(obj, entityType, entityProperties, entityName)
|
||||||
|
|
||||||
|
switch (entityType) {
|
||||||
|
case 'player':
|
||||||
|
player = entity
|
||||||
|
break
|
||||||
|
case 'monster':
|
||||||
|
monsters.push(entity)
|
||||||
|
break
|
||||||
|
case 'slimeMonster':
|
||||||
|
monsters.push(entity)
|
||||||
|
break
|
||||||
|
case 'treasure':
|
||||||
|
treasure.push(entity)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'ammo':
|
||||||
|
pickups.push(entity)
|
||||||
|
break
|
||||||
|
case 'health':
|
||||||
|
pickups.push(entity)
|
||||||
|
break
|
||||||
|
case 'extralife':
|
||||||
|
pickups.push(entity)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'water':
|
||||||
|
liquids.push(entity)
|
||||||
|
break
|
||||||
|
case 'door':
|
||||||
|
doors.push(entity)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cells = data
|
||||||
|
bgCells = background
|
||||||
|
fgCells = foreground
|
||||||
|
lqCells = liquidTiles
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Diese Funktion entspricht grob meiner actor init Funktion.
|
||||||
|
// ? Muss ich noch eine Sprite Animation Funktion erstellen, die ihre Daten aus dem Entity Object für alle Entities (player, monster, etc.) erhält?
|
||||||
|
function setupEntity(obj, entityType, entityProperties, entityName) {
|
||||||
|
let entityTemplate =
|
||||||
|
entityName == 'monster'
|
||||||
|
? bounderMonster
|
||||||
|
: entityName == 'player'
|
||||||
|
? playerObject
|
||||||
|
: entityName == 'slimeMonster'
|
||||||
|
? slimeMonster
|
||||||
|
: entityName == 'treasure'
|
||||||
|
? coinTreasure
|
||||||
|
: entityName == 'ammo'
|
||||||
|
? ammo
|
||||||
|
: entityName == 'health'
|
||||||
|
? health
|
||||||
|
: entityName == 'extralife'
|
||||||
|
? extralife
|
||||||
|
: entityName == 'door'
|
||||||
|
? null
|
||||||
|
: null
|
||||||
|
|
||||||
|
let entitySprites = entityTemplate?.sprites
|
||||||
|
|
||||||
|
const entityMaxHitpoints =
|
||||||
|
entityType == 'player' ? playerObject.maxHitpoints : 1
|
||||||
|
|
||||||
|
const entityCurrentHitpoints =
|
||||||
|
entityType == 'player'
|
||||||
|
? playerObject.currentHitpoints
|
||||||
|
: !!entityTemplate?.currentHitpoints
|
||||||
|
? entityTemplate?.currentHitpoints
|
||||||
|
: 1
|
||||||
|
|
||||||
|
console.log('log entity ', entity)
|
||||||
|
|
||||||
|
var entity = {}
|
||||||
|
entity.x = obj.x
|
||||||
|
entity.y = obj.y
|
||||||
|
entity.dx = 0
|
||||||
|
entity.dy = 0
|
||||||
|
entity.gravity = METER * (entityProperties.gravity || GRAVITY)
|
||||||
|
entity.maxdx = METER * (entityProperties.maxdx || MAXDX)
|
||||||
|
entity.maxdy = METER * (entityProperties.maxdy || MAXDY)
|
||||||
|
entity.impulse = METER * (entityProperties.impulse || IMPULSE)
|
||||||
|
entity.accel = entity.maxdx / (entityProperties.accel || ACCEL)
|
||||||
|
entity.friction = entity.maxdx / (entityProperties.friction || FRICTION)
|
||||||
|
entity.monster = entityType == 'monster'
|
||||||
|
entity.slimeMonster = entityType == 'slimeMonster'
|
||||||
|
entity.player = entityType == 'player'
|
||||||
|
entity.treasure = entityType == 'treasure'
|
||||||
|
entity.ammo = entityType == 'ammo'
|
||||||
|
entity.health = entityType == 'health'
|
||||||
|
entity.extralife = entityType == 'extralife'
|
||||||
|
entity.water = entityType == 'water'
|
||||||
|
entity.door = entityType == 'door'
|
||||||
|
entity.leadsTo = entityProperties.leadsTo
|
||||||
|
entity.left = entityProperties.left
|
||||||
|
entity.right = entityProperties.right
|
||||||
|
entity.up = entityProperties.up || false
|
||||||
|
entity.down = entityProperties.down || false
|
||||||
|
entity.start = { x: obj.x, y: obj.y }
|
||||||
|
entity.killed = 0
|
||||||
|
entity.collected = false
|
||||||
|
entity.sprites = entitySprites
|
||||||
|
entity.flipped = false
|
||||||
|
entity.width = obj.width
|
||||||
|
entity.height = obj.height
|
||||||
|
entity.maxHitpoints = entityMaxHitpoints
|
||||||
|
entity.currentHitpoints = entityCurrentHitpoints
|
||||||
|
entity.hurt = false
|
||||||
|
entity.vul = true
|
||||||
|
|
||||||
|
return entity
|
||||||
|
}
|
||||||
@@ -0,0 +1,411 @@
|
|||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// 3. UPDATE LOOP
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function update(dt) {
|
||||||
|
updatePlayer(dt)
|
||||||
|
updateBullets(dt)
|
||||||
|
updateMonsters(dt)
|
||||||
|
checkTreasure()
|
||||||
|
checkPickups()
|
||||||
|
checkLiquids()
|
||||||
|
// updateDoors(dt)
|
||||||
|
checkDoors()
|
||||||
|
|
||||||
|
// ! Vielleicht eigene update Funktion für Musik schreiben
|
||||||
|
// theme.level1.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePlayer(dt) {
|
||||||
|
updateEntity(player, dt)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMonsters(dt) {
|
||||||
|
var n, max
|
||||||
|
for (n = 0, max = monsters.length; n < max; n++)
|
||||||
|
updateMonster(monsters[n], dt)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMonster(monster, dt) {
|
||||||
|
monster.slimeMonster && console.log('log monster ', monster)
|
||||||
|
if (!monster.dead) {
|
||||||
|
updateEntity(monster, dt)
|
||||||
|
if (
|
||||||
|
overlap(player.x, player.y, TILE, TILE, monster.x, monster.y, TILE, TILE)
|
||||||
|
) {
|
||||||
|
if (player.dy > 0 && monster.y - player.y > TILE / 2) killMonster(monster)
|
||||||
|
else if (player.vul) {
|
||||||
|
reduceHitpoints(player, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shoot(player) {
|
||||||
|
//globalObject.ammo > 0 ? sfx.shoot.play() : sfx.click.play()
|
||||||
|
if (player.shooting) return
|
||||||
|
player.shooting = true
|
||||||
|
const bulletX = player.x
|
||||||
|
const bulletY = player.y
|
||||||
|
const direction = player.flipped
|
||||||
|
const weaponType = player?.currentWeapon || 'simpleGun'
|
||||||
|
|
||||||
|
if (globalObject.ammo > 0) {
|
||||||
|
sfx.shoot.play()
|
||||||
|
globalObject.ammo--
|
||||||
|
bullets.push({
|
||||||
|
bullet: true,
|
||||||
|
width: 4,
|
||||||
|
height: 4,
|
||||||
|
flipped: player.flipped,
|
||||||
|
originX: bulletX,
|
||||||
|
originY: bulletY,
|
||||||
|
x: bulletX,
|
||||||
|
y: bulletY + player.height / 2,
|
||||||
|
directionLeft: direction,
|
||||||
|
ammoType: weaponType,
|
||||||
|
damage: 1,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
sfx.click.play()
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
player.shooting = false
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBullets(dt) {
|
||||||
|
var n, max
|
||||||
|
for (n = 0, max = bullets.length; n < max; n++) updateBullet(bullets[n], dt)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBullet(bullet, dt) {
|
||||||
|
if (!!bullet) {
|
||||||
|
if (bullet.directionLeft) {
|
||||||
|
bullet.x = bullet.x - ammoType.velocity
|
||||||
|
} else {
|
||||||
|
bullet.x = bullet.x + ammoType.velocity
|
||||||
|
}
|
||||||
|
var n, max, monster, thisBullet
|
||||||
|
for (n = 0, max = monsters.length; n < max; n++) {
|
||||||
|
monster = monsters[n]
|
||||||
|
thisBullet = bullet
|
||||||
|
if (!monster.dead) {
|
||||||
|
if (
|
||||||
|
overlap(
|
||||||
|
thisBullet.x,
|
||||||
|
thisBullet.y,
|
||||||
|
ammoType.width,
|
||||||
|
ammoType.height,
|
||||||
|
monster.x,
|
||||||
|
monster.y,
|
||||||
|
TILE,
|
||||||
|
TILE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
reduceHitpoints(monster, bullet.damage),
|
||||||
|
sfx.explode.play(),
|
||||||
|
destroyBullet(bullet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(!bullet.directionLeft &&
|
||||||
|
bullet.x > bullet.originX + ammoType.distance) ||
|
||||||
|
(bullet.directionLeft && bullet.x < bullet.originX - ammoType.distance)
|
||||||
|
) {
|
||||||
|
destroyBullet(bullet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function destroyBullet(bullet) {
|
||||||
|
const index = bullets.indexOf(bullet)
|
||||||
|
if (index > -1) {
|
||||||
|
bullets.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkTreasure() {
|
||||||
|
var n, max, t
|
||||||
|
for (n = 0, max = treasure.length; n < max; n++) {
|
||||||
|
t = treasure[n]
|
||||||
|
if (
|
||||||
|
!t.collected &&
|
||||||
|
overlap(player.x, player.y, TILE, TILE, t.x, t.y, TILE, TILE)
|
||||||
|
)
|
||||||
|
collectTreasure(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPickups() {
|
||||||
|
var n, max, p
|
||||||
|
for (n = 0, max = pickups.length; n < max; n++) {
|
||||||
|
p = pickups[n]
|
||||||
|
if (
|
||||||
|
!p.collected &&
|
||||||
|
overlap(player.x, player.y, TILE, TILE, p.x, p.y, TILE, TILE)
|
||||||
|
)
|
||||||
|
collectPickup(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectPickup(p) {
|
||||||
|
// console.log('log pickup ', p)
|
||||||
|
|
||||||
|
if (p.extralife) {
|
||||||
|
addExtraLife(p)
|
||||||
|
}
|
||||||
|
if (p.ammo) {
|
||||||
|
addAmmo(p)
|
||||||
|
}
|
||||||
|
if (p.health) {
|
||||||
|
addHitPoints(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addExtraLife(p) {
|
||||||
|
globalObject.lifes++
|
||||||
|
p.collected = true
|
||||||
|
sfx.smb_powerup.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHitPoints(p) {
|
||||||
|
if (globalObject.hitpoints < globalObject.maxHitpoints) {
|
||||||
|
globalObject.hitpoints++
|
||||||
|
p.collected = true
|
||||||
|
sfx.stomp.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAmmo(p) {
|
||||||
|
if (globalObject.ammo < globalObject.maxAmmo) {
|
||||||
|
globalObject.ammo++
|
||||||
|
p.collected = true
|
||||||
|
sfx.kick.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkLiquids() {
|
||||||
|
var n, max, l
|
||||||
|
for (n = 0, max = liquids.length; n < max; n++) {
|
||||||
|
l = liquids[n]
|
||||||
|
if (overlap(player.x, player.y, TILE, TILE, l.x, l.y, l.width, l.height)) {
|
||||||
|
!player.swimming && sfx.splash.play()
|
||||||
|
player.dy = player.dy / 1.15
|
||||||
|
if (!player.swimming) player.jumping = false
|
||||||
|
player.swimming = true
|
||||||
|
} else {
|
||||||
|
player.swimming = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDoors() {
|
||||||
|
// ! Hier habe ich rudimentär einen Levelwechsel eingebaut.
|
||||||
|
// ! Was noch fehlt ist, dass ein globales Player-Objekt beibehalten wird.
|
||||||
|
var n, max, t
|
||||||
|
for (n = 0, max = doors.length; n < max; n++) {
|
||||||
|
t = doors[n]
|
||||||
|
if (
|
||||||
|
overlap(
|
||||||
|
player.x,
|
||||||
|
player.y,
|
||||||
|
TILE * 2,
|
||||||
|
TILE * 2,
|
||||||
|
t.x,
|
||||||
|
t.y,
|
||||||
|
TILE * 2,
|
||||||
|
TILE * 2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
player.interact && useDoor(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useDoor(t) {
|
||||||
|
let leadsTo
|
||||||
|
switch (t.leadsTo) {
|
||||||
|
case 'level1':
|
||||||
|
leadsTo = levelObject.level1
|
||||||
|
break
|
||||||
|
case 'level2':
|
||||||
|
leadsTo = levelObject.level2
|
||||||
|
break
|
||||||
|
case 'level3':
|
||||||
|
leadsTo = levelObject.level3
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sfx.openDoor.play()
|
||||||
|
levelTransition(leadsTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
function levelTransition(level) {
|
||||||
|
setTimeout(() => {
|
||||||
|
_initLevel(level)
|
||||||
|
}, 800)
|
||||||
|
}
|
||||||
|
|
||||||
|
function killMonster(monster) {
|
||||||
|
globalObject.killed++
|
||||||
|
monster.dead = true
|
||||||
|
sfx.killMonster.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Take Damage Function
|
||||||
|
function reduceHitpoints(entity, damage) {
|
||||||
|
if (
|
||||||
|
entity.player ? globalObject.hitpoints > 1 : entity.currentHitpoints > 1
|
||||||
|
) {
|
||||||
|
entity.player
|
||||||
|
? (globalObject.hitpoints -= damage)
|
||||||
|
: (entity.currentHitpoints -= damage)
|
||||||
|
entity.vul = false
|
||||||
|
entity.hurt = true
|
||||||
|
sfx.takeDamage.play()
|
||||||
|
setTimeout(() => {
|
||||||
|
entity.vul = true
|
||||||
|
entity.hurt = false
|
||||||
|
}, 1500)
|
||||||
|
} else killEntity(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
function killEntity(entity) {
|
||||||
|
console.log('killEntity: ', entity)
|
||||||
|
if (entity.player === true) {
|
||||||
|
killPlayer(entity)
|
||||||
|
}
|
||||||
|
if (entity.monster === true) {
|
||||||
|
killMonster(entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function killPlayer(player) {
|
||||||
|
sfx.die.play()
|
||||||
|
player.x = player.start.x
|
||||||
|
player.y = player.start.y
|
||||||
|
player.dx = player.dy = 0
|
||||||
|
globalObject.hitpoints = player.maxHitpoints
|
||||||
|
globalObject.lifes--
|
||||||
|
console.log('log globalObject: ', globalObject.lifes)
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectTreasure(t) {
|
||||||
|
globalObject.collected++
|
||||||
|
t.collected = true
|
||||||
|
sfx.pickup.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateEntity(entity, dt) {
|
||||||
|
var wasleft = entity.dx < 0,
|
||||||
|
wasright = entity.dx > 0,
|
||||||
|
falling = entity.falling,
|
||||||
|
swimming = entity.swimming || false,
|
||||||
|
friction =
|
||||||
|
entity.friction * (falling && !swimming ? 0.5 : swimming ? 0.1 : 1),
|
||||||
|
impulse = entity.impulse,
|
||||||
|
swimImpulse = entity.impulse / 3,
|
||||||
|
accel = entity.accel * (falling && !swimming ? 0.5 : swimming ? 0.1 : 1),
|
||||||
|
gravity = swimming ? METER * 8 : METER * GRAVITY
|
||||||
|
|
||||||
|
entity.ddx = 0
|
||||||
|
entity.ddy = gravity // ! Hier wir gravity angewandt
|
||||||
|
|
||||||
|
// * Hier wird die Entity bewegt.
|
||||||
|
if (entity.left) (entity.ddx = entity.ddx - accel), (entity.flipped = true)
|
||||||
|
else if (wasleft) entity.ddx = entity.ddx + friction
|
||||||
|
|
||||||
|
if (entity.right) (entity.ddx = entity.ddx + accel), (entity.flipped = false)
|
||||||
|
else if (wasright) entity.ddx = entity.ddx - friction
|
||||||
|
|
||||||
|
if (entity.jump) {
|
||||||
|
if (!entity.jumping && !falling && !swimming) {
|
||||||
|
jump(entity)
|
||||||
|
} else if (!entity.jumping && swimming) {
|
||||||
|
swim(entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function jump(entity) {
|
||||||
|
sfx.jump.play()
|
||||||
|
entity.ddy = entity.ddy - impulse // an instant big force impulse
|
||||||
|
entity.jumping = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function swim(entity) {
|
||||||
|
sfx.swim.play()
|
||||||
|
entity.ddy = entity.down
|
||||||
|
? entity.ddy + swimImpulse
|
||||||
|
: entity.up
|
||||||
|
? entity.ddy - swimImpulse * 2
|
||||||
|
: entity.ddy - swimImpulse
|
||||||
|
entity.jumping = true
|
||||||
|
setTimeout(() => {
|
||||||
|
entity.jumping = false
|
||||||
|
}, 400)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ! "dt" ist hier identisch mit "step", was in der index.js in die update() gegeben wird. Konstant bei ca. 0.01666
|
||||||
|
//console.log('log entity.x + dt * entity.dx: ', player.x + dt * player.dx)
|
||||||
|
|
||||||
|
entity.x = entity.x + dt * entity.dx
|
||||||
|
entity.y = entity.y + dt * entity.dy
|
||||||
|
entity.dx = bound(entity.dx + dt * entity.ddx, -entity.maxdx, entity.maxdx)
|
||||||
|
entity.dy = bound(entity.dy + dt * entity.ddy, -entity.maxdy, entity.maxdy)
|
||||||
|
|
||||||
|
if ((wasleft && entity.dx > 0) || (wasright && entity.dx < 0)) {
|
||||||
|
entity.dx = 0 // clamp at zero to prevent friction from making us jiggle side to side
|
||||||
|
}
|
||||||
|
|
||||||
|
var tx = p2t(entity.x),
|
||||||
|
ty = p2t(entity.y),
|
||||||
|
nx = entity.x % TILE,
|
||||||
|
ny = entity.y % TILE,
|
||||||
|
cell = tcell(tx, ty, mapWidth), //! Was ist das hier?
|
||||||
|
cellright = tcell(tx + 1, ty, mapWidth),
|
||||||
|
celldown = tcell(tx, ty + 1, mapWidth),
|
||||||
|
celldiag = tcell(tx + 1, ty + 1, mapWidth)
|
||||||
|
|
||||||
|
// ? Ist das hier der Collision Check?
|
||||||
|
|
||||||
|
if (entity.dy > 0) {
|
||||||
|
if ((celldown && !cell) || (celldiag && !cellright && nx)) {
|
||||||
|
entity.y = t2p(ty)
|
||||||
|
entity.dy = 0
|
||||||
|
entity.falling = false
|
||||||
|
entity.jumping = false
|
||||||
|
ny = 0
|
||||||
|
}
|
||||||
|
} else if (entity.dy < 0) {
|
||||||
|
if ((cell && !celldown) || (cellright && !celldiag && nx)) {
|
||||||
|
entity.y = t2p(ty + 1)
|
||||||
|
entity.dy = 0
|
||||||
|
cell = celldown
|
||||||
|
cellright = celldiag
|
||||||
|
ny = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity.dx > 0) {
|
||||||
|
if ((cellright && !cell) || (celldiag && !celldown && ny)) {
|
||||||
|
entity.x = t2p(tx)
|
||||||
|
entity.dx = 0
|
||||||
|
}
|
||||||
|
} else if (entity.dx < 0) {
|
||||||
|
if ((cell && !cellright) || (celldown && !celldiag && ny)) {
|
||||||
|
entity.x = t2p(tx + 1)
|
||||||
|
entity.dx = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity.monster || entity.slimeMonster) {
|
||||||
|
if (entity.left && (cell || !celldown)) {
|
||||||
|
entity.left = false
|
||||||
|
entity.right = true
|
||||||
|
} else if (entity.right && (cellright || !celldiag)) {
|
||||||
|
entity.right = false
|
||||||
|
entity.left = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.falling = !(celldown || (nx && celldiag))
|
||||||
|
}
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// 1. UTILITIES
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function timestamp() {
|
||||||
|
return window.performance && window.performance.now
|
||||||
|
? window.performance.now()
|
||||||
|
: new Date().getTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
function bound(x, min, max) {
|
||||||
|
return Math.max(min, Math.min(max, x))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function get(url, onsuccess) {
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.onreadystatechange = function () {
|
||||||
|
if (request.readyState == 4 && request.status == 200) onsuccess(request);
|
||||||
|
};
|
||||||
|
request.open("GET", url, true);
|
||||||
|
request.send();
|
||||||
|
} */
|
||||||
|
|
||||||
|
function overlap(x1, y1, w1, h1, x2, y2, w2, h2) {
|
||||||
|
return !(
|
||||||
|
x1 + w1 - 1 < x2 ||
|
||||||
|
x2 + w2 - 1 < x1 ||
|
||||||
|
y1 + h1 - 1 < y2 ||
|
||||||
|
y2 + h2 - 1 < y1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// * SFX function
|
||||||
|
function sound(src, volume) {
|
||||||
|
this.sound = document.createElement('audio')
|
||||||
|
this.sound.src = src
|
||||||
|
this.sound.setAttribute('preload', 'auto')
|
||||||
|
this.sound.setAttribute('controls', 'none')
|
||||||
|
this.sound.style.display = 'none'
|
||||||
|
document.body.appendChild(this.sound)
|
||||||
|
this.sound.volume = volume
|
||||||
|
this.play = function () {
|
||||||
|
this.sound.play()
|
||||||
|
}
|
||||||
|
this.stop = function () {
|
||||||
|
this.sound.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Music function
|
||||||
|
function music(src, volume) {
|
||||||
|
this.music = document.createElement('audio')
|
||||||
|
this.music.src = src
|
||||||
|
this.music.setAttribute('preload', 'auto')
|
||||||
|
this.music.setAttribute('controls', 'none')
|
||||||
|
this.music.setAttribute('loop', 'loop')
|
||||||
|
this.music.style.display = 'none'
|
||||||
|
document.body.appendChild(this.music)
|
||||||
|
this.music.volume = volume
|
||||||
|
this.play = function () {
|
||||||
|
this.music.play()
|
||||||
|
}
|
||||||
|
this.stop = function () {
|
||||||
|
this.music.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Pause function
|
||||||
|
function togglePause() {
|
||||||
|
console.log('log togglePause', paused)
|
||||||
|
if (!paused) {
|
||||||
|
paused = true
|
||||||
|
} else if (paused) {
|
||||||
|
paused = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleDevInfos() {
|
||||||
|
if (!showDevInfo) {
|
||||||
|
showDevInfo = true
|
||||||
|
} else if (showDevInfo) {
|
||||||
|
showDevInfo = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderHudSprites() {
|
||||||
|
const hudObject = {
|
||||||
|
player_hitpoints: player.currentHitpoints,
|
||||||
|
player_collected: player.collected,
|
||||||
|
player_lifes: globalObject.lifes,
|
||||||
|
player_ammo: globalObject.ammo,
|
||||||
|
}
|
||||||
|
|
||||||
|
function hitpoints() {
|
||||||
|
ctx.save()
|
||||||
|
ctx.scale(scalingFactor / 4, scalingFactor / 4)
|
||||||
|
ctx.textAlign = 'left'
|
||||||
|
ctx.font = '12px C64 TrueType'
|
||||||
|
|
||||||
|
ctx.fillStyle = 'white'
|
||||||
|
ctx.fillText(`lifes: ${hudObject.player_lifes}`, 50, 50)
|
||||||
|
ctx.fillText(`ammo: ${hudObject.player_ammo}`, 200, 50)
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
hitpoints()
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Renderfunktion für DevInfos
|
||||||
|
function renderDevInfos() {
|
||||||
|
ctx.save()
|
||||||
|
ctx.scale(scalingFactor / 6, scalingFactor / 6)
|
||||||
|
if (showDevInfo) {
|
||||||
|
const backgroundColor = 'rgba(18, 64, 90, 0.15)'
|
||||||
|
const color = 'white'
|
||||||
|
const lineHeight = 18
|
||||||
|
|
||||||
|
const devObject = {
|
||||||
|
player_accel: player.accel,
|
||||||
|
player_ddx: player.ddx,
|
||||||
|
player_ddy: player.ddy,
|
||||||
|
player_dx: player.dx,
|
||||||
|
player_dy: player.dy,
|
||||||
|
player_interact: player.interact,
|
||||||
|
player_falling: player.falling,
|
||||||
|
player_friction: player.friction,
|
||||||
|
player_gravity: player.gravity,
|
||||||
|
player_impulse: player.impulse,
|
||||||
|
player_jumping: player.jumping,
|
||||||
|
player_swimming: player.swimming,
|
||||||
|
player_shooting: player.shooting,
|
||||||
|
player_left: player.left,
|
||||||
|
player_maxdx: player.maxdx,
|
||||||
|
player_maxdy: player.maxdy,
|
||||||
|
player_right: player.right,
|
||||||
|
player_x: player.x,
|
||||||
|
player_y: player.y,
|
||||||
|
player_vul: player.vul,
|
||||||
|
player_hurt: player.hurt,
|
||||||
|
player_flipped: player.flipped,
|
||||||
|
canvas_width: canvas.height,
|
||||||
|
cameraWithBox_x: cameraWithBox.position.x,
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvas_width = canvas.width
|
||||||
|
const canvas_height = canvas.height
|
||||||
|
|
||||||
|
function text() {
|
||||||
|
ctx.textAlign = 'left'
|
||||||
|
ctx.font = '12px C64 TrueType'
|
||||||
|
for (const [index, [key, value]] of Object.entries(
|
||||||
|
Object.entries(devObject)
|
||||||
|
)) {
|
||||||
|
const newIndex = Number(index) + 1
|
||||||
|
const newValue = typeof value === 'number' ? value.toFixed(2) : value
|
||||||
|
ctx.fillStyle = backgroundColor
|
||||||
|
ctx.fillRect(15, lineHeight * newIndex - 15, 500, lineHeight)
|
||||||
|
ctx.fillStyle = color
|
||||||
|
ctx.fillText(`${key}: ${newValue}`, 15, lineHeight * newIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text()
|
||||||
|
}
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
@@ -0,0 +1,363 @@
|
|||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// 2. GAME CONSTANTS AND VARIABLES
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ! Globales Test-Objekt
|
||||||
|
let globalObject = {
|
||||||
|
lifes: 3,
|
||||||
|
hitpoints: 3,
|
||||||
|
maxHitpoints: 5,
|
||||||
|
collected: null,
|
||||||
|
killed: null,
|
||||||
|
ammo: 10,
|
||||||
|
maxAmmo: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Import tiles and level map //
|
||||||
|
|
||||||
|
const tileAtlas1 = new Image()
|
||||||
|
tileAtlas1.src = './img/tiles.png'
|
||||||
|
|
||||||
|
const tileAtlas2 = new Image()
|
||||||
|
tileAtlas2.src = './img/Grotto-escape-2-files/PNG/environment-tiles.png'
|
||||||
|
|
||||||
|
const tileAtlasBg = new Image()
|
||||||
|
tileAtlasBg.src = './img/Grotto-escape-2-files/PNG/environment-background.png'
|
||||||
|
|
||||||
|
let tileSize = 16
|
||||||
|
let tileOutputSize = 2 // can set to 1 for 32px or higher
|
||||||
|
|
||||||
|
const level1Atlas = {
|
||||||
|
tileAtlas: tileAtlas1,
|
||||||
|
offset: 1,
|
||||||
|
tileSize: 16,
|
||||||
|
tileOutputSize: 2,
|
||||||
|
updatedTileSize: tileSize * tileOutputSize,
|
||||||
|
atlasCol: 8,
|
||||||
|
atlasRow: 5,
|
||||||
|
mapIndex: 0,
|
||||||
|
sourceX: 0,
|
||||||
|
sourceY: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
const level2Atlas = {
|
||||||
|
tileAtlas: tileAtlas2,
|
||||||
|
offset: 1,
|
||||||
|
tileSize: 16,
|
||||||
|
tileOutputSize: 2,
|
||||||
|
updatedTileSize: tileSize * tileOutputSize,
|
||||||
|
atlasCol: 42,
|
||||||
|
atlasRow: 16,
|
||||||
|
mapIndex: 0,
|
||||||
|
sourceX: 0,
|
||||||
|
sourceY: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
const backgroundAtlas = {
|
||||||
|
tileAtlas: tileAtlasBg,
|
||||||
|
offset: 673,
|
||||||
|
tileSize: 16,
|
||||||
|
tileOutputSize: 2,
|
||||||
|
updatedTileSize: tileSize * tileOutputSize,
|
||||||
|
atlasCol: 28,
|
||||||
|
atlasRow: 10,
|
||||||
|
mapIndex: 0,
|
||||||
|
sourceX: 0,
|
||||||
|
sourceY: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// * end of Import tiles and level map //
|
||||||
|
|
||||||
|
// * Test Sprite Imports
|
||||||
|
|
||||||
|
const playerSpriteAtlas = new Image()
|
||||||
|
playerSpriteAtlas.src = './img/player.png'
|
||||||
|
|
||||||
|
const playerAtlas = {
|
||||||
|
tileAtlas: playerSpriteAtlas,
|
||||||
|
tileSize: 16,
|
||||||
|
tileOutputSize: 2,
|
||||||
|
updatedTileSize: tileSize * tileOutputSize,
|
||||||
|
atlasCol: 4,
|
||||||
|
atlasRow: 2,
|
||||||
|
mapIndex: 0,
|
||||||
|
sourceX: 0,
|
||||||
|
sourceY: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
const enemySpriteAtlas = new Image()
|
||||||
|
enemySpriteAtlas.src = './img/enemies.png'
|
||||||
|
|
||||||
|
const enemyAtlas = {
|
||||||
|
tileAtlas: enemySpriteAtlas,
|
||||||
|
tileSize: 16,
|
||||||
|
tileOutputSize: 2,
|
||||||
|
updatedTileSize: tileSize * tileOutputSize,
|
||||||
|
atlasCol: 4,
|
||||||
|
atlasRow: 3,
|
||||||
|
mapIndex: 0,
|
||||||
|
sourceX: 0,
|
||||||
|
sourceY: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemsSpriteAtlas = new Image()
|
||||||
|
itemsSpriteAtlas.src = './img/items.png'
|
||||||
|
|
||||||
|
const itemsAtlas = {
|
||||||
|
tileAtlas: itemsSpriteAtlas,
|
||||||
|
tileSize: 16,
|
||||||
|
tileOutputSize: 2,
|
||||||
|
updatedTileSize: tileSize * tileOutputSize,
|
||||||
|
atlasCol: 4,
|
||||||
|
atlasRow: 4,
|
||||||
|
mapIndex: 0,
|
||||||
|
sourceX: 0,
|
||||||
|
sourceY: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
const meterSpriteAtlas = new Image()
|
||||||
|
meterSpriteAtlas.src = './img/meter.png'
|
||||||
|
|
||||||
|
const meterAtlas = {
|
||||||
|
tileAtlas: meterSpriteAtlas,
|
||||||
|
tileSize: 7,
|
||||||
|
tileOutputSize: 2,
|
||||||
|
updatedTileSize: tileSize * tileOutputSize,
|
||||||
|
atlasCol: 4,
|
||||||
|
atlasRow: 6,
|
||||||
|
mapIndex: 0,
|
||||||
|
sourceX: 0,
|
||||||
|
sourceY: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// * End Test Sprite Imports
|
||||||
|
|
||||||
|
// * Das currentLevel Objekt sollte durch irgendeine Funktion befüllt werden, wenn ein Level gewechselt wird.
|
||||||
|
|
||||||
|
let levelObject = {
|
||||||
|
level1: {
|
||||||
|
scalingFactor: 8,
|
||||||
|
levelData: level1,
|
||||||
|
levelAtlas: level2Atlas,
|
||||||
|
backgroundAtlas: backgroundAtlas,
|
||||||
|
foregroundAtlas: level2Atlas,
|
||||||
|
playerStartCoordinates: { x: 96, y: 480 }, // * Für wenn man das Level durch eine Tür erneut betritt.
|
||||||
|
},
|
||||||
|
level2: {
|
||||||
|
scalingFactor: 4,
|
||||||
|
levelData: level2,
|
||||||
|
levelAtlas: level2Atlas,
|
||||||
|
backgroundAtlas: backgroundAtlas,
|
||||||
|
foregroundAtlas: level2Atlas,
|
||||||
|
playerStartCoordinates: { x: 96, y: 480 }, // * Für wenn man das Level durch eine Tür erneut betritt.
|
||||||
|
},
|
||||||
|
level3: {
|
||||||
|
scalingFactor: 8,
|
||||||
|
levelData: level3,
|
||||||
|
levelAtlas: level2Atlas,
|
||||||
|
backgroundAtlas: backgroundAtlas,
|
||||||
|
foregroundAtlas: level2Atlas,
|
||||||
|
playerStartCoordinates: { x: 96, y: 480 }, // * Für wenn man das Level durch eine Tür erneut betritt.
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentLevel = levelObject.level1 // ! hier kann ich noch eine function draus machen, die das currentLevel immer auf dem aktuellen Stand hält. Vorläufig erstmal mit einem Button-Press
|
||||||
|
|
||||||
|
let mapWidth = currentLevel.levelData.width
|
||||||
|
let mapHeight = currentLevel.levelData.height
|
||||||
|
|
||||||
|
let MAP = { tw: mapWidth, th: mapHeight }
|
||||||
|
let TILE = 16
|
||||||
|
let METER = TILE
|
||||||
|
|
||||||
|
let GRAVITY = 60
|
||||||
|
|
||||||
|
let MAXDX = 15, // * default max horizontal speed (15 tiles per second)
|
||||||
|
MAXDY = 60, // * default max vertical speed (60 tiles per second)
|
||||||
|
ACCEL = 1 / 2, // * default take 1/2 second to reach maxdx (horizontal acceleration)
|
||||||
|
FRICTION = 1 / 6, // * default take 1/6 second to stop from maxdx (horizontal friction)
|
||||||
|
IMPULSE = 1500, // * default player jump impulse
|
||||||
|
COLOR = {
|
||||||
|
BLACK: '#000000',
|
||||||
|
YELLOW: '#ECD078',
|
||||||
|
BRICK: '#D95B43',
|
||||||
|
PINK: '#C02942',
|
||||||
|
PURPLE: '#542437',
|
||||||
|
GREY: '#333',
|
||||||
|
SLATE: '#53777A',
|
||||||
|
GOLD: 'gold',
|
||||||
|
},
|
||||||
|
COLORS = [COLOR.YELLOW, COLOR.BRICK, COLOR.PINK, COLOR.PURPLE, COLOR.GREY],
|
||||||
|
KEY = {
|
||||||
|
SPACE: ' ',
|
||||||
|
LEFT: 'ArrowLeft',
|
||||||
|
UP: 'ArrowUp',
|
||||||
|
RIGHT: 'ArrowRight',
|
||||||
|
DOWN: 'ArrowDown',
|
||||||
|
PAUSE: 'p',
|
||||||
|
ENTER: 'Enter',
|
||||||
|
X: 'x',
|
||||||
|
Y: 'y',
|
||||||
|
L: 'l',
|
||||||
|
}
|
||||||
|
|
||||||
|
const ammoType = {
|
||||||
|
width: 4,
|
||||||
|
height: 4,
|
||||||
|
distance: 100,
|
||||||
|
velocity: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
let fps = 60,
|
||||||
|
step = 1 / fps,
|
||||||
|
canvas = document.getElementById('canvas'),
|
||||||
|
ctx = canvas.getContext('2d'),
|
||||||
|
player = {},
|
||||||
|
monsters = [],
|
||||||
|
pickups = [],
|
||||||
|
treasure = [],
|
||||||
|
doors = [], // ! Hier sammel ich alle Door Objekte.
|
||||||
|
liquids = [], // ! Alle Bereich, die mit Wasser gefüllt sind.
|
||||||
|
bullets = [],
|
||||||
|
cells = [], // ! vielleicht kann ich hieraus aber auch einfach ein Objekt machen, das Arrays für collCells, bgCells und fgCells beinhaltet
|
||||||
|
bgCells = [], // * Die sind für die background cells.
|
||||||
|
fgCells = [], // * Die sind für die foreground cells.
|
||||||
|
lqCells = [],
|
||||||
|
paused = false,
|
||||||
|
showDevInfo = false
|
||||||
|
|
||||||
|
canvas.width = mapWidth * TILE
|
||||||
|
canvas.height = mapHeight * TILE
|
||||||
|
|
||||||
|
let width = (canvas.width = mapWidth * TILE) // * Das ist clever, weil ich so der Canvas immer von der Map Size abhängig ist. Zusammen mit ctx.scale und ctx.translate bekomme ich so auch eine Camera hin!
|
||||||
|
let height = (canvas.height = mapHeight * TILE)
|
||||||
|
|
||||||
|
let scalingFactor = currentLevel.scalingFactor // * 8 fühlt sich ungefähr nach 8bit Grafik an.
|
||||||
|
|
||||||
|
let scaledCanvas = {
|
||||||
|
width: canvas.width / scalingFactor,
|
||||||
|
height: canvas.height / scalingFactor,
|
||||||
|
}
|
||||||
|
|
||||||
|
let t2p = function (t) {
|
||||||
|
return t * TILE
|
||||||
|
},
|
||||||
|
p2t = function (p) {
|
||||||
|
return Math.floor(p / TILE)
|
||||||
|
},
|
||||||
|
cell = function (x, y) {
|
||||||
|
return tcell(p2t(x), p2t(y), mapWidth)
|
||||||
|
},
|
||||||
|
tcell = function (tx, ty, mapWidth) {
|
||||||
|
// ! Hier brauche ich entweder eine allgemeinere Funktion oder noch weiter für bgCells ud fgCells.
|
||||||
|
return cells[tx + ty * mapWidth]
|
||||||
|
},
|
||||||
|
bgTcell = function (tx, ty, mapWidth) {
|
||||||
|
return bgCells[tx + ty * mapWidth]
|
||||||
|
},
|
||||||
|
fgTcell = function (tx, ty, mapWidth) {
|
||||||
|
return fgCells[tx + ty * mapWidth]
|
||||||
|
},
|
||||||
|
lqTcell = function (tx, ty, mapWidth) {
|
||||||
|
return !!lqCells && lqCells[tx + ty * mapWidth]
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Init Level Funtion
|
||||||
|
function _initLevel(newLevel) {
|
||||||
|
monsters = []
|
||||||
|
treasure = []
|
||||||
|
pickups = []
|
||||||
|
doors = []
|
||||||
|
liquids = []
|
||||||
|
cells = []
|
||||||
|
currentLevel = newLevel
|
||||||
|
player.collected = 0
|
||||||
|
|
||||||
|
mapWidth = currentLevel.levelData.width
|
||||||
|
mapHeight = currentLevel.levelData.height
|
||||||
|
|
||||||
|
canvas.width = mapWidth * TILE
|
||||||
|
canvas.height = mapHeight * TILE
|
||||||
|
|
||||||
|
scalingFactor = currentLevel.scalingFactor
|
||||||
|
|
||||||
|
scaledCanvas = {
|
||||||
|
width: canvas.width / scalingFactor,
|
||||||
|
height: canvas.height / scalingFactor,
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(newLevel.levelData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Const Sfx from audio files //
|
||||||
|
// ! Achtung: utils müssen in index.html vor den variables geladen werden.
|
||||||
|
|
||||||
|
let sfxVolume = 0.2
|
||||||
|
let musicVolume = 0.5
|
||||||
|
|
||||||
|
let sfx = {
|
||||||
|
jump: new sound('./audio/Jump.wav', sfxVolume),
|
||||||
|
pickup: new sound('./audio/Pickup_Coin.wav', sfxVolume),
|
||||||
|
lowHealth: new sound('./audio/LA_LowHealth.wav', sfxVolume),
|
||||||
|
die: new sound('./audio/Explosion.wav', sfxVolume),
|
||||||
|
takeDamage: new sound('./audio/Randomize3.wav', sfxVolume),
|
||||||
|
killMonster: new sound('./audio/Randomize.wav', sfxVolume),
|
||||||
|
powerup: new sound('./audio/Powerup.wav', sfxVolume),
|
||||||
|
openDoor: new sound('./audio/LA_Chest_Open.wav', sfxVolume),
|
||||||
|
shoot: new sound('./audio/Laser2.wav', sfxVolume),
|
||||||
|
explode: new sound('./audio/Explosion.wav', sfxVolume),
|
||||||
|
splash: new sound('./audio/Oracle_Rock_Shatter.wav', sfxVolume),
|
||||||
|
swim: new sound('./audio/SML2_Swim.ogg', sfxVolume),
|
||||||
|
click: new sound('./audio/click.wav', sfxVolume),
|
||||||
|
|
||||||
|
stomp: new sound('./audio/smb_stomp.wav', sfxVolume),
|
||||||
|
kick: new sound('./audio/smb_kick.wav', sfxVolume),
|
||||||
|
extraLife: new sound('./audio/smb_1-up.wav', sfxVolume),
|
||||||
|
smb_powerup: new sound('./audio/smb_powerup.wav', sfxVolume),
|
||||||
|
}
|
||||||
|
|
||||||
|
let theme = {
|
||||||
|
level1: new music('./audio/music/8BitCave.wav', musicVolume),
|
||||||
|
}
|
||||||
|
|
||||||
|
let globalPlayer = {
|
||||||
|
collected: 0,
|
||||||
|
killed: 0,
|
||||||
|
maxHitpoints: 5,
|
||||||
|
currentHitpoints: 3,
|
||||||
|
vul: true,
|
||||||
|
hurt: false,
|
||||||
|
interact: false,
|
||||||
|
shooting: false,
|
||||||
|
swimming: false,
|
||||||
|
sprites: {
|
||||||
|
idle: {
|
||||||
|
tiles: [5],
|
||||||
|
framerate: 5,
|
||||||
|
framebuffer: 8,
|
||||||
|
loop: true,
|
||||||
|
currentFrame: 0,
|
||||||
|
},
|
||||||
|
run: {
|
||||||
|
tiles: [0, 1, 2, 1],
|
||||||
|
framerate: 5,
|
||||||
|
framebuffer: 10,
|
||||||
|
loop: true,
|
||||||
|
currentFrame: 0,
|
||||||
|
},
|
||||||
|
jump: {
|
||||||
|
tiles: [4],
|
||||||
|
framerate: 5,
|
||||||
|
framebuffer: 8,
|
||||||
|
loop: true,
|
||||||
|
currentFrame: 0,
|
||||||
|
},
|
||||||
|
shoot: {
|
||||||
|
tiles: [3],
|
||||||
|
framerate: 5,
|
||||||
|
framebuffer: 8,
|
||||||
|
loop: true,
|
||||||
|
currentFrame: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||