├── .gitattributes ├── .gitignore ├── .gitmodules ├── LICENSE ├── MMHOOK_Celeste.dll ├── README.md ├── RainbowMod.sln ├── RainbowMod ├── Content │ ├── Dialog │ │ └── English.txt │ └── Graphics │ │ └── Atlases │ │ └── Gameplay │ │ └── characters │ │ └── player │ │ ├── dab.png │ │ ├── foxbangs00.png │ │ ├── foxbangs01.png │ │ ├── foxbangs02.png │ │ ├── foxhair00.png │ │ ├── foxhair01.png │ │ └── skateboard.png ├── Properties │ └── AssemblyInfo.cs ├── RainbowMod.csproj ├── RainbowModule.cs ├── RainbowModuleSettings.cs ├── everest.yaml ├── packages.config └── settings.celeste └── rainbowdeline.gif /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Everest"] 2 | path = Everest 3 | url = https://github.com/EverestAPI/Everest.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Everest Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MMHOOK_Celeste.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EverestAPI/RainbowMod/HEAD/MMHOOK_Celeste.dll -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Everest RainbodMod 2 | 3 | ### License: MIT 4 | 5 | ---- 6 | 7 | Sample mod - Make Madeline's hair and dash be rainbows. 8 | 9 | ![preview](/rainbowdeline.gif) 10 | 11 | [Install Everest](https://everestapi.github.io/), download the mod `.zip` [here](/releases) and place it in your `Mods` directory. Don't extract anything. 12 | 13 | For more information about mod _development_, read the [Everest README.md](https://github.com/EverestAPI/Everest/blob/master/README.md) 14 | -------------------------------------------------------------------------------- /RainbowMod.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RainbowMod", "RainbowMod\RainbowMod.csproj", "{8CBA6958-CD94-44B4-970B-5FC3DBE6B019}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Celeste.Mod.mm", "Everest\Celeste.Mod.mm\Celeste.Mod.mm.csproj", "{D5D0239D-FF95-4897-9484-1898AB7E82F5}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {8CBA6958-CD94-44B4-970B-5FC3DBE6B019}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {8CBA6958-CD94-44B4-970B-5FC3DBE6B019}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {8CBA6958-CD94-44B4-970B-5FC3DBE6B019}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {8CBA6958-CD94-44B4-970B-5FC3DBE6B019}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {D5D0239D-FF95-4897-9484-1898AB7E82F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {D5D0239D-FF95-4897-9484-1898AB7E82F5}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {D5D0239D-FF95-4897-9484-1898AB7E82F5}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {D5D0239D-FF95-4897-9484-1898AB7E82F5}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /RainbowMod/Content/Dialog/English.txt: -------------------------------------------------------------------------------- 1 | # NOTES: 2 | # The # Symbol at the start of a line counts as a Comment. To include in dialog, use a \# 3 | # The . Symbol will cause a pause unless escaped with \. (ex: Mr. Oshiro has a pause, Mr\. Oshiro does not) 4 | # Newlines automatically create a Page Break, unless there is an {n} command on the previous line 5 | # Commands: Anything inside of curly braces {...} is a command and should not be translated. 6 | 7 | # Inline Text Commands: 8 | # {~}wavy text{/~} 9 | # {!}impact text{/!} 10 | # {>> x}changes speed at which characters are displayed{>>} 11 | # {# 000000}this text is black{#} (uses HEX color values) 12 | # {+MENU_BEGIN} inserts the dialog from the MENU_BEGIN value (in English, "CLIMB") 13 | # {n} creates a newline, without a page break 14 | # {0.5} creates a 0.5 second pause 15 | # {big}this text is large{/big} 16 | 17 | # Gameplay Control Commands (should never change) 18 | # {trigger x} this triggers an in-game event 19 | # {anchor} controls the visual position of the textbox in-game 20 | 21 | # Rainbow Module Options 22 | MODOPTIONS_RAINBOWMODULE_TITLE= Fabeline 23 | MODOPTIONS_RAINBOWMODULE_ENABLED= Enabled 24 | MODOPTIONS_RAINBOWMODULE_SPEED= Speed 25 | MODOPTIONS_RAINBOWMODULE_RELOADCOLORS= Reload Color Settings 26 | -------------------------------------------------------------------------------- /RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/dab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EverestAPI/RainbowMod/HEAD/RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/dab.png -------------------------------------------------------------------------------- /RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/foxbangs00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EverestAPI/RainbowMod/HEAD/RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/foxbangs00.png -------------------------------------------------------------------------------- /RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/foxbangs01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EverestAPI/RainbowMod/HEAD/RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/foxbangs01.png -------------------------------------------------------------------------------- /RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/foxbangs02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EverestAPI/RainbowMod/HEAD/RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/foxbangs02.png -------------------------------------------------------------------------------- /RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/foxhair00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EverestAPI/RainbowMod/HEAD/RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/foxhair00.png -------------------------------------------------------------------------------- /RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/foxhair01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EverestAPI/RainbowMod/HEAD/RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/foxhair01.png -------------------------------------------------------------------------------- /RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/skateboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EverestAPI/RainbowMod/HEAD/RainbowMod/Content/Graphics/Atlases/Gameplay/characters/player/skateboard.png -------------------------------------------------------------------------------- /RainbowMod/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RainbowMod")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RainbowMod")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8cba6958-cd94-44b4-970b-5fc3dbe6b019")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /RainbowMod/RainbowMod.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {8CBA6958-CD94-44B4-970B-5FC3DBE6B019} 7 | Library 8 | Properties 9 | Celeste.Mod.Rainbow 10 | RainbowMod 11 | v4.5.2 12 | 512 13 | Artifact\ 14 | 15 | 16 | 17 | 18 | true 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | true 22 | full 23 | AnyCPU 24 | prompt 25 | 4 26 | 27 | 28 | bin\Release\ 29 | TRACE 30 | true 31 | true 32 | pdbonly 33 | AnyCPU 34 | prompt 35 | 4 36 | 37 | 38 | 39 | ..\Everest\lib-stripped\Celeste.exe 40 | False 41 | 42 | 43 | ..\Everest\lib-stripped\FNA.dll 44 | False 45 | 46 | 47 | ..\MMHOOK_Celeste.dll 48 | False 49 | 50 | 51 | ..\packages\Mono.Cecil.0.10.0\lib\net40\Mono.Cecil.dll 52 | False 53 | 54 | 55 | ..\packages\Mono.Cecil.0.10.0\lib\net40\Mono.Cecil.Mdb.dll 56 | False 57 | 58 | 59 | ..\packages\Mono.Cecil.0.10.0\lib\net40\Mono.Cecil.Pdb.dll 60 | False 61 | 62 | 63 | ..\packages\Mono.Cecil.0.10.0\lib\net40\Mono.Cecil.Rocks.dll 64 | False 65 | 66 | 67 | ..\Everest\lib-stripped\Steamworks.NET.dll 68 | False 69 | 70 | 71 | 72 | 73 | ..\Everest\lib\YamlDotNet.dll 74 | False 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | {d5d0239d-ff95-4897-9484-1898ab7e82f5} 85 | Celeste.Mod.mm 86 | False 87 | 88 | 89 | 90 | 91 | PreserveNewest 92 | 93 | 94 | 95 | 96 | 97 | Content\Dialog\English.txt 98 | 99 | 100 | Content\Graphics\Atlases\Gameplay\characters\player\foxbangs00.png 101 | 102 | 103 | Content\Graphics\Atlases\Gameplay\characters\player\foxbangs01.png 104 | 105 | 106 | Content\Graphics\Atlases\Gameplay\characters\player\foxbangs02.png 107 | 108 | 109 | Content\Graphics\Atlases\Gameplay\characters\player\foxhair00.png 110 | 111 | 112 | Content\Graphics\Atlases\Gameplay\characters\player\foxhair01.png 113 | 114 | 115 | Content\Graphics\Atlases\Gameplay\characters\player\skateboard.png 116 | 117 | 118 | Content\Graphics\Atlases\Gameplay\characters\player\dab.png 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /RainbowMod/RainbowModule.cs: -------------------------------------------------------------------------------- 1 | using Celeste.Mod; 2 | using FMOD.Studio; 3 | using Microsoft.Xna.Framework; 4 | using Monocle; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using On.Celeste; 12 | using Mono.Cecil.Cil; 13 | 14 | namespace Celeste.Mod.Rainbow { 15 | public class RainbowModule : EverestModule { 16 | 17 | public static RainbowModule Instance; 18 | 19 | public override Type SettingsType => typeof(RainbowModuleSettings); 20 | public static RainbowModuleSettings Settings => (RainbowModuleSettings) Instance._Settings; 21 | 22 | private static int trailIndex = 0; 23 | 24 | private static List FoxBangs; 25 | private static List FoxHair; 26 | 27 | private static MTexture Skateboard; 28 | private readonly static Vector2 SkateboardPlayerOffset = new Vector2(0, -3); 29 | 30 | private static MTexture Dab; 31 | private readonly static Vector2 DabPlayerOffset = new Vector2(0, -5); 32 | 33 | public RainbowModule() { 34 | Instance = this; 35 | } 36 | 37 | public override void LoadSettings() { 38 | base.LoadSettings(); 39 | 40 | bool updated = false; 41 | 42 | if (Settings.FoxColorLight.A == 0) { 43 | Settings.FoxColorLight = new Color(0.8f, 0.5f, 0.05f, 1f); 44 | updated = true; 45 | } 46 | if (Settings.FoxColorDark.A == 0) { 47 | Settings.FoxColorDark = new Color(0.1f, 0.05f, 0f, 1f); 48 | updated = true; 49 | } 50 | 51 | if (updated) { 52 | SaveSettings(); 53 | } 54 | } 55 | 56 | public override void Load() { 57 | // This is a runtime mod, but we can still manipulate the game. 58 | 59 | #if false // Set this to true if you want to demo new HookGen features. 60 | 61 | // Let's hook Celeste.Player.GetTrailColor 62 | On.Celeste.Player.GetTrailColor += (orig, player, wasDashB) => { 63 | Console.WriteLine("1 - Hello, World!"); 64 | 65 | // Get the "original" color and manipulate it. 66 | // This step is optional - we can return anything we want. 67 | // We can also pass anything to the orig method. 68 | Color color = orig(player, wasDashB); 69 | 70 | // If the player is facing left, display a modified color. 71 | if (player.Facing == Facings.Left) 72 | return new Color(0xFF, color.G, color.B, color.A); 73 | 74 | return color; 75 | }; 76 | 77 | IL.Celeste.Player.GetTrailColor += (body, il) => { 78 | // Let'd dump the method before manipulating it. 79 | Console.WriteLine("MANIPULATING " + body.Method); 80 | foreach (Instruction i in body.Instructions) 81 | Console.WriteLine(i); 82 | 83 | int index = 0; 84 | 85 | // Insert Console.WriteLine(...) at the beginning. 86 | il.Emit(ref index, OpCodes.Ldstr, "2 - Hello, IL manipulation!"); 87 | il.Emit(ref index, OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) })); 88 | 89 | // After that, emit an inline delegate call. 90 | il.EmitDelegateCall(ref index, () => { 91 | Console.WriteLine("3 - Hello, C# code in IL!"); 92 | }); 93 | 94 | // Note that we can also insert any arbitrary delegate anywhere. 95 | // We know that the original method ends with ldloc.0, then ret 96 | // Let's wrap ldloc.0 with a delegate call, then invoke it before ret. 97 | if (il.GotoNext(ref index, (instrs, i) => 98 | instrs[i + 0].OpCode == OpCodes.Ldloc_0 && 99 | instrs[i + 1].OpCode == OpCodes.Ret 100 | )) { 101 | // Note that we also need to update any branches pointing towards ldloc.0 102 | // We only need to do this because we know that there's at least one jump to ldloc.0 103 | int indexPush = index; 104 | int delegateID = il.EmitDelegatePush>(ref index, color => { 105 | Console.WriteLine("4 - Hello, arbitrary C# code in IL!"); 106 | color.G = 0xFF; 107 | return color; 108 | }); 109 | il.UpdateBranches(index, indexPush); 110 | 111 | index += 1; // Skip ldloc.0 112 | 113 | il.EmitDelegateInvoke(ref index, delegateID); 114 | // The delegate returns a modified color, which gets returned by the following ret instruction. 115 | 116 | } else { 117 | // If we know that there are multiple versions, we could chain them via else-ifs. 118 | // Otherwise, do nothing (or throw an exception). 119 | } 120 | 121 | // Leave the rest of the method unmodified. 122 | }; 123 | 124 | #endif 125 | 126 | On.Celeste.PlayerHair.GetHairColor += GetHairColor; 127 | On.Celeste.Player.GetTrailColor += GetTrailColor; 128 | On.Celeste.PlayerHair.GetHairTexture += GetHairTexture; 129 | On.Celeste.PlayerHair.Render += RenderHair; 130 | On.Celeste.Player.Render += RenderPlayer; 131 | On.Celeste.PlayerSprite.Render += RenderPlayerSprite; 132 | } 133 | 134 | public override void LoadContent(bool firstLoad) { 135 | FoxBangs = GFX.Game.GetAtlasSubtextures("characters/player/foxbangs"); 136 | FoxHair = GFX.Game.GetAtlasSubtextures("characters/player/foxhair"); 137 | Skateboard = GFX.Game["characters/player/skateboard"]; 138 | Dab = GFX.Game["characters/player/dab"]; 139 | } 140 | 141 | public override void Unload() { 142 | On.Celeste.PlayerHair.GetHairColor -= GetHairColor; 143 | On.Celeste.Player.GetTrailColor -= GetTrailColor; 144 | On.Celeste.PlayerHair.GetHairTexture -= GetHairTexture; 145 | On.Celeste.PlayerHair.Render -= RenderHair; 146 | On.Celeste.Player.Render -= RenderPlayer; 147 | On.Celeste.PlayerSprite.Render -= RenderPlayerSprite; 148 | } 149 | 150 | public override void CreateModMenuSection(TextMenu menu, bool inGame, EventInstance snapshot) { 151 | base.CreateModMenuSection(menu, inGame, snapshot); 152 | 153 | if (inGame) { 154 | menu.Add(new TextMenu.Button(Dialog.Clean("modoptions_rainbowmodule_reloadcolors")).Pressed(() => { 155 | // Temporarily store current settings, load new settings and replace colors. 156 | RainbowModuleSettings settings = Settings; 157 | LoadSettings(); 158 | settings.FoxColorLight = Settings.FoxColorLight; 159 | settings.FoxColorDark = Settings.FoxColorDark; 160 | _Settings = settings; 161 | })); 162 | } 163 | } 164 | 165 | public static Color GetHairColor(On.Celeste.PlayerHair.orig_GetHairColor orig, PlayerHair self, int index) { 166 | Color colorOrig = orig(self, index); 167 | if (!(self.Entity is Player) || self.GetSprite().Mode == PlayerSpriteMode.Badeline) 168 | return colorOrig; 169 | 170 | Color color = colorOrig; 171 | 172 | if (Settings.FoxEnabled) { 173 | Color colorFox; 174 | if (index % 2 == 0) { 175 | colorFox = Settings.FoxColorLight; 176 | color = new Color( 177 | (color.R / 255f) * 0.1f + (colorFox.R / 255f) * 0.9f, 178 | (color.G / 255f) * 0.05f + (colorFox.G / 255f) * 0.95f, 179 | (color.B / 255f) * 0.2f + (colorFox.B / 255f) * 0.8f, 180 | color.A 181 | ); 182 | } else { 183 | colorFox = Settings.FoxColorDark; 184 | color = new Color( 185 | (color.R / 255f) * 0.1f + (colorFox.R / 255f) * 0.7f, 186 | (color.G / 255f) * 0.1f + (colorFox.G / 255f) * 0.7f, 187 | (color.B / 255f) * 0.1f + (colorFox.B / 255f) * 0.7f, 188 | color.A 189 | ); 190 | } 191 | } 192 | 193 | if (Settings.RainbowEnabled) { 194 | float wave = self.GetWave() * 60f; 195 | wave *= Settings.RainbowSpeedFactor; 196 | Color colorRainbow = ColorFromHSV((index / (float) self.GetSprite().HairCount) * 180f + wave, 0.6f, 1.0f); 197 | color = new Color( 198 | (color.R / 255f) * 0.3f + (colorRainbow.R / 255f) * 0.7f, 199 | (color.G / 255f) * 0.3f + (colorRainbow.G / 255f) * 0.7f, 200 | (color.B / 255f) * 0.3f + (colorRainbow.B / 255f) * 0.7f, 201 | color.A 202 | ); 203 | } 204 | 205 | color.A = colorOrig.A; 206 | return color; 207 | } 208 | 209 | public static Color GetTrailColor(On.Celeste.Player.orig_GetTrailColor orig, Player self, bool wasDashB) { 210 | if (!Settings.RainbowEnabled || self.Sprite.Mode == PlayerSpriteMode.Badeline || self.Hair == null) 211 | return orig(self, wasDashB); 212 | 213 | return self.Hair.GetHairColor((trailIndex++) % self.Hair.GetSprite().HairCount); 214 | } 215 | 216 | public static MTexture GetHairTexture(On.Celeste.PlayerHair.orig_GetHairTexture orig, PlayerHair self, int index) { 217 | if (!(self.Entity is Player) || self.GetSprite().Mode == PlayerSpriteMode.Badeline) 218 | return orig(self, index); 219 | 220 | if (Settings.FoxEnabled) { 221 | if (index == 0) 222 | return FoxBangs[self.GetSprite().HairFrame]; 223 | return FoxHair[index % FoxHair.Count]; 224 | } 225 | 226 | return orig(self, index); 227 | } 228 | 229 | public static void RenderHair(On.Celeste.PlayerHair.orig_Render orig, PlayerHair self) { 230 | if (Settings.BaldelineEnabled) return; 231 | Player player = self.Entity as Player; 232 | if (player == null || self.GetSprite().Mode == PlayerSpriteMode.Badeline) { 233 | orig(self); 234 | return; 235 | } 236 | 237 | if (Settings.SkateboardEnabled) 238 | for (int i = 0; i < self.Nodes.Count; i++) 239 | self.Nodes[i] = self.Nodes[i] + SkateboardPlayerOffset; 240 | if (Settings.DuckToDabEnabled && player.Ducking) 241 | for (int i = 0; i < self.Nodes.Count; i++) 242 | self.Nodes[i] = self.Nodes[i] + DabPlayerOffset; 243 | 244 | if (Settings.WoomyEnabled) { 245 | PlayerSprite sprite = self.GetSprite(); 246 | if (!sprite.HasHair) 247 | return; 248 | 249 | const float woomyOffs = 3f; 250 | Vector2 woomyScaleMul = new Vector2(0.7f, 0.7f); 251 | Vector2 woomyScaleOffs = new Vector2(-0.2f, -0.2f); 252 | 253 | Vector2 origin = new Vector2(5f, 5f); 254 | Color colorBorder = self.Border * self.Alpha; 255 | 256 | RenderHairPlayerOutline(self); 257 | 258 | Vector2 pos; 259 | MTexture tex; 260 | Color color; 261 | Vector2 scale; 262 | 263 | self.Nodes[0] = self.Nodes[0].Floor(); 264 | 265 | if (colorBorder.A > 0) { 266 | tex = self.GetHairTexture(0); 267 | scale = self.GetHairScale(0); 268 | pos = self.Nodes[0]; 269 | tex.Draw(pos + new Vector2(-1f, 0f), origin, colorBorder, scale); 270 | tex.Draw(pos + new Vector2( 1f, 0f), origin, colorBorder, scale); 271 | tex.Draw(pos + new Vector2(0f, -1f), origin, colorBorder, scale); 272 | tex.Draw(pos + new Vector2( 0f, 1f), origin, colorBorder, scale); 273 | 274 | tex = self.GetHairTexture(2); 275 | scale = self.GetHairScale(sprite.HairCount - 2) * woomyScaleMul + woomyScaleOffs; 276 | pos = self.Nodes[0]; 277 | tex.Draw(pos + new Vector2(-1f - woomyOffs, 0f), origin, colorBorder, scale); 278 | tex.Draw(pos + new Vector2( 1f - woomyOffs, 0f), origin, colorBorder, scale); 279 | tex.Draw(pos + new Vector2( 0f - woomyOffs, -1f), origin, colorBorder, scale); 280 | tex.Draw(pos + new Vector2( 0f - woomyOffs, 1f), origin, colorBorder, scale); 281 | tex.Draw(pos + new Vector2(-1f + woomyOffs, 0f), origin, colorBorder, scale); 282 | tex.Draw(pos + new Vector2( 1f + woomyOffs, 0f), origin, colorBorder, scale); 283 | tex.Draw(pos + new Vector2( 0f + woomyOffs, -1f), origin, colorBorder, scale); 284 | tex.Draw(pos + new Vector2( 0f + woomyOffs, 1f), origin, colorBorder, scale); 285 | 286 | for (int i = 1; i < sprite.HairCount; i++) { 287 | tex = self.GetHairTexture(i); 288 | scale = self.GetHairScale(sprite.HairCount - i - 1) * woomyScaleMul + woomyScaleOffs; 289 | pos = self.Nodes[i]; 290 | tex.Draw(pos + new Vector2(-1f - woomyOffs, 0f), origin, colorBorder, scale); 291 | tex.Draw(pos + new Vector2( 1f - woomyOffs, 0f), origin, colorBorder, scale); 292 | tex.Draw(pos + new Vector2( 0f - woomyOffs, -1f), origin, colorBorder, scale); 293 | tex.Draw(pos + new Vector2( 0f - woomyOffs, 1f), origin, colorBorder, scale); 294 | tex.Draw(pos + new Vector2(-1f + woomyOffs, 0f), origin, colorBorder, scale); 295 | tex.Draw(pos + new Vector2( 1f + woomyOffs, 0f), origin, colorBorder, scale); 296 | tex.Draw(pos + new Vector2( 0f + woomyOffs, -1f), origin, colorBorder, scale); 297 | tex.Draw(pos + new Vector2( 0f + woomyOffs, 1f), origin, colorBorder, scale); 298 | } 299 | } 300 | 301 | tex = self.GetHairTexture(0); 302 | color = self.GetHairColor(0); 303 | scale = self.GetHairScale(0); 304 | tex.Draw(self.Nodes[0], origin, color, scale); 305 | 306 | tex = self.GetHairTexture(2); 307 | color = self.GetHairColor(0); 308 | scale = self.GetHairScale(sprite.HairCount - 2) * woomyScaleMul + woomyScaleOffs; 309 | tex.Draw(self.Nodes[0] + new Vector2(-woomyOffs, 0f), origin, color, scale); 310 | tex.Draw(self.Nodes[0] + new Vector2( woomyOffs, 0f), origin, color, scale); 311 | 312 | for (int i = sprite.HairCount - 1; i >= 1; i--) { 313 | tex = self.GetHairTexture(i); 314 | color = self.GetHairColor(i); 315 | scale = self.GetHairScale(sprite.HairCount - i - 1) * woomyScaleMul + woomyScaleOffs; 316 | tex.Draw(self.Nodes[i] + new Vector2(-woomyOffs, 0f), origin, color, scale); 317 | tex.Draw(self.Nodes[i] + new Vector2( woomyOffs, 0f), origin, color, scale); 318 | } 319 | 320 | goto End; 321 | } 322 | 323 | orig(self); 324 | 325 | End: 326 | if (Settings.SkateboardEnabled) 327 | for (int i = 0; i < self.Nodes.Count; i++) 328 | self.Nodes[i] = self.Nodes[i] - SkateboardPlayerOffset; 329 | if (Settings.DuckToDabEnabled && player.Ducking) 330 | for (int i = 0; i < self.Nodes.Count; i++) 331 | self.Nodes[i] = self.Nodes[i] - DabPlayerOffset; 332 | } 333 | 334 | private static void RenderHairPlayerOutline(PlayerHair self) { 335 | PlayerSprite sprite = self.GetSprite(); 336 | if (!self.DrawPlayerSpriteOutline) 337 | return; 338 | 339 | Vector2 origin = sprite.Position; 340 | Color color = sprite.Color; 341 | 342 | sprite.Color = self.Border * self.Alpha; 343 | 344 | sprite.Position = origin + new Vector2(0f, -1f); 345 | sprite.Render(); 346 | sprite.Position = origin + new Vector2(0f, 1f); 347 | sprite.Render(); 348 | sprite.Position = origin + new Vector2(-1f, 0f); 349 | sprite.Render(); 350 | sprite.Position = origin + new Vector2(1f, 0f); 351 | sprite.Render(); 352 | 353 | sprite.Color = color; 354 | sprite.Position = origin; 355 | } 356 | 357 | public static void RenderPlayer(On.Celeste.Player.orig_Render orig, Player self) { 358 | Vector2 renderPos = self.Sprite.RenderPosition; 359 | 360 | if (Settings.SkateboardEnabled) 361 | self.Sprite.RenderPosition += SkateboardPlayerOffset; 362 | if (Settings.DuckToDabEnabled && self.Ducking) 363 | self.Sprite.RenderPosition += DabPlayerOffset; 364 | 365 | orig(self); 366 | 367 | if (Settings.SkateboardEnabled) { 368 | Skateboard.Draw( 369 | renderPos.Floor() + new Vector2(self.Facing == Facings.Left ? 9 : -8, -4), 370 | Vector2.Zero, Color.White, 371 | new Vector2(self.Facing == Facings.Left ? -1 : 1, 1) 372 | ); 373 | } 374 | 375 | if (Settings.SkateboardEnabled) 376 | self.Sprite.RenderPosition -= SkateboardPlayerOffset; 377 | if (Settings.DuckToDabEnabled && self.Ducking) 378 | self.Sprite.RenderPosition -= DabPlayerOffset; 379 | } 380 | 381 | public static void RenderPlayerSprite(On.Celeste.PlayerSprite.orig_Render orig, PlayerSprite self) { 382 | Player player = self.Entity as Player; 383 | if (player == null || self.Mode == PlayerSpriteMode.Badeline) { 384 | orig(self); 385 | return; 386 | } 387 | 388 | if (Settings.DuckToDabEnabled && player.Ducking) { 389 | Dab.Draw( 390 | self.RenderPosition.Floor() + new Vector2(player.Facing == Facings.Left ? 6 : -6, -7), 391 | Vector2.Zero, Color.White, 392 | self.Scale 393 | ); 394 | return; 395 | } 396 | 397 | orig(self); 398 | } 399 | 400 | // Conversion algorithms found randomly on the net - best source for HSV <-> RGB ever:tm: 401 | 402 | private static void ColorToHSV(Color c, out float h, out float s, out float v) { 403 | float r = c.R / 255f; 404 | float g = c.G / 255f; 405 | float b = c.B / 255f; 406 | float min, max, delta; 407 | min = Math.Min(Math.Min(r, g), b); 408 | max = Math.Max(Math.Max(r, g), b); 409 | v = max; 410 | delta = max - min; 411 | if (max != 0) { 412 | s = delta / max; 413 | 414 | if (r == max) 415 | h = (g - b) / delta; 416 | else if (g == max) 417 | h = 2 + (b - r) / delta; 418 | else 419 | h = 4 + (r - g) / delta; 420 | h *= 60f; 421 | if (h < 0) 422 | h += 360f; 423 | } else { 424 | s = 0f; 425 | h = 0f; 426 | } 427 | } 428 | 429 | private static Color ColorFromHSV(float hue, float saturation, float value) { 430 | int hi = (int) (Math.Floor(hue / 60f)) % 6; 431 | float f = hue / 60f - (float) Math.Floor(hue / 60f); 432 | 433 | value = value * 255; 434 | int v = (int) Math.Round(value); 435 | int p = (int) Math.Round(value * (1 - saturation)); 436 | int q = (int) Math.Round(value * (1 - f * saturation)); 437 | int t = (int) Math.Round(value * (1 - (1 - f) * saturation)); 438 | 439 | if (hi == 0) 440 | return new Color(v, t, p, 255); 441 | else if (hi == 1) 442 | return new Color(q, v, p, 255); 443 | else if (hi == 2) 444 | return new Color(p, v, t, 255); 445 | else if (hi == 3) 446 | return new Color(p, q, v, 255); 447 | else if (hi == 4) 448 | return new Color(t, p, v, 255); 449 | else 450 | return new Color(v, p, q, 255); 451 | } 452 | 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /RainbowMod/RainbowModuleSettings.cs: -------------------------------------------------------------------------------- 1 | using FMOD.Studio; 2 | using Microsoft.Xna.Framework; 3 | using Monocle; 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using YamlDotNet.Serialization; 11 | 12 | namespace Celeste.Mod.Rainbow { 13 | public class RainbowModuleSettings : EverestModuleSettings { 14 | public bool RainbowEnabled { get; set; } 15 | 16 | [SettingRange(0, 20)] 17 | public int RainbowSpeed { get; set; } = 10; 18 | [YamlIgnore] 19 | [SettingIgnore] 20 | public float RainbowSpeedFactor => RainbowSpeed / 20f; 21 | 22 | public bool FoxEnabled { get; set; } 23 | 24 | [YamlMember(Alias = "FoxColorLight")] 25 | [SettingIgnore] 26 | public string FoxColorLightHex { 27 | get { 28 | return FoxColorLight.R.ToString("X2") + FoxColorLight.G.ToString("X2") + FoxColorLight.B.ToString("X2"); 29 | } 30 | set { 31 | if (string.IsNullOrEmpty(value)) 32 | return; 33 | try { 34 | FoxColorLight = Calc.HexToColor(value); 35 | } catch (Exception e) { 36 | Logger.Log(LogLevel.Warn, "rainbowmod", "Invalid FoxColorLight!"); 37 | e.LogDetailed(); 38 | } 39 | } 40 | } 41 | [YamlIgnore] 42 | [SettingIgnore] 43 | public Color FoxColorLight { get; set; } 44 | 45 | [YamlMember(Alias = "FoxColorDark")] 46 | [SettingIgnore] 47 | public string FoxColorDarkHex { 48 | get { 49 | return FoxColorDark.R.ToString("X2") + FoxColorDark.G.ToString("X2") + FoxColorDark.B.ToString("X2"); 50 | } 51 | set { 52 | if (string.IsNullOrEmpty(value)) 53 | return; 54 | try { 55 | FoxColorDark = Calc.HexToColor(value); 56 | } catch (Exception e) { 57 | Logger.Log(LogLevel.Warn, "rainbowmod", "Invalid FoxColorDark!"); 58 | e.LogDetailed(); 59 | } 60 | } 61 | } 62 | [YamlIgnore] 63 | [SettingIgnore] 64 | public Color FoxColorDark { get; set; } 65 | 66 | public bool WoomyEnabled { get; set; } 67 | 68 | public bool SkateboardEnabled { get; set; } 69 | 70 | public bool DuckToDabEnabled { get; set; } 71 | 72 | public bool DuckToSneezeEnabled { get; set; } 73 | 74 | public bool BaldelineEnabled { get; set; } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /RainbowMod/everest.yaml: -------------------------------------------------------------------------------- 1 | - Name: RainbowMod 2 | Version: 3.2.0 3 | DLL: RainbowMod.dll 4 | Dependencies: 5 | - Name: Everest 6 | Version: 1.0.0 7 | -------------------------------------------------------------------------------- /RainbowMod/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /RainbowMod/settings.celeste: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 3 5 | 0 6 | true 7 | false 8 | false 9 | true 10 | 10 11 | 10 12 | true 13 | 0 14 | english 15 | true 16 | false 17 | Left 18 | Right 19 | Down 20 | Up 21 | 22 | Z 23 | V 24 | LeftShift 25 | 26 | 27 | C 28 | 29 | 30 | X 31 | 32 | 33 | X 34 | 35 | 36 | 37 | C 38 | Enter 39 | 40 | 41 | X 42 | 43 | 44 | Tab 45 | 46 | 47 | R 48 | 49 | 50 | LeftTrigger 51 | RightTrigger 52 | LeftShoulder 53 | RightShoulder 54 | 55 | 56 | A 57 | Y 58 | 59 | 60 | X 61 | B 62 | 63 | 64 | B 65 | 66 | 67 | false 68 | -------------------------------------------------------------------------------- /rainbowdeline.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EverestAPI/RainbowMod/HEAD/rainbowdeline.gif --------------------------------------------------------------------------------