XP system

This commit is contained in:
Pyrex 2025-02-02 21:47:45 -08:00
parent 783dcd0ca3
commit c23a7b6d75
9 changed files with 75 additions and 10 deletions

BIN
src/art/pickups/chest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

View File

@ -2,6 +2,9 @@
export type Stat = "AGI" | "INT" | "CHA" | "PSI"; export type Stat = "AGI" | "INT" | "CHA" | "PSI";
export const ALL_STATS: Array<Stat> = ["AGI", "INT", "CHA", "PSI"]; export const ALL_STATS: Array<Stat> = ["AGI", "INT", "CHA", "PSI"];
export type Resource = "EXP";
export const ALL_RESOURCES: Array<Resource> = ["EXP"]
export type SkillGoverning = { export type SkillGoverning = {
stats: Stat[], underTarget: number, target: number, cost: number, note: string stats: Stat[], underTarget: number, target: number, cost: number, note: string
}; };

View File

@ -24,7 +24,7 @@ export class Hud {
y += 16; y += 16;
} }
D.drawText("EXP", new Point(0, 128), FG_BOLD); D.drawText("EXP", new Point(0, 128), FG_BOLD);
D.drawText("0", new Point(32, 128), FG_TEXT); D.drawText(`${prog.getExperience()}`, new Point(32, 128), FG_TEXT);
D.drawText("BLD", new Point(0, 144), FG_BOLD); D.drawText("BLD", new Point(0, 144), FG_BOLD);
D.drawText(`${prog.getBlood()}cc`, new Point(32, 144), FG_TEXT); D.drawText(`${prog.getBlood()}cc`, new Point(32, 144), FG_TEXT);
} }

View File

@ -1,14 +1,15 @@
import {Grid, Point, Rect, Size} from "./engine/datatypes.ts"; import {Grid, Point, Rect, Size} from "./engine/datatypes.ts";
import {ConceptualCell, maps} from "./maps.ts"; import {ConceptualCell, maps} from "./maps.ts";
import {ALL_STATS, Stat} from "./datatypes.ts"; import {ALL_STATS, Resource, Stat} from "./datatypes.ts";
import {DrawPile} from "./drawpile.ts"; import {DrawPile} from "./drawpile.ts";
import {D} from "./engine/public.ts"; import {D} from "./engine/public.ts";
import {sprDrips, sprRaccoonWalking, sprStatPickup} from "./sprites.ts"; import {sprDrips, sprRaccoonWalking, sprResourcePickup, sprStatPickup} from "./sprites.ts";
import {BG_INSET, FG_TEXT} from "./colors.ts"; import {BG_INSET, FG_TEXT} from "./colors.ts";
import {getPlayerProgress} from "./playerprogress.ts"; import {getPlayerProgress} from "./playerprogress.ts";
export type MapCellContent = export type MapCellContent =
{type: "statPickup", stat: Stat} | {type: "statPickup", stat: Stat} |
{type: "resourcePickup", resource: Resource} |
{type: "stairs"} | {type: "stairs"} |
{type: "empty"} | {type: "empty"} |
{type: "block"} {type: "block"}
@ -112,9 +113,13 @@ export class HuntMode {
let gsp = (): MapCellContent => { let gsp = (): MapCellContent => {
return {type: "statPickup", stat: choose(ALL_STATS)} return {type: "statPickup", stat: choose(ALL_STATS)}
}; };
let exp = (): MapCellContent => {
return {type: "resourcePickup", resource: "EXP"}
}
// TODO: Other objects? // TODO: Other objects?
return choose([ return choose([
gsp, gsp, gsp, gsp gsp, gsp, gsp, gsp,
exp,
])(); ])();
} }
@ -142,17 +147,31 @@ export class HuntMode {
#collectResources() { #collectResources() {
let present = this.cells.get(this.player); let present = this.cells.get(this.player);
if (present.content.type == "statPickup") { if (present.content.type == "statPickup") {
let stat = present.content.stat; let stat = present.content.stat;
let amount = 1; let amount = 1;
present.content = {type: "empty"}; present.content = {type: "empty"};
getPlayerProgress().add(stat, amount); getPlayerProgress().add(stat, amount);
} }
if (present.content.type == "resourcePickup") {
let resource = present.content.resource;
switch(resource) {
case "EXP":
getPlayerProgress().addExperience(25);
break;
default:
throw `not sure how to add ${resource}`
}
}
present.content = {type: "empty"};
} }
#computeCostToMoveTo(mapPosition: Point): number | null { #computeCostToMoveTo(mapPosition: Point): number | null {
let present = this.cells.get(mapPosition); let present = this.cells.get(mapPosition);
if (present.content.type == "statPickup") { if (present.content.type == "statPickup" || present.content.type == "resourcePickup") {
return 100; return 100;
} }
if (present.content.type == "empty") { if (present.content.type == "empty") {
@ -267,6 +286,20 @@ export class HuntMode {
) )
}); });
} }
if (cellData.content.type == "resourcePickup" && cellData.content.resource == "EXP") {
this.drawpile.add(inAir, () => {
D.drawSprite(
sprResourcePickup,
cellOffset.offset(new Point(0, -16 * 3)),
0,
{
xScale: 3,
yScale: 3,
}
);
});
}
} }
#drawPlayer(globalOffset: Point) { #drawPlayer(globalOffset: Point) {

View File

@ -3,6 +3,7 @@ import {getSkills} from "./skills.ts";
export class PlayerProgress { export class PlayerProgress {
#stats: Record<Stat, number> #stats: Record<Stat, number>
#exp: number;
#blood: number #blood: number
#skillsLearned: number[] // use the raw ID representation for indexOf #skillsLearned: number[] // use the raw ID representation for indexOf
#untrimmedSkillsAvailable: Skill[] #untrimmedSkillsAvailable: Skill[]
@ -14,6 +15,7 @@ export class PlayerProgress {
CHA: 10, CHA: 10,
PSI: 10, PSI: 10,
}; };
this.#exp = 0;
this.#blood = 0; this.#blood = 0;
this.#skillsLearned = []; this.#skillsLearned = [];
this.#untrimmedSkillsAvailable = [] this.#untrimmedSkillsAvailable = []
@ -82,6 +84,21 @@ export class PlayerProgress {
this.#stats[stat] += amount; this.#stats[stat] += amount;
} }
addExperience(amt: number) {
this.#exp += amt;
}
getExperience(): number {
return this.#exp
}
spendExperience(cost: number) {
if (this.#exp < cost) {
throw `can't spend ${cost}`
}
this.#exp -= cost;
}
getStat(stat: Stat): number { getStat(stat: Stat): number {
return this.#stats[stat] return this.#stats[stat]
} }
@ -97,9 +114,6 @@ export class PlayerProgress {
getAvailableSkills(): Skill[] { getAvailableSkills(): Skill[] {
// Sort by cost, then by name, then trim down to first 6 // Sort by cost, then by name, then trim down to first 6
let skillsAvailable = [...this.#untrimmedSkillsAvailable]; let skillsAvailable = [...this.#untrimmedSkillsAvailable];
skillsAvailable.sort((a, b) => {
return getSkills().computeCost(a) - getSkills().computeCost(b)
});
skillsAvailable.sort((a, b) => { skillsAvailable.sort((a, b) => {
let name1 = getSkills().get(a).profile.name; let name1 = getSkills().get(a).profile.name;
let name2 = getSkills().get(b).profile.name; let name2 = getSkills().get(b).profile.name;
@ -108,8 +122,12 @@ export class PlayerProgress {
if (name1 > name2) { return 1; } if (name1 > name2) { return 1; }
return 0; return 0;
}); });
skillsAvailable.sort((a, b) => {
return getSkills().computeCost(a) - getSkills().computeCost(b)
});
return skillsAvailable.slice(0, 6) return skillsAvailable.slice(0, 6)
} }
} }
let active: PlayerProgress = new PlayerProgress(); let active: PlayerProgress = new PlayerProgress();

View File

@ -53,7 +53,6 @@ function geomInterpolate(
if (x >= highIn) { return lowOut; } if (x >= highIn) { return lowOut; }
const proportion = 1.0 - (x - lowIn) / (highIn - lowIn); const proportion = 1.0 - (x - lowIn) / (highIn - lowIn);
console.log(`proportion: ${x} ${proportion}`)
return lowOut * Math.pow(highOut / lowOut, proportion) return lowOut * Math.pow(highOut / lowOut, proportion)
} }

View File

@ -89,6 +89,7 @@ export class SkillsModal {
let selection = this.#skillSelection; let selection = this.#skillSelection;
if (selection != null) { if (selection != null) {
let data = getSkills().get(selection); let data = getSkills().get(selection);
let cost = getSkills().computeCost(selection);
let size = this.#size; let size = this.#size;
let remainingWidth = size.w - 160; let remainingWidth = size.w - 160;
@ -99,8 +100,14 @@ export class SkillsModal {
// add learn button // add learn button
let drawButtonRect = new Rect(new Point(160, 96), new Size(remainingWidth, 32)) let drawButtonRect = new Rect(new Point(160, 96), new Size(remainingWidth, 32))
let canAfford = getPlayerProgress().getExperience() >= cost;
let caption = `Learn ${data.profile.name}` let caption = `Learn ${data.profile.name}`
addButton(this.#drawpile, caption, drawButtonRect, true, () => { if (!canAfford) {
caption = `Can't Afford`;
}
addButton(this.#drawpile, caption, drawButtonRect, canAfford, () => {
getPlayerProgress().spendExperience(cost);
getPlayerProgress().learnSkill(selection); getPlayerProgress().learnSkill(selection);
}) })
} }

View File

@ -7,6 +7,7 @@ import imgSnake from "./art/characters/snake.png";
*/ */
import imgRaccoon from "./art/characters/raccoon.png"; import imgRaccoon from "./art/characters/raccoon.png";
import imgRaccoonWalking from "./art/characters/raccoon_walking.png"; import imgRaccoonWalking from "./art/characters/raccoon_walking.png";
import imgResourcePickup from "./art/pickups/resources.png";
import imgStatPickup from "./art/pickups/stats.png"; import imgStatPickup from "./art/pickups/stats.png";
import imgDrips from "./art/tilesets/drips.png"; import imgDrips from "./art/tilesets/drips.png";
import {Point, Size} from "./engine/datatypes.ts"; import {Point, Size} from "./engine/datatypes.ts";
@ -28,6 +29,10 @@ export let sprRaccoonWalking = new Sprite(
new Size(64, 64), new Point(32, 32), new Size(8, 1), new Size(64, 64), new Point(32, 32), new Size(8, 1),
8 8
); );
export let sprResourcePickup = new Sprite(
imgResourcePickup, new Size(32, 32), new Point(16, 16),
new Size(1, 1), 1
);
export let sprStatPickup = new Sprite( export let sprStatPickup = new Sprite(
imgStatPickup, new Size(32, 32), new Point(16, 16), imgStatPickup, new Size(32, 32), new Point(16, 16),