diff --git a/Makefile b/Makefile index 4484113..c1f6d10 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/data/renpy/script.rpy b/data/renpy/script.rpy index 72abb1d..d17b868 100644 --- a/data/renpy/script.rpy +++ b/data/renpy/script.rpy @@ -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" diff --git a/filenames.txt b/filenames.txt index ed46b01..e79d7de 100644 --- a/filenames.txt +++ b/filenames.txt @@ -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 diff --git a/include/font/outline.h b/include/font/outline.h index 7e19bc9..97ee848 100644 --- a/include/font/outline.h +++ b/include/font/outline.h @@ -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); }; diff --git a/include/renpy/composite/vulkan.h b/include/renpy/composite/vulkan.h new file mode 100644 index 0000000..cb9ee36 --- /dev/null +++ b/include/renpy/composite/vulkan.h @@ -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); + }; +} diff --git a/include/renpy/interpreter.h b/include/renpy/interpreter.h index 674bc05..1203464 100644 --- a/include/renpy/interpreter.h +++ b/include/renpy/interpreter.h @@ -3,6 +3,7 @@ #include #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; + } }; } diff --git a/include/renpy/language.h b/include/renpy/language.h index 08cfe9a..6bacb16 100644 --- a/include/renpy/language.h +++ b/include/renpy/language.h @@ -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; }; }; } diff --git a/include/renpy/script.h b/include/renpy/script.h index 5b24357..b6ede6d 100644 --- a/include/renpy/script.h +++ b/include/renpy/script.h @@ -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; diff --git a/include/renpy/vulkan.h b/include/renpy/vulkan.h index 17695dc..f043552 100644 --- a/include/renpy/vulkan.h +++ b/include/renpy/vulkan.h @@ -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; }; } diff --git a/renpy-parser/transform.py b/renpy-parser/transform.py index de302fd..ac863c9 100644 --- a/renpy-parser/transform.py +++ b/renpy-parser/transform.py @@ -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(), diff --git a/shader/renpy.hlsl b/shader/renpy.hlsl index edd4298..440774c 100644 --- a/shader/renpy.hlsl +++ b/shader/renpy.hlsl @@ -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); } diff --git a/shader/renpy_composite.hlsl b/shader/renpy_composite.hlsl new file mode 100644 index 0000000..42bce40 --- /dev/null +++ b/shader/renpy_composite.hlsl @@ -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); +} diff --git a/src/font/outline.cpp b/src/font/outline.cpp index db90ce9..2f5d5eb 100644 --- a/src/font/outline.cpp +++ b/src/font/outline.cpp @@ -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; diff --git a/src/main.cpp b/src/main.cpp index e6f78b1..04ab839 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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(windowSize.x), + .height = static_cast(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); diff --git a/src/renpy/composite/vulkan.cpp b/src/renpy/composite/vulkan.cpp new file mode 100644 index 0000000..a95680e --- /dev/null +++ b/src/renpy/composite/vulkan.cpp @@ -0,0 +1,414 @@ +#include + +#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); + } +} diff --git a/src/renpy/interact.cpp b/src/renpy/interact.cpp index 8c002c5..6859f17 100644 --- a/src/renpy/interact.cpp +++ b/src/renpy/interact.cpp @@ -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) diff --git a/src/renpy/interpreter.cpp b/src/renpy/interpreter.cpp index 525eb24..bbd979d 100644 --- a/src/renpy/interpreter.cpp +++ b/src/renpy/interpreter.cpp @@ -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(); + } } } }; diff --git a/src/renpy/script.cpp b/src/renpy/script.cpp index 0aef43c..34d107a 100644 --- a/src/renpy/script.cpp +++ b/src/renpy/script.cpp @@ -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])); diff --git a/src/renpy/vulkan.cpp b/src/renpy/vulkan.cpp index 55f0435..ff1e3de 100644 --- a/src/renpy/vulkan.cpp +++ b/src/renpy/vulkan.cpp @@ -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); } } diff --git a/validation.sh b/validation.sh index ce9b7fe..ba506d0 100644 --- a/validation.sh +++ b/validation.sh @@ -1,2 +1 @@ export VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation -export VK_VALIDATION_VALIDATE_SYNC=1