renpy: pause and dissolve

This commit is contained in:
Zack Buhman 2026-05-28 19:37:13 -05:00
parent 4ead8403f3
commit bb10732ce1
20 changed files with 1230 additions and 311 deletions

View File

@ -70,6 +70,7 @@ OBJS = \
src/tga/tga.o \
src/font/outline.o \
src/renpy/vulkan.o \
src/renpy/composite/vulkan.o \
src/renpy/script.o \
src/renpy/interpreter.o \
src/renpy/interact.o \
@ -130,8 +131,8 @@ all: main
main: $(OBJS) $(LIBS)
$(CC) $(ARCH) $(LDFLAGS) $(FLAGS) $(OPT) $(DEBUG) $^ -o $@
#%.spv: %.hlsl
# ../dxc/bin/dxc -spirv -T lib_6_3 -fspv-target-env=vulkan1.3 $< -Fo $@
%.spv: %.hlsl
../dxc/bin/dxc -spirv -T lib_6_3 -fspv-target-env=vulkan1.3 $< -Fo $@
tool/pack_file: tool/pack_file.cpp
make -C tool pack_file
@ -141,6 +142,9 @@ tool/pack_file: tool/pack_file.cpp
src/pack.o: files.pack.zlib
#src/renpy/script.cpp: data/renpy/script.rpy
# PYTHONPATH=renpy-parser python -m transform $< > $@
PACK_FILENAMES = $(shell cat filenames.txt)
files.pack: tool/pack_file $(PACK_FILENAMES) filenames.txt
./tool/pack_file $@ $(PACK_FILENAMES)

View File

@ -3,6 +3,7 @@
# Declare characters used by this game. The color argument colorizes the
# name of the character.
image bgwhite = "#ffffff"
image bgblack = "#000000"
image bgforest1 = "bg/forest1.png"
image bgforest2 = "bg/forest2.png"
image bgflower1 = "bg/flowerfield1.png"
@ -62,6 +63,8 @@ label start:
#play Chime "sfx/Chime.ogg" fadeout 1.0
play sound "sfx/Chime.ogg"
play MistAmbience "sfx/MistAmbience.ogg"
scene bgblack
pause 0.0
scene bgwhite
with Dissolve(3.0)
#play music "IntroMusicTest.ogg"

View File

@ -2,6 +2,7 @@ shader/font.spv
data/font/outline/medieval_sharp_24.data
shader/renpy.spv
shader/renpy_composite.spv
data/renpy/images/flowers.dds
data/renpy/images/bg/forest1.dds
data/renpy/images/bg/forest2.dds

View File

@ -110,15 +110,15 @@ namespace font::outline {
int& outputIndex,
int startIndex,
int endIndex,
uint32_t color);
uint32_t color) const;
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);
uint32_t color) const;
void draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
renpy::interpreter const& state);
renpy::interpreter const& state) const;
LoadedFont load_font(font_desc const& desc);
};

View File

@ -0,0 +1,52 @@
#pragma once
namespace renpy::composite {
struct vulkan {
static constexpr int perVertexSize = (4) * 2;
// externally initialized, opaque handle
VkInstance instance;
VkDevice device;
VkQueue queue;
VkCommandPool commandPool;
// externally initialized, structures
VkPhysicalDeviceProperties physicalDeviceProperties;
VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties;
// externally initialized, enum
VkFormat colorFormat;
VkFormat depthFormat;
VkSampler linearSampler;
// internal vulkan state
VkPipelineLayout pipelineLayout;
VkShaderModule shaderModule;
VkPipeline pipeline;
VertexIndex vertexIndex;
VkDescriptorPool descriptorPool{ VK_NULL_HANDLE };
static constexpr int descriptorSetLayoutCount = 1;
VkDescriptorSetLayout descriptorSetLayouts[descriptorSetLayoutCount]; // unrelated to maxFrames, unrelated to descriptorCount
VkDescriptorSet descriptorSet0;
void initial_state(VkInstance instance,
VkDevice device,
VkQueue queue,
VkCommandPool commandPool,
VkPhysicalDeviceProperties physicalDeviceProperties,
VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties,
VkFormat colorFormat,
VkFormat depthFormat,
VkSampler linearSampler);
void init();
void load_vertex_index_buffer();
void load_shader();
void create_descriptor_sets();
void write_descriptor_sets(VkImageView * imageViews, int imageViewCount);
void create_pipeline();
void draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
float dissolveLerp,
float textLerp);
};
}

View File

@ -3,6 +3,7 @@
#include <stdint.h>
#include "renpy/language.h"
#include "renpy/script.h"
namespace renpy {
struct top_left {
@ -40,7 +41,15 @@ namespace renpy {
uint32_t count;
uint32_t optionIndex;
} menu;
bool interactionWait;
uint32_t dissolveIndex;
struct {
bool voice;
bool menu;
bool dissolve;
bool pause;
} pause;
double dissolveDuration;
double pauseDuration;
uint32_t findImage(uint32_t imageIndex);
void showImage(uint32_t imageIndex, uint32_t transformIndex);
@ -48,5 +57,15 @@ namespace renpy {
void reset();
void interpret_one();
void interpret();
inline bool interactionWait()
{
return pause.voice || pause.menu || pause.dissolve || pause.pause;
}
inline bool dissolvePC()
{
return dissolveIndex < (uint32_t)script::dissolves_length && script::dissolves[dissolveIndex].first_statement == pc;
}
};
}

View File

@ -48,7 +48,6 @@ namespace renpy::language {
stop,
pause,
hide,
dissolve,
};
struct jump {
@ -107,7 +106,9 @@ namespace renpy::language {
};
struct dissolve {
float duration;
double duration;
uint32_t first_statement;
uint32_t count;
};
struct statement {
@ -124,7 +125,6 @@ namespace renpy::language {
renpy::language::stop stop;
renpy::language::pause pause;
renpy::language::hide hide;
renpy::language::dissolve dissolve;
};
};
}

View File

@ -16,6 +16,9 @@ namespace renpy::script {
extern const language::option options[];
extern const int options_length;
extern const language::dissolve dissolves[];
extern const int dissolves_length;
extern const language::statement statements[];
extern const int statements_length;

View File

@ -80,11 +80,21 @@ namespace renpy {
void load_images();
void create_pipeline();
void create_instance_buffers();
void draw_menu_frame(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
renpy::interpreter const& state,
int & outputIndex,
int mx, int my) const;
void draw_say_frame(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
renpy::interpreter const& state,
int & outputIndex) const;
void draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
renpy::interpreter const& state,
int mx,
int my);
int mx, int my,
bool drawText) const;
};
}

View File

@ -14,6 +14,7 @@ class State:
statements: list
menus: list
entries: dict
dissolves: list
images_lookup: dict[str, int] # identifier to image index
colors_lookup: dict[str, int] # identifier to image index
@ -35,6 +36,12 @@ class InternalMenu:
class InternalJump:
target: tuple
@dataclass
class InternalDissolve:
duration: int
first_statement: int
count: int
def lhs_key(lhs):
return tuple(l.lexeme for l in lhs)
@ -46,7 +53,6 @@ simple_statement_types = {
parse.Scene,
parse.Show,
parse.Voice,
parse.With,
parse.Stop,
parse.Pause,
parse.Hide,
@ -123,6 +129,34 @@ def pass1(state, ast):
state.statements.append(InternalJump(menu_end_key))
assert menu_end_key not in state.labels_lookup
state.labels_lookup[menu_end_key] = len(state.statements)
elif type(ast) is parse.With:
if ast.function_call.name.lexeme != b'Dissolve':
assert False, ast
duration, = ast.function_call.args
duration = duration.lexeme
scene_index = None
for i in reversed(range(len(state.statements))):
if type(state.statements[i]) is parse.Scene:
scene_index = i
break
for i in range(scene_index + 1, len(state.statements)):
ast = state.statements[i]
if type(ast) is parse.Voice:
assert False, ast
elif type(ast) is parse.Menu:
assert False, ast
elif type(ast) is parse.Pause:
assert False, ast
else:
assert type(ast) in simple_statement_types, ast
assert scene_index is not None
state.dissolves.append(InternalDissolve(
duration = duration,
first_statement = scene_index,
count = len(state.statements) - scene_index,
))
elif type(ast) in simple_statement_types:
pass
else:
@ -161,13 +195,6 @@ def pass2_statement(state, pc, statement):
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:
#print(f"not implemented: {statement}", file=sys.stderr)
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:
comment = statement.path.lexeme.decode('utf-8')
audio_index = state.audio_lookup[statement.path.lexeme]
@ -217,7 +244,6 @@ def pass2_statement(state, pc, statement):
comment = ".".join(k.decode('utf-8') for k in key)
yield f"{{ .type = type::hide, .hide = {{ .imageIndex = {image_index} }} }}, // {pc} {comment}"
else:
pass
assert False, (type(statement), statement)
def pass2_statements(state):
@ -286,6 +312,13 @@ def pass2_options(state):
yield "};"
yield "const int options_length = (sizeof (options)) / (sizeof (options[0]));"
def pass2_dissolves(state):
yield "const language::dissolve dissolves[] = {"
for dissolve in state.dissolves:
yield f"{{ .duration = {dissolve.duration}, .first_statement = {dissolve.first_statement}, .count = {dissolve.count} }},"
yield "};"
yield "const int dissolves_length = (sizeof (dissolves)) / (sizeof (dissolves[0]));"
def pass2(state):
yield "#include \"renpy/language.h\""
yield "#include \"renpy/script.h\""
@ -297,6 +330,7 @@ def pass2(state):
yield from pass2_audio(state)
yield from pass2_images(state)
yield from pass2_options(state)
yield from pass2_dissolves(state)
yield from pass2_statements(state)
yield "}"
@ -315,6 +349,7 @@ image _internal_flowers = "flowers.png"
statements = list(),
menus = list(),
entries = dict(),
dissolves = list(),
images_lookup = dict(),
colors_lookup = dict(),
characters_lookup = dict(),

View File

@ -88,8 +88,8 @@ float4 PSMain(VSOutput input) : SV_TARGET
if (color.w == 0.0)
discard;
float gamma = 2.2;
color.xyz = pow(color.xyz, float3(gamma.xxx));
//float gamma = 2.2;
//color.xyz = pow(color.xyz, float3(gamma.xxx));
return float4(color.xyzw);
}

View File

@ -0,0 +1,59 @@
[[vk::binding(0, 0)]] SamplerState ClosestSampler;
[[vk::binding(1, 0)]] Texture2D Texture[];
struct PushConstant {
float DissolveLerp;
float TextLerp;
};
[[vk::push_constant]] PushConstant PushConstant;
struct VSInput
{
float2 Position : POSITION0;
float2 Texture : TEXCOORD0;
};
struct VSOutput
{
float4 Position : SV_POSITION;
float2 Texture : NORMAL0;
float DissolveLerp : DissolveLerp;
float TextLerp : TextLerp;
};
float4 mix(float4 x, float4 y, float a)
{
return x * (1 - a) + y * a;
}
[shader("vertex")]
VSOutput VSMain(VSInput input)
{
VSOutput output = (VSOutput)0;
output.Position = float4(input.Position, 0, 1);
output.Texture = input.Texture;
output.DissolveLerp = PushConstant.DissolveLerp;
output.TextLerp = PushConstant.TextLerp;
return output;
}
[shader("pixel")]
float4 PSMain(VSOutput input) : SV_TARGET
{
float4 color0 = Texture[0].Sample(ClosestSampler, input.Texture);
float4 color = float4(0, 0, 0, 0);
if (input.DissolveLerp >= 0) {
float4 color1 = Texture[1].Sample(ClosestSampler, input.Texture);
float4 color01 = mix(color0, color1, input.TextLerp);
float4 color2 = Texture[2].Sample(ClosestSampler, input.Texture);
color = mix(color01, color2, input.DissolveLerp);
} else {
color = color0;
}
return float4(color.xyzw);
}

View File

@ -622,7 +622,7 @@ namespace font::outline {
int& outputIndex,
int startIndex,
int endIndex,
uint32_t color)
uint32_t color) const
{
for (int i = startIndex; i < endIndex; i++) {
char c = string[i];
@ -645,7 +645,7 @@ namespace font::outline {
uint32_t minX,
uint32_t maxWidth,
int& outputIndex,
uint32_t color)
uint32_t color) const
{
int stringIndex = 0;
int lineStart = stringIndex;
@ -700,7 +700,7 @@ namespace font::outline {
void font::draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
renpy::interpreter const& state)
renpy::interpreter const& state) const
{
// transfer
int outputIndex = 0;

View File

@ -23,6 +23,7 @@
#include "font/outline.h"
#include "renpy/vulkan.h"
#include "renpy/composite/vulkan.h"
#include "renpy/interpreter.h"
#include "renpy/interact.h"
#include "renpy/script.h"
@ -43,6 +44,11 @@ VkImage depthImage{ VK_NULL_HANDLE };
VkImageView depthImageView{ VK_NULL_HANDLE };
VkDeviceMemory depthMemory{ VK_NULL_HANDLE };
constexpr int colorImageCount = 3;
VkImage colorImage[colorImageCount]{ VK_NULL_HANDLE, VK_NULL_HANDLE };
VkImageView colorImageView[colorImageCount]{ VK_NULL_HANDLE, VK_NULL_HANDLE };
VkDeviceMemory colorMemory[colorImageCount]{ VK_NULL_HANDLE, VK_NULL_HANDLE };
uint32_t shadowArrayLayers{ 6 };
VkImage shadowDepthImage{ VK_NULL_HANDLE };
VkImageView shadowDepthImageView{ VK_NULL_HANDLE };
@ -114,6 +120,7 @@ void createDepth(VkDeviceSize nonCoherentAtomSize,
uint32_t height,
VkFormat format,
VkImageUsageFlags usage,
VkImageAspectFlags aspect,
uint32_t arrayLayers,
VkImage * image,
VkDeviceMemory * memory,
@ -160,7 +167,7 @@ void createDepth(VkDeviceSize nonCoherentAtomSize,
.viewType = viewType,
.format = format,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
.aspectMask = aspect,
.levelCount = 1,
.layerCount = arrayLayers
}
@ -275,6 +282,7 @@ void recreateSwapchain(VkSurfaceFormatKHR surfaceFormat,
imageExtent.height,
depthFormat,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
arrayLayers,
&depthImage,
&depthMemory,
@ -356,6 +364,151 @@ void gamepad_update(view & viewState)
}
}
void offscreenRender(VkCommandBuffer commandBuffer, int frameIndex,
int mx, int my, int colorIndex, bool drawText,
renpy::vulkan const & renpy_state,
renpy::interpreter const & interpreter_state,
font::outline::font const & font_state)
{
// barrier
constexpr int colorBarriersCount = 2;
VkImageMemoryBarrier2 colorBarriers[colorBarriersCount]{
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.image = colorImage[colorIndex],
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
},
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT,
.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.image = depthImage,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
.levelCount = 1,
.layerCount = 1
}
}
};
VkDependencyInfo colorBarrierDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = colorBarriersCount,
.pImageMemoryBarriers = colorBarriers
};
vkCmdPipelineBarrier2(commandBuffer, &colorBarrierDependencyInfo);
// attachments
VkRenderingAttachmentInfo colorRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = colorImageView[colorIndex],
.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue{ .color{ { 0.0f, 0.0f, 0.0f, 0.0f} } }
};
VkRenderingAttachmentInfo depthRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = depthImageView,
.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.clearValue{ .depthStencil{ 1.0f, 0 } }
};
VkRenderingInfo colorRenderingInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea{ .extent{ .width = 1280, .height = 720 } },
.layerCount = 1,
.colorAttachmentCount = 1,
.pColorAttachments = &colorRenderingAttachmentInfo,
.pDepthAttachment = &depthRenderingAttachmentInfo,
.pStencilAttachment = &depthRenderingAttachmentInfo,
};
vkCmdBeginRendering(commandBuffer, &colorRenderingInfo);
// viewport/scissor
VkViewport shadowViewport{
.x = 0,
.y = 0,
.width = static_cast<float>(windowSize.x),
.height = static_cast<float>(windowSize.y),
.minDepth = 0.0f,
.maxDepth = 1.0f
};
vkCmdSetViewport(commandBuffer, 0, 1, &shadowViewport);
VkRect2D shadowScissor{
.extent{
.width = (uint32_t)windowSize.x,
.height = (uint32_t)windowSize.y
}
};
vkCmdSetScissor(commandBuffer, 0, 1, &shadowScissor);
// draw
//collada_state.vulkan.excludeMaterialIndex = lightMaterialIndex;
//collada_state.vulkan.pipelineIndex = 0; // shadow pipeline
//collada_state.draw();
renpy_state.draw(commandBuffer, frameIndex, interpreter_state, mx, my, drawText);
if (drawText) {
font_state.draw(commandBuffer, frameIndex, interpreter_state);
}
vkCmdEndRendering(commandBuffer);
// barrier
{
VkImageMemoryBarrier2 colorBarriers[1]{
{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_INPUT_ATTACHMENT_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.image = colorImage[frameIndex],
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1,
}
}
};
VkDependencyInfo colorBarrierDependencyInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = colorBarriers
};
vkCmdPipelineBarrier2(commandBuffer, &colorBarrierDependencyInfo);
}
}
static inline double clamp01(double a)
{
if (a < 0.0) return 0.0;
if (a > 1.0) return 1.0;
return a;
}
int main()
{
file::init();
@ -530,6 +683,16 @@ int main()
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &surfaceFormatCount, surfaceFormats));
uint32_t surfaceFormatIndex{ 0 };
printf("surfaceFormatCount %d\n", surfaceFormatCount);
for (uint32_t i = 0; i < surfaceFormatCount; i++) {
if (surfaceFormats[i].format == VK_FORMAT_B8G8R8A8_UNORM) {
surfaceFormatIndex = i;
break;
}
if (surfaceFormats[i].format == VK_FORMAT_R8G8B8A8_UNORM) {
surfaceFormatIndex = i;
break;
}
}
for (uint32_t i = 0; i < surfaceFormatCount; i++) {
printf("surfaceFormat[%d] %s %s%s\n", i, string_VkFormat(surfaceFormats[i].format), string_VkColorSpaceKHR(surfaceFormats[i].colorSpace), (i == surfaceFormatIndex) ? " [selected]" : "");
}
@ -564,6 +727,7 @@ int main()
720,
depthFormat,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
shadowArrayLayers,
&shadowDepthImage,
&shadowDepthMemory,
@ -656,6 +820,25 @@ int main()
};
VK_CHECK(vkCreateSampler(device, &samplerCreateInfo2, nullptr, &textureSamplers[2]));
//////////////////////////////////////////////////////////////////////
// off-screen color buffers
//////////////////////////////////////////////////////////////////////
for (int i = 0; i < colorImageCount; i++) {
uint32_t arrayLayers{ 1 };
createDepth(physicalDeviceProperties.limits.nonCoherentAtomSize,
physicalDeviceMemoryProperties,
1280,
720,
surfaceFormat.format,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT,
arrayLayers,
&colorImage[i],
&colorMemory[i],
&colorImageView[i]);
}
//////////////////////////////////////////////////////////////////////
// initialize collada
//////////////////////////////////////////////////////////////////////
@ -736,6 +919,23 @@ int main()
renpy::interpreter interpreter_state;
interpreter_state.reset();
//////////////////////////////////////////////////////////////////////
// renpy composite
//////////////////////////////////////////////////////////////////////
renpy::composite::vulkan composite_state;
composite_state.initial_state(instance,
device,
queue,
commandPool,
physicalDeviceProperties,
physicalDeviceMemoryProperties,
surfaceFormat.format,
depthFormat,
textureSamplers[2]);
composite_state.init();
composite_state.write_descriptor_sets(colorImageView, colorImageCount);
//////////////////////////////////////////////////////////////////////
// initialize view
//////////////////////////////////////////////////////////////////////
@ -775,6 +975,9 @@ int main()
int64_t start_time;
SDL_GetCurrentTime(&start_time);
double pause_start = 0.0;
double dissolve_start = 0.0;
//collada_state.update(0);
audio::init();
@ -782,8 +985,39 @@ int main()
while (quit == false) {
audio::update();
//////////////////////////////////////////////////////////////////////
// interpreter update
//////////////////////////////////////////////////////////////////////
interpreter_state.interpret();
if (interpreter_state.pause.pause) {
if (pause_start == 0.0) {
fprintf(stderr, "pause %f\n", interpreter_state.pauseDuration);
pause_start = getTime(start_time);
} else if (getTime(start_time) - pause_start >= interpreter_state.pauseDuration) {
fprintf(stderr, "unpause %f\n", interpreter_state.pauseDuration);
pause_start = 0.0;
interpreter_state.pause.pause = false;
}
}
if (interpreter_state.pause.dissolve) {
if (dissolve_start == 0.0) {
fprintf(stderr, "dissolve %f\n", interpreter_state.dissolveDuration);
dissolve_start = getTime(start_time);
} else if (getTime(start_time) - dissolve_start >= interpreter_state.dissolveDuration) {
fprintf(stderr, "undissolve %f\n", interpreter_state.dissolveDuration);
dissolve_start = 0.0;
interpreter_state.pause.dissolve = false;
}
}
//////////////////////////////////////////////////////////////////////
// sdl
//////////////////////////////////////////////////////////////////////
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) {
@ -827,6 +1061,16 @@ int main()
}
}
//////////////////////////////////////////////////////////////////////
// renpy update
//////////////////////////////////////////////////////////////////////
float mx;
float my;
uint32_t mouseFlags = SDL_GetMouseState(&mx, &my);
bool mLeft = (mouseFlags & SDL_BUTTON_LMASK) != 0;
renpy::update(interpreter_state, mx, my, mLeft);
//////////////////////////////////////////////////////////////////////
// gamepad update
//////////////////////////////////////////////////////////////////////
@ -902,6 +1146,7 @@ int main()
// shadow render
//////////////////////////////////////////////////////////////////////
/*
// barrier
VkImageMemoryBarrier2 shadowBarriers[1]{
VkImageMemoryBarrier2{
@ -1002,6 +1247,19 @@ int main()
};
vkCmdPipelineBarrier2(commandBuffer, &shadowBarrierDependencyInfo);
}
*/
//////////////////////////////////////////////////////////////////////
// offscreen render
//////////////////////////////////////////////////////////////////////
if (interpreter_state.pause.dissolve) {
offscreenRender(commandBuffer, frameIndex, mx, my, 2, true, renpy_state, interpreter_state, font_state);
} else {
offscreenRender(commandBuffer, frameIndex, mx, my, 0, true, renpy_state, interpreter_state, font_state);
offscreenRender(commandBuffer, frameIndex, mx, my, 1, false, renpy_state, interpreter_state, font_state);
}
//////////////////////////////////////////////////////////////////////
// render
@ -1086,7 +1344,12 @@ int main()
.maxDepth = 1.0f
};
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{ .extent{ .width = (uint32_t)windowSize.x, .height = (uint32_t)windowSize.y } };
VkRect2D scissor{
.extent{
.width = (uint32_t)windowSize.x,
.height = (uint32_t)windowSize.y
}
};
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
// draw
@ -1099,13 +1362,19 @@ int main()
//minecraft_state.draw(commandBuffer, frameIndex);
float mx;
float my;
uint32_t mouseFlags = SDL_GetMouseState(&mx, &my);
bool mLeft = (mouseFlags & SDL_BUTTON_LMASK) != 0;
renpy::update(interpreter_state, mx, my, mLeft);
renpy_state.draw(commandBuffer, frameIndex, interpreter_state, mx, my);
font_state.draw(commandBuffer, frameIndex, interpreter_state);
//renpy_state.draw(commandBuffer, frameIndex, interpreter_state, mx, my);
//font_state.draw(commandBuffer, frameIndex, interpreter_state);
float dissolve_lerp = -1.0;
float text_lerp = -1.0;
constexpr double textDissolveDuration = 0.3;
if (interpreter_state.pause.dissolve) {
double delta = getTime(start_time) - dissolve_start;
assert(interpreter_state.dissolveDuration > 0.0000001);
dissolve_lerp = clamp01(delta / interpreter_state.dissolveDuration);
text_lerp = clamp01(delta / textDissolveDuration);
}
composite_state.draw(commandBuffer, frameIndex, dissolve_lerp, text_lerp);
vkCmdEndRendering(commandBuffer);

View File

@ -0,0 +1,414 @@
#include <string.h>
#ifdef __APPLE__
#include "vulkan/vulkan.h"
#else
#include "volk/volk.h"
#endif
#include "vulkan/vk_enum_string_helper.h"
#include "vulkan_helper.h"
#include "check.h"
#include "new.h"
#include "file.h"
#include "renpy/composite/vulkan.h"
namespace renpy::composite {
static constexpr int imageCount = 3;
static const _Float16 vertexData[] = {
// x y u v
(_Float16)-1.0, (_Float16)-1.0, (_Float16)0.0, (_Float16)0.0,
(_Float16)1.0, (_Float16)-1.0, (_Float16)1.0, (_Float16)0.0,
(_Float16)-1.0, (_Float16)1.0, (_Float16)0.0, (_Float16)1.0,
(_Float16)1.0, (_Float16)1.0, (_Float16)1.0, (_Float16)1.0,
};
static const uint32_t vertexSize = (sizeof (vertexData));
static const uint16_t indexData[] = {
0, 1, 2, 3,
};
static const uint32_t indexSize = (sizeof (indexData));
void vulkan::initial_state(VkInstance instance,
VkDevice device,
VkQueue queue,
VkCommandPool commandPool,
VkPhysicalDeviceProperties physicalDeviceProperties,
VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties,
VkFormat colorFormat,
VkFormat depthFormat,
VkSampler linearSampler)
{
this->instance = instance;
this->device = device;
this->queue = queue;
this->commandPool = commandPool;
this->physicalDeviceProperties = physicalDeviceProperties;
this->physicalDeviceMemoryProperties = physicalDeviceMemoryProperties;
this->colorFormat = colorFormat;
this->depthFormat = depthFormat;
this->linearSampler = linearSampler;
}
void vulkan::init()
{
load_vertex_index_buffer();
load_shader();
create_descriptor_sets();
create_pipeline();
}
//////////////////////////////////////////////////////////////////////
// vertex index buffer
//////////////////////////////////////////////////////////////////////
void vulkan::load_vertex_index_buffer()
{
void const * vertexStart = (void const *)vertexData;
void const * indexStart = (void const *)indexData;
vertexIndex = createVertexIndexBuffer(device,
physicalDeviceProperties,
physicalDeviceMemoryProperties,
vertexStart, vertexSize,
indexStart, indexSize);
}
//////////////////////////////////////////////////////////////////////
// shader
//////////////////////////////////////////////////////////////////////
void vulkan::load_shader()
{
uint32_t shaderSize;
void const * shaderStart = file::open("shader/renpy_composite.spv", &shaderSize);
VkShaderModuleCreateInfo shaderModuleCreateInfo{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = shaderSize,
.pCode = (uint32_t *)shaderStart
};
VK_CHECK(vkCreateShaderModule(device, &shaderModuleCreateInfo, nullptr, &shaderModule));
}
//////////////////////////////////////////////////////////////////////
// descriptor sets
//////////////////////////////////////////////////////////////////////
void vulkan::create_descriptor_sets()
{
//
// pool
//
constexpr int descriptorPoolSizesCount = 2;
VkDescriptorPoolSize descriptorPoolSizes[descriptorPoolSizesCount]{
{ // linear sampler
.type = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = 1,
},
{
.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = imageCount,
},
};
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.maxSets = 1,
.poolSizeCount = descriptorPoolSizesCount,
.pPoolSizes = descriptorPoolSizes
};
VK_CHECK(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool));
//
// (set 0, constant)
//
{
constexpr int bindingCount = 2;
VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[bindingCount]{
{
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
},
{ // font image
.binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.descriptorCount = imageCount,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT
}
};
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = bindingCount,
.pBindings = descriptorSetLayoutBindings
};
VK_CHECK(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayouts[0]));
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &descriptorSetLayouts[0]
};
VK_CHECK(vkAllocateDescriptorSets(device, &descriptorSetAllocateInfo, &descriptorSet0));
}
}
//////////////////////////////////////////////////////////////////////
// descriptor set writes
//////////////////////////////////////////////////////////////////////
void vulkan::write_descriptor_sets(VkImageView * imageViews, int imageViewCount)
{
assert(imageViewCount == imageCount);
constexpr uint32_t writeCount = 2;
VkWriteDescriptorSet writeDescriptorSets[writeCount];
uint32_t writeIndex = 0;
// set0 bindings
VkDescriptorImageInfo samplerDescriptorImageInfo = {
.sampler = linearSampler,
};
writeDescriptorSets[writeIndex++] = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptorSet0,
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER,
.pImageInfo = &samplerDescriptorImageInfo
};
VkDescriptorImageInfo sceneDescriptorImageInfos[imageCount];
for (int i = 0; i < imageCount; i++) {
sceneDescriptorImageInfos[i] = {
.imageView = imageViews[i],
.imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL
};
}
writeDescriptorSets[writeIndex++] = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = descriptorSet0,
.dstBinding = 1,
.descriptorCount = imageCount,
.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
.pImageInfo = sceneDescriptorImageInfos
};
assert(writeIndex == writeCount);
vkUpdateDescriptorSets(device, writeIndex, writeDescriptorSets, 0, nullptr);
//free(sceneDescriptorImageInfos);
}
//////////////////////////////////////////////////////////////////////
// pipeline
//////////////////////////////////////////////////////////////////////
void vulkan::create_pipeline()
{
VkPushConstantRange pushConstantRange{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.size = (sizeof (float)),
};
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = descriptorSetLayoutCount,
.pSetLayouts = descriptorSetLayouts,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushConstantRange,
};
VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP
};
VkPipelineShaderStageCreateInfo shaderStages[2]{
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = shaderModule,
.pName = "VSMain"
},
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = shaderModule,
.pName = "PSMain"
}
};
VkPipelineViewportStateCreateInfo viewportState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1
};
constexpr uint32_t dynamicStateCount = 2;
VkDynamicState dynamicStates[dynamicStateCount]{
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
VkPipelineDynamicStateCreateInfo dynamicState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = dynamicStateCount,
.pDynamicStates = dynamicStates
};
VkPipelineDepthStencilStateCreateInfo depthStencilState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_FALSE,
.depthWriteEnable = VK_FALSE,
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
.stencilTestEnable = VK_FALSE,
.front = {
.failOp = VK_STENCIL_OP_REPLACE,
.passOp = VK_STENCIL_OP_REPLACE,
.depthFailOp = VK_STENCIL_OP_REPLACE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.compareMask = 0x01,
.writeMask = 0x01,
.reference = 1,
},
.back = {
.failOp = VK_STENCIL_OP_REPLACE,
.passOp = VK_STENCIL_OP_REPLACE,
.depthFailOp = VK_STENCIL_OP_REPLACE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.compareMask = 0x01,
.writeMask = 0x01,
.reference = 1,
},
};
VkPipelineRenderingCreateInfo renderingCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &colorFormat,
.depthAttachmentFormat = depthFormat,
.stencilAttachmentFormat = depthFormat
};
VkPipelineColorBlendAttachmentState blendAttachment{
.blendEnable = VK_FALSE,
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = 0xF,
};
VkPipelineColorBlendStateCreateInfo colorBlendState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &blendAttachment
};
VkPipelineRasterizationStateCreateInfo rasterizationState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
//.cullMode = VK_CULL_MODE_BACK_BIT,
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.lineWidth = 1.0f
};
VkPipelineMultisampleStateCreateInfo multisampleState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
constexpr int vertexBindingDescriptionsCount = 1;
VkVertexInputBindingDescription vertexBindingDescriptions[vertexBindingDescriptionsCount]{
{
.binding = 0,
.stride = perVertexSize,
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
},
};
constexpr int vertexAttributeDescriptionsCount = 2;
VkVertexInputAttributeDescription vertexAttributeDescriptions[vertexAttributeDescriptionsCount]{
// per-vertex
{ // position
.location = 0,
.binding = 0,
.format = VK_FORMAT_R16G16_SFLOAT,
.offset = 0,
},
{ // texture
.location = 1,
.binding = 0,
.format = VK_FORMAT_R16G16_SFLOAT,
.offset = 4,
},
};
VkPipelineVertexInputStateCreateInfo vertexInputState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = vertexBindingDescriptionsCount,
.pVertexBindingDescriptions = vertexBindingDescriptions,
.vertexAttributeDescriptionCount = vertexAttributeDescriptionsCount,
.pVertexAttributeDescriptions = vertexAttributeDescriptions,
};
VkGraphicsPipelineCreateInfo pipelineCreateInfos[1]{
{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &renderingCreateInfo,
.stageCount = 2,
.pStages = shaderStages,
.pVertexInputState = &vertexInputState,
.pInputAssemblyState = &inputAssemblyState,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizationState,
.pMultisampleState = &multisampleState,
.pDepthStencilState = &depthStencilState,
.pColorBlendState = &colorBlendState,
.pDynamicState = &dynamicState,
.layout = pipelineLayout
},
};
VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pipelineCreateInfos, nullptr, &pipeline));
}
//////////////////////////////////////////////////////////////////////
// draw
//////////////////////////////////////////////////////////////////////
void vulkan::draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
float dissolveLerp,
float textLerp)
{
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
float lerp[2] = { dissolveLerp, textLerp };
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, (sizeof (float)) * 2, lerp);
VkDescriptorSet descriptorSets[1] = {
descriptorSet0,
};
vkCmdBindDescriptorSets(commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipelineLayout,
0, 1, descriptorSets,
0, nullptr);
vkCmdBindIndexBuffer(commandBuffer, vertexIndex.buffer, vertexIndex.indexOffset, VK_INDEX_TYPE_UINT16);
VkDeviceSize vertexOffsets[1]{ 0 };
VkBuffer vertexBuffers[1]{ vertexIndex.buffer };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, vertexOffsets);
vkCmdDrawIndexed(commandBuffer, 4, 1, 0, 0, 0);
}
}

View File

@ -22,7 +22,7 @@ namespace renpy {
bool mDown = mLeft && (!lastmLeft);
lastmLeft = mLeft;
if (mDown) {
state.interactionWait = false;
state.pause.voice = false;
}
if (state.menu.count == 0 || !mDown)

View File

@ -10,13 +10,17 @@ namespace renpy {
void interpreter::reset()
{
pc = 0;
backgroundIndex = -1;
backgroundIndex = ~0u;
backgroundColor = 0;
shownImagesCount = 0;
say.stringIndex = -1;
say.characterIndex = -1;
say.stringIndex = ~0u;
say.characterIndex = ~0u;
menu.count = 0;
interactionWait = false;
dissolveIndex = 0;
pause.voice = false;
pause.menu = false;
pause.dissolve = false;
pause.pause = false;
}
uint32_t interpreter::findImage(uint32_t imageIndex)
@ -60,6 +64,8 @@ namespace renpy {
void interpreter::interpret_one()
{
uint32_t last_pc = pc;
assert(pc < (uint32_t)script::statements_length);
language::statement const& statement = script::statements[pc];
@ -90,8 +96,8 @@ namespace renpy {
backgroundIndex = statement.scene.imageIndex;
shownImagesCount = 0;
say.stringIndex = -1;
say.characterIndex = -1;
say.stringIndex = ~0u;
say.characterIndex = ~0u;
pc += 1;
break;
case language::type::say:
@ -99,7 +105,7 @@ namespace renpy {
assert(statement.say.stringIndex < (uint32_t)script::strings_length);
say.stringIndex = statement.say.stringIndex;
say.characterIndex = statement.say.characterIndex;
interactionWait = true;
pause.voice = true;
pc += 1;
break;
case language::type::hide:
@ -129,22 +135,37 @@ namespace renpy {
assert(statement.jump.statementIndex < (uint32_t)script::statements_length);
pc = statement.jump.statementIndex;
break;
case language::type::pause:
fprintf(stderr, "interpret_one[%d]: pause %f\n", pc, statement.pause.duration);
pauseDuration = statement.pause.duration;
pause.pause = true;
pc += 1;
break;
default:
fprintf(stderr, "unknown statement type at pc %d\n", pc);
assert(false);
pc += 1;
break;
}
assert(pc != last_pc);
}
void interpreter::interpret()
{
while (!interactionWait) {
//while (true) {
uint32_t last_pc = pc;
interpret_one();
assert(pc != last_pc);
//if (pc == 18) break;
while (!interactionWait()) {
if (dissolvePC()) {
fprintf(stderr, "dissolve pc %d\n", pc);
language::dissolve const & dissolve = script::dissolves[dissolveIndex];
for (uint32_t i = 0; i < dissolve.count; i++) {
interpret_one();
}
pause.dissolve = true;
dissolveDuration = dissolve.duration;
dissolveIndex += 1;
} else {
interpret_one();
}
}
}
};

View File

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

View File

@ -514,11 +514,62 @@ namespace renpy {
// draw
//////////////////////////////////////////////////////////////////////
void vulkan::draw_menu_frame(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
renpy::interpreter const& state,
int & outputIndex,
int mx, int my) const
{
for (uint32_t i = 0; i < state.menu.count; i++) {
int y = menu::yStride * i + menu::y;
bool overlap = renpy::overlap(menu::width, menu::height, menu::x, y, mx, my);
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {menu::width, menu::height},
.topLeft = {menu::x, (int16_t)(y)},
.color = overlap ? 0xf0494493u : 0xa0ffffffu,
.imageIndex = -3, // white gradient 2
};
}
}
void vulkan::draw_say_frame(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
renpy::interpreter const& state,
int & outputIndex) const
{
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {708, 200},
.topLeft = {286, 720 - 200},
.color = 0x80ffffffu,
.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},
.color = 0x80ffffffu,
.imageIndex = -4, // white gradient 2
};
}
}
void vulkan::draw(VkCommandBuffer commandBuffer,
uint32_t frameIndex,
renpy::interpreter const& state,
int mx,
int my)
int mx, int my,
bool drawText) const
{
int outputIndex = 0;
// update
@ -538,43 +589,11 @@ namespace renpy {
};
}
if (state.menu.count == 0) {
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {708, 200},
.topLeft = {286, 720 - 200},
.color = 0x80ffffffu,
.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},
.color = 0x80ffffffu,
.imageIndex = -4, // white gradient 2
};
}
} else {
for (uint32_t i = 0; i < state.menu.count; i++) {
int y = menu::yStride * i + menu::y;
bool overlap = renpy::overlap(menu::width, menu::height, menu::x, y, mx, my);
instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = {
.size = {menu::width, menu::height},
.topLeft = {menu::x, (int16_t)(y)},
.color = overlap ? 0xf0494493u : 0xa0ffffffu,
.imageIndex = -3, // white gradient 2
};
if (drawText) {
if (state.menu.count != 0) {
draw_menu_frame(commandBuffer, frameIndex, state, outputIndex, mx, my);
} else if (state.say.stringIndex != ~0u) {
draw_say_frame(commandBuffer, frameIndex, state, outputIndex);
}
}

View File

@ -1,2 +1 @@
export VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation
export VK_VALIDATION_VALIDATE_SYNC=1