Initial commit
14
.babelrc
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env"],
|
||||
"plugins": [
|
||||
[
|
||||
"@babel/plugin-transform-runtime",
|
||||
{
|
||||
"corejs": false,
|
||||
"helpers": true,
|
||||
"regenerator": true,
|
||||
"useESModules": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
14
.editorconfig
Normal file
@ -0,0 +1,14 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# Matches multiple files with brace expansion notation
|
||||
# Set default charset
|
||||
[*.js]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/node_modules/
|
||||
/dist/
|
||||
/package-lock.json
|
||||
/**/dna.txt
|
||||
*.db
|
||||
*.sqlite3
|
10
index.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<style type="text/css">*{margin:0;padding:0;}body{width:100%;height:100%;}body,html{overflow:hidden;}</style>
|
||||
<title>Hook Miner</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
27
package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "aaaidk",
|
||||
"version": "0.0.1",
|
||||
"description": "idk",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "webpack -p",
|
||||
"watch": "webpack -w --mode=development"
|
||||
},
|
||||
"keywords": [],
|
||||
"private": true,
|
||||
"author": "Evert \"Diamond\" Prants <evert@lunasqu.ee>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.1.6",
|
||||
"@babel/plugin-transform-runtime": "^7.1.0",
|
||||
"@babel/preset-env": "^7.1.6",
|
||||
"babel-loader": "^8.0.4",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"standard": "^12.0.1",
|
||||
"webpack": "^4.26.0",
|
||||
"webpack-command": "^0.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.1.5"
|
||||
}
|
||||
}
|
65
src/canvas.js
Normal file
@ -0,0 +1,65 @@
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
ctx.mouse = { pos: {} }
|
||||
|
||||
canvas.addEventListener('mousemove', (e) => {
|
||||
let x
|
||||
let y
|
||||
|
||||
if (e.changedTouches) {
|
||||
let touch = e.changedTouches[0]
|
||||
if (touch) {
|
||||
e.pageX = touch.pageX
|
||||
e.pageY = touch.pageY
|
||||
}
|
||||
}
|
||||
|
||||
if (e.pageX || e.pageY) {
|
||||
x = e.pageX
|
||||
y = e.pageY
|
||||
} else {
|
||||
x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft
|
||||
y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop
|
||||
}
|
||||
|
||||
x -= canvas.offsetLeft
|
||||
y -= canvas.offsetTop
|
||||
|
||||
if (ctx.oX && ctx.oY) {
|
||||
x += ctx.oX
|
||||
y += ctx.oY
|
||||
}
|
||||
|
||||
ctx.mouse.pos.x = x
|
||||
ctx.mouse.pos.y = y
|
||||
}, false)
|
||||
|
||||
canvas.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault()
|
||||
ctx.mouse['btn' + e.button] = true
|
||||
})
|
||||
|
||||
canvas.addEventListener('mouseup', (e) => {
|
||||
e.preventDefault()
|
||||
ctx.mouse['btn' + e.button] = false
|
||||
})
|
||||
|
||||
canvas.addEventListener('contextmenu', (e) => {
|
||||
e.preventDefault()
|
||||
})
|
||||
|
||||
// Resize the canvas when window is resized
|
||||
canvas.resizeWorkspace = function () {
|
||||
canvas.width = window.innerWidth
|
||||
canvas.height = window.innerHeight
|
||||
}
|
||||
|
||||
window.addEventListener('resize', canvas.resizeWorkspace, false)
|
||||
|
||||
// Add elements to the document
|
||||
document.body.appendChild(canvas)
|
||||
|
||||
canvas.resizeWorkspace()
|
||||
|
||||
export { canvas, ctx }
|
187
src/game.js
Normal file
@ -0,0 +1,187 @@
|
||||
import { canvas, ctx } from './canvas'
|
||||
import { Level, Rock, Gold, Diamond, Lootbag } from './level'
|
||||
import { randomi } from './utils'
|
||||
|
||||
const REWARD_TABLE = {
|
||||
rock: 16,
|
||||
gold: [129, 543, 2399],
|
||||
diamond: 599,
|
||||
lootbag: [5, 5000]
|
||||
}
|
||||
|
||||
export class Game {
|
||||
constructor (time, player, oh, gw, gh) {
|
||||
this.currentLevel = 0
|
||||
this.player = player
|
||||
this.roundTime = time
|
||||
this.time = time
|
||||
this.score = 0
|
||||
this.goal = 0
|
||||
|
||||
this.level = null
|
||||
this.oh = oh
|
||||
this.gh = gh
|
||||
this.gw = gw
|
||||
|
||||
this.message = null
|
||||
|
||||
this.state = 0
|
||||
this.wait = 0
|
||||
}
|
||||
|
||||
static calculateScore (obj, skipLoot) {
|
||||
if (obj instanceof Rock) {
|
||||
return REWARD_TABLE.rock * obj.size
|
||||
}
|
||||
|
||||
if (obj instanceof Gold) {
|
||||
return REWARD_TABLE.gold[obj.size - 1]
|
||||
}
|
||||
|
||||
if (obj instanceof Diamond) {
|
||||
return REWARD_TABLE.diamond
|
||||
}
|
||||
|
||||
if (skipLoot) return 0
|
||||
|
||||
if (obj instanceof Lootbag) {
|
||||
let ssChance = randomi(0, 3)
|
||||
if (ssChance === 1) {
|
||||
return 'special'
|
||||
}
|
||||
return randomi(REWARD_TABLE.lootbag[0], REWARD_TABLE.lootbag[1])
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
static calculateLevelScore (level) {
|
||||
let total = 0
|
||||
for (let i in level.objects) {
|
||||
total += Game.calculateScore(level.objects[i], true)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
nextLevel () {
|
||||
this.player.superStrength = false
|
||||
this.currentLevel++
|
||||
this.time = this.roundTime
|
||||
this.level = Level.create(this.currentLevel, this.oh, this.gw, this.gh)
|
||||
this.goal = Math.floor(this.score / 2 + Game.calculateLevelScore(this.level) * 0.65)
|
||||
}
|
||||
|
||||
displayMessage (msg, time = 60) {
|
||||
this.message = { text: msg, time: time, duration: time }
|
||||
}
|
||||
|
||||
scoredItem (obj) {
|
||||
let scored = Game.calculateScore(obj)
|
||||
if (scored === 'special') {
|
||||
this.displayMessage('SUPER STRENGTH!')
|
||||
this.player.superStrength = true
|
||||
return
|
||||
}
|
||||
|
||||
this.displayMessage('$' + scored)
|
||||
this.score += scored
|
||||
}
|
||||
|
||||
update () {
|
||||
ctx.oX = (canvas.width - this.gw) / 2
|
||||
ctx.oY = (canvas.height - this.gh) / 2
|
||||
|
||||
if (this.state === 0 && this.time === 0) {
|
||||
if (this.score > this.goal) {
|
||||
this.state = 1
|
||||
this.wait = 160
|
||||
this.nextLevel()
|
||||
} else {
|
||||
this.state = 2
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state === 1) {
|
||||
if (this.wait > 0) {
|
||||
this.wait--
|
||||
} else {
|
||||
this.state = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (this.state !== 0) return
|
||||
if (this.message) {
|
||||
if (this.message.time > 0) {
|
||||
this.message.time--
|
||||
} else {
|
||||
this.message = null
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
this.player.update(this.level)
|
||||
}
|
||||
|
||||
tick () {
|
||||
if (this.state === 0 && this.time > 0) {
|
||||
this.time -= 1
|
||||
}
|
||||
}
|
||||
|
||||
draw () {
|
||||
if (this.state === 2) {
|
||||
ctx.fillStyle = '#ff1111'
|
||||
ctx.font = '20px sans-serif'
|
||||
let t = 'Game Over!'
|
||||
let s = 'Score: $' + this.score
|
||||
ctx.fillText(t, ctx.oX + this.gw / 2 - ctx.measureText(t).width / 2, ctx.oY + this.gh / 2 - 15)
|
||||
ctx.fillText(s, ctx.oX + this.gw / 2 - ctx.measureText(s).width / 2, ctx.oY + this.gh / 2 + 15)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.state === 1) {
|
||||
ctx.fillStyle = '#05ff05'
|
||||
ctx.font = '20px sans-serif'
|
||||
let t = 'Level Cleared!'
|
||||
let s = 'Next Goal: $' + this.goal
|
||||
ctx.fillText(t, ctx.oX + this.gw / 2 - ctx.measureText(t).width / 2, ctx.oY + this.gh / 2 - 15)
|
||||
ctx.fillText(s, ctx.oX + this.gw / 2 - ctx.measureText(s).width / 2, ctx.oY + this.gh / 2 + 15)
|
||||
return
|
||||
}
|
||||
|
||||
// Underground
|
||||
ctx.fillStyle = '#3a2201'
|
||||
ctx.fillRect(ctx.oX, ctx.oY, this.gw, this.gh)
|
||||
// Room
|
||||
ctx.fillStyle = '#b26b08'
|
||||
ctx.fillRect(ctx.oX, ctx.oY, this.gw, 25 + this.player.h)
|
||||
// Floor
|
||||
ctx.fillStyle = '#2b1a02'
|
||||
ctx.fillRect(ctx.oX, ctx.oY + 25 + this.player.h, this.gw, 15)
|
||||
|
||||
if (this.level) this.level.draw()
|
||||
this.player.draw()
|
||||
|
||||
ctx.fillStyle = '#05ff05'
|
||||
ctx.font = '20px sans-serif'
|
||||
ctx.fillText('Money: $' + this.score, ctx.oX + 20, ctx.oY + 30)
|
||||
ctx.fillStyle = '#eabb4d'
|
||||
ctx.fillText('Goal: $' + this.goal, ctx.oX + 20, ctx.oY + 62)
|
||||
ctx.fillStyle = '#05ff05'
|
||||
let time = 'Time: ' + this.time
|
||||
let ftsize = ctx.measureText(time)
|
||||
ctx.fillText(time, ctx.oX + this.gw - ftsize.width - 5, ctx.oY + 30)
|
||||
|
||||
let round = 'Level ' + this.currentLevel
|
||||
let frsize = ctx.measureText(round)
|
||||
ctx.fillText(round, ctx.oX + this.gw - frsize.width - 5, ctx.oY + 62)
|
||||
|
||||
if (this.message && this.message.time > 0) {
|
||||
let tfloat = this.message.duration - this.message.time
|
||||
ctx.fillStyle = '#05ff05'
|
||||
ctx.font = '20px sans-serif'
|
||||
ctx.fillText(this.message.text, ctx.oX + this.player.x - this.player.w - tfloat, ctx.oY + this.player.y + this.player.h / 2 - tfloat / 2)
|
||||
}
|
||||
}
|
||||
}
|
33
src/image.js
Normal file
@ -0,0 +1,33 @@
|
||||
import Resource from './resource'
|
||||
import { ctx } from './canvas'
|
||||
|
||||
let imageCache = {}
|
||||
|
||||
class Image {
|
||||
constructor (file, img) {
|
||||
this.file = file
|
||||
this.img = img
|
||||
}
|
||||
|
||||
static async load (file) {
|
||||
if (imageCache[file]) return imageCache[file]
|
||||
let img = await Resource.loadImage(file)
|
||||
let imgCl = new Image(file, img)
|
||||
imageCache[file] = imgCl
|
||||
return imgCl
|
||||
}
|
||||
|
||||
get width () {
|
||||
return this.img.width
|
||||
}
|
||||
|
||||
get height () {
|
||||
return this.img.height
|
||||
}
|
||||
|
||||
draw (x, y, w, h) {
|
||||
ctx.drawImage(this.img, x, y, w, h)
|
||||
}
|
||||
}
|
||||
|
||||
export { Image }
|
44
src/index.js
Normal file
@ -0,0 +1,44 @@
|
||||
/* global requestAnimationFrame */
|
||||
import { canvas, ctx } from './canvas'
|
||||
import { Game } from './game'
|
||||
import { Player } from './player'
|
||||
import RES from './resource'
|
||||
|
||||
const GameWidth = 1080
|
||||
const GameHeight = 720
|
||||
|
||||
let playing = true
|
||||
let player = new Player(GameWidth / 2 - 30, 25, GameHeight - 80)
|
||||
let game = new Game(60, player, player.h + 60, GameWidth, GameHeight)
|
||||
|
||||
// Retranslate score function
|
||||
player.score = function (obj) {
|
||||
/* eslint-disable no-useless-call */
|
||||
game.scoredItem.call(game, obj)
|
||||
}
|
||||
|
||||
function gameLoop () {
|
||||
playing && requestAnimationFrame(gameLoop)
|
||||
ctx.fillStyle = '#111'
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||||
game.update()
|
||||
game.draw()
|
||||
}
|
||||
|
||||
function start () {
|
||||
game.nextLevel()
|
||||
gameLoop()
|
||||
setInterval(function () {
|
||||
game.tick()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
async function loadAll () {
|
||||
let images = ['static/hook_open.png', 'static/gold_1.png', 'static/gold_2.png', 'static/gold_3.png',
|
||||
'static/rock_1.png', 'static/rock_2.png', 'static/rock_3.png', 'static/diamond.png', 'static/loot.png']
|
||||
for (let i in images) {
|
||||
await RES.loadImage(images[i])
|
||||
}
|
||||
}
|
||||
|
||||
loadAll().then(start)
|
136
src/level.js
Normal file
@ -0,0 +1,136 @@
|
||||
import { ctx } from './canvas'
|
||||
import { randomi } from './utils'
|
||||
import RES from './resource'
|
||||
|
||||
const offset = 50
|
||||
|
||||
// Objects
|
||||
|
||||
export class GameObject {
|
||||
constructor (x, y, w, h, img) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.w = w
|
||||
this.h = h
|
||||
this.img = img
|
||||
this.physical = true
|
||||
}
|
||||
|
||||
draw () {
|
||||
if (!this.physical) return
|
||||
if (this.img.indexOf('#') === 0) {
|
||||
ctx.fillStyle = this.img
|
||||
ctx.fillRect(ctx.oX + this.x, ctx.oY + this.y, this.w, this.h)
|
||||
} else {
|
||||
ctx.drawImage(RES.loadImage(this.img, true), ctx.oX + this.x, ctx.oY + this.y, this.w, this.h)
|
||||
}
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.physical = false
|
||||
}
|
||||
|
||||
get weight () {
|
||||
return 0.1
|
||||
}
|
||||
}
|
||||
|
||||
export class Rock extends GameObject {
|
||||
constructor (x, y, size) {
|
||||
let o = size * 20
|
||||
super(x, y, o, o, 'static/rock_' + size + '.png')
|
||||
this.size = size
|
||||
}
|
||||
|
||||
get weight () {
|
||||
return Math.min(0.20 * this.size, 1)
|
||||
}
|
||||
}
|
||||
|
||||
export class Gold extends GameObject {
|
||||
constructor (x, y, size) {
|
||||
let o = size * 24
|
||||
super(x, y, o, o, 'static/gold_' + size + '.png')
|
||||
this.size = size
|
||||
}
|
||||
|
||||
get weight () {
|
||||
return Math.min(0.25 * this.size, 1)
|
||||
}
|
||||
}
|
||||
|
||||
export class Diamond extends GameObject {
|
||||
constructor (x, y) {
|
||||
super(x, y, 16, 16, 'static/diamond.png')
|
||||
}
|
||||
|
||||
get weight () {
|
||||
return 0.11
|
||||
}
|
||||
}
|
||||
|
||||
export class Lootbag extends GameObject {
|
||||
constructor (x, y) {
|
||||
super(x, y, 30, 30, 'static/loot.png')
|
||||
}
|
||||
|
||||
get weight () {
|
||||
return 0.56
|
||||
}
|
||||
}
|
||||
|
||||
// Level
|
||||
|
||||
export class Level {
|
||||
constructor (id, objects) {
|
||||
this.id = id
|
||||
this.objects = objects || []
|
||||
}
|
||||
|
||||
draw () {
|
||||
for (let i in this.objects) {
|
||||
this.objects[i].draw()
|
||||
}
|
||||
}
|
||||
|
||||
static create (index, my, width, height) {
|
||||
let objects = []
|
||||
let rocks = randomi(4, 12)
|
||||
let gold = randomi(4, 12)
|
||||
let diamond = randomi(0, 2)
|
||||
let loot = randomi(0, 2)
|
||||
|
||||
// Add rocks
|
||||
for (let r = 0; r < rocks; r++) {
|
||||
let x = randomi(offset, width - offset)
|
||||
let y = randomi(offset + my, height - (offset + my))
|
||||
let size = randomi(1, 4)
|
||||
objects.push(new Rock(x, y, size))
|
||||
}
|
||||
|
||||
// Add gold
|
||||
for (let r = 0; r < gold; r++) {
|
||||
let x = randomi(offset, width - offset)
|
||||
let y = randomi(offset + my, height - (offset + my))
|
||||
let size = randomi(1, 4)
|
||||
objects.push(new Gold(x, y, size))
|
||||
}
|
||||
|
||||
// Add diamonds
|
||||
for (let r = 0; r < diamond; r++) {
|
||||
let x = randomi(offset, width - offset)
|
||||
let y = randomi(offset + my, height - (offset + my))
|
||||
objects.push(new Diamond(x, y))
|
||||
}
|
||||
|
||||
// Add loot
|
||||
for (let r = 0; r < loot; r++) {
|
||||
let x = randomi(offset, width - offset)
|
||||
let y = randomi(offset + my, height - (offset + my))
|
||||
objects.push(new Lootbag(x, y))
|
||||
}
|
||||
|
||||
let n = new Level(index, objects)
|
||||
return n
|
||||
}
|
||||
}
|
174
src/player.js
Normal file
@ -0,0 +1,174 @@
|
||||
import { ctx } from './canvas'
|
||||
import { GameObject } from './level'
|
||||
import { deg2rad, rad2vec, intersectRect } from './utils'
|
||||
import RES from './resource'
|
||||
|
||||
const FULL_ROTATION = -85
|
||||
const FULL_ROTATION_EDGE = -FULL_ROTATION
|
||||
|
||||
class Hook extends GameObject {
|
||||
constructor (player, x, y, w, h, len) {
|
||||
super(x, y, w, h)
|
||||
this.player = player
|
||||
|
||||
// Return position
|
||||
this.rx = x
|
||||
this.ry = y
|
||||
|
||||
// Hook rotation
|
||||
// Hook rotation direction
|
||||
this.r = 0
|
||||
this.rd = 1
|
||||
|
||||
// Distance from center
|
||||
// Moving direction
|
||||
this.d = 0
|
||||
this.md = -1
|
||||
|
||||
// Travel length
|
||||
this.len = len
|
||||
|
||||
// Attached object
|
||||
this.obj = null
|
||||
}
|
||||
|
||||
draw () {
|
||||
if (this.md !== -1) {
|
||||
// Draw line
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(ctx.oX + this.rx, ctx.oY + this.ry)
|
||||
ctx.lineTo(ctx.oX + this.x, ctx.oY + this.y)
|
||||
ctx.stroke()
|
||||
ctx.closePath()
|
||||
}
|
||||
|
||||
let hookr = deg2rad(360 - this.r)
|
||||
ctx.save()
|
||||
ctx.translate(ctx.oX + this.x, ctx.oY + this.y)
|
||||
ctx.rotate(hookr)
|
||||
ctx.drawImage(RES.loadImage('static/hook_open.png', true), -this.w / 2, -this.h / 2, this.w, this.h)
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
update (level) {
|
||||
if (this.d === 0 && this.r < FULL_ROTATION_EDGE && this.rd === 1) {
|
||||
this.r += 1
|
||||
}
|
||||
|
||||
if (this.d === 0 && this.r > FULL_ROTATION && this.rd === 0) {
|
||||
this.r -= 1
|
||||
}
|
||||
|
||||
if (this.r >= FULL_ROTATION_EDGE && this.rd === 1) {
|
||||
this.r = FULL_ROTATION_EDGE
|
||||
this.rd = 0
|
||||
}
|
||||
|
||||
if (this.r <= FULL_ROTATION && this.rd === 0) {
|
||||
this.r = FULL_ROTATION
|
||||
this.rd = 1
|
||||
}
|
||||
|
||||
if (ctx.mouse['btn0'] && this.d === 0) {
|
||||
this.d = 0
|
||||
this.md = 1
|
||||
}
|
||||
|
||||
if (this.md > -1) {
|
||||
if (this.d > this.len && this.md === 1) {
|
||||
this.md = 0
|
||||
}
|
||||
|
||||
if (this.d <= 2 && this.md === 0) {
|
||||
this.d = 0
|
||||
this.md = -1
|
||||
this.x = this.rx
|
||||
this.y = this.ry
|
||||
|
||||
// Score
|
||||
if (this.obj) {
|
||||
this.player.score(this.obj)
|
||||
this.obj.destroy()
|
||||
this.obj = null
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let dir = rad2vec(deg2rad(90 - this.r))
|
||||
dir.x *= this.d
|
||||
dir.y *= this.d
|
||||
|
||||
this.x = this.rx + dir.x
|
||||
this.y = this.ry + dir.y
|
||||
|
||||
if (this.obj) {
|
||||
this.obj.x = this.x - this.obj.w / 2
|
||||
this.obj.y = this.y
|
||||
}
|
||||
|
||||
// Detect intersection
|
||||
if (this.md === 1) {
|
||||
if (!this.obj) {
|
||||
let firstIntersect
|
||||
for (let i in level.objects) {
|
||||
let obj = level.objects[i]
|
||||
if (obj.physical && intersectRect(obj, this)) {
|
||||
firstIntersect = obj
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (firstIntersect) {
|
||||
this.obj = firstIntersect
|
||||
this.md = 0
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (this.player.superStrength) {
|
||||
this.d += 10
|
||||
} else {
|
||||
this.d += 5
|
||||
}
|
||||
} else {
|
||||
if (this.player.superStrength) {
|
||||
this.d -= 10
|
||||
} else {
|
||||
this.d -= this.obj ? 5 * (1 - this.obj.weight) : 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Player extends GameObject {
|
||||
constructor (x, y, mh) {
|
||||
super(x, y, 60, 55, '#4d4b4f')
|
||||
this.x = x
|
||||
this.y = y
|
||||
|
||||
this.hook = new Hook(this, this.x + this.w / 2, this.y + this.h, 20, 20, mh)
|
||||
this.hook.r = FULL_ROTATION
|
||||
|
||||
this.superStrength = false
|
||||
}
|
||||
|
||||
score (object) {
|
||||
console.log('Scored', object)
|
||||
}
|
||||
|
||||
draw () {
|
||||
// Draw player
|
||||
super.draw()
|
||||
|
||||
// Draw hook
|
||||
this.hook.draw()
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
update (level) {
|
||||
if (!level) return
|
||||
this.hook.update(level)
|
||||
}
|
||||
}
|
103
src/resource.js
Normal file
@ -0,0 +1,103 @@
|
||||
/* global XMLHttpRequest, Image */
|
||||
|
||||
let imgCache = {}
|
||||
|
||||
function powerOfTwo (n) {
|
||||
return n && (n & (n - 1)) === 0
|
||||
}
|
||||
|
||||
function GET (url, istext) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var xmlHttp = new XMLHttpRequest()
|
||||
|
||||
xmlHttp.onreadystatechange = function () {
|
||||
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
|
||||
resolve(xmlHttp.responseText)
|
||||
} else if (xmlHttp.readyState === 4 && xmlHttp.status >= 400) {
|
||||
let err = new Error(xmlHttp.status)
|
||||
err.request = xmlHttp
|
||||
reject(err)
|
||||
}
|
||||
}
|
||||
|
||||
xmlHttp.open('GET', url, true)
|
||||
istext && (xmlHttp.responseType = 'text')
|
||||
xmlHttp.send(null)
|
||||
})
|
||||
}
|
||||
|
||||
function smartGET (data) {
|
||||
if (typeof data === 'string') {
|
||||
data = {
|
||||
url: data,
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.type) data.type = 'text'
|
||||
let istext = (data.type !== 'image' && data.type !== 'file')
|
||||
|
||||
let url = data.url
|
||||
if (!url) throw new Error('URL is required!')
|
||||
|
||||
if (data.type === 'json') {
|
||||
return new Promise((resolve, reject) => {
|
||||
GET(url).then((dtext) => {
|
||||
try {
|
||||
let jsonp = JSON.parse(dtext)
|
||||
return resolve(jsonp)
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
}, reject)
|
||||
})
|
||||
}
|
||||
|
||||
return GET(data.url, istext)
|
||||
}
|
||||
|
||||
function loadImage (url, onlyReturn = false, nowarn = false) {
|
||||
if (onlyReturn && imgCache[url]) {
|
||||
return imgCache[url]
|
||||
}
|
||||
|
||||
// Ensure we don't load a texture multiple times
|
||||
if (imgCache[url]) return new Promise((resolve, reject) => resolve(imgCache[url]))
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let img = new Image()
|
||||
|
||||
img.onload = function () {
|
||||
// Friendly warnings
|
||||
if ((!powerOfTwo(img.width) || !powerOfTwo(img.height)) && !nowarn) {
|
||||
console.warn(`warn: image ${url} does not have dimensions that are powers of two. 16x16 to 128x128 recommended.`)
|
||||
}
|
||||
|
||||
if ((img.width / img.height) !== 1 && !nowarn) {
|
||||
console.warn(`warn: image ${url} does not have an aspect ratio of 1 to 1.`)
|
||||
}
|
||||
|
||||
imgCache[url] = img
|
||||
resolve(img)
|
||||
}
|
||||
|
||||
img.onerror = function (e) {
|
||||
reject(e)
|
||||
}
|
||||
|
||||
img.src = url
|
||||
})
|
||||
}
|
||||
|
||||
function imageToSampler (img) {
|
||||
let canvas = document.createElement('canvas')
|
||||
let ctx = canvas.getContext('2d')
|
||||
canvas.width = img.width
|
||||
canvas.height = img.height
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height)
|
||||
return function (x, y) {
|
||||
return ctx.getImageData(x, y, 1, 1).data
|
||||
}
|
||||
}
|
||||
|
||||
export default { GET: smartGET, imageToSampler, loadImage }
|
21
src/utils.js
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
// Utility function
|
||||
|
||||
export function randomi (min, max) {
|
||||
return Math.floor(Math.random() * (max - min) + min)
|
||||
}
|
||||
|
||||
export function deg2rad (deg) {
|
||||
return deg * Math.PI / 180
|
||||
}
|
||||
|
||||
export function rad2vec (r) {
|
||||
return { x: Math.cos(r), y: Math.sin(r) }
|
||||
}
|
||||
|
||||
export function intersectRect (r1, r2) {
|
||||
return !(r2.x > r1.w + r1.x ||
|
||||
r2.w + r2.x < r1.x ||
|
||||
r2.y > r1.h + r1.y ||
|
||||
r2.h + r2.y < r1.y)
|
||||
}
|
BIN
static/diamond.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
static/gold_1.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
static/gold_2.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
static/gold_3.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
static/hook_open.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
static/loot.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
static/rock_1.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
static/rock_2.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
static/rock_3.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
30
webpack.config.js
Normal file
@ -0,0 +1,30 @@
|
||||
const path = require('path')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
module.exports = (env) => {
|
||||
return {
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'app.js'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /(node_modules)/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/preset-env']
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
devtool: env.mode === 'development' ? 'inline-source-map' : '',
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({ template: 'index.html' })
|
||||
]
|
||||
}
|
||||
}
|