fix: Cleans up code with feeding pets and adds batch coin flips random check (#1717)
This commit is contained in:
parent
183f6fa4ac
commit
29ea813242
5 changed files with 186 additions and 128 deletions
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue