javascript
a single page application using plain js and bootstrap
this was my homework for the js & bootstrap front end course portfolio project
cartesian coordinates
- my first task was making canvas use cartesian coordinates with
0,0
in the CENTER1 - canvas by default starts
0,0
at the top left - my original python version had
0,0
in the center, I didn't want to monkey around so I moved it - this line translates the
0,0
to the center
what makes the adventure random?
- the treasure, and every item on an island gets dropped in a random location each game
generateCoordinate() {
let randoX = Math.floor(Math.random() * (this.eastBoundry - this.westBoundry + 1) + this.westBoundry)
let randoY = Math.floor(Math.random() * (this.northBoundry - this.southBoundry + 1) + this.southBoundry)
let coordinate = [randoX, randoY]
while (this.validateCoordinate(coordinate)) {
let randoX = Math.floor(Math.random() * (this.eastBoundry - this.westBoundry + 1) + this.westBoundry)
let randoY = Math.floor(Math.random() * (this.northBoundry - this.southBoundry + 1) + this.southBoundry)
coordinate = [randoX, randoY]
}
return coordinate
}
islands
- it would not be random island adenture without islands
- the island is a class
- I was planning to add levels so if you win you could build a boat and travel to a bigger island
- then I realized it would be easier to maintain the code with seperate spa(s)
- copy
ria.html
and if the player wins you provide a link toria_level_2.html
etc. - I was thinking I might adjust nsew boundaries so islands could be different sizes, but haven't finished implementing it
class island {
constructor(islandName, x_max, y_max) {
this.islandName = islandName
this.northBoundry = y_max
this.southBoundry = -y_max
this.westBoundry = -x_max
this.eastBoundry = x_max
this.usedCoordinates = [] // each island has a set of used coordinates
}
use coordinates
this.usedCoordinates
is where you can store objects like the treasure, disaster, fruit trees, etc.- that provides a way to make sure you're not dropping random items on the same coordinate
useCoordinate(coordinate) {
this.usedCoordinates.push(coordinate)
}
validateCoordinate(coordinate) {
// returns true if coordinate is already used
const validation = JSON.stringify(this.usedCoordinates).includes(JSON.stringify(coordinate));
return validation
}
movement
- you lose 1 energy when you move, if you run out of energy you lose
keyboard shortcuts
- if you want to be evil, you can change the keyboard shortcuts
- to something lame like actual arrows or wasd
- later in life you will learn vim, and you will look back on this day and regret not learning it now
function checkKey(e) {
randoMessage(chipDiamond)
// console.log(e.keyCode);
switch (e.keyCode) {
case 72:
// case 37:
playerOne.moveWest()
break;
case 75:
// case 38:
playerOne.moveNorth()
break;
case 76:
// case 39:
playerOne.moveEast()
break;
case 74:
// case 40:
playerOne.moveSouth()
break;
}
}
adding boundaries for the ocean
- you don't want to walk into the ocean and drown
- or maybe you're an evil game designer or like hard mode
- you could have bountaries cause the player to be eaten by sharks, stabbed by manta rays, or poisoned by jellyfish here
- or maybe drop an npc that gives your character scuba gear so they can find even more treasures
checkBorders(coordinate) {
// true means you can move
if (coordinate[1] > this.northBoundry)
return true
else if (coordinate[1] < this.southBoundry)
return true
else if (coordinate[0] < this.westBoundry)
return true
else if (coordinate[0] > this.eastBoundry)
return true
else
return false
console.log('boundry check ok');
}
game conditions
- a minimal game has conditions for win or lose
- this drops a random treasure and disaster
- there is a 2nd lose scenario if you run out of energy
let treasureLocation = islandOne.generateCoordinate()
islandOne.useCoordinate(treasureLocation)
const treasure = new Point('treasure', 'islandOne', [treasureLocation[0], treasureLocation[1]], 0);
let disasterLocation = islandOne.generateCoordinate()
islandOne.useCoordinate(disasterLocation)
const disaster = new Point('disaster', 'islandOne', [disasterLocation[0], disasterLocation[1]], 0);
losing energy and food trees
- when you move you lose energy, this is another lose scenario
- finding a fruit tree gives you enerty
- you can cheat by revisiting a fruit tree to pump your energy up to infinity
- I might add a death by overeating option to break that cheat
- or drop a backpack allowing you to carry some food
-
or have trees turn rotton over time and become poisonous, increasing energy drops or other evil effects
-
fruit
const fruit = ['banana', 'orange', 'pear', 'apple', 'almond', 'coconut', 'pineapple', 'cherry']
const fruitDropLocations = []
for (f of fruit) {
const fruitLocation = islandOne.generateCoordinate()
islandOne.useCoordinate(fruitLocation)
let fruitDropName = f + "Drop"
fruitDropName = new Point(f, 'islandOne', [fruitLocation[0], fruitLocation[1]], 0)
fruitDropLocations.push([fruitLocation[0], fruitLocation[1]])
}
- fruit map
let fruitMapLocation = islandOne.generateCoordinate()
islandOne.useCoordinate(fruitMapLocation)
const fruitMap = new Point('fruitMap', 'islandOne', [fruitMapLocation[0], fruitMapLocation[1]], 0)
all of the code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Player</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@4.5.2/dist/solar/bootstrap.min.css"
integrity="sha384-NCwXci5f5ZqlDw+m7FwZSAwboa0svoPPylIW3Nf+GBDsyVum+yArYnaFLE9UDzLd" crossorigin="anonymous">
<style>
canvas {
/* #586e75 #002b36; */
border: 2px solid #268bd2;
background-color: #586e75;
border-radius: 5px;
padding-top: 10px;
}
h1 {
color: #268bd2;
}
h3 {
color: #cb4b16;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-3">
<br>
<div id="narration" class="alert alert-primary" role="alert">
</div>
<hr>
<h3>energy</h3>
<div class="progress">
<div id='energyBar' class="progress-bar bg-warning" role="progressbar" aria-label=""
style="width: 25%;" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<div class="col-6">
<h1>random island adventure</h1>
<canvas id="canvas" width="430" height="430">
</div>
<div class="col-3">
<div class="alert alert-info" id="island">
</div>
<br>
<div class="alert alert-secondary" id="notification">
</div>
<hr>
</div>
</div>
</div>
<script>
window.onload = function () {
var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
// Center
ctx.translate(200, 201);
function drawCartesianPoint(ctx, x, y) {
ctx.fillRect(x, -(y), 4, 4);
}
function drawCartesianText(ctx, x, y, text) {
ctx.fillText(text, x, -(y));
}
class island {
constructor(islandName, x_max, y_max) {
this.islandName = islandName
this.northBoundry = y_max
this.southBoundry = -y_max
this.westBoundry = -x_max
this.eastBoundry = x_max
this.usedCoordinates = [] // each island has a set of used coordinates
}
useCoordinate(coordinate) {
this.usedCoordinates.push(coordinate)
}
validateCoordinate(coordinate) {
// returns true if coordinate is already used
const validation = JSON.stringify(this.usedCoordinates).includes(JSON.stringify(coordinate));
return validation
}
generateCoordinate() {
let randoX = Math.floor(Math.random() * (this.eastBoundry - this.westBoundry + 1) + this.westBoundry)
let randoY = Math.floor(Math.random() * (this.northBoundry - this.southBoundry + 1) + this.southBoundry)
let coordinate = [randoX, randoY]
while (this.validateCoordinate(coordinate)) {
let randoX = Math.floor(Math.random() * (this.eastBoundry - this.westBoundry + 1) + this.westBoundry)
let randoY = Math.floor(Math.random() * (this.northBoundry - this.southBoundry + 1) + this.southBoundry)
coordinate = [randoX, randoY]
}
return coordinate
}
showCoordinates() {
console.log(this.usedCoordinates)
}
checkBorders(coordinate) {
// true means you can move
if (coordinate[1] > this.northBoundry)
return true
else if (coordinate[1] < this.southBoundry)
return true
else if (coordinate[0] < this.westBoundry)
return true
else if (coordinate[0] > this.eastBoundry)
return true
else
return false
console.log('boundry check ok');
}
}
class Point {
static allPoints = [];
constructor(userName, islandName, coordinate, energy) {
this.userName = userName;
this.energy = energy;
this.islandName = islandName;
this.coordinate = coordinate
Point.allPoints.push(this);
}
updateEnergyBar(amount) {
const energyBar = document.getElementById('energyBar')
energyBar.setAttribute('aria-valuenow', '100')
energyBar.style.width = amount + '%';
energyBar.innerHTML = `% ${amount}`
}
adjustEnergy(amount) {
this.energy = this.energy + amount
this.updateEnergyBar(this.energy)
}
somethingIsHere(coordinate) {
const whoIsThat = JSON.stringify(Point.allPoints).includes(JSON.stringify(coordinate));
return whoIsThat
}
updateNarration(text) {
const narrationBox = document.getElementById('narration')
narrationBox.innerHTML = text
}
commonMovement() {
playerOne.adjustEnergy(-1)
if (this.energy == 0) {
updateInterface(`you cannot move, energy level ${this.energy}`)
alert(`you cannot move, energy level ${this.energy}`)
location.reload()
}
updateInterface(`moving ${this.userName} to ${this.coordinate} <br> energy level ${this.energy}`)
drawCartesianText(ctx, this.coordinate[0] * 20, this.coordinate[1] * 20, '*')
if (islandOne.validateCoordinate(this.coordinate)) {
for (let item of Point.allPoints) {
if (JSON.stringify(item).includes(JSON.stringify(this.coordinate))) {
if (item.coordinate == this.coordinate) {
this.updateNarration(`${this.userName} you went backwards`)
} else if (item.userName == 'treasure') {
this.updateNarration(`YOU WON! YOU FOUND THE THE ${item.userName}`)
alert(`you won! you found the the ${item.userName}`)
location.reload()
} else if (item.userName == 'disaster') {
this.updateNarration(`you lose, found the ${item.userName}`)
alert(`you lose, found the ${item.userName}`)
location.reload()
} else if (item.userName == 'treasureMap') {
updateNotification(`you found the ${item.userName} `)
drawCartesianText(ctx, treasure.coordinate[0] * 20, treasure.coordinate[1] * 20, 'T')
} else if (item.userName == 'disasterMap') {
updateNotification(`you found the ${item.userName} `)
drawCartesianText(ctx, disaster.coordinate[0] * 20, disaster.coordinate[1] * 20, 'D')
} else if (item.userName == 'fruitMap') {
updateNotification(`you found the ${item.userName} `)
for (f of fruitDropLocations) {
drawCartesianText(ctx, f[0] * 20, f[1] * 20, 'F')
}
} else {
updateNotification(`you found a ${item.userName} tree`)
playerOne.adjustEnergy(10)
}
}
}
}
}
moveNorth() {
this.coordinate = [this.coordinate[0], this.coordinate[1] += 1]
if (islandOne.checkBorders(this.coordinate)) {
updateInterface('you reached the north beach')
this.coordinate = [this.coordinate[0], this.coordinate[1] -= 1]
} else {
this.commonMovement()
}
}
moveSouth() {
this.coordinate[1] -= 1
if (islandOne.checkBorders(this.coordinate)) {
updateInterface('you reached the south beach')
this.coordinate[1] += 1
} else {
this.commonMovement()
}
}
moveWest() {
this.coordinate[0] -= 1
if (islandOne.checkBorders(this.coordinate)) {
updateInterface('you reached the west beach')
this.coordinate[0] += 1
} else {
this.commonMovement()
}
}
moveEast() {
this.coordinate[0] += 1
if (islandOne.checkBorders(this.coordinate)) {
updateInterface('you reached the east beach')
this.coordinate[0] -= 1
} else {
this.commonMovement()
}
}
}
function checkKey(e) {
randoMessage(chipDiamond)
// console.log(e.keyCode);
switch (e.keyCode) {
case 72:
// case 37:
playerOne.moveWest()
break;
case 75:
// case 38:
playerOne.moveNorth()
break;
case 76:
// case 39:
playerOne.moveEast()
break;
case 74:
// case 40:
playerOne.moveSouth()
break;
}
}
// chipDiamond = ['look at THIS', 'LOOK AT THAT', 'just look at it', 'would you look at this', 'well just look at it']
chipDiamond = ['watch out for disasters', 'find the treasure to win', 'fruit gives you energy', 'run out of energy and you lose', 'find the disaster map', 'there is a treasure map', 'find the fruit map']
function randoMessage(narrations) {
var narrate = narrations[Math.floor(Math.random() * narrations.length)];
playerOne.updateNarration(narrate)
}
function updateInterface(message) {
const islandDisplay = document.getElementById('island')
islandDisplay.innerHTML = message
}
function updateNotification(message) {
const islandDisplay = document.getElementById('notification')
islandDisplay.innerHTML = message
}
function updateIsland(x, y, message) {
drawCartesianText(ctx, x, y, message)
}
document.onkeydown = checkKey;
const islandOne = new island('islandOne', 10, 10)
const playerOne = new Point('player_one', 'islandOne', [0, 0], 100);
islandOne.useCoordinate([0, 0])
let narrate = welcome = "hi I'm Chip Daimond I'll narrate your game, LOOK AT THAT! you woke up hungover on this random island - use hjkl to explore, hopefully you find the treasure not the disaster..."
playerOne.updateNarration(narrate)
updateInterface('starting at location 0,0')
playerOne.updateEnergyBar(playerOne.energy)
drawCartesianText(ctx, 0, 0, '*')
let treasureLocation = islandOne.generateCoordinate()
islandOne.useCoordinate(treasureLocation)
const treasure = new Point('treasure', 'islandOne', [treasureLocation[0], treasureLocation[1]], 0);
// console.log(treasureLocation[0], treasureLocation[1], 'treasure')
// drawCartesianText(ctx, treasureLocation[0] * 20, treasureLocation[1] * 20, 'treasure')
let disasterLocation = islandOne.generateCoordinate()
islandOne.useCoordinate(disasterLocation)
const disaster = new Point('disaster', 'islandOne', [disasterLocation[0], disasterLocation[1]], 0);
// console.log(disasterLocation[0], disasterLocation[1], 'disaster')
// drawCartesianText(ctx, disasterLocation[0] * 20, disasterLocation[1] * 20, 'disaster')
let treasureMapLocation = islandOne.generateCoordinate()
islandOne.useCoordinate(treasureMapLocation)
const treasureMap = new Point('treasureMap', 'islandOne', [treasureMapLocation[0], treasureMapLocation[1]], 0);
let disasterMapLocation = islandOne.generateCoordinate()
islandOne.useCoordinate(disasterMapLocation)
const disasterMap = new Point('disasterMap', 'islandOne', [disasterMapLocation[0], disasterMapLocation[1]], 0);
let fruitMapLocation = islandOne.generateCoordinate()
islandOne.useCoordinate(fruitMapLocation)
const fruitMap = new Point('fruitMap', 'islandOne', [fruitMapLocation[0], fruitMapLocation[1]], 0)
const fruit = ['banana', 'orange', 'pear', 'apple', 'almond', 'coconut', 'pineapple', 'cherry']
const fruitDropLocations = []
for (f of fruit) {
const fruitLocation = islandOne.generateCoordinate()
islandOne.useCoordinate(fruitLocation)
let fruitDropName = f + "Drop"
fruitDropName = new Point(f, 'islandOne', [fruitLocation[0], fruitLocation[1]], 0)
fruitDropLocations.push([fruitLocation[0], fruitLocation[1]])
}
function cheat() {
for (p of Point.allPoints) {
console.log(p.userName, p.coordinate[0], p.coordinate[1])
}
console.log(fruitDropLocations)
}
};
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.min.js"
integrity="sha384-ODmDIVzN+pFdexxHEHFBQH3/9/vQ9uori45z4JjnFsRydbmQbmL5t1tQ0culUzyK"
crossorigin="anonymous"></script>
</body>
</html>
react
wip: I'm currently on the full stack js course
I haven't started react native which is for mobile
I might branch this and add react
Last update:
February 5, 2023
Created: February 4, 2023
Created: February 4, 2023