Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Nowaha committed Nov 1, 2024
0 parents commit b771ee8
Show file tree
Hide file tree
Showing 16 changed files with 587 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# c#
bin
obj

# gd
.import
.mono
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# WebWardrobe

Easily store and switch between different outfits!\
[<img src="res/switch.gif" width="550"/>](res/switch.gif)\
<sub>A [WEBFISHING](https://store.steampowered.com/app/3146520/WEBFISHING/) mod using [GDWeave](https://github.com/NotNite/GDWeave/), with a combination of `C#` and `GDScript`.</sub>

## Files
Your saved outfits are put in your save file directory, in a separate file called `outfits.json`. *Your original save file is safe!*\*\
<sub>* Your last applied outfit will be saved to your player file</sub>

## Installation
> **NOTE:** GDWeave is required to use the mod, which can be found [here](https://github.com/NotNite/GDWeave/) if you do not have it yet.
[Download the latest release](https://github.com/Nowaha/WebWardrobe/releases/latest/download/WebWardrobe.zip) and extract it inside of your `GDWeave/mods` folder, so that it exists as its own folder in there alongside your other mods.

You should end up with this structure:
```
GDWeave/
mods/
WebWardrobe/
manifest.json
WebWardrobe.dll
WebWardrobe.pck
```
16 changes: 16 additions & 0 deletions c#/WebWardrobe.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebWardrobe", "WebWardrobe\WebWardrobe.csproj", "{1A273855-C2D0-40BB-B22C-CE17AA4D0A9B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1A273855-C2D0-40BB-B22C-CE17AA4D0A9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A273855-C2D0-40BB-B22C-CE17AA4D0A9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A273855-C2D0-40BB-B22C-CE17AA4D0A9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A273855-C2D0-40BB-B22C-CE17AA4D0A9B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
64 changes: 64 additions & 0 deletions c#/WebWardrobe/CosmeticMenuMod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using GDWeave.Godot;
using GDWeave.Godot.Variants;
using GDWeave.Modding;

namespace WebWardrobe;

internal class CosmeticMenuMod : IScriptMod
{
public bool ShouldRun(string path) => path == "res://Scenes/HUD/CosmeticMenu/cosmetic_menu.gdc";

public IEnumerable<Token> Modify(string path, IEnumerable<Token> tokens)
{
// $HBoxContainer / misc.modulate = Color(0.7, 0.7, 0.7) if category != "misc" else Color(1.0, 1.0, 1.0)
var test = new MultiTokenWaiter([
t => t is IdentifierToken { Name: "misc" },
t => t is IdentifierToken { Name: "modulate" },
t => t.Type == TokenType.Newline,
], allowPartialMatch: true);

foreach (var token in tokens)
{
if (test.Check(token))
{
yield return token;

// $HBoxContainer / outfits.modulate = Color(0.7, 0.7, 0.7) if category != "outfits" else Color(1.0, 1.0, 1.0)
yield return new Token(TokenType.Dollar);
yield return new IdentifierToken("HBoxContainer");
yield return new Token(TokenType.OpDiv);
yield return new IdentifierToken("outfits");
yield return new Token(TokenType.Period);
yield return new IdentifierToken("modulate");
yield return new Token(TokenType.OpAssign);
yield return new Token(TokenType.BuiltInType, 14); // Color
yield return new Token(TokenType.ParenthesisOpen);
yield return new ConstantToken(new RealVariant(0.7));
yield return new Token(TokenType.Comma);
yield return new ConstantToken(new RealVariant(0.7));
yield return new Token(TokenType.Comma);
yield return new ConstantToken(new RealVariant(0.7));
yield return new Token(TokenType.ParenthesisClose);
yield return new Token(TokenType.CfIf);
yield return new IdentifierToken("category");
yield return new Token(TokenType.OpNotEqual);
yield return new ConstantToken(new StringVariant("outfits"));
yield return new Token(TokenType.CfElse);
yield return new Token(TokenType.BuiltInType, 14); // Color
yield return new Token(TokenType.ParenthesisOpen);
yield return new ConstantToken(new RealVariant(1.0));
yield return new Token(TokenType.Comma);
yield return new ConstantToken(new RealVariant(1.0));
yield return new Token(TokenType.Comma);
yield return new ConstantToken(new RealVariant(1.0));
yield return new Token(TokenType.ParenthesisClose);
yield return new Token(TokenType.Newline, 1);
}
else
{
// return the original token
yield return token;
}
}
}
}
56 changes: 56 additions & 0 deletions c#/WebWardrobe/HudMod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using GDWeave.Godot;
using GDWeave.Godot.Variants;
using GDWeave.Modding;

namespace WebWardrobe;

internal class HudMod : IScriptMod
{
public bool ShouldRun(string path) => path == "res://Scenes/HUD/playerhud.gdc";

public IEnumerable<Token> Modify(string path, IEnumerable<Token> tokens)
{
var topOfFile = new MultiTokenWaiter([
t => t.Type == TokenType.Newline,
t => t.Type == TokenType.Newline,
], allowPartialMatch: true);

// if popups == [] and not OptionsMenu.open:
var check = new MultiTokenWaiter([
t => t.Type == TokenType.CfIf,
t => t is IdentifierToken { Name: "popups" },
t => t.Type == TokenType.OpEqual,
t => t.Type == TokenType.OpAnd,
t => t.Type == TokenType.OpNot,
t => t is IdentifierToken { Name: "open" }
], allowPartialMatch: true);

foreach (var token in tokens)
{
if (topOfFile.Check(token))
{
yield return token;

// var typing = false
yield return new Token(TokenType.PrVar);
yield return new IdentifierToken("typing");
yield return new Token(TokenType.OpAssign);
yield return new ConstantToken(new BoolVariant(false));
yield return new Token(TokenType.Newline);
} else if (check.Check(token))
{
yield return token;

// and not typing
yield return new Token(TokenType.OpAnd);
yield return new Token(TokenType.OpNot);
yield return new IdentifierToken("typing");
}
else
{
// return the original token
yield return token;
}
}
}
}
15 changes: 15 additions & 0 deletions c#/WebWardrobe/Mod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using GDWeave;

namespace WebWardrobe;

public class Mod : IMod {

public Mod(IModInterface modInterface) {
modInterface.RegisterScriptMod(new HudMod());
modInterface.RegisterScriptMod(new CosmeticMenuMod());
}

public void Dispose() {
// Cleanup anything you do here
}
}
23 changes: 23 additions & 0 deletions c#/WebWardrobe/WebWardrobe.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblySearchPaths>$(AssemblySearchPaths);$(GDWeavePath)/core</AssemblySearchPaths>
</PropertyGroup>

<ItemGroup>
<Reference Include="GDWeave" Private="false"/>
<Reference Include="Serilog" Private="false"/>
</ItemGroup>

<Target
Name="PostBuild"
AfterTargets="PostBuildEvent"
Condition="'$(GDWeavePath)' != ''">
<Exec
Command="xcopy /Y /I &quot;$(TargetDir)&quot; &quot;$(GDWeavePath)/mods/$(AssemblyName)&quot;"
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))'"
/>
</Target>
</Project>
25 changes: 25 additions & 0 deletions c#/WebWardrobe/WebWardrobe.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebWardrobe", "WebWardrobe.csproj", "{20661229-EA27-4911-9CC9-40B0C55B92E2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{20661229-EA27-4911-9CC9-40B0C55B92E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20661229-EA27-4911-9CC9-40B0C55B92E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20661229-EA27-4911-9CC9-40B0C55B92E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20661229-EA27-4911-9CC9-40B0C55B92E2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D13EFD86-374A-417A-BC9F-F6F1A7F98DE0}
EndGlobalSection
EndGlobal
42 changes: 42 additions & 0 deletions gd/export_presets.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[preset.0]

name="Windows Desktop"
platform="Windows Desktop"
runnable=true
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path=""
script_export_mode=1
script_encryption_key=""

[preset.0.options]

custom_template/debug=""
custom_template/release=""
binary_format/64_bits=true
binary_format/embed_pck=false
texture_format/bptc=false
texture_format/s3tc=true
texture_format/etc=false
texture_format/etc2=false
texture_format/no_bptc_fallbacks=true
codesign/enable=false
codesign/identity_type=0
codesign/identity=""
codesign/password=""
codesign/timestamp=true
codesign/timestamp_server_url=""
codesign/digest_algorithm=1
codesign/description=""
codesign/custom_options=PoolStringArray( )
application/modify_resources=true
application/icon=""
application/file_version=""
application/product_version=""
application/company_name=""
application/product_name=""
application/file_description=""
application/copyright=""
application/trademarks=""
99 changes: 99 additions & 0 deletions gd/mods/WebWardrobe/main.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
extends Node

var saved_outfits: Dictionary = {}

const file_path = "user://outfits.json"

func _ready():
load_outfits_from_file()

func load_outfit(name: String) -> bool:
if not saved_outfits.has(name): return false

var player = find_player()
if player == null: return false

PlayerData.cosmetics_equipped = saved_outfits[name].duplicate()
player._change_cosmetics()
return true

func save_outfit(name: String) -> bool:
saved_outfits[name] = PlayerData.cosmetics_equipped.duplicate()
save_outfits_to_file()
return true

func del_outfit(name: String):
saved_outfits.erase(name)

func _input(event):
if Input.is_action_just_pressed("menu_open"):
inject()

func inject():
var player = find_player()
if player == null: return

var hud: Node = player.hud
if hud == null: return

var main: Node = hud.get_child(0)
if main == null: return

var outfit: Node = main.find_node("outfit")

var button_container: HBoxContainer = outfit.get_child(1)
if button_container.get_child_count() != 4: return

var outfit_button: Button = create_button("outfits", "OUTFITS")
outfit_button.modulate = Color(0.7, 0.7, 0.7)
outfit_button.size_flags_horizontal = 3
outfit_button.connect("pressed", self, "_button_clicked", [outfit])
button_container.add_child(outfit_button)

var tabs: Node = outfit.get_child(2).get_child(0)

var outfits_tab = load("res://mods/WebWardrobe/outfits.tscn").instance()
outfits_tab.name = "outfits"
outfits_tab._setup(self)
tabs.add_child(outfits_tab)


func _button_clicked(menu):
menu._change_tab("outfits")





# Utils
func find_player() -> Node:
var nodes = get_tree().get_nodes_in_group("player")

if nodes.empty(): return null
return nodes[0]

func create_button(name: String, text: String = name) -> Button:
var button: Button = Button.new()
button.name = name
button.text = text
return button

func save_outfits_to_file() -> void:
var file = File.new()
if file.open(file_path, File.WRITE) == OK:
var json = to_json(saved_outfits)
file.store_string(json)
file.close()
print("Saved outfits to file!")
else:
print("Failed to save outfits")

func load_outfits_from_file() -> void:
var file = File.new()
if file.open(file_path, File.READ) == OK:
var json = file.get_as_text()
saved_outfits = parse_json(json)
file.close()
print("Loaded outfits from file!")
else:
print("Failed to load outfits")
Loading

0 comments on commit b771ee8

Please sign in to comment.