先修复一下,错误的场景 删除不必要的钓场资产 修复into场景 update:更新meta文件,修复报错 update:修复资源 修复第一章节建造 修复建造第三章 update:删除多余内容 update:更新README.md update:还原图标 update:更新README update:更新配置
351 lines
11 KiB
C#
351 lines
11 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using UnityRandom = UnityEngine.Random;
|
|
using SystemRandom = System.Random;
|
|
using UnityEngine;
|
|
using System;
|
|
using asap.core;
|
|
using cfg;
|
|
using UnityEngine.Assertions;
|
|
|
|
public static class FtUtils
|
|
{
|
|
/// <summary>
|
|
/// Split number into count pieces randomly.
|
|
/// </summary>
|
|
/// <param name="number">Number to split.</param>
|
|
/// <param name="count">Number of pieces.</param>
|
|
/// <param name="res">Result. Meaningless if result if false.</param>
|
|
/// <returns>False if fail to split.</returns>
|
|
public static bool RandomSplit(int number, int count, out List<int> res)
|
|
{
|
|
// res = new List<int>();
|
|
// if (number < count || number <= 0 || count <= 0)
|
|
// return false;
|
|
// if (number == count)
|
|
// {
|
|
// res = Enumerable.Repeat(1, count).ToList();
|
|
// return true;
|
|
// }
|
|
// var baseNumbers = new HashSet<int> { 0 };
|
|
// while (baseNumbers.Count < count)
|
|
// baseNumbers.Add(UnityRandom.Range(1, number - 1));
|
|
// var baseList = baseNumbers.OrderBy(e => e).ToList();
|
|
// for (int i = 1; i < baseList.Count; i++)
|
|
// res.Add(baseList[i] - baseList[i - 1]);
|
|
// res.Add(number - baseList[^1]);
|
|
// return res.Count == count;
|
|
|
|
res = null;
|
|
|
|
if (number < count || number <= 0 || count <= 0)
|
|
return false;
|
|
|
|
res = new List<int>(count);
|
|
|
|
// Step 1: initialize all to 1
|
|
for (int i = 0; i < count; i++)
|
|
res.Add(1);
|
|
|
|
int remaining = number - count;
|
|
|
|
// Step 2: distribute remaining randomly
|
|
for (int i = 0; i < remaining; i++)
|
|
{
|
|
int index = UnityRandom.Range(0, count);
|
|
res[index]++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool ScaleByBase(int num, int baseNum, out int res)
|
|
{
|
|
res = baseNum;
|
|
if (num <= 0 || baseNum <= 0)
|
|
return false;
|
|
if (num <= baseNum)
|
|
return true;
|
|
res = num / baseNum * baseNum;
|
|
return true;
|
|
}
|
|
|
|
public static int GetRandomIdxFromWeightList(IEnumerable<int> weightList)
|
|
{
|
|
// Check for null or empty list
|
|
if (weightList == null || !weightList.Any())
|
|
{
|
|
Debug.LogError("[GetRandomIdxFromWeightList]: Weight list is null or empty. Returning 0.");
|
|
return 0;
|
|
}
|
|
|
|
var accWeightList = new List<int>();
|
|
int tmp = 0;
|
|
foreach (int w in weightList)
|
|
{
|
|
// Check for negative weights
|
|
if (w < 0)
|
|
{
|
|
Debug.LogError($"[GetRandomIdxFromWeightList]: Negative weight detected: {w}. Returning 0.");
|
|
return 0;
|
|
}
|
|
|
|
tmp += w;
|
|
accWeightList.Add(tmp);
|
|
}
|
|
|
|
// Check for all zero weights
|
|
if (accWeightList[^1] <= 0)
|
|
{
|
|
Debug.LogError("[GetRandomIdxFromWeightList]: Total weight is zero or negative. Returning 0.");
|
|
return 0;
|
|
}
|
|
|
|
tmp = UnityRandom.Range(0, accWeightList[^1]);
|
|
for (int i = 0; i < accWeightList.Count; i++)
|
|
{
|
|
if (tmp < accWeightList[i])
|
|
return i;
|
|
}
|
|
return accWeightList.Count - 1;
|
|
}
|
|
|
|
public static int GetRandomIdxFromWeightList(IEnumerable<int> weightList, SystemRandom rng)
|
|
{
|
|
// Check for null or empty list
|
|
if (weightList == null || !weightList.Any())
|
|
{
|
|
Debug.LogError("[GetRandomIdxFromWeightList]: Weight list is null or empty. Returning 0.");
|
|
return 0;
|
|
}
|
|
|
|
var accWeightList = new List<int>();
|
|
int tmp = 0;
|
|
foreach (int w in weightList)
|
|
{
|
|
// Check for negative weights
|
|
if (w < 0)
|
|
{
|
|
Debug.LogError("[GetRandomIdxFromWeightList]: Negative weight detected. Returning 0.");
|
|
return 0;
|
|
}
|
|
|
|
tmp += w;
|
|
accWeightList.Add(tmp);
|
|
}
|
|
|
|
// Check for all zero weights
|
|
if (accWeightList[^1] <= 0)
|
|
{
|
|
Debug.LogError("[GetRandomIdxFromWeightList]: Total weight is zero or negative. Returning 0.");
|
|
return 0;
|
|
}
|
|
|
|
tmp = rng.Next(0, accWeightList[^1]);
|
|
for (int i = 0; i < accWeightList.Count; i++)
|
|
{
|
|
if (tmp < accWeightList[i])
|
|
return i;
|
|
}
|
|
return accWeightList.Count - 1;
|
|
}
|
|
|
|
public static int GetRandomIdxFromWeightList(IEnumerable<float> weightList, SystemRandom rng)
|
|
{
|
|
// Check for null or empty list
|
|
if (weightList == null || !weightList.Any())
|
|
{
|
|
Debug.LogError("[GetRandomIdxFromWeightList]: Weight list is null or empty. Returning 0.");
|
|
return 0;
|
|
}
|
|
|
|
var accWeightList = new List<float>();
|
|
double tmp = 0d;
|
|
foreach (float w in weightList)
|
|
{
|
|
// Check for negative weights
|
|
if (w < 0)
|
|
{
|
|
Debug.LogError("[GetRandomIdxFromWeightList]: Negative weight detected. Returning 0.");
|
|
return 0;
|
|
}
|
|
|
|
tmp += w;
|
|
accWeightList.Add((float)tmp);
|
|
}
|
|
|
|
// Check for all zero weights
|
|
if (accWeightList[^1] <= 0)
|
|
{
|
|
Debug.LogError("[GetRandomIdxFromWeightList]: Total weight is zero or negative. Returning 0.");
|
|
return 0;
|
|
}
|
|
|
|
tmp = rng.NextDouble() * accWeightList[^1];
|
|
for (int i = 0; i < accWeightList.Count; i++)
|
|
{
|
|
if (tmp < accWeightList[i])
|
|
return i;
|
|
}
|
|
return accWeightList.Count - 1;
|
|
}
|
|
|
|
public static T PickRandomItemWithWeight<T>(IEnumerable<T> items, SystemRandom rng, System.Func<int, float> weightGetter)
|
|
{
|
|
var weightList = items.Select((x, i) => weightGetter(i)).ToList();
|
|
return items.ElementAt(GetRandomIdxFromWeightList(weightList, rng));
|
|
}
|
|
|
|
public static T PickRandomItemWithWeight<T>(IEnumerable<T> items, System.Func<int, int> weightGetter)
|
|
{
|
|
var weightList = items.Select((x, i) => weightGetter(i)).ToList();
|
|
return items.ElementAt(GetRandomIdxFromWeightList(weightList));
|
|
}
|
|
|
|
public static Vector2 CalculateBezierCurve(float t, Vector2 p0, Vector2 p1, Vector2 p2)
|
|
{
|
|
if (t < 0 || t > 1)
|
|
return Vector2.zero;
|
|
Vector2 a = Vector2.Lerp(p0, p1, t);
|
|
Vector2 b = Vector2.Lerp(p1, p2, t);
|
|
return Vector2.Lerp(a, b, t);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// ("#,##0") Display number with comma, integer part only.
|
|
/// </summary>
|
|
/// <param name="num"></param>
|
|
/// <returns></returns>
|
|
public static string GetNumberString(float num)
|
|
{
|
|
string str;
|
|
if (num < 1000)
|
|
{
|
|
str = num.ToString("#,##0");
|
|
}
|
|
else if (num < 1000000)
|
|
{
|
|
str = (num / 1000f).ToString("#,##0") + "K";
|
|
}
|
|
else if (num < 1000000000)
|
|
{
|
|
str = (num / 1000000f).ToString("#,##0") + "M";
|
|
}
|
|
else
|
|
{
|
|
str = (num / 1000000000f).ToString("#,##0") + "G";
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
public static int[] BitWisePositionScan(int num)
|
|
{
|
|
var res = new List<int>();
|
|
int idx = 0;
|
|
while (num != 0)
|
|
{
|
|
if ((num & 1) == 1)
|
|
res.Add(idx);
|
|
num >>= 1;
|
|
idx++;
|
|
}
|
|
return res.ToArray();
|
|
}
|
|
|
|
public static void ShuffleList<T>(IList<T> list, System.Random rng)
|
|
{
|
|
for (int i = list.Count - 1; i > 0; i--)
|
|
{
|
|
int j = rng.Next(i + 1);
|
|
(list[j], list[i]) = (list[i], list[j]);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a random point in a corn-shaped spread.
|
|
/// </summary>
|
|
/// <param name="spreadHalfAngle">In degree.</param>
|
|
/// <param name="spreadNearRange">Close range.</param>
|
|
/// <param name="spreadFarRange">Far range.</param>
|
|
/// <param name="direction">In radians. Default is upward vertical, 90 degrees.</param>
|
|
/// <returns>A random delta</returns>
|
|
public static Vector2 GetCornSpread(float spreadHalfAngle, float spreadNearRange, float spreadFarRange, float direction = 90)
|
|
{
|
|
var halfAngleRadians = Mathf.Deg2Rad * spreadHalfAngle;
|
|
var angle = UnityRandom.Range(-halfAngleRadians, halfAngleRadians);
|
|
angle += Mathf.Deg2Rad * direction;
|
|
var radius = UnityRandom.Range(spreadNearRange, spreadFarRange);
|
|
return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * radius;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get Time limitation of Event from EventId. Can throw error, so either catch it or let it jam.
|
|
/// </summary>
|
|
/// <param name="eventId">Event id.</param>
|
|
/// <returns>Tuple, (startTime, endTime)</returns>
|
|
public static (DateTime startTime, DateTime endTime) GetEventTimeLimit(int eventId)
|
|
{
|
|
var res = GContext.container.Resolve<Tables>().TbFishingEvent.DataMap.TryGetValue(eventId, out var tableEvent);
|
|
Assert.IsTrue(res, $"[FtUtils] Event {eventId} not found in fishing event table.");
|
|
try
|
|
{
|
|
var startTime = DateTime.Parse((tableEvent.TimeDefinition as LimitedTime).StartTime);
|
|
var endTime = DateTime.Parse((tableEvent.TimeDefinition as LimitedTime).EndTime);
|
|
return (startTime, endTime);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
public static int RoundToInt(float num)
|
|
{
|
|
return (int)(num + 0.5f);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Counts the number of set bits (1s) in the binary representation of a positive integer.
|
|
/// </summary>
|
|
/// <param name="x">The integer to count set bits for. Must be non-negative.</param>
|
|
/// <returns>The number of set bits (1s) in the binary representation of x.</returns>
|
|
/// <exception cref="ArgumentException">Thrown when a negative number is passed.</exception>
|
|
public static int PopCount(int x)
|
|
{
|
|
if (x < 0)
|
|
{
|
|
throw new ArgumentException("PopCount does not support negative numbers", nameof(x));
|
|
}
|
|
|
|
int count = 0;
|
|
while (x != 0)
|
|
{
|
|
x &= x - 1;
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
public static List<GameCore.ItemData> ResolveChestContent(int chestItemId, float? inflationRate = null)
|
|
{
|
|
var res = new List<GameCore.ItemData>();
|
|
var item = GContext.container.Resolve<Tables>().TbItem.GetOrDefault(chestItemId);
|
|
if (item == null)
|
|
{
|
|
Debug.LogError($"[FtUtils] Item {chestItemId} not found in item table.");
|
|
return res;
|
|
}
|
|
if (item.Type != 9 || item.SubType != 7)
|
|
{
|
|
Debug.LogError($"[FtUtils] Item {chestItemId} is not a grand reward chest item.");
|
|
return res;
|
|
}
|
|
res = GContext.container.Resolve<GameCore.PlayerItemData>().GetItemDropItemList(item.RedirectID, 1);
|
|
GContext.container.Resolve<GameCore.PlayerItemData>().LureInflation(res, inflationRate);
|
|
return res;
|
|
}
|
|
|
|
}
|