Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wire up Core Plating for Gear Knights #4080

Merged
merged 3 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 292 additions & 0 deletions Source/ACE.Server/Entity/CorePlating.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
using System;

using log4net;

using ACE.Entity.Enum;
using ACE.Entity.Enum.Properties;
using ACE.Entity.Models;
using ACE.Server.Entity.Actions;
using ACE.Server.Managers;
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);
else
player.SendUseDoneEvent(WeenieError.CraftGeneralErrorNoUiMsg);
});

//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