Skip to content

Instantly share code, notes, and snippets.

@EntranceJew
Last active November 25, 2017 09:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EntranceJew/da2e954547df31b7d8cd8846b1730840 to your computer and use it in GitHub Desktop.
Save EntranceJew/da2e954547df31b7d8cd8846b1730840 to your computer and use it in GitHub Desktop.
attempt to restore some stuff
Game.Objects.Factory.minigameName = "Dungeon";
Game.Objects.Factory.minigameUrl = "https://rawgit.com/EntranceJew/da2e954547df31b7d8cd8846b1730840/raw/eadd47ab72612a8cb6efa8cc3e20e64e64e455f9/minigameDungeons.js"
Game.LoadMinigames();
// you could also set up the above and wrap the entire script with Game.LoadMod(), making the "M" object actually set itself up entirely
// this would include assigning Game.Objects.Factory.minigame
var M = {};
M.parent = Game.Objects['Temple'];
M.parent.minigame = M;
M.launch = function () {
var M = this;
M.name = M.parent.minigameName;
M.init = function (div) {
//populate div with html and initialize values
M.ourDiv = div;
// == NEW DUNGEON STUFF
M.dungeonTiles = [
['wall', [1, 0], 'join'],
['wall corner', [1, 0]],
['floor', [1, 1], 'random3'],
['tiled floor', [1, 2], 'join'],
['round pillar', [1, 4]],
['square pillar', [2, 4]],
['potted plant', [3, 4]],
['bookshelf', [4, 5], 'join'],
['door', [1, 3], 'join'],
['alt wall', [4, 0], 'join'],
['alt wall corner', [4, 0]],
['alt floor', [4, 1], 'random3'],
['alt tiled floor', [4, 2], 'join'],
['alt round pillar', [4, 4]],
['alt square pillar', [5, 4]],
['alt potted plant', [6, 4]],
['alt bookshelf', [4, 6], 'join'],
['alt door', [4, 3], 'join'],
['water', [1, 5]],
['green water', [2, 5]],
['dark water', [3, 5]],
['wooden wall', [1, 7], 'join'],
['wooden floor', [1, 6], 'random3'],
['conveyor belt', [4, 7], 'join'],
['entrance', [0, 1]],
['alt entrance', [0, 3]],
['exit', [0, 2]],
['alt exit', [0, 4]]
];
M.heroes = [
{
name: 'Chip',
pic: 'girlscoutChip',
portrait: 'portraitChip',
icon: [1, 0],
stats: {
hp: 30,
hpm: 30,
might: 5,
guard: 5,
speed: 5,
dodge: 5,
luck: 5
},
dialogue: {
'intro': 'I\'m Chip! I just really like exploring stuff. Let\'s go have an adventure!',
'greeting': 'Hello there!|I\'m ready!|Where are we going today?|Adventure!',
'win': 'Take that!|Hah!|That\'s right.',
'entrance': 'Chipping in!|Welp, here goes nothing!|I wonder what I\'ll find!|Hey, this place is new!|This place seems familiar.|Let\'s make it happen.',
'completion': 'I\'m one smart cookie.|Oh yeah!|Let\'s explore some more!|That was easy!|That sure was fun!|I\'m not lost, am I?|More exploring? Sure, why not!',
'defeat': 'B-better luck next time.|That really hurt!|I yield! I yield!|That went badly.|No half-baked excuses next time.|I think I scraped my knee!|Owie.|Woopsie!',
'win against Sentient Furnace': 'The irony, it burns! (...it\'s funny because it was burning. And made of iron. ...Moving on.)',
'win against Ascended Baking Pod': 'Where is your pod now?|That was disturbing.',
},
},
{
name: 'Crumb',
pic: 'girlscoutCrumb',
portrait: 'portraitCrumb',
icon: [2, 0],
stats: {
hp: 25,
hpm: 25,
might: 5,
guard: 7,
speed: 4,
dodge: 4,
luck: 5
},
dialogue: {
'intro': 'I\'m Crumb. I look like this because of a baking accident when I was little. Big deal. At least now I don\'t get hurt as easily as others, I guess.',
'greeting': 'Hi there.|Ready for adventure, I guess.|Reporting for duty.',
'win': 'Oh sorry, did that hurt?|Should have moved out of the way.|Oops. My bad.',
'entrance': 'Let\'s do this, I guess.|Well, let\'s go...|I gotta go in there?|Are we really doing this?|I hope I won\'t get lost like last time.|Let\'s get this over with.',
'completion': 'I... I did it...|I\'m glad that\'s over.|What, there\'s more?|In I go, I guess.|It doesn\'t end, does it?|But it\'s dark in there.',
'defeat': 'I, uh, ouch.|Why does that always happen to me?|I\'m just no good, am I?|Oh no.|I\'m... I\'m not crying.|Well that wasn\'t fun at all.|I\'m sorry I failed you.|Please... make them go away...',
'meet Ascended Baking Pod': 'That thing shouldn\'t even be alive.|Is that where they all came from?',
'win against Ascended Baking Pod': 'Hm. Fascinating.'
}
},
{
name: 'Doe',
pic: 'girlscoutDoe',
portrait: 'portraitDoe',
icon: [3, 0],
stats: {
hp: 25,
hpm: 25,
might: 4,
guard: 4,
speed: 7,
dodge: 5,
luck: 5
},
dialogue: {
'intro': 'H-hey. Name\'s Doe. I\'m pretty fast. I uh, I promise I\'ll do my best.',
'greeting': 'H-hey.|Oh, uh, h-hi there.|C-can I join?',
'win': 'Th-that looks like it hurt... awesome...|D-did I do that?|N-neat... there\'s pieces everywhere.',
'entrance': 'Alright, let\'s do this!|I-if I really have to.|I-in there? By myself?|...won\'t you come with me this time?|H-here I go!',
'completion': 'Oh... oh my.|That\'s... I uh, I\'m glad.|Y-yeah that was real easy. Piece of pie!|T-too easy, right?|S-so many cookies...|Ooh? F-fascinating.',
'defeat': 'I-if you can\'t beat them... join them.|I-it\'s because I stutter, isn\'t it?|W-well that\'s just no good at all.|I, uh, I meant for that to happen.|H-how embarrassing.',
'meet Ascended Baking Pod': 'W-whoah... it\'s... magnificent...',
'win against Ascended Baking Pod': 'I\'m sorry, buddy.|I... I think I hurt it...|Oh no... I-I think I broke it...'
}
},
{
name: 'Lucky',
pic: 'girlscoutLucky',
portrait: 'portraitLucky',
icon: [4, 0],
stats: {
hp: 25,
hpm: 25,
might: 5,
guard: 4,
speed: 4,
dodge: 5,
luck: 7
},
dialogue: {
'intro': 'Oh joy! My name\'s Lucky. Guess what I\'m good at?',
'greeting': 'I\'m feeling lucky!|It\'s a bright day today!|Let\'s do great things together.',
'win': 'Ooh lucky shot!|Pow! One more.|Damn straight!',
'entrance': 'Glad to be of service!|Oooh this one\'ll be interesting.|This will be a good one, I can feel it!|Here I come!',
'completion': 'Over already?|Let\'s explore some more!|That was lucky!|That was no luck, I\'m just that good.|Alright, let\'s move on!|I\'m just getting warmed up!',
'defeat': 'I can\'t believe it!|...This is a joke, right?|Hey! No fair!|B-but...|I\'m gonna need a bandaid. And some hot chocolate.|I\'ll, uh, try again later.|Bad luck! Bad luck!',
'win against Ascended Baking Pod': 'Golly, that was peculiar.'
}
}
];
//external
Game.GetWord = function (type) {
if (type == 'secret') return choose(['hidden', 'secret', 'mysterious', 'forgotten', 'forbidden', 'lost', 'sunk', 'buried', 'concealed', 'shrouded', 'invisible', 'elder']);
if (type == 'ruined') return choose(['ancient', 'old', 'ruined', 'ravaged', 'destroyed', 'collapsed', 'demolished', 'burnt', 'torn-down', 'shattered', 'dilapidated', 'abandoned', 'crumbling', 'derelict', 'decaying']);
if (type == 'magical') return choose(['arcane', 'magical', 'mystical', 'sacred', 'honed', 'banished', 'unholy', 'holy', 'demonic', 'enchanted', 'necromantic', 'bewitched', 'haunted', 'occult', 'astral']);
return '';
}
Game.DungeonTypes = [];
Game.DungeonType = function (name) {
this.name = name;
this.nameGenerator = function () {
return 'Mysterious dungeon';
};
this.roomTypes = [];
Game.DungeonTypes[this.name] = this;
return this;
};
/*=====================================================================================
CREATE DUNGEON TYPES
=======================================================================================*/
new Game.DungeonType('Factory').nameGenerator = function () {
var str = '';
str += Game.GetWord(choose(['secret', 'ruined', 'magical'])) + ' ' + choose(['factory', 'factories', 'bakery', 'bakeries', 'confectionery', 'laboratory', 'research center', 'chocolate forge', 'chocolate foundry', 'manufactory', 'warehouse', 'machinery', 'works', 'bakeworks', 'workshop', 'assembly line']);
return str;
};
new Game.DungeonType('Mine').nameGenerator = function () {
var str = '';
str += Game.GetWord(choose(['secret', 'ruined', 'magical'])) + ' ' + choose(['chocolate', 'chocolate', 'chocolate', 'white chocolate', 'sugar', 'cacao']) + ' ' + choose(['mine', 'mines', 'pit', 'pits', 'quarry', 'excavation', 'tunnel', 'shaft', 'lode', 'trench', 'mountain', 'vein', 'cliff', 'peak', 'dome', 'crater', 'abyss', 'chasm', 'hole', 'burrow']);
return str;
};
new Game.DungeonType('Portal').nameGenerator = function () {
var str = '';
str += Game.GetWord(choose(['secret', 'ruined', 'magical'])) + ' ' + choose(['portal', 'gate', 'dimension', 'warpgate', 'door']);
return str;
};
new Game.DungeonType('Secret zebra level').nameGenerator = function () {
var str = '';
str += Game.GetWord(choose(['secret'])) + ' ' + choose(['zebra level']);
return str;
};
Game.DungeonGen = function () {
var TILE_EMPTY = 0;//solid
var TILE_LIMIT = -100;//can't build anything here; edges of map
var TILE_FLOOR_EDGE = 100;
var TILE_FLOOR_CENTER = 110;
var TILE_DOOR = 200;
var TILE_PILLAR = 300;//not just pillars, could be any type of repetitive decoration
var TILE_WATER = 400;
var TILE_WALL = 500;
var TILE_WALL_CORNER = 510;
var TILE_ENTRANCE = 250;
var TILE_EXIT = 260;
var colors = [];
colors[TILE_EMPTY] = '000';
colors[TILE_LIMIT] = '900';
colors[TILE_FLOOR_EDGE] = 'ffc';
colors[TILE_FLOOR_CENTER] = 'ff9';
colors[TILE_DOOR] = 'f9f';
colors[TILE_PILLAR] = '990';
colors[TILE_WATER] = '99f';
colors[TILE_WALL] = '960';
colors[TILE_WALL_CORNER] = '630';
colors[TILE_ENTRANCE] = 'f9f';
colors[TILE_EXIT] = 'f9f';
var rand = function (a, b) {
return Math.floor(Math.random() * (b - a + 1) + a);
}//return random value between a and b
var Patterns = [];
this.Pattern = function (name, func) {
this.name = name;
this.func = func;
Patterns.push(this);
}
new this.Pattern('Pillars', function (x, y, room) {
if ((x + room.x) % 2 == 0 && (y + room.y) % 2 == 0 && Math.random() < 0.8) return TILE_PILLAR;
return 0;
});
new this.Pattern('Large pillars', function (x, y, room) {
if ((x + room.x) % 3 < 2 && (y + room.y) % 3 < 2 && Math.random() < 0.8) return TILE_PILLAR;
return 0;
});
new this.Pattern('Sparse pillars', function (x, y, room) {
if ((x + room.x) % 3 == 0 && (y + room.y) % 3 == 0 && Math.random() < 0.8) return TILE_PILLAR;
return 0;
});
new this.Pattern('Lines', function (x, y, room) {
if (room.x % 2 == 0) if ((x + room.x) % 2 == 0 && Math.random() < 0.98) return TILE_PILLAR;
if (room.x % 2 == 1) if ((y + room.y) % 2 == 0 && Math.random() < 0.98) return TILE_PILLAR;
return 0;
});
var getRandomPattern = function () {
return choose(Patterns);
}
var defaultGenerator = function (me) {
me.roomSize = 10;
me.corridorSize = 5;
me.fillRatio = 1 / 3;
me.corridorRatio = 0.2;
me.pillarRatio = 0.2;
me.waterRatio = 0;
me.branching = 4;
me.sizeVariance = 0.2;
me.fillRatio = 0.1 + Math.random() * 0.4;
me.roomSize = Math.ceil(rand(5, 15) * me.fillRatio * 2);
me.corridorSize = Math.ceil(rand(1, 7) * me.fillRatio * 2);
me.corridorRatio = Math.random() * 0.8 + 0.1;
me.pillarRatio = Math.random() * 0.5 + 0.5;
me.waterRatio = Math.pow(Math.random(), 2);
me.branching = Math.floor(Math.random() * 6);
me.sizeVariance = Math.random();
}
this.Map = function (w, h, seed, params) {
//create a new map
//leave the seed out for a random seed
//params is an object that contains custom parameters as defined in defaultGenerator
//example : MyMap=new DungeonGen.Map(30,30,MySeed,{waterRatio:0.8}); (80 percent of the rooms will contain water)
if (undefined != seed) this.seed = seed; else {
Math.seedrandom();
this.seed = Math.random();
}
Math.seedrandom(this.seed);
this.seedState = Math.random;
this.w = w || 20;
this.h = h || 20;
this.roomsAreHidden = 0;
this.rooms = [];
this.freeWalls = [];//all walls that would be a good spot for a door
this.freeTiles = [];//all passable floor tiles
this.doors = [];
this.tiles = this.w * this.h;
this.tilesDug = 0;
this.digs = 0;//amount of digging steps
this.stuck = 0;//how many times we ran into a problem; stop digging if we get too many of these
this.data = [];//fill the map with 0
for (var x = 0; x < this.w; x++) {
this.data[x] = [];
for (var y = 0; y < this.h; y++) {
this.data[x][y] = [TILE_EMPTY, -1, 0];//data is stored as [tile system type,room id,tile displayed type] (-1 is no room)
if (x == 0 || y == 0 || x == this.w - 1 || y == this.h - 1) this.data[x][y] = [TILE_LIMIT, -1, 0];
}
}
defaultGenerator(this);
if (params) {
for (var i in params) {
this[i] = params[i];
}
}
Math.seedrandom();
}
this.Map.prototype.getType = function (x, y) {
return this.data[x][y][0];
}
this.Map.prototype.getRoom = function (x, y) {
if (this.data[x][y][1] != -1) return this.rooms[this.data[x][y][1]]; else return -1;
}
this.Map.prototype.getTile = function (x, y) {
return this.rooms[this.data[x][y][2]];
}
this.Map.prototype.isWall = function (x, y) {
var n = 0;
for (var i in this.freeWalls) {
if (this.freeWalls[i][0] == x && this.freeWalls[i][1] == y) return n; else n++;
}
return -1;
}
this.Map.prototype.isFloor = function (x, y) {
var n = 0;
for (var i in this.freeTiles) {
if (this.freeTiles[i][0] == x && this.freeTiles[i][1] == y) return n; else n++;
}
return -1;
}
this.Map.prototype.removeFreeTile = function (x, y) {
this.freeTiles.splice(this.isFloor(x, y), 1);
}
this.Map.prototype.fill = function (what) {
//fill with something (either a set value, or a function that takes the map, a position X and a position Y as arguments)
//NOTE : this also resets the rooms!
//example : MyMap.fill(function(m,x,y){return Math.floor((Math.random());});
//...will fill the map with 0s and 1s
var func = 0;
if (typeof(what) == 'function') func = 1;
for (var x = 0; x < this.w; x++) {
for (var y = 0; y < this.h; y++) {
if (func) this.data[x][y] = [what(this, x, y), -1, 0]; else this.data[x][y] = [what, -1, 0];
}
}
this.rooms = [];
}
this.Map.prototype.fillZone = function (X, Y, W, H, what) {
//just plain fill a rectangle
for (var x = X; x < X + W; x++) {
for (var y = Y; y < Y + H; y++) {
this.data[x][y][0] = what;
}
}
}
this.Map.prototype.getRoomTile = function (room, x, y) {
var n = 0;
for (var i in room.tiles) {
if (room.tiles[i].x == x && room.tiles[i].y == y) return n; else n++;
}
return -1;
}
this.Map.prototype.getFloorTileInRoom = function (room) {
var tiles = [];
for (var i in room.tiles) {
if (room.tiles[i].type == TILE_FLOOR_EDGE || room.tiles[i].type == TILE_FLOOR_CENTER) tiles.push(room.tiles[i]);
}
return choose(tiles);
}
this.Map.prototype.canPlaceRoom = function (rx, ry, rw, rh) {
if (rx < 2 || ry < 2 || rx + rw >= this.w - 1 || ry + rh >= this.h - 1) return false;
for (var x = rx; x < rx + rw; x++) {
for (var y = ry; y < ry + rh; y++) {
var tile = this.getType(x, y);
var room = this.getRoom(x, y);
if (tile == TILE_LIMIT) return false;
if (room != -1) return false;
}
}
return true;
}
this.Map.prototype.setRoomTile = function (room, x, y, tile) {
//var mapTile=this.getType(x,y);
var oldTile = this.getRoomTile(room, x, y);
var oldTileType = oldTile != -1 ? room.tiles[oldTile].type : -1;
if (oldTile != -1 && (
//(tile!=TILE_FLOOR_EDGE && tile!=TILE_FLOOR_CENTER) ||// && (oldTileType!=TILE_FLOOR_EDGE && oldTileType!=TILE_FLOOR_CENTER)) ||
//(tile!=TILE_FLOOR_EDGE && tile!=TILE_FLOOR_CENTER && (oldTileType!=TILE_FLOOR_EDGE && oldTileType!=TILE_FLOOR_CENTER)) ||
(tile == TILE_WALL || tile == TILE_WALL_CORNER) ||//don't place a wall over an existing room
(tile == TILE_FLOOR_EDGE && oldTileType == TILE_FLOOR_CENTER)//don't place an edge floor over a center floor
)) {
return false;
}
else {
if (oldTile != -1) room.tiles.splice(oldTile, 1);
room.tiles.push({x: x, y: y, type: tile, score: 0});
if ((tile == TILE_FLOOR_EDGE || tile == TILE_FLOOR_CENTER) && (oldTileType != TILE_FLOOR_EDGE && oldTileType != TILE_FLOOR_CENTER)) room.freeTiles++;
else if (tile != TILE_FLOOR_EDGE && tile != TILE_FLOOR_CENTER && (oldTileType == TILE_FLOOR_EDGE || oldTileType == TILE_FLOOR_CENTER)) room.freeTiles--;
return true;
}
}
this.Map.prototype.expandRoom = function (room, rx, ry, rw, rh) {
var x = 0;
var y = 0;
//floor
for (var x = rx; x < rx + rw; x++) {
for (var y = ry; y < ry + rh; y++) {
this.setRoomTile(room, x, y, TILE_FLOOR_EDGE);
}
}
for (var x = rx + 1; x < rx + rw - 1; x++) {
for (var y = ry + 1; y < ry + rh - 1; y++) {
this.setRoomTile(room, x, y, TILE_FLOOR_CENTER);
}
}
//walls
y = ry - 1;
for (var x = rx; x < rx + rw; x++) {
this.setRoomTile(room, x, y, TILE_WALL);
}
y = ry + rh;
for (var x = rx; x < rx + rw; x++) {
this.setRoomTile(room, x, y, TILE_WALL);
}
x = rx - 1;
for (var y = ry; y < ry + rh; y++) {
this.setRoomTile(room, x, y, TILE_WALL);
}
x = rx + rw;
for (var y = ry; y < ry + rh; y++) {
this.setRoomTile(room, x, y, TILE_WALL);
}
//corners
x = rx - 1;
y = ry - 1;
this.setRoomTile(room, x, y, TILE_WALL_CORNER);
x = rx + rw;
y = ry - 1;
this.setRoomTile(room, x, y, TILE_WALL_CORNER);
x = rx - 1;
y = ry + rh;
this.setRoomTile(room, x, y, TILE_WALL_CORNER);
x = rx + rw;
y = ry + rh;
this.setRoomTile(room, x, y, TILE_WALL_CORNER);
//decoration
var water = Math.random() < this.waterRatio ? 1 : 0;
var pattern = Math.random() < this.pillarRatio ? getRandomPattern() : 0;
for (var x = rx; x < rx + rw; x++) {
for (var y = ry; y < ry + rh; y++) {
if (room.tiles[this.getRoomTile(room, x, y)].type == TILE_FLOOR_CENTER) {
var tile = 0;
if (water != 0) tile = TILE_WATER;
if (pattern != 0) {
tile = pattern.func(x, y, room) || tile;
}
if (tile != 0) this.setRoomTile(room, x, y, tile);
}
}
}
}
this.Map.prototype.newRoom = function (x, y, w, h, parent) {
//create a new abstract room, ready to be carved
var room = {};
room.id = this.rooms.length;
room.w = w;//||rand(2,this.roomSize);
room.h = h;//||rand(2,this.roomSize);
room.x = x || rand(1, this.w - room.w - 1);
room.y = y || rand(1, this.h - room.h - 1);
room.tiles = [];
room.freeTiles = 0;
room.parent = parent ? parent : -1;
room.children = [];
room.gen = 0;
room.door = 0;
room.corridor = Math.random() < this.corridorRatio ? 1 : 0;
room.hidden = this.roomsAreHidden;//if 1, don't draw
//if (room.parent!=-1) room.corridor=!room.parent.corridor;//alternate rooms and corridors
return room;
}
this.Map.prototype.planRoom = function (room) {
var branches = this.branching + 1;
var forcedExpansions = [];
var w = room.w;
var h = room.h;
while (w > 0 && h > 0) {
if (w > 0) {
forcedExpansions.push(1, 3);
w--;
}
if (h > 0) {
forcedExpansions.push(2, 4);
h--;
}
}
for (var i = 0; i < branches; i++) {
var steps = 0;
var expansions = [];
if (!room.corridor) {
expansions = [1, 2, 3, 4];
steps = this.roomSize;
}
else {
expansions = choose([[1, 3], [2, 4]]);
steps = this.corridorSize;
}
steps = Math.max(room.w + room.h, Math.ceil(steps * (1 - Math.random() * this.sizeVariance)));
if (room.tiles.length == 0) {
var rx = room.x;
var ry = room.y;
var rw = 1;
var rh = 1;
}
else {
var randomTile = this.getFloorTileInRoom(room);
var rx = randomTile.x;
var ry = randomTile.y;
var rw = 1;
var rh = 1;
}
for (var ii = 0; ii < steps; ii++) {
if (expansions.length == 0) break;
var xd = 0;
var yd = 0;
var wd = 0;
var hd = 0;
var side = choose(expansions);
if (forcedExpansions.length > 0) side = forcedExpansions[0];
if (side == 1) {
xd = -1;
wd = 1;
}
else if (side == 2) {
yd = -1;
hd = 1;
}
else if (side == 3) {
wd = 1;
}
else if (side == 4) {
hd = 1;
}
if (this.canPlaceRoom(rx + xd, ry + yd, rw + wd, rh + hd)) {
rx += xd;
ry += yd;
rw += wd;
rh += hd;
} else expansions.splice(expansions.indexOf(side), 1);
if (forcedExpansions.length > 0) forcedExpansions.splice(0, 1);
}
if (rw > 1 || rh > 1) {
this.expandRoom(room, rx, ry, rw, rh);
}
}
}
this.Map.prototype.carve = function (room) {
//carve a room into the map
for (var i in room.tiles) {
var thisTile = room.tiles[i];
var x = thisTile.x;
var y = thisTile.y;
var myType = this.data[x][y][0];
var type = thisTile.type;
if ((type == TILE_WALL || type == TILE_WALL_CORNER) && this.isWall(x, y) != -1) {
this.freeWalls.splice(this.isWall(x, y), 1);
}
if (this.data[x][y][1] != -1 && (type == TILE_WALL || type == TILE_WALL_CORNER)) {
}
else {
if (this.data[x][y][1] == -1) this.tilesDug++;
this.data[x][y] = [thisTile.type, room.id, 0];
if (x > 1 && y > 1 && x < this.w - 2 && y < this.h - 2 && type == TILE_WALL) this.freeWalls.push([x, y]);
if (type == TILE_FLOOR_EDGE || type == TILE_FLOOR_CENTER) this.freeTiles.push([x, y]);
}
var pos = [x, y];
}
this.rooms[room.id] = room;
}
this.Map.prototype.newRandomRoom = function (params) {
var success = 1;
params = params || {};//params is an object such as {corridor:1}
var door = choose(this.freeWalls);//select a free wall to use as a door
if (!door) {
success = 0;
}
else {
//this.data[door[0]][door[1]][0]=TILE_LIMIT;//not door
var parentRoom = this.getRoom(door[0], door[1]);
var sides = [];//select a free side of that door
if (this.getType(door[0] - 1, door[1]) == TILE_EMPTY) sides.push([-1, 0]);
if (this.getType(door[0] + 1, door[1]) == TILE_EMPTY) sides.push([1, 0]);
if (this.getType(door[0], door[1] - 1) == TILE_EMPTY) sides.push([0, -1]);
if (this.getType(door[0], door[1] + 1) == TILE_EMPTY) sides.push([0, 1]);
var side = choose(sides);
if (!side) {
success = 0;
this.freeWalls.splice(this.isWall(door[0], door[1]), 1);
}
else {
var room = this.newRoom(door[0] + side[0], door[1] + side[1], 0, 0, parentRoom);//try a new room from this spot
for (var i in params) {
room[i] = params[i];
}
this.planRoom(room);
if (room.tiles.length > 0 && room.freeTiles > 0)//we got a decent room
{
this.carve(room);
this.data[door[0]][door[1]][0] = TILE_DOOR;//place door
room.door = [door[0], door[1]];
this.data[door[0]][door[1]][1] = room.id;//set ID
this.freeWalls.splice(this.isWall(door[0], door[1]), 1);//the door isn't a wall anymore
this.doors.push([door[0], door[1], room]);
//remove free tiles on either side of the door
if (this.isFloor(door[0] + side[0], door[1] + side[1]) != -1) this.removeFreeTile(door[0] + side[0], door[1] + side[1]);
if (this.isFloor(door[0] - side[0], door[1] - side[1]) != -1) this.removeFreeTile(door[0] - side[0], door[1] - side[1]);
room.parent = parentRoom;
parentRoom.children.push(room);
room.gen = parentRoom.gen + 1;
}
else//not a good spot; remove this tile from the list of walls
{
this.freeWalls.splice(this.isWall(door[0], door[1]), 1);
success = 0;
}
}
}
if (success) return room;
else return 0;
}
this.Map.prototype.getRandomSpotInRoom = function (room) {
var listOfTiles = [];
for (var i in room.tiles) {
if ((room.tiles[i].type == TILE_FLOOR_EDGE || room.tiles[i].type == TILE_FLOOR_CENTER) && this.isFloor(room.tiles[i].x, room.tiles[i].y) != -1) {
listOfTiles.push(room.tiles[i]);
}
}
if (listOfTiles.length == 0) return -1;
return choose(listOfTiles);
}
this.Map.prototype.getBestSpotInRoom = function (room) {
var highest = -1;
var listOfHighest = [];
for (var i in room.tiles) {
if ((room.tiles[i].type == TILE_FLOOR_EDGE || room.tiles[i].type == TILE_FLOOR_CENTER) && this.isFloor(room.tiles[i].x, room.tiles[i].y) != -1) {
if (room.tiles[i].score > highest) {
listOfHighest = [];
highest = room.tiles[i].score;
listOfHighest.push(room.tiles[i]);
}
else if (room.tiles[i].score == highest) {
listOfHighest.push(room.tiles[i]);
}
}
}
if (listOfHighest.length == 0) return -1;
return choose(listOfHighest);
}
this.Map.prototype.getEarliestRoom = function () {
return this.rooms[0];
}
this.Map.prototype.getDeepestRoom = function () {
var deepest = 0;
var deepestRoom = this.rooms[0];
for (var i in this.rooms) {
if ((this.rooms[i].gen + Math.sqrt(this.rooms[i].freeTiles) * 0.05) >= deepest && this.rooms[i].corridor == 0 && this.rooms[i].freeTiles > 4) {
deepest = (this.rooms[i].gen + Math.sqrt(this.rooms[i].freeTiles) * 0.05);
deepestRoom = this.rooms[i];
}
}
return deepestRoom;
}
this.Map.prototype.dig = function () {
//one step in which we try to carve new stuff
//returns 0 when we couldn't dig this step, 1 when we could, and 2 when the digging is complete
Math.random = this.seedState;
var badDig = 0;
if (this.digs == 0)//first dig : build a starting room in the middle of the map
{
var w = rand(3, 7);
var h = rand(3, 7);
var room = this.newRoom(Math.floor(this.w / 2 - w / 2), Math.floor(this.h / 2 - h / 2), w, h);
room.corridor = 0;
this.planRoom(room);
this.carve(room);
}
else {
if (this.newRandomRoom() == 0) badDig++;
}
if (badDig > 0) this.stuck++;
this.digs++;
var finished = 0;
if (this.tilesDug >= this.tiles * this.fillRatio) finished = 1;
if (this.stuck > 100) finished = 1;
if (finished == 1)//last touch : try to add a whole room at the end
{
for (var i = 0; i < 10; i++) {
var newRoom = this.newRandomRoom({corridor: 0, w: rand(3, 7), h: rand(3, 7)});
if (newRoom != 0 && newRoom.freeTiles > 15) break;
}
}
Math.seedrandom();
if (finished == 1) return 1; else if (badDig > 0) return -1; else return 0;
}
this.Map.prototype.finish = function () {
//touch up the map : add pillars in corners etc
/*
//set paths
for (var i in this.rooms)
{
var me=this.rooms[i];
if (me.door!=0)
{
var doors=[];
doors.push(me.door);
for (var ii in me.children)
{
if (me.children[ii].door!=0) doors.push(me.children[ii].door);
}
for (var ii in doors)
{
this.data[doors[ii][0]][doors[ii][1]][0]=TILE_LIMIT;
//ideally we should run agents that step from each door to the next
}
}
}
*/
for (var i in this.rooms) {
var pillars = Math.random() < this.pillarRatio;
for (var ii in this.rooms[i].tiles) {
var x = this.rooms[i].tiles[ii].x;
var y = this.rooms[i].tiles[ii].y;
var me = this.data[x][y][0];
var x1 = this.data[x - 1][y][0];
var x2 = this.data[x + 1][y][0];
var y1 = this.data[x][y - 1][0];
var y2 = this.data[x][y + 1][0];
var xy1 = this.data[x - 1][y - 1][0];
var xy2 = this.data[x + 1][y - 1][0];
var xy3 = this.data[x - 1][y + 1][0];
var xy4 = this.data[x + 1][y + 1][0];
var walls = 0;
if ((x1 == TILE_WALL || x1 == TILE_WALL_CORNER)) walls++;
if ((y1 == TILE_WALL || y1 == TILE_WALL_CORNER)) walls++;
if ((x2 == TILE_WALL || x2 == TILE_WALL_CORNER)) walls++;
if ((y2 == TILE_WALL || y2 == TILE_WALL_CORNER)) walls++;
if ((xy1 == TILE_WALL || xy1 == TILE_WALL_CORNER)) walls++;
if ((xy2 == TILE_WALL || xy2 == TILE_WALL_CORNER)) walls++;
if ((xy3 == TILE_WALL || xy3 == TILE_WALL_CORNER)) walls++;
if ((xy4 == TILE_WALL || xy4 == TILE_WALL_CORNER)) walls++;
var floors = 0;
if ((x1 == TILE_FLOOR_CENTER || x1 == TILE_FLOOR_EDGE)) floors++;
if ((y1 == TILE_FLOOR_CENTER || y1 == TILE_FLOOR_EDGE)) floors++;
if ((x2 == TILE_FLOOR_CENTER || x2 == TILE_FLOOR_EDGE)) floors++;
if ((y2 == TILE_FLOOR_CENTER || y2 == TILE_FLOOR_EDGE)) floors++;
if ((xy1 == TILE_FLOOR_CENTER || xy1 == TILE_FLOOR_EDGE)) floors++;
if ((xy2 == TILE_FLOOR_CENTER || xy2 == TILE_FLOOR_EDGE)) floors++;
if ((xy3 == TILE_FLOOR_CENTER || xy3 == TILE_FLOOR_EDGE)) floors++;
if ((xy4 == TILE_FLOOR_CENTER || xy4 == TILE_FLOOR_EDGE)) floors++;
var complete = 0;
if (walls + floors == 8) complete = 1;
var angle = 0;
if (complete) {
var top = 0;
var left = 0;
var right = 0;
var bottom = 0;
if ((xy1 == TILE_WALL || xy1 == TILE_WALL_CORNER) && (y1 == TILE_WALL || y1 == TILE_WALL_CORNER) && (xy2 == TILE_WALL || xy2 == TILE_WALL_CORNER)) top = 1;
else if ((xy1 == TILE_FLOOR_CENTER || xy1 == TILE_FLOOR_EDGE) && (y1 == TILE_FLOOR_CENTER || y1 == TILE_FLOOR_EDGE) && (xy2 == TILE_FLOOR_CENTER || xy2 == TILE_FLOOR_EDGE)) top = -1;
if ((xy2 == TILE_WALL || xy2 == TILE_WALL_CORNER) && (x2 == TILE_WALL || x2 == TILE_WALL_CORNER) && (xy4 == TILE_WALL || xy4 == TILE_WALL_CORNER)) right = 1;
else if ((xy2 == TILE_FLOOR_CENTER || xy2 == TILE_FLOOR_EDGE) && (x2 == TILE_FLOOR_CENTER || x2 == TILE_FLOOR_EDGE) && (xy4 == TILE_FLOOR_CENTER || xy4 == TILE_FLOOR_EDGE)) right = -1;
if ((xy1 == TILE_WALL || xy1 == TILE_WALL_CORNER) && (x1 == TILE_WALL || x1 == TILE_WALL_CORNER) && (xy3 == TILE_WALL || xy3 == TILE_WALL_CORNER)) left = 1;
else if ((xy1 == TILE_FLOOR_CENTER || xy1 == TILE_FLOOR_EDGE) && (x1 == TILE_FLOOR_CENTER || x1 == TILE_FLOOR_EDGE) && (xy3 == TILE_FLOOR_CENTER || xy3 == TILE_FLOOR_EDGE)) left = -1;
if ((xy3 == TILE_WALL || xy3 == TILE_WALL_CORNER) && (y2 == TILE_WALL || y2 == TILE_WALL_CORNER) && (xy4 == TILE_WALL || xy4 == TILE_WALL_CORNER)) bottom = 1;
else if ((xy3 == TILE_FLOOR_CENTER || xy3 == TILE_FLOOR_EDGE) && (y2 == TILE_FLOOR_CENTER || y2 == TILE_FLOOR_EDGE) && (xy4 == TILE_FLOOR_CENTER || xy4 == TILE_FLOOR_EDGE)) bottom = -1;
if ((top == 1 && bottom == -1) || (top == -1 && bottom == 1) || (left == 1 && right == -1) || (left == -1 && right == 1)) angle = 1;
}
if (pillars && Math.random() < 0.8 && this.rooms[i].freeTiles > 4) {
if ((angle == 1 || (complete && walls == 7)) && me == TILE_FLOOR_EDGE && x1 != TILE_DOOR && x2 != TILE_DOOR && y1 != TILE_DOOR && y2 != TILE_DOOR) {
this.data[x][y][0] = TILE_PILLAR;
me = TILE_PILLAR;
this.removeFreeTile(x, y);
this.rooms[i].freeTiles--;
}
}
//calculate score (for placing items and exits)
if (top == 1 || bottom == 1 || left == 1 || right == 1) {
this.rooms[i].tiles[ii].score += 2;
}
if (walls > 5 || floors > 5) {
this.rooms[i].tiles[ii].score += 1;
}
if (walls == 7 || floors == 8) {
this.rooms[i].tiles[ii].score += 5;
}
if ((me != TILE_FLOOR_CENTER && me != TILE_FLOOR_EDGE) || x1 == TILE_DOOR || x2 == TILE_DOOR || y1 == TILE_DOOR || y2 == TILE_DOOR) this.rooms[i].tiles[ii].score = -1;
}
}
//carve entrance and exit
var entrance = this.getBestSpotInRoom(this.getEarliestRoom());
this.data[entrance.x][entrance.y][0] = TILE_ENTRANCE;
this.entrance = [entrance.x, entrance.y];
entrance.score = 0;
this.removeFreeTile(entrance.x, entrance.y);
var exit = this.getBestSpotInRoom(this.getDeepestRoom());
this.data[exit.x][exit.y][0] = TILE_EXIT;
this.exit = [exit.x, exit.y];
this.removeFreeTile(exit.x, exit.y);
exit.score = 0;
/*
for (var i in this.doors)//remove door tiles (to add later; replace the tiles by entities that delete themselves when opened)
{
this.data[this.doors[i][0]][this.doors[i][1]][0]=TILE_FLOOR_EDGE;
}
*/
}
this.Map.prototype.isObstacle = function (x, y) {
var free = [TILE_FLOOR_EDGE, TILE_FLOOR_CENTER, TILE_DOOR, TILE_ENTRANCE, TILE_EXIT];
for (var i in free) {
if (this.data[x][y][0] == free[i]) return 0;
}
return 1;
}
var joinTile = function (map, x, y, joinWith) {
//for the tile at x,y, return 2 if it joins with its horizontal neighbors, 3 if it joins with its vertical neighbors, 1 if it joins with either both or neither.
//joinWith contains the tile types that count as joinable, in addition to this tile. (don't add the tested tile to joinWith!)
var p = 1;
var me = map.data[x][y][0];
var x1 = map.data[x - 1][y][0];
var x2 = map.data[x + 1][y][0];
var y1 = map.data[x][y - 1][0];
var y2 = map.data[x][y + 1][0];
joinWith.push(me);
var joinsX = 0;
for (var i in joinWith) {
if (x1 == joinWith[i]) joinsX++;
if (x2 == joinWith[i]) joinsX++;
}
var joinsY = 0;
for (var i in joinWith) {
if (y1 == joinWith[i]) joinsY++;
if (y2 == joinWith[i]) joinsY++;
}
if (joinsX == 2 && joinsY == 2) p = 1;
else if (joinsX == 2) p = 2;
else if (joinsY == 2) p = 3;
return p;
}
this.Map.prototype.getPic = function (x, y) {
//return a position [x,y] in the tiles (as 0, 1, 2...) for the tile on the map at position x,y
if (Tiles[this.data[x][y][2]]) {
if (Tiles[this.data[x][y][2]].joinType == 'join') {
var thisPic = Tiles[this.data[x][y][2]].pic;
thisPic = [thisPic[0], thisPic[1]];//why is this even necessary?
var joinWith = [];
if (this.data[x][y][0] == TILE_WALL) joinWith.push(TILE_WALL_CORNER);
else if (this.data[x][y][0] == TILE_DOOR) joinWith.push(TILE_WALL, TILE_WALL_CORNER);
thisPic[0] += joinTile(this, x, y, joinWith) - 1;
return thisPic;
}
else if (Tiles[this.data[x][y][2]].joinType == 'random3') {
var thisPic = Tiles[this.data[x][y][2]].pic;
thisPic = [thisPic[0], thisPic[1]];
thisPic[0] += Math.floor(Math.random() * 3);
return thisPic;
}
return Tiles[this.data[x][y][2]].pic;
}
return [0, 0];
}
var Tiles = [];
var TilesByName = [];
this.Tile = function (name, pic, joinType) {
this.name = name;
this.pic = pic;
this.joinType = joinType || 'none';
this.id = Tiles.length;
Tiles[this.id] = this;
TilesByName[this.name] = this;
}
new this.Tile('void', [0, 0]);
this.loadTiles = function (tiles) {
for (var i in tiles) {
var name = tiles[i][0];
var pic = tiles[i][1];
var joinType = tiles[i][2];
new this.Tile(name, pic, joinType);
}
}
var computeTile = function (tile, tiles, value, name) {
if (tile == value && tiles[name]) return TilesByName[tiles[name]];
return 0;
}
this.Map.prototype.assignTiles = function (room, tiles) {
//set the displayed tiles for this room
for (var i in room.tiles) {
var type = Tiles[0];
var me = room.tiles[i];
var tile = this.data[me.x][me.y][0];
type = computeTile(tile, tiles, TILE_WALL_CORNER, 'wall corner') || type;
type = computeTile(tile, tiles, TILE_WALL, 'wall') || type;
type = computeTile(tile, tiles, TILE_FLOOR_EDGE, 'floor edges') || type;
type = computeTile(tile, tiles, TILE_FLOOR_CENTER, 'floor') || type;
type = computeTile(tile, tiles, TILE_PILLAR, 'pillar') || type;
type = computeTile(tile, tiles, TILE_DOOR, 'door') || type;
type = computeTile(tile, tiles, TILE_WATER, 'water') || type;
type = computeTile(tile, tiles, TILE_ENTRANCE, 'entrance') || type;
type = computeTile(tile, tiles, TILE_EXIT, 'exit') || type;
this.data[me.x][me.y][2] = type.id;
}
}
this.Map.prototype.draw = function (size) {
//return a string containing a rough visual representation of the map
var str = '';
var size = size || 10;
for (var y = 0; y < this.h; y++) {
for (var x = 0; x < this.w; x++) {
var text = '';
if (this.isFloor(x, y) != -1) text = 'o';
if (this.isWall(x, y) != -1) text += 'x';
var room = this.getRoom(x, y);
var opacity = Math.max(0.1, 1 - (this.getRoom(x, y).gen / 10));
var title = room.freeTiles;//this.data[x][y][0].toString();
text = '';
str += '<div style="opacity:' + opacity + ';width:' + size + 'px;height:' + size + 'px;position:absolute;left:' + (x * size) + 'px;top:' + (y * size) + 'px;display:block;padding:0px;margin:0px;background:#' + colors[this.data[x][y][0]] + ';color:#999;" title="' + title + '">' + text + '</div>';
}
str += '<br>';
}
str = '<div style="position:relative;width:' + (this.w * size) + 'px;height:' + (this.h * size) + 'px;background:#000;font-family:Courier;font-size:' + size + 'px;float:left;margin:10px;">' + str + '</div>';
return str;
}
this.Map.prototype.drawDetailed = function () {
//return a string containing a rough visual representation of the map (with graphics)
var str = '';
var size = 16;
for (var y = 0; y < this.h; y++) {
for (var x = 0; x < this.w; x++) {
var room = this.getRoom(x, y);
//var opacity=Math.max(0.1,room.tiles[this.getRoomTile(room,x,y)].score);
var opacity = 1;
var title = 'void';
if (room != -1) {
opacity = Math.max(0.1, 1 - room.gen / 5);
if (this.data[x][y][0] == TILE_ENTRANCE || this.data[x][y][0] == TILE_EXIT) opacity = 1;
title = (room.corridor ? 'corridor' : 'room') + ' ' + room.id + ' | depth : ' + room.gen + ' | children : ' + room.children.length;
}
var pic = this.getPic(x, y);
str += '<div style="opacity:' + opacity + ';width:' + size + 'px;height:' + size + 'px;position:absolute;left:' + (x * size) + 'px;top:' + (y * size) + 'px;display:block;padding:0px;margin:0px;background:#' + colors[this.data[x][y][0]] + ' url(img/dungeonTiles.png) ' + (-pic[0] * 16) + 'px ' + (-pic[1] * 16) + 'px;color:#999;" title="' + title + '"></div>';
}
str += '<br>';
}
str = '<div style="box-shadow:0px 0px 12px 6px #00061b;position:relative;width:' + (this.w * size) + 'px;height:' + (this.h * size) + 'px;background:#00061b;font-family:Courier;font-size:' + size + 'px;float:left;margin:10px;">' + str + '</div>';
return str;
}
this.Map.prototype.getStr = function () {
//return a string containing the map with tile graphics, ready to be pasted in a wrapper
var str = '';
var size = 16;
for (var y = 0; y < this.h; y++) {
for (var x = 0; x < this.w; x++) {
var room = this.getRoom(x, y);
//var opacity=Math.max(0.1,room.tiles[this.getRoomTile(room,x,y)].score);
var opacity = 1;
var title = 'void';
var pic = this.getPic(x, y);
if (room != -1) {
/*
opacity=Math.max(0.1,1-room.gen/5);
if (room.hidden) opacity=0;
if (this.data[x][y][0]==TILE_ENTRANCE || this.data[x][y][0]==TILE_EXIT) opacity=1;
*/
if (room.hidden) pic = [0, 0];
title = (room.corridor ? 'corridor' : 'room') + ' ' + room.id + ' | depth : ' + room.gen + ' | children : ' + room.children.length;
}
str += '<div style="opacity:' + opacity + ';width:' + size + 'px;height:' + size + 'px;position:absolute;left:' + (x * size) + 'px;top:' + (y * size) + 'px;display:block;padding:0px;margin:0px;background:#' + colors[this.data[x][y][0]] + ' url(img/dungeonTiles.png) ' + (-pic[0] * 16) + 'px ' + (-pic[1] * 16) + 'px;color:#999;" title="' + title + '"></div>';
}
str += '<br>';
}
return str;
}
}
/*=====================================================================================
CREATE MONSTER TYPES
=======================================================================================*/
/*
An explanation of stats :
-hp : health points
-speed : determines who attacks first in a fight; bypasses dodging; determines how fast heroes auto-run dungeons
-might : determines how much damage is done to opponents
-guard : lowers incoming damage
-dodge : chance of avoiding incoming attacks completely (affected by the opponent's speed)
-luck : heroes only, determines drops and rare encounters
-rarity : monsters only, determines how often a monster is added to the spawn table
-level : monsters only, determines which average room depth the monster is more likely to spawn in (also determines the loot amount)
*/
Game.monsterIconY = 10;//offset for dungeonItems.png monsters
Game.Monsters = [];
Game.Monster = function (name, pic, icon, level, stats, loot) {
this.name = name;
this.pic = pic;
this.icon = icon;
this.level = level;
this.stats = {};
for (var i in stats) {
this.stats[i] = stats[i];
}
this.stats.hpm = this.stats.hp;
this.stats.rarity = stats.rarity || 1;
this.loot = loot || {};
this.boss = 0;
this.quotes = {};
Game.Monsters[this.name] = this;
}
var basicLoot = {cookies: {min: 1, max: 5, prob: 0.5}};
var goodLoot = {cookies: {min: 3, max: 8, prob: 1}, gear: {prob: 0.05}};
var bossLoot = {gear: {prob: 1}};
var chestLoot = {cookies: {min: 2, max: 20, prob: 1}, gear: {prob: 0.1}};
var bossLoot = {cookies: {min: 10, max: 50, prob: 1}, gear: {prob: 0.2}};
//general monsters
new Game.Monster('Doughling', 'doughling', [0, 0], 1, {
hp: 5,
might: 2,
guard: 2,
speed: 6,
dodge: 6,
rarity: 0.7
}, basicLoot);
new Game.Monster('Elder doughling', 'elderDoughling', [1, 0], 7, {
hp: 20,
might: 7,
guard: 7,
speed: 4,
dodge: 4,
rarity: 0.7
}, goodLoot);
new Game.Monster('Angry sentient cookie', 'angrySentientCookie', [5, 0], 5, {
hp: 16,
might: 8,
guard: 4,
speed: 5,
dodge: 5,
rarity: 1
}, basicLoot);
new Game.Monster('Baby sentient cookie', 'babySentientCookie', [4, 0], 1, {
hp: 3,
might: 1,
guard: 1,
speed: 7,
dodge: 7,
rarity: 1
}, basicLoot);
new Game.Monster('Burnt sentient cookie', 'burntSentientCookie', [6, 0], 5, {
hp: 16,
might: 12,
guard: 2,
speed: 3,
dodge: 2,
rarity: 0.2
}, basicLoot);
new Game.Monster('Raw sentient cookie', 'rawSentientCookie', [5, 0], 5, {
hp: 16,
might: 6,
guard: 4,
speed: 7,
dodge: 7,
rarity: 0.2
}, basicLoot);
new Game.Monster('Sugar bunny', 'sugarBunny', [8, 0], 5, {
hp: 10,
might: 3,
guard: 8,
speed: 12,
dodge: 9,
rarity: 0.001
}, {cookies: {min: 1000, max: 10000}});
Game.Monsters['Sugar bunny'].onKill = function () {
Game.Win('Follow the white rabbit');
};
Game.Monsters['Sugar bunny'].AI = 'flee';
//factory monsters
new Game.Monster('Crazed kneader', 'crazedKneader', [0, 2], 6, {
hp: 18,
might: 6,
guard: 8,
speed: 3,
dodge: 2,
rarity: 0.5
}, goodLoot);
new Game.Monster('Crazed chip-spurter', 'crazedDoughSpurter', [0, 2], 6, {
hp: 15,
might: 6,
guard: 8,
speed: 5,
dodge: 3,
rarity: 0.5
}, goodLoot);
new Game.Monster('Alarm bot', 'alarmTurret', [3, 2], 2, {
hp: 6,
might: 3,
guard: 5,
speed: 8,
dodge: 8,
rarity: 0.5
}, basicLoot);
new Game.Monster('Chirpy', 'chirpy', [4, 2], 3, {
hp: 7,
might: 4,
guard: 6,
speed: 9,
dodge: 9,
rarity: 0.01
}, {cookies: {min: 500, max: 5000}});
Game.Monsters['Chirpy'].onKill = function () {
Game.Win('Chirped out');
};
Game.Monsters['Chirpy'].quotes = {fight: 'oh, hello <3'};
new Game.Monster('Disgruntled worker', 'disgruntledWorker', [1, 2], 4, {
hp: 14,
might: 5,
guard: 5,
speed: 6,
dodge: 4,
rarity: 0.6
}, basicLoot);
new Game.Monster('Disgruntled overseer', 'disgruntledOverseer', [1, 2], 7, {
hp: 22,
might: 7,
guard: 5,
speed: 6,
dodge: 4,
rarity: 0.5
}, basicLoot);
new Game.Monster('Disgruntled cleaning lady', 'disgruntledCleaningLady', [2, 2], 4, {
hp: 13,
might: 4,
guard: 5,
speed: 7,
dodge: 6,
rarity: 0.3
}, basicLoot);
new Game.Monster('Sentient Furnace', 'sentientFurnace', [0, 3], 0, {
hp: 60,
might: 14,
guard: 12,
speed: 4,
dodge: 0,
rarity: 1
}, bossLoot);//boss
Game.Monsters['Sentient Furnace'].onKill = function () {
Game.Win('Getting even with the oven');
};
Game.Monsters['Sentient Furnace'].AI = 'static';
Game.Monsters['Sentient Furnace'].boss = 1;
Game.Monsters['Sentient Furnace'].quotes = {fight: 'YOU ARE NOT READY!', defeat: 'OH... BURN.'};
new Game.Monster('Ascended Baking Pod', 'ascendedBakingPod', [1, 3], 0, {
hp: 60,
might: 12,
guard: 14,
speed: 4,
dodge: 0,
rarity: 0.7
}, bossLoot);//boss
Game.Monsters['Ascended Baking Pod'].onKill = function () {
Game.Win('Now this is pod-smashing');
};
Game.Monsters['Ascended Baking Pod'].AI = 'static';
Game.Monsters['Ascended Baking Pod'].boss = 1;
Game.Monsters['Ascended Baking Pod'].quotes = {fight: 'rrrrrrrise.', defeat: 'blrglblg.'};
Game.BossMonsters = [];
for (var i in Game.Monsters) {
if (Game.Monsters[i].boss) Game.BossMonsters.push(Game.Monsters[i]);
}
/*=====================================================================================
ENTITY MECHANICS
=======================================================================================*/
Game.Entity = function (type, subtype, dungeon, pic, stats)//objects you could find on the map : doors, mobs, interactables, items, player, exits...
{
this.type = type;
this.subtype = subtype || '';
this.dungeon = dungeon;
this.pic = pic || [0, 0];
this.stats = {};
for (var i in stats) {
this.stats[i] = stats[i];
}
this.x = -1;
this.y = -1;
this.obstacle = 0;
this.zIndex = 1;
if (this.type == 'monster') {
this.obstacle = 1;
this.pic = [Game.Monsters[this.subtype].icon[0], Game.Monsters[this.subtype].icon[1]];
this.pic[1] += Game.monsterIconY;
this.targets = [];
this.stuck = 0;
this.zIndex = 10;
this.fighting = 0;
this.AI = Game.Monsters[this.subtype].AI || 'normal';
this.onKill = Game.Monsters[this.subtype].onKill || function () {
};
for (var i in Game.Monsters[this.subtype].stats) {
this.stats[i] = Game.Monsters[this.subtype].stats[i];
}
}
else if (this.type == 'hero') {
this.obstacle = 1;
this.pic = [Game.Heroes[this.subtype].icon[0], Game.Heroes[this.subtype].icon[1]];
this.targets = [];
this.stuck = 0;
this.zIndex = 100;
this.fighting = 0;
for (var i in Game.Heroes[this.subtype].stats) {
this.stats[i] = Game.Heroes[this.subtype].stats[i];
}
//increase stats by amount of matching building (change that later to use gear instead)
var mult = Math.max(0, (Game.Objects[this.dungeon.type].amount / 20 - 1));
this.stats.hpm += Math.ceil(mult * 2);
this.stats.hp = this.stats.hpm;
this.stats.might += mult;
this.stats.guard += mult;
this.stats.speed += mult;
this.stats.dodge += mult;
}
else if (this.type == 'item') {
this.zIndex = 5;
this.value = 0;
}
else if (this.type == 'destructible')//crates, doors
{
this.obstacle = 1;
this.life = 3;
this.zIndex = 15;
if (this.subtype == 'door') this.pic = [0, 7];
else this.pic = [Math.floor(Math.random() * 4 + 2), 7];
this.onKill = function () {
if (this.subtype == 'random') {
var value = Math.round(Math.pow(Math.random(), 6) * (10 + this.dungeon.level));
if (value > 0) {
var entity = this.dungeon.AddEntity('item', 'cookies', this.x, this.y);
entity.value = value;
}
}
}
}
else if (this.type == 'special') {
this.zIndex = 5;
this.value = '';
this.obstacle = 1;
}
this.Say = function (what) {
if (this.type == 'monster') {
if (Game.Monsters[this.subtype].quotes[what]) this.dungeon.Log(this.subtype + ' : "<span style="color:#f96;">' + choose(Game.Monsters[this.subtype].quotes[what].split('|')) + '</span>"');
}
}
this.Draw = function ()//return the string to draw this
{
var name = '?';
if (this.subtype == 'random') name = 'clutter'; else name = this.subtype;
if (this.type == 'item' && this.subtype == 'cookies' && this.value > 0) {
if (this.value < 2) this.pic = [0, 5];
else if (this.value < 3) this.pic = [1, 5];
else if (this.value < 4) this.pic = [2, 5];
else if (this.value < 6) this.pic = [3, 5];
else if (this.value < 10) this.pic = [4, 5];
else if (this.value < 20) this.pic = [5, 5];
else if (this.value < 30) this.pic = [7, 5];
else if (this.value < 70) this.pic = [6, 5];
else if (this.value < 200) this.pic = [8, 5];
else this.pic = [6, 6];// if (this.value<1000) this.pic=[1,5];
}
else if (this.type == 'special' && this.subtype == 'upgrade') {
if (this.value != '') this.pic = [7, 6]; else this.pic = [8, 6];
}
return '<div class="thing" title="' + name + '" style="z-index:' + (200 + this.zIndex) + ';left:' + (this.x * 16) + 'px;top:' + (this.y * 16) + 'px;background-position:' + (-this.pic[0] * 16) + 'px ' + (-this.pic[1] * 16) + 'px;"></div>';
}
this.Wander = function ()//AI to move around aimlessly
{
this.targets = [];
this.targets.push([-1, 0], [1, 0], [0, -1], [0, 1]);
this.Move();
}
this.GoTo = function (x, y)//AI to move to a specific point
{
this.targets = [];
if (this.x < x) this.targets.push([1, 0]);
if (this.x > x) this.targets.push([-1, 0]);
if (this.y < y) this.targets.push([0, 1]);
if (this.y > y) this.targets.push([0, -1]);
if (!this.Move())//really stuck? try to maneuver laterally!
{
this.targets = [];
if (this.x == x) this.targets.push([1, 0], [-1, 0]);//somehow this feels inverted... but it doesn't work the other way
if (this.y == y) this.targets.push([0, 1], [0, -1]);//hypothesis : *MAGIC*
this.Move();
}
}
this.Flee = function (x, y)//AI to run away from a specific point
{
this.targets = [];
if (this.x > x) this.targets.push([1, 0]);
if (this.x < x) this.targets.push([-1, 0]);
if (this.y > y) this.targets.push([0, 1]);
if (this.y < y) this.targets.push([0, -1]);
if (!this.Move())//really stuck? try to maneuver laterally!
{
this.targets = [];
if (this.x == x) this.targets.push([1, 0], [-1, 0]);//somehow this feels inverted... but it doesn't work the other way
if (this.y == y) this.targets.push([0, 1], [0, -1]);//hypothesis : *MAGIC*
this.Move();
}
}
this.Move = function ()//AI to move to the target
{
if (this.targets.length > 0) {
var goodTargets = [];
if (this.type == 'hero') goodTargets = this.targets;
else {
for (var i in this.targets) {
var thisTarget = this.targets[i];
if (this.dungeon.CheckObstacle(this.x + thisTarget[0], this.y + thisTarget[1]) != -1) goodTargets.push([thisTarget[0], thisTarget[1]]);
}
}
if (goodTargets.length > 0) {
var target = choose(goodTargets);
var obstacle = this.dungeon.CheckObstacle(this.x + target[0], this.y + target[1]);
if (obstacle == this) obstacle = 0;
if (obstacle == 0 && this.AI != 'static') {
this.x += target[0];
this.y += target[1];
}
else this.stuck += 2;
if (obstacle != 0 && obstacle != -1) {
obstacle.HitBy(this);
}
if (obstacle == -1) return 0;
}
else {
this.stuck += 2;
return 0;
}
if (this.AI == 'static') this.stuck = 0;
return 1;
}
return 0;
}
this.HitBy = function (by)//attacked by another entity
{
if (this.type == 'destructible' && by.type == 'hero')//break destructibles
{
by.stuck = 0;
this.life--;
if (this.life <= 0) {
if (this.onKill) this.onKill();
this.Destroy();
}
else this.pic = [this.pic[0], this.pic[1] + 1];
}
else if (this.type == 'special' && this.subtype == 'upgrade')//upgrade relic
{
this.obstacle = 0;
if (Game.Upgrades[this.value]) Game.Upgrades[this.value].earn();
this.value = '';
}
else if ((this.type == 'monster' && by.type == 'hero') || (this.type == 'hero' && by.type == 'monster') && this.stats.hp > 0)//it's a fight!
{
by.stuck = 0;
var monster = (this.type == 'hero' ? by : this);
var hero = (this.type == 'hero' ? this : by);
this.dungeon.currentOpponent = monster;
if (monster.fighting == 0)//first meeting
{
Game.Heroes[hero.subtype].Say('meet ' + Game.Monsters[monster.subtype].name);
this.Say('fight');
}
if (this.fighting == 0) {
this.fighting = 1;
by.fighting = 1;
}
var attackStr = '';
var attackerName = '';
var defenderName = '';
if (by.type == 'hero') attackerName = Game.Heroes[by.subtype].name;
else if (by.type == 'monster') attackerName = Game.Monsters[by.subtype].name;
if (this.type == 'hero') defenderName = Game.Heroes[this.subtype].name;
else if (this.type == 'monster') defenderName = Game.Monsters[this.subtype].name;
//battle formulas (have fun with these)
attackStr += attackerName + ' swings at ' + defenderName + '!';
var damage = Math.round(Math.max(1, Math.min(by.stats.might, Math.pow(((by.stats.might + 2.5) / Math.max(1, this.stats.guard)), 2))) * (0.8 + Math.random() * 0.4 + Math.pow(Math.random() * 0.8, 6)));
var dodge = Math.random() > (by.stats.speed / Math.max(1, this.stats.dodge + 2.5));
if (dodge) {
attackStr += ' ' + defenderName + ' dodged the attack.';
}
else {
if (by.stats.luck && by.type == 'hero' && Math.random() < by.stats.luck * 0.01) {
damage *= 2;
attackStr += ' <b>It\'s a critical!</b>';
}//very rare critical based on luck
attackStr += ' <b>' + damage + '</b> damage!';
this.stats.hp -= damage;
this.stats.hp = Math.max(this.stats.hp, 0);
if (this.stats.luck && this.type == 'hero') {
if (this.stats.hp == 0 && Math.random() < this.stats.luck * 0.01) {
this.stats.hp = 1;
attackStr += ' ' + defenderName + ' was saved from certain death!';
}//very rare life-saving based on luck
}
}
if (this.type == 'hero') attackStr = '<span style="color:#f99;">' + attackStr + '</span>';
if (attackStr != '') this.dungeon.Log(attackStr);
if (this.stats.hp <= 0)//die
{
this.dungeon.Log(attackerName + ' crushed ' + defenderName + '!');
if (this.type == 'hero') {
Game.Heroes[this.subtype].Say('defeat');
this.dungeon.Log('<span style="color:#f66;">' + Game.Heroes[this.subtype].name + ' has been defeated.</span>');
this.dungeon.FailLevel();
}
if (this.type == 'monster' && by.type == 'hero') {
l('monsterSlot' + this.dungeon.id).style.visibility = 'hidden';
this.dungeon.monstersKilledThisRun += 1;
if (Math.random() < 0.05) Game.Heroes[by.subtype].Say('win');
Game.Heroes[by.subtype].Say('win against ' + Game.Monsters[this.subtype].name);
this.Say('defeat');
if (Game.Monsters[this.subtype].loot) {
var loot = Game.Monsters[this.subtype].loot;
if (loot.gear && (!loot.gear.prob || Math.random() < loot.gear.prob)) {
}//drop gear
if (loot.cookies && (!loot.cookies.prob || Math.random() < loot.cookies.prob)) {
var entity = this.dungeon.AddEntity('item', 'cookies', this.x, this.y);//drop cookies
entity.value = Math.round(loot.cookies.min + Math.random() * (loot.cookies.max - loot.cookies.min));
}
}
if (this.onKill) this.onKill();
this.Destroy();
}
}
}
}
this.Turn = function ()//do this every turn (walk around, heal up...)
{
if (this.type == 'monster') {
var howManyTurns = this.GetInitiative();
for (var i = 0; i < howManyTurns; i++) {
if (1 == 1)//this.AI!='static')
{
if (this.AI == 'flee') this.Flee(this.dungeon.heroEntity.x, this.dungeon.heroEntity.y);//flee from the player
else {
this.GoTo(this.dungeon.heroEntity.x, this.dungeon.heroEntity.y);//track the player
if (this.stuck || this.targets.length == []) this.Wander();//can't reach the player? walk around randomly
}
}
}
}
if (this.type == 'monster' || this.type == 'hero') {
if (this.stuck > 0) this.stuck--;
this.stuck = Math.min(10, this.stuck);
this.targets = [];
}
if ((this.type == 'hero' || this.type == 'monster') && this.fighting == 0 && this.stats.hp < this.stats.hpm) this.stats.hp++;//heal up
if (this.type == 'hero')//collect items and cookies
{
var entities = this.dungeon.GetEntities(this.x, this.y);
for (var i in entities) {
if (entities[i].type == 'item' && entities[i].subtype == 'cookies') {
var entity = entities[i];
var value = Math.ceil(entity.value * Game.Objects[this.dungeon.type].amount * 50 * (1 + Math.random() * ((this.stats.luck) / 20)));//temporary; scale with matching building CpS later
if (value > 0) {
this.dungeon.Log('<span style="color:#9f9;">Found <b>' + Beautify(value) + '</b> cookie' + (value == 1 ? '' : 's') + '!</span>');
this.dungeon.cookiesMadeThisRun += value;
Game.Earn(value);
}
entity.Destroy();
}
}
}
if (this.type == 'hero') this.fighting = 0;
}
this.Destroy = function () {
this.dungeon.entities.splice(this.dungeon.entities.indexOf(this), 1);
}
this.GetInitiative = function () {
return randomFloor((this.stats.speed / 5) * (1 / Math.max(1, (this.dungeon.heroEntity.stats.speed / 5))));
}
}
/*=====================================================================================
DUNGEON MECHANICS
=======================================================================================*/
Game.Dungeons = [];
Game.Dungeon = function (type, id) {
this.type = type;
this.id = id;
Game.Dungeons[this.id] = this;
this.log = [];
this.logNew = 0;
this.name = Game.DungeonTypes[this.type].nameGenerator();
this.hero = null;
this.currentOpponent = 0;
this.level = 0;
this.auto = 1;
this.portalPic = '';
this.cookiesMadeThisRun = 0;
this.monstersKilledThisRun = 0;
this.Log = function (what, nested) {
if (typeof what === 'string') {
this.log.unshift(what);
this.logNew++;
}
else {
for (var i in what) {
this.Log(what[i], 1);
}
}
//if (!nested) this.UpdateLog();
}
this.UpdateLog = function () {
this.log = this.log.slice(0, 30);
var str = '';
for (var i in this.log) {
if (i < this.logNew) str += '<div class="new">' + this.log[i] + '</div>';
else str += '<div>' + this.log[i] + '</div>';
}
this.logNew = 0;
l('dungeonLog' + this.id).innerHTML = str;
}
this.entities = [];
this.GetEntities = function (x, y)//returns the first entity found on tile x,y
{
var entities = [];
for (var i in this.entities) {
if (this.entities[i].x == x && this.entities[i].y == y) entities.push(this.entities[i]);
}
return entities;
}
this.AddEntity = function (type, subtype, x, y) {
//this.RemoveEntities(x,y);
var entity = new Game.Entity(type, subtype, this);
entity.x = x;
entity.y = y;
entity.dungeon = this;
this.entities.push(entity);
return entity;
}
this.RemoveEntities = function (x, y) {
var entities = this.GetEntities(x, y);
for (var i in entities) {
entities[i].Destroy();
}
}
this.DrawEntities = function () {
var str = '';
for (var i in this.entities) {
str += this.entities[i].Draw();
}
return str;
}
this.CheckObstacle = function (x, y)//returns 0 for no obstacle; -1 for a wall; an entity if there's at least one entity on this tile
{
if (x < 0 || x >= this.map.w || y < 0 || y >= this.map.h) return -1;
var entities = this.GetEntities(x, y);
for (var i in entities) {
if (entities[i].obstacle) return entities[i];
}
return this.map.isObstacle(x, y) ? -1 : 0;
}
this.map = {};
this.Generate = function () {
if (this.level == 0) this.name = Game.DungeonTypes[this.type].nameGenerator();
this.entities = [];
var Gerald = new M.dungeonGen.Map(40, 40, Math.random(), {
roomSize: 10,
corridorSize: 5,
fillRatio: 1 / 2,
corridorRatio: 0.3,
pillarRatio: Math.random() * 0.8 + 0.2,
waterRatio: Math.random(),
branching: Math.ceil(Math.random() * 6),
sizeVariance: 0.4
});
r = 0;
while (r != 1) {
r = Gerald.dig();
}
//all done! decorate and render.
Gerald.finish();
//spawn treasure
/*
for (var i in M.rooms)
{
if (M.rooms[i].freeTiles>1)
{
for (var ii=0;ii<Math.ceil(Math.sqrt(M.rooms[i].freeTiles*(M.rooms[i].gen*0.25+0.1))/2);ii++)
{
if (Math.random()<0.95 && M.rooms[i].freeTiles>1)
{
var spot=M.getBestSpotInRoom(M.rooms[i]);
M.data[spot.x][spot.y][0]=0;
spot.score=0;
M.rooms[i].freeTiles--;
}
}
}
}*/
for (var i in Gerald.doors)//place door entities on door positions
{
//M.data[M.doors[i][0]][M.doors[i][1]][0]=TILE_FLOOR_EDGE;
this.AddEntity('destructible', 'door', Gerald.doors[i][0], Gerald.doors[i][1]);
}
//set tile graphics
for (var i in Gerald.rooms) {
var altStr = choose(['alt ', '', '']);
var tiles = {
'void': altStr + 'void',
'wall': altStr + 'wall',
'wall corner': altStr + 'wall corner',
'floor': altStr + 'tiled floor',
'floor edges': altStr + 'floor',//choose([altStr+'floor',altStr+'floor edges']),
'door': altStr + 'door',
'water': choose(['water', 'green water', 'dark water']),
'pillar': choose([altStr + 'wall', altStr + 'round pillar', altStr + 'square pillar', altStr + 'potted plant', 'conveyor belt']),
'entrance': altStr + 'entrance',
'exit': altStr + 'exit',
};
if (Math.random() < 0.1) {
tiles['wall corner'] = 'wooden wall';
tiles['wall'] = 'wooden wall';
tiles['floor edges'] = 'wooden floor';
tiles['pillar'] = 'wooden wall';
}
if (Math.random() < 0.1) {
tiles['wall corner'] = altStr + 'bookshelf';
tiles['wall'] = altStr + 'bookshelf';
tiles['pillar'] = altStr + 'bookshelf';
}
Gerald.assignTiles(Gerald.rooms[i], tiles);
}
this.map = Gerald;
this.map.str = this.map.getStr();
//place a boss
var tile = this.map.exit;
var monsters = [];
for (var ii in Game.BossMonsters) {
var me = Game.BossMonsters[ii];
if (me.level <= (depth + this.level) && Math.random() < (me.stats.rarity || 1)) monsters.push(me.name);
}
if (monsters.length == 0) monsters = [choose(Game.BossMonsters).name];
if (monsters.length > 0) {
this.AddEntity('monster', choose(monsters), tile[0], tile[1]);
this.map.removeFreeTile(tile[0], tile[1]);
}
//place relics
/*
var tile=this.map.getBestSpotInRoom(this.map.getRoom(this.map.exit[0],this.map.exit[1]));
var entity=this.AddEntity('special','upgrade',tile.x,tile.y);
entity.value='Dungeon cookie upgrade';
this.map.removeFreeTile(tile.x,tile.y);
for (var i=0;i<Math.floor(Math.pow(Math.random(),2)*3);i++)
{
var room=choose(this.map.rooms);
if (room.freeTiles.length>10)
{
var tile=this.map.getBestSpotInRoom(room);
var entity=this.AddEntity('special','upgrade',tile.x,tile.y);
entity.value='Dungeon cookie upgrade';
this.map.removeFreeTile(tile.x,tile.y);
}
}*/
//sprinkle monsters and treasure
for (var i = 0; i < Math.ceil(this.map.freeTiles.length * 0.7); i++)//let's fill this up with A LOT of stuff
{
var tile = choose(this.map.freeTiles);
if (tile != -1) {
var room = this.map.getRoom(tile[0], tile[1]);
var depth = room.gen + 1;
if (Math.random() < 0.2)//2 in 10 spawns are monsters
{
var monsters = [];
for (var ii in Game.Monsters) {
var me = Game.Monsters[ii];
if (me.level != 0 && me.level <= (depth + this.level) && Math.random() < (me.stats.rarity || 1)) monsters.push(me.name);//spawn type depending on monster level and rarity
}
if (monsters.length > 0) {
this.AddEntity('monster', choose(monsters), tile[0], tile[1]);
this.map.removeFreeTile(tile[0], tile[1]);
}
}
else//the rest of the spawns are destructibles or loot
{
if (Math.random() < 0.6) {
var value = Math.round(Math.pow(Math.random(), 6) * (10 + this.level));
if (value > 0) {
var entity = this.AddEntity('item', 'cookies', tile[0], tile[1]);//random cookies
entity.value = value;
}
}
else this.AddEntity('destructible', 'random', tile[0], tile[1]);//random crates etc
this.map.removeFreeTile(tile[0], tile[1]);
}
}
}
}
this.onTile = -1;
this.Draw = function () {
var str = '';
var x = -this.hero.x;
var y = -this.hero.y;
str += '<div id="map' + this.id + '" class="map" style="width:' + (9 * 16) + 'px;height:' + (9 * 16) + 'px;"><div class="mapContainer" id="mapcontainer' + this.id + '" style="position:absolute;left:' + (x * 16) + 'px;top:' + (y * 16) + 'px;"><div id="mapitems' + this.id + '"></div>' + this.map.str + '</div></div>';
str += '<div style="position:absolute;left:' + (9 * 16 + 16) + 'px;">' +
'<a class="control west" onclick="Game.HeroesById[' + this.hero.id + '].Move(-1,0);"></a><br>' +
'<a class="control east" onclick="Game.HeroesById[' + this.hero.id + '].Move(1,0);"></a><br>' +
'<a class="control north" onclick="Game.HeroesById[' + this.hero.id + '].Move(0,-1);"></a><br>' +
'<a class="control south" onclick="Game.HeroesById[' + this.hero.id + '].Move(0,1);"></a><br>' +
'<a class="control middle" onclick="Game.HeroesById[' + this.hero.id + '].Move(0,0);"></a><br>' +
'</div>';
str += '<div style="position:absolute;left:' + (9 * 16 + 16 + 48 * 3) + 'px;bottom:16px;height:100%;">' +
'<div class="dungeonName"><a onclick="Game.ObjectsById[' + this.id + '].setSpecial(0);">Exit</a> - <span class="title" style="font-size:12px;">' + this.name + '</span> lvl.' + (this.level + 1) + '</div>' +
'<div id="heroSlot' + this.id + '" class="mobSlot"><div id="picHero' + this.id + '" class="mobPic"></div><div id="nameHero' + this.id + '" class="title mobName"></div><div class="hpmBar"><div id="hpHero' + this.id + '" class="hpBar"></div></div></div>' +
'<div id="monsterSlot' + this.id + '" class="mobSlot" style="left:128px;"><div id="picMonster' + this.id + '" class="mobPic"></div><div id="nameMonster' + this.id + '" class="title mobName"></div><div class="hpmBar"><div id="hpMonster' + this.id + '" class="hpBar"></div></div></div>' +
'</div>' +
'<div id="dungeonLog' + this.id + '" class="dungeonLog"></div>';
l('rowSpecial' + this.id).innerHTML = '<div style="width:100%;height:100%;z-index:10000;position:absolute;left:0px;top:0px;">' + str + '</div>';
l('picHero' + this.id).style.backgroundImage = 'url(img/' + this.hero.portrait + '.png)';
l('nameHero' + this.id).innerHTML = this.hero.name;
}
this.Refresh = function () {
if (!l('mapcontainer' + this.id)) this.Draw();
var x = 4 - this.hero.x;
var y = 4 - this.hero.y;
l('mapcontainer' + this.id).style.left = (x * 16) + 'px';
l('mapcontainer' + this.id).style.top = (y * 16) + 'px';
l('mapitems' + this.id).innerHTML = this.DrawEntities();
}
this.RedrawMap = function () {
this.map.str = this.map.getStr();
this.Draw();
}
this.Turn = function () {
for (var i in this.entities) {
if (this.entities[i] && this.entities[i].type) this.entities[i].Turn();
}
if (this.currentOpponent) {
l('monsterSlot' + this.id).style.visibility = 'visible';
l('hpMonster' + this.id).style.width = Math.round((this.currentOpponent.stats.hp / this.currentOpponent.stats.hpm) * 100) + '%';
l('picMonster' + this.id).style.backgroundImage = 'url(img/' + Game.Monsters[this.currentOpponent.subtype].pic + '.png)';
l('nameMonster' + this.id).innerHTML = Game.Monsters[this.currentOpponent.subtype].name;
l('picHero' + this.id).style.backgroundImage = 'url(img/' + this.hero.pic + '.png)';
}
else {
l('monsterSlot' + this.id).style.visibility = 'hidden';
l('hpMonster' + this.id).style.width = '100%';
l('picHero' + this.id).style.backgroundImage = 'url(img/' + this.hero.portrait + '.png)';
}
this.currentOpponent = 0;
l('hpHero' + this.id).style.width = Math.round((this.heroEntity.stats.hp / this.heroEntity.stats.hpm) * 100) + '%';
this.Refresh();
this.UpdateLog();
if (this.hero.x == this.map.exit[0] && this.hero.y == this.map.exit[1]) {
this.CompleteLevel();
}
}
this.DrawButton = function () {
var str = '';
//str+='<div style="text-align:center;margin:48px auto;color:#999;"><a onclick="Game.ObjectsById['+this.id+'].setSpecial(1);">Enter</a></div>';
str += '<div style="width:144px;height:144px;position:absolute;left:0px;bottom:0px;"><a class="specialButtonPic" style="background-image:url(img/' + this.portalPic + '.png);" onclick="Game.ObjectsById[' + this.id + '].setSpecial(1);"><div class="specialButtonText">Enter dungeons</div></a></div>';
return str;
}
this.CompleteLevel = function () {
this.hero.Say('completion');
this.level++;
this.Generate();
Game.HeroesById[0].EnterDungeon(this, this.map.entrance[0], this.map.entrance[1]);
this.Draw();
}
this.FailLevel = function () {
this.Log('Cookies made this run : ' + Beautify(this.cookiesMadeThisRun) + ' | Monsters defeated this run : ' + Beautify(this.monstersKilledThisRun));
this.cookiesMadeThisRun = 0;
this.monstersKilledThisRun = 0;
this.level = 0;
this.Generate();
Game.HeroesById[0].EnterDungeon(this, this.map.entrance[0], this.map.entrance[1]);
this.Draw();
}
}
Game.DungeonLocationChain = function (map, x, y)//return an array of the rooms between the root room and this tile's room, inclusive
{//we shouldn't need all this if we used A*...
var room = map.getRoom(x, y);
var chain = [];
if (room != -1) {
while (room.parent) {
chain.push(room);
room = room.parent;
}
}
chain.reverse();
return chain;
}
Game.DungeonLinkLocationChains = function (start, end)//return the room in which the first location chain should go to to get closer to the second location chain
{
/*
4 cases
-we're already in the same room
-the target is in a different branch
-the target is above in the same branch
-the target is below in the same branch
*/
start.reverse();
end.reverse();
if (start[0].id == end[0].id) return start[start.length - 1];//same room
for (var i in end) {
if (start[0] == end[i].parent) return end[i];//inferior branch, go to the inferior room
}
if (start.length > 1) return start[1];//different or superior branch, go to the superior room
return start[0];//eeeh, let's just stay in the same room
}
/*=====================================================================================
CREATE DUNGEONS
=======================================================================================*/
Game.Objects['Factory'].special = function () {
M.dungeon = new Game.Dungeon('Factory', M.parent.id);
M.dungeon.Generate();
M.specialDrawFunction = function () {
this.dungeon.Refresh();
};
M.drawSpecialButton = function () {
return this.dungeon.DrawButton();
};
M.dungeon.timer = 0;
M.dungeon.timerWarmup = 5;
M.dungeon.portalPic = 'dungeonFactory';
M.EachFrame = function () {
if (M.dungeon.auto) {
if (M.dungeon.timer > 0) M.dungeon.timer--;
if (M.dungeon.timer == 0) {
M.dungeon.timer = Game.fps * (Math.max(0.1, 2 - (M.dungeon.hero.stats.speed * 0.2)) + Math.max(M.dungeon.timerWarmup, 0));
if (M.dungeon.timerWarmup > 0) M.dungeon.timerWarmup--;
var dungeon = M.dungeon;
var hero = dungeon.heroEntity;
var targetRoom = Game.DungeonLinkLocationChains(Game.DungeonLocationChain(dungeon.map, hero.x, hero.y), Game.DungeonLocationChain(dungeon.map, dungeon.map.exit[0], dungeon.map.exit[1]));
var targetTile = (targetRoom.gen == 0 || targetRoom.id == dungeon.map.getRoom(hero.x, hero.y).id) ? [dungeon.map.exit[0], dungeon.map.exit[1]] : targetRoom.door;
hero.GoTo(targetTile[0], targetTile[1]);
if (hero.stuck) hero.Wander();
dungeon.hero.x = hero.x;
dungeon.hero.y = hero.y;
dungeon.Turn();
}
}
}
if (document.addEventListener)//clean this up later
{
l('rowSpecial' + M.dungeon.id).removeEventListener('keydown', arguments.callee, false);
l('rowSpecial' + M.dungeon.id).addEventListener('keydown', function (event) {
var dungeon = Game.Objects['Factory'].dungeon;
var control = 0;
if (event.keyCode == 37) {
dungeon.hero.Move(-1, 0);
control = 1;
}
else if (event.keyCode == 38) {
dungeon.hero.Move(0, -1);
control = 1;
}
else if (event.keyCode == 39) {
dungeon.hero.Move(1, 0);
control = 1;
}
else if (event.keyCode == 40) {
dungeon.hero.Move(0, 1);
control = 1;
}
else if (event.keyCode == 32) {
dungeon.hero.Move(0, 0);
control = 1;
}//space
else if (event.keyCode == 65)//A (auto)
{
if (dungeon.auto) {
dungeon.auto = 0;
dungeon.timerWarmup = -1;
}
else {
dungeon.auto = 1;
dungeon.timer = 0;
dungeon.timerWarmup = 0;
}
event.preventDefault();
}
if (control) {
event.preventDefault();
dungeon.timer = Game.fps * 10;
dungeon.timerWarmup = 5;
}
}
);
}
var hero = choose(Game.HeroesById);
hero.EnterDungeon(this.dungeon, this.dungeon.map.entrance[0], this.dungeon.map.entrance[1]);
}
/*=====================================================================================
HEROES
=======================================================================================*/
Game.Heroes = [];
Game.HeroesById = [];
Game.Hero = function (name, pic, portrait, icon) {
this.name = name;
this.pic = pic;
this.portrait = portrait;
this.icon = icon;
this.stats = {
hp: 25,
hpm: 25,
might: 5,
guard: 5,
speed: 5,
dodge: 5,
luck: 5
};
this.dialogue = {
'greeting': 'Oh hey.|Sup.',
'entrance': 'Here we go.|So exciting.',
'completion': 'That was easy.|All done here.',
'defeat': 'Welp.|Better luck next time.'
};
this.gear = {
'armor': -1,
'weapon': -1
};
this.inDungeon = -1;
this.completedDungeons = 0;
this.x = 0;
this.y = 0;
this.EnterDungeon = function (dungeon, x, y) {
this.inDungeon = dungeon.id;
dungeon.hero = this;
this.x = x;
this.y = y;
dungeon.heroEntity = dungeon.AddEntity('hero', dungeon.hero.name, x, y);
var room = dungeon.map.getRoom(this.x, this.y);
if (room != -1 && room.hidden) {
room.hidden = 0;
dungeon.RedrawMap();
}
Game.Dungeons[this.inDungeon].Refresh();
dungeon.Log('--------------------');
if (dungeon.level == 0) this.Say('greeting');
this.Say('entrance');
l('monsterSlot' + dungeon.id).style.visibility = 'hidden';
}
this.Move = function (x, y) {
var dungeon = Game.Dungeons[this.inDungeon];
dungeon.heroEntity.targets = [[x, y]];
if (dungeon.heroEntity.Move()) {
this.x = dungeon.heroEntity.x;
this.y = dungeon.heroEntity.y;
dungeon.Turn();
}
}
this.Say = function (what) {
if (this.dialogue[what]) Game.Dungeons[this.inDungeon].Log(this.name + ' : "<span style="color:#99f;">' + choose(this.dialogue[what].split('|')) + '</span>"');
}
this.save = function () {
var str = '';
str +=
this.inDungeon + ',' +
this.completedDungeons + ',' +
this.gear.armor + ',' +
this.gear.weapon
;
return str;
}
this.load = function (data) {
var str = data.split(',');
this.inDungeon = parseInt(str[0]);
this.completedDungeons = parseInt(str[1]);
this.gear.armor = parseInt(str[2]);
this.gear.weapon = parseInt(str[3]);
}
this.id = Game.HeroesById.length;
Game.HeroesById.push(this);
Game.Heroes[this.name] = this;
}
/*=====================================================================================
CREATE HEROES
=======================================================================================*/
var hero = new Game.Hero('Chip', 'girlscoutChip', 'portraitChip', [1, 0]);
hero.dialogue = {
'intro': 'I\'m Chip! I just really like exploring stuff. Let\'s go have an adventure!',
'greeting': 'Hello there!|I\'m ready!|Where are we going today?|Adventure!',
'win': 'Take that!|Hah!|That\'s right.',
'entrance': 'Chipping in!|Welp, here goes nothing!|I wonder what I\'ll find!|Hey, this place is new!|This place seems familiar.|Let\'s make it happen.',
'completion': 'I\'m one smart cookie.|Oh yeah!|Let\'s explore some more!|That was easy!|That sure was fun!|I\'m not lost, am I?|More exploring? Sure, why not!',
'defeat': 'B-better luck next time.|That really hurt!|I yield! I yield!|That went badly.|No half-baked excuses next time.|I think I scraped my knee!|Owie.|Woopsie!',
'win against Sentient Furnace': 'The irony, it burns! (...it\'s funny because it was burning. And made of iron. ...Moving on.)',
'win against Ascended Baking Pod': 'Where is your pod now?|That was disturbing.'
};
hero.stats = {
hp: 30,
hpm: 30,
might: 5,
guard: 5,
speed: 5,
dodge: 5,
luck: 5
};
var hero = new Game.Hero('Crumb', 'girlscoutCrumb', 'portraitCrumb', [2, 0]);
hero.dialogue = {
'intro': 'I\'m Crumb. I look like this because of a baking accident when I was little. Big deal. At least now I don\'t get hurt as easily as others, I guess.',
'greeting': 'Hi there.|Ready for adventure, I guess.|Reporting for duty.',
'win': 'Oh sorry, did that hurt?|Should have moved out of the way.|Oops. My bad.',
'entrance': 'Let\'s do this, I guess.|Well, let\'s go...|I gotta go in there?|Are we really doing this?|I hope I won\'t get lost like last time.|Let\'s get this over with.',
'completion': 'I... I did it...|I\'m glad that\'s over.|What, there\'s more?|In I go, I guess.|It doesn\'t end, does it?|But it\'s dark in there.',
'defeat': 'I, uh, ouch.|Why does that always happen to me?|I\'m just no good, am I?|Oh no.|I\'m... I\'m not crying.|Well that wasn\'t fun at all.|I\'m sorry I failed you.|Please... make them go away...',
'meet Ascended Baking Pod': 'That thing shouldn\'t even be alive.|Is that where they all came from?',
'win against Ascended Baking Pod': 'Hm. Fascinating.'
};
hero.stats = {
hp: 25,
hpm: 25,
might: 5,
guard: 7,
speed: 4,
dodge: 4,
luck: 5
};
var hero = new Game.Hero('Doe', 'girlscoutDoe', 'portraitDoe', [3, 0]);
hero.dialogue = {
'intro': 'H-hey. Name\'s Doe. I\'m pretty fast. I uh, I promise I\'ll do my best.',
'greeting': 'H-hey.|Oh, uh, h-hi there.|C-can I join?',
'win': 'Th-that looks like it hurt... awesome...|D-did I do that?|N-neat... there\'s pieces everywhere.',
'entrance': 'Alright, let\'s do this!|I-if I really have to.|I-in there? By myself?|...won\'t you come with me this time?|H-here I go!',
'completion': 'Oh... oh my.|That\'s... I uh, I\'m glad.|Y-yeah that was real easy. Piece of pie!|T-too easy, right?|S-so many cookies...|Ooh? F-fascinating.',
'defeat': 'I-if you can\'t beat them... join them.|I-it\'s because I stutter, isn\'t it?|W-well that\'s just no good at all.|I, uh, I meant for that to happen.|H-how embarrassing.',
'meet Ascended Baking Pod': 'W-whoah... it\'s... magnificent...',
'win against Ascended Baking Pod': 'I\'m sorry, buddy.|I... I think I hurt it...|Oh no... I-I think I broke it...'
};
hero.stats = {
hp: 25,
hpm: 25,
might: 4,
guard: 4,
speed: 7,
dodge: 5,
luck: 5
};
var hero = new Game.Hero('Lucky', 'girlscoutLucky', 'portraitLucky', [4, 0]);
hero.dialogue = {
'intro': 'Oh joy! My name\'s Lucky. Guess what I\'m good at?',
'greeting': 'I\'m feeling lucky!|It\'s a bright day today!|Let\'s do great things together.',
'win': 'Ooh lucky shot!|Pow! One more.|Damn straight!',
'entrance': 'Glad to be of service!|Oooh this one\'ll be interesting.|This will be a good one, I can feel it!|Here I come!',
'completion': 'Over already?|Let\'s explore some more!|That was lucky!|That was no luck, I\'m just that good.|Alright, let\'s move on!|I\'m just getting warmed up!',
'defeat': 'I can\'t believe it!|...This is a joke, right?|Hey! No fair!|B-but...|I\'m gonna need a bandaid. And some hot chocolate.|I\'ll, uh, try again later.|Bad luck! Bad luck!',
'win against Ascended Baking Pod': 'Golly, that was peculiar.'
};
hero.stats = {
hp: 25,
hpm: 25,
might: 5,
guard: 4,
speed: 4,
dodge: 5,
luck: 7
};
var str = '';
// str += '';
// stringbuild our markup here
div.innerHTML = str;
M.dungeonGen = new Game.DungeonGen();
M.dungeonGen.loadTiles(M.dungeonTiles);
Game.Objects['Factory'].special();
}
M.save = function () {
//output cannot use ",", ";" or "|"
var str = '';
/*
for (var i in M.slot) {
str += parseFloat(M.slot[i]) + '/';
}
str = str.slice(0, -1);
str += ' ' + parseFloat(M.swaps) + ' ' + parseFloat(M.swapT);*/
return str;
}
M.load = function (str) {
//interpret str; called after .init
//note : not actually called in the Game's load; see "minigameSave" in main.js
if (!str) return false;
/*var i = 0;
var spl = str.split(' ');
var bit = spl[i++].split('/') || [];
for (var ii in M.slot) {
if (parseFloat(bit[ii]) != -1) {
var god = M.godsById[parseFloat(bit[ii])];
M.slotGod(god, ii);
l('templeSlot' + god.slot).appendChild(l('templeGod' + god.id));
}
}
M.swaps = parseFloat(spl[i++] || 3);
M.swapT = parseFloat(spl[i++] || Date.now());*/
}
M.reset = function () {
// ???
}
M.logic = function () {
//run each frame
M.dungeon.EachFrame();
}
M.draw = function () {
M.ourDiv.innerHTML = M.dungeon.Draw();
}
M.init(l('rowSpecial' + M.parent.id));
}
var M = 0;

major things left undone:

  • the code for drawing the div is ugly, none of the styles are imported
  • the code does not work outright, still some basic errors but assuming i did not overlook obvious scope issues everything is there
  • the save and the load need to iterate through the heroes table and save and load each hero
  • i have no idea what the from the dungeon state requires saving or loading
  • the old hooks for triggering the minigame need to be adapted to match minigameGrimoire.js and minigamePatheon.js

other notes:

  • i inlined the existing version of dungeons.js into minigameDungeon.js as M.dungeonGen, this could be handled differently possibly
  • lots of old style stuff needs to be changed to match newer styles (not using Game.X, instead using M.X when appropriate; converting multiple declarations to data-driven loops like M.heroes)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment