Skip to content

Commit

Permalink
Merge pull request #7 from cceckman/light
Browse files Browse the repository at this point in the history
Implement light levels and items
  • Loading branch information
cceckman authored Jun 5, 2024
2 parents 68211d7 + b34fdea commit f2a295e
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 33 deletions.
102 changes: 89 additions & 13 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@ const DEATH_DESCRIPTION = `<div>You are overcome. Darkness descends and your bre
<div>Return to the place of honor as a new investigator?<br />("restart")</div>`;

function canonicalizeSense(verb) {
const lower = verb.toLowerCase();
for (const [canon, aliases] of Object.entries(INSPECTIONS)) {
if (lower === canon || aliases.includes(lower)) {
if (verb === canon || aliases.includes(verb)) {
return canon;
}
}
Expand Down Expand Up @@ -148,14 +147,16 @@ class Room {
constructor(roomid, permanent, saved) {
const static_room = permanent.rooms[roomid];

this.rad_rate = static_room.rad_rate;

// mapping of sense to description
this.senses = static_room.senses;

this.exits = static_room.exits;
this.rad_rate = static_room.rad_rate;
this.droneVolume = static_room.droneVolume;

this.drone_volume = static_room.drone_volume;
this.lightLevel = saved?.rooms[roomid]?.lightLevel ??
permanent.rooms[roomid].lightLevel ??
0;

this.items = {}; // ID to description
if (saved !== null && saved !== undefined) {
Expand All @@ -173,6 +174,10 @@ class Room {
}
});
}

getLightLevel() {
return Object.values(this.items).reduce((acc, item) => acc + (item.lightLevel ?? 0), this.lightLevel);
}
}

class Player {
Expand Down Expand Up @@ -251,10 +256,31 @@ class State {
this.player = new Player(saved)
this.currentDescription = "";

this.music.setDroneVolume(this.currentRoom().drone_volume)
this.music.setDroneVolume(this.currentRoom().droneVolume)
this.music.restartArpeggio();
}

// Functions that act based on sight -- for items, or for rooms --
// should check the "light level" before having their effect.
// This is the sum of:
// - the room's ambient light (e.g. sky)
// - any items in the room that glow (e.g. lightbulbs)
// - multiple sources can sum
// - any items in the room / state that would _prevent_ light transmission
// (...power outage???)
//
// If greater than zero, the investigator can see.
//
// TODO:
// - Investigator-carried lights
// - Investigator blindness, as a -Infinity value
// TODO: Generalize across senses? An acrid smell or a gas mask,
// earplugs to cancel hearing... deafness from a "bang"
getLightLevel() {
// TODO: implement blindness, summing a player's light-level value.
return this.currentRoom().getLightLevel()
}

render() {
console.log(this);

Expand All @@ -271,10 +297,13 @@ class State {
this.symptoms.innerText = selectedSymptom;
}

if (!this.currentDescription) {
this.currentDescription = this.renderPassiveSenses();
if (this.currentDescription) {
this.perception.innerHTML = this.currentDescription;
} else {
// Note: we re-render this every time, rather than setting currentDescription,
// so that we get updates if e.g. items change.
this.perception.innerHTML = this.renderPassiveSenses();
}
this.perception.innerHTML = this.currentDescription;

this.errors.innerText = this.lastError;
if (this.lastError === "") {
Expand All @@ -286,6 +315,13 @@ class State {
}

renderPassiveSense(sense) {
// TODO: distinguish "active" and "passive" touch.
// "active" touch would be walking around and poking things, feeling out the walls;
// "passive" touch would be airflow, temperature, etc.
if (sense === "see" && this.getLightLevel() <= 0) {
return `You cannot see.`
}

const room = this.currentRoom();
let senses = [room.senses[sense]];
const itemPassives = Object.entries(room.items).map(
Expand All @@ -310,6 +346,7 @@ class State {

return `You ${sense} ${senses.join(joiner)}.`;
}

renderPassiveSenses() {
return `
${Object.keys(DEFAULT_ROOM_SENSES)
Expand All @@ -329,7 +366,7 @@ ${Object.keys(DEFAULT_ROOM_SENSES)

const tokenizedAction = this.textin.value.split(" ");
const verb = tokenizedAction[0].toLowerCase();
const restString = tokenizedAction.slice(1).join(" ");
const restString = tokenizedAction.slice(1).join(" ").trim().toLowerCase();

// TODO - if player is null only allow the "restart" action (or "continue", or whatever we call it)
if (this.player === null) {
Expand All @@ -347,6 +384,12 @@ ${Object.keys(DEFAULT_ROOM_SENSES)
this.lastError = this.movePlayer(restString);
} else if (perceptionVerb) {
this.lastError = await this.inspect(perceptionVerb, restString);
} else if (verb && restString) {
// Unknown verb, but there's also an object.
// Try applying the verb to the object.
// Take the error, or the empty string if Charles forgot to add a return value
// from the function.
this.lastError = String(this.itemAction(verb, restString) ?? "");
} else if (verb) {
// Unknown action.
this.lastError = `I don't know ${verb}`;
Expand All @@ -361,12 +404,33 @@ ${Object.keys(DEFAULT_ROOM_SENSES)
this.render();
}

// Try to perform the provided action with any items in the room.
itemAction(verb, rest) {
const room = this.currentRoom();
for (const [itemId, item] of Object.entries(room.items)) {
if (!item.aliases.includes(rest)) {
continue;
}
// Found the item:
console.log(`applying ${verb} to ${itemId}`)
for (const action of (item.action ?? [])) {
if (action.aliases.includes(verb)) {
// Found it!
return action.callback(item, this);
}
}
// Found no such action for this item.
return `Cannot ${verb} ${rest}`;
}
return `Cannot find ${rest}`;
}

movePlayer(direction) {
const destination = this.currentRoom().exits[direction];
if (destination) {
this.player.location = destination;
this.currentDescription = this.renderPassiveSenses();
this.music.setDroneVolume(this.currentRoom().drone_volume);
this.music.setDroneVolume(this.currentRoom().droneVolume);
return "";
}
return `Cannot move to ${direction}`;
Expand All @@ -387,9 +451,21 @@ ${Object.keys(DEFAULT_ROOM_SENSES)
this.currentDescription =
item.sense[verb] ?? DEFAULT_ITEM_SENSES[verb];

// Special-casing sight:
if (verb !== "see") {
return "";
}

// Sight special cases:
if (this.getLightLevel() <= 0) {
this.currentDescription = `You cannot see the ${itemName}.`
return ""
}

// If this verb allows perceiving writing,
// and we have writing, output it.
if (["see"].includes(verb) && item.writing) {
// TODO: Allow writing-by-touch, for engraved writing
if (item.writing) {
let hidden = await hideText(
item.writing,
this.player.knowledge
Expand All @@ -401,7 +477,7 @@ The text reads:
`;
}

if (["see"].includes(verb) && item.rosetta && this.learn(item.rosetta)) {
if (item.rosetta && this.learn(item.rosetta)) {
let hidden = await hideText(item.rosetta, new Set());
let unhidden = item.rosetta;

Expand Down
Loading

0 comments on commit f2a295e

Please sign in to comment.