From 7b8583dcea4cdb4af462393c7f08c8ba3ac7d28e Mon Sep 17 00:00:00 2001 From: SilicaAndPina Date: Tue, 16 Feb 2021 01:08:54 +1300 Subject: [PATCH] Add Training Pens --- DataCollection/gamedata.json | 43 ++++++-- .../HorseIsleServer/Game/Messages.cs | 32 +++++- .../HorseIsleServer/Game/Meta.cs | 57 +++++++++- .../HorseIsleServer/Game/Services/Trainer.cs | 31 ++++++ .../HorseIsleServer/Server/GameDataJson.cs | 23 ++++ .../HorseIsleServer/Server/GameServer.cs | 100 ++++++++++++++++++ .../HorseIsleServer/Server/PacketBuilder.cs | 1 + 7 files changed, 274 insertions(+), 13 deletions(-) create mode 100644 Horse Isle Server/HorseIsleServer/Game/Services/Trainer.cs diff --git a/DataCollection/gamedata.json b/DataCollection/gamedata.json index 220f289..3c5ae8c 100755 --- a/DataCollection/gamedata.json +++ b/DataCollection/gamedata.json @@ -64,9 +64,9 @@ "pot_of_gold":"YEA! You found the fabled pot of gold at the end of the rainbow! It was worth $%PRIZE%!" }, "new_user":{ - "starting_message":"Welcome Newest Rider of Horse Isle!
Start by talking to Welcome Willy in the cabin. Click the TALK button by his name in the right hand window. He will know the location of a buried treasure on this island! Move to the spot he describes using the arrow keys. Then Click the WRENCH Icon at the lower right.", - "starting_x":522, - "starting_y":138 + "starting_message":"Welcome Newest Rider of Horse Isle!
Start by talking to Welcome Willy in the cabin. Click the TALK button by his name in the right hand window. He will know the location of a buried treasure on this island! Move to the spot he describes using the arrow keys. Then Click the WRENCH Icon at the lower right.", + "starting_x":522, + "starting_y":138 }, "bank":{ "deposit_format":"You deposited $%MONEY% into the bank.", @@ -194,6 +194,14 @@ "venus_flytrap_format":"The Giant Venus Flytrap chomped at you!
OUCH!!
It chomped your pocket, taking $%MONEY% with it!!", "password_input":"
^PLReply:|^PS14|ANSWER^R1", "last_poet":"^R1^LLast Player Poet:%USERNAME% ^R1", + "trainer_pen":{ + "train_success":"Your horse, %HORSENAME%, has been successfully trained in %STAT%.", + "train_header":"

Purchase a training session:
Costs: $%PRICE% + thirst/hunger/mood
Benefits: %AMOUNT% %STAT% + %EXPAMOUNT% experience
Click a Horse to Train Them:", + "train_format":"^I252^T6%HORSENAME% (currently:%CURSTAT%/max:%MAXSTAT%) ^B3R%RANDOMID%^R1", + "fully_trained":"^I252^T8%HORSENAME% (Fully Trained with %STAT%)^R1", + "train_again_in":"Your horse cannot be trained again for another %TIME% game minutes.", + "cant_afford":"You cannot afford training at this time!" + }, "santa":{ "hidden_text":"You can pay to have anything in your inventory wrapped.
It costs $10 per wrapping.", "wrap_format":"^I%ICONID%^T6 %NAME%^B4P%RANDOMID%^R1", @@ -239,6 +247,7 @@ "too_hungry":"Your poor horse is too hungry to compete!", "too_thisty":"Your poor horse is too thirsty to compete!", + "farrier":"Your poor horse needs a farrier!", "needs_vet":"Your poor horse needs a vet!", }, "ranch":{ @@ -295,7 +304,7 @@ "train_all":"You attempt to train all of your horses:", "train_success":"
Training %HORSENAME%: +%SPEED%SP +%STRENGTH%ST +%CONFORMATION%CO +%AGILITY%AG +%ENDURANCE%EN +%EXP%exp", "train_bad_mood":"
Training %HORSENAME%: Horse is in way too bad a mood.", - "train_too_thirsty":"
Horse is too thirsty to pay attention.", + "train_too_thirsty":"
Training %HORSENAME%: Horse is too thirsty to pay attention.", "train_cant_train":"
%HORSENAME%: Horse needs to rest %TIME% game minutes.", "fully_rested":"
You have a Barn. All of your horses are fully relaxed now.", "wagon_used":"Your wagon dropped you off at the nearest station." @@ -655,7 +664,7 @@ "all_players_header":"^ATAll Players Online^H", "all_players_alphabetical_header":"^ATAll Players Online Alphabetically^H", - "player_format":"%ICONFORMAT%^T3%USERNAME% (on %TIME% min%IDLE%)^B1L%PLAYERID%B1M%MAPXY%^B1I%PLAYERID%^B1P%USERNAME%^R1", + "player_format":"%ICONFORMAT%^T3%USERNAME% (on %TIME% min%IDLE%)^B1L%PLAYERID%^B1M%MAPXY%^B1I%PLAYERID%^B1P%USERNAME%^R1", "icon_info":"^L [Star = Subscriber] [A = Admin] [M = Moderator]^R1", "idle_text":"(IDLE)", "icon_subbed_3month":415, @@ -680,17 +689,17 @@ {"id":"Trading","value":"Trades"}, {"id":"HorseLease","value":"Horse Leases"}, {"id":"AutoSells","value":"Auto-Sell"}, - {"id":"PegasusTeamup","value":"Pegasus Team-up"}, + {"id":"PegasusTeamup","value":"Pegasus Teamups"}, {"id":"TackShopGiveaway","value":"Horse Giveaway Wins"}, {"id":"QuizWin","value":"Real Time Quiz Wins"}, {"id":"RiddleWin","value":"Real Time Riddles"}, - {"id":"IsleCardsGameWin","value":"Real Time Isle Cards Win"}, - {"id":"HorsePawn","value":"Sold Horse To Pawneer"}, + {"id":"IsleCardsGameWin","value":"Real Time Isle Cards Wins"}, + {"id":"HorsePawn","value":"Sold Horse To Pawneers"}, {"id":"WaterbaloonGameWin","value":"Water Balloon Wins"}, - {"id":"UnicornTeamup","value":"Unicorn Team-up"}, + {"id":"UnicornTeamup","value":"Unicorn Teamups"}, {"id":"PotOfGold","value":"Pot Of Golds"}, - {"id":"GameUpdates","value":"Game Updates / Resets"}, - {"id":"UnipegTeamup","value":"UniPeg Team-up"} + {"id":"GameUpdates","value":"Game Updates Resets"}, + {"id":"UnipegTeamup","value":"UniPeg Teamups"} ], }, "stats_page":{ @@ -550022,5 +550031,17 @@ {"id":72,"riddle":"I won't fall apart while moving, as my name might suggest.","answers":["Walkaloosa"],"reason":"Name makes it sound like it would come loose while walking."}, {"id":73,"riddle":"There. In few words: what breed?","answers":["Tersky"],"reason":"When one says something in as few words as possible, one is being \"terse.\""}, {"id":74,"riddle":"No, I'm not made of French maize! I'm an old, proud breed.","answers":["Norman Cob"],"reason":"Normandy is a coastal region of France, and maize grows on a cob."} + ], + "training_pens":[ + {"trainer_id":1,"improves_stat":"speed","improves_amount":10,"thirst_cost":50,"mood_cost":10,"hunger_cost":25,"money_cost":1000,"experience":5}, + {"trainer_id":2,"improves_stat":"strength","improves_amount":10,"thirst_cost":35,"mood_cost":25,"hunger_cost":35,"money_cost":1000,"experience":5}, + {"trainer_id":3,"improves_stat":"conformation","improves_amount":10,"thirst_cost":5,"mood_cost":80,"hunger_cost":5,"money_cost":1000,"experience":10}, + {"trainer_id":4,"improves_stat":"agility","improves_amount":10,"thirst_cost":50,"mood_cost":20,"hunger_cost":50,"money_cost":1000,"experience":10}, + {"trainer_id":5,"improves_stat":"endurance","improves_amount":10,"thirst_cost":80,"mood_cost":20,"hunger_cost":80,"money_cost":1000,"experience":5}, + {"trainer_id":6,"improves_stat":"speed","improves_amount":10,"thirst_cost":50,"mood_cost":10,"hunger_cost":25,"money_cost":750,"experience":5}, + {"trainer_id":7,"improves_stat":"strength","improves_amount":10,"thirst_cost":35,"mood_cost":25,"hunger_cost":35,"money_cost":750,"experience":5}, + {"trainer_id":8,"improves_stat":"conformation","improves_amount":10,"thirst_cost":5,"mood_cost":80,"hunger_cost":5,"money_cost":750,"experience":10}, + {"trainer_id":9,"improves_stat":"agility","improves_amount":10,"thirst_cost":50,"mood_cost":20,"hunger_cost":50,"money_cost":750,"experience":10}, + {"trainer_id":10,"improves_stat":"endurance","improves_amount":10,"thirst_cost":80,"mood_cost":20,"hunger_cost":80,"money_cost":750,"experience":5} ] } diff --git a/Horse Isle Server/HorseIsleServer/Game/Messages.cs b/Horse Isle Server/HorseIsleServer/Game/Messages.cs index 1dbdc76..240eb52 100755 --- a/Horse Isle Server/HorseIsleServer/Game/Messages.cs +++ b/Horse Isle Server/HorseIsleServer/Game/Messages.cs @@ -76,6 +76,14 @@ namespace HISP.Game public static string RanchHorsesFullyRested; public static string RanchWagonDroppedYouOff; + // Training Pen + public static string TrainedInStatFormat; + public static string TrainerHeaderFormat; + public static string TrainerHorseEntryFormat; + public static string TrainerHorseFullyTrainedFormat; + public static string TrainerCantTrainAgainInFormat; + public static string TrainerCantAfford; + // Santa public static string SantaHiddenText; // Text that claims that it costs $10 to wrap a present thats sent to the client but never displayed for some reason. also wrapping is free on pinto so IDEK. public static string SantaWrapItemFormat; @@ -732,6 +740,28 @@ namespace HISP.Game // Click public static string NothingInterestingHere; + + public static string FormatTrainerCantTrainAgainIn(int time) + { + return TrainerCantTrainAgainInFormat.Replace("%TIME%", time.ToString()); + } + public static string FormatTrainerFullyTrained(string horseName, int curStat) + { + return TrainerHorseFullyTrainedFormat.Replace("%HORSENAME%", horseName).Replace("%STAT%", curStat.ToString()); + } + public static string FormatTrainerTrainInEntry(string horseName, int curStat, int maxStat, int randomId) + { + return TrainerHorseEntryFormat.Replace("%HORSENAME%", horseName).Replace("%CURSTAT%", curStat.ToString()).Replace("%MAXSTAT%", maxStat.ToString()).Replace("%RANDOMID%", randomId.ToString()); + } + public static string FormatTrainerHeaderFormat(string stat, int price, int amountInStat, int expamount) + { + return TrainerHeaderFormat.Replace("%STAT%", stat).Replace("%PRICE%", price.ToString("N0", CultureInfo.InvariantCulture)).Replace("%AMOUNT%", amountInStat.ToString("N0", CultureInfo.InvariantCulture)).Replace("%EXPAMOUNT%", expamount.ToString("N0", CultureInfo.InvariantCulture)); + } + public static string FormatTrainedInStatFormat(string horseName, string stat) + { + return TrainedInStatFormat.Replace("%HORSENAME%", horseName).Replace("%STAT%", stat); + } + public static string FormatSantaOpenPresent(string itemName) { return SantaItemOpenedFormat.Replace("%ITEM%", itemName); @@ -1942,7 +1972,7 @@ namespace HISP.Game } public static string FormatDirectChatMessageForSender(string username,string toUsername, string message) { - return DirectChatFormatForSender.Replace("%FROMUSER%", username).Replace("%TOUSER%", toUsername).Replace(" %MESSAGE%", message); + return DirectChatFormatForSender.Replace("%FROMUSER%", username).Replace("%TOUSER%", toUsername).Replace("%MESSAGE%", message); } public static string FormatIdleWarningMessage() { diff --git a/Horse Isle Server/HorseIsleServer/Game/Meta.cs b/Horse Isle Server/HorseIsleServer/Game/Meta.cs index f20f625..9f2378a 100755 --- a/Horse Isle Server/HorseIsleServer/Game/Meta.cs +++ b/Horse Isle Server/HorseIsleServer/Game/Meta.cs @@ -645,6 +645,7 @@ namespace HISP.Game { string message = ""; message += Messages.RanchTrainAllAttempt; + int horsesTrained = 0; foreach(HorseInstance horse in user.HorseInventory.HorseList) { if(horse.BasicStats.Mood < 200) @@ -659,7 +660,8 @@ namespace HISP.Game horse.AdvancedStats.Agility += 1; horse.AdvancedStats.Endurance += 1; horse.BasicStats.Experience += 1; - horse.TrainTimer = 720; + horse.TrainTimer = 720; + horsesTrained++; message += Messages.FormatRanchTrain(horse.Name, 1, 1, 1, 1, 1, 1); } else @@ -1111,6 +1113,8 @@ namespace HISP.Game if (user.BankInterest > user.BankMoney) { moneyMade = user.BankInterest - user.BankMoney; + if (moneyMade > 100000000) + moneyMade = 100000000; user.BankMoney += moneyMade; } @@ -1954,6 +1958,53 @@ namespace HISP.Game message += Messages.MetaTerminator; return message; } + private static string buildTrainer(User user, Trainer trainer) + { + string message = ""; + message += Messages.FormatTrainerHeaderFormat(trainer.ImprovesStat, trainer.MoneyCost, trainer.ImprovesAmount, trainer.ExperienceGained); + + + foreach (HorseInstance horse in user.HorseInventory.HorseList.OrderBy(o => o.Name).ToList()) + { + HorseInfo.StatCalculator speedStat = new HorseInfo.StatCalculator(horse, HorseInfo.StatType.SPEED); + HorseInfo.StatCalculator strengthStat = new HorseInfo.StatCalculator(horse, HorseInfo.StatType.STRENGTH); + HorseInfo.StatCalculator conformationStat = new HorseInfo.StatCalculator(horse, HorseInfo.StatType.CONFORMATION); + HorseInfo.StatCalculator agilityStat = new HorseInfo.StatCalculator(horse, HorseInfo.StatType.AGILITY); + HorseInfo.StatCalculator enduranceStat = new HorseInfo.StatCalculator(horse, HorseInfo.StatType.ENDURANCE); + + HorseInfo.StatCalculator statCalculator; + switch (trainer.ImprovesStat.ToUpper()) + { + case "SPEED": + statCalculator = speedStat; + break; + case "STRENGTH": + statCalculator = strengthStat; + break; + case "AGILITY": + statCalculator = agilityStat; + break; + case "ENDURANCE": + statCalculator = enduranceStat; + break; + case "CONFORMATION": + statCalculator = conformationStat; + break; + default: + statCalculator = speedStat; + break; + } + + if(statCalculator.BreedValue < statCalculator.MaxValue) + message += Messages.FormatTrainerTrainInEntry(horse.Name, statCalculator.BreedValue, statCalculator.MaxValue, horse.RandomId); + else + message += Messages.FormatTrainerFullyTrained(horse.Name, statCalculator.BreedValue); + + } + message += Messages.ExitThisPlace; + message += Messages.MetaTerminator; + return message; + } private static string buildPawneer(User user) { string message = ""; @@ -2065,6 +2116,10 @@ namespace HISP.Game { message += buildRiddlerRiddle(user); } + if(TileCode == "TRAINER") + { + message += buildTrainer(user, Trainer.GetTrainerById(int.Parse(TileArg))); + } if (TileCode == "LIBRARY") { message += buildLibary(); diff --git a/Horse Isle Server/HorseIsleServer/Game/Services/Trainer.cs b/Horse Isle Server/HorseIsleServer/Game/Services/Trainer.cs new file mode 100644 index 0000000..a0535d8 --- /dev/null +++ b/Horse Isle Server/HorseIsleServer/Game/Services/Trainer.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HISP.Game.Services +{ + public class Trainer + { + public static List Trainers = new List(); + + public int Id; + public string ImprovesStat; + public int ImprovesAmount; + public int ThirstCost; + public int MoodCost; + public int HungerCost; + public int MoneyCost; + public int ExperienceGained; + + public static Trainer GetTrainerById(int id) + { + foreach (Trainer trainer in Trainers) + if (trainer.Id == id) + return trainer; + + throw new KeyNotFoundException("Trainer " + id + " not found"); + } + } +} diff --git a/Horse Isle Server/HorseIsleServer/Server/GameDataJson.cs b/Horse Isle Server/HorseIsleServer/Server/GameDataJson.cs index 5f8eb25..8f9426d 100755 --- a/Horse Isle Server/HorseIsleServer/Server/GameDataJson.cs +++ b/Horse Isle Server/HorseIsleServer/Server/GameDataJson.cs @@ -708,6 +708,21 @@ namespace HISP.Server BBCode code = new BBCode(tag, meta); Logger.DebugPrint("Registered BBCODE: " + code.Tag + " to " + code.MetaTranslation); } + int totalTrainingPens = gameData.training_pens.Count; + for (int i = 0; i < totalTrainingPens; i++) + { + Trainer trainer = new Trainer(); + trainer.Id = gameData.training_pens[i].trainer_id; + trainer.ImprovesStat = gameData.training_pens[i].improves_stat; + trainer.ImprovesAmount = gameData.training_pens[i].improves_amount; + trainer.ThirstCost = gameData.training_pens[i].thirst_cost; + trainer.MoodCost = gameData.training_pens[i].mood_cost; + trainer.HungerCost = gameData.training_pens[i].hunger_cost; + trainer.MoneyCost = gameData.training_pens[i].money_cost; + trainer.ExperienceGained = gameData.training_pens[i].experience; + Trainer.Trainers.Add(trainer); + Logger.DebugPrint("Registered Training Pen: " + trainer.Id + " for " + trainer.ImprovesStat); + } HorseInfo.HorseNames = gameData.horses.names.ToObject(); @@ -924,6 +939,14 @@ namespace HISP.Server Messages.FarrierPutOnSteelShoesAllMesssageFormat = gameData.messages.meta.farrier.put_on_steel_all; Messages.FarrierShoesCantAffordMessage = gameData.messages.meta.farrier.cant_afford_farrier; + // Trainng Pen + Messages.TrainedInStatFormat = gameData.messages.meta.trainer_pen.train_success; + Messages.TrainerHeaderFormat = gameData.messages.meta.trainer_pen.train_header; + Messages.TrainerHorseEntryFormat = gameData.messages.meta.trainer_pen.train_format; + Messages.TrainerHorseFullyTrainedFormat = gameData.messages.meta.trainer_pen.fully_trained; + Messages.TrainerCantTrainAgainInFormat = gameData.messages.meta.trainer_pen.train_again_in; + Messages.TrainerCantAfford = gameData.messages.meta.trainer_pen.cant_afford; + // Santa Messages.SantaHiddenText = gameData.messages.meta.santa.hidden_text; Messages.SantaWrapItemFormat = gameData.messages.meta.santa.wrap_format; diff --git a/Horse Isle Server/HorseIsleServer/Server/GameServer.cs b/Horse Isle Server/HorseIsleServer/Server/GameServer.cs index 497a7f8..3268db6 100755 --- a/Horse Isle Server/HorseIsleServer/Server/GameServer.cs +++ b/Horse Isle Server/HorseIsleServer/Server/GameServer.cs @@ -675,6 +675,104 @@ namespace HISP.Server } } break; + case PacketBuilder.HORSE_TRAIN: + randomId = 0; + packetStr = Encoding.UTF8.GetString(packet); + randomIdStr = packetStr.Substring(2, packetStr.Length - 4); + + if (randomIdStr == "NaN") + break; + + try + { + randomId = int.Parse(randomIdStr); + } + catch (Exception) + { + Logger.ErrorPrint(sender.LoggedinUser.Username + " Sent an invalid randomid to horse interaction packet "); + break; + } + if (sender.LoggedinUser.HorseInventory.HorseIdExist(randomId)) + { + HorseInstance trainHorseInst = sender.LoggedinUser.HorseInventory.GetHorseById(randomId); + sender.LoggedinUser.LastViewedHorse = trainHorseInst; + + if (World.InSpecialTile(sender.LoggedinUser.X, sender.LoggedinUser.Y)) + { + World.SpecialTile tile = World.GetSpecialTile(sender.LoggedinUser.X, sender.LoggedinUser.Y); + if (tile.Code.StartsWith("TRAINER-")) + { + if(trainHorseInst.TrainTimer > 0) + { + byte[] trainSuccessfulMessage = PacketBuilder.CreateChat(Messages.FormatTrainerCantTrainAgainIn(trainHorseInst.TrainTimer), PacketBuilder.CHAT_BOTTOM_RIGHT); + sender.SendPacket(trainSuccessfulMessage); + break; + } + string[] trainerInfo = tile.Code.Split('-'); + int trainerId = int.Parse(trainerInfo[1]); + + Trainer trainer = Trainer.GetTrainerById(trainerId); + + if(sender.LoggedinUser.Money >= trainer.MoneyCost) + { + sender.LoggedinUser.Money -= trainer.MoneyCost; + trainHorseInst.BasicStats.Mood -= trainer.MoodCost; + trainHorseInst.BasicStats.Thirst -= trainer.ThirstCost; + trainHorseInst.BasicStats.Hunger -= trainer.HungerCost; + + + switch (trainer.ImprovesStat.ToUpper()) + { + case "SPEED": + trainHorseInst.AdvancedStats.Speed += trainer.ImprovesAmount; + break; + case "STRENGTH": + trainHorseInst.AdvancedStats.Strength += trainer.ImprovesAmount; + break; + case "AGILITY": + trainHorseInst.AdvancedStats.Agility += trainer.ImprovesAmount; + break; + case "ENDURANCE": + trainHorseInst.AdvancedStats.Endurance += trainer.ImprovesAmount; + break; + case "CONFORMATION": + trainHorseInst.AdvancedStats.Conformation += trainer.ImprovesAmount; + break; + default: + trainHorseInst.AdvancedStats.Speed += trainer.ImprovesAmount; + break; + } + trainHorseInst.BasicStats.Experience += trainer.ExperienceGained; + trainHorseInst.TrainTimer = trainer.ImprovesAmount + trainer.ExperienceGained * 50; + + byte[] trainSuccessfulMessage = PacketBuilder.CreateChat(Messages.FormatTrainedInStatFormat(trainHorseInst.Name, trainer.ImprovesStat), PacketBuilder.CHAT_BOTTOM_RIGHT); + sender.SendPacket(trainSuccessfulMessage); + + + sender.LoggedinUser.TrackedItems.GetTrackedItem(Tracking.TrackableItem.Training).Count++; + + if (sender.LoggedinUser.TrackedItems.GetTrackedItem(Tracking.TrackableItem.Training).Count >= 1000) + sender.LoggedinUser.Awards.AddAward(Award.GetAwardById(26)); // Pro Trainer + if (sender.LoggedinUser.TrackedItems.GetTrackedItem(Tracking.TrackableItem.Training).Count >= 10000) + sender.LoggedinUser.Awards.AddAward(Award.GetAwardById(53)); // Top Trainer + + UpdateArea(sender); + } + else + { + byte[] cantAffordPacket = PacketBuilder.CreateChat(Messages.TrainerCantAfford, PacketBuilder.CHAT_BOTTOM_RIGHT); + sender.SendPacket(cantAffordPacket); + } + + } + } + break; + } + else + { + Logger.HackerPrint(sender.LoggedinUser.Username + " Tried to use trauber services on a non existant horse."); + break; + } case PacketBuilder.HORSE_GIVE_FEED: randomId = 0; packetStr = Encoding.UTF8.GetString(packet); @@ -2069,6 +2167,8 @@ namespace HISP.Server byte[] soldHorseMessage = PacketBuilder.CreateChat(Messages.FormatPawneerSold(name, price), PacketBuilder.CHAT_BOTTOM_RIGHT); sender.SendPacket(soldHorseMessage); + sender.LoggedinUser.TrackedItems.GetTrackedItem(Tracking.TrackableItem.HorsePawn).Count++; + UpdateArea(sender); break; diff --git a/Horse Isle Server/HorseIsleServer/Server/PacketBuilder.cs b/Horse Isle Server/HorseIsleServer/Server/PacketBuilder.cs index cefe6f2..9482197 100755 --- a/Horse Isle Server/HorseIsleServer/Server/PacketBuilder.cs +++ b/Horse Isle Server/HorseIsleServer/Server/PacketBuilder.cs @@ -81,6 +81,7 @@ namespace HISP.Server public const byte HORSE_SHOE_IRON = 0x28; public const byte HORSE_SHOE_STEEL = 0x29; public const byte HORSE_SHOE_ALL = 0x2E; + public const byte HORSE_TRAIN = 0x1A; public const byte HORSE_MOUNT = 0x46; public const byte HORSE_DISMOUNT = 0x47; public const byte HORSE_ESCAPE = 0x1E;