init
BIN
assets/audio/Amphibians.mp3
Normal file
BIN
assets/audio/Asteroids.mp3
Normal file
BIN
assets/audio/Down Polypore Wood.mp3
Normal file
BIN
assets/audio/Ritual Dance of The Cavern Walls Cult.mp3
Normal file
BIN
assets/audio/Sneaking Out Alone.mp3
Normal file
BIN
assets/audio/Tatari.mp3
Normal file
BIN
assets/audio/buranko.mp3
Normal file
BIN
assets/audio/new jersey again.mp3
Normal file
272
assets/css/style.css
Normal file
|
|
@ -0,0 +1,272 @@
|
||||||
|
body {
|
||||||
|
background-color: #E7D5B1;
|
||||||
|
/* background-color: #8EA7C8; */
|
||||||
|
background-image: url("https://www.transparenttextures.com/patterns/asfalt-dark.png");
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outer-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 720px;
|
||||||
|
margin: auto;
|
||||||
|
margin: 0 auto 0 auto;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
position: relative;
|
||||||
|
z-index: -1;
|
||||||
|
max-width: 720px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #f8f8ff;
|
||||||
|
border: inset 8px black;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
margin: 20px;
|
||||||
|
min-width: 680px;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.enter-indicator {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 14px;
|
||||||
|
bottom: 0px;
|
||||||
|
right: 0px;
|
||||||
|
margin: 5px;
|
||||||
|
animation: blink 3s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.window-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
margin: 20px;
|
||||||
|
border: solid 3px #dbe0e7;
|
||||||
|
margin-top: 10px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-bar {
|
||||||
|
position: relative;
|
||||||
|
background: #57C785;
|
||||||
|
background: linear-gradient(90deg, rgba(87, 199, 133, 1) 19%, rgba(191, 83, 237, 1) 100%);
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 20px;
|
||||||
|
border-bottom: solid 3px #dbe0e7;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
padding: 3px;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
border-radius: 0%;
|
||||||
|
border-top: white;
|
||||||
|
border-left: white;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#term {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
display: none;
|
||||||
|
background-color: #272E33;
|
||||||
|
color: #A7C080;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-container label {
|
||||||
|
margin-right: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prompt-container input[type="text"] {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#term input {
|
||||||
|
float: left;
|
||||||
|
color: #A7C080;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 18px;
|
||||||
|
min-width: 90%;
|
||||||
|
background-color: #272E33;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#term input:focus {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #272E33;
|
||||||
|
border: 0;
|
||||||
|
outline:none!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.music-inner-container {
|
||||||
|
background: #a3acbe;
|
||||||
|
position: relative;
|
||||||
|
height: inherit;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
overflow: auto!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#album-thumb {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audio-controls {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#album-thumb img {
|
||||||
|
border: solid 3px #dbe0e7;
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 200px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#games-container {
|
||||||
|
background: #a3acbe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.games-inner-container {
|
||||||
|
position: relative;
|
||||||
|
height: inherit;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
overflow: auto!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.games-inner-container img {
|
||||||
|
width: 200px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.games-inner-container h3 {
|
||||||
|
margin: 0px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-game-container {
|
||||||
|
display: flex;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.single-game-container:hover {
|
||||||
|
border: solid 4px black;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-thumbnail-container {
|
||||||
|
flex: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-description-container {
|
||||||
|
flex: 75%;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix::after {
|
||||||
|
content: "";
|
||||||
|
clear: both;
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
border: 1px solid #dddddd;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background-color: #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes glow {
|
||||||
|
0% { box-shadow: 0 0 -28px 13px #ff0000; }
|
||||||
|
40% { box-shadow: 0 0 15px 13px #ff0000; }
|
||||||
|
60% { box-shadow: 0 0 15px 13px #ff0000; }
|
||||||
|
100% { box-shadow: 0 0 -28px 13px #ff0000; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.indicator {
|
||||||
|
display: none;
|
||||||
|
background-color: yellow;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 0 auto;
|
||||||
|
height: 0px;
|
||||||
|
width: 0px;
|
||||||
|
animation: glow 3s infinite;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
#resume-indicator {
|
||||||
|
top: 310px;
|
||||||
|
left: 368px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#portfolio-indicator {
|
||||||
|
top: 20px;
|
||||||
|
left: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#computer-indicator {
|
||||||
|
top: 260px;
|
||||||
|
left: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tv-indicator {
|
||||||
|
top: 20px;
|
||||||
|
left: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#guitar-indicator {
|
||||||
|
top: 570px;
|
||||||
|
left: 60px;
|
||||||
|
}
|
||||||
BIN
assets/img/endesga-36-1x.png
Normal file
|
After Width: | Height: | Size: 213 B |
BIN
assets/img/sprites/sprite-map-1.ase
Normal file
BIN
assets/img/sprites/sprite-map-1.jpg
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
assets/img/sprites/sprite-map-1.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
assets/img/sprites/test-sprite.png
Normal file
|
After Width: | Height: | Size: 134 B |
BIN
assets/img/thumbnails/amphi.jpg
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
assets/img/thumbnails/asteroids.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
assets/img/thumbnails/nj.jpg
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
assets/img/thumbnails/pong.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
assets/img/thumbnails/scoundrel.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
assets/img/thumbnails/shrinkinminkin.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
assets/img/thumbnails/sokobanya.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/img/thumbnails/tatari.jpg
Normal file
|
After Width: | Height: | Size: 72 KiB |
144
assets/js/app.js
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
var _listener
|
||||||
|
var TO_RADIANS = Math.PI/180;
|
||||||
|
|
||||||
|
var terminalMode = false
|
||||||
|
var spriteSize = 16 * 5
|
||||||
|
var spriteMap = new Image()
|
||||||
|
spriteMap.src = './assets/img/sprites/sprite-map-1.png'
|
||||||
|
|
||||||
|
var canvasOffset = 9
|
||||||
|
var canvas = document.getElementById("canvas")
|
||||||
|
var ctx = canvas.getContext("2d")
|
||||||
|
|
||||||
|
canvas.width = spriteSize * canvasOffset
|
||||||
|
canvas.height = canvas.width
|
||||||
|
|
||||||
|
var audio = null
|
||||||
|
|
||||||
|
var terminal = new Terminal()
|
||||||
|
var games = new Games()
|
||||||
|
var music = new Music()
|
||||||
|
var dialog = new Dialog()
|
||||||
|
var indicator = new Indicator()
|
||||||
|
var player = new Player(1, 1, 'down')
|
||||||
|
|
||||||
|
window.addEventListener("resize", function() {
|
||||||
|
terminal.resize();
|
||||||
|
games.resize();
|
||||||
|
})
|
||||||
|
|
||||||
|
function renderRoom() {
|
||||||
|
// render wall
|
||||||
|
for (let i = 0; i < canvasOffset; i++) {
|
||||||
|
new Renderer(2, 1, i, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render Wood Floor
|
||||||
|
for (let i = 0; i < canvasOffset; i++) {
|
||||||
|
for (let j = 1; j < canvasOffset; j++) {
|
||||||
|
new Renderer(1, 0, i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render Rug row 1
|
||||||
|
new Renderer(2, 0, 1, 2)
|
||||||
|
new Renderer(3, 0, 2, 2)
|
||||||
|
new Renderer(3, 0, 3, 2)
|
||||||
|
new Renderer(3, 0, 4, 2)
|
||||||
|
new Renderer(3, 0, 5, 2)
|
||||||
|
new Renderer(3, 0, 6, 2)
|
||||||
|
new Renderer(2, 0, 7, 2, 90)
|
||||||
|
// Render Rug row 1
|
||||||
|
new Renderer(3, 0, 1, 3, 270)
|
||||||
|
new Renderer(4, 0, 2, 3)
|
||||||
|
new Renderer(4, 0, 3, 3)
|
||||||
|
new Renderer(5, 0, 4, 3)
|
||||||
|
new Renderer(5, 0, 5, 3)
|
||||||
|
new Renderer(5, 0, 6, 3)
|
||||||
|
new Renderer(3, 0, 7, 3, 90)
|
||||||
|
// Render Rug row 2
|
||||||
|
new Renderer(3, 0, 1, 4, 270)
|
||||||
|
new Renderer(5, 0, 2, 4)
|
||||||
|
new Renderer(5, 0, 3, 4)
|
||||||
|
new Renderer(5, 0, 4, 4)
|
||||||
|
new Renderer(5, 0, 5, 4)
|
||||||
|
new Renderer(5, 0, 6, 4)
|
||||||
|
new Renderer(3, 0, 7, 4, 90)
|
||||||
|
// Render Rug row 3
|
||||||
|
new Renderer(3, 0, 1, 5, 270)
|
||||||
|
new Renderer(5, 0, 2, 5)
|
||||||
|
new Renderer(5, 0, 3, 5)
|
||||||
|
new Renderer(5, 0, 4, 5)
|
||||||
|
new Renderer(5, 0, 5, 5)
|
||||||
|
new Renderer(5, 0, 6, 5)
|
||||||
|
new Renderer(3, 0, 7, 5, 90)
|
||||||
|
// Render Rug row
|
||||||
|
new Renderer(3, 0, 1, 6, 270)
|
||||||
|
new Renderer(5, 0, 2, 6)
|
||||||
|
new Renderer(5, 0, 3, 6)
|
||||||
|
new Renderer(5, 0, 4, 6)
|
||||||
|
new Renderer(5, 0, 5, 6)
|
||||||
|
new Renderer(4, 0, 6, 6)
|
||||||
|
new Renderer(3, 0, 7, 6, 90)
|
||||||
|
// Render Rug row 4
|
||||||
|
new Renderer(2, 0, 1, 7, 270)
|
||||||
|
new Renderer(3, 0, 2, 7, 180)
|
||||||
|
new Renderer(3, 0, 3, 7, 180)
|
||||||
|
new Renderer(3, 0, 4, 7, 180)
|
||||||
|
new Renderer(3, 0, 5, 7, 180)
|
||||||
|
new Renderer(3, 0, 6, 7, 180)
|
||||||
|
new Renderer(2, 0, 7, 7, 180)
|
||||||
|
|
||||||
|
// render TV
|
||||||
|
new Renderer(6, 0, 7, 0)
|
||||||
|
new Renderer(7, 0, 8, 0)
|
||||||
|
new Renderer(6, 1, 7, 1)
|
||||||
|
new Renderer(7, 1, 8, 1)
|
||||||
|
|
||||||
|
// render bookshelves
|
||||||
|
new Renderer(1, 1, 0, 0)
|
||||||
|
new Renderer(1, 1, 1, 0)
|
||||||
|
|
||||||
|
// render computer
|
||||||
|
new Renderer(0, 8, 2, 4)
|
||||||
|
new Renderer(1, 8, 3, 4)
|
||||||
|
new Renderer(2, 8, 4, 4)
|
||||||
|
new Renderer(3, 8, 5, 4)
|
||||||
|
|
||||||
|
// render maomao
|
||||||
|
new Renderer(4, 1, 6, 6)
|
||||||
|
|
||||||
|
// render window
|
||||||
|
new Renderer(3, 1, 4, 0)
|
||||||
|
|
||||||
|
//render player
|
||||||
|
player.render()
|
||||||
|
|
||||||
|
// render second half of computer
|
||||||
|
new Renderer(0, 7, 2, 3)
|
||||||
|
new Renderer(1, 7, 3, 3)
|
||||||
|
new Renderer(2, 7, 4, 3)
|
||||||
|
new Renderer(3, 7, 5, 3)
|
||||||
|
|
||||||
|
// render guitar
|
||||||
|
new Renderer(3, 2, 0, 7)
|
||||||
|
new Renderer(3, 3, 0, 8)
|
||||||
|
new Renderer(4, 2, 1, 8)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.startIntroDialogSequence()
|
||||||
|
|
||||||
|
function gameLoop() {
|
||||||
|
renderRoom()
|
||||||
|
document.removeEventListener("keydown", _listener);
|
||||||
|
if (terminalMode) {
|
||||||
|
terminal.getKeystroke();
|
||||||
|
} else {
|
||||||
|
this.getKeystroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spriteMap.onload = function() {
|
||||||
|
setInterval(gameLoop, 33);
|
||||||
|
}
|
||||||
33
assets/js/constants.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
var boundaries = [
|
||||||
|
// computer
|
||||||
|
[2, 3],
|
||||||
|
[3, 3],
|
||||||
|
[4, 3],
|
||||||
|
[5, 3],
|
||||||
|
//guitar
|
||||||
|
[1, 7],
|
||||||
|
[0, 7],
|
||||||
|
//maomao
|
||||||
|
[6, 5]
|
||||||
|
]
|
||||||
|
|
||||||
|
var computerInteractionZones = [
|
||||||
|
[2, 4],
|
||||||
|
[3, 4]
|
||||||
|
]
|
||||||
|
|
||||||
|
var gamesInteractionZones = [
|
||||||
|
[7, 0],
|
||||||
|
[8, 0]
|
||||||
|
]
|
||||||
|
|
||||||
|
var musicInteractionZones = [
|
||||||
|
[0, 6],
|
||||||
|
[1, 6],
|
||||||
|
[2, 7]
|
||||||
|
]
|
||||||
|
|
||||||
|
var resumeInteractionZones = [
|
||||||
|
[4, 4],
|
||||||
|
[4, 2]
|
||||||
|
]
|
||||||
54
assets/js/dialog.js
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
class Dialog {
|
||||||
|
constructor() {
|
||||||
|
this.dialogElem = document.getElementById('dialog')
|
||||||
|
this.dialogTextElem = document.getElementById('dialog-text')
|
||||||
|
|
||||||
|
this.currentDialogIndex = 0
|
||||||
|
this.dialogOrder = ['intro', 'controls', 'resume']
|
||||||
|
|
||||||
|
this.dialogMap = {
|
||||||
|
'intro': 'Hello! Welcome to my Interactive Portfolio!<br>Press <b>ENTER</b> to continue, or click the links below for quick access to my Resume & Projects.',
|
||||||
|
'controls': 'Use <b>ARROW KEYS</b> or <b>WASD</b> or <b>VIM</b> keys (HJKL) to move around. <br>Press the <b>ENTER</b> key to interact with what you see on the screen.<br><br>',
|
||||||
|
'resume': 'Walk over to the <b>DESK</b> and click on my <b>RESUME</b>.<br> The <b>RESUME</b> is marked by the flashing red indicator light<br>.',
|
||||||
|
'portfolio': 'Thanks for looking at my Resume!<br>Check out my <b>PORTFOLIO</b> by clicking on the <b>BOOKSHELF</br>.'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resumeIndicator = document.getElementById('resume-indicator')
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogListener(event) {
|
||||||
|
console.log(event)
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.currentTarget.self.startIntroDialogSequence()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startIntroDialogSequence() {
|
||||||
|
document.removeEventListener("keydown", this.dialogListener);
|
||||||
|
console.log('hit')
|
||||||
|
if (this.currentDialogIndex > this.dialogOrder.length - 1) {
|
||||||
|
this.dialogElem.style.display = 'none'
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var dialog = this.dialogMap[this.dialogOrder[this.currentDialogIndex]]
|
||||||
|
this.display(dialog)
|
||||||
|
|
||||||
|
if (this.dialogOrder[this.currentDialogIndex] in indicator.indicatorMap) {
|
||||||
|
indicator.show(this.dialogOrder[this.currentDialogIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentDialogIndex++
|
||||||
|
|
||||||
|
var self = this
|
||||||
|
document.addEventListener('keydown', this.dialogListener)
|
||||||
|
document.self = self
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
display(dialog) {
|
||||||
|
this.dialogElem.style.display = 'block'
|
||||||
|
this.dialogTextElem.innerHTML = this.dialogMap[this.dialogOrder[this.currentDialogIndex]]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
23
assets/js/games.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
class Games {
|
||||||
|
constructor() {
|
||||||
|
this.gamesContainer = document.getElementById("games-container")
|
||||||
|
this.gamesInnerContainer = document.getElementById("games")
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.resize()
|
||||||
|
this.gamesContainer.style.display = 'block'
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.gamesContainer.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
resize() {
|
||||||
|
this.gamesContainer.style.width = canvas.getBoundingClientRect().width - 40 + 'px';
|
||||||
|
this.gamesContainer.style.height = canvas.getBoundingClientRect().height - 40 + 'px';
|
||||||
|
this.gamesContainer.style.minHeight = canvas.getBoundingClientRect().width - 40 + 'px';
|
||||||
|
|
||||||
|
this.gamesInnerContainer.style.height = canvas.getBoundingClientRect().height - 77 + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
33
assets/js/indicator.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
class Indicator {
|
||||||
|
constructor() {
|
||||||
|
this.resumeIndicator = document.getElementById('resume-indicator')
|
||||||
|
this.portfolioIndicator = document.getElementById('portfolio-indicator')
|
||||||
|
this.computerIndicator = document.getElementById('computer-indicator')
|
||||||
|
this.tvIndicator = document.getElementById('tv-indicator')
|
||||||
|
this.guitarIndicator = document.getElementById('guitar-indicator')
|
||||||
|
|
||||||
|
this.resumeIndicatorShown = false
|
||||||
|
this.portfolioIndicatorShown = false
|
||||||
|
this.computerIndicatorShown = false
|
||||||
|
this.tvIndicatorShown = false
|
||||||
|
this.guitarIndicatorShown = false
|
||||||
|
|
||||||
|
this.indicatorMap = {
|
||||||
|
'resume': this.resumeIndicator,
|
||||||
|
'portfolio': this.portfolioIndicator,
|
||||||
|
'computer': this.computerIndicator,
|
||||||
|
'tv': this.tvIndicator,
|
||||||
|
'guitar': this.guitarIndicator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show(indicatorName) {
|
||||||
|
console.log(indicatorName)
|
||||||
|
this.indicatorMap[indicatorName].style.display = 'block'
|
||||||
|
}
|
||||||
|
|
||||||
|
hide(indicatorName) {
|
||||||
|
this.indicatorMap[indicatorName].style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
61
assets/js/keystroke.js
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
function getKeystroke() {
|
||||||
|
_listener = function (event) {
|
||||||
|
if (
|
||||||
|
["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].indexOf(
|
||||||
|
event.code,
|
||||||
|
) > -1
|
||||||
|
) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "h" || event.key === "a" || event.key === "ArrowLeft") {
|
||||||
|
player.move('left')
|
||||||
|
} else if (
|
||||||
|
event.key === "j" ||
|
||||||
|
event.key === "s" ||
|
||||||
|
event.key === "ArrowDown"
|
||||||
|
) {
|
||||||
|
player.move('down')
|
||||||
|
} else if (
|
||||||
|
event.key === "k" ||
|
||||||
|
event.key === "w" ||
|
||||||
|
event.key === "ArrowUp"
|
||||||
|
) {
|
||||||
|
player.move('up')
|
||||||
|
} else if (
|
||||||
|
event.key === "l" ||
|
||||||
|
event.key === "d" ||
|
||||||
|
event.key === "ArrowRight"
|
||||||
|
) {
|
||||||
|
player.move('right')
|
||||||
|
} else if (
|
||||||
|
event.key === "Enter" &&
|
||||||
|
!terminal.active
|
||||||
|
) {
|
||||||
|
var playerPosition = [player.canvasX, player.canvasY]
|
||||||
|
if (player.direction === 'up') {
|
||||||
|
if (computerInteractionZones.some(a => playerPosition.every((v, i) => v === a[i]))) {
|
||||||
|
terminal.show();
|
||||||
|
}
|
||||||
|
if (gamesInteractionZones.some(a => playerPosition.every((v, i) => v === a[i]))) {
|
||||||
|
games.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.direction === 'down' || player.direction === 'left') {
|
||||||
|
if (musicInteractionZones.some(a => playerPosition.every((v, i) => v === a[i]))) {
|
||||||
|
music.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.direction === 'down' || player.direction === 'up') {
|
||||||
|
if (resumeInteractionZones.some(a => playerPosition.every((v, i) => v === a[i]))) {
|
||||||
|
indicator.hide('resume');
|
||||||
|
indicator.resumeIndicatorShown = true;
|
||||||
|
// show resume
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener("keydown", _listener);
|
||||||
|
}
|
||||||
116
assets/js/music.js
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
class Music extends Window {
|
||||||
|
constructor() {
|
||||||
|
super('music')
|
||||||
|
this.albumCoverElem = document.getElementById("album-art")
|
||||||
|
this.currentlyPlayingElem = document.getElementById("currently-playing")
|
||||||
|
this.currentSong = null
|
||||||
|
|
||||||
|
this.songOrder = [
|
||||||
|
'Down Polypore Wood',
|
||||||
|
'Sneaking Out Alone',
|
||||||
|
'Ritual Dance of The Cavern Walls Cult',
|
||||||
|
'Tatari',
|
||||||
|
'Buranko',
|
||||||
|
'New Jersey Again',
|
||||||
|
'Asteroids'
|
||||||
|
]
|
||||||
|
|
||||||
|
this.songMap = {
|
||||||
|
'Down Polypore Wood': {
|
||||||
|
'audio': 'Down Polypore Wood.mp3',
|
||||||
|
'thumb': 'shrinkinminkin.jpg'
|
||||||
|
},
|
||||||
|
'Sneaking Out Alone': {
|
||||||
|
'audio': 'Sneaking Out Alone.mp3',
|
||||||
|
'thumb': 'shrinkinminkin.jpg'
|
||||||
|
},
|
||||||
|
'Ritual Dance of The Cavern Walls Cult': {
|
||||||
|
'audio': 'Ritual Dance of The Cavern Walls Cult.mp3',
|
||||||
|
'thumb': 'shrinkinminkin.jpg'
|
||||||
|
},
|
||||||
|
'Tatari': {
|
||||||
|
'audio': 'Tatari.mp3',
|
||||||
|
'thumb': 'tatari.jpg'
|
||||||
|
},
|
||||||
|
'Buranko': {
|
||||||
|
'audio': 'buranko.mp3',
|
||||||
|
'thumb': 'tatari.jpg'
|
||||||
|
},
|
||||||
|
'New Jersey Again': {
|
||||||
|
'audio': 'new jersey again.mp3',
|
||||||
|
'thumb': 'nj.jpg'
|
||||||
|
},
|
||||||
|
'Asteroids': {
|
||||||
|
'audio': 'Asteroids.mp3',
|
||||||
|
'thumb': 'amphi.jpg'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resume() {
|
||||||
|
if (!this.currentSong) {
|
||||||
|
this.play(this.songOrder[0])
|
||||||
|
}
|
||||||
|
audio.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
play(songName=null) {
|
||||||
|
if (this.currentSong) {
|
||||||
|
audio.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!songName && !this.currentSong) {
|
||||||
|
songName = this.songOrder[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentSong = songName
|
||||||
|
audio = new Audio(this.getSongFilepath(songName))
|
||||||
|
audio.play()
|
||||||
|
|
||||||
|
var self = this
|
||||||
|
audio.addEventListener('ended', function() {
|
||||||
|
self.next(self.currentSong)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.albumCoverElem.src = this.getAlbumArtFilepath(songName)
|
||||||
|
this.currentlyPlayingElem.innerHTML = this.currentSong
|
||||||
|
}
|
||||||
|
|
||||||
|
getSongFilepath(songName) {
|
||||||
|
return `./assets/audio/${this.songMap[songName]['audio']}`
|
||||||
|
}
|
||||||
|
|
||||||
|
getAlbumArtFilepath(songName) {
|
||||||
|
return `./assets/img/thumbnails/${this.songMap[songName]['thumb']}`
|
||||||
|
}
|
||||||
|
|
||||||
|
next() {
|
||||||
|
if (this.currentSong) {
|
||||||
|
var currentSongIndex = this.songOrder.indexOf(this.currentSong)
|
||||||
|
var nextSongIndex = currentSongIndex + 1
|
||||||
|
|
||||||
|
if (nextSongIndex > this.songOrder.length - 1) {
|
||||||
|
nextSongIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
this.play(this.songOrder[nextSongIndex])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prev() {
|
||||||
|
if (this.currentSong) {
|
||||||
|
var currentSongIndex = this.songOrder.indexOf(this.currentSong)
|
||||||
|
var nextSongIndex = currentSongIndex - 1
|
||||||
|
|
||||||
|
if (nextSongIndex < 0) {
|
||||||
|
nextSongIndex = this.songOrder.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
this.play(this.songOrder[nextSongIndex])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
audio.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
72
assets/js/player.js
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
class Player {
|
||||||
|
constructor(x, y, direction) {
|
||||||
|
this.canvasX = x
|
||||||
|
this.canvasY = y
|
||||||
|
|
||||||
|
this.direction = direction
|
||||||
|
|
||||||
|
this.spriteWidth = spriteSize
|
||||||
|
this.spriteHeight = spriteSize * 2
|
||||||
|
|
||||||
|
this.animationCycle = [0, 1, 0, 2]
|
||||||
|
this.animationCycleIndex = 0
|
||||||
|
this.spriteMapXAnimationOffset = 0
|
||||||
|
|
||||||
|
this.directionSpriteYMap = {
|
||||||
|
'down': 2,
|
||||||
|
'right': 4,
|
||||||
|
'up': 6,
|
||||||
|
'left': 8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
move(direction) {
|
||||||
|
this.direction = direction
|
||||||
|
var nextPosition
|
||||||
|
if (direction === 'left' && this.canvasX - 1 >= 0) {
|
||||||
|
nextPosition = [this.canvasX - 1, this.canvasY]
|
||||||
|
if (!this.isBoundary(nextPosition)) {
|
||||||
|
this.canvasX -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direction === 'right' && this.canvasX + 1 < canvas.width/spriteSize) {
|
||||||
|
nextPosition = [this.canvasX + 1, this.canvasY]
|
||||||
|
if (!this.isBoundary(nextPosition)) {
|
||||||
|
this.canvasX += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direction === 'up' && this.canvasY - 1 >= 0) {
|
||||||
|
nextPosition = [this.canvasX, this.canvasY - 1]
|
||||||
|
if (!this.isBoundary(nextPosition)) {
|
||||||
|
this.canvasY -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (direction === 'down' && this.canvasY + 2 < canvas.height/spriteSize){
|
||||||
|
nextPosition = [this.canvasX, this.canvasY + 1]
|
||||||
|
if (!this.isBoundary(nextPosition)) {
|
||||||
|
this.canvasY += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isBoundary(nextPosition) {
|
||||||
|
return boundaries.some(a => nextPosition.every((v, i) => v === a[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this.spriteMapX = this.spriteWidth * 5
|
||||||
|
this.spriteMapY = this.directionSpriteYMap[this.direction] * spriteSize
|
||||||
|
|
||||||
|
ctx.drawImage(
|
||||||
|
spriteMap,
|
||||||
|
this.spriteMapX + this.spriteMapXAnimationOffset,
|
||||||
|
this.spriteMapY,
|
||||||
|
this.spriteWidth,
|
||||||
|
this.spriteHeight,
|
||||||
|
spriteSize * this.canvasX,
|
||||||
|
spriteSize * this.canvasY,
|
||||||
|
this.spriteWidth,
|
||||||
|
this.spriteHeight,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
23
assets/js/portfolio.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
class Portfolio {
|
||||||
|
constructor() {
|
||||||
|
this.container = document.getElementById("portfolio-container")
|
||||||
|
this.innerContainer = document.getElementById("portfolio")
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.resize()
|
||||||
|
this.container.style.display = 'block'
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.container.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
resize() {
|
||||||
|
this.container.style.width = canvas.getBoundingClientRect().width - 40 + 'px';
|
||||||
|
this.container.style.height = canvas.getBoundingClientRect().height - 40 + 'px';
|
||||||
|
this.container.style.minHeight = canvas.getBoundingClientRect().width - 40 + 'px';
|
||||||
|
|
||||||
|
this.innerContainer.style.height = canvas.getBoundingClientRect().height - 77 + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
52
assets/js/renderer.js
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
class Renderer {
|
||||||
|
constructor(spriteX, spriteY, canvasX, canvasY, rotation=0) {
|
||||||
|
this.spriteX = spriteX
|
||||||
|
this.spriteY = spriteY
|
||||||
|
this.canvasX = canvasX
|
||||||
|
this.canvasY = canvasY
|
||||||
|
this.rotation = rotation
|
||||||
|
|
||||||
|
if (this.rotation === 0) {
|
||||||
|
this.render()
|
||||||
|
} else {
|
||||||
|
this.rotateRender()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
ctx.drawImage(
|
||||||
|
spriteMap,
|
||||||
|
spriteSize * this.spriteX,
|
||||||
|
spriteSize * this.spriteY,
|
||||||
|
spriteSize,
|
||||||
|
spriteSize,
|
||||||
|
spriteSize * this.canvasX,
|
||||||
|
spriteSize * this.canvasY,
|
||||||
|
spriteSize,
|
||||||
|
spriteSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
rotateRender() {
|
||||||
|
ctx.save()
|
||||||
|
|
||||||
|
ctx.translate(
|
||||||
|
spriteSize * this.canvasX + (spriteSize / 2),
|
||||||
|
spriteSize * this.canvasY + (spriteSize / 2)
|
||||||
|
)
|
||||||
|
ctx.rotate(this.rotation * TO_RADIANS)
|
||||||
|
|
||||||
|
ctx.drawImage(
|
||||||
|
spriteMap,
|
||||||
|
spriteSize * this.spriteX,
|
||||||
|
spriteSize * this.spriteY,
|
||||||
|
spriteSize,
|
||||||
|
spriteSize,
|
||||||
|
-(spriteSize/2),
|
||||||
|
-(spriteSize/2),
|
||||||
|
spriteSize,
|
||||||
|
spriteSize)
|
||||||
|
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
}
|
||||||
5
assets/js/resume.js
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
class Resume extends Window {
|
||||||
|
constructor() {
|
||||||
|
super('resume')
|
||||||
|
}
|
||||||
|
}
|
||||||
197
assets/js/terminal.js
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
class Terminal {
|
||||||
|
constructor() {
|
||||||
|
this.terminalElem = document.getElementById("term")
|
||||||
|
this.terminalContainerElem = document.getElementById("term-container")
|
||||||
|
this.promptContainerElem = document.getElementById("prompt-container")
|
||||||
|
this.promptsAndResponses = []
|
||||||
|
this.commands = []
|
||||||
|
this.commandPosition = this.commands.length
|
||||||
|
this.currentPrompt = null
|
||||||
|
this.active = false;
|
||||||
|
this.initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
if (!this.initialized) {
|
||||||
|
this.initialized = true;
|
||||||
|
this.initialize()
|
||||||
|
} else {
|
||||||
|
this.terminalElem.style.display = "block"
|
||||||
|
this.terminalContainerElem.style.display = "block"
|
||||||
|
if (this.currentPrompt) {
|
||||||
|
this.currentPrompt.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
terminalMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
hide () {
|
||||||
|
this.terminalElem.style.display = "none"
|
||||||
|
this.terminalContainerElem.style.display = "none"
|
||||||
|
terminalMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
createPrompt() {
|
||||||
|
this.commandPosition = this.commands.length
|
||||||
|
|
||||||
|
var promptContainerElem = document.createElement("div")
|
||||||
|
promptContainerElem.classList.add("prompt-container")
|
||||||
|
|
||||||
|
var label = document.createElement("label")
|
||||||
|
label.innerHTML = '>>>'
|
||||||
|
|
||||||
|
var prompt = document.createElement("input")
|
||||||
|
prompt.setAttribute("autofocus", true)
|
||||||
|
|
||||||
|
var self = this
|
||||||
|
prompt.addEventListener("keypress", function(event) {
|
||||||
|
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
if (terminalMode && this.value !== ''){
|
||||||
|
event.preventDefault();
|
||||||
|
this.disabled = true;
|
||||||
|
self.handlePrompt(this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var clearFixDiv = document.createElement("div")
|
||||||
|
clearFixDiv.classList.add("clearfix")
|
||||||
|
|
||||||
|
promptContainerElem.appendChild(label);
|
||||||
|
promptContainerElem.appendChild(prompt);
|
||||||
|
this.terminalElem.appendChild(promptContainerElem);
|
||||||
|
|
||||||
|
prompt.focus()
|
||||||
|
this.currentPrompt = prompt
|
||||||
|
this.promptsAndResponses.push(promptContainerElem)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.resize()
|
||||||
|
this.show();
|
||||||
|
this.createPrompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
resize() {
|
||||||
|
this.terminalElem.style.width = canvas.getBoundingClientRect().width - 40 + 'px';
|
||||||
|
this.terminalElem.style.height = canvas.getBoundingClientRect().height - 80 + 'px';
|
||||||
|
this.terminalElem.style.minHeight = canvas.getBoundingClientRect().width - 80 + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePrompt(promptText) {
|
||||||
|
this.commands.push(promptText);
|
||||||
|
this.determineResult(promptText);
|
||||||
|
this.createPrompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
determineResult(promptText) {
|
||||||
|
var returnText
|
||||||
|
if (promptText === 'help') {
|
||||||
|
returnText = `Documented Commands:<br>
|
||||||
|
========================================<br>
|
||||||
|
help     Shows the help screen<br>
|
||||||
|
about    Provides background of the terminal<br>
|
||||||
|
clear    Clears the terminal<br>
|
||||||
|
echo     Display a line of text<br>
|
||||||
|
exit     Exits the terminal<br>
|
||||||
|
history  Prints command history<br>
|
||||||
|
<br>
|
||||||
|
Terminal Functionality:<br>
|
||||||
|
========================================<br>
|
||||||
|
Up Arrow - cycle through commands<br>
|
||||||
|
Down Arrow - cycle through commands<br>
|
||||||
|
Ctrl+c - discard current prompt<br>
|
||||||
|
Ctrl+d - exit<br>
|
||||||
|
`
|
||||||
|
|
||||||
|
} else if (promptText.split(" ")[0] == 'echo') {
|
||||||
|
promptText = promptText.replaceAll('"', '')
|
||||||
|
promptText = promptText.split(' ').slice(1)
|
||||||
|
returnText = promptText.join(' ')
|
||||||
|
} else if (promptText === 'history') {
|
||||||
|
|
||||||
|
returnText = this.commands.join('<br>')
|
||||||
|
|
||||||
|
} else if (promptText == 'exit') {
|
||||||
|
this.hide()
|
||||||
|
|
||||||
|
} else if (promptText == 'about') {
|
||||||
|
returnText = 'I built this terminal emulator in JavaScript. While the functionality is quite simple, I wanted to showcase my love for linux and working in the terminal. All actions that can be done in the main world like: looking at my portfolio, downloading my resume, or playing one of the games I made can be done through this terminal. <br><br> I also added some quality of life commands like clear, exit, echo and more for the fun of it. That\'s right! This is what I do for fun :)'
|
||||||
|
|
||||||
|
} else if (promptText == 'clear') {
|
||||||
|
this.clear()
|
||||||
|
|
||||||
|
} else if (['vim', 'nvim', 'nano', 'ed'].includes(promptText)) {
|
||||||
|
returnText = 'Did you really think I coded a text editor in here!?'
|
||||||
|
|
||||||
|
} else {
|
||||||
|
returnText = 'Unkown command, please type "help" for options'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnText) {
|
||||||
|
var responseElem = document.createElement('span')
|
||||||
|
responseElem.innerHTML = returnText
|
||||||
|
this.terminalElem.appendChild(responseElem)
|
||||||
|
this.promptsAndResponses.push(responseElem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
for (let i = 0; i < this.promptsAndResponses.length; i++) {
|
||||||
|
this.promptsAndResponses[i].style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getKeystroke() {
|
||||||
|
var self = this
|
||||||
|
_listener = function (event) {
|
||||||
|
console.log(event.code)
|
||||||
|
if (
|
||||||
|
["ArrowUp", "ArrowDown", "ControlLeft"].indexOf(event.code,) > -1
|
||||||
|
) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.ctrlKey && event.key === 'd') {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "ArrowUp") {
|
||||||
|
if (self.commands.length > 0) {
|
||||||
|
var nextCommandPosition = self.commandPosition - 1
|
||||||
|
|
||||||
|
if (nextCommandPosition < 0) {
|
||||||
|
nextCommandPosition = self.commands.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentPrompt.value = self.commands[nextCommandPosition]
|
||||||
|
self.commandPosition = nextCommandPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "ArrowDown") {
|
||||||
|
if (self.commands.length > 0) {
|
||||||
|
var nextCommandPosition = self.commandPosition + 1
|
||||||
|
|
||||||
|
if (nextCommandPosition > self.commands.length - 1) {
|
||||||
|
nextCommandPosition = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentPrompt.value = self.commands[nextCommandPosition]
|
||||||
|
self.commandPosition = nextCommandPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.ctrlKey && event.key === 'c') {
|
||||||
|
self.createPrompt()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.ctrlKey && event.key === 'd') {
|
||||||
|
self.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("keydown", _listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
assets/js/window.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
class Window {
|
||||||
|
constructor(id) {
|
||||||
|
this.container = document.getElementById(`${id}-container`)
|
||||||
|
this.innerContainer = document.getElementById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.resize()
|
||||||
|
this.container.style.display = 'block'
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.container.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
resize() {
|
||||||
|
this.container.style.width = canvas.getBoundingClientRect().width - 40 + 'px';
|
||||||
|
this.container.style.height = canvas.getBoundingClientRect().height - 40 + 'px';
|
||||||
|
this.container.style.minHeight = canvas.getBoundingClientRect().width - 40 + 'px';
|
||||||
|
this.innerContainer.style.height = canvas.getBoundingClientRect().height - 77 + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
248
index.html
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>Dominic DiTaranto - Portfolio</title>
|
||||||
|
<link rel="stylesheet" href="./assets/css/style.css">
|
||||||
|
|
||||||
|
<script type="text/javascript" src="./assets/js/renderer.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/constants.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/player.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/window.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/terminal.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/games.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/music.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/resume.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/portfolio.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/dialog.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/indicator.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/keystroke.js" defer></script>
|
||||||
|
<script type="text/javascript" src="./assets/js/app.js" defer></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="outer-container">
|
||||||
|
<div class="container">
|
||||||
|
<h1 style="margin-bottom: -3px;">Dominic DiTaranto</h1>
|
||||||
|
Software Developer & Architect
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
|
||||||
|
<span class="indicator" id='resume-indicator'></span>
|
||||||
|
<span class="indicator" id='portfolio-indicator'></span>
|
||||||
|
<span class="indicator" id='computer-indicator'></span>
|
||||||
|
<span class="indicator" id='tv-indicator'></span>
|
||||||
|
<span class="indicator" id='guitar-indicator'></span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="dialog" id="dialog">
|
||||||
|
<span id="dialog-text"></span>
|
||||||
|
<div class="enter-indicator">PRESS ENTER</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Terminal -->
|
||||||
|
<div class="window-container" id="term-container">
|
||||||
|
<div class="window-bar">
|
||||||
|
<div style="float:inline-start;">
|
||||||
|
💻 Terminal
|
||||||
|
</div>
|
||||||
|
<div style="float:inline-end;">
|
||||||
|
<button class="close-button" onclick="terminal.hide()">X</button>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</div>
|
||||||
|
<div id="term">
|
||||||
|
DD-Term 1.0.0 (main, Dec 14 2025, 23:35:36) on javascript <br>
|
||||||
|
Type "help" for more information.<br>
|
||||||
|
<div id="prompt-container" class="prompt-container"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Resume -->
|
||||||
|
<div class="window-container" id="resume-container">
|
||||||
|
<div class="window-bar">
|
||||||
|
<div style="float:inline-start;">
|
||||||
|
📄 Resume
|
||||||
|
</div>
|
||||||
|
<div style="float:inline-end;">
|
||||||
|
<button class="close-button" onclick="resume.hide()">X</button>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</div>
|
||||||
|
<div id="resume">
|
||||||
|
Resume placeholder
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Portfolio -->
|
||||||
|
<div class="window-container" id="portfolio-container">
|
||||||
|
<div class="window-bar">
|
||||||
|
<div style="float:inline-start;">
|
||||||
|
💼 Portfolio
|
||||||
|
</div>
|
||||||
|
<div style="float:inline-end;">
|
||||||
|
<button class="close-button" onclick="portfolio.hide()">X</button>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</div>
|
||||||
|
<div id="portfolio">
|
||||||
|
Portfolio placeholder
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Music -->
|
||||||
|
<div class="window-container" id="music-container">
|
||||||
|
<div class="window-bar">
|
||||||
|
<div style="float:inline-start;">
|
||||||
|
🎸 Music
|
||||||
|
</div>
|
||||||
|
<div style="float:inline-end;">
|
||||||
|
<button class="close-button" onclick="music.hide()">X</button>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</div>
|
||||||
|
<div id="music" class="music-inner-container">
|
||||||
|
<center>
|
||||||
|
<div id="album-thumb">
|
||||||
|
<img id="album-art" src="./assets/img/thumbnails/shrinkinminkin.jpg" alt=""><br>
|
||||||
|
<div class="audio-controls">
|
||||||
|
<span onclick="music.prev()">⏮</span>
|
||||||
|
<span onclick="music.resume()">▶</span>
|
||||||
|
<span onclick="music.pause()">⏸</span>
|
||||||
|
<span onclick="music.next()">⏭</span>
|
||||||
|
</div>
|
||||||
|
Currently Playing: <span id="currently-playing">None</span>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Song</th>
|
||||||
|
<th>Artist</th>
|
||||||
|
<th>Album</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Down Polypore Wood</td>
|
||||||
|
<td>shrinkin-minkin</td>
|
||||||
|
<td>Tyrannizing Harmonics</td>
|
||||||
|
<td><a href="#" onclick="music.play('Down Polypore Wood')">play</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Sneaking Out Alone</td>
|
||||||
|
<td>shrinkin-minkin</td>
|
||||||
|
<td>Tyrannizing Harmonics</td>
|
||||||
|
<td><a href="#" onclick="music.play('Sneaking Out Alone')">play</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Ritual Dance of The...</td>
|
||||||
|
<td>shrinkin-minkin</td>
|
||||||
|
<td>Tyrannizing Harmonics</td>
|
||||||
|
<td><a href="#" onclick="music.play('Ritual Dance of The Cavern Walls Cult')">play</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Tatari</td>
|
||||||
|
<td>Dominic Ditaranto</td>
|
||||||
|
<td>Tatari</td>
|
||||||
|
<td><a href="#" onclick="music.play('Tatari')">play</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Buranko</td>
|
||||||
|
<td>Dominic Ditaranto</td>
|
||||||
|
<td>Tatari</td>
|
||||||
|
<td><a href="#" onclick="music.play('Buranko')">play</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>New Jersey Again</td>
|
||||||
|
<td>Dominic Ditaranto</td>
|
||||||
|
<td>Chicago-New Jersey</td>
|
||||||
|
<td><a href="#" onclick="music.play('New Jersey Again')">play</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Asteroids</td>
|
||||||
|
<td>Dominic Ditaranto</td>
|
||||||
|
<td>Amphibians And Asteroids</td>
|
||||||
|
<td><a href="#" onclick="music.play('Asteroids')">play</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<span style="font-size: 12px;">
|
||||||
|
I am the singer, guitarist and composer for the band <a href="https://shrinkinminkin.com/" target="_blank">shrinkin-minkin</a> <br>All songs by Dominic DiTaranto are from my <a href="https://dominicditaranto.bandcamp.com/" target="_blank">solo project</a>
|
||||||
|
</span>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Games -->
|
||||||
|
<div class="window-container" id="games-container">
|
||||||
|
<div class="window-bar">
|
||||||
|
<div style="float:inline-start;">
|
||||||
|
🕹️ Games
|
||||||
|
</div>
|
||||||
|
<div style="float:inline-end;">
|
||||||
|
<button class="close-button" onclick="games.hide()">X</button>
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</div>
|
||||||
|
<div id="games" class="games-inner-container">
|
||||||
|
Take a break and enjoy the games I have made!
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<div class="single-game-container" onclick="window.open('https://asteroids.domdit.com', '_blank')">
|
||||||
|
<div class="game-thumbnail-container">
|
||||||
|
<img src="./assets/img/thumbnails/asteroids.png" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="game-description-container">
|
||||||
|
<u><h3>Asteroids</h3></u>
|
||||||
|
<b>Description:</b> Asteroids port with a few extra features like power ups and a space ship that shoots homing missiles at you. <br><br>
|
||||||
|
<b>Technology:</b> p5.js, JavaScript
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="single-game-container" onclick="window.open('https://sokobanya.domdit.com', '_blank')">
|
||||||
|
<div class="game-thumbnail-container">
|
||||||
|
<img src="./assets/img/thumbnails/sokobanya.png" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="game-description-container">
|
||||||
|
<u><h3>Sokobanya</h3></u>
|
||||||
|
<b>Description:</b> A puzzle game where you are an old man pushing coal onto burners to keep your sauna hot! <br><br>
|
||||||
|
<b>Technology:</b> JavaScript
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="single-game-container" onclick="window.open('https://git.domdit.com/dominic/scoundrel', '_blank')">
|
||||||
|
<div class="game-thumbnail-container">
|
||||||
|
<img src="./assets/img/thumbnails/scoundrel.png" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="game-description-container">
|
||||||
|
<u><h3>Scoundrel</h3></u>
|
||||||
|
<b>Description:</b> TUI port of the rogue-like card game. I was inspired by Balatro to code a card game for the terminal.<br><br>
|
||||||
|
<b>Technology:</b> Python
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="single-game-container" onclick="window.open('https://pong.domdit.com', '_blank')">
|
||||||
|
<div class="game-thumbnail-container">
|
||||||
|
<img src="./assets/img/thumbnails/pong.png" alt="">
|
||||||
|
</div>
|
||||||
|
<div class="game-description-container">
|
||||||
|
<u><h3>Pong</h3></u>
|
||||||
|
<b>Description:</b> You know I had to do it. One of my first coding projects, PONG!<br><br>
|
||||||
|
<b>Technology:</b> p5.js, JavaScript
|
||||||
|
</div>
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||