// Roll a die of the given number of sides
function rollDie(sides) { return Math.floor(Math.random()*sides+1); }

// Role the given number of the given type of dice and return the sum
function rollDice(sides,number) {
	var total = 0;
	for(var i = 0; i < number; i++) { total += rollDie(sides); }
	return total;
}

// Role 4d6 and return the sum of the three highest dice.
function rollStat() {
	var lowest = 6;
	var total = 0;
	for(var i = 0; i < 4; i++) {
		var rolled = rollDie(6);
		if(rolled < lowest) lowest = rolled;
		total += rolled;
	}
	total = total - lowest;
	return total;
}

// Get the selected value from the given select form field object
function getValueFromSelect(selectitem) {
	return selectitem.options[selectitem.selectedIndex].value;
}

// Set the value of the select to the given index
function setSelectValue(selectitem,index) {
	selectitem.selectedIndex = index;
}

// bypass browser stupidity and set text on an element
function setElementText(doc,id,t) {
	var e = doc.getElementById(id);
	if(e == null) alert("FAILED TO GET ELEMENT "+id);
	if(e.innerText){
		e.innerText = t;
	} else{
		e.textContent = t;
	}
}

// properly formats modifiers for display. 0 = 0, > 0 = +n, < 0 = -n
function formatModifier(mod) {
if(mod >= 0) return '+'+mod;
return mod;
}

// Constructor for a character. Document is the document displaying data
function Character(doc,rules) {
	this.rules = rules;
	this.setDocument(doc);
}

// Roll character stats and display them
Character.prototype.rollStats = function() {
	this.setOldStats();
	this.stats.rolledStr = rollStat();
	this.stats.rolledCon = rollStat();
	this.stats.rolledDex = rollStat();
	this.stats.rolledInt = rollStat();
	this.stats.rolledWis = rollStat();
	this.stats.rolledCha = rollStat();
	this.recalculate();
	this.displayCharacter();
}


Character.prototype.setOldStats = function() {
	this.old = {};
	this.old.stats = this.stats;
	this.old.race = this.race;
	this.old.class = this.class;
	this.old.level = this.level;
	this.old.paragonPath = this.paragonPath;
	this.old.epicDestiny = this.epicDestiny;
	this.old.deity = this.deity;
}

// Hook all the event handlers in the form so we get notified of changes
Character.prototype.hookFormFields = function() {
	var mchar = this; // needed for closures
	this.cform.rolledStr.onchange = function(event) {
		mchar.setOldStats();
		mchar.stats.rolledStr = parseInt(mchar.cform.rolledStr.value);
		mchar.handleFormUpdate();
	}
	this.cform.rolledCon.onchange = function(event) {
		mchar.setOldStats();
		mchar.stats.rolledCon = parseInt(mchar.cform.rolledCon.value);
		mchar.handleFormUpdate();
	}
	this.cform.rolledDex.onchange = function(event) {
		mchar.setOldStats();
		mchar.stats.rolledDex = parseInt(mchar.cform.rolledDex.value);
		mchar.handleFormUpdate();
	}
	this.cform.rolledInt.onchange = function(event) {
		mchar.setOldStats();
		mchar.stats.rolledInt = parseInt(mchar.cform.rolledInt.value);
		mchar.handleFormUpdate();
	}
	this.cform.rolledWis.onchange = function(event) {
		mchar.setOldStats();
		mchar.stats.rolledWis = parseInt(mchar.cform.rolledWis.value);
		mchar.handleFormUpdate();
	}
	this.cform.rolledCha.onchange = function(event) {
		mchar.setOldStats();
		mchar.stats.rolledCha = parseInt(mchar.cform.rolledCha.value);
		mchar.handleFormUpdate();
	}
	this.cform.race.onchange = function(event) {
		mchar.setOldStats();
		mchar.race = parseInt(getValueFromSelect(mchar.cform.race));
		mchar.handleFormUpdate();
	}
	this.cform.class.onchange = function(event) {
		mchar.setOldStats();
		mchar.class = parseInt(getValueFromSelect(mchar.cform.class));
		mchar.handleFormUpdate();
	}
	this.cform.paragonPath.onchange = function(event) {
		mchar.setOldStats();
		mchar.paragonPath = mchar.cform.paragonPath.value;
		mchar.handleFormUpdate();
	}
	this.cform.epicDestiny.onchange = function(event) {
		mchar.setOldStats();
		mchar.epicDestiny = mchar.cform.epicDestiny.value;
		mchar.handleFormUpdate();
	}
	this.cform.totalXP.onchange = function(event) {
		mchar.setOldStats();
		mchar.totalXP = parseInt(mchar.cform.totalXP.value);
		mchar.handleFormUpdate();
	}
	this.cform.age.onchange = function(event) {
		mchar.age = mchar.cform.age.value;
	}
	this.cform.gender.onchange = function(event) {
		mchar.gender = mchar.cform.gender.value;
	}
	this.cform.height.onchange = function(event) {
		mchar.height = mchar.cform.height.value;
	}
	this.cform.weight.onchange = function(event) {
		mchar.weight = mchar.cform.weight.value;
	}
	this.cform.alignment.onchange = function(event) {
		mchar.alignment = mchar.cform.alignment.value;
	}
	this.cform.deity.onchange = function(event) {
		mchar.setOldStats();
		mchar.deity = mchar.cform.deity.value;
		mchar.handleFormUpdate();
	}
	this.cform.affiliation.onchange = function(event) {
		mchar.affiliation = mchar.cform.affiliation.value;
	}

	this.cform.Acrobatics.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Acrobatics = mchar.cform.Acrobatics.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Arcana.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Arcana = mchar.cform.Arcana.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Athletics.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Athletics = mchar.cform.Athletics.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Bluff.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Bluff = mchar.cform.Bluff.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Diplomacy.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Diplomacy = mchar.cform.Diplomacy.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Dungeoneering.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Dungeoneering = mchar.cform.Dungeoneering.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Endurance.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Endurance = mchar.cform.Endurance.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Heal.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Heal = mchar.cform.Heal.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.History.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.History = mchar.cform.History.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Insight.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Insight = mchar.cform.Insight.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Intimidate.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Intimidate = mchar.cform.Intimidate.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Nature.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Nature = mchar.cform.Nature.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Perception.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Perception = mchar.cform.Perception.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Religion.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Religion = mchar.cform.Religion.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Stealth.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Stealth = mchar.cform.Stealth.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Streetwise.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Streetwise = mchar.cform.Streetwise.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.Thievery.onchange = function(event) {
		mchar.setOldStats();
		mchar.skills.Thievery = mchar.cform.Thievery.checked ? 5 : 0;
		mchar.handleFormUpdate();
	}

	this.cform.armor.onchange = function(event) {
		mchar.setOldStats();
		mchar.armor = parseInt(getValueFromSelect(mchar.cform.armor));
		mchar.handleFormUpdate();
	}

	this.cform.armorenh.onchange = function(event) {
		mchar.setOldStats();
		mchar.armorenh = parseInt(mchar.cform.armorenh.value);
		mchar.handleFormUpdate();
	}

	this.cform.shield.onchange = function(event) {
		mchar.setOldStats();
		mchar.shield = parseInt(getValueFromSelect(mchar.cform.shield));
		mchar.handleFormUpdate();
	}

	this.cform.weapon1.onchange = function(event) {
		mchar.setOldStats();
		mchar.weapons[0].id = parseInt(getValueFromSelect(mchar.cform.weapon1));
		mchar.weapons[0].hand = 0;
		mchar.handleFormUpdate();
	}

	this.cform.weaponhand1.onchange = function(event) {
		mchar.setOldStats();
		mchar.weapons[0].hand = parseInt(getValueFromSelect(mchar.cform.weaponhand1));
		mchar.handleFormUpdate();
	}

	this.cform.weaponenh1.onchange = function(event) {
		mchar.setOldStats();
		mchar.weapons[0].enhancement = parseInt(mchar.cform.weaponenh1.value);
		mchar.handleFormUpdate();
	}
}

// Handle an update to one of the fields in the form. This is called by all the
// event handlers.
Character.prototype.handleFormUpdate = function() {
	this.recalculate();
	this.displayCharacter();
}

// Make the given form the one which displays this character
Character.prototype.setDocument = function(doc) {
	this.cform = doc.getElementById('stats');
	this.cdoc = doc;
	this.initFromDocument(doc);
	this.recalculate();
	this.displayCharacter();
	this.hookFormFields();
}

// Copy data from the form to the character object.
Character.prototype.initFromDocument = function(adoc) {
	var aform = adoc.getElementById('stats');
	this.raceFeatures = [];
	this.stats = {};
	this.stats.rolledStr = parseInt(aform.rolledStr.value);
	this.stats.rolledCon = parseInt(aform.rolledCon.value);
	this.stats.rolledDex = parseInt(aform.rolledDex.value);
	this.stats.rolledInt = parseInt(aform.rolledInt.value);
	this.stats.rolledWis = parseInt(aform.rolledWis.value);
	this.stats.rolledCha = parseInt(aform.rolledCha.value);
	this.race = parseInt(getValueFromSelect(aform.race));
	this.class = parseInt(getValueFromSelect(aform.class));
	this.paragonPath = aform.paragonPath.value;
	this.epicDestiny = aform.epicDestiny.value;
	this.totalXP = parseInt(aform.totalXP.value);
	this.age = aform.age.value;
	this.gender = aform.gender.value;
	this.height = aform.height.value;
	this.weight = aform.weight.value;
	this.alignment = aform.alignment.value;
	this.deity = aform.deity.value;
	this.affiliation = aform.affiliation.value;
	this.characterName = aform.characterName.value;
	this.armor = parseInt(getValueFromSelect(aform.armor));
	this.armorenh = parseInt(aform.armorenh.value);
	this.shield = parseInt(getValueFromSelect(aform.shield));
//	this.shieldenh = parseInt(aform.shieldenh);

	this.skills = {};
	this.skills.Acrobatics = aform.Acrobatics.checked ? 5 : 0;
	this.skills.AcrobaticsMisc = 0; // FIX THESE
	this.skills.Arcana = aform.Arcana.checked ? 5 : 0;
	this.skills.ArcanaMisc = 0; // FIX THESE
	this.skills.Athletics = aform.Athletics.checked ? 5 : 0;
	this.skills.AthleticsMisc = 0; // FIX THESE
	this.skills.Bluff = aform.Bluff.checked ? 5 : 0;
	this.skills.BluffMisc = 0; // FIX THESE
	this.skills.Diplomacy = aform.Diplomacy.checked ? 5 : 0;
	this.skills.DiplomacyMisc = 0; // FIX THESE
	this.skills.Dungeoneering = aform.Dungeoneering.checked ? 5 : 0;
	this.skills.DungeoneeringMisc = 0; // FIX THESE
	this.skills.Endurance = aform.Endurance.checked ? 5 : 0;
	this.skills.EnduranceMisc = 0; // FIX THESE
	this.skills.Heal = aform.Heal.checked ? 5 : 0;
	this.skills.HealMisc = 0; // FIX THESE
	this.skills.History = aform.History.checked ? 5 : 0;
	this.skills.HistoryMisc = 0; // FIX THESE
	this.skills.Insight = aform.Insight.checked ? 5 : 0;
	this.skills.InsightMisc = 0; // FIX THESE
	this.skills.Intimidate = aform.Intimidate.checked ? 5 : 0;
	this.skills.IntimidateMisc = 0; // FIX THESE
	this.skills.Nature = aform.Nature.checked ? 5 : 0;
	this.skills.NatureMisc = 0; // FIX THESE
	this.skills.Perception = aform.Perception.checked ? 5 : 0;
	this.skills.PerceptionMisc = 0; // FIX THESE
	this.skills.Religion = aform.Religion.checked ? 5 : 0;
	this.skills.ReligionMisc = 0; // FIX THESE
	this.skills.Stealth = aform.Stealth.checked ? 5 : 0;
	this.skills.StealthMisc = 0; // FIX THESE
	this.skills.Streetwise = aform.Streetwise.checked ? 5 : 0;
	this.skills.StreetwiseMisc = 0; // FIX THESE
	this.skills.Thievery = aform.Thievery.checked ? 5 : 0;
	this.skills.ThieveryMisc = 0; // FIX THESE

	this.weapons = [];
	this.weapons[0] = {};
	this.weapons[0].id = parseInt(getValueFromSelect(aform.weapon1));
	this.weapons[0].hand = parseInt(getValueFromSelect(aform.weaponhand1));
	this.weapons[0].enhancement = parseInt(aform.weaponenh1.value);
}

// Do recalculation of all derived values
Character.prototype.recalculate = function() {
	if(this.old == null) this.setOldStats();
// race related recalculations, get rid of old ability bonuses
	this.stats.rolledStr -= this.rules.raceData[this.old.race - 1].str + 0;
	this.stats.rolledCon -= this.rules.raceData[this.old.race - 1].con + 0;
	this.stats.rolledDex -= this.rules.raceData[this.old.race - 1].dex + 0;
	this.stats.rolledInt -= this.rules.raceData[this.old.race - 1].int + 0;
	this.stats.rolledWis -= this.rules.raceData[this.old.race - 1].wis + 0;
	this.stats.rolledCha -= this.rules.raceData[this.old.race - 1].cha + 0;
// add in new race ability bonuses
	this.stats.rolledStr += this.rules.raceData[this.race - 1].str + 0;
	this.stats.rolledCon += this.rules.raceData[this.race - 1].con + 0;
	this.stats.rolledDex += this.rules.raceData[this.race - 1].dex + 0;
	this.stats.rolledInt += this.rules.raceData[this.race - 1].int + 0;
	this.stats.rolledWis += this.rules.raceData[this.race - 1].wis + 0;
	this.stats.rolledCha += this.rules.raceData[this.race - 1].cha + 0;
	this.size = this.rules.raceData[this.race - 1].size;
	this.movebase = this.rules.raceData[this.race - 1].move;
// get race features for the character's race
	this.raceFeatures = this.rules.raceData[this.race - 1].features;
// ability score bonus calculations
	this.stats.strMod = this.rules.abilityData[this.stats.rolledStr - 1].modifier;
	this.stats.conMod = this.rules.abilityData[this.stats.rolledCon - 1].modifier;
	this.stats.dexMod = this.rules.abilityData[this.stats.rolledDex - 1].modifier;
	this.stats.intMod = this.rules.abilityData[this.stats.rolledInt - 1].modifier;
	this.stats.wisMod = this.rules.abilityData[this.stats.rolledWis - 1].modifier;
	this.stats.chaMod = this.rules.abilityData[this.stats.rolledCha - 1].modifier;
// calculate level based on experience
	for(var i = 0; i < this.rules.levelData.length; i++) {
		if(this.totalXP >= this.rules.levelData[i].experience) {
			this.level = i + 1;
		}
	}
	var halflevel = Math.floor(this.level/2);
// calculate class based stuff
	this.classFortMod = this.rules.classData[this.class - 1].defbonus.fort;
	this.classRefMod = this.rules.classData[this.class - 1].defbonus.ref;
	this.classWillMod = this.rules.classData[this.class - 1].defbonus.will;
	this.classACMod = 0; // always 0 AFAIK
	this.maxhitpoints = this.rules.classData[this.class - 1].hitpoints + this.level * this.rules.classData[this.class - 1].hitpointgain + this.stats.conMod;
	this.bloodied = Math.floor(this.maxhitpoints/2);
	this.surgevalue = Math.floor(this.maxhitpoints/4);
	this.surges = this.rules.classData[this.class - 1].surges + this.stats.conMod;
	this.weaponproficiencies = [];
	for(var i = 0;i < this.rules.classData[this.class - 1].weaponprof.length; i++) {
		this.weaponproficiencies.push(this.rules.classData[this.class - 1].weaponprof[i]);
	}
// need to add in other weapon proficiencies from feats etc...
// Armor calculations
	this.movearmor = this.rules.armorData[this.armor - 1].speed;
	this.checkarmor = this.rules.armorData[this.armor - 1].check;
	this.armorbonus = this.rules.armorData[this.armor - 1].bonus;
	this.acAbilMod = this.rules.armorData[this.armor - 1].type == 'light' ? Math.max(this.stats.dexMod,this.stats.intMod) : 0;
	this.acFeatMod = 0;
	this.shieldcheck = this.rules.shieldData[this.shield - 1].check;
	this.shieldbonus = this.rules.shieldData[this.shield - 1].bonus;
	this.armorclass = 10 + halflevel + this.armorbonus + this.acAbilMod + this.classACMod + this.acFeatMod + this.armorenh + this.shieldbonus;
// Defenses calculations
	this.fortAbilMod = Math.max(this.stats.strMod,this.stats.conMod);
	this.fort = 10 + halflevel + this.fortAbilMod + this.classFortMod;
	this.refAbilMod = Math.max(this.stats.dexMod,this.stats.intMod);
	this.ref = 10 + halflevel + this.refAbilMod + this.classRefMod + this.shieldbonus;
	this.willAbilMod = Math.max(this.stats.wisMod,this.stats.chaMod);
	this.will = 10 + halflevel + this.willAbilMod + this.classWillMod;

// Misc stuff, mostly just setting some values that haven't got calcs yet so they don't mess up the sheet
	this.moveitem = 0;
	this.movemisc = 0;
	this.move = this.movebase + this.movearmor + this.moveitem + this.movemisc;
// Skill calculations
	this.skills.AcrobaticsAbilMod = this.stats.dexMod + halflevel;
	this.skills.AcrobaticsScore = this.skills.Acrobatics + this.skills.AcrobaticsAbilMod + this.checkarmor + this.shieldcheck + this.skills.AcrobaticsMisc;

	this.skills.ArcanaAbilMod = this.stats.intMod + halflevel;
	this.skills.ArcanaScore = this.skills.Arcana + this.skills.ArcanaAbilMod + this.skills.ArcanaMisc;

	this.skills.AthleticsAbilMod = this.stats.strMod + halflevel;
	this.skills.AthleticsScore = this.skills.Athletics + this.skills.AthleticsAbilMod + this.checkarmor + this.shieldcheck + this.skills.AthleticsMisc;

	this.skills.BluffAbilMod = this.stats.chaMod + halflevel;
	this.skills.BluffScore = this.skills.Bluff + this.skills.BluffAbilMod + this.skills.BluffMisc;

	this.skills.DiplomacyAbilMod = this.stats.chaMod + halflevel;
	this.skills.DiplomacyScore = this.skills.Diplomacy + this.skills.DiplomacyAbilMod + this.skills.DiplomacyMisc;

	this.skills.DungeoneeringAbilMod = this.stats.wisMod + halflevel;
	this.skills.DungeoneeringScore = this.skills.Dungeoneering + this.skills.DungeoneeringAbilMod + this.skills.DungeoneeringMisc;

	this.skills.EnduranceAbilMod = this.stats.conMod + halflevel;
	this.skills.EnduranceScore = this.skills.Endurance + this.skills.EnduranceAbilMod + this.checkarmor + this.shieldcheck + this.skills.EnduranceMisc;

	this.skills.HealAbilMod = this.stats.wisMod + halflevel;
	this.skills.HealScore = this.skills.Heal + this.skills.HealAbilMod + this.skills.HealMisc;

	this.skills.HistoryAbilMod = this.stats.intMod + halflevel;
	this.skills.HistoryScore = this.skills.History + this.skills.HistoryAbilMod + this.skills.HistoryMisc;

	this.skills.InsightAbilMod = this.stats.wisMod + halflevel;
	this.skills.InsightScore = this.skills.Insight + this.skills.InsightAbilMod + this.skills.InsightMisc;

	this.skills.IntimidateAbilMod = this.stats.chaMod + halflevel;
	this.skills.IntimidateScore = this.skills.Intimidate + this.skills.IntimidateAbilMod + this.skills.IntimidateMisc;

	this.skills.NatureAbilMod = this.stats.wisMod + halflevel;
	this.skills.NatureScore = this.skills.Nature + this.skills.NatureAbilMod + this.skills.NatureMisc;

	this.skills.PerceptionAbilMod = this.stats.wisMod + halflevel;
	this.skills.PerceptionScore = this.skills.Perception + this.skills.PerceptionAbilMod + this.skills.PerceptionMisc;

	this.skills.ReligionAbilMod = this.stats.intMod + halflevel;
	this.skills.ReligionScore = this.skills.Religion + this.skills.ReligionAbilMod + this.skills.ReligionMisc;

	this.skills.StealthAbilMod = this.stats.dexMod + halflevel;
	this.skills.StealthScore = this.skills.Stealth + this.skills.StealthAbilMod + this.checkarmor + this.shieldcheck + this.skills.StealthMisc;

	this.skills.StreetwiseAbilMod = this.stats.chaMod + halflevel;
	this.skills.StreetwiseScore = this.skills.Streetwise + this.skills.StreetwiseAbilMod + this.skills.StreetwiseMisc;

	this.skills.ThieveryAbilMod = this.stats.dexMod + halflevel;
	this.skills.ThieveryScore = this.skills.Thievery + this.skills.ThieveryAbilMod + this.checkarmor + this.shieldcheck + this.skills.ThieveryMisc;
// clean up when done...

	this.passiveinsight = 10 + this.skills.InsightScore;
	this.passiveperception = 10 + this.skills.PerceptionScore;

// weapons
	if(this.weapons == null) this.weapons = [];
	for(var i = 0; i < this.weapons.length; i++) {
		var widx = this.weapons[i].id - 1;
		var weapon = this.weapons[i];
		var wrule = this.rules.weapons[widx];
		weapon.name = wrule.name;
		weapon.range = wrule.range;
		weapon.class = wrule.class;
		weapon.group = wrule.group;
		weapon.properties = wrule.properties;
		weapon.type = wrule.type;
		weapon.weight = wrule.weight;
		weapon.price = wrule.price;
		weapon.defense = 'AC';
		weapon.twohanded = this.isTwoHanded(weapon);
		weapon.onehanded = this.isOneHanded(weapon);
		weapon.offhanded = this.isOffHanded(weapon);
		weapon.proficiency = wrule.proficiency;
		if(!this.isProficient(weapon)) weapon.proficiency = 0;
		weapon.ability = wrule.range == '' ? 'STR' : 'DEX'; // thrown weapons need a 2nd section?
		// note, only calculating MELEE characteristics for thrown weapons, not use as missle weapons.
		if(doesContain(weapon.properties,"light thrown") || doesContain(weapon.properties,"heavy thrown")) this.weapons.ability = 'STR';
		var amod = weapon.ability == 'STR' ? this.stats.strMod : this.stats.dexMod;
		weapon.attackbonus = halflevel + weapon.enhancement + amod + weapon.proficiency;
		weapon.damagebonus = weapon.enhancement + amod;
		if(doesContain(weapon.properties,"versatile") && weapon.hand == 3 && this.size != "S") weapon.damagebonus += 1;
		weapon.basedamage = wrule.damage;
		weapon.damage = weapon.basedamage + '+' + (weapon.damagebonus);
// account for offhand weapon use? Nope, there are no penalties etc for that ;)
		weapon.weaponrcatk = ''; // TODO fix these
		weapon.weaponrcdmg = '';
	}

	this.stats.old = null;
}

// Return true if character can use this weapon one handed
Character.prototype.isOneHanded = function(weapon) {
	var res = weapon.type == "one-handed";
	if(this.size == "S" && doesContain(weapon.properties,"versatile")) res = false;
	return res;
}

// does the given array contain the given value
function doesContain(anarry,value) {
	for(var i = 0; i < anarry.length; i++) {
		if(anarry[i] == value) return true;
	}
	return false;
}

// Return true if character can use this weapon one handed
Character.prototype.isTwoHanded = function(weapon) {
	var res = weapon.type == "two-handed";
	if(this.size == "S" && !doesContain(weapon.properties,"small")) res = false;
	if(doesContain(weapon.properties,"versatile")) res = true;
	return res;
}

// Return true if character can use this weapon off handed
Character.prototype.isOffHanded = function(weapon) {
	return doesContain(weapon.properties,"off-hand");
}

// Is the character proficient with this weapon
Character.prototype.isProficient = function(weapon) {
	var res = doesContain(this.weaponproficiencies,weapon.name);
	if(res) return res;
	res = doesContain(this.weaponproficiencies,weapon.class);
	return res;
}

// Display the character in the form
Character.prototype.displayCharacter = function() {
	this.cform.rolledStr.value = this.stats.rolledStr;
	this.cform.rolledCon.value = this.stats.rolledCon;
	this.cform.rolledDex.value = this.stats.rolledDex;
	this.cform.rolledInt.value = this.stats.rolledInt;
	this.cform.rolledWis.value = this.stats.rolledWis;
	this.cform.rolledCha.value = this.stats.rolledCha;
	setSelectValue(this.cform.race,this.race - 1);
	setSelectValue(this.cform.class,this.class - 1);
	this.cform.paragonPath.value = this.paragonPath;
	this.cform.epicDestiny.value = this.epicDestiny;
	this.cform.totalXP.value = this.totalXP;
	setElementText(this.cdoc,'size',this.size);
	this.cform.age.value = this.age;
	this.cform.gender.value = this.gender;
	this.cform.height.value = this.height;
	this.cform.weight.value = this.weight;
	this.cform.alignment.value = this.alignment;
	this.cform.deity.value = this.deity;
	this.cform.affiliation.value = this.affiliation;
//	setElementText(this.cdoc,'level',this.level);
	$(this.cform).find('#level').text(this.level);
	this.cform.characterName.value = this.characterName;
	setElementText(this.cdoc,'strmod',formatModifier(this.stats.strMod));
	setElementText(this.cdoc,'conmod',formatModifier(this.stats.conMod));
	setElementText(this.cdoc,'dexmod',formatModifier(this.stats.dexMod));
	setElementText(this.cdoc,'intmod',formatModifier(this.stats.intMod));
	setElementText(this.cdoc,'wismod',formatModifier(this.stats.wisMod));
	setElementText(this.cdoc,'chamod',formatModifier(this.stats.chaMod));
	var halflevel = Math.floor(this.level/2);
	setElementText(this.cdoc,'strmodph',this.stats.strMod+halflevel);
	setElementText(this.cdoc,'conmodph',this.stats.conMod+halflevel);
	setElementText(this.cdoc,'dexmodph',this.stats.dexMod+halflevel);
	setElementText(this.cdoc,'intmodph',this.stats.intMod+halflevel);
	setElementText(this.cdoc,'wismodph',this.stats.wisMod+halflevel);
	setElementText(this.cdoc,'chamodph',this.stats.chaMod+halflevel);

	setElementText(this.cdoc,'initiative',formatModifier(this.stats.dexMod+halflevel));
	setElementText(this.cdoc,'initiativehalflevel',formatModifier(halflevel));
	setElementText(this.cdoc,'initiativedexmod',formatModifier(this.stats.dexMod));

	setElementText(this.cdoc,'fortitude',this.fort);
	setElementText(this.cdoc,'fort10phl',10 + halflevel);
	setElementText(this.cdoc,'fortabilmod',formatModifier(this.fortAbilMod));
	setElementText(this.cdoc,'fortclassmod',formatModifier(this.classFortMod));
	setElementText(this.cdoc,'reflex',this.ref);
	setElementText(this.cdoc,'ref10phl',10 + halflevel);
	setElementText(this.cdoc,'refabilmod',formatModifier(this.refAbilMod));
	setElementText(this.cdoc,'refclassmod',formatModifier(this.classRefMod));
	setElementText(this.cdoc,'refshieldmod',formatModifier(this.shieldbonus));
	setElementText(this.cdoc,'will',this.will);
	setElementText(this.cdoc,'will10phl',10 + halflevel);
	setElementText(this.cdoc,'willabilmod',formatModifier(this.willAbilMod));
	setElementText(this.cdoc,'willclassmod',formatModifier(this.classWillMod));

	setElementText(this.cdoc,'maxhitpoints',this.maxhitpoints);
	setElementText(this.cdoc,'bloodiedvalue',this.bloodied);
	setElementText(this.cdoc,'surgevalue',this.surgevalue);
	setElementText(this.cdoc,'surgesperday',this.surges);

	setElementText(this.cdoc,'move',this.move);
	setElementText(this.cdoc,'movebase',this.movebase);
	setElementText(this.cdoc,'movemodarmor',formatModifier(this.movearmor));
	setElementText(this.cdoc,'movemoditem',formatModifier(this.moveitem));
	setElementText(this.cdoc,'movemodmisc',formatModifier(this.movemisc));

	setElementText(this.cdoc,'Acrobatics',formatModifier(this.skills.AcrobaticsScore));
	setElementText(this.cdoc,'AcrobaticsAbilMod',formatModifier(this.skills.AcrobaticsAbilMod));
	setElementText(this.cdoc,'AcrobaticsACP',formatModifier(this.checkarmor + this.shieldcheck));
	this.cform.Acrobatics.checked = this.skills.Acrobatics != 0;

	setElementText(this.cdoc,'Arcana',formatModifier(this.skills.ArcanaScore));
	setElementText(this.cdoc,'ArcanaAbilMod',formatModifier(this.skills.ArcanaAbilMod));
	this.cform.Arcana.checked = this.skills.Arcana != 0;

	setElementText(this.cdoc,'Athletics',formatModifier(this.skills.AthleticsScore));
	setElementText(this.cdoc,'AthleticsAbilMod',formatModifier(this.skills.AthleticsAbilMod));
	setElementText(this.cdoc,'AthleticsACP',formatModifier(this.checkarmor + this.shieldcheck));
	this.cform.Athletics.checked = this.skills.Athletics != 0;

	setElementText(this.cdoc,'Bluff',formatModifier(this.skills.BluffScore));
	setElementText(this.cdoc,'BluffAbilMod',formatModifier(this.skills.BluffAbilMod));
	this.cform.Bluff.checked = this.skills.Bluff != 0;

	setElementText(this.cdoc,'Diplomacy',formatModifier(this.skills.DiplomacyScore));
	setElementText(this.cdoc,'DiplomacyAbilMod',formatModifier(this.skills.DiplomacyAbilMod));
	this.cform.Diplomacy.checked = this.skills.Diplomacy != 0;

	setElementText(this.cdoc,'Dungeoneering',formatModifier(this.skills.DungeoneeringScore));
	setElementText(this.cdoc,'DungeoneeringAbilMod',formatModifier(this.skills.DungeoneeringAbilMod));
	this.cform.Dungeoneering.checked = this.skills.Dungeoneering != 0;

	setElementText(this.cdoc,'Endurance',formatModifier(this.skills.EnduranceScore));
	setElementText(this.cdoc,'EnduranceAbilMod',formatModifier(this.skills.EnduranceAbilMod));
	setElementText(this.cdoc,'EnduranceACP',formatModifier(this.checkarmor + this.shieldcheck));
	this.cform.Endurance.checked = this.skills.Endurance != 0;

	setElementText(this.cdoc,'Heal',formatModifier(this.skills.HealScore));
	setElementText(this.cdoc,'HealAbilMod',formatModifier(this.skills.HealAbilMod));
	this.cform.Heal.checked = this.skills.Heal != 0;

	setElementText(this.cdoc,'History',formatModifier(this.skills.HistoryScore));
	setElementText(this.cdoc,'HistoryAbilMod',formatModifier(this.skills.HistoryAbilMod));
	this.cform.History.checked = this.skills.History != 0;

	setElementText(this.cdoc,'Insight',formatModifier(this.skills.InsightScore));
	setElementText(this.cdoc,'InsightAbilMod',formatModifier(this.skills.InsightAbilMod));
	this.cform.Insight.checked = this.skills.Insight != 0;

	setElementText(this.cdoc,'Intimidate',formatModifier(this.skills.IntimidateScore));
	setElementText(this.cdoc,'IntimidateAbilMod',formatModifier(this.skills.IntimidateAbilMod));
	this.cform.Intimidate.checked = this.skills.Intimidate != 0;

	setElementText(this.cdoc,'Nature',formatModifier(this.skills.NatureScore));
	setElementText(this.cdoc,'NatureAbilMod',formatModifier(this.skills.NatureAbilMod));
	this.cform.Nature.checked = this.skills.Nature != 0;

	setElementText(this.cdoc,'Perception',formatModifier(this.skills.PerceptionScore));
	setElementText(this.cdoc,'PerceptionAbilMod',formatModifier(this.skills.PerceptionAbilMod));
	this.cform.Perception.checked = this.skills.Perception != 0;

	setElementText(this.cdoc,'Religion',formatModifier(this.skills.ReligionScore));
	setElementText(this.cdoc,'ReligionAbilMod',formatModifier(this.skills.ReligionAbilMod));
	this.cform.Religion.checked = this.skills.Religion != 0;

	setElementText(this.cdoc,'Stealth',formatModifier(this.skills.StealthScore));
	setElementText(this.cdoc,'StealthAbilMod',formatModifier(this.skills.StealthAbilMod));
	setElementText(this.cdoc,'StealthACP',formatModifier(this.checkarmor + this.shieldcheck));
	this.cform.Stealth.checked = this.skills.Stealth != 0;

	setElementText(this.cdoc,'Streetwise',formatModifier(this.skills.StreetwiseScore));
	setElementText(this.cdoc,'StreetwiseAbilMod',formatModifier(this.skills.StreetwiseAbilMod));
	this.cform.Streetwise.checked = this.skills.Streetwise != 0;

	setElementText(this.cdoc,'Thievery',formatModifier(this.skills.ThieveryScore));
	setElementText(this.cdoc,'ThieveryAbilMod',formatModifier(this.skills.ThieveryAbilMod));
	setElementText(this.cdoc,'ThieveryACP',formatModifier(this.checkarmor + this.shieldcheck));
	this.cform.Thievery.checked = this.skills.Thievery != 0;

	setSelectValue(this.cform.armor,this.armor - 1);
	setElementText(this.cdoc,'armorbonus',formatModifier(this.armorbonus));
	this.cform.armorenh.value = this.armorenh;
	setElementText(this.cdoc,'armorcheck',formatModifier(this.checkarmor));
	setElementText(this.cdoc,'armorspeed',formatModifier(this.movearmor));

	setSelectValue(this.cform.shield,this.shield - 1);
	setElementText(this.cdoc,'shieldbonus',formatModifier(this.shieldbonus));
//	this.cform.sheildenh.value = this.sheildenh;
	setElementText(this.cdoc,'shieldcheck',formatModifier(this.shieldcheck));
// shield speed is a nonexistant concept AFAIK

	var halflevel = Math.floor(this.level/2);
	setElementText(this.cdoc,'armorclass',this.armorclass);
	setElementText(this.cdoc,'ac10phl',10 + halflevel);
	setElementText(this.cdoc,'acabilmod',formatModifier(this.acAbilMod));
//	setElementText(this.cdoc,'acclassmod',this.acclassmod); always zero AFAIK
	setElementText(this.cdoc,'acfeatmod',formatModifier(0)); // FIX THIS this.acfeatmod));
	setElementText(this.cdoc,'acenh',formatModifier(this.armorenh));
	setElementText(this.cdoc,'acarmor',formatModifier(this.armorbonus));
	setElementText(this.cdoc,'acshield',formatModifier(this.shieldbonus));

	setElementText(this.cdoc,'passiveinsight',this.passiveinsight);
	setElementText(this.cdoc,'senseinsight',this.skills.InsightScore);
	setElementText(this.cdoc,'passiveperception',this.passiveperception);
	setElementText(this.cdoc,'senseperception',this.skills.PerceptionScore);

// weapons
	setSelectValue(this.cform.weapon1,this.weapons[0].id - 1);
	setSelectValue(this.cform.weaponhand1,this.weapons[0].hand - 1);
	$(this.cdoc).find('#weaponability1').text(this.weapons[0].ability);
	$(this.cdoc).find('#weaponprof1').text(this.weapons[0].proficiency);
	this.cform.weaponenh1.value = this.weapons[0].enhancement;
	$(this.cdoc).find('#weapondamage1').text(this.weapons[0].basedamage);
	$(this.cdoc).find('#weaponrange1').text(this.weapons[0].range);
	$(this.cdoc).find('#weaponrcatk1').text(this.weapons[0].weaponrcatk1);
	$(this.cdoc).find('#weaponrcdmg1').text(this.weapons[0].weaponrcdmg1);
	$(this.cdoc).find('#weaponability1').text(this.weapons[0].ability);
	this.cform.weaponhand1.options[1].enabled = this.weapons[0].onehanded;
	this.cform.weaponhand1.options[2].enabled = this.weapons[0].twohanded;
	this.cform.weaponhand1.options[3].enabled = this.weapons[0].offhanded;

// basic attacks display
	$(this.cdoc).find('#basicattackweapon1').text(this.weapons[0].name);
	$(this.cdoc).find('#basicattackdefense1').text(this.weapons[0].defense);
	$(this.cdoc).find('#basicattackdamage1').text(this.weapons[0].damage);
	$(this.cdoc).find('#basicattackrange1').text(this.weapons[0].range);
	$(this.cdoc).find('#basicattackspecial1').text(this.cform.weaponspecial1.value);
	$(this.cdoc).find('#basicattackbonus1').text(formatModifier(this.weapons[0].attackbonus));
// race features display
	if(this.raceFeatures == null) this.raceFeatures = []; //WTF!!!!!!
	if(this.raceFeatures.length > 0)
		$(this.cdoc).find('#racefeature1').text(this.raceFeatures[0].name + ": " + this.raceFeatures[0].text);
	if(this.raceFeatures.length > 1)
		$(this.cdoc).find('#racefeature2').text(this.raceFeatures[1].name + ": " + this.raceFeatures[1].text);
	if(this.raceFeatures.length > 2)
		$(this.cdoc).find('#racefeature3').text(this.raceFeatures[2].name + ": " + this.raceFeatures[2].text);
	if(this.raceFeatures.length > 3)
		$(this.cdoc).find('#racefeature4').text(this.raceFeatures[3].name + ": " + this.raceFeatures[3].text);
	if(this.raceFeatures.length > 4)
		$(this.cdoc).find('#racefeature5').text(this.raceFeatures[4].name + ": " + this.raceFeatures[4].text);
	if(this.raceFeatures.length > 5)
		$(this.cdoc).find('#racefeature6').text(this.raceFeatures[5].name + ": " + this.raceFeatures[5].text);
	if(this.raceFeatures.length > 6)
		$(this.cdoc).find('#racefeature7').text(this.raceFeatures[6].name + ": " + this.raceFeatures[6].text);
	if(this.raceFeatures.length > 7)
		$(this.cdoc).find('#racefeature8').text(this.raceFeatures[7].name + ": " + this.raceFeatures[7].text);
	if(this.raceFeatures.length > 8)
		$(this.cdoc).find('#racefeature9').text(this.raceFeatures[8].name + ": " + this.raceFeatures[8].text);
}


