Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## [0.8.7]

### Fixed

- Refactored Sound System: fixed offset handling and playback management.

## [0.8.6]

### Fixed

- Incorrect behavior of `Menu.enableWhen` and `Menu.showWhen`

## [0.8.5]

### Fixed

- Incorrect signature of `Menu.enableWhen` and `Menu.showWhen`

## [0.8.4]

### _Feature_
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "narraleaf-react",
"version": "0.8.4",
"version": "0.8.7",
"description": "A React visual novel player framework",
"main": "./dist/main.js",
"types": "./dist/index.d.ts",
Expand Down Expand Up @@ -96,6 +96,7 @@
"author": "helloyork@icloud.com",
"license": "MPL-2.0",
"dependencies": {
"@narraleaf/sound": "0.1.0",
"client-only": "^0.0.1",
"clsx": "^2.1.1",
"howler": "^2.2.4",
Expand Down
2 changes: 1 addition & 1 deletion src/game/nlcore/action/actionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export type SoundActionContentType = {
[K in typeof SoundActionTypes[keyof typeof SoundActionTypes]]:
K extends "sound:play" ? [FadeOptions] :
K extends "sound:stop" ? [FadeOptions] :
K extends "sound:setVolume" ? [volumn: number, duration: number] :
K extends "sound:setVolume" ? [volume: number, duration: number] :
K extends "sound:setRate" ? [number] :
K extends "sound:pause" ? [FadeOptions] :
K extends "sound:resume" ? [FadeOptions] :
Expand Down
19 changes: 14 additions & 5 deletions src/game/nlcore/action/actions/sceneAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export class SceneAction<T extends typeof SceneActionTypes[keyof typeof SceneAct
.flush();
scene.local.init(state.getStorable());

state.getExposedStateAsync<ExposedStateType.scene>(scene, (exposed) => {
SceneAction.initBackgroundMusic(scene, exposed);
state.getExposedStateAsync<ExposedStateType.scene>(scene, async (exposed) => {
await SceneAction.initBackgroundMusic(scene, exposed);
awaitable.resolve(next);

state.logger.debug("Scene Action", "Scene init");
Expand All @@ -49,10 +49,19 @@ export class SceneAction<T extends typeof SceneActionTypes[keyof typeof SceneAct
return awaitable;
}

static initBackgroundMusic(scene: Scene, exposed: ExposedState[ExposedStateType.scene]) {
if (scene.state.backgroundMusic) {
exposed.setBackgroundMusic(scene.state.backgroundMusic, scene.config.backgroundMusicFade);
/**
* Initialize background music for the target scene.
* Waits until the previous BGM has completely faded out (if any) before
* resolving, ensuring seamless audio transition when jumping between scenes.
*/
static async initBackgroundMusic(scene: Scene, exposed: ExposedState[ExposedStateType.scene]): Promise<void> {
if (!scene.state.backgroundMusic) {
return;
}
// `setBackgroundMusic` already handles fade-out of the previous track and
// fade-in of the new track. We simply await it so that the caller can
// chain subsequent actions after the transition finishes.
await exposed.setBackgroundMusic(scene.state.backgroundMusic, scene.config.backgroundMusicFade);
}

static createSceneSnapshot(scene: Scene, state: GameState): SceneSnapshot {
Expand Down
2 changes: 2 additions & 0 deletions src/game/nlcore/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { GameConfig, SavedGame, NotificationToken, SavedGameMetaData } from "../
import type { LayoutRouter } from "@lib/game/player/lib/PageRouter/router";
import { KeyBindingType, WebKeyboardKey } from "../game/types";
import { KeyBindingValue } from "../game/keyMap";
import { SoundType } from "@core/elements/sound";

export * from "@core/elements/type";
export type {
Expand All @@ -25,4 +26,5 @@ export type {

export {
KeyBindingType,
SoundType,
};
25 changes: 25 additions & 0 deletions src/game/nlcore/elements/built-in/DevTools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ControlAction } from "../../action/actions/controlAction";
import { Chained, Proxied } from "../../action/chain";
import { LogicAction } from "../../game";
import { Control } from "../control";


export class DevTools {
public static getActionId(action: LogicAction.Actions): string {
return action.getId();
}

public static setActionId(action: LogicAction.Actions, id: string): LogicAction.Actions {
action.setId(id);
return action;
}

public static chainToActions(chain: Proxied<LogicAction.GameElement, Chained<LogicAction.Actions>>): LogicAction.Actions[] {
return chain.getActions();
}

public static wrapAction(action: LogicAction.Actions[] | Proxied<LogicAction.GameElement, Chained<LogicAction.Actions>>): ControlAction {
const actions = Chained.isChained(action) ? action.getActions() : action;
return Control.do(actions);
}
}
3 changes: 2 additions & 1 deletion src/game/nlcore/elements/built-in/built-in.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { Gallery } from "./Gallery";
export { Gallery } from "./Gallery";
export { DevTools } from "./DevTools";
7 changes: 7 additions & 0 deletions src/game/nlcore/elements/condition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ export class Lambda<T = any> {
return Lambda.isLambda(obj) ? obj : new Lambda(obj);
}

/**@internal */
public static not(lambda: Lambda<boolean>): Lambda<boolean> {
return new Lambda((ctx) => {
return !lambda.evaluate(ctx).value;
});
}

/**@internal */
handler: LambdaHandler<T>;

Expand Down
8 changes: 4 additions & 4 deletions src/game/nlcore/elements/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,12 @@ export class Menu extends Actionable<any, Menu> {
* ]);
* ```
*/
public enableWhen(condition: Lambda<boolean> | LambdaHandler<boolean>, prompt: Sentence, action: ActionStatements): Proxied<Menu, Chained<LogicAction.Actions>> {
public enableWhen(condition: Lambda<boolean> | LambdaHandler<boolean>, prompt: Sentence | SentencePrompt, action: ActionStatements): Proxied<Menu, Chained<LogicAction.Actions>> {
return this.choose({
prompt,
action,
config: {
disabled: Lambda.from(condition)
disabled: Lambda.not(Lambda.from(condition))
}
});
}
Expand All @@ -182,12 +182,12 @@ export class Menu extends Actionable<any, Menu> {
* ]);
* ```
*/
public showWhen(condition: Lambda<boolean> | LambdaHandler<boolean>, prompt: Sentence, action: ActionStatements): Proxied<Menu, Chained<LogicAction.Actions>> {
public showWhen(condition: Lambda<boolean> | LambdaHandler<boolean>, prompt: Sentence | SentencePrompt, action: ActionStatements): Proxied<Menu, Chained<LogicAction.Actions>> {
return this.choose({
prompt,
action,
config: {
hidden: Lambda.from(condition)
hidden: Lambda.not(Lambda.from(condition))
}
});
}
Expand Down
4 changes: 4 additions & 0 deletions src/game/player/elements/Player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ export default function Player(
state.stage.update();
}

useEffect(() => {
state.audioManager.initialize();
}, []);

useEffect(() => {
game.getLiveGame().setGameState(state);
if (story && !game.getLiveGame().isPlaying()) {
Expand Down
8 changes: 1 addition & 7 deletions src/game/player/elements/scene/Scene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ export default function Scene(
return scene.events.depends([
scene.events.on(GameScene.EventTypes["event:scene.preUnmount"], () => {
if (scene.state.backgroundMusic) {
return state.audioManager.stop(scene.state.backgroundMusic).then(() => {
scene.state.backgroundMusic = null;
});
return state.audioManager.stop(scene.state.backgroundMusic, scene.config.backgroundMusicFade);
}
}),
]).cancel;
Expand All @@ -50,10 +48,6 @@ export default function Scene(
useExposeState<ExposedStateType.scene>(scene, {
setBackgroundMusic(music: Sound | null, fade: number) {
return new Promise<void>((resolve) => {
if (!scene.state.backgroundMusic) {
return;
}

(async function () {
if (scene.state.backgroundMusic && state.audioManager.isManaged(scene.state.backgroundMusic)) {
await state.audioManager.stop(scene.state.backgroundMusic, fade);
Expand Down
Loading
Loading