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;
|
||||
}
|
||||
|
||||
withDropCallback(callback) {
|
||||
this.item.addDropCallback(callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
withTickCallback(callback) {
|
||||
this.item.addTickCallback(callback);
|
||||
return this;
|
||||
|
@@ -3,6 +3,6 @@ export default function EchoCommand(args, context) {
|
||||
context.print(`Usage: echo <on/off>`);
|
||||
} else {
|
||||
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) {
|
||||
context.output.say(`I could not find a ${args[1]}`);
|
||||
context.output.say(`I could not find a ${args[1]}.`);
|
||||
} else {
|
||||
context.output.say(item.name);
|
||||
context.output.say(item.description);
|
||||
|
@@ -9,14 +9,14 @@ export default function TakeCommand(args, context) {
|
||||
}
|
||||
}
|
||||
if (!item) {
|
||||
context.print(`You can't find any ${args[1]}`);
|
||||
context.print(`You can't find any ${args[1]}.`);
|
||||
} else {
|
||||
if (!item.takeable) {
|
||||
context.print(`You can't take ${item.name}`);
|
||||
context.print(`You can't take ${item.name}.`);
|
||||
} else {
|
||||
room.removeItem(item.id);
|
||||
context.player.addItem(item.id);
|
||||
context.print(`You take ${item.name}`);
|
||||
context.print(`You take ${item.name}.`);
|
||||
item.onTake();
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,16 @@ export default async function UseCommand(args, context) {
|
||||
}
|
||||
}
|
||||
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 {
|
||||
await item.onUse();
|
||||
}
|
||||
|
@@ -24,7 +24,6 @@ export default class Game {
|
||||
}
|
||||
|
||||
init(data) {
|
||||
console.log(data);
|
||||
this.rooms = data.rooms.map((room) => {
|
||||
room.context = this;
|
||||
return room;
|
||||
@@ -33,7 +32,7 @@ export default class Game {
|
||||
item.context = this;
|
||||
return item;
|
||||
});
|
||||
this.state = data.state;
|
||||
this.state = data.state || State;
|
||||
this.commandHandler.addCommands(data.commands);
|
||||
this.player = new Player();
|
||||
this.player.context = this;
|
||||
@@ -49,7 +48,7 @@ export default class Game {
|
||||
start() {
|
||||
this.interval = setInterval(() => this.advanceTick(), 1000);
|
||||
}
|
||||
|
||||
|
||||
stop() {
|
||||
clearInterval(this.interval);
|
||||
this.interval = null;
|
||||
@@ -70,16 +69,38 @@ export default class Game {
|
||||
examineItems() {
|
||||
const room = this.getRoom(this.player.currentRoom);
|
||||
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() {
|
||||
const room = this.getRoom(this.player.currentRoom);
|
||||
let exits = [];
|
||||
let exitDescription = "You can go ";
|
||||
for (let exit of room.exits.keys()) {
|
||||
exitDescription += " " + exit;
|
||||
const exitKeys = room.exits.keys();
|
||||
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) {
|
||||
|
@@ -17,9 +17,12 @@ export default class Item {
|
||||
}
|
||||
|
||||
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() {
|
||||
if (this.tickCallback) return this.tickCallback(this.context);
|
||||
}
|
||||
|
@@ -1,13 +1,16 @@
|
||||
import { TTS } from '../framework/tts';
|
||||
import { AriaOutput } from '../framework/tts/outputs/aria';
|
||||
import Sound from './sound';
|
||||
|
||||
export default class Output {
|
||||
constructor() {
|
||||
this.tts = new TTS(new AriaOutput());
|
||||
this.history = document.getElementById("output-area");
|
||||
this.sound = new Sound();
|
||||
}
|
||||
|
||||
say(string) {
|
||||
this.sound.play(`assets/scroll.wav`);
|
||||
const node = document.createElement("p");
|
||||
string.split("\n").forEach((line) => {
|
||||
node.appendChild(document.createTextNode(line));
|
||||
@@ -16,4 +19,8 @@ export default class Output {
|
||||
this.history.appendChild(node);
|
||||
// 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) {
|
||||
if (!this.states.has(key)) {
|
||||
return null;
|
||||
}
|
||||
return this.states.get(key);
|
||||
}
|
||||
|
||||
|
@@ -32,4 +32,6 @@ export default class AudioSource implements BaseSource {
|
||||
stop(): void;
|
||||
destroy(): 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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
getVolume(): number;
|
||||
loop(value: boolean): void;
|
||||
fadeOut(time: number): void;
|
||||
fadeIn(time: number): void;
|
||||
destroy(): void;
|
||||
}
|
||||
|
@@ -14,6 +14,7 @@ export declare class StreamingSource implements BaseSource {
|
||||
private node;
|
||||
private canPlay;
|
||||
private sceneNode;
|
||||
private gain;
|
||||
private position;
|
||||
constructor(graph: AudioGraph, scene: ResonatorScene, context: ResonatorAudioContext, element: HTMLAudioElement, type?: SourceType);
|
||||
private init;
|
||||
@@ -27,4 +28,6 @@ export declare class StreamingSource implements BaseSource {
|
||||
setPosition(x: number, y: number, z: number): void;
|
||||
destroy(): void;
|
||||
loop(value: boolean): void;
|
||||
fadeIn(time: number): void;
|
||||
fadeOut(time: number): void;
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ export class StreamingSource {
|
||||
}
|
||||
init() {
|
||||
this.node = this.context.createMediaElementSource(this.element);
|
||||
this.gain = this.context.createGain();
|
||||
this.createConnections();
|
||||
this.element.addEventListener('canplay', (event) => {
|
||||
this.canPlay = true;
|
||||
@@ -50,7 +51,8 @@ export class StreamingSource {
|
||||
if (!this.sceneNode) {
|
||||
this.sceneNode = this.scene.createSource();
|
||||
}
|
||||
this.node.connect(this.sceneNode);
|
||||
this.node.connect(this.gain);
|
||||
this.gain.connect(this.sceneNode);
|
||||
break;
|
||||
default:
|
||||
this.graph.connectToMaster(this.node);
|
||||
@@ -78,4 +80,18 @@ export class StreamingSource {
|
||||
loop(value) {
|
||||
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 Torch from './torch';
|
||||
import Cup from './cup';
|
||||
|
||||
export default [
|
||||
Stone
|
||||
Stone,
|
||||
Torch,
|
||||
Cup
|
||||
]
|
@@ -2,13 +2,16 @@ import ItemBuilder from "../../engine/builders/item";
|
||||
|
||||
export default new ItemBuilder()
|
||||
.withID("stone")
|
||||
.withName("A dull stone")
|
||||
.withName("a dull stone")
|
||||
.withDescription("There is nothing remarkable about this rough, bland stone.")
|
||||
.isTakeable(true)
|
||||
.isUsable(true)
|
||||
.withTakeCallback(async function(context) {
|
||||
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) {
|
||||
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 Tunnel1 from './tunnel1';
|
||||
import tunnel2 from './tunnel2';
|
||||
|
||||
export default [
|
||||
Start,
|
||||
Tunnel1
|
||||
Tunnel1,
|
||||
tunnel2
|
||||
];
|
@@ -3,15 +3,17 @@ import RoomBuilder from '../../engine/builders/room';
|
||||
export default new RoomBuilder()
|
||||
.withID("start")
|
||||
.withTitle("The starting room")
|
||||
.withFirstDescription(`
|
||||
You set foot in your very first ever room.
|
||||
.withFirstDescription(
|
||||
`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.
|
||||
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.")
|
||||
.withExit("north", "tunnel_1")
|
||||
.withExit("northwest", "tunnel_2")
|
||||
.withEnterCallback(async function(context) {
|
||||
if (context.state.get("start.awoken")) return;
|
||||
const { output, wait } = context;
|
||||
context.enableCommandInput(false);
|
||||
output.say("You slowly wake up");
|
||||
@@ -20,6 +22,9 @@ Just... make it stop. Please.
|
||||
await wait(5000);
|
||||
output.say("Yet here we are.");
|
||||
context.enableCommandInput(true);
|
||||
context.state.set("start.awoken", true);
|
||||
})
|
||||
.withItem("stone")
|
||||
.withItem("cup")
|
||||
.withItem("torch")
|
||||
.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',
|
||||
devtool: 'inline-source-map',
|
||||
performance: {
|
||||
hints: false
|
||||
hints: false
|
||||
},
|
||||
optimization: {
|
||||
minimizer: [new TerserWebpackPlugin()]
|
||||
},
|
||||
"plugins": [
|
||||
new HTMLWebpackPlugin({
|
||||
title: Package.name,
|
||||
template: path.join(__dirname, "src/game/index.html")
|
||||
}),
|
||||
new HTMLWebpackPlugin({
|
||||
title: Package.name,
|
||||
template: path.join(__dirname, "src/game/index.html")
|
||||
}),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{ from: 'assets', to: 'assets' },
|
||||
]
|
||||
})
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
@@ -28,7 +33,7 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [ '.ts', '.js' ]
|
||||
extensions: ['.ts', '.js']
|
||||
},
|
||||
output: {
|
||||
filename: 'game.js',
|
||||
|
Reference in New Issue
Block a user