From ce5d9fccba905deab7bb3d753f6d47c465eb726e Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Mon, 25 May 2026 22:42:28 -0500 Subject: [PATCH] renpy: define image instance data --- filenames.txt | 39 +-- include/renpy/interpreter.h | 27 ++ include/renpy/language.h | 10 + include/renpy/script.h | 522 +++++++++++++++++++++++++++--------- include/renpy/vulkan.h | 23 ++ renpy-parser/transform.py | 18 +- shader/font.hlsl | 2 +- shader/renpy.hlsl | 8 +- src/font/outline.cpp | 6 +- src/main.cpp | 3 +- src/renpy/vulkan.cpp | 107 +++++++- 11 files changed, 578 insertions(+), 187 deletions(-) create mode 100644 include/renpy/interpreter.h diff --git a/filenames.txt b/filenames.txt index 430da58..d4dabb6 100644 --- a/filenames.txt +++ b/filenames.txt @@ -1,47 +1,10 @@ -shader/collada.spv -data/scenes/shadow_test/shadow_test.vtx -data/scenes/shadow_test/shadow_test.idx -data/scenes/shadow_test/images/0_leaf_white.dds - -data/scenes/eidelwind/eidelwind.vtx -data/scenes/eidelwind/eidelwind.vjw -data/scenes/eidelwind/eidelwind.idx -data/scenes/eidelwind/images/0_EidelWindTextureTest.dds - -shader/minecraft.spv -data/minecraft/per_vertex.vtx -data/minecraft/configuration.idx - -data/minecraft/grandlecturn/region.0.0.instance.vtx -data/minecraft/grandlecturn/region.-1.0.instance.vtx -data/minecraft/grandlecturn/region.0.-1.instance.vtx -data/minecraft/grandlecturn/region.-1.-1.instance.vtx -data/minecraft/grandlecturn/region.0.0.instance.cfg -data/minecraft/grandlecturn/region.-1.0.instance.cfg -data/minecraft/grandlecturn/region.0.-1.instance.cfg -data/minecraft/grandlecturn/region.-1.-1.instance.cfg -data/minecraft/grandlecturn/global.dump -data/minecraft/grandlecturn/global.lights.vtx - -data/minecraft/midnightmeadow/region.0.0.instance.vtx -data/minecraft/midnightmeadow/region.-1.0.instance.vtx -data/minecraft/midnightmeadow/region.0.-1.instance.vtx -data/minecraft/midnightmeadow/region.-1.-1.instance.vtx -data/minecraft/midnightmeadow/region.0.0.instance.cfg -data/minecraft/midnightmeadow/region.-1.0.instance.cfg -data/minecraft/midnightmeadow/region.0.-1.instance.cfg -data/minecraft/midnightmeadow/region.-1.-1.instance.cfg -data/minecraft/midnightmeadow/global.dump -data/minecraft/midnightmeadow/global.lights.vtx - -data/minecraft/terrain2.dds - shader/font.spv data/font/outline/uncial_antiqua_36.data shader/renpy.spv data/renpy/images/bg/white.dds data/renpy/images/bg/forest1.dds +data/renpy/images/bg/forest2.dds data/renpy/images/bg/flowerfield1.dds data/renpy/images/bg/wheatfield1.dds data/renpy/images/ch/cat.dds diff --git a/include/renpy/interpreter.h b/include/renpy/interpreter.h new file mode 100644 index 0000000..e91e5cc --- /dev/null +++ b/include/renpy/interpreter.h @@ -0,0 +1,27 @@ + + +namespace renpy { + struct top_left { + uint32_t top; + uint32_t left; + }; + + static const top_left transforms[] = { + [language::transform::left] = { .top = 192, .left = 0 }, + [language::transform::centerleft] = { .top = 192, .left = 240 }, + [language::transform::center] = { .top = 192, .left = 416 }, + [language::transform::centerright] = { .top = 192, .left = 588 }, + [language::transform::right] = { .top = 192, .left = 828 }, + }; + + struct shownImage { + uint32_t imageIndex; + uint32_t transformIndex; + }; + + struct interpreter { + uint32_t backgroundIndex; + shownImage shownImages[16]; + uint32_t shownImagesCount; + }; +} diff --git a/include/renpy/language.h b/include/renpy/language.h index 1d1c139..2b54854 100644 --- a/include/renpy/language.h +++ b/include/renpy/language.h @@ -1,6 +1,16 @@ #include namespace renpy::language { + struct transform { + enum _transform { + left, + centerleft, + center, + centerright, + right, + }; + }; + struct option { char const * const string; uint32_t statementIndex; diff --git a/include/renpy/script.h b/include/renpy/script.h index 5f6f664..42323c4 100644 --- a/include/renpy/script.h +++ b/include/renpy/script.h @@ -1,8 +1,7 @@ #include "renpy/language.h" namespace renpy::script { -using namespace renpy::language; - + using namespace renpy::language; char const * const strings[] = { "Far over the mountains of Almystice", // 0 "Beyond the tumultuous waters of the Lilac Bay", // 1 @@ -11,9 +10,9 @@ char const * const strings[] = { "Are we almost there?", // 4 "Hmmm... Not really", // 5 "How much further have we to go?", // 6 - "About a day or two of walking", // 7 + "About two more moons", // 7 "We are still sooo far awayyy", // 8 - "And it will be even further if we dont stop taking breaks", // 9 + "And it will be even further if you dont stop complaining", // 9 "Easy for you to say, all you have to carry is a little memory pipe!", // 10 "I'm tired ><", // 11 "Don't start whining now!", // 12 @@ -34,30 +33,149 @@ char const * const strings[] = { "Yah yah", // 27 "Anyways, shall I recite a tale?", // 28 "Why dont you sing the story of Eleanor the Hero!", // 29 - "Let us sing of the Hero Eleanor", // 30 + "Sure", // 30 "...", // 31 "Serves you right for scaring those elephant-dogs", // 32 "They were asking for it, you know", // 33 "Rawrrrr", // 34 "AHHHHHHHHHH!!!!!", // 35 "Nyanyanyanya", // 36 - "Well, what do we have here? If it isnt 2 little meowse girls, all alone in the flowers", // 37 - "Please dont eat us!!!", // 38 - "Alice dont run, our only chance is through pleading!", // 39 - "Please dont eat us, miss kitty cat!!! ><", // 40 + "Well, what do we have here? If it isn't two little meowse girls, all alone amongst the flowers", // 37 + "Please don't eat us!!!", // 38 + "Alice don't run, our only chance is through pleading!", // 39 + "Please don't eat us, miss kitty cat!!! ><", // 40 "I'm not gonna eat you nyanyanya", // 41 "I just want to know what two little meowses are doing so very far away from home", // 42 "Also, are you minstrels?", // 43 "Y-Yes", // 44 "W-We are on a quest to Castle Alysen...", // 45 - "Shh dont tell her that", // 46 + "Shh don't tell her that", // 46 "The Castle of Alysen you say?!?", // 47 "Why, that's where I am headed!", // 48 - "You dont say...", // 49 - "Yah I do actually", // 50 + "You don't say...", // 49 + "Yah, I do actually", // 50 "So... Why might you be traveling to the castle?", // 51 - "nya", // 52 - "Look, your right, the castle is just up ahead!", // 53 + "I belong to the lineage of Agrepen", // 52 + "And what might that mean?", // 53 + "The Agrepens are a long line of felines loyal to the crown of corvidae", // 54 + "Really? That must mean you are a noble?", // 55 + "Well, not really...", // 56 + "My father was one of the queens knights many years ago", // 57 + "Ah I see", // 58 + "So do you live at the castle or something?", // 59 + "Well, no...", // 60 + "Then why are you traveling to Castle Alysen?", // 61 + "uhhh", // 62 + "I DONT NEED TO BE PRESSURED BY LITTLE MICE TO SAY ANYTHING!!!!", // 63 + "GOOD DAY!", // 64 + "Wha...", // 65 + "Phew, I was scared she was gonna follow us the whole way", // 66 + "She didn't seem so bad", // 67 + "Are you kidding? She's a crazy kitty!", // 68 + "After their encounter with the weird cat, the mice scurry out of the flower field and into the nearby meadow", // 69 + "I think this is the right way...", // 70 + "Then where did the path go?", // 71 + "How am I supposed to know?", // 72 + "Did you hear that?!?!", // 73 + "Hey there...", // 74 + "I apologize", // 75 + "I didn't mean to storm off like that", // 76 + "Ha ha, no problem...", // 77 + "So... Why are you traveling to Castle Alysen?", // 78 + "Alice!!!", // 79 + "If you must know, I have been summoned by the queen", // 80 + "I suspect that my poor reputation amongst the locals of Eastern Nidus has come back to haunt me", // 81 + "Though I know not what what she has summoned me for", // 82 + "Ahhhhhh", // 83 + "So then...", // 84 + "Why are YOU traveling to the Castle?", // 85 + "We are delivering a feather!!!", // 86 + "Alice no!", // 87 + "A feather that belonged to the queen herself!!!", // 88 + "Why you little...", // 89 + "A feather you say? One of the queens?", // 90 + "How on the face of Al Mot might you have aquired such a thing?", // 91 + "If it is authentic, that is...", // 92 + "Since Alice cannot keep a secret, I shall tell you", // 93 + "Seven moons ago, our town was attacked by three owls and a band of mice from the northern principalities", // 94 + "Eventually word spread to greater nearby settlements, and so", // 95 + "Messengers from the keep in Musia sent for aid from the Ravens", // 96 + "Four moons ago, the request was answered", // 97 + "And a small group of mice accompanied by two ravens a fox, and three squirrels set out to the northern principalities", // 98 + "Anyways, long story short, we drove those barbaric rats out of their home", // 99 + "They arent actual rats you know", // 100 + "Obviously, but you wont catch me speaking kindly of them", // 101 + "And you forgot the most important part", // 102 + "Yah yah, I am getting there", // 103 + "So, essentially, my brother is trained in archery, and...", // 104 + "Speed it up already", // 105 + "You tell it then!", // 106 + "My cousin found this feather in one of the highest towers of a castle far to the north", // 107 + "How do you know it belongs to the queen?", // 108 + "It said so itself above the display on the wall", // 109 + "Supposedly, it was in a room filled with treasures!", // 110 + "That is very nice and all, but what are the two of you doing out here all alone?", // 111 + "Do you expect every bird in Avia to respect your alliance with Castle Alysen?", // 112 + "What do you mean?", // 113 + "I mean, the two of you probably look like walking dinner to most creatures", // 114 + "I could go for some dinner...", // 115 + "Anyways...", // 116 + "To answer your question, upon returning to the village, the feather was taken from my brother by the needle guild", // 117 + "So... Yesterday, after sundown", // 118 + "We stole the feather from the guild hall before vanishing into the night", // 119 + "Can you imagine the look on their stupid faces, when they woke up, and not only is the feather missing", // 120 + "But so are we!", // 121 + "Hahahaha", // 122 + "Are the two of you mad?", // 123 + "I assume you are attempting to return the Queens feather?", // 124 + "Yes, we intend to deliver the feather to its rightful owner", // 125 + "Absolute madness!", // 126 + "I will follow the two of you", // 127 + "To keep you safe, that is", // 128 + "Alright!", // 129 + "Ha ha... Okay...", // 130 + "Great! Follow me, I know a shortcut! :3", // 131 + "Sounds good!", // 132 + "Oh dear!", // 133 + "And so the mice girls follow the noble cat further towards their destination", // 134 + "Nya", // 135 + "Look, your right, the castle is just up ahead!", // 136 + "Wait up", // 137 + "I told you I knew a shortcut!", // 138 + "Most people take the long way around", // 139 + "Yah because these are royal wheatfields!", // 140 + "Who cares?", // 141 + "Are you trying to get us killed?", // 142 + "Its trespassing on royal land!", // 143 + "Calm down, I have done this a million times", // 144 + "That doesnt make me calm!", // 145 + "How can the rolling fields of wheat not calm your spirit?", // 146 + "You little mice truly are mad!", // 147 + "I like the wheat!", // 148 + "Shut up!", // 149 + "Sounds like someone needs a nap!", // 150 + "Why? because I'm not insane like you?", // 151 + "Yah, your so sane, that you decided to steal from your town and then run off alone to the country of birds", // 152 + "The power of friendship wont protect the two of you from becoming dinner", // 153 + "And that, is why I feel obligated to accompany you!", // 154 + "Hey, we have a good reason!", // 155 + "And what might that be?", // 156 + "My brother found the feather, not the town guild, its a matter of family pride!", // 157 + "Pride has touched the chosen meouse", // 158 + "Flies she towards the Castle", // 159 + "But her ambition burns far too bright, and silly myice dont have any wings to myelt", // 160 + "What?", // 161 + "Nyanyanya", // 162 + "Nyevermind", // 163 + "Sing me a song little minstrels!", // 164 + "Very Nyice!", // 165 + "Now tell me little minstrels, what are your names?", // 166 + "My name is Alice", // 167 + "And my name is Eily", // 168 + "What is your name?", // 169 + "My name is Leona!", // 170 + "And so, the odd trio walked through the wheatfields and towards the castle walls", // 171 + "Upon approaching the castle walls", // 172 }; constexpr int strings_length = (sizeof (strings)) / (sizeof (strings[0])); @@ -68,6 +186,7 @@ const character characters[] = { { .characterName = "Eily" }, // 2 { .characterName = "Mouse Girls" }, // 3 { .characterName = "Narrator" }, // 4 + { .characterName = "Leona" }, // 5 }; constexpr int characters_length = (sizeof (characters)) / (sizeof (characters[0])); @@ -75,16 +194,13 @@ constexpr int characters_length = (sizeof (characters)) / (sizeof (characters[0] const audio audio[] = { { .path = "sfx/Chime.opus" }, // 0 sfx/Chime.ogg { .path = "sfx/MistAmbience.opus" }, // 1 sfx/MistAmbience.ogg - { .path = "n1test.opus" }, // 2 n1test.mp3 - { .path = "n2test.opus" }, // 3 n2test.mp3 - { .path = "n3test.opus" }, // 4 n3test.mp3 - { .path = "n4test.opus" }, // 5 n4test.mp3 - { .path = "music/TinyForestMinstrels.opus" }, // 6 music/TinyForestMinstrels.ogg - { .path = "n5test.opus" }, // 7 n5test.ogg - { .path = "music/PhrygianButterflies.opus" }, // 8 music/PhrygianButterflies.ogg - { .path = "music/Poem1.opus" }, // 9 music/Poem1.ogg - { .path = "placeholdermeow.opus" }, // 10 placeholdermeow.mp3 - { .path = "music/ScaredMice.opus" }, // 11 music/ScaredMice.ogg + { .path = "music/TinyForestMinstrels.opus" }, // 2 music/TinyForestMinstrels.ogg + { .path = "n5test.opus" }, // 3 n5test.ogg + { .path = "music/PhrygianButterflies.opus" }, // 4 music/PhrygianButterflies.ogg + { .path = "music/Poem1.opus" }, // 5 music/Poem1.ogg + { .path = "placeholdermeow.opus" }, // 6 placeholdermeow.mp3 + { .path = "music/ScaredMice.opus" }, // 7 music/ScaredMice.ogg + { .path = "music/WheatFields.opus" }, // 8 music/WheatFields.ogg }; constexpr int audio_length = (sizeof (audio)) / (sizeof (audio[0])); @@ -92,23 +208,24 @@ constexpr int audio_length = (sizeof (audio)) / (sizeof (audio[0])); const image images[] = { { .path = "data/renpy/images/bg/white.dds" }, // 0 bg/white.png { .path = "data/renpy/images/bg/forest1.dds" }, // 1 bg/forest1.png - { .path = "data/renpy/images/bg/flowerfield1.dds" }, // 2 bg/flowerfield1.png - { .path = "data/renpy/images/bg/wheatfield1.dds" }, // 3 bg/wheatfield1.png - { .path = "data/renpy/images/ch/cat.dds" }, // 4 ch/cat.png - { .path = "data/renpy/images/ch/catw.dds" }, // 5 ch/catw.png - { .path = "data/renpy/images/ch/Eily.dds" }, // 6 ch/Eily.png - { .path = "data/renpy/images/ch/Alice.dds" }, // 7 ch/Alice.png + { .path = "data/renpy/images/bg/forest2.dds" }, // 2 bg/forest2.png + { .path = "data/renpy/images/bg/flowerfield1.dds" }, // 3 bg/flowerfield1.png + { .path = "data/renpy/images/bg/wheatfield1.dds" }, // 4 bg/wheatfield1.png + { .path = "data/renpy/images/ch/cat.dds" }, // 5 ch/cat.png + { .path = "data/renpy/images/ch/catw.dds" }, // 6 ch/catw.png + { .path = "data/renpy/images/ch/Eily.dds" }, // 7 ch/Eily.png + { .path = "data/renpy/images/ch/Alice.dds" }, // 8 ch/Alice.png }; constexpr int images_length = (sizeof (images)) / (sizeof (images[0])); const option options[] = { - { .string = "Complain", .statementIndex = 22 }, // 0 - { .string = "Rationalize", .statementIndex = 30 }, // 1 - { .string = "Good idea", .statementIndex = 57 }, // 2 - { .string = "I am too tired", .statementIndex = 64 }, // 3 - { .string = "Beg for mercy", .statementIndex = 80 }, // 4 - { .string = "Run", .statementIndex = 82 }, // 5 + { .string = "Complain", .statementIndex = 18 }, // 0 + { .string = "Rationalize", .statementIndex = 26 }, // 1 + { .string = "Good idea", .statementIndex = 54 }, // 2 + { .string = "I am too tired", .statementIndex = 61 }, // 3 + { .string = "Beg for mercy", .statementIndex = 78 }, // 4 + { .string = "Run", .statementIndex = 80 }, // 5 }; constexpr int options_length = (sizeof (options)) / (sizeof (options[0])); @@ -117,103 +234,242 @@ const statement statements[] = { { .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 0 sfx/Chime.ogg { .type = type::play, .play = { .audioIndex = 1, /* FIXME channel */ } }, // 1 sfx/MistAmbience.ogg { .type = type::scene, .scene = { .imageIndex = 0 } }, // 2 bgwhite - { .type = type::voice, .voice = { .audioIndex = 2 } }, // 4 n1test.mp3 - { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 0 } }, // 5 n "Far over the mountains of Almystice" - { .type = type::voice, .voice = { .audioIndex = 3 } }, // 6 n2test.mp3 - { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 1 } }, // 7 n "Beyond the tumultuous waters of the Lilac Bay" - { .type = type::voice, .voice = { .audioIndex = 4 } }, // 8 n3test.mp3 - { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 2 } }, // 9 n "And across the vast fields of Alysen" - { .type = type::voice, .voice = { .audioIndex = 5 } }, // 10 n4test.mp3 - { .type = type::play, .play = { .audioIndex = 6, /* FIXME channel */ } }, // 11 music/TinyForestMinstrels.ogg - { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 3 } }, // 12 n "Tiny minstrels can be heard amongst the trees" - { .type = type::scene, .scene = { .imageIndex = 1 } }, // 13 bgforest1 - { .type = type::show, .show = { .imageIndex = 7 } }, // 15 al - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 4 } }, // 16 a "Are we almost there?" - { .type = type::show, .show = { .imageIndex = 6 } }, // 17 ei - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 5 } }, // 18 e "Hmmm... Not really" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 6 } }, // 19 a "How much further have we to go?" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 7 } }, // 20 e "About a day or two of walking" - { .type = type::menu, .menu = { .count = 2, .optionIndex = 0 } }, // 21 "Complain", "Rationalize" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 8 } }, // 22 a "We are still sooo far awayyy" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 9 } }, // 23 e "And it will be even further if we dont stop taking breaks" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 10 } }, // 24 a "Easy for you to say, all you have to carry is a little memory pipe!" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 11 } }, // 25 a "I'm tired ><" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 12 } }, // 26 e "Don't start whining now!" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 13 } }, // 27 e "You need to remember why we have come all this way" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 28 a "I understand... I suppose it is for an important purpose" - { .type = type::jump, .jump = { .statementIndex = 32 } }, // 29 internal jump (b'__menu_end', 0) - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 30 a "I understand... I suppose it is for an important purpose" - { .type = type::jump, .jump = { .statementIndex = 32 } }, // 31 internal jump (b'__menu_end', 0) - { .type = type::jump, .jump = { .statementIndex = 33 } }, // 32 mainbranch1 - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 15 } }, // 33 e "We're almost out of the forest, we can take a little break once we clear the tree line" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 16 } }, // 34 a "Is that where the flora field is?" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 17 } }, // 35 e "Why yes, If I remember correctly, it should be just up ahead" - { .type = type::stop, .stop = { /* FIXME channel */ } }, // 36 - { .type = type::scene, .scene = { .imageIndex = 0 } }, // 37 bgwhite - { .type = type::voice, .voice = { .audioIndex = 7 } }, // 39 n5test.ogg - { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 18 } }, // 40 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 = 8, /* FIXME channel */ } }, // 41 music/PhrygianButterflies.ogg - { .type = type::scene, .scene = { .imageIndex = 2 } }, // 42 bgflower1 - { .type = type::show, .show = { .imageIndex = 6 } }, // 44 ei - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 19 } }, // 45 e "Look at all the butterflies! They are all so pretty!" - { .type = type::show, .show = { .imageIndex = 7 } }, // 46 al - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 20 } }, // 47 a "This place is like a dream..." - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 21 } }, // 48 e "There are so many flowers this time of year" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 22 } }, // 49 e "I told you it would be worth the journey!" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 23 } }, // 50 a "Can we stop for a bit now?" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 24 } }, // 51 e "Of course" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 25 } }, // 52 e "Ya know, Its a shame we didnt save some of those giant strawberries you found" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 26 } }, // 53 a "I told you not to eat them all!" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 27 } }, // 54 e "Yah yah" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 28 } }, // 55 e "Anyways, shall I recite a tale?" - { .type = type::menu, .menu = { .count = 2, .optionIndex = 2 } }, // 56 "Good idea", "I am too tired" - { .type = type::stop, .stop = { /* FIXME channel */ } }, // 57 - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 29 } }, // 58 a "Why dont you sing the story of Eleanor the Hero!" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 30 } }, // 59 e "Let us sing of the Hero Eleanor" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 31 } }, // 60 a "..." - { .type = type::play, .play = { .audioIndex = 9, /* FIXME channel */ } }, // 61 music/Poem1.ogg - { .type = type::pause, .pause = { .duration = 40 } }, // 62 - { .type = type::jump, .jump = { .statementIndex = 67 } }, // 63 internal jump (b'__menu_end', 1) - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 32 } }, // 64 e "Serves you right for scaring those elephant-dogs" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 33 } }, // 65 a "They were asking for it, you know" - { .type = type::jump, .jump = { .statementIndex = 67 } }, // 66 internal jump (b'__menu_end', 1) - { .type = type::jump, .jump = { .statementIndex = 68 } }, // 67 mainbranch2 - { .type = type::hide, .hide = { .imageIndex = 6 } }, // 68 ei - { .type = type::show, .show = { .imageIndex = 5 } }, // 69 catw - { .type = type::show, .show = { .imageIndex = 6 } }, // 70 ei - { .type = type::voice, .voice = { .audioIndex = 10 } }, // 71 placeholdermeow.mp3 - { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 34 } }, // 72 c "Rawrrrr" - { .type = type::hide, .hide = { .imageIndex = 5 } }, // 73 catw - { .type = type::show, .show = { .imageIndex = 4 } }, // 74 cat - { .type = type::play, .play = { .audioIndex = 11, /* FIXME channel */ } }, // 75 music/ScaredMice.ogg - { .type = type::say, .say = { .characterIndex = 3, .stringIndex = 35 } }, // 76 mg "AHHHHHHHHHH!!!!!" - { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 36 } }, // 77 c "Nyanyanyanya" - { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 37 } }, // 78 c "Well, what do we have here? If it isnt 2 little meowse girls, all alone in the flowers" - { .type = type::menu, .menu = { .count = 2, .optionIndex = 4 } }, // 79 "Beg for mercy", "Run" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 38 } }, // 80 a "Please dont eat us!!!" - { .type = type::jump, .jump = { .statementIndex = 85 } }, // 81 internal jump (b'__menu_end', 2) - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 39 } }, // 82 e "Alice dont run, our only chance is through pleading!" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 40 } }, // 83 e "Please dont eat us, miss kitty cat!!! ><" - { .type = type::jump, .jump = { .statementIndex = 85 } }, // 84 internal jump (b'__menu_end', 2) - { .type = type::jump, .jump = { .statementIndex = 86 } }, // 85 mainbranch3 - { .type = type::stop, .stop = { /* FIXME channel */ } }, // 86 - { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 41 } }, // 87 c "I'm not gonna eat you nyanyanya" - { .type = type::play, .play = { .audioIndex = 6, /* FIXME channel */ } }, // 88 music/TinyForestMinstrels.ogg - { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 42 } }, // 89 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 } }, // 90 c "Also, are you minstrels?" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 44 } }, // 91 e "Y-Yes" - { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 45 } }, // 92 a "W-We are on a quest to Castle Alysen..." - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 46 } }, // 93 e "Shh dont tell her that" - { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 47 } }, // 94 c "The Castle of Alysen you say?!?" - { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 48 } }, // 95 c "Why, that's where I am headed!" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 49 } }, // 96 e "You dont say..." - { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 50 } }, // 97 c "Yah I do actually" - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 51 } }, // 98 e "So... Why might you be traveling to the castle?" - { .type = type::scene, .scene = { .imageIndex = 0 } }, // 99 bgwhite - { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 52 } }, // 100 n "nya" - { .type = type::scene, .scene = { .imageIndex = 3 } }, // 101 bgwheatfield1 - { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 53 } }, // 102 e "Look, your right, the castle is just up ahead!" - { .type = type::_return }, // 103 + { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 0 } }, // 4 n "Far over the mountains of Almystice" + { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 1 } }, // 5 n "Beyond the tumultuous waters of the Lilac Bay" + { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 2 } }, // 6 n "And across the vast fields of Alysen" + { .type = type::play, .play = { .audioIndex = 2, /* FIXME channel */ } }, // 7 music/TinyForestMinstrels.ogg + { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 3 } }, // 8 n "Tiny minstrels can be heard amongst the trees" + { .type = type::scene, .scene = { .imageIndex = 1 } }, // 9 bgforest1 + { .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 11 al + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 4 } }, // 12 a "Are we almost there?" + { .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 13 ei + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 5 } }, // 14 e "Hmmm... Not really" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 6 } }, // 15 a "How much further have we to go?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 7 } }, // 16 e "About two more moons" + { .type = type::menu, .menu = { .count = 2, .optionIndex = 0 } }, // 17 "Complain", "Rationalize" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 8 } }, // 18 a "We are still sooo far awayyy" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 9 } }, // 19 e "And it will be even further if you dont stop complaining" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 10 } }, // 20 a "Easy for you to say, all you have to carry is a little memory pipe!" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 11 } }, // 21 a "I'm tired ><" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 12 } }, // 22 e "Don't start whining now!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 13 } }, // 23 e "You need to remember why we have come all this way" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 24 a "I understand... I suppose it is for an important purpose" + { .type = type::jump, .jump = { .statementIndex = 28 } }, // 25 internal jump (b'__menu_end', 0) + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 14 } }, // 26 a "I understand... I suppose it is for an important purpose" + { .type = type::jump, .jump = { .statementIndex = 28 } }, // 27 internal jump (b'__menu_end', 0) + { .type = type::jump, .jump = { .statementIndex = 29 } }, // 28 mainbranch1 + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 15 } }, // 29 e "We're almost out of the forest, we can take a little break once we clear the tree line" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 16 } }, // 30 a "Is that where the flora field is?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 17 } }, // 31 e "Why yes, If I remember correctly, it should be just up ahead" + { .type = type::stop, .stop = { /* FIXME channel */ } }, // 32 + { .type = type::scene, .scene = { .imageIndex = 0 } }, // 33 bgwhite + { .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 34 sfx/Chime.ogg + { .type = type::voice, .voice = { .audioIndex = 3 } }, // 36 n5test.ogg + { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 18 } }, // 37 n "As the minstrel mice girls continue along the path, the forest opens up into a beautiful field of flowers" + { .type = type::play, .play = { .audioIndex = 4, /* FIXME channel */ } }, // 38 music/PhrygianButterflies.ogg + { .type = type::scene, .scene = { .imageIndex = 3 } }, // 39 bgflower1 + { .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 41 ei + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 19 } }, // 42 e "Look at all the butterflies! They are all so pretty!" + { .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 43 al + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 20 } }, // 44 a "This place is like a dream..." + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 21 } }, // 45 e "There are so many flowers this time of year" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 22 } }, // 46 e "I told you it would be worth the journey!" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 23 } }, // 47 a "Can we stop for a bit now?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 24 } }, // 48 e "Of course" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 25 } }, // 49 e "Ya know, Its a shame we didnt save some of those giant strawberries you found" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 26 } }, // 50 a "I told you not to eat them all!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 27 } }, // 51 e "Yah yah" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 28 } }, // 52 e "Anyways, shall I recite a tale?" + { .type = type::menu, .menu = { .count = 2, .optionIndex = 2 } }, // 53 "Good idea", "I am too tired" + { .type = type::stop, .stop = { /* FIXME channel */ } }, // 54 + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 29 } }, // 55 a "Why dont you sing the story of Eleanor the Hero!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 30 } }, // 56 e "Sure" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 31 } }, // 57 a "..." + { .type = type::play, .play = { .audioIndex = 5, /* FIXME channel */ } }, // 58 music/Poem1.ogg + { .type = type::pause, .pause = { .duration = 40 } }, // 59 + { .type = type::jump, .jump = { .statementIndex = 65 } }, // 60 internal jump (b'__menu_end', 1) + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 32 } }, // 61 e "Serves you right for scaring those elephant-dogs" + { .type = type::stop, .stop = { /* FIXME channel */ } }, // 62 + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 33 } }, // 63 a "They were asking for it, you know" + { .type = type::jump, .jump = { .statementIndex = 65 } }, // 64 internal jump (b'__menu_end', 1) + { .type = type::jump, .jump = { .statementIndex = 66 } }, // 65 mainbranch2 + { .type = type::hide, .hide = { .imageIndex = 7 } }, // 66 ei + { .type = type::show, .show = { .imageIndex = 6, .transformIndex = transform::right } }, // 67 catw + { .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 68 ei + { .type = type::voice, .voice = { .audioIndex = 6 } }, // 69 placeholdermeow.mp3 + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 34 } }, // 70 c "Rawrrrr" + { .type = type::hide, .hide = { .imageIndex = 6 } }, // 71 catw + { .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 72 cat + { .type = type::play, .play = { .audioIndex = 7, /* FIXME channel */ } }, // 73 music/ScaredMice.ogg + { .type = type::say, .say = { .characterIndex = 3, .stringIndex = 35 } }, // 74 mg "AHHHHHHHHHH!!!!!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 36 } }, // 75 c "Nyanyanyanya" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 37 } }, // 76 c "Well, what do we have here? If it isn't two little meowse girls, all alone amongst the flowers" + { .type = type::menu, .menu = { .count = 2, .optionIndex = 4 } }, // 77 "Beg for mercy", "Run" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 38 } }, // 78 a "Please don't eat us!!!" + { .type = type::jump, .jump = { .statementIndex = 83 } }, // 79 internal jump (b'__menu_end', 2) + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 39 } }, // 80 e "Alice don't run, our only chance is through pleading!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 40 } }, // 81 e "Please don't eat us, miss kitty cat!!! ><" + { .type = type::jump, .jump = { .statementIndex = 83 } }, // 82 internal jump (b'__menu_end', 2) + { .type = type::jump, .jump = { .statementIndex = 84 } }, // 83 mainbranch3 + { .type = type::stop, .stop = { /* FIXME channel */ } }, // 84 + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 41 } }, // 85 c "I'm not gonna eat you nyanyanya" + { .type = type::play, .play = { .audioIndex = 2, /* FIXME channel */ } }, // 86 music/TinyForestMinstrels.ogg + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 42 } }, // 87 c "I just want to know what two little meowses are doing so very far away from home" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 43 } }, // 88 c "Also, are you minstrels?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 44 } }, // 89 e "Y-Yes" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 45 } }, // 90 a "W-We are on a quest to Castle Alysen..." + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 46 } }, // 91 e "Shh don't tell her that" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 47 } }, // 92 c "The Castle of Alysen you say?!?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 48 } }, // 93 c "Why, that's where I am headed!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 49 } }, // 94 e "You don't say..." + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 50 } }, // 95 c "Yah, I do actually" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 51 } }, // 96 e "So... Why might you be traveling to the castle?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 52 } }, // 97 c "I belong to the lineage of Agrepen" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 53 } }, // 98 e "And what might that mean?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 54 } }, // 99 c "The Agrepens are a long line of felines loyal to the crown of corvidae" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 55 } }, // 100 e "Really? That must mean you are a noble?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 56 } }, // 101 c "Well, not really..." + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 57 } }, // 102 c "My father was one of the queens knights many years ago" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 58 } }, // 103 e "Ah I see" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 59 } }, // 104 e "So do you live at the castle or something?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 60 } }, // 105 c "Well, no..." + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 61 } }, // 106 a "Then why are you traveling to Castle Alysen?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 62 } }, // 107 c "uhhh" + { .type = type::play, .play = { .audioIndex = 1, /* FIXME channel */ } }, // 108 sfx/MistAmbience.ogg + { .type = type::stop, .stop = { /* FIXME channel */ } }, // 109 + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 63 } }, // 110 c "I DONT NEED TO BE PRESSURED BY LITTLE MICE TO SAY ANYTHING!!!!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 64 } }, // 111 c "GOOD DAY!" + { .type = type::hide, .hide = { .imageIndex = 5 } }, // 112 cat + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 65 } }, // 113 a "Wha..." + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 66 } }, // 114 e "Phew, I was scared she was gonna follow us the whole way" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 67 } }, // 115 a "She didn't seem so bad" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 68 } }, // 116 e "Are you kidding? She's a crazy kitty!" + { .type = type::scene, .scene = { .imageIndex = 0 } }, // 117 bgwhite + { .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 118 sfx/Chime.ogg + { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 69 } }, // 120 n "After their encounter with the weird cat, the mice scurry out of the flower field and into the nearby meadow" + { .type = type::scene, .scene = { .imageIndex = 2 } }, // 121 bgforest2 + { .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::right } }, // 123 ei + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 70 } }, // 124 e "I think this is the right way..." + { .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 125 al + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 71 } }, // 126 a "Then where did the path go?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 72 } }, // 127 e "How am I supposed to know?" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 73 } }, // 128 a "Did you hear that?!?!" + { .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 129 ei + { .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 130 cat + { .type = type::play, .play = { .audioIndex = 4, /* FIXME channel */ } }, // 131 music/PhrygianButterflies.ogg + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 74 } }, // 132 c "Hey there..." + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 75 } }, // 133 c "I apologize" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 76 } }, // 134 c "I didn't mean to storm off like that" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 77 } }, // 135 e "Ha ha, no problem..." + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 78 } }, // 136 a "So... Why are you traveling to Castle Alysen?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 79 } }, // 137 e "Alice!!!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 80 } }, // 138 c "If you must know, I have been summoned by the queen" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 81 } }, // 139 c "I suspect that my poor reputation amongst the locals of Eastern Nidus has come back to haunt me" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 82 } }, // 140 c "Though I know not what what she has summoned me for" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 83 } }, // 141 a "Ahhhhhh" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 84 } }, // 142 c "So then..." + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 85 } }, // 143 c "Why are YOU traveling to the Castle?" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 86 } }, // 144 a "We are delivering a feather!!!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 87 } }, // 145 e "Alice no!" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 88 } }, // 146 a "A feather that belonged to the queen herself!!!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 89 } }, // 147 e "Why you little..." + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 90 } }, // 148 c "A feather you say? One of the queens?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 91 } }, // 149 c "How on the face of Al Mot might you have aquired such a thing?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 92 } }, // 150 c "If it is authentic, that is..." + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 93 } }, // 151 e "Since Alice cannot keep a secret, I shall tell you" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 94 } }, // 152 e "Seven moons ago, our town was attacked by three owls and a band of mice from the northern principalities" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 95 } }, // 153 e "Eventually word spread to greater nearby settlements, and so" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 96 } }, // 154 e "Messengers from the keep in Musia sent for aid from the Ravens" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 97 } }, // 155 e "Four moons ago, the request was answered" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 98 } }, // 156 e "And a small group of mice accompanied by two ravens a fox, and three squirrels set out to the northern principalities" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 99 } }, // 157 e "Anyways, long story short, we drove those barbaric rats out of their home" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 100 } }, // 158 a "They arent actual rats you know" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 101 } }, // 159 e "Obviously, but you wont catch me speaking kindly of them" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 102 } }, // 160 a "And you forgot the most important part" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 103 } }, // 161 e "Yah yah, I am getting there" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 104 } }, // 162 e "So, essentially, my brother is trained in archery, and..." + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 105 } }, // 163 a "Speed it up already" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 106 } }, // 164 e "You tell it then!" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 107 } }, // 165 a "My cousin found this feather in one of the highest towers of a castle far to the north" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 108 } }, // 166 c "How do you know it belongs to the queen?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 109 } }, // 167 e "It said so itself above the display on the wall" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 110 } }, // 168 a "Supposedly, it was in a room filled with treasures!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 111 } }, // 169 c "That is very nice and all, but what are the two of you doing out here all alone?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 112 } }, // 170 c "Do you expect every bird in Avia to respect your alliance with Castle Alysen?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 113 } }, // 171 e "What do you mean?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 114 } }, // 172 c "I mean, the two of you probably look like walking dinner to most creatures" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 115 } }, // 173 a "I could go for some dinner..." + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 116 } }, // 174 e "Anyways..." + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 117 } }, // 175 e "To answer your question, upon returning to the village, the feather was taken from my brother by the needle guild" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 118 } }, // 176 e "So... Yesterday, after sundown" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 119 } }, // 177 e "We stole the feather from the guild hall before vanishing into the night" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 120 } }, // 178 e "Can you imagine the look on their stupid faces, when they woke up, and not only is the feather missing" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 121 } }, // 179 e "But so are we!" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 122 } }, // 180 a "Hahahaha" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 123 } }, // 181 c "Are the two of you mad?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 124 } }, // 182 c "I assume you are attempting to return the Queens feather?" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 125 } }, // 183 a "Yes, we intend to deliver the feather to its rightful owner" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 126 } }, // 184 c "Absolute madness!" + { .type = type::say, .say = { .characterIndex = 3, .stringIndex = 31 } }, // 185 mg "..." + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 127 } }, // 186 c "I will follow the two of you" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 128 } }, // 187 c "To keep you safe, that is" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 129 } }, // 188 a "Alright!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 130 } }, // 189 e "Ha ha... Okay..." + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 131 } }, // 190 c "Great! Follow me, I know a shortcut! :3" + { .type = type::hide, .hide = { .imageIndex = 5 } }, // 191 cat + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 132 } }, // 192 a "Sounds good!" + { .type = type::hide, .hide = { .imageIndex = 8 } }, // 193 al + { .type = type::stop, .stop = { /* FIXME channel */ } }, // 194 + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 133 } }, // 195 e "Oh dear!" + { .type = type::hide, .hide = { .imageIndex = 7 } }, // 196 ei + { .type = type::scene, .scene = { .imageIndex = 0 } }, // 197 bgwhite + { .type = type::play, .play = { .audioIndex = 0, /* FIXME channel */ } }, // 198 sfx/Chime.ogg + { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 134 } }, // 200 n "And so the mice girls follow the noble cat further towards their destination" + { .type = type::scene, .scene = { .imageIndex = 4 } }, // 201 bgwheatfield1 + { .type = type::play, .play = { .audioIndex = 8, /* FIXME channel */ } }, // 202 music/WheatFields.ogg + { .type = type::show, .show = { .imageIndex = 5, .transformIndex = transform::right } }, // 203 cat + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 135 } }, // 205 c "Nya" + { .type = type::show, .show = { .imageIndex = 8, .transformIndex = transform::left } }, // 206 al + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 136 } }, // 207 a "Look, your right, the castle is just up ahead!" + { .type = type::show, .show = { .imageIndex = 7, .transformIndex = transform::centerleft } }, // 208 ei + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 137 } }, // 209 e "Wait up" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 138 } }, // 210 c "I told you I knew a shortcut!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 139 } }, // 211 c "Most people take the long way around" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 140 } }, // 212 e "Yah because these are royal wheatfields!" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 141 } }, // 213 a "Who cares?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 142 } }, // 214 e "Are you trying to get us killed?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 143 } }, // 215 e "Its trespassing on royal land!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 144 } }, // 216 c "Calm down, I have done this a million times" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 145 } }, // 217 e "That doesnt make me calm!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 146 } }, // 218 c "How can the rolling fields of wheat not calm your spirit?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 147 } }, // 219 c "You little mice truly are mad!" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 148 } }, // 220 a "I like the wheat!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 149 } }, // 221 e "Shut up!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 150 } }, // 222 c "Sounds like someone needs a nap!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 151 } }, // 223 e "Why? because I'm not insane like you?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 152 } }, // 224 c "Yah, your so sane, that you decided to steal from your town and then run off alone to the country of birds" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 153 } }, // 225 c "The power of friendship wont protect the two of you from becoming dinner" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 154 } }, // 226 c "And that, is why I feel obligated to accompany you!" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 155 } }, // 227 e "Hey, we have a good reason!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 156 } }, // 228 c "And what might that be?" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 157 } }, // 229 e "My brother found the feather, not the town guild, its a matter of family pride!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 158 } }, // 230 c "Pride has touched the chosen meouse" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 159 } }, // 231 c "Flies she towards the Castle" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 160 } }, // 232 c "But her ambition burns far too bright, and silly myice dont have any wings to myelt" + { .type = type::say, .say = { .characterIndex = 3, .stringIndex = 161 } }, // 233 mg "What?" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 162 } }, // 234 c "Nyanyanya" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 163 } }, // 235 c "Nyevermind" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 164 } }, // 236 c "Sing me a song little minstrels!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 165 } }, // 237 c "Very Nyice!" + { .type = type::say, .say = { .characterIndex = 1, .stringIndex = 166 } }, // 238 c "Now tell me little minstrels, what are your names?" + { .type = type::say, .say = { .characterIndex = 0, .stringIndex = 167 } }, // 239 a "My name is Alice" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 168 } }, // 240 e "And my name is Eily" + { .type = type::say, .say = { .characterIndex = 2, .stringIndex = 169 } }, // 241 e "What is your name?" + { .type = type::say, .say = { .characterIndex = 5, .stringIndex = 170 } }, // 242 l "My name is Leona!" + { .type = type::scene, .scene = { .imageIndex = 0 } }, // 243 bgwhite + { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 171 } }, // 245 n "And so, the odd trio walked through the wheatfields and towards the castle walls" + { .type = type::say, .say = { .characterIndex = 4, .stringIndex = 172 } }, // 246 n "Upon approaching the castle walls" + { .type = type::_return }, // 247 }; constexpr int statements_length = (sizeof (statements)) / (sizeof (statements[0])); diff --git a/include/renpy/vulkan.h b/include/renpy/vulkan.h index 93e3fbf..0c9a41e 100644 --- a/include/renpy/vulkan.h +++ b/include/renpy/vulkan.h @@ -5,8 +5,24 @@ namespace renpy { VkImageView imageView; }; + struct ImageInstance { + struct { + uint16_t width; + uint16_t height; + } size; + struct { + uint16_t x; + uint16_t y; + } topLeft; + uint16_t imageIndex; + }; + + static_assert((sizeof (ImageInstance)) == 10); + struct vulkan { static constexpr int perVertexSize = (4) * 2; + static constexpr int perInstanceSize = (sizeof (ImageInstance)); + static constexpr int maximumImageCount = 16; // externally initialized, opaque handle VkInstance instance; @@ -27,6 +43,12 @@ namespace renpy { VkPipeline pipeline; VertexIndex vertexIndex; + VkDeviceSize instanceBufferOffset[2]; + VkBuffer instanceBuffer; + VkDeviceMemory instanceMemory; + VkDeviceSize instanceMemorySize; + ImageInstance * instanceMappedData; + VkDescriptorPool descriptorPool{ VK_NULL_HANDLE }; static constexpr int descriptorSetLayoutCount = 1; VkDescriptorSetLayout descriptorSetLayouts[descriptorSetLayoutCount]; // unrelated to maxFrames, unrelated to descriptorCount @@ -51,6 +73,7 @@ namespace renpy { void load_image_inner(VkCommandBuffer commandBuffer, VkFence fence, int i, char const * filename); void load_images(); void create_pipeline(); + void create_instance_buffers(); void draw(VkCommandBuffer commandBuffer, uint32_t frameIndex); diff --git a/renpy-parser/transform.py b/renpy-parser/transform.py index 4b85dc9..16b3657 100644 --- a/renpy-parser/transform.py +++ b/renpy-parser/transform.py @@ -110,6 +110,14 @@ def pass1(state, ast): if type(ast) in simple_statement_types: state.statements.append(ast) +transforms_set = { + b"left", + b"centerleft", + b"center", + b"centerright", + b"right", +} + def pass2_statement(state, pc, statement): if type(statement) is parse.Play: comment = statement.path.lexeme.decode('utf-8') @@ -136,8 +144,11 @@ def pass2_statement(state, pc, statement): elif type(statement) is parse.Show: key = lhs_key(statement.what) image_index = state.images_lookup[key] + transform = statement.transform.lexeme + assert transform in transforms_set + transform = transform.decode('utf-8') comment = ".".join(k.decode('utf-8') for k in key) - yield f"{{ .type = type::show, .show = {{ .imageIndex = {image_index} }} }}, // {pc} {comment}" + yield f"{{ .type = type::show, .show = {{ .imageIndex = {image_index}, .transformIndex = transform::{transform} }} }}, // {pc} {comment}" elif type(statement) is InternalMenu: count = len(statement.menu.entries) option_index = statement.entry_index @@ -227,9 +238,10 @@ def pass2_options(state): yield "constexpr int options_length = (sizeof (options)) / (sizeof (options[0]));" def pass2(state): - yield "#include \"statement.h\"" + yield "#include \"renpy/language.h\"" yield "" - yield "namespace language {" + yield "namespace renpy::script {" + yield "using namespace renpy::language;" yield from pass2_strings(state) yield from pass2_characters(state) yield from pass2_audio(state) diff --git a/shader/font.hlsl b/shader/font.hlsl index 0a56838..4d7c7bf 100644 --- a/shader/font.hlsl +++ b/shader/font.hlsl @@ -29,7 +29,7 @@ struct VSOutput VSOutput VSMain(VSInput input) { float2 inverseTexel = float2(1.0 / 256.0, 1.0 / 256.0); - float2 inversePixel = float2(1.0 / 1024.0, 1.0 / 1024.0); + float2 inversePixel = float2(1.0 / 1280.0, 1.0 / 720.0); int index = input.InstanceGlyph; VSOutput output = (VSOutput)0; diff --git a/shader/renpy.hlsl b/shader/renpy.hlsl index 01522a0..ee001a7 100644 --- a/shader/renpy.hlsl +++ b/shader/renpy.hlsl @@ -5,12 +5,17 @@ struct VSInput { float2 Position : POSITION0; float2 Texture : TEXCOORD0; + // per-instance + uint2 Size : Size; + uint2 TopLeft : TopLeft; + uint TextureIndex : TextureIndex; }; struct VSOutput { float4 Position : SV_POSITION; float2 Texture : NORMAL0; + uint TextureIndex : TextureIndex; }; [shader("vertex")] @@ -19,6 +24,7 @@ VSOutput VSMain(VSInput input) VSOutput output = (VSOutput)0; output.Position = float4(input.Position, 0, 1); output.Texture = input.Texture; + output.TextureIndex = input.TextureIndex; return output; } @@ -26,6 +32,6 @@ VSOutput VSMain(VSInput input) [shader("pixel")] float4 PSMain(VSOutput input) : SV_TARGET { - float4 color = Texture[1].Sample(ClosestSampler, input.Texture); + float4 color = Texture[input.TextureIndex].Sample(ClosestSampler, input.Texture); return float4(color.xyz, 1.0); } diff --git a/src/font/outline.cpp b/src/font/outline.cpp index 8b5d0ae..0859cd0 100644 --- a/src/font/outline.cpp +++ b/src/font/outline.cpp @@ -521,7 +521,7 @@ namespace font::outline { VkMemoryRequirements memoryRequirements; vkGetBufferMemoryRequirements(device, instanceBuffer, &memoryRequirements); - VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; + VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; VkMemoryAllocateFlags memoryAllocateFlags{}; VkDeviceSize stride; allocateFromMemoryRequirements(device, @@ -542,7 +542,7 @@ namespace font::outline { } ////////////////////////////////////////////////////////////////////// - // create instance buffer + // create glyphs buffer ////////////////////////////////////////////////////////////////////// void font::create_glyphs_buffer(types::font const * const font, types::glyph const * const glyphs) @@ -645,7 +645,7 @@ namespace font::outline { .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .memory = instanceMemory, .offset = 0, - .size = (sizeof (GlyphInstance)), + .size = (sizeof (GlyphInstance)) * outputIndex, } }; alignMappedMemoryRanges(physicalDeviceProperties.limits.nonCoherentAtomSize, diff --git a/src/main.cpp b/src/main.cpp index fb0fcff..28c926a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1073,9 +1073,8 @@ int main() //minecraft_state.draw(commandBuffer, frameIndex); - font_state.draw(commandBuffer, frameIndex); - renpy_state.draw(commandBuffer, frameIndex); + font_state.draw(commandBuffer, frameIndex); vkCmdEndRendering(commandBuffer); diff --git a/src/renpy/vulkan.cpp b/src/renpy/vulkan.cpp index eef9669..83e50b8 100644 --- a/src/renpy/vulkan.cpp +++ b/src/renpy/vulkan.cpp @@ -59,6 +59,7 @@ namespace renpy { load_images(); write_descriptor_sets(); create_pipeline(); + create_instance_buffers(); } ////////////////////////////////////////////////////////////////////// @@ -261,6 +262,50 @@ namespace renpy { &commandBuffer); } + ////////////////////////////////////////////////////////////////////// + // create instance buffer + ////////////////////////////////////////////////////////////////////// + + void vulkan::create_instance_buffers() + { + constexpr VkDeviceSize bufferSize{ maximumImageCount * (sizeof (ImageInstance)) }; + instanceMemorySize = bufferSize * 2; + instanceBufferOffset[0] = bufferSize * 0; + instanceBufferOffset[1] = bufferSize * 1; + + // create buffer + VkBufferCreateInfo bufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = instanceMemorySize, + .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + VK_CHECK(vkCreateBuffer(device, &bufferCreateInfo, nullptr, &instanceBuffer)); + + // allocate memory + + VkMemoryRequirements memoryRequirements; + vkGetBufferMemoryRequirements(device, instanceBuffer, &memoryRequirements); + VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; + VkMemoryAllocateFlags memoryAllocateFlags{}; + VkDeviceSize stride; + allocateFromMemoryRequirements(device, + physicalDeviceProperties.limits.nonCoherentAtomSize, + physicalDeviceMemoryProperties, + memoryRequirements, + memoryPropertyFlags, + memoryAllocateFlags, + 1, + &instanceMemory, + &stride); + + VK_CHECK(vkBindBufferMemory(device, instanceBuffer, instanceMemory, 0)); + + // map memory + + VK_CHECK(vkMapMemory(device, instanceMemory, 0, VK_WHOLE_SIZE, 0, (void **)&instanceMappedData)); + } + ////////////////////////////////////////////////////////////////////// // pipeline ////////////////////////////////////////////////////////////////////// @@ -367,16 +412,21 @@ namespace renpy { .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT }; - constexpr int vertexBindingDescriptionsCount = 1; + constexpr int vertexBindingDescriptionsCount = 2; VkVertexInputBindingDescription vertexBindingDescriptions[vertexBindingDescriptionsCount]{ { .binding = 0, .stride = perVertexSize, .inputRate = VK_VERTEX_INPUT_RATE_VERTEX }, + { + .binding = 1, + .stride = perInstanceSize, + .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE + }, }; - constexpr int vertexAttributeDescriptionsCount = 2; + constexpr int vertexAttributeDescriptionsCount = 5; VkVertexInputAttributeDescription vertexAttributeDescriptions[vertexAttributeDescriptionsCount]{ // per-vertex { // position @@ -391,6 +441,25 @@ namespace renpy { .format = VK_FORMAT_R16G16_SFLOAT, .offset = 4, }, + // per-instance + { + .location = 2, + .binding = 1, + .format = VK_FORMAT_R16G16_UINT, + .offset = 0, + }, + { + .location = 3, + .binding = 1, + .format = VK_FORMAT_R16G16_UINT, + .offset = 4, + }, + { + .location = 4, + .binding = 1, + .format = VK_FORMAT_R16_UINT, + .offset = 8, + }, }; VkPipelineVertexInputStateCreateInfo vertexInputState{ @@ -429,6 +498,32 @@ namespace renpy { void vulkan::draw(VkCommandBuffer commandBuffer, uint32_t frameIndex) { + int outputIndex = 0; + // update + instanceMappedData[maximumImageCount * frameIndex + outputIndex++] = { + .size = {1280, 720}, + .topLeft = {0, 0}, + .imageIndex = 3, + }; + + // flush + constexpr int mappedMemoryRangesCount = 1; + VkMappedMemoryRange mappedMemoryRanges[mappedMemoryRangesCount]{ + { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = instanceMemory, + .offset = 0, + .size = (sizeof (ImageInstance)) * outputIndex, + } + }; + alignMappedMemoryRanges(physicalDeviceProperties.limits.nonCoherentAtomSize, + instanceMemorySize, + mappedMemoryRangesCount, + mappedMemoryRanges); + vkFlushMappedMemoryRanges(device, mappedMemoryRangesCount, mappedMemoryRanges); + + // draw + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); VkDescriptorSet descriptorSets[1] = { @@ -442,10 +537,10 @@ namespace renpy { 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); + VkDeviceSize vertexOffsets[2]{ 0, instanceBufferOffset[frameIndex] }; + VkBuffer vertexBuffers[2]{ vertexIndex.buffer, instanceBuffer }; + vkCmdBindVertexBuffers(commandBuffer, 0, 2, vertexBuffers, vertexOffsets); - vkCmdDrawIndexed(commandBuffer, 4, 1, 0, 0, 0); + vkCmdDrawIndexed(commandBuffer, 4, outputIndex, 0, 0, 0); } }