draw more UI

This commit is contained in:
Zack Buhman 2026-05-26 18:20:52 -05:00
parent ce5d9fccba
commit 06db7983ca
20 changed files with 1091 additions and 596 deletions

View File

@ -7,7 +7,7 @@ OBJARCH = elf64-x86-64
UNAME := $(shell uname -s) UNAME := $(shell uname -s)
OPT += -O0 -march=x86-64-v3 OPT += -O3 -march=x86-64-v3
DEBUG = -g DEBUG = -g
@ -24,10 +24,14 @@ CFLAGS += -I./include
CFLAGS += -I./data CFLAGS += -I./data
CFLAGS += -I../SDL3-dist/include CFLAGS += -I../SDL3-dist/include
CFLAGS += -fpic CFLAGS += -fpic
CFLAGS += -ffunction-sections
CFLAGS += -fdata-sections
#FLAGS += -fstack-protector -fstack-protector-all -fno-omit-frame-pointer -fsanitize=address #FLAGS += -fstack-protector -fstack-protector-all -fno-omit-frame-pointer -fsanitize=address
LDFLAGS += -lm LDFLAGS += -lm
LDFLAGS += -Wl,--gc-sections
#-Wl,--print-gc-sections
ifeq ($(UNAME),Linux) ifeq ($(UNAME),Linux)
LDFLAGS += -Wl,-z noexecstack LDFLAGS += -Wl,-z noexecstack
endif endif
@ -36,6 +40,16 @@ LDFLAGS += -framework Foundation -framework Cocoa -framework IOKit -framework AV
LDFLAGS += -lstdc++ LDFLAGS += -lstdc++
endif endif
# src/collada/scene/vulkan.o \
# src/collada/scene/reload.o \
# src/collada/scene.o \
# src/collada/node_state.o \
# src/collada/animate.o \
# src/minecraft/world.o \
# src/minecraft/entry_table.o \
# src/minecraft/vulkan.o \
# src/minecraft/vulkan/per_world.o \
OBJS = \ OBJS = \
src/main.o \ src/main.o \
src/view.o \ src/view.o \
@ -43,19 +57,12 @@ OBJS = \
src/file.o \ src/file.o \
src/pack.o \ src/pack.o \
src/dds/validate.o \ src/dds/validate.o \
src/tga/tga.o \
src/vulkan_helper.o \ src/vulkan_helper.o \
src/collada/scene/vulkan.o \ src/tga/tga.o \
src/collada/scene/reload.o \
src/collada/scene.o \
src/collada/node_state.o \
src/collada/animate.o \
src/minecraft/world.o \
src/minecraft/entry_table.o \
src/minecraft/vulkan.o \
src/minecraft/vulkan/per_world.o \
src/font/outline.o \ src/font/outline.o \
src/renpy/vulkan.o src/renpy/vulkan.o \
src/renpy/script.o \
src/renpy/interpreter.o
WORLDS = \ WORLDS = \
data/minecraft/midnightmeadow/inthash.o \ data/minecraft/midnightmeadow/inthash.o \

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -2,7 +2,7 @@
# Declare characters used by this game. The color argument colorizes the # Declare characters used by this game. The color argument colorizes the
# name of the character. # name of the character.
image bgwhite = "bg/white.png" image bgwhite = "#ffffff"
image bgforest1 = "bg/forest1.png" image bgforest1 = "bg/forest1.png"
image bgforest2 = "bg/forest2.png" image bgforest2 = "bg/forest2.png"
image bgflower1 = "bg/flowerfield1.png" image bgflower1 = "bg/flowerfield1.png"
@ -303,7 +303,7 @@ label start:
with Dissolve(3.0) with Dissolve(3.0)
n "After their encounter with the weird cat, the mice scurry out of the flower field and into the nearby meadow" n "After their encounter with the weird cat, the mice scurry out of the flower field and into the nearby meadow"
scene forest2 scene bgforest2
with Dissolve(3.0) with Dissolve(3.0)
show ei at right show ei at right
@ -539,4 +539,3 @@ label start:
return return

View File

@ -1,8 +1,8 @@
shader/font.spv shader/font.spv
data/font/outline/uncial_antiqua_36.data data/font/outline/medieval_sharp_24.data
shader/renpy.spv shader/renpy.spv
data/renpy/images/bg/white.dds data/renpy/images/flowers.dds
data/renpy/images/bg/forest1.dds data/renpy/images/bg/forest1.dds
data/renpy/images/bg/forest2.dds data/renpy/images/bg/forest2.dds
data/renpy/images/bg/flowerfield1.dds data/renpy/images/bg/flowerfield1.dds

1
font.sh Normal file
View File

@ -0,0 +1 @@
~/dreamcast/tools/ttf_outline 20 7f 24 0 little ~/mouse2/mousegirl4/game/fonts/MedievalSharp-Regular.ttf ~/vulkan/data/font/outline/medieval_sharp_24.data

View File

@ -2,6 +2,7 @@
#include "outline_types.h" #include "outline_types.h"
#include "vulkan_helper.h" #include "vulkan_helper.h"
#include "renpy/interpreter.h"
namespace font::outline { namespace font::outline {
@ -9,12 +10,15 @@ namespace font::outline {
char const * const path; char const * const path;
}; };
font_desc const uncial_antiqua[] = { font_desc const medieval_sharp[] = {
{ {
.path = "data/font/outline/uncial_antiqua_36.data", .path = "data/font/outline/medieval_sharp_24.data",
}, },
}; };
int const uncial_antiqua_length = (sizeof (uncial_antiqua)) / (sizeof (font_desc)); int const medieval_sharp_length = (sizeof (medieval_sharp)) / (sizeof (font_desc));
constexpr uint32_t textboxLeft = 410;
constexpr uint32_t textboxWidth = 450;
struct AllocatedImage { struct AllocatedImage {
VkImage image; VkImage image;
@ -100,8 +104,21 @@ namespace font::outline {
void create_instance_buffers(); void create_instance_buffers();
void create_pipeline(); void create_pipeline();
void create_glyphs_buffer(types::font const * const font, types::glyph const * const glyphs); void create_glyphs_buffer(types::font const * const font, types::glyph const * const glyphs);
void emit_line(int frameIndex,
char const * const string,
uint32_t x, uint32_t y,
int& outputIndex,
int startIndex,
int endIndex,
uint32_t color);
void centered(int frameIndex, char const * const string, uint32_t& x, uint32_t& y,
uint32_t minX,
uint32_t maxWidth,
int& outputIndex,
uint32_t color);
void draw(VkCommandBuffer commandBuffer, void draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex); uint32_t frameIndex,
renpy::interpreter const& state);
LoadedFont load_font(font_desc const& desc); LoadedFont load_font(font_desc const& desc);
}; };

View File

@ -1,4 +1,8 @@
#pragma once
#include <stdint.h>
#include "renpy/language.h"
namespace renpy { namespace renpy {
struct top_left { struct top_left {
@ -6,22 +10,42 @@ namespace renpy {
uint32_t left; uint32_t left;
}; };
static const top_left transforms[] = { static constexpr top_left transforms[] = {
[language::transform::left] = { .top = 192, .left = 0 }, [language::transform::left] = { .top = 192, .left = 0 },
[language::transform::centerleft] = { .top = 192, .left = 240 }, [language::transform::centerleft] = { .top = 192, .left = 240 },
[language::transform::center] = { .top = 192, .left = 416 }, [language::transform::center] = { .top = 192, .left = 416 },
[language::transform::centerright] = { .top = 192, .left = 588 }, [language::transform::centerright] = { .top = 192, .left = 588 },
[language::transform::right] = { .top = 192, .left = 828 }, [language::transform::right] = { .top = 192, .left = 828 },
}; };
static constexpr int transformsCount = (sizeof (transforms)) / (sizeof (transforms[0]));
struct shownImage { struct shownImage {
uint32_t imageIndex; uint32_t imageIndex;
uint32_t transformIndex; uint32_t transformIndex;
}; };
static constexpr int maximumShownImagesCount = 16;
struct interpreter { struct interpreter {
uint32_t pc;
uint32_t backgroundIndex; uint32_t backgroundIndex;
shownImage shownImages[16]; uint32_t backgroundColor;
shownImage shownImages[maximumShownImagesCount];
uint32_t shownImagesCount; uint32_t shownImagesCount;
struct {
uint32_t stringIndex;
uint32_t characterIndex;
} say;
struct {
uint32_t count;
uint32_t optionIndex;
} menu;
uint32_t findImage(uint32_t imageIndex);
void showImage(uint32_t imageIndex, uint32_t transformIndex);
void hideImage(uint32_t imageIndex);
void reset();
void interpret_one();
void interpret();
}; };
} }

View File

@ -1,3 +1,5 @@
#pragma once
#include <stdint.h> #include <stdint.h>
namespace renpy::language { namespace renpy::language {
@ -18,6 +20,7 @@ namespace renpy::language {
struct character { struct character {
char const * const characterName; char const * const characterName;
uint32_t color;
}; };
struct audio { struct audio {
@ -37,12 +40,14 @@ namespace renpy::language {
_return, _return,
say, say,
scene, scene,
scene_color,
show, show,
voice, voice,
with, with,
stop, stop,
pause, pause,
hide, hide,
dissolve,
}; };
struct jump { struct jump {
@ -71,6 +76,10 @@ namespace renpy::language {
uint32_t imageIndex; uint32_t imageIndex;
}; };
struct scene_color {
uint32_t color;
};
struct show { struct show {
uint32_t imageIndex; uint32_t imageIndex;
uint32_t transformIndex; uint32_t transformIndex;
@ -95,6 +104,10 @@ namespace renpy::language {
uint32_t imageIndex; uint32_t imageIndex;
}; };
struct dissolve {
float duration;
};
struct statement { struct statement {
enum type type; enum type type;
union { union {
@ -103,11 +116,13 @@ namespace renpy::language {
renpy::language::play play; renpy::language::play play;
renpy::language::say say; renpy::language::say say;
renpy::language::scene scene; renpy::language::scene scene;
renpy::language::scene_color scene_color;
renpy::language::show show; renpy::language::show show;
renpy::language::voice voice; renpy::language::voice voice;
renpy::language::stop stop; renpy::language::stop stop;
renpy::language::pause pause; renpy::language::pause pause;
renpy::language::hide hide; renpy::language::hide hide;
renpy::language::dissolve dissolve;
}; };
}; };
} }

View File

@ -1,477 +1,22 @@
#include "renpy/language.h" #include "renpy/language.h"
namespace renpy::script { namespace renpy::script {
using namespace renpy::language; extern char const * const strings[];
char const * const strings[] = { extern const int strings_length;
"Far over the mountains of Almystice", // 0
"Beyond the tumultuous waters of the Lilac Bay", // 1
"And across the vast fields of Alysen", // 2
"Tiny minstrels can be heard amongst the trees", // 3
"Are we almost there?", // 4
"Hmmm... Not really", // 5
"How much further have we to go?", // 6
"About two more moons", // 7
"We are still sooo far awayyy", // 8
"And it will be even further if you dont stop complaining", // 9
"Easy for you to say, all you have to carry is a little memory pipe!", // 10
"I'm tired ><", // 11
"Don't start whining now!", // 12
"You need to remember why we have come all this way", // 13
"I understand... I suppose it is for an important purpose", // 14
"We're almost out of the forest, we can take a little break once we clear the tree line", // 15
"Is that where the flora field is?", // 16
"Why yes, If I remember correctly, it should be just up ahead", // 17
"As the minstrel mice girls continue along the path, the forest opens up into a beautiful field of flowers", // 18
"Look at all the butterflies! They are all so pretty!", // 19
"This place is like a dream...", // 20
"There are so many flowers this time of year", // 21
"I told you it would be worth the journey!", // 22
"Can we stop for a bit now?", // 23
"Of course", // 24
"Ya know, Its a shame we didnt save some of those giant strawberries you found", // 25
"I told you not to eat them all!", // 26
"Yah yah", // 27
"Anyways, shall I recite a tale?", // 28
"Why dont you sing the story of Eleanor the Hero!", // 29
"Sure", // 30
"...", // 31
"Serves you right for scaring those elephant-dogs", // 32
"They were asking for it, you know", // 33
"Rawrrrr", // 34
"AHHHHHHHHHH!!!!!", // 35
"Nyanyanyanya", // 36
"Well, what do we have here? If it isn't two little meowse girls, all alone amongst the flowers", // 37
"Please don't eat us!!!", // 38
"Alice don't run, our only chance is through pleading!", // 39
"Please don't eat us, miss kitty cat!!! ><", // 40
"I'm not gonna eat you nyanyanya", // 41
"I just want to know what two little meowses are doing so very far away from home", // 42
"Also, are you minstrels?", // 43
"Y-Yes", // 44
"W-We are on a quest to Castle Alysen...", // 45
"Shh don't tell her that", // 46
"The Castle of Alysen you say?!?", // 47
"Why, that's where I am headed!", // 48
"You don't say...", // 49
"Yah, I do actually", // 50
"So... Why might you be traveling to the castle?", // 51
"I belong to the lineage of Agrepen", // 52
"And what might that mean?", // 53
"The Agrepens are a long line of felines loyal to the crown of corvidae", // 54
"Really? That must mean you are a noble?", // 55
"Well, not really...", // 56
"My father was one of the queens knights many years ago", // 57
"Ah I see", // 58
"So do you live at the castle or something?", // 59
"Well, no...", // 60
"Then why are you traveling to Castle Alysen?", // 61
"uhhh", // 62
"I DONT NEED TO BE PRESSURED BY LITTLE MICE TO SAY ANYTHING!!!!", // 63
"GOOD DAY!", // 64
"Wha...", // 65
"Phew, I was scared she was gonna follow us the whole way", // 66
"She didn't seem so bad", // 67
"Are you kidding? She's a crazy kitty!", // 68
"After their encounter with the weird cat, the mice scurry out of the flower field and into the nearby meadow", // 69
"I think this is the right way...", // 70
"Then where did the path go?", // 71
"How am I supposed to know?", // 72
"Did you hear that?!?!", // 73
"Hey there...", // 74
"I apologize", // 75
"I didn't mean to storm off like that", // 76
"Ha ha, no problem...", // 77
"So... Why are you traveling to Castle Alysen?", // 78
"Alice!!!", // 79
"If you must know, I have been summoned by the queen", // 80
"I suspect that my poor reputation amongst the locals of Eastern Nidus has come back to haunt me", // 81
"Though I know not what what she has summoned me for", // 82
"Ahhhhhh", // 83
"So then...", // 84
"Why are YOU traveling to the Castle?", // 85
"We are delivering a feather!!!", // 86
"Alice no!", // 87
"A feather that belonged to the queen herself!!!", // 88
"Why you little...", // 89
"A feather you say? One of the queens?", // 90
"How on the face of Al Mot might you have aquired such a thing?", // 91
"If it is authentic, that is...", // 92
"Since Alice cannot keep a secret, I shall tell you", // 93
"Seven moons ago, our town was attacked by three owls and a band of mice from the northern principalities", // 94
"Eventually word spread to greater nearby settlements, and so", // 95
"Messengers from the keep in Musia sent for aid from the Ravens", // 96
"Four moons ago, the request was answered", // 97
"And a small group of mice accompanied by two ravens a fox, and three squirrels set out to the northern principalities", // 98
"Anyways, long story short, we drove those barbaric rats out of their home", // 99
"They arent actual rats you know", // 100
"Obviously, but you wont catch me speaking kindly of them", // 101
"And you forgot the most important part", // 102
"Yah yah, I am getting there", // 103
"So, essentially, my brother is trained in archery, and...", // 104
"Speed it up already", // 105
"You tell it then!", // 106
"My cousin found this feather in one of the highest towers of a castle far to the north", // 107
"How do you know it belongs to the queen?", // 108
"It said so itself above the display on the wall", // 109
"Supposedly, it was in a room filled with treasures!", // 110
"That is very nice and all, but what are the two of you doing out here all alone?", // 111
"Do you expect every bird in Avia to respect your alliance with Castle Alysen?", // 112
"What do you mean?", // 113
"I mean, the two of you probably look like walking dinner to most creatures", // 114
"I could go for some dinner...", // 115
"Anyways...", // 116
"To answer your question, upon returning to the village, the feather was taken from my brother by the needle guild", // 117
"So... Yesterday, after sundown", // 118
"We stole the feather from the guild hall before vanishing into the night", // 119
"Can you imagine the look on their stupid faces, when they woke up, and not only is the feather missing", // 120
"But so are we!", // 121
"Hahahaha", // 122
"Are the two of you mad?", // 123
"I assume you are attempting to return the Queens feather?", // 124
"Yes, we intend to deliver the feather to its rightful owner", // 125
"Absolute madness!", // 126
"I will follow the two of you", // 127
"To keep you safe, that is", // 128
"Alright!", // 129
"Ha ha... Okay...", // 130
"Great! Follow me, I know a shortcut! :3", // 131
"Sounds good!", // 132
"Oh dear!", // 133
"And so the mice girls follow the noble cat further towards their destination", // 134
"Nya", // 135
"Look, your right, the castle is just up ahead!", // 136
"Wait up", // 137
"I told you I knew a shortcut!", // 138
"Most people take the long way around", // 139
"Yah because these are royal wheatfields!", // 140
"Who cares?", // 141
"Are you trying to get us killed?", // 142
"Its trespassing on royal land!", // 143
"Calm down, I have done this a million times", // 144
"That doesnt make me calm!", // 145
"How can the rolling fields of wheat not calm your spirit?", // 146
"You little mice truly are mad!", // 147
"I like the wheat!", // 148
"Shut up!", // 149
"Sounds like someone needs a nap!", // 150
"Why? because I'm not insane like you?", // 151
"Yah, your so sane, that you decided to steal from your town and then run off alone to the country of birds", // 152
"The power of friendship wont protect the two of you from becoming dinner", // 153
"And that, is why I feel obligated to accompany you!", // 154
"Hey, we have a good reason!", // 155
"And what might that be?", // 156
"My brother found the feather, not the town guild, its a matter of family pride!", // 157
"Pride has touched the chosen meouse", // 158
"Flies she towards the Castle", // 159
"But her ambition burns far too bright, and silly myice dont have any wings to myelt", // 160
"What?", // 161
"Nyanyanya", // 162
"Nyevermind", // 163
"Sing me a song little minstrels!", // 164
"Very Nyice!", // 165
"Now tell me little minstrels, what are your names?", // 166
"My name is Alice", // 167
"And my name is Eily", // 168
"What is your name?", // 169
"My name is Leona!", // 170
"And so, the odd trio walked through the wheatfields and towards the castle walls", // 171
"Upon approaching the castle walls", // 172
};
constexpr int strings_length = (sizeof (strings)) / (sizeof (strings[0])); extern const language::character characters[];
extern const int characters_length;
const character characters[] = { extern const language::audio audio[];
{ .characterName = "Alice" }, // 0 extern const int audio_length;
{ .characterName = "Cat" }, // 1
{ .characterName = "Eily" }, // 2
{ .characterName = "Mouse Girls" }, // 3
{ .characterName = "Narrator" }, // 4
{ .characterName = "Leona" }, // 5
};
constexpr int characters_length = (sizeof (characters)) / (sizeof (characters[0])); extern const language::image images[];
extern const int images_length;
const audio audio[] = { extern const language::option options[];
{ .path = "sfx/Chime.opus" }, // 0 sfx/Chime.ogg extern const int options_length;
{ .path = "sfx/MistAmbience.opus" }, // 1 sfx/MistAmbience.ogg
{ .path = "music/TinyForestMinstrels.opus" }, // 2 music/TinyForestMinstrels.ogg
{ .path = "n5test.opus" }, // 3 n5test.ogg
{ .path = "music/PhrygianButterflies.opus" }, // 4 music/PhrygianButterflies.ogg
{ .path = "music/Poem1.opus" }, // 5 music/Poem1.ogg
{ .path = "placeholdermeow.opus" }, // 6 placeholdermeow.mp3
{ .path = "music/ScaredMice.opus" }, // 7 music/ScaredMice.ogg
{ .path = "music/WheatFields.opus" }, // 8 music/WheatFields.ogg
};
constexpr int audio_length = (sizeof (audio)) / (sizeof (audio[0])); extern const language::statement statements[];
extern const int statements_length;
const image images[] = {
{ .path = "data/renpy/images/bg/white.dds" }, // 0 bg/white.png
{ .path = "data/renpy/images/bg/forest1.dds" }, // 1 bg/forest1.png
{ .path = "data/renpy/images/bg/forest2.dds" }, // 2 bg/forest2.png
{ .path = "data/renpy/images/bg/flowerfield1.dds" }, // 3 bg/flowerfield1.png
{ .path = "data/renpy/images/bg/wheatfield1.dds" }, // 4 bg/wheatfield1.png
{ .path = "data/renpy/images/ch/cat.dds" }, // 5 ch/cat.png
{ .path = "data/renpy/images/ch/catw.dds" }, // 6 ch/catw.png
{ .path = "data/renpy/images/ch/Eily.dds" }, // 7 ch/Eily.png
{ .path = "data/renpy/images/ch/Alice.dds" }, // 8 ch/Alice.png
};
constexpr int images_length = (sizeof (images)) / (sizeof (images[0]));
const option options[] = {
{ .string = "Complain", .statementIndex = 18 }, // 0
{ .string = "Rationalize", .statementIndex = 26 }, // 1
{ .string = "Good idea", .statementIndex = 54 }, // 2
{ .string = "I am too tired", .statementIndex = 61 }, // 3
{ .string = "Beg for mercy", .statementIndex = 78 }, // 4
{ .string = "Run", .statementIndex = 80 }, // 5
};
constexpr int options_length = (sizeof (options)) / (sizeof (options[0]));
const statement statements[] = {
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 0 sfx/Chime.ogg
{ .type = type::play, .play = { .audioIndex = 1, /* FIXME channel */ } }, // 1 sfx/MistAmbience.ogg
{ .type = type::scene, .scene = { .imageIndex = 0 } }, // 2 bgwhite
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 0 } }, // 4 n "Far over the mountains of Almystice"
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 1 } }, // 5 n "Beyond the tumultuous waters of the Lilac Bay"
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 2 } }, // 6 n "And across the vast fields of Alysen"
{ .type = type::play, .play = { .audioIndex = 2, /* FIXME channel */ } }, // 7 music/TinyForestMinstrels.ogg
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 3 } }, // 8 n "Tiny minstrels can be heard amongst the trees"
{ .type = type::scene, .scene = { .imageIndex = 1 } }, // 9 bgforest1
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 11 al
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 4 } }, // 12 a "Are we almost there?"
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 13 ei
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 5 } }, // 14 e "Hmmm... Not really"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 6 } }, // 15 a "How much further have we to go?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 7 } }, // 16 e "About two more moons"
{ .type = type::menu, .menu = { .count = 2, .optionIndex = 0 } }, // 17 "Complain", "Rationalize"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 8 } }, // 18 a "We are still sooo far awayyy"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 9 } }, // 19 e "And it will be even further if you dont stop complaining"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 10 } }, // 20 a "Easy for you to say, all you have to carry is a little memory pipe!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 11 } }, // 21 a "I'm tired ><"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 12 } }, // 22 e "Don't start whining now!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 13 } }, // 23 e "You need to remember why we have come all this way"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 24 a "I understand... I suppose it is for an important purpose"
{ .type = type::jump, .jump = { .statementIndex = 28 } }, // 25 internal jump (b'__menu_end', 0)
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 26 a "I understand... I suppose it is for an important purpose"
{ .type = type::jump, .jump = { .statementIndex = 28 } }, // 27 internal jump (b'__menu_end', 0)
{ .type = type::jump, .jump = { .statementIndex = 29 } }, // 28 mainbranch1
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 15 } }, // 29 e "We're almost out of the forest, we can take a little break once we clear the tree line"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 16 } }, // 30 a "Is that where the flora field is?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 17 } }, // 31 e "Why yes, If I remember correctly, it should be just up ahead"
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 32
{ .type = type::scene, .scene = { .imageIndex = 0 } }, // 33 bgwhite
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 34 sfx/Chime.ogg
{ .type = type::voice, .voice = { .audioIndex = 3 } }, // 36 n5test.ogg
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 18 } }, // 37 n "As the minstrel mice girls continue along the path, the forest opens up into a beautiful field of flowers"
{ .type = type::play, .play = { .audioIndex = 4, /* FIXME channel */ } }, // 38 music/PhrygianButterflies.ogg
{ .type = type::scene, .scene = { .imageIndex = 3 } }, // 39 bgflower1
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 41 ei
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 19 } }, // 42 e "Look at all the butterflies! They are all so pretty!"
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 43 al
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 20 } }, // 44 a "This place is like a dream..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 21 } }, // 45 e "There are so many flowers this time of year"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 22 } }, // 46 e "I told you it would be worth the journey!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 23 } }, // 47 a "Can we stop for a bit now?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 24 } }, // 48 e "Of course"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 25 } }, // 49 e "Ya know, Its a shame we didnt save some of those giant strawberries you found"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 26 } }, // 50 a "I told you not to eat them all!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 27 } }, // 51 e "Yah yah"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 28 } }, // 52 e "Anyways, shall I recite a tale?"
{ .type = type::menu, .menu = { .count = 2, .optionIndex = 2 } }, // 53 "Good idea", "I am too tired"
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 54
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 29 } }, // 55 a "Why dont you sing the story of Eleanor the Hero!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 30 } }, // 56 e "Sure"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 31 } }, // 57 a "..."
{ .type = type::play, .play = { .audioIndex = 5, /* FIXME channel */ } }, // 58 music/Poem1.ogg
{ .type = type::pause, .pause = { .duration = 40 } }, // 59
{ .type = type::jump, .jump = { .statementIndex = 65 } }, // 60 internal jump (b'__menu_end', 1)
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 32 } }, // 61 e "Serves you right for scaring those elephant-dogs"
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 62
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 33 } }, // 63 a "They were asking for it, you know"
{ .type = type::jump, .jump = { .statementIndex = 65 } }, // 64 internal jump (b'__menu_end', 1)
{ .type = type::jump, .jump = { .statementIndex = 66 } }, // 65 mainbranch2
{ .type = type::hide, .hide = { .imageIndex = 7 } }, // 66 ei
{ .type = type::show, .show = { .imageIndex = 6, .transformIndex = transform::right } }, // 67 catw
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 68 ei
{ .type = type::voice, .voice = { .audioIndex = 6 } }, // 69 placeholdermeow.mp3
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 34 } }, // 70 c "Rawrrrr"
{ .type = type::hide, .hide = { .imageIndex = 6 } }, // 71 catw
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 72 cat
{ .type = type::play, .play = { .audioIndex = 7, /* FIXME channel */ } }, // 73 music/ScaredMice.ogg
{ .type = type::say, .say = { .characterIndex = 3, .stringIndex = 35 } }, // 74 mg "AHHHHHHHHHH!!!!!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 36 } }, // 75 c "Nyanyanyanya"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 37 } }, // 76 c "Well, what do we have here? If it isn't two little meowse girls, all alone amongst the flowers"
{ .type = type::menu, .menu = { .count = 2, .optionIndex = 4 } }, // 77 "Beg for mercy", "Run"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 38 } }, // 78 a "Please don't eat us!!!"
{ .type = type::jump, .jump = { .statementIndex = 83 } }, // 79 internal jump (b'__menu_end', 2)
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 39 } }, // 80 e "Alice don't run, our only chance is through pleading!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 40 } }, // 81 e "Please don't eat us, miss kitty cat!!! ><"
{ .type = type::jump, .jump = { .statementIndex = 83 } }, // 82 internal jump (b'__menu_end', 2)
{ .type = type::jump, .jump = { .statementIndex = 84 } }, // 83 mainbranch3
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 84
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 41 } }, // 85 c "I'm not gonna eat you nyanyanya"
{ .type = type::play, .play = { .audioIndex = 2, /* FIXME channel */ } }, // 86 music/TinyForestMinstrels.ogg
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 42 } }, // 87 c "I just want to know what two little meowses are doing so very far away from home"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 43 } }, // 88 c "Also, are you minstrels?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 44 } }, // 89 e "Y-Yes"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 45 } }, // 90 a "W-We are on a quest to Castle Alysen..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 46 } }, // 91 e "Shh don't tell her that"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 47 } }, // 92 c "The Castle of Alysen you say?!?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 48 } }, // 93 c "Why, that's where I am headed!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 49 } }, // 94 e "You don't say..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 50 } }, // 95 c "Yah, I do actually"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 51 } }, // 96 e "So... Why might you be traveling to the castle?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 52 } }, // 97 c "I belong to the lineage of Agrepen"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 53 } }, // 98 e "And what might that mean?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 54 } }, // 99 c "The Agrepens are a long line of felines loyal to the crown of corvidae"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 55 } }, // 100 e "Really? That must mean you are a noble?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 56 } }, // 101 c "Well, not really..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 57 } }, // 102 c "My father was one of the queens knights many years ago"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 58 } }, // 103 e "Ah I see"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 59 } }, // 104 e "So do you live at the castle or something?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 60 } }, // 105 c "Well, no..."
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 61 } }, // 106 a "Then why are you traveling to Castle Alysen?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 62 } }, // 107 c "uhhh"
{ .type = type::play, .play = { .audioIndex = 1, /* FIXME channel */ } }, // 108 sfx/MistAmbience.ogg
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 109
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 63 } }, // 110 c "I DONT NEED TO BE PRESSURED BY LITTLE MICE TO SAY ANYTHING!!!!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 64 } }, // 111 c "GOOD DAY!"
{ .type = type::hide, .hide = { .imageIndex = 5 } }, // 112 cat
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 65 } }, // 113 a "Wha..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 66 } }, // 114 e "Phew, I was scared she was gonna follow us the whole way"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 67 } }, // 115 a "She didn't seem so bad"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 68 } }, // 116 e "Are you kidding? She's a crazy kitty!"
{ .type = type::scene, .scene = { .imageIndex = 0 } }, // 117 bgwhite
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 118 sfx/Chime.ogg
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 69 } }, // 120 n "After their encounter with the weird cat, the mice scurry out of the flower field and into the nearby meadow"
{ .type = type::scene, .scene = { .imageIndex = 2 } }, // 121 bgforest2
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 123 ei
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 70 } }, // 124 e "I think this is the right way..."
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 125 al
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 71 } }, // 126 a "Then where did the path go?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 72 } }, // 127 e "How am I supposed to know?"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 73 } }, // 128 a "Did you hear that?!?!"
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 129 ei
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 130 cat
{ .type = type::play, .play = { .audioIndex = 4, /* FIXME channel */ } }, // 131 music/PhrygianButterflies.ogg
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 74 } }, // 132 c "Hey there..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 75 } }, // 133 c "I apologize"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 76 } }, // 134 c "I didn't mean to storm off like that"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 77 } }, // 135 e "Ha ha, no problem..."
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 78 } }, // 136 a "So... Why are you traveling to Castle Alysen?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 79 } }, // 137 e "Alice!!!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 80 } }, // 138 c "If you must know, I have been summoned by the queen"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 81 } }, // 139 c "I suspect that my poor reputation amongst the locals of Eastern Nidus has come back to haunt me"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 82 } }, // 140 c "Though I know not what what she has summoned me for"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 83 } }, // 141 a "Ahhhhhh"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 84 } }, // 142 c "So then..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 85 } }, // 143 c "Why are YOU traveling to the Castle?"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 86 } }, // 144 a "We are delivering a feather!!!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 87 } }, // 145 e "Alice no!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 88 } }, // 146 a "A feather that belonged to the queen herself!!!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 89 } }, // 147 e "Why you little..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 90 } }, // 148 c "A feather you say? One of the queens?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 91 } }, // 149 c "How on the face of Al Mot might you have aquired such a thing?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 92 } }, // 150 c "If it is authentic, that is..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 93 } }, // 151 e "Since Alice cannot keep a secret, I shall tell you"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 94 } }, // 152 e "Seven moons ago, our town was attacked by three owls and a band of mice from the northern principalities"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 95 } }, // 153 e "Eventually word spread to greater nearby settlements, and so"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 96 } }, // 154 e "Messengers from the keep in Musia sent for aid from the Ravens"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 97 } }, // 155 e "Four moons ago, the request was answered"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 98 } }, // 156 e "And a small group of mice accompanied by two ravens a fox, and three squirrels set out to the northern principalities"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 99 } }, // 157 e "Anyways, long story short, we drove those barbaric rats out of their home"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 100 } }, // 158 a "They arent actual rats you know"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 101 } }, // 159 e "Obviously, but you wont catch me speaking kindly of them"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 102 } }, // 160 a "And you forgot the most important part"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 103 } }, // 161 e "Yah yah, I am getting there"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 104 } }, // 162 e "So, essentially, my brother is trained in archery, and..."
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 105 } }, // 163 a "Speed it up already"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 106 } }, // 164 e "You tell it then!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 107 } }, // 165 a "My cousin found this feather in one of the highest towers of a castle far to the north"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 108 } }, // 166 c "How do you know it belongs to the queen?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 109 } }, // 167 e "It said so itself above the display on the wall"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 110 } }, // 168 a "Supposedly, it was in a room filled with treasures!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 111 } }, // 169 c "That is very nice and all, but what are the two of you doing out here all alone?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 112 } }, // 170 c "Do you expect every bird in Avia to respect your alliance with Castle Alysen?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 113 } }, // 171 e "What do you mean?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 114 } }, // 172 c "I mean, the two of you probably look like walking dinner to most creatures"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 115 } }, // 173 a "I could go for some dinner..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 116 } }, // 174 e "Anyways..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 117 } }, // 175 e "To answer your question, upon returning to the village, the feather was taken from my brother by the needle guild"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 118 } }, // 176 e "So... Yesterday, after sundown"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 119 } }, // 177 e "We stole the feather from the guild hall before vanishing into the night"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 120 } }, // 178 e "Can you imagine the look on their stupid faces, when they woke up, and not only is the feather missing"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 121 } }, // 179 e "But so are we!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 122 } }, // 180 a "Hahahaha"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 123 } }, // 181 c "Are the two of you mad?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 124 } }, // 182 c "I assume you are attempting to return the Queens feather?"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 125 } }, // 183 a "Yes, we intend to deliver the feather to its rightful owner"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 126 } }, // 184 c "Absolute madness!"
{ .type = type::say, .say = { .characterIndex = 3, .stringIndex = 31 } }, // 185 mg "..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 127 } }, // 186 c "I will follow the two of you"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 128 } }, // 187 c "To keep you safe, that is"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 129 } }, // 188 a "Alright!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 130 } }, // 189 e "Ha ha... Okay..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 131 } }, // 190 c "Great! Follow me, I know a shortcut! :3"
{ .type = type::hide, .hide = { .imageIndex = 5 } }, // 191 cat
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 132 } }, // 192 a "Sounds good!"
{ .type = type::hide, .hide = { .imageIndex = 8 } }, // 193 al
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 194
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 133 } }, // 195 e "Oh dear!"
{ .type = type::hide, .hide = { .imageIndex = 7 } }, // 196 ei
{ .type = type::scene, .scene = { .imageIndex = 0 } }, // 197 bgwhite
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 198 sfx/Chime.ogg
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 134 } }, // 200 n "And so the mice girls follow the noble cat further towards their destination"
{ .type = type::scene, .scene = { .imageIndex = 4 } }, // 201 bgwheatfield1
{ .type = type::play, .play = { .audioIndex = 8, /* FIXME channel */ } }, // 202 music/WheatFields.ogg
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 203 cat
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 135 } }, // 205 c "Nya"
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 206 al
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 136 } }, // 207 a "Look, your right, the castle is just up ahead!"
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 208 ei
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 137 } }, // 209 e "Wait up"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 138 } }, // 210 c "I told you I knew a shortcut!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 139 } }, // 211 c "Most people take the long way around"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 140 } }, // 212 e "Yah because these are royal wheatfields!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 141 } }, // 213 a "Who cares?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 142 } }, // 214 e "Are you trying to get us killed?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 143 } }, // 215 e "Its trespassing on royal land!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 144 } }, // 216 c "Calm down, I have done this a million times"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 145 } }, // 217 e "That doesnt make me calm!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 146 } }, // 218 c "How can the rolling fields of wheat not calm your spirit?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 147 } }, // 219 c "You little mice truly are mad!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 148 } }, // 220 a "I like the wheat!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 149 } }, // 221 e "Shut up!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 150 } }, // 222 c "Sounds like someone needs a nap!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 151 } }, // 223 e "Why? because I'm not insane like you?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 152 } }, // 224 c "Yah, your so sane, that you decided to steal from your town and then run off alone to the country of birds"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 153 } }, // 225 c "The power of friendship wont protect the two of you from becoming dinner"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 154 } }, // 226 c "And that, is why I feel obligated to accompany you!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 155 } }, // 227 e "Hey, we have a good reason!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 156 } }, // 228 c "And what might that be?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 157 } }, // 229 e "My brother found the feather, not the town guild, its a matter of family pride!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 158 } }, // 230 c "Pride has touched the chosen meouse"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 159 } }, // 231 c "Flies she towards the Castle"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 160 } }, // 232 c "But her ambition burns far too bright, and silly myice dont have any wings to myelt"
{ .type = type::say, .say = { .characterIndex = 3, .stringIndex = 161 } }, // 233 mg "What?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 162 } }, // 234 c "Nyanyanya"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 163 } }, // 235 c "Nyevermind"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 164 } }, // 236 c "Sing me a song little minstrels!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 165 } }, // 237 c "Very Nyice!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 166 } }, // 238 c "Now tell me little minstrels, what are your names?"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 167 } }, // 239 a "My name is Alice"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 168 } }, // 240 e "And my name is Eily"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 169 } }, // 241 e "What is your name?"
{ .type = type::say, .say = { .characterIndex = 5, .stringIndex = 170 } }, // 242 l "My name is Leona!"
{ .type = type::scene, .scene = { .imageIndex = 0 } }, // 243 bgwhite
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 171 } }, // 245 n "And so, the odd trio walked through the wheatfields and towards the castle walls"
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 172 } }, // 246 n "Upon approaching the castle walls"
{ .type = type::_return }, // 247
};
constexpr int statements_length = (sizeof (statements)) / (sizeof (statements[0]));
} }

View File

@ -1,3 +1,7 @@
#pragma once
#include "renpy/interpreter.h"
namespace renpy { namespace renpy {
struct Image { struct Image {
VkImage image; VkImage image;
@ -7,17 +11,19 @@ namespace renpy {
struct ImageInstance { struct ImageInstance {
struct { struct {
uint16_t width; int16_t width;
uint16_t height; int16_t height;
} size; } size;
struct { struct {
uint16_t x; int16_t x;
uint16_t y; int16_t y;
} topLeft; } topLeft;
uint16_t imageIndex; uint32_t color;
int16_t imageIndex;
int16_t _padding;
}; };
static_assert((sizeof (ImageInstance)) == 10); static_assert((sizeof (ImageInstance)) == 16);
struct vulkan { struct vulkan {
static constexpr int perVertexSize = (4) * 2; static constexpr int perVertexSize = (4) * 2;
@ -75,7 +81,8 @@ namespace renpy {
void create_pipeline(); void create_pipeline();
void create_instance_buffers(); void create_instance_buffers();
void draw(VkCommandBuffer commandBuffer, void draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex); uint32_t frameIndex,
renpy::interpreter const& state);
}; };
} }

View File

@ -3,17 +3,20 @@ import lex
import parse import parse
from dataclasses import dataclass from dataclasses import dataclass
from pprint import pprint from pprint import pprint
from itertools import chain
import generate import generate
@dataclass @dataclass
class State: class State:
images: list images: list
colors: list
characters: list characters: list
statements: list statements: list
menus: list menus: list
entries: dict entries: dict
images_lookup: dict[str, int] # identifier to image index images_lookup: dict[str, int] # identifier to image index
colors_lookup: dict[str, int] # identifier to image index
characters_lookup: dict[str, int] # identifier to character index characters_lookup: dict[str, int] # identifier to character index
labels_lookup: dict[str, int] # identifier to statement index labels_lookup: dict[str, int] # identifier to statement index
audio_lookup: dict[str, int] audio_lookup: dict[str, int]
@ -53,8 +56,12 @@ def pass1(state, ast):
key = lhs_key(ast.name) key = lhs_key(ast.name)
assert key not in state.global_identifiers assert key not in state.global_identifiers
state.global_identifiers.add(key) state.global_identifiers.add(key)
state.images_lookup[key] = len(state.images) if ast.path.lexeme.startswith(b"#"):
state.images.append(ast) state.colors_lookup[key] = len(state.colors)
state.colors.append(ast)
else:
state.images_lookup[key] = len(state.images)
state.images.append(ast)
elif type(ast) is parse.Define: elif type(ast) is parse.Define:
key = lhs_key(ast.name) key = lhs_key(ast.name)
assert key not in state.global_identifiers assert key not in state.global_identifiers
@ -118,6 +125,12 @@ transforms_set = {
b"right", b"right",
} }
def parse_color(b):
assert b.startswith(b"#"), b
assert len(b) == 7
color = int(b[1:].decode('utf-8'), 16)
return color
def pass2_statement(state, pc, statement): def pass2_statement(state, pc, statement):
if type(statement) is parse.Play: if type(statement) is parse.Play:
comment = statement.path.lexeme.decode('utf-8') comment = statement.path.lexeme.decode('utf-8')
@ -125,12 +138,22 @@ def pass2_statement(state, pc, statement):
yield f"{{ .type = type::play, .play = {{ .audioIndex = {audio_index}, /* FIXME channel */ }} }}, // {pc} {comment}" yield f"{{ .type = type::play, .play = {{ .audioIndex = {audio_index}, /* FIXME channel */ }} }}, // {pc} {comment}"
elif type(statement) is parse.Scene: elif type(statement) is parse.Scene:
key = lhs_key(statement.name) key = lhs_key(statement.name)
image_index = state.images_lookup[key] if key in state.images_lookup:
comment = ".".join(k.decode('utf-8') for k in key) image_index = state.images_lookup[key]
yield f"{{ .type = type::scene, .scene = {{ .imageIndex = {image_index} }} }}, // {pc} {comment}" comment = ".".join(k.decode('utf-8') for k in key)
yield f"{{ .type = type::scene, .scene = {{ .imageIndex = {image_index} }} }}, // {pc} {comment}"
else:
color_index = state.colors_lookup[key]
color = parse_color(state.colors[color_index].path.lexeme)
comment = ".".join(k.decode('utf-8') for k in key)
yield f"{{ .type = type::scene_color, .scene_color = {{ .color = 0x{color:06x} }} }}, // {pc} {comment}"
elif type(statement) is parse.With: elif type(statement) is parse.With:
print(f"not implemented: {statement}", file=sys.stderr) #print(f"not implemented: {statement}", file=sys.stderr)
pass if statement.function_call.name.lexeme == b'Dissolve':
duration, = statement.function_call.args
yield f"{{ .type = type::dissolve, .dissolve = {{ .duration = {duration.lexeme} }} }}, // {pc}"
else:
assert False, (pc, statement)
elif type(statement) is parse.Voice: elif type(statement) is parse.Voice:
comment = statement.path.lexeme.decode('utf-8') comment = statement.path.lexeme.decode('utf-8')
audio_index = state.audio_lookup[statement.path.lexeme] audio_index = state.audio_lookup[statement.path.lexeme]
@ -181,29 +204,32 @@ def pass2_statement(state, pc, statement):
assert False, (type(statement), statement) assert False, (type(statement), statement)
def pass2_statements(state): def pass2_statements(state):
yield "const statement statements[] = {" yield "const language::statement statements[] = {"
for pc, statement in enumerate(state.statements): for pc, statement in enumerate(state.statements):
print(pc, statement, file=sys.stderr)
yield from pass2_statement(state, pc, statement) yield from pass2_statement(state, pc, statement)
yield "};" yield "};"
yield "constexpr int statements_length = (sizeof (statements)) / (sizeof (statements[0]));" yield "const int statements_length = (sizeof (statements)) / (sizeof (statements[0]));"
def pass2_strings(state): def pass2_strings(state):
yield "char const * const strings[] = {" yield "char const * const strings[] = {"
for string, i in sorted(state.string_lookup.items(), key=lambda kv: kv[1]): for string, i in sorted(state.string_lookup.items(), key=lambda kv: kv[1]):
yield f"\"{string.decode('utf-8')}\", // {i}" yield f"\"{string.decode('utf-8')}\", // {i}"
yield "};" yield "};"
yield "constexpr int strings_length = (sizeof (strings)) / (sizeof (strings[0]));" yield "const int strings_length = (sizeof (strings)) / (sizeof (strings[0]));"
def pass2_characters(state): def pass2_characters(state):
yield "const character characters[] = {" yield "const language::character characters[] = {"
for i, character in enumerate(state.characters): for i, character in enumerate(state.characters):
character_name, = character.value.args character_name, = character.value.args
yield f"{{ .characterName = \"{character_name.lexeme.decode('utf-8')}\" }}, // {i}" color, = (value.lexeme for key, value in character.value.kwargs if key.lexeme == b'color')
color = int(color.decode('utf-8'), 16)
yield f"{{ .characterName = \"{character_name.lexeme.decode('utf-8')}\", .color = 0x{color:06x} }}, // {i}"
yield "};" yield "};"
yield "constexpr int characters_length = (sizeof (characters)) / (sizeof (characters[0]));" yield "const int characters_length = (sizeof (characters)) / (sizeof (characters[0]));"
def pass2_audio(state): def pass2_audio(state):
yield "const audio audio[] = {" yield "const language::audio audio[] = {"
for audio, i in sorted(state.audio_lookup.items(), key=lambda kv: kv[1]): for audio, i in sorted(state.audio_lookup.items(), key=lambda kv: kv[1]):
orig_path = audio.decode('utf-8') orig_path = audio.decode('utf-8')
path = orig_path path = orig_path
@ -215,10 +241,10 @@ def pass2_audio(state):
assert False, path assert False, path
yield f"{{ .path = \"{path}.opus\" }}, // {i} {orig_path}" yield f"{{ .path = \"{path}.opus\" }}, // {i} {orig_path}"
yield "};" yield "};"
yield "constexpr int audio_length = (sizeof (audio)) / (sizeof (audio[0]));" yield "const int audio_length = (sizeof (audio)) / (sizeof (audio[0]));"
def pass2_images(state): def pass2_images(state):
yield "const image images[] = {" yield "const language::image images[] = {"
for i, image in enumerate(state.images): for i, image in enumerate(state.images):
orig_path = image.path.lexeme.decode('utf-8') orig_path = image.path.lexeme.decode('utf-8')
path = orig_path path = orig_path
@ -228,17 +254,18 @@ def pass2_images(state):
assert False, path assert False, path
yield f"{{ .path = \"data/renpy/images/{path}.dds\" }}, // {i} {orig_path}" yield f"{{ .path = \"data/renpy/images/{path}.dds\" }}, // {i} {orig_path}"
yield "};" yield "};"
yield "constexpr int images_length = (sizeof (images)) / (sizeof (images[0]));" yield "const int images_length = (sizeof (images)) / (sizeof (images[0]));"
def pass2_options(state): def pass2_options(state):
yield "const option options[] = {" yield "const language::option options[] = {"
for i, (lexeme, statement_index) in sorted(state.entries.items(), key=lambda kv: kv[0]): for i, (lexeme, statement_index) in sorted(state.entries.items(), key=lambda kv: kv[0]):
yield f"{{ .string = \"{lexeme.decode('utf-8')}\", .statementIndex = {statement_index} }}, // {i}" yield f"{{ .string = \"{lexeme.decode('utf-8')}\", .statementIndex = {statement_index} }}, // {i}"
yield "};" yield "};"
yield "constexpr int options_length = (sizeof (options)) / (sizeof (options[0]));" yield "const int options_length = (sizeof (options)) / (sizeof (options[0]));"
def pass2(state): def pass2(state):
yield "#include \"renpy/language.h\"" yield "#include \"renpy/language.h\""
yield "#include \"renpy/script.h\""
yield "" yield ""
yield "namespace renpy::script {" yield "namespace renpy::script {"
yield "using namespace renpy::language;" yield "using namespace renpy::language;"
@ -251,17 +278,22 @@ def pass2(state):
yield "}" yield "}"
def main(): def main():
preamble = b"""
image _internal_flowers = "flowers.png"
"""
with open(sys.argv[1], 'rb') as f: with open(sys.argv[1], 'rb') as f:
mem = memoryview(f.read()) mem = memoryview(bytes(chain(preamble, f.read())))
tokens = list(lex.tokenize(mem)) tokens = list(lex.tokenize(mem))
state = State( state = State(
images = list(), images = list(),
colors = list(),
characters = list(), characters = list(),
statements = list(), statements = list(),
menus = list(), menus = list(),
entries = dict(), entries = dict(),
images_lookup = dict(), images_lookup = dict(),
colors_lookup = dict(),
characters_lookup = dict(), characters_lookup = dict(),
labels_lookup = dict(), labels_lookup = dict(),
audio_lookup = dict(), audio_lookup = dict(),

View File

@ -23,12 +23,13 @@ struct VSOutput
{ {
float4 Position : SV_POSITION; float4 Position : SV_POSITION;
float2 Texture : NORMAL0; float2 Texture : NORMAL0;
float4 Color : Color;
}; };
[shader("vertex")] [shader("vertex")]
VSOutput VSMain(VSInput input) VSOutput VSMain(VSInput input)
{ {
float2 inverseTexel = float2(1.0 / 256.0, 1.0 / 256.0); float2 inverseTexel = float2(1.0 / 128.0, 1.0 / 256.0);
float2 inversePixel = float2(1.0 / 1280.0, 1.0 / 720.0); float2 inversePixel = float2(1.0 / 1280.0, 1.0 / 720.0);
int index = input.InstanceGlyph; int index = input.InstanceGlyph;
@ -36,6 +37,7 @@ VSOutput VSMain(VSInput input)
float2 position = (input.Texture * Glyphs[index].Size + input.InstancePosition) * inversePixel; float2 position = (input.Texture * Glyphs[index].Size + input.InstancePosition) * inversePixel;
output.Position = float4(position * 2.0 - 1.0, 0, 1); output.Position = float4(position * 2.0 - 1.0, 0, 1);
output.Texture = (input.Texture * Glyphs[index].Size + Glyphs[index].Position) * inverseTexel; output.Texture = (input.Texture * Glyphs[index].Size + Glyphs[index].Position) * inverseTexel;
output.Color = input.InstanceColor.zyxw;
return output; return output;
} }
@ -44,5 +46,7 @@ VSOutput VSMain(VSInput input)
float4 PSMain(VSOutput input) : SV_TARGET float4 PSMain(VSOutput input) : SV_TARGET
{ {
float4 color = FontTexture.Sample(ClosestSampler, input.Texture); float4 color = FontTexture.Sample(ClosestSampler, input.Texture);
return float4(color.xxx, 1.0); if (color.x == 0)
discard;
return float4(input.Color.xyz, color.x);
} }

View File

@ -6,32 +6,90 @@ struct VSInput
float2 Position : POSITION0; float2 Position : POSITION0;
float2 Texture : TEXCOORD0; float2 Texture : TEXCOORD0;
// per-instance // per-instance
uint2 Size : Size; int2 Size : Size;
uint2 TopLeft : TopLeft; int2 TopLeft : TopLeft;
uint TextureIndex : TextureIndex; float4 Color : Color;
int TextureIndex : TextureIndex;
}; };
struct VSOutput struct VSOutput
{ {
float4 Position : SV_POSITION; float4 Position : SV_POSITION;
float2 Texture : NORMAL0; float2 Texture : NORMAL0;
uint TextureIndex : TextureIndex; float4 Color : Color;
int TextureIndex : TextureIndex;
}; };
[shader("vertex")] [shader("vertex")]
VSOutput VSMain(VSInput input) VSOutput VSMain(VSInput input)
{ {
float2 inversePixel = float2(1.0 / 1280.0, 1.0 / 720.0);
float2 size = abs(input.Size);
float2 texture = float2(input.Size.x < 0 ? 1.0 - input.Texture.x : input.Texture.x,
input.Size.y < 0 ? 1.0 - input.Texture.y : input.Texture.y);
VSOutput output = (VSOutput)0; VSOutput output = (VSOutput)0;
output.Position = float4(input.Position, 0, 1); float2 position = (input.Texture * size + input.TopLeft) * inversePixel;
output.Texture = input.Texture; output.Position = float4(position * 2.0 - 1.0, 0, 1);
output.Texture = texture;
output.TextureIndex = input.TextureIndex; output.TextureIndex = input.TextureIndex;
output.Color = input.Color;
return output; return output;
} }
float4 PSGradient1(VSOutput input) : SV_TARGET
{
float a = smoothstep(0.05, 0.2, input.Texture.x);
float b = 1.0 - smoothstep(0.8, 0.95, input.Texture.x);
float c = smoothstep(0.05, 0.2, input.Texture.y);
float d = a * b * c * 0.5;
float3 color = float3(1.0, 1.0, 1.0);
return float4(color, d);
}
float4 PSGradient2(VSOutput input) : SV_TARGET
{
float a = smoothstep(0.05, 0.2, input.Texture.x);
float b = 1.0 - smoothstep(0.8, 0.95, input.Texture.x);
float d = a * b * 0.5;
float3 color = float3(1.0, 1.0, 1.0);
return float4(color, d);
}
float4 PSGradient3(VSOutput input) : SV_TARGET
{
float a = smoothstep(0.02, 0.1, input.Texture.x);
float b = 1.0 - smoothstep(0.9, 0.98, input.Texture.x);
float d = a * b * 0.5;
float3 color = float3(1.0, 1.0, 1.0);
return float4(color, d);
}
[shader("pixel")] [shader("pixel")]
float4 PSMain(VSOutput input) : SV_TARGET float4 PSMain(VSOutput input) : SV_TARGET
{ {
float4 color = Texture[input.TextureIndex].Sample(ClosestSampler, input.Texture); float4 color;
return float4(color.xyz, 1.0); if (input.TextureIndex >= 0) {
color = Texture[input.TextureIndex].Sample(ClosestSampler, input.Texture);
} else if (input.TextureIndex == -1) {
color = float4(input.Color.xyz, 1.0);
} else if (input.TextureIndex == -2) {
return PSGradient1(input);
} else if (input.TextureIndex == -3) {
return PSGradient2(input);
} else if (input.TextureIndex == -4) {
return PSGradient2(input);
} else {
return float4(1, 0, 0, 1);
}
if (color.w == 0.0)
discard;
float gamma = 2.2;
color.xyz = pow(color.xyz, float3(gamma.xxx));
return float4(color.xyzw);
} }

View File

@ -12,6 +12,7 @@
#include "font/outline.h" #include "font/outline.h"
#include "font/outline_types.h" #include "font/outline_types.h"
#include "renpy/script.h"
namespace font::outline { namespace font::outline {
static const _Float16 vertexData[] = { static const _Float16 vertexData[] = {
@ -57,7 +58,7 @@ namespace font::outline {
load_vertex_index_buffer(); load_vertex_index_buffer();
load_shader(); load_shader();
create_descriptor_sets(); create_descriptor_sets();
loadedFont = load_font(uncial_antiqua[0]); loadedFont = load_font(medieval_sharp[0]);
create_glyphs_buffer(loadedFont.font, loadedFont.glyphs); create_glyphs_buffer(loadedFont.font, loadedFont.glyphs);
write_descriptor_sets(loadedFont.allocatedImage.imageView); write_descriptor_sets(loadedFont.allocatedImage.imageView);
create_instance_buffers(); create_instance_buffers();
@ -184,7 +185,14 @@ namespace font::outline {
}; };
VkPipelineColorBlendAttachmentState blendAttachment{ VkPipelineColorBlendAttachmentState blendAttachment{
.colorWriteMask = 0xF .blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = 0xF,
}; };
VkPipelineColorBlendStateCreateInfo colorBlendState{ VkPipelineColorBlendStateCreateInfo colorBlendState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
@ -603,41 +611,130 @@ namespace font::outline {
vkUnmapMemory(device, glyphsMemory); vkUnmapMemory(device, glyphsMemory);
} }
////////////////////////////////////////////////////////////////////// void font::emit_line(int frameIndex,
// draw char const * const string,
////////////////////////////////////////////////////////////////////// uint32_t x, uint32_t y,
int& outputIndex,
void font::draw(VkCommandBuffer commandBuffer, int startIndex,
uint32_t frameIndex) int endIndex,
uint32_t color)
{ {
// transfer for (int i = startIndex; i < endIndex; i++) {
const char * string = "so when Nico wants to run this game on his\n4K monitor, he gets a dinky little 1280x720\nwindow instead?"; char c = string[i];
int outputIndex = 0; types::glyph const& glyph = loadedFont.glyphs[c - 32];
int stringIndex = 0;
if (c != ' ' && c != '\n') {
instanceMappedData[maximumGlyphCount * frameIndex + outputIndex++] = {
(uint16_t)((x + glyph.metrics.horiBearingX) >> 6),
(uint16_t)((y - glyph.metrics.horiBearingY) >> 6),
(uint32_t)(c - 32),
color,
};
}
x += glyph.metrics.horiAdvance;
}
}
void font::centered(int frameIndex, char const * const string, uint32_t& x, uint32_t& y,
uint32_t minX,
uint32_t maxWidth,
int& outputIndex,
uint32_t color)
{
int stringIndex = 0;
int lineStart = stringIndex;
int lineWidth = 0;
uint32_t leftOffset = 0;
uint32_t x = 64 << 6;
uint32_t y = 64 << 6;
while (true) { while (true) {
char c = string[stringIndex++]; char c = string[stringIndex++];
if (c == 0) if (c == 0)
break; break;
if (c != ' ') { if (c == '\n') {
instanceMappedData[maximumGlyphCount * frameIndex + outputIndex++] = { continue;
(uint16_t)((x + loadedFont.glyphs[c - 32].metrics.horiBearingX) >> 6),
(uint16_t)((y - loadedFont.glyphs[c - 32].metrics.horiBearingY) >> 6),
(uint32_t)(c - 32),
0xaabbccdd,
};
} }
if (c == '\n') { types::glyph const& glyph = loadedFont.glyphs[c - 32];
if (lineWidth == 0) {
leftOffset = glyph.metrics.horiBearingX;
}
lineWidth += glyph.metrics.horiAdvance;
if ((lineWidth - leftOffset) + loadedFont.font->face_metrics.max_advance > (maxWidth << 6)) {
while (string[stringIndex] != ' ') {
char c = string[--stringIndex];
types::glyph const& glyph = loadedFont.glyphs[c - 32];
lineWidth -= glyph.metrics.horiAdvance;
}
uint32_t center = (minX + (maxWidth / 2)) << 6;
uint32_t left = center - (lineWidth / 2);
emit_line(frameIndex, string, left - leftOffset, y, outputIndex, lineStart, stringIndex, color);
while (string[stringIndex] == ' ') stringIndex++;
lineStart = stringIndex;
lineWidth = 0;
y += loadedFont.font->face_metrics.height * 1.2; y += loadedFont.font->face_metrics.height * 1.2;
x = 64 << 6; x = minX << 6;
} else {
x += loadedFont.glyphs[c - 32].metrics.horiAdvance;
} }
}; };
if (stringIndex != lineStart) {
uint32_t center = (minX + (maxWidth / 2)) << 6;
uint32_t left = center - (lineWidth / 2);
emit_line(frameIndex, string, left - leftOffset, y, outputIndex, lineStart, stringIndex, color);
y += loadedFont.font->face_metrics.height * 1.2;
x = minX << 6;
}
}
//////////////////////////////////////////////////////////////////////
// draw
//////////////////////////////////////////////////////////////////////
void font::draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
renpy::interpreter const& state)
{
// transfer
int outputIndex = 0;
if (state.menu.count == 0) {
if (state.say.stringIndex != -1u) {
char const * const string = renpy::script::strings[state.say.stringIndex];
uint32_t x = textboxLeft << 6;
uint32_t y = 590 << 6;
centered(frameIndex, string,
x, y,
textboxLeft, textboxWidth,
outputIndex,
0x000000);
}
if (state.say.characterIndex != -1u) {
const renpy::language::character & character = renpy::script::characters[state.say.characterIndex];
char const * const string = character.characterName;
uint32_t x = 580 << 6;
uint32_t y = 550 << 6;
centered(frameIndex, string,
x, y,
580, 118,
outputIndex,
character.color);
}
} else {
for (uint32_t i = 0; i < state.menu.count; i++) {
uint32_t x = 400 << 6;
uint32_t y = (100 * i + 130) << 6;
centered(frameIndex, renpy::script::options[state.menu.optionIndex + i].string,
x, y,
400, 480,
outputIndex,
0x000000);
}
}
// flush // flush
constexpr int mappedMemoryRangesCount = 1; constexpr int mappedMemoryRangesCount = 1;
VkMappedMemoryRange mappedMemoryRanges[mappedMemoryRangesCount]{ VkMappedMemoryRange mappedMemoryRanges[mappedMemoryRangesCount]{

View File

@ -22,6 +22,7 @@
#include "minecraft/vulkan.h" #include "minecraft/vulkan.h"
#include "font/outline.h" #include "font/outline.h"
#include "renpy/vulkan.h" #include "renpy/vulkan.h"
#include "renpy/interpreter.h"
#include "scenes/shadow_test/shadow_test.h" #include "scenes/shadow_test/shadow_test.h"
#include "scenes/eidelwind/eidelwind.h" #include "scenes/eidelwind/eidelwind.h"
@ -716,6 +717,14 @@ int main()
textureSamplers[2]); textureSamplers[2]);
renpy_state.init(); renpy_state.init();
//////////////////////////////////////////////////////////////////////
// interpreter
//////////////////////////////////////////////////////////////////////
renpy::interpreter interpreter_state;
interpreter_state.reset();
interpreter_state.interpret();
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// initialize view // initialize view
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -1073,8 +1082,8 @@ int main()
//minecraft_state.draw(commandBuffer, frameIndex); //minecraft_state.draw(commandBuffer, frameIndex);
renpy_state.draw(commandBuffer, frameIndex); renpy_state.draw(commandBuffer, frameIndex, interpreter_state);
font_state.draw(commandBuffer, frameIndex); font_state.draw(commandBuffer, frameIndex, interpreter_state);
vkCmdEndRendering(commandBuffer); vkCmdEndRendering(commandBuffer);

134
src/renpy/interpreter.cpp Normal file
View File

@ -0,0 +1,134 @@
#include <assert.h>
#include <stdio.h>
#include "renpy/script.h"
#include "renpy/interpreter.h"
namespace renpy {
void interpreter::reset()
{
pc = 0;
backgroundIndex = -1;
backgroundColor = 0;
shownImagesCount = 0;
say.stringIndex = -1;
say.characterIndex = -1;
menu.count = 0;
}
uint32_t interpreter::findImage(uint32_t imageIndex)
{
for (uint32_t i = 0; i < shownImagesCount; i++) {
if (shownImages[i].imageIndex == imageIndex) {
return i;
}
}
return ~0u;
}
void interpreter::showImage(uint32_t imageIndex, uint32_t transformIndex)
{
uint32_t shownImageIndex = findImage(imageIndex);
if (shownImageIndex == ~0u) {
shownImageIndex = shownImagesCount;
shownImagesCount += 1;
}
shownImages[shownImageIndex].imageIndex = imageIndex;
shownImages[shownImageIndex].transformIndex = transformIndex;
assert(shownImagesCount <= maximumShownImagesCount);
printf("shownImagesCount: %d\n", shownImagesCount);
}
void interpreter::hideImage(uint32_t imageIndex)
{
uint32_t shownImageIndex = findImage(imageIndex);
if (shownImageIndex == ~0u)
return;
for (uint32_t i = shownImageIndex; i < (shownImagesCount - 1); i++) {
shownImages[i] = shownImages[i+1];
printf("here\n");
}
printf("return\n");
shownImagesCount -= 1;
}
void interpreter::interpret_one()
{
assert(pc < (uint32_t)script::statements_length);
language::statement const& statement = script::statements[pc];
switch (statement.type) {
case language::type::play:
fprintf(stderr, "interpret_one[%d]: play\n", pc);
pc += 1;
break;
case language::type::scene_color:
fprintf(stderr, "interpret_one[%d]: scene_color\n", pc);
backgroundIndex = -1;
backgroundColor = statement.scene_color.color;
pc += 1;
break;
case language::type::scene:
fprintf(stderr, "interpret_one[%d]: scene\n", pc);
assert(statement.scene.imageIndex < (uint32_t)script::images_length);
backgroundIndex = statement.scene.imageIndex;
shownImagesCount = 0;
say.stringIndex = -1;
say.characterIndex = -1;
pc += 1;
break;
case language::type::say:
fprintf(stderr, "interpret_one[%d]: say\n", pc);
assert(statement.say.stringIndex < (uint32_t)script::strings_length);
say.stringIndex = statement.say.stringIndex;
say.characterIndex = statement.say.characterIndex;
pc += 1;
break;
case language::type::hide:
fprintf(stderr, "interpret_one[%d]: hide\n", pc);
assert(statement.show.imageIndex < (uint32_t)script::images_length);
assert(statement.show.transformIndex < transformsCount);
hideImage(statement.show.imageIndex);
pc += 1;
break;
case language::type::show:
fprintf(stderr, "interpret_one[%d]: show\n", pc);
assert(statement.show.imageIndex < (uint32_t)script::images_length);
assert(statement.show.transformIndex < transformsCount);
showImage(statement.show.imageIndex, statement.show.transformIndex);
pc += 1;
break;
case language::type::menu:
fprintf(stderr, "interpret_one[%d]: menu\n", pc);
assert(statement.menu.optionIndex < (uint32_t)script::options_length);
assert(statement.menu.count > 0);
menu.count = statement.menu.count;
menu.optionIndex = statement.menu.optionIndex;
pc += 1;
break;
default:
fprintf(stderr, "unknown statement type at pc %d\n", pc);
pc += 1;
break;
}
}
void interpreter::interpret()
{
while (true) {
uint32_t last_pc = pc;
interpret_one();
assert(pc != last_pc);
if (pc == 17) {
break;
}
//if (stop)
//break;
}
}
};

488
src/renpy/script.cpp Normal file
View File

@ -0,0 +1,488 @@
#include "renpy/language.h"
#include "renpy/script.h"
namespace renpy::script {
using namespace renpy::language;
char const * const strings[] = {
"Far over the mountains of Almystice", // 0
"Beyond the tumultuous waters of the Lilac Bay", // 1
"And across the vast fields of Alysen", // 2
"Tiny minstrels can be heard amongst the trees", // 3
"Are we almost there?", // 4
"Hmmm... Not really", // 5
"How much further have we to go?", // 6
"About two more moons", // 7
"We are still sooo far awayyy", // 8
"And it will be even further if you dont stop complaining", // 9
"Easy for you to say, all you have to carry is a little memory pipe!", // 10
"I'm tired ><", // 11
"Don't start whining now!", // 12
"You need to remember why we have come all this way", // 13
"I understand... I suppose it is for an important purpose", // 14
"We're almost out of the forest, we can take a little break once we clear the tree line", // 15
"Is that where the flora field is?", // 16
"Why yes, If I remember correctly, it should be just up ahead", // 17
"As the minstrel mice girls continue along the path, the forest opens up into a beautiful field of flowers", // 18
"Look at all the butterflies! They are all so pretty!", // 19
"This place is like a dream...", // 20
"There are so many flowers this time of year", // 21
"I told you it would be worth the journey!", // 22
"Can we stop for a bit now?", // 23
"Of course", // 24
"Ya know, Its a shame we didnt save some of those giant strawberries you found", // 25
"I told you not to eat them all!", // 26
"Yah yah", // 27
"Anyways, shall I recite a tale?", // 28
"Why dont you sing the story of Eleanor the Hero!", // 29
"Sure", // 30
"...", // 31
"Serves you right for scaring those elephant-dogs", // 32
"They were asking for it, you know", // 33
"Rawrrrr", // 34
"AHHHHHHHHHH!!!!!", // 35
"Nyanyanyanya", // 36
"Well, what do we have here? If it isn't two little meowse girls, all alone amongst the flowers", // 37
"Please don't eat us!!!", // 38
"Alice don't run, our only chance is through pleading!", // 39
"Please don't eat us, miss kitty cat!!! ><", // 40
"I'm not gonna eat you nyanyanya", // 41
"I just want to know what two little meowses are doing so very far away from home", // 42
"Also, are you minstrels?", // 43
"Y-Yes", // 44
"W-We are on a quest to Castle Alysen...", // 45
"Shh don't tell her that", // 46
"The Castle of Alysen you say?!?", // 47
"Why, that's where I am headed!", // 48
"You don't say...", // 49
"Yah, I do actually", // 50
"So... Why might you be traveling to the castle?", // 51
"I belong to the lineage of Agrepen", // 52
"And what might that mean?", // 53
"The Agrepens are a long line of felines loyal to the crown of corvidae", // 54
"Really? That must mean you are a noble?", // 55
"Well, not really...", // 56
"My father was one of the queens knights many years ago", // 57
"Ah I see", // 58
"So do you live at the castle or something?", // 59
"Well, no...", // 60
"Then why are you traveling to Castle Alysen?", // 61
"uhhh", // 62
"I DONT NEED TO BE PRESSURED BY LITTLE MICE TO SAY ANYTHING!!!!", // 63
"GOOD DAY!", // 64
"Wha...", // 65
"Phew, I was scared she was gonna follow us the whole way", // 66
"She didn't seem so bad", // 67
"Are you kidding? She's a crazy kitty!", // 68
"After their encounter with the weird cat, the mice scurry out of the flower field and into the nearby meadow", // 69
"I think this is the right way...", // 70
"Then where did the path go?", // 71
"How am I supposed to know?", // 72
"Did you hear that?!?!", // 73
"Hey there...", // 74
"I apologize", // 75
"I didn't mean to storm off like that", // 76
"Ha ha, no problem...", // 77
"So... Why are you traveling to Castle Alysen?", // 78
"Alice!!!", // 79
"If you must know, I have been summoned by the queen", // 80
"I suspect that my poor reputation amongst the locals of Eastern Nidus has come back to haunt me", // 81
"Though I know not what what she has summoned me for", // 82
"Ahhhhhh", // 83
"So then...", // 84
"Why are YOU traveling to the Castle?", // 85
"We are delivering a feather!!!", // 86
"Alice no!", // 87
"A feather that belonged to the queen herself!!!", // 88
"Why you little...", // 89
"A feather you say? One of the queens?", // 90
"How on the face of Al Mot might you have aquired such a thing?", // 91
"If it is authentic, that is...", // 92
"Since Alice cannot keep a secret, I shall tell you", // 93
"Seven moons ago, our town was attacked by three owls and a band of mice from the northern principalities", // 94
"Eventually word spread to greater nearby settlements, and so", // 95
"Messengers from the keep in Musia sent for aid from the Ravens", // 96
"Four moons ago, the request was answered", // 97
"And a small group of mice accompanied by two ravens a fox, and three squirrels set out to the northern principalities", // 98
"Anyways, long story short, we drove those barbaric rats out of their home", // 99
"They arent actual rats you know", // 100
"Obviously, but you wont catch me speaking kindly of them", // 101
"And you forgot the most important part", // 102
"Yah yah, I am getting there", // 103
"So, essentially, my brother is trained in archery, and...", // 104
"Speed it up already", // 105
"You tell it then!", // 106
"My cousin found this feather in one of the highest towers of a castle far to the north", // 107
"How do you know it belongs to the queen?", // 108
"It said so itself above the display on the wall", // 109
"Supposedly, it was in a room filled with treasures!", // 110
"That is very nice and all, but what are the two of you doing out here all alone?", // 111
"Do you expect every bird in Avia to respect your alliance with Castle Alysen?", // 112
"What do you mean?", // 113
"I mean, the two of you probably look like walking dinner to most creatures", // 114
"I could go for some dinner...", // 115
"Anyways...", // 116
"To answer your question, upon returning to the village, the feather was taken from my brother by the needle guild", // 117
"So... Yesterday, after sundown", // 118
"We stole the feather from the guild hall before vanishing into the night", // 119
"Can you imagine the look on their stupid faces, when they woke up, and not only is the feather missing", // 120
"But so are we!", // 121
"Hahahaha", // 122
"Are the two of you mad?", // 123
"I assume you are attempting to return the Queens feather?", // 124
"Yes, we intend to deliver the feather to its rightful owner", // 125
"Absolute madness!", // 126
"I will follow the two of you", // 127
"To keep you safe, that is", // 128
"Alright!", // 129
"Ha ha... Okay...", // 130
"Great! Follow me, I know a shortcut! :3", // 131
"Sounds good!", // 132
"Oh dear!", // 133
"And so the mice girls follow the noble cat further towards their destination", // 134
"Nya", // 135
"Look, your right, the castle is just up ahead!", // 136
"Wait up", // 137
"I told you I knew a shortcut!", // 138
"Most people take the long way around", // 139
"Yah because these are royal wheatfields!", // 140
"Who cares?", // 141
"Are you trying to get us killed?", // 142
"Its trespassing on royal land!", // 143
"Calm down, I have done this a million times", // 144
"That doesnt make me calm!", // 145
"How can the rolling fields of wheat not calm your spirit?", // 146
"You little mice truly are mad!", // 147
"I like the wheat!", // 148
"Shut up!", // 149
"Sounds like someone needs a nap!", // 150
"Why? because I'm not insane like you?", // 151
"Yah, your so sane, that you decided to steal from your town and then run off alone to the country of birds", // 152
"The power of friendship wont protect the two of you from becoming dinner", // 153
"And that, is why I feel obligated to accompany you!", // 154
"Hey, we have a good reason!", // 155
"And what might that be?", // 156
"My brother found the feather, not the town guild, its a matter of family pride!", // 157
"Pride has touched the chosen meouse", // 158
"Flies she towards the Castle", // 159
"But her ambition burns far too bright, and silly myice dont have any wings to myelt", // 160
"What?", // 161
"Nyanyanya", // 162
"Nyevermind", // 163
"Sing me a song little minstrels!", // 164
"Very Nyice!", // 165
"Now tell me little minstrels, what are your names?", // 166
"My name is Alice", // 167
"And my name is Eily", // 168
"What is your name?", // 169
"My name is Leona!", // 170
"And so, the odd trio walked through the wheatfields and towards the castle walls", // 171
"Upon approaching the castle walls", // 172
};
const int strings_length = (sizeof (strings)) / (sizeof (strings[0]));
const language::character characters[] = {
{ .characterName = "Alice", .color = 0x00765e }, // 0
{ .characterName = "Cat", .color = 0x590093 }, // 1
{ .characterName = "Eily", .color = 0x0b6092 }, // 2
{ .characterName = "Mouse Girls", .color = 0x000000 }, // 3
{ .characterName = "Narrator", .color = 0x000000 }, // 4
{ .characterName = "Leona", .color = 0x590093 }, // 5
};
const int characters_length = (sizeof (characters)) / (sizeof (characters[0]));
const language::audio audio[] = {
{ .path = "sfx/Chime.opus" }, // 0 sfx/Chime.ogg
{ .path = "sfx/MistAmbience.opus" }, // 1 sfx/MistAmbience.ogg
{ .path = "music/TinyForestMinstrels.opus" }, // 2 music/TinyForestMinstrels.ogg
{ .path = "n5test.opus" }, // 3 n5test.ogg
{ .path = "music/PhrygianButterflies.opus" }, // 4 music/PhrygianButterflies.ogg
{ .path = "music/Poem1.opus" }, // 5 music/Poem1.ogg
{ .path = "placeholdermeow.opus" }, // 6 placeholdermeow.mp3
{ .path = "music/ScaredMice.opus" }, // 7 music/ScaredMice.ogg
{ .path = "music/WheatFields.opus" }, // 8 music/WheatFields.ogg
};
const int audio_length = (sizeof (audio)) / (sizeof (audio[0]));
const language::image images[] = {
{ .path = "data/renpy/images/flowers.dds" }, // 0 flowers.png
{ .path = "data/renpy/images/bg/forest1.dds" }, // 1 bg/forest1.png
{ .path = "data/renpy/images/bg/forest2.dds" }, // 2 bg/forest2.png
{ .path = "data/renpy/images/bg/flowerfield1.dds" }, // 3 bg/flowerfield1.png
{ .path = "data/renpy/images/bg/wheatfield1.dds" }, // 4 bg/wheatfield1.png
{ .path = "data/renpy/images/ch/cat.dds" }, // 5 ch/cat.png
{ .path = "data/renpy/images/ch/catw.dds" }, // 6 ch/catw.png
{ .path = "data/renpy/images/ch/Eily.dds" }, // 7 ch/Eily.png
{ .path = "data/renpy/images/ch/Alice.dds" }, // 8 ch/Alice.png
};
const int images_length = (sizeof (images)) / (sizeof (images[0]));
const language::option options[] = {
{ .string = "Complain", .statementIndex = 18 }, // 0
{ .string = "Rationalize", .statementIndex = 26 }, // 1
{ .string = "Good idea", .statementIndex = 54 }, // 2
{ .string = "I am too tired", .statementIndex = 61 }, // 3
{ .string = "Beg for mercy", .statementIndex = 78 }, // 4
{ .string = "Run", .statementIndex = 80 }, // 5
};
const int options_length = (sizeof (options)) / (sizeof (options[0]));
const language::statement statements[] = {
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 0 sfx/Chime.ogg
{ .type = type::play, .play = { .audioIndex = 1, /* FIXME channel */ } }, // 1 sfx/MistAmbience.ogg
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 2 bgwhite
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 3
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 0 } }, // 4 n "Far over the mountains of Almystice"
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 1 } }, // 5 n "Beyond the tumultuous waters of the Lilac Bay"
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 2 } }, // 6 n "And across the vast fields of Alysen"
{ .type = type::play, .play = { .audioIndex = 2, /* FIXME channel */ } }, // 7 music/TinyForestMinstrels.ogg
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 3 } }, // 8 n "Tiny minstrels can be heard amongst the trees"
{ .type = type::scene, .scene = { .imageIndex = 1 } }, // 9 bgforest1
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 10
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 11 al
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 4 } }, // 12 a "Are we almost there?"
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 13 ei
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 5 } }, // 14 e "Hmmm... Not really"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 6 } }, // 15 a "How much further have we to go?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 7 } }, // 16 e "About two more moons"
{ .type = type::menu, .menu = { .count = 2, .optionIndex = 0 } }, // 17 "Complain", "Rationalize"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 8 } }, // 18 a "We are still sooo far awayyy"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 9 } }, // 19 e "And it will be even further if you dont stop complaining"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 10 } }, // 20 a "Easy for you to say, all you have to carry is a little memory pipe!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 11 } }, // 21 a "I'm tired ><"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 12 } }, // 22 e "Don't start whining now!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 13 } }, // 23 e "You need to remember why we have come all this way"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 24 a "I understand... I suppose it is for an important purpose"
{ .type = type::jump, .jump = { .statementIndex = 28 } }, // 25 internal jump (b'__menu_end', 0)
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 26 a "I understand... I suppose it is for an important purpose"
{ .type = type::jump, .jump = { .statementIndex = 28 } }, // 27 internal jump (b'__menu_end', 0)
{ .type = type::jump, .jump = { .statementIndex = 29 } }, // 28 mainbranch1
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 15 } }, // 29 e "We're almost out of the forest, we can take a little break once we clear the tree line"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 16 } }, // 30 a "Is that where the flora field is?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 17 } }, // 31 e "Why yes, If I remember correctly, it should be just up ahead"
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 32
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 33 bgwhite
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 34 sfx/Chime.ogg
{ .type = type::dissolve, .dissolve = { .duration = 1.0 } }, // 35
{ .type = type::voice, .voice = { .audioIndex = 3 } }, // 36 n5test.ogg
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 18 } }, // 37 n "As the minstrel mice girls continue along the path, the forest opens up into a beautiful field of flowers"
{ .type = type::play, .play = { .audioIndex = 4, /* FIXME channel */ } }, // 38 music/PhrygianButterflies.ogg
{ .type = type::scene, .scene = { .imageIndex = 3 } }, // 39 bgflower1
{ .type = type::dissolve, .dissolve = { .duration = 1.0 } }, // 40
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 41 ei
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 19 } }, // 42 e "Look at all the butterflies! They are all so pretty!"
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 43 al
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 20 } }, // 44 a "This place is like a dream..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 21 } }, // 45 e "There are so many flowers this time of year"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 22 } }, // 46 e "I told you it would be worth the journey!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 23 } }, // 47 a "Can we stop for a bit now?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 24 } }, // 48 e "Of course"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 25 } }, // 49 e "Ya know, Its a shame we didnt save some of those giant strawberries you found"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 26 } }, // 50 a "I told you not to eat them all!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 27 } }, // 51 e "Yah yah"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 28 } }, // 52 e "Anyways, shall I recite a tale?"
{ .type = type::menu, .menu = { .count = 2, .optionIndex = 2 } }, // 53 "Good idea", "I am too tired"
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 54
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 29 } }, // 55 a "Why dont you sing the story of Eleanor the Hero!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 30 } }, // 56 e "Sure"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 31 } }, // 57 a "..."
{ .type = type::play, .play = { .audioIndex = 5, /* FIXME channel */ } }, // 58 music/Poem1.ogg
{ .type = type::pause, .pause = { .duration = 40 } }, // 59
{ .type = type::jump, .jump = { .statementIndex = 65 } }, // 60 internal jump (b'__menu_end', 1)
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 32 } }, // 61 e "Serves you right for scaring those elephant-dogs"
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 62
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 33 } }, // 63 a "They were asking for it, you know"
{ .type = type::jump, .jump = { .statementIndex = 65 } }, // 64 internal jump (b'__menu_end', 1)
{ .type = type::jump, .jump = { .statementIndex = 66 } }, // 65 mainbranch2
{ .type = type::hide, .hide = { .imageIndex = 7 } }, // 66 ei
{ .type = type::show, .show = { .imageIndex = 6, .transformIndex = transform::right } }, // 67 catw
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 68 ei
{ .type = type::voice, .voice = { .audioIndex = 6 } }, // 69 placeholdermeow.mp3
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 34 } }, // 70 c "Rawrrrr"
{ .type = type::hide, .hide = { .imageIndex = 6 } }, // 71 catw
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 72 cat
{ .type = type::play, .play = { .audioIndex = 7, /* FIXME channel */ } }, // 73 music/ScaredMice.ogg
{ .type = type::say, .say = { .characterIndex = 3, .stringIndex = 35 } }, // 74 mg "AHHHHHHHHHH!!!!!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 36 } }, // 75 c "Nyanyanyanya"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 37 } }, // 76 c "Well, what do we have here? If it isn't two little meowse girls, all alone amongst the flowers"
{ .type = type::menu, .menu = { .count = 2, .optionIndex = 4 } }, // 77 "Beg for mercy", "Run"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 38 } }, // 78 a "Please don't eat us!!!"
{ .type = type::jump, .jump = { .statementIndex = 83 } }, // 79 internal jump (b'__menu_end', 2)
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 39 } }, // 80 e "Alice don't run, our only chance is through pleading!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 40 } }, // 81 e "Please don't eat us, miss kitty cat!!! ><"
{ .type = type::jump, .jump = { .statementIndex = 83 } }, // 82 internal jump (b'__menu_end', 2)
{ .type = type::jump, .jump = { .statementIndex = 84 } }, // 83 mainbranch3
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 84
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 41 } }, // 85 c "I'm not gonna eat you nyanyanya"
{ .type = type::play, .play = { .audioIndex = 2, /* FIXME channel */ } }, // 86 music/TinyForestMinstrels.ogg
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 42 } }, // 87 c "I just want to know what two little meowses are doing so very far away from home"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 43 } }, // 88 c "Also, are you minstrels?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 44 } }, // 89 e "Y-Yes"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 45 } }, // 90 a "W-We are on a quest to Castle Alysen..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 46 } }, // 91 e "Shh don't tell her that"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 47 } }, // 92 c "The Castle of Alysen you say?!?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 48 } }, // 93 c "Why, that's where I am headed!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 49 } }, // 94 e "You don't say..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 50 } }, // 95 c "Yah, I do actually"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 51 } }, // 96 e "So... Why might you be traveling to the castle?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 52 } }, // 97 c "I belong to the lineage of Agrepen"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 53 } }, // 98 e "And what might that mean?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 54 } }, // 99 c "The Agrepens are a long line of felines loyal to the crown of corvidae"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 55 } }, // 100 e "Really? That must mean you are a noble?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 56 } }, // 101 c "Well, not really..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 57 } }, // 102 c "My father was one of the queens knights many years ago"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 58 } }, // 103 e "Ah I see"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 59 } }, // 104 e "So do you live at the castle or something?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 60 } }, // 105 c "Well, no..."
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 61 } }, // 106 a "Then why are you traveling to Castle Alysen?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 62 } }, // 107 c "uhhh"
{ .type = type::play, .play = { .audioIndex = 1, /* FIXME channel */ } }, // 108 sfx/MistAmbience.ogg
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 109
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 63 } }, // 110 c "I DONT NEED TO BE PRESSURED BY LITTLE MICE TO SAY ANYTHING!!!!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 64 } }, // 111 c "GOOD DAY!"
{ .type = type::hide, .hide = { .imageIndex = 5 } }, // 112 cat
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 65 } }, // 113 a "Wha..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 66 } }, // 114 e "Phew, I was scared she was gonna follow us the whole way"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 67 } }, // 115 a "She didn't seem so bad"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 68 } }, // 116 e "Are you kidding? She's a crazy kitty!"
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 117 bgwhite
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 118 sfx/Chime.ogg
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 119
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 69 } }, // 120 n "After their encounter with the weird cat, the mice scurry out of the flower field and into the nearby meadow"
{ .type = type::scene, .scene = { .imageIndex = 2 } }, // 121 bgforest2
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 122
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 123 ei
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 70 } }, // 124 e "I think this is the right way..."
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 125 al
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 71 } }, // 126 a "Then where did the path go?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 72 } }, // 127 e "How am I supposed to know?"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 73 } }, // 128 a "Did you hear that?!?!"
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 129 ei
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 130 cat
{ .type = type::play, .play = { .audioIndex = 4, /* FIXME channel */ } }, // 131 music/PhrygianButterflies.ogg
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 74 } }, // 132 c "Hey there..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 75 } }, // 133 c "I apologize"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 76 } }, // 134 c "I didn't mean to storm off like that"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 77 } }, // 135 e "Ha ha, no problem..."
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 78 } }, // 136 a "So... Why are you traveling to Castle Alysen?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 79 } }, // 137 e "Alice!!!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 80 } }, // 138 c "If you must know, I have been summoned by the queen"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 81 } }, // 139 c "I suspect that my poor reputation amongst the locals of Eastern Nidus has come back to haunt me"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 82 } }, // 140 c "Though I know not what what she has summoned me for"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 83 } }, // 141 a "Ahhhhhh"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 84 } }, // 142 c "So then..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 85 } }, // 143 c "Why are YOU traveling to the Castle?"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 86 } }, // 144 a "We are delivering a feather!!!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 87 } }, // 145 e "Alice no!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 88 } }, // 146 a "A feather that belonged to the queen herself!!!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 89 } }, // 147 e "Why you little..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 90 } }, // 148 c "A feather you say? One of the queens?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 91 } }, // 149 c "How on the face of Al Mot might you have aquired such a thing?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 92 } }, // 150 c "If it is authentic, that is..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 93 } }, // 151 e "Since Alice cannot keep a secret, I shall tell you"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 94 } }, // 152 e "Seven moons ago, our town was attacked by three owls and a band of mice from the northern principalities"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 95 } }, // 153 e "Eventually word spread to greater nearby settlements, and so"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 96 } }, // 154 e "Messengers from the keep in Musia sent for aid from the Ravens"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 97 } }, // 155 e "Four moons ago, the request was answered"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 98 } }, // 156 e "And a small group of mice accompanied by two ravens a fox, and three squirrels set out to the northern principalities"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 99 } }, // 157 e "Anyways, long story short, we drove those barbaric rats out of their home"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 100 } }, // 158 a "They arent actual rats you know"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 101 } }, // 159 e "Obviously, but you wont catch me speaking kindly of them"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 102 } }, // 160 a "And you forgot the most important part"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 103 } }, // 161 e "Yah yah, I am getting there"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 104 } }, // 162 e "So, essentially, my brother is trained in archery, and..."
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 105 } }, // 163 a "Speed it up already"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 106 } }, // 164 e "You tell it then!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 107 } }, // 165 a "My cousin found this feather in one of the highest towers of a castle far to the north"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 108 } }, // 166 c "How do you know it belongs to the queen?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 109 } }, // 167 e "It said so itself above the display on the wall"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 110 } }, // 168 a "Supposedly, it was in a room filled with treasures!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 111 } }, // 169 c "That is very nice and all, but what are the two of you doing out here all alone?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 112 } }, // 170 c "Do you expect every bird in Avia to respect your alliance with Castle Alysen?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 113 } }, // 171 e "What do you mean?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 114 } }, // 172 c "I mean, the two of you probably look like walking dinner to most creatures"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 115 } }, // 173 a "I could go for some dinner..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 116 } }, // 174 e "Anyways..."
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 117 } }, // 175 e "To answer your question, upon returning to the village, the feather was taken from my brother by the needle guild"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 118 } }, // 176 e "So... Yesterday, after sundown"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 119 } }, // 177 e "We stole the feather from the guild hall before vanishing into the night"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 120 } }, // 178 e "Can you imagine the look on their stupid faces, when they woke up, and not only is the feather missing"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 121 } }, // 179 e "But so are we!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 122 } }, // 180 a "Hahahaha"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 123 } }, // 181 c "Are the two of you mad?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 124 } }, // 182 c "I assume you are attempting to return the Queens feather?"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 125 } }, // 183 a "Yes, we intend to deliver the feather to its rightful owner"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 126 } }, // 184 c "Absolute madness!"
{ .type = type::say, .say = { .characterIndex = 3, .stringIndex = 31 } }, // 185 mg "..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 127 } }, // 186 c "I will follow the two of you"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 128 } }, // 187 c "To keep you safe, that is"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 129 } }, // 188 a "Alright!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 130 } }, // 189 e "Ha ha... Okay..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 131 } }, // 190 c "Great! Follow me, I know a shortcut! :3"
{ .type = type::hide, .hide = { .imageIndex = 5 } }, // 191 cat
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 132 } }, // 192 a "Sounds good!"
{ .type = type::hide, .hide = { .imageIndex = 8 } }, // 193 al
{ .type = type::stop, .stop = { /* FIXME channel */ } }, // 194
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 133 } }, // 195 e "Oh dear!"
{ .type = type::hide, .hide = { .imageIndex = 7 } }, // 196 ei
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 197 bgwhite
{ .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 198 sfx/Chime.ogg
{ .type = type::dissolve, .dissolve = { .duration = 2.0 } }, // 199
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 134 } }, // 200 n "And so the mice girls follow the noble cat further towards their destination"
{ .type = type::scene, .scene = { .imageIndex = 4 } }, // 201 bgwheatfield1
{ .type = type::play, .play = { .audioIndex = 8, /* FIXME channel */ } }, // 202 music/WheatFields.ogg
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 203 cat
{ .type = type::dissolve, .dissolve = { .duration = 1.3 } }, // 204
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 135 } }, // 205 c "Nya"
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 206 al
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 136 } }, // 207 a "Look, your right, the castle is just up ahead!"
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 208 ei
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 137 } }, // 209 e "Wait up"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 138 } }, // 210 c "I told you I knew a shortcut!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 139 } }, // 211 c "Most people take the long way around"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 140 } }, // 212 e "Yah because these are royal wheatfields!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 141 } }, // 213 a "Who cares?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 142 } }, // 214 e "Are you trying to get us killed?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 143 } }, // 215 e "Its trespassing on royal land!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 144 } }, // 216 c "Calm down, I have done this a million times"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 145 } }, // 217 e "That doesnt make me calm!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 146 } }, // 218 c "How can the rolling fields of wheat not calm your spirit?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 147 } }, // 219 c "You little mice truly are mad!"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 148 } }, // 220 a "I like the wheat!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 149 } }, // 221 e "Shut up!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 150 } }, // 222 c "Sounds like someone needs a nap!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 151 } }, // 223 e "Why? because I'm not insane like you?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 152 } }, // 224 c "Yah, your so sane, that you decided to steal from your town and then run off alone to the country of birds"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 153 } }, // 225 c "The power of friendship wont protect the two of you from becoming dinner"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 154 } }, // 226 c "And that, is why I feel obligated to accompany you!"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 155 } }, // 227 e "Hey, we have a good reason!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 156 } }, // 228 c "And what might that be?"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 157 } }, // 229 e "My brother found the feather, not the town guild, its a matter of family pride!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 158 } }, // 230 c "Pride has touched the chosen meouse"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 159 } }, // 231 c "Flies she towards the Castle"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 160 } }, // 232 c "But her ambition burns far too bright, and silly myice dont have any wings to myelt"
{ .type = type::say, .say = { .characterIndex = 3, .stringIndex = 161 } }, // 233 mg "What?"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 162 } }, // 234 c "Nyanyanya"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 163 } }, // 235 c "Nyevermind"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 164 } }, // 236 c "Sing me a song little minstrels!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 165 } }, // 237 c "Very Nyice!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 166 } }, // 238 c "Now tell me little minstrels, what are your names?"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 167 } }, // 239 a "My name is Alice"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 168 } }, // 240 e "And my name is Eily"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 169 } }, // 241 e "What is your name?"
{ .type = type::say, .say = { .characterIndex = 5, .stringIndex = 170 } }, // 242 l "My name is Leona!"
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 243 bgwhite
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 244
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 171 } }, // 245 n "And so, the odd trio walked through the wheatfields and towards the castle walls"
{ .type = type::say, .say = { .characterIndex = 4, .stringIndex = 172 } }, // 246 n "Upon approaching the castle walls"
{ .type = type::_return }, // 247
};
const int statements_length = (sizeof (statements)) / (sizeof (statements[0]));
}

View File

@ -112,7 +112,7 @@ namespace renpy {
}, },
{ {
.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = script::images_length, .descriptorCount = (uint32_t)script::images_length,
}, },
}; };
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{ VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{
@ -138,7 +138,7 @@ namespace renpy {
{ // font image { // font image
.binding = 1, .binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = script::images_length, .descriptorCount = (uint32_t)script::images_length,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
} }
}; };
@ -393,7 +393,14 @@ namespace renpy {
}; };
VkPipelineColorBlendAttachmentState blendAttachment{ VkPipelineColorBlendAttachmentState blendAttachment{
.colorWriteMask = 0xF .blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = 0xF,
}; };
VkPipelineColorBlendStateCreateInfo colorBlendState{ VkPipelineColorBlendStateCreateInfo colorBlendState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
@ -426,7 +433,7 @@ namespace renpy {
}, },
}; };
constexpr int vertexAttributeDescriptionsCount = 5; constexpr int vertexAttributeDescriptionsCount = 6;
VkVertexInputAttributeDescription vertexAttributeDescriptions[vertexAttributeDescriptionsCount]{ VkVertexInputAttributeDescription vertexAttributeDescriptions[vertexAttributeDescriptionsCount]{
// per-vertex // per-vertex
{ // position { // position
@ -445,21 +452,27 @@ namespace renpy {
{ {
.location = 2, .location = 2,
.binding = 1, .binding = 1,
.format = VK_FORMAT_R16G16_UINT, .format = VK_FORMAT_R16G16_SINT,
.offset = 0, .offset = 0,
}, },
{ {
.location = 3, .location = 3,
.binding = 1, .binding = 1,
.format = VK_FORMAT_R16G16_UINT, .format = VK_FORMAT_R16G16_SINT,
.offset = 4, .offset = 4,
}, },
{ {
.location = 4, .location = 4,
.binding = 1, .binding = 1,
.format = VK_FORMAT_R16_UINT, .format = VK_FORMAT_R8G8B8A8_UNORM,
.offset = 8, .offset = 8,
}, },
{
.location = 5,
.binding = 1,
.format = VK_FORMAT_R16_SINT,
.offset = 12,
},
}; };
VkPipelineVertexInputStateCreateInfo vertexInputState{ VkPipelineVertexInputStateCreateInfo vertexInputState{
@ -496,16 +509,61 @@ namespace renpy {
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
void vulkan::draw(VkCommandBuffer commandBuffer, void vulkan::draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex) uint32_t frameIndex,
renpy::interpreter const& state)
{ {
int outputIndex = 0; int outputIndex = 0;
// update // update
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = { instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {1280, 720}, .size = {1280, 720},
.topLeft = {0, 0}, .topLeft = {0, 0},
.imageIndex = 3, .color = state.backgroundColor,
.imageIndex = (int16_t)state.backgroundIndex,
}; };
for (uint32_t i = 0; i < state.shownImagesCount; i++) {
renpy::top_left const& tl = renpy::transforms[state.shownImages[i].transformIndex];
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {452, 528},
.topLeft = {(int16_t)tl.left, (int16_t)tl.top},
.imageIndex = (int16_t)state.shownImages[i].imageIndex,
};
}
if (state.menu.count == 0) {
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {708, 200},
.topLeft = {286, 720 - 200},
.imageIndex = -2, // white gradient 1
};
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {244, 200},
.topLeft = {336, 720 - 200},
.imageIndex = 0, // flower
};
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {-244, 200},
.topLeft = {1280 - (336 + 244), 720 - 200},
.imageIndex = 0, // flower
};
if (state.say.characterIndex != -1u) {
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {148, 30},
.topLeft = {560, 528},
.imageIndex = -4, // white gradient 2
};
}
} else {
for (uint32_t i = 0; i < state.menu.count; i++) {
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {480, 40},
.topLeft = {400, (int16_t)(100 * i + 100)},
.imageIndex = -3, // white gradient 2
};
}
}
// flush // flush
constexpr int mappedMemoryRangesCount = 1; constexpr int mappedMemoryRangesCount = 1;
VkMappedMemoryRange mappedMemoryRanges[mappedMemoryRangesCount]{ VkMappedMemoryRange mappedMemoryRanges[mappedMemoryRangesCount]{