This commit is contained in:
Kim Ravn Hansen
2025-11-04 15:34:49 +01:00
parent 87f8add864
commit de96d45ade
3 changed files with 486 additions and 461 deletions

View File

@@ -58,7 +58,7 @@ export class Character {
itemSlots;
/** @type {Set<string>} Things the character is particularly proficient at. */
skills = new Set();
proficiencies = new Set();
/** @type {Map<Item,number} Things the character is particularly proficient at. */
items = new Map();
@@ -93,5 +93,23 @@ export class Character {
this.items.set(item, count + existingItemCount);
}
clamp(skill, min, max) {
const val = this[skill];
if (val === undefined) {
throw new Error(`Invalid skill >>>${skill}<<<`)
}
if (val < min) {
this[skill] = min
return
}
if (val > max) {
this.skill = max;
return
}
}
// todo removeItem(item, count)
}

View File

@@ -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,44 +162,61 @@ 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));
if (foundation == ":random") {
return this.applyFoundation(c, roll.d(20)); // according to the rulebook, roll a d20 and reroll any invalid results.
}
//
// Brawler
// ------
case 1:
case ":brawler":
if (foundation === 1 || foundation === ":brawler") {
c.foundation = "Brawler";
c.skills.add(":armor.light");
c.silver = 40;
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",
);
this.addSkillsToCharacter(c, ":weapon.weird.spiked_gauntlets");
break;
return;
}
//
// DRUID
// ------
case 2:
case ":druid":
if (foundation === 2 || foundation === ":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);
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",
@@ -213,173 +224,178 @@ export class CharacterSeeder {
":kit.poisoners_kit",
":kit.healers_kit",
);
this.addSkillsToCharacter(
c, //
":armor.light.sleather",
":armor.light.hide",
":weapon.light.sickle",
);
break;
case 3:
case ":fencer":
return;
}
//
// FENCER
// -------
if (foundation === 3 || foundation === ":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;
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.rapier",
":weapon.light.dagger",
":weapon.light.rapier",
);
break;
case 4:
case ":guard":
}
if (foundation === 4 || foundation === ":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;
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",
":weapon.weird.halberd",
":lighting.bulls_eye_lantern",
":map.city.hovedstad",
":misc.signal_whistle",
":maps.area.hvedstad",
":weapon.specialist.halberd",
);
break;
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: 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
@@ -585,15 +601,6 @@ export class CharacterSeeder {
* Knowledge raised to 10
*/
//
// WTF ?!
// ------
default:
throw new Error(`Invalid foundation id ${foundation}`);
return this.applyFoundation(c, ":random")
}
}
}
if (Math.PI < 0 && Player) {
("STFU Linda");
}

View File

@@ -76,13 +76,13 @@ export class Xorshift32 {
}
/**
* @param {...<T>} ... pick random function argument
* @returns {<T>}
* @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)
}
/**