chargen.System.Race = {
  initRace : function(name, p) {
    p = p.prototype;
    p.id = name;
    var initFFP = chargen.System.Commons.initFFP;
    if (!p.updateMisc) p.updateMisc = chargen.System.Class.miscUpdator;
    if (!p.updatePowers) p.updatePowers = chargen.System.powerUpdator;
    if (!p.updateFeatures) p.updateFeatures = chargen.System.featureUpdator;
    if (!p.updateProfs) p.updateProfs = chargen.System.Race.langUpdator;
    if (!p.speed)     p.speed    = 6;
    if (!p.race_languages) p.race_languages = ['common'];
    if (!p.features)  p.features = [];  else initFFP('feature', p, 'features');
    if (!p.powers)    p.powers   = [];  else initFFP('power', p, 'powers');
    if (!p.feats)     p.feats    = [];  else initFFP('feat', p, 'feats');
    if (p.dynamic_features) initFFP('feature', p, 'dynamic_features');
    p.languages = {};
    if (!p.keywords)   p.keywords = [p.id.toLowerCase(), 'nature', 'humanoid']; else p.keywords.push(p.id.toLowerCase());
    if (p.init) p.init();
  },

  langUpdator : function(chr) {
    for each (let lang in this.race_languages)
      if (lang == '*') ++chr.counts.language_count;
      else if (chr.languages[lang] && chr.languages[lang] < chargen.LIST.MANDATORY)
        chr.languages[lang] = chargen.LIST.MANDATORY;
    for each (let [lang,state] in Iterator(this.languages))
      if (state && chr.languages[lang] && chr.languages[lang] < chargen.LIST.SELECTED)
        chr.languages[lang] = chargen.LIST.SELECTED;
  }
};


let (race = chargen.Races) {


race.Dragonborn = function() {};
race.Dragonborn.prototype = {
  book : 'phb',
  height : [6*12+2,6*12+8],
  weight : [220,320],
  updateAbilities : function(chr) {
    chr.abilities.str += 2;
    chr.abilities.cha += 2;
  },
  updateSkills : function(chr) {
    chr.skills.history.addBonus(2, this, 'racial');
    chr.skills.intimidate.addBonus(2, this, 'racial');
  },
  race_languages : ['common','draconic'],
  features : ['Dragonborn.DagonbornFury'],
  feats  : ['Dragonborn.DragonbornFrenzy']
}



race.Dwarf = function() {};
race.Dwarf.prototype = {
  book : 'phb',
  height : [4*12+3, 4*12+9],
  weight : [160, 220],
  updateAbilities : function(chr) {
    chr.abilities.con += 2;
    chr.abilities.wis += 2;
  },
  updateSkills : function(chr) {
    chr.skills.dungeoneering.addBonus(2, this, 'racial');
    chr.skills.endurance.addBonus(2, this, 'racial');
  },
  speed : 5,
  race_languages : ['common','dwarven'],
  features : ['Dwarf.CastIronStomach', 'Dwarf.DwarvenResilience', 'Dwarf.StandYourGround', 'Racial.LowLightVision'],
  feats  : ['Dwarf.DodgeGiants']
}



race.Eladrin = function() {};
race.Eladrin.prototype = {
  book : 'phb',
  height : [5*12+5, 6*12+1],
  weight : [130, 180],
  keywords	: ['fey', 'humanoid'],
  updateAbilities : function(chr) {
    chr.abilities.dex += 2;
    chr.abilities.int += 2;
  },
  updateSkills : function(chr) {
    chr.skills.arcana.addBonus(2, this, 'racial');
    chr.skills.history.addBonus(2, this, 'racial');
  },
  race_languages : ['common','elven'],
  features : ['Racial.Trance','Racial.FeyOrigin','Racial.LowLightVision']
}



race.Elf = function() {};
race.Elf.prototype = {
  book : 'phb',
  height : [5*12+4, 6*12+0],
  weight : [130, 170],
  keywords	: ['fey', 'humanoid'],
  updateAbilities : function(chr) {
    chr.abilities.dex += 2;
    chr.abilities.wis += 2;
  },
  updateSkills : function(chr) {
    chr.skills.nature.addBonus(2, this, 'racial');
    chr.skills.perception.addBonus(2, this, 'racial');
  },
  speed : 7,
  race_languages : ['common','elven'],
  features : ['Elf.ElvenWeaponProficiency','Elf.WildStep','Elf.GroupAwareness','Racial.FeyOrigin','Racial.LowLightVision']
}



race.HalfElf = function() {};
race.HalfElf.prototype = {
  book : 'phb',
  height : [5*12+5, 6*12+2],
  weight : [130, 190],
  updateAbilities : function(chr) {
    chr.abilities.con += 2;
    chr.abilities.cha += 2;
  },
  updateSkills : function(chr) {
    chr.skills.insight.addBonus(2, this, 'racial');
    chr.skills.diplomacy.addBonus(2, this, 'racial');
  },
  race_languages : ['common','elven','*'],
  features : ['HalfElf.GroupDiplomacy', 'Racial.LowLightVision']
}



race.Halfling = function() {};
race.Halfling.prototype = {
  book : 'phb',
  height : [3*12+10, 4*12+2],
  weight : [75, 85],
  size : -1,
  updateAbilities : function(chr) {
    chr.abilities.dex += 2;
    chr.abilities.cha += 2;
  },
  updateSkills : function(chr) {
    chr.skills.acrobatics.addBonus(2, this, 'racial');
    chr.skills.thievery.addBonus(2, this, 'racial');
  },
  race_languages : ['common','*'],
  features : ['Halfling.Bold', 'Halfling.NimbleReaction']
}



race.Human = function() {};
race.Human.prototype = {
  book : 'phb',
  height : [5*12+6, 6*12+2],
  weight : [135, 220],
  features : [],
  race_languages : ['common','*'],
  feats  : ['Human.ActionSurge','Human.HumanPerseverance']
}





race.Tiefling = function() {},
race.Tiefling.prototype = {
  book : 'phb',
  height : [5*12+6, 6*12+2],
  weight : [140, 230],
  updateAbilities : function(chr) {
    chr.abilities.int += 2;
    chr.abilities.cha += 2;
  },
  updateSkills : function(chr) {
    chr.skills.bluff.addBonus(2, this, 'racial');
    chr.skills.stealth.addBonus(2, this, 'racial');
  },
  race_languages : ['common','*'],
  features : ['Tiefling.Bloodhunt','Tiefling.FireResistance','Racial.LowLightVision']
}

/***************** Monster Manual **********************/

race.Bugbear = function() {};
race.Bugbear.prototype = {
  book : 'mm',
  height : [6*12+10, 7*12+2],
  weight : [250, 300],
  updateAbilities : function(chr) {
    chr.abilities.str += 2;
    chr.abilities.dex += 2;
  },
  updateSkills : function(chr) {
    chr.skills.intimidate.addBonus(2, this, 'racial');
    chr.skills.stealth.addBonus(2, this, 'racial');
  },
  race_languages : ['common','goblin'],
  features : ['Racial.Oversized', 'Racial.LowLightVision']
};


race.Doppelganger = function() {};
race.Doppelganger.prototype = {
  book : 'mm',
  height : [5*12+7, 6*12+0],
  weight : [120, 160],
  updateAbilities : function(chr) {
    chr.abilities.int += 2;
    chr.abilities.cha += 2;
  },
  updateSkills : function(chr) {
    chr.skills.bluff.addBonus(2, this, 'racial');
    chr.skills.insight.addBonus(2, this, 'racial');
  },
  race_languages : ['common']
};


race.Drow = function() {};
race.Drow.prototype = {
  book : 'mm',
  height : [5*12+4, 6*12+0],
  weight : [130, 170],
  updateAbilities : function(chr) {
    chr.abilities.dex += 2;
    chr.abilities.cha += 2;
  },
  updateSkills : function(chr) {
    chr.skills.intimidate.addBonus(2, this, 'racial');
    chr.skills.stealth.addBonus(2, this, 'racial');
  },
  race_languages : ['common','elven'],
  features : ['Racial.Trance', 'Racial.Darkvision']
};


race.Githyanki = function() {};
race.Githyanki.prototype = {
  book : 'mm',
  height : [6*12+0, 6*12+5],
  weight : [160, 190],
  updateAbilities : function(chr) {
    chr.abilities.con += 2;
    chr.abilities.int += 2;
  },
  updateSkills : function(chr) {
    chr.skills.history.addBonus(2, this, 'racial');
    chr.skills.stealth.addBonus(2, this, 'racial');
  },
  race_languages : ['common','deep'],
  features : ['Racial.DangerSense']
};


race.Githzerai = function() {};
race.Githzerai.prototype = {
  book : 'mm',
  height : [6*12+0, 6*12+5],
  weight : [160, 190],
  updateAbilities : function(chr) {
    chr.abilities.dex += 2;
    chr.abilities.wis += 2;
  },
  updateSkills : function(chr) {
    chr.skills.acrobatics.addBonus(2, this, 'racial');
    chr.skills.athletics.addBonus(2, this, 'racial');
  },
  race_languages : ['common','deep'],
  features : ['Racial.DangerSense']
};


race.Gnoll = function() {};
race.Gnoll.prototype = {
  book : 'mm',
  height : [7*12+0, 7*12+6],
  weight : [280, 320],
  updateAbilities : function(chr) {
    chr.abilities.con += 2;
    chr.abilities.dex += 2;
  },
  updateSkills : function(chr) {
    chr.skills.intimidate.addBonus(2, this, 'racial');
  },
  speed : 7,
  race_languages : ['common','abyssal'],
  features : ['Gnoll.BloodFury', 'Racial.LowLightVision']
};


race.Gnome = function() {};
race.Gnome.prototype = {
  book : 'mm',
  height : [3*12+4, 3*12+8],
  weight : [50, 75],
  keywords : ['fey', 'humanoid'],
  updateAbilities : function(chr) {
    chr.abilities.int += 2;
    chr.abilities.cha += 2;
  },
  updateSkills : function(chr) {
    chr.skills.arcana.addBonus(2, this, 'racial');
    chr.skills.stealth.addBonus(2, this, 'racial');
  },
  size : -1,
  speed : 5,
  race_languages : ['common','elven'],
  features : ['Gnome.ReactiveStealth', 'Racial.LowLightVision']
};


race.Goblin = function() {};
race.Goblin.prototype = {
  book : 'mm',
  height : [3*12+4, 3*12+8],
  weight : [40, 55],
  updateAbilities : function(chr) {
    chr.abilities.dex += 2;
    chr.abilities.cha += 2;
  },
  updateSkills : function(chr) {
    chr.skills.stealth.addBonus(2, this, 'racial');
    chr.skills.thievery.addBonus(2, this, 'racial');
  },
  size : -1,
  race_languages : ['common','goblin'],
  features : ['Goblin.GoblinReflexes', 'Racial.LowLightVision']
};


race.Hobgoblin = function() {};
race.Hobgoblin.prototype = {
  book : 'mm',
  height : [6*12+1, 6*12+3],
  weight : [190, 240],
  keywords : ['goblin', 'nature', 'humanoid'],
  updateAbilities : function(chr) {
    chr.abilities.con += 2;
    chr.abilities.cha += 2;
  },
  updateSkills : function(chr) {
    chr.skills.athletics.addBonus(2, this, 'racial');
    chr.skills.history.addBonus(2, this, 'racial');
  },
  race_languages : ['common','goblin'],
  features : ['Racial.LowLightVision']
};


race.Kobold = function() {};
race.Kobold.prototype = {
  book : 'mm',
  height : [3*12+6, 4*12+0],
  weight : [60, 75],
  updateAbilities : function(chr) {
    chr.abilities.con += 2;
    chr.abilities.dex += 2;
  },
  updateSkills : function(chr) {
    chr.skills.stealth.addBonus(2, this, 'racial');
    chr.skills.thievery.addBonus(2, this, 'racial');
  },
  size : -1,
  race_languages : ['common','draconic'],
  features : ['Kobold.TrapSense']
};

race.Minotaur = function() {};
race.Minotaur.prototype = {
  book : 'mm',
  height : [7*12+1, 7*12+5],
  weight : [320, 350],
  updateAbilities : function(chr) {
    chr.abilities.str += 2;
    chr.abilities.con += 2;
  },
  updateSkills : function(chr) {
    chr.skills.nature.addBonus(2, this, 'racial');
    chr.skills.perception.addBonus(2, this, 'racial');
  },
  race_languages : ['common'],
  features : ['Minotaur.Ferocity', 'Racial.Oversized']
};

race.Orc = function() {};
race.Orc.prototype = {
  book : 'mm',
  height : [6*12+0, 6*12+5],
  weight : [200, 230],
  updateAbilities : function(chr) {
    chr.abilities.str += 2;
    chr.abilities.con += 2;
  },
  race_languages : ['common','giant'],
  features : ['Orc.RunningCharge', 'Racial.LowLightVision']
};

race.Shadarkai = function() {};
race.Shadarkai.prototype = {
  book : 'mm',
  height : [5*12+7, 6*12+0],
  weight : [130, 170],
  keywords : ['shadow', 'humanoid'],
  updateAbilities : function(chr) {
    chr.abilities.dex += 2;
    chr.abilities.int += 2;
  },
  updateSkills : function(chr) {
    chr.skills.acrobatics.addBonus(2, this, 'racial');
    chr.skills.stealth.addBonus(2, this, 'racial');
  },
  race_languages : ['common'],
  features : ['Racial.LowLightVision']
};

race.LongtoothShifter = function() {};
race.LongtoothShifter.prototype = {
  book : 'mm',
  height : [5*12+7, 6*12+0],
  weight : [130, 180],
  updateAbilities : function(chr) {
    chr.abilities.str += 2;
    chr.abilities.wis += 2;
  },
  updateSkills : function(chr) {
    chr.skills.athletics.addBonus(2, this, 'racial');
    chr.skills.endurance.addBonus(2, this, 'racial');
  },
  race_languages : ['common'],
  features : ['Racial.LowLightVision']
};

race.RazorclawShifter = function() {};
race.RazorclawShifter.prototype = {
  book : 'mm',
  height : [5*12+7, 6*12+0],
  weight : [130, 180],
  updateAbilities : function(chr) {
    chr.abilities.dex += 2;
    chr.abilities.wis += 2;
  },
  updateSkills : function(chr) {
    chr.skills.acrobatics.addBonus(2, this, 'racial');
    chr.skills.stealth.addBonus(2, this, 'racial');
  },
  race_languages : ['common'],
  features : ['Racial.LowLightVision']
};

race.Warforged = function() {};
race.Warforged.prototype = {
  book : 'mm',
  height : [6*12+0, 6*12+6],
  weight : [270, 300],
  updateAbilities : function(chr) {
    chr.abilities.str += 2;
    chr.abilities.con += 2;
  },
  updateSkills : function(chr) {
    chr.skills.endurance.addBonus(2, this, 'racial');
  },
  race_languages : ['common'],
  features : ['Warforged.LivingConstruct']
}



/***************** Forgotten Realms **********************/

race.Genasi = function() {};
race.Genasi.prototype = {
  book : 'frpg',
  height : [5*12+7, 6*12+2],
  weight : [130, 225],
  keywords : ['elemental', 'humanoid'],
  updateAbilities : function(chr) {
    chr.abilities.str += 2;
    chr.abilities.int += 2;
  },
  updateSkills : function(chr) {
    chr.skills.endurance.addBonus(2, this, 'racial');
    chr.skills.nature.addBonus(2, this, 'racial');
  },
  race_languages : ['common','primordal'],
  // Getter prevents auto addition of variable features
  features  : ['Racial.ElementalOrigin'],
  dynamic_features  : ['Genasi.Earthsoul', 'Genasi.Firesoul', 'Genasi.Stormsoul', 'Genasi.Watersoul', 'Genasi.Windsoul'],
  get powers() { return [] }
};

}