diff --git a/public/assets/icons/chevron-down.svg b/public/assets/icons/chevron-down.svg
new file mode 100644
index 0000000..5e7cbee
--- /dev/null
+++ b/public/assets/icons/chevron-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/assets/maps/intro/layer5.png b/public/assets/maps/intro/layer5.png
index b938045..bcf9dbc 100644
Binary files a/public/assets/maps/intro/layer5.png and b/public/assets/maps/intro/layer5.png differ
diff --git a/public/assets/maps/intro/train.png b/public/assets/maps/intro/train.png
index dbe0d4a..6c61cd1 100644
Binary files a/public/assets/maps/intro/train.png and b/public/assets/maps/intro/train.png differ
diff --git a/src/classes/UI/Message.ts b/src/classes/UI/Message.ts
index 724791e..0885046 100644
--- a/src/classes/UI/Message.ts
+++ b/src/classes/UI/Message.ts
@@ -19,6 +19,7 @@ const nameOffset = 40;
const maxLines = 5;
const timeout = 350;
+const fadeDuration = 125;
export class Message extends GameObjects.Container {
textWidth: number;
@@ -28,7 +29,6 @@ export class Message extends GameObjects.Container {
target?: any;
npcName: GameObjects.Text;
text: GameObjects.Text;
- box: GameObjects.Rectangle;
portrait: GameObjects.Image;
options?: string[];
@@ -51,6 +51,7 @@ export class Message extends GameObjects.Container {
this.setScrollFactor(0);
this.setPosition(padding, height - padding - boxHeight);
this.setDepth(Layer.Overlay);
+ this.setAlpha(0);
this.setVisible(false);
// Player is not necessary to show basic dialog, but might crash on complex dialogs
@@ -73,42 +74,57 @@ export class Message extends GameObjects.Container {
color: '#' + Colors.Tan,
});
- this.text = new GameObjects.Text(this.scene, padding + portraitOffset, padding + nameOffset, '', fontStyle);
+ this.text = this.scene.add.text(padding + portraitOffset, padding + nameOffset, '', fontStyle);
this.text.width = this.textWidth;
this.text.height = this.textHeight;
this.text.setOrigin(0).setMaxLines(maxLines);
- this.portrait = new GameObjects.Image(this.scene, padding, padding, '').setOrigin(0).setScale(1.5);
+ this.portrait = this.scene.add.image(padding, padding, '').setOrigin(0).setScale(1.5);
- this.box = new GameObjects.Rectangle(
- this.scene,
- 0,
- 0,
- Config.width - padding * 2,
- boxHeight,
- getColorNumber(Colors.Black),
- 0.8
- );
- this.box.setStrokeStyle(2, getColorNumber(Colors.Tan), 1);
- this.box.setOrigin(0, 0);
+ const box = this.scene.add
+ .rectangle(0, 0, Config.width - padding * 2, boxHeight, getColorNumber(Colors.Black), 0.8)
+ .setStrokeStyle(2, getColorNumber(Colors.Tan), 1)
+ .setOrigin(0, 0)
+ .setScrollFactor(0)
+ .setInteractive({ useHandCursor: true })
+ .on('pointerdown', () => {
+ if (!this.options) this.updateDialog();
+ });
this.optionsContainer = new ButtonGroup(this.scene).setDepth(Layer.Overlay);
- this.add([this.box, this.npcName, this.text, this.portrait]);
+ const arrow = this.scene.add.image(Config.width - padding * 2 - 20, boxHeight - 16, 'chevron-down').setScale(0.5);
+ this.scene.tweens.add({
+ targets: arrow,
+ y: boxHeight - 22,
+ scale: 0.4,
+ duration: 1000,
+ ease: 'Sine.easeInOut',
+ yoyo: true,
+ repeat: -1,
+ });
+
+ this.add([box, this.npcName, this.text, this.portrait, arrow]);
}
setDialog(dialog?: Dialog, target?: T, portrait?: string) {
if (!this.npcName) this.createUI();
- this.setVisible(dialog !== undefined);
+ this.setVisible(true);
+ this.scene.tweens.add({
+ targets: this,
+ alpha: dialog !== undefined ? 1 : 0,
+ duration: fadeDuration,
+ onComplete: () => this.setVisible(dialog !== undefined),
+ });
this.target = target;
this.messageIndex = 0;
this.dialog = dialog;
this.interactionTimeout = Date.now() + timeout;
- (this.scene as Game).gamepad?.offsetButtons(this.dialog !== undefined);
+ (this.scene as Game).gamepad?.setVisible(this.dialog === undefined);
if (!dialog) {
return;
@@ -147,7 +163,20 @@ export class Message extends GameObjects.Container {
const message = messages && messages[this.messageIndex];
if (message) {
- this.text.setText(message);
+ this.scene.tweens.add({
+ targets: this.text,
+ alpha: 0,
+ duration: fadeDuration,
+ onComplete: () => {
+ this.text.setText(message);
+ this.scene.tweens.add({
+ targets: this.text,
+ alpha: 1,
+ duration: fadeDuration,
+ });
+ },
+ });
+
if (this.text.getWrappedText().length > maxLines) console.error('Message too long!', message);
}
@@ -205,14 +234,20 @@ export class Message extends GameObjects.Container {
this.dialog.onCompleted(this.player, this.target);
}
this.dialog = undefined;
- this.setVisible(false);
+
+ this.scene.tweens.add({
+ targets: this,
+ alpha: 0,
+ duration: fadeDuration,
+ onComplete: () => this.setVisible(false),
+ });
(this.scene as Game).gamepad?.resetButtons();
} else {
this.showMessage();
}
- (this.scene as Game).gamepad?.offsetButtons(this.dialog !== undefined);
+ (this.scene as Game).gamepad?.setVisible(this.dialog === undefined);
this.interactionTimeout = Date.now() + timeout;
}
diff --git a/src/scenes/Intro.ts b/src/scenes/Intro.ts
index 48c6faf..4be7c85 100644
--- a/src/scenes/Intro.ts
+++ b/src/scenes/Intro.ts
@@ -4,6 +4,17 @@ import { Config } from '../config';
import { trainIntro } from '../data/cutscene';
import { fadeIn } from '../utils/util';
+// Export the preload function (so it can be used in the main Preloader)
+export function preloadIntro(scene: Scene) {
+ scene.load.image('train', 'maps/intro/train.png');
+
+ scene.load.image('layer1', 'maps/intro/layer1.png');
+ scene.load.image('layer2', 'maps/intro/layer2.png');
+ scene.load.image('layer3', 'maps/intro/layer3.png');
+ scene.load.image('layer4', 'maps/intro/layer4.png');
+ scene.load.image('layer5', 'maps/intro/layer5.png');
+}
+
export class Intro extends Scene {
player: GameObjects.Sprite;
@@ -16,18 +27,7 @@ export class Intro extends Scene {
}
preload() {
- this.load.setPath('assets');
-
- this.load.image('train', 'maps/intro/train.png');
-
- this.load.image('layer1', 'maps/intro/layer1.png');
- this.load.image('layer2', 'maps/intro/layer2.png');
- this.load.image('layer3', 'maps/intro/layer3.png');
- this.load.image('layer4', 'maps/intro/layer4.png');
- this.load.image('layer5', 'maps/intro/layer5.png');
-
- this.load.spritesheet('character', 'characters/player.png', { frameWidth: 128, frameHeight: 80 });
- this.load.image('player_portrait', 'characters/player_portrait.png');
+ preloadIntro(this);
}
create() {
@@ -114,7 +114,4 @@ export class Intro extends Scene {
trainIntro(this, this.player);
}
-
- // update(time: number, delta: number): void {
- // }
}
diff --git a/src/scenes/Preloader.ts b/src/scenes/Preloader.ts
index ddab0e8..b8d5b61 100644
--- a/src/scenes/Preloader.ts
+++ b/src/scenes/Preloader.ts
@@ -3,6 +3,7 @@ import { GameObjects, Scene } from 'phaser';
import { Config } from '../config';
import { saveKey } from '../data/saves';
import { fadeOut } from '../utils/util';
+import { preloadIntro } from './Intro';
export class Preloader extends Scene {
container: GameObjects.Container;
@@ -72,6 +73,7 @@ export class Preloader extends Scene {
this.load.svg('award', 'icons/award.svg', { width: 64, height: 64 });
this.load.svg('tv', 'icons/tv.svg', { width: 64, height: 64 });
this.load.svg('save', 'icons/save.svg', { width: 64, height: 64 });
+ this.load.svg('chevron-down', 'icons/chevron-down.svg', { width: 64, height: 64 });
// fontawesome icons
this.load.svg('gamepad', 'icons/gamepad-solid.svg', { width: 64, height: 64 });
@@ -141,6 +143,11 @@ export class Preloader extends Scene {
// puzzles
this.load.image('arrow', 'puzzles/arrow.png');
+
+ // optionally preload intro
+ if (!localStorage.getItem(saveKey)) {
+ preloadIntro(this);
+ }
}
create() {
diff --git a/src/scenes/dialogs/Paused.ts b/src/scenes/dialogs/Paused.ts
index 3d02f86..ae93074 100644
--- a/src/scenes/dialogs/Paused.ts
+++ b/src/scenes/dialogs/Paused.ts
@@ -55,7 +55,7 @@ export class Paused extends Scene {
}
)
.setOrigin(1, 1)
- .setInteractive({ useHandCursor: false })
+ .setInteractive({ useHandCursor: !import.meta.env.PROD })
.on('pointerdown', () => {
this.debugCount++;
if (this.debugCount > 10) {