diff --git a/models/character.js b/models/character.js index b7fd05b..1bd60fa 100755 --- a/models/character.js +++ b/models/character.js @@ -58,7 +58,7 @@ export class Character { itemSlots; /** @type {Set} Things the character is particularly proficient at. */ - skills = new Set(); + proficiencies = new Set(); /** @type {Map>>${skill}<<<`) + } + + if (val < min) { + this[skill] = min + return + } + + if (val > max) { + this.skill = max; + return + } + } + // todo removeItem(item, count) } diff --git a/seeders/characerSeeder.js b/seeders/characerSeeder.js index 13fc600..b211ee1 100755 --- a/seeders/characerSeeder.js +++ b/seeders/characerSeeder.js @@ -20,19 +20,11 @@ let roll = {}; export class CharacterSeeder { constructor() { - // stupid convenience hack that only works if we only have a single Game in the system. + // stupid hack that ensures we populate roll AFTER gGame is available // Which we easily could have.!! - roll = { - d: (max, min = 1) => { - return gGame.random.within(min, max); - }, - d6: () => { - return gGame.random.within(1, 6); - }, - d8: () => { - return gGame.random.within(1, 8); - }, - }; + roll.d = (max, min = 1) => gGame.random.within(min, max) + roll.d6 = () => roll.d(6) + roll.d8 = () => roll.d(8) } /** @@ -52,14 +44,14 @@ export class CharacterSeeder { /** * @param {Character} character - * @param {...string} skills + * @param {...string} proficiencies */ - addSkillsToCharacter(character, ...skills) { - for (const skill of skills) { - if (!isIdSane(skill)) { - throw new Error(`Skill id >>${skill}<< is insane!`); + addProficienciesToCharacter(character, ...proficiencies) { + for (const prof of proficiencies) { + if (!isIdSane(prof)) { + throw new Error(`Proficiency id >>${prof}<< is insane!`); } - character.skills.add(skill); + character.proficiencies.add(prof); } } @@ -72,17 +64,24 @@ export class CharacterSeeder { createCharacter() { const c = new Character(); - // - // Initializing - // - // Rolling skills + this.generateName(c); + this.rollSkills(c); + this.applyAncestry(c); + this.applyFoundation(c); + return c; + } + + generateName(c) { + /** @todo use actual random name generator */ c.name = - gGame.random.oneOf("sir ", "madam ", "mister ", "miss ", "", "", "") + // prefix + gGame.random.oneOf("sir ", "madam ", "mister ", "miss ", "", "", "") + "random " + // name - gGame.random.next().toString(); // suffix + gGame.random.next().toString(); + } + rollSkills(c) { c.awareness = roll.d6() + 2; c.grit = roll.d6() + 2; c.knowledge = roll.d6() + 2; @@ -90,11 +89,6 @@ export class CharacterSeeder { c.meleeCombat = roll.d6() + 2; c.rangedCombat = roll.d6() + 2; c.skulduggery = roll.d6() + 2; - - this.applyAncestry(c); - this.applyFoundation(c); - - return c; } applyAncestry(c) { @@ -168,432 +162,445 @@ export class CharacterSeeder { * @param {string|number} Foundation to add to character */ applyFoundation(c, foundation = ":random") { - switch (foundation) { - case ":random": - return this.applyFoundation(c, roll.d(3)); - - // - // Brawler - // ------ - case 1: - case ":brawler": - c.foundation = "Brawler"; - c.skills.add(":armor.light"); - c.silver = 40; - c.maxHitPoints = c.currentHitPoints = 15; - c.itemSlots = 7; - c.meleeCombat = Math.max(c.meleeCombat, 10); - c.knowledge = Math.min(c.knowledge, 10); - - this.addItemsToCharacter( - c, // - ":armor.light.studded_leather", - ":weapon.weird.spiked_gauntlets", - ); - - this.addSkillsToCharacter(c, ":weapon.weird.spiked_gauntlets"); - - break; - - // - // DRUID - // ------ - case 2: - case ":druid": - c.foundation = "Druid"; - c.silver = 40; - c.maxHitPoints = this.currentHitPoints = 15; - c.itemSlots = 7; - c.meleeCombat = Math.max(this.meleeCombat, 10); - c.knowledge = Math.min(this.knowledge, 10); - this.addItemsToCharacter( - c, // - ":armor.light.leather", - ":weapon.light.sickle", - ":kit.poisoners_kit", - ":kit.healers_kit", - ); - this.addSkillsToCharacter( - c, // - ":armor.light.sleather", - ":armor.light.hide", - ":weapon.light.sickle", - ); - break; - case 3: - case ":fencer": - c.foundation = "Fencer"; - - // - // Stats - c.maxHitPoints = c.currentHitPoints = 15; - c.meleeCombat = Math.max(c.meleeCombat, 10); - c.magic = Math.min(c.magic, 10); - - // - // Skills - this.addSkillsToCharacter( - c, // - ":weapon.style.two_weapons", - ":armor.light", - ); - - // - // Gear - c.silver = 40; - c.itemSlots = 5; - this.addItemsToCharacter( - c, // - ":armor.light.leather", - ":weapon.light.rapier", - ":weapon.light.dagger", - ); - break; - case 4: - case ":guard": - c.foundation = "Guard"; - - // - // Stats - c.maxHitPoints = c.currentHitPoints = 15; - - c.meleeCombat = Math.max(c.meleeCombat, 10); - c.awareness = Math.max(c.awareness, 10) - c.skulduggery = Math.min(c.skulduggery, 10); - - // - // Skills - this.addSkillsToCharacter( - c, // - ":armor.medium", - ":weapon.weird.halberd", - ); - - // - // Gear - c.silver = 50; - c.itemSlots = 5; - this.addItemsToCharacter( - c, // - ":armor.medium.breastplate", - ":weapon.weird.halberd", - ":lighting.bulls_eye_lantern", - ":misc.signal_whistle", - ":maps.area.hvedstad", - ); - break; - - /* - - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: GUARD -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -| Guard - -|[unstyled] -* Medium Armor - -|[unstyled] -* Halberd -* Bull's Eye Lantern -* Signal Whistle -* Map of Local Area -* 50 Silver Pieces - -|[unstyled] -* 10 Hit Points -* 5 Item Slots -* Awareness raised to 10 -* Melee Combat raised to 10 -* Skulduggery limited to 10 - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: MAGICIAN -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -| Magician - -|[unstyled] -* None - -|[unstyled] -* Tier 2 Wand with random spell. -* Tier 1 Wand with random spell. -* 10 Silver Pieces - -|[unstyled] -* 10 Hit Points -* 6 Item Slots -* Melee Combat limited to 10 -* Ranged Combat limited to 5 -* Magic raised to 10 -* Grit limited to 10 - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: MEDIC -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -|Medic - -|[unstyled] -* Light Armor -* Medium Armor - -|[unstyled] -* Club -* Sling -* 3 Daggers -* Healer's Kit -* 40 Silver Pieces - -|[unstyled] -* 10 Hit Points -* 6 Item Slots - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: RECKLESS -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -| Reckless - -|[unstyled] - -|[unstyled] -* Great Axe -* 50 Silver Pieces - -|[unstyled] -* 20 Hit Points -* 7 Item Slots -* Melee Combat raised to 10 -* Awareness raised to 10 -* Grit raised to 10 -* Magic limited to 10 - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: ROVER -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -| Rover - -|[unstyled] -* Light Armor - -|[unstyled] -* Leather Armor -* Short Sword -* Longbow -* Snare Maker's Kit -* 25 Silver Pieces - -|[unstyled] -* 10 Hit Points -* 5 Item Slots -* Magic Reduced to 10 -* Awareness raised to 10 -* Ranged Combat raised to 10 - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: SKIRMISHER -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -| Skirmisher - -|[unstyled] -* Light Armor -* Shields - -|[unstyled] -* Spear -* Small Shield -* 50 Silver Pieces - - -|[unstyled] -* 15 Hit Points -* 6 Item Slots -* Melee Combat raised to 10 -* Awareness raised to 10 -* Skulduggery raised to 10 -* Grit raised to 10 - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: SNEAK -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -| Sneak - -|[unstyled] -* Light Armor - -|[unstyled] -* 3 daggers -* Small Crossbow -* Poisoner's Kit -* 30 Silver Pieces - - -|[unstyled] -* 10 Hit Points -* 6 Item Slots -* Melee Combat raised to 10 -* Awareness raised to 10 -* Skulduggery raised to 10 -* Grit raised to 10 - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: SPELLSWORD -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -| Spellsword - -|[unstyled] - -|[unstyled] -* Tier 1 Wand with random spell. -* Longsword -* 30 Silver Pieces - -|[unstyled] -* 12 Hit Points -* 5 Item Slots -* Melee Combat raised to 10 -* Ranged Combat limited to 10 -* Magic raised to 10 -* Skulduggery limited to 10 -* Grit raised to 10 - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: SPELUNKER -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -| Spelunker - -|[unstyled] -* None - -|[unstyled] -* Spear -* Caltrops -* Bull's Eye Lantern -* Map Maker's Kit -* Chalk -* Caltrops -* 5 Silver Pieces - -|[unstyled] -* 10 Hit Points -* 4 Item Slots -* Awareness raised to 10 -* Melee Combat raised to 10 -* Skulduggery raised to 10 -* Magic limited to 10 - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: SPIT'N'POLISH -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -| Spit'n' Polish - -|[unstyled] -* Heavy Armor -* Shield - -|[unstyled] -* Half-Plate -* Large Shield -* Long Sword -* 10 Silver Pieces - -|[unstyled] -* 10 Hit Points -* 2 Item Slots -* Melee Combat raised to 10 -* Magic Reduced to 6 -* Awareness Reduced to 10 - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: STILETTO -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -| Stiletto - -|[unstyled] -* Light Armor - -|[unstyled] -* Padded Armor -* 3 Daggers -* Small Crossbow -* Poisoner's Kit -* 20 Silver Pieces - -|[unstyled] -* 10 Hit Points -* 5 Item Slots -* Melee Combat raised to 10 -* Ranged Combat raised to 10 -* Awareness raised to 10 -* Magic limited to 6 -* Knowledge limited to 10 - -// -//--------------------------------------------------------------------------------------- -//HEADLINE: Tinkerer -//--------------------------------------------------------------------------------------- -| {counter:foundation} - -|Tinkerer - -|[unstyled] -* Light Armor - -|[unstyled] -* Studded Leather -* Wrench (club) -* Tinkerer's Kit -* 30 Silver Pieces - -|[unstyled] -* 10 Hit Points -* 5 Item Slots -* Awareness raised to 10 -* Knowledge raised to 10 - - */ - // - // WTF ?! - // ------ - default: - throw new Error(`Invalid foundation id ${foundation}`); + if (foundation == ":random") { + return this.applyFoundation(c, roll.d(20)); // according to the rulebook, roll a d20 and reroll any invalid results. } + + // + // Brawler + // ------ + if (foundation === 1 || foundation === ":brawler") { + c.foundation = "Brawler"; + + c.maxHitPoints = c.currentHitPoints = 15; + c.itemSlots = 7; + c.silver = 40; + + c.meleeCombat = Math.max(c.meleeCombat, 10); + c.knowledge = Math.min(c.knowledge, 10); + + this.addProficienciesToCharacter( + c, + ":armor.light", + ":weapon.weird.spiked_gauntlets" + ); + + this.addItemsToCharacter( + c, // + ":armor.light.studded_leather", + ":weapon.weird.spiked_gauntlets", + ); + + return; + } + + // + // DRUID + // ------ + if (foundation === 2 || foundation === ":druid") { + + c.foundation = "Druid"; + + c.silver = 10; + c.itemSlots = 5; + c.maxHitPoints = this.currentHitPoints = 10; + + this.addProficienciesToCharacter( + c, // + ":armor.light.cloth", + ":armor.light.hide", + ":armor.light.leather", + ":kit.healers_kit", + ":kit.poisoners_kit", + ":weapon.light.sickle", + ":weapon.light.quarterstaff", + ":weapon.light.sling", + ); + + this.addItemsToCharacter( + c, // + ":armor.light.leather", + ":weapon.light.sickle", + ":kit.poisoners_kit", + ":kit.healers_kit", + ); + + return; + } + + // + // FENCER + // ------- + if (foundation === 3 || foundation === ":fencer") { + + c.foundation = "Fencer"; + + c.silver = 40; + c.itemSlots = 5; + c.maxHitPoints = c.currentHitPoints = 15; + + c.clamp("magic", 1, 10) + c.clamp("meleeCombat", 10, undefined) + + this.addProficienciesToCharacter( + c, // + ":perk.riposte", + ":armor.light", + ":weapon.light", + ); + + this.addItemsToCharacter( + c, // + ":armor.light.leather", + ":weapon.light.dagger", + ":weapon.light.rapier", + ); + } + + if (foundation === 4 || foundation === ":guard") { + + c.foundation = "Guard"; + + c.silver = 50; + c.itemSlots = 5; + c.maxHitPoints = c.currentHitPoints = 10 + + c.clamp("awareness", 10, undefined) + c.clamp("meleeCombat", 10, undefined) + c.clamp("skulduggery", 1, 10) + + this.addProficienciesToCharacter( + c, // + ":armor.medium", + ":weapon.heavy", + ":weapon.specialist.halberd", + ":wepaon.light", + ); + + this.addItemsToCharacter( + c, // + ":armor.medium.breastplate", + ":lighting.bulls_eye_lantern", + ":map.city.hovedstad", + ":misc.signal_whistle", + ":weapon.specialist.halberd", + ); + return + } + + if (foundation === 5 || foundation === ":magician") { + + c.foundation = "Magician" + + c.silver = 10; + c.itemSlots = 6 + c.maxHitPoints = c.currentHitPoints = 10 + + c.clamp("grit", 0, 10) + c.clamp("magic", 10, undefined) + c.clamp("meleeCombat", 0, 10) + c.clamp("rangedCombat", 0, 5) + + /* ---- NO PROFICIENCIES ---- */ + + + this.addItemsToCharacter( + c, // + "TODO: [TEIR 2 WAND WITH RANDOM SPELL]", + "TODO: [TEIR 1 WAND WITH RANDOM SPELL]", + ); + return + } + + if (foundation === 6 || foundation === ":medic") { + + c.foundation = "Medic"; + + c.silver = 40; + c.itemSlots = 7; + c.maxHitPoints = c.currentHitPoints = 10 + + this.addProficienciesToCharacter( + c, // + ":armor.light", + ":armor.medium", + ":weapon.light", + ); + + this.addItemsToCharacter( + c, // + ":armor.light.studded_leather", + ":kit.healers_kit", + ":weapon.light.club", + ":weapon.light.dagger", + ":weapon.light.dagger", + ":weapon.light.dagger", + ":weapon.light.sling", + ); + return + } + + if (foundation === 7 || foundation === ":reckless") { + + c.foundation = "Reckless"; + + c.silver = 50; + c.itemSlots = 7; + c.maxHitPoints = c.currentHitPoints = 20 + + c.clamp("awareness", 10, undefined) + c.clamp("grit", 10, undefined) + c.clamp("magic", 1, 10) + c.clamp("meleeCombat", 10, undefined) + + this.addProficienciesToCharacter( + c, // + ":wepaon.heavy", + ); + + this.addItemsToCharacter( + c, // + ":weapon.heavy.great_axe", + ); + return + } + + if (foundation === 8 || foundation === ":rover") { + + c.foundation = "Rover"; + + c.silver = 25; + c.itemSlots = 5; + c.maxHitPoints = c.currentHitPoints = 10 + + c.clamp("awareness", 10, undefined) + c.clamp("magic", 1, 10) + c.clamp("rangedCombat", 10, undefined) + + this.addProficienciesToCharacter( + c, // + ":armor.light", + ":weapon.light", + ); + + this.addItemsToCharacter( + c, // + ":armor.light.leather", + ":kit.snare_makers_kit", + ":weapon.heavy.longbow", + ":weapon.light.short_sword", + ":map.shayland", // Shayland is the region around Hovedstad + ); + return + } + + /* + + // + //--------------------------------------------------------------------------------------- + //HEADLINE: ROVER + //--------------------------------------------------------------------------------------- + | {counter:foundation} + + | Rover + + |[unstyled] + * Light Armor + + |[unstyled] + * Leather Armor + * Short Sword + * Longbow + * Snare Maker's Kit + * 25 Silver Pieces + + |[unstyled] + * 10 Hit Points + * 5 Item Slots + * Magic Reduced to 10 + * Awareness raised to 10 + * Ranged Combat raised to 10 + + // + //--------------------------------------------------------------------------------------- + //HEADLINE: SKIRMISHER + //--------------------------------------------------------------------------------------- + | {counter:foundation} + + | Skirmisher + + |[unstyled] + * Light Armor + * Shields + + |[unstyled] + * Spear + * Small Shield + * 50 Silver Pieces + + + |[unstyled] + * 15 Hit Points + * 6 Item Slots + * Melee Combat raised to 10 + * Awareness raised to 10 + * Skulduggery raised to 10 + * Grit raised to 10 + + // + //--------------------------------------------------------------------------------------- + //HEADLINE: SNEAK + //--------------------------------------------------------------------------------------- + | {counter:foundation} + + | Sneak + + |[unstyled] + * Light Armor + + |[unstyled] + * 3 daggers + * Small Crossbow + * Poisoner's Kit + * 30 Silver Pieces + + + |[unstyled] + * 10 Hit Points + * 6 Item Slots + * Melee Combat raised to 10 + * Awareness raised to 10 + * Skulduggery raised to 10 + * Grit raised to 10 + + // + //--------------------------------------------------------------------------------------- + //HEADLINE: SPELLSWORD + //--------------------------------------------------------------------------------------- + | {counter:foundation} + + | Spellsword + + |[unstyled] + + |[unstyled] + * Tier 1 Wand with random spell. + * Longsword + * 30 Silver Pieces + + |[unstyled] + * 12 Hit Points + * 5 Item Slots + * Melee Combat raised to 10 + * Ranged Combat limited to 10 + * Magic raised to 10 + * Skulduggery limited to 10 + * Grit raised to 10 + + // + //--------------------------------------------------------------------------------------- + //HEADLINE: SPELUNKER + //--------------------------------------------------------------------------------------- + | {counter:foundation} + + | Spelunker + + |[unstyled] + * None + + |[unstyled] + * Spear + * Caltrops + * Bull's Eye Lantern + * Map Maker's Kit + * Chalk + * Caltrops + * 5 Silver Pieces + + |[unstyled] + * 10 Hit Points + * 4 Item Slots + * Awareness raised to 10 + * Melee Combat raised to 10 + * Skulduggery raised to 10 + * Magic limited to 10 + + // + //--------------------------------------------------------------------------------------- + //HEADLINE: SPIT'N'POLISH + //--------------------------------------------------------------------------------------- + | {counter:foundation} + + | Spit'n' Polish + + |[unstyled] + * Heavy Armor + * Shield + + |[unstyled] + * Half-Plate + * Large Shield + * Long Sword + * 10 Silver Pieces + + |[unstyled] + * 10 Hit Points + * 2 Item Slots + * Melee Combat raised to 10 + * Magic Reduced to 6 + * Awareness Reduced to 10 + + // + //--------------------------------------------------------------------------------------- + //HEADLINE: STILETTO + //--------------------------------------------------------------------------------------- + | {counter:foundation} + + | Stiletto + + |[unstyled] + * Light Armor + + |[unstyled] + * Padded Armor + * 3 Daggers + * Small Crossbow + * Poisoner's Kit + * 20 Silver Pieces + + |[unstyled] + * 10 Hit Points + * 5 Item Slots + * Melee Combat raised to 10 + * Ranged Combat raised to 10 + * Awareness raised to 10 + * Magic limited to 6 + * Knowledge limited to 10 + + // + //--------------------------------------------------------------------------------------- + //HEADLINE: Tinkerer + //--------------------------------------------------------------------------------------- + | {counter:foundation} + + |Tinkerer + + |[unstyled] + * Light Armor + + |[unstyled] + * Studded Leather + * Wrench (club) + * Tinkerer's Kit + * 30 Silver Pieces + + |[unstyled] + * 10 Hit Points + * 5 Item Slots + * Awareness raised to 10 + * Knowledge raised to 10 + + */ + return this.applyFoundation(c, ":random") } } - -if (Math.PI < 0 && Player) { - ("STFU Linda"); -} diff --git a/utils/random.js b/utils/random.js index 29fa0fe..1c88b70 100755 --- a/utils/random.js +++ b/utils/random.js @@ -76,13 +76,13 @@ export class Xorshift32 { } /** - * @param {...} ... pick random function argument - * @returns {} + * @method + * @template T + * @param {...T} args pick random function argument + * @returns {T} */ oneOf(...args) { - const idx = this.lowerThan(args.length); - - return args[idx]; + return this.randomElement(args) } /**