Last active
October 15, 2022 00:31
-
-
Save barryosull/0185bfd1cd4580f0df2f86a39a940a6a to your computer and use it in GitHub Desktop.
DND prototype I made for my first DM session
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>Dnd - Lost mines of Phandelver</title> | |
<style type="text/css"> | |
body { | |
margin: 0 10px; | |
font-family: Arial; | |
background-color: darkgreen; | |
} | |
#background { | |
width: 100%; | |
height: 100%; | |
position: fixed; | |
top: 0px; | |
left: 0px; | |
z-index: 1; | |
background-position: center; | |
background-repeat: repeat; | |
background-size: cover; | |
filter: blur(16px); | |
} | |
#image { | |
height: 100%; | |
position: fixed; | |
left: 0; | |
right: 0; | |
z-index: 2; | |
background-position: center; | |
background-repeat: no-repeat; | |
background-size: contain; | |
} | |
#music { | |
position: fixed; | |
right: 0px; | |
bottom: 0px; | |
height: 150px; | |
width: 300px; | |
z-index: 3; | |
} | |
#controls { | |
position: fixed; | |
top: 0px; | |
right: 0px; | |
z-index: 4; | |
} | |
#controls select { | |
width: 90px; | |
} | |
#characters { | |
position: fixed; | |
top: 0px; | |
left: 0px; | |
z-index: 5; | |
} | |
.character { | |
position: absolute; | |
box-sizing: border-box; | |
width: 70px; | |
height: 70px; | |
border-radius: 35px; | |
border: 4px ridge #000; | |
cursor: pointer; | |
box-shadow: 1px 5px 15px -2px rgba(0, 0, 0, 0.75); | |
z-index: 1; | |
} | |
.player { | |
border-color: forestgreen; | |
} | |
.npc { | |
border-color: darkblue; | |
} | |
.enemy { | |
border-color: darkred; | |
} | |
</style> | |
</head> | |
<body style="position: relative;"> | |
<div id="image"> | |
</div> | |
<div id="background"> | |
</div> | |
<div id="controls"> | |
<select id="area"></select><br> | |
<select id="music-controls"></select><br> | |
</div> | |
<div id="characters"> | |
</div> | |
<iframe id="music" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> | |
<script type="text/javascript"> | |
let currImageIndex = 0; | |
const images = [ | |
'title.jpeg', | |
'map.jpg', | |
'intro.jpeg', | |
'road.webp', | |
'cave-1.png', | |
'cave-2.png', | |
'cave-3.png', | |
'cave-4.png', | |
'cave-5.png', | |
'cave-6.png', | |
'cave-7.png', | |
'cave-8.png', | |
'phandalin.jpg', | |
]; | |
const players = [ | |
'fighter.png', | |
'wizard.png', | |
'rogue.png', | |
]; | |
const music = { | |
intro: 'XbS3tPO9sUs', | |
forest: '6Em9tLXbhfo', | |
cave: 'E72yDpAfrgY', | |
combat: ['8Q7cioftmKs', 'H8n7K3jABhI', 'fq8OSrIUST4'], | |
}; | |
const npcs = []; | |
function imageForward() { | |
const nextImageIndex = currImageIndex + 1; | |
if (!images[nextImageIndex]) { | |
return; | |
} | |
changeImage(nextImageIndex); | |
} | |
function imageBackward() { | |
const nextImageIndex = currImageIndex - 1; | |
if (!images[nextImageIndex]) { | |
return; | |
} | |
changeImage(nextImageIndex); | |
} | |
function changeImage(imageIndex) { | |
let selectElem = document.getElementById('area'); | |
selectElem.value = imageIndex; | |
currImageIndex = imageIndex; | |
const image = images[imageIndex]; | |
const backgroundElem = document.getElementById('background'); | |
const imageElem = document.getElementById('image'); | |
backgroundElem.style['background-image'] = 'url("images/' + image + ' ")'; | |
imageElem.style['background-image'] = 'url("images/' + image + ' ")'; | |
} | |
var preloadedImages = new Array() | |
function preloadImages() { | |
for (var i in images) { | |
preloadedImages[i] = new Image(); | |
preloadedImages[i].src = 'images/' + images[i]; | |
} | |
} | |
function prepareAreaControls() { | |
let selectElem = document.getElementById('area'); | |
for (var i in images) { | |
const imageTitle = images[i].split('.')[0]; | |
const optionHtml = '<option value="' + i + '">' + imageTitle + '</option>'; | |
selectElem.innerHTML += optionHtml; | |
} | |
selectElem.onchange = function() { | |
changeImage(parseInt(this.value)); | |
} | |
} | |
function prepareMusicControls() { | |
let selectElem = document.getElementById('music-controls'); | |
for (var key in music) { | |
const optionHtml = '<option value="' + key + '">' + key + '</option>'; | |
selectElem.innerHTML += optionHtml; | |
} | |
selectElem.onchange = function() { | |
playMusic(music[this.value]); | |
} | |
} | |
function prepareKeyboardShortcuts() { | |
document.onkeydown = function (e) { | |
e = e || window.event; | |
if (e.key == 'g') { | |
addGoblin(); | |
} | |
if (e.key == 'b') { | |
addBugbear(); | |
} | |
if (e.key == 's') { | |
addSildar(); | |
} | |
if (e.key == 'w') { | |
addWolf(); | |
} | |
if (e.key == 'c') { | |
clearNpcs(); | |
} | |
if (e.code == 'ArrowLeft') { | |
imageBackward(); | |
} | |
if (e.code == 'ArrowRight') { | |
imageForward(); | |
} | |
} | |
} | |
function dragstartHandler(ev) { | |
ev.dataTransfer.setData("text/plain", ev.target.id); | |
} | |
function populatePlayers() { | |
for (var i in players) { | |
drawCharacter(players[i], 'player', players.length, i); | |
} | |
} | |
function addGoblin() { | |
const goblin = drawCharacter('goblin.png', 'enemy'); | |
npcs.push(goblin); | |
} | |
function addBugbear() { | |
const bugbear = drawCharacter('bugbear.png', 'enemy'); | |
npcs.push(bugbear); | |
} | |
function addWolf() { | |
const wolf = drawCharacter('wolf.png', 'enemy'); | |
npcs.push(wolf); | |
} | |
function addSildar() { | |
const sildar = drawCharacter('sildar.png', 'npc'); | |
npcs.push(sildar); | |
} | |
function clearNpcs() { | |
for (var i in npcs) { | |
npcs[i].remove(); | |
} | |
} | |
characterId = 0; | |
function drawCharacter(image, type, batchSize, batchPosition) { | |
batchSize = batchSize ?? 1; | |
batchPosition = batchPosition ?? 0; | |
const charactersElem = document.getElementById('characters'); | |
++characterId; | |
const characterElem = document.createElement("img"); | |
characterElem.id = 'character_' + characterId; | |
characterElem.className = "character "+type; | |
characterElem.src = 'images/' + image; | |
const startX = ((batchSize-1)/2) * -80; | |
const centerX = window.innerWidth / 2; | |
const centerY = window.innerHeight / 2; | |
const offsetX = batchPosition * 80; | |
characterElem.style.left = (startX + (centerX - 35) + offsetX) + 'px'; | |
characterElem.style.top = (centerY - 35) + 'px'; | |
charactersElem.appendChild(characterElem); | |
makeDraggable(characterElem); | |
return characterElem; | |
} | |
var topCharacterZIndex = 1; | |
function makeDraggable(characterElem) { | |
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; | |
const startDragging = function(e) { | |
e.preventDefault(); | |
pos3 = e.clientX; | |
pos4 = e.clientY; | |
document.onmouseup = stopDragging; | |
document.onmousemove = dragElement; | |
topCharacterZIndex++; | |
characterElem.style['z-index'] = topCharacterZIndex; | |
} | |
const dragElement = function(e) { | |
e.preventDefault(); | |
// calculate the new cursor position: | |
pos1 = pos3 - e.clientX; | |
pos2 = pos4 - e.clientY; | |
pos3 = e.clientX; | |
pos4 = e.clientY; | |
characterElem.style.left = (characterElem.offsetLeft - pos1) + "px"; | |
characterElem.style.top = (characterElem.offsetTop - pos2) + "px"; | |
} | |
const stopDragging = function() { | |
document.onmouseup = null; | |
document.onmousemove = null; | |
} | |
characterElem.addEventListener("mousedown", startDragging); | |
} | |
function playMusic(youtubeId) { | |
if (Array.isArray(youtubeId)) { | |
youtubeId = youtubeId[Math.floor(Math.random() * youtubeId.length)]; | |
} | |
document.getElementById('music').src = "https://www.youtube.com/embed/" + youtubeId + "?autoplay=1&t=0"; | |
} | |
// Do the work | |
preloadImages(); | |
prepareAreaControls(); | |
prepareKeyboardShortcuts(); | |
populatePlayers(); | |
changeImage(0); | |
prepareMusicControls(); | |
playMusic(music.intro); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment