-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2be858e
commit 19a20b9
Showing
1 changed file
with
365 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,365 @@ | ||
using BepInEx; | ||
using BepInEx.Configuration; | ||
using BoplFixedMath; | ||
using HarmonyLib; | ||
using System.Reflection; | ||
using UnityEngine; | ||
using Steamworks; | ||
using Steamworks.Data; | ||
using UnityEngine.SceneManagement; | ||
using System; | ||
using System.Text.RegularExpressions; | ||
using Random = UnityEngine.Random; | ||
|
||
namespace MapRandomiser | ||
{ | ||
[BepInPlugin("com.MLT.MapRandomiser", "MapRandomiser", "1.0.0")] | ||
public class Plugin : BaseUnityPlugin | ||
{ | ||
public static GameObject PlatformAbility; | ||
public static Transform levelt; | ||
public static StickyRoundedRectangle platformPrefab; | ||
public static int CurrentMapId; | ||
//jaged array of arrays with 2 arrays of 2 Fixes. the 2 arrays have 2 coradanents that make a rectatgle. | ||
//no platforms are allowed to be inside this rectangle | ||
public static Fix[][][] SpaceTakenUp; | ||
//the minimum distance betreen platforms allowed in bopl units. Default: 2 | ||
public static ConfigEntry<double> min_dist; | ||
//how many platforms it will attempt to place. if it fails there will be a chance it attempts to place agien. Default: 30 | ||
public static ConfigEntry<int> PlatPlaceAttempts; | ||
//chance of attempting to place a platform agien if it fails. with a 1/x chance where x is this value. Default: 3 | ||
public static ConfigEntry<double> ChanceYouAttemptToPlaceAPlatformAgien; | ||
//circle platform chance as 1/x where x is this value. Default: 5 | ||
public static ConfigEntry<double> CirclePlatformChance; | ||
//min and max posisons platforms can be placed | ||
//-97.27 | ||
public static Fix MinX = (Fix) (-97.27); | ||
//-26 | ||
public static Fix MinY = (Fix) (-26); | ||
//97.6 | ||
public static Fix MaxX = (Fix)97.6; | ||
//40 | ||
public static Fix MaxY = (Fix)40; | ||
//min and max sizes | ||
//0.25 | ||
public static ConfigEntry<double> MinScaleX; | ||
//5 | ||
public static ConfigEntry<double> MaxScaleX; | ||
//0.25 | ||
public static ConfigEntry<double> MinScaleY; | ||
//5 | ||
public static ConfigEntry<double> MaxScaleY; | ||
//min and max Radius | ||
//0.1 | ||
public static ConfigEntry<double> MinRadius; | ||
//5 | ||
public static ConfigEntry<double> MaxRadius; | ||
//rotatson? | ||
public static ConfigEntry<bool> Rotate; | ||
//random colors | ||
public static ConfigEntry<bool> RandomColors; | ||
//mass for 1x1 block. | ||
public static ConfigEntry<double> OneByOneBlockMass; | ||
public static ConfigFile config; | ||
|
||
private void Awake() | ||
{ | ||
Logger.LogInfo("MapRandomiser Has been loaded"); | ||
Harmony harmony = new Harmony("com.MLT.MapRandomiser"); | ||
|
||
Logger.LogInfo("Harmony harmony = new Harmony -- Melon, 2024"); | ||
harmony.PatchAll(); // Patch Harmony | ||
Logger.LogInfo("MapRandomiser Patch Compleate!"); | ||
|
||
SceneManager.sceneLoaded += OnSceneLoaded; | ||
config = Config; | ||
|
||
min_dist = config.Bind("Settings", "Minimum distance", 2d, "the minimum distance betreen platforms allowed in bopl units. below 2 there is a chance you could be squished on spawn."); | ||
PlatPlaceAttempts = config.Bind("Settings", "Platform Place Attempts", 30, "how many platforms it will attempt to place. if it fails there will be a chance it attempts to place agien."); | ||
ChanceYouAttemptToPlaceAPlatformAgien = config.Bind("Chances", "Chance It Attempts To Place A Platform Agien", 2d, "chance of attempting to place a platform agien if it fails. with a 1/x chance where x is this value."); | ||
CirclePlatformChance = config.Bind("Chances", "Circle Platform Chance", 5d, "chance a platform is a circle platform as 1/x where x is this value."); | ||
MinScaleX = config.Bind("Width", "Minimum Width", 0.25d, "Minimum Platform Width in bopl units"); | ||
MaxScaleX = config.Bind("Width", "Maximum Width", 5d, "Maximum Platform Width in bopl units"); | ||
MinScaleY = config.Bind("Height", "Minimum Height", 0.25d, "Minimum Platform Height in bopl units"); | ||
MaxScaleY = config.Bind("Height", "Maximum Height", 5d, "Maximum Platform Height in bopl units"); | ||
MinRadius = config.Bind("Radius", "Minimum Radius", 0.25d, "Minimum Platform Radius in bopl units"); | ||
MaxRadius = config.Bind("Radius", "Maximum Radius", 5d, "Maximum Platform Radius in bopl units"); | ||
Rotate = config.Bind("Rotate", "Rotate", false, "can platforms spawn rotated. IN BETA. MAY SPAWN SQUISHED!"); | ||
RandomColors = config.Bind("Colors", "Random Colors", true, "Use Random Colors (note that this is the one value you dont need everyone to have the same value for.)"); | ||
OneByOneBlockMass = config.Bind("Mass", "1 by 1 block mass", 1d, "the mass of a platform that is 1 by 1 units"); | ||
} | ||
|
||
private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) | ||
{ | ||
Debug.Log("OnSceneLoaded: " + scene.name); | ||
if (IsLevelName(scene.name)) | ||
{ | ||
CurrentMapId = GetMapIdFromSceneName(scene.name); | ||
//get the platform prefab out of the Platform ability gameobject (david) DO NOT REMOVE! | ||
//chatgpt code to get the Platform ability object | ||
GameObject[] allObjects = Resources.FindObjectsOfTypeAll(typeof(GameObject)) as GameObject[]; | ||
Debug.Log("getting platform object"); | ||
foreach (GameObject obj in allObjects) | ||
{ | ||
if (obj.name == "Platform") | ||
{ | ||
// Found the object with the desired name and HideAndDontSave flag | ||
// You can now store its reference or perform any other actions | ||
PlatformAbility = obj; | ||
Debug.Log("Found the object: " + obj.name); | ||
break; | ||
} | ||
} | ||
var platformTransform = PlatformAbility.GetComponent(typeof(PlatformTransform)) as PlatformTransform; | ||
platformPrefab = platformTransform.platformPrefab; | ||
//find the platforms and remove them (shadow + david) | ||
levelt = GameObject.Find("Level").transform; | ||
foreach (Transform tplatform in levelt) | ||
{ | ||
Updater.DestroyFix(tplatform.gameObject); | ||
} | ||
//step 1: spawn platforms under the players and add a no-platform rectange to the list\ | ||
//step 1.1: reset the list of bad spawn spots | ||
SpaceTakenUp = Array.Empty <Fix[][]>(); | ||
//step 1.5 get the player spawn points | ||
var PlayerList = GameObject.Find("PlayerList"); | ||
//handle level with id 5 (scene name Level6) and posably outers | ||
if (PlayerList == null) | ||
{ | ||
PlayerList = GameObject.Find("PlayerList (1)"); | ||
} | ||
var PlayerSpawns = PlayerList.GetComponent<GameSessionHandler>().teamSpawns; | ||
foreach (Vec2 SpawnPoint in PlayerSpawns) | ||
{ | ||
SpawnPlatform(SpawnPoint.x, SpawnPoint.y-(Fix)3, (Fix)2, (Fix)2, (Fix)1, (Fix)0); | ||
} | ||
|
||
//step 2: try to spawn PlatPlaceAttempts platforms. not spawning them if they overlap with a rectangle in the SpaceTakenUp array | ||
for (int i = 0; i < PlatPlaceAttempts.Value; i++) | ||
{ | ||
AttemptToSpawnRandomPlatform(); | ||
} | ||
//testing | ||
/*for (int x = -97; x < 97; x++) | ||
{ | ||
for (int y = -26; y < 40; y++) | ||
{ | ||
if (IsValidPlacement((Fix)x, (Fix)y, (Fix)3, (Fix)1, (Fix)5, (Fix)0)) | ||
{ | ||
SpawnPlatform((Fix)x, (Fix)y, (Fix)3, (Fix)1, (Fix)5, (Fix)0); | ||
} | ||
} | ||
} | ||
*/ | ||
//log SpaceTakenUp | ||
/*Debug.Log("SpaceTakenUp:"); | ||
foreach (var layer in SpaceTakenUp) | ||
{ | ||
foreach (var point in layer) | ||
{ | ||
Debug.Log($"[{point[0]}, {point[1]}]"); | ||
} | ||
} | ||
*/ | ||
} | ||
} | ||
|
||
public static void SpawnPlatform(Fix X, Fix Y, Fix Width, Fix Height, Fix Radius, Fix rotatson) | ||
{ | ||
// Spawn platform (david) | ||
var StickyRect = FixTransform.InstantiateFixed<StickyRoundedRectangle>(platformPrefab, new Vec2(X, Y)); | ||
StickyRect.rr.Scale = Fix.One; | ||
var platform = StickyRect.GetComponent<ResizablePlatform>(); | ||
platform.GetComponent<DPhysicsRoundedRect>().ManualInit(); | ||
ResizePlatform(platform, Width, Height, Radius); | ||
//in radiens | ||
StickyRect.GetGroundBody().up = new Vec2(rotatson); | ||
//random color | ||
if (RandomColors.Value) | ||
{ | ||
StickyRect.GetComponent<SpriteRenderer>().color = new UnityEngine.Color(Random.Range((float)0, (float)1), Random.Range((float)0, (float)1), Random.Range((float)0, (float)1)); | ||
} | ||
// set the mass | ||
AccessTools.Field(typeof(BoplBody), "mass").SetValue(StickyRect.GetGroundBody(), CalculateMassOfPlatform(Width, Height, Radius)); | ||
Debug.Log("Set mass to " + (double)CalculateMassOfPlatform(Width, Height, Radius)); | ||
// log (melon) | ||
Debug.Log("Spawned platform at position (" + X + ", " + Y + ") with dimensions (" + Width + ", " + Height + ") and radius " + Radius); | ||
//calculate the true Width and Hight including the rotatson | ||
|
||
Fix[] WidthAndHight = TrueWidthAndHight(Width, Height, Radius, rotatson); | ||
var WidthWithRotatson = WidthAndHight[0]; | ||
var HeightWithRotatson = WidthAndHight[1]; | ||
// add to list (david) | ||
//Debug.Log("WidthWithRotatson: " + WidthWithRotatson + " HightWithRotatson: " + HeightWithRotatson); | ||
var x1 = X - (WidthWithRotatson / (Fix)2) - (Fix)min_dist.Value; | ||
var y1 = Y - (HeightWithRotatson / (Fix)2) - (Fix)min_dist.Value; | ||
var x2 = X + (WidthWithRotatson / (Fix)2) + (Fix)min_dist.Value; | ||
var y2 = Y + (HeightWithRotatson / (Fix)2) + (Fix)min_dist.Value; | ||
|
||
//append them | ||
Fix[] point1 = { x1, y1 }; | ||
Fix[] point2 = { x2, y2 }; | ||
Fix[][] rect = { point1, point2 }; | ||
SpaceTakenUp = AppendRect(rect); | ||
/* | ||
Debug.Log("SpaceTakenUp:"); | ||
foreach (var point in rect) | ||
{ | ||
Debug.Log($"[{point[0]}, {point[1]}]"); | ||
} | ||
*/ | ||
} | ||
|
||
public static void Update() | ||
{ | ||
//ignore this | ||
} | ||
|
||
//this can be called anytime the object is active. this means you can have animated levels with shape changing platforms | ||
public static void ResizePlatform(ResizablePlatform platform, Fix newWidth, Fix newHeight, Fix newRadius) | ||
{ | ||
// ok so aparently the Height and Width need to be switched for some reson? idk. this took 3 hours to figger out... | ||
platform.ResizePlatform(newWidth, newHeight, newRadius, true); | ||
} | ||
public static bool IsLevelName(String input) | ||
{ | ||
Regex regex = new Regex("Level[0-9]+", RegexOptions.IgnoreCase); | ||
return regex.IsMatch(input); | ||
} | ||
//https://stormconsultancy.co.uk/blog/storm-news/convert-an-angle-in-degrees-to-radians-in-c/ | ||
public static double ConvertToRadians(double angle) | ||
{ | ||
return (Math.PI / 180) * angle; | ||
} | ||
//https://stackoverflow.com/questions/19167669/keep-only-numeric-value-from-a-string | ||
// simply replace the offending substrings with an empty string | ||
public static int GetMapIdFromSceneName(string s) | ||
{ | ||
Regex rxNonDigits = new Regex(@"[^\d]+"); | ||
if (string.IsNullOrEmpty(s)) return 0; | ||
string cleaned = rxNonDigits.Replace(s, ""); | ||
//subtract 1 as scene names start with 1 but ids start with 0 | ||
return int.Parse(cleaned)-1; | ||
} | ||
//chatgpt code | ||
public static Fix[][][] AppendRect(Fix[][] rect) | ||
{ | ||
// Resize array to accommodate the new element | ||
Array.Resize(ref SpaceTakenUp, SpaceTakenUp.Length + 1); | ||
SpaceTakenUp[SpaceTakenUp.Length - 1] = rect; | ||
return SpaceTakenUp; | ||
} | ||
public static Fix[] TrueWidthAndHight(Fix Width, Fix Height, Fix Radius, Fix rotatson) | ||
{ | ||
//convert to double so i can use Math operators instead of the Fix ones | ||
var Width2 = (double)Width; | ||
var Height2 = (double)Height; | ||
var rotatson2 = (double)rotatson; | ||
//calculate the Width And Height with the rotatson | ||
Fix WidthWithRotatson = (Fix)Math.Max(Math.Sqrt(Math.Pow(Width2, 2) + Math.Pow(Height2, 2)) * Math.Cos(rotatson2 + Math.Atan2(Height2, Width2)), Math.Sqrt(Math.Pow(Width2, 2) + Math.Pow(Height2, 2)) * Math.Cos(rotatson2 - Math.Atan2(Height2, Width2))); | ||
Fix HeightWithRotatson = (Fix)Math.Max(Math.Sqrt(Math.Pow(Width2, 2) + Math.Pow(Height2, 2)) * Math.Sin(rotatson2 + Math.Atan2(Height2, Width2)), Math.Sqrt(Math.Pow(Width2, 2) + Math.Pow(Height2, 2)) * Math.Sin(rotatson2 - Math.Atan2(Height2, Width2))); | ||
//Fix WidthWithRotatson = (Fix)(Width2 * Math.Sin(rotatson2) + Height2 * Math.Cos(rotatson2)); | ||
//Fix HeightWithRotatson = (Fix)(Width2 * Math.Cos(rotatson2) + Height2 * Math.Sin(rotatson2)); | ||
//add the radius as its additive | ||
WidthWithRotatson = WidthWithRotatson + Radius; | ||
HeightWithRotatson = HeightWithRotatson + Radius; | ||
//multiply by 2 as width and height is like radius and is distance from the center | ||
Fix[] returnArray = {WidthWithRotatson * (Fix)2, HeightWithRotatson * (Fix)2}; | ||
return returnArray; | ||
} | ||
public static bool IsValidPlacement(Fix X, Fix Y, Fix Width, Fix Height, Fix Radius, Fix rotatson) | ||
{ | ||
Fix[] WidthAndHight = TrueWidthAndHight(Width, Height, Radius, rotatson); | ||
var WidthWithRotatson = WidthAndHight[0]; | ||
var HeightWithRotatson = WidthAndHight[1]; | ||
//get the bounding box | ||
var BottomLeftX = X - (WidthWithRotatson / (Fix)2); | ||
var BottomLeftY = Y - (HeightWithRotatson / (Fix)2); | ||
var TopRightX = X + (WidthWithRotatson / (Fix)2); | ||
var TopRightY = Y + (HeightWithRotatson / (Fix)2); | ||
foreach (var rect in SpaceTakenUp) | ||
{ | ||
//split the bounding box up so we can check it easyer | ||
var BottomLeftX2 = rect[0][0]; | ||
var BottomLeftY2 = rect[0][1]; | ||
var TopRightX2 = rect[1][0]; | ||
var TopRightY2 = rect[1][1]; | ||
//https://www.geeksforgeeks.org/intersecting-rectangle-when-bottom-left-and-top-right-corners-of-two-rectangles-are-given/ | ||
// gives bottom-left point | ||
// of intersection rectangle | ||
Fix x5 = Fix.Max(BottomLeftX, BottomLeftX2); | ||
Fix y5 = Fix.Max(BottomLeftY, BottomLeftY2); | ||
|
||
// gives top-right point | ||
// of intersection rectangle | ||
Fix x6 = Fix.Min(TopRightX, TopRightX2); | ||
Fix y6 = Fix.Min(TopRightY, TopRightY2); | ||
|
||
// no intersection | ||
if (!(x5 > x6 || y5 > y6)) | ||
{ | ||
//Debug.Log("failed to spawn platform due to box {{" + BottomLeftX + ", " + BottomLeftY + "}, {" + TopRightX + ", " + TopRightY + "}} and box {{" + BottomLeftX2 + ", " + BottomLeftY2 + "}, {" + TopRightX2 + ", " + TopRightY2 + "}} colliding"); | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
public static void AttemptToSpawnRandomPlatform() | ||
{ | ||
//generate the paramiters | ||
var Xpos = Updater.RandomFix(MinX, MaxX); | ||
var Ypos = Updater.RandomFix(MinY, MaxY); | ||
Fix Width = Fix.Zero; | ||
Fix Height = Fix.Zero; | ||
//if we are not doing a circle platform | ||
if (Updater.RandomFix(Fix.Zero, (Fix)CirclePlatformChance.Value) > 1) | ||
{ | ||
Width = Updater.RandomFix((Fix)MinScaleX.Value, (Fix)MaxScaleX.Value); | ||
Height = Updater.RandomFix((Fix)MinScaleY.Value, (Fix)MaxScaleY.Value); | ||
} | ||
//if its a circle platform | ||
else | ||
{ | ||
Width = (Fix)0.05; | ||
Height = (Fix)0.05; | ||
} | ||
var Radius = Updater.RandomFix((Fix)MinRadius.Value, (Fix)MaxRadius.Value); | ||
var Rot = Fix.Zero; | ||
if (Rotate.Value) | ||
{ | ||
//because rotatson is in radiens we make it random from 0 to Pi*2 also known as Tau | ||
Rot = Updater.RandomFix(Fix.Zero, Fix.PiTimes2); | ||
} | ||
//check if its valid and if so spawn it | ||
if (IsValidPlacement(Xpos, Ypos, Width, Height, Radius, Rot)) | ||
{ | ||
SpawnPlatform(Xpos, Ypos, Width, Height, Radius, Rot); | ||
//Debug.Log("spawned platform"); | ||
} | ||
else | ||
{ | ||
if (Updater.RandomFix(Fix.Zero, (Fix)ChanceYouAttemptToPlaceAPlatformAgien.Value) < (Fix)1) | ||
{ | ||
//if its lucky it gets a chance at a new life | ||
AttemptToSpawnRandomPlatform(); | ||
} | ||
} | ||
} | ||
public static Fix CalculateMassOfPlatform(Fix Width, Fix Height, Fix Radius) | ||
{ | ||
//multiply by 2 because Width and Height are just distances from the center | ||
var TrueWidth = Width * (Fix)2 + Radius; | ||
var TrueHeight = Height * (Fix)2 + Radius; | ||
var Area = TrueWidth * TrueHeight; | ||
//if it is a circle | ||
if (Width == (Fix)0.05 && Height == (Fix)0.05) | ||
{ | ||
//A=Pi*R^2 | ||
//there is no exsponent for Fixes | ||
Area = Fix.Pi * Radius * Radius; | ||
} | ||
return Area * (Fix)OneByOneBlockMass.Value; | ||
|
||
} | ||
} | ||
} |