Compare commits

..

No commits in common. "c1558d99ee889d187d2aa818286ddbf102f5ec92" and "ac6b6138d4ffbbd925c86a948a9d833f2eac1b77" have entirely different histories.

28 changed files with 149 additions and 282 deletions

View File

@ -115,12 +115,6 @@ all: main
%.pcm: %.wav %.pcm: %.wav
ffmpeg -loglevel quiet -y -i $< -c:a pcm_s16le -ar 48000 -ac 2 -f s16le $@ ffmpeg -loglevel quiet -y -i $< -c:a pcm_s16le -ar 48000 -ac 2 -f s16le $@
%.pcm: %.ogg
ffmpeg -loglevel quiet -y -i $< -c:a pcm_s16le -ar 48000 -ac 2 -f s16le $@
%.pcm: %.mp3
ffmpeg -loglevel quiet -y -i $< -c:a pcm_s16le -ar 48000 -ac 2 -f s16le $@
%.opus.bin: %.pcm %.opus.bin: %.pcm
./tools/opus_encode $< $@ ./tools/opus_encode $< $@

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -76,7 +76,6 @@ label start:
#voice "n4test.mp3" #voice "n4test.mp3"
play TinyForestMinstrels "music/TinyForestMinstrels.ogg" play TinyForestMinstrels "music/TinyForestMinstrels.ogg"
n "Tiny minstrels can be heard amongst the trees" n "Tiny minstrels can be heard amongst the trees"
stop TinyForestMinstrels fadeout 5.5
scene bgforest1 scene bgforest1
@ -128,7 +127,7 @@ label start:
with Dissolve(1.0) with Dissolve(1.0)
#voice "n5test.ogg" voice "n5test.ogg"
n "As the minstrel mice girls continue along the path, the forest opens up into a beautiful field of flowers" n "As the minstrel mice girls continue along the path, the forest opens up into a beautiful field of flowers"
play PhrygianButterflies "music/PhrygianButterflies.ogg" play PhrygianButterflies "music/PhrygianButterflies.ogg"

View File

@ -12,11 +12,4 @@ data/renpy/images/ch/catw.dds
data/renpy/images/ch/Eily.dds data/renpy/images/ch/Eily.dds
data/renpy/images/ch/Alice.dds data/renpy/images/ch/Alice.dds
audio/sfx/Chime.opus.bin audio/PhrygianButterflies.opus.bin
audio/sfx/MistAmbience.opus.bin
audio/music/TinyForestMinstrels.opus.bin
audio/music/PhrygianButterflies.opus.bin
audio/music/Poem1.opus.bin
audio/placeholdermeow.opus.bin
audio/music/ScaredMice.opus.bin
audio/music/WheatFields.opus.bin

View File

@ -1,11 +1,7 @@
#pragma once #pragma once
#include "renpy/language.h"
namespace audio { namespace audio {
void init(); void init();
void load(renpy::language::audio const * const audio, int count); void load();
void update(); void update();
void play(int audio_index);
void stop(int audio_index, double fadeout);
} }

View File

@ -25,7 +25,6 @@ namespace renpy::language {
struct audio { struct audio {
char const * const path; char const * const path;
double loop_end;
}; };
struct image { struct image {
@ -65,11 +64,6 @@ namespace renpy::language {
uint32_t audioIndex; uint32_t audioIndex;
}; };
struct stop {
uint32_t audioIndex;
double fadeout;
};
struct _return { struct _return {
}; };
@ -98,6 +92,10 @@ namespace renpy::language {
struct with { struct with {
}; };
struct stop {
uint32_t channelIndex;
};
struct pause { struct pause {
float duration; float duration;
}; };

View File

@ -56,6 +56,7 @@ class Play:
channel: lex.Token channel: lex.Token
path: lex.Token path: lex.Token
fadeout: lex.Token fadeout: lex.Token
noloop: bool
__repr__ = lexeme_repr __repr__ = lexeme_repr
@ -262,15 +263,16 @@ def parse_play(tokens, index):
if fadeout.type != TT.NUMBER: if fadeout.type != TT.NUMBER:
raise ParseException("expected number", fadeout) raise ParseException("expected number", fadeout)
index += 2 index += 2
#noloop = False noloop = False
if token.type == TT.NOLOOP: if token.type == TT.NOLOOP:
#noloop = True noloop = True
index += 1 index += 1
play = Play( play = Play(
channel = channel, channel = channel,
path = path, path = path,
fadeout = fadeout, fadeout = fadeout,
noloop = noloop,
) )
return index, play return index, play
@ -478,19 +480,18 @@ def parse_stop(tokens, index):
if channel.type != TT.IDENTIFIER: if channel.type != TT.IDENTIFIER:
raise ParseException("expected identifier", channel) raise ParseException("expected identifier", channel)
index += 1
token = tokens[index]
fadeout = None
if token.type == TT.FADEOUT:
fadeout = tokens[index + 1] fadeout = tokens[index + 1]
if fadeout.type != TT.FADEOUT: if fadeout.type != TT.NUMBER:
raise ParseException("expected fadeout", channel) raise ParseException("expected number", fadeout)
index += 2
number = tokens[index + 2]
if number.type != TT.NUMBER:
raise ParseException("expected number", number)
index += 3
stop = Stop( stop = Stop(
channel = channel, channel = channel,
fadeout = number fadeout = fadeout
) )
return index, stop return index, stop

View File

@ -20,7 +20,6 @@ class State:
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]
channel_lookup: dict[str, int]
string_lookup: dict[str, int] string_lookup: dict[str, int]
global_identifiers: set[str] global_identifiers: set[str]
@ -52,14 +51,6 @@ simple_statement_types = {
parse.Hide, parse.Hide,
} }
loops = {
"ScaredMice": 8.0,
"PhrygianButterflies": 40.125,
"MistAmbience": 22.0,
"TinyForestMinstrels": 44.0,
"WheatFields": 34.0,
}
def pass1(state, ast): def pass1(state, ast):
if type(ast) is parse.Image: if type(ast) is parse.Image:
key = lhs_key(ast.name) key = lhs_key(ast.name)
@ -84,12 +75,7 @@ def pass1(state, ast):
key = lhs_key(ast.name) key = lhs_key(ast.name)
assert key not in state.labels_lookup assert key not in state.labels_lookup
state.labels_lookup[key] = len(state.statements) state.labels_lookup[key] = len(state.statements)
elif type(ast) is parse.Play: elif type(ast) in {parse.Play, parse.Voice}:
if ast.path.lexeme not in state.audio_lookup:
index = len(state.audio_lookup)
state.audio_lookup[ast.path.lexeme] = index
state.channel_lookup[ast.channel.lexeme] = index
elif type(ast) is parse.Voice:
if ast.path.lexeme not in state.audio_lookup: if ast.path.lexeme not in state.audio_lookup:
index = len(state.audio_lookup) index = len(state.audio_lookup)
state.audio_lookup[ast.path.lexeme] = index state.audio_lookup[ast.path.lexeme] = index
@ -149,7 +135,7 @@ 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')
audio_index = state.audio_lookup[statement.path.lexeme] audio_index = state.audio_lookup[statement.path.lexeme]
yield f"{{ .type = type::play, .play = {{ .audioIndex = {audio_index} }} }}, // {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)
if key in state.images_lookup: if key in state.images_lookup:
@ -204,10 +190,7 @@ def pass2_statement(state, pc, statement):
elif type(statement) is parse.Return: elif type(statement) is parse.Return:
yield f"{{ .type = type::_return }}, // {pc}" yield f"{{ .type = type::_return }}, // {pc}"
elif type(statement) is parse.Stop: elif type(statement) is parse.Stop:
audio_index = state.channel_lookup[statement.channel.lexeme] yield f"{{ .type = type::stop, .stop = {{ /* FIXME channel */ }} }}, // {pc}"
fadeout = statement.fadeout.lexeme
channel = statement.channel.lexeme.decode('utf-8')
yield f"{{ .type = type::stop, .stop = {{ .audioIndex = {audio_index}, .fadeout = {float(fadeout)} }} }}, // {pc} {channel}"
elif type(statement) is parse.Pause: elif type(statement) is parse.Pause:
duration = statement.duration.lexeme duration = statement.duration.lexeme
yield f"{{ .type = type::pause, .pause = {{ .duration = {duration} }} }}, // {pc}" yield f"{{ .type = type::pause, .pause = {{ .duration = {duration} }} }}, // {pc}"
@ -247,7 +230,6 @@ def pass2_characters(state):
def pass2_audio(state): def pass2_audio(state):
yield "const language::audio audio[] = {" yield "const language::audio audio[] = {"
reverse_channel = {v: k for k, v in state.channel_lookup.items()}
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
@ -257,12 +239,7 @@ def pass2_audio(state):
path = path.removesuffix(".ogg") path = path.removesuffix(".ogg")
else: else:
assert False, path assert False, path
name = audio yield f"{{ .path = \"{path}.opus\" }}, // {i} {orig_path}"
channel_name = reverse_channel[i].decode('utf-8') if i in reverse_channel else None
name = f"\"{channel_name}\"" if channel_name is not None else "nullptr"
loop = loops[channel_name] if channel_name in loops else 0
assert loop < 20_000, loop
yield f"{{ .path = \"audio/{path}.opus.bin\", .loop_end = {float(loop)} }}, // {i} {orig_path}"
yield "};" yield "};"
yield "const int audio_length = (sizeof (audio)) / (sizeof (audio[0]));" yield "const int audio_length = (sizeof (audio)) / (sizeof (audio[0]));"
@ -320,7 +297,6 @@ image _internal_flowers = "flowers.png"
characters_lookup = dict(), characters_lookup = dict(),
labels_lookup = dict(), labels_lookup = dict(),
audio_lookup = dict(), audio_lookup = dict(),
channel_lookup = dict(),
string_lookup = dict(), string_lookup = dict(),
global_identifiers = set(), global_identifiers = set(),
) )

View File

@ -9,49 +9,57 @@
#include "audio.h" #include "audio.h"
#include "new.h" #include "new.h"
#include "minmax.h" #include "minmax.h"
#include "renpy/language.h"
namespace audio { namespace audio {
static int const frame_samples = 960; // 20 milliseconds @ 48kHz int const frame_samples = 960; // 20 milliseconds @ 48kHz
static int const sample_rate = 48000; int const sample_rate = 48000;
static int const channels = 2; int const channels = 2;
static int const sample_size = (sizeof (int16_t)); int const sample_size = (sizeof (int16_t));
static int const max_frame_size = 960 * 3; // 20ms at 48kHz int const max_frame_size = 960 * 3; // 20ms at 48kHz
static int const max_packet_size = 1275; int const max_packet_size = 1275;
static int const half_period_samples = sample_rate / 2;
static int const half_period_size = half_period_samples * sample_size * channels;
// //
static SDL_AudioStream * audio_stream; SDL_AudioStream * audio_stream;
static SDL_AudioSpec audio_spec; SDL_AudioSpec audio_spec;
static OpusDecoder * opus_decoder; OpusDecoder * opus_decoder;
struct AudioFile {
char const * const path;
uint32_t loop_end;
};
struct AudioBuffer { struct AudioBuffer {
renpy::language::audio const * audio; AudioFile * audio_file;
int16_t * buf; int16_t * buf;
uint32_t sample_count; uint32_t sample_count;
}; };
struct AudioInstance { struct AudioInstance {
int audio_index;
AudioBuffer * audio_buffer; AudioBuffer * audio_buffer;
uint32_t sample_index; uint32_t sample_index;
uint32_t tail_index; uint32_t tail_index;
uint32_t fadeout_end;
uint32_t fadeout_index;
}; };
static AudioBuffer * audio_buffers; consteval uint32_t time_to_samples(double m, double s)
static int audio_buffers_count; {
return (m * 60.0 + s) * sample_rate;
}
constexpr int max_audio_instances = 128; AudioFile audio_files[] = {
static AudioInstance audio_instances[max_audio_instances]; {
static int audio_instances_count; .path = "audio/PhrygianButterflies.opus.bin",
.loop_end = time_to_samples(0, 40.125),
},
};
constexpr int audio_files_count = (sizeof (audio_files)) / (sizeof (audio_files[0]));
AudioBuffer audio_buffers[audio_files_count];
constexpr int max_audio_instances = 16;
AudioInstance audio_instances[max_audio_instances];
void init() void init()
{ {
@ -68,8 +76,6 @@ namespace audio {
fprintf(stderr, "opus_decoder_create: %s\n", opus_strerror(err)); fprintf(stderr, "opus_decoder_create: %s\n", opus_strerror(err));
assert(!"opus_decoder_create"); assert(!"opus_decoder_create");
} }
audio_instances_count = 0;
} }
void decode(char const * const filename, AudioBuffer * audio_buffer) void decode(char const * const filename, AudioBuffer * audio_buffer)
@ -125,149 +131,60 @@ namespace audio {
assert(audio_buffer->sample_count / 2); assert(audio_buffer->sample_count / 2);
} }
void load(renpy::language::audio const * const audio, int count) void load()
{ {
audio_buffers = NewM<AudioBuffer>(count); for (int i = 0; i < audio_files_count; i++) {
audio_buffers_count = count; audio_buffers[i].audio_file = &audio_files[i];
for (int i = 0; i < count; i++) { decode(audio_files[i].path, &audio_buffers[i]);
audio_buffers[i].audio = &audio[i]; audio_instances[i].audio_buffer = &audio_buffers[i];
decode(audio[i].path, &audio_buffers[i]); audio_instances[i].sample_index = 0;
audio_instances[i].tail_index = audio_buffers[i].sample_count;
} }
} }
void play(int audio_index) inline static int min(int a, int b)
{ {
assert(audio_index >= 0 && audio_index < audio_buffers_count); return (a < b) ? a : b;
assert(audio_instances_count < max_audio_instances);
AudioInstance & instance = audio_instances[audio_instances_count++];
instance.audio_index = (int)audio_index;
instance.audio_buffer = &audio_buffers[audio_index];
instance.sample_index = 0;
instance.tail_index = audio_buffers[audio_index].sample_count;
instance.fadeout_end = 0;
instance.fadeout_index = 0;
}
void stop(int audio_index, double fadeout)
{
assert(audio_index >= 0 && audio_index < audio_buffers_count);
for (int i = 0; i < audio_instances_count; i++) {
if (audio_instances[i].audio_index == audio_index) {
if (audio_instances[i].fadeout_end == 0) {
fprintf(stderr, "audio: stop instance %d index %d\n", i, audio_index);
audio_instances[i].fadeout_end = fadeout * (double)sample_rate;
audio_instances[i].fadeout_index = 0;
} else {
fprintf(stderr, "audio: duplicate stop on instance %d index %d\n", i, audio_index);
}
}
}
}
static inline void saturation_add(int16_t * mix_buffer, int32_t value)
{
int32_t mix_value = *mix_buffer;
mix_value += value;
if (mix_value > 32767)
mix_value = 32767;
if (mix_value < -32768)
mix_value = -32768;
*mix_buffer = mix_value;
}
static inline void remove_instance(int instance_index)
{
fprintf(stderr, "removed instance %d index %d\n", instance_index, audio_instances[instance_index].audio_index);
for (int i = instance_index; i < (audio_instances_count - 1); i++) {
audio_instances[i] = audio_instances[i + 1];
}
audio_instances_count -= 1;
}
static inline void update_instance(int16_t * mix_buffer, AudioInstance & instance)
{
int16_t const * const buf = instance.audio_buffer->buf;
uint32_t const sample_count = instance.audio_buffer->sample_count;
uint32_t const loop_end = instance.audio_buffer->audio->loop_end * (double)sample_rate;
uint32_t mix_index = 0;
for (int i = 0; i < half_period_samples; i++) {
if (loop_end != 0) {
if (instance.sample_index >= loop_end) {
instance.sample_index = 0;
instance.tail_index = loop_end;
}
} else if (instance.sample_index >= sample_count) {
return;
}
if (instance.fadeout_end != 0 && instance.fadeout_index >= instance.fadeout_end) {
return;
}
assert(instance.sample_index < sample_count);
assert(instance.tail_index <= sample_count);
double fadeout = 1.0;
if (instance.fadeout_end != 0) {
fadeout = 1.0 - ((double)instance.fadeout_index / (double)instance.fadeout_end);
}
for (int ch = 0; ch < channels; ch++) {
int32_t value = buf[instance.sample_index * channels + ch];
if (instance.tail_index != sample_count) {
value += buf[instance.tail_index * channels + ch];
}
saturation_add(&mix_buffer[mix_index * channels + ch], (double)value * fadeout);
}
instance.sample_index += 1;
instance.fadeout_index += 1;
if (instance.tail_index != sample_count) {
instance.tail_index += 1;
}
mix_index += 1;
}
}
static inline bool should_cull_instance(AudioInstance & instance)
{
if (instance.audio_buffer->audio->loop_end != 0.0 && instance.sample_index >= instance.audio_buffer->sample_count) {
return true;
}
if (instance.fadeout_end != 0 && instance.fadeout_index >= instance.fadeout_end) {
return true;
}
return false;
} }
void update() void update()
{ {
int half_period_samples = audio_spec.freq / 2;
int half_period_size = half_period_samples * sample_size * audio_spec.channels;
if (SDL_GetAudioStreamQueued(audio_stream) >= half_period_size) if (SDL_GetAudioStreamQueued(audio_stream) >= half_period_size)
return; return;
int16_t mix_buffer[half_period_samples * channels]; int16_t mix_buffer[half_period_samples * channels];
memset(mix_buffer, 0, (sizeof (mix_buffer))); memset(mix_buffer, 0, (sizeof (mix_buffer)));
for (int i = 0; i < audio_instances_count; i++) { AudioInstance & instance = audio_instances[0];
update_instance(mix_buffer, audio_instances[i]); int16_t const * const buf = instance.audio_buffer->buf;
uint32_t const sample_count = instance.audio_buffer->sample_count;
uint32_t const loop_end = instance.audio_buffer->audio_file->loop_end;
uint32_t mix_index = 0;
for (int i = 0; i < half_period_samples; i++) {
if (instance.sample_index >= loop_end) {
instance.sample_index = 0;
instance.tail_index = loop_end;
fprintf(stderr, "loop\n");
} }
bool culled = true; assert(instance.sample_index < sample_count);
while (culled) { assert(instance.tail_index <= sample_count);
culled = false;
for (int i = 0; i < audio_instances_count; i++) { for (int ch = 0; ch < channels; ch++) {
if (should_cull_instance(audio_instances[i])) { mix_buffer[mix_index * channels + ch] += buf[instance.sample_index * channels + ch];
culled = true; if (instance.tail_index != sample_count) {
remove_instance(i); mix_buffer[mix_index * channels + ch] += buf[instance.tail_index * channels + ch];
break;
} }
} }
instance.sample_index += 1;
if (instance.tail_index != sample_count) {
instance.tail_index += 1;
}
mix_index += 1;
} }
SDL_PutAudioStreamData(audio_stream, (void *)mix_buffer, half_period_size); SDL_PutAudioStreamData(audio_stream, (void *)mix_buffer, half_period_size);

View File

@ -24,7 +24,6 @@
#include "renpy/vulkan.h" #include "renpy/vulkan.h"
#include "renpy/interpreter.h" #include "renpy/interpreter.h"
#include "renpy/interact.h" #include "renpy/interact.h"
#include "renpy/script.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"
@ -773,7 +772,7 @@ int main()
//collada_state.update(0); //collada_state.update(0);
audio::init(); audio::init();
audio::load(renpy::script::audio, renpy::script::audio_length); audio::load();
while (quit == false) { while (quit == false) {
audio::update(); audio::update();

View File

@ -4,8 +4,6 @@
#include "renpy/script.h" #include "renpy/script.h"
#include "renpy/interpreter.h" #include "renpy/interpreter.h"
#include "audio.h"
namespace renpy { namespace renpy {
void interpreter::reset() void interpreter::reset()
{ {
@ -66,12 +64,7 @@ namespace renpy {
switch (statement.type) { switch (statement.type) {
case language::type::play: case language::type::play:
fprintf(stderr, "interpret_one[%d]: play %d\n", pc, statement.play.audioIndex); fprintf(stderr, "interpret_one[%d]: play\n", pc);
audio::play(statement.play.audioIndex);
pc += 1;
break;
case language::type::stop:
audio::stop(statement.stop.audioIndex, statement.stop.fadeout);
pc += 1; pc += 1;
break; break;
case language::type::scene_color: case language::type::scene_color:

View File

@ -194,14 +194,15 @@ const language::character characters[] = {
const int characters_length = (sizeof (characters)) / (sizeof (characters[0])); const int characters_length = (sizeof (characters)) / (sizeof (characters[0]));
const language::audio audio[] = { const language::audio audio[] = {
{ .path = "audio/sfx/Chime.opus.bin", .loop_end = 0.0 }, // 0 sfx/Chime.ogg { .path = "sfx/Chime.opus" }, // 0 sfx/Chime.ogg
{ .path = "audio/sfx/MistAmbience.opus.bin", .loop_end = 22.0 }, // 1 sfx/MistAmbience.ogg { .path = "sfx/MistAmbience.opus" }, // 1 sfx/MistAmbience.ogg
{ .path = "audio/music/TinyForestMinstrels.opus.bin", .loop_end = 44.0 }, // 2 music/TinyForestMinstrels.ogg { .path = "music/TinyForestMinstrels.opus" }, // 2 music/TinyForestMinstrels.ogg
{ .path = "audio/music/PhrygianButterflies.opus.bin", .loop_end = 40.125 }, // 3 music/PhrygianButterflies.ogg { .path = "n5test.opus" }, // 3 n5test.ogg
{ .path = "audio/music/Poem1.opus.bin", .loop_end = 0.0 }, // 4 music/Poem1.ogg { .path = "music/PhrygianButterflies.opus" }, // 4 music/PhrygianButterflies.ogg
{ .path = "audio/placeholdermeow.opus.bin", .loop_end = 0.0 }, // 5 placeholdermeow.mp3 { .path = "music/Poem1.opus" }, // 5 music/Poem1.ogg
{ .path = "audio/music/ScaredMice.opus.bin", .loop_end = 8.0 }, // 6 music/ScaredMice.ogg { .path = "placeholdermeow.opus" }, // 6 placeholdermeow.mp3
{ .path = "audio/music/WheatFields.opus.bin", .loop_end = 34.0 }, // 7 music/WheatFields.ogg { .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 int audio_length = (sizeof (audio)) / (sizeof (audio[0]));
@ -221,8 +222,8 @@ const language::image images[] = {
const int images_length = (sizeof (images)) / (sizeof (images[0])); const int images_length = (sizeof (images)) / (sizeof (images[0]));
const language::option options[] = { const language::option options[] = {
{ .string = "Complain", .statementIndex = 19 }, // 0 { .string = "Complain", .statementIndex = 18 }, // 0
{ .string = "Rationalize", .statementIndex = 27 }, // 1 { .string = "Rationalize", .statementIndex = 26 }, // 1
{ .string = "Good idea", .statementIndex = 54 }, // 2 { .string = "Good idea", .statementIndex = 54 }, // 2
{ .string = "I am too tired", .statementIndex = 61 }, // 3 { .string = "I am too tired", .statementIndex = 61 }, // 3
{ .string = "Beg for mercy", .statementIndex = 78 }, // 4 { .string = "Beg for mercy", .statementIndex = 78 }, // 4
@ -232,45 +233,45 @@ const language::option options[] = {
const int options_length = (sizeof (options)) / (sizeof (options[0])); const int options_length = (sizeof (options)) / (sizeof (options[0]));
const language::statement statements[] = { const language::statement statements[] = {
{ .type = type::play, .play = { .audioIndex = 0 } }, // 0 sfx/Chime.ogg { .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 0 sfx/Chime.ogg
{ .type = type::play, .play = { .audioIndex = 1 } }, // 1 sfx/MistAmbience.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::scene_color, .scene_color = { .color = 0xffffff } }, // 2 bgwhite
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 3 { .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 = 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 = 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::say, .say = { .characterIndex = 4, .stringIndex = 2 } }, // 6 n "And across the vast fields of Alysen"
{ .type = type::play, .play = { .audioIndex = 2 } }, // 7 music/TinyForestMinstrels.ogg { .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::say, .say = { .characterIndex = 4, .stringIndex = 3 } }, // 8 n "Tiny minstrels can be heard amongst the trees"
{ .type = type::stop, .stop = { .audioIndex = 2, .fadeout = 5.5 } }, // 9 TinyForestMinstrels { .type = type::scene, .scene = { .imageIndex = 1 } }, // 9 bgforest1
{ .type = type::scene, .scene = { .imageIndex = 1 } }, // 10 bgforest1 { .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 10
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 11 { .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 11 al
{ .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 12 al { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 4 } }, // 12 a "Are we almost there?"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 4 } }, // 13 a "Are we almost there?" { .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 13 ei
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 14 ei { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 5 } }, // 14 e "Hmmm... Not really"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 5 } }, // 15 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 = 0, .stringIndex = 6 } }, // 16 a "How much further have we to go?" { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 7 } }, // 16 e "About two more moons"
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 7 } }, // 17 e "About two more moons" { .type = type::menu, .menu = { .count = 2, .optionIndex = 0 } }, // 17 "Complain", "Rationalize"
{ .type = type::menu, .menu = { .count = 2, .optionIndex = 0 } }, // 18 "Complain", "Rationalize" { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 8 } }, // 18 a "We are still sooo far awayyy"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 8 } }, // 19 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 = 2, .stringIndex = 9 } }, // 20 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 = 10 } }, // 21 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 = 0, .stringIndex = 11 } }, // 22 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 = 12 } }, // 23 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 = 2, .stringIndex = 13 } }, // 24 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::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 25 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::jump, .jump = { .statementIndex = 29 } }, // 26 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::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 27 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 internal jump (b'__menu_end', 0) { .type = type::jump, .jump = { .statementIndex = 29 } }, // 28 mainbranch1
{ .type = type::jump, .jump = { .statementIndex = 30 } }, // 29 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 = 2, .stringIndex = 15 } }, // 30 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 = 0, .stringIndex = 16 } }, // 31 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::say, .say = { .characterIndex = 2, .stringIndex = 17 } }, // 32 e "Why yes, If I remember correctly, it should be just up ahead" { .type = type::stop, .stop = { /* FIXME channel */ } }, // 32
{ .type = type::stop, .stop = { .audioIndex = 2, .fadeout = 5.5 } }, // 33 TinyForestMinstrels { .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 33 bgwhite
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 34 bgwhite { .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 34 sfx/Chime.ogg
{ .type = type::play, .play = { .audioIndex = 0 } }, // 35 sfx/Chime.ogg { .type = type::dissolve, .dissolve = { .duration = 1.0 } }, // 35
{ .type = type::dissolve, .dissolve = { .duration = 1.0 } }, // 36 { .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::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 = 3 } }, // 38 music/PhrygianButterflies.ogg { .type = type::play, .play = { .audioIndex = 4, /* FIXME channel */ } }, // 38 music/PhrygianButterflies.ogg
{ .type = type::scene, .scene = { .imageIndex = 3 } }, // 39 bgflower1 { .type = type::scene, .scene = { .imageIndex = 3 } }, // 39 bgflower1
{ .type = type::dissolve, .dissolve = { .duration = 1.0 } }, // 40 { .type = type::dissolve, .dissolve = { .duration = 1.0 } }, // 40
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 41 ei { .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 41 ei
@ -286,26 +287,26 @@ const language::statement statements[] = {
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 27 } }, // 51 e "Yah yah" { .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::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::menu, .menu = { .count = 2, .optionIndex = 2 } }, // 53 "Good idea", "I am too tired"
{ .type = type::stop, .stop = { .audioIndex = 3, .fadeout = 4.2 } }, // 54 PhrygianButterflies { .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 = 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 = 2, .stringIndex = 30 } }, // 56 e "Sure"
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 31 } }, // 57 a "..." { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 31 } }, // 57 a "..."
{ .type = type::play, .play = { .audioIndex = 4 } }, // 58 music/Poem1.ogg { .type = type::play, .play = { .audioIndex = 5, /* FIXME channel */ } }, // 58 music/Poem1.ogg
{ .type = type::pause, .pause = { .duration = 40 } }, // 59 { .type = type::pause, .pause = { .duration = 40 } }, // 59
{ .type = type::jump, .jump = { .statementIndex = 65 } }, // 60 internal jump (b'__menu_end', 1) { .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::say, .say = { .characterIndex = 2, .stringIndex = 32 } }, // 61 e "Serves you right for scaring those elephant-dogs"
{ .type = type::stop, .stop = { .audioIndex = 3, .fadeout = 4.2 } }, // 62 PhrygianButterflies { .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::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 = 65 } }, // 64 internal jump (b'__menu_end', 1)
{ .type = type::jump, .jump = { .statementIndex = 66 } }, // 65 mainbranch2 { .type = type::jump, .jump = { .statementIndex = 66 } }, // 65 mainbranch2
{ .type = type::hide, .hide = { .imageIndex = 7 } }, // 66 ei { .type = type::hide, .hide = { .imageIndex = 7 } }, // 66 ei
{ .type = type::show, .show = { .imageIndex = 6, .transformIndex = transform::right } }, // 67 catw { .type = type::show, .show = { .imageIndex = 6, .transformIndex = transform::right } }, // 67 catw
{ .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 68 ei { .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 68 ei
{ .type = type::voice, .voice = { .audioIndex = 5 } }, // 69 placeholdermeow.mp3 { .type = type::voice, .voice = { .audioIndex = 6 } }, // 69 placeholdermeow.mp3
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 34 } }, // 70 c "Rawrrrr" { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 34 } }, // 70 c "Rawrrrr"
{ .type = type::hide, .hide = { .imageIndex = 6 } }, // 71 catw { .type = type::hide, .hide = { .imageIndex = 6 } }, // 71 catw
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 72 cat { .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 72 cat
{ .type = type::play, .play = { .audioIndex = 6 } }, // 73 music/ScaredMice.ogg { .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 = 3, .stringIndex = 35 } }, // 74 mg "AHHHHHHHHHH!!!!!"
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 36 } }, // 75 c "Nyanyanyanya" { .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::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"
@ -316,9 +317,9 @@ const language::statement statements[] = {
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 40 } }, // 81 e "Please don't eat us, miss kitty cat!!! ><" { .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 = 83 } }, // 82 internal jump (b'__menu_end', 2)
{ .type = type::jump, .jump = { .statementIndex = 84 } }, // 83 mainbranch3 { .type = type::jump, .jump = { .statementIndex = 84 } }, // 83 mainbranch3
{ .type = type::stop, .stop = { .audioIndex = 6, .fadeout = 2.0 } }, // 84 ScaredMice { .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::say, .say = { .characterIndex = 1, .stringIndex = 41 } }, // 85 c "I'm not gonna eat you nyanyanya"
{ .type = type::play, .play = { .audioIndex = 2 } }, // 86 music/TinyForestMinstrels.ogg { .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 = 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 = 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 = 2, .stringIndex = 44 } }, // 89 e "Y-Yes"
@ -340,8 +341,8 @@ const language::statement statements[] = {
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 60 } }, // 105 c "Well, no..." { .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 = 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::say, .say = { .characterIndex = 1, .stringIndex = 62 } }, // 107 c "uhhh"
{ .type = type::play, .play = { .audioIndex = 1 } }, // 108 sfx/MistAmbience.ogg { .type = type::play, .play = { .audioIndex = 1, /* FIXME channel */ } }, // 108 sfx/MistAmbience.ogg
{ .type = type::stop, .stop = { .audioIndex = 2, .fadeout = 2.0 } }, // 109 TinyForestMinstrels { .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 = 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::say, .say = { .characterIndex = 1, .stringIndex = 64 } }, // 111 c "GOOD DAY!"
{ .type = type::hide, .hide = { .imageIndex = 5 } }, // 112 cat { .type = type::hide, .hide = { .imageIndex = 5 } }, // 112 cat
@ -350,7 +351,7 @@ const language::statement statements[] = {
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 67 } }, // 115 a "She didn't seem so bad" { .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::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::scene_color, .scene_color = { .color = 0xffffff } }, // 117 bgwhite
{ .type = type::play, .play = { .audioIndex = 0 } }, // 118 sfx/Chime.ogg { .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 118 sfx/Chime.ogg
{ .type = type::dissolve, .dissolve = { .duration = 3.0 } }, // 119 { .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::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::scene, .scene = { .imageIndex = 2 } }, // 121 bgforest2
@ -363,7 +364,7 @@ const language::statement statements[] = {
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 73 } }, // 128 a "Did you hear that?!?!" { .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 = 7, .transformIndex = transform::centerleft } }, // 129 ei
{ .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 130 cat { .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 130 cat
{ .type = type::play, .play = { .audioIndex = 3 } }, // 131 music/PhrygianButterflies.ogg { .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 = 74 } }, // 132 c "Hey there..."
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 75 } }, // 133 c "I apologize" { .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 = 1, .stringIndex = 76 } }, // 134 c "I didn't mean to storm off like that"
@ -426,15 +427,15 @@ const language::statement statements[] = {
{ .type = type::hide, .hide = { .imageIndex = 5 } }, // 191 cat { .type = type::hide, .hide = { .imageIndex = 5 } }, // 191 cat
{ .type = type::say, .say = { .characterIndex = 0, .stringIndex = 132 } }, // 192 a "Sounds good!" { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 132 } }, // 192 a "Sounds good!"
{ .type = type::hide, .hide = { .imageIndex = 8 } }, // 193 al { .type = type::hide, .hide = { .imageIndex = 8 } }, // 193 al
{ .type = type::stop, .stop = { .audioIndex = 3, .fadeout = 2.0 } }, // 194 PhrygianButterflies { .type = type::stop, .stop = { /* FIXME channel */ } }, // 194
{ .type = type::say, .say = { .characterIndex = 2, .stringIndex = 133 } }, // 195 e "Oh dear!" { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 133 } }, // 195 e "Oh dear!"
{ .type = type::hide, .hide = { .imageIndex = 7 } }, // 196 ei { .type = type::hide, .hide = { .imageIndex = 7 } }, // 196 ei
{ .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 197 bgwhite { .type = type::scene_color, .scene_color = { .color = 0xffffff } }, // 197 bgwhite
{ .type = type::play, .play = { .audioIndex = 0 } }, // 198 sfx/Chime.ogg { .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 198 sfx/Chime.ogg
{ .type = type::dissolve, .dissolve = { .duration = 2.0 } }, // 199 { .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::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::scene, .scene = { .imageIndex = 4 } }, // 201 bgwheatfield1
{ .type = type::play, .play = { .audioIndex = 7 } }, // 202 music/WheatFields.ogg { .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::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 203 cat
{ .type = type::dissolve, .dissolve = { .duration = 1.3 } }, // 204 { .type = type::dissolve, .dissolve = { .duration = 1.3 } }, // 204
{ .type = type::say, .say = { .characterIndex = 1, .stringIndex = 135 } }, // 205 c "Nya" { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 135 } }, // 205 c "Nya"