Reorganizes Project (#41)

This commit is contained in:
Kamron Batman 2019-08-02 18:13:40 -07:00 committed by GitHub
parent 08bf44af9a
commit 3614a66aee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3499 changed files with 79 additions and 55 deletions

View file

@ -0,0 +1,121 @@
using Server.Gumps;
using Server.Network;
namespace Server.Engines.Plants
{
public class EmptyTheBowlGump : Gump
{
private PlantItem m_Plant;
public EmptyTheBowlGump(PlantItem plant) : base(20, 20)
{
m_Plant = plant;
DrawBackground();
AddLabel(90, 70, 0x44, "Empty the bowl?");
DrawPicture();
AddButton(98, 150, 0x47E, 0x480, 1); // Cancel
AddButton(138, 151, 0xD2, 0xD2, 2); // Help
AddLabel(143, 151, 0x835, "?");
AddButton(168, 150, 0x481, 0x483, 3); // Ok
}
private void DrawBackground()
{
AddBackground(50, 50, 200, 150, 0xE10);
AddItem(45, 45, 0xCEF);
AddItem(45, 118, 0xCF0);
AddItem(211, 45, 0xCEB);
AddItem(211, 118, 0xCEC);
}
private void DrawPicture()
{
AddItem(90, 100, 0x1602);
AddImage(140, 102, 0x15E1);
AddItem(160, 100, 0x15FD);
if (m_Plant.PlantStatus != PlantStatus.BowlOfDirt && m_Plant.PlantStatus < PlantStatus.Plant)
AddItem(156, 130, 0xDCF); // Seed
}
public override void OnResponse(NetState sender, RelayInfo info)
{
Mobile from = sender.Mobile;
if (info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus >= PlantStatus.DecorativePlant)
return;
if (info.ButtonID == 3 && !from.InRange(m_Plant.GetWorldLocation(), 3))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away.
return;
}
if (!m_Plant.IsUsableBy(from))
{
m_Plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it.
return;
}
switch (info.ButtonID)
{
case 1: // Cancel
{
from.SendGump(new MainPlantGump(m_Plant));
break;
}
case 2: // Help
{
from.Send(new DisplayHelpTopic(71, true)); // EMPTYING THE BOWL
from.SendGump(new EmptyTheBowlGump(m_Plant));
break;
}
case 3: // Ok
{
PlantBowl bowl = new PlantBowl();
if (!from.PlaceInBackpack(bowl))
{
bowl.Delete();
m_Plant.LabelTo(from, 1053047); // You cannot empty a bowl with a full pack!
from.SendGump(new MainPlantGump(m_Plant));
break;
}
if (m_Plant.PlantStatus != PlantStatus.BowlOfDirt && m_Plant.PlantStatus < PlantStatus.Plant)
{
Seed seed = new Seed(m_Plant.PlantType, m_Plant.PlantHue, m_Plant.ShowType);
if (!from.PlaceInBackpack(seed))
{
bowl.Delete();
seed.Delete();
m_Plant.LabelTo(from, 1053047); // You cannot empty a bowl with a full pack!
from.SendGump(new MainPlantGump(m_Plant));
break;
}
}
m_Plant.Delete();
break;
}
}
}
}
}

View file

@ -0,0 +1,425 @@
using System;
using Server.Gumps;
using Server.Items;
using Server.Network;
namespace Server.Engines.Plants
{
public class MainPlantGump : Gump
{
private PlantItem m_Plant;
public MainPlantGump(PlantItem plant) : base(20, 20)
{
m_Plant = plant;
DrawBackground();
DrawPlant();
AddButton(71, 67, 0xD4, 0xD4, 1); // Reproduction menu
AddItem(59, 68, 0xD08);
PlantSystem system = plant.PlantSystem;
AddButton(71, 91, 0xD4, 0xD4, 2); // Infestation
AddItem(8, 96, 0x372);
AddPlus(95, 92, system.Infestation);
AddButton(71, 115, 0xD4, 0xD4, 3); // Fungus
AddItem(58, 115, 0xD16);
AddPlus(95, 116, system.Fungus);
AddButton(71, 139, 0xD4, 0xD4, 4); // Poison
AddItem(59, 143, 0x1AE4);
AddPlus(95, 140, system.Poison);
AddButton(71, 163, 0xD4, 0xD4, 5); // Disease
AddItem(55, 167, 0x1727);
AddPlus(95, 164, system.Disease);
AddButton(209, 67, 0xD2, 0xD2, 6); // Water
AddItem(193, 67, 0x1F9D);
AddPlusMinus(196, 67, system.Water);
AddButton(209, 91, 0xD4, 0xD4, 7); // Poison potion
AddItem(201, 91, 0xF0A);
AddLevel(196, 91, system.PoisonPotion);
AddButton(209, 115, 0xD4, 0xD4, 8); // Cure potion
AddItem(201, 115, 0xF07);
AddLevel(196, 115, system.CurePotion);
AddButton(209, 139, 0xD4, 0xD4, 9); // Heal potion
AddItem(201, 139, 0xF0C);
AddLevel(196, 139, system.HealPotion);
AddButton(209, 163, 0xD4, 0xD4, 10); // Strength potion
AddItem(201, 163, 0xF09);
AddLevel(196, 163, system.StrengthPotion);
AddImage(48, 47, 0xD2);
AddLevel(54, 47, (int)m_Plant.PlantStatus);
AddImage(232, 47, 0xD2);
AddGrowthIndicator(239, 47);
AddButton(48, 183, 0xD2, 0xD2, 11); // Help
AddLabel(54, 183, 0x835, "?");
AddButton(232, 183, 0xD4, 0xD4, 12); // Empty the bowl
AddItem(219, 180, 0x15FD);
}
private void DrawBackground()
{
AddBackground(50, 50, 200, 150, 0xE10);
AddItem(45, 45, 0xCEF);
AddItem(45, 118, 0xCF0);
AddItem(211, 45, 0xCEB);
AddItem(211, 118, 0xCEC);
}
private void DrawPlant()
{
PlantStatus status = m_Plant.PlantStatus;
if (status < PlantStatus.FullGrownPlant)
{
AddImage(110, 85, 0x589);
AddItem(122, 94, 0x914);
AddItem(135, 94, 0x914);
AddItem(120, 112, 0x914);
AddItem(135, 112, 0x914);
if (status >= PlantStatus.Stage2) AddItem(127, 112, 0xC62);
if (status == PlantStatus.Stage3 || status == PlantStatus.Stage4) AddItem(129, 85, 0xC7E);
if (status >= PlantStatus.Stage4)
{
AddItem(121, 117, 0xC62);
AddItem(133, 117, 0xC62);
}
if (status >= PlantStatus.Stage5)
{
AddItem(110, 100, 0xC62);
AddItem(140, 100, 0xC62);
AddItem(110, 130, 0xC62);
AddItem(140, 130, 0xC62);
}
if (status >= PlantStatus.Stage6)
{
AddItem(105, 115, 0xC62);
AddItem(145, 115, 0xC62);
AddItem(125, 90, 0xC62);
AddItem(125, 135, 0xC62);
}
}
else
{
PlantTypeInfo typeInfo = PlantTypeInfo.GetInfo(m_Plant.PlantType);
PlantHueInfo hueInfo = PlantHueInfo.GetInfo(m_Plant.PlantHue);
// The large images for these trees trigger a client crash, so use a smaller, generic tree.
if (m_Plant.PlantType == PlantType.CypressTwisted || m_Plant.PlantType == PlantType.CypressStraight)
AddItem(130 + typeInfo.OffsetX, 96 + typeInfo.OffsetY, 0x0CCA, hueInfo.Hue);
else
AddItem(130 + typeInfo.OffsetX, 96 + typeInfo.OffsetY, typeInfo.ItemID, hueInfo.Hue);
}
if (status != PlantStatus.BowlOfDirt)
{
int message = m_Plant.PlantSystem.GetLocalizedHealth();
switch (m_Plant.PlantSystem.Health)
{
case PlantHealth.Dying:
{
AddItem(92, 167, 0x1B9D);
AddItem(161, 167, 0x1B9D);
AddHtmlLocalized(136, 167, 42, 20, message, 0x00FC00);
break;
}
case PlantHealth.Wilted:
{
AddItem(91, 164, 0x18E6);
AddItem(161, 164, 0x18E6);
AddHtmlLocalized(132, 167, 42, 20, message, 0x00C207);
break;
}
case PlantHealth.Healthy:
{
AddItem(96, 168, 0xC61);
AddItem(162, 168, 0xC61);
AddHtmlLocalized(129, 167, 42, 20, message, 0x008200);
break;
}
case PlantHealth.Vibrant:
{
AddItem(93, 162, 0x1A99);
AddItem(162, 162, 0x1A99);
AddHtmlLocalized(129, 167, 42, 20, message, 0x0083E0);
break;
}
}
}
}
private void AddPlus(int x, int y, int value)
{
switch (value)
{
case 1:
AddLabel(x, y, 0x35, "+");
break;
case 2:
AddLabel(x, y, 0x21, "+");
break;
}
}
private void AddPlusMinus(int x, int y, int value)
{
switch (value)
{
case 0:
AddLabel(x, y, 0x21, "-");
break;
case 1:
AddLabel(x, y, 0x35, "-");
break;
case 3:
AddLabel(x, y, 0x35, "+");
break;
case 4:
AddLabel(x, y, 0x21, "+");
break;
}
}
private void AddLevel(int x, int y, int value)
{
AddLabel(x, y, 0x835, value.ToString());
}
private void AddGrowthIndicator(int x, int y)
{
if (!m_Plant.IsGrowable)
return;
switch (m_Plant.PlantSystem.GrowthIndicator)
{
case PlantGrowthIndicator.InvalidLocation:
AddLabel(x, y, 0x21, "!");
break;
case PlantGrowthIndicator.NotHealthy:
AddLabel(x, y, 0x21, "-");
break;
case PlantGrowthIndicator.Delay:
AddLabel(x, y, 0x35, "-");
break;
case PlantGrowthIndicator.Grown:
AddLabel(x, y, 0x3, "+");
break;
case PlantGrowthIndicator.DoubleGrown:
AddLabel(x, y, 0x3F, "+");
break;
}
}
public override void OnResponse(NetState sender, RelayInfo info)
{
Mobile from = sender.Mobile;
if (info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus >= PlantStatus.DecorativePlant)
return;
if ((info.ButtonID >= 6 && info.ButtonID <= 10 || info.ButtonID == 12) &&
!from.InRange(m_Plant.GetWorldLocation(), 3))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away.
return;
}
if (!m_Plant.IsUsableBy(from))
{
m_Plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it.
return;
}
switch (info.ButtonID)
{
case 1: // Reproduction menu
{
if (m_Plant.PlantStatus > PlantStatus.BowlOfDirt)
{
from.SendGump(new ReproductionGump(m_Plant));
}
else
{
from.SendLocalizedMessage(1061885); // You need to plant a seed in the bowl first.
from.SendGump(new MainPlantGump(m_Plant));
}
break;
}
case 2: // Infestation
{
from.Send(new DisplayHelpTopic(54, true)); // INFESTATION LEVEL
from.SendGump(new MainPlantGump(m_Plant));
break;
}
case 3: // Fungus
{
from.Send(new DisplayHelpTopic(56, true)); // FUNGUS LEVEL
from.SendGump(new MainPlantGump(m_Plant));
break;
}
case 4: // Poison
{
from.Send(new DisplayHelpTopic(58, true)); // POISON LEVEL
from.SendGump(new MainPlantGump(m_Plant));
break;
}
case 5: // Disease
{
from.Send(new DisplayHelpTopic(60, true)); // DISEASE LEVEL
from.SendGump(new MainPlantGump(m_Plant));
break;
}
case 6: // Water
{
BaseBeverage bev = from.Backpack.FindItemsByType<BaseBeverage>().Find(beverage =>
beverage.IsEmpty && beverage.Pourable && beverage.Content == BeverageType.Water);
if (bev == null)
{
from.Target = new PlantPourTarget(m_Plant);
from.SendLocalizedMessage(1060808,
"#" + m_Plant
.GetLocalizedPlantStatus()); // Target the container you wish to use to water the ~1_val~.
}
else
{
m_Plant.Pour(from, bev);
}
from.SendGump(new MainPlantGump(m_Plant));
break;
}
case 7: // Poison potion
{
AddPotion(from, PotionEffect.PoisonGreater, PotionEffect.PoisonDeadly);
break;
}
case 8: // Cure potion
{
AddPotion(from, PotionEffect.CureGreater);
break;
}
case 9: // Heal potion
{
AddPotion(from, PotionEffect.HealGreater);
break;
}
case 10: // Strength potion
{
AddPotion(from, PotionEffect.StrengthGreater);
break;
}
case 11: // Help
{
from.Send(new DisplayHelpTopic(48, true)); // PLANT GROWING
from.SendGump(new MainPlantGump(m_Plant));
break;
}
case 12: // Empty the bowl
{
from.SendGump(new EmptyTheBowlGump(m_Plant));
break;
}
}
}
private void AddPotion(Mobile from, params PotionEffect[] effects)
{
Item item = GetPotion(from, effects);
if (item != null)
{
m_Plant.Pour(from, item);
}
else
{
if (m_Plant.ApplyPotion(effects[0], true, out int message))
{
from.SendLocalizedMessage(1061884); // You don't have any strong potions of that type in your pack.
from.Target = new PlantPourTarget(m_Plant);
from.SendLocalizedMessage(1060808,
"#" + m_Plant
.GetLocalizedPlantStatus()); // Target the container you wish to use to water the ~1_val~.
return;
}
m_Plant.LabelTo(from, message);
}
from.SendGump(new MainPlantGump(m_Plant));
}
public static Item GetPotion(Mobile from, PotionEffect[] effects)
{
if (from.Backpack == null)
return null;
Item[] items = from.Backpack.FindItemsByType(new[] { typeof(BasePotion), typeof(PotionKeg) });
foreach (Item item in items)
if (item is BasePotion potion)
{
if (Array.IndexOf(effects, potion.PotionEffect) >= 0)
return potion;
}
else
{
PotionKeg keg = (PotionKeg)item;
if (keg.Held > 0 && Array.IndexOf(effects, keg.Type) >= 0)
return keg;
}
return null;
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Items
{
public class FertileDirt : Item
{
[Constructible]
public FertileDirt(int amount = 1) : base(0xF81)
{
Stackable = true;
Weight = 1.0;
Amount = amount;
}
public FertileDirt(Serial serial) : base(serial)
{
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,750 @@
using System;
using Server.Mobiles;
using Server.Network;
using Server.Targeting;
namespace Server.Items
{
public class GreenThorns : Item
{
[Constructible]
public GreenThorns(int amount = 1) : base(0xF42)
{
Stackable = true;
Weight = 1.0;
Hue = 0x42;
Amount = amount;
}
public GreenThorns(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1060837; // green thorns
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042664); // You must have the object in your backpack to use it.
return;
}
if (!from.CanBeginAction<GreenThorns>())
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1061908); // * You must wait a while before planting another thorn. *
return;
}
from.Target = new InternalTarget(this);
from.SendLocalizedMessage(1061906); // Choose a spot to plant the thorn.
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
private class InternalTarget : Target
{
private GreenThorns m_Thorn;
public InternalTarget(GreenThorns thorn) : base(3, true, TargetFlags.None)
{
m_Thorn = thorn;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (m_Thorn.Deleted)
return;
if (!m_Thorn.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042664); // You must have the object in your backpack to use it.
return;
}
if (!from.CanBeginAction<GreenThorns>())
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1061908); // * You must wait a while before planting another thorn. *
return;
}
if (from.Map != Map.Trammel && from.Map != Map.Felucca)
{
from.LocalOverheadMessage(MessageType.Regular, 0x2B2, true,
"No solen lairs exist on this facet. Try again in Trammel or Felucca.");
return;
}
if (!(targeted is LandTarget land))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1061912); // * You cannot plant a green thorn there! *
}
else
{
GreenThornsEffect effect = GreenThornsEffect.Create(from, land);
if (effect == null)
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1061913); // * You sense it would be useless to plant a green thorn there. *
}
else
{
m_Thorn.Consume();
from.LocalOverheadMessage(MessageType.Emote, 0x961,
1061914); // * You push the strange green thorn into the ground *
from.NonlocalOverheadMessage(MessageType.Emote, 0x961, 1061915,
from.Name); // * ~1_PLAYER_NAME~ pushes a strange green thorn into the ground. *
from.BeginAction<GreenThorns>();
new EndActionTimer(from).Start();
effect.Start();
}
}
}
protected override void OnTargetOutOfRange(Mobile from, object targeted)
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 502825); // That location is too far away
}
}
private class EndActionTimer : Timer
{
private Mobile m_From;
public EndActionTimer(Mobile from) : base(TimeSpan.FromMinutes(3.0))
{
m_From = from;
Priority = TimerPriority.FiveSeconds;
}
protected override void OnTick()
{
m_From.EndAction<GreenThorns>();
}
}
}
public abstract class GreenThornsEffect : Timer
{
private static TilesAndEffect[] m_Table =
{
new TilesAndEffect(new[]
{
0x71, 0x7C,
0x82, 0xA7,
0xDC, 0xE3,
0xE8, 0xEB,
0x141, 0x144,
0x14C, 0x14F,
0x169, 0x174,
0x1DC, 0x1E7,
0x1EC, 0x1EF,
0x272, 0x275,
0x27E, 0x281,
0x2D0, 0x2D7,
0x2E5, 0x2FF,
0x303, 0x31F,
0x32C, 0x32F,
0x33D, 0x340,
0x345, 0x34C,
0x355, 0x358,
0x367, 0x36E,
0x377, 0x37A,
0x38D, 0x390,
0x395, 0x39C,
0x3A5, 0x3A8,
0x3F6, 0x405,
0x547, 0x54E,
0x553, 0x556,
0x597, 0x59E,
0x623, 0x63A,
0x6F3, 0x6FA,
0x777, 0x791,
0x79A, 0x7A9,
0x7AE, 0x7B1
},
typeof(DirtGreenThornsEffect)),
new TilesAndEffect(new[]
{
0x9, 0x15,
0x150, 0x15C
},
typeof(FurrowsGreenThornsEffect)),
new TilesAndEffect(new[]
{
0x9C4, 0x9EB,
0x3D65, 0x3D65,
0x3DC0, 0x3DD9,
0x3DDB, 0x3DDC,
0x3DDE, 0x3EF0,
0x3FF6, 0x3FF6,
0x3FFC, 0x3FFE
},
typeof(SwampGreenThornsEffect)),
new TilesAndEffect(new[]
{
0x10C, 0x10F,
0x114, 0x117,
0x119, 0x11D,
0x179, 0x18A,
0x385, 0x38C,
0x391, 0x394,
0x39D, 0x3A4,
0x3A9, 0x3AC,
0x5BF, 0x5D6,
0x5DF, 0x5E2,
0x745, 0x748,
0x751, 0x758,
0x75D, 0x760,
0x76D, 0x773
},
typeof(SnowGreenThornsEffect)),
new TilesAndEffect(new[]
{
0x16, 0x3A,
0x44, 0x4B,
0x11E, 0x121,
0x126, 0x12D,
0x192, 0x192,
0x1A8, 0x1AB,
0x1B9, 0x1D1,
0x282, 0x285,
0x28A, 0x291,
0x335, 0x33C,
0x341, 0x344,
0x34D, 0x354,
0x359, 0x35C,
0x3B7, 0x3BE,
0x3C7, 0x3CA,
0x5A7, 0x5B2,
0x64B, 0x652,
0x657, 0x65A,
0x663, 0x66A,
0x66F, 0x672,
0x7BD, 0x7D0
},
typeof(SandGreenThornsEffect))
};
private int m_Step;
public GreenThornsEffect(Point3D location, Map map, Mobile from) : base(TimeSpan.FromSeconds(2.5))
{
Location = location;
Map = map;
From = from;
Priority = TimerPriority.TwoFiftyMS;
}
public Point3D Location{ get; }
public Map Map{ get; }
public Mobile From{ get; }
public static GreenThornsEffect Create(Mobile from, LandTarget land)
{
if (!from.Map.CanSpawnMobile(land.Location))
return null;
int tileID = land.TileID;
foreach (TilesAndEffect taep in m_Table)
{
bool contains = false;
for (int i = 0; !contains && i < taep.Tiles.Length; i += 2)
contains = tileID >= taep.Tiles[i] && tileID <= taep.Tiles[i + 1];
if (contains)
{
GreenThornsEffect effect =
(GreenThornsEffect)Activator.CreateInstance(taep.Effect, land.Location, from.Map, from);
return effect;
}
}
return null;
}
protected override void OnTick()
{
TimeSpan nextDelay = Play(m_Step++);
if (nextDelay > TimeSpan.Zero)
{
Delay = nextDelay;
Start();
}
}
protected abstract TimeSpan Play(int step);
protected bool SpawnItem(Item item)
{
for (int i = 0; i < 5; i++) // Try 5 times
{
int x = Location.X + Utility.RandomMinMax(-1, 1);
int y = Location.Y + Utility.RandomMinMax(-1, 1);
int z = Map.GetAverageZ(x, y);
if (Map.CanFit(x, y, Location.Z, 1))
{
item.MoveToWorld(new Point3D(x, y, Location.Z), Map);
return true;
}
if (Map.CanFit(x, y, z, 1))
{
item.MoveToWorld(new Point3D(x, y, z), Map);
return true;
}
}
return false;
}
protected bool SpawnCreature(BaseCreature creature)
{
for (int i = 0; i < 5; i++) // Try 5 times
{
int x = Location.X + Utility.RandomMinMax(-1, 1);
int y = Location.Y + Utility.RandomMinMax(-1, 1);
int z = Map.GetAverageZ(x, y);
if (Map.CanSpawnMobile(x, y, Location.Z))
{
creature.MoveToWorld(new Point3D(x, y, Location.Z), Map);
creature.Combatant = From;
return true;
}
if (Map.CanSpawnMobile(x, y, z))
{
creature.MoveToWorld(new Point3D(x, y, z), Map);
creature.Combatant = From;
return true;
}
}
return false;
}
private class TilesAndEffect
{
public TilesAndEffect(int[] tiles, Type effect)
{
Tiles = tiles;
Effect = effect;
}
public int[] Tiles{ get; }
public Type Effect{ get; }
}
}
public class DirtGreenThornsEffect : GreenThornsEffect
{
public DirtGreenThornsEffect(Point3D location, Map map, Mobile from) : base(location, map, from)
{
}
protected override TimeSpan Play(int step)
{
switch (step)
{
case 0:
{
Effects.PlaySound(Location, Map, 0x106);
Effects.SendLocationParticles(EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x3735, 1,
182, 0xBE3);
return TimeSpan.FromSeconds(4.0);
}
case 1:
{
Effects.PlaySound(Location, Map, 0x222);
return TimeSpan.FromSeconds(4.0);
}
case 2:
{
Effects.PlaySound(Location, Map, 0x21F);
return TimeSpan.FromSeconds(5.0);
}
case 3:
{
EffectItem dummy = EffectItem.Create(Location, Map, TimeSpan.FromSeconds(20.0));
dummy.PublicOverheadMessage(MessageType.Regular, 0x3B2, true,
"* The ground erupts with chaotic growth! *");
Effects.PlaySound(Location, Map, 0x12D);
SpawnReagents();
SpawnReagents();
return TimeSpan.FromSeconds(2.0);
}
case 4:
{
Effects.PlaySound(Location, Map, 0x12D);
SpawnReagents();
SpawnReagents();
return TimeSpan.FromSeconds(2.0);
}
case 5:
{
Effects.PlaySound(Location, Map, 0x12D);
SpawnReagents();
SpawnReagents();
return TimeSpan.FromSeconds(3.0);
}
default:
{
Effects.PlaySound(Location, Map, 0x12D);
SpawnReagents();
SpawnReagents();
return TimeSpan.Zero;
}
}
}
private void SpawnReagents()
{
Item reagents;
int amount = Utility.RandomMinMax(10, 25);
switch (Utility.Random(9))
{
case 0:
reagents = new BlackPearl(amount);
break;
case 1:
reagents = new Bloodmoss(amount);
break;
case 2:
reagents = new Garlic(amount);
break;
case 3:
reagents = new Ginseng(amount);
break;
case 4:
reagents = new MandrakeRoot(amount);
break;
case 5:
reagents = new Nightshade(amount);
break;
case 6:
reagents = new SulfurousAsh(amount);
break;
case 7:
reagents = new SpidersSilk(amount);
break;
default:
reagents = new FertileDirt(amount);
break;
}
if (!SpawnItem(reagents))
reagents.Delete();
}
}
public class FurrowsGreenThornsEffect : GreenThornsEffect
{
public FurrowsGreenThornsEffect(Point3D location, Map map, Mobile from) : base(location, map, from)
{
}
protected override TimeSpan Play(int step)
{
switch (step)
{
case 0:
{
Effects.PlaySound(Location, Map, 0x106);
Effects.SendLocationParticles(EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x3735, 1,
182, 0xBE3);
return TimeSpan.FromSeconds(4.0);
}
case 1:
{
EffectItem hole = EffectItem.Create(Location, Map, TimeSpan.FromSeconds(10.0));
hole.ItemID = 0x913;
Effects.PlaySound(Location, Map, 0x222);
return TimeSpan.FromSeconds(4.0);
}
case 2:
{
Effects.PlaySound(Location, Map, 0x21F);
return TimeSpan.FromSeconds(4.0);
}
default:
{
EffectItem dummy = EffectItem.Create(Location, Map, TimeSpan.FromSeconds(20.0));
dummy.PublicOverheadMessage(MessageType.Regular, 0x3B2, true,
"* A magical bunny leaps out of its hole, disturbed by the thorn's effect! *");
BaseCreature spawn = new VorpalBunny();
if (!SpawnCreature(spawn))
spawn.Delete();
return TimeSpan.Zero;
}
}
}
}
public class SwampGreenThornsEffect : GreenThornsEffect
{
public SwampGreenThornsEffect(Point3D location, Map map, Mobile from) : base(location, map, from)
{
}
protected override TimeSpan Play(int step)
{
switch (step)
{
case 0:
{
Effects.PlaySound(Location, Map, 0x106);
Effects.SendLocationParticles(EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x3735, 1,
182, 0xBE3);
return TimeSpan.FromSeconds(4.0);
}
case 1:
{
Effects.PlaySound(Location, Map, 0x222);
return TimeSpan.FromSeconds(4.0);
}
case 2:
{
Effects.PlaySound(Location, Map, 0x21F);
return TimeSpan.FromSeconds(1.0);
}
default:
{
EffectItem dummy = EffectItem.Create(Location, Map, TimeSpan.FromSeconds(20.0));
dummy.PublicOverheadMessage(MessageType.Regular, 0x3B2, true,
"* Strange green tendrils rise from the ground, whipping wildly! *");
Effects.PlaySound(Location, Map, 0x2B0);
BaseCreature spawn = new WhippingVine();
if (!SpawnCreature(spawn))
spawn.Delete();
return TimeSpan.Zero;
}
}
}
}
public class SnowGreenThornsEffect : GreenThornsEffect
{
public SnowGreenThornsEffect(Point3D location, Map map, Mobile from) : base(location, map, from)
{
}
protected override TimeSpan Play(int step)
{
switch (step)
{
case 0:
{
Effects.PlaySound(Location, Map, 0x106);
Effects.SendLocationParticles(EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x3735, 1,
182, 0xBE3);
return TimeSpan.FromSeconds(4.0);
}
case 1:
{
Effects.PlaySound(Location, Map, 0x222);
return TimeSpan.FromSeconds(4.0);
}
case 2:
{
Effects.PlaySound(Location, Map, 0x21F);
return TimeSpan.FromSeconds(4.0);
}
default:
{
EffectItem dummy = EffectItem.Create(Location, Map, TimeSpan.FromSeconds(20.0));
dummy.PublicOverheadMessage(MessageType.Regular, 0x3B2, true,
"* Slithering ice serpents rise to the surface to investigate the disturbance! *");
BaseCreature spawn = new GiantIceWorm();
if (!SpawnCreature(spawn))
spawn.Delete();
for (int i = 0; i < 3; i++)
{
BaseCreature snake = new IceSnake();
if (!SpawnCreature(snake))
snake.Delete();
}
return TimeSpan.Zero;
}
}
}
}
public class SandGreenThornsEffect : GreenThornsEffect
{
public SandGreenThornsEffect(Point3D location, Map map, Mobile from) : base(location, map, from)
{
}
protected override TimeSpan Play(int step)
{
switch (step)
{
case 0:
{
Effects.PlaySound(Location, Map, 0x106);
Effects.SendLocationParticles(EffectItem.Create(Location, Map, EffectItem.DefaultDuration), 0x3735, 1,
182, 0xBE3);
return TimeSpan.FromSeconds(4.0);
}
case 1:
{
Effects.PlaySound(Location, Map, 0x222);
return TimeSpan.FromSeconds(4.0);
}
case 2:
{
Effects.PlaySound(Location, Map, 0x21F);
return TimeSpan.FromSeconds(5.0);
}
default:
{
EffectItem dummy = EffectItem.Create(Location, Map, TimeSpan.FromSeconds(20.0));
dummy.PublicOverheadMessage(MessageType.Regular, 0x3B2, true,
"* The sand collapses, revealing a dark hole. *");
GreenThornsSHTeleporter.Create(Location, Map);
return TimeSpan.Zero;
}
}
}
}
public class GreenThornsSHTeleporter : Item
{
public static readonly Point3D Destination = new Point3D(5738, 1856, 0);
private GreenThornsSHTeleporter() : base(0x913)
{
Movable = false;
Hue = 0x1;
}
public GreenThornsSHTeleporter(Serial serial) : base(serial)
{
}
public override string DefaultName => "a hole";
public static void Create(Point3D location, Map map)
{
GreenThornsSHTeleporter tele = new GreenThornsSHTeleporter();
tele.MoveToWorld(location, map);
new InternalTimer(tele).Start();
}
public override void OnDoubleClick(Mobile from)
{
if (from.InRange(this, 3))
{
BaseCreature.TeleportPets(from, Destination, Map);
from.Location = Destination;
}
else
{
from.SendLocalizedMessage(1019045); // I can't reach that.
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
Delete();
}
private class InternalTimer : Timer
{
private GreenThornsSHTeleporter m_Teleporter;
public InternalTimer(GreenThornsSHTeleporter teleporter) : base(TimeSpan.FromMinutes(1.0))
{
m_Teleporter = teleporter;
Priority = TimerPriority.FiveSeconds;
}
protected override void OnTick()
{
m_Teleporter.Delete();
}
}
}
}

View file

@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using Server.Network;
namespace Server.Items
{
public class OrangePetals : Item
{
private static Dictionary<Mobile, OrangePetalsContext> m_Table = new Dictionary<Mobile, OrangePetalsContext>();
[Constructible]
public OrangePetals(int amount = 1) : base(0x1021)
{
Stackable = true;
Hue = 0x2B;
Amount = amount;
}
public OrangePetals(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1053122; // orange petals
public override double DefaultWeight => 0.1;
public override bool CheckItemUse(Mobile from, Item item)
{
if (item != this)
return base.CheckItemUse(from, item);
if (from != RootParent)
{
from.SendLocalizedMessage(1042038); // You must have the object in your backpack to use it.
return false;
}
return base.CheckItemUse(from, item);
}
public override void OnDoubleClick(Mobile from)
{
OrangePetalsContext context = GetContext(from);
if (context != null)
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1061904);
return;
}
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1061905);
from.PlaySound(0x3B);
Timer timer = new OrangePetalsTimer(from);
timer.Start();
AddContext(from, new OrangePetalsContext(timer));
Consume();
}
private static void AddContext(Mobile m, OrangePetalsContext context)
{
m_Table[m] = context;
}
public static void RemoveContext(Mobile m)
{
OrangePetalsContext context = GetContext(m);
if (context != null)
RemoveContext(m, context);
}
private static void RemoveContext(Mobile m, OrangePetalsContext context)
{
m_Table.Remove(m);
context.Timer.Stop();
}
private static OrangePetalsContext GetContext(Mobile m)
{
m_Table.TryGetValue(m, out OrangePetalsContext context);
return context;
}
public static bool UnderEffect(Mobile m)
{
return m_Table.ContainsKey(m);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
private class OrangePetalsTimer : Timer
{
private Mobile m_Mobile;
public OrangePetalsTimer(Mobile from) : base(TimeSpan.FromMinutes(5.0))
{
m_Mobile = from;
}
protected override void OnTick()
{
if (!m_Mobile.Deleted)
m_Mobile.LocalOverheadMessage(MessageType.Regular, 0x3F, true,
"* You feel the effects of your poison resistance wearing off *");
RemoveContext(m_Mobile);
}
}
private class OrangePetalsContext
{
public OrangePetalsContext(Timer timer)
{
Timer = timer;
}
public Timer Timer{ get; }
}
}
}

View file

@ -0,0 +1,96 @@
using Server.Targeting;
namespace Server.Items
{
public class RedLeaves : Item
{
[Constructible]
public RedLeaves(int amount = 1) : base(0x1E85)
{
Stackable = true;
Hue = 0x21;
Amount = amount;
}
public RedLeaves(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1053123; // red leaves
public override double DefaultWeight => 0.1;
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042664); // You must have the object in your backpack to use it.
return;
}
from.Target = new InternalTarget(this);
from.SendLocalizedMessage(1061907); // Choose a book you wish to seal with the wax from the red leaf.
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
private class InternalTarget : Target
{
private RedLeaves m_RedLeaves;
public InternalTarget(RedLeaves redLeaves) : base(3, false, TargetFlags.None)
{
m_RedLeaves = redLeaves;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (m_RedLeaves.Deleted)
return;
if (!m_RedLeaves.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042664); // You must have the object in your backpack to use it.
return;
}
if (!(targeted is Item item) || !item.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042664); // You must have the object in your backpack to use it.
}
else if (!(item is BaseBook))
{
item.LabelTo(from, 1061911); // You can only use red leaves to seal the ink into book pages!
}
else
{
BaseBook book = (BaseBook)item;
if (!book.Writable)
{
book.LabelTo(from, 1061909); // The ink in this book has already been sealed.
}
else
{
m_RedLeaves.Consume();
book.Writable = false;
book.LabelTo(from, 1061910); // You seal the ink to the page using wax from the red leaf.
}
}
}
}
}
}

View file

@ -0,0 +1,71 @@
namespace Server.Mobiles
{
public class GiantIceWorm : BaseCreature
{
[Constructible]
public GiantIceWorm() : base(AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4)
{
Body = 89;
BaseSoundID = 0xDC;
SetStr(216, 245);
SetDex(76, 100);
SetInt(66, 85);
SetHits(130, 147);
SetDamage(7, 17);
SetDamageType(ResistanceType.Physical, 10);
SetDamageType(ResistanceType.Cold, 90);
SetResistance(ResistanceType.Physical, 30, 35);
SetResistance(ResistanceType.Fire, 0);
SetResistance(ResistanceType.Cold, 80, 90);
SetResistance(ResistanceType.Poison, 15, 25);
SetResistance(ResistanceType.Energy, 10, 20);
SetSkill(SkillName.Poisoning, 75.1, 95.0);
SetSkill(SkillName.MagicResist, 45.1, 60.0);
SetSkill(SkillName.Tactics, 75.1, 80.0);
SetSkill(SkillName.Wrestling, 60.1, 80.0);
Fame = 4500;
Karma = -4500;
VirtualArmor = 40;
Tamable = true;
ControlSlots = 1;
MinTameSkill = 71.1;
}
public GiantIceWorm(Serial serial) : base(serial)
{
}
public override string CorpseName => "a giant ice worm corpse";
public override bool SubdueBeforeTame => true;
public override string DefaultName => "a giant ice worm";
public override Poison PoisonImmune => Poison.Greater;
public override Poison HitPoison => Poison.Greater;
public override FoodType FavoriteFood => FoodType.Meat;
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,15 @@
namespace Server.Network
{
public class DisplayHelpTopic : Packet
{
public DisplayHelpTopic(int topicID, bool display) : base(0xBF)
{
EnsureCapacity(11);
m_Stream.Write((short)0x17);
m_Stream.Write((byte)1);
m_Stream.Write(topicID);
m_Stream.Write(display);
}
}
}

View file

@ -0,0 +1,194 @@
using Server.Items;
using Server.Network;
using Server.Targeting;
namespace Server.Engines.Plants
{
public class PlantBowl : Item
{
private static int[] m_DirtPatchTiles =
{
0x9, 0x15,
0x71, 0x7C,
0x82, 0xA7,
0xDC, 0xE3,
0xE8, 0xEB,
0x141, 0x144,
0x14C, 0x15C,
0x169, 0x174,
0x1DC, 0x1EF,
0x272, 0x275,
0x27E, 0x281,
0x2D0, 0x2D7,
0x2E5, 0x2FF,
0x303, 0x31F,
0x32C, 0x32F,
0x33D, 0x340,
0x345, 0x34C,
0x355, 0x358,
0x367, 0x36E,
0x377, 0x37A,
0x38D, 0x390,
0x395, 0x39C,
0x3A5, 0x3A8,
0x3F6, 0x405,
0x547, 0x54E,
0x553, 0x556,
0x597, 0x59E,
0x623, 0x63A,
0x6F3, 0x6FA,
0x777, 0x791,
0x79A, 0x7A9,
0x7AE, 0x7B1,
0x98C, 0x99F,
0x9AC, 0x9BF,
0x5B27, 0x5B3E,
0x71F4, 0x71FB,
0x72C9, 0x72CA
};
[Constructible]
public PlantBowl() : base(0x15FD)
{
Weight = 1.0;
}
public PlantBowl(Serial serial) : base(serial)
{
}
public override int LabelNumber => 1060834; // a plant bowl
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042664); // You must have the object in your backpack to use it.
return;
}
from.Target = new InternalTarget(this);
from.SendLocalizedMessage(1061897); // Choose a patch of dirt to scoop up.
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
public static bool IsDirtPatch(object obj)
{
int tileID;
if (obj is Static staticObj && !staticObj.Movable)
tileID = (staticObj.ItemID & 0x3FFF) | 0x4000;
else if (obj is StaticTarget staticTarget)
tileID = (staticTarget.ItemID & 0x3FFF) | 0x4000;
else if (obj is LandTarget landTarget)
tileID = landTarget.TileID;
else
return false;
bool contains = false;
for (int i = 0; !contains && i < m_DirtPatchTiles.Length; i += 2)
contains = tileID >= m_DirtPatchTiles[i] && tileID <= m_DirtPatchTiles[i + 1];
return contains;
}
private class InternalTarget : Target
{
private PlantBowl m_PlantBowl;
public InternalTarget(PlantBowl plantBowl) : base(3, true, TargetFlags.None)
{
m_PlantBowl = plantBowl;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (m_PlantBowl.Deleted)
return;
if (!m_PlantBowl.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042664); // You must have the object in your backpack to use it.
return;
}
if (targeted is FertileDirt dirt)
{
int _dirtNeeded = Core.ML ? 20 : 40;
if (!dirt.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042664); // You must have the object in your backpack to use it.
}
else if (dirt.Amount < _dirtNeeded)
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1061896); // You need more dirt to fill a plant bowl!
}
else
{
PlantItem fullBowl = new PlantItem(true);
if (from.PlaceInBackpack(fullBowl))
{
dirt.Consume(_dirtNeeded);
m_PlantBowl.Delete();
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1061895); // You fill the bowl with fresh dirt.
}
else
{
fullBowl.Delete();
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1061894); // There is no room in your backpack for a bowl full of dirt!
}
}
}
else if (IsDirtPatch(targeted))
{
PlantItem fullBowl = new PlantItem();
if (from.PlaceInBackpack(fullBowl))
{
m_PlantBowl.Delete();
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1061895); // You fill the bowl with fresh dirt.
}
else
{
fullBowl.Delete();
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1061894); // There is no room in your backpack for a bowl full of dirt!
}
}
else
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2,
1061893); // You'll want to gather fresh dirt in order to raise a healthy plant!
}
}
protected override void OnTargetOutOfRange(Mobile from, object targeted)
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 502825); // That location is too far away
}
}
}
}

View file

@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
namespace Server.Engines.Plants
{
[Flags]
public enum PlantHue
{
Plain = 0x1 | Crossable | Reproduces,
Red = 0x2 | Crossable | Reproduces,
Blue = 0x4 | Crossable | Reproduces,
Yellow = 0x8 | Crossable | Reproduces,
BrightRed = Red | Bright,
BrightBlue = Blue | Bright,
BrightYellow = Yellow | Bright,
Purple = Red | Blue,
Green = Blue | Yellow,
Orange = Red | Yellow,
BrightPurple = Purple | Bright,
BrightGreen = Green | Bright,
BrightOrange = Orange | Bright,
Black = 0x10,
White = 0x20,
Pink = 0x40,
Magenta = 0x80,
Aqua = 0x100,
FireRed = 0x200,
None = 0,
Reproduces = 0x2000000,
Crossable = 0x4000000,
Bright = 0x8000000
}
public class PlantHueInfo
{
private static Dictionary<PlantHue, PlantHueInfo> m_Table;
static PlantHueInfo()
{
m_Table = new Dictionary<PlantHue, PlantHueInfo>
{
[PlantHue.Plain] = new PlantHueInfo(0, 1060813, PlantHue.Plain, 0x835),
[PlantHue.Red] = new PlantHueInfo(0x66D, 1060814, PlantHue.Red, 0x24),
[PlantHue.Blue] = new PlantHueInfo(0x53D, 1060815, PlantHue.Blue, 0x6),
[PlantHue.Yellow] = new PlantHueInfo(0x8A5, 1060818, PlantHue.Yellow, 0x38),
[PlantHue.BrightRed] = new PlantHueInfo(0x21, 1060814, PlantHue.BrightRed, 0x21),
[PlantHue.BrightBlue] = new PlantHueInfo(0x5, 1060815, PlantHue.BrightBlue, 0x6),
[PlantHue.BrightYellow] = new PlantHueInfo(0x38, 1060818, PlantHue.BrightYellow, 0x35),
[PlantHue.Purple] = new PlantHueInfo(0xD, 1060816, PlantHue.Purple, 0x10),
[PlantHue.Green] = new PlantHueInfo(0x59B, 1060819, PlantHue.Green, 0x42),
[PlantHue.Orange] = new PlantHueInfo(0x46F, 1060817, PlantHue.Orange, 0x2E),
[PlantHue.BrightPurple] = new PlantHueInfo(0x10, 1060816, PlantHue.BrightPurple, 0xD),
[PlantHue.BrightGreen] = new PlantHueInfo(0x42, 1060819, PlantHue.BrightGreen, 0x3F),
[PlantHue.BrightOrange] = new PlantHueInfo(0x2B, 1060817, PlantHue.BrightOrange, 0x2B),
[PlantHue.Black] = new PlantHueInfo(0x455, 1060820, PlantHue.Black, 0),
[PlantHue.White] = new PlantHueInfo(0x481, 1060821, PlantHue.White, 0x481),
[PlantHue.Pink] = new PlantHueInfo(0x48E, 1061854, PlantHue.Pink),
[PlantHue.Magenta] = new PlantHueInfo(0x486, 1061852, PlantHue.Magenta),
[PlantHue.Aqua] = new PlantHueInfo(0x495, 1061853, PlantHue.Aqua),
[PlantHue.FireRed] = new PlantHueInfo(0x489, 1061855, PlantHue.FireRed)
};
}
private PlantHueInfo(int hue, int name, PlantHue plantHue) : this(hue, name, plantHue, hue)
{
}
private PlantHueInfo(int hue, int name, PlantHue plantHue, int gumpHue)
{
Hue = hue;
Name = name;
PlantHue = plantHue;
GumpHue = gumpHue;
}
public int Hue{ get; }
public int Name{ get; }
public PlantHue PlantHue{ get; }
public int GumpHue{ get; }
public static PlantHueInfo GetInfo(PlantHue plantHue)
{
return m_Table.TryGetValue(plantHue, out PlantHueInfo info) ? info : m_Table[PlantHue.Plain];
}
public static PlantHue RandomFirstGeneration()
{
switch (Utility.Random(4))
{
case 0: return PlantHue.Plain;
case 1: return PlantHue.Red;
case 2: return PlantHue.Blue;
default: return PlantHue.Yellow;
}
}
public static bool CanReproduce(PlantHue plantHue)
{
return (plantHue & PlantHue.Reproduces) != PlantHue.None;
}
public static bool IsCrossable(PlantHue plantHue)
{
return (plantHue & PlantHue.Crossable) != PlantHue.None;
}
public static bool IsBright(PlantHue plantHue)
{
return (plantHue & PlantHue.Bright) != PlantHue.None;
}
public static PlantHue GetNotBright(PlantHue plantHue)
{
return plantHue & ~PlantHue.Bright;
}
public static bool IsPrimary(PlantHue plantHue)
{
return plantHue == PlantHue.Red || plantHue == PlantHue.Blue || plantHue == PlantHue.Yellow;
}
public static PlantHue Cross(PlantHue first, PlantHue second)
{
if (!IsCrossable(first) || !IsCrossable(second))
return PlantHue.None;
if (Utility.RandomDouble() < 0.01)
return Utility.RandomBool() ? PlantHue.Black : PlantHue.White;
if (first == PlantHue.Plain || second == PlantHue.Plain)
return PlantHue.Plain;
PlantHue notBrightFirst = GetNotBright(first);
PlantHue notBrightSecond = GetNotBright(second);
if (notBrightFirst == notBrightSecond)
return first | PlantHue.Bright;
bool firstPrimary = IsPrimary(notBrightFirst);
bool secondPrimary = IsPrimary(notBrightSecond);
if (firstPrimary && secondPrimary)
return notBrightFirst | notBrightSecond;
if (firstPrimary)
return notBrightFirst;
if (secondPrimary)
return notBrightSecond;
return notBrightFirst & notBrightSecond;
}
public bool IsCrossable()
{
return IsCrossable(PlantHue);
}
public bool IsBright()
{
return IsBright(PlantHue);
}
public PlantHue GetNotBright()
{
return GetNotBright(PlantHue);
}
public bool IsPrimary()
{
return IsPrimary(PlantHue);
}
}
}

View file

@ -0,0 +1,558 @@
using System.Collections.Generic;
using Server.ContextMenus;
using Server.Gumps;
using Server.Items;
using Server.Multis;
using Server.Network;
namespace Server.Engines.Plants
{
public enum PlantStatus
{
BowlOfDirt = 0,
Seed = 1,
Sapling = 2,
Plant = 4,
FullGrownPlant = 7,
DecorativePlant = 10,
DeadTwigs = 11,
Stage1 = 1,
Stage2 = 2,
Stage3 = 3,
Stage4 = 4,
Stage5 = 5,
Stage6 = 6,
Stage7 = 7,
Stage8 = 8,
Stage9 = 9
}
public class PlantItem : Item, ISecurable
{
/*
* Clients 7.0.12.0+ expect a container type in the plant label.
* To support older (and only older) clients, change this to false.
*/
private static readonly bool ShowContainerType = true;
private PlantHue m_PlantHue;
private PlantStatus m_PlantStatus;
private PlantType m_PlantType;
private bool m_ShowType;
[Constructible]
public PlantItem(bool fertileDirt = false) : base(0x1602)
{
Weight = 1.0;
m_PlantStatus = PlantStatus.BowlOfDirt;
PlantSystem = new PlantSystem(this, fertileDirt);
Level = SecureLevel.Owner;
Plants.Add(this);
}
public PlantItem(Serial serial) : base(serial)
{
}
public PlantSystem PlantSystem{ get; private set; }
public override bool ForceShowProperties => ObjectPropertyList.Enabled;
[CommandProperty(AccessLevel.GameMaster)]
public PlantStatus PlantStatus
{
get => m_PlantStatus;
set
{
if (m_PlantStatus == value || value < PlantStatus.BowlOfDirt || value > PlantStatus.DeadTwigs)
return;
double ratio;
if (PlantSystem != null)
ratio = (double)PlantSystem.Hits / PlantSystem.MaxHits;
else
ratio = 1.0;
m_PlantStatus = value;
if (m_PlantStatus >= PlantStatus.DecorativePlant)
{
PlantSystem = null;
}
else
{
if (PlantSystem == null)
PlantSystem = new PlantSystem(this, false);
int hits = (int)(PlantSystem.MaxHits * ratio);
if (hits == 0 && m_PlantStatus > PlantStatus.BowlOfDirt)
PlantSystem.Hits = hits + 1;
else
PlantSystem.Hits = hits;
}
Update();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public PlantType PlantType
{
get => m_PlantType;
set
{
m_PlantType = value;
Update();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public PlantHue PlantHue
{
get => m_PlantHue;
set
{
m_PlantHue = value;
Update();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public bool ShowType
{
get => m_ShowType;
set
{
m_ShowType = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public bool ValidGrowthLocation
{
get
{
if (IsLockedDown && RootParent == null)
return true;
if (!(RootParent is Mobile owner))
return false;
return IsChildOf(owner.Backpack) || IsChildOf(owner.FindBankNoCreate());
}
}
[CommandProperty(AccessLevel.GameMaster)]
public bool IsGrowable => m_PlantStatus >= PlantStatus.BowlOfDirt && m_PlantStatus <= PlantStatus.Stage9;
[CommandProperty(AccessLevel.GameMaster)]
public bool IsCrossable => PlantHueInfo.IsCrossable(PlantHue) && PlantTypeInfo.IsCrossable(PlantType);
[CommandProperty(AccessLevel.GameMaster)]
public bool Reproduces => PlantHueInfo.CanReproduce(PlantHue) && PlantTypeInfo.CanReproduce(PlantType);
public static List<PlantItem> Plants{ get; } = new List<PlantItem>();
[CommandProperty(AccessLevel.GameMaster)]
public SecureLevel Level{ get; set; }
public override void OnSingleClick(Mobile from)
{
if (m_PlantStatus >= PlantStatus.DeadTwigs)
LabelTo(from, LabelNumber);
else if (m_PlantStatus >= PlantStatus.DecorativePlant)
LabelTo(from, 1061924); // a decorative plant
else if (m_PlantStatus >= PlantStatus.FullGrownPlant)
LabelTo(from, PlantTypeInfo.GetInfo(m_PlantType).Name);
else
LabelTo(from, 1029913); // plant bowl
}
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
{
base.GetContextMenuEntries(from, list);
SetSecureLevelEntry.AddTo(from, this, list);
}
public int GetLocalizedPlantStatus()
{
if (m_PlantStatus >= PlantStatus.Plant)
return 1060812; // plant
if (m_PlantStatus >= PlantStatus.Sapling)
return 1023305; // sapling
if (m_PlantStatus >= PlantStatus.Seed)
return 1060810; // seed
return 1026951; // dirt
}
public int GetLocalizedContainerType()
{
return 1150435; // bowl
}
private void Update()
{
if (m_PlantStatus >= PlantStatus.DeadTwigs)
{
ItemID = 0x1B9D;
Hue = PlantHueInfo.GetInfo(m_PlantHue).Hue;
}
else if (m_PlantStatus >= PlantStatus.FullGrownPlant)
{
ItemID = PlantTypeInfo.GetInfo(m_PlantType).ItemID;
Hue = PlantHueInfo.GetInfo(m_PlantHue).Hue;
}
else if (m_PlantStatus >= PlantStatus.Plant)
{
ItemID = 0x1600;
Hue = 0;
}
else
{
ItemID = 0x1602;
Hue = 0;
}
InvalidateProperties();
}
public override void AddNameProperty(ObjectPropertyList list)
{
if (m_PlantStatus >= PlantStatus.DeadTwigs)
{
base.AddNameProperty(list);
}
else if (m_PlantStatus < PlantStatus.Seed)
{
string args;
if (ShowContainerType)
args = $"#{GetLocalizedContainerType()}\t#{PlantSystem.GetLocalizedDirtStatus()}";
else
args = $"#{PlantSystem.GetLocalizedDirtStatus()}";
list.Add(1060830, args); // a ~1_val~ of ~2_val~ dirt
}
else
{
PlantTypeInfo typeInfo = PlantTypeInfo.GetInfo(m_PlantType);
PlantHueInfo hueInfo = PlantHueInfo.GetInfo(m_PlantHue);
if (m_PlantStatus >= PlantStatus.DecorativePlant)
{
list.Add(typeInfo.GetPlantLabelDecorative(hueInfo), $"#{hueInfo.Name}\t#{typeInfo.Name}");
}
else if (m_PlantStatus >= PlantStatus.FullGrownPlant)
{
list.Add(typeInfo.GetPlantLabelFullGrown(hueInfo),
$"#{PlantSystem.GetLocalizedHealth()}\t#{hueInfo.Name}\t#{typeInfo.Name}");
}
else
{
string args;
if (ShowContainerType)
args =
$"#{GetLocalizedContainerType()}\t#{PlantSystem.GetLocalizedDirtStatus()}\t#{PlantSystem.GetLocalizedHealth()}";
else
args = $"#{PlantSystem.GetLocalizedDirtStatus()}\t#{PlantSystem.GetLocalizedHealth()}";
if (m_ShowType)
{
args += $"\t#{hueInfo.Name}\t#{typeInfo.Name}\t#{GetLocalizedPlantStatus()}";
if (m_PlantStatus == PlantStatus.Plant)
list.Add(typeInfo.GetPlantLabelPlant(hueInfo), args);
else
list.Add(typeInfo.GetPlantLabelSeed(hueInfo), args);
}
else
{
args +=
$"\t#{(typeInfo.PlantCategory == PlantCategory.Default ? hueInfo.Name : (int)typeInfo.PlantCategory)}\t#{GetLocalizedPlantStatus()}";
list.Add(hueInfo.IsBright() ? 1060832 : 1060831,
args); // a ~1_val~ of ~2_val~ dirt with a ~3_val~ [bright] ~4_val~ ~5_val~
}
}
}
}
public bool IsUsableBy(Mobile from)
{
return IsChildOf(from.Backpack) || IsChildOf(from.FindBankNoCreate()) || IsLockedDown && IsAccessibleTo(from) ||
RootParent is Item root && root.IsSecure && root.IsAccessibleTo(from);
}
public override void OnDoubleClick(Mobile from)
{
if (m_PlantStatus >= PlantStatus.DecorativePlant)
return;
Point3D loc = GetWorldLocation();
if (!from.InLOS(loc) || !from.InRange(loc, 2))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 1019045); // I can't reach that.
return;
}
if (!IsUsableBy(from))
{
LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it.
return;
}
from.SendGump(new MainPlantGump(this));
}
public void PlantSeed(Mobile from, Seed seed)
{
if (m_PlantStatus >= PlantStatus.FullGrownPlant)
{
LabelTo(from, 1061919); // You must use a seed on some prepared soil!
}
else if (!IsUsableBy(from))
{
LabelTo(from, 1061921); // The bowl of dirt must be in your pack, or you must lock it down.
}
else if (m_PlantStatus != PlantStatus.BowlOfDirt)
{
from.SendLocalizedMessage(1080389,
"#" + GetLocalizedPlantStatus()); // This bowl of dirt already has a ~1_val~ in it!
}
else if (PlantSystem.Water < 2)
{
LabelTo(from, 1061920); // The dirt needs to be softened first.
}
else
{
m_PlantType = seed.PlantType;
m_PlantHue = seed.PlantHue;
m_ShowType = seed.ShowType;
seed.Consume();
PlantStatus = PlantStatus.Seed;
PlantSystem.Reset(false);
LabelTo(from, 1061922); // You plant the seed in the bowl of dirt.
}
}
public void Die()
{
if (m_PlantStatus >= PlantStatus.FullGrownPlant)
{
PlantStatus = PlantStatus.DeadTwigs;
}
else
{
PlantStatus = PlantStatus.BowlOfDirt;
PlantSystem.Reset(true);
}
}
public void Pour(Mobile from, Item item)
{
if (m_PlantStatus >= PlantStatus.DeadTwigs)
return;
if (m_PlantStatus == PlantStatus.DecorativePlant)
{
LabelTo(from, 1053049); // This is a decorative plant, it does not need watering!
return;
}
if (!IsUsableBy(from))
{
LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it.
return;
}
if (item is BaseBeverage beverage)
{
if (beverage.IsEmpty || !beverage.Pourable || beverage.Content != BeverageType.Water)
{
LabelTo(from, 1053069); // You can't use that on a plant!
return;
}
if (!beverage.ValidateUse(from, true))
return;
beverage.Quantity--;
PlantSystem.Water++;
from.PlaySound(0x4E);
LabelTo(from, 1061858); // You soften the dirt with water.
}
else if (item is BasePotion potion)
{
if (ApplyPotion(potion.PotionEffect, false, out int message))
{
potion.Consume();
from.PlaySound(0x240);
from.AddToBackpack(new Bottle());
}
LabelTo(from, message);
}
else if (item is PotionKeg keg)
{
if (keg.Held <= 0)
{
LabelTo(from, 1053069); // You can't use that on a plant!
return;
}
if (ApplyPotion(keg.Type, false, out int message))
{
keg.Held--;
from.PlaySound(0x240);
}
LabelTo(from, message);
}
else
{
LabelTo(from, 1053069); // You can't use that on a plant!
}
}
public bool ApplyPotion(PotionEffect effect, bool testOnly, out int message)
{
if (m_PlantStatus >= PlantStatus.DecorativePlant)
{
message = 1053049; // This is a decorative plant, it does not need watering!
return false;
}
if (m_PlantStatus == PlantStatus.BowlOfDirt)
{
message = 1053066; // You should only pour potions on a plant or seed!
return false;
}
bool full = false;
if (effect == PotionEffect.PoisonGreater || effect == PotionEffect.PoisonDeadly)
{
if (PlantSystem.IsFullPoisonPotion)
full = true;
else if (!testOnly)
PlantSystem.PoisonPotion++;
}
else if (effect == PotionEffect.CureGreater)
{
if (PlantSystem.IsFullCurePotion)
full = true;
else if (!testOnly)
PlantSystem.CurePotion++;
}
else if (effect == PotionEffect.HealGreater)
{
if (PlantSystem.IsFullHealPotion)
full = true;
else if (!testOnly)
PlantSystem.HealPotion++;
}
else if (effect == PotionEffect.StrengthGreater)
{
if (PlantSystem.IsFullStrengthPotion)
full = true;
else if (!testOnly)
PlantSystem.StrengthPotion++;
}
else if (effect == PotionEffect.PoisonLesser || effect == PotionEffect.Poison ||
effect == PotionEffect.CureLesser || effect == PotionEffect.Cure ||
effect == PotionEffect.HealLesser || effect == PotionEffect.Heal || effect == PotionEffect.Strength)
{
message = 1053068; // This potion is not powerful enough to use on a plant!
return false;
}
else
{
message = 1053069; // You can't use that on a plant!
return false;
}
if (full)
{
message = 1053065; // The plant is already soaked with this type of potion!
return false;
}
message = 1053067; // You pour the potion over the plant.
return true;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(2); // version
writer.Write((int)Level);
writer.Write((int)m_PlantStatus);
writer.Write((int)m_PlantType);
writer.Write((int)m_PlantHue);
writer.Write(m_ShowType);
if (m_PlantStatus < PlantStatus.DecorativePlant)
PlantSystem.Save(writer);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 2:
case 1:
{
Level = (SecureLevel)reader.ReadInt();
goto case 0;
}
case 0:
{
if (version < 1)
Level = SecureLevel.CoOwners;
m_PlantStatus = (PlantStatus)reader.ReadInt();
m_PlantType = (PlantType)reader.ReadInt();
m_PlantHue = (PlantHue)reader.ReadInt();
m_ShowType = reader.ReadBool();
if (m_PlantStatus < PlantStatus.DecorativePlant)
PlantSystem = new PlantSystem(this, reader);
if (version < 2 && PlantHueInfo.IsCrossable(m_PlantHue))
m_PlantHue |= PlantHue.Reproduces;
break;
}
}
Plants.Add(this);
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
Plants.Remove(this);
}
}
}

View file

@ -0,0 +1,32 @@
using Server.Targeting;
namespace Server.Engines.Plants
{
public class PlantPourTarget : Target
{
private PlantItem m_Plant;
public PlantPourTarget(PlantItem plant) : base(3, true, TargetFlags.None)
{
m_Plant = plant;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (!m_Plant.Deleted && from.InRange(m_Plant.GetWorldLocation(), 3) && targeted is Item item)
m_Plant.Pour(from, item);
}
protected override void OnTargetFinish(Mobile from)
{
if (!m_Plant.Deleted && m_Plant.PlantStatus < PlantStatus.DecorativePlant &&
from.InRange(m_Plant.GetWorldLocation(), 3) && m_Plant.IsUsableBy(from))
{
if (from.HasGump<MainPlantGump>())
from.CloseGump<MainPlantGump>();
from.SendGump(new MainPlantGump(m_Plant));
}
}
}
}

View file

@ -0,0 +1,48 @@
using System;
using Server.Items;
namespace Server.Engines.Plants
{
public class PlantResourceInfo
{
private static PlantResourceInfo[] m_ResourceList =
{
new PlantResourceInfo(PlantType.ElephantEarPlant, PlantHue.BrightRed, typeof(RedLeaves)),
new PlantResourceInfo(PlantType.PonytailPalm, PlantHue.BrightRed, typeof(RedLeaves)),
new PlantResourceInfo(PlantType.CenturyPlant, PlantHue.BrightRed, typeof(RedLeaves)),
new PlantResourceInfo(PlantType.Poppies, PlantHue.BrightOrange, typeof(OrangePetals)),
new PlantResourceInfo(PlantType.Bulrushes, PlantHue.BrightOrange, typeof(OrangePetals)),
new PlantResourceInfo(PlantType.PampasGrass, PlantHue.BrightOrange, typeof(OrangePetals)),
new PlantResourceInfo(PlantType.SnakePlant, PlantHue.BrightGreen, typeof(GreenThorns)),
new PlantResourceInfo(PlantType.BarrelCactus, PlantHue.BrightGreen, typeof(GreenThorns)),
new PlantResourceInfo(PlantType.CocoaTree, PlantHue.Plain, typeof(CocoaPulp))
};
private PlantResourceInfo(PlantType plantType, PlantHue plantHue, Type resourceType)
{
PlantType = plantType;
PlantHue = plantHue;
ResourceType = resourceType;
}
public PlantType PlantType{ get; }
public PlantHue PlantHue{ get; }
public Type ResourceType{ get; }
public static PlantResourceInfo GetInfo(PlantType plantType, PlantHue plantHue)
{
foreach (PlantResourceInfo info in m_ResourceList)
if (info.PlantType == plantType && info.PlantHue == plantHue)
return info;
return null;
}
public Item CreateResource()
{
return (Item)Activator.CreateInstance(ResourceType);
}
}
}

View file

@ -0,0 +1,684 @@
using System;
using System.Collections.Generic;
using Server.Misc;
namespace Server.Engines.Plants
{
public enum PlantHealth
{
Dying,
Wilted,
Healthy,
Vibrant
}
public enum PlantGrowthIndicator
{
None,
InvalidLocation,
NotHealthy,
Delay,
Grown,
DoubleGrown
}
public class PlantSystem
{
public static readonly TimeSpan CheckDelay = TimeSpan.FromHours(23.0);
private int m_AvailableResources;
private int m_AvailableSeeds;
private int m_CurePotion;
private int m_Disease;
private int m_Fungus;
private int m_HealPotion;
private int m_Hits;
private int m_Infestation;
private int m_LeftResources;
private int m_LeftSeeds;
private int m_Poison;
private int m_PoisonPotion;
private PlantHue m_SeedHue;
private PlantType m_SeedType;
private int m_StrengthPotion;
private int m_Water;
public PlantSystem(PlantItem plant, bool fertileDirt)
{
Plant = plant;
FertileDirt = fertileDirt;
NextGrowth = DateTime.UtcNow + CheckDelay;
GrowthIndicator = PlantGrowthIndicator.None;
m_Hits = MaxHits;
m_LeftSeeds = 8;
m_LeftResources = 8;
}
public PlantSystem(PlantItem plant, GenericReader reader)
{
Plant = plant;
int version = reader.ReadInt();
FertileDirt = reader.ReadBool();
if (version >= 1)
NextGrowth = reader.ReadDateTime();
else
NextGrowth = reader.ReadDeltaTime();
GrowthIndicator = (PlantGrowthIndicator)reader.ReadInt();
m_Water = reader.ReadInt();
m_Hits = reader.ReadInt();
m_Infestation = reader.ReadInt();
m_Fungus = reader.ReadInt();
m_Poison = reader.ReadInt();
m_Disease = reader.ReadInt();
m_PoisonPotion = reader.ReadInt();
m_CurePotion = reader.ReadInt();
m_HealPotion = reader.ReadInt();
m_StrengthPotion = reader.ReadInt();
Pollinated = reader.ReadBool();
m_SeedType = (PlantType)reader.ReadInt();
m_SeedHue = (PlantHue)reader.ReadInt();
m_AvailableSeeds = reader.ReadInt();
m_LeftSeeds = reader.ReadInt();
m_AvailableResources = reader.ReadInt();
m_LeftResources = reader.ReadInt();
if (version < 2 && PlantHueInfo.IsCrossable(m_SeedHue))
m_SeedHue |= PlantHue.Reproduces;
}
public PlantItem Plant{ get; }
public bool FertileDirt{ get; set; }
public DateTime NextGrowth{ get; private set; }
public PlantGrowthIndicator GrowthIndicator{ get; private set; }
public bool IsFullWater => m_Water >= 4;
public int Water
{
get => m_Water;
set
{
if (value < 0)
m_Water = 0;
else if (value > 4)
m_Water = 4;
else
m_Water = value;
Plant.InvalidateProperties();
}
}
public int Hits
{
get => m_Hits;
set
{
if (m_Hits == value)
return;
if (value < 0)
m_Hits = 0;
else if (value > MaxHits)
m_Hits = MaxHits;
else
m_Hits = value;
if (m_Hits == 0)
Plant.Die();
Plant.InvalidateProperties();
}
}
public int MaxHits => 10 + (int)Plant.PlantStatus * 2;
public PlantHealth Health
{
get
{
int perc = m_Hits * 100 / MaxHits;
if (perc < 33)
return PlantHealth.Dying;
if (perc < 66)
return PlantHealth.Wilted;
if (perc < 100)
return PlantHealth.Healthy;
return PlantHealth.Vibrant;
}
}
public int Infestation
{
get => m_Infestation;
set
{
if (value < 0)
m_Infestation = 0;
else if (value > 2)
m_Infestation = 2;
else
m_Infestation = value;
}
}
public int Fungus
{
get => m_Fungus;
set
{
if (value < 0)
m_Fungus = 0;
else if (value > 2)
m_Fungus = 2;
else
m_Fungus = value;
}
}
public int Poison
{
get => m_Poison;
set
{
if (value < 0)
m_Poison = 0;
else if (value > 2)
m_Poison = 2;
else
m_Poison = value;
}
}
public int Disease
{
get => m_Disease;
set
{
if (value < 0)
m_Disease = 0;
else if (value > 2)
m_Disease = 2;
else
m_Disease = value;
}
}
public bool IsFullPoisonPotion => m_PoisonPotion >= 2;
public int PoisonPotion
{
get => m_PoisonPotion;
set
{
if (value < 0)
m_PoisonPotion = 0;
else if (value > 2)
m_PoisonPotion = 2;
else
m_PoisonPotion = value;
}
}
public bool IsFullCurePotion => m_CurePotion >= 2;
public int CurePotion
{
get => m_CurePotion;
set
{
if (value < 0)
m_CurePotion = 0;
else if (value > 2)
m_CurePotion = 2;
else
m_CurePotion = value;
}
}
public bool IsFullHealPotion => m_HealPotion >= 2;
public int HealPotion
{
get => m_HealPotion;
set
{
if (value < 0)
m_HealPotion = 0;
else if (value > 2)
m_HealPotion = 2;
else
m_HealPotion = value;
}
}
public bool IsFullStrengthPotion => m_StrengthPotion >= 2;
public int StrengthPotion
{
get => m_StrengthPotion;
set
{
if (value < 0)
m_StrengthPotion = 0;
else if (value > 2)
m_StrengthPotion = 2;
else
m_StrengthPotion = value;
}
}
public bool HasMaladies => Infestation > 0 || Fungus > 0 || Poison > 0 || Disease > 0 || Water != 2;
public bool PollenProducing => Plant.IsCrossable && Plant.PlantStatus >= PlantStatus.FullGrownPlant;
public bool Pollinated{ get; set; }
public PlantType SeedType
{
get
{
if (Pollinated)
return m_SeedType;
return Plant.PlantType;
}
set => m_SeedType = value;
}
public PlantHue SeedHue
{
get
{
if (Pollinated)
return m_SeedHue;
return Plant.PlantHue;
}
set => m_SeedHue = value;
}
public int AvailableSeeds
{
get => m_AvailableSeeds;
set
{
if (value >= 0) m_AvailableSeeds = value;
}
}
public int LeftSeeds
{
get => m_LeftSeeds;
set
{
if (value >= 0) m_LeftSeeds = value;
}
}
public int AvailableResources
{
get => m_AvailableResources;
set
{
if (value >= 0) m_AvailableResources = value;
}
}
public int LeftResources
{
get => m_LeftResources;
set
{
if (value >= 0) m_LeftResources = value;
}
}
public void Reset(bool potions)
{
NextGrowth = DateTime.UtcNow + CheckDelay;
GrowthIndicator = PlantGrowthIndicator.None;
Hits = MaxHits;
m_Infestation = 0;
m_Fungus = 0;
m_Poison = 0;
m_Disease = 0;
if (potions)
{
m_PoisonPotion = 0;
m_CurePotion = 0;
m_HealPotion = 0;
m_StrengthPotion = 0;
}
Pollinated = false;
m_AvailableSeeds = 0;
m_LeftSeeds = 8;
m_AvailableResources = 0;
m_LeftResources = 8;
}
public int GetLocalizedDirtStatus()
{
if (Water <= 1)
return 1060826; // hard
if (Water <= 2)
return 1060827; // soft
if (Water <= 3)
return 1060828; // squishy
return 1060829; // sopping wet
}
public int GetLocalizedHealth()
{
switch (Health)
{
case PlantHealth.Dying: return 1060825; // dying
case PlantHealth.Wilted: return 1060824; // wilted
case PlantHealth.Healthy: return 1060823; // healthy
default: return 1060822; // vibrant
}
}
public static void Configure()
{
EventSink.WorldLoad += EventSink_WorldLoad;
if (!AutoRestart.Enabled)
EventSink.WorldSave += EventSink_WorldSave;
EventSink.Login += EventSink_Login;
}
private static void EventSink_Login(LoginEventArgs args)
{
Mobile from = args.Mobile;
from.Backpack?.FindItemsByType<PlantItem>().ForEach(plant =>
{
if (plant.IsGrowable)
plant.PlantSystem.DoGrowthCheck();
});
from.FindBankNoCreate()?.FindItemsByType<PlantItem>().ForEach(plant =>
{
if (plant.IsGrowable)
plant.PlantSystem.DoGrowthCheck();
});
}
public static void GrowAll()
{
List<PlantItem> plants = PlantItem.Plants;
DateTime now = DateTime.UtcNow;
for (int i = plants.Count - 1; i >= 0; --i)
{
PlantItem plant = plants[i];
if (plant.IsGrowable && !(plant.RootParent is Mobile) && now >= plant.PlantSystem.NextGrowth)
plant.PlantSystem.DoGrowthCheck();
}
}
private static void EventSink_WorldLoad()
{
GrowAll();
}
private static void EventSink_WorldSave(WorldSaveEventArgs args)
{
GrowAll();
}
public void DoGrowthCheck()
{
if (!Plant.IsGrowable)
return;
if (DateTime.UtcNow < NextGrowth)
{
GrowthIndicator = PlantGrowthIndicator.Delay;
return;
}
NextGrowth = DateTime.UtcNow + CheckDelay;
if (!Plant.ValidGrowthLocation)
{
GrowthIndicator = PlantGrowthIndicator.InvalidLocation;
return;
}
if (Plant.PlantStatus == PlantStatus.BowlOfDirt)
{
if (Water > 2 || Utility.RandomDouble() < 0.9)
Water--;
return;
}
ApplyBeneficialEffects();
if (!ApplyMaladiesEffects()) // Dead
return;
Grow();
UpdateMaladies();
}
private void ApplyBeneficialEffects()
{
if (PoisonPotion >= Infestation)
{
PoisonPotion -= Infestation;
Infestation = 0;
}
else
{
Infestation -= PoisonPotion;
PoisonPotion = 0;
}
if (CurePotion >= Fungus)
{
CurePotion -= Fungus;
Fungus = 0;
}
else
{
Fungus -= CurePotion;
CurePotion = 0;
}
if (HealPotion >= Poison)
{
HealPotion -= Poison;
Poison = 0;
}
else
{
Poison -= HealPotion;
HealPotion = 0;
}
if (HealPotion >= Disease)
{
HealPotion -= Disease;
Disease = 0;
}
else
{
Disease -= HealPotion;
HealPotion = 0;
}
if (!HasMaladies)
{
if (HealPotion > 0)
Hits += HealPotion * 7;
else
Hits += 2;
}
HealPotion = 0;
}
private bool ApplyMaladiesEffects()
{
int damage = 0;
if (Infestation > 0)
damage += Infestation * Utility.RandomMinMax(3, 6);
if (Fungus > 0)
damage += Fungus * Utility.RandomMinMax(3, 6);
if (Poison > 0)
damage += Poison * Utility.RandomMinMax(3, 6);
if (Disease > 0)
damage += Disease * Utility.RandomMinMax(3, 6);
if (Water > 2)
damage += (Water - 2) * Utility.RandomMinMax(3, 6);
else if (Water < 2)
damage += (2 - Water) * Utility.RandomMinMax(3, 6);
Hits -= damage;
return Plant.IsGrowable && Plant.PlantStatus != PlantStatus.BowlOfDirt;
}
private void Grow()
{
if (Health < PlantHealth.Healthy)
{
GrowthIndicator = PlantGrowthIndicator.NotHealthy;
}
else if (FertileDirt && Plant.PlantStatus <= PlantStatus.Stage5 && Utility.RandomDouble() < 0.1)
{
int curStage = (int)Plant.PlantStatus;
Plant.PlantStatus = (PlantStatus)(curStage + 2);
GrowthIndicator = PlantGrowthIndicator.DoubleGrown;
}
else if (Plant.PlantStatus < PlantStatus.Stage9)
{
int curStage = (int)Plant.PlantStatus;
Plant.PlantStatus = (PlantStatus)(curStage + 1);
GrowthIndicator = PlantGrowthIndicator.Grown;
}
else
{
if (Pollinated && LeftSeeds > 0 && Plant.Reproduces)
{
LeftSeeds--;
AvailableSeeds++;
}
if (LeftResources > 0 && PlantResourceInfo.GetInfo(Plant.PlantType, Plant.PlantHue) != null)
{
LeftResources--;
AvailableResources++;
}
GrowthIndicator = PlantGrowthIndicator.Grown;
}
if (Plant.PlantStatus >= PlantStatus.Stage9 && !Pollinated)
{
Pollinated = true;
SeedType = Plant.PlantType;
SeedHue = Plant.PlantHue;
}
}
private void UpdateMaladies()
{
double infestationChance = 0.30 - StrengthPotion * 0.075 + (Water - 2) * 0.10;
PlantTypeInfo typeInfo = PlantTypeInfo.GetInfo(Plant.PlantType);
if (typeInfo.Flowery)
infestationChance += 0.10;
if (PlantHueInfo.IsBright(Plant.PlantHue))
infestationChance += 0.10;
if (Utility.RandomDouble() < infestationChance)
Infestation++;
double fungusChance = 0.15 - StrengthPotion * 0.075 + (Water - 2) * 0.10;
if (Utility.RandomDouble() < fungusChance)
Fungus++;
if (Water > 2 || Utility.RandomDouble() < 0.9)
Water--;
if (PoisonPotion > 0)
{
Poison += PoisonPotion;
PoisonPotion = 0;
}
if (CurePotion > 0)
{
Disease += CurePotion;
CurePotion = 0;
}
StrengthPotion = 0;
}
public void Save(GenericWriter writer)
{
writer.Write(2); // version
writer.Write(FertileDirt);
writer.Write(NextGrowth);
writer.Write((int)GrowthIndicator);
writer.Write(m_Water);
writer.Write(m_Hits);
writer.Write(m_Infestation);
writer.Write(m_Fungus);
writer.Write(m_Poison);
writer.Write(m_Disease);
writer.Write(m_PoisonPotion);
writer.Write(m_CurePotion);
writer.Write(m_HealPotion);
writer.Write(m_StrengthPotion);
writer.Write(Pollinated);
writer.Write((int)m_SeedType);
writer.Write((int)m_SeedHue);
writer.Write(m_AvailableSeeds);
writer.Write(m_LeftSeeds);
writer.Write(m_AvailableResources);
writer.Write(m_LeftResources);
}
}
}

View file

@ -0,0 +1,365 @@
namespace Server.Engines.Plants
{
public enum PlantType
{
CampionFlowers,
Poppies,
Snowdrops,
Bulrushes,
Lilies,
PampasGrass,
Rushes,
ElephantEarPlant,
Fern,
PonytailPalm,
SmallPalm,
CenturyPlant,
WaterPlant,
SnakePlant,
PricklyPearCactus,
BarrelCactus,
TribarrelCactus,
CommonGreenBonsai,
CommonPinkBonsai,
UncommonGreenBonsai,
UncommonPinkBonsai,
RareGreenBonsai,
RarePinkBonsai,
ExceptionalBonsai,
ExoticBonsai,
Cactus,
FlaxFlowers,
FoxgloveFlowers,
HopsEast,
OrfluerFlowers,
CypressTwisted,
HedgeShort,
JuniperBush,
SnowdropPatch,
Cattails,
PoppyPatch,
SpiderTree,
WaterLily,
CypressStraight,
HedgeTall,
HopsSouth,
SugarCanes,
CocoaTree
}
public enum PlantCategory
{
Default,
Common = 1063335, //
Uncommon = 1063336, //
Rare = 1063337, // Bonsai
Exceptional = 1063341, //
Exotic = 1063342, //
Peculiar = 1080528,
Fragrant = 1080529
}
public class PlantTypeInfo
{
private static PlantTypeInfo[] m_Table =
{
new PlantTypeInfo(0xC83, 0, 0, PlantType.CampionFlowers, false, true, true, true, PlantCategory.Default),
new PlantTypeInfo(0xC86, 0, 0, PlantType.Poppies, false, true, true, true, PlantCategory.Default),
new PlantTypeInfo(0xC88, 0, 10, PlantType.Snowdrops, false, true, true, true, PlantCategory.Default),
new PlantTypeInfo(0xC94, -15, 0, PlantType.Bulrushes, false, true, true, true, PlantCategory.Default),
new PlantTypeInfo(0xC8B, 0, 0, PlantType.Lilies, false, true, true, true, PlantCategory.Default),
new PlantTypeInfo(0xCA5, -8, 0, PlantType.PampasGrass, false, true, true, true, PlantCategory.Default),
new PlantTypeInfo(0xCA7, -10, 0, PlantType.Rushes, false, true, true, true, PlantCategory.Default),
new PlantTypeInfo(0xC97, -20, 0, PlantType.ElephantEarPlant, true, false, true, true, PlantCategory.Default),
new PlantTypeInfo(0xC9F, -20, 0, PlantType.Fern, false, false, true, true, PlantCategory.Default),
new PlantTypeInfo(0xCA6, -16, -5, PlantType.PonytailPalm, false, false, true, true, PlantCategory.Default),
new PlantTypeInfo(0xC9C, -5, -10, PlantType.SmallPalm, false, false, true, true, PlantCategory.Default),
new PlantTypeInfo(0xD31, 0, -27, PlantType.CenturyPlant, true, false, true, true, PlantCategory.Default),
new PlantTypeInfo(0xD04, 0, 10, PlantType.WaterPlant, true, false, true, true, PlantCategory.Default),
new PlantTypeInfo(0xCA9, 0, 0, PlantType.SnakePlant, true, false, true, true, PlantCategory.Default),
new PlantTypeInfo(0xD2C, 0, 10, PlantType.PricklyPearCactus, false, false, true, true, PlantCategory.Default),
new PlantTypeInfo(0xD26, 0, 10, PlantType.BarrelCactus, false, false, true, true, PlantCategory.Default),
new PlantTypeInfo(0xD27, 0, 10, PlantType.TribarrelCactus, false, false, true, true, PlantCategory.Default),
new PlantTypeInfo(0x28DC, -5, 5, PlantType.CommonGreenBonsai, true, false, false, false, PlantCategory.Common),
new PlantTypeInfo(0x28DF, -5, 5, PlantType.CommonPinkBonsai, true, false, false, false, PlantCategory.Common),
new PlantTypeInfo(0x28DD, -5, 5, PlantType.UncommonGreenBonsai, true, false, false, false,
PlantCategory.Uncommon),
new PlantTypeInfo(0x28E0, -5, 5, PlantType.UncommonPinkBonsai, true, false, false, false,
PlantCategory.Uncommon),
new PlantTypeInfo(0x28DE, -5, 5, PlantType.RareGreenBonsai, true, false, false, false, PlantCategory.Rare),
new PlantTypeInfo(0x28E1, -5, 5, PlantType.RarePinkBonsai, true, false, false, false, PlantCategory.Rare),
new PlantTypeInfo(0x28E2, -5, 5, PlantType.ExceptionalBonsai, true, false, false, false,
PlantCategory.Exceptional),
new PlantTypeInfo(0x28E3, -5, 5, PlantType.ExoticBonsai, true, false, false, false, PlantCategory.Exotic),
new PlantTypeInfo(0x0D25, 0, 0, PlantType.Cactus, false, false, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x1A9A, 5, 10, PlantType.FlaxFlowers, false, true, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x0C84, 0, 0, PlantType.FoxgloveFlowers, false, true, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x1A9F, 5, -25, PlantType.HopsEast, false, false, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x0CC1, 0, 0, PlantType.OrfluerFlowers, false, true, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x0CFE, -45, -30, PlantType.CypressTwisted, false, false, false, false,
PlantCategory.Peculiar),
new PlantTypeInfo(0x0C8F, 0, 0, PlantType.HedgeShort, false, false, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x0CC8, 0, 0, PlantType.JuniperBush, true, false, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x0C8E, -20, 0, PlantType.SnowdropPatch, false, true, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x0CB7, 0, 0, PlantType.Cattails, false, false, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x0CBE, -20, 0, PlantType.PoppyPatch, false, true, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x0CC9, 0, 0, PlantType.SpiderTree, false, false, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x0DC1, -5, 15, PlantType.WaterLily, false, true, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x0CFB, -45, -30, PlantType.CypressStraight, false, false, false, false,
PlantCategory.Peculiar),
new PlantTypeInfo(0x0DB8, 0, -20, PlantType.HedgeTall, false, false, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x1AA1, 10, -25, PlantType.HopsSouth, false, false, false, false, PlantCategory.Peculiar),
new PlantTypeInfo(0x246C, -25, -20, PlantType.SugarCanes, false, false, false, false, PlantCategory.Peculiar,
1114898, 1114898, 1094702, 1094703, 1095221, 1113715),
new PlantTypeInfo(0xC9E, -40, -30, PlantType.CocoaTree, false, false, false, true, PlantCategory.Fragrant,
1080536, 1080536, 1080534, 1080531, 1080533, 1113716)
};
private int m_PlantLabelDecorative;
private int m_PlantLabelFullGrown;
private int m_PlantLabelPlant;
// Cliloc overrides
private int m_PlantLabelSeed;
private int m_SeedLabel;
private int m_SeedLabelPlural;
private PlantTypeInfo(int itemID, int offsetX, int offsetY, PlantType plantType, bool containsPlant, bool flowery,
bool crossable, bool reproduces, PlantCategory plantCategory, int plantLabelSeed = -1, int plantLabelPlant = -1,
int plantLabelFullGrown = -1, int plantLabelDecorative = -1, int seedLabel = -1, int seedLabelPlural = -1)
{
ItemID = itemID;
OffsetX = offsetX;
OffsetY = offsetY;
PlantType = plantType;
ContainsPlant = containsPlant;
Flowery = flowery;
Crossable = crossable;
Reproduces = reproduces;
PlantCategory = plantCategory;
m_PlantLabelSeed = plantLabelSeed;
m_PlantLabelPlant = plantLabelPlant;
m_PlantLabelFullGrown = plantLabelFullGrown;
m_PlantLabelDecorative = plantLabelDecorative;
m_SeedLabel = seedLabel;
m_SeedLabelPlural = seedLabelPlural;
}
public int ItemID{ get; }
public int OffsetX{ get; }
public int OffsetY{ get; }
public PlantType PlantType{ get; }
public PlantCategory PlantCategory{ get; }
public int Name => ItemID < 0x4000 ? 1020000 + ItemID : 1078872 + ItemID;
public bool ContainsPlant{ get; }
public bool Flowery{ get; }
public bool Crossable{ get; }
public bool Reproduces{ get; }
public static PlantTypeInfo GetInfo(PlantType plantType)
{
int index = (int)plantType;
if (index >= 0 && index < m_Table.Length)
return m_Table[index];
return m_Table[0];
}
public static PlantType RandomFirstGeneration()
{
switch (Utility.Random(3))
{
case 0: return PlantType.CampionFlowers;
case 1: return PlantType.Fern;
default: return PlantType.TribarrelCactus;
}
}
public static PlantType RandomPeculiarGroupOne()
{
switch (Utility.Random(6))
{
case 0: return PlantType.Cactus;
case 1: return PlantType.FlaxFlowers;
case 2: return PlantType.FoxgloveFlowers;
case 3: return PlantType.HopsEast;
case 4: return PlantType.CocoaTree;
default: return PlantType.OrfluerFlowers;
}
}
public static PlantType RandomPeculiarGroupTwo()
{
switch (Utility.Random(5))
{
case 0: return PlantType.CypressTwisted;
case 1: return PlantType.HedgeShort;
case 2: return PlantType.JuniperBush;
case 3: return PlantType.CocoaTree;
default: return PlantType.SnowdropPatch;
}
}
public static PlantType RandomPeculiarGroupThree()
{
switch (Utility.Random(5))
{
case 0: return PlantType.Cattails;
case 1: return PlantType.PoppyPatch;
case 2: return PlantType.SpiderTree;
case 3: return PlantType.CocoaTree;
default: return PlantType.WaterLily;
}
}
public static PlantType RandomPeculiarGroupFour()
{
switch (Utility.Random(5))
{
case 0: return PlantType.CypressStraight;
case 1: return PlantType.HedgeTall;
case 2: return PlantType.HopsSouth;
case 3: return PlantType.CocoaTree;
default: return PlantType.SugarCanes;
}
}
public static PlantType RandomBonsai(double increaseRatio)
{
/* Chances of each plant type are equal to the chances of the previous plant type * increaseRatio:
* E.g.:
* chances_of_uncommon = chances_of_common * increaseRatio
* chances_of_rare = chances_of_uncommon * increaseRatio
* ...
*
* If increaseRatio < 1 -> rare plants are actually rarer than the others
* If increaseRatio > 1 -> rare plants are actually more common than the others (it might be the case with certain monsters)
*
* If a plant type (common, uncommon, ...) has 2 different colors, they have the same chances:
* chances_of_green_common = chances_of_pink_common = chances_of_common / 2
* ...
*/
double k1 = increaseRatio >= 0.0 ? increaseRatio : 0.0;
double k2 = k1 * k1;
double k3 = k2 * k1;
double k4 = k3 * k1;
double exp1 = k1 + 1.0;
double exp2 = k2 + exp1;
double exp3 = k3 + exp2;
double exp4 = k4 + exp3;
double rand = Utility.RandomDouble();
if (rand < 0.5 / exp4)
return PlantType.CommonGreenBonsai;
if (rand < 1.0 / exp4)
return PlantType.CommonPinkBonsai;
if (rand < (k1 * 0.5 + 1.0) / exp4)
return PlantType.UncommonGreenBonsai;
if (rand < exp1 / exp4)
return PlantType.UncommonPinkBonsai;
if (rand < (k2 * 0.5 + exp1) / exp4)
return PlantType.RareGreenBonsai;
if (rand < exp2 / exp4)
return PlantType.RarePinkBonsai;
if (rand < exp3 / exp4)
return PlantType.ExceptionalBonsai;
return PlantType.ExoticBonsai;
}
public static bool IsCrossable(PlantType plantType)
{
return GetInfo(plantType).Crossable;
}
public static PlantType Cross(PlantType first, PlantType second)
{
if (!IsCrossable(first) || !IsCrossable(second))
return PlantType.CampionFlowers;
int firstIndex = (int)first;
int secondIndex = (int)second;
if (firstIndex + 1 == secondIndex || firstIndex == secondIndex + 1)
return Utility.RandomBool() ? first : second;
return (PlantType)((firstIndex + secondIndex) / 2);
}
public static bool CanReproduce(PlantType plantType)
{
return GetInfo(plantType).Reproduces;
}
public int GetPlantLabelSeed(PlantHueInfo hueInfo)
{
if (m_PlantLabelSeed != -1)
return m_PlantLabelSeed;
return
hueInfo.IsBright()
? 1061887
: 1061888; // a ~1_val~ of ~2_val~ dirt with a ~3_val~ [bright] ~4_val~ ~5_val~ ~6_val~
}
public int GetPlantLabelPlant(PlantHueInfo hueInfo)
{
if (m_PlantLabelPlant != -1)
return m_PlantLabelPlant;
if (ContainsPlant)
return
hueInfo.IsBright()
? 1060832
: 1060831; // a ~1_val~ of ~2_val~ dirt with a ~3_val~ [bright] ~4_val~ ~5_val~
return
hueInfo.IsBright()
? 1061887
: 1061888; // a ~1_val~ of ~2_val~ dirt with a ~3_val~ [bright] ~4_val~ ~5_val~ ~6_val~
}
public int GetPlantLabelFullGrown(PlantHueInfo hueInfo)
{
if (m_PlantLabelFullGrown != -1)
return m_PlantLabelFullGrown;
if (ContainsPlant)
return hueInfo.IsBright() ? 1061891 : 1061889; // a ~1_HEALTH~ [bright] ~2_COLOR~ ~3_NAME~
return hueInfo.IsBright() ? 1061892 : 1061890; // a ~1_HEALTH~ [bright] ~2_COLOR~ ~3_NAME~ plant
}
public int GetPlantLabelDecorative(PlantHueInfo hueInfo)
{
if (m_PlantLabelDecorative != -1)
return m_PlantLabelDecorative;
return hueInfo.IsBright() ? 1074267 : 1070973; // a decorative [bright] ~1_COLOR~ ~2_TYPE~
}
public int GetSeedLabel(PlantHueInfo hueInfo)
{
if (m_SeedLabel != -1)
return m_SeedLabel;
return hueInfo.IsBright() ? 1061918 : 1061917; // [bright] ~1_COLOR~ ~2_TYPE~ seed
}
public int GetSeedLabelPlural(PlantHueInfo hueInfo)
{
if (m_SeedLabelPlural != -1)
return m_SeedLabelPlural;
return hueInfo.IsBright() ? 1113493 : 1113492; // ~1_amount~ [bright] ~2_color~ ~3_type~ seeds
}
}
}

View file

@ -0,0 +1,91 @@
using Server.Targeting;
namespace Server.Engines.Plants
{
public class PollinateTarget : Target
{
private PlantItem m_Plant;
public PollinateTarget(PlantItem plant) : base(3, true, TargetFlags.None)
{
m_Plant = plant;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (!m_Plant.Deleted && m_Plant.PlantStatus < PlantStatus.DecorativePlant &&
from.InRange(m_Plant.GetWorldLocation(), 3))
{
if (!m_Plant.IsUsableBy(from))
{
m_Plant.LabelTo(from,
1061856); // You must have the item in your backpack or locked down in order to use it.
}
else if (!m_Plant.IsCrossable)
{
m_Plant.LabelTo(from, 1053050); // You cannot gather pollen from a mutated plant!
}
else if (!m_Plant.PlantSystem.PollenProducing)
{
m_Plant.LabelTo(from, 1053051); // You cannot gather pollen from a plant in this stage of development!
}
else if (m_Plant.PlantSystem.Health < PlantHealth.Healthy)
{
m_Plant.LabelTo(from, 1053052); // You cannot gather pollen from an unhealthy plant!
}
else
{
if (!(targeted is PlantItem targ) || targ.PlantStatus >= PlantStatus.DecorativePlant ||
targ.PlantStatus <= PlantStatus.BowlOfDirt)
{
m_Plant.LabelTo(from, 1053070); // You can only pollinate other specially grown plants!
}
else if (!targ.IsUsableBy(from))
{
targ.LabelTo(from,
1061856); // You must have the item in your backpack or locked down in order to use it.
}
else if (!targ.IsCrossable)
{
targ.LabelTo(from, 1053073); // You cannot cross-pollinate with a mutated plant!
}
else if (!targ.PlantSystem.PollenProducing)
{
targ.LabelTo(from, 1053074); // This plant is not in the flowering stage. You cannot pollinate it!
}
else if (targ.PlantSystem.Health < PlantHealth.Healthy)
{
targ.LabelTo(from, 1053075); // You cannot pollinate an unhealthy plant!
}
else if (targ.PlantSystem.Pollinated)
{
targ.LabelTo(from, 1053072); // This plant has already been pollinated!
}
else if (targ == m_Plant)
{
targ.PlantSystem.Pollinated = true;
targ.PlantSystem.SeedType = m_Plant.PlantType;
targ.PlantSystem.SeedHue = m_Plant.PlantHue;
targ.LabelTo(from, 1053071); // You pollinate the plant with its own pollen.
}
else
{
targ.PlantSystem.Pollinated = true;
targ.PlantSystem.SeedType = PlantTypeInfo.Cross(m_Plant.PlantType, targ.PlantType);
targ.PlantSystem.SeedHue = PlantHueInfo.Cross(m_Plant.PlantHue, targ.PlantHue);
targ.LabelTo(from, 1053076); // You successfully cross-pollinate the plant.
}
}
}
}
protected override void OnTargetFinish(Mobile from)
{
if (!m_Plant.Deleted && m_Plant.PlantStatus < PlantStatus.DecorativePlant &&
m_Plant.PlantStatus != PlantStatus.BowlOfDirt && from.InRange(m_Plant.GetWorldLocation(), 3) &&
m_Plant.IsUsableBy(from)) from.SendGump(new ReproductionGump(m_Plant));
}
}
}

View file

@ -0,0 +1,263 @@
using Server.Gumps;
using Server.Network;
namespace Server.Engines.Plants
{
public class ReproductionGump : Gump
{
private PlantItem m_Plant;
public ReproductionGump(PlantItem plant) : base(20, 20)
{
m_Plant = plant;
DrawBackground();
AddButton(70, 67, 0xD4, 0xD4, 1); // Main menu
AddItem(57, 65, 0x1600);
AddLabel(108, 67, 0x835, "Reproduction");
if (m_Plant.PlantStatus == PlantStatus.Stage9)
{
AddButton(212, 67, 0xD4, 0xD4, 2); // Set to decorative
AddItem(202, 68, 0xC61);
AddLabel(216, 66, 0x21, "/");
}
AddButton(80, 116, 0xD4, 0xD4, 3); // Pollination
AddItem(66, 117, 0x1AA2);
AddPollinationState(106, 116);
AddButton(128, 116, 0xD4, 0xD4, 4); // Resources
AddItem(113, 120, 0x1021);
AddResourcesState(149, 116);
AddButton(177, 116, 0xD4, 0xD4, 5); // Seeds
AddItem(160, 121, 0xDCF);
AddSeedsState(199, 116);
AddButton(70, 163, 0xD2, 0xD2, 6); // Gather pollen
AddItem(56, 164, 0x1AA2);
AddButton(138, 163, 0xD2, 0xD2, 7); // Gather resources
AddItem(123, 167, 0x1021);
AddButton(212, 163, 0xD2, 0xD2, 8); // Gather seeds
AddItem(195, 168, 0xDCF);
}
private void DrawBackground()
{
AddBackground(50, 50, 200, 150, 0xE10);
AddImage(60, 90, 0xE17);
AddImage(120, 90, 0xE17);
AddImage(60, 145, 0xE17);
AddImage(120, 145, 0xE17);
AddItem(45, 45, 0xCEF);
AddItem(45, 118, 0xCF0);
AddItem(211, 45, 0xCEB);
AddItem(211, 118, 0xCEC);
}
private void AddPollinationState(int x, int y)
{
PlantSystem system = m_Plant.PlantSystem;
if (!system.PollenProducing)
AddLabel(x, y, 0x35, "-");
else if (!system.Pollinated)
AddLabel(x, y, 0x21, "!");
else
AddLabel(x, y, 0x3F, "+");
}
private void AddResourcesState(int x, int y)
{
PlantResourceInfo resInfo = PlantResourceInfo.GetInfo(m_Plant.PlantType, m_Plant.PlantHue);
PlantSystem system = m_Plant.PlantSystem;
int totalResources = system.AvailableResources + system.LeftResources;
if (resInfo == null || totalResources == 0)
AddLabel(x + 5, y, 0x21, "X");
else
AddLabel(x, y, PlantHueInfo.GetInfo(m_Plant.PlantHue).GumpHue,
$"{system.AvailableResources}/{totalResources}");
}
private void AddSeedsState(int x, int y)
{
PlantSystem system = m_Plant.PlantSystem;
int totalSeeds = system.AvailableSeeds + system.LeftSeeds;
if (!m_Plant.Reproduces || totalSeeds == 0)
AddLabel(x + 5, y, 0x21, "X");
else
AddLabel(x, y, PlantHueInfo.GetInfo(system.SeedHue).GumpHue,
$"{system.AvailableSeeds}/{totalSeeds}");
}
public override void OnResponse(NetState sender, RelayInfo info)
{
Mobile from = sender.Mobile;
if (info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus >= PlantStatus.DecorativePlant ||
m_Plant.PlantStatus == PlantStatus.BowlOfDirt)
return;
if (info.ButtonID >= 6 && info.ButtonID <= 8 && !from.InRange(m_Plant.GetWorldLocation(), 3))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away.
return;
}
if (!m_Plant.IsUsableBy(from))
{
m_Plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it.
return;
}
switch (info.ButtonID)
{
case 1: // Main menu
{
from.SendGump(new MainPlantGump(m_Plant));
break;
}
case 2: // Set to decorative
{
if (m_Plant.PlantStatus == PlantStatus.Stage9) from.SendGump(new SetToDecorativeGump(m_Plant));
break;
}
case 3: // Pollination
{
from.Send(new DisplayHelpTopic(67, true)); // POLLINATION STATE
from.SendGump(new ReproductionGump(m_Plant));
break;
}
case 4: // Resources
{
from.Send(new DisplayHelpTopic(69, true)); // RESOURCE PRODUCTION
from.SendGump(new ReproductionGump(m_Plant));
break;
}
case 5: // Seeds
{
from.Send(new DisplayHelpTopic(68, true)); // SEED PRODUCTION
from.SendGump(new ReproductionGump(m_Plant));
break;
}
case 6: // Gather pollen
{
if (!m_Plant.IsCrossable)
{
m_Plant.LabelTo(from, 1053050); // You cannot gather pollen from a mutated plant!
}
else if (!m_Plant.PlantSystem.PollenProducing)
{
m_Plant.LabelTo(from,
1053051); // You cannot gather pollen from a plant in this stage of development!
}
else if (m_Plant.PlantSystem.Health < PlantHealth.Healthy)
{
m_Plant.LabelTo(from, 1053052); // You cannot gather pollen from an unhealthy plant!
}
else
{
from.Target = new PollinateTarget(m_Plant);
from.SendLocalizedMessage(1053054); // Target the plant you wish to cross-pollinate to.
break;
}
from.SendGump(new ReproductionGump(m_Plant));
break;
}
case 7: // Gather resources
{
PlantResourceInfo resInfo = PlantResourceInfo.GetInfo(m_Plant.PlantType, m_Plant.PlantHue);
PlantSystem system = m_Plant.PlantSystem;
if (resInfo == null)
{
if (m_Plant.IsCrossable)
m_Plant.LabelTo(from, 1053056); // This plant has no resources to gather!
else
m_Plant.LabelTo(from, 1053055); // Mutated plants do not produce resources!
}
else if (system.AvailableResources == 0)
{
m_Plant.LabelTo(from, 1053056); // This plant has no resources to gather!
}
else
{
Item resource = resInfo.CreateResource();
if (from.PlaceInBackpack(resource))
{
system.AvailableResources--;
m_Plant.LabelTo(from, 1053059); // You gather resources from the plant.
}
else
{
resource.Delete();
m_Plant.LabelTo(from,
1053058); // You attempt to gather as many resources as you can hold, but your backpack is full.
}
}
from.SendGump(new ReproductionGump(m_Plant));
break;
}
case 8: // Gather seeds
{
PlantSystem system = m_Plant.PlantSystem;
if (!m_Plant.Reproduces)
{
m_Plant.LabelTo(from, 1053060); // Mutated plants do not produce seeds!
}
else if (system.AvailableSeeds == 0)
{
m_Plant.LabelTo(from, 1053061); // This plant has no seeds to gather!
}
else
{
Seed seed = new Seed(system.SeedType, system.SeedHue, true);
if (from.PlaceInBackpack(seed))
{
system.AvailableSeeds--;
m_Plant.LabelTo(from, 1053063); // You gather seeds from the plant.
}
else
{
seed.Delete();
m_Plant.LabelTo(from,
1053062); // You attempt to gather as many seeds as you can hold, but your backpack is full.
}
}
from.SendGump(new ReproductionGump(m_Plant));
break;
}
}
}
}
}

View file

@ -0,0 +1,225 @@
using Server.Targeting;
namespace Server.Engines.Plants
{
public class Seed : Item
{
private PlantHue m_PlantHue;
private PlantType m_PlantType;
private bool m_ShowType;
[Constructible]
public Seed() : this(PlantTypeInfo.RandomFirstGeneration(), PlantHueInfo.RandomFirstGeneration())
{
}
[Constructible]
public Seed(PlantType plantType, PlantHue plantHue, bool showType = false) : base(0xDCF)
{
Weight = 1.0;
Stackable = Core.SA;
m_PlantType = plantType;
m_PlantHue = plantHue;
m_ShowType = showType;
Hue = PlantHueInfo.GetInfo(plantHue).Hue;
}
public Seed(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public PlantType PlantType
{
get => m_PlantType;
set
{
m_PlantType = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public PlantHue PlantHue
{
get => m_PlantHue;
set
{
m_PlantHue = value;
Hue = PlantHueInfo.GetInfo(value).Hue;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.GameMaster)]
public bool ShowType
{
get => m_ShowType;
set
{
m_ShowType = value;
InvalidateProperties();
}
}
public override int LabelNumber => 1060810; // seed
public override bool ForceShowProperties => ObjectPropertyList.Enabled;
public static Seed RandomBonsaiSeed()
{
return RandomBonsaiSeed(0.5);
}
public static Seed RandomBonsaiSeed(double increaseRatio)
{
return new Seed(PlantTypeInfo.RandomBonsai(increaseRatio), PlantHue.Plain);
}
public static Seed RandomPeculiarSeed(int group)
{
switch (group)
{
case 1: return new Seed(PlantTypeInfo.RandomPeculiarGroupOne(), PlantHue.Plain);
case 2: return new Seed(PlantTypeInfo.RandomPeculiarGroupTwo(), PlantHue.Plain);
case 3: return new Seed(PlantTypeInfo.RandomPeculiarGroupThree(), PlantHue.Plain);
default: return new Seed(PlantTypeInfo.RandomPeculiarGroupFour(), PlantHue.Plain);
}
}
private int GetLabel(out string args)
{
PlantTypeInfo typeInfo = PlantTypeInfo.GetInfo(m_PlantType);
PlantHueInfo hueInfo = PlantHueInfo.GetInfo(m_PlantHue);
int title;
if (m_ShowType || typeInfo.PlantCategory == PlantCategory.Default)
title = hueInfo.Name;
else
title = (int)typeInfo.PlantCategory;
if (Amount == 1)
{
if (m_ShowType)
{
args = $"#{title}\t#{typeInfo.Name}";
return typeInfo.GetSeedLabel(hueInfo);
}
args = $"#{title}";
return hueInfo.IsBright() ? 1060839 : 1060838; // [bright] ~1_val~ seed
}
if (m_ShowType)
{
args = $"{Amount}\t#{title}\t#{typeInfo.Name}";
return typeInfo.GetSeedLabelPlural(hueInfo);
}
args = $"{Amount}\t#{title}";
return hueInfo.IsBright() ? 1113491 : 1113490; // ~1_amount~ [bright] ~2_val~ seeds
}
public override void AddNameProperty(ObjectPropertyList list)
{
list.Add(GetLabel(out string args), args);
}
public override void OnSingleClick(Mobile from)
{
LabelTo(from, GetLabel(out string args), args);
}
public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042664); // You must have the object in your backpack to use it.
return;
}
from.Target = new InternalTarget(this);
LabelTo(from, 1061916); // Choose a bowl of dirt to plant this seed in.
}
public override bool StackWith(Mobile from, Item dropped, bool playSound)
{
return dropped is Seed other && other.PlantType == m_PlantType && other.PlantHue == m_PlantHue &&
other.ShowType == m_ShowType && base.StackWith(from, other, playSound);
}
public override void OnAfterDuped(Item newItem)
{
if (!(newItem is Seed newSeed))
return;
newSeed.PlantType = m_PlantType;
newSeed.PlantHue = m_PlantHue;
newSeed.ShowType = m_ShowType;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(2); // version
writer.Write((int)m_PlantType);
writer.Write((int)m_PlantHue);
writer.Write(m_ShowType);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
m_PlantType = (PlantType)reader.ReadInt();
m_PlantHue = (PlantHue)reader.ReadInt();
m_ShowType = reader.ReadBool();
if (Weight != 1.0)
Weight = 1.0;
if (version < 1)
Stackable = Core.SA;
if (version < 2 && PlantHueInfo.IsCrossable(m_PlantHue))
m_PlantHue |= PlantHue.Reproduces;
}
private class InternalTarget : Target
{
private Seed m_Seed;
public InternalTarget(Seed seed) : base(-1, false, TargetFlags.None)
{
m_Seed = seed;
CheckLOS = false;
}
protected override void OnTarget(Mobile from, object targeted)
{
if (m_Seed.Deleted)
return;
if (!m_Seed.IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042664); // You must have the object in your backpack to use it.
return;
}
if (targeted is PlantItem plant)
plant.PlantSeed(from, m_Seed);
else if (targeted is Item item)
item.LabelTo(from, 1061919); // You must use a seed on a bowl of dirt!
else
from.SendLocalizedMessage(1061919); // You must use a seed on a bowl of dirt!
}
}
}
}

View file

@ -0,0 +1,84 @@
using Server.Gumps;
using Server.Network;
namespace Server.Engines.Plants
{
public class SetToDecorativeGump : Gump
{
private PlantItem m_Plant;
public SetToDecorativeGump(PlantItem plant) : base(20, 20)
{
m_Plant = plant;
DrawBackground();
AddLabel(115, 85, 0x44, "Set plant");
AddLabel(82, 105, 0x44, "to decorative mode?");
AddButton(98, 140, 0x47E, 0x480, 1); // Cancel
AddButton(138, 141, 0xD2, 0xD2, 2); // Help
AddLabel(143, 141, 0x835, "?");
AddButton(168, 140, 0x481, 0x483, 3); // Ok
}
private void DrawBackground()
{
AddBackground(50, 50, 200, 150, 0xE10);
AddItem(25, 45, 0xCEB);
AddItem(25, 118, 0xCEC);
AddItem(227, 45, 0xCEF);
AddItem(227, 118, 0xCF0);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
Mobile from = sender.Mobile;
if (info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus != PlantStatus.Stage9)
return;
if (info.ButtonID == 3 && !from.InRange(m_Plant.GetWorldLocation(), 3))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3E9, 500446); // That is too far away.
return;
}
if (!m_Plant.IsUsableBy(from))
{
m_Plant.LabelTo(from, 1061856); // You must have the item in your backpack or locked down in order to use it.
return;
}
switch (info.ButtonID)
{
case 1: // Cancel
{
from.SendGump(new ReproductionGump(m_Plant));
break;
}
case 2: // Help
{
from.Send(new DisplayHelpTopic(70, true)); // DECORATIVE MODE
from.SendGump(new SetToDecorativeGump(m_Plant));
break;
}
case 3: // Ok
{
m_Plant.PlantStatus = PlantStatus.DecorativePlant;
m_Plant.LabelTo(from,
1053077); // You prune the plant. This plant will no longer produce resources or seeds, but will require no upkeep.
break;
}
}
}
}
}