Skip to content

Instantly share code, notes, and snippets.

@rfprod
Last active April 22, 2017 15:50
Show Gist options
  • Save rfprod/f8434927fd10346ee1d3 to your computer and use it in GitHub Desktop.
Save rfprod/f8434927fd10346ee1d3 to your computer and use it in GitHub Desktop.
Roguelike Dungeon Crawler
<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">&times;</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>

Roguelike Dungeon Crawler

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.

Deployed on Push2

A Pen by V on CodePen.

License.

$(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>&nbsp;
<div className="marker" id="obstacle-mrk"></div> <span className="legend">obstacle</span>&nbsp;
<div className="marker" id="npc-mrk"></div> <span className="legend">npc</span>&nbsp;
<div className="marker" id="boss-mrk"></div> <span className="legend">boss</span>&nbsp;
<div className="marker" id="weapon-mrk"></div> <span className="legend">weapon</span>&nbsp;
<div className="marker" id="potion-mrk"></div> <span className="legend">potion</span>&nbsp;
</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&nbsp;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');
});
});
<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>
$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
<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