var commonFunctions = {
	mobUsed : function (spellName) {
		this.attackedThisRound = true;
		this.basicCombat(spellName);
	},
	mobThink : function() {
		if(this.alive == false) {
			return;
		}
		
		if(this.aggro == true) {
			if(!this.attackedThisRound && this.playerIsTouchingMe) {
				var self = this;
				eventList.push(function() {
					self.basicAttack();
					executeNextEvent(750);
				})
			}
		}
		
		this.randomRoam();
		this.attackedThisRound = false;
	},
	vulnerableTo : function(type) {
		return true;
	},
	basicCombat : function(spellName) {
		if(this.alive == false) {
			return;
		}
		
		this.aggro = true;
		var randomFactor = 0.75 + Math.random() * 0.5;
		var damage = (vars.str + vars.atk) - (this.def) / 2;
		damage *= randomFactor;
		damage = Math.round(damage);
		if(damage < 1) {
			damage = 1;
		}
		var self = this;
		eventList.push(function() {
			if(self.alive == false) {
				executeNextEvent();
				return;
			}
			
			var damageDealt = true;
			
			if(spellName) {
				var spell = getSpellByName(spellName);
				if(vars.mp >= spell.cost) {
					setPlayerMana(vars.mp - spell.cost);
					display("You cast " + spellName + "!");
					if(spell.damage) {
						damage = getSpellByName(spellName).damage;
						damage -= self.def / 2;
						damage *= randomFactor;
						damage = Math.round(damage);
						if(damage < 1) {
							damage = 1;
						}
					}
					setTimeout(function() {
						spellEffect = spell.effect;
						map.renderSprites();
						setTimeout(function() {
							spellEffect = "";
							map.renderSprites();
						}, 500);
					}, 250);
				} else {
					display("You do not have enough MP to cast that.");
					damageDealt = false;
				}
			} else {
				display("You attack!");
				setTimeout(function() {
					weaponOffset = 128;
					map.renderSprites();
					setTimeout(function() {
						weaponOffset = 0;
						map.renderSprites();
					}, 500);
				}, 250);
			}
			
			eventList.push(function() {
				var nextEventTime = 0;
				if(damageDealt == true) {
					self.hp -= damage;
					display(self.name + " loses " + damage + " hp.");
					setTimeout(function() {
						self.imageOffset = 128;
						map.renderSprites();
						setTimeout(function() {
							self.imageOffset = 0;
							map.renderSprites();
						}, 500);
					}, 250);
					nextEventTime = 1000;
				}
				
				if(self.hp <= 0) {
					eventList.push(function() {
						display(self.name + " dies.");
						self.alive = false;
						self.solid = false;
						self.imageOffset = 384;
						if(self.onDie) {
							self.onDie();
						}
						map.renderSprites();
						map.renderMap();
						eventList.push(function() {
							var experience = 10 + 2 * self.lvl;
							var levelDelta = vars.lvl - self.lvl;
							if(levelDelta > 0) {
								experience -= levelDelta * levelDelta;
							} else {
								experience += levelDelta * levelDelta;
							}
							if(experience <= 0) {
								experience = 1;
							}
							addPlayerExperience(experience);
							
							eventList.push(function() {
								var goldAmount = self.lvl * 3;
								if(self.goldAmount) {
									goldAmount = self.goldAmount;
								}
								var randomFactor = 0.75 + Math.random() * 0.5;
								goldAmount *= randomFactor;
								goldAmount = Math.round(goldAmount);
								if(goldAmount < 1) {
									goldAmount = 1;
								}
								display("You gain " + goldAmount + " gold.");
								setPlayerGold(vars.gold + goldAmount);
								
								if(self.dropList) {
									for(var i=0; i<self.dropList.length; i++) {
										var item = self.dropList[i];
										var chance = Math.random();
										if(chance <= item.chance) {
											eventList.push(function(item) {
												return function() {
													display("You got " + item.name + "!");
													if(item.type == "weapon") {
														giveWeapon(item.name);
													} else if(item.type == "item") {
														giveItem(item.name);
													} else {
														giveArmor(item.name);
													}
													executeNextEvent(750);
												}
												
											}(item))
										}
									}
								}
								
								executeNextEvent(750);
							})
							
							executeNextEvent(750);
						})
						executeNextEvent(750);
					});
				} else {
					eventList.push(function() {
						self.basicAttack();
						executeNextEvent(1000);
					})
				}
				executeNextEvent(nextEventTime);
			})
			
			executeNextEvent(1000);
		});
	},
	basicAttack : function() {
		if(this.alive == false) {
			return;
		}
		
		var playerX = map.getPlayer().x;
		var playerY = map.getPlayer().y;
		var distance = Math.abs(playerX - this.x) + Math.abs(playerY - this.y);
		if(distance > 1) {
			return;
		}
		
		this.playerIsTouchingMe = true;
		
		var randomFactor = 0.75 + Math.random() * 0.5;
		var damage = (this.str) - (vars.def + vars.grd) / 2;
		damage *= randomFactor;
		damage = Math.round(damage);
		if(damage < 1) {
			damage = 1;
		}
		var self = this;
			
		display(self.name + " attacks!");
		
		setTimeout(function() {
			self.imageOffset = 256;
			map.renderSprites();
			setTimeout(function() {
				self.imageOffset = 0;
				map.renderSprites();
			}, 500);
		}, 250);
		
		eventList.push(function() {
			if(self.alive == false) {
				executeNextEvent();
				return;
			}
			
			setPlayerHealth(vars.hp - damage);
			display("You lose " + damage + " hp.");
			executeNextEvent(1000);
		});
	},
	randomRoam : function() {
		if(this.countdown != 0 && !this.countdown) {
			this.countdown = 2;
		}
		
		var playerX = map.getPlayer().x;
		var playerY = map.getPlayer().y;
		var distanceX = Math.abs(playerX - this.x);
		var distanceY = Math.abs(playerY - this.y);
		var distance = distanceX + distanceY;
		if(distance > 10) {
			return;
		}
		
		if(distance == 1) {
			this.playerIsTouchingMe = true;
		}
		
		if(this.countDown > 0) {
			this.countDown--;
			return;
		}
		
		if(this.aggro == true && distance<=4 && distanceX<=3 && distanceY<=3) {
			this.countDown = 1;
			if(distance > 1) {
				if(playerX != this.x) {
					if(playerY == this.y) {
						if(playerX > this.x) {
							if(!map.blocked(this.x + 1, this.y)) { this.x++; }
						} else {
							if(!map.blocked(this.x - 1, this.y)) { this.x--; }
						}
					} else {
						if(Math.random() > 0.5) {
							if(playerX > this.x) {
								if(!map.blocked(this.x + 1, this.y)) { 
									this.x++;
								}
							} else {
								if(!map.blocked(this.x - 1, this.y)) {
									this.x--;
								}
							}
						} else {
							if(playerY > this.y) {
								if(!map.blocked(this.x, this.y + 1)) {
									this.y++;
								}
							} else {
								if(!map.blocked(this.x, this.y - 1)) {
									this.y--;
								}
							}
						}
					}
				} else {
					if(playerY != this.y) {
						if(playerY > this.y) {
							if(!map.blocked(this.x, this.y + 1)) { this.y++; }
						} else {
							if(!map.blocked(this.x, this.y - 1)) { this.y--; }
						}
					}
				}
			}
			
			return;
		}
		
		this.countDown = Math.floor(Math.random() * 3) + 1;
		
		if(Math.random() > 0.5) {
			if(Math.random() > 0.5) {
				if(!map.blocked(this.x - 1, this.y)) { this.x--; }
			} else {
				if(!map.blocked(this.x + 1, this.y)) { this.x++; }
			}
		} else {
			if(Math.random() > 0.5) {
				if(!map.blocked(this.x, this.y - 1)) { this.y--; }
			} else {
				if(!map.blocked(this.x, this.y + 1)) { this.y++; }
			}
		}
	}
}

var entities = {
	socket : {
		onLoad : function(map) {
			if(vars["s" + this.sid]) {
				this.occupied = vars["s" + this.sid] == "full";
				
				if(this.occupied == true) {
					if(this.varCount) {
						if(vars[this.varCount.name] == this.varCount.cap) {
							if(this.associatedBlocks) {
								for(var i=0; i<this.associatedBlocks.length; i++) {
									var block = this.associatedBlocks[i];
									map.map.map[block.y][block.x] = 0;
								}
							}
						}
					} else {
						if(this.associatedBlocks) {
							for(var i=0; i<this.associatedBlocks.length; i++) {
								var block = this.associatedBlocks[i];
								map.map.map[block.y][block.x] = 0;
							}
						}
					}
				} else {
					if(this.associatedBlocks) {
						for(var i=0; i<this.associatedBlocks.length; i++) {
							var block = this.associatedBlocks[i];
							map.map.map[block.y][block.x] = 6;
						}
					}
				}
			}
			
			if(this.occupied == true) {
				map.map.map[this.y][this.x] = 4;
			} else {
				map.map.map[this.y][this.x] = 3;
			}
		},
		onUse : function() {
			var self = this;
			
			var success = function() {
				if(self.associatedBlocks) {
					for(var i=0; i<self.associatedBlocks.length; i++) {
						var block = self.associatedBlocks[i];
						map.map.map[block.y][block.x] = 0;
					}
				}
				
				if(self.associatedEvent) {
					self.associatedEvent();
				}
			}
			
			if(this.occupied == true) {
				map.map.map[this.y][this.x] = 3;
				map.render();
				dialog(["You remove the battery."]);
				giveItem("Battery");
				
				if(this.associatedBlocks) {
					for(var i=0; i<this.associatedBlocks.length; i++) {
						var block = this.associatedBlocks[i];
						map.map.map[block.y][block.x] = 6;
					}
				}
				
				if(this.varCount) {
					if(!vars[this.varCount.name]) {
						vars[this.varCount.name] = "nil";
					} else {
						if(vars[this.varCount.name] == this.varCount.cap) {
							if(this.associatedRemoveEvent) {
								this.associatedRemoveEvent();
							}
						}
						
						vars[this.varCount.name]--;
						if(vars[this.varCount.name] == 0) {
							vars[this.varCount.name] = "nil";
						}
					}
				} else {
					if(this.associatedRemoveEvent) {
						this.associatedRemoveEvent();
					}
				}
				
				this.occupied = false;
				vars["s" + this.sid] = "empty";
			} else {
				if(!vars.items.Battery) {
					vars.items.Battery = 0;
				}
				
				if(vars.items.Battery == 0) {
					dialog(["You can place a battery here."]);
				} else {
					map.map.map[this.y][this.x] = 4;
					map.render();
					dialog(["You place a battery."]);
					vars.items.Battery--;
					
					if(this.varCount) {
						if(!vars[this.varCount.name]) {
							vars[this.varCount.name] = 1;
						} else {
							if(vars[this.varCount.name] == "nil") {
								vars[this.varCount.name] = 0;
							}
							vars[this.varCount.name]++;
							if(vars[this.varCount.name] == this.varCount.cap) {
								success();
							}
						}
					} else {
						success();
					}
					
					this.occupied = true;
					vars["s" + this.sid] = "full";
				}
			}
		}
	},
	collectable : {
		onLoad : function() {
			if(vars["c" + this.cid] == "taken") {
				this.image = null;
				this.solid = false;
			}
		},
		onUse : function() {
			if(this.image != null) {
				dialog(["You got a " + this.itemName + "."]);
				if(items[this.itemName]) {
					giveItem(this.itemName);
				} else if(weapons[this.itemName]) {
					giveWeapon(this.itemName);
				} else {
					giveArmor(this.itemName);
				}
				this.image = null;
				this.solid = false;
				vars["c" + this.cid] = "taken";
				map.render();
			}
		}
	},
	battery : {
		image : "battery",
		onLoad : function() {
			if(vars["b" + this.bid] == "taken") {
				this.image = null;
				this.solid = false;
			}
		},
		onUse : function() {
			if(this.image == "battery") {
				dialog(["You got a battery."]);
				giveItem("Battery");
				this.image = null;
				this.solid = false;
				vars["b" + this.bid] = "taken";
				map.render();
			}
		}
	},
	crawler : {
		alive : true, attackable : true, attackedThisRound : false, aggro : true,
		name : "Crawler",
		hp : 12, mp : 0, str : 7, def : 4, lvl : 1,
		dropList : [],
		image : "crawler",
		onLoad : function() {
			this.randomRoam = commonFunctions.randomRoam;
			this.basicCombat = commonFunctions.basicCombat;
			this.basicAttack = commonFunctions.basicAttack;
			this.think = commonFunctions.mobThink;
			this.onUse = commonFunctions.mobUsed;
			this.vulnerableTo = commonFunctions.vulnerableTo;
			if(this.customLoad) {
				this.customLoad();
			}
		}
	},
	toughCrawler : {
		alive : true, attackable : true, attackedThisRound : false, aggro : true,
		name : "Tough Crawler",
		hp : 20, mp : 0, str : 14, def : 6, lvl : 3,
		dropList : [],
		image : "toughCrawler",
		onLoad : function() {
			this.randomRoam = commonFunctions.randomRoam;
			this.basicCombat = commonFunctions.basicCombat;
			this.basicAttack = commonFunctions.basicAttack;
			this.think = commonFunctions.mobThink;
			this.onUse = commonFunctions.mobUsed;
			this.vulnerableTo = commonFunctions.vulnerableTo;
			if(this.customLoad) {
				this.customLoad();
			}
		}
	},
	eliteCrawler : {
		alive : true, attackable : true, attackedThisRound : false, aggro : true,
		name : "Elite Crawler",
		hp : 40, mp : 0, str : 20, def : 8, lvl : 5,
		dropList : [],
		image : "eliteCrawler",
		onLoad : function() {
			this.randomRoam = commonFunctions.randomRoam;
			this.basicCombat = commonFunctions.basicCombat;
			this.basicAttack = commonFunctions.basicAttack;
			this.think = commonFunctions.mobThink;
			this.onUse = commonFunctions.mobUsed;
			this.vulnerableTo = commonFunctions.vulnerableTo;
			if(this.customLoad) {
				this.customLoad();
			}
		}
	},
	hunter : {
		alive : true, attackable : true, attackedThisRound : false, aggro : true,
		name : "Hunter",
		hp : 30, mp : 0, str : 17, def : 9, lvl : 5,
		dropList : [],
		image : "hunter",
		onLoad : function() {
			this.randomRoam = commonFunctions.randomRoam;
			this.basicCombat = commonFunctions.basicCombat;
			this.basicAttack = commonFunctions.basicAttack;
			this.think = commonFunctions.mobThink;
			this.onUse = commonFunctions.mobUsed;
			this.vulnerableTo = commonFunctions.vulnerableTo;
			if(this.customLoad) {
				this.customLoad();
			}
		}
	},
	toughHunter : {
		alive : true, attackable : true, attackedThisRound : false, aggro : true,
		name : "Tough Hunter",
		hp : 60, mp : 0, str : 25, def : 12, lvl : 7,
		dropList : [],
		image : "toughHunter",
		onLoad : function() {
			this.randomRoam = commonFunctions.randomRoam;
			this.basicCombat = commonFunctions.basicCombat;
			this.basicAttack = commonFunctions.basicAttack;
			this.think = commonFunctions.mobThink;
			this.onUse = commonFunctions.mobUsed;
			this.vulnerableTo = commonFunctions.vulnerableTo;
			if(this.customLoad) {
				this.customLoad();
			}
		}
	},
	eliteHunter : {
		alive : true, attackable : true, attackedThisRound : false, aggro : true,
		name : "Elite Hunter",
		hp : 90, mp : 0, str : 30, def : 15, lvl : 9,
		dropList : [],
		image : "eliteHunter",
		onLoad : function() {
			this.randomRoam = commonFunctions.randomRoam;
			this.basicCombat = commonFunctions.basicCombat;
			this.basicAttack = commonFunctions.basicAttack;
			this.think = commonFunctions.mobThink;
			this.onUse = commonFunctions.mobUsed;
			this.vulnerableTo = commonFunctions.vulnerableTo;
			if(this.customLoad) {
				this.customLoad();
			}
		}
	},
	boss : {
		alive : true, attackable : true, attackedThisRound : false, aggro : true,
		name : "Vulcanus",
		hp : 200, mp : 0, str : 50, def : 20, lvl : 13,
		dropList : [],
		image : "boss",
		onLoad : function() {
			this.randomRoam = commonFunctions.randomRoam;
			this.basicCombat = commonFunctions.basicCombat;
			this.basicAttack = commonFunctions.basicAttack;
			this.think = commonFunctions.mobThink;
			this.onUse = commonFunctions.mobUsed;
			this.vulnerableTo = commonFunctions.vulnerableTo;
			if(this.customLoad) {
				this.customLoad();
			}
		}
	},
	door : {
		solid : false,
		respawnCount : -1,
		originalIndex : 0,
		onUse : function() {
			if(map.map.map[this.y][this.x] == 0) {
				return;
			}
			
			if(this.usable == false) {
				dialog(["The door won't budge. It must open some other way."]);
				return;
			}
			
			if(this.useCheck) {
				var result = this.useCheck();
				if(result != true) {
					return;
				}
			}
			
			this.originalIndex = map.map.map[this.y][this.x];
			map.map.map[this.y][this.x] = 0;
			if(map.discoveredMap[this.y][this.x] != 2) {
				map.discoveredMap[this.y][this.x] = 2;	
				map.renderMiniMap();
			}
			this.respawnCount = 3;
		},
		think : function() {
			if(this.respawnCount == -1) {
				return;
			}
			
			if(this.respawnCount > 0) {
				this.respawnCount--;
			}
			
			if(this.respawnCount == 0) {
				for(var i=0; i<map.map.entities.length; i++) {
					var entity = map.map.entities[i];
					if(entity != this) {
						if(entity.x == this.x && entity.y == this.y) {
							return;
						}
					}
				}

				map.map.map[this.y][this.x] = this.originalIndex;
				this.respawnCount = -1;
			}
		}
	}
}
