initial commit

This commit is contained in:
2026-04-24 11:24:04 +02:00
commit acf80258ae
138 changed files with 5209 additions and 0 deletions
Executable
+118
View File
@@ -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
} */
}
Executable
+44
View File
@@ -0,0 +1,44 @@
//-------------------------------------------------------------------------
// 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
shouldPanCameraDown({ canvas, cameraWithBox })
ev.preventDefault()
return false
case KEY.Y:
// player.shooting = down
shoot(player)
ev.preventDefault()
return false
case KEY.UP:
player.up = down
shouldPanCameraDown({ canvas, cameraWithBox })
ev.preventDefault()
return false
case KEY.DOWN:
player.down = down
ev.preventDefault()
return false
case KEY.ENTER:
player.interact = down
ev.preventDefault()
return false
case KEY.SPACE:
ev.preventDefault()
return false
}
}
Executable
+220
View File
@@ -0,0 +1,220 @@
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()
}
})
// * Touch Controls
function touchControl(ev, button, down) {
switch (button) {
case 'jump':
player.jump = down
ev.preventDefault()
return false
case 'shoot':
shoot(player)
ev.preventDefault()
return false
case 'left':
player.left = down
ev.preventDefault()
return false
case 'right':
player.right = down
ev.preventDefault()
return false
case 'up':
player.up = down
ev.preventDefault()
return false
case 'down':
player.down = down
ev.preventDefault()
return false
}
}
const buttonA = document.getElementById('button-a')
const buttonB = document.getElementById('button-b')
const dpadLeft = document.getElementById('dpad-left')
const dpadRight = document.getElementById('dpad-right')
const dpadUp = document.getElementById('dpad-up')
const dpadDown = document.getElementById('dpad-down')
buttonA.addEventListener(
'touchstart',
function (ev) {
return touchControl(ev, 'jump', true)
},
false
)
buttonA.addEventListener(
'touchend',
function (ev) {
return touchControl(ev, 'jump', false)
},
false
)
buttonB.addEventListener(
'touchstart',
function (ev) {
return touchControl(ev, 'shoot', true)
},
false
)
buttonB.addEventListener(
'touchend',
function (ev) {
return touchControl(ev, 'shoot', false)
},
false
)
dpadLeft.addEventListener(
'touchstart',
function (ev) {
return touchControl(ev, 'left', true)
},
false
)
dpadLeft.addEventListener(
'touchend',
function (ev) {
return touchControl(ev, 'left', false)
},
false
)
dpadRight.addEventListener(
'touchstart',
function (ev) {
return touchControl(ev, 'right', true)
},
false
)
dpadRight.addEventListener(
'touchend',
function (ev) {
return touchControl(ev, 'right', false)
},
false
)
dpadUp.addEventListener(
'touchstart',
function (ev) {
return touchControl(ev, 'up', true)
},
false
)
dpadUp.addEventListener(
'touchend',
function (ev) {
return touchControl(ev, 'up', false)
},
false
)
dpadDown.addEventListener(
'touchstart',
function (ev) {
return touchControl(ev, 'down', true)
},
false
)
dpadDown.addEventListener(
'touchend',
function (ev) {
return touchControl(ev, 'down', false)
},
false
)
// ! 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()
})()
Executable
+375
View File
@@ -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 / 3) * hudScaling,
t2p(3) * hudScaling,
4 * hudScaling,
8 * 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)
}
}
}
}
Executable
+150
View File
@@ -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 ', player)
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
}
Executable
+447
View File
@@ -0,0 +1,447 @@
//-------------------------------------------------------------------------
// 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()
}
}
// * Check if Player is Swimming or Diving
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
}
if (
overlap(
player.x,
player.y - player.height,
TILE,
TILE,
l.x,
l.y,
l.width,
l.height
)
) {
player.diving = true
reduceOxygen(4)
} else {
player.diving = false
replenishOxygen(16)
}
}
}
// * Reduce & Replenish Oxygen Functions
function reduceOxygen(rate) {
if (player.diving && globalObject.oxygen >= 0) {
globalObject.oxygen -= rate / 100
} else if (player.vul) {
reduceHitpoints(player, 1, 2000)
}
}
function replenishOxygen(rate) {
if (globalObject.oxygen <= globalObject.maxOxygen) {
globalObject.oxygen += rate / 100
}
}
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, timeout) {
const milliceconds = timeout || 1500
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
}, milliceconds)
} 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.oxygen = globalObject.maxOxygen
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))
}
Executable
+177
View File
@@ -0,0 +1,177 @@
//-------------------------------------------------------------------------
// 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
}
}
// * HUD Renderfunktion
function renderHudSprites() {
const hudObject = {
player_hitpoints: globalObject.hitpoints,
player_collected: globalObject.collected,
player_lifes: globalObject.lifes,
player_ammo: globalObject.ammo,
player_swimming: player.swimming,
player_diving: player.diving,
player_oxygen: Math.trunc(globalObject.oxygen),
}
function hitpoints() {
ctx.save()
ctx.scale(scalingFactor / 4, scalingFactor / 4)
ctx.textAlign = 'left'
ctx.font = '12px C64 TrueType'
ctx.fillStyle = 'white'
// Hitpoints
ctx.fillText(`HP: ${hudObject.player_hitpoints}`, 20, 40)
// Extralifes
ctx.fillText(`lifes: ${hudObject.player_lifes}`, 20, 20)
// Ammo
ctx.fillText(`ammo: ${hudObject.player_ammo}`, 200, 50)
// Oxygen
ctx.fillText(`O2: ${hudObject.player_oxygen}`, 200, 20)
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()
}
+378
View File
@@ -0,0 +1,378 @@
//-------------------------------------------------------------------------
// 2. GAME CONSTANTS AND VARIABLES
//-------------------------------------------------------------------------
// ! Globales Test-Objekt
let globalObject = {
lifes: 3,
hitpoints: 3,
maxHitpoints: 5,
collected: null,
killed: null,
ammo: 10,
maxAmmo: 10,
oxygen: 70,
upgrades: {
jumpboots: false,
oxygenTank1: false,
oxygenTank2: false,
},
get maxOxygen() {
if (this.upgrades.oxygenTank2) {
return 240
}
if (this.upgrades.oxygenTank1) {
return 120
} else return 60
},
}
// * 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,
diving: 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,
},
},
}