Full Code of Aeroluna/NoodleExtensions for AI

master 53a21b669fb4 cached
76 files
223.5 KB
52.3k tokens
249 symbols
1 requests
Download .txt
Showing preview only (245K chars total). Download the full file or copy to clipboard to get everything.
Repository: Aeroluna/NoodleExtensions
Branch: master
Commit: 53a21b669fb4
Files: 76
Total size: 223.5 KB

Directory structure:
gitextract_67mkl1wr/

├── .editorconfig
├── .gitattributes
├── .github/
│   └── FUNDING.yml
├── .gitignore
├── Documentation/
│   ├── AnimationDocs.md
│   └── examples/
│       └── documentationMap/
│           ├── README.md
│           ├── cat.ogg
│           ├── count.txt
│           └── demo.js
├── LICENSE
├── NoodleExtensions/
│   ├── Animation/
│   │   ├── AnimationController.cs
│   │   ├── AnimationHelper.cs
│   │   ├── Events/
│   │   │   ├── AssignPlayerToTrack.cs
│   │   │   ├── AssignTrackParent.cs
│   │   │   └── NoodleEventData.cs
│   │   ├── ParentObject.cs
│   │   └── PlayerTrack.cs
│   ├── CutoutManager.cs
│   ├── Directory.Build.props
│   ├── Directory.Build.targets
│   ├── HarmonyPatches/
│   │   ├── BeatmapDataLoader.cs
│   │   ├── BeatmapDataTransformHelper.cs
│   │   ├── BeatmapObjectCallBackController.cs
│   │   ├── BeatmapObjectManager.cs
│   │   ├── BeatmapObjectSpawnController.cs
│   │   ├── BeatmapObjectSpawnMovementData.cs
│   │   ├── BeatmapObjectsInTimeRowProcessor.cs
│   │   ├── Cutout/
│   │   │   ├── BaseNoteVisual.cs
│   │   │   └── ObstacleDissolve.cs
│   │   ├── FakeNotes/
│   │   │   ├── BadNoteCutEffectSpawner.cs
│   │   │   ├── BeatmapData.cs
│   │   │   ├── BeatmapObjectExecutionRatingsRecorder.cs
│   │   │   ├── BeatmapObjectManager.cs
│   │   │   ├── BombCutSoundEffectManager.cs
│   │   │   ├── BombNoteController.cs
│   │   │   ├── FakeNoteHelper.cs
│   │   │   ├── GameEnergyCounter.cs
│   │   │   ├── GameNoteController.cs
│   │   │   ├── NoteCutCoreEffectSpawner.cs
│   │   │   ├── NoteCutScoreSpawner.cs
│   │   │   ├── NoteCutSoundEffectManager.cs
│   │   │   ├── ObstacleSaberSparkleEffectManager.cs
│   │   │   └── PlayerHeadAndObstacleInteraction.cs
│   │   ├── GameplayCoreInstaller.cs
│   │   ├── LeftHanded/
│   │   │   ├── BeatmapDataMirrorTransform.cs
│   │   │   ├── NoteData.cs
│   │   │   └── ObstacleData.cs
│   │   ├── Mirror/
│   │   │   ├── MirroredNoteController.cs
│   │   │   └── MirroredObstacleController.cs
│   │   ├── MultiplayerConnectedPlayerInstaller.cs
│   │   ├── NoteController.cs
│   │   ├── NoteFloorMovement.cs
│   │   ├── NoteJump.cs
│   │   ├── ObstacleController.cs
│   │   ├── SceneTransition/
│   │   │   ├── MissionLevelScenesTransitionSetupDataSO.cs
│   │   │   ├── MultiplayerLevelScenesTransitionSetupDataSO.cs
│   │   │   ├── SceneTransitionHelper.cs
│   │   │   ├── StandardLevelScenesTransitionSetupDataSO.cs
│   │   │   └── TutorialScenesTransitionSetupDataSO.cs
│   │   ├── SmallFixes/
│   │   │   ├── BasicBeatmapObjectManager.cs
│   │   │   ├── BeatEffectSpawner.cs
│   │   │   ├── BeatmapObjectManager.cs
│   │   │   ├── CutoutEffect.cs
│   │   │   ├── DisappearingArrowController.cs
│   │   │   ├── MemoryPoolBase.cs
│   │   │   └── PlayerTransforms.cs
│   │   ├── SpawnDataHelper.cs
│   │   └── SpawnRotationProcessor.cs
│   ├── NoodleController.cs
│   ├── NoodleExtensions.csproj
│   ├── NoodleExtensionsExtensions.cs
│   ├── NoodleObjectData.cs
│   ├── Plugin.cs
│   └── manifest.json
├── NoodleExtensions.sln
└── README.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# NOTE: Requires **VS2019 16.3** or later

# Code files
[*.{cs,vb}]


dotnet_diagnostic.CA1001.severity = warning

dotnet_diagnostic.CA1009.severity = warning

dotnet_diagnostic.CA1016.severity = warning

dotnet_diagnostic.CA1033.severity = warning

dotnet_diagnostic.CA1049.severity = warning

dotnet_diagnostic.CA1060.severity = warning

dotnet_diagnostic.CA1061.severity = warning

dotnet_diagnostic.CA1063.severity = warning

dotnet_diagnostic.CA1065.severity = warning

dotnet_diagnostic.CA1301.severity = warning

dotnet_diagnostic.CA1400.severity = warning

dotnet_diagnostic.CA1401.severity = warning

dotnet_diagnostic.CA1403.severity = warning

dotnet_diagnostic.CA1404.severity = warning

dotnet_diagnostic.CA1405.severity = warning

dotnet_diagnostic.CA1410.severity = warning

dotnet_diagnostic.CA1415.severity = warning

dotnet_diagnostic.CA1821.severity = warning

dotnet_diagnostic.CA1900.severity = warning

dotnet_diagnostic.CA1901.severity = warning

dotnet_diagnostic.CA2002.severity = warning

dotnet_diagnostic.CA2100.severity = warning

dotnet_diagnostic.CA2101.severity = warning

dotnet_diagnostic.CA2108.severity = warning

dotnet_diagnostic.CA2111.severity = warning

dotnet_diagnostic.CA2112.severity = warning

dotnet_diagnostic.CA2114.severity = warning

dotnet_diagnostic.CA2116.severity = warning

dotnet_diagnostic.CA2117.severity = warning

dotnet_diagnostic.CA2122.severity = warning

dotnet_diagnostic.CA2123.severity = warning

dotnet_diagnostic.CA2124.severity = warning

dotnet_diagnostic.CA2126.severity = warning

dotnet_diagnostic.CA2131.severity = warning

dotnet_diagnostic.CA2132.severity = warning

dotnet_diagnostic.CA2133.severity = warning

dotnet_diagnostic.CA2134.severity = warning

dotnet_diagnostic.CA2137.severity = warning

dotnet_diagnostic.CA2138.severity = warning

dotnet_diagnostic.CA2140.severity = warning

dotnet_diagnostic.CA2141.severity = warning

dotnet_diagnostic.CA2146.severity = warning

dotnet_diagnostic.CA2147.severity = warning

dotnet_diagnostic.CA2149.severity = warning

dotnet_diagnostic.CA2200.severity = warning

dotnet_diagnostic.CA2202.severity = warning

dotnet_diagnostic.CA2207.severity = warning

dotnet_diagnostic.CA2212.severity = warning

dotnet_diagnostic.CA2213.severity = warning

dotnet_diagnostic.CA2214.severity = warning

dotnet_diagnostic.CA2216.severity = warning

dotnet_diagnostic.CA2220.severity = warning

dotnet_diagnostic.CA2229.severity = warning

dotnet_diagnostic.CA2231.severity = warning

dotnet_diagnostic.CA2232.severity = warning

dotnet_diagnostic.CA2235.severity = warning

dotnet_diagnostic.CA2236.severity = warning

dotnet_diagnostic.CA2237.severity = warning

dotnet_diagnostic.CA2238.severity = warning

dotnet_diagnostic.CA2240.severity = warning

dotnet_diagnostic.CA2241.severity = warning

dotnet_diagnostic.CA2242.severity = warning

dotnet_diagnostic.CS8019.severity = warning

dotnet_diagnostic.CS8020.severity = warning

dotnet_diagnostic.IDE0001.severity = warning

dotnet_diagnostic.IDE0002.severity = warning

dotnet_diagnostic.IDE0060.severity = warning

dotnet_diagnostic.SA0001.severity = none

dotnet_diagnostic.SA1101.severity = none

dotnet_diagnostic.SA1309.severity = none

dotnet_diagnostic.SA1310.severity = none

dotnet_diagnostic.SA1313.severity = none

dotnet_diagnostic.SA1402.severity = none

dotnet_diagnostic.SA1600.severity = none

dotnet_diagnostic.SA1601.severity = none

dotnet_diagnostic.SA1602.severity = none

dotnet_diagnostic.SA1611.severity = none

dotnet_diagnostic.SA1615.severity = none

dotnet_diagnostic.SA1618.severity = none

dotnet_diagnostic.SA1619.severity = none

dotnet_diagnostic.SA1629.severity = none

dotnet_diagnostic.SA1633.severity = none

dotnet_diagnostic.SA1649.severity = none

dotnet_diagnostic.SX1101.severity = warning

dotnet_diagnostic.SX1309.severity = warning

dotnet_diagnostic.SX1309S.severity = warning


================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following 
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
# 
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the 
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: aeroluna
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# Visual Studio 2017 auto generated files
Generated\ Files/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# Benchmark Results
BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/

# StyleCop
StyleCopReport.xml

# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# Visual Studio Trace Files
*.e2e

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json

# Visual Studio code coverage results
*.coverage
*.coveragexml

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs

# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak

# SQL Server files
*.mdf
*.ldf
*.ndf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- Backup*.rdl

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# JetBrains Rider
.idea/
*.sln.iml

# CodeRush personal settings
.cr/personal

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

# Tabs Studio
*.tss

# Telerik's JustMock configuration file
*.jmconfig

# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs

# OpenCover UI analysis results
OpenCover/

# Azure Stream Analytics local run output
ASALocalRun/

# MSBuild Binary and Structured Log
*.binlog

# NVidia Nsight GPU debugger configuration file
*.nvuser

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# Local History for Visual Studio
.localhistory/

# BeatPulse healthcheck temp database
healthchecksdb

================================================
FILE: Documentation/AnimationDocs.md
================================================
These docs have moved! https://github.com/Aeroluna/Heck/wiki/Animation

================================================
FILE: Documentation/examples/documentationMap/README.md
================================================
# Documentation map
This is a JS script used to generate the map seen in the documentation of noodle extensions. Most of it should be self explanatory, it requires nodeJS to run. 

If you have any simple questions feel free to dm me on discord `Reaxt#0690`

================================================
FILE: Documentation/examples/documentationMap/count.txt
================================================
124

================================================
FILE: Documentation/examples/documentationMap/demo.js
================================================
'use strict'

const fs = require('fs');

const INPUT = "ExpertPlusStandard.dat"
const OUTPUT = "ExpertStandard.dat"

let difficulty = JSON.parse(fs.readFileSync(INPUT));


//#region this just counts how many time you ran it for fun, feel free to remove.
if(!(fs.existsSync("count.txt"))) {
    fs.writeFileSync("count.txt", parseInt("0").toString())
}
let count = parseInt(fs.readFileSync("count.txt"))
count++
fs.writeFileSync("count.txt", count.toString())
console.log("GIVE IT UP FOR RUN " + count)
//#endregion


difficulty._customData = { _pointDefinitions: [], _customEvents: [] };

const _customData = difficulty._customData;
const _obstacles = difficulty._obstacles;
const _notes = difficulty._notes;
const _customEvents = _customData._customEvents;
const _pointDefinitions = _customData._pointDefinitions;

let filterednotes

_obstacles.forEach(wall => {
    if (!wall._customData) {
        wall._customData = {}
    }
})

_notes.forEach(note => {
    if (!note._customData) {
        note._customData = {}
    }
})
//#region helper functions
function round(value, decimals) {
  return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
function getJumps(njs, offset) {
    const _startHalfJumpDurationInBeats = 4
    const _maxHalfJumpDistance = 18
    const _startBPM = 170
    const bpm = 170
    const _startNoteJumpMovementSpeed = njs
    const _noteJumpStartBeatOffset = offset

    let _noteJumpMovementSpeed = (_startNoteJumpMovementSpeed * bpm) / _startBPM
    let num = 60 / bpm
    let num2 = _startHalfJumpDurationInBeats
    while (_noteJumpMovementSpeed * num * num2 > _maxHalfJumpDistance) {
        num2 /= 2
    }
    num2 += _noteJumpStartBeatOffset
    if (num2 < 1) {
        num2 = 1
    }
    const _jumpDuration = num * num2 * 2
    const _jumpDistance = _noteJumpMovementSpeed * _jumpDuration
    return {half: num2, dist: _jumpDistance}
}

function offestOnNotesBetween(p1,p2,offset) {
	filterednotes = _notes.filter(n => n._time >= p1 && n._time <= p2)
	filterednotes.forEach(object =>
		{
			//always worth having.
			//man this shit BETTER not be undefined.
			if(typeof offset !== "undefined") {object._customData._noteJumpStartBeatOffset = offset;}

		})
	return filterednotes;
}

function lerp(v0,v1,t) {
	return v0*(1-t)+v1*t;
}
function trackOnNotesBetween(track, p1,p2,potentialOffset) {
  filterednotes = _notes.filter(n => n._time >= p1 && n._time <= p2)
  filterednotes.forEach(object =>
  {
    object._customData._track = track;
    if(typeof potentialOffset !== "undefined") {object._customData._noteJumpStartBeatOffset = potentialOffset;}

  })
  return filterednotes;
}
//applies a track to notes on two tracks between two times based on the color of the notes
//IT GONNA FUCK UP WITH BOMBS I TELL YOU HWAT BOI
//red, blue, p1, p2, potentialOffset
function trackOnNotesBetweenRBSep(trackR, trackB, p1, p2, potentialOffset){
	filterednotes = _notes.filter(n => n._time >= p1 && n._time <= p2)
	filterednotes.forEach(object =>
		{
			if(typeof potentialOffset !== "undefined") {object._customData._noteJumpStartBeatOffset = potentialOffset;}
			if(object._type == 0) {object._customData._track = trackR};
			if(object._type == 1) {object._customData._track = trackB};
	  
		})
	return filterednotes;
}
//p1, p2, potentialoffset, up, down, left, right, 
//TODO: ADD OTHER DIRS
function trackOnNotesBetweenDirSep(p1, p2, potentialOffset, trackUp, trackDown, trackLeft, trackRight) {
	filterednotes = _notes.filter(n => n._time >= p1 && n._time <= p2)
	filterednotes.forEach(object =>
		{
			if(object._cutDirection == 0 && typeof trackUp !== "undefined") {object._customData._track = trackUp}
			if(object._cutDirection == 1 && typeof trackUp !== "undefined") {object._customData._track = trackDown}
			if(object._cutDirection == 2 && typeof trackUp !== "undefined") {object._customData._track = trackLeft}
			if(object._cutDirection == 3 && typeof trackUp !== "undefined") {object._customData._track = trackRight}
			//i might want to make this only run if I assign a track...
			if(typeof potentialOffset !== "undefined") {object._customData._noteJumpStartBeatOffset = potentialOffset;}

		})
	return filterednotes;
}

//#endregion

//#region use this area to do your stuff

//#region 4-10 _position demo

trackOnNotesBetween("firstPositionDemo", 4, 20) //this function achieves the same as the lines below, its just a simpler/easier way to do so
filterednotes = _notes.filter(n=> n._time >= 4 && n._time <= 25)
filterednotes.forEach(note => {
	note._customData._track = "firstPositionDemo"
})

//push demo point definitions
_pointDefinitions.push({
	"_name":"examplePositionPointDef",
	"_points":[
		[0,0,0,0],
		[0,5,0,0.5,"splineCatmullRom"],
		[0,0,0,1,"splineCatmullRom"]
	  ]
}, {
	"_name":"examplePositionPath",
	"_points":[
		[0,0,0,0],
		[0,5,0,0.25,"splineCatmullRom"],
		[0,0,0,0.5,"splineCatmullRom"]
	  ]
})

//animate track
_customEvents.push({
	"_time":4,
	"_type":"AnimateTrack",
	"_data":{
		"_track":"firstPositionDemo",
		"_position":"examplePositionPointDef",
		"_duration":8
	}
})
//AssignPath
_customEvents.push({
	"_time":12,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"firstPositionDemo",
		"_position":"examplePositionPath",
		"_duration":4,
		"_easing":"easeInBounce"
	}
}, {
	"_time":16,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"firstPositionDemo",
		"_position":[[0,0,0,0],[0,0,0,1]],
		"_duration":4,
		"_easing":"easeOutBounce"
	}
})
//#endregion

//#region _localRotation 20-40

trackOnNotesBetween("localRotationDemo", 20, 40)
_pointDefinitions.push({
	"_name":"localSpinDemoAnimate",
	"_points":[
		[0,0,0,0],
		[90,0,0,0.25],
		[180,0,0,0.5],
		[270,0,0,0.75],
		[360,0,0,1]
	  ]
}, {
	"_name":"localSpinDemoAnimateRev",
	"_points":[
		[0,0,0,0],
		[-90,0,0,0.25],
		[-180,0,0,0.5],
		[-270,0,0,0.75],
		[-360,0,0,1]
	  ]
}, {
	"_name":"localSpinDemoPath",
	"_points":[
		[0,0,0,0],
		[0,0,90,0.125],
		[0,0,180,0.25],
		[0,0,270,0.375],
		[0,0,360,0.5]
	  ]
}
)
//AnimateTrack,
_customEvents.push({
	"_time":20,
	"_type":"AnimateTrack",
	"_data":{
		"_track":"localRotationDemo",
		"_duration":5,
		"_localRotation":"localSpinDemoAnimate",
		"_easing":"easeInOutExpo"
	}
}, {
	"_time":25,
	"_type":"AnimateTrack",
	"_data":{
		"_track":"localRotationDemo",
		"_duration":5,
		"_localRotation":"localSpinDemoAnimateRev",
		"_easing":"easeInOutExpo"
	}
}, {
	"_time":30,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"localRotationDemo",
		"_duration":0,
		"_localRotation":"localSpinDemoPath"
	}
})


//#endregion

//#region _rotation 40-60

trackOnNotesBetween("RotationDemo", 40, 60)
_pointDefinitions.push({
	"_name":"RotationPointsAnimate",
	"_points":[
		[0,0,0,0],
		[0,90,0,0.25],
		[0,180,0,0.5],
		[0,270,0,0.75],
		[0,360,0,1]
	]
}, {
	"_name":"RotationPointsPath",
	"_points":[
		[0,0,0,0],
		[0,45,0,0.125, "splineCatmullRom"],
		[0,-45,0,0.25,"splineCatmullRom"],
		[0,22.5,0,0.375,"splineCatmullRom"],
		[0,-22.5,0,0.5,"splineCatmullRom"],
		[0,0,0,0.625,"splineCatmullRom"]
	]
}
)
//AnimateTrack
_customEvents.push({
	"_time":40,
	"_type":"AnimateTrack",
	"_data":{
		"_track":"RotationDemo",
		"_rotation":"RotationPointsAnimate",
		"_duration":10
	}
},
 {
	"_time":50,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"RotationDemo",
		"_rotation":"RotationPointsPath",
		"_duration":5
	}
}, {
	"_time":55,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"RotationDemo",
		"_rotation":[[0,0,0,0]],
		"_duration":5
	}
})

//#endregion

//#region _dissolve 60-80

trackOnNotesBetween("dissolveDemo", 60, 80)
_pointDefinitions.push({
	"_name":"dissolveDemoAnimate",
	"_points":[
		[1,0],
		[0,0.25],
		[0.5,0.50],
		[0,0.75],
		[1,1]
	]
}, {
	"_name":"dissolveDemoPath",
	"_points":[
		[0,0],
		[1,0.125],
		[1, 0.30],
		[0,0.35]
	]
}
)

_customEvents.push({
	"_time":60,
	"_type":"AnimateTrack",
	"_data":{
		"_track":"dissolveDemo",
		"_dissolve":"dissolveDemoAnimate",
		"_duration":10
	}
}, {
	"_time":70,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"dissolveDemo",
		"_dissolve":"dissolveDemoPath",
		"_duration":0
	}
})
//#endregion

//#region _dissolve 80-100
trackOnNotesBetween("dissolveArrowDemo", 80, 100)

_pointDefinitions.push({
	"_name":"dissolveArrowDemoAnimate",
	"_points":[
		[1,0],
		[0,1]
	]
}, {
	"_name":"dissolveArrowDemoPath",
	"_points":[
		[0,0.10],
		[1,0.20],
		[1, 0.30],
		[0,0.35]
	]
}
)
_customEvents.push({
	"_time":80,
	"_type":"AnimateTrack",
	"_data":{
		"_track":"dissolveArrowDemo",
		"_dissolveArrow":"dissolveArrowDemoAnimate",
		"_duration":5
	}
}, {
	"_time":85,
	"_type":"AnimateTrack",
	"_data":{
		"_track":"dissolveArrowDemo",
		"_dissolveArrow":[[0,0],[1,1]],
		"_duration":5
	}
},
 {
	"_time":90,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"dissolveArrowDemo",
		"_dissolveArrow":"dissolveArrowDemoPath"
	}
})

//#endregion

//#region color walls 100-120

_obstacles.push({
	"_time":100,
	"_duration":9,
	"_lineindex":0,
	"_type":0,
	"_width":1,
	"_customData":{
		"_track":"LeftColorWall"
	}
}, {
	"_time":100,
	"_duration":9,
	"_lineindex":3,
	"_type":0,
	"_width":1,
	"_customData":{
		"_track":"RightColorWall"
	}
})
_pointDefinitions.push({
	"_name":"RightColorWallAnimate",
	"_points":[
		[1,0,0,1,0.2],
		[0,1,0,1,0.4],
		[0,0,1,1,0.6],
		[0,1,1,1,0.8],
		[1,1,1,1,1],
	]
}, {
	"_name":"LeftColorWallAnimate",
	"_points":[
		[1,0,0,0,0.2],
		[0,1,0,0,0.4],
		[0,0,1,0,0.6],
		[0,1,1,0,0.8],
		[1,1,1,0,1],
	]
}
)

_customEvents.push({
	"_time":98,
	"_type":"AnimateTrack",
	"_data":{
		"_track":"RightColorWall",
		"_color":"RightColorWallAnimate",
		"_duration":10
	}
}, {
	"_time":98,
	"_type":"AnimateTrack",
	"_data":{
		"_track":"LeftColorWall",
		"_color":"LeftColorWallAnimate",
		"_duration":10
	}
}
)
for (let i = 0; i < 400; i++) {
	let startTime = 110;
	let interval = 0.025;
	let duration = 0.01;
	
	_obstacles.push({
		"_time":startTime+(interval*i),
		"_duration":duration,
		"_lineindex":0,
		"_type":1,
		"_width":1,
		"_customData":{
			"_track":"LeftColorWallStatic"
		}
	}, {
		"_time":startTime+(interval*i),
		"_duration":duration,
		"_lineindex":3,
		"_type":1,
		"_width":1,
		"_customData":{
			"_track":"RightColorWallStatic"
		}
	})
}

_pointDefinitions.push({
	"_name":"GradientPathOne",
	"_points":[
		[1,0,0,0.5,0.0416],
		[0,1,0,0.5,0.0832],
		[0,0,1,0.5,0.1248],
		[1,0,0,0.5,0.1664],
		[0,1,0,0.5,0.208],
		[0,0,1,0.5,0.2496],
		[1,0,0,0.5,0.2912],
		[0,1,0,0.5,0.3328],
		[0,0,1,0.5,0.3743],
		[1,0,0,0.5,0.416],
		[0,1,0,0.5,0.4576],
		[0,0,1,0.5,0.4992]
	]
}, {
	"_name":"GradientPathTwo",
	"_points":[
		[0,1,0,0.5,0.0416],
		[0,0,1,0.5,0.0832],
		[1,0,0,0.5,0.1248],
		[0,1,0,0.5,0.1664],
		[0,0,1,0.5,0.208],
		[1,0,0,0.5,0.2496],
		[0,1,0,0.5,0.2912],
		[0,0,1,0.5,0.3328],
		[1,0,0,0.5,0.3743],
		[0,1,0,0.5,0.416],
		[0,0,1,0.5,0.4576],
		[1,0,0,0.5,0.4992]
	]
}
)

_customEvents.push(
	{
	"_time":110,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"RightColorWallStatic",
		"_color":"GradientPathOne",
		"_duration":2
	}
}, {
	"_time":114,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"RightColorWallStatic",
		"_color":"GradientPathTwo",
		"_duration":6,
		"_easing":"easeOutElastic",
		
	}
}, {
	"_time":110,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"LeftColorWallStatic",
		"_color":"GradientPathTwo",
		"_duration":2
	}
}, {
	"_time":114,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"LeftColorWallStatic",
		"_color":"GradientPathOne",
		"_duration":6,
		"_easing":"easeOutElastic"
}
})
//#endregion

//#region definitePosition 130-150

trackOnNotesBetween("definitePosDemo", 130, 150)
_pointDefinitions.push({
	"_name":"defPosPath",
	"_points":[
		[0, 0, 20, 0],
		[10, 0, 20, 0.1],
		[10, 10, 20, 0.2],
		[0, 10, 20, 0.3],
		[0, 0, 20, 0.4],
		[0, 0, 10, 0.5],
		[-20, 0, 10, 1.0]
	  ]
}, {
	"_name":"defPosNormal",
	"_points":[
		[0,0,23,0],
		[0,0,0,0.5],
		[0,0,-23,1]
	]
}
)

_customEvents.push({
	"_time":132,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"definitePosDemo",
		"_definitePosition":"defPosPath",
		"_duration":3
	}
}, {
	"_time":0,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"definitePosDemo",
		"_definitePosition":"defPosNormal",
		"_duration":0
	}
})
//#endregion
//#region time 155 (this example is bad, you basically need to script time. Better example coming someday)
_pointDefinitions.push({
	"_name":"SingleNoteTime",
	"_points":[
		[0,0],
		[0.45, 0.15],
		[0.15, 0.30],
		[0.5, 0.5],
		[1,1]
	]
})
trackOnNotesBetween("singleNoteTimeTrack", 155, 155)
_customEvents.push({
	"_time":153,
	"_type":"AnimateTrack",
	"_data":{
		"_time":"SingleNoteTime",
		"_duration":10,
		"_track":"singleNoteTimeTrack"
	}
})
//#endregion 

//#region scale 165-190
trackOnNotesBetween("scaleTrack", 165, 195)
for (let i = 0; i < 30; i++) {
	let mult = 1
	let dur = 0.5
	let scaleVar = 3
	_obstacles.push({
		"_time":165+(i*mult),
		"_duration":dur,
		"_lineIndex":0,
		"_type":0,
		"_width":0,
		"_customData":{
			"_position":[-6,1],
			"_scale":[1+(Math.random()*scaleVar),1+(Math.random()*scaleVar)],
			"_track":"scaleTrack"
		}
	})
	
}
_pointDefinitions.push({
	"_name":"AnimateTrackScale",
	"_points":[
		[1,1,1,0],
		[0.80,0.80,0.80,0.15,"easeOutCirc"],
		[2,2,2,0.5,"easeOutBounce"],
		[2,2,2,0.6],
		[2.5,1,1,0.8,"easeOutExpo"],
		[1,1,1,1,"easeOutBounce"]

	]
}, {
	"_name":"PathScale",
	"_points":[
		[1,1,1,0],
		[4,0.5,1,0.20,"easeInElastic"],
		[1,1,1,0.50,"easeOutElastic"]

	]
})
_customEvents.push({
	"_time":165,
	"_type":"AnimateTrack",
	"_data":{
		"_track":"scaleTrack",
		"_scale":"AnimateTrackScale",
		"_duration":5
	}
}, {
	"_time":175,
	"_type":"AssignPathAnimation",
	"_data":{
		"_track":"scaleTrack",
		"_scale":"PathScale"
	}
})


//#endregion

//#region write file
const precision = 4 //decimals to round to

const jsonP = Math.pow(10, precision)
const sortP = Math.pow(10, 2)
function deeperDaddy(obj) {
	if (obj)
		for (const key in obj) {
			if (obj[key] == null) {
				delete obj[key]
			} else if (typeof obj[key] === 'object' || Array.isArray(obj[key])) {
				deeperDaddy(obj[key])
			} else if (typeof obj[key] == 'number') {
				obj[key] = parseFloat(Math.round((obj[key] + Number.EPSILON) * jsonP) / jsonP)
			}
		}
}
deeperDaddy(difficulty)

difficulty._notes.sort(
	(a, b) =>
		parseFloat(Math.round((a._time + Number.EPSILON) * sortP) / sortP) - parseFloat(Math.round((b._time + Number.EPSILON) * sortP) / sortP) ||
		parseFloat(Math.round((a._lineIndex + Number.EPSILON) * sortP) / sortP) - parseFloat(Math.round((b._lineIndex + Number.EPSILON) * sortP) / sortP) ||
		parseFloat(Math.round((a._lineLayer + Number.EPSILON) * sortP) / sortP) - parseFloat(Math.round((b._lineLayer + Number.EPSILON) * sortP) / sortP)
)
difficulty._obstacles.sort((a, b) => a._time - b._time)
difficulty._events.sort((a, b) => a._time - b._time)

fs.writeFileSync(OUTPUT, JSON.stringify(difficulty, null, 0));

//#endregion

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 Aeroluna

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: NoodleExtensions/Animation/AnimationController.cs
================================================
namespace NoodleExtensions.Animation
{
    using CustomJSONData;
    using UnityEngine;

    public class AnimationController : MonoBehaviour
    {
        public static AnimationController? Instance { get; private set; }

        public CustomEventCallbackController? CustomEventCallbackController { get; private set; }

        internal static void CustomEventCallbackInit(CustomEventCallbackController customEventCallbackController)
        {
            if (customEventCallbackController.BeatmapData?.customData.Get<bool>("isMultiplayer") ?? false)
            {
                return;
            }

            if (Instance != null)
            {
                Destroy(Instance);
            }

            Instance = customEventCallbackController.gameObject.AddComponent<AnimationController>();

            Instance.CustomEventCallbackController = customEventCallbackController;
            Instance.CustomEventCallbackController.AddCustomEventCallback(AssignPlayerToTrack.Callback);
            Instance.CustomEventCallbackController.AddCustomEventCallback(AssignTrackParent.Callback);
        }
    }
}


================================================
FILE: NoodleExtensions/Animation/AnimationHelper.cs
================================================
namespace NoodleExtensions.Animation
{
    using System.Collections.Generic;
    using System.Linq;
    using Heck.Animation;
    using UnityEngine;
    using static Heck.Animation.AnimationHelper;
    using static Heck.NullableExtensions;
    using static NoodleExtensions.HarmonyPatches.SpawnDataHelper.BeatmapObjectSpawnMovementDataVariables;
    using static NoodleExtensions.Plugin;

    public static class AnimationHelper
    {
        private static BasicBeatmapObjectManager? BeatmapObjectManager => HarmonyPatches.BeatmapObjectSpawnControllerStart.BeatmapObjectManager;

        internal static void OnTrackCreated(Track track)
        {
            track.AddProperty(POSITION, PropertyType.Vector3);
            track.AddProperty(ROTATION, PropertyType.Quaternion);
            track.AddProperty(SCALE, PropertyType.Vector3);
            track.AddProperty(LOCALROTATION, PropertyType.Quaternion);
            track.AddProperty(DISSOLVE, PropertyType.Linear);
            track.AddProperty(DISSOLVEARROW, PropertyType.Linear);
            track.AddProperty(TIME, PropertyType.Linear);
            track.AddProperty(CUTTABLE, PropertyType.Linear);

            track.AddPathProperty(POSITION, PropertyType.Vector3);
            track.AddPathProperty(ROTATION, PropertyType.Quaternion);
            track.AddPathProperty(SCALE, PropertyType.Vector3);
            track.AddPathProperty(LOCALROTATION, PropertyType.Quaternion);
            track.AddPathProperty(DEFINITEPOSITION, PropertyType.Vector3);
            track.AddPathProperty(DISSOLVE, PropertyType.Linear);
            track.AddPathProperty(DISSOLVEARROW, PropertyType.Linear);
            track.AddPathProperty(CUTTABLE, PropertyType.Linear);
        }

        internal static void GetDefinitePositionOffset(NoodleObjectData.AnimationObjectData? animationObject, IEnumerable<Track>? tracks, float time, out Vector3? definitePosition)
        {
            Vector3? pathDefinitePosition = animationObject?.LocalDefinitePosition?.Interpolate(time);

            if (!pathDefinitePosition.HasValue && tracks != null)
            {
                if (tracks.Count() > 1)
                {
                    pathDefinitePosition = SumVectorNullables(tracks.Select(n => TryGetVector3PathProperty(n, DEFINITEPOSITION, time)));
                }
                else
                {
                    pathDefinitePosition = TryGetVector3PathProperty(tracks.First(), DEFINITEPOSITION, time);
                }
            }

            if (pathDefinitePosition.HasValue)
            {
                Vector3? pathPosition = animationObject?.LocalPosition?.Interpolate(time);
                Vector3? positionOffset = null;
                if (tracks != null)
                {
                    if (tracks.Count() > 1)
                    {
                        pathPosition ??= SumVectorNullables(tracks.Select(n => TryGetVector3PathProperty(n, POSITION, time)));
                        positionOffset = SumVectorNullables(SumVectorNullables(tracks.Select(n => TryGetProperty<Vector3?>(n, POSITION))), pathPosition);
                    }
                    else
                    {
                        Track track = tracks.First();
                        pathPosition ??= TryGetVector3PathProperty(track, POSITION, time);
                        positionOffset = SumVectorNullables(TryGetProperty<Vector3?>(track, POSITION), pathPosition);
                    }
                }
                else
                {
                    positionOffset = pathPosition;
                }

                definitePosition = SumVectorNullables(positionOffset, pathDefinitePosition) * NoteLinesDistance;

                if (LeftHandedMode)
                {
                    MirrorVectorNullable(ref definitePosition);
                }
            }
            else
            {
                definitePosition = null;
            }
        }

        internal static void GetObjectOffset(
            NoodleObjectData.AnimationObjectData? animationObject,
            IEnumerable<Track>? tracks,
            float time,
            out Vector3? positionOffset,
            out Quaternion? rotationOffset,
            out Vector3? scaleOffset,
            out Quaternion? localRotationOffset,
            out float? dissolve,
            out float? dissolveArrow,
            out float? cuttable)
        {
            /*
             * position = SumVectorNullables
             * rotation = MultQuaternionNullables
             * scale = MultVectorNullables
             * localRotation = MultQuaternionNullables
             * dissolve = MultFloatNullables
             * dissolveArrow = MultFloatNullables
             * cuttable = MultFloatNullables
             */
            Vector3? pathPosition = null;
            Quaternion? pathRotation = null;
            Vector3? pathScale = null;
            Quaternion? pathLocalRotation = null;
            float? pathDissolve = null;
            float? pathDissolveArrow = null;
            float? pathCuttable = null;

            if (animationObject != null)
            {
                pathPosition = animationObject.LocalPosition?.Interpolate(time);
                pathRotation = animationObject.LocalRotation?.InterpolateQuaternion(time);
                pathScale = animationObject.LocalScale?.Interpolate(time);
                pathLocalRotation = animationObject.LocalLocalRotation?.InterpolateQuaternion(time);
                pathDissolve = animationObject.LocalDissolve?.InterpolateLinear(time);
                pathDissolveArrow = animationObject.LocalDissolveArrow?.InterpolateLinear(time);
                pathCuttable = animationObject.LocalCuttable?.InterpolateLinear(time);
            }

            if (tracks != null)
            {
                Vector3? trackPosition;
                Quaternion? trackRotation;
                Vector3? trackScale;
                Quaternion? trackLocalRotation;
                float? trackDissolve;
                float? trackDissolveArrow;
                float? trackCuttable;

                if (tracks.Count() > 1)
                {
                    pathPosition ??= SumVectorNullables(tracks.Select(n => TryGetVector3PathProperty(n, POSITION, time)));
                    pathRotation ??= MultQuaternionNullables(tracks.Select(n => TryGetQuaternionPathProperty(n, ROTATION, time)));
                    pathScale ??= MultVectorNullables(tracks.Select(n => TryGetVector3PathProperty(n, SCALE, time)));
                    pathLocalRotation ??= MultQuaternionNullables(tracks.Select(n => TryGetQuaternionPathProperty(n, LOCALROTATION, time)));
                    pathDissolve ??= MultFloatNullables(tracks.Select(n => TryGetLinearPathProperty(n, DISSOLVE, time)));
                    pathDissolveArrow ??= MultFloatNullables(tracks.Select(n => TryGetLinearPathProperty(n, DISSOLVEARROW, time)));
                    pathCuttable ??= MultFloatNullables(tracks.Select(n => TryGetLinearPathProperty(n, CUTTABLE, time)));

                    trackPosition = SumVectorNullables(tracks.Select(n => TryGetProperty<Vector3?>(n, POSITION)));
                    trackRotation = MultQuaternionNullables(tracks.Select(n => TryGetProperty<Quaternion?>(n, ROTATION)));
                    trackScale = MultVectorNullables(tracks.Select(n => TryGetProperty<Vector3?>(n, SCALE)));
                    trackLocalRotation = MultQuaternionNullables(tracks.Select(n => TryGetProperty<Quaternion?>(n, LOCALROTATION)));
                    trackDissolve = MultFloatNullables(tracks.Select(n => TryGetProperty<float?>(n, DISSOLVE)));
                    trackDissolveArrow = MultFloatNullables(tracks.Select(n => TryGetProperty<float?>(n, DISSOLVEARROW)));
                    trackCuttable = MultFloatNullables(tracks.Select(n => TryGetProperty<float?>(n, CUTTABLE)));
                }
                else
                {
                    Track track = tracks.First();
                    pathPosition ??= TryGetVector3PathProperty(track, POSITION, time);
                    pathRotation ??= TryGetQuaternionPathProperty(track, ROTATION, time);
                    pathScale ??= TryGetVector3PathProperty(track, SCALE, time);
                    pathLocalRotation ??= TryGetQuaternionPathProperty(track, LOCALROTATION, time);
                    pathDissolve ??= TryGetLinearPathProperty(track, DISSOLVE, time);
                    pathDissolveArrow ??= TryGetLinearPathProperty(track, DISSOLVEARROW, time);
                    pathCuttable ??= TryGetLinearPathProperty(track, CUTTABLE, time);

                    trackPosition = TryGetProperty<Vector3?>(track, POSITION);
                    trackRotation = TryGetProperty<Quaternion?>(track, ROTATION);
                    trackScale = TryGetProperty<Vector3?>(track, SCALE);
                    trackLocalRotation = TryGetProperty<Quaternion?>(track, LOCALROTATION);
                    trackDissolve = TryGetProperty<float?>(track, DISSOLVE);
                    trackDissolveArrow = TryGetProperty<float?>(track, DISSOLVEARROW);
                    trackCuttable = TryGetProperty<float?>(track, CUTTABLE);
                }

                positionOffset = SumVectorNullables(trackPosition, pathPosition) * NoteLinesDistance;
                rotationOffset = MultQuaternionNullables(trackRotation, pathRotation);
                scaleOffset = MultVectorNullables(trackScale, pathScale);
                localRotationOffset = MultQuaternionNullables(trackLocalRotation, pathLocalRotation);
                dissolve = MultFloatNullables(trackDissolve, pathDissolve);
                dissolveArrow = MultFloatNullables(trackDissolveArrow, pathDissolveArrow);
                cuttable = MultFloatNullables(trackCuttable, pathCuttable);
            }
            else
            {
                positionOffset = pathPosition * NoteLinesDistance;
                rotationOffset = pathRotation;
                scaleOffset = pathScale;
                localRotationOffset = pathLocalRotation;
                dissolve = pathDissolve;
                dissolveArrow = pathDissolveArrow;
                cuttable = pathCuttable;
            }

            if (LeftHandedMode)
            {
                MirrorVectorNullable(ref positionOffset);
                MirrorQuaternionNullable(ref rotationOffset);
                MirrorQuaternionNullable(ref localRotationOffset);
            }
        }

        internal static void GetAllPointData(
            Dictionary<string, object?> customData,
            Dictionary<string, PointDefinition> pointDefinitions,
            out PointDefinition? position,
            out PointDefinition? rotation,
            out PointDefinition? scale,
            out PointDefinition? localRotation,
            out PointDefinition? dissolve,
            out PointDefinition? dissolveArrow,
            out PointDefinition? cuttable,
            out PointDefinition? definitePosition)
        {
            TryGetPointData(customData, POSITION, out position, pointDefinitions);
            TryGetPointData(customData, ROTATION, out rotation, pointDefinitions);
            TryGetPointData(customData, SCALE, out scale, pointDefinitions);
            TryGetPointData(customData, LOCALROTATION, out localRotation, pointDefinitions);
            TryGetPointData(customData, DISSOLVE, out dissolve, pointDefinitions);
            TryGetPointData(customData, DISSOLVEARROW, out dissolveArrow, pointDefinitions);
            TryGetPointData(customData, CUTTABLE, out cuttable, pointDefinitions);
            TryGetPointData(customData, DEFINITEPOSITION, out definitePosition, pointDefinitions);
        }
    }
}


================================================
FILE: NoodleExtensions/Animation/Events/AssignPlayerToTrack.cs
================================================
namespace NoodleExtensions.Animation
{
    using System.Collections.Generic;
    using CustomJSONData;
    using CustomJSONData.CustomBeatmap;
    using Heck.Animation;
    using static NoodleExtensions.Animation.NoodleEventDataManager;
    using static NoodleExtensions.Plugin;

    internal static class AssignPlayerToTrack
    {
        internal static void OnTrackManagerCreated(TrackBuilder trackManager, CustomBeatmapData customBeatmapData)
        {
            List<CustomEventData> customEventsData = customBeatmapData.customEventsData;
            foreach (CustomEventData customEventData in customEventsData)
            {
                if (customEventData.type == ASSIGNPLAYERTOTRACK)
                {
                    string? trackName = customEventData.data.Get<string>(TRACK);
                    if (trackName != null)
                    {
                        trackManager.AddTrack(trackName);
                    }
                }
            }
        }

        internal static void Callback(CustomEventData customEventData)
        {
            if (customEventData.type == ASSIGNPLAYERTOTRACK)
            {
                NoodlePlayerTrackEventData? noodleData = TryGetEventData<NoodlePlayerTrackEventData>(customEventData);
                if (noodleData != null)
                {
                    Track track = noodleData.Track;
                    if (track != null)
                    {
                        PlayerTrack.AssignTrack(track);
                    }
                }
            }
        }
    }
}


================================================
FILE: NoodleExtensions/Animation/Events/AssignTrackParent.cs
================================================
namespace NoodleExtensions.Animation
{
    using System.Collections.Generic;
    using CustomJSONData;
    using CustomJSONData.CustomBeatmap;
    using Heck.Animation;
    using static NoodleExtensions.Animation.NoodleEventDataManager;
    using static NoodleExtensions.Plugin;

    internal class AssignTrackParent
    {
        internal static void OnTrackManagerCreated(TrackBuilder trackManager, CustomBeatmapData customBeatmapData)
        {
            List<CustomEventData> customEventsData = customBeatmapData.customEventsData;
            foreach (CustomEventData customEventData in customEventsData)
            {
                if (customEventData.type == ASSIGNTRACKPARENT)
                {
                    string? trackName = customEventData.data.Get<string>("_parentTrack");
                    if (trackName != null)
                    {
                        trackManager.AddTrack(trackName);
                    }
                }
            }
        }

        internal static void Callback(CustomEventData customEventData)
        {
            if (customEventData.type == ASSIGNTRACKPARENT)
            {
                NoodleParentTrackEventData? noodleData = TryGetEventData<NoodleParentTrackEventData>(customEventData);
                if (noodleData != null)
                {
                    IEnumerable<Track> tracks = noodleData.ChildrenTracks;
                    Track parentTrack = noodleData.ParentTrack;
                    if (tracks != null && parentTrack != null)
                    {
                        ParentObject.AssignTrack(tracks, parentTrack, noodleData.Position, noodleData.Rotation, noodleData.LocalRotation, noodleData.Scale);
                    }
                    else
                    {
                        Logger.Log($"Missing _parentTrack or _childrenTracks!", IPA.Logging.Logger.Level.Error);
                    }
                }
            }
        }
    }
}


================================================
FILE: NoodleExtensions/Animation/Events/NoodleEventData.cs
================================================
namespace NoodleExtensions.Animation
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using CustomJSONData;
    using CustomJSONData.CustomBeatmap;
    using Heck.Animation;
    using UnityEngine;
    using static Heck.Animation.AnimationHelper;
    using static NoodleExtensions.Plugin;

    internal static class NoodleEventDataManager
    {
        private static Dictionary<CustomEventData, NoodleEventData> _noodleEventDatas = new Dictionary<CustomEventData, NoodleEventData>();

        internal static T? TryGetEventData<T>(CustomEventData customEventData)
        {
            if (_noodleEventDatas.TryGetValue(customEventData, out NoodleEventData noodleEventData))
            {
                if (noodleEventData is T t)
                {
                    return t;
                }
                else
                {
                    throw new InvalidOperationException($"NoodleEventData was not of correct type. Expected: {typeof(T).Name}, was: {noodleEventData.GetType().Name}");
                }
            }

            return default;
        }

        internal static void DeserializeBeatmapData(IReadonlyBeatmapData beatmapData)
        {
            _noodleEventDatas = new Dictionary<CustomEventData, NoodleEventData>();
            foreach (CustomEventData customEventData in ((CustomBeatmapData)beatmapData).customEventsData)
            {
                try
                {
                    NoodleEventData noodleEventData;

                    switch (customEventData.type)
                    {
                        case ASSIGNPLAYERTOTRACK:
                            noodleEventData = new NoodlePlayerTrackEventData(GetTrack(customEventData.data, beatmapData) ?? throw new InvalidOperationException("Track was not defined."));
                            break;

                        case ASSIGNTRACKPARENT:
                            noodleEventData = ProcessParentTrackEvent(customEventData.data, beatmapData);
                            break;

                        default:
                            continue;
                    }

                    if (noodleEventData != null)
                    {
                        _noodleEventDatas.Add(customEventData, noodleEventData);
                    }
                }
                catch (Exception e)
                {
                    Plugin.Logger.Log($"Could not create NoodleEventData for event {customEventData.type} at {customEventData.time}", IPA.Logging.Logger.Level.Error);
                    Plugin.Logger.Log(e, IPA.Logging.Logger.Level.Error);
                }
            }
        }

        private static NoodleParentTrackEventData ProcessParentTrackEvent(Dictionary<string, object?> customData, IReadonlyBeatmapData beatmapData)
        {
            IEnumerable<float>? position = customData.Get<List<object>>(POSITION)?.Select(n => Convert.ToSingle(n));
            Vector3? posVector = null;
            if (position != null)
            {
                posVector = new Vector3(position.ElementAt(0), position.ElementAt(1), position.ElementAt(2));
            }

            IEnumerable<float>? rotation = customData.Get<List<object>>(ROTATION)?.Select(n => Convert.ToSingle(n));
            Quaternion? rotQuaternion = null;
            if (rotation != null)
            {
                rotQuaternion = Quaternion.Euler(rotation.ElementAt(0), rotation.ElementAt(1), rotation.ElementAt(2));
            }

            IEnumerable<float>? localrot = customData.Get<List<object>>(LOCALROTATION)?.Select(n => Convert.ToSingle(n));
            Quaternion? localRotQuaternion = null;
            if (localrot != null)
            {
                localRotQuaternion = Quaternion.Euler(localrot.ElementAt(0), localrot.ElementAt(1), localrot.ElementAt(2));
            }

            IEnumerable<float>? scale = customData.Get<List<object>>(SCALE)?.Select(n => Convert.ToSingle(n));
            Vector3? scaleVector = null;
            if (scale != null)
            {
                scaleVector = new Vector3(scale.ElementAt(0), scale.ElementAt(1), scale.ElementAt(2));
            }

            return new NoodleParentTrackEventData(
                GetTrack(customData, beatmapData, "_parentTrack") ?? throw new InvalidOperationException("Parent track was not defined."),
                GetTrackArray(customData, beatmapData, "_childrenTracks") ?? throw new InvalidOperationException("Children track was not defined."),
                posVector,
                rotQuaternion,
                localRotQuaternion,
                scaleVector);
        }
    }

    internal record NoodlePlayerTrackEventData : NoodleEventData
    {
        internal NoodlePlayerTrackEventData(Track track)
        {
            Track = track;
        }

        internal Track Track { get; set; }
    }

    internal record NoodleParentTrackEventData : NoodleEventData
    {
        internal NoodleParentTrackEventData(Track parentTrack, IEnumerable<Track> childrenTracks, Vector3? position, Quaternion? rotation, Quaternion? localRotation, Vector3? scale)
        {
            ParentTrack = parentTrack;
            ChildrenTracks = childrenTracks;
            Position = position;
            Rotation = rotation;
            LocalRotation = localRotation;
            Scale = scale;
        }

        internal Track ParentTrack { get; }

        internal IEnumerable<Track> ChildrenTracks { get; }

        internal Vector3? Position { get; }

        internal Quaternion? Rotation { get; }

        internal Quaternion? LocalRotation { get; }

        internal Vector3? Scale { get; }
    }

    internal record NoodleEventData
    {
    }
}


================================================
FILE: NoodleExtensions/Animation/ParentObject.cs
================================================
namespace NoodleExtensions.Animation
{
    using System.Collections.Generic;
    using System.Linq;
    using Heck.Animation;
    using UnityEngine;
    using static Heck.Animation.AnimationHelper;
    using static Heck.NullableExtensions;
    using static NoodleExtensions.HarmonyPatches.SpawnDataHelper.BeatmapObjectSpawnMovementDataVariables;
    using static NoodleExtensions.Plugin;

    internal class ParentObject : MonoBehaviour
    {
        private Track? _track;
        private Transform? _origin;
        private Vector3 _startPos = Vector3.zero;
        private Quaternion _startRot = Quaternion.identity;
        private Quaternion _startLocalRot = Quaternion.identity;
        private Vector3 _startScale = Vector3.one;

        internal static ParentController? Controller { get; private set; }

        internal HashSet<Track> ChildrenTracks { get; } = new HashSet<Track>();

        internal static void AssignTrack(IEnumerable<Track> tracks, Track parentTrack, Vector3? startPos, Quaternion? startRot, Quaternion? startLocalRot, Vector3? startScale)
        {
            if (tracks.Contains(parentTrack))
            {
                throw new System.InvalidOperationException("How could a track contain itself?");
            }

            if (Controller == null)
            {
                GameObject gameObject = new GameObject("ParentController");
                Controller = gameObject.AddComponent<ParentController>();
            }

            GameObject parentGameObject = new GameObject("ParentObject");
            ParentObject instance = parentGameObject.AddComponent<ParentObject>();
            instance._origin = parentGameObject.transform;
            instance._track = parentTrack;

            Transform transform = instance.transform;
            if (startPos.HasValue)
            {
                instance._startPos = startPos.Value;
                transform.localPosition = instance._startPos * NoteLinesDistance;
            }

            if (startRot.HasValue)
            {
                instance._startRot = startRot.Value;
                instance._startLocalRot = instance._startRot;
                transform.localPosition = instance._startRot * transform.localPosition;
                transform.localRotation = instance._startRot;
            }

            if (startLocalRot.HasValue)
            {
                instance._startLocalRot = instance._startRot * startLocalRot.Value;
                transform.localRotation *= instance._startLocalRot;
            }

            if (startScale.HasValue)
            {
                instance._startScale = startScale.Value;
                transform.localScale = instance._startScale;
            }

            parentTrack.AddGameObject(parentGameObject);

            foreach (Track track in tracks)
            {
                foreach (ParentObject parentObject in Controller.ParentObjects)
                {
                    track.OnGameObjectAdded -= parentObject.OnTrackGameObjectAdded;
                    track.OnGameObjectRemoved -= parentObject.OnTrackGameObjectRemoved;
                    parentObject.ChildrenTracks.Remove(track);
                }

                foreach (GameObject gameObject in track.GameObjects)
                {
                    instance.ParentToObject(gameObject.transform);
                }

                instance.ChildrenTracks.Add(track);

                track.OnGameObjectAdded += instance.OnTrackGameObjectAdded;
                track.OnGameObjectRemoved += instance.OnTrackGameObjectRemoved;
            }

            Controller.ParentObjects.Add(instance);
        }

        private static void ResetTransformParent(Transform transform)
        {
            transform.SetParent(null, false);
        }

        private void OnTrackGameObjectAdded(GameObject gameObject)
        {
            ParentToObject(gameObject.transform);
        }

        private void OnTrackGameObjectRemoved(GameObject gameObject)
        {
            ResetTransformParent(gameObject.transform);
        }

        private void ParentToObject(Transform transform)
        {
            transform.SetParent(_origin!.transform, false);
        }

        private void OnDestroy()
        {
            foreach (Track track in ChildrenTracks)
            {
                track.OnGameObjectAdded -= OnTrackGameObjectAdded;
                track.OnGameObjectRemoved -= OnTrackGameObjectRemoved;
            }
        }

        private void Update()
        {
            Quaternion? rotation = TryGetProperty<Quaternion?>(_track, ROTATION);
            if (rotation.HasValue)
            {
                if (LeftHandedMode)
                {
                    MirrorQuaternionNullable(ref rotation);
                }
            }

            Vector3? position = TryGetProperty<Vector3?>(_track, POSITION);
            if (position.HasValue)
            {
                if (LeftHandedMode)
                {
                    MirrorVectorNullable(ref position);
                }
            }

            Quaternion worldRotationQuatnerion = _startRot;
            Vector3 positionVector = worldRotationQuatnerion * (_startPos * NoteLinesDistance);
            if (rotation.HasValue || position.HasValue)
            {
                Quaternion rotationOffset = rotation ?? Quaternion.identity;
                worldRotationQuatnerion *= rotationOffset;
                Vector3 positionOffset = position ?? Vector3.zero;
                positionVector = worldRotationQuatnerion * ((positionOffset + _startPos) * NoteLinesDistance);
            }

            worldRotationQuatnerion *= _startLocalRot;
            Quaternion? localRotation = TryGetProperty<Quaternion?>(_track, LOCALROTATION);
            if (localRotation.HasValue)
            {
                if (LeftHandedMode)
                {
                    MirrorQuaternionNullable(ref localRotation);
                }

                worldRotationQuatnerion *= localRotation!.Value;
            }

            Vector3 scaleVector = _startScale;
            Vector3? scale = TryGetProperty<Vector3?>(_track, SCALE);
            if (scale.HasValue)
            {
                scaleVector = Vector3.Scale(_startScale, scale.Value);
            }

            if (_origin!.localRotation != worldRotationQuatnerion)
            {
                _origin.localRotation = worldRotationQuatnerion;
            }

            if (_origin.localPosition != positionVector)
            {
                _origin.localPosition = positionVector;
            }

            if (_origin.localScale != scaleVector)
            {
                _origin.localScale = scaleVector;
            }
        }
    }

    internal class ParentController : MonoBehaviour
    {
        internal HashSet<ParentObject> ParentObjects { get; } = new HashSet<ParentObject>();

        internal ParentObject? GetParentObjectTrack(Track track)
        {
            ParentObject filteredParent = ParentObjects.FirstOrDefault(n => n.ChildrenTracks.Contains(track));
            if (filteredParent != null)
            {
                return filteredParent;
            }
            else
            {
                return null;
            }
        }

        internal ParentObject? GetParentObjectTrackArray(IEnumerable<Track> tracks)
        {
            ParentObject filteredParent = ParentObjects.FirstOrDefault(n => n.ChildrenTracks.Intersect(tracks).Any());
            if (filteredParent != null)
            {
                return filteredParent;
            }
            else
            {
                return null;
            }
        }

        private void OnDestroy()
        {
            ParentObjects.Clear();
        }
    }
}


================================================
FILE: NoodleExtensions/Animation/PlayerTrack.cs
================================================
namespace NoodleExtensions.Animation
{
    using Heck.Animation;
    using IPA.Utilities;
    using UnityEngine;
    using static Heck.Animation.AnimationHelper;
    using static Heck.NullableExtensions;
    using static NoodleExtensions.HarmonyPatches.SpawnDataHelper.BeatmapObjectSpawnMovementDataVariables;
    using static NoodleExtensions.Plugin;

    internal class PlayerTrack : MonoBehaviour
    {
        private static readonly FieldAccessor<PauseController, bool>.Accessor _pausedAccessor = FieldAccessor<PauseController, bool>.GetAccessor("_paused");

        private static PlayerTrack? _instance;
        private static Track? _track;
        private static Vector3 _startPos = Vector3.zero;
        private static Quaternion _startRot = Quaternion.identity;
        private static Quaternion _startLocalRot = Quaternion.identity;
        private static Transform? _origin;
        private static PauseController? _pauseController;

        internal static void AssignTrack(Track track)
        {
            if (_instance == null)
            {
                GameObject gameObject = GameObject.Find("LocalPlayerGameCore");
                GameObject noodleObject = new GameObject("NoodlePlayerTrack");
                _origin = noodleObject.transform;
                _origin.SetParent(gameObject.transform.parent, true);
                gameObject.transform.SetParent(_origin, true);

                _instance = noodleObject.AddComponent<PlayerTrack>();
                _pauseController = FindObjectOfType<PauseController>();
                if (_pauseController != null)
                {
                    _pauseController.didPauseEvent += _instance.OnDidPauseEvent;
                }

                _startLocalRot = _origin.localRotation;
                _startPos = _origin.localPosition;
            }

            _track = track;
        }

        private void OnDidPauseEvent()
        {
            _origin!.localRotation = _startLocalRot;
            _origin!.localPosition = _startPos;
        }

        private void OnDestroy()
        {
            if (_pauseController != null)
            {
                _pauseController.didPauseEvent -= OnDidPauseEvent;
            }
        }

        private void Update()
        {
            bool paused = false;
            if (_pauseController != null)
            {
                paused = _pausedAccessor(ref _pauseController);
            }

            if (!paused)
            {
                Quaternion? rotation = TryGetProperty<Quaternion?>(_track, ROTATION);
                if (rotation.HasValue)
                {
                    if (LeftHandedMode)
                    {
                        MirrorQuaternionNullable(ref rotation);
                    }
                }

                Vector3? position = TryGetProperty<Vector3?>(_track, POSITION);
                if (position.HasValue)
                {
                    if (LeftHandedMode)
                    {
                        MirrorVectorNullable(ref position);
                    }
                }

                Quaternion worldRotationQuatnerion = _startRot;
                Vector3 positionVector = _startPos;
                if (rotation.HasValue || position.HasValue)
                {
                    Quaternion finalRot = rotation ?? Quaternion.identity;
                    worldRotationQuatnerion *= finalRot;
                    Vector3 finalPos = position ?? Vector3.zero;
                    positionVector = worldRotationQuatnerion * ((finalPos * NoteLinesDistance) + _startPos);
                }

                worldRotationQuatnerion *= _startLocalRot;
                Quaternion? localRotation = TryGetProperty<Quaternion?>(_track, LOCALROTATION);
                if (localRotation.HasValue)
                {
                    if (LeftHandedMode)
                    {
                        MirrorQuaternionNullable(ref localRotation);
                    }

                    worldRotationQuatnerion *= localRotation!.Value;
                }

                if (_origin!.localRotation != worldRotationQuatnerion)
                {
                    _origin.localRotation = worldRotationQuatnerion;
                }

                if (_origin.localPosition != positionVector)
                {
                    _origin.localPosition = positionVector;
                }
            }
        }
    }
}


================================================
FILE: NoodleExtensions/CutoutManager.cs
================================================
namespace NoodleExtensions
{
    using System.Collections.Generic;
    using System.Reflection;
    using UnityEngine;

    internal static class CutoutManager
    {
        internal static Dictionary<ObstacleControllerBase, CutoutAnimateEffectWrapper> ObstacleCutoutEffects { get; } = new Dictionary<ObstacleControllerBase, CutoutAnimateEffectWrapper>();

        internal static Dictionary<NoteControllerBase, CutoutEffectWrapper> NoteCutoutEffects { get; } = new Dictionary<NoteControllerBase, CutoutEffectWrapper>();

        internal static Dictionary<NoteControllerBase, DisappearingArrowWrapper> NoteDisappearingArrowWrappers { get; } = new Dictionary<NoteControllerBase, DisappearingArrowWrapper>();
    }

    internal abstract class CutoutWrapper
    {
        internal float Cutout { get; private set; } = 1;

        internal virtual void SetCutout(float cutout)
        {
            Cutout = cutout;
        }
    }

    internal class CutoutEffectWrapper : CutoutWrapper
    {
        private readonly CutoutEffect _cutoutEffect;

        internal CutoutEffectWrapper(CutoutEffect cutoutEffect)
        {
            _cutoutEffect = cutoutEffect;
        }

        internal override void SetCutout(float cutout)
        {
            base.SetCutout(cutout);
            _cutoutEffect.SetCutout(1 - cutout);
        }
    }

    internal class CutoutAnimateEffectWrapper : CutoutWrapper
    {
        private readonly CutoutAnimateEffect _cutoutAnimateEffect;

        internal CutoutAnimateEffectWrapper(CutoutAnimateEffect cutoutAnimateEffect)
        {
            _cutoutAnimateEffect = cutoutAnimateEffect;
        }

        internal override void SetCutout(float cutout)
        {
            base.SetCutout(cutout);
            _cutoutAnimateEffect.SetCutout(1 - cutout);
        }
    }

    internal class DisappearingArrowWrapper : CutoutWrapper
    {
        private readonly MonoBehaviour _disappearingArrowController;

        private readonly MethodInfo _method;

        internal DisappearingArrowWrapper(MonoBehaviour disappearingArrowController, MethodInfo method)
        {
            _disappearingArrowController = disappearingArrowController;
            _method = method;
        }

        internal override void SetCutout(float cutout)
        {
            base.SetCutout(cutout);

            // gross nasty reflection
            _method.Invoke(_disappearingArrowController, new object[] { cutout });
        }
    }
}


================================================
FILE: NoodleExtensions/Directory.Build.props
================================================
<?xml version="1.0" encoding="utf-8"?>
<!-- This file contains project properties used by the build. -->
<Project>
  <ItemGroup>
    <SourceRoot Include="$(MSBuildThisFileDirectory)/"/>
  </ItemGroup>
  <PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
    <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
    <DisableCopyToPlugins>true</DisableCopyToPlugins>
    <DisableZipRelease>true</DisableZipRelease>
  </PropertyGroup>
  <PropertyGroup Condition="'$(NCrunch)' == '1'">
    <ContinuousIntegrationBuild>false</ContinuousIntegrationBuild>
    <DisableCopyToPlugins>true</DisableCopyToPlugins>
    <DisableZipRelease>true</DisableZipRelease>
  </PropertyGroup>
</Project>

================================================
FILE: NoodleExtensions/Directory.Build.targets
================================================
<?xml version="1.0" encoding="utf-8"?>
<!-- This file contains the build tasks and targets for verifying the manifest, zipping Release builds,
     and copying the plugin to to your Beat Saber folder. Only edit this if you know what you are doing. -->
<Project>
  <PropertyGroup>
    <BuildTargetsVersion>2.0</BuildTargetsVersion>
    <!--Set this to true if you edit this file to prevent automatic updates-->
    <BuildTargetsModified>false</BuildTargetsModified>
    <!--Output assembly path without extension-->
    <OutputAssemblyName>$(OutputPath)$(AssemblyName)</OutputAssemblyName>
    <!--Path to folder to be zipped. Needs to be relative to the project directory to work without changes to the 'BuildForCI' target.-->
    <ArtifactDestination>$(OutputPath)Final</ArtifactDestination>
    <ErrorOnMismatchedVersions Condition="'$(Configuration)' == 'Release'">True</ErrorOnMismatchedVersions>
  </PropertyGroup>
  <!--Build Targets-->
  <!--Displays a warning if BeatSaberModdingTools.Tasks is not installed.-->
  <Target Name="CheckBSMTInstalled" AfterTargets="BeforeBuild" Condition="'$(BSMTTaskAssembly)' == ''">
    <Warning Text="The BeatSaberModdingTools.Tasks nuget package doesn't seem to be installed, advanced build targets will not work." />
  </Target>
  <!--Runs a build task to get info about the project used by later targets.-->
  <Target Name="GetProjectInfo" AfterTargets="CheckBSMTInstalled" DependsOnTargets="CheckBSMTInstalled" Condition="'$(BSMTTaskAssembly)' != ''">
    <Message Text="Using AssemblyVersion defined in project instead of 'Properties\AssemblyInfo.cs'" Importance="high" Condition="'$(AssemblyVersion)' != ''" />
    <GetManifestInfo FailOnError="$(ErrorOnMismatchedVersions)">
      <Output TaskParameter="PluginVersion" PropertyName="PluginVersion" />
      <Output TaskParameter="BasePluginVersion" PropertyName="BasePluginVersion" />
      <Output TaskParameter="GameVersion" PropertyName="GameVersion" />
    </GetManifestInfo>
    <PropertyGroup>
      <AssemblyVersion>$(BasePluginVersion)</AssemblyVersion>
      <FileVersion>$(BasePluginVersion)</FileVersion>
      <InformationalVersion>$(BasePluginVersion)</InformationalVersion>
    </PropertyGroup>
    <GetCommitInfo ProjectDir="$(ProjectDir)">
      <Output TaskParameter="CommitHash" PropertyName="CommitHash" />
      <Output TaskParameter="Branch" PropertyName="Branch" />
      <Output TaskParameter="Modified" PropertyName="GitModified" />
    </GetCommitInfo>
    <PropertyGroup>
      <!--Build name for artifact/zip file-->
      <ArtifactName>$(AssemblyName)</ArtifactName>
      <ArtifactName Condition="'$(PluginVersion)' != ''">$(ArtifactName)-$(PluginVersion)</ArtifactName>
      <ArtifactName Condition="'$(GameVersion)' != ''">$(ArtifactName)-bs$(GameVersion)</ArtifactName>
      <ArtifactName Condition="'$(CommitHash)' != '' AND '$(CommitHash)' != 'local'">$(ArtifactName)-$(CommitHash)</ArtifactName>
    </PropertyGroup>
  </Target>
  <!--Build target for Continuous Integration builds. Set up for GitHub Actions.-->
  <Target Name="BuildForCI" AfterTargets="Build" DependsOnTargets="GetProjectInfo" Condition="'$(ContinuousIntegrationBuild)' == 'True' AND '$(BSMTTaskAssembly)' != ''">
    <PropertyGroup>
      <!--Set 'ArtifactName' if it failed before.-->
      <ArtifactName Condition="'$(ArtifactName)' == ''">$(AssemblyName)</ArtifactName>
    </PropertyGroup>
    <Message Text="Building for CI" Importance="high" />
    <Message Text="PluginVersion: $(PluginVersion), AssemblyVersion: $(AssemblyVersion), GameVersion: $(GameVersion)" Importance="high" />
    <Message Text="::set-output name=filename::$(ArtifactName)" Importance="high" />
    <Message Text="::set-output name=assemblyname::$(AssemblyName)" Importance="high" />
    <Message Text="::set-output name=artifactpath::$(ProjectDir)$(ArtifactDestination)" Importance="high" />
    <Message Text="Copying '$(OutputAssemblyName).dll' to '$(ProjectDir)$(ArtifactDestination)\Plugins\$(AssemblyName).dll'" Importance="high" />
    <Copy SourceFiles="$(OutputAssemblyName).dll" DestinationFiles="$(ProjectDir)$(ArtifactDestination)\Plugins\$(AssemblyName).dll" />
  </Target>
  <!--Creates a BeatMods compliant zip file with the release.-->
  <Target Name="ZipRelease" AfterTargets="Build" Condition="'$(DisableZipRelease)' != 'True' AND '$(Configuration)' == 'Release' AND '$(BSMTTaskAssembly)' != ''">
    <PropertyGroup>
      <!--Set 'ArtifactName' if it failed before.-->
      <ArtifactName Condition="'$(ArtifactName)' == ''">$(AssemblyName)</ArtifactName>
      <DestinationDirectory>$(OutDir)zip\</DestinationDirectory>
    </PropertyGroup>
    <ItemGroup>
      <OldZips Include="$(DestinationDirectory)$(AssemblyName)*.zip"/>
    </ItemGroup>
    <Copy SourceFiles="$(OutputAssemblyName).dll" DestinationFiles="$(ArtifactDestination)\Plugins\$(AssemblyName).dll" />
    <Message Text="PluginVersion: $(PluginVersion), AssemblyVersion: $(AssemblyVersion), GameVersion: $(GameVersion)" Importance="high" />
    <Delete Files="@(OldZips)" TreatErrorsAsWarnings="true" ContinueOnError="true" />
    <ZipDir SourceDirectory="$(ArtifactDestination)" DestinationFile="$(DestinationDirectory)$(ArtifactName).zip" />
  </Target>
  <!--Copies the assembly and pdb to the Beat Saber folder.-->
  <Target Name="CopyToPlugins" AfterTargets="Build" Condition="'$(DisableCopyToPlugins)' != 'True' AND '$(ContinuousIntegrationBuild)' != 'True'">
    <PropertyGroup>
      <PluginDir>$(BeatSaberDir)\Plugins</PluginDir>
      <CanCopyToPlugins>True</CanCopyToPlugins>
      <CopyToPluginsError Condition="!Exists('$(PluginDir)')">Unable to copy assembly to game folder, did you set 'BeatSaberDir' correctly in your 'csproj.user' file? Plugins folder doesn't exist: '$(PluginDir)'.</CopyToPluginsError>
      <!--Error if 'BeatSaberDir' does not have 'Beat Saber.exe'-->
      <CopyToPluginsError Condition="!Exists('$(BeatSaberDir)\Beat Saber.exe')">Unable to copy to Plugins folder, '$(BeatSaberDir)' does not appear to be a Beat Saber game install.</CopyToPluginsError>
      <!--Error if 'BeatSaberDir' is the same as 'LocalRefsDir'-->
      <CopyToPluginsError Condition="'$(BeatSaberDir)' == '$(LocalRefsDir)' OR '$(BeatSaberDir)' == ''">Unable to copy to Plugins folder, 'BeatSaberDir' has not been set in your 'csproj.user' file.</CopyToPluginsError>
      <CanCopyToPlugins Condition="'$(CopyToPluginsError)' != ''">False</CanCopyToPlugins>
    </PropertyGroup>
    <!--Check if Beat Saber is running-->
    <IsProcessRunning ProcessName="Beat Saber" Condition="'$(BSMTTaskAssembly)' != ''">
      <Output TaskParameter="IsRunning" PropertyName="IsRunning" />
    </IsProcessRunning>
    <PropertyGroup>
      <!--If Beat Saber is running, output to the Pending folder-->
      <PluginDir Condition="'$(IsRunning)' == 'True'">$(BeatSaberDir)\IPA\Pending\Plugins</PluginDir>
    </PropertyGroup>
    <Warning Text="$(CopyToPluginsError)" Condition="'$(CopyToPluginsError)' != ''" />
    <Message Text="Copying '$(OutputAssemblyName).dll' to '$(PluginDir)'." Importance="high" Condition="$(CanCopyToPlugins)" />
    <Copy SourceFiles="$(OutputAssemblyName).dll" DestinationFiles="$(PluginDir)\$(AssemblyName).dll" Condition="$(CanCopyToPlugins)"  />
    <Copy SourceFiles="$(OutputAssemblyName).pdb" DestinationFiles="$(PluginDir)\$(AssemblyName).pdb" Condition="'$(CanCopyToPlugins)' == 'True' AND Exists('$(OutputAssemblyName).pdb')"  />
    <Warning Text="Beat Saber is running, restart the game to use the latest build." Condition="'$(IsRunning)' == 'True'" />
  </Target>
</Project>

================================================
FILE: NoodleExtensions/HarmonyPatches/BeatmapDataLoader.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using CustomJSONData.CustomBeatmap;
    using HarmonyLib;

    [HarmonyPatch(typeof(BeatmapDataLoader))]
    [HarmonyPatch("GetBeatmapDataFromBeatmapSaveData")]
    internal static class BeatmapDataLoaderGetBeatmapDataFromBeatmapSaveData
    {
        private static void Postfix(BeatmapData __result, float startBpm)
        {
            if (__result is CustomBeatmapData customBeatmapData)
            {
                foreach (BeatmapLineData beatmapLineData in customBeatmapData.beatmapLinesData)
                {
                    foreach (BeatmapObjectData beatmapObjectData in beatmapLineData.beatmapObjectsData)
                    {
                        Dictionary<string, object?> dynData = beatmapObjectData.GetDataForObject();

                        // TODO: account for base game bpm changes
                        // for per object njs and spawn offset
                        float bpm = startBpm;
                        dynData["bpm"] = bpm;
                    }
                }
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/BeatmapDataTransformHelper.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;
    using CustomJSONData;
    using CustomJSONData.CustomBeatmap;
    using HarmonyLib;
    using Heck;
    using IPA.Utilities;
    using static NoodleExtensions.Plugin;

    [HeckPatch(typeof(BeatmapDataTransformHelper))]
    [HeckPatch("CreateTransformedBeatmapData")]
    internal static class BeatmapDataTransformHelperCreateTransformedBeatmapData
    {
        private static readonly MethodInfo _reorderLineData = AccessTools.Method(typeof(BeatmapDataTransformHelperCreateTransformedBeatmapData), nameof(ReorderLineData));

        private static readonly FieldAccessor<BeatmapLineData, List<BeatmapObjectData>>.Accessor _beatmapObjectsDataAccessor = FieldAccessor<BeatmapLineData, List<BeatmapObjectData>>.GetAccessor("_beatmapObjectsData");

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)
                .MatchForward(
                    false,
                    new CodeMatch(OpCodes.Ldloc_0),
                    new CodeMatch(OpCodes.Ldarg_0),
                    new CodeMatch(OpCodes.Bne_Un))
                .Advance(1)
                .InsertAndAdvance(
                    new CodeInstruction(OpCodes.Call, _reorderLineData),
                    new CodeInstruction(OpCodes.Stloc_0),
                    new CodeInstruction(OpCodes.Ldloc_0)) // Replace the opcode we replace
                .InstructionEnumeration();
        }

        private static IReadonlyBeatmapData ReorderLineData(IReadonlyBeatmapData beatmapData)
        {
            if (beatmapData is CustomBeatmapData)
            {
                CustomBeatmapData customBeatmapData = (CustomBeatmapData)beatmapData.GetCopy();

                // there is some ambiguity with these variables but who frikkin cares
                float startHalfJumpDurationInBeats = 4;
                float maxHalfJumpDistance = 18;
                float moveDuration = 0.5f;

                for (int i = 0; i < customBeatmapData.beatmapLinesData.Count; i++)
                {
                    BeatmapLineData beatmapLineData = (BeatmapLineData)customBeatmapData.beatmapLinesData[i];
                    foreach (BeatmapObjectData beatmapObjectData in beatmapLineData.beatmapObjectsData)
                    {
                        Dictionary<string, object?> dynData = beatmapObjectData.GetDataForObject();

                        float noteJumpMovementSpeed = dynData.Get<float?>(NOTEJUMPSPEED) ?? GameplayCoreInstallerInstallBindings.CachedNoteJumpMovementSpeed;
                        float noteJumpStartBeatOffset = dynData.Get<float?>(NOTESPAWNOFFSET) ?? GameplayCoreInstallerInstallBindings.CachedNoteJumpStartBeatOffset;

                        // how do i not repeat this in a reasonable way
                        float num = 60f / dynData.Get<float>("bpm");
                        float num2 = startHalfJumpDurationInBeats;
                        while (noteJumpMovementSpeed * num * num2 > maxHalfJumpDistance)
                        {
                            num2 /= 2f;
                        }

                        num2 += noteJumpStartBeatOffset;
                        if (num2 < 1f)
                        {
                            num2 = 1f;
                        }

                        float jumpDuration = num * num2 * 2f;
                        dynData["aheadTime"] = moveDuration + (jumpDuration * 0.5f);
                    }

                    _beatmapObjectsDataAccessor(ref beatmapLineData) = beatmapLineData.beatmapObjectsData
                        .OrderBy(n => n.time - (float)(n.GetDataForObject()["aheadTime"] ?? throw new System.InvalidOperationException($"Could not get aheadTime for [{n.GetType().FullName}] at time [{n.time}].")))
                        .ToList();
                }

                return customBeatmapData;
            }

            Logger.Log("beatmapData was not CustomBeatmapData", IPA.Logging.Logger.Level.Error);
            return beatmapData;
        }

        private static void Postfix(IReadonlyBeatmapData __result)
        {
            // Skip if calling class is MultiplayerConnectPlayerInstaller
            StackTrace stackTrace = new StackTrace();
            if (!stackTrace.GetFrame(2).GetMethod().Name.Contains("MultiplayerConnectedPlayerInstaller"))
            {
                NoodleObjectDataManager.DeserializeBeatmapData(__result);
                Animation.NoodleEventDataManager.DeserializeBeatmapData(__result);
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/BeatmapObjectCallBackController.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Reflection;
    using System.Reflection.Emit;
    using CustomJSONData.CustomBeatmap;
    using HarmonyLib;
    using Heck;
    using static NoodleExtensions.NoodleObjectDataManager;

    [HeckPatch(typeof(BeatmapObjectCallbackController))]
    [HeckPatch("LateUpdate")]
    internal static class BeatmapObjectCallBackControllerLateUpdate
    {
        private static readonly FieldInfo _aheadTimeField = AccessTools.Field(typeof(BeatmapObjectCallbackData), nameof(BeatmapObjectCallbackData.aheadTime));

        private static readonly MethodInfo _getAheadTime = AccessTools.Method(typeof(BeatmapObjectCallBackControllerLateUpdate), nameof(GetAheadTime));
        private static readonly MethodInfo _beatmapObjectSpawnControllerCallback = AccessTools.Method(typeof(BeatmapObjectSpawnController), nameof(BeatmapObjectSpawnController.HandleBeatmapObjectCallback));

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)
                .MatchForward(false, new CodeMatch(OpCodes.Ldfld, _aheadTimeField))
                .Advance(-1)
                .InsertAndAdvance(
                    new CodeInstruction(OpCodes.Ldloc_1),
                    new CodeInstruction(OpCodes.Ldloc_3))
                .Advance(2)
                .Insert(new CodeInstruction(OpCodes.Call, _getAheadTime))
                .InstructionEnumeration();
        }

        private static float GetAheadTime(BeatmapObjectCallbackData beatmapObjectCallbackData, BeatmapObjectData beatmapObjectData, float @default)
        {
            if (beatmapObjectCallbackData.callback.Method == _beatmapObjectSpawnControllerCallback &&
                (beatmapObjectData is CustomObstacleData || beatmapObjectData is CustomNoteData))
            {
                NoodleObjectData? noodleData = TryGetObjectData<NoodleObjectData>(beatmapObjectData);
                if (noodleData != null)
                {
                    float? aheadTime = noodleData.AheadTimeInternal;
                    if (aheadTime.HasValue)
                    {
                        return aheadTime.Value;
                    }
                }
            }

            return @default;
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/BeatmapObjectManager.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using Heck;
    using Heck.Animation;
    using static NoodleExtensions.NoodleObjectDataManager;

    [HeckPatch(typeof(BeatmapObjectManager))]
    [HeckPatch("HandleNoteControllerNoteDidFinishJump")]
    internal static class BeatmapObjectManagerHandleNoteControllerNoteDidFinishJump
    {
        private static void Prefix(NoteController noteController)
        {
            NoodleObjectData? noodleData = TryGetObjectData<NoodleObjectData>(noteController.noteData);
            if (noodleData?.Track != null)
            {
                foreach (Track track in noodleData.Track)
                {
                    track.RemoveGameObject(noteController.gameObject);
                }
            }
        }
    }

    [HeckPatch(typeof(BeatmapObjectManager))]
    [HeckPatch("HandleObstacleFinishedMovement")]
    internal static class BeatmapObjectManagerHandleObstacleFinishedMovement
    {
        private static void Prefix(ObstacleController obstacleController)
        {
            NoodleObjectData? noodleData = TryGetObjectData<NoodleObjectData>(obstacleController.obstacleData);
            if (noodleData?.Track != null)
            {
                foreach (Track track in noodleData.Track)
                {
                    track.RemoveGameObject(obstacleController.gameObject);
                }
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/BeatmapObjectSpawnController.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using Heck;

    [HeckPatch(typeof(BeatmapObjectSpawnController))]
    [HeckPatch("Start")]
    internal static class BeatmapObjectSpawnControllerStart
    {
        internal static BasicBeatmapObjectManager? BeatmapObjectManager { get; private set; }

        private static void Postfix(IBeatmapObjectSpawner ____beatmapObjectSpawner, BeatmapObjectSpawnMovementData ____beatmapObjectSpawnMovementData)
        {
            if (____beatmapObjectSpawner is BasicBeatmapObjectManager basicBeatmapObjectManager)
            {
                BeatmapObjectManager = basicBeatmapObjectManager;
                SpawnDataHelper.InitBeatmapObjectSpawnController(____beatmapObjectSpawnMovementData);
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/BeatmapObjectSpawnMovementData.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using Heck;
    using UnityEngine;
    using static NoodleExtensions.HarmonyPatches.SpawnDataHelper;
    using static NoodleExtensions.HarmonyPatches.SpawnDataHelper.BeatmapObjectSpawnMovementDataVariables;
    using static NoodleExtensions.NoodleObjectDataManager;

    [HeckPatch(typeof(BeatmapObjectSpawnMovementData))]
    [HeckPatch("GetObstacleSpawnData")]
    internal static class BeatmapObjectSpawnMovementDataGetObstacleSpawnData
    {
        private static void Postfix(Vector3 ____centerPos, ObstacleData obstacleData, ref BeatmapObjectSpawnMovementData.ObstacleSpawnData __result)
        {
            NoodleObstacleData? noodleData = TryGetObjectData<NoodleObstacleData>(obstacleData);
            if (noodleData == null)
            {
                return;
            }

            float? njs = noodleData.NJS;
            float? spawnoffset = noodleData.SpawnOffset;

            float? startX = noodleData.StartX;
            float? startY = noodleData.StartY;

            float? height = noodleData.Height;

            Vector3? finalNoteOffset = null;

            Vector3 moveStartPos = __result.moveStartPos;
            Vector3 moveEndPos = __result.moveEndPos;
            Vector3 jumpEndPos = __result.jumpEndPos;
            float obstacleHeight = __result.obstacleHeight;
            GetNoteJumpValues(njs, spawnoffset, out float jumpDuration, out float _, out Vector3 localMoveStartPos, out Vector3 localMoveEndPos, out Vector3 localJumpEndPos);

            // Actual wall stuff
            if (startX.HasValue || startY.HasValue || njs.HasValue || spawnoffset.HasValue)
            {
                // Ripped from base game
                Vector3 noteOffset = GetNoteOffset(obstacleData, startX, null);
                noteOffset.y = startY.HasValue ? VerticalObstaclePosY + (startY.GetValueOrDefault(0) * NoteLinesDistance) : ((obstacleData.obstacleType == ObstacleType.Top)
                    ? (TopObstaclePosY + JumpOffsetY) : VerticalObstaclePosY);

                finalNoteOffset = noteOffset;

                moveStartPos = localMoveStartPos + noteOffset;
                moveEndPos = localMoveEndPos + noteOffset;
                jumpEndPos = localJumpEndPos + noteOffset;
            }

            if (height.HasValue)
            {
                obstacleHeight = height.Value * NoteLinesDistance;
            }

            __result = new BeatmapObjectSpawnMovementData.ObstacleSpawnData(moveStartPos, moveEndPos, jumpEndPos, obstacleHeight, __result.moveDuration, jumpDuration, __result.noteLinesDistance);

            if (!finalNoteOffset.HasValue)
            {
                Vector3 noteOffset = GetNoteOffset(obstacleData, startX, null);
                noteOffset.y = (obstacleData.obstacleType == ObstacleType.Top) ? (TopObstaclePosY + JumpOffsetY) : VerticalObstaclePosY;
                finalNoteOffset = noteOffset;
            }

            noodleData.NoteOffset = ____centerPos + finalNoteOffset.Value;
            float? width = noodleData.Width;
            noodleData.XOffset = ((width.GetValueOrDefault(obstacleData.lineIndex) / 2f) - 0.5f) * NoteLinesDistance;
        }
    }

    [HeckPatch(typeof(BeatmapObjectSpawnMovementData))]
    [HeckPatch("GetJumpingNoteSpawnData")]
    internal static class BeatmapObjectSpawnMovementDataGetJumpingNoteSpawnData
    {
        private static void Postfix(BeatmapObjectSpawnMovementData __instance, Vector3 ____centerPos, float ____jumpDuration, NoteData noteData, ref BeatmapObjectSpawnMovementData.NoteSpawnData __result)
        {
            NoodleNoteData? noodleData = TryGetObjectData<NoodleNoteData>(noteData);
            if (noodleData == null)
            {
                return;
            }

            float? flipLineIndex = noodleData.FlipLineIndexInternal;
            float? njs = noodleData.NJS;
            float? spawnoffset = noodleData.SpawnOffset;
            float? startlinelayer = noodleData.StartNoteLineLayerInternal;

            bool gravityOverride = noodleData.DisableGravity;

            float? startRow = noodleData.StartX;
            float? startHeight = noodleData.StartY;

            float jumpDuration = ____jumpDuration;

            ////Vector3 moveStartPos = __result.moveStartPos;
            ////Vector3 moveEndPos = __result.moveEndPos;
            ////Vector3 jumpEndPos = __result.jumpEndPos;
            float jumpGravity = __result.jumpGravity;

            Vector3 noteOffset = GetNoteOffset(noteData, startRow, startlinelayer ?? (float)noteData.beforeJumpNoteLineLayer);

            if (startRow.HasValue || startHeight.HasValue || flipLineIndex.HasValue || njs.HasValue || spawnoffset.HasValue || startlinelayer.HasValue || gravityOverride)
            {
                GetNoteJumpValues(njs, spawnoffset, out jumpDuration, out float localJumpDistance, out Vector3 localMoveStartPos, out Vector3 localMoveEndPos, out Vector3 localJumpEndPos);

                float localNoteJumpMovementSpeed = njs ?? NoteJumpMovementSpeed;

                float startLayerLineYPos = LineYPosForLineLayer(noteData, startlinelayer ?? (float)noteData.beforeJumpNoteLineLayer);
                float lineYPos = LineYPosForLineLayer(noteData, startHeight);

                // Magic numbers below found with linear regression y=mx+b using existing HighestJumpPosYForLineLayer values
                float highestJump = startHeight.HasValue ? (0.875f * lineYPos) + 0.639583f + JumpOffsetY :
                    __instance.HighestJumpPosYForLineLayer(noteData.noteLineLayer);

                float GetJumpGravity(float lineYPos) => 2f * (highestJump - (gravityOverride ? lineYPos : startLayerLineYPos)) /
                    Mathf.Pow(localJumpDistance / localNoteJumpMovementSpeed * 0.5f, 2f);

                jumpGravity = GetJumpGravity(startLayerLineYPos);

                float newJumpGravity = gravityOverride ? GetJumpGravity(lineYPos) : jumpGravity;

                Vector3 jumpEndPos = localJumpEndPos + noteOffset;

                // IsBasicNote() check is skipped so bombs can flip too
                Vector3 noteOffset2 = GetNoteOffset(noteData, flipLineIndex ?? startRow, gravityOverride ? startHeight : startlinelayer ?? (float)noteData.beforeJumpNoteLineLayer);
                Vector3 moveStartPos = localMoveStartPos + noteOffset2;
                Vector3 moveEndPos = localMoveEndPos + noteOffset2;

                __result = new BeatmapObjectSpawnMovementData.NoteSpawnData(moveStartPos, moveEndPos, jumpEndPos, newJumpGravity, __result.moveDuration, jumpDuration);
            }

            // DEFINITE POSITION IS WEIRD, OK?
            // fuck
            float num = jumpDuration * 0.5f;
            float startVerticalVelocity = jumpGravity * num;
            float yOffset = (startVerticalVelocity * num) - (jumpGravity * num * num * 0.5f);
            noodleData.NoteOffset = ____centerPos + noteOffset + new Vector3(0, yOffset, 0);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/BeatmapObjectsInTimeRowProcessor.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Linq;
    using CustomJSONData.CustomBeatmap;
    using HarmonyLib;
    using Heck;
    using static NoodleExtensions.Plugin;

    [HarmonyPatch(typeof(BeatmapObjectsInTimeRowProcessor))]
    [HarmonyPatch("ProcessAllNotesInTimeRow")]
    internal static class NotesInTimeRowProcessorProcessAllNotesInTimeRow
    {
        private static void Prefix(List<NoteData> notesInTimeRow)
        {
            if (notesInTimeRow.FirstOrDefault() is CustomNoteData)
            {
                List<CustomNoteData> notesToSetFlip = new List<CustomNoteData>();

                Dictionary<float, List<CustomNoteData>> notesInColumns = new Dictionary<float, List<CustomNoteData>>();
                for (int i = 0; i < notesInTimeRow.Count; i++)
                {
                    CustomNoteData noteData = (CustomNoteData)notesInTimeRow[i];
                    Dictionary<string, object?> dynData = noteData.customData;

                    IEnumerable<float?>? position = dynData.GetNullableFloats(POSITION);
                    float lineIndex = position?.ElementAtOrDefault(0) ?? (noteData.lineIndex - 2);
                    float lineLayer = position?.ElementAtOrDefault(1) ?? (float)noteData.noteLineLayer;

                    if (!notesInColumns.TryGetValue(lineIndex, out List<CustomNoteData> list))
                    {
                        list = new List<CustomNoteData>(1);
                        notesInColumns.Add(lineIndex, list);
                    }

                    bool flag = false;
                    for (int k = 0; k < list.Count; k++)
                    {
                        IEnumerable<float?>? listPosition = list[k].customData.GetNullableFloats(POSITION);
                        float listLineLayer = listPosition?.ElementAtOrDefault(1) ?? (float)list[k].noteLineLayer;
                        if (listLineLayer > lineLayer)
                        {
                            list.Insert(k, noteData);
                            flag = true;
                            break;
                        }
                    }

                    if (!flag)
                    {
                        list.Add(noteData);
                    }

                    // Flippy stuff
                    IEnumerable<float?>? flip = dynData.GetNullableFloats(FLIP);
                    float? flipX = flip?.ElementAtOrDefault(0);
                    float? flipY = flip?.ElementAtOrDefault(1);
                    if (flipX.HasValue || flipY.HasValue)
                    {
                        if (flipX.HasValue)
                        {
                            dynData["flipLineIndex"] = flipX.Value;
                        }

                        if (flipY.HasValue)
                        {
                            dynData["flipYSide"] = flipY.Value;
                        }
                    }
                    else if (!dynData.ContainsKey("flipYSide"))
                    {
                        notesToSetFlip.Add(noteData);
                    }
                }

                foreach (KeyValuePair<float, List<CustomNoteData>> keyValue in notesInColumns)
                {
                    List<CustomNoteData> list2 = keyValue.Value;
                    for (int m = 0; m < list2.Count; m++)
                    {
                        list2[m].customData["startNoteLineLayer"] = m;
                    }
                }

                // Process flip data
                notesToSetFlip.ForEach(c => c.customData["flipYSide"] = 0);
            }
        }
    }

    [HarmonyPatch(typeof(BeatmapObjectsInTimeRowProcessor))]
    [HarmonyPatch("ProcessColorNotesInTimeRow")]
    internal static class NotesInTimeRowProcessorProcessColorNotesInTimeRow
    {
        private static void Prefix(List<NoteData> colorNotesData)
        {
            if (colorNotesData.FirstOrDefault() is CustomNoteData)
            {
                int customNoteCount = colorNotesData.Count;
                if (customNoteCount == 2)
                {
                    float[] lineIndexes = new float[2];
                    float[] lineLayers = new float[2];
                    for (int i = 0; i < customNoteCount; i++)
                    {
                        Dictionary<string, object?> dynData = ((CustomNoteData)colorNotesData[i]).customData;
                        IEnumerable<float?>? position = dynData.GetNullableFloats(POSITION);
                        lineIndexes[i] = position?.ElementAtOrDefault(0) ?? (colorNotesData[i].lineIndex - 2);
                        lineLayers[i] = position?.ElementAtOrDefault(1) ?? (float)colorNotesData[i].noteLineLayer;
                    }

                    if (colorNotesData[0].colorType != colorNotesData[1].colorType && ((colorNotesData[0].colorType == ColorType.ColorA && lineIndexes[0] > lineIndexes[1]) ||
                        (colorNotesData[0].colorType == ColorType.ColorB && lineIndexes[0] < lineIndexes[1])))
                    {
                        for (int i = 0; i < customNoteCount; i++)
                        {
                            // apparently I can use customData to store my own variables in noteData, neat
                            // ^ comment from a very young and naive aero
                            Dictionary<string, object?> dynData = ((CustomNoteData)colorNotesData[i]).customData;
                            dynData["flipLineIndex"] = lineIndexes[1 - i];

                            float flipYSide = (lineIndexes[i] > lineIndexes[1 - i]) ? 1 : -1;
                            if ((lineIndexes[i] > lineIndexes[1 - i] && lineLayers[i] < lineLayers[1 - i]) || (lineIndexes[i] < lineIndexes[1 - i] &&
                                lineLayers[i] > lineLayers[1 - i]))
                            {
                                flipYSide *= -1f;
                            }

                            dynData["flipYSide"] = flipYSide;
                        }
                    }
                }
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/Cutout/BaseNoteVisual.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using Heck;
    using IPA.Utilities;
    using UnityEngine;

    [HeckPatch(typeof(BaseNoteVisuals))]
    [HeckPatch("Awake")]
    internal static class BaseNoteVisualsAwake
    {
        private static readonly Dictionary<Type, MethodInfo> _setArrowTransparencyMethods = new Dictionary<Type, MethodInfo>();
        private static readonly FieldAccessor<BaseNoteVisuals, CutoutAnimateEffect>.Accessor _noteCutoutAnimateEffectAccessor = FieldAccessor<BaseNoteVisuals, CutoutAnimateEffect>.GetAccessor("_cutoutAnimateEffect");
        private static readonly FieldAccessor<CutoutAnimateEffect, CutoutEffect[]>.Accessor _cutoutEffectAccessor = FieldAccessor<CutoutAnimateEffect, CutoutEffect[]>.GetAccessor("_cuttoutEffects");

        private static void Postfix(BaseNoteVisuals __instance, NoteControllerBase ____noteController)
        {
            GameObject gameObject = __instance.gameObject;

            BaseNoteVisuals baseNoteVisuals = gameObject.GetComponent<BaseNoteVisuals>();
            CutoutAnimateEffect cutoutAnimateEffect = _noteCutoutAnimateEffectAccessor(ref baseNoteVisuals);
            CutoutEffect[] cutoutEffects = _cutoutEffectAccessor(ref cutoutAnimateEffect);
            CutoutEffect cutoutEffect = cutoutEffects.First(n => n.name != "NoteArrow"); // 1.11 NoteArrow has been added to the CutoutAnimateEffect and we don't want that
            CutoutManager.NoteCutoutEffects.Add(____noteController, new CutoutEffectWrapper(cutoutEffect));

            if (____noteController is ICubeNoteTypeProvider)
            {
                Type constructed = typeof(DisappearingArrowControllerBase<>).MakeGenericType(____noteController.GetType());
                MonoBehaviour disappearingArrowController = (MonoBehaviour)gameObject.GetComponent(constructed);
                MethodInfo method = GetSetArrowTransparency(disappearingArrowController.GetType());
                DisappearingArrowWrapper wrapper = new DisappearingArrowWrapper(disappearingArrowController, method);
                wrapper.SetCutout(1); // i have no fucking idea how this fixes the weird ghost arrow bug
                CutoutManager.NoteDisappearingArrowWrappers.Add(____noteController, wrapper);
            }
        }

        private static MethodInfo GetSetArrowTransparency(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            if (_setArrowTransparencyMethods.TryGetValue(type, out MethodInfo value))
            {
                return value;
            }

            Type baseType = type.BaseType;
            ////NoodleLogger.IPAlogger.Debug($"Base type is {baseType.Name}<{string.Join(", ", baseType.GenericTypeArguments.Select(t => t.Name))}>");
            MethodInfo method = baseType.GetMethod("SetArrowTransparency", BindingFlags.NonPublic | BindingFlags.Instance);
            _setArrowTransparencyMethods[type] = method ?? throw new InvalidOperationException($"Type [{type.FullName}] does not contain method [SetArrowTransparency]");
            return method;
        }
    }

    [HeckPatch(typeof(BaseNoteVisuals))]
    [HeckPatch("OnDestroy")]
    internal static class DisappearingArrowControllerBaseGameNoteControllerOnDestroy
    {
        private static void Postfix(NoteControllerBase ____noteController)
        {
            CutoutManager.NoteCutoutEffects.Remove(____noteController);
            CutoutManager.NoteDisappearingArrowWrappers.Remove(____noteController);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/Cutout/ObstacleDissolve.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using Heck;

    [HeckPatch(typeof(ObstacleDissolve))]
    [HeckPatch("Awake")]
    internal static class ObstacleDissolveAwake
    {
        private static void Postfix(ObstacleController ____obstacleController, CutoutAnimateEffect ____cutoutAnimateEffect)
        {
            CutoutManager.ObstacleCutoutEffects.Add(____obstacleController, new CutoutAnimateEffectWrapper(____cutoutAnimateEffect));
        }
    }

    [HeckPatch(typeof(ObstacleDissolve))]
    [HeckPatch("OnDestroy")]
    internal static class ObstacleDissolveOnDestroy
    {
        private static void Postfix(ObstacleController ____obstacleController)
        {
            CutoutManager.ObstacleCutoutEffects.Remove(____obstacleController);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BadNoteCutEffectSpawner.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using Heck;

    [HeckPatch(typeof(BadNoteCutEffectSpawner))]
    [HeckPatch("HandleNoteWasCut")]
    internal static class BadNoteCutEffectSpawnerHandleNoteWasCut
    {
        private static bool Prefix(NoteController noteController)
        {
            return FakeNoteHelper.GetFakeNote(noteController);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BeatmapData.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;
    using CustomJSONData;
    using CustomJSONData.CustomBeatmap;
    using HarmonyLib;

    [HarmonyPatch(typeof(BeatmapData))]
    [HarmonyPatch(MethodType.Constructor)]
    [HarmonyPatch(new Type[] { typeof(int) })]
    internal static class BeatmapDataCtor
    {
        private static void Postfix()
        {
            BeatmapDataAddBeatmapObjectData.NeedsCheck = true;
        }
    }

    [HarmonyPatch(typeof(BeatmapData))]
    [HarmonyPatch("AddBeatmapObjectData")]
    internal static class BeatmapDataAddBeatmapObjectData
    {
        private static readonly List<object> _countGetters = new List<object>
        {
            AccessTools.PropertyGetter(typeof(BeatmapData), nameof(BeatmapData.obstaclesCount)),
            AccessTools.PropertyGetter(typeof(BeatmapData), nameof(BeatmapData.cuttableNotesCount)),
            AccessTools.PropertyGetter(typeof(BeatmapData), nameof(BeatmapData.bombsCount)),
        };

        private static readonly MethodInfo _fakeObjectCheck = AccessTools.Method(typeof(BeatmapDataAddBeatmapObjectData), nameof(FakeObjectCheck));
        private static bool _noodleRequirement;

        internal static bool NeedsCheck { get; set; }

        private static void Prefix(BeatmapData __instance)
        {
            if (NeedsCheck)
            {
                NeedsCheck = false;
                if (__instance is CustomBeatmapData customBeatmapData)
                {
                    IEnumerable<string>? requirements = customBeatmapData.beatmapCustomData.Get<List<object>>("_requirements")?.Cast<string>();
                    _noodleRequirement = requirements?.Contains(Plugin.CAPABILITY) ?? false;
                }
                else
                {
                    _noodleRequirement = false;
                }
            }
        }

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)
                .MatchForward(false, new CodeMatch(OpCodes.Call) { operands = _countGetters })
                .Repeat(n => n
                    .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0))
                    .Advance(1)
                    .InsertAndAdvance(
                        new CodeInstruction(OpCodes.Ldarg_1),
                        new CodeInstruction(OpCodes.Call, _fakeObjectCheck))
                    .RemoveInstructions(5))
                .InstructionEnumeration();
        }

        private static int FakeObjectCheck(int objectCount, BeatmapObjectData beatmapObjectData)
        {
            if (_noodleRequirement)
            {
                bool? fake = beatmapObjectData.GetDataForObject().Get<bool?>(Plugin.FAKENOTE);
                if (fake.HasValue && fake.Value)
                {
                    return objectCount;
                }
            }

            return objectCount + 1;
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BeatmapObjectExecutionRatingsRecorder.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using HarmonyLib;
    using Heck;

    [HeckPatch(typeof(BeatmapObjectExecutionRatingsRecorder))]
    [HeckPatch("Update")]
    internal static class BeatmapObjectExecutionRatingsRecorderUpdate
    {
        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return FakeNoteHelper.ObstaclesTranspiler(instructions);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BeatmapObjectManager.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System;
    using System.Reflection;
    using HarmonyLib;
    using Heck;

    [HeckPatch(typeof(BeatmapObjectManager))]
    [HeckPatch("HandleNoteControllerNoteWasCut")]
    internal static class BeatmapObjectManagerHandleNoteWasCut
    {
        private static readonly MethodInfo _despawnMethod = AccessTools.Method(typeof(BeatmapObjectManager), "Despawn", new Type[] { typeof(NoteController) });

        [HarmonyPriority(Priority.High)]
        private static bool Prefix(BeatmapObjectManager __instance, NoteController noteController, in NoteCutInfo noteCutInfo)
        {
            if (!FakeNoteHelper.GetFakeNote(noteController))
            {
                NoteCutCoreEffectsSpawnerStart.NoteCutCoreEffectsSpawner!.HandleNoteWasCut(noteController, noteCutInfo);
                _despawnMethod.Invoke(__instance, new object[] { noteController });

                return false;
            }

            return true;
        }
    }

    [HeckPatch(typeof(BeatmapObjectManager))]
    [HeckPatch("HandleNoteControllerNoteWasMissed")]
    internal static class BeatmapObjectManagerHandleNoteWasMissed
    {
        [HarmonyPriority(Priority.High)]
        private static bool Prefix(NoteController noteController)
        {
            return FakeNoteHelper.GetFakeNote(noteController);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BombCutSoundEffectManager.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using Heck;

    [HeckPatch(typeof(BombCutSoundEffectManager))]
    [HeckPatch("HandleNoteWasCut")]
    internal static class BombCutSoundEffectManagerHandleNoteWasCut
    {
        // Do not create a BombCutSoundEffect for fake notes
        private static bool Prefix(NoteController noteController)
        {
            return FakeNoteHelper.GetFakeNote(noteController);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BombNoteController.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using HarmonyLib;
    using Heck;

    [HeckPatch(typeof(BombNoteController))]
    [HeckPatch("Init")]
    internal static class BombNoteControllerInit
    {
        [HarmonyPriority(Priority.High)]
        private static void Postfix(NoteData noteData, CuttableBySaber ____cuttableBySaber)
        {
            if (!FakeNoteHelper.GetCuttable(noteData))
            {
                ____cuttableBySaber.canBeCut = false;
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/FakeNoteHelper.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;
    using HarmonyLib;
    using UnityEngine;
    using static NoodleExtensions.NoodleObjectDataManager;

    internal static class FakeNoteHelper
    {
        internal static readonly MethodInfo _boundsNullCheck = AccessTools.Method(typeof(FakeNoteHelper), nameof(BoundsNullCheck));

        private static readonly MethodInfo _intersectingObstaclesGetter = AccessTools.PropertyGetter(typeof(PlayerHeadAndObstacleInteraction), nameof(PlayerHeadAndObstacleInteraction.intersectingObstacles));
        private static readonly MethodInfo _obstacleFakeCheck = AccessTools.Method(typeof(FakeNoteHelper), nameof(ObstacleFakeCheck));

        internal static bool GetFakeNote(NoteController noteController)
        {
            NoodleNoteData? noodleData = TryGetObjectData<NoodleNoteData>(noteController.noteData);
            if (noodleData != null)
            {
                bool? fake = noodleData.Fake;
                if (fake.HasValue && fake.Value)
                {
                    return false;
                }
            }

            return true;
        }

        internal static bool GetCuttable(NoteData noteData)
        {
            NoodleNoteData? noodleData = TryGetObjectData<NoodleNoteData>(noteData);
            if (noodleData != null)
            {
                bool? cuttable = noodleData.Cuttable;
                if (cuttable.HasValue && !cuttable.Value)
                {
                    return false;
                }
            }

            return true;
        }

        internal static IEnumerable<CodeInstruction> ObstaclesTranspiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)
                .MatchForward(false, new CodeMatch(OpCodes.Callvirt, _intersectingObstaclesGetter))
                .Advance(1)
                .Insert(new CodeInstruction(OpCodes.Call, _obstacleFakeCheck))
                .InstructionEnumeration();
        }

        private static bool BoundsNullCheck(ObstacleController obstacleController)
        {
            return obstacleController.bounds.size == Vector3.zero;
        }

        private static List<ObstacleController> ObstacleFakeCheck(List<ObstacleController> intersectingObstacles)
        {
            return intersectingObstacles.Where(n =>
            {
                NoodleObstacleData? noodleData = TryGetObjectData<NoodleObstacleData>(n.obstacleData);
                if (noodleData != null)
                {
                    bool? fake = noodleData.Fake;
                    if (fake.HasValue && fake.Value)
                    {
                        return false;
                    }
                }

                return true;
            }).ToList();
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/GameEnergyCounter.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using HarmonyLib;
    using Heck;

    [HeckPatch(typeof(GameEnergyCounter))]
    [HeckPatch("LateUpdate")]
    internal static class GameEnergyCounterUpdate
    {
        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return FakeNoteHelper.ObstaclesTranspiler(instructions);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/GameNoteController.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using HarmonyLib;
    using Heck;

    [HeckPatch(typeof(GameNoteController))]
    [HeckPatch("NoteDidStartJump")]
    internal static class GameNoteControllerNoteDidStartJump
    {
        [HarmonyPriority(Priority.High)]
        private static bool Prefix(GameNoteController __instance)
        {
            return FakeNoteHelper.GetCuttable(__instance.noteData);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/NoteCutCoreEffectSpawner.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using Heck;

    [HeckPatch(typeof(NoteCutCoreEffectsSpawner))]
    [HeckPatch("Start")]
    internal static class NoteCutCoreEffectsSpawnerStart
    {
        internal static NoteCutCoreEffectsSpawner? NoteCutCoreEffectsSpawner { get; private set; }

        private static void Postfix(NoteCutCoreEffectsSpawner __instance)
        {
            NoteCutCoreEffectsSpawner = __instance;
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/NoteCutScoreSpawner.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using Heck;

    [HeckPatch(typeof(NoteCutScoreSpawner))]
    [HeckPatch("HandleNoteWasCut")]
    internal static class NoteCutScoreSpawnerHandleNoteWasCut
    {
        private static bool Prefix(NoteController noteController)
        {
            return FakeNoteHelper.GetFakeNote(noteController);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/NoteCutSoundEffectManager.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using Heck;
    using UnityEngine;

    [HeckPatch(typeof(NoteCutSoundEffectManager))]
    [HeckPatch("Start")]
    internal static class NoteCutSoundEffectManagerStart
    {
        private static void Postfix(NoteCutSoundEffectManager __instance)
        {
            NoodleCutSoundEffectManager noodleManager = __instance.gameObject.AddComponent<NoodleCutSoundEffectManager>();
            noodleManager.Init(__instance);
            NoteCutSoundEffectManagerHandleNoteWasSpawned.NoodleManager = noodleManager;
        }
    }

    [HeckPatch(typeof(NoteCutSoundEffectManager))]
    [HeckPatch("HandleNoteWasSpawned")]
    internal static class NoteCutSoundEffectManagerHandleNoteWasSpawned
    {
        internal static NoodleCutSoundEffectManager? NoodleManager { get; set; }

        // Do not create a NoteCutSoundEffect for fake notes
        private static bool Prefix(NoteController noteController)
        {
            if (FakeNoteHelper.GetFakeNote(noteController))
            {
                return NoodleManager!.ProcessHitSound(noteController);
            }

            return false;
        }
    }

    // Weird cut sound shenanigans to prevent unity from crashing.
    internal class NoodleCutSoundEffectManager : MonoBehaviour
    {
        private readonly List<NoteController> _hitsoundQueue = new List<NoteController>();

        private NoteCutSoundEffectManager? _noteCutSoundEffectManager;
        private int _lastFrame = -1;
        private int _cutCount = -1;

        internal void Init(NoteCutSoundEffectManager noteCutSoundEffectManager)
        {
            _noteCutSoundEffectManager = noteCutSoundEffectManager;
        }

        internal bool ProcessHitSound(NoteController noteController)
        {
            if (Time.frameCount == _lastFrame)
            {
                _cutCount++;
            }
            else
            {
                _lastFrame = Time.frameCount;
                _cutCount = 1;
            }

            // We do not allow more than 30 NoteCutSoundEffects to be created in a single frame to prevent unity from dying
            if (_cutCount < 30)
            {
                return true;
            }
            else
            {
                _hitsoundQueue.Add(noteController);
                return false;
            }
        }

        private void Update()
        {
            if (_hitsoundQueue.Count > 0 && Time.frameCount != _lastFrame)
            {
                List<NoteController> noteControllers = new List<NoteController>(_hitsoundQueue);
                _hitsoundQueue.Clear();
                noteControllers.ForEach(_noteCutSoundEffectManager!.HandleNoteWasSpawned);
                Plugin.Logger.Log($"{noteControllers.Count} cut sounds moved to next frame!");
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/ObstacleSaberSparkleEffectManager.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Reflection;
    using System.Reflection.Emit;
    using HarmonyLib;
    using Heck;

    [HeckPatch(typeof(ObstacleSaberSparkleEffectManager))]
    [HeckPatch("Update")]
    internal static class ObstacleSaberSparkleEffectManagerUpdate
    {
        private static readonly MethodInfo _currentGetter = AccessTools.PropertyGetter(typeof(List<ObstacleController>.Enumerator), nameof(List<ObstacleController>.Enumerator.Current));

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            CodeMatcher codeMatcher = new CodeMatcher(instructions)
                .MatchForward(false, new CodeMatch(OpCodes.Br));

            object label = codeMatcher.Operand;

            return codeMatcher
                .MatchForward(false, new CodeMatch(OpCodes.Call, _currentGetter))
                .Advance(2)
                .Insert(
                    new CodeInstruction(OpCodes.Ldloc_1),
                    new CodeInstruction(OpCodes.Call, FakeNoteHelper._boundsNullCheck),
                    new CodeInstruction(OpCodes.Brtrue_S, label))
                .InstructionEnumeration();
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/FakeNotes/PlayerHeadAndObstacleInteraction.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Reflection;
    using System.Reflection.Emit;
    using HarmonyLib;
    using Heck;

    [HeckPatch(typeof(PlayerHeadAndObstacleInteraction))]
    [HeckPatch("GetObstaclesContainingPoint")]
    internal static class PlayerHeadAndObstacleInteractionGetObstaclesContainingPoint
    {
        private static readonly MethodInfo _currentGetter = AccessTools.PropertyGetter(typeof(List<ObstacleController>.Enumerator), nameof(List<ObstacleController>.Enumerator.Current));

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            CodeMatcher codeMatcher = new CodeMatcher(instructions)
                .MatchForward(false, new CodeMatch(OpCodes.Br));

            object label = codeMatcher.Operand;

            return codeMatcher
                .MatchForward(false, new CodeMatch(OpCodes.Call, _currentGetter))
                .Advance(2)
                .Insert(
                    new CodeInstruction(OpCodes.Ldloc_1),
                    new CodeInstruction(OpCodes.Call, FakeNoteHelper._boundsNullCheck),
                    new CodeInstruction(OpCodes.Brtrue_S, label))
                .InstructionEnumeration();
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/GameplayCoreInstaller.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Reflection;
    using System.Reflection.Emit;
    using HarmonyLib;
    using Heck;

    [HeckPatch(typeof(GameplayCoreInstaller))]
    [HeckPatch("InstallBindings")]
    internal static class GameplayCoreInstallerInstallBindings
    {
        private static readonly MethodInfo _createTransformedBeatmapData = AccessTools.Method(typeof(BeatmapDataTransformHelper), nameof(BeatmapDataTransformHelper.CreateTransformedBeatmapData));

        private static readonly MethodInfo _cacheNoteJumpValues = AccessTools.Method(typeof(GameplayCoreInstallerInstallBindings), nameof(CacheNoteJumpValues));

        internal static float CachedNoteJumpMovementSpeed { get; private set; }

        internal static float CachedNoteJumpStartBeatOffset { get; private set; }

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)
                .MatchForward(false, new CodeMatch(OpCodes.Call, _createTransformedBeatmapData))
                .Insert(
                    new CodeInstruction(OpCodes.Ldloc_S, 9),
                    new CodeInstruction(OpCodes.Ldloc_S, 10),
                    new CodeInstruction(OpCodes.Call, _cacheNoteJumpValues))
                .InstructionEnumeration();
        }

        private static void CacheNoteJumpValues(float defaultNoteJumpMovementSpeed, float defaultNoteJumpStartBeatOffset)
        {
            CachedNoteJumpMovementSpeed = defaultNoteJumpMovementSpeed;
            CachedNoteJumpStartBeatOffset = defaultNoteJumpStartBeatOffset;
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/LeftHanded/BeatmapDataMirrorTransform.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using CustomJSONData;
    using CustomJSONData.CustomBeatmap;
    using HarmonyLib;
    using static NoodleExtensions.Plugin;

    [HarmonyPatch(typeof(BeatmapDataMirrorTransform))]
    [HarmonyPatch("CreateTransformedData")]
    internal static class BeatDataMirrorTransformCreateTransformedData
    {
        private static void Postfix(IReadonlyBeatmapData __result)
        {
            foreach (BeatmapEventData beatmapEventData in __result.beatmapEventsData)
            {
                if (beatmapEventData.type.IsRotationEvent())
                {
                    if (beatmapEventData is CustomBeatmapEventData customData)
                    {
                        Dictionary<string, object?> dynData = customData.customData;
                        float? rotation = dynData.Get<float?>(ROTATION);

                        if (rotation.HasValue)
                        {
                            dynData["_rotation"] = rotation * -1;
                        }
                    }
                }
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/LeftHanded/NoteData.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using CustomJSONData;
    using CustomJSONData.CustomBeatmap;
    using HarmonyLib;
    using Heck;
    using UnityEngine;
    using static NoodleExtensions.Plugin;

    [HarmonyPatch(typeof(NoteData))]
    [HarmonyPatch("Mirror")]
    internal static class NoteDataMirror
    {
        private static void Postfix(NoteData __instance)
        {
            if (__instance is CustomNoteData customData)
            {
                Dictionary<string, object?> dynData = customData.customData;
                IEnumerable<float?>? position = dynData.GetNullableFloats(POSITION);
                float? flipLineIndex = dynData.Get<float?>("flipLineIndex");
                IEnumerable<float?>? flip = dynData.GetNullableFloats(FLIP);
                List<float>? localrot = dynData.Get<List<object>>(LOCALROTATION)?.Select(n => Convert.ToSingle(n)).ToList();
                object? rotation = dynData.Get<object>(ROTATION);

                float? startRow = position?.ElementAtOrDefault(0);
                float? flipX = flip?.ElementAtOrDefault(0);

                if (startRow.HasValue)
                {
                    dynData[POSITION] = new List<object?>() { ((startRow.Value + 0.5f) * -1) - 0.5f, position.ElementAtOrDefault(1) };
                }

                if (flipLineIndex.HasValue)
                {
                    dynData["flipLineIndex"] = ((flipLineIndex.Value + 0.5f) * -1) - 0.5f;
                }

                if (flipX.HasValue)
                {
                    dynData[FLIP] = new List<object?>() { ((flipX.Value + 0.5f) * -1) - 0.5f, flip.ElementAtOrDefault(1) };
                }

                if (localrot != null)
                {
                    List<float> rot = localrot.Select(n => Convert.ToSingle(n)).ToList();
                    Quaternion modifiedVector = Quaternion.Euler(rot[0], rot[1], rot[2]);
                    Vector3 vector = new Quaternion(modifiedVector.x, modifiedVector.y * -1, modifiedVector.z * -1, modifiedVector.w).eulerAngles;
                    dynData[LOCALROTATION] = new List<object> { vector.x, vector.y, vector.z };
                }

                if (rotation != null)
                {
                    if (rotation is List<object> list)
                    {
                        List<float> rot = list.Select(n => Convert.ToSingle(n)).ToList();
                        Quaternion modifiedVector = Quaternion.Euler(rot[0], rot[1], rot[2]);
                        Vector3 vector = new Quaternion(modifiedVector.x, modifiedVector.y * -1, modifiedVector.z * -1, modifiedVector.w).eulerAngles;
                        dynData[ROTATION] = new List<object> { vector.x, vector.y, vector.z };
                    }
                    else
                    {
                        dynData[ROTATION] = Convert.ToSingle(rotation) * -1;
                    }
                }

                float? cutDirection = dynData.Get<float?>(CUTDIRECTION);

                if (cutDirection.HasValue)
                {
                    dynData[CUTDIRECTION] = 360 - cutDirection.Value;
                }
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/LeftHanded/ObstacleData.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using CustomJSONData;
    using CustomJSONData.CustomBeatmap;
    using HarmonyLib;
    using Heck;
    using UnityEngine;
    using static NoodleExtensions.Plugin;

    [HarmonyPatch(typeof(ObstacleData))]
    [HarmonyPatch("Mirror")]
    internal static class ObstacleDataMirror
    {
        private static void Prefix(ObstacleData __instance) // prefix because we need to know the lineIndex before it gets mirrored
        {
            if (__instance is CustomObstacleData customData)
            {
                Dictionary<string, object?> dynData = customData.customData;
                IEnumerable<float?>? position = dynData.GetNullableFloats(POSITION);
                IEnumerable<float?>? scale = dynData.GetNullableFloats(SCALE);
                List<float>? localrot = dynData.Get<List<object>>(LOCALROTATION)?.Select(n => Convert.ToSingle(n)).ToList();
                object? rotation = dynData.Get<object>(ROTATION);

                float? startX = position?.ElementAtOrDefault(0);
                float? scaleX = scale?.ElementAtOrDefault(0);

                float width = scaleX.GetValueOrDefault(__instance.width);
                if (startX.HasValue)
                {
                    dynData[POSITION] = new List<object?>() { (startX.Value + width) * -1, position.ElementAtOrDefault(1) };
                }
                else if (scaleX.HasValue)
                {
                    float lineIndex = __instance.lineIndex - 2;
                    dynData[POSITION] = new List<object?>() { (lineIndex + width) * -1, position?.ElementAtOrDefault(1) ?? 0 };
                }

                if (localrot != null)
                {
                    List<float> rot = localrot.Select(n => Convert.ToSingle(n)).ToList();
                    Quaternion modifiedVector = Quaternion.Euler(rot[0], rot[1], rot[2]);
                    Vector3 vector = new Quaternion(modifiedVector.x, modifiedVector.y * -1, modifiedVector.z * -1, modifiedVector.w).eulerAngles;
                    dynData[LOCALROTATION] = new List<object> { vector.x, vector.y, vector.z };
                }

                if (rotation != null)
                {
                    if (rotation is List<object> list)
                    {
                        List<float> rot = list.Select(n => Convert.ToSingle(n)).ToList();
                        Quaternion modifiedVector = Quaternion.Euler(rot[0], rot[1], rot[2]);
                        Vector3 vector = new Quaternion(modifiedVector.x, modifiedVector.y * -1, modifiedVector.z * -1, modifiedVector.w).eulerAngles;
                        dynData[ROTATION] = new List<object> { vector.x, vector.y, vector.z };
                    }
                    else
                    {
                        dynData[ROTATION] = Convert.ToSingle(rotation) * -1;
                    }
                }
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/Mirror/MirroredNoteController.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using Heck;
    using Heck.Animation;
    using UnityEngine;
    using static NoodleExtensions.NoodleObjectDataManager;

    internal static class MirroredNoteControllerHelper
    {
        internal static void AddToTrack(NoteData noteData, GameObject gameObject)
        {
            NoodleNoteData? noodleData = TryGetObjectData<NoodleNoteData>(noteData);
            if (noodleData != null)
            {
                IEnumerable<Track>? tracks = noodleData.Track;
                if (tracks != null)
                {
                    foreach (Track track in tracks)
                    {
                        // add to gameobjects
                        track.AddGameObject(gameObject);
                    }
                }
            }
        }

        internal static bool CheckSkip(Transform noteTransform, Transform followedNoteTransform)
        {
            if (followedNoteTransform.position.y < 0)
            {
                if (noteTransform.gameObject.activeInHierarchy)
                {
                    noteTransform.gameObject.SetActive(false);
                }

                return false;
            }

            if (!noteTransform.gameObject.activeInHierarchy)
            {
                noteTransform.gameObject.SetActive(true);
            }

            return true;
        }

        internal static void UpdateMirror(Transform objectTransform, Transform noteTransform, Transform followedObjectTransform, Transform followedNoteTransform, NoteControllerBase noteController, NoteControllerBase followedNote)
        {
            if (objectTransform.localScale != followedObjectTransform.localScale)
            {
                objectTransform.localScale = followedObjectTransform.localScale;
            }

            if (noteTransform.localScale != followedNoteTransform.localScale)
            {
                noteTransform.localScale = followedNoteTransform.localScale;
            }

            if (CutoutManager.NoteCutoutEffects.TryGetValue(noteController, out CutoutEffectWrapper cutoutEffect))
            {
                if (CutoutManager.NoteCutoutEffects.TryGetValue(followedNote, out CutoutEffectWrapper followedCutoutEffect))
                {
                    cutoutEffect.SetCutout(followedCutoutEffect.Cutout);
                }
            }

            if (CutoutManager.NoteDisappearingArrowWrappers.TryGetValue(noteController, out DisappearingArrowWrapper disappearingArrow))
            {
                if (CutoutManager.NoteDisappearingArrowWrappers.TryGetValue(followedNote, out DisappearingArrowWrapper followedDisappearingArrow))
                {
                    disappearingArrow.SetCutout(followedDisappearingArrow.Cutout);
                }
            }
        }
    }

    // Fuck generics
    [HeckPatch(typeof(MirroredNoteController<INoteMirrorable>))]
    [HeckPatch("Mirror")]
    internal static class MirroredNoteControllerINoteMirrorableMirror
    {
        private static void Postfix(MirroredNoteController<INoteMirrorable> __instance)
        {
            MirroredNoteControllerHelper.AddToTrack(__instance.noteData, __instance.gameObject);
        }
    }

    [HeckPatch(typeof(MirroredNoteController<ICubeNoteMirrorable>))]
    [HeckPatch("Mirror")]
    internal static class MirroredNoteControllerICubeNoteMirrorableMirror
    {
        private static void Postfix(MirroredNoteController<ICubeNoteMirrorable> __instance)
        {
            MirroredNoteControllerHelper.AddToTrack(__instance.noteData, __instance.gameObject);
        }
    }

    [HeckPatch(typeof(MirroredNoteController<INoteMirrorable>))]
    [HeckPatch("UpdatePositionAndRotation")]
    internal static class MirroredNoteControllerINoteMirrorableUpdatePositionAndRotation
    {
        private static bool Prefix(Transform ____noteTransform, Transform ____followedNoteTransform)
        {
            return MirroredNoteControllerHelper.CheckSkip(____noteTransform, ____followedNoteTransform);
        }

        private static void Postfix(MirroredNoteController<INoteMirrorable> __instance, INoteMirrorable ___followedNote, Transform ____objectTransform, Transform ____noteTransform, Transform ____followedObjectTransform, Transform ____followedNoteTransform)
        {
            MirroredNoteControllerHelper.UpdateMirror(____objectTransform, ____noteTransform, ____followedObjectTransform, ____followedNoteTransform, __instance, (NoteControllerBase)___followedNote);
        }
    }

    [HeckPatch(typeof(MirroredNoteController<ICubeNoteMirrorable>))]
    [HeckPatch("UpdatePositionAndRotation")]
    internal static class MirroredNoteControllerICubeNoteMirrorableUpdatePositionAndRotation
    {
        private static bool Prefix(Transform ____noteTransform, Transform ____followedNoteTransform)
        {
            return MirroredNoteControllerHelper.CheckSkip(____noteTransform, ____followedNoteTransform);
        }

        private static void Postfix(MirroredNoteController<ICubeNoteMirrorable> __instance, ICubeNoteMirrorable ___followedNote, Transform ____objectTransform, Transform ____noteTransform, Transform ____followedObjectTransform, Transform ____followedNoteTransform)
        {
            MirroredNoteControllerHelper.UpdateMirror(____objectTransform, ____noteTransform, ____followedObjectTransform, ____followedNoteTransform, __instance, (NoteControllerBase)___followedNote);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/Mirror/MirroredObstacleController.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using Heck;
    using UnityEngine;

    [HeckPatch(typeof(MirroredObstacleController))]
    [HeckPatch("UpdatePositionAndRotation")]
    internal static class MirroredObstacleControllerUpdatePositionAndRotation
    {
        // Must be overwritten to compensate for rotation
        private static bool Prefix(MirroredObstacleController __instance, ObstacleController ____followedObstacle, Transform ____transform, Transform ____followedTransform)
        {
            // do not reflection walls above the mirror
            if (____followedTransform.position.y < 0)
            {
                // idk how to hide it without disabling the update
                ____transform.position = new Vector3(0, 100, 0);
                return false;
            }

            Vector3 position = ____followedTransform.position;
            Quaternion quaternion = ____followedTransform.rotation;
            position.y = -position.y;
            quaternion = quaternion.Reflect(Vector3.up);
            ____transform.SetPositionAndRotation(position, quaternion);

            if (____transform.localScale != ____followedTransform.localScale)
            {
                ____transform.localScale = ____followedTransform.localScale;
            }

            if (CutoutManager.ObstacleCutoutEffects.TryGetValue(__instance, out CutoutAnimateEffectWrapper cutoutAnimateEffect))
            {
                if (CutoutManager.ObstacleCutoutEffects.TryGetValue(____followedObstacle, out CutoutAnimateEffectWrapper followedCutoutAnimateEffect))
                {
                    cutoutAnimateEffect.SetCutout(followedCutoutAnimateEffect.Cutout);
                }
            }

            return false;
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/MultiplayerConnectedPlayerInstaller.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;
    using CustomJSONData;
    using CustomJSONData.CustomBeatmap;
    using HarmonyLib;
    using Heck;
    using IPA.Utilities;
    using static NoodleExtensions.Plugin;

    [HeckPatch(typeof(MultiplayerConnectedPlayerInstaller))]
    [HeckPatch("InstallBindings")]
    internal static class MultiplayerConnectedPlayerInstallerInstallBindings
    {
        private static readonly MethodInfo _createTransformedBeatmapData = AccessTools.Method(typeof(BeatmapDataTransformHelper), nameof(BeatmapDataTransformHelper.CreateTransformedBeatmapData));

        private static readonly MethodInfo _excludeFakeNote = AccessTools.Method(typeof(MultiplayerConnectedPlayerInstallerInstallBindings), nameof(ExcludeFakeNoteAndAllWalls));

        private static readonly FieldAccessor<BeatmapLineData, List<BeatmapObjectData>>.Accessor _beatmapObjectsDataAccessor = FieldAccessor<BeatmapLineData, List<BeatmapObjectData>>.GetAccessor("_beatmapObjectsData");

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)
                .MatchForward(false, new CodeMatch(OpCodes.Call, _createTransformedBeatmapData))
                .Advance(1)
                .Insert(new CodeInstruction(OpCodes.Call, _excludeFakeNote))
                .InstructionEnumeration();
        }

        private static IReadonlyBeatmapData ExcludeFakeNoteAndAllWalls(IReadonlyBeatmapData result)
        {
            foreach (BeatmapLineData b in result.beatmapLinesData)
            {
                BeatmapLineData refBeatmapLineData = b;
                _beatmapObjectsDataAccessor(ref refBeatmapLineData) = b.beatmapObjectsData.Where(n =>
                {
                    Dictionary<string, object?> dynData;

                    switch (n)
                    {
                        case CustomNoteData customNoteData:
                            dynData = customNoteData.customData;
                            break;

                        case CustomObstacleData customObstacleData:
                            return false;

                        default:
                            return true;
                    }

                    bool? fake = dynData.Get<bool?>(FAKENOTE);
                    if (fake.HasValue && fake.Value)
                    {
                        return false;
                    }

                    return true;
                }).ToList();
            }

            if (result is CustomBeatmapData customBeatmapData)
            {
                string[] excludedTypes = new string[]
                {
                    ASSIGNPLAYERTOTRACK,
                    ASSIGNTRACKPARENT,
                };

                customBeatmapData.customEventsData.RemoveAll(n => excludedTypes.Contains(n.type));
            }

            return result;
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/NoteController.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Reflection;
    using System.Reflection.Emit;
    using HarmonyLib;
    using Heck;
    using Heck.Animation;
    using IPA.Utilities;
    using UnityEngine;
    using static NoodleExtensions.NoodleObjectDataManager;

    [HeckPatch(typeof(NoteController))]
    [HeckPatch("Init")]
    internal static class NoteControllerInit
    {
        internal static readonly FieldAccessor<NoteMovement, NoteJump>.Accessor _noteJumpAccessor = FieldAccessor<NoteMovement, NoteJump>.GetAccessor("_jump");
        internal static readonly FieldAccessor<NoteMovement, NoteFloorMovement>.Accessor _noteFloorMovementAccessor = FieldAccessor<NoteMovement, NoteFloorMovement>.GetAccessor("_floorMovement");

        internal static readonly FieldAccessor<NoteFloorMovement, Quaternion>.Accessor _worldRotationFloorAccessor = FieldAccessor<NoteFloorMovement, Quaternion>.GetAccessor("_worldRotation");
        internal static readonly FieldAccessor<NoteFloorMovement, Quaternion>.Accessor _inverseWorldRotationFloorAccessor = FieldAccessor<NoteFloorMovement, Quaternion>.GetAccessor("_inverseWorldRotation");

        internal static readonly FieldAccessor<NoteJump, Quaternion>.Accessor _worldRotationJumpAccessor = FieldAccessor<NoteJump, Quaternion>.GetAccessor("_worldRotation");
        internal static readonly FieldAccessor<NoteJump, Quaternion>.Accessor _inverseWorldRotationJumpAccessor = FieldAccessor<NoteJump, Quaternion>.GetAccessor("_inverseWorldRotation");
        private static readonly FieldAccessor<NoteJump, Quaternion>.Accessor _endRotationAccessor = FieldAccessor<NoteJump, Quaternion>.GetAccessor("_endRotation");
        private static readonly FieldAccessor<NoteJump, Quaternion>.Accessor _middleRotationAccessor = FieldAccessor<NoteJump, Quaternion>.GetAccessor("_middleRotation");
        private static readonly FieldAccessor<NoteJump, Vector3[]>.Accessor _randomRotationsAccessor = FieldAccessor<NoteJump, Vector3[]>.GetAccessor("_randomRotations");
        private static readonly FieldAccessor<NoteJump, int>.Accessor _randomRotationIdxAccessor = FieldAccessor<NoteJump, int>.GetAccessor("_randomRotationIdx");

        private static readonly FieldInfo _noteDataField = AccessTools.Field(typeof(NoteController), "_noteData");
        private static readonly MethodInfo _flipYSideGetter = AccessTools.PropertyGetter(typeof(NoteData), nameof(NoteData.flipYSide));

        private static readonly MethodInfo _getFlipYSide = AccessTools.Method(typeof(NoteControllerInit), nameof(GetFlipYSide));

        private static void Postfix(NoteController __instance, NoteData noteData, NoteMovement ____noteMovement, Vector3 moveStartPos, Vector3 moveEndPos, Vector3 jumpEndPos, float endRotation)
        {
            NoodleNoteData? noodleData = TryGetObjectData<NoodleNoteData>(noteData);
            if (noodleData == null)
            {
                return;
            }

            Quaternion? cutQuaternion = noodleData.CutQuaternion;

            NoteJump noteJump = _noteJumpAccessor(ref ____noteMovement);
            NoteFloorMovement floorMovement = _noteFloorMovementAccessor(ref ____noteMovement);

            if (cutQuaternion.HasValue)
            {
                Quaternion quatVal = cutQuaternion.Value;
                _endRotationAccessor(ref noteJump) = quatVal;
                Vector3 vector = quatVal.eulerAngles;
                vector += _randomRotationsAccessor(ref noteJump)[_randomRotationIdxAccessor(ref noteJump)] * 20;
                Quaternion midrotation = Quaternion.Euler(vector);
                _middleRotationAccessor(ref noteJump) = midrotation;
            }

            Quaternion? worldRotationQuaternion = noodleData.WorldRotationQuaternion;
            Quaternion? localRotationQuaternion = noodleData.LocalRotationQuaternion;

            Transform transform = __instance.transform;

            Quaternion localRotation = Quaternion.identity;
            if (worldRotationQuaternion.HasValue || localRotationQuaternion.HasValue)
            {
                if (localRotationQuaternion.HasValue)
                {
                    localRotation = localRotationQuaternion.Value;
                }

                if (worldRotationQuaternion.HasValue)
                {
                    Quaternion quatVal = worldRotationQuaternion.Value;
                    Quaternion inverseWorldRotation = Quaternion.Inverse(quatVal);
                    _worldRotationJumpAccessor(ref noteJump) = quatVal;
                    _inverseWorldRotationJumpAccessor(ref noteJump) = inverseWorldRotation;
                    _worldRotationFloorAccessor(ref floorMovement) = quatVal;
                    _inverseWorldRotationFloorAccessor(ref floorMovement) = inverseWorldRotation;

                    quatVal *= localRotation;

                    transform.localRotation = quatVal;
                }
                else
                {
                    transform.localRotation *= localRotation;
                }
            }

            if (transform.localScale != Vector3.one)
            {
                transform.localScale = Vector3.one; // This is a fix for animation due to notes being recycled
            }

            IEnumerable<Track>? tracks = noodleData.Track;
            if (tracks != null)
            {
                foreach (Track track in tracks)
                {
                    // add to gameobjects
                    track.AddGameObject(__instance.gameObject);
                }
            }

            noodleData.EndRotation = endRotation;
            noodleData.MoveStartPos = moveStartPos;
            noodleData.MoveEndPos = moveEndPos;
            noodleData.JumpEndPos = jumpEndPos;
            noodleData.WorldRotation = __instance.worldRotation;
            noodleData.LocalRotation = localRotation;
        }

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)
                .MatchForward(false, new CodeMatch(OpCodes.Callvirt, _flipYSideGetter))
                .InsertAndAdvance(
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, _noteDataField))
                .Advance(1)
                .Insert(new CodeInstruction(OpCodes.Call, _getFlipYSide))
                .InstructionEnumeration();
        }

        private static float GetFlipYSide(NoteData noteData, float @default)
        {
            float output = @default;

            NoodleNoteData? noodleData = TryGetObjectData<NoodleNoteData>(noteData);
            if (noodleData != null)
            {
                float? flipYSide = noodleData.FlipYSideInternal;
                if (flipYSide.HasValue)
                {
                    output = flipYSide.Value;
                }
            }

            return output;
        }
    }

    [HeckPatch(typeof(NoteController))]
    [HeckPatch("ManualUpdate")]
    internal static class NoteControllerUpdate
    {
        internal static readonly FieldAccessor<NoteFloorMovement, Vector3>.Accessor _floorEndPosAccessor = FieldAccessor<NoteFloorMovement, Vector3>.GetAccessor("_endPos");
        private static readonly FieldAccessor<NoteFloorMovement, Vector3>.Accessor _floorStartPosAccessor = FieldAccessor<NoteFloorMovement, Vector3>.GetAccessor("_startPos");
        private static readonly FieldAccessor<NoteJump, Vector3>.Accessor _jumpStartPosAccessor = FieldAccessor<NoteJump, Vector3>.GetAccessor("_startPos");
        private static readonly FieldAccessor<NoteJump, Vector3>.Accessor _jumpEndPosAccessor = FieldAccessor<NoteJump, Vector3>.GetAccessor("_endPos");

        private static readonly FieldAccessor<NoteJump, IAudioTimeSource>.Accessor _audioTimeSyncControllerAccessor = FieldAccessor<NoteJump, IAudioTimeSource>.GetAccessor("_audioTimeSyncController");
        private static readonly FieldAccessor<NoteJump, float>.Accessor _jumpDurationAccessor = FieldAccessor<NoteJump, float>.GetAccessor("_jumpDuration");

        private static readonly FieldAccessor<GameNoteController, BoxCuttableBySaber[]>.Accessor _gameNoteBigCuttableAccessor = FieldAccessor<GameNoteController, BoxCuttableBySaber[]>.GetAccessor("_bigCuttableBySaberList");
        private static readonly FieldAccessor<GameNoteController, BoxCuttableBySaber[]>.Accessor _gameNoteSmallCuttableAccessor = FieldAccessor<GameNoteController, BoxCuttableBySaber[]>.GetAccessor("_smallCuttableBySaberList");
        private static readonly FieldAccessor<BombNoteController, CuttableBySaber>.Accessor _bombNoteCuttableAccessor = FieldAccessor<BombNoteController, CuttableBySaber>.GetAccessor("_cuttableBySaber");

        internal static NoodleObjectData? NoodleData { get; private set; }

        private static void Prefix(NoteController __instance, NoteData ____noteData, NoteMovement ____noteMovement)
        {
            NoodleData = TryGetObjectData<NoodleObjectData>(____noteData);
            if (NoodleData == null)
            {
                return;
            }

            NoodleNoteData noodleData = (NoodleNoteData)NoodleData;

            IEnumerable<Track>? tracks = noodleData.Track;
            NoodleObjectData.AnimationObjectData? animationObject = noodleData.AnimationObject;
            if (tracks != null || animationObject != null)
            {
                NoteJump noteJump = NoteControllerInit._noteJumpAccessor(ref ____noteMovement);
                NoteFloorMovement floorMovement = NoteControllerInit._noteFloorMovementAccessor(ref ____noteMovement);

                // idk i just copied base game time
                float jumpDuration = _jumpDurationAccessor(ref noteJump);
                float elapsedTime = _audioTimeSyncControllerAccessor(ref noteJump).songTime - (____noteData.time - (jumpDuration * 0.5f));
                elapsedTime = NoteJumpManualUpdate.NoteJumpTimeAdjust(elapsedTime, jumpDuration);
                float normalTime = elapsedTime / jumpDuration;

                Animation.AnimationHelper.GetObjectOffset(animationObject, tracks, normalTime, out Vector3? positionOffset, out Quaternion? rotationOffset, out Vector3? scaleOffset, out Quaternion? localRotationOffset, out float? dissolve, out float? dissolveArrow, out float? cuttable);

                if (positionOffset.HasValue)
                {
                    Vector3 moveStartPos = noodleData.MoveStartPos;
                    Vector3 moveEndPos = noodleData.MoveEndPos;
                    Vector3 jumpEndPos = noodleData.JumpEndPos;

                    Vector3 offset = positionOffset.Value;
                    _floorStartPosAccessor(ref floorMovement) = moveStartPos + offset;
                    _floorEndPosAccessor(ref floorMovement) = moveEndPos + offset;
                    _jumpStartPosAccessor(ref noteJump) = moveEndPos + offset;
                    _jumpEndPosAccessor(ref noteJump) = jumpEndPos + offset;
                }

                Transform transform = __instance.transform;

                if (rotationOffset.HasValue || localRotationOffset.HasValue)
                {
                    Quaternion worldRotation = noodleData.WorldRotation;
                    Quaternion localRotation = noodleData.LocalRotation;

                    Quaternion worldRotationQuatnerion = worldRotation;
                    if (rotationOffset.HasValue)
                    {
                        worldRotationQuatnerion *= rotationOffset.Value;
                        Quaternion inverseWorldRotation = Quaternion.Inverse(worldRotationQuatnerion);
                        NoteControllerInit._worldRotationJumpAccessor(ref noteJump) = worldRotationQuatnerion;
                        NoteControllerInit._inverseWorldRotationJumpAccessor(ref noteJump) = inverseWorldRotation;
                        NoteControllerInit._worldRotationFloorAccessor(ref floorMovement) = worldRotationQuatnerion;
                        NoteControllerInit._inverseWorldRotationFloorAccessor(ref floorMovement) = inverseWorldRotation;
                    }

                    worldRotationQuatnerion *= localRotation;

                    if (localRotationOffset.HasValue)
                    {
                        worldRotationQuatnerion *= localRotationOffset.Value;
                    }

                    transform.localRotation = worldRotationQuatnerion;
                }

                if (scaleOffset.HasValue)
                {
                    transform.localScale = scaleOffset.Value;
                }

                if (dissolve.HasValue)
                {
                    if (CutoutManager.NoteCutoutEffects.TryGetValue(__instance, out CutoutEffectWrapper cutoutEffect))
                    {
                        cutoutEffect.SetCutout(dissolve.Value);
                    }
                }

                if (dissolveArrow.HasValue && __instance.noteData.colorType != ColorType.None)
                {
                    if (CutoutManager.NoteDisappearingArrowWrappers.TryGetValue(__instance, out DisappearingArrowWrapper disappearingArrowWrapper))
                    {
                        disappearingArrowWrapper.SetCutout(dissolveArrow.Value);
                    }
                }

                if (cuttable.HasValue)
                {
                    bool enabled = cuttable.Value >= 1;

                    switch (__instance)
                    {
                        case GameNoteController gameNoteController:
                            BoxCuttableBySaber[] bigCuttableBySaberList = _gameNoteBigCuttableAccessor(ref gameNoteController);
                            foreach (BoxCuttableBySaber bigCuttableBySaber in bigCuttableBySaberList)
                            {
                                if (bigCuttableBySaber.canBeCut != enabled)
                                {
                                    bigCuttableBySaber.canBeCut = enabled;
                                }
                            }

                            BoxCuttableBySaber[] smallCuttableBySaberList = _gameNoteSmallCuttableAccessor(ref gameNoteController);
                            foreach (BoxCuttableBySaber smallCuttableBySaber in smallCuttableBySaberList)
                            {
                                if (smallCuttableBySaber.canBeCut != enabled)
                                {
                                    smallCuttableBySaber.canBeCut = enabled;
                                }
                            }

                            break;

                        case BombNoteController bombNoteController:
                            CuttableBySaber boxCuttableBySaber = _bombNoteCuttableAccessor(ref bombNoteController);
                            if (boxCuttableBySaber.canBeCut != enabled)
                            {
                                boxCuttableBySaber.canBeCut = enabled;
                            }

                            break;
                    }
                }
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/NoteFloorMovement.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Reflection;
    using System.Reflection.Emit;
    using HarmonyLib;
    using Heck;
    using NoodleExtensions.Animation;
    using UnityEngine;

    [HeckPatch(typeof(NoteFloorMovement))]
    [HeckPatch("ManualUpdate")]
    internal static class NoteFloorMovementManualUpdate
    {
        private static readonly FieldInfo _localPosition = AccessTools.Field(typeof(NoteFloorMovement), "_localPosition");

        private static readonly MethodInfo _definiteNoteFloorMovement = AccessTools.Method(typeof(NoteFloorMovementManualUpdate), nameof(DefiniteNoteFloorMovement));

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)
                .MatchForward(false, new CodeMatch(OpCodes.Stfld, _localPosition))
                .Insert(
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Call, _definiteNoteFloorMovement))
                .InstructionEnumeration();
        }

        private static Vector3 DefiniteNoteFloorMovement(Vector3 original, NoteFloorMovement noteFloorMovement)
        {
            NoodleObjectData? noodleData = NoteControllerUpdate.NoodleData;
            if (noodleData != null)
            {
                AnimationHelper.GetDefinitePositionOffset(noodleData.AnimationObject, noodleData.Track, 0, out Vector3? position);
                if (position.HasValue)
                {
                    Vector3 noteOffset = noodleData.NoteOffset;
                    Vector3 endPos = NoteControllerUpdate._floorEndPosAccessor(ref noteFloorMovement);
                    return original + (position.Value + noteOffset - endPos);
                }
            }

            return original;
        }
    }

    [HeckPatch(typeof(NoteFloorMovement))]
    [HeckPatch("SetToStart")]
    internal static class NoteFloorMovementSetToStart
    {
        private static void Postfix(Transform ____rotatedObject)
        {
            NoodleNoteData? noodleData = (NoodleNoteData?)NoteControllerUpdate.NoodleData;
            if (noodleData != null && noodleData.DisableLook)
            {
                ____rotatedObject.localRotation = Quaternion.Euler(0, 0, noodleData.EndRotation);
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/NoteJump.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;
    using HarmonyLib;
    using Heck;
    using IPA.Utilities;
    using NoodleExtensions.Animation;
    using UnityEngine;

    [HeckPatch(typeof(NoteJump))]
    [HeckPatch("ManualUpdate")]
    internal static class NoteJumpManualUpdate
    {
        private static readonly FieldInfo _threeQuartersMarkReportedField = AccessTools.Field(typeof(NoteJump), "_threeQuartersMarkReported");
        private static readonly MethodInfo _localRotationSetter = AccessTools.PropertySetter(typeof(Transform), nameof(Transform.localRotation));

        private static readonly FieldInfo _jumpDurationField = AccessTools.Field(typeof(NoteJump), "_jumpDuration");
        private static readonly MethodInfo _noteJumpTimeAdjust = AccessTools.Method(typeof(NoteJumpManualUpdate), nameof(NoteJumpTimeAdjust));
        private static readonly FieldInfo _localPositionField = AccessTools.Field(typeof(NoteJump), "_localPosition");
        private static readonly MethodInfo _definiteNoteJump = AccessTools.Method(typeof(NoteJumpManualUpdate), nameof(DefiniteNoteJump));
        private static readonly FieldInfo _definitePositionField = AccessTools.Field(typeof(NoteJumpManualUpdate), nameof(_definitePosition));
        private static readonly MethodInfo _getTransform = AccessTools.PropertyGetter(typeof(Component), nameof(Component.transform));
        private static readonly MethodInfo _doNoteLook = AccessTools.Method(typeof(NoteJumpManualUpdate), nameof(DoNoteLook));
        private static readonly FieldInfo _startRotationField = AccessTools.Field(typeof(NoteJump), "_startRotation");
        private static readonly FieldInfo _middleRotationField = AccessTools.Field(typeof(NoteJump), "_middleRotation");
        private static readonly FieldInfo _endRotationField = AccessTools.Field(typeof(NoteJump), "_endRotation");
        private static readonly FieldInfo _playerTransformsField = AccessTools.Field(typeof(NoteJump), "_playerTransforms");
        private static readonly FieldInfo _rotatedObjectField = AccessTools.Field(typeof(NoteJump), "_rotatedObject");
        private static readonly FieldInfo _inverseWorldRotationField = AccessTools.Field(typeof(NoteJump), "_inverseWorldRotation");
        private static readonly FieldAccessor<PlayerTransforms, Transform>.Accessor _headTransformAccessor = FieldAccessor<PlayerTransforms, Transform>.GetAccessor("_headTransform");

        // This field is used by reflection
#pragma warning disable CS0414
        private static bool _definitePosition = false;
#pragma warning restore CS0414

        internal static float NoteJumpTimeAdjust(float original, float jumpDuration)
        {
            NoodleObjectData? noodleData = NoteControllerUpdate.NoodleData;
            if (noodleData != null)
            {
                float? time = noodleData.Track?.Select(n => Heck.Animation.AnimationHelper.TryGetProperty<float?>(n, Plugin.TIME)).FirstOrDefault(n => n.HasValue);
                if (time.HasValue)
                {
                    return time.Value * jumpDuration;
                }
            }

            return original;
        }

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            CodeMatcher codeMatcher = new CodeMatcher(instructions);
            object label;

            // CodeMatcher needs some better label manipulating methods
            // replace rotation stuff
            codeMatcher
                .MatchForward(
                    false,
                    new CodeMatch(OpCodes.Callvirt, _localRotationSetter))
                .Advance(1);
            int endPos = codeMatcher.Pos;
            label = codeMatcher.Labels.First();
            codeMatcher
                .MatchBack(
                    false,
                    new CodeMatch(null, label))
                .Advance(1)
                .RemoveInstructions(endPos - codeMatcher.Pos)
                .InsertAndAdvance(
                    new CodeInstruction(OpCodes.Ldloc_1),
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, _startRotationField),
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, _middleRotationField),
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, _endRotationField),
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, _playerTransformsField),
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, _rotatedObjectField),
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Call, _getTransform),
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, _inverseWorldRotationField),
                    new CodeInstruction(OpCodes.Call, _doNoteLook))

            // Add addition check to our quirky little variable to skip end position offset when we are using definitePosition
                .MatchForward(
                    true,
                    new CodeMatch(OpCodes.Ldfld, _threeQuartersMarkReportedField),
                    new CodeMatch(OpCodes.Brfalse));
            label = codeMatcher.Operand;
            codeMatcher
                .Advance(1)
                .Insert(
                    new CodeInstruction(OpCodes.Ldsfld, _definitePositionField),
                    new CodeInstruction(OpCodes.Brtrue_S, label))
                .Start();

            return codeMatcher

                // time adjust
                .MatchForward(false, new CodeMatch(OpCodes.Stloc_0))
                .InsertAndAdvance(
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, _jumpDurationField),
                    new CodeInstruction(OpCodes.Call, _noteJumpTimeAdjust))

                // final position
                .MatchForward(false, new CodeMatch(OpCodes.Stind_R4))
                .Advance(2)
                .InsertAndAdvance(
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, _localPositionField),
                    new CodeInstruction(OpCodes.Ldloc_1),
                    new CodeInstruction(OpCodes.Call, _definiteNoteJump),
                    new CodeInstruction(OpCodes.Stfld, _localPositionField))

                .InstructionEnumeration();
        }

        private static Vector3 DefiniteNoteJump(Vector3 original, float time)
        {
            NoodleObjectData? noodleData = NoteControllerUpdate.NoodleData;
            if (noodleData != null)
            {
                AnimationHelper.GetDefinitePositionOffset(noodleData.AnimationObject, noodleData.Track, time, out Vector3? position);
                if (position.HasValue)
                {
                    Vector3 noteOffset = noodleData.NoteOffset;
                    _definitePosition = true;
                    return position.Value + noteOffset;
                }
            }

            _definitePosition = false;
            return original;
        }

        // Performs all note look rotation from world space
        // Never want to touch this again....
        private static void DoNoteLook(
            float num2,
            Quaternion startRotation,
            Quaternion middleRotation,
            Quaternion endRotation,
            PlayerTransforms playerTransforms,
            Transform rotatedObject,
            Transform baseTransform,
            Quaternion inverseWorldRotation)
        {
            NoodleNoteData? noodleData = (NoodleNoteData?)NoteControllerUpdate.NoodleData;
            if (noodleData != null && noodleData.DisableLook)
            {
                rotatedObject.localRotation = endRotation;
                return;
            }

            Quaternion a;
            if (num2 < 0.125f)
            {
                a = Quaternion.Slerp(baseTransform.rotation * startRotation, baseTransform.rotation * middleRotation, Mathf.Sin(num2 * Mathf.PI * 4f));
            }
            else
            {
                a = Quaternion.Slerp(baseTransform.rotation * middleRotation, baseTransform.rotation * endRotation, Mathf.Sin((num2 - 0.125f) * Mathf.PI * 2f));
            }

            Vector3 vector = playerTransforms.headWorldPos;

            // idk whats happening anymore
            Quaternion worldRot = inverseWorldRotation;
            if (baseTransform.parent != null)
            {
                // Handle parenting
                worldRot *= Quaternion.Inverse(baseTransform.parent.rotation);
            }

            // This line but super complicated so that "y" = "originTransform.up"
            // vector.y = Mathf.Lerp(vector.y, this._localPosition.y, 0.8f);
            Transform headTransform = _headTransformAccessor(ref playerTransforms);
            Quaternion inverse = Quaternion.Inverse(worldRot);
            Vector3 upVector = inverse * Vector3.up;
            float baseUpMagnitude = Vector3.Dot(worldRot * baseTransform.position, Vector3.up);
            float headUpMagnitude = Vector3.Dot(worldRot * headTransform.position, Vector3.up);
            float mult = Mathf.Lerp(headUpMagnitude, baseUpMagnitude, 0.8f) - headUpMagnitude;
            vector += upVector * mult;

            // more wtf
            Vector3 normalized = baseTransform.rotation * (worldRot * (baseTransform.position - vector).normalized);

            Quaternion b = Quaternion.LookRotation(normalized, rotatedObject.up);
            rotatedObject.rotation = Quaternion.Lerp(a, b, num2 * 2f);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/ObstacleController.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;
    using HarmonyLib;
    using Heck;
    using Heck.Animation;
    using UnityEngine;
    using static NoodleExtensions.HarmonyPatches.SpawnDataHelper.BeatmapObjectSpawnMovementDataVariables;
    using static NoodleExtensions.NoodleObjectDataManager;
    using static NoodleExtensions.Plugin;

    [HeckPatch(typeof(ObstacleController))]
    [HeckPatch("Init")]
    internal static class ObstacleControllerInit
    {
        internal static readonly List<ObstacleController> _activeObstacles = new List<ObstacleController>();

        private static readonly FieldInfo _worldRotationField = AccessTools.Field(typeof(ObstacleController), "_worldRotation");
        private static readonly FieldInfo _inverseWorldRotationField = AccessTools.Field(typeof(ObstacleController), "_inverseWorldRotation");
        private static readonly MethodInfo _widthGetter = AccessTools.PropertyGetter(typeof(ObstacleData), nameof(ObstacleData.width));
        private static readonly FieldInfo _lengthField = AccessTools.Field(typeof(ObstacleController), "_length");

        private static readonly MethodInfo _getCustomWidth = AccessTools.Method(typeof(ObstacleControllerInit), nameof(GetCustomWidth));
        private static readonly MethodInfo _getWorldRotation = AccessTools.Method(typeof(ObstacleControllerInit), nameof(GetWorldRotation));
        private static readonly MethodInfo _getCustomLength = AccessTools.Method(typeof(ObstacleControllerInit), nameof(GetCustomLength));
        private static readonly MethodInfo _invertQuaternion = AccessTools.Method(typeof(ObstacleControllerInit), nameof(InvertQuaternion));

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)

                // world rotation
                .MatchForward(false, new CodeMatch(OpCodes.Stfld, _worldRotationField))
                .Insert(
                    new CodeInstruction(OpCodes.Ldarg_1),
                    new CodeInstruction(OpCodes.Ldarg_2),
                    new CodeInstruction(OpCodes.Call, _getWorldRotation))
                .RemoveInstructionsWithOffsets(-4, -1)

                // inverse world rotation
                .MatchForward(false, new CodeMatch(OpCodes.Stfld, _inverseWorldRotationField))
                .Insert(
                    new CodeInstruction(OpCodes.Ldarg_0),
                    new CodeInstruction(OpCodes.Ldfld, _worldRotationField),
                    new CodeInstruction(OpCodes.Call, _invertQuaternion))
                .RemoveInstructionsWithOffsets(-5, -1)

                // width
                .MatchForward(false, new CodeMatch(OpCodes.Callvirt, _widthGetter))
                .Advance(2)
                .InsertAndAdvance(
                    new CodeInstruction(OpCodes.Ldarg_1),
                    new CodeInstruction(OpCodes.Call, _getCustomWidth))

                // length
                .MatchForward(false, new CodeMatch(OpCodes.Stfld, _lengthField))
                .Insert(
                    new CodeInstruction(OpCodes.Ldarg_1),
                    new CodeInstruction(OpCodes.Call, _getCustomLength))

                .InstructionEnumeration();
        }

        private static void Postfix(ObstacleController __instance, Quaternion ____worldRotation, ObstacleData obstacleData, Vector3 ____startPos, Vector3 ____midPos, Vector3 ____endPos, ref Bounds ____bounds)
        {
            NoodleObstacleData? noodleData = TryGetObjectData<NoodleObstacleData>(obstacleData);
            if (noodleData == null)
            {
                return;
            }

            Quaternion? localRotationQuaternion = noodleData.LocalRotationQuaternion;

            Transform transform = __instance.transform;

            Quaternion localRotation = Quaternion.identity;
            if (localRotationQuaternion.HasValue)
            {
                localRotation = localRotationQuaternion.Value;
                transform.localRotation = ____worldRotation * localRotation;
            }

            if (transform.localScale != Vector3.one)
            {
                transform.localScale = Vector3.one; // This is a fix for animation due to obstacles being recycled
            }

            IEnumerable<Track>? tracks = noodleData.Track;
            if (tracks != null)
            {
                foreach (Track track in tracks)
                {
                    // add to gameobjects
                    track.AddGameObject(__instance.gameObject);
                }
            }

            bool? cuttable = noodleData.Cuttable;
            if (cuttable.HasValue && !cuttable.Value)
            {
                ____bounds.size = Vector3.zero;
            }
            else
            {
                _activeObstacles.Add(__instance);
            }

            noodleData.StartPos = ____startPos;
            noodleData.MidPos = ____midPos;
            noodleData.EndPos = ____endPos;
            noodleData.LocalRotation = localRotation;
            noodleData.BoundsSize = ____bounds.size;
        }

        private static Quaternion InvertQuaternion(Quaternion quaternion)
        {
            return Quaternion.Inverse(quaternion);
        }

        private static Quaternion GetWorldRotation(ObstacleData obstacleData, float @default)
        {
            Quaternion worldRotation = Quaternion.Euler(0, @default, 0);

            NoodleObstacleData? noodleData = TryGetObjectData<NoodleObstacleData>(obstacleData);
            if (noodleData != null)
            {
                Quaternion? worldRotationQuaternion = noodleData.WorldRotationQuaternion;
                if (worldRotationQuaternion.HasValue)
                {
                    worldRotation = worldRotationQuaternion.Value;
                }

                noodleData.WorldRotation = worldRotation;
            }

            return worldRotation;
        }

        private static float GetCustomWidth(float @default, ObstacleData obstacleData)
        {
            NoodleObstacleData? noodleData = TryGetObjectData<NoodleObstacleData>(obstacleData);
            if (noodleData != null)
            {
                float? width = noodleData.Width;
                if (width.HasValue)
                {
                    return width.Value;
                }
            }

            return @default;
        }

        private static float GetCustomLength(float @default, ObstacleData obstacleData)
        {
            NoodleObstacleData? noodleData = TryGetObjectData<NoodleObstacleData>(obstacleData);
            if (noodleData != null)
            {
                float? length = noodleData.Length;
                if (length.HasValue)
                {
                    return length.Value * NoteLinesDistance;
                }
            }

            return @default;
        }
    }

    [HeckPatch(typeof(ObstacleController))]
    [HeckPatch("ManualUpdate")]
    internal static class ObstacleControllerManualUpdate
    {
        private static readonly FieldInfo _obstacleDataField = AccessTools.Field(typeof(ObstacleController), "_obstacleData");
        private static readonly FieldInfo _move1DurationField = AccessTools.Field(typeof(ObstacleController), "_move1Duration");
        private static readonly FieldInfo _finishMovementTime = AccessTools.Field(typeof(ObstacleController), "_finishMovementTime");
        private static readonly MethodInfo _obstacleTimeAdjust = AccessTools.Method(typeof(ObstacleControllerManualUpdate), nameof(ObstacleTimeAdjust));

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            List<CodeInstruction> instructionList = instructions.ToList();
            bool foundTime = false;
            for (int i = 0; i < instructionList.Count; i++)
            {
                if (!foundTime &&
                    instructionList[i].opcode == OpCodes.Stloc_0)
                {
                    foundTime = true;
                    instructionList.Insert(i, new CodeInstruction(OpCodes.Ldarg_0));
                    instructionList.Insert(i + 1, new CodeInstruction(OpCodes.Ldfld, _obstacleDataField));
                    instructionList.Insert(i + 2, new CodeInstruction(OpCodes.Ldarg_0));
                    instructionList.Insert(i + 3, new CodeInstruction(OpCodes.Ldfld, _move1DurationField));
                    instructionList.Insert(i + 4, new CodeInstruction(OpCodes.Ldarg_0));
                    instructionList.Insert(i + 5, new CodeInstruction(OpCodes.Ldfld, _finishMovementTime));
                    instructionList.Insert(i + 6, new CodeInstruction(OpCodes.Call, _obstacleTimeAdjust));
                }
            }

            if (!foundTime)
            {
                Plugin.Logger.Log("Failed to find stloc.0!", IPA.Logging.Logger.Level.Error);
            }

            return instructionList.AsEnumerable();
        }

        private static float ObstacleTimeAdjust(float original, ObstacleData obstacleData, float move1Duration, float finishMovementTime)
        {
            if (original > move1Duration)
            {
                NoodleObstacleData? noodleData = TryGetObjectData<NoodleObstacleData>(obstacleData);
                if (noodleData != null)
                {
                    float? time = noodleData.Track?.Select(n => AnimationHelper.TryGetProperty<float?>(n, TIME)).FirstOrDefault(n => n.HasValue);
                    if (time.HasValue)
                    {
                        return (time.Value * (finishMovementTime - move1Duration)) + move1Duration;
                    }
                }
            }

            return original;
        }

        private static void Prefix(
            ObstacleController __instance,
            ObstacleData ____obstacleData,
            AudioTimeSyncController ____audioTimeSyncController,
            float ____startTimeOffset,
            ref Vector3 ____startPos,
            ref Vector3 ____midPos,
            ref Vector3 ____endPos,
            float ____move1Duration,
            float ____move2Duration,
            float ____obstacleDuration,
            ref Quaternion ____worldRotation,
            ref Quaternion ____inverseWorldRotation,
            ref Bounds ____bounds)
        {
            NoodleObstacleData? noodleData = TryGetObjectData<NoodleObstacleData>(____obstacleData);
            if (noodleData == null)
            {
                return;
            }

            IEnumerable<Track>? tracks = noodleData.Track;
            NoodleObjectData.AnimationObjectData? animationObject = noodleData.AnimationObject;
            if (tracks != null || animationObject != null)
            {
                // idk i just copied base game time
                float elapsedTime = ____audioTimeSyncController.songTime - ____startTimeOffset;
                float normalTime = (elapsedTime - ____move1Duration) / (____move2Duration + ____obstacleDuration);

                Animation.AnimationHelper.GetObjectOffset(animationObject, tracks, normalTime, out Vector3? positionOffset, out Quaternion? rotationOffset, out Vector3? scaleOffset, out Quaternion? localRotationOffset, out float? dissolve, out float? _, out float? cuttable);

                if (positionOffset.HasValue)
                {
                    Vector3 startPos = noodleData.StartPos;
                    Vector3 midPos = noodleData.MidPos;
                    Vector3 endPos = noodleData.EndPos;

                    Vector3 offset = positionOffset.Value;
                    ____startPos = startPos + offset;
                    ____midPos = midPos + offset;
                    ____endPos = endPos + offset;
                }

                Transform transform = __instance.transform;

                if (rotationOffset.HasValue || localRotationOffset.HasValue)
                {
                    Quaternion worldRotation = noodleData.WorldRotation;
                    Quaternion localRotation = noodleData.LocalRotation;

                    Quaternion worldRotationQuatnerion = worldRotation;
                    if (rotationOffset.HasValue)
                    {
                        worldRotationQuatnerion *= rotationOffset.Value;
                        Quaternion inverseWorldRotation = Quaternion.Inverse(worldRotationQuatnerion);
                        ____worldRotation = worldRotationQuatnerion;
                        ____inverseWorldRotation = inverseWorldRotation;
                    }

                    worldRotationQuatnerion *= localRotation;

                    if (localRotationOffset.HasValue)
                    {
                        worldRotationQuatnerion *= localRotationOffset.Value;
                    }

                    transform.localRotation = worldRotationQuatnerion;
                }

                if (cuttable.HasValue)
                {
                    if (cuttable.Value >= 1)
                    {
                        if (____bounds.size != Vector3.zero)
                        {
                            ____bounds.size = Vector3.zero;
                        }
                    }
                    else
                    {
                        Vector3 boundsSize = noodleData.BoundsSize;
                        if (____bounds.size != boundsSize)
                        {
                            ____bounds.size = boundsSize;
                        }
                    }
                }

                if (scaleOffset.HasValue)
                {
                    transform.localScale = scaleOffset.Value;
                }

                if (dissolve.HasValue)
                {
                    if (CutoutManager.ObstacleCutoutEffects.TryGetValue(__instance, out CutoutAnimateEffectWrapper cutoutAnimateEffect))
                    {
                        cutoutAnimateEffect.SetCutout(dissolve.Value);
                    }
                }
            }

            if (noodleData.DoUnhide)
            {
                __instance.hide = false;
            }
        }
    }

    [HeckPatch(typeof(ObstacleController))]
    [HeckPatch("GetPosForTime")]
    internal static class ObstacleControllerGetPosForTime
    {
        private static bool Prefix(
            ref Vector3 __result,
            ObstacleData ____obstacleData,
            Vector3 ____startPos,
            Vector3 ____midPos,
            float ____move1Duration,
            float ____move2Duration,
            float ____obstacleDuration,
            float time)
        {
            NoodleObstacleData? noodleData = TryGetObjectData<NoodleObstacleData>(____obstacleData);
            if (noodleData == null)
            {
                return true;
            }

            float jumpTime = Mathf.Clamp((time - ____move1Duration) / (____move2Duration + ____obstacleDuration), 0, 1);
            Animation.AnimationHelper.GetDefinitePositionOffset(noodleData.AnimationObject, noodleData.Track, jumpTime, out Vector3? position);

            if (position.HasValue)
            {
                Vector3 noteOffset = noodleData.NoteOffset;
                Vector3 definitePosition = position.Value + noteOffset;
                definitePosition.x += noodleData.XOffset;
                if (time < ____move1Duration)
                {
                    __result = Vector3.LerpUnclamped(____startPos, ____midPos, time / ____move1Duration);
                    __result += definitePosition - ____midPos;
                }
                else
                {
                    __result = definitePosition;
                }

                return false;
            }

            return true;
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/SceneTransition/MissionLevelScenesTransitionSetupDataSO.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System;
    using HarmonyLib;

    [HarmonyPatch(
        typeof(MissionLevelScenesTransitionSetupDataSO),
        new Type[] { typeof(string), typeof(IDifficultyBeatmap), typeof(IPreviewBeatmapLevel), typeof(MissionObjective[]), typeof(ColorScheme), typeof(GameplayModifiers), typeof(PlayerSpecificSettings), typeof(string) })]
    [HarmonyPatch("Init")]
    internal static class MissionLevelScenesTransitionSetupDataSOInit
    {
        private static void Postfix(IDifficultyBeatmap difficultyBeatmap)
        {
            SceneTransitionHelper.Patch(difficultyBeatmap);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/SceneTransition/MultiplayerLevelScenesTransitionSetupDataSO.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System;
    using HarmonyLib;

    [HarmonyPatch(
        typeof(MultiplayerLevelScenesTransitionSetupDataSO),
        new Type[] { typeof(string), typeof(IPreviewBeatmapLevel), typeof(BeatmapDifficulty), typeof(BeatmapCharacteristicSO), typeof(IDifficultyBeatmap), typeof(ColorScheme), typeof(GameplayModifiers), typeof(PlayerSpecificSettings), typeof(PracticeSettings), typeof(bool) })]
    [HarmonyPatch("Init")]
    internal static class MultiplayerLevelScenesTransitionSetupDataSOInit
    {
        private static void Postfix(IDifficultyBeatmap difficultyBeatmap)
        {
            SceneTransitionHelper.Patch(difficultyBeatmap);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/SceneTransition/SceneTransitionHelper.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Linq;
    using CustomJSONData;
    using CustomJSONData.CustomBeatmap;
    using static NoodleExtensions.Plugin;

    internal static class SceneTransitionHelper
    {
        internal static void Patch(IDifficultyBeatmap difficultyBeatmap)
        {
            if (difficultyBeatmap.beatmapData is CustomBeatmapData customBeatmapData)
            {
                IEnumerable<string>? requirements = customBeatmapData.beatmapCustomData.Get<List<object>>("_requirements")?.Cast<string>();
                bool noodleRequirement = requirements?.Contains(CAPABILITY) ?? false;
                NoodleController.ToggleNoodlePatches(noodleRequirement);
            }
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/SceneTransition/StandardLevelScenesTransitionSetupDataSO.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System;
    using HarmonyLib;

    [HarmonyPatch(
        typeof(StandardLevelScenesTransitionSetupDataSO),
        new Type[] { typeof(string), typeof(IDifficultyBeatmap), typeof(IPreviewBeatmapLevel), typeof(OverrideEnvironmentSettings), typeof(ColorScheme), typeof(GameplayModifiers), typeof(PlayerSpecificSettings), typeof(PracticeSettings), typeof(string), typeof(bool) })]
    [HarmonyPatch("Init")]
    internal static class StandardLevelScenesTransitionSetupDataSOInit
    {
        private static void Postfix(IDifficultyBeatmap difficultyBeatmap)
        {
            SceneTransitionHelper.Patch(difficultyBeatmap);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/SceneTransition/TutorialScenesTransitionSetupDataSO.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using HarmonyLib;

    // Force disable Noodle Extensions in tutorial scene
    [HarmonyPatch(typeof(TutorialScenesTransitionSetupDataSO))]
    [HarmonyPatch("Init")]
    internal static class TutorialScenesTransitionSetupDataSOInit
    {
        private static void Prefix()
        {
            NoodleController.ToggleNoodlePatches(false);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/SmallFixes/BasicBeatmapObjectManager.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System;
    using System.Collections.Generic;
    using Heck;

    // Do not add fake obstacles to active obstacles to increase performance
    [HeckPatch(typeof(BasicBeatmapObjectManager))]
    [HeckPatch("Init")]
    internal static class BasicBeatmapObjectManagerInit
    {
        private static void Postfix()
        {
            ObstacleControllerInit._activeObstacles.Clear();
        }
    }

    [HeckPatch(typeof(BasicBeatmapObjectManager))]
    [HeckPatch("get_activeObstacleControllers")]
    internal static class BasicBeatmapObjectManagerGetActiveObstacleControllers
    {
        private static bool Prefix(ref List<ObstacleController> __result)
        {
            __result = ObstacleControllerInit._activeObstacles;
            return false;
        }
    }

    [HeckPatch(typeof(BasicBeatmapObjectManager))]
    [HeckPatch("DespawnInternal")]
    [HeckPatch(new Type[] { typeof(ObstacleController) })]
    internal static class BasicBeatmapObjectManagerDespawnInternal
    {
        private static void Postfix(ObstacleController obstacleController)
        {
            ObstacleControllerInit._activeObstacles.Remove(obstacleController);
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/SmallFixes/BeatEffectSpawner.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Reflection;
    using System.Reflection.Emit;
    using HarmonyLib;
    using Heck;
    using UnityEngine;

    // Readjust spawn effect to take global position instead of local
    [HeckPatch(typeof(BeatEffectSpawner))]
    [HeckPatch("HandleNoteDidStartJump")]
    internal static class BeatEffectSpawnerHandleNoteDidStartJump
    {
        private static readonly MethodInfo _jumpStartPosGetter = AccessTools.PropertyGetter(typeof(NoteController), nameof(NoteController.jumpStartPos));
        private static readonly MethodInfo _beatEffectInit = AccessTools.Method(typeof(BeatEffect), nameof(BeatEffect.Init));

        private static readonly MethodInfo _getNoteControllerPosition = AccessTools.Method(typeof(BeatEffectSpawnerHandleNoteDidStartJump), nameof(GetNoteControllerPosition));
        private static readonly MethodInfo _getNoteControllerRotation = AccessTools.Method(typeof(BeatEffectSpawnerHandleNoteDidStartJump), nameof(GetNoteControllerRotation));

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)

                // position
                .MatchForward(false, new CodeMatch(OpCodes.Callvirt, _jumpStartPosGetter))
                .Advance(-2)
                .InsertAndAdvance(new CodeInstruction(OpCodes.Call, _getNoteControllerPosition))
                .RemoveInstructions(4)

                // rotation
                .MatchForward(false, new CodeMatch(OpCodes.Callvirt, _beatEffectInit))
                .Advance(-1)
                .Set(OpCodes.Call, _getNoteControllerRotation)

                .InstructionEnumeration();
        }

        private static Vector3 GetNoteControllerPosition(NoteController noteController)
        {
            return noteController.transform.position;
        }

        private static Quaternion GetNoteControllerRotation(NoteController noteController)
        {
            return noteController.transform.rotation;
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/SmallFixes/BeatmapObjectManager.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System.Collections.Generic;
    using System.Reflection;
    using System.Reflection.Emit;
    using HarmonyLib;
    using Heck;
    using static NoodleExtensions.NoodleObjectDataManager;

    // TODO: find out what actually causes obstacle flickering
    [HeckPatch(typeof(BeatmapObjectManager))]
    [HeckPatch("SpawnObstacle")]
    internal static class BeatmapObjectManagerSpawnObstacle
    {
        private static readonly MethodInfo _spawnhiddenGetter = AccessTools.PropertyGetter(typeof(BeatmapObjectManager), nameof(BeatmapObjectManager.spawnHidden));

        private static readonly MethodInfo _getHiddenForType = AccessTools.Method(typeof(BeatmapObjectManagerSpawnObstacle), nameof(GetHiddenForType));

        private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
            return new CodeMatcher(instructions)
                .MatchForward(false, new CodeMatch(OpCodes.Call, _spawnhiddenGetter))
                .SetOperandAndAdvance(_getHiddenForType)
                .InstructionEnumeration();
        }

        private static void Postfix(ObstacleController __result)
        {
            NoodleObstacleData? noodleData = TryGetObjectData<NoodleObstacleData>(__result.obstacleData);
            if (noodleData != null)
            {
                noodleData.DoUnhide = true;
            }
        }

        private static bool GetHiddenForType(BeatmapObjectManager beatmapObjectManager)
        {
            if (beatmapObjectManager is BasicBeatmapObjectManager)
            {
                return true;
            }

            return beatmapObjectManager.spawnHidden;
        }
    }
}


================================================
FILE: NoodleExtensions/HarmonyPatches/SmallFixes/CutoutEffect.cs
================================================
namespace NoodleExtensions.HarmonyPatches
{
    using System;
    using Heck;
    using UnityEn
Download .txt
gitextract_67mkl1wr/

├── .editorconfig
├── .gitattributes
├── .github/
│   └── FUNDING.yml
├── .gitignore
├── Documentation/
│   ├── AnimationDocs.md
│   └── examples/
│       └── documentationMap/
│           ├── README.md
│           ├── cat.ogg
│           ├── count.txt
│           └── demo.js
├── LICENSE
├── NoodleExtensions/
│   ├── Animation/
│   │   ├── AnimationController.cs
│   │   ├── AnimationHelper.cs
│   │   ├── Events/
│   │   │   ├── AssignPlayerToTrack.cs
│   │   │   ├── AssignTrackParent.cs
│   │   │   └── NoodleEventData.cs
│   │   ├── ParentObject.cs
│   │   └── PlayerTrack.cs
│   ├── CutoutManager.cs
│   ├── Directory.Build.props
│   ├── Directory.Build.targets
│   ├── HarmonyPatches/
│   │   ├── BeatmapDataLoader.cs
│   │   ├── BeatmapDataTransformHelper.cs
│   │   ├── BeatmapObjectCallBackController.cs
│   │   ├── BeatmapObjectManager.cs
│   │   ├── BeatmapObjectSpawnController.cs
│   │   ├── BeatmapObjectSpawnMovementData.cs
│   │   ├── BeatmapObjectsInTimeRowProcessor.cs
│   │   ├── Cutout/
│   │   │   ├── BaseNoteVisual.cs
│   │   │   └── ObstacleDissolve.cs
│   │   ├── FakeNotes/
│   │   │   ├── BadNoteCutEffectSpawner.cs
│   │   │   ├── BeatmapData.cs
│   │   │   ├── BeatmapObjectExecutionRatingsRecorder.cs
│   │   │   ├── BeatmapObjectManager.cs
│   │   │   ├── BombCutSoundEffectManager.cs
│   │   │   ├── BombNoteController.cs
│   │   │   ├── FakeNoteHelper.cs
│   │   │   ├── GameEnergyCounter.cs
│   │   │   ├── GameNoteController.cs
│   │   │   ├── NoteCutCoreEffectSpawner.cs
│   │   │   ├── NoteCutScoreSpawner.cs
│   │   │   ├── NoteCutSoundEffectManager.cs
│   │   │   ├── ObstacleSaberSparkleEffectManager.cs
│   │   │   └── PlayerHeadAndObstacleInteraction.cs
│   │   ├── GameplayCoreInstaller.cs
│   │   ├── LeftHanded/
│   │   │   ├── BeatmapDataMirrorTransform.cs
│   │   │   ├── NoteData.cs
│   │   │   └── ObstacleData.cs
│   │   ├── Mirror/
│   │   │   ├── MirroredNoteController.cs
│   │   │   └── MirroredObstacleController.cs
│   │   ├── MultiplayerConnectedPlayerInstaller.cs
│   │   ├── NoteController.cs
│   │   ├── NoteFloorMovement.cs
│   │   ├── NoteJump.cs
│   │   ├── ObstacleController.cs
│   │   ├── SceneTransition/
│   │   │   ├── MissionLevelScenesTransitionSetupDataSO.cs
│   │   │   ├── MultiplayerLevelScenesTransitionSetupDataSO.cs
│   │   │   ├── SceneTransitionHelper.cs
│   │   │   ├── StandardLevelScenesTransitionSetupDataSO.cs
│   │   │   └── TutorialScenesTransitionSetupDataSO.cs
│   │   ├── SmallFixes/
│   │   │   ├── BasicBeatmapObjectManager.cs
│   │   │   ├── BeatEffectSpawner.cs
│   │   │   ├── BeatmapObjectManager.cs
│   │   │   ├── CutoutEffect.cs
│   │   │   ├── DisappearingArrowController.cs
│   │   │   ├── MemoryPoolBase.cs
│   │   │   └── PlayerTransforms.cs
│   │   ├── SpawnDataHelper.cs
│   │   └── SpawnRotationProcessor.cs
│   ├── NoodleController.cs
│   ├── NoodleExtensions.csproj
│   ├── NoodleExtensionsExtensions.cs
│   ├── NoodleObjectData.cs
│   ├── Plugin.cs
│   └── manifest.json
├── NoodleExtensions.sln
└── README.md
Download .txt
SYMBOL INDEX (249 symbols across 61 files)

FILE: Documentation/examples/documentationMap/demo.js
  constant INPUT (line 5) | const INPUT = "ExpertPlusStandard.dat"
  constant OUTPUT (line 6) | const OUTPUT = "ExpertStandard.dat"
  function round (line 44) | function round(value, decimals) {
  function getJumps (line 47) | function getJumps(njs, offset) {
  function offestOnNotesBetween (line 70) | function offestOnNotesBetween(p1,p2,offset) {
  function lerp (line 82) | function lerp(v0,v1,t) {
  function trackOnNotesBetween (line 85) | function trackOnNotesBetween(track, p1,p2,potentialOffset) {
  function trackOnNotesBetweenRBSep (line 98) | function trackOnNotesBetweenRBSep(trackR, trackB, p1, p2, potentialOffset){
  function trackOnNotesBetweenDirSep (line 111) | function trackOnNotesBetweenDirSep(p1, p2, potentialOffset, trackUp, tra...
  function deeperDaddy (line 684) | function deeperDaddy(obj) {

FILE: NoodleExtensions/Animation/AnimationController.cs
  class AnimationController (line 6) | public class AnimationController : MonoBehaviour
    method CustomEventCallbackInit (line 12) | internal static void CustomEventCallbackInit(CustomEventCallbackContro...

FILE: NoodleExtensions/Animation/AnimationHelper.cs
  class AnimationHelper (line 12) | public static class AnimationHelper
    method OnTrackCreated (line 16) | internal static void OnTrackCreated(Track track)
    method GetDefinitePositionOffset (line 37) | internal static void GetDefinitePositionOffset(NoodleObjectData.Animat...
    method GetObjectOffset (line 89) | internal static void GetObjectOffset(
    method GetAllPointData (line 204) | internal static void GetAllPointData(

FILE: NoodleExtensions/Animation/Events/AssignPlayerToTrack.cs
  class AssignPlayerToTrack (line 10) | internal static class AssignPlayerToTrack
    method OnTrackManagerCreated (line 12) | internal static void OnTrackManagerCreated(TrackBuilder trackManager, ...
    method Callback (line 28) | internal static void Callback(CustomEventData customEventData)

FILE: NoodleExtensions/Animation/Events/AssignTrackParent.cs
  class AssignTrackParent (line 10) | internal class AssignTrackParent
    method OnTrackManagerCreated (line 12) | internal static void OnTrackManagerCreated(TrackBuilder trackManager, ...
    method Callback (line 28) | internal static void Callback(CustomEventData customEventData)

FILE: NoodleExtensions/Animation/Events/NoodleEventData.cs
  class NoodleEventDataManager (line 13) | internal static class NoodleEventDataManager
    method TryGetEventData (line 17) | internal static T? TryGetEventData<T>(CustomEventData customEventData)
    method DeserializeBeatmapData (line 34) | internal static void DeserializeBeatmapData(IReadonlyBeatmapData beatm...
    method ProcessParentTrackEvent (line 70) | private static NoodleParentTrackEventData ProcessParentTrackEvent(Dict...
  type NoodlePlayerTrackEventData (line 110) | internal record NoodlePlayerTrackEventData : NoodleEventData
  type NoodleParentTrackEventData (line 120) | internal record NoodleParentTrackEventData : NoodleEventData
  type NoodleEventData (line 145) | internal record NoodleEventData

FILE: NoodleExtensions/Animation/ParentObject.cs
  class ParentObject (line 12) | internal class ParentObject : MonoBehaviour
    method AssignTrack (line 25) | internal static void AssignTrack(IEnumerable<Track> tracks, Track pare...
    method ResetTransformParent (line 95) | private static void ResetTransformParent(Transform transform)
    method OnTrackGameObjectAdded (line 100) | private void OnTrackGameObjectAdded(GameObject gameObject)
    method OnTrackGameObjectRemoved (line 105) | private void OnTrackGameObjectRemoved(GameObject gameObject)
    method ParentToObject (line 110) | private void ParentToObject(Transform transform)
    method OnDestroy (line 115) | private void OnDestroy()
    method Update (line 124) | private void Update()
  class ParentController (line 190) | internal class ParentController : MonoBehaviour
    method GetParentObjectTrack (line 194) | internal ParentObject? GetParentObjectTrack(Track track)
    method GetParentObjectTrackArray (line 207) | internal ParentObject? GetParentObjectTrackArray(IEnumerable<Track> tr...
    method OnDestroy (line 220) | private void OnDestroy()

FILE: NoodleExtensions/Animation/PlayerTrack.cs
  class PlayerTrack (line 11) | internal class PlayerTrack : MonoBehaviour
    method AssignTrack (line 23) | internal static void AssignTrack(Track track)
    method OnDidPauseEvent (line 47) | private void OnDidPauseEvent()
    method OnDestroy (line 53) | private void OnDestroy()
    method Update (line 61) | private void Update()

FILE: NoodleExtensions/CutoutManager.cs
  class CutoutManager (line 7) | internal static class CutoutManager
  class CutoutWrapper (line 16) | internal abstract class CutoutWrapper
    method SetCutout (line 20) | internal virtual void SetCutout(float cutout)
  class CutoutEffectWrapper (line 26) | internal class CutoutEffectWrapper : CutoutWrapper
    method CutoutEffectWrapper (line 30) | internal CutoutEffectWrapper(CutoutEffect cutoutEffect)
    method SetCutout (line 35) | internal override void SetCutout(float cutout)
  class CutoutAnimateEffectWrapper (line 42) | internal class CutoutAnimateEffectWrapper : CutoutWrapper
    method CutoutAnimateEffectWrapper (line 46) | internal CutoutAnimateEffectWrapper(CutoutAnimateEffect cutoutAnimateE...
    method SetCutout (line 51) | internal override void SetCutout(float cutout)
  class DisappearingArrowWrapper (line 58) | internal class DisappearingArrowWrapper : CutoutWrapper
    method DisappearingArrowWrapper (line 64) | internal DisappearingArrowWrapper(MonoBehaviour disappearingArrowContr...
    method SetCutout (line 70) | internal override void SetCutout(float cutout)

FILE: NoodleExtensions/HarmonyPatches/BeatmapDataLoader.cs
  class BeatmapDataLoaderGetBeatmapDataFromBeatmapSaveData (line 7) | [HarmonyPatch(typeof(BeatmapDataLoader))]
    method Postfix (line 11) | private static void Postfix(BeatmapData __result, float startBpm)

FILE: NoodleExtensions/HarmonyPatches/BeatmapDataTransformHelper.cs
  class BeatmapDataTransformHelperCreateTransformedBeatmapData (line 15) | [HeckPatch(typeof(BeatmapDataTransformHelper))]
    method Transpiler (line 23) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method ReorderLineData (line 39) | private static IReadonlyBeatmapData ReorderLineData(IReadonlyBeatmapDa...
    method Postfix (line 90) | private static void Postfix(IReadonlyBeatmapData __result)

FILE: NoodleExtensions/HarmonyPatches/BeatmapObjectCallBackController.cs
  class BeatmapObjectCallBackControllerLateUpdate (line 11) | [HeckPatch(typeof(BeatmapObjectCallbackController))]
    method Transpiler (line 20) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method GetAheadTime (line 33) | private static float GetAheadTime(BeatmapObjectCallbackData beatmapObj...

FILE: NoodleExtensions/HarmonyPatches/BeatmapObjectManager.cs
  class BeatmapObjectManagerHandleNoteControllerNoteDidFinishJump (line 7) | [HeckPatch(typeof(BeatmapObjectManager))]
    method Prefix (line 11) | private static void Prefix(NoteController noteController)
  class BeatmapObjectManagerHandleObstacleFinishedMovement (line 24) | [HeckPatch(typeof(BeatmapObjectManager))]
    method Prefix (line 28) | private static void Prefix(ObstacleController obstacleController)

FILE: NoodleExtensions/HarmonyPatches/BeatmapObjectSpawnController.cs
  class BeatmapObjectSpawnControllerStart (line 5) | [HeckPatch(typeof(BeatmapObjectSpawnController))]
    method Postfix (line 11) | private static void Postfix(IBeatmapObjectSpawner ____beatmapObjectSpa...

FILE: NoodleExtensions/HarmonyPatches/BeatmapObjectSpawnMovementData.cs
  class BeatmapObjectSpawnMovementDataGetObstacleSpawnData (line 9) | [HeckPatch(typeof(BeatmapObjectSpawnMovementData))]
    method Postfix (line 13) | private static void Postfix(Vector3 ____centerPos, ObstacleData obstac...
  class BeatmapObjectSpawnMovementDataGetJumpingNoteSpawnData (line 72) | [HeckPatch(typeof(BeatmapObjectSpawnMovementData))]
    method Postfix (line 76) | private static void Postfix(BeatmapObjectSpawnMovementData __instance,...

FILE: NoodleExtensions/HarmonyPatches/BeatmapObjectsInTimeRowProcessor.cs
  class NotesInTimeRowProcessorProcessAllNotesInTimeRow (line 10) | [HarmonyPatch(typeof(BeatmapObjectsInTimeRowProcessor))]
    method Prefix (line 14) | private static void Prefix(List<NoteData> notesInTimeRow)
  class NotesInTimeRowProcessorProcessColorNotesInTimeRow (line 91) | [HarmonyPatch(typeof(BeatmapObjectsInTimeRowProcessor))]
    method Prefix (line 95) | private static void Prefix(List<NoteData> colorNotesData)

FILE: NoodleExtensions/HarmonyPatches/Cutout/BaseNoteVisual.cs
  class BaseNoteVisualsAwake (line 11) | [HeckPatch(typeof(BaseNoteVisuals))]
    method Postfix (line 19) | private static void Postfix(BaseNoteVisuals __instance, NoteController...
    method GetSetArrowTransparency (line 40) | private static MethodInfo GetSetArrowTransparency(Type type)
  class DisappearingArrowControllerBaseGameNoteControllerOnDestroy (line 60) | [HeckPatch(typeof(BaseNoteVisuals))]
    method Postfix (line 64) | private static void Postfix(NoteControllerBase ____noteController)

FILE: NoodleExtensions/HarmonyPatches/Cutout/ObstacleDissolve.cs
  class ObstacleDissolveAwake (line 5) | [HeckPatch(typeof(ObstacleDissolve))]
    method Postfix (line 9) | private static void Postfix(ObstacleController ____obstacleController,...
  class ObstacleDissolveOnDestroy (line 15) | [HeckPatch(typeof(ObstacleDissolve))]
    method Postfix (line 19) | private static void Postfix(ObstacleController ____obstacleController)

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BadNoteCutEffectSpawner.cs
  class BadNoteCutEffectSpawnerHandleNoteWasCut (line 5) | [HeckPatch(typeof(BadNoteCutEffectSpawner))]
    method Prefix (line 9) | private static bool Prefix(NoteController noteController)

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BeatmapData.cs
  class BeatmapDataCtor (line 12) | [HarmonyPatch(typeof(BeatmapData))]
    method Postfix (line 17) | private static void Postfix()
  class BeatmapDataAddBeatmapObjectData (line 23) | [HarmonyPatch(typeof(BeatmapData))]
    method Prefix (line 39) | private static void Prefix(BeatmapData __instance)
    method Transpiler (line 56) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method FakeObjectCheck (line 70) | private static int FakeObjectCheck(int objectCount, BeatmapObjectData ...

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BeatmapObjectExecutionRatingsRecorder.cs
  class BeatmapObjectExecutionRatingsRecorderUpdate (line 7) | [HeckPatch(typeof(BeatmapObjectExecutionRatingsRecorder))]
    method Transpiler (line 11) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BeatmapObjectManager.cs
  class BeatmapObjectManagerHandleNoteWasCut (line 8) | [HeckPatch(typeof(BeatmapObjectManager))]
    method Prefix (line 14) | [HarmonyPriority(Priority.High)]
  class BeatmapObjectManagerHandleNoteWasMissed (line 29) | [HeckPatch(typeof(BeatmapObjectManager))]
    method Prefix (line 33) | [HarmonyPriority(Priority.High)]

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BombCutSoundEffectManager.cs
  class BombCutSoundEffectManagerHandleNoteWasCut (line 5) | [HeckPatch(typeof(BombCutSoundEffectManager))]
    method Prefix (line 10) | private static bool Prefix(NoteController noteController)

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/BombNoteController.cs
  class BombNoteControllerInit (line 6) | [HeckPatch(typeof(BombNoteController))]
    method Postfix (line 10) | [HarmonyPriority(Priority.High)]

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/FakeNoteHelper.cs
  class FakeNoteHelper (line 11) | internal static class FakeNoteHelper
    method GetFakeNote (line 18) | internal static bool GetFakeNote(NoteController noteController)
    method GetCuttable (line 33) | internal static bool GetCuttable(NoteData noteData)
    method ObstaclesTranspiler (line 48) | internal static IEnumerable<CodeInstruction> ObstaclesTranspiler(IEnum...
    method BoundsNullCheck (line 57) | private static bool BoundsNullCheck(ObstacleController obstacleControl...
    method ObstacleFakeCheck (line 62) | private static List<ObstacleController> ObstacleFakeCheck(List<Obstacl...

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/GameEnergyCounter.cs
  class GameEnergyCounterUpdate (line 7) | [HeckPatch(typeof(GameEnergyCounter))]
    method Transpiler (line 11) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/GameNoteController.cs
  class GameNoteControllerNoteDidStartJump (line 6) | [HeckPatch(typeof(GameNoteController))]
    method Prefix (line 10) | [HarmonyPriority(Priority.High)]

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/NoteCutCoreEffectSpawner.cs
  class NoteCutCoreEffectsSpawnerStart (line 5) | [HeckPatch(typeof(NoteCutCoreEffectsSpawner))]
    method Postfix (line 11) | private static void Postfix(NoteCutCoreEffectsSpawner __instance)

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/NoteCutScoreSpawner.cs
  class NoteCutScoreSpawnerHandleNoteWasCut (line 5) | [HeckPatch(typeof(NoteCutScoreSpawner))]
    method Prefix (line 9) | private static bool Prefix(NoteController noteController)

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/NoteCutSoundEffectManager.cs
  class NoteCutSoundEffectManagerStart (line 7) | [HeckPatch(typeof(NoteCutSoundEffectManager))]
    method Postfix (line 11) | private static void Postfix(NoteCutSoundEffectManager __instance)
  class NoteCutSoundEffectManagerHandleNoteWasSpawned (line 19) | [HeckPatch(typeof(NoteCutSoundEffectManager))]
    method Prefix (line 26) | private static bool Prefix(NoteController noteController)
  class NoodleCutSoundEffectManager (line 38) | internal class NoodleCutSoundEffectManager : MonoBehaviour
    method Init (line 46) | internal void Init(NoteCutSoundEffectManager noteCutSoundEffectManager)
    method ProcessHitSound (line 51) | internal bool ProcessHitSound(NoteController noteController)
    method Update (line 75) | private void Update()

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/ObstacleSaberSparkleEffectManager.cs
  class ObstacleSaberSparkleEffectManagerUpdate (line 9) | [HeckPatch(typeof(ObstacleSaberSparkleEffectManager))]
    method Transpiler (line 15) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...

FILE: NoodleExtensions/HarmonyPatches/FakeNotes/PlayerHeadAndObstacleInteraction.cs
  class PlayerHeadAndObstacleInteractionGetObstaclesContainingPoint (line 9) | [HeckPatch(typeof(PlayerHeadAndObstacleInteraction))]
    method Transpiler (line 15) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...

FILE: NoodleExtensions/HarmonyPatches/GameplayCoreInstaller.cs
  class GameplayCoreInstallerInstallBindings (line 9) | [HeckPatch(typeof(GameplayCoreInstaller))]
    method Transpiler (line 21) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method CacheNoteJumpValues (line 32) | private static void CacheNoteJumpValues(float defaultNoteJumpMovementS...

FILE: NoodleExtensions/HarmonyPatches/LeftHanded/BeatmapDataMirrorTransform.cs
  class BeatDataMirrorTransformCreateTransformedData (line 9) | [HarmonyPatch(typeof(BeatmapDataMirrorTransform))]
    method Postfix (line 13) | private static void Postfix(IReadonlyBeatmapData __result)

FILE: NoodleExtensions/HarmonyPatches/LeftHanded/NoteData.cs
  class NoteDataMirror (line 13) | [HarmonyPatch(typeof(NoteData))]
    method Postfix (line 17) | private static void Postfix(NoteData __instance)

FILE: NoodleExtensions/HarmonyPatches/LeftHanded/ObstacleData.cs
  class ObstacleDataMirror (line 13) | [HarmonyPatch(typeof(ObstacleData))]
    method Prefix (line 17) | private static void Prefix(ObstacleData __instance) // prefix because ...

FILE: NoodleExtensions/HarmonyPatches/Mirror/MirroredNoteController.cs
  class MirroredNoteControllerHelper (line 9) | internal static class MirroredNoteControllerHelper
    method AddToTrack (line 11) | internal static void AddToTrack(NoteData noteData, GameObject gameObject)
    method CheckSkip (line 28) | internal static bool CheckSkip(Transform noteTransform, Transform foll...
    method UpdateMirror (line 48) | internal static void UpdateMirror(Transform objectTransform, Transform...
  class MirroredNoteControllerINoteMirrorableMirror (line 79) | [HeckPatch(typeof(MirroredNoteController<INoteMirrorable>))]
    method Postfix (line 83) | private static void Postfix(MirroredNoteController<INoteMirrorable> __...
  class MirroredNoteControllerICubeNoteMirrorableMirror (line 89) | [HeckPatch(typeof(MirroredNoteController<ICubeNoteMirrorable>))]
    method Postfix (line 93) | private static void Postfix(MirroredNoteController<ICubeNoteMirrorable...
  class MirroredNoteControllerINoteMirrorableUpdatePositionAndRotation (line 99) | [HeckPatch(typeof(MirroredNoteController<INoteMirrorable>))]
    method Prefix (line 103) | private static bool Prefix(Transform ____noteTransform, Transform ____...
    method Postfix (line 108) | private static void Postfix(MirroredNoteController<INoteMirrorable> __...
  class MirroredNoteControllerICubeNoteMirrorableUpdatePositionAndRotation (line 114) | [HeckPatch(typeof(MirroredNoteController<ICubeNoteMirrorable>))]
    method Prefix (line 118) | private static bool Prefix(Transform ____noteTransform, Transform ____...
    method Postfix (line 123) | private static void Postfix(MirroredNoteController<ICubeNoteMirrorable...

FILE: NoodleExtensions/HarmonyPatches/Mirror/MirroredObstacleController.cs
  class MirroredObstacleControllerUpdatePositionAndRotation (line 6) | [HeckPatch(typeof(MirroredObstacleController))]
    method Prefix (line 11) | private static bool Prefix(MirroredObstacleController __instance, Obst...

FILE: NoodleExtensions/HarmonyPatches/MultiplayerConnectedPlayerInstaller.cs
  class MultiplayerConnectedPlayerInstallerInstallBindings (line 14) | [HeckPatch(typeof(MultiplayerConnectedPlayerInstaller))]
    method Transpiler (line 24) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method ExcludeFakeNoteAndAllWalls (line 33) | private static IReadonlyBeatmapData ExcludeFakeNoteAndAllWalls(IReadon...

FILE: NoodleExtensions/HarmonyPatches/NoteController.cs
  class NoteControllerInit (line 13) | [HeckPatch(typeof(NoteController))]
    method Postfix (line 35) | private static void Postfix(NoteController __instance, NoteData noteDa...
    method Transpiler (line 113) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method GetFlipYSide (line 125) | private static float GetFlipYSide(NoteData noteData, float @default)
  class NoteControllerUpdate (line 143) | [HeckPatch(typeof(NoteController))]
    method Prefix (line 161) | private static void Prefix(NoteController __instance, NoteData ____not...

FILE: NoodleExtensions/HarmonyPatches/NoteFloorMovement.cs
  class NoteFloorMovementManualUpdate (line 11) | [HeckPatch(typeof(NoteFloorMovement))]
    method Transpiler (line 19) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method DefiniteNoteFloorMovement (line 29) | private static Vector3 DefiniteNoteFloorMovement(Vector3 original, Not...
  class NoteFloorMovementSetToStart (line 47) | [HeckPatch(typeof(NoteFloorMovement))]
    method Postfix (line 51) | private static void Postfix(Transform ____rotatedObject)

FILE: NoodleExtensions/HarmonyPatches/NoteJump.cs
  class NoteJumpManualUpdate (line 13) | [HeckPatch(typeof(NoteJump))]
    method NoteJumpTimeAdjust (line 40) | internal static float NoteJumpTimeAdjust(float original, float jumpDur...
    method Transpiler (line 55) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method DefiniteNoteJump (line 129) | private static Vector3 DefiniteNoteJump(Vector3 original, float time)
    method DoNoteLook (line 149) | private static void DoNoteLook(

FILE: NoodleExtensions/HarmonyPatches/ObstacleController.cs
  class ObstacleControllerInit (line 15) | [HeckPatch(typeof(ObstacleController))]
    method Transpiler (line 31) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method Postfix (line 67) | private static void Postfix(ObstacleController __instance, Quaternion ...
    method InvertQuaternion (line 118) | private static Quaternion InvertQuaternion(Quaternion quaternion)
    method GetWorldRotation (line 123) | private static Quaternion GetWorldRotation(ObstacleData obstacleData, ...
    method GetCustomWidth (line 142) | private static float GetCustomWidth(float @default, ObstacleData obsta...
    method GetCustomLength (line 157) | private static float GetCustomLength(float @default, ObstacleData obst...
  class ObstacleControllerManualUpdate (line 173) | [HeckPatch(typeof(ObstacleController))]
    method Transpiler (line 182) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method ObstacleTimeAdjust (line 210) | private static float ObstacleTimeAdjust(float original, ObstacleData o...
    method Prefix (line 228) | private static void Prefix(
  class ObstacleControllerGetPosForTime (line 337) | [HeckPatch(typeof(ObstacleController))]
    method Prefix (line 341) | private static bool Prefix(

FILE: NoodleExtensions/HarmonyPatches/SceneTransition/MissionLevelScenesTransitionSetupDataSO.cs
  class MissionLevelScenesTransitionSetupDataSOInit (line 6) | [HarmonyPatch(
    method Postfix (line 12) | private static void Postfix(IDifficultyBeatmap difficultyBeatmap)

FILE: NoodleExtensions/HarmonyPatches/SceneTransition/MultiplayerLevelScenesTransitionSetupDataSO.cs
  class MultiplayerLevelScenesTransitionSetupDataSOInit (line 6) | [HarmonyPatch(
    method Postfix (line 12) | private static void Postfix(IDifficultyBeatmap difficultyBeatmap)

FILE: NoodleExtensions/HarmonyPatches/SceneTransition/SceneTransitionHelper.cs
  class SceneTransitionHelper (line 9) | internal static class SceneTransitionHelper
    method Patch (line 11) | internal static void Patch(IDifficultyBeatmap difficultyBeatmap)

FILE: NoodleExtensions/HarmonyPatches/SceneTransition/StandardLevelScenesTransitionSetupDataSO.cs
  class StandardLevelScenesTransitionSetupDataSOInit (line 6) | [HarmonyPatch(
    method Postfix (line 12) | private static void Postfix(IDifficultyBeatmap difficultyBeatmap)

FILE: NoodleExtensions/HarmonyPatches/SceneTransition/TutorialScenesTransitionSetupDataSO.cs
  class TutorialScenesTransitionSetupDataSOInit (line 6) | [HarmonyPatch(typeof(TutorialScenesTransitionSetupDataSO))]
    method Prefix (line 10) | private static void Prefix()

FILE: NoodleExtensions/HarmonyPatches/SmallFixes/BasicBeatmapObjectManager.cs
  class BasicBeatmapObjectManagerInit (line 8) | [HeckPatch(typeof(BasicBeatmapObjectManager))]
    method Postfix (line 12) | private static void Postfix()
  class BasicBeatmapObjectManagerGetActiveObstacleControllers (line 18) | [HeckPatch(typeof(BasicBeatmapObjectManager))]
    method Prefix (line 22) | private static bool Prefix(ref List<ObstacleController> __result)
  class BasicBeatmapObjectManagerDespawnInternal (line 29) | [HeckPatch(typeof(BasicBeatmapObjectManager))]
    method Postfix (line 34) | private static void Postfix(ObstacleController obstacleController)

FILE: NoodleExtensions/HarmonyPatches/SmallFixes/BeatEffectSpawner.cs
  class BeatEffectSpawnerHandleNoteDidStartJump (line 11) | [HeckPatch(typeof(BeatEffectSpawner))]
    method Transpiler (line 21) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method GetNoteControllerPosition (line 39) | private static Vector3 GetNoteControllerPosition(NoteController noteCo...
    method GetNoteControllerRotation (line 44) | private static Quaternion GetNoteControllerRotation(NoteController not...

FILE: NoodleExtensions/HarmonyPatches/SmallFixes/BeatmapObjectManager.cs
  class BeatmapObjectManagerSpawnObstacle (line 11) | [HeckPatch(typeof(BeatmapObjectManager))]
    method Transpiler (line 19) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...
    method Postfix (line 27) | private static void Postfix(ObstacleController __result)
    method GetHiddenForType (line 36) | private static bool GetHiddenForType(BeatmapObjectManager beatmapObjec...

FILE: NoodleExtensions/HarmonyPatches/SmallFixes/CutoutEffect.cs
  class CutoutEffectSetCutout (line 8) | [HeckPatch(typeof(CutoutEffect))]
    method Prefix (line 13) | private static bool Prefix(float cutout, float ____cutout)

FILE: NoodleExtensions/HarmonyPatches/SmallFixes/DisappearingArrowController.cs
  class DisappearingArrowControllerSetArrowTransparency (line 5) | [HeckPatch(typeof(DisappearingArrowControllerBase<GameNoteController>))]
    method Postfix (line 10) | private static void Postfix(CutoutEffect ____arrowCutoutEffect, float ...

FILE: NoodleExtensions/HarmonyPatches/SmallFixes/MemoryPoolBase.cs
  class MemoryPoolBaseObstacleControllerDespawn (line 11) | [HeckPatch(typeof(MemoryPoolBase<ObstacleController>))]
    method Transpiler (line 18) | private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<Cod...

FILE: NoodleExtensions/HarmonyPatches/SmallFixes/PlayerTransforms.cs
  class PlayerTransformsUpdate (line 14) | [HeckPatch(typeof(PlayerTransforms))]
    method Postfix (line 18) | private static void Postfix(ref Vector3 ____headPseudoLocalPos, Transf...

FILE: NoodleExtensions/HarmonyPatches/SpawnDataHelper.cs
  class SpawnDataHelper (line 7) | internal static class SpawnDataHelper
    method GetNoteOffset (line 9) | internal static Vector3 GetNoteOffset(BeatmapObjectData beatmapObjectD...
    method LineYPosForLineLayer (line 19) | internal static float LineYPosForLineLayer(BeatmapObjectData beatmapOb...
    method GetNoteJumpValues (line 34) | internal static void GetNoteJumpValues(
    method InitBeatmapObjectSpawnController (line 65) | internal static void InitBeatmapObjectSpawnController(BeatmapObjectSpa...
    class BeatmapObjectSpawnMovementDataVariables (line 70) | internal static class BeatmapObjectSpawnMovementDataVariables

FILE: NoodleExtensions/HarmonyPatches/SpawnRotationProcessor.cs
  class SpawnRotationProcessorProcessBeatmapEventData (line 9) | [HeckPatch(typeof(SpawnRotationProcessor))]
    method Prefix (line 13) | private static bool Prefix(BeatmapEventData beatmapEventData, ref floa...

FILE: NoodleExtensions/NoodleController.cs
  class NoodleController (line 5) | public static class NoodleController
    method ToggleNoodlePatches (line 9) | public static void ToggleNoodlePatches(bool value)

FILE: NoodleExtensions/NoodleExtensionsExtensions.cs
  class NoodleExtensionsExtensions (line 7) | internal static class NoodleExtensionsExtensions
    method GetDataForObject (line 9) | internal static Dictionary<string, object?> GetDataForObject(this Beat...

FILE: NoodleExtensions/NoodleObjectData.cs
  class NoodleObjectDataManager (line 13) | internal static class NoodleObjectDataManager
    method TryGetObjectData (line 17) | internal static T? TryGetObjectData<T>(BeatmapObjectData beatmapObject...
    method DeserializeBeatmapData (line 34) | internal static void DeserializeBeatmapData(IReadonlyBeatmapData beatm...
    method FinalizeCustomObject (line 80) | private static void FinalizeCustomObject(Dictionary<string, object?> d...
    method ProcessCustomNote (line 146) | private static NoodleNoteData ProcessCustomNote(Dictionary<string, obj...
    method ProcessCustomObstacle (line 167) | private static NoodleObstacleData ProcessCustomObstacle(Dictionary<str...
  type NoodleNoteData (line 180) | internal record NoodleNoteData : NoodleObjectData
  type NoodleObstacleData (line 203) | internal record NoodleObstacleData : NoodleObjectData
  type NoodleObjectData (line 224) | internal record NoodleObjectData

FILE: NoodleExtensions/Plugin.cs
  class Plugin (line 9) | [Plugin(RuntimeOptions.DynamicInit)]
    method Init (line 44) | [Init]
    method OnEnable (line 55) | [OnEnable]
    method OnDisable (line 62) | [OnDisable]
Condensed preview — 76 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (244K chars).
[
  {
    "path": ".editorconfig",
    "chars": 3895,
    "preview": "# NOTE: Requires **VS2019 16.3** or later\n\n# Code files\n[*.{cs,vb}]\n\n\ndotnet_diagnostic.CA1001.severity = warning\n\ndotne"
  },
  {
    "path": ".gitattributes",
    "chars": 2518,
    "preview": "###############################################################################\n# Set default behavior to automatically "
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 681,
    "preview": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [u"
  },
  {
    "path": ".gitignore",
    "chars": 5745,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
  },
  {
    "path": "Documentation/AnimationDocs.md",
    "chars": 70,
    "preview": "These docs have moved! https://github.com/Aeroluna/Heck/wiki/Animation"
  },
  {
    "path": "Documentation/examples/documentationMap/README.md",
    "chars": 256,
    "preview": "# Documentation map\nThis is a JS script used to generate the map seen in the documentation of noodle extensions. Most of"
  },
  {
    "path": "Documentation/examples/documentationMap/count.txt",
    "chars": 3,
    "preview": "124"
  },
  {
    "path": "Documentation/examples/documentationMap/demo.js",
    "chars": 14888,
    "preview": "'use strict'\n\nconst fs = require('fs');\n\nconst INPUT = \"ExpertPlusStandard.dat\"\nconst OUTPUT = \"ExpertStandard.dat\"\n\nlet"
  },
  {
    "path": "LICENSE",
    "chars": 1065,
    "preview": "MIT License\n\nCopyright (c) 2020 Aeroluna\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
  },
  {
    "path": "NoodleExtensions/Animation/AnimationController.cs",
    "chars": 1117,
    "preview": "namespace NoodleExtensions.Animation\n{\n    using CustomJSONData;\n    using UnityEngine;\n\n    public class AnimationCont"
  },
  {
    "path": "NoodleExtensions/Animation/AnimationHelper.cs",
    "chars": 11703,
    "preview": "namespace NoodleExtensions.Animation\n{\n    using System.Collections.Generic;\n    using System.Linq;\n    using Heck.Anim"
  },
  {
    "path": "NoodleExtensions/Animation/Events/AssignPlayerToTrack.cs",
    "chars": 1561,
    "preview": "namespace NoodleExtensions.Animation\n{\n    using System.Collections.Generic;\n    using CustomJSONData;\n    using Custom"
  },
  {
    "path": "NoodleExtensions/Animation/Events/AssignTrackParent.cs",
    "chars": 1951,
    "preview": "namespace NoodleExtensions.Animation\n{\n    using System.Collections.Generic;\n    using CustomJSONData;\n    using Custom"
  },
  {
    "path": "NoodleExtensions/Animation/Events/NoodleEventData.cs",
    "chars": 5725,
    "preview": "namespace NoodleExtensions.Animation\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n "
  },
  {
    "path": "NoodleExtensions/Animation/ParentObject.cs",
    "chars": 7763,
    "preview": "namespace NoodleExtensions.Animation\n{\n    using System.Collections.Generic;\n    using System.Linq;\n    using Heck.Anim"
  },
  {
    "path": "NoodleExtensions/Animation/PlayerTrack.cs",
    "chars": 4424,
    "preview": "namespace NoodleExtensions.Animation\n{\n    using Heck.Animation;\n    using IPA.Utilities;\n    using UnityEngine;\n    us"
  },
  {
    "path": "NoodleExtensions/CutoutManager.cs",
    "chars": 2463,
    "preview": "namespace NoodleExtensions\n{\n    using System.Collections.Generic;\n    using System.Reflection;\n    using UnityEngine;\n"
  },
  {
    "path": "NoodleExtensions/Directory.Build.props",
    "chars": 696,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- This file contains project properties used by the build. -->\n<Project>\n  <It"
  },
  {
    "path": "NoodleExtensions/Directory.Build.targets",
    "chars": 7585,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- This file contains the build tasks and targets for verifying the manifest, "
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/BeatmapDataLoader.cs",
    "chars": 1129,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using CustomJSONData.CustomBeatma"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/BeatmapDataTransformHelper.cs",
    "chars": 4760,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Diagnostics;\n    usi"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/BeatmapObjectCallBackController.cs",
    "chars": 2367,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Reflection;\n    usin"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/BeatmapObjectManager.cs",
    "chars": 1406,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using Heck;\n    using Heck.Animation;\n    using static NoodleExtensions"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/BeatmapObjectSpawnController.cs",
    "chars": 774,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using Heck;\n\n    [HeckPatch(typeof(BeatmapObjectSpawnController))]\n    "
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/BeatmapObjectSpawnMovementData.cs",
    "chars": 6991,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using Heck;\n    using UnityEngine;\n    using static NoodleExtensions.Ha"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/BeatmapObjectsInTimeRowProcessor.cs",
    "chars": 6129,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Linq;\n    using Cust"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/Cutout/BaseNoteVisual.cs",
    "chars": 3678,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Li"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/Cutout/ObstacleDissolve.cs",
    "chars": 784,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using Heck;\n\n    [HeckPatch(typeof(ObstacleDissolve))]\n    [HeckPatch(\""
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/BadNoteCutEffectSpawner.cs",
    "chars": 376,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using Heck;\n\n    [HeckPatch(typeof(BadNoteCutEffectSpawner))]\n    [Heck"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/BeatmapData.cs",
    "chars": 3114,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Li"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/BeatmapObjectExecutionRatingsRecorder.cs",
    "chars": 490,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using HarmonyLib;\n    using Heck;"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/BeatmapObjectManager.cs",
    "chars": 1372,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System;\n    using System.Reflection;\n    using HarmonyLib;\n    us"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/BombCutSoundEffectManager.cs",
    "chars": 441,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using Heck;\n\n    [HeckPatch(typeof(BombCutSoundEffectManager))]\n    [He"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/BombNoteController.cs",
    "chars": 505,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using HarmonyLib;\n    using Heck;\n\n    [HeckPatch(typeof(BombNoteContro"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/FakeNoteHelper.cs",
    "chars": 2918,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Linq;\n    using Syst"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/GameEnergyCounter.cs",
    "chars": 454,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using HarmonyLib;\n    using Heck;"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/GameNoteController.cs",
    "chars": 434,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using HarmonyLib;\n    using Heck;\n\n    [HeckPatch(typeof(GameNoteContro"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/NoteCutCoreEffectSpawner.cs",
    "chars": 455,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using Heck;\n\n    [HeckPatch(typeof(NoteCutCoreEffectsSpawner))]\n    [He"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/NoteCutScoreSpawner.cs",
    "chars": 368,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using Heck;\n\n    [HeckPatch(typeof(NoteCutScoreSpawner))]\n    [HeckPatc"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/NoteCutSoundEffectManager.cs",
    "chars": 2893,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using Heck;\n    using UnityEngine"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/ObstacleSaberSparkleEffectManager.cs",
    "chars": 1267,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Reflection;\n    usin"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/FakeNotes/PlayerHeadAndObstacleInteraction.cs",
    "chars": 1307,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Reflection;\n    usin"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/GameplayCoreInstaller.cs",
    "chars": 1698,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Reflection;\n    usin"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/LeftHanded/BeatmapDataMirrorTransform.cs",
    "chars": 1143,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using CustomJSONData;\n    using C"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/LeftHanded/NoteData.cs",
    "chars": 3264,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Li"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/LeftHanded/ObstacleData.cs",
    "chars": 3015,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Li"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/Mirror/MirroredNoteController.cs",
    "chars": 5505,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using Heck;\n    using Heck.Animat"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/Mirror/MirroredObstacleController.cs",
    "chars": 1767,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using Heck;\n    using UnityEngine;\n\n    [HeckPatch(typeof(MirroredObsta"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/MultiplayerConnectedPlayerInstaller.cs",
    "chars": 3068,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Linq;\n    using Syst"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/NoteController.cs",
    "chars": 15149,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Reflection;\n    usin"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/NoteFloorMovement.cs",
    "chars": 2407,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Reflection;\n    usin"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/NoteJump.cs",
    "chars": 10007,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Linq;\n    using Syst"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/ObstacleController.cs",
    "chars": 15886,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Linq;\n    using Syst"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SceneTransition/MissionLevelScenesTransitionSetupDataSO.cs",
    "chars": 649,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System;\n    using HarmonyLib;\n\n    [HarmonyPatch(\n        typeof("
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SceneTransition/MultiplayerLevelScenesTransitionSetupDataSO.cs",
    "chars": 713,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System;\n    using HarmonyLib;\n\n    [HarmonyPatch(\n        typeof("
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SceneTransition/SceneTransitionHelper.cs",
    "chars": 783,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Linq;\n    using Cust"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SceneTransition/StandardLevelScenesTransitionSetupDataSO.cs",
    "chars": 700,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System;\n    using HarmonyLib;\n\n    [HarmonyPatch(\n        typeof("
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SceneTransition/TutorialScenesTransitionSetupDataSO.cs",
    "chars": 410,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using HarmonyLib;\n\n    // Force disable Noodle Extensions in tutorial s"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SmallFixes/BasicBeatmapObjectManager.cs",
    "chars": 1236,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System;\n    using System.Collections.Generic;\n    using Heck;\n\n  "
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SmallFixes/BeatEffectSpawner.cs",
    "chars": 2118,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Reflection;\n    usin"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SmallFixes/BeatmapObjectManager.cs",
    "chars": 1726,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Reflection;\n    usin"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SmallFixes/CutoutEffect.cs",
    "chars": 491,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System;\n    using Heck;\n    using UnityEngine;\n\n    // Do not run"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SmallFixes/DisappearingArrowController.cs",
    "chars": 523,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using Heck;\n\n    [HeckPatch(typeof(DisappearingArrowControllerBase<Game"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SmallFixes/MemoryPoolBase.cs",
    "chars": 1030,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using System.Collections.Generic;\n    using System.Reflection;\n    usin"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SmallFixes/PlayerTransforms.cs",
    "chars": 2727,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    /*using System.Collections.Generic;\n    using System.Linq;\n    using Sy"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SpawnDataHelper.cs",
    "chars": 8301,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using IPA.Utilities;\n    using UnityEngine;\n    using static NoodleExte"
  },
  {
    "path": "NoodleExtensions/HarmonyPatches/SpawnRotationProcessor.cs",
    "chars": 973,
    "preview": "namespace NoodleExtensions.HarmonyPatches\n{\n    using CustomJSONData;\n    using CustomJSONData.CustomBeatmap;\n    using"
  },
  {
    "path": "NoodleExtensions/NoodleController.cs",
    "chars": 885,
    "preview": "namespace NoodleExtensions\n{\n    using static NoodleExtensions.Plugin;\n\n    public static class NoodleController\n    {\n"
  },
  {
    "path": "NoodleExtensions/NoodleExtensions.csproj",
    "chars": 3770,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>net48</TargetFramework>\n    <OutputType>Librar"
  },
  {
    "path": "NoodleExtensions/NoodleExtensionsExtensions.cs",
    "chars": 1052,
    "preview": "namespace NoodleExtensions\n{\n    using System;\n    using System.Collections.Generic;\n    using CustomJSONData.CustomBea"
  },
  {
    "path": "NoodleExtensions/NoodleObjectData.cs",
    "chars": 10557,
    "preview": "namespace NoodleExtensions\n{\n    using System;\n    using System.Collections.Generic;\n    using System.Linq;\n    using C"
  },
  {
    "path": "NoodleExtensions/Plugin.cs",
    "chars": 2959,
    "preview": "namespace NoodleExtensions\n{\n    using System.Reflection;\n    using HarmonyLib;\n    using Heck;\n    using IPA;\n    usin"
  },
  {
    "path": "NoodleExtensions/manifest.json",
    "chars": 478,
    "preview": "{\n  \"$schema\": \"https://raw.githubusercontent.com/bsmg/BSIPA-MetadataFileSchema/master/Schema.json\",\n  \"author\": \"Aerol"
  },
  {
    "path": "NoodleExtensions.sln",
    "chars": 4644,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.2902"
  },
  {
    "path": "README.md",
    "chars": 662,
    "preview": "**Noodle Extensions is now maintained in the [Aeroluna/Heck](https://github.com/Aeroluna/Heck) repository.**\n\n----\n\n# No"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the Aeroluna/NoodleExtensions GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 76 files (223.5 KB), approximately 52.3k tokens, and a symbol index with 249 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!