rearrage_stuff

This commit is contained in:
Kim Ravn Hansen
2025-09-16 11:26:40 +02:00
parent 40e8c5e0ab
commit 3f11ebe6dc
4937 changed files with 1146031 additions and 134 deletions

588
seeders/characerSeeder.js Executable file
View File

@@ -0,0 +1,588 @@
// ____ _ _
// / ___| |__ __ _ _ __ __ _ ___| |_ ___ _ __
// | | | '_ \ / _` | '__/ _` |/ __| __/ _ \ '__|
// | |___| | | | (_| | | | (_| | (__| || __/ |
// \____|_| |_|\__,_|_| \__,_|\___|\__\___|_|
// ____ _
// / ___| ___ ___ __| | ___ _ __
// \___ \ / _ \/ _ \/ _` |/ _ \ '__|
// ___) | __/ __/ (_| | __/ |
// |____/ \___|\___|\__,_|\___|_|
// ------------------------------------------------
import { Character } from "../models/character.js";
import { gGame } from "../models/globals.js";
import { Player } from "../models/player.js";
import { isIdSane } from "../utils/id.js";
// stupid convenience hack that only works if we only have a single Game in the system.
// Which we easily could have.!!
let roll = {};
export class CharacterSeeder {
constructor() {
// stupid convenience hack that only works if we only have a single Game in the system.
// 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);
},
};
}
/**
* Create an item, using an item blueprint with the given name
*
* @param {string} itemBlueprintId id of the item blueprint
* @returns {Item|undefined}
*/
item(itemBlueprintId) {}
/**
* @param {Character} character
* @param {...string} itemBlueprintIds
*/
addItemsToCharacter(character, ...itemBlueprintIds) {
for (const id of itemBlueprintIds) {
const blueprint = gGame.getItemBlueprint(id);
if (!blueprint) {
throw new Error(`No blueprint found for id: ${id}`);
}
const item = blueprint.createItem();
character.addItem(item);
}
}
/**
* @param {Character} character
* @param {...string} skills
*/
addSkillsToCharacter(character, ...skills) {
for (const skill of skills) {
if (!isIdSane(skill)) {
throw new Error(`Skill id >>${skill}<< is insane!`);
}
character.skills.add(skill);
}
}
/**
* Foundation function
* @name FoundationFunction
* @function
* @param {Character} The character to which we apply this foundation.
*/
createCharacter() {
const c = new Character();
//
// Initializing
//
// Rolling skills
c.name =
gGame.random.oneOf("sir ", "madam ", "mister ", "miss ", "", "", "") + // prefix
"random " + // name
gGame.random.get().toString(); // suffix
c.awareness = roll.d6() + 2;
c.grit = roll.d6() + 2;
c.knowledge = roll.d6() + 2;
c.magic = roll.d6() + 2;
c.meleeCombat = roll.d6() + 2;
c.rangedCombat = roll.d6() + 2;
c.skulduggery = roll.d6() + 2;
let ancestryId = roll.d8();
switch (ancestryId) {
case 1:
c.ancestry = "human";
// Humans get +1 to all skills
c.awareness++;
c.grit++;
c.knowledge++;
c.magic++;
c.meleeCombat++;
c.rangedCombat++;
c.skulduggery++;
break;
case 2:
c.ancestry = "dwarven";
c.meleeCombat = Math.max(c.meleeCombat, 10);
break;
case 3:
c.ancestry = "elven";
c.rangedCombat = Math.max(c.rangedCombat, 10);
break;
case 4:
c.ancestry = "giant";
c.meleeCombat = Math.max(c.grit, 10);
break;
case 5:
c.ancestry = "gnomish";
c.meleeCombat = Math.max(c.awareness, 10);
break;
case 6:
c.ancestry = "primordial";
c.meleeCombat = Math.max(c.magic, 10);
break;
case 7:
c.ancestry = "draconic";
c.meleeCombat = Math.max(c.knowledge, 10);
break;
case 8:
c.ancestry = "demonic";
c.meleeCombat = Math.max(c.skulduggery, 10);
break;
default:
throw new Error(`Logic error, ancestry d8() roll of ${ancestryId} was out of scope"`);
}
this.applyFoundation(c);
console.debug(c);
return c;
}
/**
* Create characters for the given player
*
* The characters are automatically added to the player's party
*
* @param {Player} player
* @param {number} partySize
*
* @return {Character[]}
*/
createParty(player, partySize) {
//
for (let i = 0; i < partySize; i++) {
player.addCharacter(
this.createCharacter(player), //
);
}
}
/**
* @param {Character} c
* @param {string|number} Foundation to add to character
*/
applyFoundation(c, foundation = ":random") {
switch (foundation) {
case ":random":
return this.applyFoundation(c, roll.d(3));
break;
//
// 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, //
":perk.two_weapons", // TODO: perks should be their own thing, and not a part of the skill system?
":armor.light",
);
//
// Gear
c.silver = 40;
c.itemSlots = 5;
this.addItemsToCharacter(
c, //
":armor.light.leather",
":weapon.light.rapier",
":weapon.light.dagger",
);
break;
/*
//
//---------------------------------------------------------------------------------------
//HEADLINE: Fencer
//---------------------------------------------------------------------------------------
| {counter:foundation}
|Fencer
|[unstyled]
* Light Armor
|[unstyled]
* Leather
* Rapier
* Dagger
* 40 Silver Pieces
|[unstyled]
//
//---------------------------------------------------------------------------------------
//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}`);
}
}
}

27
seeders/gameSeeder.js Executable file
View File

@@ -0,0 +1,27 @@
import { Config } from "../config.js";
import { gGame } from "../models/globals.js";
import { CharacterSeeder } from "./characerSeeder.js";
import { ItemSeeder } from "./itemSeeder.js";
import { PlayerSeeder } from "./playerSeeder.js";
/**
* Create and populate a Game object.
*
* This seeder creates all models necessary to play the game.
*
* If dev mode, we create some known debug logins. (username = user, password = pass) as well as a few others
*/
export class GameSeeder {
seed() {
console.info("seeding");
gGame.rngSeed = Config.rngSeed;
new PlayerSeeder().seed(); // Create debug players
new ItemSeeder().seed(); // Create items, etc.
new CharacterSeeder().createParty(gGame.getPlayer("user"), 3); // Create debug characters.
//
// Done
console.info("seeding done");
}
}

104
seeders/itemSeeder.js Executable file
View File

@@ -0,0 +1,104 @@
import { ItemBlueprint } from "../models/item.js";
import { gGame } from "../models/globals.js";
//
// ___ _ _____ _ _
// |_ _| |_ ___ _ __ ___ |_ _|__ _ __ ___ _ __ | | __ _| |_ ___ ___
// | || __/ _ \ '_ ` _ \ | |/ _ \ '_ ` _ \| '_ \| |/ _` | __/ _ \/ __|
// | || || __/ | | | | | | | __/ | | | | | |_) | | (_| | || __/\__ \
// |___|\__\___|_| |_| |_| |_|\___|_| |_| |_| .__/|_|\__,_|\__\___||___/
// |_|
//
// Seed the Game.ItemBlueprint store
export class ItemSeeder {
seed() {
//
// __ __
// \ \ / /__ __ _ _ __ ___ _ __ ___
// \ \ /\ / / _ \/ _` | '_ \ / _ \| '_ \/ __|
// \ V V / __/ (_| | |_) | (_) | | | \__ \
// \_/\_/ \___|\__,_| .__/ \___/|_| |_|___/
// |_|
//-------------------------------------------------------
gGame.addItemBlueprint(":weapon.light.dagger", {
name: "Dagger",
description: "Small shady blady",
itemSlots: 0.5,
damage: 3,
melee: true,
ranged: true,
specialEffect: ":effect.weapon.fast",
});
gGame.addItemBlueprint(":weapon.light.sickle", {
name: "Sickle",
description: "For cutting nuts, and branches",
itemSlots: 1,
damage: 4,
specialEffect: ":effect.weapon.sickle",
});
gGame.addItemBlueprint(":weapon.weird.spiked_gauntlets", {
name: "Spiked Gauntlets",
description: "Spikes with gauntlets on them!",
itemSlots: 1,
damage: 5,
specialEffect: "TBD",
});
gGame.addItemBlueprint(":weapon.light.rapier", {
name: "Rapier",
description: "Fancy musketeer sword",
itemSlots: 1,
damage: 5,
specialEffect: "TBD",
});
//
// _
// / \ _ __ _ __ ___ ___ _ __ ___
// / _ \ | '__| '_ ` _ \ / _ \| '__/ __|
// / ___ \| | | | | | | | (_) | | \__ \
// /_/ \_\_| |_| |_| |_|\___/|_| |___/
// ---------------------------------------
gGame.addItemBlueprint(":armor.light.studded_leather", {
name: "Studded Leather Armor",
description: "Padded and hardened leather with metal stud reinforcement",
itemSlots: 3,
specialEffect: "TBD",
armorHitPoints: 10,
});
gGame.addItemBlueprint(":armor.light.leather", {
name: "Leather Armor",
description: "Padded and hardened leather",
itemSlots: 2,
specialEffect: "TBD",
armorHitPoints: 6,
});
//
// _ ___ _
// | |/ (_) |_ ___
// | ' /| | __/ __|
// | . \| | |_\__ \
// |_|\_\_|\__|___/
// -------------------
gGame.addItemBlueprint(":kit.poisoners_kit", {
name: "Poisoner's Kit",
description: "Allows you to create poisons that can be applied to weapons",
itemSlots: 2,
specialEffect: "TBD",
count: 20,
maxCount: 20,
});
gGame.addItemBlueprint(":kit.healers_kit", {
name: "Healer's Kit",
description: "Allows you to heal your teammates outside of combat",
itemSlots: 2,
specialEffect: "TBD",
count: 20,
maxCount: 20,
});
}
}

28
seeders/playerSeeder.js Executable file
View File

@@ -0,0 +1,28 @@
import { gGame } from "../models/globals.js";
import { Player } from "../models/player.js";
export class PlayerSeeder {
seed() {
// Examples of the word "pass" hashed by the client and then the server:
// Note that the word "pass" has gajillions of hashed representations, all depending on the salts used to hash them.
// "pass" hashed by client: KimsKrappyKryptoV1:userSalt:1000:SHA-256:b106e097f92ff7c288ac5048efb15f1a39a15e5d64261bbbe3f7eacee24b0ef4
// "pass" hashed by server: 1000:15d79316f95ff6c89276308e4b9eb64d:2178d5ded9174c667fe0624690180012f13264a52900fe7067a13f235f4528ef
//
// Since the server-side hashes have random salts, the hashes themselves can change for the same password.
// The client side hash must not have a random salt, otherwise, it must change every time.
//
// The hash below is just one has that represents the password "pass" sent via V1 of the "Kims Krappy Krypto" scheme.
gGame.createPlayer(
"user",
"1000:15d79316f95ff6c89276308e4b9eb64d:2178d5ded9174c667fe0624690180012f13264a52900fe7067a13f235f4528ef",
"userSalt",
);
gGame.createPlayer(
"admin",
"1000:a84760824d28a9b420ee5f175a04d1e3:a6694e5c9fd41d8ee59f0a6e34c822ee2ce337c187e2d5bb5ba8612d6145aa8e",
"adminSalt",
);
}
}