Skip to content

Commit

Permalink
Wire up Core Plating for Gear Knights
Browse files Browse the repository at this point in the history
  • Loading branch information
LtRipley36706 committed Dec 30, 2023
1 parent 932a5ab commit 4413d22
Show file tree
Hide file tree
Showing 8 changed files with 350 additions and 24 deletions.
290 changes: 290 additions & 0 deletions Source/ACE.Server/Entity/CorePlating.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
using System;

using log4net;

using ACE.Entity.Enum;
using ACE.Entity.Enum.Properties;
using ACE.Server.Entity.Actions;
using ACE.Server.Managers;
using ACE.Entity.Models;
using ACE.Server.WorldObjects;

namespace ACE.Server.Entity
{
public class CorePlating
{
// http://acpedia.org/wiki/Announcements_-_2010/06_-_Shifting_Gears#Gear_Knights
// http://acpedia.org/wiki/Core_Plating_Integrator
// http://acpedia.org/wiki/Core_Plating_Deintegrator

public const uint CorePlatingIntegrator = 42979;
public const uint CorePlatingDeintegrator = 43022;

public static bool IsCorePlatingDevice(WorldObject wo)
{
return wo.WeenieClassId == CorePlatingIntegrator || wo.WeenieClassId == CorePlatingDeintegrator;
}

public static bool IsIntegrator(uint wcid)
{
return wcid == CorePlatingIntegrator;
}

public static bool IsDeintegrator(uint wcid)
{
return wcid == CorePlatingDeintegrator;
}

/// <summary>
/// The player uses a Core Plating device on a piece of armor or clothing
/// </summary>
public static void UseObjectOnTarget(Player player, WorldObject source, WorldObject target)
{
//Console.WriteLine($"CorePlating.UseObjectOnTarget({player.Name}, {source.Name}, {target.Name})");

if (player.IsBusy)
{
player.SendUseDoneEvent(WeenieError.YoureTooBusy);
return;
}

var allowCraftInCombat = PropertyManager.GetBool("allow_combat_mode_crafting").Item;

if (!allowCraftInCombat && player.CombatMode != CombatMode.NonCombat)
{
player.SendUseDoneEvent(WeenieError.YouMustBeInPeaceModeToTrade);
return;
}

// verify use requirements
var useError = VerifyUseRequirements(player, source, target);
if (useError != WeenieError.None)
{
player.SendUseDoneEvent(useError);
return;
}

var motionCommand = MotionCommand.ClapHands;

var actionChain = new ActionChain();
var nextUseTime = 0.0f;

player.IsBusy = true;

if (allowCraftInCombat && player.CombatMode != CombatMode.NonCombat)
{
// Drop out of combat mode. This depends on the server property "allow_combat_mode_craft" being True.
// If not, this action would have aborted due to not being in NonCombat mode.
var stanceTime = player.SetCombatMode(CombatMode.NonCombat);
actionChain.AddDelaySeconds(stanceTime);

nextUseTime += stanceTime;
}

var motion = new Motion(player, motionCommand);
var currentStance = player.CurrentMotionState.Stance; // expected to be MotionStance.NonCombat
var clapTime = Physics.Animation.MotionTable.GetAnimationLength(player.MotionTableId, currentStance, motionCommand);

actionChain.AddAction(player, () => player.SendMotionAsCommands(motionCommand, currentStance));
actionChain.AddDelaySeconds(clapTime);

nextUseTime += clapTime;

actionChain.AddAction(player, () =>
{
// re-verify
var useError = VerifyUseRequirements(player, source, target);
if (useError != WeenieError.None)
{
player.SendUseDoneEvent(useError);
return;
}

if (IsIntegrator(source.WeenieClassId))
Integrate(player, source, target);
else if (IsDeintegrator(source.WeenieClassId))
Deintegrate(player, source, target);
});

//player.EnqueueMotion(actionChain, MotionCommand.Ready);

actionChain.AddAction(player, () => player.IsBusy = false);

actionChain.EnqueueChain();

player.NextUseTime = DateTime.UtcNow.AddSeconds(nextUseTime);
}

public static WeenieError VerifyUseRequirements(Player player, WorldObject source, WorldObject target)
{
if (source == target)
{
player.SendTransientError($"You can't use the {source.Name} on itself.");
return WeenieError.YouDoNotPassCraftingRequirements;
}

// ensure both source and target are in player's inventory
if (player.FindObject(source.Guid.Full, Player.SearchLocations.MyInventory) == null)
return WeenieError.YouDoNotPassCraftingRequirements;

if (player.FindObject(target.Guid.Full, Player.SearchLocations.MyInventory) == null)
return WeenieError.YouDoNotPassCraftingRequirements;

if (source.WeenieClassId != CorePlatingIntegrator && source.WeenieClassId != CorePlatingDeintegrator)
return WeenieError.YouDoNotPassCraftingRequirements;

if ((target.ValidLocations & (EquipMask.Clothing | EquipMask.Armor)) == 0)
{
player.SendTransientError($"You can't use the {source.Name} on {target.Name} because it is not a piece of armor or clothing.");
return WeenieError.YouDoNotPassCraftingRequirements;
}

if (IsIntegrator(source.WeenieClassId))
{
if (target.GetProperty(PropertyInt.HeritageSpecificArmor) == (int)HeritageGroup.Gearknight)
{
//player.SendTransientError($"This armor has already been integrated into gear plating.");
player.SendMessage($"This armor has already been integrated into gear plating.");
return WeenieError.YouDoNotPassCraftingRequirements;
}

if (target.GetProperty(PropertyInt.HeritageSpecificArmor) > 0)
{
//player.SendTransientError($"This armor cannot be integrated into gear plating as it is created specifically for another race.");
player.SendMessage($"This armor cannot be integrated into gear plating as it is created specifically for another race.");
return WeenieError.YouDoNotPassCraftingRequirements;
}
}
else if (IsDeintegrator(source.WeenieClassId))
{
if (target.GetProperty(PropertyInt.HeritageSpecificArmor) != (int)HeritageGroup.Gearknight)
{
//player.SendTransientError($"This armor has not been integrated into gear plating.");
player.SendMessage($"This armor has not been integrated into gear plating.");
return WeenieError.YouDoNotPassCraftingRequirements;
}
}

return WeenieError.None;
}

public const uint CorePlatingGearOverlay = 0x06006D70;

public static void Integrate(Player player, WorldObject source, WorldObject target)
{
player.SendMessage("Your integrator forges the piece into gear plating for a Gear Knight.");

if (target.IconOverlayId != 0)
target.IconOverlaySecondary = target.IconOverlayId;

var slotName = "";
switch (target.ValidLocations)
{
case EquipMask.HeadWear:
slotName = " Helm ";
break;
case EquipMask.ChestWear:
case EquipMask.ChestArmor:
case EquipMask.ChestWear | EquipMask.AbdomenWear | EquipMask.UpperArmWear:
case EquipMask.ChestArmor | EquipMask.AbdomenArmor | EquipMask.UpperArmArmor:
slotName = " Chest ";
break;
case EquipMask.ChestWear | EquipMask.AbdomenWear | EquipMask.UpperArmWear | EquipMask.LowerArmWear:
case EquipMask.ChestArmor | EquipMask.AbdomenArmor | EquipMask.UpperArmArmor | EquipMask.LowerArmArmor:
slotName = " Hauberk ";
break;
// no pcaps for "shirts" found
//case EquipMask.ChestWear | EquipMask.AbdomenWear | EquipMask.UpperArmWear:
//case EquipMask.ChestArmor | EquipMask.AbdomenArmor | EquipMask.UpperArmArmor:
// slotName = " Shirt ";
// break;
case EquipMask.AbdomenWear:
case EquipMask.AbdomenArmor:
slotName = " Girth ";
break;
case EquipMask.UpperArmWear:
case EquipMask.UpperArmArmor:
slotName = " Pauldron ";
break;
case EquipMask.LowerArmWear:
case EquipMask.LowerArmArmor:
slotName = " Bracer ";
break;
case EquipMask.UpperArmWear | EquipMask.LowerArmWear:
case EquipMask.UpperArmArmor | EquipMask.LowerArmArmor:
slotName = " Sleeve ";
break;
case EquipMask.HandWear:
slotName = " Gauntlet ";
break;
case EquipMask.UpperLegWear:
case EquipMask.UpperLegArmor:
slotName = " Tasset ";
break;
case EquipMask.LowerLegWear:
case EquipMask.LowerLegArmor:
slotName = " Greaves ";
break;
case EquipMask.UpperLegWear | EquipMask.LowerLegWear:
case EquipMask.UpperLegArmor | EquipMask.LowerLegArmor:
case EquipMask.AbdomenWear | EquipMask.UpperLegWear | EquipMask.LowerLegWear:
case EquipMask.AbdomenArmor | EquipMask.UpperLegArmor | EquipMask.LowerLegArmor:
slotName = " Leg ";
break;
case EquipMask.FootWear:
case EquipMask.LowerLegWear | EquipMask.FootWear:
slotName = " Solleret ";
break;
// pcap showed boots were called solleret as well
//case EquipMask.LowerLegWear | EquipMask.FootWear:
// slotName = " Boot ";
// break;
case EquipMask.Armor:
case EquipMask.HeadWear | EquipMask.Armor:
slotName = " Body ";
break;
}

player.UpdateProperty(target, PropertyInt.HeritageSpecificArmor, (int)HeritageGroup.Gearknight);
player.UpdateProperty(target, PropertyDataId.IconOverlay, CorePlatingGearOverlay);
player.UpdateProperty(target, PropertyString.GearPlatingName, $"Core{slotName}Plating");
player.UpdateProperty(target, PropertyString.Use, "This Aetherium core plating installs into the frame of a Gear Knight to strengthen it.");

target.SaveBiotaToDatabase();

player.SendUseDoneEvent();
}

public static void Deintegrate(Player player, WorldObject source, WorldObject target)
{
player.SendMessage("Your deintegrator restores the original form of this piece of gear.");

player.UpdateProperty(target, PropertyInt.HeritageSpecificArmor, null);
if (target.IconOverlayId == CorePlatingGearOverlay)
player.UpdateProperty(target, PropertyDataId.IconOverlay, target.IconOverlaySecondary ?? null);
player.UpdateProperty(target, PropertyString.GearPlatingName, null);

var targetWeenie = Database.DatabaseManager.World.GetCachedWeenie(target.WeenieClassId);

if (targetWeenie != null)
{
var useString = targetWeenie.GetProperty(PropertyString.Use);
if (useString != null)
player.UpdateProperty(target, PropertyString.Use, useString);
else
player.UpdateProperty(target, PropertyString.Use, null);
}
else
player.UpdateProperty(target, PropertyString.Use, null);

if (target.IconOverlaySecondary != null)
target.IconOverlaySecondary = null;

target.SaveBiotaToDatabase();

player.SendUseDoneEvent();
}

private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
}
}
49 changes: 26 additions & 23 deletions Source/ACE.Server/Factories/PlayerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,32 +102,35 @@ public static CreateResult Create(CharacterCreateInfo characterCreateInfo, Weeni
// skip over this for olthoi, use the weenie defaults
if (!player.IsOlthoiPlayer)
{
if (characterCreateInfo.Appearance.HeadgearStyle < uint.MaxValue) // No headgear is max UINT
if (player.Heritage != (int)HeritageGroup.Gearknight) // Gear Knights do not get clothing (pcap verified)
{
var hat = GetClothingObject(sex.GetHeadgearWeenie(characterCreateInfo.Appearance.HeadgearStyle), characterCreateInfo.Appearance.HeadgearColor, characterCreateInfo.Appearance.HeadgearHue);
if (hat != null)
player.TryEquipObject(hat, hat.ValidLocations ?? 0);
if (characterCreateInfo.Appearance.HeadgearStyle < uint.MaxValue) // No headgear is max UINT
{
var hat = GetClothingObject(sex.GetHeadgearWeenie(characterCreateInfo.Appearance.HeadgearStyle), characterCreateInfo.Appearance.HeadgearColor, characterCreateInfo.Appearance.HeadgearHue);
if (hat != null)
player.TryEquipObject(hat, hat.ValidLocations ?? 0);
else
player.TryAddToInventory(CreateIOU(sex.GetHeadgearWeenie(characterCreateInfo.Appearance.HeadgearStyle)));
}

var shirt = GetClothingObject(sex.GetShirtWeenie(characterCreateInfo.Appearance.ShirtStyle), characterCreateInfo.Appearance.ShirtColor, characterCreateInfo.Appearance.ShirtHue);
if (shirt != null)
player.TryEquipObject(shirt, shirt.ValidLocations ?? 0);
else
player.TryAddToInventory(CreateIOU(sex.GetHeadgearWeenie(characterCreateInfo.Appearance.HeadgearStyle)));
}
player.TryAddToInventory(CreateIOU(sex.GetShirtWeenie(characterCreateInfo.Appearance.ShirtStyle)));

var shirt = GetClothingObject(sex.GetShirtWeenie(characterCreateInfo.Appearance.ShirtStyle), characterCreateInfo.Appearance.ShirtColor, characterCreateInfo.Appearance.ShirtHue);
if (shirt != null)
player.TryEquipObject(shirt, shirt.ValidLocations ?? 0);
else
player.TryAddToInventory(CreateIOU(sex.GetShirtWeenie(characterCreateInfo.Appearance.ShirtStyle)));

var pants = GetClothingObject(sex.GetPantsWeenie(characterCreateInfo.Appearance.PantsStyle), characterCreateInfo.Appearance.PantsColor, characterCreateInfo.Appearance.PantsHue);
if (pants != null)
player.TryEquipObject(pants, pants.ValidLocations ?? 0);
else
player.TryAddToInventory(CreateIOU(sex.GetPantsWeenie(characterCreateInfo.Appearance.PantsStyle)));

var shoes = GetClothingObject(sex.GetFootwearWeenie(characterCreateInfo.Appearance.FootwearStyle), characterCreateInfo.Appearance.FootwearColor, characterCreateInfo.Appearance.FootwearHue);
if (shoes != null)
player.TryEquipObject(shoes, shoes.ValidLocations ?? 0);
else
player.TryAddToInventory(CreateIOU(sex.GetFootwearWeenie(characterCreateInfo.Appearance.FootwearStyle)));
var pants = GetClothingObject(sex.GetPantsWeenie(characterCreateInfo.Appearance.PantsStyle), characterCreateInfo.Appearance.PantsColor, characterCreateInfo.Appearance.PantsHue);
if (pants != null)
player.TryEquipObject(pants, pants.ValidLocations ?? 0);
else
player.TryAddToInventory(CreateIOU(sex.GetPantsWeenie(characterCreateInfo.Appearance.PantsStyle)));

var shoes = GetClothingObject(sex.GetFootwearWeenie(characterCreateInfo.Appearance.FootwearStyle), characterCreateInfo.Appearance.FootwearColor, characterCreateInfo.Appearance.FootwearHue);
if (shoes != null)
player.TryEquipObject(shoes, shoes.ValidLocations ?? 0);
else
player.TryAddToInventory(CreateIOU(sex.GetFootwearWeenie(characterCreateInfo.Appearance.FootwearStyle)));
}

string templateName = heritageGroup.Templates[characterCreateInfo.TemplateOption].Name;
player.SetProperty(PropertyString.Template, templateName);
Expand Down
3 changes: 2 additions & 1 deletion Source/ACE.Server/Managers/PropertyManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ public static void LoadDefaultProperties()
("fellow_quest_bonus", new Property<bool>(false, "if TRUE, applies EvenShare formula to fellowship quest reward XP (300% max bonus, defaults to false in retail)")),
("fix_chest_missing_inventory_window", new Property<bool>(false, "Very non-standard fix. This fixes an acclient bug where unlocking a chest, and then quickly opening it before the client has received the Locked=false update from server can result in the chest opening, but with the chest inventory window not displaying. Bug has a higher chance of appearing with more network latency.")),
("gateway_ties_summonable", new Property<bool>(true, "if disabled, players cannot summon ties from gateways. defaults to enabled, as in retail")),
("gearknight_core_plating", new Property<bool>(true, "if disabled, Gear Knight players are not required to use core plating devices for armor and clothing. defaults to enabled, as in retail")),
("house_15day_account", new Property<bool>(true, "if disabled, houses can be purchased with accounts created less than 15 days old")),
("house_30day_cooldown", new Property<bool>(true, "if disabled, houses can be purchased without waiting 30 days between each purchase")),
("house_hook_limit", new Property<bool>(true, "if disabled, house hook limits are ignored")),
Expand Down Expand Up @@ -684,4 +685,4 @@ public static void LoadDefaultProperties()
("server_motd", new Property<string>("", "Server message of the day"))
);
}
}
}
6 changes: 6 additions & 0 deletions Source/ACE.Server/WorldObjects/CraftTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public override void HandleActionUseOnTarget(Player player, WorldObject target)
return;
}

if (CorePlating.IsCorePlatingDevice(this))
{
CorePlating.UseObjectOnTarget(player, this, target);
return;
}

// fallback on recipe manager
base.HandleActionUseOnTarget(player, target);
}
Expand Down
2 changes: 2 additions & 0 deletions Source/ACE.Server/WorldObjects/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ private void SetEphemeralValues()

IsOlthoiPlayer = HeritageGroup == HeritageGroup.Olthoi || HeritageGroup == HeritageGroup.OlthoiAcid;

IsGearKnightPlayer = PropertyManager.GetBool("gearknight_core_plating").Item && HeritageGroup == HeritageGroup.Gearknight;

ContainerCapacity = (byte)(7 + AugmentationExtraPackSlot);

if (Session != null && AdvocateQuest && IsAdvocate) // Advocate permissions are per character regardless of override
Expand Down
Loading

0 comments on commit 4413d22

Please sign in to comment.