diff --git a/game/options.rpy b/game/options.rpy index 68e301f..c279012 100644 --- a/game/options.rpy +++ b/game/options.rpy @@ -49,7 +49,11 @@ define config.version = "1.0" define gui.about = _p("""A game created in 3 days for Touhou Fan Game Jam 13. -By Jacoder23, Shyraku, Nanossis, and hermit_irl.""") +By Jacoder23, Shyraku, Nanossis, and hermit_irl. + +Uses EasyRenPyGui and Achievements for Ren'Py both by Feniks Development +Uses Renpy Auto Highlight and Kinetic Text Tags both by Wattson +Uses Lint+ by KigyoDev""") # TODO: REPLACE THE ABOUT WITH NEW CREDITS WHEN POSSIBLE diff --git a/game/screens/other_screens.rpy b/game/screens/other_screens.rpy index dba3b51..5aee48a 100644 --- a/game/screens/other_screens.rpy +++ b/game/screens/other_screens.rpy @@ -9,10 +9,6 @@ ## Text that is placed on the game's about screen. Place the text between the ## triple-quotes, and leave a blank line between paragraphs. -define gui.about = _p(""" -EasyRenPyGui is made by {a=https://github.com/shawna-p}Feniks{/a} {a=https://feniksdev.com/}@feniksdev.com{/a} -""") - screen about(): @@ -33,11 +29,7 @@ screen about(): label "[config.name!t]" text _("Version [config.version!t]\n") - if gui.about: - text "[gui.about!t]\n" - - text _("Made with {a=https://www.renpy.org/}Ren'Py{/a} [renpy.version_only].\n\n[renpy.license!t]") - + text "[gui.about!t]\n" style about_label_text: size 36 diff --git a/game/script.rpy b/game/script.rpy index 6f9e757..1c67aa2 100644 --- a/game/script.rpy +++ b/game/script.rpy @@ -23,8 +23,19 @@ image reimu happy: label start: - label presentation_begins: + python: + # 0 means intro + # 1 means icebreaker onwards + # 2 means hands-on lecture onwards + # 3 means consultation onwards + # 4 means exam onwards + day = 0 + label presentation_begins: + # TODO + # TEXT Y + # STAGE N + # VAR Y scene bg auditorium show yuuka happy: @@ -58,10 +69,12 @@ label start: yuuka "I will be getting to that, please do not leave your seats throughout this presentation. This will be brief." - jump intro - label intro: - scene bg entrance with fade + # TODO + # TEXT Y + # STAGE N + # VAR Y + scene bg redfield with fade show yuuka happy: xalign 0.5 @@ -71,15 +84,344 @@ label start: yuuka "What the hell am I looking at?" + yuuka "{i}Spring. It was the season for blooming petals, and I had come to one of my second-favorite flower fields in Gensokyo.{/i}" (cb_name="") + + yuuka "This used to be a field of red buckwheat flowers, growing ever so softly towards the sky blue." + + show yuuka happy: + ease 1.8 ypos 1280 + + yuuka "Tell me fallen fauna, by whose footsoles were you trampled?" + + window hide + $ renpy.pause(1.5) + # play a wind sound + + show yuuka happy: + ease 1.6 ypos 1080 + + yuuka "So it was that scientist from the longgone past?" + + yuuka "Very well, then." + + yuuka "I will have to wait until the next spring to enjoy your company, young flowers." + + show yuuka happy: + easein 0.7 xpos 960 + easein 0.7 xpos 1060 + easein 0.7 xpos 1160 + easein 0.7 xpos 1260 + easein 0.7 xpos 1360 + easein 0.7 xpos 1460 + easein 0.7 xpos 1560 + easein 0.7 xpos 1660 + easein 0.7 xpos 1760 + easein 0.7 xpos 1860 + easein 0.7 xpos 1960 + easein 0.7 xpos 2060 + easein 0.7 xpos 2160 + easein 0.7 xpos 2260 + easein 0.7 xpos 2360 + easein 0.7 xpos 2460 + easein 0.7 xpos 2560 + + yuuka "In the meantime, I think that woman's bones would make for good fertilizer." + + label entrance: + scene bg entrance with fade + if day == 0: + # TODO + # TEXT Y + # STAGE N + # VAR N + yuuka "{i}When I first laid eyes on the school, I found its exterior to be less than appealing to me.{/i}" (cb_name="") + + yuuka "What?" + + yuuka "{i}Now I'm no stranger to different architectural sensibilities, I've had the privilege of owning mansion myself, but we all ought to admit...{/i}" (cb_name="") + + yuuka "The hell am I looking at?" + + yuuka "{i}This school is very out of place for Gensokyo.{/i}" (cb_name="") + + yuuka "Gaudiness aside, this is absolutely where that outsider is." + + yuuka "Now, to call her out here." + + "???" "Miss!" + + yuuka "?" + + "???" "It's a good thing you're here, on time! Do you need directions?" + + yuuka "I think you have the wrong person, dear." + + "???" "You're the subsitute teacher right? I can show you to your class." + + menu: + "What will Yuuka do?" + + "Make an entrance": + # TODO + # TEXT N + # STAGE N + # VAR N + + yuuka "That won't be necessary, after all..." + + yuuka "I've got a landmark don't I?" + + narrator "A sunflower, each petal the size of a desk, grew out from beneath the stunned student and lifted thme into the air." + + narrator "The student rolled off, falling onto the soft grass, as the sunflower grew to eclipse the building." + + "Go along with it": + # TODO + # TEXT Y + # STAGE N + # VAR N + + yuuka "Yes, that would be very helpful." + + narrator "Led by the student, Yuuka entered the building." + + scene bg hallway with fade + + yuuka "It's very..." + + "???" "Modern, isn't it? It's Outside World-inspired." + + yuuka "Oh, I'm sure it's more than just inspired by it." + + scene bg classroom with fade + + narrator "The student, leading Yuuka to her class, held the door open for her, letting her get a peek inside." + + yuuka "Oh, would you look at that? I think there's somewhere else I need to be, th—" + + "???" "The substitute's here!" + + "Various Students" "Oh finally. I don't think there's enough time even for a quiz.{nw=1}" + + "Various Students" "I thought we're excused if they're late?{nw=1}" + + "Various Students" "She's kind of fugly with that weird outfit.{nw=1}" + + "Various Students" "Huh, what's happening? Is it a test?{nw=1}" + + yuuka "{sc}Amusing{/sc} as a misunderstanding this is, I really should be—" + + marisa "Hey teach, how about we get a move on before lunch's on!" + + yuuka "..." + + yuuka "{i}It was at this moment that I took the time actually pick out individual faces from the crowd.{/i}" (cb_name="") + + yuuka "{i}There was Marisa Kirisame, irritance, thief, and perpetual child as per usual.{/i}" (cb_name="") + + yuuka "{i}Sitting next to her was Alice Margatroid, hatchling spawn of Makai, and the relatively coolheaded one of the three.{/i}" (cb_name="") + + yuuka "{i}Last was Reimu Hakurei, sitting in the back, with a glint in her eye staring back at me.{/i}" (cb_name="") + + yuuka "{i}She seemed to be itching for a fight.{/i}" (cb_name="") + + reimu "A misunderstanding?" (cb_name="") + + yuuka "{i}While I was and forever will be confident in my ability to subdue our militant shrine maiden, I am a person who can...{/i}" (cb_name="") + + yuuka "{i}...play along, so long as the result is the same. I put on my most polite smile and...{/i}" (cb_name="") + + yuuka "Yes, class! I'm afraid there will be no tests or lectures today." + + narrator "Barely contained cheering erupted throughout the room." + + yuuka "Instead we'll be having an {sc}icebreaker{/sc}." + + narrator "Barely contained sighs and groans erupted throughout the room." + + yuuka "You there, in the red and white! Introduce yourself and tell us an interesting fact about yourself." + + yuuka "{i}I could be patient as long as it meant seeing these three flounder.{/i}" (cb_name="") + + reimu "I— uh, what?" + + narrator "Yuuka's smile grew wider." + + yuuka "You heard me." + + narrator "There were barely stifled giggles from Marisa's seat as Reimu looked at Yuuka like she cloned herself." + + narrator "That's Reimu realized her folly, allowing Yuuka to use her new assumed position to do something unforgivable: mildly inconviencing her." + + narrator "Her hand went to her gohei before she remembered all the students around her." + + reimu "Well, I'm Reimu Hakurei. I'm a shrine maiden." + + reimu "Uh, interesting facts..." + + narrator "A lull fell for a moment as Reimu collected her thoughts." + + yuuka "We haven't got all day, Ms. Hakurei." + + reimu "Well that's... I guess I'm ambidextrous?" + + narrator "Marisa then broke out into laughter, unfazed by Alice's glance, and Reimu's melting glare." + + marisa "I-I can't, oh my god, she's the teacher? They hired HER?" + + yuuka "Yes, they did. You're up next, blondie. I want TWO interesting facts now." + + marisa "Oh, crap." + + yuuka "{i}The rest of our time left in the class went swimmingly.{/i}" (cb_name="") + + yuuka "{i}Some of you may think I was overly hostile, judgemental, and sadistic on my first day.{/i}" (cb_name="") + + yuuka "{i}To that I will say: I could've gone farther. I exercised excellent restraint.{/i}" (cb_name="") + + yuuka "{i}Afterwards, the trio I named earlier pulled me aside.{/i}" (cb_name="") + + reimu "What do you think you're doing, exactly, Yuuka?" + + yuuka "I think you'll find I'm doing exactly as people expect of me." + + reimu "Terrorizing and intimidating humans?" + + marisa "Sending minions to do your dirty work?" + + alice "Standing in the middle of a field ominously?" + + yuuka "I've not done those since this morning. You know how routines get." + + yuuka "I'm merely here to take a debt owed." + + yuuka "You?" + + reimu "Hm, this school just showed up one day. Apparently it's Yumemi's." + + marisa "Been a long time since that huh? The scramble for a wish, exploring those ruins..." + + yuuka "{i}Marisa glanced back and forth between all of us.{/i}" (cb_name="") + + alice "I wasn't there." + + yuuka "Neither was I." + + marisa "Great times." + + reimu "Right, anyways we're just here to find her. There've been noise complaints from the Human Village about what they're getting up to here." + + marisa "Plus, all of the students? They're not from the village. They didn't even know about Reimu! We're thinking this is a major breach of the barrier." + + alice "But, there's otherwise no evidence of anything other than some disturbances caused by this school." + + yuuka "Other than the flower field they destroyed?" + + yuuka "{i}I paused for a response. I was angry. I was here for retribution.{/i}" (cb_name="") + + reimu "...that's new." + + yuuka "It's how I found out about this wretched place." + + reimu "In any case, we'll keep it in mind. We're going to keep attending as students until we figure this out." + + yuuka "Good, and I will be looking for Yumemi." + + narrator "Marisa chuckled." + + marisa "Yuuka, who do you think you were substituting for?" + + yuuka "...huh?" + + marisa "She's out for the week. No one told you, {bt}professor{/bt}?" + + narrator "Yuuka grit her teeth and snarled, Marisa stepping back instinctively." + + yuuka "This charade is absurd and I've apparently no reason to be here. Good day to you all." + + yuuka "{i}I stomped my way back through the hallway, turning a corner before Alice caught up to me.{/i}" (cb_name="") + + alice "Don't you think you're missing an opportunity?" + + yuuka "An opportunity to what?" + + alice "If you stick around, which by the way would be easy given the students have already accepted the fact that you're their teacher," + + alice "You can just sit tight for a week and Yumemi will walk right up to you." + + yuuka "I could lay a trap." + + alice "Exactly." + + yuuka "A tempting offer, but I don't think I'll need any tricks to deal with her." + + yuuka "{i}Once more I turned away, and—{/i}" (cb_name="") + + alice "Oh for Shinki's sake, are you scared of something?" + + yuuka "What?" + + alice "You talk to humans less than I do and I live in a poisonous swamp forest." + + alice "You've barely talked to anyone other than us three after leaving your mansion in Mugenkan years ago." + + yuuka "I talk with Medicine." + + alice "She barely counts as an exception." + + alice "Look, this is the first time I've seen you talk with humans without it being some kind of underlying threat. This could be good for you." + + yuuka "I—" + + alice "Before anything else, it's also a week. Just a week. At least come in tomorrow and give it a shot?" + + yuuka "..." + + yuuka "I do not agree with your points, Alice." + + yuuka "But I consider you to know me better than most." + + yuuka "Very well, I'll play along." + + alice "Thank you." + + yuuka "I'll be back tomorrow. Goodbye Alice." + + alice "Goodbye." + elif day == 1: + # TODO + # TEXT N + # STAGE N + # VAR N + window show # placeholder + elif day == 2: + # TODO + # TEXT N + # STAGE N + # VAR N + window show # placeholder + elif day == 3: + # TODO + # TEXT N + # STAGE N + # VAR N + window show # placeholder + elif day == 4: + # TODO + # TEXT N + # STAGE N + # VAR N + window show # placeholder + else: + "Day is currently set to [day]." + + + jump ending label ending: - narrator "This is the part where the ending goes." - - narrator "You understand?" - - $ kicked_out.grant() + # $ kicked_out.grant() # $ scare_tactics.grant() # $ very_loud_quitting.grant() # $ she_has_your_back.grant() diff --git a/game/wordcounter.rpy b/game/wordcounter.rpy new file mode 100644 index 0000000..41a7858 --- /dev/null +++ b/game/wordcounter.rpy @@ -0,0 +1,229 @@ +############################################################################################################### +### LINT+ REN'PY WORD COUNTER ################################################################################ +############################################################################################################### +# +# Thanks for downloading Lint+! ( https://kigyo.itch.io/renpy-word-counter ) +# Below are a bunch of variables that let you customize the generated output according to your needs. +# +# If you like this tool, consider dropping a donation: https://ko-fi.com/kigyodev +# Paying just $1 on itch.io also lets you access an additional feature: label-based statistics! +# +# Thank you, and may this tool help track your progress better than ever! :D +# - KigyoDev + +## Preferences: Counters ################################################################################################ + +# Line and word counts for every character +# default value: True +define wordcounter_characters = True + +# Line and word counts for each .rpy file +# default value: True +define wordcounter_files = True + +# Line and word counts for every character, within each .rpy file +# default value: True +define wordcounter_character_files = True + +# Number of menus and available choices in the game +# default value: True +define wordcounter_menu_choices = True + +# Show character/symbol counts, which might be too much detail - otherwise only displays number of dialogue blocks and words +# default value: False +define wordcounter_display_character_count = False + +# DONATE TO UNLOCK: +# Line and word counts for every label +# default value: True +define wordcounter_labels = True + + +# The old character statistics which only displays line counts +# default value: False +define config.lint_character_statistics = False + +## Preferences: File paths ################################################################################################ + +# Name of the folder your script files are in, if any. This can make the generated output look nicer. +# Example: "script/" +# default value: "" +define script_folder_path = "" + +# List of folders and files you want to ignore. +# Example: if you want to make sure nothing in the "unused" folder and the "script.rpy" file is counted, write ["unused", "script.rpy"] +# Note: Be careful with unintentionally matching filenames! Ignoring "no" will also ignore any folders and file names containing "no". +# default value: [] +define script_ignore_path = [] + + +## Preferences: Characters ################################################################################################ + +# List of equivalent characters that should be counted as one. Given ("x", "y"), "y" will be counted as "x". +# Example: if "ann", "xann", and "nann" should all be considered "ann", write [("ann", "xann"), ("ann", "nann")] +# default value: [] +define wordcounter_same = [] + +# List of characters who should be hidden from the character statistics. They still contribute to the total file word count. +# default value: ["extend"] +define wordcounter_hidden = ["extend"] + +# List of characters who should not count towards any word counts. +# default value: [] +define wordcounter_uncounted = [] + + +############################################################################################################### +### The Code ################################################################################################## +############################################################################################################### + +init python: + + import collections + + # The main function + def wordcounter(): + + all_stmts = list(renpy.game.script.all_stmts) + all_stmts.sort(key=lambda n : n.filename) + + charastats = collections.defaultdict(Count) + filestats = collections.defaultdict(Count) + filecharastats = {} + + menu_count = 0 + options_count = 0 + + unignored_name = "game/" + script_folder_path + "every file combined" + + filecharastats[unignored_name] = collections.defaultdict(Count) + + for node in all_stmts: + if isinstance(node, renpy.ast.Say): + speaker = node.who + for i in wordcounter_same: + if i[1] == speaker: + speaker = i[0] + break + + if not_ignored_path(node.filename) and speaker not in wordcounter_uncounted: + filestats[unignored_name].add(node.what) + filestats[node.filename].add(node.what) + + if node.filename not in filecharastats: + filecharastats[node.filename] = collections.defaultdict(Count) + + if speaker not in wordcounter_hidden: + charastats[speaker if speaker else 'narrator' ].add(node.what) + filecharastats[node.filename][speaker if speaker else 'narrator' ].add(node.what) + + elif isinstance(node, renpy.ast.Menu): + menu_count += 1 + for l, c, b in node.items: + options_count += 1 + + if renpy.config.developer and wordcounter_characters: + print("\n") + report_character_stats(charastats) + + if renpy.config.developer and wordcounter_files: + print("\n") + report_file_stats(filestats) + + if renpy.config.developer and wordcounter_character_files: + print("\n") + report_file_chara_stats(filestats, filecharastats) + + if renpy.config.developer and wordcounter_menu_choices: + print("\n") + report_menu_stats(menu_count, options_count) + + # This makes sure the above function is actually called whenever you use Lint + config.lint_hooks.append(wordcounter) + + def not_ignored_path(filename): + for i in script_ignore_path: + if i in filename: + return False + return True + + # The print functions: + def report_character_stats(charastats, title = True): + + if title: + print("Character statistics:") + + count_to_char = collections.defaultdict(list) + + for char in charastats: + count_to_char[charastats[char].blocks].append(char) + + for count, chars in sorted(count_to_char.items(), reverse=True): + chars.sort() + + if len(chars) == 1: + start = chars[0] + " has " + end = humanize(charastats[chars[0]].words) + elif len(chars) == 2: + start = chars[0] + " and " + chars[1] + " have " + end = humanize(charastats[chars[0]].words) + " and " + humanize(charastats[chars[1]].words) + else: + start = ", ".join(chars[:-1]) + ", and " + chars[-1] + " have " + end = "" + for char in chars[:-1]: + end += humanize(charastats[char].words) + ", " + end += "and " + humanize(charastats[chars[-1]].words) + + print(" * " + start + humanize(count) + + (" block" if count == 1 else " blocks") + " of dialogue, and " + + end + " words" + (" each." if len(chars) > 1 else ".") ) + + def report_file_stats(filestats): + + print("File statistics:") + + count_to_char = collections.defaultdict(list) + + for file in filestats: + print(" * [" + file[5+len(script_folder_path):] + "] contains " + humanize(filestats[file].blocks) + + " dialogue blocks and " + humanize(filestats[file].words) + " words.") + + def report_file_chara_stats(filestats, filecharastats): + + print("Detailed File statistics:") + + count_to_char = collections.defaultdict(list) + + for file in filestats: + print("[" + file[5+len(script_folder_path):] + "] contains " + humanize(filestats[file].blocks) + + " dialogue blocks and " + humanize(filestats[file].words) + " words:") + report_character_stats(filecharastats[file], False) + print("") + + def report_menu_stats(menu_count, options_count): + + print("Menu statistics:") + + print("The game has " + str(menu_count) + " menus, with a total of " + str(options_count) + " possible choices, \nfor an average of " + + "{:,.2f}".format(options_count and options_count/menu_count or 0) + " choices per menu.") + + # Auxiliary functions directly copied from lint.py - I take no credit for these: + def humanize(n): + s = str(n) + rv = [] + for i, c in enumerate(reversed(s)): + if i and not (i % 3): + rv.insert(0, ',') + rv.insert(0, c) + return ''.join(rv) + + class Count(object): + def __init__(self): + self.blocks = 0 + self.words = 0 + self.characters = 0 + + def add(self, s): + self.blocks += 1 + self.words += len(s.split()) + self.characters += len(s)