fix: Cleans up code with feeding pets and adds batch coin flips random check (#1717)

This commit is contained in:
Kamron Batman 2024-04-04 15:09:15 -07:00 committed by GitHub
parent 183f6fa4ac
commit 29ea813242
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 186 additions and 128 deletions

View file

@ -39,6 +39,9 @@ public static class BuiltInRng
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long Next(long minValue, long count) => minValue + Generator.NextInt64(count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long NextLong() => Generator.NextInt64();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double NextDouble() => Generator.NextDouble();

View file

@ -12,7 +12,6 @@ using System.Text;
using System.Xml;
using Server.Buffers;
using Server.Collections;
using Server.Logging;
using Server.Random;
using Server.Text;
@ -20,8 +19,6 @@ namespace Server;
public static class Utility
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(Utility));
private static Dictionary<IPAddress, IPAddress> _ipAddressTable;
private static SkillName[] _allSkills =
@ -679,6 +676,50 @@ public static class Utility
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool InUpdateRange(Point3D p1, Point3D p2) => InRange(p1, p2, 18);
// Optimized method for handling 50% random chances in succession up to a maximum
public static int CoinFlips(int amount, int maximum)
{
var heads = 0;
while (amount > 0)
{
// Range is 2^amount exclusively, maximum of 62 bits can be used
ulong num = amount >= 62
? (ulong)BuiltInRng.NextLong()
: (ulong)BuiltInRng.Next(1L << amount);
heads += BitOperations.PopCount(num);
if (heads >= maximum)
{
return maximum;
}
// 64 bits minus sign bit and exclusive maximum leaves 62 bits
amount -= 62;
}
return heads;
}
public static int CoinFlips(int amount)
{
var heads = 0;
while (amount > 0)
{
// Range is 2^amount exclusively, maximum of 62 bits can be used
ulong num = amount >= 62
? (ulong)BuiltInRng.NextLong()
: (ulong)BuiltInRng.Next(1L << amount);
heads += BitOperations.PopCount(num);
// 64 bits minus sign bit and exclusive maximum leaves 62 bits
amount -= 62;
}
return heads;
}
public static int Dice(int amount, int sides, int bonus)
{
if (amount <= 0 || sides <= 0)
@ -686,11 +727,19 @@ public static class Utility
return 0;
}
var total = 0;
int total;
for (var i = 0; i < amount; ++i)
if (sides == 2)
{
total += BuiltInRng.Next(1, sides);
total = CoinFlips(amount);
}
else
{
total = 0;
for (var i = 0; i < amount; ++i)
{
total += BuiltInRng.Next(1, sides);
}
}
return total + bonus;
@ -730,8 +779,9 @@ public static class Utility
do
{
var rand = Random(length);
if (!(list[rand] && (list[rand] = true)))
if (!list[rand])
{
list[rand] = true;
sampleList[i++] = source[rand];
}
} while (i < count);
@ -756,8 +806,9 @@ public static class Utility
do
{
var rand = Random(length);
if (!(list[rand] && (list[rand] = true)))
if (!list[rand])
{
list[rand] = true;
sampleList[i++] = source[rand];
}
} while (i < count);
@ -780,8 +831,9 @@ public static class Utility
do
{
var rand = Random(length);
if (!(list[rand] && (list[rand] = true)))
if (!list[rand])
{
list[rand] = true;
dest.Add(source[rand]);
}
} while (++i < count);

View file

@ -30,34 +30,38 @@ public enum ImageType
public class ImageTypeInfo
{
private static readonly ImageTypeInfo[] m_Table =
{
new(9734, typeof(Betrayer), 75, 45),
new(9735, typeof(Bogling), 75, 45),
new(9736, typeof(BogThing), 60, 47),
new(9615, typeof(Gazer), 75, 45),
new(9743, typeof(Beetle), 60, 55),
new(9667, typeof(GiantBlackWidow), 55, 52),
new(9657, typeof(Scorpion), 65, 47),
new(9758, typeof(JukaMage), 75, 45),
new(9759, typeof(JukaWarrior), 75, 45),
new(9636, typeof(Lich), 75, 45),
new(9756, typeof(MeerMage), 75, 45),
new(9757, typeof(MeerWarrior), 75, 45),
new(9638, typeof(Mongbat), 70, 50),
new(9639, typeof(Mummy), 75, 45),
new(9654, typeof(Pixie), 75, 45),
new(9747, typeof(PlagueBeast), 60, 45),
new(9750, typeof(SandVortex), 60, 43),
new(9614, typeof(StoneGargoyle), 75, 45),
new(9753, typeof(SwampDragon), 50, 55),
new(8448, typeof(Wisp), 75, 45),
new(9746, typeof(Juggernaut), 55, 38)
};
private static readonly ImageTypeInfo[] _table =
[
new ImageTypeInfo(9734, typeof(Betrayer), ImageType.Betrayer, 75, 45),
new ImageTypeInfo(9735, typeof(Bogling), ImageType.Bogling, 75, 45),
new ImageTypeInfo(9736, typeof(BogThing), ImageType.BogThing, 60, 47),
new ImageTypeInfo(9615, typeof(Gazer), ImageType.Gazer, 75, 45),
new ImageTypeInfo(9743, typeof(Beetle), ImageType.Beetle, 60, 55),
new ImageTypeInfo(9667, typeof(GiantBlackWidow), ImageType.GiantBlackWidow, 55, 52),
new ImageTypeInfo(9657, typeof(Scorpion), ImageType.Scorpion, 65, 47),
new ImageTypeInfo(9758, typeof(JukaMage), ImageType.JukaMage, 75, 45),
new ImageTypeInfo(9759, typeof(JukaWarrior), ImageType.JukaWarrior, 75, 45),
new ImageTypeInfo(9636, typeof(Lich), ImageType.Lich, 75, 45),
new ImageTypeInfo(9756, typeof(MeerMage), ImageType.MeerMage, 75, 45),
new ImageTypeInfo(9757, typeof(MeerWarrior), ImageType.MeerWarrior, 75, 45),
new ImageTypeInfo(9638, typeof(Mongbat), ImageType.Mongbat, 70, 50),
new ImageTypeInfo(9639, typeof(Mummy), ImageType.Mummy, 75, 45),
new ImageTypeInfo(9654, typeof(Pixie), ImageType.Pixie, 75, 45),
new ImageTypeInfo(9747, typeof(PlagueBeast), ImageType.PlagueBeast, 60, 45),
new ImageTypeInfo(9750, typeof(SandVortex), ImageType.SandVortex, 60, 43),
new ImageTypeInfo(9614, typeof(StoneGargoyle), ImageType.StoneGargoyle, 75, 45),
new ImageTypeInfo(9753, typeof(SwampDragon), ImageType.SwampDragon, 50, 55),
new ImageTypeInfo(8448, typeof(Wisp), ImageType.Wisp, 75, 45),
new ImageTypeInfo(9746, typeof(Juggernaut), ImageType.Juggernaut, 55, 38)
];
public ImageTypeInfo(int figurine, Type type, int x, int y)
// Used for sampling
private static readonly ImageTypeInfo[] _shuffleTable = (ImageTypeInfo[])_table.Clone();
public ImageTypeInfo(int figurine, Type type, ImageType image, int x, int y)
{
Figurine = figurine;
Image = image;
Type = type;
X = x;
Y = y;
@ -65,6 +69,8 @@ public class ImageTypeInfo
public int Figurine { get; }
public ImageType Image { get; }
public Type Type { get; }
public int Name => Figurine < 0x4000 ? 1020000 + Figurine : 1078872 + Figurine;
@ -74,7 +80,7 @@ public class ImageTypeInfo
public static ImageTypeInfo Get(ImageType image)
{
var index = (int)image;
return m_Table[index >= 0 && index < m_Table.Length ? index : 0];
return _table[index >= 0 && index < _table.Length ? index : 0];
}
public static ImageType[] RandomList(int count)
@ -84,21 +90,14 @@ public class ImageTypeInfo
return Array.Empty<ImageType>();
}
var length = m_Table.Length;
Span<bool> list = stackalloc bool[length];
list.Clear();
_shuffleTable.Shuffle();
var imageTypes = new ImageType[count];
var i = 0;
do
var minCount = Math.Min(count, _shuffleTable.Length);
for (var i = 0; i < minCount; i++)
{
var rand = Utility.Random(length);
if (!(list[rand] && (list[rand] = true)))
{
imageTypes[i++] = (ImageType)rand;
}
} while (i < count);
imageTypes[i] = _shuffleTable[i].Image;
}
return imageTypes;
}

View file

@ -72,7 +72,7 @@ public partial class DawnsMusicBox : Item, ISecurable
{ MusicName.ValoriaShips, new DawnsMusicInfo(1075140, DawnsMusicRarity.Rare) }
};
public static readonly MusicName[] _commonTracks =
private static readonly MusicName[] _commonTracks =
{
MusicName.Samlethe, MusicName.Sailing, MusicName.Britain2, MusicName.Britain1,
MusicName.Bucsden, MusicName.Forest_a, MusicName.Cove, MusicName.Death,
@ -86,7 +86,7 @@ public partial class DawnsMusicBox : Item, ISecurable
MusicName.Mountn_a, MusicName.Wind, MusicName.Yew, MusicName.Zento
};
public static readonly MusicName[] _uncommonTracks =
private static readonly MusicName[] _uncommonTracks =
{
MusicName.GwennoConversation, MusicName.DreadHornArea, MusicName.ElfCity,
MusicName.GoodEndGame, MusicName.GoodVsEvil, MusicName.GreatEarthSerpents,
@ -94,7 +94,7 @@ public partial class DawnsMusicBox : Item, ISecurable
MusicName.MinocNegative, MusicName.ParoxysmusLair, MusicName.Paws
};
public static readonly MusicName[] _rareTracks =
private static readonly MusicName[] _rareTracks =
{
MusicName.SelimsBar, MusicName.SerpentIsleCombat_U7, MusicName.ValoriaShips
};
@ -117,9 +117,16 @@ public partial class DawnsMusicBox : Item, ISecurable
{
Weight = 1.0;
_tracks = new List<MusicName>();
var shuffledTracks = GetTracks(DawnsMusicRarity.Common);
shuffledTracks.Shuffle();
GetTracks(DawnsMusicRarity.Common).RandomSample(4, _tracks);
_tracks =
[
shuffledTracks[0],
shuffledTracks[1],
shuffledTracks[2],
shuffledTracks[3]
];
}
public override int LabelNumber => 1075198; // Dawn's Music Box
@ -131,8 +138,7 @@ public partial class DawnsMusicBox : Item, ISecurable
return;
}
box.Tracks = new List<MusicName>();
box.Tracks.AddRange(Tracks);
box.Tracks = [..Tracks];
}
public override void GetProperties(IPropertyList list)
@ -257,7 +263,7 @@ public partial class DawnsMusicBox : Item, ISecurable
private void Deserialize(IGenericReader reader, int version)
{
var count = reader.ReadInt();
_tracks = new List<MusicName>();
_tracks = [];
for (var i = 0; i < count; i++)
{

View file

@ -173,6 +173,8 @@ namespace Server.Mobiles
}
public const int MaxLoyalty = 100;
public const int LoyaltyIncreasePerFood = 10;
public const int MaxLoyaltyIncrease = MaxLoyalty / LoyaltyIncreasePerFood;
public const int MaxOwners = 5;
@ -4180,91 +4182,87 @@ namespace Server.Mobiles
public virtual bool CheckFeed(Mobile from, Item dropped)
{
if (!IsDeadPet && Controlled && (ControlMaster == from || IsPetFriend(from)))
if (IsDeadPet || !Controlled || (ControlMaster != from && !IsPetFriend(from)))
{
if (CheckFoodPreference(dropped))
return false;
}
if (!CheckFoodPreference(dropped))
{
return false;
}
var amount = dropped.Amount;
if (amount > 0)
{
int stamGain = dropped switch
{
var amount = dropped.Amount;
Gold => amount - 50,
_ => amount * 15 - 50
};
if (amount > 0)
if (stamGain > 0)
{
Stam += stamGain;
// 64 food = 3,640 steps
StaminaSystem.RegenSteps(this as IHasSteps, stamGain * 4);
}
if (Core.SE)
{
m_Loyalty = MaxLoyalty;
}
else if (m_Loyalty < MaxLoyalty)
{
// 50% chance to increase 10 loyalty per food
m_Loyalty = Math.Min(MaxLoyalty, Utility.CoinFlips(amount, MaxLoyaltyIncrease) * 10);
}
/* if (happier )*/
// looks like in OSI pets say they are happier even if they are at maximum loyalty
SayTo(from, 502060); // Your pet looks happier.
if (Body.IsAnimal)
{
Animate(3, 5, 1, true, false, 0);
}
else if (Body.IsMonster)
{
Animate(17, 5, 1, true, false, 0);
}
if (IsBondable && !IsBonded)
{
var master = m_ControlMaster;
if (master != null && master == from) // So friends can't start the bonding process
{
int stamGain = dropped switch
if (MinTameSkill <= 29.1 || master.Skills.AnimalTaming.Base >= MinTameSkill ||
OverrideBondingReqs() ||
Core.ML && master.Skills.AnimalTaming.Value >= MinTameSkill)
{
Gold => amount - 50,
_ => amount * 15 - 50
};
if (stamGain > 0)
{
Stam += stamGain;
// 64 food = 3,640 steps
StaminaSystem.RegenSteps(this as IHasSteps, stamGain * 4);
}
if (Core.SE)
{
if (m_Loyalty < MaxLoyalty)
if (BondingBegin == DateTime.MinValue)
{
m_Loyalty = MaxLoyalty;
BondingBegin = Core.Now;
}
else if (BondingBegin + BondingDelay <= Core.Now)
{
IsBonded = true;
BondingBegin = DateTime.MinValue;
from.SendLocalizedMessage(1049666); // Your pet has bonded with you!
}
}
else
else if (Core.ML)
{
for (var i = 0; i < amount; ++i)
{
if (m_Loyalty < MaxLoyalty && Utility.RandomBool())
{
m_Loyalty += 10;
}
}
// Your pet cannot form a bond with you until your animal taming ability has risen.
from.SendLocalizedMessage(1075268);
}
/* if (happier )*/
// looks like in OSI pets say they are happier even if they are at maximum loyalty
SayTo(from, 502060); // Your pet looks happier.
if (Body.IsAnimal)
{
Animate(3, 5, 1, true, false, 0);
}
else if (Body.IsMonster)
{
Animate(17, 5, 1, true, false, 0);
}
if (IsBondable && !IsBonded)
{
var master = m_ControlMaster;
if (master != null && master == from) // So friends can't start the bonding process
{
if (MinTameSkill <= 29.1 || master.Skills.AnimalTaming.Base >= MinTameSkill ||
OverrideBondingReqs() ||
Core.ML && master.Skills.AnimalTaming.Value >= MinTameSkill)
{
if (BondingBegin == DateTime.MinValue)
{
BondingBegin = Core.Now;
}
else if (BondingBegin + BondingDelay <= Core.Now)
{
IsBonded = true;
BondingBegin = DateTime.MinValue;
from.SendLocalizedMessage(1049666); // Your pet has bonded with you!
}
}
else if (Core.ML)
{
// Your pet cannot form a bond with you until your animal taming ability has risen.
from.SendLocalizedMessage(1075268);
}
}
}
dropped.Delete();
return true;
}
}
dropped.Delete();
return true;
}
return false;