code:
image: alpine:latest

pages:
  stage: deploy
  script:
  - echo 'Nothing to do...'
  artifacts:
    paths:
    - public
  only:
  - master


code:
# justasussydude's page

### This is my new, open-source website hosted on GitGud. It has almost everything my old site had, and soon it should have even more.

## You need a Sapphire account and a GitGud profile to view the site! Also make sure to log in when prompted. This is not a bug!  (feature™)

# All content created entirely by myself in this repository is relinquished to the public domain under the Unlicense.

[Visit the homepage](https://FieryMewtwo.gitgud.site/home.html)


code:
<!DOCTYPE html>

<title>Just some blog by a Rando on the Internet</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

  All Posts
  Latest post
  About me

About me:

Hi! I'm FieryMewtwo, sometimes known as brodmoran or Fyreire! I'm very passionate about privacy rights, programming, and right to repair. My compiled.social link on the homepage should help if you want to know more about me.


code:
<!DOCTYPE html>

<title>Just some blog by a Rando on the Internet</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

  All Posts
  Latest post
  About me

Just some blog by a Rando on the Internet.

Click the buttons on the left pane to navigate around!


code:
function ajax(url,callback){
	var ajaxRequest;
	try{ajaxRequest = new XMLHttpRequest();} catch (e){try{ajaxRequest=new ActiveXObject('Msxml2.XMLHTTP');} catch (e) {try{ajaxRequest=new ActiveXObject('Microsoft.XMLHTTP');} catch (e){alert("Something broke!");return false;}}}
	if (callback){ajaxRequest.onreadystatechange=function(){if(ajaxRequest.readyState==4){callback(ajaxRequest.responseText);}}}
	ajaxRequest.open('GET',url+'&nocache='+(new Date().getTime()),true);ajaxRequest.send(null);
}


code:
/**
*
*  Base64 encode / decode
*  http://www.webtoolkit.info/
*
**/

var Base64 = {

	// private property
	_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

	// public method for encoding
	encode : function (input) {
		var output = "";
		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
		var i = 0;

		input = Base64._utf8_encode(input);

		while (i < input.length) {

			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);

			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;

			if (isNaN(chr2)) {
				enc3 = enc4 = 64;
			} else if (isNaN(chr3)) {
				enc4 = 64;
			}

			output = output +
			this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
			this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

		}

		return output;
	},

	// public method for decoding
	decode : function (input) {
		var output = "";
		var chr1, chr2, chr3;
		var enc1, enc2, enc3, enc4;
		var i = 0;

		input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

		while (i < input.length) {

			enc1 = this._keyStr.indexOf(input.charAt(i++));
			enc2 = this._keyStr.indexOf(input.charAt(i++));
			enc3 = this._keyStr.indexOf(input.charAt(i++));
			enc4 = this._keyStr.indexOf(input.charAt(i++));

			chr1 = (enc1 << 2) | (enc2 >> 4);
			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
			chr3 = ((enc3 & 3) << 6) | enc4;

			output = output + String.fromCharCode(chr1);

			if (enc3 != 64) {
				output = output + String.fromCharCode(chr2);
			}
			if (enc4 != 64) {
				output = output + String.fromCharCode(chr3);
			}

		}

		output = Base64._utf8_decode(output);

		return output;

	},

	// private method for UTF-8 encoding
	_utf8_encode : function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";

		for (var n = 0; n < string.length; n++) {

			var c = string.charCodeAt(n);

			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}

		}

		return utftext;
	},

	// private method for UTF-8 decoding
	_utf8_decode : function (utftext) {
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;

		while ( i < utftext.length ) {

			c = utftext.charCodeAt(i);

			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}

		}

		return string;
	}

}


code:
/*
Orteil's sloppy Cookie Clicker dungeons

Optimizations to do (not mentioning the dozens of missing features) :
-use canvas instead
-only compute AI for mobs with 2 tiles of view
*/
var LaunchDungeons=function()
{
	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 '';
	}

	/*=====================================================================================
	DUNGEONS
	=======================================================================================*/
	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;
	};

	/*=====================================================================================
	CREATE TILE TYPES
	=======================================================================================*/

	var D=new DungeonGen();
	D.loadTiles([
	['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]]
	]);

	/*=====================================================================================
	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+' : "'+choose(Game.Monsters[this.subtype].quotes[what].split('|'))+'"');
			}
		}
		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 '

';
		}
		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+=' It\'s a critical!';}//very rare critical based on luck
					attackStr+=' '+damage+' 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=''+attackStr+'';
				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(''+Game.Heroes[this.subtype].name+' has been defeated.');
						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('Found '+Beautify(value)+' cookie'+(value==1?'':'s')+'!');
							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+='
'+this.log[i]+'
';
				else str+='
'+this.log[i]+'
';
			}
			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 M=new D.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=M.dig();
			}
			//all done! decorate and render.
			M.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 M.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',M.doors[i][0],M.doors[i][1]);
			}
			//set tile graphics
			for (var i in M.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';}
				M.assignTiles(M.rooms[i],tiles);
			}
			this.map=M;
			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+='

'+this.map.str+'

';
			str+='
'+
			'
'+
			'
'+
			'
'+
			'
'+
			'
'+
			'
';
			str+='
'+
			'
Exit - '+this.name+' lvl.'+(this.level+1)+'
'+
			'

'+
			'

'+
			'
'+
			'

';
			l('rowSpecial'+this.id).innerHTML='
'+str+'
';

			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+='
Enter
';
			str+='

Enter dungeons

';
			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()
	{
		this.dungeon=new Game.Dungeon('Factory',this.id);
		this.dungeon.Generate();
		this.specialDrawFunction=function(){this.dungeon.Refresh();};
		this.drawSpecialButton=function(){return this.dungeon.DrawButton();};
		this.dungeon.timer=0;
		this.dungeon.timerWarmup=5;
		this.dungeon.portalPic='dungeonFactory';

		this.EachFrame=function()
		{
			if (this.dungeon.auto)
			{
				if (this.dungeon.timer>0) this.dungeon.timer--;
				if (this.dungeon.timer==0)
				{
					this.dungeon.timer=Game.fps*(Math.max(0.1,2-(this.dungeon.hero.stats.speed*0.2))+Math.max(this.dungeon.timerWarmup,0));
					if (this.dungeon.timerWarmup>0) this.dungeon.timerWarmup--;

					var dungeon=this.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'+this.dungeon.id).removeEventListener('keydown',arguments.callee,false);
			l('rowSpecial'+this.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+' : "'+choose(this.dialogue[what].split('|'))+'"');
		}

		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
	};

};


code:
// Copyright 2006 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
document.createElement("canvas").getContext||(function(){var s=Math,j=s.round,F=s.sin,G=s.cos,V=s.abs,W=s.sqrt,k=10,v=k/2;function X(){return this.context_||(this.context_=new H(this))}var L=Array.prototype.slice;function Y(b,a){var c=L.call(arguments,2);return function(){return b.apply(a,c.concat(L.call(arguments)))}}var M={init:function(b){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var a=b||document;a.createElement("canvas");a.attachEvent("onreadystatechange",Y(this.init_,this,a))}},init_:function(b){b.namespaces.g_vml_||
b.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML");b.namespaces.g_o_||b.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML");if(!b.styleSheets.ex_canvas_){var a=b.createStyleSheet();a.owningElement.id="ex_canvas_";a.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}g_o_\\:*{behavior:url(#default#VML)}"}var c=b.getElementsByTagName("canvas"),d=0;for(;d<c.length;d++)this.initElement(c[d])},
initElement:function(b){if(!b.getContext){b.getContext=X;b.innerHTML="";b.attachEvent("onpropertychange",Z);b.attachEvent("onresize",$);var a=b.attributes;if(a.width&&a.width.specified)b.style.width=a.width.nodeValue+"px";else b.width=b.clientWidth;if(a.height&&a.height.specified)b.style.height=a.height.nodeValue+"px";else b.height=b.clientHeight}return b}};function Z(b){var a=b.srcElement;switch(b.propertyName){case "width":a.style.width=a.attributes.width.nodeValue+"px";a.getContext().clearRect();
break;case "height":a.style.height=a.attributes.height.nodeValue+"px";a.getContext().clearRect();break}}function $(b){var a=b.srcElement;if(a.firstChild){a.firstChild.style.width=a.clientWidth+"px";a.firstChild.style.height=a.clientHeight+"px"}}M.init();var N=[],B=0;for(;B<16;B++){var C=0;for(;C<16;C++)N[B*16+C]=B.toString(16)+C.toString(16)}function I(){return[[1,0,0],[0,1,0],[0,0,1]]}function y(b,a){var c=I(),d=0;for(;d<3;d++){var f=0;for(;f<3;f++){var h=0,g=0;for(;g<3;g++)h+=b[d][g]*a[g][f];c[d][f]=
h}}return c}function O(b,a){a.fillStyle=b.fillStyle;a.lineCap=b.lineCap;a.lineJoin=b.lineJoin;a.lineWidth=b.lineWidth;a.miterLimit=b.miterLimit;a.shadowBlur=b.shadowBlur;a.shadowColor=b.shadowColor;a.shadowOffsetX=b.shadowOffsetX;a.shadowOffsetY=b.shadowOffsetY;a.strokeStyle=b.strokeStyle;a.globalAlpha=b.globalAlpha;a.arcScaleX_=b.arcScaleX_;a.arcScaleY_=b.arcScaleY_;a.lineScale_=b.lineScale_}function P(b){var a,c=1;b=String(b);if(b.substring(0,3)=="rgb"){var d=b.indexOf("(",3),f=b.indexOf(")",d+
1),h=b.substring(d+1,f).split(",");a="#";var g=0;for(;g<3;g++)a+=N[Number(h[g])];if(h.length==4&&b.substr(3,1)=="a")c=h[3]}else a=b;return{color:a,alpha:c}}function aa(b){switch(b){case "butt":return"flat";case "round":return"round";case "square":default:return"square"}}function H(b){this.m_=I();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.fillStyle=this.strokeStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=k*1;this.globalAlpha=1;this.canvas=b;
var a=b.ownerDocument.createElement("div");a.style.width=b.clientWidth+"px";a.style.height=b.clientHeight+"px";a.style.overflow="hidden";a.style.position="absolute";b.appendChild(a);this.element_=a;this.lineScale_=this.arcScaleY_=this.arcScaleX_=1}var i=H.prototype;i.clearRect=function(){this.element_.innerHTML=""};i.beginPath=function(){this.currentPath_=[]};i.moveTo=function(b,a){var c=this.getCoords_(b,a);this.currentPath_.push({type:"moveTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};
i.lineTo=function(b,a){var c=this.getCoords_(b,a);this.currentPath_.push({type:"lineTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};i.bezierCurveTo=function(b,a,c,d,f,h){var g=this.getCoords_(f,h),l=this.getCoords_(b,a),e=this.getCoords_(c,d);Q(this,l,e,g)};function Q(b,a,c,d){b.currentPath_.push({type:"bezierCurveTo",cp1x:a.x,cp1y:a.y,cp2x:c.x,cp2y:c.y,x:d.x,y:d.y});b.currentX_=d.x;b.currentY_=d.y}i.quadraticCurveTo=function(b,a,c,d){var f=this.getCoords_(b,a),h=this.getCoords_(c,d),g={x:this.currentX_+
0.6666666666666666*(f.x-this.currentX_),y:this.currentY_+0.6666666666666666*(f.y-this.currentY_)};Q(this,g,{x:g.x+(h.x-this.currentX_)/3,y:g.y+(h.y-this.currentY_)/3},h)};i.arc=function(b,a,c,d,f,h){c*=k;var g=h?"at":"wa",l=b+G(d)*c-v,e=a+F(d)*c-v,m=b+G(f)*c-v,r=a+F(f)*c-v;if(l==m&&!h)l+=0.125;var n=this.getCoords_(b,a),o=this.getCoords_(l,e),q=this.getCoords_(m,r);this.currentPath_.push({type:g,x:n.x,y:n.y,radius:c,xStart:o.x,yStart:o.y,xEnd:q.x,yEnd:q.y})};i.rect=function(b,a,c,d){this.moveTo(b,
a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath()};i.strokeRect=function(b,a,c,d){var f=this.currentPath_;this.beginPath();this.moveTo(b,a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath();this.stroke();this.currentPath_=f};i.fillRect=function(b,a,c,d){var f=this.currentPath_;this.beginPath();this.moveTo(b,a);this.lineTo(b+c,a);this.lineTo(b+c,a+d);this.lineTo(b,a+d);this.closePath();this.fill();this.currentPath_=f};i.createLinearGradient=function(b,
a,c,d){var f=new D("gradient");f.x0_=b;f.y0_=a;f.x1_=c;f.y1_=d;return f};i.createRadialGradient=function(b,a,c,d,f,h){var g=new D("gradientradial");g.x0_=b;g.y0_=a;g.r0_=c;g.x1_=d;g.y1_=f;g.r1_=h;return g};i.drawImage=function(b){var a,c,d,f,h,g,l,e,m=b.runtimeStyle.width,r=b.runtimeStyle.height;b.runtimeStyle.width="auto";b.runtimeStyle.height="auto";var n=b.width,o=b.height;b.runtimeStyle.width=m;b.runtimeStyle.height=r;if(arguments.length==3){a=arguments[1];c=arguments[2];h=g=0;l=d=n;e=f=o}else if(arguments.length==
5){a=arguments[1];c=arguments[2];d=arguments[3];f=arguments[4];h=g=0;l=n;e=o}else if(arguments.length==9){h=arguments[1];g=arguments[2];l=arguments[3];e=arguments[4];a=arguments[5];c=arguments[6];d=arguments[7];f=arguments[8]}else throw Error("Invalid number of arguments");var q=this.getCoords_(a,c),t=[];t.push(" <g_vml_:group",' coordsize="',k*10,",",k*10,'"',' coordorigin="0,0"',' style="width:',10,"px;height:",10,"px;position:absolute;");if(this.m_[0][0]!=1||this.m_[0][1]){var E=[];E.push("M11=",
this.m_[0][0],",","M12=",this.m_[1][0],",","M21=",this.m_[0][1],",","M22=",this.m_[1][1],",","Dx=",j(q.x/k),",","Dy=",j(q.y/k),"");var p=q,z=this.getCoords_(a+d,c),w=this.getCoords_(a,c+f),x=this.getCoords_(a+d,c+f);p.x=s.max(p.x,z.x,w.x,x.x);p.y=s.max(p.y,z.y,w.y,x.y);t.push("padding:0 ",j(p.x/k),"px ",j(p.y/k),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",E.join(""),", sizingmethod='clip');")}else t.push("top:",j(q.y/k),"px;left:",j(q.x/k),"px;");t.push(' ">','<g_vml_:image src="',b.src,
'"',' style="width:',k*d,"px;"," height:",k*f,'px;"',' cropleft="',h/n,'"',' croptop="',g/o,'"',' cropright="',(n-h-l)/n,'"',' cropbottom="',(o-g-e)/o,'"'," />","</g_vml_:group>");this.element_.insertAdjacentHTML("BeforeEnd",t.join(""))};i.stroke=function(b){var a=[],c=P(b?this.fillStyle:this.strokeStyle),d=c.color,f=c.alpha*this.globalAlpha;a.push("<g_vml_:shape",' filled="',!!b,'"',' style="position:absolute;width:',10,"px;height:",10,'px;"',' coordorigin="0 0" coordsize="',k*10," ",k*10,'"',' stroked="',
!b,'"',' path="');var h={x:null,y:null},g={x:null,y:null},l=0;for(;l<this.currentPath_.length;l++){var e=this.currentPath_[l];switch(e.type){case "moveTo":a.push(" m ",j(e.x),",",j(e.y));break;case "lineTo":a.push(" l ",j(e.x),",",j(e.y));break;case "close":a.push(" x ");e=null;break;case "bezierCurveTo":a.push(" c ",j(e.cp1x),",",j(e.cp1y),",",j(e.cp2x),",",j(e.cp2y),",",j(e.x),",",j(e.y));break;case "at":case "wa":a.push(" ",e.type," ",j(e.x-this.arcScaleX_*e.radius),",",j(e.y-this.arcScaleY_*e.radius),
" ",j(e.x+this.arcScaleX_*e.radius),",",j(e.y+this.arcScaleY_*e.radius)," ",j(e.xStart),",",j(e.yStart)," ",j(e.xEnd),",",j(e.yEnd));break}if(e){if(h.x==null||e.x<h.x)h.x=e.x;if(g.x==null||e.x>g.x)g.x=e.x;if(h.y==null||e.y<h.y)h.y=e.y;if(g.y==null||e.y>g.y)g.y=e.y}}a.push(' ">');if(b)if(typeof this.fillStyle=="object"){var m=this.fillStyle,r=0,n={x:0,y:0},o=0,q=1;if(m.type_=="gradient"){var t=m.x1_/this.arcScaleX_,E=m.y1_/this.arcScaleY_,p=this.getCoords_(m.x0_/this.arcScaleX_,m.y0_/this.arcScaleY_),
z=this.getCoords_(t,E);r=Math.atan2(z.x-p.x,z.y-p.y)*180/Math.PI;if(r<0)r+=360;if(r<1.0E-6)r=0}else{var p=this.getCoords_(m.x0_,m.y0_),w=g.x-h.x,x=g.y-h.y;n={x:(p.x-h.x)/w,y:(p.y-h.y)/x};w/=this.arcScaleX_*k;x/=this.arcScaleY_*k;var R=s.max(w,x);o=2*m.r0_/R;q=2*m.r1_/R-o}var u=m.colors_;u.sort(function(ba,ca){return ba.offset-ca.offset});var J=u.length,da=u[0].color,ea=u[J-1].color,fa=u[0].alpha*this.globalAlpha,ga=u[J-1].alpha*this.globalAlpha,S=[],l=0;for(;l<J;l++){var T=u[l];S.push(T.offset*q+
o+" "+T.color)}a.push('<g_vml_:fill type="',m.type_,'"',' method="none" focus="100%"',' color="',da,'"',' color2="',ea,'"',' colors="',S.join(","),'"',' opacity="',ga,'"',' g_o_:opacity2="',fa,'"',' angle="',r,'"',' focusposition="',n.x,",",n.y,'" />')}else a.push('<g_vml_:fill color="',d,'" opacity="',f,'" />');else{var K=this.lineScale_*this.lineWidth;if(K<1)f*=K;a.push("<g_vml_:stroke",' opacity="',f,'"',' joinstyle="',this.lineJoin,'"',' miterlimit="',this.miterLimit,'"',' endcap="',aa(this.lineCap),
'"',' weight="',K,'px"',' color="',d,'" />')}a.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",a.join(""))};i.fill=function(){this.stroke(true)};i.closePath=function(){this.currentPath_.push({type:"close"})};i.getCoords_=function(b,a){var c=this.m_;return{x:k*(b*c[0][0]+a*c[1][0]+c[2][0])-v,y:k*(b*c[0][1]+a*c[1][1]+c[2][1])-v}};i.save=function(){var b={};O(this,b);this.aStack_.push(b);this.mStack_.push(this.m_);this.m_=y(I(),this.m_)};i.restore=function(){O(this.aStack_.pop(),
this);this.m_=this.mStack_.pop()};function ha(b){var a=0;for(;a<3;a++){var c=0;for(;c<2;c++)if(!isFinite(b[a][c])||isNaN(b[a][c]))return false}return true}function A(b,a,c){if(!!ha(a)){b.m_=a;if(c)b.lineScale_=W(V(a[0][0]*a[1][1]-a[0][1]*a[1][0]))}}i.translate=function(b,a){A(this,y([[1,0,0],[0,1,0],[b,a,1]],this.m_),false)};i.rotate=function(b){var a=G(b),c=F(b);A(this,y([[a,c,0],[-c,a,0],[0,0,1]],this.m_),false)};i.scale=function(b,a){this.arcScaleX_*=b;this.arcScaleY_*=a;A(this,y([[b,0,0],[0,a,
0],[0,0,1]],this.m_),true)};i.transform=function(b,a,c,d,f,h){A(this,y([[b,a,0],[c,d,0],[f,h,1]],this.m_),true)};i.setTransform=function(b,a,c,d,f,h){A(this,[[b,a,0],[c,d,0],[f,h,1]],true)};i.clip=function(){};i.arcTo=function(){};i.createPattern=function(){return new U};function D(b){this.type_=b;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}D.prototype.addColorStop=function(b,a){a=P(a);this.colors_.push({offset:b,color:a.color,alpha:a.alpha})};function U(){}G_vmlCanvasManager=
M;CanvasRenderingContext2D=H;CanvasGradient=D;CanvasPattern=U})();


code:
BGgarden.jpg
BGgrimoire.jpg
BGmarket.jpg
BGpantheon.jpg
Thumbs.db
alchemylab.png
alchemylabBackground.png
alteredGrandma.png
alternateGrandma.png
antiGrandma.png
antimattercondenser.png
antimattercondenserBackground.png
aqworldsbanner.jpg
ascendBox.png
ascendInfo.png
ascendSlot.png
ascendWisp.png
ascendedBakingPod.png
bank.png
bankBackground.png
bankGrandma.png
bgBlack.jpg
bgBlue.jpg
bgGold.jpg
bgMoney.jpg
bgMoneyChart.jpg
bgRed.jpg
bgWhite.jpg
blackGradient.png
blackGradientLeft.png
blackGradientSmallTop.png
bracketPanelLeftS.png
bracketPanelRightS.png
brokenCookie.png
brokenCookieHalo.png
brownStripes.png
brownStripesLeftEdge.png
buildings.png
bunnies.png
bunnyGrandma.png
buttonTile.jpg
caramelWave.png
chancemaker.png
chancemakerBackground.png
chocolateMilkWave.png
clayBG.jpg
contract.png
control.png
cookieShower1.png
cookieShower2.png
cookieShower3.png
cosmicGrandma.png
cursor.png
darkNoise.jpg
darkNoise.png
darkNoiseTopBar.jpg
discord.png
dragon.png
dragonBG.png
dragonFrame.png
dungeonDot.png
dungeonFactory.png
dungeonFoes.png
dungeonHeroes.png
dungeonIcons.png
dungeonItems.png
dungeonMapFactory.jpg
dungeonOverlay.png
dungeonPictos.png
dungeonTiles.png
easterEggs.png
elfGrandma.png
empty.png
emptyFrame.png
factory.png
factoryBackground.png
farm.png
farmBackground.png
farmerGrandma.png
favicon.ico
featherLeft.png
featherRight.png
filler.png
fractalEngine.png
fractalEngineBackground.png
frameBorder.png
frostedReindeer.png
gardenPlants.png
gardenPlots.png
gardenTip.png
girlscoutChip.png
girlscoutCrumb.png
girlscoutDoe.png
girlscoutLucky.png
glint.jpg
goldCookie.png
grandma.png
grandmaBackground.png
grandmas1.jpg
grandmas2.jpg
grandmas3.jpg
grandmasGrandma.png
grimoireBG.png
heartStorm.png
hearts.png
heavenRing1.jpg
heavenRing2.jpg
heavenlyMoney.png
heraldFlag.png
icons.png
idleverse.png
idleverseBackground.png
imperfectCookie.png
infoBG.png
infoBGfade.png
javascriptconsole.png
javascriptconsoleBackground.png
levelUp.png
linkDash.png
linkPulse.gif
linkPulse.png
lockOff.png
lockOn.png
luckyGrandma.png
mapBG.jpg
mapIcons.png
marbleBG.jpg
marshmallows.png
metaGrandma.png
milk.png
milkBanana.png
milkBlack.png
milkBlood.png
milkBlueFire.png
milkBlueberry.png
milkCaramel.png
milkCherry.png
milkChocolate.png
milkCoconut.png
milkCoffee.png
milkDragonfruit.png
milkFire.png
milkGold.png
milkGreenFire.png
milkHoney.png
milkLicorice.png
milkLime.png
milkMaple.png
milkMint.png
milkOrange.png
milkPlain.png
milkRaspberry.png
milkRose.png
milkSoy.png
milkSpiced.png
milkStars.png
milkStrawberry.png
milkTea.png
milkVanilla.png
milkWave.png
milkZebra.png
mine.png
mineBackground.png
minerGrandma.png
money.png
mysticBG.jpg
nest.png
orangeWave.png
panelBG.png
panelGradientBottom.png
panelGradientLeft.png
panelGradientRight.png
panelGradientTop.png
panelHorizontal.png
panelMenu.png
panelMenu2.png
panelMenu3.png
panelVertical.png
pantheonBG.png
patreon.png
perfectCookie.png
pieFill.png
pointyLad.png
portal.png
portalBackground.png
portraitChip.png
portraitCrumb.png
portraitDoe.png
portraitLucky.png
prestigeBar.jpg
prestigeBarCap.png
prism.png
prismBackground.png
rainbowGrandma.png
raspberryWave.png
roundFrameBorder.png
roundedPanelBG.png
roundedPanelBGS.png
roundedPanelLeft.png
roundedPanelLeftS.png
roundedPanelRight.png
roundedPanelRightS.png
santa.png
scriptGrandma.png
sentientFurnace.png
shadedBorders.png
shadedBordersGold.png
shadedBordersRed.png
shadedBordersSoft.png
shine.png
shineGold.png
shineRed.png
shineSpoke.png
shinyWrinkler.png
shinyWrinklerBits.png
shipment.png
shipmentBackground.png
smallCookies.png
smallDollars.png
snow.jpg
snow2.jpg
sparkles.jpg
spellBG.png
spinnyBig.png
spinnySmall.png
spookyCookie.png
starbg.jpg
storeTile.jpg
sugarLump.png
temple.png
templeBackground.png
templeGrandma.png
timemachine.png
timemachineBackground.png
timerBars.png
transmutedGrandma.png
turnInto.png
upgradeFrame.png
upgradeFrameHeavenly.png
upgradeFrameOld.png
upgradeFrameShadowOld.png
upgradeHighlight.jpg
upgradeHighlight.png
upgradeSelector.png
weeHoodie.png
winterFrame.png
winterWrinkler.png
witchGrandma.png
wizardtower.png
wizardtowerBackground.png
workerGrandma.png
wrathContract.png
wrathCookie.png
wrinkler.png
wrinklerBits.png
wrinklerBitsOld.png


code:
<!DOCTYPE html>

<title>Cookie Clicker</title>

<meta name="viewport" content="width=900, initial-scale=1">
<link rel="shortcut icon" href="img/favicon.ico" />

<link href='https://fonts.googleapis.com/css?family=Merriweather:900&subset=latin,latin-ext' rel='stylesheet' type='text/css'>

<link href="style.css?v=2.053" rel="stylesheet" type="text/css">

Cookie Clicker™ © Orteil, 2021 - DashNet

twitter

tumblr

Discord

Merch!

Patreon

-

Cookie Clicker for Android

RandomGen

Idle Game Maker

			Other versions

			Live version
			Try the beta!
			v. 1.0466
			Classic

Loading...

This is taking longer than expected.

Slow connection? If not, please make sure your javascript is enabled, then refresh.

				If problems persist, this might be on our side - wait a few minutes, then hit ctrl+f5!

Your browser may not be recent enough to run Cookie Clicker.
You might want to update, or switch to a more modern browser such as Chrome or Firefox.

		<canvas id="backgroundCanvas"></canvas>

x

			<canvas id="backgroundLeftCanvas" style="z-index:5;"></canvas>

Options

Stats

New update!
Info

Legacy

Buildings

				Cookie Clicker is mainly supported by ads.
Consider unblocking our site or checking out our Patreon!

^ Sponsored link ^

Store

v Sponsored links v

Cookie

Buildings

Store

Menu


code:
ajax.js
base64.js
DungeonGen.js
dungeons.js
excanvas.compiled.js
main.js
minigameGarden.js
minigameGrimoire.js
minigamePantheon.js
minigameMarket.js