Add some sound functionality
This commit is contained in:
BIN
app_web/assets/Greek7EchoHall.wav
Normal file
BIN
app_web/assets/Greek7EchoHall.wav
Normal file
Binary file not shown.
BIN
app_web/assets/cave1.wav
Normal file
BIN
app_web/assets/cave1.wav
Normal file
Binary file not shown.
BIN
app_web/assets/scroll.wav
Normal file
BIN
app_web/assets/scroll.wav
Normal file
Binary file not shown.
BIN
app_web/assets/windy1.wav
Normal file
BIN
app_web/assets/windy1.wav
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
assets/Greek7EchoHall.wav
Normal file
BIN
assets/Greek7EchoHall.wav
Normal file
Binary file not shown.
BIN
assets/cave1.wav
Normal file
BIN
assets/cave1.wav
Normal file
Binary file not shown.
BIN
assets/scroll.wav
Normal file
BIN
assets/scroll.wav
Normal file
Binary file not shown.
BIN
assets/windy1.wav
Normal file
BIN
assets/windy1.wav
Normal file
Binary file not shown.
@@ -40,6 +40,11 @@ export default class ItemBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withDropCallback(callback) {
|
||||||
|
this.item.addDropCallback(callback);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
withTickCallback(callback) {
|
withTickCallback(callback) {
|
||||||
this.item.addTickCallback(callback);
|
this.item.addTickCallback(callback);
|
||||||
return this;
|
return this;
|
||||||
|
@@ -3,6 +3,6 @@ export default function EchoCommand(args, context) {
|
|||||||
context.print(`Usage: echo <on/off>`);
|
context.print(`Usage: echo <on/off>`);
|
||||||
} else {
|
} else {
|
||||||
context.setInputEcho(args[1] == "on" ? true : false);
|
context.setInputEcho(args[1] == "on" ? true : false);
|
||||||
context.print(`Command echo is now ${args[1]}`);
|
context.print(`Command echo is now ${args[1]}.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -12,7 +12,7 @@ export default function LookCommand(args, context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!item) {
|
if (!item) {
|
||||||
context.output.say(`I could not find a ${args[1]}`);
|
context.output.say(`I could not find a ${args[1]}.`);
|
||||||
} else {
|
} else {
|
||||||
context.output.say(item.name);
|
context.output.say(item.name);
|
||||||
context.output.say(item.description);
|
context.output.say(item.description);
|
||||||
|
@@ -9,14 +9,14 @@ export default function TakeCommand(args, context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!item) {
|
if (!item) {
|
||||||
context.print(`You can't find any ${args[1]}`);
|
context.print(`You can't find any ${args[1]}.`);
|
||||||
} else {
|
} else {
|
||||||
if (!item.takeable) {
|
if (!item.takeable) {
|
||||||
context.print(`You can't take ${item.name}`);
|
context.print(`You can't take ${item.name}.`);
|
||||||
} else {
|
} else {
|
||||||
room.removeItem(item.id);
|
room.removeItem(item.id);
|
||||||
context.player.addItem(item.id);
|
context.player.addItem(item.id);
|
||||||
context.print(`You take ${item.name}`);
|
context.print(`You take ${item.name}.`);
|
||||||
item.onTake();
|
item.onTake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,16 @@ export default async function UseCommand(args, context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!item) {
|
if (!item) {
|
||||||
context.output.say(`I could not find a ${args[1]}`);
|
const items = context.player.getInventory();
|
||||||
|
for (let i of items) {
|
||||||
|
if (i.name.includes(args[1])) {
|
||||||
|
item = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!item) {
|
||||||
|
context.output.say(`I could not find a ${args[1]}.`);
|
||||||
} else {
|
} else {
|
||||||
await item.onUse();
|
await item.onUse();
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,6 @@ export default class Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init(data) {
|
init(data) {
|
||||||
console.log(data);
|
|
||||||
this.rooms = data.rooms.map((room) => {
|
this.rooms = data.rooms.map((room) => {
|
||||||
room.context = this;
|
room.context = this;
|
||||||
return room;
|
return room;
|
||||||
@@ -33,7 +32,7 @@ export default class Game {
|
|||||||
item.context = this;
|
item.context = this;
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
this.state = data.state;
|
this.state = data.state || State;
|
||||||
this.commandHandler.addCommands(data.commands);
|
this.commandHandler.addCommands(data.commands);
|
||||||
this.player = new Player();
|
this.player = new Player();
|
||||||
this.player.context = this;
|
this.player.context = this;
|
||||||
@@ -49,7 +48,7 @@ export default class Game {
|
|||||||
start() {
|
start() {
|
||||||
this.interval = setInterval(() => this.advanceTick(), 1000);
|
this.interval = setInterval(() => this.advanceTick(), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
clearInterval(this.interval);
|
clearInterval(this.interval);
|
||||||
this.interval = null;
|
this.interval = null;
|
||||||
@@ -70,16 +69,38 @@ export default class Game {
|
|||||||
examineItems() {
|
examineItems() {
|
||||||
const room = this.getRoom(this.player.currentRoom);
|
const room = this.getRoom(this.player.currentRoom);
|
||||||
const items = room.getItems();
|
const items = room.getItems();
|
||||||
items.forEach((item) => this.output.say(item.name));
|
if (items.length < 1) return;
|
||||||
|
let itemDescription = `You see `;
|
||||||
|
items.forEach((item, index) => {
|
||||||
|
if (index < items.length - 2) {
|
||||||
|
itemDescription += `${item.name}, `;
|
||||||
|
} else if (index < items.length - 1) {
|
||||||
|
itemDescription += `${item.name} and `;
|
||||||
|
} else {
|
||||||
|
itemDescription += item.name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.output.say(itemDescription + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
examineExits() {
|
examineExits() {
|
||||||
const room = this.getRoom(this.player.currentRoom);
|
const room = this.getRoom(this.player.currentRoom);
|
||||||
|
let exits = [];
|
||||||
let exitDescription = "You can go ";
|
let exitDescription = "You can go ";
|
||||||
for (let exit of room.exits.keys()) {
|
const exitKeys = room.exits.keys();
|
||||||
exitDescription += " " + exit;
|
for (let exit of exitKeys) {
|
||||||
|
exits.push(exit);
|
||||||
}
|
}
|
||||||
this.output.say(exitDescription);
|
exits.forEach((item, index) => {
|
||||||
|
if (index < exits.length - 2) {
|
||||||
|
exitDescription += `${item}, `;
|
||||||
|
} else if (index < exits.length - 1) {
|
||||||
|
exitDescription += `${item} and `;
|
||||||
|
} else {
|
||||||
|
exitDescription += item
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.output.say(exitDescription + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
getRoom(id) {
|
getRoom(id) {
|
||||||
|
@@ -17,9 +17,12 @@ export default class Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onTake() {
|
async onTake() {
|
||||||
if (this.takeCallback) return this.takeCallback();
|
if (this.takeCallback) return this.takeCallback(this.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onDrop() {
|
||||||
|
if (this.dropCallback) return this.dropCallback(this.context);
|
||||||
|
}
|
||||||
async onTick() {
|
async onTick() {
|
||||||
if (this.tickCallback) return this.tickCallback(this.context);
|
if (this.tickCallback) return this.tickCallback(this.context);
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,16 @@
|
|||||||
import { TTS } from '../framework/tts';
|
import { TTS } from '../framework/tts';
|
||||||
import { AriaOutput } from '../framework/tts/outputs/aria';
|
import { AriaOutput } from '../framework/tts/outputs/aria';
|
||||||
|
import Sound from './sound';
|
||||||
|
|
||||||
export default class Output {
|
export default class Output {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.tts = new TTS(new AriaOutput());
|
this.tts = new TTS(new AriaOutput());
|
||||||
this.history = document.getElementById("output-area");
|
this.history = document.getElementById("output-area");
|
||||||
|
this.sound = new Sound();
|
||||||
}
|
}
|
||||||
|
|
||||||
say(string) {
|
say(string) {
|
||||||
|
this.sound.play(`assets/scroll.wav`);
|
||||||
const node = document.createElement("p");
|
const node = document.createElement("p");
|
||||||
string.split("\n").forEach((line) => {
|
string.split("\n").forEach((line) => {
|
||||||
node.appendChild(document.createTextNode(line));
|
node.appendChild(document.createTextNode(line));
|
||||||
@@ -16,4 +19,8 @@ export default class Output {
|
|||||||
this.history.appendChild(node);
|
this.history.appendChild(node);
|
||||||
// this.tts.speak(string);
|
// this.tts.speak(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
play(file) {
|
||||||
|
this.sound.play(file);
|
||||||
|
}
|
||||||
}
|
}
|
18
src/engine/sound.js
Normal file
18
src/engine/sound.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import Resonator from '../framework/resonator';
|
||||||
|
|
||||||
|
|
||||||
|
export default class Sound {
|
||||||
|
constructor() {
|
||||||
|
this.res = new Resonator();
|
||||||
|
this.res.setEnvironmentImpulse(`assets/Greek7EchoHall.wav`);
|
||||||
|
this.ambience = null;
|
||||||
|
this.music = null;
|
||||||
|
this.previousAmbience = null;
|
||||||
|
this.previousMusic = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
play(file) {
|
||||||
|
const sound = this.res.loadImmediate(file);
|
||||||
|
sound.play();
|
||||||
|
}
|
||||||
|
}
|
@@ -4,6 +4,9 @@ class State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get(key) {
|
get(key) {
|
||||||
|
if (!this.states.has(key)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return this.states.get(key);
|
return this.states.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,4 +32,6 @@ export default class AudioSource implements BaseSource {
|
|||||||
stop(): void;
|
stop(): void;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
loop(value: boolean): void;
|
loop(value: boolean): void;
|
||||||
|
fadeOut(time: number): void;
|
||||||
|
fadeIn(time: number): void;
|
||||||
}
|
}
|
||||||
|
@@ -129,4 +129,18 @@ export default class AudioSource {
|
|||||||
this.node.loop = value;
|
this.node.loop = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fadeOut(time) {
|
||||||
|
if (!this.node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.gain.gain.exponentialRampToValueAtTime(0.0001, this.context.getContext().currentTime + time);
|
||||||
|
setTimeout(() => this.stop(), time * 1000);
|
||||||
|
}
|
||||||
|
fadeIn(time) {
|
||||||
|
if (!this.node) {
|
||||||
|
this.play();
|
||||||
|
}
|
||||||
|
this.gain.gain.setValueAtTime(0, this.context.getContext().currentTime);
|
||||||
|
this.gain.gain.exponentialRampToValueAtTime(this.volume, this.context.getContext().currentTime + time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,5 +6,7 @@ export interface BaseSource {
|
|||||||
setVolume(value: number): void;
|
setVolume(value: number): void;
|
||||||
getVolume(): number;
|
getVolume(): number;
|
||||||
loop(value: boolean): void;
|
loop(value: boolean): void;
|
||||||
|
fadeOut(time: number): void;
|
||||||
|
fadeIn(time: number): void;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@ export declare class StreamingSource implements BaseSource {
|
|||||||
private node;
|
private node;
|
||||||
private canPlay;
|
private canPlay;
|
||||||
private sceneNode;
|
private sceneNode;
|
||||||
|
private gain;
|
||||||
private position;
|
private position;
|
||||||
constructor(graph: AudioGraph, scene: ResonatorScene, context: ResonatorAudioContext, element: HTMLAudioElement, type?: SourceType);
|
constructor(graph: AudioGraph, scene: ResonatorScene, context: ResonatorAudioContext, element: HTMLAudioElement, type?: SourceType);
|
||||||
private init;
|
private init;
|
||||||
@@ -27,4 +28,6 @@ export declare class StreamingSource implements BaseSource {
|
|||||||
setPosition(x: number, y: number, z: number): void;
|
setPosition(x: number, y: number, z: number): void;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
loop(value: boolean): void;
|
loop(value: boolean): void;
|
||||||
|
fadeIn(time: number): void;
|
||||||
|
fadeOut(time: number): void;
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@ export class StreamingSource {
|
|||||||
}
|
}
|
||||||
init() {
|
init() {
|
||||||
this.node = this.context.createMediaElementSource(this.element);
|
this.node = this.context.createMediaElementSource(this.element);
|
||||||
|
this.gain = this.context.createGain();
|
||||||
this.createConnections();
|
this.createConnections();
|
||||||
this.element.addEventListener('canplay', (event) => {
|
this.element.addEventListener('canplay', (event) => {
|
||||||
this.canPlay = true;
|
this.canPlay = true;
|
||||||
@@ -50,7 +51,8 @@ export class StreamingSource {
|
|||||||
if (!this.sceneNode) {
|
if (!this.sceneNode) {
|
||||||
this.sceneNode = this.scene.createSource();
|
this.sceneNode = this.scene.createSource();
|
||||||
}
|
}
|
||||||
this.node.connect(this.sceneNode);
|
this.node.connect(this.gain);
|
||||||
|
this.gain.connect(this.sceneNode);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.graph.connectToMaster(this.node);
|
this.graph.connectToMaster(this.node);
|
||||||
@@ -78,4 +80,18 @@ export class StreamingSource {
|
|||||||
loop(value) {
|
loop(value) {
|
||||||
this.element.loop = true;
|
this.element.loop = true;
|
||||||
}
|
}
|
||||||
|
fadeIn(time) {
|
||||||
|
if (!this.node) {
|
||||||
|
this.play();
|
||||||
|
}
|
||||||
|
this.gain.gain.setValueAtTime(0, this.context.getContext().currentTime);
|
||||||
|
this.gain.gain.exponentialRampToValueAtTime(this.getVolume(), this.context.getContext().currentTime + time);
|
||||||
|
}
|
||||||
|
fadeOut(time) {
|
||||||
|
if (!this.node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.gain.gain.exponentialRampToValueAtTime(0.0001, this.context.getContext().currentTime + time);
|
||||||
|
setTimeout(() => this.stop(), time * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
10
src/game/items/cup.js
Normal file
10
src/game/items/cup.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import ItemBuilder from "../../engine/builders/item";
|
||||||
|
import Item from "../../engine/item";
|
||||||
|
|
||||||
|
export default new ItemBuilder()
|
||||||
|
.withID("cup")
|
||||||
|
.withName("a cup")
|
||||||
|
.withDescription("A standard coffee cup")
|
||||||
|
.isTakeable(true)
|
||||||
|
.isUsable(false)
|
||||||
|
.create();
|
@@ -1,5 +1,9 @@
|
|||||||
import Stone from './stone';
|
import Stone from './stone';
|
||||||
|
import Torch from './torch';
|
||||||
|
import Cup from './cup';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
Stone
|
Stone,
|
||||||
|
Torch,
|
||||||
|
Cup
|
||||||
]
|
]
|
@@ -2,13 +2,16 @@ import ItemBuilder from "../../engine/builders/item";
|
|||||||
|
|
||||||
export default new ItemBuilder()
|
export default new ItemBuilder()
|
||||||
.withID("stone")
|
.withID("stone")
|
||||||
.withName("A dull stone")
|
.withName("a dull stone")
|
||||||
.withDescription("There is nothing remarkable about this rough, bland stone.")
|
.withDescription("There is nothing remarkable about this rough, bland stone.")
|
||||||
.isTakeable(true)
|
.isTakeable(true)
|
||||||
.isUsable(true)
|
.isUsable(true)
|
||||||
.withTakeCallback(async function(context) {
|
.withTakeCallback(async function(context) {
|
||||||
context.print(`The ${this.id} feels heavy in your hands.`);
|
context.print(`The ${this.id} feels heavy in your hands.`);
|
||||||
})
|
})
|
||||||
|
.withDropCallback(async function(context) {
|
||||||
|
context.print(`It bounces back and forth a little.`)
|
||||||
|
})
|
||||||
.withUseCallback(async function(context) {
|
.withUseCallback(async function(context) {
|
||||||
context.print(`You can't really figure out what to do with ${this.name} yet`);
|
context.print(`You can't really figure out what to do with ${this.name} yet`);
|
||||||
})
|
})
|
||||||
|
12
src/game/items/torch.js
Normal file
12
src/game/items/torch.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import ItemBuilder from "../../engine/builders/item";
|
||||||
|
|
||||||
|
export default new ItemBuilder()
|
||||||
|
.withID("torch")
|
||||||
|
.withName("a torch")
|
||||||
|
.withDescription("A standard torch that provides light.")
|
||||||
|
.isUsable(true)
|
||||||
|
.isTakeable(true)
|
||||||
|
.withUseCallback(async function(context) {
|
||||||
|
context.print(`You try to light the torch but fail.`)
|
||||||
|
})
|
||||||
|
.create();
|
@@ -1,7 +1,9 @@
|
|||||||
import Start from './start';
|
import Start from './start';
|
||||||
import Tunnel1 from './tunnel1';
|
import Tunnel1 from './tunnel1';
|
||||||
|
import tunnel2 from './tunnel2';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
Start,
|
Start,
|
||||||
Tunnel1
|
Tunnel1,
|
||||||
|
tunnel2
|
||||||
];
|
];
|
@@ -3,15 +3,17 @@ import RoomBuilder from '../../engine/builders/room';
|
|||||||
export default new RoomBuilder()
|
export default new RoomBuilder()
|
||||||
.withID("start")
|
.withID("start")
|
||||||
.withTitle("The starting room")
|
.withTitle("The starting room")
|
||||||
.withFirstDescription(`
|
.withFirstDescription(
|
||||||
You set foot in your very first ever room.
|
`You set foot in your very first ever room.
|
||||||
You're not quite sure what you were supposed to expect, but it definitely wasn't this.
|
You're not quite sure what you were supposed to expect, but it definitely wasn't this.
|
||||||
I mean who would expect a boring old room like this one? Ugh.
|
I mean who would expect a boring old room like this one? Ugh.
|
||||||
Just... make it stop. Please.
|
Just... make it stop. Please.`
|
||||||
`)
|
)
|
||||||
.withDescription("The first room. Nothing special about it.")
|
.withDescription("The first room. Nothing special about it.")
|
||||||
.withExit("north", "tunnel_1")
|
.withExit("north", "tunnel_1")
|
||||||
|
.withExit("northwest", "tunnel_2")
|
||||||
.withEnterCallback(async function(context) {
|
.withEnterCallback(async function(context) {
|
||||||
|
if (context.state.get("start.awoken")) return;
|
||||||
const { output, wait } = context;
|
const { output, wait } = context;
|
||||||
context.enableCommandInput(false);
|
context.enableCommandInput(false);
|
||||||
output.say("You slowly wake up");
|
output.say("You slowly wake up");
|
||||||
@@ -20,6 +22,9 @@ Just... make it stop. Please.
|
|||||||
await wait(5000);
|
await wait(5000);
|
||||||
output.say("Yet here we are.");
|
output.say("Yet here we are.");
|
||||||
context.enableCommandInput(true);
|
context.enableCommandInput(true);
|
||||||
|
context.state.set("start.awoken", true);
|
||||||
})
|
})
|
||||||
.withItem("stone")
|
.withItem("stone")
|
||||||
|
.withItem("cup")
|
||||||
|
.withItem("torch")
|
||||||
.create();
|
.create();
|
13
src/game/rooms/tunnel2.js
Normal file
13
src/game/rooms/tunnel2.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import RoomBuilder from "../../engine/builders/room";
|
||||||
|
|
||||||
|
export default new RoomBuilder()
|
||||||
|
.withID("tunnel_2")
|
||||||
|
.withTitle("A long, winding tunnel")
|
||||||
|
.withFirstDescription(
|
||||||
|
`You step out from your hidy hole into a thin, winding tunnel. The walls and ceiling appear to get thinner and thinner, space slowly dwindling away to nothing.`
|
||||||
|
)
|
||||||
|
.withDescription(
|
||||||
|
`A tunnel that ends in a bowl shape.`
|
||||||
|
)
|
||||||
|
.withExit("southeast", "start")
|
||||||
|
.create();
|
@@ -8,16 +8,21 @@ module.exports = {
|
|||||||
entry: './src/game/index.js',
|
entry: './src/game/index.js',
|
||||||
devtool: 'inline-source-map',
|
devtool: 'inline-source-map',
|
||||||
performance: {
|
performance: {
|
||||||
hints: false
|
hints: false
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
minimizer: [new TerserWebpackPlugin()]
|
minimizer: [new TerserWebpackPlugin()]
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
new HTMLWebpackPlugin({
|
new HTMLWebpackPlugin({
|
||||||
title: Package.name,
|
title: Package.name,
|
||||||
template: path.join(__dirname, "src/game/index.html")
|
template: path.join(__dirname, "src/game/index.html")
|
||||||
}),
|
}),
|
||||||
|
new CopyWebpackPlugin({
|
||||||
|
patterns: [
|
||||||
|
{ from: 'assets', to: 'assets' },
|
||||||
|
]
|
||||||
|
})
|
||||||
],
|
],
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
@@ -28,7 +33,7 @@ module.exports = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [ '.ts', '.js' ]
|
extensions: ['.ts', '.js']
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
filename: 'game.js',
|
filename: 'game.js',
|
||||||
|
Reference in New Issue
Block a user