Fix UI issues.

Tighten up mod display to include more buttons at once, showing the original while in android.
Expands mod buttons to fill the space the menu panel provides. Redone mod button images to fit aesthetically
Imports misc files for modding, including the readme and flags
This commit is contained in:
Map 2024-10-05 18:12:28 -05:00
parent 8dfa492442
commit e54c950ef6
11 changed files with 142 additions and 62 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 541 B

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 913 B

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 645 B

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 541 B

BIN
game/gui/mod_frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

View file

0
game/mods/-NOMODS.txt Normal file
View file

View file

@ -1,55 +1,120 @@
To normal users installing multiple mods: It's recommended to only have one mod folder installed in the mods directory in case two mods have duplicate `green_fang_story` label, otherwise the game will error out if mods conflict each other, this includes not only labels but images & sound too. --- MOD LOADER USAGE ---
The game loads an alternate storyline.rpy, this allows you to control the flow of the game's storytelling If there's problems with installed mods - like if an enabled mod makes the game crash on startup - there's 3 file flags you can apply to work around it, checked only in the root of the mods folder:
Examples include: - Any file starting with "DISABLEALLMODS". This will force disable all currently installed mods, but will still load all their metadata. Removing this flag will return the mod states to how it was previously.
- You want to inject more stuff inbetween chapters, maybe you hate time skip writing - Any file starting with "NOLOADORDER". This will erase the load order/states and always load mods in folder alphabetical order, with mod states depending on the "Enable New Mods" option in the preferences menu.
- You want to have more of an after story kind of ordeal, for example expanding Ending 2 - Any file starting with "NOMODS". This turns off mod loading entirely by making the game not find any metadata files to load.
- You want to replace the entire story route These file flags already exist in the mods folder, but renamed to not trigger in-game. Enable them by renaming the file to take off the first hyphen.
You can still call the vanilla game's chapters like the intro (call chapter_1) for example but you might want to either copy the vanilla scripts and mix in your edits to have full control
You will need to learn bit of Ren'Py & bit of actual Python anyways but you don't have to think too hard in learning anything new other than familiarizing with this game's script.rpy's already defined Character objects and images, you can freely ignore most of the UI and so on for the inexperienced but passionate artist and/or writer. When ordering the mods in the mod menu, know that not all mod code will be loaded according to the order. Ren'Py has a feature called 'init' that will run code at certain mod-defined stages (priorities) of the engine starting up, so if one mod's init block is set to run at an earlier priority than another mod's, it doesn't what order it is in the mod loader, it will always load that init first. The only time when the order comes into effect is if 2 mod's init blocks run at the same priority, or aren't running in an init block.
Textbox limitation: ~300 characters / four lines, the maximum in the vanilla game's script is around roughly ~278 and that only barely overflows to four lines, so <200 characters / three lines of text should be fine.
--- Ideal file structure of your mod --- --- MOD CREATION PRE-REQUSITES ---
Before modding, you may want to do either of these things:
-Download Snoot Game as a renpy project and launch through the SDK so you can have easy access to debugging and other QoL tools, including dev mode. (Download the repo in this link: https://git.cavemanon.xyz/Cavemanon/SnootGame)
-Open script.rpy and put 'config.developer = True' somewhere in the 'init python' block to have access to renpy's dev mode.
Pick the latter option if you're lazy, since you don't seriously need to use the SDK for most things.
--- MOD CRREATION ---
When creating a mod, make a new folder within the 'mods' directory at the root of the game directory and name it whatever you want. Inside that folder, make a file called 'metadata.json', and follow the JSON file format to implement details about your mod. An example would be:
```
{
"ID": "234234u9SDjjasdhl23",
"Name": "Test Mod",
"Label": "test_mod_label",
"Version": "1.0",
"Authors": [ "Author1", "Author2", "Author3" ],
"Links": "Link1",
"Description": "This contains the description of my mod"
}
```
Make sure there isn't a comma at the end of the last entry in your JSON.
Below is all the possible entries you can put in, and explanations for what they do. Note that you don't need to put all of these in your metadata file, and infact the only hard requirement is the "ID" entry.
"ID" : The ID of your mod. Required to be able to load your mod at all, as it is used by the mod loader for mod orders and enabling/disabling. Make this as unique of a string as you can, like a hash. Smash your keyboard if you must. This is how the game knows to differentiate your mod from others (And can be used by other mods to find if a user has your mod installed, if they so choose).
"Name" : The name of your mod. If this doesn't exist, the game will assume the name of your mod folder.
"Label" : The label to jump to start your mod story. If this doesn't exist, the button this mod will appear as will do nothing when clicked. Useful if you're only modifying something relating to the base game.
"Version" : The version number of your mod.
"Authors" : The authors of your mod. This can be a list of strings or just a string. If it's just a string, it will display in the mod details pane with only "Author:" instead of "Authors:"
"Links" : The links to download your mod and/or advertisement. This can be a list of strings or just a string. If it's just a string, it will display in the mod details pane with only "Link:" instead of "Links:". In order for this to be useful, use the 'a' text tag to make these texts hyperlinks.
"Description" : The description of your mod.
"Mobile Description" : The description of your mod, but only appearing while playing Snoot on Android. If this doesn't exist, it will assume the contents of the description entry. Otherwise, you can copy your description text here and format it however you think it fits for Android.
"Display" : How your mod button appears if there's an icon image detected. This can be set to "name" - which only displays the mod name - "icon" - which only displays the icon, taking up the entire button - or "both" - which displays the name and icon together, with the icon miniaturized and to the side of the name. This defaults to "both" if it doesn't exist, and if an icon image is not present, it will fall back to "name" mode.
"Thumbnail Displayable" : What displayable to use for the thumbnail when the mod has loaded scripts. If this doesn't exist, the game will use the thumbnail image found alongside your metadata file
"Icon Displayable" : What displayable to use for the icon when the mod has loaded scripts. If this doesn't exist, the game will use the icon image found alongside your metadata file
"Screenshot Displayables" : What displayables to use for screenshots when the mod has loaded scripts. This should be a list of strings. The game will choose each displayable in the list over images found alongside the metadata file sequentially, so if there's 5 screenshot images and 3 screenshot displayables, the last 2 will still display the screenshot images, and the first 3 will display the screenshot displayables. If there's more displayables than images, then the mod will appear to gain screenshots in the mod details pane when the mod is enabled. If you enter empty strings in the list ( "" ), the game will interpret that as a deliberate skipping over to load screenshot images instead of displayables, so if your list consists of '[ "", "my_displayable" ]' and there's any number of screenshot images found, the first screenshot will still show a screenshot image, and only the next one will show a displayable. If this entry doesn't exist, it will just use the screenshot images found alongside your metadata file.
In the same directory as your metadata file, there's image files you can put in the to make your mod more appealing. These can be any of Ren'Py's supported image file types, so the file extension here is just for demonstration:
-'thumbnail.png' will appear as a banner for your mod, at the top of the mod details pane
-'icon.png' will show a small image next your mod name or take up the entire button depending on what your "Display" entry is set to.
-'screenshot(number).png' will show screenshots at the bottom of the mod details pane. The '(number)' is a placeholder for a number that represents what order your screenshots appear in. For example, you can have 'screenshot1.png', 'screenshot2.png', and 'screenshot3.png' in your mod directory, and they will all appear in the mod details pane in order. These numbers don't need to be strictly sequential, they can be any number as long as they are integers, and the order will be derived from ASCII ordering.
As the game loads the metadata, it will also collect scripts for loading. When you make your mod scripts, ALL OF THEM NEED TO HAVE .rpym EXTENSIONS. This is important for being able to control mods with the mod loader, otherwise there would be mod conflicts galore. If you use .rpy extensions to make your mod scripts, they will work, but they will not be manageable by the mod loader. This means you should only use them for development, or if you're not using the mod loader anyway - such as adding a translation to the base game.
Additionally, Ren'Py will not load any files from the mods folder automatically, so any and all audio/image/video files need to be manually defined to be usable.
--- TRANSLATION ---
For disambiguation, a "language code" refers to the code Ren'Py uses to refer to languages program-wide, and can be found on the folder names in "game/tl", with "None" being the default language (Representing a fallback language, but usually meaning English. Internally, it's an actual None variable)
For translating mods, you should use the Ren'Py SDK to automatically generate translations from mod scripts. Make sure to use a language code that Snoot supports when you do so, so you don't accidentally create a new language. Put them into an organized directory in your mod and change them to use .rpym extensions so that translations don't activate unless your mod is as well (This prevents potential conflicts from other mods from interface/string translations).
For assets, the easiest way is to put them in the "tl" folder and have the same filepath to your mod asset as it is from the "game" folder to your asset, just like how the officially supported translations are. This will make your mod less portable, but at least there's no namespace or file conflicts for translating images. If you're deadset on making your mod portable, you'll have to make conditionals in your mod to swap out images depending on the language, which means other people translating your mod can't just add a file somewhere and be done with it, they will need to edit your mod scripts as well.
For translating metadata, you can translate the .json file by creating a new .json and naming it "metadata_(language code).json" (Ex: "metadata_es.json" for spanish). Fill out the .json how you would with the normal metadata file, but with your translated strings. The game will automatically pick this up and replace the strings (or add, if there's no 'None' variant of an entry) according to your language. Note that translating the ID or Label will do nothing, so you will always need a metadata.json file with at least an ID entry even if your mod isn't in english.
For the images found alongisde your metadata, you can do the same thing as your .json to replace them. Simply append your language code to your image filename like "thumbnail_es.png", "icon_es.png", etc.. For screenshots, the number in your filename will determine if any given translated image will replace another image, or add it inbetween images. So if you have "screenshot4.png", and you have "screenshot2_es.png" and "screenshot5_es.png", all screenshots will show up if your language is in spanish. If there's "screenshot4_es.png" in the directory, then it will replace "screenshot4.png". The final screenshot display will always take the english screenshots as a base before replacing the images with whatever translated images that exist.
--- TIPS ---
The Ren'Py documentation is your friend, but it is also a bitch-ass friend. It will sometimes be notoriously unhelpful, so consult other renpy dudes from the Ren'Py discord, your snoot communities, or youtube. This may also be of interest, as it will link to other documentation as well as many interesting libraries you can use:
https://github.com/methanoliver/awesome-renpy
Link to Ren'Py documentation:
https://www.renpy.org/doc/html/
When making the file structure for your mod, this is the ideal. Keep it nice and organized, preferably keeping the root of your mods folder clear of eveything but metadata related stuff:
```
In the root of the mods folder: In the root of the mods folder:
folder_of_your_mod_name folder_of_your_mod_name
- name_of_storyline.rpy - metadata.json
- Your various image metadatas
-> asset_folder -> asset_folder
- asset.png - asset.png
-> script_folder -> script_folder
- script.rpy - script.rpym
-> etc. folders...
name_of_storyline.rpy - etc. files...
``` ```
init python: To start your mod, just make it under the label you defined in your metadata file in a mod script. An example in a newly created "mymodscript.rpym":
# Modding Support variables ```
# All mod rpy files must have title of their mod (this shows up on a button) label my_mod_label:
# and finally the label that controls the flow of dialogue scene my_background
show my_sprite
mod_menu_access += [{ "blah blah dialogue"
'Name': "Mod Name",
'Label': "mod_storyline"
}];
image template_sample = Image("mods/folder_of_your_mod_name/asset_folder/asset.png")
label mod_storyline:
call chapter_1_new
``` ```
script_folder/script.rpy Textbox limitation: ~300 characters / four lines, the maximum in the vanilla game's script is around roughly ~278 and that only barely overflows to four lines, so <200 characters / three lines of text should be fine.
```
label chapter_1_new:
show template_sample at scenter
"Sample Text"
hide template_sample If a user has many mods installed, it would be annoying to need to keep track of which mods to enable or disable because of code conflicts, so make your variables/functions unique. Since Ren'Py's own scripting doesn't support namespaced stuff, a very simple way is to prefix all your variables/functions with a mod-unique name, similar to your mod ID. An example would be "mymodisfantastic2349234_function_or_variable". Kinda ugly, but manageable with Ctrl-F and string replacement if there's problems. If there must be code conflicts, let the user know in your mod description!
play music 'audio/OST/Those Other Two Weirdos.ogg'
show anon neutral flip at aright with dissolve
A "Sample Text"
return
```
The funny thing is I don't even like 'fanfictions' to begin with but this mod support allows these 'fanfictions', ironic.

View file

@ -708,10 +708,10 @@ screen mod_menu():
use mod_menu_top_buttons(_("Return"), ShowMenu("extras")) use mod_menu_top_buttons(_("Return"), ShowMenu("extras"))
viewport: viewport:
xpos 1338 xpos 1260
ypos 179 ypos 200
xmaximum 540 xmaximum 600
ymaximum 790 ymaximum 869
scrollbars "vertical" scrollbars "vertical"
vscrollbar_unscrollable "hide" vscrollbar_unscrollable "hide"
@ -731,7 +731,12 @@ screen mod_menu():
at truecenter at truecenter
style_prefix None style_prefix None
spacing 5 spacing 5
ysize 200
if renpy.variant(["mobile", "steam_deck"]):
ysize 200
else:
ysize 160
# Move mod up button # Move mod up button
if i!=0: if i!=0:
button: button:
@ -741,8 +746,8 @@ screen mod_menu():
activate_sound "audio/ui/snd_ui_click.wav" activate_sound "audio/ui/snd_ui_click.wav"
idle_foreground Transform("gui/button/menubuttons/up.png",xalign=0.5,yalign=0.5) idle_foreground Transform("gui/button/menubuttons/up.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#445ABB"))
hover_foreground Transform("gui/button/menubuttons/up.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#fbff18")) hover_foreground Transform("gui/button/menubuttons/up.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#00FF03"))
action Function(swapMods, i, i-1) action Function(swapMods, i, i-1)
else: else:
add Null(30,30) at truecenter add Null(30,30) at truecenter
@ -750,16 +755,21 @@ screen mod_menu():
button: button:
at truecenter at truecenter
style_prefix "main_menu" style_prefix "main_menu"
# Manual adjustment to make the arrow buttons closer to the mod toggle button
if not renpy.variant(["mobile", "steam_deck"]):
ysize 65
action Function(toggle_persistent_mods, i) action Function(toggle_persistent_mods, i)
activate_sound "audio/ui/snd_ui_click.wav" activate_sound "audio/ui/snd_ui_click.wav"
add "gui/button/menubuttons/checkbox.png" xalign 0.5 yalign 0.5 add "gui/button/menubuttons/checkbox.png" xalign 0.5 yalign 0.5
if persistent.enabled_mods[i][1]: if persistent.enabled_mods[i][1]:
idle_foreground Transform("gui/button/menubuttons/check.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#32a852")) idle_foreground Transform("gui/button/menubuttons/check.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#00ff40"))
hover_foreground Transform("gui/button/menubuttons/check.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#fbff18")) hover_foreground Transform("gui/button/menubuttons/check.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#ffffff"))
else: else:
idle_foreground Transform("gui/button/menubuttons/cross.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#a83232")) idle_foreground Transform("gui/button/menubuttons/cross.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#db1a1a"))
hover_foreground Transform("gui/button/menubuttons/cross.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#fbff18")) hover_foreground Transform("gui/button/menubuttons/cross.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#ffffff"))
# Move mod down button # Move mod down button
if i!=len(mod_menu_metadata)-1: if i!=len(mod_menu_metadata)-1:
@ -770,8 +780,8 @@ screen mod_menu():
action Function(swapMods, i, i+1) action Function(swapMods, i, i+1)
activate_sound "audio/ui/snd_ui_click.wav" activate_sound "audio/ui/snd_ui_click.wav"
idle_foreground Transform("gui/button/menubuttons/down.png",xalign=0.5,yalign=0.5) idle_foreground Transform("gui/button/menubuttons/down.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#445ABB"))
hover_foreground Transform("gui/button/menubuttons/down.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#fbff18")) hover_foreground Transform("gui/button/menubuttons/down.png",xalign=0.5,yalign=0.5,matrixcolor=TintMatrix("#00FF03"))
else: else:
add Null(30,30) at truecenter add Null(30,30) at truecenter
@ -801,11 +811,10 @@ screen mod_menu():
action NullAction() action NullAction()
frame: frame:
xsize 350 xsize 475
ymaximum 2000 ymaximum 2000
if mod_button_enabled: if mod_button_enabled:
background Frame("gui/button/menubuttons/template_idle.png", 12, 12) background Frame("gui/button/menubuttons/template_idle.png", 12, 12)
hover_background Transform(Frame("gui/button/menubuttons/template_idle.png", 12, 12), matrixcolor = BrightnessMatrix(0.1))
else: else:
background Transform(Frame("gui/button/menubuttons/template_idle.png", 12, 12),matrixcolor=SaturationMatrix(0.5)) background Transform(Frame("gui/button/menubuttons/template_idle.png", 12, 12),matrixcolor=SaturationMatrix(0.5))
@ -832,18 +841,24 @@ screen mod_menu():
# Only here for backwards compatibility to legacy mods # Only here for backwards compatibility to legacy mods
for x in mod_menu_access: for x in mod_menu_access:
hbox: hbox:
xsize 129
ysize 129
add Null(88) add Null(88)
vbox:
if renpy.variant(["mobile", "steam_deck"]):
ysize 200
else:
ysize 160
button: button:
at truecenter at truecenter
activate_sound "audio/ui/snd_ui_click.wav" activate_sound "audio/ui/snd_ui_click.wav"
action Start(x["Label"]) action Start(x["Label"])
frame: frame:
xsize 350 xsize 475
ymaximum 2000 ymaximum 2000
background Frame("gui/button/menubuttons/template_idle.png", 12, 12) background Frame("gui/button/menubuttons/template_idle.png", 12, 12)
padding (5, 5) padding (5, 5)
hover_background Transform(Frame("gui/button/menubuttons/template_idle.png", 12, 12), matrixcolor = BrightnessMatrix(0.1))
text x["Name"] xalign 0.5 yalign 0.5 size 34 textalign 0.5 text x["Name"] xalign 0.5 yalign 0.5 size 34 textalign 0.5
else: else:
@ -862,9 +877,9 @@ screen mod_menu():
if mod_metadata != {}: if mod_metadata != {}:
viewport: viewport:
xmaximum 1190 xmaximum 1190
ymaximum 930 ymaximum 1030
xpos 10 xpos 15
ypos 140 ypos 40
scrollbars "vertical" scrollbars "vertical"
vscrollbar_unscrollable "hide" vscrollbar_unscrollable "hide"
mousewheel True mousewheel True