Player has health, a level, and a weapon. Player can pick up a better weapon. Player can pick up health items. All the items and enemies on the map are arranged at random. Player can move throughout a map, discovering items. Player can move anywhere within the map's boundaries, but can't move through an enemy until it's beaten. Much of the map is hidden. When player take a step, all spaces that are within a certain number of spaces from me are revealed. Player can toggle Fog of War. When player beats an enemy, the enemy goes away and the player get XP, which eventually increases player's level. When player fight an enemy, both take turns damaging each other until one loses. Player does damage based on player's level and weapon. The enemy does damage based on its level and strength. Damage is somewhat random within a range. When player finds and beats the boss, player win. The game is challenging, but theoretically winnable.
Last active
April 22, 2017 15:50
-
-
Save rfprod/f8434927fd10346ee1d3 to your computer and use it in GitHub Desktop.
Roguelike Dungeon Crawler
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
<div class="container-fluid nopadding"> | |
<nav class="navbar navbar-inverse navbar-fixed-top topnav" role="navigation"> | |
<div class="navbar-header"> | |
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#toggle-nav" aria-expanded="false"> | |
<span class="sr-only">Toggle navigation</span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
</button> | |
<a class="navbar-brand" target=_blank href="#"><span class="glyphicon glyphicon-th"></span> Roguelike Dungeon Crawler</a> | |
</div> | |
<div class="collapse navbar-collapse" id="toggle-nav"> | |
<div class="container-fluid"> | |
<ul class="nav navbar-nav navbar-right"> | |
<li class="nav-tabs"><a href="#" id="info-toggler"><span class="glyphicon glyphicon-info-sign"></span> INFO</a></li> | |
<li class="nav-tabs"><a href="https://gist.github.com/rfprod/f8434927fd10346ee1d3" target=_blank><span class="glyphicon glyphicon-download-alt" ></span> GIST</a></li> | |
</ul> | |
</div> | |
</div> | |
</nav> | |
<div class="home"> | |
<div class="container-fluid"> | |
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12"> | |
<div id="output"> | |
Output | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="alert alert-info alert-dismissible animated fadeOut" role="alert"> | |
<button type="button" class="close" id="dismiss-info" aria-label="Close"><span aria-hidden="true">×</span></button> | |
<p>Find and kill the boss to win.<br/>Use cursor keys to navigate.<br/> | |
<a href="https://en.wikipedia.org/wiki/Roguelike" target=_blank>What is roguelike game?</a><br/>Licence - <a href="http://www.gnu.org/licenses/gpl-3.0.en.html" target=_blank>GPL 3.0</a></p> | |
<p><strong>Hints</strong></p> | |
<ul> | |
<li>while your character is of low level, you may need to use a potion after or during each combat;</li> | |
<li>if you manage to find a better weapon while your character is of low level, you'll find it easier to advance further;</li> | |
<li>you can boost your character's Hit Points with potions to get 50 more HP above maximum HP for given character level;</li> | |
<li>don't use potions when you don't need to restore HP, they'll be useful (you'll need a lot) when you find the boss;</li> | |
<li>some parts of the dungeon may be inaccessible;</li> | |
<li>you may want to kill all npc spawns before challenging the boss to have as high level as possible; thus, the bigger your scrreen the more chances to win you have, because dungeon will be bigger.</li> | |
</ul> | |
</div> | |
</div> |
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
$(document).on('ready',()=>{ | |
(()=>{ | |
var charLevel = 1, charHP = 100, charWeapon = 'Knife', charAttack = 2, charXP = 0, charNextLevel = 150; | |
var ControlsAndGridContainer = React.createClass({ | |
render: function(){ | |
return ( | |
<span> | |
<p id="user-instructions"> | |
<div className="marker" id="player-mrk"></div> <span className="legend">player</span> | |
<div className="marker" id="obstacle-mrk"></div> <span className="legend">obstacle</span> | |
<div className="marker" id="npc-mrk"></div> <span className="legend">npc</span> | |
<div className="marker" id="boss-mrk"></div> <span className="legend">boss</span> | |
<div className="marker" id="weapon-mrk"></div> <span className="legend">weapon</span> | |
<div className="marker" id="potion-mrk"></div> <span className="legend">potion</span> | |
</p> | |
<ul className="list-group" id="char-stats"> | |
<li className="list-group-item"> | |
Level<span className="label label-default label-pill pull-xs-right" id="level">{charLevel}</span> XP<span className="label label-default label-pill pull-xs-right" id="xp">{charXP}</span> Next level<span className="label label-default label-pill pull-xs-right" id="next-level">{charNextLevel}</span> | |
</li> | |
<li className="list-group-item"> | |
HP<span className="label label-success label-pill pull-xs-right" id="hp">{charHP}</span> Attack<span className="label label-default label-pill pull-xs-right" id="attack">{charAttack}</span> Weapon<span className="label label-default label-pill pull-xs-right" id="weapon">{charWeapon}</span> | |
</li> | |
</ul> | |
<div className="btn-group btn-group-justified"> | |
<a href="#" id="fog" className="btn btn-info">Toggle Fog Of War</a> | |
</div> | |
<div> | |
<table className="table table-bordered" id="grid"></table> | |
<table className="table table-bordered" id="grid-fog"></table> | |
</div> | |
<div id="dialog"><span id="dialog-marker">></span><span id="dialog-output">dialog output</span></div> | |
</span> | |
); | |
} | |
}); | |
ReactDOM.render(<ControlsAndGridContainer />,document.getElementById('output')); | |
var cellDimensions = 5; | |
if ($('#grid').width() > 480) cellDimensions = 7; | |
if ($('#grid').width() > 640) cellDimensions = 9; | |
var cellsInRow = 50; // table can be made of static size by commenting next line | |
cellsInRow = ($('#grid').width() - $('#grid').width()%cellDimensions) / cellDimensions; | |
var rowsCount = (cellsInRow - cellsInRow%2)/2; | |
var Grid = React.createClass({ | |
render: function() { | |
var markup = []; | |
var rowMarkup = []; | |
for(var j=0;j<cellsInRow;j++){ | |
rowMarkup.push(<td className="grid-unit" onClick={this.handleClick} id={j}></td>); | |
} | |
for (var i=0;i<rowsCount;i++){ | |
markup.push(<tr className="row" id={i}>{rowMarkup}</tr>); | |
} | |
return ( | |
<tbody>{markup}</tbody> | |
); | |
} | |
}); | |
ReactDOM.render(<Grid />,document.getElementById('grid')); | |
ReactDOM.render(<Grid />,document.getElementById('grid-fog')); | |
// overlay fog of war grid over dungeon grid | |
document.getElementById('grid-fog').style.marginTop = '-'+($('#grid').height()+1)+'px'; | |
// create objects | |
var player = null, playerPosition = '', obstacles = [], npcs = [], boss = null, weapons = [], potions = []; | |
var Player = function(level,hp,weapon,attack,xp,nextLevel,position){ | |
this.level = level; | |
this.hp = hp; | |
this.weapon = weapon; | |
this.attack = attack*1.5; | |
this.xp = xp; | |
this.nextLevel = nextLevel; | |
this.position = position; | |
} | |
var Obstacle = function(hp,position){ | |
this.hp = hp; // if hp = N - obstacle is indestructible, if hp is a number - obstacle is destructible | |
this.position = position; // [] | |
} | |
var NPC = function(name,level,strength,position){ | |
this.name = name; | |
this.hp = level*20; | |
this.attack = level*(1+strength); | |
this.xp = level*15*(1+strength*0.5); | |
this.position = position; | |
} | |
var Boss = function(hp,attack,position){ | |
this.hp = hp; // 10 | |
this.attack = attack; // 10 | |
this.position = position; | |
} | |
var Weapon = function(name,damage,position){ | |
this.name = name; // abc | |
this.damage = damage; // 10 | |
this.position = position; // 0-0 | |
} | |
var Potion = function(position){ | |
this.name = 'Healing Potion'; // abc | |
this.effect = 50; // 10 | |
this.position = position; // 0-0 | |
} | |
// functions to generate npcs, and items dynamically | |
/* npcs */ | |
var lesserSpawnPos = []; | |
function generateLesserSpawns(lesserSpawnPositions){ | |
for (var lsc=0;lsc<lesserSpawnPositions.length;lsc++){ | |
npcs.push(new NPC('lesser spawn',2,1,lesserSpawnPositions[lsc])); | |
} | |
} | |
/* weapons */ | |
var weaponsPos = []; | |
(function generateWeapons(wpnDamageMod){ // weapon damage modifier may depend on the level of the dungeon | |
weapons.push(new Weapon('Knife',2*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Club',3*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Dagger',4*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Long Staff',5*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Short Sword',6*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Hand Axe',7*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Long Sword',8*wpnDamageMod,'0-0')) | |
weapons.push(new Weapon('Daggers x2',9*wpnDamageMod,'0-0'));; | |
weapons.push(new Weapon('Broad Axe',10*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Two-handed Sword',11*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Two-handed Axe',12*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Short Swords x2',13*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Hand Axes x2',15*wpnDamageMod,'0-0')); | |
weapons.push(new Weapon('Double Long Sword',16*wpnDamageMod,'0-0')); | |
})(1); | |
function setWeaponsPositions(weaponsPositions){ | |
for (var w=0;w<weapons.length;w++){ | |
weapons[w].position = weaponsPositions[w]; | |
} | |
} | |
/* potions */ | |
var potionsPos = []; | |
function generatePotions(potionsPositions){ | |
for (var p=0;p<potionsPositions.length;p++){ | |
potions.push(new Potion(potionsPositions[p])); | |
} | |
} | |
/* player */ | |
player = new Player(charLevel,charHP,charWeapon,charAttack,charXP,charNextLevel,playerPosition); | |
$('#attack').html(player.attack); | |
// bind to hp indicator to detect cahnge and change colour | |
$('#hp').bind('DOMSubtreeModified',function(){ | |
if ($(this).html()>66) document.getElementById('hp').className = "label label-success label-pill pull-xs-right"; | |
if ($(this).html()>33 && $(this).html()<66) document.getElementById('hp').className = "label label-warning label-pill pull-xs-right"; | |
if ($(this).html()<33) document.getElementById('hp').className = "label label-danger label-pill pull-xs-right"; | |
}); | |
var cellsAddresses = [], neighboursAddresses = []; | |
(function getGridAddresses(){ | |
if (cellsAddresses.length === 0 || neighboursAddresses.length === 0){ | |
for (var r=0;r<rowsCount;r++){ // rows | |
for (var c=0;c<cellsInRow;c++){ // columns | |
var neighbourStatus = [], neighbourRows = [], neighbourColumns = []; | |
if (r===0) neighbourRows = [rowsCount-1,r,r+1]; | |
else if (r===rowsCount-1) neighbourRows = [r-1,r,0]; | |
else neighbourRows = [r-1,r,r+1]; | |
if (c===0) neighbourColumns = [cellsInRow-1,c,c+1]; | |
else if (c===cellsInRow-1) neighbourColumns = [c-1,c,0]; | |
else neighbourColumns = [c-1,c,c+1]; | |
var tmpNeighboursAddresses = []; | |
for (var y=0;y<neighbourRows.length;y++){ | |
var rowElmnt = neighbourRows[y]; | |
for (var z=0;z<neighbourColumns.length;z++){ | |
var colElmnt = neighbourColumns[z]; | |
var neighbour = document.getElementById('grid').getElementsByTagName('tr')[rowElmnt].getElementsByTagName('td')[colElmnt]; | |
tmpNeighboursAddresses.push(neighbour.parentNode.id+"-"+neighbour.id); | |
} | |
} | |
cellsAddresses.push(tmpNeighboursAddresses[4]); | |
tmpNeighboursAddresses.splice(4,1); // exclude cell itself, consider only neighbours | |
neighboursAddresses.push(tmpNeighboursAddresses); | |
} | |
} | |
} | |
})(); | |
// manual obstacle initialization using mouse - for configuration and debug purposes only | |
/* | |
var gridUnits = document.getElementsByClassName('grid-unit'); | |
for (var i=0;i<gridUnits.length;i++){ | |
gridUnits[i].addEventListener('click', function(){ | |
var gridUnitClass = this.className; | |
if (gridUnitClass === 'grid-unit') { | |
this.className = 'grid-unit obstacle'; | |
var addr = this.parentNode.id+"-"+this.id; | |
console.log(this.parentNode.id+"-"+this.id); | |
} | |
if (gridUnitClass === 'grid-unit obstacle'){ | |
this.className = 'grid-unit'; | |
var addr = this.parentNode.id+"-"+this.id; | |
console.log(addr); | |
} | |
}); | |
} | |
*/ | |
// init game | |
var tableRows = document.getElementById('grid').getElementsByClassName("row"); | |
var tableRowsFog = document.getElementById('grid-fog').getElementsByClassName("row"); | |
// init obstacles | |
var obstaclesAreaSquaresCount = cellsAddresses.length /3; | |
//console.log('obstaclesAreaSquaresCount: '+obstaclesAreaSquaresCount); | |
var topBorder = new RegExp(/^0-/); | |
var bottomBorder = new RegExp('^'+(rowsCount-1)+'-'); | |
var leftBorder = new RegExp(/-0$/); | |
var rightBorder = new RegExp('-'+(cellsInRow-1)); | |
//console.log('topBorder: '+topBorder+' | bottomBorder: '+bottomBorder+' | leftBorder: '+leftBorder+' | rightBorder: '+rightBorder); | |
for (var ca=0;ca<cellsAddresses.length;ca++){ | |
if (topBorder.test(cellsAddresses[ca])) if (obstacles.indexOf(cellsAddresses[ca]) == -1) obstacles.push(new Obstacle('N',cellsAddresses[ca])); | |
if (bottomBorder.test(cellsAddresses[ca])) if (obstacles.indexOf(cellsAddresses[ca]) == -1) obstacles.push(new Obstacle('N',cellsAddresses[ca])); | |
if (leftBorder.test(cellsAddresses[ca])) if (obstacles.indexOf(cellsAddresses[ca]) == -1) obstacles.push(new Obstacle('N',cellsAddresses[ca])); | |
if (rightBorder.test(cellsAddresses[ca])) if (obstacles.indexOf(cellsAddresses[ca]) == -1) obstacles.push(new Obstacle('N',cellsAddresses[ca])); | |
} | |
var obstaclesLeft = obstaclesAreaSquaresCount - obstacles.length; | |
//console.log('obstaclesLeft: '+obstaclesLeft); | |
// randomly generate rest obstacles | |
function getRandomIntInclusive(min, max) {return Math.floor(Math.random() * (max - min + 1)) + min;} | |
for (var rd=0;rd<obstaclesLeft;rd++){ | |
var randomCellId = getRandomIntInclusive(0,cellsAddresses.length-1); | |
var randomCellAddress = cellsAddresses[randomCellId]; | |
if (randomCellAddress.split('-')[0] > 1 && randomCellAddress.split('-')[0] < rowsCount-2 && randomCellAddress.split('-')[1] > 1 && randomCellAddress.split('-')[1] < cellsInRow-2) { | |
obstacles.push(new Obstacle('N',randomCellAddress)); | |
} | |
else rd--; | |
} | |
for (var n=0;n<obstacles.length;n++){ | |
var obstacleAddr = obstacles[n].position; | |
var obstacleAddrArr = obstacleAddr.split("-"); | |
tableRows[obstacleAddrArr[0]].childNodes.item(obstacleAddrArr[1]).className = "grid-unit obstacle"; | |
} | |
// fill gaps with more obstacles | |
for (var n=0;n<cellsAddresses.length;n++){ | |
var cllAddr = cellsAddresses[n]; | |
var cllAddrArr = cllAddr.split("-"); | |
if (tableRows[cllAddrArr[0]].childNodes.item(cllAddrArr[1]).className != "grid-unit"){ | |
var nCounter = 0; | |
for (var k=0;k<neighboursAddresses[n].length;k++){ | |
var nAddr = neighboursAddresses[n][k]; | |
var nAddrArr = nAddr.split("-"); | |
if (tableRows[nAddrArr[0]].childNodes.item(nAddrArr[1]).className != "grid-unit obstacle"){ | |
nCounter++; | |
} | |
} | |
if (nCounter > 7) { | |
//console.log(nCounter); | |
for (var k=0;k<neighboursAddresses[n].length;k++){ | |
var nAddr = neighboursAddresses[n][k]; | |
var nAddrArr = nAddr.split("-"); | |
tableRows[nAddrArr[0]].childNodes.item(nAddrArr[1]).className = "grid-unit obstacle"; | |
} | |
} | |
} | |
} | |
// init npcs | |
// get lesser spawns positions | |
while (lesserSpawnPos.length < (cellsAddresses.length-obstacles.length)/13){ // the more the index, the lower is spawn rate | |
var randomCellId = getRandomIntInclusive(0,cellsAddresses.length-1); | |
var randomCellAddress = cellsAddresses[randomCellId]; | |
if (tableRows[randomCellAddress.split('-')[0]].childNodes.item(randomCellAddress.split('-')[1]).className == "grid-unit"){ | |
var nCounter = 0; | |
for (var na=0;na<neighboursAddresses[randomCellId].length;na++){ | |
var nghAddr = neighboursAddresses[randomCellId][na].split('-'); | |
if (tableRows[nghAddr[0]].childNodes.item(nghAddr[1]).className != "grid-unit obstacle") nCounter++; | |
} | |
if (nCounter >= 6) if (lesserSpawnPos.indexOf(randomCellAddress) == -1) lesserSpawnPos.push(randomCellAddress); | |
} | |
} | |
generateLesserSpawns(lesserSpawnPos); | |
for (var n=0;n<npcs.length;n++){ | |
var npcAddr = npcs[n].position; | |
var npcAddrArr = npcAddr.split("-"); | |
tableRows[npcAddrArr[0]].childNodes.item(npcAddrArr[1]).className = "grid-unit npc"; | |
} | |
// init boss | |
//console.log(npcs.length); | |
boss = new Boss(Math.floor(npcs.length/1.55*10),75,'["10-10","10-11","11-10","11-11"]'); | |
//console.log(boss); | |
// init weapons | |
while (weaponsPos.length <= weapons.length){ // generate positions for weapons | |
var randomCellId = getRandomIntInclusive(0,cellsAddresses.length-1); | |
var randomCellAddress = cellsAddresses[randomCellId]; | |
if (tableRows[randomCellAddress.split('-')[0]].childNodes.item(randomCellAddress.split('-')[1]).className == "grid-unit"){ | |
var nCounter = 0; | |
for (var na=0;na<neighboursAddresses[randomCellId].length;na++){ | |
var nghAddr = neighboursAddresses[randomCellId][na].split('-'); | |
if (tableRows[nghAddr[0]].childNodes.item(nghAddr[1]).className != "grid-unit obstacle") nCounter++; | |
} | |
if (nCounter >= 4) if (weaponsPos.indexOf(randomCellAddress) == -1) weaponsPos.push(randomCellAddress); | |
} | |
} | |
setWeaponsPositions(weaponsPos); | |
for (var n=0;n<weapons.length;n++){ | |
var weaponAddr = weapons[n].position; | |
//console.log('weaponAddr: '+weaponAddr); | |
var weaponAddrArr = weaponAddr.split("-"); | |
tableRows[weaponAddrArr[0]].childNodes.item(weaponAddrArr[1]).className = "grid-unit weapon"; | |
} | |
// init potions | |
var potionsCount = (cellsAddresses.length-obstacles.length-npcs.length-weapons.length-(cellsAddresses.length-obstacles.length-npcs.length-weapons.length)%100)/100; | |
//console.log('potionsCount: '+potionsCount); | |
while (potionsPos.length < potionsCount){ | |
var randomCellId = getRandomIntInclusive(0,cellsAddresses.length-1); | |
var randomCellAddress = cellsAddresses[randomCellId]; | |
if (tableRows[randomCellAddress.split('-')[0]].childNodes.item(randomCellAddress.split('-')[1]).className == "grid-unit"){ | |
var nCounter = 0; | |
for (var na=0;na<neighboursAddresses[randomCellId].length;na++){ | |
var nghAddr = neighboursAddresses[randomCellId][na].split('-'); | |
if (tableRows[nghAddr[0]].childNodes.item(nghAddr[1]).className != "grid-unit obstacle") nCounter++; | |
} | |
if (nCounter >= 6) if (potionsPos.indexOf(randomCellAddress) == -1) potionsPos.push(randomCellAddress); | |
} | |
} | |
generatePotions(potionsPos); | |
for (var n=0;n<potions.length;n++){ | |
var potionAddr = potions[n].position; | |
//console.log('potionAddr: '+potionAddr); | |
var potionAddrArr = potionAddr.split("-"); | |
tableRows[potionAddrArr[0]].childNodes.item(potionAddrArr[1]).className = "grid-unit potion"; | |
} | |
// init player (determine position and place marker) | |
for (var n=0;n<cellsAddresses.length;n++){ | |
var cllAddr = cellsAddresses[n]; | |
var cllAddrArr = cllAddr.split("-"); | |
if (tableRows[cllAddrArr[0]].childNodes.item(cllAddrArr[1]).className != "grid-unit"){ | |
var nCounter = 0; | |
for (var k=0;k<neighboursAddresses[n].length;k++){ | |
var nAddr = neighboursAddresses[n][k]; | |
var nAddrArr = nAddr.split("-"); | |
if (tableRows[nAddrArr[0]].childNodes.item(nAddrArr[1]).className != "grid-unit obstacle"){ | |
nCounter++; | |
} | |
} | |
if (nCounter >= 7) { | |
playerPosition = cllAddr; | |
player.position = playerPosition; | |
//console.log('playerAddr: '+player.position); | |
tableRows[cllAddrArr[0]].childNodes.item(cllAddrArr[1]).className = "grid-unit player"; | |
break; | |
} | |
} | |
} | |
// init boss (determine position and place marker) | |
for (var n=cellsAddresses.length-1;n>=0;n--){ | |
var cllAddr = cellsAddresses[n]; | |
var cllAddrArr = cllAddr.split("-"); | |
if (tableRows[cllAddrArr[0]].childNodes.item(cllAddrArr[1]).className != "grid-unit"){ | |
var nCounter = 0; | |
for (var k=0;k<neighboursAddresses[n].length;k++){ | |
var nAddr = neighboursAddresses[n][k]; | |
var nAddrArr = nAddr.split("-"); | |
if (tableRows[nAddrArr[0]].childNodes.item(nAddrArr[1]).className != "grid-unit obstacle"){ | |
nCounter++; | |
} | |
} | |
if (nCounter >= 7) { | |
boss.position = cllAddr; | |
//console.log('boss position: '+boss.position); | |
tableRows[cllAddrArr[0]].childNodes.item(cllAddrArr[1]).className = "grid-unit boss"; | |
break; | |
} | |
} | |
} | |
// init fog of war | |
function initFogOfWar(){ | |
var indexOfPlayer = cellsAddresses.indexOf(player.position); | |
console.log('indexOfPlayer: '+indexOfPlayer); | |
for (var n=0;n<cellsAddresses.length;n++){ | |
var cllAddr = cellsAddresses[n]; | |
var cllAddrArr = cllAddr.split("-"); | |
if ($('#fog').hasClass('active')) tableRowsFog[cllAddrArr[0]].childNodes.item(cllAddrArr[1]).className = "grid-unit fog"; | |
else tableRowsFog[cllAddrArr[0]].childNodes.item(cllAddrArr[1]).className = "grid-unit"; | |
} | |
if ($('#fog').hasClass('active')){ | |
for (var k=0;k<neighboursAddresses[indexOfPlayer].length;k++){ | |
var nAddr = neighboursAddresses[indexOfPlayer][k]; | |
var nAddrArr = nAddr.split("-"); | |
tableRowsFog[nAddrArr[0]].childNodes.item(nAddrArr[1]).className = "grid-unit"; | |
} | |
tableRowsFog[player.position.split('-')[0]].childNodes.item(player.position.split('-')[1]).className = "grid-unit"; | |
} | |
} | |
// actions | |
function pickWeapon(pos1,pos2){ | |
if (document.getElementsByClassName("row")[pos1].childNodes.item(pos2).className === 'grid-unit weapon') { | |
//console.log('picked up a weapon'); | |
var pickingWeaponAddr = pos1+"-"+pos2; | |
//console.log('pickingWeaponAddr: '+pickingWeaponAddr); | |
for (var w=0;w<weapons.length;w++){ | |
if (weapons[w].position == pickingWeaponAddr) { | |
//console.log(weapons[w]); | |
var currentWeapon = $('#weapon').html(), currentDamage; | |
//console.log('currentWeapon: '+currentWeapon); | |
for (var wp=0;wp<weapons.length;wp++){ | |
if (weapons[wp].name == currentWeapon) currentDamage = weapons[wp].damage; | |
} | |
if (currentDamage < weapons[w].damage){ | |
player.weapon = weapons[w].name; | |
player.attack = player.level*weapons[w].damage; | |
$('#weapon').html(player.weapon); | |
$('#attack').html(player.attack); | |
$('#dialog-output').html('You picked up '+weapons[w].name+' (dmg: '+weapons[w].damage+').'); | |
}else { | |
$('#dialog-output').html('You picked up '+weapons[w].name+' (dmg: '+weapons[w].damage+'), but you already have a better weapon.'); | |
} | |
} | |
} | |
} | |
} | |
function pickPotion(pos1,pos2){ | |
if (document.getElementsByClassName("row")[pos1].childNodes.item(pos2).className === 'grid-unit potion') { | |
//console.log('picked up a potion'); | |
var pickingPotionAddr = pos1+"-"+pos2; | |
//console.log('pickingPotionAddr: '+pickingPotionAddr); | |
for (var p=0;p<potions.length;p++){ | |
if (potions[p].position == pickingPotionAddr) { | |
//console.log(potions[p]); | |
if (player.hp < 100+player.level*50) { | |
if (player.hp+potions[p].effect > 100+player.level*50) player.hp = 100+player.level*50; | |
else player.hp += potions[p].effect; | |
} | |
$('#hp').html(player.hp); | |
$('#dialog-output').html('You picked up '+potions[p].name+' (effect: '+potions[p].effect+').'); | |
} | |
} | |
} | |
} | |
function fightNPC(pos1,pos2){ | |
console.log('fighting npc'); | |
var fightingNPCAddr = pos1+"-"+pos2; | |
console.log('fightingNPCAddr: '+fightingNPCAddr); | |
for (var n=0;n<npcs.length;n++){ | |
if (npcs[n].position == fightingNPCAddr) { | |
console.log(npcs[n]); | |
// player's hit | |
var playerHit = getRandomIntInclusive(0,player.attack); | |
npcs[n].hp -= playerHit; | |
console.log('npc hp: '+npcs[n].hp); | |
if (npcs[n].hp <= 0) { | |
document.getElementsByClassName("row")[pos1].childNodes.item(pos2).className = "grid-unit"; | |
player.xp += npcs[n].xp; | |
if (player.xp >= player.nextLevel) { | |
player.level++; | |
var nextXP = Math.floor(player.xp - player.nextLevel); | |
var nextLevelXP = Math.floor(player.nextLevel*1.65); | |
player.xp = nextXP; | |
player.hp = 50+player.level*50; | |
var currentWeapon = $('#weapon').html(), currentDamage; | |
for (var wp=0;wp<weapons.length;wp++){ | |
if (weapons[wp].name == currentWeapon) player.attack = weapons[wp].damage * player.level; | |
} | |
player.nextLevel = nextLevelXP; | |
$('#attack').html(player.attack); | |
$('#level').html(player.level); | |
$('#next-level').html(player.nextLevel); | |
$('#hp').html(player.hp); | |
$('#dialog-output').html('You hit '+npcs[n].name+' for '+playerHit+' damage and killed it (gained exp: '+npcs[n].xp+'). You gained level.'); | |
}else $('#dialog-output').html('You hit '+npcs[n].name+' for '+playerHit+' damage and killed it (gained exp: '+npcs[n].xp+').'); | |
$('#xp').html(player.xp); | |
}else{ | |
// npc's hit | |
var npcHit = getRandomIntInclusive(1,npcs[n].attack); | |
player.hp -= npcHit; | |
console.log('player health: '+player.hp); | |
$('#hp').html(player.hp); | |
if (player.hp <= 0) { | |
document.getElementsByClassName("row")[player.position.split('-')[0]].childNodes.item(player.position.split('-')[1]).className = "grid-unit"; | |
$('#dialog-output').html('You were hit by '+npcs[n].name+' for '+npcHit+' damage and were killed. You failed to win the game. Refresh the page to start over.'); | |
alert('You were hit by '+npcs[n].name+' for '+npcHit+' damage and were killed. You failed to win the game. Refresh the page to start over.'); | |
}else $('#dialog-output').html('You hit '+npcs[n].name+' for '+playerHit+' damage, '+npcs[n].name+' hits you for '+npcHit+' damage.'); | |
} | |
} | |
} | |
} | |
function fightBoss(pos1,pos2){ | |
console.log('fighting boss'); | |
console.log(boss); | |
// player's hit | |
var playerHit = getRandomIntInclusive(Math.floor(player.attack/3),player.attack); | |
boss.hp -= playerHit; | |
console.log('boss hp: '+boss.hp); | |
if (boss.hp <= 0) { | |
document.getElementsByClassName("row")[pos1].childNodes.item(pos2).className = "grid-unit"; | |
player.xp += 1000; | |
if (player.xp >= player.nextLevel){ | |
player.level++; | |
var nextXP = Math.floor(player.xp - player.nextLevel); | |
var nextLevelXP = Math.floor(player.nextLevel*1.5); | |
player.xp = nextXP; | |
player.hp = 100+player.level*50; | |
var currentWeapon = $('#weapon').html(), currentDamage; | |
for (var wp=0;wp<weapons.length;wp++){ | |
if (weapons[wp].name == currentWeapon) player.attack = weapons[wp].damage * player.level; | |
} | |
player.nextLevel = nextLevelXP; | |
$('#attack').html(player.attack); | |
$('#level').html(player.level); | |
$('#next-level').html(player.nextLevel); | |
$('#hp').html(player.hp); | |
} | |
$('#xp').html(player.xp); | |
$('#dialog-output').html('You hit the boss for '+playerHit+' damage and killed it. You won the game.'); | |
alert('You hit the boss for '+playerHit+' damage and killed it. You won the game. You can continue wondering the dungeon or refresh the page to start over.'); | |
}else{ | |
// npc's hit | |
var bossHit = getRandomIntInclusive(1,boss.attack); | |
player.hp -= bossHit; | |
console.log('player hp: '+player.hp); | |
$('#hp').html(player.hp); | |
if (player.hp <= 0) { | |
document.getElementsByClassName("row")[player.position.split('-')[0]].childNodes.item(player.position.split('-')[1]).className = "grid-unit"; | |
$('#dialog-output').html('You were hit by the boss for '+bossHit+' damage and were killed. You failed to win the game. Refresh the page to start over.'); | |
alert('You were hit by the boss for '+bossHit+' damage and were killed. You failed to win the game. Refresh the page to start over.'); | |
}else $('#dialog-output').html('You hit the boss for '+playerHit+' damage, the boss hits you for '+bossHit+' damage.'); | |
} | |
} | |
function movePlayer(direction){ | |
//console.log('current player position: '+player.position); | |
var updPlayerPosition = player.position.split("-"); | |
if (direction == 'right'){ | |
if (updPlayerPosition[1] < cellsInRow-1){ | |
updPlayerPosition[1]++; | |
if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit obstacle'){ | |
console.log('indestructible obstacle'); | |
$('#dialog-output').html('Indestructible obstacle, you can not go there.'); | |
}else { | |
pickWeapon(updPlayerPosition[0],updPlayerPosition[1]); | |
pickPotion(updPlayerPosition[0],updPlayerPosition[1]); | |
if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit npc') { | |
fightNPC(updPlayerPosition[0],updPlayerPosition[1]); | |
}else if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit boss') { | |
fightBoss(updPlayerPosition[0],updPlayerPosition[1]); | |
}else{ | |
tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]-1).className = "grid-unit"; | |
playerPosition = updPlayerPosition[0]+"-"+updPlayerPosition[1]; | |
tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className = "grid-unit player"; | |
} | |
player.position = playerPosition; | |
} | |
}else console.log("right border of the dungeon is reached"); | |
} | |
if (direction == 'down'){ | |
var updPlayerPosition = player.position.split("-"); | |
if (updPlayerPosition[0] < rowsCount-1){ | |
updPlayerPosition[0]++; | |
if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit obstacle'){ | |
console.log('indestructible obstacle'); | |
$('#dialog-output').html('Indestructible obstacle, you can not go there.'); | |
}else { | |
pickWeapon(updPlayerPosition[0],updPlayerPosition[1]); | |
pickPotion(updPlayerPosition[0],updPlayerPosition[1]); | |
if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit npc') { | |
fightNPC(updPlayerPosition[0],updPlayerPosition[1]); | |
}else if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit boss') { | |
fightBoss(updPlayerPosition[0],updPlayerPosition[1]); | |
}else{ | |
tableRows[updPlayerPosition[0]-1].childNodes.item(updPlayerPosition[1]).className = "grid-unit"; | |
playerPosition = updPlayerPosition[0]+"-"+updPlayerPosition[1]; | |
tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className = "grid-unit player"; | |
} | |
player.position = playerPosition; | |
} | |
}else console.log("bottom border of the dungeon is reached"); | |
} | |
if (direction == 'left'){ | |
var updPlayerPosition = player.position.split("-"); | |
if (updPlayerPosition[1] > 0){ | |
updPlayerPosition[1]--; | |
if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit obstacle'){ | |
console.log('indestructible obstacle'); | |
$('#dialog-output').html('Indestructible obstacle, you can not go there.'); | |
}else { | |
pickWeapon(updPlayerPosition[0],updPlayerPosition[1]); | |
pickPotion(updPlayerPosition[0],updPlayerPosition[1]); | |
if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit npc') { | |
fightNPC(updPlayerPosition[0],updPlayerPosition[1]); | |
}else if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit boss') { | |
fightBoss(updPlayerPosition[0],updPlayerPosition[1]); | |
}else{ | |
tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]+1).className = "grid-unit"; | |
playerPosition = updPlayerPosition[0]+"-"+updPlayerPosition[1]; | |
tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className = "grid-unit player"; | |
} | |
player.position = playerPosition; | |
} | |
}else console.log("left border of the dungeon is reached"); | |
} | |
if (direction == 'up'){ | |
var updPlayerPosition = player.position.split("-"); | |
if (updPlayerPosition[0] > 0){ | |
updPlayerPosition[0]--; | |
if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit obstacle'){ | |
console.log('indestructible obstacle'); | |
$('#dialog-output').html('Indestructible obstacle, you can not go there.'); | |
}else { | |
pickWeapon(updPlayerPosition[0],updPlayerPosition[1]); | |
pickPotion(updPlayerPosition[0],updPlayerPosition[1]); | |
if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit npc') { | |
fightNPC(updPlayerPosition[0],updPlayerPosition[1]); | |
}else if (tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className === 'grid-unit boss') { | |
fightBoss(updPlayerPosition[0],updPlayerPosition[1]); | |
}else{ | |
tableRows[updPlayerPosition[0]+1].childNodes.item(updPlayerPosition[1]).className = "grid-unit"; | |
playerPosition = updPlayerPosition[0]+"-"+updPlayerPosition[1]; | |
tableRows[updPlayerPosition[0]].childNodes.item(updPlayerPosition[1]).className = "grid-unit player"; | |
} | |
player.position = playerPosition; | |
} | |
}else console.log("top border of the dungeon is reached"); | |
} | |
//console.log(player); | |
initFogOfWar(); | |
} | |
// set key events to handle the game | |
$(document).keydown((event)=>{ | |
event.preventDefault(); | |
if (event.keyCode == 37){ | |
console.log('left arrow triggered'); | |
movePlayer('left'); | |
}else if (event.keyCode == 38){ | |
console.log('up arrow triggered'); | |
movePlayer('up'); | |
}else if (event.keyCode == 39){ | |
console.log('right arrow triggered'); | |
movePlayer('right'); | |
}else if (event.keyCode == 40){ | |
console.log('down arrow triggered'); | |
movePlayer('down'); | |
} | |
}); | |
function toggleFogOfWar(){ | |
console.log('toggle fog of war'); | |
$('#fog').toggleClass('active'); | |
initFogOfWar(); | |
return false; | |
} | |
$('#fog').on('click', toggleFogOfWar); | |
})(); | |
$('#info-toggler').bind('click', ()=>{ | |
if ($('.alert').css('display') === 'none') { | |
$('.alert').removeClass('fadeOut').addClass('fadeIn'); | |
$('.alert').css('display','block'); | |
}else $('.alert').css('display','none'); | |
}); | |
$('#dismiss-info').bind('click', ()=>{ | |
$('.alert').css('display','none'); | |
}); | |
}); |
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
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.js"></script> | |
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> |
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
$black: #000000 | |
$white: #ffffff | |
$yellow: #ffff00 | |
$grey: #bfbfbf | |
$blue: #33ccff | |
$green: #33cc33 | |
$red: #ff0000 | |
$pink: #ff8080 | |
$fog: #e6e6e6 | |
body | |
color: $black | |
font-family: 'Play', sans-serif | |
font-size: 2.2em | |
overflow-x:hidden | |
.nopadding | |
padding: 0 | |
.alert | |
z-index: 10 | |
.navbar-brand | |
font-size: 1em | |
.alert | |
display: none | |
position: fixed | |
top: 15vh | |
left: 5vw | |
right: 5vw | |
max-height: 80vh | |
overflow-y: scroll | |
font-size: 3vh | |
a:hover | |
text-decoration: none | |
.home | |
min-height: 100vh | |
padding-top: 45px | |
#output | |
text-align: center | |
width: 100% | |
height: auto !important | |
#user-instructions | |
text-align: center | |
font-size: 0.75em | |
margin-top: 1em | |
.legend | |
font-size: 0.65em | |
.marker | |
display: inline-block | |
width: 10px | |
height: 10px | |
border: 1px $black solid | |
#player-mrk | |
background-color: $yellow | |
#obstacle-mrk | |
background-color: $grey | |
#npc-mrk | |
background-color: $pink | |
#boss-mrk | |
background-color: $red | |
#weapon-mrk | |
background-color: $green | |
#potion-mrk | |
background-color: $blue | |
.hidden | |
display: none | |
#fog | |
border-radius: 0.4em 0.4em 0em 0em | |
.cllps-toggler | |
display: block | |
#char-stats | |
font-size: 0.8em | |
#level, #weapon, #xp, #hp, #attack, #next-level | |
margin: 0.5em | |
#grid | |
background-color: $black | |
tr, td | |
border-color: $grey | |
.row | |
.grid-unit | |
width: 5px | |
height: 5px | |
padding: 0px | |
.player | |
background-color: $yellow | |
.obstacle | |
background-color: $grey | |
.npc | |
background-color: $pink | |
.boss | |
background-color: $red | |
.weapon | |
background-color: $green | |
.potion | |
background-color: $blue | |
.grid-unit:hover | |
background-color: $white | |
#grid-fog | |
background-color: transparent | |
position: relative | |
z-index: 100 | |
border-color: transparent | |
tr, td | |
border-color: transparent | |
.row | |
.grid-unit | |
width: 5px | |
height: 5px | |
padding: 0px | |
background-color: transparent | |
.fog | |
background-color: $fog | |
.table | |
margin-bottom: 0px | |
border-color: $black | |
#dialog | |
background-color: $black | |
text-align: left | |
color: $green | |
margin-bottom: 20px | |
font-size: 0.65em | |
#dialog-marker | |
margin-left: 0.5em | |
margin-right: 0.5em | |
#dialog-output | |
font-weight: bold | |
a | |
text-decoration: none |
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
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" /> | |
<link href="//cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.3/animate.min.css" rel="stylesheet" /> | |
<link href="https://fonts.googleapis.com/css?family=Play" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment